From 762184ad02765a10eb0aa880a726ae7e33e7ce38 Mon Sep 17 00:00:00 2001 From: Chinmay Garde Date: Tue, 20 Apr 2021 13:02:44 -0700 Subject: [PATCH 001/433] Initial Commit --- impeller/.clang-format | 8 + impeller/.gitignore | 26 ++ impeller/BUILD.gn | 10 + impeller/README.md | 12 + impeller/host/BUILD.gn | 28 ++ impeller/host/Info.plist | 30 ++ impeller/host/Renderer.h | 13 + impeller/host/Renderer.m | 299 ++++++++++++++++++ impeller/host/ShaderTypes.h | 39 +++ impeller/host/Shaders.metal | 50 +++ impeller/host/impeller_host_view_controller.h | 12 + impeller/host/impeller_host_view_controller.m | 37 +++ impeller/host/main.m | 58 ++++ impeller/impeller/BUILD.gn | 10 + 14 files changed, 632 insertions(+) create mode 100644 impeller/.clang-format create mode 100644 impeller/.gitignore create mode 100644 impeller/BUILD.gn create mode 100644 impeller/README.md create mode 100644 impeller/host/BUILD.gn create mode 100644 impeller/host/Info.plist create mode 100644 impeller/host/Renderer.h create mode 100644 impeller/host/Renderer.m create mode 100644 impeller/host/ShaderTypes.h create mode 100644 impeller/host/Shaders.metal create mode 100644 impeller/host/impeller_host_view_controller.h create mode 100644 impeller/host/impeller_host_view_controller.m create mode 100644 impeller/host/main.m create mode 100644 impeller/impeller/BUILD.gn diff --git a/impeller/.clang-format b/impeller/.clang-format new file mode 100644 index 0000000000000..6fdf1dc888cb6 --- /dev/null +++ b/impeller/.clang-format @@ -0,0 +1,8 @@ +# Defines the Chromium style for automatic reformatting. +# http://clang.llvm.org/docs/ClangFormatStyleOptions.html +BasedOnStyle: Chromium +# This defaults to 'Auto'. Explicitly set it for a while, so that +# 'vector >' in existing files gets formatted to +# 'vector>'. ('Auto' means that clang-format will only use +# 'int>>' if the file already contains at least one such instance.) +Standard: Cpp11 diff --git a/impeller/.gitignore b/impeller/.gitignore new file mode 100644 index 0000000000000..b057f8266e902 --- /dev/null +++ b/impeller/.gitignore @@ -0,0 +1,26 @@ + +# commonly generated files +*.pyc +*~ +.*.sw? +.ccls-cache +.checkstyle +.clangd +.classpath +.cproject +.DS_Store +.gdb_history +.gdbinit +.idea +.ignore +.landmines +.packages +.project +.pub +.pydevproject +.vscode +compile_commands.json +cscope.* +Session.vim +tags +Thumbs.db diff --git a/impeller/BUILD.gn b/impeller/BUILD.gn new file mode 100644 index 0000000000000..cf0528736c4aa --- /dev/null +++ b/impeller/BUILD.gn @@ -0,0 +1,10 @@ +# Copyright 2013 The Flutter Authors. All rights reserved. +# Use of this source code is governed by a BSD-style license that can be +# found in the LICENSE file. + +group("impeller") { + deps = [ + "host", + "impeller", + ] +} diff --git a/impeller/README.md b/impeller/README.md new file mode 100644 index 0000000000000..a0a0d521e3e98 --- /dev/null +++ b/impeller/README.md @@ -0,0 +1,12 @@ +``` +-------------------------------------------------------------------------------- +-------------------------------------------------------------------------------- +----------------- ___ _ _ -------------------- +----------------- |_ _|_ __ ___ _ __ ___| | | ___ _ __ -------------------- +----------------- | || '_ ` _ \| '_ \ / _ \ | |/ _ \ '__| -------------------- +----------------- | || | | | | | |_) | __/ | | __/ | -------------------- +----------------- |___|_| |_| |_| .__/ \___|_|_|\___|_| -------------------- +----------------- |_| -------------------- +-------------------------------------------------------------------------------- +-------------------------------------------------------------------------------- +``` diff --git a/impeller/host/BUILD.gn b/impeller/host/BUILD.gn new file mode 100644 index 0000000000000..08a949e6bacf3 --- /dev/null +++ b/impeller/host/BUILD.gn @@ -0,0 +1,28 @@ +# Copyright 2013 The Flutter Authors. All rights reserved. +# Use of this source code is governed by a BSD-style license that can be +# found in the LICENSE file. + +import("//flutter/common/config.gni") + +executable("host") { + cflags_objc = flutter_cflags_objc_arc + cflags_objcc = flutter_cflags_objcc_arc + + output_name = "impeller" + + sources = [ + "Renderer.h", + "Renderer.m", + "ShaderTypes.h", + "impeller_host_view_controller.h", + "impeller_host_view_controller.m", + "main.m", + ] + + libs = [ + "AppKit.framework", + "Metal.framework", + "MetalKit.framework", + "ModelIO.framework", + ] +} diff --git a/impeller/host/Info.plist b/impeller/host/Info.plist new file mode 100644 index 0000000000000..cfbbdb70c466b --- /dev/null +++ b/impeller/host/Info.plist @@ -0,0 +1,30 @@ + + + + + CFBundleDevelopmentRegion + $(DEVELOPMENT_LANGUAGE) + CFBundleExecutable + $(EXECUTABLE_NAME) + CFBundleIconFile + + CFBundleIdentifier + $(PRODUCT_BUNDLE_IDENTIFIER) + CFBundleInfoDictionaryVersion + 6.0 + CFBundleName + $(PRODUCT_NAME) + CFBundlePackageType + $(PRODUCT_BUNDLE_PACKAGE_TYPE) + CFBundleShortVersionString + 1.0 + CFBundleVersion + 1 + LSMinimumSystemVersion + $(MACOSX_DEPLOYMENT_TARGET) + NSMainStoryboardFile + Main + NSPrincipalClass + NSApplication + + diff --git a/impeller/host/Renderer.h b/impeller/host/Renderer.h new file mode 100644 index 0000000000000..94db66a9434ed --- /dev/null +++ b/impeller/host/Renderer.h @@ -0,0 +1,13 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#import + +// Our platform independent renderer class. Implements the MTKViewDelegate protocol which +// allows it to accept per-frame update and drawable resize callbacks. +@interface Renderer : NSObject + +- (nonnull instancetype)initWithMetalKitView:(nonnull MTKView*)view; + +@end diff --git a/impeller/host/Renderer.m b/impeller/host/Renderer.m new file mode 100644 index 0000000000000..6c5eddc5914b4 --- /dev/null +++ b/impeller/host/Renderer.m @@ -0,0 +1,299 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#import +#import + +#import "Renderer.h" + +// Include header shared between C code here, which executes Metal API commands, and .metal files +#import "ShaderTypes.h" + +static const NSUInteger kMaxBuffersInFlight = 3; + +static const size_t kAlignedUniformsSize = (sizeof(Uniforms) & ~0xFF) + 0x100; + +@implementation Renderer { + dispatch_semaphore_t _inFlightSemaphore; + id _device; + id _commandQueue; + + id _dynamicUniformBuffer; + id _pipelineState; + id _depthState; + id _colorMap; + MTLVertexDescriptor* _mtlVertexDescriptor; + + uint32_t _uniformBufferOffset; + + uint8_t _uniformBufferIndex; + + void* _uniformBufferAddress; + + matrix_float4x4 _projectionMatrix; + + float _rotation; + + MTKMesh* _mesh; +} + +- (nonnull instancetype)initWithMetalKitView:(nonnull MTKView*)view { + self = [super init]; + if (self) { + _device = view.device; + _inFlightSemaphore = dispatch_semaphore_create(kMaxBuffersInFlight); + [self _loadMetalWithView:view]; + [self _loadAssets]; + } + + return self; +} + +- (void)_loadMetalWithView:(nonnull MTKView*)view { + /// Load Metal state objects and initialize renderer dependent view properties + + view.depthStencilPixelFormat = MTLPixelFormatDepth32Float_Stencil8; + view.colorPixelFormat = MTLPixelFormatBGRA8Unorm_sRGB; + view.sampleCount = 1; + + _mtlVertexDescriptor = [[MTLVertexDescriptor alloc] init]; + + _mtlVertexDescriptor.attributes[VertexAttributePosition].format = MTLVertexFormatFloat3; + _mtlVertexDescriptor.attributes[VertexAttributePosition].offset = 0; + _mtlVertexDescriptor.attributes[VertexAttributePosition].bufferIndex = BufferIndexMeshPositions; + + _mtlVertexDescriptor.attributes[VertexAttributeTexcoord].format = MTLVertexFormatFloat2; + _mtlVertexDescriptor.attributes[VertexAttributeTexcoord].offset = 0; + _mtlVertexDescriptor.attributes[VertexAttributeTexcoord].bufferIndex = BufferIndexMeshGenerics; + + _mtlVertexDescriptor.layouts[BufferIndexMeshPositions].stride = 12; + _mtlVertexDescriptor.layouts[BufferIndexMeshPositions].stepRate = 1; + _mtlVertexDescriptor.layouts[BufferIndexMeshPositions].stepFunction = + MTLVertexStepFunctionPerVertex; + + _mtlVertexDescriptor.layouts[BufferIndexMeshGenerics].stride = 8; + _mtlVertexDescriptor.layouts[BufferIndexMeshGenerics].stepRate = 1; + _mtlVertexDescriptor.layouts[BufferIndexMeshGenerics].stepFunction = + MTLVertexStepFunctionPerVertex; + + id defaultLibrary = [_device newDefaultLibrary]; + + id vertexFunction = [defaultLibrary newFunctionWithName:@"vertexShader"]; + + id fragmentFunction = [defaultLibrary newFunctionWithName:@"fragmentShader"]; + + MTLRenderPipelineDescriptor* pipelineStateDescriptor = [[MTLRenderPipelineDescriptor alloc] init]; + pipelineStateDescriptor.label = @"MyPipeline"; + pipelineStateDescriptor.sampleCount = view.sampleCount; + pipelineStateDescriptor.vertexFunction = vertexFunction; + pipelineStateDescriptor.fragmentFunction = fragmentFunction; + pipelineStateDescriptor.vertexDescriptor = _mtlVertexDescriptor; + pipelineStateDescriptor.colorAttachments[0].pixelFormat = view.colorPixelFormat; + pipelineStateDescriptor.depthAttachmentPixelFormat = view.depthStencilPixelFormat; + pipelineStateDescriptor.stencilAttachmentPixelFormat = view.depthStencilPixelFormat; + + NSError* error = NULL; + _pipelineState = [_device newRenderPipelineStateWithDescriptor:pipelineStateDescriptor + error:&error]; + if (!_pipelineState) { + NSLog(@"Failed to created pipeline state, error %@", error); + } + + MTLDepthStencilDescriptor* depthStateDesc = [[MTLDepthStencilDescriptor alloc] init]; + depthStateDesc.depthCompareFunction = MTLCompareFunctionLess; + depthStateDesc.depthWriteEnabled = YES; + _depthState = [_device newDepthStencilStateWithDescriptor:depthStateDesc]; + + NSUInteger uniformBufferSize = kAlignedUniformsSize * kMaxBuffersInFlight; + + _dynamicUniformBuffer = [_device newBufferWithLength:uniformBufferSize + options:MTLResourceStorageModeShared]; + + _dynamicUniformBuffer.label = @"UniformBuffer"; + + _commandQueue = [_device newCommandQueue]; +} + +- (void)_loadAssets { + /// Load assets into metal objects + + NSError* error; + + MTKMeshBufferAllocator* metalAllocator = [[MTKMeshBufferAllocator alloc] initWithDevice:_device]; + + MDLMesh* mdlMesh = [MDLMesh newBoxWithDimensions:(vector_float3){4, 4, 4} + segments:(vector_uint3){2, 2, 2} + geometryType:MDLGeometryTypeTriangles + inwardNormals:NO + allocator:metalAllocator]; + + MDLVertexDescriptor* mdlVertexDescriptor = + MTKModelIOVertexDescriptorFromMetal(_mtlVertexDescriptor); + + mdlVertexDescriptor.attributes[VertexAttributePosition].name = MDLVertexAttributePosition; + mdlVertexDescriptor.attributes[VertexAttributeTexcoord].name = + MDLVertexAttributeTextureCoordinate; + + mdlMesh.vertexDescriptor = mdlVertexDescriptor; + + _mesh = [[MTKMesh alloc] initWithMesh:mdlMesh device:_device error:&error]; + + if (!_mesh || error) { + NSLog(@"Error creating MetalKit mesh %@", error.localizedDescription); + } + + MTKTextureLoader* textureLoader = [[MTKTextureLoader alloc] initWithDevice:_device]; + + NSDictionary* textureLoaderOptions = @{ + MTKTextureLoaderOptionTextureUsage : @(MTLTextureUsageShaderRead), + MTKTextureLoaderOptionTextureStorageMode : @(MTLStorageModePrivate) + }; + + _colorMap = [textureLoader newTextureWithName:@"ColorMap" + scaleFactor:1.0 + bundle:nil + options:textureLoaderOptions + error:&error]; + + if (!_colorMap || error) { + NSLog(@"Error creating texture %@", error.localizedDescription); + } +} + +- (void)_updateDynamicBufferState { + /// Update the state of our uniform buffers before rendering + + _uniformBufferIndex = (_uniformBufferIndex + 1) % kMaxBuffersInFlight; + + _uniformBufferOffset = kAlignedUniformsSize * _uniformBufferIndex; + + _uniformBufferAddress = ((uint8_t*)_dynamicUniformBuffer.contents) + _uniformBufferOffset; +} + +- (void)_updateGameState { + /// Update any game state before encoding renderint commands to our drawable + + Uniforms* uniforms = (Uniforms*)_uniformBufferAddress; + + uniforms->projectionMatrix = _projectionMatrix; + + vector_float3 rotationAxis = {1, 1, 0}; + matrix_float4x4 modelMatrix = matrix4x4_rotation(_rotation, rotationAxis); + matrix_float4x4 viewMatrix = matrix4x4_translation(0.0, 0.0, -8.0); + + uniforms->modelViewMatrix = matrix_multiply(viewMatrix, modelMatrix); + + _rotation += .01; +} + +- (void)drawInMTKView:(nonnull MTKView*)view { + /// Per frame updates here + + dispatch_semaphore_wait(_inFlightSemaphore, DISPATCH_TIME_FOREVER); + + id commandBuffer = [_commandQueue commandBuffer]; + commandBuffer.label = @"MyCommand"; + + __block dispatch_semaphore_t block_sema = _inFlightSemaphore; + [commandBuffer addCompletedHandler:^(id buffer) { + dispatch_semaphore_signal(block_sema); + }]; + + [self _updateDynamicBufferState]; + + [self _updateGameState]; + + /// Delay getting the currentRenderPassDescriptor until we absolutely need it to avoid + /// holding onto the drawable and blocking the display pipeline any longer than necessary + MTLRenderPassDescriptor* renderPassDescriptor = view.currentRenderPassDescriptor; + + if (renderPassDescriptor != nil) { + /// Final pass rendering code here + + id renderEncoder = + [commandBuffer renderCommandEncoderWithDescriptor:renderPassDescriptor]; + renderEncoder.label = @"MyRenderEncoder"; + + [renderEncoder pushDebugGroup:@"DrawBox"]; + + [renderEncoder setFrontFacingWinding:MTLWindingCounterClockwise]; + [renderEncoder setCullMode:MTLCullModeBack]; + [renderEncoder setRenderPipelineState:_pipelineState]; + [renderEncoder setDepthStencilState:_depthState]; + + [renderEncoder setVertexBuffer:_dynamicUniformBuffer + offset:_uniformBufferOffset + atIndex:BufferIndexUniforms]; + + [renderEncoder setFragmentBuffer:_dynamicUniformBuffer + offset:_uniformBufferOffset + atIndex:BufferIndexUniforms]; + + for (NSUInteger bufferIndex = 0; bufferIndex < _mesh.vertexBuffers.count; bufferIndex++) { + MTKMeshBuffer* vertexBuffer = _mesh.vertexBuffers[bufferIndex]; + if ((NSNull*)vertexBuffer != [NSNull null]) { + [renderEncoder setVertexBuffer:vertexBuffer.buffer + offset:vertexBuffer.offset + atIndex:bufferIndex]; + } + } + + [renderEncoder setFragmentTexture:_colorMap atIndex:TextureIndexColor]; + + for (MTKSubmesh* submesh in _mesh.submeshes) { + [renderEncoder drawIndexedPrimitives:submesh.primitiveType + indexCount:submesh.indexCount + indexType:submesh.indexType + indexBuffer:submesh.indexBuffer.buffer + indexBufferOffset:submesh.indexBuffer.offset]; + } + + [renderEncoder popDebugGroup]; + + [renderEncoder endEncoding]; + + [commandBuffer presentDrawable:view.currentDrawable]; + } + + [commandBuffer commit]; +} + +- (void)mtkView:(nonnull MTKView*)view drawableSizeWillChange:(CGSize)size { + /// Respond to drawable size or orientation changes here + + float aspect = size.width / (float)size.height; + _projectionMatrix = matrix_perspective_right_hand(65.0f * (M_PI / 180.0f), aspect, 0.1f, 100.0f); +} + +#pragma mark Matrix Math Utilities + +matrix_float4x4 matrix4x4_translation(float tx, float ty, float tz) { + return (matrix_float4x4){{{1, 0, 0, 0}, {0, 1, 0, 0}, {0, 0, 1, 0}, {tx, ty, tz, 1}}}; +} + +static matrix_float4x4 matrix4x4_rotation(float radians, vector_float3 axis) { + axis = vector_normalize(axis); + float ct = cosf(radians); + float st = sinf(radians); + float ci = 1 - ct; + float x = axis.x, y = axis.y, z = axis.z; + + return (matrix_float4x4){{{ct + x * x * ci, y * x * ci + z * st, z * x * ci - y * st, 0}, + {x * y * ci - z * st, ct + y * y * ci, z * y * ci + x * st, 0}, + {x * z * ci + y * st, y * z * ci - x * st, ct + z * z * ci, 0}, + {0, 0, 0, 1}}}; +} + +matrix_float4x4 matrix_perspective_right_hand(float fovyRadians, + float aspect, + float nearZ, + float farZ) { + float ys = 1 / tanf(fovyRadians * 0.5); + float xs = ys / aspect; + float zs = farZ / (nearZ - farZ); + + return (matrix_float4x4){{{xs, 0, 0, 0}, {0, ys, 0, 0}, {0, 0, zs, -1}, {0, 0, nearZ * zs, 0}}}; +} + +@end diff --git a/impeller/host/ShaderTypes.h b/impeller/host/ShaderTypes.h new file mode 100644 index 0000000000000..34a71c46198c9 --- /dev/null +++ b/impeller/host/ShaderTypes.h @@ -0,0 +1,39 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef ShaderTypes_h +#define ShaderTypes_h + +#ifdef __METAL_VERSION__ +#define NS_ENUM(_type, _name) \ + enum _name : _type _name; \ + enum _name : _type +#define NSInteger metal::int32_t +#else +#import +#endif + +#include + +typedef NS_ENUM(NSInteger, BufferIndex) { + BufferIndexMeshPositions = 0, + BufferIndexMeshGenerics = 1, + BufferIndexUniforms = 2 +}; + +typedef NS_ENUM(NSInteger, VertexAttribute) { + VertexAttributePosition = 0, + VertexAttributeTexcoord = 1, +}; + +typedef NS_ENUM(NSInteger, TextureIndex) { + TextureIndexColor = 0, +}; + +typedef struct { + matrix_float4x4 projectionMatrix; + matrix_float4x4 modelViewMatrix; +} Uniforms; + +#endif /* ShaderTypes_h */ diff --git a/impeller/host/Shaders.metal b/impeller/host/Shaders.metal new file mode 100644 index 0000000000000..a2e0ac5eb139d --- /dev/null +++ b/impeller/host/Shaders.metal @@ -0,0 +1,50 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +// File for Metal kernel and shader functions + +#include +#include + +// Including header shared between this Metal shader code and Swift/C code executing Metal API commands +#import "ShaderTypes.h" + +using namespace metal; + +typedef struct +{ + float3 position [[attribute(VertexAttributePosition)]]; + float2 texCoord [[attribute(VertexAttributeTexcoord)]]; +} Vertex; + +typedef struct +{ + float4 position [[position]]; + float2 texCoord; +} ColorInOut; + +vertex ColorInOut vertexShader(Vertex in [[stage_in]], + constant Uniforms & uniforms [[ buffer(BufferIndexUniforms) ]]) +{ + ColorInOut out; + + float4 position = float4(in.position, 1.0); + out.position = uniforms.projectionMatrix * uniforms.modelViewMatrix * position; + out.texCoord = in.texCoord; + + return out; +} + +fragment float4 fragmentShader(ColorInOut in [[stage_in]], + constant Uniforms & uniforms [[ buffer(BufferIndexUniforms) ]], + texture2d colorMap [[ texture(TextureIndexColor) ]]) +{ + constexpr sampler colorSampler(mip_filter::linear, + mag_filter::linear, + min_filter::linear); + + half4 colorSample = colorMap.sample(colorSampler, in.texCoord.xy); + + return float4(colorSample); +} diff --git a/impeller/host/impeller_host_view_controller.h b/impeller/host/impeller_host_view_controller.h new file mode 100644 index 0000000000000..08f4925c16c51 --- /dev/null +++ b/impeller/host/impeller_host_view_controller.h @@ -0,0 +1,12 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#import +#import +#import +#import "Renderer.h" + +@interface ImpellerHostViewController : NSViewController + +@end diff --git a/impeller/host/impeller_host_view_controller.m b/impeller/host/impeller_host_view_controller.m new file mode 100644 index 0000000000000..94937c8d14fef --- /dev/null +++ b/impeller/host/impeller_host_view_controller.m @@ -0,0 +1,37 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#import "impeller_host_view_controller.h" +#import "Renderer.h" + +@implementation ImpellerHostViewController { + MTKView* view_; + + Renderer* renderer_; +} + +- (void)loadView { + view_ = [[MTKView alloc] init]; + view_.autoresizingMask = NSViewWidthSizable | NSViewHeightSizable; + [self setView:view_]; +} + +- (void)viewDidLoad { + [super viewDidLoad]; + view_.device = MTLCreateSystemDefaultDevice(); + + if (!view_.device) { + NSLog(@"Metal is not supported on this device"); + self.view = [[NSView alloc] initWithFrame:self.view.frame]; + return; + } + + renderer_ = [[Renderer alloc] initWithMetalKitView:view_]; + + [renderer_ mtkView:view_ drawableSizeWillChange:view_.bounds.size]; + + view_.delegate = renderer_; +} + +@end diff --git a/impeller/host/main.m b/impeller/host/main.m new file mode 100644 index 0000000000000..77cd7b0eaa3fd --- /dev/null +++ b/impeller/host/main.m @@ -0,0 +1,58 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#import + +#include "impeller_host_view_controller.h" + +@interface ImpellerAppDelegate : NSObject { + NSWindow* window_; + ImpellerHostViewController* view_controller_; +} +@end + +@implementation ImpellerAppDelegate : NSObject + +- (id)init { + if (self = [super init]) { + view_controller_ = [[ImpellerHostViewController alloc] init]; + window_ = + [[NSWindow alloc] initWithContentRect:NSMakeRect(0, 0, 800, 600) + styleMask:NSWindowStyleMaskTitled | NSWindowStyleMaskClosable + backing:NSBackingStoreBuffered + defer:NO]; + [window_ setContentViewController:view_controller_]; + } + return self; +} + +- (void)applicationWillFinishLaunching:(NSNotification*)notification { + [window_ setTitle:@"Impeller Host"]; + [window_ makeKeyAndOrderFront:self]; +} + +- (BOOL)applicationShouldTerminateAfterLastWindowClosed:(NSApplication*)sender { + return YES; +} + +@end + +int main(int argc, const char* argv[]) { + NSApplication* app = [NSApplication sharedApplication]; + [app setActivationPolicy:NSApplicationActivationPolicyRegular]; + NSMenuItem* item = [[NSMenuItem alloc] init]; + NSApp.mainMenu = [[NSMenu alloc] init]; + item.submenu = [[NSMenu alloc] init]; + [app.mainMenu addItem:item]; + [item.submenu + addItem:[[NSMenuItem alloc] + initWithTitle:[@"Quit " + stringByAppendingString:[NSProcessInfo processInfo].processName] + action:@selector(terminate:) + keyEquivalent:@"q"]]; + ImpellerAppDelegate* appDelegate = [[ImpellerAppDelegate alloc] init]; + [app setDelegate:appDelegate]; + [app run]; + return 0; +} diff --git a/impeller/impeller/BUILD.gn b/impeller/impeller/BUILD.gn new file mode 100644 index 0000000000000..8c9c369144fd7 --- /dev/null +++ b/impeller/impeller/BUILD.gn @@ -0,0 +1,10 @@ +# Copyright 2013 The Flutter Authors. All rights reserved. +# Use of this source code is governed by a BSD-style license that can be +# found in the LICENSE file. + +import("//flutter/common/config.gni") + +source_set("impeller") { + cflags_objc = flutter_cflags_objc_arc + cflags_objcc = flutter_cflags_objcc_arc +} From 9d18c4a758634edf5160165e055995b338edaf56 Mon Sep 17 00:00:00 2001 From: Chinmay Garde Date: Tue, 20 Apr 2021 13:28:26 -0700 Subject: [PATCH 002/433] Cleanup file names --- impeller/host/BUILD.gn | 4 +- impeller/host/impeller_host_view_controller.h | 2 +- impeller/host/impeller_host_view_controller.m | 10 +- .../host/{Renderer.h => impeller_renderer.h} | 4 +- .../host/{Renderer.m => impeller_renderer.m} | 94 ++++++++++++------- 5 files changed, 67 insertions(+), 47 deletions(-) rename impeller/host/{Renderer.h => impeller_renderer.h} (55%) rename impeller/host/{Renderer.m => impeller_renderer.m} (79%) diff --git a/impeller/host/BUILD.gn b/impeller/host/BUILD.gn index 08a949e6bacf3..67f42d79e4ccb 100644 --- a/impeller/host/BUILD.gn +++ b/impeller/host/BUILD.gn @@ -11,11 +11,11 @@ executable("host") { output_name = "impeller" sources = [ - "Renderer.h", - "Renderer.m", "ShaderTypes.h", "impeller_host_view_controller.h", "impeller_host_view_controller.m", + "impeller_renderer.h", + "impeller_renderer.m", "main.m", ] diff --git a/impeller/host/impeller_host_view_controller.h b/impeller/host/impeller_host_view_controller.h index 08f4925c16c51..72e11620bf095 100644 --- a/impeller/host/impeller_host_view_controller.h +++ b/impeller/host/impeller_host_view_controller.h @@ -5,7 +5,7 @@ #import #import #import -#import "Renderer.h" +#import "impeller_renderer.h" @interface ImpellerHostViewController : NSViewController diff --git a/impeller/host/impeller_host_view_controller.m b/impeller/host/impeller_host_view_controller.m index 94937c8d14fef..06a7b3b959930 100644 --- a/impeller/host/impeller_host_view_controller.m +++ b/impeller/host/impeller_host_view_controller.m @@ -3,12 +3,12 @@ // found in the LICENSE file. #import "impeller_host_view_controller.h" -#import "Renderer.h" + +#import "impeller_renderer.h" @implementation ImpellerHostViewController { MTKView* view_; - - Renderer* renderer_; + ImpellerRenderer* renderer_; } - (void)loadView { @@ -27,10 +27,8 @@ - (void)viewDidLoad { return; } - renderer_ = [[Renderer alloc] initWithMetalKitView:view_]; - + renderer_ = [[ImpellerRenderer alloc] initWithMetalKitView:view_]; [renderer_ mtkView:view_ drawableSizeWillChange:view_.bounds.size]; - view_.delegate = renderer_; } diff --git a/impeller/host/Renderer.h b/impeller/host/impeller_renderer.h similarity index 55% rename from impeller/host/Renderer.h rename to impeller/host/impeller_renderer.h index 94db66a9434ed..09ea51284832c 100644 --- a/impeller/host/Renderer.h +++ b/impeller/host/impeller_renderer.h @@ -4,9 +4,7 @@ #import -// Our platform independent renderer class. Implements the MTKViewDelegate protocol which -// allows it to accept per-frame update and drawable resize callbacks. -@interface Renderer : NSObject +@interface ImpellerRenderer : NSObject - (nonnull instancetype)initWithMetalKitView:(nonnull MTKView*)view; diff --git a/impeller/host/Renderer.m b/impeller/host/impeller_renderer.m similarity index 79% rename from impeller/host/Renderer.m rename to impeller/host/impeller_renderer.m index 6c5eddc5914b4..36cd725a58c4e 100644 --- a/impeller/host/Renderer.m +++ b/impeller/host/impeller_renderer.m @@ -5,16 +5,17 @@ #import #import -#import "Renderer.h" +#import "impeller_renderer.h" -// Include header shared between C code here, which executes Metal API commands, and .metal files +// Include header shared between C code here, which executes Metal API commands, +// and .metal files #import "ShaderTypes.h" static const NSUInteger kMaxBuffersInFlight = 3; static const size_t kAlignedUniformsSize = (sizeof(Uniforms) & ~0xFF) + 0x100; -@implementation Renderer { +@implementation ImpellerRenderer { dispatch_semaphore_t _inFlightSemaphore; id _device; id _commandQueue; @@ -59,13 +60,17 @@ - (void)_loadMetalWithView:(nonnull MTKView*)view { _mtlVertexDescriptor = [[MTLVertexDescriptor alloc] init]; - _mtlVertexDescriptor.attributes[VertexAttributePosition].format = MTLVertexFormatFloat3; + _mtlVertexDescriptor.attributes[VertexAttributePosition].format = + MTLVertexFormatFloat3; _mtlVertexDescriptor.attributes[VertexAttributePosition].offset = 0; - _mtlVertexDescriptor.attributes[VertexAttributePosition].bufferIndex = BufferIndexMeshPositions; + _mtlVertexDescriptor.attributes[VertexAttributePosition].bufferIndex = + BufferIndexMeshPositions; - _mtlVertexDescriptor.attributes[VertexAttributeTexcoord].format = MTLVertexFormatFloat2; + _mtlVertexDescriptor.attributes[VertexAttributeTexcoord].format = + MTLVertexFormatFloat2; _mtlVertexDescriptor.attributes[VertexAttributeTexcoord].offset = 0; - _mtlVertexDescriptor.attributes[VertexAttributeTexcoord].bufferIndex = BufferIndexMeshGenerics; + _mtlVertexDescriptor.attributes[VertexAttributeTexcoord].bufferIndex = + BufferIndexMeshGenerics; _mtlVertexDescriptor.layouts[BufferIndexMeshPositions].stride = 12; _mtlVertexDescriptor.layouts[BufferIndexMeshPositions].stepRate = 1; @@ -79,36 +84,45 @@ - (void)_loadMetalWithView:(nonnull MTKView*)view { id defaultLibrary = [_device newDefaultLibrary]; - id vertexFunction = [defaultLibrary newFunctionWithName:@"vertexShader"]; + id vertexFunction = + [defaultLibrary newFunctionWithName:@"vertexShader"]; - id fragmentFunction = [defaultLibrary newFunctionWithName:@"fragmentShader"]; + id fragmentFunction = + [defaultLibrary newFunctionWithName:@"fragmentShader"]; - MTLRenderPipelineDescriptor* pipelineStateDescriptor = [[MTLRenderPipelineDescriptor alloc] init]; + MTLRenderPipelineDescriptor* pipelineStateDescriptor = + [[MTLRenderPipelineDescriptor alloc] init]; pipelineStateDescriptor.label = @"MyPipeline"; pipelineStateDescriptor.sampleCount = view.sampleCount; pipelineStateDescriptor.vertexFunction = vertexFunction; pipelineStateDescriptor.fragmentFunction = fragmentFunction; pipelineStateDescriptor.vertexDescriptor = _mtlVertexDescriptor; - pipelineStateDescriptor.colorAttachments[0].pixelFormat = view.colorPixelFormat; - pipelineStateDescriptor.depthAttachmentPixelFormat = view.depthStencilPixelFormat; - pipelineStateDescriptor.stencilAttachmentPixelFormat = view.depthStencilPixelFormat; + pipelineStateDescriptor.colorAttachments[0].pixelFormat = + view.colorPixelFormat; + pipelineStateDescriptor.depthAttachmentPixelFormat = + view.depthStencilPixelFormat; + pipelineStateDescriptor.stencilAttachmentPixelFormat = + view.depthStencilPixelFormat; NSError* error = NULL; - _pipelineState = [_device newRenderPipelineStateWithDescriptor:pipelineStateDescriptor - error:&error]; + _pipelineState = + [_device newRenderPipelineStateWithDescriptor:pipelineStateDescriptor + error:&error]; if (!_pipelineState) { NSLog(@"Failed to created pipeline state, error %@", error); } - MTLDepthStencilDescriptor* depthStateDesc = [[MTLDepthStencilDescriptor alloc] init]; + MTLDepthStencilDescriptor* depthStateDesc = + [[MTLDepthStencilDescriptor alloc] init]; depthStateDesc.depthCompareFunction = MTLCompareFunctionLess; depthStateDesc.depthWriteEnabled = YES; _depthState = [_device newDepthStencilStateWithDescriptor:depthStateDesc]; NSUInteger uniformBufferSize = kAlignedUniformsSize * kMaxBuffersInFlight; - _dynamicUniformBuffer = [_device newBufferWithLength:uniformBufferSize - options:MTLResourceStorageModeShared]; + _dynamicUniformBuffer = + [_device newBufferWithLength:uniformBufferSize + options:MTLResourceStorageModeShared]; _dynamicUniformBuffer.label = @"UniformBuffer"; @@ -120,7 +134,8 @@ - (void)_loadAssets { NSError* error; - MTKMeshBufferAllocator* metalAllocator = [[MTKMeshBufferAllocator alloc] initWithDevice:_device]; + MTKMeshBufferAllocator* metalAllocator = + [[MTKMeshBufferAllocator alloc] initWithDevice:_device]; MDLMesh* mdlMesh = [MDLMesh newBoxWithDimensions:(vector_float3){4, 4, 4} segments:(vector_uint3){2, 2, 2} @@ -131,7 +146,8 @@ - (void)_loadAssets { MDLVertexDescriptor* mdlVertexDescriptor = MTKModelIOVertexDescriptorFromMetal(_mtlVertexDescriptor); - mdlVertexDescriptor.attributes[VertexAttributePosition].name = MDLVertexAttributePosition; + mdlVertexDescriptor.attributes[VertexAttributePosition].name = + MDLVertexAttributePosition; mdlVertexDescriptor.attributes[VertexAttributeTexcoord].name = MDLVertexAttributeTextureCoordinate; @@ -143,7 +159,8 @@ - (void)_loadAssets { NSLog(@"Error creating MetalKit mesh %@", error.localizedDescription); } - MTKTextureLoader* textureLoader = [[MTKTextureLoader alloc] initWithDevice:_device]; + MTKTextureLoader* textureLoader = + [[MTKTextureLoader alloc] initWithDevice:_device]; NSDictionary* textureLoaderOptions = @{ MTKTextureLoaderOptionTextureUsage : @(MTLTextureUsageShaderRead), @@ -168,7 +185,8 @@ - (void)_updateDynamicBufferState { _uniformBufferOffset = kAlignedUniformsSize * _uniformBufferIndex; - _uniformBufferAddress = ((uint8_t*)_dynamicUniformBuffer.contents) + _uniformBufferOffset; + _uniformBufferAddress = + ((uint8_t*)_dynamicUniformBuffer.contents) + _uniformBufferOffset; } - (void)_updateGameState { @@ -204,9 +222,12 @@ - (void)drawInMTKView:(nonnull MTKView*)view { [self _updateGameState]; - /// Delay getting the currentRenderPassDescriptor until we absolutely need it to avoid - /// holding onto the drawable and blocking the display pipeline any longer than necessary - MTLRenderPassDescriptor* renderPassDescriptor = view.currentRenderPassDescriptor; + /// Delay getting the currentRenderPassDescriptor until we absolutely need it + /// to avoid + /// holding onto the drawable and blocking the display pipeline any longer + /// than necessary + MTLRenderPassDescriptor* renderPassDescriptor = + view.currentRenderPassDescriptor; if (renderPassDescriptor != nil) { /// Final pass rendering code here @@ -230,7 +251,8 @@ - (void)drawInMTKView:(nonnull MTKView*)view { offset:_uniformBufferOffset atIndex:BufferIndexUniforms]; - for (NSUInteger bufferIndex = 0; bufferIndex < _mesh.vertexBuffers.count; bufferIndex++) { + for (NSUInteger bufferIndex = 0; bufferIndex < _mesh.vertexBuffers.count; + bufferIndex++) { MTKMeshBuffer* vertexBuffer = _mesh.vertexBuffers[bufferIndex]; if ((NSNull*)vertexBuffer != [NSNull null]) { [renderEncoder setVertexBuffer:vertexBuffer.buffer @@ -260,16 +282,16 @@ - (void)drawInMTKView:(nonnull MTKView*)view { } - (void)mtkView:(nonnull MTKView*)view drawableSizeWillChange:(CGSize)size { - /// Respond to drawable size or orientation changes here - float aspect = size.width / (float)size.height; - _projectionMatrix = matrix_perspective_right_hand(65.0f * (M_PI / 180.0f), aspect, 0.1f, 100.0f); + _projectionMatrix = matrix_perspective_right_hand(65.0f * (M_PI / 180.0f), + aspect, 0.1f, 100.0f); } #pragma mark Matrix Math Utilities matrix_float4x4 matrix4x4_translation(float tx, float ty, float tz) { - return (matrix_float4x4){{{1, 0, 0, 0}, {0, 1, 0, 0}, {0, 0, 1, 0}, {tx, ty, tz, 1}}}; + return (matrix_float4x4){ + {{1, 0, 0, 0}, {0, 1, 0, 0}, {0, 0, 1, 0}, {tx, ty, tz, 1}}}; } static matrix_float4x4 matrix4x4_rotation(float radians, vector_float3 axis) { @@ -279,10 +301,11 @@ static matrix_float4x4 matrix4x4_rotation(float radians, vector_float3 axis) { float ci = 1 - ct; float x = axis.x, y = axis.y, z = axis.z; - return (matrix_float4x4){{{ct + x * x * ci, y * x * ci + z * st, z * x * ci - y * st, 0}, - {x * y * ci - z * st, ct + y * y * ci, z * y * ci + x * st, 0}, - {x * z * ci + y * st, y * z * ci - x * st, ct + z * z * ci, 0}, - {0, 0, 0, 1}}}; + return (matrix_float4x4){ + {{ct + x * x * ci, y * x * ci + z * st, z * x * ci - y * st, 0}, + {x * y * ci - z * st, ct + y * y * ci, z * y * ci + x * st, 0}, + {x * z * ci + y * st, y * z * ci - x * st, ct + z * z * ci, 0}, + {0, 0, 0, 1}}}; } matrix_float4x4 matrix_perspective_right_hand(float fovyRadians, @@ -293,7 +316,8 @@ matrix_float4x4 matrix_perspective_right_hand(float fovyRadians, float xs = ys / aspect; float zs = farZ / (nearZ - farZ); - return (matrix_float4x4){{{xs, 0, 0, 0}, {0, ys, 0, 0}, {0, 0, zs, -1}, {0, 0, nearZ * zs, 0}}}; + return (matrix_float4x4){ + {{xs, 0, 0, 0}, {0, ys, 0, 0}, {0, 0, zs, -1}, {0, 0, nearZ * zs, 0}}}; } @end From 62d7a751dc4f98e7bcdbd49c799f99c8ff2f9608 Mon Sep 17 00:00:00 2001 From: Chinmay Garde Date: Tue, 20 Apr 2021 14:43:14 -0700 Subject: [PATCH 003/433] Add Impeller geometry. --- impeller/host/BUILD.gn | 2 + impeller/impeller/BUILD.gn | 30 ++ impeller/impeller/geometry/Matrix.cc | 484 ++++++++++++++++++++ impeller/impeller/geometry/Matrix.h | 299 ++++++++++++ impeller/impeller/geometry/Path.cc | 194 ++++++++ impeller/impeller/geometry/Path.h | 80 ++++ impeller/impeller/geometry/PathBuilder.cc | 328 +++++++++++++ impeller/impeller/geometry/PathBuilder.h | 90 ++++ impeller/impeller/geometry/PathComponent.cc | 421 +++++++++++++++++ impeller/impeller/geometry/PathComponent.h | 111 +++++ impeller/impeller/geometry/Point.cc | 26 ++ impeller/impeller/geometry/Point.h | 58 +++ impeller/impeller/geometry/Quaternion.cc | 40 ++ impeller/impeller/geometry/Quaternion.h | 83 ++++ impeller/impeller/geometry/Rect.cc | 62 +++ impeller/impeller/geometry/Rect.h | 72 +++ impeller/impeller/geometry/Shear.cc | 19 + impeller/impeller/geometry/Shear.h | 37 ++ impeller/impeller/geometry/Size.cc | 26 ++ impeller/impeller/geometry/Size.h | 59 +++ impeller/impeller/geometry/Vector.cc | 25 + impeller/impeller/geometry/Vector.h | 146 ++++++ 22 files changed, 2692 insertions(+) create mode 100644 impeller/impeller/geometry/Matrix.cc create mode 100644 impeller/impeller/geometry/Matrix.h create mode 100644 impeller/impeller/geometry/Path.cc create mode 100644 impeller/impeller/geometry/Path.h create mode 100644 impeller/impeller/geometry/PathBuilder.cc create mode 100644 impeller/impeller/geometry/PathBuilder.h create mode 100644 impeller/impeller/geometry/PathComponent.cc create mode 100644 impeller/impeller/geometry/PathComponent.h create mode 100644 impeller/impeller/geometry/Point.cc create mode 100644 impeller/impeller/geometry/Point.h create mode 100644 impeller/impeller/geometry/Quaternion.cc create mode 100644 impeller/impeller/geometry/Quaternion.h create mode 100644 impeller/impeller/geometry/Rect.cc create mode 100644 impeller/impeller/geometry/Rect.h create mode 100644 impeller/impeller/geometry/Shear.cc create mode 100644 impeller/impeller/geometry/Shear.h create mode 100644 impeller/impeller/geometry/Size.cc create mode 100644 impeller/impeller/geometry/Size.h create mode 100644 impeller/impeller/geometry/Vector.cc create mode 100644 impeller/impeller/geometry/Vector.h diff --git a/impeller/host/BUILD.gn b/impeller/host/BUILD.gn index 67f42d79e4ccb..ae7b48e4034c6 100644 --- a/impeller/host/BUILD.gn +++ b/impeller/host/BUILD.gn @@ -25,4 +25,6 @@ executable("host") { "MetalKit.framework", "ModelIO.framework", ] + + deps = [ "//flutter/impeller/impeller" ] } diff --git a/impeller/impeller/BUILD.gn b/impeller/impeller/BUILD.gn index 8c9c369144fd7..f7c06059eca75 100644 --- a/impeller/impeller/BUILD.gn +++ b/impeller/impeller/BUILD.gn @@ -7,4 +7,34 @@ import("//flutter/common/config.gni") source_set("impeller") { cflags_objc = flutter_cflags_objc_arc cflags_objcc = flutter_cflags_objcc_arc + + geometry_sources = [ + "geometry/Matrix.cc", + "geometry/Matrix.h", + "geometry/Path.cc", + "geometry/Path.h", + "geometry/PathBuilder.cc", + "geometry/PathBuilder.h", + "geometry/PathComponent.cc", + "geometry/PathComponent.h", + "geometry/Point.cc", + "geometry/Point.h", + "geometry/Quaternion.cc", + "geometry/Quaternion.h", + "geometry/Rect.cc", + "geometry/Rect.h", + "geometry/Shear.cc", + "geometry/Shear.h", + "geometry/Size.cc", + "geometry/Size.h", + "geometry/Vector.cc", + "geometry/Vector.h", + ] + + sources = geometry_sources + + deps = [ + "//flutter/fml", + "//flutter/runtime:libdart", # For tracing, seems to be pulled in by FML. + ] } diff --git a/impeller/impeller/geometry/Matrix.cc b/impeller/impeller/geometry/Matrix.cc new file mode 100644 index 0000000000000..8909df29bd41f --- /dev/null +++ b/impeller/impeller/geometry/Matrix.cc @@ -0,0 +1,484 @@ +/* + * This source file is part of the Radar project. + * Licensed under the MIT License. See LICENSE file for details. + */ + +#include "Matrix.h" +#include +#include + +namespace rl { +namespace geom { + +Matrix::Matrix(const Decomposition& d) : Matrix() { + /* + * Apply perspective. + */ + for (int i = 0; i < 4; i++) { + e[i][3] = d.perspective.e[i]; + } + + /* + * Apply translation. + */ + for (int i = 0; i < 3; i++) { + for (int j = 0; j < 3; j++) { + e[3][i] += d.translation.e[j] * e[j][i]; + } + } + + /* + * Apply rotation. + */ + + Matrix rotation; + + const auto x = -d.rotation.x; + const auto y = -d.rotation.y; + const auto z = -d.rotation.z; + const auto w = d.rotation.w; + + /* + * Construct a composite rotation matrix from the quaternion values. + */ + + rotation.e[0][0] = 1.0 - 2.0 * (y * y + z * z); + rotation.e[0][1] = 2.0 * (x * y - z * w); + rotation.e[0][2] = 2.0 * (x * z + y * w); + rotation.e[1][0] = 2.0 * (x * y + z * w); + rotation.e[1][1] = 1.0 - 2.0 * (x * x + z * z); + rotation.e[1][2] = 2.0 * (y * z - x * w); + rotation.e[2][0] = 2.0 * (x * z - y * w); + rotation.e[2][1] = 2.0 * (y * z + x * w); + rotation.e[2][2] = 1.0 - 2.0 * (x * x + y * y); + + *this = *this * rotation; + + /* + * Apply shear. + */ + Matrix shear; + + if (d.shear.e[2] != 0) { + shear.e[2][1] = d.shear.e[2]; + *this = *this * shear; + } + + if (d.shear.e[1] != 0) { + shear.e[2][1] = 0.0; + shear.e[2][0] = d.shear.e[1]; + *this = *this * shear; + } + + if (d.shear.e[0] != 0) { + shear.e[2][0] = 0.0; + shear.e[1][0] = d.shear.e[0]; + *this = *this * shear; + } + + /* + * Apply scale. + */ + for (int i = 0; i < 3; i++) { + for (int j = 0; j < 3; j++) { + e[i][j] *= d.scale.e[i]; + } + } +} + +Matrix Matrix::Orthographic(double left, + double right, + double bottom, + double top, + double nearZ, + double farZ) { + double ral = right + left; + double rsl = right - left; + double tab = top + bottom; + double tsb = top - bottom; + double fan = farZ + nearZ; + double fsn = farZ - nearZ; + + // clang-format off + return Matrix(2.0 / rsl, 0.0, 0.0, 0.0, + 0.0, 2.0 / tsb, 0.0, 0.0, + 0.0, 0.0, -2.0 / fsn, 0.0, + -ral / rsl, -tab / tsb, -fan / fsn, 1.0); + // clang-format on +} + +Matrix Matrix::Orthographic(const Size& size) { + return Matrix::Orthographic(0, size.width, size.height, 0, -INT_MAX, INT_MAX); +} + +Matrix Matrix::Perspective(double fov, + double aspect, + double nearZ, + double farZ) { + double cotan = 1.0 / tan(fov / 2.0); + + return Matrix(cotan / aspect, 0.0, 0.0, 0.0, // + 0.0, cotan, 0.0, 0.0, // + 0.0, 0.0, (farZ + nearZ) / (nearZ - farZ), -1.0, // + 0.0, 0.0, (2.0 * farZ * nearZ) / (nearZ - farZ), 0.0 // + ); +} + +Matrix Matrix::LookAt(const Vector3& eye, + const Vector3& center, + const Vector3& up) { + auto n = (eye - center).normalize(); + auto u = (up.cross(n)).normalize(); + auto v = n.cross(u); + return {u.x, v.x, n.x, 0.0, // + u.y, v.y, n.y, 0.0, // + u.z, v.z, n.z, 0.0, // + (-u).dot(eye), (-v).dot(eye), (-n).dot(eye), 1.0}; +} + +Matrix Matrix::operator+(const Matrix& o) const { + return Matrix( + m[0] + o.m[0], m[1] + o.m[1], m[2] + o.m[2], m[3] + o.m[3], // + m[4] + o.m[4], m[5] + o.m[5], m[6] + o.m[6], m[7] + o.m[7], // + m[8] + o.m[8], m[9] + o.m[9], m[10] + o.m[10], m[11] + o.m[11], // + m[12] + o.m[12], m[13] + o.m[13], m[14] + o.m[14], m[15] + o.m[15] // + ); +} + +std::string Matrix::toString() const { + std::stringstream stream; + for (int i = 0, limit = 16; i < limit; i++) { + stream << m[i]; + if (i != limit - 1) { + stream << ","; + } + } + return stream.str(); +} + +void Matrix::fromString(const std::string& str) { + std::stringstream stream(str); + for (int i = 0; i < 16; i++) { + stream >> m[i]; + stream.ignore(); + } +} + +Matrix Matrix::invert() const { + Matrix tmp{ + m[5] * m[10] * m[15] - m[5] * m[11] * m[14] - m[9] * m[6] * m[15] + + m[9] * m[7] * m[14] + m[13] * m[6] * m[11] - m[13] * m[7] * m[10], + + -m[1] * m[10] * m[15] + m[1] * m[11] * m[14] + m[9] * m[2] * m[15] - + m[9] * m[3] * m[14] - m[13] * m[2] * m[11] + m[13] * m[3] * m[10], + + m[1] * m[6] * m[15] - m[1] * m[7] * m[14] - m[5] * m[2] * m[15] + + m[5] * m[3] * m[14] + m[13] * m[2] * m[7] - m[13] * m[3] * m[6], + + -m[1] * m[6] * m[11] + m[1] * m[7] * m[10] + m[5] * m[2] * m[11] - + m[5] * m[3] * m[10] - m[9] * m[2] * m[7] + m[9] * m[3] * m[6], + + -m[4] * m[10] * m[15] + m[4] * m[11] * m[14] + m[8] * m[6] * m[15] - + m[8] * m[7] * m[14] - m[12] * m[6] * m[11] + m[12] * m[7] * m[10], + + m[0] * m[10] * m[15] - m[0] * m[11] * m[14] - m[8] * m[2] * m[15] + + m[8] * m[3] * m[14] + m[12] * m[2] * m[11] - m[12] * m[3] * m[10], + + -m[0] * m[6] * m[15] + m[0] * m[7] * m[14] + m[4] * m[2] * m[15] - + m[4] * m[3] * m[14] - m[12] * m[2] * m[7] + m[12] * m[3] * m[6], + + m[0] * m[6] * m[11] - m[0] * m[7] * m[10] - m[4] * m[2] * m[11] + + m[4] * m[3] * m[10] + m[8] * m[2] * m[7] - m[8] * m[3] * m[6], + + m[4] * m[9] * m[15] - m[4] * m[11] * m[13] - m[8] * m[5] * m[15] + + m[8] * m[7] * m[13] + m[12] * m[5] * m[11] - m[12] * m[7] * m[9], + + -m[0] * m[9] * m[15] + m[0] * m[11] * m[13] + m[8] * m[1] * m[15] - + m[8] * m[3] * m[13] - m[12] * m[1] * m[11] + m[12] * m[3] * m[9], + + m[0] * m[5] * m[15] - m[0] * m[7] * m[13] - m[4] * m[1] * m[15] + + m[4] * m[3] * m[13] + m[12] * m[1] * m[7] - m[12] * m[3] * m[5], + + -m[0] * m[5] * m[11] + m[0] * m[7] * m[9] + m[4] * m[1] * m[11] - + m[4] * m[3] * m[9] - m[8] * m[1] * m[7] + m[8] * m[3] * m[5], + + -m[4] * m[9] * m[14] + m[4] * m[10] * m[13] + m[8] * m[5] * m[14] - + m[8] * m[6] * m[13] - m[12] * m[5] * m[10] + m[12] * m[6] * m[9], + + m[0] * m[9] * m[14] - m[0] * m[10] * m[13] - m[8] * m[1] * m[14] + + m[8] * m[2] * m[13] + m[12] * m[1] * m[10] - m[12] * m[2] * m[9], + + -m[0] * m[5] * m[14] + m[0] * m[6] * m[13] + m[4] * m[1] * m[14] - + m[4] * m[2] * m[13] - m[12] * m[1] * m[6] + m[12] * m[2] * m[5], + + m[0] * m[5] * m[10] - m[0] * m[6] * m[9] - m[4] * m[1] * m[10] + + m[4] * m[2] * m[9] + m[8] * m[1] * m[6] - m[8] * m[2] * m[5]}; + + double det = + m[0] * tmp.m[0] + m[1] * tmp.m[4] + m[2] * tmp.m[8] + m[3] * tmp.m[12]; + + if (det == 0) { + return {}; + } + + det = 1.0 / det; + + return {tmp.m[0] * det, tmp.m[1] * det, tmp.m[2] * det, tmp.m[3] * det, + tmp.m[4] * det, tmp.m[5] * det, tmp.m[6] * det, tmp.m[7] * det, + tmp.m[8] * det, tmp.m[9] * det, tmp.m[10] * det, tmp.m[11] * det, + tmp.m[12] * det, tmp.m[13] * det, tmp.m[14] * det, tmp.m[15] * det}; +} + +Matrix Matrix::transpose() const { + return { + m[0], m[4], m[8], m[12], // + m[1], m[5], m[9], m[13], // + m[2], m[6], m[10], m[14], // + m[3], m[7], m[11], m[15], // + }; +} + +double Matrix::determinant() const { + auto a00 = e[0][0]; + auto a01 = e[0][1]; + auto a02 = e[0][2]; + auto a03 = e[0][3]; + auto a10 = e[1][0]; + auto a11 = e[1][1]; + auto a12 = e[1][2]; + auto a13 = e[1][3]; + auto a20 = e[2][0]; + auto a21 = e[2][1]; + auto a22 = e[2][2]; + auto a23 = e[2][3]; + auto a30 = e[3][0]; + auto a31 = e[3][1]; + auto a32 = e[3][2]; + auto a33 = e[3][3]; + + auto b00 = a00 * a11 - a01 * a10; + auto b01 = a00 * a12 - a02 * a10; + auto b02 = a00 * a13 - a03 * a10; + auto b03 = a01 * a12 - a02 * a11; + auto b04 = a01 * a13 - a03 * a11; + auto b05 = a02 * a13 - a03 * a12; + auto b06 = a20 * a31 - a21 * a30; + auto b07 = a20 * a32 - a22 * a30; + auto b08 = a20 * a33 - a23 * a30; + auto b09 = a21 * a32 - a22 * a31; + auto b10 = a21 * a33 - a23 * a31; + auto b11 = a22 * a33 - a23 * a32; + + return b00 * b11 - b01 * b10 + b02 * b09 + b03 * b08 - b04 * b07 + b05 * b06; +} + +/* + * Adapted for Radar from Graphics Gems: + * http://www.realtimerendering.com/resources/GraphicsGems/gemsii/unmatrix.c + */ +Matrix::DecompositionResult Matrix::decompose() const { + /* + * Normalize the matrix. + */ + Matrix self = *this; + + if (self.e[3][3] == 0) { + return {false, {}}; + } + + for (int i = 0; i < 4; i++) { + for (int j = 0; j < 4; j++) { + self.e[i][j] /= self.e[3][3]; + } + } + + /* + * `perspectiveMatrix` is used to solve for perspective, but it also provides + * an easy way to test for singularity of the upper 3x3 component. + */ + Matrix perpectiveMatrix = self; + for (int i = 0; i < 3; i++) { + perpectiveMatrix.e[i][3] = 0; + } + + perpectiveMatrix.e[3][3] = 1; + + if (perpectiveMatrix.determinant() == 0.0) { + return {false, {}}; + } + + Decomposition result; + + /* + * ========================================================================== + * First, isolate perspective. + * ========================================================================== + */ + if (self.e[0][3] != 0.0 || self.e[1][3] != 0.0 || self.e[2][3] != 0.0) { + /* + * prhs is the right hand side of the equation. + */ + const Vector4 rightHandSide(self.e[0][3], // + self.e[1][3], // + self.e[2][3], // + self.e[3][3]); + + /* + * Solve the equation by inverting `perspectiveMatrix` and multiplying + * prhs by the inverse. + */ + + result.perspective = rightHandSide * perpectiveMatrix.invert().transpose(); + + /* + * Clear the perspective partition. + */ + self.e[0][3] = self.e[1][3] = self.e[2][3] = 0; + self.e[3][3] = 1; + } + + /* + * ========================================================================== + * Next, the translation. + * ========================================================================== + */ + result.translation = {self.e[3][0], self.e[3][1], self.e[3][2]}; + self.e[3][0] = self.e[3][1] = self.e[3][2] = 0.0; + + /* + * ========================================================================== + * Next, the scale and shear. + * ========================================================================== + */ + Vector3 row[3]; + for (int i = 0; i < 3; i++) { + row[i].x = self.e[i][0]; + row[i].y = self.e[i][1]; + row[i].z = self.e[i][2]; + } + + /* + * Compute X scale factor and normalize first row. + */ + result.scale.x = row[0].length(); + row[0] = row[0].normalize(); + + /* + * Compute XY shear factor and make 2nd row orthogonal to 1st. + */ + result.shear.xy = row[0].dot(row[1]); + row[1] = Vector3::Combine(row[1], 1.0, row[0], -result.shear.xy); + + /* + * Compute Y scale and normalize 2nd row. + */ + result.scale.y = row[1].length(); + row[1] = row[1].normalize(); + result.shear.xy /= result.scale.y; + + /* + * Compute XZ and YZ shears, orthogonalize 3rd row. + */ + result.shear.xz = row[0].dot(row[2]); + row[2] = Vector3::Combine(row[2], 1.0, row[0], -result.shear.xz); + result.shear.yz = row[1].dot(row[2]); + row[2] = Vector3::Combine(row[2], 1.0, row[1], -result.shear.yz); + + /* + * Next, get Z scale and normalize 3rd row. + */ + result.scale.z = row[2].length(); + row[2] = row[2].normalize(); + + result.shear.xz /= result.scale.z; + result.shear.yz /= result.scale.z; + + /* + * At this point, the matrix (in rows[]) is orthonormal. + * Check for a coordinate system flip. If the determinant + * is -1, then negate the matrix and the scaling factors. + */ + if (row[0].dot(row[1].cross(row[2])) < 0) { + result.scale.x *= -1; + result.scale.y *= -1; + result.scale.z *= -1; + + for (int i = 0; i < 3; i++) { + row[i].x *= -1; + row[i].y *= -1; + row[i].z *= -1; + } + } + + /* + * ========================================================================== + * Finally, get the rotations out. + * ========================================================================== + */ + result.rotation.x = + 0.5 * sqrt(fmax(1.0 + row[0].x - row[1].y - row[2].z, 0.0)); + result.rotation.y = + 0.5 * sqrt(fmax(1.0 - row[0].x + row[1].y - row[2].z, 0.0)); + result.rotation.z = + 0.5 * sqrt(fmax(1.0 - row[0].x - row[1].y + row[2].z, 0.0)); + result.rotation.w = + 0.5 * sqrt(fmax(1.0 + row[0].x + row[1].y + row[2].z, 0.0)); + + if (row[2].y > row[1].z) { + result.rotation.x = -result.rotation.x; + } + if (row[0].z > row[2].x) { + result.rotation.y = -result.rotation.y; + } + if (row[1].x > row[0].y) { + result.rotation.z = -result.rotation.z; + } + + return DecompositionResult(true, result); +} + +uint64_t Matrix::Decomposition::componentsMask() const { + uint64_t mask = 0; + + Quaternion noRotation(0.0, 0.0, 0.0, 1.0); + if (rotation != noRotation) { + mask = mask | static_cast(Component::Rotation); + } + + Vector4 defaultPerspective(0.0, 0.0, 0.0, 1.0); + if (perspective != defaultPerspective) { + mask = mask | static_cast(Component::Perspective); + } + + Shear noShear(0.0, 0.0, 0.0); + if (shear != noShear) { + mask = mask | static_cast(Component::Shear); + } + + Vector3 defaultScale(1.0, 1.0, 1.0); + if (scale != defaultScale) { + mask = mask | static_cast(Component::Scale); + } + + Vector3 defaultTranslation(0.0, 0.0, 0.0); + if (translation != defaultTranslation) { + mask = mask | static_cast(Component::Translation); + } + + return mask; +} + +std::string Matrix::Decomposition::toString() const { + std::stringstream stream; + + stream << "Translation: " << translation.toString() << std::endl; + stream << "Scale: " << scale.toString() << std::endl; + stream << "Shear: " << shear.toString() << std::endl; + stream << "Perspective: " << perspective.toString() << std::endl; + stream << "Rotation: " << rotation.toString() << std::endl; + + return stream.str(); +} + +} // namespace geom +} // namespace rl diff --git a/impeller/impeller/geometry/Matrix.h b/impeller/impeller/geometry/Matrix.h new file mode 100644 index 0000000000000..add1b681b507c --- /dev/null +++ b/impeller/impeller/geometry/Matrix.h @@ -0,0 +1,299 @@ +/* + * This source file is part of the Radar project. + * Licensed under the MIT License. See LICENSE file for details. + */ + +#pragma once + +#include +#include +#include "Point.h" +#include "Quaternion.h" +#include "Shear.h" +#include "Size.h" +#include "Vector.h" + +namespace rl { +namespace geom { + +struct Matrix { + union { + double m[16]; + double e[4][4]; + Vector4 vec[4]; + }; + + struct Decomposition { + Vector3 translation; + Vector3 scale; + Shear shear; + Vector4 perspective; + Quaternion rotation; + + enum class Component { + Translation = 1 << 0, + Scale = 1 << 1, + Shear = 1 << 2, + Perspective = 1 << 3, + Rotation = 1 << 4, + }; + + uint64_t componentsMask() const; + + std::string toString() const; + }; + + using DecompositionResult = + std::pair; + + Matrix() + // clang-format off + : vec{ Vector4(1.0, 0.0, 0.0, 0.0), + Vector4(0.0, 1.0, 0.0, 0.0), + Vector4(0.0, 0.0, 1.0, 0.0), + Vector4(0.0, 0.0, 0.0, 1.0)} {} + // clang-format on + + // clang-format off + Matrix(double m0, double m1, double m2, double m3, + double m4, double m5, double m6, double m7, + double m8, double m9, double m10, double m11, + double m12, double m13, double m14, double m15) + : vec{Vector4(m0, m1, m2, m3), + Vector4(m4, m5, m6, m7), + Vector4(m8, m9, m10, m11), + Vector4(m12, m13, m14, m15)} {} + // clang-format on + + Matrix(const Decomposition& decomposition); + + static Matrix Translation(const Vector3& t) { + // clang-format off + return Matrix(1.0, 0.0, 0.0, 0.0, + 0.0, 1.0, 0.0, 0.0, + 0.0, 0.0, 1.0, 0.0, + t.x, t.y, t.z, 1.0); + // clang-format on + } + + static Matrix Scale(const Vector3& s) { + // clang-format off + return Matrix(s.x, 0.0, 0.0, 0.0, + 0.0, s.y, 0.0, 0.0, + 0.0, 0.0, s.z, 0.0, + 0.0, 0.0, 0.0, 1.0); + // clang-format on + } + + static Matrix Rotation(double radians, const Vector4& r) { + const Vector4 v = r.normalize(); + + const double cosine = cos(radians); + const double cosp = 1.0f - cosine; + const double sine = sin(radians); + + // clang-format off + return Matrix( + cosine + cosp * v.x * v.x, + cosp * v.x * v.y + v.z * sine, + cosp * v.x * v.z - v.y * sine, + 0.0, + + cosp * v.x * v.y - v.z * sine, + cosine + cosp * v.y * v.y, + cosp * v.y * v.z + v.x * sine, + 0.0, + + cosp * v.x * v.z + v.y * sine, + cosp * v.y * v.z - v.x * sine, + cosine + cosp * v.z * v.z, + 0.0, + + 0.0, + 0.0, + 0.0, + 1.0); + // clang-format on + } + + static Matrix RotationX(double radians) { + double cosine = cos(radians); + double sine = sin(radians); + // clang-format off + return Matrix( + 1.0, 0.0, 0.0, 0.0, + 0.0, cosine, sine, 0.0, + 0.0, -sine, cosine, 0.0, + 0.0, 0.0, 0.0, 1.0 + ); + // clang-format on + } + + static Matrix RotationY(double radians) { + double cosine = cos(radians); + double sine = sin(radians); + + // clang-format off + return Matrix( + cosine, 0.0, -sine, 0.0, + 0.0, 1.0, 0.0, 0.0, + sine, 0.0, cosine, 0.0, + 0.0, 0.0, 0.0, 1.0 + ); + // clang-format on + } + + static Matrix RotationZ(double radians) { + double cosine = cos(radians); + double sine = sin(radians); + + // clang-format off + return Matrix ( + cosine, sine, 0.0, 0.0, + -sine, cosine, 0.0, 0.0, + 0.0, 0.0, 1.0, 0.0, + 0.0, 0.0, 0.0, 1.0 + ); + // clang-format on + } + + Matrix translate(const Vector3& t) const { + // clang-format off + return Matrix(m[0], m[1], m[2], m[3], + m[4], m[5], m[6], m[7], + m[8], m[9], m[10], m[11], + m[0] * t.x + m[4] * t.y + m[8] * t.z + m[12], + m[1] * t.x + m[5] * t.y + m[9] * t.z + m[13], + m[2] * t.x + m[6] * t.y + m[10] * t.z + m[14], + m[15]); + // clang-format on + } + + Matrix scale(const Vector3& s) const { + // clang-format off + return Matrix(m[0] * s.x, m[1] * s.x, m[2] * s.x , m[3] * s.x, + m[4] * s.y, m[5] * s.y, m[6] * s.y , m[7] * s.y, + m[8] * s.z, m[9] * s.z, m[10] * s.z, m[11] * s.z, + m[12] , m[13] , m[14] , m[15]); + // clang-format on + } + + Matrix multiply(const Matrix& o) const { + // clang-format off + return Matrix( + m[0] * o.m[0] + m[4] * o.m[1] + m[8] * o.m[2] + m[12] * o.m[3], + m[1] * o.m[0] + m[5] * o.m[1] + m[9] * o.m[2] + m[13] * o.m[3], + m[2] * o.m[0] + m[6] * o.m[1] + m[10] * o.m[2] + m[14] * o.m[3], + m[3] * o.m[0] + m[7] * o.m[1] + m[11] * o.m[2] + m[15] * o.m[3], + m[0] * o.m[4] + m[4] * o.m[5] + m[8] * o.m[6] + m[12] * o.m[7], + m[1] * o.m[4] + m[5] * o.m[5] + m[9] * o.m[6] + m[13] * o.m[7], + m[2] * o.m[4] + m[6] * o.m[5] + m[10] * o.m[6] + m[14] * o.m[7], + m[3] * o.m[4] + m[7] * o.m[5] + m[11] * o.m[6] + m[15] * o.m[7], + m[0] * o.m[8] + m[4] * o.m[9] + m[8] * o.m[10] + m[12] * o.m[11], + m[1] * o.m[8] + m[5] * o.m[9] + m[9] * o.m[10] + m[13] * o.m[11], + m[2] * o.m[8] + m[6] * o.m[9] + m[10] * o.m[10] + m[14] * o.m[11], + m[3] * o.m[8] + m[7] * o.m[9] + m[11] * o.m[10] + m[15] * o.m[11], + m[0] * o.m[12] + m[4] * o.m[13] + m[8] * o.m[14] + m[12] * o.m[15], + m[1] * o.m[12] + m[5] * o.m[13] + m[9] * o.m[14] + m[13] * o.m[15], + m[2] * o.m[12] + m[6] * o.m[13] + m[10] * o.m[14] + m[14] * o.m[15], + m[3] * o.m[12] + m[7] * o.m[13] + m[11] * o.m[14] + m[15] * o.m[15]); + // clang-format on + } + + Matrix transpose() const; + + Matrix invert() const; + + double determinant() const; + + bool isAffine() const { + return (m[2] == 0 && m[3] == 0 && m[6] == 0 && m[7] == 0 && m[8] == 0 && + m[9] == 0 && m[10] == 1 && m[11] == 0 && m[14] == 0 && m[15] == 1); + } + + bool isIdentity() const { + return ( + // clang-format off + m[0] == 1.0 && m[1] == 0.0 && m[2] == 0.0 && m[3] == 0.0 && + m[4] == 0.0 && m[5] == 1.0 && m[6] == 0.0 && m[7] == 0.0 && + m[8] == 0.0 && m[9] == 0.0 && m[10] == 1.0 && m[11] == 0.0 && + m[12] == 0.0 && m[13] == 0.0 && m[14] == 0.0 && m[15] == 1.0 + // clang-format on + ); + } + + DecompositionResult decompose() const; + + bool operator==(const Matrix& m) const { + // clang-format off + return vec[0] == m.vec[0] + && vec[1] == m.vec[1] + && vec[2] == m.vec[2] + && vec[3] == m.vec[3]; + // clang-format on + } + + bool operator!=(const Matrix& m) const { + // clang-format off + return vec[0] != m.vec[0] + || vec[1] != m.vec[1] + || vec[2] != m.vec[2] + || vec[3] != m.vec[3]; + // clang-format on + } + + Matrix operator+(const Vector3& t) const { return translate(t); } + + Matrix operator-(const Vector3& t) const { return translate(-t); } + + Matrix operator*(const Vector3& s) const { return scale(s); } + + Matrix operator*(const Matrix& m) const { return multiply(m); } + + Matrix operator+(const Matrix& m) const; + + static Matrix Orthographic(double left, + double right, + double bottom, + double top, + double nearZ, + double farZ); + + static Matrix Orthographic(const Size& size); + + /** + * Specify a viewing frustum in the worlds coordinate system. + * + * @param fov angle of the field of view (in radians). + * @param aspect aspect ratio. + * @param nearZ near clipping plane. + * @param farZ far clipping plane. + * + * @return the perspective projection matrix. + */ + static Matrix Perspective(double fov, + double aspect, + double nearZ, + double farZ); + + static Matrix LookAt(const Vector3& eye, + const Vector3& center, + const Vector3& up); + + std::string toString() const; + + void fromString(const std::string& str); +}; + +static_assert(sizeof(struct Matrix) == sizeof(double) * 16, + "The matrix must be of consistent size."); + +static inline Vector4 operator*(const Vector4& v, const Matrix& m) { + return Vector4(v.x * m.m[0] + v.y * m.m[4] + v.z * m.m[8] + v.w * m.m[12], + v.x * m.m[1] + v.y * m.m[5] + v.z * m.m[9] + v.w * m.m[13], + v.x * m.m[2] + v.y * m.m[6] + v.z * m.m[10] + v.w * m.m[14], + v.x * m.m[3] + v.y * m.m[7] + v.z * m.m[11] + v.w * m.m[15]); +} + +} // namespace geom +} // namespace rl diff --git a/impeller/impeller/geometry/Path.cc b/impeller/impeller/geometry/Path.cc new file mode 100644 index 0000000000000..8651108f9c276 --- /dev/null +++ b/impeller/impeller/geometry/Path.cc @@ -0,0 +1,194 @@ +/* + * This source file is part of the Radar project. + * Licensed under the MIT License. See LICENSE file for details. + */ + +#include "Path.h" + +namespace rl { +namespace geom { + +Path::Path() = default; + +Path::~Path() = default; + +size_t Path::componentCount() const { + return _components.size(); +} + +Path& Path::addLinearComponent(Point p1, Point p2) { + _linears.emplace_back(p1, p2); + _components.emplace_back(ComponentType::Linear, _linears.size() - 1); + return *this; +} + +Path& Path::addQuadraticComponent(Point p1, Point cp, Point p2) { + _quads.emplace_back(p1, cp, p2); + _components.emplace_back(ComponentType::Quadratic, _quads.size() - 1); + return *this; +} + +Path& Path::addCubicComponent(Point p1, Point cp1, Point cp2, Point p2) { + _cubics.emplace_back(p1, cp1, cp2, p2); + _components.emplace_back(ComponentType::Cubic, _cubics.size() - 1); + return *this; +} + +void Path::enumerateComponents(Applier linearApplier, + Applier quadApplier, + Applier cubicApplier) const { + size_t currentIndex = 0; + for (const auto& component : _components) { + switch (component.type) { + case ComponentType::Linear: + if (linearApplier) { + linearApplier(currentIndex, _linears[component.index]); + } + break; + case ComponentType::Quadratic: + if (quadApplier) { + quadApplier(currentIndex, _quads[component.index]); + } + break; + case ComponentType::Cubic: + if (cubicApplier) { + cubicApplier(currentIndex, _cubics[component.index]); + } + break; + } + currentIndex++; + } +} + +bool Path::linearComponentAtIndex(size_t index, + LinearPathComponent& linear) const { + if (index >= _components.size()) { + return false; + } + + if (_components[index].type != ComponentType::Linear) { + return false; + } + + linear = _linears[_components[index].index]; + return true; +} + +bool Path::quadraticComponentAtIndex(size_t index, + QuadraticPathComponent& quadratic) const { + if (index >= _components.size()) { + return false; + } + + if (_components[index].type != ComponentType::Quadratic) { + return false; + } + + quadratic = _quads[_components[index].index]; + return true; +} + +bool Path::cubicComponentAtIndex(size_t index, + CubicPathComponent& cubic) const { + if (index >= _components.size()) { + return false; + } + + if (_components[index].type != ComponentType::Cubic) { + return false; + } + + cubic = _cubics[_components[index].index]; + return true; +} + +bool Path::updateLinearComponentAtIndex(size_t index, + const LinearPathComponent& linear) { + if (index >= _components.size()) { + return false; + } + + if (_components[index].type != ComponentType::Linear) { + return false; + } + + _linears[_components[index].index] = linear; + return true; +} + +bool Path::updateQuadraticComponentAtIndex( + size_t index, + const QuadraticPathComponent& quadratic) { + if (index >= _components.size()) { + return false; + } + + if (_components[index].type != ComponentType::Quadratic) { + return false; + } + + _quads[_components[index].index] = quadratic; + return true; +} + +bool Path::updateCubicComponentAtIndex(size_t index, + CubicPathComponent& cubic) { + if (index >= _components.size()) { + return false; + } + + if (_components[index].type != ComponentType::Cubic) { + return false; + } + + _cubics[_components[index].index] = cubic; + return true; +} + +void Path::smoothPoints(SmoothPointsEnumerator enumerator, + const SmoothingApproximation& approximation) const { + if (enumerator == nullptr) { + return; + } + + for (const auto& component : _components) { + switch (component.type) { + case ComponentType::Linear: { + if (!enumerator(_linears[component.index].smoothPoints())) { + return; + } + } break; + case ComponentType::Quadratic: { + if (!enumerator(_quads[component.index].smoothPoints(approximation))) { + return; + } + } break; + case ComponentType::Cubic: { + if (!enumerator(_cubics[component.index].smoothPoints(approximation))) { + return; + } + } break; + } + } +} + +Rect Path::boundingBox() const { + Rect box; + + for (const auto& linear : _linears) { + box = box.withPoints(linear.extrema()); + } + + for (const auto& quad : _quads) { + box = box.withPoints(quad.extrema()); + } + + for (const auto& cubic : _cubics) { + box = box.withPoints(cubic.extrema()); + } + + return box; +} + +} // namespace geom +} // namespace rl diff --git a/impeller/impeller/geometry/Path.h b/impeller/impeller/geometry/Path.h new file mode 100644 index 0000000000000..d36b730074a74 --- /dev/null +++ b/impeller/impeller/geometry/Path.h @@ -0,0 +1,80 @@ +/* + * This source file is part of the Radar project. + * Licensed under the MIT License. See LICENSE file for details. + */ + +#pragma once + +#include +#include +#include "PathComponent.h" + +namespace rl { +namespace geom { + +class Path { + public: + enum class ComponentType : uint8_t { + Linear, + Quadratic, + Cubic, + }; + + Path(); + + ~Path(); + + size_t componentCount() const; + + Path& addLinearComponent(Point p1, Point p2); + + Path& addQuadraticComponent(Point p1, Point cp, Point p2); + + Path& addCubicComponent(Point p1, Point cp1, Point cp2, Point p2); + + template + using Applier = std::function; + void enumerateComponents(Applier linearApplier, + Applier quadApplier, + Applier cubicApplier) const; + + bool linearComponentAtIndex(size_t index, LinearPathComponent& linear) const; + + bool quadraticComponentAtIndex(size_t index, + QuadraticPathComponent& quadratic) const; + + bool cubicComponentAtIndex(size_t index, CubicPathComponent& cubic) const; + + bool updateLinearComponentAtIndex(size_t index, + const LinearPathComponent& linear); + + bool updateQuadraticComponentAtIndex(size_t index, + const QuadraticPathComponent& quadratic); + + bool updateCubicComponentAtIndex(size_t index, CubicPathComponent& cubic); + + using SmoothPointsEnumerator = std::function points)>; + void smoothPoints(SmoothPointsEnumerator enumerator, + const SmoothingApproximation& approximation) const; + + Rect boundingBox() const; + + private: + struct ComponentIndexPair { + ComponentType type; + size_t index; + + ComponentIndexPair() : type(ComponentType::Linear), index(0) {} + + ComponentIndexPair(ComponentType aType, size_t aIndex) + : type(aType), index(aIndex) {} + }; + + std::vector _components; + std::vector _linears; + std::vector _quads; + std::vector _cubics; +}; + +} // namespace geom +} // namespace rl diff --git a/impeller/impeller/geometry/PathBuilder.cc b/impeller/impeller/geometry/PathBuilder.cc new file mode 100644 index 0000000000000..35a8b718ed200 --- /dev/null +++ b/impeller/impeller/geometry/PathBuilder.cc @@ -0,0 +1,328 @@ +/* + * This source file is part of the Radar project. + * Licensed under the MIT License. See LICENSE file for details. + */ + +#include "PathBuilder.h" + +namespace rl { +namespace geom { + +static const double kArcApproximationMagic = 0.551915024494; + +PathBuilder::PathBuilder() = default; + +PathBuilder::~PathBuilder() = default; + +Path PathBuilder::path() const { + return _prototype; +} + +PathBuilder& PathBuilder::moveTo(Point point, bool relative) { + _current = relative ? _current + point : point; + _subpathStart = _current; + return *this; +} + +PathBuilder& PathBuilder::close() { + lineTo(_subpathStart); + return *this; +} + +PathBuilder& PathBuilder::lineTo(Point point, bool relative) { + point = relative ? _current + point : point; + _prototype.addLinearComponent(_current, point); + _current = point; + return *this; +} + +PathBuilder& PathBuilder::horizontalLineTo(double x, bool relative) { + Point endpoint = + relative ? Point{_current.x + x, _current.y} : Point{x, _current.y}; + _prototype.addLinearComponent(_current, endpoint); + _current = endpoint; + return *this; +} + +PathBuilder& PathBuilder::verticalLineTo(double y, bool relative) { + Point endpoint = + relative ? Point{_current.x, _current.y + y} : Point{_current.x, y}; + _prototype.addLinearComponent(_current, endpoint); + _current = endpoint; + return *this; +} + +PathBuilder& PathBuilder::quadraticCurveTo(Point point, + Point controlPoint, + bool relative) { + point = relative ? _current + point : point; + controlPoint = relative ? _current + controlPoint : controlPoint; + _prototype.addQuadraticComponent(_current, controlPoint, point); + _current = point; + return *this; +} + +Point PathBuilder::reflectedQuadraticControlPoint1() const { + /* + * If there is no previous command or if the previous command was not a + * quadratic, assume the control point is coincident with the current point. + */ + if (_prototype.componentCount() == 0) { + return _current; + } + + QuadraticPathComponent quad; + if (!_prototype.quadraticComponentAtIndex(_prototype.componentCount() - 1, + quad)) { + return _current; + } + + /* + * The control point is assumed to be the reflection of the control point on + * the previous command relative to the current point. + */ + return (_current * 2.0) - quad.cp; +} + +PathBuilder& PathBuilder::smoothQuadraticCurveTo(Point point, bool relative) { + point = relative ? _current + point : point; + /* + * The reflected control point is absolute and we made the endpoint absolute + * too. So there the last argument is always false (i.e, not relative). + */ + quadraticCurveTo(point, reflectedQuadraticControlPoint1(), false); + return *this; +} + +PathBuilder& PathBuilder::cubicCurveTo(Point point, + Point controlPoint1, + Point controlPoint2, + bool relative) { + controlPoint1 = relative ? _current + controlPoint1 : controlPoint1; + controlPoint2 = relative ? _current + controlPoint2 : controlPoint2; + point = relative ? _current + point : point; + _prototype.addCubicComponent(_current, controlPoint1, controlPoint2, point); + _current = point; + return *this; +} + +Point PathBuilder::reflectedCubicControlPoint1() const { + /* + * If there is no previous command or if the previous command was not a + * cubic, assume the first control point is coincident with the current + * point. + */ + if (_prototype.componentCount() == 0) { + return _current; + } + + CubicPathComponent cubic; + if (!_prototype.cubicComponentAtIndex(_prototype.componentCount() - 1, + cubic)) { + return _current; + } + + /* + * The first control point is assumed to be the reflection of the second + * control point on the previous command relative to the current point. + */ + return (_current * 2.0) - cubic.cp2; +} + +PathBuilder& PathBuilder::smoothCubicCurveTo(Point point, + Point controlPoint2, + bool relative) { + auto controlPoint1 = reflectedCubicControlPoint1(); + controlPoint2 = relative ? _current + controlPoint2 : controlPoint2; + auto endpoint = relative ? _current + point : point; + + cubicCurveTo(endpoint, // endpoint + controlPoint1, // control point 1 + controlPoint2, // control point 2 + false // relative since all points are already absolute + ); + return *this; +} + +PathBuilder& PathBuilder::addRect(Rect rect) { + _current = rect.origin; + + auto topLeft = rect.origin; + auto bottomLeft = rect.origin + Point{0.0, rect.size.height}; + auto bottomRight = rect.origin + Point{rect.size.width, rect.size.height}; + auto topRight = rect.origin + Point{rect.size.width, 0.0}; + + _prototype.addLinearComponent(topLeft, bottomLeft) + .addLinearComponent(bottomLeft, bottomRight) + .addLinearComponent(bottomRight, topRight) + .addLinearComponent(topRight, topLeft); + + return *this; +} + +PathBuilder& PathBuilder::addCircle(const Point& center, double radius) { + _current = center + Point{0.0, radius}; + + const double diameter = radius * 2.0; + const double magic = kArcApproximationMagic * radius; + + _prototype.addCubicComponent( + {center.x + radius, center.y}, // + {center.x + radius + magic, center.y}, // + {center.x + diameter, center.y + radius - magic}, // + {center.x + diameter, center.y + radius} // + ); + + _prototype.addCubicComponent( + {center.x + diameter, center.y + radius}, // + {center.x + diameter, center.y + radius + magic}, // + {center.x + radius + magic, center.y + diameter}, // + {center.x + radius, center.y + diameter} // + ); + + _prototype.addCubicComponent( + {center.x + radius, center.y + diameter}, // + {center.x + radius - magic, center.y + diameter}, // + {center.x, center.y + radius + magic}, // + {center.x, center.y + radius} // + ); + + _prototype.addCubicComponent({center.x, center.y + radius}, // + {center.x, center.y + radius - magic}, // + {center.x + radius - magic, center.y}, // + {center.x + radius, center.y} // + ); + + return *this; +} + +PathBuilder& PathBuilder::addRoundedRect(Rect rect, double radius) { + return radius == 0.0 ? addRect(rect) + : addRoundedRect(rect, {radius, radius, radius, radius}); +} + +PathBuilder& PathBuilder::addRoundedRect(Rect rect, RoundingRadii radii) { + _current = rect.origin + Point{radii.topLeft, 0.0}; + + const double magicTopRight = kArcApproximationMagic * radii.topRight; + const double magicBottomRight = kArcApproximationMagic * radii.bottomRight; + const double magicBottomLeft = kArcApproximationMagic * radii.bottomLeft; + const double magicTopLeft = kArcApproximationMagic * radii.topLeft; + + /* + * Top line. + */ + _prototype.addLinearComponent( + {rect.origin.x + radii.topLeft, rect.origin.y}, + {rect.origin.x + rect.size.width - radii.topRight, rect.origin.y}); + + /* + * Top right arc. + */ + _prototype.addCubicComponent( + {rect.origin.x + rect.size.width - radii.topRight, rect.origin.y}, + {rect.origin.x + rect.size.width - radii.topRight + magicTopRight, + rect.origin.y}, + {rect.origin.x + rect.size.width, + rect.origin.y + radii.topRight - magicTopRight}, + {rect.origin.x + rect.size.width, rect.origin.y + radii.topRight}); + + /* + * Right line. + */ + _prototype.addLinearComponent( + {rect.origin.x + rect.size.width, rect.origin.y + radii.topRight}, + {rect.origin.x + rect.size.width, + rect.origin.y + rect.size.height - radii.bottomRight}); + + /* + * Bottom right arc. + */ + _prototype.addCubicComponent( + {rect.origin.x + rect.size.width, + rect.origin.y + rect.size.height - radii.bottomRight}, + {rect.origin.x + rect.size.width, + rect.origin.y + rect.size.height - radii.bottomRight + magicBottomRight}, + {rect.origin.x + rect.size.width - radii.bottomRight + magicBottomRight, + rect.origin.y + rect.size.height}, + {rect.origin.x + rect.size.width - radii.bottomRight, + rect.origin.y + rect.size.height}); + + /* + * Bottom line. + */ + _prototype.addLinearComponent( + {rect.origin.x + rect.size.width - radii.bottomRight, + rect.origin.y + rect.size.height}, + {rect.origin.x + radii.bottomLeft, rect.origin.y + rect.size.height}); + + /* + * Bottom left arc. + */ + _prototype.addCubicComponent( + {rect.origin.x + radii.bottomLeft, rect.origin.y + rect.size.height}, + {rect.origin.x + radii.bottomLeft - magicBottomLeft, + rect.origin.y + rect.size.height}, + {rect.origin.x, + rect.origin.y + rect.size.height - radii.bottomLeft + magicBottomLeft}, + {rect.origin.x, rect.origin.y + rect.size.height - radii.bottomLeft}); + + /* + * Left line. + */ + _prototype.addLinearComponent( + {rect.origin.x, rect.origin.y + rect.size.height - radii.bottomLeft}, + {rect.origin.x, rect.origin.y + radii.topLeft}); + + /* + * Top left arc. + */ + _prototype.addCubicComponent( + {rect.origin.x, rect.origin.y + radii.topLeft}, + {rect.origin.x, rect.origin.y + radii.topLeft - magicTopLeft}, + {rect.origin.x + radii.topLeft - magicTopLeft, rect.origin.y}, + {rect.origin.x + radii.topLeft, rect.origin.y}); + + return *this; +} + +PathBuilder& PathBuilder::addEllipse(const Point& center, const Size& radius) { + _current = center + Point{0.0, radius.height}; + + const Size diameter = {radius.width * 2.0, radius.height * 2.0}; + const Size magic = {kArcApproximationMagic * radius.width, + kArcApproximationMagic * radius.height}; + + _prototype.addCubicComponent( + {center.x + radius.width, center.y}, // + {center.x + radius.width + magic.width, center.y}, // + {center.x + diameter.width, center.y + radius.height - magic.height}, // + {center.x + diameter.width, center.y + radius.height} // + ); + + _prototype.addCubicComponent( + {center.x + diameter.width, center.y + radius.height}, // + {center.x + diameter.width, center.y + radius.height + magic.height}, // + {center.x + radius.width + magic.width, center.y + diameter.height}, // + {center.x + radius.width, center.y + diameter.height} // + ); + + _prototype.addCubicComponent( + {center.x + radius.width, center.y + diameter.height}, // + {center.x + radius.width - magic.width, center.y + diameter.height}, // + {center.x, center.y + radius.height + magic.height}, // + {center.x, center.y + radius.height} // + ); + + _prototype.addCubicComponent( + {center.x, center.y + radius.height}, // + {center.x, center.y + radius.height - magic.height}, // + {center.x + radius.width - magic.width, center.y}, // + {center.x + radius.width, center.y} // + ); + + return *this; +} + +} // namespace geom +} // namespace rl diff --git a/impeller/impeller/geometry/PathBuilder.h b/impeller/impeller/geometry/PathBuilder.h new file mode 100644 index 0000000000000..ba023508b9c9e --- /dev/null +++ b/impeller/impeller/geometry/PathBuilder.h @@ -0,0 +1,90 @@ +/* + * This source file is part of the Radar project. + * Licensed under the MIT License. See LICENSE file for details. + */ + +#pragma once + +#include "Path.h" +#include "Rect.h" +#include "flutter/fml/macros.h" + +namespace rl { +namespace geom { + +class PathBuilder { + public: + PathBuilder(); + + ~PathBuilder(); + + Path path() const; + + PathBuilder& moveTo(Point point, bool relative = false); + + PathBuilder& close(); + + PathBuilder& lineTo(Point point, bool relative = false); + + PathBuilder& horizontalLineTo(double x, bool relative = false); + + PathBuilder& verticalLineTo(double y, bool relative = false); + + PathBuilder& quadraticCurveTo(Point point, + Point controlPoint, + bool relative = false); + + PathBuilder& smoothQuadraticCurveTo(Point point, bool relative = false); + + PathBuilder& cubicCurveTo(Point point, + Point controlPoint1, + Point controlPoint2, + bool relative = false); + + PathBuilder& smoothCubicCurveTo(Point point, + Point controlPoint2, + bool relative = false); + + PathBuilder& addRect(Rect rect); + + PathBuilder& addRoundedRect(Rect rect, double radius); + + PathBuilder& addCircle(const Point& center, double radius); + + PathBuilder& addEllipse(const Point& center, const Size& size); + + struct RoundingRadii { + double topLeft; + double bottomLeft; + double topRight; + double bottomRight; + + RoundingRadii() + : topLeft(0.0), bottomLeft(0.0), topRight(0.0), bottomRight(0.0) {} + + RoundingRadii(double pTopLeft, + double pBottomLeft, + double pTopRight, + double pBottomRight) + : topLeft(pTopLeft), + bottomLeft(pBottomLeft), + topRight(pTopRight), + bottomRight(pBottomRight) {} + }; + + PathBuilder& addRoundedRect(Rect rect, RoundingRadii radii); + + private: + Point _subpathStart; + Point _current; + Path _prototype; + + Point reflectedQuadraticControlPoint1() const; + + Point reflectedCubicControlPoint1() const; + + FML_DISALLOW_COPY_AND_ASSIGN(PathBuilder); +}; + +} // namespace geom +} // namespace rl diff --git a/impeller/impeller/geometry/PathComponent.cc b/impeller/impeller/geometry/PathComponent.cc new file mode 100644 index 0000000000000..f1ddeac7643d3 --- /dev/null +++ b/impeller/impeller/geometry/PathComponent.cc @@ -0,0 +1,421 @@ +/* + * This source file is part of the Radar project. + * Licensed under the MIT License. See LICENSE file for details. + */ + +#include "PathComponent.h" +#include + +namespace rl { +namespace geom { + +static const size_t kRecursionLimit = 32; +static const double kCurveCollinearityEpsilon = 1e-30; +static const double kCurveAngleToleranceEpsilon = 0.01; + +/* + * Based on: https://en.wikipedia.org/wiki/B%C3%A9zier_curve#Specific_cases + */ + +static inline double LinearSolve(double t, double p0, double p1) { + return p0 + t * (p1 - p0); +} + +static inline double QuadraticSolve(double t, double p0, double p1, double p2) { + return (1 - t) * (1 - t) * p0 + // + 2 * (1 - t) * t * p1 + // + t * t * p2; +} + +static inline double QuadraticSolveDerivative(double t, + double p0, + double p1, + double p2) { + return 2 * (1 - t) * (p1 - p0) + // + 2 * t * (p2 - p1); +} + +static inline double CubicSolve(double t, + double p0, + double p1, + double p2, + double p3) { + return (1 - t) * (1 - t) * (1 - t) * p0 + // + 3 * (1 - t) * (1 - t) * t * p1 + // + 3 * (1 - t) * t * t * p2 + // + t * t * t * p3; +} + +static inline double CubicSolveDerivative(double t, + double p0, + double p1, + double p2, + double p3) { + return -3 * p0 * (1 - t) * (1 - t) + // + p1 * (3 * (1 - t) * (1 - t) - 6 * (1 - t) * t) + + p2 * (6 * (1 - t) * t - 3 * t * t) + // + 3 * p3 * t * t; +} + +Point LinearPathComponent::solve(double time) const { + return { + LinearSolve(time, p1.x, p2.x), // x + LinearSolve(time, p1.y, p2.y), // y + }; +} + +std::vector LinearPathComponent::smoothPoints() const { + return {p1, p2}; +} + +std::vector LinearPathComponent::extrema() const { + return {p1, p2}; +} + +Point QuadraticPathComponent::solve(double time) const { + return { + QuadraticSolve(time, p1.x, cp.x, p2.x), // x + QuadraticSolve(time, p1.y, cp.y, p2.y), // y + }; +} + +Point QuadraticPathComponent::solveDerivative(double time) const { + return { + QuadraticSolveDerivative(time, p1.x, cp.x, p2.x), // x + QuadraticSolveDerivative(time, p1.y, cp.y, p2.y), // y + }; +} + +std::vector QuadraticPathComponent::smoothPoints( + const SmoothingApproximation& approximation) const { + CubicPathComponent elevated(*this); + return elevated.smoothPoints(approximation); +} + +std::vector QuadraticPathComponent::extrema() const { + CubicPathComponent elevated(*this); + return elevated.extrema(); +} + +Point CubicPathComponent::solve(double time) const { + return { + CubicSolve(time, p1.x, cp1.x, cp2.x, p2.x), // x + CubicSolve(time, p1.y, cp1.y, cp2.y, p2.y), // y + }; +} + +Point CubicPathComponent::solveDerivative(double time) const { + return { + CubicSolveDerivative(time, p1.x, cp1.x, cp2.x, p2.x), // x + CubicSolveDerivative(time, p1.y, cp1.y, cp2.y, p2.y), // y + }; +} + +/* + * Paul de Casteljau's subdivision with modifications as described in + * http://www.antigrain.com/research/adaptive_bezier/index.html. + * Refer to the diagram on that page for a description of the points. + */ +static void CubicPathSmoothenRecursive(const SmoothingApproximation& approx, + std::vector& points, + Point p1, + Point p2, + Point p3, + Point p4, + size_t level) { + if (level >= kRecursionLimit) { + return; + } + + /* + * Find all midpoints. + */ + auto p12 = (p1 + p2) / 2.0; + auto p23 = (p2 + p3) / 2.0; + auto p34 = (p3 + p4) / 2.0; + + auto p123 = (p12 + p23) / 2.0; + auto p234 = (p23 + p34) / 2.0; + + auto p1234 = (p123 + p234) / 2.0; + + /* + * Attempt approximation using single straight line. + */ + auto d = p4 - p1; + double d2 = fabs(((p2.x - p4.x) * d.y - (p2.y - p4.y) * d.x)); + double d3 = fabs(((p3.x - p4.x) * d.y - (p3.y - p4.y) * d.x)); + + double da1 = 0; + double da2 = 0; + double k = 0; + + switch ((static_cast(d2 > kCurveCollinearityEpsilon) << 1) + + static_cast(d3 > kCurveCollinearityEpsilon)) { + case 0: + /* + * All collinear OR p1 == p4. + */ + k = d.x * d.x + d.y * d.y; + if (k == 0) { + d2 = p1.distanceSquared(p2); + d3 = p4.distanceSquared(p3); + } else { + k = 1.0 / k; + da1 = p2.x - p1.x; + da2 = p2.y - p1.y; + d2 = k * (da1 * d.x + da2 * d.y); + da1 = p3.x - p1.x; + da2 = p3.y - p1.y; + d3 = k * (da1 * d.x + da2 * d.y); + + if (d2 > 0 && d2 < 1 && d3 > 0 && d3 < 1) { + /* + * Simple collinear case, 1---2---3---4. Leave just two endpoints. + */ + return; + } + + if (d2 <= 0) { + d2 = p2.distanceSquared(p1); + } else if (d2 >= 1) { + d2 = p2.distanceSquared(p4); + } else { + d2 = p2.distanceSquared({p1.x + d2 * d.x, p1.y + d2 * d.y}); + } + + if (d3 <= 0) { + d3 = p3.distanceSquared(p1); + } else if (d3 >= 1) { + d3 = p3.distanceSquared(p4); + } else { + d3 = p3.distanceSquared({p1.x + d3 * d.x, p1.y + d3 * d.y}); + } + } + + if (d2 > d3) { + if (d2 < approx.distanceToleranceSquare) { + points.emplace_back(p2); + return; + } + } else { + if (d3 < approx.distanceToleranceSquare) { + points.emplace_back(p3); + return; + } + } + break; + case 1: + /* + * p1, p2, p4 are collinear, p3 is significant. + */ + if (d3 * d3 <= approx.distanceToleranceSquare * (d.x * d.x + d.y * d.y)) { + if (approx.angleTolerance < kCurveAngleToleranceEpsilon) { + points.emplace_back(p23); + return; + } + + /* + * Angle Condition. + */ + da1 = ::fabs(::atan2(p4.y - p3.y, p4.x - p3.x) - + ::atan2(p3.y - p2.y, p3.x - p2.x)); + + if (da1 >= M_PI) { + da1 = 2.0 * M_PI - da1; + } + + if (da1 < approx.angleTolerance) { + points.emplace_back(p2); + points.emplace_back(p3); + return; + } + + if (approx.cuspLimit != 0.0) { + if (da1 > approx.cuspLimit) { + points.emplace_back(p3); + return; + } + } + } + break; + + case 2: + /* + * p1,p3,p4 are collinear, p2 is significant. + */ + if (d2 * d2 <= approx.distanceToleranceSquare * (d.x * d.x + d.y * d.y)) { + if (approx.angleTolerance < kCurveAngleToleranceEpsilon) { + points.emplace_back(p23); + return; + } + + /* + * Angle Condition. + */ + da1 = ::fabs(::atan2(p3.y - p2.y, p3.x - p2.x) - + ::atan2(p2.y - p1.y, p2.x - p1.x)); + + if (da1 >= M_PI) { + da1 = 2.0 * M_PI - da1; + } + + if (da1 < approx.angleTolerance) { + points.emplace_back(p2); + points.emplace_back(p3); + return; + } + + if (approx.cuspLimit != 0.0) { + if (da1 > approx.cuspLimit) { + points.emplace_back(p2); + return; + } + } + } + break; + + case 3: + /* + * Regular case. + */ + if ((d2 + d3) * (d2 + d3) <= + approx.distanceToleranceSquare * (d.x * d.x + d.y * d.y)) { + /* + * If the curvature doesn't exceed the distance_tolerance value + * we tend to finish subdivisions. + */ + if (approx.angleTolerance < kCurveAngleToleranceEpsilon) { + points.emplace_back(p23); + return; + } + + /* + * Angle & Cusp Condition. + */ + k = ::atan2(p3.y - p2.y, p3.x - p2.x); + da1 = ::fabs(k - ::atan2(p2.y - p1.y, p2.x - p1.x)); + da2 = ::fabs(::atan2(p4.y - p3.y, p4.x - p3.x) - k); + + if (da1 >= M_PI) { + da1 = 2.0 * M_PI - da1; + } + + if (da2 >= M_PI) { + da2 = 2.0 * M_PI - da2; + } + + if (da1 + da2 < approx.angleTolerance) { + /* + * Finally we can stop the recursion. + */ + points.emplace_back(p23); + return; + } + + if (approx.cuspLimit != 0.0) { + if (da1 > approx.cuspLimit) { + points.emplace_back(p2); + return; + } + + if (da2 > approx.cuspLimit) { + points.emplace_back(p3); + return; + } + } + } + break; + } + + /* + * Continue subdivision. + */ + CubicPathSmoothenRecursive(approx, points, p1, p12, p123, p1234, level + 1); + CubicPathSmoothenRecursive(approx, points, p1234, p234, p34, p4, level + 1); +} + +std::vector CubicPathComponent::smoothPoints( + const SmoothingApproximation& approximation) const { + std::vector points; + points.emplace_back(p1); + CubicPathSmoothenRecursive(approximation, points, p1, cp1, cp2, p2, 0); + points.emplace_back(p2); + return points; +} + +static inline bool NearEqual(double a, double b, double epsilon) { + return (a > (b - epsilon)) && (a < (b + epsilon)); +} + +static inline bool NearZero(double a) { + return NearEqual(a, 0.0, 1e-12); +} + +static void CubicPathBoundingPopulateValues(std::vector& values, + double p1, + double p2, + double p3, + double p4) { + const double a = 3.0 * (-p1 + 3.0 * p2 - 3.0 * p3 + p4); + const double b = 6.0 * (p1 - 2.0 * p2 + p3); + const double c = 3.0 * (p2 - p1); + + /* + * Boundary conditions. + */ + if (NearZero(a)) { + if (NearZero(b)) { + return; + } + + double t = -c / b; + if (t >= 0.0 && t <= 1.0) { + values.emplace_back(t); + } + return; + } + + double b2Minus4AC = (b * b) - (4.0 * a * c); + + if (b2Minus4AC < 0.0) { + return; + } + + double rootB2Minus4AC = ::sqrt(b2Minus4AC); + + { + double t = (-b + rootB2Minus4AC) / (2.0 * a); + if (t >= 0.0 && t <= 1.0) { + values.emplace_back(t); + } + } + + { + double t = (-b - rootB2Minus4AC) / (2.0 * a); + if (t >= 0.0 && t <= 1.0) { + values.emplace_back(t); + } + } +} + +std::vector CubicPathComponent::extrema() const { + /* + * As described in: https://pomax.github.io/bezierinfo/#extremities + */ + std::vector values; + + CubicPathBoundingPopulateValues(values, p1.x, cp1.x, cp2.x, p2.x); + CubicPathBoundingPopulateValues(values, p1.y, cp1.y, cp2.y, p2.y); + + std::vector points; + + for (const auto& value : values) { + points.emplace_back(solve(value)); + } + + return points; +} + +} // namespace geom +} // namespace rl diff --git a/impeller/impeller/geometry/PathComponent.h b/impeller/impeller/geometry/PathComponent.h new file mode 100644 index 0000000000000..0acf40266d630 --- /dev/null +++ b/impeller/impeller/geometry/PathComponent.h @@ -0,0 +1,111 @@ +/* + * This source file is part of the Radar project. + * Licensed under the MIT License. See LICENSE file for details. + */ + +#pragma once + +#include + +#include "Rect.h" + +namespace rl { +namespace geom { + +struct SmoothingApproximation { + const double scale; + const double angleTolerance; + const double cuspLimit; + const double distanceToleranceSquare; + + SmoothingApproximation(/* default */) + : SmoothingApproximation(1.0 /* scale */, + 0.0 /* angle tolerance */, + 0.0 /* cusp limit */) {} + + SmoothingApproximation(double pScale, + double pAngleTolerance, + double pCuspLimit) + : scale(pScale), + angleTolerance(pAngleTolerance), + cuspLimit(pCuspLimit), + distanceToleranceSquare(0.5 * pScale * 0.5 * pScale) {} +}; + +struct LinearPathComponent { + Point p1; + Point p2; + + LinearPathComponent() {} + + LinearPathComponent(Point ap1, Point ap2) : p1(ap1), p2(ap2) {} + + Point solve(double time) const; + + std::vector smoothPoints() const; + + std::vector extrema() const; + + bool operator==(const LinearPathComponent& other) const { + return p1 == other.p1 && p2 == other.p2; + } +}; + +struct QuadraticPathComponent { + Point p1; + Point cp; + Point p2; + + QuadraticPathComponent() {} + + QuadraticPathComponent(Point ap1, Point acp, Point ap2) + : p1(ap1), cp(acp), p2(ap2) {} + + Point solve(double time) const; + + Point solveDerivative(double time) const; + + std::vector smoothPoints( + const SmoothingApproximation& approximation) const; + + std::vector extrema() const; + + bool operator==(const QuadraticPathComponent& other) const { + return p1 == other.p1 && cp == other.cp && p2 == other.p2; + } +}; + +struct CubicPathComponent { + Point p1; + Point cp1; + Point cp2; + Point p2; + + CubicPathComponent() {} + + CubicPathComponent(const QuadraticPathComponent& q) + : p1(q.p1), + cp1(q.p1 + (q.cp - q.p1) * (2.0 / 3.0)), + cp2(q.p2 + (q.cp - q.p2) * (2.0 / 3.0)), + p2(q.p2) {} + + CubicPathComponent(Point ap1, Point acp1, Point acp2, Point ap2) + : p1(ap1), cp1(acp1), cp2(acp2), p2(ap2) {} + + Point solve(double time) const; + + Point solveDerivative(double time) const; + + std::vector smoothPoints( + const SmoothingApproximation& approximation) const; + + std::vector extrema() const; + + bool operator==(const CubicPathComponent& other) const { + return p1 == other.p1 && cp1 == other.cp1 && cp2 == other.cp2 && + p2 == other.p2; + } +}; + +} // namespace geom +} // namespace rl diff --git a/impeller/impeller/geometry/Point.cc b/impeller/impeller/geometry/Point.cc new file mode 100644 index 0000000000000..b7cfd3805f26b --- /dev/null +++ b/impeller/impeller/geometry/Point.cc @@ -0,0 +1,26 @@ +/* + * This source file is part of the Radar project. + * Licensed under the MIT License. See LICENSE file for details. + */ + +#include "Point.h" +#include + +namespace rl { +namespace geom { + +std::string Point::toString() const { + std::stringstream stream; + stream << x << "," << y; + return stream.str(); +} + +void Point::fromString(const std::string& str) { + std::stringstream stream(str); + stream >> x; + stream.ignore(); + stream >> y; +} + +} // namespace geom +} // namespace rl diff --git a/impeller/impeller/geometry/Point.h b/impeller/impeller/geometry/Point.h new file mode 100644 index 0000000000000..77f3cc37e4195 --- /dev/null +++ b/impeller/impeller/geometry/Point.h @@ -0,0 +1,58 @@ +/* + * This source file is part of the Radar project. + * Licensed under the MIT License. See LICENSE file for details. + */ + +#pragma once + +#include +#include +#include "Size.h" + +namespace rl { +namespace geom { + +struct Point { + double x; + double y; + + Point() : x(0.0), y(0.0) {} + Point(double x, double y) : x(x), y(y) {} + + /* + * Operator overloads + */ + bool operator==(const Point& p) const { return p.x == x && p.y == y; } + bool operator!=(const Point& p) const { return p.x != x || p.y != y; } + + Point operator-() const { return {-x, -y}; } + + Point operator+(const Point& p) const { return {x + p.x, y + p.y}; } + Point operator+(const Size& s) const { return {x + s.width, y + s.height}; } + + Point operator-(const Point& p) const { return {x - p.x, y - p.y}; } + Point operator-(const Size& s) const { return {x - s.width, y - s.height}; } + + Point operator*(double scale) const { return {x * scale, y * scale}; } + Point operator*(const Point& p) const { return {x * p.x, y * p.y}; } + Point operator*(const Size& s) const { return {x * s.width, y * s.height}; } + + Point operator/(double d) const { return {x / d, y / d}; } + Point operator/(const Point& p) const { return {x / p.x, y / p.y}; } + Point operator/(const Size& s) const { return {x / s.width, y / s.height}; } + + double distanceSquared(const Point& p) const { + double dx = p.x - x; + double dy = p.y - y; + return dx * dx + dy * dy; + } + + double distance(const Point& p) const { return sqrt(distanceSquared(p)); } + + std::string toString() const; + + void fromString(const std::string& str); +}; + +} // namespace geom +} // namespace rl diff --git a/impeller/impeller/geometry/Quaternion.cc b/impeller/impeller/geometry/Quaternion.cc new file mode 100644 index 0000000000000..bae8cdd541184 --- /dev/null +++ b/impeller/impeller/geometry/Quaternion.cc @@ -0,0 +1,40 @@ +/* + * This source file is part of the Radar project. + * Licensed under the MIT License. See LICENSE file for details. + */ + +#include "Quaternion.h" +#include + +namespace rl { +namespace geom { + +Quaternion Quaternion::slerp(const Quaternion& to, double time) const { + double cosine = dot(to); + if (fabs(cosine) < 1.0 - 1e-3 /* epsilon */) { + /* + * Spherical Interpolation. + */ + auto sine = sqrt(1.0 - cosine * cosine); + auto angle = atan2(sine, cosine); + auto sineInverse = 1.0 / sine; + auto c0 = sin((1.0 - time) * angle) * sineInverse; + auto c1 = sin(time * angle) * sineInverse; + return *this * c0 + to * c1; + } else { + /* + * Linear Interpolation. + */ + return (*this * (1.0 - time) + to * time).normalize(); + } +} + +std::string Quaternion::toString() const { + std::stringstream stream; + stream << "{" << x << ", " + << ", " << y << ", " << z << ", " << w << "}"; + return stream.str(); +} + +} // namespace geom +} // namespace rl diff --git a/impeller/impeller/geometry/Quaternion.h b/impeller/impeller/geometry/Quaternion.h new file mode 100644 index 0000000000000..2b97e4da8153b --- /dev/null +++ b/impeller/impeller/geometry/Quaternion.h @@ -0,0 +1,83 @@ +/* + * This source file is part of the Radar project. + * Licensed under the MIT License. See LICENSE file for details. + */ + +#pragma once + +#include "Vector.h" + +namespace rl { +namespace geom { + +struct Quaternion { + union { + struct { + double x; + double y; + double z; + double w; + }; + double e[4]; + }; + + Quaternion() : x(0.0), y(0.0), z(0.0), w(1.0) {} + + Quaternion(double px, double py, double pz, double pw) + : x(px), y(py), z(pz), w(pw) {} + + Quaternion(const Vector3& axis, double angle) { + const auto sine = sin(angle * 0.5); + x = sine * axis.x; + y = sine * axis.y; + z = sine * axis.z; + w = cos(angle * 0.5); + } + + double dot(const Quaternion& q) const { + return x * q.x + y * q.y + z * q.z + w * q.w; + } + + double length() const { return sqrt(x * x + y * y + z * z + w * w); } + + Quaternion normalize() const { + auto m = 1.0 / length(); + return {x * m, y * m, z * m, w * m}; + } + + Quaternion slerp(const Quaternion& to, double time) const; + + Quaternion operator*(const Quaternion& o) const { + return { + w * o.x + x * o.w + y * o.z - z * o.y, + w * o.y + y * o.w + z * o.x - x * o.z, + w * o.z + z * o.w + x * o.y - y * o.x, + w * o.w - x * o.x - y * o.y - z * o.z, + }; + } + + Quaternion operator*(double scale) const { + return {scale * x, scale * y, scale * z, scale * w}; + } + + Quaternion operator+(const Quaternion& o) const { + return {x + o.x, y + o.y, z + o.z, w + o.w}; + } + + Quaternion operator-(const Quaternion& o) const { + return {x - o.x, y - o.y, z - o.z, w - o.w}; + } + + bool operator==(const Quaternion& o) const { + return x == o.x && y == o.y && z == o.z && w == o.w; + } + + bool operator!=(const Quaternion& o) const { + return x != o.x || y != o.y || z != o.z || w != o.w; + } + + std::string toString() const; +}; + +} // namespace geom +} // namespace rl diff --git a/impeller/impeller/geometry/Rect.cc b/impeller/impeller/geometry/Rect.cc new file mode 100644 index 0000000000000..878270f17c79f --- /dev/null +++ b/impeller/impeller/geometry/Rect.cc @@ -0,0 +1,62 @@ +/* + * This source file is part of the Radar project. + * Licensed under the MIT License. See LICENSE file for details. + */ + +#include "Rect.h" +#include + +namespace rl { +namespace geom { + +Rect Rect::withPoint(const Point& p) const { + Rect copy = *this; + if (p.x < origin.x) { + copy.origin.x = p.x; + copy.size.width += (origin.x - p.x); + } + + if (p.y < origin.y) { + copy.origin.y = p.y; + copy.size.height += (origin.y - p.y); + } + + if (p.x > (size.width + origin.x)) { + copy.size.width += p.x - (size.width + origin.x); + } + + if (p.y > (size.height + origin.y)) { + copy.size.height += p.y - (size.height + origin.y); + } + + return copy; +} + +Rect Rect::withPoints(const std::vector& points) const { + Rect box = *this; + for (const auto& point : points) { + box = box.withPoint(point); + } + return box; +} + +std::string Rect::toString() const { + std::stringstream stream; + stream << origin.x << "," << origin.y << "," << size.width << "," + << size.height; + return stream.str(); +} + +void Rect::fromString(const std::string& str) { + std::stringstream stream(str); + stream >> origin.x; + stream.ignore(); + stream >> origin.y; + stream.ignore(); + stream >> size.width; + stream.ignore(); + stream >> size.height; +} + +} // namespace geom +} // namespace rl diff --git a/impeller/impeller/geometry/Rect.h b/impeller/impeller/geometry/Rect.h new file mode 100644 index 0000000000000..21abc492a03eb --- /dev/null +++ b/impeller/impeller/geometry/Rect.h @@ -0,0 +1,72 @@ +/* + * This source file is part of the Radar project. + * Licensed under the MIT License. See LICENSE file for details. + */ + +#pragma once + +#include +#include "Point.h" +#include "Size.h" + +namespace rl { +namespace geom { + +struct Rect { + Point origin; + Size size; + + Rect() : origin({0.0, 0.0}), size({0.0, 0.0}) {} + Rect(Size size) : origin({0.0, 0.0}), size(size) {} + Rect(Point origin, Size size) : origin(origin), size(size) {} + Rect(const double components[4]) + : origin(components[0], components[1]), + size(components[2], components[3]) {} + Rect(double x, double y, double width, double height) + : origin(x, y), size(width, height) {} + + /* + * Operator overloads + */ + Rect operator+(const Rect& r) const { + return Rect({origin.x + r.origin.x, origin.y + r.origin.y}, + {size.width + r.size.width, size.height + r.size.height}); + } + + Rect operator-(const Rect& r) const { + return Rect({origin.x - r.origin.x, origin.y - r.origin.y}, + {size.width - r.size.width, size.height - r.size.height}); + } + + Rect operator*(double scale) const { + return Rect({origin.x * scale, origin.y * scale}, + {size.width * scale, size.height * scale}); + } + + Rect operator*(const Rect& r) const { + return Rect({origin.x * r.origin.x, origin.y * r.origin.y}, + {size.width * r.size.width, size.height * r.size.height}); + } + + bool operator==(const Rect& r) const { + return origin == r.origin && size == r.size; + } + + bool contains(const Point& p) const { + return p.x >= origin.x && p.x <= size.width && p.y >= origin.y && + p.y <= size.height; + } + + bool isZero() const { return size.isZero(); } + + Rect withPoint(const Point& p) const; + + Rect withPoints(const std::vector& points) const; + + std::string toString() const; + + void fromString(const std::string& str); +}; + +} // namespace geom +} // namespace rl diff --git a/impeller/impeller/geometry/Shear.cc b/impeller/impeller/geometry/Shear.cc new file mode 100644 index 0000000000000..dd880a9761832 --- /dev/null +++ b/impeller/impeller/geometry/Shear.cc @@ -0,0 +1,19 @@ +/* + * This source file is part of the Radar project. + * Licensed under the MIT License. See LICENSE file for details. + */ + +#include "Shear.h" +#include + +namespace rl { +namespace geom { + +std::string Shear::toString() const { + std::stringstream stream; + stream << "{" << xy << ", " << xz << ", " << yz << "}"; + return stream.str(); +} + +} // namespace geom +} // namespace rl diff --git a/impeller/impeller/geometry/Shear.h b/impeller/impeller/geometry/Shear.h new file mode 100644 index 0000000000000..f229d08699780 --- /dev/null +++ b/impeller/impeller/geometry/Shear.h @@ -0,0 +1,37 @@ +/* + * This source file is part of the Radar project. + * Licensed under the MIT License. See LICENSE file for details. + */ + +#pragma once + +#include + +namespace rl { +namespace geom { + +struct Shear { + union { + struct { + double xy; + double xz; + double yz; + }; + double e[3]; + }; + + Shear() : xy(0.0), xz(0.0), yz(0.0) {} + + Shear(double xy, double xz, double yz) : xy(xy), xz(xz), yz(yz) {} + + bool operator==(const Shear& o) const { + return xy == o.xy && xz == o.xz && yz == o.yz; + } + + bool operator!=(const Shear& o) const { return !(*this == o); } + + std::string toString() const; +}; + +} // namespace geom +} // namespace rl diff --git a/impeller/impeller/geometry/Size.cc b/impeller/impeller/geometry/Size.cc new file mode 100644 index 0000000000000..6bc5febf7415b --- /dev/null +++ b/impeller/impeller/geometry/Size.cc @@ -0,0 +1,26 @@ +/* + * This source file is part of the Radar project. + * Licensed under the MIT License. See LICENSE file for details. + */ + +#include "Size.h" +#include + +namespace rl { +namespace geom { + +std::string Size::toString() const { + std::stringstream stream; + stream << width << "," << height; + return stream.str(); +} + +void Size::fromString(const std::string& str) { + std::stringstream stream(str); + stream >> width; + stream.ignore(); + stream >> height; +} + +} // namespace geom +} // namespace rl diff --git a/impeller/impeller/geometry/Size.h b/impeller/impeller/geometry/Size.h new file mode 100644 index 0000000000000..b2f2ab82299e5 --- /dev/null +++ b/impeller/impeller/geometry/Size.h @@ -0,0 +1,59 @@ +/* + * This source file is part of the Radar project. + * Licensed under the MIT License. See LICENSE file for details. + */ + +#pragma once + +#include + +namespace rl { +namespace geom { + +struct Size { + double width; + double height; + + Size() : width(0.0), height(0.0) {} + + Size(double width, double height) : width(width), height(height) {} + + /* + * Operator overloads + */ + Size operator*(double scale) const { return {width * scale, height * scale}; } + + bool operator==(const Size& s) const { + return s.width == width && s.height == height; + } + + bool operator!=(const Size& s) const { + return s.width != width || s.height != height; + } + + Size operator+(const Size& s) const { + return {width + s.width, height + s.height}; + } + + Size operator-(const Size& s) const { + return {width - s.width, height - s.height}; + } + + Size unionWith(const Size& o) const { + return { + std::max(width, o.width), + std::max(height, o.height), + }; + } + + bool isZero() const { return width * height == 0.0; } + + bool isPositive() const { return width > 0.0 && height > 0.0; } + + std::string toString() const; + + void fromString(const std::string& str); +}; + +} // namespace geom +} // namespace rl diff --git a/impeller/impeller/geometry/Vector.cc b/impeller/impeller/geometry/Vector.cc new file mode 100644 index 0000000000000..ad20c487e65bc --- /dev/null +++ b/impeller/impeller/geometry/Vector.cc @@ -0,0 +1,25 @@ +/* + * This source file is part of the Radar project. + * Licensed under the MIT License. See LICENSE file for details. + */ + +#include "Vector.h" +#include + +namespace rl { +namespace geom { + +std::string Vector3::toString() const { + std::stringstream stream; + stream << "{" << x << ", " << y << ", " << z << "}"; + return stream.str(); +} + +std::string Vector4::toString() const { + std::stringstream stream; + stream << "{" << x << ", " << y << ", " << z << ", " << w << "}"; + return stream.str(); +} + +} // namespace geom +} // namespace rl diff --git a/impeller/impeller/geometry/Vector.h b/impeller/impeller/geometry/Vector.h new file mode 100644 index 0000000000000..0457d1911a41c --- /dev/null +++ b/impeller/impeller/geometry/Vector.h @@ -0,0 +1,146 @@ +/* + * This source file is part of the Radar project. + * Licensed under the MIT License. See LICENSE file for details. + */ + +#pragma once + +#include +#include +#include "Point.h" +#include "Size.h" + +namespace rl { +namespace geom { + +struct Vector3 { + union { + struct { + double x; + double y; + double z; + }; + double e[3]; + }; + + Vector3() : x(0.0), y(0.0), z(0.0) {} + + Vector3(const Point& p) : x(p.x), y(p.y), z(0.0) {} + + Vector3(const Size& s) : x(s.width), y(s.height), z(0.0) {} + + Vector3(double x, double y) : x(x), y(y), z(0.0) {} + + Vector3(double x, double y, double z) : x(x), y(y), z(z) {} + + /** + * The length (or magnitude of the vector). + * + * @return the calculated length. + */ + double length() const { return sqrt(x * x + y * y + z * z); } + + Vector3 normalize() const { + const auto len = length(); + return {x / len, y / len, z / len}; + } + + double dot(const Vector3& other) const { + return ((x * other.x) + (y * other.y) + (z * other.z)); + } + + Vector3 cross(const Vector3& other) const { + return { + (y * other.z) - (z * other.y), // + (z * other.x) - (x * other.z), // + (x * other.y) - (y * other.x) // + }; + } + + bool operator==(const Vector3& v) const { + return v.x == x && v.y == y && v.z == z; + } + + bool operator!=(const Vector3& v) const { + return v.x != x || v.y != y || v.z != z; + } + + Vector3 operator-() const { return Vector3(-x, -y, -z); } + + Vector3 operator+(const Vector3& v) const { + return Vector3(x + v.x, y + v.y, z + v.z); + } + + Vector3 operator-(const Vector3& v) const { + return Vector3(x - v.x, y - v.y, z - v.z); + } + + /** + * Make a linear combination of two vectors and return the result. + * + * @param a the first vector. + * @param aScale the scale to use for the first vector. + * @param b the second vector. + * @param bScale the scale to use for the second vector. + * + * @return the combined vector. + */ + static inline Vector3 Combine(const Vector3& a, + double aScale, + const Vector3& b, + double bScale) { + return { + aScale * a.x + bScale * b.x, // + aScale * a.y + bScale * b.y, // + aScale * a.z + bScale * b.z, // + }; + } + + std::string toString() const; +}; + +struct Vector4 { + union { + struct { + double x; + double y; + double z; + double w; + }; + double e[4]; + }; + + Vector4() : x(0.0), y(0.0), z(0.0), w(1.0) {} + + Vector4(double x, double y, double z, double w) : x(x), y(y), z(z), w(w) {} + + Vector4(const Vector3& v) : x(v.x), y(v.y), z(v.z), w(1.0) {} + + Vector4(const Point& p) : x(p.x), y(p.y), z(0.0), w(1.0) {} + + Vector4 normalize() const { + const double inverse = 1.0 / sqrt(x * x + y * y + z * z + w * w); + return Vector4(x * inverse, y * inverse, z * inverse, w * inverse); + } + + bool operator==(const Vector4& v) const { + return (x == v.x) && (y == v.y) && (z == v.z) && (w == v.w); + } + + bool operator!=(const Vector4& v) const { + return (x != v.x) || (y != v.y) || (z != v.z) || (w != v.w); + } + + Vector4 operator+(const Vector4& v) const { + return Vector4(x + v.x, y + v.y, z + v.z, w + v.w); + } + + Vector4 operator-(const Vector4& v) const { + return Vector4(x - v.x, y - v.y, z - v.z, w - v.w); + } + + std::string toString() const; +}; + +} // namespace geom +} // namespace rl From e14e5b4d96951946130f9a62c345b00c6ad42cf1 Mon Sep 17 00:00:00 2001 From: Chinmay Garde Date: Tue, 20 Apr 2021 16:29:11 -0700 Subject: [PATCH 004/433] Add entity and image sources. --- impeller/impeller/BUILD.gn | 24 +- impeller/impeller/entity/Color.cc | 113 ++++ impeller/impeller/entity/Color.h | 672 +++++++++++++++++++++ impeller/impeller/entity/Entity.cc | 300 +++++++++ impeller/impeller/entity/Entity.h | 233 +++++++ impeller/impeller/image/DataImageSource.cc | 51 ++ impeller/impeller/image/DataImageSource.h | 42 ++ impeller/impeller/image/FileImageSource.cc | 64 ++ impeller/impeller/image/FileImageSource.h | 43 ++ impeller/impeller/image/Image.cc | 150 +++++ impeller/impeller/image/Image.h | 49 ++ impeller/impeller/image/ImageEncoder.cc | 86 +++ impeller/impeller/image/ImageEncoder.h | 43 ++ impeller/impeller/image/ImageResult.cc | 57 ++ impeller/impeller/image/ImageResult.h | 55 ++ impeller/impeller/image/ImageSource.cc | 59 ++ impeller/impeller/image/ImageSource.h | 54 ++ 17 files changed, 2094 insertions(+), 1 deletion(-) create mode 100644 impeller/impeller/entity/Color.cc create mode 100644 impeller/impeller/entity/Color.h create mode 100644 impeller/impeller/entity/Entity.cc create mode 100644 impeller/impeller/entity/Entity.h create mode 100644 impeller/impeller/image/DataImageSource.cc create mode 100644 impeller/impeller/image/DataImageSource.h create mode 100644 impeller/impeller/image/FileImageSource.cc create mode 100644 impeller/impeller/image/FileImageSource.h create mode 100644 impeller/impeller/image/Image.cc create mode 100644 impeller/impeller/image/Image.h create mode 100644 impeller/impeller/image/ImageEncoder.cc create mode 100644 impeller/impeller/image/ImageEncoder.h create mode 100644 impeller/impeller/image/ImageResult.cc create mode 100644 impeller/impeller/image/ImageResult.h create mode 100644 impeller/impeller/image/ImageSource.cc create mode 100644 impeller/impeller/image/ImageSource.h diff --git a/impeller/impeller/BUILD.gn b/impeller/impeller/BUILD.gn index f7c06059eca75..993e9fae2051c 100644 --- a/impeller/impeller/BUILD.gn +++ b/impeller/impeller/BUILD.gn @@ -31,7 +31,29 @@ source_set("impeller") { "geometry/Vector.h", ] - sources = geometry_sources + entity_sources = [ + "entity/Color.cc", + "entity/Color.h", + "entity/Entity.cc", + "entity/Entity.h", + ] + + image_sources = [ + "image/DataImageSource.cc", + "image/DataImageSource.h", + "image/FileImageSource.cc", + "image/FileImageSource.h", + "image/Image.cc", + "image/Image.h", + "image/ImageEncoder.cc", + "image/ImageEncoder.h", + "image/ImageResult.cc", + "image/ImageResult.h", + "image/ImageSource.cc", + "image/ImageSource.h", + ] + + sources = geometry_sources + entity_sources + image_sources deps = [ "//flutter/fml", diff --git a/impeller/impeller/entity/Color.cc b/impeller/impeller/entity/Color.cc new file mode 100644 index 0000000000000..ddeb79d54339e --- /dev/null +++ b/impeller/impeller/entity/Color.cc @@ -0,0 +1,113 @@ +/* + * This source file is part of the Radar project. + * Licensed under the MIT License. See LICENSE file for details. + */ + +#include +#include +#include +#include +#include + +namespace rl { +namespace entity { + +ColorHSB ColorHSB::FromRGB(Color rgb) { + double R = rgb.red; + double G = rgb.green; + double B = rgb.blue; + + double v = 0.0; + double x = 0.0; + double f = 0.0; + + int64_t i = 0; + + x = fmin(R, G); + x = fmin(x, B); + + v = fmax(R, G); + v = fmax(v, B); + + if (v == x) + return ColorHSB(0.0, 0.0, v, rgb.alpha); + + f = (R == x) ? G - B : ((G == x) ? B - R : R - G); + i = (R == x) ? 3 : ((G == x) ? 5 : 1); + + return ColorHSB(((i - f / (v - x)) / 6.0), (v - x) / v, v, rgb.alpha); +} + +Color ColorHSB::toRGBA() const { + double h = hue * 6.0; + double s = saturation; + double v = brightness; + + double m = 0.0; + double n = 0.0; + double f = 0.0; + + int64_t i = 0; + + if (h == 0) + h = 0.01; + + if (h == 0.0) + return Color(v, v, v, alpha); + + i = static_cast(floor(h)); + + f = h - i; + + if (!(i & 1)) + f = 1 - f; + + m = v * (1 - s); + n = v * (1 - s * f); + + switch (i) { + case 6: + case 0: + return Color(v, n, m, alpha); + case 1: + return Color(n, v, m, alpha); + case 2: + return Color(m, v, n, alpha); + case 3: + return Color(m, n, v, alpha); + case 4: + return Color(n, m, v, alpha); + case 5: + return Color(v, m, n, alpha); + } + return Color(0, 0, 0, alpha); +} + +std::string ColorHSB::toString() const { + std::stringstream stream; + stream << "{" << hue << ", " << saturation << ", " << brightness << ", " + << alpha << "}"; + return stream.str(); +} + +Color::Color(const ColorHSB& hsbColor) : Color(hsbColor.toRGBA()) {} + +std::string Color::toString() const { + std::stringstream stream; + stream << red << "," << green << "," << blue << "," << alpha; + return stream.str(); +} + +void Color::fromString(const std::string& str) { + std::stringstream stream(str); + stream >> red; + stream.ignore(); + stream >> green; + stream.ignore(); + stream >> blue; + stream.ignore(); + stream >> alpha; +} + +} // namespace entity +} // namespace rl diff --git a/impeller/impeller/entity/Color.h b/impeller/impeller/entity/Color.h new file mode 100644 index 0000000000000..a625dd281ddce --- /dev/null +++ b/impeller/impeller/entity/Color.h @@ -0,0 +1,672 @@ +/* + * This source file is part of the Radar project. + * Licensed under the MIT License. See LICENSE file for details. + */ + +#pragma once + +#include +#include + +namespace rl { +namespace entity { + +struct ColorHSB; + +/** + * Represents a RGBA color + */ +struct Color { + /** + * The red color component (0 to 1) + */ + double red; + + /** + * The green color component (0 to 1) + */ + double green; + + /** + * The blue color component (0 to 1) + */ + double blue; + + /** + * The alpha component of the color (0 to 1) + */ + double alpha; + + Color() : red(0.0), green(0.0), blue(0.0), alpha(0.0) {} + + Color(const ColorHSB& hsbColor); + + Color(double r, double g, double b, double a) + : red(r), green(g), blue(b), alpha(a) {} + + bool operator==(const Color& c) const { + return red == c.red && green == c.green && blue == c.blue && + alpha == c.alpha; + } + + Color operator+(const Color& other) const; + + std::string toString() const; + + void fromString(const std::string& str); + + static Color White() { return {1.0, 1.0, 1.0, 1.0}; } + + static Color Black() { return {0.0, 0.0, 0.0, 1.0}; } + + static Color WhiteTransparent() { return {1.0, 1.0, 1.0, 0.0}; } + + static Color BlackTransparent() { return {0.0, 0.0, 0.0, 0.0}; } + + static Color Red() { return {1.0, 0.0, 0.0, 1.0}; } + + static Color Green() { return {0.0, 1.0, 0.0, 1.0}; } + + static Color Blue() { return {0.0, 0.0, 1.0, 1.0}; } + + static Color AliceBlue() { + return {240.0 / 255.0, 248.0 / 255.0, 255.0 / 255.0, 1.0}; + } + + static Color AntiqueWhite() { + return {250.0 / 255.0, 235.0 / 255.0, 215.0 / 255.0, 1.0}; + } + + static Color Aqua() { + return {0.0 / 255.0, 255.0 / 255.0, 255.0 / 255.0, 1.0}; + } + + static Color AquaMarine() { + return {127.0 / 255.0, 255.0 / 255.0, 212.0 / 255.0, 1.0}; + } + + static Color Azure() { + return {240.0 / 255.0, 255.0 / 255.0, 255.0 / 255.0, 1.0}; + } + + static Color Beige() { + return {245.0 / 255.0, 245.0 / 255.0, 220.0 / 255.0, 1.0}; + } + + static Color Bisque() { + return {255.0 / 255.0, 228.0 / 255.0, 196.0 / 255.0, 1.0}; + } + + static Color BlanchedAlmond() { + return {255.0 / 255.0, 235.0 / 255.0, 205.0 / 255.0, 1.0}; + } + + static Color BlueViolet() { + return {138.0 / 255.0, 43.0 / 255.0, 226.0 / 255.0, 1.0}; + } + + static Color Brown() { + return {165.0 / 255.0, 42.0 / 255.0, 42.0 / 255.0, 1.0}; + } + + static Color BurlyWood() { + return {222.0 / 255.0, 184.0 / 255.0, 135.0 / 255.0, 1.0}; + } + + static Color CadetBlue() { + return {95.0 / 255.0, 158.0 / 255.0, 160.0 / 255.0, 1.0}; + } + + static Color Chartreuse() { + return {127.0 / 255.0, 255.0 / 255.0, 0.0 / 255.0, 1.0}; + } + + static Color Chocolate() { + return {210.0 / 255.0, 105.0 / 255.0, 30.0 / 255.0, 1.0}; + } + + static Color Coral() { + return {255.0 / 255.0, 127.0 / 255.0, 80.0 / 255.0, 1.0}; + } + + static Color CornflowerBlue() { + return {100.0 / 255.0, 149.0 / 255.0, 237.0 / 255.0, 1.0}; + } + + static Color Cornsilk() { + return {255.0 / 255.0, 248.0 / 255.0, 220.0 / 255.0, 1.0}; + } + + static Color Crimson() { + return {220.0 / 255.0, 20.0 / 255.0, 60.0 / 255.0, 1.0}; + } + + static Color Cyan() { + return {0.0 / 255.0, 255.0 / 255.0, 255.0 / 255.0, 1.0}; + } + + static Color DarkBlue() { + return {0.0 / 255.0, 0.0 / 255.0, 139.0 / 255.0, 1.0}; + } + + static Color DarkCyan() { + return {0.0 / 255.0, 139.0 / 255.0, 139.0 / 255.0, 1.0}; + } + + static Color DarkGoldenrod() { + return {184.0 / 255.0, 134.0 / 255.0, 11.0 / 255.0, 1.0}; + } + + static Color DarkGray() { + return {169.0 / 255.0, 169.0 / 255.0, 169.0 / 255.0, 1.0}; + } + + static Color DarkGreen() { + return {0.0 / 255.0, 100.0 / 255.0, 0.0 / 255.0, 1.0}; + } + + static Color DarkGrey() { + return {169.0 / 255.0, 169.0 / 255.0, 169.0 / 255.0, 1.0}; + } + + static Color DarkKhaki() { + return {189.0 / 255.0, 183.0 / 255.0, 107.0 / 255.0, 1.0}; + } + + static Color DarkMagenta() { + return {139.0 / 255.0, 0.0 / 255.0, 139.0 / 255.0, 1.0}; + } + + static Color DarkOliveGreen() { + return {85.0 / 255.0, 107.0 / 255.0, 47.0 / 255.0, 1.0}; + } + + static Color DarkOrange() { + return {255.0 / 255.0, 140.0 / 255.0, 0.0 / 255.0, 1.0}; + } + + static Color DarkOrchid() { + return {153.0 / 255.0, 50.0 / 255.0, 204.0 / 255.0, 1.0}; + } + + static Color DarkRed() { + return {139.0 / 255.0, 0.0 / 255.0, 0.0 / 255.0, 1.0}; + } + + static Color DarkSalmon() { + return {233.0 / 255.0, 150.0 / 255.0, 122.0 / 255.0, 1.0}; + } + + static Color DarkSeagreen() { + return {143.0 / 255.0, 188.0 / 255.0, 143.0 / 255.0, 1.0}; + } + + static Color DarkSlateBlue() { + return {72.0 / 255.0, 61.0 / 255.0, 139.0 / 255.0, 1.0}; + } + + static Color DarkSlateGray() { + return {47.0 / 255.0, 79.0 / 255.0, 79.0 / 255.0, 1.0}; + } + + static Color DarkSlateGrey() { + return {47.0 / 255.0, 79.0 / 255.0, 79.0 / 255.0, 1.0}; + } + + static Color DarkTurquoise() { + return {0.0 / 255.0, 206.0 / 255.0, 209.0 / 255.0, 1.0}; + } + + static Color DarkViolet() { + return {148.0 / 255.0, 0.0 / 255.0, 211.0 / 255.0, 1.0}; + } + + static Color DeepPink() { + return {255.0 / 255.0, 20.0 / 255.0, 147.0 / 255.0, 1.0}; + } + + static Color DeepSkyBlue() { + return {0.0 / 255.0, 191.0 / 255.0, 255.0 / 255.0, 1.0}; + } + + static Color DimGray() { + return {105.0 / 255.0, 105.0 / 255.0, 105.0 / 255.0, 1.0}; + } + + static Color DimGrey() { + return {105.0 / 255.0, 105.0 / 255.0, 105.0 / 255.0, 1.0}; + } + + static Color DodgerBlue() { + return {30.0 / 255.0, 144.0 / 255.0, 255.0 / 255.0, 1.0}; + } + + static Color Firebrick() { + return {178.0 / 255.0, 34.0 / 255.0, 34.0 / 255.0, 1.0}; + } + + static Color FloralWhite() { + return {255.0 / 255.0, 250.0 / 255.0, 240.0 / 255.0, 1.0}; + } + + static Color ForestGreen() { + return {34.0 / 255.0, 139.0 / 255.0, 34.0 / 255.0, 1.0}; + } + + static Color Fuchsia() { + return {255.0 / 255.0, 0.0 / 255.0, 255.0 / 255.0, 1.0}; + } + + static Color Gainsboro() { + return {220.0 / 255.0, 220.0 / 255.0, 220.0 / 255.0, 1.0}; + } + + static Color Ghostwhite() { + return {248.0 / 255.0, 248.0 / 255.0, 255.0 / 255.0, 1.0}; + } + + static Color Gold() { + return {255.0 / 255.0, 215.0 / 255.0, 0.0 / 255.0, 1.0}; + } + + static Color Goldenrod() { + return {218.0 / 255.0, 165.0 / 255.0, 32.0 / 255.0, 1.0}; + } + + static Color Gray() { + return {128.0 / 255.0, 128.0 / 255.0, 128.0 / 255.0, 1.0}; + } + + static Color GreenYellow() { + return {173.0 / 255.0, 255.0 / 255.0, 47.0 / 255.0, 1.0}; + } + + static Color Grey() { + return {128.0 / 255.0, 128.0 / 255.0, 128.0 / 255.0, 1.0}; + } + + static Color Honeydew() { + return {240.0 / 255.0, 255.0 / 255.0, 240.0 / 255.0, 1.0}; + } + + static Color HotPink() { + return {255.0 / 255.0, 105.0 / 255.0, 180.0 / 255.0, 1.0}; + } + + static Color IndianRed() { + return {205.0 / 255.0, 92.0 / 255.0, 92.0 / 255.0, 1.0}; + } + + static Color Indigo() { + return {75.0 / 255.0, 0.0 / 255.0, 130.0 / 255.0, 1.0}; + } + + static Color Ivory() { + return {255.0 / 255.0, 255.0 / 255.0, 240.0 / 255.0, 1.0}; + } + + static Color Khaki() { + return {240.0 / 255.0, 230.0 / 255.0, 140.0 / 255.0, 1.0}; + } + + static Color Lavender() { + return {230.0 / 255.0, 230.0 / 255.0, 250.0 / 255.0, 1.0}; + } + + static Color LavenderBlush() { + return {255.0 / 255.0, 240.0 / 255.0, 245.0 / 255.0, 1.0}; + } + + static Color LawnGreen() { + return {124.0 / 255.0, 252.0 / 255.0, 0.0 / 255.0, 1.0}; + } + + static Color LemonChiffon() { + return {255.0 / 255.0, 250.0 / 255.0, 205.0 / 255.0, 1.0}; + } + + static Color LightBlue() { + return {173.0 / 255.0, 216.0 / 255.0, 230.0 / 255.0, 1.0}; + } + + static Color LightCoral() { + return {240.0 / 255.0, 128.0 / 255.0, 128.0 / 255.0, 1.0}; + } + + static Color LightCyan() { + return {224.0 / 255.0, 255.0 / 255.0, 255.0 / 255.0, 1.0}; + } + + static Color LightGoldenrodYellow() { + return {50.0 / 255.0, 250.0 / 255.0, 210.0 / 255.0, 1.0}; + } + + static Color LightGray() { + return {211.0 / 255.0, 211.0 / 255.0, 211.0 / 255.0, 1.0}; + } + + static Color LightGreen() { + return {144.0 / 255.0, 238.0 / 255.0, 144.0 / 255.0, 1.0}; + } + + static Color LightGrey() { + return {211.0 / 255.0, 211.0 / 255.0, 211.0 / 255.0, 1.0}; + } + + static Color LightPink() { + return {255.0 / 255.0, 182.0 / 255.0, 193.0 / 255.0, 1.0}; + } + + static Color LightSalmon() { + return {255.0 / 255.0, 160.0 / 255.0, 122.0 / 255.0, 1.0}; + } + + static Color LightSeaGreen() { + return {32.0 / 255.0, 178.0 / 255.0, 170.0 / 255.0, 1.0}; + } + + static Color LightSkyBlue() { + return {135.0 / 255.0, 206.0 / 255.0, 250.0 / 255.0, 1.0}; + } + + static Color LightSlateGray() { + return {119.0 / 255.0, 136.0 / 255.0, 153.0 / 255.0, 1.0}; + } + + static Color LightSlateGrey() { + return {119.0 / 255.0, 136.0 / 255.0, 153.0 / 255.0, 1.0}; + } + + static Color LightSteelBlue() { + return {176.0 / 255.0, 196.0 / 255.0, 222.0 / 255.0, 1.0}; + } + + static Color LightYellow() { + return {255.0 / 255.0, 255.0 / 255.0, 224.0 / 255.0, 1.0}; + } + + static Color Lime() { return {0.0 / 255.0, 255.0 / 255.0, 0.0 / 255.0, 1.0}; } + + static Color LimeGreen() { + return {50.0 / 255.0, 205.0 / 255.0, 50.0 / 255.0, 1.0}; + } + + static Color Linen() { + return {250.0 / 255.0, 240.0 / 255.0, 230.0 / 255.0, 1.0}; + } + + static Color Magenta() { + return {255.0 / 255.0, 0.0 / 255.0, 255.0 / 255.0, 1.0}; + } + + static Color Maroon() { + return {128.0 / 255.0, 0.0 / 255.0, 0.0 / 255.0, 1.0}; + } + + static Color MediumAquamarine() { + return {102.0 / 255.0, 205.0 / 255.0, 170.0 / 255.0, 1.0}; + } + + static Color MediumBlue() { + return {0.0 / 255.0, 0.0 / 255.0, 205.0 / 255.0, 1.0}; + } + + static Color MediumOrchid() { + return {186.0 / 255.0, 85.0 / 255.0, 211.0 / 255.0, 1.0}; + } + + static Color MediumPurple() { + return {147.0 / 255.0, 112.0 / 255.0, 219.0 / 255.0, 1.0}; + } + + static Color MediumSeagreen() { + return {60.0 / 255.0, 179.0 / 255.0, 113.0 / 255.0, 1.0}; + } + + static Color MediumSlateBlue() { + return {123.0 / 255.0, 104.0 / 255.0, 238.0 / 255.0, 1.0}; + } + + static Color MediumSpringGreen() { + return {0.0 / 255.0, 250.0 / 255.0, 154.0 / 255.0, 1.0}; + } + + static Color MediumTurquoise() { + return {72.0 / 255.0, 209.0 / 255.0, 204.0 / 255.0, 1.0}; + } + + static Color MediumVioletRed() { + return {199.0 / 255.0, 21.0 / 255.0, 133.0 / 255.0, 1.0}; + } + + static Color MidnightBlue() { + return {25.0 / 255.0, 25.0 / 255.0, 112.0 / 255.0, 1.0}; + } + + static Color MintCream() { + return {245.0 / 255.0, 255.0 / 255.0, 250.0 / 255.0, 1.0}; + } + + static Color MistyRose() { + return {255.0 / 255.0, 228.0 / 255.0, 225.0 / 255.0, 1.0}; + } + + static Color Moccasin() { + return {255.0 / 255.0, 228.0 / 255.0, 181.0 / 255.0, 1.0}; + } + + static Color NavajoWhite() { + return {255.0 / 255.0, 222.0 / 255.0, 173.0 / 255.0, 1.0}; + } + + static Color Navy() { return {0.0 / 255.0, 0.0 / 255.0, 128.0 / 255.0, 1.0}; } + + static Color OldLace() { + return {253.0 / 255.0, 245.0 / 255.0, 230.0 / 255.0, 1.0}; + } + + static Color Olive() { + return {128.0 / 255.0, 128.0 / 255.0, 0.0 / 255.0, 1.0}; + } + + static Color OliveDrab() { + return {107.0 / 255.0, 142.0 / 255.0, 35.0 / 255.0, 1.0}; + } + + static Color Orange() { + return {255.0 / 255.0, 165.0 / 255.0, 0.0 / 255.0, 1.0}; + } + + static Color OrangeRed() { + return {255.0 / 255.0, 69.0 / 255.0, 0.0 / 255.0, 1.0}; + } + + static Color Orchid() { + return {218.0 / 255.0, 112.0 / 255.0, 214.0 / 255.0, 1.0}; + } + + static Color PaleGoldenrod() { + return {238.0 / 255.0, 232.0 / 255.0, 170.0 / 255.0, 1.0}; + } + + static Color PaleGreen() { + return {152.0 / 255.0, 251.0 / 255.0, 152.0 / 255.0, 1.0}; + } + + static Color PaleTurquoise() { + return {175.0 / 255.0, 238.0 / 255.0, 238.0 / 255.0, 1.0}; + } + + static Color PaleVioletRed() { + return {219.0 / 255.0, 112.0 / 255.0, 147.0 / 255.0, 1.0}; + } + + static Color PapayaWhip() { + return {255.0 / 255.0, 239.0 / 255.0, 213.0 / 255.0, 1.0}; + } + + static Color Peachpuff() { + return {255.0 / 255.0, 218.0 / 255.0, 185.0 / 255.0, 1.0}; + } + + static Color Peru() { + return {205.0 / 255.0, 133.0 / 255.0, 63.0 / 255.0, 1.0}; + } + + static Color Pink() { + return {255.0 / 255.0, 192.0 / 255.0, 203.0 / 255.0, 1.0}; + } + + static Color Plum() { + return {221.0 / 255.0, 160.0 / 255.0, 221.0 / 255.0, 1.0}; + } + + static Color PowderBlue() { + return {176.0 / 255.0, 224.0 / 255.0, 230.0 / 255.0, 1.0}; + } + + static Color Purple() { + return {128.0 / 255.0, 0.0 / 255.0, 128.0 / 255.0, 1.0}; + } + + static Color RosyBrown() { + return {188.0 / 255.0, 143.0 / 255.0, 143.0 / 255.0, 1.0}; + } + + static Color RoyalBlue() { + return {65.0 / 255.0, 105.0 / 255.0, 225.0 / 255.0, 1.0}; + } + + static Color SaddleBrown() { + return {139.0 / 255.0, 69.0 / 255.0, 19.0 / 255.0, 1.0}; + } + + static Color Salmon() { + return {250.0 / 255.0, 128.0 / 255.0, 114.0 / 255.0, 1.0}; + } + + static Color SandyBrown() { + return {244.0 / 255.0, 164.0 / 255.0, 96.0 / 255.0, 1.0}; + } + + static Color Seagreen() { + return {46.0 / 255.0, 139.0 / 255.0, 87.0 / 255.0, 1.0}; + } + + static Color Seashell() { + return {255.0 / 255.0, 245.0 / 255.0, 238.0 / 255.0, 1.0}; + } + + static Color Sienna() { + return {160.0 / 255.0, 82.0 / 255.0, 45.0 / 255.0, 1.0}; + } + + static Color Silver() { + return {192.0 / 255.0, 192.0 / 255.0, 192.0 / 255.0, 1.0}; + } + + static Color SkyBlue() { + return {135.0 / 255.0, 206.0 / 255.0, 235.0 / 255.0, 1.0}; + } + + static Color SlateBlue() { + return {106.0 / 255.0, 90.0 / 255.0, 205.0 / 255.0, 1.0}; + } + + static Color SlateGray() { + return {112.0 / 255.0, 128.0 / 255.0, 144.0 / 255.0, 1.0}; + } + + static Color SlateGrey() { + return {112.0 / 255.0, 128.0 / 255.0, 144.0 / 255.0, 1.0}; + } + + static Color Snow() { + return {255.0 / 255.0, 250.0 / 255.0, 250.0 / 255.0, 1.0}; + } + + static Color SpringGreen() { + return {0.0 / 255.0, 255.0 / 255.0, 127.0 / 255.0, 1.0}; + } + + static Color SteelBlue() { + return {70.0 / 255.0, 130.0 / 255.0, 180.0 / 255.0, 1.0}; + } + + static Color Tan() { + return {210.0 / 255.0, 180.0 / 255.0, 140.0 / 255.0, 1.0}; + } + + static Color Teal() { + return {0.0 / 255.0, 128.0 / 255.0, 128.0 / 255.0, 1.0}; + } + + static Color Thistle() { + return {216.0 / 255.0, 191.0 / 255.0, 216.0 / 255.0, 1.0}; + } + + static Color Tomato() { + return {255.0 / 255.0, 99.0 / 255.0, 71.0 / 255.0, 1.0}; + } + + static Color Turquoise() { + return {64.0 / 255.0, 224.0 / 255.0, 208.0 / 255.0, 1.0}; + } + + static Color Violet() { + return {238.0 / 255.0, 130.0 / 255.0, 238.0 / 255.0, 1.0}; + } + + static Color Wheat() { + return {245.0 / 255.0, 222.0 / 255.0, 179.0 / 255.0, 1.0}; + } + + static Color Whitesmoke() { + return {245.0 / 255.0, 245.0 / 255.0, 245.0 / 255.0, 1.0}; + } + + static Color Yellow() { + return {255.0 / 255.0, 255.0 / 255.0, 0.0 / 255.0, 1.0}; + } + + static Color YellowGreen() { + return {154.0 / 255.0, 205.0 / 255.0, 50.0 / 255.0, 1.0}; + } +}; + +/** + * Represents a color by its constituent hue, saturation, brightness and alpha + */ +struct ColorHSB { + /** + * The hue of the color (0 to 1) + */ + double hue; + + /** + * The saturation of the color (0 to 1) + */ + double saturation; + + /** + * The brightness of the color (0 to 1) + */ + double brightness; + + /** + * The alpha of the color (0 to 1) + */ + double alpha; + + ColorHSB(double h, double s, double b, double a) + : hue(h), saturation(s), brightness(b), alpha(a) {} + + static ColorHSB FromRGB(Color rgb); + + Color toRGBA() const; + + std::string toString() const; +}; + +} // namespace entity +} // namespace rl diff --git a/impeller/impeller/entity/Entity.cc b/impeller/impeller/entity/Entity.cc new file mode 100644 index 0000000000000..e80700e238887 --- /dev/null +++ b/impeller/impeller/entity/Entity.cc @@ -0,0 +1,300 @@ +/* + * This source file is part of the Radar project. + * Licensed under the MIT License. See LICENSE file for details. + */ + +#include + +namespace rl { +namespace entity { + +Entity::Entity(core::Name identifier, UpdateCallback updateCallback) + : _identifier(identifier), + _anchorPoint(geom::Point(0.5, 0.5)), + _opacity(1.0), + _strokeSize(1.0), + _updateCallback(updateCallback) {} + +Entity::Entity(Entity&&) = default; + +Entity::~Entity() = default; + +void Entity::mergeProperties(const Entity& entity, PropertyMaskType only) { + RL_ASSERT(_identifier == entity._identifier); + + if (only & PropertyMask::BoundsMask) { + _bounds = entity._bounds; + } + + if (only & PropertyMask::PositionMask) { + _position = entity._position; + } + + if (only & PropertyMask::AnchorPointMask) { + _anchorPoint = entity._anchorPoint; + } + + if (only & PropertyMask::TransformationMask) { + _transformation = entity._transformation; + } + + if (only & PropertyMask::BackgroundColorMask) { + _backgroundColor = entity._backgroundColor; + } + + if (only & PropertyMask::OpacityMask) { + _opacity = entity._opacity; + } + + if (only & PropertyMask::StrokeSizeMask) { + _strokeSize = entity._strokeSize; + } + + if (only & PropertyMask::StrokeColorMask) { + _strokeColor = entity._strokeColor; + } + + if (only & PropertyMask::ContentsMask) { + _contents = entity._contents; + } + + if (only & PropertyMask::PathMask) { + _path = entity._path; + } +} + +core::Name Entity::identifier() const { + return _identifier; +} + +geom::Rect Entity::frame() const { + geom::Point origin(_position.x - (_bounds.size.width * _anchorPoint.x), + _position.y - (_bounds.size.height * _anchorPoint.y)); + + return geom::Rect(origin, _bounds.size); +} + +void Entity::setFrame(const geom::Rect& frame) { + setBounds(geom::Rect(_bounds.origin, frame.size)); + setPosition( + geom::Point(frame.origin.x + (_anchorPoint.x * frame.size.width), + frame.origin.y + (_anchorPoint.y * frame.size.height))); +} + +const geom::Rect& Entity::bounds() const { + return _bounds; +} + +void Entity::setBounds(const geom::Rect& bounds) { + _bounds = bounds; + notifyInterfaceIfNecessary(Property::Bounds); +} + +const geom::Point& Entity::position() const { + return _position; +} + +void Entity::setPosition(const geom::Point& position) { + _position = position; + notifyInterfaceIfNecessary(Property::Position); +} + +const geom::Point& Entity::anchorPoint() const { + return _anchorPoint; +} + +void Entity::setAnchorPoint(const geom::Point& anchorPoint) { + _anchorPoint = anchorPoint; + notifyInterfaceIfNecessary(Property::AnchorPoint); +} + +const geom::Matrix& Entity::transformation() const { + return _transformation; +} + +void Entity::setTransformation(const geom::Matrix& transformation) { + _transformation = transformation; + notifyInterfaceIfNecessary(Property::Transformation); +} + +geom::Matrix Entity::modelMatrix() const { + /* + * The translation accounts for the offset in the origin of the bounds + * of the entity and its position about its anchor point. + */ + auto translation = geom::Matrix::Translation( + {-_bounds.origin.x + _position.x - (_bounds.size.width * _anchorPoint.x), + -_bounds.origin.y + _position.y - + (_bounds.size.height * _anchorPoint.y)}); + + /* + * The transformation of an entity is applied about is anchor point. However, + * if the transformation is identity, we can avoid having to calculate the + * matrix adjustment and also the two matrix multiplications. + */ + + if (_transformation.isIdentity()) { + return translation; + } + + auto anchorAdjustment = + geom::Matrix::Translation({-_anchorPoint.x * _bounds.size.width, + -_anchorPoint.y * _bounds.size.height}); + + return translation * anchorAdjustment.invert() * _transformation * + anchorAdjustment; +} + +const Color& Entity::backgroundColor() const { + return _backgroundColor; +} + +void Entity::setBackgroundColor(const Color& backgroundColor) { + _backgroundColor = backgroundColor; + notifyInterfaceIfNecessary(Property::BackgroundColor); +} + +const double& Entity::opacity() const { + return _opacity; +} + +void Entity::setOpacity(double opacity) { + _opacity = opacity; + notifyInterfaceIfNecessary(Property::Opacity); +} + +const Color& Entity::strokeColor() const { + return _strokeColor; +} + +void Entity::setStrokeColor(const Color& strokeColor) { + _strokeColor = strokeColor; + notifyInterfaceIfNecessary(Property::StrokeColor); +} + +double Entity::strokeSize() const { + return _strokeSize; +} + +void Entity::setStrokeSize(double strokeSize) { + _strokeSize = strokeSize; + notifyInterfaceIfNecessary(Property::StrokeSize); +} + +const image::Image& Entity::contents() const { + return _contents; +} + +void Entity::setContents(image::Image image) { + _contents = std::move(image); + notifyInterfaceIfNecessary(Property::Contents); +} + +const geom::Path& Entity::path() const { + return _path; +} + +void Entity::setPath(geom::Path path) { + _path = std::move(path); + notifyInterfaceIfNecessary(Property::Path); +} + +void Entity::notifyInterfaceIfNecessary(Property property, + core::Name other) const { + if (_updateCallback) { + _updateCallback(*this, property, other); + } +} + +enum ArchiveKey { + Identifier, + Bounds, + Position, + AnchorPoint, + Transformation, + BackgroundColor, + Opacity, + StrokeColor, + StrokeSize, +}; + +const core::ArchiveDef Entity::ArchiveDefinition = { + /* .superClass = */ nullptr, + /* .className = */ "Entity", + /* .autoAssignName = */ false, + /* .members = */ + { + ArchiveKey::Identifier, // + ArchiveKey::Bounds, // + ArchiveKey::Position, // + ArchiveKey::AnchorPoint, // + ArchiveKey::Transformation, // + ArchiveKey::BackgroundColor, // + ArchiveKey::Opacity, // + ArchiveKey::StrokeColor, // + ArchiveKey::StrokeSize, // + }, +}; + +Entity::ArchiveName Entity::archiveName() const { + return *_identifier.handle(); +} + +bool Entity::serialize(core::ArchiveItem& item) const { + RL_RETURN_IF_FALSE(item.encode(ArchiveKey::Identifier, _identifier)); + + RL_RETURN_IF_FALSE(item.encode(ArchiveKey::Bounds, _bounds.toString())); + + RL_RETURN_IF_FALSE(item.encode(ArchiveKey::Position, _position.toString())); + + RL_RETURN_IF_FALSE( + item.encode(ArchiveKey::AnchorPoint, _anchorPoint.toString())); + + RL_RETURN_IF_FALSE( + item.encode(ArchiveKey::Transformation, _transformation.toString())); + + RL_RETURN_IF_FALSE( + item.encode(ArchiveKey::BackgroundColor, _backgroundColor.toString())); + + RL_RETURN_IF_FALSE(item.encode(ArchiveKey::Opacity, _opacity)); + + RL_RETURN_IF_FALSE( + item.encode(ArchiveKey::StrokeColor, _strokeColor.toString())); + + RL_RETURN_IF_FALSE(item.encode(ArchiveKey::StrokeSize, _strokeSize)); + + return true; +} + +bool Entity::deserialize(core::ArchiveItem& item, core::Namespace* ns) { + std::string decoded; + + RL_RETURN_IF_FALSE(item.decode(ArchiveKey::Identifier, _identifier, ns)); + + RL_RETURN_IF_FALSE(item.decode(ArchiveKey::Bounds, decoded)); + _bounds.fromString(decoded); + + RL_RETURN_IF_FALSE(item.decode(ArchiveKey::Position, decoded)); + _position.fromString(decoded); + + RL_RETURN_IF_FALSE(item.decode(ArchiveKey::AnchorPoint, decoded)); + _anchorPoint.fromString(decoded); + + RL_RETURN_IF_FALSE(item.decode(ArchiveKey::Transformation, decoded)); + _transformation.fromString(decoded); + + RL_RETURN_IF_FALSE(item.decode(ArchiveKey::BackgroundColor, decoded)); + _backgroundColor.fromString(decoded); + + RL_RETURN_IF_FALSE(item.decode(ArchiveKey::Opacity, _opacity)); + + RL_RETURN_IF_FALSE(item.decode(ArchiveKey::StrokeColor, decoded)); + _strokeColor.fromString(decoded); + + RL_RETURN_IF_FALSE(item.decode(ArchiveKey::StrokeSize, _strokeSize)); + + return true; +} + +} // namespace entity +} // namespace rl diff --git a/impeller/impeller/entity/Entity.h b/impeller/impeller/entity/Entity.h new file mode 100644 index 0000000000000..1610304ef2a4d --- /dev/null +++ b/impeller/impeller/entity/Entity.h @@ -0,0 +1,233 @@ +/* + * This source file is part of the Radar project. + * Licensed under the MIT License. See LICENSE file for details. + */ + +#pragma once + +#include +#include +#include +#include +#include +#include +#include + +namespace rl { +namespace entity { + +class Entity : public core::ArchiveSerializable { + public: + using PropertyMaskType = uint16_t; + + enum class Property : PropertyMaskType { + None, + + AddedTo, + RemovedFrom, + Bounds, + Position, + AnchorPoint, + Transformation, + BackgroundColor, + Contents, + Path, + Opacity, + StrokeSize, + StrokeColor, + MakeRoot, + + Sentinel, + }; + +#define RL_MASK(x) x##Mask = (1 << static_cast(Property::x)) + enum PropertyMask { + RL_MASK(AddedTo), + RL_MASK(RemovedFrom), + RL_MASK(Bounds), + RL_MASK(Position), + RL_MASK(AnchorPoint), + RL_MASK(Transformation), + RL_MASK(BackgroundColor), + RL_MASK(Contents), + RL_MASK(Path), + RL_MASK(Opacity), + RL_MASK(StrokeSize), + RL_MASK(StrokeColor), + RL_MASK(MakeRoot), + }; +#undef RL_MASK + + using UpdateCallback = std::function; + + Entity(core::Name identifier, UpdateCallback updateCallback = nullptr); + + Entity(Entity&& entity); + + virtual ~Entity(); + + core::Name identifier() const; + + /** + * The frame specifies the origin and size of the entity in the coordinate + * space of its parent. This is a computed property derived from the bounds + * of the entity and its position. + * + * @return the frame of the entity + */ + geom::Rect frame() const; + + /** + * Set the frame of the entity + * + * @param frame the new frame + */ + void setFrame(const geom::Rect& frame); + + /** + * The bounds specifies the origin and size of the entity in its own + * coordinate space. + * + * @return the bounds of the entity + */ + const geom::Rect& bounds() const; + + /** + * Set the bounds of the entity + * + * @param bounds the new bounds + */ + void setBounds(const geom::Rect& bounds); + + /** + * The position specifies the coordinates of the anchor position of the + * entity relative to its parent + * + * @return the position of the entity + */ + const geom::Point& position() const; + + /** + * Sets the position of the entity + * + * @param point the new position + */ + void setPosition(const geom::Point& point); + + /** + * The position of the anchor point within this node in unit space + * + * @return the anchor point + */ + const geom::Point& anchorPoint() const; + + /** + * Sets the new anchor point of this node + * + * @param anchorPoint the new anchor point + */ + void setAnchorPoint(const geom::Point& anchorPoint); + + /** + * The transformation that is applied to the entity about its anchor point + * + * @return the transformation applied to the node + */ + const geom::Matrix& transformation() const; + + /** + * Sets the transformation of the entity + * + * @param transformation the new transformation + */ + void setTransformation(const geom::Matrix& transformation); + + /** + * The model matrix of the entity + * + * @return the view matrix + */ + geom::Matrix modelMatrix() const; + + /** + * The background color of the entity + * + * @return the background color + */ + const Color& backgroundColor() const; + + /** + * Set the new background color of the entity + * + * @param backgroundColor the new background color + */ + void setBackgroundColor(const Color& backgroundColor); + + /** + * The opacity of the entity. 0.0 is fully transparent and 1.0 is fully + * opaque. Default it 1.0. + * + * @return the opacity of the entity + */ + const double& opacity() const; + + /** + * Set the new opacity of the entity + * + * @param opacity the new opacity + */ + void setOpacity(double opacity); + + const Color& strokeColor() const; + + void setStrokeColor(const Color& strokeColor); + + double strokeSize() const; + + void setStrokeSize(double strokeSize); + + const image::Image& contents() const; + + void setContents(image::Image image); + + const geom::Path& path() const; + + void setPath(geom::Path path); + + void mergeProperties(const Entity& entity, PropertyMaskType only); + + static const core::ArchiveDef ArchiveDefinition; + + ArchiveName archiveName() const override; + + bool serialize(core::ArchiveItem& item) const override; + + bool deserialize(core::ArchiveItem& item, core::Namespace* ns) override; + + protected: + core::Name _identifier; + geom::Rect _bounds; + geom::Point _position; + geom::Point _anchorPoint; + geom::Matrix _transformation; + Color _backgroundColor; + image::Image _contents; + geom::Path _path; + double _opacity; + Color _strokeColor; + double _strokeSize; + + void notifyInterfaceIfNecessary( + Property property, + core::Name identifier = core::Name() /* dead name */) const; + + private: + UpdateCallback _updateCallback; + + RL_DISALLOW_COPY_AND_ASSIGN(Entity); +}; + +} // namespace entity +} // namespace rl diff --git a/impeller/impeller/image/DataImageSource.cc b/impeller/impeller/image/DataImageSource.cc new file mode 100644 index 0000000000000..12d96c6029583 --- /dev/null +++ b/impeller/impeller/image/DataImageSource.cc @@ -0,0 +1,51 @@ +/* + * This source file is part of the Radar project. + * Licensed under the MIT License. See LICENSE file for details. + */ + +#include "DataImageSource.h" +#include + +namespace rl { +namespace image { + +DataImageSource::DataImageSource() {} + +DataImageSource::DataImageSource(core::Allocation allocation) + : _allocation(std::move(allocation)) {} + +uint8_t* DataImageSource::sourceData() const { + return _allocation.data(); +} + +size_t DataImageSource::sourceDataSize() const { + return _allocation.size(); +} + +void DataImageSource::onPrepareForUse() { + /* + * Nothing to do since we already have an explicit allocation. + */ +} + +void DataImageSource::onDoneUsing() { + /* + * Nothing to do since we have an explicit allocate and we may need to be + * prepared again. + */ +} + +bool DataImageSource::serialize(core::Message& message) const { + return message.encode(_allocation); +} + +bool DataImageSource::deserialize(core::Message& message, core::Namespace* ns) { + return message.decode(_allocation, ns); +} + +ImageSource::Type DataImageSource::type() const { + return ImageSource::Type::Data; +} + +} // namespace image +} // namespace rl diff --git a/impeller/impeller/image/DataImageSource.h b/impeller/impeller/image/DataImageSource.h new file mode 100644 index 0000000000000..de25b9f6db86d --- /dev/null +++ b/impeller/impeller/image/DataImageSource.h @@ -0,0 +1,42 @@ +/* + * This source file is part of the Radar project. + * Licensed under the MIT License. See LICENSE file for details. + */ + +#pragma once + +#include +#include +#include "ImageSource.h" + +namespace rl { +namespace image { + +class DataImageSource : public ImageSource { + public: + DataImageSource(); + + DataImageSource(core::Allocation allocation); + + bool serialize(core::Message& message) const override; + + bool deserialize(core::Message& message, core::Namespace* ns) override; + + private: + core::Allocation _allocation; + + ImageSource::Type type() const override; + + uint8_t* sourceData() const override; + + size_t sourceDataSize() const override; + + void onPrepareForUse() override; + + void onDoneUsing() override; + + RL_DISALLOW_COPY_AND_ASSIGN(DataImageSource); +}; + +} // namespace image +} // namespace rl diff --git a/impeller/impeller/image/FileImageSource.cc b/impeller/impeller/image/FileImageSource.cc new file mode 100644 index 0000000000000..cb9606cdcfac1 --- /dev/null +++ b/impeller/impeller/image/FileImageSource.cc @@ -0,0 +1,64 @@ +/* + * This source file is part of the Radar project. + * Licensed under the MIT License. See LICENSE file for details. + */ + +#include "FileImageSource.h" +#include +#include + +namespace rl { +namespace image { + +FileImageSource::FileImageSource() {} + +FileImageSource::FileImageSource(core::FileHandle fileHandle) + : _handle(std::make_shared(std::move(fileHandle))) {} + +uint8_t* FileImageSource::sourceData() const { + return _mapping == nullptr ? nullptr : _mapping->mapping(); +} + +size_t FileImageSource::sourceDataSize() const { + return _mapping == nullptr ? 0 : _mapping->size(); +} + +void FileImageSource::onPrepareForUse() { + if (_handle == nullptr || !_handle->isValid()) { + return; + } + + _mapping = std::make_unique(*_handle); +} + +void FileImageSource::onDoneUsing() { + _mapping = nullptr; +} + +bool FileImageSource::serialize(core::Message& message) const { + return message.encode(_handle); +} + +bool FileImageSource::deserialize(core::Message& message, core::Namespace* ns) { + core::RawAttachment attachment; + + if (!message.decode(attachment)) { + return false; + } + + _handle = std::make_shared(std::move(attachment)); + + /* + * Clear our old mapping if present so we can prepare for another use. + */ + _mapping = nullptr; + + return true; +} + +ImageSource::Type FileImageSource::type() const { + return ImageSource::Type::File; +} + +} // namespace image +} // namespace rl diff --git a/impeller/impeller/image/FileImageSource.h b/impeller/impeller/image/FileImageSource.h new file mode 100644 index 0000000000000..9b67cbf506d62 --- /dev/null +++ b/impeller/impeller/image/FileImageSource.h @@ -0,0 +1,43 @@ +/* + * This source file is part of the Radar project. + * Licensed under the MIT License. See LICENSE file for details. + */ + +#pragma once + +#include +#include +#include "ImageSource.h" + +namespace rl { +namespace image { + +class FileImageSource : public ImageSource { + public: + FileImageSource(); + + FileImageSource(core::FileHandle fileHandle); + + bool serialize(core::Message& message) const override; + + bool deserialize(core::Message& message, core::Namespace* ns) override; + + private: + std::shared_ptr _handle; + std::unique_ptr _mapping; + + ImageSource::Type type() const override; + + uint8_t* sourceData() const override; + + size_t sourceDataSize() const override; + + void onPrepareForUse() override; + + void onDoneUsing() override; + + RL_DISALLOW_COPY_AND_ASSIGN(FileImageSource); +}; + +} // namespace image +} // namespace rl diff --git a/impeller/impeller/image/Image.cc b/impeller/impeller/image/Image.cc new file mode 100644 index 0000000000000..35d00dea2d61b --- /dev/null +++ b/impeller/impeller/image/Image.cc @@ -0,0 +1,150 @@ +/* + * This source file is part of the Radar project. + * Licensed under the MIT License. See LICENSE file for details. + */ + +#include +#include +#include +#include "ImageSource.h" + +namespace rl { +namespace image { + +Image::Image() = default; + +Image::Image(core::Allocation sourceAllocation) + : _source(ImageSource::Create(std::move(sourceAllocation))) {} + +Image::Image(core::FileHandle sourceFile) + : _source(ImageSource::Create(std::move(sourceFile))) {} + +Image::~Image() = default; + +bool Image::serialize(core::Message& message) const { + if (_source == nullptr) { + return false; + } + + /* + * Encode the type. + */ + RL_RETURN_IF_FALSE(message.encode(_source->type())); + + /* + * Encode the image source. + */ + RL_RETURN_IF_FALSE(_source->serialize(message)); + + return true; +} + +bool Image::deserialize(core::Message& message, core::Namespace* ns) { + auto type = ImageSource::Type::Unknown; + + /* + * Decode the type. + */ + RL_RETURN_IF_FALSE(message.decode(type, ns)) + + /* + * Create and decode the image source of that type. + */ + auto source = ImageSource::ImageSourceForType(type); + if (source == nullptr) { + return false; + } + RL_RETURN_IF_FALSE(source->deserialize(message, ns)); + _source = source; + + return true; +} + +ImageResult Image::decode() const { + if (_source == nullptr) { + return {}; + } + + _source->prepareForUse(); + + if (_source->sourceDataSize() == 0) { + RL_LOG("Source data for image decoding was zero sized."); + return {}; + } + + int width = 0; + int height = 0; + int comps = 0; + + stbi_uc* decoded = + stbi_load_from_memory(_source->sourceData(), // Source Data + _source->sourceDataSize(), // Source Data Size + &width, // Out: Width + &height, // Out: Height + &comps, // Out: Components + STBI_default); + + auto destinationAllocation = + core::Allocation{decoded, width * height * comps * sizeof(stbi_uc)}; + + /* + * If either the decoded allocation is null or the size works out to be zero, + * the allocation will mark itself as not ready and we know that the decode + * job failed. + */ + + if (!destinationAllocation.isReady()) { + RL_LOG("Destination allocation for image decoding was null."); + return {}; + } + + /* + * Make sure we got a valid component set. + */ + auto components = ImageResult::Components::Invalid; + + switch (comps) { + case STBI_grey: + components = ImageResult::Components::Grey; + break; + case STBI_grey_alpha: + components = ImageResult::Components::GreyAlpha; + break; + case STBI_rgb: + components = ImageResult::Components::RGB; + break; + case STBI_rgb_alpha: + components = ImageResult::Components::RGBA; + break; + default: + components = ImageResult::Components::Invalid; + break; + } + + if (components == ImageResult::Components::Invalid) { + RL_LOG("Could not detect image components when decoding."); + return {}; + } + + return ImageResult{ + geom::Size{static_cast(width), + static_cast(height)}, // size + components, // components + std::move(destinationAllocation) // allocation + }; +} + +bool Image::isValid() const { + return _source != nullptr; +} + +std::size_t Image::Hash::operator()(const Image& key) const { + return std::hash()(key._source); +} + +bool Image::Equal::operator()(const Image& lhs, const Image& rhs) const { + return lhs._source == rhs._source; +} + +} // namespace image +} // namespace rl diff --git a/impeller/impeller/image/Image.h b/impeller/impeller/image/Image.h new file mode 100644 index 0000000000000..72fb12616e006 --- /dev/null +++ b/impeller/impeller/image/Image.h @@ -0,0 +1,49 @@ +/* + * This source file is part of the Radar project. + * Licensed under the MIT License. See LICENSE file for details. + */ + +#pragma once + +#include +#include +#include +#include + +namespace rl { +namespace image { + +class ImageSource; + +class Image : public core::MessageSerializable { + public: + Image(); + + Image(core::Allocation sourceAllocation); + + Image(core::FileHandle sourceFile); + + ~Image(); + + ImageResult decode() const; + + bool serialize(core::Message& message) const override; + + bool deserialize(core::Message& message, core::Namespace* ns) override; + + bool isValid() const; + + struct Hash { + std::size_t operator()(const Image& key) const; + }; + + struct Equal { + bool operator()(const Image& lhs, const Image& rhs) const; + }; + + private: + std::shared_ptr _source; +}; + +} // namespace image +} // namespace rl diff --git a/impeller/impeller/image/ImageEncoder.cc b/impeller/impeller/image/ImageEncoder.cc new file mode 100644 index 0000000000000..3ab55a326c607 --- /dev/null +++ b/impeller/impeller/image/ImageEncoder.cc @@ -0,0 +1,86 @@ +/* + * This source file is part of the Radar project. + * Licensed under the MIT License. See LICENSE file for details. + */ + +#include +#include + +namespace rl { +namespace image { + +ImageEncoder::ImageEncoder(Type type, core::FileHandle handle) + : _isReady(false), _type(type), _adapter(std::move(handle)) { + if (!_adapter.isValid()) { + return; + } + + _isReady = true; +} + +ImageEncoder::~ImageEncoder() = default; + +bool ImageEncoder::isReady() const { + return _isReady; +} + +bool ImageEncoder::encode(ImageResult image) { + if (!_isReady) { + return false; + } + + if (!image.wasSuccessful()) { + return false; + } + + switch (_type) { + case Type::PNG: + return encodePNG(std::move(image)); + } + + return false; +} + +int ComponentsToSize(ImageResult::Components components) { + switch (components) { + case ImageResult::Components::Invalid: + return 0; + case ImageResult::Components::Grey: + return 1; + case ImageResult::Components::GreyAlpha: + return 2; + case ImageResult::Components::RGB: + return 3; + case ImageResult::Components::RGBA: + return 4; + } + + return 0; +} + +bool ImageEncoder::encodePNG(ImageResult image) { + auto size = image.size(); + + int componentSize = ComponentsToSize(image.components()); + + auto callback = [](void* context, void* data, int size) { + reinterpret_cast(context)->write( + reinterpret_cast(data), size); + }; + + return stbi_write_png_to_func(callback, // + this, // + size.width, // + size.height, // + componentSize, // + image.allocation().data(), // + componentSize // + ) == 1; +} + +void ImageEncoder::write(const uint8_t* data, size_t size) { + RL_UNUSED(_adapter.write(data, size)); +} + +} // namespace image +} // namespace rl diff --git a/impeller/impeller/image/ImageEncoder.h b/impeller/impeller/image/ImageEncoder.h new file mode 100644 index 0000000000000..ec4439b66394d --- /dev/null +++ b/impeller/impeller/image/ImageEncoder.h @@ -0,0 +1,43 @@ +/* + * This source file is part of the Radar project. + * Licensed under the MIT License. See LICENSE file for details. + */ + +#pragma once + +#include +#include +#include +#include + +namespace rl { +namespace image { + +class ImageEncoder { + public: + enum class Type { + PNG, + }; + + ImageEncoder(Type type, core::FileHandle handle); + + ~ImageEncoder(); + + bool isReady() const; + + bool encode(ImageResult image); + + private: + bool _isReady; + Type _type; + core::FileIOAdapter _adapter; + + bool encodePNG(ImageResult image); + + void write(const uint8_t* data, size_t size); + + RL_DISALLOW_COPY_AND_ASSIGN(ImageEncoder); +}; + +} // namespace image +} // namespace rl diff --git a/impeller/impeller/image/ImageResult.cc b/impeller/impeller/image/ImageResult.cc new file mode 100644 index 0000000000000..2fdbd9518c3b7 --- /dev/null +++ b/impeller/impeller/image/ImageResult.cc @@ -0,0 +1,57 @@ +/* + * This source file is part of the Radar project. + * Licensed under the MIT License. See LICENSE file for details. + */ + +#include + +namespace rl { +namespace image { + +ImageResult::ImageResult(geom::Size size, + Components components, + core::Allocation allocation) + : _success(true), + _size(size), + _components(components), + _allocation(std::move(allocation)) {} + +ImageResult::ImageResult() : _success(false) {} + +ImageResult::ImageResult(ImageResult&&) = default; + +ImageResult& ImageResult::operator=(ImageResult&& other) { + _success = other._success; + other._success = false; + + _size = other._size; + other._size = geom::Size{}; + + _components = other._components; + other._components = Components::Invalid; + + _allocation = std::move(other._allocation); + + return *this; +} + +bool ImageResult::wasSuccessful() const { + return _success; +} + +const geom::Size& ImageResult::size() const { + return _size; +} + +ImageResult::Components ImageResult::components() const { + return _components; +} + +const core::Allocation& ImageResult::allocation() const { + return _allocation; +} + +ImageResult::~ImageResult() = default; + +} // namespace image +} // namespace rl diff --git a/impeller/impeller/image/ImageResult.h b/impeller/impeller/image/ImageResult.h new file mode 100644 index 0000000000000..6fa35ca933fa5 --- /dev/null +++ b/impeller/impeller/image/ImageResult.h @@ -0,0 +1,55 @@ +/* + * This source file is part of the Radar project. + * Licensed under the MIT License. See LICENSE file for details. + */ + +#pragma once + +#include +#include +#include + +namespace rl { +namespace image { + +class ImageResult { + public: + enum class Components { + Invalid, + Grey, + GreyAlpha, + RGB, + RGBA, + }; + + ImageResult(); + + ImageResult(geom::Size size, + Components components, + core::Allocation allocation); + + ImageResult(ImageResult&&); + + ImageResult& operator=(ImageResult&&); + + ~ImageResult(); + + const geom::Size& size() const; + + bool wasSuccessful() const; + + Components components() const; + + const core::Allocation& allocation() const; + + private: + bool _success; + geom::Size _size; + Components _components; + core::Allocation _allocation; + + RL_DISALLOW_COPY_AND_ASSIGN(ImageResult); +}; + +} // namespace image +} // namespace rl diff --git a/impeller/impeller/image/ImageSource.cc b/impeller/impeller/image/ImageSource.cc new file mode 100644 index 0000000000000..8040b951c813a --- /dev/null +++ b/impeller/impeller/image/ImageSource.cc @@ -0,0 +1,59 @@ +/* + * This source file is part of the Radar project. + * Licensed under the MIT License. See LICENSE file for details. + */ + +#include "ImageSource.h" +#include "DataImageSource.h" +#include "FileImageSource.h" + +namespace rl { +namespace image { + +std::unique_ptr ImageSource::Create(core::Allocation allocation) { + return std::make_unique(std::move(allocation)); +} + +std::unique_ptr ImageSource::Create(core::FileHandle fileHandle) { + return std::make_unique(std::move(fileHandle)); +} + +std::shared_ptr ImageSource::ImageSourceForType(Type type) { + switch (type) { + case Type::File: + return std::make_shared(); + case Type::Data: + return std::make_shared(); + default: + return nullptr; + } + + return nullptr; +} + +ImageSource::ImageSource() = default; + +ImageSource::~ImageSource() = default; + +void ImageSource::prepareForUse() { + if (_prepared) { + return; + } + + onPrepareForUse(); + + _prepared = true; +} + +void ImageSource::doneUsing() { + if (!_prepared) { + return; + } + + onDoneUsing(); + + _prepared = false; +} + +} // namespace image +} // namespace rl diff --git a/impeller/impeller/image/ImageSource.h b/impeller/impeller/image/ImageSource.h new file mode 100644 index 0000000000000..5e8d345898bea --- /dev/null +++ b/impeller/impeller/image/ImageSource.h @@ -0,0 +1,54 @@ +/* + * This source file is part of the Radar project. + * Licensed under the MIT License. See LICENSE file for details. + */ + +#pragma once + +#include +#include +#include + +namespace rl { +namespace image { + +class ImageSource : public core::MessageSerializable { + public: + enum class Type : uint8_t { + Unknown, + File, + Data, + }; + + static std::unique_ptr Create(core::Allocation allocation); + + static std::unique_ptr Create(core::FileHandle fileHandle); + + static std::shared_ptr ImageSourceForType(Type type); + + ImageSource(); + + virtual ~ImageSource(); + + void prepareForUse(); + + void doneUsing(); + + protected: + bool _prepared = false; + + friend class Image; + + virtual Type type() const = 0; + + virtual uint8_t* sourceData() const = 0; + + virtual size_t sourceDataSize() const = 0; + + virtual void onPrepareForUse() = 0; + + virtual void onDoneUsing() = 0; +}; + +} // namespace image +} // namespace rl From 11bcdf7432e28dbb9b8c30c17572b09b96ed2c17 Mon Sep 17 00:00:00 2001 From: Chinmay Garde Date: Tue, 20 Apr 2021 16:36:03 -0700 Subject: [PATCH 005/433] Remove redundant data sources. --- impeller/impeller/BUILD.gn | 6 -- impeller/impeller/entity/Color.cc | 1 - impeller/impeller/entity/Entity.h | 2 - impeller/impeller/image/DataImageSource.cc | 51 ----------------- impeller/impeller/image/DataImageSource.h | 42 -------------- impeller/impeller/image/FileImageSource.cc | 64 ---------------------- impeller/impeller/image/FileImageSource.h | 43 --------------- impeller/impeller/image/Image.cc | 1 - impeller/impeller/image/Image.h | 8 +-- impeller/impeller/image/ImageEncoder.h | 2 - impeller/impeller/image/ImageResult.h | 2 - impeller/impeller/image/ImageSource.cc | 59 -------------------- impeller/impeller/image/ImageSource.h | 54 ------------------ 13 files changed, 1 insertion(+), 334 deletions(-) delete mode 100644 impeller/impeller/image/DataImageSource.cc delete mode 100644 impeller/impeller/image/DataImageSource.h delete mode 100644 impeller/impeller/image/FileImageSource.cc delete mode 100644 impeller/impeller/image/FileImageSource.h delete mode 100644 impeller/impeller/image/ImageSource.cc delete mode 100644 impeller/impeller/image/ImageSource.h diff --git a/impeller/impeller/BUILD.gn b/impeller/impeller/BUILD.gn index 993e9fae2051c..d5965dd107176 100644 --- a/impeller/impeller/BUILD.gn +++ b/impeller/impeller/BUILD.gn @@ -39,18 +39,12 @@ source_set("impeller") { ] image_sources = [ - "image/DataImageSource.cc", - "image/DataImageSource.h", - "image/FileImageSource.cc", - "image/FileImageSource.h", "image/Image.cc", "image/Image.h", "image/ImageEncoder.cc", "image/ImageEncoder.h", "image/ImageResult.cc", "image/ImageResult.h", - "image/ImageSource.cc", - "image/ImageSource.h", ] sources = geometry_sources + entity_sources + image_sources diff --git a/impeller/impeller/entity/Color.cc b/impeller/impeller/entity/Color.cc index ddeb79d54339e..641c82d4c78fa 100644 --- a/impeller/impeller/entity/Color.cc +++ b/impeller/impeller/entity/Color.cc @@ -3,7 +3,6 @@ * Licensed under the MIT License. See LICENSE file for details. */ -#include #include #include #include diff --git a/impeller/impeller/entity/Entity.h b/impeller/impeller/entity/Entity.h index 1610304ef2a4d..df59d9549fc6b 100644 --- a/impeller/impeller/entity/Entity.h +++ b/impeller/impeller/entity/Entity.h @@ -5,8 +5,6 @@ #pragma once -#include -#include #include #include #include diff --git a/impeller/impeller/image/DataImageSource.cc b/impeller/impeller/image/DataImageSource.cc deleted file mode 100644 index 12d96c6029583..0000000000000 --- a/impeller/impeller/image/DataImageSource.cc +++ /dev/null @@ -1,51 +0,0 @@ -/* - * This source file is part of the Radar project. - * Licensed under the MIT License. See LICENSE file for details. - */ - -#include "DataImageSource.h" -#include - -namespace rl { -namespace image { - -DataImageSource::DataImageSource() {} - -DataImageSource::DataImageSource(core::Allocation allocation) - : _allocation(std::move(allocation)) {} - -uint8_t* DataImageSource::sourceData() const { - return _allocation.data(); -} - -size_t DataImageSource::sourceDataSize() const { - return _allocation.size(); -} - -void DataImageSource::onPrepareForUse() { - /* - * Nothing to do since we already have an explicit allocation. - */ -} - -void DataImageSource::onDoneUsing() { - /* - * Nothing to do since we have an explicit allocate and we may need to be - * prepared again. - */ -} - -bool DataImageSource::serialize(core::Message& message) const { - return message.encode(_allocation); -} - -bool DataImageSource::deserialize(core::Message& message, core::Namespace* ns) { - return message.decode(_allocation, ns); -} - -ImageSource::Type DataImageSource::type() const { - return ImageSource::Type::Data; -} - -} // namespace image -} // namespace rl diff --git a/impeller/impeller/image/DataImageSource.h b/impeller/impeller/image/DataImageSource.h deleted file mode 100644 index de25b9f6db86d..0000000000000 --- a/impeller/impeller/image/DataImageSource.h +++ /dev/null @@ -1,42 +0,0 @@ -/* - * This source file is part of the Radar project. - * Licensed under the MIT License. See LICENSE file for details. - */ - -#pragma once - -#include -#include -#include "ImageSource.h" - -namespace rl { -namespace image { - -class DataImageSource : public ImageSource { - public: - DataImageSource(); - - DataImageSource(core::Allocation allocation); - - bool serialize(core::Message& message) const override; - - bool deserialize(core::Message& message, core::Namespace* ns) override; - - private: - core::Allocation _allocation; - - ImageSource::Type type() const override; - - uint8_t* sourceData() const override; - - size_t sourceDataSize() const override; - - void onPrepareForUse() override; - - void onDoneUsing() override; - - RL_DISALLOW_COPY_AND_ASSIGN(DataImageSource); -}; - -} // namespace image -} // namespace rl diff --git a/impeller/impeller/image/FileImageSource.cc b/impeller/impeller/image/FileImageSource.cc deleted file mode 100644 index cb9606cdcfac1..0000000000000 --- a/impeller/impeller/image/FileImageSource.cc +++ /dev/null @@ -1,64 +0,0 @@ -/* - * This source file is part of the Radar project. - * Licensed under the MIT License. See LICENSE file for details. - */ - -#include "FileImageSource.h" -#include -#include - -namespace rl { -namespace image { - -FileImageSource::FileImageSource() {} - -FileImageSource::FileImageSource(core::FileHandle fileHandle) - : _handle(std::make_shared(std::move(fileHandle))) {} - -uint8_t* FileImageSource::sourceData() const { - return _mapping == nullptr ? nullptr : _mapping->mapping(); -} - -size_t FileImageSource::sourceDataSize() const { - return _mapping == nullptr ? 0 : _mapping->size(); -} - -void FileImageSource::onPrepareForUse() { - if (_handle == nullptr || !_handle->isValid()) { - return; - } - - _mapping = std::make_unique(*_handle); -} - -void FileImageSource::onDoneUsing() { - _mapping = nullptr; -} - -bool FileImageSource::serialize(core::Message& message) const { - return message.encode(_handle); -} - -bool FileImageSource::deserialize(core::Message& message, core::Namespace* ns) { - core::RawAttachment attachment; - - if (!message.decode(attachment)) { - return false; - } - - _handle = std::make_shared(std::move(attachment)); - - /* - * Clear our old mapping if present so we can prepare for another use. - */ - _mapping = nullptr; - - return true; -} - -ImageSource::Type FileImageSource::type() const { - return ImageSource::Type::File; -} - -} // namespace image -} // namespace rl diff --git a/impeller/impeller/image/FileImageSource.h b/impeller/impeller/image/FileImageSource.h deleted file mode 100644 index 9b67cbf506d62..0000000000000 --- a/impeller/impeller/image/FileImageSource.h +++ /dev/null @@ -1,43 +0,0 @@ -/* - * This source file is part of the Radar project. - * Licensed under the MIT License. See LICENSE file for details. - */ - -#pragma once - -#include -#include -#include "ImageSource.h" - -namespace rl { -namespace image { - -class FileImageSource : public ImageSource { - public: - FileImageSource(); - - FileImageSource(core::FileHandle fileHandle); - - bool serialize(core::Message& message) const override; - - bool deserialize(core::Message& message, core::Namespace* ns) override; - - private: - std::shared_ptr _handle; - std::unique_ptr _mapping; - - ImageSource::Type type() const override; - - uint8_t* sourceData() const override; - - size_t sourceDataSize() const override; - - void onPrepareForUse() override; - - void onDoneUsing() override; - - RL_DISALLOW_COPY_AND_ASSIGN(FileImageSource); -}; - -} // namespace image -} // namespace rl diff --git a/impeller/impeller/image/Image.cc b/impeller/impeller/image/Image.cc index 35d00dea2d61b..374116fff01fd 100644 --- a/impeller/impeller/image/Image.cc +++ b/impeller/impeller/image/Image.cc @@ -3,7 +3,6 @@ * Licensed under the MIT License. See LICENSE file for details. */ -#include #include #include #include "ImageSource.h" diff --git a/impeller/impeller/image/Image.h b/impeller/impeller/image/Image.h index 72fb12616e006..59f95c330fc42 100644 --- a/impeller/impeller/image/Image.h +++ b/impeller/impeller/image/Image.h @@ -5,8 +5,6 @@ #pragma once -#include -#include #include #include @@ -15,7 +13,7 @@ namespace image { class ImageSource; -class Image : public core::MessageSerializable { +class Image { public: Image(); @@ -27,10 +25,6 @@ class Image : public core::MessageSerializable { ImageResult decode() const; - bool serialize(core::Message& message) const override; - - bool deserialize(core::Message& message, core::Namespace* ns) override; - bool isValid() const; struct Hash { diff --git a/impeller/impeller/image/ImageEncoder.h b/impeller/impeller/image/ImageEncoder.h index ec4439b66394d..edbfc97fc2688 100644 --- a/impeller/impeller/image/ImageEncoder.h +++ b/impeller/impeller/image/ImageEncoder.h @@ -5,8 +5,6 @@ #pragma once -#include -#include #include #include diff --git a/impeller/impeller/image/ImageResult.h b/impeller/impeller/image/ImageResult.h index 6fa35ca933fa5..61280c354b867 100644 --- a/impeller/impeller/image/ImageResult.h +++ b/impeller/impeller/image/ImageResult.h @@ -5,8 +5,6 @@ #pragma once -#include -#include #include namespace rl { diff --git a/impeller/impeller/image/ImageSource.cc b/impeller/impeller/image/ImageSource.cc deleted file mode 100644 index 8040b951c813a..0000000000000 --- a/impeller/impeller/image/ImageSource.cc +++ /dev/null @@ -1,59 +0,0 @@ -/* - * This source file is part of the Radar project. - * Licensed under the MIT License. See LICENSE file for details. - */ - -#include "ImageSource.h" -#include "DataImageSource.h" -#include "FileImageSource.h" - -namespace rl { -namespace image { - -std::unique_ptr ImageSource::Create(core::Allocation allocation) { - return std::make_unique(std::move(allocation)); -} - -std::unique_ptr ImageSource::Create(core::FileHandle fileHandle) { - return std::make_unique(std::move(fileHandle)); -} - -std::shared_ptr ImageSource::ImageSourceForType(Type type) { - switch (type) { - case Type::File: - return std::make_shared(); - case Type::Data: - return std::make_shared(); - default: - return nullptr; - } - - return nullptr; -} - -ImageSource::ImageSource() = default; - -ImageSource::~ImageSource() = default; - -void ImageSource::prepareForUse() { - if (_prepared) { - return; - } - - onPrepareForUse(); - - _prepared = true; -} - -void ImageSource::doneUsing() { - if (!_prepared) { - return; - } - - onDoneUsing(); - - _prepared = false; -} - -} // namespace image -} // namespace rl diff --git a/impeller/impeller/image/ImageSource.h b/impeller/impeller/image/ImageSource.h deleted file mode 100644 index 5e8d345898bea..0000000000000 --- a/impeller/impeller/image/ImageSource.h +++ /dev/null @@ -1,54 +0,0 @@ -/* - * This source file is part of the Radar project. - * Licensed under the MIT License. See LICENSE file for details. - */ - -#pragma once - -#include -#include -#include - -namespace rl { -namespace image { - -class ImageSource : public core::MessageSerializable { - public: - enum class Type : uint8_t { - Unknown, - File, - Data, - }; - - static std::unique_ptr Create(core::Allocation allocation); - - static std::unique_ptr Create(core::FileHandle fileHandle); - - static std::shared_ptr ImageSourceForType(Type type); - - ImageSource(); - - virtual ~ImageSource(); - - void prepareForUse(); - - void doneUsing(); - - protected: - bool _prepared = false; - - friend class Image; - - virtual Type type() const = 0; - - virtual uint8_t* sourceData() const = 0; - - virtual size_t sourceDataSize() const = 0; - - virtual void onPrepareForUse() = 0; - - virtual void onDoneUsing() = 0; -}; - -} // namespace image -} // namespace rl From a62bc01c7e08af2669dd16f5ecaa68ab6418595a Mon Sep 17 00:00:00 2001 From: Chinmay Garde Date: Tue, 20 Apr 2021 16:40:31 -0700 Subject: [PATCH 006/433] Fix old header include paths. --- impeller/impeller/BUILD.gn | 6 ++++++ impeller/impeller/entity/Color.cc | 2 +- impeller/impeller/entity/Entity.cc | 2 +- impeller/impeller/entity/Entity.h | 12 ++++++------ impeller/impeller/image/Image.cc | 2 +- impeller/impeller/image/Image.h | 4 ++-- impeller/impeller/image/ImageEncoder.cc | 2 +- impeller/impeller/image/ImageEncoder.h | 6 +++--- impeller/impeller/image/ImageResult.cc | 2 +- impeller/impeller/image/ImageResult.h | 4 ++-- 10 files changed, 24 insertions(+), 18 deletions(-) diff --git a/impeller/impeller/BUILD.gn b/impeller/impeller/BUILD.gn index d5965dd107176..f10f70df6b4ae 100644 --- a/impeller/impeller/BUILD.gn +++ b/impeller/impeller/BUILD.gn @@ -8,6 +8,12 @@ source_set("impeller") { cflags_objc = flutter_cflags_objc_arc cflags_objcc = flutter_cflags_objcc_arc + include_dirs = [ + "entity", + "geometry", + "image", + ] + geometry_sources = [ "geometry/Matrix.cc", "geometry/Matrix.h", diff --git a/impeller/impeller/entity/Color.cc b/impeller/impeller/entity/Color.cc index 641c82d4c78fa..502e668824dac 100644 --- a/impeller/impeller/entity/Color.cc +++ b/impeller/impeller/entity/Color.cc @@ -3,7 +3,7 @@ * Licensed under the MIT License. See LICENSE file for details. */ -#include +#include "Color.h" #include #include #include diff --git a/impeller/impeller/entity/Entity.cc b/impeller/impeller/entity/Entity.cc index e80700e238887..112ab612ece43 100644 --- a/impeller/impeller/entity/Entity.cc +++ b/impeller/impeller/entity/Entity.cc @@ -3,7 +3,7 @@ * Licensed under the MIT License. See LICENSE file for details. */ -#include +#include "Entity.h" namespace rl { namespace entity { diff --git a/impeller/impeller/entity/Entity.h b/impeller/impeller/entity/Entity.h index df59d9549fc6b..3641ca3433e2f 100644 --- a/impeller/impeller/entity/Entity.h +++ b/impeller/impeller/entity/Entity.h @@ -5,11 +5,11 @@ #pragma once -#include -#include -#include -#include -#include +#include "Color.h" +#include "Image.h" +#include "Matrix.h" +#include "Path.h" +#include "Rect.h" namespace rl { namespace entity { @@ -224,7 +224,7 @@ class Entity : public core::ArchiveSerializable { private: UpdateCallback _updateCallback; - RL_DISALLOW_COPY_AND_ASSIGN(Entity); + FML_DISALLOW_COPY_AND_ASSIGN(Entity); }; } // namespace entity diff --git a/impeller/impeller/image/Image.cc b/impeller/impeller/image/Image.cc index 374116fff01fd..69d18a4a358fc 100644 --- a/impeller/impeller/image/Image.cc +++ b/impeller/impeller/image/Image.cc @@ -3,7 +3,7 @@ * Licensed under the MIT License. See LICENSE file for details. */ -#include +#include "Image.h" #include #include "ImageSource.h" diff --git a/impeller/impeller/image/Image.h b/impeller/impeller/image/Image.h index 59f95c330fc42..b97592bb85a7e 100644 --- a/impeller/impeller/image/Image.h +++ b/impeller/impeller/image/Image.h @@ -5,8 +5,8 @@ #pragma once -#include -#include +#include "ImageResult.h" +#include "Size.h" namespace rl { namespace image { diff --git a/impeller/impeller/image/ImageEncoder.cc b/impeller/impeller/image/ImageEncoder.cc index 3ab55a326c607..939e9e07f6dd9 100644 --- a/impeller/impeller/image/ImageEncoder.cc +++ b/impeller/impeller/image/ImageEncoder.cc @@ -3,7 +3,7 @@ * Licensed under the MIT License. See LICENSE file for details. */ -#include +#include "ImageEncoder.h" #include namespace rl { diff --git a/impeller/impeller/image/ImageEncoder.h b/impeller/impeller/image/ImageEncoder.h index edbfc97fc2688..ba49a5bc2a2d4 100644 --- a/impeller/impeller/image/ImageEncoder.h +++ b/impeller/impeller/image/ImageEncoder.h @@ -5,8 +5,8 @@ #pragma once -#include -#include +#include "ImageResult.h" +#include "Size.h" namespace rl { namespace image { @@ -34,7 +34,7 @@ class ImageEncoder { void write(const uint8_t* data, size_t size); - RL_DISALLOW_COPY_AND_ASSIGN(ImageEncoder); + FML_DISALLOW_COPY_AND_ASSIGN(ImageEncoder); }; } // namespace image diff --git a/impeller/impeller/image/ImageResult.cc b/impeller/impeller/image/ImageResult.cc index 2fdbd9518c3b7..d409f8ac64a9b 100644 --- a/impeller/impeller/image/ImageResult.cc +++ b/impeller/impeller/image/ImageResult.cc @@ -3,7 +3,7 @@ * Licensed under the MIT License. See LICENSE file for details. */ -#include +#include "ImageResult.h" namespace rl { namespace image { diff --git a/impeller/impeller/image/ImageResult.h b/impeller/impeller/image/ImageResult.h index 61280c354b867..9c538be434891 100644 --- a/impeller/impeller/image/ImageResult.h +++ b/impeller/impeller/image/ImageResult.h @@ -5,7 +5,7 @@ #pragma once -#include +#include "Size.h" namespace rl { namespace image { @@ -46,7 +46,7 @@ class ImageResult { Components _components; core::Allocation _allocation; - RL_DISALLOW_COPY_AND_ASSIGN(ImageResult); + FML_DISALLOW_COPY_AND_ASSIGN(ImageResult); }; } // namespace image From 89634ed8639adbf4080247483a509f77e07254ee Mon Sep 17 00:00:00 2001 From: Chinmay Garde Date: Tue, 20 Apr 2021 16:54:51 -0700 Subject: [PATCH 007/433] Cleanup image and entity references. --- impeller/impeller/entity/Entity.cc | 163 +------------------------ impeller/impeller/entity/Entity.h | 32 +---- impeller/impeller/image/Image.cc | 45 +------ impeller/impeller/image/Image.h | 6 +- impeller/impeller/image/ImageResult.cc | 4 +- impeller/impeller/image/ImageResult.h | 9 +- 6 files changed, 20 insertions(+), 239 deletions(-) diff --git a/impeller/impeller/entity/Entity.cc b/impeller/impeller/entity/Entity.cc index 112ab612ece43..6920d120342ee 100644 --- a/impeller/impeller/entity/Entity.cc +++ b/impeller/impeller/entity/Entity.cc @@ -8,65 +8,13 @@ namespace rl { namespace entity { -Entity::Entity(core::Name identifier, UpdateCallback updateCallback) - : _identifier(identifier), - _anchorPoint(geom::Point(0.5, 0.5)), - _opacity(1.0), - _strokeSize(1.0), - _updateCallback(updateCallback) {} +Entity::Entity() + : _anchorPoint(geom::Point(0.5, 0.5)), _opacity(1.0), _strokeSize(1.0) {} Entity::Entity(Entity&&) = default; Entity::~Entity() = default; -void Entity::mergeProperties(const Entity& entity, PropertyMaskType only) { - RL_ASSERT(_identifier == entity._identifier); - - if (only & PropertyMask::BoundsMask) { - _bounds = entity._bounds; - } - - if (only & PropertyMask::PositionMask) { - _position = entity._position; - } - - if (only & PropertyMask::AnchorPointMask) { - _anchorPoint = entity._anchorPoint; - } - - if (only & PropertyMask::TransformationMask) { - _transformation = entity._transformation; - } - - if (only & PropertyMask::BackgroundColorMask) { - _backgroundColor = entity._backgroundColor; - } - - if (only & PropertyMask::OpacityMask) { - _opacity = entity._opacity; - } - - if (only & PropertyMask::StrokeSizeMask) { - _strokeSize = entity._strokeSize; - } - - if (only & PropertyMask::StrokeColorMask) { - _strokeColor = entity._strokeColor; - } - - if (only & PropertyMask::ContentsMask) { - _contents = entity._contents; - } - - if (only & PropertyMask::PathMask) { - _path = entity._path; - } -} - -core::Name Entity::identifier() const { - return _identifier; -} - geom::Rect Entity::frame() const { geom::Point origin(_position.x - (_bounds.size.width * _anchorPoint.x), _position.y - (_bounds.size.height * _anchorPoint.y)); @@ -87,7 +35,6 @@ const geom::Rect& Entity::bounds() const { void Entity::setBounds(const geom::Rect& bounds) { _bounds = bounds; - notifyInterfaceIfNecessary(Property::Bounds); } const geom::Point& Entity::position() const { @@ -96,7 +43,6 @@ const geom::Point& Entity::position() const { void Entity::setPosition(const geom::Point& position) { _position = position; - notifyInterfaceIfNecessary(Property::Position); } const geom::Point& Entity::anchorPoint() const { @@ -105,7 +51,6 @@ const geom::Point& Entity::anchorPoint() const { void Entity::setAnchorPoint(const geom::Point& anchorPoint) { _anchorPoint = anchorPoint; - notifyInterfaceIfNecessary(Property::AnchorPoint); } const geom::Matrix& Entity::transformation() const { @@ -114,7 +59,6 @@ const geom::Matrix& Entity::transformation() const { void Entity::setTransformation(const geom::Matrix& transformation) { _transformation = transformation; - notifyInterfaceIfNecessary(Property::Transformation); } geom::Matrix Entity::modelMatrix() const { @@ -151,7 +95,6 @@ const Color& Entity::backgroundColor() const { void Entity::setBackgroundColor(const Color& backgroundColor) { _backgroundColor = backgroundColor; - notifyInterfaceIfNecessary(Property::BackgroundColor); } const double& Entity::opacity() const { @@ -160,7 +103,6 @@ const double& Entity::opacity() const { void Entity::setOpacity(double opacity) { _opacity = opacity; - notifyInterfaceIfNecessary(Property::Opacity); } const Color& Entity::strokeColor() const { @@ -169,7 +111,6 @@ const Color& Entity::strokeColor() const { void Entity::setStrokeColor(const Color& strokeColor) { _strokeColor = strokeColor; - notifyInterfaceIfNecessary(Property::StrokeColor); } double Entity::strokeSize() const { @@ -178,7 +119,6 @@ double Entity::strokeSize() const { void Entity::setStrokeSize(double strokeSize) { _strokeSize = strokeSize; - notifyInterfaceIfNecessary(Property::StrokeSize); } const image::Image& Entity::contents() const { @@ -187,7 +127,6 @@ const image::Image& Entity::contents() const { void Entity::setContents(image::Image image) { _contents = std::move(image); - notifyInterfaceIfNecessary(Property::Contents); } const geom::Path& Entity::path() const { @@ -196,104 +135,6 @@ const geom::Path& Entity::path() const { void Entity::setPath(geom::Path path) { _path = std::move(path); - notifyInterfaceIfNecessary(Property::Path); -} - -void Entity::notifyInterfaceIfNecessary(Property property, - core::Name other) const { - if (_updateCallback) { - _updateCallback(*this, property, other); - } -} - -enum ArchiveKey { - Identifier, - Bounds, - Position, - AnchorPoint, - Transformation, - BackgroundColor, - Opacity, - StrokeColor, - StrokeSize, -}; - -const core::ArchiveDef Entity::ArchiveDefinition = { - /* .superClass = */ nullptr, - /* .className = */ "Entity", - /* .autoAssignName = */ false, - /* .members = */ - { - ArchiveKey::Identifier, // - ArchiveKey::Bounds, // - ArchiveKey::Position, // - ArchiveKey::AnchorPoint, // - ArchiveKey::Transformation, // - ArchiveKey::BackgroundColor, // - ArchiveKey::Opacity, // - ArchiveKey::StrokeColor, // - ArchiveKey::StrokeSize, // - }, -}; - -Entity::ArchiveName Entity::archiveName() const { - return *_identifier.handle(); -} - -bool Entity::serialize(core::ArchiveItem& item) const { - RL_RETURN_IF_FALSE(item.encode(ArchiveKey::Identifier, _identifier)); - - RL_RETURN_IF_FALSE(item.encode(ArchiveKey::Bounds, _bounds.toString())); - - RL_RETURN_IF_FALSE(item.encode(ArchiveKey::Position, _position.toString())); - - RL_RETURN_IF_FALSE( - item.encode(ArchiveKey::AnchorPoint, _anchorPoint.toString())); - - RL_RETURN_IF_FALSE( - item.encode(ArchiveKey::Transformation, _transformation.toString())); - - RL_RETURN_IF_FALSE( - item.encode(ArchiveKey::BackgroundColor, _backgroundColor.toString())); - - RL_RETURN_IF_FALSE(item.encode(ArchiveKey::Opacity, _opacity)); - - RL_RETURN_IF_FALSE( - item.encode(ArchiveKey::StrokeColor, _strokeColor.toString())); - - RL_RETURN_IF_FALSE(item.encode(ArchiveKey::StrokeSize, _strokeSize)); - - return true; -} - -bool Entity::deserialize(core::ArchiveItem& item, core::Namespace* ns) { - std::string decoded; - - RL_RETURN_IF_FALSE(item.decode(ArchiveKey::Identifier, _identifier, ns)); - - RL_RETURN_IF_FALSE(item.decode(ArchiveKey::Bounds, decoded)); - _bounds.fromString(decoded); - - RL_RETURN_IF_FALSE(item.decode(ArchiveKey::Position, decoded)); - _position.fromString(decoded); - - RL_RETURN_IF_FALSE(item.decode(ArchiveKey::AnchorPoint, decoded)); - _anchorPoint.fromString(decoded); - - RL_RETURN_IF_FALSE(item.decode(ArchiveKey::Transformation, decoded)); - _transformation.fromString(decoded); - - RL_RETURN_IF_FALSE(item.decode(ArchiveKey::BackgroundColor, decoded)); - _backgroundColor.fromString(decoded); - - RL_RETURN_IF_FALSE(item.decode(ArchiveKey::Opacity, _opacity)); - - RL_RETURN_IF_FALSE(item.decode(ArchiveKey::StrokeColor, decoded)); - _strokeColor.fromString(decoded); - - RL_RETURN_IF_FALSE(item.decode(ArchiveKey::StrokeSize, _strokeSize)); - - return true; } } // namespace entity diff --git a/impeller/impeller/entity/Entity.h b/impeller/impeller/entity/Entity.h index 3641ca3433e2f..fe0717f72015e 100644 --- a/impeller/impeller/entity/Entity.h +++ b/impeller/impeller/entity/Entity.h @@ -14,7 +14,7 @@ namespace rl { namespace entity { -class Entity : public core::ArchiveSerializable { +class Entity { public: using PropertyMaskType = uint16_t; @@ -56,17 +56,11 @@ class Entity : public core::ArchiveSerializable { }; #undef RL_MASK - using UpdateCallback = std::function; - - Entity(core::Name identifier, UpdateCallback updateCallback = nullptr); + Entity(); Entity(Entity&& entity); - virtual ~Entity(); - - core::Name identifier() const; + ~Entity(); /** * The frame specifies the origin and size of the entity in the coordinate @@ -194,18 +188,7 @@ class Entity : public core::ArchiveSerializable { void setPath(geom::Path path); - void mergeProperties(const Entity& entity, PropertyMaskType only); - - static const core::ArchiveDef ArchiveDefinition; - - ArchiveName archiveName() const override; - - bool serialize(core::ArchiveItem& item) const override; - - bool deserialize(core::ArchiveItem& item, core::Namespace* ns) override; - - protected: - core::Name _identifier; + private: geom::Rect _bounds; geom::Point _position; geom::Point _anchorPoint; @@ -217,13 +200,6 @@ class Entity : public core::ArchiveSerializable { Color _strokeColor; double _strokeSize; - void notifyInterfaceIfNecessary( - Property property, - core::Name identifier = core::Name() /* dead name */) const; - - private: - UpdateCallback _updateCallback; - FML_DISALLOW_COPY_AND_ASSIGN(Entity); }; diff --git a/impeller/impeller/image/Image.cc b/impeller/impeller/image/Image.cc index 69d18a4a358fc..953d2f0a1a493 100644 --- a/impeller/impeller/image/Image.cc +++ b/impeller/impeller/image/Image.cc @@ -12,7 +12,7 @@ namespace image { Image::Image() = default; -Image::Image(core::Allocation sourceAllocation) +Image::Image(fml::RefPtr sourceAllocation) : _source(ImageSource::Create(std::move(sourceAllocation))) {} Image::Image(core::FileHandle sourceFile) @@ -20,45 +20,6 @@ Image::Image(core::FileHandle sourceFile) Image::~Image() = default; -bool Image::serialize(core::Message& message) const { - if (_source == nullptr) { - return false; - } - - /* - * Encode the type. - */ - RL_RETURN_IF_FALSE(message.encode(_source->type())); - - /* - * Encode the image source. - */ - RL_RETURN_IF_FALSE(_source->serialize(message)); - - return true; -} - -bool Image::deserialize(core::Message& message, core::Namespace* ns) { - auto type = ImageSource::Type::Unknown; - - /* - * Decode the type. - */ - RL_RETURN_IF_FALSE(message.decode(type, ns)) - - /* - * Create and decode the image source of that type. - */ - auto source = ImageSource::ImageSourceForType(type); - if (source == nullptr) { - return false; - } - RL_RETURN_IF_FALSE(source->deserialize(message, ns)); - _source = source; - - return true; -} - ImageResult Image::decode() const { if (_source == nullptr) { return {}; @@ -83,8 +44,8 @@ ImageResult Image::decode() const { &comps, // Out: Components STBI_default); - auto destinationAllocation = - core::Allocation{decoded, width * height * comps * sizeof(stbi_uc)}; + auto destinationAllocation = fml::RefPtr{ + decoded, width * height * comps * sizeof(stbi_uc)}; /* * If either the decoded allocation is null or the size works out to be zero, diff --git a/impeller/impeller/image/Image.h b/impeller/impeller/image/Image.h index b97592bb85a7e..8b163b69608ca 100644 --- a/impeller/impeller/image/Image.h +++ b/impeller/impeller/image/Image.h @@ -7,6 +7,8 @@ #include "ImageResult.h" #include "Size.h" +#include "flutter/fml/macros.h" +#include "flutter/fml/mapping.h" namespace rl { namespace image { @@ -17,9 +19,7 @@ class Image { public: Image(); - Image(core::Allocation sourceAllocation); - - Image(core::FileHandle sourceFile); + Image(fml::RefPtr sourceAllocation); ~Image(); diff --git a/impeller/impeller/image/ImageResult.cc b/impeller/impeller/image/ImageResult.cc index d409f8ac64a9b..f9dbbb592297b 100644 --- a/impeller/impeller/image/ImageResult.cc +++ b/impeller/impeller/image/ImageResult.cc @@ -10,7 +10,7 @@ namespace image { ImageResult::ImageResult(geom::Size size, Components components, - core::Allocation allocation) + fml::RefPtr allocation) : _success(true), _size(size), _components(components), @@ -47,7 +47,7 @@ ImageResult::Components ImageResult::components() const { return _components; } -const core::Allocation& ImageResult::allocation() const { +const fml::RefPtr& ImageResult::allocation() const { return _allocation; } diff --git a/impeller/impeller/image/ImageResult.h b/impeller/impeller/image/ImageResult.h index 9c538be434891..4ee85619b7fb5 100644 --- a/impeller/impeller/image/ImageResult.h +++ b/impeller/impeller/image/ImageResult.h @@ -6,6 +6,9 @@ #pragma once #include "Size.h" +#include "flutter/fml/macros.h" +#include "flutter/fml/mapping.h" +#include "flutter/fml/memory/ref_counted.h" namespace rl { namespace image { @@ -24,7 +27,7 @@ class ImageResult { ImageResult(geom::Size size, Components components, - core::Allocation allocation); + fml::RefPtr allocation); ImageResult(ImageResult&&); @@ -38,13 +41,13 @@ class ImageResult { Components components() const; - const core::Allocation& allocation() const; + const fml::RefPtr& allocation() const; private: bool _success; geom::Size _size; Components _components; - core::Allocation _allocation; + fml::RefPtr _allocation; FML_DISALLOW_COPY_AND_ASSIGN(ImageResult); }; From 2ea9be9b3e3d962f2067cccc9bdbd0822916b79b Mon Sep 17 00:00:00 2001 From: Chinmay Garde Date: Sat, 1 May 2021 17:37:33 -0700 Subject: [PATCH 008/433] Import STB for rl::image::Image. --- impeller/impeller/BUILD.gn | 3 +- impeller/impeller/entity/Entity.cc | 8 - impeller/impeller/entity/Entity.h | 6 +- impeller/impeller/image/Image.cc | 62 +- impeller/impeller/image/Image.h | 14 +- impeller/impeller/image/ImageEncoder.cc | 86 - impeller/impeller/image/ImageEncoder.h | 41 - impeller/impeller/image/ImageResult.cc | 27 +- impeller/impeller/image/ImageResult.h | 17 +- impeller/third_party/stb/BUILD.gn | 12 + impeller/third_party/stb/STBImplementation.c | 12 + impeller/third_party/stb/stb/README.md | 131 + .../stb/stb/data/atari_8bit_font_revised.png | Bin 0 -> 1769 bytes .../stb/stb/data/easy_font_raw.png | Bin 0 -> 645 bytes .../stb/stb/data/herringbone/license.txt | 4 + .../template_caves_limit_connectivity.png | Bin 0 -> 10818 bytes .../template_caves_tiny_corridors.png | Bin 0 -> 4904 bytes .../herringbone/template_corner_caves.png | Bin 0 -> 9337 bytes .../template_horizontal_corridors_v1.png | Bin 0 -> 5491 bytes .../template_horizontal_corridors_v2.png | Bin 0 -> 5759 bytes .../template_horizontal_corridors_v3.png | Bin 0 -> 6766 bytes .../template_limit_connectivity_fat.png | Bin 0 -> 6655 bytes .../template_limited_connectivity.png | Bin 0 -> 6728 bytes .../data/herringbone/template_maze_2_wide.png | Bin 0 -> 12776 bytes .../herringbone/template_maze_plus_2_wide.png | Bin 0 -> 13141 bytes .../data/herringbone/template_open_areas.png | Bin 0 -> 7764 bytes .../template_ref2_corner_caves.png | Bin 0 -> 9729 bytes .../template_rooms_and_corridors.png | Bin 0 -> 4736 bytes ...oms_and_corridors_2_wide_diagonal_bias.png | Bin 0 -> 4193 bytes .../template_rooms_limit_connectivity.png | Bin 0 -> 7664 bytes ...emplate_round_rooms_diagonal_corridors.png | Bin 0 -> 8172 bytes .../herringbone/template_sean_dungeon.png | Bin 0 -> 10261 bytes .../template_simple_caves_2_wide.png | Bin 0 -> 15712 bytes ...emplate_square_rooms_with_random_rects.png | Bin 0 -> 4772 bytes .../stb/stb/deprecated/stb_image.c | 4678 +++++ .../stb/stb/deprecated/stretchy_buffer.txt | 28 + .../third_party/stb/stb/docs/other_libs.md | 181 + .../third_party/stb/stb/docs/stb_howto.txt | 185 + .../stb/docs/stb_voxel_render_interview.md | 173 + .../stb/stb/docs/why_public_domain.md | 116 + impeller/third_party/stb/stb/release_notes.md | 26 + impeller/third_party/stb/stb/stb.h | 14185 ++++++++++++++++ impeller/third_party/stb/stb/stb_c_lexer.h | 816 + impeller/third_party/stb/stb/stb_divide.h | 379 + impeller/third_party/stb/stb/stb_dxt.h | 630 + impeller/third_party/stb/stb/stb_easy_font.h | 258 + .../stb/stb/stb_herringbone_wang_tile.h | 1220 ++ impeller/third_party/stb/stb/stb_image.h | 6755 ++++++++ .../third_party/stb/stb/stb_image_resize.h | 2578 +++ .../third_party/stb/stb/stb_image_write.h | 1048 ++ impeller/third_party/stb/stb/stb_leakcheck.h | 124 + impeller/third_party/stb/stb/stb_perlin.h | 182 + impeller/third_party/stb/stb/stb_rect_pack.h | 572 + impeller/third_party/stb/stb/stb_textedit.h | 1304 ++ .../third_party/stb/stb/stb_tilemap_editor.h | 4131 +++++ impeller/third_party/stb/stb/stb_truetype.h | 3267 ++++ impeller/third_party/stb/stb/stb_vorbis.c | 5397 ++++++ .../third_party/stb/stb/stb_voxel_render.h | 3752 ++++ .../third_party/stb/stb/stretchy_buffer.h | 216 + .../third_party/stb/stb/tests/c_lexer_test.c | 3 + .../stb/stb/tests/c_lexer_test.dsp | 89 + .../stb/stb/tests/caveview/README.md | 85 + .../stb/stb/tests/caveview/cave_main.c | 598 + .../stb/stb/tests/caveview/cave_mesher.c | 928 + .../stb/stb/tests/caveview/cave_parse.c | 632 + .../stb/stb/tests/caveview/cave_parse.h | 41 + .../stb/stb/tests/caveview/cave_render.c | 951 ++ .../stb/stb/tests/caveview/caveview.dsp | 157 + .../stb/stb/tests/caveview/caveview.dsw | 29 + .../stb/stb/tests/caveview/caveview.h | 50 + .../stb/stb/tests/caveview/glext.h | 11124 ++++++++++++ .../stb/stb/tests/caveview/glext_list.h | 34 + .../third_party/stb/stb/tests/caveview/main.c | 0 .../stb/stb/tests/caveview/stb_gl.h | 1103 ++ .../stb/stb/tests/caveview/stb_glprog.h | 504 + .../tests/caveview/win32/SDL_windows_main.c | 224 + .../third_party/stb/stb/tests/herringbone.dsp | 95 + .../stb/stb/tests/herringbone_generator.c | 87 + .../stb/stb/tests/herringbone_map.c | 83 + .../stb/stb/tests/herringbone_map.dsp | 94 + .../third_party/stb/stb/tests/image_test.c | 166 + .../third_party/stb/stb/tests/image_test.dsp | 97 + .../stb/stb/tests/oversample/README.md | 94 + .../stb/stb/tests/oversample/main.c | 332 + .../stb/stb/tests/oversample/oversample.dsp | 97 + .../stb/stb/tests/oversample/oversample.dsw | 29 + .../stb/stb/tests/oversample/oversample.exe | Bin 0 -> 54272 bytes .../stb/stb/tests/oversample/stb_wingraph.h | 829 + .../stb/stb/tests/pngsuite/16bit/basi0g16.png | Bin 0 -> 299 bytes .../stb/stb/tests/pngsuite/16bit/basi2c16.png | Bin 0 -> 595 bytes .../stb/stb/tests/pngsuite/16bit/basi4a16.png | Bin 0 -> 2855 bytes .../stb/stb/tests/pngsuite/16bit/basi6a16.png | Bin 0 -> 4180 bytes .../stb/stb/tests/pngsuite/16bit/basn0g16.png | Bin 0 -> 167 bytes .../stb/stb/tests/pngsuite/16bit/basn2c16.png | Bin 0 -> 302 bytes .../stb/stb/tests/pngsuite/16bit/basn4a16.png | Bin 0 -> 2206 bytes .../stb/stb/tests/pngsuite/16bit/basn6a16.png | Bin 0 -> 3435 bytes .../stb/stb/tests/pngsuite/16bit/bgai4a16.png | Bin 0 -> 2855 bytes .../stb/stb/tests/pngsuite/16bit/bgan6a16.png | Bin 0 -> 3435 bytes .../stb/stb/tests/pngsuite/16bit/bggn4a16.png | Bin 0 -> 2220 bytes .../stb/stb/tests/pngsuite/16bit/bgyn6a16.png | Bin 0 -> 3453 bytes .../stb/stb/tests/pngsuite/16bit/oi1n0g16.png | Bin 0 -> 167 bytes .../stb/stb/tests/pngsuite/16bit/oi1n2c16.png | Bin 0 -> 302 bytes .../stb/stb/tests/pngsuite/16bit/oi2n0g16.png | Bin 0 -> 179 bytes .../stb/stb/tests/pngsuite/16bit/oi2n2c16.png | Bin 0 -> 314 bytes .../stb/stb/tests/pngsuite/16bit/oi4n0g16.png | Bin 0 -> 203 bytes .../stb/stb/tests/pngsuite/16bit/oi4n2c16.png | Bin 0 -> 338 bytes .../stb/stb/tests/pngsuite/16bit/oi9n0g16.png | Bin 0 -> 1283 bytes .../stb/stb/tests/pngsuite/16bit/oi9n2c16.png | Bin 0 -> 3038 bytes .../stb/stb/tests/pngsuite/16bit/tbbn2c16.png | Bin 0 -> 2041 bytes .../stb/stb/tests/pngsuite/16bit/tbgn2c16.png | Bin 0 -> 2041 bytes .../stb/stb/tests/pngsuite/16bit/tbwn0g16.png | Bin 0 -> 1313 bytes .../stb/stb/tests/pngsuite/PngSuite.LICENSE | 9 + .../stb/tests/pngsuite/corrupt/xc1n0g08.png | Bin 0 -> 138 bytes .../stb/tests/pngsuite/corrupt/xc9n2c08.png | Bin 0 -> 145 bytes .../stb/tests/pngsuite/corrupt/xcrn0g04.png | Bin 0 -> 145 bytes .../stb/tests/pngsuite/corrupt/xcsn0g01.png | Bin 0 -> 164 bytes .../stb/tests/pngsuite/corrupt/xd0n2c08.png | Bin 0 -> 145 bytes .../stb/tests/pngsuite/corrupt/xd3n2c08.png | Bin 0 -> 145 bytes .../stb/tests/pngsuite/corrupt/xd9n2c08.png | Bin 0 -> 145 bytes .../stb/tests/pngsuite/corrupt/xdtn0g01.png | Bin 0 -> 61 bytes .../stb/tests/pngsuite/corrupt/xhdn0g08.png | Bin 0 -> 138 bytes .../stb/tests/pngsuite/corrupt/xlfn0g04.png | Bin 0 -> 145 bytes .../stb/tests/pngsuite/corrupt/xs1n0g01.png | Bin 0 -> 164 bytes .../stb/tests/pngsuite/corrupt/xs2n0g01.png | Bin 0 -> 164 bytes .../stb/tests/pngsuite/corrupt/xs4n0g01.png | Bin 0 -> 164 bytes .../stb/tests/pngsuite/corrupt/xs7n0g01.png | Bin 0 -> 164 bytes .../stb/tests/pngsuite/primary/basi0g01.png | Bin 0 -> 217 bytes .../stb/tests/pngsuite/primary/basi0g02.png | Bin 0 -> 154 bytes .../stb/tests/pngsuite/primary/basi0g04.png | Bin 0 -> 247 bytes .../stb/tests/pngsuite/primary/basi0g08.png | Bin 0 -> 254 bytes .../stb/tests/pngsuite/primary/basi2c08.png | Bin 0 -> 315 bytes .../stb/tests/pngsuite/primary/basi3p01.png | Bin 0 -> 132 bytes .../stb/tests/pngsuite/primary/basi3p02.png | Bin 0 -> 193 bytes .../stb/tests/pngsuite/primary/basi3p04.png | Bin 0 -> 327 bytes .../stb/tests/pngsuite/primary/basi3p08.png | Bin 0 -> 1527 bytes .../stb/tests/pngsuite/primary/basi4a08.png | Bin 0 -> 214 bytes .../stb/tests/pngsuite/primary/basi6a08.png | Bin 0 -> 361 bytes .../stb/tests/pngsuite/primary/basn0g01.png | Bin 0 -> 164 bytes .../stb/tests/pngsuite/primary/basn0g02.png | Bin 0 -> 104 bytes .../stb/tests/pngsuite/primary/basn0g04.png | Bin 0 -> 145 bytes .../stb/tests/pngsuite/primary/basn0g08.png | Bin 0 -> 138 bytes .../stb/tests/pngsuite/primary/basn2c08.png | Bin 0 -> 145 bytes .../stb/tests/pngsuite/primary/basn3p01.png | Bin 0 -> 112 bytes .../stb/tests/pngsuite/primary/basn3p02.png | Bin 0 -> 146 bytes .../stb/tests/pngsuite/primary/basn3p04.png | Bin 0 -> 216 bytes .../stb/tests/pngsuite/primary/basn3p08.png | Bin 0 -> 1286 bytes .../stb/tests/pngsuite/primary/basn4a08.png | Bin 0 -> 126 bytes .../stb/tests/pngsuite/primary/basn6a08.png | Bin 0 -> 184 bytes .../stb/tests/pngsuite/primary/bgai4a08.png | Bin 0 -> 214 bytes .../stb/tests/pngsuite/primary/bgan6a08.png | Bin 0 -> 184 bytes .../stb/tests/pngsuite/primary/bgbn4a08.png | Bin 0 -> 140 bytes .../stb/tests/pngsuite/primary/bgwn6a08.png | Bin 0 -> 202 bytes .../stb/tests/pngsuite/primary/s01i3p01.png | Bin 0 -> 113 bytes .../stb/tests/pngsuite/primary/s01n3p01.png | Bin 0 -> 113 bytes .../stb/tests/pngsuite/primary/s02i3p01.png | Bin 0 -> 114 bytes .../stb/tests/pngsuite/primary/s02n3p01.png | Bin 0 -> 115 bytes .../stb/tests/pngsuite/primary/s03i3p01.png | Bin 0 -> 118 bytes .../stb/tests/pngsuite/primary/s03n3p01.png | Bin 0 -> 120 bytes .../stb/tests/pngsuite/primary/s04i3p01.png | Bin 0 -> 126 bytes .../stb/tests/pngsuite/primary/s04n3p01.png | Bin 0 -> 121 bytes .../stb/tests/pngsuite/primary/s05i3p02.png | Bin 0 -> 134 bytes .../stb/tests/pngsuite/primary/s05n3p02.png | Bin 0 -> 129 bytes .../stb/tests/pngsuite/primary/s06i3p02.png | Bin 0 -> 143 bytes .../stb/tests/pngsuite/primary/s06n3p02.png | Bin 0 -> 131 bytes .../stb/tests/pngsuite/primary/s07i3p02.png | Bin 0 -> 149 bytes .../stb/tests/pngsuite/primary/s07n3p02.png | Bin 0 -> 138 bytes .../stb/tests/pngsuite/primary/s08i3p02.png | Bin 0 -> 149 bytes .../stb/tests/pngsuite/primary/s08n3p02.png | Bin 0 -> 139 bytes .../stb/tests/pngsuite/primary/s09i3p02.png | Bin 0 -> 147 bytes .../stb/tests/pngsuite/primary/s09n3p02.png | Bin 0 -> 143 bytes .../stb/tests/pngsuite/primary/s32i3p04.png | Bin 0 -> 355 bytes .../stb/tests/pngsuite/primary/s32n3p04.png | Bin 0 -> 263 bytes .../stb/tests/pngsuite/primary/s33i3p04.png | Bin 0 -> 385 bytes .../stb/tests/pngsuite/primary/s33n3p04.png | Bin 0 -> 329 bytes .../stb/tests/pngsuite/primary/s34i3p04.png | Bin 0 -> 349 bytes .../stb/tests/pngsuite/primary/s34n3p04.png | Bin 0 -> 248 bytes .../stb/tests/pngsuite/primary/s35i3p04.png | Bin 0 -> 399 bytes .../stb/tests/pngsuite/primary/s35n3p04.png | Bin 0 -> 338 bytes .../stb/tests/pngsuite/primary/s36i3p04.png | Bin 0 -> 356 bytes .../stb/tests/pngsuite/primary/s36n3p04.png | Bin 0 -> 258 bytes .../stb/tests/pngsuite/primary/s37i3p04.png | Bin 0 -> 393 bytes .../stb/tests/pngsuite/primary/s37n3p04.png | Bin 0 -> 336 bytes .../stb/tests/pngsuite/primary/s38i3p04.png | Bin 0 -> 357 bytes .../stb/tests/pngsuite/primary/s38n3p04.png | Bin 0 -> 245 bytes .../stb/tests/pngsuite/primary/s39i3p04.png | Bin 0 -> 420 bytes .../stb/tests/pngsuite/primary/s39n3p04.png | Bin 0 -> 352 bytes .../stb/tests/pngsuite/primary/s40i3p04.png | Bin 0 -> 357 bytes .../stb/tests/pngsuite/primary/s40n3p04.png | Bin 0 -> 256 bytes .../stb/tests/pngsuite/primary/tbbn0g04.png | Bin 0 -> 429 bytes .../stb/tests/pngsuite/primary/tbbn3p08.png | Bin 0 -> 1499 bytes .../stb/tests/pngsuite/primary/tbgn3p08.png | Bin 0 -> 1499 bytes .../stb/tests/pngsuite/primary/tbrn2c08.png | Bin 0 -> 1633 bytes .../stb/tests/pngsuite/primary/tbwn3p08.png | Bin 0 -> 1496 bytes .../stb/tests/pngsuite/primary/tbyn3p08.png | Bin 0 -> 1499 bytes .../stb/tests/pngsuite/primary/tm3n3p02.png | Bin 0 -> 116 bytes .../stb/tests/pngsuite/primary/tp0n0g08.png | Bin 0 -> 719 bytes .../stb/tests/pngsuite/primary/tp0n2c08.png | Bin 0 -> 1594 bytes .../stb/tests/pngsuite/primary/tp0n3p08.png | Bin 0 -> 1476 bytes .../stb/tests/pngsuite/primary/tp1n3p08.png | Bin 0 -> 1483 bytes .../stb/tests/pngsuite/primary/z00n2c08.png | Bin 0 -> 3172 bytes .../stb/tests/pngsuite/primary/z03n2c08.png | Bin 0 -> 232 bytes .../stb/tests/pngsuite/primary/z06n2c08.png | Bin 0 -> 224 bytes .../stb/tests/pngsuite/primary/z09n2c08.png | Bin 0 -> 224 bytes .../tests/pngsuite/primary_check/basi0g01.png | Bin 0 -> 391 bytes .../tests/pngsuite/primary_check/basi0g02.png | Bin 0 -> 283 bytes .../tests/pngsuite/primary_check/basi0g04.png | Bin 0 -> 252 bytes .../tests/pngsuite/primary_check/basi0g08.png | Bin 0 -> 293 bytes .../tests/pngsuite/primary_check/basi2c08.png | Bin 0 -> 274 bytes .../tests/pngsuite/primary_check/basi3p01.png | Bin 0 -> 273 bytes .../tests/pngsuite/primary_check/basi3p02.png | Bin 0 -> 286 bytes .../tests/pngsuite/primary_check/basi3p04.png | Bin 0 -> 331 bytes .../tests/pngsuite/primary_check/basi3p08.png | Bin 0 -> 387 bytes .../tests/pngsuite/primary_check/basi4a08.png | Bin 0 -> 263 bytes .../tests/pngsuite/primary_check/basi6a08.png | Bin 0 -> 298 bytes .../tests/pngsuite/primary_check/basn0g01.png | Bin 0 -> 391 bytes .../tests/pngsuite/primary_check/basn0g02.png | Bin 0 -> 283 bytes .../tests/pngsuite/primary_check/basn0g04.png | Bin 0 -> 252 bytes .../tests/pngsuite/primary_check/basn0g08.png | Bin 0 -> 293 bytes .../tests/pngsuite/primary_check/basn2c08.png | Bin 0 -> 274 bytes .../tests/pngsuite/primary_check/basn3p01.png | Bin 0 -> 273 bytes .../tests/pngsuite/primary_check/basn3p02.png | Bin 0 -> 286 bytes .../tests/pngsuite/primary_check/basn3p04.png | Bin 0 -> 331 bytes .../tests/pngsuite/primary_check/basn3p08.png | Bin 0 -> 387 bytes .../tests/pngsuite/primary_check/basn4a08.png | Bin 0 -> 263 bytes .../tests/pngsuite/primary_check/basn6a08.png | Bin 0 -> 298 bytes .../tests/pngsuite/primary_check/bgai4a08.png | Bin 0 -> 263 bytes .../tests/pngsuite/primary_check/bgan6a08.png | Bin 0 -> 298 bytes .../tests/pngsuite/primary_check/bgbn4a08.png | Bin 0 -> 263 bytes .../tests/pngsuite/primary_check/bgwn6a08.png | Bin 0 -> 298 bytes .../tests/pngsuite/primary_check/s01i3p01.png | Bin 0 -> 202 bytes .../tests/pngsuite/primary_check/s01n3p01.png | Bin 0 -> 202 bytes .../tests/pngsuite/primary_check/s02i3p01.png | Bin 0 -> 210 bytes .../tests/pngsuite/primary_check/s02n3p01.png | Bin 0 -> 210 bytes .../tests/pngsuite/primary_check/s03i3p01.png | Bin 0 -> 216 bytes .../tests/pngsuite/primary_check/s03n3p01.png | Bin 0 -> 216 bytes .../tests/pngsuite/primary_check/s04i3p01.png | Bin 0 -> 221 bytes .../tests/pngsuite/primary_check/s04n3p01.png | Bin 0 -> 221 bytes .../tests/pngsuite/primary_check/s05i3p02.png | Bin 0 -> 232 bytes .../tests/pngsuite/primary_check/s05n3p02.png | Bin 0 -> 232 bytes .../tests/pngsuite/primary_check/s06i3p02.png | Bin 0 -> 239 bytes .../tests/pngsuite/primary_check/s06n3p02.png | Bin 0 -> 239 bytes .../tests/pngsuite/primary_check/s07i3p02.png | Bin 0 -> 249 bytes .../tests/pngsuite/primary_check/s07n3p02.png | Bin 0 -> 249 bytes .../tests/pngsuite/primary_check/s08i3p02.png | Bin 0 -> 255 bytes .../tests/pngsuite/primary_check/s08n3p02.png | Bin 0 -> 255 bytes .../tests/pngsuite/primary_check/s09i3p02.png | Bin 0 -> 263 bytes .../tests/pngsuite/primary_check/s09n3p02.png | Bin 0 -> 263 bytes .../tests/pngsuite/primary_check/s32i3p04.png | Bin 0 -> 441 bytes .../tests/pngsuite/primary_check/s32n3p04.png | Bin 0 -> 441 bytes .../tests/pngsuite/primary_check/s33i3p04.png | Bin 0 -> 470 bytes .../tests/pngsuite/primary_check/s33n3p04.png | Bin 0 -> 470 bytes .../tests/pngsuite/primary_check/s34i3p04.png | Bin 0 -> 431 bytes .../tests/pngsuite/primary_check/s34n3p04.png | Bin 0 -> 431 bytes .../tests/pngsuite/primary_check/s35i3p04.png | Bin 0 -> 477 bytes .../tests/pngsuite/primary_check/s35n3p04.png | Bin 0 -> 477 bytes .../tests/pngsuite/primary_check/s36i3p04.png | Bin 0 -> 448 bytes .../tests/pngsuite/primary_check/s36n3p04.png | Bin 0 -> 448 bytes .../tests/pngsuite/primary_check/s37i3p04.png | Bin 0 -> 478 bytes .../tests/pngsuite/primary_check/s37n3p04.png | Bin 0 -> 478 bytes .../tests/pngsuite/primary_check/s38i3p04.png | Bin 0 -> 439 bytes .../tests/pngsuite/primary_check/s38n3p04.png | Bin 0 -> 439 bytes .../tests/pngsuite/primary_check/s39i3p04.png | Bin 0 -> 499 bytes .../tests/pngsuite/primary_check/s39n3p04.png | Bin 0 -> 499 bytes .../tests/pngsuite/primary_check/s40i3p04.png | Bin 0 -> 463 bytes .../tests/pngsuite/primary_check/s40n3p04.png | Bin 0 -> 463 bytes .../tests/pngsuite/primary_check/tbbn0g04.png | Bin 0 -> 762 bytes .../tests/pngsuite/primary_check/tbbn3p08.png | Bin 0 -> 1911 bytes .../tests/pngsuite/primary_check/tbgn3p08.png | Bin 0 -> 1911 bytes .../tests/pngsuite/primary_check/tbrn2c08.png | Bin 0 -> 1901 bytes .../tests/pngsuite/primary_check/tbwn3p08.png | Bin 0 -> 1911 bytes .../tests/pngsuite/primary_check/tbyn3p08.png | Bin 0 -> 1911 bytes .../tests/pngsuite/primary_check/tm3n3p02.png | Bin 0 -> 306 bytes .../tests/pngsuite/primary_check/tp0n0g08.png | Bin 0 -> 1802 bytes .../tests/pngsuite/primary_check/tp0n2c08.png | Bin 0 -> 1955 bytes .../tests/pngsuite/primary_check/tp0n3p08.png | Bin 0 -> 1959 bytes .../tests/pngsuite/primary_check/tp1n3p08.png | Bin 0 -> 1911 bytes .../tests/pngsuite/primary_check/z00n2c08.png | Bin 0 -> 422 bytes .../tests/pngsuite/primary_check/z03n2c08.png | Bin 0 -> 422 bytes .../tests/pngsuite/primary_check/z06n2c08.png | Bin 0 -> 422 bytes .../tests/pngsuite/primary_check/z09n2c08.png | Bin 0 -> 422 bytes .../stb/tests/pngsuite/unused/ccwn2c08.png | Bin 0 -> 1514 bytes .../stb/tests/pngsuite/unused/ccwn3p08.png | Bin 0 -> 1554 bytes .../stb/tests/pngsuite/unused/cdfn2c08.png | Bin 0 -> 404 bytes .../stb/tests/pngsuite/unused/cdhn2c08.png | Bin 0 -> 344 bytes .../stb/tests/pngsuite/unused/cdsn2c08.png | Bin 0 -> 232 bytes .../stb/tests/pngsuite/unused/cdun2c08.png | Bin 0 -> 724 bytes .../stb/tests/pngsuite/unused/ch1n3p04.png | Bin 0 -> 258 bytes .../stb/tests/pngsuite/unused/ch2n3p08.png | Bin 0 -> 1810 bytes .../stb/tests/pngsuite/unused/cm0n0g04.png | Bin 0 -> 292 bytes .../stb/tests/pngsuite/unused/cm7n0g04.png | Bin 0 -> 292 bytes .../stb/tests/pngsuite/unused/cm9n0g04.png | Bin 0 -> 292 bytes .../stb/tests/pngsuite/unused/cs3n2c16.png | Bin 0 -> 214 bytes .../stb/tests/pngsuite/unused/cs3n3p08.png | Bin 0 -> 259 bytes .../stb/tests/pngsuite/unused/cs5n2c08.png | Bin 0 -> 186 bytes .../stb/tests/pngsuite/unused/cs5n3p08.png | Bin 0 -> 271 bytes .../stb/tests/pngsuite/unused/cs8n2c08.png | Bin 0 -> 149 bytes .../stb/tests/pngsuite/unused/cs8n3p08.png | Bin 0 -> 256 bytes .../stb/tests/pngsuite/unused/ct0n0g04.png | Bin 0 -> 273 bytes .../stb/tests/pngsuite/unused/ct1n0g04.png | Bin 0 -> 792 bytes .../stb/tests/pngsuite/unused/cten0g04.png | Bin 0 -> 742 bytes .../stb/tests/pngsuite/unused/ctfn0g04.png | Bin 0 -> 716 bytes .../stb/tests/pngsuite/unused/ctgn0g04.png | Bin 0 -> 1182 bytes .../stb/tests/pngsuite/unused/cthn0g04.png | Bin 0 -> 1269 bytes .../stb/tests/pngsuite/unused/ctjn0g04.png | Bin 0 -> 941 bytes .../stb/tests/pngsuite/unused/ctzn0g04.png | Bin 0 -> 753 bytes .../stb/tests/pngsuite/unused/f00n0g08.png | Bin 0 -> 319 bytes .../stb/tests/pngsuite/unused/f00n2c08.png | Bin 0 -> 2475 bytes .../stb/tests/pngsuite/unused/f01n0g08.png | Bin 0 -> 321 bytes .../stb/tests/pngsuite/unused/f01n2c08.png | Bin 0 -> 1180 bytes .../stb/tests/pngsuite/unused/f02n0g08.png | Bin 0 -> 355 bytes .../stb/tests/pngsuite/unused/f02n2c08.png | Bin 0 -> 1729 bytes .../stb/tests/pngsuite/unused/f03n0g08.png | Bin 0 -> 389 bytes .../stb/tests/pngsuite/unused/f03n2c08.png | Bin 0 -> 1291 bytes .../stb/tests/pngsuite/unused/f04n0g08.png | Bin 0 -> 269 bytes .../stb/tests/pngsuite/unused/f04n2c08.png | Bin 0 -> 985 bytes .../stb/tests/pngsuite/unused/f99n0g04.png | Bin 0 -> 426 bytes .../stb/tests/pngsuite/unused/g03n0g16.png | Bin 0 -> 345 bytes .../stb/tests/pngsuite/unused/g03n2c08.png | Bin 0 -> 370 bytes .../stb/tests/pngsuite/unused/g03n3p04.png | Bin 0 -> 214 bytes .../stb/tests/pngsuite/unused/g04n0g16.png | Bin 0 -> 363 bytes .../stb/tests/pngsuite/unused/g04n2c08.png | Bin 0 -> 377 bytes .../stb/tests/pngsuite/unused/g04n3p04.png | Bin 0 -> 219 bytes .../stb/tests/pngsuite/unused/g05n0g16.png | Bin 0 -> 339 bytes .../stb/tests/pngsuite/unused/g05n2c08.png | Bin 0 -> 350 bytes .../stb/tests/pngsuite/unused/g05n3p04.png | Bin 0 -> 206 bytes .../stb/tests/pngsuite/unused/g07n0g16.png | Bin 0 -> 321 bytes .../stb/tests/pngsuite/unused/g07n2c08.png | Bin 0 -> 340 bytes .../stb/tests/pngsuite/unused/g07n3p04.png | Bin 0 -> 207 bytes .../stb/tests/pngsuite/unused/g10n0g16.png | Bin 0 -> 262 bytes .../stb/tests/pngsuite/unused/g10n2c08.png | Bin 0 -> 285 bytes .../stb/tests/pngsuite/unused/g10n3p04.png | Bin 0 -> 214 bytes .../stb/tests/pngsuite/unused/g25n0g16.png | Bin 0 -> 383 bytes .../stb/tests/pngsuite/unused/g25n2c08.png | Bin 0 -> 405 bytes .../stb/tests/pngsuite/unused/g25n3p04.png | Bin 0 -> 215 bytes .../stb/tests/pngsuite/unused/pp0n2c16.png | Bin 0 -> 962 bytes .../stb/tests/pngsuite/unused/pp0n6a08.png | Bin 0 -> 818 bytes .../stb/tests/pngsuite/unused/ps1n0g08.png | Bin 0 -> 1456 bytes .../stb/tests/pngsuite/unused/ps1n2c16.png | Bin 0 -> 1620 bytes .../stb/tests/pngsuite/unused/ps2n0g08.png | Bin 0 -> 2320 bytes .../stb/tests/pngsuite/unused/ps2n2c16.png | Bin 0 -> 2484 bytes .../stb/stb/tests/resample_test.cpp | 1116 ++ .../stb/stb/tests/resample_test_c.c | 5 + impeller/third_party/stb/stb/tests/resize.dsp | 94 + impeller/third_party/stb/stb/tests/stb.c | 3323 ++++ impeller/third_party/stb/stb/tests/stb.dsp | 192 + impeller/third_party/stb/stb/tests/stb.dsw | 161 + .../third_party/stb/stb/tests/stb_cpp.cpp | 82 + .../third_party/stb/stb/tests/stb_cpp.dsp | 98 + .../third_party/stb/stb/tests/stretch_test.c | 28 + .../stb/stb/tests/stretch_test.dsp | 89 + .../stb/stb/tests/stretchy_buffer_test.c | 1 + .../stb/stb/tests/test_c_compilation.c | 30 + .../stb/stb/tests/test_cpp_compilation.cpp | 137 + .../third_party/stb/stb/tests/test_truetype.c | 95 + .../third_party/stb/stb/tests/test_vorbis.c | 18 + .../stb/stb/tests/textedit_sample.c | 84 + .../tilemap_editor_integration_example.c | 193 + .../stb/stb/tests/vorbseek/vorbseek.c | 125 + .../stb/stb/tests/vorbseek/vorbseek.dsp | 96 + .../stb/stb/tools/README.footer.md | 100 + .../stb/stb/tools/README.header.md | 7 + .../third_party/stb/stb/tools/README.list | 18 + .../stb/stb/tools/easy_font_maker.c | 211 + .../third_party/stb/stb/tools/make_readme.c | 61 + .../third_party/stb/stb/tools/make_readme.dsp | 97 + impeller/third_party/stb/stb/tools/mr.bat | 1 + impeller/third_party/stb/stb/tools/unicode.c | 749 + .../stb/stb/tools/unicode/unicode.dsp | 88 + 368 files changed, 78259 insertions(+), 228 deletions(-) delete mode 100644 impeller/impeller/image/ImageEncoder.cc delete mode 100644 impeller/impeller/image/ImageEncoder.h create mode 100644 impeller/third_party/stb/BUILD.gn create mode 100644 impeller/third_party/stb/STBImplementation.c create mode 100644 impeller/third_party/stb/stb/README.md create mode 100644 impeller/third_party/stb/stb/data/atari_8bit_font_revised.png create mode 100644 impeller/third_party/stb/stb/data/easy_font_raw.png create mode 100644 impeller/third_party/stb/stb/data/herringbone/license.txt create mode 100644 impeller/third_party/stb/stb/data/herringbone/template_caves_limit_connectivity.png create mode 100644 impeller/third_party/stb/stb/data/herringbone/template_caves_tiny_corridors.png create mode 100644 impeller/third_party/stb/stb/data/herringbone/template_corner_caves.png create mode 100644 impeller/third_party/stb/stb/data/herringbone/template_horizontal_corridors_v1.png create mode 100644 impeller/third_party/stb/stb/data/herringbone/template_horizontal_corridors_v2.png create mode 100644 impeller/third_party/stb/stb/data/herringbone/template_horizontal_corridors_v3.png create mode 100644 impeller/third_party/stb/stb/data/herringbone/template_limit_connectivity_fat.png create mode 100644 impeller/third_party/stb/stb/data/herringbone/template_limited_connectivity.png create mode 100644 impeller/third_party/stb/stb/data/herringbone/template_maze_2_wide.png create mode 100644 impeller/third_party/stb/stb/data/herringbone/template_maze_plus_2_wide.png create mode 100644 impeller/third_party/stb/stb/data/herringbone/template_open_areas.png create mode 100644 impeller/third_party/stb/stb/data/herringbone/template_ref2_corner_caves.png create mode 100644 impeller/third_party/stb/stb/data/herringbone/template_rooms_and_corridors.png create mode 100644 impeller/third_party/stb/stb/data/herringbone/template_rooms_and_corridors_2_wide_diagonal_bias.png create mode 100644 impeller/third_party/stb/stb/data/herringbone/template_rooms_limit_connectivity.png create mode 100644 impeller/third_party/stb/stb/data/herringbone/template_round_rooms_diagonal_corridors.png create mode 100644 impeller/third_party/stb/stb/data/herringbone/template_sean_dungeon.png create mode 100644 impeller/third_party/stb/stb/data/herringbone/template_simple_caves_2_wide.png create mode 100644 impeller/third_party/stb/stb/data/herringbone/template_square_rooms_with_random_rects.png create mode 100644 impeller/third_party/stb/stb/deprecated/stb_image.c create mode 100644 impeller/third_party/stb/stb/deprecated/stretchy_buffer.txt create mode 100644 impeller/third_party/stb/stb/docs/other_libs.md create mode 100644 impeller/third_party/stb/stb/docs/stb_howto.txt create mode 100644 impeller/third_party/stb/stb/docs/stb_voxel_render_interview.md create mode 100644 impeller/third_party/stb/stb/docs/why_public_domain.md create mode 100644 impeller/third_party/stb/stb/release_notes.md create mode 100644 impeller/third_party/stb/stb/stb.h create mode 100644 impeller/third_party/stb/stb/stb_c_lexer.h create mode 100644 impeller/third_party/stb/stb/stb_divide.h create mode 100644 impeller/third_party/stb/stb/stb_dxt.h create mode 100644 impeller/third_party/stb/stb/stb_easy_font.h create mode 100644 impeller/third_party/stb/stb/stb_herringbone_wang_tile.h create mode 100644 impeller/third_party/stb/stb/stb_image.h create mode 100644 impeller/third_party/stb/stb/stb_image_resize.h create mode 100644 impeller/third_party/stb/stb/stb_image_write.h create mode 100644 impeller/third_party/stb/stb/stb_leakcheck.h create mode 100644 impeller/third_party/stb/stb/stb_perlin.h create mode 100644 impeller/third_party/stb/stb/stb_rect_pack.h create mode 100644 impeller/third_party/stb/stb/stb_textedit.h create mode 100644 impeller/third_party/stb/stb/stb_tilemap_editor.h create mode 100644 impeller/third_party/stb/stb/stb_truetype.h create mode 100644 impeller/third_party/stb/stb/stb_vorbis.c create mode 100644 impeller/third_party/stb/stb/stb_voxel_render.h create mode 100644 impeller/third_party/stb/stb/stretchy_buffer.h create mode 100644 impeller/third_party/stb/stb/tests/c_lexer_test.c create mode 100644 impeller/third_party/stb/stb/tests/c_lexer_test.dsp create mode 100644 impeller/third_party/stb/stb/tests/caveview/README.md create mode 100644 impeller/third_party/stb/stb/tests/caveview/cave_main.c create mode 100644 impeller/third_party/stb/stb/tests/caveview/cave_mesher.c create mode 100644 impeller/third_party/stb/stb/tests/caveview/cave_parse.c create mode 100644 impeller/third_party/stb/stb/tests/caveview/cave_parse.h create mode 100644 impeller/third_party/stb/stb/tests/caveview/cave_render.c create mode 100644 impeller/third_party/stb/stb/tests/caveview/caveview.dsp create mode 100644 impeller/third_party/stb/stb/tests/caveview/caveview.dsw create mode 100644 impeller/third_party/stb/stb/tests/caveview/caveview.h create mode 100644 impeller/third_party/stb/stb/tests/caveview/glext.h create mode 100644 impeller/third_party/stb/stb/tests/caveview/glext_list.h create mode 100644 impeller/third_party/stb/stb/tests/caveview/main.c create mode 100644 impeller/third_party/stb/stb/tests/caveview/stb_gl.h create mode 100644 impeller/third_party/stb/stb/tests/caveview/stb_glprog.h create mode 100644 impeller/third_party/stb/stb/tests/caveview/win32/SDL_windows_main.c create mode 100644 impeller/third_party/stb/stb/tests/herringbone.dsp create mode 100644 impeller/third_party/stb/stb/tests/herringbone_generator.c create mode 100644 impeller/third_party/stb/stb/tests/herringbone_map.c create mode 100644 impeller/third_party/stb/stb/tests/herringbone_map.dsp create mode 100644 impeller/third_party/stb/stb/tests/image_test.c create mode 100644 impeller/third_party/stb/stb/tests/image_test.dsp create mode 100644 impeller/third_party/stb/stb/tests/oversample/README.md create mode 100644 impeller/third_party/stb/stb/tests/oversample/main.c create mode 100644 impeller/third_party/stb/stb/tests/oversample/oversample.dsp create mode 100644 impeller/third_party/stb/stb/tests/oversample/oversample.dsw create mode 100644 impeller/third_party/stb/stb/tests/oversample/oversample.exe create mode 100644 impeller/third_party/stb/stb/tests/oversample/stb_wingraph.h create mode 100644 impeller/third_party/stb/stb/tests/pngsuite/16bit/basi0g16.png create mode 100644 impeller/third_party/stb/stb/tests/pngsuite/16bit/basi2c16.png create mode 100644 impeller/third_party/stb/stb/tests/pngsuite/16bit/basi4a16.png create mode 100644 impeller/third_party/stb/stb/tests/pngsuite/16bit/basi6a16.png create mode 100644 impeller/third_party/stb/stb/tests/pngsuite/16bit/basn0g16.png create mode 100644 impeller/third_party/stb/stb/tests/pngsuite/16bit/basn2c16.png create mode 100644 impeller/third_party/stb/stb/tests/pngsuite/16bit/basn4a16.png create mode 100644 impeller/third_party/stb/stb/tests/pngsuite/16bit/basn6a16.png create mode 100644 impeller/third_party/stb/stb/tests/pngsuite/16bit/bgai4a16.png create mode 100644 impeller/third_party/stb/stb/tests/pngsuite/16bit/bgan6a16.png create mode 100644 impeller/third_party/stb/stb/tests/pngsuite/16bit/bggn4a16.png create mode 100644 impeller/third_party/stb/stb/tests/pngsuite/16bit/bgyn6a16.png create mode 100644 impeller/third_party/stb/stb/tests/pngsuite/16bit/oi1n0g16.png create mode 100644 impeller/third_party/stb/stb/tests/pngsuite/16bit/oi1n2c16.png create mode 100644 impeller/third_party/stb/stb/tests/pngsuite/16bit/oi2n0g16.png create mode 100644 impeller/third_party/stb/stb/tests/pngsuite/16bit/oi2n2c16.png create mode 100644 impeller/third_party/stb/stb/tests/pngsuite/16bit/oi4n0g16.png create mode 100644 impeller/third_party/stb/stb/tests/pngsuite/16bit/oi4n2c16.png create mode 100644 impeller/third_party/stb/stb/tests/pngsuite/16bit/oi9n0g16.png create mode 100644 impeller/third_party/stb/stb/tests/pngsuite/16bit/oi9n2c16.png create mode 100644 impeller/third_party/stb/stb/tests/pngsuite/16bit/tbbn2c16.png create mode 100644 impeller/third_party/stb/stb/tests/pngsuite/16bit/tbgn2c16.png create mode 100644 impeller/third_party/stb/stb/tests/pngsuite/16bit/tbwn0g16.png create mode 100644 impeller/third_party/stb/stb/tests/pngsuite/PngSuite.LICENSE create mode 100644 impeller/third_party/stb/stb/tests/pngsuite/corrupt/xc1n0g08.png create mode 100644 impeller/third_party/stb/stb/tests/pngsuite/corrupt/xc9n2c08.png create mode 100644 impeller/third_party/stb/stb/tests/pngsuite/corrupt/xcrn0g04.png create mode 100644 impeller/third_party/stb/stb/tests/pngsuite/corrupt/xcsn0g01.png create mode 100644 impeller/third_party/stb/stb/tests/pngsuite/corrupt/xd0n2c08.png create mode 100644 impeller/third_party/stb/stb/tests/pngsuite/corrupt/xd3n2c08.png create mode 100644 impeller/third_party/stb/stb/tests/pngsuite/corrupt/xd9n2c08.png create mode 100644 impeller/third_party/stb/stb/tests/pngsuite/corrupt/xdtn0g01.png create mode 100644 impeller/third_party/stb/stb/tests/pngsuite/corrupt/xhdn0g08.png create mode 100644 impeller/third_party/stb/stb/tests/pngsuite/corrupt/xlfn0g04.png create mode 100644 impeller/third_party/stb/stb/tests/pngsuite/corrupt/xs1n0g01.png create mode 100644 impeller/third_party/stb/stb/tests/pngsuite/corrupt/xs2n0g01.png create mode 100644 impeller/third_party/stb/stb/tests/pngsuite/corrupt/xs4n0g01.png create mode 100644 impeller/third_party/stb/stb/tests/pngsuite/corrupt/xs7n0g01.png create mode 100644 impeller/third_party/stb/stb/tests/pngsuite/primary/basi0g01.png create mode 100644 impeller/third_party/stb/stb/tests/pngsuite/primary/basi0g02.png create mode 100644 impeller/third_party/stb/stb/tests/pngsuite/primary/basi0g04.png create mode 100644 impeller/third_party/stb/stb/tests/pngsuite/primary/basi0g08.png create mode 100644 impeller/third_party/stb/stb/tests/pngsuite/primary/basi2c08.png create mode 100644 impeller/third_party/stb/stb/tests/pngsuite/primary/basi3p01.png create mode 100644 impeller/third_party/stb/stb/tests/pngsuite/primary/basi3p02.png create mode 100644 impeller/third_party/stb/stb/tests/pngsuite/primary/basi3p04.png create mode 100644 impeller/third_party/stb/stb/tests/pngsuite/primary/basi3p08.png create mode 100644 impeller/third_party/stb/stb/tests/pngsuite/primary/basi4a08.png create mode 100644 impeller/third_party/stb/stb/tests/pngsuite/primary/basi6a08.png create mode 100644 impeller/third_party/stb/stb/tests/pngsuite/primary/basn0g01.png create mode 100644 impeller/third_party/stb/stb/tests/pngsuite/primary/basn0g02.png create mode 100644 impeller/third_party/stb/stb/tests/pngsuite/primary/basn0g04.png create mode 100644 impeller/third_party/stb/stb/tests/pngsuite/primary/basn0g08.png create mode 100644 impeller/third_party/stb/stb/tests/pngsuite/primary/basn2c08.png create mode 100644 impeller/third_party/stb/stb/tests/pngsuite/primary/basn3p01.png create mode 100644 impeller/third_party/stb/stb/tests/pngsuite/primary/basn3p02.png create mode 100644 impeller/third_party/stb/stb/tests/pngsuite/primary/basn3p04.png create mode 100644 impeller/third_party/stb/stb/tests/pngsuite/primary/basn3p08.png create mode 100644 impeller/third_party/stb/stb/tests/pngsuite/primary/basn4a08.png create mode 100644 impeller/third_party/stb/stb/tests/pngsuite/primary/basn6a08.png create mode 100644 impeller/third_party/stb/stb/tests/pngsuite/primary/bgai4a08.png create mode 100644 impeller/third_party/stb/stb/tests/pngsuite/primary/bgan6a08.png create mode 100644 impeller/third_party/stb/stb/tests/pngsuite/primary/bgbn4a08.png create mode 100644 impeller/third_party/stb/stb/tests/pngsuite/primary/bgwn6a08.png create mode 100644 impeller/third_party/stb/stb/tests/pngsuite/primary/s01i3p01.png create mode 100644 impeller/third_party/stb/stb/tests/pngsuite/primary/s01n3p01.png create mode 100644 impeller/third_party/stb/stb/tests/pngsuite/primary/s02i3p01.png create mode 100644 impeller/third_party/stb/stb/tests/pngsuite/primary/s02n3p01.png create mode 100644 impeller/third_party/stb/stb/tests/pngsuite/primary/s03i3p01.png create mode 100644 impeller/third_party/stb/stb/tests/pngsuite/primary/s03n3p01.png create mode 100644 impeller/third_party/stb/stb/tests/pngsuite/primary/s04i3p01.png create mode 100644 impeller/third_party/stb/stb/tests/pngsuite/primary/s04n3p01.png create mode 100644 impeller/third_party/stb/stb/tests/pngsuite/primary/s05i3p02.png create mode 100644 impeller/third_party/stb/stb/tests/pngsuite/primary/s05n3p02.png create mode 100644 impeller/third_party/stb/stb/tests/pngsuite/primary/s06i3p02.png create mode 100644 impeller/third_party/stb/stb/tests/pngsuite/primary/s06n3p02.png create mode 100644 impeller/third_party/stb/stb/tests/pngsuite/primary/s07i3p02.png create mode 100644 impeller/third_party/stb/stb/tests/pngsuite/primary/s07n3p02.png create mode 100644 impeller/third_party/stb/stb/tests/pngsuite/primary/s08i3p02.png create mode 100644 impeller/third_party/stb/stb/tests/pngsuite/primary/s08n3p02.png create mode 100644 impeller/third_party/stb/stb/tests/pngsuite/primary/s09i3p02.png create mode 100644 impeller/third_party/stb/stb/tests/pngsuite/primary/s09n3p02.png create mode 100644 impeller/third_party/stb/stb/tests/pngsuite/primary/s32i3p04.png create mode 100644 impeller/third_party/stb/stb/tests/pngsuite/primary/s32n3p04.png create mode 100644 impeller/third_party/stb/stb/tests/pngsuite/primary/s33i3p04.png create mode 100644 impeller/third_party/stb/stb/tests/pngsuite/primary/s33n3p04.png create mode 100644 impeller/third_party/stb/stb/tests/pngsuite/primary/s34i3p04.png create mode 100644 impeller/third_party/stb/stb/tests/pngsuite/primary/s34n3p04.png create mode 100644 impeller/third_party/stb/stb/tests/pngsuite/primary/s35i3p04.png create mode 100644 impeller/third_party/stb/stb/tests/pngsuite/primary/s35n3p04.png create mode 100644 impeller/third_party/stb/stb/tests/pngsuite/primary/s36i3p04.png create mode 100644 impeller/third_party/stb/stb/tests/pngsuite/primary/s36n3p04.png create mode 100644 impeller/third_party/stb/stb/tests/pngsuite/primary/s37i3p04.png create mode 100644 impeller/third_party/stb/stb/tests/pngsuite/primary/s37n3p04.png create mode 100644 impeller/third_party/stb/stb/tests/pngsuite/primary/s38i3p04.png create mode 100644 impeller/third_party/stb/stb/tests/pngsuite/primary/s38n3p04.png create mode 100644 impeller/third_party/stb/stb/tests/pngsuite/primary/s39i3p04.png create mode 100644 impeller/third_party/stb/stb/tests/pngsuite/primary/s39n3p04.png create mode 100644 impeller/third_party/stb/stb/tests/pngsuite/primary/s40i3p04.png create mode 100644 impeller/third_party/stb/stb/tests/pngsuite/primary/s40n3p04.png create mode 100644 impeller/third_party/stb/stb/tests/pngsuite/primary/tbbn0g04.png create mode 100644 impeller/third_party/stb/stb/tests/pngsuite/primary/tbbn3p08.png create mode 100644 impeller/third_party/stb/stb/tests/pngsuite/primary/tbgn3p08.png create mode 100644 impeller/third_party/stb/stb/tests/pngsuite/primary/tbrn2c08.png create mode 100644 impeller/third_party/stb/stb/tests/pngsuite/primary/tbwn3p08.png create mode 100644 impeller/third_party/stb/stb/tests/pngsuite/primary/tbyn3p08.png create mode 100644 impeller/third_party/stb/stb/tests/pngsuite/primary/tm3n3p02.png create mode 100644 impeller/third_party/stb/stb/tests/pngsuite/primary/tp0n0g08.png create mode 100644 impeller/third_party/stb/stb/tests/pngsuite/primary/tp0n2c08.png create mode 100644 impeller/third_party/stb/stb/tests/pngsuite/primary/tp0n3p08.png create mode 100644 impeller/third_party/stb/stb/tests/pngsuite/primary/tp1n3p08.png create mode 100644 impeller/third_party/stb/stb/tests/pngsuite/primary/z00n2c08.png create mode 100644 impeller/third_party/stb/stb/tests/pngsuite/primary/z03n2c08.png create mode 100644 impeller/third_party/stb/stb/tests/pngsuite/primary/z06n2c08.png create mode 100644 impeller/third_party/stb/stb/tests/pngsuite/primary/z09n2c08.png create mode 100644 impeller/third_party/stb/stb/tests/pngsuite/primary_check/basi0g01.png create mode 100644 impeller/third_party/stb/stb/tests/pngsuite/primary_check/basi0g02.png create mode 100644 impeller/third_party/stb/stb/tests/pngsuite/primary_check/basi0g04.png create mode 100644 impeller/third_party/stb/stb/tests/pngsuite/primary_check/basi0g08.png create mode 100644 impeller/third_party/stb/stb/tests/pngsuite/primary_check/basi2c08.png create mode 100644 impeller/third_party/stb/stb/tests/pngsuite/primary_check/basi3p01.png create mode 100644 impeller/third_party/stb/stb/tests/pngsuite/primary_check/basi3p02.png create mode 100644 impeller/third_party/stb/stb/tests/pngsuite/primary_check/basi3p04.png create mode 100644 impeller/third_party/stb/stb/tests/pngsuite/primary_check/basi3p08.png create mode 100644 impeller/third_party/stb/stb/tests/pngsuite/primary_check/basi4a08.png create mode 100644 impeller/third_party/stb/stb/tests/pngsuite/primary_check/basi6a08.png create mode 100644 impeller/third_party/stb/stb/tests/pngsuite/primary_check/basn0g01.png create mode 100644 impeller/third_party/stb/stb/tests/pngsuite/primary_check/basn0g02.png create mode 100644 impeller/third_party/stb/stb/tests/pngsuite/primary_check/basn0g04.png create mode 100644 impeller/third_party/stb/stb/tests/pngsuite/primary_check/basn0g08.png create mode 100644 impeller/third_party/stb/stb/tests/pngsuite/primary_check/basn2c08.png create mode 100644 impeller/third_party/stb/stb/tests/pngsuite/primary_check/basn3p01.png create mode 100644 impeller/third_party/stb/stb/tests/pngsuite/primary_check/basn3p02.png create mode 100644 impeller/third_party/stb/stb/tests/pngsuite/primary_check/basn3p04.png create mode 100644 impeller/third_party/stb/stb/tests/pngsuite/primary_check/basn3p08.png create mode 100644 impeller/third_party/stb/stb/tests/pngsuite/primary_check/basn4a08.png create mode 100644 impeller/third_party/stb/stb/tests/pngsuite/primary_check/basn6a08.png create mode 100644 impeller/third_party/stb/stb/tests/pngsuite/primary_check/bgai4a08.png create mode 100644 impeller/third_party/stb/stb/tests/pngsuite/primary_check/bgan6a08.png create mode 100644 impeller/third_party/stb/stb/tests/pngsuite/primary_check/bgbn4a08.png create mode 100644 impeller/third_party/stb/stb/tests/pngsuite/primary_check/bgwn6a08.png create mode 100644 impeller/third_party/stb/stb/tests/pngsuite/primary_check/s01i3p01.png create mode 100644 impeller/third_party/stb/stb/tests/pngsuite/primary_check/s01n3p01.png create mode 100644 impeller/third_party/stb/stb/tests/pngsuite/primary_check/s02i3p01.png create mode 100644 impeller/third_party/stb/stb/tests/pngsuite/primary_check/s02n3p01.png create mode 100644 impeller/third_party/stb/stb/tests/pngsuite/primary_check/s03i3p01.png create mode 100644 impeller/third_party/stb/stb/tests/pngsuite/primary_check/s03n3p01.png create mode 100644 impeller/third_party/stb/stb/tests/pngsuite/primary_check/s04i3p01.png create mode 100644 impeller/third_party/stb/stb/tests/pngsuite/primary_check/s04n3p01.png create mode 100644 impeller/third_party/stb/stb/tests/pngsuite/primary_check/s05i3p02.png create mode 100644 impeller/third_party/stb/stb/tests/pngsuite/primary_check/s05n3p02.png create mode 100644 impeller/third_party/stb/stb/tests/pngsuite/primary_check/s06i3p02.png create mode 100644 impeller/third_party/stb/stb/tests/pngsuite/primary_check/s06n3p02.png create mode 100644 impeller/third_party/stb/stb/tests/pngsuite/primary_check/s07i3p02.png create mode 100644 impeller/third_party/stb/stb/tests/pngsuite/primary_check/s07n3p02.png create mode 100644 impeller/third_party/stb/stb/tests/pngsuite/primary_check/s08i3p02.png create mode 100644 impeller/third_party/stb/stb/tests/pngsuite/primary_check/s08n3p02.png create mode 100644 impeller/third_party/stb/stb/tests/pngsuite/primary_check/s09i3p02.png create mode 100644 impeller/third_party/stb/stb/tests/pngsuite/primary_check/s09n3p02.png create mode 100644 impeller/third_party/stb/stb/tests/pngsuite/primary_check/s32i3p04.png create mode 100644 impeller/third_party/stb/stb/tests/pngsuite/primary_check/s32n3p04.png create mode 100644 impeller/third_party/stb/stb/tests/pngsuite/primary_check/s33i3p04.png create mode 100644 impeller/third_party/stb/stb/tests/pngsuite/primary_check/s33n3p04.png create mode 100644 impeller/third_party/stb/stb/tests/pngsuite/primary_check/s34i3p04.png create mode 100644 impeller/third_party/stb/stb/tests/pngsuite/primary_check/s34n3p04.png create mode 100644 impeller/third_party/stb/stb/tests/pngsuite/primary_check/s35i3p04.png create mode 100644 impeller/third_party/stb/stb/tests/pngsuite/primary_check/s35n3p04.png create mode 100644 impeller/third_party/stb/stb/tests/pngsuite/primary_check/s36i3p04.png create mode 100644 impeller/third_party/stb/stb/tests/pngsuite/primary_check/s36n3p04.png create mode 100644 impeller/third_party/stb/stb/tests/pngsuite/primary_check/s37i3p04.png create mode 100644 impeller/third_party/stb/stb/tests/pngsuite/primary_check/s37n3p04.png create mode 100644 impeller/third_party/stb/stb/tests/pngsuite/primary_check/s38i3p04.png create mode 100644 impeller/third_party/stb/stb/tests/pngsuite/primary_check/s38n3p04.png create mode 100644 impeller/third_party/stb/stb/tests/pngsuite/primary_check/s39i3p04.png create mode 100644 impeller/third_party/stb/stb/tests/pngsuite/primary_check/s39n3p04.png create mode 100644 impeller/third_party/stb/stb/tests/pngsuite/primary_check/s40i3p04.png create mode 100644 impeller/third_party/stb/stb/tests/pngsuite/primary_check/s40n3p04.png create mode 100644 impeller/third_party/stb/stb/tests/pngsuite/primary_check/tbbn0g04.png create mode 100644 impeller/third_party/stb/stb/tests/pngsuite/primary_check/tbbn3p08.png create mode 100644 impeller/third_party/stb/stb/tests/pngsuite/primary_check/tbgn3p08.png create mode 100644 impeller/third_party/stb/stb/tests/pngsuite/primary_check/tbrn2c08.png create mode 100644 impeller/third_party/stb/stb/tests/pngsuite/primary_check/tbwn3p08.png create mode 100644 impeller/third_party/stb/stb/tests/pngsuite/primary_check/tbyn3p08.png create mode 100644 impeller/third_party/stb/stb/tests/pngsuite/primary_check/tm3n3p02.png create mode 100644 impeller/third_party/stb/stb/tests/pngsuite/primary_check/tp0n0g08.png create mode 100644 impeller/third_party/stb/stb/tests/pngsuite/primary_check/tp0n2c08.png create mode 100644 impeller/third_party/stb/stb/tests/pngsuite/primary_check/tp0n3p08.png create mode 100644 impeller/third_party/stb/stb/tests/pngsuite/primary_check/tp1n3p08.png create mode 100644 impeller/third_party/stb/stb/tests/pngsuite/primary_check/z00n2c08.png create mode 100644 impeller/third_party/stb/stb/tests/pngsuite/primary_check/z03n2c08.png create mode 100644 impeller/third_party/stb/stb/tests/pngsuite/primary_check/z06n2c08.png create mode 100644 impeller/third_party/stb/stb/tests/pngsuite/primary_check/z09n2c08.png create mode 100644 impeller/third_party/stb/stb/tests/pngsuite/unused/ccwn2c08.png create mode 100644 impeller/third_party/stb/stb/tests/pngsuite/unused/ccwn3p08.png create mode 100644 impeller/third_party/stb/stb/tests/pngsuite/unused/cdfn2c08.png create mode 100644 impeller/third_party/stb/stb/tests/pngsuite/unused/cdhn2c08.png create mode 100644 impeller/third_party/stb/stb/tests/pngsuite/unused/cdsn2c08.png create mode 100644 impeller/third_party/stb/stb/tests/pngsuite/unused/cdun2c08.png create mode 100644 impeller/third_party/stb/stb/tests/pngsuite/unused/ch1n3p04.png create mode 100644 impeller/third_party/stb/stb/tests/pngsuite/unused/ch2n3p08.png create mode 100644 impeller/third_party/stb/stb/tests/pngsuite/unused/cm0n0g04.png create mode 100644 impeller/third_party/stb/stb/tests/pngsuite/unused/cm7n0g04.png create mode 100644 impeller/third_party/stb/stb/tests/pngsuite/unused/cm9n0g04.png create mode 100644 impeller/third_party/stb/stb/tests/pngsuite/unused/cs3n2c16.png create mode 100644 impeller/third_party/stb/stb/tests/pngsuite/unused/cs3n3p08.png create mode 100644 impeller/third_party/stb/stb/tests/pngsuite/unused/cs5n2c08.png create mode 100644 impeller/third_party/stb/stb/tests/pngsuite/unused/cs5n3p08.png create mode 100644 impeller/third_party/stb/stb/tests/pngsuite/unused/cs8n2c08.png create mode 100644 impeller/third_party/stb/stb/tests/pngsuite/unused/cs8n3p08.png create mode 100644 impeller/third_party/stb/stb/tests/pngsuite/unused/ct0n0g04.png create mode 100644 impeller/third_party/stb/stb/tests/pngsuite/unused/ct1n0g04.png create mode 100644 impeller/third_party/stb/stb/tests/pngsuite/unused/cten0g04.png create mode 100644 impeller/third_party/stb/stb/tests/pngsuite/unused/ctfn0g04.png create mode 100644 impeller/third_party/stb/stb/tests/pngsuite/unused/ctgn0g04.png create mode 100644 impeller/third_party/stb/stb/tests/pngsuite/unused/cthn0g04.png create mode 100644 impeller/third_party/stb/stb/tests/pngsuite/unused/ctjn0g04.png create mode 100644 impeller/third_party/stb/stb/tests/pngsuite/unused/ctzn0g04.png create mode 100644 impeller/third_party/stb/stb/tests/pngsuite/unused/f00n0g08.png create mode 100644 impeller/third_party/stb/stb/tests/pngsuite/unused/f00n2c08.png create mode 100644 impeller/third_party/stb/stb/tests/pngsuite/unused/f01n0g08.png create mode 100644 impeller/third_party/stb/stb/tests/pngsuite/unused/f01n2c08.png create mode 100644 impeller/third_party/stb/stb/tests/pngsuite/unused/f02n0g08.png create mode 100644 impeller/third_party/stb/stb/tests/pngsuite/unused/f02n2c08.png create mode 100644 impeller/third_party/stb/stb/tests/pngsuite/unused/f03n0g08.png create mode 100644 impeller/third_party/stb/stb/tests/pngsuite/unused/f03n2c08.png create mode 100644 impeller/third_party/stb/stb/tests/pngsuite/unused/f04n0g08.png create mode 100644 impeller/third_party/stb/stb/tests/pngsuite/unused/f04n2c08.png create mode 100644 impeller/third_party/stb/stb/tests/pngsuite/unused/f99n0g04.png create mode 100644 impeller/third_party/stb/stb/tests/pngsuite/unused/g03n0g16.png create mode 100644 impeller/third_party/stb/stb/tests/pngsuite/unused/g03n2c08.png create mode 100644 impeller/third_party/stb/stb/tests/pngsuite/unused/g03n3p04.png create mode 100644 impeller/third_party/stb/stb/tests/pngsuite/unused/g04n0g16.png create mode 100644 impeller/third_party/stb/stb/tests/pngsuite/unused/g04n2c08.png create mode 100644 impeller/third_party/stb/stb/tests/pngsuite/unused/g04n3p04.png create mode 100644 impeller/third_party/stb/stb/tests/pngsuite/unused/g05n0g16.png create mode 100644 impeller/third_party/stb/stb/tests/pngsuite/unused/g05n2c08.png create mode 100644 impeller/third_party/stb/stb/tests/pngsuite/unused/g05n3p04.png create mode 100644 impeller/third_party/stb/stb/tests/pngsuite/unused/g07n0g16.png create mode 100644 impeller/third_party/stb/stb/tests/pngsuite/unused/g07n2c08.png create mode 100644 impeller/third_party/stb/stb/tests/pngsuite/unused/g07n3p04.png create mode 100644 impeller/third_party/stb/stb/tests/pngsuite/unused/g10n0g16.png create mode 100644 impeller/third_party/stb/stb/tests/pngsuite/unused/g10n2c08.png create mode 100644 impeller/third_party/stb/stb/tests/pngsuite/unused/g10n3p04.png create mode 100644 impeller/third_party/stb/stb/tests/pngsuite/unused/g25n0g16.png create mode 100644 impeller/third_party/stb/stb/tests/pngsuite/unused/g25n2c08.png create mode 100644 impeller/third_party/stb/stb/tests/pngsuite/unused/g25n3p04.png create mode 100644 impeller/third_party/stb/stb/tests/pngsuite/unused/pp0n2c16.png create mode 100644 impeller/third_party/stb/stb/tests/pngsuite/unused/pp0n6a08.png create mode 100644 impeller/third_party/stb/stb/tests/pngsuite/unused/ps1n0g08.png create mode 100644 impeller/third_party/stb/stb/tests/pngsuite/unused/ps1n2c16.png create mode 100644 impeller/third_party/stb/stb/tests/pngsuite/unused/ps2n0g08.png create mode 100644 impeller/third_party/stb/stb/tests/pngsuite/unused/ps2n2c16.png create mode 100644 impeller/third_party/stb/stb/tests/resample_test.cpp create mode 100644 impeller/third_party/stb/stb/tests/resample_test_c.c create mode 100644 impeller/third_party/stb/stb/tests/resize.dsp create mode 100644 impeller/third_party/stb/stb/tests/stb.c create mode 100644 impeller/third_party/stb/stb/tests/stb.dsp create mode 100644 impeller/third_party/stb/stb/tests/stb.dsw create mode 100644 impeller/third_party/stb/stb/tests/stb_cpp.cpp create mode 100644 impeller/third_party/stb/stb/tests/stb_cpp.dsp create mode 100644 impeller/third_party/stb/stb/tests/stretch_test.c create mode 100644 impeller/third_party/stb/stb/tests/stretch_test.dsp create mode 100644 impeller/third_party/stb/stb/tests/stretchy_buffer_test.c create mode 100644 impeller/third_party/stb/stb/tests/test_c_compilation.c create mode 100644 impeller/third_party/stb/stb/tests/test_cpp_compilation.cpp create mode 100644 impeller/third_party/stb/stb/tests/test_truetype.c create mode 100644 impeller/third_party/stb/stb/tests/test_vorbis.c create mode 100644 impeller/third_party/stb/stb/tests/textedit_sample.c create mode 100644 impeller/third_party/stb/stb/tests/tilemap_editor_integration_example.c create mode 100644 impeller/third_party/stb/stb/tests/vorbseek/vorbseek.c create mode 100644 impeller/third_party/stb/stb/tests/vorbseek/vorbseek.dsp create mode 100644 impeller/third_party/stb/stb/tools/README.footer.md create mode 100644 impeller/third_party/stb/stb/tools/README.header.md create mode 100644 impeller/third_party/stb/stb/tools/README.list create mode 100644 impeller/third_party/stb/stb/tools/easy_font_maker.c create mode 100644 impeller/third_party/stb/stb/tools/make_readme.c create mode 100644 impeller/third_party/stb/stb/tools/make_readme.dsp create mode 100644 impeller/third_party/stb/stb/tools/mr.bat create mode 100644 impeller/third_party/stb/stb/tools/unicode.c create mode 100644 impeller/third_party/stb/stb/tools/unicode/unicode.dsp diff --git a/impeller/impeller/BUILD.gn b/impeller/impeller/BUILD.gn index f10f70df6b4ae..306f295025d78 100644 --- a/impeller/impeller/BUILD.gn +++ b/impeller/impeller/BUILD.gn @@ -47,8 +47,6 @@ source_set("impeller") { image_sources = [ "image/Image.cc", "image/Image.h", - "image/ImageEncoder.cc", - "image/ImageEncoder.h", "image/ImageResult.cc", "image/ImageResult.h", ] @@ -57,6 +55,7 @@ source_set("impeller") { deps = [ "//flutter/fml", + "//flutter/impeller/third_party/stb", "//flutter/runtime:libdart", # For tracing, seems to be pulled in by FML. ] } diff --git a/impeller/impeller/entity/Entity.cc b/impeller/impeller/entity/Entity.cc index 6920d120342ee..772ceef394eea 100644 --- a/impeller/impeller/entity/Entity.cc +++ b/impeller/impeller/entity/Entity.cc @@ -121,14 +121,6 @@ void Entity::setStrokeSize(double strokeSize) { _strokeSize = strokeSize; } -const image::Image& Entity::contents() const { - return _contents; -} - -void Entity::setContents(image::Image image) { - _contents = std::move(image); -} - const geom::Path& Entity::path() const { return _path; } diff --git a/impeller/impeller/entity/Entity.h b/impeller/impeller/entity/Entity.h index fe0717f72015e..c1e9e4d557b24 100644 --- a/impeller/impeller/entity/Entity.h +++ b/impeller/impeller/entity/Entity.h @@ -180,10 +180,6 @@ class Entity { void setStrokeSize(double strokeSize); - const image::Image& contents() const; - - void setContents(image::Image image); - const geom::Path& path() const; void setPath(geom::Path path); @@ -194,7 +190,7 @@ class Entity { geom::Point _anchorPoint; geom::Matrix _transformation; Color _backgroundColor; - image::Image _contents; + geom::Path _path; double _opacity; Color _strokeColor; diff --git a/impeller/impeller/image/Image.cc b/impeller/impeller/image/Image.cc index 953d2f0a1a493..bf0bf186cc008 100644 --- a/impeller/impeller/image/Image.cc +++ b/impeller/impeller/image/Image.cc @@ -5,30 +5,17 @@ #include "Image.h" #include -#include "ImageSource.h" namespace rl { namespace image { -Image::Image() = default; - -Image::Image(fml::RefPtr sourceAllocation) - : _source(ImageSource::Create(std::move(sourceAllocation))) {} - -Image::Image(core::FileHandle sourceFile) - : _source(ImageSource::Create(std::move(sourceFile))) {} +Image::Image(std::shared_ptr sourceAllocation) + : _source(std::move(sourceAllocation)) {} Image::~Image() = default; ImageResult Image::decode() const { - if (_source == nullptr) { - return {}; - } - - _source->prepareForUse(); - - if (_source->sourceDataSize() == 0) { - RL_LOG("Source data for image decoding was zero sized."); + if (!_source) { return {}; } @@ -37,27 +24,26 @@ ImageResult Image::decode() const { int comps = 0; stbi_uc* decoded = - stbi_load_from_memory(_source->sourceData(), // Source Data - _source->sourceDataSize(), // Source Data Size - &width, // Out: Width - &height, // Out: Height - &comps, // Out: Components + stbi_load_from_memory(_source->GetMapping(), // Source Data + _source->GetSize(), // Source Data Size + &width, // Out: Width + &height, // Out: Height + &comps, // Out: Components STBI_default); - auto destinationAllocation = fml::RefPtr{ - decoded, width * height * comps * sizeof(stbi_uc)}; - - /* - * If either the decoded allocation is null or the size works out to be zero, - * the allocation will mark itself as not ready and we know that the decode - * job failed. - */ - - if (!destinationAllocation.isReady()) { - RL_LOG("Destination allocation for image decoding was null."); + if (decoded == nullptr) { + FML_LOG(ERROR) << "Could not decode image from host memory."; return {}; } + auto destinationAllocation = std::make_shared( + decoded, // bytes + width * height * comps * sizeof(stbi_uc), // byte size + [](const uint8_t* data, size_t size) { + ::stbi_image_free(const_cast(data)); + } // release proc + ); + /* * Make sure we got a valid component set. */ @@ -82,7 +68,7 @@ ImageResult Image::decode() const { } if (components == ImageResult::Components::Invalid) { - RL_LOG("Could not detect image components when decoding."); + FML_LOG(ERROR) << "Could not detect image components when decoding."; return {}; } @@ -95,15 +81,7 @@ ImageResult Image::decode() const { } bool Image::isValid() const { - return _source != nullptr; -} - -std::size_t Image::Hash::operator()(const Image& key) const { - return std::hash()(key._source); -} - -bool Image::Equal::operator()(const Image& lhs, const Image& rhs) const { - return lhs._source == rhs._source; + return static_cast(_source); } } // namespace image diff --git a/impeller/impeller/image/Image.h b/impeller/impeller/image/Image.h index 8b163b69608ca..40b5471cef84e 100644 --- a/impeller/impeller/image/Image.h +++ b/impeller/impeller/image/Image.h @@ -17,9 +17,7 @@ class ImageSource; class Image { public: - Image(); - - Image(fml::RefPtr sourceAllocation); + Image(std::shared_ptr sourceAllocation); ~Image(); @@ -27,16 +25,8 @@ class Image { bool isValid() const; - struct Hash { - std::size_t operator()(const Image& key) const; - }; - - struct Equal { - bool operator()(const Image& lhs, const Image& rhs) const; - }; - private: - std::shared_ptr _source; + std::shared_ptr _source; }; } // namespace image diff --git a/impeller/impeller/image/ImageEncoder.cc b/impeller/impeller/image/ImageEncoder.cc deleted file mode 100644 index 939e9e07f6dd9..0000000000000 --- a/impeller/impeller/image/ImageEncoder.cc +++ /dev/null @@ -1,86 +0,0 @@ -/* - * This source file is part of the Radar project. - * Licensed under the MIT License. See LICENSE file for details. - */ - -#include "ImageEncoder.h" -#include - -namespace rl { -namespace image { - -ImageEncoder::ImageEncoder(Type type, core::FileHandle handle) - : _isReady(false), _type(type), _adapter(std::move(handle)) { - if (!_adapter.isValid()) { - return; - } - - _isReady = true; -} - -ImageEncoder::~ImageEncoder() = default; - -bool ImageEncoder::isReady() const { - return _isReady; -} - -bool ImageEncoder::encode(ImageResult image) { - if (!_isReady) { - return false; - } - - if (!image.wasSuccessful()) { - return false; - } - - switch (_type) { - case Type::PNG: - return encodePNG(std::move(image)); - } - - return false; -} - -int ComponentsToSize(ImageResult::Components components) { - switch (components) { - case ImageResult::Components::Invalid: - return 0; - case ImageResult::Components::Grey: - return 1; - case ImageResult::Components::GreyAlpha: - return 2; - case ImageResult::Components::RGB: - return 3; - case ImageResult::Components::RGBA: - return 4; - } - - return 0; -} - -bool ImageEncoder::encodePNG(ImageResult image) { - auto size = image.size(); - - int componentSize = ComponentsToSize(image.components()); - - auto callback = [](void* context, void* data, int size) { - reinterpret_cast(context)->write( - reinterpret_cast(data), size); - }; - - return stbi_write_png_to_func(callback, // - this, // - size.width, // - size.height, // - componentSize, // - image.allocation().data(), // - componentSize // - ) == 1; -} - -void ImageEncoder::write(const uint8_t* data, size_t size) { - RL_UNUSED(_adapter.write(data, size)); -} - -} // namespace image -} // namespace rl diff --git a/impeller/impeller/image/ImageEncoder.h b/impeller/impeller/image/ImageEncoder.h deleted file mode 100644 index ba49a5bc2a2d4..0000000000000 --- a/impeller/impeller/image/ImageEncoder.h +++ /dev/null @@ -1,41 +0,0 @@ -/* - * This source file is part of the Radar project. - * Licensed under the MIT License. See LICENSE file for details. - */ - -#pragma once - -#include "ImageResult.h" -#include "Size.h" - -namespace rl { -namespace image { - -class ImageEncoder { - public: - enum class Type { - PNG, - }; - - ImageEncoder(Type type, core::FileHandle handle); - - ~ImageEncoder(); - - bool isReady() const; - - bool encode(ImageResult image); - - private: - bool _isReady; - Type _type; - core::FileIOAdapter _adapter; - - bool encodePNG(ImageResult image); - - void write(const uint8_t* data, size_t size); - - FML_DISALLOW_COPY_AND_ASSIGN(ImageEncoder); -}; - -} // namespace image -} // namespace rl diff --git a/impeller/impeller/image/ImageResult.cc b/impeller/impeller/image/ImageResult.cc index f9dbbb592297b..09c4ea10eb728 100644 --- a/impeller/impeller/image/ImageResult.cc +++ b/impeller/impeller/image/ImageResult.cc @@ -8,32 +8,17 @@ namespace rl { namespace image { +ImageResult::ImageResult() = default; + ImageResult::ImageResult(geom::Size size, Components components, - fml::RefPtr allocation) + std::shared_ptr allocation) : _success(true), _size(size), _components(components), _allocation(std::move(allocation)) {} -ImageResult::ImageResult() : _success(false) {} - -ImageResult::ImageResult(ImageResult&&) = default; - -ImageResult& ImageResult::operator=(ImageResult&& other) { - _success = other._success; - other._success = false; - - _size = other._size; - other._size = geom::Size{}; - - _components = other._components; - other._components = Components::Invalid; - - _allocation = std::move(other._allocation); - - return *this; -} +ImageResult::~ImageResult() = default; bool ImageResult::wasSuccessful() const { return _success; @@ -47,11 +32,9 @@ ImageResult::Components ImageResult::components() const { return _components; } -const fml::RefPtr& ImageResult::allocation() const { +const std::shared_ptr& ImageResult::allocation() const { return _allocation; } -ImageResult::~ImageResult() = default; - } // namespace image } // namespace rl diff --git a/impeller/impeller/image/ImageResult.h b/impeller/impeller/image/ImageResult.h index 4ee85619b7fb5..8943dc02d7c9e 100644 --- a/impeller/impeller/image/ImageResult.h +++ b/impeller/impeller/image/ImageResult.h @@ -5,10 +5,11 @@ #pragma once +#include + #include "Size.h" #include "flutter/fml/macros.h" #include "flutter/fml/mapping.h" -#include "flutter/fml/memory/ref_counted.h" namespace rl { namespace image { @@ -27,11 +28,7 @@ class ImageResult { ImageResult(geom::Size size, Components components, - fml::RefPtr allocation); - - ImageResult(ImageResult&&); - - ImageResult& operator=(ImageResult&&); + std::shared_ptr allocation); ~ImageResult(); @@ -41,13 +38,13 @@ class ImageResult { Components components() const; - const fml::RefPtr& allocation() const; + const std::shared_ptr& allocation() const; private: - bool _success; + bool _success = false; geom::Size _size; - Components _components; - fml::RefPtr _allocation; + Components _components = Components::Invalid; + std::shared_ptr _allocation; FML_DISALLOW_COPY_AND_ASSIGN(ImageResult); }; diff --git a/impeller/third_party/stb/BUILD.gn b/impeller/third_party/stb/BUILD.gn new file mode 100644 index 0000000000000..942bbd0ba1685 --- /dev/null +++ b/impeller/third_party/stb/BUILD.gn @@ -0,0 +1,12 @@ +# Copyright 2013 The Flutter Authors. All rights reserved. +# Use of this source code is governed by a BSD-style license that can be +# found in the LICENSE file. + +config("stb_config") { + include_dirs = [ "stb" ] +} + +source_set("stb") { + public_configs = [ ":stb_config" ] + sources = [ "STBImplementation.c" ] +} diff --git a/impeller/third_party/stb/STBImplementation.c b/impeller/third_party/stb/STBImplementation.c new file mode 100644 index 0000000000000..b8063c168d537 --- /dev/null +++ b/impeller/third_party/stb/STBImplementation.c @@ -0,0 +1,12 @@ +/* + * This source file is part of the Radar project. + * Licensed under the MIT License. See LICENSE file for details. + */ + +#define STB_IMAGE_IMPLEMENTATION + +#include + +#define STB_IMAGE_WRITE_IMPLEMENTATION + +#include diff --git a/impeller/third_party/stb/stb/README.md b/impeller/third_party/stb/stb/README.md new file mode 100644 index 0000000000000..f813a5bb360f8 --- /dev/null +++ b/impeller/third_party/stb/stb/README.md @@ -0,0 +1,131 @@ + + +stb +=== + +single-file public domain libraries for C/C++ + +library | lastest version | category | LoC | description +--------------------- | ---- | -------- | --- | -------------------------------- +**stb_vorbis.c** | 1.09 | audio | 5397 | decode ogg vorbis files from file/memory to float/16-bit signed output +**stb_image.h** | 2.12 | graphics | 6755 | image loading/decoding from file/memory: JPG, PNG, TGA, BMP, PSD, GIF, HDR, PIC +**stb_truetype.h** | 1.11 | graphics | 3267 | parse, decode, and rasterize characters from truetype fonts +**stb_image_write.h** | 1.02 | graphics | 1048 | image writing to disk: PNG, TGA, BMP +**stb_image_resize.h** | 0.91 | graphics | 2578 | resize images larger/smaller with good quality +**stb_rect_pack.h** | 0.08 | graphics | 572 | simple 2D rectangle packer with decent quality +**stretchy_buffer.h** | 1.02 | utility | 216 | typesafe dynamic array for C (i.e. approximation to vector<>), doesn't compile as C++ +**stb_textedit.h** | 1.8 | user interface | 1304 | guts of a text editor for games etc implementing them from scratch +**stb_voxel_render.h** | 0.84 | 3D graphics | 3752 | Minecraft-esque voxel rendering "engine" with many more features +**stb_dxt.h** | 1.04 | 3D graphics | 630 | Fabian "ryg" Giesen's real-time DXT compressor +**stb_perlin.h** | 0.2 | 3D graphics | 182 | revised Perlin noise (3D input, 1D output) +**stb_easy_font.h** | 0.7 | 3D graphics | 258 | quick-and-dirty easy-to-deploy bitmap font for printing frame rate, etc +**stb_tilemap_editor.h** | 0.37 | game dev | 4131 | embeddable tilemap editor +**stb_herringbone_wa...** | 0.6 | game dev | 1220 | herringbone Wang tile map generator +**stb_c_lexer.h** | 0.07 | parsing | 816 | simplify writing parsers for C-like languages +**stb_divide.h** | 0.91 | math | 379 | more useful 32-bit modulus e.g. "euclidean divide" +**stb.h** | 2.27 | misc | 14185 | helper functions for C, mostly redundant in C++; basically author's personal stuff +**stb_leakcheck.h** | 0.2 | misc | 124 | quick-and-dirty malloc/free leak-checking + +Total libraries: 18 +Total lines of C code: 46814 + + +FAQ +--- + +#### What's the license? + +These libraries are in the public domain (or the equivalent where that is not +possible). You can do anything you want with them. You have no legal obligation +to do anything else, although I appreciate attribution. + +#### Are there other single-file public-domain/open source libraries with minimal dependencies out there? + +[Yes.](https://github.com/nothings/stb/blob/master/docs/other_libs.md) + +#### If I wrap an stb library in a new library, does the new library have to be public domain? + +No. + +#### Some of these libraries seem redundant to existing open source libraries. Are they better somehow? + +Generally they're only better in that they're easier to integrate, +easier to use, and easier to release (single file; good API; no +attribution requirement). They may be less featureful, slower, +and/or use more memory. If you're already using an equivalent +library, there's probably no good reason to switch. + +###### Can I link directly to the table of stb libraries? + +You can use [this URL](https://github.com/nothings/stb#stb_libs) to link directly to that list. + +#### Why do you list "lines of code"? It's a terrible metric. + +Just to give you some idea of the internal complexity of the library, +to help you manage your expectations, or to let you know what you're +getting into. While not all the libraries are written in the same +style, they're certainly similar styles, and so comparisons between +the libraries are probably still meaningful. + +Note though that the lines do include both the implementation, the +part that corresponds to a header file, and the documentation. + +#### Why single-file headers? + +Windows doesn't have standard directories where libraries +live. That makes deploying libraries in Windows a lot more +painful than open source developers on Unix-derivates generally +realize. (It also makes library dependencies a lot worse in Windows.) + +There's also a common problem in Windows where a library was built +against a different version of the runtime library, which causes +link conflicts and confusion. Shipping the libs as headers means +you normally just compile them straight into your project without +making libraries, thus sidestepping that problem. + +Making them a single file makes it very easy to just +drop them into a project that needs them. (Of course you can +still put them in a proper shared library tree if you want.) + +Why not two files, one a header and one an implementation? +The difference between 10 files and 9 files is not a big deal, +but the difference between 2 files and 1 file is a big deal. +You don't need to zip or tar the files up, you don't have to +remember to attach *two* files, etc. + +#### Why "stb"? Is this something to do with Set-Top Boxes? + +No, they are just the initials for my name, Sean T. Barrett. +This was not chosen out of egomania, but as a moderately sane +way of namespacing the filenames and source function names. + +#### Will you add more image types to stb_image.h? + +If people submit them, I generally add them, but the goal of stb_image +is less for applications like image viewer apps (which need to support +every type of image under the sun) and more for things like games which +can choose what images to use, so I may decline to add them if they're +too rare or if the size of implementation vs. apparent benefit is too low. + +#### Do you have any advice on how to create my own single-file library? + +Yes. https://github.com/nothings/stb/blob/master/docs/stb_howto.txt + +#### Why public domain? + +I prefer it over GPL, LGPL, BSD, zlib, etc. for many reasons. +Some of them are listed here: +https://github.com/nothings/stb/blob/master/docs/why_public_domain.md + +#### Why C? + +Primarily, because I use C, not C++. But it does also make it easier +for other people to use them from other languages. + +#### Why not C99? stdint.h, declare-anywhere, etc. + +I still use MSVC 6 (1998) as my IDE because it has better human factors +for me than later versions of MSVC. + + + diff --git a/impeller/third_party/stb/stb/data/atari_8bit_font_revised.png b/impeller/third_party/stb/stb/data/atari_8bit_font_revised.png new file mode 100644 index 0000000000000000000000000000000000000000..91c553c6387fd5eb6c09db3b1aa05ede45d4ba82 GIT binary patch literal 1769 zcmVU8P*7-ZbZ>KLZ*U+lnSp_Ufq@}0xwybFAi#%#fq@|}KQEO56)-X|e7nZL z$iTqBa9P*U#mSX{G{Bl%P*lRez;J+pfx##xwK$o9f#C}S14DXwNkIt%17i#W1A|CX zc0maP17iUL1A|C*NRTrF17iyV0~1e4YDEbH0|SF|enDkXW_m`6f}y3QrGjHhep0GJ zaAk2xYHqQDXI^rCQ9*uDVo7QW0|Nup4h9AW240u^5(W3f%sd4n162kpgNVo|1qcff zJ_s=cNG>fZg9jx8g8+j9g8_pBLjXe}Lp{R+hNBE`7{wV~7)u#fFy3PlV+vxLz;uCG zm^qSpA@ds+OO_6nTdaDlt*rOhEZL^9ePa)2-_4=K(Z%tFGm-NGmm}8}ZcXk5JW@PU zd4+f<@d@)yL(o<5icqT158+-B6_LH7;i6x}CW#w~Uy-Pgl#@Irl`kzV zeL|*8R$ca%T%Wv){2zs_iiJvgN^h0dsuZZ2sQy$tsNSU!s;Q*;LF<6_B%M@UD?LHI zSNcZ`78uqV#TeU~$eS{ozBIdFzSClfs*^S+dw;4dus<{M;#|MXC)T}S9v!D zcV!QCPhBq)ZyO(X-(bH4|NMaZz==UigLj2o41F2S6d@OB6%`R(5i>J(Puzn9wnW{e zu;hl6HK{k#IWjCVGqdJqU(99Cv(K+6*i`tgSi2;vbXD1#3jNBGs$DgVwO(~o>mN4i zHPtkqZIx>)Y(Ls5-Br|mx>vQYvH$Kwn@O`L|D75??eGkZnfg$5<;Xeg_o%+-I&+-3%01W^SH2RkDT>t<8AY({UO#lFTB>(_`g8%^e z{{R4h=>PzAFaQARU;qF*m;eA5Z<1fdMgRZ9Ya zfbQPS&6I9gN+2Pe@ZLVg#M7Yp7n=!*KBhs3V0@#wJYRvu4-EIh0eFD(WQ_!C0MS1yUwmi$ zJOuHM4kldFpro=@Axc~(JETEnh|ZpJz-E{swgX>4b&wnoTOZ>V&NW)JLl@EyagL5* z<%k38M}Yb>cl!Y3=nD{U?_0&j{5?*Mi=jpZqlp3NKM;{arC$))B zMejr(NF3})kH|P*J0!7;eV@V{PN3FAk#h8PVjiYvxZBBribS1Mmc9dOO0dyfO4tnn zK8E_624=rWe)H>v@3q07AGsTI9`c06a zBi|D*8`IJkCmwYDgqD_3QJpK^xBahAKCuD6l`Ca?g-)%{ag9C=&Yhx@1%%J?d6lf4 zQZU{emJ$p<+{ovqV1Jg+TUjyW^UBSQ#9pzkz|;~WyACtFJ3t(Z*0X<@>#=RhmhJALb# zqPNH3e6XUIB`hrGNSf;NlZCE6p9hSX3v7oSaOL0nxvA6QVph1qgEw(*3Jb)2oKG=j zlo+eWDSQqKaae^lrurOPvD>l+CW8l8QnXBWR~D(o-ap}$g0GlL20-|dP~QmIdiX|tuH~m;D>P3r z)#qd&2P>y<+ literal 0 HcmV?d00001 diff --git a/impeller/third_party/stb/stb/data/easy_font_raw.png b/impeller/third_party/stb/stb/data/easy_font_raw.png new file mode 100644 index 0000000000000000000000000000000000000000..2f08148b4614413c1c975ab264ca563fdf667c65 GIT binary patch literal 645 zcmV;00($+4P)dL_t(|UhS5FYQr!LMDHp4|8E|o2WL&hWbGJiRCUf$ zY{`)?s-q7{A4XlE+q@aq`w+kGn&`JeMBR@DHueUS=)8LYGtsDfr|vv4(Bu9ac}tzY z2j~Pid+-f}c9Mx;I0Ir^(F{}YpnIgUhRPP+y~S(gUW5JS8hGbs@i?eVmxG< zJ&U8a3qQ>wGB?wcgM&3m^G!?mg2nQbyDTC%*XSClo@CL6^-_J4ML!?v%VtfoaY#^W znS)E8gA7`nI0*SZ(J1c-;$Xg7DEkJiEFwCKsm1k0O2VcY07-K1DonO=GB}YFGT?j@ zK58ut#5*G(gM{aV#rn#kAtP2*m88~rsW<17E1PM#de0a^AIOrMG=c2FU{NHj4V5(% zNiEtp4=!zMG3(}PB}4w0ttA4okW~Qg)M9OFQJN9_^K?xXt19&t@?u$bEW!ngXv<=X zrqZ&j9_V*aMQ f1)BOYi~B&Iu%ieWP969(00000NkvXXu0mjfRf{XR literal 0 HcmV?d00001 diff --git a/impeller/third_party/stb/stb/data/herringbone/license.txt b/impeller/third_party/stb/stb/data/herringbone/license.txt new file mode 100644 index 0000000000000..11ffc42e57199 --- /dev/null +++ b/impeller/third_party/stb/stb/data/herringbone/license.txt @@ -0,0 +1,4 @@ +All files in this directory are in the public domain. Where +a public domain declaration is not recognized, you are granted +a license to freely use, modify, and redistribute them in +any way you choose. \ No newline at end of file diff --git a/impeller/third_party/stb/stb/data/herringbone/template_caves_limit_connectivity.png b/impeller/third_party/stb/stb/data/herringbone/template_caves_limit_connectivity.png new file mode 100644 index 0000000000000000000000000000000000000000..1c286e7ce44665d478b9bf4c767826793bab28b4 GIT binary patch literal 10818 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ⅇhl(t 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 M07*qoM6N<$f^o<=cK`qY literal 0 HcmV?d00001 diff --git a/impeller/third_party/stb/stb/data/herringbone/template_caves_tiny_corridors.png b/impeller/third_party/stb/stb/data/herringbone/template_caves_tiny_corridors.png new file mode 100644 index 0000000000000000000000000000000000000000..e9b0d444eade835c71554eb09e253d0d4c541fb1 GIT binary patch literal 4904 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 a@$UfWm`XT>Heh}L0000OtuM9NCZ-^ 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 literal 0 HcmV?d00001 diff --git a/impeller/third_party/stb/stb/data/herringbone/template_horizontal_corridors_v3.png b/impeller/third_party/stb/stb/data/herringbone/template_horizontal_corridors_v3.png new file mode 100644 index 0000000000000000000000000000000000000000..f921807255481904cd8800be3adff4cd7138acb1 GIT binary patch literal 6766 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 Qk literal 0 HcmV?d00001 diff --git a/impeller/third_party/stb/stb/data/herringbone/template_limit_connectivity_fat.png b/impeller/third_party/stb/stb/data/herringbone/template_limit_connectivity_fat.png new file mode 100644 index 0000000000000000000000000000000000000000..dda13023c77749a9f5dfe7d36eb9d5c62404e70b GIT binary patch literal 6655 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 literal 0 HcmV?d00001 diff --git a/impeller/third_party/stb/stb/data/herringbone/template_limited_connectivity.png b/impeller/third_party/stb/stb/data/herringbone/template_limited_connectivity.png new file mode 100644 index 0000000000000000000000000000000000000000..d9f97c9c6463b3ccba096d946e86412eb57e8846 GIT binary patch literal 6728 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 literal 0 HcmV?d00001 diff --git a/impeller/third_party/stb/stb/data/herringbone/template_maze_plus_2_wide.png b/impeller/third_party/stb/stb/data/herringbone/template_maze_plus_2_wide.png new file mode 100644 index 0000000000000000000000000000000000000000..d27f7bd8762461db38869c8397ca335f7619de3d GIT binary patch literal 13141 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 }>*>vQfd-W65-M^3{ff6BuI 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%0DA5(W={doe zMj?-_Wk()(rLJ#eN5SRL8BkS0O6>J->Y<}ra_-~BcF>dMvK(9v3c7(AGJjc^=b402 zbbrDc_wLDc5^Rok=_R9CT2WzQ7B5$N0QCZWo!ZsGsznOD=uMfO%xIt&KB|PrjjI=S z7A?7z|1i!Y7F-VQgMUsBFLwGrCb}3n$UtnKj2^&bF!1TT{H~1a10^DN=rAL;m|Ps1H%lWKXY}xr;sW}qem^;05T)e|-^8<; zdF*9&Ch*absYqUJBmJs$9*Jg7I*{UqpRIZOgIIzFtM{SGQ)GD7Cx^Dn} z=Tzju!!eU@a7lvH_IFB50k2>o$gmH=rS+--p5xIfdC8b2V_?{!r{-Vufc+FJ-b4}_Q3;Nqsd^^%h7Of0_RNs~)WCD`Z@}2Q zU+s8~w&XpF0 zA*_A=6Yav&Sff7|s`$$!Zo?vd=SsAG;VAVy3?1H(rkd6Et_rkB;_|kbJYW!fzBovL zCRn*lxJ|F3iCgk1S%mN{S-c0e&v@peU$ODa47R8-sL2o19j1(IXh*N&4npv}`-ZuJ z)nB7JdHZz<%_v+qoGfwtdaFKIO}^*n`a2(cOqqBgLGyNFKv&{3xxRAhunAgjdUyDZ zes5@rc7C7wNr(Gs&A$%7GJ(-x024Hj_<`d4eyL3-RdMqxNE^mgr22|EQP^-J!dPmO zA=zQ*jz)Rami89+WcANm!?L?0K9#z)KW!`1+9x*ph;L3Ll8|^*F_0?rQGby8stz>p z|08<(O<^J%6}`-CwRq@R>dN8b3G#4I%X_NLqx(VR{*Lmc%;VPf@X}X=x4359s+b+_ z)RJQ(E98x($?k@P2cI59qfGhZ+xf-y_;-{)g(;=OnOMszK8J&Ch16rM)Ux@ooHBy? z8`)l^SLwaVF6(WFe$CkLs7rM0E57nqxk_82SV8(70u6Dx3$ulwxAeR*G@rbKC6}2n zDN_97nUmS0b+-MR+OPAq3cJYtp{t>6NnuSj!kLI`*ZP?;>{@3rw~Y8~+q-kg<>_p$ zRCmUUBgPmVtri6-Z)cx{dTe(N@|$>OOyP>A-0kPT?4Xigy+hQ`_+|Qxi`hvwPnxgY zthA&a1NQ_g;zJ_N=7(2~cc$_Pkz{3$Kazx``{$Jr-D78;hceF52UY~Ub9z7p{cd#b z%v$U|!NSSI5NhL)Dzf=(!rxWcEW+Z_C`(Lxx8p2iIJy_(;M@J1{ktqA^!|i@oTnSWp?BlZv&h~Zh=f&c_IPPN|cYUAH?HxozX zEGZ9oz5&zwvaT!RnQ^OB1%>Sw`3!e;e7V5uOWbaJI8!||GyK+!dyGMZ54tfw?2+mmJ7XKCp76WQoRrcIy#_*P$8G`@uv_2>(x z=r^2|h@zNzLIS5KEE>AmuL#Y2xw@i#@uGARnPC5aoBiV-+Bf@-(@CE0?^41ZAN~gt zzps+6vBhdKo@ZqU9p+0~go>n4q?_5KH*k~444sbfdl%7kKhGT2>5uKCAbnsKd5z}r zf{Ll6s*XP2X!_Yi<*7FHtI0nc7PNP(F1I-dmw5TLAvUu>TJVa$2PxBzmzbAiasij- zkWZKSVG zDbJ$NCA#G`ohhml_6vO7T@BO(axi81h50~s*G-o$?}um`*Ja--x@{#qF-$cm4=61> zocP;mq$)033z8WWh7#~D@K2+A<~AV6W9?NyKu_f+0euJh;I3Lnz+UUHCSI34388AF z&_zM3)U1h=H0e7Ve%nA}l#L@20Z447`?WXCC)GG2L1O#r-*y_4UlBzw?KBV$sk|x& zN3=4TyfF}aZu@Z>*3kz}vuv0vMdNL=cV@GFHpi|7?kLv}?i~WRj$3pp-eC<6{cO_I zunxK?+P*EXM!1|RhuLk%44(bC6)CsckjOiDIxe$6SZaV2)J>s%RJHd-nxej*^p#y? zLZLtH*eC3>*dJe;>vDCrhqL@$0V$R16~Ui^qSVu)>tEk_INBhO1Vzn%3YuuWYn(`` zv@GoS14CKIP&>Q{szRF1YHCdW;CAoVezpeJ~g`2qYNurEQ*yD;Vb$F7i-NndO9%{HA zV19n-Q-H*aA{O94R8~!B9&tEo?a9$2mJbxS!t)ltOEv2mT^4|8xY7ZsdrsNlirFx& z;@I=}7xvWPCk~(GPlIZ{uOUmZ3LN=NB514OK}yQGuio4IWp_zY-9_PL^3r1Bnm-*{ z67Y{Th&XMMMv3^9A9uk=_wv&qdkqnr1g#hgtlt@8i&% zc$Keh&sW&3#L8e%$2*S~=M%Q{DHW*NzGnL*c+f!CZypt@aC5leNv>S8TQ&xKwwc;) zJWc#=h|qrgi<_DZ^xA1z?|r%CGbw@D&L*4;JvhL^dHQ?VwgaRbg~D6e`gL9wLczyW z)g%7a*Iu;Mxw2pZw|mQsUB>Grm&6j|OCA$`7oi;20Zzf^D0D3a6PGRMznT@>0~*e; zy!t-RW|&^i4~Hf(XxD!^dmTk|p-xi~^}duU#rV1?O+ph+(%UwbGu|D169sfwTB;9# z6!gs1P&Sr<(xyYcEq=n{%3|>u%~nHdTp>Ac?5IQ<8O-pox@v^~|0LZgomL@=AP0SWkd2KBp<2`}-%D|PAUUi4Lr|LBaT0!wl&GO4))CRgA0XP=s-T+1WuMp?w zx#z`BDGGn=t$jAco|Pz5zudPTQURP33s=BwH(ZQmC;Um9pS}DSj6Rr{nF|}n8tTQE z7L?MH#D}noSE*$35<~1K4Yn~mUInM}p3X6v;Rp~yc2%Kr%?faD4zsW1oW!hZG4$C^ zrKw_>ytGzGH6``n7X@U`66c!jjBza62aG7T`)JpBJNDt-T*8R6a~t9b1*O{ZG*0Ow zA<19Y0qW)3{!77<1#8RXWy`|C0NS*bKS@#edg;C$Z||W=zDfSSILgl6Q~J($43LTo zC$npX6|xQHI@zUCWr}uw(OXT9*z`2{4@9y9H;!k$!h@T>>_;DsrWVsY@EK z_i7MwxZw9vY%ZXSfNMcOxy5Sjc40;x`4xYj7o8ed3Bg6~FQ)0%#H-R-5CA>90gw5$ zZ-JmzMm0dm?KLrb&{XiZO6?R3E1xH4vOSb}<-47|o3U7S=Zx4&+a11$(^jvfo5aO4 z;GD(J44mxq#({&BOa_DX>DR2a#MiH}Y)D74w-oc??|1le18Bjjafqd{?0IF0_YL`h z>}EqopArO5H_#2PDp(Sah1TwDE1v6h;X6v(TqZBRk~*>4q;5CbzrT!*7I?Hq-ADgu z`3)sPDW#CaoiO%m!BYQlH3vmE$?4)~DK-$H0(1#@)a3bYBvHVxfe6zIsU>DK-d_0| zP@yXk6~Nc!kB+wEVacoc9Fdv=)z(%vn*doZWd_Tz#)m^sZb z1}0-;vYxwzjq=uh0KLb?!ON$A*ZH$ipPUTEq9OJf{yi!36?K)O^P*oAcGGzYtun}b za>wYW=gV~JaE-D=8+6ei+XK^e=-=wzOaM@iafCT=tscz0qjLc^7m>%E^USeAHIU--K7nEJEd z^X3wLD%lO&kcvgE`IQ+PuJhgOv<)8B|FNi>x9EQ?%32BtP_-W)em}*-h$w$yr(my9 z&tV2X|GRq$KO0SUt6+)ylgH3n00+*#_qH|v=PWE%>gK$(G}7GiWkm zv}k?V06)X8J6;2-v`1CKe|^+H6W(&ux9a+n+y!L*PwKiiyk5TjcdaG;=&s5S)5#F%xZ&=5~(7c3wOlW&a(el=oS8_+A4*pBQ zs}vt*kN_M=7~E@+t90r{G@iZ2^veUaxx^TM*q>qFd4z9CxZFwcx>_)wFcCQPyN4h} z8X&twPDd01(f&C3 zo+=`P_a~T+h*mv)qMf)8F)aFF06M6eeg&k`f96K7iw?6ZGD~g8JXk>QdFC7i_ugv)W5u!m*ouPr>GW346E$rZN@It}=7un>4nm8Q9W(> zAoIWqaQHOFZFJ}#9T?N{EWv^(rPZTqP(kWSvspjpmPNe`{+gyUI*C!l-Ke??aDWnq zEpYdgi(3>$#OVcLHQROFQ5HF-3>j2n?akwPRY7Z~A(CgKsICLoeXewn;;`Mge{?J< zr!BaZPP<$`>$^2LwEL`MG1hg@c)MySDUD*stQo4@8NDW^-NpLyq{+II3h!A)*x@7d z`BV2^K8n_Tu_!nl@X&UPszxSQ&Kc5u&vWk}pFYS)!Y%dA8p|&FN%>fA&2QxBxXEI~ zI<9>Ivy_jc!V)Vr5W_Orwi0=$-F+5SP5+6icJ(I)KqwEjmn)3u-SZtAKut+Yu~yzH G^8W$t>>vFA literal 0 HcmV?d00001 diff --git a/impeller/third_party/stb/stb/data/herringbone/template_rooms_and_corridors.png b/impeller/third_party/stb/stb/data/herringbone/template_rooms_and_corridors.png new file mode 100644 index 0000000000000000000000000000000000000000..c0467f3e92b00086f4d74ca4498ed4f1b6156d64 GIT binary patch literal 4736 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�Ea(pn}$By;{m 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 Y)$SCh)qml~ifuP#?FV;25@0Cg$+ng9R* literal 0 HcmV?d00001 diff --git a/impeller/third_party/stb/stb/data/herringbone/template_round_rooms_diagonal_corridors.png b/impeller/third_party/stb/stb/data/herringbone/template_round_rooms_diagonal_corridors.png new file mode 100644 index 0000000000000000000000000000000000000000..2073f988906f634b1c0f884470df9f10c8bac369 GIT binary patch literal 8172 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 bwErIfH*5$lY+ElB00000NkvXXu0mjfWy4L8 literal 0 HcmV?d00001 diff --git a/impeller/third_party/stb/stb/data/herringbone/template_simple_caves_2_wide.png b/impeller/third_party/stb/stb/data/herringbone/template_simple_caves_2_wide.png new file mode 100644 index 0000000000000000000000000000000000000000..321727146615e7644ced4a1c96157cae96cf76be GIT binary patch literal 15712 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 UDo4cI6&8SkjH+~%)Ti(N2Vn*JQ~&?~ literal 0 HcmV?d00001 diff --git a/impeller/third_party/stb/stb/data/herringbone/template_square_rooms_with_random_rects.png b/impeller/third_party/stb/stb/data/herringbone/template_square_rooms_with_random_rects.png new file mode 100644 index 0000000000000000000000000000000000000000..0d7e82e560c996d89533869f71aa66aed4e9dbf4 GIT binary patch literal 4772 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 1400 +#define _CRT_SECURE_NO_WARNINGS // suppress warnings about fopen() +#pragma warning(push) +#pragma warning(disable:4996) // suppress even more warnings about fopen() +#endif +#include +#endif // STBI_NO_STDIO + +#define STBI_VERSION 1 + +enum +{ + STBI_default = 0, // only used for req_comp + + STBI_grey = 1, + STBI_grey_alpha = 2, + STBI_rgb = 3, + STBI_rgb_alpha = 4 +}; + +typedef unsigned char stbi_uc; + +#ifdef __cplusplus +extern "C" { +#endif + +////////////////////////////////////////////////////////////////////////////// +// +// PRIMARY API - works on images of any type +// + +// +// load image by filename, open file, or memory buffer +// + +extern stbi_uc *stbi_load_from_memory(stbi_uc const *buffer, int len, int *x, int *y, int *comp, int req_comp); + +#ifndef STBI_NO_STDIO +extern stbi_uc *stbi_load (char const *filename, int *x, int *y, int *comp, int req_comp); +extern stbi_uc *stbi_load_from_file (FILE *f, int *x, int *y, int *comp, int req_comp); +// for stbi_load_from_file, file pointer is left pointing immediately after image +#endif + +typedef struct +{ + int (*read) (void *user,char *data,int size); // fill 'data' with 'size' bytes. return number of bytes actually read + void (*skip) (void *user,int n); // skip the next 'n' bytes, or 'unget' the last -n bytes if negative + int (*eof) (void *user); // returns nonzero if we are at end of file/data +} stbi_io_callbacks; + +extern stbi_uc *stbi_load_from_callbacks (stbi_io_callbacks const *clbk, void *user, int *x, int *y, int *comp, int req_comp); + +#ifndef STBI_NO_HDR + extern float *stbi_loadf_from_memory(stbi_uc const *buffer, int len, int *x, int *y, int *comp, int req_comp); + + #ifndef STBI_NO_STDIO + extern float *stbi_loadf (char const *filename, int *x, int *y, int *comp, int req_comp); + extern float *stbi_loadf_from_file (FILE *f, int *x, int *y, int *comp, int req_comp); + #endif + + extern float *stbi_loadf_from_callbacks (stbi_io_callbacks const *clbk, void *user, int *x, int *y, int *comp, int req_comp); + + extern void stbi_hdr_to_ldr_gamma(float gamma); + extern void stbi_hdr_to_ldr_scale(float scale); + + extern void stbi_ldr_to_hdr_gamma(float gamma); + extern void stbi_ldr_to_hdr_scale(float scale); +#endif // STBI_NO_HDR + +// stbi_is_hdr is always defined +extern int stbi_is_hdr_from_callbacks(stbi_io_callbacks const *clbk, void *user); +extern int stbi_is_hdr_from_memory(stbi_uc const *buffer, int len); +#ifndef STBI_NO_STDIO +extern int stbi_is_hdr (char const *filename); +extern int stbi_is_hdr_from_file(FILE *f); +#endif // STBI_NO_STDIO + + +// get a VERY brief reason for failure +// NOT THREADSAFE +extern const char *stbi_failure_reason (void); + +// free the loaded image -- this is just free() +extern void stbi_image_free (void *retval_from_stbi_load); + +// get image dimensions & components without fully decoding +extern int stbi_info_from_memory(stbi_uc const *buffer, int len, int *x, int *y, int *comp); +extern int stbi_info_from_callbacks(stbi_io_callbacks const *clbk, void *user, int *x, int *y, int *comp); + +#ifndef STBI_NO_STDIO +extern int stbi_info (char const *filename, int *x, int *y, int *comp); +extern int stbi_info_from_file (FILE *f, int *x, int *y, int *comp); + +#endif + + + +// for image formats that explicitly notate that they have premultiplied alpha, +// we just return the colors as stored in the file. set this flag to force +// unpremultiplication. results are undefined if the unpremultiply overflow. +extern void stbi_set_unpremultiply_on_load(int flag_true_if_should_unpremultiply); + +// indicate whether we should process iphone images back to canonical format, +// or just pass them through "as-is" +extern void stbi_convert_iphone_png_to_rgb(int flag_true_if_should_convert); + + +// ZLIB client - used by PNG, available for other purposes + +extern char *stbi_zlib_decode_malloc_guesssize(const char *buffer, int len, int initial_size, int *outlen); +extern char *stbi_zlib_decode_malloc_guesssize_headerflag(const char *buffer, int len, int initial_size, int *outlen, int parse_header); +extern char *stbi_zlib_decode_malloc(const char *buffer, int len, int *outlen); +extern int stbi_zlib_decode_buffer(char *obuffer, int olen, const char *ibuffer, int ilen); + +extern char *stbi_zlib_decode_noheader_malloc(const char *buffer, int len, int *outlen); +extern int stbi_zlib_decode_noheader_buffer(char *obuffer, int olen, const char *ibuffer, int ilen); + + +// define faster low-level operations (typically SIMD support) +#ifdef STBI_SIMD +typedef void (*stbi_idct_8x8)(stbi_uc *out, int out_stride, short data[64], unsigned short *dequantize); +// compute an integer IDCT on "input" +// input[x] = data[x] * dequantize[x] +// write results to 'out': 64 samples, each run of 8 spaced by 'out_stride' +// CLAMP results to 0..255 +typedef void (*stbi_YCbCr_to_RGB_run)(stbi_uc *output, stbi_uc const *y, stbi_uc const *cb, stbi_uc const *cr, int count, int step); +// compute a conversion from YCbCr to RGB +// 'count' pixels +// write pixels to 'output'; each pixel is 'step' bytes (either 3 or 4; if 4, write '255' as 4th), order R,G,B +// y: Y input channel +// cb: Cb input channel; scale/biased to be 0..255 +// cr: Cr input channel; scale/biased to be 0..255 + +extern void stbi_install_idct(stbi_idct_8x8 func); +extern void stbi_install_YCbCr_to_RGB(stbi_YCbCr_to_RGB_run func); +#endif // STBI_SIMD + + +#ifdef __cplusplus +} +#endif + +// +// +//// end header file ///////////////////////////////////////////////////// +#endif // STBI_INCLUDE_STB_IMAGE_H + +#ifndef STBI_HEADER_FILE_ONLY + +#ifndef STBI_NO_HDR +#include // ldexp +#include // strcmp, strtok +#endif + +#ifndef STBI_NO_STDIO +#include +#endif +#include +#include +#include +#include +#include // ptrdiff_t on osx + +#ifndef _MSC_VER + #ifdef __cplusplus + #define stbi_inline inline + #else + #define stbi_inline + #endif +#else + #define stbi_inline __forceinline +#endif + + +#ifdef _MSC_VER +typedef unsigned char stbi__uint8; +typedef unsigned short stbi__uint16; +typedef signed short stbi__int16; +typedef unsigned int stbi__uint32; +typedef signed int stbi__int32; +#else +#include +typedef uint8_t stbi__uint8; +typedef uint16_t stbi__uint16; +typedef int16_t stbi__int16; +typedef uint32_t stbi__uint32; +typedef int32_t stbi__int32; +#endif + +// should produce compiler error if size is wrong +typedef unsigned char validate_uint32[sizeof(stbi__uint32)==4 ? 1 : -1]; + +#ifdef _MSC_VER +#define STBI_NOTUSED(v) (void)(v) +#else +#define STBI_NOTUSED(v) (void)sizeof(v) +#endif + +#ifdef _MSC_VER +#define STBI_HAS_LROTL +#endif + +#ifdef STBI_HAS_LROTL + #define stbi_lrot(x,y) _lrotl(x,y) +#else + #define stbi_lrot(x,y) (((x) << (y)) | ((x) >> (32 - (y)))) +#endif + +/////////////////////////////////////////////// +// +// stbi struct and start_xxx functions + +// stbi structure is our basic context used by all images, so it +// contains all the IO context, plus some basic image information +typedef struct +{ + stbi__uint32 img_x, img_y; + int img_n, img_out_n; + + stbi_io_callbacks io; + void *io_user_data; + + int read_from_callbacks; + int buflen; + stbi__uint8 buffer_start[128]; + + stbi__uint8 *img_buffer, *img_buffer_end; + stbi__uint8 *img_buffer_original; +} stbi; + + +static void refill_buffer(stbi *s); + +// initialize a memory-decode context +static void start_mem(stbi *s, stbi__uint8 const *buffer, int len) +{ + s->io.read = NULL; + s->read_from_callbacks = 0; + s->img_buffer = s->img_buffer_original = (stbi__uint8 *) buffer; + s->img_buffer_end = (stbi__uint8 *) buffer+len; +} + +// initialize a callback-based context +static void start_callbacks(stbi *s, stbi_io_callbacks *c, void *user) +{ + s->io = *c; + s->io_user_data = user; + s->buflen = sizeof(s->buffer_start); + s->read_from_callbacks = 1; + s->img_buffer_original = s->buffer_start; + refill_buffer(s); +} + +#ifndef STBI_NO_STDIO + +static int stdio_read(void *user, char *data, int size) +{ + return (int) fread(data,1,size,(FILE*) user); +} + +static void stdio_skip(void *user, int n) +{ + fseek((FILE*) user, n, SEEK_CUR); +} + +static int stdio_eof(void *user) +{ + return feof((FILE*) user); +} + +static stbi_io_callbacks stbi_stdio_callbacks = +{ + stdio_read, + stdio_skip, + stdio_eof, +}; + +static void start_file(stbi *s, FILE *f) +{ + start_callbacks(s, &stbi_stdio_callbacks, (void *) f); +} + +//static void stop_file(stbi *s) { } + +#endif // !STBI_NO_STDIO + +static void stbi_rewind(stbi *s) +{ + // conceptually rewind SHOULD rewind to the beginning of the stream, + // but we just rewind to the beginning of the initial buffer, because + // we only use it after doing 'test', which only ever looks at at most 92 bytes + s->img_buffer = s->img_buffer_original; +} + +static int stbi_jpeg_test(stbi *s); +static stbi_uc *stbi_jpeg_load(stbi *s, int *x, int *y, int *comp, int req_comp); +static int stbi_jpeg_info(stbi *s, int *x, int *y, int *comp); +static int stbi_png_test(stbi *s); +static stbi_uc *stbi_png_load(stbi *s, int *x, int *y, int *comp, int req_comp); +static int stbi_png_info(stbi *s, int *x, int *y, int *comp); +static int stbi_bmp_test(stbi *s); +static stbi_uc *stbi_bmp_load(stbi *s, int *x, int *y, int *comp, int req_comp); +static int stbi_tga_test(stbi *s); +static stbi_uc *stbi_tga_load(stbi *s, int *x, int *y, int *comp, int req_comp); +static int stbi_tga_info(stbi *s, int *x, int *y, int *comp); +static int stbi_psd_test(stbi *s); +static stbi_uc *stbi_psd_load(stbi *s, int *x, int *y, int *comp, int req_comp); +#ifndef STBI_NO_HDR +static int stbi_hdr_test(stbi *s); +static float *stbi_hdr_load(stbi *s, int *x, int *y, int *comp, int req_comp); +#endif +static int stbi_pic_test(stbi *s); +static stbi_uc *stbi_pic_load(stbi *s, int *x, int *y, int *comp, int req_comp); +static int stbi_gif_test(stbi *s); +static stbi_uc *stbi_gif_load(stbi *s, int *x, int *y, int *comp, int req_comp); +static int stbi_gif_info(stbi *s, int *x, int *y, int *comp); + + +// this is not threadsafe +static const char *failure_reason; + +const char *stbi_failure_reason(void) +{ + return failure_reason; +} + +static int e(const char *str) +{ + failure_reason = str; + return 0; +} + +// e - error +// epf - error returning pointer to float +// epuc - error returning pointer to unsigned char + +#ifdef STBI_NO_FAILURE_STRINGS + #define e(x,y) 0 +#elif defined(STBI_FAILURE_USERMSG) + #define e(x,y) e(y) +#else + #define e(x,y) e(x) +#endif + +#define epf(x,y) ((float *) (e(x,y)?NULL:NULL)) +#define epuc(x,y) ((unsigned char *) (e(x,y)?NULL:NULL)) + +void stbi_image_free(void *retval_from_stbi_load) +{ + free(retval_from_stbi_load); +} + +#ifndef STBI_NO_HDR +static float *ldr_to_hdr(stbi_uc *data, int x, int y, int comp); +static stbi_uc *hdr_to_ldr(float *data, int x, int y, int comp); +#endif + +static unsigned char *stbi_load_main(stbi *s, int *x, int *y, int *comp, int req_comp) +{ + if (stbi_jpeg_test(s)) return stbi_jpeg_load(s,x,y,comp,req_comp); + if (stbi_png_test(s)) return stbi_png_load(s,x,y,comp,req_comp); + if (stbi_bmp_test(s)) return stbi_bmp_load(s,x,y,comp,req_comp); + if (stbi_gif_test(s)) return stbi_gif_load(s,x,y,comp,req_comp); + if (stbi_psd_test(s)) return stbi_psd_load(s,x,y,comp,req_comp); + if (stbi_pic_test(s)) return stbi_pic_load(s,x,y,comp,req_comp); + + #ifndef STBI_NO_HDR + if (stbi_hdr_test(s)) { + float *hdr = stbi_hdr_load(s, x,y,comp,req_comp); + return hdr_to_ldr(hdr, *x, *y, req_comp ? req_comp : *comp); + } + #endif + + // test tga last because it's a crappy test! + if (stbi_tga_test(s)) + return stbi_tga_load(s,x,y,comp,req_comp); + return epuc("unknown image type", "Image not of any known type, or corrupt"); +} + +#ifndef STBI_NO_STDIO +unsigned char *stbi_load(char const *filename, int *x, int *y, int *comp, int req_comp) +{ + FILE *f = fopen(filename, "rb"); + unsigned char *result; + if (!f) return epuc("can't fopen", "Unable to open file"); + result = stbi_load_from_file(f,x,y,comp,req_comp); + fclose(f); + return result; +} + +unsigned char *stbi_load_from_file(FILE *f, int *x, int *y, int *comp, int req_comp) +{ + unsigned char *result; + stbi s; + start_file(&s,f); + result = stbi_load_main(&s,x,y,comp,req_comp); + if (result) { + // need to 'unget' all the characters in the IO buffer + fseek(f, - (int) (s.img_buffer_end - s.img_buffer), SEEK_CUR); + } + return result; +} +#endif //!STBI_NO_STDIO + +unsigned char *stbi_load_from_memory(stbi_uc const *buffer, int len, int *x, int *y, int *comp, int req_comp) +{ + stbi s; + start_mem(&s,buffer,len); + return stbi_load_main(&s,x,y,comp,req_comp); +} + +unsigned char *stbi_load_from_callbacks(stbi_io_callbacks const *clbk, void *user, int *x, int *y, int *comp, int req_comp) +{ + stbi s; + start_callbacks(&s, (stbi_io_callbacks *) clbk, user); + return stbi_load_main(&s,x,y,comp,req_comp); +} + +#ifndef STBI_NO_HDR + +float *stbi_loadf_main(stbi *s, int *x, int *y, int *comp, int req_comp) +{ + unsigned char *data; + #ifndef STBI_NO_HDR + if (stbi_hdr_test(s)) + return stbi_hdr_load(s,x,y,comp,req_comp); + #endif + data = stbi_load_main(s, x, y, comp, req_comp); + if (data) + return ldr_to_hdr(data, *x, *y, req_comp ? req_comp : *comp); + return epf("unknown image type", "Image not of any known type, or corrupt"); +} + +float *stbi_loadf_from_memory(stbi_uc const *buffer, int len, int *x, int *y, int *comp, int req_comp) +{ + stbi s; + start_mem(&s,buffer,len); + return stbi_loadf_main(&s,x,y,comp,req_comp); +} + +float *stbi_loadf_from_callbacks(stbi_io_callbacks const *clbk, void *user, int *x, int *y, int *comp, int req_comp) +{ + stbi s; + start_callbacks(&s, (stbi_io_callbacks *) clbk, user); + return stbi_loadf_main(&s,x,y,comp,req_comp); +} + +#ifndef STBI_NO_STDIO +float *stbi_loadf(char const *filename, int *x, int *y, int *comp, int req_comp) +{ + FILE *f = fopen(filename, "rb"); + float *result; + if (!f) return epf("can't fopen", "Unable to open file"); + result = stbi_loadf_from_file(f,x,y,comp,req_comp); + fclose(f); + return result; +} + +float *stbi_loadf_from_file(FILE *f, int *x, int *y, int *comp, int req_comp) +{ + stbi s; + start_file(&s,f); + return stbi_loadf_main(&s,x,y,comp,req_comp); +} +#endif // !STBI_NO_STDIO + +#endif // !STBI_NO_HDR + +// these is-hdr-or-not is defined independent of whether STBI_NO_HDR is +// defined, for API simplicity; if STBI_NO_HDR is defined, it always +// reports false! + +int stbi_is_hdr_from_memory(stbi_uc const *buffer, int len) +{ + #ifndef STBI_NO_HDR + stbi s; + start_mem(&s,buffer,len); + return stbi_hdr_test(&s); + #else + STBI_NOTUSED(buffer); + STBI_NOTUSED(len); + return 0; + #endif +} + +#ifndef STBI_NO_STDIO +extern int stbi_is_hdr (char const *filename) +{ + FILE *f = fopen(filename, "rb"); + int result=0; + if (f) { + result = stbi_is_hdr_from_file(f); + fclose(f); + } + return result; +} + +extern int stbi_is_hdr_from_file(FILE *f) +{ + #ifndef STBI_NO_HDR + stbi s; + start_file(&s,f); + return stbi_hdr_test(&s); + #else + return 0; + #endif +} +#endif // !STBI_NO_STDIO + +extern int stbi_is_hdr_from_callbacks(stbi_io_callbacks const *clbk, void *user) +{ + #ifndef STBI_NO_HDR + stbi s; + start_callbacks(&s, (stbi_io_callbacks *) clbk, user); + return stbi_hdr_test(&s); + #else + return 0; + #endif +} + +#ifndef STBI_NO_HDR +static float h2l_gamma_i=1.0f/2.2f, h2l_scale_i=1.0f; +static float l2h_gamma=2.2f, l2h_scale=1.0f; + +void stbi_hdr_to_ldr_gamma(float gamma) { h2l_gamma_i = 1/gamma; } +void stbi_hdr_to_ldr_scale(float scale) { h2l_scale_i = 1/scale; } + +void stbi_ldr_to_hdr_gamma(float gamma) { l2h_gamma = gamma; } +void stbi_ldr_to_hdr_scale(float scale) { l2h_scale = scale; } +#endif + + +////////////////////////////////////////////////////////////////////////////// +// +// Common code used by all image loaders +// + +enum +{ + SCAN_load=0, + SCAN_type, + SCAN_header +}; + +static void refill_buffer(stbi *s) +{ + int n = (s->io.read)(s->io_user_data,(char*)s->buffer_start,s->buflen); + if (n == 0) { + // at end of file, treat same as if from memory, but need to handle case + // where s->img_buffer isn't pointing to safe memory, e.g. 0-byte file + s->read_from_callbacks = 0; + s->img_buffer = s->buffer_start; + s->img_buffer_end = s->buffer_start+1; + *s->img_buffer = 0; + } else { + s->img_buffer = s->buffer_start; + s->img_buffer_end = s->buffer_start + n; + } +} + +stbi_inline static int get8(stbi *s) +{ + if (s->img_buffer < s->img_buffer_end) + return *s->img_buffer++; + if (s->read_from_callbacks) { + refill_buffer(s); + return *s->img_buffer++; + } + return 0; +} + +stbi_inline static int at_eof(stbi *s) +{ + if (s->io.read) { + if (!(s->io.eof)(s->io_user_data)) return 0; + // if feof() is true, check if buffer = end + // special case: we've only got the special 0 character at the end + if (s->read_from_callbacks == 0) return 1; + } + + return s->img_buffer >= s->img_buffer_end; +} + +stbi_inline static stbi__uint8 get8u(stbi *s) +{ + return (stbi__uint8) get8(s); +} + +static void skip(stbi *s, int n) +{ + if (s->io.read) { + int blen = (int) (s->img_buffer_end - s->img_buffer); + if (blen < n) { + s->img_buffer = s->img_buffer_end; + (s->io.skip)(s->io_user_data, n - blen); + return; + } + } + s->img_buffer += n; +} + +static int getn(stbi *s, stbi_uc *buffer, int n) +{ + if (s->io.read) { + int blen = (int) (s->img_buffer_end - s->img_buffer); + if (blen < n) { + int res, count; + + memcpy(buffer, s->img_buffer, blen); + + count = (s->io.read)(s->io_user_data, (char*) buffer + blen, n - blen); + res = (count == (n-blen)); + s->img_buffer = s->img_buffer_end; + return res; + } + } + + if (s->img_buffer+n <= s->img_buffer_end) { + memcpy(buffer, s->img_buffer, n); + s->img_buffer += n; + return 1; + } else + return 0; +} + +static int get16(stbi *s) +{ + int z = get8(s); + return (z << 8) + get8(s); +} + +static stbi__uint32 get32(stbi *s) +{ + stbi__uint32 z = get16(s); + return (z << 16) + get16(s); +} + +static int get16le(stbi *s) +{ + int z = get8(s); + return z + (get8(s) << 8); +} + +static stbi__uint32 get32le(stbi *s) +{ + stbi__uint32 z = get16le(s); + return z + (get16le(s) << 16); +} + +////////////////////////////////////////////////////////////////////////////// +// +// generic converter from built-in img_n to req_comp +// individual types do this automatically as much as possible (e.g. jpeg +// does all cases internally since it needs to colorspace convert anyway, +// and it never has alpha, so very few cases ). png can automatically +// interleave an alpha=255 channel, but falls back to this for other cases +// +// assume data buffer is malloced, so malloc a new one and free that one +// only failure mode is malloc failing + +static stbi__uint8 compute_y(int r, int g, int b) +{ + return (stbi__uint8) (((r*77) + (g*150) + (29*b)) >> 8); +} + +static unsigned char *convert_format(unsigned char *data, int img_n, int req_comp, unsigned int x, unsigned int y) +{ + int i,j; + unsigned char *good; + + if (req_comp == img_n) return data; + assert(req_comp >= 1 && req_comp <= 4); + + good = (unsigned char *) malloc(req_comp * x * y); + if (good == NULL) { + free(data); + return epuc("outofmem", "Out of memory"); + } + + for (j=0; j < (int) y; ++j) { + unsigned char *src = data + j * x * img_n ; + unsigned char *dest = good + j * x * req_comp; + + #define COMBO(a,b) ((a)*8+(b)) + #define CASE(a,b) case COMBO(a,b): for(i=x-1; i >= 0; --i, src += a, dest += b) + // convert source image with img_n components to one with req_comp components; + // avoid switch per pixel, so use switch per scanline and massive macros + switch (COMBO(img_n, req_comp)) { + CASE(1,2) dest[0]=src[0], dest[1]=255; break; + CASE(1,3) dest[0]=dest[1]=dest[2]=src[0]; break; + CASE(1,4) dest[0]=dest[1]=dest[2]=src[0], dest[3]=255; break; + CASE(2,1) dest[0]=src[0]; break; + CASE(2,3) dest[0]=dest[1]=dest[2]=src[0]; break; + CASE(2,4) dest[0]=dest[1]=dest[2]=src[0], dest[3]=src[1]; break; + CASE(3,4) dest[0]=src[0],dest[1]=src[1],dest[2]=src[2],dest[3]=255; break; + CASE(3,1) dest[0]=compute_y(src[0],src[1],src[2]); break; + CASE(3,2) dest[0]=compute_y(src[0],src[1],src[2]), dest[1] = 255; break; + CASE(4,1) dest[0]=compute_y(src[0],src[1],src[2]); break; + CASE(4,2) dest[0]=compute_y(src[0],src[1],src[2]), dest[1] = src[3]; break; + CASE(4,3) dest[0]=src[0],dest[1]=src[1],dest[2]=src[2]; break; + default: assert(0); + } + #undef CASE + } + + free(data); + return good; +} + +#ifndef STBI_NO_HDR +static float *ldr_to_hdr(stbi_uc *data, int x, int y, int comp) +{ + int i,k,n; + float *output = (float *) malloc(x * y * comp * sizeof(float)); + if (output == NULL) { free(data); return epf("outofmem", "Out of memory"); } + // compute number of non-alpha components + if (comp & 1) n = comp; else n = comp-1; + for (i=0; i < x*y; ++i) { + for (k=0; k < n; ++k) { + output[i*comp + k] = (float) pow(data[i*comp+k]/255.0f, l2h_gamma) * l2h_scale; + } + if (k < comp) output[i*comp + k] = data[i*comp+k]/255.0f; + } + free(data); + return output; +} + +#define float2int(x) ((int) (x)) +static stbi_uc *hdr_to_ldr(float *data, int x, int y, int comp) +{ + int i,k,n; + stbi_uc *output = (stbi_uc *) malloc(x * y * comp); + if (output == NULL) { free(data); return epuc("outofmem", "Out of memory"); } + // compute number of non-alpha components + if (comp & 1) n = comp; else n = comp-1; + for (i=0; i < x*y; ++i) { + for (k=0; k < n; ++k) { + float z = (float) pow(data[i*comp+k]*h2l_scale_i, h2l_gamma_i) * 255 + 0.5f; + if (z < 0) z = 0; + if (z > 255) z = 255; + output[i*comp + k] = (stbi__uint8) float2int(z); + } + if (k < comp) { + float z = data[i*comp+k] * 255 + 0.5f; + if (z < 0) z = 0; + if (z > 255) z = 255; + output[i*comp + k] = (stbi__uint8) float2int(z); + } + } + free(data); + return output; +} +#endif + +////////////////////////////////////////////////////////////////////////////// +// +// "baseline" JPEG/JFIF decoder (not actually fully baseline implementation) +// +// simple implementation +// - channel subsampling of at most 2 in each dimension +// - doesn't support delayed output of y-dimension +// - simple interface (only one output format: 8-bit interleaved RGB) +// - doesn't try to recover corrupt jpegs +// - doesn't allow partial loading, loading multiple at once +// - still fast on x86 (copying globals into locals doesn't help x86) +// - allocates lots of intermediate memory (full size of all components) +// - non-interleaved case requires this anyway +// - allows good upsampling (see next) +// high-quality +// - upsampled channels are bilinearly interpolated, even across blocks +// - quality integer IDCT derived from IJG's 'slow' +// performance +// - fast huffman; reasonable integer IDCT +// - uses a lot of intermediate memory, could cache poorly +// - load http://nothings.org/remote/anemones.jpg 3 times on 2.8Ghz P4 +// stb_jpeg: 1.34 seconds (MSVC6, default release build) +// stb_jpeg: 1.06 seconds (MSVC6, processor = Pentium Pro) +// IJL11.dll: 1.08 seconds (compiled by intel) +// IJG 1998: 0.98 seconds (MSVC6, makefile provided by IJG) +// IJG 1998: 0.95 seconds (MSVC6, makefile + proc=PPro) + +// huffman decoding acceleration +#define FAST_BITS 9 // larger handles more cases; smaller stomps less cache + +typedef struct +{ + stbi__uint8 fast[1 << FAST_BITS]; + // weirdly, repacking this into AoS is a 10% speed loss, instead of a win + stbi__uint16 code[256]; + stbi__uint8 values[256]; + stbi__uint8 size[257]; + unsigned int maxcode[18]; + int delta[17]; // old 'firstsymbol' - old 'firstcode' +} huffman; + +typedef struct +{ + #ifdef STBI_SIMD + unsigned short dequant2[4][64]; + #endif + stbi *s; + huffman huff_dc[4]; + huffman huff_ac[4]; + stbi__uint8 dequant[4][64]; + +// sizes for components, interleaved MCUs + int img_h_max, img_v_max; + int img_mcu_x, img_mcu_y; + int img_mcu_w, img_mcu_h; + +// definition of jpeg image component + struct + { + int id; + int h,v; + int tq; + int hd,ha; + int dc_pred; + + int x,y,w2,h2; + stbi__uint8 *data; + void *raw_data; + stbi__uint8 *linebuf; + } img_comp[4]; + + stbi__uint32 code_buffer; // jpeg entropy-coded buffer + int code_bits; // number of valid bits + unsigned char marker; // marker seen while filling entropy buffer + int nomore; // flag if we saw a marker so must stop + + int scan_n, order[4]; + int restart_interval, todo; +} jpeg; + +static int build_huffman(huffman *h, int *count) +{ + int i,j,k=0,code; + // build size list for each symbol (from JPEG spec) + for (i=0; i < 16; ++i) + for (j=0; j < count[i]; ++j) + h->size[k++] = (stbi__uint8) (i+1); + h->size[k] = 0; + + // compute actual symbols (from jpeg spec) + code = 0; + k = 0; + for(j=1; j <= 16; ++j) { + // compute delta to add to code to compute symbol id + h->delta[j] = k - code; + if (h->size[k] == j) { + while (h->size[k] == j) + h->code[k++] = (stbi__uint16) (code++); + if (code-1 >= (1 << j)) return e("bad code lengths","Corrupt JPEG"); + } + // compute largest code + 1 for this size, preshifted as needed later + h->maxcode[j] = code << (16-j); + code <<= 1; + } + h->maxcode[j] = 0xffffffff; + + // build non-spec acceleration table; 255 is flag for not-accelerated + memset(h->fast, 255, 1 << FAST_BITS); + for (i=0; i < k; ++i) { + int s = h->size[i]; + if (s <= FAST_BITS) { + int c = h->code[i] << (FAST_BITS-s); + int m = 1 << (FAST_BITS-s); + for (j=0; j < m; ++j) { + h->fast[c+j] = (stbi__uint8) i; + } + } + } + return 1; +} + +static void grow_buffer_unsafe(jpeg *j) +{ + do { + int b = j->nomore ? 0 : get8(j->s); + if (b == 0xff) { + int c = get8(j->s); + if (c != 0) { + j->marker = (unsigned char) c; + j->nomore = 1; + return; + } + } + j->code_buffer |= b << (24 - j->code_bits); + j->code_bits += 8; + } while (j->code_bits <= 24); +} + +// (1 << n) - 1 +static stbi__uint32 bmask[17]={0,1,3,7,15,31,63,127,255,511,1023,2047,4095,8191,16383,32767,65535}; + +// decode a jpeg huffman value from the bitstream +stbi_inline static int decode(jpeg *j, huffman *h) +{ + unsigned int temp; + int c,k; + + if (j->code_bits < 16) grow_buffer_unsafe(j); + + // look at the top FAST_BITS and determine what symbol ID it is, + // if the code is <= FAST_BITS + c = (j->code_buffer >> (32 - FAST_BITS)) & ((1 << FAST_BITS)-1); + k = h->fast[c]; + if (k < 255) { + int s = h->size[k]; + if (s > j->code_bits) + return -1; + j->code_buffer <<= s; + j->code_bits -= s; + return h->values[k]; + } + + // naive test is to shift the code_buffer down so k bits are + // valid, then test against maxcode. To speed this up, we've + // preshifted maxcode left so that it has (16-k) 0s at the + // end; in other words, regardless of the number of bits, it + // wants to be compared against something shifted to have 16; + // that way we don't need to shift inside the loop. + temp = j->code_buffer >> 16; + for (k=FAST_BITS+1 ; ; ++k) + if (temp < h->maxcode[k]) + break; + if (k == 17) { + // error! code not found + j->code_bits -= 16; + return -1; + } + + if (k > j->code_bits) + return -1; + + // convert the huffman code to the symbol id + c = ((j->code_buffer >> (32 - k)) & bmask[k]) + h->delta[k]; + assert((((j->code_buffer) >> (32 - h->size[c])) & bmask[h->size[c]]) == h->code[c]); + + // convert the id to a symbol + j->code_bits -= k; + j->code_buffer <<= k; + return h->values[c]; +} + +// combined JPEG 'receive' and JPEG 'extend', since baseline +// always extends everything it receives. +stbi_inline static int extend_receive(jpeg *j, int n) +{ + unsigned int m = 1 << (n-1); + unsigned int k; + if (j->code_bits < n) grow_buffer_unsafe(j); + + #if 1 + k = stbi_lrot(j->code_buffer, n); + j->code_buffer = k & ~bmask[n]; + k &= bmask[n]; + j->code_bits -= n; + #else + k = (j->code_buffer >> (32 - n)) & bmask[n]; + j->code_bits -= n; + j->code_buffer <<= n; + #endif + // the following test is probably a random branch that won't + // predict well. I tried to table accelerate it but failed. + // maybe it's compiling as a conditional move? + if (k < m) + return (-1 << n) + k + 1; + else + return k; +} + +// given a value that's at position X in the zigzag stream, +// where does it appear in the 8x8 matrix coded as row-major? +static stbi__uint8 dezigzag[64+15] = +{ + 0, 1, 8, 16, 9, 2, 3, 10, + 17, 24, 32, 25, 18, 11, 4, 5, + 12, 19, 26, 33, 40, 48, 41, 34, + 27, 20, 13, 6, 7, 14, 21, 28, + 35, 42, 49, 56, 57, 50, 43, 36, + 29, 22, 15, 23, 30, 37, 44, 51, + 58, 59, 52, 45, 38, 31, 39, 46, + 53, 60, 61, 54, 47, 55, 62, 63, + // let corrupt input sample past end + 63, 63, 63, 63, 63, 63, 63, 63, + 63, 63, 63, 63, 63, 63, 63 +}; + +// decode one 64-entry block-- +static int decode_block(jpeg *j, short data[64], huffman *hdc, huffman *hac, int b) +{ + int diff,dc,k; + int t = decode(j, hdc); + if (t < 0) return e("bad huffman code","Corrupt JPEG"); + + // 0 all the ac values now so we can do it 32-bits at a time + memset(data,0,64*sizeof(data[0])); + + diff = t ? extend_receive(j, t) : 0; + dc = j->img_comp[b].dc_pred + diff; + j->img_comp[b].dc_pred = dc; + data[0] = (short) dc; + + // decode AC components, see JPEG spec + k = 1; + do { + int r,s; + int rs = decode(j, hac); + if (rs < 0) return e("bad huffman code","Corrupt JPEG"); + s = rs & 15; + r = rs >> 4; + if (s == 0) { + if (rs != 0xf0) break; // end block + k += 16; + } else { + k += r; + // decode into unzigzag'd location + data[dezigzag[k++]] = (short) extend_receive(j,s); + } + } while (k < 64); + return 1; +} + +// take a -128..127 value and clamp it and convert to 0..255 +stbi_inline static stbi__uint8 clamp(int x) +{ + // trick to use a single test to catch both cases + if ((unsigned int) x > 255) { + if (x < 0) return 0; + if (x > 255) return 255; + } + return (stbi__uint8) x; +} + +#define f2f(x) (int) (((x) * 4096 + 0.5)) +#define fsh(x) ((x) << 12) + +// derived from jidctint -- DCT_ISLOW +#define IDCT_1D(s0,s1,s2,s3,s4,s5,s6,s7) \ + int t0,t1,t2,t3,p1,p2,p3,p4,p5,x0,x1,x2,x3; \ + p2 = s2; \ + p3 = s6; \ + p1 = (p2+p3) * f2f(0.5411961f); \ + t2 = p1 + p3*f2f(-1.847759065f); \ + t3 = p1 + p2*f2f( 0.765366865f); \ + p2 = s0; \ + p3 = s4; \ + t0 = fsh(p2+p3); \ + t1 = fsh(p2-p3); \ + x0 = t0+t3; \ + x3 = t0-t3; \ + x1 = t1+t2; \ + x2 = t1-t2; \ + t0 = s7; \ + t1 = s5; \ + t2 = s3; \ + t3 = s1; \ + p3 = t0+t2; \ + p4 = t1+t3; \ + p1 = t0+t3; \ + p2 = t1+t2; \ + p5 = (p3+p4)*f2f( 1.175875602f); \ + t0 = t0*f2f( 0.298631336f); \ + t1 = t1*f2f( 2.053119869f); \ + t2 = t2*f2f( 3.072711026f); \ + t3 = t3*f2f( 1.501321110f); \ + p1 = p5 + p1*f2f(-0.899976223f); \ + p2 = p5 + p2*f2f(-2.562915447f); \ + p3 = p3*f2f(-1.961570560f); \ + p4 = p4*f2f(-0.390180644f); \ + t3 += p1+p4; \ + t2 += p2+p3; \ + t1 += p2+p4; \ + t0 += p1+p3; + +#ifdef STBI_SIMD +typedef unsigned short stbi_dequantize_t; +#else +typedef stbi__uint8 stbi_dequantize_t; +#endif + +// .344 seconds on 3*anemones.jpg +static void idct_block(stbi__uint8 *out, int out_stride, short data[64], stbi_dequantize_t *dequantize) +{ + int i,val[64],*v=val; + stbi_dequantize_t *dq = dequantize; + stbi__uint8 *o; + short *d = data; + + // columns + for (i=0; i < 8; ++i,++d,++dq, ++v) { + // if all zeroes, shortcut -- this avoids dequantizing 0s and IDCTing + if (d[ 8]==0 && d[16]==0 && d[24]==0 && d[32]==0 + && d[40]==0 && d[48]==0 && d[56]==0) { + // no shortcut 0 seconds + // (1|2|3|4|5|6|7)==0 0 seconds + // all separate -0.047 seconds + // 1 && 2|3 && 4|5 && 6|7: -0.047 seconds + int dcterm = d[0] * dq[0] << 2; + v[0] = v[8] = v[16] = v[24] = v[32] = v[40] = v[48] = v[56] = dcterm; + } else { + IDCT_1D(d[ 0]*dq[ 0],d[ 8]*dq[ 8],d[16]*dq[16],d[24]*dq[24], + d[32]*dq[32],d[40]*dq[40],d[48]*dq[48],d[56]*dq[56]) + // constants scaled things up by 1<<12; let's bring them back + // down, but keep 2 extra bits of precision + x0 += 512; x1 += 512; x2 += 512; x3 += 512; + v[ 0] = (x0+t3) >> 10; + v[56] = (x0-t3) >> 10; + v[ 8] = (x1+t2) >> 10; + v[48] = (x1-t2) >> 10; + v[16] = (x2+t1) >> 10; + v[40] = (x2-t1) >> 10; + v[24] = (x3+t0) >> 10; + v[32] = (x3-t0) >> 10; + } + } + + for (i=0, v=val, o=out; i < 8; ++i,v+=8,o+=out_stride) { + // no fast case since the first 1D IDCT spread components out + IDCT_1D(v[0],v[1],v[2],v[3],v[4],v[5],v[6],v[7]) + // constants scaled things up by 1<<12, plus we had 1<<2 from first + // loop, plus horizontal and vertical each scale by sqrt(8) so together + // we've got an extra 1<<3, so 1<<17 total we need to remove. + // so we want to round that, which means adding 0.5 * 1<<17, + // aka 65536. Also, we'll end up with -128 to 127 that we want + // to encode as 0..255 by adding 128, so we'll add that before the shift + x0 += 65536 + (128<<17); + x1 += 65536 + (128<<17); + x2 += 65536 + (128<<17); + x3 += 65536 + (128<<17); + // tried computing the shifts into temps, or'ing the temps to see + // if any were out of range, but that was slower + o[0] = clamp((x0+t3) >> 17); + o[7] = clamp((x0-t3) >> 17); + o[1] = clamp((x1+t2) >> 17); + o[6] = clamp((x1-t2) >> 17); + o[2] = clamp((x2+t1) >> 17); + o[5] = clamp((x2-t1) >> 17); + o[3] = clamp((x3+t0) >> 17); + o[4] = clamp((x3-t0) >> 17); + } +} + +#ifdef STBI_SIMD +static stbi_idct_8x8 stbi_idct_installed = idct_block; + +void stbi_install_idct(stbi_idct_8x8 func) +{ + stbi_idct_installed = func; +} +#endif + +#define MARKER_none 0xff +// if there's a pending marker from the entropy stream, return that +// otherwise, fetch from the stream and get a marker. if there's no +// marker, return 0xff, which is never a valid marker value +static stbi__uint8 get_marker(jpeg *j) +{ + stbi__uint8 x; + if (j->marker != MARKER_none) { x = j->marker; j->marker = MARKER_none; return x; } + x = get8u(j->s); + if (x != 0xff) return MARKER_none; + while (x == 0xff) + x = get8u(j->s); + return x; +} + +// in each scan, we'll have scan_n components, and the order +// of the components is specified by order[] +#define RESTART(x) ((x) >= 0xd0 && (x) <= 0xd7) + +// after a restart interval, reset the entropy decoder and +// the dc prediction +static void reset(jpeg *j) +{ + j->code_bits = 0; + j->code_buffer = 0; + j->nomore = 0; + j->img_comp[0].dc_pred = j->img_comp[1].dc_pred = j->img_comp[2].dc_pred = 0; + j->marker = MARKER_none; + j->todo = j->restart_interval ? j->restart_interval : 0x7fffffff; + // no more than 1<<31 MCUs if no restart_interal? that's plenty safe, + // since we don't even allow 1<<30 pixels +} + +static int parse_entropy_coded_data(jpeg *z) +{ + reset(z); + if (z->scan_n == 1) { + int i,j; + #ifdef STBI_SIMD + __declspec(align(16)) + #endif + short data[64]; + int n = z->order[0]; + // non-interleaved data, we just need to process one block at a time, + // in trivial scanline order + // number of blocks to do just depends on how many actual "pixels" this + // component has, independent of interleaved MCU blocking and such + int w = (z->img_comp[n].x+7) >> 3; + int h = (z->img_comp[n].y+7) >> 3; + for (j=0; j < h; ++j) { + for (i=0; i < w; ++i) { + if (!decode_block(z, data, z->huff_dc+z->img_comp[n].hd, z->huff_ac+z->img_comp[n].ha, n)) return 0; + #ifdef STBI_SIMD + stbi_idct_installed(z->img_comp[n].data+z->img_comp[n].w2*j*8+i*8, z->img_comp[n].w2, data, z->dequant2[z->img_comp[n].tq]); + #else + idct_block(z->img_comp[n].data+z->img_comp[n].w2*j*8+i*8, z->img_comp[n].w2, data, z->dequant[z->img_comp[n].tq]); + #endif + // every data block is an MCU, so countdown the restart interval + if (--z->todo <= 0) { + if (z->code_bits < 24) grow_buffer_unsafe(z); + // if it's NOT a restart, then just bail, so we get corrupt data + // rather than no data + if (!RESTART(z->marker)) return 1; + reset(z); + } + } + } + } else { // interleaved! + int i,j,k,x,y; + short data[64]; + for (j=0; j < z->img_mcu_y; ++j) { + for (i=0; i < z->img_mcu_x; ++i) { + // scan an interleaved mcu... process scan_n components in order + for (k=0; k < z->scan_n; ++k) { + int n = z->order[k]; + // scan out an mcu's worth of this component; that's just determined + // by the basic H and V specified for the component + for (y=0; y < z->img_comp[n].v; ++y) { + for (x=0; x < z->img_comp[n].h; ++x) { + int x2 = (i*z->img_comp[n].h + x)*8; + int y2 = (j*z->img_comp[n].v + y)*8; + if (!decode_block(z, data, z->huff_dc+z->img_comp[n].hd, z->huff_ac+z->img_comp[n].ha, n)) return 0; + #ifdef STBI_SIMD + stbi_idct_installed(z->img_comp[n].data+z->img_comp[n].w2*y2+x2, z->img_comp[n].w2, data, z->dequant2[z->img_comp[n].tq]); + #else + idct_block(z->img_comp[n].data+z->img_comp[n].w2*y2+x2, z->img_comp[n].w2, data, z->dequant[z->img_comp[n].tq]); + #endif + } + } + } + // after all interleaved components, that's an interleaved MCU, + // so now count down the restart interval + if (--z->todo <= 0) { + if (z->code_bits < 24) grow_buffer_unsafe(z); + // if it's NOT a restart, then just bail, so we get corrupt data + // rather than no data + if (!RESTART(z->marker)) return 1; + reset(z); + } + } + } + } + return 1; +} + +static int process_marker(jpeg *z, int m) +{ + int L; + switch (m) { + case MARKER_none: // no marker found + return e("expected marker","Corrupt JPEG"); + + case 0xC2: // SOF - progressive + return e("progressive jpeg","JPEG format not supported (progressive)"); + + case 0xDD: // DRI - specify restart interval + if (get16(z->s) != 4) return e("bad DRI len","Corrupt JPEG"); + z->restart_interval = get16(z->s); + return 1; + + case 0xDB: // DQT - define quantization table + L = get16(z->s)-2; + while (L > 0) { + int q = get8(z->s); + int p = q >> 4; + int t = q & 15,i; + if (p != 0) return e("bad DQT type","Corrupt JPEG"); + if (t > 3) return e("bad DQT table","Corrupt JPEG"); + for (i=0; i < 64; ++i) + z->dequant[t][dezigzag[i]] = get8u(z->s); + #ifdef STBI_SIMD + for (i=0; i < 64; ++i) + z->dequant2[t][i] = z->dequant[t][i]; + #endif + L -= 65; + } + return L==0; + + case 0xC4: // DHT - define huffman table + L = get16(z->s)-2; + while (L > 0) { + stbi__uint8 *v; + int sizes[16],i,n=0; + int q = get8(z->s); + int tc = q >> 4; + int th = q & 15; + if (tc > 1 || th > 3) return e("bad DHT header","Corrupt JPEG"); + for (i=0; i < 16; ++i) { + sizes[i] = get8(z->s); + n += sizes[i]; + } + L -= 17; + if (tc == 0) { + if (!build_huffman(z->huff_dc+th, sizes)) return 0; + v = z->huff_dc[th].values; + } else { + if (!build_huffman(z->huff_ac+th, sizes)) return 0; + v = z->huff_ac[th].values; + } + for (i=0; i < n; ++i) + v[i] = get8u(z->s); + L -= n; + } + return L==0; + } + // check for comment block or APP blocks + if ((m >= 0xE0 && m <= 0xEF) || m == 0xFE) { + skip(z->s, get16(z->s)-2); + return 1; + } + return 0; +} + +// after we see SOS +static int process_scan_header(jpeg *z) +{ + int i; + int Ls = get16(z->s); + z->scan_n = get8(z->s); + if (z->scan_n < 1 || z->scan_n > 4 || z->scan_n > (int) z->s->img_n) return e("bad SOS component count","Corrupt JPEG"); + if (Ls != 6+2*z->scan_n) return e("bad SOS len","Corrupt JPEG"); + for (i=0; i < z->scan_n; ++i) { + int id = get8(z->s), which; + int q = get8(z->s); + for (which = 0; which < z->s->img_n; ++which) + if (z->img_comp[which].id == id) + break; + if (which == z->s->img_n) return 0; + z->img_comp[which].hd = q >> 4; if (z->img_comp[which].hd > 3) return e("bad DC huff","Corrupt JPEG"); + z->img_comp[which].ha = q & 15; if (z->img_comp[which].ha > 3) return e("bad AC huff","Corrupt JPEG"); + z->order[i] = which; + } + if (get8(z->s) != 0) return e("bad SOS","Corrupt JPEG"); + get8(z->s); // should be 63, but might be 0 + if (get8(z->s) != 0) return e("bad SOS","Corrupt JPEG"); + + return 1; +} + +static int process_frame_header(jpeg *z, int scan) +{ + stbi *s = z->s; + int Lf,p,i,q, h_max=1,v_max=1,c; + Lf = get16(s); if (Lf < 11) return e("bad SOF len","Corrupt JPEG"); // JPEG + p = get8(s); if (p != 8) return e("only 8-bit","JPEG format not supported: 8-bit only"); // JPEG baseline + s->img_y = get16(s); if (s->img_y == 0) return e("no header height", "JPEG format not supported: delayed height"); // Legal, but we don't handle it--but neither does IJG + s->img_x = get16(s); if (s->img_x == 0) return e("0 width","Corrupt JPEG"); // JPEG requires + c = get8(s); + if (c != 3 && c != 1) return e("bad component count","Corrupt JPEG"); // JFIF requires + s->img_n = c; + for (i=0; i < c; ++i) { + z->img_comp[i].data = NULL; + z->img_comp[i].linebuf = NULL; + } + + if (Lf != 8+3*s->img_n) return e("bad SOF len","Corrupt JPEG"); + + for (i=0; i < s->img_n; ++i) { + z->img_comp[i].id = get8(s); + if (z->img_comp[i].id != i+1) // JFIF requires + if (z->img_comp[i].id != i) // some version of jpegtran outputs non-JFIF-compliant files! + return e("bad component ID","Corrupt JPEG"); + q = get8(s); + z->img_comp[i].h = (q >> 4); if (!z->img_comp[i].h || z->img_comp[i].h > 4) return e("bad H","Corrupt JPEG"); + z->img_comp[i].v = q & 15; if (!z->img_comp[i].v || z->img_comp[i].v > 4) return e("bad V","Corrupt JPEG"); + z->img_comp[i].tq = get8(s); if (z->img_comp[i].tq > 3) return e("bad TQ","Corrupt JPEG"); + } + + if (scan != SCAN_load) return 1; + + if ((1 << 30) / s->img_x / s->img_n < s->img_y) return e("too large", "Image too large to decode"); + + for (i=0; i < s->img_n; ++i) { + if (z->img_comp[i].h > h_max) h_max = z->img_comp[i].h; + if (z->img_comp[i].v > v_max) v_max = z->img_comp[i].v; + } + + // compute interleaved mcu info + z->img_h_max = h_max; + z->img_v_max = v_max; + z->img_mcu_w = h_max * 8; + z->img_mcu_h = v_max * 8; + z->img_mcu_x = (s->img_x + z->img_mcu_w-1) / z->img_mcu_w; + z->img_mcu_y = (s->img_y + z->img_mcu_h-1) / z->img_mcu_h; + + for (i=0; i < s->img_n; ++i) { + // number of effective pixels (e.g. for non-interleaved MCU) + z->img_comp[i].x = (s->img_x * z->img_comp[i].h + h_max-1) / h_max; + z->img_comp[i].y = (s->img_y * z->img_comp[i].v + v_max-1) / v_max; + // to simplify generation, we'll allocate enough memory to decode + // the bogus oversized data from using interleaved MCUs and their + // big blocks (e.g. a 16x16 iMCU on an image of width 33); we won't + // discard the extra data until colorspace conversion + z->img_comp[i].w2 = z->img_mcu_x * z->img_comp[i].h * 8; + z->img_comp[i].h2 = z->img_mcu_y * z->img_comp[i].v * 8; + z->img_comp[i].raw_data = malloc(z->img_comp[i].w2 * z->img_comp[i].h2+15); + if (z->img_comp[i].raw_data == NULL) { + for(--i; i >= 0; --i) { + free(z->img_comp[i].raw_data); + z->img_comp[i].data = NULL; + } + return e("outofmem", "Out of memory"); + } + // align blocks for installable-idct using mmx/sse + z->img_comp[i].data = (stbi__uint8*) (((size_t) z->img_comp[i].raw_data + 15) & ~15); + z->img_comp[i].linebuf = NULL; + } + + return 1; +} + +// use comparisons since in some cases we handle more than one case (e.g. SOF) +#define DNL(x) ((x) == 0xdc) +#define SOI(x) ((x) == 0xd8) +#define EOI(x) ((x) == 0xd9) +#define SOF(x) ((x) == 0xc0 || (x) == 0xc1) +#define SOS(x) ((x) == 0xda) + +static int decode_jpeg_header(jpeg *z, int scan) +{ + int m; + z->marker = MARKER_none; // initialize cached marker to empty + m = get_marker(z); + if (!SOI(m)) return e("no SOI","Corrupt JPEG"); + if (scan == SCAN_type) return 1; + m = get_marker(z); + while (!SOF(m)) { + if (!process_marker(z,m)) return 0; + m = get_marker(z); + while (m == MARKER_none) { + // some files have extra padding after their blocks, so ok, we'll scan + if (at_eof(z->s)) return e("no SOF", "Corrupt JPEG"); + m = get_marker(z); + } + } + if (!process_frame_header(z, scan)) return 0; + return 1; +} + +static int decode_jpeg_image(jpeg *j) +{ + int m; + j->restart_interval = 0; + if (!decode_jpeg_header(j, SCAN_load)) return 0; + m = get_marker(j); + while (!EOI(m)) { + if (SOS(m)) { + if (!process_scan_header(j)) return 0; + if (!parse_entropy_coded_data(j)) return 0; + if (j->marker == MARKER_none ) { + // handle 0s at the end of image data from IP Kamera 9060 + while (!at_eof(j->s)) { + int x = get8(j->s); + if (x == 255) { + j->marker = get8u(j->s); + break; + } else if (x != 0) { + return 0; + } + } + // if we reach eof without hitting a marker, get_marker() below will fail and we'll eventually return 0 + } + } else { + if (!process_marker(j, m)) return 0; + } + m = get_marker(j); + } + return 1; +} + +// static jfif-centered resampling (across block boundaries) + +typedef stbi__uint8 *(*resample_row_func)(stbi__uint8 *out, stbi__uint8 *in0, stbi__uint8 *in1, + int w, int hs); + +#define div4(x) ((stbi__uint8) ((x) >> 2)) + +static stbi__uint8 *resample_row_1(stbi__uint8 *out, stbi__uint8 *in_near, stbi__uint8 *in_far, int w, int hs) +{ + STBI_NOTUSED(out); + STBI_NOTUSED(in_far); + STBI_NOTUSED(w); + STBI_NOTUSED(hs); + return in_near; +} + +static stbi__uint8* resample_row_v_2(stbi__uint8 *out, stbi__uint8 *in_near, stbi__uint8 *in_far, int w, int hs) +{ + // need to generate two samples vertically for every one in input + int i; + STBI_NOTUSED(hs); + for (i=0; i < w; ++i) + out[i] = div4(3*in_near[i] + in_far[i] + 2); + return out; +} + +static stbi__uint8* resample_row_h_2(stbi__uint8 *out, stbi__uint8 *in_near, stbi__uint8 *in_far, int w, int hs) +{ + // need to generate two samples horizontally for every one in input + int i; + stbi__uint8 *input = in_near; + + if (w == 1) { + // if only one sample, can't do any interpolation + out[0] = out[1] = input[0]; + return out; + } + + out[0] = input[0]; + out[1] = div4(input[0]*3 + input[1] + 2); + for (i=1; i < w-1; ++i) { + int n = 3*input[i]+2; + out[i*2+0] = div4(n+input[i-1]); + out[i*2+1] = div4(n+input[i+1]); + } + out[i*2+0] = div4(input[w-2]*3 + input[w-1] + 2); + out[i*2+1] = input[w-1]; + + STBI_NOTUSED(in_far); + STBI_NOTUSED(hs); + + return out; +} + +#define div16(x) ((stbi__uint8) ((x) >> 4)) + +static stbi__uint8 *resample_row_hv_2(stbi__uint8 *out, stbi__uint8 *in_near, stbi__uint8 *in_far, int w, int hs) +{ + // need to generate 2x2 samples for every one in input + int i,t0,t1; + if (w == 1) { + out[0] = out[1] = div4(3*in_near[0] + in_far[0] + 2); + return out; + } + + t1 = 3*in_near[0] + in_far[0]; + out[0] = div4(t1+2); + for (i=1; i < w; ++i) { + t0 = t1; + t1 = 3*in_near[i]+in_far[i]; + out[i*2-1] = div16(3*t0 + t1 + 8); + out[i*2 ] = div16(3*t1 + t0 + 8); + } + out[w*2-1] = div4(t1+2); + + STBI_NOTUSED(hs); + + return out; +} + +static stbi__uint8 *resample_row_generic(stbi__uint8 *out, stbi__uint8 *in_near, stbi__uint8 *in_far, int w, int hs) +{ + // resample with nearest-neighbor + int i,j; + STBI_NOTUSED(in_far); + for (i=0; i < w; ++i) + for (j=0; j < hs; ++j) + out[i*hs+j] = in_near[i]; + return out; +} + +#define float2fixed(x) ((int) ((x) * 65536 + 0.5)) + +// 0.38 seconds on 3*anemones.jpg (0.25 with processor = Pro) +// VC6 without processor=Pro is generating multiple LEAs per multiply! +static void YCbCr_to_RGB_row(stbi__uint8 *out, const stbi__uint8 *y, const stbi__uint8 *pcb, const stbi__uint8 *pcr, int count, int step) +{ + int i; + for (i=0; i < count; ++i) { + int y_fixed = (y[i] << 16) + 32768; // rounding + int r,g,b; + int cr = pcr[i] - 128; + int cb = pcb[i] - 128; + r = y_fixed + cr*float2fixed(1.40200f); + g = y_fixed - cr*float2fixed(0.71414f) - cb*float2fixed(0.34414f); + b = y_fixed + cb*float2fixed(1.77200f); + r >>= 16; + g >>= 16; + b >>= 16; + if ((unsigned) r > 255) { if (r < 0) r = 0; else r = 255; } + if ((unsigned) g > 255) { if (g < 0) g = 0; else g = 255; } + if ((unsigned) b > 255) { if (b < 0) b = 0; else b = 255; } + out[0] = (stbi__uint8)r; + out[1] = (stbi__uint8)g; + out[2] = (stbi__uint8)b; + out[3] = 255; + out += step; + } +} + +#ifdef STBI_SIMD +static stbi_YCbCr_to_RGB_run stbi_YCbCr_installed = YCbCr_to_RGB_row; + +void stbi_install_YCbCr_to_RGB(stbi_YCbCr_to_RGB_run func) +{ + stbi_YCbCr_installed = func; +} +#endif + + +// clean up the temporary component buffers +static void cleanup_jpeg(jpeg *j) +{ + int i; + for (i=0; i < j->s->img_n; ++i) { + if (j->img_comp[i].data) { + free(j->img_comp[i].raw_data); + j->img_comp[i].data = NULL; + } + if (j->img_comp[i].linebuf) { + free(j->img_comp[i].linebuf); + j->img_comp[i].linebuf = NULL; + } + } +} + +typedef struct +{ + resample_row_func resample; + stbi__uint8 *line0,*line1; + int hs,vs; // expansion factor in each axis + int w_lores; // horizontal pixels pre-expansion + int ystep; // how far through vertical expansion we are + int ypos; // which pre-expansion row we're on +} stbi_resample; + +static stbi__uint8 *load_jpeg_image(jpeg *z, int *out_x, int *out_y, int *comp, int req_comp) +{ + int n, decode_n; + // validate req_comp + if (req_comp < 0 || req_comp > 4) return epuc("bad req_comp", "Internal error"); + z->s->img_n = 0; + + // load a jpeg image from whichever source + if (!decode_jpeg_image(z)) { cleanup_jpeg(z); return NULL; } + + // determine actual number of components to generate + n = req_comp ? req_comp : z->s->img_n; + + if (z->s->img_n == 3 && n < 3) + decode_n = 1; + else + decode_n = z->s->img_n; + + // resample and color-convert + { + int k; + unsigned int i,j; + stbi__uint8 *output; + stbi__uint8 *coutput[4]; + + stbi_resample res_comp[4]; + + for (k=0; k < decode_n; ++k) { + stbi_resample *r = &res_comp[k]; + + // allocate line buffer big enough for upsampling off the edges + // with upsample factor of 4 + z->img_comp[k].linebuf = (stbi__uint8 *) malloc(z->s->img_x + 3); + if (!z->img_comp[k].linebuf) { cleanup_jpeg(z); return epuc("outofmem", "Out of memory"); } + + r->hs = z->img_h_max / z->img_comp[k].h; + r->vs = z->img_v_max / z->img_comp[k].v; + r->ystep = r->vs >> 1; + r->w_lores = (z->s->img_x + r->hs-1) / r->hs; + r->ypos = 0; + r->line0 = r->line1 = z->img_comp[k].data; + + if (r->hs == 1 && r->vs == 1) r->resample = resample_row_1; + else if (r->hs == 1 && r->vs == 2) r->resample = resample_row_v_2; + else if (r->hs == 2 && r->vs == 1) r->resample = resample_row_h_2; + else if (r->hs == 2 && r->vs == 2) r->resample = resample_row_hv_2; + else r->resample = resample_row_generic; + } + + // can't error after this so, this is safe + output = (stbi__uint8 *) malloc(n * z->s->img_x * z->s->img_y + 1); + if (!output) { cleanup_jpeg(z); return epuc("outofmem", "Out of memory"); } + + // now go ahead and resample + for (j=0; j < z->s->img_y; ++j) { + stbi__uint8 *out = output + n * z->s->img_x * j; + for (k=0; k < decode_n; ++k) { + stbi_resample *r = &res_comp[k]; + int y_bot = r->ystep >= (r->vs >> 1); + coutput[k] = r->resample(z->img_comp[k].linebuf, + y_bot ? r->line1 : r->line0, + y_bot ? r->line0 : r->line1, + r->w_lores, r->hs); + if (++r->ystep >= r->vs) { + r->ystep = 0; + r->line0 = r->line1; + if (++r->ypos < z->img_comp[k].y) + r->line1 += z->img_comp[k].w2; + } + } + if (n >= 3) { + stbi__uint8 *y = coutput[0]; + if (z->s->img_n == 3) { + #ifdef STBI_SIMD + stbi_YCbCr_installed(out, y, coutput[1], coutput[2], z->s->img_x, n); + #else + YCbCr_to_RGB_row(out, y, coutput[1], coutput[2], z->s->img_x, n); + #endif + } else + for (i=0; i < z->s->img_x; ++i) { + out[0] = out[1] = out[2] = y[i]; + out[3] = 255; // not used if n==3 + out += n; + } + } else { + stbi__uint8 *y = coutput[0]; + if (n == 1) + for (i=0; i < z->s->img_x; ++i) out[i] = y[i]; + else + for (i=0; i < z->s->img_x; ++i) *out++ = y[i], *out++ = 255; + } + } + cleanup_jpeg(z); + *out_x = z->s->img_x; + *out_y = z->s->img_y; + if (comp) *comp = z->s->img_n; // report original components, not output + return output; + } +} + +static unsigned char *stbi_jpeg_load(stbi *s, int *x, int *y, int *comp, int req_comp) +{ + jpeg j; + j.s = s; + return load_jpeg_image(&j, x,y,comp,req_comp); +} + +static int stbi_jpeg_test(stbi *s) +{ + int r; + jpeg j; + j.s = s; + r = decode_jpeg_header(&j, SCAN_type); + stbi_rewind(s); + return r; +} + +static int stbi_jpeg_info_raw(jpeg *j, int *x, int *y, int *comp) +{ + if (!decode_jpeg_header(j, SCAN_header)) { + stbi_rewind( j->s ); + return 0; + } + if (x) *x = j->s->img_x; + if (y) *y = j->s->img_y; + if (comp) *comp = j->s->img_n; + return 1; +} + +static int stbi_jpeg_info(stbi *s, int *x, int *y, int *comp) +{ + jpeg j; + j.s = s; + return stbi_jpeg_info_raw(&j, x, y, comp); +} + +// public domain zlib decode v0.2 Sean Barrett 2006-11-18 +// simple implementation +// - all input must be provided in an upfront buffer +// - all output is written to a single output buffer (can malloc/realloc) +// performance +// - fast huffman + +// fast-way is faster to check than jpeg huffman, but slow way is slower +#define ZFAST_BITS 9 // accelerate all cases in default tables +#define ZFAST_MASK ((1 << ZFAST_BITS) - 1) + +// zlib-style huffman encoding +// (jpegs packs from left, zlib from right, so can't share code) +typedef struct +{ + stbi__uint16 fast[1 << ZFAST_BITS]; + stbi__uint16 firstcode[16]; + int maxcode[17]; + stbi__uint16 firstsymbol[16]; + stbi__uint8 size[288]; + stbi__uint16 value[288]; +} zhuffman; + +stbi_inline static int bitreverse16(int n) +{ + n = ((n & 0xAAAA) >> 1) | ((n & 0x5555) << 1); + n = ((n & 0xCCCC) >> 2) | ((n & 0x3333) << 2); + n = ((n & 0xF0F0) >> 4) | ((n & 0x0F0F) << 4); + n = ((n & 0xFF00) >> 8) | ((n & 0x00FF) << 8); + return n; +} + +stbi_inline static int bit_reverse(int v, int bits) +{ + assert(bits <= 16); + // to bit reverse n bits, reverse 16 and shift + // e.g. 11 bits, bit reverse and shift away 5 + return bitreverse16(v) >> (16-bits); +} + +static int zbuild_huffman(zhuffman *z, stbi__uint8 *sizelist, int num) +{ + int i,k=0; + int code, next_code[16], sizes[17]; + + // DEFLATE spec for generating codes + memset(sizes, 0, sizeof(sizes)); + memset(z->fast, 255, sizeof(z->fast)); + for (i=0; i < num; ++i) + ++sizes[sizelist[i]]; + sizes[0] = 0; + for (i=1; i < 16; ++i) + assert(sizes[i] <= (1 << i)); + code = 0; + for (i=1; i < 16; ++i) { + next_code[i] = code; + z->firstcode[i] = (stbi__uint16) code; + z->firstsymbol[i] = (stbi__uint16) k; + code = (code + sizes[i]); + if (sizes[i]) + if (code-1 >= (1 << i)) return e("bad codelengths","Corrupt JPEG"); + z->maxcode[i] = code << (16-i); // preshift for inner loop + code <<= 1; + k += sizes[i]; + } + z->maxcode[16] = 0x10000; // sentinel + for (i=0; i < num; ++i) { + int s = sizelist[i]; + if (s) { + int c = next_code[s] - z->firstcode[s] + z->firstsymbol[s]; + z->size[c] = (stbi__uint8)s; + z->value[c] = (stbi__uint16)i; + if (s <= ZFAST_BITS) { + int k = bit_reverse(next_code[s],s); + while (k < (1 << ZFAST_BITS)) { + z->fast[k] = (stbi__uint16) c; + k += (1 << s); + } + } + ++next_code[s]; + } + } + return 1; +} + +// zlib-from-memory implementation for PNG reading +// because PNG allows splitting the zlib stream arbitrarily, +// and it's annoying structurally to have PNG call ZLIB call PNG, +// we require PNG read all the IDATs and combine them into a single +// memory buffer + +typedef struct +{ + stbi__uint8 *zbuffer, *zbuffer_end; + int num_bits; + stbi__uint32 code_buffer; + + char *zout; + char *zout_start; + char *zout_end; + int z_expandable; + + zhuffman z_length, z_distance; +} zbuf; + +stbi_inline static int zget8(zbuf *z) +{ + if (z->zbuffer >= z->zbuffer_end) return 0; + return *z->zbuffer++; +} + +static void fill_bits(zbuf *z) +{ + do { + assert(z->code_buffer < (1U << z->num_bits)); + z->code_buffer |= zget8(z) << z->num_bits; + z->num_bits += 8; + } while (z->num_bits <= 24); +} + +stbi_inline static unsigned int zreceive(zbuf *z, int n) +{ + unsigned int k; + if (z->num_bits < n) fill_bits(z); + k = z->code_buffer & ((1 << n) - 1); + z->code_buffer >>= n; + z->num_bits -= n; + return k; +} + +stbi_inline static int zhuffman_decode(zbuf *a, zhuffman *z) +{ + int b,s,k; + if (a->num_bits < 16) fill_bits(a); + b = z->fast[a->code_buffer & ZFAST_MASK]; + if (b < 0xffff) { + s = z->size[b]; + a->code_buffer >>= s; + a->num_bits -= s; + return z->value[b]; + } + + // not resolved by fast table, so compute it the slow way + // use jpeg approach, which requires MSbits at top + k = bit_reverse(a->code_buffer, 16); + for (s=ZFAST_BITS+1; ; ++s) + if (k < z->maxcode[s]) + break; + if (s == 16) return -1; // invalid code! + // code size is s, so: + b = (k >> (16-s)) - z->firstcode[s] + z->firstsymbol[s]; + assert(z->size[b] == s); + a->code_buffer >>= s; + a->num_bits -= s; + return z->value[b]; +} + +static int expand(zbuf *z, int n) // need to make room for n bytes +{ + char *q; + int cur, limit; + if (!z->z_expandable) return e("output buffer limit","Corrupt PNG"); + cur = (int) (z->zout - z->zout_start); + limit = (int) (z->zout_end - z->zout_start); + while (cur + n > limit) + limit *= 2; + q = (char *) realloc(z->zout_start, limit); + if (q == NULL) return e("outofmem", "Out of memory"); + z->zout_start = q; + z->zout = q + cur; + z->zout_end = q + limit; + return 1; +} + +static int length_base[31] = { + 3,4,5,6,7,8,9,10,11,13, + 15,17,19,23,27,31,35,43,51,59, + 67,83,99,115,131,163,195,227,258,0,0 }; + +static int length_extra[31]= +{ 0,0,0,0,0,0,0,0,1,1,1,1,2,2,2,2,3,3,3,3,4,4,4,4,5,5,5,5,0,0,0 }; + +static int dist_base[32] = { 1,2,3,4,5,7,9,13,17,25,33,49,65,97,129,193, +257,385,513,769,1025,1537,2049,3073,4097,6145,8193,12289,16385,24577,0,0}; + +static int dist_extra[32] = +{ 0,0,0,0,1,1,2,2,3,3,4,4,5,5,6,6,7,7,8,8,9,9,10,10,11,11,12,12,13,13}; + +static int parse_huffman_block(zbuf *a) +{ + for(;;) { + int z = zhuffman_decode(a, &a->z_length); + if (z < 256) { + if (z < 0) return e("bad huffman code","Corrupt PNG"); // error in huffman codes + if (a->zout >= a->zout_end) if (!expand(a, 1)) return 0; + *a->zout++ = (char) z; + } else { + stbi__uint8 *p; + int len,dist; + if (z == 256) return 1; + z -= 257; + len = length_base[z]; + if (length_extra[z]) len += zreceive(a, length_extra[z]); + z = zhuffman_decode(a, &a->z_distance); + if (z < 0) return e("bad huffman code","Corrupt PNG"); + dist = dist_base[z]; + if (dist_extra[z]) dist += zreceive(a, dist_extra[z]); + if (a->zout - a->zout_start < dist) return e("bad dist","Corrupt PNG"); + if (a->zout + len > a->zout_end) if (!expand(a, len)) return 0; + p = (stbi__uint8 *) (a->zout - dist); + while (len--) + *a->zout++ = *p++; + } + } +} + +static int compute_huffman_codes(zbuf *a) +{ + static stbi__uint8 length_dezigzag[19] = { 16,17,18,0,8,7,9,6,10,5,11,4,12,3,13,2,14,1,15 }; + zhuffman z_codelength; + stbi__uint8 lencodes[286+32+137];//padding for maximum single op + stbi__uint8 codelength_sizes[19]; + int i,n; + + int hlit = zreceive(a,5) + 257; + int hdist = zreceive(a,5) + 1; + int hclen = zreceive(a,4) + 4; + + memset(codelength_sizes, 0, sizeof(codelength_sizes)); + for (i=0; i < hclen; ++i) { + int s = zreceive(a,3); + codelength_sizes[length_dezigzag[i]] = (stbi__uint8) s; + } + if (!zbuild_huffman(&z_codelength, codelength_sizes, 19)) return 0; + + n = 0; + while (n < hlit + hdist) { + int c = zhuffman_decode(a, &z_codelength); + assert(c >= 0 && c < 19); + if (c < 16) + lencodes[n++] = (stbi__uint8) c; + else if (c == 16) { + c = zreceive(a,2)+3; + memset(lencodes+n, lencodes[n-1], c); + n += c; + } else if (c == 17) { + c = zreceive(a,3)+3; + memset(lencodes+n, 0, c); + n += c; + } else { + assert(c == 18); + c = zreceive(a,7)+11; + memset(lencodes+n, 0, c); + n += c; + } + } + if (n != hlit+hdist) return e("bad codelengths","Corrupt PNG"); + if (!zbuild_huffman(&a->z_length, lencodes, hlit)) return 0; + if (!zbuild_huffman(&a->z_distance, lencodes+hlit, hdist)) return 0; + return 1; +} + +static int parse_uncompressed_block(zbuf *a) +{ + stbi__uint8 header[4]; + int len,nlen,k; + if (a->num_bits & 7) + zreceive(a, a->num_bits & 7); // discard + // drain the bit-packed data into header + k = 0; + while (a->num_bits > 0) { + header[k++] = (stbi__uint8) (a->code_buffer & 255); // wtf this warns? + a->code_buffer >>= 8; + a->num_bits -= 8; + } + assert(a->num_bits == 0); + // now fill header the normal way + while (k < 4) + header[k++] = (stbi__uint8) zget8(a); + len = header[1] * 256 + header[0]; + nlen = header[3] * 256 + header[2]; + if (nlen != (len ^ 0xffff)) return e("zlib corrupt","Corrupt PNG"); + if (a->zbuffer + len > a->zbuffer_end) return e("read past buffer","Corrupt PNG"); + if (a->zout + len > a->zout_end) + if (!expand(a, len)) return 0; + memcpy(a->zout, a->zbuffer, len); + a->zbuffer += len; + a->zout += len; + return 1; +} + +static int parse_zlib_header(zbuf *a) +{ + int cmf = zget8(a); + int cm = cmf & 15; + /* int cinfo = cmf >> 4; */ + int flg = zget8(a); + if ((cmf*256+flg) % 31 != 0) return e("bad zlib header","Corrupt PNG"); // zlib spec + if (flg & 32) return e("no preset dict","Corrupt PNG"); // preset dictionary not allowed in png + if (cm != 8) return e("bad compression","Corrupt PNG"); // DEFLATE required for png + // window = 1 << (8 + cinfo)... but who cares, we fully buffer output + return 1; +} + +// @TODO: should statically initialize these for optimal thread safety +static stbi__uint8 default_length[288], default_distance[32]; +static void init_defaults(void) +{ + int i; // use <= to match clearly with spec + for (i=0; i <= 143; ++i) default_length[i] = 8; + for ( ; i <= 255; ++i) default_length[i] = 9; + for ( ; i <= 279; ++i) default_length[i] = 7; + for ( ; i <= 287; ++i) default_length[i] = 8; + + for (i=0; i <= 31; ++i) default_distance[i] = 5; +} + +int stbi_png_partial; // a quick hack to only allow decoding some of a PNG... I should implement real streaming support instead +static int parse_zlib(zbuf *a, int parse_header) +{ + int final, type; + if (parse_header) + if (!parse_zlib_header(a)) return 0; + a->num_bits = 0; + a->code_buffer = 0; + do { + final = zreceive(a,1); + type = zreceive(a,2); + if (type == 0) { + if (!parse_uncompressed_block(a)) return 0; + } else if (type == 3) { + return 0; + } else { + if (type == 1) { + // use fixed code lengths + if (!default_distance[31]) init_defaults(); + if (!zbuild_huffman(&a->z_length , default_length , 288)) return 0; + if (!zbuild_huffman(&a->z_distance, default_distance, 32)) return 0; + } else { + if (!compute_huffman_codes(a)) return 0; + } + if (!parse_huffman_block(a)) return 0; + } + if (stbi_png_partial && a->zout - a->zout_start > 65536) + break; + } while (!final); + return 1; +} + +static int do_zlib(zbuf *a, char *obuf, int olen, int exp, int parse_header) +{ + a->zout_start = obuf; + a->zout = obuf; + a->zout_end = obuf + olen; + a->z_expandable = exp; + + return parse_zlib(a, parse_header); +} + +char *stbi_zlib_decode_malloc_guesssize(const char *buffer, int len, int initial_size, int *outlen) +{ + zbuf a; + char *p = (char *) malloc(initial_size); + if (p == NULL) return NULL; + a.zbuffer = (stbi__uint8 *) buffer; + a.zbuffer_end = (stbi__uint8 *) buffer + len; + if (do_zlib(&a, p, initial_size, 1, 1)) { + if (outlen) *outlen = (int) (a.zout - a.zout_start); + return a.zout_start; + } else { + free(a.zout_start); + return NULL; + } +} + +char *stbi_zlib_decode_malloc(char const *buffer, int len, int *outlen) +{ + return stbi_zlib_decode_malloc_guesssize(buffer, len, 16384, outlen); +} + +char *stbi_zlib_decode_malloc_guesssize_headerflag(const char *buffer, int len, int initial_size, int *outlen, int parse_header) +{ + zbuf a; + char *p = (char *) malloc(initial_size); + if (p == NULL) return NULL; + a.zbuffer = (stbi__uint8 *) buffer; + a.zbuffer_end = (stbi__uint8 *) buffer + len; + if (do_zlib(&a, p, initial_size, 1, parse_header)) { + if (outlen) *outlen = (int) (a.zout - a.zout_start); + return a.zout_start; + } else { + free(a.zout_start); + return NULL; + } +} + +int stbi_zlib_decode_buffer(char *obuffer, int olen, char const *ibuffer, int ilen) +{ + zbuf a; + a.zbuffer = (stbi__uint8 *) ibuffer; + a.zbuffer_end = (stbi__uint8 *) ibuffer + ilen; + if (do_zlib(&a, obuffer, olen, 0, 1)) + return (int) (a.zout - a.zout_start); + else + return -1; +} + +char *stbi_zlib_decode_noheader_malloc(char const *buffer, int len, int *outlen) +{ + zbuf a; + char *p = (char *) malloc(16384); + if (p == NULL) return NULL; + a.zbuffer = (stbi__uint8 *) buffer; + a.zbuffer_end = (stbi__uint8 *) buffer+len; + if (do_zlib(&a, p, 16384, 1, 0)) { + if (outlen) *outlen = (int) (a.zout - a.zout_start); + return a.zout_start; + } else { + free(a.zout_start); + return NULL; + } +} + +int stbi_zlib_decode_noheader_buffer(char *obuffer, int olen, const char *ibuffer, int ilen) +{ + zbuf a; + a.zbuffer = (stbi__uint8 *) ibuffer; + a.zbuffer_end = (stbi__uint8 *) ibuffer + ilen; + if (do_zlib(&a, obuffer, olen, 0, 0)) + return (int) (a.zout - a.zout_start); + else + return -1; +} + +// public domain "baseline" PNG decoder v0.10 Sean Barrett 2006-11-18 +// simple implementation +// - only 8-bit samples +// - no CRC checking +// - allocates lots of intermediate memory +// - avoids problem of streaming data between subsystems +// - avoids explicit window management +// performance +// - uses stb_zlib, a PD zlib implementation with fast huffman decoding + + +typedef struct +{ + stbi__uint32 length; + stbi__uint32 type; +} chunk; + +#define PNG_TYPE(a,b,c,d) (((a) << 24) + ((b) << 16) + ((c) << 8) + (d)) + +static chunk get_chunk_header(stbi *s) +{ + chunk c; + c.length = get32(s); + c.type = get32(s); + return c; +} + +static int check_png_header(stbi *s) +{ + static stbi__uint8 png_sig[8] = { 137,80,78,71,13,10,26,10 }; + int i; + for (i=0; i < 8; ++i) + if (get8u(s) != png_sig[i]) return e("bad png sig","Not a PNG"); + return 1; +} + +typedef struct +{ + stbi *s; + stbi__uint8 *idata, *expanded, *out; +} png; + + +enum { + F_none=0, F_sub=1, F_up=2, F_avg=3, F_paeth=4, + F_avg_first, F_paeth_first +}; + +static stbi__uint8 first_row_filter[5] = +{ + F_none, F_sub, F_none, F_avg_first, F_paeth_first +}; + +static int paeth(int a, int b, int c) +{ + int p = a + b - c; + int pa = abs(p-a); + int pb = abs(p-b); + int pc = abs(p-c); + if (pa <= pb && pa <= pc) return a; + if (pb <= pc) return b; + return c; +} + +// create the png data from post-deflated data +static int create_png_image_raw(png *a, stbi__uint8 *raw, stbi__uint32 raw_len, int out_n, stbi__uint32 x, stbi__uint32 y) +{ + stbi *s = a->s; + stbi__uint32 i,j,stride = x*out_n; + int k; + int img_n = s->img_n; // copy it into a local for later + assert(out_n == s->img_n || out_n == s->img_n+1); + if (stbi_png_partial) y = 1; + a->out = (stbi__uint8 *) malloc(x * y * out_n); + if (!a->out) return e("outofmem", "Out of memory"); + if (!stbi_png_partial) { + if (s->img_x == x && s->img_y == y) { + if (raw_len != (img_n * x + 1) * y) return e("not enough pixels","Corrupt PNG"); + } else { // interlaced: + if (raw_len < (img_n * x + 1) * y) return e("not enough pixels","Corrupt PNG"); + } + } + for (j=0; j < y; ++j) { + stbi__uint8 *cur = a->out + stride*j; + stbi__uint8 *prior = cur - stride; + int filter = *raw++; + if (filter > 4) return e("invalid filter","Corrupt PNG"); + // if first row, use special filter that doesn't sample previous row + if (j == 0) filter = first_row_filter[filter]; + // handle first pixel explicitly + for (k=0; k < img_n; ++k) { + switch (filter) { + case F_none : cur[k] = raw[k]; break; + case F_sub : cur[k] = raw[k]; break; + case F_up : cur[k] = raw[k] + prior[k]; break; + case F_avg : cur[k] = raw[k] + (prior[k]>>1); break; + case F_paeth : cur[k] = (stbi__uint8) (raw[k] + paeth(0,prior[k],0)); break; + case F_avg_first : cur[k] = raw[k]; break; + case F_paeth_first: cur[k] = raw[k]; break; + } + } + if (img_n != out_n) cur[img_n] = 255; + raw += img_n; + cur += out_n; + prior += out_n; + // this is a little gross, so that we don't switch per-pixel or per-component + if (img_n == out_n) { + #define CASE(f) \ + case f: \ + for (i=x-1; i >= 1; --i, raw+=img_n,cur+=img_n,prior+=img_n) \ + for (k=0; k < img_n; ++k) + switch (filter) { + CASE(F_none) cur[k] = raw[k]; break; + CASE(F_sub) cur[k] = raw[k] + cur[k-img_n]; break; + CASE(F_up) cur[k] = raw[k] + prior[k]; break; + CASE(F_avg) cur[k] = raw[k] + ((prior[k] + cur[k-img_n])>>1); break; + CASE(F_paeth) cur[k] = (stbi__uint8) (raw[k] + paeth(cur[k-img_n],prior[k],prior[k-img_n])); break; + CASE(F_avg_first) cur[k] = raw[k] + (cur[k-img_n] >> 1); break; + CASE(F_paeth_first) cur[k] = (stbi__uint8) (raw[k] + paeth(cur[k-img_n],0,0)); break; + } + #undef CASE + } else { + assert(img_n+1 == out_n); + #define CASE(f) \ + case f: \ + for (i=x-1; i >= 1; --i, cur[img_n]=255,raw+=img_n,cur+=out_n,prior+=out_n) \ + for (k=0; k < img_n; ++k) + switch (filter) { + CASE(F_none) cur[k] = raw[k]; break; + CASE(F_sub) cur[k] = raw[k] + cur[k-out_n]; break; + CASE(F_up) cur[k] = raw[k] + prior[k]; break; + CASE(F_avg) cur[k] = raw[k] + ((prior[k] + cur[k-out_n])>>1); break; + CASE(F_paeth) cur[k] = (stbi__uint8) (raw[k] + paeth(cur[k-out_n],prior[k],prior[k-out_n])); break; + CASE(F_avg_first) cur[k] = raw[k] + (cur[k-out_n] >> 1); break; + CASE(F_paeth_first) cur[k] = (stbi__uint8) (raw[k] + paeth(cur[k-out_n],0,0)); break; + } + #undef CASE + } + } + return 1; +} + +static int create_png_image(png *a, stbi__uint8 *raw, stbi__uint32 raw_len, int out_n, int interlaced) +{ + stbi__uint8 *final; + int p; + int save; + if (!interlaced) + return create_png_image_raw(a, raw, raw_len, out_n, a->s->img_x, a->s->img_y); + save = stbi_png_partial; + stbi_png_partial = 0; + + // de-interlacing + final = (stbi__uint8 *) malloc(a->s->img_x * a->s->img_y * out_n); + for (p=0; p < 7; ++p) { + int xorig[] = { 0,4,0,2,0,1,0 }; + int yorig[] = { 0,0,4,0,2,0,1 }; + int xspc[] = { 8,8,4,4,2,2,1 }; + int yspc[] = { 8,8,8,4,4,2,2 }; + int i,j,x,y; + // pass1_x[4] = 0, pass1_x[5] = 1, pass1_x[12] = 1 + x = (a->s->img_x - xorig[p] + xspc[p]-1) / xspc[p]; + y = (a->s->img_y - yorig[p] + yspc[p]-1) / yspc[p]; + if (x && y) { + if (!create_png_image_raw(a, raw, raw_len, out_n, x, y)) { + free(final); + return 0; + } + for (j=0; j < y; ++j) + for (i=0; i < x; ++i) + memcpy(final + (j*yspc[p]+yorig[p])*a->s->img_x*out_n + (i*xspc[p]+xorig[p])*out_n, + a->out + (j*x+i)*out_n, out_n); + free(a->out); + raw += (x*out_n+1)*y; + raw_len -= (x*out_n+1)*y; + } + } + a->out = final; + + stbi_png_partial = save; + return 1; +} + +static int compute_transparency(png *z, stbi__uint8 tc[3], int out_n) +{ + stbi *s = z->s; + stbi__uint32 i, pixel_count = s->img_x * s->img_y; + stbi__uint8 *p = z->out; + + // compute color-based transparency, assuming we've + // already got 255 as the alpha value in the output + assert(out_n == 2 || out_n == 4); + + if (out_n == 2) { + for (i=0; i < pixel_count; ++i) { + p[1] = (p[0] == tc[0] ? 0 : 255); + p += 2; + } + } else { + for (i=0; i < pixel_count; ++i) { + if (p[0] == tc[0] && p[1] == tc[1] && p[2] == tc[2]) + p[3] = 0; + p += 4; + } + } + return 1; +} + +static int expand_palette(png *a, stbi__uint8 *palette, int len, int pal_img_n) +{ + stbi__uint32 i, pixel_count = a->s->img_x * a->s->img_y; + stbi__uint8 *p, *temp_out, *orig = a->out; + + p = (stbi__uint8 *) malloc(pixel_count * pal_img_n); + if (p == NULL) return e("outofmem", "Out of memory"); + + // between here and free(out) below, exitting would leak + temp_out = p; + + if (pal_img_n == 3) { + for (i=0; i < pixel_count; ++i) { + int n = orig[i]*4; + p[0] = palette[n ]; + p[1] = palette[n+1]; + p[2] = palette[n+2]; + p += 3; + } + } else { + for (i=0; i < pixel_count; ++i) { + int n = orig[i]*4; + p[0] = palette[n ]; + p[1] = palette[n+1]; + p[2] = palette[n+2]; + p[3] = palette[n+3]; + p += 4; + } + } + free(a->out); + a->out = temp_out; + + STBI_NOTUSED(len); + + return 1; +} + +static int stbi_unpremultiply_on_load = 0; +static int stbi_de_iphone_flag = 0; + +void stbi_set_unpremultiply_on_load(int flag_true_if_should_unpremultiply) +{ + stbi_unpremultiply_on_load = flag_true_if_should_unpremultiply; +} +void stbi_convert_iphone_png_to_rgb(int flag_true_if_should_convert) +{ + stbi_de_iphone_flag = flag_true_if_should_convert; +} + +static void stbi_de_iphone(png *z) +{ + stbi *s = z->s; + stbi__uint32 i, pixel_count = s->img_x * s->img_y; + stbi__uint8 *p = z->out; + + if (s->img_out_n == 3) { // convert bgr to rgb + for (i=0; i < pixel_count; ++i) { + stbi__uint8 t = p[0]; + p[0] = p[2]; + p[2] = t; + p += 3; + } + } else { + assert(s->img_out_n == 4); + if (stbi_unpremultiply_on_load) { + // convert bgr to rgb and unpremultiply + for (i=0; i < pixel_count; ++i) { + stbi__uint8 a = p[3]; + stbi__uint8 t = p[0]; + if (a) { + p[0] = p[2] * 255 / a; + p[1] = p[1] * 255 / a; + p[2] = t * 255 / a; + } else { + p[0] = p[2]; + p[2] = t; + } + p += 4; + } + } else { + // convert bgr to rgb + for (i=0; i < pixel_count; ++i) { + stbi__uint8 t = p[0]; + p[0] = p[2]; + p[2] = t; + p += 4; + } + } + } +} + +static int parse_png_file(png *z, int scan, int req_comp) +{ + stbi__uint8 palette[1024], pal_img_n=0; + stbi__uint8 has_trans=0, tc[3]; + stbi__uint32 ioff=0, idata_limit=0, i, pal_len=0; + int first=1,k,interlace=0, iphone=0; + stbi *s = z->s; + + z->expanded = NULL; + z->idata = NULL; + z->out = NULL; + + if (!check_png_header(s)) return 0; + + if (scan == SCAN_type) return 1; + + for (;;) { + chunk c = get_chunk_header(s); + switch (c.type) { + case PNG_TYPE('C','g','B','I'): + iphone = stbi_de_iphone_flag; + skip(s, c.length); + break; + case PNG_TYPE('I','H','D','R'): { + int depth,color,comp,filter; + if (!first) return e("multiple IHDR","Corrupt PNG"); + first = 0; + if (c.length != 13) return e("bad IHDR len","Corrupt PNG"); + s->img_x = get32(s); if (s->img_x > (1 << 24)) return e("too large","Very large image (corrupt?)"); + s->img_y = get32(s); if (s->img_y > (1 << 24)) return e("too large","Very large image (corrupt?)"); + depth = get8(s); if (depth != 8) return e("8bit only","PNG not supported: 8-bit only"); + color = get8(s); if (color > 6) return e("bad ctype","Corrupt PNG"); + if (color == 3) pal_img_n = 3; else if (color & 1) return e("bad ctype","Corrupt PNG"); + comp = get8(s); if (comp) return e("bad comp method","Corrupt PNG"); + filter= get8(s); if (filter) return e("bad filter method","Corrupt PNG"); + interlace = get8(s); if (interlace>1) return e("bad interlace method","Corrupt PNG"); + if (!s->img_x || !s->img_y) return e("0-pixel image","Corrupt PNG"); + if (!pal_img_n) { + s->img_n = (color & 2 ? 3 : 1) + (color & 4 ? 1 : 0); + if ((1 << 30) / s->img_x / s->img_n < s->img_y) return e("too large", "Image too large to decode"); + if (scan == SCAN_header) return 1; + } else { + // if paletted, then pal_n is our final components, and + // img_n is # components to decompress/filter. + s->img_n = 1; + if ((1 << 30) / s->img_x / 4 < s->img_y) return e("too large","Corrupt PNG"); + // if SCAN_header, have to scan to see if we have a tRNS + } + break; + } + + case PNG_TYPE('P','L','T','E'): { + if (first) return e("first not IHDR", "Corrupt PNG"); + if (c.length > 256*3) return e("invalid PLTE","Corrupt PNG"); + pal_len = c.length / 3; + if (pal_len * 3 != c.length) return e("invalid PLTE","Corrupt PNG"); + for (i=0; i < pal_len; ++i) { + palette[i*4+0] = get8u(s); + palette[i*4+1] = get8u(s); + palette[i*4+2] = get8u(s); + palette[i*4+3] = 255; + } + break; + } + + case PNG_TYPE('t','R','N','S'): { + if (first) return e("first not IHDR", "Corrupt PNG"); + if (z->idata) return e("tRNS after IDAT","Corrupt PNG"); + if (pal_img_n) { + if (scan == SCAN_header) { s->img_n = 4; return 1; } + if (pal_len == 0) return e("tRNS before PLTE","Corrupt PNG"); + if (c.length > pal_len) return e("bad tRNS len","Corrupt PNG"); + pal_img_n = 4; + for (i=0; i < c.length; ++i) + palette[i*4+3] = get8u(s); + } else { + if (!(s->img_n & 1)) return e("tRNS with alpha","Corrupt PNG"); + if (c.length != (stbi__uint32) s->img_n*2) return e("bad tRNS len","Corrupt PNG"); + has_trans = 1; + for (k=0; k < s->img_n; ++k) + tc[k] = (stbi__uint8) get16(s); // non 8-bit images will be larger + } + break; + } + + case PNG_TYPE('I','D','A','T'): { + if (first) return e("first not IHDR", "Corrupt PNG"); + if (pal_img_n && !pal_len) return e("no PLTE","Corrupt PNG"); + if (scan == SCAN_header) { s->img_n = pal_img_n; return 1; } + if (ioff + c.length > idata_limit) { + stbi__uint8 *p; + if (idata_limit == 0) idata_limit = c.length > 4096 ? c.length : 4096; + while (ioff + c.length > idata_limit) + idata_limit *= 2; + p = (stbi__uint8 *) realloc(z->idata, idata_limit); if (p == NULL) return e("outofmem", "Out of memory"); + z->idata = p; + } + if (!getn(s, z->idata+ioff,c.length)) return e("outofdata","Corrupt PNG"); + ioff += c.length; + break; + } + + case PNG_TYPE('I','E','N','D'): { + stbi__uint32 raw_len; + if (first) return e("first not IHDR", "Corrupt PNG"); + if (scan != SCAN_load) return 1; + if (z->idata == NULL) return e("no IDAT","Corrupt PNG"); + z->expanded = (stbi__uint8 *) stbi_zlib_decode_malloc_guesssize_headerflag((char *) z->idata, ioff, 16384, (int *) &raw_len, !iphone); + if (z->expanded == NULL) return 0; // zlib should set error + free(z->idata); z->idata = NULL; + if ((req_comp == s->img_n+1 && req_comp != 3 && !pal_img_n) || has_trans) + s->img_out_n = s->img_n+1; + else + s->img_out_n = s->img_n; + if (!create_png_image(z, z->expanded, raw_len, s->img_out_n, interlace)) return 0; + if (has_trans) + if (!compute_transparency(z, tc, s->img_out_n)) return 0; + if (iphone && s->img_out_n > 2) + stbi_de_iphone(z); + if (pal_img_n) { + // pal_img_n == 3 or 4 + s->img_n = pal_img_n; // record the actual colors we had + s->img_out_n = pal_img_n; + if (req_comp >= 3) s->img_out_n = req_comp; + if (!expand_palette(z, palette, pal_len, s->img_out_n)) + return 0; + } + free(z->expanded); z->expanded = NULL; + return 1; + } + + default: + // if critical, fail + if (first) return e("first not IHDR", "Corrupt PNG"); + if ((c.type & (1 << 29)) == 0) { + #ifndef STBI_NO_FAILURE_STRINGS + // not threadsafe + static char invalid_chunk[] = "XXXX chunk not known"; + invalid_chunk[0] = (stbi__uint8) (c.type >> 24); + invalid_chunk[1] = (stbi__uint8) (c.type >> 16); + invalid_chunk[2] = (stbi__uint8) (c.type >> 8); + invalid_chunk[3] = (stbi__uint8) (c.type >> 0); + #endif + return e(invalid_chunk, "PNG not supported: unknown chunk type"); + } + skip(s, c.length); + break; + } + // end of chunk, read and skip CRC + get32(s); + } +} + +static unsigned char *do_png(png *p, int *x, int *y, int *n, int req_comp) +{ + unsigned char *result=NULL; + if (req_comp < 0 || req_comp > 4) return epuc("bad req_comp", "Internal error"); + if (parse_png_file(p, SCAN_load, req_comp)) { + result = p->out; + p->out = NULL; + if (req_comp && req_comp != p->s->img_out_n) { + result = convert_format(result, p->s->img_out_n, req_comp, p->s->img_x, p->s->img_y); + p->s->img_out_n = req_comp; + if (result == NULL) return result; + } + *x = p->s->img_x; + *y = p->s->img_y; + if (n) *n = p->s->img_n; + } + free(p->out); p->out = NULL; + free(p->expanded); p->expanded = NULL; + free(p->idata); p->idata = NULL; + + return result; +} + +static unsigned char *stbi_png_load(stbi *s, int *x, int *y, int *comp, int req_comp) +{ + png p; + p.s = s; + return do_png(&p, x,y,comp,req_comp); +} + +static int stbi_png_test(stbi *s) +{ + int r; + r = check_png_header(s); + stbi_rewind(s); + return r; +} + +static int stbi_png_info_raw(png *p, int *x, int *y, int *comp) +{ + if (!parse_png_file(p, SCAN_header, 0)) { + stbi_rewind( p->s ); + return 0; + } + if (x) *x = p->s->img_x; + if (y) *y = p->s->img_y; + if (comp) *comp = p->s->img_n; + return 1; +} + +static int stbi_png_info(stbi *s, int *x, int *y, int *comp) +{ + png p; + p.s = s; + return stbi_png_info_raw(&p, x, y, comp); +} + +// Microsoft/Windows BMP image + +static int bmp_test(stbi *s) +{ + int sz; + if (get8(s) != 'B') return 0; + if (get8(s) != 'M') return 0; + get32le(s); // discard filesize + get16le(s); // discard reserved + get16le(s); // discard reserved + get32le(s); // discard data offset + sz = get32le(s); + if (sz == 12 || sz == 40 || sz == 56 || sz == 108) return 1; + return 0; +} + +static int stbi_bmp_test(stbi *s) +{ + int r = bmp_test(s); + stbi_rewind(s); + return r; +} + + +// returns 0..31 for the highest set bit +static int high_bit(unsigned int z) +{ + int n=0; + if (z == 0) return -1; + if (z >= 0x10000) n += 16, z >>= 16; + if (z >= 0x00100) n += 8, z >>= 8; + if (z >= 0x00010) n += 4, z >>= 4; + if (z >= 0x00004) n += 2, z >>= 2; + if (z >= 0x00002) n += 1, z >>= 1; + return n; +} + +static int bitcount(unsigned int a) +{ + a = (a & 0x55555555) + ((a >> 1) & 0x55555555); // max 2 + a = (a & 0x33333333) + ((a >> 2) & 0x33333333); // max 4 + a = (a + (a >> 4)) & 0x0f0f0f0f; // max 8 per 4, now 8 bits + a = (a + (a >> 8)); // max 16 per 8 bits + a = (a + (a >> 16)); // max 32 per 8 bits + return a & 0xff; +} + +static int shiftsigned(int v, int shift, int bits) +{ + int result; + int z=0; + + if (shift < 0) v <<= -shift; + else v >>= shift; + result = v; + + z = bits; + while (z < 8) { + result += v >> z; + z += bits; + } + return result; +} + +static stbi_uc *bmp_load(stbi *s, int *x, int *y, int *comp, int req_comp) +{ + stbi__uint8 *out; + unsigned int mr=0,mg=0,mb=0,ma=0, fake_a=0; + stbi_uc pal[256][4]; + int psize=0,i,j,compress=0,width; + int bpp, flip_vertically, pad, target, offset, hsz; + if (get8(s) != 'B' || get8(s) != 'M') return epuc("not BMP", "Corrupt BMP"); + get32le(s); // discard filesize + get16le(s); // discard reserved + get16le(s); // discard reserved + offset = get32le(s); + hsz = get32le(s); + if (hsz != 12 && hsz != 40 && hsz != 56 && hsz != 108) return epuc("unknown BMP", "BMP type not supported: unknown"); + if (hsz == 12) { + s->img_x = get16le(s); + s->img_y = get16le(s); + } else { + s->img_x = get32le(s); + s->img_y = get32le(s); + } + if (get16le(s) != 1) return epuc("bad BMP", "bad BMP"); + bpp = get16le(s); + if (bpp == 1) return epuc("monochrome", "BMP type not supported: 1-bit"); + flip_vertically = ((int) s->img_y) > 0; + s->img_y = abs((int) s->img_y); + if (hsz == 12) { + if (bpp < 24) + psize = (offset - 14 - 24) / 3; + } else { + compress = get32le(s); + if (compress == 1 || compress == 2) return epuc("BMP RLE", "BMP type not supported: RLE"); + get32le(s); // discard sizeof + get32le(s); // discard hres + get32le(s); // discard vres + get32le(s); // discard colorsused + get32le(s); // discard max important + if (hsz == 40 || hsz == 56) { + if (hsz == 56) { + get32le(s); + get32le(s); + get32le(s); + get32le(s); + } + if (bpp == 16 || bpp == 32) { + mr = mg = mb = 0; + if (compress == 0) { + if (bpp == 32) { + mr = 0xffu << 16; + mg = 0xffu << 8; + mb = 0xffu << 0; + ma = 0xffu << 24; + fake_a = 1; // @TODO: check for cases like alpha value is all 0 and switch it to 255 + STBI_NOTUSED(fake_a); + } else { + mr = 31u << 10; + mg = 31u << 5; + mb = 31u << 0; + } + } else if (compress == 3) { + mr = get32le(s); + mg = get32le(s); + mb = get32le(s); + // not documented, but generated by photoshop and handled by mspaint + if (mr == mg && mg == mb) { + // ?!?!? + return epuc("bad BMP", "bad BMP"); + } + } else + return epuc("bad BMP", "bad BMP"); + } + } else { + assert(hsz == 108); + mr = get32le(s); + mg = get32le(s); + mb = get32le(s); + ma = get32le(s); + get32le(s); // discard color space + for (i=0; i < 12; ++i) + get32le(s); // discard color space parameters + } + if (bpp < 16) + psize = (offset - 14 - hsz) >> 2; + } + s->img_n = ma ? 4 : 3; + if (req_comp && req_comp >= 3) // we can directly decode 3 or 4 + target = req_comp; + else + target = s->img_n; // if they want monochrome, we'll post-convert + out = (stbi_uc *) malloc(target * s->img_x * s->img_y); + if (!out) return epuc("outofmem", "Out of memory"); + if (bpp < 16) { + int z=0; + if (psize == 0 || psize > 256) { free(out); return epuc("invalid", "Corrupt BMP"); } + for (i=0; i < psize; ++i) { + pal[i][2] = get8u(s); + pal[i][1] = get8u(s); + pal[i][0] = get8u(s); + if (hsz != 12) get8(s); + pal[i][3] = 255; + } + skip(s, offset - 14 - hsz - psize * (hsz == 12 ? 3 : 4)); + if (bpp == 4) width = (s->img_x + 1) >> 1; + else if (bpp == 8) width = s->img_x; + else { free(out); return epuc("bad bpp", "Corrupt BMP"); } + pad = (-width)&3; + for (j=0; j < (int) s->img_y; ++j) { + for (i=0; i < (int) s->img_x; i += 2) { + int v=get8(s),v2=0; + if (bpp == 4) { + v2 = v & 15; + v >>= 4; + } + out[z++] = pal[v][0]; + out[z++] = pal[v][1]; + out[z++] = pal[v][2]; + if (target == 4) out[z++] = 255; + if (i+1 == (int) s->img_x) break; + v = (bpp == 8) ? get8(s) : v2; + out[z++] = pal[v][0]; + out[z++] = pal[v][1]; + out[z++] = pal[v][2]; + if (target == 4) out[z++] = 255; + } + skip(s, pad); + } + } else { + int rshift=0,gshift=0,bshift=0,ashift=0,rcount=0,gcount=0,bcount=0,acount=0; + int z = 0; + int easy=0; + skip(s, offset - 14 - hsz); + if (bpp == 24) width = 3 * s->img_x; + else if (bpp == 16) width = 2*s->img_x; + else /* bpp = 32 and pad = 0 */ width=0; + pad = (-width) & 3; + if (bpp == 24) { + easy = 1; + } else if (bpp == 32) { + if (mb == 0xff && mg == 0xff00 && mr == 0x00ff0000 && ma == 0xff000000) + easy = 2; + } + if (!easy) { + if (!mr || !mg || !mb) { free(out); return epuc("bad masks", "Corrupt BMP"); } + // right shift amt to put high bit in position #7 + rshift = high_bit(mr)-7; rcount = bitcount(mr); + gshift = high_bit(mg)-7; gcount = bitcount(mg); + bshift = high_bit(mb)-7; bcount = bitcount(mb); + ashift = high_bit(ma)-7; acount = bitcount(ma); + } + for (j=0; j < (int) s->img_y; ++j) { + if (easy) { + for (i=0; i < (int) s->img_x; ++i) { + int a; + out[z+2] = get8u(s); + out[z+1] = get8u(s); + out[z+0] = get8u(s); + z += 3; + a = (easy == 2 ? get8(s) : 255); + if (target == 4) out[z++] = (stbi__uint8) a; + } + } else { + for (i=0; i < (int) s->img_x; ++i) { + stbi__uint32 v = (stbi__uint32) (bpp == 16 ? get16le(s) : get32le(s)); + int a; + out[z++] = (stbi__uint8) shiftsigned(v & mr, rshift, rcount); + out[z++] = (stbi__uint8) shiftsigned(v & mg, gshift, gcount); + out[z++] = (stbi__uint8) shiftsigned(v & mb, bshift, bcount); + a = (ma ? shiftsigned(v & ma, ashift, acount) : 255); + if (target == 4) out[z++] = (stbi__uint8) a; + } + } + skip(s, pad); + } + } + if (flip_vertically) { + stbi_uc t; + for (j=0; j < (int) s->img_y>>1; ++j) { + stbi_uc *p1 = out + j *s->img_x*target; + stbi_uc *p2 = out + (s->img_y-1-j)*s->img_x*target; + for (i=0; i < (int) s->img_x*target; ++i) { + t = p1[i], p1[i] = p2[i], p2[i] = t; + } + } + } + + if (req_comp && req_comp != target) { + out = convert_format(out, target, req_comp, s->img_x, s->img_y); + if (out == NULL) return out; // convert_format frees input on failure + } + + *x = s->img_x; + *y = s->img_y; + if (comp) *comp = s->img_n; + return out; +} + +static stbi_uc *stbi_bmp_load(stbi *s,int *x, int *y, int *comp, int req_comp) +{ + return bmp_load(s, x,y,comp,req_comp); +} + + +// Targa Truevision - TGA +// by Jonathan Dummer + +static int tga_info(stbi *s, int *x, int *y, int *comp) +{ + int tga_w, tga_h, tga_comp; + int sz; + get8u(s); // discard Offset + sz = get8u(s); // color type + if( sz > 1 ) { + stbi_rewind(s); + return 0; // only RGB or indexed allowed + } + sz = get8u(s); // image type + // only RGB or grey allowed, +/- RLE + if ((sz != 1) && (sz != 2) && (sz != 3) && (sz != 9) && (sz != 10) && (sz != 11)) return 0; + skip(s,9); + tga_w = get16le(s); + if( tga_w < 1 ) { + stbi_rewind(s); + return 0; // test width + } + tga_h = get16le(s); + if( tga_h < 1 ) { + stbi_rewind(s); + return 0; // test height + } + sz = get8(s); // bits per pixel + // only RGB or RGBA or grey allowed + if ((sz != 8) && (sz != 16) && (sz != 24) && (sz != 32)) { + stbi_rewind(s); + return 0; + } + tga_comp = sz; + if (x) *x = tga_w; + if (y) *y = tga_h; + if (comp) *comp = tga_comp / 8; + return 1; // seems to have passed everything +} + +int stbi_tga_info(stbi *s, int *x, int *y, int *comp) +{ + return tga_info(s, x, y, comp); +} + +static int tga_test(stbi *s) +{ + int sz; + get8u(s); // discard Offset + sz = get8u(s); // color type + if ( sz > 1 ) return 0; // only RGB or indexed allowed + sz = get8u(s); // image type + if ( (sz != 1) && (sz != 2) && (sz != 3) && (sz != 9) && (sz != 10) && (sz != 11) ) return 0; // only RGB or grey allowed, +/- RLE + get16(s); // discard palette start + get16(s); // discard palette length + get8(s); // discard bits per palette color entry + get16(s); // discard x origin + get16(s); // discard y origin + if ( get16(s) < 1 ) return 0; // test width + if ( get16(s) < 1 ) return 0; // test height + sz = get8(s); // bits per pixel + if ( (sz != 8) && (sz != 16) && (sz != 24) && (sz != 32) ) return 0; // only RGB or RGBA or grey allowed + return 1; // seems to have passed everything +} + +static int stbi_tga_test(stbi *s) +{ + int res = tga_test(s); + stbi_rewind(s); + return res; +} + +static stbi_uc *tga_load(stbi *s, int *x, int *y, int *comp, int req_comp) +{ + // read in the TGA header stuff + int tga_offset = get8u(s); + int tga_indexed = get8u(s); + int tga_image_type = get8u(s); + int tga_is_RLE = 0; + int tga_palette_start = get16le(s); + int tga_palette_len = get16le(s); + int tga_palette_bits = get8u(s); + int tga_x_origin = get16le(s); + int tga_y_origin = get16le(s); + int tga_width = get16le(s); + int tga_height = get16le(s); + int tga_bits_per_pixel = get8u(s); + int tga_comp = tga_bits_per_pixel / 8; + int tga_inverted = get8u(s); + // image data + unsigned char *tga_data; + unsigned char *tga_palette = NULL; + int i, j; + unsigned char raw_data[4]; + int RLE_count = 0; + int RLE_repeating = 0; + int read_next_pixel = 1; + + // do a tiny bit of precessing + if ( tga_image_type >= 8 ) + { + tga_image_type -= 8; + tga_is_RLE = 1; + } + /* int tga_alpha_bits = tga_inverted & 15; */ + tga_inverted = 1 - ((tga_inverted >> 5) & 1); + + // error check + if ( //(tga_indexed) || + (tga_width < 1) || (tga_height < 1) || + (tga_image_type < 1) || (tga_image_type > 3) || + ((tga_bits_per_pixel != 8) && (tga_bits_per_pixel != 16) && + (tga_bits_per_pixel != 24) && (tga_bits_per_pixel != 32)) + ) + { + return NULL; // we don't report this as a bad TGA because we don't even know if it's TGA + } + + // If I'm paletted, then I'll use the number of bits from the palette + if ( tga_indexed ) + { + tga_comp = tga_palette_bits / 8; + } + + // tga info + *x = tga_width; + *y = tga_height; + if (comp) *comp = tga_comp; + + tga_data = (unsigned char*)malloc( tga_width * tga_height * req_comp ); + if (!tga_data) return epuc("outofmem", "Out of memory"); + + // skip to the data's starting position (offset usually = 0) + skip(s, tga_offset ); + + if ( !tga_indexed && !tga_is_RLE) { + for (i=0; i < tga_height; ++i) { + int y = tga_inverted ? tga_height -i - 1 : i; + stbi__uint8 *tga_row = tga_data + y*tga_width*tga_comp; + getn(s, tga_row, tga_width * tga_comp); + } + } else { + // do I need to load a palette? + if ( tga_indexed) + { + // any data to skip? (offset usually = 0) + skip(s, tga_palette_start ); + // load the palette + tga_palette = (unsigned char*)malloc( tga_palette_len * tga_palette_bits / 8 ); + if (!tga_palette) { + free(tga_data); + return epuc("outofmem", "Out of memory"); + } + if (!getn(s, tga_palette, tga_palette_len * tga_palette_bits / 8 )) { + free(tga_data); + free(tga_palette); + return epuc("bad palette", "Corrupt TGA"); + } + } + // load the data + for (i=0; i < tga_width * tga_height; ++i) + { + // if I'm in RLE mode, do I need to get a RLE chunk? + if ( tga_is_RLE ) + { + if ( RLE_count == 0 ) + { + // yep, get the next byte as a RLE command + int RLE_cmd = get8u(s); + RLE_count = 1 + (RLE_cmd & 127); + RLE_repeating = RLE_cmd >> 7; + read_next_pixel = 1; + } else if ( !RLE_repeating ) + { + read_next_pixel = 1; + } + } else + { + read_next_pixel = 1; + } + // OK, if I need to read a pixel, do it now + if ( read_next_pixel ) + { + // load however much data we did have + if ( tga_indexed ) + { + // read in 1 byte, then perform the lookup + int pal_idx = get8u(s); + if ( pal_idx >= tga_palette_len ) + { + // invalid index + pal_idx = 0; + } + pal_idx *= tga_bits_per_pixel / 8; + for (j = 0; j*8 < tga_bits_per_pixel; ++j) + { + raw_data[j] = tga_palette[pal_idx+j]; + } + } else + { + // read in the data raw + for (j = 0; j*8 < tga_bits_per_pixel; ++j) + { + raw_data[j] = get8u(s); + } + } + // clear the reading flag for the next pixel + read_next_pixel = 0; + } // end of reading a pixel + + // copy data + for (j = 0; j < tga_comp; ++j) + tga_data[i*tga_comp+j] = raw_data[j]; + + // in case we're in RLE mode, keep counting down + --RLE_count; + } + // do I need to invert the image? + if ( tga_inverted ) + { + for (j = 0; j*2 < tga_height; ++j) + { + int index1 = j * tga_width * req_comp; + int index2 = (tga_height - 1 - j) * tga_width * req_comp; + for (i = tga_width * req_comp; i > 0; --i) + { + unsigned char temp = tga_data[index1]; + tga_data[index1] = tga_data[index2]; + tga_data[index2] = temp; + ++index1; + ++index2; + } + } + } + // clear my palette, if I had one + if ( tga_palette != NULL ) + { + free( tga_palette ); + } + } + + // swap RGB + if (tga_comp >= 3) + { + unsigned char* tga_pixel = tga_data; + for (i=0; i < tga_width * tga_height; ++i) + { + unsigned char temp = tga_pixel[0]; + tga_pixel[0] = tga_pixel[2]; + tga_pixel[2] = temp; + tga_pixel += tga_comp; + } + } + + // convert to target component count + if (req_comp && req_comp != tga_comp) + tga_data = convert_format(tga_data, tga_comp, req_comp, tga_width, tga_height); + + // the things I do to get rid of an error message, and yet keep + // Microsoft's C compilers happy... [8^( + tga_palette_start = tga_palette_len = tga_palette_bits = + tga_x_origin = tga_y_origin = 0; + // OK, done + return tga_data; +} + +static stbi_uc *stbi_tga_load(stbi *s, int *x, int *y, int *comp, int req_comp) +{ + return tga_load(s,x,y,comp,req_comp); +} + + +// ************************************************************************************************* +// Photoshop PSD loader -- PD by Thatcher Ulrich, integration by Nicolas Schulz, tweaked by STB + +static int psd_test(stbi *s) +{ + if (get32(s) != 0x38425053) return 0; // "8BPS" + else return 1; +} + +static int stbi_psd_test(stbi *s) +{ + int r = psd_test(s); + stbi_rewind(s); + return r; +} + +static stbi_uc *psd_load(stbi *s, int *x, int *y, int *comp, int req_comp) +{ + int pixelCount; + int channelCount, compression; + int channel, i, count, len; + int w,h; + stbi__uint8 *out; + + // Check identifier + if (get32(s) != 0x38425053) // "8BPS" + return epuc("not PSD", "Corrupt PSD image"); + + // Check file type version. + if (get16(s) != 1) + return epuc("wrong version", "Unsupported version of PSD image"); + + // Skip 6 reserved bytes. + skip(s, 6 ); + + // Read the number of channels (R, G, B, A, etc). + channelCount = get16(s); + if (channelCount < 0 || channelCount > 16) + return epuc("wrong channel count", "Unsupported number of channels in PSD image"); + + // Read the rows and columns of the image. + h = get32(s); + w = get32(s); + + // Make sure the depth is 8 bits. + if (get16(s) != 8) + return epuc("unsupported bit depth", "PSD bit depth is not 8 bit"); + + // Make sure the color mode is RGB. + // Valid options are: + // 0: Bitmap + // 1: Grayscale + // 2: Indexed color + // 3: RGB color + // 4: CMYK color + // 7: Multichannel + // 8: Duotone + // 9: Lab color + if (get16(s) != 3) + return epuc("wrong color format", "PSD is not in RGB color format"); + + // Skip the Mode Data. (It's the palette for indexed color; other info for other modes.) + skip(s,get32(s) ); + + // Skip the image resources. (resolution, pen tool paths, etc) + skip(s, get32(s) ); + + // Skip the reserved data. + skip(s, get32(s) ); + + // Find out if the data is compressed. + // Known values: + // 0: no compression + // 1: RLE compressed + compression = get16(s); + if (compression > 1) + return epuc("bad compression", "PSD has an unknown compression format"); + + // Create the destination image. + out = (stbi_uc *) malloc(4 * w*h); + if (!out) return epuc("outofmem", "Out of memory"); + pixelCount = w*h; + + // Initialize the data to zero. + //memset( out, 0, pixelCount * 4 ); + + // Finally, the image data. + if (compression) { + // RLE as used by .PSD and .TIFF + // Loop until you get the number of unpacked bytes you are expecting: + // Read the next source byte into n. + // If n is between 0 and 127 inclusive, copy the next n+1 bytes literally. + // Else if n is between -127 and -1 inclusive, copy the next byte -n+1 times. + // Else if n is 128, noop. + // Endloop + + // The RLE-compressed data is preceeded by a 2-byte data count for each row in the data, + // which we're going to just skip. + skip(s, h * channelCount * 2 ); + + // Read the RLE data by channel. + for (channel = 0; channel < 4; channel++) { + stbi__uint8 *p; + + p = out+channel; + if (channel >= channelCount) { + // Fill this channel with default data. + for (i = 0; i < pixelCount; i++) *p = (channel == 3 ? 255 : 0), p += 4; + } else { + // Read the RLE data. + count = 0; + while (count < pixelCount) { + len = get8(s); + if (len == 128) { + // No-op. + } else if (len < 128) { + // Copy next len+1 bytes literally. + len++; + count += len; + while (len) { + *p = get8u(s); + p += 4; + len--; + } + } else if (len > 128) { + stbi__uint8 val; + // Next -len+1 bytes in the dest are replicated from next source byte. + // (Interpret len as a negative 8-bit int.) + len ^= 0x0FF; + len += 2; + val = get8u(s); + count += len; + while (len) { + *p = val; + p += 4; + len--; + } + } + } + } + } + + } else { + // We're at the raw image data. It's each channel in order (Red, Green, Blue, Alpha, ...) + // where each channel consists of an 8-bit value for each pixel in the image. + + // Read the data by channel. + for (channel = 0; channel < 4; channel++) { + stbi__uint8 *p; + + p = out + channel; + if (channel > channelCount) { + // Fill this channel with default data. + for (i = 0; i < pixelCount; i++) *p = channel == 3 ? 255 : 0, p += 4; + } else { + // Read the data. + for (i = 0; i < pixelCount; i++) + *p = get8u(s), p += 4; + } + } + } + + if (req_comp && req_comp != 4) { + out = convert_format(out, 4, req_comp, w, h); + if (out == NULL) return out; // convert_format frees input on failure + } + + if (comp) *comp = channelCount; + *y = h; + *x = w; + + return out; +} + +static stbi_uc *stbi_psd_load(stbi *s, int *x, int *y, int *comp, int req_comp) +{ + return psd_load(s,x,y,comp,req_comp); +} + +// ************************************************************************************************* +// Softimage PIC loader +// by Tom Seddon +// +// See http://softimage.wiki.softimage.com/index.php/INFO:_PIC_file_format +// See http://ozviz.wasp.uwa.edu.au/~pbourke/dataformats/softimagepic/ + +static int pic_is4(stbi *s,const char *str) +{ + int i; + for (i=0; i<4; ++i) + if (get8(s) != (stbi_uc)str[i]) + return 0; + + return 1; +} + +static int pic_test(stbi *s) +{ + int i; + + if (!pic_is4(s,"\x53\x80\xF6\x34")) + return 0; + + for(i=0;i<84;++i) + get8(s); + + if (!pic_is4(s,"PICT")) + return 0; + + return 1; +} + +typedef struct +{ + stbi_uc size,type,channel; +} pic_packet_t; + +static stbi_uc *pic_readval(stbi *s, int channel, stbi_uc *dest) +{ + int mask=0x80, i; + + for (i=0; i<4; ++i, mask>>=1) { + if (channel & mask) { + if (at_eof(s)) return epuc("bad file","PIC file too short"); + dest[i]=get8u(s); + } + } + + return dest; +} + +static void pic_copyval(int channel,stbi_uc *dest,const stbi_uc *src) +{ + int mask=0x80,i; + + for (i=0;i<4; ++i, mask>>=1) + if (channel&mask) + dest[i]=src[i]; +} + +static stbi_uc *pic_load2(stbi *s,int width,int height,int *comp, stbi_uc *result) +{ + int act_comp=0,num_packets=0,y,chained; + pic_packet_t packets[10]; + + // this will (should...) cater for even some bizarre stuff like having data + // for the same channel in multiple packets. + do { + pic_packet_t *packet; + + if (num_packets==sizeof(packets)/sizeof(packets[0])) + return epuc("bad format","too many packets"); + + packet = &packets[num_packets++]; + + chained = get8(s); + packet->size = get8u(s); + packet->type = get8u(s); + packet->channel = get8u(s); + + act_comp |= packet->channel; + + if (at_eof(s)) return epuc("bad file","file too short (reading packets)"); + if (packet->size != 8) return epuc("bad format","packet isn't 8bpp"); + } while (chained); + + *comp = (act_comp & 0x10 ? 4 : 3); // has alpha channel? + + for(y=0; ytype) { + default: + return epuc("bad format","packet has bad compression type"); + + case 0: {//uncompressed + int x; + + for(x=0;xchannel,dest)) + return 0; + break; + } + + case 1://Pure RLE + { + int left=width, i; + + while (left>0) { + stbi_uc count,value[4]; + + count=get8u(s); + if (at_eof(s)) return epuc("bad file","file too short (pure read count)"); + + if (count > left) + count = (stbi__uint8) left; + + if (!pic_readval(s,packet->channel,value)) return 0; + + for(i=0; ichannel,dest,value); + left -= count; + } + } + break; + + case 2: {//Mixed RLE + int left=width; + while (left>0) { + int count = get8(s), i; + if (at_eof(s)) return epuc("bad file","file too short (mixed read count)"); + + if (count >= 128) { // Repeated + stbi_uc value[4]; + int i; + + if (count==128) + count = get16(s); + else + count -= 127; + if (count > left) + return epuc("bad file","scanline overrun"); + + if (!pic_readval(s,packet->channel,value)) + return 0; + + for(i=0;ichannel,dest,value); + } else { // Raw + ++count; + if (count>left) return epuc("bad file","scanline overrun"); + + for(i=0;ichannel,dest)) + return 0; + } + left-=count; + } + break; + } + } + } + } + + return result; +} + +static stbi_uc *pic_load(stbi *s,int *px,int *py,int *comp,int req_comp) +{ + stbi_uc *result; + int i, x,y; + + for (i=0; i<92; ++i) + get8(s); + + x = get16(s); + y = get16(s); + if (at_eof(s)) return epuc("bad file","file too short (pic header)"); + if ((1 << 28) / x < y) return epuc("too large", "Image too large to decode"); + + get32(s); //skip `ratio' + get16(s); //skip `fields' + get16(s); //skip `pad' + + // intermediate buffer is RGBA + result = (stbi_uc *) malloc(x*y*4); + memset(result, 0xff, x*y*4); + + if (!pic_load2(s,x,y,comp, result)) { + free(result); + result=0; + } + *px = x; + *py = y; + if (req_comp == 0) req_comp = *comp; + result=convert_format(result,4,req_comp,x,y); + + return result; +} + +static int stbi_pic_test(stbi *s) +{ + int r = pic_test(s); + stbi_rewind(s); + return r; +} + +static stbi_uc *stbi_pic_load(stbi *s, int *x, int *y, int *comp, int req_comp) +{ + return pic_load(s,x,y,comp,req_comp); +} + +// ************************************************************************************************* +// GIF loader -- public domain by Jean-Marc Lienher -- simplified/shrunk by stb +typedef struct stbi_gif_lzw_struct { + stbi__int16 prefix; + stbi__uint8 first; + stbi__uint8 suffix; +} stbi_gif_lzw; + +typedef struct stbi_gif_struct +{ + int w,h; + stbi_uc *out; // output buffer (always 4 components) + int flags, bgindex, ratio, transparent, eflags; + stbi__uint8 pal[256][4]; + stbi__uint8 lpal[256][4]; + stbi_gif_lzw codes[4096]; + stbi__uint8 *color_table; + int parse, step; + int lflags; + int start_x, start_y; + int max_x, max_y; + int cur_x, cur_y; + int line_size; +} stbi_gif; + +static int gif_test(stbi *s) +{ + int sz; + if (get8(s) != 'G' || get8(s) != 'I' || get8(s) != 'F' || get8(s) != '8') return 0; + sz = get8(s); + if (sz != '9' && sz != '7') return 0; + if (get8(s) != 'a') return 0; + return 1; +} + +static int stbi_gif_test(stbi *s) +{ + int r = gif_test(s); + stbi_rewind(s); + return r; +} + +static void stbi_gif_parse_colortable(stbi *s, stbi__uint8 pal[256][4], int num_entries, int transp) +{ + int i; + for (i=0; i < num_entries; ++i) { + pal[i][2] = get8u(s); + pal[i][1] = get8u(s); + pal[i][0] = get8u(s); + pal[i][3] = transp ? 0 : 255; + } +} + +static int stbi_gif_header(stbi *s, stbi_gif *g, int *comp, int is_info) +{ + stbi__uint8 version; + if (get8(s) != 'G' || get8(s) != 'I' || get8(s) != 'F' || get8(s) != '8') + return e("not GIF", "Corrupt GIF"); + + version = get8u(s); + if (version != '7' && version != '9') return e("not GIF", "Corrupt GIF"); + if (get8(s) != 'a') return e("not GIF", "Corrupt GIF"); + + failure_reason = ""; + g->w = get16le(s); + g->h = get16le(s); + g->flags = get8(s); + g->bgindex = get8(s); + g->ratio = get8(s); + g->transparent = -1; + + if (comp != 0) *comp = 4; // can't actually tell whether it's 3 or 4 until we parse the comments + + if (is_info) return 1; + + if (g->flags & 0x80) + stbi_gif_parse_colortable(s,g->pal, 2 << (g->flags & 7), -1); + + return 1; +} + +static int stbi_gif_info_raw(stbi *s, int *x, int *y, int *comp) +{ + stbi_gif g; + if (!stbi_gif_header(s, &g, comp, 1)) { + stbi_rewind( s ); + return 0; + } + if (x) *x = g.w; + if (y) *y = g.h; + return 1; +} + +static void stbi_out_gif_code(stbi_gif *g, stbi__uint16 code) +{ + stbi__uint8 *p, *c; + + // recurse to decode the prefixes, since the linked-list is backwards, + // and working backwards through an interleaved image would be nasty + if (g->codes[code].prefix >= 0) + stbi_out_gif_code(g, g->codes[code].prefix); + + if (g->cur_y >= g->max_y) return; + + p = &g->out[g->cur_x + g->cur_y]; + c = &g->color_table[g->codes[code].suffix * 4]; + + if (c[3] >= 128) { + p[0] = c[2]; + p[1] = c[1]; + p[2] = c[0]; + p[3] = c[3]; + } + g->cur_x += 4; + + if (g->cur_x >= g->max_x) { + g->cur_x = g->start_x; + g->cur_y += g->step; + + while (g->cur_y >= g->max_y && g->parse > 0) { + g->step = (1 << g->parse) * g->line_size; + g->cur_y = g->start_y + (g->step >> 1); + --g->parse; + } + } +} + +static stbi__uint8 *stbi_process_gif_raster(stbi *s, stbi_gif *g) +{ + stbi__uint8 lzw_cs; + stbi__int32 len, code; + stbi__uint32 first; + stbi__int32 codesize, codemask, avail, oldcode, bits, valid_bits, clear; + stbi_gif_lzw *p; + + lzw_cs = get8u(s); + clear = 1 << lzw_cs; + first = 1; + codesize = lzw_cs + 1; + codemask = (1 << codesize) - 1; + bits = 0; + valid_bits = 0; + for (code = 0; code < clear; code++) { + g->codes[code].prefix = -1; + g->codes[code].first = (stbi__uint8) code; + g->codes[code].suffix = (stbi__uint8) code; + } + + // support no starting clear code + avail = clear+2; + oldcode = -1; + + len = 0; + for(;;) { + if (valid_bits < codesize) { + if (len == 0) { + len = get8(s); // start new block + if (len == 0) + return g->out; + } + --len; + bits |= (stbi__int32) get8(s) << valid_bits; + valid_bits += 8; + } else { + stbi__int32 code = bits & codemask; + bits >>= codesize; + valid_bits -= codesize; + // @OPTIMIZE: is there some way we can accelerate the non-clear path? + if (code == clear) { // clear code + codesize = lzw_cs + 1; + codemask = (1 << codesize) - 1; + avail = clear + 2; + oldcode = -1; + first = 0; + } else if (code == clear + 1) { // end of stream code + skip(s, len); + while ((len = get8(s)) > 0) + skip(s,len); + return g->out; + } else if (code <= avail) { + if (first) return epuc("no clear code", "Corrupt GIF"); + + if (oldcode >= 0) { + p = &g->codes[avail++]; + if (avail > 4096) return epuc("too many codes", "Corrupt GIF"); + p->prefix = (stbi__int16) oldcode; + p->first = g->codes[oldcode].first; + p->suffix = (code == avail) ? p->first : g->codes[code].first; + } else if (code == avail) + return epuc("illegal code in raster", "Corrupt GIF"); + + stbi_out_gif_code(g, (stbi__uint16) code); + + if ((avail & codemask) == 0 && avail <= 0x0FFF) { + codesize++; + codemask = (1 << codesize) - 1; + } + + oldcode = code; + } else { + return epuc("illegal code in raster", "Corrupt GIF"); + } + } + } +} + +static void stbi_fill_gif_background(stbi_gif *g) +{ + int i; + stbi__uint8 *c = g->pal[g->bgindex]; + // @OPTIMIZE: write a dword at a time + for (i = 0; i < g->w * g->h * 4; i += 4) { + stbi__uint8 *p = &g->out[i]; + p[0] = c[2]; + p[1] = c[1]; + p[2] = c[0]; + p[3] = c[3]; + } +} + +// this function is designed to support animated gifs, although stb_image doesn't support it +static stbi__uint8 *stbi_gif_load_next(stbi *s, stbi_gif *g, int *comp, int req_comp) +{ + int i; + stbi__uint8 *old_out = 0; + + if (g->out == 0) { + if (!stbi_gif_header(s, g, comp,0)) return 0; // failure_reason set by stbi_gif_header + g->out = (stbi__uint8 *) malloc(4 * g->w * g->h); + if (g->out == 0) return epuc("outofmem", "Out of memory"); + stbi_fill_gif_background(g); + } else { + // animated-gif-only path + if (((g->eflags & 0x1C) >> 2) == 3) { + old_out = g->out; + g->out = (stbi__uint8 *) malloc(4 * g->w * g->h); + if (g->out == 0) return epuc("outofmem", "Out of memory"); + memcpy(g->out, old_out, g->w*g->h*4); + } + } + + for (;;) { + switch (get8(s)) { + case 0x2C: /* Image Descriptor */ + { + stbi__int32 x, y, w, h; + stbi__uint8 *o; + + x = get16le(s); + y = get16le(s); + w = get16le(s); + h = get16le(s); + if (((x + w) > (g->w)) || ((y + h) > (g->h))) + return epuc("bad Image Descriptor", "Corrupt GIF"); + + g->line_size = g->w * 4; + g->start_x = x * 4; + g->start_y = y * g->line_size; + g->max_x = g->start_x + w * 4; + g->max_y = g->start_y + h * g->line_size; + g->cur_x = g->start_x; + g->cur_y = g->start_y; + + g->lflags = get8(s); + + if (g->lflags & 0x40) { + g->step = 8 * g->line_size; // first interlaced spacing + g->parse = 3; + } else { + g->step = g->line_size; + g->parse = 0; + } + + if (g->lflags & 0x80) { + stbi_gif_parse_colortable(s,g->lpal, 2 << (g->lflags & 7), g->eflags & 0x01 ? g->transparent : -1); + g->color_table = (stbi__uint8 *) g->lpal; + } else if (g->flags & 0x80) { + for (i=0; i < 256; ++i) // @OPTIMIZE: reset only the previous transparent + g->pal[i][3] = 255; + if (g->transparent >= 0 && (g->eflags & 0x01)) + g->pal[g->transparent][3] = 0; + g->color_table = (stbi__uint8 *) g->pal; + } else + return epuc("missing color table", "Corrupt GIF"); + + o = stbi_process_gif_raster(s, g); + if (o == NULL) return NULL; + + if (req_comp && req_comp != 4) + o = convert_format(o, 4, req_comp, g->w, g->h); + return o; + } + + case 0x21: // Comment Extension. + { + int len; + if (get8(s) == 0xF9) { // Graphic Control Extension. + len = get8(s); + if (len == 4) { + g->eflags = get8(s); + get16le(s); // delay + g->transparent = get8(s); + } else { + skip(s, len); + break; + } + } + while ((len = get8(s)) != 0) + skip(s, len); + break; + } + + case 0x3B: // gif stream termination code + return (stbi__uint8 *) 1; + + default: + return epuc("unknown code", "Corrupt GIF"); + } + } +} + +static stbi_uc *stbi_gif_load(stbi *s, int *x, int *y, int *comp, int req_comp) +{ + stbi__uint8 *u = 0; + stbi_gif g={0}; + + u = stbi_gif_load_next(s, &g, comp, req_comp); + if (u == (void *) 1) u = 0; // end of animated gif marker + if (u) { + *x = g.w; + *y = g.h; + } + + return u; +} + +static int stbi_gif_info(stbi *s, int *x, int *y, int *comp) +{ + return stbi_gif_info_raw(s,x,y,comp); +} + + +// ************************************************************************************************* +// Radiance RGBE HDR loader +// originally by Nicolas Schulz +#ifndef STBI_NO_HDR +static int hdr_test(stbi *s) +{ + const char *signature = "#?RADIANCE\n"; + int i; + for (i=0; signature[i]; ++i) + if (get8(s) != signature[i]) + return 0; + return 1; +} + +static int stbi_hdr_test(stbi* s) +{ + int r = hdr_test(s); + stbi_rewind(s); + return r; +} + +#define HDR_BUFLEN 1024 +static char *hdr_gettoken(stbi *z, char *buffer) +{ + int len=0; + char c = '\0'; + + c = (char) get8(z); + + while (!at_eof(z) && c != '\n') { + buffer[len++] = c; + if (len == HDR_BUFLEN-1) { + // flush to end of line + while (!at_eof(z) && get8(z) != '\n') + ; + break; + } + c = (char) get8(z); + } + + buffer[len] = 0; + return buffer; +} + +static void hdr_convert(float *output, stbi_uc *input, int req_comp) +{ + if ( input[3] != 0 ) { + float f1; + // Exponent + f1 = (float) ldexp(1.0f, input[3] - (int)(128 + 8)); + if (req_comp <= 2) + output[0] = (input[0] + input[1] + input[2]) * f1 / 3; + else { + output[0] = input[0] * f1; + output[1] = input[1] * f1; + output[2] = input[2] * f1; + } + if (req_comp == 2) output[1] = 1; + if (req_comp == 4) output[3] = 1; + } else { + switch (req_comp) { + case 4: output[3] = 1; /* fallthrough */ + case 3: output[0] = output[1] = output[2] = 0; + break; + case 2: output[1] = 1; /* fallthrough */ + case 1: output[0] = 0; + break; + } + } +} + +static float *hdr_load(stbi *s, int *x, int *y, int *comp, int req_comp) +{ + char buffer[HDR_BUFLEN]; + char *token; + int valid = 0; + int width, height; + stbi_uc *scanline; + float *hdr_data; + int len; + unsigned char count, value; + int i, j, k, c1,c2, z; + + + // Check identifier + if (strcmp(hdr_gettoken(s,buffer), "#?RADIANCE") != 0) + return epf("not HDR", "Corrupt HDR image"); + + // Parse header + for(;;) { + token = hdr_gettoken(s,buffer); + if (token[0] == 0) break; + if (strcmp(token, "FORMAT=32-bit_rle_rgbe") == 0) valid = 1; + } + + if (!valid) return epf("unsupported format", "Unsupported HDR format"); + + // Parse width and height + // can't use sscanf() if we're not using stdio! + token = hdr_gettoken(s,buffer); + if (strncmp(token, "-Y ", 3)) return epf("unsupported data layout", "Unsupported HDR format"); + token += 3; + height = (int) strtol(token, &token, 10); + while (*token == ' ') ++token; + if (strncmp(token, "+X ", 3)) return epf("unsupported data layout", "Unsupported HDR format"); + token += 3; + width = (int) strtol(token, NULL, 10); + + *x = width; + *y = height; + + *comp = 3; + if (req_comp == 0) req_comp = 3; + + // Read data + hdr_data = (float *) malloc(height * width * req_comp * sizeof(float)); + + // Load image data + // image data is stored as some number of sca + if ( width < 8 || width >= 32768) { + // Read flat data + for (j=0; j < height; ++j) { + for (i=0; i < width; ++i) { + stbi_uc rgbe[4]; + main_decode_loop: + getn(s, rgbe, 4); + hdr_convert(hdr_data + j * width * req_comp + i * req_comp, rgbe, req_comp); + } + } + } else { + // Read RLE-encoded data + scanline = NULL; + + for (j = 0; j < height; ++j) { + c1 = get8(s); + c2 = get8(s); + len = get8(s); + if (c1 != 2 || c2 != 2 || (len & 0x80)) { + // not run-length encoded, so we have to actually use THIS data as a decoded + // pixel (note this can't be a valid pixel--one of RGB must be >= 128) + stbi__uint8 rgbe[4]; + rgbe[0] = (stbi__uint8) c1; + rgbe[1] = (stbi__uint8) c2; + rgbe[2] = (stbi__uint8) len; + rgbe[3] = (stbi__uint8) get8u(s); + hdr_convert(hdr_data, rgbe, req_comp); + i = 1; + j = 0; + free(scanline); + goto main_decode_loop; // yes, this makes no sense + } + len <<= 8; + len |= get8(s); + if (len != width) { free(hdr_data); free(scanline); return epf("invalid decoded scanline length", "corrupt HDR"); } + if (scanline == NULL) scanline = (stbi_uc *) malloc(width * 4); + + for (k = 0; k < 4; ++k) { + i = 0; + while (i < width) { + count = get8u(s); + if (count > 128) { + // Run + value = get8u(s); + count -= 128; + for (z = 0; z < count; ++z) + scanline[i++ * 4 + k] = value; + } else { + // Dump + for (z = 0; z < count; ++z) + scanline[i++ * 4 + k] = get8u(s); + } + } + } + for (i=0; i < width; ++i) + hdr_convert(hdr_data+(j*width + i)*req_comp, scanline + i*4, req_comp); + } + free(scanline); + } + + return hdr_data; +} + +static float *stbi_hdr_load(stbi *s, int *x, int *y, int *comp, int req_comp) +{ + return hdr_load(s,x,y,comp,req_comp); +} + +static int stbi_hdr_info(stbi *s, int *x, int *y, int *comp) +{ + char buffer[HDR_BUFLEN]; + char *token; + int valid = 0; + + if (strcmp(hdr_gettoken(s,buffer), "#?RADIANCE") != 0) { + stbi_rewind( s ); + return 0; + } + + for(;;) { + token = hdr_gettoken(s,buffer); + if (token[0] == 0) break; + if (strcmp(token, "FORMAT=32-bit_rle_rgbe") == 0) valid = 1; + } + + if (!valid) { + stbi_rewind( s ); + return 0; + } + token = hdr_gettoken(s,buffer); + if (strncmp(token, "-Y ", 3)) { + stbi_rewind( s ); + return 0; + } + token += 3; + *y = (int) strtol(token, &token, 10); + while (*token == ' ') ++token; + if (strncmp(token, "+X ", 3)) { + stbi_rewind( s ); + return 0; + } + token += 3; + *x = (int) strtol(token, NULL, 10); + *comp = 3; + return 1; +} +#endif // STBI_NO_HDR + +static int stbi_bmp_info(stbi *s, int *x, int *y, int *comp) +{ + int hsz; + if (get8(s) != 'B' || get8(s) != 'M') { + stbi_rewind( s ); + return 0; + } + skip(s,12); + hsz = get32le(s); + if (hsz != 12 && hsz != 40 && hsz != 56 && hsz != 108) { + stbi_rewind( s ); + return 0; + } + if (hsz == 12) { + *x = get16le(s); + *y = get16le(s); + } else { + *x = get32le(s); + *y = get32le(s); + } + if (get16le(s) != 1) { + stbi_rewind( s ); + return 0; + } + *comp = get16le(s) / 8; + return 1; +} + +static int stbi_psd_info(stbi *s, int *x, int *y, int *comp) +{ + int channelCount; + if (get32(s) != 0x38425053) { + stbi_rewind( s ); + return 0; + } + if (get16(s) != 1) { + stbi_rewind( s ); + return 0; + } + skip(s, 6); + channelCount = get16(s); + if (channelCount < 0 || channelCount > 16) { + stbi_rewind( s ); + return 0; + } + *y = get32(s); + *x = get32(s); + if (get16(s) != 8) { + stbi_rewind( s ); + return 0; + } + if (get16(s) != 3) { + stbi_rewind( s ); + return 0; + } + *comp = 4; + return 1; +} + +static int stbi_pic_info(stbi *s, int *x, int *y, int *comp) +{ + int act_comp=0,num_packets=0,chained; + pic_packet_t packets[10]; + + skip(s, 92); + + *x = get16(s); + *y = get16(s); + if (at_eof(s)) return 0; + if ( (*x) != 0 && (1 << 28) / (*x) < (*y)) { + stbi_rewind( s ); + return 0; + } + + skip(s, 8); + + do { + pic_packet_t *packet; + + if (num_packets==sizeof(packets)/sizeof(packets[0])) + return 0; + + packet = &packets[num_packets++]; + chained = get8(s); + packet->size = get8u(s); + packet->type = get8u(s); + packet->channel = get8u(s); + act_comp |= packet->channel; + + if (at_eof(s)) { + stbi_rewind( s ); + return 0; + } + if (packet->size != 8) { + stbi_rewind( s ); + return 0; + } + } while (chained); + + *comp = (act_comp & 0x10 ? 4 : 3); + + return 1; +} + +static int stbi_info_main(stbi *s, int *x, int *y, int *comp) +{ + if (stbi_jpeg_info(s, x, y, comp)) + return 1; + if (stbi_png_info(s, x, y, comp)) + return 1; + if (stbi_gif_info(s, x, y, comp)) + return 1; + if (stbi_bmp_info(s, x, y, comp)) + return 1; + if (stbi_psd_info(s, x, y, comp)) + return 1; + if (stbi_pic_info(s, x, y, comp)) + return 1; + #ifndef STBI_NO_HDR + if (stbi_hdr_info(s, x, y, comp)) + return 1; + #endif + // test tga last because it's a crappy test! + if (stbi_tga_info(s, x, y, comp)) + return 1; + return e("unknown image type", "Image not of any known type, or corrupt"); +} + +#ifndef STBI_NO_STDIO +int stbi_info(char const *filename, int *x, int *y, int *comp) +{ + FILE *f = fopen(filename, "rb"); + int result; + if (!f) return e("can't fopen", "Unable to open file"); + result = stbi_info_from_file(f, x, y, comp); + fclose(f); + return result; +} + +int stbi_info_from_file(FILE *f, int *x, int *y, int *comp) +{ + int r; + stbi s; + long pos = ftell(f); + start_file(&s, f); + r = stbi_info_main(&s,x,y,comp); + fseek(f,pos,SEEK_SET); + return r; +} +#endif // !STBI_NO_STDIO + +int stbi_info_from_memory(stbi_uc const *buffer, int len, int *x, int *y, int *comp) +{ + stbi s; + start_mem(&s,buffer,len); + return stbi_info_main(&s,x,y,comp); +} + +int stbi_info_from_callbacks(stbi_io_callbacks const *c, void *user, int *x, int *y, int *comp) +{ + stbi s; + start_callbacks(&s, (stbi_io_callbacks *) c, user); + return stbi_info_main(&s,x,y,comp); +} + +#endif // STBI_HEADER_FILE_ONLY + +#if !defined(STBI_NO_STDIO) && defined(_MSC_VER) && _MSC_VER >= 1400 +#pragma warning(pop) +#endif + + +/* + revision history: + 1.35 (2014-05-27) + various warnings + fix broken STBI_SIMD path + fix bug where stbi_load_from_file no longer left file pointer in correct place + fix broken non-easy path for 32-bit BMP (possibly never used) + TGA optimization by Arseny Kapoulkine + 1.34 (unknown) + use STBI_NOTUSED in resample_row_generic(), fix one more leak in tga failure case + 1.33 (2011-07-14) + make stbi_is_hdr work in STBI_NO_HDR (as specified), minor compiler-friendly improvements + 1.32 (2011-07-13) + support for "info" function for all supported filetypes (SpartanJ) + 1.31 (2011-06-20) + a few more leak fixes, bug in PNG handling (SpartanJ) + 1.30 (2011-06-11) + added ability to load files via callbacks to accomidate custom input streams (Ben Wenger) + removed deprecated format-specific test/load functions + removed support for installable file formats (stbi_loader) -- would have been broken for IO callbacks anyway + error cases in bmp and tga give messages and don't leak (Raymond Barbiero, grisha) + fix inefficiency in decoding 32-bit BMP (David Woo) + 1.29 (2010-08-16) + various warning fixes from Aurelien Pocheville + 1.28 (2010-08-01) + fix bug in GIF palette transparency (SpartanJ) + 1.27 (2010-08-01) + cast-to-stbi__uint8 to fix warnings + 1.26 (2010-07-24) + fix bug in file buffering for PNG reported by SpartanJ + 1.25 (2010-07-17) + refix trans_data warning (Won Chun) + 1.24 (2010-07-12) + perf improvements reading from files on platforms with lock-heavy fgetc() + minor perf improvements for jpeg + deprecated type-specific functions so we'll get feedback if they're needed + attempt to fix trans_data warning (Won Chun) + 1.23 fixed bug in iPhone support + 1.22 (2010-07-10) + removed image *writing* support + stbi_info support from Jetro Lauha + GIF support from Jean-Marc Lienher + iPhone PNG-extensions from James Brown + warning-fixes from Nicolas Schulz and Janez Zemva (i.e. Janez (U+017D)emva) + 1.21 fix use of 'stbi__uint8' in header (reported by jon blow) + 1.20 added support for Softimage PIC, by Tom Seddon + 1.19 bug in interlaced PNG corruption check (found by ryg) + 1.18 2008-08-02 + fix a threading bug (local mutable static) + 1.17 support interlaced PNG + 1.16 major bugfix - convert_format converted one too many pixels + 1.15 initialize some fields for thread safety + 1.14 fix threadsafe conversion bug + header-file-only version (#define STBI_HEADER_FILE_ONLY before including) + 1.13 threadsafe + 1.12 const qualifiers in the API + 1.11 Support installable IDCT, colorspace conversion routines + 1.10 Fixes for 64-bit (don't use "unsigned long") + optimized upsampling by Fabian "ryg" Giesen + 1.09 Fix format-conversion for PSD code (bad global variables!) + 1.08 Thatcher Ulrich's PSD code integrated by Nicolas Schulz + 1.07 attempt to fix C++ warning/errors again + 1.06 attempt to fix C++ warning/errors again + 1.05 fix TGA loading to return correct *comp and use good luminance calc + 1.04 default float alpha is 1, not 255; use 'void *' for stbi_image_free + 1.03 bugfixes to STBI_NO_STDIO, STBI_NO_HDR + 1.02 support for (subset of) HDR files, float interface for preferred access to them + 1.01 fix bug: possible bug in handling right-side up bmps... not sure + fix bug: the stbi_bmp_load() and stbi_tga_load() functions didn't work at all + 1.00 interface to zlib that skips zlib header + 0.99 correct handling of alpha in palette + 0.98 TGA loader by lonesock; dynamically add loaders (untested) + 0.97 jpeg errors on too large a file; also catch another malloc failure + 0.96 fix detection of invalid v value - particleman@mollyrocket forum + 0.95 during header scan, seek to markers in case of padding + 0.94 STBI_NO_STDIO to disable stdio usage; rename all #defines the same + 0.93 handle jpegtran output; verbose errors + 0.92 read 4,8,16,24,32-bit BMP files of several formats + 0.91 output 24-bit Windows 3.0 BMP files + 0.90 fix a few more warnings; bump version number to approach 1.0 + 0.61 bugfixes due to Marc LeBlanc, Christopher Lloyd + 0.60 fix compiling as c++ + 0.59 fix warnings: merge Dave Moore's -Wall fixes + 0.58 fix bug: zlib uncompressed mode len/nlen was wrong endian + 0.57 fix bug: jpg last huffman symbol before marker was >9 bits but less than 16 available + 0.56 fix bug: zlib uncompressed mode len vs. nlen + 0.55 fix bug: restart_interval not initialized to 0 + 0.54 allow NULL for 'int *comp' + 0.53 fix bug in png 3->4; speedup png decoding + 0.52 png handles req_comp=3,4 directly; minor cleanup; jpeg comments + 0.51 obey req_comp requests, 1-component jpegs return as 1-component, + on 'test' only check type, not whether we support this variant + 0.50 first released version +*/ diff --git a/impeller/third_party/stb/stb/deprecated/stretchy_buffer.txt b/impeller/third_party/stb/stb/deprecated/stretchy_buffer.txt new file mode 100644 index 0000000000000..dcd747e195b2f --- /dev/null +++ b/impeller/third_party/stb/stb/deprecated/stretchy_buffer.txt @@ -0,0 +1,28 @@ +// stretchy buffer // init: NULL // free: sbfree() // push_back: sbpush() // size: sbcount() // +#define sbfree(a) ((a) ? free(stb__sbraw(a)),0 : 0) +#define sbpush(a,v) (stb__sbmaybegrow(a,1), (a)[stb__sbn(a)++] = (v)) +#define sbcount(a) ((a) ? stb__sbn(a) : 0) +#define sbadd(a,n) (stb__sbmaybegrow(a,n), stb__sbn(a)+=(n), &(a)[stb__sbn(a)-(n)]) +#define sblast(a) ((a)[stb__sbn(a)-1]) + +#include +#define stb__sbraw(a) ((int *) (a) - 2) +#define stb__sbm(a) stb__sbraw(a)[0] +#define stb__sbn(a) stb__sbraw(a)[1] + +#define stb__sbneedgrow(a,n) ((a)==0 || stb__sbn(a)+n >= stb__sbm(a)) +#define stb__sbmaybegrow(a,n) (stb__sbneedgrow(a,(n)) ? stb__sbgrow(a,n) : 0) +#define stb__sbgrow(a,n) stb__sbgrowf((void **) &(a), (n), sizeof(*(a))) + +static void stb__sbgrowf(void **arr, int increment, int itemsize) +{ + int m = *arr ? 2*stb__sbm(*arr)+increment : increment+1; + void *p = realloc(*arr ? stb__sbraw(*arr) : 0, itemsize * m + sizeof(int)*2); + assert(p); + if (p) { + if (!*arr) ((int *) p)[1] = 0; + *arr = (void *) ((int *) p + 2); + stb__sbm(*arr) = m; + } +} + diff --git a/impeller/third_party/stb/stb/docs/other_libs.md b/impeller/third_party/stb/stb/docs/other_libs.md new file mode 100644 index 0000000000000..d50fe164c46ff --- /dev/null +++ b/impeller/third_party/stb/stb/docs/other_libs.md @@ -0,0 +1,181 @@ +# Other single-file public-domain/open source libraries with minimal dependencies + +In addition to all of [my libraries](https://github.com/nothings/stb), there are other, similar libraries. + +The following is a list of small, easy-to-integrate, portable libraries +which are usable from C and/or C++, and should be able to be compiled on both +32-bit and 64-bit platforms. + +### Rules + +- Libraries must be usable from C or C++, ideally both +- Libraries should be usable from more than one platform (ideally, all major desktops and/or all major mobile) +- Libraries should compile and work on both 32-bit and 64-bit platforms +- Libraries should use at most two files + +Exceptions will be allowed for good reasons. + +### New libraries and corrections + +See discussion after the list. + +## Library listing + +**Public domain single-file libraries usable from C and C++ are in bold.** Other +libraries are either non-public domain, or two files, or not usable from both C and C++, or +all three. Libraries of more than two files are mostly forbidden. + +For the API column, "C" means C only, "C++" means C++ only, and "C/C++" means C/C++ usable +from either; some files may require *building* as C or C++ but still qualify as "C/C++" as +long as the header file uses `extern "C"` to make it work. (In some cases, a header-file-only +library may compile as both C or C++, but produce an implementation that can only be called from +one or the other, because of a lack of use of `extern "C"`; in this case the table still qualifies it +as C/C++, as this is not an obstacle to most users.) + + +category | library | license | API |files| description +----------------- | --------------------------------------------------------------------- |:--------------------:|:---:|:---:| ----------- +AI | [micropather](http://www.grinninglizard.com/MicroPather/) | zlib | C++ | 2 | pathfinding with A* +argv | [parg](https://github.com/jibsen/parg) | **public domain** | C | 1 | argv parsing +audio | [aw_ima.h](https://github.com/afterwise/aw-ima/blob/master/aw-ima.h) | MIT |C/C++|**1**| IMA-ADPCM audio decoder +audio |**[dr_flac](https://github.com/mackron/dr_libs)** | **public domain** |C/C++|**1**| FLAC audio decoder +compression |**[miniz.c](https://github.com/richgel999/miniz)** |**public domain**|C/C++|**1**| compression,decompression, zip file, png writing +compression | [lz4](https://github.com/Cyan4973/lz4) | BSD |C/C++| 2 | fast but larger LZ compression +compression | [fastlz](https://code.google.com/archive/p/fastlz/source/default/source) | MIT |C/C++| 2 | fast but larger LZ compression +compression | [pithy](https://github.com/johnezang/pithy) | BSD |C/C++| 2 | fast but larger LZ compression +crypto | [TweetNaCl](http://tweetnacl.cr.yp.to/software.html) | **public domain** | C | 2 | high-quality tiny cryptography library +data structures|[klib](http://attractivechaos.github.io/klib/) | MIT |C/C++| 2 | many 2-file libs: hash, sort, b-tree, etc +data structures | [uthash](https://github.com/troydhanson/uthash) | BSD |C/C++| 2 | several 1-header, 1-license-file libs: generic hash, list, etc +data structures | [PackedArray](https://github.com/gpakosz/PackedArray) | **WTFPLv2** | C | 2 | memory-efficient array of elements with non-pow2 bitcount +data structures | [minilibs](https://github.com/ccxvii/minilibs) | **public domain** | C | 2 | two-file binary tress (also regex, etc) +files & filenames |**[DG_misc.h](https://github.com/DanielGibson/Snippets/)** | **public domain** |C/C++|**1**| Daniel Gibson's stb.h-esque cross-platform helpers: path/file, strings +files & filenames | [whereami](https://github.com/gpakosz/whereami) |**WTFPLv2** |C/C++| 2 | get path/filename of executable or module +files & filenames | [noc_file_dialog.h](https://github.com/guillaumechereau/noc) | MIT |C/C++| 1 | file open/save dialogs (Linux/OSX/Windows) +files & filenames | [dirent](https://github.com/tronkko/dirent) | MIT |C/C++|**1**| dirent for windows: retrieve file & dir info +files & filenames | [TinyDir](https://github.com/cxong/tinydir) | BSD | C |**1**| cross-platform directory reader +geometry file | [tk_objfile](https://github.com/joeld42/tk_objfile) | MIT |C/C++|**1**| OBJ file loader +geometry file | [tinyply](https://github.com/ddiakopoulos/tinyply) | **public domain** | C++ | 2 | PLY mesh file loader +geometry file | [tinyobjloader](https://github.com/syoyo/tinyobjloader) | BSD | C++ |**1**| wavefront OBJ file loader +geometry math |**[nv_voronoi.h](http://www.icculus.org/~mordred/nvlib/)** | **public domain** |C/C++|**1**| find voronoi regions on lattice w/ integer inputs +geometry math |**[sobol.h](https://github.com/Marc-B-Reynolds/Stand-alone-junk/)** | **public domain** |C/C++|**1**| sobol & stratified sampling sequences +geometry math | [sdf.h](https://github.com/memononen/SDF) | MIT |C/C++|**1**| compute signed-distance field from antialiased image +geometry math | [nanoflann](https://github.com/jlblancoc/nanoflann) | BSD | C++ |**1**| build KD trees for point clouds +geometry math | [jc_voronoi](https://github.com/JCash/voronoi) | MIT |C/C++|**1**| find voronoi regions on float/double data +geometry math | [par_msquares](https://github.com/prideout/par) | MIT |C/C++|**1**| convert (binarized) image to triangles +geometry math | [par_shapes](http://github.prideout.net/shapes) | MIT |C/C++|**1**| generate various 3d geometric shapes +geometry math | [Tomas Akenine-Moller snippets](http://tinyurl.com/ht79ndj) | **public domain** |C/C++| 2 | various 3D intersection calculations, not lib-ified +geometry math | [Clipper](http://www.angusj.com/delphi/clipper.php) | Boost | C++ | 2 | line & polygon clipping & offsetting +geometry math | [PolyPartition](https://github.com/ivanfratric/polypartition) | MIT | C++ | 2 | polygon triangulation, partitioning +geometry math | [Voxelizer](https://github.com/karimnaaji/voxelizer) | MIT |C/C++|**1**| convert triangle mesh to voxel triangle mesh +graphics (2d) | [blendish](https://bitbucket.org/duangle/oui-blendish/src) | MIT |C/C++|**1**| blender-style widget rendering +graphics (2d) | [tigr](https://bitbucket.org/rmitton/tigr/src) | **public domain** |C/C++| 2 | quick-n-dirty window text/graphics for Windows +graphics (2d) | [noc_turtle](https://github.com/guillaumechereau/noc) | MIT |C/C++| 2 | procedural graphics generator +graphics (3-D) | [mikktspace](http://tinyurl.com/z6xtucm) | zlib |C/C++| 2 | compute tangent space for normal mapping +graphics (3-D) | [debug-draw](https://github.com/glampert/debug-draw) | **public domain** | C++ |**1**| API-agnostic immediate-mode debug rendering +hardware |**[EasyTab](https://github.com/ApoorvaJ/EasyTab)** | **public domain** |C/C++|**1**| multi-platform tablet input +images | [jo_gif.cpp](http://www.jonolick.com/home/gif-writer) | **public domain** | C++ |**1**| animated GIF writer (CPP file can also be used as H file) +images |**[gif.h](https://github.com/ginsweater/gif-h)** | **public domain** | C |**1**| animated GIF writer (can only include once) +images |**[tiny_jpeg.h](https://github.com/serge-rgb/TinyJPEG/)** | **public domain** |C/C++|**1**| JPEG encoder +images | [miniexr](https://github.com/aras-p/miniexr) | **public domain** | C++ | 2 | OpenEXR writer, needs header file +images | [tinyexr](https://github.com/syoyo/tinyexr) | BSD |C/C++|**1**| EXR image read/write, uses miniz internally +images | [lodepng](http://lodev.org/lodepng/) | zlib |C/C++| 2 | PNG encoder/decoder +images | [nanoSVG](https://github.com/memononen/nanosvg) | zlib |C/C++|**1**| 1-file SVG parser; 1-file SVG rasterizer +images | [picopng.cpp](http://lodev.org/lodepng/picopng.cpp) | zlib | C++ | 2 | tiny PNG loader +images | [jpeg-compressor](https://github.com/richgel999/jpeg-compressor) | **public domain** | C++ | 2 | 2-file jpeg compress, 2-file jpeg decompress +images | [easyexif](https://github.com/mayanklahiri/easyexif) | MIT | C++ | 2 | EXIF metadata extractor for JPEG images +images |**[cro_mipmap.h](https://github.com/thebeast33/cro_lib)** | **public domain** |C/C++|**1**| average, min, max mipmap generators +math | [mm_vec.h](https://github.com/vurtun/mmx) | BSD |C/C++|**1**| SIMD vector math +math | [ShaderFastLibs](https://github.com/michaldrobot/ShaderFastLibs) | MIT | C++ |**1**| (also HLSL) approximate transcendental functions optimized for shaders (esp. GCN) +math | [TinyExpr](https://github.com/codeplea/tinyexpr) | zlib | C | 2 | evaluation of math expressions from strings +math | [linalg.h](https://github.com/sgorsten/linalg) | **unlicense** | C++ |**1**| vector/matrix/quaternion math +math | [PoissonGenerator.h](https://github.com/corporateshark/poisson-disk-generator) | MIT | C++ |**1**| Poisson disk points generator (disk or rect) +multithreading | [mm_sched.h](https://github.com/vurtun/mmx) | zlib |C/C++|**1**| cross-platform multithreaded task scheduler +network |**[zed_net](https://github.com/ZedZull/zed_net)** | **public domain** |C/C++|**1**| cross-platform socket wrapper +network | [mm_web.h](https://github.com/vurtun/mmx) | BSD |C/C++|**1**| lightweight webserver, fork of webby +network | [par_easycurl.h](https://github.com/prideout/par) | MIT |C/C++|**1**| curl wrapper +network | [yocto](https://github.com/tom-seddon/yhs) | **public domain** |C/C++| 2 | non-production-use http server +network | [happyhttp](http://scumways.com/happyhttp/happyhttp.html) | zlib | C++ | 2 | http client requests +network | [mongoose](https://github.com/cesanta/mongoose) |_GPLv2_ |C/C++| 2 | http server +network | [LUrlParser](https://github.com/corporateshark/LUrlParser) | MIT | C++ | 2 | lightweight URL & URI parser RFC 1738, RFC 3986 +parsing | [SLRE](https://github.com/cesanta/slre) |_GPLv2_ |C/C++|**1**| regular expression matcher +parsing | [PicoJSON](https://github.com/kazuho/picojson) | BSD | C++ |**1**| JSON parse/serializer +parsing | [mm_lexer.h](https://github.com/vurtun/mmx) | zlib |C/C++|**1**| C-esque language lexer +parsing | [json.h](https://github.com/sheredom/json.h) | **public domain** |C/C++| 2 | JSON parser +parsing | [jzon.h](https://github.com/Zguy/Jzon) | MIT | C++ | 2 | JSON parser +parsing | [parson](https://github.com/kgabis/parson) | MIT |C/C++| 2 | JSON parser and serializer +parsing | [minilibs](https://github.com/ccxvii/minilibs) | **public domain** | C | 2 | two-file regex (also binary tree, etc) +profiling | [Remotery](https://github.com/Celtoys/Remotery) | Apache 2.0 |C/C++| 2 | CPU/GPU profiler Win/Mac/Linux, using web browser for viewer +profiling | [MicroProfile](https://bitbucket.org/jonasmeyer/microprofile) | **unlicense** | C++ | 2-4 | CPU (and GPU?) profiler, 1-3 header files, uses miniz internally +scripting | [LIL](http://runtimelegend.com/rep/lil/) | zlib |C/C++| 2 | interpreter for a Tcl-like scripting language +scripting | [lualite](https://github.com/janezz55/lualite/) | MIT | C++ |**1**| generate lua bindings in C++ +scripting | [Picol](https://chiselapp.com/user/dbohdan/repository/picol/) | BSD |C/C++|**1**| interpreter for a Tcl-like scripting language +strings |**[DG_misc.h](https://github.com/DanielGibson/Snippets/)** | **public domain** |C/C++|**1**| Daniel Gibson's stb.h-esque cross-platform helpers: path/file, strings +strings |**[utf8](https://github.com/sheredom/utf8.h)** | **public domain** |C/C++|**1**| utf8 string library +strings |**[strpool.h](https://github.com/mattiasgustavsson/libs)** | **public domain** |C/C++|**1**| string interning +strings | [dfa](http://bjoern.hoehrmann.de/utf-8/decoder/dfa/) | MIT |C/C++| 2 | fast utf8 decoder (need a header file) +strings |**[gb_string.h](https://github.com/gingerBill/gb)** | **public domain** |C/C++|**1**| dynamic strings +tests | [utest](https://github.com/evolutional/utest) | MIT |C/C++|**1**| unit testing +tests | [catch](https://github.com/philsquared/Catch) | Boost | C++ |**1**| unit testing +tests | [SPUT](http://www.lingua-systems.com/unit-testing/) | BSD |C/C++|**1**| unit testing +tests | [pempek_assert.cpp](https://github.com/gpakosz/Assert) | **WTFPLv2** | C++ | 2 | flexible assertions +tests | [minctest](https://github.com/codeplea/minctest) | zlib | C |**1**| unit testing +tests | [greatest](https://github.com/silentbicycle/greatest) | iSC | C |**1**| unit testing +tests | [µnit](https://github.com/nemequ/munit) | MIT | C |**1**| unit testing +user interface | [dear imgui](https://github.com/ocornut/imgui) | MIT | C++*| 9 | an immediate-mode GUI formerly named "ImGui"; [3rd-party C wrapper](https://github.com/Extrawurst/cimgui) +_misc_ | [MakeID.h](http://www.humus.name/3D/MakeID.h) | **public domain** | C++ |**1**| allocate/deallocate small integer IDs efficiently +_misc_ | [loguru](https://github.com/emilk/loguru) | **public domain** | C++ |**1**| flexible logging +_misc_ | [tinyformat](https://github.com/c42f/tinyformat) | Boost | C++ |**1**| typesafe printf +_misc_ | [dbgtools](https://github.com/wc-duck/dbgtools) | zlib |C/C++| 2 | cross-platform debug util libraries +_misc_ | [stmr](https://github.com/wooorm/stmr.c) | MIT | C | 2 | extract English word stems +_misc_ | [levenshtein](https://github.com/wooorm/levenshtein.c) | MIT | C | 2 | compute edit distance between two strings + +There are also these XML libraries, but if you're using XML, shame on you: + +- parsing: [tinyxml2](https://github.com/leethomason/tinyxml2): XML +- parsing: [pugixml](http://pugixml.org/): XML (MIT license) + +Also you might be interested in other related, but different lists: + +- [clib](https://github.com/clibs/clib/wiki/Packages): list of (mostly) small single C functions (licenses not listed) + +## New libraries and corrections + +Submissions of new libraries: I accept submissions (as issues or as pull requests). Please +note that every file that must be included in a user's project counts; a header and a source +file is 2 files, but a header file, source file, and LICENSE (if the license isn't in the +source file) is 3 files, and won't be accepted, because it's not 2 files. But actually +'LICENSE' is a problem for just dropping the library in a source tree anyway, since it's +not scoped to just the library, so library authors are encouraged to include the license in the +source file and not require a separate LICENSE. + +Corrections: if information for a library above is wrong, please send a correction as an +issue, pull request, or email. Note that if the list indicates a library works from both +C/C++, but it doesn't, this could be an error in the list or it could be a bug in the +library. If you find a library doesn't work in 32-bit or 64-bit, the library should be +removed from this list, unless it's a bug in the library. + +## *List FAQ* + +### Can I link directly to this list? + +Yes, you can just use this page. If you want a shorter, more readable link, you can use [this URL](https://github.com/nothings/stb#other_libs) to link to the FAQ question that links to this page. + +### Why isn't library XXX which is made of 3 or more files on this list? + +I draw the line arbitrarily at 2 files at most. (Note that some libraries that appear to +be two files require a separate LICENSE file, which made me leave them out). Some of these +libraries are still easy to drop into your project and build, so you might still be ok with them. +But since people come to stb for single-file public domain libraries, I feel that starts +to get too far from what we do here. + +### Why isn't library XXX which is at most two files and has minimal other dependencies on this list? + +Probably because I don't know about it, feel free to submit a pull request, issue, email, or tweet it at +me (it can be your own library or somebody else's). But I might not include it for various +other reasons, including subtleties of what is 'minimal other dependencies' and subtleties +about what is 'lightweight'. + +### Why isn't SQLite's amalgamated build on this list? + +Come on. + diff --git a/impeller/third_party/stb/stb/docs/stb_howto.txt b/impeller/third_party/stb/stb/docs/stb_howto.txt new file mode 100644 index 0000000000000..a969b540225d2 --- /dev/null +++ b/impeller/third_party/stb/stb/docs/stb_howto.txt @@ -0,0 +1,185 @@ +Lessons learned about how to make a header-file library +V1.0 +September 2013 Sean Barrett + +Things to do in an stb-style header-file library, +and rationales: + + +1. #define LIBRARYNAME_IMPLEMENTATION + +Use a symbol like the above to control creating +the implementation. (I used a far-less-clear name +in my first header-file library; it became +clear that was a mistake once I had multiple +libraries.) + +Include a "header-file" section with header-file +guards and declarations for all the functions, +but only guard the implementation with LIBRARYNAME_IMPLEMENTATION, +not the header-file guard. That way, if client's +header file X includes your header file for +declarations, they can still include header file X +in the source file that creates the implementation; +if you guard the implementation too, then the first +include (before the #define) creates the declarations, +and the second one (after the #define) does nothing. + + +2. AVOID DEPENDENCIES + +Don't rely on anything other than the C standard libraries. + +(If you're creating a library specifically to leverage/wrap +some other library, then obviously you can rely on that +library. But if that library is public domain, you might +be better off directly embedding the source, to reduce +dependencies for your clients. But of course now you have +to update whenever that library updates.) + +If you use stdlib, consider wrapping all stdlib calls in +macros, and then conditionally define those macros to the +stdlib function, allowing the user to replace them. + +For functions with side effects, like memory allocations, +consider letting the user pass in a context and pass +that in to the macros. (The stdlib versions will ignore +the parameter.) Otherwise, users may have to use global +or thread-local variables to achieve the same effect. + + +3. AVOID MALLOC + +You can't always do this, but when you can, embedded developers +will appreciate it. I almost never bother avoiding, as it's +too much work (and in some cases is pretty infeasible; +see http://nothings.org/gamedev/font_rendering_malloc.txt ). +But it's definitely something one of the things I've gotten +the most pushback on from potential users. + + +4. ALLOW STATIC IMPLEMENTATION + +Have a #define which makes function declarations and +function definitions static. This makes the implementation +private to the source file that creates it. This allows +people to use your library multiple times in their project +without collision. (This is only necessary if your library +has configuration macros or global state, or if your +library has multiple versions that are not backwards +compatible. I've run into both of those cases.) + + +5. MAKE ACCESSIBLE FROM C + +Making your code accessible from C instead of C++ (i.e. +either coding in C, or using extern "C") makes it more +straightforward to be used in C and in other languages, +which often only have support for C bindings, not C++. +(One of the earliest results I found in googling for +stb_image was a Haskell wrapper.) Otherwise, people +have to wrap it in another set of function calls, and +the whole point here is to make it convenient for people +to use, isn't it? (See below.) + +I prefer to code entirely in C, so the source file that +instantiates the implementation can be C itself, for +those crazy people out there who are programming in C. +But it's probably not a big hardship for a C programmer +to create a single C++ source file to instantiate your +library. + + +6. NAMESPACE PRIVATE FUNCTIONS + +Try to avoid having names in your source code that +will cause conflicts with identical names in client +code. You can do this either by namespacing in C++, +or prefixing with your library name in C. + +In C, generally, I use the same prefix for API +functions and private symbols, such as "stbtt_" +for stb_truetype; but private functions (and +static globals) use a second underscore as +in "stbtt__" to further minimize the chance of +additional collisions in the unlikely but not +impossible event that users write wrapper +functions that have names of the form "stbtt_". +(Consider the user that has used "stbtt_foo" +*successfully*, and then upgrades to a new +version of your library which has a new private +function named either "stbtt_foo" or "stbtt__foo".) + +Note that the double-underscore is reserved for +use by the compiler, but (1) there is nothing +reserved for "middleware", i.e. libraries +desiring to avoid conflicts with user symbols +have no other good options, and (2) in practice +no compilers use double-underscore in the middle +rather than the beginning/end. (Unfortunately, +there is at least one videogame-console compiler that +will warn about double-underscores by default.) + + +7. EASY-TO-COMPLY LICENSE + +I make my libraries public domain. You don't have to. +But my goal in releasing stb-style libraries is to +reduce friction for potential users as much as +possible. That means: + + a. easy to build (what this file is mostly about) + b. easy to invoke (which requires good API design) + c. easy to deploy (which is about licensing) + +I choose to place all my libraries in the public +domain, abjuring copyright, rather than license +the libraries. This has some benefits and some +drawbacks. + +Any license which is "viral" to modifications +causes worries for lawyers, even if their programmers +aren't modifying it. + +Any license which requires crediting in documentation +adds friction which can add up. Valve used to have +a page with a list of all of these on their web site, +and it was insane, and obviously nobody ever looked +at it so why would you care whether your credit appeared +there? + +Permissive licenses like zlib and BSD license are +perfectly reasonable, but they are very wordy and +have only two benefits over public domain: legally-mandated +attribution and liability-control. I do not believe these +are worth the excessive verbosity and user-unfriendliness +these licenses induce, especially in the single-file +case where those licenses tend to be at the top of +the file, the first thing you see. (To the specific +points, I have had no trouble receiving attribution +for my libraries; liability in the face of no explicit +disclaimer of liability is an open question.) + +However, public domain has frictions of its own, because +public domain declarations aren't necessary recognized +in the USA and some other locations. For that reason, +I recommend a declaration along these lines: + +// This software is dual-licensed to the public domain and under the following +// license: you are granted a perpetual, irrevocable license to copy, modify, +// publish, and distribute this file as you see fit. + +I typically place this declaration at the end of the initial +comment block of the file and just say 'public domain' +at the top. + +I have had people say they couldn't use one of my +libraries because it was only "public domain" and didn't +have the additional fallback clause, who asked if +I could dual-license it under a traditional license. + +My answer: they can create a derivative work by +modifying one character, and then license that however +they like. (Indeed, *adding* the zlib or BSD license +would be such a modification!) Unfortunately, their +lawyers reportedly didn't like that answer. :( diff --git a/impeller/third_party/stb/stb/docs/stb_voxel_render_interview.md b/impeller/third_party/stb/stb/docs/stb_voxel_render_interview.md new file mode 100644 index 0000000000000..7071466e26f0c --- /dev/null +++ b/impeller/third_party/stb/stb/docs/stb_voxel_render_interview.md @@ -0,0 +1,173 @@ +# An interview with STB about stb_voxel_render.h + +**Q:** +I suppose you really like Minecraft? + +**A:** +Not really. I mean, I do own it and play it some, and +I do watch YouTube videos of other people playing it +once in a while, but I'm not saying it's that great. + +But I do love voxels. I've been playing with voxel rendering +since the mid-late 90's when we were still doing software +rendering and thinking maybe polygons weren't the answer. +Once GPUs came along that kind of died off, at least until +Minecraft brought it back to attention. + +**Q:** +Do you expect people will make a lot of Minecraft clones +with this? + +**A:** +I hope not! + +For one thing, it's a terrible idea for the +developer. Remember before Minecraft was on the Xbox 360, +there were a ton of "indie" clones (some maybe making +decent money even), but then the real Minecraft came out +and just crushed them (as far as I know). It's just not +something you really want to compete with. + +The reason I made this library is because I'd like +to see more games with Minecraft's *art style*, not +necessary its *gameplay*. + +I can understand the urge to clone the gameplay. When +you have a world made of voxels/blocks, there are a +few things that become incredibly easy to do that would +otherwise be very hard (at least for an indie) to do in 3D. +One thing is that procedural generation becomes much easier. +Another is that destructible environments are easy. Another +is that you have a world where your average user can build +stuff that they find satisfactory. + +Minecraft is at a sort of local maximum, a sweet spot, where +it leverages all of those easy-to-dos. And so I'm sure it's +hard to look at the space of 'games using voxels' and move +away from that local maximum, to give up some of that. +But I think that's what people should do. + +**Q:** +So what else can people do with stb_voxel_render? + +**A:** +All of those benefits I mentioned above are still valid even +if you stay away from the sweet spot. You can make a 3D roguelike +without player-creation/destruction that uses procedural generation. +You could make a shooter with pre-designed maps but destructible +environments. + +And I'm sure there are other possible benefits to using voxels/blocks. +Hopefully this will make it easier for people to explore the space. + +The library has a pretty wide range of features to allow +people to come up with some distinctive looks. For example, +the art style of Continue?9876543210 was one of the inspirations +for trying to make the multitexturing capabilities flexible. +I'm terrible at art, so this isn't really something I can +come up with myself, but I tried to put in flexible +technology that could be used multiple ways. + +One thing I did intentionally was try to make it possible to +make nicer looking ground terrain, using the half-height +slopes and "weird slopes". There are Minecraft mods with +drivable cars and they just go up these blocky slopes and, +like, what? So I wanted you to be able to make smoother +terrain, either just for the look, or for vehicles etc. +Also, you can spatially cross-fade between two ground textures for +that classic bad dirt/grass transition that has shipped +in plenty of professional games. Of course, you could +just use a separate non-voxel ground renderer for all of +this. But this way, you can seamlessly integrate everything +else with it. E.g. in your authoring tool (or procedural +generation) you can make smooth ground and then cut a +sharp-edged hole in it for a building's basement or whatever. + +Another thing you can do is work at a very different scale. +In Minecraft, a person is just under 2 blocks tall. In +Ace of Spades, a person is just under 3 blocks tall. Why +not 4 or 6? Well, partly because you just need a lot more +voxels; if a meter is 2 voxels in Mineraft and 4 voxels in +your game, and you draw the same number of voxels due to +hardware limits, then your game has half the view distance +of Minecraft. Since stb_voxel_render is designed to keep +the meshes small and render efficiently, you can push the +view distance out further than Minecraft--or use a similar +view distance and a higher voxel resolution. You could also +stop making infinite worlds and work at entirely different +scales; where Minecraft is 1 voxel per meter, you could +have 20 voxels per meter and make a small arena that's +50 meters wide and 5 meters tall. + +Back when the voxel game Voxatron was announced, the weekend +after the trailer came out I wrote my own little GPU-accelerated +version of the engine and thought that was pretty cool. I've +been tempted many times to extract that and release it +as a library, but +I don't want to steal Voxatron's thunder so I've avoided +it. You could use this engine to do the same kind of thing, +although it won't be as efficient as an engine dedicated to +that style of thing would be. + +**Q:** +What one thing would you really like to see somebody do? + +**A:** +Before Unity, 3D has seemed deeply problematic in the indie +space. Software like GameMaker has tried to support 3D but +it seems like little of note has been done with it. + +Minecraft has shown that people can build worlds with the +Minecraft toolset far more easily than we've ever seen from those +other tools. Obviously people have done great things with +Unity, but those people are much closer to professional +developers; typically they still need real 3D modelling +and all of that stuff. + +So what I'd really like to see is someone build some kind +of voxel-game-construction-set. Start with stb_voxel_render, +maybe expose all the flexibility of stb_voxel_render (so +people can do different things). Thrown in lua or something +else for scripting, make some kind of editor that feels +at least as good as Minecraft and Infinifactory, and see +where that gets you. + +**Q:** +Why'd you make this library? + +**A:** +Mainly as a way of releasing this technology I've been working +on since 2011 and seemed unlikely to ever ship myself. In 2011 +I was playing the voxel shooter Ace of Spades. One of the maps +that we played on was a partial port of Broville (which is the +first Minecraft map in stb_voxel_render release trailer). I'd +made a bunch of procedural level generators for the game, and +I started trying to make a city generator inspired by Broville. + +But I realized it would be a lot of work, and of very little +value (most of my maps didn't get much play because people +preferred to play on maps where they could charge straight +at the enemies and shoot them as fast as possible). So I +wrote my own voxel engine and started working on a procedural +city game. But I got bogged down after I finally got the road +generator working and never got anywhere with building +generation or gameplay. + +stb_voxel_render is actually a complete rewrite from scratch, +but it's based a lot on what I learned from that previous work. + +**Q:** +About the release video... how long did that take to edit? + +**A:** +About seven or eight hours. I had the first version done in +maybe six or seven hours, but then I realized I'd left out +one clip, and when I went back to add it I also gussied up +a couple other moments in the video. But there was something +basically identical to it that was done in around six. + +**Q:** +Ok, that's it. Thanks, me. + +**A:** +Thanks *me!* diff --git a/impeller/third_party/stb/stb/docs/why_public_domain.md b/impeller/third_party/stb/stb/docs/why_public_domain.md new file mode 100644 index 0000000000000..56cef3927ed1a --- /dev/null +++ b/impeller/third_party/stb/stb/docs/why_public_domain.md @@ -0,0 +1,116 @@ +My collected rationales for placing these libraries +in the public domain: + +1. Public domain vs. viral licenses + + Why is this library public domain? + Because more people will use it. Because it's not viral, people are + not obligated to give back, so you could argue that it hurts the + development of it, and then because it doesn't develop as well it's + not as good, and then because it's not as good, in the long run + maybe fewer people will use it. I have total respect for that + opinion, but I just don't believe it myself for most software. + +2. Public domain vs. attribution-required licenses + + The primary difference between public domain and, say, a Creative Commons + commercial / non-share-alike / attribution license is solely the + requirement for attribution. (Similarly the BSD license and such.) + While I would *appreciate* acknowledgement and attribution, I believe + that it is foolish to place a legal encumberment (i.e. a license) on + the software *solely* to get attribution. + + In other words, I'm arguing that PD is superior to the BSD license and + the Creative Commons 'Attribution' license. If the license offers + anything besides attribution -- as does, e.g., CC NonCommercial-ShareAlike, + or the GPL -- that's a separate discussion. + +3. Other aspects of BSD-style licenses besides attribution + + Permissive licenses like zlib and BSD license are perfectly reasonable + in their requirements, but they are very wordy and + have only two benefits over public domain: legally-mandated + attribution and liability-control. I do not believe these + are worth the excessive verbosity and user-unfriendliness + these licenses induce, especially in the single-file + case where those licenses tend to be at the top of + the file, the first thing you see. + + To the specific points, I have had no trouble receiving + attribution for my libraries; liability in the face of + no explicit disclaimer of liability is an open question, + but one I have a lot of difficulty imagining there being + any actual doubt about in court. Sometimes I explicitly + note in my libraries that I make no guarantees about them + being fit for purpose, but it's pretty absurd to do this; + as a whole, it comes across as "here is a library to decode + vorbis audio files, but it may not actually work and if + you have problems it's not my fault, but also please + report bugs so I can fix them"--so dumb! + +4. full discussion from stb_howto.txt on what YOU should do for YOUR libs + +``` +EASY-TO-COMPLY LICENSE + +I make my libraries public domain. You don't have to. +But my goal in releasing stb-style libraries is to +reduce friction for potential users as much as +possible. That means: + + a. easy to build (what this file is mostly about) + b. easy to invoke (which requires good API design) + c. easy to deploy (which is about licensing) + +I choose to place all my libraries in the public +domain, abjuring copyright, rather than license +the libraries. This has some benefits and some +drawbacks. + +Any license which is "viral" to modifications +causes worries for lawyers, even if their programmers +aren't modifying it. + +Any license which requires crediting in documentation +adds friction which can add up. Valve used to have +a page with a list of all of these on their web site, +and it was insane, and obviously nobody ever looked +at it so why would you care whether your credit appeared +there? + +Permissive licenses like zlib and BSD license are +perfectly reasonable, but they are very wordy and +have only two benefits over public domain: legally-mandated +attribution and liability-control. I do not believe these +are worth the excessive verbosity and user-unfriendliness +these licenses induce, especially in the single-file +case where those licenses tend to be at the top of +the file, the first thing you see. (To the specific +points, I have had no trouble receiving attribution +for my libraries; liability in the face of no explicit +disclaimer of liability is an open question.) + +However, public domain has frictions of its own, because +public domain declarations aren't necessary recognized +in the USA and some other locations. For that reason, +I recommend a declaration along these lines: + +// This software is dual-licensed to the public domain and under the following +// license: you are granted a perpetual, irrevocable license to copy, modify, +// publish, and distribute this file as you see fit. + +I typically place this declaration at the end of the initial +comment block of the file and just say 'public domain' +at the top. + +I have had people say they couldn't use one of my +libraries because it was only "public domain" and didn't +have the additional fallback clause, who asked if +I could dual-license it under a traditional license. + +My answer: they can create a derivative work by +modifying one character, and then license that however +they like. (Indeed, *adding* the zlib or BSD license +would be such a modification!) Unfortunately, their +lawyers reportedly didn't like that answer. :( +``` diff --git a/impeller/third_party/stb/stb/release_notes.md b/impeller/third_party/stb/stb/release_notes.md new file mode 100644 index 0000000000000..6712e9daf3ea4 --- /dev/null +++ b/impeller/third_party/stb/stb/release_notes.md @@ -0,0 +1,26 @@ +---- + +2016-04-02: + +- other_libs: cro_mipmap, greatest, munit, parg, dr_flac +- stb_image_write: allocate large structures on stack for embedded (Thatcher Ulrich) +- stb_image: allocate large structures on stack for embedded (Thatcher Ulrich) +- stb_image: remove white matting for transparent PSD (stb, Oriol Ferrer Mesia) +- stb_image: fix reported channel count in PNG when req_comp is non-zero +- stb_image: re-enable SSE2 in x64 (except in gcc) +- stb_image: fix harmless typo in name (Matthew Gregan) +- stb_image: support JPEG images coded as RGB +- stb_image: bmp could return wrong channel count (snagar@github) +- stb_image: read 16-bit PNGs as 8-bit (socks-the-fox) +- stb_image_resize: fix handling of subpixel regions +- stb_image_resize: avoid warnings on asserts (Wu Shuang) +- stb_truetype: allow fabs() to be supplied by user (Simon Glass) +- stb_truetype: duplicate typedef +- stb_truetype: don't leak memory if fontsize=0 +- stb_vorbis: warnings (Thiago Goulart) +- stb_vorbis: fix multiple memory leaks of setup-memory (manxorist@github) +- stb_vorbis: avoid dropping final frame of audio data +- stb_textedit: better support for keying while holding mouse drag button (ocornut) +- stb_voxel_render: fix type of glModelview matrix (Stephen Olsen) +- stb_leakcheck: typo in comment (Lukas Meller) +- stb.h: fix _WIN32 when defining STB_THREADS diff --git a/impeller/third_party/stb/stb/stb.h b/impeller/third_party/stb/stb/stb.h new file mode 100644 index 0000000000000..b985b13258fbb --- /dev/null +++ b/impeller/third_party/stb/stb/stb.h @@ -0,0 +1,14185 @@ +/* stb.h - v2.27 - Sean's Tool Box -- public domain -- http://nothings.org/stb.h + no warranty is offered or implied; use this code at your own risk + + This is a single header file with a bunch of useful utilities + for getting stuff done in C/C++. + + Documentation: http://nothings.org/stb/stb_h.html + Unit tests: http://nothings.org/stb/stb.c + + + ============================================================================ + You MUST + + #define STB_DEFINE + + in EXACTLY _one_ C or C++ file that includes this header, BEFORE the + include, like this: + + #define STB_DEFINE + #include "stb.h" + + All other files should just #include "stb.h" without the #define. + ============================================================================ + + +Version History + + 2.27 test _WIN32 not WIN32 in STB_THREADS + 2.26 various warning & bugfixes + 2.25 various warning & bugfixes + 2.24 various warning & bugfixes + 2.23 fix 2.22 + 2.22 64-bit fixes from '!='; fix stb_sdict_copy() to have preferred name + 2.21 utf-8 decoder rejects "overlong" encodings; attempted 64-bit improvements + 2.20 fix to hash "copy" function--reported by someone with handle "!=" + 2.19 ??? + 2.18 stb_readdir_subdirs_mask + 2.17 stb_cfg_dir + 2.16 fix stb_bgio_, add stb_bgio_stat(); begin a streaming wrapper + 2.15 upgraded hash table template to allow: + - aggregate keys (explicit comparison func for EMPTY and DEL keys) + - "static" implementations (so they can be culled if unused) + 2.14 stb_mprintf + 2.13 reduce identifiable strings in STB_NO_STB_STRINGS + 2.12 fix STB_ONLY -- lots of uint32s, TRUE/FALSE things had crept in + 2.11 fix bug in stb_dirtree_get() which caused "c://path" sorts of stuff + 2.10 STB_F(), STB_I() inline constants (also KI,KU,KF,KD) + 2.09 stb_box_face_vertex_axis_side + 2.08 bugfix stb_trimwhite() + 2.07 colored printing in windows (why are we in 1985?) + 2.06 comparison functions are now functions-that-return-functions and + accept a struct-offset as a parameter (not thread-safe) + 2.05 compile and pass tests under Linux (but no threads); thread cleanup + 2.04 stb_cubic_bezier_1d, smoothstep, avoid dependency on registry + 2.03 ? + 2.02 remove integrated documentation + 2.01 integrate various fixes; stb_force_uniprocessor + 2.00 revised stb_dupe to use multiple hashes + 1.99 stb_charcmp + 1.98 stb_arr_deleten, stb_arr_insertn + 1.97 fix stb_newell_normal() + 1.96 stb_hash_number() + 1.95 hack stb__rec_max; clean up recursion code to use new functions + 1.94 stb_dirtree; rename stb_extra to stb_ptrmap + 1.93 stb_sem_new() API cleanup (no blockflag-starts blocked; use 'extra') + 1.92 stb_threadqueue--multi reader/writer queue, fixed size or resizeable + 1.91 stb_bgio_* for reading disk asynchronously + 1.90 stb_mutex uses CRITICAL_REGION; new stb_sync primitive for thread + joining; workqueue supports stb_sync instead of stb_semaphore + 1.89 support ';' in constant-string wildcards; stb_mutex wrapper (can + implement with EnterCriticalRegion eventually) + 1.88 portable threading API (only for win32 so far); worker thread queue + 1.87 fix wildcard handling in stb_readdir_recursive + 1.86 support ';' in wildcards + 1.85 make stb_regex work with non-constant strings; + beginnings of stb_introspect() + 1.84 (forgot to make notes) + 1.83 whoops, stb_keep_if_different wasn't deleting the temp file + 1.82 bring back stb_compress from stb_file.h for cmirror + 1.81 various bugfixes, STB_FASTMALLOC_INIT inits FASTMALLOC in release + 1.80 stb_readdir returns utf8; write own utf8-utf16 because lib was wrong + 1.79 stb_write + 1.78 calloc() support for malloc wrapper, STB_FASTMALLOC + 1.77 STB_FASTMALLOC + 1.76 STB_STUA - Lua-like language; (stb_image, stb_csample, stb_bilinear) + 1.75 alloc/free array of blocks; stb_hheap bug; a few stb_ps_ funcs; + hash*getkey, hash*copy; stb_bitset; stb_strnicmp; bugfix stb_bst + 1.74 stb_replaceinplace; use stdlib C function to convert utf8 to UTF-16 + 1.73 fix performance bug & leak in stb_ischar (C++ port lost a 'static') + 1.72 remove stb_block, stb_block_manager, stb_decompress (to stb_file.h) + 1.71 stb_trimwhite, stb_tokens_nested, etc. + 1.70 back out 1.69 because it might problemize mixed builds; stb_filec() + 1.69 (stb_file returns 'char *' in C++) + 1.68 add a special 'tree root' data type for stb_bst; stb_arr_end + 1.67 full C++ port. (stb_block_manager) + 1.66 stb_newell_normal + 1.65 stb_lex_item_wild -- allow wildcard items which MUST match entirely + 1.64 stb_data + 1.63 stb_log_name + 1.62 stb_define_sort; C++ cleanup + 1.61 stb_hash_fast -- Paul Hsieh's hash function (beats Bob Jenkins'?) + 1.60 stb_delete_directory_recursive + 1.59 stb_readdir_recursive + 1.58 stb_bst variant with parent pointer for O(1) iteration, not O(log N) + 1.57 replace LCG random with Mersenne Twister (found a public domain one) + 1.56 stb_perfect_hash, stb_ischar, stb_regex + 1.55 new stb_bst API allows multiple BSTs per node (e.g. secondary keys) + 1.54 bugfix: stb_define_hash, stb_wildmatch, regexp + 1.53 stb_define_hash; recoded stb_extra, stb_sdict use it + 1.52 stb_rand_define, stb_bst, stb_reverse + 1.51 fix 'stb_arr_setlen(NULL, 0)' + 1.50 stb_wordwrap + 1.49 minor improvements to enable the scripting language + 1.48 better approach for stb_arr using stb_malloc; more invasive, clearer + 1.47 stb_lex (lexes stb.h at 1.5ML/s on 3Ghz P4; 60/70% of optimal/flex) + 1.46 stb_wrapper_*, STB_MALLOC_WRAPPER + 1.45 lightly tested DFA acceleration of regexp searching + 1.44 wildcard matching & searching; regexp matching & searching + 1.43 stb_temp + 1.42 allow stb_arr to use stb_malloc/realloc; note this is global + 1.41 make it compile in C++; (disable stb_arr in C++) + 1.40 stb_dupe tweak; stb_swap; stb_substr + 1.39 stb_dupe; improve stb_file_max to be less stupid + 1.38 stb_sha1_file: generate sha1 for file, even > 4GB + 1.37 stb_file_max; partial support for utf8 filenames in Windows + 1.36 remove STB__NO_PREFIX - poor interaction with IDE, not worth it + streamline stb_arr to make it separately publishable + 1.35 bugfixes for stb_sdict, stb_malloc(0), stristr + 1.34 (streaming interfaces for stb_compress) + 1.33 stb_alloc; bug in stb_getopt; remove stb_overflow + 1.32 (stb_compress returns, smaller&faster; encode window & 64-bit len) + 1.31 stb_prefix_count + 1.30 (STB__NO_PREFIX - remove stb_ prefixes for personal projects) + 1.29 stb_fput_varlen64, etc. + 1.28 stb_sha1 + 1.27 ? + 1.26 stb_extra + 1.25 ? + 1.24 stb_copyfile + 1.23 stb_readdir + 1.22 ? + 1.21 ? + 1.20 ? + 1.19 ? + 1.18 ? + 1.17 ? + 1.16 ? + 1.15 stb_fixpath, stb_splitpath, stb_strchr2 + 1.14 stb_arr + 1.13 ?stb, stb_log, stb_fatal + 1.12 ?stb_hash2 + 1.11 miniML + 1.10 stb_crc32, stb_adler32 + 1.09 stb_sdict + 1.08 stb_bitreverse, stb_ispow2, stb_big32 + stb_fopen, stb_fput_varlen, stb_fput_ranged + stb_fcmp, stb_feq + 1.07 (stb_encompress) + 1.06 stb_compress + 1.05 stb_tokens, (stb_hheap) + 1.04 stb_rand + 1.03 ?(s-strings) + 1.02 ?stb_filelen, stb_tokens + 1.01 stb_tolower + 1.00 stb_hash, stb_intcmp + stb_file, stb_stringfile, stb_fgets + stb_prefix, stb_strlower, stb_strtok + stb_image + (stb_array), (stb_arena) + +Parenthesized items have since been removed. + +LICENSE + +This software is dual-licensed to the public domain and under the following +license: you are granted a perpetual, irrevocable license to copy, modify, +publish, and distribute this file as you see fit. + +CREDITS + + Written by Sean Barrett. + + Fixes: + Philipp Wiesemann + Robert Nix + r-lyeh + blackpawn + Mojofreem@github + Ryan Whitworth + Vincent Isambart + Mike Sartain + Eugene Opalev + Tim Sjostrand +*/ + +#ifndef STB__INCLUDE_STB_H +#define STB__INCLUDE_STB_H + +#define STB_VERSION 1 + +#ifdef STB_INTROSPECT + #define STB_DEFINE +#endif + +#ifdef STB_DEFINE_THREADS + #ifndef STB_DEFINE + #define STB_DEFINE + #endif + #ifndef STB_THREADS + #define STB_THREADS + #endif +#endif + +#if defined(_WIN32) && !defined(__MINGW32__) + #ifndef _CRT_SECURE_NO_WARNINGS + #define _CRT_SECURE_NO_WARNINGS + #endif + #ifndef _CRT_NONSTDC_NO_DEPRECATE + #define _CRT_NONSTDC_NO_DEPRECATE + #endif + #ifndef _CRT_NON_CONFORMING_SWPRINTFS + #define _CRT_NON_CONFORMING_SWPRINTFS + #endif + #if !defined(_MSC_VER) || _MSC_VER > 1700 + #include // _BitScanReverse + #endif +#endif + +#include // stdlib could have min/max +#include // need FILE +#include // stb_define_hash needs memcpy/memset +#include // stb_dirtree +#ifdef __MINGW32__ + #include // O_RDWR +#endif + +#ifdef STB_PERSONAL + typedef int Bool; + #define False 0 + #define True 1 +#endif + +#ifdef STB_MALLOC_WRAPPER_PAGED + #define STB_MALLOC_WRAPPER_DEBUG +#endif +#ifdef STB_MALLOC_WRAPPER_DEBUG + #define STB_MALLOC_WRAPPER +#endif +#ifdef STB_MALLOC_WRAPPER_FASTMALLOC + #define STB_FASTMALLOC + #define STB_MALLOC_WRAPPER +#endif + +#ifdef STB_FASTMALLOC + #ifndef _WIN32 + #undef STB_FASTMALLOC + #endif +#endif + +#ifdef STB_DEFINE + #include + #include + #include + #include + #include + #ifndef _WIN32 + #include + #else + #include // _mktemp + #include // _rmdir + #endif + #include // stat()/_stat() + #include // stat()/_stat() +#endif + +#define stb_min(a,b) ((a) < (b) ? (a) : (b)) +#define stb_max(a,b) ((a) > (b) ? (a) : (b)) + +#ifndef STB_ONLY + #if !defined(__cplusplus) && !defined(min) && !defined(max) + #define min(x,y) stb_min(x,y) + #define max(x,y) stb_max(x,y) + #endif + + #ifndef M_PI + #define M_PI 3.14159265358979323846f + #endif + + #ifndef TRUE + #define TRUE 1 + #define FALSE 0 + #endif + + #ifndef deg2rad + #define deg2rad(a) ((a)*(M_PI/180)) + #endif + #ifndef rad2deg + #define rad2deg(a) ((a)*(180/M_PI)) + #endif + + #ifndef swap + #ifndef __cplusplus + #define swap(TYPE,a,b) \ + do { TYPE stb__t; stb__t = (a); (a) = (b); (b) = stb__t; } while (0) + #endif + #endif + + typedef unsigned char uint8 ; + typedef signed char int8 ; + typedef unsigned short uint16; + typedef signed short int16; + #if defined(STB_USE_LONG_FOR_32_BIT_INT) || defined(STB_LONG32) + typedef unsigned long uint32; + typedef signed long int32; + #else + typedef unsigned int uint32; + typedef signed int int32; + #endif + + typedef unsigned char uchar ; + typedef unsigned short ushort; + typedef unsigned int uint ; + typedef unsigned long ulong ; + + // produce compile errors if the sizes aren't right + typedef char stb__testsize16[sizeof(int16)==2]; + typedef char stb__testsize32[sizeof(int32)==4]; +#endif + +#ifndef STB_TRUE + #define STB_TRUE 1 + #define STB_FALSE 0 +#endif + +// if we're STB_ONLY, can't rely on uint32 or even uint, so all the +// variables we'll use herein need typenames prefixed with 'stb': +typedef unsigned char stb_uchar; +typedef unsigned char stb_uint8; +typedef unsigned int stb_uint; +typedef unsigned short stb_uint16; +typedef short stb_int16; +typedef signed char stb_int8; +#if defined(STB_USE_LONG_FOR_32_BIT_INT) || defined(STB_LONG32) + typedef unsigned long stb_uint32; + typedef long stb_int32; +#else + typedef unsigned int stb_uint32; + typedef int stb_int32; +#endif +typedef char stb__testsize2_16[sizeof(stb_uint16)==2 ? 1 : -1]; +typedef char stb__testsize2_32[sizeof(stb_uint32)==4 ? 1 : -1]; + +#ifdef _MSC_VER + typedef unsigned __int64 stb_uint64; + typedef __int64 stb_int64; + #define STB_IMM_UINT64(literalui64) (literalui64##ui64) + #define STB_IMM_INT64(literali64) (literali64##i64) +#else + // ?? + typedef unsigned long long stb_uint64; + typedef long long stb_int64; + #define STB_IMM_UINT64(literalui64) (literalui64##ULL) + #define STB_IMM_INT64(literali64) (literali64##LL) +#endif +typedef char stb__testsize2_64[sizeof(stb_uint64)==8 ? 1 : -1]; + +// add platform-specific ways of checking for sizeof(char*) == 8, +// and make those define STB_PTR64 +#if defined(_WIN64) || defined(__x86_64__) || defined(__ia64__) || defined(__LP64__) + #define STB_PTR64 +#endif + +#ifdef STB_PTR64 +typedef char stb__testsize2_ptr[sizeof(char *) == 8]; +typedef stb_uint64 stb_uinta; +typedef stb_int64 stb_inta; +#else +typedef char stb__testsize2_ptr[sizeof(char *) == 4]; +typedef stb_uint32 stb_uinta; +typedef stb_int32 stb_inta; +#endif +typedef char stb__testsize2_uinta[sizeof(stb_uinta)==sizeof(char*) ? 1 : -1]; + +// if so, we should define an int type that is the pointer size. until then, +// we'll have to make do with this (which is not the same at all!) + +typedef union +{ + unsigned int i; + void * p; +} stb_uintptr; + + +#ifdef __cplusplus + #define STB_EXTERN extern "C" +#else + #define STB_EXTERN extern +#endif + +// check for well-known debug defines +#if defined(DEBUG) || defined(_DEBUG) || defined(DBG) + #ifndef NDEBUG + #define STB_DEBUG + #endif +#endif + +#ifdef STB_DEBUG + #include +#endif + + +STB_EXTERN void stb_wrapper_malloc(void *newp, int sz, char *file, int line); +STB_EXTERN void stb_wrapper_free(void *oldp, char *file, int line); +STB_EXTERN void stb_wrapper_realloc(void *oldp, void *newp, int sz, char *file, int line); +STB_EXTERN void stb_wrapper_calloc(size_t num, size_t sz, char *file, int line); +STB_EXTERN void stb_wrapper_listall(void (*func)(void *ptr, int sz, char *file, int line)); +STB_EXTERN void stb_wrapper_dump(char *filename); +STB_EXTERN int stb_wrapper_allocsize(void *oldp); +STB_EXTERN void stb_wrapper_check(void *oldp); + +#ifdef STB_DEFINE +// this is a special function used inside malloc wrapper +// to do allocations that aren't tracked (to avoid +// reentrancy). Of course if someone _else_ wraps realloc, +// this breaks, but if they're doing that AND the malloc +// wrapper they need to explicitly check for reentrancy. +// +// only define realloc_raw() and we do realloc(NULL,sz) +// for malloc() and realloc(p,0) for free(). +static void * stb__realloc_raw(void *p, int sz) +{ + if (p == NULL) return malloc(sz); + if (sz == 0) { free(p); return NULL; } + return realloc(p,sz); +} +#endif + +#ifdef _WIN32 +STB_EXTERN void * stb_smalloc(size_t sz); +STB_EXTERN void stb_sfree(void *p); +STB_EXTERN void * stb_srealloc(void *p, size_t sz); +STB_EXTERN void * stb_scalloc(size_t n, size_t sz); +STB_EXTERN char * stb_sstrdup(char *s); +#endif + +#ifdef STB_FASTMALLOC +#define malloc stb_smalloc +#define free stb_sfree +#define realloc stb_srealloc +#define strdup stb_sstrdup +#define calloc stb_scalloc +#endif + +#ifndef STB_MALLOC_ALLCHECK + #define stb__check(p) 1 +#else + #ifndef STB_MALLOC_WRAPPER + #error STB_MALLOC_ALLCHECK requires STB_MALLOC_WRAPPER + #else + #define stb__check(p) stb_mcheck(p) + #endif +#endif + +#ifdef STB_MALLOC_WRAPPER + STB_EXTERN void * stb__malloc(int, char *, int); + STB_EXTERN void * stb__realloc(void *, int, char *, int); + STB_EXTERN void * stb__calloc(size_t n, size_t s, char *, int); + STB_EXTERN void stb__free(void *, char *file, int); + STB_EXTERN char * stb__strdup(char *s, char *file, int); + STB_EXTERN void stb_malloc_checkall(void); + STB_EXTERN void stb_malloc_check_counter(int init_delay, int rep_delay); + #ifndef STB_MALLOC_WRAPPER_DEBUG + #define stb_mcheck(p) 1 + #else + STB_EXTERN int stb_mcheck(void *); + #endif + + + #ifdef STB_DEFINE + + #ifdef STB_MALLOC_WRAPPER_DEBUG + #define STB__PAD 32 + #define STB__BIAS 16 + #define STB__SIG 0x51b01234 + #define STB__FIXSIZE(sz) (((sz+3) & ~3) + STB__PAD) + #define STB__ptr(x,y) ((char *) (x) + (y)) + #else + #define STB__ptr(x,y) (x) + #define STB__FIXSIZE(sz) (sz) + #endif + + #ifdef STB_MALLOC_WRAPPER_DEBUG + int stb_mcheck(void *p) + { + unsigned int sz; + if (p == NULL) return 1; + p = ((char *) p) - STB__BIAS; + sz = * (unsigned int *) p; + assert(* (unsigned int *) STB__ptr(p,4) == STB__SIG); + assert(* (unsigned int *) STB__ptr(p,8) == STB__SIG); + assert(* (unsigned int *) STB__ptr(p,12) == STB__SIG); + assert(* (unsigned int *) STB__ptr(p,sz-4) == STB__SIG+1); + assert(* (unsigned int *) STB__ptr(p,sz-8) == STB__SIG+1); + assert(* (unsigned int *) STB__ptr(p,sz-12) == STB__SIG+1); + assert(* (unsigned int *) STB__ptr(p,sz-16) == STB__SIG+1); + stb_wrapper_check(STB__ptr(p, STB__BIAS)); + return 1; + } + + static void stb__check2(void *p, int sz, char *file, int line) + { + stb_mcheck(p); + } + + void stb_malloc_checkall(void) + { + stb_wrapper_listall(stb__check2); + } + #else + void stb_malloc_checkall(void) { } + #endif + + static int stb__malloc_wait=(1 << 30), stb__malloc_next_wait = (1 << 30), stb__malloc_iter; + void stb_malloc_check_counter(int init_delay, int rep_delay) + { + stb__malloc_wait = init_delay; + stb__malloc_next_wait = rep_delay; + } + + void stb_mcheck_all(void) + { + #ifdef STB_MALLOC_WRAPPER_DEBUG + ++stb__malloc_iter; + if (--stb__malloc_wait <= 0) { + stb_malloc_checkall(); + stb__malloc_wait = stb__malloc_next_wait; + } + #endif + } + + #ifdef STB_MALLOC_WRAPPER_PAGED + #define STB__WINDOWS_PAGE (1 << 12) + #ifndef _WINDOWS_ + STB_EXTERN __declspec(dllimport) void * __stdcall VirtualAlloc(void *p, unsigned long size, unsigned long type, unsigned long protect); + STB_EXTERN __declspec(dllimport) int __stdcall VirtualFree(void *p, unsigned long size, unsigned long freetype); + #endif + #endif + + static void *stb__malloc_final(int sz) + { + #ifdef STB_MALLOC_WRAPPER_PAGED + int aligned = (sz + STB__WINDOWS_PAGE - 1) & ~(STB__WINDOWS_PAGE-1); + char *p = VirtualAlloc(NULL, aligned + STB__WINDOWS_PAGE, 0x2000, 0x04); // RESERVE, READWRITE + if (p == NULL) return p; + VirtualAlloc(p, aligned, 0x1000, 0x04); // COMMIT, READWRITE + return p; + #else + return malloc(sz); + #endif + } + + static void stb__free_final(void *p) + { + #ifdef STB_MALLOC_WRAPPER_PAGED + VirtualFree(p, 0, 0x8000); // RELEASE + #else + free(p); + #endif + } + + int stb__malloc_failure; + static void *stb__realloc_final(void *p, int sz, int old_sz) + { + #ifdef STB_MALLOC_WRAPPER_PAGED + void *q = stb__malloc_final(sz); + if (q == NULL) + return ++stb__malloc_failure, q; + // @TODO: deal with p being smaller! + memcpy(q, p, sz < old_sz ? sz : old_sz); + stb__free_final(p); + return q; + #else + return realloc(p,sz); + #endif + } + + void stb__free(void *p, char *file, int line) + { + stb_mcheck_all(); + if (!p) return; + #ifdef STB_MALLOC_WRAPPER_DEBUG + stb_mcheck(p); + #endif + stb_wrapper_free(p,file,line); + #ifdef STB_MALLOC_WRAPPER_DEBUG + p = STB__ptr(p,-STB__BIAS); + * (unsigned int *) STB__ptr(p,0) = 0xdeadbeef; + * (unsigned int *) STB__ptr(p,4) = 0xdeadbeef; + * (unsigned int *) STB__ptr(p,8) = 0xdeadbeef; + * (unsigned int *) STB__ptr(p,12) = 0xdeadbeef; + #endif + stb__free_final(p); + } + + void * stb__malloc(int sz, char *file, int line) + { + void *p; + stb_mcheck_all(); + if (sz == 0) return NULL; + p = stb__malloc_final(STB__FIXSIZE(sz)); + if (p == NULL) p = stb__malloc_final(STB__FIXSIZE(sz)); + if (p == NULL) p = stb__malloc_final(STB__FIXSIZE(sz)); + if (p == NULL) { + ++stb__malloc_failure; + #ifdef STB_MALLOC_WRAPPER_DEBUG + stb_malloc_checkall(); + #endif + return p; + } + #ifdef STB_MALLOC_WRAPPER_DEBUG + * (int *) STB__ptr(p,0) = STB__FIXSIZE(sz); + * (unsigned int *) STB__ptr(p,4) = STB__SIG; + * (unsigned int *) STB__ptr(p,8) = STB__SIG; + * (unsigned int *) STB__ptr(p,12) = STB__SIG; + * (unsigned int *) STB__ptr(p,STB__FIXSIZE(sz)-4) = STB__SIG+1; + * (unsigned int *) STB__ptr(p,STB__FIXSIZE(sz)-8) = STB__SIG+1; + * (unsigned int *) STB__ptr(p,STB__FIXSIZE(sz)-12) = STB__SIG+1; + * (unsigned int *) STB__ptr(p,STB__FIXSIZE(sz)-16) = STB__SIG+1; + p = STB__ptr(p, STB__BIAS); + #endif + stb_wrapper_malloc(p,sz,file,line); + return p; + } + + void * stb__realloc(void *p, int sz, char *file, int line) + { + void *q; + + stb_mcheck_all(); + if (p == NULL) return stb__malloc(sz,file,line); + if (sz == 0 ) { stb__free(p,file,line); return NULL; } + + #ifdef STB_MALLOC_WRAPPER_DEBUG + stb_mcheck(p); + p = STB__ptr(p,-STB__BIAS); + #endif + #ifdef STB_MALLOC_WRAPPER_PAGED + { + int n = stb_wrapper_allocsize(STB__ptr(p,STB__BIAS)); + if (!n) + stb_wrapper_check(STB__ptr(p,STB__BIAS)); + q = stb__realloc_final(p, STB__FIXSIZE(sz), STB__FIXSIZE(n)); + } + #else + q = realloc(p, STB__FIXSIZE(sz)); + #endif + if (q == NULL) + return ++stb__malloc_failure, q; + #ifdef STB_MALLOC_WRAPPER_DEBUG + * (int *) STB__ptr(q,0) = STB__FIXSIZE(sz); + * (unsigned int *) STB__ptr(q,4) = STB__SIG; + * (unsigned int *) STB__ptr(q,8) = STB__SIG; + * (unsigned int *) STB__ptr(q,12) = STB__SIG; + * (unsigned int *) STB__ptr(q,STB__FIXSIZE(sz)-4) = STB__SIG+1; + * (unsigned int *) STB__ptr(q,STB__FIXSIZE(sz)-8) = STB__SIG+1; + * (unsigned int *) STB__ptr(q,STB__FIXSIZE(sz)-12) = STB__SIG+1; + * (unsigned int *) STB__ptr(q,STB__FIXSIZE(sz)-16) = STB__SIG+1; + + q = STB__ptr(q, STB__BIAS); + p = STB__ptr(p, STB__BIAS); + #endif + stb_wrapper_realloc(p,q,sz,file,line); + return q; + } + + STB_EXTERN int stb_log2_ceil(unsigned int); + static void *stb__calloc(size_t n, size_t sz, char *file, int line) + { + void *q; + stb_mcheck_all(); + if (n == 0 || sz == 0) return NULL; + if (stb_log2_ceil(n) + stb_log2_ceil(sz) >= 32) return NULL; + q = stb__malloc(n*sz, file, line); + if (q) memset(q, 0, n*sz); + return q; + } + + char * stb__strdup(char *s, char *file, int line) + { + char *p; + stb_mcheck_all(); + p = stb__malloc(strlen(s)+1, file, line); + if (!p) return p; + strcpy(p, s); + return p; + } + #endif // STB_DEFINE + + #ifdef STB_FASTMALLOC + #undef malloc + #undef realloc + #undef free + #undef strdup + #undef calloc + #endif + + // include everything that might define these, BEFORE making macros + #include + #include + #include + + #define malloc(s) stb__malloc ( s, __FILE__, __LINE__) + #define realloc(p,s) stb__realloc(p,s, __FILE__, __LINE__) + #define calloc(n,s) stb__calloc (n,s, __FILE__, __LINE__) + #define free(p) stb__free (p, __FILE__, __LINE__) + #define strdup(p) stb__strdup (p, __FILE__, __LINE__) + +#endif + +////////////////////////////////////////////////////////////////////////////// +// +// Windows pretty display +// + +STB_EXTERN void stbprint(const char *fmt, ...); +STB_EXTERN char *stb_sprintf(const char *fmt, ...); +STB_EXTERN char *stb_mprintf(const char *fmt, ...); +STB_EXTERN int stb_snprintf(char *s, size_t n, const char *fmt, ...); +STB_EXTERN int stb_vsnprintf(char *s, size_t n, const char *fmt, va_list v); + +#ifdef STB_DEFINE +int stb_vsnprintf(char *s, size_t n, const char *fmt, va_list v) +{ + int res; + #ifdef _WIN32 + // Could use "_vsnprintf_s(s, n, _TRUNCATE, fmt, v)" ? + res = _vsnprintf(s,n,fmt,v); + #else + res = vsnprintf(s,n,fmt,v); + #endif + if (n) s[n-1] = 0; + // Unix returns length output would require, Windows returns negative when truncated. + return (res >= (int) n || res < 0) ? -1 : res; +} + +int stb_snprintf(char *s, size_t n, const char *fmt, ...) +{ + int res; + va_list v; + va_start(v,fmt); + res = stb_vsnprintf(s, n, fmt, v); + va_end(v); + return res; +} + +char *stb_sprintf(const char *fmt, ...) +{ + static char buffer[1024]; + va_list v; + va_start(v,fmt); + stb_vsnprintf(buffer,1024,fmt,v); + va_end(v); + return buffer; +} + +char *stb_mprintf(const char *fmt, ...) +{ + static char buffer[1024]; + va_list v; + va_start(v,fmt); + stb_vsnprintf(buffer,1024,fmt,v); + va_end(v); + return strdup(buffer); +} + +#ifdef _WIN32 + +#ifndef _WINDOWS_ +STB_EXTERN __declspec(dllimport) int __stdcall WriteConsoleA(void *, const void *, unsigned int, unsigned int *, void *); +STB_EXTERN __declspec(dllimport) void * __stdcall GetStdHandle(unsigned int); +STB_EXTERN __declspec(dllimport) int __stdcall SetConsoleTextAttribute(void *, unsigned short); +#endif + +static void stb__print_one(void *handle, char *s, int len) +{ + if (len) + if (WriteConsoleA(handle, s, len, NULL,NULL)) + fwrite(s, 1, len, stdout); // if it fails, maybe redirected, so do normal +} + +static void stb__print(char *s) +{ + void *handle = GetStdHandle((unsigned int) -11); // STD_OUTPUT_HANDLE + int pad=0; // number of padding characters to add + + char *t = s; + while (*s) { + int lpad; + while (*s && *s != '{') { + if (pad) { + if (*s == '\r' || *s == '\n') + pad = 0; + else if (s[0] == ' ' && s[1] == ' ') { + stb__print_one(handle, t, s-t); + t = s; + while (pad) { + stb__print_one(handle, t, 1); + --pad; + } + } + } + ++s; + } + if (!*s) break; + stb__print_one(handle, t, s-t); + if (s[1] == '{') { + ++s; + continue; + } + + if (s[1] == '#') { + t = s+3; + if (isxdigit(s[2])) + if (isdigit(s[2])) + SetConsoleTextAttribute(handle, s[2] - '0'); + else + SetConsoleTextAttribute(handle, tolower(s[2]) - 'a' + 10); + else { + SetConsoleTextAttribute(handle, 0x0f); + t=s+2; + } + } else if (s[1] == '!') { + SetConsoleTextAttribute(handle, 0x0c); + t = s+2; + } else if (s[1] == '@') { + SetConsoleTextAttribute(handle, 0x09); + t = s+2; + } else if (s[1] == '$') { + SetConsoleTextAttribute(handle, 0x0a); + t = s+2; + } else { + SetConsoleTextAttribute(handle, 0x08); // 0,7,8,15 => shades of grey + t = s+1; + } + + lpad = (t-s); + s = t; + while (*s && *s != '}') ++s; + if (!*s) break; + stb__print_one(handle, t, s-t); + if (s[1] == '}') { + t = s+2; + } else { + pad += 1+lpad; + t = s+1; + } + s=t; + SetConsoleTextAttribute(handle, 0x07); + } + stb__print_one(handle, t, s-t); + SetConsoleTextAttribute(handle, 0x07); +} + +void stbprint(const char *fmt, ...) +{ + int res; + char buffer[1024]; + char *tbuf = buffer; + va_list v; + + va_start(v,fmt); + res = stb_vsnprintf(buffer, sizeof(buffer), fmt, v); + va_end(v); + + if (res < 0) { + tbuf = (char *) malloc(16384); + va_start(v,fmt); + res = _vsnprintf(tbuf,16384, fmt, v); + va_end(v); + tbuf[16383] = 0; + } + + stb__print(tbuf); + + if (tbuf != buffer) + free(tbuf); +} + +#else // _WIN32 +void stbprint(const char *fmt, ...) +{ + va_list v; + va_start(v,fmt); + vprintf(fmt,v); + va_end(v); +} +#endif // _WIN32 +#endif // STB_DEFINE + + + +////////////////////////////////////////////////////////////////////////////// +// +// Windows UTF8 filename handling +// +// Windows stupidly treats 8-bit filenames as some dopey code page, +// rather than utf-8. If we want to use utf8 filenames, we have to +// convert them to WCHAR explicitly and call WCHAR versions of the +// file functions. So, ok, we do. + + +#ifdef _WIN32 + #define stb__fopen(x,y) _wfopen((const wchar_t *)stb__from_utf8(x), (const wchar_t *)stb__from_utf8_alt(y)) + #define stb__windows(x,y) x +#else + #define stb__fopen(x,y) fopen(x,y) + #define stb__windows(x,y) y +#endif + + +typedef unsigned short stb__wchar; + +STB_EXTERN stb__wchar * stb_from_utf8(stb__wchar *buffer, char *str, int n); +STB_EXTERN char * stb_to_utf8 (char *buffer, stb__wchar *str, int n); + +STB_EXTERN stb__wchar *stb__from_utf8(char *str); +STB_EXTERN stb__wchar *stb__from_utf8_alt(char *str); +STB_EXTERN char *stb__to_utf8(stb__wchar *str); + + +#ifdef STB_DEFINE +stb__wchar * stb_from_utf8(stb__wchar *buffer, char *ostr, int n) +{ + unsigned char *str = (unsigned char *) ostr; + stb_uint32 c; + int i=0; + --n; + while (*str) { + if (i >= n) + return NULL; + if (!(*str & 0x80)) + buffer[i++] = *str++; + else if ((*str & 0xe0) == 0xc0) { + if (*str < 0xc2) return NULL; + c = (*str++ & 0x1f) << 6; + if ((*str & 0xc0) != 0x80) return NULL; + buffer[i++] = c + (*str++ & 0x3f); + } else if ((*str & 0xf0) == 0xe0) { + if (*str == 0xe0 && (str[1] < 0xa0 || str[1] > 0xbf)) return NULL; + if (*str == 0xed && str[1] > 0x9f) return NULL; // str[1] < 0x80 is checked below + c = (*str++ & 0x0f) << 12; + if ((*str & 0xc0) != 0x80) return NULL; + c += (*str++ & 0x3f) << 6; + if ((*str & 0xc0) != 0x80) return NULL; + buffer[i++] = c + (*str++ & 0x3f); + } else if ((*str & 0xf8) == 0xf0) { + if (*str > 0xf4) return NULL; + if (*str == 0xf0 && (str[1] < 0x90 || str[1] > 0xbf)) return NULL; + if (*str == 0xf4 && str[1] > 0x8f) return NULL; // str[1] < 0x80 is checked below + c = (*str++ & 0x07) << 18; + if ((*str & 0xc0) != 0x80) return NULL; + c += (*str++ & 0x3f) << 12; + if ((*str & 0xc0) != 0x80) return NULL; + c += (*str++ & 0x3f) << 6; + if ((*str & 0xc0) != 0x80) return NULL; + c += (*str++ & 0x3f); + // utf-8 encodings of values used in surrogate pairs are invalid + if ((c & 0xFFFFF800) == 0xD800) return NULL; + if (c >= 0x10000) { + c -= 0x10000; + if (i + 2 > n) return NULL; + buffer[i++] = 0xD800 | (0x3ff & (c >> 10)); + buffer[i++] = 0xDC00 | (0x3ff & (c )); + } + } else + return NULL; + } + buffer[i] = 0; + return buffer; +} + +char * stb_to_utf8(char *buffer, stb__wchar *str, int n) +{ + int i=0; + --n; + while (*str) { + if (*str < 0x80) { + if (i+1 > n) return NULL; + buffer[i++] = (char) *str++; + } else if (*str < 0x800) { + if (i+2 > n) return NULL; + buffer[i++] = 0xc0 + (*str >> 6); + buffer[i++] = 0x80 + (*str & 0x3f); + str += 1; + } else if (*str >= 0xd800 && *str < 0xdc00) { + stb_uint32 c; + if (i+4 > n) return NULL; + c = ((str[0] - 0xd800) << 10) + ((str[1]) - 0xdc00) + 0x10000; + buffer[i++] = 0xf0 + (c >> 18); + buffer[i++] = 0x80 + ((c >> 12) & 0x3f); + buffer[i++] = 0x80 + ((c >> 6) & 0x3f); + buffer[i++] = 0x80 + ((c ) & 0x3f); + str += 2; + } else if (*str >= 0xdc00 && *str < 0xe000) { + return NULL; + } else { + if (i+3 > n) return NULL; + buffer[i++] = 0xe0 + (*str >> 12); + buffer[i++] = 0x80 + ((*str >> 6) & 0x3f); + buffer[i++] = 0x80 + ((*str ) & 0x3f); + str += 1; + } + } + buffer[i] = 0; + return buffer; +} + +stb__wchar *stb__from_utf8(char *str) +{ + static stb__wchar buffer[4096]; + return stb_from_utf8(buffer, str, 4096); +} + +stb__wchar *stb__from_utf8_alt(char *str) +{ + static stb__wchar buffer[64]; + return stb_from_utf8(buffer, str, 64); +} + +char *stb__to_utf8(stb__wchar *str) +{ + static char buffer[4096]; + return stb_to_utf8(buffer, str, 4096); +} + +#endif + +////////////////////////////////////////////////////////////////////////////// +// +// Miscellany +// + +STB_EXTERN void stb_fatal(char *fmt, ...); +STB_EXTERN void stb_(char *fmt, ...); +STB_EXTERN void stb_append_to_file(char *file, char *fmt, ...); +STB_EXTERN void stb_log(int active); +STB_EXTERN void stb_log_fileline(int active); +STB_EXTERN void stb_log_name(char *filename); + +STB_EXTERN void stb_swap(void *p, void *q, size_t sz); +STB_EXTERN void *stb_copy(void *p, size_t sz); +STB_EXTERN void stb_pointer_array_free(void *p, int len); +STB_EXTERN void **stb_array_block_alloc(int count, int blocksize); + +#define stb_arrcount(x) (sizeof(x)/sizeof((x)[0])) + + +STB_EXTERN int stb__record_fileline(char *f, int n); + +#ifdef STB_DEFINE + +static char *stb__file; +static int stb__line; + +int stb__record_fileline(char *f, int n) +{ + stb__file = f; + stb__line = n; + return 0; +} + +void stb_fatal(char *s, ...) +{ + va_list a; + if (stb__file) + fprintf(stderr, "[%s:%d] ", stb__file, stb__line); + va_start(a,s); + fputs("Fatal error: ", stderr); + vfprintf(stderr, s, a); + va_end(a); + fputs("\n", stderr); + #ifdef STB_DEBUG + #ifdef _MSC_VER + #ifndef STB_PTR64 + __asm int 3; // trap to debugger! + #else + __debugbreak(); + #endif + #else + __builtin_trap(); + #endif + #endif + exit(1); +} + +static int stb__log_active=1, stb__log_fileline=1; + +void stb_log(int active) +{ + stb__log_active = active; +} + +void stb_log_fileline(int active) +{ + stb__log_fileline = active; +} + +#ifdef STB_NO_STB_STRINGS +char *stb__log_filename = "temp.log"; +#else +char *stb__log_filename = "stb.log"; +#endif + +void stb_log_name(char *s) +{ + stb__log_filename = s; +} + +void stb_(char *s, ...) +{ + if (stb__log_active) { + FILE *f = fopen(stb__log_filename, "a"); + if (f) { + va_list a; + if (stb__log_fileline && stb__file) + fprintf(f, "[%s:%4d] ", stb__file, stb__line); + va_start(a,s); + vfprintf(f, s, a); + va_end(a); + fputs("\n", f); + fclose(f); + } + } +} + +void stb_append_to_file(char *filename, char *s, ...) +{ + FILE *f = fopen(filename, "a"); + if (f) { + va_list a; + va_start(a,s); + vfprintf(f, s, a); + va_end(a); + fputs("\n", f); + fclose(f); + } +} + + +typedef struct { char d[4]; } stb__4; +typedef struct { char d[8]; } stb__8; + +// optimize the small cases, though you shouldn't be calling this for those! +void stb_swap(void *p, void *q, size_t sz) +{ + char buffer[256]; + if (p == q) return; + if (sz == 4) { + stb__4 temp = * ( stb__4 *) p; + * (stb__4 *) p = * ( stb__4 *) q; + * (stb__4 *) q = temp; + return; + } else if (sz == 8) { + stb__8 temp = * ( stb__8 *) p; + * (stb__8 *) p = * ( stb__8 *) q; + * (stb__8 *) q = temp; + return; + } + + while (sz > sizeof(buffer)) { + stb_swap(p, q, sizeof(buffer)); + p = (char *) p + sizeof(buffer); + q = (char *) q + sizeof(buffer); + sz -= sizeof(buffer); + } + + memcpy(buffer, p , sz); + memcpy(p , q , sz); + memcpy(q , buffer, sz); +} + +void *stb_copy(void *p, size_t sz) +{ + void *q = malloc(sz); + memcpy(q, p, sz); + return q; +} + +void stb_pointer_array_free(void *q, int len) +{ + void **p = (void **) q; + int i; + for (i=0; i < len; ++i) + free(p[i]); +} + +void **stb_array_block_alloc(int count, int blocksize) +{ + int i; + char *p = (char *) malloc(sizeof(void *) * count + count * blocksize); + void **q; + if (p == NULL) return NULL; + q = (void **) p; + p += sizeof(void *) * count; + for (i=0; i < count; ++i) + q[i] = p + i * blocksize; + return q; +} +#endif + +#ifdef STB_DEBUG + // tricky hack to allow recording FILE,LINE even in varargs functions + #define STB__RECORD_FILE(x) (stb__record_fileline(__FILE__, __LINE__),(x)) + #define stb_log STB__RECORD_FILE(stb_log) + #define stb_ STB__RECORD_FILE(stb_) + #ifndef STB_FATAL_CLEAN + #define stb_fatal STB__RECORD_FILE(stb_fatal) + #endif + #define STB__DEBUG(x) x +#else + #define STB__DEBUG(x) +#endif + +////////////////////////////////////////////////////////////////////////////// +// +// stb_temp +// + +#define stb_temp(block, sz) stb__temp(block, sizeof(block), (sz)) + +STB_EXTERN void * stb__temp(void *b, int b_sz, int want_sz); +STB_EXTERN void stb_tempfree(void *block, void *ptr); + +#ifdef STB_DEFINE + +void * stb__temp(void *b, int b_sz, int want_sz) +{ + if (b_sz >= want_sz) + return b; + else + return malloc(want_sz); +} + +void stb_tempfree(void *b, void *p) +{ + if (p != b) + free(p); +} +#endif + + +////////////////////////////////////////////////////////////////////////////// +// +// math/sampling operations +// + + +#define stb_lerp(t,a,b) ( (a) + (t) * (float) ((b)-(a)) ) +#define stb_unlerp(t,a,b) ( ((t) - (a)) / (float) ((b) - (a)) ) + +#define stb_clamp(x,xmin,xmax) ((x) < (xmin) ? (xmin) : (x) > (xmax) ? (xmax) : (x)) + +STB_EXTERN void stb_newell_normal(float *normal, int num_vert, float **vert, int normalize); +STB_EXTERN int stb_box_face_vertex_axis_side(int face_number, int vertex_number, int axis); +STB_EXTERN void stb_linear_controller(float *curpos, float target_pos, float acc, float deacc, float dt); + +STB_EXTERN int stb_float_eq(float x, float y, float delta, int max_ulps); +STB_EXTERN int stb_is_prime(unsigned int m); +STB_EXTERN unsigned int stb_power_of_two_nearest_prime(int n); + +STB_EXTERN float stb_smoothstep(float t); +STB_EXTERN float stb_cubic_bezier_1d(float t, float p0, float p1, float p2, float p3); + +STB_EXTERN double stb_linear_remap(double x, double a, double b, + double c, double d); + +#ifdef STB_DEFINE +float stb_smoothstep(float t) +{ + return (3 - 2*t)*(t*t); +} + +float stb_cubic_bezier_1d(float t, float p0, float p1, float p2, float p3) +{ + float it = 1-t; + return it*it*it*p0 + 3*it*it*t*p1 + 3*it*t*t*p2 + t*t*t*p3; +} + +void stb_newell_normal(float *normal, int num_vert, float **vert, int normalize) +{ + int i,j; + float p; + normal[0] = normal[1] = normal[2] = 0; + for (i=num_vert-1,j=0; j < num_vert; i=j++) { + float *u = vert[i]; + float *v = vert[j]; + normal[0] += (u[1] - v[1]) * (u[2] + v[2]); + normal[1] += (u[2] - v[2]) * (u[0] + v[0]); + normal[2] += (u[0] - v[0]) * (u[1] + v[1]); + } + if (normalize) { + p = normal[0]*normal[0] + normal[1]*normal[1] + normal[2]*normal[2]; + p = (float) (1.0 / sqrt(p)); + normal[0] *= p; + normal[1] *= p; + normal[2] *= p; + } +} + +int stb_box_face_vertex_axis_side(int face_number, int vertex_number, int axis) +{ + static int box_vertices[6][4][3] = + { + { { 1,1,1 }, { 1,0,1 }, { 1,0,0 }, { 1,1,0 } }, + { { 0,0,0 }, { 0,0,1 }, { 0,1,1 }, { 0,1,0 } }, + { { 0,0,0 }, { 0,1,0 }, { 1,1,0 }, { 1,0,0 } }, + { { 0,0,0 }, { 1,0,0 }, { 1,0,1 }, { 0,0,1 } }, + { { 1,1,1 }, { 0,1,1 }, { 0,0,1 }, { 1,0,1 } }, + { { 1,1,1 }, { 1,1,0 }, { 0,1,0 }, { 0,1,1 } }, + }; + assert(face_number >= 0 && face_number < 6); + assert(vertex_number >= 0 && vertex_number < 4); + assert(axis >= 0 && axis < 3); + return box_vertices[face_number][vertex_number][axis]; +} + +void stb_linear_controller(float *curpos, float target_pos, float acc, float deacc, float dt) +{ + float sign = 1, p, cp = *curpos; + if (cp == target_pos) return; + if (target_pos < cp) { + target_pos = -target_pos; + cp = -cp; + sign = -1; + } + // first decelerate + if (cp < 0) { + p = cp + deacc * dt; + if (p > 0) { + p = 0; + dt = dt - cp / deacc; + if (dt < 0) dt = 0; + } else { + dt = 0; + } + cp = p; + } + // now accelerate + p = cp + acc*dt; + if (p > target_pos) p = target_pos; + *curpos = p * sign; + // @TODO: testing +} + +float stb_quadratic_controller(float target_pos, float curpos, float maxvel, float maxacc, float dt, float *curvel) +{ + return 0; // @TODO +} + +int stb_float_eq(float x, float y, float delta, int max_ulps) +{ + if (fabs(x-y) <= delta) return 1; + if (abs(*(int *)&x - *(int *)&y) <= max_ulps) return 1; + return 0; +} + +int stb_is_prime(unsigned int m) +{ + unsigned int i,j; + if (m < 2) return 0; + if (m == 2) return 1; + if (!(m & 1)) return 0; + if (m % 3 == 0) return (m == 3); + for (i=5; (j=i*i), j <= m && j > i; i += 6) { + if (m % i == 0) return 0; + if (m % (i+2) == 0) return 0; + } + return 1; +} + +unsigned int stb_power_of_two_nearest_prime(int n) +{ + static signed char tab[32] = { 0,0,0,0,1,0,-1,0,1,-1,-1,3,-1,0,-1,2,1, + 0,2,0,-1,-4,-1,5,-1,18,-2,15,2,-1,2,0 }; + if (!tab[0]) { + int i; + for (i=0; i < 32; ++i) + tab[i] = (1 << i) + 2*tab[i] - 1; + tab[1] = 2; + tab[0] = 1; + } + if (n >= 32) return 0xfffffffb; + return tab[n]; +} + +double stb_linear_remap(double x, double x_min, double x_max, + double out_min, double out_max) +{ + return stb_lerp(stb_unlerp(x,x_min,x_max),out_min,out_max); +} +#endif + +// create a macro so it's faster, but you can get at the function pointer +#define stb_linear_remap(t,a,b,c,d) stb_lerp(stb_unlerp(t,a,b),c,d) + + +////////////////////////////////////////////////////////////////////////////// +// +// bit operations +// + +#define stb_big32(c) (((c)[0]<<24) + (c)[1]*65536 + (c)[2]*256 + (c)[3]) +#define stb_little32(c) (((c)[3]<<24) + (c)[2]*65536 + (c)[1]*256 + (c)[0]) +#define stb_big16(c) ((c)[0]*256 + (c)[1]) +#define stb_little16(c) ((c)[1]*256 + (c)[0]) + +STB_EXTERN int stb_bitcount(unsigned int a); +STB_EXTERN unsigned int stb_bitreverse8(unsigned char n); +STB_EXTERN unsigned int stb_bitreverse(unsigned int n); + +STB_EXTERN int stb_is_pow2(unsigned int n); +STB_EXTERN int stb_log2_ceil(unsigned int n); +STB_EXTERN int stb_log2_floor(unsigned int n); + +STB_EXTERN int stb_lowbit8(unsigned int n); +STB_EXTERN int stb_highbit8(unsigned int n); + +#ifdef STB_DEFINE +int stb_bitcount(unsigned int a) +{ + a = (a & 0x55555555) + ((a >> 1) & 0x55555555); // max 2 + a = (a & 0x33333333) + ((a >> 2) & 0x33333333); // max 4 + a = (a + (a >> 4)) & 0x0f0f0f0f; // max 8 per 4, now 8 bits + a = (a + (a >> 8)); // max 16 per 8 bits + a = (a + (a >> 16)); // max 32 per 8 bits + return a & 0xff; +} + +unsigned int stb_bitreverse8(unsigned char n) +{ + n = ((n & 0xAA) >> 1) + ((n & 0x55) << 1); + n = ((n & 0xCC) >> 2) + ((n & 0x33) << 2); + return (unsigned char) ((n >> 4) + (n << 4)); +} + +unsigned int stb_bitreverse(unsigned int n) +{ + n = ((n & 0xAAAAAAAA) >> 1) | ((n & 0x55555555) << 1); + n = ((n & 0xCCCCCCCC) >> 2) | ((n & 0x33333333) << 2); + n = ((n & 0xF0F0F0F0) >> 4) | ((n & 0x0F0F0F0F) << 4); + n = ((n & 0xFF00FF00) >> 8) | ((n & 0x00FF00FF) << 8); + return (n >> 16) | (n << 16); +} + +int stb_is_pow2(unsigned int n) +{ + return (n & (n-1)) == 0; +} + +// tricky use of 4-bit table to identify 5 bit positions (note the '-1') +// 3-bit table would require another tree level; 5-bit table wouldn't save one +#if defined(_WIN32) && !defined(__MINGW32__) +#pragma warning(push) +#pragma warning(disable: 4035) // disable warning about no return value +int stb_log2_floor(unsigned int n) +{ + #if _MSC_VER > 1700 + unsigned long i; + _BitScanReverse(&i, n); + return i != 0 ? i : -1; + #else + __asm { + bsr eax,n + jnz done + mov eax,-1 + } + done:; + #endif +} +#pragma warning(pop) +#else +int stb_log2_floor(unsigned int n) +{ + static signed char log2_4[16] = { -1,0,1,1,2,2,2,2,3,3,3,3,3,3,3,3 }; + + // 2 compares if n < 16, 3 compares otherwise + if (n < (1U << 14)) + if (n < (1U << 4)) return 0 + log2_4[n ]; + else if (n < (1U << 9)) return 5 + log2_4[n >> 5]; + else return 10 + log2_4[n >> 10]; + else if (n < (1U << 24)) + if (n < (1U << 19)) return 15 + log2_4[n >> 15]; + else return 20 + log2_4[n >> 20]; + else if (n < (1U << 29)) return 25 + log2_4[n >> 25]; + else return 30 + log2_4[n >> 30]; +} +#endif + +// define ceil from floor +int stb_log2_ceil(unsigned int n) +{ + if (stb_is_pow2(n)) return stb_log2_floor(n); + else return 1 + stb_log2_floor(n); +} + +int stb_highbit8(unsigned int n) +{ + return stb_log2_ceil(n&255); +} + +int stb_lowbit8(unsigned int n) +{ + static signed char lowbit4[16] = { -1,0,1,0, 2,0,1,0, 3,0,1,0, 2,0,1,0 }; + int k = lowbit4[n & 15]; + if (k >= 0) return k; + k = lowbit4[(n >> 4) & 15]; + if (k >= 0) return k+4; + return k; +} +#endif + + + +////////////////////////////////////////////////////////////////////////////// +// +// qsort Compare Routines +// + +#ifdef _WIN32 + #define stb_stricmp(a,b) stricmp(a,b) + #define stb_strnicmp(a,b,n) strnicmp(a,b,n) +#else + #define stb_stricmp(a,b) strcasecmp(a,b) + #define stb_strnicmp(a,b,n) strncasecmp(a,b,n) +#endif + + +STB_EXTERN int (*stb_intcmp(int offset))(const void *a, const void *b); +STB_EXTERN int (*stb_qsort_strcmp(int offset))(const void *a, const void *b); +STB_EXTERN int (*stb_qsort_stricmp(int offset))(const void *a, const void *b); +STB_EXTERN int (*stb_floatcmp(int offset))(const void *a, const void *b); +STB_EXTERN int (*stb_doublecmp(int offset))(const void *a, const void *b); +STB_EXTERN int (*stb_charcmp(int offset))(const void *a, const void *b); + +#ifdef STB_DEFINE +static int stb__intcmpoffset, stb__charcmpoffset, stb__strcmpoffset; +static int stb__floatcmpoffset, stb__doublecmpoffset; + +int stb__intcmp(const void *a, const void *b) +{ + const int p = *(const int *) ((const char *) a + stb__intcmpoffset); + const int q = *(const int *) ((const char *) b + stb__intcmpoffset); + return p < q ? -1 : p > q; +} + +int stb__charcmp(const void *a, const void *b) +{ + const int p = *(const unsigned char *) ((const char *) a + stb__charcmpoffset); + const int q = *(const unsigned char *) ((const char *) b + stb__charcmpoffset); + return p < q ? -1 : p > q; +} + +int stb__floatcmp(const void *a, const void *b) +{ + const float p = *(const float *) ((const char *) a + stb__floatcmpoffset); + const float q = *(const float *) ((const char *) b + stb__floatcmpoffset); + return p < q ? -1 : p > q; +} + +int stb__doublecmp(const void *a, const void *b) +{ + const double p = *(const double *) ((const char *) a + stb__doublecmpoffset); + const double q = *(const double *) ((const char *) b + stb__doublecmpoffset); + return p < q ? -1 : p > q; +} + +int stb__qsort_strcmp(const void *a, const void *b) +{ + const char *p = *(const char **) ((const char *) a + stb__strcmpoffset); + const char *q = *(const char **) ((const char *) b + stb__strcmpoffset); + return strcmp(p,q); +} + +int stb__qsort_stricmp(const void *a, const void *b) +{ + const char *p = *(const char **) ((const char *) a + stb__strcmpoffset); + const char *q = *(const char **) ((const char *) b + stb__strcmpoffset); + return stb_stricmp(p,q); +} + +int (*stb_intcmp(int offset))(const void *, const void *) +{ + stb__intcmpoffset = offset; + return &stb__intcmp; +} + +int (*stb_charcmp(int offset))(const void *, const void *) +{ + stb__charcmpoffset = offset; + return &stb__charcmp; +} + +int (*stb_qsort_strcmp(int offset))(const void *, const void *) +{ + stb__strcmpoffset = offset; + return &stb__qsort_strcmp; +} + +int (*stb_qsort_stricmp(int offset))(const void *, const void *) +{ + stb__strcmpoffset = offset; + return &stb__qsort_stricmp; +} + +int (*stb_floatcmp(int offset))(const void *, const void *) +{ + stb__floatcmpoffset = offset; + return &stb__floatcmp; +} + +int (*stb_doublecmp(int offset))(const void *, const void *) +{ + stb__doublecmpoffset = offset; + return &stb__doublecmp; +} + +#endif + +////////////////////////////////////////////////////////////////////////////// +// +// Binary Search Toolkit +// + +typedef struct +{ + int minval, maxval, guess; + int mode, step; +} stb_search; + +STB_EXTERN int stb_search_binary(stb_search *s, int minv, int maxv, int find_smallest); +STB_EXTERN int stb_search_open(stb_search *s, int minv, int find_smallest); +STB_EXTERN int stb_probe(stb_search *s, int compare, int *result); // return 0 when done + +#ifdef STB_DEFINE +enum +{ + STB_probe_binary_smallest, + STB_probe_binary_largest, + STB_probe_open_smallest, + STB_probe_open_largest, +}; + +static int stb_probe_guess(stb_search *s, int *result) +{ + switch(s->mode) { + case STB_probe_binary_largest: + if (s->minval == s->maxval) { + *result = s->minval; + return 0; + } + assert(s->minval < s->maxval); + // if a < b, then a < p <= b + s->guess = s->minval + (((unsigned) s->maxval - s->minval + 1) >> 1); + break; + + case STB_probe_binary_smallest: + if (s->minval == s->maxval) { + *result = s->minval; + return 0; + } + assert(s->minval < s->maxval); + // if a < b, then a <= p < b + s->guess = s->minval + (((unsigned) s->maxval - s->minval) >> 1); + break; + case STB_probe_open_smallest: + case STB_probe_open_largest: + s->guess = s->maxval; // guess the current maxval + break; + } + *result = s->guess; + return 1; +} + +int stb_probe(stb_search *s, int compare, int *result) +{ + switch(s->mode) { + case STB_probe_open_smallest: + case STB_probe_open_largest: { + if (compare <= 0) { + // then it lies within minval & maxval + if (s->mode == STB_probe_open_smallest) + s->mode = STB_probe_binary_smallest; + else + s->mode = STB_probe_binary_largest; + } else { + // otherwise, we need to probe larger + s->minval = s->maxval + 1; + s->maxval = s->minval + s->step; + s->step += s->step; + } + break; + } + case STB_probe_binary_smallest: { + // if compare < 0, then s->minval <= a < p + // if compare = 0, then s->minval <= a <= p + // if compare > 0, then p < a <= s->maxval + if (compare <= 0) + s->maxval = s->guess; + else + s->minval = s->guess+1; + break; + } + case STB_probe_binary_largest: { + // if compare < 0, then s->minval <= a < p + // if compare = 0, then p <= a <= s->maxval + // if compare > 0, then p < a <= s->maxval + if (compare < 0) + s->maxval = s->guess-1; + else + s->minval = s->guess; + break; + } + } + return stb_probe_guess(s, result); +} + +int stb_search_binary(stb_search *s, int minv, int maxv, int find_smallest) +{ + int r; + if (maxv < minv) return minv-1; + s->minval = minv; + s->maxval = maxv; + s->mode = find_smallest ? STB_probe_binary_smallest : STB_probe_binary_largest; + stb_probe_guess(s, &r); + return r; +} + +int stb_search_open(stb_search *s, int minv, int find_smallest) +{ + int r; + s->step = 4; + s->minval = minv; + s->maxval = minv+s->step; + s->mode = find_smallest ? STB_probe_open_smallest : STB_probe_open_largest; + stb_probe_guess(s, &r); + return r; +} +#endif + +////////////////////////////////////////////////////////////////////////////// +// +// String Processing +// + +#define stb_prefixi(s,t) (0==stb_strnicmp((s),(t),strlen(t))) + +enum stb_splitpath_flag +{ + STB_PATH = 1, + STB_FILE = 2, + STB_EXT = 4, + STB_PATH_FILE = STB_PATH + STB_FILE, + STB_FILE_EXT = STB_FILE + STB_EXT, + STB_EXT_NO_PERIOD = 8, +}; + +STB_EXTERN char * stb_skipwhite(char *s); +STB_EXTERN char * stb_trimwhite(char *s); +STB_EXTERN char * stb_skipnewline(char *s); +STB_EXTERN char * stb_strncpy(char *s, char *t, int n); +STB_EXTERN char * stb_substr(char *t, int n); +STB_EXTERN char * stb_duplower(char *s); +STB_EXTERN void stb_tolower (char *s); +STB_EXTERN char * stb_strchr2 (char *s, char p1, char p2); +STB_EXTERN char * stb_strrchr2(char *s, char p1, char p2); +STB_EXTERN char * stb_strtok(char *output, char *src, char *delimit); +STB_EXTERN char * stb_strtok_keep(char *output, char *src, char *delimit); +STB_EXTERN char * stb_strtok_invert(char *output, char *src, char *allowed); +STB_EXTERN char * stb_dupreplace(char *s, char *find, char *replace); +STB_EXTERN void stb_replaceinplace(char *s, char *find, char *replace); +STB_EXTERN char * stb_splitpath(char *output, char *src, int flag); +STB_EXTERN char * stb_splitpathdup(char *src, int flag); +STB_EXTERN char * stb_replacedir(char *output, char *src, char *dir); +STB_EXTERN char * stb_replaceext(char *output, char *src, char *ext); +STB_EXTERN void stb_fixpath(char *path); +STB_EXTERN char * stb_shorten_path_readable(char *path, int max_len); +STB_EXTERN int stb_suffix (char *s, char *t); +STB_EXTERN int stb_suffixi(char *s, char *t); +STB_EXTERN int stb_prefix (char *s, char *t); +STB_EXTERN char * stb_strichr(char *s, char t); +STB_EXTERN char * stb_stristr(char *s, char *t); +STB_EXTERN int stb_prefix_count(char *s, char *t); +STB_EXTERN char * stb_plural(int n); // "s" or "" +STB_EXTERN size_t stb_strscpy(char *d, const char *s, size_t n); + +STB_EXTERN char **stb_tokens(char *src, char *delimit, int *count); +STB_EXTERN char **stb_tokens_nested(char *src, char *delimit, int *count, char *nest_in, char *nest_out); +STB_EXTERN char **stb_tokens_nested_empty(char *src, char *delimit, int *count, char *nest_in, char *nest_out); +STB_EXTERN char **stb_tokens_allowempty(char *src, char *delimit, int *count); +STB_EXTERN char **stb_tokens_stripwhite(char *src, char *delimit, int *count); +STB_EXTERN char **stb_tokens_withdelim(char *src, char *delimit, int *count); +STB_EXTERN char **stb_tokens_quoted(char *src, char *delimit, int *count); +// with 'quoted', allow delimiters to appear inside quotation marks, and don't +// strip whitespace inside them (and we delete the quotation marks unless they +// appear back to back, in which case they're considered escaped) + +#ifdef STB_DEFINE + +size_t stb_strscpy(char *d, const char *s, size_t n) +{ + size_t len = strlen(s); + if (len >= n) { + if (n) d[0] = 0; + return 0; + } + strcpy(d,s); + return len + 1; +} + +char *stb_plural(int n) +{ + return n == 1 ? "" : "s"; +} + +int stb_prefix(char *s, char *t) +{ + while (*t) + if (*s++ != *t++) + return STB_FALSE; + return STB_TRUE; +} + +int stb_prefix_count(char *s, char *t) +{ + int c=0; + while (*t) { + if (*s++ != *t++) + break; + ++c; + } + return c; +} + +int stb_suffix(char *s, char *t) +{ + size_t n = strlen(s); + size_t m = strlen(t); + if (m <= n) + return 0 == strcmp(s+n-m, t); + else + return 0; +} + +int stb_suffixi(char *s, char *t) +{ + size_t n = strlen(s); + size_t m = strlen(t); + if (m <= n) + return 0 == stb_stricmp(s+n-m, t); + else + return 0; +} + +// originally I was using this table so that I could create known sentinel +// values--e.g. change whitetable[0] to be true if I was scanning for whitespace, +// and false if I was scanning for nonwhite. I don't appear to be using that +// functionality anymore (I do for tokentable, though), so just replace it +// with isspace() +char *stb_skipwhite(char *s) +{ + while (isspace((unsigned char) *s)) ++s; + return s; +} + +char *stb_skipnewline(char *s) +{ + if (s[0] == '\r' || s[0] == '\n') { + if (s[0]+s[1] == '\r' + '\n') ++s; + ++s; + } + return s; +} + +char *stb_trimwhite(char *s) +{ + int i,n; + s = stb_skipwhite(s); + n = (int) strlen(s); + for (i=n-1; i >= 0; --i) + if (!isspace(s[i])) + break; + s[i+1] = 0; + return s; +} + +char *stb_strncpy(char *s, char *t, int n) +{ + strncpy(s,t,n); + s[n-1] = 0; + return s; +} + +char *stb_substr(char *t, int n) +{ + char *a; + int z = (int) strlen(t); + if (z < n) n = z; + a = (char *) malloc(n+1); + strncpy(a,t,n); + a[n] = 0; + return a; +} + +char *stb_duplower(char *s) +{ + char *p = strdup(s), *q = p; + while (*q) { + *q = tolower(*q); + ++q; + } + return p; +} + +void stb_tolower(char *s) +{ + while (*s) { + *s = tolower(*s); + ++s; + } +} + +char *stb_strchr2(char *s, char x, char y) +{ + for(; *s; ++s) + if (*s == x || *s == y) + return s; + return NULL; +} + +char *stb_strrchr2(char *s, char x, char y) +{ + char *r = NULL; + for(; *s; ++s) + if (*s == x || *s == y) + r = s; + return r; +} + +char *stb_strichr(char *s, char t) +{ + if (tolower(t) == toupper(t)) + return strchr(s,t); + return stb_strchr2(s, (char) tolower(t), (char) toupper(t)); +} + +char *stb_stristr(char *s, char *t) +{ + size_t n = strlen(t); + char *z; + if (n==0) return s; + while ((z = stb_strichr(s, *t)) != NULL) { + if (0==stb_strnicmp(z, t, n)) + return z; + s = z+1; + } + return NULL; +} + +static char *stb_strtok_raw(char *output, char *src, char *delimit, int keep, int invert) +{ + if (invert) { + while (*src && strchr(delimit, *src) != NULL) { + *output++ = *src++; + } + } else { + while (*src && strchr(delimit, *src) == NULL) { + *output++ = *src++; + } + } + *output = 0; + if (keep) + return src; + else + return *src ? src+1 : src; +} + +char *stb_strtok(char *output, char *src, char *delimit) +{ + return stb_strtok_raw(output, src, delimit, 0, 0); +} + +char *stb_strtok_keep(char *output, char *src, char *delimit) +{ + return stb_strtok_raw(output, src, delimit, 1, 0); +} + +char *stb_strtok_invert(char *output, char *src, char *delimit) +{ + return stb_strtok_raw(output, src, delimit, 1,1); +} + +static char **stb_tokens_raw(char *src_, char *delimit, int *count, + int stripwhite, int allow_empty, char *start, char *end) +{ + int nested = 0; + unsigned char *src = (unsigned char *) src_; + static char stb_tokentable[256]; // rely on static initializion to 0 + static char stable[256],etable[256]; + char *out; + char **result; + int num=0; + unsigned char *s; + + s = (unsigned char *) delimit; while (*s) stb_tokentable[*s++] = 1; + if (start) { + s = (unsigned char *) start; while (*s) stable[*s++] = 1; + s = (unsigned char *) end; if (s) while (*s) stable[*s++] = 1; + s = (unsigned char *) end; if (s) while (*s) etable[*s++] = 1; + } + stable[0] = 1; + + // two passes through: the first time, counting how many + s = (unsigned char *) src; + while (*s) { + // state: just found delimiter + // skip further delimiters + if (!allow_empty) { + stb_tokentable[0] = 0; + while (stb_tokentable[*s]) + ++s; + if (!*s) break; + } + ++num; + // skip further non-delimiters + stb_tokentable[0] = 1; + if (stripwhite == 2) { // quoted strings + while (!stb_tokentable[*s]) { + if (*s != '"') + ++s; + else { + ++s; + if (*s == '"') + ++s; // "" -> ", not start a string + else { + // begin a string + while (*s) { + if (s[0] == '"') { + if (s[1] == '"') s += 2; // "" -> " + else { ++s; break; } // terminating " + } else + ++s; + } + } + } + } + } else + while (nested || !stb_tokentable[*s]) { + if (stable[*s]) { + if (!*s) break; + if (end ? etable[*s] : nested) + --nested; + else + ++nested; + } + ++s; + } + if (allow_empty) { + if (*s) ++s; + } + } + // now num has the actual count... malloc our output structure + // need space for all the strings: strings won't be any longer than + // original input, since for every '\0' there's at least one delimiter + result = (char **) malloc(sizeof(*result) * (num+1) + (s-src+1)); + if (result == NULL) return result; + out = (char *) (result + (num+1)); + // second pass: copy out the data + s = (unsigned char *) src; + num = 0; + nested = 0; + while (*s) { + char *last_nonwhite; + // state: just found delimiter + // skip further delimiters + if (!allow_empty) { + stb_tokentable[0] = 0; + if (stripwhite) + while (stb_tokentable[*s] || isspace(*s)) + ++s; + else + while (stb_tokentable[*s]) + ++s; + } else if (stripwhite) { + while (isspace(*s)) ++s; + } + if (!*s) break; + // we're past any leading delimiters and whitespace + result[num] = out; + ++num; + // copy non-delimiters + stb_tokentable[0] = 1; + last_nonwhite = out-1; + if (stripwhite == 2) { + while (!stb_tokentable[*s]) { + if (*s != '"') { + if (!isspace(*s)) last_nonwhite = out; + *out++ = *s++; + } else { + ++s; + if (*s == '"') { + if (!isspace(*s)) last_nonwhite = out; + *out++ = *s++; // "" -> ", not start string + } else { + // begin a quoted string + while (*s) { + if (s[0] == '"') { + if (s[1] == '"') { *out++ = *s; s += 2; } + else { ++s; break; } // terminating " + } else + *out++ = *s++; + } + last_nonwhite = out-1; // all in quotes counts as non-white + } + } + } + } else { + while (nested || !stb_tokentable[*s]) { + if (!isspace(*s)) last_nonwhite = out; + if (stable[*s]) { + if (!*s) break; + if (end ? etable[*s] : nested) + --nested; + else + ++nested; + } + *out++ = *s++; + } + } + + if (stripwhite) // rewind to last non-whitespace char + out = last_nonwhite+1; + *out++ = '\0'; + + if (*s) ++s; // skip delimiter + } + s = (unsigned char *) delimit; while (*s) stb_tokentable[*s++] = 0; + if (start) { + s = (unsigned char *) start; while (*s) stable[*s++] = 1; + s = (unsigned char *) end; if (s) while (*s) stable[*s++] = 1; + s = (unsigned char *) end; if (s) while (*s) etable[*s++] = 1; + } + if (count != NULL) *count = num; + result[num] = 0; + return result; +} + +char **stb_tokens(char *src, char *delimit, int *count) +{ + return stb_tokens_raw(src,delimit,count,0,0,0,0); +} + +char **stb_tokens_nested(char *src, char *delimit, int *count, char *nest_in, char *nest_out) +{ + return stb_tokens_raw(src,delimit,count,0,0,nest_in,nest_out); +} + +char **stb_tokens_nested_empty(char *src, char *delimit, int *count, char *nest_in, char *nest_out) +{ + return stb_tokens_raw(src,delimit,count,0,1,nest_in,nest_out); +} + +char **stb_tokens_allowempty(char *src, char *delimit, int *count) +{ + return stb_tokens_raw(src,delimit,count,0,1,0,0); +} + +char **stb_tokens_stripwhite(char *src, char *delimit, int *count) +{ + return stb_tokens_raw(src,delimit,count,1,1,0,0); +} + +char **stb_tokens_quoted(char *src, char *delimit, int *count) +{ + return stb_tokens_raw(src,delimit,count,2,1,0,0); +} + +char *stb_dupreplace(char *src, char *find, char *replace) +{ + size_t len_find = strlen(find); + size_t len_replace = strlen(replace); + int count = 0; + + char *s,*p,*q; + + s = strstr(src, find); + if (s == NULL) return strdup(src); + do { + ++count; + s = strstr(s + len_find, find); + } while (s != NULL); + + p = (char *) malloc(strlen(src) + count * (len_replace - len_find) + 1); + if (p == NULL) return p; + q = p; + s = src; + for (;;) { + char *t = strstr(s, find); + if (t == NULL) { + strcpy(q,s); + assert(strlen(p) == strlen(src) + count*(len_replace-len_find)); + return p; + } + memcpy(q, s, t-s); + q += t-s; + memcpy(q, replace, len_replace); + q += len_replace; + s = t + len_find; + } +} + +void stb_replaceinplace(char *src, char *find, char *replace) +{ + size_t len_find = strlen(find); + size_t len_replace = strlen(replace); + int delta; + + char *s,*p,*q; + + delta = len_replace - len_find; + assert(delta <= 0); + if (delta > 0) return; + + p = strstr(src, find); + if (p == NULL) return; + + s = q = p; + while (*s) { + memcpy(q, replace, len_replace); + p += len_find; + q += len_replace; + s = strstr(p, find); + if (s == NULL) s = p + strlen(p); + memmove(q, p, s-p); + q += s-p; + p = s; + } + *q = 0; +} + +void stb_fixpath(char *path) +{ + for(; *path; ++path) + if (*path == '\\') + *path = '/'; +} + +void stb__add_section(char *buffer, char *data, int curlen, int newlen) +{ + if (newlen < curlen) { + int z1 = newlen >> 1, z2 = newlen-z1; + memcpy(buffer, data, z1-1); + buffer[z1-1] = '.'; + buffer[z1-0] = '.'; + memcpy(buffer+z1+1, data+curlen-z2+1, z2-1); + } else + memcpy(buffer, data, curlen); +} + +char * stb_shorten_path_readable(char *path, int len) +{ + static char buffer[1024]; + int n = strlen(path),n1,n2,r1,r2; + char *s; + if (n <= len) return path; + if (len > 1024) return path; + s = stb_strrchr2(path, '/', '\\'); + if (s) { + n1 = s - path + 1; + n2 = n - n1; + ++s; + } else { + n1 = 0; + n2 = n; + s = path; + } + // now we need to reduce r1 and r2 so that they fit in len + if (n1 < len>>1) { + r1 = n1; + r2 = len - r1; + } else if (n2 < len >> 1) { + r2 = n2; + r1 = len - r2; + } else { + r1 = n1 * len / n; + r2 = n2 * len / n; + if (r1 < len>>2) r1 = len>>2, r2 = len-r1; + if (r2 < len>>2) r2 = len>>2, r1 = len-r2; + } + assert(r1 <= n1 && r2 <= n2); + if (n1) + stb__add_section(buffer, path, n1, r1); + stb__add_section(buffer+r1, s, n2, r2); + buffer[len] = 0; + return buffer; +} + +static char *stb__splitpath_raw(char *buffer, char *path, int flag) +{ + int len=0,x,y, n = (int) strlen(path), f1,f2; + char *s = stb_strrchr2(path, '/', '\\'); + char *t = strrchr(path, '.'); + if (s && t && t < s) t = NULL; + if (s) ++s; + + if (flag == STB_EXT_NO_PERIOD) + flag |= STB_EXT; + + if (!(flag & (STB_PATH | STB_FILE | STB_EXT))) return NULL; + + f1 = s == NULL ? 0 : s-path; // start of filename + f2 = t == NULL ? n : t-path; // just past end of filename + + if (flag & STB_PATH) { + x = 0; if (f1 == 0 && flag == STB_PATH) len=2; + } else if (flag & STB_FILE) { + x = f1; + } else { + x = f2; + if (flag & STB_EXT_NO_PERIOD) + if (buffer[x] == '.') + ++x; + } + + if (flag & STB_EXT) + y = n; + else if (flag & STB_FILE) + y = f2; + else + y = f1; + + if (buffer == NULL) { + buffer = (char *) malloc(y-x + len + 1); + if (!buffer) return NULL; + } + + if (len) { strcpy(buffer, "./"); return buffer; } + strncpy(buffer, path+x, y-x); + buffer[y-x] = 0; + return buffer; +} + +char *stb_splitpath(char *output, char *src, int flag) +{ + return stb__splitpath_raw(output, src, flag); +} + +char *stb_splitpathdup(char *src, int flag) +{ + return stb__splitpath_raw(NULL, src, flag); +} + +char *stb_replacedir(char *output, char *src, char *dir) +{ + char buffer[4096]; + stb_splitpath(buffer, src, STB_FILE | STB_EXT); + if (dir) + sprintf(output, "%s/%s", dir, buffer); + else + strcpy(output, buffer); + return output; +} + +char *stb_replaceext(char *output, char *src, char *ext) +{ + char buffer[4096]; + stb_splitpath(buffer, src, STB_PATH | STB_FILE); + if (ext) + sprintf(output, "%s.%s", buffer, ext[0] == '.' ? ext+1 : ext); + else + strcpy(output, buffer); + return output; +} +#endif + +////////////////////////////////////////////////////////////////////////////// +// +// stb_alloc - hierarchical allocator +// +// inspired by http://swapped.cc/halloc +// +// +// When you alloc a given block through stb_alloc, you have these choices: +// +// 1. does it have a parent? +// 2. can it have children? +// 3. can it be freed directly? +// 4. is it transferrable? +// 5. what is its alignment? +// +// Here are interesting combinations of those: +// +// children free transfer alignment +// arena Y Y N n/a +// no-overhead, chunked N N N normal +// string pool alloc N N N 1 +// parent-ptr, chunked Y N N normal +// low-overhead, unchunked N Y Y normal +// general purpose alloc Y Y Y normal +// +// Unchunked allocations will probably return 16-aligned pointers. If +// we 16-align the results, we have room for 4 pointers. For smaller +// allocations that allow finer alignment, we can reduce the pointers. +// +// The strategy is that given a pointer, assuming it has a header (only +// the no-overhead allocations have no header), we can determine the +// type of the header fields, and the number of them, by stepping backwards +// through memory and looking at the tags in the bottom bits. +// +// Implementation strategy: +// chunked allocations come from the middle of chunks, and can't +// be freed. thefore they do not need to be on a sibling chain. +// they may need child pointers if they have children. +// +// chunked, with-children +// void *parent; +// +// unchunked, no-children -- reduced storage +// void *next_sibling; +// void *prev_sibling_nextp; +// +// unchunked, general +// void *first_child; +// void *next_sibling; +// void *prev_sibling_nextp; +// void *chunks; +// +// so, if we code each of these fields with different bit patterns +// (actually same one for next/prev/child), then we can identify which +// each one is from the last field. + +STB_EXTERN void stb_free(void *p); +STB_EXTERN void *stb_malloc_global(size_t size); +STB_EXTERN void *stb_malloc(void *context, size_t size); +STB_EXTERN void *stb_malloc_nofree(void *context, size_t size); +STB_EXTERN void *stb_malloc_leaf(void *context, size_t size); +STB_EXTERN void *stb_malloc_raw(void *context, size_t size); +STB_EXTERN void *stb_realloc(void *ptr, size_t newsize); + +STB_EXTERN void stb_reassign(void *new_context, void *ptr); +STB_EXTERN void stb_malloc_validate(void *p, void *parent); + +extern int stb_alloc_chunk_size ; +extern int stb_alloc_count_free ; +extern int stb_alloc_count_alloc; +extern int stb_alloc_alignment ; + +#ifdef STB_DEFINE + +int stb_alloc_chunk_size = 65536; +int stb_alloc_count_free = 0; +int stb_alloc_count_alloc = 0; +int stb_alloc_alignment = -16; + +typedef struct stb__chunk +{ + struct stb__chunk *next; + int data_left; + int alloc; +} stb__chunk; + +typedef struct +{ + void * next; + void ** prevn; +} stb__nochildren; + +typedef struct +{ + void ** prevn; + void * child; + void * next; + stb__chunk *chunks; +} stb__alloc; + +typedef struct +{ + stb__alloc *parent; +} stb__chunked; + +#define STB__PARENT 1 +#define STB__CHUNKS 2 + +typedef enum +{ + STB__nochildren = 0, + STB__chunked = STB__PARENT, + STB__alloc = STB__CHUNKS, + + STB__chunk_raw = 4, +} stb__alloc_type; + +// these functions set the bottom bits of a pointer efficiently +#define STB__DECODE(x,v) ((void *) ((char *) (x) - (v))) +#define STB__ENCODE(x,v) ((void *) ((char *) (x) + (v))) + +#define stb__parent(z) (stb__alloc *) STB__DECODE((z)->parent, STB__PARENT) +#define stb__chunks(z) (stb__chunk *) STB__DECODE((z)->chunks, STB__CHUNKS) + +#define stb__setparent(z,p) (z)->parent = (stb__alloc *) STB__ENCODE((p), STB__PARENT) +#define stb__setchunks(z,c) (z)->chunks = (stb__chunk *) STB__ENCODE((c), STB__CHUNKS) + +static stb__alloc stb__alloc_global = +{ + NULL, + NULL, + NULL, + (stb__chunk *) STB__ENCODE(NULL, STB__CHUNKS) +}; + +static stb__alloc_type stb__identify(void *p) +{ + void **q = (void **) p; + return (stb__alloc_type) ((stb_uinta) q[-1] & 3); +} + +static void *** stb__prevn(void *p) +{ + if (stb__identify(p) == STB__alloc) { + stb__alloc *s = (stb__alloc *) p - 1; + return &s->prevn; + } else { + stb__nochildren *s = (stb__nochildren *) p - 1; + return &s->prevn; + } +} + +void stb_free(void *p) +{ + if (p == NULL) return; + + // count frees so that unit tests can see what's happening + ++stb_alloc_count_free; + + switch(stb__identify(p)) { + case STB__chunked: + // freeing a chunked-block with children does nothing; + // they only get freed when the parent does + // surely this is wrong, and it should free them immediately? + // otherwise how are they getting put on the right chain? + return; + case STB__nochildren: { + stb__nochildren *s = (stb__nochildren *) p - 1; + // unlink from sibling chain + *(s->prevn) = s->next; + if (s->next) + *stb__prevn(s->next) = s->prevn; + free(s); + return; + } + case STB__alloc: { + stb__alloc *s = (stb__alloc *) p - 1; + stb__chunk *c, *n; + void *q; + + // unlink from sibling chain, if any + *(s->prevn) = s->next; + if (s->next) + *stb__prevn(s->next) = s->prevn; + + // first free chunks + c = (stb__chunk *) stb__chunks(s); + while (c != NULL) { + n = c->next; + stb_alloc_count_free += c->alloc; + free(c); + c = n; + } + + // validating + stb__setchunks(s,NULL); + s->prevn = NULL; + s->next = NULL; + + // now free children + while ((q = s->child) != NULL) { + stb_free(q); + } + + // now free self + free(s); + return; + } + default: + assert(0); /* NOTREACHED */ + } +} + +void stb_malloc_validate(void *p, void *parent) +{ + if (p == NULL) return; + + switch(stb__identify(p)) { + case STB__chunked: + return; + case STB__nochildren: { + stb__nochildren *n = (stb__nochildren *) p - 1; + if (n->prevn) + assert(*n->prevn == p); + if (n->next) { + assert(*stb__prevn(n->next) == &n->next); + stb_malloc_validate(n, parent); + } + return; + } + case STB__alloc: { + stb__alloc *s = (stb__alloc *) p - 1; + + if (s->prevn) + assert(*s->prevn == p); + + if (s->child) { + assert(*stb__prevn(s->child) == &s->child); + stb_malloc_validate(s->child, p); + } + + if (s->next) { + assert(*stb__prevn(s->next) == &s->next); + stb_malloc_validate(s->next, parent); + } + return; + } + default: + assert(0); /* NOTREACHED */ + } +} + +static void * stb__try_chunk(stb__chunk *c, int size, int align, int pre_align) +{ + char *memblock = (char *) (c+1), *q; + stb_inta iq; + int start_offset; + + // we going to allocate at the end of the chunk, not the start. confusing, + // but it means we don't need both a 'limit' and a 'cur', just a 'cur'. + // the block ends at: p + c->data_left + // then we move back by size + start_offset = c->data_left - size; + + // now we need to check the alignment of that + q = memblock + start_offset; + iq = (stb_inta) q; + assert(sizeof(q) == sizeof(iq)); + + // suppose align = 2 + // then we need to retreat iq far enough that (iq & (2-1)) == 0 + // to get (iq & (align-1)) = 0 requires subtracting (iq & (align-1)) + + start_offset -= iq & (align-1); + assert(((stb_uinta) (memblock+start_offset) & (align-1)) == 0); + + // now, if that + pre_align works, go for it! + start_offset -= pre_align; + + if (start_offset >= 0) { + c->data_left = start_offset; + return memblock + start_offset; + } + + return NULL; +} + +static void stb__sort_chunks(stb__alloc *src) +{ + // of the first two chunks, put the chunk with more data left in it first + stb__chunk *c = stb__chunks(src), *d; + if (c == NULL) return; + d = c->next; + if (d == NULL) return; + if (c->data_left > d->data_left) return; + + c->next = d->next; + d->next = c; + stb__setchunks(src, d); +} + +static void * stb__alloc_chunk(stb__alloc *src, int size, int align, int pre_align) +{ + void *p; + stb__chunk *c = stb__chunks(src); + + if (c && size <= stb_alloc_chunk_size) { + + p = stb__try_chunk(c, size, align, pre_align); + if (p) { ++c->alloc; return p; } + + // try a second chunk to reduce wastage + if (c->next) { + p = stb__try_chunk(c->next, size, align, pre_align); + if (p) { ++c->alloc; return p; } + + // put the bigger chunk first, since the second will get buried + // the upshot of this is that, until it gets allocated from, chunk #2 + // is always the largest remaining chunk. (could formalize + // this with a heap!) + stb__sort_chunks(src); + c = stb__chunks(src); + } + } + + // allocate a new chunk + { + stb__chunk *n; + + int chunk_size = stb_alloc_chunk_size; + // we're going to allocate a new chunk to put this in + if (size > chunk_size) + chunk_size = size; + + assert(sizeof(*n) + pre_align <= 16); + + // loop trying to allocate a large enough chunk + // the loop is because the alignment may cause problems if it's big... + // and we don't know what our chunk alignment is going to be + while (1) { + n = (stb__chunk *) malloc(16 + chunk_size); + if (n == NULL) return NULL; + + n->data_left = chunk_size - sizeof(*n); + + p = stb__try_chunk(n, size, align, pre_align); + if (p != NULL) { + n->next = c; + stb__setchunks(src, n); + + // if we just used up the whole block immediately, + // move the following chunk up + n->alloc = 1; + if (size == chunk_size) + stb__sort_chunks(src); + + return p; + } + + free(n); + chunk_size += 16+align; + } + } +} + +static stb__alloc * stb__get_context(void *context) +{ + if (context == NULL) { + return &stb__alloc_global; + } else { + int u = stb__identify(context); + // if context is chunked, grab parent + if (u == STB__chunked) { + stb__chunked *s = (stb__chunked *) context - 1; + return stb__parent(s); + } else { + return (stb__alloc *) context - 1; + } + } +} + +static void stb__insert_alloc(stb__alloc *src, stb__alloc *s) +{ + s->prevn = &src->child; + s->next = src->child; + src->child = s+1; + if (s->next) + *stb__prevn(s->next) = &s->next; +} + +static void stb__insert_nochild(stb__alloc *src, stb__nochildren *s) +{ + s->prevn = &src->child; + s->next = src->child; + src->child = s+1; + if (s->next) + *stb__prevn(s->next) = &s->next; +} + +static void * malloc_base(void *context, size_t size, stb__alloc_type t, int align) +{ + void *p; + + stb__alloc *src = stb__get_context(context); + + if (align <= 0) { + // compute worst-case C packed alignment + // e.g. a 24-byte struct is 8-aligned + int align_proposed = 1 << stb_lowbit8(size); + + if (align_proposed < 0) + align_proposed = 4; + + if (align_proposed == 0) { + if (size == 0) + align_proposed = 1; + else + align_proposed = 256; + } + + // a negative alignment means 'don't align any larger + // than this'; so -16 means we align 1,2,4,8, or 16 + + if (align < 0) { + if (align_proposed > -align) + align_proposed = -align; + } + + align = align_proposed; + } + + assert(stb_is_pow2(align)); + + // don't cause misalignment when allocating nochildren + if (t == STB__nochildren && align > 8) + t = STB__alloc; + + switch (t) { + case STB__alloc: { + stb__alloc *s = (stb__alloc *) malloc(size + sizeof(*s)); + if (s == NULL) return NULL; + p = s+1; + s->child = NULL; + stb__insert_alloc(src, s); + + stb__setchunks(s,NULL); + break; + } + + case STB__nochildren: { + stb__nochildren *s = (stb__nochildren *) malloc(size + sizeof(*s)); + if (s == NULL) return NULL; + p = s+1; + stb__insert_nochild(src, s); + break; + } + + case STB__chunk_raw: { + p = stb__alloc_chunk(src, size, align, 0); + if (p == NULL) return NULL; + break; + } + + case STB__chunked: { + stb__chunked *s; + if (align < sizeof(stb_uintptr)) align = sizeof(stb_uintptr); + s = (stb__chunked *) stb__alloc_chunk(src, size, align, sizeof(*s)); + if (s == NULL) return NULL; + stb__setparent(s, src); + p = s+1; + break; + } + + default: p = NULL; assert(0); /* NOTREACHED */ + } + + ++stb_alloc_count_alloc; + return p; +} + +void *stb_malloc_global(size_t size) +{ + return malloc_base(NULL, size, STB__alloc, stb_alloc_alignment); +} + +void *stb_malloc(void *context, size_t size) +{ + return malloc_base(context, size, STB__alloc, stb_alloc_alignment); +} + +void *stb_malloc_nofree(void *context, size_t size) +{ + return malloc_base(context, size, STB__chunked, stb_alloc_alignment); +} + +void *stb_malloc_leaf(void *context, size_t size) +{ + return malloc_base(context, size, STB__nochildren, stb_alloc_alignment); +} + +void *stb_malloc_raw(void *context, size_t size) +{ + return malloc_base(context, size, STB__chunk_raw, stb_alloc_alignment); +} + +char *stb_malloc_string(void *context, size_t size) +{ + return (char *) malloc_base(context, size, STB__chunk_raw, 1); +} + +void *stb_realloc(void *ptr, size_t newsize) +{ + stb__alloc_type t; + + if (ptr == NULL) return stb_malloc(NULL, newsize); + if (newsize == 0) { stb_free(ptr); return NULL; } + + t = stb__identify(ptr); + assert(t == STB__alloc || t == STB__nochildren); + + if (t == STB__alloc) { + stb__alloc *s = (stb__alloc *) ptr - 1; + + s = (stb__alloc *) realloc(s, newsize + sizeof(*s)); + if (s == NULL) return NULL; + + ptr = s+1; + + // update pointers + (*s->prevn) = ptr; + if (s->next) + *stb__prevn(s->next) = &s->next; + + if (s->child) + *stb__prevn(s->child) = &s->child; + + return ptr; + } else { + stb__nochildren *s = (stb__nochildren *) ptr - 1; + + s = (stb__nochildren *) realloc(ptr, newsize + sizeof(s)); + if (s == NULL) return NULL; + + // update pointers + (*s->prevn) = s+1; + if (s->next) + *stb__prevn(s->next) = &s->next; + + return s+1; + } +} + +void *stb_realloc_c(void *context, void *ptr, size_t newsize) +{ + if (ptr == NULL) return stb_malloc(context, newsize); + if (newsize == 0) { stb_free(ptr); return NULL; } + // @TODO: verify you haven't changed contexts + return stb_realloc(ptr, newsize); +} + +void stb_reassign(void *new_context, void *ptr) +{ + stb__alloc *src = stb__get_context(new_context); + + stb__alloc_type t = stb__identify(ptr); + assert(t == STB__alloc || t == STB__nochildren); + + if (t == STB__alloc) { + stb__alloc *s = (stb__alloc *) ptr - 1; + + // unlink from old + *(s->prevn) = s->next; + if (s->next) + *stb__prevn(s->next) = s->prevn; + + stb__insert_alloc(src, s); + } else { + stb__nochildren *s = (stb__nochildren *) ptr - 1; + + // unlink from old + *(s->prevn) = s->next; + if (s->next) + *stb__prevn(s->next) = s->prevn; + + stb__insert_nochild(src, s); + } +} + +#endif + + +////////////////////////////////////////////////////////////////////////////// +// +// stb_arr +// +// An stb_arr is directly useable as a pointer (use the actual type in your +// definition), but when it resizes, it returns a new pointer and you can't +// use the old one, so you have to be careful to copy-in-out as necessary. +// +// Use a NULL pointer as a 0-length array. +// +// float *my_array = NULL, *temp; +// +// // add elements on the end one at a time +// stb_arr_push(my_array, 0.0f); +// stb_arr_push(my_array, 1.0f); +// stb_arr_push(my_array, 2.0f); +// +// assert(my_array[1] == 2.0f); +// +// // add an uninitialized element at the end, then assign it +// *stb_arr_add(my_array) = 3.0f; +// +// // add three uninitialized elements at the end +// temp = stb_arr_addn(my_array,3); +// temp[0] = 4.0f; +// temp[1] = 5.0f; +// temp[2] = 6.0f; +// +// assert(my_array[5] == 5.0f); +// +// // remove the last one +// stb_arr_pop(my_array); +// +// assert(stb_arr_len(my_array) == 6); + + +#ifdef STB_MALLOC_WRAPPER + #define STB__PARAMS , char *file, int line + #define STB__ARGS , file, line +#else + #define STB__PARAMS + #define STB__ARGS +#endif + +// calling this function allocates an empty stb_arr attached to p +// (whereas NULL isn't attached to anything) +STB_EXTERN void stb_arr_malloc(void **target, void *context); + +// call this function with a non-NULL value to have all successive +// stbs that are created be attached to the associated parent. Note +// that once a given stb_arr is non-empty, it stays attached to its +// current parent, even if you call this function again. +// it turns the previous value, so you can restore it +STB_EXTERN void* stb_arr_malloc_parent(void *p); + +// simple functions written on top of other functions +#define stb_arr_empty(a) ( stb_arr_len(a) == 0 ) +#define stb_arr_add(a) ( stb_arr_addn((a),1) ) +#define stb_arr_push(a,v) ( *stb_arr_add(a)=(v) ) + +typedef struct +{ + int len, limit; + int stb_malloc; + unsigned int signature; +} stb__arr; + +#define stb_arr_signature 0x51bada7b // ends with 0123 in decimal + +// access the header block stored before the data +#define stb_arrhead(a) /*lint --e(826)*/ (((stb__arr *) (a)) - 1) +#define stb_arrhead2(a) /*lint --e(826)*/ (((stb__arr *) (a)) - 1) + +#ifdef STB_DEBUG +#define stb_arr_check(a) assert(!a || stb_arrhead(a)->signature == stb_arr_signature) +#define stb_arr_check2(a) assert(!a || stb_arrhead2(a)->signature == stb_arr_signature) +#else +#define stb_arr_check(a) ((void) 0) +#define stb_arr_check2(a) ((void) 0) +#endif + +// ARRAY LENGTH + +// get the array length; special case if pointer is NULL +#define stb_arr_len(a) (a ? stb_arrhead(a)->len : 0) +#define stb_arr_len2(a) ((stb__arr *) (a) ? stb_arrhead2(a)->len : 0) +#define stb_arr_lastn(a) (stb_arr_len(a)-1) + +// check whether a given index is valid -- tests 0 <= i < stb_arr_len(a) +#define stb_arr_valid(a,i) (a ? (int) (i) < stb_arrhead(a)->len : 0) + +// change the array length so is is exactly N entries long, creating +// uninitialized entries as needed +#define stb_arr_setlen(a,n) \ + (stb__arr_setlen((void **) &(a), sizeof(a[0]), (n))) + +// change the array length so that N is a valid index (that is, so +// it is at least N entries long), creating uninitialized entries as needed +#define stb_arr_makevalid(a,n) \ + (stb_arr_len(a) < (n)+1 ? stb_arr_setlen(a,(n)+1),(a) : (a)) + +// remove the last element of the array, returning it +#define stb_arr_pop(a) ((stb_arr_check(a), (a))[--stb_arrhead(a)->len]) + +// access the last element in the array +#define stb_arr_last(a) ((stb_arr_check(a), (a))[stb_arr_len(a)-1]) + +// is iterator at end of list? +#define stb_arr_end(a,i) ((i) >= &(a)[stb_arr_len(a)]) + +// (internal) change the allocated length of the array +#define stb_arr__grow(a,n) (stb_arr_check(a), stb_arrhead(a)->len += (n)) + +// add N new unitialized elements to the end of the array +#define stb_arr__addn(a,n) /*lint --e(826)*/ \ + ((stb_arr_len(a)+(n) > stb_arrcurmax(a)) \ + ? (stb__arr_addlen((void **) &(a),sizeof(*a),(n)),0) \ + : ((stb_arr__grow(a,n), 0))) + +// add N new unitialized elements to the end of the array, and return +// a pointer to the first new one +#define stb_arr_addn(a,n) (stb_arr__addn((a),n),(a)+stb_arr_len(a)-(n)) + +// add N new uninitialized elements starting at index 'i' +#define stb_arr_insertn(a,i,n) (stb__arr_insertn((void **) &(a), sizeof(*a), i, n)) + +// insert an element at i +#define stb_arr_insert(a,i,v) (stb__arr_insertn((void **) &(a), sizeof(*a), i, 1), ((a)[i] = v)) + +// delete N elements from the middle starting at index 'i' +#define stb_arr_deleten(a,i,n) (stb__arr_deleten((void **) &(a), sizeof(*a), i, n)) + +// delete the i'th element +#define stb_arr_delete(a,i) stb_arr_deleten(a,i,1) + +// delete the i'th element, swapping down from the end +#define stb_arr_fastdelete(a,i) \ + (stb_swap(&a[i], &a[stb_arrhead(a)->len-1], sizeof(*a)), stb_arr_pop(a)) + + +// ARRAY STORAGE + +// get the array maximum storage; special case if NULL +#define stb_arrcurmax(a) (a ? stb_arrhead(a)->limit : 0) +#define stb_arrcurmax2(a) (a ? stb_arrhead2(a)->limit : 0) + +// set the maxlength of the array to n in anticipation of further growth +#define stb_arr_setsize(a,n) (stb_arr_check(a), stb__arr_setsize((void **) &(a),sizeof((a)[0]),n)) + +// make sure maxlength is large enough for at least N new allocations +#define stb_arr_atleast(a,n) (stb_arr_len(a)+(n) > stb_arrcurmax(a) \ + ? stb_arr_setsize((a), (n)) : 0) + +// make a copy of a given array (copies contents via 'memcpy'!) +#define stb_arr_copy(a) stb__arr_copy(a, sizeof((a)[0])) + +// compute the storage needed to store all the elements of the array +#define stb_arr_storage(a) (stb_arr_len(a) * sizeof((a)[0])) + +#define stb_arr_for(v,arr) for((v)=(arr); (v) < (arr)+stb_arr_len(arr); ++(v)) + +// IMPLEMENTATION + +STB_EXTERN void stb_arr_free_(void **p); +STB_EXTERN void *stb__arr_copy_(void *p, int elem_size); +STB_EXTERN void stb__arr_setsize_(void **p, int size, int limit STB__PARAMS); +STB_EXTERN void stb__arr_setlen_(void **p, int size, int newlen STB__PARAMS); +STB_EXTERN void stb__arr_addlen_(void **p, int size, int addlen STB__PARAMS); +STB_EXTERN void stb__arr_deleten_(void **p, int size, int loc, int n STB__PARAMS); +STB_EXTERN void stb__arr_insertn_(void **p, int size, int loc, int n STB__PARAMS); + +#define stb_arr_free(p) stb_arr_free_((void **) &(p)) +#define stb__arr_copy stb__arr_copy_ + +#ifndef STB_MALLOC_WRAPPER + #define stb__arr_setsize stb__arr_setsize_ + #define stb__arr_setlen stb__arr_setlen_ + #define stb__arr_addlen stb__arr_addlen_ + #define stb__arr_deleten stb__arr_deleten_ + #define stb__arr_insertn stb__arr_insertn_ +#else + #define stb__arr_addlen(p,s,n) stb__arr_addlen_(p,s,n,__FILE__,__LINE__) + #define stb__arr_setlen(p,s,n) stb__arr_setlen_(p,s,n,__FILE__,__LINE__) + #define stb__arr_setsize(p,s,n) stb__arr_setsize_(p,s,n,__FILE__,__LINE__) + #define stb__arr_deleten(p,s,i,n) stb__arr_deleten_(p,s,i,n,__FILE__,__LINE__) + #define stb__arr_insertn(p,s,i,n) stb__arr_insertn_(p,s,i,n,__FILE__,__LINE__) +#endif + +#ifdef STB_DEFINE +static void *stb__arr_context; + +void *stb_arr_malloc_parent(void *p) +{ + void *q = stb__arr_context; + stb__arr_context = p; + return q; +} + +void stb_arr_malloc(void **target, void *context) +{ + stb__arr *q = (stb__arr *) stb_malloc(context, sizeof(*q)); + q->len = q->limit = 0; + q->stb_malloc = 1; + q->signature = stb_arr_signature; + *target = (void *) (q+1); +} + +static void * stb__arr_malloc(int size) +{ + if (stb__arr_context) + return stb_malloc(stb__arr_context, size); + return malloc(size); +} + +void * stb__arr_copy_(void *p, int elem_size) +{ + stb__arr *q; + if (p == NULL) return p; + q = (stb__arr *) stb__arr_malloc(sizeof(*q) + elem_size * stb_arrhead2(p)->limit); + stb_arr_check2(p); + memcpy(q, stb_arrhead2(p), sizeof(*q) + elem_size * stb_arrhead2(p)->len); + q->stb_malloc = !!stb__arr_context; + return q+1; +} + +void stb_arr_free_(void **pp) +{ + void *p = *pp; + stb_arr_check2(p); + if (p) { + stb__arr *q = stb_arrhead2(p); + if (q->stb_malloc) + stb_free(q); + else + free(q); + } + *pp = NULL; +} + +static void stb__arrsize_(void **pp, int size, int limit, int len STB__PARAMS) +{ + void *p = *pp; + stb__arr *a; + stb_arr_check2(p); + if (p == NULL) { + if (len == 0 && size == 0) return; + a = (stb__arr *) stb__arr_malloc(sizeof(*a) + size*limit); + a->limit = limit; + a->len = len; + a->stb_malloc = !!stb__arr_context; + a->signature = stb_arr_signature; + } else { + a = stb_arrhead2(p); + a->len = len; + if (a->limit < limit) { + void *p; + if (a->limit >= 4 && limit < a->limit * 2) + limit = a->limit * 2; + if (a->stb_malloc) + p = stb_realloc(a, sizeof(*a) + limit*size); + else + #ifdef STB_MALLOC_WRAPPER + p = stb__realloc(a, sizeof(*a) + limit*size, file, line); + #else + p = realloc(a, sizeof(*a) + limit*size); + #endif + if (p) { + a = (stb__arr *) p; + a->limit = limit; + } else { + // throw an error! + } + } + } + a->len = stb_min(a->len, a->limit); + *pp = a+1; +} + +void stb__arr_setsize_(void **pp, int size, int limit STB__PARAMS) +{ + void *p = *pp; + stb_arr_check2(p); + stb__arrsize_(pp, size, limit, stb_arr_len2(p) STB__ARGS); +} + +void stb__arr_setlen_(void **pp, int size, int newlen STB__PARAMS) +{ + void *p = *pp; + stb_arr_check2(p); + if (stb_arrcurmax2(p) < newlen || p == NULL) { + stb__arrsize_(pp, size, newlen, newlen STB__ARGS); + } else { + stb_arrhead2(p)->len = newlen; + } +} + +void stb__arr_addlen_(void **p, int size, int addlen STB__PARAMS) +{ + stb__arr_setlen_(p, size, stb_arr_len2(*p) + addlen STB__ARGS); +} + +void stb__arr_insertn_(void **pp, int size, int i, int n STB__PARAMS) +{ + void *p = *pp; + if (n) { + int z; + + if (p == NULL) { + stb__arr_addlen_(pp, size, n STB__ARGS); + return; + } + + z = stb_arr_len2(p); + stb__arr_addlen_(&p, size, n STB__ARGS); + memmove((char *) p + (i+n)*size, (char *) p + i*size, size * (z-i)); + } + *pp = p; +} + +void stb__arr_deleten_(void **pp, int size, int i, int n STB__PARAMS) +{ + void *p = *pp; + if (n) { + memmove((char *) p + i*size, (char *) p + (i+n)*size, size * (stb_arr_len2(p)-(i+n))); + stb_arrhead2(p)->len -= n; + } + *pp = p; +} + +#endif + +////////////////////////////////////////////////////////////////////////////// +// +// Hashing +// +// typical use for this is to make a power-of-two hash table. +// +// let N = size of table (2^n) +// let H = stb_hash(str) +// let S = stb_rehash(H) | 1 +// +// then hash probe sequence P(i) for i=0..N-1 +// P(i) = (H + S*i) & (N-1) +// +// the idea is that H has 32 bits of hash information, but the +// table has only, say, 2^20 entries so only uses 20 of the bits. +// then by rehashing the original H we get 2^12 different probe +// sequences for a given initial probe location. (So it's optimal +// for 64K tables and its optimality decreases past that.) +// +// ok, so I've added something that generates _two separate_ +// 32-bit hashes simultaneously which should scale better to +// very large tables. + + +STB_EXTERN unsigned int stb_hash(char *str); +STB_EXTERN unsigned int stb_hashptr(void *p); +STB_EXTERN unsigned int stb_hashlen(char *str, int len); +STB_EXTERN unsigned int stb_rehash_improved(unsigned int v); +STB_EXTERN unsigned int stb_hash_fast(void *p, int len); +STB_EXTERN unsigned int stb_hash2(char *str, unsigned int *hash2_ptr); +STB_EXTERN unsigned int stb_hash_number(unsigned int hash); + +#define stb_rehash(x) ((x) + ((x) >> 6) + ((x) >> 19)) + +#ifdef STB_DEFINE +unsigned int stb_hash(char *str) +{ + unsigned int hash = 0; + while (*str) + hash = (hash << 7) + (hash >> 25) + *str++; + return hash + (hash >> 16); +} + +unsigned int stb_hashlen(char *str, int len) +{ + unsigned int hash = 0; + while (len-- > 0 && *str) + hash = (hash << 7) + (hash >> 25) + *str++; + return hash + (hash >> 16); +} + +unsigned int stb_hashptr(void *p) +{ + unsigned int x = (unsigned int) p; + + // typically lacking in low bits and high bits + x = stb_rehash(x); + x += x << 16; + + // pearson's shuffle + x ^= x << 3; + x += x >> 5; + x ^= x << 2; + x += x >> 15; + x ^= x << 10; + return stb_rehash(x); +} + +unsigned int stb_rehash_improved(unsigned int v) +{ + return stb_hashptr((void *)(size_t) v); +} + +unsigned int stb_hash2(char *str, unsigned int *hash2_ptr) +{ + unsigned int hash1 = 0x3141592c; + unsigned int hash2 = 0x77f044ed; + while (*str) { + hash1 = (hash1 << 7) + (hash1 >> 25) + *str; + hash2 = (hash2 << 11) + (hash2 >> 21) + *str; + ++str; + } + *hash2_ptr = hash2 + (hash1 >> 16); + return hash1 + (hash2 >> 16); +} + +// Paul Hsieh hash +#define stb__get16_slow(p) ((p)[0] + ((p)[1] << 8)) +#if defined(_MSC_VER) + #define stb__get16(p) (*((unsigned short *) (p))) +#else + #define stb__get16(p) stb__get16_slow(p) +#endif + +unsigned int stb_hash_fast(void *p, int len) +{ + unsigned char *q = (unsigned char *) p; + unsigned int hash = len; + + if (len <= 0 || q == NULL) return 0; + + /* Main loop */ + if (((int) q & 1) == 0) { + for (;len > 3; len -= 4) { + unsigned int val; + hash += stb__get16(q); + val = (stb__get16(q+2) << 11); + hash = (hash << 16) ^ hash ^ val; + q += 4; + hash += hash >> 11; + } + } else { + for (;len > 3; len -= 4) { + unsigned int val; + hash += stb__get16_slow(q); + val = (stb__get16_slow(q+2) << 11); + hash = (hash << 16) ^ hash ^ val; + q += 4; + hash += hash >> 11; + } + } + + /* Handle end cases */ + switch (len) { + case 3: hash += stb__get16_slow(q); + hash ^= hash << 16; + hash ^= q[2] << 18; + hash += hash >> 11; + break; + case 2: hash += stb__get16_slow(q); + hash ^= hash << 11; + hash += hash >> 17; + break; + case 1: hash += q[0]; + hash ^= hash << 10; + hash += hash >> 1; + break; + case 0: break; + } + + /* Force "avalanching" of final 127 bits */ + hash ^= hash << 3; + hash += hash >> 5; + hash ^= hash << 4; + hash += hash >> 17; + hash ^= hash << 25; + hash += hash >> 6; + + return hash; +} + +unsigned int stb_hash_number(unsigned int hash) +{ + hash ^= hash << 3; + hash += hash >> 5; + hash ^= hash << 4; + hash += hash >> 17; + hash ^= hash << 25; + hash += hash >> 6; + return hash; +} + +#endif + +////////////////////////////////////////////////////////////////////////////// +// +// Perfect hashing for ints/pointers +// +// This is mainly useful for making faster pointer-indexed tables +// that don't change frequently. E.g. for stb_ischar(). +// + +typedef struct +{ + stb_uint32 addend; + stb_uint multiplicand; + stb_uint b_mask; + stb_uint8 small_bmap[16]; + stb_uint16 *large_bmap; + + stb_uint table_mask; + stb_uint32 *table; +} stb_perfect; + +STB_EXTERN int stb_perfect_create(stb_perfect *,unsigned int*,int n); +STB_EXTERN void stb_perfect_destroy(stb_perfect *); +STB_EXTERN int stb_perfect_hash(stb_perfect *, unsigned int x); +extern int stb_perfect_hash_max_failures; + +#ifdef STB_DEFINE + +int stb_perfect_hash_max_failures; + +int stb_perfect_hash(stb_perfect *p, unsigned int x) +{ + stb_uint m = x * p->multiplicand; + stb_uint y = x >> 16; + stb_uint bv = (m >> 24) + y; + stb_uint av = (m + y) >> 12; + if (p->table == NULL) return -1; // uninitialized table fails + bv &= p->b_mask; + av &= p->table_mask; + if (p->large_bmap) + av ^= p->large_bmap[bv]; + else + av ^= p->small_bmap[bv]; + return p->table[av] == x ? av : -1; +} + +static void stb__perfect_prehash(stb_perfect *p, stb_uint x, stb_uint16 *a, stb_uint16 *b) +{ + stb_uint m = x * p->multiplicand; + stb_uint y = x >> 16; + stb_uint bv = (m >> 24) + y; + stb_uint av = (m + y) >> 12; + bv &= p->b_mask; + av &= p->table_mask; + *b = bv; + *a = av; +} + +static unsigned long stb__perfect_rand(void) +{ + static unsigned long stb__rand; + stb__rand = stb__rand * 2147001325 + 715136305; + return 0x31415926 ^ ((stb__rand >> 16) + (stb__rand << 16)); +} + +typedef struct { + unsigned short count; + unsigned short b; + unsigned short map; + unsigned short *entries; +} stb__slot; + +static int stb__slot_compare(const void *p, const void *q) +{ + stb__slot *a = (stb__slot *) p; + stb__slot *b = (stb__slot *) q; + return a->count > b->count ? -1 : a->count < b->count; // sort large to small +} + +int stb_perfect_create(stb_perfect *p, unsigned int *v, int n) +{ + unsigned int buffer1[64], buffer2[64], buffer3[64], buffer4[64], buffer5[32]; + unsigned short *as = (unsigned short *) stb_temp(buffer1, sizeof(*v)*n); + unsigned short *bs = (unsigned short *) stb_temp(buffer2, sizeof(*v)*n); + unsigned short *entries = (unsigned short *) stb_temp(buffer4, sizeof(*entries) * n); + int size = 1 << stb_log2_ceil(n), bsize=8; + int failure = 0,i,j,k; + + assert(n <= 32768); + p->large_bmap = NULL; + + for(;;) { + stb__slot *bcount = (stb__slot *) stb_temp(buffer3, sizeof(*bcount) * bsize); + unsigned short *bloc = (unsigned short *) stb_temp(buffer5, sizeof(*bloc) * bsize); + unsigned short *e; + int bad=0; + + p->addend = stb__perfect_rand(); + p->multiplicand = stb__perfect_rand() | 1; + p->table_mask = size-1; + p->b_mask = bsize-1; + p->table = (stb_uint32 *) malloc(size * sizeof(*p->table)); + + for (i=0; i < bsize; ++i) { + bcount[i].b = i; + bcount[i].count = 0; + bcount[i].map = 0; + } + for (i=0; i < n; ++i) { + stb__perfect_prehash(p, v[i], as+i, bs+i); + ++bcount[bs[i]].count; + } + qsort(bcount, bsize, sizeof(*bcount), stb__slot_compare); + e = entries; // now setup up their entries index + for (i=0; i < bsize; ++i) { + bcount[i].entries = e; + e += bcount[i].count; + bcount[i].count = 0; + bloc[bcount[i].b] = i; + } + // now fill them out + for (i=0; i < n; ++i) { + int b = bs[i]; + int w = bloc[b]; + bcount[w].entries[bcount[w].count++] = i; + } + stb_tempfree(buffer5,bloc); + // verify + for (i=0; i < bsize; ++i) + for (j=0; j < bcount[i].count; ++j) + assert(bs[bcount[i].entries[j]] == bcount[i].b); + memset(p->table, 0, size*sizeof(*p->table)); + + // check if any b has duplicate a + for (i=0; i < bsize; ++i) { + if (bcount[i].count > 1) { + for (j=0; j < bcount[i].count; ++j) { + if (p->table[as[bcount[i].entries[j]]]) + bad = 1; + p->table[as[bcount[i].entries[j]]] = 1; + } + for (j=0; j < bcount[i].count; ++j) { + p->table[as[bcount[i].entries[j]]] = 0; + } + if (bad) break; + } + } + + if (!bad) { + // go through the bs and populate the table, first fit + for (i=0; i < bsize; ++i) { + if (bcount[i].count) { + // go through the candidate table[b] values + for (j=0; j < size; ++j) { + // go through the a values and see if they fit + for (k=0; k < bcount[i].count; ++k) { + int a = as[bcount[i].entries[k]]; + if (p->table[(a^j)&p->table_mask]) { + break; // fails + } + } + // if succeeded, accept + if (k == bcount[i].count) { + bcount[i].map = j; + for (k=0; k < bcount[i].count; ++k) { + int a = as[bcount[i].entries[k]]; + p->table[(a^j)&p->table_mask] = 1; + } + break; + } + } + if (j == size) + break; // no match for i'th entry, so break out in failure + } + } + if (i == bsize) { + // success... fill out map + if (bsize <= 16 && size <= 256) { + p->large_bmap = NULL; + for (i=0; i < bsize; ++i) + p->small_bmap[bcount[i].b] = (stb_uint8) bcount[i].map; + } else { + p->large_bmap = (unsigned short *) malloc(sizeof(*p->large_bmap) * bsize); + for (i=0; i < bsize; ++i) + p->large_bmap[bcount[i].b] = bcount[i].map; + } + + // initialize table to v[0], so empty slots will fail + for (i=0; i < size; ++i) + p->table[i] = v[0]; + + for (i=0; i < n; ++i) + if (p->large_bmap) + p->table[as[i] ^ p->large_bmap[bs[i]]] = v[i]; + else + p->table[as[i] ^ p->small_bmap[bs[i]]] = v[i]; + + // and now validate that none of them collided + for (i=0; i < n; ++i) + assert(stb_perfect_hash(p, v[i]) >= 0); + + stb_tempfree(buffer3, bcount); + break; + } + } + free(p->table); + p->table = NULL; + stb_tempfree(buffer3, bcount); + + ++failure; + if (failure >= 4 && bsize < size) bsize *= 2; + if (failure >= 8 && (failure & 3) == 0 && size < 4*n) { + size *= 2; + bsize *= 2; + } + if (failure == 6) { + // make sure the input data is unique, so we don't infinite loop + unsigned int *data = (unsigned int *) stb_temp(buffer3, n * sizeof(*data)); + memcpy(data, v, sizeof(*data) * n); + qsort(data, n, sizeof(*data), stb_intcmp(0)); + for (i=1; i < n; ++i) { + if (data[i] == data[i-1]) + size = 0; // size is return value, so 0 it + } + stb_tempfree(buffer3, data); + if (!size) break; + } + } + + if (failure > stb_perfect_hash_max_failures) + stb_perfect_hash_max_failures = failure; + + stb_tempfree(buffer1, as); + stb_tempfree(buffer2, bs); + stb_tempfree(buffer4, entries); + + return size; +} + +void stb_perfect_destroy(stb_perfect *p) +{ + if (p->large_bmap) free(p->large_bmap); + if (p->table ) free(p->table); + p->large_bmap = NULL; + p->table = NULL; + p->b_mask = 0; + p->table_mask = 0; +} +#endif + +////////////////////////////////////////////////////////////////////////////// +// +// Perfect hash clients + +STB_EXTERN int stb_ischar(char s, char *set); + +#ifdef STB_DEFINE + +int stb_ischar(char c, char *set) +{ + static unsigned char bit[8] = { 1,2,4,8,16,32,64,128 }; + static stb_perfect p; + static unsigned char (*tables)[256]; + static char ** sets = NULL; + + int z = stb_perfect_hash(&p, (int) set); + if (z < 0) { + int i,k,n,j,f; + // special code that means free all existing data + if (set == NULL) { + stb_arr_free(sets); + free(tables); + tables = NULL; + stb_perfect_destroy(&p); + return 0; + } + stb_arr_push(sets, set); + stb_perfect_destroy(&p); + n = stb_perfect_create(&p, (unsigned int *) (char **) sets, stb_arr_len(sets)); + assert(n != 0); + k = (n+7) >> 3; + tables = (unsigned char (*)[256]) realloc(tables, sizeof(*tables) * k); + memset(tables, 0, sizeof(*tables) * k); + for (i=0; i < stb_arr_len(sets); ++i) { + k = stb_perfect_hash(&p, (int) sets[i]); + assert(k >= 0); + n = k >> 3; + f = bit[k&7]; + for (j=0; !j || sets[i][j]; ++j) { + tables[n][(unsigned char) sets[i][j]] |= f; + } + } + z = stb_perfect_hash(&p, (int) set); + } + return tables[z >> 3][(unsigned char) c] & bit[z & 7]; +} + +#endif + +////////////////////////////////////////////////////////////////////////////// +// +// Instantiated data structures +// +// This is an attempt to implement a templated data structure. +// +// Hash table: call stb_define_hash(TYPE,N,KEY,K1,K2,HASH,VALUE) +// TYPE -- will define a structure type containing the hash table +// N -- the name, will prefix functions named: +// N create +// N destroy +// N get +// N set, N add, N update, +// N remove +// KEY -- the type of the key. 'x == y' must be valid +// K1,K2 -- keys never used by the app, used as flags in the hashtable +// HASH -- a piece of code ending with 'return' that hashes key 'k' +// VALUE -- the type of the value. 'x = y' must be valid +// +// Note that stb_define_hash_base can be used to define more sophisticated +// hash tables, e.g. those that make copies of the key or use special +// comparisons (e.g. strcmp). + +#define STB_(prefix,name) stb__##prefix##name +#define STB__(prefix,name) prefix##name +#define STB__use(x) x +#define STB__skip(x) + +#define stb_declare_hash(PREFIX,TYPE,N,KEY,VALUE) \ + typedef struct stb__st_##TYPE TYPE;\ + PREFIX int STB__(N, init)(TYPE *h, int count);\ + PREFIX int STB__(N, memory_usage)(TYPE *h);\ + PREFIX TYPE * STB__(N, create)(void);\ + PREFIX TYPE * STB__(N, copy)(TYPE *h);\ + PREFIX void STB__(N, destroy)(TYPE *h);\ + PREFIX int STB__(N,get_flag)(TYPE *a, KEY k, VALUE *v);\ + PREFIX VALUE STB__(N,get)(TYPE *a, KEY k);\ + PREFIX int STB__(N, set)(TYPE *a, KEY k, VALUE v);\ + PREFIX int STB__(N, add)(TYPE *a, KEY k, VALUE v);\ + PREFIX int STB__(N, update)(TYPE*a,KEY k,VALUE v);\ + PREFIX int STB__(N, remove)(TYPE *a, KEY k, VALUE *v); + +#define STB_nocopy(x) (x) +#define STB_nodelete(x) 0 +#define STB_nofields +#define STB_nonullvalue(x) +#define STB_nullvalue(x) x +#define STB_safecompare(x) x +#define STB_nosafe(x) +#define STB_noprefix + +#ifdef __GNUC__ +#define STB__nogcc(x) +#else +#define STB__nogcc(x) x +#endif + +#define stb_define_hash_base(PREFIX,TYPE,FIELDS,N,NC,LOAD_FACTOR, \ + KEY,EMPTY,DEL,COPY,DISPOSE,SAFE, \ + VCOMPARE,CCOMPARE,HASH, \ + VALUE,HASVNULL,VNULL) \ + \ +typedef struct \ +{ \ + KEY k; \ + VALUE v; \ +} STB_(N,_hashpair); \ + \ +STB__nogcc( typedef struct stb__st_##TYPE TYPE; ) \ +struct stb__st_##TYPE { \ + FIELDS \ + STB_(N,_hashpair) *table; \ + unsigned int mask; \ + int count, limit; \ + int deleted; \ + \ + int delete_threshhold; \ + int grow_threshhold; \ + int shrink_threshhold; \ + unsigned char alloced, has_empty, has_del; \ + VALUE ev; VALUE dv; \ +}; \ + \ +static unsigned int STB_(N, hash)(KEY k) \ +{ \ + HASH \ +} \ + \ +PREFIX int STB__(N, init)(TYPE *h, int count) \ +{ \ + int i; \ + if (count < 4) count = 4; \ + h->limit = count; \ + h->count = 0; \ + h->mask = count-1; \ + h->deleted = 0; \ + h->grow_threshhold = (int) (count * LOAD_FACTOR); \ + h->has_empty = h->has_del = 0; \ + h->alloced = 0; \ + if (count <= 64) \ + h->shrink_threshhold = 0; \ + else \ + h->shrink_threshhold = (int) (count * (LOAD_FACTOR/2.25)); \ + h->delete_threshhold = (int) (count * (1-LOAD_FACTOR)/2); \ + h->table = (STB_(N,_hashpair)*) malloc(sizeof(h->table[0]) * count); \ + if (h->table == NULL) return 0; \ + /* ideally this gets turned into a memset32 automatically */ \ + for (i=0; i < count; ++i) \ + h->table[i].k = EMPTY; \ + return 1; \ +} \ + \ +PREFIX int STB__(N, memory_usage)(TYPE *h) \ +{ \ + return sizeof(*h) + h->limit * sizeof(h->table[0]); \ +} \ + \ +PREFIX TYPE * STB__(N, create)(void) \ +{ \ + TYPE *h = (TYPE *) malloc(sizeof(*h)); \ + if (h) { \ + if (STB__(N, init)(h, 16)) \ + h->alloced = 1; \ + else { free(h); h=NULL; } \ + } \ + return h; \ +} \ + \ +PREFIX void STB__(N, destroy)(TYPE *a) \ +{ \ + int i; \ + for (i=0; i < a->limit; ++i) \ + if (!CCOMPARE(a->table[i].k,EMPTY) && !CCOMPARE(a->table[i].k, DEL)) \ + DISPOSE(a->table[i].k); \ + free(a->table); \ + if (a->alloced) \ + free(a); \ +} \ + \ +static void STB_(N, rehash)(TYPE *a, int count); \ + \ +PREFIX int STB__(N,get_flag)(TYPE *a, KEY k, VALUE *v) \ +{ \ + unsigned int h = STB_(N, hash)(k); \ + unsigned int n = h & a->mask, s; \ + if (CCOMPARE(k,EMPTY)){ if (a->has_empty) *v = a->ev; return a->has_empty;}\ + if (CCOMPARE(k,DEL)) { if (a->has_del ) *v = a->dv; return a->has_del; }\ + if (CCOMPARE(a->table[n].k,EMPTY)) return 0; \ + SAFE(if (!CCOMPARE(a->table[n].k,DEL))) \ + if (VCOMPARE(a->table[n].k,k)) { *v = a->table[n].v; return 1; } \ + s = stb_rehash(h) | 1; \ + for(;;) { \ + n = (n + s) & a->mask; \ + if (CCOMPARE(a->table[n].k,EMPTY)) return 0; \ + SAFE(if (CCOMPARE(a->table[n].k,DEL)) continue;) \ + if (VCOMPARE(a->table[n].k,k)) \ + { *v = a->table[n].v; return 1; } \ + } \ +} \ + \ +HASVNULL( \ + PREFIX VALUE STB__(N,get)(TYPE *a, KEY k) \ + { \ + VALUE v; \ + if (STB__(N,get_flag)(a,k,&v)) return v; \ + else return VNULL; \ + } \ +) \ + \ +PREFIX int STB__(N,getkey)(TYPE *a, KEY k, KEY *kout) \ +{ \ + unsigned int h = STB_(N, hash)(k); \ + unsigned int n = h & a->mask, s; \ + if (CCOMPARE(k,EMPTY)||CCOMPARE(k,DEL)) return 0; \ + if (CCOMPARE(a->table[n].k,EMPTY)) return 0; \ + SAFE(if (!CCOMPARE(a->table[n].k,DEL))) \ + if (VCOMPARE(a->table[n].k,k)) { *kout = a->table[n].k; return 1; } \ + s = stb_rehash(h) | 1; \ + for(;;) { \ + n = (n + s) & a->mask; \ + if (CCOMPARE(a->table[n].k,EMPTY)) return 0; \ + SAFE(if (CCOMPARE(a->table[n].k,DEL)) continue;) \ + if (VCOMPARE(a->table[n].k,k)) \ + { *kout = a->table[n].k; return 1; } \ + } \ +} \ + \ +static int STB_(N,addset)(TYPE *a, KEY k, VALUE v, \ + int allow_new, int allow_old, int copy) \ +{ \ + unsigned int h = STB_(N, hash)(k); \ + unsigned int n = h & a->mask; \ + int b = -1; \ + if (CCOMPARE(k,EMPTY)) { \ + if (a->has_empty ? allow_old : allow_new) { \ + n=a->has_empty; a->ev = v; a->has_empty = 1; return !n; \ + } else return 0; \ + } \ + if (CCOMPARE(k,DEL)) { \ + if (a->has_del ? allow_old : allow_new) { \ + n=a->has_del; a->dv = v; a->has_del = 1; return !n; \ + } else return 0; \ + } \ + if (!CCOMPARE(a->table[n].k, EMPTY)) { \ + unsigned int s; \ + if (CCOMPARE(a->table[n].k, DEL)) \ + b = n; \ + else if (VCOMPARE(a->table[n].k,k)) { \ + if (allow_old) \ + a->table[n].v = v; \ + return !allow_new; \ + } \ + s = stb_rehash(h) | 1; \ + for(;;) { \ + n = (n + s) & a->mask; \ + if (CCOMPARE(a->table[n].k, EMPTY)) break; \ + if (CCOMPARE(a->table[n].k, DEL)) { \ + if (b < 0) b = n; \ + } else if (VCOMPARE(a->table[n].k,k)) { \ + if (allow_old) \ + a->table[n].v = v; \ + return !allow_new; \ + } \ + } \ + } \ + if (!allow_new) return 0; \ + if (b < 0) b = n; else --a->deleted; \ + a->table[b].k = copy ? COPY(k) : k; \ + a->table[b].v = v; \ + ++a->count; \ + if (a->count > a->grow_threshhold) \ + STB_(N,rehash)(a, a->limit*2); \ + return 1; \ +} \ + \ +PREFIX int STB__(N, set)(TYPE *a, KEY k, VALUE v){return STB_(N,addset)(a,k,v,1,1,1);}\ +PREFIX int STB__(N, add)(TYPE *a, KEY k, VALUE v){return STB_(N,addset)(a,k,v,1,0,1);}\ +PREFIX int STB__(N, update)(TYPE*a,KEY k,VALUE v){return STB_(N,addset)(a,k,v,0,1,1);}\ + \ +PREFIX int STB__(N, remove)(TYPE *a, KEY k, VALUE *v) \ +{ \ + unsigned int h = STB_(N, hash)(k); \ + unsigned int n = h & a->mask, s; \ + if (CCOMPARE(k,EMPTY)) { if (a->has_empty) { if(v)*v = a->ev; a->has_empty=0; return 1; } return 0; } \ + if (CCOMPARE(k,DEL)) { if (a->has_del ) { if(v)*v = a->dv; a->has_del =0; return 1; } return 0; } \ + if (CCOMPARE(a->table[n].k,EMPTY)) return 0; \ + if (SAFE(CCOMPARE(a->table[n].k,DEL) || ) !VCOMPARE(a->table[n].k,k)) { \ + s = stb_rehash(h) | 1; \ + for(;;) { \ + n = (n + s) & a->mask; \ + if (CCOMPARE(a->table[n].k,EMPTY)) return 0; \ + SAFE(if (CCOMPARE(a->table[n].k, DEL)) continue;) \ + if (VCOMPARE(a->table[n].k,k)) break; \ + } \ + } \ + DISPOSE(a->table[n].k); \ + a->table[n].k = DEL; \ + --a->count; \ + ++a->deleted; \ + if (v != NULL) \ + *v = a->table[n].v; \ + if (a->count < a->shrink_threshhold) \ + STB_(N, rehash)(a, a->limit >> 1); \ + else if (a->deleted > a->delete_threshhold) \ + STB_(N, rehash)(a, a->limit); \ + return 1; \ +} \ + \ +PREFIX TYPE * STB__(NC, copy)(TYPE *a) \ +{ \ + int i; \ + TYPE *h = (TYPE *) malloc(sizeof(*h)); \ + if (!h) return NULL; \ + if (!STB__(N, init)(h, a->limit)) { free(h); return NULL; } \ + h->count = a->count; \ + h->deleted = a->deleted; \ + h->alloced = 1; \ + h->ev = a->ev; h->dv = a->dv; \ + h->has_empty = a->has_empty; h->has_del = a->has_del; \ + memcpy(h->table, a->table, h->limit * sizeof(h->table[0])); \ + for (i=0; i < a->limit; ++i) \ + if (!CCOMPARE(h->table[i].k,EMPTY) && !CCOMPARE(h->table[i].k,DEL)) \ + h->table[i].k = COPY(h->table[i].k); \ + return h; \ +} \ + \ +static void STB_(N, rehash)(TYPE *a, int count) \ +{ \ + int i; \ + TYPE b; \ + STB__(N, init)(&b, count); \ + for (i=0; i < a->limit; ++i) \ + if (!CCOMPARE(a->table[i].k,EMPTY) && !CCOMPARE(a->table[i].k,DEL)) \ + STB_(N,addset)(&b, a->table[i].k, a->table[i].v,1,1,0); \ + free(a->table); \ + a->table = b.table; \ + a->mask = b.mask; \ + a->count = b.count; \ + a->limit = b.limit; \ + a->deleted = b.deleted; \ + a->delete_threshhold = b.delete_threshhold; \ + a->grow_threshhold = b.grow_threshhold; \ + a->shrink_threshhold = b.shrink_threshhold; \ +} + +#define STB_equal(a,b) ((a) == (b)) + +#define stb_define_hash(TYPE,N,KEY,EMPTY,DEL,HASH,VALUE) \ + stb_define_hash_base(STB_noprefix, TYPE,STB_nofields,N,NC,0.85f, \ + KEY,EMPTY,DEL,STB_nocopy,STB_nodelete,STB_nosafe, \ + STB_equal,STB_equal,HASH, \ + VALUE,STB_nonullvalue,0) + +#define stb_define_hash_vnull(TYPE,N,KEY,EMPTY,DEL,HASH,VALUE,VNULL) \ + stb_define_hash_base(STB_noprefix, TYPE,STB_nofields,N,NC,0.85f, \ + KEY,EMPTY,DEL,STB_nocopy,STB_nodelete,STB_nosafe, \ + STB_equal,STB_equal,HASH, \ + VALUE,STB_nullvalue,VNULL) + +////////////////////////////////////////////////////////////////////////////// +// +// stb_ptrmap +// +// An stb_ptrmap data structure is an O(1) hash table between pointers. One +// application is to let you store "extra" data associated with pointers, +// which is why it was originally called stb_extra. + +stb_declare_hash(STB_EXTERN, stb_ptrmap, stb_ptrmap_, void *, void *) +stb_declare_hash(STB_EXTERN, stb_idict, stb_idict_, stb_int32, stb_int32) + +STB_EXTERN void stb_ptrmap_delete(stb_ptrmap *e, void (*free_func)(void *)); +STB_EXTERN stb_ptrmap *stb_ptrmap_new(void); + +STB_EXTERN stb_idict * stb_idict_new_size(int size); +STB_EXTERN void stb_idict_remove_all(stb_idict *e); + +#ifdef STB_DEFINE + +#define STB_EMPTY ((void *) 2) +#define STB_EDEL ((void *) 6) + +stb_define_hash_base(STB_noprefix,stb_ptrmap, STB_nofields, stb_ptrmap_,stb_ptrmap_,0.85f, + void *,STB_EMPTY,STB_EDEL,STB_nocopy,STB_nodelete,STB_nosafe, + STB_equal,STB_equal,return stb_hashptr(k);, + void *,STB_nullvalue,NULL) + +stb_ptrmap *stb_ptrmap_new(void) +{ + return stb_ptrmap_create(); +} + +void stb_ptrmap_delete(stb_ptrmap *e, void (*free_func)(void *)) +{ + int i; + if (free_func) + for (i=0; i < e->limit; ++i) + if (e->table[i].k != STB_EMPTY && e->table[i].k != STB_EDEL) { + if (free_func == free) + free(e->table[i].v); // allow STB_MALLOC_WRAPPER to operate + else + free_func(e->table[i].v); + } + stb_ptrmap_destroy(e); +} + +// extra fields needed for stua_dict +#define STB_IEMPTY ((int) 1) +#define STB_IDEL ((int) 3) +stb_define_hash_base(STB_noprefix, stb_idict, short type; short gc; STB_nofields, stb_idict_,stb_idict_,0.85f, + stb_int32,STB_IEMPTY,STB_IDEL,STB_nocopy,STB_nodelete,STB_nosafe, + STB_equal,STB_equal, + return stb_rehash_improved(k);,stb_int32,STB_nonullvalue,0) + +stb_idict * stb_idict_new_size(int size) +{ + stb_idict *e = (stb_idict *) malloc(sizeof(*e)); + if (e) { + if (!stb_is_pow2(size)) + size = 1 << stb_log2_ceil(size); + stb_idict_init(e, size); + e->alloced = 1; + } + return e; +} + +void stb_idict_remove_all(stb_idict *e) +{ + int n; + for (n=0; n < e->limit; ++n) + e->table[n].k = STB_IEMPTY; + e->has_empty = e->has_del = 0; +} +#endif + +////////////////////////////////////////////////////////////////////////////// +// +// stb_sparse_ptr_matrix +// +// An stb_ptrmap data structure is an O(1) hash table storing an arbitrary +// block of data for a given pair of pointers. +// +// If create=0, returns + +typedef struct stb__st_stb_spmatrix stb_spmatrix; + +STB_EXTERN stb_spmatrix * stb_sparse_ptr_matrix_new(int val_size); +STB_EXTERN void stb_sparse_ptr_matrix_free(stb_spmatrix *z); +STB_EXTERN void * stb_sparse_ptr_matrix_get(stb_spmatrix *z, void *a, void *b, int create); + +#ifdef STB_DEFINE +typedef struct +{ + void *a; + void *b; +} stb__ptrpair; + +static stb__ptrpair stb__ptrpair_empty = { (void *) 1, (void *) 1 }; +static stb__ptrpair stb__ptrpair_del = { (void *) 2, (void *) 2 }; + +#define STB__equal_ptrpair(x,y) ((x).a == (y).a && (x).b == (y).b) + +stb_define_hash_base(static, stb_spmatrix, int val_size; void *arena;, stb__spmatrix_,stb__spmatrix_, 0.85, + stb__ptrpair, stb__ptrpair_empty, stb__ptrpair_del, + STB_nocopy, STB_nodelete, STB_nosafe, + STB__equal_ptrpair, STB__equal_ptrpair, return stb_rehash(stb_hashptr(k.a))+stb_hashptr(k.b);, + void *, STB_nullvalue, 0) + +stb_spmatrix *stb_sparse_ptr_matrix_new(int val_size) +{ + stb_spmatrix *m = stb__spmatrix_create(); + if (m) m->val_size = val_size; + if (m) m->arena = stb_malloc_global(1); + return m; +} + +void stb_sparse_ptr_matrix_free(stb_spmatrix *z) +{ + if (z->arena) stb_free(z->arena); + stb__spmatrix_destroy(z); +} + +void *stb_sparse_ptr_matrix_get(stb_spmatrix *z, void *a, void *b, int create) +{ + stb__ptrpair t = { a,b }; + void *data = stb__spmatrix_get(z, t); + if (!data && create) { + data = stb_malloc_raw(z->arena, z->val_size); + if (!data) return NULL; + memset(data, 0, z->val_size); + stb__spmatrix_add(z, t, data); + } + return data; +} +#endif + + + +////////////////////////////////////////////////////////////////////////////// +// +// SDICT: Hash Table for Strings (symbol table) +// +// if "use_arena=1", then strings will be copied +// into blocks and never freed until the sdict is freed; +// otherwise they're malloc()ed and free()d on the fly. +// (specify use_arena=1 if you never stb_sdict_remove) + +stb_declare_hash(STB_EXTERN, stb_sdict, stb_sdict_, char *, void *) + +STB_EXTERN stb_sdict * stb_sdict_new(int use_arena); +STB_EXTERN stb_sdict * stb_sdict_copy(stb_sdict*); +STB_EXTERN void stb_sdict_delete(stb_sdict *); +STB_EXTERN void * stb_sdict_change(stb_sdict *, char *str, void *p); +STB_EXTERN int stb_sdict_count(stb_sdict *d); + +#define stb_sdict_for(d,i,q,z) \ + for(i=0; i < (d)->limit ? q=(d)->table[i].k,z=(d)->table[i].v,1 : 0; ++i) \ + if (q==NULL||q==(void *) 1);else // reversed makes macro friendly + +#ifdef STB_DEFINE + +#define STB_DEL ((void *) 1) +#define STB_SDEL ((char *) 1) + +#define stb_sdict__copy(x) \ + strcpy(a->arena ? stb_malloc_string(a->arena, strlen(x)+1) \ + : (char *) malloc(strlen(x)+1), x) + +#define stb_sdict__dispose(x) if (!a->arena) free(x) + +stb_define_hash_base(STB_noprefix, stb_sdict, void*arena;, stb_sdict_,stb_sdictinternal_, 0.85f, + char *, NULL, STB_SDEL, stb_sdict__copy, stb_sdict__dispose, + STB_safecompare, !strcmp, STB_equal, return stb_hash(k);, + void *, STB_nullvalue, NULL) + +int stb_sdict_count(stb_sdict *a) +{ + return a->count; +} + +stb_sdict * stb_sdict_new(int use_arena) +{ + stb_sdict *d = stb_sdict_create(); + if (d == NULL) return NULL; + d->arena = use_arena ? stb_malloc_global(1) : NULL; + return d; +} + +stb_sdict* stb_sdict_copy(stb_sdict *old) +{ + stb_sdict *n; + void *old_arena = old->arena; + void *new_arena = old_arena ? stb_malloc_global(1) : NULL; + old->arena = new_arena; + n = stb_sdictinternal_copy(old); + old->arena = old_arena; + if (n) + n->arena = new_arena; + else if (new_arena) + stb_free(new_arena); + return n; +} + + +void stb_sdict_delete(stb_sdict *d) +{ + if (d->arena) + stb_free(d->arena); + stb_sdict_destroy(d); +} + +void * stb_sdict_change(stb_sdict *d, char *str, void *p) +{ + void *q = stb_sdict_get(d, str); + stb_sdict_set(d, str, p); + return q; +} +#endif + +////////////////////////////////////////////////////////////////////////////// +// +// Instantiated data structures +// +// This is an attempt to implement a templated data structure. +// What you do is define a struct foo, and then include several +// pointer fields to struct foo in your struct. Then you call +// the instantiator, which creates the functions that implement +// the data structure. This requires massive undebuggable #defines, +// so we limit the cases where we do this. +// +// AA tree is an encoding of a 2-3 tree whereas RB trees encode a 2-3-4 tree; +// much simpler code due to fewer cases. + +#define stb__bst_parent(x) x +#define stb__bst_noparent(x) + +#define stb_bst_fields(N) \ + *STB_(N,left), *STB_(N,right); \ + unsigned char STB_(N,level) + +#define stb_bst_fields_parent(N) \ + *STB_(N,left), *STB_(N,right), *STB_(N,parent); \ + unsigned char STB_(N,level) + +#define STB__level(N,x) ((x) ? (x)->STB_(N,level) : 0) + +#define stb_bst_base(TYPE, N, TREE, M, compare, PAR) \ + \ +static int STB_(N,_compare)(TYPE *p, TYPE *q) \ +{ \ + compare \ +} \ + \ +static void STB_(N,setleft)(TYPE *q, TYPE *v) \ +{ \ + q->STB_(N,left) = v; \ + PAR(if (v) v->STB_(N,parent) = q;) \ +} \ + \ +static void STB_(N,setright)(TYPE *q, TYPE *v) \ +{ \ + q->STB_(N,right) = v; \ + PAR(if (v) v->STB_(N,parent) = q;) \ +} \ + \ +static TYPE *STB_(N,skew)(TYPE *q) \ +{ \ + if (q == NULL) return q; \ + if (q->STB_(N,left) \ + && q->STB_(N,left)->STB_(N,level) == q->STB_(N,level)) { \ + TYPE *p = q->STB_(N,left); \ + STB_(N,setleft)(q, p->STB_(N,right)); \ + STB_(N,setright)(p, q); \ + return p; \ + } \ + return q; \ +} \ + \ +static TYPE *STB_(N,split)(TYPE *p) \ +{ \ + TYPE *q = p->STB_(N,right); \ + if (q && q->STB_(N,right) \ + && q->STB_(N,right)->STB_(N,level) == p->STB_(N,level)) { \ + STB_(N,setright)(p, q->STB_(N,left)); \ + STB_(N,setleft)(q,p); \ + ++q->STB_(N,level); \ + return q; \ + } \ + return p; \ +} \ + \ +TYPE *STB__(N,insert)(TYPE *tree, TYPE *item) \ +{ \ + int c; \ + if (tree == NULL) { \ + item->STB_(N,left) = NULL; \ + item->STB_(N,right) = NULL; \ + item->STB_(N,level) = 1; \ + PAR(item->STB_(N,parent) = NULL;) \ + return item; \ + } \ + c = STB_(N,_compare)(item,tree); \ + if (c == 0) { \ + if (item != tree) { \ + STB_(N,setleft)(item, tree->STB_(N,left)); \ + STB_(N,setright)(item, tree->STB_(N,right)); \ + item->STB_(N,level) = tree->STB_(N,level); \ + PAR(item->STB_(N,parent) = NULL;) \ + } \ + return item; \ + } \ + if (c < 0) \ + STB_(N,setleft )(tree, STB__(N,insert)(tree->STB_(N,left), item)); \ + else \ + STB_(N,setright)(tree, STB__(N,insert)(tree->STB_(N,right), item)); \ + tree = STB_(N,skew)(tree); \ + tree = STB_(N,split)(tree); \ + PAR(tree->STB_(N,parent) = NULL;) \ + return tree; \ +} \ + \ +TYPE *STB__(N,remove)(TYPE *tree, TYPE *item) \ +{ \ + static TYPE *delnode, *leaf, *restore; \ + if (tree == NULL) return NULL; \ + leaf = tree; \ + if (STB_(N,_compare)(item, tree) < 0) { \ + STB_(N,setleft)(tree, STB__(N,remove)(tree->STB_(N,left), item)); \ + } else { \ + TYPE *r; \ + delnode = tree; \ + r = STB__(N,remove)(tree->STB_(N,right), item); \ + /* maybe move 'leaf' up to this location */ \ + if (restore == tree) { tree = leaf; leaf = restore = NULL; } \ + STB_(N,setright)(tree,r); \ + assert(tree->STB_(N,right) != tree); \ + } \ + if (tree == leaf) { \ + if (delnode == item) { \ + tree = tree->STB_(N,right); \ + assert(leaf->STB_(N,left) == NULL); \ + /* move leaf (the right sibling) up to delnode */ \ + STB_(N,setleft )(leaf, item->STB_(N,left )); \ + STB_(N,setright)(leaf, item->STB_(N,right)); \ + leaf->STB_(N,level) = item->STB_(N,level); \ + if (leaf != item) \ + restore = delnode; \ + } \ + delnode = NULL; \ + } else { \ + if (STB__level(N,tree->STB_(N,left) ) < tree->STB_(N,level)-1 || \ + STB__level(N,tree->STB_(N,right)) < tree->STB_(N,level)-1) { \ + --tree->STB_(N,level); \ + if (STB__level(N,tree->STB_(N,right)) > tree->STB_(N,level)) \ + tree->STB_(N,right)->STB_(N,level) = tree->STB_(N,level); \ + tree = STB_(N,skew)(tree); \ + STB_(N,setright)(tree, STB_(N,skew)(tree->STB_(N,right))); \ + if (tree->STB_(N,right)) \ + STB_(N,setright)(tree->STB_(N,right), \ + STB_(N,skew)(tree->STB_(N,right)->STB_(N,right))); \ + tree = STB_(N,split)(tree); \ + if (tree->STB_(N,right)) \ + STB_(N,setright)(tree, STB_(N,split)(tree->STB_(N,right))); \ + } \ + } \ + PAR(if (tree) tree->STB_(N,parent) = NULL;) \ + return tree; \ +} \ + \ +TYPE *STB__(N,last)(TYPE *tree) \ +{ \ + if (tree) \ + while (tree->STB_(N,right)) tree = tree->STB_(N,right); \ + return tree; \ +} \ + \ +TYPE *STB__(N,first)(TYPE *tree) \ +{ \ + if (tree) \ + while (tree->STB_(N,left)) tree = tree->STB_(N,left); \ + return tree; \ +} \ + \ +TYPE *STB__(N,next)(TYPE *tree, TYPE *item) \ +{ \ + TYPE *next = NULL; \ + if (item->STB_(N,right)) \ + return STB__(N,first)(item->STB_(N,right)); \ + PAR( \ + while(item->STB_(N,parent)) { \ + TYPE *up = item->STB_(N,parent); \ + if (up->STB_(N,left) == item) return up; \ + item = up; \ + } \ + return NULL; \ + ) \ + while (tree != item) { \ + if (STB_(N,_compare)(item, tree) < 0) { \ + next = tree; \ + tree = tree->STB_(N,left); \ + } else { \ + tree = tree->STB_(N,right); \ + } \ + } \ + return next; \ +} \ + \ +TYPE *STB__(N,prev)(TYPE *tree, TYPE *item) \ +{ \ + TYPE *next = NULL; \ + if (item->STB_(N,left)) \ + return STB__(N,last)(item->STB_(N,left)); \ + PAR( \ + while(item->STB_(N,parent)) { \ + TYPE *up = item->STB_(N,parent); \ + if (up->STB_(N,right) == item) return up; \ + item = up; \ + } \ + return NULL; \ + ) \ + while (tree != item) { \ + if (STB_(N,_compare)(item, tree) < 0) { \ + tree = tree->STB_(N,left); \ + } else { \ + next = tree; \ + tree = tree->STB_(N,right); \ + } \ + } \ + return next; \ +} \ + \ +STB__DEBUG( \ + void STB__(N,_validate)(TYPE *tree, int root) \ + { \ + if (tree == NULL) return; \ + PAR(if(root) assert(tree->STB_(N,parent) == NULL);) \ + assert(STB__level(N,tree->STB_(N,left) ) == tree->STB_(N,level)-1); \ + assert(STB__level(N,tree->STB_(N,right)) <= tree->STB_(N,level)); \ + assert(STB__level(N,tree->STB_(N,right)) >= tree->STB_(N,level)-1); \ + if (tree->STB_(N,right)) { \ + assert(STB__level(N,tree->STB_(N,right)->STB_(N,right)) \ + != tree->STB_(N,level)); \ + PAR(assert(tree->STB_(N,right)->STB_(N,parent) == tree);) \ + } \ + PAR(if(tree->STB_(N,left)) assert(tree->STB_(N,left)->STB_(N,parent) == tree);) \ + STB__(N,_validate)(tree->STB_(N,left) ,0); \ + STB__(N,_validate)(tree->STB_(N,right),0); \ + } \ +) \ + \ +typedef struct \ +{ \ + TYPE *root; \ +} TREE; \ + \ +void STB__(M,Insert)(TREE *tree, TYPE *item) \ +{ tree->root = STB__(N,insert)(tree->root, item); } \ +void STB__(M,Remove)(TREE *tree, TYPE *item) \ +{ tree->root = STB__(N,remove)(tree->root, item); } \ +TYPE *STB__(M,Next)(TREE *tree, TYPE *item) \ +{ return STB__(N,next)(tree->root, item); } \ +TYPE *STB__(M,Prev)(TREE *tree, TYPE *item) \ +{ return STB__(N,prev)(tree->root, item); } \ +TYPE *STB__(M,First)(TREE *tree) { return STB__(N,first)(tree->root); } \ +TYPE *STB__(M,Last) (TREE *tree) { return STB__(N,last) (tree->root); } \ +void STB__(M,Init)(TREE *tree) { tree->root = NULL; } + + +#define stb_bst_find(N,tree,fcompare) \ +{ \ + int c; \ + while (tree != NULL) { \ + fcompare \ + if (c == 0) return tree; \ + if (c < 0) tree = tree->STB_(N,left); \ + else tree = tree->STB_(N,right); \ + } \ + return NULL; \ +} + +#define stb_bst_raw(TYPE,N,TREE,M,vfield,VTYPE,compare,PAR) \ + stb_bst_base(TYPE,N,TREE,M, \ + VTYPE a = p->vfield; VTYPE b = q->vfield; return (compare);, PAR ) \ + \ +TYPE *STB__(N,find)(TYPE *tree, VTYPE a) \ + stb_bst_find(N,tree,VTYPE b = tree->vfield; c = (compare);) \ +TYPE *STB__(M,Find)(TREE *tree, VTYPE a) \ +{ return STB__(N,find)(tree->root, a); } + +#define stb_bst(TYPE,N,TREE,M,vfield,VTYPE,compare) \ + stb_bst_raw(TYPE,N,TREE,M,vfield,VTYPE,compare,stb__bst_noparent) +#define stb_bst_parent(TYPE,N,TREE,M,vfield,VTYPE,compare) \ + stb_bst_raw(TYPE,N,TREE,M,vfield,VTYPE,compare,stb__bst_parent) + + + +////////////////////////////////////////////////////////////////////////////// +// +// Pointer Nulling +// +// This lets you automatically NULL dangling pointers to "registered" +// objects. Note that you have to make sure you call the appropriate +// functions when you free or realloc blocks of memory that contain +// pointers or pointer targets. stb.h can automatically do this for +// stb_arr, or for all frees/reallocs if it's wrapping them. +// + +#ifdef STB_NPTR + +STB_EXTERN void stb_nptr_set(void *address_of_pointer, void *value_to_write); +STB_EXTERN void stb_nptr_didset(void *address_of_pointer); + +STB_EXTERN void stb_nptr_didfree(void *address_being_freed, int len); +STB_EXTERN void stb_nptr_free(void *address_being_freed, int len); + +STB_EXTERN void stb_nptr_didrealloc(void *new_address, void *old_address, int len); +STB_EXTERN void stb_nptr_recache(void); // recache all known pointers + // do this after pointer sets outside your control, slow + +#ifdef STB_DEFINE +// for fast updating on free/realloc, we need to be able to find +// all the objects (pointers and targets) within a given block; +// this precludes hashing + +// we use a three-level hierarchy of memory to minimize storage: +// level 1: 65536 pointers to stb__memory_node (always uses 256 KB) +// level 2: each stb__memory_node represents a 64K block of memory +// with 256 stb__memory_leafs (worst case 64MB) +// level 3: each stb__memory_leaf represents 256 bytes of memory +// using a list of target locations and a list of pointers +// (which are hopefully fairly short normally!) + +// this approach won't work in 64-bit, which has a much larger address +// space. need to redesign + +#define STB__NPTR_ROOT_LOG2 16 +#define STB__NPTR_ROOT_NUM (1 << STB__NPTR_ROOT_LOG2) +#define STB__NPTR_ROOT_SHIFT (32 - STB__NPTR_ROOT_LOG2) + +#define STB__NPTR_NODE_LOG2 5 +#define STB__NPTR_NODE_NUM (1 << STB__NPTR_NODE_LOG2) +#define STB__NPTR_NODE_MASK (STB__NPTR_NODE_NUM-1) +#define STB__NPTR_NODE_SHIFT (STB__NPTR_ROOT_SHIFT - STB__NPTR_NODE_LOG2) +#define STB__NPTR_NODE_OFFSET(x) (((x) >> STB__NPTR_NODE_SHIFT) & STB__NPTR_NODE_MASK) + +typedef struct stb__st_nptr +{ + void *ptr; // address of actual pointer + struct stb__st_nptr *next; // next pointer with same target + struct stb__st_nptr **prev; // prev pointer with same target, address of 'next' field (or first) + struct stb__st_nptr *next_in_block; +} stb__nptr; + +typedef struct stb__st_nptr_target +{ + void *ptr; // address of target + stb__nptr *first; // address of first nptr pointing to this + struct stb__st_nptr_target *next_in_block; +} stb__nptr_target; + +typedef struct +{ + stb__nptr *pointers; + stb__nptr_target *targets; +} stb__memory_leaf; + +typedef struct +{ + stb__memory_leaf *children[STB__NPTR_NODE_NUM]; +} stb__memory_node; + +stb__memory_node *stb__memtab_root[STB__NPTR_ROOT_NUM]; + +static stb__memory_leaf *stb__nptr_find_leaf(void *mem) +{ + stb_uint32 address = (stb_uint32) mem; + stb__memory_node *z = stb__memtab_root[address >> STB__NPTR_ROOT_SHIFT]; + if (z) + return z->children[STB__NPTR_NODE_OFFSET(address)]; + else + return NULL; +} + +static void * stb__nptr_alloc(int size) +{ + return stb__realloc_raw(0,size); +} + +static void stb__nptr_free(void *p) +{ + stb__realloc_raw(p,0); +} + +static stb__memory_leaf *stb__nptr_make_leaf(void *mem) +{ + stb_uint32 address = (stb_uint32) mem; + stb__memory_node *z = stb__memtab_root[address >> STB__NPTR_ROOT_SHIFT]; + stb__memory_leaf *f; + if (!z) { + int i; + z = (stb__memory_node *) stb__nptr_alloc(sizeof(*stb__memtab_root[0])); + stb__memtab_root[address >> STB__NPTR_ROOT_SHIFT] = z; + for (i=0; i < 256; ++i) + z->children[i] = 0; + } + f = (stb__memory_leaf *) stb__nptr_alloc(sizeof(*f)); + z->children[STB__NPTR_NODE_OFFSET(address)] = f; + f->pointers = NULL; + f->targets = NULL; + return f; +} + +static stb__nptr_target *stb__nptr_find_target(void *target, int force) +{ + stb__memory_leaf *p = stb__nptr_find_leaf(target); + if (p) { + stb__nptr_target *t = p->targets; + while (t) { + if (t->ptr == target) + return t; + t = t->next_in_block; + } + } + if (force) { + stb__nptr_target *t = (stb__nptr_target*) stb__nptr_alloc(sizeof(*t)); + if (!p) p = stb__nptr_make_leaf(target); + t->ptr = target; + t->first = NULL; + t->next_in_block = p->targets; + p->targets = t; + return t; + } else + return NULL; +} + +static stb__nptr *stb__nptr_find_pointer(void *ptr, int force) +{ + stb__memory_leaf *p = stb__nptr_find_leaf(ptr); + if (p) { + stb__nptr *t = p->pointers; + while (t) { + if (t->ptr == ptr) + return t; + t = t->next_in_block; + } + } + if (force) { + stb__nptr *t = (stb__nptr *) stb__nptr_alloc(sizeof(*t)); + if (!p) p = stb__nptr_make_leaf(ptr); + t->ptr = ptr; + t->next = NULL; + t->prev = NULL; + t->next_in_block = p->pointers; + p->pointers = t; + return t; + } else + return NULL; +} + +void stb_nptr_set(void *address_of_pointer, void *value_to_write) +{ + if (*(void **)address_of_pointer != value_to_write) { + *(void **) address_of_pointer = value_to_write; + stb_nptr_didset(address_of_pointer); + } +} + +void stb_nptr_didset(void *address_of_pointer) +{ + // first unlink from old chain + void *new_address; + stb__nptr *p = stb__nptr_find_pointer(address_of_pointer, 1); // force building if doesn't exist + if (p->prev) { // if p->prev is NULL, we just built it, or it was NULL + *(p->prev) = p->next; + if (p->next) p->next->prev = p->prev; + } + // now add to new chain + new_address = *(void **)address_of_pointer; + if (new_address != NULL) { + stb__nptr_target *t = stb__nptr_find_target(new_address, 1); + p->next = t->first; + if (p->next) p->next->prev = &p->next; + p->prev = &t->first; + t->first = p; + } else { + p->prev = NULL; + p->next = NULL; + } +} + +void stb__nptr_block(void *address, int len, void (*function)(stb__memory_leaf *f, int datum, void *start, void *end), int datum) +{ + void *end_address = (void *) ((char *) address + len - 1); + stb__memory_node *n; + stb_uint32 start = (stb_uint32) address; + stb_uint32 end = start + len - 1; + + int b0 = start >> STB__NPTR_ROOT_SHIFT; + int b1 = end >> STB__NPTR_ROOT_SHIFT; + int b=b0,i,e0,e1; + + e0 = STB__NPTR_NODE_OFFSET(start); + + if (datum <= 0) { + // first block + n = stb__memtab_root[b0]; + if (n) { + if (b0 != b1) + e1 = STB__NPTR_NODE_NUM-1; + else + e1 = STB__NPTR_NODE_OFFSET(end); + for (i=e0; i <= e1; ++i) + if (n->children[i]) + function(n->children[i], datum, address, end_address); + } + if (b1 > b0) { + // blocks other than the first and last block + for (b=b0+1; b < b1; ++b) { + n = stb__memtab_root[b]; + if (n) + for (i=0; i <= STB__NPTR_NODE_NUM-1; ++i) + if (n->children[i]) + function(n->children[i], datum, address, end_address); + } + // last block + n = stb__memtab_root[b1]; + if (n) { + e1 = STB__NPTR_NODE_OFFSET(end); + for (i=0; i <= e1; ++i) + if (n->children[i]) + function(n->children[i], datum, address, end_address); + } + } + } else { + if (b1 > b0) { + // last block + n = stb__memtab_root[b1]; + if (n) { + e1 = STB__NPTR_NODE_OFFSET(end); + for (i=e1; i >= 0; --i) + if (n->children[i]) + function(n->children[i], datum, address, end_address); + } + // blocks other than the first and last block + for (b=b1-1; b > b0; --b) { + n = stb__memtab_root[b]; + if (n) + for (i=STB__NPTR_NODE_NUM-1; i >= 0; --i) + if (n->children[i]) + function(n->children[i], datum, address, end_address); + } + } + // first block + n = stb__memtab_root[b0]; + if (n) { + if (b0 != b1) + e1 = STB__NPTR_NODE_NUM-1; + else + e1 = STB__NPTR_NODE_OFFSET(end); + for (i=e1; i >= e0; --i) + if (n->children[i]) + function(n->children[i], datum, address, end_address); + } + } +} + +static void stb__nptr_delete_pointers(stb__memory_leaf *f, int offset, void *start, void *end) +{ + stb__nptr **p = &f->pointers; + while (*p) { + stb__nptr *n = *p; + if (n->ptr >= start && n->ptr <= end) { + // unlink + if (n->prev) { + *(n->prev) = n->next; + if (n->next) n->next->prev = n->prev; + } + *p = n->next_in_block; + stb__nptr_free(n); + } else + p = &(n->next_in_block); + } +} + +static void stb__nptr_delete_targets(stb__memory_leaf *f, int offset, void *start, void *end) +{ + stb__nptr_target **p = &f->targets; + while (*p) { + stb__nptr_target *n = *p; + if (n->ptr >= start && n->ptr <= end) { + // null pointers + stb__nptr *z = n->first; + while (z) { + stb__nptr *y = z->next; + z->prev = NULL; + z->next = NULL; + *(void **) z->ptr = NULL; + z = y; + } + // unlink this target + *p = n->next_in_block; + stb__nptr_free(n); + } else + p = &(n->next_in_block); + } +} + +void stb_nptr_didfree(void *address_being_freed, int len) +{ + // step one: delete all pointers in this block + stb__nptr_block(address_being_freed, len, stb__nptr_delete_pointers, 0); + // step two: NULL all pointers to this block; do this second to avoid NULLing deleted pointers + stb__nptr_block(address_being_freed, len, stb__nptr_delete_targets, 0); +} + +void stb_nptr_free(void *address_being_freed, int len) +{ + free(address_being_freed); + stb_nptr_didfree(address_being_freed, len); +} + +static void stb__nptr_move_targets(stb__memory_leaf *f, int offset, void *start, void *end) +{ + stb__nptr_target **t = &f->targets; + while (*t) { + stb__nptr_target *n = *t; + if (n->ptr >= start && n->ptr <= end) { + stb__nptr *z; + stb__memory_leaf *f; + // unlink n + *t = n->next_in_block; + // update n to new address + n->ptr = (void *) ((char *) n->ptr + offset); + f = stb__nptr_find_leaf(n->ptr); + if (!f) f = stb__nptr_make_leaf(n->ptr); + n->next_in_block = f->targets; + f->targets = n; + // now go through all pointers and make them point here + z = n->first; + while (z) { + *(void**) z->ptr = n->ptr; + z = z->next; + } + } else + t = &(n->next_in_block); + } +} + +static void stb__nptr_move_pointers(stb__memory_leaf *f, int offset, void *start, void *end) +{ + stb__nptr **p = &f->pointers; + while (*p) { + stb__nptr *n = *p; + if (n->ptr >= start && n->ptr <= end) { + // unlink + *p = n->next_in_block; + n->ptr = (void *) ((int) n->ptr + offset); + // move to new block + f = stb__nptr_find_leaf(n->ptr); + if (!f) f = stb__nptr_make_leaf(n->ptr); + n->next_in_block = f->pointers; + f->pointers = n; + } else + p = &(n->next_in_block); + } +} + +void stb_nptr_realloc(void *new_address, void *old_address, int len) +{ + if (new_address == old_address) return; + + // have to move the pointers first, because moving the targets + // requires writing to the pointers-to-the-targets, and if some of those moved too, + // we need to make sure we don't write to the old memory + + // step one: move all pointers within the block + stb__nptr_block(old_address, len, stb__nptr_move_pointers, (char *) new_address - (char *) old_address); + // step two: move all targets within the block + stb__nptr_block(old_address, len, stb__nptr_move_targets, (char *) new_address - (char *) old_address); +} + +void stb_nptr_move(void *new_address, void *old_address) +{ + stb_nptr_realloc(new_address, old_address, 1); +} + +void stb_nptr_recache(void) +{ + int i,j; + for (i=0; i < STB__NPTR_ROOT_NUM; ++i) + if (stb__memtab_root[i]) + for (j=0; j < STB__NPTR_NODE_NUM; ++j) + if (stb__memtab_root[i]->children[j]) { + stb__nptr *p = stb__memtab_root[i]->children[j]->pointers; + while (p) { + stb_nptr_didset(p->ptr); + p = p->next_in_block; + } + } +} + +#endif // STB_DEFINE +#endif // STB_NPTR + + +////////////////////////////////////////////////////////////////////////////// +// +// File Processing +// + + +#ifdef _MSC_VER + #define stb_rename(x,y) _wrename((const wchar_t *)stb__from_utf8(x), (const wchar_t *)stb__from_utf8_alt(y)) + #define stb_mktemp _mktemp +#else + #define stb_mktemp mktemp + #define stb_rename rename +#endif + +STB_EXTERN void stb_fput_varlen64(FILE *f, stb_uint64 v); +STB_EXTERN stb_uint64 stb_fget_varlen64(FILE *f); +STB_EXTERN int stb_size_varlen64(stb_uint64 v); + + +#define stb_filec (char *) stb_file +#define stb_fileu (unsigned char *) stb_file +STB_EXTERN void * stb_file(char *filename, size_t *length); +STB_EXTERN void * stb_file_max(char *filename, size_t *length); +STB_EXTERN size_t stb_filelen(FILE *f); +STB_EXTERN int stb_filewrite(char *filename, void *data, size_t length); +STB_EXTERN int stb_filewritestr(char *filename, char *data); +STB_EXTERN char ** stb_stringfile(char *filename, int *len); +STB_EXTERN char ** stb_stringfile_trimmed(char *name, int *len, char comm); +STB_EXTERN char * stb_fgets(char *buffer, int buflen, FILE *f); +STB_EXTERN char * stb_fgets_malloc(FILE *f); +STB_EXTERN int stb_fexists(char *filename); +STB_EXTERN int stb_fcmp(char *s1, char *s2); +STB_EXTERN int stb_feq(char *s1, char *s2); +STB_EXTERN time_t stb_ftimestamp(char *filename); + +STB_EXTERN int stb_fullpath(char *abs, int abs_size, char *rel); +STB_EXTERN FILE * stb_fopen(char *filename, char *mode); +STB_EXTERN int stb_fclose(FILE *f, int keep); + +enum +{ + stb_keep_no = 0, + stb_keep_yes = 1, + stb_keep_if_different = 2, +}; + +STB_EXTERN int stb_copyfile(char *src, char *dest); + +STB_EXTERN void stb_fput_varlen64(FILE *f, stb_uint64 v); +STB_EXTERN stb_uint64 stb_fget_varlen64(FILE *f); +STB_EXTERN int stb_size_varlen64(stb_uint64 v); + +STB_EXTERN void stb_fwrite32(FILE *f, stb_uint32 datum); +STB_EXTERN void stb_fput_varlen (FILE *f, int v); +STB_EXTERN void stb_fput_varlenu(FILE *f, unsigned int v); +STB_EXTERN int stb_fget_varlen (FILE *f); +STB_EXTERN stb_uint stb_fget_varlenu(FILE *f); +STB_EXTERN void stb_fput_ranged (FILE *f, int v, int b, stb_uint n); +STB_EXTERN int stb_fget_ranged (FILE *f, int b, stb_uint n); +STB_EXTERN int stb_size_varlen (int v); +STB_EXTERN int stb_size_varlenu(unsigned int v); +STB_EXTERN int stb_size_ranged (int b, stb_uint n); + +STB_EXTERN int stb_fread(void *data, size_t len, size_t count, void *f); +STB_EXTERN int stb_fwrite(void *data, size_t len, size_t count, void *f); + +#if 0 +typedef struct +{ + FILE *base_file; + char *buffer; + int buffer_size; + int buffer_off; + int buffer_left; +} STBF; + +STB_EXTERN STBF *stb_tfopen(char *filename, char *mode); +STB_EXTERN int stb_tfread(void *data, size_t len, size_t count, STBF *f); +STB_EXTERN int stb_tfwrite(void *data, size_t len, size_t count, STBF *f); +#endif + +#ifdef STB_DEFINE + +#if 0 +STBF *stb_tfopen(char *filename, char *mode) +{ + STBF *z; + FILE *f = fopen(filename, mode); + if (!f) return NULL; + z = (STBF *) malloc(sizeof(*z)); + if (!z) { fclose(f); return NULL; } + z->base_file = f; + if (!strcmp(mode, "rb") || !strcmp(mode, "wb")) { + z->buffer_size = 4096; + z->buffer_off = z->buffer_size; + z->buffer_left = 0; + z->buffer = malloc(z->buffer_size); + if (!z->buffer) { free(z); fclose(f); return NULL; } + } else { + z->buffer = 0; + z->buffer_size = 0; + z->buffer_left = 0; + } + return z; +} + +int stb_tfread(void *data, size_t len, size_t count, STBF *f) +{ + int total = len*count, done=0; + if (!total) return 0; + if (total <= z->buffer_left) { + memcpy(data, z->buffer + z->buffer_off, total); + z->buffer_off += total; + z->buffer_left -= total; + return count; + } else { + char *out = (char *) data; + + // consume all buffered data + memcpy(data, z->buffer + z->buffer_off, z->buffer_left); + done = z->buffer_left; + out += z->buffer_left; + z->buffer_left=0; + + if (total-done > (z->buffer_size >> 1)) { + done += fread(out + } + } +} +#endif + +void stb_fwrite32(FILE *f, stb_uint32 x) +{ + fwrite(&x, 4, 1, f); +} + +#if defined(_MSC_VER) || defined(__MINGW32__) + #define stb__stat _stat +#else + #define stb__stat stat +#endif + +int stb_fexists(char *filename) +{ + struct stb__stat buf; + return stb__windows( + _wstat((const wchar_t *)stb__from_utf8(filename), &buf), + stat(filename,&buf) + ) == 0; +} + +time_t stb_ftimestamp(char *filename) +{ + struct stb__stat buf; + if (stb__windows( + _wstat((const wchar_t *)stb__from_utf8(filename), &buf), + stat(filename,&buf) + ) == 0) + { + return buf.st_mtime; + } else { + return 0; + } +} + +size_t stb_filelen(FILE *f) +{ + size_t len, pos; + pos = ftell(f); + fseek(f, 0, SEEK_END); + len = ftell(f); + fseek(f, pos, SEEK_SET); + return len; +} + +void *stb_file(char *filename, size_t *length) +{ + FILE *f = stb__fopen(filename, "rb"); + char *buffer; + size_t len, len2; + if (!f) return NULL; + len = stb_filelen(f); + buffer = (char *) malloc(len+2); // nul + extra + len2 = fread(buffer, 1, len, f); + if (len2 == len) { + if (length) *length = len; + buffer[len] = 0; + } else { + free(buffer); + buffer = NULL; + } + fclose(f); + return buffer; +} + +int stb_filewrite(char *filename, void *data, size_t length) +{ + FILE *f = stb_fopen(filename, "wb"); + if (f) { + fwrite(data, 1, length, f); + stb_fclose(f, stb_keep_if_different); + } + return f != NULL; +} + +int stb_filewritestr(char *filename, char *data) +{ + return stb_filewrite(filename, data, strlen(data)); +} + +void * stb_file_max(char *filename, size_t *length) +{ + FILE *f = stb__fopen(filename, "rb"); + char *buffer; + size_t len, maxlen; + if (!f) return NULL; + maxlen = *length; + buffer = (char *) malloc(maxlen+1); + len = fread(buffer, 1, maxlen, f); + buffer[len] = 0; + fclose(f); + *length = len; + return buffer; +} + +char ** stb_stringfile(char *filename, int *plen) +{ + FILE *f = stb__fopen(filename, "rb"); + char *buffer, **list=NULL, *s; + size_t len, count, i; + + if (!f) return NULL; + len = stb_filelen(f); + buffer = (char *) malloc(len+1); + len = fread(buffer, 1, len, f); + buffer[len] = 0; + fclose(f); + + // two passes through: first time count lines, second time set them + for (i=0; i < 2; ++i) { + s = buffer; + if (i == 1) + list[0] = s; + count = 1; + while (*s) { + if (*s == '\n' || *s == '\r') { + // detect if both cr & lf are together + int crlf = (s[0] + s[1]) == ('\n' + '\r'); + if (i == 1) *s = 0; + if (crlf) ++s; + if (s[1]) { // it's not over yet + if (i == 1) list[count] = s+1; + ++count; + } + } + ++s; + } + if (i == 0) { + list = (char **) malloc(sizeof(*list) * (count+1) + len+1); + if (!list) return NULL; + list[count] = 0; + // recopy the file so there's just a single allocation to free + memcpy(&list[count+1], buffer, len+1); + free(buffer); + buffer = (char *) &list[count+1]; + if (plen) *plen = count; + } + } + return list; +} + +char ** stb_stringfile_trimmed(char *name, int *len, char comment) +{ + int i,n,o=0; + char **s = stb_stringfile(name, &n); + if (s == NULL) return NULL; + for (i=0; i < n; ++i) { + char *p = stb_skipwhite(s[i]); + if (*p && *p != comment) + s[o++] = p; + } + s[o] = NULL; + if (len) *len = o; + return s; +} + +char * stb_fgets(char *buffer, int buflen, FILE *f) +{ + char *p; + buffer[0] = 0; + p = fgets(buffer, buflen, f); + if (p) { + int n = strlen(p)-1; + if (n >= 0) + if (p[n] == '\n') + p[n] = 0; + } + return p; +} + +char * stb_fgets_malloc(FILE *f) +{ + // avoid reallocing for small strings + char quick_buffer[800]; + quick_buffer[sizeof(quick_buffer)-2] = 0; + if (!fgets(quick_buffer, sizeof(quick_buffer), f)) + return NULL; + + if (quick_buffer[sizeof(quick_buffer)-2] == 0) { + int n = strlen(quick_buffer); + if (n > 0 && quick_buffer[n-1] == '\n') + quick_buffer[n-1] = 0; + return strdup(quick_buffer); + } else { + char *p; + char *a = strdup(quick_buffer); + int len = sizeof(quick_buffer)-1; + + while (!feof(f)) { + if (a[len-1] == '\n') break; + a = (char *) realloc(a, len*2); + p = &a[len]; + p[len-2] = 0; + if (!fgets(p, len, f)) + break; + if (p[len-2] == 0) { + len += strlen(p); + break; + } + len = len + (len-1); + } + if (a[len-1] == '\n') + a[len-1] = 0; + return a; + } +} + +int stb_fullpath(char *abs, int abs_size, char *rel) +{ + #ifdef _MSC_VER + return _fullpath(abs, rel, abs_size) != NULL; + #else + if (rel[0] == '/' || rel[0] == '~') { + if ((int) strlen(rel) >= abs_size) + return 0; + strcpy(abs,rel); + return STB_TRUE; + } else { + int n; + getcwd(abs, abs_size); + n = strlen(abs); + if (n+(int) strlen(rel)+2 <= abs_size) { + abs[n] = '/'; + strcpy(abs+n+1, rel); + return STB_TRUE; + } else { + return STB_FALSE; + } + } + #endif +} + +static int stb_fcmp_core(FILE *f, FILE *g) +{ + char buf1[1024],buf2[1024]; + int n1,n2, res=0; + + while (1) { + n1 = fread(buf1, 1, sizeof(buf1), f); + n2 = fread(buf2, 1, sizeof(buf2), g); + res = memcmp(buf1,buf2,stb_min(n1,n2)); + if (res) + break; + if (n1 != n2) { + res = n1 < n2 ? -1 : 1; + break; + } + if (n1 == 0) + break; + } + + fclose(f); + fclose(g); + return res; +} + +int stb_fcmp(char *s1, char *s2) +{ + FILE *f = stb__fopen(s1, "rb"); + FILE *g = stb__fopen(s2, "rb"); + + if (f == NULL || g == NULL) { + if (f) fclose(f); + if (g) { + fclose(g); + return STB_TRUE; + } + return f != NULL; + } + + return stb_fcmp_core(f,g); +} + +int stb_feq(char *s1, char *s2) +{ + FILE *f = stb__fopen(s1, "rb"); + FILE *g = stb__fopen(s2, "rb"); + + if (f == NULL || g == NULL) { + if (f) fclose(f); + if (g) fclose(g); + return f == g; + } + + // feq is faster because it shortcuts if they're different length + if (stb_filelen(f) != stb_filelen(g)) { + fclose(f); + fclose(g); + return 0; + } + + return !stb_fcmp_core(f,g); +} + +static stb_ptrmap *stb__files; + +typedef struct +{ + char *temp_name; + char *name; + int errors; +} stb__file_data; + +FILE * stb_fopen(char *filename, char *mode) +{ + FILE *f; + char name_full[4096]; + char temp_full[sizeof(name_full) + 12]; + int p; +#ifdef _MSC_VER + int j; +#endif + if (mode[0] != 'w' && !strchr(mode, '+')) + return stb__fopen(filename, mode); + + // save away the full path to the file so if the program + // changes the cwd everything still works right! unix has + // better ways to do this, but we have to work in windows + name_full[0] = '\0'; // stb_fullpath reads name_full[0] + if (stb_fullpath(name_full, sizeof(name_full), filename)==0) + return 0; + + // try to generate a temporary file in the same directory + p = strlen(name_full)-1; + while (p > 0 && name_full[p] != '/' && name_full[p] != '\\' + && name_full[p] != ':' && name_full[p] != '~') + --p; + ++p; + + memcpy(temp_full, name_full, p); + + #ifdef _MSC_VER + // try multiple times to make a temp file... just in + // case some other process makes the name first + for (j=0; j < 32; ++j) { + strcpy(temp_full+p, "stmpXXXXXX"); + if (stb_mktemp(temp_full) == NULL) + return 0; + + f = fopen(temp_full, mode); + if (f != NULL) + break; + } + #else + { + strcpy(temp_full+p, "stmpXXXXXX"); + #ifdef __MINGW32__ + int fd = open(mktemp(temp_full), O_RDWR); + #else + int fd = mkstemp(temp_full); + #endif + if (fd == -1) return NULL; + f = fdopen(fd, mode); + if (f == NULL) { + unlink(temp_full); + close(fd); + return NULL; + } + } + #endif + if (f != NULL) { + stb__file_data *d = (stb__file_data *) malloc(sizeof(*d)); + if (!d) { assert(0); /* NOTREACHED */fclose(f); return NULL; } + if (stb__files == NULL) stb__files = stb_ptrmap_create(); + d->temp_name = strdup(temp_full); + d->name = strdup(name_full); + d->errors = 0; + stb_ptrmap_add(stb__files, f, d); + return f; + } + + return NULL; +} + +int stb_fclose(FILE *f, int keep) +{ + stb__file_data *d; + + int ok = STB_FALSE; + if (f == NULL) return 0; + + if (ferror(f)) + keep = stb_keep_no; + + fclose(f); + + if (stb__files && stb_ptrmap_remove(stb__files, f, (void **) &d)) { + if (stb__files->count == 0) { + stb_ptrmap_destroy(stb__files); + stb__files = NULL; + } + } else + return STB_TRUE; // not special + + if (keep == stb_keep_if_different) { + // check if the files are identical + if (stb_feq(d->name, d->temp_name)) { + keep = stb_keep_no; + ok = STB_TRUE; // report success if no change + } + } + + if (keep != stb_keep_no) { + if (stb_fexists(d->name) && remove(d->name)) { + // failed to delete old, so don't keep new + keep = stb_keep_no; + } else { + if (!stb_rename(d->temp_name, d->name)) + ok = STB_TRUE; + else + keep=stb_keep_no; + } + } + + if (keep == stb_keep_no) + remove(d->temp_name); + + free(d->temp_name); + free(d->name); + free(d); + + return ok; +} + +int stb_copyfile(char *src, char *dest) +{ + char raw_buffer[1024]; + char *buffer; + int buf_size = 65536; + + FILE *f, *g; + + // if file already exists at destination, do nothing + if (stb_feq(src, dest)) return STB_TRUE; + + // open file + f = stb__fopen(src, "rb"); + if (f == NULL) return STB_FALSE; + + // open file for writing + g = stb__fopen(dest, "wb"); + if (g == NULL) { + fclose(f); + return STB_FALSE; + } + + buffer = (char *) malloc(buf_size); + if (buffer == NULL) { + buffer = raw_buffer; + buf_size = sizeof(raw_buffer); + } + + while (!feof(f)) { + int n = fread(buffer, 1, buf_size, f); + if (n != 0) + fwrite(buffer, 1, n, g); + } + + fclose(f); + if (buffer != raw_buffer) + free(buffer); + + fclose(g); + return STB_TRUE; +} + +// varlen: +// v' = (v >> 31) + (v < 0 ? ~v : v)<<1; // small abs(v) => small v' +// output v as big endian v'+k for v' <= k: +// 1 byte : v' <= 0x00000080 ( -64 <= v < 64) 7 bits +// 2 bytes: v' <= 0x00004000 (-8192 <= v < 8192) 14 bits +// 3 bytes: v' <= 0x00200000 21 bits +// 4 bytes: v' <= 0x10000000 28 bits +// the number of most significant 1-bits in the first byte +// equals the number of bytes after the first + +#define stb__varlen_xform(v) (v<0 ? (~v << 1)+1 : (v << 1)) + +int stb_size_varlen(int v) { return stb_size_varlenu(stb__varlen_xform(v)); } +int stb_size_varlenu(unsigned int v) +{ + if (v < 0x00000080) return 1; + if (v < 0x00004000) return 2; + if (v < 0x00200000) return 3; + if (v < 0x10000000) return 4; + return 5; +} + +void stb_fput_varlen(FILE *f, int v) { stb_fput_varlenu(f, stb__varlen_xform(v)); } + +void stb_fput_varlenu(FILE *f, unsigned int z) +{ + if (z >= 0x10000000) fputc(0xF0,f); + if (z >= 0x00200000) fputc((z < 0x10000000 ? 0xE0 : 0)+(z>>24),f); + if (z >= 0x00004000) fputc((z < 0x00200000 ? 0xC0 : 0)+(z>>16),f); + if (z >= 0x00000080) fputc((z < 0x00004000 ? 0x80 : 0)+(z>> 8),f); + fputc(z,f); +} + +#define stb_fgetc(f) ((unsigned char) fgetc(f)) + +int stb_fget_varlen(FILE *f) +{ + unsigned int z = stb_fget_varlenu(f); + return (z & 1) ? ~(z>>1) : (z>>1); +} + +unsigned int stb_fget_varlenu(FILE *f) +{ + unsigned int z; + unsigned char d; + d = stb_fgetc(f); + + if (d >= 0x80) { + if (d >= 0xc0) { + if (d >= 0xe0) { + if (d == 0xf0) z = stb_fgetc(f) << 24; + else z = (d - 0xe0) << 24; + z += stb_fgetc(f) << 16; + } + else + z = (d - 0xc0) << 16; + z += stb_fgetc(f) << 8; + } else + z = (d - 0x80) << 8; + z += stb_fgetc(f); + } else + z = d; + return z; +} + +stb_uint64 stb_fget_varlen64(FILE *f) +{ + stb_uint64 z; + unsigned char d; + d = stb_fgetc(f); + + if (d >= 0x80) { + if (d >= 0xc0) { + if (d >= 0xe0) { + if (d >= 0xf0) { + if (d >= 0xf8) { + if (d >= 0xfc) { + if (d >= 0xfe) { + if (d >= 0xff) + z = (stb_uint64) stb_fgetc(f) << 56; + else + z = (stb_uint64) (d - 0xfe) << 56; + z |= (stb_uint64) stb_fgetc(f) << 48; + } else z = (stb_uint64) (d - 0xfc) << 48; + z |= (stb_uint64) stb_fgetc(f) << 40; + } else z = (stb_uint64) (d - 0xf8) << 40; + z |= (stb_uint64) stb_fgetc(f) << 32; + } else z = (stb_uint64) (d - 0xf0) << 32; + z |= (stb_uint) stb_fgetc(f) << 24; + } else z = (stb_uint) (d - 0xe0) << 24; + z |= (stb_uint) stb_fgetc(f) << 16; + } else z = (stb_uint) (d - 0xc0) << 16; + z |= (stb_uint) stb_fgetc(f) << 8; + } else z = (stb_uint) (d - 0x80) << 8; + z |= stb_fgetc(f); + } else + z = d; + + return (z & 1) ? ~(z >> 1) : (z >> 1); +} + +int stb_size_varlen64(stb_uint64 v) +{ + if (v < 0x00000080) return 1; + if (v < 0x00004000) return 2; + if (v < 0x00200000) return 3; + if (v < 0x10000000) return 4; + if (v < STB_IMM_UINT64(0x0000000800000000)) return 5; + if (v < STB_IMM_UINT64(0x0000040000000000)) return 6; + if (v < STB_IMM_UINT64(0x0002000000000000)) return 7; + if (v < STB_IMM_UINT64(0x0100000000000000)) return 8; + return 9; +} + +void stb_fput_varlen64(FILE *f, stb_uint64 v) +{ + stb_uint64 z = stb__varlen_xform(v); + int first=1; + if (z >= STB_IMM_UINT64(0x100000000000000)) { + fputc(0xff,f); + first=0; + } + if (z >= STB_IMM_UINT64(0x02000000000000)) fputc((first ? 0xFE : 0)+(char)(z>>56),f), first=0; + if (z >= STB_IMM_UINT64(0x00040000000000)) fputc((first ? 0xFC : 0)+(char)(z>>48),f), first=0; + if (z >= STB_IMM_UINT64(0x00000800000000)) fputc((first ? 0xF8 : 0)+(char)(z>>40),f), first=0; + if (z >= STB_IMM_UINT64(0x00000010000000)) fputc((first ? 0xF0 : 0)+(char)(z>>32),f), first=0; + if (z >= STB_IMM_UINT64(0x00000000200000)) fputc((first ? 0xE0 : 0)+(char)(z>>24),f), first=0; + if (z >= STB_IMM_UINT64(0x00000000004000)) fputc((first ? 0xC0 : 0)+(char)(z>>16),f), first=0; + if (z >= STB_IMM_UINT64(0x00000000000080)) fputc((first ? 0x80 : 0)+(char)(z>> 8),f), first=0; + fputc((char)z,f); +} + +void stb_fput_ranged(FILE *f, int v, int b, stb_uint n) +{ + v -= b; + if (n <= (1 << 31)) + assert((stb_uint) v < n); + if (n > (1 << 24)) fputc(v >> 24, f); + if (n > (1 << 16)) fputc(v >> 16, f); + if (n > (1 << 8)) fputc(v >> 8, f); + fputc(v,f); +} + +int stb_fget_ranged(FILE *f, int b, stb_uint n) +{ + unsigned int v=0; + if (n > (1 << 24)) v += stb_fgetc(f) << 24; + if (n > (1 << 16)) v += stb_fgetc(f) << 16; + if (n > (1 << 8)) v += stb_fgetc(f) << 8; + v += stb_fgetc(f); + return b+v; +} + +int stb_size_ranged(int b, stb_uint n) +{ + if (n > (1 << 24)) return 4; + if (n > (1 << 16)) return 3; + if (n > (1 << 8)) return 2; + return 1; +} + +void stb_fput_string(FILE *f, char *s) +{ + int len = strlen(s); + stb_fput_varlenu(f, len); + fwrite(s, 1, len, f); +} + +// inverse of the above algorithm +char *stb_fget_string(FILE *f, void *p) +{ + char *s; + int len = stb_fget_varlenu(f); + if (len > 4096) return NULL; + s = p ? stb_malloc_string(p, len+1) : (char *) malloc(len+1); + fread(s, 1, len, f); + s[len] = 0; + return s; +} + +char *stb_strdup(char *str, void *pool) +{ + int len = strlen(str); + char *p = stb_malloc_string(pool, len+1); + strcpy(p, str); + return p; +} + +// strip the trailing '/' or '\\' from a directory so we can refer to it +// as a file for _stat() +char *stb_strip_final_slash(char *t) +{ + if (t[0]) { + char *z = t + strlen(t) - 1; + // *z is the last character + if (*z == '\\' || *z == '/') + if (z != t+2 || t[1] != ':') // but don't strip it if it's e.g. "c:/" + *z = 0; + if (*z == '\\') + *z = '/'; // canonicalize to make sure it matches db + } + return t; +} +#endif + +////////////////////////////////////////////////////////////////////////////// +// +// Options parsing +// + +STB_EXTERN char **stb_getopt_param(int *argc, char **argv, char *param); +STB_EXTERN char **stb_getopt(int *argc, char **argv); +STB_EXTERN void stb_getopt_free(char **opts); + +#ifdef STB_DEFINE + +void stb_getopt_free(char **opts) +{ + int i; + char ** o2 = opts; + for (i=0; i < stb_arr_len(o2); ++i) + free(o2[i]); + stb_arr_free(o2); +} + +char **stb_getopt(int *argc, char **argv) +{ + return stb_getopt_param(argc, argv, ""); +} + +char **stb_getopt_param(int *argc, char **argv, char *param) +{ + char ** opts=NULL; + int i,j=1; + for (i=1; i < *argc; ++i) { + if (argv[i][0] != '-') { + argv[j++] = argv[i]; + } else { + if (argv[i][1] == 0) { // plain - == don't parse further options + ++i; + while (i < *argc) + argv[j++] = argv[i++]; + break; + } else { + int k; + char *q = argv[i]; // traverse options list + for (k=1; q[k]; ++k) { + char *s; + if (strchr(param, q[k])) { // does it take a parameter? + char *t = &q[k+1], z = q[k]; + int len=0; + if (*t == 0) { + if (i == *argc-1) { // takes a parameter, but none found + *argc = 0; + stb_getopt_free(opts); + return NULL; + } + t = argv[++i]; + } else + k += strlen(t); + len = strlen(t); + s = (char *) malloc(len+2); + if (!s) return NULL; + s[0] = z; + strcpy(s+1, t); + } else { + // no parameter + s = (char *) malloc(2); + if (!s) return NULL; + s[0] = q[k]; + s[1] = 0; + } + stb_arr_push(opts, s); + } + } + } + } + stb_arr_push(opts, NULL); + *argc = j; + return opts; +} +#endif + + +////////////////////////////////////////////////////////////////////////////// +// +// Portable directory reading +// + +STB_EXTERN char **stb_readdir_files (char *dir); +STB_EXTERN char **stb_readdir_files_mask(char *dir, char *wild); +STB_EXTERN char **stb_readdir_subdirs(char *dir); +STB_EXTERN char **stb_readdir_subdirs_mask(char *dir, char *wild); +STB_EXTERN void stb_readdir_free (char **files); +STB_EXTERN char **stb_readdir_recursive(char *dir, char *filespec); +STB_EXTERN void stb_delete_directory_recursive(char *dir); + +#ifdef STB_DEFINE + +#ifdef _MSC_VER +#include +#else +#include +#include +#endif + +void stb_readdir_free(char **files) +{ + char **f2 = files; + int i; + for (i=0; i < stb_arr_len(f2); ++i) + free(f2[i]); + stb_arr_free(f2); +} + +static int isdotdirname(char *name) +{ + if (name[0] == '.') + return (name[1] == '.') ? !name[2] : !name[1]; + return 0; +} + +STB_EXTERN int stb_wildmatchi(char *expr, char *candidate); +static char **readdir_raw(char *dir, int return_subdirs, char *mask) +{ + char **results = NULL; + char buffer[4096], with_slash[4096]; + size_t n; + + #ifdef _MSC_VER + stb__wchar *ws; + struct _wfinddata_t data; + #ifdef _WIN64 + const intptr_t none = -1; + intptr_t z; + #else + const long none = -1; + long z; + #endif + #else // !_MSC_VER + const DIR *none = NULL; + DIR *z; + #endif + + n = stb_strscpy(buffer,dir,sizeof(buffer)); + if (!n || n >= sizeof(buffer)) + return NULL; + stb_fixpath(buffer); + n--; + + if (n > 0 && (buffer[n-1] != '/')) { + buffer[n++] = '/'; + } + buffer[n] = 0; + if (!stb_strscpy(with_slash,buffer,sizeof(with_slash))) + return NULL; + + #ifdef _MSC_VER + if (!stb_strscpy(buffer+n,"*.*",sizeof(buffer)-n)) + return NULL; + ws = stb__from_utf8(buffer); + z = _wfindfirst((const wchar_t *)ws, &data); + #else + z = opendir(dir); + #endif + + if (z != none) { + int nonempty = STB_TRUE; + #ifndef _MSC_VER + struct dirent *data = readdir(z); + nonempty = (data != NULL); + #endif + + if (nonempty) { + + do { + int is_subdir; + #ifdef _MSC_VER + char *name = stb__to_utf8((stb__wchar *)data.name); + if (name == NULL) { + fprintf(stderr, "%s to convert '%S' to %s!\n", "Unable", data.name, "utf8"); + continue; + } + is_subdir = !!(data.attrib & _A_SUBDIR); + #else + char *name = data->d_name; + if (!stb_strscpy(buffer+n,name,sizeof(buffer)-n)) + break; + // Could follow DT_LNK, but would need to check for recursive links. + is_subdir = !!(data->d_type & DT_DIR); + #endif + + if (is_subdir == return_subdirs) { + if (!is_subdir || !isdotdirname(name)) { + if (!mask || stb_wildmatchi(mask, name)) { + char buffer[4096],*p=buffer; + if ( stb_snprintf(buffer, sizeof(buffer), "%s%s", with_slash, name) < 0 ) + break; + if (buffer[0] == '.' && buffer[1] == '/') + p = buffer+2; + stb_arr_push(results, strdup(p)); + } + } + } + } + #ifdef _MSC_VER + while (0 == _wfindnext(z, &data)); + #else + while ((data = readdir(z)) != NULL); + #endif + } + #ifdef _MSC_VER + _findclose(z); + #else + closedir(z); + #endif + } + return results; +} + +char **stb_readdir_files (char *dir) { return readdir_raw(dir, 0, NULL); } +char **stb_readdir_subdirs(char *dir) { return readdir_raw(dir, 1, NULL); } +char **stb_readdir_files_mask(char *dir, char *wild) { return readdir_raw(dir, 0, wild); } +char **stb_readdir_subdirs_mask(char *dir, char *wild) { return readdir_raw(dir, 1, wild); } + +int stb__rec_max=0x7fffffff; +static char **stb_readdir_rec(char **sofar, char *dir, char *filespec) +{ + char **files; + char ** dirs; + char **p; + + if (stb_arr_len(sofar) >= stb__rec_max) return sofar; + + files = stb_readdir_files_mask(dir, filespec); + stb_arr_for(p, files) { + stb_arr_push(sofar, strdup(*p)); + if (stb_arr_len(sofar) >= stb__rec_max) break; + } + stb_readdir_free(files); + if (stb_arr_len(sofar) >= stb__rec_max) return sofar; + + dirs = stb_readdir_subdirs(dir); + stb_arr_for(p, dirs) + sofar = stb_readdir_rec(sofar, *p, filespec); + stb_readdir_free(dirs); + return sofar; +} + +char **stb_readdir_recursive(char *dir, char *filespec) +{ + return stb_readdir_rec(NULL, dir, filespec); +} + +void stb_delete_directory_recursive(char *dir) +{ + char **list = stb_readdir_subdirs(dir); + int i; + for (i=0; i < stb_arr_len(list); ++i) + stb_delete_directory_recursive(list[i]); + stb_arr_free(list); + list = stb_readdir_files(dir); + for (i=0; i < stb_arr_len(list); ++i) + if (!remove(list[i])) { + // on windows, try again after making it writeable; don't ALWAYS + // do this first since that would be slow in the normal case + #ifdef _MSC_VER + _chmod(list[i], _S_IWRITE); + remove(list[i]); + #endif + } + stb_arr_free(list); + stb__windows(_rmdir,rmdir)(dir); +} + +#endif + +////////////////////////////////////////////////////////////////////////////// +// +// construct trees from filenames; useful for cmirror summaries + +typedef struct stb_dirtree2 stb_dirtree2; + +struct stb_dirtree2 +{ + stb_dirtree2 **subdirs; + + // make convenient for stb_summarize_tree + int num_subdir; + float weight; + + // actual data + char *fullpath; + char *relpath; + char **files; +}; + +STB_EXTERN stb_dirtree2 *stb_dirtree2_from_files_relative(char *src, char **filelist, int count); +STB_EXTERN stb_dirtree2 *stb_dirtree2_from_files(char **filelist, int count); +STB_EXTERN int stb_dir_is_prefix(char *dir, int dirlen, char *file); + +#ifdef STB_DEFINE + +int stb_dir_is_prefix(char *dir, int dirlen, char *file) +{ + if (dirlen == 0) return STB_TRUE; + if (stb_strnicmp(dir, file, dirlen)) return STB_FALSE; + if (file[dirlen] == '/' || file[dirlen] == '\\') return STB_TRUE; + return STB_FALSE; +} + +stb_dirtree2 *stb_dirtree2_from_files_relative(char *src, char **filelist, int count) +{ + char buffer1[1024]; + int i; + int dlen = strlen(src), elen; + stb_dirtree2 *d; + char ** descendents = NULL; + char ** files = NULL; + char *s; + if (!count) return NULL; + // first find all the ones that belong here... note this is will take O(NM) with N files and M subdirs + for (i=0; i < count; ++i) { + if (stb_dir_is_prefix(src, dlen, filelist[i])) { + stb_arr_push(descendents, filelist[i]); + } + } + if (descendents == NULL) + return NULL; + elen = dlen; + // skip a leading slash + if (elen == 0 && (descendents[0][0] == '/' || descendents[0][0] == '\\')) + ++elen; + else if (elen) + ++elen; + // now extract all the ones that have their root here + for (i=0; i < stb_arr_len(descendents);) { + if (!stb_strchr2(descendents[i]+elen, '/', '\\')) { + stb_arr_push(files, descendents[i]); + descendents[i] = descendents[stb_arr_len(descendents)-1]; + stb_arr_pop(descendents); + } else + ++i; + } + // now create a record + d = (stb_dirtree2 *) malloc(sizeof(*d)); + d->files = files; + d->subdirs = NULL; + d->fullpath = strdup(src); + s = stb_strrchr2(d->fullpath, '/', '\\'); + if (s) + ++s; + else + s = d->fullpath; + d->relpath = s; + // now create the children + qsort(descendents, stb_arr_len(descendents), sizeof(char *), stb_qsort_stricmp(0)); + buffer1[0] = 0; + for (i=0; i < stb_arr_len(descendents); ++i) { + char buffer2[1024]; + char *s = descendents[i] + elen, *t; + t = stb_strchr2(s, '/', '\\'); + assert(t); + stb_strncpy(buffer2, descendents[i], t-descendents[i]+1); + if (stb_stricmp(buffer1, buffer2)) { + stb_dirtree2 *t = stb_dirtree2_from_files_relative(buffer2, descendents, stb_arr_len(descendents)); + assert(t != NULL); + strcpy(buffer1, buffer2); + stb_arr_push(d->subdirs, t); + } + } + d->num_subdir = stb_arr_len(d->subdirs); + d->weight = 0; + return d; +} + +stb_dirtree2 *stb_dirtree2_from_files(char **filelist, int count) +{ + return stb_dirtree2_from_files_relative("", filelist, count); +} +#endif + +////////////////////////////////////////////////////////////////////////////// +// +// Checksums: CRC-32, ADLER32, SHA-1 +// +// CRC-32 and ADLER32 allow streaming blocks +// SHA-1 requires either a complete buffer, max size 2^32 - 73 +// or it can checksum directly from a file, max 2^61 + +#define STB_ADLER32_SEED 1 +#define STB_CRC32_SEED 0 // note that we logical NOT this in the code + +STB_EXTERN stb_uint + stb_adler32(stb_uint adler32, stb_uchar *buffer, stb_uint buflen); +STB_EXTERN stb_uint + stb_crc32_block(stb_uint crc32, stb_uchar *buffer, stb_uint len); +STB_EXTERN stb_uint stb_crc32(unsigned char *buffer, stb_uint len); + +STB_EXTERN void stb_sha1( + unsigned char output[20], unsigned char *buffer, unsigned int len); +STB_EXTERN int stb_sha1_file(unsigned char output[20], char *file); + +STB_EXTERN void stb_sha1_readable(char display[27], unsigned char sha[20]); + +#ifdef STB_DEFINE +stb_uint stb_crc32_block(stb_uint crc, unsigned char *buffer, stb_uint len) +{ + static stb_uint crc_table[256]; + stb_uint i,j,s; + crc = ~crc; + + if (crc_table[1] == 0) + for(i=0; i < 256; i++) { + for (s=i, j=0; j < 8; ++j) + s = (s >> 1) ^ (s & 1 ? 0xedb88320 : 0); + crc_table[i] = s; + } + for (i=0; i < len; ++i) + crc = (crc >> 8) ^ crc_table[buffer[i] ^ (crc & 0xff)]; + return ~crc; +} + +stb_uint stb_crc32(unsigned char *buffer, stb_uint len) +{ + return stb_crc32_block(0, buffer, len); +} + +stb_uint stb_adler32(stb_uint adler32, stb_uchar *buffer, stb_uint buflen) +{ + const unsigned long ADLER_MOD = 65521; + unsigned long s1 = adler32 & 0xffff, s2 = adler32 >> 16; + unsigned long blocklen, i; + + blocklen = buflen % 5552; + while (buflen) { + for (i=0; i + 7 < blocklen; i += 8) { + s1 += buffer[0], s2 += s1; + s1 += buffer[1], s2 += s1; + s1 += buffer[2], s2 += s1; + s1 += buffer[3], s2 += s1; + s1 += buffer[4], s2 += s1; + s1 += buffer[5], s2 += s1; + s1 += buffer[6], s2 += s1; + s1 += buffer[7], s2 += s1; + + buffer += 8; + } + + for (; i < blocklen; ++i) + s1 += *buffer++, s2 += s1; + + s1 %= ADLER_MOD, s2 %= ADLER_MOD; + buflen -= blocklen; + blocklen = 5552; + } + return (s2 << 16) + s1; +} + +static void stb__sha1(stb_uchar *chunk, stb_uint h[5]) +{ + int i; + stb_uint a,b,c,d,e; + stb_uint w[80]; + + for (i=0; i < 16; ++i) + w[i] = stb_big32(&chunk[i*4]); + for (i=16; i < 80; ++i) { + stb_uint t; + t = w[i-3] ^ w[i-8] ^ w[i-14] ^ w[i-16]; + w[i] = (t + t) | (t >> 31); + } + + a = h[0]; + b = h[1]; + c = h[2]; + d = h[3]; + e = h[4]; + + #define STB__SHA1(k,f) \ + { \ + stb_uint temp = (a << 5) + (a >> 27) + (f) + e + (k) + w[i]; \ + e = d; \ + d = c; \ + c = (b << 30) + (b >> 2); \ + b = a; \ + a = temp; \ + } + + i=0; + for (; i < 20; ++i) STB__SHA1(0x5a827999, d ^ (b & (c ^ d)) ); + for (; i < 40; ++i) STB__SHA1(0x6ed9eba1, b ^ c ^ d ); + for (; i < 60; ++i) STB__SHA1(0x8f1bbcdc, (b & c) + (d & (b ^ c)) ); + for (; i < 80; ++i) STB__SHA1(0xca62c1d6, b ^ c ^ d ); + + #undef STB__SHA1 + + h[0] += a; + h[1] += b; + h[2] += c; + h[3] += d; + h[4] += e; +} + +void stb_sha1(stb_uchar output[20], stb_uchar *buffer, stb_uint len) +{ + unsigned char final_block[128]; + stb_uint end_start, final_len, j; + int i; + + stb_uint h[5]; + + h[0] = 0x67452301; + h[1] = 0xefcdab89; + h[2] = 0x98badcfe; + h[3] = 0x10325476; + h[4] = 0xc3d2e1f0; + + // we need to write padding to the last one or two + // blocks, so build those first into 'final_block' + + // we have to write one special byte, plus the 8-byte length + + // compute the block where the data runs out + end_start = len & ~63; + + // compute the earliest we can encode the length + if (((len+9) & ~63) == end_start) { + // it all fits in one block, so fill a second-to-last block + end_start -= 64; + } + + final_len = end_start + 128; + + // now we need to copy the data in + assert(end_start + 128 >= len+9); + assert(end_start < len || len < 64-9); + + j = 0; + if (end_start > len) + j = (stb_uint) - (int) end_start; + + for (; end_start + j < len; ++j) + final_block[j] = buffer[end_start + j]; + final_block[j++] = 0x80; + while (j < 128-5) // 5 byte length, so write 4 extra padding bytes + final_block[j++] = 0; + // big-endian size + final_block[j++] = len >> 29; + final_block[j++] = len >> 21; + final_block[j++] = len >> 13; + final_block[j++] = len >> 5; + final_block[j++] = len << 3; + assert(j == 128 && end_start + j == final_len); + + for (j=0; j < final_len; j += 64) { // 512-bit chunks + if (j+64 >= end_start+64) + stb__sha1(&final_block[j - end_start], h); + else + stb__sha1(&buffer[j], h); + } + + for (i=0; i < 5; ++i) { + output[i*4 + 0] = h[i] >> 24; + output[i*4 + 1] = h[i] >> 16; + output[i*4 + 2] = h[i] >> 8; + output[i*4 + 3] = h[i] >> 0; + } +} + +#ifdef _MSC_VER +int stb_sha1_file(stb_uchar output[20], char *file) +{ + int i; + stb_uint64 length=0; + unsigned char buffer[128]; + + FILE *f = stb__fopen(file, "rb"); + stb_uint h[5]; + + if (f == NULL) return 0; // file not found + + h[0] = 0x67452301; + h[1] = 0xefcdab89; + h[2] = 0x98badcfe; + h[3] = 0x10325476; + h[4] = 0xc3d2e1f0; + + for(;;) { + int n = fread(buffer, 1, 64, f); + if (n == 64) { + stb__sha1(buffer, h); + length += n; + } else { + int block = 64; + + length += n; + + buffer[n++] = 0x80; + + // if there isn't enough room for the length, double the block + if (n + 8 > 64) + block = 128; + + // pad to end + memset(buffer+n, 0, block-8-n); + + i = block - 8; + buffer[i++] = (stb_uchar) (length >> 53); + buffer[i++] = (stb_uchar) (length >> 45); + buffer[i++] = (stb_uchar) (length >> 37); + buffer[i++] = (stb_uchar) (length >> 29); + buffer[i++] = (stb_uchar) (length >> 21); + buffer[i++] = (stb_uchar) (length >> 13); + buffer[i++] = (stb_uchar) (length >> 5); + buffer[i++] = (stb_uchar) (length << 3); + assert(i == block); + stb__sha1(buffer, h); + if (block == 128) + stb__sha1(buffer+64, h); + else + assert(block == 64); + break; + } + } + fclose(f); + + for (i=0; i < 5; ++i) { + output[i*4 + 0] = h[i] >> 24; + output[i*4 + 1] = h[i] >> 16; + output[i*4 + 2] = h[i] >> 8; + output[i*4 + 3] = h[i] >> 0; + } + + return 1; +} +#endif // _MSC_VER + +// client can truncate this wherever they like +void stb_sha1_readable(char display[27], unsigned char sha[20]) +{ + char encoding[65] = "0123456789abcdefghijklmnopqrstuv" + "wxyzABCDEFGHIJKLMNOPQRSTUVWXYZ%$"; + int num_bits = 0, acc=0; + int i=0,o=0; + while (o < 26) { + int v; + // expand the accumulator + if (num_bits < 6) { + assert(i != 20); + acc += sha[i++] << num_bits; + num_bits += 8; + } + v = acc & ((1 << 6) - 1); + display[o++] = encoding[v]; + acc >>= 6; + num_bits -= 6; + } + assert(num_bits == 20*8 - 26*6); + display[o++] = encoding[acc]; +} + +#endif // STB_DEFINE + +/////////////////////////////////////////////////////////// +// +// simplified WINDOWS registry interface... hopefully +// we'll never actually use this? + +#if defined(_WIN32) + +STB_EXTERN void * stb_reg_open(char *mode, char *where); // mode: "rHKLM" or "rHKCU" or "w.." +STB_EXTERN void stb_reg_close(void *reg); +STB_EXTERN int stb_reg_read(void *zreg, char *str, void *data, unsigned long len); +STB_EXTERN int stb_reg_read_string(void *zreg, char *str, char *data, int len); +STB_EXTERN void stb_reg_write(void *zreg, char *str, void *data, unsigned long len); +STB_EXTERN void stb_reg_write_string(void *zreg, char *str, char *data); + +#if defined(STB_DEFINE) && !defined(STB_NO_REGISTRY) + +#define STB_HAS_REGISTRY + +#ifndef _WINDOWS_ + +#define HKEY void * + +STB_EXTERN __declspec(dllimport) long __stdcall RegCloseKey ( HKEY hKey ); +STB_EXTERN __declspec(dllimport) long __stdcall RegCreateKeyExA ( HKEY hKey, const char * lpSubKey, + int Reserved, char * lpClass, int dwOptions, + int samDesired, void *lpSecurityAttributes, HKEY * phkResult, int * lpdwDisposition ); +STB_EXTERN __declspec(dllimport) long __stdcall RegDeleteKeyA ( HKEY hKey, const char * lpSubKey ); +STB_EXTERN __declspec(dllimport) long __stdcall RegQueryValueExA ( HKEY hKey, const char * lpValueName, + int * lpReserved, unsigned long * lpType, unsigned char * lpData, unsigned long * lpcbData ); +STB_EXTERN __declspec(dllimport) long __stdcall RegSetValueExA ( HKEY hKey, const char * lpValueName, + int Reserved, int dwType, const unsigned char* lpData, int cbData ); +STB_EXTERN __declspec(dllimport) long __stdcall RegOpenKeyExA ( HKEY hKey, const char * lpSubKey, + int ulOptions, int samDesired, HKEY * phkResult ); + +#endif // _WINDOWS_ + +#define STB__REG_OPTION_NON_VOLATILE 0 +#define STB__REG_KEY_ALL_ACCESS 0x000f003f +#define STB__REG_KEY_READ 0x00020019 + +void *stb_reg_open(char *mode, char *where) +{ + long res; + HKEY base; + HKEY zreg; + if (!stb_stricmp(mode+1, "cu") || !stb_stricmp(mode+1, "hkcu")) + base = (HKEY) 0x80000001; // HKCU + else if (!stb_stricmp(mode+1, "lm") || !stb_stricmp(mode+1, "hklm")) + base = (HKEY) 0x80000002; // HKLM + else + return NULL; + + if (mode[0] == 'r') + res = RegOpenKeyExA(base, where, 0, STB__REG_KEY_READ, &zreg); + else if (mode[0] == 'w') + res = RegCreateKeyExA(base, where, 0, NULL, STB__REG_OPTION_NON_VOLATILE, STB__REG_KEY_ALL_ACCESS, NULL, &zreg, NULL); + else + return NULL; + + return res ? NULL : zreg; +} + +void stb_reg_close(void *reg) +{ + RegCloseKey((HKEY) reg); +} + +#define STB__REG_SZ 1 +#define STB__REG_BINARY 3 +#define STB__REG_DWORD 4 + +int stb_reg_read(void *zreg, char *str, void *data, unsigned long len) +{ + unsigned long type; + unsigned long alen = len; + if (0 == RegQueryValueExA((HKEY) zreg, str, 0, &type, (unsigned char *) data, &len)) + if (type == STB__REG_BINARY || type == STB__REG_SZ || type == STB__REG_DWORD) { + if (len < alen) + *((char *) data + len) = 0; + return 1; + } + return 0; +} + +void stb_reg_write(void *zreg, char *str, void *data, unsigned long len) +{ + if (zreg) + RegSetValueExA((HKEY) zreg, str, 0, STB__REG_BINARY, (const unsigned char *) data, len); +} + +int stb_reg_read_string(void *zreg, char *str, char *data, int len) +{ + if (!stb_reg_read(zreg, str, data, len)) return 0; + data[len-1] = 0; // force a 0 at the end of the string no matter what + return 1; +} + +void stb_reg_write_string(void *zreg, char *str, char *data) +{ + if (zreg) + RegSetValueExA((HKEY) zreg, str, 0, STB__REG_SZ, (const unsigned char *) data, strlen(data)+1); +} +#endif // STB_DEFINE +#endif // _WIN32 + + +////////////////////////////////////////////////////////////////////////////// +// +// stb_cfg - This is like the registry, but the config info +// is all stored in plain old files where we can +// backup and restore them easily. The LOCATION of +// the config files is gotten from... the registry! + +#ifndef STB_NO_STB_STRINGS +typedef struct stb_cfg_st stb_cfg; + +STB_EXTERN stb_cfg * stb_cfg_open(char *config, char *mode); // mode = "r", "w" +STB_EXTERN void stb_cfg_close(stb_cfg *cfg); +STB_EXTERN int stb_cfg_read(stb_cfg *cfg, char *key, void *value, int len); +STB_EXTERN void stb_cfg_write(stb_cfg *cfg, char *key, void *value, int len); +STB_EXTERN int stb_cfg_read_string(stb_cfg *cfg, char *key, char *value, int len); +STB_EXTERN void stb_cfg_write_string(stb_cfg *cfg, char *key, char *value); +STB_EXTERN int stb_cfg_delete(stb_cfg *cfg, char *key); +STB_EXTERN void stb_cfg_set_directory(char *dir); + +#ifdef STB_DEFINE + +typedef struct +{ + char *key; + void *value; + int value_len; +} stb__cfg_item; + +struct stb_cfg_st +{ + stb__cfg_item *data; + char *loaded_file; // this needs to be freed + FILE *f; // write the data to this file on close +}; + +static char *stb__cfg_sig = "sTbCoNfIg!\0\0"; +static char stb__cfg_dir[512]; +STB_EXTERN void stb_cfg_set_directory(char *dir) +{ + strcpy(stb__cfg_dir, dir); +} + +STB_EXTERN stb_cfg * stb_cfg_open(char *config, char *mode) +{ + size_t len; + stb_cfg *z; + char file[512]; + if (mode[0] != 'r' && mode[0] != 'w') return NULL; + + if (!stb__cfg_dir[0]) { + #ifdef _WIN32 + strcpy(stb__cfg_dir, "c:/stb"); + #else + strcpy(stb__cfg_dir, "~/.stbconfig"); + #endif + + #ifdef STB_HAS_REGISTRY + { + void *reg = stb_reg_open("rHKLM", "Software\\SilverSpaceship\\stb"); + if (reg) { + stb_reg_read_string(reg, "config_dir", stb__cfg_dir, sizeof(stb__cfg_dir)); + stb_reg_close(reg); + } + } + #endif + } + + sprintf(file, "%s/%s.cfg", stb__cfg_dir, config); + + z = (stb_cfg *) stb_malloc(0, sizeof(*z)); + z->data = NULL; + + z->loaded_file = stb_filec(file, &len); + if (z->loaded_file) { + char *s = z->loaded_file; + if (!memcmp(s, stb__cfg_sig, 12)) { + char *s = z->loaded_file + 12; + while (s < z->loaded_file + len) { + stb__cfg_item a; + int n = *(stb_int16 *) s; + a.key = s+2; + s = s+2 + n; + a.value_len = *(int *) s; + s += 4; + a.value = s; + s += a.value_len; + stb_arr_push(z->data, a); + } + assert(s == z->loaded_file + len); + } + } + + if (mode[0] == 'w') + z->f = fopen(file, "wb"); + else + z->f = NULL; + + return z; +} + +void stb_cfg_close(stb_cfg *z) +{ + if (z->f) { + int i; + // write the file out + fwrite(stb__cfg_sig, 12, 1, z->f); + for (i=0; i < stb_arr_len(z->data); ++i) { + stb_int16 n = strlen(z->data[i].key)+1; + fwrite(&n, 2, 1, z->f); + fwrite(z->data[i].key, n, 1, z->f); + fwrite(&z->data[i].value_len, 4, 1, z->f); + fwrite(z->data[i].value, z->data[i].value_len, 1, z->f); + } + fclose(z->f); + } + stb_arr_free(z->data); + stb_free(z); +} + +int stb_cfg_read(stb_cfg *z, char *key, void *value, int len) +{ + int i; + for (i=0; i < stb_arr_len(z->data); ++i) { + if (!stb_stricmp(z->data[i].key, key)) { + int n = stb_min(len, z->data[i].value_len); + memcpy(value, z->data[i].value, n); + if (n < len) + *((char *) value + n) = 0; + return 1; + } + } + return 0; +} + +void stb_cfg_write(stb_cfg *z, char *key, void *value, int len) +{ + int i; + for (i=0; i < stb_arr_len(z->data); ++i) + if (!stb_stricmp(z->data[i].key, key)) + break; + if (i == stb_arr_len(z->data)) { + stb__cfg_item p; + p.key = stb_strdup(key, z); + p.value = NULL; + p.value_len = 0; + stb_arr_push(z->data, p); + } + z->data[i].value = stb_malloc(z, len); + z->data[i].value_len = len; + memcpy(z->data[i].value, value, len); +} + +int stb_cfg_delete(stb_cfg *z, char *key) +{ + int i; + for (i=0; i < stb_arr_len(z->data); ++i) + if (!stb_stricmp(z->data[i].key, key)) { + stb_arr_fastdelete(z->data, i); + return 1; + } + return 0; +} + +int stb_cfg_read_string(stb_cfg *z, char *key, char *value, int len) +{ + if (!stb_cfg_read(z, key, value, len)) return 0; + value[len-1] = 0; + return 1; +} + +void stb_cfg_write_string(stb_cfg *z, char *key, char *value) +{ + stb_cfg_write(z, key, value, strlen(value)+1); +} +#endif + +////////////////////////////////////////////////////////////////////////////// +// +// stb_dirtree - load a description of a directory tree +// uses a cache and stat()s the directories for changes +// MUCH faster on NTFS, _wrong_ on FAT32, so should +// ignore the db on FAT32 + +#ifdef _WIN32 + +typedef struct +{ + char * path; // full path from passed-in root + time_t last_modified; + int num_files; +} stb_dirtree_dir; + +typedef struct +{ + char *name; // name relative to path + int dir; // index into dirs[] array + unsigned long size; // size, max 4GB + time_t last_modified; +} stb_dirtree_file; + +typedef struct +{ + stb_dirtree_dir *dirs; + stb_dirtree_file *files; + + // internal use + void * string_pool; // used to free data en masse +} stb_dirtree; + +extern void stb_dirtree_free ( stb_dirtree *d ); +extern stb_dirtree *stb_dirtree_get ( char *dir); +extern stb_dirtree *stb_dirtree_get_dir ( char *dir, char *cache_dir); +extern stb_dirtree *stb_dirtree_get_with_file ( char *dir, char *cache_file); + +// get a list of all the files recursively underneath 'dir' +// +// cache_file is used to store a copy of the directory tree to speed up +// later calls. It must be unique to 'dir' and the current working +// directory! Otherwise who knows what will happen (a good solution +// is to put it _in_ dir, but this API doesn't force that). +// +// Also, it might be possible to break this if you have two different processes +// do a call to stb_dirtree_get() with the same cache file at about the same +// time, but I _think_ it might just work. + + +#ifdef STB_DEFINE +static void stb__dirtree_add_dir(char *path, time_t last, stb_dirtree *active) +{ + stb_dirtree_dir d; + d.last_modified = last; + d.num_files = 0; + d.path = stb_strdup(path, active->string_pool); + stb_arr_push(active->dirs, d); +} + +static void stb__dirtree_add_file(char *name, int dir, unsigned long size, time_t last, stb_dirtree *active) +{ + stb_dirtree_file f; + f.dir = dir; + f.size = size; + f.last_modified = last; + f.name = stb_strdup(name, active->string_pool); + ++active->dirs[dir].num_files; + stb_arr_push(active->files, f); +} + +static char stb__signature[12] = { 's', 'T', 'b', 'D', 'i', 'R', 't', 'R', 'e', 'E', '0', '1' }; + +static void stb__dirtree_save_db(char *filename, stb_dirtree *data, char *root) +{ + int i, num_dirs_final=0, num_files_final; + int *remap; + FILE *f = fopen(filename, "wb"); + if (!f) return; + + fwrite(stb__signature, sizeof(stb__signature), 1, f); + fwrite(root, strlen(root)+1, 1, f); + // need to be slightly tricky and not write out NULLed directories, nor the root + + // build remapping table of all dirs we'll be writing out + remap = (int *) malloc(sizeof(remap[0]) * stb_arr_len(data->dirs)); + for (i=0; i < stb_arr_len(data->dirs); ++i) { + if (data->dirs[i].path == NULL || 0==stb_stricmp(data->dirs[i].path, root)) { + remap[i] = -1; + } else { + remap[i] = num_dirs_final++; + } + } + + fwrite(&num_dirs_final, 4, 1, f); + for (i=0; i < stb_arr_len(data->dirs); ++i) { + if (remap[i] >= 0) { + fwrite(&data->dirs[i].last_modified, 4, 1, f); + stb_fput_string(f, data->dirs[i].path); + } + } + + num_files_final = 0; + for (i=0; i < stb_arr_len(data->files); ++i) + if (remap[data->files[i].dir] >= 0) + ++num_files_final; + + fwrite(&num_files_final, 4, 1, f); + for (i=0; i < stb_arr_len(data->files); ++i) { + if (remap[data->files[i].dir] >= 0) { + stb_fput_ranged(f, remap[data->files[i].dir], 0, num_dirs_final); + stb_fput_varlenu(f, data->files[i].size); + fwrite(&data->files[i].last_modified, 4, 1, f); + stb_fput_string(f, data->files[i].name); + } + } + + fclose(f); +} + +// note: stomps any existing data, rather than appending +static void stb__dirtree_load_db(char *filename, stb_dirtree *data, char *dir) +{ + char sig[2048]; + int i,n; + FILE *f = fopen(filename, "rb"); + + if (!f) return; + + data->string_pool = stb_malloc(0,1); + + fread(sig, sizeof(stb__signature), 1, f); + if (memcmp(stb__signature, sig, sizeof(stb__signature))) { fclose(f); return; } + if (!fread(sig, strlen(dir)+1, 1, f)) { fclose(f); return; } + if (stb_stricmp(sig,dir)) { fclose(f); return; } + + // we can just read them straight in, because they're guaranteed to be valid + fread(&n, 4, 1, f); + stb_arr_setlen(data->dirs, n); + for(i=0; i < stb_arr_len(data->dirs); ++i) { + fread(&data->dirs[i].last_modified, 4, 1, f); + data->dirs[i].path = stb_fget_string(f, data->string_pool); + if (data->dirs[i].path == NULL) goto bail; + } + fread(&n, 4, 1, f); + stb_arr_setlen(data->files, n); + for (i=0; i < stb_arr_len(data->files); ++i) { + data->files[i].dir = stb_fget_ranged(f, 0, stb_arr_len(data->dirs)); + data->files[i].size = stb_fget_varlenu(f); + fread(&data->files[i].last_modified, 4, 1, f); + data->files[i].name = stb_fget_string(f, data->string_pool); + if (data->files[i].name == NULL) goto bail; + } + + if (0) { + bail: + stb_arr_free(data->dirs); + stb_arr_free(data->files); + } + fclose(f); +} + +static void stb__dirtree_scandir(char *path, time_t last_time, stb_dirtree *active) +{ + // this is dumb depth first; theoretically it might be faster + // to fully traverse each directory before visiting its children, + // but it's complicated and didn't seem like a gain in the test app + + int n; + + struct _wfinddata_t c_file; + #ifdef STB_PTR64 + intptr_t hFile; + #else + long hFile; + #endif + stb__wchar full_path[1024]; + int has_slash; + + has_slash = (path[0] && path[strlen(path)-1] == '/'); + if (has_slash) + swprintf((wchar_t *)full_path, L"%s*", stb__from_utf8(path)); + else + swprintf((wchar_t *)full_path, L"%s/*", stb__from_utf8(path)); + + // it's possible this directory is already present: that means it was in the + // cache, but its parent wasn't... in that case, we're done with it + for (n=0; n < stb_arr_len(active->dirs); ++n) + if (0 == stb_stricmp(active->dirs[n].path, path)) + return; + + // otherwise, we need to add it + stb__dirtree_add_dir(path, last_time, active); + n = stb_arr_lastn(active->dirs); + + if( (hFile = _wfindfirst((const wchar_t *)full_path, &c_file )) != -1L ) { + do { + if (c_file.attrib & _A_SUBDIR) { + // ignore subdirectories starting with '.', e.g. "." and ".." + if (c_file.name[0] != '.') { + char *new_path = (char *) full_path; + char *temp = stb__to_utf8((stb__wchar *)c_file.name); + if (has_slash) + sprintf(new_path, "%s%s", path, temp); + else + sprintf(new_path, "%s/%s", path, temp); + stb__dirtree_scandir(new_path, c_file.time_write, active); + } + } else { + char *temp = stb__to_utf8((stb__wchar *)c_file.name); + stb__dirtree_add_file(temp, n, c_file.size, c_file.time_write, active); + } + } while( _wfindnext( hFile, &c_file ) == 0 ); + + _findclose( hFile ); + } +} + +// scan the database and see if it's all valid +static int stb__dirtree_update_db(stb_dirtree *db, stb_dirtree *active) +{ + int changes_detected = STB_FALSE; + int i; + int *remap; + int *rescan=NULL; + remap = (int *) malloc(sizeof(remap[0]) * stb_arr_len(db->dirs)); + memset(remap, 0, sizeof(remap[0]) * stb_arr_len(db->dirs)); + rescan = NULL; + + for (i=0; i < stb_arr_len(db->dirs); ++i) { + struct _stat info; + if (0 == _stat(db->dirs[i].path, &info)) { + if (info.st_mode & _S_IFDIR) { + // it's still a directory, as expected + if (info.st_mtime > db->dirs[i].last_modified) { + // it's changed! force a rescan + // we don't want to scan it until we've stat()d its + // subdirs, though, so we queue it + stb_arr_push(rescan, i); + // update the last_mod time + db->dirs[i].last_modified = info.st_mtime; + // ignore existing files in this dir + remap[i] = -1; + changes_detected = STB_TRUE; + } else { + // it hasn't changed, just copy it through unchanged + stb__dirtree_add_dir(db->dirs[i].path, db->dirs[i].last_modified, active); + remap[i] = stb_arr_lastn(active->dirs); + } + } else { + // this path used to refer to a directory, but now it's a file! + // assume that the parent directory is going to be forced to rescan anyway + goto delete_entry; + } + } else { + delete_entry: + // directory no longer exists, so don't copy it + // we don't free it because it's in the string pool now + db->dirs[i].path = NULL; + remap[i] = -1; + changes_detected = STB_TRUE; + } + } + + // at this point, we have: + // + // holds a list of directory indices that need to be scanned due to being out of date + // holds the directory index in for each dir in , if it exists; -1 if not + // directories in are not in yet + + // so we can go ahead and remap all the known files right now + for (i=0; i < stb_arr_len(db->files); ++i) { + int dir = db->files[i].dir; + if (remap[dir] >= 0) { + stb__dirtree_add_file(db->files[i].name, remap[dir], db->files[i].size, db->files[i].last_modified, active); + } + } + + // at this point we're done with db->files, and done with remap + free(remap); + + // now scan those directories using the standard scan + for (i=0; i < stb_arr_len(rescan); ++i) { + int z = rescan[i]; + stb__dirtree_scandir(db->dirs[z].path, db->dirs[z].last_modified, active); + } + stb_arr_free(rescan); + + return changes_detected; +} + +static void stb__dirtree_free_raw(stb_dirtree *d) +{ + stb_free(d->string_pool); + stb_arr_free(d->dirs); + stb_arr_free(d->files); +} + +stb_dirtree *stb_dirtree_get_with_file(char *dir, char *cache_file) +{ + stb_dirtree *output = (stb_dirtree *) malloc(sizeof(*output)); + stb_dirtree db,active; + int prev_dir_count, cache_mismatch; + + char *stripped_dir; // store the directory name without a trailing '/' or '\\' + + // load the database of last-known state on disk + db.string_pool = NULL; + db.files = NULL; + db.dirs = NULL; + + stripped_dir = stb_strip_final_slash(strdup(dir)); + + if (cache_file != NULL) + stb__dirtree_load_db(cache_file, &db, stripped_dir); + + active.files = NULL; + active.dirs = NULL; + active.string_pool = stb_malloc(0,1); // @TODO: share string pools between both? + + // check all the directories in the database; make note if + // anything we scanned had changed, and rescan those things + cache_mismatch = stb__dirtree_update_db(&db, &active); + + // check the root tree + prev_dir_count = stb_arr_len(active.dirs); // record how many directories we've seen + + stb__dirtree_scandir(stripped_dir, 0, &active); // no last_modified time available for root + + // done with the DB; write it back out if any changes, i.e. either + // 1. any inconsistency found between cached information and actual disk + // or 2. if scanning the root found any new directories--which we detect because + // more than one directory got added to the active db during that scan + if (cache_mismatch || stb_arr_len(active.dirs) > prev_dir_count+1) + stb__dirtree_save_db(cache_file, &active, stripped_dir); + + free(stripped_dir); + + stb__dirtree_free_raw(&db); + + *output = active; + return output; +} + +stb_dirtree *stb_dirtree_get_dir(char *dir, char *cache_dir) +{ + int i; + stb_uint8 sha[20]; + char dir_lower[1024]; + char cache_file[1024],*s; + if (cache_dir == NULL) + return stb_dirtree_get_with_file(dir, NULL); + strcpy(dir_lower, dir); + stb_tolower(dir_lower); + stb_sha1(sha, (unsigned char *) dir_lower, strlen(dir_lower)); + strcpy(cache_file, cache_dir); + s = cache_file + strlen(cache_file); + if (s[-1] != '/' && s[-1] != '\\') *s++ = '/'; + strcpy(s, "dirtree_"); + s += strlen(s); + for (i=0; i < 8; ++i) { + char *hex = "0123456789abcdef"; + stb_uint z = sha[i]; + *s++ = hex[z >> 4]; + *s++ = hex[z & 15]; + } + strcpy(s, ".bin"); + return stb_dirtree_get_with_file(dir, cache_file); +} + +stb_dirtree *stb_dirtree_get(char *dir) +{ + char cache_dir[256]; + strcpy(cache_dir, "c:/stb"); + #ifdef STB_HAS_REGISTRY + { + void *reg = stb_reg_open("rHKLM", "Software\\SilverSpaceship\\stb"); + if (reg) { + stb_reg_read(reg, "dirtree", cache_dir, sizeof(cache_dir)); + stb_reg_close(reg); + } + } + #endif + return stb_dirtree_get_dir(dir, cache_dir); +} + +void stb_dirtree_free(stb_dirtree *d) +{ + stb__dirtree_free_raw(d); + free(d); +} +#endif // STB_DEFINE + +#endif // _WIN32 +#endif // STB_NO_STB_STRINGS + +////////////////////////////////////////////////////////////////////////////// +// +// STB_MALLOC_WRAPPER +// +// you can use the wrapper functions with your own malloc wrapper, +// or define STB_MALLOC_WRAPPER project-wide to have +// malloc/free/realloc/strdup all get vectored to it + +// this has too many very specific error messages you could google for and find in stb.h, +// so don't use it if they don't want any stb.h-identifiable strings +#if defined(STB_DEFINE) && !defined(STB_NO_STB_STRINGS) + +typedef struct +{ + void *p; + char *file; + int line; + int size; +} stb_malloc_record; + +#ifndef STB_MALLOC_HISTORY_COUNT +#define STB_MALLOC_HISTORY_COUNT 50 // 800 bytes +#endif + +stb_malloc_record *stb__allocations; +static int stb__alloc_size, stb__alloc_limit, stb__alloc_mask; +int stb__alloc_count; + +stb_malloc_record stb__alloc_history[STB_MALLOC_HISTORY_COUNT]; +int stb__history_pos; + +static int stb__hashfind(void *p) +{ + stb_uint32 h = stb_hashptr(p); + int s,n = h & stb__alloc_mask; + if (stb__allocations[n].p == p) + return n; + s = stb_rehash(h)|1; + for(;;) { + if (stb__allocations[n].p == NULL) + return -1; + n = (n+s) & stb__alloc_mask; + if (stb__allocations[n].p == p) + return n; + } +} + +int stb_wrapper_allocsize(void *p) +{ + int n = stb__hashfind(p); + if (n < 0) return 0; + return stb__allocations[n].size; +} + +static int stb__historyfind(void *p) +{ + int n = stb__history_pos; + int i; + for (i=0; i < STB_MALLOC_HISTORY_COUNT; ++i) { + if (--n < 0) n = STB_MALLOC_HISTORY_COUNT-1; + if (stb__alloc_history[n].p == p) + return n; + } + return -1; +} + +static void stb__add_alloc(void *p, int sz, char *file, int line); +static void stb__grow_alloc(void) +{ + int i,old_num = stb__alloc_size; + stb_malloc_record *old = stb__allocations; + if (stb__alloc_size == 0) + stb__alloc_size = 64; + else + stb__alloc_size *= 2; + + stb__allocations = (stb_malloc_record *) stb__realloc_raw(NULL, stb__alloc_size * sizeof(stb__allocations[0])); + if (stb__allocations == NULL) + stb_fatal("Internal error: couldn't grow malloc wrapper table"); + memset(stb__allocations, 0, stb__alloc_size * sizeof(stb__allocations[0])); + stb__alloc_limit = (stb__alloc_size*3)>>2; + stb__alloc_mask = stb__alloc_size-1; + + stb__alloc_count = 0; + + for (i=0; i < old_num; ++i) + if (old[i].p > STB_DEL) { + stb__add_alloc(old[i].p, old[i].size, old[i].file, old[i].line); + assert(stb__hashfind(old[i].p) >= 0); + } + for (i=0; i < old_num; ++i) + if (old[i].p > STB_DEL) + assert(stb__hashfind(old[i].p) >= 0); + stb__realloc_raw(old, 0); +} + +static void stb__add_alloc(void *p, int sz, char *file, int line) +{ + stb_uint32 h; + int n; + if (stb__alloc_count >= stb__alloc_limit) + stb__grow_alloc(); + h = stb_hashptr(p); + n = h & stb__alloc_mask; + if (stb__allocations[n].p > STB_DEL) { + int s = stb_rehash(h)|1; + do { + n = (n+s) & stb__alloc_mask; + } while (stb__allocations[n].p > STB_DEL); + } + assert(stb__allocations[n].p == NULL || stb__allocations[n].p == STB_DEL); + stb__allocations[n].p = p; + stb__allocations[n].size = sz; + stb__allocations[n].line = line; + stb__allocations[n].file = file; + ++stb__alloc_count; +} + +static void stb__remove_alloc(int n, char *file, int line) +{ + stb__alloc_history[stb__history_pos] = stb__allocations[n]; + stb__alloc_history[stb__history_pos].file = file; + stb__alloc_history[stb__history_pos].line = line; + if (++stb__history_pos == STB_MALLOC_HISTORY_COUNT) + stb__history_pos = 0; + stb__allocations[n].p = STB_DEL; + --stb__alloc_count; +} + +void stb_wrapper_malloc(void *p, int sz, char *file, int line) +{ + if (!p) return; + stb__add_alloc(p,sz,file,line); +} + +void stb_wrapper_free(void *p, char *file, int line) +{ + int n; + + if (p == NULL) return; + + n = stb__hashfind(p); + + if (n >= 0) + stb__remove_alloc(n, file, line); + else { + // tried to free something we hadn't allocated! + n = stb__historyfind(p); + assert(0); /* NOTREACHED */ + if (n >= 0) + stb_fatal("Attempted to free %d-byte block %p at %s:%d previously freed/realloced at %s:%d", + stb__alloc_history[n].size, p, + file, line, + stb__alloc_history[n].file, stb__alloc_history[n].line); + else + stb_fatal("Attempted to free unknown block %p at %s:%d", p, file,line); + } +} + +void stb_wrapper_check(void *p) +{ + int n; + + if (p == NULL) return; + + n = stb__hashfind(p); + + if (n >= 0) return; + + for (n=0; n < stb__alloc_size; ++n) + if (stb__allocations[n].p == p) + stb_fatal("Internal error: pointer %p was allocated, but hash search failed", p); + + // tried to free something that wasn't allocated! + n = stb__historyfind(p); + if (n >= 0) + stb_fatal("Checked %d-byte block %p previously freed/realloced at %s:%d", + stb__alloc_history[n].size, p, + stb__alloc_history[n].file, stb__alloc_history[n].line); + stb_fatal("Checked unknown block %p"); +} + +void stb_wrapper_realloc(void *p, void *q, int sz, char *file, int line) +{ + int n; + if (p == NULL) { stb_wrapper_malloc(q, sz, file, line); return; } + if (q == NULL) return; // nothing happened + + n = stb__hashfind(p); + if (n == -1) { + // tried to free something we hadn't allocated! + // this is weird, though, because we got past the realloc! + n = stb__historyfind(p); + assert(0); /* NOTREACHED */ + if (n >= 0) + stb_fatal("Attempted to realloc %d-byte block %p at %s:%d previously freed/realloced at %s:%d", + stb__alloc_history[n].size, p, + file, line, + stb__alloc_history[n].file, stb__alloc_history[n].line); + else + stb_fatal("Attempted to realloc unknown block %p at %s:%d", p, file,line); + } else { + if (q == p) { + stb__allocations[n].size = sz; + stb__allocations[n].file = file; + stb__allocations[n].line = line; + } else { + stb__remove_alloc(n, file, line); + stb__add_alloc(q,sz,file,line); + } + } +} + +void stb_wrapper_listall(void (*func)(void *ptr, int sz, char *file, int line)) +{ + int i; + for (i=0; i < stb__alloc_size; ++i) + if (stb__allocations[i].p > STB_DEL) + func(stb__allocations[i].p , stb__allocations[i].size, + stb__allocations[i].file, stb__allocations[i].line); +} + +void stb_wrapper_dump(char *filename) +{ + int i; + FILE *f = fopen(filename, "w"); + if (!f) return; + for (i=0; i < stb__alloc_size; ++i) + if (stb__allocations[i].p > STB_DEL) + fprintf(f, "%p %7d - %4d %s\n", + stb__allocations[i].p , stb__allocations[i].size, + stb__allocations[i].line, stb__allocations[i].file); +} +#endif // STB_DEFINE + + +////////////////////////////////////////////////////////////////////////////// +// +// stb_pointer_set +// +// +// For data structures that support querying by key, data structure +// classes always hand-wave away the issue of what to do if two entries +// have the same key: basically, store a linked list of all the nodes +// which have the same key (a LISP-style list). +// +// The thing is, it's not that trivial. If you have an O(log n) +// lookup data structure, but then n/4 items have the same value, +// you don't want to spend O(n) time scanning that list when +// deleting an item if you already have a pointer to the item. +// (You have to spend O(n) time enumerating all the items with +// a given key, sure, and you can't accelerate deleting a particular +// item if you only have the key, not a pointer to the item.) +// +// I'm going to call this data structure, whatever it turns out to +// be, a "pointer set", because we don't store any associated data for +// items in this data structure, we just answer the question of +// whether an item is in it or not (it's effectively one bit per pointer). +// Technically they don't have to be pointers; you could cast ints +// to (void *) if you want, but you can't store 0 or 1 because of the +// hash table. +// +// Since the fastest data structure we might want to add support for +// identical-keys to is a hash table with O(1)-ish lookup time, +// that means that the conceptual "linked list of all items with +// the same indexed value" that we build needs to have the same +// performance; that way when we index a table we think is arbitrary +// ints, but in fact half of them are 0, we don't get screwed. +// +// Therefore, it needs to be a hash table, at least when it gets +// large. On the other hand, when the data has totally arbitrary ints +// or floats, there won't be many collisions, and we'll have tons of +// 1-item bitmaps. That will be grossly inefficient as hash tables; +// trade-off; the hash table is reasonably efficient per-item when +// it's large, but not when it's small. So we need to do something +// Judy-like and use different strategies depending on the size. +// +// Like Judy, we'll use the bottom bit to encode the strategy: +// +// bottom bits: +// 00 - direct pointer +// 01 - 4-item bucket (16 bytes, no length, NULLs) +// 10 - N-item array +// 11 - hash table + +typedef struct stb_ps stb_ps; + +STB_EXTERN int stb_ps_find (stb_ps *ps, void *value); +STB_EXTERN stb_ps * stb_ps_add (stb_ps *ps, void *value); +STB_EXTERN stb_ps * stb_ps_remove(stb_ps *ps, void *value); +STB_EXTERN stb_ps * stb_ps_remove_any(stb_ps *ps, void **value); +STB_EXTERN void stb_ps_delete(stb_ps *ps); +STB_EXTERN int stb_ps_count (stb_ps *ps); + +STB_EXTERN stb_ps * stb_ps_copy (stb_ps *ps); +STB_EXTERN int stb_ps_subset(stb_ps *bigger, stb_ps *smaller); +STB_EXTERN int stb_ps_eq (stb_ps *p0, stb_ps *p1); + +STB_EXTERN void ** stb_ps_getlist (stb_ps *ps, int *count); +STB_EXTERN int stb_ps_writelist(stb_ps *ps, void **list, int size ); + +// enum and fastlist don't allocate storage, but you must consume the +// list before there's any chance the data structure gets screwed up; +STB_EXTERN int stb_ps_enum (stb_ps *ps, void *data, + int (*func)(void *value, void*data) ); +STB_EXTERN void ** stb_ps_fastlist(stb_ps *ps, int *count); +// result: +// returns a list, *count is the length of that list, +// but some entries of the list may be invalid; +// test with 'stb_ps_fastlist_valid(x)' + +#define stb_ps_fastlist_valid(x) ((stb_uinta) (x) > 1) + +#ifdef STB_DEFINE + +enum +{ + STB_ps_direct = 0, + STB_ps_bucket = 1, + STB_ps_array = 2, + STB_ps_hash = 3, +}; + +#define STB_BUCKET_SIZE 4 + +typedef struct +{ + void *p[STB_BUCKET_SIZE]; +} stb_ps_bucket; +#define GetBucket(p) ((stb_ps_bucket *) ((char *) (p) - STB_ps_bucket)) +#define EncodeBucket(p) ((stb_ps *) ((char *) (p) + STB_ps_bucket)) + +static void stb_bucket_free(stb_ps_bucket *b) +{ + free(b); +} + +static stb_ps_bucket *stb_bucket_create2(void *v0, void *v1) +{ + stb_ps_bucket *b = (stb_ps_bucket*) malloc(sizeof(*b)); + b->p[0] = v0; + b->p[1] = v1; + b->p[2] = NULL; + b->p[3] = NULL; + return b; +} + +static stb_ps_bucket * stb_bucket_create3(void **v) +{ + stb_ps_bucket *b = (stb_ps_bucket*) malloc(sizeof(*b)); + b->p[0] = v[0]; + b->p[1] = v[1]; + b->p[2] = v[2]; + b->p[3] = NULL; + return b; +} + + +// could use stb_arr, but this will save us memory +typedef struct +{ + int count; + void *p[1]; +} stb_ps_array; +#define GetArray(p) ((stb_ps_array *) ((char *) (p) - STB_ps_array)) +#define EncodeArray(p) ((stb_ps *) ((char *) (p) + STB_ps_array)) + +static int stb_ps_array_max = 13; + +typedef struct +{ + int size, mask; + int count, count_deletes; + int grow_threshhold; + int shrink_threshhold; + int rehash_threshhold; + int any_offset; + void *table[1]; +} stb_ps_hash; +#define GetHash(p) ((stb_ps_hash *) ((char *) (p) - STB_ps_hash)) +#define EncodeHash(p) ((stb_ps *) ((char *) (p) + STB_ps_hash)) + +#define stb_ps_empty(v) (((stb_uint32) v) <= 1) + +static stb_ps_hash *stb_ps_makehash(int size, int old_size, void **old_data) +{ + int i; + stb_ps_hash *h = (stb_ps_hash *) malloc(sizeof(*h) + (size-1) * sizeof(h->table[0])); + assert(stb_is_pow2(size)); + h->size = size; + h->mask = size-1; + h->shrink_threshhold = (int) (0.3f * size); + h-> grow_threshhold = (int) (0.8f * size); + h->rehash_threshhold = (int) (0.9f * size); + h->count = 0; + h->count_deletes = 0; + h->any_offset = 0; + memset(h->table, 0, size * sizeof(h->table[0])); + for (i=0; i < old_size; ++i) + if (!stb_ps_empty(old_data[i])) + stb_ps_add(EncodeHash(h), old_data[i]); + return h; +} + +void stb_ps_delete(stb_ps *ps) +{ + switch (3 & (int) ps) { + case STB_ps_direct: break; + case STB_ps_bucket: stb_bucket_free(GetBucket(ps)); break; + case STB_ps_array : free(GetArray(ps)); break; + case STB_ps_hash : free(GetHash(ps)); break; + } +} + +stb_ps *stb_ps_copy(stb_ps *ps) +{ + int i; + // not a switch: order based on expected performance/power-law distribution + switch (3 & (int) ps) { + case STB_ps_direct: return ps; + case STB_ps_bucket: { + stb_ps_bucket *n = (stb_ps_bucket *) malloc(sizeof(*n)); + *n = *GetBucket(ps); + return EncodeBucket(n); + } + case STB_ps_array: { + stb_ps_array *a = GetArray(ps); + stb_ps_array *n = (stb_ps_array *) malloc(sizeof(*n) + stb_ps_array_max * sizeof(n->p[0])); + n->count = a->count; + for (i=0; i < a->count; ++i) + n->p[i] = a->p[i]; + return EncodeArray(n); + } + case STB_ps_hash: { + stb_ps_hash *h = GetHash(ps); + stb_ps_hash *n = stb_ps_makehash(h->size, h->size, h->table); + return EncodeHash(n); + } + } + assert(0); /* NOTREACHED */ + return NULL; +} + +int stb_ps_find(stb_ps *ps, void *value) +{ + int i, code = 3 & (int) ps; + assert((3 & (int) value) == STB_ps_direct); + assert(stb_ps_fastlist_valid(value)); + // not a switch: order based on expected performance/power-law distribution + if (code == STB_ps_direct) + return value == ps; + if (code == STB_ps_bucket) { + stb_ps_bucket *b = GetBucket(ps); + assert(STB_BUCKET_SIZE == 4); + if (b->p[0] == value || b->p[1] == value || + b->p[2] == value || b->p[3] == value) + return STB_TRUE; + return STB_FALSE; + } + if (code == STB_ps_array) { + stb_ps_array *a = GetArray(ps); + for (i=0; i < a->count; ++i) + if (a->p[i] == value) + return STB_TRUE; + return STB_FALSE; + } else { + stb_ps_hash *h = GetHash(ps); + stb_uint32 hash = stb_hashptr(value); + stb_uint32 s, n = hash & h->mask; + void **t = h->table; + if (t[n] == value) return STB_TRUE; + if (t[n] == NULL) return STB_FALSE; + s = stb_rehash(hash) | 1; + do { + n = (n + s) & h->mask; + if (t[n] == value) return STB_TRUE; + } while (t[n] != NULL); + return STB_FALSE; + } +} + +stb_ps * stb_ps_add (stb_ps *ps, void *value) +{ + #ifdef STB_DEBUG + assert(!stb_ps_find(ps,value)); + #endif + if (value == NULL) return ps; // ignore NULL adds to avoid bad breakage + assert((3 & (int) value) == STB_ps_direct); + assert(stb_ps_fastlist_valid(value)); + assert(value != STB_DEL); // STB_DEL is less likely + + switch (3 & (int) ps) { + case STB_ps_direct: + if (ps == NULL) return (stb_ps *) value; + return EncodeBucket(stb_bucket_create2(ps,value)); + + case STB_ps_bucket: { + stb_ps_bucket *b = GetBucket(ps); + stb_ps_array *a; + assert(STB_BUCKET_SIZE == 4); + if (b->p[0] == NULL) { b->p[0] = value; return ps; } + if (b->p[1] == NULL) { b->p[1] = value; return ps; } + if (b->p[2] == NULL) { b->p[2] = value; return ps; } + if (b->p[3] == NULL) { b->p[3] = value; return ps; } + a = (stb_ps_array *) malloc(sizeof(*a) + 7 * sizeof(a->p[0])); // 8 slots, must be 2^k + memcpy(a->p, b, sizeof(*b)); + a->p[4] = value; + a->count = 5; + stb_bucket_free(b); + return EncodeArray(a); + } + + case STB_ps_array: { + stb_ps_array *a = GetArray(ps); + if (a->count == stb_ps_array_max) { + // promote from array to hash + stb_ps_hash *h = stb_ps_makehash(2 << stb_log2_ceil(a->count), a->count, a->p); + free(a); + return stb_ps_add(EncodeHash(h), value); + } + // do we need to resize the array? the array doubles in size when it + // crosses a power-of-two + if ((a->count & (a->count-1))==0) { + int newsize = a->count*2; + // clamp newsize to max if: + // 1. it's larger than max + // 2. newsize*1.5 is larger than max (to avoid extra resizing) + if (newsize + a->count > stb_ps_array_max) + newsize = stb_ps_array_max; + a = (stb_ps_array *) realloc(a, sizeof(*a) + (newsize-1) * sizeof(a->p[0])); + } + a->p[a->count++] = value; + return EncodeArray(a); + } + case STB_ps_hash: { + stb_ps_hash *h = GetHash(ps); + stb_uint32 hash = stb_hashptr(value); + stb_uint32 n = hash & h->mask; + void **t = h->table; + // find first NULL or STB_DEL entry + if (!stb_ps_empty(t[n])) { + stb_uint32 s = stb_rehash(hash) | 1; + do { + n = (n + s) & h->mask; + } while (!stb_ps_empty(t[n])); + } + if (t[n] == STB_DEL) + -- h->count_deletes; + t[n] = value; + ++ h->count; + if (h->count == h->grow_threshhold) { + stb_ps_hash *h2 = stb_ps_makehash(h->size*2, h->size, t); + free(h); + return EncodeHash(h2); + } + if (h->count + h->count_deletes == h->rehash_threshhold) { + stb_ps_hash *h2 = stb_ps_makehash(h->size, h->size, t); + free(h); + return EncodeHash(h2); + } + return ps; + } + } + return NULL; /* NOTREACHED */ +} + +stb_ps *stb_ps_remove(stb_ps *ps, void *value) +{ + #ifdef STB_DEBUG + assert(stb_ps_find(ps, value)); + #endif + assert((3 & (int) value) == STB_ps_direct); + if (value == NULL) return ps; // ignore NULL removes to avoid bad breakage + switch (3 & (int) ps) { + case STB_ps_direct: + return ps == value ? NULL : ps; + case STB_ps_bucket: { + stb_ps_bucket *b = GetBucket(ps); + int count=0; + assert(STB_BUCKET_SIZE == 4); + if (b->p[0] == value) b->p[0] = NULL; else count += (b->p[0] != NULL); + if (b->p[1] == value) b->p[1] = NULL; else count += (b->p[1] != NULL); + if (b->p[2] == value) b->p[2] = NULL; else count += (b->p[2] != NULL); + if (b->p[3] == value) b->p[3] = NULL; else count += (b->p[3] != NULL); + if (count == 1) { // shrink bucket at size 1 + value = b->p[0]; + if (value == NULL) value = b->p[1]; + if (value == NULL) value = b->p[2]; + if (value == NULL) value = b->p[3]; + assert(value != NULL); + stb_bucket_free(b); + return (stb_ps *) value; // return STB_ps_direct of value + } + return ps; + } + case STB_ps_array: { + stb_ps_array *a = GetArray(ps); + int i; + for (i=0; i < a->count; ++i) { + if (a->p[i] == value) { + a->p[i] = a->p[--a->count]; + if (a->count == 3) { // shrink to bucket! + stb_ps_bucket *b = stb_bucket_create3(a->p); + free(a); + return EncodeBucket(b); + } + return ps; + } + } + return ps; + } + case STB_ps_hash: { + stb_ps_hash *h = GetHash(ps); + stb_uint32 hash = stb_hashptr(value); + stb_uint32 s, n = hash & h->mask; + void **t = h->table; + if (t[n] != value) { + s = stb_rehash(hash) | 1; + do { + n = (n + s) & h->mask; + } while (t[n] != value); + } + t[n] = STB_DEL; + -- h->count; + ++ h->count_deletes; + // should we shrink down to an array? + if (h->count < stb_ps_array_max) { + int n = 1 << stb_log2_floor(stb_ps_array_max); + if (h->count < n) { + stb_ps_array *a = (stb_ps_array *) malloc(sizeof(*a) + (n-1) * sizeof(a->p[0])); + int i,j=0; + for (i=0; i < h->size; ++i) + if (!stb_ps_empty(t[i])) + a->p[j++] = t[i]; + assert(j == h->count); + a->count = j; + free(h); + return EncodeArray(a); + } + } + if (h->count == h->shrink_threshhold) { + stb_ps_hash *h2 = stb_ps_makehash(h->size >> 1, h->size, t); + free(h); + return EncodeHash(h2); + } + return ps; + } + } + return ps; /* NOTREACHED */ +} + +stb_ps *stb_ps_remove_any(stb_ps *ps, void **value) +{ + assert(ps != NULL); + switch (3 & (int) ps) { + case STB_ps_direct: + *value = ps; + return NULL; + case STB_ps_bucket: { + stb_ps_bucket *b = GetBucket(ps); + int count=0, slast=0, last=0; + assert(STB_BUCKET_SIZE == 4); + if (b->p[0]) { ++count; last = 0; } + if (b->p[1]) { ++count; slast = last; last = 1; } + if (b->p[2]) { ++count; slast = last; last = 2; } + if (b->p[3]) { ++count; slast = last; last = 3; } + *value = b->p[last]; + b->p[last] = 0; + if (count == 2) { + void *leftover = b->p[slast]; // second to last + stb_bucket_free(b); + return (stb_ps *) leftover; + } + return ps; + } + case STB_ps_array: { + stb_ps_array *a = GetArray(ps); + *value = a->p[a->count-1]; + if (a->count == 4) + return stb_ps_remove(ps, *value); + --a->count; + return ps; + } + case STB_ps_hash: { + stb_ps_hash *h = GetHash(ps); + void **t = h->table; + stb_uint32 n = h->any_offset; + while (stb_ps_empty(t[n])) + n = (n + 1) & h->mask; + *value = t[n]; + h->any_offset = (n+1) & h->mask; + // check if we need to skip down to the previous type + if (h->count-1 < stb_ps_array_max || h->count-1 == h->shrink_threshhold) + return stb_ps_remove(ps, *value); + t[n] = STB_DEL; + -- h->count; + ++ h->count_deletes; + return ps; + } + } + return ps; /* NOTREACHED */ +} + + +void ** stb_ps_getlist(stb_ps *ps, int *count) +{ + int i,n=0; + void **p = NULL; + switch (3 & (int) ps) { + case STB_ps_direct: + if (ps == NULL) { *count = 0; return NULL; } + p = (void **) malloc(sizeof(*p) * 1); + p[0] = ps; + *count = 1; + return p; + case STB_ps_bucket: { + stb_ps_bucket *b = GetBucket(ps); + p = (void **) malloc(sizeof(*p) * STB_BUCKET_SIZE); + for (i=0; i < STB_BUCKET_SIZE; ++i) + if (b->p[i] != NULL) + p[n++] = b->p[i]; + break; + } + case STB_ps_array: { + stb_ps_array *a = GetArray(ps); + p = (void **) malloc(sizeof(*p) * a->count); + memcpy(p, a->p, sizeof(*p) * a->count); + *count = a->count; + return p; + } + case STB_ps_hash: { + stb_ps_hash *h = GetHash(ps); + p = (void **) malloc(sizeof(*p) * h->count); + for (i=0; i < h->size; ++i) + if (!stb_ps_empty(h->table[i])) + p[n++] = h->table[i]; + break; + } + } + *count = n; + return p; +} + +int stb_ps_writelist(stb_ps *ps, void **list, int size ) +{ + int i,n=0; + switch (3 & (int) ps) { + case STB_ps_direct: + if (ps == NULL || size <= 0) return 0; + list[0] = ps; + return 1; + case STB_ps_bucket: { + stb_ps_bucket *b = GetBucket(ps); + for (i=0; i < STB_BUCKET_SIZE; ++i) + if (b->p[i] != NULL && n < size) + list[n++] = b->p[i]; + return n; + } + case STB_ps_array: { + stb_ps_array *a = GetArray(ps); + n = stb_min(size, a->count); + memcpy(list, a->p, sizeof(*list) * n); + return n; + } + case STB_ps_hash: { + stb_ps_hash *h = GetHash(ps); + if (size <= 0) return 0; + for (i=0; i < h->count; ++i) { + if (!stb_ps_empty(h->table[i])) { + list[n++] = h->table[i]; + if (n == size) break; + } + } + return n; + } + } + return 0; /* NOTREACHED */ +} + +int stb_ps_enum(stb_ps *ps, void *data, int (*func)(void *value, void *data)) +{ + int i; + switch (3 & (int) ps) { + case STB_ps_direct: + if (ps == NULL) return STB_TRUE; + return func(ps, data); + case STB_ps_bucket: { + stb_ps_bucket *b = GetBucket(ps); + for (i=0; i < STB_BUCKET_SIZE; ++i) + if (b->p[i] != NULL) + if (!func(b->p[i], data)) + return STB_FALSE; + return STB_TRUE; + } + case STB_ps_array: { + stb_ps_array *a = GetArray(ps); + for (i=0; i < a->count; ++i) + if (!func(a->p[i], data)) + return STB_FALSE; + return STB_TRUE; + } + case STB_ps_hash: { + stb_ps_hash *h = GetHash(ps); + for (i=0; i < h->count; ++i) + if (!stb_ps_empty(h->table[i])) + if (!func(h->table[i], data)) + return STB_FALSE; + return STB_TRUE; + } + } + return STB_TRUE; /* NOTREACHED */ +} + +int stb_ps_count (stb_ps *ps) +{ + switch (3 & (int) ps) { + case STB_ps_direct: + return ps != NULL; + case STB_ps_bucket: { + stb_ps_bucket *b = GetBucket(ps); + return (b->p[0] != NULL) + (b->p[1] != NULL) + + (b->p[2] != NULL) + (b->p[3] != NULL); + } + case STB_ps_array: { + stb_ps_array *a = GetArray(ps); + return a->count; + } + case STB_ps_hash: { + stb_ps_hash *h = GetHash(ps); + return h->count; + } + } + return 0; +} + +void ** stb_ps_fastlist(stb_ps *ps, int *count) +{ + static void *storage; + + switch (3 & (int) ps) { + case STB_ps_direct: + if (ps == NULL) { *count = 0; return NULL; } + storage = ps; + *count = 1; + return &storage; + case STB_ps_bucket: { + stb_ps_bucket *b = GetBucket(ps); + *count = STB_BUCKET_SIZE; + return b->p; + } + case STB_ps_array: { + stb_ps_array *a = GetArray(ps); + *count = a->count; + return a->p; + } + case STB_ps_hash: { + stb_ps_hash *h = GetHash(ps); + *count = h->size; + return h->table; + } + } + return NULL; /* NOTREACHED */ +} + +int stb_ps_subset(stb_ps *bigger, stb_ps *smaller) +{ + int i, listlen; + void **list = stb_ps_fastlist(smaller, &listlen); + for(i=0; i < listlen; ++i) + if (stb_ps_fastlist_valid(list[i])) + if (!stb_ps_find(bigger, list[i])) + return 0; + return 1; +} + +int stb_ps_eq(stb_ps *p0, stb_ps *p1) +{ + if (stb_ps_count(p0) != stb_ps_count(p1)) + return 0; + return stb_ps_subset(p0, p1); +} + +#undef GetBucket +#undef GetArray +#undef GetHash + +#undef EncodeBucket +#undef EncodeArray +#undef EncodeHash + +#endif + + +////////////////////////////////////////////////////////////////////////////// +// +// Random Numbers via Meresenne Twister or LCG +// + +STB_EXTERN unsigned long stb_srandLCG(unsigned long seed); +STB_EXTERN unsigned long stb_randLCG(void); +STB_EXTERN double stb_frandLCG(void); + +STB_EXTERN void stb_srand(unsigned long seed); +STB_EXTERN unsigned long stb_rand(void); +STB_EXTERN double stb_frand(void); +STB_EXTERN void stb_shuffle(void *p, size_t n, size_t sz, + unsigned long seed); +STB_EXTERN void stb_reverse(void *p, size_t n, size_t sz); + +STB_EXTERN unsigned long stb_randLCG_explicit(unsigned long seed); + +#define stb_rand_define(x,y) \ + \ + unsigned long x(void) \ + { \ + static unsigned long stb__rand = y; \ + stb__rand = stb__rand * 2147001325 + 715136305; /* BCPL */ \ + return 0x31415926 ^ ((stb__rand >> 16) + (stb__rand << 16)); \ + } + +#ifdef STB_DEFINE +unsigned long stb_randLCG_explicit(unsigned long seed) +{ + return seed * 2147001325 + 715136305; +} + +static unsigned long stb__rand_seed=0; + +unsigned long stb_srandLCG(unsigned long seed) +{ + unsigned long previous = stb__rand_seed; + stb__rand_seed = seed; + return previous; +} + +unsigned long stb_randLCG(void) +{ + stb__rand_seed = stb__rand_seed * 2147001325 + 715136305; // BCPL generator + // shuffle non-random bits to the middle, and xor to decorrelate with seed + return 0x31415926 ^ ((stb__rand_seed >> 16) + (stb__rand_seed << 16)); +} + +double stb_frandLCG(void) +{ + return stb_randLCG() / ((double) (1 << 16) * (1 << 16)); +} + +void stb_shuffle(void *p, size_t n, size_t sz, unsigned long seed) +{ + char *a; + unsigned long old_seed; + int i; + if (seed) + old_seed = stb_srandLCG(seed); + a = (char *) p + (n-1) * sz; + + for (i=n; i > 1; --i) { + int j = stb_randLCG() % i; + stb_swap(a, (char *) p + j * sz, sz); + a -= sz; + } + if (seed) + stb_srandLCG(old_seed); +} + +void stb_reverse(void *p, size_t n, size_t sz) +{ + int i,j = n-1; + for (i=0; i < j; ++i,--j) { + stb_swap((char *) p + i * sz, (char *) p + j * sz, sz); + } +} + +// public domain Mersenne Twister by Michael Brundage +#define STB__MT_LEN 624 + +int stb__mt_index = STB__MT_LEN*sizeof(unsigned long)+1; +unsigned long stb__mt_buffer[STB__MT_LEN]; + +void stb_srand(unsigned long seed) +{ + int i; + unsigned long old = stb_srandLCG(seed); + for (i = 0; i < STB__MT_LEN; i++) + stb__mt_buffer[i] = stb_randLCG(); + stb_srandLCG(old); + stb__mt_index = STB__MT_LEN*sizeof(unsigned long); +} + +#define STB__MT_IA 397 +#define STB__MT_IB (STB__MT_LEN - STB__MT_IA) +#define STB__UPPER_MASK 0x80000000 +#define STB__LOWER_MASK 0x7FFFFFFF +#define STB__MATRIX_A 0x9908B0DF +#define STB__TWIST(b,i,j) ((b)[i] & STB__UPPER_MASK) | ((b)[j] & STB__LOWER_MASK) +#define STB__MAGIC(s) (((s)&1)*STB__MATRIX_A) + +unsigned long stb_rand() +{ + unsigned long * b = stb__mt_buffer; + int idx = stb__mt_index; + unsigned long s,r; + int i; + + if (idx >= STB__MT_LEN*sizeof(unsigned long)) { + if (idx > STB__MT_LEN*sizeof(unsigned long)) + stb_srand(0); + idx = 0; + i = 0; + for (; i < STB__MT_IB; i++) { + s = STB__TWIST(b, i, i+1); + b[i] = b[i + STB__MT_IA] ^ (s >> 1) ^ STB__MAGIC(s); + } + for (; i < STB__MT_LEN-1; i++) { + s = STB__TWIST(b, i, i+1); + b[i] = b[i - STB__MT_IB] ^ (s >> 1) ^ STB__MAGIC(s); + } + + s = STB__TWIST(b, STB__MT_LEN-1, 0); + b[STB__MT_LEN-1] = b[STB__MT_IA-1] ^ (s >> 1) ^ STB__MAGIC(s); + } + stb__mt_index = idx + sizeof(unsigned long); + + r = *(unsigned long *)((unsigned char *)b + idx); + + r ^= (r >> 11); + r ^= (r << 7) & 0x9D2C5680; + r ^= (r << 15) & 0xEFC60000; + r ^= (r >> 18); + + return r; +} + +double stb_frand(void) +{ + return stb_rand() / ((double) (1 << 16) * (1 << 16)); +} + +#endif + + +////////////////////////////////////////////////////////////////////////////// +// +// stb_dupe +// +// stb_dupe is a duplicate-finding system for very, very large data +// structures--large enough that sorting is too slow, but not so large +// that we can't keep all the data in memory. using it works as follows: +// +// 1. create an stb_dupe: +// provide a hash function +// provide an equality function +// provide an estimate for the size +// optionally provide a comparison function +// +// 2. traverse your data, 'adding' pointers to the stb_dupe +// +// 3. finish and ask for duplicates +// +// the stb_dupe will discard its intermediate data and build +// a collection of sorted lists of duplicates, with non-duplicate +// entries omitted entirely +// +// +// Implementation strategy: +// +// while collecting the N items, we keep a hash table of approximate +// size sqrt(N). (if you tell use the N up front, the hash table is +// just that size exactly) +// +// each entry in the hash table is just an stb__arr of pointers (no need +// to use stb_ps, because we don't need to delete from these) +// +// for step 3, for each entry in the hash table, we apply stb_dupe to it +// recursively. once the size gets small enough (or doesn't decrease +// significantly), we switch to either using qsort() on the comparison +// function, or else we just do the icky N^2 gather + + +typedef struct stb_dupe stb_dupe; + +typedef int (*stb_compare_func)(void *a, void *b); +typedef int (*stb_hash_func)(void *a, unsigned int seed); + +STB_EXTERN void stb_dupe_free(stb_dupe *sd); +STB_EXTERN stb_dupe *stb_dupe_create(stb_hash_func hash, + stb_compare_func eq, int size, stb_compare_func ineq); +STB_EXTERN void stb_dupe_add(stb_dupe *sd, void *item); +STB_EXTERN void stb_dupe_finish(stb_dupe *sd); +STB_EXTERN int stb_dupe_numsets(stb_dupe *sd); +STB_EXTERN void **stb_dupe_set(stb_dupe *sd, int num); +STB_EXTERN int stb_dupe_set_count(stb_dupe *sd, int num); + +struct stb_dupe +{ + void ***hash_table; + int hash_size; + int size_log2; + int population; + + int hash_shift; + stb_hash_func hash; + + stb_compare_func eq; + stb_compare_func ineq; + + void ***dupes; +}; + +#ifdef STB_DEFINE + +int stb_dupe_numsets(stb_dupe *sd) +{ + assert(sd->hash_table == NULL); + return stb_arr_len(sd->dupes); +} + +void **stb_dupe_set(stb_dupe *sd, int num) +{ + assert(sd->hash_table == NULL); + return sd->dupes[num]; +} + +int stb_dupe_set_count(stb_dupe *sd, int num) +{ + assert(sd->hash_table == NULL); + return stb_arr_len(sd->dupes[num]); +} + +stb_dupe *stb_dupe_create(stb_hash_func hash, stb_compare_func eq, int size, + stb_compare_func ineq) +{ + int i, hsize; + stb_dupe *sd = (stb_dupe *) malloc(sizeof(*sd)); + + sd->size_log2 = 4; + hsize = 1 << sd->size_log2; + while (hsize * hsize < size) { + ++sd->size_log2; + hsize *= 2; + } + + sd->hash = hash; + sd->eq = eq; + sd->ineq = ineq; + sd->hash_shift = 0; + + sd->population = 0; + sd->hash_size = hsize; + sd->hash_table = (void ***) malloc(sizeof(*sd->hash_table) * hsize); + for (i=0; i < hsize; ++i) + sd->hash_table[i] = NULL; + + sd->dupes = NULL; + + return sd; +} + +void stb_dupe_add(stb_dupe *sd, void *item) +{ + stb_uint32 hash = sd->hash(item, sd->hash_shift); + int z = hash & (sd->hash_size-1); + stb_arr_push(sd->hash_table[z], item); + ++sd->population; +} + +void stb_dupe_free(stb_dupe *sd) +{ + int i; + for (i=0; i < stb_arr_len(sd->dupes); ++i) + if (sd->dupes[i]) + stb_arr_free(sd->dupes[i]); + stb_arr_free(sd->dupes); + free(sd); +} + +static stb_compare_func stb__compare; + +static int stb__dupe_compare(const void *a, const void *b) +{ + void *p = *(void **) a; + void *q = *(void **) b; + + return stb__compare(p,q); +} + +void stb_dupe_finish(stb_dupe *sd) +{ + int i,j,k; + assert(sd->dupes == NULL); + for (i=0; i < sd->hash_size; ++i) { + void ** list = sd->hash_table[i]; + if (list != NULL) { + int n = stb_arr_len(list); + // @TODO: measure to find good numbers instead of just making them up! + int thresh = (sd->ineq ? 200 : 20); + // if n is large enough to be worth it, and n is smaller than + // before (so we can guarantee we'll use a smaller hash table); + // and there are enough hash bits left, assuming full 32-bit hash + if (n > thresh && n < (sd->population >> 3) && sd->hash_shift + sd->size_log2*2 < 32) { + + // recursively process this row using stb_dupe, O(N log log N) + + stb_dupe *d = stb_dupe_create(sd->hash, sd->eq, n, sd->ineq); + d->hash_shift = stb_randLCG_explicit(sd->hash_shift); + for (j=0; j < n; ++j) + stb_dupe_add(d, list[j]); + stb_arr_free(sd->hash_table[i]); + stb_dupe_finish(d); + for (j=0; j < stb_arr_len(d->dupes); ++j) { + stb_arr_push(sd->dupes, d->dupes[j]); + d->dupes[j] = NULL; // take over ownership + } + stb_dupe_free(d); + + } else if (sd->ineq) { + + // process this row using qsort(), O(N log N) + stb__compare = sd->ineq; + qsort(list, n, sizeof(list[0]), stb__dupe_compare); + + // find equal subsequences of the list + for (j=0; j < n-1; ) { + // find a subsequence from j..k + for (k=j; k < n; ++k) + // only use ineq so eq can be left undefined + if (sd->ineq(list[j], list[k])) + break; + // k is the first one not in the subsequence + if (k-j > 1) { + void **mylist = NULL; + stb_arr_setlen(mylist, k-j); + memcpy(mylist, list+j, sizeof(list[j]) * (k-j)); + stb_arr_push(sd->dupes, mylist); + } + j = k; + } + stb_arr_free(sd->hash_table[i]); + } else { + + // process this row using eq(), O(N^2) + for (j=0; j < n; ++j) { + if (list[j] != NULL) { + void **output = NULL; + for (k=j+1; k < n; ++k) { + if (sd->eq(list[j], list[k])) { + if (output == NULL) + stb_arr_push(output, list[j]); + stb_arr_push(output, list[k]); + list[k] = NULL; + } + } + list[j] = NULL; + if (output) + stb_arr_push(sd->dupes, output); + } + } + stb_arr_free(sd->hash_table[i]); + } + } + } + free(sd->hash_table); + sd->hash_table = NULL; +} +#endif + +////////////////////////////////////////////////////////////////////////////// +// +// templatized Sort routine +// +// This is an attempt to implement a templated sorting algorithm. +// To use it, you have to explicitly instantiate it as a _function_, +// then you call that function. This allows the comparison to be inlined, +// giving the sort similar performance to C++ sorts. +// +// It implements quicksort with three-way-median partitioning (generally +// well-behaved), with a final insertion sort pass. +// +// When you define the compare expression, you should assume you have +// elements of your array pointed to by 'a' and 'b', and perform the comparison +// on those. OR you can use one or more statements; first say '0;', then +// write whatever code you want, and compute the result into a variable 'c'. + +#define stb_declare_sort(FUNCNAME, TYPE) \ + void FUNCNAME(TYPE *p, int n) +#define stb_define_sort(FUNCNAME,TYPE,COMPARE) \ + stb__define_sort( void, FUNCNAME,TYPE,COMPARE) +#define stb_define_sort_static(FUNCNAME,TYPE,COMPARE) \ + stb__define_sort(static void, FUNCNAME,TYPE,COMPARE) + +#define stb__define_sort(MODE, FUNCNAME, TYPE, COMPARE) \ + \ +static void STB_(FUNCNAME,_ins_sort)(TYPE *p, int n) \ +{ \ + int i,j; \ + for (i=1; i < n; ++i) { \ + TYPE t = p[i], *a = &t; \ + j = i; \ + while (j > 0) { \ + TYPE *b = &p[j-1]; \ + int c = COMPARE; \ + if (!c) break; \ + p[j] = p[j-1]; \ + --j; \ + } \ + if (i != j) \ + p[j] = t; \ + } \ +} \ + \ +static void STB_(FUNCNAME,_quicksort)(TYPE *p, int n) \ +{ \ + /* threshhold for transitioning to insertion sort */ \ + while (n > 12) { \ + TYPE *a,*b,t; \ + int c01,c12,c,m,i,j; \ + \ + /* compute median of three */ \ + m = n >> 1; \ + a = &p[0]; \ + b = &p[m]; \ + c = COMPARE; \ + c01 = c; \ + a = &p[m]; \ + b = &p[n-1]; \ + c = COMPARE; \ + c12 = c; \ + /* if 0 >= mid >= end, or 0 < mid < end, then use mid */ \ + if (c01 != c12) { \ + /* otherwise, we'll need to swap something else to middle */ \ + int z; \ + a = &p[0]; \ + b = &p[n-1]; \ + c = COMPARE; \ + /* 0>mid && midn => n; 0 0 */ \ + /* 0n: 0>n => 0; 0 n */ \ + z = (c == c12) ? 0 : n-1; \ + t = p[z]; \ + p[z] = p[m]; \ + p[m] = t; \ + } \ + /* now p[m] is the median-of-three */ \ + /* swap it to the beginning so it won't move around */ \ + t = p[0]; \ + p[0] = p[m]; \ + p[m] = t; \ + \ + /* partition loop */ \ + i=1; \ + j=n-1; \ + for(;;) { \ + /* handling of equality is crucial here */ \ + /* for sentinels & efficiency with duplicates */ \ + b = &p[0]; \ + for (;;++i) { \ + a=&p[i]; \ + c = COMPARE; \ + if (!c) break; \ + } \ + a = &p[0]; \ + for (;;--j) { \ + b=&p[j]; \ + c = COMPARE; \ + if (!c) break; \ + } \ + /* make sure we haven't crossed */ \ + if (i >= j) break; \ + t = p[i]; \ + p[i] = p[j]; \ + p[j] = t; \ + \ + ++i; \ + --j; \ + } \ + /* recurse on smaller side, iterate on larger */ \ + if (j < (n-i)) { \ + STB_(FUNCNAME,_quicksort)(p,j); \ + p = p+i; \ + n = n-i; \ + } else { \ + STB_(FUNCNAME,_quicksort)(p+i, n-i); \ + n = j; \ + } \ + } \ +} \ + \ +MODE FUNCNAME(TYPE *p, int n) \ +{ \ + STB_(FUNCNAME, _quicksort)(p, n); \ + STB_(FUNCNAME, _ins_sort)(p, n); \ +} \ + + +////////////////////////////////////////////////////////////////////////////// +// +// stb_bitset an array of booleans indexed by integers +// + +typedef stb_uint32 stb_bitset; + +STB_EXTERN stb_bitset *stb_bitset_new(int value, int len); + +#define stb_bitset_clearall(arr,len) (memset(arr, 0, 4 * (len))) +#define stb_bitset_setall(arr,len) (memset(arr, 255, 4 * (len))) + +#define stb_bitset_setbit(arr,n) ((arr)[(n) >> 5] |= (1 << (n & 31))) +#define stb_bitset_clearbit(arr,n) ((arr)[(n) >> 5] &= ~(1 << (n & 31))) +#define stb_bitset_testbit(arr,n) ((arr)[(n) >> 5] & (1 << (n & 31))) + +STB_EXTERN stb_bitset *stb_bitset_union(stb_bitset *p0, stb_bitset *p1, int len); + +STB_EXTERN int *stb_bitset_getlist(stb_bitset *out, int start, int end); + +STB_EXTERN int stb_bitset_eq(stb_bitset *p0, stb_bitset *p1, int len); +STB_EXTERN int stb_bitset_disjoint(stb_bitset *p0, stb_bitset *p1, int len); +STB_EXTERN int stb_bitset_disjoint_0(stb_bitset *p0, stb_bitset *p1, int len); +STB_EXTERN int stb_bitset_subset(stb_bitset *bigger, stb_bitset *smaller, int len); +STB_EXTERN int stb_bitset_unioneq_changed(stb_bitset *p0, stb_bitset *p1, int len); + +#ifdef STB_DEFINE +int stb_bitset_eq(stb_bitset *p0, stb_bitset *p1, int len) +{ + int i; + for (i=0; i < len; ++i) + if (p0[i] != p1[i]) return 0; + return 1; +} + +int stb_bitset_disjoint(stb_bitset *p0, stb_bitset *p1, int len) +{ + int i; + for (i=0; i < len; ++i) + if (p0[i] & p1[i]) return 0; + return 1; +} + +int stb_bitset_disjoint_0(stb_bitset *p0, stb_bitset *p1, int len) +{ + int i; + for (i=0; i < len; ++i) + if ((p0[i] | p1[i]) != 0xffffffff) return 0; + return 1; +} + +int stb_bitset_subset(stb_bitset *bigger, stb_bitset *smaller, int len) +{ + int i; + for (i=0; i < len; ++i) + if ((bigger[i] & smaller[i]) != smaller[i]) return 0; + return 1; +} + +stb_bitset *stb_bitset_union(stb_bitset *p0, stb_bitset *p1, int len) +{ + int i; + stb_bitset *d = (stb_bitset *) malloc(sizeof(*d) * len); + for (i=0; i < len; ++i) d[i] = p0[i] | p1[i]; + return d; +} + +int stb_bitset_unioneq_changed(stb_bitset *p0, stb_bitset *p1, int len) +{ + int i, changed=0; + for (i=0; i < len; ++i) { + stb_bitset d = p0[i] | p1[i]; + if (d != p0[i]) { + p0[i] = d; + changed = 1; + } + } + return changed; +} + +stb_bitset *stb_bitset_new(int value, int len) +{ + int i; + stb_bitset *d = (stb_bitset *) malloc(sizeof(*d) * len); + if (value) value = 0xffffffff; + for (i=0; i < len; ++i) d[i] = value; + return d; +} + +int *stb_bitset_getlist(stb_bitset *out, int start, int end) +{ + int *list = NULL; + int i; + for (i=start; i < end; ++i) + if (stb_bitset_testbit(out, i)) + stb_arr_push(list, i); + return list; +} +#endif + +////////////////////////////////////////////////////////////////////////////// +// +// stb_wordwrap quality word-wrapping for fixed-width fonts +// + +STB_EXTERN int stb_wordwrap(int *pairs, int pair_max, int count, char *str); +STB_EXTERN int *stb_wordwrapalloc(int count, char *str); + +#ifdef STB_DEFINE + +int stb_wordwrap(int *pairs, int pair_max, int count, char *str) +{ + int n=0,i=0, start=0,nonwhite=0; + if (pairs == NULL) pair_max = 0x7ffffff0; + else pair_max *= 2; + // parse + for(;;) { + int s=i; // first whitespace char; last nonwhite+1 + int w; // word start + // accept whitespace + while (isspace(str[i])) { + if (str[i] == '\n' || str[i] == '\r') { + if (str[i] + str[i+1] == '\n' + '\r') ++i; + if (n >= pair_max) return -1; + if (pairs) pairs[n] = start, pairs[n+1] = s-start; + n += 2; + nonwhite=0; + start = i+1; + s = start; + } + ++i; + } + if (i >= start+count) { + // we've gone off the end using whitespace + if (nonwhite) { + if (n >= pair_max) return -1; + if (pairs) pairs[n] = start, pairs[n+1] = s-start; + n += 2; + start = s = i; + nonwhite=0; + } else { + // output all the whitespace + while (i >= start+count) { + if (n >= pair_max) return -1; + if (pairs) pairs[n] = start, pairs[n+1] = count; + n += 2; + start += count; + } + s = start; + } + } + + if (str[i] == 0) break; + // now scan out a word and see if it fits + w = i; + while (str[i] && !isspace(str[i])) { + ++i; + } + // wrapped? + if (i > start + count) { + // huge? + if (i-s <= count) { + if (n >= pair_max) return -1; + if (pairs) pairs[n] = start, pairs[n+1] = s-start; + n += 2; + start = w; + } else { + // This word is longer than one line. If we wrap it onto N lines + // there are leftover chars. do those chars fit on the cur line? + // But if we have leading whitespace, we force it to start here. + if ((w-start) + ((i-w) % count) <= count || !nonwhite) { + // output a full line + if (n >= pair_max) return -1; + if (pairs) pairs[n] = start, pairs[n+1] = count; + n += 2; + start += count; + w = start; + } else { + // output a partial line, trimming trailing whitespace + if (s != start) { + if (n >= pair_max) return -1; + if (pairs) pairs[n] = start, pairs[n+1] = s-start; + n += 2; + start = w; + } + } + // now output full lines as needed + while (start + count <= i) { + if (n >= pair_max) return -1; + if (pairs) pairs[n] = start, pairs[n+1] = count; + n += 2; + start += count; + } + } + } + nonwhite=1; + } + if (start < i) { + if (n >= pair_max) return -1; + if (pairs) pairs[n] = start, pairs[n+1] = i-start; + n += 2; + } + return n>>1; +} + +int *stb_wordwrapalloc(int count, char *str) +{ + int n = stb_wordwrap(NULL,0,count,str); + int *z = NULL; + stb_arr_setlen(z, n*2); + stb_wordwrap(z, n, count, str); + return z; +} +#endif + + +////////////////////////////////////////////////////////////////////////////// +// +// stb_match: wildcards and regexping +// + +STB_EXTERN int stb_wildmatch (char *expr, char *candidate); +STB_EXTERN int stb_wildmatchi(char *expr, char *candidate); +STB_EXTERN int stb_wildfind (char *expr, char *candidate); +STB_EXTERN int stb_wildfindi (char *expr, char *candidate); + +STB_EXTERN int stb_regex(char *regex, char *candidate); + +typedef struct stb_matcher stb_matcher; + +STB_EXTERN stb_matcher *stb_regex_matcher(char *regex); +STB_EXTERN int stb_matcher_match(stb_matcher *m, char *str); +STB_EXTERN int stb_matcher_find(stb_matcher *m, char *str); +STB_EXTERN void stb_matcher_free(stb_matcher *f); + +STB_EXTERN stb_matcher *stb_lex_matcher(void); +STB_EXTERN int stb_lex_item(stb_matcher *m, char *str, int result); +STB_EXTERN int stb_lex_item_wild(stb_matcher *matcher, char *regex, int result); +STB_EXTERN int stb_lex(stb_matcher *m, char *str, int *len); + + + +#ifdef STB_DEFINE + +static int stb__match_qstring(char *candidate, char *qstring, int qlen, int insensitive) +{ + int i; + if (insensitive) { + for (i=0; i < qlen; ++i) + if (qstring[i] == '?') { + if (!candidate[i]) return 0; + } else + if (tolower(qstring[i]) != tolower(candidate[i])) + return 0; + } else { + for (i=0; i < qlen; ++i) + if (qstring[i] == '?') { + if (!candidate[i]) return 0; + } else + if (qstring[i] != candidate[i]) + return 0; + } + return 1; +} + +static int stb__find_qstring(char *candidate, char *qstring, int qlen, int insensitive) +{ + char c; + + int offset=0; + while (*qstring == '?') { + ++qstring; + --qlen; + ++candidate; + if (qlen == 0) return 0; + if (*candidate == 0) return -1; + } + + c = *qstring++; + --qlen; + if (insensitive) c = tolower(c); + + while (candidate[offset]) { + if (c == (insensitive ? tolower(candidate[offset]) : candidate[offset])) + if (stb__match_qstring(candidate+offset+1, qstring, qlen, insensitive)) + return offset; + ++offset; + } + + return -1; +} + +int stb__wildmatch_raw2(char *expr, char *candidate, int search, int insensitive) +{ + int where=0; + int start = -1; + + if (!search) { + // parse to first '*' + if (*expr != '*') + start = 0; + while (*expr != '*') { + if (!*expr) + return *candidate == 0 ? 0 : -1; + if (*expr == '?') { + if (!*candidate) return -1; + } else { + if (insensitive) { + if (tolower(*candidate) != tolower(*expr)) + return -1; + } else + if (*candidate != *expr) + return -1; + } + ++candidate, ++expr, ++where; + } + } else { + // 0-length search string + if (!*expr) + return 0; + } + + assert(search || *expr == '*'); + if (!search) + ++expr; + + // implicit '*' at this point + + while (*expr) { + int o=0; + // combine redundant * characters + while (expr[0] == '*') ++expr; + + // ok, at this point, expr[-1] == '*', + // and expr[0] != '*' + + if (!expr[0]) return start >= 0 ? start : 0; + + // now find next '*' + o = 0; + while (expr[o] != '*') { + if (expr[o] == 0) + break; + ++o; + } + // if no '*', scan to end, then match at end + if (expr[o] == 0 && !search) { + int z; + for (z=0; z < o; ++z) + if (candidate[z] == 0) + return -1; + while (candidate[z]) + ++z; + // ok, now check if they match + if (stb__match_qstring(candidate+z-o, expr, o, insensitive)) + return start >= 0 ? start : 0; + return -1; + } else { + // if yes '*', then do stb__find_qmatch on the intervening chars + int n = stb__find_qstring(candidate, expr, o, insensitive); + if (n < 0) + return -1; + if (start < 0) + start = where + n; + expr += o; + candidate += n+o; + } + + if (*expr == 0) { + assert(search); + return start; + } + + assert(*expr == '*'); + ++expr; + } + + return start >= 0 ? start : 0; +} + +int stb__wildmatch_raw(char *expr, char *candidate, int search, int insensitive) +{ + char buffer[256]; + // handle multiple search strings + char *s = strchr(expr, ';'); + char *last = expr; + while (s) { + int z; + // need to allow for non-writeable strings... assume they're small + if (s - last < 256) { + stb_strncpy(buffer, last, s-last+1); + z = stb__wildmatch_raw2(buffer, candidate, search, insensitive); + } else { + *s = 0; + z = stb__wildmatch_raw2(last, candidate, search, insensitive); + *s = ';'; + } + if (z >= 0) return z; + last = s+1; + s = strchr(last, ';'); + } + return stb__wildmatch_raw2(last, candidate, search, insensitive); +} + +int stb_wildmatch(char *expr, char *candidate) +{ + return stb__wildmatch_raw(expr, candidate, 0,0) >= 0; +} + +int stb_wildmatchi(char *expr, char *candidate) +{ + return stb__wildmatch_raw(expr, candidate, 0,1) >= 0; +} + +int stb_wildfind(char *expr, char *candidate) +{ + return stb__wildmatch_raw(expr, candidate, 1,0); +} + +int stb_wildfindi(char *expr, char *candidate) +{ + return stb__wildmatch_raw(expr, candidate, 1,1); +} + +typedef struct +{ + stb_int16 transition[256]; +} stb_dfa; + +// an NFA node represents a state you're in; it then has +// an arbitrary number of edges dangling off of it +// note this isn't utf8-y +typedef struct +{ + stb_int16 match; // character/set to match + stb_uint16 node; // output node to go to +} stb_nfa_edge; + +typedef struct +{ + stb_int16 goal; // does reaching this win the prize? + stb_uint8 active; // is this in the active list + stb_nfa_edge *out; + stb_uint16 *eps; // list of epsilon closures +} stb_nfa_node; + +#define STB__DFA_UNDEF -1 +#define STB__DFA_GOAL -2 +#define STB__DFA_END -3 +#define STB__DFA_MGOAL -4 +#define STB__DFA_VALID 0 + +#define STB__NFA_STOP_GOAL -1 + +// compiled regexp +struct stb_matcher +{ + stb_uint16 start_node; + stb_int16 dfa_start; + stb_uint32 *charset; + int num_charset; + int match_start; + stb_nfa_node *nodes; + int does_lex; + + // dfa matcher + stb_dfa * dfa; + stb_uint32 * dfa_mapping; + stb_int16 * dfa_result; + int num_words_per_dfa; +}; + +static int stb__add_node(stb_matcher *matcher) +{ + stb_nfa_node z; + z.active = 0; + z.eps = 0; + z.goal = 0; + z.out = 0; + stb_arr_push(matcher->nodes, z); + return stb_arr_len(matcher->nodes)-1; +} + +static void stb__add_epsilon(stb_matcher *matcher, int from, int to) +{ + assert(from != to); + if (matcher->nodes[from].eps == NULL) + stb_arr_malloc((void **) &matcher->nodes[from].eps, matcher); + stb_arr_push(matcher->nodes[from].eps, to); +} + +static void stb__add_edge(stb_matcher *matcher, int from, int to, int type) +{ + stb_nfa_edge z = { type, to }; + if (matcher->nodes[from].out == NULL) + stb_arr_malloc((void **) &matcher->nodes[from].out, matcher); + stb_arr_push(matcher->nodes[from].out, z); +} + +static char *stb__reg_parse_alt(stb_matcher *m, int s, char *r, stb_uint16 *e); +static char *stb__reg_parse(stb_matcher *matcher, int start, char *regex, stb_uint16 *end) +{ + int n; + int last_start = -1; + stb_uint16 last_end = start; + + while (*regex) { + switch (*regex) { + case '(': + last_start = last_end; + regex = stb__reg_parse_alt(matcher, last_end, regex+1, &last_end); + if (regex == NULL || *regex != ')') + return NULL; + ++regex; + break; + + case '|': + case ')': + *end = last_end; + return regex; + + case '?': + if (last_start < 0) return NULL; + stb__add_epsilon(matcher, last_start, last_end); + ++regex; + break; + + case '*': + if (last_start < 0) return NULL; + stb__add_epsilon(matcher, last_start, last_end); + + // fall through + + case '+': + if (last_start < 0) return NULL; + stb__add_epsilon(matcher, last_end, last_start); + // prevent links back to last_end from chaining to last_start + n = stb__add_node(matcher); + stb__add_epsilon(matcher, last_end, n); + last_end = n; + ++regex; + break; + + case '{': // not supported! + // @TODO: given {n,m}, clone last_start to last_end m times, + // and include epsilons from start to first m-n blocks + return NULL; + + case '\\': + ++regex; + if (!*regex) return NULL; + + // fallthrough + default: // match exactly this character + n = stb__add_node(matcher); + stb__add_edge(matcher, last_end, n, *regex); + last_start = last_end; + last_end = n; + ++regex; + break; + + case '$': + n = stb__add_node(matcher); + stb__add_edge(matcher, last_end, n, '\n'); + last_start = last_end; + last_end = n; + ++regex; + break; + + case '.': + n = stb__add_node(matcher); + stb__add_edge(matcher, last_end, n, -1); + last_start = last_end; + last_end = n; + ++regex; + break; + + case '[': { + stb_uint8 flags[256]; + int invert = 0,z; + ++regex; + if (matcher->num_charset == 0) { + matcher->charset = (stb_uint *) stb_malloc(matcher, sizeof(*matcher->charset) * 256); + memset(matcher->charset, 0, sizeof(*matcher->charset) * 256); + } + + memset(flags,0,sizeof(flags)); + + // leading ^ is special + if (*regex == '^') + ++regex, invert = 1; + + // leading ] is special + if (*regex == ']') { + flags[']'] = 1; + ++regex; + } + while (*regex != ']') { + stb_uint a; + if (!*regex) return NULL; + a = *regex++; + if (regex[0] == '-' && regex[1] != ']') { + stb_uint i,b = regex[1]; + regex += 2; + if (b == 0) return NULL; + if (a > b) return NULL; + for (i=a; i <= b; ++i) + flags[i] = 1; + } else + flags[a] = 1; + } + ++regex; + if (invert) { + int i; + for (i=0; i < 256; ++i) + flags[i] = 1-flags[i]; + } + + // now check if any existing charset matches + for (z=0; z < matcher->num_charset; ++z) { + int i, k[2] = { 0, 1 << z}; + for (i=0; i < 256; ++i) { + unsigned int f = k[flags[i]]; + if ((matcher->charset[i] & k[1]) != f) + break; + } + if (i == 256) break; + } + + if (z == matcher->num_charset) { + int i; + ++matcher->num_charset; + if (matcher->num_charset > 32) { + assert(0); /* NOTREACHED */ + return NULL; // too many charsets, oops + } + for (i=0; i < 256; ++i) + if (flags[i]) + matcher->charset[i] |= (1 << z); + } + + n = stb__add_node(matcher); + stb__add_edge(matcher, last_end, n, -2 - z); + last_start = last_end; + last_end = n; + break; + } + } + } + *end = last_end; + return regex; +} + +static char *stb__reg_parse_alt(stb_matcher *matcher, int start, char *regex, stb_uint16 *end) +{ + stb_uint16 last_end = start; + stb_uint16 main_end; + + int head, tail; + + head = stb__add_node(matcher); + stb__add_epsilon(matcher, start, head); + + regex = stb__reg_parse(matcher, head, regex, &last_end); + if (regex == NULL) return NULL; + if (*regex == 0 || *regex == ')') { + *end = last_end; + return regex; + } + + main_end = last_end; + tail = stb__add_node(matcher); + + stb__add_epsilon(matcher, last_end, tail); + + // start alternatives from the same starting node; use epsilon + // transitions to combine their endings + while(*regex && *regex != ')') { + assert(*regex == '|'); + head = stb__add_node(matcher); + stb__add_epsilon(matcher, start, head); + regex = stb__reg_parse(matcher, head, regex+1, &last_end); + if (regex == NULL) + return NULL; + stb__add_epsilon(matcher, last_end, tail); + } + + *end = tail; + return regex; +} + +static char *stb__wild_parse(stb_matcher *matcher, int start, char *str, stb_uint16 *end) +{ + int n; + stb_uint16 last_end; + + last_end = stb__add_node(matcher); + stb__add_epsilon(matcher, start, last_end); + + while (*str) { + switch (*str) { + // fallthrough + default: // match exactly this character + n = stb__add_node(matcher); + if (toupper(*str) == tolower(*str)) { + stb__add_edge(matcher, last_end, n, *str); + } else { + stb__add_edge(matcher, last_end, n, tolower(*str)); + stb__add_edge(matcher, last_end, n, toupper(*str)); + } + last_end = n; + ++str; + break; + + case '?': + n = stb__add_node(matcher); + stb__add_edge(matcher, last_end, n, -1); + last_end = n; + ++str; + break; + + case '*': + n = stb__add_node(matcher); + stb__add_edge(matcher, last_end, n, -1); + stb__add_epsilon(matcher, last_end, n); + stb__add_epsilon(matcher, n, last_end); + last_end = n; + ++str; + break; + } + } + + // now require end of string to match + n = stb__add_node(matcher); + stb__add_edge(matcher, last_end, n, 0); + last_end = n; + + *end = last_end; + return str; +} + +static int stb__opt(stb_matcher *m, int n) +{ + for(;;) { + stb_nfa_node *p = &m->nodes[n]; + if (p->goal) return n; + if (stb_arr_len(p->out)) return n; + if (stb_arr_len(p->eps) != 1) return n; + n = p->eps[0]; + } +} + +static void stb__optimize(stb_matcher *m) +{ + // if the target of any edge is a node with exactly + // one out-epsilon, shorten it + int i,j; + for (i=0; i < stb_arr_len(m->nodes); ++i) { + stb_nfa_node *p = &m->nodes[i]; + for (j=0; j < stb_arr_len(p->out); ++j) + p->out[j].node = stb__opt(m,p->out[j].node); + for (j=0; j < stb_arr_len(p->eps); ++j) + p->eps[j] = stb__opt(m,p->eps[j] ); + } + m->start_node = stb__opt(m,m->start_node); +} + +void stb_matcher_free(stb_matcher *f) +{ + stb_free(f); +} + +static stb_matcher *stb__alloc_matcher(void) +{ + stb_matcher *matcher = (stb_matcher *) stb_malloc(0,sizeof(*matcher)); + + matcher->start_node = 0; + stb_arr_malloc((void **) &matcher->nodes, matcher); + matcher->num_charset = 0; + matcher->match_start = 0; + matcher->does_lex = 0; + + matcher->dfa_start = STB__DFA_UNDEF; + stb_arr_malloc((void **) &matcher->dfa, matcher); + stb_arr_malloc((void **) &matcher->dfa_mapping, matcher); + stb_arr_malloc((void **) &matcher->dfa_result, matcher); + + stb__add_node(matcher); + + return matcher; +} + +static void stb__lex_reset(stb_matcher *matcher) +{ + // flush cached dfa data + stb_arr_setlen(matcher->dfa, 0); + stb_arr_setlen(matcher->dfa_mapping, 0); + stb_arr_setlen(matcher->dfa_result, 0); + matcher->dfa_start = STB__DFA_UNDEF; +} + +stb_matcher *stb_regex_matcher(char *regex) +{ + char *z; + stb_uint16 end; + stb_matcher *matcher = stb__alloc_matcher(); + if (*regex == '^') { + matcher->match_start = 1; + ++regex; + } + + z = stb__reg_parse_alt(matcher, matcher->start_node, regex, &end); + + if (!z || *z) { + stb_free(matcher); + return NULL; + } + + ((matcher->nodes)[(int) end]).goal = STB__NFA_STOP_GOAL; + + return matcher; +} + +stb_matcher *stb_lex_matcher(void) +{ + stb_matcher *matcher = stb__alloc_matcher(); + + matcher->match_start = 1; + matcher->does_lex = 1; + + return matcher; +} + +int stb_lex_item(stb_matcher *matcher, char *regex, int result) +{ + char *z; + stb_uint16 end; + + z = stb__reg_parse_alt(matcher, matcher->start_node, regex, &end); + + if (z == NULL) + return 0; + + stb__lex_reset(matcher); + + matcher->nodes[(int) end].goal = result; + return 1; +} + +int stb_lex_item_wild(stb_matcher *matcher, char *regex, int result) +{ + char *z; + stb_uint16 end; + + z = stb__wild_parse(matcher, matcher->start_node, regex, &end); + + if (z == NULL) + return 0; + + stb__lex_reset(matcher); + + matcher->nodes[(int) end].goal = result; + return 1; +} + +static void stb__clear(stb_matcher *m, stb_uint16 *list) +{ + int i; + for (i=0; i < stb_arr_len(list); ++i) + m->nodes[(int) list[i]].active = 0; +} + +static int stb__clear_goalcheck(stb_matcher *m, stb_uint16 *list) +{ + int i, t=0; + for (i=0; i < stb_arr_len(list); ++i) { + t += m->nodes[(int) list[i]].goal; + m->nodes[(int) list[i]].active = 0; + } + return t; +} + +static stb_uint16 * stb__add_if_inactive(stb_matcher *m, stb_uint16 *list, int n) +{ + if (!m->nodes[n].active) { + stb_arr_push(list, n); + m->nodes[n].active = 1; + } + return list; +} + +static stb_uint16 * stb__eps_closure(stb_matcher *m, stb_uint16 *list) +{ + int i,n = stb_arr_len(list); + + for(i=0; i < n; ++i) { + stb_uint16 *e = m->nodes[(int) list[i]].eps; + if (e) { + int j,k = stb_arr_len(e); + for (j=0; j < k; ++j) + list = stb__add_if_inactive(m, list, e[j]); + n = stb_arr_len(list); + } + } + + return list; +} + +int stb_matcher_match(stb_matcher *m, char *str) +{ + int result = 0; + int i,j,y,z; + stb_uint16 *previous = NULL; + stb_uint16 *current = NULL; + stb_uint16 *temp; + + stb_arr_setsize(previous, 4); + stb_arr_setsize(current, 4); + + previous = stb__add_if_inactive(m, previous, m->start_node); + previous = stb__eps_closure(m,previous); + stb__clear(m, previous); + + while (*str && stb_arr_len(previous)) { + y = stb_arr_len(previous); + for (i=0; i < y; ++i) { + stb_nfa_node *n = &m->nodes[(int) previous[i]]; + z = stb_arr_len(n->out); + for (j=0; j < z; ++j) { + if (n->out[j].match >= 0) { + if (n->out[j].match == *str) + current = stb__add_if_inactive(m, current, n->out[j].node); + } else if (n->out[j].match == -1) { + if (*str != '\n') + current = stb__add_if_inactive(m, current, n->out[j].node); + } else if (n->out[j].match < -1) { + int z = -n->out[j].match - 2; + if (m->charset[(stb_uint8) *str] & (1 << z)) + current = stb__add_if_inactive(m, current, n->out[j].node); + } + } + } + stb_arr_setlen(previous, 0); + + temp = previous; + previous = current; + current = temp; + + previous = stb__eps_closure(m,previous); + stb__clear(m, previous); + + ++str; + } + + // transition to pick up a '$' at the end + y = stb_arr_len(previous); + for (i=0; i < y; ++i) + m->nodes[(int) previous[i]].active = 1; + + for (i=0; i < y; ++i) { + stb_nfa_node *n = &m->nodes[(int) previous[i]]; + z = stb_arr_len(n->out); + for (j=0; j < z; ++j) { + if (n->out[j].match == '\n') + current = stb__add_if_inactive(m, current, n->out[j].node); + } + } + + previous = stb__eps_closure(m,previous); + stb__clear(m, previous); + + y = stb_arr_len(previous); + for (i=0; i < y; ++i) + if (m->nodes[(int) previous[i]].goal) + result = 1; + + stb_arr_free(previous); + stb_arr_free(current); + + return result && *str == 0; +} + +stb_int16 stb__get_dfa_node(stb_matcher *m, stb_uint16 *list) +{ + stb_uint16 node; + stb_uint32 data[8], *state, *newstate; + int i,j,n; + + state = (stb_uint32 *) stb_temp(data, m->num_words_per_dfa * 4); + memset(state, 0, m->num_words_per_dfa*4); + + n = stb_arr_len(list); + for (i=0; i < n; ++i) { + int x = list[i]; + state[x >> 5] |= 1 << (x & 31); + } + + // @TODO use a hash table + n = stb_arr_len(m->dfa_mapping); + i=j=0; + for(; j < n; ++i, j += m->num_words_per_dfa) { + // @TODO special case for <= 32 + if (!memcmp(state, m->dfa_mapping + j, m->num_words_per_dfa*4)) { + node = i; + goto done; + } + } + + assert(stb_arr_len(m->dfa) == i); + node = i; + + newstate = stb_arr_addn(m->dfa_mapping, m->num_words_per_dfa); + memcpy(newstate, state, m->num_words_per_dfa*4); + + // set all transitions to 'unknown' + stb_arr_add(m->dfa); + memset(m->dfa[i].transition, -1, sizeof(m->dfa[i].transition)); + + if (m->does_lex) { + int result = -1; + n = stb_arr_len(list); + for (i=0; i < n; ++i) { + if (m->nodes[(int) list[i]].goal > result) + result = m->nodes[(int) list[i]].goal; + } + + stb_arr_push(m->dfa_result, result); + } + +done: + stb_tempfree(data, state); + return node; +} + +static int stb__matcher_dfa(stb_matcher *m, char *str_c, int *len) +{ + stb_uint8 *str = (stb_uint8 *) str_c; + stb_int16 node,prevnode; + stb_dfa *trans; + int match_length = 0; + stb_int16 match_result=0; + + if (m->dfa_start == STB__DFA_UNDEF) { + stb_uint16 *list; + + m->num_words_per_dfa = (stb_arr_len(m->nodes)+31) >> 5; + stb__optimize(m); + + list = stb__add_if_inactive(m, NULL, m->start_node); + list = stb__eps_closure(m,list); + if (m->does_lex) { + m->dfa_start = stb__get_dfa_node(m,list); + stb__clear(m, list); + // DON'T allow start state to be a goal state! + // this allows people to specify regexes that can match 0 + // characters without them actually matching (also we don't + // check _before_ advancing anyway + if (m->dfa_start <= STB__DFA_MGOAL) + m->dfa_start = -(m->dfa_start - STB__DFA_MGOAL); + } else { + if (stb__clear_goalcheck(m, list)) + m->dfa_start = STB__DFA_GOAL; + else + m->dfa_start = stb__get_dfa_node(m,list); + } + stb_arr_free(list); + } + + prevnode = STB__DFA_UNDEF; + node = m->dfa_start; + trans = m->dfa; + + if (m->dfa_start == STB__DFA_GOAL) + return 1; + + for(;;) { + assert(node >= STB__DFA_VALID); + + // fast inner DFA loop; especially if STB__DFA_VALID is 0 + + do { + prevnode = node; + node = trans[node].transition[*str++]; + } while (node >= STB__DFA_VALID); + + assert(node >= STB__DFA_MGOAL - stb_arr_len(m->dfa)); + assert(node < stb_arr_len(m->dfa)); + + // special case for lex: need _longest_ match, so notice goal + // state without stopping + if (node <= STB__DFA_MGOAL) { + match_length = str - (stb_uint8 *) str_c; + node = -(node - STB__DFA_MGOAL); + match_result = node; + continue; + } + + // slow NFA->DFA conversion + + // or we hit the goal or the end of the string, but those + // can only happen once per search... + + if (node == STB__DFA_UNDEF) { + // build a list -- @TODO special case <= 32 states + // heck, use a more compact data structure for <= 16 and <= 8 ?! + + // @TODO keep states/newstates around instead of reallocating them + stb_uint16 *states = NULL; + stb_uint16 *newstates = NULL; + int i,j,y,z; + stb_uint32 *flags = &m->dfa_mapping[prevnode * m->num_words_per_dfa]; + assert(prevnode != STB__DFA_UNDEF); + stb_arr_setsize(states, 4); + stb_arr_setsize(newstates,4); + for (j=0; j < m->num_words_per_dfa; ++j) { + for (i=0; i < 32; ++i) { + if (*flags & (1 << i)) + stb_arr_push(states, j*32+i); + } + ++flags; + } + // states is now the states we were in in the previous node; + // so now we can compute what node it transitions to on str[-1] + + y = stb_arr_len(states); + for (i=0; i < y; ++i) { + stb_nfa_node *n = &m->nodes[(int) states[i]]; + z = stb_arr_len(n->out); + for (j=0; j < z; ++j) { + if (n->out[j].match >= 0) { + if (n->out[j].match == str[-1] || (str[-1] == 0 && n->out[j].match == '\n')) + newstates = stb__add_if_inactive(m, newstates, n->out[j].node); + } else if (n->out[j].match == -1) { + if (str[-1] != '\n' && str[-1]) + newstates = stb__add_if_inactive(m, newstates, n->out[j].node); + } else if (n->out[j].match < -1) { + int z = -n->out[j].match - 2; + if (m->charset[str[-1]] & (1 << z)) + newstates = stb__add_if_inactive(m, newstates, n->out[j].node); + } + } + } + // AND add in the start state! + if (!m->match_start || (str[-1] == '\n' && !m->does_lex)) + newstates = stb__add_if_inactive(m, newstates, m->start_node); + // AND epsilon close it + newstates = stb__eps_closure(m, newstates); + // if it's a goal state, then that's all there is to it + if (stb__clear_goalcheck(m, newstates)) { + if (m->does_lex) { + match_length = str - (stb_uint8 *) str_c; + node = stb__get_dfa_node(m,newstates); + match_result = node; + node = -node + STB__DFA_MGOAL; + trans = m->dfa; // could have gotten realloc()ed + } else + node = STB__DFA_GOAL; + } else if (str[-1] == 0 || stb_arr_len(newstates) == 0) { + node = STB__DFA_END; + } else { + node = stb__get_dfa_node(m,newstates); + trans = m->dfa; // could have gotten realloc()ed + } + trans[prevnode].transition[str[-1]] = node; + if (node <= STB__DFA_MGOAL) + node = -(node - STB__DFA_MGOAL); + stb_arr_free(newstates); + stb_arr_free(states); + } + + if (node == STB__DFA_GOAL) { + return 1; + } + if (node == STB__DFA_END) { + if (m->does_lex) { + if (match_result) { + if (len) *len = match_length; + return m->dfa_result[(int) match_result]; + } + } + return 0; + } + + assert(node != STB__DFA_UNDEF); + } +} + +int stb_matcher_find(stb_matcher *m, char *str) +{ + assert(m->does_lex == 0); + return stb__matcher_dfa(m, str, NULL); +} + +int stb_lex(stb_matcher *m, char *str, int *len) +{ + assert(m->does_lex); + return stb__matcher_dfa(m, str, len); +} + +int stb_regex(char *regex, char *str) +{ + static stb_perfect p; + static stb_matcher ** matchers; + static char ** regexps; + static char ** regexp_cache; + static unsigned short *mapping; + int z = stb_perfect_hash(&p, (int) regex); + if (z >= 0) { + if (strcmp(regex, regexp_cache[(int) mapping[z]])) { + int i = mapping[z]; + stb_matcher_free(matchers[i]); + free(regexp_cache[i]); + regexps[i] = regex; + regexp_cache[i] = strdup(regex); + matchers[i] = stb_regex_matcher(regex); + } + } else { + int i,n; + if (regex == NULL) { + for (i=0; i < stb_arr_len(matchers); ++i) { + stb_matcher_free(matchers[i]); + free(regexp_cache[i]); + } + stb_arr_free(matchers); + stb_arr_free(regexps); + stb_arr_free(regexp_cache); + stb_perfect_destroy(&p); + free(mapping); mapping = NULL; + return -1; + } + stb_arr_push(regexps, regex); + stb_arr_push(regexp_cache, strdup(regex)); + stb_arr_push(matchers, stb_regex_matcher(regex)); + stb_perfect_destroy(&p); + n = stb_perfect_create(&p, (unsigned int *) (char **) regexps, stb_arr_len(regexps)); + mapping = (unsigned short *) realloc(mapping, n * sizeof(*mapping)); + for (i=0; i < stb_arr_len(regexps); ++i) + mapping[stb_perfect_hash(&p, (int) regexps[i])] = i; + z = stb_perfect_hash(&p, (int) regex); + } + return stb_matcher_find(matchers[(int) mapping[z]], str); +} + +#endif // STB_DEFINE + + +#if 0 +////////////////////////////////////////////////////////////////////////////// +// +// C source-code introspection +// + +// runtime structure +typedef struct +{ + char *name; + char *type; // base type + char *comment; // content of comment field + int size; // size of base type + int offset; // field offset + int arrcount[8]; // array sizes; -1 = pointer indirection; 0 = end of list +} stb_info_field; + +typedef struct +{ + char *structname; + int size; + int num_fields; + stb_info_field *fields; +} stb_info_struct; + +extern stb_info_struct stb_introspect_output[]; + +// + +STB_EXTERN void stb_introspect_precompiled(stb_info_struct *compiled); +STB_EXTERN void stb__introspect(char *path, char *file); + +#define stb_introspect_ship() stb__introspect(NULL, NULL, stb__introspect_output) + +#ifdef STB_SHIP +#define stb_introspect() stb_introspect_ship() +#define stb_introspect_path(p) stb_introspect_ship() +#else +// bootstrapping: define stb_introspect() (or 'path') the first time +#define stb_introspect() stb__introspect(NULL, __FILE__, NULL) +#define stb_introspect_auto() stb__introspect(NULL, __FILE__, stb__introspect_output) + +#define stb_introspect_path(p) stb__introspect(p, __FILE__, NULL) +#define stb_introspect_path(p) stb__introspect(p, __FILE__, NULL) +#endif + +#ifdef STB_DEFINE + +#ifndef STB_INTROSPECT_CPP + #ifdef __cplusplus + #define STB_INTROSPECT_CPP 1 + #else + #define STB_INTROSPECT_CPP 0 + #endif +#endif + +void stb_introspect_precompiled(stb_info_struct *compiled) +{ + +} + + +static void stb__introspect_filename(char *buffer, char *path) +{ + #if STB_INTROSPECT_CPP + sprintf(buffer, "%s/stb_introspect.cpp", path); + #else + sprintf(buffer, "%s/stb_introspect.c", path); + #endif +} + +static void stb__introspect_compute(char *path, char *file) +{ + int i; + char ** include_list = NULL; + char ** introspect_list = NULL; + FILE *f; + f = fopen(file, "w"); + if (!f) return; + + fputs("// if you get compiler errors, change the following 0 to a 1:\n", f); + fputs("#define STB_INTROSPECT_INVALID 0\n\n", f); + fputs("// this will force the code to compile, and force the introspector\n", f); + fputs("// to run and then exit, allowing you to recompile\n\n\n", f); + fputs("#include \"stb.h\"\n\n",f ); + fputs("#if STB_INTROSPECT_INVALID\n", f); + fputs(" stb_info_struct stb__introspect_output[] = { (void *) 1 }\n", f); + fputs("#else\n\n", f); + for (i=0; i < stb_arr_len(include_list); ++i) + fprintf(f, " #include \"%s\"\n", include_list[i]); + + fputs(" stb_info_struct stb__introspect_output[] =\n{\n", f); + for (i=0; i < stb_arr_len(introspect_list); ++i) + fprintf(f, " stb_introspect_%s,\n", introspect_list[i]); + fputs(" };\n", f); + fputs("#endif\n", f); + fclose(f); +} + +static stb_info_struct *stb__introspect_info; + +#ifndef STB_SHIP + +#endif + +void stb__introspect(char *path, char *file, stb_info_struct *compiled) +{ + static int first=1; + if (!first) return; + first=0; + + stb__introspect_info = compiled; + + #ifndef STB_SHIP + if (path || file) { + int bail_flag = compiled && compiled[0].structname == (void *) 1; + int needs_building = bail_flag; + struct stb__stat st; + char buffer[1024], buffer2[1024]; + if (!path) { + stb_splitpath(buffer, file, STB_PATH); + path = buffer; + } + // bail if the source path doesn't exist + if (!stb_fexists(path)) return; + + stb__introspect_filename(buffer2, path); + + // get source/include files timestamps, compare to output-file timestamp; + // if mismatched, regenerate + + if (stb__stat(buffer2, &st)) + needs_building = STB_TRUE; + + { + // find any file that contains an introspection command and is newer + // if needs_building is already true, we don't need to do this test, + // but we still need these arrays, so go ahead and get them + char **all[3]; + all[0] = stb_readdir_files_mask(path, "*.h"); + all[1] = stb_readdir_files_mask(path, "*.c"); + all[2] = stb_readdir_files_mask(path, "*.cpp"); + int i,j; + if (needs_building) { + for (j=0; j < 3; ++j) { + for (i=0; i < stb_arr_len(all[j]); ++i) { + struct stb__stat st2; + if (!stb__stat(all[j][i], &st2)) { + if (st.st_mtime < st2.st_mtime) { + char *z = stb_filec(all[j][i], NULL); + int found=STB_FALSE; + while (y) { + y = strstr(y, "//si"); + if (y && isspace(y[4])) { + found = STB_TRUE; + break; + } + } + needs_building = STB_TRUE; + goto done; + } + } + } + } + done:; + } + char *z = stb_filec(all[i], NULL), *y = z; + int found=STB_FALSE; + while (y) { + y = strstr(y, "//si"); + if (y && isspace(y[4])) { + found = STB_TRUE; + break; + } + } + if (found) + stb_arr_push(introspect_h, strdup(all[i])); + free(z); + } + } + stb_readdir_free(all); + if (!needs_building) { + for (i=0; i < stb_arr_len(introspect_h); ++i) { + struct stb__stat st2; + if (!stb__stat(introspect_h[i], &st2)) + if (st.st_mtime < st2.st_mtime) + needs_building = STB_TRUE; + } + } + + if (needs_building) { + stb__introspect_compute(path, buffer2); + } + } + } + #endif +} +#endif +#endif + +#ifdef STB_INTROSPECT +// compile-time code-generator +#define INTROSPECT(x) int main(int argc, char **argv) { stb__introspect(__FILE__); return 0; } +#define FILE(x) + +void stb__introspect(char *filename) +{ + char *file = stb_file(filename, NULL); + char *s = file, *t, **p; + char *out_name = "stb_introspect.c"; + char *out_path; + STB_ARR(char) filelist = NULL; + int i,n; + if (!file) stb_fatal("Couldn't open %s", filename); + + out_path = stb_splitpathdup(filename, STB_PATH); + + // search for the macros + while (*s) { + char buffer[256]; + while (*s && !isupper(*s)) ++s; + s = stb_strtok_invert(buffer, s, "ABCDEFGHIJKLMNOPQRSTUVWXYZ"); + s = stb_skipwhite(s); + if (*s == '(') { + ++s; + t = strchr(s, ')'); + if (t == NULL) stb_fatal("Error parsing %s", filename); + + } + } +} + + + +#endif + + +////////////////////////////////////////////////////////////////////////////// +// +// STB-C sliding-window dictionary compression +// +// This uses a DEFLATE-style sliding window, but no bitwise entropy. +// Everything is on byte boundaries, so you could then apply a byte-wise +// entropy code, though that's nowhere near as effective. +// +// An STB-C stream begins with a 16-byte header: +// 4 bytes: 0x57 0xBC 0x00 0x00 +// 8 bytes: big-endian size of decompressed data, 64-bits +// 4 bytes: big-endian size of window (how far back decompressor may need) +// +// The following symbols appear in the stream (these were determined ad hoc, +// not by analysis): +// +// [dict] 00000100 yyyyyyyy yyyyyyyy yyyyyyyy xxxxxxxx xxxxxxxx +// [END] 00000101 11111010 cccccccc cccccccc cccccccc cccccccc +// [dict] 00000110 yyyyyyyy yyyyyyyy yyyyyyyy xxxxxxxx +// [literals] 00000111 zzzzzzzz zzzzzzzz +// [literals] 00001zzz zzzzzzzz +// [dict] 00010yyy yyyyyyyy yyyyyyyy xxxxxxxx xxxxxxxx +// [dict] 00011yyy yyyyyyyy yyyyyyyy xxxxxxxx +// [literals] 001zzzzz +// [dict] 01yyyyyy yyyyyyyy xxxxxxxx +// [dict] 1xxxxxxx yyyyyyyy +// +// xxxxxxxx: match length - 1 +// yyyyyyyy: backwards distance - 1 +// zzzzzzzz: num literals - 1 +// cccccccc: adler32 checksum of decompressed data +// (all big-endian) + + +STB_EXTERN stb_uint stb_decompress_length(stb_uchar *input); +STB_EXTERN stb_uint stb_decompress(stb_uchar *out,stb_uchar *in,stb_uint len); +STB_EXTERN stb_uint stb_compress (stb_uchar *out,stb_uchar *in,stb_uint len); +STB_EXTERN void stb_compress_window(int z); +STB_EXTERN void stb_compress_hashsize(unsigned int z); + +STB_EXTERN int stb_compress_tofile(char *filename, char *in, stb_uint len); +STB_EXTERN int stb_compress_intofile(FILE *f, char *input, stb_uint len); +STB_EXTERN char *stb_decompress_fromfile(char *filename, stb_uint *len); + +STB_EXTERN int stb_compress_stream_start(FILE *f); +STB_EXTERN void stb_compress_stream_end(int close); +STB_EXTERN void stb_write(char *data, int data_len); + +#ifdef STB_DEFINE + +stb_uint stb_decompress_length(stb_uchar *input) +{ + return (input[8] << 24) + (input[9] << 16) + (input[10] << 8) + input[11]; +} + +//////////////////// decompressor /////////////////////// + +// simple implementation that just writes whole thing into big block + +static unsigned char *stb__barrier; +static unsigned char *stb__barrier2; +static unsigned char *stb__barrier3; +static unsigned char *stb__barrier4; + +static stb_uchar *stb__dout; +static void stb__match(stb_uchar *data, stb_uint length) +{ + // INVERSE of memmove... write each byte before copying the next... + assert (stb__dout + length <= stb__barrier); + if (stb__dout + length > stb__barrier) { stb__dout += length; return; } + if (data < stb__barrier4) { stb__dout = stb__barrier+1; return; } + while (length--) *stb__dout++ = *data++; +} + +static void stb__lit(stb_uchar *data, stb_uint length) +{ + assert (stb__dout + length <= stb__barrier); + if (stb__dout + length > stb__barrier) { stb__dout += length; return; } + if (data < stb__barrier2) { stb__dout = stb__barrier+1; return; } + memcpy(stb__dout, data, length); + stb__dout += length; +} + +#define stb__in2(x) ((i[x] << 8) + i[(x)+1]) +#define stb__in3(x) ((i[x] << 16) + stb__in2((x)+1)) +#define stb__in4(x) ((i[x] << 24) + stb__in3((x)+1)) + +static stb_uchar *stb_decompress_token(stb_uchar *i) +{ + if (*i >= 0x20) { // use fewer if's for cases that expand small + if (*i >= 0x80) stb__match(stb__dout-i[1]-1, i[0] - 0x80 + 1), i += 2; + else if (*i >= 0x40) stb__match(stb__dout-(stb__in2(0) - 0x4000 + 1), i[2]+1), i += 3; + else /* *i >= 0x20 */ stb__lit(i+1, i[0] - 0x20 + 1), i += 1 + (i[0] - 0x20 + 1); + } else { // more ifs for cases that expand large, since overhead is amortized + if (*i >= 0x18) stb__match(stb__dout-(stb__in3(0) - 0x180000 + 1), i[3]+1), i += 4; + else if (*i >= 0x10) stb__match(stb__dout-(stb__in3(0) - 0x100000 + 1), stb__in2(3)+1), i += 5; + else if (*i >= 0x08) stb__lit(i+2, stb__in2(0) - 0x0800 + 1), i += 2 + (stb__in2(0) - 0x0800 + 1); + else if (*i == 0x07) stb__lit(i+3, stb__in2(1) + 1), i += 3 + (stb__in2(1) + 1); + else if (*i == 0x06) stb__match(stb__dout-(stb__in3(1)+1), i[4]+1), i += 5; + else if (*i == 0x04) stb__match(stb__dout-(stb__in3(1)+1), stb__in2(4)+1), i += 6; + } + return i; +} + +stb_uint stb_decompress(stb_uchar *output, stb_uchar *i, stb_uint length) +{ + stb_uint olen; + if (stb__in4(0) != 0x57bC0000) return 0; + if (stb__in4(4) != 0) return 0; // error! stream is > 4GB + olen = stb_decompress_length(i); + stb__barrier2 = i; + stb__barrier3 = i+length; + stb__barrier = output + olen; + stb__barrier4 = output; + i += 16; + + stb__dout = output; + while (1) { + stb_uchar *old_i = i; + i = stb_decompress_token(i); + if (i == old_i) { + if (*i == 0x05 && i[1] == 0xfa) { + assert(stb__dout == output + olen); + if (stb__dout != output + olen) return 0; + if (stb_adler32(1, output, olen) != (stb_uint) stb__in4(2)) + return 0; + return olen; + } else { + assert(0); /* NOTREACHED */ + return 0; + } + } + assert(stb__dout <= output + olen); + if (stb__dout > output + olen) + return 0; + } +} + +char *stb_decompress_fromfile(char *filename, unsigned int *len) +{ + unsigned int n; + char *q; + unsigned char *p; + FILE *f = fopen(filename, "rb"); if (f == NULL) return NULL; + fseek(f, 0, SEEK_END); + n = ftell(f); + fseek(f, 0, SEEK_SET); + p = (unsigned char * ) malloc(n); if (p == NULL) return NULL; + fread(p, 1, n, f); + fclose(f); + if (p == NULL) return NULL; + if (p[0] != 0x57 || p[1] != 0xBc || p[2] || p[3]) { free(p); return NULL; } + q = (char *) malloc(stb_decompress_length(p)+1); + if (!q) { free(p); return NULL; } + *len = stb_decompress((unsigned char *) q, p, n); + if (*len) q[*len] = 0; + free(p); + return q; +} + +#if 0 +// streaming decompressor + +static struct +{ + stb__uchar *in_buffer; + stb__uchar *match; + + stb__uint pending_literals; + stb__uint pending_match; +} xx; + + + +static void stb__match(stb_uchar *data, stb_uint length) +{ + // INVERSE of memmove... write each byte before copying the next... + assert (stb__dout + length <= stb__barrier); + if (stb__dout + length > stb__barrier) { stb__dout += length; return; } + if (data < stb__barrier2) { stb__dout = stb__barrier+1; return; } + while (length--) *stb__dout++ = *data++; +} + +static void stb__lit(stb_uchar *data, stb_uint length) +{ + assert (stb__dout + length <= stb__barrier); + if (stb__dout + length > stb__barrier) { stb__dout += length; return; } + if (data < stb__barrier2) { stb__dout = stb__barrier+1; return; } + memcpy(stb__dout, data, length); + stb__dout += length; +} + +static void sx_match(stb_uchar *data, stb_uint length) +{ + xx.match = data; + xx.pending_match = length; +} + +static void sx_lit(stb_uchar *data, stb_uint length) +{ + xx.pending_lit = length; +} + +static int stb_decompress_token_state(void) +{ + stb__uchar *i = xx.in_buffer; + + if (*i >= 0x20) { // use fewer if's for cases that expand small + if (*i >= 0x80) sx_match(stb__dout-i[1]-1, i[0] - 0x80 + 1), i += 2; + else if (*i >= 0x40) sx_match(stb__dout-(stb__in2(0) - 0x4000 + 1), i[2]+1), i += 3; + else /* *i >= 0x20 */ sx_lit(i+1, i[0] - 0x20 + 1), i += 1; + } else { // more ifs for cases that expand large, since overhead is amortized + if (*i >= 0x18) sx_match(stb__dout-(stb__in3(0) - 0x180000 + 1), i[3]+1), i += 4; + else if (*i >= 0x10) sx_match(stb__dout-(stb__in3(0) - 0x100000 + 1), stb__in2(3)+1), i += 5; + else if (*i >= 0x08) sx_lit(i+2, stb__in2(0) - 0x0800 + 1), i += 2; + else if (*i == 0x07) sx_lit(i+3, stb__in2(1) + 1), i += 3; + else if (*i == 0x06) sx_match(stb__dout-(stb__in3(1)+1), i[4]+1), i += 5; + else if (*i == 0x04) sx_match(stb__dout-(stb__in3(1)+1), stb__in2(4)+1), i += 6; + else return 0; + } + xx.in_buffer = i; + return 1; +} +#endif + + + +//////////////////// compressor /////////////////////// + +static unsigned int stb_matchlen(stb_uchar *m1, stb_uchar *m2, stb_uint maxlen) +{ + stb_uint i; + for (i=0; i < maxlen; ++i) + if (m1[i] != m2[i]) return i; + return i; +} + +// simple implementation that just takes the source data in a big block + +static stb_uchar *stb__out; +static FILE *stb__outfile; +static stb_uint stb__outbytes; + +static void stb__write(unsigned char v) +{ + fputc(v, stb__outfile); + ++stb__outbytes; +} + +#define stb_out(v) (stb__out ? *stb__out++ = (stb_uchar) (v) : stb__write((stb_uchar) (v))) + +static void stb_out2(stb_uint v) +{ + stb_out(v >> 8); + stb_out(v); +} + +static void stb_out3(stb_uint v) { stb_out(v >> 16); stb_out(v >> 8); stb_out(v); } +static void stb_out4(stb_uint v) { stb_out(v >> 24); stb_out(v >> 16); + stb_out(v >> 8 ); stb_out(v); } + +static void outliterals(stb_uchar *in, int numlit) +{ + while (numlit > 65536) { + outliterals(in,65536); + in += 65536; + numlit -= 65536; + } + + if (numlit == 0) ; + else if (numlit <= 32) stb_out (0x000020 + numlit-1); + else if (numlit <= 2048) stb_out2(0x000800 + numlit-1); + else /* numlit <= 65536) */ stb_out3(0x070000 + numlit-1); + + if (stb__out) { + memcpy(stb__out,in,numlit); + stb__out += numlit; + } else + fwrite(in, 1, numlit, stb__outfile); +} + +static int stb__window = 0x40000; // 256K +void stb_compress_window(int z) +{ + if (z >= 0x1000000) z = 0x1000000; // limit of implementation + if (z < 0x100) z = 0x100; // insanely small + stb__window = z; +} + +static int stb_not_crap(int best, int dist) +{ + return ((best > 2 && dist <= 0x00100) + || (best > 5 && dist <= 0x04000) + || (best > 7 && dist <= 0x80000)); +} + +static stb_uint stb__hashsize = 32768; +void stb_compress_hashsize(unsigned int y) +{ + unsigned int z = 1024; + while (z < y) z <<= 1; + stb__hashsize = z >> 2; // pass in bytes, store #pointers +} + +// note that you can play with the hashing functions all you +// want without needing to change the decompressor +#define stb__hc(q,h,c) (((h) << 7) + ((h) >> 25) + q[c]) +#define stb__hc2(q,h,c,d) (((h) << 14) + ((h) >> 18) + (q[c] << 7) + q[d]) +#define stb__hc3(q,c,d,e) ((q[c] << 14) + (q[d] << 7) + q[e]) + +static stb_uint32 stb__running_adler; + +static int stb_compress_chunk(stb_uchar *history, + stb_uchar *start, + stb_uchar *end, + int length, + int *pending_literals, + stb_uchar **chash, + stb_uint mask) +{ + int window = stb__window; + stb_uint match_max; + stb_uchar *lit_start = start - *pending_literals; + stb_uchar *q = start; + + #define STB__SCRAMBLE(h) (((h) + ((h) >> 16)) & mask) + + // stop short of the end so we don't scan off the end doing + // the hashing; this means we won't compress the last few bytes + // unless they were part of something longer + while (q < start+length && q+12 < end) { + int m; + stb_uint h1,h2,h3,h4, h; + stb_uchar *t; + int best = 2, dist=0; + + if (q+65536 > end) + match_max = end-q; + else + match_max = 65536; + + #define stb__nc(b,d) ((d) <= window && ((b) > 9 || stb_not_crap(b,d))) + + #define STB__TRY(t,p) /* avoid retrying a match we already tried */ \ + if (p ? dist != q-t : 1) \ + if ((m = stb_matchlen(t, q, match_max)) > best) \ + if (stb__nc(m,q-(t))) \ + best = m, dist = q - (t) + + // rather than search for all matches, only try 4 candidate locations, + // chosen based on 4 different hash functions of different lengths. + // this strategy is inspired by LZO; hashing is unrolled here using the + // 'hc' macro + h = stb__hc3(q,0, 1, 2); h1 = STB__SCRAMBLE(h); + t = chash[h1]; if (t) STB__TRY(t,0); + h = stb__hc2(q,h, 3, 4); h2 = STB__SCRAMBLE(h); + h = stb__hc2(q,h, 5, 6); t = chash[h2]; if (t) STB__TRY(t,1); + h = stb__hc2(q,h, 7, 8); h3 = STB__SCRAMBLE(h); + h = stb__hc2(q,h, 9,10); t = chash[h3]; if (t) STB__TRY(t,1); + h = stb__hc2(q,h,11,12); h4 = STB__SCRAMBLE(h); + t = chash[h4]; if (t) STB__TRY(t,1); + + // because we use a shared hash table, can only update it + // _after_ we've probed all of them + chash[h1] = chash[h2] = chash[h3] = chash[h4] = q; + + if (best > 2) + assert(dist > 0); + + // see if our best match qualifies + if (best < 3) { // fast path literals + ++q; + } else if (best > 2 && best <= 0x80 && dist <= 0x100) { + outliterals(lit_start, q-lit_start); lit_start = (q += best); + stb_out(0x80 + best-1); + stb_out(dist-1); + } else if (best > 5 && best <= 0x100 && dist <= 0x4000) { + outliterals(lit_start, q-lit_start); lit_start = (q += best); + stb_out2(0x4000 + dist-1); + stb_out(best-1); + } else if (best > 7 && best <= 0x100 && dist <= 0x80000) { + outliterals(lit_start, q-lit_start); lit_start = (q += best); + stb_out3(0x180000 + dist-1); + stb_out(best-1); + } else if (best > 8 && best <= 0x10000 && dist <= 0x80000) { + outliterals(lit_start, q-lit_start); lit_start = (q += best); + stb_out3(0x100000 + dist-1); + stb_out2(best-1); + } else if (best > 9 && dist <= 0x1000000) { + if (best > 65536) best = 65536; + outliterals(lit_start, q-lit_start); lit_start = (q += best); + if (best <= 0x100) { + stb_out(0x06); + stb_out3(dist-1); + stb_out(best-1); + } else { + stb_out(0x04); + stb_out3(dist-1); + stb_out2(best-1); + } + } else { // fallback literals if no match was a balanced tradeoff + ++q; + } + } + + // if we didn't get all the way, add the rest to literals + if (q-start < length) + q = start+length; + + // the literals are everything from lit_start to q + *pending_literals = (q - lit_start); + + stb__running_adler = stb_adler32(stb__running_adler, start, q - start); + return q - start; +} + +static int stb_compress_inner(stb_uchar *input, stb_uint length) +{ + int literals = 0; + stb_uint len,i; + + stb_uchar **chash; + chash = (stb_uchar**) malloc(stb__hashsize * sizeof(stb_uchar*)); + if (chash == NULL) return 0; // failure + for (i=0; i < stb__hashsize; ++i) + chash[i] = NULL; + + // stream signature + stb_out(0x57); stb_out(0xbc); + stb_out2(0); + + stb_out4(0); // 64-bit length requires 32-bit leading 0 + stb_out4(length); + stb_out4(stb__window); + + stb__running_adler = 1; + + len = stb_compress_chunk(input, input, input+length, length, &literals, chash, stb__hashsize-1); + assert(len == length); + + outliterals(input+length - literals, literals); + + free(chash); + + stb_out2(0x05fa); // end opcode + + stb_out4(stb__running_adler); + + return 1; // success +} + +stb_uint stb_compress(stb_uchar *out, stb_uchar *input, stb_uint length) +{ + stb__out = out; + stb__outfile = NULL; + + stb_compress_inner(input, length); + + return stb__out - out; +} + +int stb_compress_tofile(char *filename, char *input, unsigned int length) +{ + //int maxlen = length + 512 + (length >> 2); // total guess + //char *buffer = (char *) malloc(maxlen); + //int blen = stb_compress((stb_uchar*)buffer, (stb_uchar*)input, length); + + stb__out = NULL; + stb__outfile = fopen(filename, "wb"); + if (!stb__outfile) return 0; + + stb__outbytes = 0; + + if (!stb_compress_inner((stb_uchar*)input, length)) + return 0; + + fclose(stb__outfile); + + return stb__outbytes; +} + +int stb_compress_intofile(FILE *f, char *input, unsigned int length) +{ + //int maxlen = length + 512 + (length >> 2); // total guess + //char *buffer = (char*)malloc(maxlen); + //int blen = stb_compress((stb_uchar*)buffer, (stb_uchar*)input, length); + + stb__out = NULL; + stb__outfile = f; + if (!stb__outfile) return 0; + + stb__outbytes = 0; + + if (!stb_compress_inner((stb_uchar*)input, length)) + return 0; + + return stb__outbytes; +} + +////////////////////// streaming I/O version ///////////////////// + + +static size_t stb_out_backpatch_id(void) +{ + if (stb__out) + return (size_t) stb__out; + else + return ftell(stb__outfile); +} + +static void stb_out_backpatch(size_t id, stb_uint value) +{ + stb_uchar data[4] = { value >> 24, value >> 16, value >> 8, value }; + if (stb__out) { + memcpy((void *) id, data, 4); + } else { + stb_uint where = ftell(stb__outfile); + fseek(stb__outfile, id, SEEK_SET); + fwrite(data, 4, 1, stb__outfile); + fseek(stb__outfile, where, SEEK_SET); + } +} + +// ok, the wraparound buffer was a total failure. let's instead +// use a copying-in-place buffer, which lets us share the code. +// This is way less efficient but it'll do for now. + +static struct +{ + stb_uchar *buffer; + int size; // physical size of buffer in bytes + + int valid; // amount of valid data in bytes + int start; // bytes of data already output + + int window; + int fsize; + + int pending_literals; // bytes not-quite output but counted in start + int length_id; + + stb_uint total_bytes; + + stb_uchar **chash; + stb_uint hashmask; +} xtb; + +static int stb_compress_streaming_start(void) +{ + stb_uint i; + xtb.size = stb__window * 3; + xtb.buffer = (stb_uchar*)malloc(xtb.size); + if (!xtb.buffer) return 0; + + xtb.chash = (stb_uchar**)malloc(sizeof(*xtb.chash) * stb__hashsize); + if (!xtb.chash) { + free(xtb.buffer); + return 0; + } + + for (i=0; i < stb__hashsize; ++i) + xtb.chash[i] = NULL; + + xtb.hashmask = stb__hashsize-1; + + xtb.valid = 0; + xtb.start = 0; + xtb.window = stb__window; + xtb.fsize = stb__window; + xtb.pending_literals = 0; + xtb.total_bytes = 0; + + // stream signature + stb_out(0x57); stb_out(0xbc); stb_out2(0); + + stb_out4(0); // 64-bit length requires 32-bit leading 0 + + xtb.length_id = stb_out_backpatch_id(); + stb_out4(0); // we don't know the output length yet + + stb_out4(stb__window); + + stb__running_adler = 1; + + return 1; +} + +static int stb_compress_streaming_end(void) +{ + // flush out any remaining data + stb_compress_chunk(xtb.buffer, xtb.buffer+xtb.start, xtb.buffer+xtb.valid, + xtb.valid-xtb.start, &xtb.pending_literals, xtb.chash, xtb.hashmask); + + // write out pending literals + outliterals(xtb.buffer + xtb.valid - xtb.pending_literals, xtb.pending_literals); + + stb_out2(0x05fa); // end opcode + stb_out4(stb__running_adler); + + stb_out_backpatch(xtb.length_id, xtb.total_bytes); + + free(xtb.buffer); + free(xtb.chash); + return 1; +} + +void stb_write(char *data, int data_len) +{ + stb_uint i; + + // @TODO: fast path for filling the buffer and doing nothing else + // if (xtb.valid + data_len < xtb.size) + + xtb.total_bytes += data_len; + + while (data_len) { + // fill buffer + if (xtb.valid < xtb.size) { + int amt = xtb.size - xtb.valid; + if (data_len < amt) amt = data_len; + memcpy(xtb.buffer + xtb.valid, data, amt); + data_len -= amt; + data += amt; + xtb.valid += amt; + } + if (xtb.valid < xtb.size) + return; + + // at this point, the buffer is full + + // if we can process some data, go for it; make sure + // we leave an 'fsize's worth of data, though + if (xtb.start + xtb.fsize < xtb.valid) { + int amount = (xtb.valid - xtb.fsize) - xtb.start; + int n; + assert(amount > 0); + n = stb_compress_chunk(xtb.buffer, xtb.buffer + xtb.start, xtb.buffer + xtb.valid, + amount, &xtb.pending_literals, xtb.chash, xtb.hashmask); + xtb.start += n; + } + + assert(xtb.start + xtb.fsize >= xtb.valid); + // at this point, our future size is too small, so we + // need to flush some history. we, in fact, flush exactly + // one window's worth of history + + { + int flush = xtb.window; + assert(xtb.start >= flush); + assert(xtb.valid >= flush); + + // if 'pending literals' extends back into the shift region, + // write them out + if (xtb.start - xtb.pending_literals < flush) { + outliterals(xtb.buffer + xtb.start - xtb.pending_literals, xtb.pending_literals); + xtb.pending_literals = 0; + } + + // now shift the window + memmove(xtb.buffer, xtb.buffer + flush, xtb.valid - flush); + xtb.start -= flush; + xtb.valid -= flush; + + for (i=0; i <= xtb.hashmask; ++i) + if (xtb.chash[i] < xtb.buffer + flush) + xtb.chash[i] = NULL; + else + xtb.chash[i] -= flush; + } + // and now that we've made room for more data, go back to the top + } +} + +int stb_compress_stream_start(FILE *f) +{ + stb__out = NULL; + stb__outfile = f; + + if (f == NULL) + return 0; + + if (!stb_compress_streaming_start()) + return 0; + + return 1; +} + +void stb_compress_stream_end(int close) +{ + stb_compress_streaming_end(); + if (close && stb__outfile) { + fclose(stb__outfile); + } +} + +#endif // STB_DEFINE + +////////////////////////////////////////////////////////////////////////////// +// +// File abstraction... tired of not having this... we can write +// compressors to be layers over these that auto-close their children. + + +typedef struct stbfile +{ + int (*getbyte)(struct stbfile *); // -1 on EOF + unsigned int (*getdata)(struct stbfile *, void *block, unsigned int len); + + int (*putbyte)(struct stbfile *, int byte); + unsigned int (*putdata)(struct stbfile *, void *block, unsigned int len); + + unsigned int (*size)(struct stbfile *); + + unsigned int (*tell)(struct stbfile *); + void (*backpatch)(struct stbfile *, unsigned int tell, void *block, unsigned int len); + + void (*close)(struct stbfile *); + + FILE *f; // file to fread/fwrite + unsigned char *buffer; // input/output buffer + unsigned char *indata, *inend; // input buffer + union { + int various; + void *ptr; + }; +} stbfile; + +STB_EXTERN unsigned int stb_getc(stbfile *f); // read +STB_EXTERN int stb_putc(stbfile *f, int ch); // write +STB_EXTERN unsigned int stb_getdata(stbfile *f, void *buffer, unsigned int len); // read +STB_EXTERN unsigned int stb_putdata(stbfile *f, void *buffer, unsigned int len); // write +STB_EXTERN unsigned int stb_tell(stbfile *f); // read +STB_EXTERN unsigned int stb_size(stbfile *f); // read/write +STB_EXTERN void stb_backpatch(stbfile *f, unsigned int tell, void *buffer, unsigned int len); // write + +#ifdef STB_DEFINE + +unsigned int stb_getc(stbfile *f) { return f->getbyte(f); } +int stb_putc(stbfile *f, int ch) { return f->putbyte(f, ch); } + +unsigned int stb_getdata(stbfile *f, void *buffer, unsigned int len) +{ + return f->getdata(f, buffer, len); +} +unsigned int stb_putdata(stbfile *f, void *buffer, unsigned int len) +{ + return f->putdata(f, buffer, len); +} +void stb_close(stbfile *f) +{ + f->close(f); + free(f); +} +unsigned int stb_tell(stbfile *f) { return f->tell(f); } +unsigned int stb_size(stbfile *f) { return f->size(f); } +void stb_backpatch(stbfile *f, unsigned int tell, void *buffer, unsigned int len) +{ + f->backpatch(f,tell,buffer,len); +} + +// FILE * implementation +static int stb__fgetbyte(stbfile *f) { return fgetc(f->f); } +static int stb__fputbyte(stbfile *f, int ch) { return fputc(ch, f->f)==0; } +static unsigned int stb__fgetdata(stbfile *f, void *buffer, unsigned int len) { return fread(buffer,1,len,f->f); } +static unsigned int stb__fputdata(stbfile *f, void *buffer, unsigned int len) { return fwrite(buffer,1,len,f->f); } +static unsigned int stb__fsize(stbfile *f) { return stb_filelen(f->f); } +static unsigned int stb__ftell(stbfile *f) { return ftell(f->f); } +static void stb__fbackpatch(stbfile *f, unsigned int where, void *buffer, unsigned int len) +{ + fseek(f->f, where, SEEK_SET); + fwrite(buffer, 1, len, f->f); + fseek(f->f, 0, SEEK_END); +} +static void stb__fclose(stbfile *f) { fclose(f->f); } + +stbfile *stb_openf(FILE *f) +{ + stbfile m = { stb__fgetbyte, stb__fgetdata, + stb__fputbyte, stb__fputdata, + stb__fsize, stb__ftell, stb__fbackpatch, stb__fclose, + 0,0,0, }; + stbfile *z = (stbfile *) malloc(sizeof(*z)); + if (z) { + *z = m; + z->f = f; + } + return z; +} + +static int stb__nogetbyte(stbfile *f) { assert(0); return -1; } +static unsigned int stb__nogetdata(stbfile *f, void *buffer, unsigned int len) { assert(0); return 0; } +static int stb__noputbyte(stbfile *f, int ch) { assert(0); return 0; } +static unsigned int stb__noputdata(stbfile *f, void *buffer, unsigned int len) { assert(0); return 0; } +static void stb__nobackpatch(stbfile *f, unsigned int where, void *buffer, unsigned int len) { assert(0); } + +static int stb__bgetbyte(stbfile *s) +{ + if (s->indata < s->inend) + return *s->indata++; + else + return -1; +} + +static unsigned int stb__bgetdata(stbfile *s, void *buffer, unsigned int len) +{ + if (s->indata + len > s->inend) + len = s->inend - s->indata; + memcpy(buffer, s->indata, len); + s->indata += len; + return len; +} +static unsigned int stb__bsize(stbfile *s) { return s->inend - s->buffer; } +static unsigned int stb__btell(stbfile *s) { return s->indata - s->buffer; } + +static void stb__bclose(stbfile *s) +{ + if (s->various) + free(s->buffer); +} + +stbfile *stb_open_inbuffer(void *buffer, unsigned int len) +{ + stbfile m = { stb__bgetbyte, stb__bgetdata, + stb__noputbyte, stb__noputdata, + stb__bsize, stb__btell, stb__nobackpatch, stb__bclose }; + stbfile *z = (stbfile *) malloc(sizeof(*z)); + if (z) { + *z = m; + z->buffer = (unsigned char *) buffer; + z->indata = z->buffer; + z->inend = z->indata + len; + } + return z; +} + +stbfile *stb_open_inbuffer_free(void *buffer, unsigned int len) +{ + stbfile *z = stb_open_inbuffer(buffer, len); + if (z) + z->various = 1; // free + return z; +} + +#ifndef STB_VERSION +// if we've been cut-and-pasted elsewhere, you get a limited +// version of stb_open, without the 'k' flag and utf8 support +static void stb__fclose2(stbfile *f) +{ + fclose(f->f); +} + +stbfile *stb_open(char *filename, char *mode) +{ + FILE *f = fopen(filename, mode); + stbfile *s; + if (f == NULL) return NULL; + s = stb_openf(f); + if (s) + s->close = stb__fclose2; + return s; +} +#else +// the full version depends on some code in stb.h; this +// also includes the memory buffer output format implemented with stb_arr +static void stb__fclose2(stbfile *f) +{ + stb_fclose(f->f, f->various); +} + +stbfile *stb_open(char *filename, char *mode) +{ + FILE *f = stb_fopen(filename, mode[0] == 'k' ? mode+1 : mode); + stbfile *s; + if (f == NULL) return NULL; + s = stb_openf(f); + if (s) { + s->close = stb__fclose2; + s->various = mode[0] == 'k' ? stb_keep_if_different : stb_keep_yes; + } + return s; +} + +static int stb__aputbyte(stbfile *f, int ch) +{ + stb_arr_push(f->buffer, ch); + return 1; +} +static unsigned int stb__aputdata(stbfile *f, void *data, unsigned int len) +{ + memcpy(stb_arr_addn(f->buffer, (int) len), data, len); + return len; +} +static unsigned int stb__asize(stbfile *f) { return stb_arr_len(f->buffer); } +static void stb__abackpatch(stbfile *f, unsigned int where, void *data, unsigned int len) +{ + memcpy(f->buffer+where, data, len); +} +static void stb__aclose(stbfile *f) +{ + *(unsigned char **) f->ptr = f->buffer; +} + +stbfile *stb_open_outbuffer(unsigned char **update_on_close) +{ + stbfile m = { stb__nogetbyte, stb__nogetdata, + stb__aputbyte, stb__aputdata, + stb__asize, stb__asize, stb__abackpatch, stb__aclose }; + stbfile *z = (stbfile *) malloc(sizeof(*z)); + if (z) { + z->ptr = update_on_close; + *z = m; + } + return z; +} +#endif +#endif + + +////////////////////////////////////////////////////////////////////////////// +// +// Arithmetic coder... based on cbloom's notes on the subject, should be +// less code than a huffman code. + +typedef struct +{ + unsigned int range_low; + unsigned int range_high; + unsigned int code, range; // decode + int buffered_u8; + int pending_ffs; + stbfile *output; +} stb_arith; + +STB_EXTERN void stb_arith_init_encode(stb_arith *a, stbfile *out); +STB_EXTERN void stb_arith_init_decode(stb_arith *a, stbfile *in); +STB_EXTERN stbfile *stb_arith_encode_close(stb_arith *a); +STB_EXTERN stbfile *stb_arith_decode_close(stb_arith *a); + +STB_EXTERN void stb_arith_encode(stb_arith *a, unsigned int totalfreq, unsigned int freq, unsigned int cumfreq); +STB_EXTERN void stb_arith_encode_log2(stb_arith *a, unsigned int totalfreq2, unsigned int freq, unsigned int cumfreq); +STB_EXTERN unsigned int stb_arith_decode_value(stb_arith *a, unsigned int totalfreq); +STB_EXTERN void stb_arith_decode_advance(stb_arith *a, unsigned int totalfreq, unsigned int freq, unsigned int cumfreq); +STB_EXTERN unsigned int stb_arith_decode_value_log2(stb_arith *a, unsigned int totalfreq2); +STB_EXTERN void stb_arith_decode_advance_log2(stb_arith *a, unsigned int totalfreq2, unsigned int freq, unsigned int cumfreq); + +STB_EXTERN void stb_arith_encode_byte(stb_arith *a, int byte); +STB_EXTERN int stb_arith_decode_byte(stb_arith *a); + +// this is a memory-inefficient way of doing things, but it's +// fast(?) and simple +typedef struct +{ + unsigned short cumfreq; + unsigned short samples; +} stb_arith_symstate_item; + +typedef struct +{ + int num_sym; + unsigned int pow2; + int countdown; + stb_arith_symstate_item data[1]; +} stb_arith_symstate; + +#ifdef STB_DEFINE +void stb_arith_init_encode(stb_arith *a, stbfile *out) +{ + a->range_low = 0; + a->range_high = 0xffffffff; + a->pending_ffs = -1; // means no buffered character currently, to speed up normal case + a->output = out; +} + +static void stb__arith_carry(stb_arith *a) +{ + int i; + assert(a->pending_ffs != -1); // can't carry with no data + stb_putc(a->output, a->buffered_u8); + for (i=0; i < a->pending_ffs; ++i) + stb_putc(a->output, 0); +} + +static void stb__arith_putbyte(stb_arith *a, int byte) +{ + if (a->pending_ffs) { + if (a->pending_ffs == -1) { // means no buffered data; encoded for fast path efficiency + if (byte == 0xff) + stb_putc(a->output, byte); // just write it immediately + else { + a->buffered_u8 = byte; + a->pending_ffs = 0; + } + } else if (byte == 0xff) { + ++a->pending_ffs; + } else { + int i; + stb_putc(a->output, a->buffered_u8); + for (i=0; i < a->pending_ffs; ++i) + stb_putc(a->output, 0xff); + } + } else if (byte == 0xff) { + ++a->pending_ffs; + } else { + // fast path + stb_putc(a->output, a->buffered_u8); + a->buffered_u8 = byte; + } +} + +static void stb__arith_flush(stb_arith *a) +{ + if (a->pending_ffs >= 0) { + int i; + stb_putc(a->output, a->buffered_u8); + for (i=0; i < a->pending_ffs; ++i) + stb_putc(a->output, 0xff); + } +} + +static void stb__renorm_encoder(stb_arith *a) +{ + stb__arith_putbyte(a, a->range_low >> 24); + a->range_low <<= 8; + a->range_high = (a->range_high << 8) | 0xff; +} + +static void stb__renorm_decoder(stb_arith *a) +{ + int c = stb_getc(a->output); + a->code = (a->code << 8) + (c >= 0 ? c : 0); // if EOF, insert 0 +} + +void stb_arith_encode(stb_arith *a, unsigned int totalfreq, unsigned int freq, unsigned int cumfreq) +{ + unsigned int range = a->range_high - a->range_low; + unsigned int old = a->range_low; + range /= totalfreq; + a->range_low += range * cumfreq; + a->range_high = a->range_low + range*freq; + if (a->range_low < old) + stb__arith_carry(a); + while (a->range_high - a->range_low < 0x1000000) + stb__renorm_encoder(a); +} + +void stb_arith_encode_log2(stb_arith *a, unsigned int totalfreq2, unsigned int freq, unsigned int cumfreq) +{ + unsigned int range = a->range_high - a->range_low; + unsigned int old = a->range_low; + range >>= totalfreq2; + a->range_low += range * cumfreq; + a->range_high = a->range_low + range*freq; + if (a->range_low < old) + stb__arith_carry(a); + while (a->range_high - a->range_low < 0x1000000) + stb__renorm_encoder(a); +} + +unsigned int stb_arith_decode_value(stb_arith *a, unsigned int totalfreq) +{ + unsigned int freqsize = a->range / totalfreq; + unsigned int z = a->code / freqsize; + return z >= totalfreq ? totalfreq-1 : z; +} + +void stb_arith_decode_advance(stb_arith *a, unsigned int totalfreq, unsigned int freq, unsigned int cumfreq) +{ + unsigned int freqsize = a->range / totalfreq; // @OPTIMIZE, share with above divide somehow? + a->code -= freqsize * cumfreq; + a->range = freqsize * freq; + while (a->range < 0x1000000) + stb__renorm_decoder(a); +} + +unsigned int stb_arith_decode_value_log2(stb_arith *a, unsigned int totalfreq2) +{ + unsigned int freqsize = a->range >> totalfreq2; + unsigned int z = a->code / freqsize; + return z >= (1U<range >> totalfreq2; + a->code -= freqsize * cumfreq; + a->range = freqsize * freq; + while (a->range < 0x1000000) + stb__renorm_decoder(a); +} + +stbfile *stb_arith_encode_close(stb_arith *a) +{ + // put exactly as many bytes as we'll read, so we can turn on/off arithmetic coding in a stream + stb__arith_putbyte(a, a->range_low >> 24); + stb__arith_putbyte(a, a->range_low >> 16); + stb__arith_putbyte(a, a->range_low >> 8); + stb__arith_putbyte(a, a->range_low >> 0); + stb__arith_flush(a); + return a->output; +} + +stbfile *stb_arith_decode_close(stb_arith *a) +{ + return a->output; +} + +// this is a simple power-of-two based model -- using +// power of two means we need one divide per decode, +// not two. +#define POW2_LIMIT 12 +stb_arith_symstate *stb_arith_state_create(int num_sym) +{ + stb_arith_symstate *s = (stb_arith_symstate *) malloc(sizeof(*s) + (num_sym-1) * sizeof(s->data[0])); + if (s) { + int i, cf, cf_next, next; + int start_freq, extra; + s->num_sym = num_sym; + s->pow2 = 4; + while (s->pow2 < 15 && (1 << s->pow2) < 3*num_sym) { + ++s->pow2; + } + start_freq = (1 << s->pow2) / num_sym; + assert(start_freq >= 1); + extra = (1 << s->pow2) % num_sym; + // now set up the initial stats + + if (s->pow2 < POW2_LIMIT) + next = 0; + else + next = 1; + + cf = cf_next = 0; + for (i=0; i < extra; ++i) { + s->data[i].cumfreq = cf; + s->data[i].samples = next; + cf += start_freq+1; + cf_next += next; + } + for (; i < num_sym; ++i) { + s->data[i].cumfreq = cf; + s->data[i].samples = next; + cf += start_freq; + cf_next += next; + } + assert(cf == (1 << s->pow2)); + // now, how long should we go until we have 2 << s->pow2 samples? + s->countdown = (2 << s->pow2) - cf - cf_next; + } + return s; +} + +static void stb_arith_state_rescale(stb_arith_symstate *s) +{ + if (s->pow2 < POW2_LIMIT) { + int pcf, cf, cf_next, next, i; + ++s->pow2; + if (s->pow2 < POW2_LIMIT) + next = 0; + else + next = 1; + cf = cf_next = 0; + pcf = 0; + for (i=0; i < s->num_sym; ++i) { + int sample = s->data[i].cumfreq - pcf + s->data[i].samples; + s->data[i].cumfreq = cf; + cf += sample; + s->data[i].samples = next; + cf_next += next; + } + assert(cf == (1 << s->pow2)); + s->countdown = (2 << s->pow2) - cf - cf_next; + } else { + int pcf, cf, cf_next, i; + cf = cf_next = 0; + pcf = 0; + for (i=0; i < s->num_sym; ++i) { + int sample = (s->data[i].cumfreq - pcf + s->data[i].samples) >> 1; + s->data[i].cumfreq = cf; + cf += sample; + s->data[i].samples = 1; + cf_next += 1; + } + assert(cf == (1 << s->pow2)); // this isn't necessarily true, due to rounding down! + s->countdown = (2 << s->pow2) - cf - cf_next; + } +} + +void stb_arith_encode_byte(stb_arith *a, int byte) +{ +} + +int stb_arith_decode_byte(stb_arith *a) +{ + return -1; +} +#endif + +////////////////////////////////////////////////////////////////////////////// +// +// Threads +// + +#ifndef _WIN32 +#ifdef STB_THREADS +#error "threads not implemented except for Windows" +#endif +#endif + +// call this function to free any global variables for memory testing +STB_EXTERN void stb_thread_cleanup(void); + +typedef void * (*stb_thread_func)(void *); + +// do not rely on these types, this is an implementation detail. +// compare against STB_THREAD_NULL and ST_SEMAPHORE_NULL +typedef void *stb_thread; +typedef void *stb_semaphore; +typedef void *stb_mutex; +typedef struct stb__sync *stb_sync; + +#define STB_SEMAPHORE_NULL NULL +#define STB_THREAD_NULL NULL +#define STB_MUTEX_NULL NULL +#define STB_SYNC_NULL NULL + +// get the number of processors (limited to those in the affinity mask for this process). +STB_EXTERN int stb_processor_count(void); +// force to run on a single core -- needed for RDTSC to work, e.g. for iprof +STB_EXTERN void stb_force_uniprocessor(void); + +// stb_work functions: queue up work to be done by some worker threads + +// set number of threads to serve the queue; you can change this on the fly, +// but if you decrease it, it won't decrease until things currently on the +// queue are finished +STB_EXTERN void stb_work_numthreads(int n); +// set maximum number of units in the queue; you can only set this BEFORE running any work functions +STB_EXTERN int stb_work_maxunits(int n); +// enqueue some work to be done (can do this from any thread, or even from a piece of work); +// return value of f is stored in *return_code if non-NULL +STB_EXTERN int stb_work(stb_thread_func f, void *d, volatile void **return_code); +// as above, but stb_sync_reach is called on 'rel' after work is complete +STB_EXTERN int stb_work_reach(stb_thread_func f, void *d, volatile void **return_code, stb_sync rel); + + +// necessary to call this when using volatile to order writes/reads +STB_EXTERN void stb_barrier(void); + +// support for independent queues with their own threads + +typedef struct stb__workqueue stb_workqueue; + +STB_EXTERN stb_workqueue*stb_workq_new(int numthreads, int max_units); +STB_EXTERN stb_workqueue*stb_workq_new_flags(int numthreads, int max_units, int no_add_mutex, int no_remove_mutex); +STB_EXTERN void stb_workq_delete(stb_workqueue *q); +STB_EXTERN void stb_workq_numthreads(stb_workqueue *q, int n); +STB_EXTERN int stb_workq(stb_workqueue *q, stb_thread_func f, void *d, volatile void **return_code); +STB_EXTERN int stb_workq_reach(stb_workqueue *q, stb_thread_func f, void *d, volatile void **return_code, stb_sync rel); +STB_EXTERN int stb_workq_length(stb_workqueue *q); + +STB_EXTERN stb_thread stb_create_thread (stb_thread_func f, void *d); +STB_EXTERN stb_thread stb_create_thread2(stb_thread_func f, void *d, volatile void **return_code, stb_semaphore rel); +STB_EXTERN void stb_destroy_thread(stb_thread t); + +STB_EXTERN stb_semaphore stb_sem_new(int max_val); +STB_EXTERN stb_semaphore stb_sem_new_extra(int max_val, int start_val); +STB_EXTERN void stb_sem_delete (stb_semaphore s); +STB_EXTERN void stb_sem_waitfor(stb_semaphore s); +STB_EXTERN void stb_sem_release(stb_semaphore s); + +STB_EXTERN stb_mutex stb_mutex_new(void); +STB_EXTERN void stb_mutex_delete(stb_mutex m); +STB_EXTERN void stb_mutex_begin(stb_mutex m); +STB_EXTERN void stb_mutex_end(stb_mutex m); + +STB_EXTERN stb_sync stb_sync_new(void); +STB_EXTERN void stb_sync_delete(stb_sync s); +STB_EXTERN int stb_sync_set_target(stb_sync s, int count); +STB_EXTERN void stb_sync_reach_and_wait(stb_sync s); // wait for 'target' reachers +STB_EXTERN int stb_sync_reach(stb_sync s); + +typedef struct stb__threadqueue stb_threadqueue; +#define STB_THREADQ_DYNAMIC 0 +STB_EXTERN stb_threadqueue *stb_threadq_new(int item_size, int num_items, int many_add, int many_remove); +STB_EXTERN void stb_threadq_delete(stb_threadqueue *tq); +STB_EXTERN int stb_threadq_get(stb_threadqueue *tq, void *output); +STB_EXTERN void stb_threadq_get_block(stb_threadqueue *tq, void *output); +STB_EXTERN int stb_threadq_add(stb_threadqueue *tq, void *input); +// can return FALSE if STB_THREADQ_DYNAMIC and attempt to grow fails +STB_EXTERN int stb_threadq_add_block(stb_threadqueue *tq, void *input); + +#ifdef STB_THREADS +#ifdef STB_DEFINE + +typedef struct +{ + stb_thread_func f; + void *d; + volatile void **return_val; + stb_semaphore sem; +} stb__thread; + +// this is initialized along all possible paths to create threads, therefore +// it's always initialized before any other threads are create, therefore +// it's free of races AS LONG AS you only create threads through stb_* +static stb_mutex stb__threadmutex, stb__workmutex; + +static void stb__threadmutex_init(void) +{ + if (stb__threadmutex == STB_SEMAPHORE_NULL) { + stb__threadmutex = stb_mutex_new(); + stb__workmutex = stb_mutex_new(); + } +} + +#ifdef STB_THREAD_TEST +volatile float stb__t1=1, stb__t2; + +static void stb__wait(int n) +{ + float z = 0; + int i; + for (i=0; i < n; ++i) + z += 1 / (stb__t1+i); + stb__t2 = z; +} +#else +#define stb__wait(x) +#endif + +#ifdef _WIN32 + +// avoid including windows.h -- note that our definitions aren't +// exactly the same (we don't define the security descriptor struct) +// so if you want to include windows.h, make sure you do it first. +#include + +#ifndef _WINDOWS_ // check windows.h guard +#define STB__IMPORT STB_EXTERN __declspec(dllimport) +#define STB__DW unsigned long + +STB__IMPORT int __stdcall TerminateThread(void *, STB__DW); +STB__IMPORT void * __stdcall CreateSemaphoreA(void *sec, long,long,char*); +STB__IMPORT int __stdcall CloseHandle(void *); +STB__IMPORT STB__DW __stdcall WaitForSingleObject(void *, STB__DW); +STB__IMPORT int __stdcall ReleaseSemaphore(void *, long, long *); +STB__IMPORT void __stdcall Sleep(STB__DW); +#endif + +// necessary to call this when using volatile to order writes/reads +void stb_barrier(void) +{ + #ifdef MemoryBarrier + MemoryBarrier(); + #else + long temp; + __asm xchg temp,eax; + #endif +} + +static void stb__thread_run(void *t) +{ + void *res; + stb__thread info = * (stb__thread *) t; + free(t); + res = info.f(info.d); + if (info.return_val) + *info.return_val = res; + if (info.sem != STB_SEMAPHORE_NULL) + stb_sem_release(info.sem); +} + +static stb_thread stb_create_thread_raw(stb_thread_func f, void *d, volatile void **return_code, stb_semaphore rel) +{ +#ifdef _MT +#if defined(STB_FASTMALLOC) && !defined(STB_FASTMALLOC_ITS_OKAY_I_ONLY_MALLOC_IN_ONE_THREAD) + stb_fatal("Error! Cannot use STB_FASTMALLOC with threads.\n"); + return STB_THREAD_NULL; +#else + unsigned long id; + stb__thread *data = (stb__thread *) malloc(sizeof(*data)); + if (!data) return NULL; + stb__threadmutex_init(); + data->f = f; + data->d = d; + data->return_val = return_code; + data->sem = rel; + id = _beginthread(stb__thread_run, 0, data); + if (id == -1) return NULL; + return (void *) id; +#endif +#else +#ifdef STB_NO_STB_STRINGS + stb_fatal("Invalid compilation"); +#else + stb_fatal("Must compile mult-threaded to use stb_thread/stb_work."); +#endif + return NULL; +#endif +} + +// trivial win32 wrappers +void stb_destroy_thread(stb_thread t) { TerminateThread(t,0); } +stb_semaphore stb_sem_new(int maxv) {return CreateSemaphoreA(NULL,0,maxv,NULL); } +stb_semaphore stb_sem_new_extra(int maxv,int start){return CreateSemaphoreA(NULL,start,maxv,NULL); } +void stb_sem_delete(stb_semaphore s) { if (s != NULL) CloseHandle(s); } +void stb_sem_waitfor(stb_semaphore s) { WaitForSingleObject(s, 0xffffffff); } // INFINITE +void stb_sem_release(stb_semaphore s) { ReleaseSemaphore(s,1,NULL); } +static void stb__thread_sleep(int ms) { Sleep(ms); } + +#ifndef _WINDOWS_ +STB__IMPORT int __stdcall GetProcessAffinityMask(void *, STB__DW *, STB__DW *); +STB__IMPORT void * __stdcall GetCurrentProcess(void); +STB__IMPORT int __stdcall SetProcessAffinityMask(void *, STB__DW); +#endif + +int stb_processor_count(void) +{ + unsigned long proc,sys; + GetProcessAffinityMask(GetCurrentProcess(), &proc, &sys); + return stb_bitcount(proc); +} + +void stb_force_uniprocessor(void) +{ + unsigned long proc,sys; + GetProcessAffinityMask(GetCurrentProcess(), &proc, &sys); + if (stb_bitcount(proc) > 1) { + int z; + for (z=0; z < 32; ++z) + if (proc & (1 << z)) + break; + if (z < 32) { + proc = 1 << z; + SetProcessAffinityMask(GetCurrentProcess(), proc); + } + } +} + +#ifdef _WINDOWS_ +#define STB_MUTEX_NATIVE +void *stb_mutex_new(void) +{ + CRITICAL_SECTION *p = (CRITICAL_SECTION *) malloc(sizeof(*p)); + if (p) +#if _WIN32_WINNT >= 0x0500 + InitializeCriticalSectionAndSpinCount(p, 500); +#else + InitializeCriticalSection(p); +#endif + return p; +} + +void stb_mutex_delete(void *p) +{ + if (p) { + DeleteCriticalSection((CRITICAL_SECTION *) p); + free(p); + } +} + +void stb_mutex_begin(void *p) +{ + stb__wait(500); + if (p) + EnterCriticalSection((CRITICAL_SECTION *) p); +} + +void stb_mutex_end(void *p) +{ + if (p) + LeaveCriticalSection((CRITICAL_SECTION *) p); + stb__wait(500); +} +#endif // _WINDOWS_ + +#if 0 +// for future reference, +// InterlockedCompareExchange for x86: + int cas64_mp(void * dest, void * xcmp, void * xxchg) { + __asm + { + mov esi, [xxchg] ; exchange + mov ebx, [esi + 0] + mov ecx, [esi + 4] + + mov esi, [xcmp] ; comparand + mov eax, [esi + 0] + mov edx, [esi + 4] + + mov edi, [dest] ; destination + lock cmpxchg8b [edi] + jz yyyy; + + mov [esi + 0], eax; + mov [esi + 4], edx; + +yyyy: + xor eax, eax; + setz al; + }; + +inline unsigned __int64 _InterlockedCompareExchange64(volatile unsigned __int64 *dest + ,unsigned __int64 exchange + ,unsigned __int64 comperand) +{ + //value returned in eax::edx + __asm { + lea esi,comperand; + lea edi,exchange; + + mov eax,[esi]; + mov edx,4[esi]; + mov ebx,[edi]; + mov ecx,4[edi]; + mov esi,dest; + lock CMPXCHG8B [esi]; + } +#endif // #if 0 + +#endif // _WIN32 + +stb_thread stb_create_thread2(stb_thread_func f, void *d, volatile void **return_code, stb_semaphore rel) +{ + return stb_create_thread_raw(f,d,return_code,rel); +} + +stb_thread stb_create_thread(stb_thread_func f, void *d) +{ + return stb_create_thread2(f,d,NULL,STB_SEMAPHORE_NULL); +} + +// mutex implemented by wrapping semaphore +#ifndef STB_MUTEX_NATIVE +stb_mutex stb_mutex_new(void) { return stb_sem_new_extra(1,1); } +void stb_mutex_delete(stb_mutex m) { stb_sem_delete (m); } +void stb_mutex_begin(stb_mutex m) { stb__wait(500); if (m) stb_sem_waitfor(m); } +void stb_mutex_end(stb_mutex m) { if (m) stb_sem_release(m); stb__wait(500); } +#endif + +// thread merge operation +struct stb__sync +{ + int target; // target number of threads to hit it + int sofar; // total threads that hit it + int waiting; // total threads waiting + + stb_mutex start; // mutex to prevent starting again before finishing previous + stb_mutex mutex; // mutex while tweaking state + stb_semaphore release; // semaphore wake up waiting threads + // we have to wake them up one at a time, rather than using a single release + // call, because win32 semaphores don't let you dynamically change the max count! +}; + +stb_sync stb_sync_new(void) +{ + stb_sync s = (stb_sync) malloc(sizeof(*s)); + if (!s) return s; + + s->target = s->sofar = s->waiting = 0; + s->mutex = stb_mutex_new(); + s->start = stb_mutex_new(); + s->release = stb_sem_new(1); + if (s->mutex == STB_MUTEX_NULL || s->release == STB_SEMAPHORE_NULL || s->start == STB_MUTEX_NULL) { + stb_mutex_delete(s->mutex); + stb_mutex_delete(s->mutex); + stb_sem_delete(s->release); + free(s); + return NULL; + } + return s; +} + +void stb_sync_delete(stb_sync s) +{ + if (s->waiting) { + // it's bad to delete while there are threads waiting! + // shall we wait for them to reach, or just bail? just bail + assert(0); + } + stb_mutex_delete(s->mutex); + stb_mutex_delete(s->release); + free(s); +} + +int stb_sync_set_target(stb_sync s, int count) +{ + // don't allow setting a target until the last one is fully released; + // note that this can lead to inefficient pipelining, and maybe we'd + // be better off ping-ponging between two internal syncs? + // I tried seeing how often this happened using TryEnterCriticalSection + // and could _never_ get it to happen in imv(stb), even with more threads + // than processors. So who knows! + stb_mutex_begin(s->start); + + // this mutex is pointless, since it's not valid for threads + // to call reach() before anyone calls set_target() anyway + stb_mutex_begin(s->mutex); + + assert(s->target == 0); // enforced by start mutex + s->target = count; + s->sofar = 0; + s->waiting = 0; + stb_mutex_end(s->mutex); + return STB_TRUE; +} + +void stb__sync_release(stb_sync s) +{ + if (s->waiting) + stb_sem_release(s->release); + else { + s->target = 0; + stb_mutex_end(s->start); + } +} + +int stb_sync_reach(stb_sync s) +{ + int n; + stb_mutex_begin(s->mutex); + assert(s->sofar < s->target); + n = ++s->sofar; // record this value to avoid possible race if we did 'return s->sofar'; + if (s->sofar == s->target) + stb__sync_release(s); + stb_mutex_end(s->mutex); + return n; +} + +void stb_sync_reach_and_wait(stb_sync s) +{ + stb_mutex_begin(s->mutex); + assert(s->sofar < s->target); + ++s->sofar; + if (s->sofar == s->target) { + stb__sync_release(s); + stb_mutex_end(s->mutex); + } else { + ++s->waiting; // we're waiting, so one more waiter + stb_mutex_end(s->mutex); // release the mutex to other threads + + stb_sem_waitfor(s->release); // wait for merge completion + + stb_mutex_begin(s->mutex); // on merge completion, grab the mutex + --s->waiting; // we're done waiting + stb__sync_release(s); // restart the next waiter + stb_mutex_end(s->mutex); // and now we're done + // this ends the same as the first case, but it's a lot + // clearer to understand without sharing the code + } +} + +struct stb__threadqueue +{ + stb_mutex add, remove; + stb_semaphore nonempty, nonfull; + int head_blockers; // number of threads blocking--used to know whether to release(avail) + int tail_blockers; + int head, tail, array_size, growable; + int item_size; + char *data; +}; + +static int stb__tq_wrap(volatile stb_threadqueue *z, int p) +{ + if (p == z->array_size) + return p - z->array_size; + else + return p; +} + +int stb__threadq_get_raw(stb_threadqueue *tq2, void *output, int block) +{ + volatile stb_threadqueue *tq = (volatile stb_threadqueue *) tq2; + if (tq->head == tq->tail && !block) return 0; + + stb_mutex_begin(tq->remove); + + while (tq->head == tq->tail) { + if (!block) { + stb_mutex_end(tq->remove); + return 0; + } + ++tq->head_blockers; + stb_mutex_end(tq->remove); + + stb_sem_waitfor(tq->nonempty); + + stb_mutex_begin(tq->remove); + --tq->head_blockers; + } + + memcpy(output, tq->data + tq->head*tq->item_size, tq->item_size); + stb_barrier(); + tq->head = stb__tq_wrap(tq, tq->head+1); + + stb_sem_release(tq->nonfull); + if (tq->head_blockers) // can't check if actually non-empty due to race? + stb_sem_release(tq->nonempty); // if there are other blockers, wake one + + stb_mutex_end(tq->remove); + return STB_TRUE; +} + +int stb__threadq_grow(volatile stb_threadqueue *tq) +{ + int n; + char *p; + assert(tq->remove != STB_MUTEX_NULL); // must have this to allow growth! + stb_mutex_begin(tq->remove); + + n = tq->array_size * 2; + p = (char *) realloc(tq->data, n * tq->item_size); + if (p == NULL) { + stb_mutex_end(tq->remove); + stb_mutex_end(tq->add); + return STB_FALSE; + } + if (tq->tail < tq->head) { + memcpy(p + tq->array_size * tq->item_size, p, tq->tail * tq->item_size); + tq->tail += tq->array_size; + } + tq->data = p; + tq->array_size = n; + + stb_mutex_end(tq->remove); + return STB_TRUE; +} + +int stb__threadq_add_raw(stb_threadqueue *tq2, void *input, int block) +{ + int tail,pos; + volatile stb_threadqueue *tq = (volatile stb_threadqueue *) tq2; + stb_mutex_begin(tq->add); + for(;;) { + pos = tq->tail; + tail = stb__tq_wrap(tq, pos+1); + if (tail != tq->head) break; + + // full + if (tq->growable) { + if (!stb__threadq_grow(tq)) { + stb_mutex_end(tq->add); + return STB_FALSE; // out of memory + } + } else if (!block) { + stb_mutex_end(tq->add); + return STB_FALSE; + } else { + ++tq->tail_blockers; + stb_mutex_end(tq->add); + + stb_sem_waitfor(tq->nonfull); + + stb_mutex_begin(tq->add); + --tq->tail_blockers; + } + } + memcpy(tq->data + tq->item_size * pos, input, tq->item_size); + stb_barrier(); + tq->tail = tail; + stb_sem_release(tq->nonempty); + if (tq->tail_blockers) // can't check if actually non-full due to race? + stb_sem_release(tq->nonfull); + stb_mutex_end(tq->add); + return STB_TRUE; +} + +int stb_threadq_length(stb_threadqueue *tq2) +{ + int a,b,n; + volatile stb_threadqueue *tq = (volatile stb_threadqueue *) tq2; + stb_mutex_begin(tq->add); + a = tq->head; + b = tq->tail; + n = tq->array_size; + stb_mutex_end(tq->add); + if (a > b) b += n; + return b-a; +} + +int stb_threadq_get(stb_threadqueue *tq, void *output) +{ + return stb__threadq_get_raw(tq, output, STB_FALSE); +} + +void stb_threadq_get_block(stb_threadqueue *tq, void *output) +{ + stb__threadq_get_raw(tq, output, STB_TRUE); +} + +int stb_threadq_add(stb_threadqueue *tq, void *input) +{ + return stb__threadq_add_raw(tq, input, STB_FALSE); +} + +int stb_threadq_add_block(stb_threadqueue *tq, void *input) +{ + return stb__threadq_add_raw(tq, input, STB_TRUE); +} + +void stb_threadq_delete(stb_threadqueue *tq) +{ + if (tq) { + free(tq->data); + stb_mutex_delete(tq->add); + stb_mutex_delete(tq->remove); + stb_sem_delete(tq->nonempty); + stb_sem_delete(tq->nonfull); + free(tq); + } +} + +#define STB_THREADQUEUE_DYNAMIC 0 +stb_threadqueue *stb_threadq_new(int item_size, int num_items, int many_add, int many_remove) +{ + int error=0; + stb_threadqueue *tq = (stb_threadqueue *) malloc(sizeof(*tq)); + if (tq == NULL) return NULL; + + if (num_items == STB_THREADQUEUE_DYNAMIC) { + tq->growable = STB_TRUE; + num_items = 32; + } else + tq->growable = STB_FALSE; + + tq->item_size = item_size; + tq->array_size = num_items+1; + + tq->add = tq->remove = STB_MUTEX_NULL; + tq->nonempty = tq->nonfull = STB_SEMAPHORE_NULL; + tq->data = NULL; + if (many_add) + { tq->add = stb_mutex_new(); if (tq->add == STB_MUTEX_NULL) goto error; } + if (many_remove || tq->growable) + { tq->remove = stb_mutex_new(); if (tq->remove == STB_MUTEX_NULL) goto error; } + tq->nonempty = stb_sem_new(1); if (tq->nonempty == STB_SEMAPHORE_NULL) goto error; + tq->nonfull = stb_sem_new(1); if (tq->nonfull == STB_SEMAPHORE_NULL) goto error; + tq->data = (char *) malloc(tq->item_size * tq->array_size); + if (tq->data == NULL) goto error; + + tq->head = tq->tail = 0; + tq->head_blockers = tq->tail_blockers = 0; + + return tq; + +error: + stb_threadq_delete(tq); + return NULL; +} + +typedef struct +{ + stb_thread_func f; + void *d; + volatile void **retval; + stb_sync sync; +} stb__workinfo; + +//static volatile stb__workinfo *stb__work; + +struct stb__workqueue +{ + int numthreads; + stb_threadqueue *tq; +}; + +static stb_workqueue *stb__work_global; + +static void *stb__thread_workloop(void *p) +{ + volatile stb_workqueue *q = (volatile stb_workqueue *) p; + for(;;) { + void *z; + stb__workinfo w; + stb_threadq_get_block(q->tq, &w); + if (w.f == NULL) // null work is a signal to end the thread + return NULL; + z = w.f(w.d); + if (w.retval) { stb_barrier(); *w.retval = z; } + if (w.sync != STB_SYNC_NULL) stb_sync_reach(w.sync); + } +} + +stb_workqueue *stb_workq_new(int num_threads, int max_units) +{ + return stb_workq_new_flags(num_threads, max_units, 0,0); +} + +stb_workqueue *stb_workq_new_flags(int numthreads, int max_units, int no_add_mutex, int no_remove_mutex) +{ + stb_workqueue *q = (stb_workqueue *) malloc(sizeof(*q)); + if (q == NULL) return NULL; + q->tq = stb_threadq_new(sizeof(stb__workinfo), max_units, !no_add_mutex, !no_remove_mutex); + if (q->tq == NULL) { free(q); return NULL; } + q->numthreads = 0; + stb_workq_numthreads(q, numthreads); + return q; +} + +void stb_workq_delete(stb_workqueue *q) +{ + while (stb_workq_length(q) != 0) + stb__thread_sleep(1); + stb_threadq_delete(q->tq); + free(q); +} + +static int stb__work_maxitems = STB_THREADQUEUE_DYNAMIC; + +static void stb_work_init(int num_threads) +{ + if (stb__work_global == NULL) { + stb__threadmutex_init(); + stb_mutex_begin(stb__workmutex); + stb_barrier(); + if (*(stb_workqueue * volatile *) &stb__work_global == NULL) + stb__work_global = stb_workq_new(num_threads, stb__work_maxitems); + stb_mutex_end(stb__workmutex); + } +} + +static int stb__work_raw(stb_workqueue *q, stb_thread_func f, void *d, volatile void **return_code, stb_sync rel) +{ + stb__workinfo w; + if (q == NULL) { + stb_work_init(1); + q = stb__work_global; + } + w.f = f; + w.d = d; + w.retval = return_code; + w.sync = rel; + return stb_threadq_add(q->tq, &w); +} + +int stb_workq_length(stb_workqueue *q) +{ + return stb_threadq_length(q->tq); +} + +int stb_workq(stb_workqueue *q, stb_thread_func f, void *d, volatile void **return_code) +{ + if (f == NULL) return 0; + return stb_workq_reach(q, f, d, return_code, NULL); +} + +int stb_workq_reach(stb_workqueue *q, stb_thread_func f, void *d, volatile void **return_code, stb_sync rel) +{ + if (f == NULL) return 0; + return stb__work_raw(q, f, d, return_code, rel); +} + +static void stb__workq_numthreads(stb_workqueue *q, int n) +{ + while (q->numthreads < n) { + stb_create_thread(stb__thread_workloop, q); + ++q->numthreads; + } + while (q->numthreads > n) { + stb__work_raw(q, NULL, NULL, NULL, NULL); + --q->numthreads; + } +} + +void stb_workq_numthreads(stb_workqueue *q, int n) +{ + stb_mutex_begin(stb__threadmutex); + stb__workq_numthreads(q,n); + stb_mutex_end(stb__threadmutex); +} + +int stb_work_maxunits(int n) +{ + if (stb__work_global == NULL) { + stb__work_maxitems = n; + stb_work_init(1); + } + return stb__work_maxitems; +} + +int stb_work(stb_thread_func f, void *d, volatile void **return_code) +{ + return stb_workq(stb__work_global, f,d,return_code); +} + +int stb_work_reach(stb_thread_func f, void *d, volatile void **return_code, stb_sync rel) +{ + return stb_workq_reach(stb__work_global, f,d,return_code,rel); +} + +void stb_work_numthreads(int n) +{ + if (stb__work_global == NULL) + stb_work_init(n); + else + stb_workq_numthreads(stb__work_global, n); +} +#endif // STB_DEFINE + + +////////////////////////////////////////////////////////////////////////////// +// +// Background disk I/O +// +// + +#define STB_BGIO_READ_ALL (-1) +STB_EXTERN int stb_bgio_read (char *filename, int offset, int len, stb_uchar **result, int *olen); +STB_EXTERN int stb_bgio_readf (FILE *f , int offset, int len, stb_uchar **result, int *olen); +STB_EXTERN int stb_bgio_read_to (char *filename, int offset, int len, stb_uchar *buffer, int *olen); +STB_EXTERN int stb_bgio_readf_to(FILE *f , int offset, int len, stb_uchar *buffer, int *olen); + +typedef struct +{ + int have_data; + int is_valid; + int is_dir; + time_t filetime; + stb_int64 filesize; +} stb_bgstat; + +STB_EXTERN int stb_bgio_stat (char *filename, stb_bgstat *result); + +#ifdef STB_DEFINE + +static stb_workqueue *stb__diskio; +static stb_mutex stb__diskio_mutex; + +void stb_thread_cleanup(void) +{ + if (stb__work_global) stb_workq_delete(stb__work_global); stb__work_global = NULL; + if (stb__threadmutex) stb_mutex_delete(stb__threadmutex); stb__threadmutex = NULL; + if (stb__workmutex) stb_mutex_delete(stb__workmutex); stb__workmutex = NULL; + if (stb__diskio) stb_workq_delete(stb__diskio); stb__diskio = NULL; + if (stb__diskio_mutex)stb_mutex_delete(stb__diskio_mutex);stb__diskio_mutex= NULL; +} + + +typedef struct +{ + char *filename; + FILE *f; + int offset; + int len; + + stb_bgstat *stat_out; + stb_uchar *output; + stb_uchar **result; + int *len_output; + int *flag; +} stb__disk_command; + +#define STB__MAX_DISK_COMMAND 100 +static stb__disk_command stb__dc_queue[STB__MAX_DISK_COMMAND]; +static int stb__dc_offset; + +void stb__io_init(void) +{ + if (!stb__diskio) { + stb__threadmutex_init(); + stb_mutex_begin(stb__threadmutex); + stb_barrier(); + if (*(stb_thread * volatile *) &stb__diskio == NULL) { + stb__diskio_mutex = stb_mutex_new(); + // use many threads so OS can try to schedule seeks + stb__diskio = stb_workq_new_flags(16,STB__MAX_DISK_COMMAND,STB_FALSE,STB_FALSE); + } + stb_mutex_end(stb__threadmutex); + } +} + +static void * stb__io_error(stb__disk_command *dc) +{ + if (dc->len_output) *dc->len_output = 0; + if (dc->result) *dc->result = NULL; + if (dc->flag) *dc->flag = -1; + return NULL; +} + +static void * stb__io_task(void *p) +{ + stb__disk_command *dc = (stb__disk_command *) p; + int len; + FILE *f; + stb_uchar *buf; + + if (dc->stat_out) { + struct _stati64 s; + if (!_stati64(dc->filename, &s)) { + dc->stat_out->filesize = s.st_size; + dc->stat_out->filetime = s.st_mtime; + dc->stat_out->is_dir = s.st_mode & _S_IFDIR; + dc->stat_out->is_valid = (s.st_mode & _S_IFREG) || dc->stat_out->is_dir; + } else + dc->stat_out->is_valid = 0; + stb_barrier(); + dc->stat_out->have_data = 1; + free(dc->filename); + return 0; + } + if (dc->f) { + #ifdef WIN32 + f = _fdopen(_dup(_fileno(dc->f)), "rb"); + #else + f = fdopen(dup(fileno(dc->f)), "rb"); + #endif + if (!f) + return stb__io_error(dc); + } else { + f = fopen(dc->filename, "rb"); + free(dc->filename); + if (!f) + return stb__io_error(dc); + } + + len = dc->len; + if (len < 0) { + fseek(f, 0, SEEK_END); + len = ftell(f) - dc->offset; + } + + if (fseek(f, dc->offset, SEEK_SET)) { + fclose(f); + return stb__io_error(dc); + } + + if (dc->output) + buf = dc->output; + else { + buf = (stb_uchar *) malloc(len); + if (buf == NULL) { + fclose(f); + return stb__io_error(dc); + } + } + + len = fread(buf, 1, len, f); + fclose(f); + if (dc->len_output) *dc->len_output = len; + if (dc->result) *dc->result = buf; + if (dc->flag) *dc->flag = 1; + + return NULL; +} + +int stb__io_add(char *fname, FILE *f, int off, int len, stb_uchar *out, stb_uchar **result, int *olen, int *flag, stb_bgstat *stat) +{ + int res; + stb__io_init(); + // do memory allocation outside of mutex + if (fname) fname = strdup(fname); + stb_mutex_begin(stb__diskio_mutex); + { + stb__disk_command *dc = &stb__dc_queue[stb__dc_offset]; + dc->filename = fname; + dc->f = f; + dc->offset = off; + dc->len = len; + dc->output = out; + dc->result = result; + dc->len_output = olen; + dc->flag = flag; + dc->stat_out = stat; + res = stb_workq(stb__diskio, stb__io_task, dc, NULL); + if (res) + stb__dc_offset = (stb__dc_offset + 1 == STB__MAX_DISK_COMMAND ? 0 : stb__dc_offset+1); + } + stb_mutex_end(stb__diskio_mutex); + return res; +} + +int stb_bgio_read(char *filename, int offset, int len, stb_uchar **result, int *olen) +{ + return stb__io_add(filename,NULL,offset,len,NULL,result,olen,NULL,NULL); +} + +int stb_bgio_readf(FILE *f, int offset, int len, stb_uchar **result, int *olen) +{ + return stb__io_add(NULL,f,offset,len,NULL,result,olen,NULL,NULL); +} + +int stb_bgio_read_to(char *filename, int offset, int len, stb_uchar *buffer, int *olen) +{ + return stb__io_add(filename,NULL,offset,len,buffer,NULL,olen,NULL,NULL); +} + +int stb_bgio_readf_to(FILE *f, int offset, int len, stb_uchar *buffer, int *olen) +{ + return stb__io_add(NULL,f,offset,len,buffer,NULL,olen,NULL,NULL); +} + +STB_EXTERN int stb_bgio_stat (char *filename, stb_bgstat *result) +{ + result->have_data = 0; + return stb__io_add(filename,NULL,0,0,0,NULL,0,NULL, result); +} +#endif +#endif + + + +////////////////////////////////////////////////////////////////////////////// +// +// Fast malloc implementation +// +// This is a clone of TCMalloc, but without the thread support. +// 1. large objects are allocated directly, page-aligned +// 2. small objects are allocated in homogeonous heaps, 0 overhead +// +// We keep an allocation table for pages a la TCMalloc. This would +// require 4MB for the entire address space, but we only allocate +// the parts that are in use. The overhead from using homogenous heaps +// everywhere is 3MB. (That is, if you allocate 1 object of each size, +// you'll use 3MB.) + +#if defined(STB_DEFINE) && (defined(_WIN32) || defined(STB_FASTMALLOC)) + +#ifdef _WIN32 + #ifndef _WINDOWS_ + #ifndef STB__IMPORT + #define STB__IMPORT STB_EXTERN __declspec(dllimport) + #define STB__DW unsigned long + #endif + STB__IMPORT void * __stdcall VirtualAlloc(void *p, unsigned long size, unsigned long type, unsigned long protect); + STB__IMPORT int __stdcall VirtualFree(void *p, unsigned long size, unsigned long freetype); + #endif + #define stb__alloc_pages_raw(x) (stb_uint32) VirtualAlloc(NULL, (x), 0x3000, 0x04) + #define stb__dealloc_pages_raw(p) VirtualFree((void *) p, 0, 0x8000) +#else + #error "Platform not currently supported" +#endif + +typedef struct stb__span +{ + int start, len; + struct stb__span *next, *prev; + void *first_free; + unsigned short list; // 1..256 free; 257..511 sizeclass; 0=large block + short allocations; // # outstanding allocations for sizeclass +} stb__span; // 24 + +static stb__span **stb__span_for_page; +static int stb__firstpage, stb__lastpage; +static void stb__update_page_range(int first, int last) +{ + stb__span **sfp; + int i, f,l; + if (first >= stb__firstpage && last <= stb__lastpage) return; + if (stb__span_for_page == NULL) { + f = first; + l = f+stb_max(last-f, 16384); + l = stb_min(l, 1<<20); + } else if (last > stb__lastpage) { + f = stb__firstpage; + l = f + (stb__lastpage - f) * 2; + l = stb_clamp(last, l,1<<20); + } else { + l = stb__lastpage; + f = l - (l - stb__firstpage) * 2; + f = stb_clamp(f, 0,first); + } + sfp = (stb__span **) stb__alloc_pages_raw(sizeof(void *) * (l-f)); + for (i=f; i < stb__firstpage; ++i) sfp[i - f] = NULL; + for ( ; i < stb__lastpage ; ++i) sfp[i - f] = stb__span_for_page[i - stb__firstpage]; + for ( ; i < l ; ++i) sfp[i - f] = NULL; + if (stb__span_for_page) stb__dealloc_pages_raw(stb__span_for_page); + stb__firstpage = f; + stb__lastpage = l; + stb__span_for_page = sfp; +} + +static stb__span *stb__span_free=NULL; +static stb__span *stb__span_first, *stb__span_end; +static stb__span *stb__span_alloc(void) +{ + stb__span *s = stb__span_free; + if (s) + stb__span_free = s->next; + else { + if (!stb__span_first) { + stb__span_first = (stb__span *) stb__alloc_pages_raw(65536); + if (stb__span_first == NULL) return NULL; + stb__span_end = stb__span_first + (65536 / sizeof(stb__span)); + } + s = stb__span_first++; + if (stb__span_first == stb__span_end) stb__span_first = NULL; + } + return s; +} + +static stb__span *stb__spanlist[512]; + +static void stb__spanlist_unlink(stb__span *s) +{ + if (s->prev) + s->prev->next = s->next; + else { + int n = s->list; + assert(stb__spanlist[n] == s); + stb__spanlist[n] = s->next; + } + if (s->next) + s->next->prev = s->prev; + s->next = s->prev = NULL; + s->list = 0; +} + +static void stb__spanlist_add(int n, stb__span *s) +{ + s->list = n; + s->next = stb__spanlist[n]; + s->prev = NULL; + stb__spanlist[n] = s; + if (s->next) s->next->prev = s; +} + +#define stb__page_shift 12 +#define stb__page_size (1 << stb__page_shift) +#define stb__page_number(x) ((x) >> stb__page_shift) +#define stb__page_address(x) ((x) << stb__page_shift) + +static void stb__set_span_for_page(stb__span *s) +{ + int i; + for (i=0; i < s->len; ++i) + stb__span_for_page[s->start + i - stb__firstpage] = s; +} + +static stb__span *stb__coalesce(stb__span *a, stb__span *b) +{ + assert(a->start + a->len == b->start); + if (a->list) stb__spanlist_unlink(a); + if (b->list) stb__spanlist_unlink(b); + a->len += b->len; + b->len = 0; + b->next = stb__span_free; + stb__span_free = b; + stb__set_span_for_page(a); + return a; +} + +static void stb__free_span(stb__span *s) +{ + stb__span *n = NULL; + if (s->start > stb__firstpage) { + n = stb__span_for_page[s->start-1 - stb__firstpage]; + if (n && n->allocations == -2 && n->start + n->len == s->start) s = stb__coalesce(n,s); + } + if (s->start + s->len < stb__lastpage) { + n = stb__span_for_page[s->start + s->len - stb__firstpage]; + if (n && n->allocations == -2 && s->start + s->len == n->start) s = stb__coalesce(s,n); + } + s->allocations = -2; + stb__spanlist_add(s->len > 256 ? 256 : s->len, s); +} + +static stb__span *stb__alloc_pages(int num) +{ + stb__span *s = stb__span_alloc(); + int p; + if (!s) return NULL; + p = stb__alloc_pages_raw(num << stb__page_shift); + if (p == 0) { s->next = stb__span_free; stb__span_free = s; return 0; } + assert(stb__page_address(stb__page_number(p)) == p); + p = stb__page_number(p); + stb__update_page_range(p, p+num); + s->start = p; + s->len = num; + s->next = NULL; + s->prev = NULL; + stb__set_span_for_page(s); + return s; +} + +static stb__span *stb__alloc_span(int pagecount) +{ + int i; + stb__span *p = NULL; + for(i=pagecount; i < 256; ++i) + if (stb__spanlist[i]) { + p = stb__spanlist[i]; + break; + } + if (!p) { + p = stb__spanlist[256]; + while (p && p->len < pagecount) + p = p->next; + } + if (!p) { + p = stb__alloc_pages(pagecount < 16 ? 16 : pagecount); + if (p == NULL) return 0; + } else + stb__spanlist_unlink(p); + + if (p->len > pagecount) { + stb__span *q = stb__span_alloc(); + if (q) { + q->start = p->start + pagecount; + q->len = p->len - pagecount; + p->len = pagecount; + for (i=0; i < q->len; ++i) + stb__span_for_page[q->start+i - stb__firstpage] = q; + stb__spanlist_add(q->len > 256 ? 256 : q->len, q); + } + } + return p; +} + +#define STB__MAX_SMALL_SIZE 32768 +#define STB__MAX_SIZE_CLASSES 256 + +static unsigned char stb__class_base[32]; +static unsigned char stb__class_shift[32]; +static unsigned char stb__pages_for_class[STB__MAX_SIZE_CLASSES]; +static int stb__size_for_class[STB__MAX_SIZE_CLASSES]; + +stb__span *stb__get_nonempty_sizeclass(int c) +{ + int s = c + 256, i, size, tsize; // remap to span-list index + char *z; + void *q; + stb__span *p = stb__spanlist[s]; + if (p) { + if (p->first_free) return p; // fast path: it's in the first one in list + for (p=p->next; p; p=p->next) + if (p->first_free) { + // move to front for future queries + stb__spanlist_unlink(p); + stb__spanlist_add(s, p); + return p; + } + } + // no non-empty ones, so allocate a new one + p = stb__alloc_span(stb__pages_for_class[c]); + if (!p) return NULL; + // create the free list up front + size = stb__size_for_class[c]; + tsize = stb__pages_for_class[c] << stb__page_shift; + i = 0; + z = (char *) stb__page_address(p->start); + q = NULL; + while (i + size <= tsize) { + * (void **) z = q; q = z; + z += size; + i += size; + } + p->first_free = q; + p->allocations = 0; + stb__spanlist_add(s,p); + return p; +} + +static int stb__sizeclass(size_t sz) +{ + int z = stb_log2_floor(sz); // -1 below to group e.g. 13,14,15,16 correctly + return stb__class_base[z] + ((sz-1) >> stb__class_shift[z]); +} + +static void stb__init_sizeclass(void) +{ + int i, size, overhead; + int align_shift = 2; // allow 4-byte and 12-byte blocks as well, vs. TCMalloc + int next_class = 1; + int last_log = 0; + + for (i = 0; i < align_shift; i++) { + stb__class_base [i] = next_class; + stb__class_shift[i] = align_shift; + } + + for (size = 1 << align_shift; size <= STB__MAX_SMALL_SIZE; size += 1 << align_shift) { + i = stb_log2_floor(size); + if (i > last_log) { + if (size == 16) ++align_shift; // switch from 4-byte to 8-byte alignment + else if (size >= 128 && align_shift < 8) ++align_shift; + stb__class_base[i] = next_class - ((size-1) >> align_shift); + stb__class_shift[i] = align_shift; + last_log = i; + } + stb__size_for_class[next_class++] = size; + } + + for (i=1; i <= STB__MAX_SMALL_SIZE; ++i) + assert(i <= stb__size_for_class[stb__sizeclass(i)]); + + overhead = 0; + for (i = 1; i < next_class; i++) { + int s = stb__size_for_class[i]; + size = stb__page_size; + while (size % s > size >> 3) + size += stb__page_size; + stb__pages_for_class[i] = (unsigned char) (size >> stb__page_shift); + overhead += size; + } + assert(overhead < (4 << 20)); // make sure it's under 4MB of overhead +} + +#ifdef STB_DEBUG +#define stb__smemset(a,b,c) memset((void *) a, b, c) +#elif defined(STB_FASTMALLOC_INIT) +#define stb__smemset(a,b,c) memset((void *) a, b, c) +#else +#define stb__smemset(a,b,c) +#endif +void *stb_smalloc(size_t sz) +{ + stb__span *s; + if (sz == 0) return NULL; + if (stb__size_for_class[1] == 0) stb__init_sizeclass(); + if (sz > STB__MAX_SMALL_SIZE) { + s = stb__alloc_span((sz + stb__page_size - 1) >> stb__page_shift); + if (s == NULL) return NULL; + s->list = 0; + s->next = s->prev = NULL; + s->allocations = -32767; + stb__smemset(stb__page_address(s->start), 0xcd, (sz+3)&~3); + return (void *) stb__page_address(s->start); + } else { + void *p; + int c = stb__sizeclass(sz); + s = stb__spanlist[256+c]; + if (!s || !s->first_free) + s = stb__get_nonempty_sizeclass(c); + if (s == NULL) return NULL; + p = s->first_free; + s->first_free = * (void **) p; + ++s->allocations; + stb__smemset(p,0xcd, sz); + return p; + } +} + +int stb_ssize(void *p) +{ + stb__span *s; + if (p == NULL) return 0; + s = stb__span_for_page[stb__page_number((stb_uint) p) - stb__firstpage]; + if (s->list >= 256) { + return stb__size_for_class[s->list - 256]; + } else { + assert(s->list == 0); + return s->len << stb__page_shift; + } +} + +void stb_sfree(void *p) +{ + stb__span *s; + if (p == NULL) return; + s = stb__span_for_page[stb__page_number((stb_uint) p) - stb__firstpage]; + if (s->list >= 256) { + stb__smemset(p, 0xfe, stb__size_for_class[s->list-256]); + * (void **) p = s->first_free; + s->first_free = p; + if (--s->allocations == 0) { + stb__spanlist_unlink(s); + stb__free_span(s); + } + } else { + assert(s->list == 0); + stb__smemset(p, 0xfe, stb_ssize(p)); + stb__free_span(s); + } +} + +void *stb_srealloc(void *p, size_t sz) +{ + size_t cur_size; + if (p == NULL) return stb_smalloc(sz); + if (sz == 0) { stb_sfree(p); return NULL; } + cur_size = stb_ssize(p); + if (sz > cur_size || sz <= (cur_size >> 1)) { + void *q; + if (sz > cur_size && sz < (cur_size << 1)) sz = cur_size << 1; + q = stb_smalloc(sz); if (q == NULL) return NULL; + memcpy(q, p, sz < cur_size ? sz : cur_size); + stb_sfree(p); + return q; + } + return p; +} + +void *stb_scalloc(size_t n, size_t sz) +{ + void *p; + if (n == 0 || sz == 0) return NULL; + if (stb_log2_ceil(n) + stb_log2_ceil(n) >= 32) return NULL; + p = stb_smalloc(n*sz); + if (p) memset(p, 0, n*sz); + return p; +} + +char *stb_sstrdup(char *s) +{ + int n = strlen(s); + char *p = (char *) stb_smalloc(n+1); + if (p) strcpy(p,s); + return p; +} +#endif // STB_DEFINE + + + +////////////////////////////////////////////////////////////////////////////// +// +// Source code constants +// +// This is a trivial system to let you specify constants in source code, +// then while running you can change the constants. +// +// Note that you can't wrap the #defines, because we need to know their +// names. So we provide a pre-wrapped version without 'STB_' for convenience; +// to request it, #define STB_CONVENIENT_H, yielding: +// KI -- integer +// KU -- unsigned integer +// KF -- float +// KD -- double +// KS -- string constant +// +// Defaults to functioning in debug build, not in release builds. +// To force on, define STB_ALWAYS_H + +#ifdef STB_CONVENIENT_H +#define KI(x) STB_I(x) +#define KU(x) STB_UI(x) +#define KF(x) STB_F(x) +#define KD(x) STB_D(x) +#define KS(x) STB_S(x) +#endif + +STB_EXTERN void stb_source_path(char *str); +#ifdef STB_DEFINE +char *stb__source_path; +void stb_source_path(char *path) +{ + stb__source_path = path; +} + +char *stb__get_sourcefile_path(char *file) +{ + static char filebuf[512]; + if (stb__source_path) { + sprintf(filebuf, "%s/%s", stb__source_path, file); + if (stb_fexists(filebuf)) return filebuf; + } + + if (stb_fexists(file)) return file; + + sprintf(filebuf, "../%s", file); + if (!stb_fexists(filebuf)) return filebuf; + + return file; +} +#endif + +#define STB_F(x) ((float) STB_H(x)) +#define STB_UI(x) ((unsigned int) STB_I(x)) + +#if !defined(STB_DEBUG) && !defined(STB_ALWAYS_H) +#define STB_D(x) ((double) (x)) +#define STB_I(x) ((int) (x)) +#define STB_S(x) ((char *) (x)) +#else +#define STB_D(x) stb__double_constant(__FILE__, __LINE__-1, (x)) +#define STB_I(x) stb__int_constant(__FILE__, __LINE__-1, (x)) +#define STB_S(x) stb__string_constant(__FILE__, __LINE__-1, (x)) + +STB_EXTERN double stb__double_constant(char *file, int line, double x); +STB_EXTERN int stb__int_constant(char *file, int line, int x); +STB_EXTERN char * stb__string_constant(char *file, int line, char *str); + +#ifdef STB_DEFINE + +enum +{ + STB__CTYPE_int, + STB__CTYPE_uint, + STB__CTYPE_float, + STB__CTYPE_double, + STB__CTYPE_string, +}; + +typedef struct +{ + int line; + int type; + union { + int ival; + double dval; + char *sval; + }; +} stb__Entry; + +typedef struct +{ + stb__Entry *entries; + char *filename; + time_t timestamp; + char **file_data; + int file_len; + unsigned short *line_index; +} stb__FileEntry; + +static void stb__constant_parse(stb__FileEntry *f, int i) +{ + char *s; + int n; + if (!stb_arr_valid(f->entries, i)) return; + n = f->entries[i].line; + if (n >= f->file_len) return; + s = f->file_data[n]; + switch (f->entries[i].type) { + case STB__CTYPE_float: + while (*s) { + if (!strncmp(s, "STB_D(", 6)) { s+=6; goto matched_float; } + if (!strncmp(s, "STB_F(", 6)) { s+=6; goto matched_float; } + if (!strncmp(s, "KD(", 3)) { s+=3; goto matched_float; } + if (!strncmp(s, "KF(", 3)) { s+=3; goto matched_float; } + ++s; + } + break; + matched_float: + f->entries[i].dval = strtod(s, NULL); + break; + case STB__CTYPE_int: + while (*s) { + if (!strncmp(s, "STB_I(", 6)) { s+=6; goto matched_int; } + if (!strncmp(s, "STB_UI(", 7)) { s+=7; goto matched_int; } + if (!strncmp(s, "KI(", 3)) { s+=3; goto matched_int; } + if (!strncmp(s, "KU(", 3)) { s+=3; goto matched_int; } + ++s; + } + break; + matched_int: { + int neg=0; + s = stb_skipwhite(s); + while (*s == '-') { neg = !neg; s = stb_skipwhite(s+1); } // handle '- - 5', pointlessly + if (s[0] == '0' && tolower(s[1]) == 'x') + f->entries[i].ival = strtol(s, NULL, 16); + else if (s[0] == '0') + f->entries[i].ival = strtol(s, NULL, 8); + else + f->entries[i].ival = strtol(s, NULL, 10); + if (neg) f->entries[i].ival = -f->entries[i].ival; + break; + } + case STB__CTYPE_string: + // @TODO + break; + } +} + +static stb_sdict *stb__constant_file_hash; + +stb__Entry *stb__constant_get_entry(char *filename, int line, int type) +{ + int i; + stb__FileEntry *f; + if (stb__constant_file_hash == NULL) + stb__constant_file_hash = stb_sdict_new(STB_TRUE); + f = (stb__FileEntry*) stb_sdict_get(stb__constant_file_hash, filename); + if (f == NULL) { + char *s = stb__get_sourcefile_path(filename); + if (s == NULL || !stb_fexists(s)) return 0; + f = (stb__FileEntry *) malloc(sizeof(*f)); + f->timestamp = stb_ftimestamp(s); + f->file_data = stb_stringfile(s, &f->file_len); + f->filename = strdup(s); // cache the full path + f->entries = NULL; + f->line_index = 0; + stb_arr_setlen(f->line_index, f->file_len); + memset(f->line_index, 0xff, stb_arr_storage(f->line_index)); + } else { + time_t t = stb_ftimestamp(f->filename); + if (f->timestamp != t) { + f->timestamp = t; + free(f->file_data); + f->file_data = stb_stringfile(f->filename, &f->file_len); + stb_arr_setlen(f->line_index, f->file_len); + for (i=0; i < stb_arr_len(f->entries); ++i) + stb__constant_parse(f, i); + } + } + + if (line >= f->file_len) return 0; + + if (f->line_index[line] >= stb_arr_len(f->entries)) { + // need a new entry + int n = stb_arr_len(f->entries); + stb__Entry e; + e.line = line; + if (line < f->file_len) + f->line_index[line] = n; + e.type = type; + stb_arr_push(f->entries, e); + stb__constant_parse(f, n); + } + return f->entries + f->line_index[line]; +} + +double stb__double_constant(char *file, int line, double x) +{ + stb__Entry *e = stb__constant_get_entry(file, line, STB__CTYPE_float); + if (!e) return x; + return e->dval; +} + +int stb__int_constant(char *file, int line, int x) +{ + stb__Entry *e = stb__constant_get_entry(file, line, STB__CTYPE_int); + if (!e) return x; + return e->ival; +} + +char * stb__string_constant(char *file, int line, char *x) +{ + stb__Entry *e = stb__constant_get_entry(file, line, STB__CTYPE_string); + if (!e) return x; + return e->sval; +} + +#endif // STB_DEFINE +#endif // !STB_DEBUG && !STB_ALWAYS_H + + +#ifdef STB_STUA +////////////////////////////////////////////////////////////////////////// +// +// stua: little scripting language +// +// define STB_STUA to compile it +// +// see http://nothings.org/stb/stb_stua.html for documentation +// +// basic parsing model: +// +// lexical analysis +// use stb_lex() to parse tokens; keywords get their own tokens +// +// parsing: +// recursive descent parser. too much of a hassle to make an unambiguous +// LR(1) grammar, and one-pass generation is clumsier (recursive descent +// makes it easier to e.g. compile nested functions). on the other hand, +// dictionary syntax required hackery to get extra lookahead. +// +// codegen: +// output into an evaluation tree, using array indices as 'pointers' +// +// run: +// traverse the tree; support for 'break/continue/return' is tricky +// +// garbage collection: +// stu__mark and sweep; explicit stack with non-stu__compile_global_scope roots + +typedef stb_int32 stua_obj; + +typedef stb_idict stua_dict; + +STB_EXTERN void stua_run_script(char *s); +STB_EXTERN void stua_uninit(void); + +extern stua_obj stua_globals; + +STB_EXTERN double stua_number(stua_obj z); + +STB_EXTERN stua_obj stua_getnil(void); +STB_EXTERN stua_obj stua_getfalse(void); +STB_EXTERN stua_obj stua_gettrue(void); +STB_EXTERN stua_obj stua_string(char *z); +STB_EXTERN stua_obj stua_make_number(double d); +STB_EXTERN stua_obj stua_box(int type, void *data, int size); + +enum +{ + STUA_op_negate=129, + STUA_op_shl, STUA_op_ge, + STUA_op_shr, STUA_op_le, + STUA_op_shru, + STUA_op_last +}; + +#define STUA_NO_VALUE 2 // equivalent to a tagged NULL +STB_EXTERN stua_obj (*stua_overload)(int op, stua_obj a, stua_obj b, stua_obj c); + +STB_EXTERN stua_obj stua_error(char *err, ...); + +STB_EXTERN stua_obj stua_pushroot(stua_obj o); +STB_EXTERN void stua_poproot ( void ); + + +#ifdef STB_DEFINE +// INTERPRETER + +// 31-bit floating point implementation +// force the (1 << 30) bit (2nd highest bit) to be zero by re-biasing the exponent; +// then shift and set the bottom bit + +static stua_obj stu__floatp(float *f) +{ + unsigned int n = *(unsigned int *) f; + unsigned int e = n & (0xff << 23); + + assert(sizeof(int) == 4 && sizeof(float) == 4); + + if (!e) // zero? + n = n; // no change + else if (e < (64 << 23)) // underflow of the packed encoding? + n = (n & 0x80000000); // signed 0 + else if (e > (190 << 23)) // overflow of the encoding? (or INF or NAN) + n = (n & 0x80000000) + (127 << 23); // new INF encoding + else + n -= 0x20000000; + + // now we need to shuffle the bits so that the spare bit is at the bottom + assert((n & 0x40000000) == 0); + return (n & 0x80000000) + (n << 1) + 1; +} + +static unsigned char stu__getfloat_addend[256]; +static float stu__getfloat(stua_obj v) +{ + unsigned int n; + unsigned int e = ((unsigned int) v) >> 24; + + n = (int) v >> 1; // preserve high bit + n += stu__getfloat_addend[e] << 24; + return *(float *) &n; +} + +stua_obj stua_float(float f) +{ + return stu__floatp(&f); +} + +static void stu__float_init(void) +{ + int i; + stu__getfloat_addend[0] = 0; // do nothing to biased exponent of 0 + for (i=1; i < 127; ++i) + stu__getfloat_addend[i] = 32; // undo the -0x20000000 + stu__getfloat_addend[127] = 64; // convert packed INF to INF (0x3f -> 0x7f) + + for (i=0; i < 128; ++i) // for signed floats, remove the bit we just shifted down + stu__getfloat_addend[128+i] = stu__getfloat_addend[i] - 64; +} + +// Tagged data type implementation + + // TAGS: +#define stu__int_tag 0 // of 2 bits // 00 int +#define stu__float_tag 1 // of 1 bit // 01 float +#define stu__ptr_tag 2 // of 2 bits // 10 boxed + // 11 float + +#define stu__tag(x) ((x) & 3) +#define stu__number(x) (stu__tag(x) != stu__ptr_tag) +#define stu__isint(x) (stu__tag(x) == stu__int_tag) + +#define stu__int(x) ((x) >> 2) +#define stu__float(x) (stu__getfloat(x)) + +#define stu__makeint(v) ((v)*4+stu__int_tag) + +// boxed data, and tag support for boxed data + +enum +{ + STU___float = 1, STU___int = 2, + STU___number = 3, STU___string = 4, + STU___function = 5, STU___dict = 6, + STU___boolean = 7, STU___error = 8, +}; + +// boxed data +#define STU__BOX short type, stua_gc +typedef struct stu__box { STU__BOX; } stu__box; + +stu__box stu__nil = { 0, 1 }; +stu__box stu__true = { STU___boolean, 1, }; +stu__box stu__false = { STU___boolean, 1, }; + +#define stu__makeptr(v) ((stua_obj) (v) + stu__ptr_tag) + +#define stua_nil stu__makeptr(&stu__nil) +#define stua_true stu__makeptr(&stu__true) +#define stua_false stu__makeptr(&stu__false) + +stua_obj stua_getnil(void) { return stua_nil; } +stua_obj stua_getfalse(void) { return stua_false; } +stua_obj stua_gettrue(void) { return stua_true; } + +#define stu__ptr(x) ((stu__box *) ((x) - stu__ptr_tag)) + +#define stu__checkt(t,x) ((t) == STU___float ? ((x) & 1) == stu__float_tag : \ + (t) == STU___int ? stu__isint(x) : \ + (t) == STU___number ? stu__number(x) : \ + stu__tag(x) == stu__ptr_tag && stu__ptr(x)->type == (t)) + +typedef struct +{ + STU__BOX; + void *ptr; +} stu__wrapper; + +// implementation of a 'function' or function + closure + +typedef struct stu__func +{ + STU__BOX; + stua_obj closure_source; // 0 - regular function; 4 - C function + // if closure, pointer to source function + union { + stua_obj closure_data; // partial-application data + void *store; // pointer to free that holds 'code' + stua_obj (*func)(stua_dict *context); + } f; + // closure ends here + short *code; + int num_param; + stua_obj *param; // list of parameter strings +} stu__func; + +// apply this to 'short *code' to get at data +#define stu__const(f) ((stua_obj *) (f)) + +static void stu__free_func(stu__func *f) +{ + if (f->closure_source == 0) free(f->f.store); + if ((stb_uint) f->closure_source <= 4) free(f->param); + free(f); +} + +#define stu__pd(x) ((stua_dict *) stu__ptr(x)) +#define stu__pw(x) ((stu__wrapper *) stu__ptr(x)) +#define stu__pf(x) ((stu__func *) stu__ptr(x)) + + +// garbage-collection + + +static stu__box ** stu__gc_ptrlist; +static stua_obj * stu__gc_root_stack; + +stua_obj stua_pushroot(stua_obj o) { stb_arr_push(stu__gc_root_stack, o); return o; } +void stua_poproot ( void ) { stb_arr_pop(stu__gc_root_stack); } + +static stb_sdict *stu__strings; +static void stu__mark(stua_obj z) +{ + int i; + stu__box *p = stu__ptr(z); + if (p->stua_gc == 1) return; // already marked + assert(p->stua_gc == 0); + p->stua_gc = 1; + switch(p->type) { + case STU___function: { + stu__func *f = (stu__func *) p; + if ((stb_uint) f->closure_source <= 4) { + if (f->closure_source == 0) { + for (i=1; i <= f->code[0]; ++i) + if (!stu__number(((stua_obj *) f->code)[-i])) + stu__mark(((stua_obj *) f->code)[-i]); + } + for (i=0; i < f->num_param; ++i) + stu__mark(f->param[i]); + } else { + stu__mark(f->closure_source); + stu__mark(f->f.closure_data); + } + break; + } + case STU___dict: { + stua_dict *e = (stua_dict *) p; + for (i=0; i < e->limit; ++i) + if (e->table[i].k != STB_IEMPTY && e->table[i].k != STB_IDEL) { + if (!stu__number(e->table[i].k)) stu__mark((int) e->table[i].k); + if (!stu__number(e->table[i].v)) stu__mark((int) e->table[i].v); + } + break; + } + } +} + +static int stu__num_allocs, stu__size_allocs; +static stua_obj stu__flow_val = stua_nil; // used for break & return + +static void stua_gc(int force) +{ + int i; + if (!force && stu__num_allocs == 0 && stu__size_allocs == 0) return; + stu__num_allocs = stu__size_allocs = 0; + //printf("[gc]\n"); + + // clear marks + for (i=0; i < stb_arr_len(stu__gc_ptrlist); ++i) + stu__gc_ptrlist[i]->stua_gc = 0; + + // stu__mark everything reachable + stu__nil.stua_gc = stu__true.stua_gc = stu__false.stua_gc = 1; + stu__mark(stua_globals); + if (!stu__number(stu__flow_val)) + stu__mark(stu__flow_val); + for (i=0; i < stb_arr_len(stu__gc_root_stack); ++i) + if (!stu__number(stu__gc_root_stack[i])) + stu__mark(stu__gc_root_stack[i]); + + // sweep unreachables + for (i=0; i < stb_arr_len(stu__gc_ptrlist);) { + stu__box *z = stu__gc_ptrlist[i]; + if (!z->stua_gc) { + switch (z->type) { + case STU___dict: stb_idict_destroy((stua_dict *) z); break; + case STU___error: free(((stu__wrapper *) z)->ptr); break; + case STU___string: stb_sdict_remove(stu__strings, (char*) ((stu__wrapper *) z)->ptr, NULL); free(z); break; + case STU___function: stu__free_func((stu__func *) z); break; + } + // swap in the last item over this, and repeat + z = stb_arr_pop(stu__gc_ptrlist); + stu__gc_ptrlist[i] = z; + } else + ++i; + } +} + +static void stu__consider_gc(stua_obj x) +{ + if (stu__size_allocs < 100000) return; + if (stu__num_allocs < 10 && stu__size_allocs < 1000000) return; + stb_arr_push(stu__gc_root_stack, x); + stua_gc(0); + stb_arr_pop(stu__gc_root_stack); +} + +static stua_obj stu__makeobj(int type, void *data, int size, int safe_to_gc) +{ + stua_obj x = stu__makeptr(data); + ((stu__box *) data)->type = type; + stb_arr_push(stu__gc_ptrlist, (stu__box *) data); + stu__num_allocs += 1; + stu__size_allocs += size; + if (safe_to_gc) stu__consider_gc(x); + return x; +} + +stua_obj stua_box(int type, void *data, int size) +{ + stu__wrapper *p = (stu__wrapper *) malloc(sizeof(*p)); + p->ptr = data; + return stu__makeobj(type, p, size, 0); +} + +// a stu string can be directly compared for equality, because +// they go into a hash table +stua_obj stua_string(char *z) +{ + stu__wrapper *b = (stu__wrapper *) stb_sdict_get(stu__strings, z); + if (b == NULL) { + int o = stua_box(STU___string, NULL, strlen(z) + sizeof(*b)); + b = stu__pw(o); + stb_sdict_add(stu__strings, z, b); + stb_sdict_getkey(stu__strings, z, (char **) &b->ptr); + } + return stu__makeptr(b); +} + +// stb_obj dictionary is just an stb_idict +static void stu__set(stua_dict *d, stua_obj k, stua_obj v) +{ if (stb_idict_set(d, k, v)) stu__size_allocs += 8; } + +static stua_obj stu__get(stua_dict *d, stua_obj k, stua_obj res) +{ + stb_idict_get_flag(d, k, &res); + return res; +} + +static stua_obj make_string(char *z, int len) +{ + stua_obj s; + char temp[256], *q = (char *) stb_temp(temp, len+1), *p = q; + while (len > 0) { + if (*z == '\\') { + if (z[1] == 'n') *p = '\n'; + else if (z[1] == 'r') *p = '\r'; + else if (z[1] == 't') *p = '\t'; + else *p = z[1]; + p += 1; z += 2; len -= 2; + } else { + *p++ = *z++; len -= 1; + } + } + *p = 0; + s = stua_string(q); + stb_tempfree(temp, q); + return s; +} + +enum token_names +{ + T__none=128, + ST_shl = STUA_op_shl, ST_ge = STUA_op_ge, + ST_shr = STUA_op_shr, ST_le = STUA_op_le, + ST_shru = STUA_op_shru, STU__negate = STUA_op_negate, + ST__reset_numbering = STUA_op_last, + ST_white, + ST_id, ST_float, ST_decimal, ST_hex, ST_char,ST_string, ST_number, + // make sure the keywords come _AFTER_ ST_id, so stb_lex prefer them + ST_if, ST_while, ST_for, ST_eq, ST_nil, + ST_then, ST_do, ST_in, ST_ne, ST_true, + ST_else, ST_break, ST_let, ST_and, ST_false, + ST_elseif, ST_continue, ST_into, ST_or, ST_repeat, + ST_end, ST_as, ST_return, ST_var, ST_func, + ST_catch, ST__frame, + ST__max_terminals, + + STU__defaultparm, STU__seq, +}; + +static stua_dict * stu__globaldict; + stua_obj stua_globals; + +static enum +{ + FLOW_normal, FLOW_continue, FLOW_break, FLOW_return, FLOW_error, +} stu__flow; + +stua_obj stua_error(char *z, ...) +{ + stua_obj a; + char temp[4096], *x; + va_list v; va_start(v,z); vsprintf(temp, z, v); va_end(v); + x = strdup(temp); + a = stua_box(STU___error, x, strlen(x)); + stu__flow = FLOW_error; + stu__flow_val = a; + return stua_nil; +} + +double stua_number(stua_obj z) +{ + return stu__tag(z) == stu__int_tag ? stu__int(z) : stu__float(z); +} + +stua_obj stua_make_number(double d) +{ + double e = floor(d); + if (e == d && e < (1 << 29) && e >= -(1 << 29)) + return stu__makeint((int) e); + else + return stua_float((float) d); +} + +stua_obj (*stua_overload)(int op, stua_obj a, stua_obj b, stua_obj c) = NULL; + +static stua_obj stu__op(int op, stua_obj a, stua_obj b, stua_obj c) +{ + stua_obj r = STUA_NO_VALUE; + if (op == '+') { + if (stu__checkt(STU___string, a) && stu__checkt(STU___string, b)) { + ;// @TODO: string concatenation + } else if (stu__checkt(STU___function, a) && stu__checkt(STU___dict, b)) { + stu__func *f = (stu__func *) malloc(12); + assert(offsetof(stu__func, code)==12); + f->closure_source = a; + f->f.closure_data = b; + return stu__makeobj(STU___function, f, 16, 1); + } + } + if (stua_overload) r = stua_overload(op,a,b,c); + if (stu__flow != FLOW_error && r == STUA_NO_VALUE) + stua_error("Typecheck for operator %d", op), r=stua_nil; + return r; +} + +#define STU__EVAL2(a,b) \ + a = stu__eval(stu__f[n+1]); if (stu__flow) break; stua_pushroot(a); \ + b = stu__eval(stu__f[n+2]); stua_poproot(); if (stu__flow) break; + +#define STU__FB(op) \ + STU__EVAL2(a,b) \ + if (stu__tag(a) == stu__int_tag && stu__tag(b) == stu__int_tag) \ + return ((a) op (b)); \ + if (stu__number(a) && stu__number(b)) \ + return stua_make_number(stua_number(a) op stua_number(b)); \ + return stu__op(stu__f[n], a,b, stua_nil) + +#define STU__F(op) \ + STU__EVAL2(a,b) \ + if (stu__number(a) && stu__number(b)) \ + return stua_make_number(stua_number(a) op stua_number(b)); \ + return stu__op(stu__f[n], a,b, stua_nil) + +#define STU__I(op) \ + STU__EVAL2(a,b) \ + if (stu__tag(a) == stu__int_tag && stu__tag(b) == stu__int_tag) \ + return stu__makeint(stu__int(a) op stu__int(b)); \ + return stu__op(stu__f[n], a,b, stua_nil) + +#define STU__C(op) \ + STU__EVAL2(a,b) \ + if (stu__number(a) && stu__number(b)) \ + return (stua_number(a) op stua_number(b)) ? stua_true : stua_false; \ + return stu__op(stu__f[n], a,b, stua_nil) + +#define STU__CE(op) \ + STU__EVAL2(a,b) \ + return (a op b) ? stua_true : stua_false + +static short *stu__f; +static stua_obj stu__f_obj; +static stua_dict *stu__c; +static stua_obj stu__funceval(stua_obj fo, stua_obj co); + +static int stu__cond(stua_obj x) +{ + if (stu__flow) return 0; + if (!stu__checkt(STU___boolean, x)) + x = stu__op('!', x, stua_nil, stua_nil); + if (x == stua_true ) return 1; + if (x == stua_false) return 0; + stu__flow = FLOW_error; + return 0; +} + +// had to manually eliminate tailcall recursion for debugging complex stuff +#define TAILCALL(x) n = (x); goto top; +static stua_obj stu__eval(int n) +{ +top: + if (stu__flow >= FLOW_return) return stua_nil; // is this needed? + if (n < 0) return stu__const(stu__f)[n]; + assert(n != 0 && n != 1); + switch (stu__f[n]) { + stua_obj a,b,c; + case ST_catch: a = stu__eval(stu__f[n+1]); + if (stu__flow == FLOW_error) { a=stu__flow_val; stu__flow = FLOW_normal; } + return a; + case ST_var: b = stu__eval(stu__f[n+2]); if (stu__flow) break; + stu__set(stu__c, stu__const(stu__f)[stu__f[n+1]], b); + return b; + case STU__seq: stu__eval(stu__f[n+1]); if (stu__flow) break; + TAILCALL(stu__f[n+2]); + case ST_if: if (!stu__cond(stu__eval(stu__f[n+1]))) return stua_nil; + TAILCALL(stu__f[n+2]); + case ST_else: a = stu__cond(stu__eval(stu__f[n+1])); + TAILCALL(stu__f[n + 2 + !a]); + #define STU__HANDLE_BREAK \ + if (stu__flow >= FLOW_break) { \ + if (stu__flow == FLOW_break) { \ + a = stu__flow_val; \ + stu__flow = FLOW_normal; \ + stu__flow_val = stua_nil; \ + return a; \ + } \ + return stua_nil; \ + } + case ST_as: stu__eval(stu__f[n+3]); + STU__HANDLE_BREAK + // fallthrough! + case ST_while: a = stua_nil; stua_pushroot(a); + while (stu__cond(stu__eval(stu__f[n+1]))) { + stua_poproot(); + a = stu__eval(stu__f[n+2]); + STU__HANDLE_BREAK + stu__flow = FLOW_normal; // clear 'continue' flag + stua_pushroot(a); + if (stu__f[n+3]) stu__eval(stu__f[n+3]); + STU__HANDLE_BREAK + stu__flow = FLOW_normal; // clear 'continue' flag + } + stua_poproot(); + return a; + case ST_break: stu__flow = FLOW_break; stu__flow_val = stu__eval(stu__f[n+1]); break; + case ST_continue:stu__flow = FLOW_continue; break; + case ST_return: stu__flow = FLOW_return; stu__flow_val = stu__eval(stu__f[n+1]); break; + case ST__frame: return stu__f_obj; + case '[': STU__EVAL2(a,b); + if (stu__checkt(STU___dict, a)) + return stu__get(stu__pd(a), b, stua_nil); + return stu__op(stu__f[n], a, b, stua_nil); + case '=': a = stu__eval(stu__f[n+2]); if (stu__flow) break; + n = stu__f[n+1]; + if (stu__f[n] == ST_id) { + if (!stb_idict_update(stu__c, stu__const(stu__f)[stu__f[n+1]], a)) + if (!stb_idict_update(stu__globaldict, stu__const(stu__f)[stu__f[n+1]], a)) + return stua_error("Assignment to undefined variable"); + } else if (stu__f[n] == '[') { + stua_pushroot(a); + b = stu__eval(stu__f[n+1]); if (stu__flow) { stua_poproot(); break; } + stua_pushroot(b); + c = stu__eval(stu__f[n+2]); stua_poproot(); stua_poproot(); + if (stu__flow) break; + if (!stu__checkt(STU___dict, b)) return stua_nil; + stu__set(stu__pd(b), c, a); + } else { + return stu__op(stu__f[n], stu__eval(n), a, stua_nil); + } + return a; + case STU__defaultparm: + a = stu__eval(stu__f[n+2]); + stu__flow = FLOW_normal; + if (stb_idict_add(stu__c, stu__const(stu__f)[stu__f[n+1]], a)) + stu__size_allocs += 8; + return stua_nil; + case ST_id: a = stu__get(stu__c, stu__const(stu__f)[stu__f[n+1]], STUA_NO_VALUE); // try local variable + return a != STUA_NO_VALUE // else try stu__compile_global_scope variable + ? a : stu__get(stu__globaldict, stu__const(stu__f)[stu__f[n+1]], stua_nil); + case STU__negate:a = stu__eval(stu__f[n+1]); if (stu__flow) break; + return stu__isint(a) ? -a : stu__op(stu__f[n], a, stua_nil, stua_nil); + case '~': a = stu__eval(stu__f[n+1]); if (stu__flow) break; + return stu__isint(a) ? (~a)&~3 : stu__op(stu__f[n], a, stua_nil, stua_nil); + case '!': a = stu__eval(stu__f[n+1]); if (stu__flow) break; + a = stu__cond(a); if (stu__flow) break; + return a ? stua_true : stua_false; + case ST_eq: STU__CE(==); case ST_le: STU__C(<=); case '<': STU__C(<); + case ST_ne: STU__CE(!=); case ST_ge: STU__C(>=); case '>': STU__C(>); + case '+' : STU__FB(+); case '*': STU__F(*); case '&': STU__I(&); case ST_shl: STU__I(<<); + case '-' : STU__FB(-); case '/': STU__F(/); case '|': STU__I(|); case ST_shr: STU__I(>>); + case '%': STU__I(%); case '^': STU__I(^); + case ST_shru: STU__EVAL2(a,b); + if (stu__tag(a) == stu__int_tag && stu__tag(b) == stu__int_tag) + return stu__makeint((unsigned) stu__int(a) >> stu__int(b)); + return stu__op(stu__f[n], a,b, stua_nil); + case ST_and: a = stu__eval(stu__f[n+1]); b = stu__cond(a); if (stu__flow) break; + return a ? stu__eval(stu__f[n+2]) : a; + case ST_or : a = stu__eval(stu__f[n+1]); b = stu__cond(a); if (stu__flow) break; + return a ? b : stu__eval(stu__f[n+2]); + case'(':case':': STU__EVAL2(a,b); + if (!stu__checkt(STU___function, a)) + return stu__op(stu__f[n], a,b, stua_nil); + if (!stu__checkt(STU___dict, b)) + return stua_nil; + if (stu__f[n] == ':') + b = stu__makeobj(STU___dict, stb_idict_copy(stu__pd(b)), stb_idict_memory_usage(stu__pd(b)), 0); + a = stu__funceval(a,b); + return a; + case '{' : { + stua_dict *d; + d = stb_idict_new_size(stu__f[n+1] > 40 ? 64 : 16); + if (d == NULL) + return stua_nil; // breakpoint fodder + c = stu__makeobj(STU___dict, d, 32, 1); + stua_pushroot(c); + a = stu__f[n+1]; + for (b=0; b < a; ++b) { + stua_obj x = stua_pushroot(stu__eval(stu__f[n+2 + b*2 + 0])); + stua_obj y = stu__eval(stu__f[n+2 + b*2 + 1]); + stua_poproot(); + if (stu__flow) { stua_poproot(); return stua_nil; } + stu__set(d, x, y); + } + stua_poproot(); + return c; + } + default: if (stu__f[n] < 0) return stu__const(stu__f)[stu__f[n]]; + assert(0); /* NOTREACHED */ // internal error! + } + return stua_nil; +} + +int stb__stua_nesting; +static stua_obj stu__funceval(stua_obj fo, stua_obj co) +{ + stu__func *f = stu__pf(fo); + stua_dict *context = stu__pd(co); + int i,j; + stua_obj p; + short *tf = stu__f; // save previous function + stua_dict *tc = stu__c; + + if (stu__flow == FLOW_error) return stua_nil; + assert(stu__flow == FLOW_normal); + + stua_pushroot(fo); + stua_pushroot(co); + stu__consider_gc(stua_nil); + + while ((stb_uint) f->closure_source > 4) { + // add data from closure to context + stua_dict *e = (stua_dict *) stu__pd(f->f.closure_data); + for (i=0; i < e->limit; ++i) + if (e->table[i].k != STB_IEMPTY && e->table[i].k != STB_IDEL) + if (stb_idict_add(context, e->table[i].k, e->table[i].v)) + stu__size_allocs += 8; + // use add so if it's already defined, we don't override it; that way + // explicit parameters win over applied ones, and most recent applications + // win over previous ones + f = stu__pf(f->closure_source); + } + + for (j=0, i=0; i < f->num_param; ++i) + // if it doesn't already exist, add it from the numbered parameters + if (stb_idict_add(context, f->param[i], stu__get(context, stu__int(j), stua_nil))) + ++j; + + // @TODO: if (stu__get(context, stu__int(f->num_param+1)) != STUA_NO_VALUE) // error: too many parameters + // @TODO: ditto too few parameters + + if (f->closure_source == 4) + p = f->f.func(context); + else { + stu__f = f->code, stu__c = context; + stu__f_obj = co; + ++stb__stua_nesting; + if (stu__f[1]) + p = stu__eval(stu__f[1]); + else + p = stua_nil; + --stb__stua_nesting; + stu__f = tf, stu__c = tc; // restore previous function + if (stu__flow == FLOW_return) { + stu__flow = FLOW_normal; + p = stu__flow_val; + stu__flow_val = stua_nil; + } + } + + stua_poproot(); + stua_poproot(); + + return p; +} + +// Parser + +static int stu__tok; +static stua_obj stu__tokval; + +static char *stu__curbuf, *stu__bufstart; + +static stb_matcher *stu__lex_matcher; + +static unsigned char stu__prec[ST__max_terminals], stu__end[ST__max_terminals]; + +static void stu__nexttoken(void) +{ + int len; + +retry: + stu__tok = stb_lex(stu__lex_matcher, stu__curbuf, &len); + if (stu__tok == 0) + return; + switch(stu__tok) { + case ST_white : stu__curbuf += len; goto retry; + case T__none : stu__tok = *stu__curbuf; break; + case ST_string: stu__tokval = make_string(stu__curbuf+1, len-2); break; + case ST_id : stu__tokval = make_string(stu__curbuf, len); break; + case ST_hex : stu__tokval = stu__makeint(strtol(stu__curbuf+2,NULL,16)); stu__tok = ST_number; break; + case ST_decimal: stu__tokval = stu__makeint(strtol(stu__curbuf ,NULL,10)); stu__tok = ST_number; break; + case ST_float : stu__tokval = stua_float((float) atof(stu__curbuf)) ; stu__tok = ST_number; break; + case ST_char : stu__tokval = stu__curbuf[2] == '\\' ? stu__curbuf[3] : stu__curbuf[2]; + if (stu__curbuf[3] == 't') stu__tokval = '\t'; + if (stu__curbuf[3] == 'n') stu__tokval = '\n'; + if (stu__curbuf[3] == 'r') stu__tokval = '\r'; + stu__tokval = stu__makeint(stu__tokval); + stu__tok = ST_number; + break; + } + stu__curbuf += len; +} + +static struct { int stu__tok; char *regex; } stu__lexemes[] = +{ + ST_white , "([ \t\n\r]|/\\*(.|\n)*\\*/|//[^\r\n]*([\r\n]|$))+", + ST_id , "[_a-zA-Z][_a-zA-Z0-9]*", + ST_hex , "0x[0-9a-fA-F]+", + ST_decimal, "[0-9]+[0-9]*", + ST_float , "[0-9]+\\.?[0-9]*([eE][-+]?[0-9]+)?", + ST_float , "\\.[0-9]+([eE][-+]?[0-9]+)?", + ST_char , "c'(\\\\.|[^\\'])'", + ST_string , "\"(\\\\.|[^\\\"\n\r])*\"", + ST_string , "\'(\\\\.|[^\\\'\n\r])*\'", + + #define stua_key4(a,b,c,d) ST_##a, #a, ST_##b, #b, ST_##c, #c, ST_##d, #d, + stua_key4(if,then,else,elseif) stua_key4(while,do,for,in) + stua_key4(func,var,let,break) stua_key4(nil,true,false,end) + stua_key4(return,continue,as,repeat) stua_key4(_frame,catch,catch,catch) + + ST_shl, "<<", ST_and, "&&", ST_eq, "==", ST_ge, ">=", + ST_shr, ">>", ST_or , "||", ST_ne, "!=", ST_le, "<=", + ST_shru,">>>", ST_into, "=>", + T__none, ".", +}; + +typedef struct +{ + stua_obj *data; // constants being compiled + short *code; // code being compiled + stua_dict *locals; + short *non_local_refs; +} stu__comp_func; + +static stu__comp_func stu__pfunc; +static stu__comp_func *func_stack = NULL; +static void stu__push_func_comp(void) +{ + stb_arr_push(func_stack, stu__pfunc); + stu__pfunc.data = NULL; + stu__pfunc.code = NULL; + stu__pfunc.locals = stb_idict_new_size(16); + stu__pfunc.non_local_refs = NULL; + stb_arr_push(stu__pfunc.code, 0); // number of data items + stb_arr_push(stu__pfunc.code, 1); // starting execution address +} + +static void stu__pop_func_comp(void) +{ + stb_arr_free(stu__pfunc.code); + stb_arr_free(stu__pfunc.data); + stb_idict_destroy(stu__pfunc.locals); + stb_arr_free(stu__pfunc.non_local_refs); + stu__pfunc = stb_arr_pop(func_stack); +} + +// if an id is a reference to an outer lexical scope, this +// function returns the "name" of it, and updates the stack +// structures to make sure the names are propogated in. +static int stu__nonlocal_id(stua_obj var_obj) +{ + stua_obj dummy, var = var_obj; + int i, n = stb_arr_len(func_stack), j,k; + if (stb_idict_get_flag(stu__pfunc.locals, var, &dummy)) return 0; + for (i=n-1; i > 1; --i) { + if (stb_idict_get_flag(func_stack[i].locals, var, &dummy)) + break; + } + if (i <= 1) return 0; // stu__compile_global_scope + j = i; // need to access variable from j'th frame + for (i=0; i < stb_arr_len(stu__pfunc.non_local_refs); ++i) + if (stu__pfunc.non_local_refs[i] == j) return j-n; + stb_arr_push(stu__pfunc.non_local_refs, j-n); + // now make sure all the parents propogate it down + for (k=n-1; k > 1; --k) { + if (j-k >= 0) return j-n; // comes direct from this parent + for(i=0; i < stb_arr_len(func_stack[k].non_local_refs); ++i) + if (func_stack[k].non_local_refs[i] == j-k) + return j-n; + stb_arr_push(func_stack[k].non_local_refs, j-k); + } + assert (k != 1); + + return j-n; +} + +static int stu__off(void) { return stb_arr_len(stu__pfunc.code); } +static void stu__cc(int a) +{ + assert(a >= -2000 && a < 5000); + stb_arr_push(stu__pfunc.code, a); +} +static int stu__cc1(int a) { stu__cc(a); return stu__off()-1; } +static int stu__cc2(int a, int b) { stu__cc(a); stu__cc(b); return stu__off()-2; } +static int stu__cc3(int a, int b, int c) { + if (a == '=') assert(c != 0); + stu__cc(a); stu__cc(b); stu__cc(c); return stu__off()-3; } +static int stu__cc4(int a, int b, int c, int d) { stu__cc(a); stu__cc(b); stu__cc(c); stu__cc(d); return stu__off()-4; } + +static int stu__cdv(stua_obj p) +{ + int i; + assert(p != STUA_NO_VALUE); + for (i=0; i < stb_arr_len(stu__pfunc.data); ++i) + if (stu__pfunc.data[i] == p) + break; + if (i == stb_arr_len(stu__pfunc.data)) + stb_arr_push(stu__pfunc.data, p); + return ~i; +} + +static int stu__cdt(void) +{ + int z = stu__cdv(stu__tokval); + stu__nexttoken(); + return z; +} + +static int stu__seq(int a, int b) +{ + return !a ? b : !b ? a : stu__cc3(STU__seq, a,b); +} + +static char stu__comp_err_str[1024]; +static int stu__comp_err_line; +static int stu__err(char *str, ...) +{ + va_list v; + char *s = stu__bufstart; + stu__comp_err_line = 1; + while (s < stu__curbuf) { + if (s[0] == '\n' || s[0] == '\r') { + if (s[0]+s[1] == '\n' + '\r') ++s; + ++stu__comp_err_line; + } + ++s; + } + va_start(v, str); + vsprintf(stu__comp_err_str, str, v); + va_end(v); + return 0; +} + +static int stu__accept(int p) +{ + if (stu__tok != p) return 0; + stu__nexttoken(); + return 1; +} + +static int stu__demand(int p) +{ + if (stu__accept(p)) return 1; + return stu__err("Didn't find expected stu__tok"); +} + +static int stu__demandv(int p, stua_obj *val) +{ + if (stu__tok == p || p==0) { + *val = stu__tokval; + stu__nexttoken(); + return 1; + } else + return 0; +} + +static int stu__expr(int p); +int stu__nexpr(int p) { stu__nexttoken(); return stu__expr(p); } +static int stu__statements(int once, int as); + +static int stu__parse_if(void) // parse both ST_if and ST_elseif +{ + int b,c,a; + a = stu__nexpr(1); if (!a) return 0; + if (!stu__demand(ST_then)) return stu__err("expecting THEN"); + b = stu__statements(0,0); if (!b) return 0; + if (b == 1) b = -1; + + if (stu__tok == ST_elseif) { + return stu__parse_if(); + } else if (stu__accept(ST_else)) { + c = stu__statements(0,0); if (!c) return 0; + if (!stu__demand(ST_end)) return stu__err("expecting END after else clause"); + return stu__cc4(ST_else, a, b, c); + } else { + if (!stu__demand(ST_end)) return stu__err("expecting END in if statement"); + return stu__cc3(ST_if, a, b); + } +} + +int stu__varinit(int z, int in_globals) +{ + int a,b; + stu__nexttoken(); + while (stu__demandv(ST_id, &b)) { + if (!stb_idict_add(stu__pfunc.locals, b, 1)) + if (!in_globals) return stu__err("Redefined variable %s.", stu__pw(b)->ptr); + if (stu__accept('=')) { + a = stu__expr(1); if (!a) return 0; + } else + a = stu__cdv(stua_nil); + z = stu__seq(z, stu__cc3(ST_var, stu__cdv(b), a)); + if (!stu__accept(',')) break; + } + return z; +} + +static int stu__compile_unary(int z, int outparm, int require_inparm) +{ + int op = stu__tok, a, b; + stu__nexttoken(); + if (outparm) { + if (require_inparm || (stu__tok && stu__tok != ST_end && stu__tok != ST_else && stu__tok != ST_elseif && stu__tok !=';')) { + a = stu__expr(1); if (!a) return 0; + } else + a = stu__cdv(stua_nil); + b = stu__cc2(op, a); + } else + b = stu__cc1(op); + return stu__seq(z,b); +} + +static int stu__assign(void) +{ + int z; + stu__accept(ST_let); + z = stu__expr(1); if (!z) return 0; + if (stu__accept('=')) { + int y,p = (z >= 0 ? stu__pfunc.code[z] : 0); + if (z < 0 || (p != ST_id && p != '[')) return stu__err("Invalid lvalue in assignment"); + y = stu__assign(); if (!y) return 0; + z = stu__cc3('=', z, y); + } + return z; +} + +static int stu__statements(int once, int stop_while) +{ + int a,b, c, z=0; + for(;;) { + switch (stu__tok) { + case ST_if : a = stu__parse_if(); if (!a) return 0; + z = stu__seq(z, a); + break; + case ST_while : if (stop_while) return (z ? z:1); + a = stu__nexpr(1); if (!a) return 0; + if (stu__accept(ST_as)) c = stu__statements(0,0); else c = 0; + if (!stu__demand(ST_do)) return stu__err("expecting DO"); + b = stu__statements(0,0); if (!b) return 0; + if (!stu__demand(ST_end)) return stu__err("expecting END"); + if (b == 1) b = -1; + z = stu__seq(z, stu__cc4(ST_while, a, b, c)); + break; + case ST_repeat : stu__nexttoken(); + c = stu__statements(0,1); if (!c) return 0; + if (!stu__demand(ST_while)) return stu__err("expecting WHILE"); + a = stu__expr(1); if (!a) return 0; + if (!stu__demand(ST_do)) return stu__err("expecting DO"); + b = stu__statements(0,0); if (!b) return 0; + if (!stu__demand(ST_end)) return stu__err("expecting END"); + if (b == 1) b = -1; + z = stu__seq(z, stu__cc4(ST_as, a, b, c)); + break; + case ST_catch : a = stu__nexpr(1); if (!a) return 0; + z = stu__seq(z, stu__cc2(ST_catch, a)); + break; + case ST_var : z = stu__varinit(z,0); break; + case ST_return : z = stu__compile_unary(z,1,1); break; + case ST_continue:z = stu__compile_unary(z,0,0); break; + case ST_break : z = stu__compile_unary(z,1,0); break; + case ST_into : if (z == 0 && !once) return stu__err("=> cannot be first statement in block"); + a = stu__nexpr(99); + b = (a >= 0? stu__pfunc.code[a] : 0); + if (a < 0 || (b != ST_id && b != '[')) return stu__err("Invalid lvalue on right side of =>"); + z = stu__cc3('=', a, z); + break; + default : if (stu__end[stu__tok]) return once ? 0 : (z ? z:1); + a = stu__assign(); if (!a) return 0; + stu__accept(';'); + if (stu__tok && !stu__end[stu__tok]) { + if (a < 0) + return stu__err("Constant has no effect"); + if (stu__pfunc.code[a] != '(' && stu__pfunc.code[a] != '=') + return stu__err("Expression has no effect"); + } + z = stu__seq(z, a); + break; + } + if (!z) return 0; + stu__accept(';'); + if (once && stu__tok != ST_into) return z; + } +} + +static int stu__postexpr(int z, int p); +static int stu__dictdef(int end, int *count) +{ + int z,n=0,i,flags=0; + short *dict=NULL; + stu__nexttoken(); + while (stu__tok != end) { + if (stu__tok == ST_id) { + stua_obj id = stu__tokval; + stu__nexttoken(); + if (stu__tok == '=') { + flags |= 1; + stb_arr_push(dict, stu__cdv(id)); + z = stu__nexpr(1); if (!z) return 0; + } else { + z = stu__cc2(ST_id, stu__cdv(id)); + z = stu__postexpr(z,1); if (!z) return 0; + flags |= 2; + stb_arr_push(dict, stu__cdv(stu__makeint(n++))); + } + } else { + z = stu__expr(1); if (!z) return 0; + flags |= 2; + stb_arr_push(dict, stu__cdv(stu__makeint(n++))); + } + if (end != ')' && flags == 3) { z=stu__err("can't mix initialized and uninitialized defs"); goto done;} + stb_arr_push(dict, z); + if (!stu__accept(',')) break; + } + if (!stu__demand(end)) + return stu__err(end == ')' ? "Expecting ) at end of function call" + : "Expecting } at end of dictionary definition"); + z = stu__cc2('{', stb_arr_len(dict)/2); + for (i=0; i < stb_arr_len(dict); ++i) + stu__cc(dict[i]); + if (count) *count = n; +done: + stb_arr_free(dict); + return z; +} + +static int stu__comp_id(void) +{ + int z,d; + d = stu__nonlocal_id(stu__tokval); + if (d == 0) + return z = stu__cc2(ST_id, stu__cdt()); + // access a non-local frame by naming it with the appropriate int + assert(d < 0); + z = stu__cdv(d); // relative frame # is the 'variable' in our local frame + z = stu__cc2(ST_id, z); // now access that dictionary + return stu__cc3('[', z, stu__cdt()); // now access the variable from that dir +} + +static stua_obj stu__funcdef(stua_obj *id, stua_obj *func); +static int stu__expr(int p) +{ + int z; + // unary + switch (stu__tok) { + case ST_number: z = stu__cdt(); break; + case ST_string: z = stu__cdt(); break; // @TODO - string concatenation like C + case ST_id : z = stu__comp_id(); break; + case ST__frame: z = stu__cc1(ST__frame); stu__nexttoken(); break; + case ST_func : z = stu__funcdef(NULL,NULL); break; + case ST_if : z = stu__parse_if(); break; + case ST_nil : z = stu__cdv(stua_nil); stu__nexttoken(); break; + case ST_true : z = stu__cdv(stua_true); stu__nexttoken(); break; + case ST_false : z = stu__cdv(stua_false); stu__nexttoken(); break; + case '-' : z = stu__nexpr(99); if (z) z=stu__cc2(STU__negate,z); else return z; break; + case '!' : z = stu__nexpr(99); if (z) z=stu__cc2('!',z); else return z; break; + case '~' : z = stu__nexpr(99); if (z) z=stu__cc2('~',z); else return z; break; + case '{' : z = stu__dictdef('}', NULL); break; + default : return stu__err("Unexpected token"); + case '(' : stu__nexttoken(); z = stu__statements(0,0); if (!stu__demand(')')) return stu__err("Expecting )"); + } + return stu__postexpr(z,p); +} + +static int stu__postexpr(int z, int p) +{ + int q; + // postfix + while (stu__tok == '(' || stu__tok == '[' || stu__tok == '.') { + if (stu__accept('.')) { + // MUST be followed by a plain identifier! use [] for other stuff + if (stu__tok != ST_id) return stu__err("Must follow . with plain name; try [] instead"); + z = stu__cc3('[', z, stu__cdv(stu__tokval)); + stu__nexttoken(); + } else if (stu__accept('[')) { + while (stu__tok != ']') { + int r = stu__expr(1); if (!r) return 0; + z = stu__cc3('[', z, r); + if (!stu__accept(',')) break; + } + if (!stu__demand(']')) return stu__err("Expecting ]"); + } else { + int n, p = stu__dictdef(')', &n); if (!p) return 0; + #if 0 // this is incorrect! + if (z > 0 && stu__pfunc.code[z] == ST_id) { + stua_obj q = stu__get(stu__globaldict, stu__pfunc.data[-stu__pfunc.code[z+1]-1], stua_nil); + if (stu__checkt(STU___function, q)) + if ((stu__pf(q))->num_param != n) + return stu__err("Incorrect number of parameters"); + } + #endif + z = stu__cc3('(', z, p); + } + } + // binop - this implementation taken from lcc + for (q=stu__prec[stu__tok]; q >= p; --q) { + while (stu__prec[stu__tok] == q) { + int o = stu__tok, y = stu__nexpr(p+1); if (!y) return 0; + z = stu__cc3(o,z,y); + } + } + return z; +} + +static stua_obj stu__finish_func(stua_obj *param, int start) +{ + int n, size; + stu__func *f = (stu__func *) malloc(sizeof(*f)); + f->closure_source = 0; + f->num_param = stb_arr_len(param); + f->param = (int *) stb_copy(param, f->num_param * sizeof(*f->param)); + size = stb_arr_storage(stu__pfunc.code) + stb_arr_storage(stu__pfunc.data) + sizeof(*f) + 8; + f->f.store = malloc(stb_arr_storage(stu__pfunc.code) + stb_arr_storage(stu__pfunc.data)); + f->code = (short *) ((char *) f->f.store + stb_arr_storage(stu__pfunc.data)); + memcpy(f->code, stu__pfunc.code, stb_arr_storage(stu__pfunc.code)); + f->code[1] = start; + f->code[0] = stb_arr_len(stu__pfunc.data); + for (n=0; n < f->code[0]; ++n) + ((stua_obj *) f->code)[-1-n] = stu__pfunc.data[n]; + return stu__makeobj(STU___function, f, size, 0); +} + +static int stu__funcdef(stua_obj *id, stua_obj *result) +{ + int n,z=0,i,q; + stua_obj *param = NULL; + short *nonlocal; + stua_obj v,f=stua_nil; + assert(stu__tok == ST_func); + stu__nexttoken(); + if (id) { + if (!stu__demandv(ST_id, id)) return stu__err("Expecting function name"); + } else + stu__accept(ST_id); + if (!stu__demand('(')) return stu__err("Expecting ( for function parameter"); + stu__push_func_comp(); + while (stu__tok != ')') { + if (!stu__demandv(ST_id, &v)) { z=stu__err("Expecting parameter name"); goto done; } + stb_idict_add(stu__pfunc.locals, v, 1); + if (stu__tok == '=') { + n = stu__nexpr(1); if (!n) { z=0; goto done; } + z = stu__seq(z, stu__cc3(STU__defaultparm, stu__cdv(v), n)); + } else + stb_arr_push(param, v); + if (!stu__accept(',')) break; + } + if (!stu__demand(')')) { z=stu__err("Expecting ) at end of parameter list"); goto done; } + n = stu__statements(0,0); if (!n) { z=0; goto done; } + if (!stu__demand(ST_end)) { z=stu__err("Expecting END at end of function"); goto done; } + if (n == 1) n = 0; + n = stu__seq(z,n); + f = stu__finish_func(param, n); + if (result) { *result = f; z=1; stu__pop_func_comp(); } + else { + nonlocal = stu__pfunc.non_local_refs; + stu__pfunc.non_local_refs = NULL; + stu__pop_func_comp(); + z = stu__cdv(f); + if (nonlocal) { // build a closure with references to the needed frames + short *initcode = NULL; + for (i=0; i < stb_arr_len(nonlocal); ++i) { + int k = nonlocal[i], p; + stb_arr_push(initcode, stu__cdv(k)); + if (k == -1) p = stu__cc1(ST__frame); + else { p = stu__cdv(stu__makeint(k+1)); p = stu__cc2(ST_id, p); } + stb_arr_push(initcode, p); + } + q = stu__cc2('{', stb_arr_len(nonlocal)); + for (i=0; i < stb_arr_len(initcode); ++i) + stu__cc(initcode[i]); + z = stu__cc3('+', z, q); + stb_arr_free(initcode); + } + stb_arr_free(nonlocal); + } +done: + stb_arr_free(param); + if (!z) stu__pop_func_comp(); + return z; +} + +static int stu__compile_global_scope(void) +{ + stua_obj o; + int z=0; + + stu__push_func_comp(); + while (stu__tok != 0) { + if (stu__tok == ST_func) { + stua_obj id, f; + if (!stu__funcdef(&id,&f)) + goto error; + stu__set(stu__globaldict, id, f); + } else if (stu__tok == ST_var) { + z = stu__varinit(z,1); if (!z) goto error; + } else { + int y = stu__statements(1,0); if (!y) goto error; + z = stu__seq(z,y); + } + stu__accept(';'); + } + o = stu__finish_func(NULL, z); + stu__pop_func_comp(); + + o = stu__funceval(o, stua_globals); // initialize stu__globaldict + if (stu__flow == FLOW_error) + printf("Error: %s\n", ((stu__wrapper *) stu__ptr(stu__flow_val))->ptr); + return 1; +error: + stu__pop_func_comp(); + return 0; +} + +stua_obj stu__myprint(stua_dict *context) +{ + stua_obj x = stu__get(context, stua_string("x"), stua_nil); + if ((x & 1) == stu__float_tag) printf("%f", stu__getfloat(x)); + else if (stu__tag(x) == stu__int_tag) printf("%d", stu__int(x)); + else { + stu__wrapper *s = stu__pw(x); + if (s->type == STU___string || s->type == STU___error) + printf("%s", s->ptr); + else if (s->type == STU___dict) printf("{{dictionary}}"); + else if (s->type == STU___function) printf("[[function]]"); + else + printf("[[ERROR:%s]]", s->ptr); + } + return x; +} + +void stua_init(void) +{ + if (!stu__globaldict) { + int i; + stua_obj s; + stu__func *f; + + stu__prec[ST_and] = stu__prec[ST_or] = 1; + stu__prec[ST_eq ] = stu__prec[ST_ne] = stu__prec[ST_le] = + stu__prec[ST_ge] = stu__prec['>' ] = stu__prec['<'] = 2; + stu__prec[':'] = 3; + stu__prec['&'] = stu__prec['|'] = stu__prec['^'] = 4; + stu__prec['+'] = stu__prec['-'] = 5; + stu__prec['*'] = stu__prec['/'] = stu__prec['%'] = + stu__prec[ST_shl]= stu__prec[ST_shr]= stu__prec[ST_shru]= 6; + + stu__end[')'] = stu__end[ST_end] = stu__end[ST_else] = 1; + stu__end[ST_do] = stu__end[ST_elseif] = 1; + + stu__float_init(); + stu__lex_matcher = stb_lex_matcher(); + for (i=0; i < sizeof(stu__lexemes)/sizeof(stu__lexemes[0]); ++i) + stb_lex_item(stu__lex_matcher, stu__lexemes[i].regex, stu__lexemes[i].stu__tok); + + stu__globaldict = stb_idict_new_size(64); + stua_globals = stu__makeobj(STU___dict, stu__globaldict, 0,0); + stu__strings = stb_sdict_new(0); + + stu__curbuf = stu__bufstart = "func _print(x) end\n" + "func print()\n var x=0 while _frame[x] != nil as x=x+1 do _print(_frame[x]) end end\n"; + stu__nexttoken(); + if (!stu__compile_global_scope()) + printf("Compile error in line %d: %s\n", stu__comp_err_line, stu__comp_err_str); + + s = stu__get(stu__globaldict, stua_string("_print"), stua_nil); + if (stu__tag(s) == stu__ptr_tag && stu__ptr(s)->type == STU___function) { + f = stu__pf(s); + free(f->f.store); + f->closure_source = 4; + f->f.func = stu__myprint; + f->code = NULL; + } + } +} + +void stua_uninit(void) +{ + if (stu__globaldict) { + stb_idict_remove_all(stu__globaldict); + stb_arr_setlen(stu__gc_root_stack, 0); + stua_gc(1); + stb_idict_destroy(stu__globaldict); + stb_sdict_delete(stu__strings); + stb_matcher_free(stu__lex_matcher); + stb_arr_free(stu__gc_ptrlist); + stb_arr_free(func_stack); + stb_arr_free(stu__gc_root_stack); + stu__globaldict = NULL; + } +} + +void stua_run_script(char *s) +{ + stua_init(); + + stu__curbuf = stu__bufstart = s; + stu__nexttoken(); + + stu__flow = FLOW_normal; + + if (!stu__compile_global_scope()) + printf("Compile error in line %d: %s\n", stu__comp_err_line, stu__comp_err_str); + stua_gc(1); +} +#endif // STB_DEFINE + +#endif // STB_STUA + + +#undef STB_EXTERN +#endif // STB_INCLUDE_STB_H + diff --git a/impeller/third_party/stb/stb/stb_c_lexer.h b/impeller/third_party/stb/stb/stb_c_lexer.h new file mode 100644 index 0000000000000..e91c6c3c4813f --- /dev/null +++ b/impeller/third_party/stb/stb/stb_c_lexer.h @@ -0,0 +1,816 @@ +// stb_c_lexer.h - v0.07 - public domain Sean Barrett 2013 +// lexer for making little C-like languages with recursive-descent parsers +// +// This file provides both the interface and the implementation. +// To instantiate the implementation, +// #define STB_C_LEXER_IMPLEMENTATION +// in *ONE* source file, before #including this file. +// +// The default configuration is fairly close to a C lexer, although +// suffixes on integer constants are not handled (you can override this). +// +// History: +// 0.07 fix mishandling of hexadecimal constants parsed by strtol +// 0.06 fix missing next character after ending quote mark (Andreas Fredriksson) +// 0.05 refixed get_location because github version had lost the fix +// 0.04 fix octal parsing bug +// 0.03 added STB_C_LEX_DISCARD_PREPROCESSOR option +// refactor API to simplify (only one struct instead of two) +// change literal enum names to have 'lit' at the end +// 0.02 first public release +// +// Status: +// - haven't tested compiling as C++ +// - haven't tested the float parsing path +// - haven't tested the non-default-config paths (e.g. non-stdlib) +// - only tested default-config paths by eyeballing output of self-parse +// +// - haven't implemented multiline strings +// - haven't implemented octal/hex character constants +// - haven't implemented support for unicode CLEX_char +// - need to expand error reporting so you don't just get "CLEX_parse_error" +// +// LICENSE +// +// This software is dual-licensed to the public domain and under the following +// license: you are granted a perpetual, irrevocable license to copy, modify, +// publish, and distribute this file as you see fit. + +#ifndef STB_C_LEXER_DEFINITIONS +// to change the default parsing rules, copy the following lines +// into your C/C++ file *before* including this, and then replace +// the Y's with N's for the ones you don't want. +// --BEGIN-- + +#define STB_C_LEX_C_DECIMAL_INTS Y // "0|[1-9][0-9]*" CLEX_intlit +#define STB_C_LEX_C_HEX_INTS Y // "0x[0-9a-fA-F]+" CLEX_intlit +#define STB_C_LEX_C_OCTAL_INTS Y // "[0-7]+" CLEX_intlit +#define STB_C_LEX_C_DECIMAL_FLOATS Y // "[0-9]*(.[0-9]*([eE]-?[0-9]+)?) CLEX_floatlit +#define STB_C_LEX_C_IDENTIFIERS Y // "[_a-zA-Z][_a-zA-Z0-9]*" CLEX_id +#define STB_C_LEX_C_DQ_STRINGS Y // double-quote-delimited strings with escapes CLEX_dqstring +#define STB_C_LEX_C_SQ_STRINGS N // single-quote-delimited strings with escapes CLEX_ssstring +#define STB_C_LEX_C_CHARS Y // single-quote-delimited character with escape CLEX_charlits +#define STB_C_LEX_C_COMMENTS Y // "/* comment */" +#define STB_C_LEX_CPP_COMMENTS Y // "// comment to end of line\n" +#define STB_C_LEX_C_COMPARISONS Y // "==" CLEX_eq "!=" CLEX_noteq "<=" CLEX_lesseq ">=" CLEX_greatereq +#define STB_C_LEX_C_LOGICAL Y // "&&" CLEX_andand "||" CLEX_oror +#define STB_C_LEX_C_SHIFTS Y // "<<" CLEX_shl ">>" CLEX_shr +#define STB_C_LEX_C_INCREMENTS Y // "++" CLEX_plusplus "--" CLEX_minusminus +#define STB_C_LEX_C_ARROW Y // "->" CLEX_arrow +#define STB_C_LEX_EQUAL_ARROW N // "=>" CLEX_eqarrow +#define STB_C_LEX_C_BITWISEEQ Y // "&=" CLEX_andeq "|=" CLEX_oreq "^=" CLEX_xoreq +#define STB_C_LEX_C_ARITHEQ Y // "+=" CLEX_pluseq "-=" CLEX_minuseq + // "*=" CLEX_muleq "/=" CLEX_diveq "%=" CLEX_modeq + // if both STB_C_LEX_SHIFTS & STB_C_LEX_ARITHEQ: + // "<<=" CLEX_shleq ">>=" CLEX_shreq + +#define STB_C_LEX_PARSE_SUFFIXES N // letters after numbers are parsed as part of those numbers, and must be in suffix list below +#define STB_C_LEX_DECIMAL_SUFFIXES "" // decimal integer suffixes e.g. "uUlL" -- these are returned as-is in string storage +#define STB_C_LEX_HEX_SUFFIXES "" // e.g. "uUlL" +#define STB_C_LEX_OCTAL_SUFFIXES "" // e.g. "uUlL" +#define STB_C_LEX_FLOAT_SUFFIXES "" // + +#define STB_C_LEX_0_IS_EOF N // if Y, ends parsing at '\0'; if N, returns '\0' as token +#define STB_C_LEX_INTEGERS_AS_DOUBLES N // parses integers as doubles so they can be larger than 'int', but only if STB_C_LEX_STDLIB==N +#define STB_C_LEX_MULTILINE_DSTRINGS N // allow newlines in double-quoted strings +#define STB_C_LEX_MULTILINE_SSTRINGS N // allow newlines in single-quoted strings +#define STB_C_LEX_USE_STDLIB Y // use strtod,strtol for parsing #s; otherwise inaccurate hack +#define STB_C_LEX_DOLLAR_IDENTIFIER Y // allow $ as an identifier character +#define STB_C_LEX_FLOAT_NO_DECIMAL Y // allow floats that have no decimal point if they have an exponent + +#define STB_C_LEX_DEFINE_ALL_TOKEN_NAMES N // if Y, all CLEX_ token names are defined, even if never returned + // leaving it as N should help you catch config bugs + +#define STB_C_LEX_DISCARD_PREPROCESSOR Y // discard C-preprocessor directives (e.g. after prepocess + // still have #line, #pragma, etc) + +//#define STB_C_LEX_ISWHITE(str) ... // return length in bytes of first character if it is whitespace + +#define STB_C_LEXER_DEFINITIONS // This line prevents the header file from replacing your definitions +// --END-- + +#endif + +#ifndef INCLUDE_STB_C_LEXER_H +#define INCLUDE_STB_C_LEXER_H + +typedef struct +{ + // lexer variables + char *input_stream; + char *eof; + char *parse_point; + char *string_storage; + int string_storage_len; + + // lexer parse location for error messages + char *where_firstchar; + char *where_lastchar; + + // lexer token variables + long token; + double real_number; + long int_number; + char *string; + int string_len; +} stb_lexer; + +typedef struct +{ + int line_number; + int line_offset; +} stb_lex_location; + +#ifdef __cplusplus +extern "C" { +#endif + +extern void stb_c_lexer_init(stb_lexer *lexer, const char *input_stream, const char *input_stream_end, char *string_store, int store_length); +// this function initialize the 'lexer' structure +// Input: +// - input_stream points to the file to parse, loaded into memory +// - input_stream_end points to the end of the file, or NULL if you use 0-for-EOF +// - string_store is storage the lexer can use for storing parsed strings and identifiers +// - store_length is the length of that storage + +extern int stb_c_lexer_get_token(stb_lexer *lexer); +// this function returns non-zero if a token is parsed, or 0 if at EOF +// Output: +// - lexer->token is the token ID, which is unicode code point for a single-char token, < 0 for a multichar or eof or error +// - lexer->real_number is a double constant value for CLEX_floatlit, or CLEX_intlit if STB_C_LEX_INTEGERS_AS_DOUBLES +// - lexer->int_number is an integer constant for CLEX_intlit if !STB_C_LEX_INTEGERS_AS_DOUBLES, or character for CLEX_charlit +// - lexer->string is a 0-terminated string for CLEX_dqstring or CLEX_sqstring or CLEX_identifier +// - lexer->string_len is the byte length of lexer->string + +extern void stb_c_lexer_get_location(const stb_lexer *lexer, const char *where, stb_lex_location *loc); +// this inefficient function returns the line number and character offset of a +// given location in the file as returned by stb_lex_token. Because it's inefficient, +// you should only call it for errors, not for every token. +// For error messages of invalid tokens, you typically want the location of the start +// of the token (which caused the token to be invalid). For bugs involving legit +// tokens, you can report the first or the range. +// Output: +// - loc->line_number is the line number in the file, counting from 1, of the location +// - loc->line_offset is the char-offset in the line, counting from 0, of the location + + +#ifdef __cplusplus +} +#endif + +#endif // INCLUDE_STB_C_LEXER_H + +#ifdef STB_C_LEXER_IMPLEMENTATION + + #if defined(Y) || defined(N) + #error "Can only use stb_c_lexer in contexts where the preprocessor symbols 'Y' and 'N' are not defined" + #endif + + +// Hacky definitions so we can easily #if on them +#define Y(x) 1 +#define N(x) 0 + +#if STB_C_LEX_USE_STDLIB(x) +#define STB__CLEX_use_stdlib +#include +#endif + +#if STB_C_LEX_INTEGERS_AS_DOUBLES(x) +typedef double stb__clex_int; +#define intfield real_number +#define STB__clex_int_as_double +#else +typedef long stb__clex_int; +#define intfield int_number +#endif + +// Convert these config options to simple conditional #defines so we can more +// easily test them once we've change the meaning of Y/N + +#if STB_C_LEX_PARSE_SUFFIXES(x) +#define STB__clex_parse_suffixes +#endif + +#if STB_C_LEX_C_DECIMAL_INTS(x) || STB_C_LEX_C_HEX_INTS(x) || STB_C_LEX_DEFINE_ALL_TOKEN_NAMES(x) +#define STB__clex_define_int +#endif + +#if (STB_C_LEX_C_ARITHEQ(x) && STB_C_LEX_C_SHIFTS(x)) || STB_C_LEX_DEFINE_ALL_TOKEN_NAMES(x) +#define STB__clex_define_shifts +#endif + +#if STB_C_LEX_C_HEX_INTS(x) +#define STB__clex_hex_ints +#endif + +#if STB_C_LEX_C_DECIMAL_INTS(x) +#define STB__clex_decimal_ints +#endif + +#if STB_C_LEX_C_OCTAL_INTS(x) +#define STB__clex_octal_ints +#endif + +#if STB_C_LEX_C_DECIMAL_FLOATS(x) +#define STB__clex_decimal_floats +#endif + +#if STB_C_LEX_DISCARD_PREPROCESSOR(x) +#define STB__clex_discard_preprocessor +#endif + +// Now pick a definition of Y/N that's conducive to +// defining the enum of token names. +#if STB_C_LEX_DEFINE_ALL_TOKEN_NAMES(x) || defined(STB_C_LEXER_SELF_TEST) + #undef N + #define N(a) Y(a) +#else + #undef N + #define N(a) +#endif + +#undef Y +#define Y(a) a, + +enum +{ + CLEX_eof = 256, + CLEX_parse_error, + +#ifdef STB__clex_define_int + CLEX_intlit, +#endif + + STB_C_LEX_C_DECIMAL_FLOATS( CLEX_floatlit ) + STB_C_LEX_C_IDENTIFIERS( CLEX_id ) + STB_C_LEX_C_DQ_STRINGS( CLEX_dqstring ) + STB_C_LEX_C_SQ_STRINGS( CLEX_sqstring ) + STB_C_LEX_C_CHARS( CLEX_charlit ) + STB_C_LEX_C_COMPARISONS( CLEX_eq ) + STB_C_LEX_C_COMPARISONS( CLEX_noteq ) + STB_C_LEX_C_COMPARISONS( CLEX_lesseq ) + STB_C_LEX_C_COMPARISONS( CLEX_greatereq ) + STB_C_LEX_C_LOGICAL( CLEX_andand ) + STB_C_LEX_C_LOGICAL( CLEX_oror ) + STB_C_LEX_C_SHIFTS( CLEX_shl ) + STB_C_LEX_C_SHIFTS( CLEX_shr ) + STB_C_LEX_C_INCREMENTS( CLEX_plusplus ) + STB_C_LEX_C_INCREMENTS( CLEX_minusminus ) + STB_C_LEX_C_ARITHEQ( CLEX_pluseq ) + STB_C_LEX_C_ARITHEQ( CLEX_minuseq ) + STB_C_LEX_C_ARITHEQ( CLEX_muleq ) + STB_C_LEX_C_ARITHEQ( CLEX_diveq ) + STB_C_LEX_C_ARITHEQ( CLEX_modeq ) + STB_C_LEX_C_BITWISEEQ( CLEX_andeq ) + STB_C_LEX_C_BITWISEEQ( CLEX_oreq ) + STB_C_LEX_C_BITWISEEQ( CLEX_xoreq ) + STB_C_LEX_C_ARROW( CLEX_arrow ) + STB_C_LEX_EQUAL_ARROW( CLEX_eqarrow ) + +#ifdef STB__clex_define_shifts + CLEX_shleq, CLEX_shreq, +#endif + + CLEX_first_unused_token + +#undef Y +#define Y(a) a +}; + +// Now for the rest of the file we'll use the basic definition where +// where Y expands to its contents and N expands to nothing +#undef N +#define N(a) + +// API function +void stb_c_lexer_init(stb_lexer *lexer, const char *input_stream, const char *input_stream_end, char *string_store, int store_length) +{ + lexer->input_stream = (char *) input_stream; + lexer->eof = (char *) input_stream_end; + lexer->parse_point = (char *) input_stream; + lexer->string_storage = string_store; + lexer->string_storage_len = store_length; +} + +// API function +void stb_c_lexer_get_location(const stb_lexer *lexer, const char *where, stb_lex_location *loc) +{ + char *p = lexer->input_stream; + int line_number = 1; + int char_offset = 0; + while (*p && p < where) { + if (*p == '\n' || *p == '\r') { + p += (p[0]+p[1] == '\r'+'\n' ? 2 : 1); // skip newline + line_number += 1; + char_offset = 0; + } else { + ++p; + ++char_offset; + } + } + loc->line_number = line_number; + loc->line_offset = char_offset; +} + +// main helper function for returning a parsed token +static int stb__clex_token(stb_lexer *lexer, int token, char *start, char *end) +{ + lexer->token = token; + lexer->where_firstchar = start; + lexer->where_lastchar = end; + lexer->parse_point = end+1; + return 1; +} + +// helper function for returning eof +static int stb__clex_eof(stb_lexer *lexer) +{ + lexer->token = CLEX_eof; + return 0; +} + +static int stb__clex_iswhite(int x) +{ + return x == ' ' || x == '\t' || x == '\r' || x == '\n' || x == '\f'; +} + +static const char *stb__strchr(const char *str, int ch) +{ + for (; *str; ++str) + if (*str == ch) + return str; + return 0; +} + +// parse suffixes at the end of a number +static int stb__clex_parse_suffixes(stb_lexer *lexer, long tokenid, char *start, char *cur, const char *suffixes) +{ + #ifdef STB__clex_parse_suffixes + lexer->string = lexer->string_storage; + lexer->string_len = 0; + + while ((*cur >= 'a' && *cur <= 'z') || (*cur >= 'A' && *cur <= 'Z')) { + if (stb__strchr(suffixes, *cur) == 0) + return stb__clex_token(lexer, CLEX_parse_error, start, cur); + if (lexer->string_len+1 >= lexer->string_storage_len) + return stb__clex_token(lexer, CLEX_parse_error, start, cur); + lexer->string[lexer->string_len++] = *cur++; + } + #else + suffixes = suffixes; // attempt to suppress warnings + #endif + return stb__clex_token(lexer, tokenid, start, cur-1); +} + +#ifndef STB__CLEX_use_stdlib +static double stb__clex_parse_float(char *p, char **q) +{ + double value=0; + while (*p >= '0' && *p <= '9') + value = value*10 + (*p++ - '0'); + if (*p == '.') { + double powten=1, addend = 0; + ++p; + while (*p >= '0' && *p <= '9') { + addend = addend + 10*(*p++ - '0'); + powten *= 10; + } + value += addend / powten; + } + if (*p == 'e' || *p == 'E') { + int sign = p[1] == '-'; + int exponent=0; + double pow10=1; + p += 1+sign; + while (*p >= '0' && *p <= '9') + exponent = exponent*10 + (*p++ - '0'); + // can't use pow() from stdlib, so do it slow way + while (exponent-- > 0) + pow10 *= 10; + if (sign) + value /= pow10; + else + value *= pow10; + } + *q = p; + return value; +} +#endif + +static int stb__clex_parse_char(char *p, char **q) +{ + if (*p == '\\') { + *q = p+2; // tentatively guess we'll parse two characters + switch(p[1]) { + case '\\': return '\\'; + case '\'': return '\''; + case '"': return '"'; + case 't': return '\t'; + case 'f': return '\f'; + case 'n': return '\n'; + case 'r': return '\r'; + case '0': return '\0'; // @TODO ocatal constants + case 'x': case 'X': return -1; // @TODO hex constants + case 'u': return -1; // @TODO unicode constants + } + } + *q = p+1; + return (unsigned char) *p; +} + +static int stb__clex_parse_string(stb_lexer *lexer, char *p, int type) +{ + char *start = p; + char delim = *p++; // grab the " or ' for later matching + char *out = lexer->string_storage; + char *outend = lexer->string_storage + lexer->string_storage_len; + while (*p != delim) { + int n; + if (*p == '\\') { + char *q; + n = stb__clex_parse_char(p, &q); + if (n < 0) + return stb__clex_token(lexer, CLEX_parse_error, start, q); + p = q; + } else { + // @OPTIMIZE: could speed this up by looping-while-not-backslash + n = (unsigned char) *p++; + } + if (out+1 > outend) + return stb__clex_token(lexer, CLEX_parse_error, start, p); + // @TODO expand unicode escapes to UTF8 + *out++ = (char) n; + } + *out = 0; + lexer->string = lexer->string_storage; + lexer->string_len = out - lexer->string_storage; + return stb__clex_token(lexer, type, start, p); +} + +int stb_c_lexer_get_token(stb_lexer *lexer) +{ + char *p = lexer->parse_point; + + // skip whitespace and comments + for (;;) { + #ifdef STB_C_LEX_ISWHITE + while (p != lexer->stream_end) { + int n; + n = STB_C_LEX_ISWHITE(p); + if (n == 0) break; + if (lexer->eof && lexer+n > lexer->eof) + return stb__clex_token(tok, CLEX_parse_error, p,lexer->eof-1); + p += n; + } + #else + while (p != lexer->eof && stb__clex_iswhite(*p)) + ++p; + #endif + + STB_C_LEX_CPP_COMMENTS( + if (p != lexer->eof && p[0] == '/' && p[1] == '/') { + while (p != lexer->eof && *p != '\r' && *p != '\n') + ++p; + continue; + } + ) + + STB_C_LEX_C_COMMENTS( + if (p != lexer->eof && p[0] == '/' && p[1] == '*') { + char *start = p; + p += 2; + while (p != lexer->eof && (p[0] != '*' || p[1] != '/')) + ++p; + if (p == lexer->eof) + return stb__clex_token(lexer, CLEX_parse_error, start, p-1); + p += 2; + continue; + } + ) + + #ifdef STB__clex_discard_preprocessor + // @TODO this discards everything after a '#', regardless + // of where in the line the # is, rather than requiring it + // be at the start. (because this parser doesn't otherwise + // check for line breaks!) + if (p != lexer->eof && p[0] == '#') { + while (p != lexer->eof && *p != '\r' && *p != '\n') + ++p; + continue; + } + #endif + + break; + } + + if (p == lexer->eof) + return stb__clex_eof(lexer); + + switch (*p) { + default: + if ( (*p >= 'a' && *p <= 'z') + || (*p >= 'A' && *p <= 'Z') + || *p == '_' || (unsigned char) *p >= 128 // >= 128 is UTF8 char + STB_C_LEX_DOLLAR_IDENTIFIER( || *p == '$' ) ) + { + int n = 0; + lexer->string = lexer->string_storage; + lexer->string_len = n; + do { + if (n+1 >= lexer->string_storage_len) + return stb__clex_token(lexer, CLEX_parse_error, p, p+n); + lexer->string[n] = p[n]; + ++n; + } while ( + (p[n] >= 'a' && p[n] <= 'z') + || (p[n] >= 'A' && p[n] <= 'Z') + || (p[n] >= '0' && p[n] <= '9') // allow digits in middle of identifier + || p[n] == '_' || (unsigned char) p[n] >= 128 + STB_C_LEX_DOLLAR_IDENTIFIER( || p[n] == '$' ) + ); + lexer->string[n] = 0; + return stb__clex_token(lexer, CLEX_id, p, p+n-1); + } + + // check for EOF + STB_C_LEX_0_IS_EOF( + if (*p == 0) + return stb__clex_eof(tok); + ) + + single_char: + // not an identifier, return the character as itself + return stb__clex_token(lexer, *p, p, p); + + case '+': + if (p+1 != lexer->eof) { + STB_C_LEX_C_INCREMENTS(if (p[1] == '+') return stb__clex_token(lexer, CLEX_plusplus, p,p+1);) + STB_C_LEX_C_ARITHEQ( if (p[1] == '=') return stb__clex_token(lexer, CLEX_pluseq , p,p+1);) + } + goto single_char; + case '-': + if (p+1 != lexer->eof) { + STB_C_LEX_C_INCREMENTS(if (p[1] == '-') return stb__clex_token(lexer, CLEX_minusminus, p,p+1);) + STB_C_LEX_C_ARITHEQ( if (p[1] == '=') return stb__clex_token(lexer, CLEX_minuseq , p,p+1);) + STB_C_LEX_C_ARROW( if (p[1] == '>') return stb__clex_token(lexer, CLEX_arrow , p,p+1);) + } + goto single_char; + case '&': + if (p+1 != lexer->eof) { + STB_C_LEX_C_LOGICAL( if (p[1] == '&') return stb__clex_token(lexer, CLEX_andand, p,p+1);) + STB_C_LEX_C_BITWISEEQ(if (p[1] == '=') return stb__clex_token(lexer, CLEX_andeq , p,p+1);) + } + goto single_char; + case '|': + if (p+1 != lexer->eof) { + STB_C_LEX_C_LOGICAL( if (p[1] == '|') return stb__clex_token(lexer, CLEX_oror, p,p+1);) + STB_C_LEX_C_BITWISEEQ(if (p[1] == '=') return stb__clex_token(lexer, CLEX_oreq, p,p+1);) + } + goto single_char; + case '=': + if (p+1 != lexer->eof) { + STB_C_LEX_C_COMPARISONS(if (p[1] == '=') return stb__clex_token(lexer, CLEX_eq, p,p+1);) + STB_C_LEX_EQUAL_ARROW( if (p[1] == '>') return stb__clex_token(lexer, CLEX_eqarrow, p,p+1);) + } + goto single_char; + case '!': + STB_C_LEX_C_COMPARISONS(if (p+1 != lexer->eof && p[1] == '=') return stb__clex_token(lexer, CLEX_noteq, p,p+1);) + goto single_char; + case '^': + STB_C_LEX_C_BITWISEEQ(if (p+1 != lexer->eof && p[1] == '=') return stb__clex_token(lexer, CLEX_xoreq, p,p+1)); + goto single_char; + case '%': + STB_C_LEX_C_ARITHEQ(if (p+1 != lexer->eof && p[1] == '=') return stb__clex_token(lexer, CLEX_modeq, p,p+1)); + goto single_char; + case '*': + STB_C_LEX_C_ARITHEQ(if (p+1 != lexer->eof && p[1] == '=') return stb__clex_token(lexer, CLEX_muleq, p,p+1)); + goto single_char; + case '/': + STB_C_LEX_C_ARITHEQ(if (p+1 != lexer->eof && p[1] == '=') return stb__clex_token(lexer, CLEX_diveq, p,p+1)); + goto single_char; + case '<': + if (p+1 != lexer->eof) { + STB_C_LEX_C_COMPARISONS(if (p[1] == '=') return stb__clex_token(lexer, CLEX_lesseq, p,p+1);) + STB_C_LEX_C_SHIFTS( if (p[1] == '<') { + STB_C_LEX_C_ARITHEQ(if (p+2 != lexer->eof && p[2] == '=') + return stb__clex_token(lexer, CLEX_shleq, p,p+2);) + return stb__clex_token(lexer, CLEX_shl, p,p+1); + } + ) + } + goto single_char; + case '>': + if (p+1 != lexer->eof) { + STB_C_LEX_C_COMPARISONS(if (p[1] == '=') return stb__clex_token(lexer, CLEX_greatereq, p,p+1);) + STB_C_LEX_C_SHIFTS( if (p[1] == '>') { + STB_C_LEX_C_ARITHEQ(if (p+2 != lexer->eof && p[2] == '=') + return stb__clex_token(lexer, CLEX_shreq, p,p+2);) + return stb__clex_token(lexer, CLEX_shr, p,p+1); + } + ) + } + goto single_char; + + case '"': + STB_C_LEX_C_DQ_STRINGS(return stb__clex_parse_string(lexer, p, CLEX_dqstring);) + goto single_char; + case '\'': + STB_C_LEX_C_SQ_STRINGS(return stb__clex_parse_string(lexer, p, CLEX_sqstring);) + STB_C_LEX_C_CHARS( + { + char *start = p; + lexer->int_number = stb__clex_parse_char(p+1, &p); + if (lexer->int_number < 0) + return stb__clex_token(lexer, CLEX_parse_error, start,start); + if (p == lexer->eof || *p != '\'') + return stb__clex_token(lexer, CLEX_parse_error, start,p); + return stb__clex_token(lexer, CLEX_charlit, start, p+1); + }) + goto single_char; + + case '0': + #ifdef STB__clex_hex_ints + if (p+1 != lexer->eof) { + if (p[1] == 'x' || p[1] == 'X') { + char *q = p+2; + #ifdef STB__CLEX_use_stdlib + lexer->int_number = strtol((char *) p, (char **) &q, 16); + #else + stb__clex_int n=0; + while (q != lexer->eof) { + if (*q >= '0' && *q <= '9') + n = n*16 + (*q - '0'); + else if (*q >= 'a' && *q <= 'f') + n = n*16 + (*q - 'a') + 10; + else if (*q >= 'A' && *q <= 'F') + n = n*16 + (*q - 'A') + 10; + else + break; + ++q; + } + lexer->int_field = n; // int_field is macro that expands to real_number/int_number depending on type of n + #endif + if (q == p+2) + return stb__clex_token(lexer, CLEX_parse_error, p-2,p-1); + return stb__clex_parse_suffixes(lexer, CLEX_intlit, p,q, STB_C_LEX_HEX_SUFFIXES); + } + } + #endif // STB__clex_hex_ints + // can't test for octal because we might parse '0.0' as float or as '0' '.' '0', + // so have to do float first + + /* FALL THROUGH */ + case '1': case '2': case '3': case '4': case '5': case '6': case '7': case '8': case '9': + + #ifdef STB__clex_decimal_floats + { + char *q = p; + while (q != lexer->eof && (*q >= '0' && *q <= '9')) + ++q; + if (q != lexer->eof) { + if (*q == '.' STB_C_LEX_FLOAT_NO_DECIMAL(|| *q == 'e' || *q == 'E')) { + #ifdef STB__CLEX_use_stdlib + lexer->real_number = strtod((char *) p, (char**) &q); + #else + lexer->real_number = stb__clex_parse_float(p, &q); + #endif + + return stb__clex_parse_suffixes(lexer, CLEX_floatlit, p,q, STB_C_LEX_FLOAT_SUFFIXES); + + } + } + } + #endif // STB__clex_decimal_floats + + #ifdef STB__clex_octal_ints + if (p[0] == '0') { + char *q = p; + #ifdef STB__CLEX_use_stdlib + lexer->int_number = strtol((char *) p, (char **) &q, 8); + #else + stb__clex_int n=0; + while (q != lexer->eof) { + if (*q >= '0' && *q <= '7') + n = n*8 + (q - '0'); + else + break; + ++q; + } + if (q != lexer->eof && (*q == '8' || *q=='9')) + return stb__clex_token(tok, CLEX_parse_error, p, q); + lexer->int_field = n; + #endif + return stb__clex_parse_suffixes(lexer, CLEX_intlit, p,q, STB_C_LEX_OCTAL_SUFFIXES); + } + #endif // STB__clex_octal_ints + + #ifdef STB__clex_decimal_ints + { + char *q = p; + #ifdef STB__CLEX_use_stdlib + lexer->int_number = strtol((char *) p, (char **) &q, 10); + #else + stb__clex_int n=0; + while (q != lexer->eof) { + if (*q >= '0' && *q <= '9') + n = n*10 + (q - '0'); + else + break; + ++q; + } + lexer->int_field = n; + #endif + return stb__clex_parse_suffixes(lexer, CLEX_intlit, p,q, STB_C_LEX_OCTAL_SUFFIXES); + } + #endif // STB__clex_decimal_ints + goto single_char; + } +} +#endif // STB_C_LEXER_IMPLEMENTATION + +#ifdef STB_C_LEXER_SELF_TEST + +#include + +static void print_token(stb_lexer *lexer) +{ + switch (lexer->token) { + case CLEX_id : printf("_%s", lexer->string); break; + case CLEX_eq : printf("=="); break; + case CLEX_noteq : printf("!="); break; + case CLEX_lesseq : printf("<="); break; + case CLEX_greatereq : printf(">="); break; + case CLEX_andand : printf("&&"); break; + case CLEX_oror : printf("||"); break; + case CLEX_shl : printf("<<"); break; + case CLEX_shr : printf(">>"); break; + case CLEX_plusplus : printf("++"); break; + case CLEX_minusminus: printf("--"); break; + case CLEX_arrow : printf("->"); break; + case CLEX_andeq : printf("&="); break; + case CLEX_oreq : printf("|="); break; + case CLEX_xoreq : printf("^="); break; + case CLEX_pluseq : printf("+="); break; + case CLEX_minuseq : printf("-="); break; + case CLEX_muleq : printf("*="); break; + case CLEX_diveq : printf("/="); break; + case CLEX_modeq : printf("%%="); break; + case CLEX_shleq : printf("<<="); break; + case CLEX_shreq : printf(">>="); break; + case CLEX_eqarrow : printf("=>"); break; + case CLEX_dqstring : printf("\"%s\"", lexer->string); break; + case CLEX_sqstring : printf("'\"%s\"'", lexer->string); break; + case CLEX_charlit : printf("'%s'", lexer->string); break; + #if defined(STB__clex_int_as_double) && !defined(STB__CLEX_use_stdlib) + case CLEX_intlit : printf("#%g", lexer->real_number); break; + #else + case CLEX_intlit : printf("#%ld", lexer->int_number); break; + #endif + case CLEX_floatlit : printf("%g", lexer->real_number); break; + default: + if (lexer->token >= 0 && lexer->token < 256) + printf("%c", (int) lexer->token); + else { + printf("<<>>\n", lexer->token); + } + break; + } +} + +/* Force a test +of parsing +multiline comments */ + +/*/ comment /*/ +/**/ extern /**/ + +void dummy(void) +{ + printf("test",1); // https://github.com/nothings/stb/issues/13 +} + +int main(int argc, char **argv) +{ + FILE *f = fopen("stb_c_lexer.h","rb"); + char *text = (char *) malloc(1 << 20); + int len = f ? fread(text, 1, 1<<20, f) : -1; + stb_lexer lex; + if (len < 0) { + fprintf(stderr, "Error opening file\n"); + return 1; + } + fclose(f); + + stb_c_lexer_init(&lex, text, text+len, (char *) malloc(1<<16), 1<<16); + while (stb_c_lexer_get_token(&lex)) { + if (lex.token == CLEX_parse_error) { + printf("\n<<>>\n"); + break; + } + print_token(&lex); + printf(" "); + } + return 0; +} +#endif diff --git a/impeller/third_party/stb/stb/stb_divide.h b/impeller/third_party/stb/stb/stb_divide.h new file mode 100644 index 0000000000000..d1702f3b522af --- /dev/null +++ b/impeller/third_party/stb/stb/stb_divide.h @@ -0,0 +1,379 @@ +// stb_divide.h - v0.91 - public domain - Sean Barrett, Feb 2010 +// Three kinds of divide/modulus of signed integers. +// +// HISTORY +// +// v0.91 2010-02-27 Fix euclidean division by INT_MIN for non-truncating C +// Check result with 64-bit math to catch such cases +// v0.90 2010-02-24 First public release +// +// USAGE +// +// In *ONE* source file, put: +// +// #define STB_DIVIDE_IMPLEMENTATION +// // #define C_INTEGER_DIVISION_TRUNCATES // see Note 1 +// // #define C_INTEGER_DIVISION_FLOORS // see Note 2 +// #include "stb_divide.h" +// +// Other source files should just include stb_divide.h +// +// Note 1: On platforms/compilers that you know signed C division +// truncates, you can #define C_INTEGER_DIVISION_TRUNCATES. +// +// Note 2: On platforms/compilers that you know signed C division +// floors (rounds to negative infinity), you can #define +// C_INTEGER_DIVISION_FLOORS. +// +// You can #define STB_DIVIDE_TEST in which case the implementation +// will generate a main() and compiling the result will create a +// program that tests the implementation. Run it with no arguments +// and any output indicates an error; run it with any argument and +// it will also print the test results. Define STB_DIVIDE_TEST_64 +// to a 64-bit integer type to avoid overflows in the result-checking +// which give false negatives. +// +// ABOUT +// +// This file provides three different consistent divide/mod pairs +// implemented on top of arbitrary C/C++ division, including correct +// handling of overflow of intermediate calculations: +// +// trunc: a/b truncates to 0, a%b has same sign as a +// floor: a/b truncates to -inf, a%b has same sign as b +// eucl: a/b truncates to sign(b)*inf, a%b is non-negative +// +// Not necessarily optimal; I tried to keep it generally efficient, +// but there may be better ways. +// +// Briefly, for those who are not familiar with the problem, we note +// the reason these divides exist and are interesting: +// +// 'trunc' is easy to implement in hardware (strip the signs, +// compute, reapply the signs), thus is commonly defined +// by many languages (including C99) +// +// 'floor' is simple to define and better behaved than trunc; +// for example it divides integers into fixed-size buckets +// without an extra-wide bucket at 0, and for a fixed +// divisor N there are only |N| possible moduli. +// +// 'eucl' guarantees fixed-sized buckets *and* a non-negative +// modulus and defines division to be whatever is needed +// to achieve that result. +// +// See "The Euclidean definition of the functions div and mod" +// by Raymond Boute (1992), or "Division and Modulus for Computer +// Scientists" by Daan Leijen (2001) +// +// We assume of the built-in C division: +// (a) modulus is the remainder for the corresponding division +// (b) a/b truncates if a and b are the same sign +// +// Property (a) requires (a/b)*b + (a%b)==a, and is required by C. +// Property (b) seems to be true of all hardware but is *not* satisfied +// by the euclidean division operator we define, so it's possibly not +// always true. If any such platform turns up, we can add more cases. +// (Possibly only stb_div_trunc currently relies on property (b).) +// +// LICENSE +// +// This software is dual-licensed to the public domain and under the following +// license: you are granted a perpetual, irrevocable license to copy, modify, +// publish, and distribute this file as you see fit. + + +#ifndef INCLUDE_STB_DIVIDE_H +#define INCLUDE_STB_DIVIDE_H + +#ifdef __cplusplus +extern "C" { +#endif + +extern int stb_div_trunc(int value_to_be_divided, int value_to_divide_by); +extern int stb_div_floor(int value_to_be_divided, int value_to_divide_by); +extern int stb_div_eucl (int value_to_be_divided, int value_to_divide_by); +extern int stb_mod_trunc(int value_to_be_divided, int value_to_divide_by); +extern int stb_mod_floor(int value_to_be_divided, int value_to_divide_by); +extern int stb_mod_eucl (int value_to_be_divided, int value_to_divide_by); + +#ifdef __cplusplus +} +#endif + +#ifdef STB_DIVIDE_IMPLEMENTATION + +#if defined(__STDC_VERSION) && __STDC_VERSION__ >= 19901 + #ifndef C_INTEGER_DIVISION_TRUNCATES + #define C_INTEGER_DIVISION_TRUNCATES + #endif +#endif + +#ifndef INT_MIN +#include // if you have no limits.h, #define INT_MIN yourself +#endif + +// the following macros are designed to allow testing +// other platforms by simulating them +#ifndef STB_DIVIDE_TEST_FLOOR + #define stb__div(a,b) ((a)/(b)) + #define stb__mod(a,b) ((a)%(b)) +#else + // implement floor-style divide on trunc platform + #ifndef C_INTEGER_DIVISION_TRUNCATES + #error "floor test requires truncating division" + #endif + #undef C_INTEGER_DIVISION_TRUNCATES + int stb__div(int v1, int v2) + { + int q = v1/v2, r = v1%v2; + if ((r > 0 && v2 < 0) || (r < 0 && v2 > 0)) + return q-1; + else + return q; + } + + int stb__mod(int v1, int v2) + { + int r = v1%v2; + if ((r > 0 && v2 < 0) || (r < 0 && v2 > 0)) + return r+v2; + else + return r; + } +#endif + +int stb_div_trunc(int v1, int v2) +{ + #ifdef C_INTEGER_DIVISION_TRUNCATES + return v1/v2; + #else + if (v1 >= 0 && v2 <= 0) + return -stb__div(-v1,v2); // both negative to avoid overflow + if (v1 <= 0 && v2 >= 0) + if (v1 != INT_MIN) + return -stb__div(v1,-v2); // both negative to avoid overflow + else + return -stb__div(v1+v2,-v2)-1; // push v1 away from wrap point + else + return v1/v2; // same sign, so expect truncation + #endif +} + +int stb_div_floor(int v1, int v2) +{ + #ifdef C_INTEGER_DIVISION_FLOORS + return v1/v2; + #else + if (v1 >= 0 && v2 < 0) + if ((-v1)+v2+1 < 0) // check if increasing v1's magnitude overflows + return -stb__div(-v1+v2+1,v2); // nope, so just compute it + else + return -stb__div(-v1,v2) + ((-v1)%v2 ? -1 : 0); + if (v1 < 0 && v2 >= 0) + if (v1 != INT_MIN) + if (v1-v2+1 < 0) // check if increasing v1's magnitude overflows + return -stb__div(v1-v2+1,-v2); // nope, so just compute it + else + return -stb__div(-v1,v2) + (stb__mod(v1,-v2) ? -1 : 0); + else // it must be possible to compute -(v1+v2) without overflowing + return -stb__div(-(v1+v2),v2) + (stb__mod(-(v1+v2),v2) ? -2 : -1); + else + return v1/v2; // same sign, so expect truncation + #endif +} + +int stb_div_eucl(int v1, int v2) +{ + int q,r; + #ifdef C_INTEGER_DIVISION_TRUNCATES + q = v1/v2; + r = v1%v2; + #else + // handle every quadrant separately, since we can't rely on q and r flor + if (v1 >= 0) + if (v2 >= 0) + return stb__div(v1,v2); + else if (v2 != INT_MIN) + q = -stb__div(v1,-v2), r = stb__mod(v1,-v2); + else + q = 0, r = v1; + else if (v1 != INT_MIN) + if (v2 >= 0) + q = -stb__div(-v1,v2), r = -stb__mod(-v1,v2); + else if (v2 != INT_MIN) + q = stb__div(-v1,-v2), r = -stb__mod(-v1,-v2); + else // if v2 is INT_MIN, then we can't use -v2, but we can't divide by v2 + q = 1, r = v1-q*v2; + else // if v1 is INT_MIN, we have to move away from overflow place + if (v2 >= 0) + q = -stb__div(-(v1+v2),v2)-1, r = -stb__mod(-(v1+v2),v2); + else + q = stb__div(-(v1-v2),-v2)+1, r = -stb__mod(-(v1-v2),-v2); + #endif + if (r >= 0) + return q; + else + return q + (v2 > 0 ? -1 : 1); +} + +int stb_mod_trunc(int v1, int v2) +{ + #ifdef C_INTEGER_DIVISION_TRUNCATES + return v1%v2; + #else + if (v1 >= 0) { // modulus result should always be positive + int r = stb__mod(v1,v2); + if (r >= 0) + return r; + else + return r + (v2 > 0 ? v2 : -v2); + } else { // modulus result should always be negative + int r = stb__mod(v1,v2); + if (r <= 0) + return r; + else + return r - (v2 > 0 ? v2 : -v2); + } + #endif +} + +int stb_mod_floor(int v1, int v2) +{ + #ifdef C_INTEGER_DIVISION_FLOORS + return v1%v2; + #else + if (v2 >= 0) { // result should always be positive + int r = stb__mod(v1,v2); + if (r >= 0) + return r; + else + return r + v2; + } else { // result should always be negative + int r = stb__mod(v1,v2); + if (r <= 0) + return r; + else + return r + v2; + } + #endif +} + +int stb_mod_eucl(int v1, int v2) +{ + int r = stb__mod(v1,v2); + + if (r >= 0) + return r; + else + return r + (v2 > 0 ? v2 : -v2); // abs() +} + +#ifdef STB_DIVIDE_TEST +#include +#include +#include + +int show=0; + +void stbdiv_check(int q, int r, int a, int b, char *type, int dir) +{ + if ((dir > 0 && r < 0) || (dir < 0 && r > 0)) + fprintf(stderr, "FAILED: %s(%d,%d) remainder %d in wrong direction\n", type,a,b,r); + else + if (b != INT_MIN) // can't compute abs(), but if b==INT_MIN all remainders are valid + if (r <= -abs(b) || r >= abs(b)) + fprintf(stderr, "FAILED: %s(%d,%d) remainder %d out of range\n", type,a,b,r); + #ifdef STB_DIVIDE_TEST_64 + { + STB_DIVIDE_TEST_64 q64 = q, r64=r, a64=a, b64=b; + if (q64*b64+r64 != a64) + fprintf(stderr, "FAILED: %s(%d,%d) remainder %d doesn't match quotient %d\n", type,a,b,r,q); + } + #else + if (q*b+r != a) + fprintf(stderr, "FAILED: %s(%d,%d) remainder %d doesn't match quotient %d\n", type,a,b,r,q); + #endif +} + +void test(int a, int b) +{ + int q,r; + if (show) printf("(%+11d,%+d) | ", a,b); + q = stb_div_trunc(a,b), r = stb_mod_trunc(a,b); + if (show) printf("(%+11d,%+2d) ", q,r); stbdiv_check(q,r,a,b, "trunc",a); + q = stb_div_floor(a,b), r = stb_mod_floor(a,b); + if (show) printf("(%+11d,%+2d) ", q,r); stbdiv_check(q,r,a,b, "floor",b); + q = stb_div_eucl (a,b), r = stb_mod_eucl (a,b); + if (show) printf("(%+11d,%+2d)\n", q,r); stbdiv_check(q,r,a,b, "euclidean",1); +} + +void testh(int a, int b) +{ + int q,r; + if (show) printf("(%08x,%08x) |\n", a,b); + q = stb_div_trunc(a,b), r = stb_mod_trunc(a,b); stbdiv_check(q,r,a,b, "trunc",a); + if (show) printf(" (%08x,%08x)", q,r); + q = stb_div_floor(a,b), r = stb_mod_floor(a,b); stbdiv_check(q,r,a,b, "floor",b); + if (show) printf(" (%08x,%08x)", q,r); + q = stb_div_eucl (a,b), r = stb_mod_eucl (a,b); stbdiv_check(q,r,a,b, "euclidean",1); + if (show) printf(" (%08x,%08x)\n ", q,r); +} + +int main(int argc, char **argv) +{ + if (argc > 1) show=1; + + test(8,3); + test(8,-3); + test(-8,3); + test(-8,-3); + test(1,2); + test(1,-2); + test(-1,2); + test(-1,-2); + test(8,4); + test(8,-4); + test(-8,4); + test(-8,-4); + + test(INT_MAX,1); + test(INT_MIN,1); + test(INT_MIN+1,1); + test(INT_MAX,-1); + //test(INT_MIN,-1); // this traps in MSVC, so we leave it untested + test(INT_MIN+1,-1); + test(INT_MIN,-2); + test(INT_MIN+1,2); + test(INT_MIN+1,-2); + test(INT_MAX,2); + test(INT_MAX,-2); + test(INT_MIN+1,2); + test(INT_MIN+1,-2); + test(INT_MIN,2); + test(INT_MIN,-2); + test(INT_MIN,7); + test(INT_MIN,-7); + test(INT_MIN+1,4); + test(INT_MIN+1,-4); + + testh(-7, INT_MIN); + testh(-1, INT_MIN); + testh(1, INT_MIN); + testh(7, INT_MIN); + + testh(INT_MAX-1, INT_MIN); + testh(INT_MAX, INT_MIN); + testh(INT_MIN, INT_MIN); + testh(INT_MIN+1, INT_MIN); + + testh(INT_MAX-1, INT_MAX); + testh(INT_MAX , INT_MAX); + testh(INT_MIN , INT_MAX); + testh(INT_MIN+1, INT_MAX); + + return 0; +} +#endif // STB_DIVIDE_TEST +#endif // STB_DIVIDE_IMPLEMENTATION +#endif // INCLUDE_STB_DIVIDE_H diff --git a/impeller/third_party/stb/stb/stb_dxt.h b/impeller/third_party/stb/stb/stb_dxt.h new file mode 100644 index 0000000000000..0a8b34ae2350d --- /dev/null +++ b/impeller/third_party/stb/stb/stb_dxt.h @@ -0,0 +1,630 @@ +// stb_dxt.h - v1.04 - DXT1/DXT5 compressor - public domain +// original by fabian "ryg" giesen - ported to C by stb +// use '#define STB_DXT_IMPLEMENTATION' before including to create the implementation +// +// USAGE: +// call stb_compress_dxt_block() for every block (you must pad) +// source should be a 4x4 block of RGBA data in row-major order; +// A is ignored if you specify alpha=0; you can turn on dithering +// and "high quality" using mode. +// +// version history: +// v1.04 - (ryg) default to no rounding bias for lerped colors (as per S3TC/DX10 spec); +// single color match fix (allow for inexact color interpolation); +// optimal DXT5 index finder; "high quality" mode that runs multiple refinement steps. +// v1.03 - (stb) endianness support +// v1.02 - (stb) fix alpha encoding bug +// v1.01 - (stb) fix bug converting to RGB that messed up quality, thanks ryg & cbloom +// v1.00 - (stb) first release +// +// LICENSE +// +// This software is dual-licensed to the public domain and under the following +// license: you are granted a perpetual, irrevocable license to copy, modify, +// publish, and distribute this file as you see fit. + +#ifndef STB_INCLUDE_STB_DXT_H +#define STB_INCLUDE_STB_DXT_H + +// compression mode (bitflags) +#define STB_DXT_NORMAL 0 +#define STB_DXT_DITHER 1 // use dithering. dubious win. never use for normal maps and the like! +#define STB_DXT_HIGHQUAL 2 // high quality mode, does two refinement steps instead of 1. ~30-40% slower. + +void stb_compress_dxt_block(unsigned char *dest, const unsigned char *src, int alpha, int mode); +#define STB_COMPRESS_DXT_BLOCK + +#ifdef STB_DXT_IMPLEMENTATION + +// configuration options for DXT encoder. set them in the project/makefile or just define +// them at the top. + +// STB_DXT_USE_ROUNDING_BIAS +// use a rounding bias during color interpolation. this is closer to what "ideal" +// interpolation would do but doesn't match the S3TC/DX10 spec. old versions (pre-1.03) +// implicitly had this turned on. +// +// in case you're targeting a specific type of hardware (e.g. console programmers): +// NVidia and Intel GPUs (as of 2010) as well as DX9 ref use DXT decoders that are closer +// to STB_DXT_USE_ROUNDING_BIAS. AMD/ATI, S3 and DX10 ref are closer to rounding with no bias. +// you also see "(a*5 + b*3) / 8" on some old GPU designs. +// #define STB_DXT_USE_ROUNDING_BIAS + +#include +#include +#include // memset + +static unsigned char stb__Expand5[32]; +static unsigned char stb__Expand6[64]; +static unsigned char stb__OMatch5[256][2]; +static unsigned char stb__OMatch6[256][2]; +static unsigned char stb__QuantRBTab[256+16]; +static unsigned char stb__QuantGTab[256+16]; + +static int stb__Mul8Bit(int a, int b) +{ + int t = a*b + 128; + return (t + (t >> 8)) >> 8; +} + +static void stb__From16Bit(unsigned char *out, unsigned short v) +{ + int rv = (v & 0xf800) >> 11; + int gv = (v & 0x07e0) >> 5; + int bv = (v & 0x001f) >> 0; + + out[0] = stb__Expand5[rv]; + out[1] = stb__Expand6[gv]; + out[2] = stb__Expand5[bv]; + out[3] = 0; +} + +static unsigned short stb__As16Bit(int r, int g, int b) +{ + return (stb__Mul8Bit(r,31) << 11) + (stb__Mul8Bit(g,63) << 5) + stb__Mul8Bit(b,31); +} + +// linear interpolation at 1/3 point between a and b, using desired rounding type +static int stb__Lerp13(int a, int b) +{ +#ifdef STB_DXT_USE_ROUNDING_BIAS + // with rounding bias + return a + stb__Mul8Bit(b-a, 0x55); +#else + // without rounding bias + // replace "/ 3" by "* 0xaaab) >> 17" if your compiler sucks or you really need every ounce of speed. + return (2*a + b) / 3; +#endif +} + +// lerp RGB color +static void stb__Lerp13RGB(unsigned char *out, unsigned char *p1, unsigned char *p2) +{ + out[0] = stb__Lerp13(p1[0], p2[0]); + out[1] = stb__Lerp13(p1[1], p2[1]); + out[2] = stb__Lerp13(p1[2], p2[2]); +} + +/****************************************************************************/ + +// compute table to reproduce constant colors as accurately as possible +static void stb__PrepareOptTable(unsigned char *Table,const unsigned char *expand,int size) +{ + int i,mn,mx; + for (i=0;i<256;i++) { + int bestErr = 256; + for (mn=0;mn> 4)]; + ep1[0] = bp[ 0] - dp[ 0]; + dp[ 4] = quant[bp[ 4] + ((7*ep1[0] + 3*ep2[2] + 5*ep2[1] + ep2[0]) >> 4)]; + ep1[1] = bp[ 4] - dp[ 4]; + dp[ 8] = quant[bp[ 8] + ((7*ep1[1] + 3*ep2[3] + 5*ep2[2] + ep2[1]) >> 4)]; + ep1[2] = bp[ 8] - dp[ 8]; + dp[12] = quant[bp[12] + ((7*ep1[2] + 5*ep2[3] + ep2[2]) >> 4)]; + ep1[3] = bp[12] - dp[12]; + bp += 16; + dp += 16; + et = ep1, ep1 = ep2, ep2 = et; // swap + } + } +} + +// The color matching function +static unsigned int stb__MatchColorsBlock(unsigned char *block, unsigned char *color,int dither) +{ + unsigned int mask = 0; + int dirr = color[0*4+0] - color[1*4+0]; + int dirg = color[0*4+1] - color[1*4+1]; + int dirb = color[0*4+2] - color[1*4+2]; + int dots[16]; + int stops[4]; + int i; + int c0Point, halfPoint, c3Point; + + for(i=0;i<16;i++) + dots[i] = block[i*4+0]*dirr + block[i*4+1]*dirg + block[i*4+2]*dirb; + + for(i=0;i<4;i++) + stops[i] = color[i*4+0]*dirr + color[i*4+1]*dirg + color[i*4+2]*dirb; + + // think of the colors as arranged on a line; project point onto that line, then choose + // next color out of available ones. we compute the crossover points for "best color in top + // half"/"best in bottom half" and then the same inside that subinterval. + // + // relying on this 1d approximation isn't always optimal in terms of euclidean distance, + // but it's very close and a lot faster. + // http://cbloomrants.blogspot.com/2008/12/12-08-08-dxtc-summary.html + + c0Point = (stops[1] + stops[3]) >> 1; + halfPoint = (stops[3] + stops[2]) >> 1; + c3Point = (stops[2] + stops[0]) >> 1; + + if(!dither) { + // the version without dithering is straightforward + for (i=15;i>=0;i--) { + int dot = dots[i]; + mask <<= 2; + + if(dot < halfPoint) + mask |= (dot < c0Point) ? 1 : 3; + else + mask |= (dot < c3Point) ? 2 : 0; + } + } else { + // with floyd-steinberg dithering + int err[8],*ep1 = err,*ep2 = err+4; + int *dp = dots, y; + + c0Point <<= 4; + halfPoint <<= 4; + c3Point <<= 4; + for(i=0;i<8;i++) + err[i] = 0; + + for(y=0;y<4;y++) + { + int dot,lmask,step; + + dot = (dp[0] << 4) + (3*ep2[1] + 5*ep2[0]); + if(dot < halfPoint) + step = (dot < c0Point) ? 1 : 3; + else + step = (dot < c3Point) ? 2 : 0; + ep1[0] = dp[0] - stops[step]; + lmask = step; + + dot = (dp[1] << 4) + (7*ep1[0] + 3*ep2[2] + 5*ep2[1] + ep2[0]); + if(dot < halfPoint) + step = (dot < c0Point) ? 1 : 3; + else + step = (dot < c3Point) ? 2 : 0; + ep1[1] = dp[1] - stops[step]; + lmask |= step<<2; + + dot = (dp[2] << 4) + (7*ep1[1] + 3*ep2[3] + 5*ep2[2] + ep2[1]); + if(dot < halfPoint) + step = (dot < c0Point) ? 1 : 3; + else + step = (dot < c3Point) ? 2 : 0; + ep1[2] = dp[2] - stops[step]; + lmask |= step<<4; + + dot = (dp[3] << 4) + (7*ep1[2] + 5*ep2[3] + ep2[2]); + if(dot < halfPoint) + step = (dot < c0Point) ? 1 : 3; + else + step = (dot < c3Point) ? 2 : 0; + ep1[3] = dp[3] - stops[step]; + lmask |= step<<6; + + dp += 4; + mask |= lmask << (y*8); + { int *et = ep1; ep1 = ep2; ep2 = et; } // swap + } + } + + return mask; +} + +// The color optimization function. (Clever code, part 1) +static void stb__OptimizeColorsBlock(unsigned char *block, unsigned short *pmax16, unsigned short *pmin16) +{ + int mind = 0x7fffffff,maxd = -0x7fffffff; + unsigned char *minp, *maxp; + double magn; + int v_r,v_g,v_b; + static const int nIterPower = 4; + float covf[6],vfr,vfg,vfb; + + // determine color distribution + int cov[6]; + int mu[3],min[3],max[3]; + int ch,i,iter; + + for(ch=0;ch<3;ch++) + { + const unsigned char *bp = ((const unsigned char *) block) + ch; + int muv,minv,maxv; + + muv = minv = maxv = bp[0]; + for(i=4;i<64;i+=4) + { + muv += bp[i]; + if (bp[i] < minv) minv = bp[i]; + else if (bp[i] > maxv) maxv = bp[i]; + } + + mu[ch] = (muv + 8) >> 4; + min[ch] = minv; + max[ch] = maxv; + } + + // determine covariance matrix + for (i=0;i<6;i++) + cov[i] = 0; + + for (i=0;i<16;i++) + { + int r = block[i*4+0] - mu[0]; + int g = block[i*4+1] - mu[1]; + int b = block[i*4+2] - mu[2]; + + cov[0] += r*r; + cov[1] += r*g; + cov[2] += r*b; + cov[3] += g*g; + cov[4] += g*b; + cov[5] += b*b; + } + + // convert covariance matrix to float, find principal axis via power iter + for(i=0;i<6;i++) + covf[i] = cov[i] / 255.0f; + + vfr = (float) (max[0] - min[0]); + vfg = (float) (max[1] - min[1]); + vfb = (float) (max[2] - min[2]); + + for(iter=0;iter magn) magn = fabs(vfg); + if (fabs(vfb) > magn) magn = fabs(vfb); + + if(magn < 4.0f) { // too small, default to luminance + v_r = 299; // JPEG YCbCr luma coefs, scaled by 1000. + v_g = 587; + v_b = 114; + } else { + magn = 512.0 / magn; + v_r = (int) (vfr * magn); + v_g = (int) (vfg * magn); + v_b = (int) (vfb * magn); + } + + // Pick colors at extreme points + for(i=0;i<16;i++) + { + int dot = block[i*4+0]*v_r + block[i*4+1]*v_g + block[i*4+2]*v_b; + + if (dot < mind) { + mind = dot; + minp = block+i*4; + } + + if (dot > maxd) { + maxd = dot; + maxp = block+i*4; + } + } + + *pmax16 = stb__As16Bit(maxp[0],maxp[1],maxp[2]); + *pmin16 = stb__As16Bit(minp[0],minp[1],minp[2]); +} + +static int stb__sclamp(float y, int p0, int p1) +{ + int x = (int) y; + if (x < p0) return p0; + if (x > p1) return p1; + return x; +} + +// The refinement function. (Clever code, part 2) +// Tries to optimize colors to suit block contents better. +// (By solving a least squares system via normal equations+Cramer's rule) +static int stb__RefineBlock(unsigned char *block, unsigned short *pmax16, unsigned short *pmin16, unsigned int mask) +{ + static const int w1Tab[4] = { 3,0,2,1 }; + static const int prods[4] = { 0x090000,0x000900,0x040102,0x010402 }; + // ^some magic to save a lot of multiplies in the accumulating loop... + // (precomputed products of weights for least squares system, accumulated inside one 32-bit register) + + float frb,fg; + unsigned short oldMin, oldMax, min16, max16; + int i, akku = 0, xx,xy,yy; + int At1_r,At1_g,At1_b; + int At2_r,At2_g,At2_b; + unsigned int cm = mask; + + oldMin = *pmin16; + oldMax = *pmax16; + + if((mask ^ (mask<<2)) < 4) // all pixels have the same index? + { + // yes, linear system would be singular; solve using optimal + // single-color match on average color + int r = 8, g = 8, b = 8; + for (i=0;i<16;++i) { + r += block[i*4+0]; + g += block[i*4+1]; + b += block[i*4+2]; + } + + r >>= 4; g >>= 4; b >>= 4; + + max16 = (stb__OMatch5[r][0]<<11) | (stb__OMatch6[g][0]<<5) | stb__OMatch5[b][0]; + min16 = (stb__OMatch5[r][1]<<11) | (stb__OMatch6[g][1]<<5) | stb__OMatch5[b][1]; + } else { + At1_r = At1_g = At1_b = 0; + At2_r = At2_g = At2_b = 0; + for (i=0;i<16;++i,cm>>=2) { + int step = cm&3; + int w1 = w1Tab[step]; + int r = block[i*4+0]; + int g = block[i*4+1]; + int b = block[i*4+2]; + + akku += prods[step]; + At1_r += w1*r; + At1_g += w1*g; + At1_b += w1*b; + At2_r += r; + At2_g += g; + At2_b += b; + } + + At2_r = 3*At2_r - At1_r; + At2_g = 3*At2_g - At1_g; + At2_b = 3*At2_b - At1_b; + + // extract solutions and decide solvability + xx = akku >> 16; + yy = (akku >> 8) & 0xff; + xy = (akku >> 0) & 0xff; + + frb = 3.0f * 31.0f / 255.0f / (xx*yy - xy*xy); + fg = frb * 63.0f / 31.0f; + + // solve. + max16 = stb__sclamp((At1_r*yy - At2_r*xy)*frb+0.5f,0,31) << 11; + max16 |= stb__sclamp((At1_g*yy - At2_g*xy)*fg +0.5f,0,63) << 5; + max16 |= stb__sclamp((At1_b*yy - At2_b*xy)*frb+0.5f,0,31) << 0; + + min16 = stb__sclamp((At2_r*xx - At1_r*xy)*frb+0.5f,0,31) << 11; + min16 |= stb__sclamp((At2_g*xx - At1_g*xy)*fg +0.5f,0,63) << 5; + min16 |= stb__sclamp((At2_b*xx - At1_b*xy)*frb+0.5f,0,31) << 0; + } + + *pmin16 = min16; + *pmax16 = max16; + return oldMin != min16 || oldMax != max16; +} + +// Color block compression +static void stb__CompressColorBlock(unsigned char *dest, unsigned char *block, int mode) +{ + unsigned int mask; + int i; + int dither; + int refinecount; + unsigned short max16, min16; + unsigned char dblock[16*4],color[4*4]; + + dither = mode & STB_DXT_DITHER; + refinecount = (mode & STB_DXT_HIGHQUAL) ? 2 : 1; + + // check if block is constant + for (i=1;i<16;i++) + if (((unsigned int *) block)[i] != ((unsigned int *) block)[0]) + break; + + if(i == 16) { // constant color + int r = block[0], g = block[1], b = block[2]; + mask = 0xaaaaaaaa; + max16 = (stb__OMatch5[r][0]<<11) | (stb__OMatch6[g][0]<<5) | stb__OMatch5[b][0]; + min16 = (stb__OMatch5[r][1]<<11) | (stb__OMatch6[g][1]<<5) | stb__OMatch5[b][1]; + } else { + // first step: compute dithered version for PCA if desired + if(dither) + stb__DitherBlock(dblock,block); + + // second step: pca+map along principal axis + stb__OptimizeColorsBlock(dither ? dblock : block,&max16,&min16); + if (max16 != min16) { + stb__EvalColors(color,max16,min16); + mask = stb__MatchColorsBlock(block,color,dither); + } else + mask = 0; + + // third step: refine (multiple times if requested) + for (i=0;i> 8); + dest[2] = (unsigned char) (min16); + dest[3] = (unsigned char) (min16 >> 8); + dest[4] = (unsigned char) (mask); + dest[5] = (unsigned char) (mask >> 8); + dest[6] = (unsigned char) (mask >> 16); + dest[7] = (unsigned char) (mask >> 24); +} + +// Alpha block compression (this is easy for a change) +static void stb__CompressAlphaBlock(unsigned char *dest,unsigned char *src,int mode) +{ + int i,dist,bias,dist4,dist2,bits,mask; + + // find min/max color + int mn,mx; + mn = mx = src[3]; + + for (i=1;i<16;i++) + { + if (src[i*4+3] < mn) mn = src[i*4+3]; + else if (src[i*4+3] > mx) mx = src[i*4+3]; + } + + // encode them + ((unsigned char *)dest)[0] = mx; + ((unsigned char *)dest)[1] = mn; + dest += 2; + + // determine bias and emit color indices + // given the choice of mx/mn, these indices are optimal: + // http://fgiesen.wordpress.com/2009/12/15/dxt5-alpha-block-index-determination/ + dist = mx-mn; + dist4 = dist*4; + dist2 = dist*2; + bias = (dist < 8) ? (dist - 1) : (dist/2 + 2); + bias -= mn * 7; + bits = 0,mask=0; + + for (i=0;i<16;i++) { + int a = src[i*4+3]*7 + bias; + int ind,t; + + // select index. this is a "linear scale" lerp factor between 0 (val=min) and 7 (val=max). + t = (a >= dist4) ? -1 : 0; ind = t & 4; a -= dist4 & t; + t = (a >= dist2) ? -1 : 0; ind += t & 2; a -= dist2 & t; + ind += (a >= dist); + + // turn linear scale into DXT index (0/1 are extremal pts) + ind = -ind & 7; + ind ^= (2 > ind); + + // write index + mask |= ind << bits; + if((bits += 3) >= 8) { + *dest++ = mask; + mask >>= 8; + bits -= 8; + } + } +} + +static void stb__InitDXT() +{ + int i; + for(i=0;i<32;i++) + stb__Expand5[i] = (i<<3)|(i>>2); + + for(i=0;i<64;i++) + stb__Expand6[i] = (i<<2)|(i>>4); + + for(i=0;i<256+16;i++) + { + int v = i-8 < 0 ? 0 : i-8 > 255 ? 255 : i-8; + stb__QuantRBTab[i] = stb__Expand5[stb__Mul8Bit(v,31)]; + stb__QuantGTab[i] = stb__Expand6[stb__Mul8Bit(v,63)]; + } + + stb__PrepareOptTable(&stb__OMatch5[0][0],stb__Expand5,32); + stb__PrepareOptTable(&stb__OMatch6[0][0],stb__Expand6,64); +} + +void stb_compress_dxt_block(unsigned char *dest, const unsigned char *src, int alpha, int mode) +{ + static int init=1; + if (init) { + stb__InitDXT(); + init=0; + } + + if (alpha) { + stb__CompressAlphaBlock(dest,(unsigned char*) src,mode); + dest += 8; + } + + stb__CompressColorBlock(dest,(unsigned char*) src,mode); +} +#endif // STB_DXT_IMPLEMENTATION + +#endif // STB_INCLUDE_STB_DXT_H diff --git a/impeller/third_party/stb/stb/stb_easy_font.h b/impeller/third_party/stb/stb/stb_easy_font.h new file mode 100644 index 0000000000000..45bddc99478fc --- /dev/null +++ b/impeller/third_party/stb/stb/stb_easy_font.h @@ -0,0 +1,258 @@ +// stb_easy_font.h - v0.7 - bitmap font for 3D rendering - public domain +// Sean Barrett, Feb 2015 +// +// Easy-to-deploy, +// reasonably compact, +// extremely inefficient performance-wise, +// crappy-looking, +// ASCII-only, +// bitmap font for use in 3D APIs. +// +// Intended for when you just want to get some text displaying +// in a 3D app as quickly as possible. +// +// Doesn't use any textures, instead builds characters out of quads. +// +// DOCUMENTATION: +// +// int stb_easy_font_width(char *text) +// int stb_easy_font_height(char *text) +// +// Takes a string and returns the horizontal size and the +// vertical size (which can vary if 'text' has newlines). +// +// int stb_easy_font_print(float x, float y, +// char *text, unsigned char color[4], +// void *vertex_buffer, int vbuf_size) +// +// Takes a string (which can contain '\n') and fills out a +// vertex buffer with renderable data to draw the string. +// Output data assumes increasing x is rightwards, increasing y +// is downwards. +// +// The vertex data is divided into quads, i.e. there are four +// vertices in the vertex buffer for each quad. +// +// The vertices are stored in an interleaved format: +// +// x:float +// y:float +// z:float +// color:uint8[4] +// +// You can ignore z and color if you get them from elsewhere +// This format was chosen in the hopes it would make it +// easier for you to reuse existing vertex-buffer-drawing code. +// +// If you pass in NULL for color, it becomes 255,255,255,255. +// +// Returns the number of quads. +// +// If the buffer isn't large enough, it will truncate. +// Expect it to use an average of ~270 bytes per character. +// +// If your API doesn't draw quads, build a reusable index +// list that allows you to render quads as indexed triangles. +// +// void stb_easy_font_spacing(float spacing) +// +// Use positive values to expand the space between characters, +// and small negative values (no smaller than -1.5) to contract +// the space between characters. +// +// E.g. spacing = 1 adds one "pixel" of spacing between the +// characters. spacing = -1 is reasonable but feels a bit too +// compact to me; -0.5 is a reasonable compromise as long as +// you're scaling the font up. +// +// LICENSE +// +// This software is dual-licensed to the public domain and under the following +// license: you are granted a perpetual, irrevocable license to copy, modify, +// publish, and distribute this file as you see fit. +// +// VERSION HISTORY +// +// (2016-01-22) 0.7 width() supports multiline text; add height() +// (2015-09-13) 0.6 #include ; updated license +// (2015-02-01) 0.5 First release + +#if 0 +// SAMPLE CODE: +// +// Here's sample code for old OpenGL; it's a lot more complicated +// to make work on modern APIs, and that's your problem. +// +void print_string(float x, float y, char *text, float r, float g, float b) +{ + static char buffer[99999]; // ~500 chars + int num_quads; + + num_quads = stb_easy_font_print(x, y, text, NULL, buffer, sizeof(buffer)); + + glColor3f(r,g,b); + glEnableClientState(GL_VERTEX_ARRAY); + glVertexPointer(2, GL_FLOAT, 16, buffer); + glDrawArrays(GL_QUADS, 0, num_quads*4); + glDisableClientState(GL_VERTEX_ARRAY); +} +#endif + +#ifndef INCLUDE_STB_EASY_FONT_H +#define INCLUDE_STB_EASY_FONT_H + +#include +#include + +struct { + unsigned char advance; + unsigned char h_seg; + unsigned char v_seg; +} stb_easy_font_charinfo[96] = { + { 5, 0, 0 }, { 3, 0, 0 }, { 5, 1, 1 }, { 7, 1, 4 }, + { 7, 3, 7 }, { 7, 6, 12 }, { 7, 8, 19 }, { 4, 16, 21 }, + { 4, 17, 22 }, { 4, 19, 23 }, { 23, 21, 24 }, { 23, 22, 31 }, + { 20, 23, 34 }, { 22, 23, 36 }, { 19, 24, 36 }, { 21, 25, 36 }, + { 6, 25, 39 }, { 6, 27, 43 }, { 6, 28, 45 }, { 6, 30, 49 }, + { 6, 33, 53 }, { 6, 34, 57 }, { 6, 40, 58 }, { 6, 46, 59 }, + { 6, 47, 62 }, { 6, 55, 64 }, { 19, 57, 68 }, { 20, 59, 68 }, + { 21, 61, 69 }, { 22, 66, 69 }, { 21, 68, 69 }, { 7, 73, 69 }, + { 9, 75, 74 }, { 6, 78, 81 }, { 6, 80, 85 }, { 6, 83, 90 }, + { 6, 85, 91 }, { 6, 87, 95 }, { 6, 90, 96 }, { 7, 92, 97 }, + { 6, 96,102 }, { 5, 97,106 }, { 6, 99,107 }, { 6,100,110 }, + { 6,100,115 }, { 7,101,116 }, { 6,101,121 }, { 6,101,125 }, + { 6,102,129 }, { 7,103,133 }, { 6,104,140 }, { 6,105,145 }, + { 7,107,149 }, { 6,108,151 }, { 7,109,155 }, { 7,109,160 }, + { 7,109,165 }, { 7,118,167 }, { 6,118,172 }, { 4,120,176 }, + { 6,122,177 }, { 4,122,181 }, { 23,124,182 }, { 22,129,182 }, + { 4,130,182 }, { 22,131,183 }, { 6,133,187 }, { 22,135,191 }, + { 6,137,192 }, { 22,139,196 }, { 5,144,197 }, { 22,147,198 }, + { 6,150,202 }, { 19,151,206 }, { 21,152,207 }, { 6,155,209 }, + { 3,160,210 }, { 23,160,211 }, { 22,164,216 }, { 22,165,220 }, + { 22,167,224 }, { 22,169,228 }, { 21,171,232 }, { 21,173,233 }, + { 5,178,233 }, { 22,179,234 }, { 23,180,238 }, { 23,180,243 }, + { 23,180,248 }, { 22,189,248 }, { 22,191,252 }, { 5,196,252 }, + { 3,203,252 }, { 5,203,253 }, { 22,210,253 }, { 0,214,253 }, +}; + +unsigned char stb_easy_font_hseg[214] = { + 97,37,69,84,28,51,2,18,10,49,98,41,65,25,81,105,33,9,97,1,97,37,37,36, + 81,10,98,107,3,100,3,99,58,51,4,99,58,8,73,81,10,50,98,8,73,81,4,10,50, + 98,8,25,33,65,81,10,50,17,65,97,25,33,25,49,9,65,20,68,1,65,25,49,41, + 11,105,13,101,76,10,50,10,50,98,11,99,10,98,11,50,99,11,50,11,99,8,57, + 58,3,99,99,107,10,10,11,10,99,11,5,100,41,65,57,41,65,9,17,81,97,3,107, + 9,97,1,97,33,25,9,25,41,100,41,26,82,42,98,27,83,42,98,26,51,82,8,41, + 35,8,10,26,82,114,42,1,114,8,9,73,57,81,41,97,18,8,8,25,26,26,82,26,82, + 26,82,41,25,33,82,26,49,73,35,90,17,81,41,65,57,41,65,25,81,90,114,20, + 84,73,57,41,49,25,33,65,81,9,97,1,97,25,33,65,81,57,33,25,41,25, +}; + +unsigned char stb_easy_font_vseg[253] = { + 4,2,8,10,15,8,15,33,8,15,8,73,82,73,57,41,82,10,82,18,66,10,21,29,1,65, + 27,8,27,9,65,8,10,50,97,74,66,42,10,21,57,41,29,25,14,81,73,57,26,8,8, + 26,66,3,8,8,15,19,21,90,58,26,18,66,18,105,89,28,74,17,8,73,57,26,21, + 8,42,41,42,8,28,22,8,8,30,7,8,8,26,66,21,7,8,8,29,7,7,21,8,8,8,59,7,8, + 8,15,29,8,8,14,7,57,43,10,82,7,7,25,42,25,15,7,25,41,15,21,105,105,29, + 7,57,57,26,21,105,73,97,89,28,97,7,57,58,26,82,18,57,57,74,8,30,6,8,8, + 14,3,58,90,58,11,7,74,43,74,15,2,82,2,42,75,42,10,67,57,41,10,7,2,42, + 74,106,15,2,35,8,8,29,7,8,8,59,35,51,8,8,15,35,30,35,8,8,30,7,8,8,60, + 36,8,45,7,7,36,8,43,8,44,21,8,8,44,35,8,8,43,23,8,8,43,35,8,8,31,21,15, + 20,8,8,28,18,58,89,58,26,21,89,73,89,29,20,8,8,30,7, +}; + +typedef struct +{ + unsigned char c[4]; +} stb_easy_font_color; + +static int stb_easy_font_draw_segs(float x, float y, unsigned char *segs, int num_segs, int vertical, stb_easy_font_color c, char *vbuf, int vbuf_size, int offset) +{ + int i,j; + for (i=0; i < num_segs; ++i) { + int len = segs[i] & 7; + x += (float) ((segs[i] >> 3) & 1); + if (len && offset+64 <= vbuf_size) { + float y0 = y + (float) (segs[i]>>4); + for (j=0; j < 4; ++j) { + * (float *) (vbuf+offset+0) = x + (j==1 || j==2 ? (vertical ? 1 : len) : 0); + * (float *) (vbuf+offset+4) = y0 + ( j >= 2 ? (vertical ? len : 1) : 0); + * (float *) (vbuf+offset+8) = 0.f; + * (stb_easy_font_color *) (vbuf+offset+12) = c; + offset += 16; + } + } + } + return offset; +} + +float stb_easy_font_spacing_val = 0; +static void stb_easy_font_spacing(float spacing) +{ + stb_easy_font_spacing_val = spacing; +} + +static int stb_easy_font_print(float x, float y, char *text, unsigned char color[4], void *vertex_buffer, int vbuf_size) +{ + char *vbuf = (char *) vertex_buffer; + float start_x = x; + int offset = 0; + + stb_easy_font_color c = { 255,255,255,255 }; // use structure copying to avoid needing depending on memcpy() + if (color) { c.c[0] = color[0]; c.c[1] = color[1]; c.c[2] = color[2]; c.c[3] = color[3]; } + + while (*text && offset < vbuf_size) { + if (*text == '\n') { + y += 12; + x = start_x; + } else { + unsigned char advance = stb_easy_font_charinfo[*text-32].advance; + float y_ch = advance & 16 ? y+1 : y; + int h_seg, v_seg, num_h, num_v; + h_seg = stb_easy_font_charinfo[*text-32 ].h_seg; + v_seg = stb_easy_font_charinfo[*text-32 ].v_seg; + num_h = stb_easy_font_charinfo[*text-32+1].h_seg - h_seg; + num_v = stb_easy_font_charinfo[*text-32+1].v_seg - v_seg; + offset = stb_easy_font_draw_segs(x, y_ch, &stb_easy_font_hseg[h_seg], num_h, 0, c, vbuf, vbuf_size, offset); + offset = stb_easy_font_draw_segs(x, y_ch, &stb_easy_font_vseg[v_seg], num_v, 1, c, vbuf, vbuf_size, offset); + x += advance & 15; + x += stb_easy_font_spacing_val; + } + ++text; + } + return (unsigned) offset/64; +} + +static int stb_easy_font_width(char *text) +{ + float len = 0; + float max_len = 0; + while (*text) { + if (*text == '\n') { + if (len > max_len) max_len = len; + len = 0; + } else { + len += stb_easy_font_charinfo[*text-32].advance & 15; + len += stb_easy_font_spacing_val; + } + ++text; + } + if (len > max_len) max_len = len; + return (int) ceil(max_len); +} + +static int stb_easy_font_height(char *text) +{ + float y = 0; + int nonempty_line=0; + while (*text) { + if (*text == '\n') { + y += 12; + nonempty_line = 0; + } else { + nonempty_line = 1; + } + ++text; + } + return (int) ceil(y + (nonempty_line ? 12 : 0)); +} +#endif diff --git a/impeller/third_party/stb/stb/stb_herringbone_wang_tile.h b/impeller/third_party/stb/stb/stb_herringbone_wang_tile.h new file mode 100644 index 0000000000000..ba2cf6027ef6f --- /dev/null +++ b/impeller/third_party/stb/stb/stb_herringbone_wang_tile.h @@ -0,0 +1,1220 @@ +/* stbhw - v0.6 - http://nothings.org/gamedev/herringbone + Herringbone Wang Tile Generator - Sean Barrett 2014 - public domain + +== LICENSE ============================== + +This software is dual-licensed to the public domain and under the following +license: you are granted a perpetual, irrevocable license to copy, modify, +publish, and distribute this file as you see fit. + +== WHAT IT IS =========================== + + This library is an SDK for Herringbone Wang Tile generation: + + http://nothings.org/gamedev/herringbone + + The core design is that you use this library offline to generate a + "template" of the tiles you'll create. You then edit those tiles, then + load the created tile image file back into this library and use it at + runtime to generate "maps". + + You cannot load arbitrary tile image files with this library; it is + only designed to load image files made from the template it created. + It stores a binary description of the tile sizes & constraints in a + few pixels, and uses those to recover the rules, rather than trying + to parse the tiles themselves. + + You *can* use this library to generate from arbitrary tile sets, but + only by loading the tile set and specifying the constraints explicitly + yourself. + +== COMPILING ============================ + + 1. #define STB_HERRINGBONE_WANG_TILE_IMPLEMENTATION before including this + header file in *one* source file to create the implementation + in that source file. + + 2. optionally #define STB_HBWANG_RAND() to be a random number + generator. if you don't define it, it will use rand(), + and you need to seed srand() yourself. + + 3. optionally #define STB_HBWANG_ASSERT(x), otherwise + it will use assert() + + 4. optionally #define STB_HBWANG_STATIC to force all symbols to be + static instead of public, so they are only accesible + in the source file that creates the implementation + + 5. optionally #define STB_HBWANG_NO_REPITITION_REDUCTION to disable + the code that tries to reduce having the same tile appear + adjacent to itself in wang-corner-tile mode (e.g. imagine + if you were doing something where 90% of things should be + the same grass tile, you need to disable this system) + + 6. optionally define STB_HBWANG_MAX_X and STB_HBWANG_MAX_Y + to be the max dimensions of the generated map in multiples + of the wang tile's short side's length (e.g. if you + have 20x10 wang tiles, so short_side_len=10, and you + have MAX_X is 17, then the largest map you can generate + is 170 pixels wide). The defaults are 100x100. This + is used to define static arrays which affect memory + usage. + +== USING ================================ + + To use the map generator, you need a tileset. You can download + some sample tilesets from http://nothings.org/gamedev/herringbone + + Then see the "sample application" below. + + You can also use this file to generate templates for + tilesets which you then hand-edit to create the data. + + +== MEMORY MANAGEMENT ==================== + + The tileset loader allocates memory with malloc(). The map + generator does no memory allocation, so e.g. you can load + tilesets at startup and never free them and never do any + further allocation. + + +== SAMPLE APPLICATION =================== + +#include +#include +#include + +#define STB_IMAGE_IMPLEMENTATION +#include "stb_image.h" // http://nothings.org/stb_image.c + +#define STB_IMAGE_WRITE_IMPLEMENTATION +#include "stb_image_write.h" // http://nothings.org/stb/stb_image_write.h + +#define STB_HBWANG_IMPLEMENTATION +#include "stb_hbwang.h" + +int main(int argc, char **argv) +{ + unsigned char *data; + int xs,ys, w,h; + stbhw_tileset ts; + + if (argc != 4) { + fprintf(stderr, "Usage: mapgen {tile-file} {xsize} {ysize}\n" + "generates file named 'test_map.png'\n"); + exit(1); + } + data = stbi_load(argv[1], &w, &h, NULL, 3); + xs = atoi(argv[2]); + ys = atoi(argv[3]); + if (data == NULL) { + fprintf(stderr, "Error opening or parsing '%s' as an image file\n", argv[1]); + exit(1); + } + if (xs < 1 || xs > 1000) { + fprintf(stderr, "xsize invalid or out of range\n"); + exit(1); + } + if (ys < 1 || ys > 1000) { + fprintf(stderr, "ysize invalid or out of range\n"); + exit(1); + } + + stbhw_build_tileset_from_image(&ts, data, w*3, w, h); + free(data); + + // allocate a buffer to create the final image to + data = malloc(3 * xs * ys); + + srand(time(NULL)); + stbhw_generate_image(&ts, NULL, data, xs*3, xs, ys); + + stbi_write_png("test_map.png", xs, ys, 3, data, xs*3); + + stbhw_free_tileset(&ts); + free(data); + + return 0; +} + +== VERSION HISTORY =================== + + 0.6 2014-08-17 - fix broken map-maker + 0.5 2014-07-07 - initial release + +*/ + +////////////////////////////////////////////////////////////////////////////// +// // +// HEADER FILE SECTION // +// // + +#ifndef INCLUDE_STB_HWANG_H +#define INCLUDE_STB_HWANG_H + +#ifdef STB_HBWANG_STATIC +#define STBHW_EXTERN static +#else +#ifdef __cplusplus +#define STBHW_EXTERN extern "C" +#else +#define STBHW_EXTERN extern +#endif +#endif + +typedef struct stbhw_tileset stbhw_tileset; + +// returns description of last error produced by any function (not thread-safe) +STBHW_EXTERN char *stbhw_get_last_error(void); + +// build a tileset from an image that conforms to a template created by this +// library. (you allocate storage for stbhw_tileset and function fills it out; +// memory for individual tiles are malloc()ed). +// returns non-zero on success, 0 on error +STBHW_EXTERN int stbhw_build_tileset_from_image(stbhw_tileset *ts, + unsigned char *pixels, int stride_in_bytes, int w, int h); + +// free a tileset built by stbhw_build_tileset_from_image +STBHW_EXTERN void stbhw_free_tileset(stbhw_tileset *ts); + +// generate a map that is w * h pixels (3-bytes each) +// returns non-zero on success, 0 on error +// not thread-safe (uses a global data structure to avoid memory management) +// weighting should be NULL, as non-NULL weighting is currently untested +STBHW_EXTERN int stbhw_generate_image(stbhw_tileset *ts, int **weighting, + unsigned char *pixels, int stride_in_bytes, int w, int h); + +////////////////////////////////////// +// +// TILESET DATA STRUCTURE +// +// if you use the image-to-tileset system from this file, you +// don't need to worry about these data structures. but if you +// want to build/load a tileset yourself, you'll need to fill +// these out. + +typedef struct +{ + // the edge or vertex constraints, according to diagram below + signed char a,b,c,d,e,f; + + // The herringbone wang tile data; it is a bitmap which is either + // w=2*short_sidelen,h=short_sidelen, or w=short_sidelen,h=2*short_sidelen. + // it is always RGB, stored row-major, with no padding between rows. + // (allocate stbhw_tile structure to be large enough for the pixel data) + unsigned char pixels[1]; +} stbhw_tile; + +struct stbhw_tileset +{ + int is_corner; + int num_color[6]; // number of colors for each of 6 edge types or 4 corner types + int short_side_len; + stbhw_tile **h_tiles; + stbhw_tile **v_tiles; + int num_h_tiles, max_h_tiles; + int num_v_tiles, max_v_tiles; +}; + +/////////////// TEMPLATE GENERATOR ////////////////////////// + +// when requesting a template, you fill out this data +typedef struct +{ + int is_corner; // using corner colors or edge colors? + int short_side_len; // rectangles is 2n x n, n = short_side_len + int num_color[6]; // see below diagram for meaning of the index to this; + // 6 values if edge (!is_corner), 4 values if is_corner + // legal numbers: 1..8 if edge, 1..4 if is_corner + int num_vary_x; // additional number of variations along x axis in the template + int num_vary_y; // additional number of variations along y axis in the template + int corner_type_color_template[4][4]; + // if corner_type_color_template[s][t] is non-zero, then any + // corner of type s generated as color t will get a little + // corner sample markup in the template image data + +} stbhw_config; + +// computes the size needed for the template image +STBHW_EXTERN void stbhw_get_template_size(stbhw_config *c, int *w, int *h); + +// generates a template image, assuming data is 3*w*h bytes long, RGB format +STBHW_EXTERN int stbhw_make_template(stbhw_config *c, unsigned char *data, int w, int h, int stride_in_bytes); + +#endif//INCLUDE_STB_HWANG_H + + +// TILE CONSTRAINT TYPES +// +// there are 4 "types" of corners and 6 types of edges. +// you can configure the tileset to have different numbers +// of colors for each type of color or edge. +// +// corner types: +// +// 0---*---1---*---2---*---3 +// | | | +// * * * +// | | | +// 1---*---2---*---3 0---*---1---*---2 +// | | | +// * * * +// | | | +// 0---*---1---*---2---*---3 +// +// +// edge types: +// +// *---2---*---3---* *---0---* +// | | | | +// 1 4 5 1 +// | | | | +// *---0---*---2---* * * +// | | +// 4 5 +// | | +// *---3---* +// +// TILE CONSTRAINTS +// +// each corner/edge has a color; this shows the name +// of the variable containing the color +// +// corner constraints: +// +// a---*---d +// | | +// * * +// | | +// a---*---b---*---c b e +// | | | | +// * * * * +// | | | | +// d---*---e---*---f c---*---f +// +// +// edge constraints: +// +// *---a---*---b---* *---a---* +// | | | | +// c d b c +// | | | | +// *---e---*---f---* * * +// | | +// d e +// | | +// *---f---* +// + + +////////////////////////////////////////////////////////////////////////////// +// // +// IMPLEMENTATION SECTION // +// // + +#ifdef STB_HERRINGBONE_WANG_TILE_IMPLEMENTATION + + +#include // memcpy +#include // malloc + +#ifndef STB_HBWANG_RAND +#include +#define STB_HBWANG_RAND() (rand() >> 4) +#endif + +#ifndef STB_HBWANG_ASSERT +#include +#define STB_HBWANG_ASSERT(x) assert(x) +#endif + +// map size +#ifndef STB_HBWANG_MAX_X +#define STB_HBWANG_MAX_X 100 +#endif + +#ifndef STB_HBWANG_MAX_Y +#define STB_HBWANG_MAX_Y 100 +#endif + +// global variables for color assignments +// @MEMORY change these to just store last two/three rows +// and keep them on the stack +static signed char c_color[STB_HBWANG_MAX_Y+6][STB_HBWANG_MAX_X+6]; +static signed char v_color[STB_HBWANG_MAX_Y+6][STB_HBWANG_MAX_X+5]; +static signed char h_color[STB_HBWANG_MAX_Y+5][STB_HBWANG_MAX_X+6]; + +static char *stbhw_error; +STBHW_EXTERN char *stbhw_get_last_error(void) +{ + char *temp = stbhw_error; + stbhw_error = 0; + return temp; +} + + + + +///////////////////////////////////////////////////////////// +// +// SHARED TEMPLATE-DESCRIPTION CODE +// +// Used by both template generator and tileset parser; by +// using the same code, they are locked in sync and we don't +// need to try to do more sophisticated parsing of edge color +// markup or something. + +typedef void stbhw__process_rect(struct stbhw__process *p, int xpos, int ypos, + int a, int b, int c, int d, int e, int f); + +typedef struct stbhw__process +{ + stbhw_tileset *ts; + stbhw_config *c; + stbhw__process_rect *process_h_rect; + stbhw__process_rect *process_v_rect; + unsigned char *data; + int stride,w,h; +} stbhw__process; + +static void stbhw__process_h_row(stbhw__process *p, + int xpos, int ypos, + int a0, int a1, + int b0, int b1, + int c0, int c1, + int d0, int d1, + int e0, int e1, + int f0, int f1, + int variants) +{ + int a,b,c,d,e,f,v; + + for (v=0; v < variants; ++v) + for (f=f0; f <= f1; ++f) + for (e=e0; e <= e1; ++e) + for (d=d0; d <= d1; ++d) + for (c=c0; c <= c1; ++c) + for (b=b0; b <= b1; ++b) + for (a=a0; a <= a1; ++a) { + p->process_h_rect(p, xpos, ypos, a,b,c,d,e,f); + xpos += 2*p->c->short_side_len + 3; + } +} + +static void stbhw__process_v_row(stbhw__process *p, + int xpos, int ypos, + int a0, int a1, + int b0, int b1, + int c0, int c1, + int d0, int d1, + int e0, int e1, + int f0, int f1, + int variants) +{ + int a,b,c,d,e,f,v; + + for (v=0; v < variants; ++v) + for (f=f0; f <= f1; ++f) + for (e=e0; e <= e1; ++e) + for (d=d0; d <= d1; ++d) + for (c=c0; c <= c1; ++c) + for (b=b0; b <= b1; ++b) + for (a=a0; a <= a1; ++a) { + p->process_v_rect(p, xpos, ypos, a,b,c,d,e,f); + xpos += p->c->short_side_len+3; + } +} + +static void stbhw__get_template_info(stbhw_config *c, int *w, int *h, int *h_count, int *v_count) +{ + int size_x,size_y; + int horz_count,vert_count; + + if (c->is_corner) { + int horz_w = c->num_color[1] * c->num_color[2] * c->num_color[3] * c->num_vary_x; + int horz_h = c->num_color[0] * c->num_color[1] * c->num_color[2] * c->num_vary_y; + + int vert_w = c->num_color[0] * c->num_color[3] * c->num_color[2] * c->num_vary_y; + int vert_h = c->num_color[1] * c->num_color[0] * c->num_color[3] * c->num_vary_x; + + int horz_x = horz_w * (2*c->short_side_len + 3); + int horz_y = horz_h * ( c->short_side_len + 3); + + int vert_x = vert_w * ( c->short_side_len + 3); + int vert_y = vert_h * (2*c->short_side_len + 3); + + horz_count = horz_w * horz_h; + vert_count = vert_w * vert_h; + + size_x = horz_x > vert_x ? horz_x : vert_x; + size_y = 2 + horz_y + 2 + vert_y; + } else { + int horz_w = c->num_color[0] * c->num_color[1] * c->num_color[2] * c->num_vary_x; + int horz_h = c->num_color[3] * c->num_color[4] * c->num_color[2] * c->num_vary_y; + + int vert_w = c->num_color[0] * c->num_color[5] * c->num_color[1] * c->num_vary_y; + int vert_h = c->num_color[3] * c->num_color[4] * c->num_color[5] * c->num_vary_x; + + int horz_x = horz_w * (2*c->short_side_len + 3); + int horz_y = horz_h * ( c->short_side_len + 3); + + int vert_x = vert_w * ( c->short_side_len + 3); + int vert_y = vert_h * (2*c->short_side_len + 3); + + horz_count = horz_w * horz_h; + vert_count = vert_w * vert_h; + + size_x = horz_x > vert_x ? horz_x : vert_x; + size_y = 2 + horz_y + 2 + vert_y; + } + if (w) *w = size_x; + if (h) *h = size_y; + if (h_count) *h_count = horz_count; + if (v_count) *v_count = vert_count; +} + +STBHW_EXTERN void stbhw_get_template_size(stbhw_config *c, int *w, int *h) +{ + stbhw__get_template_info(c, w, h, NULL, NULL); +} + +static int stbhw__process_template(stbhw__process *p) +{ + int i,j,k,q, ypos; + int size_x, size_y; + stbhw_config *c = p->c; + + stbhw__get_template_info(c, &size_x, &size_y, NULL, NULL); + + if (p->w < size_x || p->h < size_y) { + stbhw_error = "image too small for configuration"; + return 0; + } + + if (c->is_corner) { + ypos = 2; + for (k=0; k < c->num_color[2]; ++k) { + for (j=0; j < c->num_color[1]; ++j) { + for (i=0; i < c->num_color[0]; ++i) { + for (q=0; q < c->num_vary_y; ++q) { + stbhw__process_h_row(p, 0,ypos, + 0,c->num_color[1]-1, 0,c->num_color[2]-1, 0,c->num_color[3]-1, + i,i, j,j, k,k, + c->num_vary_x); + ypos += c->short_side_len + 3; + } + } + } + } + ypos += 2; + for (k=0; k < c->num_color[3]; ++k) { + for (j=0; j < c->num_color[0]; ++j) { + for (i=0; i < c->num_color[1]; ++i) { + for (q=0; q < c->num_vary_x; ++q) { + stbhw__process_v_row(p, 0,ypos, + 0,c->num_color[0]-1, 0,c->num_color[3]-1, 0,c->num_color[2]-1, + i,i, j,j, k,k, + c->num_vary_y); + ypos += (c->short_side_len*2) + 3; + } + } + } + } + assert(ypos == size_y); + } else { + ypos = 2; + for (k=0; k < c->num_color[3]; ++k) { + for (j=0; j < c->num_color[4]; ++j) { + for (i=0; i < c->num_color[2]; ++i) { + for (q=0; q < c->num_vary_y; ++q) { + stbhw__process_h_row(p, 0,ypos, + 0,c->num_color[2]-1, k,k, + 0,c->num_color[1]-1, j,j, + 0,c->num_color[0]-1, i,i, + c->num_vary_x); + ypos += c->short_side_len + 3; + } + } + } + } + ypos += 2; + for (k=0; k < c->num_color[3]; ++k) { + for (j=0; j < c->num_color[4]; ++j) { + for (i=0; i < c->num_color[5]; ++i) { + for (q=0; q < c->num_vary_x; ++q) { + stbhw__process_v_row(p, 0,ypos, + 0,c->num_color[0]-1, i,i, + 0,c->num_color[1]-1, j,j, + 0,c->num_color[5]-1, k,k, + c->num_vary_y); + ypos += (c->short_side_len*2) + 3; + } + } + } + } + assert(ypos == size_y); + } + return 1; +} + + +///////////////////////////////////////////////////////////// +// +// MAP GENERATOR +// + +static void stbhw__draw_pixel(unsigned char *output, int stride, int x, int y, unsigned char c[3]) +{ + memcpy(output + y*stride + x*3, c, 3); +} + +static void stbhw__draw_h_tile(unsigned char *output, int stride, int xmax, int ymax, int x, int y, stbhw_tile *h, int sz) +{ + int i,j; + for (j=0; j < sz; ++j) + if (y+j >= 0 && y+j < ymax) + for (i=0; i < sz*2; ++i) + if (x+i >= 0 && x+i < xmax) + stbhw__draw_pixel(output,stride, x+i,y+j, &h->pixels[(j*sz*2 + i)*3]); +} + +static void stbhw__draw_v_tile(unsigned char *output, int stride, int xmax, int ymax, int x, int y, stbhw_tile *h, int sz) +{ + int i,j; + for (j=0; j < sz*2; ++j) + if (y+j >= 0 && y+j < ymax) + for (i=0; i < sz; ++i) + if (x+i >= 0 && x+i < xmax) + stbhw__draw_pixel(output,stride, x+i,y+j, &h->pixels[(j*sz + i)*3]); +} + + +// randomly choose a tile that fits constraints for a given spot, and update the constraints +static stbhw_tile * stbhw__choose_tile(stbhw_tile **list, int numlist, + signed char *a, signed char *b, signed char *c, + signed char *d, signed char *e, signed char *f, + int **weighting) +{ + int i,n,m = 1<<30,pass; + for (pass=0; pass < 2; ++pass) { + n=0; + // pass #1: + // count number of variants that match this partial set of constraints + // pass #2: + // stop on randomly selected match + for (i=0; i < numlist; ++i) { + stbhw_tile *h = list[i]; + if ((*a < 0 || *a == h->a) && + (*b < 0 || *b == h->b) && + (*c < 0 || *c == h->c) && + (*d < 0 || *d == h->d) && + (*e < 0 || *e == h->e) && + (*f < 0 || *f == h->f)) { + if (weighting) + n += weighting[0][i]; + else + n += 1; + if (n > m) { + // use list[i] + // update constraints to reflect what we placed + *a = h->a; + *b = h->b; + *c = h->c; + *d = h->d; + *e = h->e; + *f = h->f; + return h; + } + } + } + if (n == 0) { + stbhw_error = "couldn't find tile matching constraints"; + return NULL; + } + m = STB_HBWANG_RAND() % n; + } + STB_HBWANG_ASSERT(0); + return NULL; +} + +static int stbhw__match(int x, int y) +{ + return c_color[y][x] == c_color[y+1][x+1]; +} + +static int stbhw__weighted(int num_options, int *weights) +{ + int k, total, choice; + total = 0; + for (k=0; k < num_options; ++k) + total += weights[k]; + choice = STB_HBWANG_RAND() % total; + total = 0; + for (k=0; k < num_options; ++k) { + total += weights[k]; + if (choice < total) + break; + } + STB_HBWANG_ASSERT(k < num_options); + return k; +} + +static int stbhw__change_color(int old_color, int num_options, int *weights) +{ + if (weights) { + int k, total, choice; + total = 0; + for (k=0; k < num_options; ++k) + if (k != old_color) + total += weights[k]; + choice = STB_HBWANG_RAND() % total; + total = 0; + for (k=0; k < num_options; ++k) { + if (k != old_color) { + total += weights[k]; + if (choice < total) + break; + } + } + STB_HBWANG_ASSERT(k < num_options); + return k; + } else { + int offset = 1+STB_HBWANG_RAND() % (num_options-1); + return (old_color+offset) % num_options; + } +} + + + +// generate a map that is w * h pixels (3-bytes each) +// returns 1 on success, 0 on error +STBHW_EXTERN int stbhw_generate_image(stbhw_tileset *ts, int **weighting, unsigned char *output, int stride, int w, int h) +{ + int sidelen = ts->short_side_len; + int xmax = (w / sidelen) + 6; + int ymax = (h / sidelen) + 6; + if (xmax > STB_HBWANG_MAX_X+6 || ymax > STB_HBWANG_MAX_Y+6) { + stbhw_error = "increase STB_HBWANG_MAX_X/Y"; + return 0; + } + + if (ts->is_corner) { + int i,j, ypos; + int *cc = ts->num_color; + + for (j=0; j < ymax; ++j) { + for (i=0; i < xmax; ++i) { + int p = (i-j+1)&3; // corner type + if (weighting==NULL || weighting[p]==0 || cc[p] == 1) + c_color[j][i] = STB_HBWANG_RAND() % cc[p]; + else + c_color[j][i] = stbhw__weighted(cc[p], weighting[p]); + } + } + #ifndef STB_HBWANG_NO_REPITITION_REDUCTION + // now go back through and make sure we don't have adjancent 3x2 vertices that are identical, + // to avoid really obvious repetition (which happens easily with extreme weights) + for (j=0; j < ymax-3; ++j) { + for (i=0; i < xmax-3; ++i) { + int p = (i-j+1) & 3; // corner type + STB_HBWANG_ASSERT(i+3 < STB_HBWANG_MAX_X+6); + STB_HBWANG_ASSERT(j+3 < STB_HBWANG_MAX_Y+6); + if (stbhw__match(i,j) && stbhw__match(i,j+1) && stbhw__match(i,j+2) + && stbhw__match(i+1,j) && stbhw__match(i+1,j+1) && stbhw__match(i+1,j+2)) { + int p = ((i+1)-(j+1)+1) & 3; + if (cc[p] > 1) + c_color[j+1][i+1] = stbhw__change_color(c_color[j+1][i+1], cc[p], weighting ? weighting[p] : NULL); + } + if (stbhw__match(i,j) && stbhw__match(i+1,j) && stbhw__match(i+2,j) + && stbhw__match(i,j+1) && stbhw__match(i+1,j+1) && stbhw__match(i+2,j+1)) { + int p = ((i+2)-(j+1)+1) & 3; + if (cc[p] > 1) + c_color[j+1][i+2] = stbhw__change_color(c_color[j+1][i+2], cc[p], weighting ? weighting[p] : NULL); + } + } + } + #endif + + ypos = -1 * sidelen; + for (j = -1; ypos < h; ++j) { + // a general herringbone row consists of: + // horizontal left block, the bottom of a previous vertical, the top of a new vertical + int phase = (j & 3); + // displace horizontally according to pattern + if (phase == 0) { + i = 0; + } else { + i = phase-4; + } + for (i;; i += 4) { + int xpos = i * sidelen; + if (xpos >= w) + break; + // horizontal left-block + if (xpos + sidelen*2 >= 0 && ypos >= 0) { + stbhw_tile *t = stbhw__choose_tile( + ts->h_tiles, ts->num_h_tiles, + &c_color[j+2][i+2], &c_color[j+2][i+3], &c_color[j+2][i+4], + &c_color[j+3][i+2], &c_color[j+3][i+3], &c_color[j+3][i+4], + weighting + ); + if (t == NULL) + return 0; + stbhw__draw_h_tile(output,stride,w,h, xpos, ypos, t, sidelen); + } + xpos += sidelen * 2; + // now we're at the end of a previous vertical one + xpos += sidelen; + // now we're at the start of a new vertical one + if (xpos < w) { + stbhw_tile *t = stbhw__choose_tile( + ts->v_tiles, ts->num_v_tiles, + &c_color[j+2][i+5], &c_color[j+3][i+5], &c_color[j+4][i+5], + &c_color[j+2][i+6], &c_color[j+3][i+6], &c_color[j+4][i+6], + weighting + ); + if (t == NULL) + return 0; + stbhw__draw_v_tile(output,stride,w,h, xpos, ypos, t, sidelen); + } + } + ypos += sidelen; + } + } else { + // @TODO edge-color repetition reduction + int i,j, ypos; + memset(v_color, -1, sizeof(v_color)); + memset(h_color, -1, sizeof(h_color)); + + ypos = -1 * sidelen; + for (j = -1; ypos= w) + break; + // horizontal left-block + if (xpos + sidelen*2 >= 0 && ypos >= 0) { + stbhw_tile *t = stbhw__choose_tile( + ts->h_tiles, ts->num_h_tiles, + &h_color[j+2][i+2], &h_color[j+2][i+3], + &v_color[j+2][i+2], &v_color[j+2][i+4], + &h_color[j+3][i+2], &h_color[j+3][i+3], + weighting + ); + if (t == NULL) return 0; + stbhw__draw_h_tile(output,stride,w,h, xpos, ypos, t, sidelen); + } + xpos += sidelen * 2; + // now we're at the end of a previous vertical one + xpos += sidelen; + // now we're at the start of a new vertical one + if (xpos < w) { + stbhw_tile *t = stbhw__choose_tile( + ts->v_tiles, ts->num_v_tiles, + &h_color[j+2][i+5], + &v_color[j+2][i+5], &v_color[j+2][i+6], + &v_color[j+3][i+5], &v_color[j+3][i+6], + &h_color[j+4][i+5], + weighting + ); + if (t == NULL) return 0; + stbhw__draw_v_tile(output,stride,w,h, xpos, ypos, t, sidelen); + } + } + ypos += sidelen; + } + } + return 1; +} + +static void stbhw__parse_h_rect(stbhw__process *p, int xpos, int ypos, + int a, int b, int c, int d, int e, int f) +{ + int len = p->c->short_side_len; + stbhw_tile *h = (stbhw_tile *) malloc(sizeof(*h)-1 + 3 * (len*2) * len); + int i,j; + ++xpos; + ++ypos; + h->a = a, h->b = b, h->c = c, h->d = d, h->e = e, h->f = f; + for (j=0; j < len; ++j) + for (i=0; i < len*2; ++i) + memcpy(h->pixels + j*(3*len*2) + i*3, p->data+(ypos+j)*p->stride+(xpos+i)*3, 3); + STB_HBWANG_ASSERT(p->ts->num_h_tiles < p->ts->max_h_tiles); + p->ts->h_tiles[p->ts->num_h_tiles++] = h; +} + +static void stbhw__parse_v_rect(stbhw__process *p, int xpos, int ypos, + int a, int b, int c, int d, int e, int f) +{ + int len = p->c->short_side_len; + stbhw_tile *h = (stbhw_tile *) malloc(sizeof(*h)-1 + 3 * (len*2) * len); + int i,j; + ++xpos; + ++ypos; + h->a = a, h->b = b, h->c = c, h->d = d, h->e = e, h->f = f; + for (j=0; j < len*2; ++j) + for (i=0; i < len; ++i) + memcpy(h->pixels + j*(3*len) + i*3, p->data+(ypos+j)*p->stride+(xpos+i)*3, 3); + STB_HBWANG_ASSERT(p->ts->num_v_tiles < p->ts->max_v_tiles); + p->ts->v_tiles[p->ts->num_v_tiles++] = h; +} + +STBHW_EXTERN int stbhw_build_tileset_from_image(stbhw_tileset *ts, unsigned char *data, int stride, int w, int h) +{ + int i, h_count, v_count; + unsigned char header[9]; + stbhw_config c = { 0 }; + stbhw__process p = { 0 }; + + // extract binary header + + // remove encoding that makes it more visually obvious it encodes actual data + for (i=0; i < 9; ++i) + header[i] = data[w*3 - 1 - i] ^ (i*55); + + // extract header info + if (header[7] == 0xc0) { + // corner-type + c.is_corner = 1; + for (i=0; i < 4; ++i) + c.num_color[i] = header[i]; + c.num_vary_x = header[4]; + c.num_vary_y = header[5]; + c.short_side_len = header[6]; + } else { + c.is_corner = 0; + // edge-type + for (i=0; i < 6; ++i) + c.num_color[i] = header[i]; + c.num_vary_x = header[6]; + c.num_vary_y = header[7]; + c.short_side_len = header[8]; + } + + if (c.num_vary_x < 0 || c.num_vary_x > 64 || c.num_vary_y < 0 || c.num_vary_y > 64) + return 0; + if (c.short_side_len == 0) + return 0; + if (c.num_color[0] > 32 || c.num_color[1] > 32 || c.num_color[2] > 32 || c.num_color[3] > 32) + return 0; + + stbhw__get_template_info(&c, NULL, NULL, &h_count, &v_count); + + ts->is_corner = c.is_corner; + ts->short_side_len = c.short_side_len; + memcpy(ts->num_color, c.num_color, sizeof(ts->num_color)); + + ts->max_h_tiles = h_count; + ts->max_v_tiles = v_count; + + ts->num_h_tiles = ts->num_v_tiles = 0; + + ts->h_tiles = (stbhw_tile **) malloc(sizeof(*ts->h_tiles) * h_count); + ts->v_tiles = (stbhw_tile **) malloc(sizeof(*ts->v_tiles) * v_count); + + p.ts = ts; + p.data = data; + p.stride = stride; + p.process_h_rect = stbhw__parse_h_rect; + p.process_v_rect = stbhw__parse_v_rect; + p.w = w; + p.h = h; + p.c = &c; + + // load all the tiles out of the image + return stbhw__process_template(&p); +} + +STBHW_EXTERN void stbhw_free_tileset(stbhw_tileset *ts) +{ + int i; + for (i=0; i < ts->num_h_tiles; ++i) + free(ts->h_tiles[i]); + for (i=0; i < ts->num_v_tiles; ++i) + free(ts->v_tiles[i]); + free(ts->h_tiles); + free(ts->v_tiles); + ts->h_tiles = NULL; + ts->v_tiles = NULL; + ts->num_h_tiles = ts->max_h_tiles = 0; + ts->num_v_tiles = ts->max_v_tiles = 0; +} + +////////////////////////////////////////////////////////////////////////////// +// +// GENERATOR +// +// + + +// shared code + +static void stbhw__set_pixel(unsigned char *data, int stride, int xpos, int ypos, unsigned char color[3]) +{ + memcpy(data + ypos*stride + xpos*3, color, 3); +} + +static void stbhw__stbhw__set_pixel_whiten(unsigned char *data, int stride, int xpos, int ypos, unsigned char color[3]) +{ + unsigned char c2[3]; + int i; + for (i=0; i < 3; ++i) + c2[i] = (color[i]*2 + 255)/3; + memcpy(data + ypos*stride + xpos*3, c2, 3); +} + + +static unsigned char stbhw__black[3] = { 0,0,0 }; + +// each edge set gets its own unique color variants +// used http://phrogz.net/css/distinct-colors.html to generate this set, +// but it's not very good and needs to be revised + +static unsigned char stbhw__color[7][8][3] = +{ + { {255,51,51} , {143,143,29}, {0,199,199}, {159,119,199}, {0,149,199} , {143, 0,143}, {255,128,0}, {64,255,0}, }, + { {235,255,30 }, {255,0,255}, {199,139,119}, {29,143, 57}, {143,0,71} , { 0,143,143}, {0,99,199}, {143,71,0}, }, + { {0,149,199} , {143, 0,143}, {255,128,0}, {64,255,0}, {255,191,0} , {51,255,153}, {0,0,143}, {199,119,159},}, + { {143,0,71} , { 0,143,143}, {0,99,199}, {143,71,0}, {255,190,153}, { 0,255,255}, {128,0,255}, {255,51,102},}, + { {255,191,0} , {51,255,153}, {0,0,143}, {199,119,159}, {255,51,51} , {143,143,29}, {0,199,199}, {159,119,199},}, + { {255,190,153}, { 0,255,255}, {128,0,255}, {255,51,102}, {235,255,30 }, {255,0,255}, {199,139,119}, {29,143, 57}, }, + + { {40,40,40 }, { 90,90,90 }, { 150,150,150 }, { 200,200,200 }, + { 255,90,90 }, { 160,160,80}, { 50,150,150 }, { 200,50,200 } }, +}; + +static void stbhw__draw_hline(unsigned char *data, int stride, int xpos, int ypos, int color, int len, int slot) +{ + int i; + int j = len * 6 / 16; + int k = len * 10 / 16; + for (i=0; i < len; ++i) + stbhw__set_pixel(data, stride, xpos+i, ypos, stbhw__black); + if (k-j < 2) { + j = len/2 - 1; + k = j+2; + if (len & 1) + ++k; + } + for (i=j; i < k; ++i) + stbhw__stbhw__set_pixel_whiten(data, stride, xpos+i, ypos, stbhw__color[slot][color]); +} + +static void stbhw__draw_vline(unsigned char *data, int stride, int xpos, int ypos, int color, int len, int slot) +{ + int i; + int j = len * 6 / 16; + int k = len * 10 / 16; + for (i=0; i < len; ++i) + stbhw__set_pixel(data, stride, xpos, ypos+i, stbhw__black); + if (k-j < 2) { + j = len/2 - 1; + k = j+2; + if (len & 1) + ++k; + } + for (i=j; i < k; ++i) + stbhw__stbhw__set_pixel_whiten(data, stride, xpos, ypos+i, stbhw__color[slot][color]); +} + +// 0--*--1--*--2--*--3 +// | | | +// * * * +// | | | +// 1--*--2--*--3 0--*--1--*--2 +// | | | +// * * * +// | | | +// 0--*--1--*--2--*--3 +// +// variables while enumerating (no correspondence between corners +// of the types is implied by these variables) +// +// a-----b-----c a-----d +// | | | | +// | | | | +// | | | | +// d-----e-----f b e +// | | +// | | +// | | +// c-----f +// + +unsigned char stbhw__corner_colors[4][4][3] = +{ + { { 255,0,0 }, { 200,200,200 }, { 100,100,200 }, { 255,200,150 }, }, + { { 0,0,255 }, { 255,255,0 }, { 100,200,100 }, { 150,255,200 }, }, + { { 255,0,255 }, { 80,80,80 }, { 200,100,100 }, { 200,150,255 }, }, + { { 0,255,255 }, { 0,255,0 }, { 200,120,200 }, { 255,200,200 }, }, +}; + +int stbhw__corner_colors_to_edge_color[4][4] = +{ + // 0 1 2 3 + { 0, 1, 4, 9, }, // 0 + { 2, 3, 5, 10, }, // 1 + { 6, 7, 8, 11, }, // 2 + { 12, 13, 14, 15, }, // 3 +}; + +#define stbhw__c2e stbhw__corner_colors_to_edge_color + +static void stbhw__draw_clipped_corner(unsigned char *data, int stride, int xpos, int ypos, int w, int h, int x, int y) +{ + static unsigned char template_color[3] = { 167,204,204 }; + int i,j; + for (j = -2; j <= 1; ++j) { + for (i = -2; i <= 1; ++i) { + if ((i == -2 || i == 1) && (j == -2 || j == 1)) + continue; + else { + if (x+i < 1 || x+i > w) continue; + if (y+j < 1 || y+j > h) continue; + stbhw__set_pixel(data, stride, xpos+x+i, ypos+y+j, template_color); + + } + } + } +} + +static void stbhw__edge_process_h_rect(stbhw__process *p, int xpos, int ypos, + int a, int b, int c, int d, int e, int f) +{ + int len = p->c->short_side_len; + stbhw__draw_hline(p->data, p->stride, xpos+1 , ypos , a, len, 2); + stbhw__draw_hline(p->data, p->stride, xpos+ len+1 , ypos , b, len, 3); + stbhw__draw_vline(p->data, p->stride, xpos , ypos+1 , c, len, 1); + stbhw__draw_vline(p->data, p->stride, xpos+2*len+1 , ypos+1 , d, len, 4); + stbhw__draw_hline(p->data, p->stride, xpos+1 , ypos + len+1, e, len, 0); + stbhw__draw_hline(p->data, p->stride, xpos + len+1 , ypos + len+1, f, len, 2); +} + +static void stbhw__edge_process_v_rect(stbhw__process *p, int xpos, int ypos, + int a, int b, int c, int d, int e, int f) +{ + int len = p->c->short_side_len; + stbhw__draw_hline(p->data, p->stride, xpos+1 , ypos , a, len, 0); + stbhw__draw_vline(p->data, p->stride, xpos , ypos+1 , b, len, 5); + stbhw__draw_vline(p->data, p->stride, xpos + len+1, ypos+1 , c, len, 1); + stbhw__draw_vline(p->data, p->stride, xpos , ypos + len+1, d, len, 4); + stbhw__draw_vline(p->data, p->stride, xpos + len+1, ypos + len+1, e, len, 5); + stbhw__draw_hline(p->data, p->stride, xpos+1 , ypos + 2*len+1, f, len, 3); +} + +static void stbhw__corner_process_h_rect(stbhw__process *p, int xpos, int ypos, + int a, int b, int c, int d, int e, int f) +{ + int len = p->c->short_side_len; + + stbhw__draw_hline(p->data, p->stride, xpos+1 , ypos , stbhw__c2e[a][b], len, 2); + stbhw__draw_hline(p->data, p->stride, xpos+ len+1 , ypos , stbhw__c2e[b][c], len, 3); + stbhw__draw_vline(p->data, p->stride, xpos , ypos+1 , stbhw__c2e[a][d], len, 1); + stbhw__draw_vline(p->data, p->stride, xpos+2*len+1 , ypos+1 , stbhw__c2e[c][f], len, 4); + stbhw__draw_hline(p->data, p->stride, xpos+1 , ypos + len+1, stbhw__c2e[d][e], len, 0); + stbhw__draw_hline(p->data, p->stride, xpos + len+1 , ypos + len+1, stbhw__c2e[e][f], len, 2); + + if (p->c->corner_type_color_template[1][a]) stbhw__draw_clipped_corner(p->data,p->stride, xpos,ypos, len*2,len, 1,1); + if (p->c->corner_type_color_template[2][b]) stbhw__draw_clipped_corner(p->data,p->stride, xpos,ypos, len*2,len, len+1,1); + if (p->c->corner_type_color_template[3][c]) stbhw__draw_clipped_corner(p->data,p->stride, xpos,ypos, len*2,len, len*2+1,1); + + if (p->c->corner_type_color_template[0][d]) stbhw__draw_clipped_corner(p->data,p->stride, xpos,ypos, len*2,len, 1,len+1); + if (p->c->corner_type_color_template[1][e]) stbhw__draw_clipped_corner(p->data,p->stride, xpos,ypos, len*2,len, len+1,len+1); + if (p->c->corner_type_color_template[2][f]) stbhw__draw_clipped_corner(p->data,p->stride, xpos,ypos, len*2,len, len*2+1,len+1); + + stbhw__set_pixel(p->data, p->stride, xpos , ypos, stbhw__corner_colors[1][a]); + stbhw__set_pixel(p->data, p->stride, xpos+len , ypos, stbhw__corner_colors[2][b]); + stbhw__set_pixel(p->data, p->stride, xpos+2*len+1, ypos, stbhw__corner_colors[3][c]); + stbhw__set_pixel(p->data, p->stride, xpos , ypos+len+1, stbhw__corner_colors[0][d]); + stbhw__set_pixel(p->data, p->stride, xpos+len , ypos+len+1, stbhw__corner_colors[1][e]); + stbhw__set_pixel(p->data, p->stride, xpos+2*len+1, ypos+len+1, stbhw__corner_colors[2][f]); +} + +static void stbhw__corner_process_v_rect(stbhw__process *p, int xpos, int ypos, + int a, int b, int c, int d, int e, int f) +{ + int len = p->c->short_side_len; + + stbhw__draw_hline(p->data, p->stride, xpos+1 , ypos , stbhw__c2e[a][d], len, 0); + stbhw__draw_vline(p->data, p->stride, xpos , ypos+1 , stbhw__c2e[a][b], len, 5); + stbhw__draw_vline(p->data, p->stride, xpos + len+1, ypos+1 , stbhw__c2e[d][e], len, 1); + stbhw__draw_vline(p->data, p->stride, xpos , ypos + len+1, stbhw__c2e[b][c], len, 4); + stbhw__draw_vline(p->data, p->stride, xpos + len+1, ypos + len+1, stbhw__c2e[e][f], len, 5); + stbhw__draw_hline(p->data, p->stride, xpos+1 , ypos + 2*len+1, stbhw__c2e[c][f], len, 3); + + if (p->c->corner_type_color_template[0][a]) stbhw__draw_clipped_corner(p->data,p->stride, xpos,ypos, len,len*2, 1,1); + if (p->c->corner_type_color_template[3][b]) stbhw__draw_clipped_corner(p->data,p->stride, xpos,ypos, len,len*2, 1,len+1); + if (p->c->corner_type_color_template[2][c]) stbhw__draw_clipped_corner(p->data,p->stride, xpos,ypos, len,len*2, 1,len*2+1); + + if (p->c->corner_type_color_template[1][d]) stbhw__draw_clipped_corner(p->data,p->stride, xpos,ypos, len,len*2, len+1,1); + if (p->c->corner_type_color_template[0][e]) stbhw__draw_clipped_corner(p->data,p->stride, xpos,ypos, len,len*2, len+1,len+1); + if (p->c->corner_type_color_template[3][f]) stbhw__draw_clipped_corner(p->data,p->stride, xpos,ypos, len,len*2, len+1,len*2+1); + + stbhw__set_pixel(p->data, p->stride, xpos , ypos , stbhw__corner_colors[0][a]); + stbhw__set_pixel(p->data, p->stride, xpos , ypos+len , stbhw__corner_colors[3][b]); + stbhw__set_pixel(p->data, p->stride, xpos , ypos+2*len+1, stbhw__corner_colors[2][c]); + stbhw__set_pixel(p->data, p->stride, xpos+len+1, ypos , stbhw__corner_colors[1][d]); + stbhw__set_pixel(p->data, p->stride, xpos+len+1, ypos+len , stbhw__corner_colors[0][e]); + stbhw__set_pixel(p->data, p->stride, xpos+len+1, ypos+2*len+1, stbhw__corner_colors[3][f]); +} + +// generates a template image, assuming data is 3*w*h bytes long, RGB format +STBHW_EXTERN int stbhw_make_template(stbhw_config *c, unsigned char *data, int w, int h, int stride_in_bytes) +{ + stbhw__process p; + int i; + + p.data = data; + p.w = w; + p.h = h; + p.stride = stride_in_bytes; + p.ts = 0; + p.c = c; + + if (c->is_corner) { + p.process_h_rect = stbhw__corner_process_h_rect; + p.process_v_rect = stbhw__corner_process_v_rect; + } else { + p.process_h_rect = stbhw__edge_process_h_rect; + p.process_v_rect = stbhw__edge_process_v_rect; + } + + for (i=0; i < p.h; ++i) + memset(p.data + i*p.stride, 255, 3*p.w); + + if (!stbhw__process_template(&p)) + return 0; + + if (c->is_corner) { + // write out binary information in first line of image + for (i=0; i < 4; ++i) + data[w*3-1-i] = c->num_color[i]; + data[w*3-1-i] = c->num_vary_x; + data[w*3-2-i] = c->num_vary_y; + data[w*3-3-i] = c->short_side_len; + data[w*3-4-i] = 0xc0; + } else { + for (i=0; i < 6; ++i) + data[w*3-1-i] = c->num_color[i]; + data[w*3-1-i] = c->num_vary_x; + data[w*3-2-i] = c->num_vary_y; + data[w*3-3-i] = c->short_side_len; + } + + // make it more obvious it encodes actual data + for (i=0; i < 9; ++i) + p.data[p.w*3 - 1 - i] ^= i*55; + + return 1; +} +#endif // STB_HBWANG_IMPLEMENTATION diff --git a/impeller/third_party/stb/stb/stb_image.h b/impeller/third_party/stb/stb/stb_image.h new file mode 100644 index 0000000000000..a3c1129932fc3 --- /dev/null +++ b/impeller/third_party/stb/stb/stb_image.h @@ -0,0 +1,6755 @@ +/* stb_image - v2.12 - public domain image loader - http://nothings.org/stb_image.h + no warranty implied; use at your own risk + + Do this: + #define STB_IMAGE_IMPLEMENTATION + before you include this file in *one* C or C++ file to create the implementation. + + // i.e. it should look like this: + #include ... + #include ... + #include ... + #define STB_IMAGE_IMPLEMENTATION + #include "stb_image.h" + + You can #define STBI_ASSERT(x) before the #include to avoid using assert.h. + And #define STBI_MALLOC, STBI_REALLOC, and STBI_FREE to avoid using malloc,realloc,free + + + QUICK NOTES: + Primarily of interest to game developers and other people who can + avoid problematic images and only need the trivial interface + + JPEG baseline & progressive (12 bpc/arithmetic not supported, same as stock IJG lib) + PNG 1/2/4/8-bit-per-channel (16 bpc not supported) + + TGA (not sure what subset, if a subset) + BMP non-1bpp, non-RLE + PSD (composited view only, no extra channels, 8/16 bit-per-channel) + + GIF (*comp always reports as 4-channel) + HDR (radiance rgbE format) + PIC (Softimage PIC) + PNM (PPM and PGM binary only) + + Animated GIF still needs a proper API, but here's one way to do it: + http://gist.github.com/urraka/685d9a6340b26b830d49 + + - decode from memory or through FILE (define STBI_NO_STDIO to remove code) + - decode from arbitrary I/O callbacks + - SIMD acceleration on x86/x64 (SSE2) and ARM (NEON) + + Full documentation under "DOCUMENTATION" below. + + + Revision 2.00 release notes: + + - Progressive JPEG is now supported. + + - PPM and PGM binary formats are now supported, thanks to Ken Miller. + + - x86 platforms now make use of SSE2 SIMD instructions for + JPEG decoding, and ARM platforms can use NEON SIMD if requested. + This work was done by Fabian "ryg" Giesen. SSE2 is used by + default, but NEON must be enabled explicitly; see docs. + + With other JPEG optimizations included in this version, we see + 2x speedup on a JPEG on an x86 machine, and a 1.5x speedup + on a JPEG on an ARM machine, relative to previous versions of this + library. The same results will not obtain for all JPGs and for all + x86/ARM machines. (Note that progressive JPEGs are significantly + slower to decode than regular JPEGs.) This doesn't mean that this + is the fastest JPEG decoder in the land; rather, it brings it + closer to parity with standard libraries. If you want the fastest + decode, look elsewhere. (See "Philosophy" section of docs below.) + + See final bullet items below for more info on SIMD. + + - Added STBI_MALLOC, STBI_REALLOC, and STBI_FREE macros for replacing + the memory allocator. Unlike other STBI libraries, these macros don't + support a context parameter, so if you need to pass a context in to + the allocator, you'll have to store it in a global or a thread-local + variable. + + - Split existing STBI_NO_HDR flag into two flags, STBI_NO_HDR and + STBI_NO_LINEAR. + STBI_NO_HDR: suppress implementation of .hdr reader format + STBI_NO_LINEAR: suppress high-dynamic-range light-linear float API + + - You can suppress implementation of any of the decoders to reduce + your code footprint by #defining one or more of the following + symbols before creating the implementation. + + STBI_NO_JPEG + STBI_NO_PNG + STBI_NO_BMP + STBI_NO_PSD + STBI_NO_TGA + STBI_NO_GIF + STBI_NO_HDR + STBI_NO_PIC + STBI_NO_PNM (.ppm and .pgm) + + - You can request *only* certain decoders and suppress all other ones + (this will be more forward-compatible, as addition of new decoders + doesn't require you to disable them explicitly): + + STBI_ONLY_JPEG + STBI_ONLY_PNG + STBI_ONLY_BMP + STBI_ONLY_PSD + STBI_ONLY_TGA + STBI_ONLY_GIF + STBI_ONLY_HDR + STBI_ONLY_PIC + STBI_ONLY_PNM (.ppm and .pgm) + + Note that you can define multiples of these, and you will get all + of them ("only x" and "only y" is interpreted to mean "only x&y"). + + - If you use STBI_NO_PNG (or _ONLY_ without PNG), and you still + want the zlib decoder to be available, #define STBI_SUPPORT_ZLIB + + - Compilation of all SIMD code can be suppressed with + #define STBI_NO_SIMD + It should not be necessary to disable SIMD unless you have issues + compiling (e.g. using an x86 compiler which doesn't support SSE + intrinsics or that doesn't support the method used to detect + SSE2 support at run-time), and even those can be reported as + bugs so I can refine the built-in compile-time checking to be + smarter. + + - The old STBI_SIMD system which allowed installing a user-defined + IDCT etc. has been removed. If you need this, don't upgrade. My + assumption is that almost nobody was doing this, and those who + were will find the built-in SIMD more satisfactory anyway. + + - RGB values computed for JPEG images are slightly different from + previous versions of stb_image. (This is due to using less + integer precision in SIMD.) The C code has been adjusted so + that the same RGB values will be computed regardless of whether + SIMD support is available, so your app should always produce + consistent results. But these results are slightly different from + previous versions. (Specifically, about 3% of available YCbCr values + will compute different RGB results from pre-1.49 versions by +-1; + most of the deviating values are one smaller in the G channel.) + + - If you must produce consistent results with previous versions of + stb_image, #define STBI_JPEG_OLD and you will get the same results + you used to; however, you will not get the SIMD speedups for + the YCbCr-to-RGB conversion step (although you should still see + significant JPEG speedup from the other changes). + + Please note that STBI_JPEG_OLD is a temporary feature; it will be + removed in future versions of the library. It is only intended for + near-term back-compatibility use. + + + Latest revision history: + 2.12 (2016-04-02) fix typo in 2.11 PSD fix that caused crashes + 2.11 (2016-04-02) 16-bit PNGS; enable SSE2 in non-gcc x64 + RGB-format JPEG; remove white matting in PSD; + allocate large structures on the stack; + correct channel count for PNG & BMP + 2.10 (2016-01-22) avoid warning introduced in 2.09 + 2.09 (2016-01-16) 16-bit TGA; comments in PNM files; STBI_REALLOC_SIZED + 2.08 (2015-09-13) fix to 2.07 cleanup, reading RGB PSD as RGBA + 2.07 (2015-09-13) partial animated GIF support + limited 16-bit PSD support + minor bugs, code cleanup, and compiler warnings + 2.06 (2015-04-19) fix bug where PSD returns wrong '*comp' value + 2.05 (2015-04-19) fix bug in progressive JPEG handling, fix warning + 2.04 (2015-04-15) try to re-enable SIMD on MinGW 64-bit + 2.03 (2015-04-12) additional corruption checking + stbi_set_flip_vertically_on_load + fix NEON support; fix mingw support + 2.02 (2015-01-19) fix incorrect assert, fix warning + 2.01 (2015-01-17) fix various warnings + 2.00b (2014-12-25) fix STBI_MALLOC in progressive JPEG + 2.00 (2014-12-25) optimize JPEG, including x86 SSE2 & ARM NEON SIMD + progressive JPEG + PGM/PPM support + STBI_MALLOC,STBI_REALLOC,STBI_FREE + STBI_NO_*, STBI_ONLY_* + GIF bugfix + + See end of file for full revision history. + + + ============================ Contributors ========================= + + Image formats Extensions, features + Sean Barrett (jpeg, png, bmp) Jetro Lauha (stbi_info) + Nicolas Schulz (hdr, psd) Martin "SpartanJ" Golini (stbi_info) + Jonathan Dummer (tga) James "moose2000" Brown (iPhone PNG) + Jean-Marc Lienher (gif) Ben "Disch" Wenger (io callbacks) + Tom Seddon (pic) Omar Cornut (1/2/4-bit PNG) + Thatcher Ulrich (psd) Nicolas Guillemot (vertical flip) + Ken Miller (pgm, ppm) Richard Mitton (16-bit PSD) + urraka@github (animated gif) Junggon Kim (PNM comments) + Daniel Gibson (16-bit TGA) + + Optimizations & bugfixes + Fabian "ryg" Giesen + Arseny Kapoulkine + + Bug & warning fixes + Marc LeBlanc David Woo Guillaume George Martins Mozeiko + Christpher Lloyd Martin Golini Jerry Jansson Joseph Thomson + Dave Moore Roy Eltham Hayaki Saito Phil Jordan + Won Chun Luke Graham Johan Duparc Nathan Reed + the Horde3D community Thomas Ruf Ronny Chevalier Nick Verigakis + Janez Zemva John Bartholomew Michal Cichon svdijk@github + Jonathan Blow Ken Hamada Tero Hanninen Baldur Karlsson + Laurent Gomila Cort Stratton Sergio Gonzalez romigrou@github + Aruelien Pocheville Thibault Reuille Cass Everitt Matthew Gregan + Ryamond Barbiero Paul Du Bois Engin Manap snagar@github + Michaelangel007@github Oriol Ferrer Mesia socks-the-fox + Blazej Dariusz Roszkowski + + +LICENSE + +This software is dual-licensed to the public domain and under the following +license: you are granted a perpetual, irrevocable license to copy, modify, +publish, and distribute this file as you see fit. + +*/ + +#ifndef STBI_INCLUDE_STB_IMAGE_H +#define STBI_INCLUDE_STB_IMAGE_H + +// DOCUMENTATION +// +// Limitations: +// - no 16-bit-per-channel PNG +// - no 12-bit-per-channel JPEG +// - no JPEGs with arithmetic coding +// - no 1-bit BMP +// - GIF always returns *comp=4 +// +// Basic usage (see HDR discussion below for HDR usage): +// int x,y,n; +// unsigned char *data = stbi_load(filename, &x, &y, &n, 0); +// // ... process data if not NULL ... +// // ... x = width, y = height, n = # 8-bit components per pixel ... +// // ... replace '0' with '1'..'4' to force that many components per pixel +// // ... but 'n' will always be the number that it would have been if you said 0 +// stbi_image_free(data) +// +// Standard parameters: +// int *x -- outputs image width in pixels +// int *y -- outputs image height in pixels +// int *comp -- outputs # of image components in image file +// int req_comp -- if non-zero, # of image components requested in result +// +// The return value from an image loader is an 'unsigned char *' which points +// to the pixel data, or NULL on an allocation failure or if the image is +// corrupt or invalid. The pixel data consists of *y scanlines of *x pixels, +// with each pixel consisting of N interleaved 8-bit components; the first +// pixel pointed to is top-left-most in the image. There is no padding between +// image scanlines or between pixels, regardless of format. The number of +// components N is 'req_comp' if req_comp is non-zero, or *comp otherwise. +// If req_comp is non-zero, *comp has the number of components that _would_ +// have been output otherwise. E.g. if you set req_comp to 4, you will always +// get RGBA output, but you can check *comp to see if it's trivially opaque +// because e.g. there were only 3 channels in the source image. +// +// An output image with N components has the following components interleaved +// in this order in each pixel: +// +// N=#comp components +// 1 grey +// 2 grey, alpha +// 3 red, green, blue +// 4 red, green, blue, alpha +// +// If image loading fails for any reason, the return value will be NULL, +// and *x, *y, *comp will be unchanged. The function stbi_failure_reason() +// can be queried for an extremely brief, end-user unfriendly explanation +// of why the load failed. Define STBI_NO_FAILURE_STRINGS to avoid +// compiling these strings at all, and STBI_FAILURE_USERMSG to get slightly +// more user-friendly ones. +// +// Paletted PNG, BMP, GIF, and PIC images are automatically depalettized. +// +// =========================================================================== +// +// Philosophy +// +// stb libraries are designed with the following priorities: +// +// 1. easy to use +// 2. easy to maintain +// 3. good performance +// +// Sometimes I let "good performance" creep up in priority over "easy to maintain", +// and for best performance I may provide less-easy-to-use APIs that give higher +// performance, in addition to the easy to use ones. Nevertheless, it's important +// to keep in mind that from the standpoint of you, a client of this library, +// all you care about is #1 and #3, and stb libraries do not emphasize #3 above all. +// +// Some secondary priorities arise directly from the first two, some of which +// make more explicit reasons why performance can't be emphasized. +// +// - Portable ("ease of use") +// - Small footprint ("easy to maintain") +// - No dependencies ("ease of use") +// +// =========================================================================== +// +// I/O callbacks +// +// I/O callbacks allow you to read from arbitrary sources, like packaged +// files or some other source. Data read from callbacks are processed +// through a small internal buffer (currently 128 bytes) to try to reduce +// overhead. +// +// The three functions you must define are "read" (reads some bytes of data), +// "skip" (skips some bytes of data), "eof" (reports if the stream is at the end). +// +// =========================================================================== +// +// SIMD support +// +// The JPEG decoder will try to automatically use SIMD kernels on x86 when +// supported by the compiler. For ARM Neon support, you must explicitly +// request it. +// +// (The old do-it-yourself SIMD API is no longer supported in the current +// code.) +// +// On x86, SSE2 will automatically be used when available based on a run-time +// test; if not, the generic C versions are used as a fall-back. On ARM targets, +// the typical path is to have separate builds for NEON and non-NEON devices +// (at least this is true for iOS and Android). Therefore, the NEON support is +// toggled by a build flag: define STBI_NEON to get NEON loops. +// +// The output of the JPEG decoder is slightly different from versions where +// SIMD support was introduced (that is, for versions before 1.49). The +// difference is only +-1 in the 8-bit RGB channels, and only on a small +// fraction of pixels. You can force the pre-1.49 behavior by defining +// STBI_JPEG_OLD, but this will disable some of the SIMD decoding path +// and hence cost some performance. +// +// If for some reason you do not want to use any of SIMD code, or if +// you have issues compiling it, you can disable it entirely by +// defining STBI_NO_SIMD. +// +// =========================================================================== +// +// HDR image support (disable by defining STBI_NO_HDR) +// +// stb_image now supports loading HDR images in general, and currently +// the Radiance .HDR file format, although the support is provided +// generically. You can still load any file through the existing interface; +// if you attempt to load an HDR file, it will be automatically remapped to +// LDR, assuming gamma 2.2 and an arbitrary scale factor defaulting to 1; +// both of these constants can be reconfigured through this interface: +// +// stbi_hdr_to_ldr_gamma(2.2f); +// stbi_hdr_to_ldr_scale(1.0f); +// +// (note, do not use _inverse_ constants; stbi_image will invert them +// appropriately). +// +// Additionally, there is a new, parallel interface for loading files as +// (linear) floats to preserve the full dynamic range: +// +// float *data = stbi_loadf(filename, &x, &y, &n, 0); +// +// If you load LDR images through this interface, those images will +// be promoted to floating point values, run through the inverse of +// constants corresponding to the above: +// +// stbi_ldr_to_hdr_scale(1.0f); +// stbi_ldr_to_hdr_gamma(2.2f); +// +// Finally, given a filename (or an open file or memory block--see header +// file for details) containing image data, you can query for the "most +// appropriate" interface to use (that is, whether the image is HDR or +// not), using: +// +// stbi_is_hdr(char *filename); +// +// =========================================================================== +// +// iPhone PNG support: +// +// By default we convert iphone-formatted PNGs back to RGB, even though +// they are internally encoded differently. You can disable this conversion +// by by calling stbi_convert_iphone_png_to_rgb(0), in which case +// you will always just get the native iphone "format" through (which +// is BGR stored in RGB). +// +// Call stbi_set_unpremultiply_on_load(1) as well to force a divide per +// pixel to remove any premultiplied alpha *only* if the image file explicitly +// says there's premultiplied data (currently only happens in iPhone images, +// and only if iPhone convert-to-rgb processing is on). +// + + +#ifndef STBI_NO_STDIO +#include +#endif // STBI_NO_STDIO + +#define STBI_VERSION 1 + +enum +{ + STBI_default = 0, // only used for req_comp + + STBI_grey = 1, + STBI_grey_alpha = 2, + STBI_rgb = 3, + STBI_rgb_alpha = 4 +}; + +typedef unsigned char stbi_uc; + +#ifdef __cplusplus +extern "C" { +#endif + +#ifdef STB_IMAGE_STATIC +#define STBIDEF static +#else +#define STBIDEF extern +#endif + +////////////////////////////////////////////////////////////////////////////// +// +// PRIMARY API - works on images of any type +// + +// +// load image by filename, open file, or memory buffer +// + +typedef struct +{ + int (*read) (void *user,char *data,int size); // fill 'data' with 'size' bytes. return number of bytes actually read + void (*skip) (void *user,int n); // skip the next 'n' bytes, or 'unget' the last -n bytes if negative + int (*eof) (void *user); // returns nonzero if we are at end of file/data +} stbi_io_callbacks; + +STBIDEF stbi_uc *stbi_load (char const *filename, int *x, int *y, int *comp, int req_comp); +STBIDEF stbi_uc *stbi_load_from_memory (stbi_uc const *buffer, int len , int *x, int *y, int *comp, int req_comp); +STBIDEF stbi_uc *stbi_load_from_callbacks(stbi_io_callbacks const *clbk , void *user, int *x, int *y, int *comp, int req_comp); + +#ifndef STBI_NO_STDIO +STBIDEF stbi_uc *stbi_load_from_file (FILE *f, int *x, int *y, int *comp, int req_comp); +// for stbi_load_from_file, file pointer is left pointing immediately after image +#endif + +#ifndef STBI_NO_LINEAR + STBIDEF float *stbi_loadf (char const *filename, int *x, int *y, int *comp, int req_comp); + STBIDEF float *stbi_loadf_from_memory (stbi_uc const *buffer, int len, int *x, int *y, int *comp, int req_comp); + STBIDEF float *stbi_loadf_from_callbacks (stbi_io_callbacks const *clbk, void *user, int *x, int *y, int *comp, int req_comp); + + #ifndef STBI_NO_STDIO + STBIDEF float *stbi_loadf_from_file (FILE *f, int *x, int *y, int *comp, int req_comp); + #endif +#endif + +#ifndef STBI_NO_HDR + STBIDEF void stbi_hdr_to_ldr_gamma(float gamma); + STBIDEF void stbi_hdr_to_ldr_scale(float scale); +#endif // STBI_NO_HDR + +#ifndef STBI_NO_LINEAR + STBIDEF void stbi_ldr_to_hdr_gamma(float gamma); + STBIDEF void stbi_ldr_to_hdr_scale(float scale); +#endif // STBI_NO_LINEAR + +// stbi_is_hdr is always defined, but always returns false if STBI_NO_HDR +STBIDEF int stbi_is_hdr_from_callbacks(stbi_io_callbacks const *clbk, void *user); +STBIDEF int stbi_is_hdr_from_memory(stbi_uc const *buffer, int len); +#ifndef STBI_NO_STDIO +STBIDEF int stbi_is_hdr (char const *filename); +STBIDEF int stbi_is_hdr_from_file(FILE *f); +#endif // STBI_NO_STDIO + + +// get a VERY brief reason for failure +// NOT THREADSAFE +STBIDEF const char *stbi_failure_reason (void); + +// free the loaded image -- this is just free() +STBIDEF void stbi_image_free (void *retval_from_stbi_load); + +// get image dimensions & components without fully decoding +STBIDEF int stbi_info_from_memory(stbi_uc const *buffer, int len, int *x, int *y, int *comp); +STBIDEF int stbi_info_from_callbacks(stbi_io_callbacks const *clbk, void *user, int *x, int *y, int *comp); + +#ifndef STBI_NO_STDIO +STBIDEF int stbi_info (char const *filename, int *x, int *y, int *comp); +STBIDEF int stbi_info_from_file (FILE *f, int *x, int *y, int *comp); + +#endif + + + +// for image formats that explicitly notate that they have premultiplied alpha, +// we just return the colors as stored in the file. set this flag to force +// unpremultiplication. results are undefined if the unpremultiply overflow. +STBIDEF void stbi_set_unpremultiply_on_load(int flag_true_if_should_unpremultiply); + +// indicate whether we should process iphone images back to canonical format, +// or just pass them through "as-is" +STBIDEF void stbi_convert_iphone_png_to_rgb(int flag_true_if_should_convert); + +// flip the image vertically, so the first pixel in the output array is the bottom left +STBIDEF void stbi_set_flip_vertically_on_load(int flag_true_if_should_flip); + +// ZLIB client - used by PNG, available for other purposes + +STBIDEF char *stbi_zlib_decode_malloc_guesssize(const char *buffer, int len, int initial_size, int *outlen); +STBIDEF char *stbi_zlib_decode_malloc_guesssize_headerflag(const char *buffer, int len, int initial_size, int *outlen, int parse_header); +STBIDEF char *stbi_zlib_decode_malloc(const char *buffer, int len, int *outlen); +STBIDEF int stbi_zlib_decode_buffer(char *obuffer, int olen, const char *ibuffer, int ilen); + +STBIDEF char *stbi_zlib_decode_noheader_malloc(const char *buffer, int len, int *outlen); +STBIDEF int stbi_zlib_decode_noheader_buffer(char *obuffer, int olen, const char *ibuffer, int ilen); + + +#ifdef __cplusplus +} +#endif + +// +// +//// end header file ///////////////////////////////////////////////////// +#endif // STBI_INCLUDE_STB_IMAGE_H + +#ifdef STB_IMAGE_IMPLEMENTATION + +#if defined(STBI_ONLY_JPEG) || defined(STBI_ONLY_PNG) || defined(STBI_ONLY_BMP) \ + || defined(STBI_ONLY_TGA) || defined(STBI_ONLY_GIF) || defined(STBI_ONLY_PSD) \ + || defined(STBI_ONLY_HDR) || defined(STBI_ONLY_PIC) || defined(STBI_ONLY_PNM) \ + || defined(STBI_ONLY_ZLIB) + #ifndef STBI_ONLY_JPEG + #define STBI_NO_JPEG + #endif + #ifndef STBI_ONLY_PNG + #define STBI_NO_PNG + #endif + #ifndef STBI_ONLY_BMP + #define STBI_NO_BMP + #endif + #ifndef STBI_ONLY_PSD + #define STBI_NO_PSD + #endif + #ifndef STBI_ONLY_TGA + #define STBI_NO_TGA + #endif + #ifndef STBI_ONLY_GIF + #define STBI_NO_GIF + #endif + #ifndef STBI_ONLY_HDR + #define STBI_NO_HDR + #endif + #ifndef STBI_ONLY_PIC + #define STBI_NO_PIC + #endif + #ifndef STBI_ONLY_PNM + #define STBI_NO_PNM + #endif +#endif + +#if defined(STBI_NO_PNG) && !defined(STBI_SUPPORT_ZLIB) && !defined(STBI_NO_ZLIB) +#define STBI_NO_ZLIB +#endif + + +#include +#include // ptrdiff_t on osx +#include +#include + +#if !defined(STBI_NO_LINEAR) || !defined(STBI_NO_HDR) +#include // ldexp +#endif + +#ifndef STBI_NO_STDIO +#include +#endif + +#ifndef STBI_ASSERT +#include +#define STBI_ASSERT(x) assert(x) +#endif + + +#ifndef _MSC_VER + #ifdef __cplusplus + #define stbi_inline inline + #else + #define stbi_inline + #endif +#else + #define stbi_inline __forceinline +#endif + + +#ifdef _MSC_VER +typedef unsigned short stbi__uint16; +typedef signed short stbi__int16; +typedef unsigned int stbi__uint32; +typedef signed int stbi__int32; +#else +#include +typedef uint16_t stbi__uint16; +typedef int16_t stbi__int16; +typedef uint32_t stbi__uint32; +typedef int32_t stbi__int32; +#endif + +// should produce compiler error if size is wrong +typedef unsigned char validate_uint32[sizeof(stbi__uint32)==4 ? 1 : -1]; + +#ifdef _MSC_VER +#define STBI_NOTUSED(v) (void)(v) +#else +#define STBI_NOTUSED(v) (void)sizeof(v) +#endif + +#ifdef _MSC_VER +#define STBI_HAS_LROTL +#endif + +#ifdef STBI_HAS_LROTL + #define stbi_lrot(x,y) _lrotl(x,y) +#else + #define stbi_lrot(x,y) (((x) << (y)) | ((x) >> (32 - (y)))) +#endif + +#if defined(STBI_MALLOC) && defined(STBI_FREE) && (defined(STBI_REALLOC) || defined(STBI_REALLOC_SIZED)) +// ok +#elif !defined(STBI_MALLOC) && !defined(STBI_FREE) && !defined(STBI_REALLOC) && !defined(STBI_REALLOC_SIZED) +// ok +#else +#error "Must define all or none of STBI_MALLOC, STBI_FREE, and STBI_REALLOC (or STBI_REALLOC_SIZED)." +#endif + +#ifndef STBI_MALLOC +#define STBI_MALLOC(sz) malloc(sz) +#define STBI_REALLOC(p,newsz) realloc(p,newsz) +#define STBI_FREE(p) free(p) +#endif + +#ifndef STBI_REALLOC_SIZED +#define STBI_REALLOC_SIZED(p,oldsz,newsz) STBI_REALLOC(p,newsz) +#endif + +// x86/x64 detection +#if defined(__x86_64__) || defined(_M_X64) +#define STBI__X64_TARGET +#elif defined(__i386) || defined(_M_IX86) +#define STBI__X86_TARGET +#endif + +#if defined(__GNUC__) && (defined(STBI__X86_TARGET) || defined(STBI__X64_TARGET)) && !defined(__SSE2__) && !defined(STBI_NO_SIMD) +// NOTE: not clear do we actually need this for the 64-bit path? +// gcc doesn't support sse2 intrinsics unless you compile with -msse2, +// (but compiling with -msse2 allows the compiler to use SSE2 everywhere; +// this is just broken and gcc are jerks for not fixing it properly +// http://www.virtualdub.org/blog/pivot/entry.php?id=363 ) +#define STBI_NO_SIMD +#endif + +#if defined(__MINGW32__) && defined(STBI__X86_TARGET) && !defined(STBI_MINGW_ENABLE_SSE2) && !defined(STBI_NO_SIMD) +// Note that __MINGW32__ doesn't actually mean 32-bit, so we have to avoid STBI__X64_TARGET +// +// 32-bit MinGW wants ESP to be 16-byte aligned, but this is not in the +// Windows ABI and VC++ as well as Windows DLLs don't maintain that invariant. +// As a result, enabling SSE2 on 32-bit MinGW is dangerous when not +// simultaneously enabling "-mstackrealign". +// +// See https://github.com/nothings/stb/issues/81 for more information. +// +// So default to no SSE2 on 32-bit MinGW. If you've read this far and added +// -mstackrealign to your build settings, feel free to #define STBI_MINGW_ENABLE_SSE2. +#define STBI_NO_SIMD +#endif + +#if !defined(STBI_NO_SIMD) && (defined(STBI__X86_TARGET) || defined(STBI__X64_TARGET)) +#define STBI_SSE2 +#include + +#ifdef _MSC_VER + +#if _MSC_VER >= 1400 // not VC6 +#include // __cpuid +static int stbi__cpuid3(void) +{ + int info[4]; + __cpuid(info,1); + return info[3]; +} +#else +static int stbi__cpuid3(void) +{ + int res; + __asm { + mov eax,1 + cpuid + mov res,edx + } + return res; +} +#endif + +#define STBI_SIMD_ALIGN(type, name) __declspec(align(16)) type name + +static int stbi__sse2_available() +{ + int info3 = stbi__cpuid3(); + return ((info3 >> 26) & 1) != 0; +} +#else // assume GCC-style if not VC++ +#define STBI_SIMD_ALIGN(type, name) type name __attribute__((aligned(16))) + +static int stbi__sse2_available() +{ +#if defined(__GNUC__) && (__GNUC__ * 100 + __GNUC_MINOR__) >= 408 // GCC 4.8 or later + // GCC 4.8+ has a nice way to do this + return __builtin_cpu_supports("sse2"); +#else + // portable way to do this, preferably without using GCC inline ASM? + // just bail for now. + return 0; +#endif +} +#endif +#endif + +// ARM NEON +#if defined(STBI_NO_SIMD) && defined(STBI_NEON) +#undef STBI_NEON +#endif + +#ifdef STBI_NEON +#include +// assume GCC or Clang on ARM targets +#define STBI_SIMD_ALIGN(type, name) type name __attribute__((aligned(16))) +#endif + +#ifndef STBI_SIMD_ALIGN +#define STBI_SIMD_ALIGN(type, name) type name +#endif + +/////////////////////////////////////////////// +// +// stbi__context struct and start_xxx functions + +// stbi__context structure is our basic context used by all images, so it +// contains all the IO context, plus some basic image information +typedef struct +{ + stbi__uint32 img_x, img_y; + int img_n, img_out_n; + + stbi_io_callbacks io; + void *io_user_data; + + int read_from_callbacks; + int buflen; + stbi_uc buffer_start[128]; + + stbi_uc *img_buffer, *img_buffer_end; + stbi_uc *img_buffer_original, *img_buffer_original_end; +} stbi__context; + + +static void stbi__refill_buffer(stbi__context *s); + +// initialize a memory-decode context +static void stbi__start_mem(stbi__context *s, stbi_uc const *buffer, int len) +{ + s->io.read = NULL; + s->read_from_callbacks = 0; + s->img_buffer = s->img_buffer_original = (stbi_uc *) buffer; + s->img_buffer_end = s->img_buffer_original_end = (stbi_uc *) buffer+len; +} + +// initialize a callback-based context +static void stbi__start_callbacks(stbi__context *s, stbi_io_callbacks *c, void *user) +{ + s->io = *c; + s->io_user_data = user; + s->buflen = sizeof(s->buffer_start); + s->read_from_callbacks = 1; + s->img_buffer_original = s->buffer_start; + stbi__refill_buffer(s); + s->img_buffer_original_end = s->img_buffer_end; +} + +#ifndef STBI_NO_STDIO + +static int stbi__stdio_read(void *user, char *data, int size) +{ + return (int) fread(data,1,size,(FILE*) user); +} + +static void stbi__stdio_skip(void *user, int n) +{ + fseek((FILE*) user, n, SEEK_CUR); +} + +static int stbi__stdio_eof(void *user) +{ + return feof((FILE*) user); +} + +static stbi_io_callbacks stbi__stdio_callbacks = +{ + stbi__stdio_read, + stbi__stdio_skip, + stbi__stdio_eof, +}; + +static void stbi__start_file(stbi__context *s, FILE *f) +{ + stbi__start_callbacks(s, &stbi__stdio_callbacks, (void *) f); +} + +//static void stop_file(stbi__context *s) { } + +#endif // !STBI_NO_STDIO + +static void stbi__rewind(stbi__context *s) +{ + // conceptually rewind SHOULD rewind to the beginning of the stream, + // but we just rewind to the beginning of the initial buffer, because + // we only use it after doing 'test', which only ever looks at at most 92 bytes + s->img_buffer = s->img_buffer_original; + s->img_buffer_end = s->img_buffer_original_end; +} + +#ifndef STBI_NO_JPEG +static int stbi__jpeg_test(stbi__context *s); +static stbi_uc *stbi__jpeg_load(stbi__context *s, int *x, int *y, int *comp, int req_comp); +static int stbi__jpeg_info(stbi__context *s, int *x, int *y, int *comp); +#endif + +#ifndef STBI_NO_PNG +static int stbi__png_test(stbi__context *s); +static stbi_uc *stbi__png_load(stbi__context *s, int *x, int *y, int *comp, int req_comp); +static int stbi__png_info(stbi__context *s, int *x, int *y, int *comp); +#endif + +#ifndef STBI_NO_BMP +static int stbi__bmp_test(stbi__context *s); +static stbi_uc *stbi__bmp_load(stbi__context *s, int *x, int *y, int *comp, int req_comp); +static int stbi__bmp_info(stbi__context *s, int *x, int *y, int *comp); +#endif + +#ifndef STBI_NO_TGA +static int stbi__tga_test(stbi__context *s); +static stbi_uc *stbi__tga_load(stbi__context *s, int *x, int *y, int *comp, int req_comp); +static int stbi__tga_info(stbi__context *s, int *x, int *y, int *comp); +#endif + +#ifndef STBI_NO_PSD +static int stbi__psd_test(stbi__context *s); +static stbi_uc *stbi__psd_load(stbi__context *s, int *x, int *y, int *comp, int req_comp); +static int stbi__psd_info(stbi__context *s, int *x, int *y, int *comp); +#endif + +#ifndef STBI_NO_HDR +static int stbi__hdr_test(stbi__context *s); +static float *stbi__hdr_load(stbi__context *s, int *x, int *y, int *comp, int req_comp); +static int stbi__hdr_info(stbi__context *s, int *x, int *y, int *comp); +#endif + +#ifndef STBI_NO_PIC +static int stbi__pic_test(stbi__context *s); +static stbi_uc *stbi__pic_load(stbi__context *s, int *x, int *y, int *comp, int req_comp); +static int stbi__pic_info(stbi__context *s, int *x, int *y, int *comp); +#endif + +#ifndef STBI_NO_GIF +static int stbi__gif_test(stbi__context *s); +static stbi_uc *stbi__gif_load(stbi__context *s, int *x, int *y, int *comp, int req_comp); +static int stbi__gif_info(stbi__context *s, int *x, int *y, int *comp); +#endif + +#ifndef STBI_NO_PNM +static int stbi__pnm_test(stbi__context *s); +static stbi_uc *stbi__pnm_load(stbi__context *s, int *x, int *y, int *comp, int req_comp); +static int stbi__pnm_info(stbi__context *s, int *x, int *y, int *comp); +#endif + +// this is not threadsafe +static const char *stbi__g_failure_reason; + +STBIDEF const char *stbi_failure_reason(void) +{ + return stbi__g_failure_reason; +} + +static int stbi__err(const char *str) +{ + stbi__g_failure_reason = str; + return 0; +} + +static void *stbi__malloc(size_t size) +{ + return STBI_MALLOC(size); +} + +// stbi__err - error +// stbi__errpf - error returning pointer to float +// stbi__errpuc - error returning pointer to unsigned char + +#ifdef STBI_NO_FAILURE_STRINGS + #define stbi__err(x,y) 0 +#elif defined(STBI_FAILURE_USERMSG) + #define stbi__err(x,y) stbi__err(y) +#else + #define stbi__err(x,y) stbi__err(x) +#endif + +#define stbi__errpf(x,y) ((float *)(size_t) (stbi__err(x,y)?NULL:NULL)) +#define stbi__errpuc(x,y) ((unsigned char *)(size_t) (stbi__err(x,y)?NULL:NULL)) + +STBIDEF void stbi_image_free(void *retval_from_stbi_load) +{ + STBI_FREE(retval_from_stbi_load); +} + +#ifndef STBI_NO_LINEAR +static float *stbi__ldr_to_hdr(stbi_uc *data, int x, int y, int comp); +#endif + +#ifndef STBI_NO_HDR +static stbi_uc *stbi__hdr_to_ldr(float *data, int x, int y, int comp); +#endif + +static int stbi__vertically_flip_on_load = 0; + +STBIDEF void stbi_set_flip_vertically_on_load(int flag_true_if_should_flip) +{ + stbi__vertically_flip_on_load = flag_true_if_should_flip; +} + +static unsigned char *stbi__load_main(stbi__context *s, int *x, int *y, int *comp, int req_comp) +{ + #ifndef STBI_NO_JPEG + if (stbi__jpeg_test(s)) return stbi__jpeg_load(s,x,y,comp,req_comp); + #endif + #ifndef STBI_NO_PNG + if (stbi__png_test(s)) return stbi__png_load(s,x,y,comp,req_comp); + #endif + #ifndef STBI_NO_BMP + if (stbi__bmp_test(s)) return stbi__bmp_load(s,x,y,comp,req_comp); + #endif + #ifndef STBI_NO_GIF + if (stbi__gif_test(s)) return stbi__gif_load(s,x,y,comp,req_comp); + #endif + #ifndef STBI_NO_PSD + if (stbi__psd_test(s)) return stbi__psd_load(s,x,y,comp,req_comp); + #endif + #ifndef STBI_NO_PIC + if (stbi__pic_test(s)) return stbi__pic_load(s,x,y,comp,req_comp); + #endif + #ifndef STBI_NO_PNM + if (stbi__pnm_test(s)) return stbi__pnm_load(s,x,y,comp,req_comp); + #endif + + #ifndef STBI_NO_HDR + if (stbi__hdr_test(s)) { + float *hdr = stbi__hdr_load(s, x,y,comp,req_comp); + return stbi__hdr_to_ldr(hdr, *x, *y, req_comp ? req_comp : *comp); + } + #endif + + #ifndef STBI_NO_TGA + // test tga last because it's a crappy test! + if (stbi__tga_test(s)) + return stbi__tga_load(s,x,y,comp,req_comp); + #endif + + return stbi__errpuc("unknown image type", "Image not of any known type, or corrupt"); +} + +static unsigned char *stbi__load_flip(stbi__context *s, int *x, int *y, int *comp, int req_comp) +{ + unsigned char *result = stbi__load_main(s, x, y, comp, req_comp); + + if (stbi__vertically_flip_on_load && result != NULL) { + int w = *x, h = *y; + int depth = req_comp ? req_comp : *comp; + int row,col,z; + stbi_uc temp; + + // @OPTIMIZE: use a bigger temp buffer and memcpy multiple pixels at once + for (row = 0; row < (h>>1); row++) { + for (col = 0; col < w; col++) { + for (z = 0; z < depth; z++) { + temp = result[(row * w + col) * depth + z]; + result[(row * w + col) * depth + z] = result[((h - row - 1) * w + col) * depth + z]; + result[((h - row - 1) * w + col) * depth + z] = temp; + } + } + } + } + + return result; +} + +#ifndef STBI_NO_HDR +static void stbi__float_postprocess(float *result, int *x, int *y, int *comp, int req_comp) +{ + if (stbi__vertically_flip_on_load && result != NULL) { + int w = *x, h = *y; + int depth = req_comp ? req_comp : *comp; + int row,col,z; + float temp; + + // @OPTIMIZE: use a bigger temp buffer and memcpy multiple pixels at once + for (row = 0; row < (h>>1); row++) { + for (col = 0; col < w; col++) { + for (z = 0; z < depth; z++) { + temp = result[(row * w + col) * depth + z]; + result[(row * w + col) * depth + z] = result[((h - row - 1) * w + col) * depth + z]; + result[((h - row - 1) * w + col) * depth + z] = temp; + } + } + } + } +} +#endif + +#ifndef STBI_NO_STDIO + +static FILE *stbi__fopen(char const *filename, char const *mode) +{ + FILE *f; +#if defined(_MSC_VER) && _MSC_VER >= 1400 + if (0 != fopen_s(&f, filename, mode)) + f=0; +#else + f = fopen(filename, mode); +#endif + return f; +} + + +STBIDEF stbi_uc *stbi_load(char const *filename, int *x, int *y, int *comp, int req_comp) +{ + FILE *f = stbi__fopen(filename, "rb"); + unsigned char *result; + if (!f) return stbi__errpuc("can't fopen", "Unable to open file"); + result = stbi_load_from_file(f,x,y,comp,req_comp); + fclose(f); + return result; +} + +STBIDEF stbi_uc *stbi_load_from_file(FILE *f, int *x, int *y, int *comp, int req_comp) +{ + unsigned char *result; + stbi__context s; + stbi__start_file(&s,f); + result = stbi__load_flip(&s,x,y,comp,req_comp); + if (result) { + // need to 'unget' all the characters in the IO buffer + fseek(f, - (int) (s.img_buffer_end - s.img_buffer), SEEK_CUR); + } + return result; +} +#endif //!STBI_NO_STDIO + +STBIDEF stbi_uc *stbi_load_from_memory(stbi_uc const *buffer, int len, int *x, int *y, int *comp, int req_comp) +{ + stbi__context s; + stbi__start_mem(&s,buffer,len); + return stbi__load_flip(&s,x,y,comp,req_comp); +} + +STBIDEF stbi_uc *stbi_load_from_callbacks(stbi_io_callbacks const *clbk, void *user, int *x, int *y, int *comp, int req_comp) +{ + stbi__context s; + stbi__start_callbacks(&s, (stbi_io_callbacks *) clbk, user); + return stbi__load_flip(&s,x,y,comp,req_comp); +} + +#ifndef STBI_NO_LINEAR +static float *stbi__loadf_main(stbi__context *s, int *x, int *y, int *comp, int req_comp) +{ + unsigned char *data; + #ifndef STBI_NO_HDR + if (stbi__hdr_test(s)) { + float *hdr_data = stbi__hdr_load(s,x,y,comp,req_comp); + if (hdr_data) + stbi__float_postprocess(hdr_data,x,y,comp,req_comp); + return hdr_data; + } + #endif + data = stbi__load_flip(s, x, y, comp, req_comp); + if (data) + return stbi__ldr_to_hdr(data, *x, *y, req_comp ? req_comp : *comp); + return stbi__errpf("unknown image type", "Image not of any known type, or corrupt"); +} + +STBIDEF float *stbi_loadf_from_memory(stbi_uc const *buffer, int len, int *x, int *y, int *comp, int req_comp) +{ + stbi__context s; + stbi__start_mem(&s,buffer,len); + return stbi__loadf_main(&s,x,y,comp,req_comp); +} + +STBIDEF float *stbi_loadf_from_callbacks(stbi_io_callbacks const *clbk, void *user, int *x, int *y, int *comp, int req_comp) +{ + stbi__context s; + stbi__start_callbacks(&s, (stbi_io_callbacks *) clbk, user); + return stbi__loadf_main(&s,x,y,comp,req_comp); +} + +#ifndef STBI_NO_STDIO +STBIDEF float *stbi_loadf(char const *filename, int *x, int *y, int *comp, int req_comp) +{ + float *result; + FILE *f = stbi__fopen(filename, "rb"); + if (!f) return stbi__errpf("can't fopen", "Unable to open file"); + result = stbi_loadf_from_file(f,x,y,comp,req_comp); + fclose(f); + return result; +} + +STBIDEF float *stbi_loadf_from_file(FILE *f, int *x, int *y, int *comp, int req_comp) +{ + stbi__context s; + stbi__start_file(&s,f); + return stbi__loadf_main(&s,x,y,comp,req_comp); +} +#endif // !STBI_NO_STDIO + +#endif // !STBI_NO_LINEAR + +// these is-hdr-or-not is defined independent of whether STBI_NO_LINEAR is +// defined, for API simplicity; if STBI_NO_LINEAR is defined, it always +// reports false! + +STBIDEF int stbi_is_hdr_from_memory(stbi_uc const *buffer, int len) +{ + #ifndef STBI_NO_HDR + stbi__context s; + stbi__start_mem(&s,buffer,len); + return stbi__hdr_test(&s); + #else + STBI_NOTUSED(buffer); + STBI_NOTUSED(len); + return 0; + #endif +} + +#ifndef STBI_NO_STDIO +STBIDEF int stbi_is_hdr (char const *filename) +{ + FILE *f = stbi__fopen(filename, "rb"); + int result=0; + if (f) { + result = stbi_is_hdr_from_file(f); + fclose(f); + } + return result; +} + +STBIDEF int stbi_is_hdr_from_file(FILE *f) +{ + #ifndef STBI_NO_HDR + stbi__context s; + stbi__start_file(&s,f); + return stbi__hdr_test(&s); + #else + STBI_NOTUSED(f); + return 0; + #endif +} +#endif // !STBI_NO_STDIO + +STBIDEF int stbi_is_hdr_from_callbacks(stbi_io_callbacks const *clbk, void *user) +{ + #ifndef STBI_NO_HDR + stbi__context s; + stbi__start_callbacks(&s, (stbi_io_callbacks *) clbk, user); + return stbi__hdr_test(&s); + #else + STBI_NOTUSED(clbk); + STBI_NOTUSED(user); + return 0; + #endif +} + +#ifndef STBI_NO_LINEAR +static float stbi__l2h_gamma=2.2f, stbi__l2h_scale=1.0f; + +STBIDEF void stbi_ldr_to_hdr_gamma(float gamma) { stbi__l2h_gamma = gamma; } +STBIDEF void stbi_ldr_to_hdr_scale(float scale) { stbi__l2h_scale = scale; } +#endif + +static float stbi__h2l_gamma_i=1.0f/2.2f, stbi__h2l_scale_i=1.0f; + +STBIDEF void stbi_hdr_to_ldr_gamma(float gamma) { stbi__h2l_gamma_i = 1/gamma; } +STBIDEF void stbi_hdr_to_ldr_scale(float scale) { stbi__h2l_scale_i = 1/scale; } + + +////////////////////////////////////////////////////////////////////////////// +// +// Common code used by all image loaders +// + +enum +{ + STBI__SCAN_load=0, + STBI__SCAN_type, + STBI__SCAN_header +}; + +static void stbi__refill_buffer(stbi__context *s) +{ + int n = (s->io.read)(s->io_user_data,(char*)s->buffer_start,s->buflen); + if (n == 0) { + // at end of file, treat same as if from memory, but need to handle case + // where s->img_buffer isn't pointing to safe memory, e.g. 0-byte file + s->read_from_callbacks = 0; + s->img_buffer = s->buffer_start; + s->img_buffer_end = s->buffer_start+1; + *s->img_buffer = 0; + } else { + s->img_buffer = s->buffer_start; + s->img_buffer_end = s->buffer_start + n; + } +} + +stbi_inline static stbi_uc stbi__get8(stbi__context *s) +{ + if (s->img_buffer < s->img_buffer_end) + return *s->img_buffer++; + if (s->read_from_callbacks) { + stbi__refill_buffer(s); + return *s->img_buffer++; + } + return 0; +} + +stbi_inline static int stbi__at_eof(stbi__context *s) +{ + if (s->io.read) { + if (!(s->io.eof)(s->io_user_data)) return 0; + // if feof() is true, check if buffer = end + // special case: we've only got the special 0 character at the end + if (s->read_from_callbacks == 0) return 1; + } + + return s->img_buffer >= s->img_buffer_end; +} + +static void stbi__skip(stbi__context *s, int n) +{ + if (n < 0) { + s->img_buffer = s->img_buffer_end; + return; + } + if (s->io.read) { + int blen = (int) (s->img_buffer_end - s->img_buffer); + if (blen < n) { + s->img_buffer = s->img_buffer_end; + (s->io.skip)(s->io_user_data, n - blen); + return; + } + } + s->img_buffer += n; +} + +static int stbi__getn(stbi__context *s, stbi_uc *buffer, int n) +{ + if (s->io.read) { + int blen = (int) (s->img_buffer_end - s->img_buffer); + if (blen < n) { + int res, count; + + memcpy(buffer, s->img_buffer, blen); + + count = (s->io.read)(s->io_user_data, (char*) buffer + blen, n - blen); + res = (count == (n-blen)); + s->img_buffer = s->img_buffer_end; + return res; + } + } + + if (s->img_buffer+n <= s->img_buffer_end) { + memcpy(buffer, s->img_buffer, n); + s->img_buffer += n; + return 1; + } else + return 0; +} + +static int stbi__get16be(stbi__context *s) +{ + int z = stbi__get8(s); + return (z << 8) + stbi__get8(s); +} + +static stbi__uint32 stbi__get32be(stbi__context *s) +{ + stbi__uint32 z = stbi__get16be(s); + return (z << 16) + stbi__get16be(s); +} + +#if defined(STBI_NO_BMP) && defined(STBI_NO_TGA) && defined(STBI_NO_GIF) +// nothing +#else +static int stbi__get16le(stbi__context *s) +{ + int z = stbi__get8(s); + return z + (stbi__get8(s) << 8); +} +#endif + +#ifndef STBI_NO_BMP +static stbi__uint32 stbi__get32le(stbi__context *s) +{ + stbi__uint32 z = stbi__get16le(s); + return z + (stbi__get16le(s) << 16); +} +#endif + +#define STBI__BYTECAST(x) ((stbi_uc) ((x) & 255)) // truncate int to byte without warnings + + +////////////////////////////////////////////////////////////////////////////// +// +// generic converter from built-in img_n to req_comp +// individual types do this automatically as much as possible (e.g. jpeg +// does all cases internally since it needs to colorspace convert anyway, +// and it never has alpha, so very few cases ). png can automatically +// interleave an alpha=255 channel, but falls back to this for other cases +// +// assume data buffer is malloced, so malloc a new one and free that one +// only failure mode is malloc failing + +static stbi_uc stbi__compute_y(int r, int g, int b) +{ + return (stbi_uc) (((r*77) + (g*150) + (29*b)) >> 8); +} + +static unsigned char *stbi__convert_format(unsigned char *data, int img_n, int req_comp, unsigned int x, unsigned int y) +{ + int i,j; + unsigned char *good; + + if (req_comp == img_n) return data; + STBI_ASSERT(req_comp >= 1 && req_comp <= 4); + + good = (unsigned char *) stbi__malloc(req_comp * x * y); + if (good == NULL) { + STBI_FREE(data); + return stbi__errpuc("outofmem", "Out of memory"); + } + + for (j=0; j < (int) y; ++j) { + unsigned char *src = data + j * x * img_n ; + unsigned char *dest = good + j * x * req_comp; + + #define COMBO(a,b) ((a)*8+(b)) + #define CASE(a,b) case COMBO(a,b): for(i=x-1; i >= 0; --i, src += a, dest += b) + // convert source image with img_n components to one with req_comp components; + // avoid switch per pixel, so use switch per scanline and massive macros + switch (COMBO(img_n, req_comp)) { + CASE(1,2) dest[0]=src[0], dest[1]=255; break; + CASE(1,3) dest[0]=dest[1]=dest[2]=src[0]; break; + CASE(1,4) dest[0]=dest[1]=dest[2]=src[0], dest[3]=255; break; + CASE(2,1) dest[0]=src[0]; break; + CASE(2,3) dest[0]=dest[1]=dest[2]=src[0]; break; + CASE(2,4) dest[0]=dest[1]=dest[2]=src[0], dest[3]=src[1]; break; + CASE(3,4) dest[0]=src[0],dest[1]=src[1],dest[2]=src[2],dest[3]=255; break; + CASE(3,1) dest[0]=stbi__compute_y(src[0],src[1],src[2]); break; + CASE(3,2) dest[0]=stbi__compute_y(src[0],src[1],src[2]), dest[1] = 255; break; + CASE(4,1) dest[0]=stbi__compute_y(src[0],src[1],src[2]); break; + CASE(4,2) dest[0]=stbi__compute_y(src[0],src[1],src[2]), dest[1] = src[3]; break; + CASE(4,3) dest[0]=src[0],dest[1]=src[1],dest[2]=src[2]; break; + default: STBI_ASSERT(0); + } + #undef CASE + } + + STBI_FREE(data); + return good; +} + +#ifndef STBI_NO_LINEAR +static float *stbi__ldr_to_hdr(stbi_uc *data, int x, int y, int comp) +{ + int i,k,n; + float *output = (float *) stbi__malloc(x * y * comp * sizeof(float)); + if (output == NULL) { STBI_FREE(data); return stbi__errpf("outofmem", "Out of memory"); } + // compute number of non-alpha components + if (comp & 1) n = comp; else n = comp-1; + for (i=0; i < x*y; ++i) { + for (k=0; k < n; ++k) { + output[i*comp + k] = (float) (pow(data[i*comp+k]/255.0f, stbi__l2h_gamma) * stbi__l2h_scale); + } + if (k < comp) output[i*comp + k] = data[i*comp+k]/255.0f; + } + STBI_FREE(data); + return output; +} +#endif + +#ifndef STBI_NO_HDR +#define stbi__float2int(x) ((int) (x)) +static stbi_uc *stbi__hdr_to_ldr(float *data, int x, int y, int comp) +{ + int i,k,n; + stbi_uc *output = (stbi_uc *) stbi__malloc(x * y * comp); + if (output == NULL) { STBI_FREE(data); return stbi__errpuc("outofmem", "Out of memory"); } + // compute number of non-alpha components + if (comp & 1) n = comp; else n = comp-1; + for (i=0; i < x*y; ++i) { + for (k=0; k < n; ++k) { + float z = (float) pow(data[i*comp+k]*stbi__h2l_scale_i, stbi__h2l_gamma_i) * 255 + 0.5f; + if (z < 0) z = 0; + if (z > 255) z = 255; + output[i*comp + k] = (stbi_uc) stbi__float2int(z); + } + if (k < comp) { + float z = data[i*comp+k] * 255 + 0.5f; + if (z < 0) z = 0; + if (z > 255) z = 255; + output[i*comp + k] = (stbi_uc) stbi__float2int(z); + } + } + STBI_FREE(data); + return output; +} +#endif + +////////////////////////////////////////////////////////////////////////////// +// +// "baseline" JPEG/JFIF decoder +// +// simple implementation +// - doesn't support delayed output of y-dimension +// - simple interface (only one output format: 8-bit interleaved RGB) +// - doesn't try to recover corrupt jpegs +// - doesn't allow partial loading, loading multiple at once +// - still fast on x86 (copying globals into locals doesn't help x86) +// - allocates lots of intermediate memory (full size of all components) +// - non-interleaved case requires this anyway +// - allows good upsampling (see next) +// high-quality +// - upsampled channels are bilinearly interpolated, even across blocks +// - quality integer IDCT derived from IJG's 'slow' +// performance +// - fast huffman; reasonable integer IDCT +// - some SIMD kernels for common paths on targets with SSE2/NEON +// - uses a lot of intermediate memory, could cache poorly + +#ifndef STBI_NO_JPEG + +// huffman decoding acceleration +#define FAST_BITS 9 // larger handles more cases; smaller stomps less cache + +typedef struct +{ + stbi_uc fast[1 << FAST_BITS]; + // weirdly, repacking this into AoS is a 10% speed loss, instead of a win + stbi__uint16 code[256]; + stbi_uc values[256]; + stbi_uc size[257]; + unsigned int maxcode[18]; + int delta[17]; // old 'firstsymbol' - old 'firstcode' +} stbi__huffman; + +typedef struct +{ + stbi__context *s; + stbi__huffman huff_dc[4]; + stbi__huffman huff_ac[4]; + stbi_uc dequant[4][64]; + stbi__int16 fast_ac[4][1 << FAST_BITS]; + +// sizes for components, interleaved MCUs + int img_h_max, img_v_max; + int img_mcu_x, img_mcu_y; + int img_mcu_w, img_mcu_h; + +// definition of jpeg image component + struct + { + int id; + int h,v; + int tq; + int hd,ha; + int dc_pred; + + int x,y,w2,h2; + stbi_uc *data; + void *raw_data, *raw_coeff; + stbi_uc *linebuf; + short *coeff; // progressive only + int coeff_w, coeff_h; // number of 8x8 coefficient blocks + } img_comp[4]; + + stbi__uint32 code_buffer; // jpeg entropy-coded buffer + int code_bits; // number of valid bits + unsigned char marker; // marker seen while filling entropy buffer + int nomore; // flag if we saw a marker so must stop + + int progressive; + int spec_start; + int spec_end; + int succ_high; + int succ_low; + int eob_run; + int rgb; + + int scan_n, order[4]; + int restart_interval, todo; + +// kernels + void (*idct_block_kernel)(stbi_uc *out, int out_stride, short data[64]); + void (*YCbCr_to_RGB_kernel)(stbi_uc *out, const stbi_uc *y, const stbi_uc *pcb, const stbi_uc *pcr, int count, int step); + stbi_uc *(*resample_row_hv_2_kernel)(stbi_uc *out, stbi_uc *in_near, stbi_uc *in_far, int w, int hs); +} stbi__jpeg; + +static int stbi__build_huffman(stbi__huffman *h, int *count) +{ + int i,j,k=0,code; + // build size list for each symbol (from JPEG spec) + for (i=0; i < 16; ++i) + for (j=0; j < count[i]; ++j) + h->size[k++] = (stbi_uc) (i+1); + h->size[k] = 0; + + // compute actual symbols (from jpeg spec) + code = 0; + k = 0; + for(j=1; j <= 16; ++j) { + // compute delta to add to code to compute symbol id + h->delta[j] = k - code; + if (h->size[k] == j) { + while (h->size[k] == j) + h->code[k++] = (stbi__uint16) (code++); + if (code-1 >= (1 << j)) return stbi__err("bad code lengths","Corrupt JPEG"); + } + // compute largest code + 1 for this size, preshifted as needed later + h->maxcode[j] = code << (16-j); + code <<= 1; + } + h->maxcode[j] = 0xffffffff; + + // build non-spec acceleration table; 255 is flag for not-accelerated + memset(h->fast, 255, 1 << FAST_BITS); + for (i=0; i < k; ++i) { + int s = h->size[i]; + if (s <= FAST_BITS) { + int c = h->code[i] << (FAST_BITS-s); + int m = 1 << (FAST_BITS-s); + for (j=0; j < m; ++j) { + h->fast[c+j] = (stbi_uc) i; + } + } + } + return 1; +} + +// build a table that decodes both magnitude and value of small ACs in +// one go. +static void stbi__build_fast_ac(stbi__int16 *fast_ac, stbi__huffman *h) +{ + int i; + for (i=0; i < (1 << FAST_BITS); ++i) { + stbi_uc fast = h->fast[i]; + fast_ac[i] = 0; + if (fast < 255) { + int rs = h->values[fast]; + int run = (rs >> 4) & 15; + int magbits = rs & 15; + int len = h->size[fast]; + + if (magbits && len + magbits <= FAST_BITS) { + // magnitude code followed by receive_extend code + int k = ((i << len) & ((1 << FAST_BITS) - 1)) >> (FAST_BITS - magbits); + int m = 1 << (magbits - 1); + if (k < m) k += (-1 << magbits) + 1; + // if the result is small enough, we can fit it in fast_ac table + if (k >= -128 && k <= 127) + fast_ac[i] = (stbi__int16) ((k << 8) + (run << 4) + (len + magbits)); + } + } + } +} + +static void stbi__grow_buffer_unsafe(stbi__jpeg *j) +{ + do { + int b = j->nomore ? 0 : stbi__get8(j->s); + if (b == 0xff) { + int c = stbi__get8(j->s); + if (c != 0) { + j->marker = (unsigned char) c; + j->nomore = 1; + return; + } + } + j->code_buffer |= b << (24 - j->code_bits); + j->code_bits += 8; + } while (j->code_bits <= 24); +} + +// (1 << n) - 1 +static stbi__uint32 stbi__bmask[17]={0,1,3,7,15,31,63,127,255,511,1023,2047,4095,8191,16383,32767,65535}; + +// decode a jpeg huffman value from the bitstream +stbi_inline static int stbi__jpeg_huff_decode(stbi__jpeg *j, stbi__huffman *h) +{ + unsigned int temp; + int c,k; + + if (j->code_bits < 16) stbi__grow_buffer_unsafe(j); + + // look at the top FAST_BITS and determine what symbol ID it is, + // if the code is <= FAST_BITS + c = (j->code_buffer >> (32 - FAST_BITS)) & ((1 << FAST_BITS)-1); + k = h->fast[c]; + if (k < 255) { + int s = h->size[k]; + if (s > j->code_bits) + return -1; + j->code_buffer <<= s; + j->code_bits -= s; + return h->values[k]; + } + + // naive test is to shift the code_buffer down so k bits are + // valid, then test against maxcode. To speed this up, we've + // preshifted maxcode left so that it has (16-k) 0s at the + // end; in other words, regardless of the number of bits, it + // wants to be compared against something shifted to have 16; + // that way we don't need to shift inside the loop. + temp = j->code_buffer >> 16; + for (k=FAST_BITS+1 ; ; ++k) + if (temp < h->maxcode[k]) + break; + if (k == 17) { + // error! code not found + j->code_bits -= 16; + return -1; + } + + if (k > j->code_bits) + return -1; + + // convert the huffman code to the symbol id + c = ((j->code_buffer >> (32 - k)) & stbi__bmask[k]) + h->delta[k]; + STBI_ASSERT((((j->code_buffer) >> (32 - h->size[c])) & stbi__bmask[h->size[c]]) == h->code[c]); + + // convert the id to a symbol + j->code_bits -= k; + j->code_buffer <<= k; + return h->values[c]; +} + +// bias[n] = (-1<code_bits < n) stbi__grow_buffer_unsafe(j); + + sgn = (stbi__int32)j->code_buffer >> 31; // sign bit is always in MSB + k = stbi_lrot(j->code_buffer, n); + STBI_ASSERT(n >= 0 && n < (int) (sizeof(stbi__bmask)/sizeof(*stbi__bmask))); + j->code_buffer = k & ~stbi__bmask[n]; + k &= stbi__bmask[n]; + j->code_bits -= n; + return k + (stbi__jbias[n] & ~sgn); +} + +// get some unsigned bits +stbi_inline static int stbi__jpeg_get_bits(stbi__jpeg *j, int n) +{ + unsigned int k; + if (j->code_bits < n) stbi__grow_buffer_unsafe(j); + k = stbi_lrot(j->code_buffer, n); + j->code_buffer = k & ~stbi__bmask[n]; + k &= stbi__bmask[n]; + j->code_bits -= n; + return k; +} + +stbi_inline static int stbi__jpeg_get_bit(stbi__jpeg *j) +{ + unsigned int k; + if (j->code_bits < 1) stbi__grow_buffer_unsafe(j); + k = j->code_buffer; + j->code_buffer <<= 1; + --j->code_bits; + return k & 0x80000000; +} + +// given a value that's at position X in the zigzag stream, +// where does it appear in the 8x8 matrix coded as row-major? +static stbi_uc stbi__jpeg_dezigzag[64+15] = +{ + 0, 1, 8, 16, 9, 2, 3, 10, + 17, 24, 32, 25, 18, 11, 4, 5, + 12, 19, 26, 33, 40, 48, 41, 34, + 27, 20, 13, 6, 7, 14, 21, 28, + 35, 42, 49, 56, 57, 50, 43, 36, + 29, 22, 15, 23, 30, 37, 44, 51, + 58, 59, 52, 45, 38, 31, 39, 46, + 53, 60, 61, 54, 47, 55, 62, 63, + // let corrupt input sample past end + 63, 63, 63, 63, 63, 63, 63, 63, + 63, 63, 63, 63, 63, 63, 63 +}; + +// decode one 64-entry block-- +static int stbi__jpeg_decode_block(stbi__jpeg *j, short data[64], stbi__huffman *hdc, stbi__huffman *hac, stbi__int16 *fac, int b, stbi_uc *dequant) +{ + int diff,dc,k; + int t; + + if (j->code_bits < 16) stbi__grow_buffer_unsafe(j); + t = stbi__jpeg_huff_decode(j, hdc); + if (t < 0) return stbi__err("bad huffman code","Corrupt JPEG"); + + // 0 all the ac values now so we can do it 32-bits at a time + memset(data,0,64*sizeof(data[0])); + + diff = t ? stbi__extend_receive(j, t) : 0; + dc = j->img_comp[b].dc_pred + diff; + j->img_comp[b].dc_pred = dc; + data[0] = (short) (dc * dequant[0]); + + // decode AC components, see JPEG spec + k = 1; + do { + unsigned int zig; + int c,r,s; + if (j->code_bits < 16) stbi__grow_buffer_unsafe(j); + c = (j->code_buffer >> (32 - FAST_BITS)) & ((1 << FAST_BITS)-1); + r = fac[c]; + if (r) { // fast-AC path + k += (r >> 4) & 15; // run + s = r & 15; // combined length + j->code_buffer <<= s; + j->code_bits -= s; + // decode into unzigzag'd location + zig = stbi__jpeg_dezigzag[k++]; + data[zig] = (short) ((r >> 8) * dequant[zig]); + } else { + int rs = stbi__jpeg_huff_decode(j, hac); + if (rs < 0) return stbi__err("bad huffman code","Corrupt JPEG"); + s = rs & 15; + r = rs >> 4; + if (s == 0) { + if (rs != 0xf0) break; // end block + k += 16; + } else { + k += r; + // decode into unzigzag'd location + zig = stbi__jpeg_dezigzag[k++]; + data[zig] = (short) (stbi__extend_receive(j,s) * dequant[zig]); + } + } + } while (k < 64); + return 1; +} + +static int stbi__jpeg_decode_block_prog_dc(stbi__jpeg *j, short data[64], stbi__huffman *hdc, int b) +{ + int diff,dc; + int t; + if (j->spec_end != 0) return stbi__err("can't merge dc and ac", "Corrupt JPEG"); + + if (j->code_bits < 16) stbi__grow_buffer_unsafe(j); + + if (j->succ_high == 0) { + // first scan for DC coefficient, must be first + memset(data,0,64*sizeof(data[0])); // 0 all the ac values now + t = stbi__jpeg_huff_decode(j, hdc); + diff = t ? stbi__extend_receive(j, t) : 0; + + dc = j->img_comp[b].dc_pred + diff; + j->img_comp[b].dc_pred = dc; + data[0] = (short) (dc << j->succ_low); + } else { + // refinement scan for DC coefficient + if (stbi__jpeg_get_bit(j)) + data[0] += (short) (1 << j->succ_low); + } + return 1; +} + +// @OPTIMIZE: store non-zigzagged during the decode passes, +// and only de-zigzag when dequantizing +static int stbi__jpeg_decode_block_prog_ac(stbi__jpeg *j, short data[64], stbi__huffman *hac, stbi__int16 *fac) +{ + int k; + if (j->spec_start == 0) return stbi__err("can't merge dc and ac", "Corrupt JPEG"); + + if (j->succ_high == 0) { + int shift = j->succ_low; + + if (j->eob_run) { + --j->eob_run; + return 1; + } + + k = j->spec_start; + do { + unsigned int zig; + int c,r,s; + if (j->code_bits < 16) stbi__grow_buffer_unsafe(j); + c = (j->code_buffer >> (32 - FAST_BITS)) & ((1 << FAST_BITS)-1); + r = fac[c]; + if (r) { // fast-AC path + k += (r >> 4) & 15; // run + s = r & 15; // combined length + j->code_buffer <<= s; + j->code_bits -= s; + zig = stbi__jpeg_dezigzag[k++]; + data[zig] = (short) ((r >> 8) << shift); + } else { + int rs = stbi__jpeg_huff_decode(j, hac); + if (rs < 0) return stbi__err("bad huffman code","Corrupt JPEG"); + s = rs & 15; + r = rs >> 4; + if (s == 0) { + if (r < 15) { + j->eob_run = (1 << r); + if (r) + j->eob_run += stbi__jpeg_get_bits(j, r); + --j->eob_run; + break; + } + k += 16; + } else { + k += r; + zig = stbi__jpeg_dezigzag[k++]; + data[zig] = (short) (stbi__extend_receive(j,s) << shift); + } + } + } while (k <= j->spec_end); + } else { + // refinement scan for these AC coefficients + + short bit = (short) (1 << j->succ_low); + + if (j->eob_run) { + --j->eob_run; + for (k = j->spec_start; k <= j->spec_end; ++k) { + short *p = &data[stbi__jpeg_dezigzag[k]]; + if (*p != 0) + if (stbi__jpeg_get_bit(j)) + if ((*p & bit)==0) { + if (*p > 0) + *p += bit; + else + *p -= bit; + } + } + } else { + k = j->spec_start; + do { + int r,s; + int rs = stbi__jpeg_huff_decode(j, hac); // @OPTIMIZE see if we can use the fast path here, advance-by-r is so slow, eh + if (rs < 0) return stbi__err("bad huffman code","Corrupt JPEG"); + s = rs & 15; + r = rs >> 4; + if (s == 0) { + if (r < 15) { + j->eob_run = (1 << r) - 1; + if (r) + j->eob_run += stbi__jpeg_get_bits(j, r); + r = 64; // force end of block + } else { + // r=15 s=0 should write 16 0s, so we just do + // a run of 15 0s and then write s (which is 0), + // so we don't have to do anything special here + } + } else { + if (s != 1) return stbi__err("bad huffman code", "Corrupt JPEG"); + // sign bit + if (stbi__jpeg_get_bit(j)) + s = bit; + else + s = -bit; + } + + // advance by r + while (k <= j->spec_end) { + short *p = &data[stbi__jpeg_dezigzag[k++]]; + if (*p != 0) { + if (stbi__jpeg_get_bit(j)) + if ((*p & bit)==0) { + if (*p > 0) + *p += bit; + else + *p -= bit; + } + } else { + if (r == 0) { + *p = (short) s; + break; + } + --r; + } + } + } while (k <= j->spec_end); + } + } + return 1; +} + +// take a -128..127 value and stbi__clamp it and convert to 0..255 +stbi_inline static stbi_uc stbi__clamp(int x) +{ + // trick to use a single test to catch both cases + if ((unsigned int) x > 255) { + if (x < 0) return 0; + if (x > 255) return 255; + } + return (stbi_uc) x; +} + +#define stbi__f2f(x) ((int) (((x) * 4096 + 0.5))) +#define stbi__fsh(x) ((x) << 12) + +// derived from jidctint -- DCT_ISLOW +#define STBI__IDCT_1D(s0,s1,s2,s3,s4,s5,s6,s7) \ + int t0,t1,t2,t3,p1,p2,p3,p4,p5,x0,x1,x2,x3; \ + p2 = s2; \ + p3 = s6; \ + p1 = (p2+p3) * stbi__f2f(0.5411961f); \ + t2 = p1 + p3*stbi__f2f(-1.847759065f); \ + t3 = p1 + p2*stbi__f2f( 0.765366865f); \ + p2 = s0; \ + p3 = s4; \ + t0 = stbi__fsh(p2+p3); \ + t1 = stbi__fsh(p2-p3); \ + x0 = t0+t3; \ + x3 = t0-t3; \ + x1 = t1+t2; \ + x2 = t1-t2; \ + t0 = s7; \ + t1 = s5; \ + t2 = s3; \ + t3 = s1; \ + p3 = t0+t2; \ + p4 = t1+t3; \ + p1 = t0+t3; \ + p2 = t1+t2; \ + p5 = (p3+p4)*stbi__f2f( 1.175875602f); \ + t0 = t0*stbi__f2f( 0.298631336f); \ + t1 = t1*stbi__f2f( 2.053119869f); \ + t2 = t2*stbi__f2f( 3.072711026f); \ + t3 = t3*stbi__f2f( 1.501321110f); \ + p1 = p5 + p1*stbi__f2f(-0.899976223f); \ + p2 = p5 + p2*stbi__f2f(-2.562915447f); \ + p3 = p3*stbi__f2f(-1.961570560f); \ + p4 = p4*stbi__f2f(-0.390180644f); \ + t3 += p1+p4; \ + t2 += p2+p3; \ + t1 += p2+p4; \ + t0 += p1+p3; + +static void stbi__idct_block(stbi_uc *out, int out_stride, short data[64]) +{ + int i,val[64],*v=val; + stbi_uc *o; + short *d = data; + + // columns + for (i=0; i < 8; ++i,++d, ++v) { + // if all zeroes, shortcut -- this avoids dequantizing 0s and IDCTing + if (d[ 8]==0 && d[16]==0 && d[24]==0 && d[32]==0 + && d[40]==0 && d[48]==0 && d[56]==0) { + // no shortcut 0 seconds + // (1|2|3|4|5|6|7)==0 0 seconds + // all separate -0.047 seconds + // 1 && 2|3 && 4|5 && 6|7: -0.047 seconds + int dcterm = d[0] << 2; + v[0] = v[8] = v[16] = v[24] = v[32] = v[40] = v[48] = v[56] = dcterm; + } else { + STBI__IDCT_1D(d[ 0],d[ 8],d[16],d[24],d[32],d[40],d[48],d[56]) + // constants scaled things up by 1<<12; let's bring them back + // down, but keep 2 extra bits of precision + x0 += 512; x1 += 512; x2 += 512; x3 += 512; + v[ 0] = (x0+t3) >> 10; + v[56] = (x0-t3) >> 10; + v[ 8] = (x1+t2) >> 10; + v[48] = (x1-t2) >> 10; + v[16] = (x2+t1) >> 10; + v[40] = (x2-t1) >> 10; + v[24] = (x3+t0) >> 10; + v[32] = (x3-t0) >> 10; + } + } + + for (i=0, v=val, o=out; i < 8; ++i,v+=8,o+=out_stride) { + // no fast case since the first 1D IDCT spread components out + STBI__IDCT_1D(v[0],v[1],v[2],v[3],v[4],v[5],v[6],v[7]) + // constants scaled things up by 1<<12, plus we had 1<<2 from first + // loop, plus horizontal and vertical each scale by sqrt(8) so together + // we've got an extra 1<<3, so 1<<17 total we need to remove. + // so we want to round that, which means adding 0.5 * 1<<17, + // aka 65536. Also, we'll end up with -128 to 127 that we want + // to encode as 0..255 by adding 128, so we'll add that before the shift + x0 += 65536 + (128<<17); + x1 += 65536 + (128<<17); + x2 += 65536 + (128<<17); + x3 += 65536 + (128<<17); + // tried computing the shifts into temps, or'ing the temps to see + // if any were out of range, but that was slower + o[0] = stbi__clamp((x0+t3) >> 17); + o[7] = stbi__clamp((x0-t3) >> 17); + o[1] = stbi__clamp((x1+t2) >> 17); + o[6] = stbi__clamp((x1-t2) >> 17); + o[2] = stbi__clamp((x2+t1) >> 17); + o[5] = stbi__clamp((x2-t1) >> 17); + o[3] = stbi__clamp((x3+t0) >> 17); + o[4] = stbi__clamp((x3-t0) >> 17); + } +} + +#ifdef STBI_SSE2 +// sse2 integer IDCT. not the fastest possible implementation but it +// produces bit-identical results to the generic C version so it's +// fully "transparent". +static void stbi__idct_simd(stbi_uc *out, int out_stride, short data[64]) +{ + // This is constructed to match our regular (generic) integer IDCT exactly. + __m128i row0, row1, row2, row3, row4, row5, row6, row7; + __m128i tmp; + + // dot product constant: even elems=x, odd elems=y + #define dct_const(x,y) _mm_setr_epi16((x),(y),(x),(y),(x),(y),(x),(y)) + + // out(0) = c0[even]*x + c0[odd]*y (c0, x, y 16-bit, out 32-bit) + // out(1) = c1[even]*x + c1[odd]*y + #define dct_rot(out0,out1, x,y,c0,c1) \ + __m128i c0##lo = _mm_unpacklo_epi16((x),(y)); \ + __m128i c0##hi = _mm_unpackhi_epi16((x),(y)); \ + __m128i out0##_l = _mm_madd_epi16(c0##lo, c0); \ + __m128i out0##_h = _mm_madd_epi16(c0##hi, c0); \ + __m128i out1##_l = _mm_madd_epi16(c0##lo, c1); \ + __m128i out1##_h = _mm_madd_epi16(c0##hi, c1) + + // out = in << 12 (in 16-bit, out 32-bit) + #define dct_widen(out, in) \ + __m128i out##_l = _mm_srai_epi32(_mm_unpacklo_epi16(_mm_setzero_si128(), (in)), 4); \ + __m128i out##_h = _mm_srai_epi32(_mm_unpackhi_epi16(_mm_setzero_si128(), (in)), 4) + + // wide add + #define dct_wadd(out, a, b) \ + __m128i out##_l = _mm_add_epi32(a##_l, b##_l); \ + __m128i out##_h = _mm_add_epi32(a##_h, b##_h) + + // wide sub + #define dct_wsub(out, a, b) \ + __m128i out##_l = _mm_sub_epi32(a##_l, b##_l); \ + __m128i out##_h = _mm_sub_epi32(a##_h, b##_h) + + // butterfly a/b, add bias, then shift by "s" and pack + #define dct_bfly32o(out0, out1, a,b,bias,s) \ + { \ + __m128i abiased_l = _mm_add_epi32(a##_l, bias); \ + __m128i abiased_h = _mm_add_epi32(a##_h, bias); \ + dct_wadd(sum, abiased, b); \ + dct_wsub(dif, abiased, b); \ + out0 = _mm_packs_epi32(_mm_srai_epi32(sum_l, s), _mm_srai_epi32(sum_h, s)); \ + out1 = _mm_packs_epi32(_mm_srai_epi32(dif_l, s), _mm_srai_epi32(dif_h, s)); \ + } + + // 8-bit interleave step (for transposes) + #define dct_interleave8(a, b) \ + tmp = a; \ + a = _mm_unpacklo_epi8(a, b); \ + b = _mm_unpackhi_epi8(tmp, b) + + // 16-bit interleave step (for transposes) + #define dct_interleave16(a, b) \ + tmp = a; \ + a = _mm_unpacklo_epi16(a, b); \ + b = _mm_unpackhi_epi16(tmp, b) + + #define dct_pass(bias,shift) \ + { \ + /* even part */ \ + dct_rot(t2e,t3e, row2,row6, rot0_0,rot0_1); \ + __m128i sum04 = _mm_add_epi16(row0, row4); \ + __m128i dif04 = _mm_sub_epi16(row0, row4); \ + dct_widen(t0e, sum04); \ + dct_widen(t1e, dif04); \ + dct_wadd(x0, t0e, t3e); \ + dct_wsub(x3, t0e, t3e); \ + dct_wadd(x1, t1e, t2e); \ + dct_wsub(x2, t1e, t2e); \ + /* odd part */ \ + dct_rot(y0o,y2o, row7,row3, rot2_0,rot2_1); \ + dct_rot(y1o,y3o, row5,row1, rot3_0,rot3_1); \ + __m128i sum17 = _mm_add_epi16(row1, row7); \ + __m128i sum35 = _mm_add_epi16(row3, row5); \ + dct_rot(y4o,y5o, sum17,sum35, rot1_0,rot1_1); \ + dct_wadd(x4, y0o, y4o); \ + dct_wadd(x5, y1o, y5o); \ + dct_wadd(x6, y2o, y5o); \ + dct_wadd(x7, y3o, y4o); \ + dct_bfly32o(row0,row7, x0,x7,bias,shift); \ + dct_bfly32o(row1,row6, x1,x6,bias,shift); \ + dct_bfly32o(row2,row5, x2,x5,bias,shift); \ + dct_bfly32o(row3,row4, x3,x4,bias,shift); \ + } + + __m128i rot0_0 = dct_const(stbi__f2f(0.5411961f), stbi__f2f(0.5411961f) + stbi__f2f(-1.847759065f)); + __m128i rot0_1 = dct_const(stbi__f2f(0.5411961f) + stbi__f2f( 0.765366865f), stbi__f2f(0.5411961f)); + __m128i rot1_0 = dct_const(stbi__f2f(1.175875602f) + stbi__f2f(-0.899976223f), stbi__f2f(1.175875602f)); + __m128i rot1_1 = dct_const(stbi__f2f(1.175875602f), stbi__f2f(1.175875602f) + stbi__f2f(-2.562915447f)); + __m128i rot2_0 = dct_const(stbi__f2f(-1.961570560f) + stbi__f2f( 0.298631336f), stbi__f2f(-1.961570560f)); + __m128i rot2_1 = dct_const(stbi__f2f(-1.961570560f), stbi__f2f(-1.961570560f) + stbi__f2f( 3.072711026f)); + __m128i rot3_0 = dct_const(stbi__f2f(-0.390180644f) + stbi__f2f( 2.053119869f), stbi__f2f(-0.390180644f)); + __m128i rot3_1 = dct_const(stbi__f2f(-0.390180644f), stbi__f2f(-0.390180644f) + stbi__f2f( 1.501321110f)); + + // rounding biases in column/row passes, see stbi__idct_block for explanation. + __m128i bias_0 = _mm_set1_epi32(512); + __m128i bias_1 = _mm_set1_epi32(65536 + (128<<17)); + + // load + row0 = _mm_load_si128((const __m128i *) (data + 0*8)); + row1 = _mm_load_si128((const __m128i *) (data + 1*8)); + row2 = _mm_load_si128((const __m128i *) (data + 2*8)); + row3 = _mm_load_si128((const __m128i *) (data + 3*8)); + row4 = _mm_load_si128((const __m128i *) (data + 4*8)); + row5 = _mm_load_si128((const __m128i *) (data + 5*8)); + row6 = _mm_load_si128((const __m128i *) (data + 6*8)); + row7 = _mm_load_si128((const __m128i *) (data + 7*8)); + + // column pass + dct_pass(bias_0, 10); + + { + // 16bit 8x8 transpose pass 1 + dct_interleave16(row0, row4); + dct_interleave16(row1, row5); + dct_interleave16(row2, row6); + dct_interleave16(row3, row7); + + // transpose pass 2 + dct_interleave16(row0, row2); + dct_interleave16(row1, row3); + dct_interleave16(row4, row6); + dct_interleave16(row5, row7); + + // transpose pass 3 + dct_interleave16(row0, row1); + dct_interleave16(row2, row3); + dct_interleave16(row4, row5); + dct_interleave16(row6, row7); + } + + // row pass + dct_pass(bias_1, 17); + + { + // pack + __m128i p0 = _mm_packus_epi16(row0, row1); // a0a1a2a3...a7b0b1b2b3...b7 + __m128i p1 = _mm_packus_epi16(row2, row3); + __m128i p2 = _mm_packus_epi16(row4, row5); + __m128i p3 = _mm_packus_epi16(row6, row7); + + // 8bit 8x8 transpose pass 1 + dct_interleave8(p0, p2); // a0e0a1e1... + dct_interleave8(p1, p3); // c0g0c1g1... + + // transpose pass 2 + dct_interleave8(p0, p1); // a0c0e0g0... + dct_interleave8(p2, p3); // b0d0f0h0... + + // transpose pass 3 + dct_interleave8(p0, p2); // a0b0c0d0... + dct_interleave8(p1, p3); // a4b4c4d4... + + // store + _mm_storel_epi64((__m128i *) out, p0); out += out_stride; + _mm_storel_epi64((__m128i *) out, _mm_shuffle_epi32(p0, 0x4e)); out += out_stride; + _mm_storel_epi64((__m128i *) out, p2); out += out_stride; + _mm_storel_epi64((__m128i *) out, _mm_shuffle_epi32(p2, 0x4e)); out += out_stride; + _mm_storel_epi64((__m128i *) out, p1); out += out_stride; + _mm_storel_epi64((__m128i *) out, _mm_shuffle_epi32(p1, 0x4e)); out += out_stride; + _mm_storel_epi64((__m128i *) out, p3); out += out_stride; + _mm_storel_epi64((__m128i *) out, _mm_shuffle_epi32(p3, 0x4e)); + } + +#undef dct_const +#undef dct_rot +#undef dct_widen +#undef dct_wadd +#undef dct_wsub +#undef dct_bfly32o +#undef dct_interleave8 +#undef dct_interleave16 +#undef dct_pass +} + +#endif // STBI_SSE2 + +#ifdef STBI_NEON + +// NEON integer IDCT. should produce bit-identical +// results to the generic C version. +static void stbi__idct_simd(stbi_uc *out, int out_stride, short data[64]) +{ + int16x8_t row0, row1, row2, row3, row4, row5, row6, row7; + + int16x4_t rot0_0 = vdup_n_s16(stbi__f2f(0.5411961f)); + int16x4_t rot0_1 = vdup_n_s16(stbi__f2f(-1.847759065f)); + int16x4_t rot0_2 = vdup_n_s16(stbi__f2f( 0.765366865f)); + int16x4_t rot1_0 = vdup_n_s16(stbi__f2f( 1.175875602f)); + int16x4_t rot1_1 = vdup_n_s16(stbi__f2f(-0.899976223f)); + int16x4_t rot1_2 = vdup_n_s16(stbi__f2f(-2.562915447f)); + int16x4_t rot2_0 = vdup_n_s16(stbi__f2f(-1.961570560f)); + int16x4_t rot2_1 = vdup_n_s16(stbi__f2f(-0.390180644f)); + int16x4_t rot3_0 = vdup_n_s16(stbi__f2f( 0.298631336f)); + int16x4_t rot3_1 = vdup_n_s16(stbi__f2f( 2.053119869f)); + int16x4_t rot3_2 = vdup_n_s16(stbi__f2f( 3.072711026f)); + int16x4_t rot3_3 = vdup_n_s16(stbi__f2f( 1.501321110f)); + +#define dct_long_mul(out, inq, coeff) \ + int32x4_t out##_l = vmull_s16(vget_low_s16(inq), coeff); \ + int32x4_t out##_h = vmull_s16(vget_high_s16(inq), coeff) + +#define dct_long_mac(out, acc, inq, coeff) \ + int32x4_t out##_l = vmlal_s16(acc##_l, vget_low_s16(inq), coeff); \ + int32x4_t out##_h = vmlal_s16(acc##_h, vget_high_s16(inq), coeff) + +#define dct_widen(out, inq) \ + int32x4_t out##_l = vshll_n_s16(vget_low_s16(inq), 12); \ + int32x4_t out##_h = vshll_n_s16(vget_high_s16(inq), 12) + +// wide add +#define dct_wadd(out, a, b) \ + int32x4_t out##_l = vaddq_s32(a##_l, b##_l); \ + int32x4_t out##_h = vaddq_s32(a##_h, b##_h) + +// wide sub +#define dct_wsub(out, a, b) \ + int32x4_t out##_l = vsubq_s32(a##_l, b##_l); \ + int32x4_t out##_h = vsubq_s32(a##_h, b##_h) + +// butterfly a/b, then shift using "shiftop" by "s" and pack +#define dct_bfly32o(out0,out1, a,b,shiftop,s) \ + { \ + dct_wadd(sum, a, b); \ + dct_wsub(dif, a, b); \ + out0 = vcombine_s16(shiftop(sum_l, s), shiftop(sum_h, s)); \ + out1 = vcombine_s16(shiftop(dif_l, s), shiftop(dif_h, s)); \ + } + +#define dct_pass(shiftop, shift) \ + { \ + /* even part */ \ + int16x8_t sum26 = vaddq_s16(row2, row6); \ + dct_long_mul(p1e, sum26, rot0_0); \ + dct_long_mac(t2e, p1e, row6, rot0_1); \ + dct_long_mac(t3e, p1e, row2, rot0_2); \ + int16x8_t sum04 = vaddq_s16(row0, row4); \ + int16x8_t dif04 = vsubq_s16(row0, row4); \ + dct_widen(t0e, sum04); \ + dct_widen(t1e, dif04); \ + dct_wadd(x0, t0e, t3e); \ + dct_wsub(x3, t0e, t3e); \ + dct_wadd(x1, t1e, t2e); \ + dct_wsub(x2, t1e, t2e); \ + /* odd part */ \ + int16x8_t sum15 = vaddq_s16(row1, row5); \ + int16x8_t sum17 = vaddq_s16(row1, row7); \ + int16x8_t sum35 = vaddq_s16(row3, row5); \ + int16x8_t sum37 = vaddq_s16(row3, row7); \ + int16x8_t sumodd = vaddq_s16(sum17, sum35); \ + dct_long_mul(p5o, sumodd, rot1_0); \ + dct_long_mac(p1o, p5o, sum17, rot1_1); \ + dct_long_mac(p2o, p5o, sum35, rot1_2); \ + dct_long_mul(p3o, sum37, rot2_0); \ + dct_long_mul(p4o, sum15, rot2_1); \ + dct_wadd(sump13o, p1o, p3o); \ + dct_wadd(sump24o, p2o, p4o); \ + dct_wadd(sump23o, p2o, p3o); \ + dct_wadd(sump14o, p1o, p4o); \ + dct_long_mac(x4, sump13o, row7, rot3_0); \ + dct_long_mac(x5, sump24o, row5, rot3_1); \ + dct_long_mac(x6, sump23o, row3, rot3_2); \ + dct_long_mac(x7, sump14o, row1, rot3_3); \ + dct_bfly32o(row0,row7, x0,x7,shiftop,shift); \ + dct_bfly32o(row1,row6, x1,x6,shiftop,shift); \ + dct_bfly32o(row2,row5, x2,x5,shiftop,shift); \ + dct_bfly32o(row3,row4, x3,x4,shiftop,shift); \ + } + + // load + row0 = vld1q_s16(data + 0*8); + row1 = vld1q_s16(data + 1*8); + row2 = vld1q_s16(data + 2*8); + row3 = vld1q_s16(data + 3*8); + row4 = vld1q_s16(data + 4*8); + row5 = vld1q_s16(data + 5*8); + row6 = vld1q_s16(data + 6*8); + row7 = vld1q_s16(data + 7*8); + + // add DC bias + row0 = vaddq_s16(row0, vsetq_lane_s16(1024, vdupq_n_s16(0), 0)); + + // column pass + dct_pass(vrshrn_n_s32, 10); + + // 16bit 8x8 transpose + { +// these three map to a single VTRN.16, VTRN.32, and VSWP, respectively. +// whether compilers actually get this is another story, sadly. +#define dct_trn16(x, y) { int16x8x2_t t = vtrnq_s16(x, y); x = t.val[0]; y = t.val[1]; } +#define dct_trn32(x, y) { int32x4x2_t t = vtrnq_s32(vreinterpretq_s32_s16(x), vreinterpretq_s32_s16(y)); x = vreinterpretq_s16_s32(t.val[0]); y = vreinterpretq_s16_s32(t.val[1]); } +#define dct_trn64(x, y) { int16x8_t x0 = x; int16x8_t y0 = y; x = vcombine_s16(vget_low_s16(x0), vget_low_s16(y0)); y = vcombine_s16(vget_high_s16(x0), vget_high_s16(y0)); } + + // pass 1 + dct_trn16(row0, row1); // a0b0a2b2a4b4a6b6 + dct_trn16(row2, row3); + dct_trn16(row4, row5); + dct_trn16(row6, row7); + + // pass 2 + dct_trn32(row0, row2); // a0b0c0d0a4b4c4d4 + dct_trn32(row1, row3); + dct_trn32(row4, row6); + dct_trn32(row5, row7); + + // pass 3 + dct_trn64(row0, row4); // a0b0c0d0e0f0g0h0 + dct_trn64(row1, row5); + dct_trn64(row2, row6); + dct_trn64(row3, row7); + +#undef dct_trn16 +#undef dct_trn32 +#undef dct_trn64 + } + + // row pass + // vrshrn_n_s32 only supports shifts up to 16, we need + // 17. so do a non-rounding shift of 16 first then follow + // up with a rounding shift by 1. + dct_pass(vshrn_n_s32, 16); + + { + // pack and round + uint8x8_t p0 = vqrshrun_n_s16(row0, 1); + uint8x8_t p1 = vqrshrun_n_s16(row1, 1); + uint8x8_t p2 = vqrshrun_n_s16(row2, 1); + uint8x8_t p3 = vqrshrun_n_s16(row3, 1); + uint8x8_t p4 = vqrshrun_n_s16(row4, 1); + uint8x8_t p5 = vqrshrun_n_s16(row5, 1); + uint8x8_t p6 = vqrshrun_n_s16(row6, 1); + uint8x8_t p7 = vqrshrun_n_s16(row7, 1); + + // again, these can translate into one instruction, but often don't. +#define dct_trn8_8(x, y) { uint8x8x2_t t = vtrn_u8(x, y); x = t.val[0]; y = t.val[1]; } +#define dct_trn8_16(x, y) { uint16x4x2_t t = vtrn_u16(vreinterpret_u16_u8(x), vreinterpret_u16_u8(y)); x = vreinterpret_u8_u16(t.val[0]); y = vreinterpret_u8_u16(t.val[1]); } +#define dct_trn8_32(x, y) { uint32x2x2_t t = vtrn_u32(vreinterpret_u32_u8(x), vreinterpret_u32_u8(y)); x = vreinterpret_u8_u32(t.val[0]); y = vreinterpret_u8_u32(t.val[1]); } + + // sadly can't use interleaved stores here since we only write + // 8 bytes to each scan line! + + // 8x8 8-bit transpose pass 1 + dct_trn8_8(p0, p1); + dct_trn8_8(p2, p3); + dct_trn8_8(p4, p5); + dct_trn8_8(p6, p7); + + // pass 2 + dct_trn8_16(p0, p2); + dct_trn8_16(p1, p3); + dct_trn8_16(p4, p6); + dct_trn8_16(p5, p7); + + // pass 3 + dct_trn8_32(p0, p4); + dct_trn8_32(p1, p5); + dct_trn8_32(p2, p6); + dct_trn8_32(p3, p7); + + // store + vst1_u8(out, p0); out += out_stride; + vst1_u8(out, p1); out += out_stride; + vst1_u8(out, p2); out += out_stride; + vst1_u8(out, p3); out += out_stride; + vst1_u8(out, p4); out += out_stride; + vst1_u8(out, p5); out += out_stride; + vst1_u8(out, p6); out += out_stride; + vst1_u8(out, p7); + +#undef dct_trn8_8 +#undef dct_trn8_16 +#undef dct_trn8_32 + } + +#undef dct_long_mul +#undef dct_long_mac +#undef dct_widen +#undef dct_wadd +#undef dct_wsub +#undef dct_bfly32o +#undef dct_pass +} + +#endif // STBI_NEON + +#define STBI__MARKER_none 0xff +// if there's a pending marker from the entropy stream, return that +// otherwise, fetch from the stream and get a marker. if there's no +// marker, return 0xff, which is never a valid marker value +static stbi_uc stbi__get_marker(stbi__jpeg *j) +{ + stbi_uc x; + if (j->marker != STBI__MARKER_none) { x = j->marker; j->marker = STBI__MARKER_none; return x; } + x = stbi__get8(j->s); + if (x != 0xff) return STBI__MARKER_none; + while (x == 0xff) + x = stbi__get8(j->s); + return x; +} + +// in each scan, we'll have scan_n components, and the order +// of the components is specified by order[] +#define STBI__RESTART(x) ((x) >= 0xd0 && (x) <= 0xd7) + +// after a restart interval, stbi__jpeg_reset the entropy decoder and +// the dc prediction +static void stbi__jpeg_reset(stbi__jpeg *j) +{ + j->code_bits = 0; + j->code_buffer = 0; + j->nomore = 0; + j->img_comp[0].dc_pred = j->img_comp[1].dc_pred = j->img_comp[2].dc_pred = 0; + j->marker = STBI__MARKER_none; + j->todo = j->restart_interval ? j->restart_interval : 0x7fffffff; + j->eob_run = 0; + // no more than 1<<31 MCUs if no restart_interal? that's plenty safe, + // since we don't even allow 1<<30 pixels +} + +static int stbi__parse_entropy_coded_data(stbi__jpeg *z) +{ + stbi__jpeg_reset(z); + if (!z->progressive) { + if (z->scan_n == 1) { + int i,j; + STBI_SIMD_ALIGN(short, data[64]); + int n = z->order[0]; + // non-interleaved data, we just need to process one block at a time, + // in trivial scanline order + // number of blocks to do just depends on how many actual "pixels" this + // component has, independent of interleaved MCU blocking and such + int w = (z->img_comp[n].x+7) >> 3; + int h = (z->img_comp[n].y+7) >> 3; + for (j=0; j < h; ++j) { + for (i=0; i < w; ++i) { + int ha = z->img_comp[n].ha; + if (!stbi__jpeg_decode_block(z, data, z->huff_dc+z->img_comp[n].hd, z->huff_ac+ha, z->fast_ac[ha], n, z->dequant[z->img_comp[n].tq])) return 0; + z->idct_block_kernel(z->img_comp[n].data+z->img_comp[n].w2*j*8+i*8, z->img_comp[n].w2, data); + // every data block is an MCU, so countdown the restart interval + if (--z->todo <= 0) { + if (z->code_bits < 24) stbi__grow_buffer_unsafe(z); + // if it's NOT a restart, then just bail, so we get corrupt data + // rather than no data + if (!STBI__RESTART(z->marker)) return 1; + stbi__jpeg_reset(z); + } + } + } + return 1; + } else { // interleaved + int i,j,k,x,y; + STBI_SIMD_ALIGN(short, data[64]); + for (j=0; j < z->img_mcu_y; ++j) { + for (i=0; i < z->img_mcu_x; ++i) { + // scan an interleaved mcu... process scan_n components in order + for (k=0; k < z->scan_n; ++k) { + int n = z->order[k]; + // scan out an mcu's worth of this component; that's just determined + // by the basic H and V specified for the component + for (y=0; y < z->img_comp[n].v; ++y) { + for (x=0; x < z->img_comp[n].h; ++x) { + int x2 = (i*z->img_comp[n].h + x)*8; + int y2 = (j*z->img_comp[n].v + y)*8; + int ha = z->img_comp[n].ha; + if (!stbi__jpeg_decode_block(z, data, z->huff_dc+z->img_comp[n].hd, z->huff_ac+ha, z->fast_ac[ha], n, z->dequant[z->img_comp[n].tq])) return 0; + z->idct_block_kernel(z->img_comp[n].data+z->img_comp[n].w2*y2+x2, z->img_comp[n].w2, data); + } + } + } + // after all interleaved components, that's an interleaved MCU, + // so now count down the restart interval + if (--z->todo <= 0) { + if (z->code_bits < 24) stbi__grow_buffer_unsafe(z); + if (!STBI__RESTART(z->marker)) return 1; + stbi__jpeg_reset(z); + } + } + } + return 1; + } + } else { + if (z->scan_n == 1) { + int i,j; + int n = z->order[0]; + // non-interleaved data, we just need to process one block at a time, + // in trivial scanline order + // number of blocks to do just depends on how many actual "pixels" this + // component has, independent of interleaved MCU blocking and such + int w = (z->img_comp[n].x+7) >> 3; + int h = (z->img_comp[n].y+7) >> 3; + for (j=0; j < h; ++j) { + for (i=0; i < w; ++i) { + short *data = z->img_comp[n].coeff + 64 * (i + j * z->img_comp[n].coeff_w); + if (z->spec_start == 0) { + if (!stbi__jpeg_decode_block_prog_dc(z, data, &z->huff_dc[z->img_comp[n].hd], n)) + return 0; + } else { + int ha = z->img_comp[n].ha; + if (!stbi__jpeg_decode_block_prog_ac(z, data, &z->huff_ac[ha], z->fast_ac[ha])) + return 0; + } + // every data block is an MCU, so countdown the restart interval + if (--z->todo <= 0) { + if (z->code_bits < 24) stbi__grow_buffer_unsafe(z); + if (!STBI__RESTART(z->marker)) return 1; + stbi__jpeg_reset(z); + } + } + } + return 1; + } else { // interleaved + int i,j,k,x,y; + for (j=0; j < z->img_mcu_y; ++j) { + for (i=0; i < z->img_mcu_x; ++i) { + // scan an interleaved mcu... process scan_n components in order + for (k=0; k < z->scan_n; ++k) { + int n = z->order[k]; + // scan out an mcu's worth of this component; that's just determined + // by the basic H and V specified for the component + for (y=0; y < z->img_comp[n].v; ++y) { + for (x=0; x < z->img_comp[n].h; ++x) { + int x2 = (i*z->img_comp[n].h + x); + int y2 = (j*z->img_comp[n].v + y); + short *data = z->img_comp[n].coeff + 64 * (x2 + y2 * z->img_comp[n].coeff_w); + if (!stbi__jpeg_decode_block_prog_dc(z, data, &z->huff_dc[z->img_comp[n].hd], n)) + return 0; + } + } + } + // after all interleaved components, that's an interleaved MCU, + // so now count down the restart interval + if (--z->todo <= 0) { + if (z->code_bits < 24) stbi__grow_buffer_unsafe(z); + if (!STBI__RESTART(z->marker)) return 1; + stbi__jpeg_reset(z); + } + } + } + return 1; + } + } +} + +static void stbi__jpeg_dequantize(short *data, stbi_uc *dequant) +{ + int i; + for (i=0; i < 64; ++i) + data[i] *= dequant[i]; +} + +static void stbi__jpeg_finish(stbi__jpeg *z) +{ + if (z->progressive) { + // dequantize and idct the data + int i,j,n; + for (n=0; n < z->s->img_n; ++n) { + int w = (z->img_comp[n].x+7) >> 3; + int h = (z->img_comp[n].y+7) >> 3; + for (j=0; j < h; ++j) { + for (i=0; i < w; ++i) { + short *data = z->img_comp[n].coeff + 64 * (i + j * z->img_comp[n].coeff_w); + stbi__jpeg_dequantize(data, z->dequant[z->img_comp[n].tq]); + z->idct_block_kernel(z->img_comp[n].data+z->img_comp[n].w2*j*8+i*8, z->img_comp[n].w2, data); + } + } + } + } +} + +static int stbi__process_marker(stbi__jpeg *z, int m) +{ + int L; + switch (m) { + case STBI__MARKER_none: // no marker found + return stbi__err("expected marker","Corrupt JPEG"); + + case 0xDD: // DRI - specify restart interval + if (stbi__get16be(z->s) != 4) return stbi__err("bad DRI len","Corrupt JPEG"); + z->restart_interval = stbi__get16be(z->s); + return 1; + + case 0xDB: // DQT - define quantization table + L = stbi__get16be(z->s)-2; + while (L > 0) { + int q = stbi__get8(z->s); + int p = q >> 4; + int t = q & 15,i; + if (p != 0) return stbi__err("bad DQT type","Corrupt JPEG"); + if (t > 3) return stbi__err("bad DQT table","Corrupt JPEG"); + for (i=0; i < 64; ++i) + z->dequant[t][stbi__jpeg_dezigzag[i]] = stbi__get8(z->s); + L -= 65; + } + return L==0; + + case 0xC4: // DHT - define huffman table + L = stbi__get16be(z->s)-2; + while (L > 0) { + stbi_uc *v; + int sizes[16],i,n=0; + int q = stbi__get8(z->s); + int tc = q >> 4; + int th = q & 15; + if (tc > 1 || th > 3) return stbi__err("bad DHT header","Corrupt JPEG"); + for (i=0; i < 16; ++i) { + sizes[i] = stbi__get8(z->s); + n += sizes[i]; + } + L -= 17; + if (tc == 0) { + if (!stbi__build_huffman(z->huff_dc+th, sizes)) return 0; + v = z->huff_dc[th].values; + } else { + if (!stbi__build_huffman(z->huff_ac+th, sizes)) return 0; + v = z->huff_ac[th].values; + } + for (i=0; i < n; ++i) + v[i] = stbi__get8(z->s); + if (tc != 0) + stbi__build_fast_ac(z->fast_ac[th], z->huff_ac + th); + L -= n; + } + return L==0; + } + // check for comment block or APP blocks + if ((m >= 0xE0 && m <= 0xEF) || m == 0xFE) { + stbi__skip(z->s, stbi__get16be(z->s)-2); + return 1; + } + return 0; +} + +// after we see SOS +static int stbi__process_scan_header(stbi__jpeg *z) +{ + int i; + int Ls = stbi__get16be(z->s); + z->scan_n = stbi__get8(z->s); + if (z->scan_n < 1 || z->scan_n > 4 || z->scan_n > (int) z->s->img_n) return stbi__err("bad SOS component count","Corrupt JPEG"); + if (Ls != 6+2*z->scan_n) return stbi__err("bad SOS len","Corrupt JPEG"); + for (i=0; i < z->scan_n; ++i) { + int id = stbi__get8(z->s), which; + int q = stbi__get8(z->s); + for (which = 0; which < z->s->img_n; ++which) + if (z->img_comp[which].id == id) + break; + if (which == z->s->img_n) return 0; // no match + z->img_comp[which].hd = q >> 4; if (z->img_comp[which].hd > 3) return stbi__err("bad DC huff","Corrupt JPEG"); + z->img_comp[which].ha = q & 15; if (z->img_comp[which].ha > 3) return stbi__err("bad AC huff","Corrupt JPEG"); + z->order[i] = which; + } + + { + int aa; + z->spec_start = stbi__get8(z->s); + z->spec_end = stbi__get8(z->s); // should be 63, but might be 0 + aa = stbi__get8(z->s); + z->succ_high = (aa >> 4); + z->succ_low = (aa & 15); + if (z->progressive) { + if (z->spec_start > 63 || z->spec_end > 63 || z->spec_start > z->spec_end || z->succ_high > 13 || z->succ_low > 13) + return stbi__err("bad SOS", "Corrupt JPEG"); + } else { + if (z->spec_start != 0) return stbi__err("bad SOS","Corrupt JPEG"); + if (z->succ_high != 0 || z->succ_low != 0) return stbi__err("bad SOS","Corrupt JPEG"); + z->spec_end = 63; + } + } + + return 1; +} + +static int stbi__process_frame_header(stbi__jpeg *z, int scan) +{ + stbi__context *s = z->s; + int Lf,p,i,q, h_max=1,v_max=1,c; + Lf = stbi__get16be(s); if (Lf < 11) return stbi__err("bad SOF len","Corrupt JPEG"); // JPEG + p = stbi__get8(s); if (p != 8) return stbi__err("only 8-bit","JPEG format not supported: 8-bit only"); // JPEG baseline + s->img_y = stbi__get16be(s); if (s->img_y == 0) return stbi__err("no header height", "JPEG format not supported: delayed height"); // Legal, but we don't handle it--but neither does IJG + s->img_x = stbi__get16be(s); if (s->img_x == 0) return stbi__err("0 width","Corrupt JPEG"); // JPEG requires + c = stbi__get8(s); + if (c != 3 && c != 1) return stbi__err("bad component count","Corrupt JPEG"); // JFIF requires + s->img_n = c; + for (i=0; i < c; ++i) { + z->img_comp[i].data = NULL; + z->img_comp[i].linebuf = NULL; + } + + if (Lf != 8+3*s->img_n) return stbi__err("bad SOF len","Corrupt JPEG"); + + z->rgb = 0; + for (i=0; i < s->img_n; ++i) { + static unsigned char rgb[3] = { 'R', 'G', 'B' }; + z->img_comp[i].id = stbi__get8(s); + if (z->img_comp[i].id != i+1) // JFIF requires + if (z->img_comp[i].id != i) { // some version of jpegtran outputs non-JFIF-compliant files! + // somethings output this (see http://fileformats.archiveteam.org/wiki/JPEG#Color_format) + if (z->img_comp[i].id != rgb[i]) + return stbi__err("bad component ID","Corrupt JPEG"); + ++z->rgb; + } + q = stbi__get8(s); + z->img_comp[i].h = (q >> 4); if (!z->img_comp[i].h || z->img_comp[i].h > 4) return stbi__err("bad H","Corrupt JPEG"); + z->img_comp[i].v = q & 15; if (!z->img_comp[i].v || z->img_comp[i].v > 4) return stbi__err("bad V","Corrupt JPEG"); + z->img_comp[i].tq = stbi__get8(s); if (z->img_comp[i].tq > 3) return stbi__err("bad TQ","Corrupt JPEG"); + } + + if (scan != STBI__SCAN_load) return 1; + + if ((1 << 30) / s->img_x / s->img_n < s->img_y) return stbi__err("too large", "Image too large to decode"); + + for (i=0; i < s->img_n; ++i) { + if (z->img_comp[i].h > h_max) h_max = z->img_comp[i].h; + if (z->img_comp[i].v > v_max) v_max = z->img_comp[i].v; + } + + // compute interleaved mcu info + z->img_h_max = h_max; + z->img_v_max = v_max; + z->img_mcu_w = h_max * 8; + z->img_mcu_h = v_max * 8; + z->img_mcu_x = (s->img_x + z->img_mcu_w-1) / z->img_mcu_w; + z->img_mcu_y = (s->img_y + z->img_mcu_h-1) / z->img_mcu_h; + + for (i=0; i < s->img_n; ++i) { + // number of effective pixels (e.g. for non-interleaved MCU) + z->img_comp[i].x = (s->img_x * z->img_comp[i].h + h_max-1) / h_max; + z->img_comp[i].y = (s->img_y * z->img_comp[i].v + v_max-1) / v_max; + // to simplify generation, we'll allocate enough memory to decode + // the bogus oversized data from using interleaved MCUs and their + // big blocks (e.g. a 16x16 iMCU on an image of width 33); we won't + // discard the extra data until colorspace conversion + z->img_comp[i].w2 = z->img_mcu_x * z->img_comp[i].h * 8; + z->img_comp[i].h2 = z->img_mcu_y * z->img_comp[i].v * 8; + z->img_comp[i].raw_data = stbi__malloc(z->img_comp[i].w2 * z->img_comp[i].h2+15); + + if (z->img_comp[i].raw_data == NULL) { + for(--i; i >= 0; --i) { + STBI_FREE(z->img_comp[i].raw_data); + z->img_comp[i].raw_data = NULL; + } + return stbi__err("outofmem", "Out of memory"); + } + // align blocks for idct using mmx/sse + z->img_comp[i].data = (stbi_uc*) (((size_t) z->img_comp[i].raw_data + 15) & ~15); + z->img_comp[i].linebuf = NULL; + if (z->progressive) { + z->img_comp[i].coeff_w = (z->img_comp[i].w2 + 7) >> 3; + z->img_comp[i].coeff_h = (z->img_comp[i].h2 + 7) >> 3; + z->img_comp[i].raw_coeff = STBI_MALLOC(z->img_comp[i].coeff_w * z->img_comp[i].coeff_h * 64 * sizeof(short) + 15); + z->img_comp[i].coeff = (short*) (((size_t) z->img_comp[i].raw_coeff + 15) & ~15); + } else { + z->img_comp[i].coeff = 0; + z->img_comp[i].raw_coeff = 0; + } + } + + return 1; +} + +// use comparisons since in some cases we handle more than one case (e.g. SOF) +#define stbi__DNL(x) ((x) == 0xdc) +#define stbi__SOI(x) ((x) == 0xd8) +#define stbi__EOI(x) ((x) == 0xd9) +#define stbi__SOF(x) ((x) == 0xc0 || (x) == 0xc1 || (x) == 0xc2) +#define stbi__SOS(x) ((x) == 0xda) + +#define stbi__SOF_progressive(x) ((x) == 0xc2) + +static int stbi__decode_jpeg_header(stbi__jpeg *z, int scan) +{ + int m; + z->marker = STBI__MARKER_none; // initialize cached marker to empty + m = stbi__get_marker(z); + if (!stbi__SOI(m)) return stbi__err("no SOI","Corrupt JPEG"); + if (scan == STBI__SCAN_type) return 1; + m = stbi__get_marker(z); + while (!stbi__SOF(m)) { + if (!stbi__process_marker(z,m)) return 0; + m = stbi__get_marker(z); + while (m == STBI__MARKER_none) { + // some files have extra padding after their blocks, so ok, we'll scan + if (stbi__at_eof(z->s)) return stbi__err("no SOF", "Corrupt JPEG"); + m = stbi__get_marker(z); + } + } + z->progressive = stbi__SOF_progressive(m); + if (!stbi__process_frame_header(z, scan)) return 0; + return 1; +} + +// decode image to YCbCr format +static int stbi__decode_jpeg_image(stbi__jpeg *j) +{ + int m; + for (m = 0; m < 4; m++) { + j->img_comp[m].raw_data = NULL; + j->img_comp[m].raw_coeff = NULL; + } + j->restart_interval = 0; + if (!stbi__decode_jpeg_header(j, STBI__SCAN_load)) return 0; + m = stbi__get_marker(j); + while (!stbi__EOI(m)) { + if (stbi__SOS(m)) { + if (!stbi__process_scan_header(j)) return 0; + if (!stbi__parse_entropy_coded_data(j)) return 0; + if (j->marker == STBI__MARKER_none ) { + // handle 0s at the end of image data from IP Kamera 9060 + while (!stbi__at_eof(j->s)) { + int x = stbi__get8(j->s); + if (x == 255) { + j->marker = stbi__get8(j->s); + break; + } else if (x != 0) { + return stbi__err("junk before marker", "Corrupt JPEG"); + } + } + // if we reach eof without hitting a marker, stbi__get_marker() below will fail and we'll eventually return 0 + } + } else { + if (!stbi__process_marker(j, m)) return 0; + } + m = stbi__get_marker(j); + } + if (j->progressive) + stbi__jpeg_finish(j); + return 1; +} + +// static jfif-centered resampling (across block boundaries) + +typedef stbi_uc *(*resample_row_func)(stbi_uc *out, stbi_uc *in0, stbi_uc *in1, + int w, int hs); + +#define stbi__div4(x) ((stbi_uc) ((x) >> 2)) + +static stbi_uc *resample_row_1(stbi_uc *out, stbi_uc *in_near, stbi_uc *in_far, int w, int hs) +{ + STBI_NOTUSED(out); + STBI_NOTUSED(in_far); + STBI_NOTUSED(w); + STBI_NOTUSED(hs); + return in_near; +} + +static stbi_uc* stbi__resample_row_v_2(stbi_uc *out, stbi_uc *in_near, stbi_uc *in_far, int w, int hs) +{ + // need to generate two samples vertically for every one in input + int i; + STBI_NOTUSED(hs); + for (i=0; i < w; ++i) + out[i] = stbi__div4(3*in_near[i] + in_far[i] + 2); + return out; +} + +static stbi_uc* stbi__resample_row_h_2(stbi_uc *out, stbi_uc *in_near, stbi_uc *in_far, int w, int hs) +{ + // need to generate two samples horizontally for every one in input + int i; + stbi_uc *input = in_near; + + if (w == 1) { + // if only one sample, can't do any interpolation + out[0] = out[1] = input[0]; + return out; + } + + out[0] = input[0]; + out[1] = stbi__div4(input[0]*3 + input[1] + 2); + for (i=1; i < w-1; ++i) { + int n = 3*input[i]+2; + out[i*2+0] = stbi__div4(n+input[i-1]); + out[i*2+1] = stbi__div4(n+input[i+1]); + } + out[i*2+0] = stbi__div4(input[w-2]*3 + input[w-1] + 2); + out[i*2+1] = input[w-1]; + + STBI_NOTUSED(in_far); + STBI_NOTUSED(hs); + + return out; +} + +#define stbi__div16(x) ((stbi_uc) ((x) >> 4)) + +static stbi_uc *stbi__resample_row_hv_2(stbi_uc *out, stbi_uc *in_near, stbi_uc *in_far, int w, int hs) +{ + // need to generate 2x2 samples for every one in input + int i,t0,t1; + if (w == 1) { + out[0] = out[1] = stbi__div4(3*in_near[0] + in_far[0] + 2); + return out; + } + + t1 = 3*in_near[0] + in_far[0]; + out[0] = stbi__div4(t1+2); + for (i=1; i < w; ++i) { + t0 = t1; + t1 = 3*in_near[i]+in_far[i]; + out[i*2-1] = stbi__div16(3*t0 + t1 + 8); + out[i*2 ] = stbi__div16(3*t1 + t0 + 8); + } + out[w*2-1] = stbi__div4(t1+2); + + STBI_NOTUSED(hs); + + return out; +} + +#if defined(STBI_SSE2) || defined(STBI_NEON) +static stbi_uc *stbi__resample_row_hv_2_simd(stbi_uc *out, stbi_uc *in_near, stbi_uc *in_far, int w, int hs) +{ + // need to generate 2x2 samples for every one in input + int i=0,t0,t1; + + if (w == 1) { + out[0] = out[1] = stbi__div4(3*in_near[0] + in_far[0] + 2); + return out; + } + + t1 = 3*in_near[0] + in_far[0]; + // process groups of 8 pixels for as long as we can. + // note we can't handle the last pixel in a row in this loop + // because we need to handle the filter boundary conditions. + for (; i < ((w-1) & ~7); i += 8) { +#if defined(STBI_SSE2) + // load and perform the vertical filtering pass + // this uses 3*x + y = 4*x + (y - x) + __m128i zero = _mm_setzero_si128(); + __m128i farb = _mm_loadl_epi64((__m128i *) (in_far + i)); + __m128i nearb = _mm_loadl_epi64((__m128i *) (in_near + i)); + __m128i farw = _mm_unpacklo_epi8(farb, zero); + __m128i nearw = _mm_unpacklo_epi8(nearb, zero); + __m128i diff = _mm_sub_epi16(farw, nearw); + __m128i nears = _mm_slli_epi16(nearw, 2); + __m128i curr = _mm_add_epi16(nears, diff); // current row + + // horizontal filter works the same based on shifted vers of current + // row. "prev" is current row shifted right by 1 pixel; we need to + // insert the previous pixel value (from t1). + // "next" is current row shifted left by 1 pixel, with first pixel + // of next block of 8 pixels added in. + __m128i prv0 = _mm_slli_si128(curr, 2); + __m128i nxt0 = _mm_srli_si128(curr, 2); + __m128i prev = _mm_insert_epi16(prv0, t1, 0); + __m128i next = _mm_insert_epi16(nxt0, 3*in_near[i+8] + in_far[i+8], 7); + + // horizontal filter, polyphase implementation since it's convenient: + // even pixels = 3*cur + prev = cur*4 + (prev - cur) + // odd pixels = 3*cur + next = cur*4 + (next - cur) + // note the shared term. + __m128i bias = _mm_set1_epi16(8); + __m128i curs = _mm_slli_epi16(curr, 2); + __m128i prvd = _mm_sub_epi16(prev, curr); + __m128i nxtd = _mm_sub_epi16(next, curr); + __m128i curb = _mm_add_epi16(curs, bias); + __m128i even = _mm_add_epi16(prvd, curb); + __m128i odd = _mm_add_epi16(nxtd, curb); + + // interleave even and odd pixels, then undo scaling. + __m128i int0 = _mm_unpacklo_epi16(even, odd); + __m128i int1 = _mm_unpackhi_epi16(even, odd); + __m128i de0 = _mm_srli_epi16(int0, 4); + __m128i de1 = _mm_srli_epi16(int1, 4); + + // pack and write output + __m128i outv = _mm_packus_epi16(de0, de1); + _mm_storeu_si128((__m128i *) (out + i*2), outv); +#elif defined(STBI_NEON) + // load and perform the vertical filtering pass + // this uses 3*x + y = 4*x + (y - x) + uint8x8_t farb = vld1_u8(in_far + i); + uint8x8_t nearb = vld1_u8(in_near + i); + int16x8_t diff = vreinterpretq_s16_u16(vsubl_u8(farb, nearb)); + int16x8_t nears = vreinterpretq_s16_u16(vshll_n_u8(nearb, 2)); + int16x8_t curr = vaddq_s16(nears, diff); // current row + + // horizontal filter works the same based on shifted vers of current + // row. "prev" is current row shifted right by 1 pixel; we need to + // insert the previous pixel value (from t1). + // "next" is current row shifted left by 1 pixel, with first pixel + // of next block of 8 pixels added in. + int16x8_t prv0 = vextq_s16(curr, curr, 7); + int16x8_t nxt0 = vextq_s16(curr, curr, 1); + int16x8_t prev = vsetq_lane_s16(t1, prv0, 0); + int16x8_t next = vsetq_lane_s16(3*in_near[i+8] + in_far[i+8], nxt0, 7); + + // horizontal filter, polyphase implementation since it's convenient: + // even pixels = 3*cur + prev = cur*4 + (prev - cur) + // odd pixels = 3*cur + next = cur*4 + (next - cur) + // note the shared term. + int16x8_t curs = vshlq_n_s16(curr, 2); + int16x8_t prvd = vsubq_s16(prev, curr); + int16x8_t nxtd = vsubq_s16(next, curr); + int16x8_t even = vaddq_s16(curs, prvd); + int16x8_t odd = vaddq_s16(curs, nxtd); + + // undo scaling and round, then store with even/odd phases interleaved + uint8x8x2_t o; + o.val[0] = vqrshrun_n_s16(even, 4); + o.val[1] = vqrshrun_n_s16(odd, 4); + vst2_u8(out + i*2, o); +#endif + + // "previous" value for next iter + t1 = 3*in_near[i+7] + in_far[i+7]; + } + + t0 = t1; + t1 = 3*in_near[i] + in_far[i]; + out[i*2] = stbi__div16(3*t1 + t0 + 8); + + for (++i; i < w; ++i) { + t0 = t1; + t1 = 3*in_near[i]+in_far[i]; + out[i*2-1] = stbi__div16(3*t0 + t1 + 8); + out[i*2 ] = stbi__div16(3*t1 + t0 + 8); + } + out[w*2-1] = stbi__div4(t1+2); + + STBI_NOTUSED(hs); + + return out; +} +#endif + +static stbi_uc *stbi__resample_row_generic(stbi_uc *out, stbi_uc *in_near, stbi_uc *in_far, int w, int hs) +{ + // resample with nearest-neighbor + int i,j; + STBI_NOTUSED(in_far); + for (i=0; i < w; ++i) + for (j=0; j < hs; ++j) + out[i*hs+j] = in_near[i]; + return out; +} + +#ifdef STBI_JPEG_OLD +// this is the same YCbCr-to-RGB calculation that stb_image has used +// historically before the algorithm changes in 1.49 +#define float2fixed(x) ((int) ((x) * 65536 + 0.5)) +static void stbi__YCbCr_to_RGB_row(stbi_uc *out, const stbi_uc *y, const stbi_uc *pcb, const stbi_uc *pcr, int count, int step) +{ + int i; + for (i=0; i < count; ++i) { + int y_fixed = (y[i] << 16) + 32768; // rounding + int r,g,b; + int cr = pcr[i] - 128; + int cb = pcb[i] - 128; + r = y_fixed + cr*float2fixed(1.40200f); + g = y_fixed - cr*float2fixed(0.71414f) - cb*float2fixed(0.34414f); + b = y_fixed + cb*float2fixed(1.77200f); + r >>= 16; + g >>= 16; + b >>= 16; + if ((unsigned) r > 255) { if (r < 0) r = 0; else r = 255; } + if ((unsigned) g > 255) { if (g < 0) g = 0; else g = 255; } + if ((unsigned) b > 255) { if (b < 0) b = 0; else b = 255; } + out[0] = (stbi_uc)r; + out[1] = (stbi_uc)g; + out[2] = (stbi_uc)b; + out[3] = 255; + out += step; + } +} +#else +// this is a reduced-precision calculation of YCbCr-to-RGB introduced +// to make sure the code produces the same results in both SIMD and scalar +#define float2fixed(x) (((int) ((x) * 4096.0f + 0.5f)) << 8) +static void stbi__YCbCr_to_RGB_row(stbi_uc *out, const stbi_uc *y, const stbi_uc *pcb, const stbi_uc *pcr, int count, int step) +{ + int i; + for (i=0; i < count; ++i) { + int y_fixed = (y[i] << 20) + (1<<19); // rounding + int r,g,b; + int cr = pcr[i] - 128; + int cb = pcb[i] - 128; + r = y_fixed + cr* float2fixed(1.40200f); + g = y_fixed + (cr*-float2fixed(0.71414f)) + ((cb*-float2fixed(0.34414f)) & 0xffff0000); + b = y_fixed + cb* float2fixed(1.77200f); + r >>= 20; + g >>= 20; + b >>= 20; + if ((unsigned) r > 255) { if (r < 0) r = 0; else r = 255; } + if ((unsigned) g > 255) { if (g < 0) g = 0; else g = 255; } + if ((unsigned) b > 255) { if (b < 0) b = 0; else b = 255; } + out[0] = (stbi_uc)r; + out[1] = (stbi_uc)g; + out[2] = (stbi_uc)b; + out[3] = 255; + out += step; + } +} +#endif + +#if defined(STBI_SSE2) || defined(STBI_NEON) +static void stbi__YCbCr_to_RGB_simd(stbi_uc *out, stbi_uc const *y, stbi_uc const *pcb, stbi_uc const *pcr, int count, int step) +{ + int i = 0; + +#ifdef STBI_SSE2 + // step == 3 is pretty ugly on the final interleave, and i'm not convinced + // it's useful in practice (you wouldn't use it for textures, for example). + // so just accelerate step == 4 case. + if (step == 4) { + // this is a fairly straightforward implementation and not super-optimized. + __m128i signflip = _mm_set1_epi8(-0x80); + __m128i cr_const0 = _mm_set1_epi16( (short) ( 1.40200f*4096.0f+0.5f)); + __m128i cr_const1 = _mm_set1_epi16( - (short) ( 0.71414f*4096.0f+0.5f)); + __m128i cb_const0 = _mm_set1_epi16( - (short) ( 0.34414f*4096.0f+0.5f)); + __m128i cb_const1 = _mm_set1_epi16( (short) ( 1.77200f*4096.0f+0.5f)); + __m128i y_bias = _mm_set1_epi8((char) (unsigned char) 128); + __m128i xw = _mm_set1_epi16(255); // alpha channel + + for (; i+7 < count; i += 8) { + // load + __m128i y_bytes = _mm_loadl_epi64((__m128i *) (y+i)); + __m128i cr_bytes = _mm_loadl_epi64((__m128i *) (pcr+i)); + __m128i cb_bytes = _mm_loadl_epi64((__m128i *) (pcb+i)); + __m128i cr_biased = _mm_xor_si128(cr_bytes, signflip); // -128 + __m128i cb_biased = _mm_xor_si128(cb_bytes, signflip); // -128 + + // unpack to short (and left-shift cr, cb by 8) + __m128i yw = _mm_unpacklo_epi8(y_bias, y_bytes); + __m128i crw = _mm_unpacklo_epi8(_mm_setzero_si128(), cr_biased); + __m128i cbw = _mm_unpacklo_epi8(_mm_setzero_si128(), cb_biased); + + // color transform + __m128i yws = _mm_srli_epi16(yw, 4); + __m128i cr0 = _mm_mulhi_epi16(cr_const0, crw); + __m128i cb0 = _mm_mulhi_epi16(cb_const0, cbw); + __m128i cb1 = _mm_mulhi_epi16(cbw, cb_const1); + __m128i cr1 = _mm_mulhi_epi16(crw, cr_const1); + __m128i rws = _mm_add_epi16(cr0, yws); + __m128i gwt = _mm_add_epi16(cb0, yws); + __m128i bws = _mm_add_epi16(yws, cb1); + __m128i gws = _mm_add_epi16(gwt, cr1); + + // descale + __m128i rw = _mm_srai_epi16(rws, 4); + __m128i bw = _mm_srai_epi16(bws, 4); + __m128i gw = _mm_srai_epi16(gws, 4); + + // back to byte, set up for transpose + __m128i brb = _mm_packus_epi16(rw, bw); + __m128i gxb = _mm_packus_epi16(gw, xw); + + // transpose to interleave channels + __m128i t0 = _mm_unpacklo_epi8(brb, gxb); + __m128i t1 = _mm_unpackhi_epi8(brb, gxb); + __m128i o0 = _mm_unpacklo_epi16(t0, t1); + __m128i o1 = _mm_unpackhi_epi16(t0, t1); + + // store + _mm_storeu_si128((__m128i *) (out + 0), o0); + _mm_storeu_si128((__m128i *) (out + 16), o1); + out += 32; + } + } +#endif + +#ifdef STBI_NEON + // in this version, step=3 support would be easy to add. but is there demand? + if (step == 4) { + // this is a fairly straightforward implementation and not super-optimized. + uint8x8_t signflip = vdup_n_u8(0x80); + int16x8_t cr_const0 = vdupq_n_s16( (short) ( 1.40200f*4096.0f+0.5f)); + int16x8_t cr_const1 = vdupq_n_s16( - (short) ( 0.71414f*4096.0f+0.5f)); + int16x8_t cb_const0 = vdupq_n_s16( - (short) ( 0.34414f*4096.0f+0.5f)); + int16x8_t cb_const1 = vdupq_n_s16( (short) ( 1.77200f*4096.0f+0.5f)); + + for (; i+7 < count; i += 8) { + // load + uint8x8_t y_bytes = vld1_u8(y + i); + uint8x8_t cr_bytes = vld1_u8(pcr + i); + uint8x8_t cb_bytes = vld1_u8(pcb + i); + int8x8_t cr_biased = vreinterpret_s8_u8(vsub_u8(cr_bytes, signflip)); + int8x8_t cb_biased = vreinterpret_s8_u8(vsub_u8(cb_bytes, signflip)); + + // expand to s16 + int16x8_t yws = vreinterpretq_s16_u16(vshll_n_u8(y_bytes, 4)); + int16x8_t crw = vshll_n_s8(cr_biased, 7); + int16x8_t cbw = vshll_n_s8(cb_biased, 7); + + // color transform + int16x8_t cr0 = vqdmulhq_s16(crw, cr_const0); + int16x8_t cb0 = vqdmulhq_s16(cbw, cb_const0); + int16x8_t cr1 = vqdmulhq_s16(crw, cr_const1); + int16x8_t cb1 = vqdmulhq_s16(cbw, cb_const1); + int16x8_t rws = vaddq_s16(yws, cr0); + int16x8_t gws = vaddq_s16(vaddq_s16(yws, cb0), cr1); + int16x8_t bws = vaddq_s16(yws, cb1); + + // undo scaling, round, convert to byte + uint8x8x4_t o; + o.val[0] = vqrshrun_n_s16(rws, 4); + o.val[1] = vqrshrun_n_s16(gws, 4); + o.val[2] = vqrshrun_n_s16(bws, 4); + o.val[3] = vdup_n_u8(255); + + // store, interleaving r/g/b/a + vst4_u8(out, o); + out += 8*4; + } + } +#endif + + for (; i < count; ++i) { + int y_fixed = (y[i] << 20) + (1<<19); // rounding + int r,g,b; + int cr = pcr[i] - 128; + int cb = pcb[i] - 128; + r = y_fixed + cr* float2fixed(1.40200f); + g = y_fixed + cr*-float2fixed(0.71414f) + ((cb*-float2fixed(0.34414f)) & 0xffff0000); + b = y_fixed + cb* float2fixed(1.77200f); + r >>= 20; + g >>= 20; + b >>= 20; + if ((unsigned) r > 255) { if (r < 0) r = 0; else r = 255; } + if ((unsigned) g > 255) { if (g < 0) g = 0; else g = 255; } + if ((unsigned) b > 255) { if (b < 0) b = 0; else b = 255; } + out[0] = (stbi_uc)r; + out[1] = (stbi_uc)g; + out[2] = (stbi_uc)b; + out[3] = 255; + out += step; + } +} +#endif + +// set up the kernels +static void stbi__setup_jpeg(stbi__jpeg *j) +{ + j->idct_block_kernel = stbi__idct_block; + j->YCbCr_to_RGB_kernel = stbi__YCbCr_to_RGB_row; + j->resample_row_hv_2_kernel = stbi__resample_row_hv_2; + +#ifdef STBI_SSE2 + if (stbi__sse2_available()) { + j->idct_block_kernel = stbi__idct_simd; + #ifndef STBI_JPEG_OLD + j->YCbCr_to_RGB_kernel = stbi__YCbCr_to_RGB_simd; + #endif + j->resample_row_hv_2_kernel = stbi__resample_row_hv_2_simd; + } +#endif + +#ifdef STBI_NEON + j->idct_block_kernel = stbi__idct_simd; + #ifndef STBI_JPEG_OLD + j->YCbCr_to_RGB_kernel = stbi__YCbCr_to_RGB_simd; + #endif + j->resample_row_hv_2_kernel = stbi__resample_row_hv_2_simd; +#endif +} + +// clean up the temporary component buffers +static void stbi__cleanup_jpeg(stbi__jpeg *j) +{ + int i; + for (i=0; i < j->s->img_n; ++i) { + if (j->img_comp[i].raw_data) { + STBI_FREE(j->img_comp[i].raw_data); + j->img_comp[i].raw_data = NULL; + j->img_comp[i].data = NULL; + } + if (j->img_comp[i].raw_coeff) { + STBI_FREE(j->img_comp[i].raw_coeff); + j->img_comp[i].raw_coeff = 0; + j->img_comp[i].coeff = 0; + } + if (j->img_comp[i].linebuf) { + STBI_FREE(j->img_comp[i].linebuf); + j->img_comp[i].linebuf = NULL; + } + } +} + +typedef struct +{ + resample_row_func resample; + stbi_uc *line0,*line1; + int hs,vs; // expansion factor in each axis + int w_lores; // horizontal pixels pre-expansion + int ystep; // how far through vertical expansion we are + int ypos; // which pre-expansion row we're on +} stbi__resample; + +static stbi_uc *load_jpeg_image(stbi__jpeg *z, int *out_x, int *out_y, int *comp, int req_comp) +{ + int n, decode_n; + z->s->img_n = 0; // make stbi__cleanup_jpeg safe + + // validate req_comp + if (req_comp < 0 || req_comp > 4) return stbi__errpuc("bad req_comp", "Internal error"); + + // load a jpeg image from whichever source, but leave in YCbCr format + if (!stbi__decode_jpeg_image(z)) { stbi__cleanup_jpeg(z); return NULL; } + + // determine actual number of components to generate + n = req_comp ? req_comp : z->s->img_n; + + if (z->s->img_n == 3 && n < 3) + decode_n = 1; + else + decode_n = z->s->img_n; + + // resample and color-convert + { + int k; + unsigned int i,j; + stbi_uc *output; + stbi_uc *coutput[4]; + + stbi__resample res_comp[4]; + + for (k=0; k < decode_n; ++k) { + stbi__resample *r = &res_comp[k]; + + // allocate line buffer big enough for upsampling off the edges + // with upsample factor of 4 + z->img_comp[k].linebuf = (stbi_uc *) stbi__malloc(z->s->img_x + 3); + if (!z->img_comp[k].linebuf) { stbi__cleanup_jpeg(z); return stbi__errpuc("outofmem", "Out of memory"); } + + r->hs = z->img_h_max / z->img_comp[k].h; + r->vs = z->img_v_max / z->img_comp[k].v; + r->ystep = r->vs >> 1; + r->w_lores = (z->s->img_x + r->hs-1) / r->hs; + r->ypos = 0; + r->line0 = r->line1 = z->img_comp[k].data; + + if (r->hs == 1 && r->vs == 1) r->resample = resample_row_1; + else if (r->hs == 1 && r->vs == 2) r->resample = stbi__resample_row_v_2; + else if (r->hs == 2 && r->vs == 1) r->resample = stbi__resample_row_h_2; + else if (r->hs == 2 && r->vs == 2) r->resample = z->resample_row_hv_2_kernel; + else r->resample = stbi__resample_row_generic; + } + + // can't error after this so, this is safe + output = (stbi_uc *) stbi__malloc(n * z->s->img_x * z->s->img_y + 1); + if (!output) { stbi__cleanup_jpeg(z); return stbi__errpuc("outofmem", "Out of memory"); } + + // now go ahead and resample + for (j=0; j < z->s->img_y; ++j) { + stbi_uc *out = output + n * z->s->img_x * j; + for (k=0; k < decode_n; ++k) { + stbi__resample *r = &res_comp[k]; + int y_bot = r->ystep >= (r->vs >> 1); + coutput[k] = r->resample(z->img_comp[k].linebuf, + y_bot ? r->line1 : r->line0, + y_bot ? r->line0 : r->line1, + r->w_lores, r->hs); + if (++r->ystep >= r->vs) { + r->ystep = 0; + r->line0 = r->line1; + if (++r->ypos < z->img_comp[k].y) + r->line1 += z->img_comp[k].w2; + } + } + if (n >= 3) { + stbi_uc *y = coutput[0]; + if (z->s->img_n == 3) { + if (z->rgb == 3) { + for (i=0; i < z->s->img_x; ++i) { + out[0] = y[i]; + out[1] = coutput[1][i]; + out[2] = coutput[2][i]; + out[3] = 255; + out += n; + } + } else { + z->YCbCr_to_RGB_kernel(out, y, coutput[1], coutput[2], z->s->img_x, n); + } + } else + for (i=0; i < z->s->img_x; ++i) { + out[0] = out[1] = out[2] = y[i]; + out[3] = 255; // not used if n==3 + out += n; + } + } else { + stbi_uc *y = coutput[0]; + if (n == 1) + for (i=0; i < z->s->img_x; ++i) out[i] = y[i]; + else + for (i=0; i < z->s->img_x; ++i) *out++ = y[i], *out++ = 255; + } + } + stbi__cleanup_jpeg(z); + *out_x = z->s->img_x; + *out_y = z->s->img_y; + if (comp) *comp = z->s->img_n; // report original components, not output + return output; + } +} + +static unsigned char *stbi__jpeg_load(stbi__context *s, int *x, int *y, int *comp, int req_comp) +{ + unsigned char* result; + stbi__jpeg* j = (stbi__jpeg*) stbi__malloc(sizeof(stbi__jpeg)); + j->s = s; + stbi__setup_jpeg(j); + result = load_jpeg_image(j, x,y,comp,req_comp); + STBI_FREE(j); + return result; +} + +static int stbi__jpeg_test(stbi__context *s) +{ + int r; + stbi__jpeg j; + j.s = s; + stbi__setup_jpeg(&j); + r = stbi__decode_jpeg_header(&j, STBI__SCAN_type); + stbi__rewind(s); + return r; +} + +static int stbi__jpeg_info_raw(stbi__jpeg *j, int *x, int *y, int *comp) +{ + if (!stbi__decode_jpeg_header(j, STBI__SCAN_header)) { + stbi__rewind( j->s ); + return 0; + } + if (x) *x = j->s->img_x; + if (y) *y = j->s->img_y; + if (comp) *comp = j->s->img_n; + return 1; +} + +static int stbi__jpeg_info(stbi__context *s, int *x, int *y, int *comp) +{ + int result; + stbi__jpeg* j = (stbi__jpeg*) (stbi__malloc(sizeof(stbi__jpeg))); + j->s = s; + result = stbi__jpeg_info_raw(j, x, y, comp); + STBI_FREE(j); + return result; +} +#endif + +// public domain zlib decode v0.2 Sean Barrett 2006-11-18 +// simple implementation +// - all input must be provided in an upfront buffer +// - all output is written to a single output buffer (can malloc/realloc) +// performance +// - fast huffman + +#ifndef STBI_NO_ZLIB + +// fast-way is faster to check than jpeg huffman, but slow way is slower +#define STBI__ZFAST_BITS 9 // accelerate all cases in default tables +#define STBI__ZFAST_MASK ((1 << STBI__ZFAST_BITS) - 1) + +// zlib-style huffman encoding +// (jpegs packs from left, zlib from right, so can't share code) +typedef struct +{ + stbi__uint16 fast[1 << STBI__ZFAST_BITS]; + stbi__uint16 firstcode[16]; + int maxcode[17]; + stbi__uint16 firstsymbol[16]; + stbi_uc size[288]; + stbi__uint16 value[288]; +} stbi__zhuffman; + +stbi_inline static int stbi__bitreverse16(int n) +{ + n = ((n & 0xAAAA) >> 1) | ((n & 0x5555) << 1); + n = ((n & 0xCCCC) >> 2) | ((n & 0x3333) << 2); + n = ((n & 0xF0F0) >> 4) | ((n & 0x0F0F) << 4); + n = ((n & 0xFF00) >> 8) | ((n & 0x00FF) << 8); + return n; +} + +stbi_inline static int stbi__bit_reverse(int v, int bits) +{ + STBI_ASSERT(bits <= 16); + // to bit reverse n bits, reverse 16 and shift + // e.g. 11 bits, bit reverse and shift away 5 + return stbi__bitreverse16(v) >> (16-bits); +} + +static int stbi__zbuild_huffman(stbi__zhuffman *z, stbi_uc *sizelist, int num) +{ + int i,k=0; + int code, next_code[16], sizes[17]; + + // DEFLATE spec for generating codes + memset(sizes, 0, sizeof(sizes)); + memset(z->fast, 0, sizeof(z->fast)); + for (i=0; i < num; ++i) + ++sizes[sizelist[i]]; + sizes[0] = 0; + for (i=1; i < 16; ++i) + if (sizes[i] > (1 << i)) + return stbi__err("bad sizes", "Corrupt PNG"); + code = 0; + for (i=1; i < 16; ++i) { + next_code[i] = code; + z->firstcode[i] = (stbi__uint16) code; + z->firstsymbol[i] = (stbi__uint16) k; + code = (code + sizes[i]); + if (sizes[i]) + if (code-1 >= (1 << i)) return stbi__err("bad codelengths","Corrupt PNG"); + z->maxcode[i] = code << (16-i); // preshift for inner loop + code <<= 1; + k += sizes[i]; + } + z->maxcode[16] = 0x10000; // sentinel + for (i=0; i < num; ++i) { + int s = sizelist[i]; + if (s) { + int c = next_code[s] - z->firstcode[s] + z->firstsymbol[s]; + stbi__uint16 fastv = (stbi__uint16) ((s << 9) | i); + z->size [c] = (stbi_uc ) s; + z->value[c] = (stbi__uint16) i; + if (s <= STBI__ZFAST_BITS) { + int j = stbi__bit_reverse(next_code[s],s); + while (j < (1 << STBI__ZFAST_BITS)) { + z->fast[j] = fastv; + j += (1 << s); + } + } + ++next_code[s]; + } + } + return 1; +} + +// zlib-from-memory implementation for PNG reading +// because PNG allows splitting the zlib stream arbitrarily, +// and it's annoying structurally to have PNG call ZLIB call PNG, +// we require PNG read all the IDATs and combine them into a single +// memory buffer + +typedef struct +{ + stbi_uc *zbuffer, *zbuffer_end; + int num_bits; + stbi__uint32 code_buffer; + + char *zout; + char *zout_start; + char *zout_end; + int z_expandable; + + stbi__zhuffman z_length, z_distance; +} stbi__zbuf; + +stbi_inline static stbi_uc stbi__zget8(stbi__zbuf *z) +{ + if (z->zbuffer >= z->zbuffer_end) return 0; + return *z->zbuffer++; +} + +static void stbi__fill_bits(stbi__zbuf *z) +{ + do { + STBI_ASSERT(z->code_buffer < (1U << z->num_bits)); + z->code_buffer |= (unsigned int) stbi__zget8(z) << z->num_bits; + z->num_bits += 8; + } while (z->num_bits <= 24); +} + +stbi_inline static unsigned int stbi__zreceive(stbi__zbuf *z, int n) +{ + unsigned int k; + if (z->num_bits < n) stbi__fill_bits(z); + k = z->code_buffer & ((1 << n) - 1); + z->code_buffer >>= n; + z->num_bits -= n; + return k; +} + +static int stbi__zhuffman_decode_slowpath(stbi__zbuf *a, stbi__zhuffman *z) +{ + int b,s,k; + // not resolved by fast table, so compute it the slow way + // use jpeg approach, which requires MSbits at top + k = stbi__bit_reverse(a->code_buffer, 16); + for (s=STBI__ZFAST_BITS+1; ; ++s) + if (k < z->maxcode[s]) + break; + if (s == 16) return -1; // invalid code! + // code size is s, so: + b = (k >> (16-s)) - z->firstcode[s] + z->firstsymbol[s]; + STBI_ASSERT(z->size[b] == s); + a->code_buffer >>= s; + a->num_bits -= s; + return z->value[b]; +} + +stbi_inline static int stbi__zhuffman_decode(stbi__zbuf *a, stbi__zhuffman *z) +{ + int b,s; + if (a->num_bits < 16) stbi__fill_bits(a); + b = z->fast[a->code_buffer & STBI__ZFAST_MASK]; + if (b) { + s = b >> 9; + a->code_buffer >>= s; + a->num_bits -= s; + return b & 511; + } + return stbi__zhuffman_decode_slowpath(a, z); +} + +static int stbi__zexpand(stbi__zbuf *z, char *zout, int n) // need to make room for n bytes +{ + char *q; + int cur, limit, old_limit; + z->zout = zout; + if (!z->z_expandable) return stbi__err("output buffer limit","Corrupt PNG"); + cur = (int) (z->zout - z->zout_start); + limit = old_limit = (int) (z->zout_end - z->zout_start); + while (cur + n > limit) + limit *= 2; + q = (char *) STBI_REALLOC_SIZED(z->zout_start, old_limit, limit); + STBI_NOTUSED(old_limit); + if (q == NULL) return stbi__err("outofmem", "Out of memory"); + z->zout_start = q; + z->zout = q + cur; + z->zout_end = q + limit; + return 1; +} + +static int stbi__zlength_base[31] = { + 3,4,5,6,7,8,9,10,11,13, + 15,17,19,23,27,31,35,43,51,59, + 67,83,99,115,131,163,195,227,258,0,0 }; + +static int stbi__zlength_extra[31]= +{ 0,0,0,0,0,0,0,0,1,1,1,1,2,2,2,2,3,3,3,3,4,4,4,4,5,5,5,5,0,0,0 }; + +static int stbi__zdist_base[32] = { 1,2,3,4,5,7,9,13,17,25,33,49,65,97,129,193, +257,385,513,769,1025,1537,2049,3073,4097,6145,8193,12289,16385,24577,0,0}; + +static int stbi__zdist_extra[32] = +{ 0,0,0,0,1,1,2,2,3,3,4,4,5,5,6,6,7,7,8,8,9,9,10,10,11,11,12,12,13,13}; + +static int stbi__parse_huffman_block(stbi__zbuf *a) +{ + char *zout = a->zout; + for(;;) { + int z = stbi__zhuffman_decode(a, &a->z_length); + if (z < 256) { + if (z < 0) return stbi__err("bad huffman code","Corrupt PNG"); // error in huffman codes + if (zout >= a->zout_end) { + if (!stbi__zexpand(a, zout, 1)) return 0; + zout = a->zout; + } + *zout++ = (char) z; + } else { + stbi_uc *p; + int len,dist; + if (z == 256) { + a->zout = zout; + return 1; + } + z -= 257; + len = stbi__zlength_base[z]; + if (stbi__zlength_extra[z]) len += stbi__zreceive(a, stbi__zlength_extra[z]); + z = stbi__zhuffman_decode(a, &a->z_distance); + if (z < 0) return stbi__err("bad huffman code","Corrupt PNG"); + dist = stbi__zdist_base[z]; + if (stbi__zdist_extra[z]) dist += stbi__zreceive(a, stbi__zdist_extra[z]); + if (zout - a->zout_start < dist) return stbi__err("bad dist","Corrupt PNG"); + if (zout + len > a->zout_end) { + if (!stbi__zexpand(a, zout, len)) return 0; + zout = a->zout; + } + p = (stbi_uc *) (zout - dist); + if (dist == 1) { // run of one byte; common in images. + stbi_uc v = *p; + if (len) { do *zout++ = v; while (--len); } + } else { + if (len) { do *zout++ = *p++; while (--len); } + } + } + } +} + +static int stbi__compute_huffman_codes(stbi__zbuf *a) +{ + static stbi_uc length_dezigzag[19] = { 16,17,18,0,8,7,9,6,10,5,11,4,12,3,13,2,14,1,15 }; + stbi__zhuffman z_codelength; + stbi_uc lencodes[286+32+137];//padding for maximum single op + stbi_uc codelength_sizes[19]; + int i,n; + + int hlit = stbi__zreceive(a,5) + 257; + int hdist = stbi__zreceive(a,5) + 1; + int hclen = stbi__zreceive(a,4) + 4; + + memset(codelength_sizes, 0, sizeof(codelength_sizes)); + for (i=0; i < hclen; ++i) { + int s = stbi__zreceive(a,3); + codelength_sizes[length_dezigzag[i]] = (stbi_uc) s; + } + if (!stbi__zbuild_huffman(&z_codelength, codelength_sizes, 19)) return 0; + + n = 0; + while (n < hlit + hdist) { + int c = stbi__zhuffman_decode(a, &z_codelength); + if (c < 0 || c >= 19) return stbi__err("bad codelengths", "Corrupt PNG"); + if (c < 16) + lencodes[n++] = (stbi_uc) c; + else if (c == 16) { + c = stbi__zreceive(a,2)+3; + memset(lencodes+n, lencodes[n-1], c); + n += c; + } else if (c == 17) { + c = stbi__zreceive(a,3)+3; + memset(lencodes+n, 0, c); + n += c; + } else { + STBI_ASSERT(c == 18); + c = stbi__zreceive(a,7)+11; + memset(lencodes+n, 0, c); + n += c; + } + } + if (n != hlit+hdist) return stbi__err("bad codelengths","Corrupt PNG"); + if (!stbi__zbuild_huffman(&a->z_length, lencodes, hlit)) return 0; + if (!stbi__zbuild_huffman(&a->z_distance, lencodes+hlit, hdist)) return 0; + return 1; +} + +static int stbi__parse_uncompressed_block(stbi__zbuf *a) +{ + stbi_uc header[4]; + int len,nlen,k; + if (a->num_bits & 7) + stbi__zreceive(a, a->num_bits & 7); // discard + // drain the bit-packed data into header + k = 0; + while (a->num_bits > 0) { + header[k++] = (stbi_uc) (a->code_buffer & 255); // suppress MSVC run-time check + a->code_buffer >>= 8; + a->num_bits -= 8; + } + STBI_ASSERT(a->num_bits == 0); + // now fill header the normal way + while (k < 4) + header[k++] = stbi__zget8(a); + len = header[1] * 256 + header[0]; + nlen = header[3] * 256 + header[2]; + if (nlen != (len ^ 0xffff)) return stbi__err("zlib corrupt","Corrupt PNG"); + if (a->zbuffer + len > a->zbuffer_end) return stbi__err("read past buffer","Corrupt PNG"); + if (a->zout + len > a->zout_end) + if (!stbi__zexpand(a, a->zout, len)) return 0; + memcpy(a->zout, a->zbuffer, len); + a->zbuffer += len; + a->zout += len; + return 1; +} + +static int stbi__parse_zlib_header(stbi__zbuf *a) +{ + int cmf = stbi__zget8(a); + int cm = cmf & 15; + /* int cinfo = cmf >> 4; */ + int flg = stbi__zget8(a); + if ((cmf*256+flg) % 31 != 0) return stbi__err("bad zlib header","Corrupt PNG"); // zlib spec + if (flg & 32) return stbi__err("no preset dict","Corrupt PNG"); // preset dictionary not allowed in png + if (cm != 8) return stbi__err("bad compression","Corrupt PNG"); // DEFLATE required for png + // window = 1 << (8 + cinfo)... but who cares, we fully buffer output + return 1; +} + +// @TODO: should statically initialize these for optimal thread safety +static stbi_uc stbi__zdefault_length[288], stbi__zdefault_distance[32]; +static void stbi__init_zdefaults(void) +{ + int i; // use <= to match clearly with spec + for (i=0; i <= 143; ++i) stbi__zdefault_length[i] = 8; + for ( ; i <= 255; ++i) stbi__zdefault_length[i] = 9; + for ( ; i <= 279; ++i) stbi__zdefault_length[i] = 7; + for ( ; i <= 287; ++i) stbi__zdefault_length[i] = 8; + + for (i=0; i <= 31; ++i) stbi__zdefault_distance[i] = 5; +} + +static int stbi__parse_zlib(stbi__zbuf *a, int parse_header) +{ + int final, type; + if (parse_header) + if (!stbi__parse_zlib_header(a)) return 0; + a->num_bits = 0; + a->code_buffer = 0; + do { + final = stbi__zreceive(a,1); + type = stbi__zreceive(a,2); + if (type == 0) { + if (!stbi__parse_uncompressed_block(a)) return 0; + } else if (type == 3) { + return 0; + } else { + if (type == 1) { + // use fixed code lengths + if (!stbi__zdefault_distance[31]) stbi__init_zdefaults(); + if (!stbi__zbuild_huffman(&a->z_length , stbi__zdefault_length , 288)) return 0; + if (!stbi__zbuild_huffman(&a->z_distance, stbi__zdefault_distance, 32)) return 0; + } else { + if (!stbi__compute_huffman_codes(a)) return 0; + } + if (!stbi__parse_huffman_block(a)) return 0; + } + } while (!final); + return 1; +} + +static int stbi__do_zlib(stbi__zbuf *a, char *obuf, int olen, int exp, int parse_header) +{ + a->zout_start = obuf; + a->zout = obuf; + a->zout_end = obuf + olen; + a->z_expandable = exp; + + return stbi__parse_zlib(a, parse_header); +} + +STBIDEF char *stbi_zlib_decode_malloc_guesssize(const char *buffer, int len, int initial_size, int *outlen) +{ + stbi__zbuf a; + char *p = (char *) stbi__malloc(initial_size); + if (p == NULL) return NULL; + a.zbuffer = (stbi_uc *) buffer; + a.zbuffer_end = (stbi_uc *) buffer + len; + if (stbi__do_zlib(&a, p, initial_size, 1, 1)) { + if (outlen) *outlen = (int) (a.zout - a.zout_start); + return a.zout_start; + } else { + STBI_FREE(a.zout_start); + return NULL; + } +} + +STBIDEF char *stbi_zlib_decode_malloc(char const *buffer, int len, int *outlen) +{ + return stbi_zlib_decode_malloc_guesssize(buffer, len, 16384, outlen); +} + +STBIDEF char *stbi_zlib_decode_malloc_guesssize_headerflag(const char *buffer, int len, int initial_size, int *outlen, int parse_header) +{ + stbi__zbuf a; + char *p = (char *) stbi__malloc(initial_size); + if (p == NULL) return NULL; + a.zbuffer = (stbi_uc *) buffer; + a.zbuffer_end = (stbi_uc *) buffer + len; + if (stbi__do_zlib(&a, p, initial_size, 1, parse_header)) { + if (outlen) *outlen = (int) (a.zout - a.zout_start); + return a.zout_start; + } else { + STBI_FREE(a.zout_start); + return NULL; + } +} + +STBIDEF int stbi_zlib_decode_buffer(char *obuffer, int olen, char const *ibuffer, int ilen) +{ + stbi__zbuf a; + a.zbuffer = (stbi_uc *) ibuffer; + a.zbuffer_end = (stbi_uc *) ibuffer + ilen; + if (stbi__do_zlib(&a, obuffer, olen, 0, 1)) + return (int) (a.zout - a.zout_start); + else + return -1; +} + +STBIDEF char *stbi_zlib_decode_noheader_malloc(char const *buffer, int len, int *outlen) +{ + stbi__zbuf a; + char *p = (char *) stbi__malloc(16384); + if (p == NULL) return NULL; + a.zbuffer = (stbi_uc *) buffer; + a.zbuffer_end = (stbi_uc *) buffer+len; + if (stbi__do_zlib(&a, p, 16384, 1, 0)) { + if (outlen) *outlen = (int) (a.zout - a.zout_start); + return a.zout_start; + } else { + STBI_FREE(a.zout_start); + return NULL; + } +} + +STBIDEF int stbi_zlib_decode_noheader_buffer(char *obuffer, int olen, const char *ibuffer, int ilen) +{ + stbi__zbuf a; + a.zbuffer = (stbi_uc *) ibuffer; + a.zbuffer_end = (stbi_uc *) ibuffer + ilen; + if (stbi__do_zlib(&a, obuffer, olen, 0, 0)) + return (int) (a.zout - a.zout_start); + else + return -1; +} +#endif + +// public domain "baseline" PNG decoder v0.10 Sean Barrett 2006-11-18 +// simple implementation +// - only 8-bit samples +// - no CRC checking +// - allocates lots of intermediate memory +// - avoids problem of streaming data between subsystems +// - avoids explicit window management +// performance +// - uses stb_zlib, a PD zlib implementation with fast huffman decoding + +#ifndef STBI_NO_PNG +typedef struct +{ + stbi__uint32 length; + stbi__uint32 type; +} stbi__pngchunk; + +static stbi__pngchunk stbi__get_chunk_header(stbi__context *s) +{ + stbi__pngchunk c; + c.length = stbi__get32be(s); + c.type = stbi__get32be(s); + return c; +} + +static int stbi__check_png_header(stbi__context *s) +{ + static stbi_uc png_sig[8] = { 137,80,78,71,13,10,26,10 }; + int i; + for (i=0; i < 8; ++i) + if (stbi__get8(s) != png_sig[i]) return stbi__err("bad png sig","Not a PNG"); + return 1; +} + +typedef struct +{ + stbi__context *s; + stbi_uc *idata, *expanded, *out; + int depth; +} stbi__png; + + +enum { + STBI__F_none=0, + STBI__F_sub=1, + STBI__F_up=2, + STBI__F_avg=3, + STBI__F_paeth=4, + // synthetic filters used for first scanline to avoid needing a dummy row of 0s + STBI__F_avg_first, + STBI__F_paeth_first +}; + +static stbi_uc first_row_filter[5] = +{ + STBI__F_none, + STBI__F_sub, + STBI__F_none, + STBI__F_avg_first, + STBI__F_paeth_first +}; + +static int stbi__paeth(int a, int b, int c) +{ + int p = a + b - c; + int pa = abs(p-a); + int pb = abs(p-b); + int pc = abs(p-c); + if (pa <= pb && pa <= pc) return a; + if (pb <= pc) return b; + return c; +} + +static stbi_uc stbi__depth_scale_table[9] = { 0, 0xff, 0x55, 0, 0x11, 0,0,0, 0x01 }; + +// create the png data from post-deflated data +static int stbi__create_png_image_raw(stbi__png *a, stbi_uc *raw, stbi__uint32 raw_len, int out_n, stbi__uint32 x, stbi__uint32 y, int depth, int color) +{ + int bytes = (depth == 16? 2 : 1); + stbi__context *s = a->s; + stbi__uint32 i,j,stride = x*out_n*bytes; + stbi__uint32 img_len, img_width_bytes; + int k; + int img_n = s->img_n; // copy it into a local for later + + int output_bytes = out_n*bytes; + int filter_bytes = img_n*bytes; + int width = x; + + STBI_ASSERT(out_n == s->img_n || out_n == s->img_n+1); + a->out = (stbi_uc *) stbi__malloc(x * y * output_bytes); // extra bytes to write off the end into + if (!a->out) return stbi__err("outofmem", "Out of memory"); + + img_width_bytes = (((img_n * x * depth) + 7) >> 3); + img_len = (img_width_bytes + 1) * y; + if (s->img_x == x && s->img_y == y) { + if (raw_len != img_len) return stbi__err("not enough pixels","Corrupt PNG"); + } else { // interlaced: + if (raw_len < img_len) return stbi__err("not enough pixels","Corrupt PNG"); + } + + for (j=0; j < y; ++j) { + stbi_uc *cur = a->out + stride*j; + stbi_uc *prior = cur - stride; + int filter = *raw++; + + if (filter > 4) + return stbi__err("invalid filter","Corrupt PNG"); + + if (depth < 8) { + STBI_ASSERT(img_width_bytes <= x); + cur += x*out_n - img_width_bytes; // store output to the rightmost img_len bytes, so we can decode in place + filter_bytes = 1; + width = img_width_bytes; + } + + // if first row, use special filter that doesn't sample previous row + if (j == 0) filter = first_row_filter[filter]; + + // handle first byte explicitly + for (k=0; k < filter_bytes; ++k) { + switch (filter) { + case STBI__F_none : cur[k] = raw[k]; break; + case STBI__F_sub : cur[k] = raw[k]; break; + case STBI__F_up : cur[k] = STBI__BYTECAST(raw[k] + prior[k]); break; + case STBI__F_avg : cur[k] = STBI__BYTECAST(raw[k] + (prior[k]>>1)); break; + case STBI__F_paeth : cur[k] = STBI__BYTECAST(raw[k] + stbi__paeth(0,prior[k],0)); break; + case STBI__F_avg_first : cur[k] = raw[k]; break; + case STBI__F_paeth_first: cur[k] = raw[k]; break; + } + } + + if (depth == 8) { + if (img_n != out_n) + cur[img_n] = 255; // first pixel + raw += img_n; + cur += out_n; + prior += out_n; + } else if (depth == 16) { + if (img_n != out_n) { + cur[filter_bytes] = 255; // first pixel top byte + cur[filter_bytes+1] = 255; // first pixel bottom byte + } + raw += filter_bytes; + cur += output_bytes; + prior += output_bytes; + } else { + raw += 1; + cur += 1; + prior += 1; + } + + // this is a little gross, so that we don't switch per-pixel or per-component + if (depth < 8 || img_n == out_n) { + int nk = (width - 1)*filter_bytes; + #define CASE(f) \ + case f: \ + for (k=0; k < nk; ++k) + switch (filter) { + // "none" filter turns into a memcpy here; make that explicit. + case STBI__F_none: memcpy(cur, raw, nk); break; + CASE(STBI__F_sub) cur[k] = STBI__BYTECAST(raw[k] + cur[k-filter_bytes]); break; + CASE(STBI__F_up) cur[k] = STBI__BYTECAST(raw[k] + prior[k]); break; + CASE(STBI__F_avg) cur[k] = STBI__BYTECAST(raw[k] + ((prior[k] + cur[k-filter_bytes])>>1)); break; + CASE(STBI__F_paeth) cur[k] = STBI__BYTECAST(raw[k] + stbi__paeth(cur[k-filter_bytes],prior[k],prior[k-filter_bytes])); break; + CASE(STBI__F_avg_first) cur[k] = STBI__BYTECAST(raw[k] + (cur[k-filter_bytes] >> 1)); break; + CASE(STBI__F_paeth_first) cur[k] = STBI__BYTECAST(raw[k] + stbi__paeth(cur[k-filter_bytes],0,0)); break; + } + #undef CASE + raw += nk; + } else { + STBI_ASSERT(img_n+1 == out_n); + #define CASE(f) \ + case f: \ + for (i=x-1; i >= 1; --i, cur[filter_bytes]=255,raw+=filter_bytes,cur+=output_bytes,prior+=output_bytes) \ + for (k=0; k < filter_bytes; ++k) + switch (filter) { + CASE(STBI__F_none) cur[k] = raw[k]; break; + CASE(STBI__F_sub) cur[k] = STBI__BYTECAST(raw[k] + cur[k- output_bytes]); break; + CASE(STBI__F_up) cur[k] = STBI__BYTECAST(raw[k] + prior[k]); break; + CASE(STBI__F_avg) cur[k] = STBI__BYTECAST(raw[k] + ((prior[k] + cur[k- output_bytes])>>1)); break; + CASE(STBI__F_paeth) cur[k] = STBI__BYTECAST(raw[k] + stbi__paeth(cur[k- output_bytes],prior[k],prior[k- output_bytes])); break; + CASE(STBI__F_avg_first) cur[k] = STBI__BYTECAST(raw[k] + (cur[k- output_bytes] >> 1)); break; + CASE(STBI__F_paeth_first) cur[k] = STBI__BYTECAST(raw[k] + stbi__paeth(cur[k- output_bytes],0,0)); break; + } + #undef CASE + + // the loop above sets the high byte of the pixels' alpha, but for + // 16 bit png files we also need the low byte set. we'll do that here. + if (depth == 16) { + cur = a->out + stride*j; // start at the beginning of the row again + for (i=0; i < x; ++i,cur+=output_bytes) { + cur[filter_bytes+1] = 255; + } + } + } + } + + // we make a separate pass to expand bits to pixels; for performance, + // this could run two scanlines behind the above code, so it won't + // intefere with filtering but will still be in the cache. + if (depth < 8) { + for (j=0; j < y; ++j) { + stbi_uc *cur = a->out + stride*j; + stbi_uc *in = a->out + stride*j + x*out_n - img_width_bytes; + // unpack 1/2/4-bit into a 8-bit buffer. allows us to keep the common 8-bit path optimal at minimal cost for 1/2/4-bit + // png guarante byte alignment, if width is not multiple of 8/4/2 we'll decode dummy trailing data that will be skipped in the later loop + stbi_uc scale = (color == 0) ? stbi__depth_scale_table[depth] : 1; // scale grayscale values to 0..255 range + + // note that the final byte might overshoot and write more data than desired. + // we can allocate enough data that this never writes out of memory, but it + // could also overwrite the next scanline. can it overwrite non-empty data + // on the next scanline? yes, consider 1-pixel-wide scanlines with 1-bit-per-pixel. + // so we need to explicitly clamp the final ones + + if (depth == 4) { + for (k=x*img_n; k >= 2; k-=2, ++in) { + *cur++ = scale * ((*in >> 4) ); + *cur++ = scale * ((*in ) & 0x0f); + } + if (k > 0) *cur++ = scale * ((*in >> 4) ); + } else if (depth == 2) { + for (k=x*img_n; k >= 4; k-=4, ++in) { + *cur++ = scale * ((*in >> 6) ); + *cur++ = scale * ((*in >> 4) & 0x03); + *cur++ = scale * ((*in >> 2) & 0x03); + *cur++ = scale * ((*in ) & 0x03); + } + if (k > 0) *cur++ = scale * ((*in >> 6) ); + if (k > 1) *cur++ = scale * ((*in >> 4) & 0x03); + if (k > 2) *cur++ = scale * ((*in >> 2) & 0x03); + } else if (depth == 1) { + for (k=x*img_n; k >= 8; k-=8, ++in) { + *cur++ = scale * ((*in >> 7) ); + *cur++ = scale * ((*in >> 6) & 0x01); + *cur++ = scale * ((*in >> 5) & 0x01); + *cur++ = scale * ((*in >> 4) & 0x01); + *cur++ = scale * ((*in >> 3) & 0x01); + *cur++ = scale * ((*in >> 2) & 0x01); + *cur++ = scale * ((*in >> 1) & 0x01); + *cur++ = scale * ((*in ) & 0x01); + } + if (k > 0) *cur++ = scale * ((*in >> 7) ); + if (k > 1) *cur++ = scale * ((*in >> 6) & 0x01); + if (k > 2) *cur++ = scale * ((*in >> 5) & 0x01); + if (k > 3) *cur++ = scale * ((*in >> 4) & 0x01); + if (k > 4) *cur++ = scale * ((*in >> 3) & 0x01); + if (k > 5) *cur++ = scale * ((*in >> 2) & 0x01); + if (k > 6) *cur++ = scale * ((*in >> 1) & 0x01); + } + if (img_n != out_n) { + int q; + // insert alpha = 255 + cur = a->out + stride*j; + if (img_n == 1) { + for (q=x-1; q >= 0; --q) { + cur[q*2+1] = 255; + cur[q*2+0] = cur[q]; + } + } else { + STBI_ASSERT(img_n == 3); + for (q=x-1; q >= 0; --q) { + cur[q*4+3] = 255; + cur[q*4+2] = cur[q*3+2]; + cur[q*4+1] = cur[q*3+1]; + cur[q*4+0] = cur[q*3+0]; + } + } + } + } + } else if (depth == 16) { + // force the image data from big-endian to platform-native. + // this is done in a separate pass due to the decoding relying + // on the data being untouched, but could probably be done + // per-line during decode if care is taken. + stbi_uc *cur = a->out; + stbi__uint16 *cur16 = (stbi__uint16*)cur; + + for(i=0; i < x*y*out_n; ++i,cur16++,cur+=2) { + *cur16 = (cur[0] << 8) | cur[1]; + } + } + + return 1; +} + +static int stbi__create_png_image(stbi__png *a, stbi_uc *image_data, stbi__uint32 image_data_len, int out_n, int depth, int color, int interlaced) +{ + stbi_uc *final; + int p; + if (!interlaced) + return stbi__create_png_image_raw(a, image_data, image_data_len, out_n, a->s->img_x, a->s->img_y, depth, color); + + // de-interlacing + final = (stbi_uc *) stbi__malloc(a->s->img_x * a->s->img_y * out_n); + for (p=0; p < 7; ++p) { + int xorig[] = { 0,4,0,2,0,1,0 }; + int yorig[] = { 0,0,4,0,2,0,1 }; + int xspc[] = { 8,8,4,4,2,2,1 }; + int yspc[] = { 8,8,8,4,4,2,2 }; + int i,j,x,y; + // pass1_x[4] = 0, pass1_x[5] = 1, pass1_x[12] = 1 + x = (a->s->img_x - xorig[p] + xspc[p]-1) / xspc[p]; + y = (a->s->img_y - yorig[p] + yspc[p]-1) / yspc[p]; + if (x && y) { + stbi__uint32 img_len = ((((a->s->img_n * x * depth) + 7) >> 3) + 1) * y; + if (!stbi__create_png_image_raw(a, image_data, image_data_len, out_n, x, y, depth, color)) { + STBI_FREE(final); + return 0; + } + for (j=0; j < y; ++j) { + for (i=0; i < x; ++i) { + int out_y = j*yspc[p]+yorig[p]; + int out_x = i*xspc[p]+xorig[p]; + memcpy(final + out_y*a->s->img_x*out_n + out_x*out_n, + a->out + (j*x+i)*out_n, out_n); + } + } + STBI_FREE(a->out); + image_data += img_len; + image_data_len -= img_len; + } + } + a->out = final; + + return 1; +} + +static int stbi__compute_transparency(stbi__png *z, stbi_uc tc[3], int out_n) +{ + stbi__context *s = z->s; + stbi__uint32 i, pixel_count = s->img_x * s->img_y; + stbi_uc *p = z->out; + + // compute color-based transparency, assuming we've + // already got 255 as the alpha value in the output + STBI_ASSERT(out_n == 2 || out_n == 4); + + if (out_n == 2) { + for (i=0; i < pixel_count; ++i) { + p[1] = (p[0] == tc[0] ? 0 : 255); + p += 2; + } + } else { + for (i=0; i < pixel_count; ++i) { + if (p[0] == tc[0] && p[1] == tc[1] && p[2] == tc[2]) + p[3] = 0; + p += 4; + } + } + return 1; +} + +static int stbi__compute_transparency16(stbi__png *z, stbi__uint16 tc[3], int out_n) +{ + stbi__context *s = z->s; + stbi__uint32 i, pixel_count = s->img_x * s->img_y; + stbi__uint16 *p = (stbi__uint16*) z->out; + + // compute color-based transparency, assuming we've + // already got 65535 as the alpha value in the output + STBI_ASSERT(out_n == 2 || out_n == 4); + + if (out_n == 2) { + for (i = 0; i < pixel_count; ++i) { + p[1] = (p[0] == tc[0] ? 0 : 65535); + p += 2; + } + } else { + for (i = 0; i < pixel_count; ++i) { + if (p[0] == tc[0] && p[1] == tc[1] && p[2] == tc[2]) + p[3] = 0; + p += 4; + } + } + return 1; +} + +static int stbi__expand_png_palette(stbi__png *a, stbi_uc *palette, int len, int pal_img_n) +{ + stbi__uint32 i, pixel_count = a->s->img_x * a->s->img_y; + stbi_uc *p, *temp_out, *orig = a->out; + + p = (stbi_uc *) stbi__malloc(pixel_count * pal_img_n); + if (p == NULL) return stbi__err("outofmem", "Out of memory"); + + // between here and free(out) below, exitting would leak + temp_out = p; + + if (pal_img_n == 3) { + for (i=0; i < pixel_count; ++i) { + int n = orig[i]*4; + p[0] = palette[n ]; + p[1] = palette[n+1]; + p[2] = palette[n+2]; + p += 3; + } + } else { + for (i=0; i < pixel_count; ++i) { + int n = orig[i]*4; + p[0] = palette[n ]; + p[1] = palette[n+1]; + p[2] = palette[n+2]; + p[3] = palette[n+3]; + p += 4; + } + } + STBI_FREE(a->out); + a->out = temp_out; + + STBI_NOTUSED(len); + + return 1; +} + +static int stbi__reduce_png(stbi__png *p) +{ + int i; + int img_len = p->s->img_x * p->s->img_y * p->s->img_out_n; + stbi_uc *reduced; + stbi__uint16 *orig = (stbi__uint16*)p->out; + + if (p->depth != 16) return 1; // don't need to do anything if not 16-bit data + + reduced = (stbi_uc *)stbi__malloc(img_len); + if (p == NULL) return stbi__err("outofmem", "Out of memory"); + + for (i = 0; i < img_len; ++i) reduced[i] = (stbi_uc)((orig[i] >> 8) & 0xFF); // top half of each byte is a decent approx of 16->8 bit scaling + + p->out = reduced; + STBI_FREE(orig); + + return 1; +} + +static int stbi__unpremultiply_on_load = 0; +static int stbi__de_iphone_flag = 0; + +STBIDEF void stbi_set_unpremultiply_on_load(int flag_true_if_should_unpremultiply) +{ + stbi__unpremultiply_on_load = flag_true_if_should_unpremultiply; +} + +STBIDEF void stbi_convert_iphone_png_to_rgb(int flag_true_if_should_convert) +{ + stbi__de_iphone_flag = flag_true_if_should_convert; +} + +static void stbi__de_iphone(stbi__png *z) +{ + stbi__context *s = z->s; + stbi__uint32 i, pixel_count = s->img_x * s->img_y; + stbi_uc *p = z->out; + + if (s->img_out_n == 3) { // convert bgr to rgb + for (i=0; i < pixel_count; ++i) { + stbi_uc t = p[0]; + p[0] = p[2]; + p[2] = t; + p += 3; + } + } else { + STBI_ASSERT(s->img_out_n == 4); + if (stbi__unpremultiply_on_load) { + // convert bgr to rgb and unpremultiply + for (i=0; i < pixel_count; ++i) { + stbi_uc a = p[3]; + stbi_uc t = p[0]; + if (a) { + p[0] = p[2] * 255 / a; + p[1] = p[1] * 255 / a; + p[2] = t * 255 / a; + } else { + p[0] = p[2]; + p[2] = t; + } + p += 4; + } + } else { + // convert bgr to rgb + for (i=0; i < pixel_count; ++i) { + stbi_uc t = p[0]; + p[0] = p[2]; + p[2] = t; + p += 4; + } + } + } +} + +#define STBI__PNG_TYPE(a,b,c,d) (((a) << 24) + ((b) << 16) + ((c) << 8) + (d)) + +static int stbi__parse_png_file(stbi__png *z, int scan, int req_comp) +{ + stbi_uc palette[1024], pal_img_n=0; + stbi_uc has_trans=0, tc[3]; + stbi__uint16 tc16[3]; + stbi__uint32 ioff=0, idata_limit=0, i, pal_len=0; + int first=1,k,interlace=0, color=0, is_iphone=0; + stbi__context *s = z->s; + + z->expanded = NULL; + z->idata = NULL; + z->out = NULL; + + if (!stbi__check_png_header(s)) return 0; + + if (scan == STBI__SCAN_type) return 1; + + for (;;) { + stbi__pngchunk c = stbi__get_chunk_header(s); + switch (c.type) { + case STBI__PNG_TYPE('C','g','B','I'): + is_iphone = 1; + stbi__skip(s, c.length); + break; + case STBI__PNG_TYPE('I','H','D','R'): { + int comp,filter; + if (!first) return stbi__err("multiple IHDR","Corrupt PNG"); + first = 0; + if (c.length != 13) return stbi__err("bad IHDR len","Corrupt PNG"); + s->img_x = stbi__get32be(s); if (s->img_x > (1 << 24)) return stbi__err("too large","Very large image (corrupt?)"); + s->img_y = stbi__get32be(s); if (s->img_y > (1 << 24)) return stbi__err("too large","Very large image (corrupt?)"); + z->depth = stbi__get8(s); if (z->depth != 1 && z->depth != 2 && z->depth != 4 && z->depth != 8 && z->depth != 16) return stbi__err("1/2/4/8/16-bit only","PNG not supported: 1/2/4/8/16-bit only"); + color = stbi__get8(s); if (color > 6) return stbi__err("bad ctype","Corrupt PNG"); + if (color == 3 && z->depth == 16) return stbi__err("bad ctype","Corrupt PNG"); + if (color == 3) pal_img_n = 3; else if (color & 1) return stbi__err("bad ctype","Corrupt PNG"); + comp = stbi__get8(s); if (comp) return stbi__err("bad comp method","Corrupt PNG"); + filter= stbi__get8(s); if (filter) return stbi__err("bad filter method","Corrupt PNG"); + interlace = stbi__get8(s); if (interlace>1) return stbi__err("bad interlace method","Corrupt PNG"); + if (!s->img_x || !s->img_y) return stbi__err("0-pixel image","Corrupt PNG"); + if (!pal_img_n) { + s->img_n = (color & 2 ? 3 : 1) + (color & 4 ? 1 : 0); + if ((1 << 30) / s->img_x / s->img_n < s->img_y) return stbi__err("too large", "Image too large to decode"); + if (scan == STBI__SCAN_header) return 1; + } else { + // if paletted, then pal_n is our final components, and + // img_n is # components to decompress/filter. + s->img_n = 1; + if ((1 << 30) / s->img_x / 4 < s->img_y) return stbi__err("too large","Corrupt PNG"); + // if SCAN_header, have to scan to see if we have a tRNS + } + break; + } + + case STBI__PNG_TYPE('P','L','T','E'): { + if (first) return stbi__err("first not IHDR", "Corrupt PNG"); + if (c.length > 256*3) return stbi__err("invalid PLTE","Corrupt PNG"); + pal_len = c.length / 3; + if (pal_len * 3 != c.length) return stbi__err("invalid PLTE","Corrupt PNG"); + for (i=0; i < pal_len; ++i) { + palette[i*4+0] = stbi__get8(s); + palette[i*4+1] = stbi__get8(s); + palette[i*4+2] = stbi__get8(s); + palette[i*4+3] = 255; + } + break; + } + + case STBI__PNG_TYPE('t','R','N','S'): { + if (first) return stbi__err("first not IHDR", "Corrupt PNG"); + if (z->idata) return stbi__err("tRNS after IDAT","Corrupt PNG"); + if (pal_img_n) { + if (scan == STBI__SCAN_header) { s->img_n = 4; return 1; } + if (pal_len == 0) return stbi__err("tRNS before PLTE","Corrupt PNG"); + if (c.length > pal_len) return stbi__err("bad tRNS len","Corrupt PNG"); + pal_img_n = 4; + for (i=0; i < c.length; ++i) + palette[i*4+3] = stbi__get8(s); + } else { + if (!(s->img_n & 1)) return stbi__err("tRNS with alpha","Corrupt PNG"); + if (c.length != (stbi__uint32) s->img_n*2) return stbi__err("bad tRNS len","Corrupt PNG"); + has_trans = 1; + if (z->depth == 16) { + for (k = 0; k < s->img_n; ++k) tc16[k] = stbi__get16be(s); // copy the values as-is + } else { + for (k = 0; k < s->img_n; ++k) tc[k] = (stbi_uc)(stbi__get16be(s) & 255) * stbi__depth_scale_table[z->depth]; // non 8-bit images will be larger + } + } + break; + } + + case STBI__PNG_TYPE('I','D','A','T'): { + if (first) return stbi__err("first not IHDR", "Corrupt PNG"); + if (pal_img_n && !pal_len) return stbi__err("no PLTE","Corrupt PNG"); + if (scan == STBI__SCAN_header) { s->img_n = pal_img_n; return 1; } + if ((int)(ioff + c.length) < (int)ioff) return 0; + if (ioff + c.length > idata_limit) { + stbi__uint32 idata_limit_old = idata_limit; + stbi_uc *p; + if (idata_limit == 0) idata_limit = c.length > 4096 ? c.length : 4096; + while (ioff + c.length > idata_limit) + idata_limit *= 2; + STBI_NOTUSED(idata_limit_old); + p = (stbi_uc *) STBI_REALLOC_SIZED(z->idata, idata_limit_old, idata_limit); if (p == NULL) return stbi__err("outofmem", "Out of memory"); + z->idata = p; + } + if (!stbi__getn(s, z->idata+ioff,c.length)) return stbi__err("outofdata","Corrupt PNG"); + ioff += c.length; + break; + } + + case STBI__PNG_TYPE('I','E','N','D'): { + stbi__uint32 raw_len, bpl; + if (first) return stbi__err("first not IHDR", "Corrupt PNG"); + if (scan != STBI__SCAN_load) return 1; + if (z->idata == NULL) return stbi__err("no IDAT","Corrupt PNG"); + // initial guess for decoded data size to avoid unnecessary reallocs + bpl = (s->img_x * z->depth + 7) / 8; // bytes per line, per component + raw_len = bpl * s->img_y * s->img_n /* pixels */ + s->img_y /* filter mode per row */; + z->expanded = (stbi_uc *) stbi_zlib_decode_malloc_guesssize_headerflag((char *) z->idata, ioff, raw_len, (int *) &raw_len, !is_iphone); + if (z->expanded == NULL) return 0; // zlib should set error + STBI_FREE(z->idata); z->idata = NULL; + if ((req_comp == s->img_n+1 && req_comp != 3 && !pal_img_n) || has_trans) + s->img_out_n = s->img_n+1; + else + s->img_out_n = s->img_n; + if (!stbi__create_png_image(z, z->expanded, raw_len, s->img_out_n, z->depth, color, interlace)) return 0; + if (has_trans) { + if (z->depth == 16) { + if (!stbi__compute_transparency16(z, tc16, s->img_out_n)) return 0; + } else { + if (!stbi__compute_transparency(z, tc, s->img_out_n)) return 0; + } + } + if (is_iphone && stbi__de_iphone_flag && s->img_out_n > 2) + stbi__de_iphone(z); + if (pal_img_n) { + // pal_img_n == 3 or 4 + s->img_n = pal_img_n; // record the actual colors we had + s->img_out_n = pal_img_n; + if (req_comp >= 3) s->img_out_n = req_comp; + if (!stbi__expand_png_palette(z, palette, pal_len, s->img_out_n)) + return 0; + } + STBI_FREE(z->expanded); z->expanded = NULL; + return 1; + } + + default: + // if critical, fail + if (first) return stbi__err("first not IHDR", "Corrupt PNG"); + if ((c.type & (1 << 29)) == 0) { + #ifndef STBI_NO_FAILURE_STRINGS + // not threadsafe + static char invalid_chunk[] = "XXXX PNG chunk not known"; + invalid_chunk[0] = STBI__BYTECAST(c.type >> 24); + invalid_chunk[1] = STBI__BYTECAST(c.type >> 16); + invalid_chunk[2] = STBI__BYTECAST(c.type >> 8); + invalid_chunk[3] = STBI__BYTECAST(c.type >> 0); + #endif + return stbi__err(invalid_chunk, "PNG not supported: unknown PNG chunk type"); + } + stbi__skip(s, c.length); + break; + } + // end of PNG chunk, read and skip CRC + stbi__get32be(s); + } +} + +static unsigned char *stbi__do_png(stbi__png *p, int *x, int *y, int *n, int req_comp) +{ + unsigned char *result=NULL; + if (req_comp < 0 || req_comp > 4) return stbi__errpuc("bad req_comp", "Internal error"); + if (stbi__parse_png_file(p, STBI__SCAN_load, req_comp)) { + if (p->depth == 16) { + if (!stbi__reduce_png(p)) { + return result; + } + } + result = p->out; + p->out = NULL; + if (req_comp && req_comp != p->s->img_out_n) { + result = stbi__convert_format(result, p->s->img_out_n, req_comp, p->s->img_x, p->s->img_y); + p->s->img_out_n = req_comp; + if (result == NULL) return result; + } + *x = p->s->img_x; + *y = p->s->img_y; + if (n) *n = p->s->img_n; + } + STBI_FREE(p->out); p->out = NULL; + STBI_FREE(p->expanded); p->expanded = NULL; + STBI_FREE(p->idata); p->idata = NULL; + + return result; +} + +static unsigned char *stbi__png_load(stbi__context *s, int *x, int *y, int *comp, int req_comp) +{ + stbi__png p; + p.s = s; + return stbi__do_png(&p, x,y,comp,req_comp); +} + +static int stbi__png_test(stbi__context *s) +{ + int r; + r = stbi__check_png_header(s); + stbi__rewind(s); + return r; +} + +static int stbi__png_info_raw(stbi__png *p, int *x, int *y, int *comp) +{ + if (!stbi__parse_png_file(p, STBI__SCAN_header, 0)) { + stbi__rewind( p->s ); + return 0; + } + if (x) *x = p->s->img_x; + if (y) *y = p->s->img_y; + if (comp) *comp = p->s->img_n; + return 1; +} + +static int stbi__png_info(stbi__context *s, int *x, int *y, int *comp) +{ + stbi__png p; + p.s = s; + return stbi__png_info_raw(&p, x, y, comp); +} +#endif + +// Microsoft/Windows BMP image + +#ifndef STBI_NO_BMP +static int stbi__bmp_test_raw(stbi__context *s) +{ + int r; + int sz; + if (stbi__get8(s) != 'B') return 0; + if (stbi__get8(s) != 'M') return 0; + stbi__get32le(s); // discard filesize + stbi__get16le(s); // discard reserved + stbi__get16le(s); // discard reserved + stbi__get32le(s); // discard data offset + sz = stbi__get32le(s); + r = (sz == 12 || sz == 40 || sz == 56 || sz == 108 || sz == 124); + return r; +} + +static int stbi__bmp_test(stbi__context *s) +{ + int r = stbi__bmp_test_raw(s); + stbi__rewind(s); + return r; +} + + +// returns 0..31 for the highest set bit +static int stbi__high_bit(unsigned int z) +{ + int n=0; + if (z == 0) return -1; + if (z >= 0x10000) n += 16, z >>= 16; + if (z >= 0x00100) n += 8, z >>= 8; + if (z >= 0x00010) n += 4, z >>= 4; + if (z >= 0x00004) n += 2, z >>= 2; + if (z >= 0x00002) n += 1, z >>= 1; + return n; +} + +static int stbi__bitcount(unsigned int a) +{ + a = (a & 0x55555555) + ((a >> 1) & 0x55555555); // max 2 + a = (a & 0x33333333) + ((a >> 2) & 0x33333333); // max 4 + a = (a + (a >> 4)) & 0x0f0f0f0f; // max 8 per 4, now 8 bits + a = (a + (a >> 8)); // max 16 per 8 bits + a = (a + (a >> 16)); // max 32 per 8 bits + return a & 0xff; +} + +static int stbi__shiftsigned(int v, int shift, int bits) +{ + int result; + int z=0; + + if (shift < 0) v <<= -shift; + else v >>= shift; + result = v; + + z = bits; + while (z < 8) { + result += v >> z; + z += bits; + } + return result; +} + +typedef struct +{ + int bpp, offset, hsz; + unsigned int mr,mg,mb,ma, all_a; +} stbi__bmp_data; + +static void *stbi__bmp_parse_header(stbi__context *s, stbi__bmp_data *info) +{ + int hsz; + if (stbi__get8(s) != 'B' || stbi__get8(s) != 'M') return stbi__errpuc("not BMP", "Corrupt BMP"); + stbi__get32le(s); // discard filesize + stbi__get16le(s); // discard reserved + stbi__get16le(s); // discard reserved + info->offset = stbi__get32le(s); + info->hsz = hsz = stbi__get32le(s); + info->mr = info->mg = info->mb = info->ma = 0; + + if (hsz != 12 && hsz != 40 && hsz != 56 && hsz != 108 && hsz != 124) return stbi__errpuc("unknown BMP", "BMP type not supported: unknown"); + if (hsz == 12) { + s->img_x = stbi__get16le(s); + s->img_y = stbi__get16le(s); + } else { + s->img_x = stbi__get32le(s); + s->img_y = stbi__get32le(s); + } + if (stbi__get16le(s) != 1) return stbi__errpuc("bad BMP", "bad BMP"); + info->bpp = stbi__get16le(s); + if (info->bpp == 1) return stbi__errpuc("monochrome", "BMP type not supported: 1-bit"); + if (hsz != 12) { + int compress = stbi__get32le(s); + if (compress == 1 || compress == 2) return stbi__errpuc("BMP RLE", "BMP type not supported: RLE"); + stbi__get32le(s); // discard sizeof + stbi__get32le(s); // discard hres + stbi__get32le(s); // discard vres + stbi__get32le(s); // discard colorsused + stbi__get32le(s); // discard max important + if (hsz == 40 || hsz == 56) { + if (hsz == 56) { + stbi__get32le(s); + stbi__get32le(s); + stbi__get32le(s); + stbi__get32le(s); + } + if (info->bpp == 16 || info->bpp == 32) { + if (compress == 0) { + if (info->bpp == 32) { + info->mr = 0xffu << 16; + info->mg = 0xffu << 8; + info->mb = 0xffu << 0; + info->ma = 0xffu << 24; + info->all_a = 0; // if all_a is 0 at end, then we loaded alpha channel but it was all 0 + } else { + info->mr = 31u << 10; + info->mg = 31u << 5; + info->mb = 31u << 0; + } + } else if (compress == 3) { + info->mr = stbi__get32le(s); + info->mg = stbi__get32le(s); + info->mb = stbi__get32le(s); + // not documented, but generated by photoshop and handled by mspaint + if (info->mr == info->mg && info->mg == info->mb) { + // ?!?!? + return stbi__errpuc("bad BMP", "bad BMP"); + } + } else + return stbi__errpuc("bad BMP", "bad BMP"); + } + } else { + int i; + if (hsz != 108 && hsz != 124) + return stbi__errpuc("bad BMP", "bad BMP"); + info->mr = stbi__get32le(s); + info->mg = stbi__get32le(s); + info->mb = stbi__get32le(s); + info->ma = stbi__get32le(s); + stbi__get32le(s); // discard color space + for (i=0; i < 12; ++i) + stbi__get32le(s); // discard color space parameters + if (hsz == 124) { + stbi__get32le(s); // discard rendering intent + stbi__get32le(s); // discard offset of profile data + stbi__get32le(s); // discard size of profile data + stbi__get32le(s); // discard reserved + } + } + } + return (void *) 1; +} + + +static stbi_uc *stbi__bmp_load(stbi__context *s, int *x, int *y, int *comp, int req_comp) +{ + stbi_uc *out; + unsigned int mr=0,mg=0,mb=0,ma=0, all_a; + stbi_uc pal[256][4]; + int psize=0,i,j,width; + int flip_vertically, pad, target; + stbi__bmp_data info; + + info.all_a = 255; + if (stbi__bmp_parse_header(s, &info) == NULL) + return NULL; // error code already set + + flip_vertically = ((int) s->img_y) > 0; + s->img_y = abs((int) s->img_y); + + mr = info.mr; + mg = info.mg; + mb = info.mb; + ma = info.ma; + all_a = info.all_a; + + if (info.hsz == 12) { + if (info.bpp < 24) + psize = (info.offset - 14 - 24) / 3; + } else { + if (info.bpp < 16) + psize = (info.offset - 14 - info.hsz) >> 2; + } + + s->img_n = ma ? 4 : 3; + if (req_comp && req_comp >= 3) // we can directly decode 3 or 4 + target = req_comp; + else + target = s->img_n; // if they want monochrome, we'll post-convert + + out = (stbi_uc *) stbi__malloc(target * s->img_x * s->img_y); + if (!out) return stbi__errpuc("outofmem", "Out of memory"); + if (info.bpp < 16) { + int z=0; + if (psize == 0 || psize > 256) { STBI_FREE(out); return stbi__errpuc("invalid", "Corrupt BMP"); } + for (i=0; i < psize; ++i) { + pal[i][2] = stbi__get8(s); + pal[i][1] = stbi__get8(s); + pal[i][0] = stbi__get8(s); + if (info.hsz != 12) stbi__get8(s); + pal[i][3] = 255; + } + stbi__skip(s, info.offset - 14 - info.hsz - psize * (info.hsz == 12 ? 3 : 4)); + if (info.bpp == 4) width = (s->img_x + 1) >> 1; + else if (info.bpp == 8) width = s->img_x; + else { STBI_FREE(out); return stbi__errpuc("bad bpp", "Corrupt BMP"); } + pad = (-width)&3; + for (j=0; j < (int) s->img_y; ++j) { + for (i=0; i < (int) s->img_x; i += 2) { + int v=stbi__get8(s),v2=0; + if (info.bpp == 4) { + v2 = v & 15; + v >>= 4; + } + out[z++] = pal[v][0]; + out[z++] = pal[v][1]; + out[z++] = pal[v][2]; + if (target == 4) out[z++] = 255; + if (i+1 == (int) s->img_x) break; + v = (info.bpp == 8) ? stbi__get8(s) : v2; + out[z++] = pal[v][0]; + out[z++] = pal[v][1]; + out[z++] = pal[v][2]; + if (target == 4) out[z++] = 255; + } + stbi__skip(s, pad); + } + } else { + int rshift=0,gshift=0,bshift=0,ashift=0,rcount=0,gcount=0,bcount=0,acount=0; + int z = 0; + int easy=0; + stbi__skip(s, info.offset - 14 - info.hsz); + if (info.bpp == 24) width = 3 * s->img_x; + else if (info.bpp == 16) width = 2*s->img_x; + else /* bpp = 32 and pad = 0 */ width=0; + pad = (-width) & 3; + if (info.bpp == 24) { + easy = 1; + } else if (info.bpp == 32) { + if (mb == 0xff && mg == 0xff00 && mr == 0x00ff0000 && ma == 0xff000000) + easy = 2; + } + if (!easy) { + if (!mr || !mg || !mb) { STBI_FREE(out); return stbi__errpuc("bad masks", "Corrupt BMP"); } + // right shift amt to put high bit in position #7 + rshift = stbi__high_bit(mr)-7; rcount = stbi__bitcount(mr); + gshift = stbi__high_bit(mg)-7; gcount = stbi__bitcount(mg); + bshift = stbi__high_bit(mb)-7; bcount = stbi__bitcount(mb); + ashift = stbi__high_bit(ma)-7; acount = stbi__bitcount(ma); + } + for (j=0; j < (int) s->img_y; ++j) { + if (easy) { + for (i=0; i < (int) s->img_x; ++i) { + unsigned char a; + out[z+2] = stbi__get8(s); + out[z+1] = stbi__get8(s); + out[z+0] = stbi__get8(s); + z += 3; + a = (easy == 2 ? stbi__get8(s) : 255); + all_a |= a; + if (target == 4) out[z++] = a; + } + } else { + int bpp = info.bpp; + for (i=0; i < (int) s->img_x; ++i) { + stbi__uint32 v = (bpp == 16 ? (stbi__uint32) stbi__get16le(s) : stbi__get32le(s)); + int a; + out[z++] = STBI__BYTECAST(stbi__shiftsigned(v & mr, rshift, rcount)); + out[z++] = STBI__BYTECAST(stbi__shiftsigned(v & mg, gshift, gcount)); + out[z++] = STBI__BYTECAST(stbi__shiftsigned(v & mb, bshift, bcount)); + a = (ma ? stbi__shiftsigned(v & ma, ashift, acount) : 255); + all_a |= a; + if (target == 4) out[z++] = STBI__BYTECAST(a); + } + } + stbi__skip(s, pad); + } + } + + // if alpha channel is all 0s, replace with all 255s + if (target == 4 && all_a == 0) + for (i=4*s->img_x*s->img_y-1; i >= 0; i -= 4) + out[i] = 255; + + if (flip_vertically) { + stbi_uc t; + for (j=0; j < (int) s->img_y>>1; ++j) { + stbi_uc *p1 = out + j *s->img_x*target; + stbi_uc *p2 = out + (s->img_y-1-j)*s->img_x*target; + for (i=0; i < (int) s->img_x*target; ++i) { + t = p1[i], p1[i] = p2[i], p2[i] = t; + } + } + } + + if (req_comp && req_comp != target) { + out = stbi__convert_format(out, target, req_comp, s->img_x, s->img_y); + if (out == NULL) return out; // stbi__convert_format frees input on failure + } + + *x = s->img_x; + *y = s->img_y; + if (comp) *comp = s->img_n; + return out; +} +#endif + +// Targa Truevision - TGA +// by Jonathan Dummer +#ifndef STBI_NO_TGA +// returns STBI_rgb or whatever, 0 on error +static int stbi__tga_get_comp(int bits_per_pixel, int is_grey, int* is_rgb16) +{ + // only RGB or RGBA (incl. 16bit) or grey allowed + if(is_rgb16) *is_rgb16 = 0; + switch(bits_per_pixel) { + case 8: return STBI_grey; + case 16: if(is_grey) return STBI_grey_alpha; + // else: fall-through + case 15: if(is_rgb16) *is_rgb16 = 1; + return STBI_rgb; + case 24: // fall-through + case 32: return bits_per_pixel/8; + default: return 0; + } +} + +static int stbi__tga_info(stbi__context *s, int *x, int *y, int *comp) +{ + int tga_w, tga_h, tga_comp, tga_image_type, tga_bits_per_pixel, tga_colormap_bpp; + int sz, tga_colormap_type; + stbi__get8(s); // discard Offset + tga_colormap_type = stbi__get8(s); // colormap type + if( tga_colormap_type > 1 ) { + stbi__rewind(s); + return 0; // only RGB or indexed allowed + } + tga_image_type = stbi__get8(s); // image type + if ( tga_colormap_type == 1 ) { // colormapped (paletted) image + if (tga_image_type != 1 && tga_image_type != 9) { + stbi__rewind(s); + return 0; + } + stbi__skip(s,4); // skip index of first colormap entry and number of entries + sz = stbi__get8(s); // check bits per palette color entry + if ( (sz != 8) && (sz != 15) && (sz != 16) && (sz != 24) && (sz != 32) ) { + stbi__rewind(s); + return 0; + } + stbi__skip(s,4); // skip image x and y origin + tga_colormap_bpp = sz; + } else { // "normal" image w/o colormap - only RGB or grey allowed, +/- RLE + if ( (tga_image_type != 2) && (tga_image_type != 3) && (tga_image_type != 10) && (tga_image_type != 11) ) { + stbi__rewind(s); + return 0; // only RGB or grey allowed, +/- RLE + } + stbi__skip(s,9); // skip colormap specification and image x/y origin + tga_colormap_bpp = 0; + } + tga_w = stbi__get16le(s); + if( tga_w < 1 ) { + stbi__rewind(s); + return 0; // test width + } + tga_h = stbi__get16le(s); + if( tga_h < 1 ) { + stbi__rewind(s); + return 0; // test height + } + tga_bits_per_pixel = stbi__get8(s); // bits per pixel + stbi__get8(s); // ignore alpha bits + if (tga_colormap_bpp != 0) { + if((tga_bits_per_pixel != 8) && (tga_bits_per_pixel != 16)) { + // when using a colormap, tga_bits_per_pixel is the size of the indexes + // I don't think anything but 8 or 16bit indexes makes sense + stbi__rewind(s); + return 0; + } + tga_comp = stbi__tga_get_comp(tga_colormap_bpp, 0, NULL); + } else { + tga_comp = stbi__tga_get_comp(tga_bits_per_pixel, (tga_image_type == 3) || (tga_image_type == 11), NULL); + } + if(!tga_comp) { + stbi__rewind(s); + return 0; + } + if (x) *x = tga_w; + if (y) *y = tga_h; + if (comp) *comp = tga_comp; + return 1; // seems to have passed everything +} + +static int stbi__tga_test(stbi__context *s) +{ + int res = 0; + int sz, tga_color_type; + stbi__get8(s); // discard Offset + tga_color_type = stbi__get8(s); // color type + if ( tga_color_type > 1 ) goto errorEnd; // only RGB or indexed allowed + sz = stbi__get8(s); // image type + if ( tga_color_type == 1 ) { // colormapped (paletted) image + if (sz != 1 && sz != 9) goto errorEnd; // colortype 1 demands image type 1 or 9 + stbi__skip(s,4); // skip index of first colormap entry and number of entries + sz = stbi__get8(s); // check bits per palette color entry + if ( (sz != 8) && (sz != 15) && (sz != 16) && (sz != 24) && (sz != 32) ) goto errorEnd; + stbi__skip(s,4); // skip image x and y origin + } else { // "normal" image w/o colormap + if ( (sz != 2) && (sz != 3) && (sz != 10) && (sz != 11) ) goto errorEnd; // only RGB or grey allowed, +/- RLE + stbi__skip(s,9); // skip colormap specification and image x/y origin + } + if ( stbi__get16le(s) < 1 ) goto errorEnd; // test width + if ( stbi__get16le(s) < 1 ) goto errorEnd; // test height + sz = stbi__get8(s); // bits per pixel + if ( (tga_color_type == 1) && (sz != 8) && (sz != 16) ) goto errorEnd; // for colormapped images, bpp is size of an index + if ( (sz != 8) && (sz != 15) && (sz != 16) && (sz != 24) && (sz != 32) ) goto errorEnd; + + res = 1; // if we got this far, everything's good and we can return 1 instead of 0 + +errorEnd: + stbi__rewind(s); + return res; +} + +// read 16bit value and convert to 24bit RGB +void stbi__tga_read_rgb16(stbi__context *s, stbi_uc* out) +{ + stbi__uint16 px = stbi__get16le(s); + stbi__uint16 fiveBitMask = 31; + // we have 3 channels with 5bits each + int r = (px >> 10) & fiveBitMask; + int g = (px >> 5) & fiveBitMask; + int b = px & fiveBitMask; + // Note that this saves the data in RGB(A) order, so it doesn't need to be swapped later + out[0] = (r * 255)/31; + out[1] = (g * 255)/31; + out[2] = (b * 255)/31; + + // some people claim that the most significant bit might be used for alpha + // (possibly if an alpha-bit is set in the "image descriptor byte") + // but that only made 16bit test images completely translucent.. + // so let's treat all 15 and 16bit TGAs as RGB with no alpha. +} + +static stbi_uc *stbi__tga_load(stbi__context *s, int *x, int *y, int *comp, int req_comp) +{ + // read in the TGA header stuff + int tga_offset = stbi__get8(s); + int tga_indexed = stbi__get8(s); + int tga_image_type = stbi__get8(s); + int tga_is_RLE = 0; + int tga_palette_start = stbi__get16le(s); + int tga_palette_len = stbi__get16le(s); + int tga_palette_bits = stbi__get8(s); + int tga_x_origin = stbi__get16le(s); + int tga_y_origin = stbi__get16le(s); + int tga_width = stbi__get16le(s); + int tga_height = stbi__get16le(s); + int tga_bits_per_pixel = stbi__get8(s); + int tga_comp, tga_rgb16=0; + int tga_inverted = stbi__get8(s); + // int tga_alpha_bits = tga_inverted & 15; // the 4 lowest bits - unused (useless?) + // image data + unsigned char *tga_data; + unsigned char *tga_palette = NULL; + int i, j; + unsigned char raw_data[4]; + int RLE_count = 0; + int RLE_repeating = 0; + int read_next_pixel = 1; + + // do a tiny bit of precessing + if ( tga_image_type >= 8 ) + { + tga_image_type -= 8; + tga_is_RLE = 1; + } + tga_inverted = 1 - ((tga_inverted >> 5) & 1); + + // If I'm paletted, then I'll use the number of bits from the palette + if ( tga_indexed ) tga_comp = stbi__tga_get_comp(tga_palette_bits, 0, &tga_rgb16); + else tga_comp = stbi__tga_get_comp(tga_bits_per_pixel, (tga_image_type == 3), &tga_rgb16); + + if(!tga_comp) // shouldn't really happen, stbi__tga_test() should have ensured basic consistency + return stbi__errpuc("bad format", "Can't find out TGA pixelformat"); + + // tga info + *x = tga_width; + *y = tga_height; + if (comp) *comp = tga_comp; + + tga_data = (unsigned char*)stbi__malloc( (size_t)tga_width * tga_height * tga_comp ); + if (!tga_data) return stbi__errpuc("outofmem", "Out of memory"); + + // skip to the data's starting position (offset usually = 0) + stbi__skip(s, tga_offset ); + + if ( !tga_indexed && !tga_is_RLE && !tga_rgb16 ) { + for (i=0; i < tga_height; ++i) { + int row = tga_inverted ? tga_height -i - 1 : i; + stbi_uc *tga_row = tga_data + row*tga_width*tga_comp; + stbi__getn(s, tga_row, tga_width * tga_comp); + } + } else { + // do I need to load a palette? + if ( tga_indexed) + { + // any data to skip? (offset usually = 0) + stbi__skip(s, tga_palette_start ); + // load the palette + tga_palette = (unsigned char*)stbi__malloc( tga_palette_len * tga_comp ); + if (!tga_palette) { + STBI_FREE(tga_data); + return stbi__errpuc("outofmem", "Out of memory"); + } + if (tga_rgb16) { + stbi_uc *pal_entry = tga_palette; + STBI_ASSERT(tga_comp == STBI_rgb); + for (i=0; i < tga_palette_len; ++i) { + stbi__tga_read_rgb16(s, pal_entry); + pal_entry += tga_comp; + } + } else if (!stbi__getn(s, tga_palette, tga_palette_len * tga_comp)) { + STBI_FREE(tga_data); + STBI_FREE(tga_palette); + return stbi__errpuc("bad palette", "Corrupt TGA"); + } + } + // load the data + for (i=0; i < tga_width * tga_height; ++i) + { + // if I'm in RLE mode, do I need to get a RLE stbi__pngchunk? + if ( tga_is_RLE ) + { + if ( RLE_count == 0 ) + { + // yep, get the next byte as a RLE command + int RLE_cmd = stbi__get8(s); + RLE_count = 1 + (RLE_cmd & 127); + RLE_repeating = RLE_cmd >> 7; + read_next_pixel = 1; + } else if ( !RLE_repeating ) + { + read_next_pixel = 1; + } + } else + { + read_next_pixel = 1; + } + // OK, if I need to read a pixel, do it now + if ( read_next_pixel ) + { + // load however much data we did have + if ( tga_indexed ) + { + // read in index, then perform the lookup + int pal_idx = (tga_bits_per_pixel == 8) ? stbi__get8(s) : stbi__get16le(s); + if ( pal_idx >= tga_palette_len ) { + // invalid index + pal_idx = 0; + } + pal_idx *= tga_comp; + for (j = 0; j < tga_comp; ++j) { + raw_data[j] = tga_palette[pal_idx+j]; + } + } else if(tga_rgb16) { + STBI_ASSERT(tga_comp == STBI_rgb); + stbi__tga_read_rgb16(s, raw_data); + } else { + // read in the data raw + for (j = 0; j < tga_comp; ++j) { + raw_data[j] = stbi__get8(s); + } + } + // clear the reading flag for the next pixel + read_next_pixel = 0; + } // end of reading a pixel + + // copy data + for (j = 0; j < tga_comp; ++j) + tga_data[i*tga_comp+j] = raw_data[j]; + + // in case we're in RLE mode, keep counting down + --RLE_count; + } + // do I need to invert the image? + if ( tga_inverted ) + { + for (j = 0; j*2 < tga_height; ++j) + { + int index1 = j * tga_width * tga_comp; + int index2 = (tga_height - 1 - j) * tga_width * tga_comp; + for (i = tga_width * tga_comp; i > 0; --i) + { + unsigned char temp = tga_data[index1]; + tga_data[index1] = tga_data[index2]; + tga_data[index2] = temp; + ++index1; + ++index2; + } + } + } + // clear my palette, if I had one + if ( tga_palette != NULL ) + { + STBI_FREE( tga_palette ); + } + } + + // swap RGB - if the source data was RGB16, it already is in the right order + if (tga_comp >= 3 && !tga_rgb16) + { + unsigned char* tga_pixel = tga_data; + for (i=0; i < tga_width * tga_height; ++i) + { + unsigned char temp = tga_pixel[0]; + tga_pixel[0] = tga_pixel[2]; + tga_pixel[2] = temp; + tga_pixel += tga_comp; + } + } + + // convert to target component count + if (req_comp && req_comp != tga_comp) + tga_data = stbi__convert_format(tga_data, tga_comp, req_comp, tga_width, tga_height); + + // the things I do to get rid of an error message, and yet keep + // Microsoft's C compilers happy... [8^( + tga_palette_start = tga_palette_len = tga_palette_bits = + tga_x_origin = tga_y_origin = 0; + // OK, done + return tga_data; +} +#endif + +// ************************************************************************************************* +// Photoshop PSD loader -- PD by Thatcher Ulrich, integration by Nicolas Schulz, tweaked by STB + +#ifndef STBI_NO_PSD +static int stbi__psd_test(stbi__context *s) +{ + int r = (stbi__get32be(s) == 0x38425053); + stbi__rewind(s); + return r; +} + +static stbi_uc *stbi__psd_load(stbi__context *s, int *x, int *y, int *comp, int req_comp) +{ + int pixelCount; + int channelCount, compression; + int channel, i, count, len; + int bitdepth; + int w,h; + stbi_uc *out; + + // Check identifier + if (stbi__get32be(s) != 0x38425053) // "8BPS" + return stbi__errpuc("not PSD", "Corrupt PSD image"); + + // Check file type version. + if (stbi__get16be(s) != 1) + return stbi__errpuc("wrong version", "Unsupported version of PSD image"); + + // Skip 6 reserved bytes. + stbi__skip(s, 6 ); + + // Read the number of channels (R, G, B, A, etc). + channelCount = stbi__get16be(s); + if (channelCount < 0 || channelCount > 16) + return stbi__errpuc("wrong channel count", "Unsupported number of channels in PSD image"); + + // Read the rows and columns of the image. + h = stbi__get32be(s); + w = stbi__get32be(s); + + // Make sure the depth is 8 bits. + bitdepth = stbi__get16be(s); + if (bitdepth != 8 && bitdepth != 16) + return stbi__errpuc("unsupported bit depth", "PSD bit depth is not 8 or 16 bit"); + + // Make sure the color mode is RGB. + // Valid options are: + // 0: Bitmap + // 1: Grayscale + // 2: Indexed color + // 3: RGB color + // 4: CMYK color + // 7: Multichannel + // 8: Duotone + // 9: Lab color + if (stbi__get16be(s) != 3) + return stbi__errpuc("wrong color format", "PSD is not in RGB color format"); + + // Skip the Mode Data. (It's the palette for indexed color; other info for other modes.) + stbi__skip(s,stbi__get32be(s) ); + + // Skip the image resources. (resolution, pen tool paths, etc) + stbi__skip(s, stbi__get32be(s) ); + + // Skip the reserved data. + stbi__skip(s, stbi__get32be(s) ); + + // Find out if the data is compressed. + // Known values: + // 0: no compression + // 1: RLE compressed + compression = stbi__get16be(s); + if (compression > 1) + return stbi__errpuc("bad compression", "PSD has an unknown compression format"); + + // Create the destination image. + out = (stbi_uc *) stbi__malloc(4 * w*h); + if (!out) return stbi__errpuc("outofmem", "Out of memory"); + pixelCount = w*h; + + // Initialize the data to zero. + //memset( out, 0, pixelCount * 4 ); + + // Finally, the image data. + if (compression) { + // RLE as used by .PSD and .TIFF + // Loop until you get the number of unpacked bytes you are expecting: + // Read the next source byte into n. + // If n is between 0 and 127 inclusive, copy the next n+1 bytes literally. + // Else if n is between -127 and -1 inclusive, copy the next byte -n+1 times. + // Else if n is 128, noop. + // Endloop + + // The RLE-compressed data is preceeded by a 2-byte data count for each row in the data, + // which we're going to just skip. + stbi__skip(s, h * channelCount * 2 ); + + // Read the RLE data by channel. + for (channel = 0; channel < 4; channel++) { + stbi_uc *p; + + p = out+channel; + if (channel >= channelCount) { + // Fill this channel with default data. + for (i = 0; i < pixelCount; i++, p += 4) + *p = (channel == 3 ? 255 : 0); + } else { + // Read the RLE data. + count = 0; + while (count < pixelCount) { + len = stbi__get8(s); + if (len == 128) { + // No-op. + } else if (len < 128) { + // Copy next len+1 bytes literally. + len++; + count += len; + while (len) { + *p = stbi__get8(s); + p += 4; + len--; + } + } else if (len > 128) { + stbi_uc val; + // Next -len+1 bytes in the dest are replicated from next source byte. + // (Interpret len as a negative 8-bit int.) + len ^= 0x0FF; + len += 2; + val = stbi__get8(s); + count += len; + while (len) { + *p = val; + p += 4; + len--; + } + } + } + } + } + + } else { + // We're at the raw image data. It's each channel in order (Red, Green, Blue, Alpha, ...) + // where each channel consists of an 8-bit value for each pixel in the image. + + // Read the data by channel. + for (channel = 0; channel < 4; channel++) { + stbi_uc *p; + + p = out + channel; + if (channel >= channelCount) { + // Fill this channel with default data. + stbi_uc val = channel == 3 ? 255 : 0; + for (i = 0; i < pixelCount; i++, p += 4) + *p = val; + } else { + // Read the data. + if (bitdepth == 16) { + for (i = 0; i < pixelCount; i++, p += 4) + *p = (stbi_uc) (stbi__get16be(s) >> 8); + } else { + for (i = 0; i < pixelCount; i++, p += 4) + *p = stbi__get8(s); + } + } + } + } + + if (channelCount >= 4) { + for (i=0; i < w*h; ++i) { + unsigned char *pixel = out + 4*i; + if (pixel[3] != 0 && pixel[3] != 255) { + // remove weird white matte from PSD + float a = pixel[3] / 255.0f; + float ra = 1.0f / a; + float inv_a = 255.0f * (1 - ra); + pixel[0] = (unsigned char) (pixel[0]*ra + inv_a); + pixel[1] = (unsigned char) (pixel[1]*ra + inv_a); + pixel[2] = (unsigned char) (pixel[2]*ra + inv_a); + } + } + } + + if (req_comp && req_comp != 4) { + out = stbi__convert_format(out, 4, req_comp, w, h); + if (out == NULL) return out; // stbi__convert_format frees input on failure + } + + if (comp) *comp = 4; + *y = h; + *x = w; + + return out; +} +#endif + +// ************************************************************************************************* +// Softimage PIC loader +// by Tom Seddon +// +// See http://softimage.wiki.softimage.com/index.php/INFO:_PIC_file_format +// See http://ozviz.wasp.uwa.edu.au/~pbourke/dataformats/softimagepic/ + +#ifndef STBI_NO_PIC +static int stbi__pic_is4(stbi__context *s,const char *str) +{ + int i; + for (i=0; i<4; ++i) + if (stbi__get8(s) != (stbi_uc)str[i]) + return 0; + + return 1; +} + +static int stbi__pic_test_core(stbi__context *s) +{ + int i; + + if (!stbi__pic_is4(s,"\x53\x80\xF6\x34")) + return 0; + + for(i=0;i<84;++i) + stbi__get8(s); + + if (!stbi__pic_is4(s,"PICT")) + return 0; + + return 1; +} + +typedef struct +{ + stbi_uc size,type,channel; +} stbi__pic_packet; + +static stbi_uc *stbi__readval(stbi__context *s, int channel, stbi_uc *dest) +{ + int mask=0x80, i; + + for (i=0; i<4; ++i, mask>>=1) { + if (channel & mask) { + if (stbi__at_eof(s)) return stbi__errpuc("bad file","PIC file too short"); + dest[i]=stbi__get8(s); + } + } + + return dest; +} + +static void stbi__copyval(int channel,stbi_uc *dest,const stbi_uc *src) +{ + int mask=0x80,i; + + for (i=0;i<4; ++i, mask>>=1) + if (channel&mask) + dest[i]=src[i]; +} + +static stbi_uc *stbi__pic_load_core(stbi__context *s,int width,int height,int *comp, stbi_uc *result) +{ + int act_comp=0,num_packets=0,y,chained; + stbi__pic_packet packets[10]; + + // this will (should...) cater for even some bizarre stuff like having data + // for the same channel in multiple packets. + do { + stbi__pic_packet *packet; + + if (num_packets==sizeof(packets)/sizeof(packets[0])) + return stbi__errpuc("bad format","too many packets"); + + packet = &packets[num_packets++]; + + chained = stbi__get8(s); + packet->size = stbi__get8(s); + packet->type = stbi__get8(s); + packet->channel = stbi__get8(s); + + act_comp |= packet->channel; + + if (stbi__at_eof(s)) return stbi__errpuc("bad file","file too short (reading packets)"); + if (packet->size != 8) return stbi__errpuc("bad format","packet isn't 8bpp"); + } while (chained); + + *comp = (act_comp & 0x10 ? 4 : 3); // has alpha channel? + + for(y=0; ytype) { + default: + return stbi__errpuc("bad format","packet has bad compression type"); + + case 0: {//uncompressed + int x; + + for(x=0;xchannel,dest)) + return 0; + break; + } + + case 1://Pure RLE + { + int left=width, i; + + while (left>0) { + stbi_uc count,value[4]; + + count=stbi__get8(s); + if (stbi__at_eof(s)) return stbi__errpuc("bad file","file too short (pure read count)"); + + if (count > left) + count = (stbi_uc) left; + + if (!stbi__readval(s,packet->channel,value)) return 0; + + for(i=0; ichannel,dest,value); + left -= count; + } + } + break; + + case 2: {//Mixed RLE + int left=width; + while (left>0) { + int count = stbi__get8(s), i; + if (stbi__at_eof(s)) return stbi__errpuc("bad file","file too short (mixed read count)"); + + if (count >= 128) { // Repeated + stbi_uc value[4]; + + if (count==128) + count = stbi__get16be(s); + else + count -= 127; + if (count > left) + return stbi__errpuc("bad file","scanline overrun"); + + if (!stbi__readval(s,packet->channel,value)) + return 0; + + for(i=0;ichannel,dest,value); + } else { // Raw + ++count; + if (count>left) return stbi__errpuc("bad file","scanline overrun"); + + for(i=0;ichannel,dest)) + return 0; + } + left-=count; + } + break; + } + } + } + } + + return result; +} + +static stbi_uc *stbi__pic_load(stbi__context *s,int *px,int *py,int *comp,int req_comp) +{ + stbi_uc *result; + int i, x,y; + + for (i=0; i<92; ++i) + stbi__get8(s); + + x = stbi__get16be(s); + y = stbi__get16be(s); + if (stbi__at_eof(s)) return stbi__errpuc("bad file","file too short (pic header)"); + if ((1 << 28) / x < y) return stbi__errpuc("too large", "Image too large to decode"); + + stbi__get32be(s); //skip `ratio' + stbi__get16be(s); //skip `fields' + stbi__get16be(s); //skip `pad' + + // intermediate buffer is RGBA + result = (stbi_uc *) stbi__malloc(x*y*4); + memset(result, 0xff, x*y*4); + + if (!stbi__pic_load_core(s,x,y,comp, result)) { + STBI_FREE(result); + result=0; + } + *px = x; + *py = y; + if (req_comp == 0) req_comp = *comp; + result=stbi__convert_format(result,4,req_comp,x,y); + + return result; +} + +static int stbi__pic_test(stbi__context *s) +{ + int r = stbi__pic_test_core(s); + stbi__rewind(s); + return r; +} +#endif + +// ************************************************************************************************* +// GIF loader -- public domain by Jean-Marc Lienher -- simplified/shrunk by stb + +#ifndef STBI_NO_GIF +typedef struct +{ + stbi__int16 prefix; + stbi_uc first; + stbi_uc suffix; +} stbi__gif_lzw; + +typedef struct +{ + int w,h; + stbi_uc *out, *old_out; // output buffer (always 4 components) + int flags, bgindex, ratio, transparent, eflags, delay; + stbi_uc pal[256][4]; + stbi_uc lpal[256][4]; + stbi__gif_lzw codes[4096]; + stbi_uc *color_table; + int parse, step; + int lflags; + int start_x, start_y; + int max_x, max_y; + int cur_x, cur_y; + int line_size; +} stbi__gif; + +static int stbi__gif_test_raw(stbi__context *s) +{ + int sz; + if (stbi__get8(s) != 'G' || stbi__get8(s) != 'I' || stbi__get8(s) != 'F' || stbi__get8(s) != '8') return 0; + sz = stbi__get8(s); + if (sz != '9' && sz != '7') return 0; + if (stbi__get8(s) != 'a') return 0; + return 1; +} + +static int stbi__gif_test(stbi__context *s) +{ + int r = stbi__gif_test_raw(s); + stbi__rewind(s); + return r; +} + +static void stbi__gif_parse_colortable(stbi__context *s, stbi_uc pal[256][4], int num_entries, int transp) +{ + int i; + for (i=0; i < num_entries; ++i) { + pal[i][2] = stbi__get8(s); + pal[i][1] = stbi__get8(s); + pal[i][0] = stbi__get8(s); + pal[i][3] = transp == i ? 0 : 255; + } +} + +static int stbi__gif_header(stbi__context *s, stbi__gif *g, int *comp, int is_info) +{ + stbi_uc version; + if (stbi__get8(s) != 'G' || stbi__get8(s) != 'I' || stbi__get8(s) != 'F' || stbi__get8(s) != '8') + return stbi__err("not GIF", "Corrupt GIF"); + + version = stbi__get8(s); + if (version != '7' && version != '9') return stbi__err("not GIF", "Corrupt GIF"); + if (stbi__get8(s) != 'a') return stbi__err("not GIF", "Corrupt GIF"); + + stbi__g_failure_reason = ""; + g->w = stbi__get16le(s); + g->h = stbi__get16le(s); + g->flags = stbi__get8(s); + g->bgindex = stbi__get8(s); + g->ratio = stbi__get8(s); + g->transparent = -1; + + if (comp != 0) *comp = 4; // can't actually tell whether it's 3 or 4 until we parse the comments + + if (is_info) return 1; + + if (g->flags & 0x80) + stbi__gif_parse_colortable(s,g->pal, 2 << (g->flags & 7), -1); + + return 1; +} + +static int stbi__gif_info_raw(stbi__context *s, int *x, int *y, int *comp) +{ + stbi__gif* g = (stbi__gif*) stbi__malloc(sizeof(stbi__gif)); + if (!stbi__gif_header(s, g, comp, 1)) { + STBI_FREE(g); + stbi__rewind( s ); + return 0; + } + if (x) *x = g->w; + if (y) *y = g->h; + STBI_FREE(g); + return 1; +} + +static void stbi__out_gif_code(stbi__gif *g, stbi__uint16 code) +{ + stbi_uc *p, *c; + + // recurse to decode the prefixes, since the linked-list is backwards, + // and working backwards through an interleaved image would be nasty + if (g->codes[code].prefix >= 0) + stbi__out_gif_code(g, g->codes[code].prefix); + + if (g->cur_y >= g->max_y) return; + + p = &g->out[g->cur_x + g->cur_y]; + c = &g->color_table[g->codes[code].suffix * 4]; + + if (c[3] >= 128) { + p[0] = c[2]; + p[1] = c[1]; + p[2] = c[0]; + p[3] = c[3]; + } + g->cur_x += 4; + + if (g->cur_x >= g->max_x) { + g->cur_x = g->start_x; + g->cur_y += g->step; + + while (g->cur_y >= g->max_y && g->parse > 0) { + g->step = (1 << g->parse) * g->line_size; + g->cur_y = g->start_y + (g->step >> 1); + --g->parse; + } + } +} + +static stbi_uc *stbi__process_gif_raster(stbi__context *s, stbi__gif *g) +{ + stbi_uc lzw_cs; + stbi__int32 len, init_code; + stbi__uint32 first; + stbi__int32 codesize, codemask, avail, oldcode, bits, valid_bits, clear; + stbi__gif_lzw *p; + + lzw_cs = stbi__get8(s); + if (lzw_cs > 12) return NULL; + clear = 1 << lzw_cs; + first = 1; + codesize = lzw_cs + 1; + codemask = (1 << codesize) - 1; + bits = 0; + valid_bits = 0; + for (init_code = 0; init_code < clear; init_code++) { + g->codes[init_code].prefix = -1; + g->codes[init_code].first = (stbi_uc) init_code; + g->codes[init_code].suffix = (stbi_uc) init_code; + } + + // support no starting clear code + avail = clear+2; + oldcode = -1; + + len = 0; + for(;;) { + if (valid_bits < codesize) { + if (len == 0) { + len = stbi__get8(s); // start new block + if (len == 0) + return g->out; + } + --len; + bits |= (stbi__int32) stbi__get8(s) << valid_bits; + valid_bits += 8; + } else { + stbi__int32 code = bits & codemask; + bits >>= codesize; + valid_bits -= codesize; + // @OPTIMIZE: is there some way we can accelerate the non-clear path? + if (code == clear) { // clear code + codesize = lzw_cs + 1; + codemask = (1 << codesize) - 1; + avail = clear + 2; + oldcode = -1; + first = 0; + } else if (code == clear + 1) { // end of stream code + stbi__skip(s, len); + while ((len = stbi__get8(s)) > 0) + stbi__skip(s,len); + return g->out; + } else if (code <= avail) { + if (first) return stbi__errpuc("no clear code", "Corrupt GIF"); + + if (oldcode >= 0) { + p = &g->codes[avail++]; + if (avail > 4096) return stbi__errpuc("too many codes", "Corrupt GIF"); + p->prefix = (stbi__int16) oldcode; + p->first = g->codes[oldcode].first; + p->suffix = (code == avail) ? p->first : g->codes[code].first; + } else if (code == avail) + return stbi__errpuc("illegal code in raster", "Corrupt GIF"); + + stbi__out_gif_code(g, (stbi__uint16) code); + + if ((avail & codemask) == 0 && avail <= 0x0FFF) { + codesize++; + codemask = (1 << codesize) - 1; + } + + oldcode = code; + } else { + return stbi__errpuc("illegal code in raster", "Corrupt GIF"); + } + } + } +} + +static void stbi__fill_gif_background(stbi__gif *g, int x0, int y0, int x1, int y1) +{ + int x, y; + stbi_uc *c = g->pal[g->bgindex]; + for (y = y0; y < y1; y += 4 * g->w) { + for (x = x0; x < x1; x += 4) { + stbi_uc *p = &g->out[y + x]; + p[0] = c[2]; + p[1] = c[1]; + p[2] = c[0]; + p[3] = 0; + } + } +} + +// this function is designed to support animated gifs, although stb_image doesn't support it +static stbi_uc *stbi__gif_load_next(stbi__context *s, stbi__gif *g, int *comp, int req_comp) +{ + int i; + stbi_uc *prev_out = 0; + + if (g->out == 0 && !stbi__gif_header(s, g, comp,0)) + return 0; // stbi__g_failure_reason set by stbi__gif_header + + prev_out = g->out; + g->out = (stbi_uc *) stbi__malloc(4 * g->w * g->h); + if (g->out == 0) return stbi__errpuc("outofmem", "Out of memory"); + + switch ((g->eflags & 0x1C) >> 2) { + case 0: // unspecified (also always used on 1st frame) + stbi__fill_gif_background(g, 0, 0, 4 * g->w, 4 * g->w * g->h); + break; + case 1: // do not dispose + if (prev_out) memcpy(g->out, prev_out, 4 * g->w * g->h); + g->old_out = prev_out; + break; + case 2: // dispose to background + if (prev_out) memcpy(g->out, prev_out, 4 * g->w * g->h); + stbi__fill_gif_background(g, g->start_x, g->start_y, g->max_x, g->max_y); + break; + case 3: // dispose to previous + if (g->old_out) { + for (i = g->start_y; i < g->max_y; i += 4 * g->w) + memcpy(&g->out[i + g->start_x], &g->old_out[i + g->start_x], g->max_x - g->start_x); + } + break; + } + + for (;;) { + switch (stbi__get8(s)) { + case 0x2C: /* Image Descriptor */ + { + int prev_trans = -1; + stbi__int32 x, y, w, h; + stbi_uc *o; + + x = stbi__get16le(s); + y = stbi__get16le(s); + w = stbi__get16le(s); + h = stbi__get16le(s); + if (((x + w) > (g->w)) || ((y + h) > (g->h))) + return stbi__errpuc("bad Image Descriptor", "Corrupt GIF"); + + g->line_size = g->w * 4; + g->start_x = x * 4; + g->start_y = y * g->line_size; + g->max_x = g->start_x + w * 4; + g->max_y = g->start_y + h * g->line_size; + g->cur_x = g->start_x; + g->cur_y = g->start_y; + + g->lflags = stbi__get8(s); + + if (g->lflags & 0x40) { + g->step = 8 * g->line_size; // first interlaced spacing + g->parse = 3; + } else { + g->step = g->line_size; + g->parse = 0; + } + + if (g->lflags & 0x80) { + stbi__gif_parse_colortable(s,g->lpal, 2 << (g->lflags & 7), g->eflags & 0x01 ? g->transparent : -1); + g->color_table = (stbi_uc *) g->lpal; + } else if (g->flags & 0x80) { + if (g->transparent >= 0 && (g->eflags & 0x01)) { + prev_trans = g->pal[g->transparent][3]; + g->pal[g->transparent][3] = 0; + } + g->color_table = (stbi_uc *) g->pal; + } else + return stbi__errpuc("missing color table", "Corrupt GIF"); + + o = stbi__process_gif_raster(s, g); + if (o == NULL) return NULL; + + if (prev_trans != -1) + g->pal[g->transparent][3] = (stbi_uc) prev_trans; + + return o; + } + + case 0x21: // Comment Extension. + { + int len; + if (stbi__get8(s) == 0xF9) { // Graphic Control Extension. + len = stbi__get8(s); + if (len == 4) { + g->eflags = stbi__get8(s); + g->delay = stbi__get16le(s); + g->transparent = stbi__get8(s); + } else { + stbi__skip(s, len); + break; + } + } + while ((len = stbi__get8(s)) != 0) + stbi__skip(s, len); + break; + } + + case 0x3B: // gif stream termination code + return (stbi_uc *) s; // using '1' causes warning on some compilers + + default: + return stbi__errpuc("unknown code", "Corrupt GIF"); + } + } + + STBI_NOTUSED(req_comp); +} + +static stbi_uc *stbi__gif_load(stbi__context *s, int *x, int *y, int *comp, int req_comp) +{ + stbi_uc *u = 0; + stbi__gif* g = (stbi__gif*) stbi__malloc(sizeof(stbi__gif)); + memset(g, 0, sizeof(*g)); + + u = stbi__gif_load_next(s, g, comp, req_comp); + if (u == (stbi_uc *) s) u = 0; // end of animated gif marker + if (u) { + *x = g->w; + *y = g->h; + if (req_comp && req_comp != 4) + u = stbi__convert_format(u, 4, req_comp, g->w, g->h); + } + else if (g->out) + STBI_FREE(g->out); + STBI_FREE(g); + return u; +} + +static int stbi__gif_info(stbi__context *s, int *x, int *y, int *comp) +{ + return stbi__gif_info_raw(s,x,y,comp); +} +#endif + +// ************************************************************************************************* +// Radiance RGBE HDR loader +// originally by Nicolas Schulz +#ifndef STBI_NO_HDR +static int stbi__hdr_test_core(stbi__context *s) +{ + const char *signature = "#?RADIANCE\n"; + int i; + for (i=0; signature[i]; ++i) + if (stbi__get8(s) != signature[i]) + return 0; + return 1; +} + +static int stbi__hdr_test(stbi__context* s) +{ + int r = stbi__hdr_test_core(s); + stbi__rewind(s); + return r; +} + +#define STBI__HDR_BUFLEN 1024 +static char *stbi__hdr_gettoken(stbi__context *z, char *buffer) +{ + int len=0; + char c = '\0'; + + c = (char) stbi__get8(z); + + while (!stbi__at_eof(z) && c != '\n') { + buffer[len++] = c; + if (len == STBI__HDR_BUFLEN-1) { + // flush to end of line + while (!stbi__at_eof(z) && stbi__get8(z) != '\n') + ; + break; + } + c = (char) stbi__get8(z); + } + + buffer[len] = 0; + return buffer; +} + +static void stbi__hdr_convert(float *output, stbi_uc *input, int req_comp) +{ + if ( input[3] != 0 ) { + float f1; + // Exponent + f1 = (float) ldexp(1.0f, input[3] - (int)(128 + 8)); + if (req_comp <= 2) + output[0] = (input[0] + input[1] + input[2]) * f1 / 3; + else { + output[0] = input[0] * f1; + output[1] = input[1] * f1; + output[2] = input[2] * f1; + } + if (req_comp == 2) output[1] = 1; + if (req_comp == 4) output[3] = 1; + } else { + switch (req_comp) { + case 4: output[3] = 1; /* fallthrough */ + case 3: output[0] = output[1] = output[2] = 0; + break; + case 2: output[1] = 1; /* fallthrough */ + case 1: output[0] = 0; + break; + } + } +} + +static float *stbi__hdr_load(stbi__context *s, int *x, int *y, int *comp, int req_comp) +{ + char buffer[STBI__HDR_BUFLEN]; + char *token; + int valid = 0; + int width, height; + stbi_uc *scanline; + float *hdr_data; + int len; + unsigned char count, value; + int i, j, k, c1,c2, z; + + + // Check identifier + if (strcmp(stbi__hdr_gettoken(s,buffer), "#?RADIANCE") != 0) + return stbi__errpf("not HDR", "Corrupt HDR image"); + + // Parse header + for(;;) { + token = stbi__hdr_gettoken(s,buffer); + if (token[0] == 0) break; + if (strcmp(token, "FORMAT=32-bit_rle_rgbe") == 0) valid = 1; + } + + if (!valid) return stbi__errpf("unsupported format", "Unsupported HDR format"); + + // Parse width and height + // can't use sscanf() if we're not using stdio! + token = stbi__hdr_gettoken(s,buffer); + if (strncmp(token, "-Y ", 3)) return stbi__errpf("unsupported data layout", "Unsupported HDR format"); + token += 3; + height = (int) strtol(token, &token, 10); + while (*token == ' ') ++token; + if (strncmp(token, "+X ", 3)) return stbi__errpf("unsupported data layout", "Unsupported HDR format"); + token += 3; + width = (int) strtol(token, NULL, 10); + + *x = width; + *y = height; + + if (comp) *comp = 3; + if (req_comp == 0) req_comp = 3; + + // Read data + hdr_data = (float *) stbi__malloc(height * width * req_comp * sizeof(float)); + + // Load image data + // image data is stored as some number of sca + if ( width < 8 || width >= 32768) { + // Read flat data + for (j=0; j < height; ++j) { + for (i=0; i < width; ++i) { + stbi_uc rgbe[4]; + main_decode_loop: + stbi__getn(s, rgbe, 4); + stbi__hdr_convert(hdr_data + j * width * req_comp + i * req_comp, rgbe, req_comp); + } + } + } else { + // Read RLE-encoded data + scanline = NULL; + + for (j = 0; j < height; ++j) { + c1 = stbi__get8(s); + c2 = stbi__get8(s); + len = stbi__get8(s); + if (c1 != 2 || c2 != 2 || (len & 0x80)) { + // not run-length encoded, so we have to actually use THIS data as a decoded + // pixel (note this can't be a valid pixel--one of RGB must be >= 128) + stbi_uc rgbe[4]; + rgbe[0] = (stbi_uc) c1; + rgbe[1] = (stbi_uc) c2; + rgbe[2] = (stbi_uc) len; + rgbe[3] = (stbi_uc) stbi__get8(s); + stbi__hdr_convert(hdr_data, rgbe, req_comp); + i = 1; + j = 0; + STBI_FREE(scanline); + goto main_decode_loop; // yes, this makes no sense + } + len <<= 8; + len |= stbi__get8(s); + if (len != width) { STBI_FREE(hdr_data); STBI_FREE(scanline); return stbi__errpf("invalid decoded scanline length", "corrupt HDR"); } + if (scanline == NULL) scanline = (stbi_uc *) stbi__malloc(width * 4); + + for (k = 0; k < 4; ++k) { + i = 0; + while (i < width) { + count = stbi__get8(s); + if (count > 128) { + // Run + value = stbi__get8(s); + count -= 128; + for (z = 0; z < count; ++z) + scanline[i++ * 4 + k] = value; + } else { + // Dump + for (z = 0; z < count; ++z) + scanline[i++ * 4 + k] = stbi__get8(s); + } + } + } + for (i=0; i < width; ++i) + stbi__hdr_convert(hdr_data+(j*width + i)*req_comp, scanline + i*4, req_comp); + } + STBI_FREE(scanline); + } + + return hdr_data; +} + +static int stbi__hdr_info(stbi__context *s, int *x, int *y, int *comp) +{ + char buffer[STBI__HDR_BUFLEN]; + char *token; + int valid = 0; + + if (stbi__hdr_test(s) == 0) { + stbi__rewind( s ); + return 0; + } + + for(;;) { + token = stbi__hdr_gettoken(s,buffer); + if (token[0] == 0) break; + if (strcmp(token, "FORMAT=32-bit_rle_rgbe") == 0) valid = 1; + } + + if (!valid) { + stbi__rewind( s ); + return 0; + } + token = stbi__hdr_gettoken(s,buffer); + if (strncmp(token, "-Y ", 3)) { + stbi__rewind( s ); + return 0; + } + token += 3; + *y = (int) strtol(token, &token, 10); + while (*token == ' ') ++token; + if (strncmp(token, "+X ", 3)) { + stbi__rewind( s ); + return 0; + } + token += 3; + *x = (int) strtol(token, NULL, 10); + *comp = 3; + return 1; +} +#endif // STBI_NO_HDR + +#ifndef STBI_NO_BMP +static int stbi__bmp_info(stbi__context *s, int *x, int *y, int *comp) +{ + void *p; + stbi__bmp_data info; + + info.all_a = 255; + p = stbi__bmp_parse_header(s, &info); + stbi__rewind( s ); + if (p == NULL) + return 0; + *x = s->img_x; + *y = s->img_y; + *comp = info.ma ? 4 : 3; + return 1; +} +#endif + +#ifndef STBI_NO_PSD +static int stbi__psd_info(stbi__context *s, int *x, int *y, int *comp) +{ + int channelCount; + if (stbi__get32be(s) != 0x38425053) { + stbi__rewind( s ); + return 0; + } + if (stbi__get16be(s) != 1) { + stbi__rewind( s ); + return 0; + } + stbi__skip(s, 6); + channelCount = stbi__get16be(s); + if (channelCount < 0 || channelCount > 16) { + stbi__rewind( s ); + return 0; + } + *y = stbi__get32be(s); + *x = stbi__get32be(s); + if (stbi__get16be(s) != 8) { + stbi__rewind( s ); + return 0; + } + if (stbi__get16be(s) != 3) { + stbi__rewind( s ); + return 0; + } + *comp = 4; + return 1; +} +#endif + +#ifndef STBI_NO_PIC +static int stbi__pic_info(stbi__context *s, int *x, int *y, int *comp) +{ + int act_comp=0,num_packets=0,chained; + stbi__pic_packet packets[10]; + + if (!stbi__pic_is4(s,"\x53\x80\xF6\x34")) { + stbi__rewind(s); + return 0; + } + + stbi__skip(s, 88); + + *x = stbi__get16be(s); + *y = stbi__get16be(s); + if (stbi__at_eof(s)) { + stbi__rewind( s); + return 0; + } + if ( (*x) != 0 && (1 << 28) / (*x) < (*y)) { + stbi__rewind( s ); + return 0; + } + + stbi__skip(s, 8); + + do { + stbi__pic_packet *packet; + + if (num_packets==sizeof(packets)/sizeof(packets[0])) + return 0; + + packet = &packets[num_packets++]; + chained = stbi__get8(s); + packet->size = stbi__get8(s); + packet->type = stbi__get8(s); + packet->channel = stbi__get8(s); + act_comp |= packet->channel; + + if (stbi__at_eof(s)) { + stbi__rewind( s ); + return 0; + } + if (packet->size != 8) { + stbi__rewind( s ); + return 0; + } + } while (chained); + + *comp = (act_comp & 0x10 ? 4 : 3); + + return 1; +} +#endif + +// ************************************************************************************************* +// Portable Gray Map and Portable Pixel Map loader +// by Ken Miller +// +// PGM: http://netpbm.sourceforge.net/doc/pgm.html +// PPM: http://netpbm.sourceforge.net/doc/ppm.html +// +// Known limitations: +// Does not support comments in the header section +// Does not support ASCII image data (formats P2 and P3) +// Does not support 16-bit-per-channel + +#ifndef STBI_NO_PNM + +static int stbi__pnm_test(stbi__context *s) +{ + char p, t; + p = (char) stbi__get8(s); + t = (char) stbi__get8(s); + if (p != 'P' || (t != '5' && t != '6')) { + stbi__rewind( s ); + return 0; + } + return 1; +} + +static stbi_uc *stbi__pnm_load(stbi__context *s, int *x, int *y, int *comp, int req_comp) +{ + stbi_uc *out; + if (!stbi__pnm_info(s, (int *)&s->img_x, (int *)&s->img_y, (int *)&s->img_n)) + return 0; + *x = s->img_x; + *y = s->img_y; + *comp = s->img_n; + + out = (stbi_uc *) stbi__malloc(s->img_n * s->img_x * s->img_y); + if (!out) return stbi__errpuc("outofmem", "Out of memory"); + stbi__getn(s, out, s->img_n * s->img_x * s->img_y); + + if (req_comp && req_comp != s->img_n) { + out = stbi__convert_format(out, s->img_n, req_comp, s->img_x, s->img_y); + if (out == NULL) return out; // stbi__convert_format frees input on failure + } + return out; +} + +static int stbi__pnm_isspace(char c) +{ + return c == ' ' || c == '\t' || c == '\n' || c == '\v' || c == '\f' || c == '\r'; +} + +static void stbi__pnm_skip_whitespace(stbi__context *s, char *c) +{ + for (;;) { + while (!stbi__at_eof(s) && stbi__pnm_isspace(*c)) + *c = (char) stbi__get8(s); + + if (stbi__at_eof(s) || *c != '#') + break; + + while (!stbi__at_eof(s) && *c != '\n' && *c != '\r' ) + *c = (char) stbi__get8(s); + } +} + +static int stbi__pnm_isdigit(char c) +{ + return c >= '0' && c <= '9'; +} + +static int stbi__pnm_getinteger(stbi__context *s, char *c) +{ + int value = 0; + + while (!stbi__at_eof(s) && stbi__pnm_isdigit(*c)) { + value = value*10 + (*c - '0'); + *c = (char) stbi__get8(s); + } + + return value; +} + +static int stbi__pnm_info(stbi__context *s, int *x, int *y, int *comp) +{ + int maxv; + char c, p, t; + + stbi__rewind( s ); + + // Get identifier + p = (char) stbi__get8(s); + t = (char) stbi__get8(s); + if (p != 'P' || (t != '5' && t != '6')) { + stbi__rewind( s ); + return 0; + } + + *comp = (t == '6') ? 3 : 1; // '5' is 1-component .pgm; '6' is 3-component .ppm + + c = (char) stbi__get8(s); + stbi__pnm_skip_whitespace(s, &c); + + *x = stbi__pnm_getinteger(s, &c); // read width + stbi__pnm_skip_whitespace(s, &c); + + *y = stbi__pnm_getinteger(s, &c); // read height + stbi__pnm_skip_whitespace(s, &c); + + maxv = stbi__pnm_getinteger(s, &c); // read max value + + if (maxv > 255) + return stbi__err("max value > 255", "PPM image not 8-bit"); + else + return 1; +} +#endif + +static int stbi__info_main(stbi__context *s, int *x, int *y, int *comp) +{ + #ifndef STBI_NO_JPEG + if (stbi__jpeg_info(s, x, y, comp)) return 1; + #endif + + #ifndef STBI_NO_PNG + if (stbi__png_info(s, x, y, comp)) return 1; + #endif + + #ifndef STBI_NO_GIF + if (stbi__gif_info(s, x, y, comp)) return 1; + #endif + + #ifndef STBI_NO_BMP + if (stbi__bmp_info(s, x, y, comp)) return 1; + #endif + + #ifndef STBI_NO_PSD + if (stbi__psd_info(s, x, y, comp)) return 1; + #endif + + #ifndef STBI_NO_PIC + if (stbi__pic_info(s, x, y, comp)) return 1; + #endif + + #ifndef STBI_NO_PNM + if (stbi__pnm_info(s, x, y, comp)) return 1; + #endif + + #ifndef STBI_NO_HDR + if (stbi__hdr_info(s, x, y, comp)) return 1; + #endif + + // test tga last because it's a crappy test! + #ifndef STBI_NO_TGA + if (stbi__tga_info(s, x, y, comp)) + return 1; + #endif + return stbi__err("unknown image type", "Image not of any known type, or corrupt"); +} + +#ifndef STBI_NO_STDIO +STBIDEF int stbi_info(char const *filename, int *x, int *y, int *comp) +{ + FILE *f = stbi__fopen(filename, "rb"); + int result; + if (!f) return stbi__err("can't fopen", "Unable to open file"); + result = stbi_info_from_file(f, x, y, comp); + fclose(f); + return result; +} + +STBIDEF int stbi_info_from_file(FILE *f, int *x, int *y, int *comp) +{ + int r; + stbi__context s; + long pos = ftell(f); + stbi__start_file(&s, f); + r = stbi__info_main(&s,x,y,comp); + fseek(f,pos,SEEK_SET); + return r; +} +#endif // !STBI_NO_STDIO + +STBIDEF int stbi_info_from_memory(stbi_uc const *buffer, int len, int *x, int *y, int *comp) +{ + stbi__context s; + stbi__start_mem(&s,buffer,len); + return stbi__info_main(&s,x,y,comp); +} + +STBIDEF int stbi_info_from_callbacks(stbi_io_callbacks const *c, void *user, int *x, int *y, int *comp) +{ + stbi__context s; + stbi__start_callbacks(&s, (stbi_io_callbacks *) c, user); + return stbi__info_main(&s,x,y,comp); +} + +#endif // STB_IMAGE_IMPLEMENTATION + +/* + revision history: + 2.12 (2016-04-02) fix typo in 2.11 PSD fix that caused crashes + 2.11 (2016-04-02) allocate large structures on the stack + remove white matting for transparent PSD + fix reported channel count for PNG & BMP + re-enable SSE2 in non-gcc 64-bit + support RGB-formatted JPEG + read 16-bit PNGs (only as 8-bit) + 2.10 (2016-01-22) avoid warning introduced in 2.09 by STBI_REALLOC_SIZED + 2.09 (2016-01-16) allow comments in PNM files + 16-bit-per-pixel TGA (not bit-per-component) + info() for TGA could break due to .hdr handling + info() for BMP to shares code instead of sloppy parse + can use STBI_REALLOC_SIZED if allocator doesn't support realloc + code cleanup + 2.08 (2015-09-13) fix to 2.07 cleanup, reading RGB PSD as RGBA + 2.07 (2015-09-13) fix compiler warnings + partial animated GIF support + limited 16-bpc PSD support + #ifdef unused functions + bug with < 92 byte PIC,PNM,HDR,TGA + 2.06 (2015-04-19) fix bug where PSD returns wrong '*comp' value + 2.05 (2015-04-19) fix bug in progressive JPEG handling, fix warning + 2.04 (2015-04-15) try to re-enable SIMD on MinGW 64-bit + 2.03 (2015-04-12) extra corruption checking (mmozeiko) + stbi_set_flip_vertically_on_load (nguillemot) + fix NEON support; fix mingw support + 2.02 (2015-01-19) fix incorrect assert, fix warning + 2.01 (2015-01-17) fix various warnings; suppress SIMD on gcc 32-bit without -msse2 + 2.00b (2014-12-25) fix STBI_MALLOC in progressive JPEG + 2.00 (2014-12-25) optimize JPG, including x86 SSE2 & NEON SIMD (ryg) + progressive JPEG (stb) + PGM/PPM support (Ken Miller) + STBI_MALLOC,STBI_REALLOC,STBI_FREE + GIF bugfix -- seemingly never worked + STBI_NO_*, STBI_ONLY_* + 1.48 (2014-12-14) fix incorrectly-named assert() + 1.47 (2014-12-14) 1/2/4-bit PNG support, both direct and paletted (Omar Cornut & stb) + optimize PNG (ryg) + fix bug in interlaced PNG with user-specified channel count (stb) + 1.46 (2014-08-26) + fix broken tRNS chunk (colorkey-style transparency) in non-paletted PNG + 1.45 (2014-08-16) + fix MSVC-ARM internal compiler error by wrapping malloc + 1.44 (2014-08-07) + various warning fixes from Ronny Chevalier + 1.43 (2014-07-15) + fix MSVC-only compiler problem in code changed in 1.42 + 1.42 (2014-07-09) + don't define _CRT_SECURE_NO_WARNINGS (affects user code) + fixes to stbi__cleanup_jpeg path + added STBI_ASSERT to avoid requiring assert.h + 1.41 (2014-06-25) + fix search&replace from 1.36 that messed up comments/error messages + 1.40 (2014-06-22) + fix gcc struct-initialization warning + 1.39 (2014-06-15) + fix to TGA optimization when req_comp != number of components in TGA; + fix to GIF loading because BMP wasn't rewinding (whoops, no GIFs in my test suite) + add support for BMP version 5 (more ignored fields) + 1.38 (2014-06-06) + suppress MSVC warnings on integer casts truncating values + fix accidental rename of 'skip' field of I/O + 1.37 (2014-06-04) + remove duplicate typedef + 1.36 (2014-06-03) + convert to header file single-file library + if de-iphone isn't set, load iphone images color-swapped instead of returning NULL + 1.35 (2014-05-27) + various warnings + fix broken STBI_SIMD path + fix bug where stbi_load_from_file no longer left file pointer in correct place + fix broken non-easy path for 32-bit BMP (possibly never used) + TGA optimization by Arseny Kapoulkine + 1.34 (unknown) + use STBI_NOTUSED in stbi__resample_row_generic(), fix one more leak in tga failure case + 1.33 (2011-07-14) + make stbi_is_hdr work in STBI_NO_HDR (as specified), minor compiler-friendly improvements + 1.32 (2011-07-13) + support for "info" function for all supported filetypes (SpartanJ) + 1.31 (2011-06-20) + a few more leak fixes, bug in PNG handling (SpartanJ) + 1.30 (2011-06-11) + added ability to load files via callbacks to accomidate custom input streams (Ben Wenger) + removed deprecated format-specific test/load functions + removed support for installable file formats (stbi_loader) -- would have been broken for IO callbacks anyway + error cases in bmp and tga give messages and don't leak (Raymond Barbiero, grisha) + fix inefficiency in decoding 32-bit BMP (David Woo) + 1.29 (2010-08-16) + various warning fixes from Aurelien Pocheville + 1.28 (2010-08-01) + fix bug in GIF palette transparency (SpartanJ) + 1.27 (2010-08-01) + cast-to-stbi_uc to fix warnings + 1.26 (2010-07-24) + fix bug in file buffering for PNG reported by SpartanJ + 1.25 (2010-07-17) + refix trans_data warning (Won Chun) + 1.24 (2010-07-12) + perf improvements reading from files on platforms with lock-heavy fgetc() + minor perf improvements for jpeg + deprecated type-specific functions so we'll get feedback if they're needed + attempt to fix trans_data warning (Won Chun) + 1.23 fixed bug in iPhone support + 1.22 (2010-07-10) + removed image *writing* support + stbi_info support from Jetro Lauha + GIF support from Jean-Marc Lienher + iPhone PNG-extensions from James Brown + warning-fixes from Nicolas Schulz and Janez Zemva (i.stbi__err. Janez (U+017D)emva) + 1.21 fix use of 'stbi_uc' in header (reported by jon blow) + 1.20 added support for Softimage PIC, by Tom Seddon + 1.19 bug in interlaced PNG corruption check (found by ryg) + 1.18 (2008-08-02) + fix a threading bug (local mutable static) + 1.17 support interlaced PNG + 1.16 major bugfix - stbi__convert_format converted one too many pixels + 1.15 initialize some fields for thread safety + 1.14 fix threadsafe conversion bug + header-file-only version (#define STBI_HEADER_FILE_ONLY before including) + 1.13 threadsafe + 1.12 const qualifiers in the API + 1.11 Support installable IDCT, colorspace conversion routines + 1.10 Fixes for 64-bit (don't use "unsigned long") + optimized upsampling by Fabian "ryg" Giesen + 1.09 Fix format-conversion for PSD code (bad global variables!) + 1.08 Thatcher Ulrich's PSD code integrated by Nicolas Schulz + 1.07 attempt to fix C++ warning/errors again + 1.06 attempt to fix C++ warning/errors again + 1.05 fix TGA loading to return correct *comp and use good luminance calc + 1.04 default float alpha is 1, not 255; use 'void *' for stbi_image_free + 1.03 bugfixes to STBI_NO_STDIO, STBI_NO_HDR + 1.02 support for (subset of) HDR files, float interface for preferred access to them + 1.01 fix bug: possible bug in handling right-side up bmps... not sure + fix bug: the stbi__bmp_load() and stbi__tga_load() functions didn't work at all + 1.00 interface to zlib that skips zlib header + 0.99 correct handling of alpha in palette + 0.98 TGA loader by lonesock; dynamically add loaders (untested) + 0.97 jpeg errors on too large a file; also catch another malloc failure + 0.96 fix detection of invalid v value - particleman@mollyrocket forum + 0.95 during header scan, seek to markers in case of padding + 0.94 STBI_NO_STDIO to disable stdio usage; rename all #defines the same + 0.93 handle jpegtran output; verbose errors + 0.92 read 4,8,16,24,32-bit BMP files of several formats + 0.91 output 24-bit Windows 3.0 BMP files + 0.90 fix a few more warnings; bump version number to approach 1.0 + 0.61 bugfixes due to Marc LeBlanc, Christopher Lloyd + 0.60 fix compiling as c++ + 0.59 fix warnings: merge Dave Moore's -Wall fixes + 0.58 fix bug: zlib uncompressed mode len/nlen was wrong endian + 0.57 fix bug: jpg last huffman symbol before marker was >9 bits but less than 16 available + 0.56 fix bug: zlib uncompressed mode len vs. nlen + 0.55 fix bug: restart_interval not initialized to 0 + 0.54 allow NULL for 'int *comp' + 0.53 fix bug in png 3->4; speedup png decoding + 0.52 png handles req_comp=3,4 directly; minor cleanup; jpeg comments + 0.51 obey req_comp requests, 1-component jpegs return as 1-component, + on 'test' only check type, not whether we support this variant + 0.50 (2006-11-19) + first released version +*/ diff --git a/impeller/third_party/stb/stb/stb_image_resize.h b/impeller/third_party/stb/stb/stb_image_resize.h new file mode 100644 index 0000000000000..4cabe54089dda --- /dev/null +++ b/impeller/third_party/stb/stb/stb_image_resize.h @@ -0,0 +1,2578 @@ +/* stb_image_resize - v0.91 - public domain image resizing + by Jorge L Rodriguez (@VinoBS) - 2014 + http://github.com/nothings/stb + + Written with emphasis on usability, portability, and efficiency. (No + SIMD or threads, so it be easily outperformed by libs that use those.) + Only scaling and translation is supported, no rotations or shears. + Easy API downsamples w/Mitchell filter, upsamples w/cubic interpolation. + + COMPILING & LINKING + In one C/C++ file that #includes this file, do this: + #define STB_IMAGE_RESIZE_IMPLEMENTATION + before the #include. That will create the implementation in that file. + + QUICKSTART + stbir_resize_uint8( input_pixels , in_w , in_h , 0, + output_pixels, out_w, out_h, 0, num_channels) + stbir_resize_float(...) + stbir_resize_uint8_srgb( input_pixels , in_w , in_h , 0, + output_pixels, out_w, out_h, 0, + num_channels , alpha_chan , 0) + stbir_resize_uint8_srgb_edgemode( + input_pixels , in_w , in_h , 0, + output_pixels, out_w, out_h, 0, + num_channels , alpha_chan , 0, STBIR_EDGE_CLAMP) + // WRAP/REFLECT/ZERO + + FULL API + See the "header file" section of the source for API documentation. + + ADDITIONAL DOCUMENTATION + + SRGB & FLOATING POINT REPRESENTATION + The sRGB functions presume IEEE floating point. If you do not have + IEEE floating point, define STBIR_NON_IEEE_FLOAT. This will use + a slower implementation. + + MEMORY ALLOCATION + The resize functions here perform a single memory allocation using + malloc. To control the memory allocation, before the #include that + triggers the implementation, do: + + #define STBIR_MALLOC(size,context) ... + #define STBIR_FREE(ptr,context) ... + + Each resize function makes exactly one call to malloc/free, so to use + temp memory, store the temp memory in the context and return that. + + ASSERT + Define STBIR_ASSERT(boolval) to override assert() and not use assert.h + + OPTIMIZATION + Define STBIR_SATURATE_INT to compute clamp values in-range using + integer operations instead of float operations. This may be faster + on some platforms. + + DEFAULT FILTERS + For functions which don't provide explicit control over what filters + to use, you can change the compile-time defaults with + + #define STBIR_DEFAULT_FILTER_UPSAMPLE STBIR_FILTER_something + #define STBIR_DEFAULT_FILTER_DOWNSAMPLE STBIR_FILTER_something + + See stbir_filter in the header-file section for the list of filters. + + NEW FILTERS + A number of 1D filter kernels are used. For a list of + supported filters see the stbir_filter enum. To add a new filter, + write a filter function and add it to stbir__filter_info_table. + + PROGRESS + For interactive use with slow resize operations, you can install + a progress-report callback: + + #define STBIR_PROGRESS_REPORT(val) some_func(val) + + The parameter val is a float which goes from 0 to 1 as progress is made. + + For example: + + static void my_progress_report(float progress); + #define STBIR_PROGRESS_REPORT(val) my_progress_report(val) + + #define STB_IMAGE_RESIZE_IMPLEMENTATION + #include "stb_image_resize.h" + + static void my_progress_report(float progress) + { + printf("Progress: %f%%\n", progress*100); + } + + MAX CHANNELS + If your image has more than 64 channels, define STBIR_MAX_CHANNELS + to the max you'll have. + + ALPHA CHANNEL + Most of the resizing functions provide the ability to control how + the alpha channel of an image is processed. The important things + to know about this: + + 1. The best mathematically-behaved version of alpha to use is + called "premultiplied alpha", in which the other color channels + have had the alpha value multiplied in. If you use premultiplied + alpha, linear filtering (such as image resampling done by this + library, or performed in texture units on GPUs) does the "right + thing". While premultiplied alpha is standard in the movie CGI + industry, it is still uncommon in the videogame/real-time world. + + If you linearly filter non-premultiplied alpha, strange effects + occur. (For example, the average of 1% opaque bright green + and 99% opaque black produces 50% transparent dark green when + non-premultiplied, whereas premultiplied it produces 50% + transparent near-black. The former introduces green energy + that doesn't exist in the source image.) + + 2. Artists should not edit premultiplied-alpha images; artists + want non-premultiplied alpha images. Thus, art tools generally output + non-premultiplied alpha images. + + 3. You will get best results in most cases by converting images + to premultiplied alpha before processing them mathematically. + + 4. If you pass the flag STBIR_FLAG_ALPHA_PREMULTIPLIED, the + resizer does not do anything special for the alpha channel; + it is resampled identically to other channels. This produces + the correct results for premultiplied-alpha images, but produces + less-than-ideal results for non-premultiplied-alpha images. + + 5. If you do not pass the flag STBIR_FLAG_ALPHA_PREMULTIPLIED, + then the resizer weights the contribution of input pixels + based on their alpha values, or, equivalently, it multiplies + the alpha value into the color channels, resamples, then divides + by the resultant alpha value. Input pixels which have alpha=0 do + not contribute at all to output pixels unless _all_ of the input + pixels affecting that output pixel have alpha=0, in which case + the result for that pixel is the same as it would be without + STBIR_FLAG_ALPHA_PREMULTIPLIED. However, this is only true for + input images in integer formats. For input images in float format, + input pixels with alpha=0 have no effect, and output pixels + which have alpha=0 will be 0 in all channels. (For float images, + you can manually achieve the same result by adding a tiny epsilon + value to the alpha channel of every image, and then subtracting + or clamping it at the end.) + + 6. You can suppress the behavior described in #5 and make + all-0-alpha pixels have 0 in all channels by #defining + STBIR_NO_ALPHA_EPSILON. + + 7. You can separately control whether the alpha channel is + interpreted as linear or affected by the colorspace. By default + it is linear; you almost never want to apply the colorspace. + (For example, graphics hardware does not apply sRGB conversion + to the alpha channel.) + + ADDITIONAL CONTRIBUTORS + Sean Barrett: API design, optimizations + + REVISIONS + 0.91 (2016-04-02) fix warnings; fix handling of subpixel regions + 0.90 (2014-09-17) first released version + + LICENSE + + This software is dual-licensed to the public domain and under the following + license: you are granted a perpetual, irrevocable license to copy, modify, + publish, and distribute this file as you see fit. + + TODO + Don't decode all of the image data when only processing a partial tile + Don't use full-width decode buffers when only processing a partial tile + When processing wide images, break processing into tiles so data fits in L1 cache + Installable filters? + Resize that respects alpha test coverage + (Reference code: FloatImage::alphaTestCoverage and FloatImage::scaleAlphaToCoverage: + https://code.google.com/p/nvidia-texture-tools/source/browse/trunk/src/nvimage/FloatImage.cpp ) +*/ + +#ifndef STBIR_INCLUDE_STB_IMAGE_RESIZE_H +#define STBIR_INCLUDE_STB_IMAGE_RESIZE_H + +#ifdef _MSC_VER +typedef unsigned char stbir_uint8; +typedef unsigned short stbir_uint16; +typedef unsigned int stbir_uint32; +#else +#include +typedef uint8_t stbir_uint8; +typedef uint16_t stbir_uint16; +typedef uint32_t stbir_uint32; +#endif + +#ifdef STB_IMAGE_RESIZE_STATIC +#define STBIRDEF static +#else +#ifdef __cplusplus +#define STBIRDEF extern "C" +#else +#define STBIRDEF extern +#endif +#endif + + +////////////////////////////////////////////////////////////////////////////// +// +// Easy-to-use API: +// +// * "input pixels" points to an array of image data with 'num_channels' channels (e.g. RGB=3, RGBA=4) +// * input_w is input image width (x-axis), input_h is input image height (y-axis) +// * stride is the offset between successive rows of image data in memory, in bytes. you can +// specify 0 to mean packed continuously in memory +// * alpha channel is treated identically to other channels. +// * colorspace is linear or sRGB as specified by function name +// * returned result is 1 for success or 0 in case of an error. +// #define STBIR_ASSERT() to trigger an assert on parameter validation errors. +// * Memory required grows approximately linearly with input and output size, but with +// discontinuities at input_w == output_w and input_h == output_h. +// * These functions use a "default" resampling filter defined at compile time. To change the filter, +// you can change the compile-time defaults by #defining STBIR_DEFAULT_FILTER_UPSAMPLE +// and STBIR_DEFAULT_FILTER_DOWNSAMPLE, or you can use the medium-complexity API. + +STBIRDEF int stbir_resize_uint8( const unsigned char *input_pixels , int input_w , int input_h , int input_stride_in_bytes, + unsigned char *output_pixels, int output_w, int output_h, int output_stride_in_bytes, + int num_channels); + +STBIRDEF int stbir_resize_float( const float *input_pixels , int input_w , int input_h , int input_stride_in_bytes, + float *output_pixels, int output_w, int output_h, int output_stride_in_bytes, + int num_channels); + + +// The following functions interpret image data as gamma-corrected sRGB. +// Specify STBIR_ALPHA_CHANNEL_NONE if you have no alpha channel, +// or otherwise provide the index of the alpha channel. Flags value +// of 0 will probably do the right thing if you're not sure what +// the flags mean. + +#define STBIR_ALPHA_CHANNEL_NONE -1 + +// Set this flag if your texture has premultiplied alpha. Otherwise, stbir will +// use alpha-weighted resampling (effectively premultiplying, resampling, +// then unpremultiplying). +#define STBIR_FLAG_ALPHA_PREMULTIPLIED (1 << 0) +// The specified alpha channel should be handled as gamma-corrected value even +// when doing sRGB operations. +#define STBIR_FLAG_ALPHA_USES_COLORSPACE (1 << 1) + +STBIRDEF int stbir_resize_uint8_srgb(const unsigned char *input_pixels , int input_w , int input_h , int input_stride_in_bytes, + unsigned char *output_pixels, int output_w, int output_h, int output_stride_in_bytes, + int num_channels, int alpha_channel, int flags); + + +typedef enum +{ + STBIR_EDGE_CLAMP = 1, + STBIR_EDGE_REFLECT = 2, + STBIR_EDGE_WRAP = 3, + STBIR_EDGE_ZERO = 4, +} stbir_edge; + +// This function adds the ability to specify how requests to sample off the edge of the image are handled. +STBIRDEF int stbir_resize_uint8_srgb_edgemode(const unsigned char *input_pixels , int input_w , int input_h , int input_stride_in_bytes, + unsigned char *output_pixels, int output_w, int output_h, int output_stride_in_bytes, + int num_channels, int alpha_channel, int flags, + stbir_edge edge_wrap_mode); + +////////////////////////////////////////////////////////////////////////////// +// +// Medium-complexity API +// +// This extends the easy-to-use API as follows: +// +// * Alpha-channel can be processed separately +// * If alpha_channel is not STBIR_ALPHA_CHANNEL_NONE +// * Alpha channel will not be gamma corrected (unless flags&STBIR_FLAG_GAMMA_CORRECT) +// * Filters will be weighted by alpha channel (unless flags&STBIR_FLAG_ALPHA_PREMULTIPLIED) +// * Filter can be selected explicitly +// * uint16 image type +// * sRGB colorspace available for all types +// * context parameter for passing to STBIR_MALLOC + +typedef enum +{ + STBIR_FILTER_DEFAULT = 0, // use same filter type that easy-to-use API chooses + STBIR_FILTER_BOX = 1, // A trapezoid w/1-pixel wide ramps, same result as box for integer scale ratios + STBIR_FILTER_TRIANGLE = 2, // On upsampling, produces same results as bilinear texture filtering + STBIR_FILTER_CUBICBSPLINE = 3, // The cubic b-spline (aka Mitchell-Netrevalli with B=1,C=0), gaussian-esque + STBIR_FILTER_CATMULLROM = 4, // An interpolating cubic spline + STBIR_FILTER_MITCHELL = 5, // Mitchell-Netrevalli filter with B=1/3, C=1/3 +} stbir_filter; + +typedef enum +{ + STBIR_COLORSPACE_LINEAR, + STBIR_COLORSPACE_SRGB, + + STBIR_MAX_COLORSPACES, +} stbir_colorspace; + +// The following functions are all identical except for the type of the image data + +STBIRDEF int stbir_resize_uint8_generic( const unsigned char *input_pixels , int input_w , int input_h , int input_stride_in_bytes, + unsigned char *output_pixels, int output_w, int output_h, int output_stride_in_bytes, + int num_channels, int alpha_channel, int flags, + stbir_edge edge_wrap_mode, stbir_filter filter, stbir_colorspace space, + void *alloc_context); + +STBIRDEF int stbir_resize_uint16_generic(const stbir_uint16 *input_pixels , int input_w , int input_h , int input_stride_in_bytes, + stbir_uint16 *output_pixels , int output_w, int output_h, int output_stride_in_bytes, + int num_channels, int alpha_channel, int flags, + stbir_edge edge_wrap_mode, stbir_filter filter, stbir_colorspace space, + void *alloc_context); + +STBIRDEF int stbir_resize_float_generic( const float *input_pixels , int input_w , int input_h , int input_stride_in_bytes, + float *output_pixels , int output_w, int output_h, int output_stride_in_bytes, + int num_channels, int alpha_channel, int flags, + stbir_edge edge_wrap_mode, stbir_filter filter, stbir_colorspace space, + void *alloc_context); + + + +////////////////////////////////////////////////////////////////////////////// +// +// Full-complexity API +// +// This extends the medium API as follows: +// +// * uint32 image type +// * not typesafe +// * separate filter types for each axis +// * separate edge modes for each axis +// * can specify scale explicitly for subpixel correctness +// * can specify image source tile using texture coordinates + +typedef enum +{ + STBIR_TYPE_UINT8 , + STBIR_TYPE_UINT16, + STBIR_TYPE_UINT32, + STBIR_TYPE_FLOAT , + + STBIR_MAX_TYPES +} stbir_datatype; + +STBIRDEF int stbir_resize( const void *input_pixels , int input_w , int input_h , int input_stride_in_bytes, + void *output_pixels, int output_w, int output_h, int output_stride_in_bytes, + stbir_datatype datatype, + int num_channels, int alpha_channel, int flags, + stbir_edge edge_mode_horizontal, stbir_edge edge_mode_vertical, + stbir_filter filter_horizontal, stbir_filter filter_vertical, + stbir_colorspace space, void *alloc_context); + +STBIRDEF int stbir_resize_subpixel(const void *input_pixels , int input_w , int input_h , int input_stride_in_bytes, + void *output_pixels, int output_w, int output_h, int output_stride_in_bytes, + stbir_datatype datatype, + int num_channels, int alpha_channel, int flags, + stbir_edge edge_mode_horizontal, stbir_edge edge_mode_vertical, + stbir_filter filter_horizontal, stbir_filter filter_vertical, + stbir_colorspace space, void *alloc_context, + float x_scale, float y_scale, + float x_offset, float y_offset); + +STBIRDEF int stbir_resize_region( const void *input_pixels , int input_w , int input_h , int input_stride_in_bytes, + void *output_pixels, int output_w, int output_h, int output_stride_in_bytes, + stbir_datatype datatype, + int num_channels, int alpha_channel, int flags, + stbir_edge edge_mode_horizontal, stbir_edge edge_mode_vertical, + stbir_filter filter_horizontal, stbir_filter filter_vertical, + stbir_colorspace space, void *alloc_context, + float s0, float t0, float s1, float t1); +// (s0, t0) & (s1, t1) are the top-left and bottom right corner (uv addressing style: [0, 1]x[0, 1]) of a region of the input image to use. + +// +// +//// end header file ///////////////////////////////////////////////////// +#endif // STBIR_INCLUDE_STB_IMAGE_RESIZE_H + + + + + +#ifdef STB_IMAGE_RESIZE_IMPLEMENTATION + +#ifndef STBIR_ASSERT +#include +#define STBIR_ASSERT(x) assert(x) +#endif + +// For memset +#include + +#include + +#ifndef STBIR_MALLOC +#include +#define STBIR_MALLOC(size,c) malloc(size) +#define STBIR_FREE(ptr,c) free(ptr) +#endif + +#ifndef _MSC_VER +#ifdef __cplusplus +#define stbir__inline inline +#else +#define stbir__inline +#endif +#else +#define stbir__inline __forceinline +#endif + + +// should produce compiler error if size is wrong +typedef unsigned char stbir__validate_uint32[sizeof(stbir_uint32) == 4 ? 1 : -1]; + +#ifdef _MSC_VER +#define STBIR__NOTUSED(v) (void)(v) +#else +#define STBIR__NOTUSED(v) (void)sizeof(v) +#endif + +#define STBIR__ARRAY_SIZE(a) (sizeof((a))/sizeof((a)[0])) + +#ifndef STBIR_DEFAULT_FILTER_UPSAMPLE +#define STBIR_DEFAULT_FILTER_UPSAMPLE STBIR_FILTER_CATMULLROM +#endif + +#ifndef STBIR_DEFAULT_FILTER_DOWNSAMPLE +#define STBIR_DEFAULT_FILTER_DOWNSAMPLE STBIR_FILTER_MITCHELL +#endif + +#ifndef STBIR_PROGRESS_REPORT +#define STBIR_PROGRESS_REPORT(float_0_to_1) +#endif + +#ifndef STBIR_MAX_CHANNELS +#define STBIR_MAX_CHANNELS 64 +#endif + +#if STBIR_MAX_CHANNELS > 65536 +#error "Too many channels; STBIR_MAX_CHANNELS must be no more than 65536." +// because we store the indices in 16-bit variables +#endif + +// This value is added to alpha just before premultiplication to avoid +// zeroing out color values. It is equivalent to 2^-80. If you don't want +// that behavior (it may interfere if you have floating point images with +// very small alpha values) then you can define STBIR_NO_ALPHA_EPSILON to +// disable it. +#ifndef STBIR_ALPHA_EPSILON +#define STBIR_ALPHA_EPSILON ((float)1 / (1 << 20) / (1 << 20) / (1 << 20) / (1 << 20)) +#endif + + + +#ifdef _MSC_VER +#define STBIR__UNUSED_PARAM(v) (void)(v) +#else +#define STBIR__UNUSED_PARAM(v) (void)sizeof(v) +#endif + +// must match stbir_datatype +static unsigned char stbir__type_size[] = { + 1, // STBIR_TYPE_UINT8 + 2, // STBIR_TYPE_UINT16 + 4, // STBIR_TYPE_UINT32 + 4, // STBIR_TYPE_FLOAT +}; + +// Kernel function centered at 0 +typedef float (stbir__kernel_fn)(float x, float scale); +typedef float (stbir__support_fn)(float scale); + +typedef struct +{ + stbir__kernel_fn* kernel; + stbir__support_fn* support; +} stbir__filter_info; + +// When upsampling, the contributors are which source pixels contribute. +// When downsampling, the contributors are which destination pixels are contributed to. +typedef struct +{ + int n0; // First contributing pixel + int n1; // Last contributing pixel +} stbir__contributors; + +typedef struct +{ + const void* input_data; + int input_w; + int input_h; + int input_stride_bytes; + + void* output_data; + int output_w; + int output_h; + int output_stride_bytes; + + float s0, t0, s1, t1; + + float horizontal_shift; // Units: output pixels + float vertical_shift; // Units: output pixels + float horizontal_scale; + float vertical_scale; + + int channels; + int alpha_channel; + stbir_uint32 flags; + stbir_datatype type; + stbir_filter horizontal_filter; + stbir_filter vertical_filter; + stbir_edge edge_horizontal; + stbir_edge edge_vertical; + stbir_colorspace colorspace; + + stbir__contributors* horizontal_contributors; + float* horizontal_coefficients; + + stbir__contributors* vertical_contributors; + float* vertical_coefficients; + + int decode_buffer_pixels; + float* decode_buffer; + + float* horizontal_buffer; + + // cache these because ceil/floor are inexplicably showing up in profile + int horizontal_coefficient_width; + int vertical_coefficient_width; + int horizontal_filter_pixel_width; + int vertical_filter_pixel_width; + int horizontal_filter_pixel_margin; + int vertical_filter_pixel_margin; + int horizontal_num_contributors; + int vertical_num_contributors; + + int ring_buffer_length_bytes; // The length of an individual entry in the ring buffer. The total number of ring buffers is stbir__get_filter_pixel_width(filter) + int ring_buffer_first_scanline; + int ring_buffer_last_scanline; + int ring_buffer_begin_index; + float* ring_buffer; + + float* encode_buffer; // A temporary buffer to store floats so we don't lose precision while we do multiply-adds. + + int horizontal_contributors_size; + int horizontal_coefficients_size; + int vertical_contributors_size; + int vertical_coefficients_size; + int decode_buffer_size; + int horizontal_buffer_size; + int ring_buffer_size; + int encode_buffer_size; +} stbir__info; + +static stbir__inline int stbir__min(int a, int b) +{ + return a < b ? a : b; +} + +static stbir__inline int stbir__max(int a, int b) +{ + return a > b ? a : b; +} + +static stbir__inline float stbir__saturate(float x) +{ + if (x < 0) + return 0; + + if (x > 1) + return 1; + + return x; +} + +#ifdef STBIR_SATURATE_INT +static stbir__inline stbir_uint8 stbir__saturate8(int x) +{ + if ((unsigned int) x <= 255) + return x; + + if (x < 0) + return 0; + + return 255; +} + +static stbir__inline stbir_uint16 stbir__saturate16(int x) +{ + if ((unsigned int) x <= 65535) + return x; + + if (x < 0) + return 0; + + return 65535; +} +#endif + +static float stbir__srgb_uchar_to_linear_float[256] = { + 0.000000f, 0.000304f, 0.000607f, 0.000911f, 0.001214f, 0.001518f, 0.001821f, 0.002125f, 0.002428f, 0.002732f, 0.003035f, + 0.003347f, 0.003677f, 0.004025f, 0.004391f, 0.004777f, 0.005182f, 0.005605f, 0.006049f, 0.006512f, 0.006995f, 0.007499f, + 0.008023f, 0.008568f, 0.009134f, 0.009721f, 0.010330f, 0.010960f, 0.011612f, 0.012286f, 0.012983f, 0.013702f, 0.014444f, + 0.015209f, 0.015996f, 0.016807f, 0.017642f, 0.018500f, 0.019382f, 0.020289f, 0.021219f, 0.022174f, 0.023153f, 0.024158f, + 0.025187f, 0.026241f, 0.027321f, 0.028426f, 0.029557f, 0.030713f, 0.031896f, 0.033105f, 0.034340f, 0.035601f, 0.036889f, + 0.038204f, 0.039546f, 0.040915f, 0.042311f, 0.043735f, 0.045186f, 0.046665f, 0.048172f, 0.049707f, 0.051269f, 0.052861f, + 0.054480f, 0.056128f, 0.057805f, 0.059511f, 0.061246f, 0.063010f, 0.064803f, 0.066626f, 0.068478f, 0.070360f, 0.072272f, + 0.074214f, 0.076185f, 0.078187f, 0.080220f, 0.082283f, 0.084376f, 0.086500f, 0.088656f, 0.090842f, 0.093059f, 0.095307f, + 0.097587f, 0.099899f, 0.102242f, 0.104616f, 0.107023f, 0.109462f, 0.111932f, 0.114435f, 0.116971f, 0.119538f, 0.122139f, + 0.124772f, 0.127438f, 0.130136f, 0.132868f, 0.135633f, 0.138432f, 0.141263f, 0.144128f, 0.147027f, 0.149960f, 0.152926f, + 0.155926f, 0.158961f, 0.162029f, 0.165132f, 0.168269f, 0.171441f, 0.174647f, 0.177888f, 0.181164f, 0.184475f, 0.187821f, + 0.191202f, 0.194618f, 0.198069f, 0.201556f, 0.205079f, 0.208637f, 0.212231f, 0.215861f, 0.219526f, 0.223228f, 0.226966f, + 0.230740f, 0.234551f, 0.238398f, 0.242281f, 0.246201f, 0.250158f, 0.254152f, 0.258183f, 0.262251f, 0.266356f, 0.270498f, + 0.274677f, 0.278894f, 0.283149f, 0.287441f, 0.291771f, 0.296138f, 0.300544f, 0.304987f, 0.309469f, 0.313989f, 0.318547f, + 0.323143f, 0.327778f, 0.332452f, 0.337164f, 0.341914f, 0.346704f, 0.351533f, 0.356400f, 0.361307f, 0.366253f, 0.371238f, + 0.376262f, 0.381326f, 0.386430f, 0.391573f, 0.396755f, 0.401978f, 0.407240f, 0.412543f, 0.417885f, 0.423268f, 0.428691f, + 0.434154f, 0.439657f, 0.445201f, 0.450786f, 0.456411f, 0.462077f, 0.467784f, 0.473532f, 0.479320f, 0.485150f, 0.491021f, + 0.496933f, 0.502887f, 0.508881f, 0.514918f, 0.520996f, 0.527115f, 0.533276f, 0.539480f, 0.545725f, 0.552011f, 0.558340f, + 0.564712f, 0.571125f, 0.577581f, 0.584078f, 0.590619f, 0.597202f, 0.603827f, 0.610496f, 0.617207f, 0.623960f, 0.630757f, + 0.637597f, 0.644480f, 0.651406f, 0.658375f, 0.665387f, 0.672443f, 0.679543f, 0.686685f, 0.693872f, 0.701102f, 0.708376f, + 0.715694f, 0.723055f, 0.730461f, 0.737911f, 0.745404f, 0.752942f, 0.760525f, 0.768151f, 0.775822f, 0.783538f, 0.791298f, + 0.799103f, 0.806952f, 0.814847f, 0.822786f, 0.830770f, 0.838799f, 0.846873f, 0.854993f, 0.863157f, 0.871367f, 0.879622f, + 0.887923f, 0.896269f, 0.904661f, 0.913099f, 0.921582f, 0.930111f, 0.938686f, 0.947307f, 0.955974f, 0.964686f, 0.973445f, + 0.982251f, 0.991102f, 1.0f +}; + +static float stbir__srgb_to_linear(float f) +{ + if (f <= 0.04045f) + return f / 12.92f; + else + return (float)pow((f + 0.055f) / 1.055f, 2.4f); +} + +static float stbir__linear_to_srgb(float f) +{ + if (f <= 0.0031308f) + return f * 12.92f; + else + return 1.055f * (float)pow(f, 1 / 2.4f) - 0.055f; +} + +#ifndef STBIR_NON_IEEE_FLOAT +// From https://gist.github.com/rygorous/2203834 + +typedef union +{ + stbir_uint32 u; + float f; +} stbir__FP32; + +static const stbir_uint32 fp32_to_srgb8_tab4[104] = { + 0x0073000d, 0x007a000d, 0x0080000d, 0x0087000d, 0x008d000d, 0x0094000d, 0x009a000d, 0x00a1000d, + 0x00a7001a, 0x00b4001a, 0x00c1001a, 0x00ce001a, 0x00da001a, 0x00e7001a, 0x00f4001a, 0x0101001a, + 0x010e0033, 0x01280033, 0x01410033, 0x015b0033, 0x01750033, 0x018f0033, 0x01a80033, 0x01c20033, + 0x01dc0067, 0x020f0067, 0x02430067, 0x02760067, 0x02aa0067, 0x02dd0067, 0x03110067, 0x03440067, + 0x037800ce, 0x03df00ce, 0x044600ce, 0x04ad00ce, 0x051400ce, 0x057b00c5, 0x05dd00bc, 0x063b00b5, + 0x06970158, 0x07420142, 0x07e30130, 0x087b0120, 0x090b0112, 0x09940106, 0x0a1700fc, 0x0a9500f2, + 0x0b0f01cb, 0x0bf401ae, 0x0ccb0195, 0x0d950180, 0x0e56016e, 0x0f0d015e, 0x0fbc0150, 0x10630143, + 0x11070264, 0x1238023e, 0x1357021d, 0x14660201, 0x156601e9, 0x165a01d3, 0x174401c0, 0x182401af, + 0x18fe0331, 0x1a9602fe, 0x1c1502d2, 0x1d7e02ad, 0x1ed4028d, 0x201a0270, 0x21520256, 0x227d0240, + 0x239f0443, 0x25c003fe, 0x27bf03c4, 0x29a10392, 0x2b6a0367, 0x2d1d0341, 0x2ebe031f, 0x304d0300, + 0x31d105b0, 0x34a80555, 0x37520507, 0x39d504c5, 0x3c37048b, 0x3e7c0458, 0x40a8042a, 0x42bd0401, + 0x44c20798, 0x488e071e, 0x4c1c06b6, 0x4f76065d, 0x52a50610, 0x55ac05cc, 0x5892058f, 0x5b590559, + 0x5e0c0a23, 0x631c0980, 0x67db08f6, 0x6c55087f, 0x70940818, 0x74a007bd, 0x787d076c, 0x7c330723, +}; + +static stbir_uint8 stbir__linear_to_srgb_uchar(float in) +{ + static const stbir__FP32 almostone = { 0x3f7fffff }; // 1-eps + static const stbir__FP32 minval = { (127-13) << 23 }; + stbir_uint32 tab,bias,scale,t; + stbir__FP32 f; + + // Clamp to [2^(-13), 1-eps]; these two values map to 0 and 1, respectively. + // The tests are carefully written so that NaNs map to 0, same as in the reference + // implementation. + if (!(in > minval.f)) // written this way to catch NaNs + in = minval.f; + if (in > almostone.f) + in = almostone.f; + + // Do the table lookup and unpack bias, scale + f.f = in; + tab = fp32_to_srgb8_tab4[(f.u - minval.u) >> 20]; + bias = (tab >> 16) << 9; + scale = tab & 0xffff; + + // Grab next-highest mantissa bits and perform linear interpolation + t = (f.u >> 12) & 0xff; + return (unsigned char) ((bias + scale*t) >> 16); +} + +#else +// sRGB transition values, scaled by 1<<28 +static int stbir__srgb_offset_to_linear_scaled[256] = +{ + 0, 40738, 122216, 203693, 285170, 366648, 448125, 529603, + 611080, 692557, 774035, 855852, 942009, 1033024, 1128971, 1229926, + 1335959, 1447142, 1563542, 1685229, 1812268, 1944725, 2082664, 2226148, + 2375238, 2529996, 2690481, 2856753, 3028870, 3206888, 3390865, 3580856, + 3776916, 3979100, 4187460, 4402049, 4622919, 4850123, 5083710, 5323731, + 5570236, 5823273, 6082892, 6349140, 6622065, 6901714, 7188133, 7481369, + 7781466, 8088471, 8402427, 8723380, 9051372, 9386448, 9728650, 10078021, + 10434603, 10798439, 11169569, 11548036, 11933879, 12327139, 12727857, 13136073, + 13551826, 13975156, 14406100, 14844697, 15290987, 15745007, 16206795, 16676389, + 17153826, 17639142, 18132374, 18633560, 19142734, 19659934, 20185196, 20718552, + 21260042, 21809696, 22367554, 22933648, 23508010, 24090680, 24681686, 25281066, + 25888850, 26505076, 27129772, 27762974, 28404716, 29055026, 29713942, 30381490, + 31057708, 31742624, 32436272, 33138682, 33849884, 34569912, 35298800, 36036568, + 36783260, 37538896, 38303512, 39077136, 39859796, 40651528, 41452360, 42262316, + 43081432, 43909732, 44747252, 45594016, 46450052, 47315392, 48190064, 49074096, + 49967516, 50870356, 51782636, 52704392, 53635648, 54576432, 55526772, 56486700, + 57456236, 58435408, 59424248, 60422780, 61431036, 62449032, 63476804, 64514376, + 65561776, 66619028, 67686160, 68763192, 69850160, 70947088, 72053992, 73170912, + 74297864, 75434880, 76581976, 77739184, 78906536, 80084040, 81271736, 82469648, + 83677792, 84896192, 86124888, 87363888, 88613232, 89872928, 91143016, 92423512, + 93714432, 95015816, 96327688, 97650056, 98982952, 100326408, 101680440, 103045072, + 104420320, 105806224, 107202800, 108610064, 110028048, 111456776, 112896264, 114346544, + 115807632, 117279552, 118762328, 120255976, 121760536, 123276016, 124802440, 126339832, + 127888216, 129447616, 131018048, 132599544, 134192112, 135795792, 137410592, 139036528, + 140673648, 142321952, 143981456, 145652208, 147334208, 149027488, 150732064, 152447968, + 154175200, 155913792, 157663776, 159425168, 161197984, 162982240, 164777968, 166585184, + 168403904, 170234160, 172075968, 173929344, 175794320, 177670896, 179559120, 181458992, + 183370528, 185293776, 187228736, 189175424, 191133888, 193104112, 195086128, 197079968, + 199085648, 201103184, 203132592, 205173888, 207227120, 209292272, 211369392, 213458480, + 215559568, 217672656, 219797792, 221934976, 224084240, 226245600, 228419056, 230604656, + 232802400, 235012320, 237234432, 239468736, 241715280, 243974080, 246245120, 248528464, + 250824112, 253132064, 255452368, 257785040, 260130080, 262487520, 264857376, 267239664, +}; + +static stbir_uint8 stbir__linear_to_srgb_uchar(float f) +{ + int x = (int) (f * (1 << 28)); // has headroom so you don't need to clamp + int v = 0; + int i; + + // Refine the guess with a short binary search. + i = v + 128; if (x >= stbir__srgb_offset_to_linear_scaled[i]) v = i; + i = v + 64; if (x >= stbir__srgb_offset_to_linear_scaled[i]) v = i; + i = v + 32; if (x >= stbir__srgb_offset_to_linear_scaled[i]) v = i; + i = v + 16; if (x >= stbir__srgb_offset_to_linear_scaled[i]) v = i; + i = v + 8; if (x >= stbir__srgb_offset_to_linear_scaled[i]) v = i; + i = v + 4; if (x >= stbir__srgb_offset_to_linear_scaled[i]) v = i; + i = v + 2; if (x >= stbir__srgb_offset_to_linear_scaled[i]) v = i; + i = v + 1; if (x >= stbir__srgb_offset_to_linear_scaled[i]) v = i; + + return (stbir_uint8) v; +} +#endif + +static float stbir__filter_trapezoid(float x, float scale) +{ + float halfscale = scale / 2; + float t = 0.5f + halfscale; + STBIR_ASSERT(scale <= 1); + + x = (float)fabs(x); + + if (x >= t) + return 0; + else + { + float r = 0.5f - halfscale; + if (x <= r) + return 1; + else + return (t - x) / scale; + } +} + +static float stbir__support_trapezoid(float scale) +{ + STBIR_ASSERT(scale <= 1); + return 0.5f + scale / 2; +} + +static float stbir__filter_triangle(float x, float s) +{ + STBIR__UNUSED_PARAM(s); + + x = (float)fabs(x); + + if (x <= 1.0f) + return 1 - x; + else + return 0; +} + +static float stbir__filter_cubic(float x, float s) +{ + STBIR__UNUSED_PARAM(s); + + x = (float)fabs(x); + + if (x < 1.0f) + return (4 + x*x*(3*x - 6))/6; + else if (x < 2.0f) + return (8 + x*(-12 + x*(6 - x)))/6; + + return (0.0f); +} + +static float stbir__filter_catmullrom(float x, float s) +{ + STBIR__UNUSED_PARAM(s); + + x = (float)fabs(x); + + if (x < 1.0f) + return 1 - x*x*(2.5f - 1.5f*x); + else if (x < 2.0f) + return 2 - x*(4 + x*(0.5f*x - 2.5f)); + + return (0.0f); +} + +static float stbir__filter_mitchell(float x, float s) +{ + STBIR__UNUSED_PARAM(s); + + x = (float)fabs(x); + + if (x < 1.0f) + return (16 + x*x*(21 * x - 36))/18; + else if (x < 2.0f) + return (32 + x*(-60 + x*(36 - 7*x)))/18; + + return (0.0f); +} + +static float stbir__support_zero(float s) +{ + STBIR__UNUSED_PARAM(s); + return 0; +} + +static float stbir__support_one(float s) +{ + STBIR__UNUSED_PARAM(s); + return 1; +} + +static float stbir__support_two(float s) +{ + STBIR__UNUSED_PARAM(s); + return 2; +} + +static stbir__filter_info stbir__filter_info_table[] = { + { NULL, stbir__support_zero }, + { stbir__filter_trapezoid, stbir__support_trapezoid }, + { stbir__filter_triangle, stbir__support_one }, + { stbir__filter_cubic, stbir__support_two }, + { stbir__filter_catmullrom, stbir__support_two }, + { stbir__filter_mitchell, stbir__support_two }, +}; + +stbir__inline static int stbir__use_upsampling(float ratio) +{ + return ratio > 1; +} + +stbir__inline static int stbir__use_width_upsampling(stbir__info* stbir_info) +{ + return stbir__use_upsampling(stbir_info->horizontal_scale); +} + +stbir__inline static int stbir__use_height_upsampling(stbir__info* stbir_info) +{ + return stbir__use_upsampling(stbir_info->vertical_scale); +} + +// This is the maximum number of input samples that can affect an output sample +// with the given filter +static int stbir__get_filter_pixel_width(stbir_filter filter, float scale) +{ + STBIR_ASSERT(filter != 0); + STBIR_ASSERT(filter < STBIR__ARRAY_SIZE(stbir__filter_info_table)); + + if (stbir__use_upsampling(scale)) + return (int)ceil(stbir__filter_info_table[filter].support(1/scale) * 2); + else + return (int)ceil(stbir__filter_info_table[filter].support(scale) * 2 / scale); +} + +// This is how much to expand buffers to account for filters seeking outside +// the image boundaries. +static int stbir__get_filter_pixel_margin(stbir_filter filter, float scale) +{ + return stbir__get_filter_pixel_width(filter, scale) / 2; +} + +static int stbir__get_coefficient_width(stbir_filter filter, float scale) +{ + if (stbir__use_upsampling(scale)) + return (int)ceil(stbir__filter_info_table[filter].support(1 / scale) * 2); + else + return (int)ceil(stbir__filter_info_table[filter].support(scale) * 2); +} + +static int stbir__get_contributors(float scale, stbir_filter filter, int input_size, int output_size) +{ + if (stbir__use_upsampling(scale)) + return output_size; + else + return (input_size + stbir__get_filter_pixel_margin(filter, scale) * 2); +} + +static int stbir__get_total_horizontal_coefficients(stbir__info* info) +{ + return info->horizontal_num_contributors + * stbir__get_coefficient_width (info->horizontal_filter, info->horizontal_scale); +} + +static int stbir__get_total_vertical_coefficients(stbir__info* info) +{ + return info->vertical_num_contributors + * stbir__get_coefficient_width (info->vertical_filter, info->vertical_scale); +} + +static stbir__contributors* stbir__get_contributor(stbir__contributors* contributors, int n) +{ + return &contributors[n]; +} + +// For perf reasons this code is duplicated in stbir__resample_horizontal_upsample/downsample, +// if you change it here change it there too. +static float* stbir__get_coefficient(float* coefficients, stbir_filter filter, float scale, int n, int c) +{ + int width = stbir__get_coefficient_width(filter, scale); + return &coefficients[width*n + c]; +} + +static int stbir__edge_wrap_slow(stbir_edge edge, int n, int max) +{ + switch (edge) + { + case STBIR_EDGE_ZERO: + return 0; // we'll decode the wrong pixel here, and then overwrite with 0s later + + case STBIR_EDGE_CLAMP: + if (n < 0) + return 0; + + if (n >= max) + return max - 1; + + return n; // NOTREACHED + + case STBIR_EDGE_REFLECT: + { + if (n < 0) + { + if (n < max) + return -n; + else + return max - 1; + } + + if (n >= max) + { + int max2 = max * 2; + if (n >= max2) + return 0; + else + return max2 - n - 1; + } + + return n; // NOTREACHED + } + + case STBIR_EDGE_WRAP: + if (n >= 0) + return (n % max); + else + { + int m = (-n) % max; + + if (m != 0) + m = max - m; + + return (m); + } + return n; // NOTREACHED + + default: + STBIR_ASSERT(!"Unimplemented edge type"); + return 0; + } +} + +stbir__inline static int stbir__edge_wrap(stbir_edge edge, int n, int max) +{ + // avoid per-pixel switch + if (n >= 0 && n < max) + return n; + return stbir__edge_wrap_slow(edge, n, max); +} + +// What input pixels contribute to this output pixel? +static void stbir__calculate_sample_range_upsample(int n, float out_filter_radius, float scale_ratio, float out_shift, int* in_first_pixel, int* in_last_pixel, float* in_center_of_out) +{ + float out_pixel_center = (float)n + 0.5f; + float out_pixel_influence_lowerbound = out_pixel_center - out_filter_radius; + float out_pixel_influence_upperbound = out_pixel_center + out_filter_radius; + + float in_pixel_influence_lowerbound = (out_pixel_influence_lowerbound + out_shift) / scale_ratio; + float in_pixel_influence_upperbound = (out_pixel_influence_upperbound + out_shift) / scale_ratio; + + *in_center_of_out = (out_pixel_center + out_shift) / scale_ratio; + *in_first_pixel = (int)(floor(in_pixel_influence_lowerbound + 0.5)); + *in_last_pixel = (int)(floor(in_pixel_influence_upperbound - 0.5)); +} + +// What output pixels does this input pixel contribute to? +static void stbir__calculate_sample_range_downsample(int n, float in_pixels_radius, float scale_ratio, float out_shift, int* out_first_pixel, int* out_last_pixel, float* out_center_of_in) +{ + float in_pixel_center = (float)n + 0.5f; + float in_pixel_influence_lowerbound = in_pixel_center - in_pixels_radius; + float in_pixel_influence_upperbound = in_pixel_center + in_pixels_radius; + + float out_pixel_influence_lowerbound = in_pixel_influence_lowerbound * scale_ratio - out_shift; + float out_pixel_influence_upperbound = in_pixel_influence_upperbound * scale_ratio - out_shift; + + *out_center_of_in = in_pixel_center * scale_ratio - out_shift; + *out_first_pixel = (int)(floor(out_pixel_influence_lowerbound + 0.5)); + *out_last_pixel = (int)(floor(out_pixel_influence_upperbound - 0.5)); +} + +static void stbir__calculate_coefficients_upsample(stbir__info* stbir_info, stbir_filter filter, float scale, int in_first_pixel, int in_last_pixel, float in_center_of_out, stbir__contributors* contributor, float* coefficient_group) +{ + int i; + float total_filter = 0; + float filter_scale; + + STBIR_ASSERT(in_last_pixel - in_first_pixel <= (int)ceil(stbir__filter_info_table[filter].support(1/scale) * 2)); // Taken directly from stbir__get_coefficient_width() which we can't call because we don't know if we're horizontal or vertical. + + contributor->n0 = in_first_pixel; + contributor->n1 = in_last_pixel; + + STBIR_ASSERT(contributor->n1 >= contributor->n0); + + for (i = 0; i <= in_last_pixel - in_first_pixel; i++) + { + float in_pixel_center = (float)(i + in_first_pixel) + 0.5f; + coefficient_group[i] = stbir__filter_info_table[filter].kernel(in_center_of_out - in_pixel_center, 1 / scale); + + // If the coefficient is zero, skip it. (Don't do the <0 check here, we want the influence of those outside pixels.) + if (i == 0 && !coefficient_group[i]) + { + contributor->n0 = ++in_first_pixel; + i--; + continue; + } + + total_filter += coefficient_group[i]; + } + + STBIR_ASSERT(stbir__filter_info_table[filter].kernel((float)(in_last_pixel + 1) + 0.5f - in_center_of_out, 1/scale) == 0); + + STBIR_ASSERT(total_filter > 0.9); + STBIR_ASSERT(total_filter < 1.1f); // Make sure it's not way off. + + // Make sure the sum of all coefficients is 1. + filter_scale = 1 / total_filter; + + for (i = 0; i <= in_last_pixel - in_first_pixel; i++) + coefficient_group[i] *= filter_scale; + + for (i = in_last_pixel - in_first_pixel; i >= 0; i--) + { + if (coefficient_group[i]) + break; + + // This line has no weight. We can skip it. + contributor->n1 = contributor->n0 + i - 1; + } +} + +static void stbir__calculate_coefficients_downsample(stbir__info* stbir_info, stbir_filter filter, float scale_ratio, int out_first_pixel, int out_last_pixel, float out_center_of_in, stbir__contributors* contributor, float* coefficient_group) +{ + int i; + + STBIR_ASSERT(out_last_pixel - out_first_pixel <= (int)ceil(stbir__filter_info_table[filter].support(scale_ratio) * 2)); // Taken directly from stbir__get_coefficient_width() which we can't call because we don't know if we're horizontal or vertical. + + contributor->n0 = out_first_pixel; + contributor->n1 = out_last_pixel; + + STBIR_ASSERT(contributor->n1 >= contributor->n0); + + for (i = 0; i <= out_last_pixel - out_first_pixel; i++) + { + float out_pixel_center = (float)(i + out_first_pixel) + 0.5f; + float x = out_pixel_center - out_center_of_in; + coefficient_group[i] = stbir__filter_info_table[filter].kernel(x, scale_ratio) * scale_ratio; + } + + STBIR_ASSERT(stbir__filter_info_table[filter].kernel((float)(out_last_pixel + 1) + 0.5f - out_center_of_in, scale_ratio) == 0); + + for (i = out_last_pixel - out_first_pixel; i >= 0; i--) + { + if (coefficient_group[i]) + break; + + // This line has no weight. We can skip it. + contributor->n1 = contributor->n0 + i - 1; + } +} + +static void stbir__normalize_downsample_coefficients(stbir__info* stbir_info, stbir__contributors* contributors, float* coefficients, stbir_filter filter, float scale_ratio, float shift, int input_size, int output_size) +{ + int num_contributors = stbir__get_contributors(scale_ratio, filter, input_size, output_size); + int num_coefficients = stbir__get_coefficient_width(filter, scale_ratio); + int i, j; + int skip; + + for (i = 0; i < output_size; i++) + { + float scale; + float total = 0; + + for (j = 0; j < num_contributors; j++) + { + if (i >= contributors[j].n0 && i <= contributors[j].n1) + { + float coefficient = *stbir__get_coefficient(coefficients, filter, scale_ratio, j, i - contributors[j].n0); + total += coefficient; + } + else if (i < contributors[j].n0) + break; + } + + STBIR_ASSERT(total > 0.9f); + STBIR_ASSERT(total < 1.1f); + + scale = 1 / total; + + for (j = 0; j < num_contributors; j++) + { + if (i >= contributors[j].n0 && i <= contributors[j].n1) + *stbir__get_coefficient(coefficients, filter, scale_ratio, j, i - contributors[j].n0) *= scale; + else if (i < contributors[j].n0) + break; + } + } + + // Optimize: Skip zero coefficients and contributions outside of image bounds. + // Do this after normalizing because normalization depends on the n0/n1 values. + for (j = 0; j < num_contributors; j++) + { + int range, max, width; + + skip = 0; + while (*stbir__get_coefficient(coefficients, filter, scale_ratio, j, skip) == 0) + skip++; + + contributors[j].n0 += skip; + + while (contributors[j].n0 < 0) + { + contributors[j].n0++; + skip++; + } + + range = contributors[j].n1 - contributors[j].n0 + 1; + max = stbir__min(num_coefficients, range); + + width = stbir__get_coefficient_width(filter, scale_ratio); + for (i = 0; i < max; i++) + { + if (i + skip >= width) + break; + + *stbir__get_coefficient(coefficients, filter, scale_ratio, j, i) = *stbir__get_coefficient(coefficients, filter, scale_ratio, j, i + skip); + } + + continue; + } + + // Using min to avoid writing into invalid pixels. + for (i = 0; i < num_contributors; i++) + contributors[i].n1 = stbir__min(contributors[i].n1, output_size - 1); +} + +// Each scan line uses the same kernel values so we should calculate the kernel +// values once and then we can use them for every scan line. +static void stbir__calculate_filters(stbir__info* stbir_info, stbir__contributors* contributors, float* coefficients, stbir_filter filter, float scale_ratio, float shift, int input_size, int output_size) +{ + int n; + int total_contributors = stbir__get_contributors(scale_ratio, filter, input_size, output_size); + + if (stbir__use_upsampling(scale_ratio)) + { + float out_pixels_radius = stbir__filter_info_table[filter].support(1 / scale_ratio) * scale_ratio; + + // Looping through out pixels + for (n = 0; n < total_contributors; n++) + { + float in_center_of_out; // Center of the current out pixel in the in pixel space + int in_first_pixel, in_last_pixel; + + stbir__calculate_sample_range_upsample(n, out_pixels_radius, scale_ratio, shift, &in_first_pixel, &in_last_pixel, &in_center_of_out); + + stbir__calculate_coefficients_upsample(stbir_info, filter, scale_ratio, in_first_pixel, in_last_pixel, in_center_of_out, stbir__get_contributor(contributors, n), stbir__get_coefficient(coefficients, filter, scale_ratio, n, 0)); + } + } + else + { + float in_pixels_radius = stbir__filter_info_table[filter].support(scale_ratio) / scale_ratio; + + // Looping through in pixels + for (n = 0; n < total_contributors; n++) + { + float out_center_of_in; // Center of the current out pixel in the in pixel space + int out_first_pixel, out_last_pixel; + int n_adjusted = n - stbir__get_filter_pixel_margin(filter, scale_ratio); + + stbir__calculate_sample_range_downsample(n_adjusted, in_pixels_radius, scale_ratio, shift, &out_first_pixel, &out_last_pixel, &out_center_of_in); + + stbir__calculate_coefficients_downsample(stbir_info, filter, scale_ratio, out_first_pixel, out_last_pixel, out_center_of_in, stbir__get_contributor(contributors, n), stbir__get_coefficient(coefficients, filter, scale_ratio, n, 0)); + } + + stbir__normalize_downsample_coefficients(stbir_info, contributors, coefficients, filter, scale_ratio, shift, input_size, output_size); + } +} + +static float* stbir__get_decode_buffer(stbir__info* stbir_info) +{ + // The 0 index of the decode buffer starts after the margin. This makes + // it okay to use negative indexes on the decode buffer. + return &stbir_info->decode_buffer[stbir_info->horizontal_filter_pixel_margin * stbir_info->channels]; +} + +#define STBIR__DECODE(type, colorspace) ((type) * (STBIR_MAX_COLORSPACES) + (colorspace)) + +static void stbir__decode_scanline(stbir__info* stbir_info, int n) +{ + int c; + int channels = stbir_info->channels; + int alpha_channel = stbir_info->alpha_channel; + int type = stbir_info->type; + int colorspace = stbir_info->colorspace; + int input_w = stbir_info->input_w; + int input_stride_bytes = stbir_info->input_stride_bytes; + float* decode_buffer = stbir__get_decode_buffer(stbir_info); + stbir_edge edge_horizontal = stbir_info->edge_horizontal; + stbir_edge edge_vertical = stbir_info->edge_vertical; + int in_buffer_row_offset = stbir__edge_wrap(edge_vertical, n, stbir_info->input_h) * input_stride_bytes; + const void* input_data = (char *) stbir_info->input_data + in_buffer_row_offset; + int max_x = input_w + stbir_info->horizontal_filter_pixel_margin; + int decode = STBIR__DECODE(type, colorspace); + + int x = -stbir_info->horizontal_filter_pixel_margin; + + // special handling for STBIR_EDGE_ZERO because it needs to return an item that doesn't appear in the input, + // and we want to avoid paying overhead on every pixel if not STBIR_EDGE_ZERO + if (edge_vertical == STBIR_EDGE_ZERO && (n < 0 || n >= stbir_info->input_h)) + { + for (; x < max_x; x++) + for (c = 0; c < channels; c++) + decode_buffer[x*channels + c] = 0; + return; + } + + switch (decode) + { + case STBIR__DECODE(STBIR_TYPE_UINT8, STBIR_COLORSPACE_LINEAR): + for (; x < max_x; x++) + { + int decode_pixel_index = x * channels; + int input_pixel_index = stbir__edge_wrap(edge_horizontal, x, input_w) * channels; + for (c = 0; c < channels; c++) + decode_buffer[decode_pixel_index + c] = ((float)((const unsigned char*)input_data)[input_pixel_index + c]) / 255; + } + break; + + case STBIR__DECODE(STBIR_TYPE_UINT8, STBIR_COLORSPACE_SRGB): + for (; x < max_x; x++) + { + int decode_pixel_index = x * channels; + int input_pixel_index = stbir__edge_wrap(edge_horizontal, x, input_w) * channels; + for (c = 0; c < channels; c++) + decode_buffer[decode_pixel_index + c] = stbir__srgb_uchar_to_linear_float[((const unsigned char*)input_data)[input_pixel_index + c]]; + + if (!(stbir_info->flags&STBIR_FLAG_ALPHA_USES_COLORSPACE)) + decode_buffer[decode_pixel_index + alpha_channel] = ((float)((const unsigned char*)input_data)[input_pixel_index + alpha_channel]) / 255; + } + break; + + case STBIR__DECODE(STBIR_TYPE_UINT16, STBIR_COLORSPACE_LINEAR): + for (; x < max_x; x++) + { + int decode_pixel_index = x * channels; + int input_pixel_index = stbir__edge_wrap(edge_horizontal, x, input_w) * channels; + for (c = 0; c < channels; c++) + decode_buffer[decode_pixel_index + c] = ((float)((const unsigned short*)input_data)[input_pixel_index + c]) / 65535; + } + break; + + case STBIR__DECODE(STBIR_TYPE_UINT16, STBIR_COLORSPACE_SRGB): + for (; x < max_x; x++) + { + int decode_pixel_index = x * channels; + int input_pixel_index = stbir__edge_wrap(edge_horizontal, x, input_w) * channels; + for (c = 0; c < channels; c++) + decode_buffer[decode_pixel_index + c] = stbir__srgb_to_linear(((float)((const unsigned short*)input_data)[input_pixel_index + c]) / 65535); + + if (!(stbir_info->flags&STBIR_FLAG_ALPHA_USES_COLORSPACE)) + decode_buffer[decode_pixel_index + alpha_channel] = ((float)((const unsigned short*)input_data)[input_pixel_index + alpha_channel]) / 65535; + } + break; + + case STBIR__DECODE(STBIR_TYPE_UINT32, STBIR_COLORSPACE_LINEAR): + for (; x < max_x; x++) + { + int decode_pixel_index = x * channels; + int input_pixel_index = stbir__edge_wrap(edge_horizontal, x, input_w) * channels; + for (c = 0; c < channels; c++) + decode_buffer[decode_pixel_index + c] = (float)(((double)((const unsigned int*)input_data)[input_pixel_index + c]) / 4294967295); + } + break; + + case STBIR__DECODE(STBIR_TYPE_UINT32, STBIR_COLORSPACE_SRGB): + for (; x < max_x; x++) + { + int decode_pixel_index = x * channels; + int input_pixel_index = stbir__edge_wrap(edge_horizontal, x, input_w) * channels; + for (c = 0; c < channels; c++) + decode_buffer[decode_pixel_index + c] = stbir__srgb_to_linear((float)(((double)((const unsigned int*)input_data)[input_pixel_index + c]) / 4294967295)); + + if (!(stbir_info->flags&STBIR_FLAG_ALPHA_USES_COLORSPACE)) + decode_buffer[decode_pixel_index + alpha_channel] = (float)(((double)((const unsigned int*)input_data)[input_pixel_index + alpha_channel]) / 4294967295); + } + break; + + case STBIR__DECODE(STBIR_TYPE_FLOAT, STBIR_COLORSPACE_LINEAR): + for (; x < max_x; x++) + { + int decode_pixel_index = x * channels; + int input_pixel_index = stbir__edge_wrap(edge_horizontal, x, input_w) * channels; + for (c = 0; c < channels; c++) + decode_buffer[decode_pixel_index + c] = ((const float*)input_data)[input_pixel_index + c]; + } + break; + + case STBIR__DECODE(STBIR_TYPE_FLOAT, STBIR_COLORSPACE_SRGB): + for (; x < max_x; x++) + { + int decode_pixel_index = x * channels; + int input_pixel_index = stbir__edge_wrap(edge_horizontal, x, input_w) * channels; + for (c = 0; c < channels; c++) + decode_buffer[decode_pixel_index + c] = stbir__srgb_to_linear(((const float*)input_data)[input_pixel_index + c]); + + if (!(stbir_info->flags&STBIR_FLAG_ALPHA_USES_COLORSPACE)) + decode_buffer[decode_pixel_index + alpha_channel] = ((const float*)input_data)[input_pixel_index + alpha_channel]; + } + + break; + + default: + STBIR_ASSERT(!"Unknown type/colorspace/channels combination."); + break; + } + + if (!(stbir_info->flags & STBIR_FLAG_ALPHA_PREMULTIPLIED)) + { + for (x = -stbir_info->horizontal_filter_pixel_margin; x < max_x; x++) + { + int decode_pixel_index = x * channels; + + // If the alpha value is 0 it will clobber the color values. Make sure it's not. + float alpha = decode_buffer[decode_pixel_index + alpha_channel]; +#ifndef STBIR_NO_ALPHA_EPSILON + if (stbir_info->type != STBIR_TYPE_FLOAT) { + alpha += STBIR_ALPHA_EPSILON; + decode_buffer[decode_pixel_index + alpha_channel] = alpha; + } +#endif + for (c = 0; c < channels; c++) + { + if (c == alpha_channel) + continue; + + decode_buffer[decode_pixel_index + c] *= alpha; + } + } + } + + if (edge_horizontal == STBIR_EDGE_ZERO) + { + for (x = -stbir_info->horizontal_filter_pixel_margin; x < 0; x++) + { + for (c = 0; c < channels; c++) + decode_buffer[x*channels + c] = 0; + } + for (x = input_w; x < max_x; x++) + { + for (c = 0; c < channels; c++) + decode_buffer[x*channels + c] = 0; + } + } +} + +static float* stbir__get_ring_buffer_entry(float* ring_buffer, int index, int ring_buffer_length) +{ + return &ring_buffer[index * ring_buffer_length]; +} + +static float* stbir__add_empty_ring_buffer_entry(stbir__info* stbir_info, int n) +{ + int ring_buffer_index; + float* ring_buffer; + + if (stbir_info->ring_buffer_begin_index < 0) + { + ring_buffer_index = stbir_info->ring_buffer_begin_index = 0; + stbir_info->ring_buffer_first_scanline = n; + } + else + { + ring_buffer_index = (stbir_info->ring_buffer_begin_index + (stbir_info->ring_buffer_last_scanline - stbir_info->ring_buffer_first_scanline) + 1) % stbir_info->vertical_filter_pixel_width; + STBIR_ASSERT(ring_buffer_index != stbir_info->ring_buffer_begin_index); + } + + ring_buffer = stbir__get_ring_buffer_entry(stbir_info->ring_buffer, ring_buffer_index, stbir_info->ring_buffer_length_bytes / sizeof(float)); + memset(ring_buffer, 0, stbir_info->ring_buffer_length_bytes); + + stbir_info->ring_buffer_last_scanline = n; + + return ring_buffer; +} + + +static void stbir__resample_horizontal_upsample(stbir__info* stbir_info, int n, float* output_buffer) +{ + int x, k; + int output_w = stbir_info->output_w; + int kernel_pixel_width = stbir_info->horizontal_filter_pixel_width; + int channels = stbir_info->channels; + float* decode_buffer = stbir__get_decode_buffer(stbir_info); + stbir__contributors* horizontal_contributors = stbir_info->horizontal_contributors; + float* horizontal_coefficients = stbir_info->horizontal_coefficients; + int coefficient_width = stbir_info->horizontal_coefficient_width; + + for (x = 0; x < output_w; x++) + { + int n0 = horizontal_contributors[x].n0; + int n1 = horizontal_contributors[x].n1; + + int out_pixel_index = x * channels; + int coefficient_group = coefficient_width * x; + int coefficient_counter = 0; + + STBIR_ASSERT(n1 >= n0); + STBIR_ASSERT(n0 >= -stbir_info->horizontal_filter_pixel_margin); + STBIR_ASSERT(n1 >= -stbir_info->horizontal_filter_pixel_margin); + STBIR_ASSERT(n0 < stbir_info->input_w + stbir_info->horizontal_filter_pixel_margin); + STBIR_ASSERT(n1 < stbir_info->input_w + stbir_info->horizontal_filter_pixel_margin); + + switch (channels) { + case 1: + for (k = n0; k <= n1; k++) + { + int in_pixel_index = k * 1; + float coefficient = horizontal_coefficients[coefficient_group + coefficient_counter++]; + STBIR_ASSERT(coefficient != 0); + output_buffer[out_pixel_index + 0] += decode_buffer[in_pixel_index + 0] * coefficient; + } + break; + case 2: + for (k = n0; k <= n1; k++) + { + int in_pixel_index = k * 2; + float coefficient = horizontal_coefficients[coefficient_group + coefficient_counter++]; + STBIR_ASSERT(coefficient != 0); + output_buffer[out_pixel_index + 0] += decode_buffer[in_pixel_index + 0] * coefficient; + output_buffer[out_pixel_index + 1] += decode_buffer[in_pixel_index + 1] * coefficient; + } + break; + case 3: + for (k = n0; k <= n1; k++) + { + int in_pixel_index = k * 3; + float coefficient = horizontal_coefficients[coefficient_group + coefficient_counter++]; + STBIR_ASSERT(coefficient != 0); + output_buffer[out_pixel_index + 0] += decode_buffer[in_pixel_index + 0] * coefficient; + output_buffer[out_pixel_index + 1] += decode_buffer[in_pixel_index + 1] * coefficient; + output_buffer[out_pixel_index + 2] += decode_buffer[in_pixel_index + 2] * coefficient; + } + break; + case 4: + for (k = n0; k <= n1; k++) + { + int in_pixel_index = k * 4; + float coefficient = horizontal_coefficients[coefficient_group + coefficient_counter++]; + STBIR_ASSERT(coefficient != 0); + output_buffer[out_pixel_index + 0] += decode_buffer[in_pixel_index + 0] * coefficient; + output_buffer[out_pixel_index + 1] += decode_buffer[in_pixel_index + 1] * coefficient; + output_buffer[out_pixel_index + 2] += decode_buffer[in_pixel_index + 2] * coefficient; + output_buffer[out_pixel_index + 3] += decode_buffer[in_pixel_index + 3] * coefficient; + } + break; + default: + for (k = n0; k <= n1; k++) + { + int in_pixel_index = k * channels; + float coefficient = horizontal_coefficients[coefficient_group + coefficient_counter++]; + int c; + STBIR_ASSERT(coefficient != 0); + for (c = 0; c < channels; c++) + output_buffer[out_pixel_index + c] += decode_buffer[in_pixel_index + c] * coefficient; + } + break; + } + } +} + +static void stbir__resample_horizontal_downsample(stbir__info* stbir_info, int n, float* output_buffer) +{ + int x, k; + int input_w = stbir_info->input_w; + int output_w = stbir_info->output_w; + int kernel_pixel_width = stbir_info->horizontal_filter_pixel_width; + int channels = stbir_info->channels; + float* decode_buffer = stbir__get_decode_buffer(stbir_info); + stbir__contributors* horizontal_contributors = stbir_info->horizontal_contributors; + float* horizontal_coefficients = stbir_info->horizontal_coefficients; + int coefficient_width = stbir_info->horizontal_coefficient_width; + int filter_pixel_margin = stbir_info->horizontal_filter_pixel_margin; + int max_x = input_w + filter_pixel_margin * 2; + + STBIR_ASSERT(!stbir__use_width_upsampling(stbir_info)); + + switch (channels) { + case 1: + for (x = 0; x < max_x; x++) + { + int n0 = horizontal_contributors[x].n0; + int n1 = horizontal_contributors[x].n1; + + int in_x = x - filter_pixel_margin; + int in_pixel_index = in_x * 1; + int max_n = n1; + int coefficient_group = coefficient_width * x; + + for (k = n0; k <= max_n; k++) + { + int out_pixel_index = k * 1; + float coefficient = horizontal_coefficients[coefficient_group + k - n0]; + STBIR_ASSERT(coefficient != 0); + output_buffer[out_pixel_index + 0] += decode_buffer[in_pixel_index + 0] * coefficient; + } + } + break; + + case 2: + for (x = 0; x < max_x; x++) + { + int n0 = horizontal_contributors[x].n0; + int n1 = horizontal_contributors[x].n1; + + int in_x = x - filter_pixel_margin; + int in_pixel_index = in_x * 2; + int max_n = n1; + int coefficient_group = coefficient_width * x; + + for (k = n0; k <= max_n; k++) + { + int out_pixel_index = k * 2; + float coefficient = horizontal_coefficients[coefficient_group + k - n0]; + STBIR_ASSERT(coefficient != 0); + output_buffer[out_pixel_index + 0] += decode_buffer[in_pixel_index + 0] * coefficient; + output_buffer[out_pixel_index + 1] += decode_buffer[in_pixel_index + 1] * coefficient; + } + } + break; + + case 3: + for (x = 0; x < max_x; x++) + { + int n0 = horizontal_contributors[x].n0; + int n1 = horizontal_contributors[x].n1; + + int in_x = x - filter_pixel_margin; + int in_pixel_index = in_x * 3; + int max_n = n1; + int coefficient_group = coefficient_width * x; + + for (k = n0; k <= max_n; k++) + { + int out_pixel_index = k * 3; + float coefficient = horizontal_coefficients[coefficient_group + k - n0]; + STBIR_ASSERT(coefficient != 0); + output_buffer[out_pixel_index + 0] += decode_buffer[in_pixel_index + 0] * coefficient; + output_buffer[out_pixel_index + 1] += decode_buffer[in_pixel_index + 1] * coefficient; + output_buffer[out_pixel_index + 2] += decode_buffer[in_pixel_index + 2] * coefficient; + } + } + break; + + case 4: + for (x = 0; x < max_x; x++) + { + int n0 = horizontal_contributors[x].n0; + int n1 = horizontal_contributors[x].n1; + + int in_x = x - filter_pixel_margin; + int in_pixel_index = in_x * 4; + int max_n = n1; + int coefficient_group = coefficient_width * x; + + for (k = n0; k <= max_n; k++) + { + int out_pixel_index = k * 4; + float coefficient = horizontal_coefficients[coefficient_group + k - n0]; + STBIR_ASSERT(coefficient != 0); + output_buffer[out_pixel_index + 0] += decode_buffer[in_pixel_index + 0] * coefficient; + output_buffer[out_pixel_index + 1] += decode_buffer[in_pixel_index + 1] * coefficient; + output_buffer[out_pixel_index + 2] += decode_buffer[in_pixel_index + 2] * coefficient; + output_buffer[out_pixel_index + 3] += decode_buffer[in_pixel_index + 3] * coefficient; + } + } + break; + + default: + for (x = 0; x < max_x; x++) + { + int n0 = horizontal_contributors[x].n0; + int n1 = horizontal_contributors[x].n1; + + int in_x = x - filter_pixel_margin; + int in_pixel_index = in_x * channels; + int max_n = n1; + int coefficient_group = coefficient_width * x; + + for (k = n0; k <= max_n; k++) + { + int c; + int out_pixel_index = k * channels; + float coefficient = horizontal_coefficients[coefficient_group + k - n0]; + STBIR_ASSERT(coefficient != 0); + for (c = 0; c < channels; c++) + output_buffer[out_pixel_index + c] += decode_buffer[in_pixel_index + c] * coefficient; + } + } + break; + } +} + +static void stbir__decode_and_resample_upsample(stbir__info* stbir_info, int n) +{ + // Decode the nth scanline from the source image into the decode buffer. + stbir__decode_scanline(stbir_info, n); + + // Now resample it into the ring buffer. + if (stbir__use_width_upsampling(stbir_info)) + stbir__resample_horizontal_upsample(stbir_info, n, stbir__add_empty_ring_buffer_entry(stbir_info, n)); + else + stbir__resample_horizontal_downsample(stbir_info, n, stbir__add_empty_ring_buffer_entry(stbir_info, n)); + + // Now it's sitting in the ring buffer ready to be used as source for the vertical sampling. +} + +static void stbir__decode_and_resample_downsample(stbir__info* stbir_info, int n) +{ + // Decode the nth scanline from the source image into the decode buffer. + stbir__decode_scanline(stbir_info, n); + + memset(stbir_info->horizontal_buffer, 0, stbir_info->output_w * stbir_info->channels * sizeof(float)); + + // Now resample it into the horizontal buffer. + if (stbir__use_width_upsampling(stbir_info)) + stbir__resample_horizontal_upsample(stbir_info, n, stbir_info->horizontal_buffer); + else + stbir__resample_horizontal_downsample(stbir_info, n, stbir_info->horizontal_buffer); + + // Now it's sitting in the horizontal buffer ready to be distributed into the ring buffers. +} + +// Get the specified scan line from the ring buffer. +static float* stbir__get_ring_buffer_scanline(int get_scanline, float* ring_buffer, int begin_index, int first_scanline, int ring_buffer_size, int ring_buffer_length) +{ + int ring_buffer_index = (begin_index + (get_scanline - first_scanline)) % ring_buffer_size; + return stbir__get_ring_buffer_entry(ring_buffer, ring_buffer_index, ring_buffer_length); +} + + +static void stbir__encode_scanline(stbir__info* stbir_info, int num_pixels, void *output_buffer, float *encode_buffer, int channels, int alpha_channel, int decode) +{ + int x; + int n; + int num_nonalpha; + stbir_uint16 nonalpha[STBIR_MAX_CHANNELS]; + + if (!(stbir_info->flags&STBIR_FLAG_ALPHA_PREMULTIPLIED)) + { + for (x=0; x < num_pixels; ++x) + { + int pixel_index = x*channels; + + float alpha = encode_buffer[pixel_index + alpha_channel]; + float reciprocal_alpha = alpha ? 1.0f / alpha : 0; + + // unrolling this produced a 1% slowdown upscaling a large RGBA linear-space image on my machine - stb + for (n = 0; n < channels; n++) + if (n != alpha_channel) + encode_buffer[pixel_index + n] *= reciprocal_alpha; + + // We added in a small epsilon to prevent the color channel from being deleted with zero alpha. + // Because we only add it for integer types, it will automatically be discarded on integer + // conversion, so we don't need to subtract it back out (which would be problematic for + // numeric precision reasons). + } + } + + // build a table of all channels that need colorspace correction, so + // we don't perform colorspace correction on channels that don't need it. + for (x=0, num_nonalpha=0; x < channels; ++x) + if (x != alpha_channel || (stbir_info->flags & STBIR_FLAG_ALPHA_USES_COLORSPACE)) + nonalpha[num_nonalpha++] = x; + + #define STBIR__ROUND_INT(f) ((int) ((f)+0.5)) + #define STBIR__ROUND_UINT(f) ((stbir_uint32) ((f)+0.5)) + + #ifdef STBIR__SATURATE_INT + #define STBIR__ENCODE_LINEAR8(f) stbir__saturate8 (STBIR__ROUND_INT((f) * 255 )) + #define STBIR__ENCODE_LINEAR16(f) stbir__saturate16(STBIR__ROUND_INT((f) * 65535)) + #else + #define STBIR__ENCODE_LINEAR8(f) (unsigned char ) STBIR__ROUND_INT(stbir__saturate(f) * 255 ) + #define STBIR__ENCODE_LINEAR16(f) (unsigned short) STBIR__ROUND_INT(stbir__saturate(f) * 65535) + #endif + + switch (decode) + { + case STBIR__DECODE(STBIR_TYPE_UINT8, STBIR_COLORSPACE_LINEAR): + for (x=0; x < num_pixels; ++x) + { + int pixel_index = x*channels; + + for (n = 0; n < channels; n++) + { + int index = pixel_index + n; + ((unsigned char*)output_buffer)[index] = STBIR__ENCODE_LINEAR8(encode_buffer[index]); + } + } + break; + + case STBIR__DECODE(STBIR_TYPE_UINT8, STBIR_COLORSPACE_SRGB): + for (x=0; x < num_pixels; ++x) + { + int pixel_index = x*channels; + + for (n = 0; n < num_nonalpha; n++) + { + int index = pixel_index + nonalpha[n]; + ((unsigned char*)output_buffer)[index] = stbir__linear_to_srgb_uchar(encode_buffer[index]); + } + + if (!(stbir_info->flags & STBIR_FLAG_ALPHA_USES_COLORSPACE)) + ((unsigned char *)output_buffer)[pixel_index + alpha_channel] = STBIR__ENCODE_LINEAR8(encode_buffer[pixel_index+alpha_channel]); + } + break; + + case STBIR__DECODE(STBIR_TYPE_UINT16, STBIR_COLORSPACE_LINEAR): + for (x=0; x < num_pixels; ++x) + { + int pixel_index = x*channels; + + for (n = 0; n < channels; n++) + { + int index = pixel_index + n; + ((unsigned short*)output_buffer)[index] = STBIR__ENCODE_LINEAR16(encode_buffer[index]); + } + } + break; + + case STBIR__DECODE(STBIR_TYPE_UINT16, STBIR_COLORSPACE_SRGB): + for (x=0; x < num_pixels; ++x) + { + int pixel_index = x*channels; + + for (n = 0; n < num_nonalpha; n++) + { + int index = pixel_index + nonalpha[n]; + ((unsigned short*)output_buffer)[index] = (unsigned short)STBIR__ROUND_INT(stbir__linear_to_srgb(stbir__saturate(encode_buffer[index])) * 65535); + } + + if (!(stbir_info->flags&STBIR_FLAG_ALPHA_USES_COLORSPACE)) + ((unsigned short*)output_buffer)[pixel_index + alpha_channel] = STBIR__ENCODE_LINEAR16(encode_buffer[pixel_index + alpha_channel]); + } + + break; + + case STBIR__DECODE(STBIR_TYPE_UINT32, STBIR_COLORSPACE_LINEAR): + for (x=0; x < num_pixels; ++x) + { + int pixel_index = x*channels; + + for (n = 0; n < channels; n++) + { + int index = pixel_index + n; + ((unsigned int*)output_buffer)[index] = (unsigned int)STBIR__ROUND_UINT(((double)stbir__saturate(encode_buffer[index])) * 4294967295); + } + } + break; + + case STBIR__DECODE(STBIR_TYPE_UINT32, STBIR_COLORSPACE_SRGB): + for (x=0; x < num_pixels; ++x) + { + int pixel_index = x*channels; + + for (n = 0; n < num_nonalpha; n++) + { + int index = pixel_index + nonalpha[n]; + ((unsigned int*)output_buffer)[index] = (unsigned int)STBIR__ROUND_UINT(((double)stbir__linear_to_srgb(stbir__saturate(encode_buffer[index]))) * 4294967295); + } + + if (!(stbir_info->flags&STBIR_FLAG_ALPHA_USES_COLORSPACE)) + ((unsigned int*)output_buffer)[pixel_index + alpha_channel] = (unsigned int)STBIR__ROUND_INT(((double)stbir__saturate(encode_buffer[pixel_index + alpha_channel])) * 4294967295); + } + break; + + case STBIR__DECODE(STBIR_TYPE_FLOAT, STBIR_COLORSPACE_LINEAR): + for (x=0; x < num_pixels; ++x) + { + int pixel_index = x*channels; + + for (n = 0; n < channels; n++) + { + int index = pixel_index + n; + ((float*)output_buffer)[index] = encode_buffer[index]; + } + } + break; + + case STBIR__DECODE(STBIR_TYPE_FLOAT, STBIR_COLORSPACE_SRGB): + for (x=0; x < num_pixels; ++x) + { + int pixel_index = x*channels; + + for (n = 0; n < num_nonalpha; n++) + { + int index = pixel_index + nonalpha[n]; + ((float*)output_buffer)[index] = stbir__linear_to_srgb(encode_buffer[index]); + } + + if (!(stbir_info->flags&STBIR_FLAG_ALPHA_USES_COLORSPACE)) + ((float*)output_buffer)[pixel_index + alpha_channel] = encode_buffer[pixel_index + alpha_channel]; + } + break; + + default: + STBIR_ASSERT(!"Unknown type/colorspace/channels combination."); + break; + } +} + +static void stbir__resample_vertical_upsample(stbir__info* stbir_info, int n, int in_first_scanline, int in_last_scanline, float in_center_of_out) +{ + int x, k; + int output_w = stbir_info->output_w; + stbir__contributors* vertical_contributors = stbir_info->vertical_contributors; + float* vertical_coefficients = stbir_info->vertical_coefficients; + int channels = stbir_info->channels; + int alpha_channel = stbir_info->alpha_channel; + int type = stbir_info->type; + int colorspace = stbir_info->colorspace; + int kernel_pixel_width = stbir_info->vertical_filter_pixel_width; + void* output_data = stbir_info->output_data; + float* encode_buffer = stbir_info->encode_buffer; + int decode = STBIR__DECODE(type, colorspace); + int coefficient_width = stbir_info->vertical_coefficient_width; + int coefficient_counter; + int contributor = n; + + float* ring_buffer = stbir_info->ring_buffer; + int ring_buffer_begin_index = stbir_info->ring_buffer_begin_index; + int ring_buffer_first_scanline = stbir_info->ring_buffer_first_scanline; + int ring_buffer_last_scanline = stbir_info->ring_buffer_last_scanline; + int ring_buffer_length = stbir_info->ring_buffer_length_bytes/sizeof(float); + + int n0,n1, output_row_start; + int coefficient_group = coefficient_width * contributor; + + n0 = vertical_contributors[contributor].n0; + n1 = vertical_contributors[contributor].n1; + + output_row_start = n * stbir_info->output_stride_bytes; + + STBIR_ASSERT(stbir__use_height_upsampling(stbir_info)); + + memset(encode_buffer, 0, output_w * sizeof(float) * channels); + + // I tried reblocking this for better cache usage of encode_buffer + // (using x_outer, k, x_inner), but it lost speed. -- stb + + coefficient_counter = 0; + switch (channels) { + case 1: + for (k = n0; k <= n1; k++) + { + int coefficient_index = coefficient_counter++; + float* ring_buffer_entry = stbir__get_ring_buffer_scanline(k, ring_buffer, ring_buffer_begin_index, ring_buffer_first_scanline, kernel_pixel_width, ring_buffer_length); + float coefficient = vertical_coefficients[coefficient_group + coefficient_index]; + for (x = 0; x < output_w; ++x) + { + int in_pixel_index = x * 1; + encode_buffer[in_pixel_index + 0] += ring_buffer_entry[in_pixel_index + 0] * coefficient; + } + } + break; + case 2: + for (k = n0; k <= n1; k++) + { + int coefficient_index = coefficient_counter++; + float* ring_buffer_entry = stbir__get_ring_buffer_scanline(k, ring_buffer, ring_buffer_begin_index, ring_buffer_first_scanline, kernel_pixel_width, ring_buffer_length); + float coefficient = vertical_coefficients[coefficient_group + coefficient_index]; + for (x = 0; x < output_w; ++x) + { + int in_pixel_index = x * 2; + encode_buffer[in_pixel_index + 0] += ring_buffer_entry[in_pixel_index + 0] * coefficient; + encode_buffer[in_pixel_index + 1] += ring_buffer_entry[in_pixel_index + 1] * coefficient; + } + } + break; + case 3: + for (k = n0; k <= n1; k++) + { + int coefficient_index = coefficient_counter++; + float* ring_buffer_entry = stbir__get_ring_buffer_scanline(k, ring_buffer, ring_buffer_begin_index, ring_buffer_first_scanline, kernel_pixel_width, ring_buffer_length); + float coefficient = vertical_coefficients[coefficient_group + coefficient_index]; + for (x = 0; x < output_w; ++x) + { + int in_pixel_index = x * 3; + encode_buffer[in_pixel_index + 0] += ring_buffer_entry[in_pixel_index + 0] * coefficient; + encode_buffer[in_pixel_index + 1] += ring_buffer_entry[in_pixel_index + 1] * coefficient; + encode_buffer[in_pixel_index + 2] += ring_buffer_entry[in_pixel_index + 2] * coefficient; + } + } + break; + case 4: + for (k = n0; k <= n1; k++) + { + int coefficient_index = coefficient_counter++; + float* ring_buffer_entry = stbir__get_ring_buffer_scanline(k, ring_buffer, ring_buffer_begin_index, ring_buffer_first_scanline, kernel_pixel_width, ring_buffer_length); + float coefficient = vertical_coefficients[coefficient_group + coefficient_index]; + for (x = 0; x < output_w; ++x) + { + int in_pixel_index = x * 4; + encode_buffer[in_pixel_index + 0] += ring_buffer_entry[in_pixel_index + 0] * coefficient; + encode_buffer[in_pixel_index + 1] += ring_buffer_entry[in_pixel_index + 1] * coefficient; + encode_buffer[in_pixel_index + 2] += ring_buffer_entry[in_pixel_index + 2] * coefficient; + encode_buffer[in_pixel_index + 3] += ring_buffer_entry[in_pixel_index + 3] * coefficient; + } + } + break; + default: + for (k = n0; k <= n1; k++) + { + int coefficient_index = coefficient_counter++; + float* ring_buffer_entry = stbir__get_ring_buffer_scanline(k, ring_buffer, ring_buffer_begin_index, ring_buffer_first_scanline, kernel_pixel_width, ring_buffer_length); + float coefficient = vertical_coefficients[coefficient_group + coefficient_index]; + for (x = 0; x < output_w; ++x) + { + int in_pixel_index = x * channels; + int c; + for (c = 0; c < channels; c++) + encode_buffer[in_pixel_index + c] += ring_buffer_entry[in_pixel_index + c] * coefficient; + } + } + break; + } + stbir__encode_scanline(stbir_info, output_w, (char *) output_data + output_row_start, encode_buffer, channels, alpha_channel, decode); +} + +static void stbir__resample_vertical_downsample(stbir__info* stbir_info, int n, int in_first_scanline, int in_last_scanline, float in_center_of_out) +{ + int x, k; + int output_w = stbir_info->output_w; + int output_h = stbir_info->output_h; + stbir__contributors* vertical_contributors = stbir_info->vertical_contributors; + float* vertical_coefficients = stbir_info->vertical_coefficients; + int channels = stbir_info->channels; + int kernel_pixel_width = stbir_info->vertical_filter_pixel_width; + void* output_data = stbir_info->output_data; + float* horizontal_buffer = stbir_info->horizontal_buffer; + int coefficient_width = stbir_info->vertical_coefficient_width; + int contributor = n + stbir_info->vertical_filter_pixel_margin; + + float* ring_buffer = stbir_info->ring_buffer; + int ring_buffer_begin_index = stbir_info->ring_buffer_begin_index; + int ring_buffer_first_scanline = stbir_info->ring_buffer_first_scanline; + int ring_buffer_last_scanline = stbir_info->ring_buffer_last_scanline; + int ring_buffer_length = stbir_info->ring_buffer_length_bytes/sizeof(float); + int n0,n1; + + n0 = vertical_contributors[contributor].n0; + n1 = vertical_contributors[contributor].n1; + + STBIR_ASSERT(!stbir__use_height_upsampling(stbir_info)); + + for (k = n0; k <= n1; k++) + { + int coefficient_index = k - n0; + int coefficient_group = coefficient_width * contributor; + float coefficient = vertical_coefficients[coefficient_group + coefficient_index]; + + float* ring_buffer_entry = stbir__get_ring_buffer_scanline(k, ring_buffer, ring_buffer_begin_index, ring_buffer_first_scanline, kernel_pixel_width, ring_buffer_length); + + switch (channels) { + case 1: + for (x = 0; x < output_w; x++) + { + int in_pixel_index = x * 1; + ring_buffer_entry[in_pixel_index + 0] += horizontal_buffer[in_pixel_index + 0] * coefficient; + } + break; + case 2: + for (x = 0; x < output_w; x++) + { + int in_pixel_index = x * 2; + ring_buffer_entry[in_pixel_index + 0] += horizontal_buffer[in_pixel_index + 0] * coefficient; + ring_buffer_entry[in_pixel_index + 1] += horizontal_buffer[in_pixel_index + 1] * coefficient; + } + break; + case 3: + for (x = 0; x < output_w; x++) + { + int in_pixel_index = x * 3; + ring_buffer_entry[in_pixel_index + 0] += horizontal_buffer[in_pixel_index + 0] * coefficient; + ring_buffer_entry[in_pixel_index + 1] += horizontal_buffer[in_pixel_index + 1] * coefficient; + ring_buffer_entry[in_pixel_index + 2] += horizontal_buffer[in_pixel_index + 2] * coefficient; + } + break; + case 4: + for (x = 0; x < output_w; x++) + { + int in_pixel_index = x * 4; + ring_buffer_entry[in_pixel_index + 0] += horizontal_buffer[in_pixel_index + 0] * coefficient; + ring_buffer_entry[in_pixel_index + 1] += horizontal_buffer[in_pixel_index + 1] * coefficient; + ring_buffer_entry[in_pixel_index + 2] += horizontal_buffer[in_pixel_index + 2] * coefficient; + ring_buffer_entry[in_pixel_index + 3] += horizontal_buffer[in_pixel_index + 3] * coefficient; + } + break; + default: + for (x = 0; x < output_w; x++) + { + int in_pixel_index = x * channels; + + int c; + for (c = 0; c < channels; c++) + ring_buffer_entry[in_pixel_index + c] += horizontal_buffer[in_pixel_index + c] * coefficient; + } + break; + } + } +} + +static void stbir__buffer_loop_upsample(stbir__info* stbir_info) +{ + int y; + float scale_ratio = stbir_info->vertical_scale; + float out_scanlines_radius = stbir__filter_info_table[stbir_info->vertical_filter].support(1/scale_ratio) * scale_ratio; + + STBIR_ASSERT(stbir__use_height_upsampling(stbir_info)); + + for (y = 0; y < stbir_info->output_h; y++) + { + float in_center_of_out = 0; // Center of the current out scanline in the in scanline space + int in_first_scanline = 0, in_last_scanline = 0; + + stbir__calculate_sample_range_upsample(y, out_scanlines_radius, scale_ratio, stbir_info->vertical_shift, &in_first_scanline, &in_last_scanline, &in_center_of_out); + + STBIR_ASSERT(in_last_scanline - in_first_scanline <= stbir_info->vertical_filter_pixel_width); + + if (stbir_info->ring_buffer_begin_index >= 0) + { + // Get rid of whatever we don't need anymore. + while (in_first_scanline > stbir_info->ring_buffer_first_scanline) + { + if (stbir_info->ring_buffer_first_scanline == stbir_info->ring_buffer_last_scanline) + { + // We just popped the last scanline off the ring buffer. + // Reset it to the empty state. + stbir_info->ring_buffer_begin_index = -1; + stbir_info->ring_buffer_first_scanline = 0; + stbir_info->ring_buffer_last_scanline = 0; + break; + } + else + { + stbir_info->ring_buffer_first_scanline++; + stbir_info->ring_buffer_begin_index = (stbir_info->ring_buffer_begin_index + 1) % stbir_info->vertical_filter_pixel_width; + } + } + } + + // Load in new ones. + if (stbir_info->ring_buffer_begin_index < 0) + stbir__decode_and_resample_upsample(stbir_info, in_first_scanline); + + while (in_last_scanline > stbir_info->ring_buffer_last_scanline) + stbir__decode_and_resample_upsample(stbir_info, stbir_info->ring_buffer_last_scanline + 1); + + // Now all buffers should be ready to write a row of vertical sampling. + stbir__resample_vertical_upsample(stbir_info, y, in_first_scanline, in_last_scanline, in_center_of_out); + + STBIR_PROGRESS_REPORT((float)y / stbir_info->output_h); + } +} + +static void stbir__empty_ring_buffer(stbir__info* stbir_info, int first_necessary_scanline) +{ + int output_stride_bytes = stbir_info->output_stride_bytes; + int channels = stbir_info->channels; + int alpha_channel = stbir_info->alpha_channel; + int type = stbir_info->type; + int colorspace = stbir_info->colorspace; + int output_w = stbir_info->output_w; + void* output_data = stbir_info->output_data; + int decode = STBIR__DECODE(type, colorspace); + + float* ring_buffer = stbir_info->ring_buffer; + int ring_buffer_length = stbir_info->ring_buffer_length_bytes/sizeof(float); + + if (stbir_info->ring_buffer_begin_index >= 0) + { + // Get rid of whatever we don't need anymore. + while (first_necessary_scanline > stbir_info->ring_buffer_first_scanline) + { + if (stbir_info->ring_buffer_first_scanline >= 0 && stbir_info->ring_buffer_first_scanline < stbir_info->output_h) + { + int output_row_start = stbir_info->ring_buffer_first_scanline * output_stride_bytes; + float* ring_buffer_entry = stbir__get_ring_buffer_entry(ring_buffer, stbir_info->ring_buffer_begin_index, ring_buffer_length); + stbir__encode_scanline(stbir_info, output_w, (char *) output_data + output_row_start, ring_buffer_entry, channels, alpha_channel, decode); + STBIR_PROGRESS_REPORT((float)stbir_info->ring_buffer_first_scanline / stbir_info->output_h); + } + + if (stbir_info->ring_buffer_first_scanline == stbir_info->ring_buffer_last_scanline) + { + // We just popped the last scanline off the ring buffer. + // Reset it to the empty state. + stbir_info->ring_buffer_begin_index = -1; + stbir_info->ring_buffer_first_scanline = 0; + stbir_info->ring_buffer_last_scanline = 0; + break; + } + else + { + stbir_info->ring_buffer_first_scanline++; + stbir_info->ring_buffer_begin_index = (stbir_info->ring_buffer_begin_index + 1) % stbir_info->vertical_filter_pixel_width; + } + } + } +} + +static void stbir__buffer_loop_downsample(stbir__info* stbir_info) +{ + int y; + float scale_ratio = stbir_info->vertical_scale; + int output_h = stbir_info->output_h; + float in_pixels_radius = stbir__filter_info_table[stbir_info->vertical_filter].support(scale_ratio) / scale_ratio; + int pixel_margin = stbir_info->vertical_filter_pixel_margin; + int max_y = stbir_info->input_h + pixel_margin; + + STBIR_ASSERT(!stbir__use_height_upsampling(stbir_info)); + + for (y = -pixel_margin; y < max_y; y++) + { + float out_center_of_in; // Center of the current out scanline in the in scanline space + int out_first_scanline, out_last_scanline; + + stbir__calculate_sample_range_downsample(y, in_pixels_radius, scale_ratio, stbir_info->vertical_shift, &out_first_scanline, &out_last_scanline, &out_center_of_in); + + STBIR_ASSERT(out_last_scanline - out_first_scanline <= stbir_info->vertical_filter_pixel_width); + + if (out_last_scanline < 0 || out_first_scanline >= output_h) + continue; + + stbir__empty_ring_buffer(stbir_info, out_first_scanline); + + stbir__decode_and_resample_downsample(stbir_info, y); + + // Load in new ones. + if (stbir_info->ring_buffer_begin_index < 0) + stbir__add_empty_ring_buffer_entry(stbir_info, out_first_scanline); + + while (out_last_scanline > stbir_info->ring_buffer_last_scanline) + stbir__add_empty_ring_buffer_entry(stbir_info, stbir_info->ring_buffer_last_scanline + 1); + + // Now the horizontal buffer is ready to write to all ring buffer rows. + stbir__resample_vertical_downsample(stbir_info, y, out_first_scanline, out_last_scanline, out_center_of_in); + } + + stbir__empty_ring_buffer(stbir_info, stbir_info->output_h); +} + +static void stbir__setup(stbir__info *info, int input_w, int input_h, int output_w, int output_h, int channels) +{ + info->input_w = input_w; + info->input_h = input_h; + info->output_w = output_w; + info->output_h = output_h; + info->channels = channels; +} + +static void stbir__calculate_transform(stbir__info *info, float s0, float t0, float s1, float t1, float *transform) +{ + info->s0 = s0; + info->t0 = t0; + info->s1 = s1; + info->t1 = t1; + + if (transform) + { + info->horizontal_scale = transform[0]; + info->vertical_scale = transform[1]; + info->horizontal_shift = transform[2]; + info->vertical_shift = transform[3]; + } + else + { + info->horizontal_scale = ((float)info->output_w / info->input_w) / (s1 - s0); + info->vertical_scale = ((float)info->output_h / info->input_h) / (t1 - t0); + + info->horizontal_shift = s0 * info->output_w / (s1 - s0); + info->vertical_shift = t0 * info->output_h / (t1 - t0); + } +} + +static void stbir__choose_filter(stbir__info *info, stbir_filter h_filter, stbir_filter v_filter) +{ + if (h_filter == 0) + h_filter = stbir__use_upsampling(info->horizontal_scale) ? STBIR_DEFAULT_FILTER_UPSAMPLE : STBIR_DEFAULT_FILTER_DOWNSAMPLE; + if (v_filter == 0) + v_filter = stbir__use_upsampling(info->vertical_scale) ? STBIR_DEFAULT_FILTER_UPSAMPLE : STBIR_DEFAULT_FILTER_DOWNSAMPLE; + info->horizontal_filter = h_filter; + info->vertical_filter = v_filter; +} + +static stbir_uint32 stbir__calculate_memory(stbir__info *info) +{ + int pixel_margin = stbir__get_filter_pixel_margin(info->horizontal_filter, info->horizontal_scale); + int filter_height = stbir__get_filter_pixel_width(info->vertical_filter, info->vertical_scale); + + info->horizontal_num_contributors = stbir__get_contributors(info->horizontal_scale, info->horizontal_filter, info->input_w, info->output_w); + info->vertical_num_contributors = stbir__get_contributors(info->vertical_scale , info->vertical_filter , info->input_h, info->output_h); + + info->horizontal_contributors_size = info->horizontal_num_contributors * sizeof(stbir__contributors); + info->horizontal_coefficients_size = stbir__get_total_horizontal_coefficients(info) * sizeof(float); + info->vertical_contributors_size = info->vertical_num_contributors * sizeof(stbir__contributors); + info->vertical_coefficients_size = stbir__get_total_vertical_coefficients(info) * sizeof(float); + info->decode_buffer_size = (info->input_w + pixel_margin * 2) * info->channels * sizeof(float); + info->horizontal_buffer_size = info->output_w * info->channels * sizeof(float); + info->ring_buffer_size = info->output_w * info->channels * filter_height * sizeof(float); + info->encode_buffer_size = info->output_w * info->channels * sizeof(float); + + STBIR_ASSERT(info->horizontal_filter != 0); + STBIR_ASSERT(info->horizontal_filter < STBIR__ARRAY_SIZE(stbir__filter_info_table)); // this now happens too late + STBIR_ASSERT(info->vertical_filter != 0); + STBIR_ASSERT(info->vertical_filter < STBIR__ARRAY_SIZE(stbir__filter_info_table)); // this now happens too late + + if (stbir__use_height_upsampling(info)) + // The horizontal buffer is for when we're downsampling the height and we + // can't output the result of sampling the decode buffer directly into the + // ring buffers. + info->horizontal_buffer_size = 0; + else + // The encode buffer is to retain precision in the height upsampling method + // and isn't used when height downsampling. + info->encode_buffer_size = 0; + + return info->horizontal_contributors_size + info->horizontal_coefficients_size + + info->vertical_contributors_size + info->vertical_coefficients_size + + info->decode_buffer_size + info->horizontal_buffer_size + + info->ring_buffer_size + info->encode_buffer_size; +} + +static int stbir__resize_allocated(stbir__info *info, + const void* input_data, int input_stride_in_bytes, + void* output_data, int output_stride_in_bytes, + int alpha_channel, stbir_uint32 flags, stbir_datatype type, + stbir_edge edge_horizontal, stbir_edge edge_vertical, stbir_colorspace colorspace, + void* tempmem, size_t tempmem_size_in_bytes) +{ + size_t memory_required = stbir__calculate_memory(info); + + int width_stride_input = input_stride_in_bytes ? input_stride_in_bytes : info->channels * info->input_w * stbir__type_size[type]; + int width_stride_output = output_stride_in_bytes ? output_stride_in_bytes : info->channels * info->output_w * stbir__type_size[type]; + +#ifdef STBIR_DEBUG_OVERWRITE_TEST +#define OVERWRITE_ARRAY_SIZE 8 + unsigned char overwrite_output_before_pre[OVERWRITE_ARRAY_SIZE]; + unsigned char overwrite_tempmem_before_pre[OVERWRITE_ARRAY_SIZE]; + unsigned char overwrite_output_after_pre[OVERWRITE_ARRAY_SIZE]; + unsigned char overwrite_tempmem_after_pre[OVERWRITE_ARRAY_SIZE]; + + size_t begin_forbidden = width_stride_output * (info->output_h - 1) + info->output_w * info->channels * stbir__type_size[type]; + memcpy(overwrite_output_before_pre, &((unsigned char*)output_data)[-OVERWRITE_ARRAY_SIZE], OVERWRITE_ARRAY_SIZE); + memcpy(overwrite_output_after_pre, &((unsigned char*)output_data)[begin_forbidden], OVERWRITE_ARRAY_SIZE); + memcpy(overwrite_tempmem_before_pre, &((unsigned char*)tempmem)[-OVERWRITE_ARRAY_SIZE], OVERWRITE_ARRAY_SIZE); + memcpy(overwrite_tempmem_after_pre, &((unsigned char*)tempmem)[tempmem_size_in_bytes], OVERWRITE_ARRAY_SIZE); +#endif + + STBIR_ASSERT(info->channels >= 0); + STBIR_ASSERT(info->channels <= STBIR_MAX_CHANNELS); + + if (info->channels < 0 || info->channels > STBIR_MAX_CHANNELS) + return 0; + + STBIR_ASSERT(info->horizontal_filter < STBIR__ARRAY_SIZE(stbir__filter_info_table)); + STBIR_ASSERT(info->vertical_filter < STBIR__ARRAY_SIZE(stbir__filter_info_table)); + + if (info->horizontal_filter >= STBIR__ARRAY_SIZE(stbir__filter_info_table)) + return 0; + if (info->vertical_filter >= STBIR__ARRAY_SIZE(stbir__filter_info_table)) + return 0; + + if (alpha_channel < 0) + flags |= STBIR_FLAG_ALPHA_USES_COLORSPACE | STBIR_FLAG_ALPHA_PREMULTIPLIED; + + if (!(flags&STBIR_FLAG_ALPHA_USES_COLORSPACE) || !(flags&STBIR_FLAG_ALPHA_PREMULTIPLIED)) + STBIR_ASSERT(alpha_channel >= 0 && alpha_channel < info->channels); + + if (alpha_channel >= info->channels) + return 0; + + STBIR_ASSERT(tempmem); + + if (!tempmem) + return 0; + + STBIR_ASSERT(tempmem_size_in_bytes >= memory_required); + + if (tempmem_size_in_bytes < memory_required) + return 0; + + memset(tempmem, 0, tempmem_size_in_bytes); + + info->input_data = input_data; + info->input_stride_bytes = width_stride_input; + + info->output_data = output_data; + info->output_stride_bytes = width_stride_output; + + info->alpha_channel = alpha_channel; + info->flags = flags; + info->type = type; + info->edge_horizontal = edge_horizontal; + info->edge_vertical = edge_vertical; + info->colorspace = colorspace; + + info->horizontal_coefficient_width = stbir__get_coefficient_width (info->horizontal_filter, info->horizontal_scale); + info->vertical_coefficient_width = stbir__get_coefficient_width (info->vertical_filter , info->vertical_scale ); + info->horizontal_filter_pixel_width = stbir__get_filter_pixel_width (info->horizontal_filter, info->horizontal_scale); + info->vertical_filter_pixel_width = stbir__get_filter_pixel_width (info->vertical_filter , info->vertical_scale ); + info->horizontal_filter_pixel_margin = stbir__get_filter_pixel_margin(info->horizontal_filter, info->horizontal_scale); + info->vertical_filter_pixel_margin = stbir__get_filter_pixel_margin(info->vertical_filter , info->vertical_scale ); + + info->ring_buffer_length_bytes = info->output_w * info->channels * sizeof(float); + info->decode_buffer_pixels = info->input_w + info->horizontal_filter_pixel_margin * 2; + +#define STBIR__NEXT_MEMPTR(current, newtype) (newtype*)(((unsigned char*)current) + current##_size) + + info->horizontal_contributors = (stbir__contributors *) tempmem; + info->horizontal_coefficients = STBIR__NEXT_MEMPTR(info->horizontal_contributors, float); + info->vertical_contributors = STBIR__NEXT_MEMPTR(info->horizontal_coefficients, stbir__contributors); + info->vertical_coefficients = STBIR__NEXT_MEMPTR(info->vertical_contributors, float); + info->decode_buffer = STBIR__NEXT_MEMPTR(info->vertical_coefficients, float); + + if (stbir__use_height_upsampling(info)) + { + info->horizontal_buffer = NULL; + info->ring_buffer = STBIR__NEXT_MEMPTR(info->decode_buffer, float); + info->encode_buffer = STBIR__NEXT_MEMPTR(info->ring_buffer, float); + + STBIR_ASSERT((size_t)STBIR__NEXT_MEMPTR(info->encode_buffer, unsigned char) == (size_t)tempmem + tempmem_size_in_bytes); + } + else + { + info->horizontal_buffer = STBIR__NEXT_MEMPTR(info->decode_buffer, float); + info->ring_buffer = STBIR__NEXT_MEMPTR(info->horizontal_buffer, float); + info->encode_buffer = NULL; + + STBIR_ASSERT((size_t)STBIR__NEXT_MEMPTR(info->ring_buffer, unsigned char) == (size_t)tempmem + tempmem_size_in_bytes); + } + +#undef STBIR__NEXT_MEMPTR + + // This signals that the ring buffer is empty + info->ring_buffer_begin_index = -1; + + stbir__calculate_filters(info, info->horizontal_contributors, info->horizontal_coefficients, info->horizontal_filter, info->horizontal_scale, info->horizontal_shift, info->input_w, info->output_w); + stbir__calculate_filters(info, info->vertical_contributors, info->vertical_coefficients, info->vertical_filter, info->vertical_scale, info->vertical_shift, info->input_h, info->output_h); + + STBIR_PROGRESS_REPORT(0); + + if (stbir__use_height_upsampling(info)) + stbir__buffer_loop_upsample(info); + else + stbir__buffer_loop_downsample(info); + + STBIR_PROGRESS_REPORT(1); + +#ifdef STBIR_DEBUG_OVERWRITE_TEST + STBIR_ASSERT(memcmp(overwrite_output_before_pre, &((unsigned char*)output_data)[-OVERWRITE_ARRAY_SIZE], OVERWRITE_ARRAY_SIZE) == 0); + STBIR_ASSERT(memcmp(overwrite_output_after_pre, &((unsigned char*)output_data)[begin_forbidden], OVERWRITE_ARRAY_SIZE) == 0); + STBIR_ASSERT(memcmp(overwrite_tempmem_before_pre, &((unsigned char*)tempmem)[-OVERWRITE_ARRAY_SIZE], OVERWRITE_ARRAY_SIZE) == 0); + STBIR_ASSERT(memcmp(overwrite_tempmem_after_pre, &((unsigned char*)tempmem)[tempmem_size_in_bytes], OVERWRITE_ARRAY_SIZE) == 0); +#endif + + return 1; +} + + +static int stbir__resize_arbitrary( + void *alloc_context, + const void* input_data, int input_w, int input_h, int input_stride_in_bytes, + void* output_data, int output_w, int output_h, int output_stride_in_bytes, + float s0, float t0, float s1, float t1, float *transform, + int channels, int alpha_channel, stbir_uint32 flags, stbir_datatype type, + stbir_filter h_filter, stbir_filter v_filter, + stbir_edge edge_horizontal, stbir_edge edge_vertical, stbir_colorspace colorspace) +{ + stbir__info info; + int result; + size_t memory_required; + void* extra_memory; + + stbir__setup(&info, input_w, input_h, output_w, output_h, channels); + stbir__calculate_transform(&info, s0,t0,s1,t1,transform); + stbir__choose_filter(&info, h_filter, v_filter); + memory_required = stbir__calculate_memory(&info); + extra_memory = STBIR_MALLOC(memory_required, alloc_context); + + if (!extra_memory) + return 0; + + result = stbir__resize_allocated(&info, input_data, input_stride_in_bytes, + output_data, output_stride_in_bytes, + alpha_channel, flags, type, + edge_horizontal, edge_vertical, + colorspace, extra_memory, memory_required); + + STBIR_FREE(extra_memory, alloc_context); + + return result; +} + +STBIRDEF int stbir_resize_uint8( const unsigned char *input_pixels , int input_w , int input_h , int input_stride_in_bytes, + unsigned char *output_pixels, int output_w, int output_h, int output_stride_in_bytes, + int num_channels) +{ + return stbir__resize_arbitrary(NULL, input_pixels, input_w, input_h, input_stride_in_bytes, + output_pixels, output_w, output_h, output_stride_in_bytes, + 0,0,1,1,NULL,num_channels,-1,0, STBIR_TYPE_UINT8, STBIR_FILTER_DEFAULT, STBIR_FILTER_DEFAULT, + STBIR_EDGE_CLAMP, STBIR_EDGE_CLAMP, STBIR_COLORSPACE_LINEAR); +} + +STBIRDEF int stbir_resize_float( const float *input_pixels , int input_w , int input_h , int input_stride_in_bytes, + float *output_pixels, int output_w, int output_h, int output_stride_in_bytes, + int num_channels) +{ + return stbir__resize_arbitrary(NULL, input_pixels, input_w, input_h, input_stride_in_bytes, + output_pixels, output_w, output_h, output_stride_in_bytes, + 0,0,1,1,NULL,num_channels,-1,0, STBIR_TYPE_FLOAT, STBIR_FILTER_DEFAULT, STBIR_FILTER_DEFAULT, + STBIR_EDGE_CLAMP, STBIR_EDGE_CLAMP, STBIR_COLORSPACE_LINEAR); +} + +STBIRDEF int stbir_resize_uint8_srgb(const unsigned char *input_pixels , int input_w , int input_h , int input_stride_in_bytes, + unsigned char *output_pixels, int output_w, int output_h, int output_stride_in_bytes, + int num_channels, int alpha_channel, int flags) +{ + return stbir__resize_arbitrary(NULL, input_pixels, input_w, input_h, input_stride_in_bytes, + output_pixels, output_w, output_h, output_stride_in_bytes, + 0,0,1,1,NULL,num_channels,alpha_channel,flags, STBIR_TYPE_UINT8, STBIR_FILTER_DEFAULT, STBIR_FILTER_DEFAULT, + STBIR_EDGE_CLAMP, STBIR_EDGE_CLAMP, STBIR_COLORSPACE_SRGB); +} + +STBIRDEF int stbir_resize_uint8_srgb_edgemode(const unsigned char *input_pixels , int input_w , int input_h , int input_stride_in_bytes, + unsigned char *output_pixels, int output_w, int output_h, int output_stride_in_bytes, + int num_channels, int alpha_channel, int flags, + stbir_edge edge_wrap_mode) +{ + return stbir__resize_arbitrary(NULL, input_pixels, input_w, input_h, input_stride_in_bytes, + output_pixels, output_w, output_h, output_stride_in_bytes, + 0,0,1,1,NULL,num_channels,alpha_channel,flags, STBIR_TYPE_UINT8, STBIR_FILTER_DEFAULT, STBIR_FILTER_DEFAULT, + edge_wrap_mode, edge_wrap_mode, STBIR_COLORSPACE_SRGB); +} + +STBIRDEF int stbir_resize_uint8_generic( const unsigned char *input_pixels , int input_w , int input_h , int input_stride_in_bytes, + unsigned char *output_pixels, int output_w, int output_h, int output_stride_in_bytes, + int num_channels, int alpha_channel, int flags, + stbir_edge edge_wrap_mode, stbir_filter filter, stbir_colorspace space, + void *alloc_context) +{ + return stbir__resize_arbitrary(alloc_context, input_pixels, input_w, input_h, input_stride_in_bytes, + output_pixels, output_w, output_h, output_stride_in_bytes, + 0,0,1,1,NULL,num_channels,alpha_channel,flags, STBIR_TYPE_UINT8, filter, filter, + edge_wrap_mode, edge_wrap_mode, space); +} + +STBIRDEF int stbir_resize_uint16_generic(const stbir_uint16 *input_pixels , int input_w , int input_h , int input_stride_in_bytes, + stbir_uint16 *output_pixels , int output_w, int output_h, int output_stride_in_bytes, + int num_channels, int alpha_channel, int flags, + stbir_edge edge_wrap_mode, stbir_filter filter, stbir_colorspace space, + void *alloc_context) +{ + return stbir__resize_arbitrary(alloc_context, input_pixels, input_w, input_h, input_stride_in_bytes, + output_pixels, output_w, output_h, output_stride_in_bytes, + 0,0,1,1,NULL,num_channels,alpha_channel,flags, STBIR_TYPE_UINT16, filter, filter, + edge_wrap_mode, edge_wrap_mode, space); +} + + +STBIRDEF int stbir_resize_float_generic( const float *input_pixels , int input_w , int input_h , int input_stride_in_bytes, + float *output_pixels , int output_w, int output_h, int output_stride_in_bytes, + int num_channels, int alpha_channel, int flags, + stbir_edge edge_wrap_mode, stbir_filter filter, stbir_colorspace space, + void *alloc_context) +{ + return stbir__resize_arbitrary(alloc_context, input_pixels, input_w, input_h, input_stride_in_bytes, + output_pixels, output_w, output_h, output_stride_in_bytes, + 0,0,1,1,NULL,num_channels,alpha_channel,flags, STBIR_TYPE_FLOAT, filter, filter, + edge_wrap_mode, edge_wrap_mode, space); +} + + +STBIRDEF int stbir_resize( const void *input_pixels , int input_w , int input_h , int input_stride_in_bytes, + void *output_pixels, int output_w, int output_h, int output_stride_in_bytes, + stbir_datatype datatype, + int num_channels, int alpha_channel, int flags, + stbir_edge edge_mode_horizontal, stbir_edge edge_mode_vertical, + stbir_filter filter_horizontal, stbir_filter filter_vertical, + stbir_colorspace space, void *alloc_context) +{ + return stbir__resize_arbitrary(alloc_context, input_pixels, input_w, input_h, input_stride_in_bytes, + output_pixels, output_w, output_h, output_stride_in_bytes, + 0,0,1,1,NULL,num_channels,alpha_channel,flags, datatype, filter_horizontal, filter_vertical, + edge_mode_horizontal, edge_mode_vertical, space); +} + + +STBIRDEF int stbir_resize_subpixel(const void *input_pixels , int input_w , int input_h , int input_stride_in_bytes, + void *output_pixels, int output_w, int output_h, int output_stride_in_bytes, + stbir_datatype datatype, + int num_channels, int alpha_channel, int flags, + stbir_edge edge_mode_horizontal, stbir_edge edge_mode_vertical, + stbir_filter filter_horizontal, stbir_filter filter_vertical, + stbir_colorspace space, void *alloc_context, + float x_scale, float y_scale, + float x_offset, float y_offset) +{ + float transform[4]; + transform[0] = x_scale; + transform[1] = y_scale; + transform[2] = x_offset; + transform[3] = y_offset; + return stbir__resize_arbitrary(alloc_context, input_pixels, input_w, input_h, input_stride_in_bytes, + output_pixels, output_w, output_h, output_stride_in_bytes, + 0,0,1,1,transform,num_channels,alpha_channel,flags, datatype, filter_horizontal, filter_vertical, + edge_mode_horizontal, edge_mode_vertical, space); +} + +STBIRDEF int stbir_resize_region( const void *input_pixels , int input_w , int input_h , int input_stride_in_bytes, + void *output_pixels, int output_w, int output_h, int output_stride_in_bytes, + stbir_datatype datatype, + int num_channels, int alpha_channel, int flags, + stbir_edge edge_mode_horizontal, stbir_edge edge_mode_vertical, + stbir_filter filter_horizontal, stbir_filter filter_vertical, + stbir_colorspace space, void *alloc_context, + float s0, float t0, float s1, float t1) +{ + return stbir__resize_arbitrary(alloc_context, input_pixels, input_w, input_h, input_stride_in_bytes, + output_pixels, output_w, output_h, output_stride_in_bytes, + s0,t0,s1,t1,NULL,num_channels,alpha_channel,flags, datatype, filter_horizontal, filter_vertical, + edge_mode_horizontal, edge_mode_vertical, space); +} + +#endif // STB_IMAGE_RESIZE_IMPLEMENTATION diff --git a/impeller/third_party/stb/stb/stb_image_write.h b/impeller/third_party/stb/stb/stb_image_write.h new file mode 100644 index 0000000000000..4319c0de1d93d --- /dev/null +++ b/impeller/third_party/stb/stb/stb_image_write.h @@ -0,0 +1,1048 @@ +/* stb_image_write - v1.02 - public domain - http://nothings.org/stb/stb_image_write.h + writes out PNG/BMP/TGA images to C stdio - Sean Barrett 2010-2015 + no warranty implied; use at your own risk + + Before #including, + + #define STB_IMAGE_WRITE_IMPLEMENTATION + + in the file that you want to have the implementation. + + Will probably not work correctly with strict-aliasing optimizations. + +ABOUT: + + This header file is a library for writing images to C stdio. It could be + adapted to write to memory or a general streaming interface; let me know. + + The PNG output is not optimal; it is 20-50% larger than the file + written by a decent optimizing implementation. This library is designed + for source code compactness and simplicity, not optimal image file size + or run-time performance. + +BUILDING: + + You can #define STBIW_ASSERT(x) before the #include to avoid using assert.h. + You can #define STBIW_MALLOC(), STBIW_REALLOC(), and STBIW_FREE() to replace + malloc,realloc,free. + You can define STBIW_MEMMOVE() to replace memmove() + +USAGE: + + There are four functions, one for each image file format: + + int stbi_write_png(char const *filename, int w, int h, int comp, const void *data, int stride_in_bytes); + int stbi_write_bmp(char const *filename, int w, int h, int comp, const void *data); + int stbi_write_tga(char const *filename, int w, int h, int comp, const void *data); + int stbi_write_hdr(char const *filename, int w, int h, int comp, const float *data); + + There are also four equivalent functions that use an arbitrary write function. You are + expected to open/close your file-equivalent before and after calling these: + + int stbi_write_png_to_func(stbi_write_func *func, void *context, int w, int h, int comp, const void *data, int stride_in_bytes); + int stbi_write_bmp_to_func(stbi_write_func *func, void *context, int w, int h, int comp, const void *data); + int stbi_write_tga_to_func(stbi_write_func *func, void *context, int w, int h, int comp, const void *data); + int stbi_write_hdr_to_func(stbi_write_func *func, void *context, int w, int h, int comp, const float *data); + + where the callback is: + void stbi_write_func(void *context, void *data, int size); + + You can define STBI_WRITE_NO_STDIO to disable the file variant of these + functions, so the library will not use stdio.h at all. However, this will + also disable HDR writing, because it requires stdio for formatted output. + + Each function returns 0 on failure and non-0 on success. + + The functions create an image file defined by the parameters. The image + is a rectangle of pixels stored from left-to-right, top-to-bottom. + Each pixel contains 'comp' channels of data stored interleaved with 8-bits + per channel, in the following order: 1=Y, 2=YA, 3=RGB, 4=RGBA. (Y is + monochrome color.) The rectangle is 'w' pixels wide and 'h' pixels tall. + The *data pointer points to the first byte of the top-left-most pixel. + For PNG, "stride_in_bytes" is the distance in bytes from the first byte of + a row of pixels to the first byte of the next row of pixels. + + PNG creates output files with the same number of components as the input. + The BMP format expands Y to RGB in the file format and does not + output alpha. + + PNG supports writing rectangles of data even when the bytes storing rows of + data are not consecutive in memory (e.g. sub-rectangles of a larger image), + by supplying the stride between the beginning of adjacent rows. The other + formats do not. (Thus you cannot write a native-format BMP through the BMP + writer, both because it is in BGR order and because it may have padding + at the end of the line.) + + HDR expects linear float data. Since the format is always 32-bit rgb(e) + data, alpha (if provided) is discarded, and for monochrome data it is + replicated across all three channels. + + TGA supports RLE or non-RLE compressed data. To use non-RLE-compressed + data, set the global variable 'stbi_write_tga_with_rle' to 0. + +CREDITS: + + PNG/BMP/TGA + Sean Barrett + HDR + Baldur Karlsson + TGA monochrome: + Jean-Sebastien Guay + misc enhancements: + Tim Kelsey + TGA RLE + Alan Hickman + initial file IO callback implementation + Emmanuel Julien + bugfixes: + github:Chribba + Guillaume Chereau + github:jry2 + github:romigrou + Sergio Gonzalez + Jonas Karlsson + Filip Wasil + Thatcher Ulrich + +LICENSE + +This software is dual-licensed to the public domain and under the following +license: you are granted a perpetual, irrevocable license to copy, modify, +publish, and distribute this file as you see fit. + +*/ + +#ifndef INCLUDE_STB_IMAGE_WRITE_H +#define INCLUDE_STB_IMAGE_WRITE_H + +#ifdef __cplusplus +extern "C" { +#endif + +#ifdef STB_IMAGE_WRITE_STATIC +#define STBIWDEF static +#else +#define STBIWDEF extern +extern int stbi_write_tga_with_rle; +#endif + +#ifndef STBI_WRITE_NO_STDIO +STBIWDEF int stbi_write_png(char const *filename, int w, int h, int comp, const void *data, int stride_in_bytes); +STBIWDEF int stbi_write_bmp(char const *filename, int w, int h, int comp, const void *data); +STBIWDEF int stbi_write_tga(char const *filename, int w, int h, int comp, const void *data); +STBIWDEF int stbi_write_hdr(char const *filename, int w, int h, int comp, const float *data); +#endif + +typedef void stbi_write_func(void *context, void *data, int size); + +STBIWDEF int stbi_write_png_to_func(stbi_write_func *func, void *context, int w, int h, int comp, const void *data, int stride_in_bytes); +STBIWDEF int stbi_write_bmp_to_func(stbi_write_func *func, void *context, int w, int h, int comp, const void *data); +STBIWDEF int stbi_write_tga_to_func(stbi_write_func *func, void *context, int w, int h, int comp, const void *data); +STBIWDEF int stbi_write_hdr_to_func(stbi_write_func *func, void *context, int w, int h, int comp, const float *data); + +#ifdef __cplusplus +} +#endif + +#endif//INCLUDE_STB_IMAGE_WRITE_H + +#ifdef STB_IMAGE_WRITE_IMPLEMENTATION + +#ifdef _WIN32 + #ifndef _CRT_SECURE_NO_WARNINGS + #define _CRT_SECURE_NO_WARNINGS + #endif + #ifndef _CRT_NONSTDC_NO_DEPRECATE + #define _CRT_NONSTDC_NO_DEPRECATE + #endif +#endif + +#ifndef STBI_WRITE_NO_STDIO +#include +#endif // STBI_WRITE_NO_STDIO + +#include +#include +#include +#include + +#if defined(STBIW_MALLOC) && defined(STBIW_FREE) && (defined(STBIW_REALLOC) || defined(STBIW_REALLOC_SIZED)) +// ok +#elif !defined(STBIW_MALLOC) && !defined(STBIW_FREE) && !defined(STBIW_REALLOC) && !defined(STBIW_REALLOC_SIZED) +// ok +#else +#error "Must define all or none of STBIW_MALLOC, STBIW_FREE, and STBIW_REALLOC (or STBIW_REALLOC_SIZED)." +#endif + +#ifndef STBIW_MALLOC +#define STBIW_MALLOC(sz) malloc(sz) +#define STBIW_REALLOC(p,newsz) realloc(p,newsz) +#define STBIW_FREE(p) free(p) +#endif + +#ifndef STBIW_REALLOC_SIZED +#define STBIW_REALLOC_SIZED(p,oldsz,newsz) STBIW_REALLOC(p,newsz) +#endif + + +#ifndef STBIW_MEMMOVE +#define STBIW_MEMMOVE(a,b,sz) memmove(a,b,sz) +#endif + + +#ifndef STBIW_ASSERT +#include +#define STBIW_ASSERT(x) assert(x) +#endif + +#define STBIW_UCHAR(x) (unsigned char) ((x) & 0xff) + +typedef struct +{ + stbi_write_func *func; + void *context; +} stbi__write_context; + +// initialize a callback-based context +static void stbi__start_write_callbacks(stbi__write_context *s, stbi_write_func *c, void *context) +{ + s->func = c; + s->context = context; +} + +#ifndef STBI_WRITE_NO_STDIO + +static void stbi__stdio_write(void *context, void *data, int size) +{ + fwrite(data,1,size,(FILE*) context); +} + +static int stbi__start_write_file(stbi__write_context *s, const char *filename) +{ + FILE *f = fopen(filename, "wb"); + stbi__start_write_callbacks(s, stbi__stdio_write, (void *) f); + return f != NULL; +} + +static void stbi__end_write_file(stbi__write_context *s) +{ + fclose((FILE *)s->context); +} + +#endif // !STBI_WRITE_NO_STDIO + +typedef unsigned int stbiw_uint32; +typedef int stb_image_write_test[sizeof(stbiw_uint32)==4 ? 1 : -1]; + +#ifdef STB_IMAGE_WRITE_STATIC +static int stbi_write_tga_with_rle = 1; +#else +int stbi_write_tga_with_rle = 1; +#endif + +static void stbiw__writefv(stbi__write_context *s, const char *fmt, va_list v) +{ + while (*fmt) { + switch (*fmt++) { + case ' ': break; + case '1': { unsigned char x = STBIW_UCHAR(va_arg(v, int)); + s->func(s->context,&x,1); + break; } + case '2': { int x = va_arg(v,int); + unsigned char b[2]; + b[0] = STBIW_UCHAR(x); + b[1] = STBIW_UCHAR(x>>8); + s->func(s->context,b,2); + break; } + case '4': { stbiw_uint32 x = va_arg(v,int); + unsigned char b[4]; + b[0]=STBIW_UCHAR(x); + b[1]=STBIW_UCHAR(x>>8); + b[2]=STBIW_UCHAR(x>>16); + b[3]=STBIW_UCHAR(x>>24); + s->func(s->context,b,4); + break; } + default: + STBIW_ASSERT(0); + return; + } + } +} + +static void stbiw__writef(stbi__write_context *s, const char *fmt, ...) +{ + va_list v; + va_start(v, fmt); + stbiw__writefv(s, fmt, v); + va_end(v); +} + +static void stbiw__write3(stbi__write_context *s, unsigned char a, unsigned char b, unsigned char c) +{ + unsigned char arr[3]; + arr[0] = a, arr[1] = b, arr[2] = c; + s->func(s->context, arr, 3); +} + +static void stbiw__write_pixel(stbi__write_context *s, int rgb_dir, int comp, int write_alpha, int expand_mono, unsigned char *d) +{ + unsigned char bg[3] = { 255, 0, 255}, px[3]; + int k; + + if (write_alpha < 0) + s->func(s->context, &d[comp - 1], 1); + + switch (comp) { + case 1: + s->func(s->context,d,1); + break; + case 2: + if (expand_mono) + stbiw__write3(s, d[0], d[0], d[0]); // monochrome bmp + else + s->func(s->context, d, 1); // monochrome TGA + break; + case 4: + if (!write_alpha) { + // composite against pink background + for (k = 0; k < 3; ++k) + px[k] = bg[k] + ((d[k] - bg[k]) * d[3]) / 255; + stbiw__write3(s, px[1 - rgb_dir], px[1], px[1 + rgb_dir]); + break; + } + /* FALLTHROUGH */ + case 3: + stbiw__write3(s, d[1 - rgb_dir], d[1], d[1 + rgb_dir]); + break; + } + if (write_alpha > 0) + s->func(s->context, &d[comp - 1], 1); +} + +static void stbiw__write_pixels(stbi__write_context *s, int rgb_dir, int vdir, int x, int y, int comp, void *data, int write_alpha, int scanline_pad, int expand_mono) +{ + stbiw_uint32 zero = 0; + int i,j, j_end; + + if (y <= 0) + return; + + if (vdir < 0) + j_end = -1, j = y-1; + else + j_end = y, j = 0; + + for (; j != j_end; j += vdir) { + for (i=0; i < x; ++i) { + unsigned char *d = (unsigned char *) data + (j*x+i)*comp; + stbiw__write_pixel(s, rgb_dir, comp, write_alpha, expand_mono, d); + } + s->func(s->context, &zero, scanline_pad); + } +} + +static int stbiw__outfile(stbi__write_context *s, int rgb_dir, int vdir, int x, int y, int comp, int expand_mono, void *data, int alpha, int pad, const char *fmt, ...) +{ + if (y < 0 || x < 0) { + return 0; + } else { + va_list v; + va_start(v, fmt); + stbiw__writefv(s, fmt, v); + va_end(v); + stbiw__write_pixels(s,rgb_dir,vdir,x,y,comp,data,alpha,pad, expand_mono); + return 1; + } +} + +static int stbi_write_bmp_core(stbi__write_context *s, int x, int y, int comp, const void *data) +{ + int pad = (-x*3) & 3; + return stbiw__outfile(s,-1,-1,x,y,comp,1,(void *) data,0,pad, + "11 4 22 4" "4 44 22 444444", + 'B', 'M', 14+40+(x*3+pad)*y, 0,0, 14+40, // file header + 40, x,y, 1,24, 0,0,0,0,0,0); // bitmap header +} + +STBIWDEF int stbi_write_bmp_to_func(stbi_write_func *func, void *context, int x, int y, int comp, const void *data) +{ + stbi__write_context s; + stbi__start_write_callbacks(&s, func, context); + return stbi_write_bmp_core(&s, x, y, comp, data); +} + +#ifndef STBI_WRITE_NO_STDIO +STBIWDEF int stbi_write_bmp(char const *filename, int x, int y, int comp, const void *data) +{ + stbi__write_context s; + if (stbi__start_write_file(&s,filename)) { + int r = stbi_write_bmp_core(&s, x, y, comp, data); + stbi__end_write_file(&s); + return r; + } else + return 0; +} +#endif //!STBI_WRITE_NO_STDIO + +static int stbi_write_tga_core(stbi__write_context *s, int x, int y, int comp, void *data) +{ + int has_alpha = (comp == 2 || comp == 4); + int colorbytes = has_alpha ? comp-1 : comp; + int format = colorbytes < 2 ? 3 : 2; // 3 color channels (RGB/RGBA) = 2, 1 color channel (Y/YA) = 3 + + if (y < 0 || x < 0) + return 0; + + if (!stbi_write_tga_with_rle) { + return stbiw__outfile(s, -1, -1, x, y, comp, 0, (void *) data, has_alpha, 0, + "111 221 2222 11", 0, 0, format, 0, 0, 0, 0, 0, x, y, (colorbytes + has_alpha) * 8, has_alpha * 8); + } else { + int i,j,k; + + stbiw__writef(s, "111 221 2222 11", 0,0,format+8, 0,0,0, 0,0,x,y, (colorbytes + has_alpha) * 8, has_alpha * 8); + + for (j = y - 1; j >= 0; --j) { + unsigned char *row = (unsigned char *) data + j * x * comp; + int len; + + for (i = 0; i < x; i += len) { + unsigned char *begin = row + i * comp; + int diff = 1; + len = 1; + + if (i < x - 1) { + ++len; + diff = memcmp(begin, row + (i + 1) * comp, comp); + if (diff) { + const unsigned char *prev = begin; + for (k = i + 2; k < x && len < 128; ++k) { + if (memcmp(prev, row + k * comp, comp)) { + prev += comp; + ++len; + } else { + --len; + break; + } + } + } else { + for (k = i + 2; k < x && len < 128; ++k) { + if (!memcmp(begin, row + k * comp, comp)) { + ++len; + } else { + break; + } + } + } + } + + if (diff) { + unsigned char header = STBIW_UCHAR(len - 1); + s->func(s->context, &header, 1); + for (k = 0; k < len; ++k) { + stbiw__write_pixel(s, -1, comp, has_alpha, 0, begin + k * comp); + } + } else { + unsigned char header = STBIW_UCHAR(len - 129); + s->func(s->context, &header, 1); + stbiw__write_pixel(s, -1, comp, has_alpha, 0, begin); + } + } + } + } + return 1; +} + +int stbi_write_tga_to_func(stbi_write_func *func, void *context, int x, int y, int comp, const void *data) +{ + stbi__write_context s; + stbi__start_write_callbacks(&s, func, context); + return stbi_write_tga_core(&s, x, y, comp, (void *) data); +} + +#ifndef STBI_WRITE_NO_STDIO +int stbi_write_tga(char const *filename, int x, int y, int comp, const void *data) +{ + stbi__write_context s; + if (stbi__start_write_file(&s,filename)) { + int r = stbi_write_tga_core(&s, x, y, comp, (void *) data); + stbi__end_write_file(&s); + return r; + } else + return 0; +} +#endif + +// ************************************************************************************************* +// Radiance RGBE HDR writer +// by Baldur Karlsson +#ifndef STBI_WRITE_NO_STDIO + +#define stbiw__max(a, b) ((a) > (b) ? (a) : (b)) + +void stbiw__linear_to_rgbe(unsigned char *rgbe, float *linear) +{ + int exponent; + float maxcomp = stbiw__max(linear[0], stbiw__max(linear[1], linear[2])); + + if (maxcomp < 1e-32f) { + rgbe[0] = rgbe[1] = rgbe[2] = rgbe[3] = 0; + } else { + float normalize = (float) frexp(maxcomp, &exponent) * 256.0f/maxcomp; + + rgbe[0] = (unsigned char)(linear[0] * normalize); + rgbe[1] = (unsigned char)(linear[1] * normalize); + rgbe[2] = (unsigned char)(linear[2] * normalize); + rgbe[3] = (unsigned char)(exponent + 128); + } +} + +void stbiw__write_run_data(stbi__write_context *s, int length, unsigned char databyte) +{ + unsigned char lengthbyte = STBIW_UCHAR(length+128); + STBIW_ASSERT(length+128 <= 255); + s->func(s->context, &lengthbyte, 1); + s->func(s->context, &databyte, 1); +} + +void stbiw__write_dump_data(stbi__write_context *s, int length, unsigned char *data) +{ + unsigned char lengthbyte = STBIW_UCHAR(length); + STBIW_ASSERT(length <= 128); // inconsistent with spec but consistent with official code + s->func(s->context, &lengthbyte, 1); + s->func(s->context, data, length); +} + +void stbiw__write_hdr_scanline(stbi__write_context *s, int width, int ncomp, unsigned char *scratch, float *scanline) +{ + unsigned char scanlineheader[4] = { 2, 2, 0, 0 }; + unsigned char rgbe[4]; + float linear[3]; + int x; + + scanlineheader[2] = (width&0xff00)>>8; + scanlineheader[3] = (width&0x00ff); + + /* skip RLE for images too small or large */ + if (width < 8 || width >= 32768) { + for (x=0; x < width; x++) { + switch (ncomp) { + case 4: /* fallthrough */ + case 3: linear[2] = scanline[x*ncomp + 2]; + linear[1] = scanline[x*ncomp + 1]; + linear[0] = scanline[x*ncomp + 0]; + break; + default: + linear[0] = linear[1] = linear[2] = scanline[x*ncomp + 0]; + break; + } + stbiw__linear_to_rgbe(rgbe, linear); + s->func(s->context, rgbe, 4); + } + } else { + int c,r; + /* encode into scratch buffer */ + for (x=0; x < width; x++) { + switch(ncomp) { + case 4: /* fallthrough */ + case 3: linear[2] = scanline[x*ncomp + 2]; + linear[1] = scanline[x*ncomp + 1]; + linear[0] = scanline[x*ncomp + 0]; + break; + default: + linear[0] = linear[1] = linear[2] = scanline[x*ncomp + 0]; + break; + } + stbiw__linear_to_rgbe(rgbe, linear); + scratch[x + width*0] = rgbe[0]; + scratch[x + width*1] = rgbe[1]; + scratch[x + width*2] = rgbe[2]; + scratch[x + width*3] = rgbe[3]; + } + + s->func(s->context, scanlineheader, 4); + + /* RLE each component separately */ + for (c=0; c < 4; c++) { + unsigned char *comp = &scratch[width*c]; + + x = 0; + while (x < width) { + // find first run + r = x; + while (r+2 < width) { + if (comp[r] == comp[r+1] && comp[r] == comp[r+2]) + break; + ++r; + } + if (r+2 >= width) + r = width; + // dump up to first run + while (x < r) { + int len = r-x; + if (len > 128) len = 128; + stbiw__write_dump_data(s, len, &comp[x]); + x += len; + } + // if there's a run, output it + if (r+2 < width) { // same test as what we break out of in search loop, so only true if we break'd + // find next byte after run + while (r < width && comp[r] == comp[x]) + ++r; + // output run up to r + while (x < r) { + int len = r-x; + if (len > 127) len = 127; + stbiw__write_run_data(s, len, comp[x]); + x += len; + } + } + } + } + } +} + +static int stbi_write_hdr_core(stbi__write_context *s, int x, int y, int comp, float *data) +{ + if (y <= 0 || x <= 0 || data == NULL) + return 0; + else { + // Each component is stored separately. Allocate scratch space for full output scanline. + unsigned char *scratch = (unsigned char *) STBIW_MALLOC(x*4); + int i, len; + char buffer[128]; + char header[] = "#?RADIANCE\n# Written by stb_image_write.h\nFORMAT=32-bit_rle_rgbe\n"; + s->func(s->context, header, sizeof(header)-1); + + len = sprintf(buffer, "EXPOSURE= 1.0000000000000\n\n-Y %d +X %d\n", y, x); + s->func(s->context, buffer, len); + + for(i=0; i < y; i++) + stbiw__write_hdr_scanline(s, x, comp, scratch, data + comp*i*x); + STBIW_FREE(scratch); + return 1; + } +} + +int stbi_write_hdr_to_func(stbi_write_func *func, void *context, int x, int y, int comp, const float *data) +{ + stbi__write_context s; + stbi__start_write_callbacks(&s, func, context); + return stbi_write_hdr_core(&s, x, y, comp, (float *) data); +} + +int stbi_write_hdr(char const *filename, int x, int y, int comp, const float *data) +{ + stbi__write_context s; + if (stbi__start_write_file(&s,filename)) { + int r = stbi_write_hdr_core(&s, x, y, comp, (float *) data); + stbi__end_write_file(&s); + return r; + } else + return 0; +} +#endif // STBI_WRITE_NO_STDIO + + +////////////////////////////////////////////////////////////////////////////// +// +// PNG writer +// + +// stretchy buffer; stbiw__sbpush() == vector<>::push_back() -- stbiw__sbcount() == vector<>::size() +#define stbiw__sbraw(a) ((int *) (a) - 2) +#define stbiw__sbm(a) stbiw__sbraw(a)[0] +#define stbiw__sbn(a) stbiw__sbraw(a)[1] + +#define stbiw__sbneedgrow(a,n) ((a)==0 || stbiw__sbn(a)+n >= stbiw__sbm(a)) +#define stbiw__sbmaybegrow(a,n) (stbiw__sbneedgrow(a,(n)) ? stbiw__sbgrow(a,n) : 0) +#define stbiw__sbgrow(a,n) stbiw__sbgrowf((void **) &(a), (n), sizeof(*(a))) + +#define stbiw__sbpush(a, v) (stbiw__sbmaybegrow(a,1), (a)[stbiw__sbn(a)++] = (v)) +#define stbiw__sbcount(a) ((a) ? stbiw__sbn(a) : 0) +#define stbiw__sbfree(a) ((a) ? STBIW_FREE(stbiw__sbraw(a)),0 : 0) + +static void *stbiw__sbgrowf(void **arr, int increment, int itemsize) +{ + int m = *arr ? 2*stbiw__sbm(*arr)+increment : increment+1; + void *p = STBIW_REALLOC_SIZED(*arr ? stbiw__sbraw(*arr) : 0, *arr ? (stbiw__sbm(*arr)*itemsize + sizeof(int)*2) : 0, itemsize * m + sizeof(int)*2); + STBIW_ASSERT(p); + if (p) { + if (!*arr) ((int *) p)[1] = 0; + *arr = (void *) ((int *) p + 2); + stbiw__sbm(*arr) = m; + } + return *arr; +} + +static unsigned char *stbiw__zlib_flushf(unsigned char *data, unsigned int *bitbuffer, int *bitcount) +{ + while (*bitcount >= 8) { + stbiw__sbpush(data, STBIW_UCHAR(*bitbuffer)); + *bitbuffer >>= 8; + *bitcount -= 8; + } + return data; +} + +static int stbiw__zlib_bitrev(int code, int codebits) +{ + int res=0; + while (codebits--) { + res = (res << 1) | (code & 1); + code >>= 1; + } + return res; +} + +static unsigned int stbiw__zlib_countm(unsigned char *a, unsigned char *b, int limit) +{ + int i; + for (i=0; i < limit && i < 258; ++i) + if (a[i] != b[i]) break; + return i; +} + +static unsigned int stbiw__zhash(unsigned char *data) +{ + stbiw_uint32 hash = data[0] + (data[1] << 8) + (data[2] << 16); + hash ^= hash << 3; + hash += hash >> 5; + hash ^= hash << 4; + hash += hash >> 17; + hash ^= hash << 25; + hash += hash >> 6; + return hash; +} + +#define stbiw__zlib_flush() (out = stbiw__zlib_flushf(out, &bitbuf, &bitcount)) +#define stbiw__zlib_add(code,codebits) \ + (bitbuf |= (code) << bitcount, bitcount += (codebits), stbiw__zlib_flush()) +#define stbiw__zlib_huffa(b,c) stbiw__zlib_add(stbiw__zlib_bitrev(b,c),c) +// default huffman tables +#define stbiw__zlib_huff1(n) stbiw__zlib_huffa(0x30 + (n), 8) +#define stbiw__zlib_huff2(n) stbiw__zlib_huffa(0x190 + (n)-144, 9) +#define stbiw__zlib_huff3(n) stbiw__zlib_huffa(0 + (n)-256,7) +#define stbiw__zlib_huff4(n) stbiw__zlib_huffa(0xc0 + (n)-280,8) +#define stbiw__zlib_huff(n) ((n) <= 143 ? stbiw__zlib_huff1(n) : (n) <= 255 ? stbiw__zlib_huff2(n) : (n) <= 279 ? stbiw__zlib_huff3(n) : stbiw__zlib_huff4(n)) +#define stbiw__zlib_huffb(n) ((n) <= 143 ? stbiw__zlib_huff1(n) : stbiw__zlib_huff2(n)) + +#define stbiw__ZHASH 16384 + +unsigned char * stbi_zlib_compress(unsigned char *data, int data_len, int *out_len, int quality) +{ + static unsigned short lengthc[] = { 3,4,5,6,7,8,9,10,11,13,15,17,19,23,27,31,35,43,51,59,67,83,99,115,131,163,195,227,258, 259 }; + static unsigned char lengtheb[]= { 0,0,0,0,0,0,0, 0, 1, 1, 1, 1, 2, 2, 2, 2, 3, 3, 3, 3, 4, 4, 4, 4, 5, 5, 5, 5, 0 }; + static unsigned short distc[] = { 1,2,3,4,5,7,9,13,17,25,33,49,65,97,129,193,257,385,513,769,1025,1537,2049,3073,4097,6145,8193,12289,16385,24577, 32768 }; + static unsigned char disteb[] = { 0,0,0,0,1,1,2,2,3,3,4,4,5,5,6,6,7,7,8,8,9,9,10,10,11,11,12,12,13,13 }; + unsigned int bitbuf=0; + int i,j, bitcount=0; + unsigned char *out = NULL; + unsigned char ***hash_table = (unsigned char***) STBIW_MALLOC(stbiw__ZHASH * sizeof(char**)); + if (quality < 5) quality = 5; + + stbiw__sbpush(out, 0x78); // DEFLATE 32K window + stbiw__sbpush(out, 0x5e); // FLEVEL = 1 + stbiw__zlib_add(1,1); // BFINAL = 1 + stbiw__zlib_add(1,2); // BTYPE = 1 -- fixed huffman + + for (i=0; i < stbiw__ZHASH; ++i) + hash_table[i] = NULL; + + i=0; + while (i < data_len-3) { + // hash next 3 bytes of data to be compressed + int h = stbiw__zhash(data+i)&(stbiw__ZHASH-1), best=3; + unsigned char *bestloc = 0; + unsigned char **hlist = hash_table[h]; + int n = stbiw__sbcount(hlist); + for (j=0; j < n; ++j) { + if (hlist[j]-data > i-32768) { // if entry lies within window + int d = stbiw__zlib_countm(hlist[j], data+i, data_len-i); + if (d >= best) best=d,bestloc=hlist[j]; + } + } + // when hash table entry is too long, delete half the entries + if (hash_table[h] && stbiw__sbn(hash_table[h]) == 2*quality) { + STBIW_MEMMOVE(hash_table[h], hash_table[h]+quality, sizeof(hash_table[h][0])*quality); + stbiw__sbn(hash_table[h]) = quality; + } + stbiw__sbpush(hash_table[h],data+i); + + if (bestloc) { + // "lazy matching" - check match at *next* byte, and if it's better, do cur byte as literal + h = stbiw__zhash(data+i+1)&(stbiw__ZHASH-1); + hlist = hash_table[h]; + n = stbiw__sbcount(hlist); + for (j=0; j < n; ++j) { + if (hlist[j]-data > i-32767) { + int e = stbiw__zlib_countm(hlist[j], data+i+1, data_len-i-1); + if (e > best) { // if next match is better, bail on current match + bestloc = NULL; + break; + } + } + } + } + + if (bestloc) { + int d = (int) (data+i - bestloc); // distance back + STBIW_ASSERT(d <= 32767 && best <= 258); + for (j=0; best > lengthc[j+1]-1; ++j); + stbiw__zlib_huff(j+257); + if (lengtheb[j]) stbiw__zlib_add(best - lengthc[j], lengtheb[j]); + for (j=0; d > distc[j+1]-1; ++j); + stbiw__zlib_add(stbiw__zlib_bitrev(j,5),5); + if (disteb[j]) stbiw__zlib_add(d - distc[j], disteb[j]); + i += best; + } else { + stbiw__zlib_huffb(data[i]); + ++i; + } + } + // write out final bytes + for (;i < data_len; ++i) + stbiw__zlib_huffb(data[i]); + stbiw__zlib_huff(256); // end of block + // pad with 0 bits to byte boundary + while (bitcount) + stbiw__zlib_add(0,1); + + for (i=0; i < stbiw__ZHASH; ++i) + (void) stbiw__sbfree(hash_table[i]); + STBIW_FREE(hash_table); + + { + // compute adler32 on input + unsigned int s1=1, s2=0; + int blocklen = (int) (data_len % 5552); + j=0; + while (j < data_len) { + for (i=0; i < blocklen; ++i) s1 += data[j+i], s2 += s1; + s1 %= 65521, s2 %= 65521; + j += blocklen; + blocklen = 5552; + } + stbiw__sbpush(out, STBIW_UCHAR(s2 >> 8)); + stbiw__sbpush(out, STBIW_UCHAR(s2)); + stbiw__sbpush(out, STBIW_UCHAR(s1 >> 8)); + stbiw__sbpush(out, STBIW_UCHAR(s1)); + } + *out_len = stbiw__sbn(out); + // make returned pointer freeable + STBIW_MEMMOVE(stbiw__sbraw(out), out, *out_len); + return (unsigned char *) stbiw__sbraw(out); +} + +static unsigned int stbiw__crc32(unsigned char *buffer, int len) +{ + static unsigned int crc_table[256] = + { + 0x00000000, 0x77073096, 0xEE0E612C, 0x990951BA, 0x076DC419, 0x706AF48F, 0xE963A535, 0x9E6495A3, + 0x0eDB8832, 0x79DCB8A4, 0xE0D5E91E, 0x97D2D988, 0x09B64C2B, 0x7EB17CBD, 0xE7B82D07, 0x90BF1D91, + 0x1DB71064, 0x6AB020F2, 0xF3B97148, 0x84BE41DE, 0x1ADAD47D, 0x6DDDE4EB, 0xF4D4B551, 0x83D385C7, + 0x136C9856, 0x646BA8C0, 0xFD62F97A, 0x8A65C9EC, 0x14015C4F, 0x63066CD9, 0xFA0F3D63, 0x8D080DF5, + 0x3B6E20C8, 0x4C69105E, 0xD56041E4, 0xA2677172, 0x3C03E4D1, 0x4B04D447, 0xD20D85FD, 0xA50AB56B, + 0x35B5A8FA, 0x42B2986C, 0xDBBBC9D6, 0xACBCF940, 0x32D86CE3, 0x45DF5C75, 0xDCD60DCF, 0xABD13D59, + 0x26D930AC, 0x51DE003A, 0xC8D75180, 0xBFD06116, 0x21B4F4B5, 0x56B3C423, 0xCFBA9599, 0xB8BDA50F, + 0x2802B89E, 0x5F058808, 0xC60CD9B2, 0xB10BE924, 0x2F6F7C87, 0x58684C11, 0xC1611DAB, 0xB6662D3D, + 0x76DC4190, 0x01DB7106, 0x98D220BC, 0xEFD5102A, 0x71B18589, 0x06B6B51F, 0x9FBFE4A5, 0xE8B8D433, + 0x7807C9A2, 0x0F00F934, 0x9609A88E, 0xE10E9818, 0x7F6A0DBB, 0x086D3D2D, 0x91646C97, 0xE6635C01, + 0x6B6B51F4, 0x1C6C6162, 0x856530D8, 0xF262004E, 0x6C0695ED, 0x1B01A57B, 0x8208F4C1, 0xF50FC457, + 0x65B0D9C6, 0x12B7E950, 0x8BBEB8EA, 0xFCB9887C, 0x62DD1DDF, 0x15DA2D49, 0x8CD37CF3, 0xFBD44C65, + 0x4DB26158, 0x3AB551CE, 0xA3BC0074, 0xD4BB30E2, 0x4ADFA541, 0x3DD895D7, 0xA4D1C46D, 0xD3D6F4FB, + 0x4369E96A, 0x346ED9FC, 0xAD678846, 0xDA60B8D0, 0x44042D73, 0x33031DE5, 0xAA0A4C5F, 0xDD0D7CC9, + 0x5005713C, 0x270241AA, 0xBE0B1010, 0xC90C2086, 0x5768B525, 0x206F85B3, 0xB966D409, 0xCE61E49F, + 0x5EDEF90E, 0x29D9C998, 0xB0D09822, 0xC7D7A8B4, 0x59B33D17, 0x2EB40D81, 0xB7BD5C3B, 0xC0BA6CAD, + 0xEDB88320, 0x9ABFB3B6, 0x03B6E20C, 0x74B1D29A, 0xEAD54739, 0x9DD277AF, 0x04DB2615, 0x73DC1683, + 0xE3630B12, 0x94643B84, 0x0D6D6A3E, 0x7A6A5AA8, 0xE40ECF0B, 0x9309FF9D, 0x0A00AE27, 0x7D079EB1, + 0xF00F9344, 0x8708A3D2, 0x1E01F268, 0x6906C2FE, 0xF762575D, 0x806567CB, 0x196C3671, 0x6E6B06E7, + 0xFED41B76, 0x89D32BE0, 0x10DA7A5A, 0x67DD4ACC, 0xF9B9DF6F, 0x8EBEEFF9, 0x17B7BE43, 0x60B08ED5, + 0xD6D6A3E8, 0xA1D1937E, 0x38D8C2C4, 0x4FDFF252, 0xD1BB67F1, 0xA6BC5767, 0x3FB506DD, 0x48B2364B, + 0xD80D2BDA, 0xAF0A1B4C, 0x36034AF6, 0x41047A60, 0xDF60EFC3, 0xA867DF55, 0x316E8EEF, 0x4669BE79, + 0xCB61B38C, 0xBC66831A, 0x256FD2A0, 0x5268E236, 0xCC0C7795, 0xBB0B4703, 0x220216B9, 0x5505262F, + 0xC5BA3BBE, 0xB2BD0B28, 0x2BB45A92, 0x5CB36A04, 0xC2D7FFA7, 0xB5D0CF31, 0x2CD99E8B, 0x5BDEAE1D, + 0x9B64C2B0, 0xEC63F226, 0x756AA39C, 0x026D930A, 0x9C0906A9, 0xEB0E363F, 0x72076785, 0x05005713, + 0x95BF4A82, 0xE2B87A14, 0x7BB12BAE, 0x0CB61B38, 0x92D28E9B, 0xE5D5BE0D, 0x7CDCEFB7, 0x0BDBDF21, + 0x86D3D2D4, 0xF1D4E242, 0x68DDB3F8, 0x1FDA836E, 0x81BE16CD, 0xF6B9265B, 0x6FB077E1, 0x18B74777, + 0x88085AE6, 0xFF0F6A70, 0x66063BCA, 0x11010B5C, 0x8F659EFF, 0xF862AE69, 0x616BFFD3, 0x166CCF45, + 0xA00AE278, 0xD70DD2EE, 0x4E048354, 0x3903B3C2, 0xA7672661, 0xD06016F7, 0x4969474D, 0x3E6E77DB, + 0xAED16A4A, 0xD9D65ADC, 0x40DF0B66, 0x37D83BF0, 0xA9BCAE53, 0xDEBB9EC5, 0x47B2CF7F, 0x30B5FFE9, + 0xBDBDF21C, 0xCABAC28A, 0x53B39330, 0x24B4A3A6, 0xBAD03605, 0xCDD70693, 0x54DE5729, 0x23D967BF, + 0xB3667A2E, 0xC4614AB8, 0x5D681B02, 0x2A6F2B94, 0xB40BBE37, 0xC30C8EA1, 0x5A05DF1B, 0x2D02EF8D + }; + + unsigned int crc = ~0u; + int i; + for (i=0; i < len; ++i) + crc = (crc >> 8) ^ crc_table[buffer[i] ^ (crc & 0xff)]; + return ~crc; +} + +#define stbiw__wpng4(o,a,b,c,d) ((o)[0]=STBIW_UCHAR(a),(o)[1]=STBIW_UCHAR(b),(o)[2]=STBIW_UCHAR(c),(o)[3]=STBIW_UCHAR(d),(o)+=4) +#define stbiw__wp32(data,v) stbiw__wpng4(data, (v)>>24,(v)>>16,(v)>>8,(v)); +#define stbiw__wptag(data,s) stbiw__wpng4(data, s[0],s[1],s[2],s[3]) + +static void stbiw__wpcrc(unsigned char **data, int len) +{ + unsigned int crc = stbiw__crc32(*data - len - 4, len+4); + stbiw__wp32(*data, crc); +} + +static unsigned char stbiw__paeth(int a, int b, int c) +{ + int p = a + b - c, pa = abs(p-a), pb = abs(p-b), pc = abs(p-c); + if (pa <= pb && pa <= pc) return STBIW_UCHAR(a); + if (pb <= pc) return STBIW_UCHAR(b); + return STBIW_UCHAR(c); +} + +unsigned char *stbi_write_png_to_mem(unsigned char *pixels, int stride_bytes, int x, int y, int n, int *out_len) +{ + int ctype[5] = { -1, 0, 4, 2, 6 }; + unsigned char sig[8] = { 137,80,78,71,13,10,26,10 }; + unsigned char *out,*o, *filt, *zlib; + signed char *line_buffer; + int i,j,k,p,zlen; + + if (stride_bytes == 0) + stride_bytes = x * n; + + filt = (unsigned char *) STBIW_MALLOC((x*n+1) * y); if (!filt) return 0; + line_buffer = (signed char *) STBIW_MALLOC(x * n); if (!line_buffer) { STBIW_FREE(filt); return 0; } + for (j=0; j < y; ++j) { + static int mapping[] = { 0,1,2,3,4 }; + static int firstmap[] = { 0,1,0,5,6 }; + int *mymap = j ? mapping : firstmap; + int best = 0, bestval = 0x7fffffff; + for (p=0; p < 2; ++p) { + for (k= p?best:0; k < 5; ++k) { + int type = mymap[k],est=0; + unsigned char *z = pixels + stride_bytes*j; + for (i=0; i < n; ++i) + switch (type) { + case 0: line_buffer[i] = z[i]; break; + case 1: line_buffer[i] = z[i]; break; + case 2: line_buffer[i] = z[i] - z[i-stride_bytes]; break; + case 3: line_buffer[i] = z[i] - (z[i-stride_bytes]>>1); break; + case 4: line_buffer[i] = (signed char) (z[i] - stbiw__paeth(0,z[i-stride_bytes],0)); break; + case 5: line_buffer[i] = z[i]; break; + case 6: line_buffer[i] = z[i]; break; + } + for (i=n; i < x*n; ++i) { + switch (type) { + case 0: line_buffer[i] = z[i]; break; + case 1: line_buffer[i] = z[i] - z[i-n]; break; + case 2: line_buffer[i] = z[i] - z[i-stride_bytes]; break; + case 3: line_buffer[i] = z[i] - ((z[i-n] + z[i-stride_bytes])>>1); break; + case 4: line_buffer[i] = z[i] - stbiw__paeth(z[i-n], z[i-stride_bytes], z[i-stride_bytes-n]); break; + case 5: line_buffer[i] = z[i] - (z[i-n]>>1); break; + case 6: line_buffer[i] = z[i] - stbiw__paeth(z[i-n], 0,0); break; + } + } + if (p) break; + for (i=0; i < x*n; ++i) + est += abs((signed char) line_buffer[i]); + if (est < bestval) { bestval = est; best = k; } + } + } + // when we get here, best contains the filter type, and line_buffer contains the data + filt[j*(x*n+1)] = (unsigned char) best; + STBIW_MEMMOVE(filt+j*(x*n+1)+1, line_buffer, x*n); + } + STBIW_FREE(line_buffer); + zlib = stbi_zlib_compress(filt, y*( x*n+1), &zlen, 8); // increase 8 to get smaller but use more memory + STBIW_FREE(filt); + if (!zlib) return 0; + + // each tag requires 12 bytes of overhead + out = (unsigned char *) STBIW_MALLOC(8 + 12+13 + 12+zlen + 12); + if (!out) return 0; + *out_len = 8 + 12+13 + 12+zlen + 12; + + o=out; + STBIW_MEMMOVE(o,sig,8); o+= 8; + stbiw__wp32(o, 13); // header length + stbiw__wptag(o, "IHDR"); + stbiw__wp32(o, x); + stbiw__wp32(o, y); + *o++ = 8; + *o++ = STBIW_UCHAR(ctype[n]); + *o++ = 0; + *o++ = 0; + *o++ = 0; + stbiw__wpcrc(&o,13); + + stbiw__wp32(o, zlen); + stbiw__wptag(o, "IDAT"); + STBIW_MEMMOVE(o, zlib, zlen); + o += zlen; + STBIW_FREE(zlib); + stbiw__wpcrc(&o, zlen); + + stbiw__wp32(o,0); + stbiw__wptag(o, "IEND"); + stbiw__wpcrc(&o,0); + + STBIW_ASSERT(o == out + *out_len); + + return out; +} + +#ifndef STBI_WRITE_NO_STDIO +STBIWDEF int stbi_write_png(char const *filename, int x, int y, int comp, const void *data, int stride_bytes) +{ + FILE *f; + int len; + unsigned char *png = stbi_write_png_to_mem((unsigned char *) data, stride_bytes, x, y, comp, &len); + if (png == NULL) return 0; + f = fopen(filename, "wb"); + if (!f) { STBIW_FREE(png); return 0; } + fwrite(png, 1, len, f); + fclose(f); + STBIW_FREE(png); + return 1; +} +#endif + +STBIWDEF int stbi_write_png_to_func(stbi_write_func *func, void *context, int x, int y, int comp, const void *data, int stride_bytes) +{ + int len; + unsigned char *png = stbi_write_png_to_mem((unsigned char *) data, stride_bytes, x, y, comp, &len); + if (png == NULL) return 0; + func(context, png, len); + STBIW_FREE(png); + return 1; +} + +#endif // STB_IMAGE_WRITE_IMPLEMENTATION + +/* Revision history + 1.02 (2016-04-02) + avoid allocating large structures on the stack + 1.01 (2016-01-16) + STBIW_REALLOC_SIZED: support allocators with no realloc support + avoid race-condition in crc initialization + minor compile issues + 1.00 (2015-09-14) + installable file IO function + 0.99 (2015-09-13) + warning fixes; TGA rle support + 0.98 (2015-04-08) + added STBIW_MALLOC, STBIW_ASSERT etc + 0.97 (2015-01-18) + fixed HDR asserts, rewrote HDR rle logic + 0.96 (2015-01-17) + add HDR output + fix monochrome BMP + 0.95 (2014-08-17) + add monochrome TGA output + 0.94 (2014-05-31) + rename private functions to avoid conflicts with stb_image.h + 0.93 (2014-05-27) + warning fixes + 0.92 (2010-08-01) + casts to unsigned char to fix warnings + 0.91 (2010-07-17) + first public release + 0.90 first internal release +*/ diff --git a/impeller/third_party/stb/stb/stb_leakcheck.h b/impeller/third_party/stb/stb/stb_leakcheck.h new file mode 100644 index 0000000000000..6e50b224af42d --- /dev/null +++ b/impeller/third_party/stb/stb/stb_leakcheck.h @@ -0,0 +1,124 @@ +// stb_leakcheck.h - v0.2 - quick & dirty malloc leak-checking - public domain +// LICENSE +// +// This software is dual-licensed to the public domain and under the following +// license: you are granted a perpetual, irrevocable license to copy, modify, +// publish, and distribute this file as you see fit. + +#ifdef STB_LEAKCHECK_IMPLEMENTATION +#undef STB_LEAKCHECK_IMPLEMENTATION // don't implement more than once + +// if we've already included leakcheck before, undefine the macros +#ifdef malloc +#undef malloc +#undef free +#undef realloc +#endif + +#include +#include +#include +#include +#include +typedef struct malloc_info stb_leakcheck_malloc_info; + +struct malloc_info +{ + char *file; + int line; + size_t size; + stb_leakcheck_malloc_info *next,*prev; +}; + +static stb_leakcheck_malloc_info *mi_head; + +void *stb_leakcheck_malloc(size_t sz, char *file, int line) +{ + stb_leakcheck_malloc_info *mi = (stb_leakcheck_malloc_info *) malloc(sz + sizeof(*mi)); + if (mi == NULL) return mi; + mi->file = file; + mi->line = line; + mi->next = mi_head; + if (mi_head) + mi->next->prev = mi; + mi->prev = NULL; + mi->size = (int) sz; + mi_head = mi; + return mi+1; +} + +void stb_leakcheck_free(void *ptr) +{ + if (ptr != NULL) { + stb_leakcheck_malloc_info *mi = (stb_leakcheck_malloc_info *) ptr - 1; + mi->size = ~mi->size; + #ifndef STB_LEAKCHECK_SHOWALL + if (mi->prev == NULL) { + assert(mi_head == mi); + mi_head = mi->next; + } else + mi->prev->next = mi->next; + if (mi->next) + mi->next->prev = mi->prev; + #endif + } +} + +void *stb_leakcheck_realloc(void *ptr, size_t sz, char *file, int line) +{ + if (ptr == NULL) { + return stb_leakcheck_malloc(sz, file, line); + } else if (sz == 0) { + stb_leakcheck_free(ptr); + return NULL; + } else { + stb_leakcheck_malloc_info *mi = (stb_leakcheck_malloc_info *) ptr - 1; + if (sz <= mi->size) + return ptr; + else { + #ifdef STB_LEAKCHECK_REALLOC_PRESERVE_MALLOC_FILELINE + void *q = stb_leakcheck_malloc(sz, mi->file, mi->line); + #else + void *q = stb_leakcheck_malloc(sz, file, line); + #endif + if (q) { + memcpy(q, ptr, mi->size); + stb_leakcheck_free(ptr); + } + return q; + } + } +} + +void stb_leakcheck_dumpmem(void) +{ + stb_leakcheck_malloc_info *mi = mi_head; + while (mi) { + if ((ptrdiff_t) mi->size >= 0) + printf("LEAKED: %s (%4d): %8z bytes at %p\n", mi->file, mi->line, mi->size, mi+1); + mi = mi->next; + } + #ifdef STB_LEAKCHECK_SHOWALL + mi = mi_head; + while (mi) { + if ((ptrdiff_t) mi->size < 0) + printf("FREED : %s (%4d): %8z bytes at %p\n", mi->file, mi->line, ~mi->size, mi+1); + mi = mi->next; + } + #endif +} +#endif // STB_LEAKCHECK_IMPLEMENTATION + +#ifndef INCLUDE_STB_LEAKCHECK_H +#define INCLUDE_STB_LEAKCHECK_H + +#define malloc(sz) stb_leakcheck_malloc(sz, __FILE__, __LINE__) +#define free(p) stb_leakcheck_free(p) +#define realloc(p,sz) stb_leakcheck_realloc(p,sz, __FILE__, __LINE__) + +extern void * stb_leakcheck_malloc(size_t sz, char *file, int line); +extern void * stb_leakcheck_realloc(void *ptr, size_t sz, char *file, int line); +extern void stb_leakcheck_free(void *ptr); +extern void stb_leakcheck_dumpmem(void); + +#endif // INCLUDE_STB_LEAKCHECK_H diff --git a/impeller/third_party/stb/stb/stb_perlin.h b/impeller/third_party/stb/stb/stb_perlin.h new file mode 100644 index 0000000000000..0dac7d9f9bb43 --- /dev/null +++ b/impeller/third_party/stb/stb/stb_perlin.h @@ -0,0 +1,182 @@ +// stb_perlin.h - v0.2 - perlin noise +// public domain single-file C implementation by Sean Barrett +// +// LICENSE +// +// This software is dual-licensed to the public domain and under the following +// license: you are granted a perpetual, irrevocable license to copy, modify, +// publish, and distribute this file as you see fit. +// +// +// to create the implementation, +// #define STB_PERLIN_IMPLEMENTATION +// in *one* C/CPP file that includes this file. + + +// Documentation: +// +// float stb_perlin_noise3( float x, +// float y, +// float z, +// int x_wrap=0, +// int y_wrap=0, +// int z_wrap=0) +// +// This function computes a random value at the coordinate (x,y,z). +// Adjacent random values are continuous but the noise fluctuates +// its randomness with period 1, i.e. takes on wholly unrelated values +// at integer points. Specifically, this implements Ken Perlin's +// revised noise function from 2002. +// +// The "wrap" parameters can be used to create wraparound noise that +// wraps at powers of two. The numbers MUST be powers of two. Specify +// 0 to mean "don't care". (The noise always wraps every 256 due +// details of the implementation, even if you ask for larger or no +// wrapping.) + + +#ifdef __cplusplus +extern "C" float stb_perlin_noise3(float x, float y, float z, int x_wrap=0, int y_wrap=0, int z_wrap=0); +#else +extern float stb_perlin_noise3(float x, float y, float z, int x_wrap, int y_wrap, int z_wrap); +#endif + +#ifdef STB_PERLIN_IMPLEMENTATION + +#include // floor() + +// not same permutation table as Perlin's reference to avoid copyright issues; +// Perlin's table can be found at http://mrl.nyu.edu/~perlin/noise/ +// @OPTIMIZE: should this be unsigned char instead of int for cache? +static int stb__perlin_randtab[512] = +{ + 23, 125, 161, 52, 103, 117, 70, 37, 247, 101, 203, 169, 124, 126, 44, 123, + 152, 238, 145, 45, 171, 114, 253, 10, 192, 136, 4, 157, 249, 30, 35, 72, + 175, 63, 77, 90, 181, 16, 96, 111, 133, 104, 75, 162, 93, 56, 66, 240, + 8, 50, 84, 229, 49, 210, 173, 239, 141, 1, 87, 18, 2, 198, 143, 57, + 225, 160, 58, 217, 168, 206, 245, 204, 199, 6, 73, 60, 20, 230, 211, 233, + 94, 200, 88, 9, 74, 155, 33, 15, 219, 130, 226, 202, 83, 236, 42, 172, + 165, 218, 55, 222, 46, 107, 98, 154, 109, 67, 196, 178, 127, 158, 13, 243, + 65, 79, 166, 248, 25, 224, 115, 80, 68, 51, 184, 128, 232, 208, 151, 122, + 26, 212, 105, 43, 179, 213, 235, 148, 146, 89, 14, 195, 28, 78, 112, 76, + 250, 47, 24, 251, 140, 108, 186, 190, 228, 170, 183, 139, 39, 188, 244, 246, + 132, 48, 119, 144, 180, 138, 134, 193, 82, 182, 120, 121, 86, 220, 209, 3, + 91, 241, 149, 85, 205, 150, 113, 216, 31, 100, 41, 164, 177, 214, 153, 231, + 38, 71, 185, 174, 97, 201, 29, 95, 7, 92, 54, 254, 191, 118, 34, 221, + 131, 11, 163, 99, 234, 81, 227, 147, 156, 176, 17, 142, 69, 12, 110, 62, + 27, 255, 0, 194, 59, 116, 242, 252, 19, 21, 187, 53, 207, 129, 64, 135, + 61, 40, 167, 237, 102, 223, 106, 159, 197, 189, 215, 137, 36, 32, 22, 5, + + // and a second copy so we don't need an extra mask or static initializer + 23, 125, 161, 52, 103, 117, 70, 37, 247, 101, 203, 169, 124, 126, 44, 123, + 152, 238, 145, 45, 171, 114, 253, 10, 192, 136, 4, 157, 249, 30, 35, 72, + 175, 63, 77, 90, 181, 16, 96, 111, 133, 104, 75, 162, 93, 56, 66, 240, + 8, 50, 84, 229, 49, 210, 173, 239, 141, 1, 87, 18, 2, 198, 143, 57, + 225, 160, 58, 217, 168, 206, 245, 204, 199, 6, 73, 60, 20, 230, 211, 233, + 94, 200, 88, 9, 74, 155, 33, 15, 219, 130, 226, 202, 83, 236, 42, 172, + 165, 218, 55, 222, 46, 107, 98, 154, 109, 67, 196, 178, 127, 158, 13, 243, + 65, 79, 166, 248, 25, 224, 115, 80, 68, 51, 184, 128, 232, 208, 151, 122, + 26, 212, 105, 43, 179, 213, 235, 148, 146, 89, 14, 195, 28, 78, 112, 76, + 250, 47, 24, 251, 140, 108, 186, 190, 228, 170, 183, 139, 39, 188, 244, 246, + 132, 48, 119, 144, 180, 138, 134, 193, 82, 182, 120, 121, 86, 220, 209, 3, + 91, 241, 149, 85, 205, 150, 113, 216, 31, 100, 41, 164, 177, 214, 153, 231, + 38, 71, 185, 174, 97, 201, 29, 95, 7, 92, 54, 254, 191, 118, 34, 221, + 131, 11, 163, 99, 234, 81, 227, 147, 156, 176, 17, 142, 69, 12, 110, 62, + 27, 255, 0, 194, 59, 116, 242, 252, 19, 21, 187, 53, 207, 129, 64, 135, + 61, 40, 167, 237, 102, 223, 106, 159, 197, 189, 215, 137, 36, 32, 22, 5, +}; + +static float stb__perlin_lerp(float a, float b, float t) +{ + return a + (b-a) * t; +} + +// different grad function from Perlin's, but easy to modify to match reference +static float stb__perlin_grad(int hash, float x, float y, float z) +{ + static float basis[12][4] = + { + { 1, 1, 0 }, + { -1, 1, 0 }, + { 1,-1, 0 }, + { -1,-1, 0 }, + { 1, 0, 1 }, + { -1, 0, 1 }, + { 1, 0,-1 }, + { -1, 0,-1 }, + { 0, 1, 1 }, + { 0,-1, 1 }, + { 0, 1,-1 }, + { 0,-1,-1 }, + }; + + // perlin's gradient has 12 cases so some get used 1/16th of the time + // and some 2/16ths. We reduce bias by changing those fractions + // to 5/16ths and 6/16ths, and the same 4 cases get the extra weight. + static unsigned char indices[64] = + { + 0,1,2,3,4,5,6,7,8,9,10,11, + 0,9,1,11, + 0,1,2,3,4,5,6,7,8,9,10,11, + 0,1,2,3,4,5,6,7,8,9,10,11, + 0,1,2,3,4,5,6,7,8,9,10,11, + 0,1,2,3,4,5,6,7,8,9,10,11, + }; + + // if you use reference permutation table, change 63 below to 15 to match reference + float *grad = basis[indices[hash & 63]]; + return grad[0]*x + grad[1]*y + grad[2]*z; +} + +float stb_perlin_noise3(float x, float y, float z, int x_wrap, int y_wrap, int z_wrap) +{ + float u,v,w; + float n000,n001,n010,n011,n100,n101,n110,n111; + float n00,n01,n10,n11; + float n0,n1; + + unsigned int x_mask = (x_wrap-1) & 255; + unsigned int y_mask = (y_wrap-1) & 255; + unsigned int z_mask = (z_wrap-1) & 255; + int px = (int) floor(x); + int py = (int) floor(y); + int pz = (int) floor(z); + int x0 = px & x_mask, x1 = (px+1) & x_mask; + int y0 = py & y_mask, y1 = (py+1) & y_mask; + int z0 = pz & z_mask, z1 = (pz+1) & z_mask; + int r0,r1, r00,r01,r10,r11; + + #define stb__perlin_ease(a) (((a*6-15)*a + 10) * a * a * a) + + x -= px; u = stb__perlin_ease(x); + y -= py; v = stb__perlin_ease(y); + z -= pz; w = stb__perlin_ease(z); + + r0 = stb__perlin_randtab[x0]; + r1 = stb__perlin_randtab[x1]; + + r00 = stb__perlin_randtab[r0+y0]; + r01 = stb__perlin_randtab[r0+y1]; + r10 = stb__perlin_randtab[r1+y0]; + r11 = stb__perlin_randtab[r1+y1]; + + n000 = stb__perlin_grad(stb__perlin_randtab[r00+z0], x , y , z ); + n001 = stb__perlin_grad(stb__perlin_randtab[r00+z1], x , y , z-1 ); + n010 = stb__perlin_grad(stb__perlin_randtab[r01+z0], x , y-1, z ); + n011 = stb__perlin_grad(stb__perlin_randtab[r01+z1], x , y-1, z-1 ); + n100 = stb__perlin_grad(stb__perlin_randtab[r10+z0], x-1, y , z ); + n101 = stb__perlin_grad(stb__perlin_randtab[r10+z1], x-1, y , z-1 ); + n110 = stb__perlin_grad(stb__perlin_randtab[r11+z0], x-1, y-1, z ); + n111 = stb__perlin_grad(stb__perlin_randtab[r11+z1], x-1, y-1, z-1 ); + + n00 = stb__perlin_lerp(n000,n001,w); + n01 = stb__perlin_lerp(n010,n011,w); + n10 = stb__perlin_lerp(n100,n101,w); + n11 = stb__perlin_lerp(n110,n111,w); + + n0 = stb__perlin_lerp(n00,n01,v); + n1 = stb__perlin_lerp(n10,n11,v); + + return stb__perlin_lerp(n0,n1,u); +} +#endif // STB_PERLIN_IMPLEMENTATION diff --git a/impeller/third_party/stb/stb/stb_rect_pack.h b/impeller/third_party/stb/stb/stb_rect_pack.h new file mode 100644 index 0000000000000..bd1cfc60b2575 --- /dev/null +++ b/impeller/third_party/stb/stb/stb_rect_pack.h @@ -0,0 +1,572 @@ +// stb_rect_pack.h - v0.08 - public domain - rectangle packing +// Sean Barrett 2014 +// +// Useful for e.g. packing rectangular textures into an atlas. +// Does not do rotation. +// +// Not necessarily the awesomest packing method, but better than +// the totally naive one in stb_truetype (which is primarily what +// this is meant to replace). +// +// Has only had a few tests run, may have issues. +// +// More docs to come. +// +// No memory allocations; uses qsort() and assert() from stdlib. +// Can override those by defining STBRP_SORT and STBRP_ASSERT. +// +// This library currently uses the Skyline Bottom-Left algorithm. +// +// Please note: better rectangle packers are welcome! Please +// implement them to the same API, but with a different init +// function. +// +// Credits +// +// Library +// Sean Barrett +// Minor features +// Martins Mozeiko +// Bugfixes / warning fixes +// Jeremy Jaussaud +// +// Version history: +// +// 0.08 (2015-09-13) really fix bug with empty rects (w=0 or h=0) +// 0.07 (2015-09-13) fix bug with empty rects (w=0 or h=0) +// 0.06 (2015-04-15) added STBRP_SORT to allow replacing qsort +// 0.05: added STBRP_ASSERT to allow replacing assert +// 0.04: fixed minor bug in STBRP_LARGE_RECTS support +// 0.01: initial release +// +// LICENSE +// +// This software is dual-licensed to the public domain and under the following +// license: you are granted a perpetual, irrevocable license to copy, modify, +// publish, and distribute this file as you see fit. + +////////////////////////////////////////////////////////////////////////////// +// +// INCLUDE SECTION +// + +#ifndef STB_INCLUDE_STB_RECT_PACK_H +#define STB_INCLUDE_STB_RECT_PACK_H + +#define STB_RECT_PACK_VERSION 1 + +#ifdef STBRP_STATIC +#define STBRP_DEF static +#else +#define STBRP_DEF extern +#endif + +#ifdef __cplusplus +extern "C" { +#endif + +typedef struct stbrp_context stbrp_context; +typedef struct stbrp_node stbrp_node; +typedef struct stbrp_rect stbrp_rect; + +#ifdef STBRP_LARGE_RECTS +typedef int stbrp_coord; +#else +typedef unsigned short stbrp_coord; +#endif + +STBRP_DEF void stbrp_pack_rects (stbrp_context *context, stbrp_rect *rects, int num_rects); +// Assign packed locations to rectangles. The rectangles are of type +// 'stbrp_rect' defined below, stored in the array 'rects', and there +// are 'num_rects' many of them. +// +// Rectangles which are successfully packed have the 'was_packed' flag +// set to a non-zero value and 'x' and 'y' store the minimum location +// on each axis (i.e. bottom-left in cartesian coordinates, top-left +// if you imagine y increasing downwards). Rectangles which do not fit +// have the 'was_packed' flag set to 0. +// +// You should not try to access the 'rects' array from another thread +// while this function is running, as the function temporarily reorders +// the array while it executes. +// +// To pack into another rectangle, you need to call stbrp_init_target +// again. To continue packing into the same rectangle, you can call +// this function again. Calling this multiple times with multiple rect +// arrays will probably produce worse packing results than calling it +// a single time with the full rectangle array, but the option is +// available. + +struct stbrp_rect +{ + // reserved for your use: + int id; + + // input: + stbrp_coord w, h; + + // output: + stbrp_coord x, y; + int was_packed; // non-zero if valid packing + +}; // 16 bytes, nominally + + +STBRP_DEF void stbrp_init_target (stbrp_context *context, int width, int height, stbrp_node *nodes, int num_nodes); +// Initialize a rectangle packer to: +// pack a rectangle that is 'width' by 'height' in dimensions +// using temporary storage provided by the array 'nodes', which is 'num_nodes' long +// +// You must call this function every time you start packing into a new target. +// +// There is no "shutdown" function. The 'nodes' memory must stay valid for +// the following stbrp_pack_rects() call (or calls), but can be freed after +// the call (or calls) finish. +// +// Note: to guarantee best results, either: +// 1. make sure 'num_nodes' >= 'width' +// or 2. call stbrp_allow_out_of_mem() defined below with 'allow_out_of_mem = 1' +// +// If you don't do either of the above things, widths will be quantized to multiples +// of small integers to guarantee the algorithm doesn't run out of temporary storage. +// +// If you do #2, then the non-quantized algorithm will be used, but the algorithm +// may run out of temporary storage and be unable to pack some rectangles. + +STBRP_DEF void stbrp_setup_allow_out_of_mem (stbrp_context *context, int allow_out_of_mem); +// Optionally call this function after init but before doing any packing to +// change the handling of the out-of-temp-memory scenario, described above. +// If you call init again, this will be reset to the default (false). + + +STBRP_DEF void stbrp_setup_heuristic (stbrp_context *context, int heuristic); +// Optionally select which packing heuristic the library should use. Different +// heuristics will produce better/worse results for different data sets. +// If you call init again, this will be reset to the default. + +enum +{ + STBRP_HEURISTIC_Skyline_default=0, + STBRP_HEURISTIC_Skyline_BL_sortHeight = STBRP_HEURISTIC_Skyline_default, + STBRP_HEURISTIC_Skyline_BF_sortHeight, +}; + + +////////////////////////////////////////////////////////////////////////////// +// +// the details of the following structures don't matter to you, but they must +// be visible so you can handle the memory allocations for them + +struct stbrp_node +{ + stbrp_coord x,y; + stbrp_node *next; +}; + +struct stbrp_context +{ + int width; + int height; + int align; + int init_mode; + int heuristic; + int num_nodes; + stbrp_node *active_head; + stbrp_node *free_head; + stbrp_node extra[2]; // we allocate two extra nodes so optimal user-node-count is 'width' not 'width+2' +}; + +#ifdef __cplusplus +} +#endif + +#endif + +////////////////////////////////////////////////////////////////////////////// +// +// IMPLEMENTATION SECTION +// + +#ifdef STB_RECT_PACK_IMPLEMENTATION +#ifndef STBRP_SORT +#include +#define STBRP_SORT qsort +#endif + +#ifndef STBRP_ASSERT +#include +#define STBRP_ASSERT assert +#endif + +enum +{ + STBRP__INIT_skyline = 1, +}; + +STBRP_DEF void stbrp_setup_heuristic(stbrp_context *context, int heuristic) +{ + switch (context->init_mode) { + case STBRP__INIT_skyline: + STBRP_ASSERT(heuristic == STBRP_HEURISTIC_Skyline_BL_sortHeight || heuristic == STBRP_HEURISTIC_Skyline_BF_sortHeight); + context->heuristic = heuristic; + break; + default: + STBRP_ASSERT(0); + } +} + +STBRP_DEF void stbrp_setup_allow_out_of_mem(stbrp_context *context, int allow_out_of_mem) +{ + if (allow_out_of_mem) + // if it's ok to run out of memory, then don't bother aligning them; + // this gives better packing, but may fail due to OOM (even though + // the rectangles easily fit). @TODO a smarter approach would be to only + // quantize once we've hit OOM, then we could get rid of this parameter. + context->align = 1; + else { + // if it's not ok to run out of memory, then quantize the widths + // so that num_nodes is always enough nodes. + // + // I.e. num_nodes * align >= width + // align >= width / num_nodes + // align = ceil(width/num_nodes) + + context->align = (context->width + context->num_nodes-1) / context->num_nodes; + } +} + +STBRP_DEF void stbrp_init_target(stbrp_context *context, int width, int height, stbrp_node *nodes, int num_nodes) +{ + int i; +#ifndef STBRP_LARGE_RECTS + STBRP_ASSERT(width <= 0xffff && height <= 0xffff); +#endif + + for (i=0; i < num_nodes-1; ++i) + nodes[i].next = &nodes[i+1]; + nodes[i].next = NULL; + context->init_mode = STBRP__INIT_skyline; + context->heuristic = STBRP_HEURISTIC_Skyline_default; + context->free_head = &nodes[0]; + context->active_head = &context->extra[0]; + context->width = width; + context->height = height; + context->num_nodes = num_nodes; + stbrp_setup_allow_out_of_mem(context, 0); + + // node 0 is the full width, node 1 is the sentinel (lets us not store width explicitly) + context->extra[0].x = 0; + context->extra[0].y = 0; + context->extra[0].next = &context->extra[1]; + context->extra[1].x = (stbrp_coord) width; +#ifdef STBRP_LARGE_RECTS + context->extra[1].y = (1<<30); +#else + context->extra[1].y = 65535; +#endif + context->extra[1].next = NULL; +} + +// find minimum y position if it starts at x1 +static int stbrp__skyline_find_min_y(stbrp_context *c, stbrp_node *first, int x0, int width, int *pwaste) +{ + stbrp_node *node = first; + int x1 = x0 + width; + int min_y, visited_width, waste_area; + STBRP_ASSERT(first->x <= x0); + + #if 0 + // skip in case we're past the node + while (node->next->x <= x0) + ++node; + #else + STBRP_ASSERT(node->next->x > x0); // we ended up handling this in the caller for efficiency + #endif + + STBRP_ASSERT(node->x <= x0); + + min_y = 0; + waste_area = 0; + visited_width = 0; + while (node->x < x1) { + if (node->y > min_y) { + // raise min_y higher. + // we've accounted for all waste up to min_y, + // but we'll now add more waste for everything we've visted + waste_area += visited_width * (node->y - min_y); + min_y = node->y; + // the first time through, visited_width might be reduced + if (node->x < x0) + visited_width += node->next->x - x0; + else + visited_width += node->next->x - node->x; + } else { + // add waste area + int under_width = node->next->x - node->x; + if (under_width + visited_width > width) + under_width = width - visited_width; + waste_area += under_width * (min_y - node->y); + visited_width += under_width; + } + node = node->next; + } + + *pwaste = waste_area; + return min_y; +} + +typedef struct +{ + int x,y; + stbrp_node **prev_link; +} stbrp__findresult; + +static stbrp__findresult stbrp__skyline_find_best_pos(stbrp_context *c, int width, int height) +{ + int best_waste = (1<<30), best_x, best_y = (1 << 30); + stbrp__findresult fr; + stbrp_node **prev, *node, *tail, **best = NULL; + + // align to multiple of c->align + width = (width + c->align - 1); + width -= width % c->align; + STBRP_ASSERT(width % c->align == 0); + + node = c->active_head; + prev = &c->active_head; + while (node->x + width <= c->width) { + int y,waste; + y = stbrp__skyline_find_min_y(c, node, node->x, width, &waste); + if (c->heuristic == STBRP_HEURISTIC_Skyline_BL_sortHeight) { // actually just want to test BL + // bottom left + if (y < best_y) { + best_y = y; + best = prev; + } + } else { + // best-fit + if (y + height <= c->height) { + // can only use it if it first vertically + if (y < best_y || (y == best_y && waste < best_waste)) { + best_y = y; + best_waste = waste; + best = prev; + } + } + } + prev = &node->next; + node = node->next; + } + + best_x = (best == NULL) ? 0 : (*best)->x; + + // if doing best-fit (BF), we also have to try aligning right edge to each node position + // + // e.g, if fitting + // + // ____________________ + // |____________________| + // + // into + // + // | | + // | ____________| + // |____________| + // + // then right-aligned reduces waste, but bottom-left BL is always chooses left-aligned + // + // This makes BF take about 2x the time + + if (c->heuristic == STBRP_HEURISTIC_Skyline_BF_sortHeight) { + tail = c->active_head; + node = c->active_head; + prev = &c->active_head; + // find first node that's admissible + while (tail->x < width) + tail = tail->next; + while (tail) { + int xpos = tail->x - width; + int y,waste; + STBRP_ASSERT(xpos >= 0); + // find the left position that matches this + while (node->next->x <= xpos) { + prev = &node->next; + node = node->next; + } + STBRP_ASSERT(node->next->x > xpos && node->x <= xpos); + y = stbrp__skyline_find_min_y(c, node, xpos, width, &waste); + if (y + height < c->height) { + if (y <= best_y) { + if (y < best_y || waste < best_waste || (waste==best_waste && xpos < best_x)) { + best_x = xpos; + STBRP_ASSERT(y <= best_y); + best_y = y; + best_waste = waste; + best = prev; + } + } + } + tail = tail->next; + } + } + + fr.prev_link = best; + fr.x = best_x; + fr.y = best_y; + return fr; +} + +static stbrp__findresult stbrp__skyline_pack_rectangle(stbrp_context *context, int width, int height) +{ + // find best position according to heuristic + stbrp__findresult res = stbrp__skyline_find_best_pos(context, width, height); + stbrp_node *node, *cur; + + // bail if: + // 1. it failed + // 2. the best node doesn't fit (we don't always check this) + // 3. we're out of memory + if (res.prev_link == NULL || res.y + height > context->height || context->free_head == NULL) { + res.prev_link = NULL; + return res; + } + + // on success, create new node + node = context->free_head; + node->x = (stbrp_coord) res.x; + node->y = (stbrp_coord) (res.y + height); + + context->free_head = node->next; + + // insert the new node into the right starting point, and + // let 'cur' point to the remaining nodes needing to be + // stiched back in + + cur = *res.prev_link; + if (cur->x < res.x) { + // preserve the existing one, so start testing with the next one + stbrp_node *next = cur->next; + cur->next = node; + cur = next; + } else { + *res.prev_link = node; + } + + // from here, traverse cur and free the nodes, until we get to one + // that shouldn't be freed + while (cur->next && cur->next->x <= res.x + width) { + stbrp_node *next = cur->next; + // move the current node to the free list + cur->next = context->free_head; + context->free_head = cur; + cur = next; + } + + // stitch the list back in + node->next = cur; + + if (cur->x < res.x + width) + cur->x = (stbrp_coord) (res.x + width); + +#ifdef _DEBUG + cur = context->active_head; + while (cur->x < context->width) { + STBRP_ASSERT(cur->x < cur->next->x); + cur = cur->next; + } + STBRP_ASSERT(cur->next == NULL); + + { + stbrp_node *L1 = NULL, *L2 = NULL; + int count=0; + cur = context->active_head; + while (cur) { + L1 = cur; + cur = cur->next; + ++count; + } + cur = context->free_head; + while (cur) { + L2 = cur; + cur = cur->next; + ++count; + } + STBRP_ASSERT(count == context->num_nodes+2); + } +#endif + + return res; +} + +static int rect_height_compare(const void *a, const void *b) +{ + stbrp_rect *p = (stbrp_rect *) a; + stbrp_rect *q = (stbrp_rect *) b; + if (p->h > q->h) + return -1; + if (p->h < q->h) + return 1; + return (p->w > q->w) ? -1 : (p->w < q->w); +} + +static int rect_width_compare(const void *a, const void *b) +{ + stbrp_rect *p = (stbrp_rect *) a; + stbrp_rect *q = (stbrp_rect *) b; + if (p->w > q->w) + return -1; + if (p->w < q->w) + return 1; + return (p->h > q->h) ? -1 : (p->h < q->h); +} + +static int rect_original_order(const void *a, const void *b) +{ + stbrp_rect *p = (stbrp_rect *) a; + stbrp_rect *q = (stbrp_rect *) b; + return (p->was_packed < q->was_packed) ? -1 : (p->was_packed > q->was_packed); +} + +#ifdef STBRP_LARGE_RECTS +#define STBRP__MAXVAL 0xffffffff +#else +#define STBRP__MAXVAL 0xffff +#endif + +STBRP_DEF void stbrp_pack_rects(stbrp_context *context, stbrp_rect *rects, int num_rects) +{ + int i; + + // we use the 'was_packed' field internally to allow sorting/unsorting + for (i=0; i < num_rects; ++i) { + rects[i].was_packed = i; + #ifndef STBRP_LARGE_RECTS + STBRP_ASSERT(rects[i].w <= 0xffff && rects[i].h <= 0xffff); + #endif + } + + // sort according to heuristic + STBRP_SORT(rects, num_rects, sizeof(rects[0]), rect_height_compare); + + for (i=0; i < num_rects; ++i) { + if (rects[i].w == 0 || rects[i].h == 0) { + rects[i].x = rects[i].y = 0; // empty rect needs no space + } else { + stbrp__findresult fr = stbrp__skyline_pack_rectangle(context, rects[i].w, rects[i].h); + if (fr.prev_link) { + rects[i].x = (stbrp_coord) fr.x; + rects[i].y = (stbrp_coord) fr.y; + } else { + rects[i].x = rects[i].y = STBRP__MAXVAL; + } + } + } + + // unsort + STBRP_SORT(rects, num_rects, sizeof(rects[0]), rect_original_order); + + // set was_packed flags + for (i=0; i < num_rects; ++i) + rects[i].was_packed = !(rects[i].x == STBRP__MAXVAL && rects[i].y == STBRP__MAXVAL); +} +#endif diff --git a/impeller/third_party/stb/stb/stb_textedit.h b/impeller/third_party/stb/stb/stb_textedit.h new file mode 100644 index 0000000000000..29af484b1c6da --- /dev/null +++ b/impeller/third_party/stb/stb/stb_textedit.h @@ -0,0 +1,1304 @@ +// stb_textedit.h - v1.8 - public domain - Sean Barrett +// Development of this library was sponsored by RAD Game Tools +// +// This C header file implements the guts of a multi-line text-editing +// widget; you implement display, word-wrapping, and low-level string +// insertion/deletion, and stb_textedit will map user inputs into +// insertions & deletions, plus updates to the cursor position, +// selection state, and undo state. +// +// It is intended for use in games and other systems that need to build +// their own custom widgets and which do not have heavy text-editing +// requirements (this library is not recommended for use for editing large +// texts, as its performance does not scale and it has limited undo). +// +// Non-trivial behaviors are modelled after Windows text controls. +// +// +// LICENSE +// +// This software is dual-licensed to the public domain and under the following +// license: you are granted a perpetual, irrevocable license to copy, modify, +// publish, and distribute this file as you see fit. +// +// +// DEPENDENCIES +// +// Uses the C runtime function 'memmove', which you can override +// by defining STB_TEXTEDIT_memmove before the implementation. +// Uses no other functions. Performs no runtime allocations. +// +// +// VERSION HISTORY +// +// 1.8 (2016-04-02) better keyboard handling when mouse button is down +// 1.7 (2015-09-13) change y range handling in case baseline is non-0 +// 1.6 (2015-04-15) allow STB_TEXTEDIT_memmove +// 1.5 (2014-09-10) add support for secondary keys for OS X +// 1.4 (2014-08-17) fix signed/unsigned warnings +// 1.3 (2014-06-19) fix mouse clicking to round to nearest char boundary +// 1.2 (2014-05-27) fix some RAD types that had crept into the new code +// 1.1 (2013-12-15) move-by-word (requires STB_TEXTEDIT_IS_SPACE ) +// 1.0 (2012-07-26) improve documentation, initial public release +// 0.3 (2012-02-24) bugfixes, single-line mode; insert mode +// 0.2 (2011-11-28) fixes to undo/redo +// 0.1 (2010-07-08) initial version +// +// ADDITIONAL CONTRIBUTORS +// +// Ulf Winklemann: move-by-word in 1.1 +// Fabian Giesen: secondary key inputs in 1.5 +// Martins Mozeiko: STB_TEXTEDIT_memmove +// +// Bugfixes: +// Scott Graham +// Daniel Keller +// Omar Cornut +// +// USAGE +// +// This file behaves differently depending on what symbols you define +// before including it. +// +// +// Header-file mode: +// +// If you do not define STB_TEXTEDIT_IMPLEMENTATION before including this, +// it will operate in "header file" mode. In this mode, it declares a +// single public symbol, STB_TexteditState, which encapsulates the current +// state of a text widget (except for the string, which you will store +// separately). +// +// To compile in this mode, you must define STB_TEXTEDIT_CHARTYPE to a +// primitive type that defines a single character (e.g. char, wchar_t, etc). +// +// To save space or increase undo-ability, you can optionally define the +// following things that are used by the undo system: +// +// STB_TEXTEDIT_POSITIONTYPE small int type encoding a valid cursor position +// STB_TEXTEDIT_UNDOSTATECOUNT the number of undo states to allow +// STB_TEXTEDIT_UNDOCHARCOUNT the number of characters to store in the undo buffer +// +// If you don't define these, they are set to permissive types and +// moderate sizes. The undo system does no memory allocations, so +// it grows STB_TexteditState by the worst-case storage which is (in bytes): +// +// [4 + sizeof(STB_TEXTEDIT_POSITIONTYPE)] * STB_TEXTEDIT_UNDOSTATE_COUNT +// + sizeof(STB_TEXTEDIT_CHARTYPE) * STB_TEXTEDIT_UNDOCHAR_COUNT +// +// +// Implementation mode: +// +// If you define STB_TEXTEDIT_IMPLEMENTATION before including this, it +// will compile the implementation of the text edit widget, depending +// on a large number of symbols which must be defined before the include. +// +// The implementation is defined only as static functions. You will then +// need to provide your own APIs in the same file which will access the +// static functions. +// +// The basic concept is that you provide a "string" object which +// behaves like an array of characters. stb_textedit uses indices to +// refer to positions in the string, implicitly representing positions +// in the displayed textedit. This is true for both plain text and +// rich text; even with rich text stb_truetype interacts with your +// code as if there was an array of all the displayed characters. +// +// Symbols that must be the same in header-file and implementation mode: +// +// STB_TEXTEDIT_CHARTYPE the character type +// STB_TEXTEDIT_POSITIONTYPE small type that a valid cursor position +// STB_TEXTEDIT_UNDOSTATECOUNT the number of undo states to allow +// STB_TEXTEDIT_UNDOCHARCOUNT the number of characters to store in the undo buffer +// +// Symbols you must define for implementation mode: +// +// STB_TEXTEDIT_STRING the type of object representing a string being edited, +// typically this is a wrapper object with other data you need +// +// STB_TEXTEDIT_STRINGLEN(obj) the length of the string (ideally O(1)) +// STB_TEXTEDIT_LAYOUTROW(&r,obj,n) returns the results of laying out a line of characters +// starting from character #n (see discussion below) +// STB_TEXTEDIT_GETWIDTH(obj,n,i) returns the pixel delta from the xpos of the i'th character +// to the xpos of the i+1'th char for a line of characters +// starting at character #n (i.e. accounts for kerning +// with previous char) +// STB_TEXTEDIT_KEYTOTEXT(k) maps a keyboard input to an insertable character +// (return type is int, -1 means not valid to insert) +// STB_TEXTEDIT_GETCHAR(obj,i) returns the i'th character of obj, 0-based +// STB_TEXTEDIT_NEWLINE the character returned by _GETCHAR() we recognize +// as manually wordwrapping for end-of-line positioning +// +// STB_TEXTEDIT_DELETECHARS(obj,i,n) delete n characters starting at i +// STB_TEXTEDIT_INSERTCHARS(obj,i,c*,n) insert n characters at i (pointed to by STB_TEXTEDIT_CHARTYPE*) +// +// STB_TEXTEDIT_K_SHIFT a power of two that is or'd in to a keyboard input to represent the shift key +// +// STB_TEXTEDIT_K_LEFT keyboard input to move cursor left +// STB_TEXTEDIT_K_RIGHT keyboard input to move cursor right +// STB_TEXTEDIT_K_UP keyboard input to move cursor up +// STB_TEXTEDIT_K_DOWN keyboard input to move cursor down +// STB_TEXTEDIT_K_LINESTART keyboard input to move cursor to start of line // e.g. HOME +// STB_TEXTEDIT_K_LINEEND keyboard input to move cursor to end of line // e.g. END +// STB_TEXTEDIT_K_TEXTSTART keyboard input to move cursor to start of text // e.g. ctrl-HOME +// STB_TEXTEDIT_K_TEXTEND keyboard input to move cursor to end of text // e.g. ctrl-END +// STB_TEXTEDIT_K_DELETE keyboard input to delete selection or character under cursor +// STB_TEXTEDIT_K_BACKSPACE keyboard input to delete selection or character left of cursor +// STB_TEXTEDIT_K_UNDO keyboard input to perform undo +// STB_TEXTEDIT_K_REDO keyboard input to perform redo +// +// Optional: +// STB_TEXTEDIT_K_INSERT keyboard input to toggle insert mode +// STB_TEXTEDIT_IS_SPACE(ch) true if character is whitespace (e.g. 'isspace'), +// required for WORDLEFT/WORDRIGHT +// STB_TEXTEDIT_K_WORDLEFT keyboard input to move cursor left one word // e.g. ctrl-LEFT +// STB_TEXTEDIT_K_WORDRIGHT keyboard input to move cursor right one word // e.g. ctrl-RIGHT +// STB_TEXTEDIT_K_LINESTART2 secondary keyboard input to move cursor to start of line +// STB_TEXTEDIT_K_LINEEND2 secondary keyboard input to move cursor to end of line +// STB_TEXTEDIT_K_TEXTSTART2 secondary keyboard input to move cursor to start of text +// STB_TEXTEDIT_K_TEXTEND2 secondary keyboard input to move cursor to end of text +// +// Todo: +// STB_TEXTEDIT_K_PGUP keyboard input to move cursor up a page +// STB_TEXTEDIT_K_PGDOWN keyboard input to move cursor down a page +// +// Keyboard input must be encoded as a single integer value; e.g. a character code +// and some bitflags that represent shift states. to simplify the interface, SHIFT must +// be a bitflag, so we can test the shifted state of cursor movements to allow selection, +// i.e. (STB_TEXTED_K_RIGHT|STB_TEXTEDIT_K_SHIFT) should be shifted right-arrow. +// +// You can encode other things, such as CONTROL or ALT, in additional bits, and +// then test for their presence in e.g. STB_TEXTEDIT_K_WORDLEFT. For example, +// my Windows implementations add an additional CONTROL bit, and an additional KEYDOWN +// bit. Then all of the STB_TEXTEDIT_K_ values bitwise-or in the KEYDOWN bit, +// and I pass both WM_KEYDOWN and WM_CHAR events to the "key" function in the +// API below. The control keys will only match WM_KEYDOWN events because of the +// keydown bit I add, and STB_TEXTEDIT_KEYTOTEXT only tests for the KEYDOWN +// bit so it only decodes WM_CHAR events. +// +// STB_TEXTEDIT_LAYOUTROW returns information about the shape of one displayed +// row of characters assuming they start on the i'th character--the width and +// the height and the number of characters consumed. This allows this library +// to traverse the entire layout incrementally. You need to compute word-wrapping +// here. +// +// Each textfield keeps its own insert mode state, which is not how normal +// applications work. To keep an app-wide insert mode, update/copy the +// "insert_mode" field of STB_TexteditState before/after calling API functions. +// +// API +// +// void stb_textedit_initialize_state(STB_TexteditState *state, int is_single_line) +// +// void stb_textedit_click(STB_TEXTEDIT_STRING *str, STB_TexteditState *state, float x, float y) +// void stb_textedit_drag(STB_TEXTEDIT_STRING *str, STB_TexteditState *state, float x, float y) +// int stb_textedit_cut(STB_TEXTEDIT_STRING *str, STB_TexteditState *state) +// int stb_textedit_paste(STB_TEXTEDIT_STRING *str, STB_TexteditState *state, STB_TEXTEDIT_CHARTYPE *text, int len) +// void stb_textedit_key(STB_TEXTEDIT_STRING *str, STB_TexteditState *state, int key) +// +// Each of these functions potentially updates the string and updates the +// state. +// +// initialize_state: +// set the textedit state to a known good default state when initially +// constructing the textedit. +// +// click: +// call this with the mouse x,y on a mouse down; it will update the cursor +// and reset the selection start/end to the cursor point. the x,y must +// be relative to the text widget, with (0,0) being the top left. +// +// drag: +// call this with the mouse x,y on a mouse drag/up; it will update the +// cursor and the selection end point +// +// cut: +// call this to delete the current selection; returns true if there was +// one. you should FIRST copy the current selection to the system paste buffer. +// (To copy, just copy the current selection out of the string yourself.) +// +// paste: +// call this to paste text at the current cursor point or over the current +// selection if there is one. +// +// key: +// call this for keyboard inputs sent to the textfield. you can use it +// for "key down" events or for "translated" key events. if you need to +// do both (as in Win32), or distinguish Unicode characters from control +// inputs, set a high bit to distinguish the two; then you can define the +// various definitions like STB_TEXTEDIT_K_LEFT have the is-key-event bit +// set, and make STB_TEXTEDIT_KEYTOCHAR check that the is-key-event bit is +// clear. +// +// When rendering, you can read the cursor position and selection state from +// the STB_TexteditState. +// +// +// Notes: +// +// This is designed to be usable in IMGUI, so it allows for the possibility of +// running in an IMGUI that has NOT cached the multi-line layout. For this +// reason, it provides an interface that is compatible with computing the +// layout incrementally--we try to make sure we make as few passes through +// as possible. (For example, to locate the mouse pointer in the text, we +// could define functions that return the X and Y positions of characters +// and binary search Y and then X, but if we're doing dynamic layout this +// will run the layout algorithm many times, so instead we manually search +// forward in one pass. Similar logic applies to e.g. up-arrow and +// down-arrow movement.) +// +// If it's run in a widget that *has* cached the layout, then this is less +// efficient, but it's not horrible on modern computers. But you wouldn't +// want to edit million-line files with it. + + +//////////////////////////////////////////////////////////////////////////// +//////////////////////////////////////////////////////////////////////////// +//// +//// Header-file mode +//// +//// + +#ifndef INCLUDE_STB_TEXTEDIT_H +#define INCLUDE_STB_TEXTEDIT_H + +//////////////////////////////////////////////////////////////////////// +// +// STB_TexteditState +// +// Definition of STB_TexteditState which you should store +// per-textfield; it includes cursor position, selection state, +// and undo state. +// + +#ifndef STB_TEXTEDIT_UNDOSTATECOUNT +#define STB_TEXTEDIT_UNDOSTATECOUNT 99 +#endif +#ifndef STB_TEXTEDIT_UNDOCHARCOUNT +#define STB_TEXTEDIT_UNDOCHARCOUNT 999 +#endif +#ifndef STB_TEXTEDIT_CHARTYPE +#define STB_TEXTEDIT_CHARTYPE int +#endif +#ifndef STB_TEXTEDIT_POSITIONTYPE +#define STB_TEXTEDIT_POSITIONTYPE int +#endif + +typedef struct +{ + // private data + STB_TEXTEDIT_POSITIONTYPE where; + short insert_length; + short delete_length; + short char_storage; +} StbUndoRecord; + +typedef struct +{ + // private data + StbUndoRecord undo_rec [STB_TEXTEDIT_UNDOSTATECOUNT]; + STB_TEXTEDIT_CHARTYPE undo_char[STB_TEXTEDIT_UNDOCHARCOUNT]; + short undo_point, redo_point; + short undo_char_point, redo_char_point; +} StbUndoState; + +typedef struct +{ + ///////////////////// + // + // public data + // + + int cursor; + // position of the text cursor within the string + + int select_start; // selection start point + int select_end; + // selection start and end point in characters; if equal, no selection. + // note that start may be less than or greater than end (e.g. when + // dragging the mouse, start is where the initial click was, and you + // can drag in either direction) + + unsigned char insert_mode; + // each textfield keeps its own insert mode state. to keep an app-wide + // insert mode, copy this value in/out of the app state + + ///////////////////// + // + // private data + // + unsigned char cursor_at_end_of_line; // not implemented yet + unsigned char initialized; + unsigned char has_preferred_x; + unsigned char single_line; + unsigned char padding1, padding2, padding3; + float preferred_x; // this determines where the cursor up/down tries to seek to along x + StbUndoState undostate; +} STB_TexteditState; + + +//////////////////////////////////////////////////////////////////////// +// +// StbTexteditRow +// +// Result of layout query, used by stb_textedit to determine where +// the text in each row is. + +// result of layout query +typedef struct +{ + float x0,x1; // starting x location, end x location (allows for align=right, etc) + float baseline_y_delta; // position of baseline relative to previous row's baseline + float ymin,ymax; // height of row above and below baseline + int num_chars; +} StbTexteditRow; +#endif //INCLUDE_STB_TEXTEDIT_H + + +//////////////////////////////////////////////////////////////////////////// +//////////////////////////////////////////////////////////////////////////// +//// +//// Implementation mode +//// +//// + + +// implementation isn't include-guarded, since it might have indirectly +// included just the "header" portion +#ifdef STB_TEXTEDIT_IMPLEMENTATION + +#ifndef STB_TEXTEDIT_memmove +#include +#define STB_TEXTEDIT_memmove memmove +#endif + + +///////////////////////////////////////////////////////////////////////////// +// +// Mouse input handling +// + +// traverse the layout to locate the nearest character to a display position +static int stb_text_locate_coord(STB_TEXTEDIT_STRING *str, float x, float y) +{ + StbTexteditRow r; + int n = STB_TEXTEDIT_STRINGLEN(str); + float base_y = 0, prev_x; + int i=0, k; + + r.x0 = r.x1 = 0; + r.ymin = r.ymax = 0; + r.num_chars = 0; + + // search rows to find one that straddles 'y' + while (i < n) { + STB_TEXTEDIT_LAYOUTROW(&r, str, i); + if (r.num_chars <= 0) + return n; + + if (i==0 && y < base_y + r.ymin) + return 0; + + if (y < base_y + r.ymax) + break; + + i += r.num_chars; + base_y += r.baseline_y_delta; + } + + // below all text, return 'after' last character + if (i >= n) + return n; + + // check if it's before the beginning of the line + if (x < r.x0) + return i; + + // check if it's before the end of the line + if (x < r.x1) { + // search characters in row for one that straddles 'x' + k = i; + prev_x = r.x0; + for (i=0; i < r.num_chars; ++i) { + float w = STB_TEXTEDIT_GETWIDTH(str, k, i); + if (x < prev_x+w) { + if (x < prev_x+w/2) + return k+i; + else + return k+i+1; + } + prev_x += w; + } + // shouldn't happen, but if it does, fall through to end-of-line case + } + + // if the last character is a newline, return that. otherwise return 'after' the last character + if (STB_TEXTEDIT_GETCHAR(str, i+r.num_chars-1) == STB_TEXTEDIT_NEWLINE) + return i+r.num_chars-1; + else + return i+r.num_chars; +} + +// API click: on mouse down, move the cursor to the clicked location, and reset the selection +static void stb_textedit_click(STB_TEXTEDIT_STRING *str, STB_TexteditState *state, float x, float y) +{ + state->cursor = stb_text_locate_coord(str, x, y); + state->select_start = state->cursor; + state->select_end = state->cursor; + state->has_preferred_x = 0; +} + +// API drag: on mouse drag, move the cursor and selection endpoint to the clicked location +static void stb_textedit_drag(STB_TEXTEDIT_STRING *str, STB_TexteditState *state, float x, float y) +{ + int p = stb_text_locate_coord(str, x, y); + if (state->select_start == state->select_end) + state->select_start = state->cursor; + state->cursor = state->select_end = p; +} + +///////////////////////////////////////////////////////////////////////////// +// +// Keyboard input handling +// + +// forward declarations +static void stb_text_undo(STB_TEXTEDIT_STRING *str, STB_TexteditState *state); +static void stb_text_redo(STB_TEXTEDIT_STRING *str, STB_TexteditState *state); +static void stb_text_makeundo_delete(STB_TEXTEDIT_STRING *str, STB_TexteditState *state, int where, int length); +static void stb_text_makeundo_insert(STB_TexteditState *state, int where, int length); +static void stb_text_makeundo_replace(STB_TEXTEDIT_STRING *str, STB_TexteditState *state, int where, int old_length, int new_length); + +typedef struct +{ + float x,y; // position of n'th character + float height; // height of line + int first_char, length; // first char of row, and length + int prev_first; // first char of previous row +} StbFindState; + +// find the x/y location of a character, and remember info about the previous row in +// case we get a move-up event (for page up, we'll have to rescan) +static void stb_textedit_find_charpos(StbFindState *find, STB_TEXTEDIT_STRING *str, int n, int single_line) +{ + StbTexteditRow r; + int prev_start = 0; + int z = STB_TEXTEDIT_STRINGLEN(str); + int i=0, first; + + if (n == z) { + // if it's at the end, then find the last line -- simpler than trying to + // explicitly handle this case in the regular code + if (single_line) { + STB_TEXTEDIT_LAYOUTROW(&r, str, 0); + find->y = 0; + find->first_char = 0; + find->length = z; + find->height = r.ymax - r.ymin; + find->x = r.x1; + } else { + find->y = 0; + find->x = 0; + find->height = 1; + while (i < z) { + STB_TEXTEDIT_LAYOUTROW(&r, str, i); + prev_start = i; + i += r.num_chars; + } + find->first_char = i; + find->length = 0; + find->prev_first = prev_start; + } + return; + } + + // search rows to find the one that straddles character n + find->y = 0; + + for(;;) { + STB_TEXTEDIT_LAYOUTROW(&r, str, i); + if (n < i + r.num_chars) + break; + prev_start = i; + i += r.num_chars; + find->y += r.baseline_y_delta; + } + + find->first_char = first = i; + find->length = r.num_chars; + find->height = r.ymax - r.ymin; + find->prev_first = prev_start; + + // now scan to find xpos + find->x = r.x0; + i = 0; + for (i=0; first+i < n; ++i) + find->x += STB_TEXTEDIT_GETWIDTH(str, first, i); +} + +#define STB_TEXT_HAS_SELECTION(s) ((s)->select_start != (s)->select_end) + +// make the selection/cursor state valid if client altered the string +static void stb_textedit_clamp(STB_TEXTEDIT_STRING *str, STB_TexteditState *state) +{ + int n = STB_TEXTEDIT_STRINGLEN(str); + if (STB_TEXT_HAS_SELECTION(state)) { + if (state->select_start > n) state->select_start = n; + if (state->select_end > n) state->select_end = n; + // if clamping forced them to be equal, move the cursor to match + if (state->select_start == state->select_end) + state->cursor = state->select_start; + } + if (state->cursor > n) state->cursor = n; +} + +// delete characters while updating undo +static void stb_textedit_delete(STB_TEXTEDIT_STRING *str, STB_TexteditState *state, int where, int len) +{ + stb_text_makeundo_delete(str, state, where, len); + STB_TEXTEDIT_DELETECHARS(str, where, len); + state->has_preferred_x = 0; +} + +// delete the section +static void stb_textedit_delete_selection(STB_TEXTEDIT_STRING *str, STB_TexteditState *state) +{ + stb_textedit_clamp(str, state); + if (STB_TEXT_HAS_SELECTION(state)) { + if (state->select_start < state->select_end) { + stb_textedit_delete(str, state, state->select_start, state->select_end - state->select_start); + state->select_end = state->cursor = state->select_start; + } else { + stb_textedit_delete(str, state, state->select_end, state->select_start - state->select_end); + state->select_start = state->cursor = state->select_end; + } + state->has_preferred_x = 0; + } +} + +// canoncialize the selection so start <= end +static void stb_textedit_sortselection(STB_TexteditState *state) +{ + if (state->select_end < state->select_start) { + int temp = state->select_end; + state->select_end = state->select_start; + state->select_start = temp; + } +} + +// move cursor to first character of selection +static void stb_textedit_move_to_first(STB_TexteditState *state) +{ + if (STB_TEXT_HAS_SELECTION(state)) { + stb_textedit_sortselection(state); + state->cursor = state->select_start; + state->select_end = state->select_start; + state->has_preferred_x = 0; + } +} + +// move cursor to last character of selection +static void stb_textedit_move_to_last(STB_TEXTEDIT_STRING *str, STB_TexteditState *state) +{ + if (STB_TEXT_HAS_SELECTION(state)) { + stb_textedit_sortselection(state); + stb_textedit_clamp(str, state); + state->cursor = state->select_end; + state->select_start = state->select_end; + state->has_preferred_x = 0; + } +} + +#ifdef STB_TEXTEDIT_IS_SPACE +static int is_word_boundary( STB_TEXTEDIT_STRING *_str, int _idx ) +{ + return _idx > 0 ? (STB_TEXTEDIT_IS_SPACE( STB_TEXTEDIT_GETCHAR(_str,_idx-1) ) && !STB_TEXTEDIT_IS_SPACE( STB_TEXTEDIT_GETCHAR(_str, _idx) ) ) : 1; +} + +static int stb_textedit_move_to_word_previous( STB_TEXTEDIT_STRING *_str, STB_TexteditState *_state ) +{ + int c = _state->cursor - 1; + while( c >= 0 && !is_word_boundary( _str, c ) ) + --c; + + if( c < 0 ) + c = 0; + + return c; +} + +static int stb_textedit_move_to_word_next( STB_TEXTEDIT_STRING *_str, STB_TexteditState *_state ) +{ + const int len = STB_TEXTEDIT_STRINGLEN(_str); + int c = _state->cursor+1; + while( c < len && !is_word_boundary( _str, c ) ) + ++c; + + if( c > len ) + c = len; + + return c; +} +#endif + +// update selection and cursor to match each other +static void stb_textedit_prep_selection_at_cursor(STB_TexteditState *state) +{ + if (!STB_TEXT_HAS_SELECTION(state)) + state->select_start = state->select_end = state->cursor; + else + state->cursor = state->select_end; +} + +// API cut: delete selection +static int stb_textedit_cut(STB_TEXTEDIT_STRING *str, STB_TexteditState *state) +{ + if (STB_TEXT_HAS_SELECTION(state)) { + stb_textedit_delete_selection(str,state); // implicity clamps + state->has_preferred_x = 0; + return 1; + } + return 0; +} + +// API paste: replace existing selection with passed-in text +static int stb_textedit_paste(STB_TEXTEDIT_STRING *str, STB_TexteditState *state, STB_TEXTEDIT_CHARTYPE const *ctext, int len) +{ + STB_TEXTEDIT_CHARTYPE *text = (STB_TEXTEDIT_CHARTYPE *) ctext; + // if there's a selection, the paste should delete it + stb_textedit_clamp(str, state); + stb_textedit_delete_selection(str,state); + // try to insert the characters + if (STB_TEXTEDIT_INSERTCHARS(str, state->cursor, text, len)) { + stb_text_makeundo_insert(state, state->cursor, len); + state->cursor += len; + state->has_preferred_x = 0; + return 1; + } + // remove the undo since we didn't actually insert the characters + if (state->undostate.undo_point) + --state->undostate.undo_point; + return 0; +} + +// API key: process a keyboard input +static void stb_textedit_key(STB_TEXTEDIT_STRING *str, STB_TexteditState *state, int key) +{ +retry: + switch (key) { + default: { + int c = STB_TEXTEDIT_KEYTOTEXT(key); + if (c > 0) { + STB_TEXTEDIT_CHARTYPE ch = (STB_TEXTEDIT_CHARTYPE) c; + + // can't add newline in single-line mode + if (c == '\n' && state->single_line) + break; + + if (state->insert_mode && !STB_TEXT_HAS_SELECTION(state) && state->cursor < STB_TEXTEDIT_STRINGLEN(str)) { + stb_text_makeundo_replace(str, state, state->cursor, 1, 1); + STB_TEXTEDIT_DELETECHARS(str, state->cursor, 1); + if (STB_TEXTEDIT_INSERTCHARS(str, state->cursor, &ch, 1)) { + ++state->cursor; + state->has_preferred_x = 0; + } + } else { + stb_textedit_delete_selection(str,state); // implicity clamps + if (STB_TEXTEDIT_INSERTCHARS(str, state->cursor, &ch, 1)) { + stb_text_makeundo_insert(state, state->cursor, 1); + ++state->cursor; + state->has_preferred_x = 0; + } + } + } + break; + } + +#ifdef STB_TEXTEDIT_K_INSERT + case STB_TEXTEDIT_K_INSERT: + state->insert_mode = !state->insert_mode; + break; +#endif + + case STB_TEXTEDIT_K_UNDO: + stb_text_undo(str, state); + state->has_preferred_x = 0; + break; + + case STB_TEXTEDIT_K_REDO: + stb_text_redo(str, state); + state->has_preferred_x = 0; + break; + + case STB_TEXTEDIT_K_LEFT: + // if currently there's a selection, move cursor to start of selection + if (STB_TEXT_HAS_SELECTION(state)) + stb_textedit_move_to_first(state); + else + if (state->cursor > 0) + --state->cursor; + state->has_preferred_x = 0; + break; + + case STB_TEXTEDIT_K_RIGHT: + // if currently there's a selection, move cursor to end of selection + if (STB_TEXT_HAS_SELECTION(state)) + stb_textedit_move_to_last(str, state); + else + ++state->cursor; + stb_textedit_clamp(str, state); + state->has_preferred_x = 0; + break; + + case STB_TEXTEDIT_K_LEFT | STB_TEXTEDIT_K_SHIFT: + stb_textedit_clamp(str, state); + stb_textedit_prep_selection_at_cursor(state); + // move selection left + if (state->select_end > 0) + --state->select_end; + state->cursor = state->select_end; + state->has_preferred_x = 0; + break; + +#ifdef STB_TEXTEDIT_IS_SPACE + case STB_TEXTEDIT_K_WORDLEFT: + if (STB_TEXT_HAS_SELECTION(state)) + stb_textedit_move_to_first(state); + else { + state->cursor = stb_textedit_move_to_word_previous(str, state); + stb_textedit_clamp( str, state ); + } + break; + + case STB_TEXTEDIT_K_WORDRIGHT: + if (STB_TEXT_HAS_SELECTION(state)) + stb_textedit_move_to_last(str, state); + else { + state->cursor = stb_textedit_move_to_word_next(str, state); + stb_textedit_clamp( str, state ); + } + break; + + case STB_TEXTEDIT_K_WORDLEFT | STB_TEXTEDIT_K_SHIFT: + if( !STB_TEXT_HAS_SELECTION( state ) ) + stb_textedit_prep_selection_at_cursor(state); + + state->cursor = stb_textedit_move_to_word_previous(str, state); + state->select_end = state->cursor; + + stb_textedit_clamp( str, state ); + break; + + case STB_TEXTEDIT_K_WORDRIGHT | STB_TEXTEDIT_K_SHIFT: + if( !STB_TEXT_HAS_SELECTION( state ) ) + stb_textedit_prep_selection_at_cursor(state); + + state->cursor = stb_textedit_move_to_word_next(str, state); + state->select_end = state->cursor; + + stb_textedit_clamp( str, state ); + break; +#endif + + case STB_TEXTEDIT_K_RIGHT | STB_TEXTEDIT_K_SHIFT: + stb_textedit_prep_selection_at_cursor(state); + // move selection right + ++state->select_end; + stb_textedit_clamp(str, state); + state->cursor = state->select_end; + state->has_preferred_x = 0; + break; + + case STB_TEXTEDIT_K_DOWN: + case STB_TEXTEDIT_K_DOWN | STB_TEXTEDIT_K_SHIFT: { + StbFindState find; + StbTexteditRow row; + int i, sel = (key & STB_TEXTEDIT_K_SHIFT) != 0; + + if (state->single_line) { + // on windows, up&down in single-line behave like left&right + key = STB_TEXTEDIT_K_RIGHT | (key & STB_TEXTEDIT_K_SHIFT); + goto retry; + } + + if (sel) + stb_textedit_prep_selection_at_cursor(state); + else if (STB_TEXT_HAS_SELECTION(state)) + stb_textedit_move_to_last(str,state); + + // compute current position of cursor point + stb_textedit_clamp(str, state); + stb_textedit_find_charpos(&find, str, state->cursor, state->single_line); + + // now find character position down a row + if (find.length) { + float goal_x = state->has_preferred_x ? state->preferred_x : find.x; + float x; + int start = find.first_char + find.length; + state->cursor = start; + STB_TEXTEDIT_LAYOUTROW(&row, str, state->cursor); + x = row.x0; + for (i=0; i < row.num_chars; ++i) { + float dx = STB_TEXTEDIT_GETWIDTH(str, start, i); + #ifdef STB_TEXTEDIT_GETWIDTH_NEWLINE + if (dx == STB_TEXTEDIT_GETWIDTH_NEWLINE) + break; + #endif + x += dx; + if (x > goal_x) + break; + ++state->cursor; + } + stb_textedit_clamp(str, state); + + state->has_preferred_x = 1; + state->preferred_x = goal_x; + + if (sel) + state->select_end = state->cursor; + } + break; + } + + case STB_TEXTEDIT_K_UP: + case STB_TEXTEDIT_K_UP | STB_TEXTEDIT_K_SHIFT: { + StbFindState find; + StbTexteditRow row; + int i, sel = (key & STB_TEXTEDIT_K_SHIFT) != 0; + + if (state->single_line) { + // on windows, up&down become left&right + key = STB_TEXTEDIT_K_LEFT | (key & STB_TEXTEDIT_K_SHIFT); + goto retry; + } + + if (sel) + stb_textedit_prep_selection_at_cursor(state); + else if (STB_TEXT_HAS_SELECTION(state)) + stb_textedit_move_to_first(state); + + // compute current position of cursor point + stb_textedit_clamp(str, state); + stb_textedit_find_charpos(&find, str, state->cursor, state->single_line); + + // can only go up if there's a previous row + if (find.prev_first != find.first_char) { + // now find character position up a row + float goal_x = state->has_preferred_x ? state->preferred_x : find.x; + float x; + state->cursor = find.prev_first; + STB_TEXTEDIT_LAYOUTROW(&row, str, state->cursor); + x = row.x0; + for (i=0; i < row.num_chars; ++i) { + float dx = STB_TEXTEDIT_GETWIDTH(str, find.prev_first, i); + #ifdef STB_TEXTEDIT_GETWIDTH_NEWLINE + if (dx == STB_TEXTEDIT_GETWIDTH_NEWLINE) + break; + #endif + x += dx; + if (x > goal_x) + break; + ++state->cursor; + } + stb_textedit_clamp(str, state); + + state->has_preferred_x = 1; + state->preferred_x = goal_x; + + if (sel) + state->select_end = state->cursor; + } + break; + } + + case STB_TEXTEDIT_K_DELETE: + case STB_TEXTEDIT_K_DELETE | STB_TEXTEDIT_K_SHIFT: + if (STB_TEXT_HAS_SELECTION(state)) + stb_textedit_delete_selection(str, state); + else { + int n = STB_TEXTEDIT_STRINGLEN(str); + if (state->cursor < n) + stb_textedit_delete(str, state, state->cursor, 1); + } + state->has_preferred_x = 0; + break; + + case STB_TEXTEDIT_K_BACKSPACE: + case STB_TEXTEDIT_K_BACKSPACE | STB_TEXTEDIT_K_SHIFT: + if (STB_TEXT_HAS_SELECTION(state)) + stb_textedit_delete_selection(str, state); + else { + stb_textedit_clamp(str, state); + if (state->cursor > 0) { + stb_textedit_delete(str, state, state->cursor-1, 1); + --state->cursor; + } + } + state->has_preferred_x = 0; + break; + +#ifdef STB_TEXTEDIT_K_TEXTSTART2 + case STB_TEXTEDIT_K_TEXTSTART2: +#endif + case STB_TEXTEDIT_K_TEXTSTART: + state->cursor = state->select_start = state->select_end = 0; + state->has_preferred_x = 0; + break; + +#ifdef STB_TEXTEDIT_K_TEXTEND2 + case STB_TEXTEDIT_K_TEXTEND2: +#endif + case STB_TEXTEDIT_K_TEXTEND: + state->cursor = STB_TEXTEDIT_STRINGLEN(str); + state->select_start = state->select_end = 0; + state->has_preferred_x = 0; + break; + +#ifdef STB_TEXTEDIT_K_TEXTSTART2 + case STB_TEXTEDIT_K_TEXTSTART2 | STB_TEXTEDIT_K_SHIFT: +#endif + case STB_TEXTEDIT_K_TEXTSTART | STB_TEXTEDIT_K_SHIFT: + stb_textedit_prep_selection_at_cursor(state); + state->cursor = state->select_end = 0; + state->has_preferred_x = 0; + break; + +#ifdef STB_TEXTEDIT_K_TEXTEND2 + case STB_TEXTEDIT_K_TEXTEND2 | STB_TEXTEDIT_K_SHIFT: +#endif + case STB_TEXTEDIT_K_TEXTEND | STB_TEXTEDIT_K_SHIFT: + stb_textedit_prep_selection_at_cursor(state); + state->cursor = state->select_end = STB_TEXTEDIT_STRINGLEN(str); + state->has_preferred_x = 0; + break; + + +#ifdef STB_TEXTEDIT_K_LINESTART2 + case STB_TEXTEDIT_K_LINESTART2: +#endif + case STB_TEXTEDIT_K_LINESTART: { + StbFindState find; + stb_textedit_clamp(str, state); + stb_textedit_move_to_first(state); + stb_textedit_find_charpos(&find, str, state->cursor, state->single_line); + state->cursor = find.first_char; + state->has_preferred_x = 0; + break; + } + +#ifdef STB_TEXTEDIT_K_LINEEND2 + case STB_TEXTEDIT_K_LINEEND2: +#endif + case STB_TEXTEDIT_K_LINEEND: { + StbFindState find; + stb_textedit_clamp(str, state); + stb_textedit_move_to_first(state); + stb_textedit_find_charpos(&find, str, state->cursor, state->single_line); + + state->has_preferred_x = 0; + state->cursor = find.first_char + find.length; + if (find.length > 0 && STB_TEXTEDIT_GETCHAR(str, state->cursor-1) == STB_TEXTEDIT_NEWLINE) + --state->cursor; + break; + } + +#ifdef STB_TEXTEDIT_K_LINESTART2 + case STB_TEXTEDIT_K_LINESTART2 | STB_TEXTEDIT_K_SHIFT: +#endif + case STB_TEXTEDIT_K_LINESTART | STB_TEXTEDIT_K_SHIFT: { + StbFindState find; + stb_textedit_clamp(str, state); + stb_textedit_prep_selection_at_cursor(state); + stb_textedit_find_charpos(&find, str, state->cursor, state->single_line); + state->cursor = state->select_end = find.first_char; + state->has_preferred_x = 0; + break; + } + +#ifdef STB_TEXTEDIT_K_LINEEND2 + case STB_TEXTEDIT_K_LINEEND2 | STB_TEXTEDIT_K_SHIFT: +#endif + case STB_TEXTEDIT_K_LINEEND | STB_TEXTEDIT_K_SHIFT: { + StbFindState find; + stb_textedit_clamp(str, state); + stb_textedit_prep_selection_at_cursor(state); + stb_textedit_find_charpos(&find, str, state->cursor, state->single_line); + state->has_preferred_x = 0; + state->cursor = find.first_char + find.length; + if (find.length > 0 && STB_TEXTEDIT_GETCHAR(str, state->cursor-1) == STB_TEXTEDIT_NEWLINE) + --state->cursor; + state->select_end = state->cursor; + break; + } + +// @TODO: +// STB_TEXTEDIT_K_PGUP - move cursor up a page +// STB_TEXTEDIT_K_PGDOWN - move cursor down a page + } +} + +///////////////////////////////////////////////////////////////////////////// +// +// Undo processing +// +// @OPTIMIZE: the undo/redo buffer should be circular + +static void stb_textedit_flush_redo(StbUndoState *state) +{ + state->redo_point = STB_TEXTEDIT_UNDOSTATECOUNT; + state->redo_char_point = STB_TEXTEDIT_UNDOCHARCOUNT; +} + +// discard the oldest entry in the undo list +static void stb_textedit_discard_undo(StbUndoState *state) +{ + if (state->undo_point > 0) { + // if the 0th undo state has characters, clean those up + if (state->undo_rec[0].char_storage >= 0) { + int n = state->undo_rec[0].insert_length, i; + // delete n characters from all other records + state->undo_char_point = state->undo_char_point - (short) n; // vsnet05 + STB_TEXTEDIT_memmove(state->undo_char, state->undo_char + n, (size_t) (state->undo_char_point*sizeof(STB_TEXTEDIT_CHARTYPE))); + for (i=0; i < state->undo_point; ++i) + if (state->undo_rec[i].char_storage >= 0) + state->undo_rec[i].char_storage = state->undo_rec[i].char_storage - (short) n; // vsnet05 // @OPTIMIZE: get rid of char_storage and infer it + } + --state->undo_point; + STB_TEXTEDIT_memmove(state->undo_rec, state->undo_rec+1, (size_t) (state->undo_point*sizeof(state->undo_rec[0]))); + } +} + +// discard the oldest entry in the redo list--it's bad if this +// ever happens, but because undo & redo have to store the actual +// characters in different cases, the redo character buffer can +// fill up even though the undo buffer didn't +static void stb_textedit_discard_redo(StbUndoState *state) +{ + int k = STB_TEXTEDIT_UNDOSTATECOUNT-1; + + if (state->redo_point <= k) { + // if the k'th undo state has characters, clean those up + if (state->undo_rec[k].char_storage >= 0) { + int n = state->undo_rec[k].insert_length, i; + // delete n characters from all other records + state->redo_char_point = state->redo_char_point + (short) n; // vsnet05 + STB_TEXTEDIT_memmove(state->undo_char + state->redo_char_point, state->undo_char + state->redo_char_point-n, (size_t) ((STB_TEXTEDIT_UNDOSTATECOUNT - state->redo_char_point)*sizeof(STB_TEXTEDIT_CHARTYPE))); + for (i=state->redo_point; i < k; ++i) + if (state->undo_rec[i].char_storage >= 0) + state->undo_rec[i].char_storage = state->undo_rec[i].char_storage + (short) n; // vsnet05 + } + ++state->redo_point; + STB_TEXTEDIT_memmove(state->undo_rec + state->redo_point-1, state->undo_rec + state->redo_point, (size_t) ((STB_TEXTEDIT_UNDOSTATECOUNT - state->redo_point)*sizeof(state->undo_rec[0]))); + } +} + +static StbUndoRecord *stb_text_create_undo_record(StbUndoState *state, int numchars) +{ + // any time we create a new undo record, we discard redo + stb_textedit_flush_redo(state); + + // if we have no free records, we have to make room, by sliding the + // existing records down + if (state->undo_point == STB_TEXTEDIT_UNDOSTATECOUNT) + stb_textedit_discard_undo(state); + + // if the characters to store won't possibly fit in the buffer, we can't undo + if (numchars > STB_TEXTEDIT_UNDOCHARCOUNT) { + state->undo_point = 0; + state->undo_char_point = 0; + return NULL; + } + + // if we don't have enough free characters in the buffer, we have to make room + while (state->undo_char_point + numchars > STB_TEXTEDIT_UNDOCHARCOUNT) + stb_textedit_discard_undo(state); + + return &state->undo_rec[state->undo_point++]; +} + +static STB_TEXTEDIT_CHARTYPE *stb_text_createundo(StbUndoState *state, int pos, int insert_len, int delete_len) +{ + StbUndoRecord *r = stb_text_create_undo_record(state, insert_len); + if (r == NULL) + return NULL; + + r->where = pos; + r->insert_length = (short) insert_len; + r->delete_length = (short) delete_len; + + if (insert_len == 0) { + r->char_storage = -1; + return NULL; + } else { + r->char_storage = state->undo_char_point; + state->undo_char_point = state->undo_char_point + (short) insert_len; + return &state->undo_char[r->char_storage]; + } +} + +static void stb_text_undo(STB_TEXTEDIT_STRING *str, STB_TexteditState *state) +{ + StbUndoState *s = &state->undostate; + StbUndoRecord u, *r; + if (s->undo_point == 0) + return; + + // we need to do two things: apply the undo record, and create a redo record + u = s->undo_rec[s->undo_point-1]; + r = &s->undo_rec[s->redo_point-1]; + r->char_storage = -1; + + r->insert_length = u.delete_length; + r->delete_length = u.insert_length; + r->where = u.where; + + if (u.delete_length) { + // if the undo record says to delete characters, then the redo record will + // need to re-insert the characters that get deleted, so we need to store + // them. + + // there are three cases: + // there's enough room to store the characters + // characters stored for *redoing* don't leave room for redo + // characters stored for *undoing* don't leave room for redo + // if the last is true, we have to bail + + if (s->undo_char_point + u.delete_length >= STB_TEXTEDIT_UNDOCHARCOUNT) { + // the undo records take up too much character space; there's no space to store the redo characters + r->insert_length = 0; + } else { + int i; + + // there's definitely room to store the characters eventually + while (s->undo_char_point + u.delete_length > s->redo_char_point) { + // there's currently not enough room, so discard a redo record + stb_textedit_discard_redo(s); + // should never happen: + if (s->redo_point == STB_TEXTEDIT_UNDOSTATECOUNT) + return; + } + r = &s->undo_rec[s->redo_point-1]; + + r->char_storage = s->redo_char_point - u.delete_length; + s->redo_char_point = s->redo_char_point - (short) u.delete_length; + + // now save the characters + for (i=0; i < u.delete_length; ++i) + s->undo_char[r->char_storage + i] = STB_TEXTEDIT_GETCHAR(str, u.where + i); + } + + // now we can carry out the deletion + STB_TEXTEDIT_DELETECHARS(str, u.where, u.delete_length); + } + + // check type of recorded action: + if (u.insert_length) { + // easy case: was a deletion, so we need to insert n characters + STB_TEXTEDIT_INSERTCHARS(str, u.where, &s->undo_char[u.char_storage], u.insert_length); + s->undo_char_point -= u.insert_length; + } + + state->cursor = u.where + u.insert_length; + + s->undo_point--; + s->redo_point--; +} + +static void stb_text_redo(STB_TEXTEDIT_STRING *str, STB_TexteditState *state) +{ + StbUndoState *s = &state->undostate; + StbUndoRecord *u, r; + if (s->redo_point == STB_TEXTEDIT_UNDOSTATECOUNT) + return; + + // we need to do two things: apply the redo record, and create an undo record + u = &s->undo_rec[s->undo_point]; + r = s->undo_rec[s->redo_point]; + + // we KNOW there must be room for the undo record, because the redo record + // was derived from an undo record + + u->delete_length = r.insert_length; + u->insert_length = r.delete_length; + u->where = r.where; + u->char_storage = -1; + + if (r.delete_length) { + // the redo record requires us to delete characters, so the undo record + // needs to store the characters + + if (s->undo_char_point + u->insert_length > s->redo_char_point) { + u->insert_length = 0; + u->delete_length = 0; + } else { + int i; + u->char_storage = s->undo_char_point; + s->undo_char_point = s->undo_char_point + u->insert_length; + + // now save the characters + for (i=0; i < u->insert_length; ++i) + s->undo_char[u->char_storage + i] = STB_TEXTEDIT_GETCHAR(str, u->where + i); + } + + STB_TEXTEDIT_DELETECHARS(str, r.where, r.delete_length); + } + + if (r.insert_length) { + // easy case: need to insert n characters + STB_TEXTEDIT_INSERTCHARS(str, r.where, &s->undo_char[r.char_storage], r.insert_length); + } + + state->cursor = r.where + r.insert_length; + + s->undo_point++; + s->redo_point++; +} + +static void stb_text_makeundo_insert(STB_TexteditState *state, int where, int length) +{ + stb_text_createundo(&state->undostate, where, 0, length); +} + +static void stb_text_makeundo_delete(STB_TEXTEDIT_STRING *str, STB_TexteditState *state, int where, int length) +{ + int i; + STB_TEXTEDIT_CHARTYPE *p = stb_text_createundo(&state->undostate, where, length, 0); + if (p) { + for (i=0; i < length; ++i) + p[i] = STB_TEXTEDIT_GETCHAR(str, where+i); + } +} + +static void stb_text_makeundo_replace(STB_TEXTEDIT_STRING *str, STB_TexteditState *state, int where, int old_length, int new_length) +{ + int i; + STB_TEXTEDIT_CHARTYPE *p = stb_text_createundo(&state->undostate, where, old_length, new_length); + if (p) { + for (i=0; i < old_length; ++i) + p[i] = STB_TEXTEDIT_GETCHAR(str, where+i); + } +} + +// reset the state to default +static void stb_textedit_clear_state(STB_TexteditState *state, int is_single_line) +{ + state->undostate.undo_point = 0; + state->undostate.undo_char_point = 0; + state->undostate.redo_point = STB_TEXTEDIT_UNDOSTATECOUNT; + state->undostate.redo_char_point = STB_TEXTEDIT_UNDOCHARCOUNT; + state->select_end = state->select_start = 0; + state->cursor = 0; + state->has_preferred_x = 0; + state->preferred_x = 0; + state->cursor_at_end_of_line = 0; + state->initialized = 1; + state->single_line = (unsigned char) is_single_line; + state->insert_mode = 0; +} + +// API initialize +static void stb_textedit_initialize_state(STB_TexteditState *state, int is_single_line) +{ + stb_textedit_clear_state(state, is_single_line); +} +#endif//STB_TEXTEDIT_IMPLEMENTATION diff --git a/impeller/third_party/stb/stb/stb_tilemap_editor.h b/impeller/third_party/stb/stb/stb_tilemap_editor.h new file mode 100644 index 0000000000000..cc66dd58f9623 --- /dev/null +++ b/impeller/third_party/stb/stb/stb_tilemap_editor.h @@ -0,0 +1,4131 @@ +// stb_tilemap_editor.h - v0.37 - Sean Barrett - http://nothings.org/stb +// placed in the public domain - not copyrighted - first released 2014-09 +// +// Embeddable tilemap editor for C/C++ +// +// +// TABLE OF CONTENTS +// FAQ +// How to compile/use the library +// Additional configuration macros +// API documentation +// Info on editing multiple levels +// Revision history +// Todo +// Credits +// License +// +// +// FAQ +// +// Q: What counts as a tilemap for this library? +// +// A: An array of rectangles, where each rectangle contains a small +// stack of images. +// +// Q: What are the limitations? +// +// A: Maps are limited to 4096x4096 in dimension. +// Each map square can only contain a stack of at most 32 images. +// A map can only use up to 32768 distinct image tiles. +// +// Q: How do I compile this? +// +// A: You need to #define several symbols before #including it, but only +// in one file. This will cause all the function definitions to be +// generated in that file. See the "HOW TO COMPILE" section. +// +// Q: What advantages does this have over a standalone editor? +// +// A: For one, you can integrate the editor into your game so you can +// flip between editing and testing without even switching windows. +// For another, you don't need an XML parser to get at the map data. +// +// Q: Can I live-edit my game maps? +// +// A: Not really, the editor keeps its own map representation. +// +// Q: How do I save and load maps? +// +// A: You have to do this yourself. The editor provides serialization +// functions (get & set) for reading and writing the map it holds. +// You can choose whatever format you want to store the map to on +// disk; you just need to provide functions to convert. (For example, +// I actually store the editor's map representation to disk basically +// as-is; then I have a single function that converts from the editor +// map representation to the game representation, which is used both +// to go from editor-to-game and from loaded-map-to-game.) +// +// Q: I want to have tiles change appearance based on what's +// adjacent, or other tile-display/substitution trickiness. +// +// A: You can do this when you convert from the editor's map +// representation to the game representation, but there's +// no way to show this live in the editor. +// +// Q: The editor appears to be put map location (0,0) at the top left? +// I want to use a different coordinate system in my game (e.g. y +// increasing upwards, or origin at the center). +// +// A: You can do this when you convert from the editor's map +// representation to the game representation. (Don't forget to +// translate link coordinates as well!) +// +// Q: The editor appears to put pixel (0,0) at the top left? I want +// to use a different coordinate system in my game. +// +// A: The editor defines an "editor pixel coordinate system" with +// (0,0) at the top left and requires you to display things in +// that coordinate system. You can freely remap those coordinates +// to anything you want on screen. +// +// Q: How do I scale the user interface? +// +// A: Since you do all the rendering, you can scale up all the rendering +// calls that the library makes to you. If you do, (a) you need +// to also scale up the mouse coordinates, and (b) you may want +// to scale the map display back down so that you're only scaling +// the UI and not everything. See the next question. +// +// Q: How do I scale the map display? +// +// A: Use stbte_set_spacing() to change the size that the map is displayed +// at. Note that the "callbacks" to draw tiles are used for both drawing +// the map and drawing the tile palette, so that callback may need to +// draw at two different scales. You should choose the scales to match +// You can tell them apart because the +// tile palette gets NULL for the property pointer. +// +// Q: How does object editing work? +// +// A: One way to think of this is that in the editor, you're placing +// spawners, not objects. Each spawner must be tile-aligned, because +// it's only a tile editor. Each tile (stack of layers) gets +// an associated set of properties, and it's up to you to +// determine what properties should appear for a given tile, +// based on e.g. the spawners that are in it. +// +// Q: How are properties themselves handled? +// +// A: All properties, regardless of UI behavior, are internally floats. +// Each tile has an array of floats associated with it, which is +// passed back to you when drawing the tiles so you can draw +// objects appropriately modified by the properties. +// +// Q: What if I want to have two different objects/spawners in +// one tile, both of which have their own properties? +// +// A: Make sure STBTE_MAX_PROPERTIES is large enough for the sum of +// properties in both objects, and then you have to explicitly +// map the property slot #s to the appropriate objects. They'll +// still all appear in a single property panel; there's no way +// to get multiple panels. +// +// Q: Can I do one-to-many linking? +// +// A: The library only supports one link per tile. However, you +// can have multiple tiles all link to a single tile. So, you +// can fake one-to-many linking by linking in the reverse +// direction. +// +// Q: What if I have two objects in the same tile, and they each +// need an independent link? Or I have two kinds of link associated +// with a single object? +// +// A: There is no way to do this. (Unless you can reverse one link.) +// +// Q: How does cut & paste interact with object properties & links? +// +// A: Currently the library has no idea which properties or links +// are associated with which layers of a tile. So currently, the +// library will only copy properties & links if the layer panel +// is set to allow all layers to be copied, OR if you set the +// "props" in the layer panel to "always". Similarly, you can +// set "props" to "none" so it will never copy. +// +// Q: What happens if the library gets a memory allocation failure +// while I'm editing? Will I lose my work? +// +// A: The library allocates all editor memory when you create +// the tilemap. It allocates a maximally-sized map and a +// fixed-size undo buffer (and the fixed-size copy buffer +// is static), and never allocates memory while it's running. +// So it can't fail due to running out of memory. +// +// Q: What happens if the library crashes while I'm editing? Will +// I lose my work? +// +// A: Yes. Save often. +// +// +// HOW TO COMPILE +// +// This header file contains both the header file and the +// implementation file in one. To create the implementation, +// in one source file define a few symbols first and then +// include this header: +// +// #define STB_TILEMAP_EDITOR_IMPLEMENTATION +// // this triggers the implementation +// +// void STBTE_DRAW_RECT(int x0, int y0, int x1, int y1, uint color); +// // this must draw a filled rectangle (exclusive on right/bottom) +// // color = (r<<16)|(g<<8)|(b) +// +// void STBTE_DRAW_TILE(int x0, int y0, +// unsigned short id, int highlight, float *data); +// // this draws the tile image identified by 'id' in one of several +// // highlight modes (see STBTE_drawmode_* in the header section); +// // if 'data' is NULL, it's drawing the tile in the palette; if 'data' +// // is not NULL, it's drawing a tile on the map, and that is the data +// // associated with that map tile +// +// #include "stb_tilemap_editor.h" +// +// Optionally you can define the following functions before the include; +// note these must be macros (but they can just call a function) so +// this library can #ifdef to detect if you've defined them: +// +// #define STBTE_PROP_TYPE(int n, short *tiledata, float *params) ... +// // Returns the type of the n'th property of a given tile, which +// // controls how it is edited. Legal types are: +// // 0 /* no editable property in this slot */ +// // STBTE_PROP_int /* uses a slider to adjust value */ +// // STBTE_PROP_float /* uses a weird multi-axis control */ +// // STBTE_PROP_bool /* uses a checkbox to change value */ +// // And you can bitwise-OR in the following flags: +// // STBTE_PROP_disabled +// // Note that all of these are stored as floats in the param array. +// // The integer slider is limited in precision based on the space +// // available on screen, so for wide-ranged integers you may want +// // to use floats instead. +// // +// // Since the tiledata is passed to you, you can choose which property +// // is bound to that slot based on that data. +// // +// // Changing the type of a parameter does not cause the underlying +// // value to be clamped to the type min/max except when the tile is +// // explicitly selected. +// +// #define STBTE_PROP_NAME(int n, short *tiledata, float *params) ... +// // these return a string with the name for slot #n in the float +// // property list for the tile. +// +// #define STBTE_PROP_MIN(int n, short *tiledata) ...your code here... +// #define STBTE_PROP_MAX(int n, short *tiledata) ...your code here... +// // These return the allowable range for the property values for +// // the specified slot. It is never called for boolean types. +// +// #define STBTE_PROP_FLOAT_SCALE(int n, short *tiledata, float *params) +// // This rescales the float control for a given property; by default +// // left mouse drags add integers, right mouse drags adds fractions, +// // but you can rescale this per-property. +// +// #define STBTE_FLOAT_CONTROL_GRANULARITY ... value ... +// // This returns the number of pixels of mouse motion necessary +// // to advance the object float control. Default is 4 +// +// #define STBTE_ALLOW_LINK(short *src, float *src_data, \ +// short *dest, float *dest_data) ...your code... +// // this returns true or false depending on whether you allow a link +// // to be drawn from a tile 'src' to a tile 'dest'. if you don't +// // define this, linking will not be supported +// +// #define STBTE_LINK_COLOR(short *src, float *src_data, \ +// short *dest, float *dest_data) ...your code... +// // return a color encoded as a 24-bit unsigned integer in the +// // form 0xRRGGBB. If you don't define this, default colors will +// // be used. +// +// +// [[ support for those below is not implemented yet ]] +// +// #define STBTE_HITTEST_TILE(x0,y0,id,mx,my) ...your code here... +// // this returns true or false depending on whether the mouse +// // pointer at mx,my is over (touching) a tile of type 'id' +// // displayed at x0,y0. Normally stb_tilemap_editor just does +// // this hittest based on the tile geometry, but if you have +// // tiles whose images extend out of the tile, you'll need this. +// +// ADDITIONAL CONFIGURATION +// +// The following symbols set static limits which determine how much +// memory will be allocated for the editor. You can override them +// by making similiar definitions, but memory usage will increase. +// +// #define STBTE_MAX_TILEMAP_X 200 // max 4096 +// #define STBTE_MAX_TILEMAP_Y 200 // max 4096 +// #define STBTE_MAX_LAYERS 8 // max 32 +// #define STBTE_MAX_CATEGORIES 100 +// #define STBTE_UNDO_BUFFER_BYTES (1 << 24) // 16 MB +// #define STBTE_MAX_COPY 90000 // e.g. 300x300 +// #define STBTE_MAX_PROPERTIES 10 // max properties per tile +// +// API +// +// Further documentation appears in the header-file section below. +// +// EDITING MULTIPLE LEVELS +// +// You can only have one active editor instance. To switch between multiple +// levels, you can either store the levels in your own format and copy them +// in and out of the editor format, or you can create multiple stbte_tilemap +// objects and switch between them. The latter has the advantage that each +// stbte_tilemap keeps its own undo state. (The clipboard is global, so +// either approach allows cut&pasting between levels.) +// +// REVISION HISTORY +// 0.37 fix warning +// 0.36 minor compiler support +// 0.35 layername button changes +// - layername buttons grow with the layer panel +// - fix stbte_create_map being declared as stbte_create +// - fix declaration of stbte_create_map +// 0.30 properties release +// - properties panel for editing user-defined "object" properties +// - can link each tile to one other tile +// - keyboard interface +// - fix eraser tool bug (worked in complex cases, failed in simple) +// - undo/redo tools have visible disabled state +// - tiles on higher layers draw on top of adjacent lower-layer tiles +// 0.20 erasable release +// - eraser tool +// - fix bug when pasting into protected layer +// - better color scheme +// - internal-use color picker +// 0.10 initial release +// +// TODO +// +// Separate scroll state for each category +// Implement paint bucket +// Support STBTE_HITTEST_TILE above +// ?Cancel drags by clicking other button? - may be fixed +// Finish support for toolbar at side +// +// CREDITS +// +// +// Main editor & features +// Sean Barrett +// Additional features: +// Josh Huelsman +// Bugfixes: +// Ryan Whitworth +// Eugene Opalev +// +// LICENSE +// +// This software is dual-licensed to the public domain and under the following +// license: you are granted a perpetual, irrevocable license to copy, modify, +// publish, and distribute this file as you see fit. + + + +/////////////////////////////////////////////////////////////////////// +// +// HEADER SECTION + +#ifndef STB_TILEMAP_INCLUDE_STB_TILEMAP_EDITOR_H +#define STB_TILEMAP_INCLUDE_STB_TILEMAP_EDITOR_H + +#ifdef _WIN32 + #ifndef _CRT_SECURE_NO_WARNINGS + #define _CRT_SECURE_NO_WARNINGS + #endif + #include + #include +#endif + +typedef struct stbte_tilemap stbte_tilemap; + +// these are the drawmodes used in STBTE_DRAW_TILE +enum +{ + STBTE_drawmode_deemphasize = -1, + STBTE_drawmode_normal = 0, + STBTE_drawmode_emphasize = 1, +}; + +// these are the property types +#define STBTE_PROP_none 0 +#define STBTE_PROP_int 1 +#define STBTE_PROP_float 2 +#define STBTE_PROP_bool 3 +#define STBTE_PROP_disabled 4 + +//////// +// +// creation +// + +extern stbte_tilemap *stbte_create_map(int map_x, int map_y, int map_layers, int spacing_x, int spacing_y, int max_tiles); +// create an editable tilemap +// map_x : dimensions of map horizontally (user can change this in editor), <= STBTE_MAX_TILEMAP_X +// map_y : dimensions of map vertically (user can change this in editor) <= STBTE_MAX_TILEMAP_Y +// map_layers : number of layers to use (fixed), <= STBTE_MAX_LAYERS +// spacing_x : initial horizontal distance between left edges of map tiles in stb_tilemap_editor pixels +// spacing_y : initial vertical distance between top edges of map tiles in stb_tilemap_editor pixels +// max_tiles : maximum number of tiles that can defined +// +// If insufficient memory, returns NULL + +extern void stbte_define_tile(stbte_tilemap *tm, unsigned short id, unsigned int layermask, const char * category); +// call this repeatedly for each tile to install the tile definitions into the editable tilemap +// tm : tilemap created by stbte_create_map +// id : unique identifier for each tile, 0 <= id < 32768 +// layermask : bitmask of which layers tile is allowed on: 1 = layer 0, 255 = layers 0..7 +// (note that onscreen, the editor numbers the layers from 1 not 0) +// layer 0 is the furthest back, layer 1 is just in front of layer 0, etc +// category : which category this tile is grouped in + +extern void stbte_set_display(int x0, int y0, int x1, int y1); +// call this once to set the size; if you resize, call it again + + +///////// +// +// every frame +// + +extern void stbte_draw(stbte_tilemap *tm); + +extern void stbte_tick(stbte_tilemap *tm, float time_in_seconds_since_last_frame); + +//////////// +// +// user input +// + +// if you're using SDL, call the next function for SDL_MOUSEMOVE, SDL_MOUSEBUTTON, SDL_MOUSEWHEEL; +// the transformation lets you scale from SDL mouse coords to stb_tilemap_editor coords +extern void stbte_mouse_sdl(stbte_tilemap *tm, const void *sdl_event, float xscale, float yscale, int xoffset, int yoffset); + +// otherwise, hook these up explicitly: +extern void stbte_mouse_move(stbte_tilemap *tm, int x, int y, int shifted, int scrollkey); +extern void stbte_mouse_button(stbte_tilemap *tm, int x, int y, int right, int down, int shifted, int scrollkey); +extern void stbte_mouse_wheel(stbte_tilemap *tm, int x, int y, int vscroll); + +// for keyboard, define your own mapping from keys to the following actions. +// this is totally optional, as all features are accessible with the mouse +enum stbte_action +{ + STBTE_tool_select, + STBTE_tool_brush, + STBTE_tool_erase, + STBTE_tool_rectangle, + STBTE_tool_eyedropper, + STBTE_tool_link, + STBTE_act_toggle_grid, + STBTE_act_toggle_links, + STBTE_act_undo, + STBTE_act_redo, + STBTE_act_cut, + STBTE_act_copy, + STBTE_act_paste, + STBTE_scroll_left, + STBTE_scroll_right, + STBTE_scroll_up, + STBTE_scroll_down, +}; +extern void stbte_action(stbte_tilemap *tm, enum stbte_action act); + +//////////////// +// +// save/load +// +// There is no editor file format. You have to save and load the data yourself +// through the following functions. You can also use these functions to get the +// data to generate game-formatted levels directly. (But make sure you save +// first! You may also want to autosave to a temp file periodically, etc etc.) + +#define STBTE_EMPTY -1 + +extern void stbte_get_dimensions(stbte_tilemap *tm, int *max_x, int *max_y); +// get the dimensions of the level, since the user can change them + +extern short* stbte_get_tile(stbte_tilemap *tm, int x, int y); +// returns an array of shorts that is 'map_layers' in length. each short is +// either one of the tile_id values from define_tile, or STBTE_EMPTY. + +extern float *stbte_get_properties(stbte_tilemap *tm, int x, int y); +// get the property array associated with the tile at x,y. this is an +// array of floats that is STBTE_MAX_PROPERTIES in length; you have to +// interpret the slots according to the semantics you've chosen + +extern void stbte_get_link(stbte_tilemap *tm, int x, int y, int *destx, int *desty); +// gets the link associated with the tile at x,y. + +extern void stbte_set_dimensions(stbte_tilemap *tm, int max_x, int max_y); +// set the dimensions of the level, overrides previous stbte_create_map() +// values or anything the user has changed + +extern void stbte_clear_map(stbte_tilemap *tm); +// clears the map, including the region outside the defined region, so if the +// user expands the map, they won't see garbage there + +extern void stbte_set_tile(stbte_tilemap *tm, int x, int y, int layer, signed short tile); +// tile is your tile_id from define_tile, or STBTE_EMPTY + +extern void stbte_set_property(stbte_tilemap *tm, int x, int y, int n, float val); +// set the value of the n'th slot of the tile at x,y + +extern void stbte_set_link(stbte_tilemap *tm, int x, int y, int destx, int desty); +// set a link going from x,y to destx,desty. to force no link, +// use destx=desty=-1 + +//////// +// +// optional +// + +extern void stbte_set_background_tile(stbte_tilemap *tm, short id); +// selects the tile to fill the bottom layer with and used to clear bottom tiles to; +// should be same ID as + +extern void stbte_set_sidewidths(int left, int right); +// call this once to set the left & right side widths. don't call +// it again since the user can change it + +extern void stbte_set_spacing(stbte_tilemap *tm, int spacing_x, int spacing_y, int palette_spacing_x, int palette_spacing_y); +// call this to set the spacing of map tiles and the spacing of palette tiles. +// if you rescale your display, call it again (e.g. you can implement map zooming yourself) + +extern void stbte_set_layername(stbte_tilemap *tm, int layer, const char *layername); +// sets a string name for your layer that shows in the layer selector. note that this +// makes the layer selector wider. 'layer' is from 0..(map_layers-1) + +#endif + +#ifdef STB_TILEMAP_EDITOR_IMPLEMENTATION + +#ifndef STBTE_ASSERT +#define STBTE_ASSERT assert +#include +#endif + +#ifdef _MSC_VER +#define STBTE__NOTUSED(v) (void)(v) +#else +#define STBTE__NOTUSED(v) (void)sizeof(v) +#endif + +#ifndef STBTE_MAX_TILEMAP_X +#define STBTE_MAX_TILEMAP_X 200 +#endif + +#ifndef STBTE_MAX_TILEMAP_Y +#define STBTE_MAX_TILEMAP_Y 200 +#endif + +#ifndef STBTE_MAX_LAYERS +#define STBTE_MAX_LAYERS 8 +#endif + +#ifndef STBTE_MAX_CATEGORIES +#define STBTE_MAX_CATEGORIES 100 +#endif + +#ifndef STBTE_MAX_COPY +#define STBTE_MAX_COPY 65536 +#endif + +#ifndef STBTE_UNDO_BUFFER_BYTES +#define STBTE_UNDO_BUFFER_BYTES (1 << 24) // 16 MB +#endif + +#ifndef STBTE_PROP_TYPE +#define STBTE__NO_PROPS +#define STBTE_PROP_TYPE(n,td,tp) 0 +#endif + +#ifndef STBTE_PROP_NAME +#define STBTE_PROP_NAME(n,td,tp) "" +#endif + +#ifndef STBTE_MAX_PROPERTIES +#define STBTE_MAX_PROPERTIES 10 +#endif + +#ifndef STBTE_PROP_MIN +#define STBTE_PROP_MIN(n,td,tp) 0 +#endif + +#ifndef STBTE_PROP_MAX +#define STBTE_PROP_MAX(n,td,tp) 100.0 +#endif + +#ifndef STBTE_PROP_FLOAT_SCALE +#define STBTE_PROP_FLOAT_SCALE(n,td,tp) 1 // default scale size +#endif + +#ifndef STBTE_FLOAT_CONTROL_GRANULARITY +#define STBTE_FLOAT_CONTROL_GRANULARITY 4 +#endif + + +#define STBTE__UNDO_BUFFER_COUNT (STBTE_UNDO_BUFFER_BYTES>>1) + +#if STBTE_MAX_TILEMAP_X > 4096 || STBTE_MAX_TILEMAP_Y > 4096 +#error "Maximum editable map size is 4096 x 4096" +#endif +#if STBTE_MAX_LAYERS > 32 +#error "Maximum layers allowed is 32" +#endif +#if STBTE_UNDO_BUFFER_COUNT & (STBTE_UNDO_BUFFER_COUNT-1) +#error "Undo buffer size must be a power of 2" +#endif + +#if STBTE_MAX_PROPERTIES == 0 +#define STBTE__NO_PROPS +#endif + +#ifdef STBTE__NO_PROPS +#undef STBTE_MAX_PROPERTIES +#define STBTE_MAX_PROPERTIES 1 // so we can declare arrays +#endif + +typedef struct +{ + short x,y; +} stbte__link; + +enum +{ + STBTE__base, + STBTE__outline, + STBTE__text, + + STBTE__num_color_aspects, +}; + +enum +{ + STBTE__idle, + STBTE__over, + STBTE__down, + STBTE__over_down, + STBTE__selected, + STBTE__selected_over, + STBTE__disabled, + STBTE__num_color_states, +}; + +enum +{ + STBTE__cexpander, + STBTE__ctoolbar, + STBTE__ctoolbar_button, + STBTE__cpanel, + STBTE__cpanel_sider, + STBTE__cpanel_sizer, + STBTE__cscrollbar, + STBTE__cmapsize, + STBTE__clayer_button, + STBTE__clayer_hide, + STBTE__clayer_lock, + STBTE__clayer_solo, + STBTE__ccategory_button, + + STBTE__num_color_modes, +}; + +#ifdef STBTE__COLORPICKER +static char *stbte__color_names[] = +{ + "expander", "toolbar", "tool button", "panel", + "panel c1", "panel c2", "scollbar", "map button", + "layer", "hide", "lock", "solo", + "category", +}; +#endif // STBTE__COLORPICKER + + // idle, over, down, over&down, selected, sel&over, disabled +static int stbte__color_table[STBTE__num_color_modes][STBTE__num_color_aspects][STBTE__num_color_states] = +{ + { + { 0x000000, 0x84987c, 0xdcdca8, 0xdcdca8, 0x40c040, 0x60d060, 0x505050, }, + { 0xa4b090, 0xe0ec80, 0xffffc0, 0xffffc0, 0x80ff80, 0x80ff80, 0x606060, }, + { 0xffffff, 0xffffff, 0xffffff, 0xffffff, 0xffffff, 0xffffff, 0x909090, }, + }, { + { 0x808890, 0x606060, 0x606060, 0x606060, 0x606060, 0x606060, 0x606060, }, + { 0x605860, 0x606060, 0x606060, 0x606060, 0x606060, 0x606060, 0x606060, }, + { 0x000000, 0x000000, 0x000000, 0x000000, 0x000000, 0x000000, 0x000000, }, + }, { + { 0x3c5068, 0x7088a8, 0x647488, 0x94b4dc, 0x8890c4, 0x9caccc, 0x404040, }, + { 0x889cb8, 0x889cb8, 0x889cb8, 0x889cb8, 0x84c4e8, 0xacc8ff, 0x0c0c08, }, + { 0xbcc4cc, 0xffffff, 0xffffff, 0xffffff, 0xffffff, 0xffffff, 0x707074, }, + }, { + { 0x403848, 0x403010, 0x403010, 0x403010, 0x403010, 0x403010, 0x303024, }, + { 0x68546c, 0xc08040, 0xc08040, 0xc08040, 0xc08040, 0xc08040, 0x605030, }, + { 0xf4e4ff, 0xffffff, 0xffffff, 0xffffff, 0xffffff, 0xffffff, 0x909090, }, + }, { + { 0xb4b04c, 0xacac60, 0xc0ffc0, 0xc0ffc0, 0x40c040, 0x60d060, 0x505050, }, + { 0xa0a04c, 0xd0d04c, 0xffff80, 0xffff80, 0x80ff80, 0x80ff80, 0x606060, }, + { 0xffffff, 0xffffff, 0xffffff, 0xffffff, 0xffffff, 0xffffff, 0x909090, }, + }, { + { 0x40c440, 0x60d060, 0xc0ffc0, 0xc0ffc0, 0x40c040, 0x60d060, 0x505050, }, + { 0x40c040, 0x80ff80, 0x80ff80, 0x80ff80, 0x80ff80, 0x80ff80, 0x606060, }, + { 0xffffff, 0xffffff, 0xffffff, 0xffffff, 0xffffff, 0xffffff, 0x909090, }, + }, { + { 0x9090ac, 0xa0a0b8, 0xbcb8cc, 0xbcb8cc, 0x909040, 0x909040, 0x909040, }, + { 0xa0a0b8, 0xb0b4d0, 0xa0a0b8, 0xa0a0b8, 0xa0a050, 0xa0a050, 0xa0a050, }, + { 0x808088, 0x808030, 0x808030, 0x808030, 0x808030, 0x808030, 0x808030, }, + }, { + { 0x704c70, 0x885c8c, 0x9c68a4, 0xb870bc, 0xb490bc, 0xb490bc, 0x302828, }, + { 0x646064, 0xcca8d4, 0xc060c0, 0xa07898, 0xe0b8e0, 0xe0b8e0, 0x403838, }, + { 0xdccce4, 0xffffff, 0xffffff, 0xffffff, 0xffffff, 0xffffff, 0x909090, }, + }, { + { 0x704c70, 0x885c8c, 0x9c68a4, 0xb870bc, 0xb490bc, 0xb490bc, 0x302828, }, + { 0xb09cb4, 0xcca8d4, 0xc060c0, 0xa07898, 0xe0b8e0, 0xe0b8e0, 0x403838, }, + { 0xdccce4, 0xffffff, 0xffffff, 0xffffff, 0xffffff, 0xffffff, 0x909090, }, + }, { + { 0x646494, 0x888cb8, 0xb0b0b0, 0xb0b0cc, 0x9c9cf4, 0x8888b0, 0x50506c, }, + { 0x9090a4, 0xb0b4d4, 0xb0b0dc, 0xb0b0cc, 0xd0d0fc, 0xd0d4f0, 0x606060, }, + { 0xb4b4d4, 0xe4e4ff, 0xffffff, 0xffffff, 0xe0e4ff, 0xececff, 0x909090, }, + }, { + { 0x646444, 0x888c64, 0xb0b0b0, 0xb0b088, 0xaca858, 0x88886c, 0x505050, }, + { 0x88886c, 0xb0b490, 0xb0b0b0, 0xb0b088, 0xd8d898, 0xd0d4b0, 0x606060, }, + { 0xb4b49c, 0xffffd8, 0xffffff, 0xffffd4, 0xffffdc, 0xffffcc, 0x909090, }, + }, { + { 0x906464, 0xb48c8c, 0xd4b0b0, 0xdcb0b0, 0xff9c9c, 0xc88888, 0x505050, }, + { 0xb47c80, 0xd4b4b8, 0xc4a8a8, 0xdcb0b0, 0xffc0c0, 0xfce8ec, 0x606060, }, + { 0xe0b4b4, 0xffdcd8, 0xffd8d4, 0xffe0e4, 0xffece8, 0xffffff, 0x909090, }, + }, { + { 0x403848, 0x403848, 0x403848, 0x886894, 0x7c80c8, 0x7c80c8, 0x302828, }, + { 0x403848, 0x403848, 0x403848, 0x403848, 0x7c80c8, 0x7c80c8, 0x403838, }, + { 0xc8c4c8, 0xffffff, 0xffffff, 0xffffff, 0xe8e8ec, 0xffffff, 0x909090, }, + }, +}; + +#define STBTE_COLOR_TILEMAP_BACKGROUND 0x000000 +#define STBTE_COLOR_TILEMAP_BORDER 0x203060 +#define STBTE_COLOR_TILEMAP_HIGHLIGHT 0xffffff +#define STBTE_COLOR_GRID 0x404040 +#define STBTE_COLOR_SELECTION_OUTLINE1 0xdfdfdf +#define STBTE_COLOR_SELECTION_OUTLINE2 0x303030 +#define STBTE_COLOR_TILEPALETTE_OUTLINE 0xffffff +#define STBTE_COLOR_TILEPALETTE_BACKGROUND 0x000000 + +#ifndef STBTE_LINK_COLOR +#define STBTE_LINK_COLOR(src,sp,dest,dp) 0x5030ff +#endif + +#ifndef STBTE_LINK_COLOR_DRAWING +#define STBTE_LINK_COLOR_DRAWING 0xff40ff +#endif + +#ifndef STBTE_LINK_COLOR_DISALLOWED +#define STBTE_LINK_COLOR_DISALLOWED 0x602060 +#endif + + +// disabled, selected, down, over +static unsigned char stbte__state_to_index[2][2][2][2] = +{ + { + { { STBTE__idle , STBTE__over }, { STBTE__down , STBTE__over_down }, }, + { { STBTE__selected, STBTE__selected_over }, { STBTE__down , STBTE__over_down }, }, + },{ + { { STBTE__disabled, STBTE__disabled }, { STBTE__disabled, STBTE__disabled }, }, + { { STBTE__selected, STBTE__selected_over }, { STBTE__disabled, STBTE__disabled }, }, + } +}; +#define STBTE__INDEX_FOR_STATE(disable,select,down,over) stbte__state_to_index[disable][select][down][over] +#define STBTE__INDEX_FOR_ID(id,disable,select) STBTE__INDEX_FOR_STATE(disable,select,STBTE__IS_ACTIVE(id),STBTE__IS_HOT(id)) + +#define STBTE__FONT_HEIGHT 9 +static short stbte__font_offset[95+16]; +static short stbte__fontdata[769] = +{ + 4,9,6,9,9,9,9,8,9,8,4,9,7,7,7,7,4,2,6,8,6,6,7,3,4,4,8,6,3,6,2,6,6,6,6,6,6, + 6,6,6,6,6,2,3,5,4,5,6,6,6,6,6,6,6,6,6,6,6,6,7,6,7,7,7,6,7,6,6,6,6,7,7,6,6, + 6,4,6,4,7,7,3,6,6,5,6,6,5,6,6,4,5,6,4,7,6,6,6,6,6,6,6,6,6,7,6,6,6,5,2,5,8, + 0,0,0,0,2,253,130,456,156,8,72,184,64,2,125,66,64,160,64,146,511,146,146, + 511,146,146,511,146,511,257,341,297,341,297,341,257,511,16,56,124,16,16,16, + 124,56,16,96,144,270,261,262,136,80,48,224,192,160,80,40,22,14,15,3,448,496, + 496,240,232,20,10,5,2,112,232,452,450,225,113,58,28,63,30,60,200,455,257, + 257,0,0,0,257,257,455,120,204,132,132,159,14,4,4,14,159,132,132,204,120,8, + 24,56,120,56,24,8,32,48,56,60,56,48,32,0,0,0,0,111,111,7,7,0,0,7,7,34,127, + 127,34,34,127,127,34,36,46,107,107,58,18,99,51,24,12,102,99,48,122,79,93, + 55,114,80,4,7,3,62,127,99,65,65,99,127,62,8,42,62,28,28,62,42,8,8,8,62,62, + 8,8,128,224,96,8,8,8,8,8,8,96,96,96,48,24,12,6,3,62,127,89,77,127,62,64,66, + 127,127,64,64,98,115,89,77,71,66,33,97,73,93,119,35,24,28,22,127,127,16,39, + 103,69,69,125,57,62,127,73,73,121,48,1,1,113,121,15,7,54,127,73,73,127,54, + 6,79,73,105,63,30,54,54,128,246,118,8,28,54,99,65,20,20,20,20,65,99,54,28, + 8,2,3,105,109,7,2,30,63,33,45,47,46,124,126,19,19,126,124,127,127,73,73,127, + 54,62,127,65,65,99,34,127,127,65,99,62,28,127,127,73,73,73,65,127,127,9,9, + 9,1,62,127,65,73,121,121,127,127,8,8,127,127,65,65,127,127,65,65,32,96,64, + 64,127,63,127,127,8,28,54,99,65,127,127,64,64,64,64,127,127,6,12,6,127,127, + 127,127,6,12,24,127,127,62,127,65,65,65,127,62,127,127,9,9,15,6,62,127,65, + 81,49,127,94,127,127,9,25,127,102,70,79,73,73,121,49,1,1,127,127,1,1,63,127, + 64,64,127,63,15,31,48,96,48,31,15,127,127,48,24,48,127,127,99,119,28,28,119, + 99,7,15,120,120,15,7,97,113,89,77,71,67,127,127,65,65,3,6,12,24,48,96,65, + 65,127,127,8,12,6,3,6,12,8,64,64,64,64,64,64,64,3,7,4,32,116,84,84,124,120, + 127,127,68,68,124,56,56,124,68,68,68,56,124,68,68,127,127,56,124,84,84,92, + 24,8,124,126,10,10,56,380,324,324,508,252,127,127,4,4,124,120,72,122,122, + 64,256,256,256,506,250,126,126,16,56,104,64,66,126,126,64,124,124,24,56,28, + 124,120,124,124,4,4,124,120,56,124,68,68,124,56,508,508,68,68,124,56,56,124, + 68,68,508,508,124,124,4,4,12,8,72,92,84,84,116,36,4,4,62,126,68,68,60,124, + 64,64,124,124,28,60,96,96,60,28,28,124,112,56,112,124,28,68,108,56,56,108, + 68,284,316,352,320,508,252,68,100,116,92,76,68,8,62,119,65,65,127,127,65, + 65,119,62,8,16,24,12,12,24,24,12,4, +}; + +typedef struct +{ + short id; + unsigned short category_id; + char *category; + unsigned int layermask; +} stbte__tileinfo; + +#define MAX_LAYERMASK (1 << (8*sizeof(unsigned int))) + +typedef short stbte__tiledata; + +#define STBTE__NO_TILE -1 + +enum +{ + STBTE__panel_toolbar, + STBTE__panel_colorpick, + STBTE__panel_info, + STBTE__panel_layers, + STBTE__panel_props, + STBTE__panel_categories, + STBTE__panel_tiles, + + STBTE__num_panel, +}; + +enum +{ + STBTE__side_left, + STBTE__side_right, + STBTE__side_top, + STBTE__side_bottom, +}; + +enum +{ + STBTE__tool_select, + STBTE__tool_brush, + STBTE__tool_erase, + STBTE__tool_rect, + STBTE__tool_eyedrop, + STBTE__tool_fill, + STBTE__tool_link, + + STBTE__tool_showgrid, + STBTE__tool_showlinks, + + STBTE__tool_undo, + STBTE__tool_redo, + // copy/cut/paste aren't included here because they're displayed differently + + STBTE__num_tool, +}; + +// icons are stored in the 0-31 range of ASCII in the font +static int toolchar[] = { 26,24,25,20,23,22,18, 19,17, 29,28, }; + +enum +{ + STBTE__propmode_default, + STBTE__propmode_always, + STBTE__propmode_never, +}; + +enum +{ + STBTE__paint, + + // from here down does hittesting + STBTE__tick, + STBTE__mousemove, + STBTE__mousewheel, + STBTE__leftdown, + STBTE__leftup, + STBTE__rightdown, + STBTE__rightup, +}; + +typedef struct +{ + int expanded, mode; + int delta_height; // number of rows they've requested for this + int side; + int width,height; + int x0,y0; +} stbte__panel; + +typedef struct +{ + int x0,y0,x1,y1,color; +} stbte__colorrect; + +#define STBTE__MAX_DELAYRECT 256 + +typedef struct +{ + int tool, active_event; + int active_id, hot_id, next_hot_id; + int event; + int mx,my, dx,dy; + int ms_time; + int shift, scrollkey; + int initted; + int side_extended[2]; + stbte__colorrect delayrect[STBTE__MAX_DELAYRECT]; + int delaycount; + int show_grid, show_links; + int brush_state; // used to decide which kind of erasing + int eyedrop_x, eyedrop_y, eyedrop_last_layer; + int pasting, paste_x, paste_y; + int scrolling, start_x, start_y; + int last_mouse_x, last_mouse_y; + int accum_x, accum_y; + int linking; + int dragging; + int drag_x, drag_y, drag_w, drag_h; + int drag_offx, drag_offy, drag_dest_x, drag_dest_y; + int undoing; + int has_selection, select_x0, select_y0, select_x1, select_y1; + int sx,sy; + int x0,y0,x1,y1, left_width, right_width; // configurable widths + float alert_timer; + const char *alert_msg; + float dt; + stbte__panel panel[STBTE__num_panel]; + short copybuffer[STBTE_MAX_COPY][STBTE_MAX_LAYERS]; + float copyprops[STBTE_MAX_COPY][STBTE_MAX_PROPERTIES]; +#ifdef STBTE_ALLOW_LINK + stbte__link copylinks[STBTE_MAX_COPY]; +#endif + int copy_src_x, copy_src_y; + stbte_tilemap *copy_src; + int copy_width,copy_height,has_copy,copy_has_props; +} stbte__ui_t; + +// there's only one UI system at a time, so we can globalize this +static stbte__ui_t stbte__ui = { STBTE__tool_brush, 0 }; + +#define STBTE__INACTIVE() (stbte__ui.active_id == 0) +#define STBTE__IS_ACTIVE(id) (stbte__ui.active_id == (id)) +#define STBTE__IS_HOT(id) (stbte__ui.hot_id == (id)) + +#define STBTE__BUTTON_HEIGHT (STBTE__FONT_HEIGHT + 2 * STBTE__BUTTON_INTERNAL_SPACING) +#define STBTE__BUTTON_INTERNAL_SPACING (2 + (STBTE__FONT_HEIGHT>>4)) + +typedef struct +{ + const char *name; + int locked; + int hidden; +} stbte__layer; + +enum +{ + STBTE__unlocked, + STBTE__protected, + STBTE__locked, +}; + +struct stbte_tilemap +{ + stbte__tiledata data[STBTE_MAX_TILEMAP_Y][STBTE_MAX_TILEMAP_X][STBTE_MAX_LAYERS]; + float props[STBTE_MAX_TILEMAP_Y][STBTE_MAX_TILEMAP_X][STBTE_MAX_PROPERTIES]; + #ifdef STBTE_ALLOW_LINK + stbte__link link[STBTE_MAX_TILEMAP_Y][STBTE_MAX_TILEMAP_X]; + int linkcount[STBTE_MAX_TILEMAP_Y][STBTE_MAX_TILEMAP_X]; + #endif + int max_x, max_y, num_layers; + int spacing_x, spacing_y; + int palette_spacing_x, palette_spacing_y; + int scroll_x,scroll_y; + int cur_category, cur_tile, cur_layer; + char *categories[STBTE_MAX_CATEGORIES]; + int num_categories, category_scroll; + stbte__tileinfo *tiles; + int num_tiles, max_tiles, digits; + unsigned char undo_available_valid; + unsigned char undo_available; + unsigned char redo_available; + unsigned char padding; + int cur_palette_count; + int palette_scroll; + int tileinfo_dirty; + stbte__layer layerinfo[STBTE_MAX_LAYERS]; + int has_layer_names; + int layername_width; + int layer_scroll; + int propmode; + int solo_layer; + int undo_pos, undo_len, redo_len; + short background_tile; + unsigned char id_in_use[32768>>3]; + short *undo_buffer; +}; + +static char *default_category = "[unassigned]"; + +static void stbte__init_gui(void) +{ + int i,n; + stbte__ui.initted = 1; + // init UI state + stbte__ui.show_links = 1; + for (i=0; i < STBTE__num_panel; ++i) { + stbte__ui.panel[i].expanded = 1; // visible if not autohidden + stbte__ui.panel[i].delta_height = 0; + stbte__ui.panel[i].side = STBTE__side_left; + } + stbte__ui.panel[STBTE__panel_toolbar ].side = STBTE__side_top; + stbte__ui.panel[STBTE__panel_colorpick].side = STBTE__side_right; + + if (stbte__ui.left_width == 0) + stbte__ui.left_width = 80; + if (stbte__ui.right_width == 0) + stbte__ui.right_width = 80; + + // init font + n=95+16; + for (i=0; i < 95+16; ++i) { + stbte__font_offset[i] = n; + n += stbte__fontdata[i]; + } +} + +stbte_tilemap *stbte_create_map(int map_x, int map_y, int map_layers, int spacing_x, int spacing_y, int max_tiles) +{ + int i; + stbte_tilemap *tm; + STBTE_ASSERT(map_layers >= 0 && map_layers <= STBTE_MAX_LAYERS); + STBTE_ASSERT(map_x >= 0 && map_x <= STBTE_MAX_TILEMAP_X); + STBTE_ASSERT(map_y >= 0 && map_y <= STBTE_MAX_TILEMAP_Y); + if (map_x < 0 || map_y < 0 || map_layers < 0 || + map_x > STBTE_MAX_TILEMAP_X || map_y > STBTE_MAX_TILEMAP_Y || map_layers > STBTE_MAX_LAYERS) + return NULL; + + if (!stbte__ui.initted) + stbte__init_gui(); + + tm = (stbte_tilemap *) malloc(sizeof(*tm) + sizeof(*tm->tiles) * max_tiles + STBTE_UNDO_BUFFER_BYTES); + if (tm == NULL) + return NULL; + + tm->tiles = (stbte__tileinfo *) (tm+1); + tm->undo_buffer = (short *) (tm->tiles + max_tiles); + tm->num_layers = map_layers; + tm->max_x = map_x; + tm->max_y = map_y; + tm->spacing_x = spacing_x; + tm->spacing_y = spacing_y; + tm->scroll_x = 0; + tm->scroll_y = 0; + tm->palette_scroll = 0; + tm->palette_spacing_x = spacing_x+1; + tm->palette_spacing_y = spacing_y+1; + tm->cur_category = -1; + tm->cur_tile = 0; + tm->solo_layer = -1; + tm->undo_len = 0; + tm->redo_len = 0; + tm->undo_pos = 0; + tm->category_scroll = 0; + tm->layer_scroll = 0; + tm->propmode = 0; + tm->has_layer_names = 0; + tm->layername_width = 0; + tm->undo_available_valid = 0; + + for (i=0; i < tm->num_layers; ++i) { + tm->layerinfo[i].hidden = 0; + tm->layerinfo[i].locked = STBTE__unlocked; + tm->layerinfo[i].name = 0; + } + + tm->background_tile = STBTE__NO_TILE; + stbte_clear_map(tm); + + tm->max_tiles = max_tiles; + tm->num_tiles = 0; + for (i=0; i < 32768/8; ++i) + tm->id_in_use[i] = 0; + tm->tileinfo_dirty = 1; + return tm; +} + +void stbte_set_background_tile(stbte_tilemap *tm, short id) +{ + int i; + STBTE_ASSERT(id >= -1 && id < 32768); + if (id >= 32768 || id < -1) + return; + for (i=0; i < STBTE_MAX_TILEMAP_X * STBTE_MAX_TILEMAP_Y; ++i) + if (tm->data[0][i][0] == -1) + tm->data[0][i][0] = id; + tm->background_tile = id; +} + +void stbte_set_spacing(stbte_tilemap *tm, int spacing_x, int spacing_y, int palette_spacing_x, int palette_spacing_y) +{ + tm->spacing_x = spacing_x; + tm->spacing_y = spacing_y; + tm->palette_spacing_x = palette_spacing_x; + tm->palette_spacing_y = palette_spacing_y; +} + +void stbte_set_sidewidths(int left, int right) +{ + stbte__ui.left_width = left; + stbte__ui.right_width = right; +} + +void stbte_set_display(int x0, int y0, int x1, int y1) +{ + stbte__ui.x0 = x0; + stbte__ui.y0 = y0; + stbte__ui.x1 = x1; + stbte__ui.y1 = y1; +} + +void stbte_define_tile(stbte_tilemap *tm, unsigned short id, unsigned int layermask, const char * category_c) +{ + char *category = (char *) category_c; + STBTE_ASSERT(id < 32768); + STBTE_ASSERT(tm->num_tiles < tm->max_tiles); + STBTE_ASSERT((tm->id_in_use[id>>3]&(1<<(id&7))) == 0); + if (id >= 32768 || tm->num_tiles >= tm->max_tiles || (tm->id_in_use[id>>3]&(1<<(id&7)))) + return; + + if (category == NULL) + category = (char*) default_category; + tm->id_in_use[id>>3] |= 1 << (id&7); + tm->tiles[tm->num_tiles].category = category; + tm->tiles[tm->num_tiles].id = id; + tm->tiles[tm->num_tiles].layermask = layermask; + ++tm->num_tiles; + tm->tileinfo_dirty = 1; +} + +static int stbte__text_width(const char *str); + +void stbte_set_layername(stbte_tilemap *tm, int layer, const char *layername) +{ + STBTE_ASSERT(layer >= 0 && layer < tm->num_layers); + if (layer >= 0 && layer < tm->num_layers) { + int width; + tm->layerinfo[layer].name = layername; + tm->has_layer_names = 1; + width = stbte__text_width(layername); + tm->layername_width = (width > tm->layername_width ? width : tm->layername_width); + } +} + +void stbte_get_dimensions(stbte_tilemap *tm, int *max_x, int *max_y) +{ + *max_x = tm->max_x; + *max_y = tm->max_y; +} + +short* stbte_get_tile(stbte_tilemap *tm, int x, int y) +{ + STBTE_ASSERT(x >= 0 && x < tm->max_x && y >= 0 && y < tm->max_y); + if (x < 0 || x >= STBTE_MAX_TILEMAP_X || y < 0 || y >= STBTE_MAX_TILEMAP_Y) + return NULL; + return tm->data[y][x]; +} + +float *stbte_get_properties(stbte_tilemap *tm, int x, int y) +{ + STBTE_ASSERT(x >= 0 && x < tm->max_x && y >= 0 && y < tm->max_y); + if (x < 0 || x >= STBTE_MAX_TILEMAP_X || y < 0 || y >= STBTE_MAX_TILEMAP_Y) + return NULL; + return tm->props[y][x]; +} + +void stbte_get_link(stbte_tilemap *tm, int x, int y, int *destx, int *desty) +{ + int gx=-1,gy=-1; + STBTE_ASSERT(x >= 0 && x < tm->max_x && y >= 0 && y < tm->max_y); +#ifdef STBTE_ALLOW_LINK + if (x >= 0 && x < STBTE_MAX_TILEMAP_X && y >= 0 && y < STBTE_MAX_TILEMAP_Y) { + gx = tm->link[y][x].x; + gy = tm->link[y][x].y; + if (gx >= 0) + if (!STBTE_ALLOW_LINK(tm->data[y][x], tm->props[y][x], tm->data[gy][gx], tm->props[gy][gx])) + gx = gy = -1; + } +#endif + *destx = gx; + *desty = gy; +} + +void stbte_set_property(stbte_tilemap *tm, int x, int y, int n, float val) +{ + tm->props[y][x][n] = val; +} + +static void stbte__set_link(stbte_tilemap *tm, int src_x, int src_y, int dest_x, int dest_y, int undo_mode); + +enum +{ + STBTE__undo_none, + STBTE__undo_record, + STBTE__undo_block, +}; + +void stbte_set_link(stbte_tilemap *tm, int x, int y, int destx, int desty) +{ +#ifdef STBTE_ALLOW_LINK + stbte__set_link(tm, x, y, destx, desty, STBTE__undo_none); +#else + STBTE_ASSERT(0); +#endif +} + + +// returns an array of map_layers shorts. each short is either +// one of the tile_id values from define_tile, or STBTE_EMPTY + +void stbte_set_dimensions(stbte_tilemap *tm, int map_x, int map_y) +{ + STBTE_ASSERT(map_x >= 0 && map_x <= STBTE_MAX_TILEMAP_X); + STBTE_ASSERT(map_y >= 0 && map_y <= STBTE_MAX_TILEMAP_Y); + if (map_x < 0 || map_y < 0 || map_x > STBTE_MAX_TILEMAP_X || map_y > STBTE_MAX_TILEMAP_Y) + return; + tm->max_x = map_x; + tm->max_y = map_y; +} + +void stbte_clear_map(stbte_tilemap *tm) +{ + int i,j; + for (i=0; i < STBTE_MAX_TILEMAP_X * STBTE_MAX_TILEMAP_Y; ++i) { + tm->data[0][i][0] = tm->background_tile; + for (j=1; j < tm->num_layers; ++j) + tm->data[0][i][j] = STBTE__NO_TILE; + for (j=0; j < STBTE_MAX_PROPERTIES; ++j) + tm->props[0][i][j] = 0; + #ifdef STBTE_ALLOW_LINK + tm->link[0][i].x = -1; + tm->link[0][i].y = -1; + tm->linkcount[0][i] = 0; + #endif + } +} + +void stbte_set_tile(stbte_tilemap *tm, int x, int y, int layer, signed short tile) +{ + STBTE_ASSERT(x >= 0 && x < tm->max_x && y >= 0 && y < tm->max_y); + STBTE_ASSERT(layer >= 0 && layer < tm->num_layers); + STBTE_ASSERT(tile >= -1 && tile < 32768); + if (x < 0 || x >= STBTE_MAX_TILEMAP_X || y < 0 || y >= STBTE_MAX_TILEMAP_Y) + return; + if (layer < 0 || layer >= tm->num_layers || tile < -1) + return; + tm->data[y][x][layer] = tile; +} + +static void stbte__choose_category(stbte_tilemap *tm, int category) +{ + int i,n=0; + tm->cur_category = category; + for (i=0; i < tm->num_tiles; ++i) + if (tm->tiles[i].category_id == category || category == -1) + ++n; + tm->cur_palette_count = n; + tm->palette_scroll = 0; +} + +static int stbte__strequal(char *p, char *q) +{ + while (*p) + if (*p++ != *q++) return 0; + return *q == 0; +} + +static void stbte__compute_tileinfo(stbte_tilemap *tm) +{ + int i,j,n=0; + + tm->num_categories=0; + + for (i=0; i < tm->num_tiles; ++i) { + stbte__tileinfo *t = &tm->tiles[i]; + // find category + for (j=0; j < tm->num_categories; ++j) + if (stbte__strequal(t->category, tm->categories[j])) + goto found; + tm->categories[j] = t->category; + ++tm->num_categories; + found: + t->category_id = (unsigned short) j; + } + + // currently number of categories can never decrease because you + // can't remove tile definitions, but let's get it right anyway + if (tm->cur_category > tm->num_categories) { + tm->cur_category = -1; + } + + stbte__choose_category(tm, tm->cur_category); + + tm->tileinfo_dirty = 0; +} + +static void stbte__prepare_tileinfo(stbte_tilemap *tm) +{ + if (tm->tileinfo_dirty) + stbte__compute_tileinfo(tm); +} + + +/////////////////////// undo system //////////////////////// + +// the undo system works by storing "commands" into a buffer, and +// then playing back those commands. undo and redo have to store +// the commands in different order. +// +// the commands are: +// +// 1) end_of_undo_record +// -1:short +// +// 2) end_of_redo_record +// -2:short +// +// 3) tile update +// tile_id:short (-1..32767) +// x_coord:short +// y_coord:short +// layer:short (0..31) +// +// 4) property update (also used for links) +// value_hi:short +// value_lo:short +// y_coord:short +// x_coord:short +// property:short (256+prop#) +// +// Since we use a circular buffer, we might overwrite the undo storage. +// To detect this, before playing back commands we scan back and see +// if we see an end_of_undo_record before hitting the relevant boundary, +// it's wholly contained. +// +// When we read back through, we see them in reverse order, so +// we'll see the layer number or property number first +// +// To be clearer about the circular buffer, there are two cases: +// 1. a single record is larger than the whole buffer. +// this is caught because the end_of_undo_record will +// get overwritten. +// 2. multiple records written are larger than the whole +// buffer, so some of them have been overwritten by +// the later ones. this is handled by explicitly tracking +// the undo length; we never try to parse the data that +// got overwritten + +// given two points, compute the length between them +#define stbte__wrap(pos) ((pos) & (STBTE__UNDO_BUFFER_COUNT-1)) + +#define STBTE__undo_record -2 +#define STBTE__redo_record -3 +#define STBTE__undo_junk -4 // this is written underneath the undo pointer, never used + +static void stbte__write_undo(stbte_tilemap *tm, short value) +{ + int pos = tm->undo_pos; + tm->undo_buffer[pos] = value; + tm->undo_pos = stbte__wrap(pos+1); + tm->undo_len += (tm->undo_len < STBTE__UNDO_BUFFER_COUNT-2); + tm->redo_len -= (tm->redo_len > 0); + tm->undo_available_valid = 0; +} + +static void stbte__write_redo(stbte_tilemap *tm, short value) +{ + int pos = tm->undo_pos; + tm->undo_buffer[pos] = value; + tm->undo_pos = stbte__wrap(pos-1); + tm->redo_len += (tm->redo_len < STBTE__UNDO_BUFFER_COUNT-2); + tm->undo_len -= (tm->undo_len > 0); + tm->undo_available_valid = 0; +} + +static void stbte__begin_undo(stbte_tilemap *tm) +{ + tm->redo_len = 0; + stbte__write_undo(tm, STBTE__undo_record); + stbte__ui.undoing = 1; + stbte__ui.alert_msg = 0; // clear alert if they start doing something +} + +static void stbte__end_undo(stbte_tilemap *tm) +{ + if (stbte__ui.undoing) { + // check if anything got written + int pos = stbte__wrap(tm->undo_pos-1); + if (tm->undo_buffer[pos] == STBTE__undo_record) { + // empty undo record, move back + tm->undo_pos = pos; + STBTE_ASSERT(tm->undo_len > 0); + tm->undo_len -= 1; + } + tm->undo_buffer[tm->undo_pos] = STBTE__undo_junk; + // otherwise do nothing + + stbte__ui.undoing = 0; + } +} + +static void stbte__undo_record(stbte_tilemap *tm, int x, int y, int i, int v) +{ + STBTE_ASSERT(stbte__ui.undoing); + if (stbte__ui.undoing) { + stbte__write_undo(tm, v); + stbte__write_undo(tm, x); + stbte__write_undo(tm, y); + stbte__write_undo(tm, i); + } +} + +static void stbte__redo_record(stbte_tilemap *tm, int x, int y, int i, int v) +{ + stbte__write_redo(tm, v); + stbte__write_redo(tm, x); + stbte__write_redo(tm, y); + stbte__write_redo(tm, i); +} + +static float stbte__extract_float(short s0, short s1) +{ + union { float f; short s[2]; } converter; + converter.s[0] = s0; + converter.s[1] = s1; + return converter.f; +} + +static short stbte__extract_short(float f, int slot) +{ + union { float f; short s[2]; } converter; + converter.f = f; + return converter.s[slot]; +} + +static void stbte__undo_record_prop(stbte_tilemap *tm, int x, int y, int i, short s0, short s1) +{ + STBTE_ASSERT(stbte__ui.undoing); + if (stbte__ui.undoing) { + stbte__write_undo(tm, s1); + stbte__write_undo(tm, s0); + stbte__write_undo(tm, x); + stbte__write_undo(tm, y); + stbte__write_undo(tm, 256+i); + } +} + +static void stbte__undo_record_prop_float(stbte_tilemap *tm, int x, int y, int i, float f) +{ + stbte__undo_record_prop(tm, x,y,i, stbte__extract_short(f,0), stbte__extract_short(f,1)); +} + +static void stbte__redo_record_prop(stbte_tilemap *tm, int x, int y, int i, short s0, short s1) +{ + stbte__write_redo(tm, s1); + stbte__write_redo(tm, s0); + stbte__write_redo(tm, x); + stbte__write_redo(tm, y); + stbte__write_redo(tm, 256+i); +} + + +static int stbte__undo_find_end(stbte_tilemap *tm) +{ + // first scan through for the end record + int i, pos = stbte__wrap(tm->undo_pos-1); + for (i=0; i < tm->undo_len;) { + STBTE_ASSERT(tm->undo_buffer[pos] != STBTE__undo_junk); + if (tm->undo_buffer[pos] == STBTE__undo_record) + break; + if (tm->undo_buffer[pos] >= 255) + pos = stbte__wrap(pos-5), i += 5; + else + pos = stbte__wrap(pos-4), i += 4; + } + if (i >= tm->undo_len) + return -1; + return pos; +} + +static void stbte__undo(stbte_tilemap *tm) +{ + int i, pos, endpos; + endpos = stbte__undo_find_end(tm); + if (endpos < 0) + return; + + // we found a complete undo record + pos = stbte__wrap(tm->undo_pos-1); + + // start a redo record + stbte__write_redo(tm, STBTE__redo_record); + + // so now go back through undo and apply in reverse + // order, and copy it to redo + for (i=0; endpos != pos; i += 4) { + int x,y,n,v; + // get the undo entry + n = tm->undo_buffer[pos]; + y = tm->undo_buffer[stbte__wrap(pos-1)]; + x = tm->undo_buffer[stbte__wrap(pos-2)]; + v = tm->undo_buffer[stbte__wrap(pos-3)]; + if (n >= 255) { + short s0=0,s1=0; + int v2 = tm->undo_buffer[stbte__wrap(pos-4)]; + pos = stbte__wrap(pos-5); + if (n > 255) { + float vf = stbte__extract_float(v, v2); + s0 = stbte__extract_short(tm->props[y][x][n-256], 0); + s1 = stbte__extract_short(tm->props[y][x][n-256], 1); + tm->props[y][x][n-256] = vf; + } else { +#ifdef STBTE_ALLOW_LINK + s0 = tm->link[y][x].x; + s1 = tm->link[y][x].y; + stbte__set_link(tm, x,y, v, v2, STBTE__undo_none); +#endif + } + // write the redo entry + stbte__redo_record_prop(tm, x, y, n-256, s0,s1); + // apply the undo entry + } else { + pos = stbte__wrap(pos-4); + // write the redo entry + stbte__redo_record(tm, x, y, n, tm->data[y][x][n]); + // apply the undo entry + tm->data[y][x][n] = (short) v; + } + } + // overwrite undo record with junk + tm->undo_buffer[tm->undo_pos] = STBTE__undo_junk; +} + +static int stbte__redo_find_end(stbte_tilemap *tm) +{ + // first scan through for the end record + int i, pos = stbte__wrap(tm->undo_pos+1); + for (i=0; i < tm->redo_len;) { + STBTE_ASSERT(tm->undo_buffer[pos] != STBTE__undo_junk); + if (tm->undo_buffer[pos] == STBTE__redo_record) + break; + if (tm->undo_buffer[pos] >= 255) + pos = stbte__wrap(pos+5), i += 5; + else + pos = stbte__wrap(pos+4), i += 4; + } + if (i >= tm->redo_len) + return -1; // this should only ever happen if redo buffer is empty + return pos; +} + +static void stbte__redo(stbte_tilemap *tm) +{ + // first scan through for the end record + int i, pos, endpos; + endpos = stbte__redo_find_end(tm); + if (endpos < 0) + return; + + // we found a complete redo record + pos = stbte__wrap(tm->undo_pos+1); + + // start an undo record + stbte__write_undo(tm, STBTE__undo_record); + + for (i=0; pos != endpos; i += 4) { + int x,y,n,v; + n = tm->undo_buffer[pos]; + y = tm->undo_buffer[stbte__wrap(pos+1)]; + x = tm->undo_buffer[stbte__wrap(pos+2)]; + v = tm->undo_buffer[stbte__wrap(pos+3)]; + if (n >= 255) { + int v2 = tm->undo_buffer[stbte__wrap(pos+4)]; + short s0=0,s1=0; + pos = stbte__wrap(pos+5); + if (n > 255) { + float vf = stbte__extract_float(v, v2); + s0 = stbte__extract_short(tm->props[y][x][n-256],0); + s1 = stbte__extract_short(tm->props[y][x][n-256],1); + tm->props[y][x][n-256] = vf; + } else { +#ifdef STBTE_ALLOW_LINK + s0 = tm->link[y][x].x; + s1 = tm->link[y][x].y; + stbte__set_link(tm, x,y,v,v2, STBTE__undo_none); +#endif + } + // don't use stbte__undo_record_prop because it's guarded + stbte__write_undo(tm, s1); + stbte__write_undo(tm, s0); + stbte__write_undo(tm, x); + stbte__write_undo(tm, y); + stbte__write_undo(tm, n); + } else { + pos = stbte__wrap(pos+4); + // don't use stbte__undo_record because it's guarded + stbte__write_undo(tm, tm->data[y][x][n]); + stbte__write_undo(tm, x); + stbte__write_undo(tm, y); + stbte__write_undo(tm, n); + tm->data[y][x][n] = (short) v; + } + } + tm->undo_buffer[tm->undo_pos] = STBTE__undo_junk; +} + +// because detecting that undo is available +static void stbte__recompute_undo_available(stbte_tilemap *tm) +{ + tm->undo_available = (stbte__undo_find_end(tm) >= 0); + tm->redo_available = (stbte__redo_find_end(tm) >= 0); +} + +static int stbte__undo_available(stbte_tilemap *tm) +{ + if (!tm->undo_available_valid) + stbte__recompute_undo_available(tm); + return tm->undo_available; +} + +static int stbte__redo_available(stbte_tilemap *tm) +{ + if (!tm->undo_available_valid) + stbte__recompute_undo_available(tm); + return tm->redo_available; +} + +/////////////////////////////////////////////////////////////////////////////////////////////////// + +#ifdef STBTE_ALLOW_LINK +static void stbte__set_link(stbte_tilemap *tm, int src_x, int src_y, int dest_x, int dest_y, int undo_mode) +{ + stbte__link *a; + STBTE_ASSERT(src_x >= 0 && src_x < STBTE_MAX_TILEMAP_X && src_y >= 0 && src_y < STBTE_MAX_TILEMAP_Y); + a = &tm->link[src_y][src_x]; + // check if it's a do nothing + if (a->x == dest_x && a->y == dest_y) + return; + if (undo_mode != STBTE__undo_none ) { + if (undo_mode == STBTE__undo_block) stbte__begin_undo(tm); + stbte__undo_record_prop(tm, src_x, src_y, -1, a->x, a->y); + if (undo_mode == STBTE__undo_block) stbte__end_undo(tm); + } + // check if there's an existing link + if (a->x >= 0) { + // decrement existing link refcount + STBTE_ASSERT(tm->linkcount[a->y][a->x] > 0); + --tm->linkcount[a->y][a->x]; + } + // increment new dest + if (dest_x >= 0) { + ++tm->linkcount[dest_y][dest_x]; + } + a->x = dest_x; + a->y = dest_y; +} +#endif + + +static void stbte__draw_rect(int x0, int y0, int x1, int y1, unsigned int color) +{ + STBTE_DRAW_RECT(x0,y0,x1,y1, color); +} + +static void stbte__draw_line(int x0, int y0, int x1, int y1, unsigned int color) +{ + int temp; + if (x1 < x0) temp=x0,x0=x1,x1=temp; + if (y1 < y0) temp=y0,y0=y1,y1=temp; + stbte__draw_rect(x0,y0,x1+1,y1+1,color); +} + +static void stbte__draw_link(int x0, int y0, int x1, int y1, unsigned int color) +{ + stbte__draw_line(x0,y0,x0,y1, color); + stbte__draw_line(x0,y1,x1,y1, color); +} + +static void stbte__draw_frame(int x0, int y0, int x1, int y1, unsigned int color) +{ + stbte__draw_rect(x0,y0,x1-1,y0+1,color); + stbte__draw_rect(x1-1,y0,x1,y1-1,color); + stbte__draw_rect(x0+1,y1-1,x1,y1,color); + stbte__draw_rect(x0,y0+1,x0+1,y1,color); +} + +static void stbte__draw_halfframe(int x0, int y0, int x1, int y1, unsigned int color) +{ + stbte__draw_rect(x0,y0,x1,y0+1,color); + stbte__draw_rect(x0,y0+1,x0+1,y1,color); +} + +static int stbte__get_char_width(int ch) +{ + return stbte__fontdata[ch-16]; +} + +static short *stbte__get_char_bitmap(int ch) +{ + return stbte__fontdata + stbte__font_offset[ch-16]; +} + +static void stbte__draw_bitmask_as_columns(int x, int y, short bitmask, int color) +{ + int start_i = -1, i=0; + while (bitmask) { + if (bitmask & (1<= 0) { + stbte__draw_rect(x, y+start_i, x+1, y+i, color); + start_i = -1; + bitmask &= ~((1< x_end) + break; + stbte__draw_bitmap(x, y, cw, stbte__get_char_bitmap(c), color); + if (digitspace && c == ' ') + cw = stbte__get_char_width('0'); + x += cw+1; + } +} + +static void stbte__draw_text(int x, int y, const char *str, int w, int color) +{ + stbte__draw_text_core(x,y,str,w,color,0); +} + +static int stbte__text_width(const char *str) +{ + int x = 0; + while (*str) { + int c = *str++; + int cw = stbte__get_char_width(c); + x += cw+1; + } + return x; +} + +static void stbte__draw_frame_delayed(int x0, int y0, int x1, int y1, int color) +{ + if (stbte__ui.delaycount < STBTE__MAX_DELAYRECT) { + stbte__colorrect r = { x0,y0,x1,y1,color }; + stbte__ui.delayrect[stbte__ui.delaycount++] = r; + } +} + +static void stbte__flush_delay(void) +{ + stbte__colorrect *r; + int i; + r = stbte__ui.delayrect; + for (i=0; i < stbte__ui.delaycount; ++i,++r) + stbte__draw_frame(r->x0,r->y0,r->x1,r->y1,r->color); + stbte__ui.delaycount = 0; +} + +static void stbte__activate(int id) +{ + stbte__ui.active_id = id; + stbte__ui.active_event = stbte__ui.event; + stbte__ui.accum_x = 0; + stbte__ui.accum_y = 0; +} + +static int stbte__hittest(int x0, int y0, int x1, int y1, int id) +{ + int over = stbte__ui.mx >= x0 && stbte__ui.my >= y0 + && stbte__ui.mx < x1 && stbte__ui.my < y1; + + if (over && stbte__ui.event >= STBTE__tick) + stbte__ui.next_hot_id = id; + + return over; +} + +static int stbte__button_core(int id) +{ + switch (stbte__ui.event) { + case STBTE__leftdown: + if (stbte__ui.hot_id == id && STBTE__INACTIVE()) + stbte__activate(id); + break; + case STBTE__leftup: + if (stbte__ui.active_id == id && STBTE__IS_HOT(id)) { + stbte__activate(0); + return 1; + } + break; + case STBTE__rightdown: + if (stbte__ui.hot_id == id && STBTE__INACTIVE()) + stbte__activate(id); + break; + case STBTE__rightup: + if (stbte__ui.active_id == id && STBTE__IS_HOT(id)) { + stbte__activate(0); + return -1; + } + break; + } + return 0; +} + +static void stbte__draw_box(int x0, int y0, int x1, int y1, int colormode, int colorindex) +{ + stbte__draw_rect (x0,y0,x1,y1, stbte__color_table[colormode][STBTE__base ][colorindex]); + stbte__draw_frame(x0,y0,x1,y1, stbte__color_table[colormode][STBTE__outline][colorindex]); +} + +static void stbte__draw_textbox(int x0, int y0, int x1, int y1, char *text, int xoff, int yoff, int colormode, int colorindex) +{ + stbte__draw_box(x0,y0,x1,y1,colormode,colorindex); + stbte__draw_text(x0+xoff,y0+yoff, text, x1-x0-xoff-1, stbte__color_table[colormode][STBTE__text][colorindex]); +} + +static int stbte__button(int colormode, char *label, int x, int y, int textoff, int width, int id, int toggled, int disabled) +{ + int x0=x,y0=y, x1=x+width,y1=y+STBTE__BUTTON_HEIGHT; + int s = STBTE__BUTTON_INTERNAL_SPACING; + + int over = !disabled && stbte__hittest(x0,y0,x1,y1,id); + + if (stbte__ui.event == STBTE__paint) + stbte__draw_textbox(x0,y0,x1,y1, label,s+textoff,s, colormode, STBTE__INDEX_FOR_ID(id,disabled,toggled)); + if (disabled) + return 0; + return (stbte__button_core(id) == 1); +} + +static int stbte__button_icon(int colormode, char ch, int x, int y, int width, int id, int toggled, int disabled) +{ + int x0=x,y0=y, x1=x+width,y1=y+STBTE__BUTTON_HEIGHT; + int s = STBTE__BUTTON_INTERNAL_SPACING; + + int over = stbte__hittest(x0,y0,x1,y1,id); + + if (stbte__ui.event == STBTE__paint) { + char label[2] = { ch, 0 }; + int pad = (9 - stbte__get_char_width(ch))/2; + stbte__draw_textbox(x0,y0,x1,y1, label,s+pad,s, colormode, STBTE__INDEX_FOR_ID(id,disabled,toggled)); + } + if (disabled) + return 0; + return (stbte__button_core(id) == 1); +} + +static int stbte__minibutton(int colormode, int x, int y, int ch, int id) +{ + int x0 = x, y0 = y, x1 = x+8, y1 = y+7; + int over = stbte__hittest(x0,y0,x1,y1,id); + if (stbte__ui.event == STBTE__paint) { + char str[2] = { ch,0 }; + stbte__draw_textbox(x0,y0,x1,y1, str,1,0,colormode, STBTE__INDEX_FOR_ID(id,0,0)); + } + return stbte__button_core(id); +} + +static int stbte__layerbutton(int x, int y, int ch, int id, int toggled, int disabled, int colormode) +{ + int x0 = x, y0 = y, x1 = x+10, y1 = y+11; + int over = !disabled && stbte__hittest(x0,y0,x1,y1,id); + if (stbte__ui.event == STBTE__paint) { + char str[2] = { ch,0 }; + int off = (9-stbte__get_char_width(ch))/2; + stbte__draw_textbox(x0,y0,x1,y1, str, off+1,2, colormode, STBTE__INDEX_FOR_ID(id,disabled,toggled)); + } + if (disabled) + return 0; + return stbte__button_core(id); +} + +static int stbte__microbutton(int x, int y, int size, int id, int colormode) +{ + int x0 = x, y0 = y, x1 = x+size, y1 = y+size; + int over = stbte__hittest(x0,y0,x1,y1,id); + if (stbte__ui.event == STBTE__paint) { + stbte__draw_box(x0,y0,x1,y1, colormode, STBTE__INDEX_FOR_ID(id,0,0)); + } + return stbte__button_core(id); +} + +static int stbte__microbutton_dragger(int x, int y, int size, int id, int *pos) +{ + int x0 = x, y0 = y, x1 = x+size, y1 = y+size; + int over = stbte__hittest(x0,y0,x1,y1,id); + switch (stbte__ui.event) { + case STBTE__paint: + stbte__draw_box(x0,y0,x1,y1, STBTE__cexpander, STBTE__INDEX_FOR_ID(id,0,0)); + break; + case STBTE__leftdown: + if (STBTE__IS_HOT(id) && STBTE__INACTIVE()) { + stbte__activate(id); + stbte__ui.sx = stbte__ui.mx - *pos; + } + break; + case STBTE__mousemove: + if (STBTE__IS_ACTIVE(id) && stbte__ui.active_event == STBTE__leftdown) { + *pos = stbte__ui.mx - stbte__ui.sx; + } + break; + case STBTE__leftup: + if (STBTE__IS_ACTIVE(id)) + stbte__activate(0); + break; + default: + return stbte__button_core(id); + } + return 0; +} + +static int stbte__category_button(char *label, int x, int y, int width, int id, int toggled) +{ + int x0=x,y0=y, x1=x+width,y1=y+STBTE__BUTTON_HEIGHT; + int s = STBTE__BUTTON_INTERNAL_SPACING; + + int over = stbte__hittest(x0,y0,x1,y1,id); + + if (stbte__ui.event == STBTE__paint) + stbte__draw_textbox(x0,y0,x1,y1, label, s,s, STBTE__ccategory_button, STBTE__INDEX_FOR_ID(id,0,toggled)); + + return (stbte__button_core(id) == 1); +} + +enum +{ + STBTE__none, + STBTE__begin, + STBTE__end, + STBTE__change, +}; + +// returns -1 if value changes, 1 at end of drag +static int stbte__slider(int x0, int w, int y, int range, int *value, int id) +{ + int x1 = x0+w; + int pos = *value * w / (range+1); + int over = stbte__hittest(x0,y-2,x1,y+3,id); + int event_mouse_move = STBTE__change; + switch (stbte__ui.event) { + case STBTE__paint: + stbte__draw_rect(x0,y,x1,y+1, 0x808080); + stbte__draw_rect(x0+pos-1,y-1,x0+pos+2,y+2, 0xffffff); + break; + case STBTE__leftdown: + if (STBTE__IS_HOT(id) && STBTE__INACTIVE()) { + stbte__activate(id); + event_mouse_move = STBTE__begin; + } + // fall through + case STBTE__mousemove: + if (STBTE__IS_ACTIVE(id)) { + int v = (stbte__ui.mx-x0)*(range+1)/w; + if (v < 0) v = 0; else if (v > range) v = range; + *value = v; + return event_mouse_move; + } + break; + case STBTE__leftup: + if (STBTE__IS_ACTIVE(id)) { + stbte__activate(0); + return STBTE__end; + } + break; + } + return STBTE__none; +} + +static int stbte__float_control(int x0, int y0, int w, float minv, float maxv, float scale, char *fmt, float *value, int colormode, int id) +{ + int x1 = x0+w; + int y1 = y0+11; + int over = stbte__hittest(x0,y0,x1,y1,id); + switch (stbte__ui.event) { + case STBTE__paint: { + char text[32]; + sprintf(text, fmt ? fmt : "%6.2f", *value); + stbte__draw_textbox(x0,y0,x1,y1, text, 1,2, colormode, STBTE__INDEX_FOR_ID(id,0,0)); + break; + } + case STBTE__leftdown: + case STBTE__rightdown: + if (STBTE__IS_HOT(id) && STBTE__INACTIVE()) + stbte__activate(id); + return STBTE__begin; + break; + case STBTE__leftup: + case STBTE__rightup: + if (STBTE__IS_ACTIVE(id)) { + stbte__activate(0); + return STBTE__end; + } + break; + case STBTE__mousemove: + if (STBTE__IS_ACTIVE(id)) { + float v = *value, delta; + int ax = stbte__ui.accum_x/STBTE_FLOAT_CONTROL_GRANULARITY; + int ay = stbte__ui.accum_y/STBTE_FLOAT_CONTROL_GRANULARITY; + stbte__ui.accum_x -= ax*STBTE_FLOAT_CONTROL_GRANULARITY; + stbte__ui.accum_y -= ay*STBTE_FLOAT_CONTROL_GRANULARITY; + if (stbte__ui.shift) { + if (stbte__ui.active_event == STBTE__leftdown) + delta = ax * 16.0f + ay; + else + delta = ax / 16.0f + ay / 256.0f; + } else { + if (stbte__ui.active_event == STBTE__leftdown) + delta = ax*10.0f + ay; + else + delta = ax * 0.1f + ay * 0.01f; + } + v += delta * scale; + if (v < minv) v = minv; + if (v > maxv) v = maxv; + *value = v; + return STBTE__change; + } + break; + } + return STBTE__none; +} + +static void stbte__scrollbar(int x, int y0, int y1, int *val, int v0, int v1, int num_vis, int id) +{ + int over; + int thumbpos; + if (v1 - v0 <= num_vis) + return; + + // generate thumbpos from numvis + thumbpos = y0+2 + (y1-y0-4) * *val / (v1 - v0 - num_vis); + if (thumbpos < y0) thumbpos = y0; + if (thumbpos >= y1) thumbpos = y1; + over = stbte__hittest(x-1,y0,x+2,y1,id); + switch (stbte__ui.event) { + case STBTE__paint: + stbte__draw_rect(x,y0,x+1,y1, stbte__color_table[STBTE__cscrollbar][STBTE__text][STBTE__idle]); + stbte__draw_box(x-1,thumbpos-3,x+2,thumbpos+4, STBTE__cscrollbar, STBTE__INDEX_FOR_ID(id,0,0)); + break; + case STBTE__leftdown: + if (STBTE__IS_HOT(id) && STBTE__INACTIVE()) { + // check if it's over the thumb + stbte__activate(id); + *val = ((stbte__ui.my-y0) * (v1 - v0 - num_vis) + (y1-y0)/2)/ (y1-y0); + } + break; + case STBTE__mousemove: + if (STBTE__IS_ACTIVE(id) && stbte__ui.mx >= x-15 && stbte__ui.mx <= x+15) + *val = ((stbte__ui.my-y0) * (v1 - v0 - num_vis) + (y1-y0)/2)/ (y1-y0); + break; + case STBTE__leftup: + if (STBTE__IS_ACTIVE(id)) + stbte__activate(0); + break; + + } + + if (*val >= v1-num_vis) + *val = v1-num_vis; + if (*val <= v0) + *val = v0; +} + + +static void stbte__compute_digits(stbte_tilemap *tm) +{ + if (tm->max_x >= 1000 || tm->max_y >= 1000) + tm->digits = 4; + else if (tm->max_x >= 100 || tm->max_y >= 100) + tm->digits = 3; + else + tm->digits = 2; +} + +static int stbte__is_single_selection(void) +{ + return stbte__ui.has_selection + && stbte__ui.select_x0 == stbte__ui.select_x1 + && stbte__ui.select_y0 == stbte__ui.select_y1; +} + +typedef struct +{ + int width, height; + int x,y; + int active; + float retracted; +} stbte__region_t; + +static stbte__region_t stbte__region[4]; + +#define STBTE__TOOLBAR_ICON_SIZE (9+2*2) +#define STBTE__TOOLBAR_PASTE_SIZE (34+2*2) + +// This routine computes where every panel goes onscreen: computes +// a minimum width for each side based on which panels are on that +// side, and accounts for width-dependent layout of certain panels. +static void stbte__compute_panel_locations(stbte_tilemap *tm) +{ + int i, limit, w, k; + int window_width = stbte__ui.x1 - stbte__ui.x0; + int window_height = stbte__ui.y1 - stbte__ui.y0; + int min_width[STBTE__num_panel]={0,0,0,0,0,0,0}; + int height[STBTE__num_panel]={0,0,0,0,0,0,0}; + int panel_active[STBTE__num_panel]={1,0,1,1,1,1,1}; + int vpos[4] = { 0,0,0,0 }; + stbte__panel *p = stbte__ui.panel; + stbte__panel *pt = &p[STBTE__panel_toolbar]; +#ifdef STBTE__NO_PROPS + int props = 0; +#else + int props = 1; +#endif + + for (i=0; i < 4; ++i) { + stbte__region[i].active = 0; + stbte__region[i].width = 0; + stbte__region[i].height = 0; + } + + // compute number of digits needs for info panel + stbte__compute_digits(tm); + + // determine which panels are active + panel_active[STBTE__panel_categories] = tm->num_categories != 0; + panel_active[STBTE__panel_layers ] = tm->num_layers > 1; +#ifdef STBTE__COLORPICKER + panel_active[STBTE__panel_colorpick ] = 1; +#endif + + panel_active[STBTE__panel_props ] = props && stbte__is_single_selection(); + + // compute minimum widths for each panel (assuming they're on sides not top) + min_width[STBTE__panel_info ] = 8 + 11 + 7*tm->digits+17+7; // estimate min width of "w:0000" + min_width[STBTE__panel_colorpick ] = 120; + min_width[STBTE__panel_tiles ] = 4 + tm->palette_spacing_x + 5; // 5 for scrollbar + min_width[STBTE__panel_categories] = 4 + 42 + 5; // 42 is enough to show ~7 chars; 5 for scrollbar + min_width[STBTE__panel_layers ] = 4 + 54 + 30*tm->has_layer_names; // 2 digits plus 3 buttons plus scrollbar + min_width[STBTE__panel_toolbar ] = 4 + STBTE__TOOLBAR_PASTE_SIZE; // wide enough for 'Paste' button + min_width[STBTE__panel_props ] = 80; // narrowest info panel + + // compute minimum widths for left & right panels based on the above + stbte__region[0].width = stbte__ui.left_width; + stbte__region[1].width = stbte__ui.right_width; + + for (i=0; i < STBTE__num_panel; ++i) { + if (panel_active[i]) { + int side = stbte__ui.panel[i].side; + if (min_width[i] > stbte__region[side].width) + stbte__region[side].width = min_width[i]; + stbte__region[side].active = 1; + } + } + + // now compute the heights of each panel + + // if toolbar at top, compute its size & push the left and right start points down + if (stbte__region[STBTE__side_top].active) { + int height = STBTE__TOOLBAR_ICON_SIZE+2; + pt->x0 = stbte__ui.x0; + pt->y0 = stbte__ui.y0; + pt->width = window_width; + pt->height = height; + vpos[STBTE__side_left] = vpos[STBTE__side_right] = height; + } else { + int num_rows = STBTE__num_tool * ((stbte__region[pt->side].width-4)/STBTE__TOOLBAR_ICON_SIZE); + height[STBTE__panel_toolbar] = num_rows*13 + 3*15 + 4; // 3*15 for cut/copy/paste, which are stacked vertically + } + + for (i=0; i < 4; ++i) + stbte__region[i].y = stbte__ui.y0 + vpos[i]; + + for (i=0; i < 2; ++i) { + int anim = (int) (stbte__region[i].width * stbte__region[i].retracted); + stbte__region[i].x = (i == STBTE__side_left) ? stbte__ui.x0 - anim : stbte__ui.x1 - stbte__region[i].width + anim; + } + + // color picker + height[STBTE__panel_colorpick] = 300; + + // info panel + w = stbte__region[p[STBTE__panel_info].side].width; + p[STBTE__panel_info].mode = (w >= 8 + (11+7*tm->digits+17)*2 + 4); + if (p[STBTE__panel_info].mode) + height[STBTE__panel_info] = 5 + 11*2 + 2 + tm->palette_spacing_y; + else + height[STBTE__panel_info] = 5 + 11*4 + 2 + tm->palette_spacing_y; + + // layers + limit = 6 + stbte__ui.panel[STBTE__panel_layers].delta_height; + height[STBTE__panel_layers] = (tm->num_layers > limit ? limit : tm->num_layers)*15 + 7 + (tm->has_layer_names ? 0 : 11) + props*13; + + // categories + limit = 6 + stbte__ui.panel[STBTE__panel_categories].delta_height; + height[STBTE__panel_categories] = (tm->num_categories+1 > limit ? limit : tm->num_categories+1)*11 + 14; + if (stbte__ui.panel[STBTE__panel_categories].side == stbte__ui.panel[STBTE__panel_categories].side) + height[STBTE__panel_categories] -= 4; + + // palette + k = (stbte__region[p[STBTE__panel_tiles].side].width - 8) / tm->palette_spacing_x; + if (k == 0) k = 1; + height[STBTE__panel_tiles] = ((tm->num_tiles+k-1)/k) * tm->palette_spacing_y + 8; + + // properties panel + height[STBTE__panel_props] = 9 + STBTE_MAX_PROPERTIES*14; + + // now compute the locations of all the panels + for (i=0; i < STBTE__num_panel; ++i) { + if (panel_active[i]) { + int side = p[i].side; + if (side == STBTE__side_left || side == STBTE__side_right) { + p[i].width = stbte__region[side].width; + p[i].x0 = stbte__region[side].x; + p[i].y0 = stbte__ui.y0 + vpos[side]; + p[i].height = height[i]; + vpos[side] += height[i]; + if (vpos[side] > window_height) { + vpos[side] = window_height; + p[i].height = stbte__ui.y1 - p[i].y0; + } + } else { + ; // it's at top, it's already been explicitly set up earlier + } + } else { + // inactive panel + p[i].height = 0; + p[i].width = 0; + p[i].x0 = stbte__ui.x1; + p[i].y0 = stbte__ui.y1; + } + } +} + +// unique identifiers for imgui +enum +{ + STBTE__map=1, + STBTE__region, + STBTE__panel, // panel background to hide map, and misc controls + STBTE__info, // info data + STBTE__toolbarA, STBTE__toolbarB, // toolbar buttons: param is tool number + STBTE__palette, // palette selectors: param is tile index + STBTE__categories, // category selectors: param is category index + STBTE__layer, // + STBTE__solo, STBTE__hide, STBTE__lock, // layer controls: param is layer + STBTE__scrollbar, // param is panel ID + STBTE__panel_mover, // p1 is panel ID, p2 is destination side + STBTE__panel_sizer, // param panel ID + STBTE__scrollbar_id, + STBTE__colorpick_id, + STBTE__prop_flag, + STBTE__prop_float, + STBTE__prop_int, +}; + +// id is: [ 24-bit data : 7-bit identifer ] +// map id is: [ 12-bit y : 12 bit x : 7-bit identifier ] + +#define STBTE__ID(n,p) ((n) + ((p)<<7)) +#define STBTE__ID2(n,p,q) STBTE__ID(n, ((p)<<12)+(q) ) +#define STBTE__IDMAP(x,y) STBTE__ID2(STBTE__map, x,y) + +static void stbte__activate_map(int x, int y) +{ + stbte__ui.active_id = STBTE__IDMAP(x,y); + stbte__ui.active_event = stbte__ui.event; + stbte__ui.sx = x; + stbte__ui.sy = y; +} + +static void stbte__alert(const char *msg) +{ + stbte__ui.alert_msg = msg; + stbte__ui.alert_timer = 3; +} + +#define STBTE__BG(tm,layer) ((layer) == 0 ? (tm)->background_tile : STBTE__NO_TILE) + + + +static void stbte__brush_predict(stbte_tilemap *tm, short result[]) +{ + int layer_to_paint = tm->cur_layer; + stbte__tileinfo *ti; + int i; + + if (tm->cur_tile < 0) return; + + ti = &tm->tiles[tm->cur_tile]; + + // find lowest legit layer to paint it on, and put it there + for (i=0; i < tm->num_layers; ++i) { + // check if object is allowed on layer + if (!(ti->layermask & (1 << i))) + continue; + + if (i != tm->solo_layer) { + // if there's a selected layer, can only paint on that + if (tm->cur_layer >= 0 && i != tm->cur_layer) + continue; + + // if the layer is hidden, we can't see it + if (tm->layerinfo[i].hidden) + continue; + + // if the layer is locked, we can't write to it + if (tm->layerinfo[i].locked == STBTE__locked) + continue; + + // if the layer is non-empty and protected, can't write to it + if (tm->layerinfo[i].locked == STBTE__protected && result[i] != STBTE__BG(tm,i)) + continue; + } + + result[i] = ti->id; + return; + } +} + +static void stbte__brush(stbte_tilemap *tm, int x, int y) +{ + int layer_to_paint = tm->cur_layer; + stbte__tileinfo *ti; + + // find lowest legit layer to paint it on, and put it there + int i; + + if (tm->cur_tile < 0) return; + + ti = &tm->tiles[tm->cur_tile]; + + for (i=0; i < tm->num_layers; ++i) { + // check if object is allowed on layer + if (!(ti->layermask & (1 << i))) + continue; + + if (i != tm->solo_layer) { + // if there's a selected layer, can only paint on that + if (tm->cur_layer >= 0 && i != tm->cur_layer) + continue; + + // if the layer is hidden, we can't see it + if (tm->layerinfo[i].hidden) + continue; + + // if the layer is locked, we can't write to it + if (tm->layerinfo[i].locked == STBTE__locked) + continue; + + // if the layer is non-empty and protected, can't write to it + if (tm->layerinfo[i].locked == STBTE__protected && tm->data[y][x][i] != STBTE__BG(tm,i)) + continue; + } + + stbte__undo_record(tm,x,y,i,tm->data[y][x][i]); + tm->data[y][x][i] = ti->id; + return; + } + + //stbte__alert("Selected tile not valid on active layer(s)"); +} + +enum +{ + STBTE__erase_none = -1, + STBTE__erase_brushonly = 0, + STBTE__erase_any = 1, + STBTE__erase_all = 2, +}; + +static int stbte__erase_predict(stbte_tilemap *tm, short result[], int allow_any) +{ + stbte__tileinfo *ti = tm->cur_tile >= 0 ? &tm->tiles[tm->cur_tile] : NULL; + int i; + + if (allow_any == STBTE__erase_none) + return allow_any; + + // first check if only one layer is legit + i = tm->cur_layer; + if (tm->solo_layer >= 0) + i = tm->solo_layer; + + // if only one layer is legit, directly process that one for clarity + if (i >= 0) { + short bg = (i == 0 ? tm->background_tile : -1); + if (tm->solo_layer < 0) { + // check that we're allowed to write to it + if (tm->layerinfo[i].hidden) return STBTE__erase_none; + if (tm->layerinfo[i].locked) return STBTE__erase_none; + } + if (result[i] == bg) + return STBTE__erase_none; // didn't erase anything + if (ti && result[i] == ti->id && (i != 0 || ti->id != tm->background_tile)) { + result[i] = bg; + return STBTE__erase_brushonly; + } + if (allow_any == STBTE__erase_any) { + result[i] = bg; + return STBTE__erase_any; + } + return STBTE__erase_none; + } + + // if multiple layers are legit, first scan all for brush data + + if (ti && allow_any != STBTE__erase_all) { + for (i=tm->num_layers-1; i >= 0; --i) { + if (result[i] != ti->id) + continue; + if (tm->layerinfo[i].locked || tm->layerinfo[i].hidden) + continue; + if (i == 0 && result[i] == tm->background_tile) + return STBTE__erase_none; + result[i] = STBTE__BG(tm,i); + return STBTE__erase_brushonly; + } + } + + if (allow_any != STBTE__erase_any && allow_any != STBTE__erase_all) + return STBTE__erase_none; + + // apply layer filters, erase from top + for (i=tm->num_layers-1; i >= 0; --i) { + if (result[i] < 0) + continue; + if (tm->layerinfo[i].locked || tm->layerinfo[i].hidden) + continue; + if (i == 0 && result[i] == tm->background_tile) + return STBTE__erase_none; + result[i] = STBTE__BG(tm,i); + if (allow_any != STBTE__erase_all) + return STBTE__erase_any; + } + + if (allow_any == STBTE__erase_all) + return allow_any; + return STBTE__erase_none; +} + +static int stbte__erase(stbte_tilemap *tm, int x, int y, int allow_any) +{ + stbte__tileinfo *ti = tm->cur_tile >= 0 ? &tm->tiles[tm->cur_tile] : NULL; + int i; + + if (allow_any == STBTE__erase_none) + return allow_any; + + // first check if only one layer is legit + i = tm->cur_layer; + if (tm->solo_layer >= 0) + i = tm->solo_layer; + + // if only one layer is legit, directly process that one for clarity + if (i >= 0) { + short bg = (i == 0 ? tm->background_tile : -1); + if (tm->solo_layer < 0) { + // check that we're allowed to write to it + if (tm->layerinfo[i].hidden) return STBTE__erase_none; + if (tm->layerinfo[i].locked) return STBTE__erase_none; + } + if (tm->data[y][x][i] == bg) + return -1; // didn't erase anything + if (ti && tm->data[y][x][i] == ti->id && (i != 0 || ti->id != tm->background_tile)) { + stbte__undo_record(tm,x,y,i,tm->data[y][x][i]); + tm->data[y][x][i] = bg; + return STBTE__erase_brushonly; + } + if (allow_any == STBTE__erase_any) { + stbte__undo_record(tm,x,y,i,tm->data[y][x][i]); + tm->data[y][x][i] = bg; + return STBTE__erase_any; + } + return STBTE__erase_none; + } + + // if multiple layers are legit, first scan all for brush data + + if (ti && allow_any != STBTE__erase_all) { + for (i=tm->num_layers-1; i >= 0; --i) { + if (tm->data[y][x][i] != ti->id) + continue; + if (tm->layerinfo[i].locked || tm->layerinfo[i].hidden) + continue; + if (i == 0 && tm->data[y][x][i] == tm->background_tile) + return STBTE__erase_none; + stbte__undo_record(tm,x,y,i,tm->data[y][x][i]); + tm->data[y][x][i] = STBTE__BG(tm,i); + return STBTE__erase_brushonly; + } + } + + if (allow_any != STBTE__erase_any && allow_any != STBTE__erase_all) + return STBTE__erase_none; + + // apply layer filters, erase from top + for (i=tm->num_layers-1; i >= 0; --i) { + if (tm->data[y][x][i] < 0) + continue; + if (tm->layerinfo[i].locked || tm->layerinfo[i].hidden) + continue; + if (i == 0 && tm->data[y][x][i] == tm->background_tile) + return STBTE__erase_none; + stbte__undo_record(tm,x,y,i,tm->data[y][x][i]); + tm->data[y][x][i] = STBTE__BG(tm,i); + if (allow_any != STBTE__erase_all) + return STBTE__erase_any; + } + if (allow_any == STBTE__erase_all) + return allow_any; + return STBTE__erase_none; +} + +static int stbte__find_tile(stbte_tilemap *tm, int tile_id) +{ + int i; + for (i=0; i < tm->num_tiles; ++i) + if (tm->tiles[i].id == tile_id) + return i; + stbte__alert("Eyedropped tile that isn't in tileset"); + return -1; +} + +static void stbte__eyedrop(stbte_tilemap *tm, int x, int y) +{ + int i,j; + + // flush eyedropper state + if (stbte__ui.eyedrop_x != x || stbte__ui.eyedrop_y != y) { + stbte__ui.eyedrop_x = x; + stbte__ui.eyedrop_y = y; + stbte__ui.eyedrop_last_layer = tm->num_layers; + } + + // if only one layer is active, query that + i = tm->cur_layer; + if (tm->solo_layer >= 0) + i = tm->solo_layer; + if (i >= 0) { + if (tm->data[y][x][i] == STBTE__NO_TILE) + return; + tm->cur_tile = stbte__find_tile(tm, tm->data[y][x][i]); + return; + } + + // if multiple layers, continue from previous + i = stbte__ui.eyedrop_last_layer; + for (j=0; j < tm->num_layers; ++j) { + if (--i < 0) + i = tm->num_layers-1; + if (tm->layerinfo[i].hidden) + continue; + if (tm->data[y][x][i] == STBTE__NO_TILE) + continue; + stbte__ui.eyedrop_last_layer = i; + tm->cur_tile = stbte__find_tile(tm, tm->data[y][x][i]); + return; + } +} + +static int stbte__should_copy_properties(stbte_tilemap *tm) +{ + int i; + if (tm->propmode == STBTE__propmode_always) + return 1; + if (tm->propmode == STBTE__propmode_never) + return 0; + if (tm->solo_layer >= 0 || tm->cur_layer >= 0) + return 0; + for (i=0; i < tm->num_layers; ++i) + if (tm->layerinfo[i].hidden || tm->layerinfo[i].locked) + return 0; + return 1; +} + +// compute the result of pasting into a tile non-destructively so we can preview it +static void stbte__paste_stack(stbte_tilemap *tm, short result[], short dest[], short src[], int dragging) +{ + int i; + + // special case single-layer + i = tm->cur_layer; + if (tm->solo_layer >= 0) + i = tm->solo_layer; + if (i >= 0) { + if (tm->solo_layer < 0) { + // check that we're allowed to write to it + if (tm->layerinfo[i].hidden) return; + if (tm->layerinfo[i].locked == STBTE__locked) return; + // if protected, dest has to be empty + if (tm->layerinfo[i].locked == STBTE__protected && dest[i] != STBTE__BG(tm,i)) return; + // if dragging w/o copy, we will try to erase stuff, which protection disallows + if (dragging && tm->layerinfo[i].locked == STBTE__protected) + return; + } + result[i] = dest[i]; + if (src[i] != STBTE__BG(tm,i)) + result[i] = src[i]; + return; + } + + for (i=0; i < tm->num_layers; ++i) { + result[i] = dest[i]; + if (src[i] != STBTE__NO_TILE) + if (!tm->layerinfo[i].hidden && tm->layerinfo[i].locked != STBTE__locked) + if (tm->layerinfo[i].locked == STBTE__unlocked || (!dragging && dest[i] == STBTE__BG(tm,i))) + result[i] = src[i]; + } +} + +// compute the result of dragging away from a tile +static void stbte__clear_stack(stbte_tilemap *tm, short result[]) +{ + int i; + // special case single-layer + i = tm->cur_layer; + if (tm->solo_layer >= 0) + i = tm->solo_layer; + if (i >= 0) + result[i] = STBTE__BG(tm,i); + else + for (i=0; i < tm->num_layers; ++i) + if (!tm->layerinfo[i].hidden && tm->layerinfo[i].locked == STBTE__unlocked) + result[i] = STBTE__BG(tm,i); +} + +// check if some map square is active +#define STBTE__IS_MAP_ACTIVE() ((stbte__ui.active_id & 127) == STBTE__map) +#define STBTE__IS_MAP_HOT() ((stbte__ui.hot_id & 127) == STBTE__map) + +static void stbte__fillrect(stbte_tilemap *tm, int x0, int y0, int x1, int y1, int fill) +{ + int i,j; + int x=x0,y=y0; + + stbte__begin_undo(tm); + if (x0 > x1) i=x0,x0=x1,x1=i; + if (y0 > y1) j=y0,y0=y1,y1=j; + for (j=y0; j <= y1; ++j) + for (i=x0; i <= x1; ++i) + if (fill) + stbte__brush(tm, i,j); + else + stbte__erase(tm, i,j,STBTE__erase_any); + stbte__end_undo(tm); + // suppress warning from brush + stbte__ui.alert_msg = 0; +} + +static void stbte__select_rect(stbte_tilemap *tm, int x0, int y0, int x1, int y1) +{ + stbte__ui.has_selection = 1; + stbte__ui.select_x0 = (x0 < x1 ? x0 : x1); + stbte__ui.select_x1 = (x0 < x1 ? x1 : x0); + stbte__ui.select_y0 = (y0 < y1 ? y0 : y1); + stbte__ui.select_y1 = (y0 < y1 ? y1 : y0); +} + +static void stbte__copy_properties(float *dest, float *src) +{ + int i; + for (i=0; i < STBTE_MAX_PROPERTIES; ++i) + dest[i] = src[i]; +} + +static void stbte__copy_cut(stbte_tilemap *tm, int cut) +{ + int i,j,n,w,h,p=0; + int copy_props = stbte__should_copy_properties(tm); + if (!stbte__ui.has_selection) + return; + w = stbte__ui.select_x1 - stbte__ui.select_x0 + 1; + h = stbte__ui.select_y1 - stbte__ui.select_y0 + 1; + if (STBTE_MAX_COPY / w < h) { + stbte__alert("Selection too large for copy buffer, increase STBTE_MAX_COPY"); + return; + } + + for (i=0; i < w*h; ++i) + for (n=0; n < tm->num_layers; ++n) + stbte__ui.copybuffer[i][n] = STBTE__NO_TILE; + + if (cut) + stbte__begin_undo(tm); + for (j=stbte__ui.select_y0; j <= stbte__ui.select_y1; ++j) { + for (i=stbte__ui.select_x0; i <= stbte__ui.select_x1; ++i) { + for (n=0; n < tm->num_layers; ++n) { + if (tm->solo_layer >= 0) { + if (tm->solo_layer != n) + continue; + } else { + if (tm->cur_layer >= 0) + if (tm->cur_layer != n) + continue; + if (tm->layerinfo[n].hidden) + continue; + if (cut && tm->layerinfo[n].locked) + continue; + } + stbte__ui.copybuffer[p][n] = tm->data[j][i][n]; + if (cut) { + stbte__undo_record(tm,i,j,n, tm->data[j][i][n]); + tm->data[j][i][n] = (n==0 ? tm->background_tile : -1); + } + } + if (copy_props) { + stbte__copy_properties(stbte__ui.copyprops[p], tm->props[j][i]); +#ifdef STBTE_ALLOW_LINK + stbte__ui.copylinks[p] = tm->link[j][i]; + if (cut) + stbte__set_link(tm, i,j,-1,-1, STBTE__undo_record); +#endif + } + ++p; + } + } + if (cut) + stbte__end_undo(tm); + stbte__ui.copy_width = w; + stbte__ui.copy_height = h; + stbte__ui.has_copy = 1; + //stbte__ui.has_selection = 0; + stbte__ui.copy_has_props = copy_props; + stbte__ui.copy_src = tm; // used to give better semantics when copying links + stbte__ui.copy_src_x = stbte__ui.select_x0; + stbte__ui.copy_src_y = stbte__ui.select_y0; +} + +static int stbte__in_rect(int x, int y, int x0, int y0, int w, int h) +{ + return x >= x0 && x < x0+w && y >= y0 && y < y0+h; +} + +static int stbte__in_src_rect(int x, int y) +{ + return stbte__in_rect(x,y, stbte__ui.copy_src_x, stbte__ui.copy_src_y, stbte__ui.copy_width, stbte__ui.copy_height); +} + +static int stbte__in_dest_rect(int x, int y, int destx, int desty) +{ + return stbte__in_rect(x,y, destx, desty, stbte__ui.copy_width, stbte__ui.copy_height); +} + +static void stbte__paste(stbte_tilemap *tm, int mapx, int mapy) +{ + int w = stbte__ui.copy_width; + int h = stbte__ui.copy_height; + int i,j,k,p; + int x = mapx - (w>>1); + int y = mapy - (h>>1); + int copy_props = stbte__should_copy_properties(tm) && stbte__ui.copy_has_props; + if (stbte__ui.has_copy == 0) + return; + stbte__begin_undo(tm); + p = 0; + for (j=0; j < h; ++j) { + for (i=0; i < w; ++i) { + if (y+j >= 0 && y+j < tm->max_y && x+i >= 0 && x+i < tm->max_x) { + // compute the new stack + short tilestack[STBTE_MAX_LAYERS]; + for (k=0; k < tm->num_layers; ++k) + tilestack[k] = tm->data[y+j][x+i][k]; + stbte__paste_stack(tm, tilestack, tilestack, stbte__ui.copybuffer[p], 0); + // update anything that changed + for (k=0; k < tm->num_layers; ++k) { + if (tilestack[k] != tm->data[y+j][x+i][k]) { + stbte__undo_record(tm, x+i,y+j,k, tm->data[y+j][x+i][k]); + tm->data[y+j][x+i][k] = tilestack[k]; + } + } + } + if (copy_props) { +#ifdef STBTE_ALLOW_LINK + // need to decide how to paste a link, so there's a few cases + int destx = -1, desty = -1; + stbte__link *link = &stbte__ui.copylinks[p]; + + // check if link is within-rect + if (stbte__in_src_rect(link->x, link->y)) { + // new link should point to copy (but only if copy is within map) + destx = x + (link->x - stbte__ui.copy_src_x); + desty = y + (link->y - stbte__ui.copy_src_y); + } else if (tm == stbte__ui.copy_src) { + // if same map, then preserve link unless target is overwritten + if (!stbte__in_dest_rect(link->x,link->y,x,y)) { + destx = link->x; + desty = link->y; + } + } + // this is necessary for offset-copy, but also in case max_x/max_y has changed + if (destx < 0 || destx >= tm->max_x || desty < 0 || desty >= tm->max_y) + destx = -1, desty = -1; + stbte__set_link(tm, x+i, y+j, destx, desty, STBTE__undo_record); +#endif + for (k=0; k < STBTE_MAX_PROPERTIES; ++k) { + if (tm->props[y+j][x+i][k] != stbte__ui.copyprops[p][k]) + stbte__undo_record_prop_float(tm, x+i, y+j, k, tm->props[y+j][x+i][k]); + } + stbte__copy_properties(tm->props[y+j][x+i], stbte__ui.copyprops[p]); + } + ++p; + } + } + stbte__end_undo(tm); +} + +static void stbte__drag_update(stbte_tilemap *tm, int mapx, int mapy, int copy_props) +{ + int w = stbte__ui.drag_w, h = stbte__ui.drag_h; + int ox,oy,i,deleted=0,written=0; + short temp[STBTE_MAX_LAYERS]; + short *data = NULL; + if (!stbte__ui.shift) { + ox = mapx - stbte__ui.drag_x; + oy = mapy - stbte__ui.drag_y; + if (ox >= 0 && ox < w && oy >= 0 && oy < h) { + deleted=1; + for (i=0; i < tm->num_layers; ++i) + temp[i] = tm->data[mapy][mapx][i]; + data = temp; + stbte__clear_stack(tm, data); + } + } + ox = mapx - stbte__ui.drag_dest_x; + oy = mapy - stbte__ui.drag_dest_y; + // if this map square is in the target drag region + if (ox >= 0 && ox < w && oy >= 0 && oy < h) { + // and the src map square is on the map + if (stbte__in_rect(stbte__ui.drag_x+ox, stbte__ui.drag_y+oy, 0, 0, tm->max_x, tm->max_y)) { + written = 1; + if (data == NULL) { + for (i=0; i < tm->num_layers; ++i) + temp[i] = tm->data[mapy][mapx][i]; + data = temp; + } + stbte__paste_stack(tm, data, data, tm->data[stbte__ui.drag_y+oy][stbte__ui.drag_x+ox], !stbte__ui.shift); + if (copy_props) { + for (i=0; i < STBTE_MAX_PROPERTIES; ++i) { + if (tm->props[mapy][mapx][i] != tm->props[stbte__ui.drag_y+oy][stbte__ui.drag_x+ox][i]) { + stbte__undo_record_prop_float(tm, mapx, mapy, i, tm->props[mapy][mapx][i]); + tm->props[mapy][mapx][i] = tm->props[stbte__ui.drag_y+oy][stbte__ui.drag_x+ox][i]; + } + } + } + } + } + if (data) { + for (i=0; i < tm->num_layers; ++i) { + if (tm->data[mapy][mapx][i] != data[i]) { + stbte__undo_record(tm, mapx, mapy, i, tm->data[mapy][mapx][i]); + tm->data[mapy][mapx][i] = data[i]; + } + } + } + #ifdef STBTE_ALLOW_LINK + if (copy_props) { + int overwritten=0, moved=0, copied=0; + // since this function is called on EVERY tile, we can fix up even tiles not + // involved in the move + + stbte__link *k; + // first, determine what src link ends up here + k = &tm->link[mapy][mapx]; // by default, it's the one currently here + if (deleted) // if dragged away, it's erased + k = NULL; + if (written) // if dragged into, it gets that link + k = &tm->link[stbte__ui.drag_y+oy][stbte__ui.drag_x+ox]; + + // now check whether the *target* gets moved or overwritten + if (k && k->x >= 0) { + overwritten = stbte__in_rect(k->x, k->y, stbte__ui.drag_dest_x, stbte__ui.drag_dest_y, w, h); + if (!stbte__ui.shift) + moved = stbte__in_rect(k->x, k->y, stbte__ui.drag_x , stbte__ui.drag_y , w, h); + else + copied = stbte__in_rect(k->x, k->y, stbte__ui.drag_x , stbte__ui.drag_y , w, h); + } + + if (deleted || written || overwritten || moved || copied) { + // choose the final link value based on the above + if (k == NULL || k->x < 0) + stbte__set_link(tm, mapx, mapy, -1, -1, STBTE__undo_record); + else if (moved || (copied && written)) { + // if we move the target, we update to point to the new target; + // or, if we copy the target and the source is part ofthe copy, then update to new target + int x = k->x + (stbte__ui.drag_dest_x - stbte__ui.drag_x); + int y = k->y + (stbte__ui.drag_dest_y - stbte__ui.drag_y); + if (!(x >= 0 && y >= 0 && x < tm->max_x && y < tm->max_y)) + x = -1, y = -1; + stbte__set_link(tm, mapx, mapy, x, y, STBTE__undo_record); + } else if (overwritten) { + stbte__set_link(tm, mapx, mapy, -1, -1, STBTE__undo_record); + } else + stbte__set_link(tm, mapx, mapy, k->x, k->y, STBTE__undo_record); + } + } + #endif +} + +static void stbte__drag_place(stbte_tilemap *tm, int mapx, int mapy) +{ + int i,j; + int copy_props = stbte__should_copy_properties(tm); + int move_x = (stbte__ui.drag_dest_x - stbte__ui.drag_x); + int move_y = (stbte__ui.drag_dest_y - stbte__ui.drag_y); + if (move_x == 0 && move_y == 0) + return; + + stbte__begin_undo(tm); + // we now need a 2D memmove-style mover that doesn't + // overwrite any data as it goes. this requires being + // direction sensitive in the same way as memmove + if (move_y > 0 || (move_y == 0 && move_x > 0)) { + for (j=tm->max_y-1; j >= 0; --j) + for (i=tm->max_x-1; i >= 0; --i) + stbte__drag_update(tm,i,j,copy_props); + } else { + for (j=0; j < tm->max_y; ++j) + for (i=0; i < tm->max_x; ++i) + stbte__drag_update(tm,i,j,copy_props); + } + stbte__end_undo(tm); + + stbte__ui.has_selection = 1; + stbte__ui.select_x0 = stbte__ui.drag_dest_x; + stbte__ui.select_y0 = stbte__ui.drag_dest_y; + stbte__ui.select_x1 = stbte__ui.select_x0 + stbte__ui.drag_w - 1; + stbte__ui.select_y1 = stbte__ui.select_y0 + stbte__ui.drag_h - 1; +} + +static void stbte__tile_paint(stbte_tilemap *tm, int sx, int sy, int mapx, int mapy, int layer) +{ + int i; + int id = STBTE__IDMAP(mapx,mapy); + int x0=sx, y0=sy; + int x1=sx+tm->spacing_x, y1=sy+tm->spacing_y; + int over = stbte__hittest(x0,y0,x1,y1, id); + short *data = tm->data[mapy][mapx]; + short temp[STBTE_MAX_LAYERS]; + + if (STBTE__IS_MAP_HOT()) { + if (stbte__ui.pasting) { + int ox = mapx - stbte__ui.paste_x; + int oy = mapy - stbte__ui.paste_y; + if (ox >= 0 && ox < stbte__ui.copy_width && oy >= 0 && oy < stbte__ui.copy_height) { + stbte__paste_stack(tm, temp, tm->data[mapy][mapx], stbte__ui.copybuffer[oy*stbte__ui.copy_width+ox], 0); + data = temp; + } + } else if (stbte__ui.dragging) { + int ox,oy; + for (i=0; i < tm->num_layers; ++i) + temp[i] = tm->data[mapy][mapx][i]; + data = temp; + + // if it's in the source area, remove things unless shift-dragging + ox = mapx - stbte__ui.drag_x; + oy = mapy - stbte__ui.drag_y; + if (!stbte__ui.shift && ox >= 0 && ox < stbte__ui.drag_w && oy >= 0 && oy < stbte__ui.drag_h) { + stbte__clear_stack(tm, temp); + } + + ox = mapx - stbte__ui.drag_dest_x; + oy = mapy - stbte__ui.drag_dest_y; + if (ox >= 0 && ox < stbte__ui.drag_w && oy >= 0 && oy < stbte__ui.drag_h) { + stbte__paste_stack(tm, temp, temp, tm->data[stbte__ui.drag_y+oy][stbte__ui.drag_x+ox], !stbte__ui.shift); + } + } else if (STBTE__IS_MAP_ACTIVE()) { + if (stbte__ui.tool == STBTE__tool_rect) { + if ((stbte__ui.ms_time & 511) < 380) { + int ex = ((stbte__ui.hot_id >> 19) & 4095); + int ey = ((stbte__ui.hot_id >> 7) & 4095); + int sx = stbte__ui.sx; + int sy = stbte__ui.sy; + + if ( ((mapx >= sx && mapx < ex+1) || (mapx >= ex && mapx < sx+1)) + && ((mapy >= sy && mapy < ey+1) || (mapy >= ey && mapy < sy+1))) { + int i; + for (i=0; i < tm->num_layers; ++i) + temp[i] = tm->data[mapy][mapx][i]; + data = temp; + if (stbte__ui.active_event == STBTE__leftdown) + stbte__brush_predict(tm, temp); + else + stbte__erase_predict(tm, temp, STBTE__erase_any); + } + } + } + } + } + + if (STBTE__IS_HOT(id) && STBTE__INACTIVE() && !stbte__ui.pasting) { + if (stbte__ui.tool == STBTE__tool_brush) { + if ((stbte__ui.ms_time & 511) < 300) { + data = temp; + for (i=0; i < tm->num_layers; ++i) + temp[i] = tm->data[mapy][mapx][i]; + stbte__brush_predict(tm, temp); + } + } + } + + { + i = layer; + if (i == tm->solo_layer || (!tm->layerinfo[i].hidden && tm->solo_layer < 0)) + if (data[i] >= 0) + STBTE_DRAW_TILE(x0,y0, (unsigned short) data[i], 0, tm->props[mapy][mapx]); + } +} + +static void stbte__tile(stbte_tilemap *tm, int sx, int sy, int mapx, int mapy) +{ + int tool = stbte__ui.tool; + int x0=sx, y0=sy; + int x1=sx+tm->spacing_x, y1=sy+tm->spacing_y; + int id = STBTE__IDMAP(mapx,mapy); + int over = stbte__hittest(x0,y0,x1,y1, id); + switch (stbte__ui.event) { + case STBTE__paint: { + if (stbte__ui.pasting || stbte__ui.dragging || stbte__ui.scrolling) + break; + if (stbte__ui.scrollkey && !STBTE__IS_MAP_ACTIVE()) + break; + if (STBTE__IS_HOT(id) && STBTE__IS_MAP_ACTIVE() && (tool == STBTE__tool_rect || tool == STBTE__tool_select)) { + int rx0,ry0,rx1,ry1,t; + // compute the center of each rect + rx0 = x0 + tm->spacing_x/2; + ry0 = y0 + tm->spacing_y/2; + rx1 = rx0 + (stbte__ui.sx - mapx) * tm->spacing_x; + ry1 = ry0 + (stbte__ui.sy - mapy) * tm->spacing_y; + if (rx0 > rx1) t=rx0,rx0=rx1,rx1=t; + if (ry0 > ry1) t=ry0,ry0=ry1,ry1=t; + rx0 -= tm->spacing_x/2; + ry0 -= tm->spacing_y/2; + rx1 += tm->spacing_x/2; + ry1 += tm->spacing_y/2; + stbte__draw_frame(rx0-1,ry0-1,rx1+1,ry1+1, STBTE_COLOR_TILEMAP_HIGHLIGHT); + break; + } + if (STBTE__IS_HOT(id) && STBTE__INACTIVE()) { + stbte__draw_frame(x0-1,y0-1,x1+1,y1+1, STBTE_COLOR_TILEMAP_HIGHLIGHT); + } +#ifdef STBTE_ALLOW_LINK + if (stbte__ui.show_links && tm->link[mapy][mapx].x >= 0) { + int tx = tm->link[mapy][mapx].x; + int ty = tm->link[mapy][mapx].y; + int lx0,ly0,lx1,ly1; + if (STBTE_ALLOW_LINK(tm->data[mapy][mapx], tm->props[mapy][mapx], + tm->data[ty ][tx ], tm->props[ty ][tx ])) + { + lx0 = x0 + (tm->spacing_x >> 1) - 1; + ly0 = y0 + (tm->spacing_y >> 1) - 1; + lx1 = lx0 + (tx - mapx) * tm->spacing_x + 2; + ly1 = ly0 + (ty - mapy) * tm->spacing_y + 2; + stbte__draw_link(lx0,ly0,lx1,ly1, + STBTE_LINK_COLOR(tm->data[mapy][mapx], tm->props[mapy][mapx], + tm->data[ty ][tx ], tm->props[ty ][tx])); + } + } +#endif + break; + } + } + + if (stbte__ui.pasting) { + switch (stbte__ui.event) { + case STBTE__leftdown: + if (STBTE__IS_HOT(id)) { + stbte__ui.pasting = 0; + stbte__paste(tm, mapx, mapy); + stbte__activate(0); + } + break; + case STBTE__leftup: + // just clear it no matter what, since they might click away to clear it + stbte__activate(0); + break; + case STBTE__rightdown: + if (STBTE__IS_HOT(id)) { + stbte__activate(0); + stbte__ui.pasting = 0; + } + break; + } + return; + } + + if (stbte__ui.scrolling) { + if (stbte__ui.event == STBTE__leftup) { + stbte__activate(0); + stbte__ui.scrolling = 0; + } + if (stbte__ui.event == STBTE__mousemove) { + tm->scroll_x += (stbte__ui.start_x - stbte__ui.mx); + tm->scroll_y += (stbte__ui.start_y - stbte__ui.my); + stbte__ui.start_x = stbte__ui.mx; + stbte__ui.start_y = stbte__ui.my; + } + return; + } + + // regardless of tool, leftdown is a scrolldrag + if (STBTE__IS_HOT(id) && stbte__ui.scrollkey && stbte__ui.event == STBTE__leftdown) { + stbte__ui.scrolling = 1; + stbte__ui.start_x = stbte__ui.mx; + stbte__ui.start_y = stbte__ui.my; + return; + } + + switch (tool) { + case STBTE__tool_brush: + switch (stbte__ui.event) { + case STBTE__mousemove: + if (STBTE__IS_MAP_ACTIVE() && over) { + // don't brush/erase same tile multiple times unless they move away and back @TODO should just be only once, but that needs another data structure + if (!STBTE__IS_ACTIVE(id)) { + if (stbte__ui.active_event == STBTE__leftdown) + stbte__brush(tm, mapx, mapy); + else + stbte__erase(tm, mapx, mapy, stbte__ui.brush_state); + stbte__ui.active_id = id; // switch to this map square so we don't rebrush IT multiple times + } + } + break; + case STBTE__leftdown: + if (STBTE__IS_HOT(id) && STBTE__INACTIVE()) { + stbte__activate(id); + stbte__begin_undo(tm); + stbte__brush(tm, mapx, mapy); + } + break; + case STBTE__rightdown: + if (STBTE__IS_HOT(id) && STBTE__INACTIVE()) { + stbte__activate(id); + stbte__begin_undo(tm); + if (stbte__erase(tm, mapx, mapy, STBTE__erase_any) == STBTE__erase_brushonly) + stbte__ui.brush_state = STBTE__erase_brushonly; + else + stbte__ui.brush_state = STBTE__erase_any; + } + break; + case STBTE__leftup: + case STBTE__rightup: + if (STBTE__IS_MAP_ACTIVE()) { + stbte__end_undo(tm); + stbte__activate(0); + } + break; + } + break; + +#ifdef STBTE_ALLOW_LINK + case STBTE__tool_link: + switch (stbte__ui.event) { + case STBTE__leftdown: + if (STBTE__IS_HOT(id) && STBTE__INACTIVE()) { + stbte__activate(id); + stbte__ui.linking = 1; + stbte__ui.sx = mapx; + stbte__ui.sy = mapy; + // @TODO: undo + } + break; + case STBTE__leftup: + if (STBTE__IS_HOT(id) && STBTE__IS_MAP_ACTIVE()) { + if ((mapx != stbte__ui.sx || mapy != stbte__ui.sy) && + STBTE_ALLOW_LINK(tm->data[stbte__ui.sy][stbte__ui.sx], tm->props[stbte__ui.sy][stbte__ui.sx], + tm->data[mapy][mapx], tm->props[mapy][mapx])) + stbte__set_link(tm, stbte__ui.sx, stbte__ui.sy, mapx, mapy, STBTE__undo_block); + else + stbte__set_link(tm, stbte__ui.sx, stbte__ui.sy, -1,-1, STBTE__undo_block); + stbte__ui.linking = 0; + stbte__activate(0); + } + break; + + case STBTE__rightdown: + if (STBTE__IS_ACTIVE(id)) { + stbte__activate(0); + stbte__ui.linking = 0; + } + break; + } + break; +#endif + + case STBTE__tool_erase: + switch (stbte__ui.event) { + case STBTE__mousemove: + if (STBTE__IS_MAP_ACTIVE() && over) + stbte__erase(tm, mapx, mapy, STBTE__erase_all); + break; + case STBTE__leftdown: + if (STBTE__IS_HOT(id) && STBTE__INACTIVE()) { + stbte__activate(id); + stbte__begin_undo(tm); + stbte__erase(tm, mapx, mapy, STBTE__erase_all); + } + break; + case STBTE__leftup: + if (STBTE__IS_MAP_ACTIVE()) { + stbte__end_undo(tm); + stbte__activate(0); + } + break; + } + break; + + case STBTE__tool_select: + if (STBTE__IS_HOT(id)) { + switch (stbte__ui.event) { + case STBTE__leftdown: + if (STBTE__INACTIVE()) { + // if we're clicking in an existing selection... + if (stbte__ui.has_selection) { + if ( mapx >= stbte__ui.select_x0 && mapx <= stbte__ui.select_x1 + && mapy >= stbte__ui.select_y0 && mapy <= stbte__ui.select_y1) + { + stbte__ui.dragging = 1; + stbte__ui.drag_x = stbte__ui.select_x0; + stbte__ui.drag_y = stbte__ui.select_y0; + stbte__ui.drag_w = stbte__ui.select_x1 - stbte__ui.select_x0 + 1; + stbte__ui.drag_h = stbte__ui.select_y1 - stbte__ui.select_y0 + 1; + stbte__ui.drag_offx = mapx - stbte__ui.select_x0; + stbte__ui.drag_offy = mapy - stbte__ui.select_y0; + } + } + stbte__ui.has_selection = 0; // no selection until it completes + stbte__activate_map(mapx,mapy); + } + break; + case STBTE__leftup: + if (STBTE__IS_MAP_ACTIVE()) { + if (stbte__ui.dragging) { + stbte__drag_place(tm, mapx,mapy); + stbte__ui.dragging = 0; + stbte__activate(0); + } else { + stbte__select_rect(tm, stbte__ui.sx, stbte__ui.sy, mapx, mapy); + stbte__activate(0); + } + } + break; + case STBTE__rightdown: + stbte__ui.has_selection = 0; + break; + } + } + break; + + case STBTE__tool_rect: + if (STBTE__IS_HOT(id)) { + switch (stbte__ui.event) { + case STBTE__leftdown: + if (STBTE__INACTIVE()) + stbte__activate_map(mapx,mapy); + break; + case STBTE__leftup: + if (STBTE__IS_MAP_ACTIVE()) { + stbte__fillrect(tm, stbte__ui.sx, stbte__ui.sy, mapx, mapy, 1); + stbte__activate(0); + } + break; + case STBTE__rightdown: + if (STBTE__INACTIVE()) + stbte__activate_map(mapx,mapy); + break; + case STBTE__rightup: + if (STBTE__IS_MAP_ACTIVE()) { + stbte__fillrect(tm, stbte__ui.sx, stbte__ui.sy, mapx, mapy, 0); + stbte__activate(0); + } + break; + } + } + break; + + + case STBTE__tool_eyedrop: + switch (stbte__ui.event) { + case STBTE__leftdown: + if (STBTE__IS_HOT(id) && STBTE__INACTIVE()) + stbte__eyedrop(tm,mapx,mapy); + break; + } + break; + } +} + +static void stbte__start_paste(stbte_tilemap *tm) +{ + if (stbte__ui.has_copy) { + stbte__ui.pasting = 1; + stbte__activate(STBTE__ID(STBTE__toolbarB,3)); + } +} + +static void stbte__toolbar(stbte_tilemap *tm, int x0, int y0, int w, int h) +{ + int i; + int estimated_width = 13 * STBTE__num_tool + 8+8+ 120+4 - 30; + int x = x0 + w/2 - estimated_width/2; + int y = y0+1; + + for (i=0; i < STBTE__num_tool; ++i) { + int highlight=0, disable=0; + highlight = (stbte__ui.tool == i); + if (i == STBTE__tool_undo || i == STBTE__tool_showgrid) + x += 8; + if (i == STBTE__tool_showgrid && stbte__ui.show_grid) + highlight = 1; + if (i == STBTE__tool_showlinks && stbte__ui.show_links) + highlight = 1; + if (i == STBTE__tool_fill) + continue; + #ifndef STBTE_ALLOW_LINK + if (i == STBTE__tool_link || i == STBTE__tool_showlinks) + disable = 1; + #endif + if (i == STBTE__tool_undo && !stbte__undo_available(tm)) + disable = 1; + if (i == STBTE__tool_redo && !stbte__redo_available(tm)) + disable = 1; + if (stbte__button_icon(STBTE__ctoolbar_button, toolchar[i], x, y, 13, STBTE__ID(STBTE__toolbarA, i), highlight, disable)) { + switch (i) { + case STBTE__tool_eyedrop: + stbte__ui.eyedrop_last_layer = tm->num_layers; // flush eyedropper state + // fallthrough + default: + stbte__ui.tool = i; + stbte__ui.has_selection = 0; + break; + case STBTE__tool_showlinks: + stbte__ui.show_links = !stbte__ui.show_links; + break; + case STBTE__tool_showgrid: + stbte__ui.show_grid = (stbte__ui.show_grid+1)%3; + break; + case STBTE__tool_undo: + stbte__undo(tm); + break; + case STBTE__tool_redo: + stbte__redo(tm); + break; + } + } + x += 13; + } + + x += 8; + if (stbte__button(STBTE__ctoolbar_button, "cut" , x, y,10, 40, STBTE__ID(STBTE__toolbarB,0), 0, !stbte__ui.has_selection)) + stbte__copy_cut(tm, 1); + x += 42; + if (stbte__button(STBTE__ctoolbar_button, "copy" , x, y, 5, 40, STBTE__ID(STBTE__toolbarB,1), 0, !stbte__ui.has_selection)) + stbte__copy_cut(tm, 0); + x += 42; + if (stbte__button(STBTE__ctoolbar_button, "paste", x, y, 0, 40, STBTE__ID(STBTE__toolbarB,2), stbte__ui.pasting, !stbte__ui.has_copy)) + stbte__start_paste(tm); +} + +#define STBTE__TEXTCOLOR(n) stbte__color_table[n][STBTE__text][STBTE__idle] + +static int stbte__info_value(char *label, int x, int y, int val, int digits, int id) +{ + if (stbte__ui.event == STBTE__paint) { + int off = 9-stbte__get_char_width(label[0]); + char text[16]; + sprintf(text, label, digits, val); + stbte__draw_text_core(x+off,y, text, 999, STBTE__TEXTCOLOR(STBTE__cpanel),1); + } + if (id) { + x += 9+7*digits+4; + if (stbte__minibutton(STBTE__cmapsize, x,y, '+', STBTE__ID2(id,1,0))) + val += (stbte__ui.shift ? 10 : 1); + x += 9; + if (stbte__minibutton(STBTE__cmapsize, x,y, '-', STBTE__ID2(id,2,0))) + val -= (stbte__ui.shift ? 10 : 1); + if (val < 1) val = 1; else if (val > 4096) val = 4096; + } + return val; +} + +static void stbte__info(stbte_tilemap *tm, int x0, int y0, int w, int h) +{ + int mode = stbte__ui.panel[STBTE__panel_info].mode; + int s = 11+7*tm->digits+4+15; + int x,y; + int in_region; + + x = x0+2; + y = y0+2; + tm->max_x = stbte__info_value("w:%*d",x,y, tm->max_x, tm->digits, STBTE__ID(STBTE__info,0)); + if (mode) + x += s; + else + y += 11; + tm->max_y = stbte__info_value("h:%*d",x,y, tm->max_y, tm->digits, STBTE__ID(STBTE__info,1)); + x = x0+2; + y += 11; + in_region = (stbte__ui.hot_id & 127) == STBTE__map; + stbte__info_value(in_region ? "x:%*d" : "x:",x,y, (stbte__ui.hot_id>>19)&4095, tm->digits, 0); + if (mode) + x += s; + else + y += 11; + stbte__info_value(in_region ? "y:%*d" : "y:",x,y, (stbte__ui.hot_id>> 7)&4095, tm->digits, 0); + y += 15; + x = x0+2; + stbte__draw_text(x,y,"brush:",40,STBTE__TEXTCOLOR(STBTE__cpanel)); + if (tm->cur_tile >= 0) + STBTE_DRAW_TILE(x+43,y-3,tm->tiles[tm->cur_tile].id,1,0); +} + +static void stbte__layers(stbte_tilemap *tm, int x0, int y0, int w, int h) +{ + static char *propmodes[3] = { + "default", "always", "never" + }; + int num_rows; + int i, y, n; + int x1 = x0+w; + int y1 = y0+h; + int xoff = 20; + + if (tm->has_layer_names) { + int side = stbte__ui.panel[STBTE__panel_layers].side; + xoff = stbte__region[side].width - 42; + xoff = (xoff < tm->layername_width + 10 ? xoff : tm->layername_width + 10); + } + + x0 += 2; + y0 += 5; + if (!tm->has_layer_names) { + if (stbte__ui.event == STBTE__paint) { + stbte__draw_text(x0,y0, "Layers", w-4, STBTE__TEXTCOLOR(STBTE__cpanel)); + } + y0 += 11; + } + num_rows = (y1-y0)/15; +#ifndef STBTE_NO_PROPS + --num_rows; +#endif + y = y0; + for (i=0; i < tm->num_layers; ++i) { + char text[3], *str = (char *) tm->layerinfo[i].name; + static char lockedchar[3] = { 'U', 'P', 'L' }; + int locked = tm->layerinfo[i].locked; + int disabled = (tm->solo_layer >= 0 && tm->solo_layer != i); + if (i-tm->layer_scroll >= 0 && i-tm->layer_scroll < num_rows) { + if (str == NULL) + sprintf(str=text, "%2d", i+1); + if (stbte__button(STBTE__clayer_button, str, x0,y,(i+1<10)*2,xoff-2, STBTE__ID(STBTE__layer,i), tm->cur_layer==i,0)) + tm->cur_layer = (tm->cur_layer == i ? -1 : i); + if (stbte__layerbutton(x0+xoff + 0,y+1,'H',STBTE__ID(STBTE__hide,i), tm->layerinfo[i].hidden,disabled,STBTE__clayer_hide)) + tm->layerinfo[i].hidden = !tm->layerinfo[i].hidden; + if (stbte__layerbutton(x0+xoff + 12,y+1,lockedchar[locked],STBTE__ID(STBTE__lock,i), locked!=0,disabled,STBTE__clayer_lock)) + tm->layerinfo[i].locked = (locked+1)%3; + if (stbte__layerbutton(x0+xoff + 24,y+1,'S',STBTE__ID(STBTE__solo,i), tm->solo_layer==i,0,STBTE__clayer_solo)) + tm->solo_layer = (tm->solo_layer == i ? -1 : i); + y += 15; + } + } + stbte__scrollbar(x1-4, y0,y-2, &tm->layer_scroll, 0, tm->num_layers, num_rows, STBTE__ID(STBTE__scrollbar_id, STBTE__layer)); +#ifndef STBTE_NO_PROPS + n = stbte__text_width("prop:")+2; + stbte__draw_text(x0,y+2, "prop:", w, STBTE__TEXTCOLOR(STBTE__cpanel)); + i = w - n - 4; + if (i > 50) i = 50; + if (stbte__button(STBTE__clayer_button, propmodes[tm->propmode], x0+n,y,0,i, STBTE__ID(STBTE__layer,256), 0,0)) + tm->propmode = (tm->propmode+1)%3; +#endif +} + +static void stbte__categories(stbte_tilemap *tm, int x0, int y0, int w, int h) +{ + int s=11, x,y, i; + int num_rows = h / s; + + w -= 4; + x = x0+2; + y = y0+4; + if (tm->category_scroll == 0) { + if (stbte__category_button("*ALL*", x,y, w, STBTE__ID(STBTE__categories, 65535), tm->cur_category == -1)) { + stbte__choose_category(tm, -1); + } + y += s; + } + + for (i=0; i < tm->num_categories; ++i) { + if (i+1 - tm->category_scroll >= 0 && i+1 - tm->category_scroll < num_rows) { + if (y + 10 > y0+h) + return; + if (stbte__category_button(tm->categories[i], x,y,w, STBTE__ID(STBTE__categories,i), tm->cur_category == i)) + stbte__choose_category(tm, i); + y += s; + } + } + stbte__scrollbar(x0+w, y0+4, y0+h-4, &tm->category_scroll, 0, tm->num_categories+1, num_rows, STBTE__ID(STBTE__scrollbar_id, STBTE__categories)); +} + +static void stbte__tile_in_palette(stbte_tilemap *tm, int x, int y, int slot) +{ + stbte__tileinfo *t = &tm->tiles[slot]; + int x0=x, y0=y, x1 = x+tm->palette_spacing_x - 1, y1 = y+tm->palette_spacing_y; + int id = STBTE__ID(STBTE__palette, slot); + int over = stbte__hittest(x0,y0,x1,y1, id); + switch (stbte__ui.event) { + case STBTE__paint: + stbte__draw_rect(x,y,x+tm->palette_spacing_x-1,y+tm->palette_spacing_x-1, STBTE_COLOR_TILEPALETTE_BACKGROUND); + STBTE_DRAW_TILE(x,y,t->id, slot == tm->cur_tile,0); + if (slot == tm->cur_tile) + stbte__draw_frame_delayed(x-1,y-1,x+tm->palette_spacing_x,y+tm->palette_spacing_y, STBTE_COLOR_TILEPALETTE_OUTLINE); + break; + default: + if (stbte__button_core(id)) + tm->cur_tile = slot; + break; + } +} + +static void stbte__palette_of_tiles(stbte_tilemap *tm, int x0, int y0, int w, int h) +{ + int i,x,y; + int num_vis_rows = (h-6) / tm->palette_spacing_y; + int num_columns = (w-2-6) / tm->palette_spacing_x; + int num_total_rows; + int column,row; + int x1 = x0+w, y1=y0+h; + x = x0+2; + y = y0+6; + + if (num_columns == 0) + return; + + num_total_rows = (tm->cur_palette_count + num_columns-1) / num_columns; // ceil() + + column = 0; + row = -tm->palette_scroll; + for (i=0; i < tm->num_tiles; ++i) { + stbte__tileinfo *t = &tm->tiles[i]; + + // filter based on category + if (tm->cur_category >= 0 && t->category_id != tm->cur_category) + continue; + + // display it + if (row >= 0 && row < num_vis_rows) { + x = x0 + 2 + tm->palette_spacing_x * column; + y = y0 + 6 + tm->palette_spacing_y * row; + stbte__tile_in_palette(tm,x,y,i); + } + + ++column; + if (column == num_columns) { + column = 0; + ++row; + } + } + stbte__flush_delay(); + stbte__scrollbar(x1-4, y0+6, y1-2, &tm->palette_scroll, 0, num_total_rows, num_vis_rows, STBTE__ID(STBTE__scrollbar_id, STBTE__palette)); +} + +static float stbte__linear_remap(float n, float x0, float x1, float y0, float y1) +{ + return (n-x0)/(x1-x0)*(y1-y0) + y0; +} + +static float stbte__saved; +static void stbte__props_panel(stbte_tilemap *tm, int x0, int y0, int w, int h) +{ + int x1 = x0+w, y1 = y0+h; + int i; + int y = y0 + 5, x = x0+2; + int slider_width = 60; + int mx,my; + float *p; + short *data; + if (!stbte__is_single_selection()) + return; + mx = stbte__ui.select_x0; + my = stbte__ui.select_y0; + p = tm->props[my][mx]; + data = tm->data[my][mx]; + for (i=0; i < STBTE_MAX_PROPERTIES; ++i) { + unsigned int n = STBTE_PROP_TYPE(i, data, p); + if (n) { + char *s = STBTE_PROP_NAME(i, data, p); + if (s == NULL) s = ""; + switch (n & 3) { + case STBTE_PROP_bool: { + int flag = (int) p[i]; + if (stbte__layerbutton(x,y, flag ? 'x' : ' ', STBTE__ID(STBTE__prop_flag,i), flag, 0, 2)) { + stbte__begin_undo(tm); + stbte__undo_record_prop_float(tm,mx,my,i,(float) flag); + p[i] = (float) !flag; + stbte__end_undo(tm); + } + stbte__draw_text(x+13,y+1,s,x1-(x+13)-2,STBTE__TEXTCOLOR(STBTE__cpanel)); + y += 13; + break; + } + case STBTE_PROP_int: { + int a = (int) STBTE_PROP_MIN(i,data,p); + int b = (int) STBTE_PROP_MAX(i,data,p); + int v = (int) p[i] - a; + if (a+v != p[i] || v < 0 || v > b-a) { + if (v < 0) v = 0; + if (v > b-a) v = b-a; + p[i] = (float) (a+v); // @TODO undo + } + switch (stbte__slider(x, slider_width, y+7, b-a, &v, STBTE__ID(STBTE__prop_int,i))) + { + case STBTE__begin: + stbte__saved = p[i]; + // fallthrough + case STBTE__change: + p[i] = (float) (a+v); // @TODO undo + break; + case STBTE__end: + if (p[i] != stbte__saved) { + stbte__begin_undo(tm); + stbte__undo_record_prop_float(tm,mx,my,i,stbte__saved); + stbte__end_undo(tm); + } + break; + } + stbte__draw_text(x+slider_width+2,y+2, s, x1-1-(x+slider_width+2), STBTE__TEXTCOLOR(STBTE__cpanel)); + y += 12; + break; + } + case STBTE_PROP_float: { + float a = (float) STBTE_PROP_MIN(i, data,p); + float b = (float) STBTE_PROP_MAX(i, data,p); + float c = STBTE_PROP_FLOAT_SCALE(i, data, p); + float old; + if (p[i] < a || p[i] > b) { + // @TODO undo + if (p[i] < a) p[i] = a; + if (p[i] > b) p[i] = b; + } + old = p[i]; + switch (stbte__float_control(x, y, 50, a, b, c, "%8.4f", &p[i], STBTE__layer,STBTE__ID(STBTE__prop_float,i))) { + case STBTE__begin: + stbte__saved = old; + break; + case STBTE__end: + if (stbte__saved != p[i]) { + stbte__begin_undo(tm); + stbte__undo_record_prop_float(tm,mx,my,i, stbte__saved); + stbte__end_undo(tm); + } + break; + } + stbte__draw_text(x+53,y+1, s, x1-1-(x+53), STBTE__TEXTCOLOR(STBTE__cpanel)); + y += 12; + break; + } + } + } + } +} + +static int stbte__cp_mode, stbte__cp_aspect, stbte__cp_state, stbte__cp_index, stbte__save, stbte__cp_altered, stbte__color_copy; +#ifdef STBTE__COLORPICKER +static void stbte__dump_colorstate(void) +{ + int i,j,k; + printf("static int stbte__color_table[STBTE__num_color_modes][STBTE__num_color_aspects][STBTE__num_color_states] =\n"); + printf("{\n"); + printf(" {\n"); + for (k=0; k < STBTE__num_color_modes; ++k) { + for (j=0; j < STBTE__num_color_aspects; ++j) { + printf(" { "); + for (i=0; i < STBTE__num_color_states; ++i) { + printf("0x%06x, ", stbte__color_table[k][j][i]); + } + printf("},\n"); + } + if (k+1 < STBTE__num_color_modes) + printf(" }, {\n"); + else + printf(" },\n"); + } + printf("};\n"); +} + +static void stbte__colorpicker(int x0, int y0, int w, int h) +{ + int x1 = x0+w, y1 = y0+h, x,y, i; + + x = x0+2; y = y0+6; + + y += 5; + x += 8; + + + { + int color = stbte__color_table[stbte__cp_mode][stbte__cp_aspect][stbte__cp_index]; + int rgb[3]; + if (stbte__cp_altered && stbte__cp_index == STBTE__idle) + color = stbte__save; + + if (stbte__minibutton(STBTE__cmapsize, x1-20,y+ 5, 'C', STBTE__ID2(STBTE__colorpick_id,4,0))) + stbte__color_copy = color; + if (stbte__minibutton(STBTE__cmapsize, x1-20,y+15, 'P', STBTE__ID2(STBTE__colorpick_id,4,1))) + color = stbte__color_copy; + + rgb[0] = color >> 16; rgb[1] = (color>>8)&255; rgb[2] = color & 255; + for (i=0; i < 3; ++i) { + if (stbte__slider(x+8,64, y, 255, rgb+i, STBTE__ID2(STBTE__colorpick_id,3,i)) > 0) + stbte__dump_colorstate(); + y += 15; + } + if (stbte__ui.event != STBTE__paint && stbte__ui.event != STBTE__tick) + stbte__color_table[stbte__cp_mode][stbte__cp_aspect][stbte__cp_index] = (rgb[0]<<16)|(rgb[1]<<8)|(rgb[2]); + } + + y += 5; + + // states + x = x0+2+35; + if (stbte__ui.event == STBTE__paint) { + static char *states[] = { "idle", "over", "down", "down&over", "selected", "selected&over", "disabled" }; + stbte__draw_text(x, y+1, states[stbte__cp_index], x1-x-1, 0xffffff); + } + + x = x0+24; y += 12; + + for (i=3; i >= 0; --i) { + int state = 0 != (stbte__cp_state & (1 << i)); + if (stbte__layerbutton(x,y, "OASD"[i], STBTE__ID2(STBTE__colorpick_id, 0,i), state,0, STBTE__clayer_button)) { + stbte__cp_state ^= (1 << i); + stbte__cp_index = stbte__state_to_index[0][0][0][stbte__cp_state]; + } + x += 16; + } + x = x0+2; y += 18; + + for (i=0; i < 3; ++i) { + static char *labels[] = { "Base", "Edge", "Text" }; + if (stbte__button(STBTE__ctoolbar_button, labels[i], x,y,0,36, STBTE__ID2(STBTE__colorpick_id,1,i), stbte__cp_aspect==i,0)) + stbte__cp_aspect = i; + x += 40; + } + + y += 18; + x = x0+2; + + for (i=0; i < STBTE__num_color_modes; ++i) { + if (stbte__button(STBTE__ctoolbar_button, stbte__color_names[i], x, y, 0,80, STBTE__ID2(STBTE__colorpick_id,2,i), stbte__cp_mode == i,0)) + stbte__cp_mode = i; + y += 12; + } + + // make the currently selected aspect flash, unless we're actively dragging color slider etc + if (stbte__ui.event == STBTE__tick) { + stbte__save = stbte__color_table[stbte__cp_mode][stbte__cp_aspect][STBTE__idle]; + if ((stbte__ui.active_id & 127) != STBTE__colorpick_id) { + if ((stbte__ui.ms_time & 2047) < 200) { + stbte__color_table[stbte__cp_mode][stbte__cp_aspect][STBTE__idle] ^= 0x1f1f1f; + stbte__cp_altered = 1; + } + } + } +} +#endif + +static void stbte__editor_traverse(stbte_tilemap *tm) +{ + int i,j,i0,j0,i1,j1,n; + + if (tm == NULL) + return; + if (stbte__ui.x0 == stbte__ui.x1 || stbte__ui.y0 == stbte__ui.y1) + return; + + stbte__prepare_tileinfo(tm); + + stbte__compute_panel_locations(tm); // @OPTIMIZE: we don't need to recompute this every time + + if (stbte__ui.event == STBTE__paint) { + // fill screen with border + stbte__draw_rect(stbte__ui.x0, stbte__ui.y0, stbte__ui.x1, stbte__ui.y1, STBTE_COLOR_TILEMAP_BORDER); + // fill tilemap with tilemap background + stbte__draw_rect(stbte__ui.x0 - tm->scroll_x, stbte__ui.y0 - tm->scroll_y, + stbte__ui.x0 - tm->scroll_x + tm->spacing_x * tm->max_x, + stbte__ui.y0 - tm->scroll_y + tm->spacing_y * tm->max_y, STBTE_COLOR_TILEMAP_BACKGROUND); + } + + // step 1: traverse all the tilemap data... + + i0 = (tm->scroll_x - tm->spacing_x) / tm->spacing_x; + j0 = (tm->scroll_y - tm->spacing_y) / tm->spacing_y; + i1 = (tm->scroll_x + stbte__ui.x1 - stbte__ui.x0) / tm->spacing_x + 1; + j1 = (tm->scroll_y + stbte__ui.y1 - stbte__ui.y0) / tm->spacing_y + 1; + + if (i0 < 0) i0 = 0; + if (j0 < 0) j0 = 0; + if (i1 > tm->max_x) i1 = tm->max_x; + if (j1 > tm->max_y) j1 = tm->max_y; + + if (stbte__ui.event == STBTE__paint) { + // draw all of layer 0, then all of layer 1, etc, instead of old + // way which drew entire stack of each tile at once + for (n=0; n < tm->num_layers; ++n) { + for (j=j0; j < j1; ++j) { + for (i=i0; i < i1; ++i) { + int x = stbte__ui.x0 + i * tm->spacing_x - tm->scroll_x; + int y = stbte__ui.y0 + j * tm->spacing_y - tm->scroll_y; + stbte__tile_paint(tm, x, y, i, j, n); + } + } + if (n == 0 && stbte__ui.show_grid == 1) { + int x = stbte__ui.x0 + i0 * tm->spacing_x - tm->scroll_x; + int y = stbte__ui.y0 + j0 * tm->spacing_y - tm->scroll_y; + for (i=0; x < stbte__ui.x1 && i <= i1; ++i, x += tm->spacing_x) + stbte__draw_rect(x, stbte__ui.y0, x+1, stbte__ui.y1, STBTE_COLOR_GRID); + for (j=0; y < stbte__ui.y1 && j <= j1; ++j, y += tm->spacing_y) + stbte__draw_rect(stbte__ui.x0, y, stbte__ui.x1, y+1, STBTE_COLOR_GRID); + } + } + } + + if (stbte__ui.event == STBTE__paint) { + // draw grid on top of everything except UI + if (stbte__ui.show_grid == 2) { + int x = stbte__ui.x0 + i0 * tm->spacing_x - tm->scroll_x; + int y = stbte__ui.y0 + j0 * tm->spacing_y - tm->scroll_y; + for (i=0; x < stbte__ui.x1 && i <= i1; ++i, x += tm->spacing_x) + stbte__draw_rect(x, stbte__ui.y0, x+1, stbte__ui.y1, STBTE_COLOR_GRID); + for (j=0; y < stbte__ui.y1 && j <= j1; ++j, y += tm->spacing_y) + stbte__draw_rect(stbte__ui.x0, y, stbte__ui.x1, y+1, STBTE_COLOR_GRID); + } + } + + for (j=j0; j < j1; ++j) { + for (i=i0; i < i1; ++i) { + int x = stbte__ui.x0 + i * tm->spacing_x - tm->scroll_x; + int y = stbte__ui.y0 + j * tm->spacing_y - tm->scroll_y; + stbte__tile(tm, x, y, i, j); + } + } + + if (stbte__ui.event == STBTE__paint) { + // draw the selection border + if (stbte__ui.has_selection) { + int x0,y0,x1,y1; + x0 = stbte__ui.x0 + (stbte__ui.select_x0 ) * tm->spacing_x - tm->scroll_x; + y0 = stbte__ui.y0 + (stbte__ui.select_y0 ) * tm->spacing_y - tm->scroll_y; + x1 = stbte__ui.x0 + (stbte__ui.select_x1 + 1) * tm->spacing_x - tm->scroll_x + 1; + y1 = stbte__ui.y0 + (stbte__ui.select_y1 + 1) * tm->spacing_y - tm->scroll_y + 1; + stbte__draw_frame(x0,y0,x1,y1, (stbte__ui.ms_time & 256 ? STBTE_COLOR_SELECTION_OUTLINE1 : STBTE_COLOR_SELECTION_OUTLINE2)); + } + + stbte__flush_delay(); // draw a dynamic link on top of the queued links + + #ifdef STBTE_ALLOW_LINK + if (stbte__ui.linking && STBTE__IS_MAP_HOT()) { + int x0,y0,x1,y1; + int color; + int ex = ((stbte__ui.hot_id >> 19) & 4095); + int ey = ((stbte__ui.hot_id >> 7) & 4095); + x0 = stbte__ui.x0 + (stbte__ui.sx ) * tm->spacing_x - tm->scroll_x + (tm->spacing_x>>1)+1; + y0 = stbte__ui.y0 + (stbte__ui.sy ) * tm->spacing_y - tm->scroll_y + (tm->spacing_y>>1)+1; + x1 = stbte__ui.x0 + (ex ) * tm->spacing_x - tm->scroll_x + (tm->spacing_x>>1)-1; + y1 = stbte__ui.y0 + (ey ) * tm->spacing_y - tm->scroll_y + (tm->spacing_y>>1)-1; + if (STBTE_ALLOW_LINK(tm->data[stbte__ui.sy][stbte__ui.sx], tm->props[stbte__ui.sy][stbte__ui.sx], tm->data[ey][ex], tm->props[ey][ex])) + color = STBTE_LINK_COLOR_DRAWING; + else + color = STBTE_LINK_COLOR_DISALLOWED; + stbte__draw_link(x0,y0,x1,y1, color); + } + #endif + } + stbte__flush_delay(); + + // step 2: traverse the panels + for (i=0; i < STBTE__num_panel; ++i) { + stbte__panel *p = &stbte__ui.panel[i]; + if (stbte__ui.event == STBTE__paint) { + stbte__draw_box(p->x0,p->y0,p->x0+p->width,p->y0+p->height, STBTE__cpanel, STBTE__idle); + } + // obscure tilemap data underneath panel + stbte__hittest(p->x0,p->y0,p->x0+p->width,p->y0+p->height, STBTE__ID2(STBTE__panel, i, 0)); + switch (i) { + case STBTE__panel_toolbar: + if (stbte__ui.event == STBTE__paint) + stbte__draw_rect(p->x0,p->y0,p->x0+p->width,p->y0+p->height, stbte__color_table[STBTE__ctoolbar][STBTE__base][STBTE__idle]); + stbte__toolbar(tm,p->x0,p->y0,p->width,p->height); + break; + case STBTE__panel_info: + stbte__info(tm,p->x0,p->y0,p->width,p->height); + break; + case STBTE__panel_layers: + stbte__layers(tm,p->x0,p->y0,p->width,p->height); + break; + case STBTE__panel_categories: + stbte__categories(tm,p->x0,p->y0,p->width,p->height); + break; + case STBTE__panel_colorpick: +#ifdef STBTE__COLORPICKER + stbte__colorpicker(p->x0,p->y0,p->width,p->height); +#endif + break; + case STBTE__panel_tiles: + // erase boundary between categories and tiles if they're on same side + if (stbte__ui.event == STBTE__paint && p->side == stbte__ui.panel[STBTE__panel_categories].side) + stbte__draw_rect(p->x0+1,p->y0-1,p->x0+p->width-1,p->y0+1, stbte__color_table[STBTE__cpanel][STBTE__base][STBTE__idle]); + stbte__palette_of_tiles(tm,p->x0,p->y0,p->width,p->height); + break; + case STBTE__panel_props: + stbte__props_panel(tm,p->x0,p->y0,p->width,p->height); + break; + } + // draw the panel side selectors + for (j=0; j < 2; ++j) { + int result; + if (i == STBTE__panel_toolbar) continue; + result = stbte__microbutton(p->x0+p->width - 1 - 2*4 + 4*j,p->y0+2,3, STBTE__ID2(STBTE__panel, i, j+1), STBTE__cpanel_sider+j); + if (result) { + switch (j) { + case 0: p->side = result > 0 ? STBTE__side_left : STBTE__side_right; break; + case 1: p->delta_height += result; break; + } + } + } + } + + if (stbte__ui.panel[STBTE__panel_categories].delta_height < -5) stbte__ui.panel[STBTE__panel_categories].delta_height = -5; + if (stbte__ui.panel[STBTE__panel_layers ].delta_height < -5) stbte__ui.panel[STBTE__panel_layers ].delta_height = -5; + + + // step 3: traverse the regions to place expander controls on them + for (i=0; i < 2; ++i) { + if (stbte__region[i].active) { + int x = stbte__region[i].x; + int width; + if (i == STBTE__side_left) + width = stbte__ui.left_width , x += stbte__region[i].width + 1; + else + width = -stbte__ui.right_width, x -= 6; + if (stbte__microbutton_dragger(x, stbte__region[i].y+2, 5, STBTE__ID(STBTE__region,i), &width)) { + // if non-0, it is expanding, so retract it + if (stbte__region[i].retracted == 0.0) + stbte__region[i].retracted = 0.01f; + else + stbte__region[i].retracted = 0.0; + } + if (i == STBTE__side_left) + stbte__ui.left_width = width; + else + stbte__ui.right_width = -width; + if (stbte__ui.event == STBTE__tick) { + if (stbte__region[i].retracted && stbte__region[i].retracted < 1.0f) { + stbte__region[i].retracted += stbte__ui.dt*4; + if (stbte__region[i].retracted > 1) + stbte__region[i].retracted = 1; + } + } + } + } + + if (stbte__ui.event == STBTE__paint && stbte__ui.alert_msg) { + int w = stbte__text_width(stbte__ui.alert_msg); + int x = (stbte__ui.x0+stbte__ui.x1)/2; + int y = (stbte__ui.y0+stbte__ui.y1)*5/6; + stbte__draw_rect (x-w/2-4,y-8, x+w/2+4,y+8, 0x604020); + stbte__draw_frame(x-w/2-4,y-8, x+w/2+4,y+8, 0x906030); + stbte__draw_text (x-w/2,y-4, stbte__ui.alert_msg, w+1, 0xff8040); + } + +#ifdef STBTE_SHOW_CURSOR + if (stbte__ui.event == STBTE__paint) + stbte__draw_bitmap(stbte__ui.mx, stbte__ui.my, stbte__get_char_width(26), stbte__get_char_bitmap(26), 0xe0e0e0); +#endif + + if (stbte__ui.event == STBTE__tick && stbte__ui.alert_msg) { + stbte__ui.alert_timer -= stbte__ui.dt; + if (stbte__ui.alert_timer < 0) { + stbte__ui.alert_timer = 0; + stbte__ui.alert_msg = 0; + } + } + + if (stbte__ui.event == STBTE__paint) { + stbte__color_table[stbte__cp_mode][stbte__cp_aspect][STBTE__idle] = stbte__save; + stbte__cp_altered = 0; + } +} + +static void stbte__do_event(stbte_tilemap *tm) +{ + stbte__ui.next_hot_id = 0; + stbte__editor_traverse(tm); + stbte__ui.hot_id = stbte__ui.next_hot_id; + + // automatically cancel on mouse-up in case the object that triggered it + // doesn't exist anymore + if (stbte__ui.active_id) { + if (stbte__ui.event == STBTE__leftup || stbte__ui.event == STBTE__rightup) { + if (!stbte__ui.pasting) { + stbte__activate(0); + if (stbte__ui.undoing) + stbte__end_undo(tm); + stbte__ui.scrolling = 0; + stbte__ui.dragging = 0; + stbte__ui.linking = 0; + } + } + } + + // we could do this stuff in the widgets directly, but it would keep recomputing + // the same thing on every tile, which seems dumb. + + if (stbte__ui.pasting) { + if (STBTE__IS_MAP_HOT()) { + // compute pasting location based on last hot + stbte__ui.paste_x = ((stbte__ui.hot_id >> 19) & 4095) - (stbte__ui.copy_width >> 1); + stbte__ui.paste_y = ((stbte__ui.hot_id >> 7) & 4095) - (stbte__ui.copy_height >> 1); + } + } + if (stbte__ui.dragging) { + if (STBTE__IS_MAP_HOT()) { + stbte__ui.drag_dest_x = ((stbte__ui.hot_id >> 19) & 4095) - stbte__ui.drag_offx; + stbte__ui.drag_dest_y = ((stbte__ui.hot_id >> 7) & 4095) - stbte__ui.drag_offy; + } + } +} + +static void stbte__set_event(int event, int x, int y) +{ + stbte__ui.event = event; + stbte__ui.mx = x; + stbte__ui.my = y; + stbte__ui.dx = x - stbte__ui.last_mouse_x; + stbte__ui.dy = y - stbte__ui.last_mouse_y; + stbte__ui.last_mouse_x = x; + stbte__ui.last_mouse_y = y; + stbte__ui.accum_x += stbte__ui.dx; + stbte__ui.accum_y += stbte__ui.dy; +} + +void stbte_draw(stbte_tilemap *tm) +{ + stbte__ui.event = STBTE__paint; + stbte__editor_traverse(tm); +} + +void stbte_mouse_move(stbte_tilemap *tm, int x, int y, int shifted, int scrollkey) +{ + stbte__set_event(STBTE__mousemove, x,y); + stbte__ui.shift = shifted; + stbte__ui.scrollkey = scrollkey; + stbte__do_event(tm); +} + +void stbte_mouse_button(stbte_tilemap *tm, int x, int y, int right, int down, int shifted, int scrollkey) +{ + static int events[2][2] = { { STBTE__leftup , STBTE__leftdown }, + { STBTE__rightup, STBTE__rightdown } }; + stbte__set_event(events[right][down], x,y); + stbte__ui.shift = shifted; + stbte__ui.scrollkey = scrollkey; + + stbte__do_event(tm); +} + +void stbte_mouse_wheel(stbte_tilemap *tm, int x, int y, int vscroll) +{ + // not implemented yet -- need different way of hittesting +} + +void stbte_action(stbte_tilemap *tm, enum stbte_action act) +{ + switch (act) { + case STBTE_tool_select: stbte__ui.tool = STBTE__tool_select; break; + case STBTE_tool_brush: stbte__ui.tool = STBTE__tool_brush; break; + case STBTE_tool_erase: stbte__ui.tool = STBTE__tool_erase; break; + case STBTE_tool_rectangle: stbte__ui.tool = STBTE__tool_rect; break; + case STBTE_tool_eyedropper: stbte__ui.tool = STBTE__tool_eyedrop; break; + case STBTE_tool_link: stbte__ui.tool = STBTE__tool_link; break; + case STBTE_act_toggle_grid: stbte__ui.show_grid = (stbte__ui.show_grid+1) % 3; break; + case STBTE_act_toggle_links: stbte__ui.show_links ^= 1; break; + case STBTE_act_undo: stbte__undo(tm); break; + case STBTE_act_redo: stbte__redo(tm); break; + case STBTE_act_cut: stbte__copy_cut(tm, 1); break; + case STBTE_act_copy: stbte__copy_cut(tm, 0); break; + case STBTE_act_paste: stbte__start_paste(tm); break; + case STBTE_scroll_left: tm->scroll_x -= tm->spacing_x; break; + case STBTE_scroll_right: tm->scroll_x += tm->spacing_x; break; + case STBTE_scroll_up: tm->scroll_y -= tm->spacing_y; break; + case STBTE_scroll_down: tm->scroll_y += tm->spacing_y; break; + } +} + +void stbte_tick(stbte_tilemap *tm, float dt) +{ + stbte__ui.event = STBTE__tick; + stbte__ui.dt = dt; + stbte__do_event(tm); + stbte__ui.ms_time += (int) (dt * 1024) + 1; // make sure if time is superfast it always updates a little +} + +void stbte_mouse_sdl(stbte_tilemap *tm, const void *sdl_event, float xs, float ys, int xo, int yo) +{ +#ifdef _SDL_H + SDL_Event *event = (SDL_Event *) sdl_event; + SDL_Keymod km = SDL_GetModState(); + int shift = (km & KMOD_LCTRL) || (km & KMOD_RCTRL); + int scrollkey = 0 != SDL_GetKeyboardState(NULL)[SDL_SCANCODE_SPACE]; + switch (event->type) { + case SDL_MOUSEMOTION: + stbte_mouse_move(tm, (int) (xs*event->motion.x+xo), (int) (ys*event->motion.y+yo), shift, scrollkey); + break; + case SDL_MOUSEBUTTONUP: + stbte_mouse_button(tm, (int) (xs*event->button.x+xo), (int) (ys*event->button.y+yo), event->button.button != SDL_BUTTON_LEFT, 0, shift, scrollkey); + break; + case SDL_MOUSEBUTTONDOWN: + stbte_mouse_button(tm, (int) (xs*event->button.x+xo), (int) (ys*event->button.y+yo), event->button.button != SDL_BUTTON_LEFT, 1, shift, scrollkey); + break; + case SDL_MOUSEWHEEL: + stbte_mouse_wheel(tm, stbte__ui.mx, stbte__ui.my, event->wheel.y); + break; + } +#else + STBTE__NOTUSED(tm); + STBTE__NOTUSED(sdl_event); + STBTE__NOTUSED(xs); + STBTE__NOTUSED(ys); + STBTE__NOTUSED(xo); + STBTE__NOTUSED(yo); +#endif +} + +#endif // STB_TILEMAP_EDITOR_IMPLEMENTATION diff --git a/impeller/third_party/stb/stb/stb_truetype.h b/impeller/third_party/stb/stb/stb_truetype.h new file mode 100644 index 0000000000000..d360d60920b30 --- /dev/null +++ b/impeller/third_party/stb/stb/stb_truetype.h @@ -0,0 +1,3267 @@ +// stb_truetype.h - v1.11 - public domain +// authored from 2009-2015 by Sean Barrett / RAD Game Tools +// +// This library processes TrueType files: +// parse files +// extract glyph metrics +// extract glyph shapes +// render glyphs to one-channel bitmaps with antialiasing (box filter) +// +// Todo: +// non-MS cmaps +// crashproof on bad data +// hinting? (no longer patented) +// cleartype-style AA? +// optimize: use simple memory allocator for intermediates +// optimize: build edge-list directly from curves +// optimize: rasterize directly from curves? +// +// ADDITIONAL CONTRIBUTORS +// +// Mikko Mononen: compound shape support, more cmap formats +// Tor Andersson: kerning, subpixel rendering +// +// Misc other: +// Ryan Gordon +// Simon Glass +// +// Bug/warning reports/fixes: +// "Zer" on mollyrocket (with fix) +// Cass Everitt +// stoiko (Haemimont Games) +// Brian Hook +// Walter van Niftrik +// David Gow +// David Given +// Ivan-Assen Ivanov +// Anthony Pesch +// Johan Duparc +// Hou Qiming +// Fabian "ryg" Giesen +// Martins Mozeiko +// Cap Petschulat +// Omar Cornut +// github:aloucks +// Peter LaValle +// Sergey Popov +// Giumo X. Clanjor +// Higor Euripedes +// Thomas Fields +// Derek Vinyard +// +// VERSION HISTORY +// +// 1.11 (2016-04-02) fix unused-variable warning +// 1.10 (2016-04-02) user-defined fabs(); rare memory leak; remove duplicate typedef +// 1.09 (2016-01-16) warning fix; avoid crash on outofmem; use allocation userdata properly +// 1.08 (2015-09-13) document stbtt_Rasterize(); fixes for vertical & horizontal edges +// 1.07 (2015-08-01) allow PackFontRanges to accept arrays of sparse codepoints; +// variant PackFontRanges to pack and render in separate phases; +// fix stbtt_GetFontOFfsetForIndex (never worked for non-0 input?); +// fixed an assert() bug in the new rasterizer +// replace assert() with STBTT_assert() in new rasterizer +// 1.06 (2015-07-14) performance improvements (~35% faster on x86 and x64 on test machine) +// also more precise AA rasterizer, except if shapes overlap +// remove need for STBTT_sort +// 1.05 (2015-04-15) fix misplaced definitions for STBTT_STATIC +// 1.04 (2015-04-15) typo in example +// 1.03 (2015-04-12) STBTT_STATIC, fix memory leak in new packing, various fixes +// +// Full history can be found at the end of this file. +// +// LICENSE +// +// This software is dual-licensed to the public domain and under the following +// license: you are granted a perpetual, irrevocable license to copy, modify, +// publish, and distribute this file as you see fit. +// +// USAGE +// +// Include this file in whatever places neeed to refer to it. In ONE C/C++ +// file, write: +// #define STB_TRUETYPE_IMPLEMENTATION +// before the #include of this file. This expands out the actual +// implementation into that C/C++ file. +// +// To make the implementation private to the file that generates the implementation, +// #define STBTT_STATIC +// +// Simple 3D API (don't ship this, but it's fine for tools and quick start) +// stbtt_BakeFontBitmap() -- bake a font to a bitmap for use as texture +// stbtt_GetBakedQuad() -- compute quad to draw for a given char +// +// Improved 3D API (more shippable): +// #include "stb_rect_pack.h" -- optional, but you really want it +// stbtt_PackBegin() +// stbtt_PackSetOversample() -- for improved quality on small fonts +// stbtt_PackFontRanges() -- pack and renders +// stbtt_PackEnd() +// stbtt_GetPackedQuad() +// +// "Load" a font file from a memory buffer (you have to keep the buffer loaded) +// stbtt_InitFont() +// stbtt_GetFontOffsetForIndex() -- use for TTC font collections +// +// Render a unicode codepoint to a bitmap +// stbtt_GetCodepointBitmap() -- allocates and returns a bitmap +// stbtt_MakeCodepointBitmap() -- renders into bitmap you provide +// stbtt_GetCodepointBitmapBox() -- how big the bitmap must be +// +// Character advance/positioning +// stbtt_GetCodepointHMetrics() +// stbtt_GetFontVMetrics() +// stbtt_GetCodepointKernAdvance() +// +// Starting with version 1.06, the rasterizer was replaced with a new, +// faster and generally-more-precise rasterizer. The new rasterizer more +// accurately measures pixel coverage for anti-aliasing, except in the case +// where multiple shapes overlap, in which case it overestimates the AA pixel +// coverage. Thus, anti-aliasing of intersecting shapes may look wrong. If +// this turns out to be a problem, you can re-enable the old rasterizer with +// #define STBTT_RASTERIZER_VERSION 1 +// which will incur about a 15% speed hit. +// +// ADDITIONAL DOCUMENTATION +// +// Immediately after this block comment are a series of sample programs. +// +// After the sample programs is the "header file" section. This section +// includes documentation for each API function. +// +// Some important concepts to understand to use this library: +// +// Codepoint +// Characters are defined by unicode codepoints, e.g. 65 is +// uppercase A, 231 is lowercase c with a cedilla, 0x7e30 is +// the hiragana for "ma". +// +// Glyph +// A visual character shape (every codepoint is rendered as +// some glyph) +// +// Glyph index +// A font-specific integer ID representing a glyph +// +// Baseline +// Glyph shapes are defined relative to a baseline, which is the +// bottom of uppercase characters. Characters extend both above +// and below the baseline. +// +// Current Point +// As you draw text to the screen, you keep track of a "current point" +// which is the origin of each character. The current point's vertical +// position is the baseline. Even "baked fonts" use this model. +// +// Vertical Font Metrics +// The vertical qualities of the font, used to vertically position +// and space the characters. See docs for stbtt_GetFontVMetrics. +// +// Font Size in Pixels or Points +// The preferred interface for specifying font sizes in stb_truetype +// is to specify how tall the font's vertical extent should be in pixels. +// If that sounds good enough, skip the next paragraph. +// +// Most font APIs instead use "points", which are a common typographic +// measurement for describing font size, defined as 72 points per inch. +// stb_truetype provides a point API for compatibility. However, true +// "per inch" conventions don't make much sense on computer displays +// since they different monitors have different number of pixels per +// inch. For example, Windows traditionally uses a convention that +// there are 96 pixels per inch, thus making 'inch' measurements have +// nothing to do with inches, and thus effectively defining a point to +// be 1.333 pixels. Additionally, the TrueType font data provides +// an explicit scale factor to scale a given font's glyphs to points, +// but the author has observed that this scale factor is often wrong +// for non-commercial fonts, thus making fonts scaled in points +// according to the TrueType spec incoherently sized in practice. +// +// ADVANCED USAGE +// +// Quality: +// +// - Use the functions with Subpixel at the end to allow your characters +// to have subpixel positioning. Since the font is anti-aliased, not +// hinted, this is very import for quality. (This is not possible with +// baked fonts.) +// +// - Kerning is now supported, and if you're supporting subpixel rendering +// then kerning is worth using to give your text a polished look. +// +// Performance: +// +// - Convert Unicode codepoints to glyph indexes and operate on the glyphs; +// if you don't do this, stb_truetype is forced to do the conversion on +// every call. +// +// - There are a lot of memory allocations. We should modify it to take +// a temp buffer and allocate from the temp buffer (without freeing), +// should help performance a lot. +// +// NOTES +// +// The system uses the raw data found in the .ttf file without changing it +// and without building auxiliary data structures. This is a bit inefficient +// on little-endian systems (the data is big-endian), but assuming you're +// caching the bitmaps or glyph shapes this shouldn't be a big deal. +// +// It appears to be very hard to programmatically determine what font a +// given file is in a general way. I provide an API for this, but I don't +// recommend it. +// +// +// SOURCE STATISTICS (based on v0.6c, 2050 LOC) +// +// Documentation & header file 520 LOC \___ 660 LOC documentation +// Sample code 140 LOC / +// Truetype parsing 620 LOC ---- 620 LOC TrueType +// Software rasterization 240 LOC \ . +// Curve tesselation 120 LOC \__ 550 LOC Bitmap creation +// Bitmap management 100 LOC / +// Baked bitmap interface 70 LOC / +// Font name matching & access 150 LOC ---- 150 +// C runtime library abstraction 60 LOC ---- 60 +// +// +// PERFORMANCE MEASUREMENTS FOR 1.06: +// +// 32-bit 64-bit +// Previous release: 8.83 s 7.68 s +// Pool allocations: 7.72 s 6.34 s +// Inline sort : 6.54 s 5.65 s +// New rasterizer : 5.63 s 5.00 s + +////////////////////////////////////////////////////////////////////////////// +////////////////////////////////////////////////////////////////////////////// +//// +//// SAMPLE PROGRAMS +//// +// +// Incomplete text-in-3d-api example, which draws quads properly aligned to be lossless +// +#if 0 +#define STB_TRUETYPE_IMPLEMENTATION // force following include to generate implementation +#include "stb_truetype.h" + +unsigned char ttf_buffer[1<<20]; +unsigned char temp_bitmap[512*512]; + +stbtt_bakedchar cdata[96]; // ASCII 32..126 is 95 glyphs +GLuint ftex; + +void my_stbtt_initfont(void) +{ + fread(ttf_buffer, 1, 1<<20, fopen("c:/windows/fonts/times.ttf", "rb")); + stbtt_BakeFontBitmap(ttf_buffer,0, 32.0, temp_bitmap,512,512, 32,96, cdata); // no guarantee this fits! + // can free ttf_buffer at this point + glGenTextures(1, &ftex); + glBindTexture(GL_TEXTURE_2D, ftex); + glTexImage2D(GL_TEXTURE_2D, 0, GL_ALPHA, 512,512, 0, GL_ALPHA, GL_UNSIGNED_BYTE, temp_bitmap); + // can free temp_bitmap at this point + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); +} + +void my_stbtt_print(float x, float y, char *text) +{ + // assume orthographic projection with units = screen pixels, origin at top left + glEnable(GL_TEXTURE_2D); + glBindTexture(GL_TEXTURE_2D, ftex); + glBegin(GL_QUADS); + while (*text) { + if (*text >= 32 && *text < 128) { + stbtt_aligned_quad q; + stbtt_GetBakedQuad(cdata, 512,512, *text-32, &x,&y,&q,1);//1=opengl & d3d10+,0=d3d9 + glTexCoord2f(q.s0,q.t1); glVertex2f(q.x0,q.y0); + glTexCoord2f(q.s1,q.t1); glVertex2f(q.x1,q.y0); + glTexCoord2f(q.s1,q.t0); glVertex2f(q.x1,q.y1); + glTexCoord2f(q.s0,q.t0); glVertex2f(q.x0,q.y1); + } + ++text; + } + glEnd(); +} +#endif +// +// +////////////////////////////////////////////////////////////////////////////// +// +// Complete program (this compiles): get a single bitmap, print as ASCII art +// +#if 0 +#include +#define STB_TRUETYPE_IMPLEMENTATION // force following include to generate implementation +#include "stb_truetype.h" + +char ttf_buffer[1<<25]; + +int main(int argc, char **argv) +{ + stbtt_fontinfo font; + unsigned char *bitmap; + int w,h,i,j,c = (argc > 1 ? atoi(argv[1]) : 'a'), s = (argc > 2 ? atoi(argv[2]) : 20); + + fread(ttf_buffer, 1, 1<<25, fopen(argc > 3 ? argv[3] : "c:/windows/fonts/arialbd.ttf", "rb")); + + stbtt_InitFont(&font, ttf_buffer, stbtt_GetFontOffsetForIndex(ttf_buffer,0)); + bitmap = stbtt_GetCodepointBitmap(&font, 0,stbtt_ScaleForPixelHeight(&font, s), c, &w, &h, 0,0); + + for (j=0; j < h; ++j) { + for (i=0; i < w; ++i) + putchar(" .:ioVM@"[bitmap[j*w+i]>>5]); + putchar('\n'); + } + return 0; +} +#endif +// +// Output: +// +// .ii. +// @@@@@@. +// V@Mio@@o +// :i. V@V +// :oM@@M +// :@@@MM@M +// @@o o@M +// :@@. M@M +// @@@o@@@@ +// :M@@V:@@. +// +////////////////////////////////////////////////////////////////////////////// +// +// Complete program: print "Hello World!" banner, with bugs +// +#if 0 +char buffer[24<<20]; +unsigned char screen[20][79]; + +int main(int arg, char **argv) +{ + stbtt_fontinfo font; + int i,j,ascent,baseline,ch=0; + float scale, xpos=2; // leave a little padding in case the character extends left + char *text = "Heljo World!"; // intentionally misspelled to show 'lj' brokenness + + fread(buffer, 1, 1000000, fopen("c:/windows/fonts/arialbd.ttf", "rb")); + stbtt_InitFont(&font, buffer, 0); + + scale = stbtt_ScaleForPixelHeight(&font, 15); + stbtt_GetFontVMetrics(&font, &ascent,0,0); + baseline = (int) (ascent*scale); + + while (text[ch]) { + int advance,lsb,x0,y0,x1,y1; + float x_shift = xpos - (float) floor(xpos); + stbtt_GetCodepointHMetrics(&font, text[ch], &advance, &lsb); + stbtt_GetCodepointBitmapBoxSubpixel(&font, text[ch], scale,scale,x_shift,0, &x0,&y0,&x1,&y1); + stbtt_MakeCodepointBitmapSubpixel(&font, &screen[baseline + y0][(int) xpos + x0], x1-x0,y1-y0, 79, scale,scale,x_shift,0, text[ch]); + // note that this stomps the old data, so where character boxes overlap (e.g. 'lj') it's wrong + // because this API is really for baking character bitmaps into textures. if you want to render + // a sequence of characters, you really need to render each bitmap to a temp buffer, then + // "alpha blend" that into the working buffer + xpos += (advance * scale); + if (text[ch+1]) + xpos += scale*stbtt_GetCodepointKernAdvance(&font, text[ch],text[ch+1]); + ++ch; + } + + for (j=0; j < 20; ++j) { + for (i=0; i < 78; ++i) + putchar(" .:ioVM@"[screen[j][i]>>5]); + putchar('\n'); + } + + return 0; +} +#endif + + +////////////////////////////////////////////////////////////////////////////// +////////////////////////////////////////////////////////////////////////////// +//// +//// INTEGRATION WITH YOUR CODEBASE +//// +//// The following sections allow you to supply alternate definitions +//// of C library functions used by stb_truetype. + +#ifdef STB_TRUETYPE_IMPLEMENTATION + // #define your own (u)stbtt_int8/16/32 before including to override this + #ifndef stbtt_uint8 + typedef unsigned char stbtt_uint8; + typedef signed char stbtt_int8; + typedef unsigned short stbtt_uint16; + typedef signed short stbtt_int16; + typedef unsigned int stbtt_uint32; + typedef signed int stbtt_int32; + #endif + + typedef char stbtt__check_size32[sizeof(stbtt_int32)==4 ? 1 : -1]; + typedef char stbtt__check_size16[sizeof(stbtt_int16)==2 ? 1 : -1]; + + // #define your own STBTT_ifloor/STBTT_iceil() to avoid math.h + #ifndef STBTT_ifloor + #include + #define STBTT_ifloor(x) ((int) floor(x)) + #define STBTT_iceil(x) ((int) ceil(x)) + #endif + + #ifndef STBTT_sqrt + #include + #define STBTT_sqrt(x) sqrt(x) + #endif + + #ifndef STBTT_fabs + #include + #define STBTT_fabs(x) fabs(x) + #endif + + // #define your own functions "STBTT_malloc" / "STBTT_free" to avoid malloc.h + #ifndef STBTT_malloc + #include + #define STBTT_malloc(x,u) ((void)(u),malloc(x)) + #define STBTT_free(x,u) ((void)(u),free(x)) + #endif + + #ifndef STBTT_assert + #include + #define STBTT_assert(x) assert(x) + #endif + + #ifndef STBTT_strlen + #include + #define STBTT_strlen(x) strlen(x) + #endif + + #ifndef STBTT_memcpy + #include + #define STBTT_memcpy memcpy + #define STBTT_memset memset + #endif +#endif + +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// +//// +//// INTERFACE +//// +//// + +#ifndef __STB_INCLUDE_STB_TRUETYPE_H__ +#define __STB_INCLUDE_STB_TRUETYPE_H__ + +#ifdef STBTT_STATIC +#define STBTT_DEF static +#else +#define STBTT_DEF extern +#endif + +#ifdef __cplusplus +extern "C" { +#endif + +////////////////////////////////////////////////////////////////////////////// +// +// TEXTURE BAKING API +// +// If you use this API, you only have to call two functions ever. +// + +typedef struct +{ + unsigned short x0,y0,x1,y1; // coordinates of bbox in bitmap + float xoff,yoff,xadvance; +} stbtt_bakedchar; + +STBTT_DEF int stbtt_BakeFontBitmap(const unsigned char *data, int offset, // font location (use offset=0 for plain .ttf) + float pixel_height, // height of font in pixels + unsigned char *pixels, int pw, int ph, // bitmap to be filled in + int first_char, int num_chars, // characters to bake + stbtt_bakedchar *chardata); // you allocate this, it's num_chars long +// if return is positive, the first unused row of the bitmap +// if return is negative, returns the negative of the number of characters that fit +// if return is 0, no characters fit and no rows were used +// This uses a very crappy packing. + +typedef struct +{ + float x0,y0,s0,t0; // top-left + float x1,y1,s1,t1; // bottom-right +} stbtt_aligned_quad; + +STBTT_DEF void stbtt_GetBakedQuad(stbtt_bakedchar *chardata, int pw, int ph, // same data as above + int char_index, // character to display + float *xpos, float *ypos, // pointers to current position in screen pixel space + stbtt_aligned_quad *q, // output: quad to draw + int opengl_fillrule); // true if opengl fill rule; false if DX9 or earlier +// Call GetBakedQuad with char_index = 'character - first_char', and it +// creates the quad you need to draw and advances the current position. +// +// The coordinate system used assumes y increases downwards. +// +// Characters will extend both above and below the current position; +// see discussion of "BASELINE" above. +// +// It's inefficient; you might want to c&p it and optimize it. + + + +////////////////////////////////////////////////////////////////////////////// +// +// NEW TEXTURE BAKING API +// +// This provides options for packing multiple fonts into one atlas, not +// perfectly but better than nothing. + +typedef struct +{ + unsigned short x0,y0,x1,y1; // coordinates of bbox in bitmap + float xoff,yoff,xadvance; + float xoff2,yoff2; +} stbtt_packedchar; + +typedef struct stbtt_pack_context stbtt_pack_context; +typedef struct stbtt_fontinfo stbtt_fontinfo; +#ifndef STB_RECT_PACK_VERSION +typedef struct stbrp_rect stbrp_rect; +#endif + +STBTT_DEF int stbtt_PackBegin(stbtt_pack_context *spc, unsigned char *pixels, int width, int height, int stride_in_bytes, int padding, void *alloc_context); +// Initializes a packing context stored in the passed-in stbtt_pack_context. +// Future calls using this context will pack characters into the bitmap passed +// in here: a 1-channel bitmap that is weight x height. stride_in_bytes is +// the distance from one row to the next (or 0 to mean they are packed tightly +// together). "padding" is the amount of padding to leave between each +// character (normally you want '1' for bitmaps you'll use as textures with +// bilinear filtering). +// +// Returns 0 on failure, 1 on success. + +STBTT_DEF void stbtt_PackEnd (stbtt_pack_context *spc); +// Cleans up the packing context and frees all memory. + +#define STBTT_POINT_SIZE(x) (-(x)) + +STBTT_DEF int stbtt_PackFontRange(stbtt_pack_context *spc, unsigned char *fontdata, int font_index, float font_size, + int first_unicode_char_in_range, int num_chars_in_range, stbtt_packedchar *chardata_for_range); +// Creates character bitmaps from the font_index'th font found in fontdata (use +// font_index=0 if you don't know what that is). It creates num_chars_in_range +// bitmaps for characters with unicode values starting at first_unicode_char_in_range +// and increasing. Data for how to render them is stored in chardata_for_range; +// pass these to stbtt_GetPackedQuad to get back renderable quads. +// +// font_size is the full height of the character from ascender to descender, +// as computed by stbtt_ScaleForPixelHeight. To use a point size as computed +// by stbtt_ScaleForMappingEmToPixels, wrap the point size in STBTT_POINT_SIZE() +// and pass that result as 'font_size': +// ..., 20 , ... // font max minus min y is 20 pixels tall +// ..., STBTT_POINT_SIZE(20), ... // 'M' is 20 pixels tall + +typedef struct +{ + float font_size; + int first_unicode_codepoint_in_range; // if non-zero, then the chars are continuous, and this is the first codepoint + int *array_of_unicode_codepoints; // if non-zero, then this is an array of unicode codepoints + int num_chars; + stbtt_packedchar *chardata_for_range; // output + unsigned char h_oversample, v_oversample; // don't set these, they're used internally +} stbtt_pack_range; + +STBTT_DEF int stbtt_PackFontRanges(stbtt_pack_context *spc, unsigned char *fontdata, int font_index, stbtt_pack_range *ranges, int num_ranges); +// Creates character bitmaps from multiple ranges of characters stored in +// ranges. This will usually create a better-packed bitmap than multiple +// calls to stbtt_PackFontRange. Note that you can call this multiple +// times within a single PackBegin/PackEnd. + +STBTT_DEF void stbtt_PackSetOversampling(stbtt_pack_context *spc, unsigned int h_oversample, unsigned int v_oversample); +// Oversampling a font increases the quality by allowing higher-quality subpixel +// positioning, and is especially valuable at smaller text sizes. +// +// This function sets the amount of oversampling for all following calls to +// stbtt_PackFontRange(s) or stbtt_PackFontRangesGatherRects for a given +// pack context. The default (no oversampling) is achieved by h_oversample=1 +// and v_oversample=1. The total number of pixels required is +// h_oversample*v_oversample larger than the default; for example, 2x2 +// oversampling requires 4x the storage of 1x1. For best results, render +// oversampled textures with bilinear filtering. Look at the readme in +// stb/tests/oversample for information about oversampled fonts +// +// To use with PackFontRangesGather etc., you must set it before calls +// call to PackFontRangesGatherRects. + +STBTT_DEF void stbtt_GetPackedQuad(stbtt_packedchar *chardata, int pw, int ph, // same data as above + int char_index, // character to display + float *xpos, float *ypos, // pointers to current position in screen pixel space + stbtt_aligned_quad *q, // output: quad to draw + int align_to_integer); + +STBTT_DEF int stbtt_PackFontRangesGatherRects(stbtt_pack_context *spc, stbtt_fontinfo *info, stbtt_pack_range *ranges, int num_ranges, stbrp_rect *rects); +STBTT_DEF void stbtt_PackFontRangesPackRects(stbtt_pack_context *spc, stbrp_rect *rects, int num_rects); +STBTT_DEF int stbtt_PackFontRangesRenderIntoRects(stbtt_pack_context *spc, stbtt_fontinfo *info, stbtt_pack_range *ranges, int num_ranges, stbrp_rect *rects); +// Calling these functions in sequence is roughly equivalent to calling +// stbtt_PackFontRanges(). If you more control over the packing of multiple +// fonts, or if you want to pack custom data into a font texture, take a look +// at the source to of stbtt_PackFontRanges() and create a custom version +// using these functions, e.g. call GatherRects multiple times, +// building up a single array of rects, then call PackRects once, +// then call RenderIntoRects repeatedly. This may result in a +// better packing than calling PackFontRanges multiple times +// (or it may not). + +// this is an opaque structure that you shouldn't mess with which holds +// all the context needed from PackBegin to PackEnd. +struct stbtt_pack_context { + void *user_allocator_context; + void *pack_info; + int width; + int height; + int stride_in_bytes; + int padding; + unsigned int h_oversample, v_oversample; + unsigned char *pixels; + void *nodes; +}; + +////////////////////////////////////////////////////////////////////////////// +// +// FONT LOADING +// +// + +STBTT_DEF int stbtt_GetFontOffsetForIndex(const unsigned char *data, int index); +// Each .ttf/.ttc file may have more than one font. Each font has a sequential +// index number starting from 0. Call this function to get the font offset for +// a given index; it returns -1 if the index is out of range. A regular .ttf +// file will only define one font and it always be at offset 0, so it will +// return '0' for index 0, and -1 for all other indices. You can just skip +// this step if you know it's that kind of font. + + +// The following structure is defined publically so you can declare one on +// the stack or as a global or etc, but you should treat it as opaque. +struct stbtt_fontinfo +{ + void * userdata; + unsigned char * data; // pointer to .ttf file + int fontstart; // offset of start of font + + int numGlyphs; // number of glyphs, needed for range checking + + int loca,head,glyf,hhea,hmtx,kern; // table locations as offset from start of .ttf + int index_map; // a cmap mapping for our chosen character encoding + int indexToLocFormat; // format needed to map from glyph index to glyph +}; + +STBTT_DEF int stbtt_InitFont(stbtt_fontinfo *info, const unsigned char *data, int offset); +// Given an offset into the file that defines a font, this function builds +// the necessary cached info for the rest of the system. You must allocate +// the stbtt_fontinfo yourself, and stbtt_InitFont will fill it out. You don't +// need to do anything special to free it, because the contents are pure +// value data with no additional data structures. Returns 0 on failure. + + +////////////////////////////////////////////////////////////////////////////// +// +// CHARACTER TO GLYPH-INDEX CONVERSIOn + +STBTT_DEF int stbtt_FindGlyphIndex(const stbtt_fontinfo *info, int unicode_codepoint); +// If you're going to perform multiple operations on the same character +// and you want a speed-up, call this function with the character you're +// going to process, then use glyph-based functions instead of the +// codepoint-based functions. + + +////////////////////////////////////////////////////////////////////////////// +// +// CHARACTER PROPERTIES +// + +STBTT_DEF float stbtt_ScaleForPixelHeight(const stbtt_fontinfo *info, float pixels); +// computes a scale factor to produce a font whose "height" is 'pixels' tall. +// Height is measured as the distance from the highest ascender to the lowest +// descender; in other words, it's equivalent to calling stbtt_GetFontVMetrics +// and computing: +// scale = pixels / (ascent - descent) +// so if you prefer to measure height by the ascent only, use a similar calculation. + +STBTT_DEF float stbtt_ScaleForMappingEmToPixels(const stbtt_fontinfo *info, float pixels); +// computes a scale factor to produce a font whose EM size is mapped to +// 'pixels' tall. This is probably what traditional APIs compute, but +// I'm not positive. + +STBTT_DEF void stbtt_GetFontVMetrics(const stbtt_fontinfo *info, int *ascent, int *descent, int *lineGap); +// ascent is the coordinate above the baseline the font extends; descent +// is the coordinate below the baseline the font extends (i.e. it is typically negative) +// lineGap is the spacing between one row's descent and the next row's ascent... +// so you should advance the vertical position by "*ascent - *descent + *lineGap" +// these are expressed in unscaled coordinates, so you must multiply by +// the scale factor for a given size + +STBTT_DEF void stbtt_GetFontBoundingBox(const stbtt_fontinfo *info, int *x0, int *y0, int *x1, int *y1); +// the bounding box around all possible characters + +STBTT_DEF void stbtt_GetCodepointHMetrics(const stbtt_fontinfo *info, int codepoint, int *advanceWidth, int *leftSideBearing); +// leftSideBearing is the offset from the current horizontal position to the left edge of the character +// advanceWidth is the offset from the current horizontal position to the next horizontal position +// these are expressed in unscaled coordinates + +STBTT_DEF int stbtt_GetCodepointKernAdvance(const stbtt_fontinfo *info, int ch1, int ch2); +// an additional amount to add to the 'advance' value between ch1 and ch2 + +STBTT_DEF int stbtt_GetCodepointBox(const stbtt_fontinfo *info, int codepoint, int *x0, int *y0, int *x1, int *y1); +// Gets the bounding box of the visible part of the glyph, in unscaled coordinates + +STBTT_DEF void stbtt_GetGlyphHMetrics(const stbtt_fontinfo *info, int glyph_index, int *advanceWidth, int *leftSideBearing); +STBTT_DEF int stbtt_GetGlyphKernAdvance(const stbtt_fontinfo *info, int glyph1, int glyph2); +STBTT_DEF int stbtt_GetGlyphBox(const stbtt_fontinfo *info, int glyph_index, int *x0, int *y0, int *x1, int *y1); +// as above, but takes one or more glyph indices for greater efficiency + + +////////////////////////////////////////////////////////////////////////////// +// +// GLYPH SHAPES (you probably don't need these, but they have to go before +// the bitmaps for C declaration-order reasons) +// + +#ifndef STBTT_vmove // you can predefine these to use different values (but why?) + enum { + STBTT_vmove=1, + STBTT_vline, + STBTT_vcurve + }; +#endif + +#ifndef stbtt_vertex // you can predefine this to use different values + // (we share this with other code at RAD) + #define stbtt_vertex_type short // can't use stbtt_int16 because that's not visible in the header file + typedef struct + { + stbtt_vertex_type x,y,cx,cy; + unsigned char type,padding; + } stbtt_vertex; +#endif + +STBTT_DEF int stbtt_IsGlyphEmpty(const stbtt_fontinfo *info, int glyph_index); +// returns non-zero if nothing is drawn for this glyph + +STBTT_DEF int stbtt_GetCodepointShape(const stbtt_fontinfo *info, int unicode_codepoint, stbtt_vertex **vertices); +STBTT_DEF int stbtt_GetGlyphShape(const stbtt_fontinfo *info, int glyph_index, stbtt_vertex **vertices); +// returns # of vertices and fills *vertices with the pointer to them +// these are expressed in "unscaled" coordinates +// +// The shape is a series of countours. Each one starts with +// a STBTT_moveto, then consists of a series of mixed +// STBTT_lineto and STBTT_curveto segments. A lineto +// draws a line from previous endpoint to its x,y; a curveto +// draws a quadratic bezier from previous endpoint to +// its x,y, using cx,cy as the bezier control point. + +STBTT_DEF void stbtt_FreeShape(const stbtt_fontinfo *info, stbtt_vertex *vertices); +// frees the data allocated above + +////////////////////////////////////////////////////////////////////////////// +// +// BITMAP RENDERING +// + +STBTT_DEF void stbtt_FreeBitmap(unsigned char *bitmap, void *userdata); +// frees the bitmap allocated below + +STBTT_DEF unsigned char *stbtt_GetCodepointBitmap(const stbtt_fontinfo *info, float scale_x, float scale_y, int codepoint, int *width, int *height, int *xoff, int *yoff); +// allocates a large-enough single-channel 8bpp bitmap and renders the +// specified character/glyph at the specified scale into it, with +// antialiasing. 0 is no coverage (transparent), 255 is fully covered (opaque). +// *width & *height are filled out with the width & height of the bitmap, +// which is stored left-to-right, top-to-bottom. +// +// xoff/yoff are the offset it pixel space from the glyph origin to the top-left of the bitmap + +STBTT_DEF unsigned char *stbtt_GetCodepointBitmapSubpixel(const stbtt_fontinfo *info, float scale_x, float scale_y, float shift_x, float shift_y, int codepoint, int *width, int *height, int *xoff, int *yoff); +// the same as stbtt_GetCodepoitnBitmap, but you can specify a subpixel +// shift for the character + +STBTT_DEF void stbtt_MakeCodepointBitmap(const stbtt_fontinfo *info, unsigned char *output, int out_w, int out_h, int out_stride, float scale_x, float scale_y, int codepoint); +// the same as stbtt_GetCodepointBitmap, but you pass in storage for the bitmap +// in the form of 'output', with row spacing of 'out_stride' bytes. the bitmap +// is clipped to out_w/out_h bytes. Call stbtt_GetCodepointBitmapBox to get the +// width and height and positioning info for it first. + +STBTT_DEF void stbtt_MakeCodepointBitmapSubpixel(const stbtt_fontinfo *info, unsigned char *output, int out_w, int out_h, int out_stride, float scale_x, float scale_y, float shift_x, float shift_y, int codepoint); +// same as stbtt_MakeCodepointBitmap, but you can specify a subpixel +// shift for the character + +STBTT_DEF void stbtt_GetCodepointBitmapBox(const stbtt_fontinfo *font, int codepoint, float scale_x, float scale_y, int *ix0, int *iy0, int *ix1, int *iy1); +// get the bbox of the bitmap centered around the glyph origin; so the +// bitmap width is ix1-ix0, height is iy1-iy0, and location to place +// the bitmap top left is (leftSideBearing*scale,iy0). +// (Note that the bitmap uses y-increases-down, but the shape uses +// y-increases-up, so CodepointBitmapBox and CodepointBox are inverted.) + +STBTT_DEF void stbtt_GetCodepointBitmapBoxSubpixel(const stbtt_fontinfo *font, int codepoint, float scale_x, float scale_y, float shift_x, float shift_y, int *ix0, int *iy0, int *ix1, int *iy1); +// same as stbtt_GetCodepointBitmapBox, but you can specify a subpixel +// shift for the character + +// the following functions are equivalent to the above functions, but operate +// on glyph indices instead of Unicode codepoints (for efficiency) +STBTT_DEF unsigned char *stbtt_GetGlyphBitmap(const stbtt_fontinfo *info, float scale_x, float scale_y, int glyph, int *width, int *height, int *xoff, int *yoff); +STBTT_DEF unsigned char *stbtt_GetGlyphBitmapSubpixel(const stbtt_fontinfo *info, float scale_x, float scale_y, float shift_x, float shift_y, int glyph, int *width, int *height, int *xoff, int *yoff); +STBTT_DEF void stbtt_MakeGlyphBitmap(const stbtt_fontinfo *info, unsigned char *output, int out_w, int out_h, int out_stride, float scale_x, float scale_y, int glyph); +STBTT_DEF void stbtt_MakeGlyphBitmapSubpixel(const stbtt_fontinfo *info, unsigned char *output, int out_w, int out_h, int out_stride, float scale_x, float scale_y, float shift_x, float shift_y, int glyph); +STBTT_DEF void stbtt_GetGlyphBitmapBox(const stbtt_fontinfo *font, int glyph, float scale_x, float scale_y, int *ix0, int *iy0, int *ix1, int *iy1); +STBTT_DEF void stbtt_GetGlyphBitmapBoxSubpixel(const stbtt_fontinfo *font, int glyph, float scale_x, float scale_y,float shift_x, float shift_y, int *ix0, int *iy0, int *ix1, int *iy1); + + +// @TODO: don't expose this structure +typedef struct +{ + int w,h,stride; + unsigned char *pixels; +} stbtt__bitmap; + +// rasterize a shape with quadratic beziers into a bitmap +STBTT_DEF void stbtt_Rasterize(stbtt__bitmap *result, // 1-channel bitmap to draw into + float flatness_in_pixels, // allowable error of curve in pixels + stbtt_vertex *vertices, // array of vertices defining shape + int num_verts, // number of vertices in above array + float scale_x, float scale_y, // scale applied to input vertices + float shift_x, float shift_y, // translation applied to input vertices + int x_off, int y_off, // another translation applied to input + int invert, // if non-zero, vertically flip shape + void *userdata); // context for to STBTT_MALLOC + +////////////////////////////////////////////////////////////////////////////// +// +// Finding the right font... +// +// You should really just solve this offline, keep your own tables +// of what font is what, and don't try to get it out of the .ttf file. +// That's because getting it out of the .ttf file is really hard, because +// the names in the file can appear in many possible encodings, in many +// possible languages, and e.g. if you need a case-insensitive comparison, +// the details of that depend on the encoding & language in a complex way +// (actually underspecified in truetype, but also gigantic). +// +// But you can use the provided functions in two possible ways: +// stbtt_FindMatchingFont() will use *case-sensitive* comparisons on +// unicode-encoded names to try to find the font you want; +// you can run this before calling stbtt_InitFont() +// +// stbtt_GetFontNameString() lets you get any of the various strings +// from the file yourself and do your own comparisons on them. +// You have to have called stbtt_InitFont() first. + + +STBTT_DEF int stbtt_FindMatchingFont(const unsigned char *fontdata, const char *name, int flags); +// returns the offset (not index) of the font that matches, or -1 if none +// if you use STBTT_MACSTYLE_DONTCARE, use a font name like "Arial Bold". +// if you use any other flag, use a font name like "Arial"; this checks +// the 'macStyle' header field; i don't know if fonts set this consistently +#define STBTT_MACSTYLE_DONTCARE 0 +#define STBTT_MACSTYLE_BOLD 1 +#define STBTT_MACSTYLE_ITALIC 2 +#define STBTT_MACSTYLE_UNDERSCORE 4 +#define STBTT_MACSTYLE_NONE 8 // <= not same as 0, this makes us check the bitfield is 0 + +STBTT_DEF int stbtt_CompareUTF8toUTF16_bigendian(const char *s1, int len1, const char *s2, int len2); +// returns 1/0 whether the first string interpreted as utf8 is identical to +// the second string interpreted as big-endian utf16... useful for strings from next func + +STBTT_DEF const char *stbtt_GetFontNameString(const stbtt_fontinfo *font, int *length, int platformID, int encodingID, int languageID, int nameID); +// returns the string (which may be big-endian double byte, e.g. for unicode) +// and puts the length in bytes in *length. +// +// some of the values for the IDs are below; for more see the truetype spec: +// http://developer.apple.com/textfonts/TTRefMan/RM06/Chap6name.html +// http://www.microsoft.com/typography/otspec/name.htm + +enum { // platformID + STBTT_PLATFORM_ID_UNICODE =0, + STBTT_PLATFORM_ID_MAC =1, + STBTT_PLATFORM_ID_ISO =2, + STBTT_PLATFORM_ID_MICROSOFT =3 +}; + +enum { // encodingID for STBTT_PLATFORM_ID_UNICODE + STBTT_UNICODE_EID_UNICODE_1_0 =0, + STBTT_UNICODE_EID_UNICODE_1_1 =1, + STBTT_UNICODE_EID_ISO_10646 =2, + STBTT_UNICODE_EID_UNICODE_2_0_BMP=3, + STBTT_UNICODE_EID_UNICODE_2_0_FULL=4 +}; + +enum { // encodingID for STBTT_PLATFORM_ID_MICROSOFT + STBTT_MS_EID_SYMBOL =0, + STBTT_MS_EID_UNICODE_BMP =1, + STBTT_MS_EID_SHIFTJIS =2, + STBTT_MS_EID_UNICODE_FULL =10 +}; + +enum { // encodingID for STBTT_PLATFORM_ID_MAC; same as Script Manager codes + STBTT_MAC_EID_ROMAN =0, STBTT_MAC_EID_ARABIC =4, + STBTT_MAC_EID_JAPANESE =1, STBTT_MAC_EID_HEBREW =5, + STBTT_MAC_EID_CHINESE_TRAD =2, STBTT_MAC_EID_GREEK =6, + STBTT_MAC_EID_KOREAN =3, STBTT_MAC_EID_RUSSIAN =7 +}; + +enum { // languageID for STBTT_PLATFORM_ID_MICROSOFT; same as LCID... + // problematic because there are e.g. 16 english LCIDs and 16 arabic LCIDs + STBTT_MS_LANG_ENGLISH =0x0409, STBTT_MS_LANG_ITALIAN =0x0410, + STBTT_MS_LANG_CHINESE =0x0804, STBTT_MS_LANG_JAPANESE =0x0411, + STBTT_MS_LANG_DUTCH =0x0413, STBTT_MS_LANG_KOREAN =0x0412, + STBTT_MS_LANG_FRENCH =0x040c, STBTT_MS_LANG_RUSSIAN =0x0419, + STBTT_MS_LANG_GERMAN =0x0407, STBTT_MS_LANG_SPANISH =0x0409, + STBTT_MS_LANG_HEBREW =0x040d, STBTT_MS_LANG_SWEDISH =0x041D +}; + +enum { // languageID for STBTT_PLATFORM_ID_MAC + STBTT_MAC_LANG_ENGLISH =0 , STBTT_MAC_LANG_JAPANESE =11, + STBTT_MAC_LANG_ARABIC =12, STBTT_MAC_LANG_KOREAN =23, + STBTT_MAC_LANG_DUTCH =4 , STBTT_MAC_LANG_RUSSIAN =32, + STBTT_MAC_LANG_FRENCH =1 , STBTT_MAC_LANG_SPANISH =6 , + STBTT_MAC_LANG_GERMAN =2 , STBTT_MAC_LANG_SWEDISH =5 , + STBTT_MAC_LANG_HEBREW =10, STBTT_MAC_LANG_CHINESE_SIMPLIFIED =33, + STBTT_MAC_LANG_ITALIAN =3 , STBTT_MAC_LANG_CHINESE_TRAD =19 +}; + +#ifdef __cplusplus +} +#endif + +#endif // __STB_INCLUDE_STB_TRUETYPE_H__ + +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// +//// +//// IMPLEMENTATION +//// +//// + +#ifdef STB_TRUETYPE_IMPLEMENTATION + +#ifndef STBTT_MAX_OVERSAMPLE +#define STBTT_MAX_OVERSAMPLE 8 +#endif + +#if STBTT_MAX_OVERSAMPLE > 255 +#error "STBTT_MAX_OVERSAMPLE cannot be > 255" +#endif + +typedef int stbtt__test_oversample_pow2[(STBTT_MAX_OVERSAMPLE & (STBTT_MAX_OVERSAMPLE-1)) == 0 ? 1 : -1]; + +#ifndef STBTT_RASTERIZER_VERSION +#define STBTT_RASTERIZER_VERSION 2 +#endif + +#ifdef _MSC_VER +#define STBTT__NOTUSED(v) (void)(v) +#else +#define STBTT__NOTUSED(v) (void)sizeof(v) +#endif + +////////////////////////////////////////////////////////////////////////// +// +// accessors to parse data from file +// + +// on platforms that don't allow misaligned reads, if we want to allow +// truetype fonts that aren't padded to alignment, define ALLOW_UNALIGNED_TRUETYPE + +#define ttBYTE(p) (* (stbtt_uint8 *) (p)) +#define ttCHAR(p) (* (stbtt_int8 *) (p)) +#define ttFixed(p) ttLONG(p) + +#if defined(STB_TRUETYPE_BIGENDIAN) && !defined(ALLOW_UNALIGNED_TRUETYPE) + + #define ttUSHORT(p) (* (stbtt_uint16 *) (p)) + #define ttSHORT(p) (* (stbtt_int16 *) (p)) + #define ttULONG(p) (* (stbtt_uint32 *) (p)) + #define ttLONG(p) (* (stbtt_int32 *) (p)) + +#else + + static stbtt_uint16 ttUSHORT(const stbtt_uint8 *p) { return p[0]*256 + p[1]; } + static stbtt_int16 ttSHORT(const stbtt_uint8 *p) { return p[0]*256 + p[1]; } + static stbtt_uint32 ttULONG(const stbtt_uint8 *p) { return (p[0]<<24) + (p[1]<<16) + (p[2]<<8) + p[3]; } + static stbtt_int32 ttLONG(const stbtt_uint8 *p) { return (p[0]<<24) + (p[1]<<16) + (p[2]<<8) + p[3]; } + +#endif + +#define stbtt_tag4(p,c0,c1,c2,c3) ((p)[0] == (c0) && (p)[1] == (c1) && (p)[2] == (c2) && (p)[3] == (c3)) +#define stbtt_tag(p,str) stbtt_tag4(p,str[0],str[1],str[2],str[3]) + +static int stbtt__isfont(const stbtt_uint8 *font) +{ + // check the version number + if (stbtt_tag4(font, '1',0,0,0)) return 1; // TrueType 1 + if (stbtt_tag(font, "typ1")) return 1; // TrueType with type 1 font -- we don't support this! + if (stbtt_tag(font, "OTTO")) return 1; // OpenType with CFF + if (stbtt_tag4(font, 0,1,0,0)) return 1; // OpenType 1.0 + return 0; +} + +// @OPTIMIZE: binary search +static stbtt_uint32 stbtt__find_table(stbtt_uint8 *data, stbtt_uint32 fontstart, const char *tag) +{ + stbtt_int32 num_tables = ttUSHORT(data+fontstart+4); + stbtt_uint32 tabledir = fontstart + 12; + stbtt_int32 i; + for (i=0; i < num_tables; ++i) { + stbtt_uint32 loc = tabledir + 16*i; + if (stbtt_tag(data+loc+0, tag)) + return ttULONG(data+loc+8); + } + return 0; +} + +STBTT_DEF int stbtt_GetFontOffsetForIndex(const unsigned char *font_collection, int index) +{ + // if it's just a font, there's only one valid index + if (stbtt__isfont(font_collection)) + return index == 0 ? 0 : -1; + + // check if it's a TTC + if (stbtt_tag(font_collection, "ttcf")) { + // version 1? + if (ttULONG(font_collection+4) == 0x00010000 || ttULONG(font_collection+4) == 0x00020000) { + stbtt_int32 n = ttLONG(font_collection+8); + if (index >= n) + return -1; + return ttULONG(font_collection+12+index*4); + } + } + return -1; +} + +STBTT_DEF int stbtt_InitFont(stbtt_fontinfo *info, const unsigned char *data2, int fontstart) +{ + stbtt_uint8 *data = (stbtt_uint8 *) data2; + stbtt_uint32 cmap, t; + stbtt_int32 i,numTables; + + info->data = data; + info->fontstart = fontstart; + + cmap = stbtt__find_table(data, fontstart, "cmap"); // required + info->loca = stbtt__find_table(data, fontstart, "loca"); // required + info->head = stbtt__find_table(data, fontstart, "head"); // required + info->glyf = stbtt__find_table(data, fontstart, "glyf"); // required + info->hhea = stbtt__find_table(data, fontstart, "hhea"); // required + info->hmtx = stbtt__find_table(data, fontstart, "hmtx"); // required + info->kern = stbtt__find_table(data, fontstart, "kern"); // not required + if (!cmap || !info->loca || !info->head || !info->glyf || !info->hhea || !info->hmtx) + return 0; + + t = stbtt__find_table(data, fontstart, "maxp"); + if (t) + info->numGlyphs = ttUSHORT(data+t+4); + else + info->numGlyphs = 0xffff; + + // find a cmap encoding table we understand *now* to avoid searching + // later. (todo: could make this installable) + // the same regardless of glyph. + numTables = ttUSHORT(data + cmap + 2); + info->index_map = 0; + for (i=0; i < numTables; ++i) { + stbtt_uint32 encoding_record = cmap + 4 + 8 * i; + // find an encoding we understand: + switch(ttUSHORT(data+encoding_record)) { + case STBTT_PLATFORM_ID_MICROSOFT: + switch (ttUSHORT(data+encoding_record+2)) { + case STBTT_MS_EID_UNICODE_BMP: + case STBTT_MS_EID_UNICODE_FULL: + // MS/Unicode + info->index_map = cmap + ttULONG(data+encoding_record+4); + break; + } + break; + case STBTT_PLATFORM_ID_UNICODE: + // Mac/iOS has these + // all the encodingIDs are unicode, so we don't bother to check it + info->index_map = cmap + ttULONG(data+encoding_record+4); + break; + } + } + if (info->index_map == 0) + return 0; + + info->indexToLocFormat = ttUSHORT(data+info->head + 50); + return 1; +} + +STBTT_DEF int stbtt_FindGlyphIndex(const stbtt_fontinfo *info, int unicode_codepoint) +{ + stbtt_uint8 *data = info->data; + stbtt_uint32 index_map = info->index_map; + + stbtt_uint16 format = ttUSHORT(data + index_map + 0); + if (format == 0) { // apple byte encoding + stbtt_int32 bytes = ttUSHORT(data + index_map + 2); + if (unicode_codepoint < bytes-6) + return ttBYTE(data + index_map + 6 + unicode_codepoint); + return 0; + } else if (format == 6) { + stbtt_uint32 first = ttUSHORT(data + index_map + 6); + stbtt_uint32 count = ttUSHORT(data + index_map + 8); + if ((stbtt_uint32) unicode_codepoint >= first && (stbtt_uint32) unicode_codepoint < first+count) + return ttUSHORT(data + index_map + 10 + (unicode_codepoint - first)*2); + return 0; + } else if (format == 2) { + STBTT_assert(0); // @TODO: high-byte mapping for japanese/chinese/korean + return 0; + } else if (format == 4) { // standard mapping for windows fonts: binary search collection of ranges + stbtt_uint16 segcount = ttUSHORT(data+index_map+6) >> 1; + stbtt_uint16 searchRange = ttUSHORT(data+index_map+8) >> 1; + stbtt_uint16 entrySelector = ttUSHORT(data+index_map+10); + stbtt_uint16 rangeShift = ttUSHORT(data+index_map+12) >> 1; + + // do a binary search of the segments + stbtt_uint32 endCount = index_map + 14; + stbtt_uint32 search = endCount; + + if (unicode_codepoint > 0xffff) + return 0; + + // they lie from endCount .. endCount + segCount + // but searchRange is the nearest power of two, so... + if (unicode_codepoint >= ttUSHORT(data + search + rangeShift*2)) + search += rangeShift*2; + + // now decrement to bias correctly to find smallest + search -= 2; + while (entrySelector) { + stbtt_uint16 end; + searchRange >>= 1; + end = ttUSHORT(data + search + searchRange*2); + if (unicode_codepoint > end) + search += searchRange*2; + --entrySelector; + } + search += 2; + + { + stbtt_uint16 offset, start; + stbtt_uint16 item = (stbtt_uint16) ((search - endCount) >> 1); + + STBTT_assert(unicode_codepoint <= ttUSHORT(data + endCount + 2*item)); + start = ttUSHORT(data + index_map + 14 + segcount*2 + 2 + 2*item); + if (unicode_codepoint < start) + return 0; + + offset = ttUSHORT(data + index_map + 14 + segcount*6 + 2 + 2*item); + if (offset == 0) + return (stbtt_uint16) (unicode_codepoint + ttSHORT(data + index_map + 14 + segcount*4 + 2 + 2*item)); + + return ttUSHORT(data + offset + (unicode_codepoint-start)*2 + index_map + 14 + segcount*6 + 2 + 2*item); + } + } else if (format == 12 || format == 13) { + stbtt_uint32 ngroups = ttULONG(data+index_map+12); + stbtt_int32 low,high; + low = 0; high = (stbtt_int32)ngroups; + // Binary search the right group. + while (low < high) { + stbtt_int32 mid = low + ((high-low) >> 1); // rounds down, so low <= mid < high + stbtt_uint32 start_char = ttULONG(data+index_map+16+mid*12); + stbtt_uint32 end_char = ttULONG(data+index_map+16+mid*12+4); + if ((stbtt_uint32) unicode_codepoint < start_char) + high = mid; + else if ((stbtt_uint32) unicode_codepoint > end_char) + low = mid+1; + else { + stbtt_uint32 start_glyph = ttULONG(data+index_map+16+mid*12+8); + if (format == 12) + return start_glyph + unicode_codepoint-start_char; + else // format == 13 + return start_glyph; + } + } + return 0; // not found + } + // @TODO + STBTT_assert(0); + return 0; +} + +STBTT_DEF int stbtt_GetCodepointShape(const stbtt_fontinfo *info, int unicode_codepoint, stbtt_vertex **vertices) +{ + return stbtt_GetGlyphShape(info, stbtt_FindGlyphIndex(info, unicode_codepoint), vertices); +} + +static void stbtt_setvertex(stbtt_vertex *v, stbtt_uint8 type, stbtt_int32 x, stbtt_int32 y, stbtt_int32 cx, stbtt_int32 cy) +{ + v->type = type; + v->x = (stbtt_int16) x; + v->y = (stbtt_int16) y; + v->cx = (stbtt_int16) cx; + v->cy = (stbtt_int16) cy; +} + +static int stbtt__GetGlyfOffset(const stbtt_fontinfo *info, int glyph_index) +{ + int g1,g2; + + if (glyph_index >= info->numGlyphs) return -1; // glyph index out of range + if (info->indexToLocFormat >= 2) return -1; // unknown index->glyph map format + + if (info->indexToLocFormat == 0) { + g1 = info->glyf + ttUSHORT(info->data + info->loca + glyph_index * 2) * 2; + g2 = info->glyf + ttUSHORT(info->data + info->loca + glyph_index * 2 + 2) * 2; + } else { + g1 = info->glyf + ttULONG (info->data + info->loca + glyph_index * 4); + g2 = info->glyf + ttULONG (info->data + info->loca + glyph_index * 4 + 4); + } + + return g1==g2 ? -1 : g1; // if length is 0, return -1 +} + +STBTT_DEF int stbtt_GetGlyphBox(const stbtt_fontinfo *info, int glyph_index, int *x0, int *y0, int *x1, int *y1) +{ + int g = stbtt__GetGlyfOffset(info, glyph_index); + if (g < 0) return 0; + + if (x0) *x0 = ttSHORT(info->data + g + 2); + if (y0) *y0 = ttSHORT(info->data + g + 4); + if (x1) *x1 = ttSHORT(info->data + g + 6); + if (y1) *y1 = ttSHORT(info->data + g + 8); + return 1; +} + +STBTT_DEF int stbtt_GetCodepointBox(const stbtt_fontinfo *info, int codepoint, int *x0, int *y0, int *x1, int *y1) +{ + return stbtt_GetGlyphBox(info, stbtt_FindGlyphIndex(info,codepoint), x0,y0,x1,y1); +} + +STBTT_DEF int stbtt_IsGlyphEmpty(const stbtt_fontinfo *info, int glyph_index) +{ + stbtt_int16 numberOfContours; + int g = stbtt__GetGlyfOffset(info, glyph_index); + if (g < 0) return 1; + numberOfContours = ttSHORT(info->data + g); + return numberOfContours == 0; +} + +static int stbtt__close_shape(stbtt_vertex *vertices, int num_vertices, int was_off, int start_off, + stbtt_int32 sx, stbtt_int32 sy, stbtt_int32 scx, stbtt_int32 scy, stbtt_int32 cx, stbtt_int32 cy) +{ + if (start_off) { + if (was_off) + stbtt_setvertex(&vertices[num_vertices++], STBTT_vcurve, (cx+scx)>>1, (cy+scy)>>1, cx,cy); + stbtt_setvertex(&vertices[num_vertices++], STBTT_vcurve, sx,sy,scx,scy); + } else { + if (was_off) + stbtt_setvertex(&vertices[num_vertices++], STBTT_vcurve,sx,sy,cx,cy); + else + stbtt_setvertex(&vertices[num_vertices++], STBTT_vline,sx,sy,0,0); + } + return num_vertices; +} + +STBTT_DEF int stbtt_GetGlyphShape(const stbtt_fontinfo *info, int glyph_index, stbtt_vertex **pvertices) +{ + stbtt_int16 numberOfContours; + stbtt_uint8 *endPtsOfContours; + stbtt_uint8 *data = info->data; + stbtt_vertex *vertices=0; + int num_vertices=0; + int g = stbtt__GetGlyfOffset(info, glyph_index); + + *pvertices = NULL; + + if (g < 0) return 0; + + numberOfContours = ttSHORT(data + g); + + if (numberOfContours > 0) { + stbtt_uint8 flags=0,flagcount; + stbtt_int32 ins, i,j=0,m,n, next_move, was_off=0, off, start_off=0; + stbtt_int32 x,y,cx,cy,sx,sy, scx,scy; + stbtt_uint8 *points; + endPtsOfContours = (data + g + 10); + ins = ttUSHORT(data + g + 10 + numberOfContours * 2); + points = data + g + 10 + numberOfContours * 2 + 2 + ins; + + n = 1+ttUSHORT(endPtsOfContours + numberOfContours*2-2); + + m = n + 2*numberOfContours; // a loose bound on how many vertices we might need + vertices = (stbtt_vertex *) STBTT_malloc(m * sizeof(vertices[0]), info->userdata); + if (vertices == 0) + return 0; + + next_move = 0; + flagcount=0; + + // in first pass, we load uninterpreted data into the allocated array + // above, shifted to the end of the array so we won't overwrite it when + // we create our final data starting from the front + + off = m - n; // starting offset for uninterpreted data, regardless of how m ends up being calculated + + // first load flags + + for (i=0; i < n; ++i) { + if (flagcount == 0) { + flags = *points++; + if (flags & 8) + flagcount = *points++; + } else + --flagcount; + vertices[off+i].type = flags; + } + + // now load x coordinates + x=0; + for (i=0; i < n; ++i) { + flags = vertices[off+i].type; + if (flags & 2) { + stbtt_int16 dx = *points++; + x += (flags & 16) ? dx : -dx; // ??? + } else { + if (!(flags & 16)) { + x = x + (stbtt_int16) (points[0]*256 + points[1]); + points += 2; + } + } + vertices[off+i].x = (stbtt_int16) x; + } + + // now load y coordinates + y=0; + for (i=0; i < n; ++i) { + flags = vertices[off+i].type; + if (flags & 4) { + stbtt_int16 dy = *points++; + y += (flags & 32) ? dy : -dy; // ??? + } else { + if (!(flags & 32)) { + y = y + (stbtt_int16) (points[0]*256 + points[1]); + points += 2; + } + } + vertices[off+i].y = (stbtt_int16) y; + } + + // now convert them to our format + num_vertices=0; + sx = sy = cx = cy = scx = scy = 0; + for (i=0; i < n; ++i) { + flags = vertices[off+i].type; + x = (stbtt_int16) vertices[off+i].x; + y = (stbtt_int16) vertices[off+i].y; + + if (next_move == i) { + if (i != 0) + num_vertices = stbtt__close_shape(vertices, num_vertices, was_off, start_off, sx,sy,scx,scy,cx,cy); + + // now start the new one + start_off = !(flags & 1); + if (start_off) { + // if we start off with an off-curve point, then when we need to find a point on the curve + // where we can start, and we need to save some state for when we wraparound. + scx = x; + scy = y; + if (!(vertices[off+i+1].type & 1)) { + // next point is also a curve point, so interpolate an on-point curve + sx = (x + (stbtt_int32) vertices[off+i+1].x) >> 1; + sy = (y + (stbtt_int32) vertices[off+i+1].y) >> 1; + } else { + // otherwise just use the next point as our start point + sx = (stbtt_int32) vertices[off+i+1].x; + sy = (stbtt_int32) vertices[off+i+1].y; + ++i; // we're using point i+1 as the starting point, so skip it + } + } else { + sx = x; + sy = y; + } + stbtt_setvertex(&vertices[num_vertices++], STBTT_vmove,sx,sy,0,0); + was_off = 0; + next_move = 1 + ttUSHORT(endPtsOfContours+j*2); + ++j; + } else { + if (!(flags & 1)) { // if it's a curve + if (was_off) // two off-curve control points in a row means interpolate an on-curve midpoint + stbtt_setvertex(&vertices[num_vertices++], STBTT_vcurve, (cx+x)>>1, (cy+y)>>1, cx, cy); + cx = x; + cy = y; + was_off = 1; + } else { + if (was_off) + stbtt_setvertex(&vertices[num_vertices++], STBTT_vcurve, x,y, cx, cy); + else + stbtt_setvertex(&vertices[num_vertices++], STBTT_vline, x,y,0,0); + was_off = 0; + } + } + } + num_vertices = stbtt__close_shape(vertices, num_vertices, was_off, start_off, sx,sy,scx,scy,cx,cy); + } else if (numberOfContours == -1) { + // Compound shapes. + int more = 1; + stbtt_uint8 *comp = data + g + 10; + num_vertices = 0; + vertices = 0; + while (more) { + stbtt_uint16 flags, gidx; + int comp_num_verts = 0, i; + stbtt_vertex *comp_verts = 0, *tmp = 0; + float mtx[6] = {1,0,0,1,0,0}, m, n; + + flags = ttSHORT(comp); comp+=2; + gidx = ttSHORT(comp); comp+=2; + + if (flags & 2) { // XY values + if (flags & 1) { // shorts + mtx[4] = ttSHORT(comp); comp+=2; + mtx[5] = ttSHORT(comp); comp+=2; + } else { + mtx[4] = ttCHAR(comp); comp+=1; + mtx[5] = ttCHAR(comp); comp+=1; + } + } + else { + // @TODO handle matching point + STBTT_assert(0); + } + if (flags & (1<<3)) { // WE_HAVE_A_SCALE + mtx[0] = mtx[3] = ttSHORT(comp)/16384.0f; comp+=2; + mtx[1] = mtx[2] = 0; + } else if (flags & (1<<6)) { // WE_HAVE_AN_X_AND_YSCALE + mtx[0] = ttSHORT(comp)/16384.0f; comp+=2; + mtx[1] = mtx[2] = 0; + mtx[3] = ttSHORT(comp)/16384.0f; comp+=2; + } else if (flags & (1<<7)) { // WE_HAVE_A_TWO_BY_TWO + mtx[0] = ttSHORT(comp)/16384.0f; comp+=2; + mtx[1] = ttSHORT(comp)/16384.0f; comp+=2; + mtx[2] = ttSHORT(comp)/16384.0f; comp+=2; + mtx[3] = ttSHORT(comp)/16384.0f; comp+=2; + } + + // Find transformation scales. + m = (float) STBTT_sqrt(mtx[0]*mtx[0] + mtx[1]*mtx[1]); + n = (float) STBTT_sqrt(mtx[2]*mtx[2] + mtx[3]*mtx[3]); + + // Get indexed glyph. + comp_num_verts = stbtt_GetGlyphShape(info, gidx, &comp_verts); + if (comp_num_verts > 0) { + // Transform vertices. + for (i = 0; i < comp_num_verts; ++i) { + stbtt_vertex* v = &comp_verts[i]; + stbtt_vertex_type x,y; + x=v->x; y=v->y; + v->x = (stbtt_vertex_type)(m * (mtx[0]*x + mtx[2]*y + mtx[4])); + v->y = (stbtt_vertex_type)(n * (mtx[1]*x + mtx[3]*y + mtx[5])); + x=v->cx; y=v->cy; + v->cx = (stbtt_vertex_type)(m * (mtx[0]*x + mtx[2]*y + mtx[4])); + v->cy = (stbtt_vertex_type)(n * (mtx[1]*x + mtx[3]*y + mtx[5])); + } + // Append vertices. + tmp = (stbtt_vertex*)STBTT_malloc((num_vertices+comp_num_verts)*sizeof(stbtt_vertex), info->userdata); + if (!tmp) { + if (vertices) STBTT_free(vertices, info->userdata); + if (comp_verts) STBTT_free(comp_verts, info->userdata); + return 0; + } + if (num_vertices > 0) STBTT_memcpy(tmp, vertices, num_vertices*sizeof(stbtt_vertex)); + STBTT_memcpy(tmp+num_vertices, comp_verts, comp_num_verts*sizeof(stbtt_vertex)); + if (vertices) STBTT_free(vertices, info->userdata); + vertices = tmp; + STBTT_free(comp_verts, info->userdata); + num_vertices += comp_num_verts; + } + // More components ? + more = flags & (1<<5); + } + } else if (numberOfContours < 0) { + // @TODO other compound variations? + STBTT_assert(0); + } else { + // numberOfCounters == 0, do nothing + } + + *pvertices = vertices; + return num_vertices; +} + +STBTT_DEF void stbtt_GetGlyphHMetrics(const stbtt_fontinfo *info, int glyph_index, int *advanceWidth, int *leftSideBearing) +{ + stbtt_uint16 numOfLongHorMetrics = ttUSHORT(info->data+info->hhea + 34); + if (glyph_index < numOfLongHorMetrics) { + if (advanceWidth) *advanceWidth = ttSHORT(info->data + info->hmtx + 4*glyph_index); + if (leftSideBearing) *leftSideBearing = ttSHORT(info->data + info->hmtx + 4*glyph_index + 2); + } else { + if (advanceWidth) *advanceWidth = ttSHORT(info->data + info->hmtx + 4*(numOfLongHorMetrics-1)); + if (leftSideBearing) *leftSideBearing = ttSHORT(info->data + info->hmtx + 4*numOfLongHorMetrics + 2*(glyph_index - numOfLongHorMetrics)); + } +} + +STBTT_DEF int stbtt_GetGlyphKernAdvance(const stbtt_fontinfo *info, int glyph1, int glyph2) +{ + stbtt_uint8 *data = info->data + info->kern; + stbtt_uint32 needle, straw; + int l, r, m; + + // we only look at the first table. it must be 'horizontal' and format 0. + if (!info->kern) + return 0; + if (ttUSHORT(data+2) < 1) // number of tables, need at least 1 + return 0; + if (ttUSHORT(data+8) != 1) // horizontal flag must be set in format + return 0; + + l = 0; + r = ttUSHORT(data+10) - 1; + needle = glyph1 << 16 | glyph2; + while (l <= r) { + m = (l + r) >> 1; + straw = ttULONG(data+18+(m*6)); // note: unaligned read + if (needle < straw) + r = m - 1; + else if (needle > straw) + l = m + 1; + else + return ttSHORT(data+22+(m*6)); + } + return 0; +} + +STBTT_DEF int stbtt_GetCodepointKernAdvance(const stbtt_fontinfo *info, int ch1, int ch2) +{ + if (!info->kern) // if no kerning table, don't waste time looking up both codepoint->glyphs + return 0; + return stbtt_GetGlyphKernAdvance(info, stbtt_FindGlyphIndex(info,ch1), stbtt_FindGlyphIndex(info,ch2)); +} + +STBTT_DEF void stbtt_GetCodepointHMetrics(const stbtt_fontinfo *info, int codepoint, int *advanceWidth, int *leftSideBearing) +{ + stbtt_GetGlyphHMetrics(info, stbtt_FindGlyphIndex(info,codepoint), advanceWidth, leftSideBearing); +} + +STBTT_DEF void stbtt_GetFontVMetrics(const stbtt_fontinfo *info, int *ascent, int *descent, int *lineGap) +{ + if (ascent ) *ascent = ttSHORT(info->data+info->hhea + 4); + if (descent) *descent = ttSHORT(info->data+info->hhea + 6); + if (lineGap) *lineGap = ttSHORT(info->data+info->hhea + 8); +} + +STBTT_DEF void stbtt_GetFontBoundingBox(const stbtt_fontinfo *info, int *x0, int *y0, int *x1, int *y1) +{ + *x0 = ttSHORT(info->data + info->head + 36); + *y0 = ttSHORT(info->data + info->head + 38); + *x1 = ttSHORT(info->data + info->head + 40); + *y1 = ttSHORT(info->data + info->head + 42); +} + +STBTT_DEF float stbtt_ScaleForPixelHeight(const stbtt_fontinfo *info, float height) +{ + int fheight = ttSHORT(info->data + info->hhea + 4) - ttSHORT(info->data + info->hhea + 6); + return (float) height / fheight; +} + +STBTT_DEF float stbtt_ScaleForMappingEmToPixels(const stbtt_fontinfo *info, float pixels) +{ + int unitsPerEm = ttUSHORT(info->data + info->head + 18); + return pixels / unitsPerEm; +} + +STBTT_DEF void stbtt_FreeShape(const stbtt_fontinfo *info, stbtt_vertex *v) +{ + STBTT_free(v, info->userdata); +} + +////////////////////////////////////////////////////////////////////////////// +// +// antialiasing software rasterizer +// + +STBTT_DEF void stbtt_GetGlyphBitmapBoxSubpixel(const stbtt_fontinfo *font, int glyph, float scale_x, float scale_y,float shift_x, float shift_y, int *ix0, int *iy0, int *ix1, int *iy1) +{ + int x0=0,y0=0,x1,y1; // =0 suppresses compiler warning + if (!stbtt_GetGlyphBox(font, glyph, &x0,&y0,&x1,&y1)) { + // e.g. space character + if (ix0) *ix0 = 0; + if (iy0) *iy0 = 0; + if (ix1) *ix1 = 0; + if (iy1) *iy1 = 0; + } else { + // move to integral bboxes (treating pixels as little squares, what pixels get touched)? + if (ix0) *ix0 = STBTT_ifloor( x0 * scale_x + shift_x); + if (iy0) *iy0 = STBTT_ifloor(-y1 * scale_y + shift_y); + if (ix1) *ix1 = STBTT_iceil ( x1 * scale_x + shift_x); + if (iy1) *iy1 = STBTT_iceil (-y0 * scale_y + shift_y); + } +} + +STBTT_DEF void stbtt_GetGlyphBitmapBox(const stbtt_fontinfo *font, int glyph, float scale_x, float scale_y, int *ix0, int *iy0, int *ix1, int *iy1) +{ + stbtt_GetGlyphBitmapBoxSubpixel(font, glyph, scale_x, scale_y,0.0f,0.0f, ix0, iy0, ix1, iy1); +} + +STBTT_DEF void stbtt_GetCodepointBitmapBoxSubpixel(const stbtt_fontinfo *font, int codepoint, float scale_x, float scale_y, float shift_x, float shift_y, int *ix0, int *iy0, int *ix1, int *iy1) +{ + stbtt_GetGlyphBitmapBoxSubpixel(font, stbtt_FindGlyphIndex(font,codepoint), scale_x, scale_y,shift_x,shift_y, ix0,iy0,ix1,iy1); +} + +STBTT_DEF void stbtt_GetCodepointBitmapBox(const stbtt_fontinfo *font, int codepoint, float scale_x, float scale_y, int *ix0, int *iy0, int *ix1, int *iy1) +{ + stbtt_GetCodepointBitmapBoxSubpixel(font, codepoint, scale_x, scale_y,0.0f,0.0f, ix0,iy0,ix1,iy1); +} + +////////////////////////////////////////////////////////////////////////////// +// +// Rasterizer + +typedef struct stbtt__hheap_chunk +{ + struct stbtt__hheap_chunk *next; +} stbtt__hheap_chunk; + +typedef struct stbtt__hheap +{ + struct stbtt__hheap_chunk *head; + void *first_free; + int num_remaining_in_head_chunk; +} stbtt__hheap; + +static void *stbtt__hheap_alloc(stbtt__hheap *hh, size_t size, void *userdata) +{ + if (hh->first_free) { + void *p = hh->first_free; + hh->first_free = * (void **) p; + return p; + } else { + if (hh->num_remaining_in_head_chunk == 0) { + int count = (size < 32 ? 2000 : size < 128 ? 800 : 100); + stbtt__hheap_chunk *c = (stbtt__hheap_chunk *) STBTT_malloc(sizeof(stbtt__hheap_chunk) + size * count, userdata); + if (c == NULL) + return NULL; + c->next = hh->head; + hh->head = c; + hh->num_remaining_in_head_chunk = count; + } + --hh->num_remaining_in_head_chunk; + return (char *) (hh->head) + size * hh->num_remaining_in_head_chunk; + } +} + +static void stbtt__hheap_free(stbtt__hheap *hh, void *p) +{ + *(void **) p = hh->first_free; + hh->first_free = p; +} + +static void stbtt__hheap_cleanup(stbtt__hheap *hh, void *userdata) +{ + stbtt__hheap_chunk *c = hh->head; + while (c) { + stbtt__hheap_chunk *n = c->next; + STBTT_free(c, userdata); + c = n; + } +} + +typedef struct stbtt__edge { + float x0,y0, x1,y1; + int invert; +} stbtt__edge; + + +typedef struct stbtt__active_edge +{ + struct stbtt__active_edge *next; + #if STBTT_RASTERIZER_VERSION==1 + int x,dx; + float ey; + int direction; + #elif STBTT_RASTERIZER_VERSION==2 + float fx,fdx,fdy; + float direction; + float sy; + float ey; + #else + #error "Unrecognized value of STBTT_RASTERIZER_VERSION" + #endif +} stbtt__active_edge; + +#if STBTT_RASTERIZER_VERSION == 1 +#define STBTT_FIXSHIFT 10 +#define STBTT_FIX (1 << STBTT_FIXSHIFT) +#define STBTT_FIXMASK (STBTT_FIX-1) + +static stbtt__active_edge *stbtt__new_active(stbtt__hheap *hh, stbtt__edge *e, int off_x, float start_point, void *userdata) +{ + stbtt__active_edge *z = (stbtt__active_edge *) stbtt__hheap_alloc(hh, sizeof(*z), userdata); + float dxdy = (e->x1 - e->x0) / (e->y1 - e->y0); + STBTT_assert(z != NULL); + if (!z) return z; + + // round dx down to avoid overshooting + if (dxdy < 0) + z->dx = -STBTT_ifloor(STBTT_FIX * -dxdy); + else + z->dx = STBTT_ifloor(STBTT_FIX * dxdy); + + z->x = STBTT_ifloor(STBTT_FIX * e->x0 + z->dx * (start_point - e->y0)); // use z->dx so when we offset later it's by the same amount + z->x -= off_x * STBTT_FIX; + + z->ey = e->y1; + z->next = 0; + z->direction = e->invert ? 1 : -1; + return z; +} +#elif STBTT_RASTERIZER_VERSION == 2 +static stbtt__active_edge *stbtt__new_active(stbtt__hheap *hh, stbtt__edge *e, int off_x, float start_point, void *userdata) +{ + stbtt__active_edge *z = (stbtt__active_edge *) stbtt__hheap_alloc(hh, sizeof(*z), userdata); + float dxdy = (e->x1 - e->x0) / (e->y1 - e->y0); + STBTT_assert(z != NULL); + //STBTT_assert(e->y0 <= start_point); + if (!z) return z; + z->fdx = dxdy; + z->fdy = dxdy != 0.0f ? (1.0f/dxdy) : 0.0f; + z->fx = e->x0 + dxdy * (start_point - e->y0); + z->fx -= off_x; + z->direction = e->invert ? 1.0f : -1.0f; + z->sy = e->y0; + z->ey = e->y1; + z->next = 0; + return z; +} +#else +#error "Unrecognized value of STBTT_RASTERIZER_VERSION" +#endif + +#if STBTT_RASTERIZER_VERSION == 1 +// note: this routine clips fills that extend off the edges... ideally this +// wouldn't happen, but it could happen if the truetype glyph bounding boxes +// are wrong, or if the user supplies a too-small bitmap +static void stbtt__fill_active_edges(unsigned char *scanline, int len, stbtt__active_edge *e, int max_weight) +{ + // non-zero winding fill + int x0=0, w=0; + + while (e) { + if (w == 0) { + // if we're currently at zero, we need to record the edge start point + x0 = e->x; w += e->direction; + } else { + int x1 = e->x; w += e->direction; + // if we went to zero, we need to draw + if (w == 0) { + int i = x0 >> STBTT_FIXSHIFT; + int j = x1 >> STBTT_FIXSHIFT; + + if (i < len && j >= 0) { + if (i == j) { + // x0,x1 are the same pixel, so compute combined coverage + scanline[i] = scanline[i] + (stbtt_uint8) ((x1 - x0) * max_weight >> STBTT_FIXSHIFT); + } else { + if (i >= 0) // add antialiasing for x0 + scanline[i] = scanline[i] + (stbtt_uint8) (((STBTT_FIX - (x0 & STBTT_FIXMASK)) * max_weight) >> STBTT_FIXSHIFT); + else + i = -1; // clip + + if (j < len) // add antialiasing for x1 + scanline[j] = scanline[j] + (stbtt_uint8) (((x1 & STBTT_FIXMASK) * max_weight) >> STBTT_FIXSHIFT); + else + j = len; // clip + + for (++i; i < j; ++i) // fill pixels between x0 and x1 + scanline[i] = scanline[i] + (stbtt_uint8) max_weight; + } + } + } + } + + e = e->next; + } +} + +static void stbtt__rasterize_sorted_edges(stbtt__bitmap *result, stbtt__edge *e, int n, int vsubsample, int off_x, int off_y, void *userdata) +{ + stbtt__hheap hh = { 0, 0, 0 }; + stbtt__active_edge *active = NULL; + int y,j=0; + int max_weight = (255 / vsubsample); // weight per vertical scanline + int s; // vertical subsample index + unsigned char scanline_data[512], *scanline; + + if (result->w > 512) + scanline = (unsigned char *) STBTT_malloc(result->w, userdata); + else + scanline = scanline_data; + + y = off_y * vsubsample; + e[n].y0 = (off_y + result->h) * (float) vsubsample + 1; + + while (j < result->h) { + STBTT_memset(scanline, 0, result->w); + for (s=0; s < vsubsample; ++s) { + // find center of pixel for this scanline + float scan_y = y + 0.5f; + stbtt__active_edge **step = &active; + + // update all active edges; + // remove all active edges that terminate before the center of this scanline + while (*step) { + stbtt__active_edge * z = *step; + if (z->ey <= scan_y) { + *step = z->next; // delete from list + STBTT_assert(z->direction); + z->direction = 0; + stbtt__hheap_free(&hh, z); + } else { + z->x += z->dx; // advance to position for current scanline + step = &((*step)->next); // advance through list + } + } + + // resort the list if needed + for(;;) { + int changed=0; + step = &active; + while (*step && (*step)->next) { + if ((*step)->x > (*step)->next->x) { + stbtt__active_edge *t = *step; + stbtt__active_edge *q = t->next; + + t->next = q->next; + q->next = t; + *step = q; + changed = 1; + } + step = &(*step)->next; + } + if (!changed) break; + } + + // insert all edges that start before the center of this scanline -- omit ones that also end on this scanline + while (e->y0 <= scan_y) { + if (e->y1 > scan_y) { + stbtt__active_edge *z = stbtt__new_active(&hh, e, off_x, scan_y, userdata); + if (z != NULL) { + // find insertion point + if (active == NULL) + active = z; + else if (z->x < active->x) { + // insert at front + z->next = active; + active = z; + } else { + // find thing to insert AFTER + stbtt__active_edge *p = active; + while (p->next && p->next->x < z->x) + p = p->next; + // at this point, p->next->x is NOT < z->x + z->next = p->next; + p->next = z; + } + } + } + ++e; + } + + // now process all active edges in XOR fashion + if (active) + stbtt__fill_active_edges(scanline, result->w, active, max_weight); + + ++y; + } + STBTT_memcpy(result->pixels + j * result->stride, scanline, result->w); + ++j; + } + + stbtt__hheap_cleanup(&hh, userdata); + + if (scanline != scanline_data) + STBTT_free(scanline, userdata); +} + +#elif STBTT_RASTERIZER_VERSION == 2 + +// the edge passed in here does not cross the vertical line at x or the vertical line at x+1 +// (i.e. it has already been clipped to those) +static void stbtt__handle_clipped_edge(float *scanline, int x, stbtt__active_edge *e, float x0, float y0, float x1, float y1) +{ + if (y0 == y1) return; + STBTT_assert(y0 < y1); + STBTT_assert(e->sy <= e->ey); + if (y0 > e->ey) return; + if (y1 < e->sy) return; + if (y0 < e->sy) { + x0 += (x1-x0) * (e->sy - y0) / (y1-y0); + y0 = e->sy; + } + if (y1 > e->ey) { + x1 += (x1-x0) * (e->ey - y1) / (y1-y0); + y1 = e->ey; + } + + if (x0 == x) + STBTT_assert(x1 <= x+1); + else if (x0 == x+1) + STBTT_assert(x1 >= x); + else if (x0 <= x) + STBTT_assert(x1 <= x); + else if (x0 >= x+1) + STBTT_assert(x1 >= x+1); + else + STBTT_assert(x1 >= x && x1 <= x+1); + + if (x0 <= x && x1 <= x) + scanline[x] += e->direction * (y1-y0); + else if (x0 >= x+1 && x1 >= x+1) + ; + else { + STBTT_assert(x0 >= x && x0 <= x+1 && x1 >= x && x1 <= x+1); + scanline[x] += e->direction * (y1-y0) * (1-((x0-x)+(x1-x))/2); // coverage = 1 - average x position + } +} + +static void stbtt__fill_active_edges_new(float *scanline, float *scanline_fill, int len, stbtt__active_edge *e, float y_top) +{ + float y_bottom = y_top+1; + + while (e) { + // brute force every pixel + + // compute intersection points with top & bottom + STBTT_assert(e->ey >= y_top); + + if (e->fdx == 0) { + float x0 = e->fx; + if (x0 < len) { + if (x0 >= 0) { + stbtt__handle_clipped_edge(scanline,(int) x0,e, x0,y_top, x0,y_bottom); + stbtt__handle_clipped_edge(scanline_fill-1,(int) x0+1,e, x0,y_top, x0,y_bottom); + } else { + stbtt__handle_clipped_edge(scanline_fill-1,0,e, x0,y_top, x0,y_bottom); + } + } + } else { + float x0 = e->fx; + float dx = e->fdx; + float xb = x0 + dx; + float x_top, x_bottom; + float sy0,sy1; + float dy = e->fdy; + STBTT_assert(e->sy <= y_bottom && e->ey >= y_top); + + // compute endpoints of line segment clipped to this scanline (if the + // line segment starts on this scanline. x0 is the intersection of the + // line with y_top, but that may be off the line segment. + if (e->sy > y_top) { + x_top = x0 + dx * (e->sy - y_top); + sy0 = e->sy; + } else { + x_top = x0; + sy0 = y_top; + } + if (e->ey < y_bottom) { + x_bottom = x0 + dx * (e->ey - y_top); + sy1 = e->ey; + } else { + x_bottom = xb; + sy1 = y_bottom; + } + + if (x_top >= 0 && x_bottom >= 0 && x_top < len && x_bottom < len) { + // from here on, we don't have to range check x values + + if ((int) x_top == (int) x_bottom) { + float height; + // simple case, only spans one pixel + int x = (int) x_top; + height = sy1 - sy0; + STBTT_assert(x >= 0 && x < len); + scanline[x] += e->direction * (1-((x_top - x) + (x_bottom-x))/2) * height; + scanline_fill[x] += e->direction * height; // everything right of this pixel is filled + } else { + int x,x1,x2; + float y_crossing, step, sign, area; + // covers 2+ pixels + if (x_top > x_bottom) { + // flip scanline vertically; signed area is the same + float t; + sy0 = y_bottom - (sy0 - y_top); + sy1 = y_bottom - (sy1 - y_top); + t = sy0, sy0 = sy1, sy1 = t; + t = x_bottom, x_bottom = x_top, x_top = t; + dx = -dx; + dy = -dy; + t = x0, x0 = xb, xb = t; + } + + x1 = (int) x_top; + x2 = (int) x_bottom; + // compute intersection with y axis at x1+1 + y_crossing = (x1+1 - x0) * dy + y_top; + + sign = e->direction; + // area of the rectangle covered from y0..y_crossing + area = sign * (y_crossing-sy0); + // area of the triangle (x_top,y0), (x+1,y0), (x+1,y_crossing) + scanline[x1] += area * (1-((x_top - x1)+(x1+1-x1))/2); + + step = sign * dy; + for (x = x1+1; x < x2; ++x) { + scanline[x] += area + step/2; + area += step; + } + y_crossing += dy * (x2 - (x1+1)); + + STBTT_assert(STBTT_fabs(area) <= 1.01f); + + scanline[x2] += area + sign * (1-((x2-x2)+(x_bottom-x2))/2) * (sy1-y_crossing); + + scanline_fill[x2] += sign * (sy1-sy0); + } + } else { + // if edge goes outside of box we're drawing, we require + // clipping logic. since this does not match the intended use + // of this library, we use a different, very slow brute + // force implementation + int x; + for (x=0; x < len; ++x) { + // cases: + // + // there can be up to two intersections with the pixel. any intersection + // with left or right edges can be handled by splitting into two (or three) + // regions. intersections with top & bottom do not necessitate case-wise logic. + // + // the old way of doing this found the intersections with the left & right edges, + // then used some simple logic to produce up to three segments in sorted order + // from top-to-bottom. however, this had a problem: if an x edge was epsilon + // across the x border, then the corresponding y position might not be distinct + // from the other y segment, and it might ignored as an empty segment. to avoid + // that, we need to explicitly produce segments based on x positions. + + // rename variables to clear pairs + float y0 = y_top; + float x1 = (float) (x); + float x2 = (float) (x+1); + float x3 = xb; + float y3 = y_bottom; + float y1,y2; + + // x = e->x + e->dx * (y-y_top) + // (y-y_top) = (x - e->x) / e->dx + // y = (x - e->x) / e->dx + y_top + y1 = (x - x0) / dx + y_top; + y2 = (x+1 - x0) / dx + y_top; + + if (x0 < x1 && x3 > x2) { // three segments descending down-right + stbtt__handle_clipped_edge(scanline,x,e, x0,y0, x1,y1); + stbtt__handle_clipped_edge(scanline,x,e, x1,y1, x2,y2); + stbtt__handle_clipped_edge(scanline,x,e, x2,y2, x3,y3); + } else if (x3 < x1 && x0 > x2) { // three segments descending down-left + stbtt__handle_clipped_edge(scanline,x,e, x0,y0, x2,y2); + stbtt__handle_clipped_edge(scanline,x,e, x2,y2, x1,y1); + stbtt__handle_clipped_edge(scanline,x,e, x1,y1, x3,y3); + } else if (x0 < x1 && x3 > x1) { // two segments across x, down-right + stbtt__handle_clipped_edge(scanline,x,e, x0,y0, x1,y1); + stbtt__handle_clipped_edge(scanline,x,e, x1,y1, x3,y3); + } else if (x3 < x1 && x0 > x1) { // two segments across x, down-left + stbtt__handle_clipped_edge(scanline,x,e, x0,y0, x1,y1); + stbtt__handle_clipped_edge(scanline,x,e, x1,y1, x3,y3); + } else if (x0 < x2 && x3 > x2) { // two segments across x+1, down-right + stbtt__handle_clipped_edge(scanline,x,e, x0,y0, x2,y2); + stbtt__handle_clipped_edge(scanline,x,e, x2,y2, x3,y3); + } else if (x3 < x2 && x0 > x2) { // two segments across x+1, down-left + stbtt__handle_clipped_edge(scanline,x,e, x0,y0, x2,y2); + stbtt__handle_clipped_edge(scanline,x,e, x2,y2, x3,y3); + } else { // one segment + stbtt__handle_clipped_edge(scanline,x,e, x0,y0, x3,y3); + } + } + } + } + e = e->next; + } +} + +// directly AA rasterize edges w/o supersampling +static void stbtt__rasterize_sorted_edges(stbtt__bitmap *result, stbtt__edge *e, int n, int vsubsample, int off_x, int off_y, void *userdata) +{ + stbtt__hheap hh = { 0, 0, 0 }; + stbtt__active_edge *active = NULL; + int y,j=0, i; + float scanline_data[129], *scanline, *scanline2; + + STBTT__NOTUSED(vsubsample); + + if (result->w > 64) + scanline = (float *) STBTT_malloc((result->w*2+1) * sizeof(float), userdata); + else + scanline = scanline_data; + + scanline2 = scanline + result->w; + + y = off_y; + e[n].y0 = (float) (off_y + result->h) + 1; + + while (j < result->h) { + // find center of pixel for this scanline + float scan_y_top = y + 0.0f; + float scan_y_bottom = y + 1.0f; + stbtt__active_edge **step = &active; + + STBTT_memset(scanline , 0, result->w*sizeof(scanline[0])); + STBTT_memset(scanline2, 0, (result->w+1)*sizeof(scanline[0])); + + // update all active edges; + // remove all active edges that terminate before the top of this scanline + while (*step) { + stbtt__active_edge * z = *step; + if (z->ey <= scan_y_top) { + *step = z->next; // delete from list + STBTT_assert(z->direction); + z->direction = 0; + stbtt__hheap_free(&hh, z); + } else { + step = &((*step)->next); // advance through list + } + } + + // insert all edges that start before the bottom of this scanline + while (e->y0 <= scan_y_bottom) { + if (e->y0 != e->y1) { + stbtt__active_edge *z = stbtt__new_active(&hh, e, off_x, scan_y_top, userdata); + if (z != NULL) { + STBTT_assert(z->ey >= scan_y_top); + // insert at front + z->next = active; + active = z; + } + } + ++e; + } + + // now process all active edges + if (active) + stbtt__fill_active_edges_new(scanline, scanline2+1, result->w, active, scan_y_top); + + { + float sum = 0; + for (i=0; i < result->w; ++i) { + float k; + int m; + sum += scanline2[i]; + k = scanline[i] + sum; + k = (float) STBTT_fabs(k)*255 + 0.5f; + m = (int) k; + if (m > 255) m = 255; + result->pixels[j*result->stride + i] = (unsigned char) m; + } + } + // advance all the edges + step = &active; + while (*step) { + stbtt__active_edge *z = *step; + z->fx += z->fdx; // advance to position for current scanline + step = &((*step)->next); // advance through list + } + + ++y; + ++j; + } + + stbtt__hheap_cleanup(&hh, userdata); + + if (scanline != scanline_data) + STBTT_free(scanline, userdata); +} +#else +#error "Unrecognized value of STBTT_RASTERIZER_VERSION" +#endif + +#define STBTT__COMPARE(a,b) ((a)->y0 < (b)->y0) + +static void stbtt__sort_edges_ins_sort(stbtt__edge *p, int n) +{ + int i,j; + for (i=1; i < n; ++i) { + stbtt__edge t = p[i], *a = &t; + j = i; + while (j > 0) { + stbtt__edge *b = &p[j-1]; + int c = STBTT__COMPARE(a,b); + if (!c) break; + p[j] = p[j-1]; + --j; + } + if (i != j) + p[j] = t; + } +} + +static void stbtt__sort_edges_quicksort(stbtt__edge *p, int n) +{ + /* threshhold for transitioning to insertion sort */ + while (n > 12) { + stbtt__edge t; + int c01,c12,c,m,i,j; + + /* compute median of three */ + m = n >> 1; + c01 = STBTT__COMPARE(&p[0],&p[m]); + c12 = STBTT__COMPARE(&p[m],&p[n-1]); + /* if 0 >= mid >= end, or 0 < mid < end, then use mid */ + if (c01 != c12) { + /* otherwise, we'll need to swap something else to middle */ + int z; + c = STBTT__COMPARE(&p[0],&p[n-1]); + /* 0>mid && midn => n; 0 0 */ + /* 0n: 0>n => 0; 0 n */ + z = (c == c12) ? 0 : n-1; + t = p[z]; + p[z] = p[m]; + p[m] = t; + } + /* now p[m] is the median-of-three */ + /* swap it to the beginning so it won't move around */ + t = p[0]; + p[0] = p[m]; + p[m] = t; + + /* partition loop */ + i=1; + j=n-1; + for(;;) { + /* handling of equality is crucial here */ + /* for sentinels & efficiency with duplicates */ + for (;;++i) { + if (!STBTT__COMPARE(&p[i], &p[0])) break; + } + for (;;--j) { + if (!STBTT__COMPARE(&p[0], &p[j])) break; + } + /* make sure we haven't crossed */ + if (i >= j) break; + t = p[i]; + p[i] = p[j]; + p[j] = t; + + ++i; + --j; + } + /* recurse on smaller side, iterate on larger */ + if (j < (n-i)) { + stbtt__sort_edges_quicksort(p,j); + p = p+i; + n = n-i; + } else { + stbtt__sort_edges_quicksort(p+i, n-i); + n = j; + } + } +} + +static void stbtt__sort_edges(stbtt__edge *p, int n) +{ + stbtt__sort_edges_quicksort(p, n); + stbtt__sort_edges_ins_sort(p, n); +} + +typedef struct +{ + float x,y; +} stbtt__point; + +static void stbtt__rasterize(stbtt__bitmap *result, stbtt__point *pts, int *wcount, int windings, float scale_x, float scale_y, float shift_x, float shift_y, int off_x, int off_y, int invert, void *userdata) +{ + float y_scale_inv = invert ? -scale_y : scale_y; + stbtt__edge *e; + int n,i,j,k,m; +#if STBTT_RASTERIZER_VERSION == 1 + int vsubsample = result->h < 8 ? 15 : 5; +#elif STBTT_RASTERIZER_VERSION == 2 + int vsubsample = 1; +#else + #error "Unrecognized value of STBTT_RASTERIZER_VERSION" +#endif + // vsubsample should divide 255 evenly; otherwise we won't reach full opacity + + // now we have to blow out the windings into explicit edge lists + n = 0; + for (i=0; i < windings; ++i) + n += wcount[i]; + + e = (stbtt__edge *) STBTT_malloc(sizeof(*e) * (n+1), userdata); // add an extra one as a sentinel + if (e == 0) return; + n = 0; + + m=0; + for (i=0; i < windings; ++i) { + stbtt__point *p = pts + m; + m += wcount[i]; + j = wcount[i]-1; + for (k=0; k < wcount[i]; j=k++) { + int a=k,b=j; + // skip the edge if horizontal + if (p[j].y == p[k].y) + continue; + // add edge from j to k to the list + e[n].invert = 0; + if (invert ? p[j].y > p[k].y : p[j].y < p[k].y) { + e[n].invert = 1; + a=j,b=k; + } + e[n].x0 = p[a].x * scale_x + shift_x; + e[n].y0 = (p[a].y * y_scale_inv + shift_y) * vsubsample; + e[n].x1 = p[b].x * scale_x + shift_x; + e[n].y1 = (p[b].y * y_scale_inv + shift_y) * vsubsample; + ++n; + } + } + + // now sort the edges by their highest point (should snap to integer, and then by x) + //STBTT_sort(e, n, sizeof(e[0]), stbtt__edge_compare); + stbtt__sort_edges(e, n); + + // now, traverse the scanlines and find the intersections on each scanline, use xor winding rule + stbtt__rasterize_sorted_edges(result, e, n, vsubsample, off_x, off_y, userdata); + + STBTT_free(e, userdata); +} + +static void stbtt__add_point(stbtt__point *points, int n, float x, float y) +{ + if (!points) return; // during first pass, it's unallocated + points[n].x = x; + points[n].y = y; +} + +// tesselate until threshhold p is happy... @TODO warped to compensate for non-linear stretching +static int stbtt__tesselate_curve(stbtt__point *points, int *num_points, float x0, float y0, float x1, float y1, float x2, float y2, float objspace_flatness_squared, int n) +{ + // midpoint + float mx = (x0 + 2*x1 + x2)/4; + float my = (y0 + 2*y1 + y2)/4; + // versus directly drawn line + float dx = (x0+x2)/2 - mx; + float dy = (y0+y2)/2 - my; + if (n > 16) // 65536 segments on one curve better be enough! + return 1; + if (dx*dx+dy*dy > objspace_flatness_squared) { // half-pixel error allowed... need to be smaller if AA + stbtt__tesselate_curve(points, num_points, x0,y0, (x0+x1)/2.0f,(y0+y1)/2.0f, mx,my, objspace_flatness_squared,n+1); + stbtt__tesselate_curve(points, num_points, mx,my, (x1+x2)/2.0f,(y1+y2)/2.0f, x2,y2, objspace_flatness_squared,n+1); + } else { + stbtt__add_point(points, *num_points,x2,y2); + *num_points = *num_points+1; + } + return 1; +} + +// returns number of contours +static stbtt__point *stbtt_FlattenCurves(stbtt_vertex *vertices, int num_verts, float objspace_flatness, int **contour_lengths, int *num_contours, void *userdata) +{ + stbtt__point *points=0; + int num_points=0; + + float objspace_flatness_squared = objspace_flatness * objspace_flatness; + int i,n=0,start=0, pass; + + // count how many "moves" there are to get the contour count + for (i=0; i < num_verts; ++i) + if (vertices[i].type == STBTT_vmove) + ++n; + + *num_contours = n; + if (n == 0) return 0; + + *contour_lengths = (int *) STBTT_malloc(sizeof(**contour_lengths) * n, userdata); + + if (*contour_lengths == 0) { + *num_contours = 0; + return 0; + } + + // make two passes through the points so we don't need to realloc + for (pass=0; pass < 2; ++pass) { + float x=0,y=0; + if (pass == 1) { + points = (stbtt__point *) STBTT_malloc(num_points * sizeof(points[0]), userdata); + if (points == NULL) goto error; + } + num_points = 0; + n= -1; + for (i=0; i < num_verts; ++i) { + switch (vertices[i].type) { + case STBTT_vmove: + // start the next contour + if (n >= 0) + (*contour_lengths)[n] = num_points - start; + ++n; + start = num_points; + + x = vertices[i].x, y = vertices[i].y; + stbtt__add_point(points, num_points++, x,y); + break; + case STBTT_vline: + x = vertices[i].x, y = vertices[i].y; + stbtt__add_point(points, num_points++, x, y); + break; + case STBTT_vcurve: + stbtt__tesselate_curve(points, &num_points, x,y, + vertices[i].cx, vertices[i].cy, + vertices[i].x, vertices[i].y, + objspace_flatness_squared, 0); + x = vertices[i].x, y = vertices[i].y; + break; + } + } + (*contour_lengths)[n] = num_points - start; + } + + return points; +error: + STBTT_free(points, userdata); + STBTT_free(*contour_lengths, userdata); + *contour_lengths = 0; + *num_contours = 0; + return NULL; +} + +STBTT_DEF void stbtt_Rasterize(stbtt__bitmap *result, float flatness_in_pixels, stbtt_vertex *vertices, int num_verts, float scale_x, float scale_y, float shift_x, float shift_y, int x_off, int y_off, int invert, void *userdata) +{ + float scale = scale_x > scale_y ? scale_y : scale_x; + int winding_count, *winding_lengths; + stbtt__point *windings = stbtt_FlattenCurves(vertices, num_verts, flatness_in_pixels / scale, &winding_lengths, &winding_count, userdata); + if (windings) { + stbtt__rasterize(result, windings, winding_lengths, winding_count, scale_x, scale_y, shift_x, shift_y, x_off, y_off, invert, userdata); + STBTT_free(winding_lengths, userdata); + STBTT_free(windings, userdata); + } +} + +STBTT_DEF void stbtt_FreeBitmap(unsigned char *bitmap, void *userdata) +{ + STBTT_free(bitmap, userdata); +} + +STBTT_DEF unsigned char *stbtt_GetGlyphBitmapSubpixel(const stbtt_fontinfo *info, float scale_x, float scale_y, float shift_x, float shift_y, int glyph, int *width, int *height, int *xoff, int *yoff) +{ + int ix0,iy0,ix1,iy1; + stbtt__bitmap gbm; + stbtt_vertex *vertices; + int num_verts = stbtt_GetGlyphShape(info, glyph, &vertices); + + if (scale_x == 0) scale_x = scale_y; + if (scale_y == 0) { + if (scale_x == 0) { + STBTT_free(vertices, info->userdata); + return NULL; + } + scale_y = scale_x; + } + + stbtt_GetGlyphBitmapBoxSubpixel(info, glyph, scale_x, scale_y, shift_x, shift_y, &ix0,&iy0,&ix1,&iy1); + + // now we get the size + gbm.w = (ix1 - ix0); + gbm.h = (iy1 - iy0); + gbm.pixels = NULL; // in case we error + + if (width ) *width = gbm.w; + if (height) *height = gbm.h; + if (xoff ) *xoff = ix0; + if (yoff ) *yoff = iy0; + + if (gbm.w && gbm.h) { + gbm.pixels = (unsigned char *) STBTT_malloc(gbm.w * gbm.h, info->userdata); + if (gbm.pixels) { + gbm.stride = gbm.w; + + stbtt_Rasterize(&gbm, 0.35f, vertices, num_verts, scale_x, scale_y, shift_x, shift_y, ix0, iy0, 1, info->userdata); + } + } + STBTT_free(vertices, info->userdata); + return gbm.pixels; +} + +STBTT_DEF unsigned char *stbtt_GetGlyphBitmap(const stbtt_fontinfo *info, float scale_x, float scale_y, int glyph, int *width, int *height, int *xoff, int *yoff) +{ + return stbtt_GetGlyphBitmapSubpixel(info, scale_x, scale_y, 0.0f, 0.0f, glyph, width, height, xoff, yoff); +} + +STBTT_DEF void stbtt_MakeGlyphBitmapSubpixel(const stbtt_fontinfo *info, unsigned char *output, int out_w, int out_h, int out_stride, float scale_x, float scale_y, float shift_x, float shift_y, int glyph) +{ + int ix0,iy0; + stbtt_vertex *vertices; + int num_verts = stbtt_GetGlyphShape(info, glyph, &vertices); + stbtt__bitmap gbm; + + stbtt_GetGlyphBitmapBoxSubpixel(info, glyph, scale_x, scale_y, shift_x, shift_y, &ix0,&iy0,0,0); + gbm.pixels = output; + gbm.w = out_w; + gbm.h = out_h; + gbm.stride = out_stride; + + if (gbm.w && gbm.h) + stbtt_Rasterize(&gbm, 0.35f, vertices, num_verts, scale_x, scale_y, shift_x, shift_y, ix0,iy0, 1, info->userdata); + + STBTT_free(vertices, info->userdata); +} + +STBTT_DEF void stbtt_MakeGlyphBitmap(const stbtt_fontinfo *info, unsigned char *output, int out_w, int out_h, int out_stride, float scale_x, float scale_y, int glyph) +{ + stbtt_MakeGlyphBitmapSubpixel(info, output, out_w, out_h, out_stride, scale_x, scale_y, 0.0f,0.0f, glyph); +} + +STBTT_DEF unsigned char *stbtt_GetCodepointBitmapSubpixel(const stbtt_fontinfo *info, float scale_x, float scale_y, float shift_x, float shift_y, int codepoint, int *width, int *height, int *xoff, int *yoff) +{ + return stbtt_GetGlyphBitmapSubpixel(info, scale_x, scale_y,shift_x,shift_y, stbtt_FindGlyphIndex(info,codepoint), width,height,xoff,yoff); +} + +STBTT_DEF void stbtt_MakeCodepointBitmapSubpixel(const stbtt_fontinfo *info, unsigned char *output, int out_w, int out_h, int out_stride, float scale_x, float scale_y, float shift_x, float shift_y, int codepoint) +{ + stbtt_MakeGlyphBitmapSubpixel(info, output, out_w, out_h, out_stride, scale_x, scale_y, shift_x, shift_y, stbtt_FindGlyphIndex(info,codepoint)); +} + +STBTT_DEF unsigned char *stbtt_GetCodepointBitmap(const stbtt_fontinfo *info, float scale_x, float scale_y, int codepoint, int *width, int *height, int *xoff, int *yoff) +{ + return stbtt_GetCodepointBitmapSubpixel(info, scale_x, scale_y, 0.0f,0.0f, codepoint, width,height,xoff,yoff); +} + +STBTT_DEF void stbtt_MakeCodepointBitmap(const stbtt_fontinfo *info, unsigned char *output, int out_w, int out_h, int out_stride, float scale_x, float scale_y, int codepoint) +{ + stbtt_MakeCodepointBitmapSubpixel(info, output, out_w, out_h, out_stride, scale_x, scale_y, 0.0f,0.0f, codepoint); +} + +////////////////////////////////////////////////////////////////////////////// +// +// bitmap baking +// +// This is SUPER-CRAPPY packing to keep source code small + +STBTT_DEF int stbtt_BakeFontBitmap(const unsigned char *data, int offset, // font location (use offset=0 for plain .ttf) + float pixel_height, // height of font in pixels + unsigned char *pixels, int pw, int ph, // bitmap to be filled in + int first_char, int num_chars, // characters to bake + stbtt_bakedchar *chardata) +{ + float scale; + int x,y,bottom_y, i; + stbtt_fontinfo f; + f.userdata = NULL; + if (!stbtt_InitFont(&f, data, offset)) + return -1; + STBTT_memset(pixels, 0, pw*ph); // background of 0 around pixels + x=y=1; + bottom_y = 1; + + scale = stbtt_ScaleForPixelHeight(&f, pixel_height); + + for (i=0; i < num_chars; ++i) { + int advance, lsb, x0,y0,x1,y1,gw,gh; + int g = stbtt_FindGlyphIndex(&f, first_char + i); + stbtt_GetGlyphHMetrics(&f, g, &advance, &lsb); + stbtt_GetGlyphBitmapBox(&f, g, scale,scale, &x0,&y0,&x1,&y1); + gw = x1-x0; + gh = y1-y0; + if (x + gw + 1 >= pw) + y = bottom_y, x = 1; // advance to next row + if (y + gh + 1 >= ph) // check if it fits vertically AFTER potentially moving to next row + return -i; + STBTT_assert(x+gw < pw); + STBTT_assert(y+gh < ph); + stbtt_MakeGlyphBitmap(&f, pixels+x+y*pw, gw,gh,pw, scale,scale, g); + chardata[i].x0 = (stbtt_int16) x; + chardata[i].y0 = (stbtt_int16) y; + chardata[i].x1 = (stbtt_int16) (x + gw); + chardata[i].y1 = (stbtt_int16) (y + gh); + chardata[i].xadvance = scale * advance; + chardata[i].xoff = (float) x0; + chardata[i].yoff = (float) y0; + x = x + gw + 1; + if (y+gh+1 > bottom_y) + bottom_y = y+gh+1; + } + return bottom_y; +} + +STBTT_DEF void stbtt_GetBakedQuad(stbtt_bakedchar *chardata, int pw, int ph, int char_index, float *xpos, float *ypos, stbtt_aligned_quad *q, int opengl_fillrule) +{ + float d3d_bias = opengl_fillrule ? 0 : -0.5f; + float ipw = 1.0f / pw, iph = 1.0f / ph; + stbtt_bakedchar *b = chardata + char_index; + int round_x = STBTT_ifloor((*xpos + b->xoff) + 0.5f); + int round_y = STBTT_ifloor((*ypos + b->yoff) + 0.5f); + + q->x0 = round_x + d3d_bias; + q->y0 = round_y + d3d_bias; + q->x1 = round_x + b->x1 - b->x0 + d3d_bias; + q->y1 = round_y + b->y1 - b->y0 + d3d_bias; + + q->s0 = b->x0 * ipw; + q->t0 = b->y0 * iph; + q->s1 = b->x1 * ipw; + q->t1 = b->y1 * iph; + + *xpos += b->xadvance; +} + +////////////////////////////////////////////////////////////////////////////// +// +// rectangle packing replacement routines if you don't have stb_rect_pack.h +// + +#ifndef STB_RECT_PACK_VERSION + +typedef int stbrp_coord; + +//////////////////////////////////////////////////////////////////////////////////// +// // +// // +// COMPILER WARNING ?!?!? // +// // +// // +// if you get a compile warning due to these symbols being defined more than // +// once, move #include "stb_rect_pack.h" before #include "stb_truetype.h" // +// // +//////////////////////////////////////////////////////////////////////////////////// + +typedef struct +{ + int width,height; + int x,y,bottom_y; +} stbrp_context; + +typedef struct +{ + unsigned char x; +} stbrp_node; + +struct stbrp_rect +{ + stbrp_coord x,y; + int id,w,h,was_packed; +}; + +static void stbrp_init_target(stbrp_context *con, int pw, int ph, stbrp_node *nodes, int num_nodes) +{ + con->width = pw; + con->height = ph; + con->x = 0; + con->y = 0; + con->bottom_y = 0; + STBTT__NOTUSED(nodes); + STBTT__NOTUSED(num_nodes); +} + +static void stbrp_pack_rects(stbrp_context *con, stbrp_rect *rects, int num_rects) +{ + int i; + for (i=0; i < num_rects; ++i) { + if (con->x + rects[i].w > con->width) { + con->x = 0; + con->y = con->bottom_y; + } + if (con->y + rects[i].h > con->height) + break; + rects[i].x = con->x; + rects[i].y = con->y; + rects[i].was_packed = 1; + con->x += rects[i].w; + if (con->y + rects[i].h > con->bottom_y) + con->bottom_y = con->y + rects[i].h; + } + for ( ; i < num_rects; ++i) + rects[i].was_packed = 0; +} +#endif + +////////////////////////////////////////////////////////////////////////////// +// +// bitmap baking +// +// This is SUPER-AWESOME (tm Ryan Gordon) packing using stb_rect_pack.h. If +// stb_rect_pack.h isn't available, it uses the BakeFontBitmap strategy. + +STBTT_DEF int stbtt_PackBegin(stbtt_pack_context *spc, unsigned char *pixels, int pw, int ph, int stride_in_bytes, int padding, void *alloc_context) +{ + stbrp_context *context = (stbrp_context *) STBTT_malloc(sizeof(*context) ,alloc_context); + int num_nodes = pw - padding; + stbrp_node *nodes = (stbrp_node *) STBTT_malloc(sizeof(*nodes ) * num_nodes,alloc_context); + + if (context == NULL || nodes == NULL) { + if (context != NULL) STBTT_free(context, alloc_context); + if (nodes != NULL) STBTT_free(nodes , alloc_context); + return 0; + } + + spc->user_allocator_context = alloc_context; + spc->width = pw; + spc->height = ph; + spc->pixels = pixels; + spc->pack_info = context; + spc->nodes = nodes; + spc->padding = padding; + spc->stride_in_bytes = stride_in_bytes != 0 ? stride_in_bytes : pw; + spc->h_oversample = 1; + spc->v_oversample = 1; + + stbrp_init_target(context, pw-padding, ph-padding, nodes, num_nodes); + + if (pixels) + STBTT_memset(pixels, 0, pw*ph); // background of 0 around pixels + + return 1; +} + +STBTT_DEF void stbtt_PackEnd (stbtt_pack_context *spc) +{ + STBTT_free(spc->nodes , spc->user_allocator_context); + STBTT_free(spc->pack_info, spc->user_allocator_context); +} + +STBTT_DEF void stbtt_PackSetOversampling(stbtt_pack_context *spc, unsigned int h_oversample, unsigned int v_oversample) +{ + STBTT_assert(h_oversample <= STBTT_MAX_OVERSAMPLE); + STBTT_assert(v_oversample <= STBTT_MAX_OVERSAMPLE); + if (h_oversample <= STBTT_MAX_OVERSAMPLE) + spc->h_oversample = h_oversample; + if (v_oversample <= STBTT_MAX_OVERSAMPLE) + spc->v_oversample = v_oversample; +} + +#define STBTT__OVER_MASK (STBTT_MAX_OVERSAMPLE-1) + +static void stbtt__h_prefilter(unsigned char *pixels, int w, int h, int stride_in_bytes, unsigned int kernel_width) +{ + unsigned char buffer[STBTT_MAX_OVERSAMPLE]; + int safe_w = w - kernel_width; + int j; + STBTT_memset(buffer, 0, STBTT_MAX_OVERSAMPLE); // suppress bogus warning from VS2013 -analyze + for (j=0; j < h; ++j) { + int i; + unsigned int total; + STBTT_memset(buffer, 0, kernel_width); + + total = 0; + + // make kernel_width a constant in common cases so compiler can optimize out the divide + switch (kernel_width) { + case 2: + for (i=0; i <= safe_w; ++i) { + total += pixels[i] - buffer[i & STBTT__OVER_MASK]; + buffer[(i+kernel_width) & STBTT__OVER_MASK] = pixels[i]; + pixels[i] = (unsigned char) (total / 2); + } + break; + case 3: + for (i=0; i <= safe_w; ++i) { + total += pixels[i] - buffer[i & STBTT__OVER_MASK]; + buffer[(i+kernel_width) & STBTT__OVER_MASK] = pixels[i]; + pixels[i] = (unsigned char) (total / 3); + } + break; + case 4: + for (i=0; i <= safe_w; ++i) { + total += pixels[i] - buffer[i & STBTT__OVER_MASK]; + buffer[(i+kernel_width) & STBTT__OVER_MASK] = pixels[i]; + pixels[i] = (unsigned char) (total / 4); + } + break; + case 5: + for (i=0; i <= safe_w; ++i) { + total += pixels[i] - buffer[i & STBTT__OVER_MASK]; + buffer[(i+kernel_width) & STBTT__OVER_MASK] = pixels[i]; + pixels[i] = (unsigned char) (total / 5); + } + break; + default: + for (i=0; i <= safe_w; ++i) { + total += pixels[i] - buffer[i & STBTT__OVER_MASK]; + buffer[(i+kernel_width) & STBTT__OVER_MASK] = pixels[i]; + pixels[i] = (unsigned char) (total / kernel_width); + } + break; + } + + for (; i < w; ++i) { + STBTT_assert(pixels[i] == 0); + total -= buffer[i & STBTT__OVER_MASK]; + pixels[i] = (unsigned char) (total / kernel_width); + } + + pixels += stride_in_bytes; + } +} + +static void stbtt__v_prefilter(unsigned char *pixels, int w, int h, int stride_in_bytes, unsigned int kernel_width) +{ + unsigned char buffer[STBTT_MAX_OVERSAMPLE]; + int safe_h = h - kernel_width; + int j; + STBTT_memset(buffer, 0, STBTT_MAX_OVERSAMPLE); // suppress bogus warning from VS2013 -analyze + for (j=0; j < w; ++j) { + int i; + unsigned int total; + STBTT_memset(buffer, 0, kernel_width); + + total = 0; + + // make kernel_width a constant in common cases so compiler can optimize out the divide + switch (kernel_width) { + case 2: + for (i=0; i <= safe_h; ++i) { + total += pixels[i*stride_in_bytes] - buffer[i & STBTT__OVER_MASK]; + buffer[(i+kernel_width) & STBTT__OVER_MASK] = pixels[i*stride_in_bytes]; + pixels[i*stride_in_bytes] = (unsigned char) (total / 2); + } + break; + case 3: + for (i=0; i <= safe_h; ++i) { + total += pixels[i*stride_in_bytes] - buffer[i & STBTT__OVER_MASK]; + buffer[(i+kernel_width) & STBTT__OVER_MASK] = pixels[i*stride_in_bytes]; + pixels[i*stride_in_bytes] = (unsigned char) (total / 3); + } + break; + case 4: + for (i=0; i <= safe_h; ++i) { + total += pixels[i*stride_in_bytes] - buffer[i & STBTT__OVER_MASK]; + buffer[(i+kernel_width) & STBTT__OVER_MASK] = pixels[i*stride_in_bytes]; + pixels[i*stride_in_bytes] = (unsigned char) (total / 4); + } + break; + case 5: + for (i=0; i <= safe_h; ++i) { + total += pixels[i*stride_in_bytes] - buffer[i & STBTT__OVER_MASK]; + buffer[(i+kernel_width) & STBTT__OVER_MASK] = pixels[i*stride_in_bytes]; + pixels[i*stride_in_bytes] = (unsigned char) (total / 5); + } + break; + default: + for (i=0; i <= safe_h; ++i) { + total += pixels[i*stride_in_bytes] - buffer[i & STBTT__OVER_MASK]; + buffer[(i+kernel_width) & STBTT__OVER_MASK] = pixels[i*stride_in_bytes]; + pixels[i*stride_in_bytes] = (unsigned char) (total / kernel_width); + } + break; + } + + for (; i < h; ++i) { + STBTT_assert(pixels[i*stride_in_bytes] == 0); + total -= buffer[i & STBTT__OVER_MASK]; + pixels[i*stride_in_bytes] = (unsigned char) (total / kernel_width); + } + + pixels += 1; + } +} + +static float stbtt__oversample_shift(int oversample) +{ + if (!oversample) + return 0.0f; + + // The prefilter is a box filter of width "oversample", + // which shifts phase by (oversample - 1)/2 pixels in + // oversampled space. We want to shift in the opposite + // direction to counter this. + return (float)-(oversample - 1) / (2.0f * (float)oversample); +} + +// rects array must be big enough to accommodate all characters in the given ranges +STBTT_DEF int stbtt_PackFontRangesGatherRects(stbtt_pack_context *spc, stbtt_fontinfo *info, stbtt_pack_range *ranges, int num_ranges, stbrp_rect *rects) +{ + int i,j,k; + + k=0; + for (i=0; i < num_ranges; ++i) { + float fh = ranges[i].font_size; + float scale = fh > 0 ? stbtt_ScaleForPixelHeight(info, fh) : stbtt_ScaleForMappingEmToPixels(info, -fh); + ranges[i].h_oversample = (unsigned char) spc->h_oversample; + ranges[i].v_oversample = (unsigned char) spc->v_oversample; + for (j=0; j < ranges[i].num_chars; ++j) { + int x0,y0,x1,y1; + int codepoint = ranges[i].array_of_unicode_codepoints == NULL ? ranges[i].first_unicode_codepoint_in_range + j : ranges[i].array_of_unicode_codepoints[j]; + int glyph = stbtt_FindGlyphIndex(info, codepoint); + stbtt_GetGlyphBitmapBoxSubpixel(info,glyph, + scale * spc->h_oversample, + scale * spc->v_oversample, + 0,0, + &x0,&y0,&x1,&y1); + rects[k].w = (stbrp_coord) (x1-x0 + spc->padding + spc->h_oversample-1); + rects[k].h = (stbrp_coord) (y1-y0 + spc->padding + spc->v_oversample-1); + ++k; + } + } + + return k; +} + +// rects array must be big enough to accommodate all characters in the given ranges +STBTT_DEF int stbtt_PackFontRangesRenderIntoRects(stbtt_pack_context *spc, stbtt_fontinfo *info, stbtt_pack_range *ranges, int num_ranges, stbrp_rect *rects) +{ + int i,j,k, return_value = 1; + + // save current values + int old_h_over = spc->h_oversample; + int old_v_over = spc->v_oversample; + + k = 0; + for (i=0; i < num_ranges; ++i) { + float fh = ranges[i].font_size; + float scale = fh > 0 ? stbtt_ScaleForPixelHeight(info, fh) : stbtt_ScaleForMappingEmToPixels(info, -fh); + float recip_h,recip_v,sub_x,sub_y; + spc->h_oversample = ranges[i].h_oversample; + spc->v_oversample = ranges[i].v_oversample; + recip_h = 1.0f / spc->h_oversample; + recip_v = 1.0f / spc->v_oversample; + sub_x = stbtt__oversample_shift(spc->h_oversample); + sub_y = stbtt__oversample_shift(spc->v_oversample); + for (j=0; j < ranges[i].num_chars; ++j) { + stbrp_rect *r = &rects[k]; + if (r->was_packed) { + stbtt_packedchar *bc = &ranges[i].chardata_for_range[j]; + int advance, lsb, x0,y0,x1,y1; + int codepoint = ranges[i].array_of_unicode_codepoints == NULL ? ranges[i].first_unicode_codepoint_in_range + j : ranges[i].array_of_unicode_codepoints[j]; + int glyph = stbtt_FindGlyphIndex(info, codepoint); + stbrp_coord pad = (stbrp_coord) spc->padding; + + // pad on left and top + r->x += pad; + r->y += pad; + r->w -= pad; + r->h -= pad; + stbtt_GetGlyphHMetrics(info, glyph, &advance, &lsb); + stbtt_GetGlyphBitmapBox(info, glyph, + scale * spc->h_oversample, + scale * spc->v_oversample, + &x0,&y0,&x1,&y1); + stbtt_MakeGlyphBitmapSubpixel(info, + spc->pixels + r->x + r->y*spc->stride_in_bytes, + r->w - spc->h_oversample+1, + r->h - spc->v_oversample+1, + spc->stride_in_bytes, + scale * spc->h_oversample, + scale * spc->v_oversample, + 0,0, + glyph); + + if (spc->h_oversample > 1) + stbtt__h_prefilter(spc->pixels + r->x + r->y*spc->stride_in_bytes, + r->w, r->h, spc->stride_in_bytes, + spc->h_oversample); + + if (spc->v_oversample > 1) + stbtt__v_prefilter(spc->pixels + r->x + r->y*spc->stride_in_bytes, + r->w, r->h, spc->stride_in_bytes, + spc->v_oversample); + + bc->x0 = (stbtt_int16) r->x; + bc->y0 = (stbtt_int16) r->y; + bc->x1 = (stbtt_int16) (r->x + r->w); + bc->y1 = (stbtt_int16) (r->y + r->h); + bc->xadvance = scale * advance; + bc->xoff = (float) x0 * recip_h + sub_x; + bc->yoff = (float) y0 * recip_v + sub_y; + bc->xoff2 = (x0 + r->w) * recip_h + sub_x; + bc->yoff2 = (y0 + r->h) * recip_v + sub_y; + } else { + return_value = 0; // if any fail, report failure + } + + ++k; + } + } + + // restore original values + spc->h_oversample = old_h_over; + spc->v_oversample = old_v_over; + + return return_value; +} + +STBTT_DEF void stbtt_PackFontRangesPackRects(stbtt_pack_context *spc, stbrp_rect *rects, int num_rects) +{ + stbrp_pack_rects((stbrp_context *) spc->pack_info, rects, num_rects); +} + +STBTT_DEF int stbtt_PackFontRanges(stbtt_pack_context *spc, unsigned char *fontdata, int font_index, stbtt_pack_range *ranges, int num_ranges) +{ + stbtt_fontinfo info; + int i,j,n, return_value = 1; + //stbrp_context *context = (stbrp_context *) spc->pack_info; + stbrp_rect *rects; + + // flag all characters as NOT packed + for (i=0; i < num_ranges; ++i) + for (j=0; j < ranges[i].num_chars; ++j) + ranges[i].chardata_for_range[j].x0 = + ranges[i].chardata_for_range[j].y0 = + ranges[i].chardata_for_range[j].x1 = + ranges[i].chardata_for_range[j].y1 = 0; + + n = 0; + for (i=0; i < num_ranges; ++i) + n += ranges[i].num_chars; + + rects = (stbrp_rect *) STBTT_malloc(sizeof(*rects) * n, spc->user_allocator_context); + if (rects == NULL) + return 0; + + info.userdata = spc->user_allocator_context; + stbtt_InitFont(&info, fontdata, stbtt_GetFontOffsetForIndex(fontdata,font_index)); + + n = stbtt_PackFontRangesGatherRects(spc, &info, ranges, num_ranges, rects); + + stbtt_PackFontRangesPackRects(spc, rects, n); + + return_value = stbtt_PackFontRangesRenderIntoRects(spc, &info, ranges, num_ranges, rects); + + STBTT_free(rects, spc->user_allocator_context); + return return_value; +} + +STBTT_DEF int stbtt_PackFontRange(stbtt_pack_context *spc, unsigned char *fontdata, int font_index, float font_size, + int first_unicode_codepoint_in_range, int num_chars_in_range, stbtt_packedchar *chardata_for_range) +{ + stbtt_pack_range range; + range.first_unicode_codepoint_in_range = first_unicode_codepoint_in_range; + range.array_of_unicode_codepoints = NULL; + range.num_chars = num_chars_in_range; + range.chardata_for_range = chardata_for_range; + range.font_size = font_size; + return stbtt_PackFontRanges(spc, fontdata, font_index, &range, 1); +} + +STBTT_DEF void stbtt_GetPackedQuad(stbtt_packedchar *chardata, int pw, int ph, int char_index, float *xpos, float *ypos, stbtt_aligned_quad *q, int align_to_integer) +{ + float ipw = 1.0f / pw, iph = 1.0f / ph; + stbtt_packedchar *b = chardata + char_index; + + if (align_to_integer) { + float x = (float) STBTT_ifloor((*xpos + b->xoff) + 0.5f); + float y = (float) STBTT_ifloor((*ypos + b->yoff) + 0.5f); + q->x0 = x; + q->y0 = y; + q->x1 = x + b->xoff2 - b->xoff; + q->y1 = y + b->yoff2 - b->yoff; + } else { + q->x0 = *xpos + b->xoff; + q->y0 = *ypos + b->yoff; + q->x1 = *xpos + b->xoff2; + q->y1 = *ypos + b->yoff2; + } + + q->s0 = b->x0 * ipw; + q->t0 = b->y0 * iph; + q->s1 = b->x1 * ipw; + q->t1 = b->y1 * iph; + + *xpos += b->xadvance; +} + + +////////////////////////////////////////////////////////////////////////////// +// +// font name matching -- recommended not to use this +// + +// check if a utf8 string contains a prefix which is the utf16 string; if so return length of matching utf8 string +static stbtt_int32 stbtt__CompareUTF8toUTF16_bigendian_prefix(const stbtt_uint8 *s1, stbtt_int32 len1, const stbtt_uint8 *s2, stbtt_int32 len2) +{ + stbtt_int32 i=0; + + // convert utf16 to utf8 and compare the results while converting + while (len2) { + stbtt_uint16 ch = s2[0]*256 + s2[1]; + if (ch < 0x80) { + if (i >= len1) return -1; + if (s1[i++] != ch) return -1; + } else if (ch < 0x800) { + if (i+1 >= len1) return -1; + if (s1[i++] != 0xc0 + (ch >> 6)) return -1; + if (s1[i++] != 0x80 + (ch & 0x3f)) return -1; + } else if (ch >= 0xd800 && ch < 0xdc00) { + stbtt_uint32 c; + stbtt_uint16 ch2 = s2[2]*256 + s2[3]; + if (i+3 >= len1) return -1; + c = ((ch - 0xd800) << 10) + (ch2 - 0xdc00) + 0x10000; + if (s1[i++] != 0xf0 + (c >> 18)) return -1; + if (s1[i++] != 0x80 + ((c >> 12) & 0x3f)) return -1; + if (s1[i++] != 0x80 + ((c >> 6) & 0x3f)) return -1; + if (s1[i++] != 0x80 + ((c ) & 0x3f)) return -1; + s2 += 2; // plus another 2 below + len2 -= 2; + } else if (ch >= 0xdc00 && ch < 0xe000) { + return -1; + } else { + if (i+2 >= len1) return -1; + if (s1[i++] != 0xe0 + (ch >> 12)) return -1; + if (s1[i++] != 0x80 + ((ch >> 6) & 0x3f)) return -1; + if (s1[i++] != 0x80 + ((ch ) & 0x3f)) return -1; + } + s2 += 2; + len2 -= 2; + } + return i; +} + +STBTT_DEF int stbtt_CompareUTF8toUTF16_bigendian(const char *s1, int len1, const char *s2, int len2) +{ + return len1 == stbtt__CompareUTF8toUTF16_bigendian_prefix((const stbtt_uint8*) s1, len1, (const stbtt_uint8*) s2, len2); +} + +// returns results in whatever encoding you request... but note that 2-byte encodings +// will be BIG-ENDIAN... use stbtt_CompareUTF8toUTF16_bigendian() to compare +STBTT_DEF const char *stbtt_GetFontNameString(const stbtt_fontinfo *font, int *length, int platformID, int encodingID, int languageID, int nameID) +{ + stbtt_int32 i,count,stringOffset; + stbtt_uint8 *fc = font->data; + stbtt_uint32 offset = font->fontstart; + stbtt_uint32 nm = stbtt__find_table(fc, offset, "name"); + if (!nm) return NULL; + + count = ttUSHORT(fc+nm+2); + stringOffset = nm + ttUSHORT(fc+nm+4); + for (i=0; i < count; ++i) { + stbtt_uint32 loc = nm + 6 + 12 * i; + if (platformID == ttUSHORT(fc+loc+0) && encodingID == ttUSHORT(fc+loc+2) + && languageID == ttUSHORT(fc+loc+4) && nameID == ttUSHORT(fc+loc+6)) { + *length = ttUSHORT(fc+loc+8); + return (const char *) (fc+stringOffset+ttUSHORT(fc+loc+10)); + } + } + return NULL; +} + +static int stbtt__matchpair(stbtt_uint8 *fc, stbtt_uint32 nm, stbtt_uint8 *name, stbtt_int32 nlen, stbtt_int32 target_id, stbtt_int32 next_id) +{ + stbtt_int32 i; + stbtt_int32 count = ttUSHORT(fc+nm+2); + stbtt_int32 stringOffset = nm + ttUSHORT(fc+nm+4); + + for (i=0; i < count; ++i) { + stbtt_uint32 loc = nm + 6 + 12 * i; + stbtt_int32 id = ttUSHORT(fc+loc+6); + if (id == target_id) { + // find the encoding + stbtt_int32 platform = ttUSHORT(fc+loc+0), encoding = ttUSHORT(fc+loc+2), language = ttUSHORT(fc+loc+4); + + // is this a Unicode encoding? + if (platform == 0 || (platform == 3 && encoding == 1) || (platform == 3 && encoding == 10)) { + stbtt_int32 slen = ttUSHORT(fc+loc+8); + stbtt_int32 off = ttUSHORT(fc+loc+10); + + // check if there's a prefix match + stbtt_int32 matchlen = stbtt__CompareUTF8toUTF16_bigendian_prefix(name, nlen, fc+stringOffset+off,slen); + if (matchlen >= 0) { + // check for target_id+1 immediately following, with same encoding & language + if (i+1 < count && ttUSHORT(fc+loc+12+6) == next_id && ttUSHORT(fc+loc+12) == platform && ttUSHORT(fc+loc+12+2) == encoding && ttUSHORT(fc+loc+12+4) == language) { + slen = ttUSHORT(fc+loc+12+8); + off = ttUSHORT(fc+loc+12+10); + if (slen == 0) { + if (matchlen == nlen) + return 1; + } else if (matchlen < nlen && name[matchlen] == ' ') { + ++matchlen; + if (stbtt_CompareUTF8toUTF16_bigendian((char*) (name+matchlen), nlen-matchlen, (char*)(fc+stringOffset+off),slen)) + return 1; + } + } else { + // if nothing immediately following + if (matchlen == nlen) + return 1; + } + } + } + + // @TODO handle other encodings + } + } + return 0; +} + +static int stbtt__matches(stbtt_uint8 *fc, stbtt_uint32 offset, stbtt_uint8 *name, stbtt_int32 flags) +{ + stbtt_int32 nlen = (stbtt_int32) STBTT_strlen((char *) name); + stbtt_uint32 nm,hd; + if (!stbtt__isfont(fc+offset)) return 0; + + // check italics/bold/underline flags in macStyle... + if (flags) { + hd = stbtt__find_table(fc, offset, "head"); + if ((ttUSHORT(fc+hd+44) & 7) != (flags & 7)) return 0; + } + + nm = stbtt__find_table(fc, offset, "name"); + if (!nm) return 0; + + if (flags) { + // if we checked the macStyle flags, then just check the family and ignore the subfamily + if (stbtt__matchpair(fc, nm, name, nlen, 16, -1)) return 1; + if (stbtt__matchpair(fc, nm, name, nlen, 1, -1)) return 1; + if (stbtt__matchpair(fc, nm, name, nlen, 3, -1)) return 1; + } else { + if (stbtt__matchpair(fc, nm, name, nlen, 16, 17)) return 1; + if (stbtt__matchpair(fc, nm, name, nlen, 1, 2)) return 1; + if (stbtt__matchpair(fc, nm, name, nlen, 3, -1)) return 1; + } + + return 0; +} + +STBTT_DEF int stbtt_FindMatchingFont(const unsigned char *font_collection, const char *name_utf8, stbtt_int32 flags) +{ + stbtt_int32 i; + for (i=0;;++i) { + stbtt_int32 off = stbtt_GetFontOffsetForIndex(font_collection, i); + if (off < 0) return off; + if (stbtt__matches((stbtt_uint8 *) font_collection, off, (stbtt_uint8*) name_utf8, flags)) + return off; + } +} + +#endif // STB_TRUETYPE_IMPLEMENTATION + + +// FULL VERSION HISTORY +// +// 1.11 (2016-04-02) fix unused-variable warning +// 1.10 (2016-04-02) allow user-defined fabs() replacement +// fix memory leak if fontsize=0.0 +// fix warning from duplicate typedef +// 1.09 (2016-01-16) warning fix; avoid crash on outofmem; use alloc userdata for PackFontRanges +// 1.08 (2015-09-13) document stbtt_Rasterize(); fixes for vertical & horizontal edges +// 1.07 (2015-08-01) allow PackFontRanges to accept arrays of sparse codepoints; +// allow PackFontRanges to pack and render in separate phases; +// fix stbtt_GetFontOFfsetForIndex (never worked for non-0 input?); +// fixed an assert() bug in the new rasterizer +// replace assert() with STBTT_assert() in new rasterizer +// 1.06 (2015-07-14) performance improvements (~35% faster on x86 and x64 on test machine) +// also more precise AA rasterizer, except if shapes overlap +// remove need for STBTT_sort +// 1.05 (2015-04-15) fix misplaced definitions for STBTT_STATIC +// 1.04 (2015-04-15) typo in example +// 1.03 (2015-04-12) STBTT_STATIC, fix memory leak in new packing, various fixes +// 1.02 (2014-12-10) fix various warnings & compile issues w/ stb_rect_pack, C++ +// 1.01 (2014-12-08) fix subpixel position when oversampling to exactly match +// non-oversampled; STBTT_POINT_SIZE for packed case only +// 1.00 (2014-12-06) add new PackBegin etc. API, w/ support for oversampling +// 0.99 (2014-09-18) fix multiple bugs with subpixel rendering (ryg) +// 0.9 (2014-08-07) support certain mac/iOS fonts without an MS platformID +// 0.8b (2014-07-07) fix a warning +// 0.8 (2014-05-25) fix a few more warnings +// 0.7 (2013-09-25) bugfix: subpixel glyph bug fixed in 0.5 had come back +// 0.6c (2012-07-24) improve documentation +// 0.6b (2012-07-20) fix a few more warnings +// 0.6 (2012-07-17) fix warnings; added stbtt_ScaleForMappingEmToPixels, +// stbtt_GetFontBoundingBox, stbtt_IsGlyphEmpty +// 0.5 (2011-12-09) bugfixes: +// subpixel glyph renderer computed wrong bounding box +// first vertex of shape can be off-curve (FreeSans) +// 0.4b (2011-12-03) fixed an error in the font baking example +// 0.4 (2011-12-01) kerning, subpixel rendering (tor) +// bugfixes for: +// codepoint-to-glyph conversion using table fmt=12 +// codepoint-to-glyph conversion using table fmt=4 +// stbtt_GetBakedQuad with non-square texture (Zer) +// updated Hello World! sample to use kerning and subpixel +// fixed some warnings +// 0.3 (2009-06-24) cmap fmt=12, compound shapes (MM) +// userdata, malloc-from-userdata, non-zero fill (stb) +// 0.2 (2009-03-11) Fix unsigned/signed char warnings +// 0.1 (2009-03-09) First public release +// diff --git a/impeller/third_party/stb/stb/stb_vorbis.c b/impeller/third_party/stb/stb/stb_vorbis.c new file mode 100644 index 0000000000000..19459eb1a354e --- /dev/null +++ b/impeller/third_party/stb/stb/stb_vorbis.c @@ -0,0 +1,5397 @@ +// Ogg Vorbis audio decoder - v1.09 - public domain +// http://nothings.org/stb_vorbis/ +// +// Original version written by Sean Barrett in 2007. +// +// Originally sponsored by RAD Game Tools. Seeking sponsored +// by Phillip Bennefall, Marc Andersen, Aaron Baker, Elias Software, +// Aras Pranckevicius, and Sean Barrett. +// +// LICENSE +// +// This software is dual-licensed to the public domain and under the following +// license: you are granted a perpetual, irrevocable license to copy, modify, +// publish, and distribute this file as you see fit. +// +// No warranty for any purpose is expressed or implied by the author (nor +// by RAD Game Tools). Report bugs and send enhancements to the author. +// +// Limitations: +// +// - floor 0 not supported (used in old ogg vorbis files pre-2004) +// - lossless sample-truncation at beginning ignored +// - cannot concatenate multiple vorbis streams +// - sample positions are 32-bit, limiting seekable 192Khz +// files to around 6 hours (Ogg supports 64-bit) +// +// Feature contributors: +// Dougall Johnson (sample-exact seeking) +// +// Bugfix/warning contributors: +// Terje Mathisen Niklas Frykholm Andy Hill +// Casey Muratori John Bolton Gargaj +// Laurent Gomila Marc LeBlanc Ronny Chevalier +// Bernhard Wodo Evan Balster alxprd@github +// Tom Beaumont Ingo Leitgeb Nicolas Guillemot +// Phillip Bennefall Rohit Thiago Goulart +// manxorist@github saga musix +// +// Partial history: +// 1.09 - 2016/04/04 - back out 'truncation of last frame' fix from previous version +// 1.08 - 2016/04/02 - warnings; setup memory leaks; truncation of last frame +// 1.07 - 2015/01/16 - fixes for crashes on invalid files; warning fixes; const +// 1.06 - 2015/08/31 - full, correct support for seeking API (Dougall Johnson) +// some crash fixes when out of memory or with corrupt files +// fix some inappropriately signed shifts +// 1.05 - 2015/04/19 - don't define __forceinline if it's redundant +// 1.04 - 2014/08/27 - fix missing const-correct case in API +// 1.03 - 2014/08/07 - warning fixes +// 1.02 - 2014/07/09 - declare qsort comparison as explicitly _cdecl in Windows +// 1.01 - 2014/06/18 - fix stb_vorbis_get_samples_float (interleaved was correct) +// 1.0 - 2014/05/26 - fix memory leaks; fix warnings; fix bugs in >2-channel; +// (API change) report sample rate for decode-full-file funcs +// +// See end of file for full version history. + + +////////////////////////////////////////////////////////////////////////////// +// +// HEADER BEGINS HERE +// + +#ifndef STB_VORBIS_INCLUDE_STB_VORBIS_H +#define STB_VORBIS_INCLUDE_STB_VORBIS_H + +#if defined(STB_VORBIS_NO_CRT) && !defined(STB_VORBIS_NO_STDIO) +#define STB_VORBIS_NO_STDIO 1 +#endif + +#ifndef STB_VORBIS_NO_STDIO +#include +#endif + +#ifdef __cplusplus +extern "C" { +#endif + +/////////// THREAD SAFETY + +// Individual stb_vorbis* handles are not thread-safe; you cannot decode from +// them from multiple threads at the same time. However, you can have multiple +// stb_vorbis* handles and decode from them independently in multiple thrads. + + +/////////// MEMORY ALLOCATION + +// normally stb_vorbis uses malloc() to allocate memory at startup, +// and alloca() to allocate temporary memory during a frame on the +// stack. (Memory consumption will depend on the amount of setup +// data in the file and how you set the compile flags for speed +// vs. size. In my test files the maximal-size usage is ~150KB.) +// +// You can modify the wrapper functions in the source (setup_malloc, +// setup_temp_malloc, temp_malloc) to change this behavior, or you +// can use a simpler allocation model: you pass in a buffer from +// which stb_vorbis will allocate _all_ its memory (including the +// temp memory). "open" may fail with a VORBIS_outofmem if you +// do not pass in enough data; there is no way to determine how +// much you do need except to succeed (at which point you can +// query get_info to find the exact amount required. yes I know +// this is lame). +// +// If you pass in a non-NULL buffer of the type below, allocation +// will occur from it as described above. Otherwise just pass NULL +// to use malloc()/alloca() + +typedef struct +{ + char *alloc_buffer; + int alloc_buffer_length_in_bytes; +} stb_vorbis_alloc; + + +/////////// FUNCTIONS USEABLE WITH ALL INPUT MODES + +typedef struct stb_vorbis stb_vorbis; + +typedef struct +{ + unsigned int sample_rate; + int channels; + + unsigned int setup_memory_required; + unsigned int setup_temp_memory_required; + unsigned int temp_memory_required; + + int max_frame_size; +} stb_vorbis_info; + +// get general information about the file +extern stb_vorbis_info stb_vorbis_get_info(stb_vorbis *f); + +// get the last error detected (clears it, too) +extern int stb_vorbis_get_error(stb_vorbis *f); + +// close an ogg vorbis file and free all memory in use +extern void stb_vorbis_close(stb_vorbis *f); + +// this function returns the offset (in samples) from the beginning of the +// file that will be returned by the next decode, if it is known, or -1 +// otherwise. after a flush_pushdata() call, this may take a while before +// it becomes valid again. +// NOT WORKING YET after a seek with PULLDATA API +extern int stb_vorbis_get_sample_offset(stb_vorbis *f); + +// returns the current seek point within the file, or offset from the beginning +// of the memory buffer. In pushdata mode it returns 0. +extern unsigned int stb_vorbis_get_file_offset(stb_vorbis *f); + +/////////// PUSHDATA API + +#ifndef STB_VORBIS_NO_PUSHDATA_API + +// this API allows you to get blocks of data from any source and hand +// them to stb_vorbis. you have to buffer them; stb_vorbis will tell +// you how much it used, and you have to give it the rest next time; +// and stb_vorbis may not have enough data to work with and you will +// need to give it the same data again PLUS more. Note that the Vorbis +// specification does not bound the size of an individual frame. + +extern stb_vorbis *stb_vorbis_open_pushdata( + const unsigned char * datablock, int datablock_length_in_bytes, + int *datablock_memory_consumed_in_bytes, + int *error, + const stb_vorbis_alloc *alloc_buffer); +// create a vorbis decoder by passing in the initial data block containing +// the ogg&vorbis headers (you don't need to do parse them, just provide +// the first N bytes of the file--you're told if it's not enough, see below) +// on success, returns an stb_vorbis *, does not set error, returns the amount of +// data parsed/consumed on this call in *datablock_memory_consumed_in_bytes; +// on failure, returns NULL on error and sets *error, does not change *datablock_memory_consumed +// if returns NULL and *error is VORBIS_need_more_data, then the input block was +// incomplete and you need to pass in a larger block from the start of the file + +extern int stb_vorbis_decode_frame_pushdata( + stb_vorbis *f, + const unsigned char *datablock, int datablock_length_in_bytes, + int *channels, // place to write number of float * buffers + float ***output, // place to write float ** array of float * buffers + int *samples // place to write number of output samples + ); +// decode a frame of audio sample data if possible from the passed-in data block +// +// return value: number of bytes we used from datablock +// +// possible cases: +// 0 bytes used, 0 samples output (need more data) +// N bytes used, 0 samples output (resynching the stream, keep going) +// N bytes used, M samples output (one frame of data) +// note that after opening a file, you will ALWAYS get one N-bytes,0-sample +// frame, because Vorbis always "discards" the first frame. +// +// Note that on resynch, stb_vorbis will rarely consume all of the buffer, +// instead only datablock_length_in_bytes-3 or less. This is because it wants +// to avoid missing parts of a page header if they cross a datablock boundary, +// without writing state-machiney code to record a partial detection. +// +// The number of channels returned are stored in *channels (which can be +// NULL--it is always the same as the number of channels reported by +// get_info). *output will contain an array of float* buffers, one per +// channel. In other words, (*output)[0][0] contains the first sample from +// the first channel, and (*output)[1][0] contains the first sample from +// the second channel. + +extern void stb_vorbis_flush_pushdata(stb_vorbis *f); +// inform stb_vorbis that your next datablock will not be contiguous with +// previous ones (e.g. you've seeked in the data); future attempts to decode +// frames will cause stb_vorbis to resynchronize (as noted above), and +// once it sees a valid Ogg page (typically 4-8KB, as large as 64KB), it +// will begin decoding the _next_ frame. +// +// if you want to seek using pushdata, you need to seek in your file, then +// call stb_vorbis_flush_pushdata(), then start calling decoding, then once +// decoding is returning you data, call stb_vorbis_get_sample_offset, and +// if you don't like the result, seek your file again and repeat. +#endif + + +////////// PULLING INPUT API + +#ifndef STB_VORBIS_NO_PULLDATA_API +// This API assumes stb_vorbis is allowed to pull data from a source-- +// either a block of memory containing the _entire_ vorbis stream, or a +// FILE * that you or it create, or possibly some other reading mechanism +// if you go modify the source to replace the FILE * case with some kind +// of callback to your code. (But if you don't support seeking, you may +// just want to go ahead and use pushdata.) + +#if !defined(STB_VORBIS_NO_STDIO) && !defined(STB_VORBIS_NO_INTEGER_CONVERSION) +extern int stb_vorbis_decode_filename(const char *filename, int *channels, int *sample_rate, short **output); +#endif +#if !defined(STB_VORBIS_NO_INTEGER_CONVERSION) +extern int stb_vorbis_decode_memory(const unsigned char *mem, int len, int *channels, int *sample_rate, short **output); +#endif +// decode an entire file and output the data interleaved into a malloc()ed +// buffer stored in *output. The return value is the number of samples +// decoded, or -1 if the file could not be opened or was not an ogg vorbis file. +// When you're done with it, just free() the pointer returned in *output. + +extern stb_vorbis * stb_vorbis_open_memory(const unsigned char *data, int len, + int *error, const stb_vorbis_alloc *alloc_buffer); +// create an ogg vorbis decoder from an ogg vorbis stream in memory (note +// this must be the entire stream!). on failure, returns NULL and sets *error + +#ifndef STB_VORBIS_NO_STDIO +extern stb_vorbis * stb_vorbis_open_filename(const char *filename, + int *error, const stb_vorbis_alloc *alloc_buffer); +// create an ogg vorbis decoder from a filename via fopen(). on failure, +// returns NULL and sets *error (possibly to VORBIS_file_open_failure). + +extern stb_vorbis * stb_vorbis_open_file(FILE *f, int close_handle_on_close, + int *error, const stb_vorbis_alloc *alloc_buffer); +// create an ogg vorbis decoder from an open FILE *, looking for a stream at +// the _current_ seek point (ftell). on failure, returns NULL and sets *error. +// note that stb_vorbis must "own" this stream; if you seek it in between +// calls to stb_vorbis, it will become confused. Morever, if you attempt to +// perform stb_vorbis_seek_*() operations on this file, it will assume it +// owns the _entire_ rest of the file after the start point. Use the next +// function, stb_vorbis_open_file_section(), to limit it. + +extern stb_vorbis * stb_vorbis_open_file_section(FILE *f, int close_handle_on_close, + int *error, const stb_vorbis_alloc *alloc_buffer, unsigned int len); +// create an ogg vorbis decoder from an open FILE *, looking for a stream at +// the _current_ seek point (ftell); the stream will be of length 'len' bytes. +// on failure, returns NULL and sets *error. note that stb_vorbis must "own" +// this stream; if you seek it in between calls to stb_vorbis, it will become +// confused. +#endif + +extern int stb_vorbis_seek_frame(stb_vorbis *f, unsigned int sample_number); +extern int stb_vorbis_seek(stb_vorbis *f, unsigned int sample_number); +// these functions seek in the Vorbis file to (approximately) 'sample_number'. +// after calling seek_frame(), the next call to get_frame_*() will include +// the specified sample. after calling stb_vorbis_seek(), the next call to +// stb_vorbis_get_samples_* will start with the specified sample. If you +// do not need to seek to EXACTLY the target sample when using get_samples_*, +// you can also use seek_frame(). + +extern void stb_vorbis_seek_start(stb_vorbis *f); +// this function is equivalent to stb_vorbis_seek(f,0) + +extern unsigned int stb_vorbis_stream_length_in_samples(stb_vorbis *f); +extern float stb_vorbis_stream_length_in_seconds(stb_vorbis *f); +// these functions return the total length of the vorbis stream + +extern int stb_vorbis_get_frame_float(stb_vorbis *f, int *channels, float ***output); +// decode the next frame and return the number of samples. the number of +// channels returned are stored in *channels (which can be NULL--it is always +// the same as the number of channels reported by get_info). *output will +// contain an array of float* buffers, one per channel. These outputs will +// be overwritten on the next call to stb_vorbis_get_frame_*. +// +// You generally should not intermix calls to stb_vorbis_get_frame_*() +// and stb_vorbis_get_samples_*(), since the latter calls the former. + +#ifndef STB_VORBIS_NO_INTEGER_CONVERSION +extern int stb_vorbis_get_frame_short_interleaved(stb_vorbis *f, int num_c, short *buffer, int num_shorts); +extern int stb_vorbis_get_frame_short (stb_vorbis *f, int num_c, short **buffer, int num_samples); +#endif +// decode the next frame and return the number of *samples* per channel. +// Note that for interleaved data, you pass in the number of shorts (the +// size of your array), but the return value is the number of samples per +// channel, not the total number of samples. +// +// The data is coerced to the number of channels you request according to the +// channel coercion rules (see below). You must pass in the size of your +// buffer(s) so that stb_vorbis will not overwrite the end of the buffer. +// The maximum buffer size needed can be gotten from get_info(); however, +// the Vorbis I specification implies an absolute maximum of 4096 samples +// per channel. + +// Channel coercion rules: +// Let M be the number of channels requested, and N the number of channels present, +// and Cn be the nth channel; let stereo L be the sum of all L and center channels, +// and stereo R be the sum of all R and center channels (channel assignment from the +// vorbis spec). +// M N output +// 1 k sum(Ck) for all k +// 2 * stereo L, stereo R +// k l k > l, the first l channels, then 0s +// k l k <= l, the first k channels +// Note that this is not _good_ surround etc. mixing at all! It's just so +// you get something useful. + +extern int stb_vorbis_get_samples_float_interleaved(stb_vorbis *f, int channels, float *buffer, int num_floats); +extern int stb_vorbis_get_samples_float(stb_vorbis *f, int channels, float **buffer, int num_samples); +// gets num_samples samples, not necessarily on a frame boundary--this requires +// buffering so you have to supply the buffers. DOES NOT APPLY THE COERCION RULES. +// Returns the number of samples stored per channel; it may be less than requested +// at the end of the file. If there are no more samples in the file, returns 0. + +#ifndef STB_VORBIS_NO_INTEGER_CONVERSION +extern int stb_vorbis_get_samples_short_interleaved(stb_vorbis *f, int channels, short *buffer, int num_shorts); +extern int stb_vorbis_get_samples_short(stb_vorbis *f, int channels, short **buffer, int num_samples); +#endif +// gets num_samples samples, not necessarily on a frame boundary--this requires +// buffering so you have to supply the buffers. Applies the coercion rules above +// to produce 'channels' channels. Returns the number of samples stored per channel; +// it may be less than requested at the end of the file. If there are no more +// samples in the file, returns 0. + +#endif + +//////// ERROR CODES + +enum STBVorbisError +{ + VORBIS__no_error, + + VORBIS_need_more_data=1, // not a real error + + VORBIS_invalid_api_mixing, // can't mix API modes + VORBIS_outofmem, // not enough memory + VORBIS_feature_not_supported, // uses floor 0 + VORBIS_too_many_channels, // STB_VORBIS_MAX_CHANNELS is too small + VORBIS_file_open_failure, // fopen() failed + VORBIS_seek_without_length, // can't seek in unknown-length file + + VORBIS_unexpected_eof=10, // file is truncated? + VORBIS_seek_invalid, // seek past EOF + + // decoding errors (corrupt/invalid stream) -- you probably + // don't care about the exact details of these + + // vorbis errors: + VORBIS_invalid_setup=20, + VORBIS_invalid_stream, + + // ogg errors: + VORBIS_missing_capture_pattern=30, + VORBIS_invalid_stream_structure_version, + VORBIS_continued_packet_flag_invalid, + VORBIS_incorrect_stream_serial_number, + VORBIS_invalid_first_page, + VORBIS_bad_packet_type, + VORBIS_cant_find_last_page, + VORBIS_seek_failed +}; + + +#ifdef __cplusplus +} +#endif + +#endif // STB_VORBIS_INCLUDE_STB_VORBIS_H +// +// HEADER ENDS HERE +// +////////////////////////////////////////////////////////////////////////////// + +#ifndef STB_VORBIS_HEADER_ONLY + +// global configuration settings (e.g. set these in the project/makefile), +// or just set them in this file at the top (although ideally the first few +// should be visible when the header file is compiled too, although it's not +// crucial) + +// STB_VORBIS_NO_PUSHDATA_API +// does not compile the code for the various stb_vorbis_*_pushdata() +// functions +// #define STB_VORBIS_NO_PUSHDATA_API + +// STB_VORBIS_NO_PULLDATA_API +// does not compile the code for the non-pushdata APIs +// #define STB_VORBIS_NO_PULLDATA_API + +// STB_VORBIS_NO_STDIO +// does not compile the code for the APIs that use FILE *s internally +// or externally (implied by STB_VORBIS_NO_PULLDATA_API) +// #define STB_VORBIS_NO_STDIO + +// STB_VORBIS_NO_INTEGER_CONVERSION +// does not compile the code for converting audio sample data from +// float to integer (implied by STB_VORBIS_NO_PULLDATA_API) +// #define STB_VORBIS_NO_INTEGER_CONVERSION + +// STB_VORBIS_NO_FAST_SCALED_FLOAT +// does not use a fast float-to-int trick to accelerate float-to-int on +// most platforms which requires endianness be defined correctly. +//#define STB_VORBIS_NO_FAST_SCALED_FLOAT + + +// STB_VORBIS_MAX_CHANNELS [number] +// globally define this to the maximum number of channels you need. +// The spec does not put a restriction on channels except that +// the count is stored in a byte, so 255 is the hard limit. +// Reducing this saves about 16 bytes per value, so using 16 saves +// (255-16)*16 or around 4KB. Plus anything other memory usage +// I forgot to account for. Can probably go as low as 8 (7.1 audio), +// 6 (5.1 audio), or 2 (stereo only). +#ifndef STB_VORBIS_MAX_CHANNELS +#define STB_VORBIS_MAX_CHANNELS 16 // enough for anyone? +#endif + +// STB_VORBIS_PUSHDATA_CRC_COUNT [number] +// after a flush_pushdata(), stb_vorbis begins scanning for the +// next valid page, without backtracking. when it finds something +// that looks like a page, it streams through it and verifies its +// CRC32. Should that validation fail, it keeps scanning. But it's +// possible that _while_ streaming through to check the CRC32 of +// one candidate page, it sees another candidate page. This #define +// determines how many "overlapping" candidate pages it can search +// at once. Note that "real" pages are typically ~4KB to ~8KB, whereas +// garbage pages could be as big as 64KB, but probably average ~16KB. +// So don't hose ourselves by scanning an apparent 64KB page and +// missing a ton of real ones in the interim; so minimum of 2 +#ifndef STB_VORBIS_PUSHDATA_CRC_COUNT +#define STB_VORBIS_PUSHDATA_CRC_COUNT 4 +#endif + +// STB_VORBIS_FAST_HUFFMAN_LENGTH [number] +// sets the log size of the huffman-acceleration table. Maximum +// supported value is 24. with larger numbers, more decodings are O(1), +// but the table size is larger so worse cache missing, so you'll have +// to probe (and try multiple ogg vorbis files) to find the sweet spot. +#ifndef STB_VORBIS_FAST_HUFFMAN_LENGTH +#define STB_VORBIS_FAST_HUFFMAN_LENGTH 10 +#endif + +// STB_VORBIS_FAST_BINARY_LENGTH [number] +// sets the log size of the binary-search acceleration table. this +// is used in similar fashion to the fast-huffman size to set initial +// parameters for the binary search + +// STB_VORBIS_FAST_HUFFMAN_INT +// The fast huffman tables are much more efficient if they can be +// stored as 16-bit results instead of 32-bit results. This restricts +// the codebooks to having only 65535 possible outcomes, though. +// (At least, accelerated by the huffman table.) +#ifndef STB_VORBIS_FAST_HUFFMAN_INT +#define STB_VORBIS_FAST_HUFFMAN_SHORT +#endif + +// STB_VORBIS_NO_HUFFMAN_BINARY_SEARCH +// If the 'fast huffman' search doesn't succeed, then stb_vorbis falls +// back on binary searching for the correct one. This requires storing +// extra tables with the huffman codes in sorted order. Defining this +// symbol trades off space for speed by forcing a linear search in the +// non-fast case, except for "sparse" codebooks. +// #define STB_VORBIS_NO_HUFFMAN_BINARY_SEARCH + +// STB_VORBIS_DIVIDES_IN_RESIDUE +// stb_vorbis precomputes the result of the scalar residue decoding +// that would otherwise require a divide per chunk. you can trade off +// space for time by defining this symbol. +// #define STB_VORBIS_DIVIDES_IN_RESIDUE + +// STB_VORBIS_DIVIDES_IN_CODEBOOK +// vorbis VQ codebooks can be encoded two ways: with every case explicitly +// stored, or with all elements being chosen from a small range of values, +// and all values possible in all elements. By default, stb_vorbis expands +// this latter kind out to look like the former kind for ease of decoding, +// because otherwise an integer divide-per-vector-element is required to +// unpack the index. If you define STB_VORBIS_DIVIDES_IN_CODEBOOK, you can +// trade off storage for speed. +//#define STB_VORBIS_DIVIDES_IN_CODEBOOK + +#ifdef STB_VORBIS_CODEBOOK_SHORTS +#error "STB_VORBIS_CODEBOOK_SHORTS is no longer supported as it produced incorrect results for some input formats" +#endif + +// STB_VORBIS_DIVIDE_TABLE +// this replaces small integer divides in the floor decode loop with +// table lookups. made less than 1% difference, so disabled by default. + +// STB_VORBIS_NO_INLINE_DECODE +// disables the inlining of the scalar codebook fast-huffman decode. +// might save a little codespace; useful for debugging +// #define STB_VORBIS_NO_INLINE_DECODE + +// STB_VORBIS_NO_DEFER_FLOOR +// Normally we only decode the floor without synthesizing the actual +// full curve. We can instead synthesize the curve immediately. This +// requires more memory and is very likely slower, so I don't think +// you'd ever want to do it except for debugging. +// #define STB_VORBIS_NO_DEFER_FLOOR + + + + +////////////////////////////////////////////////////////////////////////////// + +#ifdef STB_VORBIS_NO_PULLDATA_API + #define STB_VORBIS_NO_INTEGER_CONVERSION + #define STB_VORBIS_NO_STDIO +#endif + +#if defined(STB_VORBIS_NO_CRT) && !defined(STB_VORBIS_NO_STDIO) + #define STB_VORBIS_NO_STDIO 1 +#endif + +#ifndef STB_VORBIS_NO_INTEGER_CONVERSION +#ifndef STB_VORBIS_NO_FAST_SCALED_FLOAT + + // only need endianness for fast-float-to-int, which we don't + // use for pushdata + + #ifndef STB_VORBIS_BIG_ENDIAN + #define STB_VORBIS_ENDIAN 0 + #else + #define STB_VORBIS_ENDIAN 1 + #endif + +#endif +#endif + + +#ifndef STB_VORBIS_NO_STDIO +#include +#endif + +#ifndef STB_VORBIS_NO_CRT +#include +#include +#include +#include +#if !(defined(__APPLE__) || defined(MACOSX) || defined(macintosh) || defined(Macintosh)) +#include +#if defined(__linux__) || defined(__linux) || defined(__EMSCRIPTEN__) +#include +#endif +#endif +#else // STB_VORBIS_NO_CRT +#define NULL 0 +#define malloc(s) 0 +#define free(s) ((void) 0) +#define realloc(s) 0 +#endif // STB_VORBIS_NO_CRT + +#include + +#ifdef __MINGW32__ + // eff you mingw: + // "fixed": + // http://sourceforge.net/p/mingw-w64/mailman/message/32882927/ + // "no that broke the build, reverted, who cares about C": + // http://sourceforge.net/p/mingw-w64/mailman/message/32890381/ + #ifdef __forceinline + #undef __forceinline + #endif + #define __forceinline +#elif !defined(_MSC_VER) + #if __GNUC__ + #define __forceinline inline + #else + #define __forceinline + #endif +#endif + +#if STB_VORBIS_MAX_CHANNELS > 256 +#error "Value of STB_VORBIS_MAX_CHANNELS outside of allowed range" +#endif + +#if STB_VORBIS_FAST_HUFFMAN_LENGTH > 24 +#error "Value of STB_VORBIS_FAST_HUFFMAN_LENGTH outside of allowed range" +#endif + + +#if 0 +#include +#define CHECK(f) _CrtIsValidHeapPointer(f->channel_buffers[1]) +#else +#define CHECK(f) ((void) 0) +#endif + +#define MAX_BLOCKSIZE_LOG 13 // from specification +#define MAX_BLOCKSIZE (1 << MAX_BLOCKSIZE_LOG) + + +typedef unsigned char uint8; +typedef signed char int8; +typedef unsigned short uint16; +typedef signed short int16; +typedef unsigned int uint32; +typedef signed int int32; + +#ifndef TRUE +#define TRUE 1 +#define FALSE 0 +#endif + +typedef float codetype; + +// @NOTE +// +// Some arrays below are tagged "//varies", which means it's actually +// a variable-sized piece of data, but rather than malloc I assume it's +// small enough it's better to just allocate it all together with the +// main thing +// +// Most of the variables are specified with the smallest size I could pack +// them into. It might give better performance to make them all full-sized +// integers. It should be safe to freely rearrange the structures or change +// the sizes larger--nothing relies on silently truncating etc., nor the +// order of variables. + +#define FAST_HUFFMAN_TABLE_SIZE (1 << STB_VORBIS_FAST_HUFFMAN_LENGTH) +#define FAST_HUFFMAN_TABLE_MASK (FAST_HUFFMAN_TABLE_SIZE - 1) + +typedef struct +{ + int dimensions, entries; + uint8 *codeword_lengths; + float minimum_value; + float delta_value; + uint8 value_bits; + uint8 lookup_type; + uint8 sequence_p; + uint8 sparse; + uint32 lookup_values; + codetype *multiplicands; + uint32 *codewords; + #ifdef STB_VORBIS_FAST_HUFFMAN_SHORT + int16 fast_huffman[FAST_HUFFMAN_TABLE_SIZE]; + #else + int32 fast_huffman[FAST_HUFFMAN_TABLE_SIZE]; + #endif + uint32 *sorted_codewords; + int *sorted_values; + int sorted_entries; +} Codebook; + +typedef struct +{ + uint8 order; + uint16 rate; + uint16 bark_map_size; + uint8 amplitude_bits; + uint8 amplitude_offset; + uint8 number_of_books; + uint8 book_list[16]; // varies +} Floor0; + +typedef struct +{ + uint8 partitions; + uint8 partition_class_list[32]; // varies + uint8 class_dimensions[16]; // varies + uint8 class_subclasses[16]; // varies + uint8 class_masterbooks[16]; // varies + int16 subclass_books[16][8]; // varies + uint16 Xlist[31*8+2]; // varies + uint8 sorted_order[31*8+2]; + uint8 neighbors[31*8+2][2]; + uint8 floor1_multiplier; + uint8 rangebits; + int values; +} Floor1; + +typedef union +{ + Floor0 floor0; + Floor1 floor1; +} Floor; + +typedef struct +{ + uint32 begin, end; + uint32 part_size; + uint8 classifications; + uint8 classbook; + uint8 **classdata; + int16 (*residue_books)[8]; +} Residue; + +typedef struct +{ + uint8 magnitude; + uint8 angle; + uint8 mux; +} MappingChannel; + +typedef struct +{ + uint16 coupling_steps; + MappingChannel *chan; + uint8 submaps; + uint8 submap_floor[15]; // varies + uint8 submap_residue[15]; // varies +} Mapping; + +typedef struct +{ + uint8 blockflag; + uint8 mapping; + uint16 windowtype; + uint16 transformtype; +} Mode; + +typedef struct +{ + uint32 goal_crc; // expected crc if match + int bytes_left; // bytes left in packet + uint32 crc_so_far; // running crc + int bytes_done; // bytes processed in _current_ chunk + uint32 sample_loc; // granule pos encoded in page +} CRCscan; + +typedef struct +{ + uint32 page_start, page_end; + uint32 last_decoded_sample; +} ProbedPage; + +struct stb_vorbis +{ + // user-accessible info + unsigned int sample_rate; + int channels; + + unsigned int setup_memory_required; + unsigned int temp_memory_required; + unsigned int setup_temp_memory_required; + + // input config +#ifndef STB_VORBIS_NO_STDIO + FILE *f; + uint32 f_start; + int close_on_free; +#endif + + uint8 *stream; + uint8 *stream_start; + uint8 *stream_end; + + uint32 stream_len; + + uint8 push_mode; + + uint32 first_audio_page_offset; + + ProbedPage p_first, p_last; + + // memory management + stb_vorbis_alloc alloc; + int setup_offset; + int temp_offset; + + // run-time results + int eof; + enum STBVorbisError error; + + // user-useful data + + // header info + int blocksize[2]; + int blocksize_0, blocksize_1; + int codebook_count; + Codebook *codebooks; + int floor_count; + uint16 floor_types[64]; // varies + Floor *floor_config; + int residue_count; + uint16 residue_types[64]; // varies + Residue *residue_config; + int mapping_count; + Mapping *mapping; + int mode_count; + Mode mode_config[64]; // varies + + uint32 total_samples; + + // decode buffer + float *channel_buffers[STB_VORBIS_MAX_CHANNELS]; + float *outputs [STB_VORBIS_MAX_CHANNELS]; + + float *previous_window[STB_VORBIS_MAX_CHANNELS]; + int previous_length; + + #ifndef STB_VORBIS_NO_DEFER_FLOOR + int16 *finalY[STB_VORBIS_MAX_CHANNELS]; + #else + float *floor_buffers[STB_VORBIS_MAX_CHANNELS]; + #endif + + uint32 current_loc; // sample location of next frame to decode + int current_loc_valid; + + // per-blocksize precomputed data + + // twiddle factors + float *A[2],*B[2],*C[2]; + float *window[2]; + uint16 *bit_reverse[2]; + + // current page/packet/segment streaming info + uint32 serial; // stream serial number for verification + int last_page; + int segment_count; + uint8 segments[255]; + uint8 page_flag; + uint8 bytes_in_seg; + uint8 first_decode; + int next_seg; + int last_seg; // flag that we're on the last segment + int last_seg_which; // what was the segment number of the last seg? + uint32 acc; + int valid_bits; + int packet_bytes; + int end_seg_with_known_loc; + uint32 known_loc_for_packet; + int discard_samples_deferred; + uint32 samples_output; + + // push mode scanning + int page_crc_tests; // only in push_mode: number of tests active; -1 if not searching +#ifndef STB_VORBIS_NO_PUSHDATA_API + CRCscan scan[STB_VORBIS_PUSHDATA_CRC_COUNT]; +#endif + + // sample-access + int channel_buffer_start; + int channel_buffer_end; +}; + +#if defined(STB_VORBIS_NO_PUSHDATA_API) + #define IS_PUSH_MODE(f) FALSE +#elif defined(STB_VORBIS_NO_PULLDATA_API) + #define IS_PUSH_MODE(f) TRUE +#else + #define IS_PUSH_MODE(f) ((f)->push_mode) +#endif + +typedef struct stb_vorbis vorb; + +static int error(vorb *f, enum STBVorbisError e) +{ + f->error = e; + if (!f->eof && e != VORBIS_need_more_data) { + f->error=e; // breakpoint for debugging + } + return 0; +} + + +// these functions are used for allocating temporary memory +// while decoding. if you can afford the stack space, use +// alloca(); otherwise, provide a temp buffer and it will +// allocate out of those. + +#define array_size_required(count,size) (count*(sizeof(void *)+(size))) + +#define temp_alloc(f,size) (f->alloc.alloc_buffer ? setup_temp_malloc(f,size) : alloca(size)) +#ifdef dealloca +#define temp_free(f,p) (f->alloc.alloc_buffer ? 0 : dealloca(size)) +#else +#define temp_free(f,p) 0 +#endif +#define temp_alloc_save(f) ((f)->temp_offset) +#define temp_alloc_restore(f,p) ((f)->temp_offset = (p)) + +#define temp_block_array(f,count,size) make_block_array(temp_alloc(f,array_size_required(count,size)), count, size) + +// given a sufficiently large block of memory, make an array of pointers to subblocks of it +static void *make_block_array(void *mem, int count, int size) +{ + int i; + void ** p = (void **) mem; + char *q = (char *) (p + count); + for (i=0; i < count; ++i) { + p[i] = q; + q += size; + } + return p; +} + +static void *setup_malloc(vorb *f, int sz) +{ + sz = (sz+3) & ~3; + f->setup_memory_required += sz; + if (f->alloc.alloc_buffer) { + void *p = (char *) f->alloc.alloc_buffer + f->setup_offset; + if (f->setup_offset + sz > f->temp_offset) return NULL; + f->setup_offset += sz; + return p; + } + return sz ? malloc(sz) : NULL; +} + +static void setup_free(vorb *f, void *p) +{ + if (f->alloc.alloc_buffer) return; // do nothing; setup mem is a stack + free(p); +} + +static void *setup_temp_malloc(vorb *f, int sz) +{ + sz = (sz+3) & ~3; + if (f->alloc.alloc_buffer) { + if (f->temp_offset - sz < f->setup_offset) return NULL; + f->temp_offset -= sz; + return (char *) f->alloc.alloc_buffer + f->temp_offset; + } + return malloc(sz); +} + +static void setup_temp_free(vorb *f, void *p, int sz) +{ + if (f->alloc.alloc_buffer) { + f->temp_offset += (sz+3)&~3; + return; + } + free(p); +} + +#define CRC32_POLY 0x04c11db7 // from spec + +static uint32 crc_table[256]; +static void crc32_init(void) +{ + int i,j; + uint32 s; + for(i=0; i < 256; i++) { + for (s=(uint32) i << 24, j=0; j < 8; ++j) + s = (s << 1) ^ (s >= (1U<<31) ? CRC32_POLY : 0); + crc_table[i] = s; + } +} + +static __forceinline uint32 crc32_update(uint32 crc, uint8 byte) +{ + return (crc << 8) ^ crc_table[byte ^ (crc >> 24)]; +} + + +// used in setup, and for huffman that doesn't go fast path +static unsigned int bit_reverse(unsigned int n) +{ + n = ((n & 0xAAAAAAAA) >> 1) | ((n & 0x55555555) << 1); + n = ((n & 0xCCCCCCCC) >> 2) | ((n & 0x33333333) << 2); + n = ((n & 0xF0F0F0F0) >> 4) | ((n & 0x0F0F0F0F) << 4); + n = ((n & 0xFF00FF00) >> 8) | ((n & 0x00FF00FF) << 8); + return (n >> 16) | (n << 16); +} + +static float square(float x) +{ + return x*x; +} + +// this is a weird definition of log2() for which log2(1) = 1, log2(2) = 2, log2(4) = 3 +// as required by the specification. fast(?) implementation from stb.h +// @OPTIMIZE: called multiple times per-packet with "constants"; move to setup +static int ilog(int32 n) +{ + static signed char log2_4[16] = { 0,1,2,2,3,3,3,3,4,4,4,4,4,4,4,4 }; + + // 2 compares if n < 16, 3 compares otherwise (4 if signed or n > 1<<29) + if (n < (1 << 14)) + if (n < (1 << 4)) return 0 + log2_4[n ]; + else if (n < (1 << 9)) return 5 + log2_4[n >> 5]; + else return 10 + log2_4[n >> 10]; + else if (n < (1 << 24)) + if (n < (1 << 19)) return 15 + log2_4[n >> 15]; + else return 20 + log2_4[n >> 20]; + else if (n < (1 << 29)) return 25 + log2_4[n >> 25]; + else if (n < (1 << 31)) return 30 + log2_4[n >> 30]; + else return 0; // signed n returns 0 +} + +#ifndef M_PI + #define M_PI 3.14159265358979323846264f // from CRC +#endif + +// code length assigned to a value with no huffman encoding +#define NO_CODE 255 + +/////////////////////// LEAF SETUP FUNCTIONS ////////////////////////// +// +// these functions are only called at setup, and only a few times +// per file + +static float float32_unpack(uint32 x) +{ + // from the specification + uint32 mantissa = x & 0x1fffff; + uint32 sign = x & 0x80000000; + uint32 exp = (x & 0x7fe00000) >> 21; + double res = sign ? -(double)mantissa : (double)mantissa; + return (float) ldexp((float)res, exp-788); +} + + +// zlib & jpeg huffman tables assume that the output symbols +// can either be arbitrarily arranged, or have monotonically +// increasing frequencies--they rely on the lengths being sorted; +// this makes for a very simple generation algorithm. +// vorbis allows a huffman table with non-sorted lengths. This +// requires a more sophisticated construction, since symbols in +// order do not map to huffman codes "in order". +static void add_entry(Codebook *c, uint32 huff_code, int symbol, int count, int len, uint32 *values) +{ + if (!c->sparse) { + c->codewords [symbol] = huff_code; + } else { + c->codewords [count] = huff_code; + c->codeword_lengths[count] = len; + values [count] = symbol; + } +} + +static int compute_codewords(Codebook *c, uint8 *len, int n, uint32 *values) +{ + int i,k,m=0; + uint32 available[32]; + + memset(available, 0, sizeof(available)); + // find the first entry + for (k=0; k < n; ++k) if (len[k] < NO_CODE) break; + if (k == n) { assert(c->sorted_entries == 0); return TRUE; } + // add to the list + add_entry(c, 0, k, m++, len[k], values); + // add all available leaves + for (i=1; i <= len[k]; ++i) + available[i] = 1U << (32-i); + // note that the above code treats the first case specially, + // but it's really the same as the following code, so they + // could probably be combined (except the initial code is 0, + // and I use 0 in available[] to mean 'empty') + for (i=k+1; i < n; ++i) { + uint32 res; + int z = len[i], y; + if (z == NO_CODE) continue; + // find lowest available leaf (should always be earliest, + // which is what the specification calls for) + // note that this property, and the fact we can never have + // more than one free leaf at a given level, isn't totally + // trivial to prove, but it seems true and the assert never + // fires, so! + while (z > 0 && !available[z]) --z; + if (z == 0) { return FALSE; } + res = available[z]; + assert(z >= 0 && z < 32); + available[z] = 0; + add_entry(c, bit_reverse(res), i, m++, len[i], values); + // propogate availability up the tree + if (z != len[i]) { + assert(len[i] >= 0 && len[i] < 32); + for (y=len[i]; y > z; --y) { + assert(available[y] == 0); + available[y] = res + (1 << (32-y)); + } + } + } + return TRUE; +} + +// accelerated huffman table allows fast O(1) match of all symbols +// of length <= STB_VORBIS_FAST_HUFFMAN_LENGTH +static void compute_accelerated_huffman(Codebook *c) +{ + int i, len; + for (i=0; i < FAST_HUFFMAN_TABLE_SIZE; ++i) + c->fast_huffman[i] = -1; + + len = c->sparse ? c->sorted_entries : c->entries; + #ifdef STB_VORBIS_FAST_HUFFMAN_SHORT + if (len > 32767) len = 32767; // largest possible value we can encode! + #endif + for (i=0; i < len; ++i) { + if (c->codeword_lengths[i] <= STB_VORBIS_FAST_HUFFMAN_LENGTH) { + uint32 z = c->sparse ? bit_reverse(c->sorted_codewords[i]) : c->codewords[i]; + // set table entries for all bit combinations in the higher bits + while (z < FAST_HUFFMAN_TABLE_SIZE) { + c->fast_huffman[z] = i; + z += 1 << c->codeword_lengths[i]; + } + } + } +} + +#ifdef _MSC_VER +#define STBV_CDECL __cdecl +#else +#define STBV_CDECL +#endif + +static int STBV_CDECL uint32_compare(const void *p, const void *q) +{ + uint32 x = * (uint32 *) p; + uint32 y = * (uint32 *) q; + return x < y ? -1 : x > y; +} + +static int include_in_sort(Codebook *c, uint8 len) +{ + if (c->sparse) { assert(len != NO_CODE); return TRUE; } + if (len == NO_CODE) return FALSE; + if (len > STB_VORBIS_FAST_HUFFMAN_LENGTH) return TRUE; + return FALSE; +} + +// if the fast table above doesn't work, we want to binary +// search them... need to reverse the bits +static void compute_sorted_huffman(Codebook *c, uint8 *lengths, uint32 *values) +{ + int i, len; + // build a list of all the entries + // OPTIMIZATION: don't include the short ones, since they'll be caught by FAST_HUFFMAN. + // this is kind of a frivolous optimization--I don't see any performance improvement, + // but it's like 4 extra lines of code, so. + if (!c->sparse) { + int k = 0; + for (i=0; i < c->entries; ++i) + if (include_in_sort(c, lengths[i])) + c->sorted_codewords[k++] = bit_reverse(c->codewords[i]); + assert(k == c->sorted_entries); + } else { + for (i=0; i < c->sorted_entries; ++i) + c->sorted_codewords[i] = bit_reverse(c->codewords[i]); + } + + qsort(c->sorted_codewords, c->sorted_entries, sizeof(c->sorted_codewords[0]), uint32_compare); + c->sorted_codewords[c->sorted_entries] = 0xffffffff; + + len = c->sparse ? c->sorted_entries : c->entries; + // now we need to indicate how they correspond; we could either + // #1: sort a different data structure that says who they correspond to + // #2: for each sorted entry, search the original list to find who corresponds + // #3: for each original entry, find the sorted entry + // #1 requires extra storage, #2 is slow, #3 can use binary search! + for (i=0; i < len; ++i) { + int huff_len = c->sparse ? lengths[values[i]] : lengths[i]; + if (include_in_sort(c,huff_len)) { + uint32 code = bit_reverse(c->codewords[i]); + int x=0, n=c->sorted_entries; + while (n > 1) { + // invariant: sc[x] <= code < sc[x+n] + int m = x + (n >> 1); + if (c->sorted_codewords[m] <= code) { + x = m; + n -= (n>>1); + } else { + n >>= 1; + } + } + assert(c->sorted_codewords[x] == code); + if (c->sparse) { + c->sorted_values[x] = values[i]; + c->codeword_lengths[x] = huff_len; + } else { + c->sorted_values[x] = i; + } + } + } +} + +// only run while parsing the header (3 times) +static int vorbis_validate(uint8 *data) +{ + static uint8 vorbis[6] = { 'v', 'o', 'r', 'b', 'i', 's' }; + return memcmp(data, vorbis, 6) == 0; +} + +// called from setup only, once per code book +// (formula implied by specification) +static int lookup1_values(int entries, int dim) +{ + int r = (int) floor(exp((float) log((float) entries) / dim)); + if ((int) floor(pow((float) r+1, dim)) <= entries) // (int) cast for MinGW warning; + ++r; // floor() to avoid _ftol() when non-CRT + assert(pow((float) r+1, dim) > entries); + assert((int) floor(pow((float) r, dim)) <= entries); // (int),floor() as above + return r; +} + +// called twice per file +static void compute_twiddle_factors(int n, float *A, float *B, float *C) +{ + int n4 = n >> 2, n8 = n >> 3; + int k,k2; + + for (k=k2=0; k < n4; ++k,k2+=2) { + A[k2 ] = (float) cos(4*k*M_PI/n); + A[k2+1] = (float) -sin(4*k*M_PI/n); + B[k2 ] = (float) cos((k2+1)*M_PI/n/2) * 0.5f; + B[k2+1] = (float) sin((k2+1)*M_PI/n/2) * 0.5f; + } + for (k=k2=0; k < n8; ++k,k2+=2) { + C[k2 ] = (float) cos(2*(k2+1)*M_PI/n); + C[k2+1] = (float) -sin(2*(k2+1)*M_PI/n); + } +} + +static void compute_window(int n, float *window) +{ + int n2 = n >> 1, i; + for (i=0; i < n2; ++i) + window[i] = (float) sin(0.5 * M_PI * square((float) sin((i - 0 + 0.5) / n2 * 0.5 * M_PI))); +} + +static void compute_bitreverse(int n, uint16 *rev) +{ + int ld = ilog(n) - 1; // ilog is off-by-one from normal definitions + int i, n8 = n >> 3; + for (i=0; i < n8; ++i) + rev[i] = (bit_reverse(i) >> (32-ld+3)) << 2; +} + +static int init_blocksize(vorb *f, int b, int n) +{ + int n2 = n >> 1, n4 = n >> 2, n8 = n >> 3; + f->A[b] = (float *) setup_malloc(f, sizeof(float) * n2); + f->B[b] = (float *) setup_malloc(f, sizeof(float) * n2); + f->C[b] = (float *) setup_malloc(f, sizeof(float) * n4); + if (!f->A[b] || !f->B[b] || !f->C[b]) return error(f, VORBIS_outofmem); + compute_twiddle_factors(n, f->A[b], f->B[b], f->C[b]); + f->window[b] = (float *) setup_malloc(f, sizeof(float) * n2); + if (!f->window[b]) return error(f, VORBIS_outofmem); + compute_window(n, f->window[b]); + f->bit_reverse[b] = (uint16 *) setup_malloc(f, sizeof(uint16) * n8); + if (!f->bit_reverse[b]) return error(f, VORBIS_outofmem); + compute_bitreverse(n, f->bit_reverse[b]); + return TRUE; +} + +static void neighbors(uint16 *x, int n, int *plow, int *phigh) +{ + int low = -1; + int high = 65536; + int i; + for (i=0; i < n; ++i) { + if (x[i] > low && x[i] < x[n]) { *plow = i; low = x[i]; } + if (x[i] < high && x[i] > x[n]) { *phigh = i; high = x[i]; } + } +} + +// this has been repurposed so y is now the original index instead of y +typedef struct +{ + uint16 x,y; +} Point; + +static int STBV_CDECL point_compare(const void *p, const void *q) +{ + Point *a = (Point *) p; + Point *b = (Point *) q; + return a->x < b->x ? -1 : a->x > b->x; +} + +// +/////////////////////// END LEAF SETUP FUNCTIONS ////////////////////////// + + +#if defined(STB_VORBIS_NO_STDIO) + #define USE_MEMORY(z) TRUE +#else + #define USE_MEMORY(z) ((z)->stream) +#endif + +static uint8 get8(vorb *z) +{ + if (USE_MEMORY(z)) { + if (z->stream >= z->stream_end) { z->eof = TRUE; return 0; } + return *z->stream++; + } + + #ifndef STB_VORBIS_NO_STDIO + { + int c = fgetc(z->f); + if (c == EOF) { z->eof = TRUE; return 0; } + return c; + } + #endif +} + +static uint32 get32(vorb *f) +{ + uint32 x; + x = get8(f); + x += get8(f) << 8; + x += get8(f) << 16; + x += (uint32) get8(f) << 24; + return x; +} + +static int getn(vorb *z, uint8 *data, int n) +{ + if (USE_MEMORY(z)) { + if (z->stream+n > z->stream_end) { z->eof = 1; return 0; } + memcpy(data, z->stream, n); + z->stream += n; + return 1; + } + + #ifndef STB_VORBIS_NO_STDIO + if (fread(data, n, 1, z->f) == 1) + return 1; + else { + z->eof = 1; + return 0; + } + #endif +} + +static void skip(vorb *z, int n) +{ + if (USE_MEMORY(z)) { + z->stream += n; + if (z->stream >= z->stream_end) z->eof = 1; + return; + } + #ifndef STB_VORBIS_NO_STDIO + { + long x = ftell(z->f); + fseek(z->f, x+n, SEEK_SET); + } + #endif +} + +static int set_file_offset(stb_vorbis *f, unsigned int loc) +{ + #ifndef STB_VORBIS_NO_PUSHDATA_API + if (f->push_mode) return 0; + #endif + f->eof = 0; + if (USE_MEMORY(f)) { + if (f->stream_start + loc >= f->stream_end || f->stream_start + loc < f->stream_start) { + f->stream = f->stream_end; + f->eof = 1; + return 0; + } else { + f->stream = f->stream_start + loc; + return 1; + } + } + #ifndef STB_VORBIS_NO_STDIO + if (loc + f->f_start < loc || loc >= 0x80000000) { + loc = 0x7fffffff; + f->eof = 1; + } else { + loc += f->f_start; + } + if (!fseek(f->f, loc, SEEK_SET)) + return 1; + f->eof = 1; + fseek(f->f, f->f_start, SEEK_END); + return 0; + #endif +} + + +static uint8 ogg_page_header[4] = { 0x4f, 0x67, 0x67, 0x53 }; + +static int capture_pattern(vorb *f) +{ + if (0x4f != get8(f)) return FALSE; + if (0x67 != get8(f)) return FALSE; + if (0x67 != get8(f)) return FALSE; + if (0x53 != get8(f)) return FALSE; + return TRUE; +} + +#define PAGEFLAG_continued_packet 1 +#define PAGEFLAG_first_page 2 +#define PAGEFLAG_last_page 4 + +static int start_page_no_capturepattern(vorb *f) +{ + uint32 loc0,loc1,n; + // stream structure version + if (0 != get8(f)) return error(f, VORBIS_invalid_stream_structure_version); + // header flag + f->page_flag = get8(f); + // absolute granule position + loc0 = get32(f); + loc1 = get32(f); + // @TODO: validate loc0,loc1 as valid positions? + // stream serial number -- vorbis doesn't interleave, so discard + get32(f); + //if (f->serial != get32(f)) return error(f, VORBIS_incorrect_stream_serial_number); + // page sequence number + n = get32(f); + f->last_page = n; + // CRC32 + get32(f); + // page_segments + f->segment_count = get8(f); + if (!getn(f, f->segments, f->segment_count)) + return error(f, VORBIS_unexpected_eof); + // assume we _don't_ know any the sample position of any segments + f->end_seg_with_known_loc = -2; + if (loc0 != ~0U || loc1 != ~0U) { + int i; + // determine which packet is the last one that will complete + for (i=f->segment_count-1; i >= 0; --i) + if (f->segments[i] < 255) + break; + // 'i' is now the index of the _last_ segment of a packet that ends + if (i >= 0) { + f->end_seg_with_known_loc = i; + f->known_loc_for_packet = loc0; + } + } + if (f->first_decode) { + int i,len; + ProbedPage p; + len = 0; + for (i=0; i < f->segment_count; ++i) + len += f->segments[i]; + len += 27 + f->segment_count; + p.page_start = f->first_audio_page_offset; + p.page_end = p.page_start + len; + p.last_decoded_sample = loc0; + f->p_first = p; + } + f->next_seg = 0; + return TRUE; +} + +static int start_page(vorb *f) +{ + if (!capture_pattern(f)) return error(f, VORBIS_missing_capture_pattern); + return start_page_no_capturepattern(f); +} + +static int start_packet(vorb *f) +{ + while (f->next_seg == -1) { + if (!start_page(f)) return FALSE; + if (f->page_flag & PAGEFLAG_continued_packet) + return error(f, VORBIS_continued_packet_flag_invalid); + } + f->last_seg = FALSE; + f->valid_bits = 0; + f->packet_bytes = 0; + f->bytes_in_seg = 0; + // f->next_seg is now valid + return TRUE; +} + +static int maybe_start_packet(vorb *f) +{ + if (f->next_seg == -1) { + int x = get8(f); + if (f->eof) return FALSE; // EOF at page boundary is not an error! + if (0x4f != x ) return error(f, VORBIS_missing_capture_pattern); + if (0x67 != get8(f)) return error(f, VORBIS_missing_capture_pattern); + if (0x67 != get8(f)) return error(f, VORBIS_missing_capture_pattern); + if (0x53 != get8(f)) return error(f, VORBIS_missing_capture_pattern); + if (!start_page_no_capturepattern(f)) return FALSE; + if (f->page_flag & PAGEFLAG_continued_packet) { + // set up enough state that we can read this packet if we want, + // e.g. during recovery + f->last_seg = FALSE; + f->bytes_in_seg = 0; + return error(f, VORBIS_continued_packet_flag_invalid); + } + } + return start_packet(f); +} + +static int next_segment(vorb *f) +{ + int len; + if (f->last_seg) return 0; + if (f->next_seg == -1) { + f->last_seg_which = f->segment_count-1; // in case start_page fails + if (!start_page(f)) { f->last_seg = 1; return 0; } + if (!(f->page_flag & PAGEFLAG_continued_packet)) return error(f, VORBIS_continued_packet_flag_invalid); + } + len = f->segments[f->next_seg++]; + if (len < 255) { + f->last_seg = TRUE; + f->last_seg_which = f->next_seg-1; + } + if (f->next_seg >= f->segment_count) + f->next_seg = -1; + assert(f->bytes_in_seg == 0); + f->bytes_in_seg = len; + return len; +} + +#define EOP (-1) +#define INVALID_BITS (-1) + +static int get8_packet_raw(vorb *f) +{ + if (!f->bytes_in_seg) { // CLANG! + if (f->last_seg) return EOP; + else if (!next_segment(f)) return EOP; + } + assert(f->bytes_in_seg > 0); + --f->bytes_in_seg; + ++f->packet_bytes; + return get8(f); +} + +static int get8_packet(vorb *f) +{ + int x = get8_packet_raw(f); + f->valid_bits = 0; + return x; +} + +static void flush_packet(vorb *f) +{ + while (get8_packet_raw(f) != EOP); +} + +// @OPTIMIZE: this is the secondary bit decoder, so it's probably not as important +// as the huffman decoder? +static uint32 get_bits(vorb *f, int n) +{ + uint32 z; + + if (f->valid_bits < 0) return 0; + if (f->valid_bits < n) { + if (n > 24) { + // the accumulator technique below would not work correctly in this case + z = get_bits(f, 24); + z += get_bits(f, n-24) << 24; + return z; + } + if (f->valid_bits == 0) f->acc = 0; + while (f->valid_bits < n) { + int z = get8_packet_raw(f); + if (z == EOP) { + f->valid_bits = INVALID_BITS; + return 0; + } + f->acc += z << f->valid_bits; + f->valid_bits += 8; + } + } + if (f->valid_bits < 0) return 0; + z = f->acc & ((1 << n)-1); + f->acc >>= n; + f->valid_bits -= n; + return z; +} + +// @OPTIMIZE: primary accumulator for huffman +// expand the buffer to as many bits as possible without reading off end of packet +// it might be nice to allow f->valid_bits and f->acc to be stored in registers, +// e.g. cache them locally and decode locally +static __forceinline void prep_huffman(vorb *f) +{ + if (f->valid_bits <= 24) { + if (f->valid_bits == 0) f->acc = 0; + do { + int z; + if (f->last_seg && !f->bytes_in_seg) return; + z = get8_packet_raw(f); + if (z == EOP) return; + f->acc += (unsigned) z << f->valid_bits; + f->valid_bits += 8; + } while (f->valid_bits <= 24); + } +} + +enum +{ + VORBIS_packet_id = 1, + VORBIS_packet_comment = 3, + VORBIS_packet_setup = 5 +}; + +static int codebook_decode_scalar_raw(vorb *f, Codebook *c) +{ + int i; + prep_huffman(f); + + if (c->codewords == NULL && c->sorted_codewords == NULL) + return -1; + + // cases to use binary search: sorted_codewords && !c->codewords + // sorted_codewords && c->entries > 8 + if (c->entries > 8 ? c->sorted_codewords!=NULL : !c->codewords) { + // binary search + uint32 code = bit_reverse(f->acc); + int x=0, n=c->sorted_entries, len; + + while (n > 1) { + // invariant: sc[x] <= code < sc[x+n] + int m = x + (n >> 1); + if (c->sorted_codewords[m] <= code) { + x = m; + n -= (n>>1); + } else { + n >>= 1; + } + } + // x is now the sorted index + if (!c->sparse) x = c->sorted_values[x]; + // x is now sorted index if sparse, or symbol otherwise + len = c->codeword_lengths[x]; + if (f->valid_bits >= len) { + f->acc >>= len; + f->valid_bits -= len; + return x; + } + + f->valid_bits = 0; + return -1; + } + + // if small, linear search + assert(!c->sparse); + for (i=0; i < c->entries; ++i) { + if (c->codeword_lengths[i] == NO_CODE) continue; + if (c->codewords[i] == (f->acc & ((1 << c->codeword_lengths[i])-1))) { + if (f->valid_bits >= c->codeword_lengths[i]) { + f->acc >>= c->codeword_lengths[i]; + f->valid_bits -= c->codeword_lengths[i]; + return i; + } + f->valid_bits = 0; + return -1; + } + } + + error(f, VORBIS_invalid_stream); + f->valid_bits = 0; + return -1; +} + +#ifndef STB_VORBIS_NO_INLINE_DECODE + +#define DECODE_RAW(var, f,c) \ + if (f->valid_bits < STB_VORBIS_FAST_HUFFMAN_LENGTH) \ + prep_huffman(f); \ + var = f->acc & FAST_HUFFMAN_TABLE_MASK; \ + var = c->fast_huffman[var]; \ + if (var >= 0) { \ + int n = c->codeword_lengths[var]; \ + f->acc >>= n; \ + f->valid_bits -= n; \ + if (f->valid_bits < 0) { f->valid_bits = 0; var = -1; } \ + } else { \ + var = codebook_decode_scalar_raw(f,c); \ + } + +#else + +static int codebook_decode_scalar(vorb *f, Codebook *c) +{ + int i; + if (f->valid_bits < STB_VORBIS_FAST_HUFFMAN_LENGTH) + prep_huffman(f); + // fast huffman table lookup + i = f->acc & FAST_HUFFMAN_TABLE_MASK; + i = c->fast_huffman[i]; + if (i >= 0) { + f->acc >>= c->codeword_lengths[i]; + f->valid_bits -= c->codeword_lengths[i]; + if (f->valid_bits < 0) { f->valid_bits = 0; return -1; } + return i; + } + return codebook_decode_scalar_raw(f,c); +} + +#define DECODE_RAW(var,f,c) var = codebook_decode_scalar(f,c); + +#endif + +#define DECODE(var,f,c) \ + DECODE_RAW(var,f,c) \ + if (c->sparse) var = c->sorted_values[var]; + +#ifndef STB_VORBIS_DIVIDES_IN_CODEBOOK + #define DECODE_VQ(var,f,c) DECODE_RAW(var,f,c) +#else + #define DECODE_VQ(var,f,c) DECODE(var,f,c) +#endif + + + + + + +// CODEBOOK_ELEMENT_FAST is an optimization for the CODEBOOK_FLOATS case +// where we avoid one addition +#define CODEBOOK_ELEMENT(c,off) (c->multiplicands[off]) +#define CODEBOOK_ELEMENT_FAST(c,off) (c->multiplicands[off]) +#define CODEBOOK_ELEMENT_BASE(c) (0) + +static int codebook_decode_start(vorb *f, Codebook *c) +{ + int z = -1; + + // type 0 is only legal in a scalar context + if (c->lookup_type == 0) + error(f, VORBIS_invalid_stream); + else { + DECODE_VQ(z,f,c); + if (c->sparse) assert(z < c->sorted_entries); + if (z < 0) { // check for EOP + if (!f->bytes_in_seg) + if (f->last_seg) + return z; + error(f, VORBIS_invalid_stream); + } + } + return z; +} + +static int codebook_decode(vorb *f, Codebook *c, float *output, int len) +{ + int i,z = codebook_decode_start(f,c); + if (z < 0) return FALSE; + if (len > c->dimensions) len = c->dimensions; + +#ifdef STB_VORBIS_DIVIDES_IN_CODEBOOK + if (c->lookup_type == 1) { + float last = CODEBOOK_ELEMENT_BASE(c); + int div = 1; + for (i=0; i < len; ++i) { + int off = (z / div) % c->lookup_values; + float val = CODEBOOK_ELEMENT_FAST(c,off) + last; + output[i] += val; + if (c->sequence_p) last = val + c->minimum_value; + div *= c->lookup_values; + } + return TRUE; + } +#endif + + z *= c->dimensions; + if (c->sequence_p) { + float last = CODEBOOK_ELEMENT_BASE(c); + for (i=0; i < len; ++i) { + float val = CODEBOOK_ELEMENT_FAST(c,z+i) + last; + output[i] += val; + last = val + c->minimum_value; + } + } else { + float last = CODEBOOK_ELEMENT_BASE(c); + for (i=0; i < len; ++i) { + output[i] += CODEBOOK_ELEMENT_FAST(c,z+i) + last; + } + } + + return TRUE; +} + +static int codebook_decode_step(vorb *f, Codebook *c, float *output, int len, int step) +{ + int i,z = codebook_decode_start(f,c); + float last = CODEBOOK_ELEMENT_BASE(c); + if (z < 0) return FALSE; + if (len > c->dimensions) len = c->dimensions; + +#ifdef STB_VORBIS_DIVIDES_IN_CODEBOOK + if (c->lookup_type == 1) { + int div = 1; + for (i=0; i < len; ++i) { + int off = (z / div) % c->lookup_values; + float val = CODEBOOK_ELEMENT_FAST(c,off) + last; + output[i*step] += val; + if (c->sequence_p) last = val; + div *= c->lookup_values; + } + return TRUE; + } +#endif + + z *= c->dimensions; + for (i=0; i < len; ++i) { + float val = CODEBOOK_ELEMENT_FAST(c,z+i) + last; + output[i*step] += val; + if (c->sequence_p) last = val; + } + + return TRUE; +} + +static int codebook_decode_deinterleave_repeat(vorb *f, Codebook *c, float **outputs, int ch, int *c_inter_p, int *p_inter_p, int len, int total_decode) +{ + int c_inter = *c_inter_p; + int p_inter = *p_inter_p; + int i,z, effective = c->dimensions; + + // type 0 is only legal in a scalar context + if (c->lookup_type == 0) return error(f, VORBIS_invalid_stream); + + while (total_decode > 0) { + float last = CODEBOOK_ELEMENT_BASE(c); + DECODE_VQ(z,f,c); + #ifndef STB_VORBIS_DIVIDES_IN_CODEBOOK + assert(!c->sparse || z < c->sorted_entries); + #endif + if (z < 0) { + if (!f->bytes_in_seg) + if (f->last_seg) return FALSE; + return error(f, VORBIS_invalid_stream); + } + + // if this will take us off the end of the buffers, stop short! + // we check by computing the length of the virtual interleaved + // buffer (len*ch), our current offset within it (p_inter*ch)+(c_inter), + // and the length we'll be using (effective) + if (c_inter + p_inter*ch + effective > len * ch) { + effective = len*ch - (p_inter*ch - c_inter); + } + + #ifdef STB_VORBIS_DIVIDES_IN_CODEBOOK + if (c->lookup_type == 1) { + int div = 1; + for (i=0; i < effective; ++i) { + int off = (z / div) % c->lookup_values; + float val = CODEBOOK_ELEMENT_FAST(c,off) + last; + if (outputs[c_inter]) + outputs[c_inter][p_inter] += val; + if (++c_inter == ch) { c_inter = 0; ++p_inter; } + if (c->sequence_p) last = val; + div *= c->lookup_values; + } + } else + #endif + { + z *= c->dimensions; + if (c->sequence_p) { + for (i=0; i < effective; ++i) { + float val = CODEBOOK_ELEMENT_FAST(c,z+i) + last; + if (outputs[c_inter]) + outputs[c_inter][p_inter] += val; + if (++c_inter == ch) { c_inter = 0; ++p_inter; } + last = val; + } + } else { + for (i=0; i < effective; ++i) { + float val = CODEBOOK_ELEMENT_FAST(c,z+i) + last; + if (outputs[c_inter]) + outputs[c_inter][p_inter] += val; + if (++c_inter == ch) { c_inter = 0; ++p_inter; } + } + } + } + + total_decode -= effective; + } + *c_inter_p = c_inter; + *p_inter_p = p_inter; + return TRUE; +} + +static int predict_point(int x, int x0, int x1, int y0, int y1) +{ + int dy = y1 - y0; + int adx = x1 - x0; + // @OPTIMIZE: force int division to round in the right direction... is this necessary on x86? + int err = abs(dy) * (x - x0); + int off = err / adx; + return dy < 0 ? y0 - off : y0 + off; +} + +// the following table is block-copied from the specification +static float inverse_db_table[256] = +{ + 1.0649863e-07f, 1.1341951e-07f, 1.2079015e-07f, 1.2863978e-07f, + 1.3699951e-07f, 1.4590251e-07f, 1.5538408e-07f, 1.6548181e-07f, + 1.7623575e-07f, 1.8768855e-07f, 1.9988561e-07f, 2.1287530e-07f, + 2.2670913e-07f, 2.4144197e-07f, 2.5713223e-07f, 2.7384213e-07f, + 2.9163793e-07f, 3.1059021e-07f, 3.3077411e-07f, 3.5226968e-07f, + 3.7516214e-07f, 3.9954229e-07f, 4.2550680e-07f, 4.5315863e-07f, + 4.8260743e-07f, 5.1396998e-07f, 5.4737065e-07f, 5.8294187e-07f, + 6.2082472e-07f, 6.6116941e-07f, 7.0413592e-07f, 7.4989464e-07f, + 7.9862701e-07f, 8.5052630e-07f, 9.0579828e-07f, 9.6466216e-07f, + 1.0273513e-06f, 1.0941144e-06f, 1.1652161e-06f, 1.2409384e-06f, + 1.3215816e-06f, 1.4074654e-06f, 1.4989305e-06f, 1.5963394e-06f, + 1.7000785e-06f, 1.8105592e-06f, 1.9282195e-06f, 2.0535261e-06f, + 2.1869758e-06f, 2.3290978e-06f, 2.4804557e-06f, 2.6416497e-06f, + 2.8133190e-06f, 2.9961443e-06f, 3.1908506e-06f, 3.3982101e-06f, + 3.6190449e-06f, 3.8542308e-06f, 4.1047004e-06f, 4.3714470e-06f, + 4.6555282e-06f, 4.9580707e-06f, 5.2802740e-06f, 5.6234160e-06f, + 5.9888572e-06f, 6.3780469e-06f, 6.7925283e-06f, 7.2339451e-06f, + 7.7040476e-06f, 8.2047000e-06f, 8.7378876e-06f, 9.3057248e-06f, + 9.9104632e-06f, 1.0554501e-05f, 1.1240392e-05f, 1.1970856e-05f, + 1.2748789e-05f, 1.3577278e-05f, 1.4459606e-05f, 1.5399272e-05f, + 1.6400004e-05f, 1.7465768e-05f, 1.8600792e-05f, 1.9809576e-05f, + 2.1096914e-05f, 2.2467911e-05f, 2.3928002e-05f, 2.5482978e-05f, + 2.7139006e-05f, 2.8902651e-05f, 3.0780908e-05f, 3.2781225e-05f, + 3.4911534e-05f, 3.7180282e-05f, 3.9596466e-05f, 4.2169667e-05f, + 4.4910090e-05f, 4.7828601e-05f, 5.0936773e-05f, 5.4246931e-05f, + 5.7772202e-05f, 6.1526565e-05f, 6.5524908e-05f, 6.9783085e-05f, + 7.4317983e-05f, 7.9147585e-05f, 8.4291040e-05f, 8.9768747e-05f, + 9.5602426e-05f, 0.00010181521f, 0.00010843174f, 0.00011547824f, + 0.00012298267f, 0.00013097477f, 0.00013948625f, 0.00014855085f, + 0.00015820453f, 0.00016848555f, 0.00017943469f, 0.00019109536f, + 0.00020351382f, 0.00021673929f, 0.00023082423f, 0.00024582449f, + 0.00026179955f, 0.00027881276f, 0.00029693158f, 0.00031622787f, + 0.00033677814f, 0.00035866388f, 0.00038197188f, 0.00040679456f, + 0.00043323036f, 0.00046138411f, 0.00049136745f, 0.00052329927f, + 0.00055730621f, 0.00059352311f, 0.00063209358f, 0.00067317058f, + 0.00071691700f, 0.00076350630f, 0.00081312324f, 0.00086596457f, + 0.00092223983f, 0.00098217216f, 0.0010459992f, 0.0011139742f, + 0.0011863665f, 0.0012634633f, 0.0013455702f, 0.0014330129f, + 0.0015261382f, 0.0016253153f, 0.0017309374f, 0.0018434235f, + 0.0019632195f, 0.0020908006f, 0.0022266726f, 0.0023713743f, + 0.0025254795f, 0.0026895994f, 0.0028643847f, 0.0030505286f, + 0.0032487691f, 0.0034598925f, 0.0036847358f, 0.0039241906f, + 0.0041792066f, 0.0044507950f, 0.0047400328f, 0.0050480668f, + 0.0053761186f, 0.0057254891f, 0.0060975636f, 0.0064938176f, + 0.0069158225f, 0.0073652516f, 0.0078438871f, 0.0083536271f, + 0.0088964928f, 0.009474637f, 0.010090352f, 0.010746080f, + 0.011444421f, 0.012188144f, 0.012980198f, 0.013823725f, + 0.014722068f, 0.015678791f, 0.016697687f, 0.017782797f, + 0.018938423f, 0.020169149f, 0.021479854f, 0.022875735f, + 0.024362330f, 0.025945531f, 0.027631618f, 0.029427276f, + 0.031339626f, 0.033376252f, 0.035545228f, 0.037855157f, + 0.040315199f, 0.042935108f, 0.045725273f, 0.048696758f, + 0.051861348f, 0.055231591f, 0.058820850f, 0.062643361f, + 0.066714279f, 0.071049749f, 0.075666962f, 0.080584227f, + 0.085821044f, 0.091398179f, 0.097337747f, 0.10366330f, + 0.11039993f, 0.11757434f, 0.12521498f, 0.13335215f, + 0.14201813f, 0.15124727f, 0.16107617f, 0.17154380f, + 0.18269168f, 0.19456402f, 0.20720788f, 0.22067342f, + 0.23501402f, 0.25028656f, 0.26655159f, 0.28387361f, + 0.30232132f, 0.32196786f, 0.34289114f, 0.36517414f, + 0.38890521f, 0.41417847f, 0.44109412f, 0.46975890f, + 0.50028648f, 0.53279791f, 0.56742212f, 0.60429640f, + 0.64356699f, 0.68538959f, 0.72993007f, 0.77736504f, + 0.82788260f, 0.88168307f, 0.9389798f, 1.0f +}; + + +// @OPTIMIZE: if you want to replace this bresenham line-drawing routine, +// note that you must produce bit-identical output to decode correctly; +// this specific sequence of operations is specified in the spec (it's +// drawing integer-quantized frequency-space lines that the encoder +// expects to be exactly the same) +// ... also, isn't the whole point of Bresenham's algorithm to NOT +// have to divide in the setup? sigh. +#ifndef STB_VORBIS_NO_DEFER_FLOOR +#define LINE_OP(a,b) a *= b +#else +#define LINE_OP(a,b) a = b +#endif + +#ifdef STB_VORBIS_DIVIDE_TABLE +#define DIVTAB_NUMER 32 +#define DIVTAB_DENOM 64 +int8 integer_divide_table[DIVTAB_NUMER][DIVTAB_DENOM]; // 2KB +#endif + +static __forceinline void draw_line(float *output, int x0, int y0, int x1, int y1, int n) +{ + int dy = y1 - y0; + int adx = x1 - x0; + int ady = abs(dy); + int base; + int x=x0,y=y0; + int err = 0; + int sy; + +#ifdef STB_VORBIS_DIVIDE_TABLE + if (adx < DIVTAB_DENOM && ady < DIVTAB_NUMER) { + if (dy < 0) { + base = -integer_divide_table[ady][adx]; + sy = base-1; + } else { + base = integer_divide_table[ady][adx]; + sy = base+1; + } + } else { + base = dy / adx; + if (dy < 0) + sy = base - 1; + else + sy = base+1; + } +#else + base = dy / adx; + if (dy < 0) + sy = base - 1; + else + sy = base+1; +#endif + ady -= abs(base) * adx; + if (x1 > n) x1 = n; + if (x < x1) { + LINE_OP(output[x], inverse_db_table[y]); + for (++x; x < x1; ++x) { + err += ady; + if (err >= adx) { + err -= adx; + y += sy; + } else + y += base; + LINE_OP(output[x], inverse_db_table[y]); + } + } +} + +static int residue_decode(vorb *f, Codebook *book, float *target, int offset, int n, int rtype) +{ + int k; + if (rtype == 0) { + int step = n / book->dimensions; + for (k=0; k < step; ++k) + if (!codebook_decode_step(f, book, target+offset+k, n-offset-k, step)) + return FALSE; + } else { + for (k=0; k < n; ) { + if (!codebook_decode(f, book, target+offset, n-k)) + return FALSE; + k += book->dimensions; + offset += book->dimensions; + } + } + return TRUE; +} + +static void decode_residue(vorb *f, float *residue_buffers[], int ch, int n, int rn, uint8 *do_not_decode) +{ + int i,j,pass; + Residue *r = f->residue_config + rn; + int rtype = f->residue_types[rn]; + int c = r->classbook; + int classwords = f->codebooks[c].dimensions; + int n_read = r->end - r->begin; + int part_read = n_read / r->part_size; + int temp_alloc_point = temp_alloc_save(f); + #ifndef STB_VORBIS_DIVIDES_IN_RESIDUE + uint8 ***part_classdata = (uint8 ***) temp_block_array(f,f->channels, part_read * sizeof(**part_classdata)); + #else + int **classifications = (int **) temp_block_array(f,f->channels, part_read * sizeof(**classifications)); + #endif + + CHECK(f); + + for (i=0; i < ch; ++i) + if (!do_not_decode[i]) + memset(residue_buffers[i], 0, sizeof(float) * n); + + if (rtype == 2 && ch != 1) { + for (j=0; j < ch; ++j) + if (!do_not_decode[j]) + break; + if (j == ch) + goto done; + + for (pass=0; pass < 8; ++pass) { + int pcount = 0, class_set = 0; + if (ch == 2) { + while (pcount < part_read) { + int z = r->begin + pcount*r->part_size; + int c_inter = (z & 1), p_inter = z>>1; + if (pass == 0) { + Codebook *c = f->codebooks+r->classbook; + int q; + DECODE(q,f,c); + if (q == EOP) goto done; + #ifndef STB_VORBIS_DIVIDES_IN_RESIDUE + part_classdata[0][class_set] = r->classdata[q]; + #else + for (i=classwords-1; i >= 0; --i) { + classifications[0][i+pcount] = q % r->classifications; + q /= r->classifications; + } + #endif + } + for (i=0; i < classwords && pcount < part_read; ++i, ++pcount) { + int z = r->begin + pcount*r->part_size; + #ifndef STB_VORBIS_DIVIDES_IN_RESIDUE + int c = part_classdata[0][class_set][i]; + #else + int c = classifications[0][pcount]; + #endif + int b = r->residue_books[c][pass]; + if (b >= 0) { + Codebook *book = f->codebooks + b; + #ifdef STB_VORBIS_DIVIDES_IN_CODEBOOK + if (!codebook_decode_deinterleave_repeat(f, book, residue_buffers, ch, &c_inter, &p_inter, n, r->part_size)) + goto done; + #else + // saves 1% + if (!codebook_decode_deinterleave_repeat(f, book, residue_buffers, ch, &c_inter, &p_inter, n, r->part_size)) + goto done; + #endif + } else { + z += r->part_size; + c_inter = z & 1; + p_inter = z >> 1; + } + } + #ifndef STB_VORBIS_DIVIDES_IN_RESIDUE + ++class_set; + #endif + } + } else if (ch == 1) { + while (pcount < part_read) { + int z = r->begin + pcount*r->part_size; + int c_inter = 0, p_inter = z; + if (pass == 0) { + Codebook *c = f->codebooks+r->classbook; + int q; + DECODE(q,f,c); + if (q == EOP) goto done; + #ifndef STB_VORBIS_DIVIDES_IN_RESIDUE + part_classdata[0][class_set] = r->classdata[q]; + #else + for (i=classwords-1; i >= 0; --i) { + classifications[0][i+pcount] = q % r->classifications; + q /= r->classifications; + } + #endif + } + for (i=0; i < classwords && pcount < part_read; ++i, ++pcount) { + int z = r->begin + pcount*r->part_size; + #ifndef STB_VORBIS_DIVIDES_IN_RESIDUE + int c = part_classdata[0][class_set][i]; + #else + int c = classifications[0][pcount]; + #endif + int b = r->residue_books[c][pass]; + if (b >= 0) { + Codebook *book = f->codebooks + b; + if (!codebook_decode_deinterleave_repeat(f, book, residue_buffers, ch, &c_inter, &p_inter, n, r->part_size)) + goto done; + } else { + z += r->part_size; + c_inter = 0; + p_inter = z; + } + } + #ifndef STB_VORBIS_DIVIDES_IN_RESIDUE + ++class_set; + #endif + } + } else { + while (pcount < part_read) { + int z = r->begin + pcount*r->part_size; + int c_inter = z % ch, p_inter = z/ch; + if (pass == 0) { + Codebook *c = f->codebooks+r->classbook; + int q; + DECODE(q,f,c); + if (q == EOP) goto done; + #ifndef STB_VORBIS_DIVIDES_IN_RESIDUE + part_classdata[0][class_set] = r->classdata[q]; + #else + for (i=classwords-1; i >= 0; --i) { + classifications[0][i+pcount] = q % r->classifications; + q /= r->classifications; + } + #endif + } + for (i=0; i < classwords && pcount < part_read; ++i, ++pcount) { + int z = r->begin + pcount*r->part_size; + #ifndef STB_VORBIS_DIVIDES_IN_RESIDUE + int c = part_classdata[0][class_set][i]; + #else + int c = classifications[0][pcount]; + #endif + int b = r->residue_books[c][pass]; + if (b >= 0) { + Codebook *book = f->codebooks + b; + if (!codebook_decode_deinterleave_repeat(f, book, residue_buffers, ch, &c_inter, &p_inter, n, r->part_size)) + goto done; + } else { + z += r->part_size; + c_inter = z % ch; + p_inter = z / ch; + } + } + #ifndef STB_VORBIS_DIVIDES_IN_RESIDUE + ++class_set; + #endif + } + } + } + goto done; + } + CHECK(f); + + for (pass=0; pass < 8; ++pass) { + int pcount = 0, class_set=0; + while (pcount < part_read) { + if (pass == 0) { + for (j=0; j < ch; ++j) { + if (!do_not_decode[j]) { + Codebook *c = f->codebooks+r->classbook; + int temp; + DECODE(temp,f,c); + if (temp == EOP) goto done; + #ifndef STB_VORBIS_DIVIDES_IN_RESIDUE + part_classdata[j][class_set] = r->classdata[temp]; + #else + for (i=classwords-1; i >= 0; --i) { + classifications[j][i+pcount] = temp % r->classifications; + temp /= r->classifications; + } + #endif + } + } + } + for (i=0; i < classwords && pcount < part_read; ++i, ++pcount) { + for (j=0; j < ch; ++j) { + if (!do_not_decode[j]) { + #ifndef STB_VORBIS_DIVIDES_IN_RESIDUE + int c = part_classdata[j][class_set][i]; + #else + int c = classifications[j][pcount]; + #endif + int b = r->residue_books[c][pass]; + if (b >= 0) { + float *target = residue_buffers[j]; + int offset = r->begin + pcount * r->part_size; + int n = r->part_size; + Codebook *book = f->codebooks + b; + if (!residue_decode(f, book, target, offset, n, rtype)) + goto done; + } + } + } + } + #ifndef STB_VORBIS_DIVIDES_IN_RESIDUE + ++class_set; + #endif + } + } + done: + CHECK(f); + #ifndef STB_VORBIS_DIVIDES_IN_RESIDUE + temp_free(f,part_classdata); + #else + temp_free(f,classifications); + #endif + temp_alloc_restore(f,temp_alloc_point); +} + + +#if 0 +// slow way for debugging +void inverse_mdct_slow(float *buffer, int n) +{ + int i,j; + int n2 = n >> 1; + float *x = (float *) malloc(sizeof(*x) * n2); + memcpy(x, buffer, sizeof(*x) * n2); + for (i=0; i < n; ++i) { + float acc = 0; + for (j=0; j < n2; ++j) + // formula from paper: + //acc += n/4.0f * x[j] * (float) cos(M_PI / 2 / n * (2 * i + 1 + n/2.0)*(2*j+1)); + // formula from wikipedia + //acc += 2.0f / n2 * x[j] * (float) cos(M_PI/n2 * (i + 0.5 + n2/2)*(j + 0.5)); + // these are equivalent, except the formula from the paper inverts the multiplier! + // however, what actually works is NO MULTIPLIER!?! + //acc += 64 * 2.0f / n2 * x[j] * (float) cos(M_PI/n2 * (i + 0.5 + n2/2)*(j + 0.5)); + acc += x[j] * (float) cos(M_PI / 2 / n * (2 * i + 1 + n/2.0)*(2*j+1)); + buffer[i] = acc; + } + free(x); +} +#elif 0 +// same as above, but just barely able to run in real time on modern machines +void inverse_mdct_slow(float *buffer, int n, vorb *f, int blocktype) +{ + float mcos[16384]; + int i,j; + int n2 = n >> 1, nmask = (n << 2) -1; + float *x = (float *) malloc(sizeof(*x) * n2); + memcpy(x, buffer, sizeof(*x) * n2); + for (i=0; i < 4*n; ++i) + mcos[i] = (float) cos(M_PI / 2 * i / n); + + for (i=0; i < n; ++i) { + float acc = 0; + for (j=0; j < n2; ++j) + acc += x[j] * mcos[(2 * i + 1 + n2)*(2*j+1) & nmask]; + buffer[i] = acc; + } + free(x); +} +#elif 0 +// transform to use a slow dct-iv; this is STILL basically trivial, +// but only requires half as many ops +void dct_iv_slow(float *buffer, int n) +{ + float mcos[16384]; + float x[2048]; + int i,j; + int n2 = n >> 1, nmask = (n << 3) - 1; + memcpy(x, buffer, sizeof(*x) * n); + for (i=0; i < 8*n; ++i) + mcos[i] = (float) cos(M_PI / 4 * i / n); + for (i=0; i < n; ++i) { + float acc = 0; + for (j=0; j < n; ++j) + acc += x[j] * mcos[((2 * i + 1)*(2*j+1)) & nmask]; + buffer[i] = acc; + } +} + +void inverse_mdct_slow(float *buffer, int n, vorb *f, int blocktype) +{ + int i, n4 = n >> 2, n2 = n >> 1, n3_4 = n - n4; + float temp[4096]; + + memcpy(temp, buffer, n2 * sizeof(float)); + dct_iv_slow(temp, n2); // returns -c'-d, a-b' + + for (i=0; i < n4 ; ++i) buffer[i] = temp[i+n4]; // a-b' + for ( ; i < n3_4; ++i) buffer[i] = -temp[n3_4 - i - 1]; // b-a', c+d' + for ( ; i < n ; ++i) buffer[i] = -temp[i - n3_4]; // c'+d +} +#endif + +#ifndef LIBVORBIS_MDCT +#define LIBVORBIS_MDCT 0 +#endif + +#if LIBVORBIS_MDCT +// directly call the vorbis MDCT using an interface documented +// by Jeff Roberts... useful for performance comparison +typedef struct +{ + int n; + int log2n; + + float *trig; + int *bitrev; + + float scale; +} mdct_lookup; + +extern void mdct_init(mdct_lookup *lookup, int n); +extern void mdct_clear(mdct_lookup *l); +extern void mdct_backward(mdct_lookup *init, float *in, float *out); + +mdct_lookup M1,M2; + +void inverse_mdct(float *buffer, int n, vorb *f, int blocktype) +{ + mdct_lookup *M; + if (M1.n == n) M = &M1; + else if (M2.n == n) M = &M2; + else if (M1.n == 0) { mdct_init(&M1, n); M = &M1; } + else { + if (M2.n) __asm int 3; + mdct_init(&M2, n); + M = &M2; + } + + mdct_backward(M, buffer, buffer); +} +#endif + + +// the following were split out into separate functions while optimizing; +// they could be pushed back up but eh. __forceinline showed no change; +// they're probably already being inlined. +static void imdct_step3_iter0_loop(int n, float *e, int i_off, int k_off, float *A) +{ + float *ee0 = e + i_off; + float *ee2 = ee0 + k_off; + int i; + + assert((n & 3) == 0); + for (i=(n>>2); i > 0; --i) { + float k00_20, k01_21; + k00_20 = ee0[ 0] - ee2[ 0]; + k01_21 = ee0[-1] - ee2[-1]; + ee0[ 0] += ee2[ 0];//ee0[ 0] = ee0[ 0] + ee2[ 0]; + ee0[-1] += ee2[-1];//ee0[-1] = ee0[-1] + ee2[-1]; + ee2[ 0] = k00_20 * A[0] - k01_21 * A[1]; + ee2[-1] = k01_21 * A[0] + k00_20 * A[1]; + A += 8; + + k00_20 = ee0[-2] - ee2[-2]; + k01_21 = ee0[-3] - ee2[-3]; + ee0[-2] += ee2[-2];//ee0[-2] = ee0[-2] + ee2[-2]; + ee0[-3] += ee2[-3];//ee0[-3] = ee0[-3] + ee2[-3]; + ee2[-2] = k00_20 * A[0] - k01_21 * A[1]; + ee2[-3] = k01_21 * A[0] + k00_20 * A[1]; + A += 8; + + k00_20 = ee0[-4] - ee2[-4]; + k01_21 = ee0[-5] - ee2[-5]; + ee0[-4] += ee2[-4];//ee0[-4] = ee0[-4] + ee2[-4]; + ee0[-5] += ee2[-5];//ee0[-5] = ee0[-5] + ee2[-5]; + ee2[-4] = k00_20 * A[0] - k01_21 * A[1]; + ee2[-5] = k01_21 * A[0] + k00_20 * A[1]; + A += 8; + + k00_20 = ee0[-6] - ee2[-6]; + k01_21 = ee0[-7] - ee2[-7]; + ee0[-6] += ee2[-6];//ee0[-6] = ee0[-6] + ee2[-6]; + ee0[-7] += ee2[-7];//ee0[-7] = ee0[-7] + ee2[-7]; + ee2[-6] = k00_20 * A[0] - k01_21 * A[1]; + ee2[-7] = k01_21 * A[0] + k00_20 * A[1]; + A += 8; + ee0 -= 8; + ee2 -= 8; + } +} + +static void imdct_step3_inner_r_loop(int lim, float *e, int d0, int k_off, float *A, int k1) +{ + int i; + float k00_20, k01_21; + + float *e0 = e + d0; + float *e2 = e0 + k_off; + + for (i=lim >> 2; i > 0; --i) { + k00_20 = e0[-0] - e2[-0]; + k01_21 = e0[-1] - e2[-1]; + e0[-0] += e2[-0];//e0[-0] = e0[-0] + e2[-0]; + e0[-1] += e2[-1];//e0[-1] = e0[-1] + e2[-1]; + e2[-0] = (k00_20)*A[0] - (k01_21) * A[1]; + e2[-1] = (k01_21)*A[0] + (k00_20) * A[1]; + + A += k1; + + k00_20 = e0[-2] - e2[-2]; + k01_21 = e0[-3] - e2[-3]; + e0[-2] += e2[-2];//e0[-2] = e0[-2] + e2[-2]; + e0[-3] += e2[-3];//e0[-3] = e0[-3] + e2[-3]; + e2[-2] = (k00_20)*A[0] - (k01_21) * A[1]; + e2[-3] = (k01_21)*A[0] + (k00_20) * A[1]; + + A += k1; + + k00_20 = e0[-4] - e2[-4]; + k01_21 = e0[-5] - e2[-5]; + e0[-4] += e2[-4];//e0[-4] = e0[-4] + e2[-4]; + e0[-5] += e2[-5];//e0[-5] = e0[-5] + e2[-5]; + e2[-4] = (k00_20)*A[0] - (k01_21) * A[1]; + e2[-5] = (k01_21)*A[0] + (k00_20) * A[1]; + + A += k1; + + k00_20 = e0[-6] - e2[-6]; + k01_21 = e0[-7] - e2[-7]; + e0[-6] += e2[-6];//e0[-6] = e0[-6] + e2[-6]; + e0[-7] += e2[-7];//e0[-7] = e0[-7] + e2[-7]; + e2[-6] = (k00_20)*A[0] - (k01_21) * A[1]; + e2[-7] = (k01_21)*A[0] + (k00_20) * A[1]; + + e0 -= 8; + e2 -= 8; + + A += k1; + } +} + +static void imdct_step3_inner_s_loop(int n, float *e, int i_off, int k_off, float *A, int a_off, int k0) +{ + int i; + float A0 = A[0]; + float A1 = A[0+1]; + float A2 = A[0+a_off]; + float A3 = A[0+a_off+1]; + float A4 = A[0+a_off*2+0]; + float A5 = A[0+a_off*2+1]; + float A6 = A[0+a_off*3+0]; + float A7 = A[0+a_off*3+1]; + + float k00,k11; + + float *ee0 = e +i_off; + float *ee2 = ee0+k_off; + + for (i=n; i > 0; --i) { + k00 = ee0[ 0] - ee2[ 0]; + k11 = ee0[-1] - ee2[-1]; + ee0[ 0] = ee0[ 0] + ee2[ 0]; + ee0[-1] = ee0[-1] + ee2[-1]; + ee2[ 0] = (k00) * A0 - (k11) * A1; + ee2[-1] = (k11) * A0 + (k00) * A1; + + k00 = ee0[-2] - ee2[-2]; + k11 = ee0[-3] - ee2[-3]; + ee0[-2] = ee0[-2] + ee2[-2]; + ee0[-3] = ee0[-3] + ee2[-3]; + ee2[-2] = (k00) * A2 - (k11) * A3; + ee2[-3] = (k11) * A2 + (k00) * A3; + + k00 = ee0[-4] - ee2[-4]; + k11 = ee0[-5] - ee2[-5]; + ee0[-4] = ee0[-4] + ee2[-4]; + ee0[-5] = ee0[-5] + ee2[-5]; + ee2[-4] = (k00) * A4 - (k11) * A5; + ee2[-5] = (k11) * A4 + (k00) * A5; + + k00 = ee0[-6] - ee2[-6]; + k11 = ee0[-7] - ee2[-7]; + ee0[-6] = ee0[-6] + ee2[-6]; + ee0[-7] = ee0[-7] + ee2[-7]; + ee2[-6] = (k00) * A6 - (k11) * A7; + ee2[-7] = (k11) * A6 + (k00) * A7; + + ee0 -= k0; + ee2 -= k0; + } +} + +static __forceinline void iter_54(float *z) +{ + float k00,k11,k22,k33; + float y0,y1,y2,y3; + + k00 = z[ 0] - z[-4]; + y0 = z[ 0] + z[-4]; + y2 = z[-2] + z[-6]; + k22 = z[-2] - z[-6]; + + z[-0] = y0 + y2; // z0 + z4 + z2 + z6 + z[-2] = y0 - y2; // z0 + z4 - z2 - z6 + + // done with y0,y2 + + k33 = z[-3] - z[-7]; + + z[-4] = k00 + k33; // z0 - z4 + z3 - z7 + z[-6] = k00 - k33; // z0 - z4 - z3 + z7 + + // done with k33 + + k11 = z[-1] - z[-5]; + y1 = z[-1] + z[-5]; + y3 = z[-3] + z[-7]; + + z[-1] = y1 + y3; // z1 + z5 + z3 + z7 + z[-3] = y1 - y3; // z1 + z5 - z3 - z7 + z[-5] = k11 - k22; // z1 - z5 + z2 - z6 + z[-7] = k11 + k22; // z1 - z5 - z2 + z6 +} + +static void imdct_step3_inner_s_loop_ld654(int n, float *e, int i_off, float *A, int base_n) +{ + int a_off = base_n >> 3; + float A2 = A[0+a_off]; + float *z = e + i_off; + float *base = z - 16 * n; + + while (z > base) { + float k00,k11; + + k00 = z[-0] - z[-8]; + k11 = z[-1] - z[-9]; + z[-0] = z[-0] + z[-8]; + z[-1] = z[-1] + z[-9]; + z[-8] = k00; + z[-9] = k11 ; + + k00 = z[ -2] - z[-10]; + k11 = z[ -3] - z[-11]; + z[ -2] = z[ -2] + z[-10]; + z[ -3] = z[ -3] + z[-11]; + z[-10] = (k00+k11) * A2; + z[-11] = (k11-k00) * A2; + + k00 = z[-12] - z[ -4]; // reverse to avoid a unary negation + k11 = z[ -5] - z[-13]; + z[ -4] = z[ -4] + z[-12]; + z[ -5] = z[ -5] + z[-13]; + z[-12] = k11; + z[-13] = k00; + + k00 = z[-14] - z[ -6]; // reverse to avoid a unary negation + k11 = z[ -7] - z[-15]; + z[ -6] = z[ -6] + z[-14]; + z[ -7] = z[ -7] + z[-15]; + z[-14] = (k00+k11) * A2; + z[-15] = (k00-k11) * A2; + + iter_54(z); + iter_54(z-8); + z -= 16; + } +} + +static void inverse_mdct(float *buffer, int n, vorb *f, int blocktype) +{ + int n2 = n >> 1, n4 = n >> 2, n8 = n >> 3, l; + int ld; + // @OPTIMIZE: reduce register pressure by using fewer variables? + int save_point = temp_alloc_save(f); + float *buf2 = (float *) temp_alloc(f, n2 * sizeof(*buf2)); + float *u=NULL,*v=NULL; + // twiddle factors + float *A = f->A[blocktype]; + + // IMDCT algorithm from "The use of multirate filter banks for coding of high quality digital audio" + // See notes about bugs in that paper in less-optimal implementation 'inverse_mdct_old' after this function. + + // kernel from paper + + + // merged: + // copy and reflect spectral data + // step 0 + + // note that it turns out that the items added together during + // this step are, in fact, being added to themselves (as reflected + // by step 0). inexplicable inefficiency! this became obvious + // once I combined the passes. + + // so there's a missing 'times 2' here (for adding X to itself). + // this propogates through linearly to the end, where the numbers + // are 1/2 too small, and need to be compensated for. + + { + float *d,*e, *AA, *e_stop; + d = &buf2[n2-2]; + AA = A; + e = &buffer[0]; + e_stop = &buffer[n2]; + while (e != e_stop) { + d[1] = (e[0] * AA[0] - e[2]*AA[1]); + d[0] = (e[0] * AA[1] + e[2]*AA[0]); + d -= 2; + AA += 2; + e += 4; + } + + e = &buffer[n2-3]; + while (d >= buf2) { + d[1] = (-e[2] * AA[0] - -e[0]*AA[1]); + d[0] = (-e[2] * AA[1] + -e[0]*AA[0]); + d -= 2; + AA += 2; + e -= 4; + } + } + + // now we use symbolic names for these, so that we can + // possibly swap their meaning as we change which operations + // are in place + + u = buffer; + v = buf2; + + // step 2 (paper output is w, now u) + // this could be in place, but the data ends up in the wrong + // place... _somebody_'s got to swap it, so this is nominated + { + float *AA = &A[n2-8]; + float *d0,*d1, *e0, *e1; + + e0 = &v[n4]; + e1 = &v[0]; + + d0 = &u[n4]; + d1 = &u[0]; + + while (AA >= A) { + float v40_20, v41_21; + + v41_21 = e0[1] - e1[1]; + v40_20 = e0[0] - e1[0]; + d0[1] = e0[1] + e1[1]; + d0[0] = e0[0] + e1[0]; + d1[1] = v41_21*AA[4] - v40_20*AA[5]; + d1[0] = v40_20*AA[4] + v41_21*AA[5]; + + v41_21 = e0[3] - e1[3]; + v40_20 = e0[2] - e1[2]; + d0[3] = e0[3] + e1[3]; + d0[2] = e0[2] + e1[2]; + d1[3] = v41_21*AA[0] - v40_20*AA[1]; + d1[2] = v40_20*AA[0] + v41_21*AA[1]; + + AA -= 8; + + d0 += 4; + d1 += 4; + e0 += 4; + e1 += 4; + } + } + + // step 3 + ld = ilog(n) - 1; // ilog is off-by-one from normal definitions + + // optimized step 3: + + // the original step3 loop can be nested r inside s or s inside r; + // it's written originally as s inside r, but this is dumb when r + // iterates many times, and s few. So I have two copies of it and + // switch between them halfway. + + // this is iteration 0 of step 3 + imdct_step3_iter0_loop(n >> 4, u, n2-1-n4*0, -(n >> 3), A); + imdct_step3_iter0_loop(n >> 4, u, n2-1-n4*1, -(n >> 3), A); + + // this is iteration 1 of step 3 + imdct_step3_inner_r_loop(n >> 5, u, n2-1 - n8*0, -(n >> 4), A, 16); + imdct_step3_inner_r_loop(n >> 5, u, n2-1 - n8*1, -(n >> 4), A, 16); + imdct_step3_inner_r_loop(n >> 5, u, n2-1 - n8*2, -(n >> 4), A, 16); + imdct_step3_inner_r_loop(n >> 5, u, n2-1 - n8*3, -(n >> 4), A, 16); + + l=2; + for (; l < (ld-3)>>1; ++l) { + int k0 = n >> (l+2), k0_2 = k0>>1; + int lim = 1 << (l+1); + int i; + for (i=0; i < lim; ++i) + imdct_step3_inner_r_loop(n >> (l+4), u, n2-1 - k0*i, -k0_2, A, 1 << (l+3)); + } + + for (; l < ld-6; ++l) { + int k0 = n >> (l+2), k1 = 1 << (l+3), k0_2 = k0>>1; + int rlim = n >> (l+6), r; + int lim = 1 << (l+1); + int i_off; + float *A0 = A; + i_off = n2-1; + for (r=rlim; r > 0; --r) { + imdct_step3_inner_s_loop(lim, u, i_off, -k0_2, A0, k1, k0); + A0 += k1*4; + i_off -= 8; + } + } + + // iterations with count: + // ld-6,-5,-4 all interleaved together + // the big win comes from getting rid of needless flops + // due to the constants on pass 5 & 4 being all 1 and 0; + // combining them to be simultaneous to improve cache made little difference + imdct_step3_inner_s_loop_ld654(n >> 5, u, n2-1, A, n); + + // output is u + + // step 4, 5, and 6 + // cannot be in-place because of step 5 + { + uint16 *bitrev = f->bit_reverse[blocktype]; + // weirdly, I'd have thought reading sequentially and writing + // erratically would have been better than vice-versa, but in + // fact that's not what my testing showed. (That is, with + // j = bitreverse(i), do you read i and write j, or read j and write i.) + + float *d0 = &v[n4-4]; + float *d1 = &v[n2-4]; + while (d0 >= v) { + int k4; + + k4 = bitrev[0]; + d1[3] = u[k4+0]; + d1[2] = u[k4+1]; + d0[3] = u[k4+2]; + d0[2] = u[k4+3]; + + k4 = bitrev[1]; + d1[1] = u[k4+0]; + d1[0] = u[k4+1]; + d0[1] = u[k4+2]; + d0[0] = u[k4+3]; + + d0 -= 4; + d1 -= 4; + bitrev += 2; + } + } + // (paper output is u, now v) + + + // data must be in buf2 + assert(v == buf2); + + // step 7 (paper output is v, now v) + // this is now in place + { + float *C = f->C[blocktype]; + float *d, *e; + + d = v; + e = v + n2 - 4; + + while (d < e) { + float a02,a11,b0,b1,b2,b3; + + a02 = d[0] - e[2]; + a11 = d[1] + e[3]; + + b0 = C[1]*a02 + C[0]*a11; + b1 = C[1]*a11 - C[0]*a02; + + b2 = d[0] + e[ 2]; + b3 = d[1] - e[ 3]; + + d[0] = b2 + b0; + d[1] = b3 + b1; + e[2] = b2 - b0; + e[3] = b1 - b3; + + a02 = d[2] - e[0]; + a11 = d[3] + e[1]; + + b0 = C[3]*a02 + C[2]*a11; + b1 = C[3]*a11 - C[2]*a02; + + b2 = d[2] + e[ 0]; + b3 = d[3] - e[ 1]; + + d[2] = b2 + b0; + d[3] = b3 + b1; + e[0] = b2 - b0; + e[1] = b1 - b3; + + C += 4; + d += 4; + e -= 4; + } + } + + // data must be in buf2 + + + // step 8+decode (paper output is X, now buffer) + // this generates pairs of data a la 8 and pushes them directly through + // the decode kernel (pushing rather than pulling) to avoid having + // to make another pass later + + // this cannot POSSIBLY be in place, so we refer to the buffers directly + + { + float *d0,*d1,*d2,*d3; + + float *B = f->B[blocktype] + n2 - 8; + float *e = buf2 + n2 - 8; + d0 = &buffer[0]; + d1 = &buffer[n2-4]; + d2 = &buffer[n2]; + d3 = &buffer[n-4]; + while (e >= v) { + float p0,p1,p2,p3; + + p3 = e[6]*B[7] - e[7]*B[6]; + p2 = -e[6]*B[6] - e[7]*B[7]; + + d0[0] = p3; + d1[3] = - p3; + d2[0] = p2; + d3[3] = p2; + + p1 = e[4]*B[5] - e[5]*B[4]; + p0 = -e[4]*B[4] - e[5]*B[5]; + + d0[1] = p1; + d1[2] = - p1; + d2[1] = p0; + d3[2] = p0; + + p3 = e[2]*B[3] - e[3]*B[2]; + p2 = -e[2]*B[2] - e[3]*B[3]; + + d0[2] = p3; + d1[1] = - p3; + d2[2] = p2; + d3[1] = p2; + + p1 = e[0]*B[1] - e[1]*B[0]; + p0 = -e[0]*B[0] - e[1]*B[1]; + + d0[3] = p1; + d1[0] = - p1; + d2[3] = p0; + d3[0] = p0; + + B -= 8; + e -= 8; + d0 += 4; + d2 += 4; + d1 -= 4; + d3 -= 4; + } + } + + temp_free(f,buf2); + temp_alloc_restore(f,save_point); +} + +#if 0 +// this is the original version of the above code, if you want to optimize it from scratch +void inverse_mdct_naive(float *buffer, int n) +{ + float s; + float A[1 << 12], B[1 << 12], C[1 << 11]; + int i,k,k2,k4, n2 = n >> 1, n4 = n >> 2, n8 = n >> 3, l; + int n3_4 = n - n4, ld; + // how can they claim this only uses N words?! + // oh, because they're only used sparsely, whoops + float u[1 << 13], X[1 << 13], v[1 << 13], w[1 << 13]; + // set up twiddle factors + + for (k=k2=0; k < n4; ++k,k2+=2) { + A[k2 ] = (float) cos(4*k*M_PI/n); + A[k2+1] = (float) -sin(4*k*M_PI/n); + B[k2 ] = (float) cos((k2+1)*M_PI/n/2); + B[k2+1] = (float) sin((k2+1)*M_PI/n/2); + } + for (k=k2=0; k < n8; ++k,k2+=2) { + C[k2 ] = (float) cos(2*(k2+1)*M_PI/n); + C[k2+1] = (float) -sin(2*(k2+1)*M_PI/n); + } + + // IMDCT algorithm from "The use of multirate filter banks for coding of high quality digital audio" + // Note there are bugs in that pseudocode, presumably due to them attempting + // to rename the arrays nicely rather than representing the way their actual + // implementation bounces buffers back and forth. As a result, even in the + // "some formulars corrected" version, a direct implementation fails. These + // are noted below as "paper bug". + + // copy and reflect spectral data + for (k=0; k < n2; ++k) u[k] = buffer[k]; + for ( ; k < n ; ++k) u[k] = -buffer[n - k - 1]; + // kernel from paper + // step 1 + for (k=k2=k4=0; k < n4; k+=1, k2+=2, k4+=4) { + v[n-k4-1] = (u[k4] - u[n-k4-1]) * A[k2] - (u[k4+2] - u[n-k4-3])*A[k2+1]; + v[n-k4-3] = (u[k4] - u[n-k4-1]) * A[k2+1] + (u[k4+2] - u[n-k4-3])*A[k2]; + } + // step 2 + for (k=k4=0; k < n8; k+=1, k4+=4) { + w[n2+3+k4] = v[n2+3+k4] + v[k4+3]; + w[n2+1+k4] = v[n2+1+k4] + v[k4+1]; + w[k4+3] = (v[n2+3+k4] - v[k4+3])*A[n2-4-k4] - (v[n2+1+k4]-v[k4+1])*A[n2-3-k4]; + w[k4+1] = (v[n2+1+k4] - v[k4+1])*A[n2-4-k4] + (v[n2+3+k4]-v[k4+3])*A[n2-3-k4]; + } + // step 3 + ld = ilog(n) - 1; // ilog is off-by-one from normal definitions + for (l=0; l < ld-3; ++l) { + int k0 = n >> (l+2), k1 = 1 << (l+3); + int rlim = n >> (l+4), r4, r; + int s2lim = 1 << (l+2), s2; + for (r=r4=0; r < rlim; r4+=4,++r) { + for (s2=0; s2 < s2lim; s2+=2) { + u[n-1-k0*s2-r4] = w[n-1-k0*s2-r4] + w[n-1-k0*(s2+1)-r4]; + u[n-3-k0*s2-r4] = w[n-3-k0*s2-r4] + w[n-3-k0*(s2+1)-r4]; + u[n-1-k0*(s2+1)-r4] = (w[n-1-k0*s2-r4] - w[n-1-k0*(s2+1)-r4]) * A[r*k1] + - (w[n-3-k0*s2-r4] - w[n-3-k0*(s2+1)-r4]) * A[r*k1+1]; + u[n-3-k0*(s2+1)-r4] = (w[n-3-k0*s2-r4] - w[n-3-k0*(s2+1)-r4]) * A[r*k1] + + (w[n-1-k0*s2-r4] - w[n-1-k0*(s2+1)-r4]) * A[r*k1+1]; + } + } + if (l+1 < ld-3) { + // paper bug: ping-ponging of u&w here is omitted + memcpy(w, u, sizeof(u)); + } + } + + // step 4 + for (i=0; i < n8; ++i) { + int j = bit_reverse(i) >> (32-ld+3); + assert(j < n8); + if (i == j) { + // paper bug: original code probably swapped in place; if copying, + // need to directly copy in this case + int i8 = i << 3; + v[i8+1] = u[i8+1]; + v[i8+3] = u[i8+3]; + v[i8+5] = u[i8+5]; + v[i8+7] = u[i8+7]; + } else if (i < j) { + int i8 = i << 3, j8 = j << 3; + v[j8+1] = u[i8+1], v[i8+1] = u[j8 + 1]; + v[j8+3] = u[i8+3], v[i8+3] = u[j8 + 3]; + v[j8+5] = u[i8+5], v[i8+5] = u[j8 + 5]; + v[j8+7] = u[i8+7], v[i8+7] = u[j8 + 7]; + } + } + // step 5 + for (k=0; k < n2; ++k) { + w[k] = v[k*2+1]; + } + // step 6 + for (k=k2=k4=0; k < n8; ++k, k2 += 2, k4 += 4) { + u[n-1-k2] = w[k4]; + u[n-2-k2] = w[k4+1]; + u[n3_4 - 1 - k2] = w[k4+2]; + u[n3_4 - 2 - k2] = w[k4+3]; + } + // step 7 + for (k=k2=0; k < n8; ++k, k2 += 2) { + v[n2 + k2 ] = ( u[n2 + k2] + u[n-2-k2] + C[k2+1]*(u[n2+k2]-u[n-2-k2]) + C[k2]*(u[n2+k2+1]+u[n-2-k2+1]))/2; + v[n-2 - k2] = ( u[n2 + k2] + u[n-2-k2] - C[k2+1]*(u[n2+k2]-u[n-2-k2]) - C[k2]*(u[n2+k2+1]+u[n-2-k2+1]))/2; + v[n2+1+ k2] = ( u[n2+1+k2] - u[n-1-k2] + C[k2+1]*(u[n2+1+k2]+u[n-1-k2]) - C[k2]*(u[n2+k2]-u[n-2-k2]))/2; + v[n-1 - k2] = (-u[n2+1+k2] + u[n-1-k2] + C[k2+1]*(u[n2+1+k2]+u[n-1-k2]) - C[k2]*(u[n2+k2]-u[n-2-k2]))/2; + } + // step 8 + for (k=k2=0; k < n4; ++k,k2 += 2) { + X[k] = v[k2+n2]*B[k2 ] + v[k2+1+n2]*B[k2+1]; + X[n2-1-k] = v[k2+n2]*B[k2+1] - v[k2+1+n2]*B[k2 ]; + } + + // decode kernel to output + // determined the following value experimentally + // (by first figuring out what made inverse_mdct_slow work); then matching that here + // (probably vorbis encoder premultiplies by n or n/2, to save it on the decoder?) + s = 0.5; // theoretically would be n4 + + // [[[ note! the s value of 0.5 is compensated for by the B[] in the current code, + // so it needs to use the "old" B values to behave correctly, or else + // set s to 1.0 ]]] + for (i=0; i < n4 ; ++i) buffer[i] = s * X[i+n4]; + for ( ; i < n3_4; ++i) buffer[i] = -s * X[n3_4 - i - 1]; + for ( ; i < n ; ++i) buffer[i] = -s * X[i - n3_4]; +} +#endif + +static float *get_window(vorb *f, int len) +{ + len <<= 1; + if (len == f->blocksize_0) return f->window[0]; + if (len == f->blocksize_1) return f->window[1]; + assert(0); + return NULL; +} + +#ifndef STB_VORBIS_NO_DEFER_FLOOR +typedef int16 YTYPE; +#else +typedef int YTYPE; +#endif +static int do_floor(vorb *f, Mapping *map, int i, int n, float *target, YTYPE *finalY, uint8 *step2_flag) +{ + int n2 = n >> 1; + int s = map->chan[i].mux, floor; + floor = map->submap_floor[s]; + if (f->floor_types[floor] == 0) { + return error(f, VORBIS_invalid_stream); + } else { + Floor1 *g = &f->floor_config[floor].floor1; + int j,q; + int lx = 0, ly = finalY[0] * g->floor1_multiplier; + for (q=1; q < g->values; ++q) { + j = g->sorted_order[q]; + #ifndef STB_VORBIS_NO_DEFER_FLOOR + if (finalY[j] >= 0) + #else + if (step2_flag[j]) + #endif + { + int hy = finalY[j] * g->floor1_multiplier; + int hx = g->Xlist[j]; + if (lx != hx) + draw_line(target, lx,ly, hx,hy, n2); + CHECK(f); + lx = hx, ly = hy; + } + } + if (lx < n2) { + // optimization of: draw_line(target, lx,ly, n,ly, n2); + for (j=lx; j < n2; ++j) + LINE_OP(target[j], inverse_db_table[ly]); + CHECK(f); + } + } + return TRUE; +} + +// The meaning of "left" and "right" +// +// For a given frame: +// we compute samples from 0..n +// window_center is n/2 +// we'll window and mix the samples from left_start to left_end with data from the previous frame +// all of the samples from left_end to right_start can be output without mixing; however, +// this interval is 0-length except when transitioning between short and long frames +// all of the samples from right_start to right_end need to be mixed with the next frame, +// which we don't have, so those get saved in a buffer +// frame N's right_end-right_start, the number of samples to mix with the next frame, +// has to be the same as frame N+1's left_end-left_start (which they are by +// construction) + +static int vorbis_decode_initial(vorb *f, int *p_left_start, int *p_left_end, int *p_right_start, int *p_right_end, int *mode) +{ + Mode *m; + int i, n, prev, next, window_center; + f->channel_buffer_start = f->channel_buffer_end = 0; + + retry: + if (f->eof) return FALSE; + if (!maybe_start_packet(f)) + return FALSE; + // check packet type + if (get_bits(f,1) != 0) { + if (IS_PUSH_MODE(f)) + return error(f,VORBIS_bad_packet_type); + while (EOP != get8_packet(f)); + goto retry; + } + + if (f->alloc.alloc_buffer) + assert(f->alloc.alloc_buffer_length_in_bytes == f->temp_offset); + + i = get_bits(f, ilog(f->mode_count-1)); + if (i == EOP) return FALSE; + if (i >= f->mode_count) return FALSE; + *mode = i; + m = f->mode_config + i; + if (m->blockflag) { + n = f->blocksize_1; + prev = get_bits(f,1); + next = get_bits(f,1); + } else { + prev = next = 0; + n = f->blocksize_0; + } + +// WINDOWING + + window_center = n >> 1; + if (m->blockflag && !prev) { + *p_left_start = (n - f->blocksize_0) >> 2; + *p_left_end = (n + f->blocksize_0) >> 2; + } else { + *p_left_start = 0; + *p_left_end = window_center; + } + if (m->blockflag && !next) { + *p_right_start = (n*3 - f->blocksize_0) >> 2; + *p_right_end = (n*3 + f->blocksize_0) >> 2; + } else { + *p_right_start = window_center; + *p_right_end = n; + } + + return TRUE; +} + +static int vorbis_decode_packet_rest(vorb *f, int *len, Mode *m, int left_start, int left_end, int right_start, int right_end, int *p_left) +{ + Mapping *map; + int i,j,k,n,n2; + int zero_channel[256]; + int really_zero_channel[256]; + +// WINDOWING + + n = f->blocksize[m->blockflag]; + map = &f->mapping[m->mapping]; + +// FLOORS + n2 = n >> 1; + + CHECK(f); + + for (i=0; i < f->channels; ++i) { + int s = map->chan[i].mux, floor; + zero_channel[i] = FALSE; + floor = map->submap_floor[s]; + if (f->floor_types[floor] == 0) { + return error(f, VORBIS_invalid_stream); + } else { + Floor1 *g = &f->floor_config[floor].floor1; + if (get_bits(f, 1)) { + short *finalY; + uint8 step2_flag[256]; + static int range_list[4] = { 256, 128, 86, 64 }; + int range = range_list[g->floor1_multiplier-1]; + int offset = 2; + finalY = f->finalY[i]; + finalY[0] = get_bits(f, ilog(range)-1); + finalY[1] = get_bits(f, ilog(range)-1); + for (j=0; j < g->partitions; ++j) { + int pclass = g->partition_class_list[j]; + int cdim = g->class_dimensions[pclass]; + int cbits = g->class_subclasses[pclass]; + int csub = (1 << cbits)-1; + int cval = 0; + if (cbits) { + Codebook *c = f->codebooks + g->class_masterbooks[pclass]; + DECODE(cval,f,c); + } + for (k=0; k < cdim; ++k) { + int book = g->subclass_books[pclass][cval & csub]; + cval = cval >> cbits; + if (book >= 0) { + int temp; + Codebook *c = f->codebooks + book; + DECODE(temp,f,c); + finalY[offset++] = temp; + } else + finalY[offset++] = 0; + } + } + if (f->valid_bits == INVALID_BITS) goto error; // behavior according to spec + step2_flag[0] = step2_flag[1] = 1; + for (j=2; j < g->values; ++j) { + int low, high, pred, highroom, lowroom, room, val; + low = g->neighbors[j][0]; + high = g->neighbors[j][1]; + //neighbors(g->Xlist, j, &low, &high); + pred = predict_point(g->Xlist[j], g->Xlist[low], g->Xlist[high], finalY[low], finalY[high]); + val = finalY[j]; + highroom = range - pred; + lowroom = pred; + if (highroom < lowroom) + room = highroom * 2; + else + room = lowroom * 2; + if (val) { + step2_flag[low] = step2_flag[high] = 1; + step2_flag[j] = 1; + if (val >= room) + if (highroom > lowroom) + finalY[j] = val - lowroom + pred; + else + finalY[j] = pred - val + highroom - 1; + else + if (val & 1) + finalY[j] = pred - ((val+1)>>1); + else + finalY[j] = pred + (val>>1); + } else { + step2_flag[j] = 0; + finalY[j] = pred; + } + } + +#ifdef STB_VORBIS_NO_DEFER_FLOOR + do_floor(f, map, i, n, f->floor_buffers[i], finalY, step2_flag); +#else + // defer final floor computation until _after_ residue + for (j=0; j < g->values; ++j) { + if (!step2_flag[j]) + finalY[j] = -1; + } +#endif + } else { + error: + zero_channel[i] = TRUE; + } + // So we just defer everything else to later + + // at this point we've decoded the floor into buffer + } + } + CHECK(f); + // at this point we've decoded all floors + + if (f->alloc.alloc_buffer) + assert(f->alloc.alloc_buffer_length_in_bytes == f->temp_offset); + + // re-enable coupled channels if necessary + memcpy(really_zero_channel, zero_channel, sizeof(really_zero_channel[0]) * f->channels); + for (i=0; i < map->coupling_steps; ++i) + if (!zero_channel[map->chan[i].magnitude] || !zero_channel[map->chan[i].angle]) { + zero_channel[map->chan[i].magnitude] = zero_channel[map->chan[i].angle] = FALSE; + } + + CHECK(f); +// RESIDUE DECODE + for (i=0; i < map->submaps; ++i) { + float *residue_buffers[STB_VORBIS_MAX_CHANNELS]; + int r; + uint8 do_not_decode[256]; + int ch = 0; + for (j=0; j < f->channels; ++j) { + if (map->chan[j].mux == i) { + if (zero_channel[j]) { + do_not_decode[ch] = TRUE; + residue_buffers[ch] = NULL; + } else { + do_not_decode[ch] = FALSE; + residue_buffers[ch] = f->channel_buffers[j]; + } + ++ch; + } + } + r = map->submap_residue[i]; + decode_residue(f, residue_buffers, ch, n2, r, do_not_decode); + } + + if (f->alloc.alloc_buffer) + assert(f->alloc.alloc_buffer_length_in_bytes == f->temp_offset); + CHECK(f); + +// INVERSE COUPLING + for (i = map->coupling_steps-1; i >= 0; --i) { + int n2 = n >> 1; + float *m = f->channel_buffers[map->chan[i].magnitude]; + float *a = f->channel_buffers[map->chan[i].angle ]; + for (j=0; j < n2; ++j) { + float a2,m2; + if (m[j] > 0) + if (a[j] > 0) + m2 = m[j], a2 = m[j] - a[j]; + else + a2 = m[j], m2 = m[j] + a[j]; + else + if (a[j] > 0) + m2 = m[j], a2 = m[j] + a[j]; + else + a2 = m[j], m2 = m[j] - a[j]; + m[j] = m2; + a[j] = a2; + } + } + CHECK(f); + + // finish decoding the floors +#ifndef STB_VORBIS_NO_DEFER_FLOOR + for (i=0; i < f->channels; ++i) { + if (really_zero_channel[i]) { + memset(f->channel_buffers[i], 0, sizeof(*f->channel_buffers[i]) * n2); + } else { + do_floor(f, map, i, n, f->channel_buffers[i], f->finalY[i], NULL); + } + } +#else + for (i=0; i < f->channels; ++i) { + if (really_zero_channel[i]) { + memset(f->channel_buffers[i], 0, sizeof(*f->channel_buffers[i]) * n2); + } else { + for (j=0; j < n2; ++j) + f->channel_buffers[i][j] *= f->floor_buffers[i][j]; + } + } +#endif + +// INVERSE MDCT + CHECK(f); + for (i=0; i < f->channels; ++i) + inverse_mdct(f->channel_buffers[i], n, f, m->blockflag); + CHECK(f); + + // this shouldn't be necessary, unless we exited on an error + // and want to flush to get to the next packet + flush_packet(f); + + if (f->first_decode) { + // assume we start so first non-discarded sample is sample 0 + // this isn't to spec, but spec would require us to read ahead + // and decode the size of all current frames--could be done, + // but presumably it's not a commonly used feature + f->current_loc = -n2; // start of first frame is positioned for discard + // we might have to discard samples "from" the next frame too, + // if we're lapping a large block then a small at the start? + f->discard_samples_deferred = n - right_end; + f->current_loc_valid = TRUE; + f->first_decode = FALSE; + } else if (f->discard_samples_deferred) { + if (f->discard_samples_deferred >= right_start - left_start) { + f->discard_samples_deferred -= (right_start - left_start); + left_start = right_start; + *p_left = left_start; + } else { + left_start += f->discard_samples_deferred; + *p_left = left_start; + f->discard_samples_deferred = 0; + } + } else if (f->previous_length == 0 && f->current_loc_valid) { + // we're recovering from a seek... that means we're going to discard + // the samples from this packet even though we know our position from + // the last page header, so we need to update the position based on + // the discarded samples here + // but wait, the code below is going to add this in itself even + // on a discard, so we don't need to do it here... + } + + // check if we have ogg information about the sample # for this packet + if (f->last_seg_which == f->end_seg_with_known_loc) { + // if we have a valid current loc, and this is final: + if (f->current_loc_valid && (f->page_flag & PAGEFLAG_last_page)) { + uint32 current_end = f->known_loc_for_packet - (n-right_end); + // then let's infer the size of the (probably) short final frame + if (current_end < f->current_loc + (right_end-left_start)) { + if (current_end < f->current_loc) { + // negative truncation, that's impossible! + *len = 0; + } else { + *len = current_end - f->current_loc; + } + *len += left_start; + if (*len > right_end) *len = right_end; // this should never happen + f->current_loc += *len; + return TRUE; + } + } + // otherwise, just set our sample loc + // guess that the ogg granule pos refers to the _middle_ of the + // last frame? + // set f->current_loc to the position of left_start + f->current_loc = f->known_loc_for_packet - (n2-left_start); + f->current_loc_valid = TRUE; + } + if (f->current_loc_valid) + f->current_loc += (right_start - left_start); + + if (f->alloc.alloc_buffer) + assert(f->alloc.alloc_buffer_length_in_bytes == f->temp_offset); + *len = right_end; // ignore samples after the window goes to 0 + CHECK(f); + + return TRUE; +} + +static int vorbis_decode_packet(vorb *f, int *len, int *p_left, int *p_right) +{ + int mode, left_end, right_end; + if (!vorbis_decode_initial(f, p_left, &left_end, p_right, &right_end, &mode)) return 0; + return vorbis_decode_packet_rest(f, len, f->mode_config + mode, *p_left, left_end, *p_right, right_end, p_left); +} + +static int vorbis_finish_frame(stb_vorbis *f, int len, int left, int right) +{ + int prev,i,j; + // we use right&left (the start of the right- and left-window sin()-regions) + // to determine how much to return, rather than inferring from the rules + // (same result, clearer code); 'left' indicates where our sin() window + // starts, therefore where the previous window's right edge starts, and + // therefore where to start mixing from the previous buffer. 'right' + // indicates where our sin() ending-window starts, therefore that's where + // we start saving, and where our returned-data ends. + + // mixin from previous window + if (f->previous_length) { + int i,j, n = f->previous_length; + float *w = get_window(f, n); + for (i=0; i < f->channels; ++i) { + for (j=0; j < n; ++j) + f->channel_buffers[i][left+j] = + f->channel_buffers[i][left+j]*w[ j] + + f->previous_window[i][ j]*w[n-1-j]; + } + } + + prev = f->previous_length; + + // last half of this data becomes previous window + f->previous_length = len - right; + + // @OPTIMIZE: could avoid this copy by double-buffering the + // output (flipping previous_window with channel_buffers), but + // then previous_window would have to be 2x as large, and + // channel_buffers couldn't be temp mem (although they're NOT + // currently temp mem, they could be (unless we want to level + // performance by spreading out the computation)) + for (i=0; i < f->channels; ++i) + for (j=0; right+j < len; ++j) + f->previous_window[i][j] = f->channel_buffers[i][right+j]; + + if (!prev) + // there was no previous packet, so this data isn't valid... + // this isn't entirely true, only the would-have-overlapped data + // isn't valid, but this seems to be what the spec requires + return 0; + + // truncate a short frame + if (len < right) right = len; + + f->samples_output += right-left; + + return right - left; +} + +static void vorbis_pump_first_frame(stb_vorbis *f) +{ + int len, right, left; + if (vorbis_decode_packet(f, &len, &left, &right)) + vorbis_finish_frame(f, len, left, right); +} + +#ifndef STB_VORBIS_NO_PUSHDATA_API +static int is_whole_packet_present(stb_vorbis *f, int end_page) +{ + // make sure that we have the packet available before continuing... + // this requires a full ogg parse, but we know we can fetch from f->stream + + // instead of coding this out explicitly, we could save the current read state, + // read the next packet with get8() until end-of-packet, check f->eof, then + // reset the state? but that would be slower, esp. since we'd have over 256 bytes + // of state to restore (primarily the page segment table) + + int s = f->next_seg, first = TRUE; + uint8 *p = f->stream; + + if (s != -1) { // if we're not starting the packet with a 'continue on next page' flag + for (; s < f->segment_count; ++s) { + p += f->segments[s]; + if (f->segments[s] < 255) // stop at first short segment + break; + } + // either this continues, or it ends it... + if (end_page) + if (s < f->segment_count-1) return error(f, VORBIS_invalid_stream); + if (s == f->segment_count) + s = -1; // set 'crosses page' flag + if (p > f->stream_end) return error(f, VORBIS_need_more_data); + first = FALSE; + } + for (; s == -1;) { + uint8 *q; + int n; + + // check that we have the page header ready + if (p + 26 >= f->stream_end) return error(f, VORBIS_need_more_data); + // validate the page + if (memcmp(p, ogg_page_header, 4)) return error(f, VORBIS_invalid_stream); + if (p[4] != 0) return error(f, VORBIS_invalid_stream); + if (first) { // the first segment must NOT have 'continued_packet', later ones MUST + if (f->previous_length) + if ((p[5] & PAGEFLAG_continued_packet)) return error(f, VORBIS_invalid_stream); + // if no previous length, we're resynching, so we can come in on a continued-packet, + // which we'll just drop + } else { + if (!(p[5] & PAGEFLAG_continued_packet)) return error(f, VORBIS_invalid_stream); + } + n = p[26]; // segment counts + q = p+27; // q points to segment table + p = q + n; // advance past header + // make sure we've read the segment table + if (p > f->stream_end) return error(f, VORBIS_need_more_data); + for (s=0; s < n; ++s) { + p += q[s]; + if (q[s] < 255) + break; + } + if (end_page) + if (s < n-1) return error(f, VORBIS_invalid_stream); + if (s == n) + s = -1; // set 'crosses page' flag + if (p > f->stream_end) return error(f, VORBIS_need_more_data); + first = FALSE; + } + return TRUE; +} +#endif // !STB_VORBIS_NO_PUSHDATA_API + +static int start_decoder(vorb *f) +{ + uint8 header[6], x,y; + int len,i,j,k, max_submaps = 0; + int longest_floorlist=0; + + // first page, first packet + + if (!start_page(f)) return FALSE; + // validate page flag + if (!(f->page_flag & PAGEFLAG_first_page)) return error(f, VORBIS_invalid_first_page); + if (f->page_flag & PAGEFLAG_last_page) return error(f, VORBIS_invalid_first_page); + if (f->page_flag & PAGEFLAG_continued_packet) return error(f, VORBIS_invalid_first_page); + // check for expected packet length + if (f->segment_count != 1) return error(f, VORBIS_invalid_first_page); + if (f->segments[0] != 30) return error(f, VORBIS_invalid_first_page); + // read packet + // check packet header + if (get8(f) != VORBIS_packet_id) return error(f, VORBIS_invalid_first_page); + if (!getn(f, header, 6)) return error(f, VORBIS_unexpected_eof); + if (!vorbis_validate(header)) return error(f, VORBIS_invalid_first_page); + // vorbis_version + if (get32(f) != 0) return error(f, VORBIS_invalid_first_page); + f->channels = get8(f); if (!f->channels) return error(f, VORBIS_invalid_first_page); + if (f->channels > STB_VORBIS_MAX_CHANNELS) return error(f, VORBIS_too_many_channels); + f->sample_rate = get32(f); if (!f->sample_rate) return error(f, VORBIS_invalid_first_page); + get32(f); // bitrate_maximum + get32(f); // bitrate_nominal + get32(f); // bitrate_minimum + x = get8(f); + { + int log0,log1; + log0 = x & 15; + log1 = x >> 4; + f->blocksize_0 = 1 << log0; + f->blocksize_1 = 1 << log1; + if (log0 < 6 || log0 > 13) return error(f, VORBIS_invalid_setup); + if (log1 < 6 || log1 > 13) return error(f, VORBIS_invalid_setup); + if (log0 > log1) return error(f, VORBIS_invalid_setup); + } + + // framing_flag + x = get8(f); + if (!(x & 1)) return error(f, VORBIS_invalid_first_page); + + // second packet! + if (!start_page(f)) return FALSE; + + if (!start_packet(f)) return FALSE; + do { + len = next_segment(f); + skip(f, len); + f->bytes_in_seg = 0; + } while (len); + + // third packet! + if (!start_packet(f)) return FALSE; + + #ifndef STB_VORBIS_NO_PUSHDATA_API + if (IS_PUSH_MODE(f)) { + if (!is_whole_packet_present(f, TRUE)) { + // convert error in ogg header to write type + if (f->error == VORBIS_invalid_stream) + f->error = VORBIS_invalid_setup; + return FALSE; + } + } + #endif + + crc32_init(); // always init it, to avoid multithread race conditions + + if (get8_packet(f) != VORBIS_packet_setup) return error(f, VORBIS_invalid_setup); + for (i=0; i < 6; ++i) header[i] = get8_packet(f); + if (!vorbis_validate(header)) return error(f, VORBIS_invalid_setup); + + // codebooks + + f->codebook_count = get_bits(f,8) + 1; + f->codebooks = (Codebook *) setup_malloc(f, sizeof(*f->codebooks) * f->codebook_count); + if (f->codebooks == NULL) return error(f, VORBIS_outofmem); + memset(f->codebooks, 0, sizeof(*f->codebooks) * f->codebook_count); + for (i=0; i < f->codebook_count; ++i) { + uint32 *values; + int ordered, sorted_count; + int total=0; + uint8 *lengths; + Codebook *c = f->codebooks+i; + CHECK(f); + x = get_bits(f, 8); if (x != 0x42) return error(f, VORBIS_invalid_setup); + x = get_bits(f, 8); if (x != 0x43) return error(f, VORBIS_invalid_setup); + x = get_bits(f, 8); if (x != 0x56) return error(f, VORBIS_invalid_setup); + x = get_bits(f, 8); + c->dimensions = (get_bits(f, 8)<<8) + x; + x = get_bits(f, 8); + y = get_bits(f, 8); + c->entries = (get_bits(f, 8)<<16) + (y<<8) + x; + ordered = get_bits(f,1); + c->sparse = ordered ? 0 : get_bits(f,1); + + if (c->dimensions == 0 && c->entries != 0) return error(f, VORBIS_invalid_setup); + + if (c->sparse) + lengths = (uint8 *) setup_temp_malloc(f, c->entries); + else + lengths = c->codeword_lengths = (uint8 *) setup_malloc(f, c->entries); + + if (!lengths) return error(f, VORBIS_outofmem); + + if (ordered) { + int current_entry = 0; + int current_length = get_bits(f,5) + 1; + while (current_entry < c->entries) { + int limit = c->entries - current_entry; + int n = get_bits(f, ilog(limit)); + if (current_entry + n > (int) c->entries) { return error(f, VORBIS_invalid_setup); } + memset(lengths + current_entry, current_length, n); + current_entry += n; + ++current_length; + } + } else { + for (j=0; j < c->entries; ++j) { + int present = c->sparse ? get_bits(f,1) : 1; + if (present) { + lengths[j] = get_bits(f, 5) + 1; + ++total; + if (lengths[j] == 32) + return error(f, VORBIS_invalid_setup); + } else { + lengths[j] = NO_CODE; + } + } + } + + if (c->sparse && total >= c->entries >> 2) { + // convert sparse items to non-sparse! + if (c->entries > (int) f->setup_temp_memory_required) + f->setup_temp_memory_required = c->entries; + + c->codeword_lengths = (uint8 *) setup_malloc(f, c->entries); + if (c->codeword_lengths == NULL) return error(f, VORBIS_outofmem); + memcpy(c->codeword_lengths, lengths, c->entries); + setup_temp_free(f, lengths, c->entries); // note this is only safe if there have been no intervening temp mallocs! + lengths = c->codeword_lengths; + c->sparse = 0; + } + + // compute the size of the sorted tables + if (c->sparse) { + sorted_count = total; + } else { + sorted_count = 0; + #ifndef STB_VORBIS_NO_HUFFMAN_BINARY_SEARCH + for (j=0; j < c->entries; ++j) + if (lengths[j] > STB_VORBIS_FAST_HUFFMAN_LENGTH && lengths[j] != NO_CODE) + ++sorted_count; + #endif + } + + c->sorted_entries = sorted_count; + values = NULL; + + CHECK(f); + if (!c->sparse) { + c->codewords = (uint32 *) setup_malloc(f, sizeof(c->codewords[0]) * c->entries); + if (!c->codewords) return error(f, VORBIS_outofmem); + } else { + unsigned int size; + if (c->sorted_entries) { + c->codeword_lengths = (uint8 *) setup_malloc(f, c->sorted_entries); + if (!c->codeword_lengths) return error(f, VORBIS_outofmem); + c->codewords = (uint32 *) setup_temp_malloc(f, sizeof(*c->codewords) * c->sorted_entries); + if (!c->codewords) return error(f, VORBIS_outofmem); + values = (uint32 *) setup_temp_malloc(f, sizeof(*values) * c->sorted_entries); + if (!values) return error(f, VORBIS_outofmem); + } + size = c->entries + (sizeof(*c->codewords) + sizeof(*values)) * c->sorted_entries; + if (size > f->setup_temp_memory_required) + f->setup_temp_memory_required = size; + } + + if (!compute_codewords(c, lengths, c->entries, values)) { + if (c->sparse) setup_temp_free(f, values, 0); + return error(f, VORBIS_invalid_setup); + } + + if (c->sorted_entries) { + // allocate an extra slot for sentinels + c->sorted_codewords = (uint32 *) setup_malloc(f, sizeof(*c->sorted_codewords) * (c->sorted_entries+1)); + if (c->sorted_codewords == NULL) return error(f, VORBIS_outofmem); + // allocate an extra slot at the front so that c->sorted_values[-1] is defined + // so that we can catch that case without an extra if + c->sorted_values = ( int *) setup_malloc(f, sizeof(*c->sorted_values ) * (c->sorted_entries+1)); + if (c->sorted_values == NULL) return error(f, VORBIS_outofmem); + ++c->sorted_values; + c->sorted_values[-1] = -1; + compute_sorted_huffman(c, lengths, values); + } + + if (c->sparse) { + setup_temp_free(f, values, sizeof(*values)*c->sorted_entries); + setup_temp_free(f, c->codewords, sizeof(*c->codewords)*c->sorted_entries); + setup_temp_free(f, lengths, c->entries); + c->codewords = NULL; + } + + compute_accelerated_huffman(c); + + CHECK(f); + c->lookup_type = get_bits(f, 4); + if (c->lookup_type > 2) return error(f, VORBIS_invalid_setup); + if (c->lookup_type > 0) { + uint16 *mults; + c->minimum_value = float32_unpack(get_bits(f, 32)); + c->delta_value = float32_unpack(get_bits(f, 32)); + c->value_bits = get_bits(f, 4)+1; + c->sequence_p = get_bits(f,1); + if (c->lookup_type == 1) { + c->lookup_values = lookup1_values(c->entries, c->dimensions); + } else { + c->lookup_values = c->entries * c->dimensions; + } + if (c->lookup_values == 0) return error(f, VORBIS_invalid_setup); + mults = (uint16 *) setup_temp_malloc(f, sizeof(mults[0]) * c->lookup_values); + if (mults == NULL) return error(f, VORBIS_outofmem); + for (j=0; j < (int) c->lookup_values; ++j) { + int q = get_bits(f, c->value_bits); + if (q == EOP) { setup_temp_free(f,mults,sizeof(mults[0])*c->lookup_values); return error(f, VORBIS_invalid_setup); } + mults[j] = q; + } + +#ifndef STB_VORBIS_DIVIDES_IN_CODEBOOK + if (c->lookup_type == 1) { + int len, sparse = c->sparse; + float last=0; + // pre-expand the lookup1-style multiplicands, to avoid a divide in the inner loop + if (sparse) { + if (c->sorted_entries == 0) goto skip; + c->multiplicands = (codetype *) setup_malloc(f, sizeof(c->multiplicands[0]) * c->sorted_entries * c->dimensions); + } else + c->multiplicands = (codetype *) setup_malloc(f, sizeof(c->multiplicands[0]) * c->entries * c->dimensions); + if (c->multiplicands == NULL) { setup_temp_free(f,mults,sizeof(mults[0])*c->lookup_values); return error(f, VORBIS_outofmem); } + len = sparse ? c->sorted_entries : c->entries; + for (j=0; j < len; ++j) { + unsigned int z = sparse ? c->sorted_values[j] : j; + unsigned int div=1; + for (k=0; k < c->dimensions; ++k) { + int off = (z / div) % c->lookup_values; + float val = mults[off]; + val = mults[off]*c->delta_value + c->minimum_value + last; + c->multiplicands[j*c->dimensions + k] = val; + if (c->sequence_p) + last = val; + if (k+1 < c->dimensions) { + if (div > UINT_MAX / (unsigned int) c->lookup_values) { + setup_temp_free(f, mults,sizeof(mults[0])*c->lookup_values); + return error(f, VORBIS_invalid_setup); + } + div *= c->lookup_values; + } + } + } + c->lookup_type = 2; + } + else +#endif + { + float last=0; + CHECK(f); + c->multiplicands = (codetype *) setup_malloc(f, sizeof(c->multiplicands[0]) * c->lookup_values); + if (c->multiplicands == NULL) { setup_temp_free(f, mults,sizeof(mults[0])*c->lookup_values); return error(f, VORBIS_outofmem); } + for (j=0; j < (int) c->lookup_values; ++j) { + float val = mults[j] * c->delta_value + c->minimum_value + last; + c->multiplicands[j] = val; + if (c->sequence_p) + last = val; + } + } +#ifndef STB_VORBIS_DIVIDES_IN_CODEBOOK + skip:; +#endif + setup_temp_free(f, mults, sizeof(mults[0])*c->lookup_values); + + CHECK(f); + } + CHECK(f); + } + + // time domain transfers (notused) + + x = get_bits(f, 6) + 1; + for (i=0; i < x; ++i) { + uint32 z = get_bits(f, 16); + if (z != 0) return error(f, VORBIS_invalid_setup); + } + + // Floors + f->floor_count = get_bits(f, 6)+1; + f->floor_config = (Floor *) setup_malloc(f, f->floor_count * sizeof(*f->floor_config)); + if (f->floor_config == NULL) return error(f, VORBIS_outofmem); + for (i=0; i < f->floor_count; ++i) { + f->floor_types[i] = get_bits(f, 16); + if (f->floor_types[i] > 1) return error(f, VORBIS_invalid_setup); + if (f->floor_types[i] == 0) { + Floor0 *g = &f->floor_config[i].floor0; + g->order = get_bits(f,8); + g->rate = get_bits(f,16); + g->bark_map_size = get_bits(f,16); + g->amplitude_bits = get_bits(f,6); + g->amplitude_offset = get_bits(f,8); + g->number_of_books = get_bits(f,4) + 1; + for (j=0; j < g->number_of_books; ++j) + g->book_list[j] = get_bits(f,8); + return error(f, VORBIS_feature_not_supported); + } else { + Point p[31*8+2]; + Floor1 *g = &f->floor_config[i].floor1; + int max_class = -1; + g->partitions = get_bits(f, 5); + for (j=0; j < g->partitions; ++j) { + g->partition_class_list[j] = get_bits(f, 4); + if (g->partition_class_list[j] > max_class) + max_class = g->partition_class_list[j]; + } + for (j=0; j <= max_class; ++j) { + g->class_dimensions[j] = get_bits(f, 3)+1; + g->class_subclasses[j] = get_bits(f, 2); + if (g->class_subclasses[j]) { + g->class_masterbooks[j] = get_bits(f, 8); + if (g->class_masterbooks[j] >= f->codebook_count) return error(f, VORBIS_invalid_setup); + } + for (k=0; k < 1 << g->class_subclasses[j]; ++k) { + g->subclass_books[j][k] = get_bits(f,8)-1; + if (g->subclass_books[j][k] >= f->codebook_count) return error(f, VORBIS_invalid_setup); + } + } + g->floor1_multiplier = get_bits(f,2)+1; + g->rangebits = get_bits(f,4); + g->Xlist[0] = 0; + g->Xlist[1] = 1 << g->rangebits; + g->values = 2; + for (j=0; j < g->partitions; ++j) { + int c = g->partition_class_list[j]; + for (k=0; k < g->class_dimensions[c]; ++k) { + g->Xlist[g->values] = get_bits(f, g->rangebits); + ++g->values; + } + } + // precompute the sorting + for (j=0; j < g->values; ++j) { + p[j].x = g->Xlist[j]; + p[j].y = j; + } + qsort(p, g->values, sizeof(p[0]), point_compare); + for (j=0; j < g->values; ++j) + g->sorted_order[j] = (uint8) p[j].y; + // precompute the neighbors + for (j=2; j < g->values; ++j) { + int low,hi; + neighbors(g->Xlist, j, &low,&hi); + g->neighbors[j][0] = low; + g->neighbors[j][1] = hi; + } + + if (g->values > longest_floorlist) + longest_floorlist = g->values; + } + } + + // Residue + f->residue_count = get_bits(f, 6)+1; + f->residue_config = (Residue *) setup_malloc(f, f->residue_count * sizeof(f->residue_config[0])); + if (f->residue_config == NULL) return error(f, VORBIS_outofmem); + memset(f->residue_config, 0, f->residue_count * sizeof(f->residue_config[0])); + for (i=0; i < f->residue_count; ++i) { + uint8 residue_cascade[64]; + Residue *r = f->residue_config+i; + f->residue_types[i] = get_bits(f, 16); + if (f->residue_types[i] > 2) return error(f, VORBIS_invalid_setup); + r->begin = get_bits(f, 24); + r->end = get_bits(f, 24); + if (r->end < r->begin) return error(f, VORBIS_invalid_setup); + r->part_size = get_bits(f,24)+1; + r->classifications = get_bits(f,6)+1; + r->classbook = get_bits(f,8); + if (r->classbook >= f->codebook_count) return error(f, VORBIS_invalid_setup); + for (j=0; j < r->classifications; ++j) { + uint8 high_bits=0; + uint8 low_bits=get_bits(f,3); + if (get_bits(f,1)) + high_bits = get_bits(f,5); + residue_cascade[j] = high_bits*8 + low_bits; + } + r->residue_books = (short (*)[8]) setup_malloc(f, sizeof(r->residue_books[0]) * r->classifications); + if (r->residue_books == NULL) return error(f, VORBIS_outofmem); + for (j=0; j < r->classifications; ++j) { + for (k=0; k < 8; ++k) { + if (residue_cascade[j] & (1 << k)) { + r->residue_books[j][k] = get_bits(f, 8); + if (r->residue_books[j][k] >= f->codebook_count) return error(f, VORBIS_invalid_setup); + } else { + r->residue_books[j][k] = -1; + } + } + } + // precompute the classifications[] array to avoid inner-loop mod/divide + // call it 'classdata' since we already have r->classifications + r->classdata = (uint8 **) setup_malloc(f, sizeof(*r->classdata) * f->codebooks[r->classbook].entries); + if (!r->classdata) return error(f, VORBIS_outofmem); + memset(r->classdata, 0, sizeof(*r->classdata) * f->codebooks[r->classbook].entries); + for (j=0; j < f->codebooks[r->classbook].entries; ++j) { + int classwords = f->codebooks[r->classbook].dimensions; + int temp = j; + r->classdata[j] = (uint8 *) setup_malloc(f, sizeof(r->classdata[j][0]) * classwords); + if (r->classdata[j] == NULL) return error(f, VORBIS_outofmem); + for (k=classwords-1; k >= 0; --k) { + r->classdata[j][k] = temp % r->classifications; + temp /= r->classifications; + } + } + } + + f->mapping_count = get_bits(f,6)+1; + f->mapping = (Mapping *) setup_malloc(f, f->mapping_count * sizeof(*f->mapping)); + if (f->mapping == NULL) return error(f, VORBIS_outofmem); + memset(f->mapping, 0, f->mapping_count * sizeof(*f->mapping)); + for (i=0; i < f->mapping_count; ++i) { + Mapping *m = f->mapping + i; + int mapping_type = get_bits(f,16); + if (mapping_type != 0) return error(f, VORBIS_invalid_setup); + m->chan = (MappingChannel *) setup_malloc(f, f->channels * sizeof(*m->chan)); + if (m->chan == NULL) return error(f, VORBIS_outofmem); + if (get_bits(f,1)) + m->submaps = get_bits(f,4)+1; + else + m->submaps = 1; + if (m->submaps > max_submaps) + max_submaps = m->submaps; + if (get_bits(f,1)) { + m->coupling_steps = get_bits(f,8)+1; + for (k=0; k < m->coupling_steps; ++k) { + m->chan[k].magnitude = get_bits(f, ilog(f->channels-1)); + m->chan[k].angle = get_bits(f, ilog(f->channels-1)); + if (m->chan[k].magnitude >= f->channels) return error(f, VORBIS_invalid_setup); + if (m->chan[k].angle >= f->channels) return error(f, VORBIS_invalid_setup); + if (m->chan[k].magnitude == m->chan[k].angle) return error(f, VORBIS_invalid_setup); + } + } else + m->coupling_steps = 0; + + // reserved field + if (get_bits(f,2)) return error(f, VORBIS_invalid_setup); + if (m->submaps > 1) { + for (j=0; j < f->channels; ++j) { + m->chan[j].mux = get_bits(f, 4); + if (m->chan[j].mux >= m->submaps) return error(f, VORBIS_invalid_setup); + } + } else + // @SPECIFICATION: this case is missing from the spec + for (j=0; j < f->channels; ++j) + m->chan[j].mux = 0; + + for (j=0; j < m->submaps; ++j) { + get_bits(f,8); // discard + m->submap_floor[j] = get_bits(f,8); + m->submap_residue[j] = get_bits(f,8); + if (m->submap_floor[j] >= f->floor_count) return error(f, VORBIS_invalid_setup); + if (m->submap_residue[j] >= f->residue_count) return error(f, VORBIS_invalid_setup); + } + } + + // Modes + f->mode_count = get_bits(f, 6)+1; + for (i=0; i < f->mode_count; ++i) { + Mode *m = f->mode_config+i; + m->blockflag = get_bits(f,1); + m->windowtype = get_bits(f,16); + m->transformtype = get_bits(f,16); + m->mapping = get_bits(f,8); + if (m->windowtype != 0) return error(f, VORBIS_invalid_setup); + if (m->transformtype != 0) return error(f, VORBIS_invalid_setup); + if (m->mapping >= f->mapping_count) return error(f, VORBIS_invalid_setup); + } + + flush_packet(f); + + f->previous_length = 0; + + for (i=0; i < f->channels; ++i) { + f->channel_buffers[i] = (float *) setup_malloc(f, sizeof(float) * f->blocksize_1); + f->previous_window[i] = (float *) setup_malloc(f, sizeof(float) * f->blocksize_1/2); + f->finalY[i] = (int16 *) setup_malloc(f, sizeof(int16) * longest_floorlist); + if (f->channel_buffers[i] == NULL || f->previous_window[i] == NULL || f->finalY[i] == NULL) return error(f, VORBIS_outofmem); + #ifdef STB_VORBIS_NO_DEFER_FLOOR + f->floor_buffers[i] = (float *) setup_malloc(f, sizeof(float) * f->blocksize_1/2); + if (f->floor_buffers[i] == NULL) return error(f, VORBIS_outofmem); + #endif + } + + if (!init_blocksize(f, 0, f->blocksize_0)) return FALSE; + if (!init_blocksize(f, 1, f->blocksize_1)) return FALSE; + f->blocksize[0] = f->blocksize_0; + f->blocksize[1] = f->blocksize_1; + +#ifdef STB_VORBIS_DIVIDE_TABLE + if (integer_divide_table[1][1]==0) + for (i=0; i < DIVTAB_NUMER; ++i) + for (j=1; j < DIVTAB_DENOM; ++j) + integer_divide_table[i][j] = i / j; +#endif + + // compute how much temporary memory is needed + + // 1. + { + uint32 imdct_mem = (f->blocksize_1 * sizeof(float) >> 1); + uint32 classify_mem; + int i,max_part_read=0; + for (i=0; i < f->residue_count; ++i) { + Residue *r = f->residue_config + i; + int n_read = r->end - r->begin; + int part_read = n_read / r->part_size; + if (part_read > max_part_read) + max_part_read = part_read; + } + #ifndef STB_VORBIS_DIVIDES_IN_RESIDUE + classify_mem = f->channels * (sizeof(void*) + max_part_read * sizeof(uint8 *)); + #else + classify_mem = f->channels * (sizeof(void*) + max_part_read * sizeof(int *)); + #endif + + f->temp_memory_required = classify_mem; + if (imdct_mem > f->temp_memory_required) + f->temp_memory_required = imdct_mem; + } + + f->first_decode = TRUE; + + if (f->alloc.alloc_buffer) { + assert(f->temp_offset == f->alloc.alloc_buffer_length_in_bytes); + // check if there's enough temp memory so we don't error later + if (f->setup_offset + sizeof(*f) + f->temp_memory_required > (unsigned) f->temp_offset) + return error(f, VORBIS_outofmem); + } + + f->first_audio_page_offset = stb_vorbis_get_file_offset(f); + + return TRUE; +} + +static void vorbis_deinit(stb_vorbis *p) +{ + int i,j; + if (p->residue_config) { + for (i=0; i < p->residue_count; ++i) { + Residue *r = p->residue_config+i; + if (r->classdata) { + for (j=0; j < p->codebooks[r->classbook].entries; ++j) + setup_free(p, r->classdata[j]); + setup_free(p, r->classdata); + } + setup_free(p, r->residue_books); + } + } + + if (p->codebooks) { + CHECK(p); + for (i=0; i < p->codebook_count; ++i) { + Codebook *c = p->codebooks + i; + setup_free(p, c->codeword_lengths); + setup_free(p, c->multiplicands); + setup_free(p, c->codewords); + setup_free(p, c->sorted_codewords); + // c->sorted_values[-1] is the first entry in the array + setup_free(p, c->sorted_values ? c->sorted_values-1 : NULL); + } + setup_free(p, p->codebooks); + } + setup_free(p, p->floor_config); + setup_free(p, p->residue_config); + if (p->mapping) { + for (i=0; i < p->mapping_count; ++i) + setup_free(p, p->mapping[i].chan); + setup_free(p, p->mapping); + } + CHECK(p); + for (i=0; i < p->channels && i < STB_VORBIS_MAX_CHANNELS; ++i) { + setup_free(p, p->channel_buffers[i]); + setup_free(p, p->previous_window[i]); + #ifdef STB_VORBIS_NO_DEFER_FLOOR + setup_free(p, p->floor_buffers[i]); + #endif + setup_free(p, p->finalY[i]); + } + for (i=0; i < 2; ++i) { + setup_free(p, p->A[i]); + setup_free(p, p->B[i]); + setup_free(p, p->C[i]); + setup_free(p, p->window[i]); + setup_free(p, p->bit_reverse[i]); + } + #ifndef STB_VORBIS_NO_STDIO + if (p->close_on_free) fclose(p->f); + #endif +} + +void stb_vorbis_close(stb_vorbis *p) +{ + if (p == NULL) return; + vorbis_deinit(p); + setup_free(p,p); +} + +static void vorbis_init(stb_vorbis *p, const stb_vorbis_alloc *z) +{ + memset(p, 0, sizeof(*p)); // NULL out all malloc'd pointers to start + if (z) { + p->alloc = *z; + p->alloc.alloc_buffer_length_in_bytes = (p->alloc.alloc_buffer_length_in_bytes+3) & ~3; + p->temp_offset = p->alloc.alloc_buffer_length_in_bytes; + } + p->eof = 0; + p->error = VORBIS__no_error; + p->stream = NULL; + p->codebooks = NULL; + p->page_crc_tests = -1; + #ifndef STB_VORBIS_NO_STDIO + p->close_on_free = FALSE; + p->f = NULL; + #endif +} + +int stb_vorbis_get_sample_offset(stb_vorbis *f) +{ + if (f->current_loc_valid) + return f->current_loc; + else + return -1; +} + +stb_vorbis_info stb_vorbis_get_info(stb_vorbis *f) +{ + stb_vorbis_info d; + d.channels = f->channels; + d.sample_rate = f->sample_rate; + d.setup_memory_required = f->setup_memory_required; + d.setup_temp_memory_required = f->setup_temp_memory_required; + d.temp_memory_required = f->temp_memory_required; + d.max_frame_size = f->blocksize_1 >> 1; + return d; +} + +int stb_vorbis_get_error(stb_vorbis *f) +{ + int e = f->error; + f->error = VORBIS__no_error; + return e; +} + +static stb_vorbis * vorbis_alloc(stb_vorbis *f) +{ + stb_vorbis *p = (stb_vorbis *) setup_malloc(f, sizeof(*p)); + return p; +} + +#ifndef STB_VORBIS_NO_PUSHDATA_API + +void stb_vorbis_flush_pushdata(stb_vorbis *f) +{ + f->previous_length = 0; + f->page_crc_tests = 0; + f->discard_samples_deferred = 0; + f->current_loc_valid = FALSE; + f->first_decode = FALSE; + f->samples_output = 0; + f->channel_buffer_start = 0; + f->channel_buffer_end = 0; +} + +static int vorbis_search_for_page_pushdata(vorb *f, uint8 *data, int data_len) +{ + int i,n; + for (i=0; i < f->page_crc_tests; ++i) + f->scan[i].bytes_done = 0; + + // if we have room for more scans, search for them first, because + // they may cause us to stop early if their header is incomplete + if (f->page_crc_tests < STB_VORBIS_PUSHDATA_CRC_COUNT) { + if (data_len < 4) return 0; + data_len -= 3; // need to look for 4-byte sequence, so don't miss + // one that straddles a boundary + for (i=0; i < data_len; ++i) { + if (data[i] == 0x4f) { + if (0==memcmp(data+i, ogg_page_header, 4)) { + int j,len; + uint32 crc; + // make sure we have the whole page header + if (i+26 >= data_len || i+27+data[i+26] >= data_len) { + // only read up to this page start, so hopefully we'll + // have the whole page header start next time + data_len = i; + break; + } + // ok, we have it all; compute the length of the page + len = 27 + data[i+26]; + for (j=0; j < data[i+26]; ++j) + len += data[i+27+j]; + // scan everything up to the embedded crc (which we must 0) + crc = 0; + for (j=0; j < 22; ++j) + crc = crc32_update(crc, data[i+j]); + // now process 4 0-bytes + for ( ; j < 26; ++j) + crc = crc32_update(crc, 0); + // len is the total number of bytes we need to scan + n = f->page_crc_tests++; + f->scan[n].bytes_left = len-j; + f->scan[n].crc_so_far = crc; + f->scan[n].goal_crc = data[i+22] + (data[i+23] << 8) + (data[i+24]<<16) + (data[i+25]<<24); + // if the last frame on a page is continued to the next, then + // we can't recover the sample_loc immediately + if (data[i+27+data[i+26]-1] == 255) + f->scan[n].sample_loc = ~0; + else + f->scan[n].sample_loc = data[i+6] + (data[i+7] << 8) + (data[i+ 8]<<16) + (data[i+ 9]<<24); + f->scan[n].bytes_done = i+j; + if (f->page_crc_tests == STB_VORBIS_PUSHDATA_CRC_COUNT) + break; + // keep going if we still have room for more + } + } + } + } + + for (i=0; i < f->page_crc_tests;) { + uint32 crc; + int j; + int n = f->scan[i].bytes_done; + int m = f->scan[i].bytes_left; + if (m > data_len - n) m = data_len - n; + // m is the bytes to scan in the current chunk + crc = f->scan[i].crc_so_far; + for (j=0; j < m; ++j) + crc = crc32_update(crc, data[n+j]); + f->scan[i].bytes_left -= m; + f->scan[i].crc_so_far = crc; + if (f->scan[i].bytes_left == 0) { + // does it match? + if (f->scan[i].crc_so_far == f->scan[i].goal_crc) { + // Houston, we have page + data_len = n+m; // consumption amount is wherever that scan ended + f->page_crc_tests = -1; // drop out of page scan mode + f->previous_length = 0; // decode-but-don't-output one frame + f->next_seg = -1; // start a new page + f->current_loc = f->scan[i].sample_loc; // set the current sample location + // to the amount we'd have decoded had we decoded this page + f->current_loc_valid = f->current_loc != ~0U; + return data_len; + } + // delete entry + f->scan[i] = f->scan[--f->page_crc_tests]; + } else { + ++i; + } + } + + return data_len; +} + +// return value: number of bytes we used +int stb_vorbis_decode_frame_pushdata( + stb_vorbis *f, // the file we're decoding + const uint8 *data, int data_len, // the memory available for decoding + int *channels, // place to write number of float * buffers + float ***output, // place to write float ** array of float * buffers + int *samples // place to write number of output samples + ) +{ + int i; + int len,right,left; + + if (!IS_PUSH_MODE(f)) return error(f, VORBIS_invalid_api_mixing); + + if (f->page_crc_tests >= 0) { + *samples = 0; + return vorbis_search_for_page_pushdata(f, (uint8 *) data, data_len); + } + + f->stream = (uint8 *) data; + f->stream_end = (uint8 *) data + data_len; + f->error = VORBIS__no_error; + + // check that we have the entire packet in memory + if (!is_whole_packet_present(f, FALSE)) { + *samples = 0; + return 0; + } + + if (!vorbis_decode_packet(f, &len, &left, &right)) { + // save the actual error we encountered + enum STBVorbisError error = f->error; + if (error == VORBIS_bad_packet_type) { + // flush and resynch + f->error = VORBIS__no_error; + while (get8_packet(f) != EOP) + if (f->eof) break; + *samples = 0; + return (int) (f->stream - data); + } + if (error == VORBIS_continued_packet_flag_invalid) { + if (f->previous_length == 0) { + // we may be resynching, in which case it's ok to hit one + // of these; just discard the packet + f->error = VORBIS__no_error; + while (get8_packet(f) != EOP) + if (f->eof) break; + *samples = 0; + return (int) (f->stream - data); + } + } + // if we get an error while parsing, what to do? + // well, it DEFINITELY won't work to continue from where we are! + stb_vorbis_flush_pushdata(f); + // restore the error that actually made us bail + f->error = error; + *samples = 0; + return 1; + } + + // success! + len = vorbis_finish_frame(f, len, left, right); + for (i=0; i < f->channels; ++i) + f->outputs[i] = f->channel_buffers[i] + left; + + if (channels) *channels = f->channels; + *samples = len; + *output = f->outputs; + return (int) (f->stream - data); +} + +stb_vorbis *stb_vorbis_open_pushdata( + const unsigned char *data, int data_len, // the memory available for decoding + int *data_used, // only defined if result is not NULL + int *error, const stb_vorbis_alloc *alloc) +{ + stb_vorbis *f, p; + vorbis_init(&p, alloc); + p.stream = (uint8 *) data; + p.stream_end = (uint8 *) data + data_len; + p.push_mode = TRUE; + if (!start_decoder(&p)) { + if (p.eof) + *error = VORBIS_need_more_data; + else + *error = p.error; + return NULL; + } + f = vorbis_alloc(&p); + if (f) { + *f = p; + *data_used = (int) (f->stream - data); + *error = 0; + return f; + } else { + vorbis_deinit(&p); + return NULL; + } +} +#endif // STB_VORBIS_NO_PUSHDATA_API + +unsigned int stb_vorbis_get_file_offset(stb_vorbis *f) +{ + #ifndef STB_VORBIS_NO_PUSHDATA_API + if (f->push_mode) return 0; + #endif + if (USE_MEMORY(f)) return (unsigned int) (f->stream - f->stream_start); + #ifndef STB_VORBIS_NO_STDIO + return (unsigned int) (ftell(f->f) - f->f_start); + #endif +} + +#ifndef STB_VORBIS_NO_PULLDATA_API +// +// DATA-PULLING API +// + +static uint32 vorbis_find_page(stb_vorbis *f, uint32 *end, uint32 *last) +{ + for(;;) { + int n; + if (f->eof) return 0; + n = get8(f); + if (n == 0x4f) { // page header candidate + unsigned int retry_loc = stb_vorbis_get_file_offset(f); + int i; + // check if we're off the end of a file_section stream + if (retry_loc - 25 > f->stream_len) + return 0; + // check the rest of the header + for (i=1; i < 4; ++i) + if (get8(f) != ogg_page_header[i]) + break; + if (f->eof) return 0; + if (i == 4) { + uint8 header[27]; + uint32 i, crc, goal, len; + for (i=0; i < 4; ++i) + header[i] = ogg_page_header[i]; + for (; i < 27; ++i) + header[i] = get8(f); + if (f->eof) return 0; + if (header[4] != 0) goto invalid; + goal = header[22] + (header[23] << 8) + (header[24]<<16) + (header[25]<<24); + for (i=22; i < 26; ++i) + header[i] = 0; + crc = 0; + for (i=0; i < 27; ++i) + crc = crc32_update(crc, header[i]); + len = 0; + for (i=0; i < header[26]; ++i) { + int s = get8(f); + crc = crc32_update(crc, s); + len += s; + } + if (len && f->eof) return 0; + for (i=0; i < len; ++i) + crc = crc32_update(crc, get8(f)); + // finished parsing probable page + if (crc == goal) { + // we could now check that it's either got the last + // page flag set, OR it's followed by the capture + // pattern, but I guess TECHNICALLY you could have + // a file with garbage between each ogg page and recover + // from it automatically? So even though that paranoia + // might decrease the chance of an invalid decode by + // another 2^32, not worth it since it would hose those + // invalid-but-useful files? + if (end) + *end = stb_vorbis_get_file_offset(f); + if (last) { + if (header[5] & 0x04) + *last = 1; + else + *last = 0; + } + set_file_offset(f, retry_loc-1); + return 1; + } + } + invalid: + // not a valid page, so rewind and look for next one + set_file_offset(f, retry_loc); + } + } +} + + +#define SAMPLE_unknown 0xffffffff + +// seeking is implemented with a binary search, which narrows down the range to +// 64K, before using a linear search (because finding the synchronization +// pattern can be expensive, and the chance we'd find the end page again is +// relatively high for small ranges) +// +// two initial interpolation-style probes are used at the start of the search +// to try to bound either side of the binary search sensibly, while still +// working in O(log n) time if they fail. + +static int get_seek_page_info(stb_vorbis *f, ProbedPage *z) +{ + uint8 header[27], lacing[255]; + int i,len; + + // record where the page starts + z->page_start = stb_vorbis_get_file_offset(f); + + // parse the header + getn(f, header, 27); + if (header[0] != 'O' || header[1] != 'g' || header[2] != 'g' || header[3] != 'S') + return 0; + getn(f, lacing, header[26]); + + // determine the length of the payload + len = 0; + for (i=0; i < header[26]; ++i) + len += lacing[i]; + + // this implies where the page ends + z->page_end = z->page_start + 27 + header[26] + len; + + // read the last-decoded sample out of the data + z->last_decoded_sample = header[6] + (header[7] << 8) + (header[8] << 16) + (header[9] << 24); + + // restore file state to where we were + set_file_offset(f, z->page_start); + return 1; +} + +// rarely used function to seek back to the preceeding page while finding the +// start of a packet +static int go_to_page_before(stb_vorbis *f, unsigned int limit_offset) +{ + unsigned int previous_safe, end; + + // now we want to seek back 64K from the limit + if (limit_offset >= 65536 && limit_offset-65536 >= f->first_audio_page_offset) + previous_safe = limit_offset - 65536; + else + previous_safe = f->first_audio_page_offset; + + set_file_offset(f, previous_safe); + + while (vorbis_find_page(f, &end, NULL)) { + if (end >= limit_offset && stb_vorbis_get_file_offset(f) < limit_offset) + return 1; + set_file_offset(f, end); + } + + return 0; +} + +// implements the search logic for finding a page and starting decoding. if +// the function succeeds, current_loc_valid will be true and current_loc will +// be less than or equal to the provided sample number (the closer the +// better). +static int seek_to_sample_coarse(stb_vorbis *f, uint32 sample_number) +{ + ProbedPage left, right, mid; + int i, start_seg_with_known_loc, end_pos, page_start; + uint32 delta, stream_length, padding; + double offset, bytes_per_sample; + int probe = 0; + + // find the last page and validate the target sample + stream_length = stb_vorbis_stream_length_in_samples(f); + if (stream_length == 0) return error(f, VORBIS_seek_without_length); + if (sample_number > stream_length) return error(f, VORBIS_seek_invalid); + + // this is the maximum difference between the window-center (which is the + // actual granule position value), and the right-start (which the spec + // indicates should be the granule position (give or take one)). + padding = ((f->blocksize_1 - f->blocksize_0) >> 2); + if (sample_number < padding) + sample_number = 0; + else + sample_number -= padding; + + left = f->p_first; + while (left.last_decoded_sample == ~0U) { + // (untested) the first page does not have a 'last_decoded_sample' + set_file_offset(f, left.page_end); + if (!get_seek_page_info(f, &left)) goto error; + } + + right = f->p_last; + assert(right.last_decoded_sample != ~0U); + + // starting from the start is handled differently + if (sample_number <= left.last_decoded_sample) { + stb_vorbis_seek_start(f); + return 1; + } + + while (left.page_end != right.page_start) { + assert(left.page_end < right.page_start); + // search range in bytes + delta = right.page_start - left.page_end; + if (delta <= 65536) { + // there's only 64K left to search - handle it linearly + set_file_offset(f, left.page_end); + } else { + if (probe < 2) { + if (probe == 0) { + // first probe (interpolate) + double data_bytes = right.page_end - left.page_start; + bytes_per_sample = data_bytes / right.last_decoded_sample; + offset = left.page_start + bytes_per_sample * (sample_number - left.last_decoded_sample); + } else { + // second probe (try to bound the other side) + double error = ((double) sample_number - mid.last_decoded_sample) * bytes_per_sample; + if (error >= 0 && error < 8000) error = 8000; + if (error < 0 && error > -8000) error = -8000; + offset += error * 2; + } + + // ensure the offset is valid + if (offset < left.page_end) + offset = left.page_end; + if (offset > right.page_start - 65536) + offset = right.page_start - 65536; + + set_file_offset(f, (unsigned int) offset); + } else { + // binary search for large ranges (offset by 32K to ensure + // we don't hit the right page) + set_file_offset(f, left.page_end + (delta / 2) - 32768); + } + + if (!vorbis_find_page(f, NULL, NULL)) goto error; + } + + for (;;) { + if (!get_seek_page_info(f, &mid)) goto error; + if (mid.last_decoded_sample != ~0U) break; + // (untested) no frames end on this page + set_file_offset(f, mid.page_end); + assert(mid.page_start < right.page_start); + } + + // if we've just found the last page again then we're in a tricky file, + // and we're close enough. + if (mid.page_start == right.page_start) + break; + + if (sample_number < mid.last_decoded_sample) + right = mid; + else + left = mid; + + ++probe; + } + + // seek back to start of the last packet + page_start = left.page_start; + set_file_offset(f, page_start); + if (!start_page(f)) return error(f, VORBIS_seek_failed); + end_pos = f->end_seg_with_known_loc; + assert(end_pos >= 0); + + for (;;) { + for (i = end_pos; i > 0; --i) + if (f->segments[i-1] != 255) + break; + + start_seg_with_known_loc = i; + + if (start_seg_with_known_loc > 0 || !(f->page_flag & PAGEFLAG_continued_packet)) + break; + + // (untested) the final packet begins on an earlier page + if (!go_to_page_before(f, page_start)) + goto error; + + page_start = stb_vorbis_get_file_offset(f); + if (!start_page(f)) goto error; + end_pos = f->segment_count - 1; + } + + // prepare to start decoding + f->current_loc_valid = FALSE; + f->last_seg = FALSE; + f->valid_bits = 0; + f->packet_bytes = 0; + f->bytes_in_seg = 0; + f->previous_length = 0; + f->next_seg = start_seg_with_known_loc; + + for (i = 0; i < start_seg_with_known_loc; i++) + skip(f, f->segments[i]); + + // start decoding (optimizable - this frame is generally discarded) + vorbis_pump_first_frame(f); + return 1; + +error: + // try to restore the file to a valid state + stb_vorbis_seek_start(f); + return error(f, VORBIS_seek_failed); +} + +// the same as vorbis_decode_initial, but without advancing +static int peek_decode_initial(vorb *f, int *p_left_start, int *p_left_end, int *p_right_start, int *p_right_end, int *mode) +{ + int bits_read, bytes_read; + + if (!vorbis_decode_initial(f, p_left_start, p_left_end, p_right_start, p_right_end, mode)) + return 0; + + // either 1 or 2 bytes were read, figure out which so we can rewind + bits_read = 1 + ilog(f->mode_count-1); + if (f->mode_config[*mode].blockflag) + bits_read += 2; + bytes_read = (bits_read + 7) / 8; + + f->bytes_in_seg += bytes_read; + f->packet_bytes -= bytes_read; + skip(f, -bytes_read); + if (f->next_seg == -1) + f->next_seg = f->segment_count - 1; + else + f->next_seg--; + f->valid_bits = 0; + + return 1; +} + +int stb_vorbis_seek_frame(stb_vorbis *f, unsigned int sample_number) +{ + uint32 max_frame_samples; + + if (IS_PUSH_MODE(f)) return error(f, VORBIS_invalid_api_mixing); + + // fast page-level search + if (!seek_to_sample_coarse(f, sample_number)) + return 0; + + assert(f->current_loc_valid); + assert(f->current_loc <= sample_number); + + // linear search for the relevant packet + max_frame_samples = (f->blocksize_1*3 - f->blocksize_0) >> 2; + while (f->current_loc < sample_number) { + int left_start, left_end, right_start, right_end, mode, frame_samples; + if (!peek_decode_initial(f, &left_start, &left_end, &right_start, &right_end, &mode)) + return error(f, VORBIS_seek_failed); + // calculate the number of samples returned by the next frame + frame_samples = right_start - left_start; + if (f->current_loc + frame_samples > sample_number) { + return 1; // the next frame will contain the sample + } else if (f->current_loc + frame_samples + max_frame_samples > sample_number) { + // there's a chance the frame after this could contain the sample + vorbis_pump_first_frame(f); + } else { + // this frame is too early to be relevant + f->current_loc += frame_samples; + f->previous_length = 0; + maybe_start_packet(f); + flush_packet(f); + } + } + // the next frame will start with the sample + assert(f->current_loc == sample_number); + return 1; +} + +int stb_vorbis_seek(stb_vorbis *f, unsigned int sample_number) +{ + if (!stb_vorbis_seek_frame(f, sample_number)) + return 0; + + if (sample_number != f->current_loc) { + int n; + uint32 frame_start = f->current_loc; + stb_vorbis_get_frame_float(f, &n, NULL); + assert(sample_number > frame_start); + assert(f->channel_buffer_start + (int) (sample_number-frame_start) <= f->channel_buffer_end); + f->channel_buffer_start += (sample_number - frame_start); + } + + return 1; +} + +void stb_vorbis_seek_start(stb_vorbis *f) +{ + if (IS_PUSH_MODE(f)) { error(f, VORBIS_invalid_api_mixing); return; } + set_file_offset(f, f->first_audio_page_offset); + f->previous_length = 0; + f->first_decode = TRUE; + f->next_seg = -1; + vorbis_pump_first_frame(f); +} + +unsigned int stb_vorbis_stream_length_in_samples(stb_vorbis *f) +{ + unsigned int restore_offset, previous_safe; + unsigned int end, last_page_loc; + + if (IS_PUSH_MODE(f)) return error(f, VORBIS_invalid_api_mixing); + if (!f->total_samples) { + unsigned int last; + uint32 lo,hi; + char header[6]; + + // first, store the current decode position so we can restore it + restore_offset = stb_vorbis_get_file_offset(f); + + // now we want to seek back 64K from the end (the last page must + // be at most a little less than 64K, but let's allow a little slop) + if (f->stream_len >= 65536 && f->stream_len-65536 >= f->first_audio_page_offset) + previous_safe = f->stream_len - 65536; + else + previous_safe = f->first_audio_page_offset; + + set_file_offset(f, previous_safe); + // previous_safe is now our candidate 'earliest known place that seeking + // to will lead to the final page' + + if (!vorbis_find_page(f, &end, &last)) { + // if we can't find a page, we're hosed! + f->error = VORBIS_cant_find_last_page; + f->total_samples = 0xffffffff; + goto done; + } + + // check if there are more pages + last_page_loc = stb_vorbis_get_file_offset(f); + + // stop when the last_page flag is set, not when we reach eof; + // this allows us to stop short of a 'file_section' end without + // explicitly checking the length of the section + while (!last) { + set_file_offset(f, end); + if (!vorbis_find_page(f, &end, &last)) { + // the last page we found didn't have the 'last page' flag + // set. whoops! + break; + } + previous_safe = last_page_loc+1; + last_page_loc = stb_vorbis_get_file_offset(f); + } + + set_file_offset(f, last_page_loc); + + // parse the header + getn(f, (unsigned char *)header, 6); + // extract the absolute granule position + lo = get32(f); + hi = get32(f); + if (lo == 0xffffffff && hi == 0xffffffff) { + f->error = VORBIS_cant_find_last_page; + f->total_samples = SAMPLE_unknown; + goto done; + } + if (hi) + lo = 0xfffffffe; // saturate + f->total_samples = lo; + + f->p_last.page_start = last_page_loc; + f->p_last.page_end = end; + f->p_last.last_decoded_sample = lo; + + done: + set_file_offset(f, restore_offset); + } + return f->total_samples == SAMPLE_unknown ? 0 : f->total_samples; +} + +float stb_vorbis_stream_length_in_seconds(stb_vorbis *f) +{ + return stb_vorbis_stream_length_in_samples(f) / (float) f->sample_rate; +} + + + +int stb_vorbis_get_frame_float(stb_vorbis *f, int *channels, float ***output) +{ + int len, right,left,i; + if (IS_PUSH_MODE(f)) return error(f, VORBIS_invalid_api_mixing); + + if (!vorbis_decode_packet(f, &len, &left, &right)) { + f->channel_buffer_start = f->channel_buffer_end = 0; + return 0; + } + + len = vorbis_finish_frame(f, len, left, right); + for (i=0; i < f->channels; ++i) + f->outputs[i] = f->channel_buffers[i] + left; + + f->channel_buffer_start = left; + f->channel_buffer_end = left+len; + + if (channels) *channels = f->channels; + if (output) *output = f->outputs; + return len; +} + +#ifndef STB_VORBIS_NO_STDIO + +stb_vorbis * stb_vorbis_open_file_section(FILE *file, int close_on_free, int *error, const stb_vorbis_alloc *alloc, unsigned int length) +{ + stb_vorbis *f, p; + vorbis_init(&p, alloc); + p.f = file; + p.f_start = (uint32) ftell(file); + p.stream_len = length; + p.close_on_free = close_on_free; + if (start_decoder(&p)) { + f = vorbis_alloc(&p); + if (f) { + *f = p; + vorbis_pump_first_frame(f); + return f; + } + } + if (error) *error = p.error; + vorbis_deinit(&p); + return NULL; +} + +stb_vorbis * stb_vorbis_open_file(FILE *file, int close_on_free, int *error, const stb_vorbis_alloc *alloc) +{ + unsigned int len, start; + start = (unsigned int) ftell(file); + fseek(file, 0, SEEK_END); + len = (unsigned int) (ftell(file) - start); + fseek(file, start, SEEK_SET); + return stb_vorbis_open_file_section(file, close_on_free, error, alloc, len); +} + +stb_vorbis * stb_vorbis_open_filename(const char *filename, int *error, const stb_vorbis_alloc *alloc) +{ + FILE *f = fopen(filename, "rb"); + if (f) + return stb_vorbis_open_file(f, TRUE, error, alloc); + if (error) *error = VORBIS_file_open_failure; + return NULL; +} +#endif // STB_VORBIS_NO_STDIO + +stb_vorbis * stb_vorbis_open_memory(const unsigned char *data, int len, int *error, const stb_vorbis_alloc *alloc) +{ + stb_vorbis *f, p; + if (data == NULL) return NULL; + vorbis_init(&p, alloc); + p.stream = (uint8 *) data; + p.stream_end = (uint8 *) data + len; + p.stream_start = (uint8 *) p.stream; + p.stream_len = len; + p.push_mode = FALSE; + if (start_decoder(&p)) { + f = vorbis_alloc(&p); + if (f) { + *f = p; + vorbis_pump_first_frame(f); + return f; + } + } + if (error) *error = p.error; + vorbis_deinit(&p); + return NULL; +} + +#ifndef STB_VORBIS_NO_INTEGER_CONVERSION +#define PLAYBACK_MONO 1 +#define PLAYBACK_LEFT 2 +#define PLAYBACK_RIGHT 4 + +#define L (PLAYBACK_LEFT | PLAYBACK_MONO) +#define C (PLAYBACK_LEFT | PLAYBACK_RIGHT | PLAYBACK_MONO) +#define R (PLAYBACK_RIGHT | PLAYBACK_MONO) + +static int8 channel_position[7][6] = +{ + { 0 }, + { C }, + { L, R }, + { L, C, R }, + { L, R, L, R }, + { L, C, R, L, R }, + { L, C, R, L, R, C }, +}; + + +#ifndef STB_VORBIS_NO_FAST_SCALED_FLOAT + typedef union { + float f; + int i; + } float_conv; + typedef char stb_vorbis_float_size_test[sizeof(float)==4 && sizeof(int) == 4]; + #define FASTDEF(x) float_conv x + // add (1<<23) to convert to int, then divide by 2^SHIFT, then add 0.5/2^SHIFT to round + #define MAGIC(SHIFT) (1.5f * (1 << (23-SHIFT)) + 0.5f/(1 << SHIFT)) + #define ADDEND(SHIFT) (((150-SHIFT) << 23) + (1 << 22)) + #define FAST_SCALED_FLOAT_TO_INT(temp,x,s) (temp.f = (x) + MAGIC(s), temp.i - ADDEND(s)) + #define check_endianness() +#else + #define FAST_SCALED_FLOAT_TO_INT(temp,x,s) ((int) ((x) * (1 << (s)))) + #define check_endianness() + #define FASTDEF(x) +#endif + +static void copy_samples(short *dest, float *src, int len) +{ + int i; + check_endianness(); + for (i=0; i < len; ++i) { + FASTDEF(temp); + int v = FAST_SCALED_FLOAT_TO_INT(temp, src[i],15); + if ((unsigned int) (v + 32768) > 65535) + v = v < 0 ? -32768 : 32767; + dest[i] = v; + } +} + +static void compute_samples(int mask, short *output, int num_c, float **data, int d_offset, int len) +{ + #define BUFFER_SIZE 32 + float buffer[BUFFER_SIZE]; + int i,j,o,n = BUFFER_SIZE; + check_endianness(); + for (o = 0; o < len; o += BUFFER_SIZE) { + memset(buffer, 0, sizeof(buffer)); + if (o + n > len) n = len - o; + for (j=0; j < num_c; ++j) { + if (channel_position[num_c][j] & mask) { + for (i=0; i < n; ++i) + buffer[i] += data[j][d_offset+o+i]; + } + } + for (i=0; i < n; ++i) { + FASTDEF(temp); + int v = FAST_SCALED_FLOAT_TO_INT(temp,buffer[i],15); + if ((unsigned int) (v + 32768) > 65535) + v = v < 0 ? -32768 : 32767; + output[o+i] = v; + } + } +} + +static void compute_stereo_samples(short *output, int num_c, float **data, int d_offset, int len) +{ + #define BUFFER_SIZE 32 + float buffer[BUFFER_SIZE]; + int i,j,o,n = BUFFER_SIZE >> 1; + // o is the offset in the source data + check_endianness(); + for (o = 0; o < len; o += BUFFER_SIZE >> 1) { + // o2 is the offset in the output data + int o2 = o << 1; + memset(buffer, 0, sizeof(buffer)); + if (o + n > len) n = len - o; + for (j=0; j < num_c; ++j) { + int m = channel_position[num_c][j] & (PLAYBACK_LEFT | PLAYBACK_RIGHT); + if (m == (PLAYBACK_LEFT | PLAYBACK_RIGHT)) { + for (i=0; i < n; ++i) { + buffer[i*2+0] += data[j][d_offset+o+i]; + buffer[i*2+1] += data[j][d_offset+o+i]; + } + } else if (m == PLAYBACK_LEFT) { + for (i=0; i < n; ++i) { + buffer[i*2+0] += data[j][d_offset+o+i]; + } + } else if (m == PLAYBACK_RIGHT) { + for (i=0; i < n; ++i) { + buffer[i*2+1] += data[j][d_offset+o+i]; + } + } + } + for (i=0; i < (n<<1); ++i) { + FASTDEF(temp); + int v = FAST_SCALED_FLOAT_TO_INT(temp,buffer[i],15); + if ((unsigned int) (v + 32768) > 65535) + v = v < 0 ? -32768 : 32767; + output[o2+i] = v; + } + } +} + +static void convert_samples_short(int buf_c, short **buffer, int b_offset, int data_c, float **data, int d_offset, int samples) +{ + int i; + if (buf_c != data_c && buf_c <= 2 && data_c <= 6) { + static int channel_selector[3][2] = { {0}, {PLAYBACK_MONO}, {PLAYBACK_LEFT, PLAYBACK_RIGHT} }; + for (i=0; i < buf_c; ++i) + compute_samples(channel_selector[buf_c][i], buffer[i]+b_offset, data_c, data, d_offset, samples); + } else { + int limit = buf_c < data_c ? buf_c : data_c; + for (i=0; i < limit; ++i) + copy_samples(buffer[i]+b_offset, data[i]+d_offset, samples); + for ( ; i < buf_c; ++i) + memset(buffer[i]+b_offset, 0, sizeof(short) * samples); + } +} + +int stb_vorbis_get_frame_short(stb_vorbis *f, int num_c, short **buffer, int num_samples) +{ + float **output; + int len = stb_vorbis_get_frame_float(f, NULL, &output); + if (len > num_samples) len = num_samples; + if (len) + convert_samples_short(num_c, buffer, 0, f->channels, output, 0, len); + return len; +} + +static void convert_channels_short_interleaved(int buf_c, short *buffer, int data_c, float **data, int d_offset, int len) +{ + int i; + check_endianness(); + if (buf_c != data_c && buf_c <= 2 && data_c <= 6) { + assert(buf_c == 2); + for (i=0; i < buf_c; ++i) + compute_stereo_samples(buffer, data_c, data, d_offset, len); + } else { + int limit = buf_c < data_c ? buf_c : data_c; + int j; + for (j=0; j < len; ++j) { + for (i=0; i < limit; ++i) { + FASTDEF(temp); + float f = data[i][d_offset+j]; + int v = FAST_SCALED_FLOAT_TO_INT(temp, f,15);//data[i][d_offset+j],15); + if ((unsigned int) (v + 32768) > 65535) + v = v < 0 ? -32768 : 32767; + *buffer++ = v; + } + for ( ; i < buf_c; ++i) + *buffer++ = 0; + } + } +} + +int stb_vorbis_get_frame_short_interleaved(stb_vorbis *f, int num_c, short *buffer, int num_shorts) +{ + float **output; + int len; + if (num_c == 1) return stb_vorbis_get_frame_short(f,num_c,&buffer, num_shorts); + len = stb_vorbis_get_frame_float(f, NULL, &output); + if (len) { + if (len*num_c > num_shorts) len = num_shorts / num_c; + convert_channels_short_interleaved(num_c, buffer, f->channels, output, 0, len); + } + return len; +} + +int stb_vorbis_get_samples_short_interleaved(stb_vorbis *f, int channels, short *buffer, int num_shorts) +{ + float **outputs; + int len = num_shorts / channels; + int n=0; + int z = f->channels; + if (z > channels) z = channels; + while (n < len) { + int k = f->channel_buffer_end - f->channel_buffer_start; + if (n+k >= len) k = len - n; + if (k) + convert_channels_short_interleaved(channels, buffer, f->channels, f->channel_buffers, f->channel_buffer_start, k); + buffer += k*channels; + n += k; + f->channel_buffer_start += k; + if (n == len) break; + if (!stb_vorbis_get_frame_float(f, NULL, &outputs)) break; + } + return n; +} + +int stb_vorbis_get_samples_short(stb_vorbis *f, int channels, short **buffer, int len) +{ + float **outputs; + int n=0; + int z = f->channels; + if (z > channels) z = channels; + while (n < len) { + int k = f->channel_buffer_end - f->channel_buffer_start; + if (n+k >= len) k = len - n; + if (k) + convert_samples_short(channels, buffer, n, f->channels, f->channel_buffers, f->channel_buffer_start, k); + n += k; + f->channel_buffer_start += k; + if (n == len) break; + if (!stb_vorbis_get_frame_float(f, NULL, &outputs)) break; + } + return n; +} + +#ifndef STB_VORBIS_NO_STDIO +int stb_vorbis_decode_filename(const char *filename, int *channels, int *sample_rate, short **output) +{ + int data_len, offset, total, limit, error; + short *data; + stb_vorbis *v = stb_vorbis_open_filename(filename, &error, NULL); + if (v == NULL) return -1; + limit = v->channels * 4096; + *channels = v->channels; + if (sample_rate) + *sample_rate = v->sample_rate; + offset = data_len = 0; + total = limit; + data = (short *) malloc(total * sizeof(*data)); + if (data == NULL) { + stb_vorbis_close(v); + return -2; + } + for (;;) { + int n = stb_vorbis_get_frame_short_interleaved(v, v->channels, data+offset, total-offset); + if (n == 0) break; + data_len += n; + offset += n * v->channels; + if (offset + limit > total) { + short *data2; + total *= 2; + data2 = (short *) realloc(data, total * sizeof(*data)); + if (data2 == NULL) { + free(data); + stb_vorbis_close(v); + return -2; + } + data = data2; + } + } + *output = data; + stb_vorbis_close(v); + return data_len; +} +#endif // NO_STDIO + +int stb_vorbis_decode_memory(const uint8 *mem, int len, int *channels, int *sample_rate, short **output) +{ + int data_len, offset, total, limit, error; + short *data; + stb_vorbis *v = stb_vorbis_open_memory(mem, len, &error, NULL); + if (v == NULL) return -1; + limit = v->channels * 4096; + *channels = v->channels; + if (sample_rate) + *sample_rate = v->sample_rate; + offset = data_len = 0; + total = limit; + data = (short *) malloc(total * sizeof(*data)); + if (data == NULL) { + stb_vorbis_close(v); + return -2; + } + for (;;) { + int n = stb_vorbis_get_frame_short_interleaved(v, v->channels, data+offset, total-offset); + if (n == 0) break; + data_len += n; + offset += n * v->channels; + if (offset + limit > total) { + short *data2; + total *= 2; + data2 = (short *) realloc(data, total * sizeof(*data)); + if (data2 == NULL) { + free(data); + stb_vorbis_close(v); + return -2; + } + data = data2; + } + } + *output = data; + stb_vorbis_close(v); + return data_len; +} +#endif // STB_VORBIS_NO_INTEGER_CONVERSION + +int stb_vorbis_get_samples_float_interleaved(stb_vorbis *f, int channels, float *buffer, int num_floats) +{ + float **outputs; + int len = num_floats / channels; + int n=0; + int z = f->channels; + if (z > channels) z = channels; + while (n < len) { + int i,j; + int k = f->channel_buffer_end - f->channel_buffer_start; + if (n+k >= len) k = len - n; + for (j=0; j < k; ++j) { + for (i=0; i < z; ++i) + *buffer++ = f->channel_buffers[i][f->channel_buffer_start+j]; + for ( ; i < channels; ++i) + *buffer++ = 0; + } + n += k; + f->channel_buffer_start += k; + if (n == len) + break; + if (!stb_vorbis_get_frame_float(f, NULL, &outputs)) + break; + } + return n; +} + +int stb_vorbis_get_samples_float(stb_vorbis *f, int channels, float **buffer, int num_samples) +{ + float **outputs; + int n=0; + int z = f->channels; + if (z > channels) z = channels; + while (n < num_samples) { + int i; + int k = f->channel_buffer_end - f->channel_buffer_start; + if (n+k >= num_samples) k = num_samples - n; + if (k) { + for (i=0; i < z; ++i) + memcpy(buffer[i]+n, f->channel_buffers[i]+f->channel_buffer_start, sizeof(float)*k); + for ( ; i < channels; ++i) + memset(buffer[i]+n, 0, sizeof(float) * k); + } + n += k; + f->channel_buffer_start += k; + if (n == num_samples) + break; + if (!stb_vorbis_get_frame_float(f, NULL, &outputs)) + break; + } + return n; +} +#endif // STB_VORBIS_NO_PULLDATA_API + +/* Version history + 1.09 - 2016/04/04 - back out 'avoid discarding last frame' fix from previous version + 1.08 - 2016/04/02 - fixed multiple warnings; fix setup memory leaks; + avoid discarding last frame of audio data + 1.07 - 2015/01/16 - fixed some warnings, fix mingw, const-correct API + some more crash fixes when out of memory or with corrupt files + 1.06 - 2015/08/31 - full, correct support for seeking API (Dougall Johnson) + some crash fixes when out of memory or with corrupt files + 1.05 - 2015/04/19 - don't define __forceinline if it's redundant + 1.04 - 2014/08/27 - fix missing const-correct case in API + 1.03 - 2014/08/07 - Warning fixes + 1.02 - 2014/07/09 - Declare qsort compare function _cdecl on windows + 1.01 - 2014/06/18 - fix stb_vorbis_get_samples_float + 1.0 - 2014/05/26 - fix memory leaks; fix warnings; fix bugs in multichannel + (API change) report sample rate for decode-full-file funcs + 0.99996 - bracket #include for macintosh compilation by Laurent Gomila + 0.99995 - use union instead of pointer-cast for fast-float-to-int to avoid alias-optimization problem + 0.99994 - change fast-float-to-int to work in single-precision FPU mode, remove endian-dependence + 0.99993 - remove assert that fired on legal files with empty tables + 0.99992 - rewind-to-start + 0.99991 - bugfix to stb_vorbis_get_samples_short by Bernhard Wodo + 0.9999 - (should have been 0.99990) fix no-CRT support, compiling as C++ + 0.9998 - add a full-decode function with a memory source + 0.9997 - fix a bug in the read-from-FILE case in 0.9996 addition + 0.9996 - query length of vorbis stream in samples/seconds + 0.9995 - bugfix to another optimization that only happened in certain files + 0.9994 - bugfix to one of the optimizations that caused significant (but inaudible?) errors + 0.9993 - performance improvements; runs in 99% to 104% of time of reference implementation + 0.9992 - performance improvement of IMDCT; now performs close to reference implementation + 0.9991 - performance improvement of IMDCT + 0.999 - (should have been 0.9990) performance improvement of IMDCT + 0.998 - no-CRT support from Casey Muratori + 0.997 - bugfixes for bugs found by Terje Mathisen + 0.996 - bugfix: fast-huffman decode initialized incorrectly for sparse codebooks; fixing gives 10% speedup - found by Terje Mathisen + 0.995 - bugfix: fix to 'effective' overrun detection - found by Terje Mathisen + 0.994 - bugfix: garbage decode on final VQ symbol of a non-multiple - found by Terje Mathisen + 0.993 - bugfix: pushdata API required 1 extra byte for empty page (failed to consume final page if empty) - found by Terje Mathisen + 0.992 - fixes for MinGW warning + 0.991 - turn fast-float-conversion on by default + 0.990 - fix push-mode seek recovery if you seek into the headers + 0.98b - fix to bad release of 0.98 + 0.98 - fix push-mode seek recovery; robustify float-to-int and support non-fast mode + 0.97 - builds under c++ (typecasting, don't use 'class' keyword) + 0.96 - somehow MY 0.95 was right, but the web one was wrong, so here's my 0.95 rereleased as 0.96, fixes a typo in the clamping code + 0.95 - clamping code for 16-bit functions + 0.94 - not publically released + 0.93 - fixed all-zero-floor case (was decoding garbage) + 0.92 - fixed a memory leak + 0.91 - conditional compiles to omit parts of the API and the infrastructure to support them: STB_VORBIS_NO_PULLDATA_API, STB_VORBIS_NO_PUSHDATA_API, STB_VORBIS_NO_STDIO, STB_VORBIS_NO_INTEGER_CONVERSION + 0.90 - first public release +*/ + +#endif // STB_VORBIS_HEADER_ONLY diff --git a/impeller/third_party/stb/stb/stb_voxel_render.h b/impeller/third_party/stb/stb/stb_voxel_render.h new file mode 100644 index 0000000000000..c49aa400aa12e --- /dev/null +++ b/impeller/third_party/stb/stb/stb_voxel_render.h @@ -0,0 +1,3752 @@ +// stb_voxel_render.h - v0.84 - Sean Barrett, 2015 - public domain +// +// This library helps render large-scale "voxel" worlds for games, +// in this case, one with blocks that can have textures and that +// can also be a few shapes other than cubes. +// +// Video introduction: +// http://www.youtube.com/watch?v=2vnTtiLrV1w +// +// Minecraft-viewer sample app (not very simple though): +// http://github.com/nothings/stb/tree/master/tests/caveview +// +// It works by creating triangle meshes. The library includes +// +// - converter from dense 3D arrays of block info to vertex mesh +// - shader for the vertex mesh +// - assistance in setting up shader state +// +// For portability, none of the library code actually accesses +// the 3D graphics API. (At the moment, it's not actually portable +// since the shaders are GLSL only, but patches are welcome.) +// +// You have to do all the caching and tracking of vertex buffers +// yourself. However, you could also try making a game with +// a small enough world that it's fully loaded rather than +// streaming. Currently the preferred vertex format is 20 bytes +// per quad. There are plans to allow much more compact formats +// with a slight reduction in shader features. +// +// +// USAGE +// +// #define the symbol STB_VOXEL_RENDER_IMPLEMENTATION in *one* +// C/C++ file before the #include of this file; the implementation +// will be generated in that file. +// +// If you define the symbols STB_VOXEL_RENDER_STATIC, then the +// implementation will be private to that file. +// +// +// FEATURES +// +// - you can choose textured blocks with the features below, +// or colored voxels with 2^24 colors and no textures. +// +// - voxels are mostly just cubes, but there's support for +// half-height cubes and diagonal slopes, half-height +// diagonals, and even odder shapes especially for doing +// more-continuous "ground". +// +// - texture coordinates are projections along one of the major +// axes, with the per-texture scaling. +// +// - a number of aspects of the shader and the vertex format +// are configurable; the library generally takes care of +// coordinating the vertex format with the mesh for you. +// +// +// FEATURES (SHADER PERSPECTIVE) +// +// - vertices aligned on integer lattice, z on multiples of 0.5 +// - per-vertex "lighting" or "ambient occlusion" value (6 bits) +// - per-vertex texture crossfade (3 bits) +// +// - per-face texture #1 id (8-bit index into array texture) +// - per-face texture #2 id (8-bit index into second array texture) +// - per-face color (6-bit palette index, 2 bits of per-texture boolean enable) +// - per-face 5-bit normal for lighting calculations & texture coord computation +// - per-face 2-bit texture matrix rotation to rotate faces +// +// - indexed-by-texture-id scale factor (separate for texture #1 and texture #2) +// - indexed-by-texture-#2-id blend mode (alpha composite or modulate/multiply); +// the first is good for decals, the second for detail textures, "light maps", +// etc; both modes are controlled by texture #2's alpha, scaled by the +// per-vertex texture crossfade and the per-face color (if enabled on texture #2); +// modulate/multiply multiplies by an extra factor of 2.0 so that if you +// make detail maps whose average brightness is 0.5 everything works nicely. +// +// - ambient lighting: half-lambert directional plus constant, all scaled by vertex ao +// - face can be fullbright (emissive), controlled by per-face color +// - installable lighting, with default single-point-light +// - installable fog, with default hacked smoothstep +// +// Note that all the variations of lighting selection and texture +// blending are run-time conditions in the shader, so they can be +// intermixed in a single mesh. +// +// +// INTEGRATION ARC +// +// The way to get this library to work from scratch is to do the following: +// +// Step 1. define STBVOX_CONFIG_MODE to 0 +// +// This mode uses only vertex attributes and uniforms, and is easiest +// to get working. It requires 32 bytes per quad and limits the +// size of some tables to avoid hitting uniform limits. +// +// Step 2. define STBVOX_CONFIG_MODE to 1 +// +// This requires using a texture buffer to store the quad data, +// reducing the size to 20 bytes per quad. +// +// Step 3: define STBVOX_CONFIG_PREFER_TEXBUFFER +// +// This causes some uniforms to be stored as texture buffers +// instead. This increases the size of some of those tables, +// and avoids a potential slow path (gathering non-uniform +// data from uniforms) on some hardware. +// +// In the future I hope to add additional modes that have significantly +// smaller meshes but reduce features, down as small as 6 bytes per quad. +// See elsewhere in this file for a table of candidate modes. Switching +// to a mode will require changing some of your mesh creation code, but +// everything else should be seamless. (And I'd like to change the API +// so that mesh creation is data-driven the way the uniforms are, and +// then you wouldn't even have to change anything but the mode number.) +// +// +// IMPROVEMENTS FOR SHIP-WORTHY PROGRAMS USING THIS LIBRARY +// +// I currently tolerate a certain level of "bugginess" in this library. +// +// I'm referring to things which look a little wrong (as long as they +// don't cause holes or cracks in the output meshes), or things which +// do not produce as optimal a mesh as possible. Notable examples: +// +// - incorrect lighting on slopes +// - inefficient meshes for vheight blocks +// +// I am willing to do the work to improve these things if someone is +// going to ship a substantial program that would be improved by them. +// (It need not be commercial, nor need it be a game.) I just didn't +// want to do the work up front if it might never be leveraged. So just +// submit a bug report as usual (github is preferred), but add a note +// that this is for a thing that is really going to ship. (That means +// you need to be far enough into the project that it's clear you're +// committed to it; not during early exploratory development.) +// +// +// VOXEL MESH API +// +// Context +// +// To understand the API, make sure you first understand the feature set +// listed above. +// +// Because the vertices are compact, they have very limited spatial +// precision. Thus a single mesh can only contain the data for a limited +// area. To make very large voxel maps, you'll need to build multiple +// vertex buffers. (But you want this anyway for frustum culling.) +// +// Each generated mesh has three components: +// - vertex data (vertex buffer) +// - face data (optional, stored in texture buffer) +// - mesh transform (uniforms) +// +// Once you've generated the mesh with this library, it's up to you +// to upload it to the GPU, to keep track of the state, and to render +// it. +// +// Concept +// +// The basic design is that you pass in one or more 3D arrays; each array +// is (typically) one-byte-per-voxel and contains information about one +// or more properties of some particular voxel property. +// +// Because there is so much per-vertex and per-face data possible +// in the output, and each voxel can have 6 faces and 8 vertices, it +// would require an very large data structure to describe all +// of the possibilities, and this would cause the mesh-creation +// process to be slow. Instead, the API provides multiple ways +// to express each property, some more compact, others less so; +// each such way has some limitations on what it can express. +// +// Note that there are so many paths and combinations, not all of them +// have been tested. Just report bugs and I'll fix 'em. +// +// Details +// +// See the API documentation in the header-file section. +// +// +// CONTRIBUTORS +// +// Features Porting Bugfixes & Warnings +// Sean Barrett github:r-leyh Jesus Fernandez +// Miguel Lechon github:Arbeiterunfallversicherungsgesetz +// Thomas Frase James Hofmann +// Stephen Olsen +// +// VERSION HISTORY +// +// 0.84 (2016-04-02) fix GLSL syntax error on glModelView path +// 0.83 (2015-09-13) remove non-constant struct initializers to support more compilers +// 0.82 (2015-08-01) added input.packed_compact to store rot, vheight & texlerp efficiently +// fix broken tex_overlay2 +// 0.81 (2015-05-28) fix broken STBVOX_CONFIG_OPTIMIZED_VHEIGHT +// 0.80 (2015-04-11) fix broken STBVOX_CONFIG_ROTATION_IN_LIGHTING refactoring +// change STBVOX_MAKE_LIGHTING to STBVOX_MAKE_LIGHTING_EXT so +// that header defs don't need to see config vars +// add STBVOX_CONFIG_VHEIGHT_IN_LIGHTING and other vheight fixes +// added documentation for vheight ("weird slopes") +// 0.79 (2015-04-01) fix the missing types from 0.78; fix string constants being const +// 0.78 (2015-04-02) bad "#else", compile as C++ +// 0.77 (2015-04-01) documentation tweaks, rename config var to STB_VOXEL_RENDER_STATIC +// 0.76 (2015-04-01) typos, signed/unsigned shader issue, more documentation +// 0.75 (2015-04-01) initial release +// +// +// HISTORICAL FOUNDATION +// +// stb_voxel_render 20-byte quads 2015/01 +// zmc engine 32-byte quads 2013/12 +// zmc engine 96-byte quads 2011/10 +// +// +// LICENSE +// +// This software is dual-licensed to the public domain and under the following +// license: you are granted a perpetual, irrevocable license to copy, modify, +// publish, and distribute this file as you see fit. + +#ifndef INCLUDE_STB_VOXEL_RENDER_H +#define INCLUDE_STB_VOXEL_RENDER_H + +#include + +typedef struct stbvox_mesh_maker stbvox_mesh_maker; +typedef struct stbvox_input_description stbvox_input_description; + +#ifdef STB_VOXEL_RENDER_STATIC +#define STBVXDEC static +#else +#define STBVXDEC extern +#endif + +#ifdef __cplusplus +extern "C" { +#endif + +////////////////////////////////////////////////////////////////////////////// +// +// CONFIGURATION MACROS +// +// #define STBVOX_CONFIG_MODE // REQUIRED +// Configures the overall behavior of stb_voxel_render. This +// can affect the shaders, the uniform info, and other things. +// (If you need more than one mode in the same app, you can +// use STB_VOXEL_RENDER_STATIC to create multiple versions +// in separate files, and then wrap them.) +// +// Mode value Meaning +// 0 Textured blocks, 32-byte quads +// 1 Textured blocks, 20-byte quads +// 20 Untextured blocks, 32-byte quads +// 21 Untextured blocks, 20-byte quads +// +// +// #define STBVOX_CONFIG_PRECISION_Z // OPTIONAL +// Defines the number of bits of fractional position for Z. +// Only 0 or 1 are valid. 1 is the default. If 0, then a +// single mesh has twice the legal Z range; e.g. in +// modes 0,1,20,21, Z in the mesh can extend to 511 instead +// of 255. However, half-height blocks cannot be used. +// +// All of the following just #ifdef tested so need no values, and are optional. +// +// STBVOX_CONFIG_BLOCKTYPE_SHORT +// use unsigned 16-bit values for 'blocktype' in the input instead of 8-bit values +// +// STBVOX_CONFIG_OPENGL_MODELVIEW +// use the gl_ModelView matrix rather than the explicit uniform +// +// STBVOX_CONFIG_HLSL +// NOT IMPLEMENTED! Define HLSL shaders instead of GLSL shaders +// +// STBVOX_CONFIG_PREFER_TEXBUFFER +// Stores many of the uniform arrays in texture buffers intead, +// so they can be larger and may be more efficient on some hardware. +// +// STBVOX_CONFIG_LIGHTING_SIMPLE +// Creates a simple lighting engine with a single point light source +// in addition to the default half-lambert ambient light. +// +// STBVOX_CONFIG_LIGHTING +// Declares a lighting function hook; you must append a lighting function +// to the shader before compiling it: +// vec3 compute_lighting(vec3 pos, vec3 norm, vec3 albedo, vec3 ambient); +// 'ambient' is the half-lambert ambient light with vertex ambient-occlusion applied +// +// STBVOX_CONFIG_FOG_SMOOTHSTEP +// Defines a simple unrealistic fog system designed to maximize +// unobscured view distance while not looking too weird when things +// emerge from the fog. Configured using an extra array element +// in the STBVOX_UNIFORM_ambient uniform. +// +// STBVOX_CONFIG_FOG +// Defines a fog function hook; you must append a fog function to +// the shader before compiling it: +// vec3 compute_fog(vec3 color, vec3 relative_pos, float fragment_alpha); +// "color" is the incoming pre-fogged color, fragment_alpha is the alpha value, +// and relative_pos is the vector from the point to the camera in worldspace +// +// STBVOX_CONFIG_DISABLE_TEX2 +// This disables all processing of texture 2 in the shader in case +// you don't use it. Eventually this will be replaced with a mode +// that omits the unused data entirely. +// +// STBVOX_CONFIG_TEX1_EDGE_CLAMP +// STBVOX_CONFIG_TEX2_EDGE_CLAMP +// If you want to edge clamp the textures, instead of letting them wrap, +// set this flag. By default stb_voxel_render relies on texture wrapping +// to simplify texture coordinate generation. This flag forces it to do +// it correctly, although there can still be minor artifacts. +// +// STBVOX_CONFIG_ROTATION_IN_LIGHTING +// Changes the meaning of the 'lighting' mesher input variable to also +// store the rotation; see later discussion. +// +// STBVOX_CONFIG_VHEIGHT_IN_LIGHTING +// Changes the meaning of the 'lighting' mesher input variable to also +// store the vheight; see later discussion. Cannot use both this and +// the previous variable. +// +// STBVOX_CONFIG_PREMULTIPLIED_ALPHA +// Adjusts the shader calculations on the assumption that tex1.rgba, +// tex2.rgba, and color.rgba all use premultiplied values, and that +// the output of the fragment shader should be premultiplied. +// +// STBVOX_CONFIG_UNPREMULTIPLY +// Only meaningful if STBVOX_CONFIG_PREMULTIPLIED_ALPHA is defined. +// Changes the behavior described above so that the inputs are +// still premultiplied alpha, but the output of the fragment +// shader is not premultiplied alpha. This is needed when allowing +// non-unit alpha values but not doing alpha-blending (for example +// when alpha testing). +// + +////////////////////////////////////////////////////////////////////////////// +// +// MESHING +// +// A mesh represents a (typically) small chunk of a larger world. +// Meshes encode coordinates using small integers, so those +// coordinates must be relative to some base location. +// All of the coordinates in the functions below use +// these relative coordinates unless explicitly stated +// otherwise. +// +// Input to the meshing step is documented further down + +STBVXDEC void stbvox_init_mesh_maker(stbvox_mesh_maker *mm); +// Call this function to initialize a mesh-maker context structure +// used to build meshes. You should have one context per thread +// that's building meshes. + +STBVXDEC void stbvox_set_buffer(stbvox_mesh_maker *mm, int mesh, int slot, void *buffer, size_t len); +// Call this to set the buffer into which stbvox will write the mesh +// it creates. It can build more than one mesh in parallel (distinguished +// by the 'mesh' parameter), and each mesh can be made up of more than +// one buffer (distinguished by the 'slot' parameter). +// +// Multiple meshes are under your control; use the 'selector' input +// variable to choose which mesh each voxel's vertices are written to. +// For example, you can use this to generate separate meshes for opaque +// and transparent data. +// +// You can query the number of slots by calling stbvox_get_buffer_count +// described below. The meaning of the buffer for each slot depends +// on STBVOX_CONFIG_MODE. +// +// In mode 0 & mode 20, there is only one slot. The mesh data for that +// slot is two interleaved vertex attributes: attr_vertex, a single +// 32-bit uint, and attr_face, a single 32-bit uint. +// +// In mode 1 & mode 21, there are two slots. The first buffer should +// be four times as large as the second buffer. The first buffer +// contains a single vertex attribute: 'attr_vertex', a single 32-bit uint. +// The second buffer contains texture buffer data (an array of 32-bit uints) +// that will be accessed through the sampler identified by STBVOX_UNIFORM_face_data. + +STBVXDEC int stbvox_get_buffer_count(stbvox_mesh_maker *mm); +// Returns the number of buffers needed per mesh as described above. + +STBVXDEC int stbvox_get_buffer_size_per_quad(stbvox_mesh_maker *mm, int slot); +// Returns how much of a given buffer will get used per quad. This +// allows you to choose correct relative sizes for each buffer, although +// the values are fixed based on the configuration you've selected at +// compile time, and the details are described in stbvox_set_buffer. + +STBVXDEC void stbvox_set_default_mesh(stbvox_mesh_maker *mm, int mesh); +// Selects which mesh the mesher will output to (see previous function) +// if the input doesn't specify a per-voxel selector. (I doubt this is +// useful, but it's here just in case.) + +STBVXDEC stbvox_input_description *stbvox_get_input_description(stbvox_mesh_maker *mm); +// This function call returns a pointer to the stbvox_input_description part +// of stbvox_mesh_maker (which you should otherwise treat as opaque). You +// zero this structure, then fill out the relevant pointers to the data +// describing your voxel object/world. +// +// See further documentation at the description of stbvox_input_description below. + +STBVXDEC void stbvox_set_input_stride(stbvox_mesh_maker *mm, int x_stride_in_elements, int y_stride_in_elements); +// This sets the stride between successive elements of the 3D arrays +// in the stbvox_input_description. Z values are always stored consecutively. +// (The preferred coordinate system for stbvox is X right, Y forwards, Z up.) + +STBVXDEC void stbvox_set_input_range(stbvox_mesh_maker *mm, int x0, int y0, int z0, int x1, int y1, int z1); +// This sets the range of values in the 3D array for the voxels that +// the mesh generator will convert. The lower values are inclusive, +// the higher values are exclusive, so (0,0,0) to (16,16,16) generates +// mesh data associated with voxels up to (15,15,15) but no higher. +// +// The mesh generate generates faces at the boundary between open space +// and solid space but associates them with the solid space, so if (15,0,0) +// is open and (16,0,0) is solid, then the mesh will contain the boundary +// between them if x0 <= 16 and x1 > 16. +// +// Note that the mesh generator will access array elements 1 beyond the +// limits set in these parameters. For example, if you set the limits +// to be (0,0,0) and (16,16,16), then the generator will access all of +// the voxels between (-1,-1,-1) and (16,16,16), including (16,16,16). +// You may have to do pointer arithmetic to make it work. +// +// For example, caveview processes mesh chunks that are 32x32x16, but it +// does this using input buffers that are 34x34x18. +// +// The lower limits are x0 >= 0, y0 >= 0, and z0 >= 0. +// +// The upper limits are mode dependent, but all the current methods are +// limited to x1 < 127, y1 < 127, z1 < 255. Note that these are not +// powers of two; if you want to use power-of-two chunks (to make +// it efficient to decide which chunk a coordinate falls in), you're +// limited to at most x1=64, y1=64, z1=128. For classic Minecraft-style +// worlds with limited vertical extent, I recommend using a single +// chunk for the entire height, which limits the height to 255 blocks +// (one less than Minecraft), and only chunk the map in X & Y. + +STBVXDEC int stbvox_make_mesh(stbvox_mesh_maker *mm); +// Call this function to create mesh data for the currently configured +// set of input data. This appends to the currently configured mesh output +// buffer. Returns 1 on success. If there is not enough room in the buffer, +// it outputs as much as it can, and returns 0; you need to switch output +// buffers (either by calling stbvox_set_buffer to set new buffers, or +// by copying the data out and calling stbvox_reset_buffers), and then +// call this function again without changing any of the input parameters. +// +// Note that this function appends; you can call it multiple times to +// build a single mesh. For example, caveview uses chunks that are +// 32x32x255, but builds the mesh for it by processing 32x32x16 at atime +// (this is faster as it is reuses the same 34x34x18 input buffers rather +// than needing 34x34x257 input buffers). + +// Once you're done creating a mesh into a given buffer, +// consider the following functions: + +STBVXDEC int stbvox_get_quad_count(stbvox_mesh_maker *mm, int mesh); +// Returns the number of quads in the mesh currently generated by mm. +// This is the sum of all consecutive stbvox_make_mesh runs appending +// to the same buffer. 'mesh' distinguishes between the multiple user +// meshes available via 'selector' or stbvox_set_default_mesh. +// +// Typically you use this function when you're done building the mesh +// and want to record how to draw it. +// +// Note that there are no index buffers; the data stored in the buffers +// should be drawn as quads (e.g. with GL_QUAD); if your API does not +// support quads, you can create a single index buffer large enough to +// draw your largest vertex buffer, and reuse it for every rendering. +// (Note that if you use 32-bit indices, you'll use 24 bytes of bandwidth +// per quad, more than the 20 bytes for the vertex/face mesh data.) + +STBVXDEC void stbvox_set_mesh_coordinates(stbvox_mesh_maker *mm, int x, int y, int z); +// Sets the global coordinates for this chunk, such that (0,0,0) relative +// coordinates will be at (x,y,z) in global coordinates. + +STBVXDEC void stbvox_get_bounds(stbvox_mesh_maker *mm, float bounds[2][3]); +// Returns the bounds for the mesh in global coordinates. Use this +// for e.g. frustum culling the mesh. @BUG: this just uses the +// values from stbvox_set_input_range(), so if you build by +// appending multiple values, this will be wrong, and you need to +// set stbvox_set_input_range() to the full size. Someday this +// will switch to tracking the actual bounds of the *mesh*, though. + +STBVXDEC void stbvox_get_transform(stbvox_mesh_maker *mm, float transform[3][3]); +// Returns the 'transform' data for the shader uniforms. It is your +// job to set this to the shader before drawing the mesh. It is the +// only uniform that needs to change per-mesh. Note that it is not +// a 3x3 matrix, but rather a scale to decode fixed point numbers as +// floats, a translate from relative to global space, and a special +// translation for texture coordinate generation that avoids +// floating-point precision issues. @TODO: currently we add the +// global translation to the vertex, than multiply by modelview, +// but this means if camera location and vertex are far from the +// origin, we lose precision. Need to make a special modelview with +// the translation (or some of it) factored out to avoid this. + +STBVXDEC void stbvox_reset_buffers(stbvox_mesh_maker *mm); +// Call this function if you're done with the current output buffer +// but want to reuse it (e.g. you're done appending with +// stbvox_make_mesh and you've copied the data out to your graphics API +// so can reuse the buffer). + +////////////////////////////////////////////////////////////////////////////// +// +// RENDERING +// + +STBVXDEC char *stbvox_get_vertex_shader(void); +// Returns the (currently GLSL-only) vertex shader. + +STBVXDEC char *stbvox_get_fragment_shader(void); +// Returns the (currently GLSL-only) fragment shader. +// You can override the lighting and fogging calculations +// by appending data to the end of these; see the #define +// documentation for more information. + +STBVXDEC char *stbvox_get_fragment_shader_alpha_only(void); +// Returns a slightly cheaper fragment shader that computes +// alpha but not color. This is useful for e.g. a depth-only +// pass when using alpha test. + +typedef struct stbvox_uniform_info stbvox_uniform_info; + +STBVXDEC int stbvox_get_uniform_info(stbvox_uniform_info *info, int uniform); +// Gets the information about a uniform necessary for you to +// set up each uniform with a minimal amount of explicit code. +// See the sample code after the structure definition for stbvox_uniform_info, +// further down in this header section. +// +// "uniform" is from the list immediately following. For many +// of these, default values are provided which you can set. +// Most values are shared for most draw calls; e.g. for stateful +// APIs you can set most of the state only once. Only +// STBVOX_UNIFORM_transform needs to change per draw call. +// +// STBVOX_UNIFORM_texscale +// 64- or 128-long vec4 array. (128 only if STBVOX_CONFIG_PREFER_TEXBUFFER) +// x: scale factor to apply to texture #1. must be a power of two. 1.0 means 'face-sized' +// y: scale factor to apply to texture #2. must be a power of two. 1.0 means 'face-sized' +// z: blend mode indexed by texture #2. 0.0 is alpha compositing; 1.0 is multiplication. +// w: unused currently. @TODO use to support texture animation? +// +// Texscale is indexed by the bottom 6 or 7 bits of the texture id; thus for +// example the texture at index 0 in the array and the texture in index 128 of +// the array must be scaled the same. This means that if you only have 64 or 128 +// unique textures, they all get distinct values anyway; otherwise you have +// to group them in pairs or sets of four. +// +// STBVOX_UNIFORM_ambient +// 4-long vec4 array: +// ambient[0].xyz - negative of direction of a directional light for half-lambert +// ambient[1].rgb - color of light scaled by NdotL (can be negative) +// ambient[2].rgb - constant light added to above calculation; +// effectively light ranges from ambient[2]-ambient[1] to ambient[2]+ambient[1] +// ambient[3].rgb - fog color for STBVOX_CONFIG_FOG_SMOOTHSTEP +// ambient[3].a - reciprocal of squared distance of farthest fog point (viewing distance) + + + // +----- has a default value + // | +-- you should always use the default value +enum // V V +{ // ------------------------------------------------ + STBVOX_UNIFORM_face_data, // n the sampler with the face texture buffer + STBVOX_UNIFORM_transform, // n the transform data from stbvox_get_transform + STBVOX_UNIFORM_tex_array, // n an array of two texture samplers containing the two texture arrays + STBVOX_UNIFORM_texscale, // Y a table of texture properties, see above + STBVOX_UNIFORM_color_table, // Y 64 vec4 RGBA values; a default palette is provided; if A > 1.0, fullbright + STBVOX_UNIFORM_normals, // Y Y table of normals, internal-only + STBVOX_UNIFORM_texgen, // Y Y table of texgen vectors, internal-only + STBVOX_UNIFORM_ambient, // n lighting & fog info, see above + STBVOX_UNIFORM_camera_pos, // Y camera position in global voxel space (for lighting & fog) + + STBVOX_UNIFORM_count, +}; + +enum +{ + STBVOX_UNIFORM_TYPE_none, + STBVOX_UNIFORM_TYPE_sampler, + STBVOX_UNIFORM_TYPE_vec2, + STBVOX_UNIFORM_TYPE_vec3, + STBVOX_UNIFORM_TYPE_vec4, +}; + +struct stbvox_uniform_info +{ + int type; // which type of uniform + int bytes_per_element; // the size of each uniform array element (e.g. vec3 = 12 bytes) + int array_length; // length of the uniform array + char *name; // name in the shader @TODO use numeric binding + float *default_value; // if not NULL, you can use this as the uniform pointer + int use_tex_buffer; // if true, then the uniform is a sampler but the data can come from default_value +}; + +////////////////////////////////////////////////////////////////////////////// +// +// Uniform sample code +// + +#if 0 +// Run this once per frame before drawing all the meshes. +// You still need to separately set the 'transform' uniform for every mesh. +void setup_uniforms(GLuint shader, float camera_pos[4], GLuint tex1, GLuint tex2) +{ + int i; + glUseProgram(shader); // so uniform binding works + for (i=0; i < STBVOX_UNIFORM_count; ++i) { + stbvox_uniform_info sui; + if (stbvox_get_uniform_info(&sui, i)) { + GLint loc = glGetUniformLocation(shader, sui.name); + if (loc != 0) { + switch (i) { + case STBVOX_UNIFORM_camera_pos: // only needed for fog + glUniform4fv(loc, sui.array_length, camera_pos); + break; + + case STBVOX_UNIFORM_tex_array: { + GLuint tex_unit[2] = { 0, 1 }; // your choice of samplers + glUniform1iv(loc, 2, tex_unit); + + glActiveTexture(GL_TEXTURE0 + tex_unit[0]); glBindTexture(GL_TEXTURE_2D_ARRAY, tex1); + glActiveTexture(GL_TEXTURE0 + tex_unit[1]); glBindTexture(GL_TEXTURE_2D_ARRAY, tex2); + glActiveTexture(GL_TEXTURE0); // reset to default + break; + } + + case STBVOX_UNIFORM_face_data: + glUniform1i(loc, SAMPLER_YOU_WILL_BIND_PER_MESH_FACE_DATA_TO); + break; + + case STBVOX_UNIFORM_ambient: // you definitely want to override this + case STBVOX_UNIFORM_color_table: // you might want to override this + case STBVOX_UNIFORM_texscale: // you may want to override this + glUniform4fv(loc, sui.array_length, sui.default_value); + break; + + case STBVOX_UNIFORM_normals: // you never want to override this + case STBVOX_UNIFORM_texgen: // you never want to override this + glUniform3fv(loc, sui.array_length, sui.default_value); + break; + } + } + } + } +} +#endif + +#ifdef __cplusplus +} +#endif + +////////////////////////////////////////////////////////////////////////////// +// +// INPUT TO MESHING +// + +// Shapes of blocks that aren't always cubes +enum +{ + STBVOX_GEOM_empty, + STBVOX_GEOM_knockout, // creates a hole in the mesh + STBVOX_GEOM_solid, + STBVOX_GEOM_transp, // solid geometry, but transparent contents so neighbors generate normally, unless same blocktype + + // following 4 can be represented by vheight as well + STBVOX_GEOM_slab_upper, + STBVOX_GEOM_slab_lower, + STBVOX_GEOM_floor_slope_north_is_top, + STBVOX_GEOM_ceil_slope_north_is_bottom, + + STBVOX_GEOM_floor_slope_north_is_top_as_wall_UNIMPLEMENTED, // same as floor_slope above, but uses wall's texture & texture projection + STBVOX_GEOM_ceil_slope_north_is_bottom_as_wall_UNIMPLEMENTED, + STBVOX_GEOM_crossed_pair, // corner-to-corner pairs, with normal vector bumped upwards + STBVOX_GEOM_force, // like GEOM_transp, but faces visible even if neighbor is same type, e.g. minecraft fancy leaves + + // these access vheight input + STBVOX_GEOM_floor_vheight_03 = 12, // diagonal is SW-NE + STBVOX_GEOM_floor_vheight_12, // diagonal is SE-NW + STBVOX_GEOM_ceil_vheight_03, + STBVOX_GEOM_ceil_vheight_12, + + STBVOX_GEOM_count, // number of geom cases +}; + +enum +{ + STBVOX_FACE_east, + STBVOX_FACE_north, + STBVOX_FACE_west, + STBVOX_FACE_south, + STBVOX_FACE_up, + STBVOX_FACE_down, + + STBVOX_FACE_count, +}; + +#ifdef STBVOX_CONFIG_BLOCKTYPE_SHORT +typedef unsigned short stbvox_block_type; +#else +typedef unsigned char stbvox_block_type; +#endif + +// 24-bit color +typedef struct +{ + unsigned char r,g,b; +} stbvox_rgb; + +#define STBVOX_COLOR_TEX1_ENABLE 64 +#define STBVOX_COLOR_TEX2_ENABLE 128 + +// This is the data structure you fill out. Most of the arrays can be +// NULL, except when one is required to get the value to index another. +// +// The compass system used in the following descriptions is: +// east means increasing x +// north means increasing y +// up means increasing z +struct stbvox_input_description +{ + unsigned char lighting_at_vertices; + // The default is lighting values (i.e. ambient occlusion) are at block + // center, and the vertex light is gathered from those adjacent block + // centers that the vertex is facing. This makes smooth lighting + // consistent across adjacent faces with the same orientation. + // + // Setting this flag to non-zero gives you explicit control + // of light at each vertex, but now the lighting/ao will be + // shared by all vertices at the same point, even if they + // have different normals. + + // these are mostly 3D maps you use to define your voxel world, using x_stride and y_stride + // note that for cache efficiency, you want to use the block_foo palettes as much as possible instead + + stbvox_rgb *rgb; + // Indexed by 3D coordinate. + // 24-bit voxel color for STBVOX_CONFIG_MODE = 20 or 21 only + + unsigned char *lighting; + // Indexed by 3D coordinate. The lighting value / ambient occlusion + // value that is used to define the vertex lighting values. + // The raw lighting values are defined at the center of blocks + // (or at vertex if 'lighting_at_vertices' is true). + // + // If the macro STBVOX_CONFIG_ROTATION_IN_LIGHTING is defined, + // then an additional 2-bit block rotation value is stored + // in this field as well. + // + // Encode with STBVOX_MAKE_LIGHTING_EXT(lighting,rot)--here + // 'lighting' should still be 8 bits, as the macro will + // discard the bottom bits automatically. Similarly, if + // using STBVOX_CONFIG_VHEIGHT_IN_LIGHTING, encode with + // STBVOX_MAKE_LIGHTING_EXT(lighting,vheight). + // + // (Rationale: rotation needs to be independent of blocktype, + // but is only 2 bits so doesn't want to be its own array. + // Lighting is the one thing that was likely to already be + // in use and that I could easily steal 2 bits from.) + + stbvox_block_type *blocktype; + // Indexed by 3D coordinate. This is a core "block type" value, which is used + // to index into other arrays; essentially a "palette". This is much more + // memory-efficient and performance-friendly than storing the values explicitly, + // but only makes sense if the values are always synchronized. + // + // If a voxel's blocktype is 0, it is assumed to be empty (STBVOX_GEOM_empty), + // and no other blocktypes should be STBVOX_GEOM_empty. (Only if you do not + // have blocktypes should STBVOX_GEOM_empty ever used.) + // + // Normally it is an unsigned byte, but you can override it to be + // a short if you have too many blocktypes. + + unsigned char *geometry; + // Indexed by 3D coordinate. Contains the geometry type for the block. + // Also contains a 2-bit rotation for how the whole block is rotated. + // Also includes a 2-bit vheight value when using shared vheight values. + // See the separate vheight documentation. + // Encode with STBVOX_MAKE_GEOMETRY(geom, rot, vheight) + + unsigned char *block_geometry; + // Array indexed by blocktype containing the geometry for this block, plus + // a 2-bit "simple rotation". Note rotation has limited use since it's not + // independent of blocktype. + // + // Encode with STBVOX_MAKE_GEOMETRY(geom,simple_rot,0) + + unsigned char *block_tex1; + // Array indexed by blocktype containing the texture id for texture #1. + + unsigned char (*block_tex1_face)[6]; + // Array indexed by blocktype and face containing the texture id for texture #1. + // The N/E/S/W face choices can be rotated by one of the rotation selectors; + // The top & bottom face textures will rotate to match. + // Note that it only makes sense to use one of block_tex1 or block_tex1_face; + // this pattern repeats throughout and this notice is not repeated. + + unsigned char *tex2; + // Indexed by 3D coordinate. Contains the texture id for texture #2 + // to use on all faces of the block. + + unsigned char *block_tex2; + // Array indexed by blocktype containing the texture id for texture #2. + + unsigned char (*block_tex2_face)[6]; + // Array indexed by blocktype and face containing the texture id for texture #2. + // The N/E/S/W face choices can be rotated by one of the rotation selectors; + // The top & bottom face textures will rotate to match. + + unsigned char *color; + // Indexed by 3D coordinate. Contains the color for all faces of the block. + // The core color value is 0..63. + // Encode with STBVOX_MAKE_COLOR(color_number, tex1_enable, tex2_enable) + + unsigned char *block_color; + // Array indexed by blocktype containing the color value to apply to the faces. + // The core color value is 0..63. + // Encode with STBVOX_MAKE_COLOR(color_number, tex1_enable, tex2_enable) + + unsigned char (*block_color_face)[6]; + // Array indexed by blocktype and face containing the color value to apply to that face. + // The core color value is 0..63. + // Encode with STBVOX_MAKE_COLOR(color_number, tex1_enable, tex2_enable) + + unsigned char *block_texlerp; + // Array indexed by blocktype containing 3-bit scalar for texture #2 alpha + // (known throughout as 'texlerp'). This is constant over every face even + // though the property is potentially per-vertex. + + unsigned char (*block_texlerp_face)[6]; + // Array indexed by blocktype and face containing 3-bit scalar for texture #2 alpha. + // This is constant over the face even though the property is potentially per-vertex. + + unsigned char *block_vheight; + // Array indexed by blocktype containing the vheight values for the + // top or bottom face of this block. These will rotate properly if the + // block is rotated. See discussion of vheight. + // Encode with STBVOX_MAKE_VHEIGHT(sw_height, se_height, nw_height, ne_height) + + unsigned char *selector; + // Array indexed by 3D coordinates indicating which output mesh to select. + + unsigned char *block_selector; + // Array indexed by blocktype indicating which output mesh to select. + + unsigned char *side_texrot; + // Array indexed by 3D coordinates encoding 2-bit texture rotations for the + // faces on the E/N/W/S sides of the block. + // Encode with STBVOX_MAKE_SIDE_TEXROT(rot_e, rot_n, rot_w, rot_s) + + unsigned char *block_side_texrot; + // Array indexed by blocktype encoding 2-bit texture rotations for the faces + // on the E/N/W/S sides of the block. + // Encode with STBVOX_MAKE_SIDE_TEXROT(rot_e, rot_n, rot_w, rot_s) + + unsigned char *overlay; // index into palettes listed below + // Indexed by 3D coordinate. If 0, there is no overlay. If non-zero, + // it indexes into to the below arrays and overrides the values + // defined by the blocktype. + + unsigned char (*overlay_tex1)[6]; + // Array indexed by overlay value and face, containing an override value + // for the texture id for texture #1. If 0, the value defined by blocktype + // is used. + + unsigned char (*overlay_tex2)[6]; + // Array indexed by overlay value and face, containing an override value + // for the texture id for texture #2. If 0, the value defined by blocktype + // is used. + + unsigned char (*overlay_color)[6]; + // Array indexed by overlay value and face, containing an override value + // for the face color. If 0, the value defined by blocktype is used. + + unsigned char *overlay_side_texrot; + // Array indexed by overlay value, encoding 2-bit texture rotations for the faces + // on the E/N/W/S sides of the block. + // Encode with STBVOX_MAKE_SIDE_TEXROT(rot_e, rot_n, rot_w, rot_s) + + unsigned char *rotate; + // Indexed by 3D coordinate. Allows independent rotation of several + // parts of the voxel, where by rotation I mean swapping textures + // and colors between E/N/S/W faces. + // Block: rotates anything indexed by blocktype + // Overlay: rotates anything indexed by overlay + // EColor: rotates faces defined in ecolor_facemask + // Encode with STBVOX_MAKE_MATROT(block,overlay,ecolor) + + unsigned char *tex2_for_tex1; + // Array indexed by tex1 containing the texture id for texture #2. + // You can use this if the two are always/almost-always strictly + // correlated (e.g. if tex2 is a detail texture for tex1), as it + // will be more efficient (touching fewer cache lines) than using + // e.g. block_tex2_face. + + unsigned char *tex2_replace; + // Indexed by 3D coordinate. Specifies the texture id for texture #2 + // to use on a single face of the voxel, which must be E/N/W/S (not U/D). + // The texture id is limited to 6 bits unless tex2_facemask is also + // defined (see below). + // Encode with STBVOX_MAKE_TEX2_REPLACE(tex2, face) + + unsigned char *tex2_facemask; + // Indexed by 3D coordinate. Specifies which of the six faces should + // have their tex2 replaced by the value of tex2_replace. In this + // case, all 8 bits of tex2_replace are used as the texture id. + // Encode with STBVOX_MAKE_FACE_MASK(east,north,west,south,up,down) + + unsigned char *extended_color; + // Indexed by 3D coordinate. Specifies a value that indexes into + // the ecolor arrays below (both of which must be defined). + + unsigned char *ecolor_color; + // Indexed by extended_color value, specifies an optional override + // for the color value on some faces. + // Encode with STBVOX_MAKE_COLOR(color_number, tex1_enable, tex2_enable) + + unsigned char *ecolor_facemask; + // Indexed by extended_color value, this specifies which faces the + // color in ecolor_color should be applied to. The faces can be + // independently rotated by the ecolor value of 'rotate', if it exists. + // Encode with STBVOX_MAKE_FACE_MASK(e,n,w,s,u,d) + + unsigned char *color2; + // Indexed by 3D coordinates, specifies an alternative color to apply + // to some of the faces of the block. + // Encode with STBVOX_MAKE_COLOR(color_number, tex1_enable, tex2_enable) + + unsigned char *color2_facemask; + // Indexed by 3D coordinates, specifies which faces should use the + // color defined in color2. No rotation value is applied. + // Encode with STBVOX_MAKE_FACE_MASK(e,n,w,s,u,d) + + unsigned char *color3; + // Indexed by 3D coordinates, specifies an alternative color to apply + // to some of the faces of the block. + // Encode with STBVOX_MAKE_COLOR(color_number, tex1_enable, tex2_enable) + + unsigned char *color3_facemask; + // Indexed by 3D coordinates, specifies which faces should use the + // color defined in color3. No rotation value is applied. + // Encode with STBVOX_MAKE_FACE_MASK(e,n,w,s,u,d) + + unsigned char *texlerp_simple; + // Indexed by 3D coordinates, this is the smallest texlerp encoding + // that can do useful work. It consits of three values: baselerp, + // vertlerp, and face_vertlerp. Baselerp defines the value + // to use on all of the faces but one, from the STBVOX_TEXLERP_BASE + // values. face_vertlerp is one of the 6 face values (or STBVOX_FACE_NONE) + // which specifies the face should use the vertlerp values. + // Vertlerp defines a lerp value at every vertex of the mesh. + // Thus, one face can have per-vertex texlerp values, and those + // values are encoded in the space so that they will be shared + // by adjacent faces that also use vertlerp, allowing continuity + // (this is used for the "texture crossfade" bit of the release video). + // Encode with STBVOX_MAKE_TEXLERP_SIMPLE(baselerp, vertlerp, face_vertlerp) + + // The following texlerp encodings are experimental and maybe not + // that useful. + + unsigned char *texlerp; + // Indexed by 3D coordinates, this defines four values: + // vertlerp is a lerp value at every vertex of the mesh (using STBVOX_TEXLERP_BASE values). + // ud is the value to use on up and down faces, from STBVOX_TEXLERP_FACE values + // ew is the value to use on east and west faces, from STBVOX_TEXLERP_FACE values + // ns is the value to use on north and south faces, from STBVOX_TEXLERP_FACE values + // If any of ud, ew, or ns is STBVOX_TEXLERP_FACE_use_vert, then the + // vertlerp values for the vertices are gathered and used for those faces. + // Encode with STBVOX_MAKE_TEXLERP(vertlerp,ud,ew,sw) + + unsigned short *texlerp_vert3; + // Indexed by 3D coordinates, this works with texlerp and + // provides a unique texlerp value for every direction at + // every vertex. The same rules of whether faces share values + // applies. The STBVOX_TEXLERP_FACE vertlerp value defined in + // texlerp is only used for the down direction. The values at + // each vertex in other directions are defined in this array, + // and each uses the STBVOX_TEXLERP3 values (i.e. full precision + // 3-bit texlerp values). + // Encode with STBVOX_MAKE_VERT3(vertlerp_e,vertlerp_n,vertlerp_w,vertlerp_s,vertlerp_u) + + unsigned short *texlerp_face3; // e:3,n:3,w:3,s:3,u:2,d:2 + // Indexed by 3D coordinates, this provides a compact way to + // fully specify the texlerp value indepenendly for every face, + // but doesn't allow per-vertex variation. E/N/W/S values are + // encoded using STBVOX_TEXLERP3 values, whereas up and down + // use STBVOX_TEXLERP_SIMPLE values. + // Encode with STBVOX_MAKE_FACE3(face_e,face_n,face_w,face_s,face_u,face_d) + + unsigned char *vheight; // STBVOX_MAKE_VHEIGHT -- sw:2, se:2, nw:2, ne:2, doesn't rotate + // Indexed by 3D coordinates, this defines the four + // vheight values to use if the geometry is STBVOX_GEOM_vheight*. + // See the vheight discussion. + + unsigned char *packed_compact; + // Stores block rotation, vheight, and texlerp values: + // block rotation: 2 bits + // vertex vheight: 2 bits + // use_texlerp : 1 bit + // vertex texlerp: 3 bits + // If STBVOX_CONFIG_UP_TEXLERP_PACKED is defined, then 'vertex texlerp' is + // used for up faces if use_texlerp is 1. If STBVOX_CONFIG_DOWN_TEXLERP_PACKED + // is defined, then 'vertex texlerp' is used for down faces if use_texlerp is 1. + // Note if those symbols are defined but packed_compact is NULL, the normal + // texlerp default will be used. + // Encode with STBVOX_MAKE_PACKED_COMPACT(rot, vheight, texlerp, use_texlerp) +}; +// @OPTIMIZE allow specializing; build a single struct with all of the +// 3D-indexed arrays combined so it's AoS instead of SoA for better +// cache efficiency + + +////////////////////////////////////////////////////////////////////////////// +// +// VHEIGHT DOCUMENTATION +// +// "vheight" is the internal name for the special block types +// with sloped tops or bottoms. "vheight" stands for "vertex height". +// +// Note that these blocks are very flexible (there are 256 of them, +// although at least 17 of them should never be used), but they +// also have a disadvantage that they generate extra invisible +// faces; the generator does not currently detect whether adjacent +// vheight blocks hide each others sides, so those side faces are +// always generated. For a continuous ground terrain, this means +// that you may generate 5x as many quads as needed. See notes +// on "improvements for shipping products" in the introduction. + +enum +{ + STBVOX_VERTEX_HEIGHT_0, + STBVOX_VERTEX_HEIGHT_half, + STBVOX_VERTEX_HEIGHT_1, + STBVOX_VERTEX_HEIGHT_one_and_a_half, +}; +// These are the "vheight" values. Vheight stands for "vertex height". +// The idea is that for a "floor vheight" block, you take a cube and +// reposition the top-most vertices at various heights as specified by +// the vheight values. Similarly, a "ceiling vheight" block takes a +// cube and repositions the bottom-most vertices. +// +// A floor block only adjusts the top four vertices; the bottom four vertices +// remain at the bottom of the block. The height values are 2 bits, +// measured in halves of a block; so you can specify heights of 0/2, +// 1/2, 2/2, or 3/2. 0 is the bottom of the block, 1 is halfway +// up the block, 2 is the top of the block, and 3 is halfway up the +// next block (and actually outside of the block). The value 3 is +// actually legal for floor vheight (but not ceiling), and allows you to: +// +// (A) have smoother terrain by having slopes that cross blocks, +// e.g. (1,1,3,3) is a regular-seeming slope halfway between blocks +// (B) make slopes steeper than 45-degrees, e.g. (0,0,3,3) +// +// (Because only z coordinates have half-block precision, and x&y are +// limited to block corner precision, it's not possible to make these +// things "properly" out of blocks, e.g. a half-slope block on its side +// or a sloped block halfway between blocks that's made out of two blocks.) +// +// If you define STBVOX_CONFIG_OPTIMIZED_VHEIGHT, then the top face +// (or bottom face for a ceiling vheight block) will be drawn as a +// single quad even if the four vertex heights aren't planar, and a +// single normal will be used over the entire quad. If you +// don't define it, then if the top face is non-planar, it will be +// split into two triangles, each with their own normal/lighting. +// (Note that since all output from stb_voxel_render is quad meshes, +// triangles are actually rendered as degenerate quads.) In this case, +// the distinction betwen STBVOX_GEOM_floor_vheight_03 and +// STBVOX_GEOM_floor_vheight_12 comes into play; the former introduces +// an edge from the SW to NE corner (i.e. from <0,0,?> to <1,1,?>), +// while the latter introduces an edge from the NW to SE corner +// (i.e. from <0,1,?> to <1,0,?>.) For a "lazy mesh" look, use +// exclusively _03 or _12. For a "classic mesh" look, alternate +// _03 and _12 in a checkerboard pattern. For a "smoothest surface" +// look, choose the edge based on actual vertex heights. +// +// The four vertex heights can come from several places. The simplest +// encoding is to just use the 'vheight' parameter which stores four +// explicit vertex heights for every block. This allows total independence, +// but at the cost of the largest memory usage, 1 byte per 3D block. +// Encode this with STBVOX_MAKE_VHEIGHT(vh_sw, vh_se, vh_nw, vh_ne). +// These coordinates are absolute, not affected by block rotations. +// +// An alternative if you just want to encode some very specific block +// types, not all the possibilities--say you just want half-height slopes, +// so you want (0,0,1,1) and (1,1,2,2)--then you can use block_vheight +// to specify them. The geometry rotation will cause block_vheight values +// to be rotated (because it's as if you're just defining a type of +// block). This value is also encoded with STBVOX_MAKE_VHEIGHT. +// +// If you want to save memory and you're creating a "continuous ground" +// sort of effect, you can make each vertex of the lattice share the +// vheight value; that is, two adjacent blocks that share a vertex will +// always get the same vheight value for that vertex. Then you need to +// store two bits of vheight for every block, which you do by storing it +// as part another data structure. Store the south-west vertex's vheight +// with the block. You can either use the "geometry" mesh variable (it's +// a parameter to STBVOX_MAKE_GEOMETRY) or you can store it in the +// "lighting" mesh variable if you defined STBVOX_CONFIG_VHEIGHT_IN_LIGHTING, +// using STBVOX_MAKE_LIGHTING_EXT(lighting,vheight). +// +// Note that if you start with a 2D height map and generate vheight data from +// it, you don't necessarily store only one value per (x,y) coordinate, +// as the same value may need to be set up at multiple z heights. For +// example, if height(8,8) = 13.5, then you want the block at (8,8,13) +// to store STBVOX_VERTEX_HEIGHT_half, and this will be used by blocks +// at (7,7,13), (8,7,13), (7,8,13), and (8,8,13). However, if you're +// allowing steep slopes, it might be the case that you have a block +// at (7,7,12) which is supposed to stick up to 13.5; that means +// you also need to store STBVOX_VERTEX_HEIGHT_one_and_a_half at (8,8,12). + +enum +{ + STBVOX_TEXLERP_FACE_0, + STBVOX_TEXLERP_FACE_half, + STBVOX_TEXLERP_FACE_1, + STBVOX_TEXLERP_FACE_use_vert, +}; + +enum +{ + STBVOX_TEXLERP_BASE_0, // 0.0 + STBVOX_TEXLERP_BASE_2_7, // 2/7 + STBVOX_TEXLERP_BASE_5_7, // 4/7 + STBVOX_TEXLERP_BASE_1 // 1.0 +}; + +enum +{ + STBVOX_TEXLERP3_0_8, + STBVOX_TEXLERP3_1_8, + STBVOX_TEXLERP3_2_8, + STBVOX_TEXLERP3_3_8, + STBVOX_TEXLERP3_4_8, + STBVOX_TEXLERP3_5_8, + STBVOX_TEXLERP3_6_8, + STBVOX_TEXLERP3_7_8, +}; + +#define STBVOX_FACE_NONE 7 + +#define STBVOX_BLOCKTYPE_EMPTY 0 + +#ifdef STBVOX_BLOCKTYPE_SHORT +#define STBVOX_BLOCKTYPE_HOLE 65535 +#else +#define STBVOX_BLOCKTYPE_HOLE 255 +#endif + +#define STBVOX_MAKE_GEOMETRY(geom, rotate, vheight) ((geom) + (rotate)*16 + (vheight)*64) +#define STBVOX_MAKE_VHEIGHT(v_sw, v_se, v_nw, v_ne) ((v_sw) + (v_se)*4 + (v_nw)*16 + (v_ne)*64) +#define STBVOX_MAKE_MATROT(block, overlay, color) ((block) + (overlay)*4 + (color)*64) +#define STBVOX_MAKE_TEX2_REPLACE(tex2, tex2_replace_face) ((tex2) + ((tex2_replace_face) & 3)*64) +#define STBVOX_MAKE_TEXLERP(ns2, ew2, ud2, vert) ((ew2) + (ns2)*4 + (ud2)*16 + (vert)*64) +#define STBVOX_MAKE_TEXLERP_SIMPLE(baselerp,vert,face) ((vert)*32 + (face)*4 + (baselerp)) +#define STBVOX_MAKE_TEXLERP1(vert,e2,n2,w2,s2,u4,d2) STBVOX_MAKE_TEXLERP(s2, w2, d2, vert) +#define STBVOX_MAKE_TEXLERP2(vert,e2,n2,w2,s2,u4,d2) ((u2)*16 + (n2)*4 + (s2)) +#define STBVOX_MAKE_FACE_MASK(e,n,w,s,u,d) ((e)+(n)*2+(w)*4+(s)*8+(u)*16+(d)*32) +#define STBVOX_MAKE_SIDE_TEXROT(e,n,w,s) ((e)+(n)*4+(w)*16+(s)*64) +#define STBVOX_MAKE_COLOR(color,t1,t2) ((color)+(t1)*64+(t2)*128) +#define STBVOX_MAKE_TEXLERP_VERT3(e,n,w,s,u) ((e)+(n)*8+(w)*64+(s)*512+(u)*4096) +#define STBVOX_MAKE_TEXLERP_FACE3(e,n,w,s,u,d) ((e)+(n)*8+(w)*64+(s)*512+(u)*4096+(d)*16384) +#define STBVOX_MAKE_PACKED_COMPACT(rot, vheight, texlerp, def) ((rot)+4*(vheight)+16*(use)+32*(texlerp)) + +#define STBVOX_MAKE_LIGHTING_EXT(lighting, rot) (((lighting)&~3)+(rot)) +#define STBVOX_MAKE_LIGHTING(lighting) (lighting) + +#ifndef STBVOX_MAX_MESHES +#define STBVOX_MAX_MESHES 2 // opaque & transparent +#endif + +#define STBVOX_MAX_MESH_SLOTS 3 // one vertex & two faces, or two vertex and one face + + +// don't mess with this directly, it's just here so you can +// declare stbvox_mesh_maker on the stack or as a global +struct stbvox_mesh_maker +{ + stbvox_input_description input; + int cur_x, cur_y, cur_z; // last unprocessed voxel if it splits into multiple buffers + int x0,y0,z0,x1,y1,z1; + int x_stride_in_bytes; + int y_stride_in_bytes; + int config_dirty; + int default_mesh; + unsigned int tags; + + int cube_vertex_offset[6][4]; // this allows access per-vertex data stored block-centered (like texlerp, ambient) + int vertex_gather_offset[6][4]; + + int pos_x,pos_y,pos_z; + int full; + + // computed from user input + char *output_cur [STBVOX_MAX_MESHES][STBVOX_MAX_MESH_SLOTS]; + char *output_end [STBVOX_MAX_MESHES][STBVOX_MAX_MESH_SLOTS]; + char *output_buffer[STBVOX_MAX_MESHES][STBVOX_MAX_MESH_SLOTS]; + int output_len [STBVOX_MAX_MESHES][STBVOX_MAX_MESH_SLOTS]; + + // computed from config + int output_size [STBVOX_MAX_MESHES][STBVOX_MAX_MESH_SLOTS]; // per quad + int output_step [STBVOX_MAX_MESHES][STBVOX_MAX_MESH_SLOTS]; // per vertex or per face, depending + int num_mesh_slots; + + float default_tex_scale[128][2]; +}; + +#endif // INCLUDE_STB_VOXEL_RENDER_H + + +#ifdef STB_VOXEL_RENDER_IMPLEMENTATION + +#include +#include +#include // memset + +// have to use our own names to avoid the _MSC_VER path having conflicting type names +#ifndef _MSC_VER + #include + typedef uint16_t stbvox_uint16; + typedef uint32_t stbvox_uint32; +#else + typedef unsigned short stbvox_uint16; + typedef unsigned int stbvox_uint32; +#endif + +#ifdef _MSC_VER + #define STBVOX_NOTUSED(v) (void)(v) +#else + #define STBVOX_NOTUSED(v) (void)sizeof(v) +#endif + + + +#ifndef STBVOX_CONFIG_MODE +#error "Must defined STBVOX_CONFIG_MODE to select the mode" +#endif + +#if defined(STBVOX_CONFIG_ROTATION_IN_LIGHTING) && defined(STBVOX_CONFIG_VHEIGHT_IN_LIGHTING) +#error "Can't store both rotation and vheight in lighting" +#endif + + +// The following are candidate voxel modes. Only modes 0, 1, and 20, and 21 are +// currently implemented. Reducing the storage-per-quad further +// shouldn't improve performance, although obviously it allow you +// to create larger worlds without streaming. +// +// +// ----------- Two textures ----------- -- One texture -- ---- Color only ---- +// Mode: 0 1 2 3 4 5 6 10 11 12 20 21 22 23 24 +// ============================================================================================================ +// uses Tex Buffer n Y Y Y Y Y Y Y Y Y n Y Y Y Y +// bytes per quad 32 20 14 12 10 6 6 8 8 4 32 20 10 6 4 +// non-blocks all all some some some slabs stairs some some none all all slabs slabs none +// tex1 256 256 256 256 256 256 256 256 256 256 n n n n n +// tex2 256 256 256 256 256 256 128 n n n n n n n n +// colors 64 64 64 64 64 64 64 8 n n 2^24 2^24 2^24 2^24 256 +// vertex ao Y Y Y Y Y n n Y Y n Y Y Y n n +// vertex texlerp Y Y Y n n n n - - - - - - - - +// x&y extents 127 127 128 64 64 128 64 64 128 128 127 127 128 128 128 +// z extents 255 255 128 64? 64? 64 64 32 64 128 255 255 128 64 128 + +// not sure why I only wrote down the above "result data" and didn't preserve +// the vertex formats, but here I've tried to reconstruct the designs... +// mode # 3 is wrong, one byte too large, but they may have been an error originally + +// Mode: 0 1 2 3 4 5 6 10 11 12 20 21 22 23 24 +// ============================================================================================================= +// bytes per quad 32 20 14 12 10 6 6 8 8 4 20 10 6 4 +// +// vertex x bits 7 7 0 6 0 0 0 0 0 0 7 0 0 0 +// vertex y bits 7 7 0 0 0 0 0 0 0 0 7 0 0 0 +// vertex z bits 9 9 7 4 2 0 0 2 2 0 9 2 0 0 +// vertex ao bits 6 6 6 6 6 0 0 6 6 0 6 6 0 0 +// vertex txl bits 3 3 3 0 0 0 0 0 0 0 (3) 0 0 0 +// +// face tex1 bits (8) 8 8 8 8 8 8 8 8 8 +// face tex2 bits (8) 8 8 8 8 8 7 - - - +// face color bits (8) 8 8 8 8 8 8 3 0 0 24 24 24 8 +// face normal bits (8) 8 8 8 6 4 7 4 4 3 8 3 4 3 +// face x bits 7 0 6 7 6 6 7 7 0 7 7 7 +// face y bits 7 6 6 7 6 6 7 7 0 7 7 7 +// face z bits 2 2 6 6 6 5 6 7 0 7 6 7 + + +#if STBVOX_CONFIG_MODE==0 || STBVOX_CONFIG_MODE==1 + + #define STBVOX_ICONFIG_VERTEX_32 + #define STBVOX_ICONFIG_FACE1_1 + +#elif STBVOX_CONFIG_MODE==20 || STBVOX_CONFIG_MODE==21 + + #define STBVOX_ICONFIG_VERTEX_32 + #define STBVOX_ICONFIG_FACE1_1 + #define STBVOX_ICONFIG_UNTEXTURED + +#else +#error "Selected value of STBVOX_CONFIG_MODE is not supported" +#endif + +#if STBVOX_CONFIG_MODE==0 || STBVOX_CONFIG_MODE==20 +#define STBVOX_ICONFIG_FACE_ATTRIBUTE +#endif + +#ifndef STBVOX_CONFIG_HLSL +// the fallback if all others are exhausted is GLSL +#define STBVOX_ICONFIG_GLSL +#endif + +#ifdef STBVOX_CONFIG_OPENGL_MODELVIEW +#define STBVOX_ICONFIG_OPENGL_3_1_COMPATIBILITY +#endif + +#if defined(STBVOX_ICONFIG_VERTEX_32) + typedef stbvox_uint32 stbvox_mesh_vertex; + #define stbvox_vertex_encode(x,y,z,ao,texlerp) \ + ((stbvox_uint32) ((x)+((y)<<7)+((z)<<14)+((ao)<<23)+((texlerp)<<29))) +#elif defined(STBVOX_ICONFIG_VERTEX_16_1) // mode=2 + typedef stbvox_uint16 stbvox_mesh_vertex; + #define stbvox_vertex_encode(x,y,z,ao,texlerp) \ + ((stbvox_uint16) ((z)+((ao)<<7)+((texlerp)<<13) +#elif defined(STBVOX_ICONFIG_VERTEX_16_2) // mode=3 + typedef stbvox_uint16 stbvox_mesh_vertex; + #define stbvox_vertex_encode(x,y,z,ao,texlerp) \ + ((stbvox_uint16) ((x)+((z)<<6))+((ao)<<10)) +#elif defined(STBVOX_ICONFIG_VERTEX_8) + typedef stbvox_uint8 stbvox_mesh_vertex; + #define stbvox_vertex_encode(x,y,z,ao,texlerp) \ + ((stbvox_uint8) ((z)+((ao)<<6)) +#else + #error "internal error, no vertex type" +#endif + +#ifdef STBVOX_ICONFIG_FACE1_1 + typedef struct + { + unsigned char tex1,tex2,color,face_info; + } stbvox_mesh_face; +#else + #error "internal error, no face type" +#endif + + +// 20-byte quad format: +// +// per vertex: +// +// x:7 +// y:7 +// z:9 +// ao:6 +// tex_lerp:3 +// +// per face: +// +// tex1:8 +// tex2:8 +// face:8 +// color:8 + + +// Faces: +// +// Faces use the bottom 3 bits to choose the texgen +// mode, and all the bits to choose the normal. +// Thus the bottom 3 bits have to be: +// e, n, w, s, u, d, u, d +// +// These use compact names so tables are readable + +enum +{ + STBVF_e, + STBVF_n, + STBVF_w, + STBVF_s, + STBVF_u, + STBVF_d, + STBVF_eu, + STBVF_ed, + + STBVF_eu_wall, + STBVF_nu_wall, + STBVF_wu_wall, + STBVF_su_wall, + STBVF_ne_u, + STBVF_ne_d, + STBVF_nu, + STBVF_nd, + + STBVF_ed_wall, + STBVF_nd_wall, + STBVF_wd_wall, + STBVF_sd_wall, + STBVF_nw_u, + STBVF_nw_d, + STBVF_wu, + STBVF_wd, + + STBVF_ne_u_cross, + STBVF_nw_u_cross, + STBVF_sw_u_cross, + STBVF_se_u_cross, + STBVF_sw_u, + STBVF_sw_d, + STBVF_su, + STBVF_sd, + + // @TODO we need more than 5 bits to encode the normal to fit the following + // so for now we use the right projection but the wrong normal + STBVF_se_u = STBVF_su, + STBVF_se_d = STBVF_sd, + + STBVF_count, +}; + +///////////////////////////////////////////////////////////////////////////// +// +// tables -- i'd prefer if these were at the end of the file, but: C++ +// + +static float stbvox_default_texgen[2][32][3] = +{ + { { 0, 1,0 }, { 0, 0, 1 }, { 0,-1,0 }, { 0, 0,-1 }, + { -1, 0,0 }, { 0, 0, 1 }, { 1, 0,0 }, { 0, 0,-1 }, + { 0,-1,0 }, { 0, 0, 1 }, { 0, 1,0 }, { 0, 0,-1 }, + { 1, 0,0 }, { 0, 0, 1 }, { -1, 0,0 }, { 0, 0,-1 }, + + { 1, 0,0 }, { 0, 1, 0 }, { -1, 0,0 }, { 0,-1, 0 }, + { -1, 0,0 }, { 0,-1, 0 }, { 1, 0,0 }, { 0, 1, 0 }, + { 1, 0,0 }, { 0, 1, 0 }, { -1, 0,0 }, { 0,-1, 0 }, + { -1, 0,0 }, { 0,-1, 0 }, { 1, 0,0 }, { 0, 1, 0 }, + }, + { { 0, 0,-1 }, { 0, 1,0 }, { 0, 0, 1 }, { 0,-1,0 }, + { 0, 0,-1 }, { -1, 0,0 }, { 0, 0, 1 }, { 1, 0,0 }, + { 0, 0,-1 }, { 0,-1,0 }, { 0, 0, 1 }, { 0, 1,0 }, + { 0, 0,-1 }, { 1, 0,0 }, { 0, 0, 1 }, { -1, 0,0 }, + + { 0,-1, 0 }, { 1, 0,0 }, { 0, 1, 0 }, { -1, 0,0 }, + { 0, 1, 0 }, { -1, 0,0 }, { 0,-1, 0 }, { 1, 0,0 }, + { 0,-1, 0 }, { 1, 0,0 }, { 0, 1, 0 }, { -1, 0,0 }, + { 0, 1, 0 }, { -1, 0,0 }, { 0,-1, 0 }, { 1, 0,0 }, + }, +}; + +#define STBVOX_RSQRT2 0.7071067811865f +#define STBVOX_RSQRT3 0.5773502691896f + +static float stbvox_default_normals[32][3] = +{ + { 1,0,0 }, // east + { 0,1,0 }, // north + { -1,0,0 }, // west + { 0,-1,0 }, // south + { 0,0,1 }, // up + { 0,0,-1 }, // down + { STBVOX_RSQRT2,0, STBVOX_RSQRT2 }, // east & up + { STBVOX_RSQRT2,0, -STBVOX_RSQRT2 }, // east & down + + { STBVOX_RSQRT2,0, STBVOX_RSQRT2 }, // east & up + { 0, STBVOX_RSQRT2, STBVOX_RSQRT2 }, // north & up + { -STBVOX_RSQRT2,0, STBVOX_RSQRT2 }, // west & up + { 0,-STBVOX_RSQRT2, STBVOX_RSQRT2 }, // south & up + { STBVOX_RSQRT3, STBVOX_RSQRT3, STBVOX_RSQRT3 }, // ne & up + { STBVOX_RSQRT3, STBVOX_RSQRT3,-STBVOX_RSQRT3 }, // ne & down + { 0, STBVOX_RSQRT2, STBVOX_RSQRT2 }, // north & up + { 0, STBVOX_RSQRT2, -STBVOX_RSQRT2 }, // north & down + + { STBVOX_RSQRT2,0, -STBVOX_RSQRT2 }, // east & down + { 0, STBVOX_RSQRT2, -STBVOX_RSQRT2 }, // north & down + { -STBVOX_RSQRT2,0, -STBVOX_RSQRT2 }, // west & down + { 0,-STBVOX_RSQRT2, -STBVOX_RSQRT2 }, // south & down + { -STBVOX_RSQRT3, STBVOX_RSQRT3, STBVOX_RSQRT3 }, // NW & up + { -STBVOX_RSQRT3, STBVOX_RSQRT3,-STBVOX_RSQRT3 }, // NW & down + { -STBVOX_RSQRT2,0, STBVOX_RSQRT2 }, // west & up + { -STBVOX_RSQRT2,0, -STBVOX_RSQRT2 }, // west & down + + { STBVOX_RSQRT3, STBVOX_RSQRT3,STBVOX_RSQRT3 }, // NE & up crossed + { -STBVOX_RSQRT3, STBVOX_RSQRT3,STBVOX_RSQRT3 }, // NW & up crossed + { -STBVOX_RSQRT3,-STBVOX_RSQRT3,STBVOX_RSQRT3 }, // SW & up crossed + { STBVOX_RSQRT3,-STBVOX_RSQRT3,STBVOX_RSQRT3 }, // SE & up crossed + { -STBVOX_RSQRT3,-STBVOX_RSQRT3, STBVOX_RSQRT3 }, // SW & up + { -STBVOX_RSQRT3,-STBVOX_RSQRT3,-STBVOX_RSQRT3 }, // SW & up + { 0,-STBVOX_RSQRT2, STBVOX_RSQRT2 }, // south & up + { 0,-STBVOX_RSQRT2, -STBVOX_RSQRT2 }, // south & down +}; + +static float stbvox_default_texscale[128][4] = +{ + {1,1,0,0},{1,1,0,0},{1,1,0,0},{1,1,0,0},{1,1,0,0},{1,1,0,0},{1,1,0,0},{1,1,0,0}, + {1,1,0,0},{1,1,0,0},{1,1,0,0},{1,1,0,0},{1,1,0,0},{1,1,0,0},{1,1,0,0},{1,1,0,0}, + {1,1,0,0},{1,1,0,0},{1,1,0,0},{1,1,0,0},{1,1,0,0},{1,1,0,0},{1,1,0,0},{1,1,0,0}, + {1,1,0,0},{1,1,0,0},{1,1,0,0},{1,1,0,0},{1,1,0,0},{1,1,0,0},{1,1,0,0},{1,1,0,0}, + {1,1,0,0},{1,1,0,0},{1,1,0,0},{1,1,0,0},{1,1,0,0},{1,1,0,0},{1,1,0,0},{1,1,0,0}, + {1,1,0,0},{1,1,0,0},{1,1,0,0},{1,1,0,0},{1,1,0,0},{1,1,0,0},{1,1,0,0},{1,1,0,0}, + {1,1,0,0},{1,1,0,0},{1,1,0,0},{1,1,0,0},{1,1,0,0},{1,1,0,0},{1,1,0,0},{1,1,0,0}, + {1,1,0,0},{1,1,0,0},{1,1,0,0},{1,1,0,0},{1,1,0,0},{1,1,0,0},{1,1,0,0},{1,1,0,0}, + {1,1,0,0},{1,1,0,0},{1,1,0,0},{1,1,0,0},{1,1,0,0},{1,1,0,0},{1,1,0,0},{1,1,0,0}, + {1,1,0,0},{1,1,0,0},{1,1,0,0},{1,1,0,0},{1,1,0,0},{1,1,0,0},{1,1,0,0},{1,1,0,0}, + {1,1,0,0},{1,1,0,0},{1,1,0,0},{1,1,0,0},{1,1,0,0},{1,1,0,0},{1,1,0,0},{1,1,0,0}, + {1,1,0,0},{1,1,0,0},{1,1,0,0},{1,1,0,0},{1,1,0,0},{1,1,0,0},{1,1,0,0},{1,1,0,0}, + {1,1,0,0},{1,1,0,0},{1,1,0,0},{1,1,0,0},{1,1,0,0},{1,1,0,0},{1,1,0,0},{1,1,0,0}, + {1,1,0,0},{1,1,0,0},{1,1,0,0},{1,1,0,0},{1,1,0,0},{1,1,0,0},{1,1,0,0},{1,1,0,0}, + {1,1,0,0},{1,1,0,0},{1,1,0,0},{1,1,0,0},{1,1,0,0},{1,1,0,0},{1,1,0,0},{1,1,0,0}, + {1,1,0,0},{1,1,0,0},{1,1,0,0},{1,1,0,0},{1,1,0,0},{1,1,0,0},{1,1,0,0},{1,1,0,0}, +}; + +static unsigned char stbvox_default_palette_compact[64][3] = +{ + { 255,255,255 }, { 238,238,238 }, { 221,221,221 }, { 204,204,204 }, + { 187,187,187 }, { 170,170,170 }, { 153,153,153 }, { 136,136,136 }, + { 119,119,119 }, { 102,102,102 }, { 85, 85, 85 }, { 68, 68, 68 }, + { 51, 51, 51 }, { 34, 34, 34 }, { 17, 17, 17 }, { 0, 0, 0 }, + { 255,240,240 }, { 255,220,220 }, { 255,160,160 }, { 255, 32, 32 }, + { 200,120,160 }, { 200, 60,150 }, { 220,100,130 }, { 255, 0,128 }, + { 240,240,255 }, { 220,220,255 }, { 160,160,255 }, { 32, 32,255 }, + { 120,160,200 }, { 60,150,200 }, { 100,130,220 }, { 0,128,255 }, + { 240,255,240 }, { 220,255,220 }, { 160,255,160 }, { 32,255, 32 }, + { 160,200,120 }, { 150,200, 60 }, { 130,220,100 }, { 128,255, 0 }, + { 255,255,240 }, { 255,255,220 }, { 220,220,180 }, { 255,255, 32 }, + { 200,160,120 }, { 200,150, 60 }, { 220,130,100 }, { 255,128, 0 }, + { 255,240,255 }, { 255,220,255 }, { 220,180,220 }, { 255, 32,255 }, + { 160,120,200 }, { 150, 60,200 }, { 130,100,220 }, { 128, 0,255 }, + { 240,255,255 }, { 220,255,255 }, { 180,220,220 }, { 32,255,255 }, + { 120,200,160 }, { 60,200,150 }, { 100,220,130 }, { 0,255,128 }, +}; + +static float stbvox_default_ambient[4][4] = +{ + { 0,0,1 ,0 }, // reversed lighting direction + { 0.5,0.5,0.5,0 }, // directional color + { 0.5,0.5,0.5,0 }, // constant color + { 0.5,0.5,0.5,1.0f/1000.0f/1000.0f }, // fog data for simple_fog +}; + +static float stbvox_default_palette[64][4]; + +static void stbvox_build_default_palette(void) +{ + int i; + for (i=0; i < 64; ++i) { + stbvox_default_palette[i][0] = stbvox_default_palette_compact[i][0] / 255.0f; + stbvox_default_palette[i][1] = stbvox_default_palette_compact[i][1] / 255.0f; + stbvox_default_palette[i][2] = stbvox_default_palette_compact[i][2] / 255.0f; + stbvox_default_palette[i][3] = 1.0f; + } +} + +////////////////////////////////////////////////////////////////////////////// +// +// Shaders +// + +#if defined(STBVOX_ICONFIG_OPENGL_3_1_COMPATIBILITY) + #define STBVOX_SHADER_VERSION "#version 150 compatibility\n" +#elif defined(STBVOX_ICONFIG_OPENGL_3_0) + #define STBVOX_SHADER_VERSION "#version 130\n" +#elif defined(STBVOX_ICONFIG_GLSL) + #define STBVOX_SHADER_VERSION "#version 150\n" +#else + #define STBVOX_SHADER_VERSION "" +#endif + +static const char *stbvox_vertex_program = +{ + STBVOX_SHADER_VERSION + + #ifdef STBVOX_ICONFIG_FACE_ATTRIBUTE // NOT TAG_face_sampled + "in uvec4 attr_face;\n" + #else + "uniform usamplerBuffer facearray;\n" + #endif + + #ifdef STBVOX_ICONFIG_FACE_ARRAY_2 + "uniform usamplerBuffer facearray2;\n" + #endif + + // vertex input data + "in uint attr_vertex;\n" + + // per-buffer data + "uniform vec3 transform[3];\n" + + // per-frame data + "uniform vec4 camera_pos;\n" // 4th value is used for arbitrary hacking + + // to simplify things, we avoid using more than 256 uniform vectors + // in fragment shader to avoid possible 1024 component limit, so + // we access this table in the fragment shader. + "uniform vec3 normal_table[32];\n" + + #ifndef STBVOX_CONFIG_OPENGL_MODELVIEW + "uniform mat4x4 model_view;\n" + #endif + + // fragment output data + "flat out uvec4 facedata;\n" + " out vec3 voxelspace_pos;\n" + " out vec3 vnormal;\n" + " out float texlerp;\n" + " out float amb_occ;\n" + + // @TODO handle the HLSL way to do this + "void main()\n" + "{\n" + #ifdef STBVOX_ICONFIG_FACE_ATTRIBUTE + " facedata = attr_face;\n" + #else + " int faceID = gl_VertexID >> 2;\n" + " facedata = texelFetch(facearray, faceID);\n" + #endif + + // extract data for vertex + " vec3 offset;\n" + " offset.x = float( (attr_vertex ) & 127u );\n" // a[0..6] + " offset.y = float( (attr_vertex >> 7u) & 127u );\n" // a[7..13] + " offset.z = float( (attr_vertex >> 14u) & 511u );\n" // a[14..22] + " amb_occ = float( (attr_vertex >> 23u) & 63u ) / 63.0;\n" // a[23..28] + " texlerp = float( (attr_vertex >> 29u) ) / 7.0;\n" // a[29..31] + + " vnormal = normal_table[(facedata.w>>2u) & 31u];\n" + " voxelspace_pos = offset * transform[0];\n" // mesh-to-object scale + " vec3 position = voxelspace_pos + transform[1];\n" // mesh-to-object translate + + #ifdef STBVOX_DEBUG_TEST_NORMALS + " if ((facedata.w & 28u) == 16u || (facedata.w & 28u) == 24u)\n" + " position += vnormal.xyz * camera_pos.w;\n" + #endif + + #ifndef STBVOX_CONFIG_OPENGL_MODELVIEW + " gl_Position = model_view * vec4(position,1.0);\n" + #else + " gl_Position = gl_ModelViewProjectionMatrix * vec4(position,1.0);\n" + #endif + + "}\n" +}; + + +static const char *stbvox_fragment_program = +{ + STBVOX_SHADER_VERSION + + // rlerp is lerp but with t on the left, like god intended + #if defined(STBVOX_ICONFIG_GLSL) + "#define rlerp(t,x,y) mix(x,y,t)\n" + #elif defined(STBVOX_CONFIG_HLSL) + "#define rlerp(t,x,y) lerp(x,y,t)\n" + #else + #error "need definition of rlerp()" + #endif + + + // vertex-shader output data + "flat in uvec4 facedata;\n" + " in vec3 voxelspace_pos;\n" + " in vec3 vnormal;\n" + " in float texlerp;\n" + " in float amb_occ;\n" + + // per-buffer data + "uniform vec3 transform[3];\n" + + // per-frame data + "uniform vec4 camera_pos;\n" // 4th value is used for arbitrary hacking + + // probably constant data + "uniform vec4 ambient[4];\n" + + #ifndef STBVOX_ICONFIG_UNTEXTURED + // generally constant data + "uniform sampler2DArray tex_array[2];\n" + + #ifdef STBVOX_CONFIG_PREFER_TEXBUFFER + "uniform samplerBuffer color_table;\n" + "uniform samplerBuffer texscale;\n" + "uniform samplerBuffer texgen;\n" + #else + "uniform vec4 color_table[64];\n" + "uniform vec4 texscale[64];\n" // instead of 128, to avoid running out of uniforms + "uniform vec3 texgen[64];\n" + #endif + #endif + + "out vec4 outcolor;\n" + + #if defined(STBVOX_CONFIG_LIGHTING) || defined(STBVOX_CONFIG_LIGHTING_SIMPLE) + "vec3 compute_lighting(vec3 pos, vec3 norm, vec3 albedo, vec3 ambient);\n" + #endif + #if defined(STBVOX_CONFIG_FOG) || defined(STBVOX_CONFIG_FOG_SMOOTHSTEP) + "vec3 compute_fog(vec3 color, vec3 relative_pos, float fragment_alpha);\n" + #endif + + "void main()\n" + "{\n" + " vec3 albedo;\n" + " float fragment_alpha;\n" + + #ifndef STBVOX_ICONFIG_UNTEXTURED + // unpack the values + " uint tex1_id = facedata.x;\n" + " uint tex2_id = facedata.y;\n" + " uint texprojid = facedata.w & 31u;\n" + " uint color_id = facedata.z;\n" + + #ifndef STBVOX_CONFIG_PREFER_TEXBUFFER + // load from uniforms / texture buffers + " vec3 texgen_s = texgen[texprojid];\n" + " vec3 texgen_t = texgen[texprojid+32u];\n" + " float tex1_scale = texscale[tex1_id & 63u].x;\n" + " vec4 color = color_table[color_id & 63u];\n" + #ifndef STBVOX_CONFIG_DISABLE_TEX2 + " vec4 tex2_props = texscale[tex2_id & 63u];\n" + #endif + #else + " vec3 texgen_s = texelFetch(texgen, int(texprojid)).xyz;\n" + " vec3 texgen_t = texelFetch(texgen, int(texprojid+32u)).xyz;\n" + " float tex1_scale = texelFetch(texscale, int(tex1_id & 127u)).x;\n" + " vec4 color = texelFetch(color_table, int(color_id & 63u));\n" + #ifndef STBVOX_CONFIG_DISABLE_TEX2 + " vec4 tex2_props = texelFetch(texscale, int(tex1_id & 127u));\n" + #endif + #endif + + #ifndef STBVOX_CONFIG_DISABLE_TEX2 + " float tex2_scale = tex2_props.y;\n" + " bool texblend_mode = tex2_props.z != 0.0;\n" + #endif + " vec2 texcoord;\n" + " vec3 texturespace_pos = voxelspace_pos + transform[2].xyz;\n" + " texcoord.s = dot(texturespace_pos, texgen_s);\n" + " texcoord.t = dot(texturespace_pos, texgen_t);\n" + + " vec2 texcoord_1 = tex1_scale * texcoord;\n" + #ifndef STBVOX_CONFIG_DISABLE_TEX2 + " vec2 texcoord_2 = tex2_scale * texcoord;\n" + #endif + + #ifdef STBVOX_CONFIG_TEX1_EDGE_CLAMP + " texcoord_1 = texcoord_1 - floor(texcoord_1);\n" + " vec4 tex1 = textureGrad(tex_array[0], vec3(texcoord_1, float(tex1_id)), dFdx(tex1_scale*texcoord), dFdy(tex1_scale*texcoord));\n" + #else + " vec4 tex1 = texture(tex_array[0], vec3(texcoord_1, float(tex1_id)));\n" + #endif + + #ifndef STBVOX_CONFIG_DISABLE_TEX2 + #ifdef STBVOX_CONFIG_TEX2_EDGE_CLAMP + " texcoord_2 = texcoord_2 - floor(texcoord_2);\n" + " vec4 tex2 = textureGrad(tex_array[0], vec3(texcoord_2, float(tex2_id)), dFdx(tex2_scale*texcoord), dFdy(tex2_scale*texcoord));\n" + #else + " vec4 tex2 = texture(tex_array[1], vec3(texcoord_2, float(tex2_id)));\n" + #endif + #endif + + " bool emissive = (color.a > 1.0);\n" + " color.a = min(color.a, 1.0);\n" + + // recolor textures + " if ((color_id & 64u) != 0u) tex1.rgba *= color.rgba;\n" + " fragment_alpha = tex1.a;\n" + #ifndef STBVOX_CONFIG_DISABLE_TEX2 + " if ((color_id & 128u) != 0u) tex2.rgba *= color.rgba;\n" + + #ifdef STBVOX_CONFIG_PREMULTIPLIED_ALPHA + " tex2.rgba *= texlerp;\n" + #else + " tex2.a *= texlerp;\n" + #endif + + " if (texblend_mode)\n" + " albedo = tex1.xyz * rlerp(tex2.a, vec3(1.0,1.0,1.0), 2.0*tex2.xyz);\n" + " else {\n" + #ifdef STBVOX_CONFIG_PREMULTIPLIED_ALPHA + " albedo = (1.0-tex2.a)*tex1.xyz + tex2.xyz;\n" + #else + " albedo = rlerp(tex2.a, tex1.xyz, tex2.xyz);\n" + #endif + " fragment_alpha = tex1.a*(1-tex2.a)+tex2.a;\n" + " }\n" + #else + " albedo = tex1.xyz;\n" + #endif + + #else // UNTEXTURED + " vec4 color;" + " color.xyz = vec3(facedata.xyz) / 255.0;\n" + " bool emissive = false;\n" + " albedo = color.xyz;\n" + " fragment_alpha = 1.0;\n" + #endif + + #ifdef STBVOX_ICONFIG_VARYING_VERTEX_NORMALS + // currently, there are no modes that trigger this path; idea is that there + // could be a couple of bits per vertex to perturb the normal to e.g. get curved look + " vec3 normal = normalize(vnormal);\n" + #else + " vec3 normal = vnormal;\n" + #endif + + " vec3 ambient_color = dot(normal, ambient[0].xyz) * ambient[1].xyz + ambient[2].xyz;\n" + + " ambient_color = clamp(ambient_color, 0.0, 1.0);" + " ambient_color *= amb_occ;\n" + + " vec3 lit_color;\n" + " if (!emissive)\n" + #if defined(STBVOX_ICONFIG_LIGHTING) || defined(STBVOX_CONFIG_LIGHTING_SIMPLE) + " lit_color = compute_lighting(voxelspace_pos + transform[1], normal, albedo, ambient_color);\n" + #else + " lit_color = albedo * ambient_color ;\n" + #endif + " else\n" + " lit_color = albedo;\n" + + #if defined(STBVOX_ICONFIG_FOG) || defined(STBVOX_CONFIG_FOG_SMOOTHSTEP) + " vec3 dist = voxelspace_pos + (transform[1] - camera_pos.xyz);\n" + " lit_color = compute_fog(lit_color, dist, fragment_alpha);\n" + #endif + + #ifdef STBVOX_CONFIG_UNPREMULTIPLY + " vec4 final_color = vec4(lit_color/fragment_alpha, fragment_alpha);\n" + #else + " vec4 final_color = vec4(lit_color, fragment_alpha);\n" + #endif + " outcolor = final_color;\n" + "}\n" + + #ifdef STBVOX_CONFIG_LIGHTING_SIMPLE + "\n" + "uniform vec3 light_source[2];\n" + "vec3 compute_lighting(vec3 pos, vec3 norm, vec3 albedo, vec3 ambient)\n" + "{\n" + " vec3 light_dir = light_source[0] - pos;\n" + " float lambert = dot(light_dir, norm) / dot(light_dir, light_dir);\n" + " vec3 diffuse = clamp(light_source[1] * clamp(lambert, 0.0, 1.0), 0.0, 1.0);\n" + " return (diffuse + ambient) * albedo;\n" + "}\n" + #endif + + #ifdef STBVOX_CONFIG_FOG_SMOOTHSTEP + "\n" + "vec3 compute_fog(vec3 color, vec3 relative_pos, float fragment_alpha)\n" + "{\n" + " float f = dot(relative_pos,relative_pos)*ambient[3].w;\n" + //" f = rlerp(f, -2,1);\n" + " f = clamp(f, 0.0, 1.0);\n" + " f = 3.0*f*f - 2.0*f*f*f;\n" // smoothstep + //" f = f*f;\n" // fade in more smoothly + #ifdef STBVOX_CONFIG_PREMULTIPLIED_ALPHA + " return rlerp(f, color.xyz, ambient[3].xyz*fragment_alpha);\n" + #else + " return rlerp(f, color.xyz, ambient[3].xyz);\n" + #endif + "}\n" + #endif +}; + + +// still requires full alpha lookups, including tex2 if texblend is enabled +static const char *stbvox_fragment_program_alpha_only = +{ + STBVOX_SHADER_VERSION + + // vertex-shader output data + "flat in uvec4 facedata;\n" + " in vec3 voxelspace_pos;\n" + " in float texlerp;\n" + + // per-buffer data + "uniform vec3 transform[3];\n" + + #ifndef STBVOX_ICONFIG_UNTEXTURED + // generally constant data + "uniform sampler2DArray tex_array[2];\n" + + #ifdef STBVOX_CONFIG_PREFER_TEXBUFFER + "uniform samplerBuffer texscale;\n" + "uniform samplerBuffer texgen;\n" + #else + "uniform vec4 texscale[64];\n" // instead of 128, to avoid running out of uniforms + "uniform vec3 texgen[64];\n" + #endif + #endif + + "out vec4 outcolor;\n" + + "void main()\n" + "{\n" + " vec3 albedo;\n" + " float fragment_alpha;\n" + + #ifndef STBVOX_ICONFIG_UNTEXTURED + // unpack the values + " uint tex1_id = facedata.x;\n" + " uint tex2_id = facedata.y;\n" + " uint texprojid = facedata.w & 31u;\n" + " uint color_id = facedata.z;\n" + + #ifndef STBVOX_CONFIG_PREFER_TEXBUFFER + // load from uniforms / texture buffers + " vec3 texgen_s = texgen[texprojid];\n" + " vec3 texgen_t = texgen[texprojid+32u];\n" + " float tex1_scale = texscale[tex1_id & 63u].x;\n" + " vec4 color = color_table[color_id & 63u];\n" + " vec4 tex2_props = texscale[tex2_id & 63u];\n" + #else + " vec3 texgen_s = texelFetch(texgen, int(texprojid)).xyz;\n" + " vec3 texgen_t = texelFetch(texgen, int(texprojid+32u)).xyz;\n" + " float tex1_scale = texelFetch(texscale, int(tex1_id & 127u)).x;\n" + " vec4 color = texelFetch(color_table, int(color_id & 63u));\n" + " vec4 tex2_props = texelFetch(texscale, int(tex2_id & 127u));\n" + #endif + + #ifndef STBVOX_CONFIG_DISABLE_TEX2 + " float tex2_scale = tex2_props.y;\n" + " bool texblend_mode = tex2_props.z &((facedata.w & 128u) != 0u);\n" + #endif + + " color.a = min(color.a, 1.0);\n" + + " vec2 texcoord;\n" + " vec3 texturespace_pos = voxelspace_pos + transform[2].xyz;\n" + " texcoord.s = dot(texturespace_pos, texgen_s);\n" + " texcoord.t = dot(texturespace_pos, texgen_t);\n" + + " vec2 texcoord_1 = tex1_scale * texcoord;\n" + " vec2 texcoord_2 = tex2_scale * texcoord;\n" + + #ifdef STBVOX_CONFIG_TEX1_EDGE_CLAMP + " texcoord_1 = texcoord_1 - floor(texcoord_1);\n" + " vec4 tex1 = textureGrad(tex_array[0], vec3(texcoord_1, float(tex1_id)), dFdx(tex1_scale*texcoord), dFdy(tex1_scale*texcoord));\n" + #else + " vec4 tex1 = texture(tex_array[0], vec3(texcoord_1, float(tex1_id)));\n" + #endif + + " if ((color_id & 64u) != 0u) tex1.a *= color.a;\n" + " fragment_alpha = tex1.a;\n" + + #ifndef STBVOX_CONFIG_DISABLE_TEX2 + " if (!texblend_mode) {\n" + #ifdef STBVOX_CONFIG_TEX2_EDGE_CLAMP + " texcoord_2 = texcoord_2 - floor(texcoord_2);\n" + " vec4 tex2 = textureGrad(tex_array[0], vec3(texcoord_2, float(tex2_id)), dFdx(tex2_scale*texcoord), dFdy(tex2_scale*texcoord));\n" + #else + " vec4 tex2 = texture(tex_array[1], vec3(texcoord_2, float(tex2_id)));\n" + #endif + + " tex2.a *= texlerp;\n" + " if ((color_id & 128u) != 0u) tex2.rgba *= color.a;\n" + " fragment_alpha = tex1.a*(1-tex2.a)+tex2.a;\n" + "}\n" + "\n" + #endif + + #else // UNTEXTURED + " fragment_alpha = 1.0;\n" + #endif + + " outcolor = vec4(0.0, 0.0, 0.0, fragment_alpha);\n" + "}\n" +}; + + +STBVXDEC char *stbvox_get_vertex_shader(void) +{ + return (char *) stbvox_vertex_program; +} + +STBVXDEC char *stbvox_get_fragment_shader(void) +{ + return (char *) stbvox_fragment_program; +} + +STBVXDEC char *stbvox_get_fragment_shader_alpha_only(void) +{ + return (char *) stbvox_fragment_program_alpha_only; +} + +static float stbvox_dummy_transform[3][3]; + +#ifdef STBVOX_CONFIG_PREFER_TEXBUFFER +#define STBVOX_TEXBUF 1 +#else +#define STBVOX_TEXBUF 0 +#endif + +static stbvox_uniform_info stbvox_uniforms[] = +{ + { STBVOX_UNIFORM_TYPE_sampler , 4, 1, (char*) "facearray" , 0 }, + { STBVOX_UNIFORM_TYPE_vec3 , 12, 3, (char*) "transform" , stbvox_dummy_transform[0] }, + { STBVOX_UNIFORM_TYPE_sampler , 4, 2, (char*) "tex_array" , 0 }, + { STBVOX_UNIFORM_TYPE_vec4 , 16, 128, (char*) "texscale" , stbvox_default_texscale[0] , STBVOX_TEXBUF }, + { STBVOX_UNIFORM_TYPE_vec4 , 16, 64, (char*) "color_table" , stbvox_default_palette[0] , STBVOX_TEXBUF }, + { STBVOX_UNIFORM_TYPE_vec3 , 12, 32, (char*) "normal_table" , stbvox_default_normals[0] }, + { STBVOX_UNIFORM_TYPE_vec3 , 12, 64, (char*) "texgen" , stbvox_default_texgen[0][0], STBVOX_TEXBUF }, + { STBVOX_UNIFORM_TYPE_vec4 , 16, 4, (char*) "ambient" , stbvox_default_ambient[0] }, + { STBVOX_UNIFORM_TYPE_vec4 , 16, 1, (char*) "camera_pos" , stbvox_dummy_transform[0] }, +}; + +STBVXDEC int stbvox_get_uniform_info(stbvox_uniform_info *info, int uniform) +{ + if (uniform < 0 || uniform >= STBVOX_UNIFORM_count) + return 0; + + *info = stbvox_uniforms[uniform]; + return 1; +} + +#define STBVOX_GET_GEO(geom_data) ((geom_data) & 15) + +typedef struct +{ + unsigned char block:2; + unsigned char overlay:2; + unsigned char facerot:2; + unsigned char ecolor:2; +} stbvox_rotate; + +typedef struct +{ + unsigned char x,y,z; +} stbvox_pos; + +static unsigned char stbvox_rotate_face[6][4] = +{ + { 0,1,2,3 }, + { 1,2,3,0 }, + { 2,3,0,1 }, + { 3,0,1,2 }, + { 4,4,4,4 }, + { 5,5,5,5 }, +}; + +#define STBVOX_ROTATE(x,r) stbvox_rotate_face[x][r] // (((x)+(r))&3) + +stbvox_mesh_face stbvox_compute_mesh_face_value(stbvox_mesh_maker *mm, stbvox_rotate rot, int face, int v_off, int normal) +{ + stbvox_mesh_face face_data = { 0 }; + stbvox_block_type bt = mm->input.blocktype[v_off]; + unsigned char bt_face = STBVOX_ROTATE(face, rot.block); + int facerot = rot.facerot; + + #ifdef STBVOX_ICONFIG_UNTEXTURED + if (mm->input.rgb) { + face_data.tex1 = mm->input.rgb[v_off].r; + face_data.tex2 = mm->input.rgb[v_off].g; + face_data.color = mm->input.rgb[v_off].b; + face_data.face_info = (normal<<2); + return face_data; + } + #else + unsigned char color_face; + + if (mm->input.color) + face_data.color = mm->input.color[v_off]; + + if (mm->input.block_tex1) + face_data.tex1 = mm->input.block_tex1[bt]; + else if (mm->input.block_tex1_face) + face_data.tex1 = mm->input.block_tex1_face[bt][bt_face]; + else + face_data.tex1 = bt; + + if (mm->input.block_tex2) + face_data.tex2 = mm->input.block_tex2[bt]; + else if (mm->input.block_tex2_face) + face_data.tex2 = mm->input.block_tex2_face[bt][bt_face]; + + if (mm->input.block_color) { + unsigned char mcol = mm->input.block_color[bt]; + if (mcol) + face_data.color = mcol; + } else if (mm->input.block_color_face) { + unsigned char mcol = mm->input.block_color_face[bt][bt_face]; + if (mcol) + face_data.color = mcol; + } + + if (face <= STBVOX_FACE_south) { + if (mm->input.side_texrot) + facerot = mm->input.side_texrot[v_off] >> (2 * face); + else if (mm->input.block_side_texrot) + facerot = mm->input.block_side_texrot[v_off] >> (2 * bt_face); + } + + if (mm->input.overlay) { + int over_face = STBVOX_ROTATE(face, rot.overlay); + unsigned char over = mm->input.overlay[v_off]; + if (over) { + if (mm->input.overlay_tex1) { + unsigned char rep1 = mm->input.overlay_tex1[over][over_face]; + if (rep1) + face_data.tex1 = rep1; + } + if (mm->input.overlay_tex2) { + unsigned char rep2 = mm->input.overlay_tex2[over][over_face]; + if (rep2) + face_data.tex2 = rep2; + } + if (mm->input.overlay_color) { + unsigned char rep3 = mm->input.overlay_color[over][over_face]; + if (rep3) + face_data.color = rep3; + } + + if (mm->input.overlay_side_texrot && face <= STBVOX_FACE_south) + facerot = mm->input.overlay_side_texrot[over] >> (2*over_face); + } + } + + if (mm->input.tex2_for_tex1) + face_data.tex2 = mm->input.tex2_for_tex1[face_data.tex1]; + if (mm->input.tex2) + face_data.tex2 = mm->input.tex2[v_off]; + if (mm->input.tex2_replace) { + if (mm->input.tex2_facemask[v_off] & (1 << face)) + face_data.tex2 = mm->input.tex2_replace[v_off]; + } + + color_face = STBVOX_ROTATE(face, rot.ecolor); + if (mm->input.extended_color) { + unsigned char ec = mm->input.extended_color[v_off]; + if (mm->input.ecolor_facemask[ec] & (1 << color_face)) + face_data.color = mm->input.ecolor_color[ec]; + } + + if (mm->input.color2) { + if (mm->input.color2_facemask[v_off] & (1 << color_face)) + face_data.color = mm->input.color2[v_off]; + if (mm->input.color3 && (mm->input.color3_facemask[v_off] & (1 << color_face))) + face_data.color = mm->input.color3[v_off]; + } + #endif + + face_data.face_info = (normal<<2) + facerot; + return face_data; +} + +// these are the types of faces each block can have +enum +{ + STBVOX_FT_none , + STBVOX_FT_upper , + STBVOX_FT_lower , + STBVOX_FT_solid , + STBVOX_FT_diag_012, + STBVOX_FT_diag_023, + STBVOX_FT_diag_013, + STBVOX_FT_diag_123, + STBVOX_FT_force , // can't be covered up, used for internal faces, also hides nothing + STBVOX_FT_partial , // only covered by solid, never covers anything else + + STBVOX_FT_count +}; + +static unsigned char stbvox_face_lerp[6] = { 0,2,0,2,4,4 }; +static unsigned char stbvox_vert3_lerp[5] = { 0,3,6,9,12 }; +static unsigned char stbvox_vert_lerp_for_face_lerp[4] = { 0, 4, 7, 7 }; +static unsigned char stbvox_face3_lerp[6] = { 0,3,6,9,12,14 }; +static unsigned char stbvox_vert_lerp_for_simple[4] = { 0,2,5,7 }; +static unsigned char stbvox_face3_updown[8] = { 0,2,5,7,0,2,5,7 }; // ignore top bit + +// vertex offsets for face vertices +static unsigned char stbvox_vertex_vector[6][4][3] = +{ + { { 1,0,1 }, { 1,1,1 }, { 1,1,0 }, { 1,0,0 } }, // east + { { 1,1,1 }, { 0,1,1 }, { 0,1,0 }, { 1,1,0 } }, // north + { { 0,1,1 }, { 0,0,1 }, { 0,0,0 }, { 0,1,0 } }, // west + { { 0,0,1 }, { 1,0,1 }, { 1,0,0 }, { 0,0,0 } }, // south + { { 0,1,1 }, { 1,1,1 }, { 1,0,1 }, { 0,0,1 } }, // up + { { 0,0,0 }, { 1,0,0 }, { 1,1,0 }, { 0,1,0 } }, // down +}; + +// stbvox_vertex_vector, but read coordinates as binary numbers, zyx +static unsigned char stbvox_vertex_selector[6][4] = +{ + { 5,7,3,1 }, + { 7,6,2,3 }, + { 6,4,0,2 }, + { 4,5,1,0 }, + { 6,7,5,4 }, + { 0,1,3,2 }, +}; + +static stbvox_mesh_vertex stbvox_vmesh_delta_normal[6][4] = +{ + { stbvox_vertex_encode(1,0,1,0,0) , + stbvox_vertex_encode(1,1,1,0,0) , + stbvox_vertex_encode(1,1,0,0,0) , + stbvox_vertex_encode(1,0,0,0,0) }, + { stbvox_vertex_encode(1,1,1,0,0) , + stbvox_vertex_encode(0,1,1,0,0) , + stbvox_vertex_encode(0,1,0,0,0) , + stbvox_vertex_encode(1,1,0,0,0) }, + { stbvox_vertex_encode(0,1,1,0,0) , + stbvox_vertex_encode(0,0,1,0,0) , + stbvox_vertex_encode(0,0,0,0,0) , + stbvox_vertex_encode(0,1,0,0,0) }, + { stbvox_vertex_encode(0,0,1,0,0) , + stbvox_vertex_encode(1,0,1,0,0) , + stbvox_vertex_encode(1,0,0,0,0) , + stbvox_vertex_encode(0,0,0,0,0) }, + { stbvox_vertex_encode(0,1,1,0,0) , + stbvox_vertex_encode(1,1,1,0,0) , + stbvox_vertex_encode(1,0,1,0,0) , + stbvox_vertex_encode(0,0,1,0,0) }, + { stbvox_vertex_encode(0,0,0,0,0) , + stbvox_vertex_encode(1,0,0,0,0) , + stbvox_vertex_encode(1,1,0,0,0) , + stbvox_vertex_encode(0,1,0,0,0) } +}; + +static stbvox_mesh_vertex stbvox_vmesh_pre_vheight[6][4] = +{ + { stbvox_vertex_encode(1,0,0,0,0) , + stbvox_vertex_encode(1,1,0,0,0) , + stbvox_vertex_encode(1,1,0,0,0) , + stbvox_vertex_encode(1,0,0,0,0) }, + { stbvox_vertex_encode(1,1,0,0,0) , + stbvox_vertex_encode(0,1,0,0,0) , + stbvox_vertex_encode(0,1,0,0,0) , + stbvox_vertex_encode(1,1,0,0,0) }, + { stbvox_vertex_encode(0,1,0,0,0) , + stbvox_vertex_encode(0,0,0,0,0) , + stbvox_vertex_encode(0,0,0,0,0) , + stbvox_vertex_encode(0,1,0,0,0) }, + { stbvox_vertex_encode(0,0,0,0,0) , + stbvox_vertex_encode(1,0,0,0,0) , + stbvox_vertex_encode(1,0,0,0,0) , + stbvox_vertex_encode(0,0,0,0,0) }, + { stbvox_vertex_encode(0,1,0,0,0) , + stbvox_vertex_encode(1,1,0,0,0) , + stbvox_vertex_encode(1,0,0,0,0) , + stbvox_vertex_encode(0,0,0,0,0) }, + { stbvox_vertex_encode(0,0,0,0,0) , + stbvox_vertex_encode(1,0,0,0,0) , + stbvox_vertex_encode(1,1,0,0,0) , + stbvox_vertex_encode(0,1,0,0,0) } +}; + +static stbvox_mesh_vertex stbvox_vmesh_delta_half_z[6][4] = +{ + { stbvox_vertex_encode(1,0,2,0,0) , + stbvox_vertex_encode(1,1,2,0,0) , + stbvox_vertex_encode(1,1,0,0,0) , + stbvox_vertex_encode(1,0,0,0,0) }, + { stbvox_vertex_encode(1,1,2,0,0) , + stbvox_vertex_encode(0,1,2,0,0) , + stbvox_vertex_encode(0,1,0,0,0) , + stbvox_vertex_encode(1,1,0,0,0) }, + { stbvox_vertex_encode(0,1,2,0,0) , + stbvox_vertex_encode(0,0,2,0,0) , + stbvox_vertex_encode(0,0,0,0,0) , + stbvox_vertex_encode(0,1,0,0,0) }, + { stbvox_vertex_encode(0,0,2,0,0) , + stbvox_vertex_encode(1,0,2,0,0) , + stbvox_vertex_encode(1,0,0,0,0) , + stbvox_vertex_encode(0,0,0,0,0) }, + { stbvox_vertex_encode(0,1,2,0,0) , + stbvox_vertex_encode(1,1,2,0,0) , + stbvox_vertex_encode(1,0,2,0,0) , + stbvox_vertex_encode(0,0,2,0,0) }, + { stbvox_vertex_encode(0,0,0,0,0) , + stbvox_vertex_encode(1,0,0,0,0) , + stbvox_vertex_encode(1,1,0,0,0) , + stbvox_vertex_encode(0,1,0,0,0) } +}; + +static stbvox_mesh_vertex stbvox_vmesh_crossed_pair[6][4] = +{ + { stbvox_vertex_encode(1,0,2,0,0) , + stbvox_vertex_encode(0,1,2,0,0) , + stbvox_vertex_encode(0,1,0,0,0) , + stbvox_vertex_encode(1,0,0,0,0) }, + { stbvox_vertex_encode(1,1,2,0,0) , + stbvox_vertex_encode(0,0,2,0,0) , + stbvox_vertex_encode(0,0,0,0,0) , + stbvox_vertex_encode(1,1,0,0,0) }, + { stbvox_vertex_encode(0,1,2,0,0) , + stbvox_vertex_encode(1,0,2,0,0) , + stbvox_vertex_encode(1,0,0,0,0) , + stbvox_vertex_encode(0,1,0,0,0) }, + { stbvox_vertex_encode(0,0,2,0,0) , + stbvox_vertex_encode(1,1,2,0,0) , + stbvox_vertex_encode(1,1,0,0,0) , + stbvox_vertex_encode(0,0,0,0,0) }, + // not used, so we leave it non-degenerate to make sure it doesn't get gen'd accidentally + { stbvox_vertex_encode(0,1,2,0,0) , + stbvox_vertex_encode(1,1,2,0,0) , + stbvox_vertex_encode(1,0,2,0,0) , + stbvox_vertex_encode(0,0,2,0,0) }, + { stbvox_vertex_encode(0,0,0,0,0) , + stbvox_vertex_encode(1,0,0,0,0) , + stbvox_vertex_encode(1,1,0,0,0) , + stbvox_vertex_encode(0,1,0,0,0) } +}; + +#define STBVOX_MAX_GEOM 16 +#define STBVOX_NUM_ROTATION 4 + +// this is used to determine if a face is ever generated at all +static unsigned char stbvox_hasface[STBVOX_MAX_GEOM][STBVOX_NUM_ROTATION] = +{ + { 0,0,0,0 }, // empty + { 0,0,0,0 }, // knockout + { 63,63,63,63 }, // solid + { 63,63,63,63 }, // transp + { 63,63,63,63 }, // slab + { 63,63,63,63 }, // slab + { 1|2|4|48, 8|1|2|48, 4|8|1|48, 2|4|8|48, }, // floor slopes + { 1|2|4|48, 8|1|2|48, 4|8|1|48, 2|4|8|48, }, // ceil slopes + { 47,47,47,47 }, // wall-projected diagonal with down face + { 31,31,31,31 }, // wall-projected diagonal with up face + { 63,63,63,63 }, // crossed-pair has special handling, but avoid early-out + { 63,63,63,63 }, // force + { 63,63,63,63 }, // vheight + { 63,63,63,63 }, // vheight + { 63,63,63,63 }, // vheight + { 63,63,63,63 }, // vheight +}; + +// this determines which face type above is visible on each side of the geometry +static unsigned char stbvox_facetype[STBVOX_GEOM_count][6] = +{ + { 0, }, // STBVOX_GEOM_empty + { STBVOX_FT_solid, STBVOX_FT_solid, STBVOX_FT_solid, STBVOX_FT_solid, STBVOX_FT_solid, STBVOX_FT_solid }, // knockout + { STBVOX_FT_solid, STBVOX_FT_solid, STBVOX_FT_solid, STBVOX_FT_solid, STBVOX_FT_solid, STBVOX_FT_solid }, // solid + { STBVOX_FT_force, STBVOX_FT_force, STBVOX_FT_force, STBVOX_FT_force, STBVOX_FT_force, STBVOX_FT_force }, // transp + + { STBVOX_FT_upper, STBVOX_FT_upper, STBVOX_FT_upper, STBVOX_FT_upper, STBVOX_FT_solid, STBVOX_FT_force }, + { STBVOX_FT_lower, STBVOX_FT_lower, STBVOX_FT_lower, STBVOX_FT_lower, STBVOX_FT_force, STBVOX_FT_solid }, + { STBVOX_FT_diag_123, STBVOX_FT_solid, STBVOX_FT_diag_023, STBVOX_FT_none, STBVOX_FT_force, STBVOX_FT_solid }, + { STBVOX_FT_diag_012, STBVOX_FT_solid, STBVOX_FT_diag_013, STBVOX_FT_none, STBVOX_FT_solid, STBVOX_FT_force }, + + { STBVOX_FT_diag_123, STBVOX_FT_solid, STBVOX_FT_diag_023, STBVOX_FT_force, STBVOX_FT_none, STBVOX_FT_solid }, + { STBVOX_FT_diag_012, STBVOX_FT_solid, STBVOX_FT_diag_013, STBVOX_FT_force, STBVOX_FT_solid, STBVOX_FT_none }, + { STBVOX_FT_force, STBVOX_FT_force, STBVOX_FT_force, STBVOX_FT_force, 0,0 }, // crossed pair + { STBVOX_FT_force, STBVOX_FT_force, STBVOX_FT_force, STBVOX_FT_force, STBVOX_FT_force, STBVOX_FT_force }, // GEOM_force + + { STBVOX_FT_partial,STBVOX_FT_partial,STBVOX_FT_partial,STBVOX_FT_partial, STBVOX_FT_force, STBVOX_FT_solid }, // floor vheight, all neighbors forced + { STBVOX_FT_partial,STBVOX_FT_partial,STBVOX_FT_partial,STBVOX_FT_partial, STBVOX_FT_force, STBVOX_FT_solid }, // floor vheight, all neighbors forced + { STBVOX_FT_partial,STBVOX_FT_partial,STBVOX_FT_partial,STBVOX_FT_partial, STBVOX_FT_solid, STBVOX_FT_force }, // ceil vheight, all neighbors forced + { STBVOX_FT_partial,STBVOX_FT_partial,STBVOX_FT_partial,STBVOX_FT_partial, STBVOX_FT_solid, STBVOX_FT_force }, // ceil vheight, all neighbors forced +}; + +// This table indicates what normal to use for the "up" face of a sloped geom +// @TODO this could be done with math given the current arrangement of the enum, but let's not require it +static unsigned char stbvox_floor_slope_for_rot[4] = +{ + STBVF_su, + STBVF_wu, // @TODO: why is this reversed from what it should be? this is a north-is-up face, so slope should be south&up + STBVF_nu, + STBVF_eu, +}; + +static unsigned char stbvox_ceil_slope_for_rot[4] = +{ + STBVF_sd, + STBVF_ed, + STBVF_nd, + STBVF_wd, +}; + +// this table indicates whether, for each pair of types above, a face is visible. +// each value indicates whether a given type is visible for all neighbor types +static unsigned short stbvox_face_visible[STBVOX_FT_count] = +{ + // we encode the table by listing which cases cause *obscuration*, and bitwise inverting that + // table is pre-shifted by 5 to save a shift when it's accessed + (unsigned short) ((~0x07ff )<<5), // none is completely obscured by everything + (unsigned short) ((~((1<output_cur[mesh][0]; + int step = mm->output_step[mesh][0]; + + // allocate a new quad from the mesh + vertices[0] = (stbvox_mesh_vertex *) p; p += step; + vertices[1] = (stbvox_mesh_vertex *) p; p += step; + vertices[2] = (stbvox_mesh_vertex *) p; p += step; + vertices[3] = (stbvox_mesh_vertex *) p; p += step; + mm->output_cur[mesh][0] = p; + + // output the face + #ifdef STBVOX_ICONFIG_FACE_ATTRIBUTE + // write face as interleaved vertex data + *(stbvox_mesh_face *) (vertices[0]+1) = face; + *(stbvox_mesh_face *) (vertices[1]+1) = face; + *(stbvox_mesh_face *) (vertices[2]+1) = face; + *(stbvox_mesh_face *) (vertices[3]+1) = face; + #else + *(stbvox_mesh_face *) mm->output_cur[mesh][1] = face; + mm->output_cur[mesh][1] += 4; + #endif +} + +void stbvox_make_mesh_for_face(stbvox_mesh_maker *mm, stbvox_rotate rot, int face, int v_off, stbvox_pos pos, stbvox_mesh_vertex vertbase, stbvox_mesh_vertex *face_coord, unsigned char mesh, int normal) +{ + stbvox_mesh_face face_data = stbvox_compute_mesh_face_value(mm,rot,face,v_off, normal); + + // still need to compute ao & texlerp for each vertex + + // first compute texlerp into p1 + stbvox_mesh_vertex p1[4] = { 0 }; + + #if defined(STBVOX_CONFIG_DOWN_TEXLERP_PACKED) && defined(STBVOX_CONFIG_UP_TEXLERP_PACKED) + #define STBVOX_USE_PACKED(f) ((f) == STBVOX_FACE_up || (f) == STBVOX_FACE_down) + #elif defined(STBVOX_CONFIG_UP_TEXLERP_PACKED) + #define STBVOX_USE_PACKED(f) ((f) == STBVOX_FACE_up ) + #elif defined(STBVOX_CONFIG_DOWN_TEXLERP_PACKED) + #define STBVOX_USE_PACKED(f) ( (f) == STBVOX_FACE_down) + #endif + + #if defined(STBVOX_CONFIG_DOWN_TEXLERP_PACKED) || defined(STBVOX_CONFIG_UP_TEXLERP_PACKED) + if (STBVOX_USE_PACKED(face)) { + if (!mm->input.packed_compact || 0==(mm->input.packed_compact[v_off]&16)) + goto set_default; + p1[0] = (mm->input.packed_compact[v_off + mm->cube_vertex_offset[face][0]] >> 5); + p1[1] = (mm->input.packed_compact[v_off + mm->cube_vertex_offset[face][1]] >> 5); + p1[2] = (mm->input.packed_compact[v_off + mm->cube_vertex_offset[face][2]] >> 5); + p1[3] = (mm->input.packed_compact[v_off + mm->cube_vertex_offset[face][3]] >> 5); + p1[0] = stbvox_vertex_encode(0,0,0,0,p1[0]); + p1[1] = stbvox_vertex_encode(0,0,0,0,p1[1]); + p1[2] = stbvox_vertex_encode(0,0,0,0,p1[2]); + p1[3] = stbvox_vertex_encode(0,0,0,0,p1[3]); + goto skip; + } + #endif + + if (mm->input.block_texlerp) { + stbvox_block_type bt = mm->input.blocktype[v_off]; + unsigned char val = mm->input.block_texlerp[bt]; + p1[0] = p1[1] = p1[2] = p1[3] = stbvox_vertex_encode(0,0,0,0,val); + } else if (mm->input.block_texlerp_face) { + stbvox_block_type bt = mm->input.blocktype[v_off]; + unsigned char bt_face = STBVOX_ROTATE(face, rot.block); + unsigned char val = mm->input.block_texlerp_face[bt][bt_face]; + p1[0] = p1[1] = p1[2] = p1[3] = stbvox_vertex_encode(0,0,0,0,val); + } else if (mm->input.texlerp_face3) { + unsigned char val = (mm->input.texlerp_face3[v_off] >> stbvox_face3_lerp[face]) & 7; + if (face >= STBVOX_FACE_up) + val = stbvox_face3_updown[val]; + p1[0] = p1[1] = p1[2] = p1[3] = stbvox_vertex_encode(0,0,0,0,val); + } else if (mm->input.texlerp_simple) { + unsigned char val = mm->input.texlerp_simple[v_off]; + unsigned char lerp_face = (val >> 2) & 7; + if (lerp_face == face) { + p1[0] = (mm->input.texlerp_simple[v_off + mm->cube_vertex_offset[face][0]] >> 5) & 7; + p1[1] = (mm->input.texlerp_simple[v_off + mm->cube_vertex_offset[face][1]] >> 5) & 7; + p1[2] = (mm->input.texlerp_simple[v_off + mm->cube_vertex_offset[face][2]] >> 5) & 7; + p1[3] = (mm->input.texlerp_simple[v_off + mm->cube_vertex_offset[face][3]] >> 5) & 7; + p1[0] = stbvox_vertex_encode(0,0,0,0,p1[0]); + p1[1] = stbvox_vertex_encode(0,0,0,0,p1[1]); + p1[2] = stbvox_vertex_encode(0,0,0,0,p1[2]); + p1[3] = stbvox_vertex_encode(0,0,0,0,p1[3]); + } else { + unsigned char base = stbvox_vert_lerp_for_simple[val&3]; + p1[0] = p1[1] = p1[2] = p1[3] = stbvox_vertex_encode(0,0,0,0,base); + } + } else if (mm->input.texlerp) { + unsigned char facelerp = (mm->input.texlerp[v_off] >> stbvox_face_lerp[face]) & 3; + if (facelerp == STBVOX_TEXLERP_FACE_use_vert) { + if (mm->input.texlerp_vert3 && face != STBVOX_FACE_down) { + unsigned char shift = stbvox_vert3_lerp[face]; + p1[0] = (mm->input.texlerp_vert3[mm->cube_vertex_offset[face][0]] >> shift) & 7; + p1[1] = (mm->input.texlerp_vert3[mm->cube_vertex_offset[face][1]] >> shift) & 7; + p1[2] = (mm->input.texlerp_vert3[mm->cube_vertex_offset[face][2]] >> shift) & 7; + p1[3] = (mm->input.texlerp_vert3[mm->cube_vertex_offset[face][3]] >> shift) & 7; + } else { + p1[0] = stbvox_vert_lerp_for_simple[mm->input.texlerp[mm->cube_vertex_offset[face][0]]>>6]; + p1[1] = stbvox_vert_lerp_for_simple[mm->input.texlerp[mm->cube_vertex_offset[face][1]]>>6]; + p1[2] = stbvox_vert_lerp_for_simple[mm->input.texlerp[mm->cube_vertex_offset[face][2]]>>6]; + p1[3] = stbvox_vert_lerp_for_simple[mm->input.texlerp[mm->cube_vertex_offset[face][3]]>>6]; + } + p1[0] = stbvox_vertex_encode(0,0,0,0,p1[0]); + p1[1] = stbvox_vertex_encode(0,0,0,0,p1[1]); + p1[2] = stbvox_vertex_encode(0,0,0,0,p1[2]); + p1[3] = stbvox_vertex_encode(0,0,0,0,p1[3]); + } else { + p1[0] = p1[1] = p1[2] = p1[3] = stbvox_vertex_encode(0,0,0,0,stbvox_vert_lerp_for_face_lerp[facelerp]); + } + } else { + #if defined(STBVOX_CONFIG_UP_TEXLERP_PACKED) || defined(STBVOX_CONFIG_DOWN_TEXLERP_PACKED) + set_default: + #endif + p1[0] = p1[1] = p1[2] = p1[3] = stbvox_vertex_encode(0,0,0,0,7); // @TODO make this configurable + } + + #if defined(STBVOX_CONFIG_UP_TEXLERP_PACKED) || defined(STBVOX_CONFIG_DOWN_TEXLERP_PACKED) + skip: + #endif + + // now compute lighting and store to vertices + { + stbvox_mesh_vertex *mv[4]; + stbvox_get_quad_vertex_pointer(mm, mesh, mv, face_data); + + if (mm->input.lighting) { + // @TODO: lighting at block centers, but not gathered, instead constant-per-face + if (mm->input.lighting_at_vertices) { + int i; + for (i=0; i < 4; ++i) { + *mv[i] = vertbase + face_coord[i] + + stbvox_vertex_encode(0,0,0,mm->input.lighting[v_off + mm->cube_vertex_offset[face][i]] & 63,0) + + p1[i]; + } + } else { + unsigned char *amb = &mm->input.lighting[v_off]; + int i,j; + #if defined(STBVOX_CONFIG_ROTATION_IN_LIGHTING) || defined(STBVOX_CONFIG_VHEIGHT_IN_LIGHTING) + #define STBVOX_GET_LIGHTING(light) ((light) & ~3) + #define STBVOX_LIGHTING_ROUNDOFF 8 + #else + #define STBVOX_GET_LIGHTING(light) (light) + #define STBVOX_LIGHTING_ROUNDOFF 2 + #endif + + for (i=0; i < 4; ++i) { + // for each vertex, gather from the four neighbor blocks it's facing + unsigned char *vamb = &amb[mm->cube_vertex_offset[face][i]]; + int total=0; + for (j=0; j < 4; ++j) + total += STBVOX_GET_LIGHTING(vamb[mm->vertex_gather_offset[face][j]]); + *mv[i] = vertbase + face_coord[i] + + stbvox_vertex_encode(0,0,0,(total+STBVOX_LIGHTING_ROUNDOFF)>>4,0) + + p1[i]; + // >> 4 is because: + // >> 2 to divide by 4 to get average over 4 samples + // >> 2 because input is 8 bits, output is 6 bits + } + + // @TODO: note that gathering baked *lighting* + // is different from gathering baked ao; baked ao can count + // solid blocks as 0 ao, but baked lighting wants average + // of non-blocked--not take average & treat blocked as 0. And + // we can't bake the right value into the solid blocks + // because they can have different lighting values on + // different sides. So we need to actually gather and + // then divide by 0..4 (which we can do with a table-driven + // multiply, or have an 'if' for the 3 case) + + } + } else { + vertbase += stbvox_vertex_encode(0,0,0,63,0); + *mv[0] = vertbase + face_coord[0] + p1[0]; + *mv[1] = vertbase + face_coord[1] + p1[1]; + *mv[2] = vertbase + face_coord[2] + p1[2]; + *mv[3] = vertbase + face_coord[3] + p1[3]; + } + } +} + +// get opposite-facing normal & texgen for opposite face, used to map up-facing vheight data to down-facing data +static unsigned char stbvox_reverse_face[STBVF_count] = +{ + STBVF_w, STBVF_s, STBVF_e, STBVF_n, STBVF_d , STBVF_u , STBVF_wd, STBVF_wu, + 0, 0, 0, 0, STBVF_sw_d, STBVF_sw_u, STBVF_sd, STBVF_su, + 0, 0, 0, 0, STBVF_se_d, STBVF_se_u, STBVF_ed, STBVF_eu, + 0, 0, 0, 0, STBVF_ne_d, STBVF_ne_d, STBVF_nd, STBVF_nu +}; + +#ifndef STBVOX_CONFIG_OPTIMIZED_VHEIGHT +// render non-planar quads by splitting into two triangles, rendering each as a degenerate quad +static void stbvox_make_12_split_mesh_for_face(stbvox_mesh_maker *mm, stbvox_rotate rot, int face, int v_off, stbvox_pos pos, stbvox_mesh_vertex vertbase, stbvox_mesh_vertex *face_coord, unsigned char mesh, unsigned char *ht) +{ + stbvox_mesh_vertex v[4]; + + unsigned char normal1 = stbvox_face_up_normal_012[ht[2]][ht[1]][ht[0]]; + unsigned char normal2 = stbvox_face_up_normal_123[ht[3]][ht[2]][ht[1]]; + + if (face == STBVOX_FACE_down) { + normal1 = stbvox_reverse_face[normal1]; + normal2 = stbvox_reverse_face[normal2]; + } + + // the floor side face_coord is stored in order NW,NE,SE,SW, but ht[] is stored SW,SE,NW,NE + v[0] = face_coord[2]; + v[1] = face_coord[3]; + v[2] = face_coord[0]; + v[3] = face_coord[2]; + stbvox_make_mesh_for_face(mm, rot, face, v_off, pos, vertbase, v, mesh, normal1); + v[1] = face_coord[0]; + v[2] = face_coord[1]; + stbvox_make_mesh_for_face(mm, rot, face, v_off, pos, vertbase, v, mesh, normal2); +} + +static void stbvox_make_03_split_mesh_for_face(stbvox_mesh_maker *mm, stbvox_rotate rot, int face, int v_off, stbvox_pos pos, stbvox_mesh_vertex vertbase, stbvox_mesh_vertex *face_coord, unsigned char mesh, unsigned char *ht) +{ + stbvox_mesh_vertex v[4]; + + unsigned char normal1 = stbvox_face_up_normal_013[ht[3]][ht[1]][ht[0]]; + unsigned char normal2 = stbvox_face_up_normal_023[ht[3]][ht[2]][ht[0]]; + + if (face == STBVOX_FACE_down) { + normal1 = stbvox_reverse_face[normal1]; + normal2 = stbvox_reverse_face[normal2]; + } + + v[0] = face_coord[1]; + v[1] = face_coord[2]; + v[2] = face_coord[3]; + v[3] = face_coord[1]; + stbvox_make_mesh_for_face(mm, rot, face, v_off, pos, vertbase, v, mesh, normal1); + v[1] = face_coord[3]; + v[2] = face_coord[0]; + stbvox_make_mesh_for_face(mm, rot, face, v_off, pos, vertbase, v, mesh, normal2); // this one is correct! +} +#endif + +#ifndef STBVOX_CONFIG_PRECISION_Z +#define STBVOX_CONFIG_PRECISION_Z 1 +#endif + +// simple case for mesh generation: we have only solid and empty blocks +static void stbvox_make_mesh_for_block(stbvox_mesh_maker *mm, stbvox_pos pos, int v_off, stbvox_mesh_vertex *vmesh) +{ + int ns_off = mm->y_stride_in_bytes; + int ew_off = mm->x_stride_in_bytes; + + unsigned char *blockptr = &mm->input.blocktype[v_off]; + stbvox_mesh_vertex basevert = stbvox_vertex_encode(pos.x, pos.y, pos.z << STBVOX_CONFIG_PRECISION_Z , 0,0); + + stbvox_rotate rot = { 0,0,0,0 }; + unsigned char simple_rot = 0; + + unsigned char mesh = mm->default_mesh; + + if (mm->input.selector) + mesh = mm->input.selector[v_off]; + + // check if we're going off the end + if (mm->output_cur[mesh][0] + mm->output_size[mesh][0]*6 > mm->output_end[mesh][0]) { + mm->full = 1; + return; + } + + #ifdef STBVOX_CONFIG_ROTATION_IN_LIGHTING + simple_rot = mm->input.lighting[v_off] & 3; + #endif + + if (mm->input.packed_compact) + simple_rot = mm->input.packed_compact[v_off] & 3; + + if (blockptr[ 1]==0) { + rot.facerot = simple_rot; + stbvox_make_mesh_for_face(mm, rot, STBVOX_FACE_up , v_off, pos, basevert, vmesh+4*STBVOX_FACE_up, mesh, STBVOX_FACE_up); + } + if (blockptr[-1]==0) { + rot.facerot = (-simple_rot) & 3; + stbvox_make_mesh_for_face(mm, rot, STBVOX_FACE_down, v_off, pos, basevert, vmesh+4*STBVOX_FACE_down, mesh, STBVOX_FACE_down); + } + + if (mm->input.rotate) { + unsigned char val = mm->input.rotate[v_off]; + rot.block = (val >> 0) & 3; + rot.overlay = (val >> 2) & 3; + //rot.tex2 = (val >> 4) & 3; + rot.ecolor = (val >> 6) & 3; + } else { + rot.block = rot.overlay = rot.ecolor = simple_rot; + } + rot.facerot = 0; + + if (blockptr[ ns_off]==0) + stbvox_make_mesh_for_face(mm, rot, STBVOX_FACE_north, v_off, pos, basevert, vmesh+4*STBVOX_FACE_north, mesh, STBVOX_FACE_north); + if (blockptr[-ns_off]==0) + stbvox_make_mesh_for_face(mm, rot, STBVOX_FACE_south, v_off, pos, basevert, vmesh+4*STBVOX_FACE_south, mesh, STBVOX_FACE_south); + if (blockptr[ ew_off]==0) + stbvox_make_mesh_for_face(mm, rot, STBVOX_FACE_east , v_off, pos, basevert, vmesh+4*STBVOX_FACE_east, mesh, STBVOX_FACE_east); + if (blockptr[-ew_off]==0) + stbvox_make_mesh_for_face(mm, rot, STBVOX_FACE_west , v_off, pos, basevert, vmesh+4*STBVOX_FACE_west, mesh, STBVOX_FACE_west); +} + +// complex case for mesh generation: we have lots of different +// block types, and we don't want to generate faces of blocks +// if they're hidden by neighbors. +// +// we use lots of tables to determine this: we have a table +// which tells us what face type is generated for each type of +// geometry, and then a table that tells us whether that type +// is hidden by a neighbor. +static void stbvox_make_mesh_for_block_with_geo(stbvox_mesh_maker *mm, stbvox_pos pos, int v_off) +{ + int ns_off = mm->y_stride_in_bytes; + int ew_off = mm->x_stride_in_bytes; + int visible_faces, visible_base; + unsigned char mesh; + + // first gather the geometry info for this block and all neighbors + + unsigned char bt, nbt[6]; + unsigned char geo, ngeo[6]; + unsigned char rot, nrot[6]; + + bt = mm->input.blocktype[v_off]; + nbt[0] = mm->input.blocktype[v_off + ew_off]; + nbt[1] = mm->input.blocktype[v_off + ns_off]; + nbt[2] = mm->input.blocktype[v_off - ew_off]; + nbt[3] = mm->input.blocktype[v_off - ns_off]; + nbt[4] = mm->input.blocktype[v_off + 1]; + nbt[5] = mm->input.blocktype[v_off - 1]; + if (mm->input.geometry) { + int i; + geo = mm->input.geometry[v_off]; + ngeo[0] = mm->input.geometry[v_off + ew_off]; + ngeo[1] = mm->input.geometry[v_off + ns_off]; + ngeo[2] = mm->input.geometry[v_off - ew_off]; + ngeo[3] = mm->input.geometry[v_off - ns_off]; + ngeo[4] = mm->input.geometry[v_off + 1]; + ngeo[5] = mm->input.geometry[v_off - 1]; + + rot = (geo >> 4) & 3; + geo &= 15; + for (i=0; i < 6; ++i) { + nrot[i] = (ngeo[i] >> 4) & 3; + ngeo[i] &= 15; + } + } else { + int i; + assert(mm->input.block_geometry); + geo = mm->input.block_geometry[bt]; + for (i=0; i < 6; ++i) + ngeo[i] = mm->input.block_geometry[nbt[i]]; + if (mm->input.selector) { + #ifndef STBVOX_CONFIG_ROTATION_IN_LIGHTING + if (mm->input.packed_compact == NULL) { + rot = (mm->input.selector[v_off ] >> 4) & 3; + nrot[0] = (mm->input.selector[v_off + ew_off] >> 4) & 3; + nrot[1] = (mm->input.selector[v_off + ns_off] >> 4) & 3; + nrot[2] = (mm->input.selector[v_off - ew_off] >> 4) & 3; + nrot[3] = (mm->input.selector[v_off - ns_off] >> 4) & 3; + nrot[4] = (mm->input.selector[v_off + 1] >> 4) & 3; + nrot[5] = (mm->input.selector[v_off - 1] >> 4) & 3; + } + #endif + } else { + #ifndef STBVOX_CONFIG_ROTATION_IN_LIGHTING + if (mm->input.packed_compact == NULL) { + rot = (geo>>4)&3; + geo &= 15; + for (i=0; i < 6; ++i) { + nrot[i] = (ngeo[i]>>4)&3; + ngeo[i] &= 15; + } + } + #endif + } + } + + #ifndef STBVOX_CONFIG_ROTATION_IN_LIGHTING + if (mm->input.packed_compact) { + rot = mm->input.packed_compact[rot] & 3; + nrot[0] = mm->input.packed_compact[v_off + ew_off] & 3; + nrot[1] = mm->input.packed_compact[v_off + ns_off] & 3; + nrot[2] = mm->input.packed_compact[v_off - ew_off] & 3; + nrot[3] = mm->input.packed_compact[v_off - ns_off] & 3; + nrot[4] = mm->input.packed_compact[v_off + 1] & 3; + nrot[5] = mm->input.packed_compact[v_off - 1] & 3; + } + #else + rot = mm->input.lighting[v_off] & 3; + nrot[0] = (mm->input.lighting[v_off + ew_off]) & 3; + nrot[1] = (mm->input.lighting[v_off + ns_off]) & 3; + nrot[2] = (mm->input.lighting[v_off - ew_off]) & 3; + nrot[3] = (mm->input.lighting[v_off - ns_off]) & 3; + nrot[4] = (mm->input.lighting[v_off + 1]) & 3; + nrot[5] = (mm->input.lighting[v_off - 1]) & 3; + #endif + + if (geo == STBVOX_GEOM_transp) { + // transparency has a special rule: if the blocktype is the same, + // and the faces are compatible, then can hide them; otherwise, + // force them on + // Note that this means we don't support any transparentshapes other + // than solid blocks, since detecting them is too complicated. If + // you wanted to do something like minecraft water, you probably + // should just do that with a separate renderer anyway. (We don't + // support transparency sorting so you need to use alpha test + // anyway) + int i; + for (i=0; i < 6; ++i) + if (nbt[i] != bt) { + nbt[i] = 0; + ngeo[i] = STBVOX_GEOM_empty; + } else + ngeo[i] = STBVOX_GEOM_solid; + geo = STBVOX_GEOM_solid; + } + + // now compute the face visibility + visible_base = stbvox_hasface[geo][rot]; + // @TODO: assert(visible_base != 0); // we should have early-outted earlier in this case + visible_faces = 0; + + // now, for every face that might be visible, check if neighbor hides it + if (visible_base & (1 << STBVOX_FACE_east)) { + int type = stbvox_facetype[ geo ][(STBVOX_FACE_east+ rot )&3]; + int ntype = stbvox_facetype[ngeo[0]][(STBVOX_FACE_west+nrot[0])&3]; + visible_faces |= ((stbvox_face_visible[type]) >> (ntype + 5 - STBVOX_FACE_east)) & (1 << STBVOX_FACE_east); + } + if (visible_base & (1 << STBVOX_FACE_north)) { + int type = stbvox_facetype[ geo ][(STBVOX_FACE_north+ rot )&3]; + int ntype = stbvox_facetype[ngeo[1]][(STBVOX_FACE_south+nrot[1])&3]; + visible_faces |= ((stbvox_face_visible[type]) >> (ntype + 5 - STBVOX_FACE_north)) & (1 << STBVOX_FACE_north); + } + if (visible_base & (1 << STBVOX_FACE_west)) { + int type = stbvox_facetype[ geo ][(STBVOX_FACE_west+ rot )&3]; + int ntype = stbvox_facetype[ngeo[2]][(STBVOX_FACE_east+nrot[2])&3]; + visible_faces |= ((stbvox_face_visible[type]) >> (ntype + 5 - STBVOX_FACE_west)) & (1 << STBVOX_FACE_west); + } + if (visible_base & (1 << STBVOX_FACE_south)) { + int type = stbvox_facetype[ geo ][(STBVOX_FACE_south+ rot )&3]; + int ntype = stbvox_facetype[ngeo[3]][(STBVOX_FACE_north+nrot[3])&3]; + visible_faces |= ((stbvox_face_visible[type]) >> (ntype + 5 - STBVOX_FACE_south)) & (1 << STBVOX_FACE_south); + } + if (visible_base & (1 << STBVOX_FACE_up)) { + int type = stbvox_facetype[ geo ][STBVOX_FACE_up]; + int ntype = stbvox_facetype[ngeo[4]][STBVOX_FACE_down]; + visible_faces |= ((stbvox_face_visible[type]) >> (ntype + 5 - STBVOX_FACE_up)) & (1 << STBVOX_FACE_up); + } + if (visible_base & (1 << STBVOX_FACE_down)) { + int type = stbvox_facetype[ geo ][STBVOX_FACE_down]; + int ntype = stbvox_facetype[ngeo[5]][STBVOX_FACE_up]; + visible_faces |= ((stbvox_face_visible[type]) >> (ntype + 5 - STBVOX_FACE_down)) & (1 << STBVOX_FACE_down); + } + + if (geo == STBVOX_GEOM_force) + geo = STBVOX_GEOM_solid; + + assert((geo == STBVOX_GEOM_crossed_pair) ? (visible_faces == 15) : 1); + + // now we finally know for sure which faces are getting generated + if (visible_faces == 0) + return; + + mesh = mm->default_mesh; + if (mm->input.selector) + mesh = mm->input.selector[v_off]; + + if (geo <= STBVOX_GEOM_ceil_slope_north_is_bottom) { + // this is the simple case, we can just use regular block gen with special vmesh calculated with vheight + stbvox_mesh_vertex basevert; + stbvox_mesh_vertex vmesh[6][4]; + stbvox_rotate rotate = { 0,0,0,0 }; + unsigned char simple_rot = rot; + int i; + // we only need to do this for the displayed faces, but it's easier + // to just do it up front; @OPTIMIZE check if it's faster to do it + // for visible faces only + for (i=0; i < 6*4; ++i) { + int vert = stbvox_vertex_selector[0][i]; + vert = stbvox_rotate_vertex[vert][rot]; + vmesh[0][i] = stbvox_vmesh_pre_vheight[0][i] + + stbvox_geometry_vheight[geo][vert]; + } + + basevert = stbvox_vertex_encode(pos.x, pos.y, pos.z << STBVOX_CONFIG_PRECISION_Z, 0,0); + if (mm->input.selector) { + mesh = mm->input.selector[v_off]; + } + + // check if we're going off the end + if (mm->output_cur[mesh][0] + mm->output_size[mesh][0]*6 > mm->output_end[mesh][0]) { + mm->full = 1; + return; + } + + if (geo >= STBVOX_GEOM_floor_slope_north_is_top) { + if (visible_faces & (1 << STBVOX_FACE_up)) { + int normal = geo == STBVOX_GEOM_floor_slope_north_is_top ? stbvox_floor_slope_for_rot[simple_rot] : STBVOX_FACE_up; + rotate.facerot = simple_rot; + stbvox_make_mesh_for_face(mm, rotate, STBVOX_FACE_up , v_off, pos, basevert, vmesh[STBVOX_FACE_up], mesh, normal); + } + if (visible_faces & (1 << STBVOX_FACE_down)) { + int normal = geo == STBVOX_GEOM_ceil_slope_north_is_bottom ? stbvox_ceil_slope_for_rot[simple_rot] : STBVOX_FACE_down; + rotate.facerot = (-rotate.facerot) & 3; + stbvox_make_mesh_for_face(mm, rotate, STBVOX_FACE_down, v_off, pos, basevert, vmesh[STBVOX_FACE_down], mesh, normal); + } + } else { + if (visible_faces & (1 << STBVOX_FACE_up)) { + rotate.facerot = simple_rot; + stbvox_make_mesh_for_face(mm, rotate, STBVOX_FACE_up , v_off, pos, basevert, vmesh[STBVOX_FACE_up], mesh, STBVOX_FACE_up); + } + if (visible_faces & (1 << STBVOX_FACE_down)) { + rotate.facerot = (-rotate.facerot) & 3; + stbvox_make_mesh_for_face(mm, rotate, STBVOX_FACE_down, v_off, pos, basevert, vmesh[STBVOX_FACE_down], mesh, STBVOX_FACE_down); + } + } + + if (mm->input.rotate) { + unsigned char val = mm->input.rotate[v_off]; + rotate.block = (val >> 0) & 3; + rotate.overlay = (val >> 2) & 3; + //rotate.tex2 = (val >> 4) & 3; + rotate.ecolor = (val >> 6) & 3; + } else { + rotate.block = rotate.overlay = rotate.ecolor = simple_rot; + } + + rotate.facerot = 0; + + if (visible_faces & (1 << STBVOX_FACE_north)) + stbvox_make_mesh_for_face(mm, rotate, STBVOX_FACE_north, v_off, pos, basevert, vmesh[STBVOX_FACE_north], mesh, STBVOX_FACE_north); + if (visible_faces & (1 << STBVOX_FACE_south)) + stbvox_make_mesh_for_face(mm, rotate, STBVOX_FACE_south, v_off, pos, basevert, vmesh[STBVOX_FACE_south], mesh, STBVOX_FACE_south); + if (visible_faces & (1 << STBVOX_FACE_east)) + stbvox_make_mesh_for_face(mm, rotate, STBVOX_FACE_east , v_off, pos, basevert, vmesh[STBVOX_FACE_east ], mesh, STBVOX_FACE_east); + if (visible_faces & (1 << STBVOX_FACE_west)) + stbvox_make_mesh_for_face(mm, rotate, STBVOX_FACE_west , v_off, pos, basevert, vmesh[STBVOX_FACE_west ], mesh, STBVOX_FACE_west); + } + if (geo >= STBVOX_GEOM_floor_vheight_03) { + // this case can also be generated with regular block gen with special vmesh, + // except: + // if we want to generate middle diagonal for 'weird' blocks + // it's more complicated to detect neighbor matchups + stbvox_mesh_vertex vmesh[6][4]; + stbvox_mesh_vertex cube[8]; + stbvox_mesh_vertex basevert; + stbvox_rotate rotate = { 0,0,0,0 }; + unsigned char simple_rot = rot; + unsigned char ht[4]; + int extreme; + + // extract the heights + #ifdef STBVOX_CONFIG_VHEIGHT_IN_LIGHTING + ht[0] = mm->input.lighting[v_off ] & 3; + ht[1] = mm->input.lighting[v_off+ew_off ] & 3; + ht[2] = mm->input.lighting[v_off +ns_off] & 3; + ht[3] = mm->input.lighting[v_off+ew_off+ns_off] & 3; + #else + if (mm->input.vheight) { + unsigned char v = mm->input.vheight[v_off]; + ht[0] = (v >> 0) & 3; + ht[1] = (v >> 2) & 3; + ht[2] = (v >> 4) & 3; + ht[3] = (v >> 6) & 3; + } else if (mm->input.block_vheight) { + unsigned char v = mm->input.block_vheight[bt]; + unsigned char raw[4]; + int i; + + raw[0] = (v >> 0) & 3; + raw[1] = (v >> 2) & 3; + raw[2] = (v >> 4) & 3; + raw[3] = (v >> 6) & 3; + + for (i=0; i < 4; ++i) + ht[i] = raw[stbvox_rotate_vertex[i][rot]]; + } else if (mm->input.packed_compact) { + ht[0] = (mm->input.packed_compact[v_off ] >> 2) & 3; + ht[1] = (mm->input.packed_compact[v_off+ew_off ] >> 2) & 3; + ht[2] = (mm->input.packed_compact[v_off +ns_off] >> 2) & 3; + ht[3] = (mm->input.packed_compact[v_off+ew_off+ns_off] >> 2) & 3; + } else if (mm->input.geometry) { + ht[0] = mm->input.geometry[v_off ] >> 6; + ht[1] = mm->input.geometry[v_off+ew_off ] >> 6; + ht[2] = mm->input.geometry[v_off +ns_off] >> 6; + ht[3] = mm->input.geometry[v_off+ew_off+ns_off] >> 6; + } else { + assert(0); + } + #endif + + // flag whether any sides go off the top of the block, which means + // our visible_faces test was wrong + extreme = (ht[0] == 3 || ht[1] == 3 || ht[2] == 3 || ht[3] == 3); + + if (geo >= STBVOX_GEOM_ceil_vheight_03) { + cube[0] = stbvox_vertex_encode(0,0,ht[0],0,0); + cube[1] = stbvox_vertex_encode(0,0,ht[1],0,0); + cube[2] = stbvox_vertex_encode(0,0,ht[2],0,0); + cube[3] = stbvox_vertex_encode(0,0,ht[3],0,0); + cube[4] = stbvox_vertex_encode(0,0,2,0,0); + cube[5] = stbvox_vertex_encode(0,0,2,0,0); + cube[6] = stbvox_vertex_encode(0,0,2,0,0); + cube[7] = stbvox_vertex_encode(0,0,2,0,0); + } else { + cube[0] = stbvox_vertex_encode(0,0,0,0,0); + cube[1] = stbvox_vertex_encode(0,0,0,0,0); + cube[2] = stbvox_vertex_encode(0,0,0,0,0); + cube[3] = stbvox_vertex_encode(0,0,0,0,0); + cube[4] = stbvox_vertex_encode(0,0,ht[0],0,0); + cube[5] = stbvox_vertex_encode(0,0,ht[1],0,0); + cube[6] = stbvox_vertex_encode(0,0,ht[2],0,0); + cube[7] = stbvox_vertex_encode(0,0,ht[3],0,0); + } + if (!mm->input.vheight && mm->input.block_vheight) { + // @TODO: support block vheight here, I've forgotten what needs to be done specially + } + + // build vertex mesh + { + int i; + for (i=0; i < 6*4; ++i) { + int vert = stbvox_vertex_selector[0][i]; + vmesh[0][i] = stbvox_vmesh_pre_vheight[0][i] + + cube[vert]; + } + } + + basevert = stbvox_vertex_encode(pos.x, pos.y, pos.z << STBVOX_CONFIG_PRECISION_Z, 0,0); + // check if we're going off the end + if (mm->output_cur[mesh][0] + mm->output_size[mesh][0]*6 > mm->output_end[mesh][0]) { + mm->full = 1; + return; + } + + // @TODO generate split faces + if (visible_faces & (1 << STBVOX_FACE_up)) { + if (geo >= STBVOX_GEOM_ceil_vheight_03) + // flat + stbvox_make_mesh_for_face(mm, rotate, STBVOX_FACE_up , v_off, pos, basevert, vmesh[STBVOX_FACE_up], mesh, STBVOX_FACE_up); + else { + #ifndef STBVOX_CONFIG_OPTIMIZED_VHEIGHT + // check if it's non-planar + if (cube[5] + cube[6] != cube[4] + cube[7]) { + // not planar, split along diagonal and make degenerate quads + if (geo == STBVOX_GEOM_floor_vheight_03) + stbvox_make_03_split_mesh_for_face(mm, rotate, STBVOX_FACE_up, v_off, pos, basevert, vmesh[STBVOX_FACE_up], mesh, ht); + else + stbvox_make_12_split_mesh_for_face(mm, rotate, STBVOX_FACE_up, v_off, pos, basevert, vmesh[STBVOX_FACE_up], mesh, ht); + } else + stbvox_make_mesh_for_face(mm, rotate, STBVOX_FACE_up , v_off, pos, basevert, vmesh[STBVOX_FACE_up], mesh, stbvox_planar_face_up_normal[ht[2]][ht[1]][ht[0]]); + #else + stbvox_make_mesh_for_face(mm, rotate, STBVOX_FACE_up , v_off, pos, basevert, vmesh[STBVOX_FACE_up], mesh, stbvox_optimized_face_up_normal[ht[3]][ht[2]][ht[1]][ht[0]]); + #endif + } + } + if (visible_faces & (1 << STBVOX_FACE_down)) { + if (geo < STBVOX_GEOM_ceil_vheight_03) + // flat + stbvox_make_mesh_for_face(mm, rotate, STBVOX_FACE_down, v_off, pos, basevert, vmesh[STBVOX_FACE_down], mesh, STBVOX_FACE_down); + else { + #ifndef STBVOX_CONFIG_OPTIMIZED_VHEIGHT + // check if it's non-planar + if (cube[1] + cube[2] != cube[0] + cube[3]) { + // not planar, split along diagonal and make degenerate quads + if (geo == STBVOX_GEOM_ceil_vheight_03) + stbvox_make_03_split_mesh_for_face(mm, rotate, STBVOX_FACE_down, v_off, pos, basevert, vmesh[STBVOX_FACE_down], mesh, ht); + else + stbvox_make_12_split_mesh_for_face(mm, rotate, STBVOX_FACE_down, v_off, pos, basevert, vmesh[STBVOX_FACE_down], mesh, ht); + } else + stbvox_make_mesh_for_face(mm, rotate, STBVOX_FACE_down, v_off, pos, basevert, vmesh[STBVOX_FACE_down], mesh, stbvox_reverse_face[stbvox_planar_face_up_normal[ht[2]][ht[1]][ht[0]]]); + #else + stbvox_make_mesh_for_face(mm, rotate, STBVOX_FACE_down, v_off, pos, basevert, vmesh[STBVOX_FACE_down], mesh, stbvox_reverse_face[stbvox_optimized_face_up_normal[ht[3]][ht[2]][ht[1]][ht[0]]]); + #endif + } + } + + if (mm->input.rotate) { + unsigned char val = mm->input.rotate[v_off]; + rotate.block = (val >> 0) & 3; + rotate.overlay = (val >> 2) & 3; + //rotate.tex2 = (val >> 4) & 3; + rotate.ecolor = (val >> 6) & 3; + } else if (mm->input.selector) { + rotate.block = rotate.overlay = rotate.ecolor = simple_rot; + } + + if ((visible_faces & (1 << STBVOX_FACE_north)) || (extreme && (ht[2] == 3 || ht[3] == 3))) + stbvox_make_mesh_for_face(mm, rotate, STBVOX_FACE_north, v_off, pos, basevert, vmesh[STBVOX_FACE_north], mesh, STBVOX_FACE_north); + if ((visible_faces & (1 << STBVOX_FACE_south)) || (extreme && (ht[0] == 3 || ht[1] == 3))) + stbvox_make_mesh_for_face(mm, rotate, STBVOX_FACE_south, v_off, pos, basevert, vmesh[STBVOX_FACE_south], mesh, STBVOX_FACE_south); + if ((visible_faces & (1 << STBVOX_FACE_east)) || (extreme && (ht[1] == 3 || ht[3] == 3))) + stbvox_make_mesh_for_face(mm, rotate, STBVOX_FACE_east , v_off, pos, basevert, vmesh[STBVOX_FACE_east ], mesh, STBVOX_FACE_east); + if ((visible_faces & (1 << STBVOX_FACE_west)) || (extreme && (ht[0] == 3 || ht[2] == 3))) + stbvox_make_mesh_for_face(mm, rotate, STBVOX_FACE_west , v_off, pos, basevert, vmesh[STBVOX_FACE_west ], mesh, STBVOX_FACE_west); + } + + if (geo == STBVOX_GEOM_crossed_pair) { + // this can be generated with a special vmesh + stbvox_mesh_vertex basevert = stbvox_vertex_encode(pos.x, pos.y, pos.z << STBVOX_CONFIG_PRECISION_Z , 0,0); + unsigned char simple_rot=0; + stbvox_rotate rot = { 0,0,0,0 }; + unsigned char mesh = mm->default_mesh; + if (mm->input.selector) { + mesh = mm->input.selector[v_off]; + simple_rot = mesh >> 4; + mesh &= 15; + } + + // check if we're going off the end + if (mm->output_cur[mesh][0] + mm->output_size[mesh][0]*4 > mm->output_end[mesh][0]) { + mm->full = 1; + return; + } + + if (mm->input.rotate) { + unsigned char val = mm->input.rotate[v_off]; + rot.block = (val >> 0) & 3; + rot.overlay = (val >> 2) & 3; + //rot.tex2 = (val >> 4) & 3; + rot.ecolor = (val >> 6) & 3; + } else if (mm->input.selector) { + rot.block = rot.overlay = rot.ecolor = simple_rot; + } + rot.facerot = 0; + + stbvox_make_mesh_for_face(mm, rot, STBVOX_FACE_north, v_off, pos, basevert, stbvox_vmesh_crossed_pair[STBVOX_FACE_north], mesh, STBVF_ne_u_cross); + stbvox_make_mesh_for_face(mm, rot, STBVOX_FACE_south, v_off, pos, basevert, stbvox_vmesh_crossed_pair[STBVOX_FACE_south], mesh, STBVF_sw_u_cross); + stbvox_make_mesh_for_face(mm, rot, STBVOX_FACE_east , v_off, pos, basevert, stbvox_vmesh_crossed_pair[STBVOX_FACE_east ], mesh, STBVF_se_u_cross); + stbvox_make_mesh_for_face(mm, rot, STBVOX_FACE_west , v_off, pos, basevert, stbvox_vmesh_crossed_pair[STBVOX_FACE_west ], mesh, STBVF_nw_u_cross); + } + + + // @TODO + // STBVOX_GEOM_floor_slope_north_is_top_as_wall, + // STBVOX_GEOM_ceil_slope_north_is_bottom_as_wall, +} + +static void stbvox_make_mesh_for_column(stbvox_mesh_maker *mm, int x, int y, int z0) +{ + stbvox_pos pos; + int v_off = x * mm->x_stride_in_bytes + y * mm->y_stride_in_bytes; + int ns_off = mm->y_stride_in_bytes; + int ew_off = mm->x_stride_in_bytes; + pos.x = x; + pos.y = y; + pos.z = 0; + if (mm->input.geometry) { + unsigned char *bt = mm->input.blocktype + v_off; + unsigned char *geo = mm->input.geometry + v_off; + int z; + for (z=z0; z < mm->z1; ++z) { + if (bt[z] && ( !bt[z+ns_off] || !STBVOX_GET_GEO(geo[z+ns_off]) || !bt[z-ns_off] || !STBVOX_GET_GEO(geo[z-ns_off]) + || !bt[z+ew_off] || !STBVOX_GET_GEO(geo[z+ew_off]) || !bt[z-ew_off] || !STBVOX_GET_GEO(geo[z-ew_off]) + || !bt[z-1] || !STBVOX_GET_GEO(geo[z-1]) || !bt[z+1] || !STBVOX_GET_GEO(geo[z+1]))) + { // TODO check up and down + pos.z = z; + stbvox_make_mesh_for_block_with_geo(mm, pos, v_off+z); + if (mm->full) { + mm->cur_z = z; + return; + } + } + } + } else if (mm->input.block_geometry) { + int z; + unsigned char *bt = mm->input.blocktype + v_off; + unsigned char *geo = mm->input.block_geometry; + for (z=z0; z < mm->z1; ++z) { + if (bt[z] && ( geo[bt[z+ns_off]] != STBVOX_GEOM_solid + || geo[bt[z-ns_off]] != STBVOX_GEOM_solid + || geo[bt[z+ew_off]] != STBVOX_GEOM_solid + || geo[bt[z-ew_off]] != STBVOX_GEOM_solid + || geo[bt[z-1]] != STBVOX_GEOM_solid + || geo[bt[z+1]] != STBVOX_GEOM_solid)) + { + pos.z = z; + stbvox_make_mesh_for_block_with_geo(mm, pos, v_off+z); + if (mm->full) { + mm->cur_z = z; + return; + } + } + } + } else { + unsigned char *bt = mm->input.blocktype + v_off; + int z; + #if STBVOX_CONFIG_PRECISION_Z == 1 + stbvox_mesh_vertex *vmesh = stbvox_vmesh_delta_half_z[0]; + #else + stbvox_mesh_vertex *vmesh = stbvox_vmesh_delta_normal[0]; + #endif + for (z=z0; z < mm->z1; ++z) { + // if it's solid and at least one neighbor isn't solid + if (bt[z] && (!bt[z+ns_off] || !bt[z-ns_off] || !bt[z+ew_off] || !bt[z-ew_off] || !bt[z-1] || !bt[z+1])) { + pos.z = z; + stbvox_make_mesh_for_block(mm, pos, v_off+z, vmesh); + if (mm->full) { + mm->cur_z = z; + return; + } + } + } + } +} + +static void stbvox_bring_up_to_date(stbvox_mesh_maker *mm) +{ + if (mm->config_dirty) { + int i; + #ifdef STBVOX_ICONFIG_FACE_ATTRIBUTE + mm->num_mesh_slots = 1; + for (i=0; i < STBVOX_MAX_MESHES; ++i) { + mm->output_size[i][0] = 32; + mm->output_step[i][0] = 8; + } + #else + mm->num_mesh_slots = 2; + for (i=0; i < STBVOX_MAX_MESHES; ++i) { + mm->output_size[i][0] = 16; + mm->output_step[i][0] = 4; + mm->output_size[i][1] = 4; + mm->output_step[i][1] = 4; + } + #endif + + mm->config_dirty = 0; + } +} + +int stbvox_make_mesh(stbvox_mesh_maker *mm) +{ + int x,y; + stbvox_bring_up_to_date(mm); + mm->full = 0; + if (mm->cur_x > mm->x0 || mm->cur_y > mm->y0 || mm->cur_z > mm->z0) { + stbvox_make_mesh_for_column(mm, mm->cur_x, mm->cur_y, mm->cur_z); + if (mm->full) + return 0; + ++mm->cur_y; + while (mm->cur_y < mm->y1 && !mm->full) { + stbvox_make_mesh_for_column(mm, mm->cur_x, mm->cur_y, mm->z0); + if (mm->full) + return 0; + ++mm->cur_y; + } + ++mm->cur_x; + } + for (x=mm->cur_x; x < mm->x1; ++x) { + for (y=mm->y0; y < mm->y1; ++y) { + stbvox_make_mesh_for_column(mm, x, y, mm->z0); + if (mm->full) { + mm->cur_x = x; + mm->cur_y = y; + return 0; + } + } + } + return 1; +} + +void stbvox_init_mesh_maker(stbvox_mesh_maker *mm) +{ + memset(mm, 0, sizeof(*mm)); + stbvox_build_default_palette(); + + mm->config_dirty = 1; + mm->default_mesh = 0; +} + +int stbvox_get_buffer_count(stbvox_mesh_maker *mm) +{ + stbvox_bring_up_to_date(mm); + return mm->num_mesh_slots; +} + +int stbvox_get_buffer_size_per_quad(stbvox_mesh_maker *mm, int n) +{ + return mm->output_size[0][n]; +} + +void stbvox_reset_buffers(stbvox_mesh_maker *mm) +{ + int i; + for (i=0; i < STBVOX_MAX_MESHES*STBVOX_MAX_MESH_SLOTS; ++i) { + mm->output_cur[0][i] = 0; + mm->output_buffer[0][i] = 0; + } +} + +void stbvox_set_buffer(stbvox_mesh_maker *mm, int mesh, int slot, void *buffer, size_t len) +{ + int i; + stbvox_bring_up_to_date(mm); + mm->output_buffer[mesh][slot] = (char *) buffer; + mm->output_cur [mesh][slot] = (char *) buffer; + mm->output_len [mesh][slot] = len; + mm->output_end [mesh][slot] = (char *) buffer + len; + for (i=0; i < STBVOX_MAX_MESH_SLOTS; ++i) { + if (mm->output_buffer[mesh][i]) { + assert(mm->output_len[mesh][i] / mm->output_size[mesh][i] == mm->output_len[mesh][slot] / mm->output_size[mesh][slot]); + } + } +} + +void stbvox_set_default_mesh(stbvox_mesh_maker *mm, int mesh) +{ + mm->default_mesh = mesh; +} + +int stbvox_get_quad_count(stbvox_mesh_maker *mm, int mesh) +{ + return (mm->output_cur[mesh][0] - mm->output_buffer[mesh][0]) / mm->output_size[mesh][0]; +} + +stbvox_input_description *stbvox_get_input_description(stbvox_mesh_maker *mm) +{ + return &mm->input; +} + +void stbvox_set_input_range(stbvox_mesh_maker *mm, int x0, int y0, int z0, int x1, int y1, int z1) +{ + mm->x0 = x0; + mm->y0 = y0; + mm->z0 = z0; + + mm->x1 = x1; + mm->y1 = y1; + mm->z1 = z1; + + mm->cur_x = x0; + mm->cur_y = y0; + mm->cur_z = z0; + + // @TODO validate that this range is representable in this mode +} + +void stbvox_get_transform(stbvox_mesh_maker *mm, float transform[3][3]) +{ + // scale + transform[0][0] = 1.0; + transform[0][1] = 1.0; + #if STBVOX_CONFIG_PRECISION_Z==1 + transform[0][2] = 0.5f; + #else + transform[0][2] = 1.0f; + #endif + // translation + transform[1][0] = (float) (mm->pos_x); + transform[1][1] = (float) (mm->pos_y); + transform[1][2] = (float) (mm->pos_z); + // texture coordinate projection translation + transform[2][0] = (float) (mm->pos_x & 255); // @TODO depends on max texture scale + transform[2][1] = (float) (mm->pos_y & 255); + transform[2][2] = (float) (mm->pos_z & 255); +} + +void stbvox_get_bounds(stbvox_mesh_maker *mm, float bounds[2][3]) +{ + bounds[0][0] = (float) (mm->pos_x + mm->x0); + bounds[0][1] = (float) (mm->pos_y + mm->y0); + bounds[0][2] = (float) (mm->pos_z + mm->z0); + bounds[1][0] = (float) (mm->pos_x + mm->x1); + bounds[1][1] = (float) (mm->pos_y + mm->y1); + bounds[1][2] = (float) (mm->pos_z + mm->z1); +} + +void stbvox_set_mesh_coordinates(stbvox_mesh_maker *mm, int x, int y, int z) +{ + mm->pos_x = x; + mm->pos_y = y; + mm->pos_z = z; +} + +void stbvox_set_input_stride(stbvox_mesh_maker *mm, int x_stride_in_bytes, int y_stride_in_bytes) +{ + int f,v; + mm->x_stride_in_bytes = x_stride_in_bytes; + mm->y_stride_in_bytes = y_stride_in_bytes; + for (f=0; f < 6; ++f) { + for (v=0; v < 4; ++v) { + mm->cube_vertex_offset[f][v] = stbvox_vertex_vector[f][v][0] * mm->x_stride_in_bytes + + stbvox_vertex_vector[f][v][1] * mm->y_stride_in_bytes + + stbvox_vertex_vector[f][v][2] ; + mm->vertex_gather_offset[f][v] = (stbvox_vertex_vector[f][v][0]-1) * mm->x_stride_in_bytes + + (stbvox_vertex_vector[f][v][1]-1) * mm->y_stride_in_bytes + + (stbvox_vertex_vector[f][v][2]-1) ; + } + } +} + +///////////////////////////////////////////////////////////////////////////// +// +// offline computation of tables +// + +#if 0 +// compute optimized vheight table +static char *normal_names[32] = +{ + 0,0,0,0,"u ",0, "eu ",0, + 0,0,0,0,"ne_u",0, "nu ",0, + 0,0,0,0,"nw_u",0, "wu ",0, + 0,0,0,0,"sw_u",0, "su ",0, +}; + +static char *find_best_normal(float x, float y, float z) +{ + int best_slot = 4; + float best_dot = 0; + int i; + for (i=0; i < 32; ++i) { + if (normal_names[i]) { + float dot = x * stbvox_default_normals[i][0] + y * stbvox_default_normals[i][1] + z * stbvox_default_normals[i][2]; + if (dot > best_dot) { + best_dot = dot; + best_slot = i; + } + } + } + return normal_names[best_slot]; +} + +int main(int argc, char **argv) +{ + int sw,se,nw,ne; + for (ne=0; ne < 4; ++ne) { + for (nw=0; nw < 4; ++nw) { + for (se=0; se < 4; ++se) { + printf(" { "); + for (sw=0; sw < 4; ++sw) { + float x = (float) (nw + sw - ne - se); + float y = (float) (sw + se - nw - ne); + float z = 2; + printf("STBVF_%s, ", find_best_normal(x,y,z)); + } + printf("},\n"); + } + } + } + return 0; +} +#endif + +// @TODO +// +// - test API for texture rotation on side faces +// - API for texture rotation on top & bottom +// - better culling of vheight faces with vheight neighbors +// - better culling of non-vheight faces with vheight neighbors +// - gather vertex lighting from slopes correctly +// - better support texture edge_clamp: currently if you fall +// exactly on 1.0 you get wrapped incorrectly; this is rare, but +// can avoid: compute texcoords in vertex shader, offset towards +// center before modding, need 2 bits per vertex to know offset direction) +// - other mesh modes (10,6,4-byte quads) +// +// +// With TexBuffer for the fixed vertex data, we can actually do +// minecrafty non-blocks like stairs -- we still probably only +// want 256 or so, so we can't do the equivalent of all the vheight +// combos, but that's ok. The 256 includes baked rotations, but only +// some of them need it, and lots of block types share some faces. +// +// mode 5 (6 bytes): mode 6 (6 bytes) +// x:7 x:6 +// y:7 y:6 +// z:6 z:6 +// tex1:8 tex1:8 +// tex2:8 tex2:7 +// color:8 color:8 +// face:4 face:7 +// +// +// side faces (all x4) top&bottom faces (2x) internal faces (1x) +// 1 regular 1 regular +// 2 slabs 2 +// 8 stairs 4 stairs 16 +// 4 diag side 8 +// 4 upper diag side 8 +// 4 lower diag side 8 +// 4 crossed pairs +// +// 23*4 + 5*4 + 46 +// == 92 + 20 + 46 = 158 +// +// Must drop 30 of them to fit in 7 bits: +// ceiling half diagonals: 16+8 = 24 +// Need to get rid of 6 more. +// ceiling diagonals: 8+4 = 12 +// This brings it to 122, so can add a crossed-pair variant. +// (diagonal and non-diagonal, or randomly offset) +// Or carpet, which would be 5 more. +// +// +// Mode 4 (10 bytes): +// v: z:2,light:6 +// f: x:6,y:6,z:7, t1:8,t2:8,c:8,f:5 +// +// Mode ? (10 bytes) +// v: xyz:5 (27 values), light:3 +// f: x:7,y:7,z:6, t1:8,t2:8,c:8,f:4 +// (v: x:2,y:2,z:2,light:2) + +#endif // STB_VOXEL_RENDER_IMPLEMENTATION diff --git a/impeller/third_party/stb/stb/stretchy_buffer.h b/impeller/third_party/stb/stb/stretchy_buffer.h new file mode 100644 index 0000000000000..9b47a670824be --- /dev/null +++ b/impeller/third_party/stb/stb/stretchy_buffer.h @@ -0,0 +1,216 @@ +// stretchy_buffer.h - v1.02 - public domain - nothings.org/stb +// a vector<>-like dynamic array for C +// +// version history: +// 1.02 - tweaks to syntax for no good reason +// 1.01 - added a "common uses" documentation section +// 1.0 - fixed bug in the version I posted prematurely +// 0.9 - rewrite to try to avoid strict-aliasing optimization +// issues, but won't compile as C++ +// +// Will probably not work correctly with strict-aliasing optimizations. +// +// The idea: +// +// This implements an approximation to C++ vector<> for C, in that it +// provides a generic definition for dynamic arrays which you can +// still access in a typesafe way using arr[i] or *(arr+i). However, +// it is simply a convenience wrapper around the common idiom of +// of keeping a set of variables (in a struct or globals) which store +// - pointer to array +// - the length of the "in-use" part of the array +// - the current size of the allocated array +// +// I find it to be single most useful non-built-in-structure when +// programming in C (hash tables a close second), but to be clear +// it lacks many of the capabilities of C++ vector<>: there is no +// range checking, the object address isn't stable (see next section +// for details), the set of methods available is small (although +// the file stb.h has another implementation of stretchy buffers +// called 'stb_arr' which provides more methods, e.g. for insertion +// and deletion). +// +// How to use: +// +// Unlike other stb header file libraries, there is no need to +// define an _IMPLEMENTATION symbol. Every #include creates as +// much implementation is needed. +// +// stretchy_buffer.h does not define any types, so you do not +// need to #include it to before defining data types that are +// stretchy buffers, only in files that *manipulate* stretchy +// buffers. +// +// If you want a stretchy buffer aka dynamic array containing +// objects of TYPE, declare such an array as: +// +// TYPE *myarray = NULL; +// +// (There is no typesafe way to distinguish between stretchy +// buffers and regular arrays/pointers; this is necessary to +// make ordinary array indexing work on these objects.) +// +// Unlike C++ vector<>, the stretchy_buffer has the same +// semantics as an object that you manually malloc and realloc. +// The pointer may relocate every time you add a new object +// to it, so you: +// +// 1. can't take long-term pointers to elements of the array +// 2. have to return the pointer from functions which might expand it +// (either as a return value or by passing it back) +// +// Now you can do the following things with this array: +// +// sb_free(TYPE *a) free the array +// sb_count(TYPE *a) the number of elements in the array +// sb_push(TYPE *a, TYPE v) adds v on the end of the array, a la push_back +// sb_add(TYPE *a, int n) adds n uninitialized elements at end of array & returns pointer to first added +// sb_last(TYPE *a) returns an lvalue of the last item in the array +// a[n] access the nth (counting from 0) element of the array +// +// #define STRETCHY_BUFFER_NO_SHORT_NAMES to only export +// names of the form 'stb_sb_' if you have a name that would +// otherwise collide. +// +// Note that these are all macros and many of them evaluate +// their arguments more than once, so the arguments should +// be side-effect-free. +// +// Note that 'TYPE *a' in sb_push and sb_add must be lvalues +// so that the library can overwrite the existing pointer if +// the object has to be reallocated. +// +// In an out-of-memory condition, the code will try to +// set up a null-pointer or otherwise-invalid-pointer +// exception to happen later. It's possible optimizing +// compilers could detect this write-to-null statically +// and optimize away some of the code, but it should only +// be along the failure path. Nevertheless, for more security +// in the face of such compilers, #define STRETCHY_BUFFER_OUT_OF_MEMORY +// to a statement such as assert(0) or exit(1) or something +// to force a failure when out-of-memory occurs. +// +// Common use: +// +// The main application for this is when building a list of +// things with an unknown quantity, either due to loading from +// a file or through a process which produces an unpredictable +// number. +// +// My most common idiom is something like: +// +// SomeStruct *arr = NULL; +// while (something) +// { +// SomeStruct new_one; +// new_one.whatever = whatever; +// new_one.whatup = whatup; +// new_one.foobar = barfoo; +// sb_push(arr, new_one); +// } +// +// and various closely-related factorings of that. For example, +// you might have several functions to create/init new SomeStructs, +// and if you use the above idiom, you might prefer to make them +// return structs rather than take non-const-pointers-to-structs, +// so you can do things like: +// +// SomeStruct *arr = NULL; +// while (something) +// { +// if (case_A) { +// sb_push(arr, some_func1()); +// } else if (case_B) { +// sb_push(arr, some_func2()); +// } else { +// sb_push(arr, some_func3()); +// } +// } +// +// Note that the above relies on the fact that sb_push doesn't +// evaluate its second argument more than once. The macros do +// evaluate the *array* argument multiple times, and numeric +// arguments may be evaluated multiple times, but you can rely +// on the second argument of sb_push being evaluated only once. +// +// Of course, you don't have to store bare objects in the array; +// if you need the objects to have stable pointers, store an array +// of pointers instead: +// +// SomeStruct **arr = NULL; +// while (something) +// { +// SomeStruct *new_one = malloc(sizeof(*new_one)); +// new_one->whatever = whatever; +// new_one->whatup = whatup; +// new_one->foobar = barfoo; +// sb_push(arr, new_one); +// } +// +// How it works: +// +// A long-standing tradition in things like malloc implementations +// is to store extra data before the beginning of the block returned +// to the user. The stretchy buffer implementation here uses the +// same trick; the current-count and current-allocation-size are +// stored before the beginning of the array returned to the user. +// (This means you can't directly free() the pointer, because the +// allocated pointer is different from the type-safe pointer provided +// to the user.) +// +// The details are trivial and implementation is straightforward; +// the main trick is in realizing in the first place that it's +// possible to do this in a generic, type-safe way in C. +// +// LICENSE +// +// This software is dual-licensed to the public domain and under the following +// license: you are granted a perpetual, irrevocable license to copy, modify, +// publish, and distribute this file as you see fit. + +#ifndef STB_STRETCHY_BUFFER_H_INCLUDED +#define STB_STRETCHY_BUFFER_H_INCLUDED + +#ifndef NO_STRETCHY_BUFFER_SHORT_NAMES +#define sb_free stb_sb_free +#define sb_push stb_sb_push +#define sb_count stb_sb_count +#define sb_add stb_sb_add +#define sb_last stb_sb_last +#endif + +#define stb_sb_free(a) ((a) ? free(stb__sbraw(a)),0 : 0) +#define stb_sb_push(a,v) (stb__sbmaybegrow(a,1), (a)[stb__sbn(a)++] = (v)) +#define stb_sb_count(a) ((a) ? stb__sbn(a) : 0) +#define stb_sb_add(a,n) (stb__sbmaybegrow(a,n), stb__sbn(a)+=(n), &(a)[stb__sbn(a)-(n)]) +#define stb_sb_last(a) ((a)[stb__sbn(a)-1]) + +#define stb__sbraw(a) ((int *) (a) - 2) +#define stb__sbm(a) stb__sbraw(a)[0] +#define stb__sbn(a) stb__sbraw(a)[1] + +#define stb__sbneedgrow(a,n) ((a)==0 || stb__sbn(a)+(n) >= stb__sbm(a)) +#define stb__sbmaybegrow(a,n) (stb__sbneedgrow(a,(n)) ? stb__sbgrow(a,n) : 0) +#define stb__sbgrow(a,n) ((a) = stb__sbgrowf((a), (n), sizeof(*(a)))) + +#include + +static void * stb__sbgrowf(void *arr, int increment, int itemsize) +{ + int dbl_cur = arr ? 2*stb__sbm(arr) : 0; + int min_needed = stb_sb_count(arr) + increment; + int m = dbl_cur > min_needed ? dbl_cur : min_needed; + int *p = (int *) realloc(arr ? stb__sbraw(arr) : 0, itemsize * m + sizeof(int)*2); + if (p) { + if (!arr) + p[1] = 0; + p[0] = m; + return p+2; + } else { + #ifdef STRETCHY_BUFFER_OUT_OF_MEMORY + STRETCHY_BUFFER_OUT_OF_MEMORY ; + #endif + return (void *) (2*sizeof(int)); // try to force a NULL pointer exception later + } +} +#endif // STB_STRETCHY_BUFFER_H_INCLUDED diff --git a/impeller/third_party/stb/stb/tests/c_lexer_test.c b/impeller/third_party/stb/stb/tests/c_lexer_test.c new file mode 100644 index 0000000000000..fda7d7579c2e9 --- /dev/null +++ b/impeller/third_party/stb/stb/tests/c_lexer_test.c @@ -0,0 +1,3 @@ +#define STB_C_LEXER_IMPLEMENTATION +#define STB_C_LEXER_SELF_TEST +#include "../stb_c_lexer.h" diff --git a/impeller/third_party/stb/stb/tests/c_lexer_test.dsp b/impeller/third_party/stb/stb/tests/c_lexer_test.dsp new file mode 100644 index 0000000000000..13f87588b7b69 --- /dev/null +++ b/impeller/third_party/stb/stb/tests/c_lexer_test.dsp @@ -0,0 +1,89 @@ +# Microsoft Developer Studio Project File - Name="c_lexer_test" - Package Owner=<4> +# Microsoft Developer Studio Generated Build File, Format Version 6.00 +# ** DO NOT EDIT ** + +# TARGTYPE "Win32 (x86) Console Application" 0x0103 + +CFG=c_lexer_test - Win32 Debug +!MESSAGE This is not a valid makefile. To build this project using NMAKE, +!MESSAGE use the Export Makefile command and run +!MESSAGE +!MESSAGE NMAKE /f "c_lexer_test.mak". +!MESSAGE +!MESSAGE You can specify a configuration when running NMAKE +!MESSAGE by defining the macro CFG on the command line. For example: +!MESSAGE +!MESSAGE NMAKE /f "c_lexer_test.mak" CFG="c_lexer_test - Win32 Debug" +!MESSAGE +!MESSAGE Possible choices for configuration are: +!MESSAGE +!MESSAGE "c_lexer_test - Win32 Release" (based on "Win32 (x86) Console Application") +!MESSAGE "c_lexer_test - Win32 Debug" (based on "Win32 (x86) Console Application") +!MESSAGE + +# Begin Project +# PROP AllowPerConfigDependencies 0 +# PROP Scc_ProjName "" +# PROP Scc_LocalPath "" +CPP=cl.exe +RSC=rc.exe + +!IF "$(CFG)" == "c_lexer_test - Win32 Release" + +# PROP BASE Use_MFC 0 +# PROP BASE Use_Debug_Libraries 0 +# PROP BASE Output_Dir "Release" +# PROP BASE Intermediate_Dir "Release" +# PROP BASE Target_Dir "" +# PROP Use_MFC 0 +# PROP Use_Debug_Libraries 0 +# PROP Output_Dir "Release" +# PROP Intermediate_Dir "Release" +# PROP Target_Dir "" +# ADD BASE CPP /nologo /W3 /GX /O2 /D "WIN32" /D "NDEBUG" /D "_CONSOLE" /D "_MBCS" /YX /FD /c +# ADD CPP /nologo /W3 /GX /O2 /D "WIN32" /D "NDEBUG" /D "_CONSOLE" /D "_MBCS" /YX /FD /c +# ADD BASE RSC /l 0x409 /d "NDEBUG" +# ADD RSC /l 0x409 /d "NDEBUG" +BSC32=bscmake.exe +# ADD BASE BSC32 /nologo +# ADD BSC32 /nologo +LINK32=link.exe +# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:console /machine:I386 +# ADD LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:console /machine:I386 + +!ELSEIF "$(CFG)" == "c_lexer_test - Win32 Debug" + +# PROP BASE Use_MFC 0 +# PROP BASE Use_Debug_Libraries 1 +# PROP BASE Output_Dir "Debug" +# PROP BASE Intermediate_Dir "Debug" +# PROP BASE Target_Dir "" +# PROP Use_MFC 0 +# PROP Use_Debug_Libraries 1 +# PROP Output_Dir "Debug" +# PROP Intermediate_Dir "Debug\c_lexer_test" +# PROP Target_Dir "" +# ADD BASE CPP /nologo /W3 /Gm /GX /ZI /Od /D "WIN32" /D "_DEBUG" /D "_CONSOLE" /D "_MBCS" /YX /FD /GZ /c +# ADD CPP /nologo /W3 /Gm /GX /ZI /Od /D "WIN32" /D "_DEBUG" /D "_CONSOLE" /D "_MBCS" /FD /GZ /c +# SUBTRACT CPP /YX +# ADD BASE RSC /l 0x409 /d "_DEBUG" +# ADD RSC /l 0x409 /d "_DEBUG" +BSC32=bscmake.exe +# ADD BASE BSC32 /nologo +# ADD BSC32 /nologo +LINK32=link.exe +# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:console /debug /machine:I386 /pdbtype:sept +# ADD LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:console /debug /machine:I386 /pdbtype:sept + +!ENDIF + +# Begin Target + +# Name "c_lexer_test - Win32 Release" +# Name "c_lexer_test - Win32 Debug" +# Begin Source File + +SOURCE=.\c_lexer_test.c +# End Source File +# End Target +# End Project diff --git a/impeller/third_party/stb/stb/tests/caveview/README.md b/impeller/third_party/stb/stb/tests/caveview/README.md new file mode 100644 index 0000000000000..10da83818e503 --- /dev/null +++ b/impeller/third_party/stb/stb/tests/caveview/README.md @@ -0,0 +1,85 @@ +# FAQ + +### How to run it? + +There's no GUI. Find a directory with Minecraft Anvil files (.mca). +Copy a Minecraft "terrain.png" into that directory (do a google +image search). Run from that directory. + +### How accurate is this as a Minecraft viewer? + +Not very. Many Minecraft blocks are not handled correctly: + +* No redstone, rails, or other "flat" blocks +* No signs, doors, fences, carpets, or other complicated geometry +* Stairs are turned into ramps +* Upper slabs turn into lower slabs +* Wood types only for blocks, not stairs, slabs, etc +* Colored glass becomes regular glass +* Glass panes become glass blocks +* Water is opaque +* Water level is incorrect +* No biome coloration +* Cactus is not shrunk, shows holes +* Chests are not shrunk +* Double-chests draw as two chests +* Pumpkins etc. are not rotated properly +* Torches are drawn hackily, do not attach to walls +* Incorrect textures for blocks that postdate terrain.png +* Transparent textures have black fringes due to non-premultiplied-alpha +* Skylight and block light are combined in a single value +* Only blocks at y=1..255 are shown (not y=0) +* If a 32x32x256 "quad-chunk" needs more than 800K quads, isn't handled (very unlikely) + +Some of these are due to engine limitations, and some of +these are because I didn't make the effort since my +goal was to make a demo for stb_voxel_render.h, not +to make a proper Minecraft viewer. + + +### Could this be turned into a proper Minecraft viewer? + +Yes and no. Yes, you could do it, but no, it wouldn't +really resemble this code that much anymore. + +You could certainly use this engine to +render the parts of Minecraft it works for, but many +of the things it doesn't handle it can't handle at all +(stairs, water, fences, carpets, etc) because it uses +low-precision coordinates to store voxel data. + +You would have to render all of the stuff it doesn't +handle through another rendering path. In a game (not +a viewer) you would need such a path for movable entities +like doors and carts anyway, so possibly handling other +things that way wouldn't be so bad. + +Rails, ladders, and redstone lines could be implemented by +using tex2 to overlay those effects, but you can't rotate +tex1 and tex2 independently, so there may be cases where +the underlying texture needs a different rotation from the +overlaid texture, which would require separate rendering. +Handling redstone's brightness being different from underlying +block's brightness would require separate rendering. + +You can use the face-color effect to do biome coloration, +but the change won't be smooth the way it is in Minecraft. + + +### Why isn't building the mesh data faster? + +Partly because converting from minecraft data is expensive. + +Here is the approximate breakdown of an older version +of this executable and lib that did the building single-threaded. + +* 25% loading & parsing minecraft files (4/5ths of this is my crappy zlib) +* 18% converting from minecraft blockids & lighting to stb blockids & lighting +* 10% reordering from data[z][y]\[x] (minecraft-style) to data[y][x]\[z] (stb-style) +* 40% building mesh data +* 7% uploading mesh data to OpenGL + +I did do significant optimizations after the above, so the +final breakdown is different, but it should give you some +sense of the costs. + diff --git a/impeller/third_party/stb/stb/tests/caveview/cave_main.c b/impeller/third_party/stb/stb/tests/caveview/cave_main.c new file mode 100644 index 0000000000000..d345cf1e35b97 --- /dev/null +++ b/impeller/third_party/stb/stb/tests/caveview/cave_main.c @@ -0,0 +1,598 @@ +#define _WIN32_WINNT 0x400 + +#include +#include + +// stb.h +#define STB_DEFINE +#include "stb.h" + +// stb_gl.h +#define STB_GL_IMPLEMENTATION +#define STB_GLEXT_DEFINE "glext_list.h" +#include "stb_gl.h" + +// SDL +#include "sdl.h" +#include "SDL_opengl.h" + +// stb_glprog.h +#define STB_GLPROG_IMPLEMENTATION +#define STB_GLPROG_ARB_DEFINE_EXTENSIONS +#include "stb_glprog.h" + +// stb_image.h +#define STB_IMAGE_IMPLEMENTATION +#include "stb_image.h" + +// stb_easy_font.h +#include "stb_easy_font.h" // doesn't require an IMPLEMENTATION + +#include "caveview.h" + +char *game_name = "caveview"; + + +#define REVERSE_DEPTH + + + +static void print_string(float x, float y, char *text, float r, float g, float b) +{ + static char buffer[99999]; + int num_quads; + + num_quads = stb_easy_font_print(x, y, text, NULL, buffer, sizeof(buffer)); + + glColor3f(r,g,b); + glEnableClientState(GL_VERTEX_ARRAY); + glVertexPointer(2, GL_FLOAT, 16, buffer); + glDrawArrays(GL_QUADS, 0, num_quads*4); + glDisableClientState(GL_VERTEX_ARRAY); +} + +static float text_color[3]; +static float pos_x = 10; +static float pos_y = 10; + +static void print(char *text, ...) +{ + char buffer[999]; + va_list va; + va_start(va, text); + vsprintf(buffer, text, va); + va_end(va); + print_string(pos_x, pos_y, buffer, text_color[0], text_color[1], text_color[2]); + pos_y += 10; +} + +float camang[3], camloc[3] = { 60,22,77 }; +float player_zoom = 1.0; +float rotate_view = 0.0; + + +void camera_to_worldspace(float world[3], float cam_x, float cam_y, float cam_z) +{ + float vec[3] = { cam_x, cam_y, cam_z }; + float t[3]; + float s,c; + s = (float) sin(camang[0]*3.141592/180); + c = (float) cos(camang[0]*3.141592/180); + + t[0] = vec[0]; + t[1] = c*vec[1] - s*vec[2]; + t[2] = s*vec[1] + c*vec[2]; + + s = (float) sin(camang[2]*3.141592/180); + c = (float) cos(camang[2]*3.141592/180); + world[0] = c*t[0] - s*t[1]; + world[1] = s*t[0] + c*t[1]; + world[2] = t[2]; +} + +// camera worldspace velocity +float cam_vel[3]; + +int controls; + +#define MAX_VEL 150.0f // blocks per second +#define ACCEL 6.0f +#define DECEL 3.0f + +#define STATIC_FRICTION DECEL +#define EFFECTIVE_ACCEL (ACCEL+DECEL) + +// dynamic friction: +// +// if going at MAX_VEL, ACCEL and friction must cancel +// EFFECTIVE_ACCEL = DECEL + DYNAMIC_FRIC*MAX_VEL +#define DYNAMIC_FRICTION (ACCEL/(float)MAX_VEL) + +float view_x_vel = 0; +float view_z_vel = 0; +float pending_view_x; +float pending_view_z; +float pending_view_x; +float pending_view_z; + +void process_tick_raw(float dt) +{ + int i; + float thrust[3] = { 0,0,0 }; + float world_thrust[3]; + + // choose direction to apply thrust + + thrust[0] = (controls & 3)== 1 ? EFFECTIVE_ACCEL : (controls & 3)== 2 ? -EFFECTIVE_ACCEL : 0; + thrust[1] = (controls & 12)== 4 ? EFFECTIVE_ACCEL : (controls & 12)== 8 ? -EFFECTIVE_ACCEL : 0; + thrust[2] = (controls & 48)==16 ? EFFECTIVE_ACCEL : (controls & 48)==32 ? -EFFECTIVE_ACCEL : 0; + + // @TODO clamp thrust[0] & thrust[1] vector length to EFFECTIVE_ACCEL + + camera_to_worldspace(world_thrust, thrust[0], thrust[1], 0); + world_thrust[2] += thrust[2]; + + for (i=0; i < 3; ++i) { + float acc = world_thrust[i]; + cam_vel[i] += acc*dt; + } + + if (cam_vel[0] || cam_vel[1] || cam_vel[2]) + { + float vel = (float) sqrt(cam_vel[0]*cam_vel[0] + cam_vel[1]*cam_vel[1] + cam_vel[2]*cam_vel[2]); + float newvel = vel; + float dec = STATIC_FRICTION + DYNAMIC_FRICTION*vel; + newvel = vel - dec*dt; + if (newvel < 0) + newvel = 0; + cam_vel[0] *= newvel/vel; + cam_vel[1] *= newvel/vel; + cam_vel[2] *= newvel/vel; + } + + camloc[0] += cam_vel[0] * dt; + camloc[1] += cam_vel[1] * dt; + camloc[2] += cam_vel[2] * dt; + + view_x_vel *= (float) pow(0.75, dt); + view_z_vel *= (float) pow(0.75, dt); + + view_x_vel += (pending_view_x - view_x_vel)*dt*60; + view_z_vel += (pending_view_z - view_z_vel)*dt*60; + + pending_view_x -= view_x_vel * dt; + pending_view_z -= view_z_vel * dt; + camang[0] += view_x_vel * dt; + camang[2] += view_z_vel * dt; + camang[0] = stb_clamp(camang[0], -90, 90); + camang[2] = (float) fmod(camang[2], 360); +} + +void process_tick(float dt) +{ + while (dt > 1.0f/60) { + process_tick_raw(1.0f/60); + dt -= 1.0f/60; + } + process_tick_raw(dt); +} + +void update_view(float dx, float dy) +{ + // hard-coded mouse sensitivity, not resolution independent? + pending_view_z -= dx*300; + pending_view_x -= dy*700; +} + +extern int screen_x, screen_y; +extern int is_synchronous_debug; +float render_time; + +extern int chunk_locations, chunks_considered, chunks_in_frustum; +extern int quads_considered, quads_rendered; +extern int chunk_storage_rendered, chunk_storage_considered, chunk_storage_total; +extern int view_dist_in_chunks; +extern int num_threads_active, num_meshes_started, num_meshes_uploaded; +extern float chunk_server_activity; + +static Uint64 start_time, end_time; // render time + +float chunk_server_status[32]; +int chunk_server_pos; + +void draw_stats(void) +{ + int i; + + static Uint64 last_frame_time; + Uint64 cur_time = SDL_GetPerformanceCounter(); + float chunk_server=0; + float frame_time = (cur_time - last_frame_time) / (float) SDL_GetPerformanceFrequency(); + last_frame_time = cur_time; + + chunk_server_status[chunk_server_pos] = chunk_server_activity; + chunk_server_pos = (chunk_server_pos+1) %32; + + for (i=0; i < 32; ++i) + chunk_server += chunk_server_status[i] / 32.0f; + + stb_easy_font_spacing(-0.75); + pos_y = 10; + text_color[0] = text_color[1] = text_color[2] = 1.0f; + print("Frame time: %6.2fms, CPU frame render time: %5.2fms", frame_time*1000, render_time*1000); + print("Tris: %4.1fM drawn of %4.1fM in range", 2*quads_rendered/1000000.0f, 2*quads_considered/1000000.0f); + print("Vbuf storage: %dMB in frustum of %dMB in range of %dMB in cache", chunk_storage_rendered>>20, chunk_storage_considered>>20, chunk_storage_total>>20); + print("Num mesh builds started this frame: %d; num uploaded this frame: %d\n", num_meshes_started, num_meshes_uploaded); + print("QChunks: %3d in frustum of %3d valid of %3d in range", chunks_in_frustum, chunks_considered, chunk_locations); + print("Mesh worker threads active: %d", num_threads_active); + print("View distance: %d blocks", view_dist_in_chunks*16); + print("%s", glGetString(GL_RENDERER)); + + if (is_synchronous_debug) { + text_color[0] = 1.0; + text_color[1] = 0.5; + text_color[2] = 0.5; + print("SLOWNESS: Synchronous debug output is enabled!"); + } +} + +void draw_main(void) +{ + glEnable(GL_CULL_FACE); + glDisable(GL_TEXTURE_2D); + glDisable(GL_LIGHTING); + glEnable(GL_DEPTH_TEST); + #ifdef REVERSE_DEPTH + glDepthFunc(GL_GREATER); + glClearDepth(0); + #else + glDepthFunc(GL_LESS); + glClearDepth(1); + #endif + glDepthMask(GL_TRUE); + glDisable(GL_SCISSOR_TEST); + glClearColor(0.6f,0.7f,0.9f,0.0f); + glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); + + glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); + glColor3f(1,1,1); + glFrontFace(GL_CW); + glEnable(GL_TEXTURE_2D); + glDisable(GL_BLEND); + + + glMatrixMode(GL_PROJECTION); + glLoadIdentity(); + + #ifdef REVERSE_DEPTH + stbgl_Perspective(player_zoom, 90, 70, 3000, 1.0/16); + #else + stbgl_Perspective(player_zoom, 90, 70, 1.0/16, 3000); + #endif + + // now compute where the camera should be + glMatrixMode(GL_MODELVIEW); + glLoadIdentity(); + stbgl_initCamera_zup_facing_y(); + + glRotatef(-camang[0],1,0,0); + glRotatef(-camang[2],0,0,1); + glTranslatef(-camloc[0], -camloc[1], -camloc[2]); + + start_time = SDL_GetPerformanceCounter(); + render_caves(camloc); + end_time = SDL_GetPerformanceCounter(); + + render_time = (end_time - start_time) / (float) SDL_GetPerformanceFrequency(); + + glMatrixMode(GL_PROJECTION); + glLoadIdentity(); + gluOrtho2D(0,screen_x/2,screen_y/2,0); + glMatrixMode(GL_MODELVIEW); + glLoadIdentity(); + glDisable(GL_TEXTURE_2D); + glDisable(GL_BLEND); + glDisable(GL_CULL_FACE); + draw_stats(); +} + + + +#pragma warning(disable:4244; disable:4305; disable:4018) + +#define SCALE 2 + +void error(char *s) +{ + SDL_ShowSimpleMessageBox(SDL_MESSAGEBOX_ERROR, "Error", s, NULL); + exit(0); +} + +void ods(char *fmt, ...) +{ + char buffer[1000]; + va_list va; + va_start(va, fmt); + vsprintf(buffer, fmt, va); + va_end(va); + SDL_Log("%s", buffer); +} + +#define TICKS_PER_SECOND 60 + +static SDL_Window *window; + +extern void draw_main(void); +extern void process_tick(float dt); +extern void editor_init(void); + +void draw(void) +{ + draw_main(); + SDL_GL_SwapWindow(window); +} + + +static int initialized=0; +static float last_dt; + +int screen_x,screen_y; + +float carried_dt = 0; +#define TICKRATE 60 + +float tex2_alpha = 1.0; + +int raw_level_time; + +float global_timer; +int global_hack; + +int loopmode(float dt, int real, int in_client) +{ + if (!initialized) return 0; + + if (!real) + return 0; + + // don't allow more than 6 frames to update at a time + if (dt > 0.075) dt = 0.075; + + global_timer += dt; + + carried_dt += dt; + while (carried_dt > 1.0/TICKRATE) { + if (global_hack) { + tex2_alpha += global_hack / 60.0f; + if (tex2_alpha < 0) tex2_alpha = 0; + if (tex2_alpha > 1) tex2_alpha = 1; + } + //update_input(); + // if the player is dead, stop the sim + carried_dt -= 1.0/TICKRATE; + } + + process_tick(dt); + draw(); + + return 0; +} + +static int quit; + +extern int controls; + +void active_control_set(int key) +{ + controls |= 1 << key; +} + +void active_control_clear(int key) +{ + controls &= ~(1 << key); +} + +extern void update_view(float dx, float dy); + +void process_sdl_mouse(SDL_Event *e) +{ + update_view((float) e->motion.xrel / screen_x, (float) e->motion.yrel / screen_y); +} + +void process_event(SDL_Event *e) +{ + switch (e->type) { + case SDL_MOUSEMOTION: + process_sdl_mouse(e); + break; + case SDL_MOUSEBUTTONDOWN: + case SDL_MOUSEBUTTONUP: + break; + + case SDL_QUIT: + quit = 1; + break; + + case SDL_WINDOWEVENT: + switch (e->window.event) { + case SDL_WINDOWEVENT_SIZE_CHANGED: + screen_x = e->window.data1; + screen_y = e->window.data2; + loopmode(0,1,0); + break; + } + break; + + case SDL_KEYDOWN: { + int k = e->key.keysym.sym; + int s = e->key.keysym.scancode; + SDL_Keymod mod; + mod = SDL_GetModState(); + if (k == SDLK_ESCAPE) + quit = 1; + + if (s == SDL_SCANCODE_D) active_control_set(0); + if (s == SDL_SCANCODE_A) active_control_set(1); + if (s == SDL_SCANCODE_W) active_control_set(2); + if (s == SDL_SCANCODE_S) active_control_set(3); + if (k == SDLK_SPACE) active_control_set(4); + if (s == SDL_SCANCODE_LCTRL) active_control_set(5); + if (s == SDL_SCANCODE_S) active_control_set(6); + if (s == SDL_SCANCODE_D) active_control_set(7); + if (k == '1') global_hack = !global_hack; + if (k == '2') global_hack = -1; + + #if 0 + if (game_mode == GAME_editor) { + switch (k) { + case SDLK_RIGHT: editor_key(STBTE_scroll_right); break; + case SDLK_LEFT : editor_key(STBTE_scroll_left ); break; + case SDLK_UP : editor_key(STBTE_scroll_up ); break; + case SDLK_DOWN : editor_key(STBTE_scroll_down ); break; + } + switch (s) { + case SDL_SCANCODE_S: editor_key(STBTE_tool_select); break; + case SDL_SCANCODE_B: editor_key(STBTE_tool_brush ); break; + case SDL_SCANCODE_E: editor_key(STBTE_tool_erase ); break; + case SDL_SCANCODE_R: editor_key(STBTE_tool_rectangle ); break; + case SDL_SCANCODE_I: editor_key(STBTE_tool_eyedropper); break; + case SDL_SCANCODE_L: editor_key(STBTE_tool_link); break; + case SDL_SCANCODE_G: editor_key(STBTE_act_toggle_grid); break; + } + if ((e->key.keysym.mod & KMOD_CTRL) && !(e->key.keysym.mod & ~KMOD_CTRL)) { + switch (s) { + case SDL_SCANCODE_X: editor_key(STBTE_act_cut ); break; + case SDL_SCANCODE_C: editor_key(STBTE_act_copy ); break; + case SDL_SCANCODE_V: editor_key(STBTE_act_paste); break; + case SDL_SCANCODE_Z: editor_key(STBTE_act_undo ); break; + case SDL_SCANCODE_Y: editor_key(STBTE_act_redo ); break; + } + } + } + #endif + break; + } + case SDL_KEYUP: { + int k = e->key.keysym.sym; + int s = e->key.keysym.scancode; + if (s == SDL_SCANCODE_D) active_control_clear(0); + if (s == SDL_SCANCODE_A) active_control_clear(1); + if (s == SDL_SCANCODE_W) active_control_clear(2); + if (s == SDL_SCANCODE_S) active_control_clear(3); + if (k == SDLK_SPACE) active_control_clear(4); + if (s == SDL_SCANCODE_LCTRL) active_control_clear(5); + if (s == SDL_SCANCODE_S) active_control_clear(6); + if (s == SDL_SCANCODE_D) active_control_clear(7); + break; + } + } +} + +static SDL_GLContext *context; + +static float getTimestep(float minimum_time) +{ + float elapsedTime; + double thisTime; + static double lastTime = -1; + + if (lastTime == -1) + lastTime = SDL_GetTicks() / 1000.0 - minimum_time; + + for(;;) { + thisTime = SDL_GetTicks() / 1000.0; + elapsedTime = (float) (thisTime - lastTime); + if (elapsedTime >= minimum_time) { + lastTime = thisTime; + return elapsedTime; + } + // @TODO: compute correct delay + SDL_Delay(1); + } +} + +void APIENTRY gl_debug(GLenum source, GLenum type, GLuint id, GLenum severity, GLsizei length, const GLchar *message, const void *param) +{ + ods("%s\n", message); +} + +int is_synchronous_debug; +void enable_synchronous(void) +{ + glEnable(GL_DEBUG_OUTPUT_SYNCHRONOUS_ARB); + is_synchronous_debug = 1; +} + +void prepare_threads(void); + +//void stbwingraph_main(void) +int SDL_main(int argc, char **argv) +{ + SDL_Init(SDL_INIT_VIDEO); + + prepare_threads(); + + SDL_GL_SetAttribute(SDL_GL_RED_SIZE , 8); + SDL_GL_SetAttribute(SDL_GL_GREEN_SIZE, 8); + SDL_GL_SetAttribute(SDL_GL_BLUE_SIZE , 8); + SDL_GL_SetAttribute(SDL_GL_DEPTH_SIZE, 24); + + SDL_GL_SetAttribute(SDL_GL_CONTEXT_PROFILE_MASK, SDL_GL_CONTEXT_PROFILE_COMPATIBILITY); + SDL_GL_SetAttribute(SDL_GL_CONTEXT_MAJOR_VERSION, 3); + SDL_GL_SetAttribute(SDL_GL_CONTEXT_MINOR_VERSION, 1); + + #ifdef GL_DEBUG + SDL_GL_SetAttribute(SDL_GL_CONTEXT_FLAGS, SDL_GL_CONTEXT_DEBUG_FLAG); + #endif + + SDL_GL_SetAttribute(SDL_GL_MULTISAMPLESAMPLES, 4); + + screen_x = 1920; + screen_y = 1080; + + window = SDL_CreateWindow("caveview", SDL_WINDOWPOS_UNDEFINED, SDL_WINDOWPOS_UNDEFINED, + screen_x, screen_y, + SDL_WINDOW_OPENGL | SDL_WINDOW_RESIZABLE + ); + if (!window) error("Couldn't create window"); + + context = SDL_GL_CreateContext(window); + if (!context) error("Couldn't create context"); + + SDL_GL_MakeCurrent(window, context); // is this true by default? + + SDL_SetRelativeMouseMode(SDL_TRUE); + #if defined(_MSC_VER) && _MSC_VER < 1300 + // work around broken behavior in VC6 debugging + if (IsDebuggerPresent()) + SDL_SetHint(SDL_HINT_MOUSE_RELATIVE_MODE_WARP, "1"); + #endif + + stbgl_initExtensions(); + + #ifdef GL_DEBUG + if (glDebugMessageCallbackARB) { + glDebugMessageCallbackARB(gl_debug, NULL); + + enable_synchronous(); + } + #endif + + SDL_GL_SetSwapInterval(1); + + render_init(); + mesh_init(); + world_init(); + + initialized = 1; + + while (!quit) { + SDL_Event e; + while (SDL_PollEvent(&e)) + process_event(&e); + + loopmode(getTimestep(0.0166f/8), 1, 1); + } + + return 0; +} diff --git a/impeller/third_party/stb/stb/tests/caveview/cave_mesher.c b/impeller/third_party/stb/stb/tests/caveview/cave_mesher.c new file mode 100644 index 0000000000000..1fac2972fbd53 --- /dev/null +++ b/impeller/third_party/stb/stb/tests/caveview/cave_mesher.c @@ -0,0 +1,928 @@ +// This file takes minecraft chunks (decoded by cave_parse) and +// uses stb_voxel_render to turn them into vertex buffers. + +#define STB_GLEXT_DECLARE "glext_list.h" +#include "stb_gl.h" +#include "stb_image.h" +#include "stb_glprog.h" + +#include "caveview.h" +#include "cave_parse.h" +#include "stb.h" +#include "sdl.h" +#include "sdl_thread.h" +#include + +//#define VHEIGHT_TEST +//#define STBVOX_OPTIMIZED_VHEIGHT + +#define STBVOX_CONFIG_MODE 1 +#define STBVOX_CONFIG_OPENGL_MODELVIEW +#define STBVOX_CONFIG_PREFER_TEXBUFFER +//#define STBVOX_CONFIG_LIGHTING_SIMPLE +#define STBVOX_CONFIG_FOG_SMOOTHSTEP +//#define STBVOX_CONFIG_PREMULTIPLIED_ALPHA // this doesn't work properly alpha test without next #define +//#define STBVOX_CONFIG_UNPREMULTIPLY // slower, fixes alpha test makes windows & fancy leaves look better +//#define STBVOX_CONFIG_TEX1_EDGE_CLAMP +#define STBVOX_CONFIG_DISABLE_TEX2 +//#define STBVOX_CONFIG_DOWN_TEXLERP_PACKED +#define STBVOX_CONFIG_ROTATION_IN_LIGHTING + +#define STB_VOXEL_RENDER_IMPLEMENTATION +#include "stb_voxel_render.h" + +extern void ods(char *fmt, ...); + +//#define FANCY_LEAVES // nearly 2x the triangles when enabled (if underground is filled) +#define FAST_CHUNK +#define IN_PLACE + +#define SKIP_TERRAIN 48 // use to avoid building underground stuff + // allows you to see what perf would be like if underground was efficiently culled, + // or if you were making a game without underground + +enum +{ + C_empty, + C_solid, + C_trans, + C_cross, + C_water, + C_slab, + C_stair, + C_force, +}; + +unsigned char geom_map[] = +{ + STBVOX_GEOM_empty, + STBVOX_GEOM_solid, + STBVOX_GEOM_transp, + STBVOX_GEOM_crossed_pair, + STBVOX_GEOM_solid, + STBVOX_GEOM_slab_lower, + STBVOX_GEOM_floor_slope_north_is_top, + STBVOX_GEOM_force, +}; + +unsigned char minecraft_info[256][7] = +{ + { C_empty, 0,0,0,0,0,0 }, + { C_solid, 1,1,1,1,1,1 }, + { C_solid, 3,3,3,3,40,2 }, + { C_solid, 2,2,2,2,2,2 }, + { C_solid, 16,16,16,16,16,16 }, + { C_solid, 4,4,4,4,4,4 }, + { C_cross, 15,15,15,15 }, + { C_solid, 17,17,17,17,17,17 }, + + // 8 + { C_water, 223,223,223,223,223,223 }, + { C_water, 223,223,223,223,223,223 }, + { C_solid, 255,255,255,255,255,255 }, + { C_solid, 255,255,255,255,255,255 }, + { C_solid, 18,18,18,18,18,18 }, + { C_solid, 19,19,19,19,19,19 }, + { C_solid, 32,32,32,32,32,32 }, + { C_solid, 33,33,33,33,33,33 }, + + // 16 + { C_solid, 34,34,34,34,34,34 }, + { C_solid, 20,20,20,20,21,21 }, +#ifdef FANCY_LEAVES + { C_force, 52,52,52,52,52,52 }, // leaves +#else + { C_solid, 53,53,53,53,53,53 }, // leaves +#endif + { C_solid, 24,24,24,24,24,24 }, + { C_trans, 49,49,49,49,49,49 }, // glass + { C_solid, 160,160,160,160,160,160 }, + { C_solid, 144,144,144,144,144,144 }, + { C_solid, 46,45,45,45,62,62 }, + + // 24 + { C_solid, 192,192,192,192, 176,176 }, + { C_solid, 74,74,74,74,74,74 }, + { C_empty }, // bed + { C_empty }, // powered rail + { C_empty }, // detector rail + { C_solid, 106,108,109,108,108,108 }, + { C_empty }, // cobweb=11 + { C_cross, 39,39,39,39 }, + + // 32 + { C_cross, 55,55,55,55,0,0 }, + { C_solid, 107,108,109,108,108,108 }, + { C_empty }, // piston head + { C_solid, 64,64,64,64,64,64 }, // various colors + { C_empty }, // unused + { C_cross, 13,13,13,13,0,0 }, + { C_cross, 12,12,12,12,0,0 }, + { C_cross, 29,29,29,29,0,0 }, + + // 40 + { C_cross, 28,28,28,28,0,0 }, + { C_solid, 23,23,23,23,23,23 }, + { C_solid, 22,22,22,22,22,22 }, + { C_solid, 5,5,5,5,6,6, }, + { C_slab , 5,5,5,5,6,6, }, + { C_solid, 7,7,7,7,7,7, }, + { C_solid, 8,8,8,8,9,10 }, + { C_solid, 35,35,35,35,4,4, }, + + // 48 + { C_solid, 36,36,36,36,36,36 }, + { C_solid, 37,37,37,37,37,37 }, + { C_cross, 80,80,80,80,80,80 }, // torch + { C_empty }, // fire + { C_trans, 65,65,65,65,65,65 }, + { C_stair, 4,4,4,4,4,4 }, + { C_solid, 26,26,26,27,25,25 }, + { C_empty }, // redstone + + // 56 + { C_solid, 50,50,50,50,50,50 }, + { C_solid, 26,26,26,26,26,26 }, + { C_solid, 60,59,59,59,43,43 }, + { C_cross, 95,95,95,95 }, + { C_solid, 2,2,2,2,86,2 }, + { C_solid, 44,45,45,45,62,62 }, + { C_solid, 61,45,45,45,62,62 }, + { C_empty }, // sign + + // 64 + { C_empty }, // door + { C_empty }, // ladder + { C_empty }, // rail + { C_stair, 16,16,16,16,16,16 }, // cobblestone stairs + { C_empty }, // sign + { C_empty }, // lever + { C_empty }, // stone pressure plate + { C_empty }, // iron door + + // 72 + { C_empty }, // wooden pressure + { C_solid, 51,51,51,51,51,51 }, + { C_solid, 51,51,51,51,51,51 }, + { C_empty }, + { C_empty }, + { C_empty }, + { C_empty }, // snow on block below, do as half slab? + { C_solid, 67,67,67,67,67,67 }, + + // 80 + { C_solid, 66,66,66,66,66,66 }, + { C_solid, 70,70,70,70,69,71 }, + { C_solid, 72,72,72,72,72,72 }, + { C_cross, 73,73,73,73,73,73 }, + { C_solid, 74,74,74,74,75,74 }, + { C_empty }, // fence + { C_solid,119,118,118,118,102,102 }, + { C_solid,103,103,103,103,103,103 }, + + // 88 + { C_solid, 104,104,104,104,104,104 }, + { C_solid, 105,105,105,105,105,105 }, + { C_solid, 167,167,167,167,167,167 }, + { C_solid, 120,118,118,118,102,102 }, + { C_empty }, // cake + { C_empty }, // repeater + { C_empty }, // repeater + { C_solid, 49,49,49,49,49,49 }, // colored glass + + // 96 + { C_empty }, + { C_empty }, + { C_solid, 54,54,54,54,54,54 }, + { C_solid, 125,125,125,125,125,125 }, + { C_solid, 126,126,126,126,126,126 }, + { C_empty }, // bars + { C_trans, 49,49,49,49,49,49 }, // glass pane + { C_solid, 136,136,136,136,137,137 }, // melon + + // 104 + { C_empty }, // pumpkin stem + { C_empty }, // melon stem + { C_empty }, // vines + { C_empty }, // gate + { C_stair, 7,7,7,7,7,7, }, // brick stairs + { C_stair, 54,54,54,54,54,54 }, // stone brick stairs + { C_empty }, // mycelium + { C_empty }, // lily pad + + // 112 + { C_solid, 224,224,224,224,224,224 }, + { C_empty }, // nether brick fence + { C_stair, 224,224,224,224,224,224 }, // nether brick stairs + { C_empty }, // nether wart + { C_solid, 182,182,182,182,166,183 }, + { C_empty }, // brewing stand + { C_empty }, // cauldron + { C_empty }, // end portal + + // 120 + { C_solid, 159,159,159,159,158,158 }, + { C_solid, 175,175,175,175,175,175 }, + { C_empty }, // dragon egg + { C_solid, 211,211,211,211,211,211 }, + { C_solid, 212,212,212,212,212,212 }, + { C_solid, 4,4,4,4,4,4, }, // wood double-slab + { C_slab , 4,4,4,4,4,4, }, // wood slab + { C_empty }, // cocoa + + // 128 + { C_solid, 192,192,192,192,176,176 }, // sandstone stairs + { C_solid, 32,32,32,32,32,32 }, // emerald ore + { C_solid, 26,26,26,27,25,25 }, // ender chest + { C_empty }, + { C_empty }, + { C_solid, 23,23,23,23,23,23 }, // emerald block + { C_solid, 198,198,198,198,198,198 }, // spruce stairs + { C_solid, 214,214,214,214,214,214 }, // birch stairs + + // 136 + { C_stair, 199,199,199,199,199,199 }, // jungle stairs + { C_empty }, // command block + { C_empty }, // beacon + { C_slab, 16,16,16,16,16,16 }, // cobblestone wall + { C_empty }, // flower pot + { C_empty }, // carrot + { C_empty }, // potatoes + { C_empty }, // wooden button + + // 144 + { C_empty }, // mob head + { C_empty }, // anvil + { C_solid, 26,26,26,27,25,25 }, // trapped chest + { C_empty }, // weighted pressure plate light + { C_empty }, // weighted pressure plat eheavy + { C_empty }, // comparator inactive + { C_empty }, // comparator active + { C_empty }, // daylight sensor + + // 152 + { C_solid, 135,135,135,135,135,135 }, // redstone block + { C_solid, 0,0,0,0,0,0, }, // nether quartz ore + { C_empty }, // hopper + { C_solid, 22,22,22,22,22,22 }, // quartz block + { C_stair, 22,22,22,22,22,22 }, // quartz stairs + { C_empty }, // activator rail + { C_solid, 46,45,45,45,62,62 }, // dropper + { C_solid, 72,72,72,72,72,72 }, // stained clay + + // 160 + { C_trans, 49,49,49,49,49,49 }, // stained glass pane + #ifdef FANCY_LEAVES + { C_force, 52,52,52,52,52,52 }, // leaves + #else + { C_solid, 53,53,53,53,53,53 }, // acacia leaves + #endif + { C_solid, 20,20,20,20,21,21 }, // acacia tree + { C_solid, 199,199,199,199,199,199 }, // acacia wood stairs + { C_solid, 198,198,198,198,198,198 }, // dark oak stairs + { C_solid, 146,146,146,146,146,146 }, // slime block + + { C_solid, 176,176,176,176,176,176 }, // red sandstone + { C_solid, 176,176,176,176,176,176 }, // red sandstone + + // 168 + { C_empty }, + { C_empty }, + { C_empty }, + { C_empty }, + { C_solid, 72,72,72,72,72,72 }, // hardened clay + { C_empty }, + { C_empty }, + { C_empty }, + + // 176 + { C_empty }, + { C_empty }, + { C_solid, 176,176,176,176,176,176 }, // red sandstone +}; + +unsigned char minecraft_tex1_for_blocktype[256][6]; +unsigned char effective_blocktype[256]; +unsigned char minecraft_color_for_blocktype[256][6]; +unsigned char minecraft_geom_for_blocktype[256]; + +uint8 build_buffer[BUILD_BUFFER_SIZE]; +uint8 face_buffer[FACE_BUFFER_SIZE]; + +//GLuint vbuf, fbuf, fbuf_tex; + +//unsigned char tex1_for_blocktype[256][6]; + +//unsigned char blocktype[34][34][257]; +//unsigned char lighting[34][34][257]; + +// a superchunk is 64x64x256, with the border blocks computed as well, +// which means we need 4x4 chunks plus 16 border chunks plus 4 corner chunks + +#define SUPERCHUNK_X 4 +#define SUPERCHUNK_Y 4 + +unsigned char remap_data[16][16]; +unsigned char remap[256]; +unsigned char rotate_data[4] = { 1,3,2,0 }; + +void convert_fastchunk_inplace(fast_chunk *fc) +{ + int i; + int num_blocks=0, step=0; + unsigned char rot[4096]; + #ifndef IN_PLACE + unsigned char *storage; + #endif + + memset(rot, 0, 4096); + + for (i=0; i < 16; ++i) + num_blocks += fc->blockdata[i] != NULL; + + #ifndef IN_PLACE + storage = malloc(16*16*16*2 * num_blocks); + #endif + + for (i=0; i < 16; ++i) { + if (fc->blockdata[i]) { + int o=0; + unsigned char *bd,*dd,*lt,*sky; + unsigned char *out, *outb; + + // this ordering allows us to determine which data we can safely overwrite for in-place processing + bd = fc->blockdata[i]; + dd = fc->data[i]; + lt = fc->light[i]; + sky = fc->skylight[i]; + + #ifdef IN_PLACE + out = bd; + #else + out = storage + 16*16*16*2*step; + #endif + + // bd is written in place, but also reads from dd + for (o=0; o < 16*16*16/2; o += 1) { + unsigned char v1,v2; + unsigned char d = dd[o]; + v1 = bd[o*2+0]; + v2 = bd[o*2+1]; + + if (remap[v1]) + { + //unsigned char d = bd[o] & 15; + v1 = remap_data[remap[v1]][d&15]; + rot[o*2+0] = rotate_data[d&3]; + } else + v1 = effective_blocktype[v1]; + + if (remap[v2]) + { + //unsigned char d = bd[o] >> 4; + v2 = remap_data[remap[v2]][d>>4]; + rot[o*2+1] = rotate_data[(d>>4)&3]; + } else + v2 = effective_blocktype[v2]; + + out[o*2+0] = v1; + out[o*2+1] = v2; + } + + // this reads from lt & sky + #ifndef IN_PLACE + outb = out + 16*16*16; + ++step; + #endif + + // MC used to write in this order and it makes it possible to compute in-place + if (dd < sky && sky < lt) { + // @TODO go this path always if !IN_PLACE + #ifdef IN_PLACE + outb = dd; + #endif + + for (o=0; o < 16*16*16/2; ++o) { + int bright; + bright = (lt[o]&15)*12 + 15 + (sky[o]&15)*16; + if (bright > 255) bright = 255; + if (bright < 32) bright = 32; + outb[o*2+0] = STBVOX_MAKE_LIGHTING_EXT((unsigned char) bright, (rot[o*2+0]&3)); + + bright = (lt[o]>>4)*12 + 15 + (sky[o]>>4)*16; + if (bright > 255) bright = 255; + if (bright < 32) bright = 32; + outb[o*2+1] = STBVOX_MAKE_LIGHTING_EXT((unsigned char) bright, (rot[o*2+1]&3)); + } + } else { + // @TODO: if blocktype is in between others, this breaks; need to find which side has two pointers, and use that + // overwrite rot[] array, then copy out + #ifdef IN_PLACE + outb = (dd < sky) ? dd : sky; + if (lt < outb) lt = outb; + #endif + + for (o=0; o < 16*16*16/2; ++o) { + int bright; + bright = (lt[o]&15)*12 + 15 + (sky[o]&15)*16; + if (bright > 255) bright = 255; + if (bright < 32) bright = 32; + rot[o*2+0] = STBVOX_MAKE_LIGHTING_EXT((unsigned char) bright, (rot[o*2+0]&3)); + + bright = (lt[o]>>4)*12 + 15 + (sky[o]>>4)*16; + if (bright > 255) bright = 255; + if (bright < 32) bright = 32; + rot[o*2+1] = STBVOX_MAKE_LIGHTING_EXT((unsigned char) bright, (rot[o*2+1]&3)); + } + + memcpy(outb, rot, 4096); + fc->data[i] = outb; + } + + #ifndef IN_PLACE + fc->blockdata[i] = out; + fc->data[i] = outb; + #endif + } + } + + #ifndef IN_PLACE + free(fc->pointer_to_free); + fc->pointer_to_free = storage; + #endif +} + +void make_converted_fastchunk(fast_chunk *fc, int x, int y, int segment, uint8 *sv_blocktype, uint8 *sv_lighting) +{ + int z; + assert(fc == NULL || (fc->refcount > 0 && fc->refcount < 64)); + if (fc == NULL || fc->blockdata[segment] == NULL) { + for (z=0; z < 16; ++z) { + sv_blocktype[z] = C_empty; + sv_lighting[z] = 255; + } + } else { + unsigned char *block = fc->blockdata[segment]; + unsigned char *data = fc->data[segment]; + y = 15-y; + for (z=0; z < 16; ++z) { + sv_blocktype[z] = block[z*256 + y*16 + x]; + sv_lighting [z] = data [z*256 + y*16 + x]; + } + } +} + + +#define CHUNK_CACHE 64 +typedef struct +{ + int valid; + int chunk_x, chunk_y; + fast_chunk *fc; +} cached_converted_chunk; + +cached_converted_chunk chunk_cache[CHUNK_CACHE][CHUNK_CACHE]; +int cache_size = CHUNK_CACHE; + +void reset_cache_size(int size) +{ + int i,j; + for (j=size; j < cache_size; ++j) { + for (i=size; i < cache_size; ++i) { + cached_converted_chunk *ccc = &chunk_cache[j][i]; + if (ccc->valid) { + if (ccc->fc) { + free(ccc->fc->pointer_to_free); + free(ccc->fc); + ccc->fc = NULL; + } + ccc->valid = 0; + } + } + } + cache_size = size; +} + +// this must be called inside mutex +void deref_fastchunk(fast_chunk *fc) +{ + if (fc) { + assert(fc->refcount > 0); + --fc->refcount; + if (fc->refcount == 0) { + free(fc->pointer_to_free); + free(fc); + } + } +} + +SDL_mutex * chunk_cache_mutex; +SDL_mutex * chunk_get_mutex; + +void lock_chunk_get_mutex(void) +{ + SDL_LockMutex(chunk_get_mutex); +} +void unlock_chunk_get_mutex(void) +{ + SDL_UnlockMutex(chunk_get_mutex); +} + +fast_chunk *get_converted_fastchunk(int chunk_x, int chunk_y) +{ + int slot_x = (chunk_x & (cache_size-1)); + int slot_y = (chunk_y & (cache_size-1)); + fast_chunk *fc; + cached_converted_chunk *ccc; + SDL_LockMutex(chunk_cache_mutex); + ccc = &chunk_cache[slot_y][slot_x]; + if (ccc->valid) { + if (ccc->chunk_x == chunk_x && ccc->chunk_y == chunk_y) { + fast_chunk *fc = ccc->fc; + if (fc) + ++fc->refcount; + SDL_UnlockMutex(chunk_cache_mutex); + return fc; + } + if (ccc->fc) { + deref_fastchunk(ccc->fc); + ccc->fc = NULL; + ccc->valid = 0; + } + } + SDL_UnlockMutex(chunk_cache_mutex); + + fc = get_decoded_fastchunk_uncached(chunk_x, -chunk_y); + if (fc) + convert_fastchunk_inplace(fc); + + SDL_LockMutex(chunk_cache_mutex); + // another thread might have updated it, so before we overwrite it... + if (ccc->fc) { + deref_fastchunk(ccc->fc); + ccc->fc = NULL; + } + + if (fc) + fc->refcount = 1; // 1 in the cache + + ccc->chunk_x = chunk_x; + ccc->chunk_y = chunk_y; + ccc->valid = 1; + if (fc) + ++fc->refcount; + ccc->fc = fc; + SDL_UnlockMutex(chunk_cache_mutex); + return fc; +} + +void make_map_segment_for_superchunk_preconvert(int chunk_x, int chunk_y, int segment, fast_chunk *fc_table[4][4], uint8 sv_blocktype[34][34][18], uint8 sv_lighting[34][34][18]) +{ + int a,b; + assert((chunk_x & 1) == 0); + assert((chunk_y & 1) == 0); + for (b=-1; b < 3; ++b) { + for (a=-1; a < 3; ++a) { + int xo = a*16+1; + int yo = b*16+1; + int x,y; + fast_chunk *fc = fc_table[b+1][a+1]; + for (y=0; y < 16; ++y) + for (x=0; x < 16; ++x) + if (xo+x >= 0 && xo+x < 34 && yo+y >= 0 && yo+y < 34) + make_converted_fastchunk(fc,x,y, segment, sv_blocktype[xo+x][yo+y], sv_lighting[xo+x][yo+y]); + } + } +} + +// build 1 mesh covering 2x2 chunks +void build_chunk(int chunk_x, int chunk_y, fast_chunk *fc_table[4][4], raw_mesh *rm) +{ + int a,b,z; + stbvox_input_description *map; + + #ifdef VHEIGHT_TEST + unsigned char vheight[34][34][18]; + #endif + + #ifndef STBVOX_CONFIG_DISABLE_TEX2 + unsigned char tex2_choice[34][34][18]; + #endif + + assert((chunk_x & 1) == 0); + assert((chunk_y & 1) == 0); + + rm->cx = chunk_x; + rm->cy = chunk_y; + + stbvox_set_input_stride(&rm->mm, 34*18, 18); + + assert(rm->mm.input.geometry == NULL); + + map = stbvox_get_input_description(&rm->mm); + map->block_tex1_face = minecraft_tex1_for_blocktype; + map->block_color_face = minecraft_color_for_blocktype; + map->block_geometry = minecraft_geom_for_blocktype; + + stbvox_reset_buffers(&rm->mm); + stbvox_set_buffer(&rm->mm, 0, 0, rm->build_buffer, BUILD_BUFFER_SIZE); + stbvox_set_buffer(&rm->mm, 0, 1, rm->face_buffer , FACE_BUFFER_SIZE); + + map->blocktype = &rm->sv_blocktype[1][1][1]; // this is (0,0,0), but we need to be able to query off the edges + map->lighting = &rm->sv_lighting[1][1][1]; + + // fill in the top two rows of the buffer + for (a=0; a < 34; ++a) { + for (b=0; b < 34; ++b) { + rm->sv_blocktype[a][b][16] = 0; + rm->sv_lighting [a][b][16] = 255; + rm->sv_blocktype[a][b][17] = 0; + rm->sv_lighting [a][b][17] = 255; + } + } + + #ifndef STBVOX_CONFIG_DISABLE_TEX2 + for (a=0; a < 34; ++a) { + for (b=0; b < 34; ++b) { + int px = chunk_x*16 + a - 1; + int py = chunk_y*16 + b - 1; + float dist = (float) sqrt(px*px + py*py); + float s1 = (float) sin(dist / 16), s2, s3; + dist = (float) sqrt((px-80)*(px-80) + (py-50)*(py-50)); + s2 = (float) sin(dist / 11); + for (z=0; z < 18; ++z) { + s3 = (float) sin(z * 3.141592 / 8); + + s3 = s1*s2*s3; + tex2_choice[a][b][z] = 63 & (int) stb_linear_remap(s3,-1,1, -20,83); + } + } + } + #endif + + for (z=256-16; z >= SKIP_TERRAIN; z -= 16) + { + int z0 = z; + int z1 = z+16; + if (z1 == 256) z1 = 255; + + make_map_segment_for_superchunk_preconvert(chunk_x, chunk_y, z >> 4, fc_table, rm->sv_blocktype, rm->sv_lighting); + + map->blocktype = &rm->sv_blocktype[1][1][1-z]; // specify location of 0,0,0 so that accessing z0..z1 gets right data + map->lighting = &rm->sv_lighting[1][1][1-z]; + #ifndef STBVOX_CONFIG_DISABLE_TEX2 + map->tex2 = &tex2_choice[1][1][1-z]; + #endif + + #ifdef VHEIGHT_TEST + // hacky test of vheight + for (a=0; a < 34; ++a) { + for (b=0; b < 34; ++b) { + int c; + for (c=0; c < 17; ++c) { + if (rm->sv_blocktype[a][b][c] != 0 && rm->sv_blocktype[a][b][c+1] == 0) { + // topmost block + vheight[a][b][c] = rand() & 255; + rm->sv_blocktype[a][b][c] = 168; + } else if (c > 0 && rm->sv_blocktype[a][b][c] != 0 && rm->sv_blocktype[a][b][c-1] == 0) { + // bottommost block + vheight[a][b][c] = ((rand() % 3) << 6) + ((rand() % 3) << 4) + ((rand() % 3) << 2) + (rand() % 3); + rm->sv_blocktype[a][b][c] = 169; + } + } + vheight[a][b][c] = STBVOX_MAKE_VHEIGHT(2,2,2,2); // flat top + } + } + map->vheight = &vheight[1][1][1-z]; + #endif + + { + stbvox_set_input_range(&rm->mm, 0,0,z0, 32,32,z1); + stbvox_set_default_mesh(&rm->mm, 0); + stbvox_make_mesh(&rm->mm); + } + + // copy the bottom two rows of data up to the top + for (a=0; a < 34; ++a) { + for (b=0; b < 34; ++b) { + rm->sv_blocktype[a][b][16] = rm->sv_blocktype[a][b][0]; + rm->sv_blocktype[a][b][17] = rm->sv_blocktype[a][b][1]; + rm->sv_lighting [a][b][16] = rm->sv_lighting [a][b][0]; + rm->sv_lighting [a][b][17] = rm->sv_lighting [a][b][1]; + } + } + } + + stbvox_set_mesh_coordinates(&rm->mm, chunk_x*16, chunk_y*16, 0); + stbvox_get_transform(&rm->mm, rm->transform); + + stbvox_set_input_range(&rm->mm, 0,0,0, 32,32,255); + stbvox_get_bounds(&rm->mm, rm->bounds); + + rm->num_quads = stbvox_get_quad_count(&rm->mm, 0); +} + +int next_blocktype = 255; + +unsigned char mc_rot[4] = { 1,3,2,0 }; + +// create blocktypes with rotation baked into type... +// @TODO we no longer need this now that we store rotations +// in lighting +void build_stair_rotations(int blocktype, unsigned char *map) +{ + int i; + + // use the existing block type for floor stairs; allocate a new type for ceil stairs + for (i=0; i < 6; ++i) { + minecraft_color_for_blocktype[next_blocktype][i] = minecraft_color_for_blocktype[blocktype][i]; + minecraft_tex1_for_blocktype [next_blocktype][i] = minecraft_tex1_for_blocktype [blocktype][i]; + } + minecraft_geom_for_blocktype[next_blocktype] = (unsigned char) STBVOX_MAKE_GEOMETRY(STBVOX_GEOM_ceil_slope_north_is_bottom, 0, 0); + minecraft_geom_for_blocktype[ blocktype] = (unsigned char) STBVOX_MAKE_GEOMETRY(STBVOX_GEOM_floor_slope_north_is_top, 0, 0); + + for (i=0; i < 4; ++i) { + map[0+i+8] = map[0+i] = blocktype; + map[4+i+8] = map[4+i] = next_blocktype; + } + --next_blocktype; +} + +void build_wool_variations(int bt, unsigned char *map) +{ + int i,k; + unsigned char tex[16] = { 64, 210, 194, 178, 162, 146, 130, 114, 225, 209, 193, 177, 161, 145, 129, 113 }; + for (i=0; i < 16; ++i) { + if (i == 0) + map[i] = bt; + else { + map[i] = next_blocktype; + for (k=0; k < 6; ++k) { + minecraft_tex1_for_blocktype[next_blocktype][k] = tex[i]; + } + minecraft_geom_for_blocktype[next_blocktype] = minecraft_geom_for_blocktype[bt]; + --next_blocktype; + } + } +} + +void build_wood_variations(int bt, unsigned char *map) +{ + int i,k; + unsigned char tex[4] = { 5, 198, 214, 199 }; + for (i=0; i < 4; ++i) { + if (i == 0) + map[i] = bt; + else { + map[i] = next_blocktype; + for (k=0; k < 6; ++k) { + minecraft_tex1_for_blocktype[next_blocktype][k] = tex[i]; + } + minecraft_geom_for_blocktype[next_blocktype] = minecraft_geom_for_blocktype[bt]; + --next_blocktype; + } + } + map[i] = map[i-1]; + ++i; + for (; i < 16; ++i) + map[i] = bt; +} + +void remap_in_place(int bt, int rm) +{ + int i; + remap[bt] = rm; + for (i=0; i < 16; ++i) + remap_data[rm][i] = bt; +} + + +void mesh_init(void) +{ + int i; + + chunk_cache_mutex = SDL_CreateMutex(); + chunk_get_mutex = SDL_CreateMutex(); + + for (i=0; i < 256; ++i) { + memcpy(minecraft_tex1_for_blocktype[i], minecraft_info[i]+1, 6); + effective_blocktype[i] = (minecraft_info[i][0] == C_empty ? 0 : i); + minecraft_geom_for_blocktype[i] = geom_map[minecraft_info[i][0]]; + } + //effective_blocktype[50] = 0; // delete torches + + for (i=0; i < 6*256; ++i) { + if (minecraft_tex1_for_blocktype[0][i] == 40) + minecraft_color_for_blocktype[0][i] = 38 | 64; // apply to tex1 + if (minecraft_tex1_for_blocktype[0][i] == 39) + minecraft_color_for_blocktype[0][i] = 39 | 64; // apply to tex1 + if (minecraft_tex1_for_blocktype[0][i] == 105) + minecraft_color_for_blocktype[0][i] = 63; // emissive + if (minecraft_tex1_for_blocktype[0][i] == 212) + minecraft_color_for_blocktype[0][i] = 63; // emissive + if (minecraft_tex1_for_blocktype[0][i] == 80) + minecraft_color_for_blocktype[0][i] = 63; // emissive + } + + for (i=0; i < 6; ++i) { + minecraft_color_for_blocktype[172][i] = 47 | 64; // apply to tex1 + minecraft_color_for_blocktype[178][i] = 47 | 64; // apply to tex1 + minecraft_color_for_blocktype[18][i] = 39 | 64; // green + minecraft_color_for_blocktype[161][i] = 37 | 64; // green + minecraft_color_for_blocktype[10][i] = 63; // emissive lava + minecraft_color_for_blocktype[11][i] = 63; // emissive + } + + #ifdef VHEIGHT_TEST + effective_blocktype[168] = 168; + minecraft_tex1_for_blocktype[168][0] = 1; + minecraft_tex1_for_blocktype[168][1] = 1; + minecraft_tex1_for_blocktype[168][2] = 1; + minecraft_tex1_for_blocktype[168][3] = 1; + minecraft_tex1_for_blocktype[168][4] = 1; + minecraft_tex1_for_blocktype[168][5] = 1; + minecraft_geom_for_blocktype[168] = STBVOX_GEOM_floor_vheight_12; + effective_blocktype[169] = 169; + minecraft_tex1_for_blocktype[169][0] = 1; + minecraft_tex1_for_blocktype[169][1] = 1; + minecraft_tex1_for_blocktype[169][2] = 1; + minecraft_tex1_for_blocktype[169][3] = 1; + minecraft_tex1_for_blocktype[169][4] = 1; + minecraft_tex1_for_blocktype[169][5] = 1; + minecraft_geom_for_blocktype[169] = STBVOX_GEOM_ceil_vheight_03; + #endif + + remap[53] = 1; + remap[67] = 2; + remap[108] = 3; + remap[109] = 4; + remap[114] = 5; + remap[136] = 6; + remap[156] = 7; + for (i=0; i < 256; ++i) + if (remap[i]) + build_stair_rotations(i, remap_data[remap[i]]); + remap[35] = 8; + build_wool_variations(35, remap_data[remap[35]]); + remap[5] = 11; + build_wood_variations(5, remap_data[remap[5]]); + + // set the remap flags for these so they write the rotation values + remap_in_place(54, 9); + remap_in_place(146, 10); +} + +// Timing stats while optimizing the single-threaded builder + +// 32..-32, 32..-32, SKIP_TERRAIN=0, !FANCY_LEAVES on 'mcrealm' data set + +// 6.27s - reblocked to do 16 z at a time instead of 256 (still using 66x66x258), 4 meshes in parallel +// 5.96s - reblocked to use FAST_CHUNK (no intermediate data structure) +// 5.45s - unknown change, or previous measurement was wrong + +// 6.12s - use preconverted data, not in-place +// 5.91s - use preconverted, in-place +// 5.34s - preconvert, in-place, avoid dependency chain (suggested by ryg) +// 5.34s - preconvert, in-place, avoid dependency chain, use bit-table instead of byte-table +// 5.50s - preconvert, in-place, branchless + +// 6.42s - non-preconvert, avoid dependency chain (not an error) +// 5.40s - non-preconvert, w/dependency chain (same as earlier) + +// 5.50s - non-FAST_CHUNK, reblocked outer loop for better cache reuse +// 4.73s - FAST_CHUNK non-preconvert, reblocked outer loop +// 4.25s - preconvert, in-place, reblocked outer loop +// 4.18s - preconvert, in-place, unrolled again +// 4.10s - 34x34 1 mesh instead of 66x66 and 4 meshes (will make it easier to do multiple threads) + +// 4.83s - building bitmasks but not using them (2 bits per block, one if empty, one if solid) + +// 5.16s - using empty bitmasks to early out +// 5.01s - using solid & empty bitmasks to early out - "foo" +// 4.64s - empty bitmask only, test 8 at a time, then test geom +// 4.72s - empty bitmask only, 8 at a time, then test bits +// 4.46s - split bitmask building into three loops (each byte is separate) +// 4.42s - further optimize computing bitmask + +// 4.58s - using solid & empty bitmasks to early out, same as "foo" but faster bitmask building +// 4.12s - using solid & empty bitmasks to efficiently test neighbors +// 4.04s - using 16-bit fetches (not endian-independent) +// - note this is first place that beats previous best '4.10s - 34x34 1 mesh' + +// 4.30s - current time with bitmasks disabled again (note was 4.10s earlier) +// 3.95s - bitmasks enabled again, no other changes +// 4.00s - current time with bitmasks disabled again, no other changes -- wide variation that is time dependent? +// (note that most of the numbers listed here are median of 3 values already) +// 3.98s - bitmasks enabled + +// Bitmasks removed from the code as not worth the complexity increase + + + +// Raw data for Q&A: +// +// 26% parsing & loading minecraft files (4/5ths of which is zlib decode) +// 39% building mesh from stb input format +// 18% converting from minecraft blocks to stb blocks +// 9% reordering from minecraft axis order to stb axis order +// 7% uploading vertex buffer to OpenGL diff --git a/impeller/third_party/stb/stb/tests/caveview/cave_parse.c b/impeller/third_party/stb/stb/tests/caveview/cave_parse.c new file mode 100644 index 0000000000000..e8ae02b7dbbf3 --- /dev/null +++ b/impeller/third_party/stb/stb/tests/caveview/cave_parse.c @@ -0,0 +1,632 @@ +#include +#include +#include +#include + +#define FAST_CHUNK // disabling this enables the old, slower path that deblocks into a regular form + +#include "cave_parse.h" + +#include "stb_image.h" +#include "stb.h" + +#define NUM_CHUNKS_PER_REGION 32 // only on one axis +#define NUM_CHUNKS_PER_REGION_LOG2 5 + +#define NUM_COLUMNS_PER_CHUNK 16 +#define NUM_COLUMNS_PER_CHUNK_LOG2 4 + +uint32 read_uint32_be(FILE *f) +{ + unsigned char data[4]; + fread(data, 1, 4, f); + return (data[0]<<24) + (data[1]<<16) + (data[2]<<8) + data[3]; +} + +typedef struct +{ + uint8 *data; + size_t len; + int x,z; // chunk index + int refcount; // for multi-threading +} compressed_chunk; + +typedef struct +{ + int x,z; + uint32 sector_data[NUM_CHUNKS_PER_REGION][NUM_CHUNKS_PER_REGION]; +} region; + +size_t cached_compressed=0; + +FILE *last_region; +int last_region_x; +int last_region_z; +int opened=0; + +static void open_file(int reg_x, int reg_z) +{ + if (!opened || last_region_x != reg_x || last_region_z != reg_z) { + char filename[256]; + if (last_region != NULL) + fclose(last_region); + sprintf(filename, "r.%d.%d.mca", reg_x, reg_z); + last_region = fopen(filename, "rb"); + last_region_x = reg_x; + last_region_z = reg_z; + opened = 1; + } +} + +static region *load_region(int reg_x, int reg_z) +{ + region *r; + int x,z; + + open_file(reg_x, reg_z); + + r = malloc(sizeof(*r)); + + if (last_region == NULL) { + memset(r, 0, sizeof(*r)); + } else { + fseek(last_region, 0, SEEK_SET); + for (z=0; z < NUM_CHUNKS_PER_REGION; ++z) + for (x=0; x < NUM_CHUNKS_PER_REGION; ++x) + r->sector_data[z][x] = read_uint32_be(last_region); + } + r->x = reg_x; + r->z = reg_z; + + return r; +} + +void free_region(region *r) +{ + free(r); +} + +#define MAX_MAP_REGIONS 64 // in one axis: 64 regions * 32 chunk/region * 16 columns/chunk = 16384 columns +region *regions[MAX_MAP_REGIONS][MAX_MAP_REGIONS]; + +static region *get_region(int reg_x, int reg_z) +{ + int slot_x = reg_x & (MAX_MAP_REGIONS-1); + int slot_z = reg_z & (MAX_MAP_REGIONS-1); + region *r; + + r = regions[slot_z][slot_x]; + + if (r) { + if (r->x == reg_x && r->z == reg_z) + return r; + free_region(r); + } + + r = load_region(reg_x, reg_z); + regions[slot_z][slot_x] = r; + + return r; +} + +// about one region, so size should be ok +#define NUM_CACHED_X 64 +#define NUM_CACHED_Z 64 + +// @TODO: is it really worth caching these? we probably can just +// pull them from the disk cache nearly as efficiently. +// Can test that by setting to 1x1? +compressed_chunk *cached_chunk[NUM_CACHED_Z][NUM_CACHED_X]; + +static void deref_compressed_chunk(compressed_chunk *cc) +{ + assert(cc->refcount > 0); + --cc->refcount; + if (cc->refcount == 0) { + if (cc->data) + free(cc->data); + free(cc); + } +} + +static compressed_chunk *get_compressed_chunk(int chunk_x, int chunk_z) +{ + int slot_x = chunk_x & (NUM_CACHED_X-1); + int slot_z = chunk_z & (NUM_CACHED_Z-1); + compressed_chunk *cc = cached_chunk[slot_z][slot_x]; + + if (cc && cc->x == chunk_x && cc->z == chunk_z) + return cc; + else { + int reg_x = chunk_x >> NUM_CHUNKS_PER_REGION_LOG2; + int reg_z = chunk_z >> NUM_CHUNKS_PER_REGION_LOG2; + region *r = get_region(reg_x, reg_z); + if (cc) { + deref_compressed_chunk(cc); + cached_chunk[slot_z][slot_x] = NULL; + } + cc = malloc(sizeof(*cc)); + cc->x = chunk_x; + cc->z = chunk_z; + { + int subchunk_x = chunk_x & (NUM_CHUNKS_PER_REGION-1); + int subchunk_z = chunk_z & (NUM_CHUNKS_PER_REGION-1); + uint32 code = r->sector_data[subchunk_z][subchunk_x]; + + if (code & 255) { + open_file(reg_x, reg_z); + fseek(last_region, (code>>8)*4096, SEEK_SET); + cc->len = (code&255)*4096; + cc->data = malloc(cc->len); + fread(cc->data, 1, cc->len, last_region); + } else { + cc->len = 0; + cc->data = 0; + } + } + cc->refcount = 1; + cached_chunk[slot_z][slot_x] = cc; + return cc; + } +} + + +// NBT parser -- can automatically parse stuff we don't +// have definitions for, but want to explicitly parse +// stuff we do have definitions for. +// +// option 1: auto-parse everything into data structures, +// then read those +// +// option 2: have a "parse next object" which +// doesn't resolve whether it expands its children +// yet, and then the user either says "expand" or +// "skip" after looking at the name. Anything with +// "children" without names can't go through this +// interface. +// +// Let's try option 2. + + +typedef struct +{ + unsigned char *buffer_start; + unsigned char *buffer_end; + unsigned char *cur; + int nesting; + char temp_buffer[256]; +} nbt; + +enum { TAG_End=0, TAG_Byte=1, TAG_Short=2, TAG_Int=3, TAG_Long=4, + TAG_Float=5, TAG_Double=6, TAG_Byte_Array=7, TAG_String=8, + TAG_List=9, TAG_Compound=10, TAG_Int_Array=11 }; + +static void nbt_get_string_data(unsigned char *data, char *buffer, size_t bufsize) +{ + int len = data[0]*256 + data[1]; + int i; + for (i=0; i < len && i+1 < (int) bufsize; ++i) + buffer[i] = (char) data[i+2]; + buffer[i] = 0; +} + +static char *nbt_peek(nbt *n) +{ + unsigned char type = *n->cur; + if (type == TAG_End) + return NULL; + nbt_get_string_data(n->cur+1, n->temp_buffer, sizeof(n->temp_buffer)); + return n->temp_buffer; +} + +static uint32 nbt_parse_uint32(unsigned char *buffer) +{ + return (buffer[0] << 24) + (buffer[1]<<16) + (buffer[2]<<8) + buffer[3]; +} + +static void nbt_skip(nbt *n); + +// skip an item that doesn't have an id or name prefix (usable in lists) +static void nbt_skip_raw(nbt *n, unsigned char type) +{ + switch (type) { + case TAG_Byte : n->cur += 1; break; + case TAG_Short : n->cur += 2; break; + case TAG_Int : n->cur += 4; break; + case TAG_Long : n->cur += 8; break; + case TAG_Float : n->cur += 4; break; + case TAG_Double: n->cur += 8; break; + case TAG_Byte_Array: n->cur += 4 + 1*nbt_parse_uint32(n->cur); break; + case TAG_Int_Array : n->cur += 4 + 4*nbt_parse_uint32(n->cur); break; + case TAG_String : n->cur += 2 + (n->cur[0]*256 + n->cur[1]); break; + case TAG_List : { + unsigned char list_type = *n->cur++; + unsigned int list_len = nbt_parse_uint32(n->cur); + unsigned int i; + n->cur += 4; // list_len + for (i=0; i < list_len; ++i) + nbt_skip_raw(n, list_type); + break; + } + case TAG_Compound : { + while (*n->cur != TAG_End) + nbt_skip(n); + nbt_skip(n); // skip the TAG_end + break; + } + } + assert(n->cur <= n->buffer_end); +} + +static void nbt_skip(nbt *n) +{ + unsigned char type = *n->cur++; + if (type == TAG_End) + return; + // skip name + n->cur += (n->cur[0]*256 + n->cur[1]) + 2; + nbt_skip_raw(n, type); +} + +// byteswap +static void nbt_swap(unsigned char *ptr, int len) +{ + int i; + for (i=0; i < (len>>1); ++i) { + unsigned char t = ptr[i]; + ptr[i] = ptr[len-1-i]; + ptr[len-1-i] = t; + } +} + +// pass in the expected type, fail if doesn't match +// returns a pointer to the data, byteswapped if appropriate +static void *nbt_get_fromlist(nbt *n, unsigned char type, int *len) +{ + unsigned char *ptr; + assert(type != TAG_Compound); + assert(type != TAG_List); // we could support getting lists of primitives as if they were arrays, but eh + if (len) *len = 1; + ptr = n->cur; + switch (type) { + case TAG_Byte : break; + + case TAG_Short : nbt_swap(ptr, 2); break; + case TAG_Int : nbt_swap(ptr, 4); break; + case TAG_Long : nbt_swap(ptr, 8); break; + case TAG_Float : nbt_swap(ptr, 4); break; + case TAG_Double: nbt_swap(ptr, 8); break; + + case TAG_Byte_Array: + *len = nbt_parse_uint32(ptr); + ptr += 4; + break; + case TAG_Int_Array: { + int i; + *len = nbt_parse_uint32(ptr); + ptr += 4; + for (i=0; i < *len; ++i) + nbt_swap(ptr + 4*i, 4); + break; + } + + default: assert(0); // unhandled case + } + nbt_skip_raw(n, type); + return ptr; +} + +static void *nbt_get(nbt *n, unsigned char type, int *len) +{ + assert(n->cur[0] == type); + n->cur += 3 + (n->cur[1]*256+n->cur[2]); + return nbt_get_fromlist(n, type, len); +} + +static void nbt_begin_compound(nbt *n) // start a compound +{ + assert(*n->cur == TAG_Compound); + // skip header + n->cur += 3 + (n->cur[1]*256 + n->cur[2]); + ++n->nesting; +} + +static void nbt_begin_compound_in_list(nbt *n) // start a compound +{ + ++n->nesting; +} + +static void nbt_end_compound(nbt *n) // end a compound +{ + assert(*n->cur == TAG_End); + assert(n->nesting != 0); + ++n->cur; + --n->nesting; +} + +// @TODO no interface to get lists from lists +static int nbt_begin_list(nbt *n, unsigned char type) +{ + uint32 len; + unsigned char *ptr; + + ptr = n->cur + 3 + (n->cur[1]*256 + n->cur[2]); + if (ptr[0] != type) + return -1; + n->cur = ptr; + len = nbt_parse_uint32(n->cur+1); + assert(n->cur[0] == type); + // @TODO keep a stack with the count to make sure they do it right + ++n->nesting; + n->cur += 5; + return (int) len; +} + +static void nbt_end_list(nbt *n) +{ + --n->nesting; +} + +// raw_block chunk is 16x256x16x4 = 2^(4+8+4+2) = 256KB +// +// if we want to process 64x64x256 at a time, that will be: +// 4*4*256KB => 4MB per area in raw_block +// +// (plus we maybe need to decode adjacent regions) + + +#ifdef FAST_CHUNK +typedef fast_chunk parse_chunk; +#else +typedef chunk parse_chunk; +#endif + +static parse_chunk *minecraft_chunk_parse(unsigned char *data, size_t len) +{ + char *s; + parse_chunk *c = NULL; + + nbt n_store, *n = &n_store; + n->buffer_start = data; + n->buffer_end = data + len; + n->cur = n->buffer_start; + n->nesting = 0; + + nbt_begin_compound(n); + while ((s = nbt_peek(n)) != NULL) { + if (!strcmp(s, "Level")) { + int *height; + c = malloc(sizeof(*c)); + #ifdef FAST_CHUNK + memset(c, 0, sizeof(*c)); + c->pointer_to_free = data; + #else + c->rb[15][15][255].block = 0; + #endif + c->max_y = 0; + + nbt_begin_compound(n); + while ((s = nbt_peek(n)) != NULL) { + if (!strcmp(s, "xPos")) + c->xpos = *(int *) nbt_get(n, TAG_Int, 0); + else if (!strcmp(s, "zPos")) + c->zpos = *(int *) nbt_get(n, TAG_Int, 0); + else if (!strcmp(s, "Sections")) { + int count = nbt_begin_list(n, TAG_Compound), i; + if (count == -1) { + // this not-a-list case happens in The End and I'm not sure + // what it means... possibly one of those silly encodings + // where it's not encoded as a list if there's only one? + // not worth figuring out + nbt_skip(n); + count = -1; + } + for (i=0; i < count; ++i) { + int yi, len; + uint8 *light = NULL, *blocks = NULL, *data = NULL, *skylight = NULL; + nbt_begin_compound_in_list(n); + while ((s = nbt_peek(n)) != NULL) { + if (!strcmp(s, "Y")) + yi = * (uint8 *) nbt_get(n, TAG_Byte, 0); + else if (!strcmp(s, "BlockLight")) { + light = nbt_get(n, TAG_Byte_Array, &len); + assert(len == 2048); + } else if (!strcmp(s, "Blocks")) { + blocks = nbt_get(n, TAG_Byte_Array, &len); + assert(len == 4096); + } else if (!strcmp(s, "Data")) { + data = nbt_get(n, TAG_Byte_Array, &len); + assert(len == 2048); + } else if (!strcmp(s, "SkyLight")) { + skylight = nbt_get(n, TAG_Byte_Array, &len); + assert(len == 2048); + } + } + nbt_end_compound(n); + + assert(yi < 16); + + #ifndef FAST_CHUNK + + // clear data below current max_y + { + int x,z; + while (c->max_y < yi*16) { + for (x=0; x < 16; ++x) + for (z=0; z < 16; ++z) + c->rb[z][x][c->max_y].block = 0; + ++c->max_y; + } + } + + // now assemble the data + { + int x,y,z, o2=0,o4=0; + for (y=0; y < 16; ++y) { + for (z=0; z < 16; ++z) { + for (x=0; x < 16; x += 2) { + raw_block *rb = &c->rb[15-z][x][y + yi*16]; // 15-z because switching to z-up will require flipping an axis + rb[0].block = blocks[o4]; + rb[0].light = light[o2] & 15; + rb[0].data = data[o2] & 15; + rb[0].skylight = skylight[o2] & 15; + + rb[256].block = blocks[o4+1]; + rb[256].light = light[o2] >> 4; + rb[256].data = data[o2] >> 4; + rb[256].skylight = skylight[o2] >> 4; + + o2 += 1; + o4 += 2; + } + } + } + c->max_y += 16; + } + #else + c->blockdata[yi] = blocks; + c->data [yi] = data; + c->light [yi] = light; + c->skylight [yi] = skylight; + #endif + } + //nbt_end_list(n); + } else if (!strcmp(s, "HeightMap")) { + height = nbt_get(n, TAG_Int_Array, &len); + assert(len == 256); + } else + nbt_skip(n); + } + nbt_end_compound(n); + + } else + nbt_skip(n); + } + nbt_end_compound(n); + assert(n->cur == n->buffer_end); + return c; +} + +#define MAX_DECODED_CHUNK_X 64 +#define MAX_DECODED_CHUNK_Z 64 + +typedef struct +{ + int cx,cz; + fast_chunk *fc; + int valid; +} decoded_buffer; + +static decoded_buffer decoded_buffers[MAX_DECODED_CHUNK_Z][MAX_DECODED_CHUNK_X]; +void lock_chunk_get_mutex(void); +void unlock_chunk_get_mutex(void); + +#ifdef FAST_CHUNK +fast_chunk *get_decoded_fastchunk_uncached(int chunk_x, int chunk_z) +{ + unsigned char *decoded; + compressed_chunk *cc; + int inlen; + int len; + fast_chunk *fc; + + lock_chunk_get_mutex(); + cc = get_compressed_chunk(chunk_x, chunk_z); + if (cc->len != 0) + ++cc->refcount; + unlock_chunk_get_mutex(); + + if (cc->len == 0) + return NULL; + + assert(cc != NULL); + + assert(cc->data[4] == 2); + + inlen = nbt_parse_uint32(cc->data); + decoded = stbi_zlib_decode_malloc_guesssize(cc->data+5, inlen, inlen*3, &len); + assert(decoded != NULL); + assert(len != 0); + + lock_chunk_get_mutex(); + deref_compressed_chunk(cc); + unlock_chunk_get_mutex(); + + #ifdef FAST_CHUNK + fc = minecraft_chunk_parse(decoded, len); + #else + fc = NULL; + #endif + if (fc == NULL) + free(decoded); + return fc; +} + + +decoded_buffer *get_decoded_buffer(int chunk_x, int chunk_z) +{ + decoded_buffer *db = &decoded_buffers[chunk_z&(MAX_DECODED_CHUNK_Z-1)][chunk_x&(MAX_DECODED_CHUNK_X-1)]; + if (db->valid) { + if (db->cx == chunk_x && db->cz == chunk_z) + return db; + if (db->fc) { + free(db->fc->pointer_to_free); + free(db->fc); + } + } + + db->cx = chunk_x; + db->cz = chunk_z; + db->valid = 1; + db->fc = 0; + + { + db->fc = get_decoded_fastchunk_uncached(chunk_x, chunk_z); + return db; + } +} + +fast_chunk *get_decoded_fastchunk(int chunk_x, int chunk_z) +{ + decoded_buffer *db = get_decoded_buffer(chunk_x, chunk_z); + return db->fc; +} +#endif + +#ifndef FAST_CHUNK +chunk *get_decoded_chunk_raw(int chunk_x, int chunk_z) +{ + unsigned char *decoded; + compressed_chunk *cc = get_compressed_chunk(chunk_x, chunk_z); + assert(cc != NULL); + if (cc->len == 0) + return NULL; + else { + chunk *ch; + int inlen = nbt_parse_uint32(cc->data); + int len; + assert(cc->data[4] == 2); + decoded = stbi_zlib_decode_malloc_guesssize(cc->data+5, inlen, inlen*3, &len); + assert(decoded != NULL); + #ifdef FAST_CHUNK + ch = NULL; + #else + ch = minecraft_chunk_parse(decoded, len); + #endif + free(decoded); + return ch; + } +} + +static chunk *decoded_chunks[MAX_DECODED_CHUNK_Z][MAX_DECODED_CHUNK_X]; +chunk *get_decoded_chunk(int chunk_x, int chunk_z) +{ + chunk *c = decoded_chunks[chunk_z&(MAX_DECODED_CHUNK_Z-1)][chunk_x&(MAX_DECODED_CHUNK_X-1)]; + if (c && c->xpos == chunk_x && c->zpos == chunk_z) + return c; + if (c) free(c); + c = get_decoded_chunk_raw(chunk_x, chunk_z); + decoded_chunks[chunk_z&(MAX_DECODED_CHUNK_Z-1)][chunk_x&(MAX_DECODED_CHUNK_X-1)] = c; + return c; +} +#endif diff --git a/impeller/third_party/stb/stb/tests/caveview/cave_parse.h b/impeller/third_party/stb/stb/tests/caveview/cave_parse.h new file mode 100644 index 0000000000000..4cdfe2ad701ed --- /dev/null +++ b/impeller/third_party/stb/stb/tests/caveview/cave_parse.h @@ -0,0 +1,41 @@ +#ifndef INCLUDE_CAVE_PARSE_H +#define INCLUDE_CAVE_PARSE_H + +typedef struct +{ + unsigned char block; + unsigned char data; + unsigned char light:4; + unsigned char skylight:4; +} raw_block; + +// this is the old fully-decoded chunk +typedef struct +{ + int xpos, zpos, max_y; + int height[16][16]; + raw_block rb[16][16][256]; // [z][x][y] which becomes [y][x][z] in stb +} chunk; + +chunk *get_decoded_chunk(int chunk_x, int chunk_z); + +#define NUM_SEGMENTS 16 +typedef struct +{ + int max_y, xpos, zpos; + + unsigned char *blockdata[NUM_SEGMENTS]; + unsigned char *data[NUM_SEGMENTS]; + unsigned char *skylight[NUM_SEGMENTS]; + unsigned char *light[NUM_SEGMENTS]; + + void *pointer_to_free; + + int refcount; // this allows multi-threaded building without wrapping in ANOTHER struct +} fast_chunk; + +fast_chunk *get_decoded_fastchunk(int chunk_x, int chunk_z); // cache, never call free() + +fast_chunk *get_decoded_fastchunk_uncached(int chunk_x, int chunk_z); + +#endif diff --git a/impeller/third_party/stb/stb/tests/caveview/cave_render.c b/impeller/third_party/stb/stb/tests/caveview/cave_render.c new file mode 100644 index 0000000000000..7ac96ec513a88 --- /dev/null +++ b/impeller/third_party/stb/stb/tests/caveview/cave_render.c @@ -0,0 +1,951 @@ +// This file renders vertex buffers, converts raw meshes +// to GL meshes, and manages threads that do the raw-mesh +// building (found in cave_mesher.c) + + +#include "stb_voxel_render.h" + +#define STB_GLEXT_DECLARE "glext_list.h" +#include "stb_gl.h" +#include "stb_image.h" +#include "stb_glprog.h" + +#include "caveview.h" +#include "cave_parse.h" +#include "stb.h" +#include "sdl.h" +#include "sdl_thread.h" +#include +#include + +//#define STBVOX_CONFIG_TEX1_EDGE_CLAMP + + +// currently no dynamic way to set mesh cache size or view distance +//#define SHORTVIEW + + +stbvox_mesh_maker g_mesh_maker; + +GLuint main_prog; +GLint uniform_locations[64]; + +//#define MAX_QUADS_PER_DRAW (65536 / 4) // assuming 16-bit indices, 4 verts per quad +//#define FIXED_INDEX_BUFFER_SIZE (MAX_QUADS_PER_DRAW * 6 * 2) // 16*1024 * 12 == ~192KB + +// while uploading texture data, this holds our each texture +#define TEX_SIZE 64 +uint32 texture[TEX_SIZE][TEX_SIZE]; + +GLuint voxel_tex[2]; + +// chunk state +enum +{ + STATE_invalid, + STATE_needed, + STATE_requested, + STATE_abandoned, + STATE_valid, +}; + +// mesh is 32x32x255 ... this is hardcoded in that +// a mesh covers 2x2 minecraft chunks, no #defines for it +typedef struct +{ + int state; + int chunk_x, chunk_y; + int num_quads; + float priority; + int vbuf_size, fbuf_size; + + float transform[3][3]; + float bounds[2][3]; + + GLuint vbuf;// vbuf_tex; + GLuint fbuf, fbuf_tex; + +} chunk_mesh; + +void scale_texture(unsigned char *src, int x, int y, int w, int h) +{ + int i,j,k; + assert(w == 256 && h == 256); + for (j=0; j < TEX_SIZE; ++j) { + for (i=0; i < TEX_SIZE; ++i) { + uint32 val=0; + for (k=0; k < 4; ++k) { + val >>= 8; + val += src[ 4*(x+(i>>2)) + 4*w*(y+(j>>2)) + k]<<24; + } + texture[j][i] = val; + } + } +} + +void build_base_texture(int n) +{ + int x,y; + uint32 color = stb_rand() | 0x808080; + for (y=0; ystate == STATE_valid) { + glDeleteTextures(1, &cm->fbuf_tex); + glDeleteBuffersARB(1, &cm->vbuf); + glDeleteBuffersARB(1, &cm->fbuf); + cached_chunk_mesh[slot_y][slot_x].state = STATE_invalid; + } +} + +void upload_mesh(chunk_mesh *cm, uint8 *build_buffer, uint8 *face_buffer) +{ + glGenBuffersARB(1, &cm->vbuf); + glBindBufferARB(GL_ARRAY_BUFFER_ARB, cm->vbuf); + glBufferDataARB(GL_ARRAY_BUFFER_ARB, cm->num_quads*4*sizeof(uint32), build_buffer, GL_STATIC_DRAW_ARB); + glBindBufferARB(GL_ARRAY_BUFFER_ARB, 0); + + glGenBuffersARB(1, &cm->fbuf); + glBindBufferARB(GL_TEXTURE_BUFFER_ARB, cm->fbuf); + glBufferDataARB(GL_TEXTURE_BUFFER_ARB, cm->num_quads*sizeof(uint32), face_buffer , GL_STATIC_DRAW_ARB); + glBindBufferARB(GL_TEXTURE_BUFFER_ARB, 0); + + glGenTextures(1, &cm->fbuf_tex); + glBindTexture(GL_TEXTURE_BUFFER_ARB, cm->fbuf_tex); + glTexBufferARB(GL_TEXTURE_BUFFER_ARB, GL_RGBA8UI, cm->fbuf); + glBindTexture(GL_TEXTURE_BUFFER_ARB, 0); +} + +static void upload_mesh_data(raw_mesh *rm) +{ + int cx = rm->cx; + int cy = rm->cy; + int slot_x = (cx >> 1) & (CACHED_MESH_NUM_X-1); + int slot_y = (cy >> 1) & (CACHED_MESH_NUM_Y-1); + chunk_mesh *cm; + + free_chunk(slot_x, slot_y); + + cm = &cached_chunk_mesh[slot_y][slot_x]; + cm->num_quads = rm->num_quads; + + upload_mesh(cm, rm->build_buffer, rm->face_buffer); + cm->vbuf_size = rm->num_quads*4*sizeof(uint32); + cm->fbuf_size = rm->num_quads*sizeof(uint32); + cm->priority = 100000; + cm->chunk_x = cx; + cm->chunk_y = cy; + + memcpy(cm->bounds, rm->bounds, sizeof(cm->bounds)); + memcpy(cm->transform, rm->transform, sizeof(cm->transform)); + + // write barrier here + cm->state = STATE_valid; +} + +GLint uniform_loc[16]; +float table3[128][3]; +float table4[64][4]; +GLint tablei[2]; + +float step=0; + +#ifdef SHORTVIEW +int view_dist_in_chunks = 50; +#else +int view_dist_in_chunks = 80; +#endif + +void setup_uniforms(float pos[3]) +{ + int i,j; + step += 1.0f/60.0f; + for (i=0; i < STBVOX_UNIFORM_count; ++i) { + stbvox_uniform_info raw, *ui=&raw; + stbvox_get_uniform_info(&raw, i); + uniform_loc[i] = -1; + + if (i == STBVOX_UNIFORM_texscale || i == STBVOX_UNIFORM_texgen || i == STBVOX_UNIFORM_color_table) + continue; + + if (ui) { + void *data = ui->default_value; + uniform_loc[i] = stbgl_find_uniform(main_prog, ui->name); + switch (i) { + case STBVOX_UNIFORM_face_data: + tablei[0] = 2; + data = tablei; + break; + + case STBVOX_UNIFORM_tex_array: + glActiveTextureARB(GL_TEXTURE0_ARB); + glBindTexture(GL_TEXTURE_2D_ARRAY_EXT, voxel_tex[0]); + glActiveTextureARB(GL_TEXTURE1_ARB); + glBindTexture(GL_TEXTURE_2D_ARRAY_EXT, voxel_tex[1]); + glActiveTextureARB(GL_TEXTURE0_ARB); + tablei[0] = 0; + tablei[1] = 1; + data = tablei; + break; + + case STBVOX_UNIFORM_color_table: + data = ui->default_value; + ((float *)data)[63*4+3] = 2.0f; // emissive + break; + + case STBVOX_UNIFORM_camera_pos: + data = table3[0]; + table3[0][0] = pos[0]; + table3[0][1] = pos[1]; + table3[0][2] = pos[2]; + table3[0][3] = stb_max(0,(float)sin(step*2)*0.125f); + break; + + case STBVOX_UNIFORM_ambient: { + float bright = 1.0; + //float bright = 0.75; + float amb[3][3]; + + // ambient direction is sky-colored upwards + // "ambient" lighting is from above + table4[0][0] = 0.3f; + table4[0][1] = -0.5f; + table4[0][2] = 0.9f; + + amb[1][0] = 0.3f; amb[1][1] = 0.3f; amb[1][2] = 0.3f; // dark-grey + amb[2][0] = 1.0; amb[2][1] = 1.0; amb[2][2] = 1.0; // white + + // convert so (table[1]*dot+table[2]) gives + // above interpolation + // lerp((dot+1)/2, amb[1], amb[2]) + // amb[1] + (amb[2] - amb[1]) * (dot+1)/2 + // amb[1] + (amb[2] - amb[1]) * dot/2 + (amb[2]-amb[1])/2 + + for (j=0; j < 3; ++j) { + table4[1][j] = (amb[2][j] - amb[1][j])/2 * bright; + table4[2][j] = (amb[1][j] + amb[2][j])/2 * bright; + } + + // fog color + table4[3][0] = 0.6f, table4[3][1] = 0.7f, table4[3][2] = 0.9f; + table4[3][3] = 1.0f / (view_dist_in_chunks * 16); + table4[3][3] *= table4[3][3]; + + data = table4; + break; + } + } + + switch (ui->type) { + case STBVOX_UNIFORM_TYPE_sampler: stbglUniform1iv(uniform_loc[i], ui->array_length, data); break; + case STBVOX_UNIFORM_TYPE_vec2: stbglUniform2fv(uniform_loc[i], ui->array_length, data); break; + case STBVOX_UNIFORM_TYPE_vec3: stbglUniform3fv(uniform_loc[i], ui->array_length, data); break; + case STBVOX_UNIFORM_TYPE_vec4: stbglUniform4fv(uniform_loc[i], ui->array_length, data); break; + } + } + } +} + +GLuint unitex[64], unibuf[64]; +void make_texture_buffer_for_uniform(int uniform, int slot) +{ + GLenum type; + stbvox_uniform_info raw, *ui=&raw; + GLint uloc; + + stbvox_get_uniform_info(ui, uniform); + uloc = stbgl_find_uniform(main_prog, ui->name); + + if (uniform == STBVOX_UNIFORM_color_table) + ((float *)ui->default_value)[63*4+3] = 2.0f; // emissive + + glGenBuffersARB(1, &unibuf[uniform]); + glBindBufferARB(GL_ARRAY_BUFFER_ARB, unibuf[uniform]); + glBufferDataARB(GL_ARRAY_BUFFER_ARB, ui->array_length * ui->bytes_per_element, ui->default_value, GL_STATIC_DRAW_ARB); + glBindBufferARB(GL_ARRAY_BUFFER_ARB, 0); + + glGenTextures(1, &unitex[uniform]); + glBindTexture(GL_TEXTURE_BUFFER_ARB, unitex[uniform]); + switch (ui->type) { + case STBVOX_UNIFORM_TYPE_vec2: type = GL_RG32F; break; + case STBVOX_UNIFORM_TYPE_vec3: type = GL_RGB32F; break; + case STBVOX_UNIFORM_TYPE_vec4: type = GL_RGBA32F; break; + default: assert(0); + } + glTexBufferARB(GL_TEXTURE_BUFFER_ARB, type, unibuf[uniform]); + glBindTexture(GL_TEXTURE_BUFFER_ARB, 0); + + glActiveTextureARB(GL_TEXTURE0 + slot); + glBindTexture(GL_TEXTURE_BUFFER_ARB, unitex[uniform]); + glActiveTextureARB(GL_TEXTURE0); + + stbglUseProgram(main_prog); + stbglUniform1i(uloc, slot); +} + +#define MAX_MESH_WORKERS 8 +#define MAX_CHUNK_LOAD_WORKERS 2 + +int num_mesh_workers; +int num_chunk_load_workers; + +typedef struct +{ + int state; + int request_cx; + int request_cy; + int padding[13]; + + SDL_sem * request_received; + + SDL_sem * chunk_server_done_processing; + int chunk_action; + int chunk_request_x; + int chunk_request_y; + fast_chunk *chunks[4][4]; + + int padding2[16]; + raw_mesh rm; + int padding3[16]; + + uint8 *build_buffer; + uint8 *face_buffer ; +} mesh_worker; + +enum +{ + WSTATE_idle, + WSTATE_requested, + WSTATE_running, + WSTATE_mesh_ready, +}; + +mesh_worker mesh_data[MAX_MESH_WORKERS]; +int num_meshes_started; // stats + +int request_chunk(int chunk_x, int chunk_y); +void update_meshes_from_render_thread(void); + +unsigned char tex2_data[64][4]; + +void init_tex2_gradient(void) +{ + int i; + for (i=0; i < 16; ++i) { + tex2_data[i+ 0][0] = 64 + 12*i; + tex2_data[i+ 0][1] = 32; + tex2_data[i+ 0][2] = 64; + + tex2_data[i+16][0] = 255; + tex2_data[i+16][1] = 32 + 8*i; + tex2_data[i+16][2] = 64; + + tex2_data[i+32][0] = 255; + tex2_data[i+32][1] = 160; + tex2_data[i+32][2] = 64 + 12*i; + + tex2_data[i+48][0] = 255; + tex2_data[i+48][1] = 160 + 6*i; + tex2_data[i+48][2] = 255; + } +} + +void set_tex2_alpha(float fa) +{ + int i; + int a = (int) stb_lerp(fa, 0, 255); + if (a < 0) a = 0; else if (a > 255) a = 255; + glBindTexture(GL_TEXTURE_2D_ARRAY_EXT, voxel_tex[1]); + for (i=0; i < 64; ++i) { + tex2_data[i][3] = a; + glTexSubImage3DEXT(GL_TEXTURE_2D_ARRAY_EXT, 0, 0,0,i, 1,1,1, GL_RGBA, GL_UNSIGNED_BYTE, tex2_data[i]); + } +} + +void render_init(void) +{ + int i; + char *binds[] = { "attr_vertex", "attr_face", NULL }; + char *vertex; + char *fragment; + int w=0,h=0; + + unsigned char *texdata = stbi_load("terrain.png", &w, &h, NULL, 4); + + stbvox_init_mesh_maker(&g_mesh_maker); + for (i=0; i < num_mesh_workers; ++i) { + stbvox_init_mesh_maker(&mesh_data[i].rm.mm); + } + + vertex = stbvox_get_vertex_shader(); + fragment = stbvox_get_fragment_shader(); + + { + char error_buffer[1024]; + char *main_vertex[] = { vertex, NULL }; + char *main_fragment[] = { fragment, NULL }; + main_prog = stbgl_create_program(main_vertex, main_fragment, binds, error_buffer, sizeof(error_buffer)); + if (main_prog == 0) { + ods("Compile error for main shader: %s\n", error_buffer); + assert(0); + exit(1); + } + } + //init_index_buffer(); + + make_texture_buffer_for_uniform(STBVOX_UNIFORM_texscale , 3); + make_texture_buffer_for_uniform(STBVOX_UNIFORM_texgen , 4); + make_texture_buffer_for_uniform(STBVOX_UNIFORM_color_table , 5); + + glGenTextures(2, voxel_tex); + + glBindTexture(GL_TEXTURE_2D_ARRAY_EXT, voxel_tex[0]); + glTexImage3DEXT(GL_TEXTURE_2D_ARRAY_EXT, 0, GL_RGBA, + TEX_SIZE,TEX_SIZE,256, + 0,GL_RGBA,GL_UNSIGNED_BYTE,NULL); + for (i=0; i < 256; ++i) { + if (texdata) + scale_texture(texdata, (i&15)*w/16, (h/16)*(i>>4), w,h); + else + build_base_texture(i); + glTexSubImage3DEXT(GL_TEXTURE_2D_ARRAY_EXT, 0, 0,0,i, TEX_SIZE,TEX_SIZE,1, GL_RGBA, GL_UNSIGNED_BYTE, texture[0]); + } + glTexParameteri(GL_TEXTURE_2D_ARRAY_EXT, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR); + glTexParameteri(GL_TEXTURE_2D_ARRAY_EXT, GL_TEXTURE_MAG_FILTER, GL_LINEAR); + glTexParameteri(GL_TEXTURE_2D_ARRAY_EXT, GL_TEXTURE_MAX_ANISOTROPY_EXT, 16); + #ifdef STBVOX_CONFIG_TEX1_EDGE_CLAMP + glTexParameteri(GL_TEXTURE_2D_ARRAY_EXT, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); + glTexParameteri(GL_TEXTURE_2D_ARRAY_EXT, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); + #endif + + glGenerateMipmapEXT(GL_TEXTURE_2D_ARRAY_EXT); + + glBindTexture(GL_TEXTURE_2D_ARRAY_EXT, voxel_tex[1]); + glTexImage3DEXT(GL_TEXTURE_2D_ARRAY_EXT, 0, GL_RGBA, + 1,1,64, + 0,GL_RGBA,GL_UNSIGNED_BYTE,NULL); + init_tex2_gradient(); + set_tex2_alpha(0.0); + #if 0 + for (i=0; i < 128; ++i) { + //build_overlay_texture(i); + glTexSubImage3DEXT(GL_TEXTURE_2D_ARRAY_EXT, 0, 0,0,i, TEX_SIZE,TEX_SIZE,1, GL_RGBA, GL_UNSIGNED_BYTE, texture[0]); + } + #endif + glTexParameteri(GL_TEXTURE_2D_ARRAY_EXT, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR); + glTexParameteri(GL_TEXTURE_2D_ARRAY_EXT, GL_TEXTURE_MAG_FILTER, GL_LINEAR); + glGenerateMipmapEXT(GL_TEXTURE_2D_ARRAY_EXT); +} + +void world_init(void) +{ + int a,b,x,y; + + Uint64 start_time, end_time; + #ifdef NDEBUG + int range = 32; + #else + int range = 12; + #endif + + start_time = SDL_GetPerformanceCounter(); + + // iterate in 8x8 clusters of qchunks at a time to get better converted-chunk-cache reuse + // than a purely row-by-row ordering is (single-threaded this is a bigger win than + // any of the above optimizations were, since it halves zlib/mc-conversion costs) + for (x=-range; x <= range; x += 16) + for (y=-range; y <= range; y += 16) + for (b=y; b < y+16 && b <= range; b += 2) + for (a=x; a < x+16 && a <= range; a += 2) + while (!request_chunk(a, b)) { // if request fails, all threads are busy + update_meshes_from_render_thread(); + SDL_Delay(1); + } + + // wait until all the workers are done, + // (this is only needed if we want to time + // when the build finishes, or when we want to reset the + // cache size; otherwise we could just go ahead and + // start rendering whatever we've got) + for(;;) { + int i; + update_meshes_from_render_thread(); + for (i=0; i < num_mesh_workers; ++i) + if (mesh_data[i].state != WSTATE_idle) + break; + if (i == num_mesh_workers) + break; + SDL_Delay(3); + } + + end_time = SDL_GetPerformanceCounter(); + ods("Build time: %7.2fs\n", (end_time - start_time) / (float) SDL_GetPerformanceFrequency()); + + // don't waste lots of storage on chunk caches once it's finished starting-up; + // this was only needed to be this large because we worked in large blocks + // to maximize sharing + reset_cache_size(32); +} + +extern SDL_mutex * chunk_cache_mutex; + +int mesh_worker_handler(void *data) +{ + mesh_worker *mw = data; + mw->face_buffer = malloc(FACE_BUFFER_SIZE); + mw->build_buffer = malloc(BUILD_BUFFER_SIZE); + + // this loop only works because the compiler can't + // tell that the SDL_calls don't access mw->state; + // really we should barrier that stuff + for(;;) { + int i,j; + int cx,cy; + + // wait for a chunk request + SDL_SemWait(mw->request_received); + + // analyze the chunk request + assert(mw->state == WSTATE_requested); + cx = mw->request_cx; + cy = mw->request_cy; + + // this is inaccurate as it can block while another thread has the cache locked + mw->state = WSTATE_running; + + // get the chunks we need (this takes a lock and caches them) + for (j=0; j < 4; ++j) + for (i=0; i < 4; ++i) + mw->chunks[j][i] = get_converted_fastchunk(cx-1 + i, cy-1 + j); + + // build the mesh based on the chunks + mw->rm.build_buffer = mw->build_buffer; + mw->rm.face_buffer = mw->face_buffer; + build_chunk(cx, cy, mw->chunks, &mw->rm); + mw->state = WSTATE_mesh_ready; + // don't need to notify of this, because it gets polled + + // when done, free the chunks + + // for efficiency we just take the mutex once around the whole thing, + // though this spreads the mutex logic over two files + SDL_LockMutex(chunk_cache_mutex); + for (j=0; j < 4; ++j) + for (i=0; i < 4; ++i) { + deref_fastchunk(mw->chunks[j][i]); + mw->chunks[j][i] = NULL; + } + SDL_UnlockMutex(chunk_cache_mutex); + } + return 0; +} + +int request_chunk(int chunk_x, int chunk_y) +{ + int i; + for (i=0; i < num_mesh_workers; ++i) { + mesh_worker *mw = &mesh_data[i]; + if (mw->state == WSTATE_idle) { + mw->request_cx = chunk_x; + mw->request_cy = chunk_y; + mw->state = WSTATE_requested; + SDL_SemPost(mw->request_received); + ++num_meshes_started; + return 1; + } + } + return 0; +} + +void prepare_threads(void) +{ + int i; + int num_proc = SDL_GetCPUCount(); + + if (num_proc > 6) + num_mesh_workers = num_proc/2; + else if (num_proc > 4) + num_mesh_workers = 4; + else + num_mesh_workers = num_proc-1; + +// @TODO +// Thread usage is probably pretty terrible; need to make a +// separate queue of needed chunks, instead of just generating +// one request per thread per frame, and a separate queue of +// results. (E.g. If it takes 1.5 frames to build mesh, thread +// is idle for 0.5 frames.) To fake this for now, I've just +// doubled the number of threads to let those serve as a 'queue', +// but that's dumb. + + num_mesh_workers *= 2; // try to get better thread usage + + if (num_mesh_workers > MAX_MESH_WORKERS) + num_mesh_workers = MAX_MESH_WORKERS; + + for (i=0; i < num_mesh_workers; ++i) { + mesh_worker *data = &mesh_data[i]; + data->request_received = SDL_CreateSemaphore(0); + data->chunk_server_done_processing = SDL_CreateSemaphore(0); + SDL_CreateThread(mesh_worker_handler, "mesh worker", data); + } +} + + +// "better" buffer uploading +#if 0 + if (glBufferStorage) { + glDeleteBuffersARB(1, &vb->vbuf); + glGenBuffersARB(1, &vb->vbuf); + + glBindBufferARB(GL_ARRAY_BUFFER_ARB, vb->vbuf); + glBufferStorage(GL_ARRAY_BUFFER_ARB, sizeof(build_buffer), build_buffer, 0); + glBindBufferARB(GL_ARRAY_BUFFER_ARB, 0); + } else { + glBindBufferARB(GL_ARRAY_BUFFER_ARB, vb->vbuf); + glBufferDataARB(GL_ARRAY_BUFFER_ARB, sizeof(build_buffer), build_buffer, GL_STATIC_DRAW_ARB); + glBindBufferARB(GL_ARRAY_BUFFER_ARB, 0); + } +#endif + + +typedef struct +{ + float x,y,z,w; +} plane; + +static plane frustum[6]; + +static void matd_mul(double out[4][4], double src1[4][4], double src2[4][4]) +{ + int i,j,k; + for (j=0; j < 4; ++j) { + for (i=0; i < 4; ++i) { + double t=0; + for (k=0; k < 4; ++k) + t += src1[k][i] * src2[j][k]; + out[i][j] = t; + } + } +} + +// https://fgiesen.wordpress.com/2012/08/31/frustum-planes-from-the-projection-matrix/ +static void compute_frustum(void) +{ + int i; + GLdouble mv[4][4],proj[4][4], mvproj[4][4]; + glGetDoublev(GL_MODELVIEW_MATRIX , mv[0]); + glGetDoublev(GL_PROJECTION_MATRIX, proj[0]); + matd_mul(mvproj, proj, mv); + for (i=0; i < 4; ++i) { + (&frustum[0].x)[i] = (float) (mvproj[3][i] + mvproj[0][i]); + (&frustum[1].x)[i] = (float) (mvproj[3][i] - mvproj[0][i]); + (&frustum[2].x)[i] = (float) (mvproj[3][i] + mvproj[1][i]); + (&frustum[3].x)[i] = (float) (mvproj[3][i] - mvproj[1][i]); + (&frustum[4].x)[i] = (float) (mvproj[3][i] + mvproj[2][i]); + (&frustum[5].x)[i] = (float) (mvproj[3][i] - mvproj[2][i]); + } +} + +static int test_plane(plane *p, float x0, float y0, float z0, float x1, float y1, float z1) +{ + // return false if the box is entirely behind the plane + float d=0; + assert(x0 <= x1 && y0 <= y1 && z0 <= z1); + if (p->x > 0) d += x1*p->x; else d += x0*p->x; + if (p->y > 0) d += y1*p->y; else d += y0*p->y; + if (p->z > 0) d += z1*p->z; else d += z0*p->z; + return d + p->w >= 0; +} + +static int is_box_in_frustum(float *bmin, float *bmax) +{ + int i; + for (i=0; i < 5; ++i) + if (!test_plane(&frustum[i], bmin[0], bmin[1], bmin[2], bmax[0], bmax[1], bmax[2])) + return 0; + return 1; +} + +float compute_priority(int cx, int cy, float x, float y) +{ + float distx, disty, dist2; + distx = (cx*16+8) - x; + disty = (cy*16+8) - y; + dist2 = distx*distx + disty*disty; + return view_dist_in_chunks*view_dist_in_chunks * 16 * 16 - dist2; +} + +int chunk_locations, chunks_considered, chunks_in_frustum; +int quads_considered, quads_rendered; +int chunk_storage_rendered, chunk_storage_considered, chunk_storage_total; +int update_frustum = 1; + +#ifdef SHORTVIEW +int max_chunk_storage = 450 << 20; +int min_chunk_storage = 350 << 20; +#else +int max_chunk_storage = 900 << 20; +int min_chunk_storage = 800 << 20; +#endif + +float min_priority = -500; // this really wants to be in unit space, not squared space + +int num_meshes_uploaded; + +void update_meshes_from_render_thread(void) +{ + int i; + for (i=0; i < num_mesh_workers; ++i) { + mesh_worker *mw = &mesh_data[i]; + if (mw->state == WSTATE_mesh_ready) { + upload_mesh_data(&mw->rm); + ++num_meshes_uploaded; + mw->state = WSTATE_idle; + } + } +} + +extern float tex2_alpha; +extern int global_hack; +int num_threads_active; +float chunk_server_activity; + +void render_caves(float campos[3]) +{ + float x = campos[0], y = campos[1]; + int qchunk_x, qchunk_y; + int cam_x, cam_y; + int i,j, rad; + + compute_frustum(); + + chunk_locations = chunks_considered = chunks_in_frustum = 0; + quads_considered = quads_rendered = 0; + chunk_storage_total = chunk_storage_considered = chunk_storage_rendered = 0; + + cam_x = (int) floor(x+0.5); + cam_y = (int) floor(y+0.5); + + qchunk_x = (((int) floor(x)+16) >> 5) << 1; + qchunk_y = (((int) floor(y)+16) >> 5) << 1; + + glEnable(GL_ALPHA_TEST); + glAlphaFunc(GL_GREATER, 0.5); + + stbglUseProgram(main_prog); + setup_uniforms(campos); // set uniforms to default values inefficiently + glActiveTextureARB(GL_TEXTURE2_ARB); + stbglEnableVertexAttribArray(0); + + { + float lighting[2][3] = { { campos[0],campos[1],campos[2] }, { 0.75,0.75,0.65f } }; + float bright = 8; + lighting[1][0] *= bright; + lighting[1][1] *= bright; + lighting[1][2] *= bright; + stbglUniform3fv(stbgl_find_uniform(main_prog, "light_source"), 2, lighting[0]); + } + + if (global_hack) + set_tex2_alpha(tex2_alpha); + + num_meshes_uploaded = 0; + update_meshes_from_render_thread(); + + // traverse all in-range chunks and analyze them + for (j=-view_dist_in_chunks; j <= view_dist_in_chunks; j += 2) { + for (i=-view_dist_in_chunks; i <= view_dist_in_chunks; i += 2) { + float priority; + int cx = qchunk_x + i; + int cy = qchunk_y + j; + + priority = compute_priority(cx, cy, x, y); + if (priority >= min_priority) { + int slot_x = (cx>>1) & (CACHED_MESH_NUM_X-1); + int slot_y = (cy>>1) & (CACHED_MESH_NUM_Y-1); + chunk_mesh *cm = &cached_chunk_mesh[slot_y][slot_x]; + ++chunk_locations; + if (cm->state == STATE_valid && priority >= 0) { + // check if chunk pos actually matches + if (cm->chunk_x != cx || cm->chunk_y != cy) { + // we have a stale chunk we need to recreate + free_chunk(slot_x, slot_y); // it probably will have already gotten freed, but just in case + } + } + if (cm->state == STATE_invalid) { + cm->chunk_x = cx; + cm->chunk_y = cy; + cm->state = STATE_needed; + } + cm->priority = priority; + } + } + } + + // draw front-to-back + for (rad = 0; rad <= view_dist_in_chunks; rad += 2) { + for (j=-rad; j <= rad; j += 2) { + // if j is +- rad, then iterate i through all values + // if j isn't +-rad, then i should be only -rad & rad + int step = 2; + if (abs(j) != rad) + step = 2*rad; + for (i=-rad; i <= rad; i += step) { + int cx = qchunk_x + i; + int cy = qchunk_y + j; + int slot_x = (cx>>1) & (CACHED_MESH_NUM_X-1); + int slot_y = (cy>>1) & (CACHED_MESH_NUM_Y-1); + chunk_mesh *cm = &cached_chunk_mesh[slot_y][slot_x]; + if (cm->state == STATE_valid && cm->priority >= 0) { + ++chunks_considered; + quads_considered += cm->num_quads; + if (is_box_in_frustum(cm->bounds[0], cm->bounds[1])) { + ++chunks_in_frustum; + + // @TODO if in range + stbglUniform3fv(uniform_loc[STBVOX_UNIFORM_transform], 3, cm->transform[0]); + glBindBufferARB(GL_ARRAY_BUFFER_ARB, cm->vbuf); + glVertexAttribIPointer(0, 1, GL_UNSIGNED_INT, 4, (void*) 0); + glBindTexture(GL_TEXTURE_BUFFER_ARB, cm->fbuf_tex); + glDrawArrays(GL_QUADS, 0, cm->num_quads*4); + quads_rendered += cm->num_quads; + + chunk_storage_rendered += cm->vbuf_size + cm->fbuf_size; + } + chunk_storage_considered += cm->vbuf_size + cm->fbuf_size; + } + } + } + } + + stbglDisableVertexAttribArray(0); + glBindBufferARB(GL_ARRAY_BUFFER_ARB, 0); + glActiveTextureARB(GL_TEXTURE0_ARB); + + stbglUseProgram(0); + num_meshes_started = 0; + + { + #define MAX_QUEUE 8 + float highest_priority[MAX_QUEUE]; + int highest_i[MAX_QUEUE], highest_j[MAX_QUEUE]; + float lowest_priority = view_dist_in_chunks * view_dist_in_chunks * 16 * 16.0f; + int lowest_i = -1, lowest_j = -1; + + for (i=0; i < MAX_QUEUE; ++i) { + highest_priority[i] = min_priority; + highest_i[i] = -1; + highest_j[i] = -1; + } + + for (j=0; j < CACHED_MESH_NUM_Y; ++j) { + for (i=0; i < CACHED_MESH_NUM_X; ++i) { + chunk_mesh *cm = &cached_chunk_mesh[j][i]; + if (cm->state == STATE_valid) { + cm->priority = compute_priority(cm->chunk_x, cm->chunk_y, x, y); + chunk_storage_total += cm->vbuf_size + cm->fbuf_size; + if (cm->priority < lowest_priority) { + lowest_priority = cm->priority; + lowest_i = i; + lowest_j = j; + } + } + if (cm->state == STATE_needed) { + cm->priority = compute_priority(cm->chunk_x, cm->chunk_y, x, y); + if (cm->priority < min_priority) + cm->state = STATE_invalid; + else if (cm->priority > highest_priority[0]) { + int k; + highest_priority[0] = cm->priority; + highest_i[0] = i; + highest_j[0] = j; + // bubble this up to right place + for (k=0; k < MAX_QUEUE-1; ++k) { + if (highest_priority[k] > highest_priority[k+1]) { + highest_priority[k] = highest_priority[k+1]; + highest_priority[k+1] = cm->priority; + highest_i[k] = highest_i[k+1]; + highest_i[k+1] = i; + highest_j[k] = highest_j[k+1]; + highest_j[k+1] = j; + } else { + break; + } + } + } + } + } + } + + + // I couldn't find any straightforward logic that avoids + // the hysteresis problem of continually creating & freeing + // a block on the margin, so I just don't free a block until + // it's out of range, but this doesn't actually correctly + // handle when the cache is too small for the given range + if (chunk_storage_total >= min_chunk_storage && lowest_i >= 0) { + if (cached_chunk_mesh[lowest_j][lowest_i].priority < -1200) // -1000? 0? + free_chunk(lowest_i, lowest_j); + } + + if (chunk_storage_total < max_chunk_storage && highest_i[0] >= 0) { + for (j=MAX_QUEUE-1; j >= 0; --j) { + if (highest_j[0] >= 0) { + chunk_mesh *cm = &cached_chunk_mesh[highest_j[j]][highest_i[j]]; + if (request_chunk(cm->chunk_x, cm->chunk_y)) { + cm->state = STATE_requested; + } else { + // if we couldn't queue this one, skip the remainder + break; + } + } + } + } + } + + update_meshes_from_render_thread(); + + num_threads_active = 0; + for (i=0; i < num_mesh_workers; ++i) { + num_threads_active += (mesh_data[i].state == WSTATE_running); + } +} diff --git a/impeller/third_party/stb/stb/tests/caveview/caveview.dsp b/impeller/third_party/stb/stb/tests/caveview/caveview.dsp new file mode 100644 index 0000000000000..2a462d3f877fb --- /dev/null +++ b/impeller/third_party/stb/stb/tests/caveview/caveview.dsp @@ -0,0 +1,157 @@ +# Microsoft Developer Studio Project File - Name="caveview" - Package Owner=<4> +# Microsoft Developer Studio Generated Build File, Format Version 6.00 +# ** DO NOT EDIT ** + +# TARGTYPE "Win32 (x86) Application" 0x0101 + +CFG=caveview - Win32 Debug +!MESSAGE This is not a valid makefile. To build this project using NMAKE, +!MESSAGE use the Export Makefile command and run +!MESSAGE +!MESSAGE NMAKE /f "caveview.mak". +!MESSAGE +!MESSAGE You can specify a configuration when running NMAKE +!MESSAGE by defining the macro CFG on the command line. For example: +!MESSAGE +!MESSAGE NMAKE /f "caveview.mak" CFG="caveview - Win32 Debug" +!MESSAGE +!MESSAGE Possible choices for configuration are: +!MESSAGE +!MESSAGE "caveview - Win32 Release" (based on "Win32 (x86) Application") +!MESSAGE "caveview - Win32 Debug" (based on "Win32 (x86) Application") +!MESSAGE + +# Begin Project +# PROP AllowPerConfigDependencies 0 +# PROP Scc_ProjName "" +# PROP Scc_LocalPath "" +CPP=cl.exe +MTL=midl.exe +RSC=rc.exe + +!IF "$(CFG)" == "caveview - Win32 Release" + +# PROP BASE Use_MFC 0 +# PROP BASE Use_Debug_Libraries 0 +# PROP BASE Output_Dir "Release" +# PROP BASE Intermediate_Dir "Release" +# PROP BASE Target_Dir "" +# PROP Use_MFC 0 +# PROP Use_Debug_Libraries 0 +# PROP Output_Dir "Release" +# PROP Intermediate_Dir "Release" +# PROP Ignore_Export_Lib 0 +# PROP Target_Dir "" +# ADD BASE CPP /nologo /W3 /GX /O2 /D "WIN32" /D "NDEBUG" /D "_WINDOWS" /D "_MBCS" /YX /FD /c +# ADD CPP /nologo /MD /W3 /WX /GX /Zd /O2 /I "../.." /D "WIN32" /D "NDEBUG" /D "_WINDOWS" /D "_MBCS" /FD /c +# SUBTRACT CPP /YX +# ADD BASE MTL /nologo /D "NDEBUG" /mktyplib203 /win32 +# ADD MTL /nologo /D "NDEBUG" /mktyplib203 /win32 +# ADD BASE RSC /l 0x409 /d "NDEBUG" +# ADD RSC /l 0x409 /d "NDEBUG" +BSC32=bscmake.exe +# ADD BASE BSC32 /nologo +# ADD BSC32 /nologo +LINK32=link.exe +# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:windows /machine:I386 +# ADD LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib sdl2.lib opengl32.lib glu32.lib winmm.lib sdl2_mixer.lib advapi32.lib /nologo /subsystem:windows /debug /machine:I386 +# SUBTRACT LINK32 /map + +!ELSEIF "$(CFG)" == "caveview - Win32 Debug" + +# PROP BASE Use_MFC 0 +# PROP BASE Use_Debug_Libraries 1 +# PROP BASE Output_Dir "Debug" +# PROP BASE Intermediate_Dir "Debug" +# PROP BASE Target_Dir "" +# PROP Use_MFC 0 +# PROP Use_Debug_Libraries 1 +# PROP Output_Dir "Debug" +# PROP Intermediate_Dir "Debug" +# PROP Ignore_Export_Lib 0 +# PROP Target_Dir "" +# ADD BASE CPP /nologo /W3 /Gm /GX /ZI /Od /D "WIN32" /D "_DEBUG" /D "_WINDOWS" /D "_MBCS" /YX /FD /GZ /c +# ADD CPP /nologo /MDd /W3 /WX /Gm /GX /Zi /Od /I "../.." /D "WIN32" /D "_DEBUG" /D "_WINDOWS" /D "_MBCS" /FD /GZ /c +# ADD BASE MTL /nologo /D "_DEBUG" /mktyplib203 /win32 +# ADD MTL /nologo /D "_DEBUG" /mktyplib203 /win32 +# ADD BASE RSC /l 0x409 /d "_DEBUG" +# ADD RSC /l 0x409 /d "_DEBUG" +BSC32=bscmake.exe +# ADD BASE BSC32 /nologo +# ADD BSC32 /nologo +LINK32=link.exe +# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:windows /debug /machine:I386 /pdbtype:sept +# ADD LINK32 kernel32.lib user32.lib gdi32.lib advapi32.lib winspool.lib comdlg32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib sdl2.lib opengl32.lib glu32.lib winmm.lib sdl2_mixer.lib /nologo /subsystem:windows /incremental:no /debug /machine:I386 /pdbtype:sept + +!ENDIF + +# Begin Target + +# Name "caveview - Win32 Release" +# Name "caveview - Win32 Debug" +# Begin Source File + +SOURCE=.\cave_main.c +# End Source File +# Begin Source File + +SOURCE=.\cave_mesher.c +# End Source File +# Begin Source File + +SOURCE=.\cave_parse.c +# End Source File +# Begin Source File + +SOURCE=.\cave_parse.h +# End Source File +# Begin Source File + +SOURCE=.\cave_render.c +# End Source File +# Begin Source File + +SOURCE=.\caveview.h +# End Source File +# Begin Source File + +SOURCE=.\glext.h +# End Source File +# Begin Source File + +SOURCE=.\glext_list.h +# End Source File +# Begin Source File + +SOURCE=.\README.md +# End Source File +# Begin Source File + +SOURCE=.\win32\SDL_windows_main.c +# End Source File +# Begin Source File + +SOURCE=..\..\stb.h +# End Source File +# Begin Source File + +SOURCE=..\..\stb_easy_font.h +# End Source File +# Begin Source File + +SOURCE=.\stb_gl.h +# End Source File +# Begin Source File + +SOURCE=.\stb_glprog.h +# End Source File +# Begin Source File + +SOURCE=..\..\stb_image.h +# End Source File +# Begin Source File + +SOURCE=..\..\stb_voxel_render.h +# End Source File +# End Target +# End Project diff --git a/impeller/third_party/stb/stb/tests/caveview/caveview.dsw b/impeller/third_party/stb/stb/tests/caveview/caveview.dsw new file mode 100644 index 0000000000000..ddc9387e05702 --- /dev/null +++ b/impeller/third_party/stb/stb/tests/caveview/caveview.dsw @@ -0,0 +1,29 @@ +Microsoft Developer Studio Workspace File, Format Version 6.00 +# WARNING: DO NOT EDIT OR DELETE THIS WORKSPACE FILE! + +############################################################################### + +Project: "caveview"=.\caveview.dsp - Package Owner=<4> + +Package=<5> +{{{ +}}} + +Package=<4> +{{{ +}}} + +############################################################################### + +Global: + +Package=<5> +{{{ +}}} + +Package=<3> +{{{ +}}} + +############################################################################### + diff --git a/impeller/third_party/stb/stb/tests/caveview/caveview.h b/impeller/third_party/stb/stb/tests/caveview/caveview.h new file mode 100644 index 0000000000000..73a71da0d3b29 --- /dev/null +++ b/impeller/third_party/stb/stb/tests/caveview/caveview.h @@ -0,0 +1,50 @@ +#ifndef INCLUDE_CAVEVIEW_H +#define INCLUDE_CAVEVIEW_H + +#include "stb.h" + +#include "stb_voxel_render.h" + +typedef struct +{ + int cx,cy; + + stbvox_mesh_maker mm; + + uint8 *build_buffer; + uint8 *face_buffer; + + int num_quads; + float transform[3][3]; + float bounds[2][3]; + + uint8 sv_blocktype[34][34][18]; + uint8 sv_lighting [34][34][18]; +} raw_mesh; + +// a 3D checkerboard of empty,solid would be: 32x32x255x6/2 ~= 800000 +// an all-leaf qchunk would be: 32 x 32 x 255 x 6 ~= 1,600,000 + +#define BUILD_QUAD_MAX 400000 +#define BUILD_BUFFER_SIZE (4*4*BUILD_QUAD_MAX) // 4 bytes per vertex, 4 vertices per quad +#define FACE_BUFFER_SIZE ( 4*BUILD_QUAD_MAX) // 4 bytes per quad + + +extern void mesh_init(void); +extern void render_init(void); +extern void world_init(void); +extern void ods(char *fmt, ...); // output debug string +extern void reset_cache_size(int size); + + +extern void render_caves(float pos[3]); + + +#include "cave_parse.h" // fast_chunk + +extern fast_chunk *get_converted_fastchunk(int chunk_x, int chunk_y); +extern void build_chunk(int chunk_x, int chunk_y, fast_chunk *fc_table[4][4], raw_mesh *rm); +extern void reset_cache_size(int size); +extern void deref_fastchunk(fast_chunk *fc); + +#endif \ No newline at end of file diff --git a/impeller/third_party/stb/stb/tests/caveview/glext.h b/impeller/third_party/stb/stb/tests/caveview/glext.h new file mode 100644 index 0000000000000..c6a233ad17f93 --- /dev/null +++ b/impeller/third_party/stb/stb/tests/caveview/glext.h @@ -0,0 +1,11124 @@ +#ifndef __glext_h_ +#define __glext_h_ 1 + +#ifdef __cplusplus +extern "C" { +#endif + +/* +** Copyright (c) 2013 The Khronos Group Inc. +** +** Permission is hereby granted, free of charge, to any person obtaining a +** copy of this software and/or associated documentation files (the +** "Materials"), to deal in the Materials without restriction, including +** without limitation the rights to use, copy, modify, merge, publish, +** distribute, sublicense, and/or sell copies of the Materials, and to +** permit persons to whom the Materials are furnished to do so, subject to +** the following conditions: +** +** The above copyright notice and this permission notice shall be included +** in all copies or substantial portions of the Materials. +** +** THE MATERIALS ARE PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +** EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +** MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. +** IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +** CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, +** TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE +** MATERIALS OR THE USE OR OTHER DEALINGS IN THE MATERIALS. +*/ +/* +** This header is generated from the Khronos OpenGL / OpenGL ES XML +** API Registry. The current version of the Registry, generator scripts +** used to make the header, and the header can be found at +** http://www.opengl.org/registry/ +** +** Khronos $Revision: 24756 $ on $Date: 2014-01-14 03:42:29 -0800 (Tue, 14 Jan 2014) $ +*/ + +#if defined(_WIN32) && !defined(APIENTRY) && !defined(__CYGWIN__) && !defined(__SCITECH_SNAP__) +#ifndef WIN32_LEAN_AND_MEAN +#define WIN32_LEAN_AND_MEAN 1 +#endif +#include +#endif + +#ifndef APIENTRY +#define APIENTRY +#endif +#ifndef APIENTRYP +#define APIENTRYP APIENTRY * +#endif +#ifndef GLAPI +#define GLAPI extern +#endif + +#define GL_GLEXT_VERSION 20140114 + +/* Generated C header for: + * API: gl + * Profile: compatibility + * Versions considered: .* + * Versions emitted: 1\.[2-9]|[234]\.[0-9] + * Default extensions included: gl + * Additional extensions included: _nomatch_^ + * Extensions removed: _nomatch_^ + */ + +#ifndef GL_VERSION_1_2 +#define GL_VERSION_1_2 1 +#define GL_UNSIGNED_BYTE_3_3_2 0x8032 +#define GL_UNSIGNED_SHORT_4_4_4_4 0x8033 +#define GL_UNSIGNED_SHORT_5_5_5_1 0x8034 +#define GL_UNSIGNED_INT_8_8_8_8 0x8035 +#define GL_UNSIGNED_INT_10_10_10_2 0x8036 +#define GL_TEXTURE_BINDING_3D 0x806A +#define GL_PACK_SKIP_IMAGES 0x806B +#define GL_PACK_IMAGE_HEIGHT 0x806C +#define GL_UNPACK_SKIP_IMAGES 0x806D +#define GL_UNPACK_IMAGE_HEIGHT 0x806E +#define GL_TEXTURE_3D 0x806F +#define GL_PROXY_TEXTURE_3D 0x8070 +#define GL_TEXTURE_DEPTH 0x8071 +#define GL_TEXTURE_WRAP_R 0x8072 +#define GL_MAX_3D_TEXTURE_SIZE 0x8073 +#define GL_UNSIGNED_BYTE_2_3_3_REV 0x8362 +#define GL_UNSIGNED_SHORT_5_6_5 0x8363 +#define GL_UNSIGNED_SHORT_5_6_5_REV 0x8364 +#define GL_UNSIGNED_SHORT_4_4_4_4_REV 0x8365 +#define GL_UNSIGNED_SHORT_1_5_5_5_REV 0x8366 +#define GL_UNSIGNED_INT_8_8_8_8_REV 0x8367 +#define GL_UNSIGNED_INT_2_10_10_10_REV 0x8368 +#define GL_BGR 0x80E0 +#define GL_BGRA 0x80E1 +#define GL_MAX_ELEMENTS_VERTICES 0x80E8 +#define GL_MAX_ELEMENTS_INDICES 0x80E9 +#define GL_CLAMP_TO_EDGE 0x812F +#define GL_TEXTURE_MIN_LOD 0x813A +#define GL_TEXTURE_MAX_LOD 0x813B +#define GL_TEXTURE_BASE_LEVEL 0x813C +#define GL_TEXTURE_MAX_LEVEL 0x813D +#define GL_SMOOTH_POINT_SIZE_RANGE 0x0B12 +#define GL_SMOOTH_POINT_SIZE_GRANULARITY 0x0B13 +#define GL_SMOOTH_LINE_WIDTH_RANGE 0x0B22 +#define GL_SMOOTH_LINE_WIDTH_GRANULARITY 0x0B23 +#define GL_ALIASED_LINE_WIDTH_RANGE 0x846E +#define GL_RESCALE_NORMAL 0x803A +#define GL_LIGHT_MODEL_COLOR_CONTROL 0x81F8 +#define GL_SINGLE_COLOR 0x81F9 +#define GL_SEPARATE_SPECULAR_COLOR 0x81FA +#define GL_ALIASED_POINT_SIZE_RANGE 0x846D +typedef void (APIENTRYP PFNGLDRAWRANGEELEMENTSPROC) (GLenum mode, GLuint start, GLuint end, GLsizei count, GLenum type, const void *indices); +typedef void (APIENTRYP PFNGLTEXIMAGE3DPROC) (GLenum target, GLint level, GLint internalformat, GLsizei width, GLsizei height, GLsizei depth, GLint border, GLenum format, GLenum type, const void *pixels); +typedef void (APIENTRYP PFNGLTEXSUBIMAGE3DPROC) (GLenum target, GLint level, GLint xoffset, GLint yoffset, GLint zoffset, GLsizei width, GLsizei height, GLsizei depth, GLenum format, GLenum type, const void *pixels); +typedef void (APIENTRYP PFNGLCOPYTEXSUBIMAGE3DPROC) (GLenum target, GLint level, GLint xoffset, GLint yoffset, GLint zoffset, GLint x, GLint y, GLsizei width, GLsizei height); +#ifdef GL_GLEXT_PROTOTYPES +GLAPI void APIENTRY glDrawRangeElements (GLenum mode, GLuint start, GLuint end, GLsizei count, GLenum type, const void *indices); +GLAPI void APIENTRY glTexImage3D (GLenum target, GLint level, GLint internalformat, GLsizei width, GLsizei height, GLsizei depth, GLint border, GLenum format, GLenum type, const void *pixels); +GLAPI void APIENTRY glTexSubImage3D (GLenum target, GLint level, GLint xoffset, GLint yoffset, GLint zoffset, GLsizei width, GLsizei height, GLsizei depth, GLenum format, GLenum type, const void *pixels); +GLAPI void APIENTRY glCopyTexSubImage3D (GLenum target, GLint level, GLint xoffset, GLint yoffset, GLint zoffset, GLint x, GLint y, GLsizei width, GLsizei height); +#endif +#endif /* GL_VERSION_1_2 */ + +#ifndef GL_VERSION_1_3 +#define GL_VERSION_1_3 1 +#define GL_TEXTURE0 0x84C0 +#define GL_TEXTURE1 0x84C1 +#define GL_TEXTURE2 0x84C2 +#define GL_TEXTURE3 0x84C3 +#define GL_TEXTURE4 0x84C4 +#define GL_TEXTURE5 0x84C5 +#define GL_TEXTURE6 0x84C6 +#define GL_TEXTURE7 0x84C7 +#define GL_TEXTURE8 0x84C8 +#define GL_TEXTURE9 0x84C9 +#define GL_TEXTURE10 0x84CA +#define GL_TEXTURE11 0x84CB +#define GL_TEXTURE12 0x84CC +#define GL_TEXTURE13 0x84CD +#define GL_TEXTURE14 0x84CE +#define GL_TEXTURE15 0x84CF +#define GL_TEXTURE16 0x84D0 +#define GL_TEXTURE17 0x84D1 +#define GL_TEXTURE18 0x84D2 +#define GL_TEXTURE19 0x84D3 +#define GL_TEXTURE20 0x84D4 +#define GL_TEXTURE21 0x84D5 +#define GL_TEXTURE22 0x84D6 +#define GL_TEXTURE23 0x84D7 +#define GL_TEXTURE24 0x84D8 +#define GL_TEXTURE25 0x84D9 +#define GL_TEXTURE26 0x84DA +#define GL_TEXTURE27 0x84DB +#define GL_TEXTURE28 0x84DC +#define GL_TEXTURE29 0x84DD +#define GL_TEXTURE30 0x84DE +#define GL_TEXTURE31 0x84DF +#define GL_ACTIVE_TEXTURE 0x84E0 +#define GL_MULTISAMPLE 0x809D +#define GL_SAMPLE_ALPHA_TO_COVERAGE 0x809E +#define GL_SAMPLE_ALPHA_TO_ONE 0x809F +#define GL_SAMPLE_COVERAGE 0x80A0 +#define GL_SAMPLE_BUFFERS 0x80A8 +#define GL_SAMPLES 0x80A9 +#define GL_SAMPLE_COVERAGE_VALUE 0x80AA +#define GL_SAMPLE_COVERAGE_INVERT 0x80AB +#define GL_TEXTURE_CUBE_MAP 0x8513 +#define GL_TEXTURE_BINDING_CUBE_MAP 0x8514 +#define GL_TEXTURE_CUBE_MAP_POSITIVE_X 0x8515 +#define GL_TEXTURE_CUBE_MAP_NEGATIVE_X 0x8516 +#define GL_TEXTURE_CUBE_MAP_POSITIVE_Y 0x8517 +#define GL_TEXTURE_CUBE_MAP_NEGATIVE_Y 0x8518 +#define GL_TEXTURE_CUBE_MAP_POSITIVE_Z 0x8519 +#define GL_TEXTURE_CUBE_MAP_NEGATIVE_Z 0x851A +#define GL_PROXY_TEXTURE_CUBE_MAP 0x851B +#define GL_MAX_CUBE_MAP_TEXTURE_SIZE 0x851C +#define GL_COMPRESSED_RGB 0x84ED +#define GL_COMPRESSED_RGBA 0x84EE +#define GL_TEXTURE_COMPRESSION_HINT 0x84EF +#define GL_TEXTURE_COMPRESSED_IMAGE_SIZE 0x86A0 +#define GL_TEXTURE_COMPRESSED 0x86A1 +#define GL_NUM_COMPRESSED_TEXTURE_FORMATS 0x86A2 +#define GL_COMPRESSED_TEXTURE_FORMATS 0x86A3 +#define GL_CLAMP_TO_BORDER 0x812D +#define GL_CLIENT_ACTIVE_TEXTURE 0x84E1 +#define GL_MAX_TEXTURE_UNITS 0x84E2 +#define GL_TRANSPOSE_MODELVIEW_MATRIX 0x84E3 +#define GL_TRANSPOSE_PROJECTION_MATRIX 0x84E4 +#define GL_TRANSPOSE_TEXTURE_MATRIX 0x84E5 +#define GL_TRANSPOSE_COLOR_MATRIX 0x84E6 +#define GL_MULTISAMPLE_BIT 0x20000000 +#define GL_NORMAL_MAP 0x8511 +#define GL_REFLECTION_MAP 0x8512 +#define GL_COMPRESSED_ALPHA 0x84E9 +#define GL_COMPRESSED_LUMINANCE 0x84EA +#define GL_COMPRESSED_LUMINANCE_ALPHA 0x84EB +#define GL_COMPRESSED_INTENSITY 0x84EC +#define GL_COMBINE 0x8570 +#define GL_COMBINE_RGB 0x8571 +#define GL_COMBINE_ALPHA 0x8572 +#define GL_SOURCE0_RGB 0x8580 +#define GL_SOURCE1_RGB 0x8581 +#define GL_SOURCE2_RGB 0x8582 +#define GL_SOURCE0_ALPHA 0x8588 +#define GL_SOURCE1_ALPHA 0x8589 +#define GL_SOURCE2_ALPHA 0x858A +#define GL_OPERAND0_RGB 0x8590 +#define GL_OPERAND1_RGB 0x8591 +#define GL_OPERAND2_RGB 0x8592 +#define GL_OPERAND0_ALPHA 0x8598 +#define GL_OPERAND1_ALPHA 0x8599 +#define GL_OPERAND2_ALPHA 0x859A +#define GL_RGB_SCALE 0x8573 +#define GL_ADD_SIGNED 0x8574 +#define GL_INTERPOLATE 0x8575 +#define GL_SUBTRACT 0x84E7 +#define GL_CONSTANT 0x8576 +#define GL_PRIMARY_COLOR 0x8577 +#define GL_PREVIOUS 0x8578 +#define GL_DOT3_RGB 0x86AE +#define GL_DOT3_RGBA 0x86AF +typedef void (APIENTRYP PFNGLACTIVETEXTUREPROC) (GLenum texture); +typedef void (APIENTRYP PFNGLSAMPLECOVERAGEPROC) (GLfloat value, GLboolean invert); +typedef void (APIENTRYP PFNGLCOMPRESSEDTEXIMAGE3DPROC) (GLenum target, GLint level, GLenum internalformat, GLsizei width, GLsizei height, GLsizei depth, GLint border, GLsizei imageSize, const void *data); +typedef void (APIENTRYP PFNGLCOMPRESSEDTEXIMAGE2DPROC) (GLenum target, GLint level, GLenum internalformat, GLsizei width, GLsizei height, GLint border, GLsizei imageSize, const void *data); +typedef void (APIENTRYP PFNGLCOMPRESSEDTEXIMAGE1DPROC) (GLenum target, GLint level, GLenum internalformat, GLsizei width, GLint border, GLsizei imageSize, const void *data); +typedef void (APIENTRYP PFNGLCOMPRESSEDTEXSUBIMAGE3DPROC) (GLenum target, GLint level, GLint xoffset, GLint yoffset, GLint zoffset, GLsizei width, GLsizei height, GLsizei depth, GLenum format, GLsizei imageSize, const void *data); +typedef void (APIENTRYP PFNGLCOMPRESSEDTEXSUBIMAGE2DPROC) (GLenum target, GLint level, GLint xoffset, GLint yoffset, GLsizei width, GLsizei height, GLenum format, GLsizei imageSize, const void *data); +typedef void (APIENTRYP PFNGLCOMPRESSEDTEXSUBIMAGE1DPROC) (GLenum target, GLint level, GLint xoffset, GLsizei width, GLenum format, GLsizei imageSize, const void *data); +typedef void (APIENTRYP PFNGLGETCOMPRESSEDTEXIMAGEPROC) (GLenum target, GLint level, void *img); +typedef void (APIENTRYP PFNGLCLIENTACTIVETEXTUREPROC) (GLenum texture); +typedef void (APIENTRYP PFNGLMULTITEXCOORD1DPROC) (GLenum target, GLdouble s); +typedef void (APIENTRYP PFNGLMULTITEXCOORD1DVPROC) (GLenum target, const GLdouble *v); +typedef void (APIENTRYP PFNGLMULTITEXCOORD1FPROC) (GLenum target, GLfloat s); +typedef void (APIENTRYP PFNGLMULTITEXCOORD1FVPROC) (GLenum target, const GLfloat *v); +typedef void (APIENTRYP PFNGLMULTITEXCOORD1IPROC) (GLenum target, GLint s); +typedef void (APIENTRYP PFNGLMULTITEXCOORD1IVPROC) (GLenum target, const GLint *v); +typedef void (APIENTRYP PFNGLMULTITEXCOORD1SPROC) (GLenum target, GLshort s); +typedef void (APIENTRYP PFNGLMULTITEXCOORD1SVPROC) (GLenum target, const GLshort *v); +typedef void (APIENTRYP PFNGLMULTITEXCOORD2DPROC) (GLenum target, GLdouble s, GLdouble t); +typedef void (APIENTRYP PFNGLMULTITEXCOORD2DVPROC) (GLenum target, const GLdouble *v); +typedef void (APIENTRYP PFNGLMULTITEXCOORD2FPROC) (GLenum target, GLfloat s, GLfloat t); +typedef void (APIENTRYP PFNGLMULTITEXCOORD2FVPROC) (GLenum target, const GLfloat *v); +typedef void (APIENTRYP PFNGLMULTITEXCOORD2IPROC) (GLenum target, GLint s, GLint t); +typedef void (APIENTRYP PFNGLMULTITEXCOORD2IVPROC) (GLenum target, const GLint *v); +typedef void (APIENTRYP PFNGLMULTITEXCOORD2SPROC) (GLenum target, GLshort s, GLshort t); +typedef void (APIENTRYP PFNGLMULTITEXCOORD2SVPROC) (GLenum target, const GLshort *v); +typedef void (APIENTRYP PFNGLMULTITEXCOORD3DPROC) (GLenum target, GLdouble s, GLdouble t, GLdouble r); +typedef void (APIENTRYP PFNGLMULTITEXCOORD3DVPROC) (GLenum target, const GLdouble *v); +typedef void (APIENTRYP PFNGLMULTITEXCOORD3FPROC) (GLenum target, GLfloat s, GLfloat t, GLfloat r); +typedef void (APIENTRYP PFNGLMULTITEXCOORD3FVPROC) (GLenum target, const GLfloat *v); +typedef void (APIENTRYP PFNGLMULTITEXCOORD3IPROC) (GLenum target, GLint s, GLint t, GLint r); +typedef void (APIENTRYP PFNGLMULTITEXCOORD3IVPROC) (GLenum target, const GLint *v); +typedef void (APIENTRYP PFNGLMULTITEXCOORD3SPROC) (GLenum target, GLshort s, GLshort t, GLshort r); +typedef void (APIENTRYP PFNGLMULTITEXCOORD3SVPROC) (GLenum target, const GLshort *v); +typedef void (APIENTRYP PFNGLMULTITEXCOORD4DPROC) (GLenum target, GLdouble s, GLdouble t, GLdouble r, GLdouble q); +typedef void (APIENTRYP PFNGLMULTITEXCOORD4DVPROC) (GLenum target, const GLdouble *v); +typedef void (APIENTRYP PFNGLMULTITEXCOORD4FPROC) (GLenum target, GLfloat s, GLfloat t, GLfloat r, GLfloat q); +typedef void (APIENTRYP PFNGLMULTITEXCOORD4FVPROC) (GLenum target, const GLfloat *v); +typedef void (APIENTRYP PFNGLMULTITEXCOORD4IPROC) (GLenum target, GLint s, GLint t, GLint r, GLint q); +typedef void (APIENTRYP PFNGLMULTITEXCOORD4IVPROC) (GLenum target, const GLint *v); +typedef void (APIENTRYP PFNGLMULTITEXCOORD4SPROC) (GLenum target, GLshort s, GLshort t, GLshort r, GLshort q); +typedef void (APIENTRYP PFNGLMULTITEXCOORD4SVPROC) (GLenum target, const GLshort *v); +typedef void (APIENTRYP PFNGLLOADTRANSPOSEMATRIXFPROC) (const GLfloat *m); +typedef void (APIENTRYP PFNGLLOADTRANSPOSEMATRIXDPROC) (const GLdouble *m); +typedef void (APIENTRYP PFNGLMULTTRANSPOSEMATRIXFPROC) (const GLfloat *m); +typedef void (APIENTRYP PFNGLMULTTRANSPOSEMATRIXDPROC) (const GLdouble *m); +#ifdef GL_GLEXT_PROTOTYPES +GLAPI void APIENTRY glActiveTexture (GLenum texture); +GLAPI void APIENTRY glSampleCoverage (GLfloat value, GLboolean invert); +GLAPI void APIENTRY glCompressedTexImage3D (GLenum target, GLint level, GLenum internalformat, GLsizei width, GLsizei height, GLsizei depth, GLint border, GLsizei imageSize, const void *data); +GLAPI void APIENTRY glCompressedTexImage2D (GLenum target, GLint level, GLenum internalformat, GLsizei width, GLsizei height, GLint border, GLsizei imageSize, const void *data); +GLAPI void APIENTRY glCompressedTexImage1D (GLenum target, GLint level, GLenum internalformat, GLsizei width, GLint border, GLsizei imageSize, const void *data); +GLAPI void APIENTRY glCompressedTexSubImage3D (GLenum target, GLint level, GLint xoffset, GLint yoffset, GLint zoffset, GLsizei width, GLsizei height, GLsizei depth, GLenum format, GLsizei imageSize, const void *data); +GLAPI void APIENTRY glCompressedTexSubImage2D (GLenum target, GLint level, GLint xoffset, GLint yoffset, GLsizei width, GLsizei height, GLenum format, GLsizei imageSize, const void *data); +GLAPI void APIENTRY glCompressedTexSubImage1D (GLenum target, GLint level, GLint xoffset, GLsizei width, GLenum format, GLsizei imageSize, const void *data); +GLAPI void APIENTRY glGetCompressedTexImage (GLenum target, GLint level, void *img); +GLAPI void APIENTRY glClientActiveTexture (GLenum texture); +GLAPI void APIENTRY glMultiTexCoord1d (GLenum target, GLdouble s); +GLAPI void APIENTRY glMultiTexCoord1dv (GLenum target, const GLdouble *v); +GLAPI void APIENTRY glMultiTexCoord1f (GLenum target, GLfloat s); +GLAPI void APIENTRY glMultiTexCoord1fv (GLenum target, const GLfloat *v); +GLAPI void APIENTRY glMultiTexCoord1i (GLenum target, GLint s); +GLAPI void APIENTRY glMultiTexCoord1iv (GLenum target, const GLint *v); +GLAPI void APIENTRY glMultiTexCoord1s (GLenum target, GLshort s); +GLAPI void APIENTRY glMultiTexCoord1sv (GLenum target, const GLshort *v); +GLAPI void APIENTRY glMultiTexCoord2d (GLenum target, GLdouble s, GLdouble t); +GLAPI void APIENTRY glMultiTexCoord2dv (GLenum target, const GLdouble *v); +GLAPI void APIENTRY glMultiTexCoord2f (GLenum target, GLfloat s, GLfloat t); +GLAPI void APIENTRY glMultiTexCoord2fv (GLenum target, const GLfloat *v); +GLAPI void APIENTRY glMultiTexCoord2i (GLenum target, GLint s, GLint t); +GLAPI void APIENTRY glMultiTexCoord2iv (GLenum target, const GLint *v); +GLAPI void APIENTRY glMultiTexCoord2s (GLenum target, GLshort s, GLshort t); +GLAPI void APIENTRY glMultiTexCoord2sv (GLenum target, const GLshort *v); +GLAPI void APIENTRY glMultiTexCoord3d (GLenum target, GLdouble s, GLdouble t, GLdouble r); +GLAPI void APIENTRY glMultiTexCoord3dv (GLenum target, const GLdouble *v); +GLAPI void APIENTRY glMultiTexCoord3f (GLenum target, GLfloat s, GLfloat t, GLfloat r); +GLAPI void APIENTRY glMultiTexCoord3fv (GLenum target, const GLfloat *v); +GLAPI void APIENTRY glMultiTexCoord3i (GLenum target, GLint s, GLint t, GLint r); +GLAPI void APIENTRY glMultiTexCoord3iv (GLenum target, const GLint *v); +GLAPI void APIENTRY glMultiTexCoord3s (GLenum target, GLshort s, GLshort t, GLshort r); +GLAPI void APIENTRY glMultiTexCoord3sv (GLenum target, const GLshort *v); +GLAPI void APIENTRY glMultiTexCoord4d (GLenum target, GLdouble s, GLdouble t, GLdouble r, GLdouble q); +GLAPI void APIENTRY glMultiTexCoord4dv (GLenum target, const GLdouble *v); +GLAPI void APIENTRY glMultiTexCoord4f (GLenum target, GLfloat s, GLfloat t, GLfloat r, GLfloat q); +GLAPI void APIENTRY glMultiTexCoord4fv (GLenum target, const GLfloat *v); +GLAPI void APIENTRY glMultiTexCoord4i (GLenum target, GLint s, GLint t, GLint r, GLint q); +GLAPI void APIENTRY glMultiTexCoord4iv (GLenum target, const GLint *v); +GLAPI void APIENTRY glMultiTexCoord4s (GLenum target, GLshort s, GLshort t, GLshort r, GLshort q); +GLAPI void APIENTRY glMultiTexCoord4sv (GLenum target, const GLshort *v); +GLAPI void APIENTRY glLoadTransposeMatrixf (const GLfloat *m); +GLAPI void APIENTRY glLoadTransposeMatrixd (const GLdouble *m); +GLAPI void APIENTRY glMultTransposeMatrixf (const GLfloat *m); +GLAPI void APIENTRY glMultTransposeMatrixd (const GLdouble *m); +#endif +#endif /* GL_VERSION_1_3 */ + +#ifndef GL_VERSION_1_4 +#define GL_VERSION_1_4 1 +#define GL_BLEND_DST_RGB 0x80C8 +#define GL_BLEND_SRC_RGB 0x80C9 +#define GL_BLEND_DST_ALPHA 0x80CA +#define GL_BLEND_SRC_ALPHA 0x80CB +#define GL_POINT_FADE_THRESHOLD_SIZE 0x8128 +#define GL_DEPTH_COMPONENT16 0x81A5 +#define GL_DEPTH_COMPONENT24 0x81A6 +#define GL_DEPTH_COMPONENT32 0x81A7 +#define GL_MIRRORED_REPEAT 0x8370 +#define GL_MAX_TEXTURE_LOD_BIAS 0x84FD +#define GL_TEXTURE_LOD_BIAS 0x8501 +#define GL_INCR_WRAP 0x8507 +#define GL_DECR_WRAP 0x8508 +#define GL_TEXTURE_DEPTH_SIZE 0x884A +#define GL_TEXTURE_COMPARE_MODE 0x884C +#define GL_TEXTURE_COMPARE_FUNC 0x884D +#define GL_POINT_SIZE_MIN 0x8126 +#define GL_POINT_SIZE_MAX 0x8127 +#define GL_POINT_DISTANCE_ATTENUATION 0x8129 +#define GL_GENERATE_MIPMAP 0x8191 +#define GL_GENERATE_MIPMAP_HINT 0x8192 +#define GL_FOG_COORDINATE_SOURCE 0x8450 +#define GL_FOG_COORDINATE 0x8451 +#define GL_FRAGMENT_DEPTH 0x8452 +#define GL_CURRENT_FOG_COORDINATE 0x8453 +#define GL_FOG_COORDINATE_ARRAY_TYPE 0x8454 +#define GL_FOG_COORDINATE_ARRAY_STRIDE 0x8455 +#define GL_FOG_COORDINATE_ARRAY_POINTER 0x8456 +#define GL_FOG_COORDINATE_ARRAY 0x8457 +#define GL_COLOR_SUM 0x8458 +#define GL_CURRENT_SECONDARY_COLOR 0x8459 +#define GL_SECONDARY_COLOR_ARRAY_SIZE 0x845A +#define GL_SECONDARY_COLOR_ARRAY_TYPE 0x845B +#define GL_SECONDARY_COLOR_ARRAY_STRIDE 0x845C +#define GL_SECONDARY_COLOR_ARRAY_POINTER 0x845D +#define GL_SECONDARY_COLOR_ARRAY 0x845E +#define GL_TEXTURE_FILTER_CONTROL 0x8500 +#define GL_DEPTH_TEXTURE_MODE 0x884B +#define GL_COMPARE_R_TO_TEXTURE 0x884E +#define GL_FUNC_ADD 0x8006 +#define GL_FUNC_SUBTRACT 0x800A +#define GL_FUNC_REVERSE_SUBTRACT 0x800B +#define GL_MIN 0x8007 +#define GL_MAX 0x8008 +#define GL_CONSTANT_COLOR 0x8001 +#define GL_ONE_MINUS_CONSTANT_COLOR 0x8002 +#define GL_CONSTANT_ALPHA 0x8003 +#define GL_ONE_MINUS_CONSTANT_ALPHA 0x8004 +typedef void (APIENTRYP PFNGLBLENDFUNCSEPARATEPROC) (GLenum sfactorRGB, GLenum dfactorRGB, GLenum sfactorAlpha, GLenum dfactorAlpha); +typedef void (APIENTRYP PFNGLMULTIDRAWARRAYSPROC) (GLenum mode, const GLint *first, const GLsizei *count, GLsizei drawcount); +typedef void (APIENTRYP PFNGLMULTIDRAWELEMENTSPROC) (GLenum mode, const GLsizei *count, GLenum type, const void *const*indices, GLsizei drawcount); +typedef void (APIENTRYP PFNGLPOINTPARAMETERFPROC) (GLenum pname, GLfloat param); +typedef void (APIENTRYP PFNGLPOINTPARAMETERFVPROC) (GLenum pname, const GLfloat *params); +typedef void (APIENTRYP PFNGLPOINTPARAMETERIPROC) (GLenum pname, GLint param); +typedef void (APIENTRYP PFNGLPOINTPARAMETERIVPROC) (GLenum pname, const GLint *params); +typedef void (APIENTRYP PFNGLFOGCOORDFPROC) (GLfloat coord); +typedef void (APIENTRYP PFNGLFOGCOORDFVPROC) (const GLfloat *coord); +typedef void (APIENTRYP PFNGLFOGCOORDDPROC) (GLdouble coord); +typedef void (APIENTRYP PFNGLFOGCOORDDVPROC) (const GLdouble *coord); +typedef void (APIENTRYP PFNGLFOGCOORDPOINTERPROC) (GLenum type, GLsizei stride, const void *pointer); +typedef void (APIENTRYP PFNGLSECONDARYCOLOR3BPROC) (GLbyte red, GLbyte green, GLbyte blue); +typedef void (APIENTRYP PFNGLSECONDARYCOLOR3BVPROC) (const GLbyte *v); +typedef void (APIENTRYP PFNGLSECONDARYCOLOR3DPROC) (GLdouble red, GLdouble green, GLdouble blue); +typedef void (APIENTRYP PFNGLSECONDARYCOLOR3DVPROC) (const GLdouble *v); +typedef void (APIENTRYP PFNGLSECONDARYCOLOR3FPROC) (GLfloat red, GLfloat green, GLfloat blue); +typedef void (APIENTRYP PFNGLSECONDARYCOLOR3FVPROC) (const GLfloat *v); +typedef void (APIENTRYP PFNGLSECONDARYCOLOR3IPROC) (GLint red, GLint green, GLint blue); +typedef void (APIENTRYP PFNGLSECONDARYCOLOR3IVPROC) (const GLint *v); +typedef void (APIENTRYP PFNGLSECONDARYCOLOR3SPROC) (GLshort red, GLshort green, GLshort blue); +typedef void (APIENTRYP PFNGLSECONDARYCOLOR3SVPROC) (const GLshort *v); +typedef void (APIENTRYP PFNGLSECONDARYCOLOR3UBPROC) (GLubyte red, GLubyte green, GLubyte blue); +typedef void (APIENTRYP PFNGLSECONDARYCOLOR3UBVPROC) (const GLubyte *v); +typedef void (APIENTRYP PFNGLSECONDARYCOLOR3UIPROC) (GLuint red, GLuint green, GLuint blue); +typedef void (APIENTRYP PFNGLSECONDARYCOLOR3UIVPROC) (const GLuint *v); +typedef void (APIENTRYP PFNGLSECONDARYCOLOR3USPROC) (GLushort red, GLushort green, GLushort blue); +typedef void (APIENTRYP PFNGLSECONDARYCOLOR3USVPROC) (const GLushort *v); +typedef void (APIENTRYP PFNGLSECONDARYCOLORPOINTERPROC) (GLint size, GLenum type, GLsizei stride, const void *pointer); +typedef void (APIENTRYP PFNGLWINDOWPOS2DPROC) (GLdouble x, GLdouble y); +typedef void (APIENTRYP PFNGLWINDOWPOS2DVPROC) (const GLdouble *v); +typedef void (APIENTRYP PFNGLWINDOWPOS2FPROC) (GLfloat x, GLfloat y); +typedef void (APIENTRYP PFNGLWINDOWPOS2FVPROC) (const GLfloat *v); +typedef void (APIENTRYP PFNGLWINDOWPOS2IPROC) (GLint x, GLint y); +typedef void (APIENTRYP PFNGLWINDOWPOS2IVPROC) (const GLint *v); +typedef void (APIENTRYP PFNGLWINDOWPOS2SPROC) (GLshort x, GLshort y); +typedef void (APIENTRYP PFNGLWINDOWPOS2SVPROC) (const GLshort *v); +typedef void (APIENTRYP PFNGLWINDOWPOS3DPROC) (GLdouble x, GLdouble y, GLdouble z); +typedef void (APIENTRYP PFNGLWINDOWPOS3DVPROC) (const GLdouble *v); +typedef void (APIENTRYP PFNGLWINDOWPOS3FPROC) (GLfloat x, GLfloat y, GLfloat z); +typedef void (APIENTRYP PFNGLWINDOWPOS3FVPROC) (const GLfloat *v); +typedef void (APIENTRYP PFNGLWINDOWPOS3IPROC) (GLint x, GLint y, GLint z); +typedef void (APIENTRYP PFNGLWINDOWPOS3IVPROC) (const GLint *v); +typedef void (APIENTRYP PFNGLWINDOWPOS3SPROC) (GLshort x, GLshort y, GLshort z); +typedef void (APIENTRYP PFNGLWINDOWPOS3SVPROC) (const GLshort *v); +typedef void (APIENTRYP PFNGLBLENDCOLORPROC) (GLfloat red, GLfloat green, GLfloat blue, GLfloat alpha); +typedef void (APIENTRYP PFNGLBLENDEQUATIONPROC) (GLenum mode); +#ifdef GL_GLEXT_PROTOTYPES +GLAPI void APIENTRY glBlendFuncSeparate (GLenum sfactorRGB, GLenum dfactorRGB, GLenum sfactorAlpha, GLenum dfactorAlpha); +GLAPI void APIENTRY glMultiDrawArrays (GLenum mode, const GLint *first, const GLsizei *count, GLsizei drawcount); +GLAPI void APIENTRY glMultiDrawElements (GLenum mode, const GLsizei *count, GLenum type, const void *const*indices, GLsizei drawcount); +GLAPI void APIENTRY glPointParameterf (GLenum pname, GLfloat param); +GLAPI void APIENTRY glPointParameterfv (GLenum pname, const GLfloat *params); +GLAPI void APIENTRY glPointParameteri (GLenum pname, GLint param); +GLAPI void APIENTRY glPointParameteriv (GLenum pname, const GLint *params); +GLAPI void APIENTRY glFogCoordf (GLfloat coord); +GLAPI void APIENTRY glFogCoordfv (const GLfloat *coord); +GLAPI void APIENTRY glFogCoordd (GLdouble coord); +GLAPI void APIENTRY glFogCoorddv (const GLdouble *coord); +GLAPI void APIENTRY glFogCoordPointer (GLenum type, GLsizei stride, const void *pointer); +GLAPI void APIENTRY glSecondaryColor3b (GLbyte red, GLbyte green, GLbyte blue); +GLAPI void APIENTRY glSecondaryColor3bv (const GLbyte *v); +GLAPI void APIENTRY glSecondaryColor3d (GLdouble red, GLdouble green, GLdouble blue); +GLAPI void APIENTRY glSecondaryColor3dv (const GLdouble *v); +GLAPI void APIENTRY glSecondaryColor3f (GLfloat red, GLfloat green, GLfloat blue); +GLAPI void APIENTRY glSecondaryColor3fv (const GLfloat *v); +GLAPI void APIENTRY glSecondaryColor3i (GLint red, GLint green, GLint blue); +GLAPI void APIENTRY glSecondaryColor3iv (const GLint *v); +GLAPI void APIENTRY glSecondaryColor3s (GLshort red, GLshort green, GLshort blue); +GLAPI void APIENTRY glSecondaryColor3sv (const GLshort *v); +GLAPI void APIENTRY glSecondaryColor3ub (GLubyte red, GLubyte green, GLubyte blue); +GLAPI void APIENTRY glSecondaryColor3ubv (const GLubyte *v); +GLAPI void APIENTRY glSecondaryColor3ui (GLuint red, GLuint green, GLuint blue); +GLAPI void APIENTRY glSecondaryColor3uiv (const GLuint *v); +GLAPI void APIENTRY glSecondaryColor3us (GLushort red, GLushort green, GLushort blue); +GLAPI void APIENTRY glSecondaryColor3usv (const GLushort *v); +GLAPI void APIENTRY glSecondaryColorPointer (GLint size, GLenum type, GLsizei stride, const void *pointer); +GLAPI void APIENTRY glWindowPos2d (GLdouble x, GLdouble y); +GLAPI void APIENTRY glWindowPos2dv (const GLdouble *v); +GLAPI void APIENTRY glWindowPos2f (GLfloat x, GLfloat y); +GLAPI void APIENTRY glWindowPos2fv (const GLfloat *v); +GLAPI void APIENTRY glWindowPos2i (GLint x, GLint y); +GLAPI void APIENTRY glWindowPos2iv (const GLint *v); +GLAPI void APIENTRY glWindowPos2s (GLshort x, GLshort y); +GLAPI void APIENTRY glWindowPos2sv (const GLshort *v); +GLAPI void APIENTRY glWindowPos3d (GLdouble x, GLdouble y, GLdouble z); +GLAPI void APIENTRY glWindowPos3dv (const GLdouble *v); +GLAPI void APIENTRY glWindowPos3f (GLfloat x, GLfloat y, GLfloat z); +GLAPI void APIENTRY glWindowPos3fv (const GLfloat *v); +GLAPI void APIENTRY glWindowPos3i (GLint x, GLint y, GLint z); +GLAPI void APIENTRY glWindowPos3iv (const GLint *v); +GLAPI void APIENTRY glWindowPos3s (GLshort x, GLshort y, GLshort z); +GLAPI void APIENTRY glWindowPos3sv (const GLshort *v); +GLAPI void APIENTRY glBlendColor (GLfloat red, GLfloat green, GLfloat blue, GLfloat alpha); +GLAPI void APIENTRY glBlendEquation (GLenum mode); +#endif +#endif /* GL_VERSION_1_4 */ + +#ifndef GL_VERSION_1_5 +#define GL_VERSION_1_5 1 +#include +typedef ptrdiff_t GLsizeiptr; +typedef ptrdiff_t GLintptr; +#define GL_BUFFER_SIZE 0x8764 +#define GL_BUFFER_USAGE 0x8765 +#define GL_QUERY_COUNTER_BITS 0x8864 +#define GL_CURRENT_QUERY 0x8865 +#define GL_QUERY_RESULT 0x8866 +#define GL_QUERY_RESULT_AVAILABLE 0x8867 +#define GL_ARRAY_BUFFER 0x8892 +#define GL_ELEMENT_ARRAY_BUFFER 0x8893 +#define GL_ARRAY_BUFFER_BINDING 0x8894 +#define GL_ELEMENT_ARRAY_BUFFER_BINDING 0x8895 +#define GL_VERTEX_ATTRIB_ARRAY_BUFFER_BINDING 0x889F +#define GL_READ_ONLY 0x88B8 +#define GL_WRITE_ONLY 0x88B9 +#define GL_READ_WRITE 0x88BA +#define GL_BUFFER_ACCESS 0x88BB +#define GL_BUFFER_MAPPED 0x88BC +#define GL_BUFFER_MAP_POINTER 0x88BD +#define GL_STREAM_DRAW 0x88E0 +#define GL_STREAM_READ 0x88E1 +#define GL_STREAM_COPY 0x88E2 +#define GL_STATIC_DRAW 0x88E4 +#define GL_STATIC_READ 0x88E5 +#define GL_STATIC_COPY 0x88E6 +#define GL_DYNAMIC_DRAW 0x88E8 +#define GL_DYNAMIC_READ 0x88E9 +#define GL_DYNAMIC_COPY 0x88EA +#define GL_SAMPLES_PASSED 0x8914 +#define GL_SRC1_ALPHA 0x8589 +#define GL_VERTEX_ARRAY_BUFFER_BINDING 0x8896 +#define GL_NORMAL_ARRAY_BUFFER_BINDING 0x8897 +#define GL_COLOR_ARRAY_BUFFER_BINDING 0x8898 +#define GL_INDEX_ARRAY_BUFFER_BINDING 0x8899 +#define GL_TEXTURE_COORD_ARRAY_BUFFER_BINDING 0x889A +#define GL_EDGE_FLAG_ARRAY_BUFFER_BINDING 0x889B +#define GL_SECONDARY_COLOR_ARRAY_BUFFER_BINDING 0x889C +#define GL_FOG_COORDINATE_ARRAY_BUFFER_BINDING 0x889D +#define GL_WEIGHT_ARRAY_BUFFER_BINDING 0x889E +#define GL_FOG_COORD_SRC 0x8450 +#define GL_FOG_COORD 0x8451 +#define GL_CURRENT_FOG_COORD 0x8453 +#define GL_FOG_COORD_ARRAY_TYPE 0x8454 +#define GL_FOG_COORD_ARRAY_STRIDE 0x8455 +#define GL_FOG_COORD_ARRAY_POINTER 0x8456 +#define GL_FOG_COORD_ARRAY 0x8457 +#define GL_FOG_COORD_ARRAY_BUFFER_BINDING 0x889D +#define GL_SRC0_RGB 0x8580 +#define GL_SRC1_RGB 0x8581 +#define GL_SRC2_RGB 0x8582 +#define GL_SRC0_ALPHA 0x8588 +#define GL_SRC2_ALPHA 0x858A +typedef void (APIENTRYP PFNGLGENQUERIESPROC) (GLsizei n, GLuint *ids); +typedef void (APIENTRYP PFNGLDELETEQUERIESPROC) (GLsizei n, const GLuint *ids); +typedef GLboolean (APIENTRYP PFNGLISQUERYPROC) (GLuint id); +typedef void (APIENTRYP PFNGLBEGINQUERYPROC) (GLenum target, GLuint id); +typedef void (APIENTRYP PFNGLENDQUERYPROC) (GLenum target); +typedef void (APIENTRYP PFNGLGETQUERYIVPROC) (GLenum target, GLenum pname, GLint *params); +typedef void (APIENTRYP PFNGLGETQUERYOBJECTIVPROC) (GLuint id, GLenum pname, GLint *params); +typedef void (APIENTRYP PFNGLGETQUERYOBJECTUIVPROC) (GLuint id, GLenum pname, GLuint *params); +typedef void (APIENTRYP PFNGLBINDBUFFERPROC) (GLenum target, GLuint buffer); +typedef void (APIENTRYP PFNGLDELETEBUFFERSPROC) (GLsizei n, const GLuint *buffers); +typedef void (APIENTRYP PFNGLGENBUFFERSPROC) (GLsizei n, GLuint *buffers); +typedef GLboolean (APIENTRYP PFNGLISBUFFERPROC) (GLuint buffer); +typedef void (APIENTRYP PFNGLBUFFERDATAPROC) (GLenum target, GLsizeiptr size, const void *data, GLenum usage); +typedef void (APIENTRYP PFNGLBUFFERSUBDATAPROC) (GLenum target, GLintptr offset, GLsizeiptr size, const void *data); +typedef void (APIENTRYP PFNGLGETBUFFERSUBDATAPROC) (GLenum target, GLintptr offset, GLsizeiptr size, void *data); +typedef void *(APIENTRYP PFNGLMAPBUFFERPROC) (GLenum target, GLenum access); +typedef GLboolean (APIENTRYP PFNGLUNMAPBUFFERPROC) (GLenum target); +typedef void (APIENTRYP PFNGLGETBUFFERPARAMETERIVPROC) (GLenum target, GLenum pname, GLint *params); +typedef void (APIENTRYP PFNGLGETBUFFERPOINTERVPROC) (GLenum target, GLenum pname, void **params); +#ifdef GL_GLEXT_PROTOTYPES +GLAPI void APIENTRY glGenQueries (GLsizei n, GLuint *ids); +GLAPI void APIENTRY glDeleteQueries (GLsizei n, const GLuint *ids); +GLAPI GLboolean APIENTRY glIsQuery (GLuint id); +GLAPI void APIENTRY glBeginQuery (GLenum target, GLuint id); +GLAPI void APIENTRY glEndQuery (GLenum target); +GLAPI void APIENTRY glGetQueryiv (GLenum target, GLenum pname, GLint *params); +GLAPI void APIENTRY glGetQueryObjectiv (GLuint id, GLenum pname, GLint *params); +GLAPI void APIENTRY glGetQueryObjectuiv (GLuint id, GLenum pname, GLuint *params); +GLAPI void APIENTRY glBindBuffer (GLenum target, GLuint buffer); +GLAPI void APIENTRY glDeleteBuffers (GLsizei n, const GLuint *buffers); +GLAPI void APIENTRY glGenBuffers (GLsizei n, GLuint *buffers); +GLAPI GLboolean APIENTRY glIsBuffer (GLuint buffer); +GLAPI void APIENTRY glBufferData (GLenum target, GLsizeiptr size, const void *data, GLenum usage); +GLAPI void APIENTRY glBufferSubData (GLenum target, GLintptr offset, GLsizeiptr size, const void *data); +GLAPI void APIENTRY glGetBufferSubData (GLenum target, GLintptr offset, GLsizeiptr size, void *data); +GLAPI void *APIENTRY glMapBuffer (GLenum target, GLenum access); +GLAPI GLboolean APIENTRY glUnmapBuffer (GLenum target); +GLAPI void APIENTRY glGetBufferParameteriv (GLenum target, GLenum pname, GLint *params); +GLAPI void APIENTRY glGetBufferPointerv (GLenum target, GLenum pname, void **params); +#endif +#endif /* GL_VERSION_1_5 */ + +#ifndef GL_VERSION_2_0 +#define GL_VERSION_2_0 1 +typedef char GLchar; +#define GL_BLEND_EQUATION_RGB 0x8009 +#define GL_VERTEX_ATTRIB_ARRAY_ENABLED 0x8622 +#define GL_VERTEX_ATTRIB_ARRAY_SIZE 0x8623 +#define GL_VERTEX_ATTRIB_ARRAY_STRIDE 0x8624 +#define GL_VERTEX_ATTRIB_ARRAY_TYPE 0x8625 +#define GL_CURRENT_VERTEX_ATTRIB 0x8626 +#define GL_VERTEX_PROGRAM_POINT_SIZE 0x8642 +#define GL_VERTEX_ATTRIB_ARRAY_POINTER 0x8645 +#define GL_STENCIL_BACK_FUNC 0x8800 +#define GL_STENCIL_BACK_FAIL 0x8801 +#define GL_STENCIL_BACK_PASS_DEPTH_FAIL 0x8802 +#define GL_STENCIL_BACK_PASS_DEPTH_PASS 0x8803 +#define GL_MAX_DRAW_BUFFERS 0x8824 +#define GL_DRAW_BUFFER0 0x8825 +#define GL_DRAW_BUFFER1 0x8826 +#define GL_DRAW_BUFFER2 0x8827 +#define GL_DRAW_BUFFER3 0x8828 +#define GL_DRAW_BUFFER4 0x8829 +#define GL_DRAW_BUFFER5 0x882A +#define GL_DRAW_BUFFER6 0x882B +#define GL_DRAW_BUFFER7 0x882C +#define GL_DRAW_BUFFER8 0x882D +#define GL_DRAW_BUFFER9 0x882E +#define GL_DRAW_BUFFER10 0x882F +#define GL_DRAW_BUFFER11 0x8830 +#define GL_DRAW_BUFFER12 0x8831 +#define GL_DRAW_BUFFER13 0x8832 +#define GL_DRAW_BUFFER14 0x8833 +#define GL_DRAW_BUFFER15 0x8834 +#define GL_BLEND_EQUATION_ALPHA 0x883D +#define GL_MAX_VERTEX_ATTRIBS 0x8869 +#define GL_VERTEX_ATTRIB_ARRAY_NORMALIZED 0x886A +#define GL_MAX_TEXTURE_IMAGE_UNITS 0x8872 +#define GL_FRAGMENT_SHADER 0x8B30 +#define GL_VERTEX_SHADER 0x8B31 +#define GL_MAX_FRAGMENT_UNIFORM_COMPONENTS 0x8B49 +#define GL_MAX_VERTEX_UNIFORM_COMPONENTS 0x8B4A +#define GL_MAX_VARYING_FLOATS 0x8B4B +#define GL_MAX_VERTEX_TEXTURE_IMAGE_UNITS 0x8B4C +#define GL_MAX_COMBINED_TEXTURE_IMAGE_UNITS 0x8B4D +#define GL_SHADER_TYPE 0x8B4F +#define GL_FLOAT_VEC2 0x8B50 +#define GL_FLOAT_VEC3 0x8B51 +#define GL_FLOAT_VEC4 0x8B52 +#define GL_INT_VEC2 0x8B53 +#define GL_INT_VEC3 0x8B54 +#define GL_INT_VEC4 0x8B55 +#define GL_BOOL 0x8B56 +#define GL_BOOL_VEC2 0x8B57 +#define GL_BOOL_VEC3 0x8B58 +#define GL_BOOL_VEC4 0x8B59 +#define GL_FLOAT_MAT2 0x8B5A +#define GL_FLOAT_MAT3 0x8B5B +#define GL_FLOAT_MAT4 0x8B5C +#define GL_SAMPLER_1D 0x8B5D +#define GL_SAMPLER_2D 0x8B5E +#define GL_SAMPLER_3D 0x8B5F +#define GL_SAMPLER_CUBE 0x8B60 +#define GL_SAMPLER_1D_SHADOW 0x8B61 +#define GL_SAMPLER_2D_SHADOW 0x8B62 +#define GL_DELETE_STATUS 0x8B80 +#define GL_COMPILE_STATUS 0x8B81 +#define GL_LINK_STATUS 0x8B82 +#define GL_VALIDATE_STATUS 0x8B83 +#define GL_INFO_LOG_LENGTH 0x8B84 +#define GL_ATTACHED_SHADERS 0x8B85 +#define GL_ACTIVE_UNIFORMS 0x8B86 +#define GL_ACTIVE_UNIFORM_MAX_LENGTH 0x8B87 +#define GL_SHADER_SOURCE_LENGTH 0x8B88 +#define GL_ACTIVE_ATTRIBUTES 0x8B89 +#define GL_ACTIVE_ATTRIBUTE_MAX_LENGTH 0x8B8A +#define GL_FRAGMENT_SHADER_DERIVATIVE_HINT 0x8B8B +#define GL_SHADING_LANGUAGE_VERSION 0x8B8C +#define GL_CURRENT_PROGRAM 0x8B8D +#define GL_POINT_SPRITE_COORD_ORIGIN 0x8CA0 +#define GL_LOWER_LEFT 0x8CA1 +#define GL_UPPER_LEFT 0x8CA2 +#define GL_STENCIL_BACK_REF 0x8CA3 +#define GL_STENCIL_BACK_VALUE_MASK 0x8CA4 +#define GL_STENCIL_BACK_WRITEMASK 0x8CA5 +#define GL_VERTEX_PROGRAM_TWO_SIDE 0x8643 +#define GL_POINT_SPRITE 0x8861 +#define GL_COORD_REPLACE 0x8862 +#define GL_MAX_TEXTURE_COORDS 0x8871 +typedef void (APIENTRYP PFNGLBLENDEQUATIONSEPARATEPROC) (GLenum modeRGB, GLenum modeAlpha); +typedef void (APIENTRYP PFNGLDRAWBUFFERSPROC) (GLsizei n, const GLenum *bufs); +typedef void (APIENTRYP PFNGLSTENCILOPSEPARATEPROC) (GLenum face, GLenum sfail, GLenum dpfail, GLenum dppass); +typedef void (APIENTRYP PFNGLSTENCILFUNCSEPARATEPROC) (GLenum face, GLenum func, GLint ref, GLuint mask); +typedef void (APIENTRYP PFNGLSTENCILMASKSEPARATEPROC) (GLenum face, GLuint mask); +typedef void (APIENTRYP PFNGLATTACHSHADERPROC) (GLuint program, GLuint shader); +typedef void (APIENTRYP PFNGLBINDATTRIBLOCATIONPROC) (GLuint program, GLuint index, const GLchar *name); +typedef void (APIENTRYP PFNGLCOMPILESHADERPROC) (GLuint shader); +typedef GLuint (APIENTRYP PFNGLCREATEPROGRAMPROC) (void); +typedef GLuint (APIENTRYP PFNGLCREATESHADERPROC) (GLenum type); +typedef void (APIENTRYP PFNGLDELETEPROGRAMPROC) (GLuint program); +typedef void (APIENTRYP PFNGLDELETESHADERPROC) (GLuint shader); +typedef void (APIENTRYP PFNGLDETACHSHADERPROC) (GLuint program, GLuint shader); +typedef void (APIENTRYP PFNGLDISABLEVERTEXATTRIBARRAYPROC) (GLuint index); +typedef void (APIENTRYP PFNGLENABLEVERTEXATTRIBARRAYPROC) (GLuint index); +typedef void (APIENTRYP PFNGLGETACTIVEATTRIBPROC) (GLuint program, GLuint index, GLsizei bufSize, GLsizei *length, GLint *size, GLenum *type, GLchar *name); +typedef void (APIENTRYP PFNGLGETACTIVEUNIFORMPROC) (GLuint program, GLuint index, GLsizei bufSize, GLsizei *length, GLint *size, GLenum *type, GLchar *name); +typedef void (APIENTRYP PFNGLGETATTACHEDSHADERSPROC) (GLuint program, GLsizei maxCount, GLsizei *count, GLuint *shaders); +typedef GLint (APIENTRYP PFNGLGETATTRIBLOCATIONPROC) (GLuint program, const GLchar *name); +typedef void (APIENTRYP PFNGLGETPROGRAMIVPROC) (GLuint program, GLenum pname, GLint *params); +typedef void (APIENTRYP PFNGLGETPROGRAMINFOLOGPROC) (GLuint program, GLsizei bufSize, GLsizei *length, GLchar *infoLog); +typedef void (APIENTRYP PFNGLGETSHADERIVPROC) (GLuint shader, GLenum pname, GLint *params); +typedef void (APIENTRYP PFNGLGETSHADERINFOLOGPROC) (GLuint shader, GLsizei bufSize, GLsizei *length, GLchar *infoLog); +typedef void (APIENTRYP PFNGLGETSHADERSOURCEPROC) (GLuint shader, GLsizei bufSize, GLsizei *length, GLchar *source); +typedef GLint (APIENTRYP PFNGLGETUNIFORMLOCATIONPROC) (GLuint program, const GLchar *name); +typedef void (APIENTRYP PFNGLGETUNIFORMFVPROC) (GLuint program, GLint location, GLfloat *params); +typedef void (APIENTRYP PFNGLGETUNIFORMIVPROC) (GLuint program, GLint location, GLint *params); +typedef void (APIENTRYP PFNGLGETVERTEXATTRIBDVPROC) (GLuint index, GLenum pname, GLdouble *params); +typedef void (APIENTRYP PFNGLGETVERTEXATTRIBFVPROC) (GLuint index, GLenum pname, GLfloat *params); +typedef void (APIENTRYP PFNGLGETVERTEXATTRIBIVPROC) (GLuint index, GLenum pname, GLint *params); +typedef void (APIENTRYP PFNGLGETVERTEXATTRIBPOINTERVPROC) (GLuint index, GLenum pname, void **pointer); +typedef GLboolean (APIENTRYP PFNGLISPROGRAMPROC) (GLuint program); +typedef GLboolean (APIENTRYP PFNGLISSHADERPROC) (GLuint shader); +typedef void (APIENTRYP PFNGLLINKPROGRAMPROC) (GLuint program); +typedef void (APIENTRYP PFNGLSHADERSOURCEPROC) (GLuint shader, GLsizei count, const GLchar *const*string, const GLint *length); +typedef void (APIENTRYP PFNGLUSEPROGRAMPROC) (GLuint program); +typedef void (APIENTRYP PFNGLUNIFORM1FPROC) (GLint location, GLfloat v0); +typedef void (APIENTRYP PFNGLUNIFORM2FPROC) (GLint location, GLfloat v0, GLfloat v1); +typedef void (APIENTRYP PFNGLUNIFORM3FPROC) (GLint location, GLfloat v0, GLfloat v1, GLfloat v2); +typedef void (APIENTRYP PFNGLUNIFORM4FPROC) (GLint location, GLfloat v0, GLfloat v1, GLfloat v2, GLfloat v3); +typedef void (APIENTRYP PFNGLUNIFORM1IPROC) (GLint location, GLint v0); +typedef void (APIENTRYP PFNGLUNIFORM2IPROC) (GLint location, GLint v0, GLint v1); +typedef void (APIENTRYP PFNGLUNIFORM3IPROC) (GLint location, GLint v0, GLint v1, GLint v2); +typedef void (APIENTRYP PFNGLUNIFORM4IPROC) (GLint location, GLint v0, GLint v1, GLint v2, GLint v3); +typedef void (APIENTRYP PFNGLUNIFORM1FVPROC) (GLint location, GLsizei count, const GLfloat *value); +typedef void (APIENTRYP PFNGLUNIFORM2FVPROC) (GLint location, GLsizei count, const GLfloat *value); +typedef void (APIENTRYP PFNGLUNIFORM3FVPROC) (GLint location, GLsizei count, const GLfloat *value); +typedef void (APIENTRYP PFNGLUNIFORM4FVPROC) (GLint location, GLsizei count, const GLfloat *value); +typedef void (APIENTRYP PFNGLUNIFORM1IVPROC) (GLint location, GLsizei count, const GLint *value); +typedef void (APIENTRYP PFNGLUNIFORM2IVPROC) (GLint location, GLsizei count, const GLint *value); +typedef void (APIENTRYP PFNGLUNIFORM3IVPROC) (GLint location, GLsizei count, const GLint *value); +typedef void (APIENTRYP PFNGLUNIFORM4IVPROC) (GLint location, GLsizei count, const GLint *value); +typedef void (APIENTRYP PFNGLUNIFORMMATRIX2FVPROC) (GLint location, GLsizei count, GLboolean transpose, const GLfloat *value); +typedef void (APIENTRYP PFNGLUNIFORMMATRIX3FVPROC) (GLint location, GLsizei count, GLboolean transpose, const GLfloat *value); +typedef void (APIENTRYP PFNGLUNIFORMMATRIX4FVPROC) (GLint location, GLsizei count, GLboolean transpose, const GLfloat *value); +typedef void (APIENTRYP PFNGLVALIDATEPROGRAMPROC) (GLuint program); +typedef void (APIENTRYP PFNGLVERTEXATTRIB1DPROC) (GLuint index, GLdouble x); +typedef void (APIENTRYP PFNGLVERTEXATTRIB1DVPROC) (GLuint index, const GLdouble *v); +typedef void (APIENTRYP PFNGLVERTEXATTRIB1FPROC) (GLuint index, GLfloat x); +typedef void (APIENTRYP PFNGLVERTEXATTRIB1FVPROC) (GLuint index, const GLfloat *v); +typedef void (APIENTRYP PFNGLVERTEXATTRIB1SPROC) (GLuint index, GLshort x); +typedef void (APIENTRYP PFNGLVERTEXATTRIB1SVPROC) (GLuint index, const GLshort *v); +typedef void (APIENTRYP PFNGLVERTEXATTRIB2DPROC) (GLuint index, GLdouble x, GLdouble y); +typedef void (APIENTRYP PFNGLVERTEXATTRIB2DVPROC) (GLuint index, const GLdouble *v); +typedef void (APIENTRYP PFNGLVERTEXATTRIB2FPROC) (GLuint index, GLfloat x, GLfloat y); +typedef void (APIENTRYP PFNGLVERTEXATTRIB2FVPROC) (GLuint index, const GLfloat *v); +typedef void (APIENTRYP PFNGLVERTEXATTRIB2SPROC) (GLuint index, GLshort x, GLshort y); +typedef void (APIENTRYP PFNGLVERTEXATTRIB2SVPROC) (GLuint index, const GLshort *v); +typedef void (APIENTRYP PFNGLVERTEXATTRIB3DPROC) (GLuint index, GLdouble x, GLdouble y, GLdouble z); +typedef void (APIENTRYP PFNGLVERTEXATTRIB3DVPROC) (GLuint index, const GLdouble *v); +typedef void (APIENTRYP PFNGLVERTEXATTRIB3FPROC) (GLuint index, GLfloat x, GLfloat y, GLfloat z); +typedef void (APIENTRYP PFNGLVERTEXATTRIB3FVPROC) (GLuint index, const GLfloat *v); +typedef void (APIENTRYP PFNGLVERTEXATTRIB3SPROC) (GLuint index, GLshort x, GLshort y, GLshort z); +typedef void (APIENTRYP PFNGLVERTEXATTRIB3SVPROC) (GLuint index, const GLshort *v); +typedef void (APIENTRYP PFNGLVERTEXATTRIB4NBVPROC) (GLuint index, const GLbyte *v); +typedef void (APIENTRYP PFNGLVERTEXATTRIB4NIVPROC) (GLuint index, const GLint *v); +typedef void (APIENTRYP PFNGLVERTEXATTRIB4NSVPROC) (GLuint index, const GLshort *v); +typedef void (APIENTRYP PFNGLVERTEXATTRIB4NUBPROC) (GLuint index, GLubyte x, GLubyte y, GLubyte z, GLubyte w); +typedef void (APIENTRYP PFNGLVERTEXATTRIB4NUBVPROC) (GLuint index, const GLubyte *v); +typedef void (APIENTRYP PFNGLVERTEXATTRIB4NUIVPROC) (GLuint index, const GLuint *v); +typedef void (APIENTRYP PFNGLVERTEXATTRIB4NUSVPROC) (GLuint index, const GLushort *v); +typedef void (APIENTRYP PFNGLVERTEXATTRIB4BVPROC) (GLuint index, const GLbyte *v); +typedef void (APIENTRYP PFNGLVERTEXATTRIB4DPROC) (GLuint index, GLdouble x, GLdouble y, GLdouble z, GLdouble w); +typedef void (APIENTRYP PFNGLVERTEXATTRIB4DVPROC) (GLuint index, const GLdouble *v); +typedef void (APIENTRYP PFNGLVERTEXATTRIB4FPROC) (GLuint index, GLfloat x, GLfloat y, GLfloat z, GLfloat w); +typedef void (APIENTRYP PFNGLVERTEXATTRIB4FVPROC) (GLuint index, const GLfloat *v); +typedef void (APIENTRYP PFNGLVERTEXATTRIB4IVPROC) (GLuint index, const GLint *v); +typedef void (APIENTRYP PFNGLVERTEXATTRIB4SPROC) (GLuint index, GLshort x, GLshort y, GLshort z, GLshort w); +typedef void (APIENTRYP PFNGLVERTEXATTRIB4SVPROC) (GLuint index, const GLshort *v); +typedef void (APIENTRYP PFNGLVERTEXATTRIB4UBVPROC) (GLuint index, const GLubyte *v); +typedef void (APIENTRYP PFNGLVERTEXATTRIB4UIVPROC) (GLuint index, const GLuint *v); +typedef void (APIENTRYP PFNGLVERTEXATTRIB4USVPROC) (GLuint index, const GLushort *v); +typedef void (APIENTRYP PFNGLVERTEXATTRIBPOINTERPROC) (GLuint index, GLint size, GLenum type, GLboolean normalized, GLsizei stride, const void *pointer); +#ifdef GL_GLEXT_PROTOTYPES +GLAPI void APIENTRY glBlendEquationSeparate (GLenum modeRGB, GLenum modeAlpha); +GLAPI void APIENTRY glDrawBuffers (GLsizei n, const GLenum *bufs); +GLAPI void APIENTRY glStencilOpSeparate (GLenum face, GLenum sfail, GLenum dpfail, GLenum dppass); +GLAPI void APIENTRY glStencilFuncSeparate (GLenum face, GLenum func, GLint ref, GLuint mask); +GLAPI void APIENTRY glStencilMaskSeparate (GLenum face, GLuint mask); +GLAPI void APIENTRY glAttachShader (GLuint program, GLuint shader); +GLAPI void APIENTRY glBindAttribLocation (GLuint program, GLuint index, const GLchar *name); +GLAPI void APIENTRY glCompileShader (GLuint shader); +GLAPI GLuint APIENTRY glCreateProgram (void); +GLAPI GLuint APIENTRY glCreateShader (GLenum type); +GLAPI void APIENTRY glDeleteProgram (GLuint program); +GLAPI void APIENTRY glDeleteShader (GLuint shader); +GLAPI void APIENTRY glDetachShader (GLuint program, GLuint shader); +GLAPI void APIENTRY glDisableVertexAttribArray (GLuint index); +GLAPI void APIENTRY glEnableVertexAttribArray (GLuint index); +GLAPI void APIENTRY glGetActiveAttrib (GLuint program, GLuint index, GLsizei bufSize, GLsizei *length, GLint *size, GLenum *type, GLchar *name); +GLAPI void APIENTRY glGetActiveUniform (GLuint program, GLuint index, GLsizei bufSize, GLsizei *length, GLint *size, GLenum *type, GLchar *name); +GLAPI void APIENTRY glGetAttachedShaders (GLuint program, GLsizei maxCount, GLsizei *count, GLuint *shaders); +GLAPI GLint APIENTRY glGetAttribLocation (GLuint program, const GLchar *name); +GLAPI void APIENTRY glGetProgramiv (GLuint program, GLenum pname, GLint *params); +GLAPI void APIENTRY glGetProgramInfoLog (GLuint program, GLsizei bufSize, GLsizei *length, GLchar *infoLog); +GLAPI void APIENTRY glGetShaderiv (GLuint shader, GLenum pname, GLint *params); +GLAPI void APIENTRY glGetShaderInfoLog (GLuint shader, GLsizei bufSize, GLsizei *length, GLchar *infoLog); +GLAPI void APIENTRY glGetShaderSource (GLuint shader, GLsizei bufSize, GLsizei *length, GLchar *source); +GLAPI GLint APIENTRY glGetUniformLocation (GLuint program, const GLchar *name); +GLAPI void APIENTRY glGetUniformfv (GLuint program, GLint location, GLfloat *params); +GLAPI void APIENTRY glGetUniformiv (GLuint program, GLint location, GLint *params); +GLAPI void APIENTRY glGetVertexAttribdv (GLuint index, GLenum pname, GLdouble *params); +GLAPI void APIENTRY glGetVertexAttribfv (GLuint index, GLenum pname, GLfloat *params); +GLAPI void APIENTRY glGetVertexAttribiv (GLuint index, GLenum pname, GLint *params); +GLAPI void APIENTRY glGetVertexAttribPointerv (GLuint index, GLenum pname, void **pointer); +GLAPI GLboolean APIENTRY glIsProgram (GLuint program); +GLAPI GLboolean APIENTRY glIsShader (GLuint shader); +GLAPI void APIENTRY glLinkProgram (GLuint program); +GLAPI void APIENTRY glShaderSource (GLuint shader, GLsizei count, const GLchar *const*string, const GLint *length); +GLAPI void APIENTRY glUseProgram (GLuint program); +GLAPI void APIENTRY glUniform1f (GLint location, GLfloat v0); +GLAPI void APIENTRY glUniform2f (GLint location, GLfloat v0, GLfloat v1); +GLAPI void APIENTRY glUniform3f (GLint location, GLfloat v0, GLfloat v1, GLfloat v2); +GLAPI void APIENTRY glUniform4f (GLint location, GLfloat v0, GLfloat v1, GLfloat v2, GLfloat v3); +GLAPI void APIENTRY glUniform1i (GLint location, GLint v0); +GLAPI void APIENTRY glUniform2i (GLint location, GLint v0, GLint v1); +GLAPI void APIENTRY glUniform3i (GLint location, GLint v0, GLint v1, GLint v2); +GLAPI void APIENTRY glUniform4i (GLint location, GLint v0, GLint v1, GLint v2, GLint v3); +GLAPI void APIENTRY glUniform1fv (GLint location, GLsizei count, const GLfloat *value); +GLAPI void APIENTRY glUniform2fv (GLint location, GLsizei count, const GLfloat *value); +GLAPI void APIENTRY glUniform3fv (GLint location, GLsizei count, const GLfloat *value); +GLAPI void APIENTRY glUniform4fv (GLint location, GLsizei count, const GLfloat *value); +GLAPI void APIENTRY glUniform1iv (GLint location, GLsizei count, const GLint *value); +GLAPI void APIENTRY glUniform2iv (GLint location, GLsizei count, const GLint *value); +GLAPI void APIENTRY glUniform3iv (GLint location, GLsizei count, const GLint *value); +GLAPI void APIENTRY glUniform4iv (GLint location, GLsizei count, const GLint *value); +GLAPI void APIENTRY glUniformMatrix2fv (GLint location, GLsizei count, GLboolean transpose, const GLfloat *value); +GLAPI void APIENTRY glUniformMatrix3fv (GLint location, GLsizei count, GLboolean transpose, const GLfloat *value); +GLAPI void APIENTRY glUniformMatrix4fv (GLint location, GLsizei count, GLboolean transpose, const GLfloat *value); +GLAPI void APIENTRY glValidateProgram (GLuint program); +GLAPI void APIENTRY glVertexAttrib1d (GLuint index, GLdouble x); +GLAPI void APIENTRY glVertexAttrib1dv (GLuint index, const GLdouble *v); +GLAPI void APIENTRY glVertexAttrib1f (GLuint index, GLfloat x); +GLAPI void APIENTRY glVertexAttrib1fv (GLuint index, const GLfloat *v); +GLAPI void APIENTRY glVertexAttrib1s (GLuint index, GLshort x); +GLAPI void APIENTRY glVertexAttrib1sv (GLuint index, const GLshort *v); +GLAPI void APIENTRY glVertexAttrib2d (GLuint index, GLdouble x, GLdouble y); +GLAPI void APIENTRY glVertexAttrib2dv (GLuint index, const GLdouble *v); +GLAPI void APIENTRY glVertexAttrib2f (GLuint index, GLfloat x, GLfloat y); +GLAPI void APIENTRY glVertexAttrib2fv (GLuint index, const GLfloat *v); +GLAPI void APIENTRY glVertexAttrib2s (GLuint index, GLshort x, GLshort y); +GLAPI void APIENTRY glVertexAttrib2sv (GLuint index, const GLshort *v); +GLAPI void APIENTRY glVertexAttrib3d (GLuint index, GLdouble x, GLdouble y, GLdouble z); +GLAPI void APIENTRY glVertexAttrib3dv (GLuint index, const GLdouble *v); +GLAPI void APIENTRY glVertexAttrib3f (GLuint index, GLfloat x, GLfloat y, GLfloat z); +GLAPI void APIENTRY glVertexAttrib3fv (GLuint index, const GLfloat *v); +GLAPI void APIENTRY glVertexAttrib3s (GLuint index, GLshort x, GLshort y, GLshort z); +GLAPI void APIENTRY glVertexAttrib3sv (GLuint index, const GLshort *v); +GLAPI void APIENTRY glVertexAttrib4Nbv (GLuint index, const GLbyte *v); +GLAPI void APIENTRY glVertexAttrib4Niv (GLuint index, const GLint *v); +GLAPI void APIENTRY glVertexAttrib4Nsv (GLuint index, const GLshort *v); +GLAPI void APIENTRY glVertexAttrib4Nub (GLuint index, GLubyte x, GLubyte y, GLubyte z, GLubyte w); +GLAPI void APIENTRY glVertexAttrib4Nubv (GLuint index, const GLubyte *v); +GLAPI void APIENTRY glVertexAttrib4Nuiv (GLuint index, const GLuint *v); +GLAPI void APIENTRY glVertexAttrib4Nusv (GLuint index, const GLushort *v); +GLAPI void APIENTRY glVertexAttrib4bv (GLuint index, const GLbyte *v); +GLAPI void APIENTRY glVertexAttrib4d (GLuint index, GLdouble x, GLdouble y, GLdouble z, GLdouble w); +GLAPI void APIENTRY glVertexAttrib4dv (GLuint index, const GLdouble *v); +GLAPI void APIENTRY glVertexAttrib4f (GLuint index, GLfloat x, GLfloat y, GLfloat z, GLfloat w); +GLAPI void APIENTRY glVertexAttrib4fv (GLuint index, const GLfloat *v); +GLAPI void APIENTRY glVertexAttrib4iv (GLuint index, const GLint *v); +GLAPI void APIENTRY glVertexAttrib4s (GLuint index, GLshort x, GLshort y, GLshort z, GLshort w); +GLAPI void APIENTRY glVertexAttrib4sv (GLuint index, const GLshort *v); +GLAPI void APIENTRY glVertexAttrib4ubv (GLuint index, const GLubyte *v); +GLAPI void APIENTRY glVertexAttrib4uiv (GLuint index, const GLuint *v); +GLAPI void APIENTRY glVertexAttrib4usv (GLuint index, const GLushort *v); +GLAPI void APIENTRY glVertexAttribPointer (GLuint index, GLint size, GLenum type, GLboolean normalized, GLsizei stride, const void *pointer); +#endif +#endif /* GL_VERSION_2_0 */ + +#ifndef GL_VERSION_2_1 +#define GL_VERSION_2_1 1 +#define GL_PIXEL_PACK_BUFFER 0x88EB +#define GL_PIXEL_UNPACK_BUFFER 0x88EC +#define GL_PIXEL_PACK_BUFFER_BINDING 0x88ED +#define GL_PIXEL_UNPACK_BUFFER_BINDING 0x88EF +#define GL_FLOAT_MAT2x3 0x8B65 +#define GL_FLOAT_MAT2x4 0x8B66 +#define GL_FLOAT_MAT3x2 0x8B67 +#define GL_FLOAT_MAT3x4 0x8B68 +#define GL_FLOAT_MAT4x2 0x8B69 +#define GL_FLOAT_MAT4x3 0x8B6A +#define GL_SRGB 0x8C40 +#define GL_SRGB8 0x8C41 +#define GL_SRGB_ALPHA 0x8C42 +#define GL_SRGB8_ALPHA8 0x8C43 +#define GL_COMPRESSED_SRGB 0x8C48 +#define GL_COMPRESSED_SRGB_ALPHA 0x8C49 +#define GL_CURRENT_RASTER_SECONDARY_COLOR 0x845F +#define GL_SLUMINANCE_ALPHA 0x8C44 +#define GL_SLUMINANCE8_ALPHA8 0x8C45 +#define GL_SLUMINANCE 0x8C46 +#define GL_SLUMINANCE8 0x8C47 +#define GL_COMPRESSED_SLUMINANCE 0x8C4A +#define GL_COMPRESSED_SLUMINANCE_ALPHA 0x8C4B +typedef void (APIENTRYP PFNGLUNIFORMMATRIX2X3FVPROC) (GLint location, GLsizei count, GLboolean transpose, const GLfloat *value); +typedef void (APIENTRYP PFNGLUNIFORMMATRIX3X2FVPROC) (GLint location, GLsizei count, GLboolean transpose, const GLfloat *value); +typedef void (APIENTRYP PFNGLUNIFORMMATRIX2X4FVPROC) (GLint location, GLsizei count, GLboolean transpose, const GLfloat *value); +typedef void (APIENTRYP PFNGLUNIFORMMATRIX4X2FVPROC) (GLint location, GLsizei count, GLboolean transpose, const GLfloat *value); +typedef void (APIENTRYP PFNGLUNIFORMMATRIX3X4FVPROC) (GLint location, GLsizei count, GLboolean transpose, const GLfloat *value); +typedef void (APIENTRYP PFNGLUNIFORMMATRIX4X3FVPROC) (GLint location, GLsizei count, GLboolean transpose, const GLfloat *value); +#ifdef GL_GLEXT_PROTOTYPES +GLAPI void APIENTRY glUniformMatrix2x3fv (GLint location, GLsizei count, GLboolean transpose, const GLfloat *value); +GLAPI void APIENTRY glUniformMatrix3x2fv (GLint location, GLsizei count, GLboolean transpose, const GLfloat *value); +GLAPI void APIENTRY glUniformMatrix2x4fv (GLint location, GLsizei count, GLboolean transpose, const GLfloat *value); +GLAPI void APIENTRY glUniformMatrix4x2fv (GLint location, GLsizei count, GLboolean transpose, const GLfloat *value); +GLAPI void APIENTRY glUniformMatrix3x4fv (GLint location, GLsizei count, GLboolean transpose, const GLfloat *value); +GLAPI void APIENTRY glUniformMatrix4x3fv (GLint location, GLsizei count, GLboolean transpose, const GLfloat *value); +#endif +#endif /* GL_VERSION_2_1 */ + +#ifndef GL_VERSION_3_0 +#define GL_VERSION_3_0 1 +typedef unsigned short GLhalf; +#define GL_COMPARE_REF_TO_TEXTURE 0x884E +#define GL_CLIP_DISTANCE0 0x3000 +#define GL_CLIP_DISTANCE1 0x3001 +#define GL_CLIP_DISTANCE2 0x3002 +#define GL_CLIP_DISTANCE3 0x3003 +#define GL_CLIP_DISTANCE4 0x3004 +#define GL_CLIP_DISTANCE5 0x3005 +#define GL_CLIP_DISTANCE6 0x3006 +#define GL_CLIP_DISTANCE7 0x3007 +#define GL_MAX_CLIP_DISTANCES 0x0D32 +#define GL_MAJOR_VERSION 0x821B +#define GL_MINOR_VERSION 0x821C +#define GL_NUM_EXTENSIONS 0x821D +#define GL_CONTEXT_FLAGS 0x821E +#define GL_COMPRESSED_RED 0x8225 +#define GL_COMPRESSED_RG 0x8226 +#define GL_CONTEXT_FLAG_FORWARD_COMPATIBLE_BIT 0x00000001 +#define GL_RGBA32F 0x8814 +#define GL_RGB32F 0x8815 +#define GL_RGBA16F 0x881A +#define GL_RGB16F 0x881B +#define GL_VERTEX_ATTRIB_ARRAY_INTEGER 0x88FD +#define GL_MAX_ARRAY_TEXTURE_LAYERS 0x88FF +#define GL_MIN_PROGRAM_TEXEL_OFFSET 0x8904 +#define GL_MAX_PROGRAM_TEXEL_OFFSET 0x8905 +#define GL_CLAMP_READ_COLOR 0x891C +#define GL_FIXED_ONLY 0x891D +#define GL_MAX_VARYING_COMPONENTS 0x8B4B +#define GL_TEXTURE_1D_ARRAY 0x8C18 +#define GL_PROXY_TEXTURE_1D_ARRAY 0x8C19 +#define GL_TEXTURE_2D_ARRAY 0x8C1A +#define GL_PROXY_TEXTURE_2D_ARRAY 0x8C1B +#define GL_TEXTURE_BINDING_1D_ARRAY 0x8C1C +#define GL_TEXTURE_BINDING_2D_ARRAY 0x8C1D +#define GL_R11F_G11F_B10F 0x8C3A +#define GL_UNSIGNED_INT_10F_11F_11F_REV 0x8C3B +#define GL_RGB9_E5 0x8C3D +#define GL_UNSIGNED_INT_5_9_9_9_REV 0x8C3E +#define GL_TEXTURE_SHARED_SIZE 0x8C3F +#define GL_TRANSFORM_FEEDBACK_VARYING_MAX_LENGTH 0x8C76 +#define GL_TRANSFORM_FEEDBACK_BUFFER_MODE 0x8C7F +#define GL_MAX_TRANSFORM_FEEDBACK_SEPARATE_COMPONENTS 0x8C80 +#define GL_TRANSFORM_FEEDBACK_VARYINGS 0x8C83 +#define GL_TRANSFORM_FEEDBACK_BUFFER_START 0x8C84 +#define GL_TRANSFORM_FEEDBACK_BUFFER_SIZE 0x8C85 +#define GL_PRIMITIVES_GENERATED 0x8C87 +#define GL_TRANSFORM_FEEDBACK_PRIMITIVES_WRITTEN 0x8C88 +#define GL_RASTERIZER_DISCARD 0x8C89 +#define GL_MAX_TRANSFORM_FEEDBACK_INTERLEAVED_COMPONENTS 0x8C8A +#define GL_MAX_TRANSFORM_FEEDBACK_SEPARATE_ATTRIBS 0x8C8B +#define GL_INTERLEAVED_ATTRIBS 0x8C8C +#define GL_SEPARATE_ATTRIBS 0x8C8D +#define GL_TRANSFORM_FEEDBACK_BUFFER 0x8C8E +#define GL_TRANSFORM_FEEDBACK_BUFFER_BINDING 0x8C8F +#define GL_RGBA32UI 0x8D70 +#define GL_RGB32UI 0x8D71 +#define GL_RGBA16UI 0x8D76 +#define GL_RGB16UI 0x8D77 +#define GL_RGBA8UI 0x8D7C +#define GL_RGB8UI 0x8D7D +#define GL_RGBA32I 0x8D82 +#define GL_RGB32I 0x8D83 +#define GL_RGBA16I 0x8D88 +#define GL_RGB16I 0x8D89 +#define GL_RGBA8I 0x8D8E +#define GL_RGB8I 0x8D8F +#define GL_RED_INTEGER 0x8D94 +#define GL_GREEN_INTEGER 0x8D95 +#define GL_BLUE_INTEGER 0x8D96 +#define GL_RGB_INTEGER 0x8D98 +#define GL_RGBA_INTEGER 0x8D99 +#define GL_BGR_INTEGER 0x8D9A +#define GL_BGRA_INTEGER 0x8D9B +#define GL_SAMPLER_1D_ARRAY 0x8DC0 +#define GL_SAMPLER_2D_ARRAY 0x8DC1 +#define GL_SAMPLER_1D_ARRAY_SHADOW 0x8DC3 +#define GL_SAMPLER_2D_ARRAY_SHADOW 0x8DC4 +#define GL_SAMPLER_CUBE_SHADOW 0x8DC5 +#define GL_UNSIGNED_INT_VEC2 0x8DC6 +#define GL_UNSIGNED_INT_VEC3 0x8DC7 +#define GL_UNSIGNED_INT_VEC4 0x8DC8 +#define GL_INT_SAMPLER_1D 0x8DC9 +#define GL_INT_SAMPLER_2D 0x8DCA +#define GL_INT_SAMPLER_3D 0x8DCB +#define GL_INT_SAMPLER_CUBE 0x8DCC +#define GL_INT_SAMPLER_1D_ARRAY 0x8DCE +#define GL_INT_SAMPLER_2D_ARRAY 0x8DCF +#define GL_UNSIGNED_INT_SAMPLER_1D 0x8DD1 +#define GL_UNSIGNED_INT_SAMPLER_2D 0x8DD2 +#define GL_UNSIGNED_INT_SAMPLER_3D 0x8DD3 +#define GL_UNSIGNED_INT_SAMPLER_CUBE 0x8DD4 +#define GL_UNSIGNED_INT_SAMPLER_1D_ARRAY 0x8DD6 +#define GL_UNSIGNED_INT_SAMPLER_2D_ARRAY 0x8DD7 +#define GL_QUERY_WAIT 0x8E13 +#define GL_QUERY_NO_WAIT 0x8E14 +#define GL_QUERY_BY_REGION_WAIT 0x8E15 +#define GL_QUERY_BY_REGION_NO_WAIT 0x8E16 +#define GL_BUFFER_ACCESS_FLAGS 0x911F +#define GL_BUFFER_MAP_LENGTH 0x9120 +#define GL_BUFFER_MAP_OFFSET 0x9121 +#define GL_DEPTH_COMPONENT32F 0x8CAC +#define GL_DEPTH32F_STENCIL8 0x8CAD +#define GL_FLOAT_32_UNSIGNED_INT_24_8_REV 0x8DAD +#define GL_INVALID_FRAMEBUFFER_OPERATION 0x0506 +#define GL_FRAMEBUFFER_ATTACHMENT_COLOR_ENCODING 0x8210 +#define GL_FRAMEBUFFER_ATTACHMENT_COMPONENT_TYPE 0x8211 +#define GL_FRAMEBUFFER_ATTACHMENT_RED_SIZE 0x8212 +#define GL_FRAMEBUFFER_ATTACHMENT_GREEN_SIZE 0x8213 +#define GL_FRAMEBUFFER_ATTACHMENT_BLUE_SIZE 0x8214 +#define GL_FRAMEBUFFER_ATTACHMENT_ALPHA_SIZE 0x8215 +#define GL_FRAMEBUFFER_ATTACHMENT_DEPTH_SIZE 0x8216 +#define GL_FRAMEBUFFER_ATTACHMENT_STENCIL_SIZE 0x8217 +#define GL_FRAMEBUFFER_DEFAULT 0x8218 +#define GL_FRAMEBUFFER_UNDEFINED 0x8219 +#define GL_DEPTH_STENCIL_ATTACHMENT 0x821A +#define GL_MAX_RENDERBUFFER_SIZE 0x84E8 +#define GL_DEPTH_STENCIL 0x84F9 +#define GL_UNSIGNED_INT_24_8 0x84FA +#define GL_DEPTH24_STENCIL8 0x88F0 +#define GL_TEXTURE_STENCIL_SIZE 0x88F1 +#define GL_TEXTURE_RED_TYPE 0x8C10 +#define GL_TEXTURE_GREEN_TYPE 0x8C11 +#define GL_TEXTURE_BLUE_TYPE 0x8C12 +#define GL_TEXTURE_ALPHA_TYPE 0x8C13 +#define GL_TEXTURE_DEPTH_TYPE 0x8C16 +#define GL_UNSIGNED_NORMALIZED 0x8C17 +#define GL_FRAMEBUFFER_BINDING 0x8CA6 +#define GL_DRAW_FRAMEBUFFER_BINDING 0x8CA6 +#define GL_RENDERBUFFER_BINDING 0x8CA7 +#define GL_READ_FRAMEBUFFER 0x8CA8 +#define GL_DRAW_FRAMEBUFFER 0x8CA9 +#define GL_READ_FRAMEBUFFER_BINDING 0x8CAA +#define GL_RENDERBUFFER_SAMPLES 0x8CAB +#define GL_FRAMEBUFFER_ATTACHMENT_OBJECT_TYPE 0x8CD0 +#define GL_FRAMEBUFFER_ATTACHMENT_OBJECT_NAME 0x8CD1 +#define GL_FRAMEBUFFER_ATTACHMENT_TEXTURE_LEVEL 0x8CD2 +#define GL_FRAMEBUFFER_ATTACHMENT_TEXTURE_CUBE_MAP_FACE 0x8CD3 +#define GL_FRAMEBUFFER_ATTACHMENT_TEXTURE_LAYER 0x8CD4 +#define GL_FRAMEBUFFER_COMPLETE 0x8CD5 +#define GL_FRAMEBUFFER_INCOMPLETE_ATTACHMENT 0x8CD6 +#define GL_FRAMEBUFFER_INCOMPLETE_MISSING_ATTACHMENT 0x8CD7 +#define GL_FRAMEBUFFER_INCOMPLETE_DRAW_BUFFER 0x8CDB +#define GL_FRAMEBUFFER_INCOMPLETE_READ_BUFFER 0x8CDC +#define GL_FRAMEBUFFER_UNSUPPORTED 0x8CDD +#define GL_MAX_COLOR_ATTACHMENTS 0x8CDF +#define GL_COLOR_ATTACHMENT0 0x8CE0 +#define GL_COLOR_ATTACHMENT1 0x8CE1 +#define GL_COLOR_ATTACHMENT2 0x8CE2 +#define GL_COLOR_ATTACHMENT3 0x8CE3 +#define GL_COLOR_ATTACHMENT4 0x8CE4 +#define GL_COLOR_ATTACHMENT5 0x8CE5 +#define GL_COLOR_ATTACHMENT6 0x8CE6 +#define GL_COLOR_ATTACHMENT7 0x8CE7 +#define GL_COLOR_ATTACHMENT8 0x8CE8 +#define GL_COLOR_ATTACHMENT9 0x8CE9 +#define GL_COLOR_ATTACHMENT10 0x8CEA +#define GL_COLOR_ATTACHMENT11 0x8CEB +#define GL_COLOR_ATTACHMENT12 0x8CEC +#define GL_COLOR_ATTACHMENT13 0x8CED +#define GL_COLOR_ATTACHMENT14 0x8CEE +#define GL_COLOR_ATTACHMENT15 0x8CEF +#define GL_DEPTH_ATTACHMENT 0x8D00 +#define GL_STENCIL_ATTACHMENT 0x8D20 +#define GL_FRAMEBUFFER 0x8D40 +#define GL_RENDERBUFFER 0x8D41 +#define GL_RENDERBUFFER_WIDTH 0x8D42 +#define GL_RENDERBUFFER_HEIGHT 0x8D43 +#define GL_RENDERBUFFER_INTERNAL_FORMAT 0x8D44 +#define GL_STENCIL_INDEX1 0x8D46 +#define GL_STENCIL_INDEX4 0x8D47 +#define GL_STENCIL_INDEX8 0x8D48 +#define GL_STENCIL_INDEX16 0x8D49 +#define GL_RENDERBUFFER_RED_SIZE 0x8D50 +#define GL_RENDERBUFFER_GREEN_SIZE 0x8D51 +#define GL_RENDERBUFFER_BLUE_SIZE 0x8D52 +#define GL_RENDERBUFFER_ALPHA_SIZE 0x8D53 +#define GL_RENDERBUFFER_DEPTH_SIZE 0x8D54 +#define GL_RENDERBUFFER_STENCIL_SIZE 0x8D55 +#define GL_FRAMEBUFFER_INCOMPLETE_MULTISAMPLE 0x8D56 +#define GL_MAX_SAMPLES 0x8D57 +#define GL_INDEX 0x8222 +#define GL_TEXTURE_LUMINANCE_TYPE 0x8C14 +#define GL_TEXTURE_INTENSITY_TYPE 0x8C15 +#define GL_FRAMEBUFFER_SRGB 0x8DB9 +#define GL_HALF_FLOAT 0x140B +#define GL_MAP_READ_BIT 0x0001 +#define GL_MAP_WRITE_BIT 0x0002 +#define GL_MAP_INVALIDATE_RANGE_BIT 0x0004 +#define GL_MAP_INVALIDATE_BUFFER_BIT 0x0008 +#define GL_MAP_FLUSH_EXPLICIT_BIT 0x0010 +#define GL_MAP_UNSYNCHRONIZED_BIT 0x0020 +#define GL_COMPRESSED_RED_RGTC1 0x8DBB +#define GL_COMPRESSED_SIGNED_RED_RGTC1 0x8DBC +#define GL_COMPRESSED_RG_RGTC2 0x8DBD +#define GL_COMPRESSED_SIGNED_RG_RGTC2 0x8DBE +#define GL_RG 0x8227 +#define GL_RG_INTEGER 0x8228 +#define GL_R8 0x8229 +#define GL_R16 0x822A +#define GL_RG8 0x822B +#define GL_RG16 0x822C +#define GL_R16F 0x822D +#define GL_R32F 0x822E +#define GL_RG16F 0x822F +#define GL_RG32F 0x8230 +#define GL_R8I 0x8231 +#define GL_R8UI 0x8232 +#define GL_R16I 0x8233 +#define GL_R16UI 0x8234 +#define GL_R32I 0x8235 +#define GL_R32UI 0x8236 +#define GL_RG8I 0x8237 +#define GL_RG8UI 0x8238 +#define GL_RG16I 0x8239 +#define GL_RG16UI 0x823A +#define GL_RG32I 0x823B +#define GL_RG32UI 0x823C +#define GL_VERTEX_ARRAY_BINDING 0x85B5 +#define GL_CLAMP_VERTEX_COLOR 0x891A +#define GL_CLAMP_FRAGMENT_COLOR 0x891B +#define GL_ALPHA_INTEGER 0x8D97 +typedef void (APIENTRYP PFNGLCOLORMASKIPROC) (GLuint index, GLboolean r, GLboolean g, GLboolean b, GLboolean a); +typedef void (APIENTRYP PFNGLGETBOOLEANI_VPROC) (GLenum target, GLuint index, GLboolean *data); +typedef void (APIENTRYP PFNGLGETINTEGERI_VPROC) (GLenum target, GLuint index, GLint *data); +typedef void (APIENTRYP PFNGLENABLEIPROC) (GLenum target, GLuint index); +typedef void (APIENTRYP PFNGLDISABLEIPROC) (GLenum target, GLuint index); +typedef GLboolean (APIENTRYP PFNGLISENABLEDIPROC) (GLenum target, GLuint index); +typedef void (APIENTRYP PFNGLBEGINTRANSFORMFEEDBACKPROC) (GLenum primitiveMode); +typedef void (APIENTRYP PFNGLENDTRANSFORMFEEDBACKPROC) (void); +typedef void (APIENTRYP PFNGLBINDBUFFERRANGEPROC) (GLenum target, GLuint index, GLuint buffer, GLintptr offset, GLsizeiptr size); +typedef void (APIENTRYP PFNGLBINDBUFFERBASEPROC) (GLenum target, GLuint index, GLuint buffer); +typedef void (APIENTRYP PFNGLTRANSFORMFEEDBACKVARYINGSPROC) (GLuint program, GLsizei count, const GLchar *const*varyings, GLenum bufferMode); +typedef void (APIENTRYP PFNGLGETTRANSFORMFEEDBACKVARYINGPROC) (GLuint program, GLuint index, GLsizei bufSize, GLsizei *length, GLsizei *size, GLenum *type, GLchar *name); +typedef void (APIENTRYP PFNGLCLAMPCOLORPROC) (GLenum target, GLenum clamp); +typedef void (APIENTRYP PFNGLBEGINCONDITIONALRENDERPROC) (GLuint id, GLenum mode); +typedef void (APIENTRYP PFNGLENDCONDITIONALRENDERPROC) (void); +typedef void (APIENTRYP PFNGLVERTEXATTRIBIPOINTERPROC) (GLuint index, GLint size, GLenum type, GLsizei stride, const void *pointer); +typedef void (APIENTRYP PFNGLGETVERTEXATTRIBIIVPROC) (GLuint index, GLenum pname, GLint *params); +typedef void (APIENTRYP PFNGLGETVERTEXATTRIBIUIVPROC) (GLuint index, GLenum pname, GLuint *params); +typedef void (APIENTRYP PFNGLVERTEXATTRIBI1IPROC) (GLuint index, GLint x); +typedef void (APIENTRYP PFNGLVERTEXATTRIBI2IPROC) (GLuint index, GLint x, GLint y); +typedef void (APIENTRYP PFNGLVERTEXATTRIBI3IPROC) (GLuint index, GLint x, GLint y, GLint z); +typedef void (APIENTRYP PFNGLVERTEXATTRIBI4IPROC) (GLuint index, GLint x, GLint y, GLint z, GLint w); +typedef void (APIENTRYP PFNGLVERTEXATTRIBI1UIPROC) (GLuint index, GLuint x); +typedef void (APIENTRYP PFNGLVERTEXATTRIBI2UIPROC) (GLuint index, GLuint x, GLuint y); +typedef void (APIENTRYP PFNGLVERTEXATTRIBI3UIPROC) (GLuint index, GLuint x, GLuint y, GLuint z); +typedef void (APIENTRYP PFNGLVERTEXATTRIBI4UIPROC) (GLuint index, GLuint x, GLuint y, GLuint z, GLuint w); +typedef void (APIENTRYP PFNGLVERTEXATTRIBI1IVPROC) (GLuint index, const GLint *v); +typedef void (APIENTRYP PFNGLVERTEXATTRIBI2IVPROC) (GLuint index, const GLint *v); +typedef void (APIENTRYP PFNGLVERTEXATTRIBI3IVPROC) (GLuint index, const GLint *v); +typedef void (APIENTRYP PFNGLVERTEXATTRIBI4IVPROC) (GLuint index, const GLint *v); +typedef void (APIENTRYP PFNGLVERTEXATTRIBI1UIVPROC) (GLuint index, const GLuint *v); +typedef void (APIENTRYP PFNGLVERTEXATTRIBI2UIVPROC) (GLuint index, const GLuint *v); +typedef void (APIENTRYP PFNGLVERTEXATTRIBI3UIVPROC) (GLuint index, const GLuint *v); +typedef void (APIENTRYP PFNGLVERTEXATTRIBI4UIVPROC) (GLuint index, const GLuint *v); +typedef void (APIENTRYP PFNGLVERTEXATTRIBI4BVPROC) (GLuint index, const GLbyte *v); +typedef void (APIENTRYP PFNGLVERTEXATTRIBI4SVPROC) (GLuint index, const GLshort *v); +typedef void (APIENTRYP PFNGLVERTEXATTRIBI4UBVPROC) (GLuint index, const GLubyte *v); +typedef void (APIENTRYP PFNGLVERTEXATTRIBI4USVPROC) (GLuint index, const GLushort *v); +typedef void (APIENTRYP PFNGLGETUNIFORMUIVPROC) (GLuint program, GLint location, GLuint *params); +typedef void (APIENTRYP PFNGLBINDFRAGDATALOCATIONPROC) (GLuint program, GLuint color, const GLchar *name); +typedef GLint (APIENTRYP PFNGLGETFRAGDATALOCATIONPROC) (GLuint program, const GLchar *name); +typedef void (APIENTRYP PFNGLUNIFORM1UIPROC) (GLint location, GLuint v0); +typedef void (APIENTRYP PFNGLUNIFORM2UIPROC) (GLint location, GLuint v0, GLuint v1); +typedef void (APIENTRYP PFNGLUNIFORM3UIPROC) (GLint location, GLuint v0, GLuint v1, GLuint v2); +typedef void (APIENTRYP PFNGLUNIFORM4UIPROC) (GLint location, GLuint v0, GLuint v1, GLuint v2, GLuint v3); +typedef void (APIENTRYP PFNGLUNIFORM1UIVPROC) (GLint location, GLsizei count, const GLuint *value); +typedef void (APIENTRYP PFNGLUNIFORM2UIVPROC) (GLint location, GLsizei count, const GLuint *value); +typedef void (APIENTRYP PFNGLUNIFORM3UIVPROC) (GLint location, GLsizei count, const GLuint *value); +typedef void (APIENTRYP PFNGLUNIFORM4UIVPROC) (GLint location, GLsizei count, const GLuint *value); +typedef void (APIENTRYP PFNGLTEXPARAMETERIIVPROC) (GLenum target, GLenum pname, const GLint *params); +typedef void (APIENTRYP PFNGLTEXPARAMETERIUIVPROC) (GLenum target, GLenum pname, const GLuint *params); +typedef void (APIENTRYP PFNGLGETTEXPARAMETERIIVPROC) (GLenum target, GLenum pname, GLint *params); +typedef void (APIENTRYP PFNGLGETTEXPARAMETERIUIVPROC) (GLenum target, GLenum pname, GLuint *params); +typedef void (APIENTRYP PFNGLCLEARBUFFERIVPROC) (GLenum buffer, GLint drawbuffer, const GLint *value); +typedef void (APIENTRYP PFNGLCLEARBUFFERUIVPROC) (GLenum buffer, GLint drawbuffer, const GLuint *value); +typedef void (APIENTRYP PFNGLCLEARBUFFERFVPROC) (GLenum buffer, GLint drawbuffer, const GLfloat *value); +typedef void (APIENTRYP PFNGLCLEARBUFFERFIPROC) (GLenum buffer, GLint drawbuffer, GLfloat depth, GLint stencil); +typedef const GLubyte *(APIENTRYP PFNGLGETSTRINGIPROC) (GLenum name, GLuint index); +typedef GLboolean (APIENTRYP PFNGLISRENDERBUFFERPROC) (GLuint renderbuffer); +typedef void (APIENTRYP PFNGLBINDRENDERBUFFERPROC) (GLenum target, GLuint renderbuffer); +typedef void (APIENTRYP PFNGLDELETERENDERBUFFERSPROC) (GLsizei n, const GLuint *renderbuffers); +typedef void (APIENTRYP PFNGLGENRENDERBUFFERSPROC) (GLsizei n, GLuint *renderbuffers); +typedef void (APIENTRYP PFNGLRENDERBUFFERSTORAGEPROC) (GLenum target, GLenum internalformat, GLsizei width, GLsizei height); +typedef void (APIENTRYP PFNGLGETRENDERBUFFERPARAMETERIVPROC) (GLenum target, GLenum pname, GLint *params); +typedef GLboolean (APIENTRYP PFNGLISFRAMEBUFFERPROC) (GLuint framebuffer); +typedef void (APIENTRYP PFNGLBINDFRAMEBUFFERPROC) (GLenum target, GLuint framebuffer); +typedef void (APIENTRYP PFNGLDELETEFRAMEBUFFERSPROC) (GLsizei n, const GLuint *framebuffers); +typedef void (APIENTRYP PFNGLGENFRAMEBUFFERSPROC) (GLsizei n, GLuint *framebuffers); +typedef GLenum (APIENTRYP PFNGLCHECKFRAMEBUFFERSTATUSPROC) (GLenum target); +typedef void (APIENTRYP PFNGLFRAMEBUFFERTEXTURE1DPROC) (GLenum target, GLenum attachment, GLenum textarget, GLuint texture, GLint level); +typedef void (APIENTRYP PFNGLFRAMEBUFFERTEXTURE2DPROC) (GLenum target, GLenum attachment, GLenum textarget, GLuint texture, GLint level); +typedef void (APIENTRYP PFNGLFRAMEBUFFERTEXTURE3DPROC) (GLenum target, GLenum attachment, GLenum textarget, GLuint texture, GLint level, GLint zoffset); +typedef void (APIENTRYP PFNGLFRAMEBUFFERRENDERBUFFERPROC) (GLenum target, GLenum attachment, GLenum renderbuffertarget, GLuint renderbuffer); +typedef void (APIENTRYP PFNGLGETFRAMEBUFFERATTACHMENTPARAMETERIVPROC) (GLenum target, GLenum attachment, GLenum pname, GLint *params); +typedef void (APIENTRYP PFNGLGENERATEMIPMAPPROC) (GLenum target); +typedef void (APIENTRYP PFNGLBLITFRAMEBUFFERPROC) (GLint srcX0, GLint srcY0, GLint srcX1, GLint srcY1, GLint dstX0, GLint dstY0, GLint dstX1, GLint dstY1, GLbitfield mask, GLenum filter); +typedef void (APIENTRYP PFNGLRENDERBUFFERSTORAGEMULTISAMPLEPROC) (GLenum target, GLsizei samples, GLenum internalformat, GLsizei width, GLsizei height); +typedef void (APIENTRYP PFNGLFRAMEBUFFERTEXTURELAYERPROC) (GLenum target, GLenum attachment, GLuint texture, GLint level, GLint layer); +typedef void *(APIENTRYP PFNGLMAPBUFFERRANGEPROC) (GLenum target, GLintptr offset, GLsizeiptr length, GLbitfield access); +typedef void (APIENTRYP PFNGLFLUSHMAPPEDBUFFERRANGEPROC) (GLenum target, GLintptr offset, GLsizeiptr length); +typedef void (APIENTRYP PFNGLBINDVERTEXARRAYPROC) (GLuint array); +typedef void (APIENTRYP PFNGLDELETEVERTEXARRAYSPROC) (GLsizei n, const GLuint *arrays); +typedef void (APIENTRYP PFNGLGENVERTEXARRAYSPROC) (GLsizei n, GLuint *arrays); +typedef GLboolean (APIENTRYP PFNGLISVERTEXARRAYPROC) (GLuint array); +#ifdef GL_GLEXT_PROTOTYPES +GLAPI void APIENTRY glColorMaski (GLuint index, GLboolean r, GLboolean g, GLboolean b, GLboolean a); +GLAPI void APIENTRY glGetBooleani_v (GLenum target, GLuint index, GLboolean *data); +GLAPI void APIENTRY glGetIntegeri_v (GLenum target, GLuint index, GLint *data); +GLAPI void APIENTRY glEnablei (GLenum target, GLuint index); +GLAPI void APIENTRY glDisablei (GLenum target, GLuint index); +GLAPI GLboolean APIENTRY glIsEnabledi (GLenum target, GLuint index); +GLAPI void APIENTRY glBeginTransformFeedback (GLenum primitiveMode); +GLAPI void APIENTRY glEndTransformFeedback (void); +GLAPI void APIENTRY glBindBufferRange (GLenum target, GLuint index, GLuint buffer, GLintptr offset, GLsizeiptr size); +GLAPI void APIENTRY glBindBufferBase (GLenum target, GLuint index, GLuint buffer); +GLAPI void APIENTRY glTransformFeedbackVaryings (GLuint program, GLsizei count, const GLchar *const*varyings, GLenum bufferMode); +GLAPI void APIENTRY glGetTransformFeedbackVarying (GLuint program, GLuint index, GLsizei bufSize, GLsizei *length, GLsizei *size, GLenum *type, GLchar *name); +GLAPI void APIENTRY glClampColor (GLenum target, GLenum clamp); +GLAPI void APIENTRY glBeginConditionalRender (GLuint id, GLenum mode); +GLAPI void APIENTRY glEndConditionalRender (void); +GLAPI void APIENTRY glVertexAttribIPointer (GLuint index, GLint size, GLenum type, GLsizei stride, const void *pointer); +GLAPI void APIENTRY glGetVertexAttribIiv (GLuint index, GLenum pname, GLint *params); +GLAPI void APIENTRY glGetVertexAttribIuiv (GLuint index, GLenum pname, GLuint *params); +GLAPI void APIENTRY glVertexAttribI1i (GLuint index, GLint x); +GLAPI void APIENTRY glVertexAttribI2i (GLuint index, GLint x, GLint y); +GLAPI void APIENTRY glVertexAttribI3i (GLuint index, GLint x, GLint y, GLint z); +GLAPI void APIENTRY glVertexAttribI4i (GLuint index, GLint x, GLint y, GLint z, GLint w); +GLAPI void APIENTRY glVertexAttribI1ui (GLuint index, GLuint x); +GLAPI void APIENTRY glVertexAttribI2ui (GLuint index, GLuint x, GLuint y); +GLAPI void APIENTRY glVertexAttribI3ui (GLuint index, GLuint x, GLuint y, GLuint z); +GLAPI void APIENTRY glVertexAttribI4ui (GLuint index, GLuint x, GLuint y, GLuint z, GLuint w); +GLAPI void APIENTRY glVertexAttribI1iv (GLuint index, const GLint *v); +GLAPI void APIENTRY glVertexAttribI2iv (GLuint index, const GLint *v); +GLAPI void APIENTRY glVertexAttribI3iv (GLuint index, const GLint *v); +GLAPI void APIENTRY glVertexAttribI4iv (GLuint index, const GLint *v); +GLAPI void APIENTRY glVertexAttribI1uiv (GLuint index, const GLuint *v); +GLAPI void APIENTRY glVertexAttribI2uiv (GLuint index, const GLuint *v); +GLAPI void APIENTRY glVertexAttribI3uiv (GLuint index, const GLuint *v); +GLAPI void APIENTRY glVertexAttribI4uiv (GLuint index, const GLuint *v); +GLAPI void APIENTRY glVertexAttribI4bv (GLuint index, const GLbyte *v); +GLAPI void APIENTRY glVertexAttribI4sv (GLuint index, const GLshort *v); +GLAPI void APIENTRY glVertexAttribI4ubv (GLuint index, const GLubyte *v); +GLAPI void APIENTRY glVertexAttribI4usv (GLuint index, const GLushort *v); +GLAPI void APIENTRY glGetUniformuiv (GLuint program, GLint location, GLuint *params); +GLAPI void APIENTRY glBindFragDataLocation (GLuint program, GLuint color, const GLchar *name); +GLAPI GLint APIENTRY glGetFragDataLocation (GLuint program, const GLchar *name); +GLAPI void APIENTRY glUniform1ui (GLint location, GLuint v0); +GLAPI void APIENTRY glUniform2ui (GLint location, GLuint v0, GLuint v1); +GLAPI void APIENTRY glUniform3ui (GLint location, GLuint v0, GLuint v1, GLuint v2); +GLAPI void APIENTRY glUniform4ui (GLint location, GLuint v0, GLuint v1, GLuint v2, GLuint v3); +GLAPI void APIENTRY glUniform1uiv (GLint location, GLsizei count, const GLuint *value); +GLAPI void APIENTRY glUniform2uiv (GLint location, GLsizei count, const GLuint *value); +GLAPI void APIENTRY glUniform3uiv (GLint location, GLsizei count, const GLuint *value); +GLAPI void APIENTRY glUniform4uiv (GLint location, GLsizei count, const GLuint *value); +GLAPI void APIENTRY glTexParameterIiv (GLenum target, GLenum pname, const GLint *params); +GLAPI void APIENTRY glTexParameterIuiv (GLenum target, GLenum pname, const GLuint *params); +GLAPI void APIENTRY glGetTexParameterIiv (GLenum target, GLenum pname, GLint *params); +GLAPI void APIENTRY glGetTexParameterIuiv (GLenum target, GLenum pname, GLuint *params); +GLAPI void APIENTRY glClearBufferiv (GLenum buffer, GLint drawbuffer, const GLint *value); +GLAPI void APIENTRY glClearBufferuiv (GLenum buffer, GLint drawbuffer, const GLuint *value); +GLAPI void APIENTRY glClearBufferfv (GLenum buffer, GLint drawbuffer, const GLfloat *value); +GLAPI void APIENTRY glClearBufferfi (GLenum buffer, GLint drawbuffer, GLfloat depth, GLint stencil); +GLAPI const GLubyte *APIENTRY glGetStringi (GLenum name, GLuint index); +GLAPI GLboolean APIENTRY glIsRenderbuffer (GLuint renderbuffer); +GLAPI void APIENTRY glBindRenderbuffer (GLenum target, GLuint renderbuffer); +GLAPI void APIENTRY glDeleteRenderbuffers (GLsizei n, const GLuint *renderbuffers); +GLAPI void APIENTRY glGenRenderbuffers (GLsizei n, GLuint *renderbuffers); +GLAPI void APIENTRY glRenderbufferStorage (GLenum target, GLenum internalformat, GLsizei width, GLsizei height); +GLAPI void APIENTRY glGetRenderbufferParameteriv (GLenum target, GLenum pname, GLint *params); +GLAPI GLboolean APIENTRY glIsFramebuffer (GLuint framebuffer); +GLAPI void APIENTRY glBindFramebuffer (GLenum target, GLuint framebuffer); +GLAPI void APIENTRY glDeleteFramebuffers (GLsizei n, const GLuint *framebuffers); +GLAPI void APIENTRY glGenFramebuffers (GLsizei n, GLuint *framebuffers); +GLAPI GLenum APIENTRY glCheckFramebufferStatus (GLenum target); +GLAPI void APIENTRY glFramebufferTexture1D (GLenum target, GLenum attachment, GLenum textarget, GLuint texture, GLint level); +GLAPI void APIENTRY glFramebufferTexture2D (GLenum target, GLenum attachment, GLenum textarget, GLuint texture, GLint level); +GLAPI void APIENTRY glFramebufferTexture3D (GLenum target, GLenum attachment, GLenum textarget, GLuint texture, GLint level, GLint zoffset); +GLAPI void APIENTRY glFramebufferRenderbuffer (GLenum target, GLenum attachment, GLenum renderbuffertarget, GLuint renderbuffer); +GLAPI void APIENTRY glGetFramebufferAttachmentParameteriv (GLenum target, GLenum attachment, GLenum pname, GLint *params); +GLAPI void APIENTRY glGenerateMipmap (GLenum target); +GLAPI void APIENTRY glBlitFramebuffer (GLint srcX0, GLint srcY0, GLint srcX1, GLint srcY1, GLint dstX0, GLint dstY0, GLint dstX1, GLint dstY1, GLbitfield mask, GLenum filter); +GLAPI void APIENTRY glRenderbufferStorageMultisample (GLenum target, GLsizei samples, GLenum internalformat, GLsizei width, GLsizei height); +GLAPI void APIENTRY glFramebufferTextureLayer (GLenum target, GLenum attachment, GLuint texture, GLint level, GLint layer); +GLAPI void *APIENTRY glMapBufferRange (GLenum target, GLintptr offset, GLsizeiptr length, GLbitfield access); +GLAPI void APIENTRY glFlushMappedBufferRange (GLenum target, GLintptr offset, GLsizeiptr length); +GLAPI void APIENTRY glBindVertexArray (GLuint array); +GLAPI void APIENTRY glDeleteVertexArrays (GLsizei n, const GLuint *arrays); +GLAPI void APIENTRY glGenVertexArrays (GLsizei n, GLuint *arrays); +GLAPI GLboolean APIENTRY glIsVertexArray (GLuint array); +#endif +#endif /* GL_VERSION_3_0 */ + +#ifndef GL_VERSION_3_1 +#define GL_VERSION_3_1 1 +#define GL_SAMPLER_2D_RECT 0x8B63 +#define GL_SAMPLER_2D_RECT_SHADOW 0x8B64 +#define GL_SAMPLER_BUFFER 0x8DC2 +#define GL_INT_SAMPLER_2D_RECT 0x8DCD +#define GL_INT_SAMPLER_BUFFER 0x8DD0 +#define GL_UNSIGNED_INT_SAMPLER_2D_RECT 0x8DD5 +#define GL_UNSIGNED_INT_SAMPLER_BUFFER 0x8DD8 +#define GL_TEXTURE_BUFFER 0x8C2A +#define GL_MAX_TEXTURE_BUFFER_SIZE 0x8C2B +#define GL_TEXTURE_BINDING_BUFFER 0x8C2C +#define GL_TEXTURE_BUFFER_DATA_STORE_BINDING 0x8C2D +#define GL_TEXTURE_RECTANGLE 0x84F5 +#define GL_TEXTURE_BINDING_RECTANGLE 0x84F6 +#define GL_PROXY_TEXTURE_RECTANGLE 0x84F7 +#define GL_MAX_RECTANGLE_TEXTURE_SIZE 0x84F8 +#define GL_R8_SNORM 0x8F94 +#define GL_RG8_SNORM 0x8F95 +#define GL_RGB8_SNORM 0x8F96 +#define GL_RGBA8_SNORM 0x8F97 +#define GL_R16_SNORM 0x8F98 +#define GL_RG16_SNORM 0x8F99 +#define GL_RGB16_SNORM 0x8F9A +#define GL_RGBA16_SNORM 0x8F9B +#define GL_SIGNED_NORMALIZED 0x8F9C +#define GL_PRIMITIVE_RESTART 0x8F9D +#define GL_PRIMITIVE_RESTART_INDEX 0x8F9E +#define GL_COPY_READ_BUFFER 0x8F36 +#define GL_COPY_WRITE_BUFFER 0x8F37 +#define GL_UNIFORM_BUFFER 0x8A11 +#define GL_UNIFORM_BUFFER_BINDING 0x8A28 +#define GL_UNIFORM_BUFFER_START 0x8A29 +#define GL_UNIFORM_BUFFER_SIZE 0x8A2A +#define GL_MAX_VERTEX_UNIFORM_BLOCKS 0x8A2B +#define GL_MAX_FRAGMENT_UNIFORM_BLOCKS 0x8A2D +#define GL_MAX_COMBINED_UNIFORM_BLOCKS 0x8A2E +#define GL_MAX_UNIFORM_BUFFER_BINDINGS 0x8A2F +#define GL_MAX_UNIFORM_BLOCK_SIZE 0x8A30 +#define GL_MAX_COMBINED_VERTEX_UNIFORM_COMPONENTS 0x8A31 +#define GL_MAX_COMBINED_FRAGMENT_UNIFORM_COMPONENTS 0x8A33 +#define GL_UNIFORM_BUFFER_OFFSET_ALIGNMENT 0x8A34 +#define GL_ACTIVE_UNIFORM_BLOCK_MAX_NAME_LENGTH 0x8A35 +#define GL_ACTIVE_UNIFORM_BLOCKS 0x8A36 +#define GL_UNIFORM_TYPE 0x8A37 +#define GL_UNIFORM_SIZE 0x8A38 +#define GL_UNIFORM_NAME_LENGTH 0x8A39 +#define GL_UNIFORM_BLOCK_INDEX 0x8A3A +#define GL_UNIFORM_OFFSET 0x8A3B +#define GL_UNIFORM_ARRAY_STRIDE 0x8A3C +#define GL_UNIFORM_MATRIX_STRIDE 0x8A3D +#define GL_UNIFORM_IS_ROW_MAJOR 0x8A3E +#define GL_UNIFORM_BLOCK_BINDING 0x8A3F +#define GL_UNIFORM_BLOCK_DATA_SIZE 0x8A40 +#define GL_UNIFORM_BLOCK_NAME_LENGTH 0x8A41 +#define GL_UNIFORM_BLOCK_ACTIVE_UNIFORMS 0x8A42 +#define GL_UNIFORM_BLOCK_ACTIVE_UNIFORM_INDICES 0x8A43 +#define GL_UNIFORM_BLOCK_REFERENCED_BY_VERTEX_SHADER 0x8A44 +#define GL_UNIFORM_BLOCK_REFERENCED_BY_FRAGMENT_SHADER 0x8A46 +#define GL_INVALID_INDEX 0xFFFFFFFFu +typedef void (APIENTRYP PFNGLDRAWARRAYSINSTANCEDPROC) (GLenum mode, GLint first, GLsizei count, GLsizei instancecount); +typedef void (APIENTRYP PFNGLDRAWELEMENTSINSTANCEDPROC) (GLenum mode, GLsizei count, GLenum type, const void *indices, GLsizei instancecount); +typedef void (APIENTRYP PFNGLTEXBUFFERPROC) (GLenum target, GLenum internalformat, GLuint buffer); +typedef void (APIENTRYP PFNGLPRIMITIVERESTARTINDEXPROC) (GLuint index); +typedef void (APIENTRYP PFNGLCOPYBUFFERSUBDATAPROC) (GLenum readTarget, GLenum writeTarget, GLintptr readOffset, GLintptr writeOffset, GLsizeiptr size); +typedef void (APIENTRYP PFNGLGETUNIFORMINDICESPROC) (GLuint program, GLsizei uniformCount, const GLchar *const*uniformNames, GLuint *uniformIndices); +typedef void (APIENTRYP PFNGLGETACTIVEUNIFORMSIVPROC) (GLuint program, GLsizei uniformCount, const GLuint *uniformIndices, GLenum pname, GLint *params); +typedef void (APIENTRYP PFNGLGETACTIVEUNIFORMNAMEPROC) (GLuint program, GLuint uniformIndex, GLsizei bufSize, GLsizei *length, GLchar *uniformName); +typedef GLuint (APIENTRYP PFNGLGETUNIFORMBLOCKINDEXPROC) (GLuint program, const GLchar *uniformBlockName); +typedef void (APIENTRYP PFNGLGETACTIVEUNIFORMBLOCKIVPROC) (GLuint program, GLuint uniformBlockIndex, GLenum pname, GLint *params); +typedef void (APIENTRYP PFNGLGETACTIVEUNIFORMBLOCKNAMEPROC) (GLuint program, GLuint uniformBlockIndex, GLsizei bufSize, GLsizei *length, GLchar *uniformBlockName); +typedef void (APIENTRYP PFNGLUNIFORMBLOCKBINDINGPROC) (GLuint program, GLuint uniformBlockIndex, GLuint uniformBlockBinding); +#ifdef GL_GLEXT_PROTOTYPES +GLAPI void APIENTRY glDrawArraysInstanced (GLenum mode, GLint first, GLsizei count, GLsizei instancecount); +GLAPI void APIENTRY glDrawElementsInstanced (GLenum mode, GLsizei count, GLenum type, const void *indices, GLsizei instancecount); +GLAPI void APIENTRY glTexBuffer (GLenum target, GLenum internalformat, GLuint buffer); +GLAPI void APIENTRY glPrimitiveRestartIndex (GLuint index); +GLAPI void APIENTRY glCopyBufferSubData (GLenum readTarget, GLenum writeTarget, GLintptr readOffset, GLintptr writeOffset, GLsizeiptr size); +GLAPI void APIENTRY glGetUniformIndices (GLuint program, GLsizei uniformCount, const GLchar *const*uniformNames, GLuint *uniformIndices); +GLAPI void APIENTRY glGetActiveUniformsiv (GLuint program, GLsizei uniformCount, const GLuint *uniformIndices, GLenum pname, GLint *params); +GLAPI void APIENTRY glGetActiveUniformName (GLuint program, GLuint uniformIndex, GLsizei bufSize, GLsizei *length, GLchar *uniformName); +GLAPI GLuint APIENTRY glGetUniformBlockIndex (GLuint program, const GLchar *uniformBlockName); +GLAPI void APIENTRY glGetActiveUniformBlockiv (GLuint program, GLuint uniformBlockIndex, GLenum pname, GLint *params); +GLAPI void APIENTRY glGetActiveUniformBlockName (GLuint program, GLuint uniformBlockIndex, GLsizei bufSize, GLsizei *length, GLchar *uniformBlockName); +GLAPI void APIENTRY glUniformBlockBinding (GLuint program, GLuint uniformBlockIndex, GLuint uniformBlockBinding); +#endif +#endif /* GL_VERSION_3_1 */ + +#ifndef GL_VERSION_3_2 +#define GL_VERSION_3_2 1 +typedef struct __GLsync *GLsync; +#ifndef GLEXT_64_TYPES_DEFINED +/* This code block is duplicated in glxext.h, so must be protected */ +#define GLEXT_64_TYPES_DEFINED +/* Define int32_t, int64_t, and uint64_t types for UST/MSC */ +/* (as used in the GL_EXT_timer_query extension). */ +#if defined(__STDC_VERSION__) && __STDC_VERSION__ >= 199901L +#include +#elif defined(__sun__) || defined(__digital__) +#include +#if defined(__STDC__) +#if defined(__arch64__) || defined(_LP64) +typedef long int int64_t; +typedef unsigned long int uint64_t; +#else +typedef long long int int64_t; +typedef unsigned long long int uint64_t; +#endif /* __arch64__ */ +#endif /* __STDC__ */ +#elif defined( __VMS ) || defined(__sgi) +#include +#elif defined(__SCO__) || defined(__USLC__) +#include +#elif defined(__UNIXOS2__) || defined(__SOL64__) +typedef long int int32_t; +typedef long long int int64_t; +typedef unsigned long long int uint64_t; +#elif defined(_WIN32) && defined(__GNUC__) +#include +#elif defined(_WIN32) +typedef __int32 int32_t; +typedef __int64 int64_t; +typedef unsigned __int64 uint64_t; +#else +/* Fallback if nothing above works */ +#include +#endif +#endif +typedef uint64_t GLuint64; +typedef int64_t GLint64; +#define GL_CONTEXT_CORE_PROFILE_BIT 0x00000001 +#define GL_CONTEXT_COMPATIBILITY_PROFILE_BIT 0x00000002 +#define GL_LINES_ADJACENCY 0x000A +#define GL_LINE_STRIP_ADJACENCY 0x000B +#define GL_TRIANGLES_ADJACENCY 0x000C +#define GL_TRIANGLE_STRIP_ADJACENCY 0x000D +#define GL_PROGRAM_POINT_SIZE 0x8642 +#define GL_MAX_GEOMETRY_TEXTURE_IMAGE_UNITS 0x8C29 +#define GL_FRAMEBUFFER_ATTACHMENT_LAYERED 0x8DA7 +#define GL_FRAMEBUFFER_INCOMPLETE_LAYER_TARGETS 0x8DA8 +#define GL_GEOMETRY_SHADER 0x8DD9 +#define GL_GEOMETRY_VERTICES_OUT 0x8916 +#define GL_GEOMETRY_INPUT_TYPE 0x8917 +#define GL_GEOMETRY_OUTPUT_TYPE 0x8918 +#define GL_MAX_GEOMETRY_UNIFORM_COMPONENTS 0x8DDF +#define GL_MAX_GEOMETRY_OUTPUT_VERTICES 0x8DE0 +#define GL_MAX_GEOMETRY_TOTAL_OUTPUT_COMPONENTS 0x8DE1 +#define GL_MAX_VERTEX_OUTPUT_COMPONENTS 0x9122 +#define GL_MAX_GEOMETRY_INPUT_COMPONENTS 0x9123 +#define GL_MAX_GEOMETRY_OUTPUT_COMPONENTS 0x9124 +#define GL_MAX_FRAGMENT_INPUT_COMPONENTS 0x9125 +#define GL_CONTEXT_PROFILE_MASK 0x9126 +#define GL_DEPTH_CLAMP 0x864F +#define GL_QUADS_FOLLOW_PROVOKING_VERTEX_CONVENTION 0x8E4C +#define GL_FIRST_VERTEX_CONVENTION 0x8E4D +#define GL_LAST_VERTEX_CONVENTION 0x8E4E +#define GL_PROVOKING_VERTEX 0x8E4F +#define GL_TEXTURE_CUBE_MAP_SEAMLESS 0x884F +#define GL_MAX_SERVER_WAIT_TIMEOUT 0x9111 +#define GL_OBJECT_TYPE 0x9112 +#define GL_SYNC_CONDITION 0x9113 +#define GL_SYNC_STATUS 0x9114 +#define GL_SYNC_FLAGS 0x9115 +#define GL_SYNC_FENCE 0x9116 +#define GL_SYNC_GPU_COMMANDS_COMPLETE 0x9117 +#define GL_UNSIGNALED 0x9118 +#define GL_SIGNALED 0x9119 +#define GL_ALREADY_SIGNALED 0x911A +#define GL_TIMEOUT_EXPIRED 0x911B +#define GL_CONDITION_SATISFIED 0x911C +#define GL_WAIT_FAILED 0x911D +#define GL_TIMEOUT_IGNORED 0xFFFFFFFFFFFFFFFFull +#define GL_SYNC_FLUSH_COMMANDS_BIT 0x00000001 +#define GL_SAMPLE_POSITION 0x8E50 +#define GL_SAMPLE_MASK 0x8E51 +#define GL_SAMPLE_MASK_VALUE 0x8E52 +#define GL_MAX_SAMPLE_MASK_WORDS 0x8E59 +#define GL_TEXTURE_2D_MULTISAMPLE 0x9100 +#define GL_PROXY_TEXTURE_2D_MULTISAMPLE 0x9101 +#define GL_TEXTURE_2D_MULTISAMPLE_ARRAY 0x9102 +#define GL_PROXY_TEXTURE_2D_MULTISAMPLE_ARRAY 0x9103 +#define GL_TEXTURE_BINDING_2D_MULTISAMPLE 0x9104 +#define GL_TEXTURE_BINDING_2D_MULTISAMPLE_ARRAY 0x9105 +#define GL_TEXTURE_SAMPLES 0x9106 +#define GL_TEXTURE_FIXED_SAMPLE_LOCATIONS 0x9107 +#define GL_SAMPLER_2D_MULTISAMPLE 0x9108 +#define GL_INT_SAMPLER_2D_MULTISAMPLE 0x9109 +#define GL_UNSIGNED_INT_SAMPLER_2D_MULTISAMPLE 0x910A +#define GL_SAMPLER_2D_MULTISAMPLE_ARRAY 0x910B +#define GL_INT_SAMPLER_2D_MULTISAMPLE_ARRAY 0x910C +#define GL_UNSIGNED_INT_SAMPLER_2D_MULTISAMPLE_ARRAY 0x910D +#define GL_MAX_COLOR_TEXTURE_SAMPLES 0x910E +#define GL_MAX_DEPTH_TEXTURE_SAMPLES 0x910F +#define GL_MAX_INTEGER_SAMPLES 0x9110 +typedef void (APIENTRYP PFNGLDRAWELEMENTSBASEVERTEXPROC) (GLenum mode, GLsizei count, GLenum type, const void *indices, GLint basevertex); +typedef void (APIENTRYP PFNGLDRAWRANGEELEMENTSBASEVERTEXPROC) (GLenum mode, GLuint start, GLuint end, GLsizei count, GLenum type, const void *indices, GLint basevertex); +typedef void (APIENTRYP PFNGLDRAWELEMENTSINSTANCEDBASEVERTEXPROC) (GLenum mode, GLsizei count, GLenum type, const void *indices, GLsizei instancecount, GLint basevertex); +typedef void (APIENTRYP PFNGLMULTIDRAWELEMENTSBASEVERTEXPROC) (GLenum mode, const GLsizei *count, GLenum type, const void *const*indices, GLsizei drawcount, const GLint *basevertex); +typedef void (APIENTRYP PFNGLPROVOKINGVERTEXPROC) (GLenum mode); +typedef GLsync (APIENTRYP PFNGLFENCESYNCPROC) (GLenum condition, GLbitfield flags); +typedef GLboolean (APIENTRYP PFNGLISSYNCPROC) (GLsync sync); +typedef void (APIENTRYP PFNGLDELETESYNCPROC) (GLsync sync); +typedef GLenum (APIENTRYP PFNGLCLIENTWAITSYNCPROC) (GLsync sync, GLbitfield flags, GLuint64 timeout); +typedef void (APIENTRYP PFNGLWAITSYNCPROC) (GLsync sync, GLbitfield flags, GLuint64 timeout); +typedef void (APIENTRYP PFNGLGETINTEGER64VPROC) (GLenum pname, GLint64 *data); +typedef void (APIENTRYP PFNGLGETSYNCIVPROC) (GLsync sync, GLenum pname, GLsizei bufSize, GLsizei *length, GLint *values); +typedef void (APIENTRYP PFNGLGETINTEGER64I_VPROC) (GLenum target, GLuint index, GLint64 *data); +typedef void (APIENTRYP PFNGLGETBUFFERPARAMETERI64VPROC) (GLenum target, GLenum pname, GLint64 *params); +typedef void (APIENTRYP PFNGLFRAMEBUFFERTEXTUREPROC) (GLenum target, GLenum attachment, GLuint texture, GLint level); +typedef void (APIENTRYP PFNGLTEXIMAGE2DMULTISAMPLEPROC) (GLenum target, GLsizei samples, GLenum internalformat, GLsizei width, GLsizei height, GLboolean fixedsamplelocations); +typedef void (APIENTRYP PFNGLTEXIMAGE3DMULTISAMPLEPROC) (GLenum target, GLsizei samples, GLenum internalformat, GLsizei width, GLsizei height, GLsizei depth, GLboolean fixedsamplelocations); +typedef void (APIENTRYP PFNGLGETMULTISAMPLEFVPROC) (GLenum pname, GLuint index, GLfloat *val); +typedef void (APIENTRYP PFNGLSAMPLEMASKIPROC) (GLuint maskNumber, GLbitfield mask); +#ifdef GL_GLEXT_PROTOTYPES +GLAPI void APIENTRY glDrawElementsBaseVertex (GLenum mode, GLsizei count, GLenum type, const void *indices, GLint basevertex); +GLAPI void APIENTRY glDrawRangeElementsBaseVertex (GLenum mode, GLuint start, GLuint end, GLsizei count, GLenum type, const void *indices, GLint basevertex); +GLAPI void APIENTRY glDrawElementsInstancedBaseVertex (GLenum mode, GLsizei count, GLenum type, const void *indices, GLsizei instancecount, GLint basevertex); +GLAPI void APIENTRY glMultiDrawElementsBaseVertex (GLenum mode, const GLsizei *count, GLenum type, const void *const*indices, GLsizei drawcount, const GLint *basevertex); +GLAPI void APIENTRY glProvokingVertex (GLenum mode); +GLAPI GLsync APIENTRY glFenceSync (GLenum condition, GLbitfield flags); +GLAPI GLboolean APIENTRY glIsSync (GLsync sync); +GLAPI void APIENTRY glDeleteSync (GLsync sync); +GLAPI GLenum APIENTRY glClientWaitSync (GLsync sync, GLbitfield flags, GLuint64 timeout); +GLAPI void APIENTRY glWaitSync (GLsync sync, GLbitfield flags, GLuint64 timeout); +GLAPI void APIENTRY glGetInteger64v (GLenum pname, GLint64 *data); +GLAPI void APIENTRY glGetSynciv (GLsync sync, GLenum pname, GLsizei bufSize, GLsizei *length, GLint *values); +GLAPI void APIENTRY glGetInteger64i_v (GLenum target, GLuint index, GLint64 *data); +GLAPI void APIENTRY glGetBufferParameteri64v (GLenum target, GLenum pname, GLint64 *params); +GLAPI void APIENTRY glFramebufferTexture (GLenum target, GLenum attachment, GLuint texture, GLint level); +GLAPI void APIENTRY glTexImage2DMultisample (GLenum target, GLsizei samples, GLenum internalformat, GLsizei width, GLsizei height, GLboolean fixedsamplelocations); +GLAPI void APIENTRY glTexImage3DMultisample (GLenum target, GLsizei samples, GLenum internalformat, GLsizei width, GLsizei height, GLsizei depth, GLboolean fixedsamplelocations); +GLAPI void APIENTRY glGetMultisamplefv (GLenum pname, GLuint index, GLfloat *val); +GLAPI void APIENTRY glSampleMaski (GLuint maskNumber, GLbitfield mask); +#endif +#endif /* GL_VERSION_3_2 */ + +#ifndef GL_VERSION_3_3 +#define GL_VERSION_3_3 1 +#define GL_VERTEX_ATTRIB_ARRAY_DIVISOR 0x88FE +#define GL_SRC1_COLOR 0x88F9 +#define GL_ONE_MINUS_SRC1_COLOR 0x88FA +#define GL_ONE_MINUS_SRC1_ALPHA 0x88FB +#define GL_MAX_DUAL_SOURCE_DRAW_BUFFERS 0x88FC +#define GL_ANY_SAMPLES_PASSED 0x8C2F +#define GL_SAMPLER_BINDING 0x8919 +#define GL_RGB10_A2UI 0x906F +#define GL_TEXTURE_SWIZZLE_R 0x8E42 +#define GL_TEXTURE_SWIZZLE_G 0x8E43 +#define GL_TEXTURE_SWIZZLE_B 0x8E44 +#define GL_TEXTURE_SWIZZLE_A 0x8E45 +#define GL_TEXTURE_SWIZZLE_RGBA 0x8E46 +#define GL_TIME_ELAPSED 0x88BF +#define GL_TIMESTAMP 0x8E28 +#define GL_INT_2_10_10_10_REV 0x8D9F +typedef void (APIENTRYP PFNGLBINDFRAGDATALOCATIONINDEXEDPROC) (GLuint program, GLuint colorNumber, GLuint index, const GLchar *name); +typedef GLint (APIENTRYP PFNGLGETFRAGDATAINDEXPROC) (GLuint program, const GLchar *name); +typedef void (APIENTRYP PFNGLGENSAMPLERSPROC) (GLsizei count, GLuint *samplers); +typedef void (APIENTRYP PFNGLDELETESAMPLERSPROC) (GLsizei count, const GLuint *samplers); +typedef GLboolean (APIENTRYP PFNGLISSAMPLERPROC) (GLuint sampler); +typedef void (APIENTRYP PFNGLBINDSAMPLERPROC) (GLuint unit, GLuint sampler); +typedef void (APIENTRYP PFNGLSAMPLERPARAMETERIPROC) (GLuint sampler, GLenum pname, GLint param); +typedef void (APIENTRYP PFNGLSAMPLERPARAMETERIVPROC) (GLuint sampler, GLenum pname, const GLint *param); +typedef void (APIENTRYP PFNGLSAMPLERPARAMETERFPROC) (GLuint sampler, GLenum pname, GLfloat param); +typedef void (APIENTRYP PFNGLSAMPLERPARAMETERFVPROC) (GLuint sampler, GLenum pname, const GLfloat *param); +typedef void (APIENTRYP PFNGLSAMPLERPARAMETERIIVPROC) (GLuint sampler, GLenum pname, const GLint *param); +typedef void (APIENTRYP PFNGLSAMPLERPARAMETERIUIVPROC) (GLuint sampler, GLenum pname, const GLuint *param); +typedef void (APIENTRYP PFNGLGETSAMPLERPARAMETERIVPROC) (GLuint sampler, GLenum pname, GLint *params); +typedef void (APIENTRYP PFNGLGETSAMPLERPARAMETERIIVPROC) (GLuint sampler, GLenum pname, GLint *params); +typedef void (APIENTRYP PFNGLGETSAMPLERPARAMETERFVPROC) (GLuint sampler, GLenum pname, GLfloat *params); +typedef void (APIENTRYP PFNGLGETSAMPLERPARAMETERIUIVPROC) (GLuint sampler, GLenum pname, GLuint *params); +typedef void (APIENTRYP PFNGLQUERYCOUNTERPROC) (GLuint id, GLenum target); +typedef void (APIENTRYP PFNGLGETQUERYOBJECTI64VPROC) (GLuint id, GLenum pname, GLint64 *params); +typedef void (APIENTRYP PFNGLGETQUERYOBJECTUI64VPROC) (GLuint id, GLenum pname, GLuint64 *params); +typedef void (APIENTRYP PFNGLVERTEXATTRIBDIVISORPROC) (GLuint index, GLuint divisor); +typedef void (APIENTRYP PFNGLVERTEXATTRIBP1UIPROC) (GLuint index, GLenum type, GLboolean normalized, GLuint value); +typedef void (APIENTRYP PFNGLVERTEXATTRIBP1UIVPROC) (GLuint index, GLenum type, GLboolean normalized, const GLuint *value); +typedef void (APIENTRYP PFNGLVERTEXATTRIBP2UIPROC) (GLuint index, GLenum type, GLboolean normalized, GLuint value); +typedef void (APIENTRYP PFNGLVERTEXATTRIBP2UIVPROC) (GLuint index, GLenum type, GLboolean normalized, const GLuint *value); +typedef void (APIENTRYP PFNGLVERTEXATTRIBP3UIPROC) (GLuint index, GLenum type, GLboolean normalized, GLuint value); +typedef void (APIENTRYP PFNGLVERTEXATTRIBP3UIVPROC) (GLuint index, GLenum type, GLboolean normalized, const GLuint *value); +typedef void (APIENTRYP PFNGLVERTEXATTRIBP4UIPROC) (GLuint index, GLenum type, GLboolean normalized, GLuint value); +typedef void (APIENTRYP PFNGLVERTEXATTRIBP4UIVPROC) (GLuint index, GLenum type, GLboolean normalized, const GLuint *value); +typedef void (APIENTRYP PFNGLVERTEXP2UIPROC) (GLenum type, GLuint value); +typedef void (APIENTRYP PFNGLVERTEXP2UIVPROC) (GLenum type, const GLuint *value); +typedef void (APIENTRYP PFNGLVERTEXP3UIPROC) (GLenum type, GLuint value); +typedef void (APIENTRYP PFNGLVERTEXP3UIVPROC) (GLenum type, const GLuint *value); +typedef void (APIENTRYP PFNGLVERTEXP4UIPROC) (GLenum type, GLuint value); +typedef void (APIENTRYP PFNGLVERTEXP4UIVPROC) (GLenum type, const GLuint *value); +typedef void (APIENTRYP PFNGLTEXCOORDP1UIPROC) (GLenum type, GLuint coords); +typedef void (APIENTRYP PFNGLTEXCOORDP1UIVPROC) (GLenum type, const GLuint *coords); +typedef void (APIENTRYP PFNGLTEXCOORDP2UIPROC) (GLenum type, GLuint coords); +typedef void (APIENTRYP PFNGLTEXCOORDP2UIVPROC) (GLenum type, const GLuint *coords); +typedef void (APIENTRYP PFNGLTEXCOORDP3UIPROC) (GLenum type, GLuint coords); +typedef void (APIENTRYP PFNGLTEXCOORDP3UIVPROC) (GLenum type, const GLuint *coords); +typedef void (APIENTRYP PFNGLTEXCOORDP4UIPROC) (GLenum type, GLuint coords); +typedef void (APIENTRYP PFNGLTEXCOORDP4UIVPROC) (GLenum type, const GLuint *coords); +typedef void (APIENTRYP PFNGLMULTITEXCOORDP1UIPROC) (GLenum texture, GLenum type, GLuint coords); +typedef void (APIENTRYP PFNGLMULTITEXCOORDP1UIVPROC) (GLenum texture, GLenum type, const GLuint *coords); +typedef void (APIENTRYP PFNGLMULTITEXCOORDP2UIPROC) (GLenum texture, GLenum type, GLuint coords); +typedef void (APIENTRYP PFNGLMULTITEXCOORDP2UIVPROC) (GLenum texture, GLenum type, const GLuint *coords); +typedef void (APIENTRYP PFNGLMULTITEXCOORDP3UIPROC) (GLenum texture, GLenum type, GLuint coords); +typedef void (APIENTRYP PFNGLMULTITEXCOORDP3UIVPROC) (GLenum texture, GLenum type, const GLuint *coords); +typedef void (APIENTRYP PFNGLMULTITEXCOORDP4UIPROC) (GLenum texture, GLenum type, GLuint coords); +typedef void (APIENTRYP PFNGLMULTITEXCOORDP4UIVPROC) (GLenum texture, GLenum type, const GLuint *coords); +typedef void (APIENTRYP PFNGLNORMALP3UIPROC) (GLenum type, GLuint coords); +typedef void (APIENTRYP PFNGLNORMALP3UIVPROC) (GLenum type, const GLuint *coords); +typedef void (APIENTRYP PFNGLCOLORP3UIPROC) (GLenum type, GLuint color); +typedef void (APIENTRYP PFNGLCOLORP3UIVPROC) (GLenum type, const GLuint *color); +typedef void (APIENTRYP PFNGLCOLORP4UIPROC) (GLenum type, GLuint color); +typedef void (APIENTRYP PFNGLCOLORP4UIVPROC) (GLenum type, const GLuint *color); +typedef void (APIENTRYP PFNGLSECONDARYCOLORP3UIPROC) (GLenum type, GLuint color); +typedef void (APIENTRYP PFNGLSECONDARYCOLORP3UIVPROC) (GLenum type, const GLuint *color); +#ifdef GL_GLEXT_PROTOTYPES +GLAPI void APIENTRY glBindFragDataLocationIndexed (GLuint program, GLuint colorNumber, GLuint index, const GLchar *name); +GLAPI GLint APIENTRY glGetFragDataIndex (GLuint program, const GLchar *name); +GLAPI void APIENTRY glGenSamplers (GLsizei count, GLuint *samplers); +GLAPI void APIENTRY glDeleteSamplers (GLsizei count, const GLuint *samplers); +GLAPI GLboolean APIENTRY glIsSampler (GLuint sampler); +GLAPI void APIENTRY glBindSampler (GLuint unit, GLuint sampler); +GLAPI void APIENTRY glSamplerParameteri (GLuint sampler, GLenum pname, GLint param); +GLAPI void APIENTRY glSamplerParameteriv (GLuint sampler, GLenum pname, const GLint *param); +GLAPI void APIENTRY glSamplerParameterf (GLuint sampler, GLenum pname, GLfloat param); +GLAPI void APIENTRY glSamplerParameterfv (GLuint sampler, GLenum pname, const GLfloat *param); +GLAPI void APIENTRY glSamplerParameterIiv (GLuint sampler, GLenum pname, const GLint *param); +GLAPI void APIENTRY glSamplerParameterIuiv (GLuint sampler, GLenum pname, const GLuint *param); +GLAPI void APIENTRY glGetSamplerParameteriv (GLuint sampler, GLenum pname, GLint *params); +GLAPI void APIENTRY glGetSamplerParameterIiv (GLuint sampler, GLenum pname, GLint *params); +GLAPI void APIENTRY glGetSamplerParameterfv (GLuint sampler, GLenum pname, GLfloat *params); +GLAPI void APIENTRY glGetSamplerParameterIuiv (GLuint sampler, GLenum pname, GLuint *params); +GLAPI void APIENTRY glQueryCounter (GLuint id, GLenum target); +GLAPI void APIENTRY glGetQueryObjecti64v (GLuint id, GLenum pname, GLint64 *params); +GLAPI void APIENTRY glGetQueryObjectui64v (GLuint id, GLenum pname, GLuint64 *params); +GLAPI void APIENTRY glVertexAttribDivisor (GLuint index, GLuint divisor); +GLAPI void APIENTRY glVertexAttribP1ui (GLuint index, GLenum type, GLboolean normalized, GLuint value); +GLAPI void APIENTRY glVertexAttribP1uiv (GLuint index, GLenum type, GLboolean normalized, const GLuint *value); +GLAPI void APIENTRY glVertexAttribP2ui (GLuint index, GLenum type, GLboolean normalized, GLuint value); +GLAPI void APIENTRY glVertexAttribP2uiv (GLuint index, GLenum type, GLboolean normalized, const GLuint *value); +GLAPI void APIENTRY glVertexAttribP3ui (GLuint index, GLenum type, GLboolean normalized, GLuint value); +GLAPI void APIENTRY glVertexAttribP3uiv (GLuint index, GLenum type, GLboolean normalized, const GLuint *value); +GLAPI void APIENTRY glVertexAttribP4ui (GLuint index, GLenum type, GLboolean normalized, GLuint value); +GLAPI void APIENTRY glVertexAttribP4uiv (GLuint index, GLenum type, GLboolean normalized, const GLuint *value); +GLAPI void APIENTRY glVertexP2ui (GLenum type, GLuint value); +GLAPI void APIENTRY glVertexP2uiv (GLenum type, const GLuint *value); +GLAPI void APIENTRY glVertexP3ui (GLenum type, GLuint value); +GLAPI void APIENTRY glVertexP3uiv (GLenum type, const GLuint *value); +GLAPI void APIENTRY glVertexP4ui (GLenum type, GLuint value); +GLAPI void APIENTRY glVertexP4uiv (GLenum type, const GLuint *value); +GLAPI void APIENTRY glTexCoordP1ui (GLenum type, GLuint coords); +GLAPI void APIENTRY glTexCoordP1uiv (GLenum type, const GLuint *coords); +GLAPI void APIENTRY glTexCoordP2ui (GLenum type, GLuint coords); +GLAPI void APIENTRY glTexCoordP2uiv (GLenum type, const GLuint *coords); +GLAPI void APIENTRY glTexCoordP3ui (GLenum type, GLuint coords); +GLAPI void APIENTRY glTexCoordP3uiv (GLenum type, const GLuint *coords); +GLAPI void APIENTRY glTexCoordP4ui (GLenum type, GLuint coords); +GLAPI void APIENTRY glTexCoordP4uiv (GLenum type, const GLuint *coords); +GLAPI void APIENTRY glMultiTexCoordP1ui (GLenum texture, GLenum type, GLuint coords); +GLAPI void APIENTRY glMultiTexCoordP1uiv (GLenum texture, GLenum type, const GLuint *coords); +GLAPI void APIENTRY glMultiTexCoordP2ui (GLenum texture, GLenum type, GLuint coords); +GLAPI void APIENTRY glMultiTexCoordP2uiv (GLenum texture, GLenum type, const GLuint *coords); +GLAPI void APIENTRY glMultiTexCoordP3ui (GLenum texture, GLenum type, GLuint coords); +GLAPI void APIENTRY glMultiTexCoordP3uiv (GLenum texture, GLenum type, const GLuint *coords); +GLAPI void APIENTRY glMultiTexCoordP4ui (GLenum texture, GLenum type, GLuint coords); +GLAPI void APIENTRY glMultiTexCoordP4uiv (GLenum texture, GLenum type, const GLuint *coords); +GLAPI void APIENTRY glNormalP3ui (GLenum type, GLuint coords); +GLAPI void APIENTRY glNormalP3uiv (GLenum type, const GLuint *coords); +GLAPI void APIENTRY glColorP3ui (GLenum type, GLuint color); +GLAPI void APIENTRY glColorP3uiv (GLenum type, const GLuint *color); +GLAPI void APIENTRY glColorP4ui (GLenum type, GLuint color); +GLAPI void APIENTRY glColorP4uiv (GLenum type, const GLuint *color); +GLAPI void APIENTRY glSecondaryColorP3ui (GLenum type, GLuint color); +GLAPI void APIENTRY glSecondaryColorP3uiv (GLenum type, const GLuint *color); +#endif +#endif /* GL_VERSION_3_3 */ + +#ifndef GL_VERSION_4_0 +#define GL_VERSION_4_0 1 +#define GL_SAMPLE_SHADING 0x8C36 +#define GL_MIN_SAMPLE_SHADING_VALUE 0x8C37 +#define GL_MIN_PROGRAM_TEXTURE_GATHER_OFFSET 0x8E5E +#define GL_MAX_PROGRAM_TEXTURE_GATHER_OFFSET 0x8E5F +#define GL_TEXTURE_CUBE_MAP_ARRAY 0x9009 +#define GL_TEXTURE_BINDING_CUBE_MAP_ARRAY 0x900A +#define GL_PROXY_TEXTURE_CUBE_MAP_ARRAY 0x900B +#define GL_SAMPLER_CUBE_MAP_ARRAY 0x900C +#define GL_SAMPLER_CUBE_MAP_ARRAY_SHADOW 0x900D +#define GL_INT_SAMPLER_CUBE_MAP_ARRAY 0x900E +#define GL_UNSIGNED_INT_SAMPLER_CUBE_MAP_ARRAY 0x900F +#define GL_DRAW_INDIRECT_BUFFER 0x8F3F +#define GL_DRAW_INDIRECT_BUFFER_BINDING 0x8F43 +#define GL_GEOMETRY_SHADER_INVOCATIONS 0x887F +#define GL_MAX_GEOMETRY_SHADER_INVOCATIONS 0x8E5A +#define GL_MIN_FRAGMENT_INTERPOLATION_OFFSET 0x8E5B +#define GL_MAX_FRAGMENT_INTERPOLATION_OFFSET 0x8E5C +#define GL_FRAGMENT_INTERPOLATION_OFFSET_BITS 0x8E5D +#define GL_MAX_VERTEX_STREAMS 0x8E71 +#define GL_DOUBLE_VEC2 0x8FFC +#define GL_DOUBLE_VEC3 0x8FFD +#define GL_DOUBLE_VEC4 0x8FFE +#define GL_DOUBLE_MAT2 0x8F46 +#define GL_DOUBLE_MAT3 0x8F47 +#define GL_DOUBLE_MAT4 0x8F48 +#define GL_DOUBLE_MAT2x3 0x8F49 +#define GL_DOUBLE_MAT2x4 0x8F4A +#define GL_DOUBLE_MAT3x2 0x8F4B +#define GL_DOUBLE_MAT3x4 0x8F4C +#define GL_DOUBLE_MAT4x2 0x8F4D +#define GL_DOUBLE_MAT4x3 0x8F4E +#define GL_ACTIVE_SUBROUTINES 0x8DE5 +#define GL_ACTIVE_SUBROUTINE_UNIFORMS 0x8DE6 +#define GL_ACTIVE_SUBROUTINE_UNIFORM_LOCATIONS 0x8E47 +#define GL_ACTIVE_SUBROUTINE_MAX_LENGTH 0x8E48 +#define GL_ACTIVE_SUBROUTINE_UNIFORM_MAX_LENGTH 0x8E49 +#define GL_MAX_SUBROUTINES 0x8DE7 +#define GL_MAX_SUBROUTINE_UNIFORM_LOCATIONS 0x8DE8 +#define GL_NUM_COMPATIBLE_SUBROUTINES 0x8E4A +#define GL_COMPATIBLE_SUBROUTINES 0x8E4B +#define GL_PATCHES 0x000E +#define GL_PATCH_VERTICES 0x8E72 +#define GL_PATCH_DEFAULT_INNER_LEVEL 0x8E73 +#define GL_PATCH_DEFAULT_OUTER_LEVEL 0x8E74 +#define GL_TESS_CONTROL_OUTPUT_VERTICES 0x8E75 +#define GL_TESS_GEN_MODE 0x8E76 +#define GL_TESS_GEN_SPACING 0x8E77 +#define GL_TESS_GEN_VERTEX_ORDER 0x8E78 +#define GL_TESS_GEN_POINT_MODE 0x8E79 +#define GL_ISOLINES 0x8E7A +#define GL_FRACTIONAL_ODD 0x8E7B +#define GL_FRACTIONAL_EVEN 0x8E7C +#define GL_MAX_PATCH_VERTICES 0x8E7D +#define GL_MAX_TESS_GEN_LEVEL 0x8E7E +#define GL_MAX_TESS_CONTROL_UNIFORM_COMPONENTS 0x8E7F +#define GL_MAX_TESS_EVALUATION_UNIFORM_COMPONENTS 0x8E80 +#define GL_MAX_TESS_CONTROL_TEXTURE_IMAGE_UNITS 0x8E81 +#define GL_MAX_TESS_EVALUATION_TEXTURE_IMAGE_UNITS 0x8E82 +#define GL_MAX_TESS_CONTROL_OUTPUT_COMPONENTS 0x8E83 +#define GL_MAX_TESS_PATCH_COMPONENTS 0x8E84 +#define GL_MAX_TESS_CONTROL_TOTAL_OUTPUT_COMPONENTS 0x8E85 +#define GL_MAX_TESS_EVALUATION_OUTPUT_COMPONENTS 0x8E86 +#define GL_MAX_TESS_CONTROL_UNIFORM_BLOCKS 0x8E89 +#define GL_MAX_TESS_EVALUATION_UNIFORM_BLOCKS 0x8E8A +#define GL_MAX_TESS_CONTROL_INPUT_COMPONENTS 0x886C +#define GL_MAX_TESS_EVALUATION_INPUT_COMPONENTS 0x886D +#define GL_MAX_COMBINED_TESS_CONTROL_UNIFORM_COMPONENTS 0x8E1E +#define GL_MAX_COMBINED_TESS_EVALUATION_UNIFORM_COMPONENTS 0x8E1F +#define GL_UNIFORM_BLOCK_REFERENCED_BY_TESS_CONTROL_SHADER 0x84F0 +#define GL_UNIFORM_BLOCK_REFERENCED_BY_TESS_EVALUATION_SHADER 0x84F1 +#define GL_TESS_EVALUATION_SHADER 0x8E87 +#define GL_TESS_CONTROL_SHADER 0x8E88 +#define GL_TRANSFORM_FEEDBACK 0x8E22 +#define GL_TRANSFORM_FEEDBACK_BUFFER_PAUSED 0x8E23 +#define GL_TRANSFORM_FEEDBACK_BUFFER_ACTIVE 0x8E24 +#define GL_TRANSFORM_FEEDBACK_BINDING 0x8E25 +#define GL_MAX_TRANSFORM_FEEDBACK_BUFFERS 0x8E70 +typedef void (APIENTRYP PFNGLMINSAMPLESHADINGPROC) (GLfloat value); +typedef void (APIENTRYP PFNGLBLENDEQUATIONIPROC) (GLuint buf, GLenum mode); +typedef void (APIENTRYP PFNGLBLENDEQUATIONSEPARATEIPROC) (GLuint buf, GLenum modeRGB, GLenum modeAlpha); +typedef void (APIENTRYP PFNGLBLENDFUNCIPROC) (GLuint buf, GLenum src, GLenum dst); +typedef void (APIENTRYP PFNGLBLENDFUNCSEPARATEIPROC) (GLuint buf, GLenum srcRGB, GLenum dstRGB, GLenum srcAlpha, GLenum dstAlpha); +typedef void (APIENTRYP PFNGLDRAWARRAYSINDIRECTPROC) (GLenum mode, const void *indirect); +typedef void (APIENTRYP PFNGLDRAWELEMENTSINDIRECTPROC) (GLenum mode, GLenum type, const void *indirect); +typedef void (APIENTRYP PFNGLUNIFORM1DPROC) (GLint location, GLdouble x); +typedef void (APIENTRYP PFNGLUNIFORM2DPROC) (GLint location, GLdouble x, GLdouble y); +typedef void (APIENTRYP PFNGLUNIFORM3DPROC) (GLint location, GLdouble x, GLdouble y, GLdouble z); +typedef void (APIENTRYP PFNGLUNIFORM4DPROC) (GLint location, GLdouble x, GLdouble y, GLdouble z, GLdouble w); +typedef void (APIENTRYP PFNGLUNIFORM1DVPROC) (GLint location, GLsizei count, const GLdouble *value); +typedef void (APIENTRYP PFNGLUNIFORM2DVPROC) (GLint location, GLsizei count, const GLdouble *value); +typedef void (APIENTRYP PFNGLUNIFORM3DVPROC) (GLint location, GLsizei count, const GLdouble *value); +typedef void (APIENTRYP PFNGLUNIFORM4DVPROC) (GLint location, GLsizei count, const GLdouble *value); +typedef void (APIENTRYP PFNGLUNIFORMMATRIX2DVPROC) (GLint location, GLsizei count, GLboolean transpose, const GLdouble *value); +typedef void (APIENTRYP PFNGLUNIFORMMATRIX3DVPROC) (GLint location, GLsizei count, GLboolean transpose, const GLdouble *value); +typedef void (APIENTRYP PFNGLUNIFORMMATRIX4DVPROC) (GLint location, GLsizei count, GLboolean transpose, const GLdouble *value); +typedef void (APIENTRYP PFNGLUNIFORMMATRIX2X3DVPROC) (GLint location, GLsizei count, GLboolean transpose, const GLdouble *value); +typedef void (APIENTRYP PFNGLUNIFORMMATRIX2X4DVPROC) (GLint location, GLsizei count, GLboolean transpose, const GLdouble *value); +typedef void (APIENTRYP PFNGLUNIFORMMATRIX3X2DVPROC) (GLint location, GLsizei count, GLboolean transpose, const GLdouble *value); +typedef void (APIENTRYP PFNGLUNIFORMMATRIX3X4DVPROC) (GLint location, GLsizei count, GLboolean transpose, const GLdouble *value); +typedef void (APIENTRYP PFNGLUNIFORMMATRIX4X2DVPROC) (GLint location, GLsizei count, GLboolean transpose, const GLdouble *value); +typedef void (APIENTRYP PFNGLUNIFORMMATRIX4X3DVPROC) (GLint location, GLsizei count, GLboolean transpose, const GLdouble *value); +typedef void (APIENTRYP PFNGLGETUNIFORMDVPROC) (GLuint program, GLint location, GLdouble *params); +typedef GLint (APIENTRYP PFNGLGETSUBROUTINEUNIFORMLOCATIONPROC) (GLuint program, GLenum shadertype, const GLchar *name); +typedef GLuint (APIENTRYP PFNGLGETSUBROUTINEINDEXPROC) (GLuint program, GLenum shadertype, const GLchar *name); +typedef void (APIENTRYP PFNGLGETACTIVESUBROUTINEUNIFORMIVPROC) (GLuint program, GLenum shadertype, GLuint index, GLenum pname, GLint *values); +typedef void (APIENTRYP PFNGLGETACTIVESUBROUTINEUNIFORMNAMEPROC) (GLuint program, GLenum shadertype, GLuint index, GLsizei bufsize, GLsizei *length, GLchar *name); +typedef void (APIENTRYP PFNGLGETACTIVESUBROUTINENAMEPROC) (GLuint program, GLenum shadertype, GLuint index, GLsizei bufsize, GLsizei *length, GLchar *name); +typedef void (APIENTRYP PFNGLUNIFORMSUBROUTINESUIVPROC) (GLenum shadertype, GLsizei count, const GLuint *indices); +typedef void (APIENTRYP PFNGLGETUNIFORMSUBROUTINEUIVPROC) (GLenum shadertype, GLint location, GLuint *params); +typedef void (APIENTRYP PFNGLGETPROGRAMSTAGEIVPROC) (GLuint program, GLenum shadertype, GLenum pname, GLint *values); +typedef void (APIENTRYP PFNGLPATCHPARAMETERIPROC) (GLenum pname, GLint value); +typedef void (APIENTRYP PFNGLPATCHPARAMETERFVPROC) (GLenum pname, const GLfloat *values); +typedef void (APIENTRYP PFNGLBINDTRANSFORMFEEDBACKPROC) (GLenum target, GLuint id); +typedef void (APIENTRYP PFNGLDELETETRANSFORMFEEDBACKSPROC) (GLsizei n, const GLuint *ids); +typedef void (APIENTRYP PFNGLGENTRANSFORMFEEDBACKSPROC) (GLsizei n, GLuint *ids); +typedef GLboolean (APIENTRYP PFNGLISTRANSFORMFEEDBACKPROC) (GLuint id); +typedef void (APIENTRYP PFNGLPAUSETRANSFORMFEEDBACKPROC) (void); +typedef void (APIENTRYP PFNGLRESUMETRANSFORMFEEDBACKPROC) (void); +typedef void (APIENTRYP PFNGLDRAWTRANSFORMFEEDBACKPROC) (GLenum mode, GLuint id); +typedef void (APIENTRYP PFNGLDRAWTRANSFORMFEEDBACKSTREAMPROC) (GLenum mode, GLuint id, GLuint stream); +typedef void (APIENTRYP PFNGLBEGINQUERYINDEXEDPROC) (GLenum target, GLuint index, GLuint id); +typedef void (APIENTRYP PFNGLENDQUERYINDEXEDPROC) (GLenum target, GLuint index); +typedef void (APIENTRYP PFNGLGETQUERYINDEXEDIVPROC) (GLenum target, GLuint index, GLenum pname, GLint *params); +#ifdef GL_GLEXT_PROTOTYPES +GLAPI void APIENTRY glMinSampleShading (GLfloat value); +GLAPI void APIENTRY glBlendEquationi (GLuint buf, GLenum mode); +GLAPI void APIENTRY glBlendEquationSeparatei (GLuint buf, GLenum modeRGB, GLenum modeAlpha); +GLAPI void APIENTRY glBlendFunci (GLuint buf, GLenum src, GLenum dst); +GLAPI void APIENTRY glBlendFuncSeparatei (GLuint buf, GLenum srcRGB, GLenum dstRGB, GLenum srcAlpha, GLenum dstAlpha); +GLAPI void APIENTRY glDrawArraysIndirect (GLenum mode, const void *indirect); +GLAPI void APIENTRY glDrawElementsIndirect (GLenum mode, GLenum type, const void *indirect); +GLAPI void APIENTRY glUniform1d (GLint location, GLdouble x); +GLAPI void APIENTRY glUniform2d (GLint location, GLdouble x, GLdouble y); +GLAPI void APIENTRY glUniform3d (GLint location, GLdouble x, GLdouble y, GLdouble z); +GLAPI void APIENTRY glUniform4d (GLint location, GLdouble x, GLdouble y, GLdouble z, GLdouble w); +GLAPI void APIENTRY glUniform1dv (GLint location, GLsizei count, const GLdouble *value); +GLAPI void APIENTRY glUniform2dv (GLint location, GLsizei count, const GLdouble *value); +GLAPI void APIENTRY glUniform3dv (GLint location, GLsizei count, const GLdouble *value); +GLAPI void APIENTRY glUniform4dv (GLint location, GLsizei count, const GLdouble *value); +GLAPI void APIENTRY glUniformMatrix2dv (GLint location, GLsizei count, GLboolean transpose, const GLdouble *value); +GLAPI void APIENTRY glUniformMatrix3dv (GLint location, GLsizei count, GLboolean transpose, const GLdouble *value); +GLAPI void APIENTRY glUniformMatrix4dv (GLint location, GLsizei count, GLboolean transpose, const GLdouble *value); +GLAPI void APIENTRY glUniformMatrix2x3dv (GLint location, GLsizei count, GLboolean transpose, const GLdouble *value); +GLAPI void APIENTRY glUniformMatrix2x4dv (GLint location, GLsizei count, GLboolean transpose, const GLdouble *value); +GLAPI void APIENTRY glUniformMatrix3x2dv (GLint location, GLsizei count, GLboolean transpose, const GLdouble *value); +GLAPI void APIENTRY glUniformMatrix3x4dv (GLint location, GLsizei count, GLboolean transpose, const GLdouble *value); +GLAPI void APIENTRY glUniformMatrix4x2dv (GLint location, GLsizei count, GLboolean transpose, const GLdouble *value); +GLAPI void APIENTRY glUniformMatrix4x3dv (GLint location, GLsizei count, GLboolean transpose, const GLdouble *value); +GLAPI void APIENTRY glGetUniformdv (GLuint program, GLint location, GLdouble *params); +GLAPI GLint APIENTRY glGetSubroutineUniformLocation (GLuint program, GLenum shadertype, const GLchar *name); +GLAPI GLuint APIENTRY glGetSubroutineIndex (GLuint program, GLenum shadertype, const GLchar *name); +GLAPI void APIENTRY glGetActiveSubroutineUniformiv (GLuint program, GLenum shadertype, GLuint index, GLenum pname, GLint *values); +GLAPI void APIENTRY glGetActiveSubroutineUniformName (GLuint program, GLenum shadertype, GLuint index, GLsizei bufsize, GLsizei *length, GLchar *name); +GLAPI void APIENTRY glGetActiveSubroutineName (GLuint program, GLenum shadertype, GLuint index, GLsizei bufsize, GLsizei *length, GLchar *name); +GLAPI void APIENTRY glUniformSubroutinesuiv (GLenum shadertype, GLsizei count, const GLuint *indices); +GLAPI void APIENTRY glGetUniformSubroutineuiv (GLenum shadertype, GLint location, GLuint *params); +GLAPI void APIENTRY glGetProgramStageiv (GLuint program, GLenum shadertype, GLenum pname, GLint *values); +GLAPI void APIENTRY glPatchParameteri (GLenum pname, GLint value); +GLAPI void APIENTRY glPatchParameterfv (GLenum pname, const GLfloat *values); +GLAPI void APIENTRY glBindTransformFeedback (GLenum target, GLuint id); +GLAPI void APIENTRY glDeleteTransformFeedbacks (GLsizei n, const GLuint *ids); +GLAPI void APIENTRY glGenTransformFeedbacks (GLsizei n, GLuint *ids); +GLAPI GLboolean APIENTRY glIsTransformFeedback (GLuint id); +GLAPI void APIENTRY glPauseTransformFeedback (void); +GLAPI void APIENTRY glResumeTransformFeedback (void); +GLAPI void APIENTRY glDrawTransformFeedback (GLenum mode, GLuint id); +GLAPI void APIENTRY glDrawTransformFeedbackStream (GLenum mode, GLuint id, GLuint stream); +GLAPI void APIENTRY glBeginQueryIndexed (GLenum target, GLuint index, GLuint id); +GLAPI void APIENTRY glEndQueryIndexed (GLenum target, GLuint index); +GLAPI void APIENTRY glGetQueryIndexediv (GLenum target, GLuint index, GLenum pname, GLint *params); +#endif +#endif /* GL_VERSION_4_0 */ + +#ifndef GL_VERSION_4_1 +#define GL_VERSION_4_1 1 +#define GL_FIXED 0x140C +#define GL_IMPLEMENTATION_COLOR_READ_TYPE 0x8B9A +#define GL_IMPLEMENTATION_COLOR_READ_FORMAT 0x8B9B +#define GL_LOW_FLOAT 0x8DF0 +#define GL_MEDIUM_FLOAT 0x8DF1 +#define GL_HIGH_FLOAT 0x8DF2 +#define GL_LOW_INT 0x8DF3 +#define GL_MEDIUM_INT 0x8DF4 +#define GL_HIGH_INT 0x8DF5 +#define GL_SHADER_COMPILER 0x8DFA +#define GL_SHADER_BINARY_FORMATS 0x8DF8 +#define GL_NUM_SHADER_BINARY_FORMATS 0x8DF9 +#define GL_MAX_VERTEX_UNIFORM_VECTORS 0x8DFB +#define GL_MAX_VARYING_VECTORS 0x8DFC +#define GL_MAX_FRAGMENT_UNIFORM_VECTORS 0x8DFD +#define GL_RGB565 0x8D62 +#define GL_PROGRAM_BINARY_RETRIEVABLE_HINT 0x8257 +#define GL_PROGRAM_BINARY_LENGTH 0x8741 +#define GL_NUM_PROGRAM_BINARY_FORMATS 0x87FE +#define GL_PROGRAM_BINARY_FORMATS 0x87FF +#define GL_VERTEX_SHADER_BIT 0x00000001 +#define GL_FRAGMENT_SHADER_BIT 0x00000002 +#define GL_GEOMETRY_SHADER_BIT 0x00000004 +#define GL_TESS_CONTROL_SHADER_BIT 0x00000008 +#define GL_TESS_EVALUATION_SHADER_BIT 0x00000010 +#define GL_ALL_SHADER_BITS 0xFFFFFFFF +#define GL_PROGRAM_SEPARABLE 0x8258 +#define GL_ACTIVE_PROGRAM 0x8259 +#define GL_PROGRAM_PIPELINE_BINDING 0x825A +#define GL_MAX_VIEWPORTS 0x825B +#define GL_VIEWPORT_SUBPIXEL_BITS 0x825C +#define GL_VIEWPORT_BOUNDS_RANGE 0x825D +#define GL_LAYER_PROVOKING_VERTEX 0x825E +#define GL_VIEWPORT_INDEX_PROVOKING_VERTEX 0x825F +#define GL_UNDEFINED_VERTEX 0x8260 +typedef void (APIENTRYP PFNGLRELEASESHADERCOMPILERPROC) (void); +typedef void (APIENTRYP PFNGLSHADERBINARYPROC) (GLsizei count, const GLuint *shaders, GLenum binaryformat, const void *binary, GLsizei length); +typedef void (APIENTRYP PFNGLGETSHADERPRECISIONFORMATPROC) (GLenum shadertype, GLenum precisiontype, GLint *range, GLint *precision); +typedef void (APIENTRYP PFNGLDEPTHRANGEFPROC) (GLfloat n, GLfloat f); +typedef void (APIENTRYP PFNGLCLEARDEPTHFPROC) (GLfloat d); +typedef void (APIENTRYP PFNGLGETPROGRAMBINARYPROC) (GLuint program, GLsizei bufSize, GLsizei *length, GLenum *binaryFormat, void *binary); +typedef void (APIENTRYP PFNGLPROGRAMBINARYPROC) (GLuint program, GLenum binaryFormat, const void *binary, GLsizei length); +typedef void (APIENTRYP PFNGLPROGRAMPARAMETERIPROC) (GLuint program, GLenum pname, GLint value); +typedef void (APIENTRYP PFNGLUSEPROGRAMSTAGESPROC) (GLuint pipeline, GLbitfield stages, GLuint program); +typedef void (APIENTRYP PFNGLACTIVESHADERPROGRAMPROC) (GLuint pipeline, GLuint program); +typedef GLuint (APIENTRYP PFNGLCREATESHADERPROGRAMVPROC) (GLenum type, GLsizei count, const GLchar *const*strings); +typedef void (APIENTRYP PFNGLBINDPROGRAMPIPELINEPROC) (GLuint pipeline); +typedef void (APIENTRYP PFNGLDELETEPROGRAMPIPELINESPROC) (GLsizei n, const GLuint *pipelines); +typedef void (APIENTRYP PFNGLGENPROGRAMPIPELINESPROC) (GLsizei n, GLuint *pipelines); +typedef GLboolean (APIENTRYP PFNGLISPROGRAMPIPELINEPROC) (GLuint pipeline); +typedef void (APIENTRYP PFNGLGETPROGRAMPIPELINEIVPROC) (GLuint pipeline, GLenum pname, GLint *params); +typedef void (APIENTRYP PFNGLPROGRAMUNIFORM1IPROC) (GLuint program, GLint location, GLint v0); +typedef void (APIENTRYP PFNGLPROGRAMUNIFORM1IVPROC) (GLuint program, GLint location, GLsizei count, const GLint *value); +typedef void (APIENTRYP PFNGLPROGRAMUNIFORM1FPROC) (GLuint program, GLint location, GLfloat v0); +typedef void (APIENTRYP PFNGLPROGRAMUNIFORM1FVPROC) (GLuint program, GLint location, GLsizei count, const GLfloat *value); +typedef void (APIENTRYP PFNGLPROGRAMUNIFORM1DPROC) (GLuint program, GLint location, GLdouble v0); +typedef void (APIENTRYP PFNGLPROGRAMUNIFORM1DVPROC) (GLuint program, GLint location, GLsizei count, const GLdouble *value); +typedef void (APIENTRYP PFNGLPROGRAMUNIFORM1UIPROC) (GLuint program, GLint location, GLuint v0); +typedef void (APIENTRYP PFNGLPROGRAMUNIFORM1UIVPROC) (GLuint program, GLint location, GLsizei count, const GLuint *value); +typedef void (APIENTRYP PFNGLPROGRAMUNIFORM2IPROC) (GLuint program, GLint location, GLint v0, GLint v1); +typedef void (APIENTRYP PFNGLPROGRAMUNIFORM2IVPROC) (GLuint program, GLint location, GLsizei count, const GLint *value); +typedef void (APIENTRYP PFNGLPROGRAMUNIFORM2FPROC) (GLuint program, GLint location, GLfloat v0, GLfloat v1); +typedef void (APIENTRYP PFNGLPROGRAMUNIFORM2FVPROC) (GLuint program, GLint location, GLsizei count, const GLfloat *value); +typedef void (APIENTRYP PFNGLPROGRAMUNIFORM2DPROC) (GLuint program, GLint location, GLdouble v0, GLdouble v1); +typedef void (APIENTRYP PFNGLPROGRAMUNIFORM2DVPROC) (GLuint program, GLint location, GLsizei count, const GLdouble *value); +typedef void (APIENTRYP PFNGLPROGRAMUNIFORM2UIPROC) (GLuint program, GLint location, GLuint v0, GLuint v1); +typedef void (APIENTRYP PFNGLPROGRAMUNIFORM2UIVPROC) (GLuint program, GLint location, GLsizei count, const GLuint *value); +typedef void (APIENTRYP PFNGLPROGRAMUNIFORM3IPROC) (GLuint program, GLint location, GLint v0, GLint v1, GLint v2); +typedef void (APIENTRYP PFNGLPROGRAMUNIFORM3IVPROC) (GLuint program, GLint location, GLsizei count, const GLint *value); +typedef void (APIENTRYP PFNGLPROGRAMUNIFORM3FPROC) (GLuint program, GLint location, GLfloat v0, GLfloat v1, GLfloat v2); +typedef void (APIENTRYP PFNGLPROGRAMUNIFORM3FVPROC) (GLuint program, GLint location, GLsizei count, const GLfloat *value); +typedef void (APIENTRYP PFNGLPROGRAMUNIFORM3DPROC) (GLuint program, GLint location, GLdouble v0, GLdouble v1, GLdouble v2); +typedef void (APIENTRYP PFNGLPROGRAMUNIFORM3DVPROC) (GLuint program, GLint location, GLsizei count, const GLdouble *value); +typedef void (APIENTRYP PFNGLPROGRAMUNIFORM3UIPROC) (GLuint program, GLint location, GLuint v0, GLuint v1, GLuint v2); +typedef void (APIENTRYP PFNGLPROGRAMUNIFORM3UIVPROC) (GLuint program, GLint location, GLsizei count, const GLuint *value); +typedef void (APIENTRYP PFNGLPROGRAMUNIFORM4IPROC) (GLuint program, GLint location, GLint v0, GLint v1, GLint v2, GLint v3); +typedef void (APIENTRYP PFNGLPROGRAMUNIFORM4IVPROC) (GLuint program, GLint location, GLsizei count, const GLint *value); +typedef void (APIENTRYP PFNGLPROGRAMUNIFORM4FPROC) (GLuint program, GLint location, GLfloat v0, GLfloat v1, GLfloat v2, GLfloat v3); +typedef void (APIENTRYP PFNGLPROGRAMUNIFORM4FVPROC) (GLuint program, GLint location, GLsizei count, const GLfloat *value); +typedef void (APIENTRYP PFNGLPROGRAMUNIFORM4DPROC) (GLuint program, GLint location, GLdouble v0, GLdouble v1, GLdouble v2, GLdouble v3); +typedef void (APIENTRYP PFNGLPROGRAMUNIFORM4DVPROC) (GLuint program, GLint location, GLsizei count, const GLdouble *value); +typedef void (APIENTRYP PFNGLPROGRAMUNIFORM4UIPROC) (GLuint program, GLint location, GLuint v0, GLuint v1, GLuint v2, GLuint v3); +typedef void (APIENTRYP PFNGLPROGRAMUNIFORM4UIVPROC) (GLuint program, GLint location, GLsizei count, const GLuint *value); +typedef void (APIENTRYP PFNGLPROGRAMUNIFORMMATRIX2FVPROC) (GLuint program, GLint location, GLsizei count, GLboolean transpose, const GLfloat *value); +typedef void (APIENTRYP PFNGLPROGRAMUNIFORMMATRIX3FVPROC) (GLuint program, GLint location, GLsizei count, GLboolean transpose, const GLfloat *value); +typedef void (APIENTRYP PFNGLPROGRAMUNIFORMMATRIX4FVPROC) (GLuint program, GLint location, GLsizei count, GLboolean transpose, const GLfloat *value); +typedef void (APIENTRYP PFNGLPROGRAMUNIFORMMATRIX2DVPROC) (GLuint program, GLint location, GLsizei count, GLboolean transpose, const GLdouble *value); +typedef void (APIENTRYP PFNGLPROGRAMUNIFORMMATRIX3DVPROC) (GLuint program, GLint location, GLsizei count, GLboolean transpose, const GLdouble *value); +typedef void (APIENTRYP PFNGLPROGRAMUNIFORMMATRIX4DVPROC) (GLuint program, GLint location, GLsizei count, GLboolean transpose, const GLdouble *value); +typedef void (APIENTRYP PFNGLPROGRAMUNIFORMMATRIX2X3FVPROC) (GLuint program, GLint location, GLsizei count, GLboolean transpose, const GLfloat *value); +typedef void (APIENTRYP PFNGLPROGRAMUNIFORMMATRIX3X2FVPROC) (GLuint program, GLint location, GLsizei count, GLboolean transpose, const GLfloat *value); +typedef void (APIENTRYP PFNGLPROGRAMUNIFORMMATRIX2X4FVPROC) (GLuint program, GLint location, GLsizei count, GLboolean transpose, const GLfloat *value); +typedef void (APIENTRYP PFNGLPROGRAMUNIFORMMATRIX4X2FVPROC) (GLuint program, GLint location, GLsizei count, GLboolean transpose, const GLfloat *value); +typedef void (APIENTRYP PFNGLPROGRAMUNIFORMMATRIX3X4FVPROC) (GLuint program, GLint location, GLsizei count, GLboolean transpose, const GLfloat *value); +typedef void (APIENTRYP PFNGLPROGRAMUNIFORMMATRIX4X3FVPROC) (GLuint program, GLint location, GLsizei count, GLboolean transpose, const GLfloat *value); +typedef void (APIENTRYP PFNGLPROGRAMUNIFORMMATRIX2X3DVPROC) (GLuint program, GLint location, GLsizei count, GLboolean transpose, const GLdouble *value); +typedef void (APIENTRYP PFNGLPROGRAMUNIFORMMATRIX3X2DVPROC) (GLuint program, GLint location, GLsizei count, GLboolean transpose, const GLdouble *value); +typedef void (APIENTRYP PFNGLPROGRAMUNIFORMMATRIX2X4DVPROC) (GLuint program, GLint location, GLsizei count, GLboolean transpose, const GLdouble *value); +typedef void (APIENTRYP PFNGLPROGRAMUNIFORMMATRIX4X2DVPROC) (GLuint program, GLint location, GLsizei count, GLboolean transpose, const GLdouble *value); +typedef void (APIENTRYP PFNGLPROGRAMUNIFORMMATRIX3X4DVPROC) (GLuint program, GLint location, GLsizei count, GLboolean transpose, const GLdouble *value); +typedef void (APIENTRYP PFNGLPROGRAMUNIFORMMATRIX4X3DVPROC) (GLuint program, GLint location, GLsizei count, GLboolean transpose, const GLdouble *value); +typedef void (APIENTRYP PFNGLVALIDATEPROGRAMPIPELINEPROC) (GLuint pipeline); +typedef void (APIENTRYP PFNGLGETPROGRAMPIPELINEINFOLOGPROC) (GLuint pipeline, GLsizei bufSize, GLsizei *length, GLchar *infoLog); +typedef void (APIENTRYP PFNGLVERTEXATTRIBL1DPROC) (GLuint index, GLdouble x); +typedef void (APIENTRYP PFNGLVERTEXATTRIBL2DPROC) (GLuint index, GLdouble x, GLdouble y); +typedef void (APIENTRYP PFNGLVERTEXATTRIBL3DPROC) (GLuint index, GLdouble x, GLdouble y, GLdouble z); +typedef void (APIENTRYP PFNGLVERTEXATTRIBL4DPROC) (GLuint index, GLdouble x, GLdouble y, GLdouble z, GLdouble w); +typedef void (APIENTRYP PFNGLVERTEXATTRIBL1DVPROC) (GLuint index, const GLdouble *v); +typedef void (APIENTRYP PFNGLVERTEXATTRIBL2DVPROC) (GLuint index, const GLdouble *v); +typedef void (APIENTRYP PFNGLVERTEXATTRIBL3DVPROC) (GLuint index, const GLdouble *v); +typedef void (APIENTRYP PFNGLVERTEXATTRIBL4DVPROC) (GLuint index, const GLdouble *v); +typedef void (APIENTRYP PFNGLVERTEXATTRIBLPOINTERPROC) (GLuint index, GLint size, GLenum type, GLsizei stride, const void *pointer); +typedef void (APIENTRYP PFNGLGETVERTEXATTRIBLDVPROC) (GLuint index, GLenum pname, GLdouble *params); +typedef void (APIENTRYP PFNGLVIEWPORTARRAYVPROC) (GLuint first, GLsizei count, const GLfloat *v); +typedef void (APIENTRYP PFNGLVIEWPORTINDEXEDFPROC) (GLuint index, GLfloat x, GLfloat y, GLfloat w, GLfloat h); +typedef void (APIENTRYP PFNGLVIEWPORTINDEXEDFVPROC) (GLuint index, const GLfloat *v); +typedef void (APIENTRYP PFNGLSCISSORARRAYVPROC) (GLuint first, GLsizei count, const GLint *v); +typedef void (APIENTRYP PFNGLSCISSORINDEXEDPROC) (GLuint index, GLint left, GLint bottom, GLsizei width, GLsizei height); +typedef void (APIENTRYP PFNGLSCISSORINDEXEDVPROC) (GLuint index, const GLint *v); +typedef void (APIENTRYP PFNGLDEPTHRANGEARRAYVPROC) (GLuint first, GLsizei count, const GLdouble *v); +typedef void (APIENTRYP PFNGLDEPTHRANGEINDEXEDPROC) (GLuint index, GLdouble n, GLdouble f); +typedef void (APIENTRYP PFNGLGETFLOATI_VPROC) (GLenum target, GLuint index, GLfloat *data); +typedef void (APIENTRYP PFNGLGETDOUBLEI_VPROC) (GLenum target, GLuint index, GLdouble *data); +#ifdef GL_GLEXT_PROTOTYPES +GLAPI void APIENTRY glReleaseShaderCompiler (void); +GLAPI void APIENTRY glShaderBinary (GLsizei count, const GLuint *shaders, GLenum binaryformat, const void *binary, GLsizei length); +GLAPI void APIENTRY glGetShaderPrecisionFormat (GLenum shadertype, GLenum precisiontype, GLint *range, GLint *precision); +GLAPI void APIENTRY glDepthRangef (GLfloat n, GLfloat f); +GLAPI void APIENTRY glClearDepthf (GLfloat d); +GLAPI void APIENTRY glGetProgramBinary (GLuint program, GLsizei bufSize, GLsizei *length, GLenum *binaryFormat, void *binary); +GLAPI void APIENTRY glProgramBinary (GLuint program, GLenum binaryFormat, const void *binary, GLsizei length); +GLAPI void APIENTRY glProgramParameteri (GLuint program, GLenum pname, GLint value); +GLAPI void APIENTRY glUseProgramStages (GLuint pipeline, GLbitfield stages, GLuint program); +GLAPI void APIENTRY glActiveShaderProgram (GLuint pipeline, GLuint program); +GLAPI GLuint APIENTRY glCreateShaderProgramv (GLenum type, GLsizei count, const GLchar *const*strings); +GLAPI void APIENTRY glBindProgramPipeline (GLuint pipeline); +GLAPI void APIENTRY glDeleteProgramPipelines (GLsizei n, const GLuint *pipelines); +GLAPI void APIENTRY glGenProgramPipelines (GLsizei n, GLuint *pipelines); +GLAPI GLboolean APIENTRY glIsProgramPipeline (GLuint pipeline); +GLAPI void APIENTRY glGetProgramPipelineiv (GLuint pipeline, GLenum pname, GLint *params); +GLAPI void APIENTRY glProgramUniform1i (GLuint program, GLint location, GLint v0); +GLAPI void APIENTRY glProgramUniform1iv (GLuint program, GLint location, GLsizei count, const GLint *value); +GLAPI void APIENTRY glProgramUniform1f (GLuint program, GLint location, GLfloat v0); +GLAPI void APIENTRY glProgramUniform1fv (GLuint program, GLint location, GLsizei count, const GLfloat *value); +GLAPI void APIENTRY glProgramUniform1d (GLuint program, GLint location, GLdouble v0); +GLAPI void APIENTRY glProgramUniform1dv (GLuint program, GLint location, GLsizei count, const GLdouble *value); +GLAPI void APIENTRY glProgramUniform1ui (GLuint program, GLint location, GLuint v0); +GLAPI void APIENTRY glProgramUniform1uiv (GLuint program, GLint location, GLsizei count, const GLuint *value); +GLAPI void APIENTRY glProgramUniform2i (GLuint program, GLint location, GLint v0, GLint v1); +GLAPI void APIENTRY glProgramUniform2iv (GLuint program, GLint location, GLsizei count, const GLint *value); +GLAPI void APIENTRY glProgramUniform2f (GLuint program, GLint location, GLfloat v0, GLfloat v1); +GLAPI void APIENTRY glProgramUniform2fv (GLuint program, GLint location, GLsizei count, const GLfloat *value); +GLAPI void APIENTRY glProgramUniform2d (GLuint program, GLint location, GLdouble v0, GLdouble v1); +GLAPI void APIENTRY glProgramUniform2dv (GLuint program, GLint location, GLsizei count, const GLdouble *value); +GLAPI void APIENTRY glProgramUniform2ui (GLuint program, GLint location, GLuint v0, GLuint v1); +GLAPI void APIENTRY glProgramUniform2uiv (GLuint program, GLint location, GLsizei count, const GLuint *value); +GLAPI void APIENTRY glProgramUniform3i (GLuint program, GLint location, GLint v0, GLint v1, GLint v2); +GLAPI void APIENTRY glProgramUniform3iv (GLuint program, GLint location, GLsizei count, const GLint *value); +GLAPI void APIENTRY glProgramUniform3f (GLuint program, GLint location, GLfloat v0, GLfloat v1, GLfloat v2); +GLAPI void APIENTRY glProgramUniform3fv (GLuint program, GLint location, GLsizei count, const GLfloat *value); +GLAPI void APIENTRY glProgramUniform3d (GLuint program, GLint location, GLdouble v0, GLdouble v1, GLdouble v2); +GLAPI void APIENTRY glProgramUniform3dv (GLuint program, GLint location, GLsizei count, const GLdouble *value); +GLAPI void APIENTRY glProgramUniform3ui (GLuint program, GLint location, GLuint v0, GLuint v1, GLuint v2); +GLAPI void APIENTRY glProgramUniform3uiv (GLuint program, GLint location, GLsizei count, const GLuint *value); +GLAPI void APIENTRY glProgramUniform4i (GLuint program, GLint location, GLint v0, GLint v1, GLint v2, GLint v3); +GLAPI void APIENTRY glProgramUniform4iv (GLuint program, GLint location, GLsizei count, const GLint *value); +GLAPI void APIENTRY glProgramUniform4f (GLuint program, GLint location, GLfloat v0, GLfloat v1, GLfloat v2, GLfloat v3); +GLAPI void APIENTRY glProgramUniform4fv (GLuint program, GLint location, GLsizei count, const GLfloat *value); +GLAPI void APIENTRY glProgramUniform4d (GLuint program, GLint location, GLdouble v0, GLdouble v1, GLdouble v2, GLdouble v3); +GLAPI void APIENTRY glProgramUniform4dv (GLuint program, GLint location, GLsizei count, const GLdouble *value); +GLAPI void APIENTRY glProgramUniform4ui (GLuint program, GLint location, GLuint v0, GLuint v1, GLuint v2, GLuint v3); +GLAPI void APIENTRY glProgramUniform4uiv (GLuint program, GLint location, GLsizei count, const GLuint *value); +GLAPI void APIENTRY glProgramUniformMatrix2fv (GLuint program, GLint location, GLsizei count, GLboolean transpose, const GLfloat *value); +GLAPI void APIENTRY glProgramUniformMatrix3fv (GLuint program, GLint location, GLsizei count, GLboolean transpose, const GLfloat *value); +GLAPI void APIENTRY glProgramUniformMatrix4fv (GLuint program, GLint location, GLsizei count, GLboolean transpose, const GLfloat *value); +GLAPI void APIENTRY glProgramUniformMatrix2dv (GLuint program, GLint location, GLsizei count, GLboolean transpose, const GLdouble *value); +GLAPI void APIENTRY glProgramUniformMatrix3dv (GLuint program, GLint location, GLsizei count, GLboolean transpose, const GLdouble *value); +GLAPI void APIENTRY glProgramUniformMatrix4dv (GLuint program, GLint location, GLsizei count, GLboolean transpose, const GLdouble *value); +GLAPI void APIENTRY glProgramUniformMatrix2x3fv (GLuint program, GLint location, GLsizei count, GLboolean transpose, const GLfloat *value); +GLAPI void APIENTRY glProgramUniformMatrix3x2fv (GLuint program, GLint location, GLsizei count, GLboolean transpose, const GLfloat *value); +GLAPI void APIENTRY glProgramUniformMatrix2x4fv (GLuint program, GLint location, GLsizei count, GLboolean transpose, const GLfloat *value); +GLAPI void APIENTRY glProgramUniformMatrix4x2fv (GLuint program, GLint location, GLsizei count, GLboolean transpose, const GLfloat *value); +GLAPI void APIENTRY glProgramUniformMatrix3x4fv (GLuint program, GLint location, GLsizei count, GLboolean transpose, const GLfloat *value); +GLAPI void APIENTRY glProgramUniformMatrix4x3fv (GLuint program, GLint location, GLsizei count, GLboolean transpose, const GLfloat *value); +GLAPI void APIENTRY glProgramUniformMatrix2x3dv (GLuint program, GLint location, GLsizei count, GLboolean transpose, const GLdouble *value); +GLAPI void APIENTRY glProgramUniformMatrix3x2dv (GLuint program, GLint location, GLsizei count, GLboolean transpose, const GLdouble *value); +GLAPI void APIENTRY glProgramUniformMatrix2x4dv (GLuint program, GLint location, GLsizei count, GLboolean transpose, const GLdouble *value); +GLAPI void APIENTRY glProgramUniformMatrix4x2dv (GLuint program, GLint location, GLsizei count, GLboolean transpose, const GLdouble *value); +GLAPI void APIENTRY glProgramUniformMatrix3x4dv (GLuint program, GLint location, GLsizei count, GLboolean transpose, const GLdouble *value); +GLAPI void APIENTRY glProgramUniformMatrix4x3dv (GLuint program, GLint location, GLsizei count, GLboolean transpose, const GLdouble *value); +GLAPI void APIENTRY glValidateProgramPipeline (GLuint pipeline); +GLAPI void APIENTRY glGetProgramPipelineInfoLog (GLuint pipeline, GLsizei bufSize, GLsizei *length, GLchar *infoLog); +GLAPI void APIENTRY glVertexAttribL1d (GLuint index, GLdouble x); +GLAPI void APIENTRY glVertexAttribL2d (GLuint index, GLdouble x, GLdouble y); +GLAPI void APIENTRY glVertexAttribL3d (GLuint index, GLdouble x, GLdouble y, GLdouble z); +GLAPI void APIENTRY glVertexAttribL4d (GLuint index, GLdouble x, GLdouble y, GLdouble z, GLdouble w); +GLAPI void APIENTRY glVertexAttribL1dv (GLuint index, const GLdouble *v); +GLAPI void APIENTRY glVertexAttribL2dv (GLuint index, const GLdouble *v); +GLAPI void APIENTRY glVertexAttribL3dv (GLuint index, const GLdouble *v); +GLAPI void APIENTRY glVertexAttribL4dv (GLuint index, const GLdouble *v); +GLAPI void APIENTRY glVertexAttribLPointer (GLuint index, GLint size, GLenum type, GLsizei stride, const void *pointer); +GLAPI void APIENTRY glGetVertexAttribLdv (GLuint index, GLenum pname, GLdouble *params); +GLAPI void APIENTRY glViewportArrayv (GLuint first, GLsizei count, const GLfloat *v); +GLAPI void APIENTRY glViewportIndexedf (GLuint index, GLfloat x, GLfloat y, GLfloat w, GLfloat h); +GLAPI void APIENTRY glViewportIndexedfv (GLuint index, const GLfloat *v); +GLAPI void APIENTRY glScissorArrayv (GLuint first, GLsizei count, const GLint *v); +GLAPI void APIENTRY glScissorIndexed (GLuint index, GLint left, GLint bottom, GLsizei width, GLsizei height); +GLAPI void APIENTRY glScissorIndexedv (GLuint index, const GLint *v); +GLAPI void APIENTRY glDepthRangeArrayv (GLuint first, GLsizei count, const GLdouble *v); +GLAPI void APIENTRY glDepthRangeIndexed (GLuint index, GLdouble n, GLdouble f); +GLAPI void APIENTRY glGetFloati_v (GLenum target, GLuint index, GLfloat *data); +GLAPI void APIENTRY glGetDoublei_v (GLenum target, GLuint index, GLdouble *data); +#endif +#endif /* GL_VERSION_4_1 */ + +#ifndef GL_VERSION_4_2 +#define GL_VERSION_4_2 1 +#define GL_UNPACK_COMPRESSED_BLOCK_WIDTH 0x9127 +#define GL_UNPACK_COMPRESSED_BLOCK_HEIGHT 0x9128 +#define GL_UNPACK_COMPRESSED_BLOCK_DEPTH 0x9129 +#define GL_UNPACK_COMPRESSED_BLOCK_SIZE 0x912A +#define GL_PACK_COMPRESSED_BLOCK_WIDTH 0x912B +#define GL_PACK_COMPRESSED_BLOCK_HEIGHT 0x912C +#define GL_PACK_COMPRESSED_BLOCK_DEPTH 0x912D +#define GL_PACK_COMPRESSED_BLOCK_SIZE 0x912E +#define GL_NUM_SAMPLE_COUNTS 0x9380 +#define GL_MIN_MAP_BUFFER_ALIGNMENT 0x90BC +#define GL_ATOMIC_COUNTER_BUFFER 0x92C0 +#define GL_ATOMIC_COUNTER_BUFFER_BINDING 0x92C1 +#define GL_ATOMIC_COUNTER_BUFFER_START 0x92C2 +#define GL_ATOMIC_COUNTER_BUFFER_SIZE 0x92C3 +#define GL_ATOMIC_COUNTER_BUFFER_DATA_SIZE 0x92C4 +#define GL_ATOMIC_COUNTER_BUFFER_ACTIVE_ATOMIC_COUNTERS 0x92C5 +#define GL_ATOMIC_COUNTER_BUFFER_ACTIVE_ATOMIC_COUNTER_INDICES 0x92C6 +#define GL_ATOMIC_COUNTER_BUFFER_REFERENCED_BY_VERTEX_SHADER 0x92C7 +#define GL_ATOMIC_COUNTER_BUFFER_REFERENCED_BY_TESS_CONTROL_SHADER 0x92C8 +#define GL_ATOMIC_COUNTER_BUFFER_REFERENCED_BY_TESS_EVALUATION_SHADER 0x92C9 +#define GL_ATOMIC_COUNTER_BUFFER_REFERENCED_BY_GEOMETRY_SHADER 0x92CA +#define GL_ATOMIC_COUNTER_BUFFER_REFERENCED_BY_FRAGMENT_SHADER 0x92CB +#define GL_MAX_VERTEX_ATOMIC_COUNTER_BUFFERS 0x92CC +#define GL_MAX_TESS_CONTROL_ATOMIC_COUNTER_BUFFERS 0x92CD +#define GL_MAX_TESS_EVALUATION_ATOMIC_COUNTER_BUFFERS 0x92CE +#define GL_MAX_GEOMETRY_ATOMIC_COUNTER_BUFFERS 0x92CF +#define GL_MAX_FRAGMENT_ATOMIC_COUNTER_BUFFERS 0x92D0 +#define GL_MAX_COMBINED_ATOMIC_COUNTER_BUFFERS 0x92D1 +#define GL_MAX_VERTEX_ATOMIC_COUNTERS 0x92D2 +#define GL_MAX_TESS_CONTROL_ATOMIC_COUNTERS 0x92D3 +#define GL_MAX_TESS_EVALUATION_ATOMIC_COUNTERS 0x92D4 +#define GL_MAX_GEOMETRY_ATOMIC_COUNTERS 0x92D5 +#define GL_MAX_FRAGMENT_ATOMIC_COUNTERS 0x92D6 +#define GL_MAX_COMBINED_ATOMIC_COUNTERS 0x92D7 +#define GL_MAX_ATOMIC_COUNTER_BUFFER_SIZE 0x92D8 +#define GL_MAX_ATOMIC_COUNTER_BUFFER_BINDINGS 0x92DC +#define GL_ACTIVE_ATOMIC_COUNTER_BUFFERS 0x92D9 +#define GL_UNIFORM_ATOMIC_COUNTER_BUFFER_INDEX 0x92DA +#define GL_UNSIGNED_INT_ATOMIC_COUNTER 0x92DB +#define GL_VERTEX_ATTRIB_ARRAY_BARRIER_BIT 0x00000001 +#define GL_ELEMENT_ARRAY_BARRIER_BIT 0x00000002 +#define GL_UNIFORM_BARRIER_BIT 0x00000004 +#define GL_TEXTURE_FETCH_BARRIER_BIT 0x00000008 +#define GL_SHADER_IMAGE_ACCESS_BARRIER_BIT 0x00000020 +#define GL_COMMAND_BARRIER_BIT 0x00000040 +#define GL_PIXEL_BUFFER_BARRIER_BIT 0x00000080 +#define GL_TEXTURE_UPDATE_BARRIER_BIT 0x00000100 +#define GL_BUFFER_UPDATE_BARRIER_BIT 0x00000200 +#define GL_FRAMEBUFFER_BARRIER_BIT 0x00000400 +#define GL_TRANSFORM_FEEDBACK_BARRIER_BIT 0x00000800 +#define GL_ATOMIC_COUNTER_BARRIER_BIT 0x00001000 +#define GL_ALL_BARRIER_BITS 0xFFFFFFFF +#define GL_MAX_IMAGE_UNITS 0x8F38 +#define GL_MAX_COMBINED_IMAGE_UNITS_AND_FRAGMENT_OUTPUTS 0x8F39 +#define GL_IMAGE_BINDING_NAME 0x8F3A +#define GL_IMAGE_BINDING_LEVEL 0x8F3B +#define GL_IMAGE_BINDING_LAYERED 0x8F3C +#define GL_IMAGE_BINDING_LAYER 0x8F3D +#define GL_IMAGE_BINDING_ACCESS 0x8F3E +#define GL_IMAGE_1D 0x904C +#define GL_IMAGE_2D 0x904D +#define GL_IMAGE_3D 0x904E +#define GL_IMAGE_2D_RECT 0x904F +#define GL_IMAGE_CUBE 0x9050 +#define GL_IMAGE_BUFFER 0x9051 +#define GL_IMAGE_1D_ARRAY 0x9052 +#define GL_IMAGE_2D_ARRAY 0x9053 +#define GL_IMAGE_CUBE_MAP_ARRAY 0x9054 +#define GL_IMAGE_2D_MULTISAMPLE 0x9055 +#define GL_IMAGE_2D_MULTISAMPLE_ARRAY 0x9056 +#define GL_INT_IMAGE_1D 0x9057 +#define GL_INT_IMAGE_2D 0x9058 +#define GL_INT_IMAGE_3D 0x9059 +#define GL_INT_IMAGE_2D_RECT 0x905A +#define GL_INT_IMAGE_CUBE 0x905B +#define GL_INT_IMAGE_BUFFER 0x905C +#define GL_INT_IMAGE_1D_ARRAY 0x905D +#define GL_INT_IMAGE_2D_ARRAY 0x905E +#define GL_INT_IMAGE_CUBE_MAP_ARRAY 0x905F +#define GL_INT_IMAGE_2D_MULTISAMPLE 0x9060 +#define GL_INT_IMAGE_2D_MULTISAMPLE_ARRAY 0x9061 +#define GL_UNSIGNED_INT_IMAGE_1D 0x9062 +#define GL_UNSIGNED_INT_IMAGE_2D 0x9063 +#define GL_UNSIGNED_INT_IMAGE_3D 0x9064 +#define GL_UNSIGNED_INT_IMAGE_2D_RECT 0x9065 +#define GL_UNSIGNED_INT_IMAGE_CUBE 0x9066 +#define GL_UNSIGNED_INT_IMAGE_BUFFER 0x9067 +#define GL_UNSIGNED_INT_IMAGE_1D_ARRAY 0x9068 +#define GL_UNSIGNED_INT_IMAGE_2D_ARRAY 0x9069 +#define GL_UNSIGNED_INT_IMAGE_CUBE_MAP_ARRAY 0x906A +#define GL_UNSIGNED_INT_IMAGE_2D_MULTISAMPLE 0x906B +#define GL_UNSIGNED_INT_IMAGE_2D_MULTISAMPLE_ARRAY 0x906C +#define GL_MAX_IMAGE_SAMPLES 0x906D +#define GL_IMAGE_BINDING_FORMAT 0x906E +#define GL_IMAGE_FORMAT_COMPATIBILITY_TYPE 0x90C7 +#define GL_IMAGE_FORMAT_COMPATIBILITY_BY_SIZE 0x90C8 +#define GL_IMAGE_FORMAT_COMPATIBILITY_BY_CLASS 0x90C9 +#define GL_MAX_VERTEX_IMAGE_UNIFORMS 0x90CA +#define GL_MAX_TESS_CONTROL_IMAGE_UNIFORMS 0x90CB +#define GL_MAX_TESS_EVALUATION_IMAGE_UNIFORMS 0x90CC +#define GL_MAX_GEOMETRY_IMAGE_UNIFORMS 0x90CD +#define GL_MAX_FRAGMENT_IMAGE_UNIFORMS 0x90CE +#define GL_MAX_COMBINED_IMAGE_UNIFORMS 0x90CF +#define GL_COMPRESSED_RGBA_BPTC_UNORM 0x8E8C +#define GL_COMPRESSED_SRGB_ALPHA_BPTC_UNORM 0x8E8D +#define GL_COMPRESSED_RGB_BPTC_SIGNED_FLOAT 0x8E8E +#define GL_COMPRESSED_RGB_BPTC_UNSIGNED_FLOAT 0x8E8F +#define GL_TEXTURE_IMMUTABLE_FORMAT 0x912F +typedef void (APIENTRYP PFNGLDRAWARRAYSINSTANCEDBASEINSTANCEPROC) (GLenum mode, GLint first, GLsizei count, GLsizei instancecount, GLuint baseinstance); +typedef void (APIENTRYP PFNGLDRAWELEMENTSINSTANCEDBASEINSTANCEPROC) (GLenum mode, GLsizei count, GLenum type, const void *indices, GLsizei instancecount, GLuint baseinstance); +typedef void (APIENTRYP PFNGLDRAWELEMENTSINSTANCEDBASEVERTEXBASEINSTANCEPROC) (GLenum mode, GLsizei count, GLenum type, const void *indices, GLsizei instancecount, GLint basevertex, GLuint baseinstance); +typedef void (APIENTRYP PFNGLGETINTERNALFORMATIVPROC) (GLenum target, GLenum internalformat, GLenum pname, GLsizei bufSize, GLint *params); +typedef void (APIENTRYP PFNGLGETACTIVEATOMICCOUNTERBUFFERIVPROC) (GLuint program, GLuint bufferIndex, GLenum pname, GLint *params); +typedef void (APIENTRYP PFNGLBINDIMAGETEXTUREPROC) (GLuint unit, GLuint texture, GLint level, GLboolean layered, GLint layer, GLenum access, GLenum format); +typedef void (APIENTRYP PFNGLMEMORYBARRIERPROC) (GLbitfield barriers); +typedef void (APIENTRYP PFNGLTEXSTORAGE1DPROC) (GLenum target, GLsizei levels, GLenum internalformat, GLsizei width); +typedef void (APIENTRYP PFNGLTEXSTORAGE2DPROC) (GLenum target, GLsizei levels, GLenum internalformat, GLsizei width, GLsizei height); +typedef void (APIENTRYP PFNGLTEXSTORAGE3DPROC) (GLenum target, GLsizei levels, GLenum internalformat, GLsizei width, GLsizei height, GLsizei depth); +typedef void (APIENTRYP PFNGLDRAWTRANSFORMFEEDBACKINSTANCEDPROC) (GLenum mode, GLuint id, GLsizei instancecount); +typedef void (APIENTRYP PFNGLDRAWTRANSFORMFEEDBACKSTREAMINSTANCEDPROC) (GLenum mode, GLuint id, GLuint stream, GLsizei instancecount); +#ifdef GL_GLEXT_PROTOTYPES +GLAPI void APIENTRY glDrawArraysInstancedBaseInstance (GLenum mode, GLint first, GLsizei count, GLsizei instancecount, GLuint baseinstance); +GLAPI void APIENTRY glDrawElementsInstancedBaseInstance (GLenum mode, GLsizei count, GLenum type, const void *indices, GLsizei instancecount, GLuint baseinstance); +GLAPI void APIENTRY glDrawElementsInstancedBaseVertexBaseInstance (GLenum mode, GLsizei count, GLenum type, const void *indices, GLsizei instancecount, GLint basevertex, GLuint baseinstance); +GLAPI void APIENTRY glGetInternalformativ (GLenum target, GLenum internalformat, GLenum pname, GLsizei bufSize, GLint *params); +GLAPI void APIENTRY glGetActiveAtomicCounterBufferiv (GLuint program, GLuint bufferIndex, GLenum pname, GLint *params); +GLAPI void APIENTRY glBindImageTexture (GLuint unit, GLuint texture, GLint level, GLboolean layered, GLint layer, GLenum access, GLenum format); +GLAPI void APIENTRY glMemoryBarrier (GLbitfield barriers); +GLAPI void APIENTRY glTexStorage1D (GLenum target, GLsizei levels, GLenum internalformat, GLsizei width); +GLAPI void APIENTRY glTexStorage2D (GLenum target, GLsizei levels, GLenum internalformat, GLsizei width, GLsizei height); +GLAPI void APIENTRY glTexStorage3D (GLenum target, GLsizei levels, GLenum internalformat, GLsizei width, GLsizei height, GLsizei depth); +GLAPI void APIENTRY glDrawTransformFeedbackInstanced (GLenum mode, GLuint id, GLsizei instancecount); +GLAPI void APIENTRY glDrawTransformFeedbackStreamInstanced (GLenum mode, GLuint id, GLuint stream, GLsizei instancecount); +#endif +#endif /* GL_VERSION_4_2 */ + +#ifndef GL_VERSION_4_3 +#define GL_VERSION_4_3 1 +typedef void (APIENTRY *GLDEBUGPROC)(GLenum source,GLenum type,GLuint id,GLenum severity,GLsizei length,const GLchar *message,const void *userParam); +#define GL_NUM_SHADING_LANGUAGE_VERSIONS 0x82E9 +#define GL_VERTEX_ATTRIB_ARRAY_LONG 0x874E +#define GL_COMPRESSED_RGB8_ETC2 0x9274 +#define GL_COMPRESSED_SRGB8_ETC2 0x9275 +#define GL_COMPRESSED_RGB8_PUNCHTHROUGH_ALPHA1_ETC2 0x9276 +#define GL_COMPRESSED_SRGB8_PUNCHTHROUGH_ALPHA1_ETC2 0x9277 +#define GL_COMPRESSED_RGBA8_ETC2_EAC 0x9278 +#define GL_COMPRESSED_SRGB8_ALPHA8_ETC2_EAC 0x9279 +#define GL_COMPRESSED_R11_EAC 0x9270 +#define GL_COMPRESSED_SIGNED_R11_EAC 0x9271 +#define GL_COMPRESSED_RG11_EAC 0x9272 +#define GL_COMPRESSED_SIGNED_RG11_EAC 0x9273 +#define GL_PRIMITIVE_RESTART_FIXED_INDEX 0x8D69 +#define GL_ANY_SAMPLES_PASSED_CONSERVATIVE 0x8D6A +#define GL_MAX_ELEMENT_INDEX 0x8D6B +#define GL_COMPUTE_SHADER 0x91B9 +#define GL_MAX_COMPUTE_UNIFORM_BLOCKS 0x91BB +#define GL_MAX_COMPUTE_TEXTURE_IMAGE_UNITS 0x91BC +#define GL_MAX_COMPUTE_IMAGE_UNIFORMS 0x91BD +#define GL_MAX_COMPUTE_SHARED_MEMORY_SIZE 0x8262 +#define GL_MAX_COMPUTE_UNIFORM_COMPONENTS 0x8263 +#define GL_MAX_COMPUTE_ATOMIC_COUNTER_BUFFERS 0x8264 +#define GL_MAX_COMPUTE_ATOMIC_COUNTERS 0x8265 +#define GL_MAX_COMBINED_COMPUTE_UNIFORM_COMPONENTS 0x8266 +#define GL_MAX_COMPUTE_WORK_GROUP_INVOCATIONS 0x90EB +#define GL_MAX_COMPUTE_WORK_GROUP_COUNT 0x91BE +#define GL_MAX_COMPUTE_WORK_GROUP_SIZE 0x91BF +#define GL_COMPUTE_WORK_GROUP_SIZE 0x8267 +#define GL_UNIFORM_BLOCK_REFERENCED_BY_COMPUTE_SHADER 0x90EC +#define GL_ATOMIC_COUNTER_BUFFER_REFERENCED_BY_COMPUTE_SHADER 0x90ED +#define GL_DISPATCH_INDIRECT_BUFFER 0x90EE +#define GL_DISPATCH_INDIRECT_BUFFER_BINDING 0x90EF +#define GL_DEBUG_OUTPUT_SYNCHRONOUS 0x8242 +#define GL_DEBUG_NEXT_LOGGED_MESSAGE_LENGTH 0x8243 +#define GL_DEBUG_CALLBACK_FUNCTION 0x8244 +#define GL_DEBUG_CALLBACK_USER_PARAM 0x8245 +#define GL_DEBUG_SOURCE_API 0x8246 +#define GL_DEBUG_SOURCE_WINDOW_SYSTEM 0x8247 +#define GL_DEBUG_SOURCE_SHADER_COMPILER 0x8248 +#define GL_DEBUG_SOURCE_THIRD_PARTY 0x8249 +#define GL_DEBUG_SOURCE_APPLICATION 0x824A +#define GL_DEBUG_SOURCE_OTHER 0x824B +#define GL_DEBUG_TYPE_ERROR 0x824C +#define GL_DEBUG_TYPE_DEPRECATED_BEHAVIOR 0x824D +#define GL_DEBUG_TYPE_UNDEFINED_BEHAVIOR 0x824E +#define GL_DEBUG_TYPE_PORTABILITY 0x824F +#define GL_DEBUG_TYPE_PERFORMANCE 0x8250 +#define GL_DEBUG_TYPE_OTHER 0x8251 +#define GL_MAX_DEBUG_MESSAGE_LENGTH 0x9143 +#define GL_MAX_DEBUG_LOGGED_MESSAGES 0x9144 +#define GL_DEBUG_LOGGED_MESSAGES 0x9145 +#define GL_DEBUG_SEVERITY_HIGH 0x9146 +#define GL_DEBUG_SEVERITY_MEDIUM 0x9147 +#define GL_DEBUG_SEVERITY_LOW 0x9148 +#define GL_DEBUG_TYPE_MARKER 0x8268 +#define GL_DEBUG_TYPE_PUSH_GROUP 0x8269 +#define GL_DEBUG_TYPE_POP_GROUP 0x826A +#define GL_DEBUG_SEVERITY_NOTIFICATION 0x826B +#define GL_MAX_DEBUG_GROUP_STACK_DEPTH 0x826C +#define GL_DEBUG_GROUP_STACK_DEPTH 0x826D +#define GL_BUFFER 0x82E0 +#define GL_SHADER 0x82E1 +#define GL_PROGRAM 0x82E2 +#define GL_QUERY 0x82E3 +#define GL_PROGRAM_PIPELINE 0x82E4 +#define GL_SAMPLER 0x82E6 +#define GL_MAX_LABEL_LENGTH 0x82E8 +#define GL_DEBUG_OUTPUT 0x92E0 +#define GL_CONTEXT_FLAG_DEBUG_BIT 0x00000002 +#define GL_MAX_UNIFORM_LOCATIONS 0x826E +#define GL_FRAMEBUFFER_DEFAULT_WIDTH 0x9310 +#define GL_FRAMEBUFFER_DEFAULT_HEIGHT 0x9311 +#define GL_FRAMEBUFFER_DEFAULT_LAYERS 0x9312 +#define GL_FRAMEBUFFER_DEFAULT_SAMPLES 0x9313 +#define GL_FRAMEBUFFER_DEFAULT_FIXED_SAMPLE_LOCATIONS 0x9314 +#define GL_MAX_FRAMEBUFFER_WIDTH 0x9315 +#define GL_MAX_FRAMEBUFFER_HEIGHT 0x9316 +#define GL_MAX_FRAMEBUFFER_LAYERS 0x9317 +#define GL_MAX_FRAMEBUFFER_SAMPLES 0x9318 +#define GL_INTERNALFORMAT_SUPPORTED 0x826F +#define GL_INTERNALFORMAT_PREFERRED 0x8270 +#define GL_INTERNALFORMAT_RED_SIZE 0x8271 +#define GL_INTERNALFORMAT_GREEN_SIZE 0x8272 +#define GL_INTERNALFORMAT_BLUE_SIZE 0x8273 +#define GL_INTERNALFORMAT_ALPHA_SIZE 0x8274 +#define GL_INTERNALFORMAT_DEPTH_SIZE 0x8275 +#define GL_INTERNALFORMAT_STENCIL_SIZE 0x8276 +#define GL_INTERNALFORMAT_SHARED_SIZE 0x8277 +#define GL_INTERNALFORMAT_RED_TYPE 0x8278 +#define GL_INTERNALFORMAT_GREEN_TYPE 0x8279 +#define GL_INTERNALFORMAT_BLUE_TYPE 0x827A +#define GL_INTERNALFORMAT_ALPHA_TYPE 0x827B +#define GL_INTERNALFORMAT_DEPTH_TYPE 0x827C +#define GL_INTERNALFORMAT_STENCIL_TYPE 0x827D +#define GL_MAX_WIDTH 0x827E +#define GL_MAX_HEIGHT 0x827F +#define GL_MAX_DEPTH 0x8280 +#define GL_MAX_LAYERS 0x8281 +#define GL_MAX_COMBINED_DIMENSIONS 0x8282 +#define GL_COLOR_COMPONENTS 0x8283 +#define GL_DEPTH_COMPONENTS 0x8284 +#define GL_STENCIL_COMPONENTS 0x8285 +#define GL_COLOR_RENDERABLE 0x8286 +#define GL_DEPTH_RENDERABLE 0x8287 +#define GL_STENCIL_RENDERABLE 0x8288 +#define GL_FRAMEBUFFER_RENDERABLE 0x8289 +#define GL_FRAMEBUFFER_RENDERABLE_LAYERED 0x828A +#define GL_FRAMEBUFFER_BLEND 0x828B +#define GL_READ_PIXELS 0x828C +#define GL_READ_PIXELS_FORMAT 0x828D +#define GL_READ_PIXELS_TYPE 0x828E +#define GL_TEXTURE_IMAGE_FORMAT 0x828F +#define GL_TEXTURE_IMAGE_TYPE 0x8290 +#define GL_GET_TEXTURE_IMAGE_FORMAT 0x8291 +#define GL_GET_TEXTURE_IMAGE_TYPE 0x8292 +#define GL_MIPMAP 0x8293 +#define GL_MANUAL_GENERATE_MIPMAP 0x8294 +#define GL_AUTO_GENERATE_MIPMAP 0x8295 +#define GL_COLOR_ENCODING 0x8296 +#define GL_SRGB_READ 0x8297 +#define GL_SRGB_WRITE 0x8298 +#define GL_FILTER 0x829A +#define GL_VERTEX_TEXTURE 0x829B +#define GL_TESS_CONTROL_TEXTURE 0x829C +#define GL_TESS_EVALUATION_TEXTURE 0x829D +#define GL_GEOMETRY_TEXTURE 0x829E +#define GL_FRAGMENT_TEXTURE 0x829F +#define GL_COMPUTE_TEXTURE 0x82A0 +#define GL_TEXTURE_SHADOW 0x82A1 +#define GL_TEXTURE_GATHER 0x82A2 +#define GL_TEXTURE_GATHER_SHADOW 0x82A3 +#define GL_SHADER_IMAGE_LOAD 0x82A4 +#define GL_SHADER_IMAGE_STORE 0x82A5 +#define GL_SHADER_IMAGE_ATOMIC 0x82A6 +#define GL_IMAGE_TEXEL_SIZE 0x82A7 +#define GL_IMAGE_COMPATIBILITY_CLASS 0x82A8 +#define GL_IMAGE_PIXEL_FORMAT 0x82A9 +#define GL_IMAGE_PIXEL_TYPE 0x82AA +#define GL_SIMULTANEOUS_TEXTURE_AND_DEPTH_TEST 0x82AC +#define GL_SIMULTANEOUS_TEXTURE_AND_STENCIL_TEST 0x82AD +#define GL_SIMULTANEOUS_TEXTURE_AND_DEPTH_WRITE 0x82AE +#define GL_SIMULTANEOUS_TEXTURE_AND_STENCIL_WRITE 0x82AF +#define GL_TEXTURE_COMPRESSED_BLOCK_WIDTH 0x82B1 +#define GL_TEXTURE_COMPRESSED_BLOCK_HEIGHT 0x82B2 +#define GL_TEXTURE_COMPRESSED_BLOCK_SIZE 0x82B3 +#define GL_CLEAR_BUFFER 0x82B4 +#define GL_TEXTURE_VIEW 0x82B5 +#define GL_VIEW_COMPATIBILITY_CLASS 0x82B6 +#define GL_FULL_SUPPORT 0x82B7 +#define GL_CAVEAT_SUPPORT 0x82B8 +#define GL_IMAGE_CLASS_4_X_32 0x82B9 +#define GL_IMAGE_CLASS_2_X_32 0x82BA +#define GL_IMAGE_CLASS_1_X_32 0x82BB +#define GL_IMAGE_CLASS_4_X_16 0x82BC +#define GL_IMAGE_CLASS_2_X_16 0x82BD +#define GL_IMAGE_CLASS_1_X_16 0x82BE +#define GL_IMAGE_CLASS_4_X_8 0x82BF +#define GL_IMAGE_CLASS_2_X_8 0x82C0 +#define GL_IMAGE_CLASS_1_X_8 0x82C1 +#define GL_IMAGE_CLASS_11_11_10 0x82C2 +#define GL_IMAGE_CLASS_10_10_10_2 0x82C3 +#define GL_VIEW_CLASS_128_BITS 0x82C4 +#define GL_VIEW_CLASS_96_BITS 0x82C5 +#define GL_VIEW_CLASS_64_BITS 0x82C6 +#define GL_VIEW_CLASS_48_BITS 0x82C7 +#define GL_VIEW_CLASS_32_BITS 0x82C8 +#define GL_VIEW_CLASS_24_BITS 0x82C9 +#define GL_VIEW_CLASS_16_BITS 0x82CA +#define GL_VIEW_CLASS_8_BITS 0x82CB +#define GL_VIEW_CLASS_S3TC_DXT1_RGB 0x82CC +#define GL_VIEW_CLASS_S3TC_DXT1_RGBA 0x82CD +#define GL_VIEW_CLASS_S3TC_DXT3_RGBA 0x82CE +#define GL_VIEW_CLASS_S3TC_DXT5_RGBA 0x82CF +#define GL_VIEW_CLASS_RGTC1_RED 0x82D0 +#define GL_VIEW_CLASS_RGTC2_RG 0x82D1 +#define GL_VIEW_CLASS_BPTC_UNORM 0x82D2 +#define GL_VIEW_CLASS_BPTC_FLOAT 0x82D3 +#define GL_UNIFORM 0x92E1 +#define GL_UNIFORM_BLOCK 0x92E2 +#define GL_PROGRAM_INPUT 0x92E3 +#define GL_PROGRAM_OUTPUT 0x92E4 +#define GL_BUFFER_VARIABLE 0x92E5 +#define GL_SHADER_STORAGE_BLOCK 0x92E6 +#define GL_VERTEX_SUBROUTINE 0x92E8 +#define GL_TESS_CONTROL_SUBROUTINE 0x92E9 +#define GL_TESS_EVALUATION_SUBROUTINE 0x92EA +#define GL_GEOMETRY_SUBROUTINE 0x92EB +#define GL_FRAGMENT_SUBROUTINE 0x92EC +#define GL_COMPUTE_SUBROUTINE 0x92ED +#define GL_VERTEX_SUBROUTINE_UNIFORM 0x92EE +#define GL_TESS_CONTROL_SUBROUTINE_UNIFORM 0x92EF +#define GL_TESS_EVALUATION_SUBROUTINE_UNIFORM 0x92F0 +#define GL_GEOMETRY_SUBROUTINE_UNIFORM 0x92F1 +#define GL_FRAGMENT_SUBROUTINE_UNIFORM 0x92F2 +#define GL_COMPUTE_SUBROUTINE_UNIFORM 0x92F3 +#define GL_TRANSFORM_FEEDBACK_VARYING 0x92F4 +#define GL_ACTIVE_RESOURCES 0x92F5 +#define GL_MAX_NAME_LENGTH 0x92F6 +#define GL_MAX_NUM_ACTIVE_VARIABLES 0x92F7 +#define GL_MAX_NUM_COMPATIBLE_SUBROUTINES 0x92F8 +#define GL_NAME_LENGTH 0x92F9 +#define GL_TYPE 0x92FA +#define GL_ARRAY_SIZE 0x92FB +#define GL_OFFSET 0x92FC +#define GL_BLOCK_INDEX 0x92FD +#define GL_ARRAY_STRIDE 0x92FE +#define GL_MATRIX_STRIDE 0x92FF +#define GL_IS_ROW_MAJOR 0x9300 +#define GL_ATOMIC_COUNTER_BUFFER_INDEX 0x9301 +#define GL_BUFFER_BINDING 0x9302 +#define GL_BUFFER_DATA_SIZE 0x9303 +#define GL_NUM_ACTIVE_VARIABLES 0x9304 +#define GL_ACTIVE_VARIABLES 0x9305 +#define GL_REFERENCED_BY_VERTEX_SHADER 0x9306 +#define GL_REFERENCED_BY_TESS_CONTROL_SHADER 0x9307 +#define GL_REFERENCED_BY_TESS_EVALUATION_SHADER 0x9308 +#define GL_REFERENCED_BY_GEOMETRY_SHADER 0x9309 +#define GL_REFERENCED_BY_FRAGMENT_SHADER 0x930A +#define GL_REFERENCED_BY_COMPUTE_SHADER 0x930B +#define GL_TOP_LEVEL_ARRAY_SIZE 0x930C +#define GL_TOP_LEVEL_ARRAY_STRIDE 0x930D +#define GL_LOCATION 0x930E +#define GL_LOCATION_INDEX 0x930F +#define GL_IS_PER_PATCH 0x92E7 +#define GL_SHADER_STORAGE_BUFFER 0x90D2 +#define GL_SHADER_STORAGE_BUFFER_BINDING 0x90D3 +#define GL_SHADER_STORAGE_BUFFER_START 0x90D4 +#define GL_SHADER_STORAGE_BUFFER_SIZE 0x90D5 +#define GL_MAX_VERTEX_SHADER_STORAGE_BLOCKS 0x90D6 +#define GL_MAX_GEOMETRY_SHADER_STORAGE_BLOCKS 0x90D7 +#define GL_MAX_TESS_CONTROL_SHADER_STORAGE_BLOCKS 0x90D8 +#define GL_MAX_TESS_EVALUATION_SHADER_STORAGE_BLOCKS 0x90D9 +#define GL_MAX_FRAGMENT_SHADER_STORAGE_BLOCKS 0x90DA +#define GL_MAX_COMPUTE_SHADER_STORAGE_BLOCKS 0x90DB +#define GL_MAX_COMBINED_SHADER_STORAGE_BLOCKS 0x90DC +#define GL_MAX_SHADER_STORAGE_BUFFER_BINDINGS 0x90DD +#define GL_MAX_SHADER_STORAGE_BLOCK_SIZE 0x90DE +#define GL_SHADER_STORAGE_BUFFER_OFFSET_ALIGNMENT 0x90DF +#define GL_SHADER_STORAGE_BARRIER_BIT 0x00002000 +#define GL_MAX_COMBINED_SHADER_OUTPUT_RESOURCES 0x8F39 +#define GL_DEPTH_STENCIL_TEXTURE_MODE 0x90EA +#define GL_TEXTURE_BUFFER_OFFSET 0x919D +#define GL_TEXTURE_BUFFER_SIZE 0x919E +#define GL_TEXTURE_BUFFER_OFFSET_ALIGNMENT 0x919F +#define GL_TEXTURE_VIEW_MIN_LEVEL 0x82DB +#define GL_TEXTURE_VIEW_NUM_LEVELS 0x82DC +#define GL_TEXTURE_VIEW_MIN_LAYER 0x82DD +#define GL_TEXTURE_VIEW_NUM_LAYERS 0x82DE +#define GL_TEXTURE_IMMUTABLE_LEVELS 0x82DF +#define GL_VERTEX_ATTRIB_BINDING 0x82D4 +#define GL_VERTEX_ATTRIB_RELATIVE_OFFSET 0x82D5 +#define GL_VERTEX_BINDING_DIVISOR 0x82D6 +#define GL_VERTEX_BINDING_OFFSET 0x82D7 +#define GL_VERTEX_BINDING_STRIDE 0x82D8 +#define GL_MAX_VERTEX_ATTRIB_RELATIVE_OFFSET 0x82D9 +#define GL_MAX_VERTEX_ATTRIB_BINDINGS 0x82DA +#define GL_DISPLAY_LIST 0x82E7 +typedef void (APIENTRYP PFNGLCLEARBUFFERDATAPROC) (GLenum target, GLenum internalformat, GLenum format, GLenum type, const void *data); +typedef void (APIENTRYP PFNGLCLEARBUFFERSUBDATAPROC) (GLenum target, GLenum internalformat, GLintptr offset, GLsizeiptr size, GLenum format, GLenum type, const void *data); +typedef void (APIENTRYP PFNGLDISPATCHCOMPUTEPROC) (GLuint num_groups_x, GLuint num_groups_y, GLuint num_groups_z); +typedef void (APIENTRYP PFNGLDISPATCHCOMPUTEINDIRECTPROC) (GLintptr indirect); +typedef void (APIENTRYP PFNGLCOPYIMAGESUBDATAPROC) (GLuint srcName, GLenum srcTarget, GLint srcLevel, GLint srcX, GLint srcY, GLint srcZ, GLuint dstName, GLenum dstTarget, GLint dstLevel, GLint dstX, GLint dstY, GLint dstZ, GLsizei srcWidth, GLsizei srcHeight, GLsizei srcDepth); +typedef void (APIENTRYP PFNGLFRAMEBUFFERPARAMETERIPROC) (GLenum target, GLenum pname, GLint param); +typedef void (APIENTRYP PFNGLGETFRAMEBUFFERPARAMETERIVPROC) (GLenum target, GLenum pname, GLint *params); +typedef void (APIENTRYP PFNGLGETINTERNALFORMATI64VPROC) (GLenum target, GLenum internalformat, GLenum pname, GLsizei bufSize, GLint64 *params); +typedef void (APIENTRYP PFNGLINVALIDATETEXSUBIMAGEPROC) (GLuint texture, GLint level, GLint xoffset, GLint yoffset, GLint zoffset, GLsizei width, GLsizei height, GLsizei depth); +typedef void (APIENTRYP PFNGLINVALIDATETEXIMAGEPROC) (GLuint texture, GLint level); +typedef void (APIENTRYP PFNGLINVALIDATEBUFFERSUBDATAPROC) (GLuint buffer, GLintptr offset, GLsizeiptr length); +typedef void (APIENTRYP PFNGLINVALIDATEBUFFERDATAPROC) (GLuint buffer); +typedef void (APIENTRYP PFNGLINVALIDATEFRAMEBUFFERPROC) (GLenum target, GLsizei numAttachments, const GLenum *attachments); +typedef void (APIENTRYP PFNGLINVALIDATESUBFRAMEBUFFERPROC) (GLenum target, GLsizei numAttachments, const GLenum *attachments, GLint x, GLint y, GLsizei width, GLsizei height); +typedef void (APIENTRYP PFNGLMULTIDRAWARRAYSINDIRECTPROC) (GLenum mode, const void *indirect, GLsizei drawcount, GLsizei stride); +typedef void (APIENTRYP PFNGLMULTIDRAWELEMENTSINDIRECTPROC) (GLenum mode, GLenum type, const void *indirect, GLsizei drawcount, GLsizei stride); +typedef void (APIENTRYP PFNGLGETPROGRAMINTERFACEIVPROC) (GLuint program, GLenum programInterface, GLenum pname, GLint *params); +typedef GLuint (APIENTRYP PFNGLGETPROGRAMRESOURCEINDEXPROC) (GLuint program, GLenum programInterface, const GLchar *name); +typedef void (APIENTRYP PFNGLGETPROGRAMRESOURCENAMEPROC) (GLuint program, GLenum programInterface, GLuint index, GLsizei bufSize, GLsizei *length, GLchar *name); +typedef void (APIENTRYP PFNGLGETPROGRAMRESOURCEIVPROC) (GLuint program, GLenum programInterface, GLuint index, GLsizei propCount, const GLenum *props, GLsizei bufSize, GLsizei *length, GLint *params); +typedef GLint (APIENTRYP PFNGLGETPROGRAMRESOURCELOCATIONPROC) (GLuint program, GLenum programInterface, const GLchar *name); +typedef GLint (APIENTRYP PFNGLGETPROGRAMRESOURCELOCATIONINDEXPROC) (GLuint program, GLenum programInterface, const GLchar *name); +typedef void (APIENTRYP PFNGLSHADERSTORAGEBLOCKBINDINGPROC) (GLuint program, GLuint storageBlockIndex, GLuint storageBlockBinding); +typedef void (APIENTRYP PFNGLTEXBUFFERRANGEPROC) (GLenum target, GLenum internalformat, GLuint buffer, GLintptr offset, GLsizeiptr size); +typedef void (APIENTRYP PFNGLTEXSTORAGE2DMULTISAMPLEPROC) (GLenum target, GLsizei samples, GLenum internalformat, GLsizei width, GLsizei height, GLboolean fixedsamplelocations); +typedef void (APIENTRYP PFNGLTEXSTORAGE3DMULTISAMPLEPROC) (GLenum target, GLsizei samples, GLenum internalformat, GLsizei width, GLsizei height, GLsizei depth, GLboolean fixedsamplelocations); +typedef void (APIENTRYP PFNGLTEXTUREVIEWPROC) (GLuint texture, GLenum target, GLuint origtexture, GLenum internalformat, GLuint minlevel, GLuint numlevels, GLuint minlayer, GLuint numlayers); +typedef void (APIENTRYP PFNGLBINDVERTEXBUFFERPROC) (GLuint bindingindex, GLuint buffer, GLintptr offset, GLsizei stride); +typedef void (APIENTRYP PFNGLVERTEXATTRIBFORMATPROC) (GLuint attribindex, GLint size, GLenum type, GLboolean normalized, GLuint relativeoffset); +typedef void (APIENTRYP PFNGLVERTEXATTRIBIFORMATPROC) (GLuint attribindex, GLint size, GLenum type, GLuint relativeoffset); +typedef void (APIENTRYP PFNGLVERTEXATTRIBLFORMATPROC) (GLuint attribindex, GLint size, GLenum type, GLuint relativeoffset); +typedef void (APIENTRYP PFNGLVERTEXATTRIBBINDINGPROC) (GLuint attribindex, GLuint bindingindex); +typedef void (APIENTRYP PFNGLVERTEXBINDINGDIVISORPROC) (GLuint bindingindex, GLuint divisor); +typedef void (APIENTRYP PFNGLDEBUGMESSAGECONTROLPROC) (GLenum source, GLenum type, GLenum severity, GLsizei count, const GLuint *ids, GLboolean enabled); +typedef void (APIENTRYP PFNGLDEBUGMESSAGEINSERTPROC) (GLenum source, GLenum type, GLuint id, GLenum severity, GLsizei length, const GLchar *buf); +typedef void (APIENTRYP PFNGLDEBUGMESSAGECALLBACKPROC) (GLDEBUGPROC callback, const void *userParam); +typedef GLuint (APIENTRYP PFNGLGETDEBUGMESSAGELOGPROC) (GLuint count, GLsizei bufSize, GLenum *sources, GLenum *types, GLuint *ids, GLenum *severities, GLsizei *lengths, GLchar *messageLog); +typedef void (APIENTRYP PFNGLPUSHDEBUGGROUPPROC) (GLenum source, GLuint id, GLsizei length, const GLchar *message); +typedef void (APIENTRYP PFNGLPOPDEBUGGROUPPROC) (void); +typedef void (APIENTRYP PFNGLOBJECTLABELPROC) (GLenum identifier, GLuint name, GLsizei length, const GLchar *label); +typedef void (APIENTRYP PFNGLGETOBJECTLABELPROC) (GLenum identifier, GLuint name, GLsizei bufSize, GLsizei *length, GLchar *label); +typedef void (APIENTRYP PFNGLOBJECTPTRLABELPROC) (const void *ptr, GLsizei length, const GLchar *label); +typedef void (APIENTRYP PFNGLGETOBJECTPTRLABELPROC) (const void *ptr, GLsizei bufSize, GLsizei *length, GLchar *label); +#ifdef GL_GLEXT_PROTOTYPES +GLAPI void APIENTRY glClearBufferData (GLenum target, GLenum internalformat, GLenum format, GLenum type, const void *data); +GLAPI void APIENTRY glClearBufferSubData (GLenum target, GLenum internalformat, GLintptr offset, GLsizeiptr size, GLenum format, GLenum type, const void *data); +GLAPI void APIENTRY glDispatchCompute (GLuint num_groups_x, GLuint num_groups_y, GLuint num_groups_z); +GLAPI void APIENTRY glDispatchComputeIndirect (GLintptr indirect); +GLAPI void APIENTRY glCopyImageSubData (GLuint srcName, GLenum srcTarget, GLint srcLevel, GLint srcX, GLint srcY, GLint srcZ, GLuint dstName, GLenum dstTarget, GLint dstLevel, GLint dstX, GLint dstY, GLint dstZ, GLsizei srcWidth, GLsizei srcHeight, GLsizei srcDepth); +GLAPI void APIENTRY glFramebufferParameteri (GLenum target, GLenum pname, GLint param); +GLAPI void APIENTRY glGetFramebufferParameteriv (GLenum target, GLenum pname, GLint *params); +GLAPI void APIENTRY glGetInternalformati64v (GLenum target, GLenum internalformat, GLenum pname, GLsizei bufSize, GLint64 *params); +GLAPI void APIENTRY glInvalidateTexSubImage (GLuint texture, GLint level, GLint xoffset, GLint yoffset, GLint zoffset, GLsizei width, GLsizei height, GLsizei depth); +GLAPI void APIENTRY glInvalidateTexImage (GLuint texture, GLint level); +GLAPI void APIENTRY glInvalidateBufferSubData (GLuint buffer, GLintptr offset, GLsizeiptr length); +GLAPI void APIENTRY glInvalidateBufferData (GLuint buffer); +GLAPI void APIENTRY glInvalidateFramebuffer (GLenum target, GLsizei numAttachments, const GLenum *attachments); +GLAPI void APIENTRY glInvalidateSubFramebuffer (GLenum target, GLsizei numAttachments, const GLenum *attachments, GLint x, GLint y, GLsizei width, GLsizei height); +GLAPI void APIENTRY glMultiDrawArraysIndirect (GLenum mode, const void *indirect, GLsizei drawcount, GLsizei stride); +GLAPI void APIENTRY glMultiDrawElementsIndirect (GLenum mode, GLenum type, const void *indirect, GLsizei drawcount, GLsizei stride); +GLAPI void APIENTRY glGetProgramInterfaceiv (GLuint program, GLenum programInterface, GLenum pname, GLint *params); +GLAPI GLuint APIENTRY glGetProgramResourceIndex (GLuint program, GLenum programInterface, const GLchar *name); +GLAPI void APIENTRY glGetProgramResourceName (GLuint program, GLenum programInterface, GLuint index, GLsizei bufSize, GLsizei *length, GLchar *name); +GLAPI void APIENTRY glGetProgramResourceiv (GLuint program, GLenum programInterface, GLuint index, GLsizei propCount, const GLenum *props, GLsizei bufSize, GLsizei *length, GLint *params); +GLAPI GLint APIENTRY glGetProgramResourceLocation (GLuint program, GLenum programInterface, const GLchar *name); +GLAPI GLint APIENTRY glGetProgramResourceLocationIndex (GLuint program, GLenum programInterface, const GLchar *name); +GLAPI void APIENTRY glShaderStorageBlockBinding (GLuint program, GLuint storageBlockIndex, GLuint storageBlockBinding); +GLAPI void APIENTRY glTexBufferRange (GLenum target, GLenum internalformat, GLuint buffer, GLintptr offset, GLsizeiptr size); +GLAPI void APIENTRY glTexStorage2DMultisample (GLenum target, GLsizei samples, GLenum internalformat, GLsizei width, GLsizei height, GLboolean fixedsamplelocations); +GLAPI void APIENTRY glTexStorage3DMultisample (GLenum target, GLsizei samples, GLenum internalformat, GLsizei width, GLsizei height, GLsizei depth, GLboolean fixedsamplelocations); +GLAPI void APIENTRY glTextureView (GLuint texture, GLenum target, GLuint origtexture, GLenum internalformat, GLuint minlevel, GLuint numlevels, GLuint minlayer, GLuint numlayers); +GLAPI void APIENTRY glBindVertexBuffer (GLuint bindingindex, GLuint buffer, GLintptr offset, GLsizei stride); +GLAPI void APIENTRY glVertexAttribFormat (GLuint attribindex, GLint size, GLenum type, GLboolean normalized, GLuint relativeoffset); +GLAPI void APIENTRY glVertexAttribIFormat (GLuint attribindex, GLint size, GLenum type, GLuint relativeoffset); +GLAPI void APIENTRY glVertexAttribLFormat (GLuint attribindex, GLint size, GLenum type, GLuint relativeoffset); +GLAPI void APIENTRY glVertexAttribBinding (GLuint attribindex, GLuint bindingindex); +GLAPI void APIENTRY glVertexBindingDivisor (GLuint bindingindex, GLuint divisor); +GLAPI void APIENTRY glDebugMessageControl (GLenum source, GLenum type, GLenum severity, GLsizei count, const GLuint *ids, GLboolean enabled); +GLAPI void APIENTRY glDebugMessageInsert (GLenum source, GLenum type, GLuint id, GLenum severity, GLsizei length, const GLchar *buf); +GLAPI void APIENTRY glDebugMessageCallback (GLDEBUGPROC callback, const void *userParam); +GLAPI GLuint APIENTRY glGetDebugMessageLog (GLuint count, GLsizei bufSize, GLenum *sources, GLenum *types, GLuint *ids, GLenum *severities, GLsizei *lengths, GLchar *messageLog); +GLAPI void APIENTRY glPushDebugGroup (GLenum source, GLuint id, GLsizei length, const GLchar *message); +GLAPI void APIENTRY glPopDebugGroup (void); +GLAPI void APIENTRY glObjectLabel (GLenum identifier, GLuint name, GLsizei length, const GLchar *label); +GLAPI void APIENTRY glGetObjectLabel (GLenum identifier, GLuint name, GLsizei bufSize, GLsizei *length, GLchar *label); +GLAPI void APIENTRY glObjectPtrLabel (const void *ptr, GLsizei length, const GLchar *label); +GLAPI void APIENTRY glGetObjectPtrLabel (const void *ptr, GLsizei bufSize, GLsizei *length, GLchar *label); +#endif +#endif /* GL_VERSION_4_3 */ + +#ifndef GL_VERSION_4_4 +#define GL_VERSION_4_4 1 +#define GL_MAX_VERTEX_ATTRIB_STRIDE 0x82E5 +#define GL_PRIMITIVE_RESTART_FOR_PATCHES_SUPPORTED 0x8221 +#define GL_TEXTURE_BUFFER_BINDING 0x8C2A +#define GL_MAP_PERSISTENT_BIT 0x0040 +#define GL_MAP_COHERENT_BIT 0x0080 +#define GL_DYNAMIC_STORAGE_BIT 0x0100 +#define GL_CLIENT_STORAGE_BIT 0x0200 +#define GL_CLIENT_MAPPED_BUFFER_BARRIER_BIT 0x00004000 +#define GL_BUFFER_IMMUTABLE_STORAGE 0x821F +#define GL_BUFFER_STORAGE_FLAGS 0x8220 +#define GL_CLEAR_TEXTURE 0x9365 +#define GL_LOCATION_COMPONENT 0x934A +#define GL_TRANSFORM_FEEDBACK_BUFFER_INDEX 0x934B +#define GL_TRANSFORM_FEEDBACK_BUFFER_STRIDE 0x934C +#define GL_QUERY_BUFFER 0x9192 +#define GL_QUERY_BUFFER_BARRIER_BIT 0x00008000 +#define GL_QUERY_BUFFER_BINDING 0x9193 +#define GL_QUERY_RESULT_NO_WAIT 0x9194 +#define GL_MIRROR_CLAMP_TO_EDGE 0x8743 +typedef void (APIENTRYP PFNGLBUFFERSTORAGEPROC) (GLenum target, GLsizeiptr size, const void *data, GLbitfield flags); +typedef void (APIENTRYP PFNGLCLEARTEXIMAGEPROC) (GLuint texture, GLint level, GLenum format, GLenum type, const void *data); +typedef void (APIENTRYP PFNGLCLEARTEXSUBIMAGEPROC) (GLuint texture, GLint level, GLint xoffset, GLint yoffset, GLint zoffset, GLsizei width, GLsizei height, GLsizei depth, GLenum format, GLenum type, const void *data); +typedef void (APIENTRYP PFNGLBINDBUFFERSBASEPROC) (GLenum target, GLuint first, GLsizei count, const GLuint *buffers); +typedef void (APIENTRYP PFNGLBINDBUFFERSRANGEPROC) (GLenum target, GLuint first, GLsizei count, const GLuint *buffers, const GLintptr *offsets, const GLsizeiptr *sizes); +typedef void (APIENTRYP PFNGLBINDTEXTURESPROC) (GLuint first, GLsizei count, const GLuint *textures); +typedef void (APIENTRYP PFNGLBINDSAMPLERSPROC) (GLuint first, GLsizei count, const GLuint *samplers); +typedef void (APIENTRYP PFNGLBINDIMAGETEXTURESPROC) (GLuint first, GLsizei count, const GLuint *textures); +typedef void (APIENTRYP PFNGLBINDVERTEXBUFFERSPROC) (GLuint first, GLsizei count, const GLuint *buffers, const GLintptr *offsets, const GLsizei *strides); +#ifdef GL_GLEXT_PROTOTYPES +GLAPI void APIENTRY glBufferStorage (GLenum target, GLsizeiptr size, const void *data, GLbitfield flags); +GLAPI void APIENTRY glClearTexImage (GLuint texture, GLint level, GLenum format, GLenum type, const void *data); +GLAPI void APIENTRY glClearTexSubImage (GLuint texture, GLint level, GLint xoffset, GLint yoffset, GLint zoffset, GLsizei width, GLsizei height, GLsizei depth, GLenum format, GLenum type, const void *data); +GLAPI void APIENTRY glBindBuffersBase (GLenum target, GLuint first, GLsizei count, const GLuint *buffers); +GLAPI void APIENTRY glBindBuffersRange (GLenum target, GLuint first, GLsizei count, const GLuint *buffers, const GLintptr *offsets, const GLsizeiptr *sizes); +GLAPI void APIENTRY glBindTextures (GLuint first, GLsizei count, const GLuint *textures); +GLAPI void APIENTRY glBindSamplers (GLuint first, GLsizei count, const GLuint *samplers); +GLAPI void APIENTRY glBindImageTextures (GLuint first, GLsizei count, const GLuint *textures); +GLAPI void APIENTRY glBindVertexBuffers (GLuint first, GLsizei count, const GLuint *buffers, const GLintptr *offsets, const GLsizei *strides); +#endif +#endif /* GL_VERSION_4_4 */ + +#ifndef GL_ARB_ES2_compatibility +#define GL_ARB_ES2_compatibility 1 +#endif /* GL_ARB_ES2_compatibility */ + +#ifndef GL_ARB_ES3_compatibility +#define GL_ARB_ES3_compatibility 1 +#endif /* GL_ARB_ES3_compatibility */ + +#ifndef GL_ARB_arrays_of_arrays +#define GL_ARB_arrays_of_arrays 1 +#endif /* GL_ARB_arrays_of_arrays */ + +#ifndef GL_ARB_base_instance +#define GL_ARB_base_instance 1 +#endif /* GL_ARB_base_instance */ + +#ifndef GL_ARB_bindless_texture +#define GL_ARB_bindless_texture 1 +typedef uint64_t GLuint64EXT; +#define GL_UNSIGNED_INT64_ARB 0x140F +typedef GLuint64 (APIENTRYP PFNGLGETTEXTUREHANDLEARBPROC) (GLuint texture); +typedef GLuint64 (APIENTRYP PFNGLGETTEXTURESAMPLERHANDLEARBPROC) (GLuint texture, GLuint sampler); +typedef void (APIENTRYP PFNGLMAKETEXTUREHANDLERESIDENTARBPROC) (GLuint64 handle); +typedef void (APIENTRYP PFNGLMAKETEXTUREHANDLENONRESIDENTARBPROC) (GLuint64 handle); +typedef GLuint64 (APIENTRYP PFNGLGETIMAGEHANDLEARBPROC) (GLuint texture, GLint level, GLboolean layered, GLint layer, GLenum format); +typedef void (APIENTRYP PFNGLMAKEIMAGEHANDLERESIDENTARBPROC) (GLuint64 handle, GLenum access); +typedef void (APIENTRYP PFNGLMAKEIMAGEHANDLENONRESIDENTARBPROC) (GLuint64 handle); +typedef void (APIENTRYP PFNGLUNIFORMHANDLEUI64ARBPROC) (GLint location, GLuint64 value); +typedef void (APIENTRYP PFNGLUNIFORMHANDLEUI64VARBPROC) (GLint location, GLsizei count, const GLuint64 *value); +typedef void (APIENTRYP PFNGLPROGRAMUNIFORMHANDLEUI64ARBPROC) (GLuint program, GLint location, GLuint64 value); +typedef void (APIENTRYP PFNGLPROGRAMUNIFORMHANDLEUI64VARBPROC) (GLuint program, GLint location, GLsizei count, const GLuint64 *values); +typedef GLboolean (APIENTRYP PFNGLISTEXTUREHANDLERESIDENTARBPROC) (GLuint64 handle); +typedef GLboolean (APIENTRYP PFNGLISIMAGEHANDLERESIDENTARBPROC) (GLuint64 handle); +typedef void (APIENTRYP PFNGLVERTEXATTRIBL1UI64ARBPROC) (GLuint index, GLuint64EXT x); +typedef void (APIENTRYP PFNGLVERTEXATTRIBL1UI64VARBPROC) (GLuint index, const GLuint64EXT *v); +typedef void (APIENTRYP PFNGLGETVERTEXATTRIBLUI64VARBPROC) (GLuint index, GLenum pname, GLuint64EXT *params); +#ifdef GL_GLEXT_PROTOTYPES +GLAPI GLuint64 APIENTRY glGetTextureHandleARB (GLuint texture); +GLAPI GLuint64 APIENTRY glGetTextureSamplerHandleARB (GLuint texture, GLuint sampler); +GLAPI void APIENTRY glMakeTextureHandleResidentARB (GLuint64 handle); +GLAPI void APIENTRY glMakeTextureHandleNonResidentARB (GLuint64 handle); +GLAPI GLuint64 APIENTRY glGetImageHandleARB (GLuint texture, GLint level, GLboolean layered, GLint layer, GLenum format); +GLAPI void APIENTRY glMakeImageHandleResidentARB (GLuint64 handle, GLenum access); +GLAPI void APIENTRY glMakeImageHandleNonResidentARB (GLuint64 handle); +GLAPI void APIENTRY glUniformHandleui64ARB (GLint location, GLuint64 value); +GLAPI void APIENTRY glUniformHandleui64vARB (GLint location, GLsizei count, const GLuint64 *value); +GLAPI void APIENTRY glProgramUniformHandleui64ARB (GLuint program, GLint location, GLuint64 value); +GLAPI void APIENTRY glProgramUniformHandleui64vARB (GLuint program, GLint location, GLsizei count, const GLuint64 *values); +GLAPI GLboolean APIENTRY glIsTextureHandleResidentARB (GLuint64 handle); +GLAPI GLboolean APIENTRY glIsImageHandleResidentARB (GLuint64 handle); +GLAPI void APIENTRY glVertexAttribL1ui64ARB (GLuint index, GLuint64EXT x); +GLAPI void APIENTRY glVertexAttribL1ui64vARB (GLuint index, const GLuint64EXT *v); +GLAPI void APIENTRY glGetVertexAttribLui64vARB (GLuint index, GLenum pname, GLuint64EXT *params); +#endif +#endif /* GL_ARB_bindless_texture */ + +#ifndef GL_ARB_blend_func_extended +#define GL_ARB_blend_func_extended 1 +#endif /* GL_ARB_blend_func_extended */ + +#ifndef GL_ARB_buffer_storage +#define GL_ARB_buffer_storage 1 +#endif /* GL_ARB_buffer_storage */ + +#ifndef GL_ARB_cl_event +#define GL_ARB_cl_event 1 +struct _cl_context; +struct _cl_event; +#define GL_SYNC_CL_EVENT_ARB 0x8240 +#define GL_SYNC_CL_EVENT_COMPLETE_ARB 0x8241 +typedef GLsync (APIENTRYP PFNGLCREATESYNCFROMCLEVENTARBPROC) (struct _cl_context *context, struct _cl_event *event, GLbitfield flags); +#ifdef GL_GLEXT_PROTOTYPES +GLAPI GLsync APIENTRY glCreateSyncFromCLeventARB (struct _cl_context *context, struct _cl_event *event, GLbitfield flags); +#endif +#endif /* GL_ARB_cl_event */ + +#ifndef GL_ARB_clear_buffer_object +#define GL_ARB_clear_buffer_object 1 +#endif /* GL_ARB_clear_buffer_object */ + +#ifndef GL_ARB_clear_texture +#define GL_ARB_clear_texture 1 +#endif /* GL_ARB_clear_texture */ + +#ifndef GL_ARB_color_buffer_float +#define GL_ARB_color_buffer_float 1 +#define GL_RGBA_FLOAT_MODE_ARB 0x8820 +#define GL_CLAMP_VERTEX_COLOR_ARB 0x891A +#define GL_CLAMP_FRAGMENT_COLOR_ARB 0x891B +#define GL_CLAMP_READ_COLOR_ARB 0x891C +#define GL_FIXED_ONLY_ARB 0x891D +typedef void (APIENTRYP PFNGLCLAMPCOLORARBPROC) (GLenum target, GLenum clamp); +#ifdef GL_GLEXT_PROTOTYPES +GLAPI void APIENTRY glClampColorARB (GLenum target, GLenum clamp); +#endif +#endif /* GL_ARB_color_buffer_float */ + +#ifndef GL_ARB_compatibility +#define GL_ARB_compatibility 1 +#endif /* GL_ARB_compatibility */ + +#ifndef GL_ARB_compressed_texture_pixel_storage +#define GL_ARB_compressed_texture_pixel_storage 1 +#endif /* GL_ARB_compressed_texture_pixel_storage */ + +#ifndef GL_ARB_compute_shader +#define GL_ARB_compute_shader 1 +#define GL_COMPUTE_SHADER_BIT 0x00000020 +#endif /* GL_ARB_compute_shader */ + +#ifndef GL_ARB_compute_variable_group_size +#define GL_ARB_compute_variable_group_size 1 +#define GL_MAX_COMPUTE_VARIABLE_GROUP_INVOCATIONS_ARB 0x9344 +#define GL_MAX_COMPUTE_FIXED_GROUP_INVOCATIONS_ARB 0x90EB +#define GL_MAX_COMPUTE_VARIABLE_GROUP_SIZE_ARB 0x9345 +#define GL_MAX_COMPUTE_FIXED_GROUP_SIZE_ARB 0x91BF +typedef void (APIENTRYP PFNGLDISPATCHCOMPUTEGROUPSIZEARBPROC) (GLuint num_groups_x, GLuint num_groups_y, GLuint num_groups_z, GLuint group_size_x, GLuint group_size_y, GLuint group_size_z); +#ifdef GL_GLEXT_PROTOTYPES +GLAPI void APIENTRY glDispatchComputeGroupSizeARB (GLuint num_groups_x, GLuint num_groups_y, GLuint num_groups_z, GLuint group_size_x, GLuint group_size_y, GLuint group_size_z); +#endif +#endif /* GL_ARB_compute_variable_group_size */ + +#ifndef GL_ARB_conservative_depth +#define GL_ARB_conservative_depth 1 +#endif /* GL_ARB_conservative_depth */ + +#ifndef GL_ARB_copy_buffer +#define GL_ARB_copy_buffer 1 +#define GL_COPY_READ_BUFFER_BINDING 0x8F36 +#define GL_COPY_WRITE_BUFFER_BINDING 0x8F37 +#endif /* GL_ARB_copy_buffer */ + +#ifndef GL_ARB_copy_image +#define GL_ARB_copy_image 1 +#endif /* GL_ARB_copy_image */ + +#ifndef GL_ARB_debug_output +#define GL_ARB_debug_output 1 +typedef void (APIENTRY *GLDEBUGPROCARB)(GLenum source,GLenum type,GLuint id,GLenum severity,GLsizei length,const GLchar *message,const void *userParam); +#define GL_DEBUG_OUTPUT_SYNCHRONOUS_ARB 0x8242 +#define GL_DEBUG_NEXT_LOGGED_MESSAGE_LENGTH_ARB 0x8243 +#define GL_DEBUG_CALLBACK_FUNCTION_ARB 0x8244 +#define GL_DEBUG_CALLBACK_USER_PARAM_ARB 0x8245 +#define GL_DEBUG_SOURCE_API_ARB 0x8246 +#define GL_DEBUG_SOURCE_WINDOW_SYSTEM_ARB 0x8247 +#define GL_DEBUG_SOURCE_SHADER_COMPILER_ARB 0x8248 +#define GL_DEBUG_SOURCE_THIRD_PARTY_ARB 0x8249 +#define GL_DEBUG_SOURCE_APPLICATION_ARB 0x824A +#define GL_DEBUG_SOURCE_OTHER_ARB 0x824B +#define GL_DEBUG_TYPE_ERROR_ARB 0x824C +#define GL_DEBUG_TYPE_DEPRECATED_BEHAVIOR_ARB 0x824D +#define GL_DEBUG_TYPE_UNDEFINED_BEHAVIOR_ARB 0x824E +#define GL_DEBUG_TYPE_PORTABILITY_ARB 0x824F +#define GL_DEBUG_TYPE_PERFORMANCE_ARB 0x8250 +#define GL_DEBUG_TYPE_OTHER_ARB 0x8251 +#define GL_MAX_DEBUG_MESSAGE_LENGTH_ARB 0x9143 +#define GL_MAX_DEBUG_LOGGED_MESSAGES_ARB 0x9144 +#define GL_DEBUG_LOGGED_MESSAGES_ARB 0x9145 +#define GL_DEBUG_SEVERITY_HIGH_ARB 0x9146 +#define GL_DEBUG_SEVERITY_MEDIUM_ARB 0x9147 +#define GL_DEBUG_SEVERITY_LOW_ARB 0x9148 +typedef void (APIENTRYP PFNGLDEBUGMESSAGECONTROLARBPROC) (GLenum source, GLenum type, GLenum severity, GLsizei count, const GLuint *ids, GLboolean enabled); +typedef void (APIENTRYP PFNGLDEBUGMESSAGEINSERTARBPROC) (GLenum source, GLenum type, GLuint id, GLenum severity, GLsizei length, const GLchar *buf); +typedef void (APIENTRYP PFNGLDEBUGMESSAGECALLBACKARBPROC) (GLDEBUGPROCARB callback, const void *userParam); +typedef GLuint (APIENTRYP PFNGLGETDEBUGMESSAGELOGARBPROC) (GLuint count, GLsizei bufSize, GLenum *sources, GLenum *types, GLuint *ids, GLenum *severities, GLsizei *lengths, GLchar *messageLog); +#ifdef GL_GLEXT_PROTOTYPES +GLAPI void APIENTRY glDebugMessageControlARB (GLenum source, GLenum type, GLenum severity, GLsizei count, const GLuint *ids, GLboolean enabled); +GLAPI void APIENTRY glDebugMessageInsertARB (GLenum source, GLenum type, GLuint id, GLenum severity, GLsizei length, const GLchar *buf); +GLAPI void APIENTRY glDebugMessageCallbackARB (GLDEBUGPROCARB callback, const void *userParam); +GLAPI GLuint APIENTRY glGetDebugMessageLogARB (GLuint count, GLsizei bufSize, GLenum *sources, GLenum *types, GLuint *ids, GLenum *severities, GLsizei *lengths, GLchar *messageLog); +#endif +#endif /* GL_ARB_debug_output */ + +#ifndef GL_ARB_depth_buffer_float +#define GL_ARB_depth_buffer_float 1 +#endif /* GL_ARB_depth_buffer_float */ + +#ifndef GL_ARB_depth_clamp +#define GL_ARB_depth_clamp 1 +#endif /* GL_ARB_depth_clamp */ + +#ifndef GL_ARB_depth_texture +#define GL_ARB_depth_texture 1 +#define GL_DEPTH_COMPONENT16_ARB 0x81A5 +#define GL_DEPTH_COMPONENT24_ARB 0x81A6 +#define GL_DEPTH_COMPONENT32_ARB 0x81A7 +#define GL_TEXTURE_DEPTH_SIZE_ARB 0x884A +#define GL_DEPTH_TEXTURE_MODE_ARB 0x884B +#endif /* GL_ARB_depth_texture */ + +#ifndef GL_ARB_draw_buffers +#define GL_ARB_draw_buffers 1 +#define GL_MAX_DRAW_BUFFERS_ARB 0x8824 +#define GL_DRAW_BUFFER0_ARB 0x8825 +#define GL_DRAW_BUFFER1_ARB 0x8826 +#define GL_DRAW_BUFFER2_ARB 0x8827 +#define GL_DRAW_BUFFER3_ARB 0x8828 +#define GL_DRAW_BUFFER4_ARB 0x8829 +#define GL_DRAW_BUFFER5_ARB 0x882A +#define GL_DRAW_BUFFER6_ARB 0x882B +#define GL_DRAW_BUFFER7_ARB 0x882C +#define GL_DRAW_BUFFER8_ARB 0x882D +#define GL_DRAW_BUFFER9_ARB 0x882E +#define GL_DRAW_BUFFER10_ARB 0x882F +#define GL_DRAW_BUFFER11_ARB 0x8830 +#define GL_DRAW_BUFFER12_ARB 0x8831 +#define GL_DRAW_BUFFER13_ARB 0x8832 +#define GL_DRAW_BUFFER14_ARB 0x8833 +#define GL_DRAW_BUFFER15_ARB 0x8834 +typedef void (APIENTRYP PFNGLDRAWBUFFERSARBPROC) (GLsizei n, const GLenum *bufs); +#ifdef GL_GLEXT_PROTOTYPES +GLAPI void APIENTRY glDrawBuffersARB (GLsizei n, const GLenum *bufs); +#endif +#endif /* GL_ARB_draw_buffers */ + +#ifndef GL_ARB_draw_buffers_blend +#define GL_ARB_draw_buffers_blend 1 +typedef void (APIENTRYP PFNGLBLENDEQUATIONIARBPROC) (GLuint buf, GLenum mode); +typedef void (APIENTRYP PFNGLBLENDEQUATIONSEPARATEIARBPROC) (GLuint buf, GLenum modeRGB, GLenum modeAlpha); +typedef void (APIENTRYP PFNGLBLENDFUNCIARBPROC) (GLuint buf, GLenum src, GLenum dst); +typedef void (APIENTRYP PFNGLBLENDFUNCSEPARATEIARBPROC) (GLuint buf, GLenum srcRGB, GLenum dstRGB, GLenum srcAlpha, GLenum dstAlpha); +#ifdef GL_GLEXT_PROTOTYPES +GLAPI void APIENTRY glBlendEquationiARB (GLuint buf, GLenum mode); +GLAPI void APIENTRY glBlendEquationSeparateiARB (GLuint buf, GLenum modeRGB, GLenum modeAlpha); +GLAPI void APIENTRY glBlendFunciARB (GLuint buf, GLenum src, GLenum dst); +GLAPI void APIENTRY glBlendFuncSeparateiARB (GLuint buf, GLenum srcRGB, GLenum dstRGB, GLenum srcAlpha, GLenum dstAlpha); +#endif +#endif /* GL_ARB_draw_buffers_blend */ + +#ifndef GL_ARB_draw_elements_base_vertex +#define GL_ARB_draw_elements_base_vertex 1 +#endif /* GL_ARB_draw_elements_base_vertex */ + +#ifndef GL_ARB_draw_indirect +#define GL_ARB_draw_indirect 1 +#endif /* GL_ARB_draw_indirect */ + +#ifndef GL_ARB_draw_instanced +#define GL_ARB_draw_instanced 1 +typedef void (APIENTRYP PFNGLDRAWARRAYSINSTANCEDARBPROC) (GLenum mode, GLint first, GLsizei count, GLsizei primcount); +typedef void (APIENTRYP PFNGLDRAWELEMENTSINSTANCEDARBPROC) (GLenum mode, GLsizei count, GLenum type, const void *indices, GLsizei primcount); +#ifdef GL_GLEXT_PROTOTYPES +GLAPI void APIENTRY glDrawArraysInstancedARB (GLenum mode, GLint first, GLsizei count, GLsizei primcount); +GLAPI void APIENTRY glDrawElementsInstancedARB (GLenum mode, GLsizei count, GLenum type, const void *indices, GLsizei primcount); +#endif +#endif /* GL_ARB_draw_instanced */ + +#ifndef GL_ARB_enhanced_layouts +#define GL_ARB_enhanced_layouts 1 +#endif /* GL_ARB_enhanced_layouts */ + +#ifndef GL_ARB_explicit_attrib_location +#define GL_ARB_explicit_attrib_location 1 +#endif /* GL_ARB_explicit_attrib_location */ + +#ifndef GL_ARB_explicit_uniform_location +#define GL_ARB_explicit_uniform_location 1 +#endif /* GL_ARB_explicit_uniform_location */ + +#ifndef GL_ARB_fragment_coord_conventions +#define GL_ARB_fragment_coord_conventions 1 +#endif /* GL_ARB_fragment_coord_conventions */ + +#ifndef GL_ARB_fragment_layer_viewport +#define GL_ARB_fragment_layer_viewport 1 +#endif /* GL_ARB_fragment_layer_viewport */ + +#ifndef GL_ARB_fragment_program +#define GL_ARB_fragment_program 1 +#define GL_FRAGMENT_PROGRAM_ARB 0x8804 +#define GL_PROGRAM_FORMAT_ASCII_ARB 0x8875 +#define GL_PROGRAM_LENGTH_ARB 0x8627 +#define GL_PROGRAM_FORMAT_ARB 0x8876 +#define GL_PROGRAM_BINDING_ARB 0x8677 +#define GL_PROGRAM_INSTRUCTIONS_ARB 0x88A0 +#define GL_MAX_PROGRAM_INSTRUCTIONS_ARB 0x88A1 +#define GL_PROGRAM_NATIVE_INSTRUCTIONS_ARB 0x88A2 +#define GL_MAX_PROGRAM_NATIVE_INSTRUCTIONS_ARB 0x88A3 +#define GL_PROGRAM_TEMPORARIES_ARB 0x88A4 +#define GL_MAX_PROGRAM_TEMPORARIES_ARB 0x88A5 +#define GL_PROGRAM_NATIVE_TEMPORARIES_ARB 0x88A6 +#define GL_MAX_PROGRAM_NATIVE_TEMPORARIES_ARB 0x88A7 +#define GL_PROGRAM_PARAMETERS_ARB 0x88A8 +#define GL_MAX_PROGRAM_PARAMETERS_ARB 0x88A9 +#define GL_PROGRAM_NATIVE_PARAMETERS_ARB 0x88AA +#define GL_MAX_PROGRAM_NATIVE_PARAMETERS_ARB 0x88AB +#define GL_PROGRAM_ATTRIBS_ARB 0x88AC +#define GL_MAX_PROGRAM_ATTRIBS_ARB 0x88AD +#define GL_PROGRAM_NATIVE_ATTRIBS_ARB 0x88AE +#define GL_MAX_PROGRAM_NATIVE_ATTRIBS_ARB 0x88AF +#define GL_MAX_PROGRAM_LOCAL_PARAMETERS_ARB 0x88B4 +#define GL_MAX_PROGRAM_ENV_PARAMETERS_ARB 0x88B5 +#define GL_PROGRAM_UNDER_NATIVE_LIMITS_ARB 0x88B6 +#define GL_PROGRAM_ALU_INSTRUCTIONS_ARB 0x8805 +#define GL_PROGRAM_TEX_INSTRUCTIONS_ARB 0x8806 +#define GL_PROGRAM_TEX_INDIRECTIONS_ARB 0x8807 +#define GL_PROGRAM_NATIVE_ALU_INSTRUCTIONS_ARB 0x8808 +#define GL_PROGRAM_NATIVE_TEX_INSTRUCTIONS_ARB 0x8809 +#define GL_PROGRAM_NATIVE_TEX_INDIRECTIONS_ARB 0x880A +#define GL_MAX_PROGRAM_ALU_INSTRUCTIONS_ARB 0x880B +#define GL_MAX_PROGRAM_TEX_INSTRUCTIONS_ARB 0x880C +#define GL_MAX_PROGRAM_TEX_INDIRECTIONS_ARB 0x880D +#define GL_MAX_PROGRAM_NATIVE_ALU_INSTRUCTIONS_ARB 0x880E +#define GL_MAX_PROGRAM_NATIVE_TEX_INSTRUCTIONS_ARB 0x880F +#define GL_MAX_PROGRAM_NATIVE_TEX_INDIRECTIONS_ARB 0x8810 +#define GL_PROGRAM_STRING_ARB 0x8628 +#define GL_PROGRAM_ERROR_POSITION_ARB 0x864B +#define GL_CURRENT_MATRIX_ARB 0x8641 +#define GL_TRANSPOSE_CURRENT_MATRIX_ARB 0x88B7 +#define GL_CURRENT_MATRIX_STACK_DEPTH_ARB 0x8640 +#define GL_MAX_PROGRAM_MATRICES_ARB 0x862F +#define GL_MAX_PROGRAM_MATRIX_STACK_DEPTH_ARB 0x862E +#define GL_MAX_TEXTURE_COORDS_ARB 0x8871 +#define GL_MAX_TEXTURE_IMAGE_UNITS_ARB 0x8872 +#define GL_PROGRAM_ERROR_STRING_ARB 0x8874 +#define GL_MATRIX0_ARB 0x88C0 +#define GL_MATRIX1_ARB 0x88C1 +#define GL_MATRIX2_ARB 0x88C2 +#define GL_MATRIX3_ARB 0x88C3 +#define GL_MATRIX4_ARB 0x88C4 +#define GL_MATRIX5_ARB 0x88C5 +#define GL_MATRIX6_ARB 0x88C6 +#define GL_MATRIX7_ARB 0x88C7 +#define GL_MATRIX8_ARB 0x88C8 +#define GL_MATRIX9_ARB 0x88C9 +#define GL_MATRIX10_ARB 0x88CA +#define GL_MATRIX11_ARB 0x88CB +#define GL_MATRIX12_ARB 0x88CC +#define GL_MATRIX13_ARB 0x88CD +#define GL_MATRIX14_ARB 0x88CE +#define GL_MATRIX15_ARB 0x88CF +#define GL_MATRIX16_ARB 0x88D0 +#define GL_MATRIX17_ARB 0x88D1 +#define GL_MATRIX18_ARB 0x88D2 +#define GL_MATRIX19_ARB 0x88D3 +#define GL_MATRIX20_ARB 0x88D4 +#define GL_MATRIX21_ARB 0x88D5 +#define GL_MATRIX22_ARB 0x88D6 +#define GL_MATRIX23_ARB 0x88D7 +#define GL_MATRIX24_ARB 0x88D8 +#define GL_MATRIX25_ARB 0x88D9 +#define GL_MATRIX26_ARB 0x88DA +#define GL_MATRIX27_ARB 0x88DB +#define GL_MATRIX28_ARB 0x88DC +#define GL_MATRIX29_ARB 0x88DD +#define GL_MATRIX30_ARB 0x88DE +#define GL_MATRIX31_ARB 0x88DF +typedef void (APIENTRYP PFNGLPROGRAMSTRINGARBPROC) (GLenum target, GLenum format, GLsizei len, const void *string); +typedef void (APIENTRYP PFNGLBINDPROGRAMARBPROC) (GLenum target, GLuint program); +typedef void (APIENTRYP PFNGLDELETEPROGRAMSARBPROC) (GLsizei n, const GLuint *programs); +typedef void (APIENTRYP PFNGLGENPROGRAMSARBPROC) (GLsizei n, GLuint *programs); +typedef void (APIENTRYP PFNGLPROGRAMENVPARAMETER4DARBPROC) (GLenum target, GLuint index, GLdouble x, GLdouble y, GLdouble z, GLdouble w); +typedef void (APIENTRYP PFNGLPROGRAMENVPARAMETER4DVARBPROC) (GLenum target, GLuint index, const GLdouble *params); +typedef void (APIENTRYP PFNGLPROGRAMENVPARAMETER4FARBPROC) (GLenum target, GLuint index, GLfloat x, GLfloat y, GLfloat z, GLfloat w); +typedef void (APIENTRYP PFNGLPROGRAMENVPARAMETER4FVARBPROC) (GLenum target, GLuint index, const GLfloat *params); +typedef void (APIENTRYP PFNGLPROGRAMLOCALPARAMETER4DARBPROC) (GLenum target, GLuint index, GLdouble x, GLdouble y, GLdouble z, GLdouble w); +typedef void (APIENTRYP PFNGLPROGRAMLOCALPARAMETER4DVARBPROC) (GLenum target, GLuint index, const GLdouble *params); +typedef void (APIENTRYP PFNGLPROGRAMLOCALPARAMETER4FARBPROC) (GLenum target, GLuint index, GLfloat x, GLfloat y, GLfloat z, GLfloat w); +typedef void (APIENTRYP PFNGLPROGRAMLOCALPARAMETER4FVARBPROC) (GLenum target, GLuint index, const GLfloat *params); +typedef void (APIENTRYP PFNGLGETPROGRAMENVPARAMETERDVARBPROC) (GLenum target, GLuint index, GLdouble *params); +typedef void (APIENTRYP PFNGLGETPROGRAMENVPARAMETERFVARBPROC) (GLenum target, GLuint index, GLfloat *params); +typedef void (APIENTRYP PFNGLGETPROGRAMLOCALPARAMETERDVARBPROC) (GLenum target, GLuint index, GLdouble *params); +typedef void (APIENTRYP PFNGLGETPROGRAMLOCALPARAMETERFVARBPROC) (GLenum target, GLuint index, GLfloat *params); +typedef void (APIENTRYP PFNGLGETPROGRAMIVARBPROC) (GLenum target, GLenum pname, GLint *params); +typedef void (APIENTRYP PFNGLGETPROGRAMSTRINGARBPROC) (GLenum target, GLenum pname, void *string); +typedef GLboolean (APIENTRYP PFNGLISPROGRAMARBPROC) (GLuint program); +#ifdef GL_GLEXT_PROTOTYPES +GLAPI void APIENTRY glProgramStringARB (GLenum target, GLenum format, GLsizei len, const void *string); +GLAPI void APIENTRY glBindProgramARB (GLenum target, GLuint program); +GLAPI void APIENTRY glDeleteProgramsARB (GLsizei n, const GLuint *programs); +GLAPI void APIENTRY glGenProgramsARB (GLsizei n, GLuint *programs); +GLAPI void APIENTRY glProgramEnvParameter4dARB (GLenum target, GLuint index, GLdouble x, GLdouble y, GLdouble z, GLdouble w); +GLAPI void APIENTRY glProgramEnvParameter4dvARB (GLenum target, GLuint index, const GLdouble *params); +GLAPI void APIENTRY glProgramEnvParameter4fARB (GLenum target, GLuint index, GLfloat x, GLfloat y, GLfloat z, GLfloat w); +GLAPI void APIENTRY glProgramEnvParameter4fvARB (GLenum target, GLuint index, const GLfloat *params); +GLAPI void APIENTRY glProgramLocalParameter4dARB (GLenum target, GLuint index, GLdouble x, GLdouble y, GLdouble z, GLdouble w); +GLAPI void APIENTRY glProgramLocalParameter4dvARB (GLenum target, GLuint index, const GLdouble *params); +GLAPI void APIENTRY glProgramLocalParameter4fARB (GLenum target, GLuint index, GLfloat x, GLfloat y, GLfloat z, GLfloat w); +GLAPI void APIENTRY glProgramLocalParameter4fvARB (GLenum target, GLuint index, const GLfloat *params); +GLAPI void APIENTRY glGetProgramEnvParameterdvARB (GLenum target, GLuint index, GLdouble *params); +GLAPI void APIENTRY glGetProgramEnvParameterfvARB (GLenum target, GLuint index, GLfloat *params); +GLAPI void APIENTRY glGetProgramLocalParameterdvARB (GLenum target, GLuint index, GLdouble *params); +GLAPI void APIENTRY glGetProgramLocalParameterfvARB (GLenum target, GLuint index, GLfloat *params); +GLAPI void APIENTRY glGetProgramivARB (GLenum target, GLenum pname, GLint *params); +GLAPI void APIENTRY glGetProgramStringARB (GLenum target, GLenum pname, void *string); +GLAPI GLboolean APIENTRY glIsProgramARB (GLuint program); +#endif +#endif /* GL_ARB_fragment_program */ + +#ifndef GL_ARB_fragment_program_shadow +#define GL_ARB_fragment_program_shadow 1 +#endif /* GL_ARB_fragment_program_shadow */ + +#ifndef GL_ARB_fragment_shader +#define GL_ARB_fragment_shader 1 +#define GL_FRAGMENT_SHADER_ARB 0x8B30 +#define GL_MAX_FRAGMENT_UNIFORM_COMPONENTS_ARB 0x8B49 +#define GL_FRAGMENT_SHADER_DERIVATIVE_HINT_ARB 0x8B8B +#endif /* GL_ARB_fragment_shader */ + +#ifndef GL_ARB_framebuffer_no_attachments +#define GL_ARB_framebuffer_no_attachments 1 +#endif /* GL_ARB_framebuffer_no_attachments */ + +#ifndef GL_ARB_framebuffer_object +#define GL_ARB_framebuffer_object 1 +#endif /* GL_ARB_framebuffer_object */ + +#ifndef GL_ARB_framebuffer_sRGB +#define GL_ARB_framebuffer_sRGB 1 +#endif /* GL_ARB_framebuffer_sRGB */ + +#ifndef GL_ARB_geometry_shader4 +#define GL_ARB_geometry_shader4 1 +#define GL_LINES_ADJACENCY_ARB 0x000A +#define GL_LINE_STRIP_ADJACENCY_ARB 0x000B +#define GL_TRIANGLES_ADJACENCY_ARB 0x000C +#define GL_TRIANGLE_STRIP_ADJACENCY_ARB 0x000D +#define GL_PROGRAM_POINT_SIZE_ARB 0x8642 +#define GL_MAX_GEOMETRY_TEXTURE_IMAGE_UNITS_ARB 0x8C29 +#define GL_FRAMEBUFFER_ATTACHMENT_LAYERED_ARB 0x8DA7 +#define GL_FRAMEBUFFER_INCOMPLETE_LAYER_TARGETS_ARB 0x8DA8 +#define GL_FRAMEBUFFER_INCOMPLETE_LAYER_COUNT_ARB 0x8DA9 +#define GL_GEOMETRY_SHADER_ARB 0x8DD9 +#define GL_GEOMETRY_VERTICES_OUT_ARB 0x8DDA +#define GL_GEOMETRY_INPUT_TYPE_ARB 0x8DDB +#define GL_GEOMETRY_OUTPUT_TYPE_ARB 0x8DDC +#define GL_MAX_GEOMETRY_VARYING_COMPONENTS_ARB 0x8DDD +#define GL_MAX_VERTEX_VARYING_COMPONENTS_ARB 0x8DDE +#define GL_MAX_GEOMETRY_UNIFORM_COMPONENTS_ARB 0x8DDF +#define GL_MAX_GEOMETRY_OUTPUT_VERTICES_ARB 0x8DE0 +#define GL_MAX_GEOMETRY_TOTAL_OUTPUT_COMPONENTS_ARB 0x8DE1 +typedef void (APIENTRYP PFNGLPROGRAMPARAMETERIARBPROC) (GLuint program, GLenum pname, GLint value); +typedef void (APIENTRYP PFNGLFRAMEBUFFERTEXTUREARBPROC) (GLenum target, GLenum attachment, GLuint texture, GLint level); +typedef void (APIENTRYP PFNGLFRAMEBUFFERTEXTURELAYERARBPROC) (GLenum target, GLenum attachment, GLuint texture, GLint level, GLint layer); +typedef void (APIENTRYP PFNGLFRAMEBUFFERTEXTUREFACEARBPROC) (GLenum target, GLenum attachment, GLuint texture, GLint level, GLenum face); +#ifdef GL_GLEXT_PROTOTYPES +GLAPI void APIENTRY glProgramParameteriARB (GLuint program, GLenum pname, GLint value); +GLAPI void APIENTRY glFramebufferTextureARB (GLenum target, GLenum attachment, GLuint texture, GLint level); +GLAPI void APIENTRY glFramebufferTextureLayerARB (GLenum target, GLenum attachment, GLuint texture, GLint level, GLint layer); +GLAPI void APIENTRY glFramebufferTextureFaceARB (GLenum target, GLenum attachment, GLuint texture, GLint level, GLenum face); +#endif +#endif /* GL_ARB_geometry_shader4 */ + +#ifndef GL_ARB_get_program_binary +#define GL_ARB_get_program_binary 1 +#endif /* GL_ARB_get_program_binary */ + +#ifndef GL_ARB_gpu_shader5 +#define GL_ARB_gpu_shader5 1 +#endif /* GL_ARB_gpu_shader5 */ + +#ifndef GL_ARB_gpu_shader_fp64 +#define GL_ARB_gpu_shader_fp64 1 +#endif /* GL_ARB_gpu_shader_fp64 */ + +#ifndef GL_ARB_half_float_pixel +#define GL_ARB_half_float_pixel 1 +typedef unsigned short GLhalfARB; +#define GL_HALF_FLOAT_ARB 0x140B +#endif /* GL_ARB_half_float_pixel */ + +#ifndef GL_ARB_half_float_vertex +#define GL_ARB_half_float_vertex 1 +#endif /* GL_ARB_half_float_vertex */ + +#ifndef GL_ARB_imaging +#define GL_ARB_imaging 1 +#define GL_BLEND_COLOR 0x8005 +#define GL_BLEND_EQUATION 0x8009 +#define GL_CONVOLUTION_1D 0x8010 +#define GL_CONVOLUTION_2D 0x8011 +#define GL_SEPARABLE_2D 0x8012 +#define GL_CONVOLUTION_BORDER_MODE 0x8013 +#define GL_CONVOLUTION_FILTER_SCALE 0x8014 +#define GL_CONVOLUTION_FILTER_BIAS 0x8015 +#define GL_REDUCE 0x8016 +#define GL_CONVOLUTION_FORMAT 0x8017 +#define GL_CONVOLUTION_WIDTH 0x8018 +#define GL_CONVOLUTION_HEIGHT 0x8019 +#define GL_MAX_CONVOLUTION_WIDTH 0x801A +#define GL_MAX_CONVOLUTION_HEIGHT 0x801B +#define GL_POST_CONVOLUTION_RED_SCALE 0x801C +#define GL_POST_CONVOLUTION_GREEN_SCALE 0x801D +#define GL_POST_CONVOLUTION_BLUE_SCALE 0x801E +#define GL_POST_CONVOLUTION_ALPHA_SCALE 0x801F +#define GL_POST_CONVOLUTION_RED_BIAS 0x8020 +#define GL_POST_CONVOLUTION_GREEN_BIAS 0x8021 +#define GL_POST_CONVOLUTION_BLUE_BIAS 0x8022 +#define GL_POST_CONVOLUTION_ALPHA_BIAS 0x8023 +#define GL_HISTOGRAM 0x8024 +#define GL_PROXY_HISTOGRAM 0x8025 +#define GL_HISTOGRAM_WIDTH 0x8026 +#define GL_HISTOGRAM_FORMAT 0x8027 +#define GL_HISTOGRAM_RED_SIZE 0x8028 +#define GL_HISTOGRAM_GREEN_SIZE 0x8029 +#define GL_HISTOGRAM_BLUE_SIZE 0x802A +#define GL_HISTOGRAM_ALPHA_SIZE 0x802B +#define GL_HISTOGRAM_LUMINANCE_SIZE 0x802C +#define GL_HISTOGRAM_SINK 0x802D +#define GL_MINMAX 0x802E +#define GL_MINMAX_FORMAT 0x802F +#define GL_MINMAX_SINK 0x8030 +#define GL_TABLE_TOO_LARGE 0x8031 +#define GL_COLOR_MATRIX 0x80B1 +#define GL_COLOR_MATRIX_STACK_DEPTH 0x80B2 +#define GL_MAX_COLOR_MATRIX_STACK_DEPTH 0x80B3 +#define GL_POST_COLOR_MATRIX_RED_SCALE 0x80B4 +#define GL_POST_COLOR_MATRIX_GREEN_SCALE 0x80B5 +#define GL_POST_COLOR_MATRIX_BLUE_SCALE 0x80B6 +#define GL_POST_COLOR_MATRIX_ALPHA_SCALE 0x80B7 +#define GL_POST_COLOR_MATRIX_RED_BIAS 0x80B8 +#define GL_POST_COLOR_MATRIX_GREEN_BIAS 0x80B9 +#define GL_POST_COLOR_MATRIX_BLUE_BIAS 0x80BA +#define GL_POST_COLOR_MATRIX_ALPHA_BIAS 0x80BB +#define GL_COLOR_TABLE 0x80D0 +#define GL_POST_CONVOLUTION_COLOR_TABLE 0x80D1 +#define GL_POST_COLOR_MATRIX_COLOR_TABLE 0x80D2 +#define GL_PROXY_COLOR_TABLE 0x80D3 +#define GL_PROXY_POST_CONVOLUTION_COLOR_TABLE 0x80D4 +#define GL_PROXY_POST_COLOR_MATRIX_COLOR_TABLE 0x80D5 +#define GL_COLOR_TABLE_SCALE 0x80D6 +#define GL_COLOR_TABLE_BIAS 0x80D7 +#define GL_COLOR_TABLE_FORMAT 0x80D8 +#define GL_COLOR_TABLE_WIDTH 0x80D9 +#define GL_COLOR_TABLE_RED_SIZE 0x80DA +#define GL_COLOR_TABLE_GREEN_SIZE 0x80DB +#define GL_COLOR_TABLE_BLUE_SIZE 0x80DC +#define GL_COLOR_TABLE_ALPHA_SIZE 0x80DD +#define GL_COLOR_TABLE_LUMINANCE_SIZE 0x80DE +#define GL_COLOR_TABLE_INTENSITY_SIZE 0x80DF +#define GL_CONSTANT_BORDER 0x8151 +#define GL_REPLICATE_BORDER 0x8153 +#define GL_CONVOLUTION_BORDER_COLOR 0x8154 +typedef void (APIENTRYP PFNGLCOLORTABLEPROC) (GLenum target, GLenum internalformat, GLsizei width, GLenum format, GLenum type, const void *table); +typedef void (APIENTRYP PFNGLCOLORTABLEPARAMETERFVPROC) (GLenum target, GLenum pname, const GLfloat *params); +typedef void (APIENTRYP PFNGLCOLORTABLEPARAMETERIVPROC) (GLenum target, GLenum pname, const GLint *params); +typedef void (APIENTRYP PFNGLCOPYCOLORTABLEPROC) (GLenum target, GLenum internalformat, GLint x, GLint y, GLsizei width); +typedef void (APIENTRYP PFNGLGETCOLORTABLEPROC) (GLenum target, GLenum format, GLenum type, void *table); +typedef void (APIENTRYP PFNGLGETCOLORTABLEPARAMETERFVPROC) (GLenum target, GLenum pname, GLfloat *params); +typedef void (APIENTRYP PFNGLGETCOLORTABLEPARAMETERIVPROC) (GLenum target, GLenum pname, GLint *params); +typedef void (APIENTRYP PFNGLCOLORSUBTABLEPROC) (GLenum target, GLsizei start, GLsizei count, GLenum format, GLenum type, const void *data); +typedef void (APIENTRYP PFNGLCOPYCOLORSUBTABLEPROC) (GLenum target, GLsizei start, GLint x, GLint y, GLsizei width); +typedef void (APIENTRYP PFNGLCONVOLUTIONFILTER1DPROC) (GLenum target, GLenum internalformat, GLsizei width, GLenum format, GLenum type, const void *image); +typedef void (APIENTRYP PFNGLCONVOLUTIONFILTER2DPROC) (GLenum target, GLenum internalformat, GLsizei width, GLsizei height, GLenum format, GLenum type, const void *image); +typedef void (APIENTRYP PFNGLCONVOLUTIONPARAMETERFPROC) (GLenum target, GLenum pname, GLfloat params); +typedef void (APIENTRYP PFNGLCONVOLUTIONPARAMETERFVPROC) (GLenum target, GLenum pname, const GLfloat *params); +typedef void (APIENTRYP PFNGLCONVOLUTIONPARAMETERIPROC) (GLenum target, GLenum pname, GLint params); +typedef void (APIENTRYP PFNGLCONVOLUTIONPARAMETERIVPROC) (GLenum target, GLenum pname, const GLint *params); +typedef void (APIENTRYP PFNGLCOPYCONVOLUTIONFILTER1DPROC) (GLenum target, GLenum internalformat, GLint x, GLint y, GLsizei width); +typedef void (APIENTRYP PFNGLCOPYCONVOLUTIONFILTER2DPROC) (GLenum target, GLenum internalformat, GLint x, GLint y, GLsizei width, GLsizei height); +typedef void (APIENTRYP PFNGLGETCONVOLUTIONFILTERPROC) (GLenum target, GLenum format, GLenum type, void *image); +typedef void (APIENTRYP PFNGLGETCONVOLUTIONPARAMETERFVPROC) (GLenum target, GLenum pname, GLfloat *params); +typedef void (APIENTRYP PFNGLGETCONVOLUTIONPARAMETERIVPROC) (GLenum target, GLenum pname, GLint *params); +typedef void (APIENTRYP PFNGLGETSEPARABLEFILTERPROC) (GLenum target, GLenum format, GLenum type, void *row, void *column, void *span); +typedef void (APIENTRYP PFNGLSEPARABLEFILTER2DPROC) (GLenum target, GLenum internalformat, GLsizei width, GLsizei height, GLenum format, GLenum type, const void *row, const void *column); +typedef void (APIENTRYP PFNGLGETHISTOGRAMPROC) (GLenum target, GLboolean reset, GLenum format, GLenum type, void *values); +typedef void (APIENTRYP PFNGLGETHISTOGRAMPARAMETERFVPROC) (GLenum target, GLenum pname, GLfloat *params); +typedef void (APIENTRYP PFNGLGETHISTOGRAMPARAMETERIVPROC) (GLenum target, GLenum pname, GLint *params); +typedef void (APIENTRYP PFNGLGETMINMAXPROC) (GLenum target, GLboolean reset, GLenum format, GLenum type, void *values); +typedef void (APIENTRYP PFNGLGETMINMAXPARAMETERFVPROC) (GLenum target, GLenum pname, GLfloat *params); +typedef void (APIENTRYP PFNGLGETMINMAXPARAMETERIVPROC) (GLenum target, GLenum pname, GLint *params); +typedef void (APIENTRYP PFNGLHISTOGRAMPROC) (GLenum target, GLsizei width, GLenum internalformat, GLboolean sink); +typedef void (APIENTRYP PFNGLMINMAXPROC) (GLenum target, GLenum internalformat, GLboolean sink); +typedef void (APIENTRYP PFNGLRESETHISTOGRAMPROC) (GLenum target); +typedef void (APIENTRYP PFNGLRESETMINMAXPROC) (GLenum target); +#ifdef GL_GLEXT_PROTOTYPES +GLAPI void APIENTRY glColorTable (GLenum target, GLenum internalformat, GLsizei width, GLenum format, GLenum type, const void *table); +GLAPI void APIENTRY glColorTableParameterfv (GLenum target, GLenum pname, const GLfloat *params); +GLAPI void APIENTRY glColorTableParameteriv (GLenum target, GLenum pname, const GLint *params); +GLAPI void APIENTRY glCopyColorTable (GLenum target, GLenum internalformat, GLint x, GLint y, GLsizei width); +GLAPI void APIENTRY glGetColorTable (GLenum target, GLenum format, GLenum type, void *table); +GLAPI void APIENTRY glGetColorTableParameterfv (GLenum target, GLenum pname, GLfloat *params); +GLAPI void APIENTRY glGetColorTableParameteriv (GLenum target, GLenum pname, GLint *params); +GLAPI void APIENTRY glColorSubTable (GLenum target, GLsizei start, GLsizei count, GLenum format, GLenum type, const void *data); +GLAPI void APIENTRY glCopyColorSubTable (GLenum target, GLsizei start, GLint x, GLint y, GLsizei width); +GLAPI void APIENTRY glConvolutionFilter1D (GLenum target, GLenum internalformat, GLsizei width, GLenum format, GLenum type, const void *image); +GLAPI void APIENTRY glConvolutionFilter2D (GLenum target, GLenum internalformat, GLsizei width, GLsizei height, GLenum format, GLenum type, const void *image); +GLAPI void APIENTRY glConvolutionParameterf (GLenum target, GLenum pname, GLfloat params); +GLAPI void APIENTRY glConvolutionParameterfv (GLenum target, GLenum pname, const GLfloat *params); +GLAPI void APIENTRY glConvolutionParameteri (GLenum target, GLenum pname, GLint params); +GLAPI void APIENTRY glConvolutionParameteriv (GLenum target, GLenum pname, const GLint *params); +GLAPI void APIENTRY glCopyConvolutionFilter1D (GLenum target, GLenum internalformat, GLint x, GLint y, GLsizei width); +GLAPI void APIENTRY glCopyConvolutionFilter2D (GLenum target, GLenum internalformat, GLint x, GLint y, GLsizei width, GLsizei height); +GLAPI void APIENTRY glGetConvolutionFilter (GLenum target, GLenum format, GLenum type, void *image); +GLAPI void APIENTRY glGetConvolutionParameterfv (GLenum target, GLenum pname, GLfloat *params); +GLAPI void APIENTRY glGetConvolutionParameteriv (GLenum target, GLenum pname, GLint *params); +GLAPI void APIENTRY glGetSeparableFilter (GLenum target, GLenum format, GLenum type, void *row, void *column, void *span); +GLAPI void APIENTRY glSeparableFilter2D (GLenum target, GLenum internalformat, GLsizei width, GLsizei height, GLenum format, GLenum type, const void *row, const void *column); +GLAPI void APIENTRY glGetHistogram (GLenum target, GLboolean reset, GLenum format, GLenum type, void *values); +GLAPI void APIENTRY glGetHistogramParameterfv (GLenum target, GLenum pname, GLfloat *params); +GLAPI void APIENTRY glGetHistogramParameteriv (GLenum target, GLenum pname, GLint *params); +GLAPI void APIENTRY glGetMinmax (GLenum target, GLboolean reset, GLenum format, GLenum type, void *values); +GLAPI void APIENTRY glGetMinmaxParameterfv (GLenum target, GLenum pname, GLfloat *params); +GLAPI void APIENTRY glGetMinmaxParameteriv (GLenum target, GLenum pname, GLint *params); +GLAPI void APIENTRY glHistogram (GLenum target, GLsizei width, GLenum internalformat, GLboolean sink); +GLAPI void APIENTRY glMinmax (GLenum target, GLenum internalformat, GLboolean sink); +GLAPI void APIENTRY glResetHistogram (GLenum target); +GLAPI void APIENTRY glResetMinmax (GLenum target); +#endif +#endif /* GL_ARB_imaging */ + +#ifndef GL_ARB_indirect_parameters +#define GL_ARB_indirect_parameters 1 +#define GL_PARAMETER_BUFFER_ARB 0x80EE +#define GL_PARAMETER_BUFFER_BINDING_ARB 0x80EF +typedef void (APIENTRYP PFNGLMULTIDRAWARRAYSINDIRECTCOUNTARBPROC) (GLenum mode, GLintptr indirect, GLintptr drawcount, GLsizei maxdrawcount, GLsizei stride); +typedef void (APIENTRYP PFNGLMULTIDRAWELEMENTSINDIRECTCOUNTARBPROC) (GLenum mode, GLenum type, GLintptr indirect, GLintptr drawcount, GLsizei maxdrawcount, GLsizei stride); +#ifdef GL_GLEXT_PROTOTYPES +GLAPI void APIENTRY glMultiDrawArraysIndirectCountARB (GLenum mode, GLintptr indirect, GLintptr drawcount, GLsizei maxdrawcount, GLsizei stride); +GLAPI void APIENTRY glMultiDrawElementsIndirectCountARB (GLenum mode, GLenum type, GLintptr indirect, GLintptr drawcount, GLsizei maxdrawcount, GLsizei stride); +#endif +#endif /* GL_ARB_indirect_parameters */ + +#ifndef GL_ARB_instanced_arrays +#define GL_ARB_instanced_arrays 1 +#define GL_VERTEX_ATTRIB_ARRAY_DIVISOR_ARB 0x88FE +typedef void (APIENTRYP PFNGLVERTEXATTRIBDIVISORARBPROC) (GLuint index, GLuint divisor); +#ifdef GL_GLEXT_PROTOTYPES +GLAPI void APIENTRY glVertexAttribDivisorARB (GLuint index, GLuint divisor); +#endif +#endif /* GL_ARB_instanced_arrays */ + +#ifndef GL_ARB_internalformat_query +#define GL_ARB_internalformat_query 1 +#endif /* GL_ARB_internalformat_query */ + +#ifndef GL_ARB_internalformat_query2 +#define GL_ARB_internalformat_query2 1 +#define GL_SRGB_DECODE_ARB 0x8299 +#endif /* GL_ARB_internalformat_query2 */ + +#ifndef GL_ARB_invalidate_subdata +#define GL_ARB_invalidate_subdata 1 +#endif /* GL_ARB_invalidate_subdata */ + +#ifndef GL_ARB_map_buffer_alignment +#define GL_ARB_map_buffer_alignment 1 +#endif /* GL_ARB_map_buffer_alignment */ + +#ifndef GL_ARB_map_buffer_range +#define GL_ARB_map_buffer_range 1 +#endif /* GL_ARB_map_buffer_range */ + +#ifndef GL_ARB_matrix_palette +#define GL_ARB_matrix_palette 1 +#define GL_MATRIX_PALETTE_ARB 0x8840 +#define GL_MAX_MATRIX_PALETTE_STACK_DEPTH_ARB 0x8841 +#define GL_MAX_PALETTE_MATRICES_ARB 0x8842 +#define GL_CURRENT_PALETTE_MATRIX_ARB 0x8843 +#define GL_MATRIX_INDEX_ARRAY_ARB 0x8844 +#define GL_CURRENT_MATRIX_INDEX_ARB 0x8845 +#define GL_MATRIX_INDEX_ARRAY_SIZE_ARB 0x8846 +#define GL_MATRIX_INDEX_ARRAY_TYPE_ARB 0x8847 +#define GL_MATRIX_INDEX_ARRAY_STRIDE_ARB 0x8848 +#define GL_MATRIX_INDEX_ARRAY_POINTER_ARB 0x8849 +typedef void (APIENTRYP PFNGLCURRENTPALETTEMATRIXARBPROC) (GLint index); +typedef void (APIENTRYP PFNGLMATRIXINDEXUBVARBPROC) (GLint size, const GLubyte *indices); +typedef void (APIENTRYP PFNGLMATRIXINDEXUSVARBPROC) (GLint size, const GLushort *indices); +typedef void (APIENTRYP PFNGLMATRIXINDEXUIVARBPROC) (GLint size, const GLuint *indices); +typedef void (APIENTRYP PFNGLMATRIXINDEXPOINTERARBPROC) (GLint size, GLenum type, GLsizei stride, const void *pointer); +#ifdef GL_GLEXT_PROTOTYPES +GLAPI void APIENTRY glCurrentPaletteMatrixARB (GLint index); +GLAPI void APIENTRY glMatrixIndexubvARB (GLint size, const GLubyte *indices); +GLAPI void APIENTRY glMatrixIndexusvARB (GLint size, const GLushort *indices); +GLAPI void APIENTRY glMatrixIndexuivARB (GLint size, const GLuint *indices); +GLAPI void APIENTRY glMatrixIndexPointerARB (GLint size, GLenum type, GLsizei stride, const void *pointer); +#endif +#endif /* GL_ARB_matrix_palette */ + +#ifndef GL_ARB_multi_bind +#define GL_ARB_multi_bind 1 +#endif /* GL_ARB_multi_bind */ + +#ifndef GL_ARB_multi_draw_indirect +#define GL_ARB_multi_draw_indirect 1 +#endif /* GL_ARB_multi_draw_indirect */ + +#ifndef GL_ARB_multisample +#define GL_ARB_multisample 1 +#define GL_MULTISAMPLE_ARB 0x809D +#define GL_SAMPLE_ALPHA_TO_COVERAGE_ARB 0x809E +#define GL_SAMPLE_ALPHA_TO_ONE_ARB 0x809F +#define GL_SAMPLE_COVERAGE_ARB 0x80A0 +#define GL_SAMPLE_BUFFERS_ARB 0x80A8 +#define GL_SAMPLES_ARB 0x80A9 +#define GL_SAMPLE_COVERAGE_VALUE_ARB 0x80AA +#define GL_SAMPLE_COVERAGE_INVERT_ARB 0x80AB +#define GL_MULTISAMPLE_BIT_ARB 0x20000000 +typedef void (APIENTRYP PFNGLSAMPLECOVERAGEARBPROC) (GLfloat value, GLboolean invert); +#ifdef GL_GLEXT_PROTOTYPES +GLAPI void APIENTRY glSampleCoverageARB (GLfloat value, GLboolean invert); +#endif +#endif /* GL_ARB_multisample */ + +#ifndef GL_ARB_multitexture +#define GL_ARB_multitexture 1 +#define GL_TEXTURE0_ARB 0x84C0 +#define GL_TEXTURE1_ARB 0x84C1 +#define GL_TEXTURE2_ARB 0x84C2 +#define GL_TEXTURE3_ARB 0x84C3 +#define GL_TEXTURE4_ARB 0x84C4 +#define GL_TEXTURE5_ARB 0x84C5 +#define GL_TEXTURE6_ARB 0x84C6 +#define GL_TEXTURE7_ARB 0x84C7 +#define GL_TEXTURE8_ARB 0x84C8 +#define GL_TEXTURE9_ARB 0x84C9 +#define GL_TEXTURE10_ARB 0x84CA +#define GL_TEXTURE11_ARB 0x84CB +#define GL_TEXTURE12_ARB 0x84CC +#define GL_TEXTURE13_ARB 0x84CD +#define GL_TEXTURE14_ARB 0x84CE +#define GL_TEXTURE15_ARB 0x84CF +#define GL_TEXTURE16_ARB 0x84D0 +#define GL_TEXTURE17_ARB 0x84D1 +#define GL_TEXTURE18_ARB 0x84D2 +#define GL_TEXTURE19_ARB 0x84D3 +#define GL_TEXTURE20_ARB 0x84D4 +#define GL_TEXTURE21_ARB 0x84D5 +#define GL_TEXTURE22_ARB 0x84D6 +#define GL_TEXTURE23_ARB 0x84D7 +#define GL_TEXTURE24_ARB 0x84D8 +#define GL_TEXTURE25_ARB 0x84D9 +#define GL_TEXTURE26_ARB 0x84DA +#define GL_TEXTURE27_ARB 0x84DB +#define GL_TEXTURE28_ARB 0x84DC +#define GL_TEXTURE29_ARB 0x84DD +#define GL_TEXTURE30_ARB 0x84DE +#define GL_TEXTURE31_ARB 0x84DF +#define GL_ACTIVE_TEXTURE_ARB 0x84E0 +#define GL_CLIENT_ACTIVE_TEXTURE_ARB 0x84E1 +#define GL_MAX_TEXTURE_UNITS_ARB 0x84E2 +typedef void (APIENTRYP PFNGLACTIVETEXTUREARBPROC) (GLenum texture); +typedef void (APIENTRYP PFNGLCLIENTACTIVETEXTUREARBPROC) (GLenum texture); +typedef void (APIENTRYP PFNGLMULTITEXCOORD1DARBPROC) (GLenum target, GLdouble s); +typedef void (APIENTRYP PFNGLMULTITEXCOORD1DVARBPROC) (GLenum target, const GLdouble *v); +typedef void (APIENTRYP PFNGLMULTITEXCOORD1FARBPROC) (GLenum target, GLfloat s); +typedef void (APIENTRYP PFNGLMULTITEXCOORD1FVARBPROC) (GLenum target, const GLfloat *v); +typedef void (APIENTRYP PFNGLMULTITEXCOORD1IARBPROC) (GLenum target, GLint s); +typedef void (APIENTRYP PFNGLMULTITEXCOORD1IVARBPROC) (GLenum target, const GLint *v); +typedef void (APIENTRYP PFNGLMULTITEXCOORD1SARBPROC) (GLenum target, GLshort s); +typedef void (APIENTRYP PFNGLMULTITEXCOORD1SVARBPROC) (GLenum target, const GLshort *v); +typedef void (APIENTRYP PFNGLMULTITEXCOORD2DARBPROC) (GLenum target, GLdouble s, GLdouble t); +typedef void (APIENTRYP PFNGLMULTITEXCOORD2DVARBPROC) (GLenum target, const GLdouble *v); +typedef void (APIENTRYP PFNGLMULTITEXCOORD2FARBPROC) (GLenum target, GLfloat s, GLfloat t); +typedef void (APIENTRYP PFNGLMULTITEXCOORD2FVARBPROC) (GLenum target, const GLfloat *v); +typedef void (APIENTRYP PFNGLMULTITEXCOORD2IARBPROC) (GLenum target, GLint s, GLint t); +typedef void (APIENTRYP PFNGLMULTITEXCOORD2IVARBPROC) (GLenum target, const GLint *v); +typedef void (APIENTRYP PFNGLMULTITEXCOORD2SARBPROC) (GLenum target, GLshort s, GLshort t); +typedef void (APIENTRYP PFNGLMULTITEXCOORD2SVARBPROC) (GLenum target, const GLshort *v); +typedef void (APIENTRYP PFNGLMULTITEXCOORD3DARBPROC) (GLenum target, GLdouble s, GLdouble t, GLdouble r); +typedef void (APIENTRYP PFNGLMULTITEXCOORD3DVARBPROC) (GLenum target, const GLdouble *v); +typedef void (APIENTRYP PFNGLMULTITEXCOORD3FARBPROC) (GLenum target, GLfloat s, GLfloat t, GLfloat r); +typedef void (APIENTRYP PFNGLMULTITEXCOORD3FVARBPROC) (GLenum target, const GLfloat *v); +typedef void (APIENTRYP PFNGLMULTITEXCOORD3IARBPROC) (GLenum target, GLint s, GLint t, GLint r); +typedef void (APIENTRYP PFNGLMULTITEXCOORD3IVARBPROC) (GLenum target, const GLint *v); +typedef void (APIENTRYP PFNGLMULTITEXCOORD3SARBPROC) (GLenum target, GLshort s, GLshort t, GLshort r); +typedef void (APIENTRYP PFNGLMULTITEXCOORD3SVARBPROC) (GLenum target, const GLshort *v); +typedef void (APIENTRYP PFNGLMULTITEXCOORD4DARBPROC) (GLenum target, GLdouble s, GLdouble t, GLdouble r, GLdouble q); +typedef void (APIENTRYP PFNGLMULTITEXCOORD4DVARBPROC) (GLenum target, const GLdouble *v); +typedef void (APIENTRYP PFNGLMULTITEXCOORD4FARBPROC) (GLenum target, GLfloat s, GLfloat t, GLfloat r, GLfloat q); +typedef void (APIENTRYP PFNGLMULTITEXCOORD4FVARBPROC) (GLenum target, const GLfloat *v); +typedef void (APIENTRYP PFNGLMULTITEXCOORD4IARBPROC) (GLenum target, GLint s, GLint t, GLint r, GLint q); +typedef void (APIENTRYP PFNGLMULTITEXCOORD4IVARBPROC) (GLenum target, const GLint *v); +typedef void (APIENTRYP PFNGLMULTITEXCOORD4SARBPROC) (GLenum target, GLshort s, GLshort t, GLshort r, GLshort q); +typedef void (APIENTRYP PFNGLMULTITEXCOORD4SVARBPROC) (GLenum target, const GLshort *v); +#ifdef GL_GLEXT_PROTOTYPES +GLAPI void APIENTRY glActiveTextureARB (GLenum texture); +GLAPI void APIENTRY glClientActiveTextureARB (GLenum texture); +GLAPI void APIENTRY glMultiTexCoord1dARB (GLenum target, GLdouble s); +GLAPI void APIENTRY glMultiTexCoord1dvARB (GLenum target, const GLdouble *v); +GLAPI void APIENTRY glMultiTexCoord1fARB (GLenum target, GLfloat s); +GLAPI void APIENTRY glMultiTexCoord1fvARB (GLenum target, const GLfloat *v); +GLAPI void APIENTRY glMultiTexCoord1iARB (GLenum target, GLint s); +GLAPI void APIENTRY glMultiTexCoord1ivARB (GLenum target, const GLint *v); +GLAPI void APIENTRY glMultiTexCoord1sARB (GLenum target, GLshort s); +GLAPI void APIENTRY glMultiTexCoord1svARB (GLenum target, const GLshort *v); +GLAPI void APIENTRY glMultiTexCoord2dARB (GLenum target, GLdouble s, GLdouble t); +GLAPI void APIENTRY glMultiTexCoord2dvARB (GLenum target, const GLdouble *v); +GLAPI void APIENTRY glMultiTexCoord2fARB (GLenum target, GLfloat s, GLfloat t); +GLAPI void APIENTRY glMultiTexCoord2fvARB (GLenum target, const GLfloat *v); +GLAPI void APIENTRY glMultiTexCoord2iARB (GLenum target, GLint s, GLint t); +GLAPI void APIENTRY glMultiTexCoord2ivARB (GLenum target, const GLint *v); +GLAPI void APIENTRY glMultiTexCoord2sARB (GLenum target, GLshort s, GLshort t); +GLAPI void APIENTRY glMultiTexCoord2svARB (GLenum target, const GLshort *v); +GLAPI void APIENTRY glMultiTexCoord3dARB (GLenum target, GLdouble s, GLdouble t, GLdouble r); +GLAPI void APIENTRY glMultiTexCoord3dvARB (GLenum target, const GLdouble *v); +GLAPI void APIENTRY glMultiTexCoord3fARB (GLenum target, GLfloat s, GLfloat t, GLfloat r); +GLAPI void APIENTRY glMultiTexCoord3fvARB (GLenum target, const GLfloat *v); +GLAPI void APIENTRY glMultiTexCoord3iARB (GLenum target, GLint s, GLint t, GLint r); +GLAPI void APIENTRY glMultiTexCoord3ivARB (GLenum target, const GLint *v); +GLAPI void APIENTRY glMultiTexCoord3sARB (GLenum target, GLshort s, GLshort t, GLshort r); +GLAPI void APIENTRY glMultiTexCoord3svARB (GLenum target, const GLshort *v); +GLAPI void APIENTRY glMultiTexCoord4dARB (GLenum target, GLdouble s, GLdouble t, GLdouble r, GLdouble q); +GLAPI void APIENTRY glMultiTexCoord4dvARB (GLenum target, const GLdouble *v); +GLAPI void APIENTRY glMultiTexCoord4fARB (GLenum target, GLfloat s, GLfloat t, GLfloat r, GLfloat q); +GLAPI void APIENTRY glMultiTexCoord4fvARB (GLenum target, const GLfloat *v); +GLAPI void APIENTRY glMultiTexCoord4iARB (GLenum target, GLint s, GLint t, GLint r, GLint q); +GLAPI void APIENTRY glMultiTexCoord4ivARB (GLenum target, const GLint *v); +GLAPI void APIENTRY glMultiTexCoord4sARB (GLenum target, GLshort s, GLshort t, GLshort r, GLshort q); +GLAPI void APIENTRY glMultiTexCoord4svARB (GLenum target, const GLshort *v); +#endif +#endif /* GL_ARB_multitexture */ + +#ifndef GL_ARB_occlusion_query +#define GL_ARB_occlusion_query 1 +#define GL_QUERY_COUNTER_BITS_ARB 0x8864 +#define GL_CURRENT_QUERY_ARB 0x8865 +#define GL_QUERY_RESULT_ARB 0x8866 +#define GL_QUERY_RESULT_AVAILABLE_ARB 0x8867 +#define GL_SAMPLES_PASSED_ARB 0x8914 +typedef void (APIENTRYP PFNGLGENQUERIESARBPROC) (GLsizei n, GLuint *ids); +typedef void (APIENTRYP PFNGLDELETEQUERIESARBPROC) (GLsizei n, const GLuint *ids); +typedef GLboolean (APIENTRYP PFNGLISQUERYARBPROC) (GLuint id); +typedef void (APIENTRYP PFNGLBEGINQUERYARBPROC) (GLenum target, GLuint id); +typedef void (APIENTRYP PFNGLENDQUERYARBPROC) (GLenum target); +typedef void (APIENTRYP PFNGLGETQUERYIVARBPROC) (GLenum target, GLenum pname, GLint *params); +typedef void (APIENTRYP PFNGLGETQUERYOBJECTIVARBPROC) (GLuint id, GLenum pname, GLint *params); +typedef void (APIENTRYP PFNGLGETQUERYOBJECTUIVARBPROC) (GLuint id, GLenum pname, GLuint *params); +#ifdef GL_GLEXT_PROTOTYPES +GLAPI void APIENTRY glGenQueriesARB (GLsizei n, GLuint *ids); +GLAPI void APIENTRY glDeleteQueriesARB (GLsizei n, const GLuint *ids); +GLAPI GLboolean APIENTRY glIsQueryARB (GLuint id); +GLAPI void APIENTRY glBeginQueryARB (GLenum target, GLuint id); +GLAPI void APIENTRY glEndQueryARB (GLenum target); +GLAPI void APIENTRY glGetQueryivARB (GLenum target, GLenum pname, GLint *params); +GLAPI void APIENTRY glGetQueryObjectivARB (GLuint id, GLenum pname, GLint *params); +GLAPI void APIENTRY glGetQueryObjectuivARB (GLuint id, GLenum pname, GLuint *params); +#endif +#endif /* GL_ARB_occlusion_query */ + +#ifndef GL_ARB_occlusion_query2 +#define GL_ARB_occlusion_query2 1 +#endif /* GL_ARB_occlusion_query2 */ + +#ifndef GL_ARB_pixel_buffer_object +#define GL_ARB_pixel_buffer_object 1 +#define GL_PIXEL_PACK_BUFFER_ARB 0x88EB +#define GL_PIXEL_UNPACK_BUFFER_ARB 0x88EC +#define GL_PIXEL_PACK_BUFFER_BINDING_ARB 0x88ED +#define GL_PIXEL_UNPACK_BUFFER_BINDING_ARB 0x88EF +#endif /* GL_ARB_pixel_buffer_object */ + +#ifndef GL_ARB_point_parameters +#define GL_ARB_point_parameters 1 +#define GL_POINT_SIZE_MIN_ARB 0x8126 +#define GL_POINT_SIZE_MAX_ARB 0x8127 +#define GL_POINT_FADE_THRESHOLD_SIZE_ARB 0x8128 +#define GL_POINT_DISTANCE_ATTENUATION_ARB 0x8129 +typedef void (APIENTRYP PFNGLPOINTPARAMETERFARBPROC) (GLenum pname, GLfloat param); +typedef void (APIENTRYP PFNGLPOINTPARAMETERFVARBPROC) (GLenum pname, const GLfloat *params); +#ifdef GL_GLEXT_PROTOTYPES +GLAPI void APIENTRY glPointParameterfARB (GLenum pname, GLfloat param); +GLAPI void APIENTRY glPointParameterfvARB (GLenum pname, const GLfloat *params); +#endif +#endif /* GL_ARB_point_parameters */ + +#ifndef GL_ARB_point_sprite +#define GL_ARB_point_sprite 1 +#define GL_POINT_SPRITE_ARB 0x8861 +#define GL_COORD_REPLACE_ARB 0x8862 +#endif /* GL_ARB_point_sprite */ + +#ifndef GL_ARB_program_interface_query +#define GL_ARB_program_interface_query 1 +#endif /* GL_ARB_program_interface_query */ + +#ifndef GL_ARB_provoking_vertex +#define GL_ARB_provoking_vertex 1 +#endif /* GL_ARB_provoking_vertex */ + +#ifndef GL_ARB_query_buffer_object +#define GL_ARB_query_buffer_object 1 +#endif /* GL_ARB_query_buffer_object */ + +#ifndef GL_ARB_robust_buffer_access_behavior +#define GL_ARB_robust_buffer_access_behavior 1 +#endif /* GL_ARB_robust_buffer_access_behavior */ + +#ifndef GL_ARB_robustness +#define GL_ARB_robustness 1 +#define GL_CONTEXT_FLAG_ROBUST_ACCESS_BIT_ARB 0x00000004 +#define GL_LOSE_CONTEXT_ON_RESET_ARB 0x8252 +#define GL_GUILTY_CONTEXT_RESET_ARB 0x8253 +#define GL_INNOCENT_CONTEXT_RESET_ARB 0x8254 +#define GL_UNKNOWN_CONTEXT_RESET_ARB 0x8255 +#define GL_RESET_NOTIFICATION_STRATEGY_ARB 0x8256 +#define GL_NO_RESET_NOTIFICATION_ARB 0x8261 +typedef GLenum (APIENTRYP PFNGLGETGRAPHICSRESETSTATUSARBPROC) (void); +typedef void (APIENTRYP PFNGLGETNTEXIMAGEARBPROC) (GLenum target, GLint level, GLenum format, GLenum type, GLsizei bufSize, void *img); +typedef void (APIENTRYP PFNGLREADNPIXELSARBPROC) (GLint x, GLint y, GLsizei width, GLsizei height, GLenum format, GLenum type, GLsizei bufSize, void *data); +typedef void (APIENTRYP PFNGLGETNCOMPRESSEDTEXIMAGEARBPROC) (GLenum target, GLint lod, GLsizei bufSize, void *img); +typedef void (APIENTRYP PFNGLGETNUNIFORMFVARBPROC) (GLuint program, GLint location, GLsizei bufSize, GLfloat *params); +typedef void (APIENTRYP PFNGLGETNUNIFORMIVARBPROC) (GLuint program, GLint location, GLsizei bufSize, GLint *params); +typedef void (APIENTRYP PFNGLGETNUNIFORMUIVARBPROC) (GLuint program, GLint location, GLsizei bufSize, GLuint *params); +typedef void (APIENTRYP PFNGLGETNUNIFORMDVARBPROC) (GLuint program, GLint location, GLsizei bufSize, GLdouble *params); +typedef void (APIENTRYP PFNGLGETNMAPDVARBPROC) (GLenum target, GLenum query, GLsizei bufSize, GLdouble *v); +typedef void (APIENTRYP PFNGLGETNMAPFVARBPROC) (GLenum target, GLenum query, GLsizei bufSize, GLfloat *v); +typedef void (APIENTRYP PFNGLGETNMAPIVARBPROC) (GLenum target, GLenum query, GLsizei bufSize, GLint *v); +typedef void (APIENTRYP PFNGLGETNPIXELMAPFVARBPROC) (GLenum map, GLsizei bufSize, GLfloat *values); +typedef void (APIENTRYP PFNGLGETNPIXELMAPUIVARBPROC) (GLenum map, GLsizei bufSize, GLuint *values); +typedef void (APIENTRYP PFNGLGETNPIXELMAPUSVARBPROC) (GLenum map, GLsizei bufSize, GLushort *values); +typedef void (APIENTRYP PFNGLGETNPOLYGONSTIPPLEARBPROC) (GLsizei bufSize, GLubyte *pattern); +typedef void (APIENTRYP PFNGLGETNCOLORTABLEARBPROC) (GLenum target, GLenum format, GLenum type, GLsizei bufSize, void *table); +typedef void (APIENTRYP PFNGLGETNCONVOLUTIONFILTERARBPROC) (GLenum target, GLenum format, GLenum type, GLsizei bufSize, void *image); +typedef void (APIENTRYP PFNGLGETNSEPARABLEFILTERARBPROC) (GLenum target, GLenum format, GLenum type, GLsizei rowBufSize, void *row, GLsizei columnBufSize, void *column, void *span); +typedef void (APIENTRYP PFNGLGETNHISTOGRAMARBPROC) (GLenum target, GLboolean reset, GLenum format, GLenum type, GLsizei bufSize, void *values); +typedef void (APIENTRYP PFNGLGETNMINMAXARBPROC) (GLenum target, GLboolean reset, GLenum format, GLenum type, GLsizei bufSize, void *values); +#ifdef GL_GLEXT_PROTOTYPES +GLAPI GLenum APIENTRY glGetGraphicsResetStatusARB (void); +GLAPI void APIENTRY glGetnTexImageARB (GLenum target, GLint level, GLenum format, GLenum type, GLsizei bufSize, void *img); +GLAPI void APIENTRY glReadnPixelsARB (GLint x, GLint y, GLsizei width, GLsizei height, GLenum format, GLenum type, GLsizei bufSize, void *data); +GLAPI void APIENTRY glGetnCompressedTexImageARB (GLenum target, GLint lod, GLsizei bufSize, void *img); +GLAPI void APIENTRY glGetnUniformfvARB (GLuint program, GLint location, GLsizei bufSize, GLfloat *params); +GLAPI void APIENTRY glGetnUniformivARB (GLuint program, GLint location, GLsizei bufSize, GLint *params); +GLAPI void APIENTRY glGetnUniformuivARB (GLuint program, GLint location, GLsizei bufSize, GLuint *params); +GLAPI void APIENTRY glGetnUniformdvARB (GLuint program, GLint location, GLsizei bufSize, GLdouble *params); +GLAPI void APIENTRY glGetnMapdvARB (GLenum target, GLenum query, GLsizei bufSize, GLdouble *v); +GLAPI void APIENTRY glGetnMapfvARB (GLenum target, GLenum query, GLsizei bufSize, GLfloat *v); +GLAPI void APIENTRY glGetnMapivARB (GLenum target, GLenum query, GLsizei bufSize, GLint *v); +GLAPI void APIENTRY glGetnPixelMapfvARB (GLenum map, GLsizei bufSize, GLfloat *values); +GLAPI void APIENTRY glGetnPixelMapuivARB (GLenum map, GLsizei bufSize, GLuint *values); +GLAPI void APIENTRY glGetnPixelMapusvARB (GLenum map, GLsizei bufSize, GLushort *values); +GLAPI void APIENTRY glGetnPolygonStippleARB (GLsizei bufSize, GLubyte *pattern); +GLAPI void APIENTRY glGetnColorTableARB (GLenum target, GLenum format, GLenum type, GLsizei bufSize, void *table); +GLAPI void APIENTRY glGetnConvolutionFilterARB (GLenum target, GLenum format, GLenum type, GLsizei bufSize, void *image); +GLAPI void APIENTRY glGetnSeparableFilterARB (GLenum target, GLenum format, GLenum type, GLsizei rowBufSize, void *row, GLsizei columnBufSize, void *column, void *span); +GLAPI void APIENTRY glGetnHistogramARB (GLenum target, GLboolean reset, GLenum format, GLenum type, GLsizei bufSize, void *values); +GLAPI void APIENTRY glGetnMinmaxARB (GLenum target, GLboolean reset, GLenum format, GLenum type, GLsizei bufSize, void *values); +#endif +#endif /* GL_ARB_robustness */ + +#ifndef GL_ARB_robustness_isolation +#define GL_ARB_robustness_isolation 1 +#endif /* GL_ARB_robustness_isolation */ + +#ifndef GL_ARB_sample_shading +#define GL_ARB_sample_shading 1 +#define GL_SAMPLE_SHADING_ARB 0x8C36 +#define GL_MIN_SAMPLE_SHADING_VALUE_ARB 0x8C37 +typedef void (APIENTRYP PFNGLMINSAMPLESHADINGARBPROC) (GLfloat value); +#ifdef GL_GLEXT_PROTOTYPES +GLAPI void APIENTRY glMinSampleShadingARB (GLfloat value); +#endif +#endif /* GL_ARB_sample_shading */ + +#ifndef GL_ARB_sampler_objects +#define GL_ARB_sampler_objects 1 +#endif /* GL_ARB_sampler_objects */ + +#ifndef GL_ARB_seamless_cube_map +#define GL_ARB_seamless_cube_map 1 +#endif /* GL_ARB_seamless_cube_map */ + +#ifndef GL_ARB_seamless_cubemap_per_texture +#define GL_ARB_seamless_cubemap_per_texture 1 +#endif /* GL_ARB_seamless_cubemap_per_texture */ + +#ifndef GL_ARB_separate_shader_objects +#define GL_ARB_separate_shader_objects 1 +#endif /* GL_ARB_separate_shader_objects */ + +#ifndef GL_ARB_shader_atomic_counters +#define GL_ARB_shader_atomic_counters 1 +#endif /* GL_ARB_shader_atomic_counters */ + +#ifndef GL_ARB_shader_bit_encoding +#define GL_ARB_shader_bit_encoding 1 +#endif /* GL_ARB_shader_bit_encoding */ + +#ifndef GL_ARB_shader_draw_parameters +#define GL_ARB_shader_draw_parameters 1 +#endif /* GL_ARB_shader_draw_parameters */ + +#ifndef GL_ARB_shader_group_vote +#define GL_ARB_shader_group_vote 1 +#endif /* GL_ARB_shader_group_vote */ + +#ifndef GL_ARB_shader_image_load_store +#define GL_ARB_shader_image_load_store 1 +#endif /* GL_ARB_shader_image_load_store */ + +#ifndef GL_ARB_shader_image_size +#define GL_ARB_shader_image_size 1 +#endif /* GL_ARB_shader_image_size */ + +#ifndef GL_ARB_shader_objects +#define GL_ARB_shader_objects 1 +#ifdef __APPLE__ +typedef void *GLhandleARB; +#else +typedef unsigned int GLhandleARB; +#endif +typedef char GLcharARB; +#define GL_PROGRAM_OBJECT_ARB 0x8B40 +#define GL_SHADER_OBJECT_ARB 0x8B48 +#define GL_OBJECT_TYPE_ARB 0x8B4E +#define GL_OBJECT_SUBTYPE_ARB 0x8B4F +#define GL_FLOAT_VEC2_ARB 0x8B50 +#define GL_FLOAT_VEC3_ARB 0x8B51 +#define GL_FLOAT_VEC4_ARB 0x8B52 +#define GL_INT_VEC2_ARB 0x8B53 +#define GL_INT_VEC3_ARB 0x8B54 +#define GL_INT_VEC4_ARB 0x8B55 +#define GL_BOOL_ARB 0x8B56 +#define GL_BOOL_VEC2_ARB 0x8B57 +#define GL_BOOL_VEC3_ARB 0x8B58 +#define GL_BOOL_VEC4_ARB 0x8B59 +#define GL_FLOAT_MAT2_ARB 0x8B5A +#define GL_FLOAT_MAT3_ARB 0x8B5B +#define GL_FLOAT_MAT4_ARB 0x8B5C +#define GL_SAMPLER_1D_ARB 0x8B5D +#define GL_SAMPLER_2D_ARB 0x8B5E +#define GL_SAMPLER_3D_ARB 0x8B5F +#define GL_SAMPLER_CUBE_ARB 0x8B60 +#define GL_SAMPLER_1D_SHADOW_ARB 0x8B61 +#define GL_SAMPLER_2D_SHADOW_ARB 0x8B62 +#define GL_SAMPLER_2D_RECT_ARB 0x8B63 +#define GL_SAMPLER_2D_RECT_SHADOW_ARB 0x8B64 +#define GL_OBJECT_DELETE_STATUS_ARB 0x8B80 +#define GL_OBJECT_COMPILE_STATUS_ARB 0x8B81 +#define GL_OBJECT_LINK_STATUS_ARB 0x8B82 +#define GL_OBJECT_VALIDATE_STATUS_ARB 0x8B83 +#define GL_OBJECT_INFO_LOG_LENGTH_ARB 0x8B84 +#define GL_OBJECT_ATTACHED_OBJECTS_ARB 0x8B85 +#define GL_OBJECT_ACTIVE_UNIFORMS_ARB 0x8B86 +#define GL_OBJECT_ACTIVE_UNIFORM_MAX_LENGTH_ARB 0x8B87 +#define GL_OBJECT_SHADER_SOURCE_LENGTH_ARB 0x8B88 +typedef void (APIENTRYP PFNGLDELETEOBJECTARBPROC) (GLhandleARB obj); +typedef GLhandleARB (APIENTRYP PFNGLGETHANDLEARBPROC) (GLenum pname); +typedef void (APIENTRYP PFNGLDETACHOBJECTARBPROC) (GLhandleARB containerObj, GLhandleARB attachedObj); +typedef GLhandleARB (APIENTRYP PFNGLCREATESHADEROBJECTARBPROC) (GLenum shaderType); +typedef void (APIENTRYP PFNGLSHADERSOURCEARBPROC) (GLhandleARB shaderObj, GLsizei count, const GLcharARB **string, const GLint *length); +typedef void (APIENTRYP PFNGLCOMPILESHADERARBPROC) (GLhandleARB shaderObj); +typedef GLhandleARB (APIENTRYP PFNGLCREATEPROGRAMOBJECTARBPROC) (void); +typedef void (APIENTRYP PFNGLATTACHOBJECTARBPROC) (GLhandleARB containerObj, GLhandleARB obj); +typedef void (APIENTRYP PFNGLLINKPROGRAMARBPROC) (GLhandleARB programObj); +typedef void (APIENTRYP PFNGLUSEPROGRAMOBJECTARBPROC) (GLhandleARB programObj); +typedef void (APIENTRYP PFNGLVALIDATEPROGRAMARBPROC) (GLhandleARB programObj); +typedef void (APIENTRYP PFNGLUNIFORM1FARBPROC) (GLint location, GLfloat v0); +typedef void (APIENTRYP PFNGLUNIFORM2FARBPROC) (GLint location, GLfloat v0, GLfloat v1); +typedef void (APIENTRYP PFNGLUNIFORM3FARBPROC) (GLint location, GLfloat v0, GLfloat v1, GLfloat v2); +typedef void (APIENTRYP PFNGLUNIFORM4FARBPROC) (GLint location, GLfloat v0, GLfloat v1, GLfloat v2, GLfloat v3); +typedef void (APIENTRYP PFNGLUNIFORM1IARBPROC) (GLint location, GLint v0); +typedef void (APIENTRYP PFNGLUNIFORM2IARBPROC) (GLint location, GLint v0, GLint v1); +typedef void (APIENTRYP PFNGLUNIFORM3IARBPROC) (GLint location, GLint v0, GLint v1, GLint v2); +typedef void (APIENTRYP PFNGLUNIFORM4IARBPROC) (GLint location, GLint v0, GLint v1, GLint v2, GLint v3); +typedef void (APIENTRYP PFNGLUNIFORM1FVARBPROC) (GLint location, GLsizei count, const GLfloat *value); +typedef void (APIENTRYP PFNGLUNIFORM2FVARBPROC) (GLint location, GLsizei count, const GLfloat *value); +typedef void (APIENTRYP PFNGLUNIFORM3FVARBPROC) (GLint location, GLsizei count, const GLfloat *value); +typedef void (APIENTRYP PFNGLUNIFORM4FVARBPROC) (GLint location, GLsizei count, const GLfloat *value); +typedef void (APIENTRYP PFNGLUNIFORM1IVARBPROC) (GLint location, GLsizei count, const GLint *value); +typedef void (APIENTRYP PFNGLUNIFORM2IVARBPROC) (GLint location, GLsizei count, const GLint *value); +typedef void (APIENTRYP PFNGLUNIFORM3IVARBPROC) (GLint location, GLsizei count, const GLint *value); +typedef void (APIENTRYP PFNGLUNIFORM4IVARBPROC) (GLint location, GLsizei count, const GLint *value); +typedef void (APIENTRYP PFNGLUNIFORMMATRIX2FVARBPROC) (GLint location, GLsizei count, GLboolean transpose, const GLfloat *value); +typedef void (APIENTRYP PFNGLUNIFORMMATRIX3FVARBPROC) (GLint location, GLsizei count, GLboolean transpose, const GLfloat *value); +typedef void (APIENTRYP PFNGLUNIFORMMATRIX4FVARBPROC) (GLint location, GLsizei count, GLboolean transpose, const GLfloat *value); +typedef void (APIENTRYP PFNGLGETOBJECTPARAMETERFVARBPROC) (GLhandleARB obj, GLenum pname, GLfloat *params); +typedef void (APIENTRYP PFNGLGETOBJECTPARAMETERIVARBPROC) (GLhandleARB obj, GLenum pname, GLint *params); +typedef void (APIENTRYP PFNGLGETINFOLOGARBPROC) (GLhandleARB obj, GLsizei maxLength, GLsizei *length, GLcharARB *infoLog); +typedef void (APIENTRYP PFNGLGETATTACHEDOBJECTSARBPROC) (GLhandleARB containerObj, GLsizei maxCount, GLsizei *count, GLhandleARB *obj); +typedef GLint (APIENTRYP PFNGLGETUNIFORMLOCATIONARBPROC) (GLhandleARB programObj, const GLcharARB *name); +typedef void (APIENTRYP PFNGLGETACTIVEUNIFORMARBPROC) (GLhandleARB programObj, GLuint index, GLsizei maxLength, GLsizei *length, GLint *size, GLenum *type, GLcharARB *name); +typedef void (APIENTRYP PFNGLGETUNIFORMFVARBPROC) (GLhandleARB programObj, GLint location, GLfloat *params); +typedef void (APIENTRYP PFNGLGETUNIFORMIVARBPROC) (GLhandleARB programObj, GLint location, GLint *params); +typedef void (APIENTRYP PFNGLGETSHADERSOURCEARBPROC) (GLhandleARB obj, GLsizei maxLength, GLsizei *length, GLcharARB *source); +#ifdef GL_GLEXT_PROTOTYPES +GLAPI void APIENTRY glDeleteObjectARB (GLhandleARB obj); +GLAPI GLhandleARB APIENTRY glGetHandleARB (GLenum pname); +GLAPI void APIENTRY glDetachObjectARB (GLhandleARB containerObj, GLhandleARB attachedObj); +GLAPI GLhandleARB APIENTRY glCreateShaderObjectARB (GLenum shaderType); +GLAPI void APIENTRY glShaderSourceARB (GLhandleARB shaderObj, GLsizei count, const GLcharARB **string, const GLint *length); +GLAPI void APIENTRY glCompileShaderARB (GLhandleARB shaderObj); +GLAPI GLhandleARB APIENTRY glCreateProgramObjectARB (void); +GLAPI void APIENTRY glAttachObjectARB (GLhandleARB containerObj, GLhandleARB obj); +GLAPI void APIENTRY glLinkProgramARB (GLhandleARB programObj); +GLAPI void APIENTRY glUseProgramObjectARB (GLhandleARB programObj); +GLAPI void APIENTRY glValidateProgramARB (GLhandleARB programObj); +GLAPI void APIENTRY glUniform1fARB (GLint location, GLfloat v0); +GLAPI void APIENTRY glUniform2fARB (GLint location, GLfloat v0, GLfloat v1); +GLAPI void APIENTRY glUniform3fARB (GLint location, GLfloat v0, GLfloat v1, GLfloat v2); +GLAPI void APIENTRY glUniform4fARB (GLint location, GLfloat v0, GLfloat v1, GLfloat v2, GLfloat v3); +GLAPI void APIENTRY glUniform1iARB (GLint location, GLint v0); +GLAPI void APIENTRY glUniform2iARB (GLint location, GLint v0, GLint v1); +GLAPI void APIENTRY glUniform3iARB (GLint location, GLint v0, GLint v1, GLint v2); +GLAPI void APIENTRY glUniform4iARB (GLint location, GLint v0, GLint v1, GLint v2, GLint v3); +GLAPI void APIENTRY glUniform1fvARB (GLint location, GLsizei count, const GLfloat *value); +GLAPI void APIENTRY glUniform2fvARB (GLint location, GLsizei count, const GLfloat *value); +GLAPI void APIENTRY glUniform3fvARB (GLint location, GLsizei count, const GLfloat *value); +GLAPI void APIENTRY glUniform4fvARB (GLint location, GLsizei count, const GLfloat *value); +GLAPI void APIENTRY glUniform1ivARB (GLint location, GLsizei count, const GLint *value); +GLAPI void APIENTRY glUniform2ivARB (GLint location, GLsizei count, const GLint *value); +GLAPI void APIENTRY glUniform3ivARB (GLint location, GLsizei count, const GLint *value); +GLAPI void APIENTRY glUniform4ivARB (GLint location, GLsizei count, const GLint *value); +GLAPI void APIENTRY glUniformMatrix2fvARB (GLint location, GLsizei count, GLboolean transpose, const GLfloat *value); +GLAPI void APIENTRY glUniformMatrix3fvARB (GLint location, GLsizei count, GLboolean transpose, const GLfloat *value); +GLAPI void APIENTRY glUniformMatrix4fvARB (GLint location, GLsizei count, GLboolean transpose, const GLfloat *value); +GLAPI void APIENTRY glGetObjectParameterfvARB (GLhandleARB obj, GLenum pname, GLfloat *params); +GLAPI void APIENTRY glGetObjectParameterivARB (GLhandleARB obj, GLenum pname, GLint *params); +GLAPI void APIENTRY glGetInfoLogARB (GLhandleARB obj, GLsizei maxLength, GLsizei *length, GLcharARB *infoLog); +GLAPI void APIENTRY glGetAttachedObjectsARB (GLhandleARB containerObj, GLsizei maxCount, GLsizei *count, GLhandleARB *obj); +GLAPI GLint APIENTRY glGetUniformLocationARB (GLhandleARB programObj, const GLcharARB *name); +GLAPI void APIENTRY glGetActiveUniformARB (GLhandleARB programObj, GLuint index, GLsizei maxLength, GLsizei *length, GLint *size, GLenum *type, GLcharARB *name); +GLAPI void APIENTRY glGetUniformfvARB (GLhandleARB programObj, GLint location, GLfloat *params); +GLAPI void APIENTRY glGetUniformivARB (GLhandleARB programObj, GLint location, GLint *params); +GLAPI void APIENTRY glGetShaderSourceARB (GLhandleARB obj, GLsizei maxLength, GLsizei *length, GLcharARB *source); +#endif +#endif /* GL_ARB_shader_objects */ + +#ifndef GL_ARB_shader_precision +#define GL_ARB_shader_precision 1 +#endif /* GL_ARB_shader_precision */ + +#ifndef GL_ARB_shader_stencil_export +#define GL_ARB_shader_stencil_export 1 +#endif /* GL_ARB_shader_stencil_export */ + +#ifndef GL_ARB_shader_storage_buffer_object +#define GL_ARB_shader_storage_buffer_object 1 +#endif /* GL_ARB_shader_storage_buffer_object */ + +#ifndef GL_ARB_shader_subroutine +#define GL_ARB_shader_subroutine 1 +#endif /* GL_ARB_shader_subroutine */ + +#ifndef GL_ARB_shader_texture_lod +#define GL_ARB_shader_texture_lod 1 +#endif /* GL_ARB_shader_texture_lod */ + +#ifndef GL_ARB_shading_language_100 +#define GL_ARB_shading_language_100 1 +#define GL_SHADING_LANGUAGE_VERSION_ARB 0x8B8C +#endif /* GL_ARB_shading_language_100 */ + +#ifndef GL_ARB_shading_language_420pack +#define GL_ARB_shading_language_420pack 1 +#endif /* GL_ARB_shading_language_420pack */ + +#ifndef GL_ARB_shading_language_include +#define GL_ARB_shading_language_include 1 +#define GL_SHADER_INCLUDE_ARB 0x8DAE +#define GL_NAMED_STRING_LENGTH_ARB 0x8DE9 +#define GL_NAMED_STRING_TYPE_ARB 0x8DEA +typedef void (APIENTRYP PFNGLNAMEDSTRINGARBPROC) (GLenum type, GLint namelen, const GLchar *name, GLint stringlen, const GLchar *string); +typedef void (APIENTRYP PFNGLDELETENAMEDSTRINGARBPROC) (GLint namelen, const GLchar *name); +typedef void (APIENTRYP PFNGLCOMPILESHADERINCLUDEARBPROC) (GLuint shader, GLsizei count, const GLchar *const*path, const GLint *length); +typedef GLboolean (APIENTRYP PFNGLISNAMEDSTRINGARBPROC) (GLint namelen, const GLchar *name); +typedef void (APIENTRYP PFNGLGETNAMEDSTRINGARBPROC) (GLint namelen, const GLchar *name, GLsizei bufSize, GLint *stringlen, GLchar *string); +typedef void (APIENTRYP PFNGLGETNAMEDSTRINGIVARBPROC) (GLint namelen, const GLchar *name, GLenum pname, GLint *params); +#ifdef GL_GLEXT_PROTOTYPES +GLAPI void APIENTRY glNamedStringARB (GLenum type, GLint namelen, const GLchar *name, GLint stringlen, const GLchar *string); +GLAPI void APIENTRY glDeleteNamedStringARB (GLint namelen, const GLchar *name); +GLAPI void APIENTRY glCompileShaderIncludeARB (GLuint shader, GLsizei count, const GLchar *const*path, const GLint *length); +GLAPI GLboolean APIENTRY glIsNamedStringARB (GLint namelen, const GLchar *name); +GLAPI void APIENTRY glGetNamedStringARB (GLint namelen, const GLchar *name, GLsizei bufSize, GLint *stringlen, GLchar *string); +GLAPI void APIENTRY glGetNamedStringivARB (GLint namelen, const GLchar *name, GLenum pname, GLint *params); +#endif +#endif /* GL_ARB_shading_language_include */ + +#ifndef GL_ARB_shading_language_packing +#define GL_ARB_shading_language_packing 1 +#endif /* GL_ARB_shading_language_packing */ + +#ifndef GL_ARB_shadow +#define GL_ARB_shadow 1 +#define GL_TEXTURE_COMPARE_MODE_ARB 0x884C +#define GL_TEXTURE_COMPARE_FUNC_ARB 0x884D +#define GL_COMPARE_R_TO_TEXTURE_ARB 0x884E +#endif /* GL_ARB_shadow */ + +#ifndef GL_ARB_shadow_ambient +#define GL_ARB_shadow_ambient 1 +#define GL_TEXTURE_COMPARE_FAIL_VALUE_ARB 0x80BF +#endif /* GL_ARB_shadow_ambient */ + +#ifndef GL_ARB_sparse_texture +#define GL_ARB_sparse_texture 1 +#define GL_TEXTURE_SPARSE_ARB 0x91A6 +#define GL_VIRTUAL_PAGE_SIZE_INDEX_ARB 0x91A7 +#define GL_MIN_SPARSE_LEVEL_ARB 0x919B +#define GL_NUM_VIRTUAL_PAGE_SIZES_ARB 0x91A8 +#define GL_VIRTUAL_PAGE_SIZE_X_ARB 0x9195 +#define GL_VIRTUAL_PAGE_SIZE_Y_ARB 0x9196 +#define GL_VIRTUAL_PAGE_SIZE_Z_ARB 0x9197 +#define GL_MAX_SPARSE_TEXTURE_SIZE_ARB 0x9198 +#define GL_MAX_SPARSE_3D_TEXTURE_SIZE_ARB 0x9199 +#define GL_MAX_SPARSE_ARRAY_TEXTURE_LAYERS_ARB 0x919A +#define GL_SPARSE_TEXTURE_FULL_ARRAY_CUBE_MIPMAPS_ARB 0x91A9 +typedef void (APIENTRYP PFNGLTEXPAGECOMMITMENTARBPROC) (GLenum target, GLint level, GLint xoffset, GLint yoffset, GLint zoffset, GLsizei width, GLsizei height, GLsizei depth, GLboolean resident); +#ifdef GL_GLEXT_PROTOTYPES +GLAPI void APIENTRY glTexPageCommitmentARB (GLenum target, GLint level, GLint xoffset, GLint yoffset, GLint zoffset, GLsizei width, GLsizei height, GLsizei depth, GLboolean resident); +#endif +#endif /* GL_ARB_sparse_texture */ + +#ifndef GL_ARB_stencil_texturing +#define GL_ARB_stencil_texturing 1 +#endif /* GL_ARB_stencil_texturing */ + +#ifndef GL_ARB_sync +#define GL_ARB_sync 1 +#endif /* GL_ARB_sync */ + +#ifndef GL_ARB_tessellation_shader +#define GL_ARB_tessellation_shader 1 +#endif /* GL_ARB_tessellation_shader */ + +#ifndef GL_ARB_texture_border_clamp +#define GL_ARB_texture_border_clamp 1 +#define GL_CLAMP_TO_BORDER_ARB 0x812D +#endif /* GL_ARB_texture_border_clamp */ + +#ifndef GL_ARB_texture_buffer_object +#define GL_ARB_texture_buffer_object 1 +#define GL_TEXTURE_BUFFER_ARB 0x8C2A +#define GL_MAX_TEXTURE_BUFFER_SIZE_ARB 0x8C2B +#define GL_TEXTURE_BINDING_BUFFER_ARB 0x8C2C +#define GL_TEXTURE_BUFFER_DATA_STORE_BINDING_ARB 0x8C2D +#define GL_TEXTURE_BUFFER_FORMAT_ARB 0x8C2E +typedef void (APIENTRYP PFNGLTEXBUFFERARBPROC) (GLenum target, GLenum internalformat, GLuint buffer); +#ifdef GL_GLEXT_PROTOTYPES +GLAPI void APIENTRY glTexBufferARB (GLenum target, GLenum internalformat, GLuint buffer); +#endif +#endif /* GL_ARB_texture_buffer_object */ + +#ifndef GL_ARB_texture_buffer_object_rgb32 +#define GL_ARB_texture_buffer_object_rgb32 1 +#endif /* GL_ARB_texture_buffer_object_rgb32 */ + +#ifndef GL_ARB_texture_buffer_range +#define GL_ARB_texture_buffer_range 1 +#endif /* GL_ARB_texture_buffer_range */ + +#ifndef GL_ARB_texture_compression +#define GL_ARB_texture_compression 1 +#define GL_COMPRESSED_ALPHA_ARB 0x84E9 +#define GL_COMPRESSED_LUMINANCE_ARB 0x84EA +#define GL_COMPRESSED_LUMINANCE_ALPHA_ARB 0x84EB +#define GL_COMPRESSED_INTENSITY_ARB 0x84EC +#define GL_COMPRESSED_RGB_ARB 0x84ED +#define GL_COMPRESSED_RGBA_ARB 0x84EE +#define GL_TEXTURE_COMPRESSION_HINT_ARB 0x84EF +#define GL_TEXTURE_COMPRESSED_IMAGE_SIZE_ARB 0x86A0 +#define GL_TEXTURE_COMPRESSED_ARB 0x86A1 +#define GL_NUM_COMPRESSED_TEXTURE_FORMATS_ARB 0x86A2 +#define GL_COMPRESSED_TEXTURE_FORMATS_ARB 0x86A3 +typedef void (APIENTRYP PFNGLCOMPRESSEDTEXIMAGE3DARBPROC) (GLenum target, GLint level, GLenum internalformat, GLsizei width, GLsizei height, GLsizei depth, GLint border, GLsizei imageSize, const void *data); +typedef void (APIENTRYP PFNGLCOMPRESSEDTEXIMAGE2DARBPROC) (GLenum target, GLint level, GLenum internalformat, GLsizei width, GLsizei height, GLint border, GLsizei imageSize, const void *data); +typedef void (APIENTRYP PFNGLCOMPRESSEDTEXIMAGE1DARBPROC) (GLenum target, GLint level, GLenum internalformat, GLsizei width, GLint border, GLsizei imageSize, const void *data); +typedef void (APIENTRYP PFNGLCOMPRESSEDTEXSUBIMAGE3DARBPROC) (GLenum target, GLint level, GLint xoffset, GLint yoffset, GLint zoffset, GLsizei width, GLsizei height, GLsizei depth, GLenum format, GLsizei imageSize, const void *data); +typedef void (APIENTRYP PFNGLCOMPRESSEDTEXSUBIMAGE2DARBPROC) (GLenum target, GLint level, GLint xoffset, GLint yoffset, GLsizei width, GLsizei height, GLenum format, GLsizei imageSize, const void *data); +typedef void (APIENTRYP PFNGLCOMPRESSEDTEXSUBIMAGE1DARBPROC) (GLenum target, GLint level, GLint xoffset, GLsizei width, GLenum format, GLsizei imageSize, const void *data); +typedef void (APIENTRYP PFNGLGETCOMPRESSEDTEXIMAGEARBPROC) (GLenum target, GLint level, void *img); +#ifdef GL_GLEXT_PROTOTYPES +GLAPI void APIENTRY glCompressedTexImage3DARB (GLenum target, GLint level, GLenum internalformat, GLsizei width, GLsizei height, GLsizei depth, GLint border, GLsizei imageSize, const void *data); +GLAPI void APIENTRY glCompressedTexImage2DARB (GLenum target, GLint level, GLenum internalformat, GLsizei width, GLsizei height, GLint border, GLsizei imageSize, const void *data); +GLAPI void APIENTRY glCompressedTexImage1DARB (GLenum target, GLint level, GLenum internalformat, GLsizei width, GLint border, GLsizei imageSize, const void *data); +GLAPI void APIENTRY glCompressedTexSubImage3DARB (GLenum target, GLint level, GLint xoffset, GLint yoffset, GLint zoffset, GLsizei width, GLsizei height, GLsizei depth, GLenum format, GLsizei imageSize, const void *data); +GLAPI void APIENTRY glCompressedTexSubImage2DARB (GLenum target, GLint level, GLint xoffset, GLint yoffset, GLsizei width, GLsizei height, GLenum format, GLsizei imageSize, const void *data); +GLAPI void APIENTRY glCompressedTexSubImage1DARB (GLenum target, GLint level, GLint xoffset, GLsizei width, GLenum format, GLsizei imageSize, const void *data); +GLAPI void APIENTRY glGetCompressedTexImageARB (GLenum target, GLint level, void *img); +#endif +#endif /* GL_ARB_texture_compression */ + +#ifndef GL_ARB_texture_compression_bptc +#define GL_ARB_texture_compression_bptc 1 +#define GL_COMPRESSED_RGBA_BPTC_UNORM_ARB 0x8E8C +#define GL_COMPRESSED_SRGB_ALPHA_BPTC_UNORM_ARB 0x8E8D +#define GL_COMPRESSED_RGB_BPTC_SIGNED_FLOAT_ARB 0x8E8E +#define GL_COMPRESSED_RGB_BPTC_UNSIGNED_FLOAT_ARB 0x8E8F +#endif /* GL_ARB_texture_compression_bptc */ + +#ifndef GL_ARB_texture_compression_rgtc +#define GL_ARB_texture_compression_rgtc 1 +#endif /* GL_ARB_texture_compression_rgtc */ + +#ifndef GL_ARB_texture_cube_map +#define GL_ARB_texture_cube_map 1 +#define GL_NORMAL_MAP_ARB 0x8511 +#define GL_REFLECTION_MAP_ARB 0x8512 +#define GL_TEXTURE_CUBE_MAP_ARB 0x8513 +#define GL_TEXTURE_BINDING_CUBE_MAP_ARB 0x8514 +#define GL_TEXTURE_CUBE_MAP_POSITIVE_X_ARB 0x8515 +#define GL_TEXTURE_CUBE_MAP_NEGATIVE_X_ARB 0x8516 +#define GL_TEXTURE_CUBE_MAP_POSITIVE_Y_ARB 0x8517 +#define GL_TEXTURE_CUBE_MAP_NEGATIVE_Y_ARB 0x8518 +#define GL_TEXTURE_CUBE_MAP_POSITIVE_Z_ARB 0x8519 +#define GL_TEXTURE_CUBE_MAP_NEGATIVE_Z_ARB 0x851A +#define GL_PROXY_TEXTURE_CUBE_MAP_ARB 0x851B +#define GL_MAX_CUBE_MAP_TEXTURE_SIZE_ARB 0x851C +#endif /* GL_ARB_texture_cube_map */ + +#ifndef GL_ARB_texture_cube_map_array +#define GL_ARB_texture_cube_map_array 1 +#define GL_TEXTURE_CUBE_MAP_ARRAY_ARB 0x9009 +#define GL_TEXTURE_BINDING_CUBE_MAP_ARRAY_ARB 0x900A +#define GL_PROXY_TEXTURE_CUBE_MAP_ARRAY_ARB 0x900B +#define GL_SAMPLER_CUBE_MAP_ARRAY_ARB 0x900C +#define GL_SAMPLER_CUBE_MAP_ARRAY_SHADOW_ARB 0x900D +#define GL_INT_SAMPLER_CUBE_MAP_ARRAY_ARB 0x900E +#define GL_UNSIGNED_INT_SAMPLER_CUBE_MAP_ARRAY_ARB 0x900F +#endif /* GL_ARB_texture_cube_map_array */ + +#ifndef GL_ARB_texture_env_add +#define GL_ARB_texture_env_add 1 +#endif /* GL_ARB_texture_env_add */ + +#ifndef GL_ARB_texture_env_combine +#define GL_ARB_texture_env_combine 1 +#define GL_COMBINE_ARB 0x8570 +#define GL_COMBINE_RGB_ARB 0x8571 +#define GL_COMBINE_ALPHA_ARB 0x8572 +#define GL_SOURCE0_RGB_ARB 0x8580 +#define GL_SOURCE1_RGB_ARB 0x8581 +#define GL_SOURCE2_RGB_ARB 0x8582 +#define GL_SOURCE0_ALPHA_ARB 0x8588 +#define GL_SOURCE1_ALPHA_ARB 0x8589 +#define GL_SOURCE2_ALPHA_ARB 0x858A +#define GL_OPERAND0_RGB_ARB 0x8590 +#define GL_OPERAND1_RGB_ARB 0x8591 +#define GL_OPERAND2_RGB_ARB 0x8592 +#define GL_OPERAND0_ALPHA_ARB 0x8598 +#define GL_OPERAND1_ALPHA_ARB 0x8599 +#define GL_OPERAND2_ALPHA_ARB 0x859A +#define GL_RGB_SCALE_ARB 0x8573 +#define GL_ADD_SIGNED_ARB 0x8574 +#define GL_INTERPOLATE_ARB 0x8575 +#define GL_SUBTRACT_ARB 0x84E7 +#define GL_CONSTANT_ARB 0x8576 +#define GL_PRIMARY_COLOR_ARB 0x8577 +#define GL_PREVIOUS_ARB 0x8578 +#endif /* GL_ARB_texture_env_combine */ + +#ifndef GL_ARB_texture_env_crossbar +#define GL_ARB_texture_env_crossbar 1 +#endif /* GL_ARB_texture_env_crossbar */ + +#ifndef GL_ARB_texture_env_dot3 +#define GL_ARB_texture_env_dot3 1 +#define GL_DOT3_RGB_ARB 0x86AE +#define GL_DOT3_RGBA_ARB 0x86AF +#endif /* GL_ARB_texture_env_dot3 */ + +#ifndef GL_ARB_texture_float +#define GL_ARB_texture_float 1 +#define GL_TEXTURE_RED_TYPE_ARB 0x8C10 +#define GL_TEXTURE_GREEN_TYPE_ARB 0x8C11 +#define GL_TEXTURE_BLUE_TYPE_ARB 0x8C12 +#define GL_TEXTURE_ALPHA_TYPE_ARB 0x8C13 +#define GL_TEXTURE_LUMINANCE_TYPE_ARB 0x8C14 +#define GL_TEXTURE_INTENSITY_TYPE_ARB 0x8C15 +#define GL_TEXTURE_DEPTH_TYPE_ARB 0x8C16 +#define GL_UNSIGNED_NORMALIZED_ARB 0x8C17 +#define GL_RGBA32F_ARB 0x8814 +#define GL_RGB32F_ARB 0x8815 +#define GL_ALPHA32F_ARB 0x8816 +#define GL_INTENSITY32F_ARB 0x8817 +#define GL_LUMINANCE32F_ARB 0x8818 +#define GL_LUMINANCE_ALPHA32F_ARB 0x8819 +#define GL_RGBA16F_ARB 0x881A +#define GL_RGB16F_ARB 0x881B +#define GL_ALPHA16F_ARB 0x881C +#define GL_INTENSITY16F_ARB 0x881D +#define GL_LUMINANCE16F_ARB 0x881E +#define GL_LUMINANCE_ALPHA16F_ARB 0x881F +#endif /* GL_ARB_texture_float */ + +#ifndef GL_ARB_texture_gather +#define GL_ARB_texture_gather 1 +#define GL_MIN_PROGRAM_TEXTURE_GATHER_OFFSET_ARB 0x8E5E +#define GL_MAX_PROGRAM_TEXTURE_GATHER_OFFSET_ARB 0x8E5F +#define GL_MAX_PROGRAM_TEXTURE_GATHER_COMPONENTS_ARB 0x8F9F +#endif /* GL_ARB_texture_gather */ + +#ifndef GL_ARB_texture_mirror_clamp_to_edge +#define GL_ARB_texture_mirror_clamp_to_edge 1 +#endif /* GL_ARB_texture_mirror_clamp_to_edge */ + +#ifndef GL_ARB_texture_mirrored_repeat +#define GL_ARB_texture_mirrored_repeat 1 +#define GL_MIRRORED_REPEAT_ARB 0x8370 +#endif /* GL_ARB_texture_mirrored_repeat */ + +#ifndef GL_ARB_texture_multisample +#define GL_ARB_texture_multisample 1 +#endif /* GL_ARB_texture_multisample */ + +#ifndef GL_ARB_texture_non_power_of_two +#define GL_ARB_texture_non_power_of_two 1 +#endif /* GL_ARB_texture_non_power_of_two */ + +#ifndef GL_ARB_texture_query_levels +#define GL_ARB_texture_query_levels 1 +#endif /* GL_ARB_texture_query_levels */ + +#ifndef GL_ARB_texture_query_lod +#define GL_ARB_texture_query_lod 1 +#endif /* GL_ARB_texture_query_lod */ + +#ifndef GL_ARB_texture_rectangle +#define GL_ARB_texture_rectangle 1 +#define GL_TEXTURE_RECTANGLE_ARB 0x84F5 +#define GL_TEXTURE_BINDING_RECTANGLE_ARB 0x84F6 +#define GL_PROXY_TEXTURE_RECTANGLE_ARB 0x84F7 +#define GL_MAX_RECTANGLE_TEXTURE_SIZE_ARB 0x84F8 +#endif /* GL_ARB_texture_rectangle */ + +#ifndef GL_ARB_texture_rg +#define GL_ARB_texture_rg 1 +#endif /* GL_ARB_texture_rg */ + +#ifndef GL_ARB_texture_rgb10_a2ui +#define GL_ARB_texture_rgb10_a2ui 1 +#endif /* GL_ARB_texture_rgb10_a2ui */ + +#ifndef GL_ARB_texture_stencil8 +#define GL_ARB_texture_stencil8 1 +#endif /* GL_ARB_texture_stencil8 */ + +#ifndef GL_ARB_texture_storage +#define GL_ARB_texture_storage 1 +#endif /* GL_ARB_texture_storage */ + +#ifndef GL_ARB_texture_storage_multisample +#define GL_ARB_texture_storage_multisample 1 +#endif /* GL_ARB_texture_storage_multisample */ + +#ifndef GL_ARB_texture_swizzle +#define GL_ARB_texture_swizzle 1 +#endif /* GL_ARB_texture_swizzle */ + +#ifndef GL_ARB_texture_view +#define GL_ARB_texture_view 1 +#endif /* GL_ARB_texture_view */ + +#ifndef GL_ARB_timer_query +#define GL_ARB_timer_query 1 +#endif /* GL_ARB_timer_query */ + +#ifndef GL_ARB_transform_feedback2 +#define GL_ARB_transform_feedback2 1 +#define GL_TRANSFORM_FEEDBACK_PAUSED 0x8E23 +#define GL_TRANSFORM_FEEDBACK_ACTIVE 0x8E24 +#endif /* GL_ARB_transform_feedback2 */ + +#ifndef GL_ARB_transform_feedback3 +#define GL_ARB_transform_feedback3 1 +#endif /* GL_ARB_transform_feedback3 */ + +#ifndef GL_ARB_transform_feedback_instanced +#define GL_ARB_transform_feedback_instanced 1 +#endif /* GL_ARB_transform_feedback_instanced */ + +#ifndef GL_ARB_transpose_matrix +#define GL_ARB_transpose_matrix 1 +#define GL_TRANSPOSE_MODELVIEW_MATRIX_ARB 0x84E3 +#define GL_TRANSPOSE_PROJECTION_MATRIX_ARB 0x84E4 +#define GL_TRANSPOSE_TEXTURE_MATRIX_ARB 0x84E5 +#define GL_TRANSPOSE_COLOR_MATRIX_ARB 0x84E6 +typedef void (APIENTRYP PFNGLLOADTRANSPOSEMATRIXFARBPROC) (const GLfloat *m); +typedef void (APIENTRYP PFNGLLOADTRANSPOSEMATRIXDARBPROC) (const GLdouble *m); +typedef void (APIENTRYP PFNGLMULTTRANSPOSEMATRIXFARBPROC) (const GLfloat *m); +typedef void (APIENTRYP PFNGLMULTTRANSPOSEMATRIXDARBPROC) (const GLdouble *m); +#ifdef GL_GLEXT_PROTOTYPES +GLAPI void APIENTRY glLoadTransposeMatrixfARB (const GLfloat *m); +GLAPI void APIENTRY glLoadTransposeMatrixdARB (const GLdouble *m); +GLAPI void APIENTRY glMultTransposeMatrixfARB (const GLfloat *m); +GLAPI void APIENTRY glMultTransposeMatrixdARB (const GLdouble *m); +#endif +#endif /* GL_ARB_transpose_matrix */ + +#ifndef GL_ARB_uniform_buffer_object +#define GL_ARB_uniform_buffer_object 1 +#define GL_MAX_GEOMETRY_UNIFORM_BLOCKS 0x8A2C +#define GL_MAX_COMBINED_GEOMETRY_UNIFORM_COMPONENTS 0x8A32 +#define GL_UNIFORM_BLOCK_REFERENCED_BY_GEOMETRY_SHADER 0x8A45 +#endif /* GL_ARB_uniform_buffer_object */ + +#ifndef GL_ARB_vertex_array_bgra +#define GL_ARB_vertex_array_bgra 1 +#endif /* GL_ARB_vertex_array_bgra */ + +#ifndef GL_ARB_vertex_array_object +#define GL_ARB_vertex_array_object 1 +#endif /* GL_ARB_vertex_array_object */ + +#ifndef GL_ARB_vertex_attrib_64bit +#define GL_ARB_vertex_attrib_64bit 1 +#endif /* GL_ARB_vertex_attrib_64bit */ + +#ifndef GL_ARB_vertex_attrib_binding +#define GL_ARB_vertex_attrib_binding 1 +#endif /* GL_ARB_vertex_attrib_binding */ + +#ifndef GL_ARB_vertex_blend +#define GL_ARB_vertex_blend 1 +#define GL_MAX_VERTEX_UNITS_ARB 0x86A4 +#define GL_ACTIVE_VERTEX_UNITS_ARB 0x86A5 +#define GL_WEIGHT_SUM_UNITY_ARB 0x86A6 +#define GL_VERTEX_BLEND_ARB 0x86A7 +#define GL_CURRENT_WEIGHT_ARB 0x86A8 +#define GL_WEIGHT_ARRAY_TYPE_ARB 0x86A9 +#define GL_WEIGHT_ARRAY_STRIDE_ARB 0x86AA +#define GL_WEIGHT_ARRAY_SIZE_ARB 0x86AB +#define GL_WEIGHT_ARRAY_POINTER_ARB 0x86AC +#define GL_WEIGHT_ARRAY_ARB 0x86AD +#define GL_MODELVIEW0_ARB 0x1700 +#define GL_MODELVIEW1_ARB 0x850A +#define GL_MODELVIEW2_ARB 0x8722 +#define GL_MODELVIEW3_ARB 0x8723 +#define GL_MODELVIEW4_ARB 0x8724 +#define GL_MODELVIEW5_ARB 0x8725 +#define GL_MODELVIEW6_ARB 0x8726 +#define GL_MODELVIEW7_ARB 0x8727 +#define GL_MODELVIEW8_ARB 0x8728 +#define GL_MODELVIEW9_ARB 0x8729 +#define GL_MODELVIEW10_ARB 0x872A +#define GL_MODELVIEW11_ARB 0x872B +#define GL_MODELVIEW12_ARB 0x872C +#define GL_MODELVIEW13_ARB 0x872D +#define GL_MODELVIEW14_ARB 0x872E +#define GL_MODELVIEW15_ARB 0x872F +#define GL_MODELVIEW16_ARB 0x8730 +#define GL_MODELVIEW17_ARB 0x8731 +#define GL_MODELVIEW18_ARB 0x8732 +#define GL_MODELVIEW19_ARB 0x8733 +#define GL_MODELVIEW20_ARB 0x8734 +#define GL_MODELVIEW21_ARB 0x8735 +#define GL_MODELVIEW22_ARB 0x8736 +#define GL_MODELVIEW23_ARB 0x8737 +#define GL_MODELVIEW24_ARB 0x8738 +#define GL_MODELVIEW25_ARB 0x8739 +#define GL_MODELVIEW26_ARB 0x873A +#define GL_MODELVIEW27_ARB 0x873B +#define GL_MODELVIEW28_ARB 0x873C +#define GL_MODELVIEW29_ARB 0x873D +#define GL_MODELVIEW30_ARB 0x873E +#define GL_MODELVIEW31_ARB 0x873F +typedef void (APIENTRYP PFNGLWEIGHTBVARBPROC) (GLint size, const GLbyte *weights); +typedef void (APIENTRYP PFNGLWEIGHTSVARBPROC) (GLint size, const GLshort *weights); +typedef void (APIENTRYP PFNGLWEIGHTIVARBPROC) (GLint size, const GLint *weights); +typedef void (APIENTRYP PFNGLWEIGHTFVARBPROC) (GLint size, const GLfloat *weights); +typedef void (APIENTRYP PFNGLWEIGHTDVARBPROC) (GLint size, const GLdouble *weights); +typedef void (APIENTRYP PFNGLWEIGHTUBVARBPROC) (GLint size, const GLubyte *weights); +typedef void (APIENTRYP PFNGLWEIGHTUSVARBPROC) (GLint size, const GLushort *weights); +typedef void (APIENTRYP PFNGLWEIGHTUIVARBPROC) (GLint size, const GLuint *weights); +typedef void (APIENTRYP PFNGLWEIGHTPOINTERARBPROC) (GLint size, GLenum type, GLsizei stride, const void *pointer); +typedef void (APIENTRYP PFNGLVERTEXBLENDARBPROC) (GLint count); +#ifdef GL_GLEXT_PROTOTYPES +GLAPI void APIENTRY glWeightbvARB (GLint size, const GLbyte *weights); +GLAPI void APIENTRY glWeightsvARB (GLint size, const GLshort *weights); +GLAPI void APIENTRY glWeightivARB (GLint size, const GLint *weights); +GLAPI void APIENTRY glWeightfvARB (GLint size, const GLfloat *weights); +GLAPI void APIENTRY glWeightdvARB (GLint size, const GLdouble *weights); +GLAPI void APIENTRY glWeightubvARB (GLint size, const GLubyte *weights); +GLAPI void APIENTRY glWeightusvARB (GLint size, const GLushort *weights); +GLAPI void APIENTRY glWeightuivARB (GLint size, const GLuint *weights); +GLAPI void APIENTRY glWeightPointerARB (GLint size, GLenum type, GLsizei stride, const void *pointer); +GLAPI void APIENTRY glVertexBlendARB (GLint count); +#endif +#endif /* GL_ARB_vertex_blend */ + +#ifndef GL_ARB_vertex_buffer_object +#define GL_ARB_vertex_buffer_object 1 +typedef ptrdiff_t GLsizeiptrARB; +typedef ptrdiff_t GLintptrARB; +#define GL_BUFFER_SIZE_ARB 0x8764 +#define GL_BUFFER_USAGE_ARB 0x8765 +#define GL_ARRAY_BUFFER_ARB 0x8892 +#define GL_ELEMENT_ARRAY_BUFFER_ARB 0x8893 +#define GL_ARRAY_BUFFER_BINDING_ARB 0x8894 +#define GL_ELEMENT_ARRAY_BUFFER_BINDING_ARB 0x8895 +#define GL_VERTEX_ARRAY_BUFFER_BINDING_ARB 0x8896 +#define GL_NORMAL_ARRAY_BUFFER_BINDING_ARB 0x8897 +#define GL_COLOR_ARRAY_BUFFER_BINDING_ARB 0x8898 +#define GL_INDEX_ARRAY_BUFFER_BINDING_ARB 0x8899 +#define GL_TEXTURE_COORD_ARRAY_BUFFER_BINDING_ARB 0x889A +#define GL_EDGE_FLAG_ARRAY_BUFFER_BINDING_ARB 0x889B +#define GL_SECONDARY_COLOR_ARRAY_BUFFER_BINDING_ARB 0x889C +#define GL_FOG_COORDINATE_ARRAY_BUFFER_BINDING_ARB 0x889D +#define GL_WEIGHT_ARRAY_BUFFER_BINDING_ARB 0x889E +#define GL_VERTEX_ATTRIB_ARRAY_BUFFER_BINDING_ARB 0x889F +#define GL_READ_ONLY_ARB 0x88B8 +#define GL_WRITE_ONLY_ARB 0x88B9 +#define GL_READ_WRITE_ARB 0x88BA +#define GL_BUFFER_ACCESS_ARB 0x88BB +#define GL_BUFFER_MAPPED_ARB 0x88BC +#define GL_BUFFER_MAP_POINTER_ARB 0x88BD +#define GL_STREAM_DRAW_ARB 0x88E0 +#define GL_STREAM_READ_ARB 0x88E1 +#define GL_STREAM_COPY_ARB 0x88E2 +#define GL_STATIC_DRAW_ARB 0x88E4 +#define GL_STATIC_READ_ARB 0x88E5 +#define GL_STATIC_COPY_ARB 0x88E6 +#define GL_DYNAMIC_DRAW_ARB 0x88E8 +#define GL_DYNAMIC_READ_ARB 0x88E9 +#define GL_DYNAMIC_COPY_ARB 0x88EA +typedef void (APIENTRYP PFNGLBINDBUFFERARBPROC) (GLenum target, GLuint buffer); +typedef void (APIENTRYP PFNGLDELETEBUFFERSARBPROC) (GLsizei n, const GLuint *buffers); +typedef void (APIENTRYP PFNGLGENBUFFERSARBPROC) (GLsizei n, GLuint *buffers); +typedef GLboolean (APIENTRYP PFNGLISBUFFERARBPROC) (GLuint buffer); +typedef void (APIENTRYP PFNGLBUFFERDATAARBPROC) (GLenum target, GLsizeiptrARB size, const void *data, GLenum usage); +typedef void (APIENTRYP PFNGLBUFFERSUBDATAARBPROC) (GLenum target, GLintptrARB offset, GLsizeiptrARB size, const void *data); +typedef void (APIENTRYP PFNGLGETBUFFERSUBDATAARBPROC) (GLenum target, GLintptrARB offset, GLsizeiptrARB size, void *data); +typedef void *(APIENTRYP PFNGLMAPBUFFERARBPROC) (GLenum target, GLenum access); +typedef GLboolean (APIENTRYP PFNGLUNMAPBUFFERARBPROC) (GLenum target); +typedef void (APIENTRYP PFNGLGETBUFFERPARAMETERIVARBPROC) (GLenum target, GLenum pname, GLint *params); +typedef void (APIENTRYP PFNGLGETBUFFERPOINTERVARBPROC) (GLenum target, GLenum pname, void **params); +#ifdef GL_GLEXT_PROTOTYPES +GLAPI void APIENTRY glBindBufferARB (GLenum target, GLuint buffer); +GLAPI void APIENTRY glDeleteBuffersARB (GLsizei n, const GLuint *buffers); +GLAPI void APIENTRY glGenBuffersARB (GLsizei n, GLuint *buffers); +GLAPI GLboolean APIENTRY glIsBufferARB (GLuint buffer); +GLAPI void APIENTRY glBufferDataARB (GLenum target, GLsizeiptrARB size, const void *data, GLenum usage); +GLAPI void APIENTRY glBufferSubDataARB (GLenum target, GLintptrARB offset, GLsizeiptrARB size, const void *data); +GLAPI void APIENTRY glGetBufferSubDataARB (GLenum target, GLintptrARB offset, GLsizeiptrARB size, void *data); +GLAPI void *APIENTRY glMapBufferARB (GLenum target, GLenum access); +GLAPI GLboolean APIENTRY glUnmapBufferARB (GLenum target); +GLAPI void APIENTRY glGetBufferParameterivARB (GLenum target, GLenum pname, GLint *params); +GLAPI void APIENTRY glGetBufferPointervARB (GLenum target, GLenum pname, void **params); +#endif +#endif /* GL_ARB_vertex_buffer_object */ + +#ifndef GL_ARB_vertex_program +#define GL_ARB_vertex_program 1 +#define GL_COLOR_SUM_ARB 0x8458 +#define GL_VERTEX_PROGRAM_ARB 0x8620 +#define GL_VERTEX_ATTRIB_ARRAY_ENABLED_ARB 0x8622 +#define GL_VERTEX_ATTRIB_ARRAY_SIZE_ARB 0x8623 +#define GL_VERTEX_ATTRIB_ARRAY_STRIDE_ARB 0x8624 +#define GL_VERTEX_ATTRIB_ARRAY_TYPE_ARB 0x8625 +#define GL_CURRENT_VERTEX_ATTRIB_ARB 0x8626 +#define GL_VERTEX_PROGRAM_POINT_SIZE_ARB 0x8642 +#define GL_VERTEX_PROGRAM_TWO_SIDE_ARB 0x8643 +#define GL_VERTEX_ATTRIB_ARRAY_POINTER_ARB 0x8645 +#define GL_MAX_VERTEX_ATTRIBS_ARB 0x8869 +#define GL_VERTEX_ATTRIB_ARRAY_NORMALIZED_ARB 0x886A +#define GL_PROGRAM_ADDRESS_REGISTERS_ARB 0x88B0 +#define GL_MAX_PROGRAM_ADDRESS_REGISTERS_ARB 0x88B1 +#define GL_PROGRAM_NATIVE_ADDRESS_REGISTERS_ARB 0x88B2 +#define GL_MAX_PROGRAM_NATIVE_ADDRESS_REGISTERS_ARB 0x88B3 +typedef void (APIENTRYP PFNGLVERTEXATTRIB1DARBPROC) (GLuint index, GLdouble x); +typedef void (APIENTRYP PFNGLVERTEXATTRIB1DVARBPROC) (GLuint index, const GLdouble *v); +typedef void (APIENTRYP PFNGLVERTEXATTRIB1FARBPROC) (GLuint index, GLfloat x); +typedef void (APIENTRYP PFNGLVERTEXATTRIB1FVARBPROC) (GLuint index, const GLfloat *v); +typedef void (APIENTRYP PFNGLVERTEXATTRIB1SARBPROC) (GLuint index, GLshort x); +typedef void (APIENTRYP PFNGLVERTEXATTRIB1SVARBPROC) (GLuint index, const GLshort *v); +typedef void (APIENTRYP PFNGLVERTEXATTRIB2DARBPROC) (GLuint index, GLdouble x, GLdouble y); +typedef void (APIENTRYP PFNGLVERTEXATTRIB2DVARBPROC) (GLuint index, const GLdouble *v); +typedef void (APIENTRYP PFNGLVERTEXATTRIB2FARBPROC) (GLuint index, GLfloat x, GLfloat y); +typedef void (APIENTRYP PFNGLVERTEXATTRIB2FVARBPROC) (GLuint index, const GLfloat *v); +typedef void (APIENTRYP PFNGLVERTEXATTRIB2SARBPROC) (GLuint index, GLshort x, GLshort y); +typedef void (APIENTRYP PFNGLVERTEXATTRIB2SVARBPROC) (GLuint index, const GLshort *v); +typedef void (APIENTRYP PFNGLVERTEXATTRIB3DARBPROC) (GLuint index, GLdouble x, GLdouble y, GLdouble z); +typedef void (APIENTRYP PFNGLVERTEXATTRIB3DVARBPROC) (GLuint index, const GLdouble *v); +typedef void (APIENTRYP PFNGLVERTEXATTRIB3FARBPROC) (GLuint index, GLfloat x, GLfloat y, GLfloat z); +typedef void (APIENTRYP PFNGLVERTEXATTRIB3FVARBPROC) (GLuint index, const GLfloat *v); +typedef void (APIENTRYP PFNGLVERTEXATTRIB3SARBPROC) (GLuint index, GLshort x, GLshort y, GLshort z); +typedef void (APIENTRYP PFNGLVERTEXATTRIB3SVARBPROC) (GLuint index, const GLshort *v); +typedef void (APIENTRYP PFNGLVERTEXATTRIB4NBVARBPROC) (GLuint index, const GLbyte *v); +typedef void (APIENTRYP PFNGLVERTEXATTRIB4NIVARBPROC) (GLuint index, const GLint *v); +typedef void (APIENTRYP PFNGLVERTEXATTRIB4NSVARBPROC) (GLuint index, const GLshort *v); +typedef void (APIENTRYP PFNGLVERTEXATTRIB4NUBARBPROC) (GLuint index, GLubyte x, GLubyte y, GLubyte z, GLubyte w); +typedef void (APIENTRYP PFNGLVERTEXATTRIB4NUBVARBPROC) (GLuint index, const GLubyte *v); +typedef void (APIENTRYP PFNGLVERTEXATTRIB4NUIVARBPROC) (GLuint index, const GLuint *v); +typedef void (APIENTRYP PFNGLVERTEXATTRIB4NUSVARBPROC) (GLuint index, const GLushort *v); +typedef void (APIENTRYP PFNGLVERTEXATTRIB4BVARBPROC) (GLuint index, const GLbyte *v); +typedef void (APIENTRYP PFNGLVERTEXATTRIB4DARBPROC) (GLuint index, GLdouble x, GLdouble y, GLdouble z, GLdouble w); +typedef void (APIENTRYP PFNGLVERTEXATTRIB4DVARBPROC) (GLuint index, const GLdouble *v); +typedef void (APIENTRYP PFNGLVERTEXATTRIB4FARBPROC) (GLuint index, GLfloat x, GLfloat y, GLfloat z, GLfloat w); +typedef void (APIENTRYP PFNGLVERTEXATTRIB4FVARBPROC) (GLuint index, const GLfloat *v); +typedef void (APIENTRYP PFNGLVERTEXATTRIB4IVARBPROC) (GLuint index, const GLint *v); +typedef void (APIENTRYP PFNGLVERTEXATTRIB4SARBPROC) (GLuint index, GLshort x, GLshort y, GLshort z, GLshort w); +typedef void (APIENTRYP PFNGLVERTEXATTRIB4SVARBPROC) (GLuint index, const GLshort *v); +typedef void (APIENTRYP PFNGLVERTEXATTRIB4UBVARBPROC) (GLuint index, const GLubyte *v); +typedef void (APIENTRYP PFNGLVERTEXATTRIB4UIVARBPROC) (GLuint index, const GLuint *v); +typedef void (APIENTRYP PFNGLVERTEXATTRIB4USVARBPROC) (GLuint index, const GLushort *v); +typedef void (APIENTRYP PFNGLVERTEXATTRIBPOINTERARBPROC) (GLuint index, GLint size, GLenum type, GLboolean normalized, GLsizei stride, const void *pointer); +typedef void (APIENTRYP PFNGLENABLEVERTEXATTRIBARRAYARBPROC) (GLuint index); +typedef void (APIENTRYP PFNGLDISABLEVERTEXATTRIBARRAYARBPROC) (GLuint index); +typedef void (APIENTRYP PFNGLGETVERTEXATTRIBDVARBPROC) (GLuint index, GLenum pname, GLdouble *params); +typedef void (APIENTRYP PFNGLGETVERTEXATTRIBFVARBPROC) (GLuint index, GLenum pname, GLfloat *params); +typedef void (APIENTRYP PFNGLGETVERTEXATTRIBIVARBPROC) (GLuint index, GLenum pname, GLint *params); +typedef void (APIENTRYP PFNGLGETVERTEXATTRIBPOINTERVARBPROC) (GLuint index, GLenum pname, void **pointer); +#ifdef GL_GLEXT_PROTOTYPES +GLAPI void APIENTRY glVertexAttrib1dARB (GLuint index, GLdouble x); +GLAPI void APIENTRY glVertexAttrib1dvARB (GLuint index, const GLdouble *v); +GLAPI void APIENTRY glVertexAttrib1fARB (GLuint index, GLfloat x); +GLAPI void APIENTRY glVertexAttrib1fvARB (GLuint index, const GLfloat *v); +GLAPI void APIENTRY glVertexAttrib1sARB (GLuint index, GLshort x); +GLAPI void APIENTRY glVertexAttrib1svARB (GLuint index, const GLshort *v); +GLAPI void APIENTRY glVertexAttrib2dARB (GLuint index, GLdouble x, GLdouble y); +GLAPI void APIENTRY glVertexAttrib2dvARB (GLuint index, const GLdouble *v); +GLAPI void APIENTRY glVertexAttrib2fARB (GLuint index, GLfloat x, GLfloat y); +GLAPI void APIENTRY glVertexAttrib2fvARB (GLuint index, const GLfloat *v); +GLAPI void APIENTRY glVertexAttrib2sARB (GLuint index, GLshort x, GLshort y); +GLAPI void APIENTRY glVertexAttrib2svARB (GLuint index, const GLshort *v); +GLAPI void APIENTRY glVertexAttrib3dARB (GLuint index, GLdouble x, GLdouble y, GLdouble z); +GLAPI void APIENTRY glVertexAttrib3dvARB (GLuint index, const GLdouble *v); +GLAPI void APIENTRY glVertexAttrib3fARB (GLuint index, GLfloat x, GLfloat y, GLfloat z); +GLAPI void APIENTRY glVertexAttrib3fvARB (GLuint index, const GLfloat *v); +GLAPI void APIENTRY glVertexAttrib3sARB (GLuint index, GLshort x, GLshort y, GLshort z); +GLAPI void APIENTRY glVertexAttrib3svARB (GLuint index, const GLshort *v); +GLAPI void APIENTRY glVertexAttrib4NbvARB (GLuint index, const GLbyte *v); +GLAPI void APIENTRY glVertexAttrib4NivARB (GLuint index, const GLint *v); +GLAPI void APIENTRY glVertexAttrib4NsvARB (GLuint index, const GLshort *v); +GLAPI void APIENTRY glVertexAttrib4NubARB (GLuint index, GLubyte x, GLubyte y, GLubyte z, GLubyte w); +GLAPI void APIENTRY glVertexAttrib4NubvARB (GLuint index, const GLubyte *v); +GLAPI void APIENTRY glVertexAttrib4NuivARB (GLuint index, const GLuint *v); +GLAPI void APIENTRY glVertexAttrib4NusvARB (GLuint index, const GLushort *v); +GLAPI void APIENTRY glVertexAttrib4bvARB (GLuint index, const GLbyte *v); +GLAPI void APIENTRY glVertexAttrib4dARB (GLuint index, GLdouble x, GLdouble y, GLdouble z, GLdouble w); +GLAPI void APIENTRY glVertexAttrib4dvARB (GLuint index, const GLdouble *v); +GLAPI void APIENTRY glVertexAttrib4fARB (GLuint index, GLfloat x, GLfloat y, GLfloat z, GLfloat w); +GLAPI void APIENTRY glVertexAttrib4fvARB (GLuint index, const GLfloat *v); +GLAPI void APIENTRY glVertexAttrib4ivARB (GLuint index, const GLint *v); +GLAPI void APIENTRY glVertexAttrib4sARB (GLuint index, GLshort x, GLshort y, GLshort z, GLshort w); +GLAPI void APIENTRY glVertexAttrib4svARB (GLuint index, const GLshort *v); +GLAPI void APIENTRY glVertexAttrib4ubvARB (GLuint index, const GLubyte *v); +GLAPI void APIENTRY glVertexAttrib4uivARB (GLuint index, const GLuint *v); +GLAPI void APIENTRY glVertexAttrib4usvARB (GLuint index, const GLushort *v); +GLAPI void APIENTRY glVertexAttribPointerARB (GLuint index, GLint size, GLenum type, GLboolean normalized, GLsizei stride, const void *pointer); +GLAPI void APIENTRY glEnableVertexAttribArrayARB (GLuint index); +GLAPI void APIENTRY glDisableVertexAttribArrayARB (GLuint index); +GLAPI void APIENTRY glGetVertexAttribdvARB (GLuint index, GLenum pname, GLdouble *params); +GLAPI void APIENTRY glGetVertexAttribfvARB (GLuint index, GLenum pname, GLfloat *params); +GLAPI void APIENTRY glGetVertexAttribivARB (GLuint index, GLenum pname, GLint *params); +GLAPI void APIENTRY glGetVertexAttribPointervARB (GLuint index, GLenum pname, void **pointer); +#endif +#endif /* GL_ARB_vertex_program */ + +#ifndef GL_ARB_vertex_shader +#define GL_ARB_vertex_shader 1 +#define GL_VERTEX_SHADER_ARB 0x8B31 +#define GL_MAX_VERTEX_UNIFORM_COMPONENTS_ARB 0x8B4A +#define GL_MAX_VARYING_FLOATS_ARB 0x8B4B +#define GL_MAX_VERTEX_TEXTURE_IMAGE_UNITS_ARB 0x8B4C +#define GL_MAX_COMBINED_TEXTURE_IMAGE_UNITS_ARB 0x8B4D +#define GL_OBJECT_ACTIVE_ATTRIBUTES_ARB 0x8B89 +#define GL_OBJECT_ACTIVE_ATTRIBUTE_MAX_LENGTH_ARB 0x8B8A +typedef void (APIENTRYP PFNGLBINDATTRIBLOCATIONARBPROC) (GLhandleARB programObj, GLuint index, const GLcharARB *name); +typedef void (APIENTRYP PFNGLGETACTIVEATTRIBARBPROC) (GLhandleARB programObj, GLuint index, GLsizei maxLength, GLsizei *length, GLint *size, GLenum *type, GLcharARB *name); +typedef GLint (APIENTRYP PFNGLGETATTRIBLOCATIONARBPROC) (GLhandleARB programObj, const GLcharARB *name); +#ifdef GL_GLEXT_PROTOTYPES +GLAPI void APIENTRY glBindAttribLocationARB (GLhandleARB programObj, GLuint index, const GLcharARB *name); +GLAPI void APIENTRY glGetActiveAttribARB (GLhandleARB programObj, GLuint index, GLsizei maxLength, GLsizei *length, GLint *size, GLenum *type, GLcharARB *name); +GLAPI GLint APIENTRY glGetAttribLocationARB (GLhandleARB programObj, const GLcharARB *name); +#endif +#endif /* GL_ARB_vertex_shader */ + +#ifndef GL_ARB_vertex_type_10f_11f_11f_rev +#define GL_ARB_vertex_type_10f_11f_11f_rev 1 +#endif /* GL_ARB_vertex_type_10f_11f_11f_rev */ + +#ifndef GL_ARB_vertex_type_2_10_10_10_rev +#define GL_ARB_vertex_type_2_10_10_10_rev 1 +#endif /* GL_ARB_vertex_type_2_10_10_10_rev */ + +#ifndef GL_ARB_viewport_array +#define GL_ARB_viewport_array 1 +#endif /* GL_ARB_viewport_array */ + +#ifndef GL_ARB_window_pos +#define GL_ARB_window_pos 1 +typedef void (APIENTRYP PFNGLWINDOWPOS2DARBPROC) (GLdouble x, GLdouble y); +typedef void (APIENTRYP PFNGLWINDOWPOS2DVARBPROC) (const GLdouble *v); +typedef void (APIENTRYP PFNGLWINDOWPOS2FARBPROC) (GLfloat x, GLfloat y); +typedef void (APIENTRYP PFNGLWINDOWPOS2FVARBPROC) (const GLfloat *v); +typedef void (APIENTRYP PFNGLWINDOWPOS2IARBPROC) (GLint x, GLint y); +typedef void (APIENTRYP PFNGLWINDOWPOS2IVARBPROC) (const GLint *v); +typedef void (APIENTRYP PFNGLWINDOWPOS2SARBPROC) (GLshort x, GLshort y); +typedef void (APIENTRYP PFNGLWINDOWPOS2SVARBPROC) (const GLshort *v); +typedef void (APIENTRYP PFNGLWINDOWPOS3DARBPROC) (GLdouble x, GLdouble y, GLdouble z); +typedef void (APIENTRYP PFNGLWINDOWPOS3DVARBPROC) (const GLdouble *v); +typedef void (APIENTRYP PFNGLWINDOWPOS3FARBPROC) (GLfloat x, GLfloat y, GLfloat z); +typedef void (APIENTRYP PFNGLWINDOWPOS3FVARBPROC) (const GLfloat *v); +typedef void (APIENTRYP PFNGLWINDOWPOS3IARBPROC) (GLint x, GLint y, GLint z); +typedef void (APIENTRYP PFNGLWINDOWPOS3IVARBPROC) (const GLint *v); +typedef void (APIENTRYP PFNGLWINDOWPOS3SARBPROC) (GLshort x, GLshort y, GLshort z); +typedef void (APIENTRYP PFNGLWINDOWPOS3SVARBPROC) (const GLshort *v); +#ifdef GL_GLEXT_PROTOTYPES +GLAPI void APIENTRY glWindowPos2dARB (GLdouble x, GLdouble y); +GLAPI void APIENTRY glWindowPos2dvARB (const GLdouble *v); +GLAPI void APIENTRY glWindowPos2fARB (GLfloat x, GLfloat y); +GLAPI void APIENTRY glWindowPos2fvARB (const GLfloat *v); +GLAPI void APIENTRY glWindowPos2iARB (GLint x, GLint y); +GLAPI void APIENTRY glWindowPos2ivARB (const GLint *v); +GLAPI void APIENTRY glWindowPos2sARB (GLshort x, GLshort y); +GLAPI void APIENTRY glWindowPos2svARB (const GLshort *v); +GLAPI void APIENTRY glWindowPos3dARB (GLdouble x, GLdouble y, GLdouble z); +GLAPI void APIENTRY glWindowPos3dvARB (const GLdouble *v); +GLAPI void APIENTRY glWindowPos3fARB (GLfloat x, GLfloat y, GLfloat z); +GLAPI void APIENTRY glWindowPos3fvARB (const GLfloat *v); +GLAPI void APIENTRY glWindowPos3iARB (GLint x, GLint y, GLint z); +GLAPI void APIENTRY glWindowPos3ivARB (const GLint *v); +GLAPI void APIENTRY glWindowPos3sARB (GLshort x, GLshort y, GLshort z); +GLAPI void APIENTRY glWindowPos3svARB (const GLshort *v); +#endif +#endif /* GL_ARB_window_pos */ + +#ifndef GL_KHR_debug +#define GL_KHR_debug 1 +#endif /* GL_KHR_debug */ + +#ifndef GL_KHR_texture_compression_astc_hdr +#define GL_KHR_texture_compression_astc_hdr 1 +#define GL_COMPRESSED_RGBA_ASTC_4x4_KHR 0x93B0 +#define GL_COMPRESSED_RGBA_ASTC_5x4_KHR 0x93B1 +#define GL_COMPRESSED_RGBA_ASTC_5x5_KHR 0x93B2 +#define GL_COMPRESSED_RGBA_ASTC_6x5_KHR 0x93B3 +#define GL_COMPRESSED_RGBA_ASTC_6x6_KHR 0x93B4 +#define GL_COMPRESSED_RGBA_ASTC_8x5_KHR 0x93B5 +#define GL_COMPRESSED_RGBA_ASTC_8x6_KHR 0x93B6 +#define GL_COMPRESSED_RGBA_ASTC_8x8_KHR 0x93B7 +#define GL_COMPRESSED_RGBA_ASTC_10x5_KHR 0x93B8 +#define GL_COMPRESSED_RGBA_ASTC_10x6_KHR 0x93B9 +#define GL_COMPRESSED_RGBA_ASTC_10x8_KHR 0x93BA +#define GL_COMPRESSED_RGBA_ASTC_10x10_KHR 0x93BB +#define GL_COMPRESSED_RGBA_ASTC_12x10_KHR 0x93BC +#define GL_COMPRESSED_RGBA_ASTC_12x12_KHR 0x93BD +#define GL_COMPRESSED_SRGB8_ALPHA8_ASTC_4x4_KHR 0x93D0 +#define GL_COMPRESSED_SRGB8_ALPHA8_ASTC_5x4_KHR 0x93D1 +#define GL_COMPRESSED_SRGB8_ALPHA8_ASTC_5x5_KHR 0x93D2 +#define GL_COMPRESSED_SRGB8_ALPHA8_ASTC_6x5_KHR 0x93D3 +#define GL_COMPRESSED_SRGB8_ALPHA8_ASTC_6x6_KHR 0x93D4 +#define GL_COMPRESSED_SRGB8_ALPHA8_ASTC_8x5_KHR 0x93D5 +#define GL_COMPRESSED_SRGB8_ALPHA8_ASTC_8x6_KHR 0x93D6 +#define GL_COMPRESSED_SRGB8_ALPHA8_ASTC_8x8_KHR 0x93D7 +#define GL_COMPRESSED_SRGB8_ALPHA8_ASTC_10x5_KHR 0x93D8 +#define GL_COMPRESSED_SRGB8_ALPHA8_ASTC_10x6_KHR 0x93D9 +#define GL_COMPRESSED_SRGB8_ALPHA8_ASTC_10x8_KHR 0x93DA +#define GL_COMPRESSED_SRGB8_ALPHA8_ASTC_10x10_KHR 0x93DB +#define GL_COMPRESSED_SRGB8_ALPHA8_ASTC_12x10_KHR 0x93DC +#define GL_COMPRESSED_SRGB8_ALPHA8_ASTC_12x12_KHR 0x93DD +#endif /* GL_KHR_texture_compression_astc_hdr */ + +#ifndef GL_KHR_texture_compression_astc_ldr +#define GL_KHR_texture_compression_astc_ldr 1 +#endif /* GL_KHR_texture_compression_astc_ldr */ + +#ifndef GL_OES_byte_coordinates +#define GL_OES_byte_coordinates 1 +typedef void (APIENTRYP PFNGLMULTITEXCOORD1BOESPROC) (GLenum texture, GLbyte s); +typedef void (APIENTRYP PFNGLMULTITEXCOORD1BVOESPROC) (GLenum texture, const GLbyte *coords); +typedef void (APIENTRYP PFNGLMULTITEXCOORD2BOESPROC) (GLenum texture, GLbyte s, GLbyte t); +typedef void (APIENTRYP PFNGLMULTITEXCOORD2BVOESPROC) (GLenum texture, const GLbyte *coords); +typedef void (APIENTRYP PFNGLMULTITEXCOORD3BOESPROC) (GLenum texture, GLbyte s, GLbyte t, GLbyte r); +typedef void (APIENTRYP PFNGLMULTITEXCOORD3BVOESPROC) (GLenum texture, const GLbyte *coords); +typedef void (APIENTRYP PFNGLMULTITEXCOORD4BOESPROC) (GLenum texture, GLbyte s, GLbyte t, GLbyte r, GLbyte q); +typedef void (APIENTRYP PFNGLMULTITEXCOORD4BVOESPROC) (GLenum texture, const GLbyte *coords); +typedef void (APIENTRYP PFNGLTEXCOORD1BOESPROC) (GLbyte s); +typedef void (APIENTRYP PFNGLTEXCOORD1BVOESPROC) (const GLbyte *coords); +typedef void (APIENTRYP PFNGLTEXCOORD2BOESPROC) (GLbyte s, GLbyte t); +typedef void (APIENTRYP PFNGLTEXCOORD2BVOESPROC) (const GLbyte *coords); +typedef void (APIENTRYP PFNGLTEXCOORD3BOESPROC) (GLbyte s, GLbyte t, GLbyte r); +typedef void (APIENTRYP PFNGLTEXCOORD3BVOESPROC) (const GLbyte *coords); +typedef void (APIENTRYP PFNGLTEXCOORD4BOESPROC) (GLbyte s, GLbyte t, GLbyte r, GLbyte q); +typedef void (APIENTRYP PFNGLTEXCOORD4BVOESPROC) (const GLbyte *coords); +typedef void (APIENTRYP PFNGLVERTEX2BOESPROC) (GLbyte x); +typedef void (APIENTRYP PFNGLVERTEX2BVOESPROC) (const GLbyte *coords); +typedef void (APIENTRYP PFNGLVERTEX3BOESPROC) (GLbyte x, GLbyte y); +typedef void (APIENTRYP PFNGLVERTEX3BVOESPROC) (const GLbyte *coords); +typedef void (APIENTRYP PFNGLVERTEX4BOESPROC) (GLbyte x, GLbyte y, GLbyte z); +typedef void (APIENTRYP PFNGLVERTEX4BVOESPROC) (const GLbyte *coords); +#ifdef GL_GLEXT_PROTOTYPES +GLAPI void APIENTRY glMultiTexCoord1bOES (GLenum texture, GLbyte s); +GLAPI void APIENTRY glMultiTexCoord1bvOES (GLenum texture, const GLbyte *coords); +GLAPI void APIENTRY glMultiTexCoord2bOES (GLenum texture, GLbyte s, GLbyte t); +GLAPI void APIENTRY glMultiTexCoord2bvOES (GLenum texture, const GLbyte *coords); +GLAPI void APIENTRY glMultiTexCoord3bOES (GLenum texture, GLbyte s, GLbyte t, GLbyte r); +GLAPI void APIENTRY glMultiTexCoord3bvOES (GLenum texture, const GLbyte *coords); +GLAPI void APIENTRY glMultiTexCoord4bOES (GLenum texture, GLbyte s, GLbyte t, GLbyte r, GLbyte q); +GLAPI void APIENTRY glMultiTexCoord4bvOES (GLenum texture, const GLbyte *coords); +GLAPI void APIENTRY glTexCoord1bOES (GLbyte s); +GLAPI void APIENTRY glTexCoord1bvOES (const GLbyte *coords); +GLAPI void APIENTRY glTexCoord2bOES (GLbyte s, GLbyte t); +GLAPI void APIENTRY glTexCoord2bvOES (const GLbyte *coords); +GLAPI void APIENTRY glTexCoord3bOES (GLbyte s, GLbyte t, GLbyte r); +GLAPI void APIENTRY glTexCoord3bvOES (const GLbyte *coords); +GLAPI void APIENTRY glTexCoord4bOES (GLbyte s, GLbyte t, GLbyte r, GLbyte q); +GLAPI void APIENTRY glTexCoord4bvOES (const GLbyte *coords); +GLAPI void APIENTRY glVertex2bOES (GLbyte x); +GLAPI void APIENTRY glVertex2bvOES (const GLbyte *coords); +GLAPI void APIENTRY glVertex3bOES (GLbyte x, GLbyte y); +GLAPI void APIENTRY glVertex3bvOES (const GLbyte *coords); +GLAPI void APIENTRY glVertex4bOES (GLbyte x, GLbyte y, GLbyte z); +GLAPI void APIENTRY glVertex4bvOES (const GLbyte *coords); +#endif +#endif /* GL_OES_byte_coordinates */ + +#ifndef GL_OES_compressed_paletted_texture +#define GL_OES_compressed_paletted_texture 1 +#define GL_PALETTE4_RGB8_OES 0x8B90 +#define GL_PALETTE4_RGBA8_OES 0x8B91 +#define GL_PALETTE4_R5_G6_B5_OES 0x8B92 +#define GL_PALETTE4_RGBA4_OES 0x8B93 +#define GL_PALETTE4_RGB5_A1_OES 0x8B94 +#define GL_PALETTE8_RGB8_OES 0x8B95 +#define GL_PALETTE8_RGBA8_OES 0x8B96 +#define GL_PALETTE8_R5_G6_B5_OES 0x8B97 +#define GL_PALETTE8_RGBA4_OES 0x8B98 +#define GL_PALETTE8_RGB5_A1_OES 0x8B99 +#endif /* GL_OES_compressed_paletted_texture */ + +#ifndef GL_OES_fixed_point +#define GL_OES_fixed_point 1 +typedef GLint GLfixed; +#define GL_FIXED_OES 0x140C +typedef void (APIENTRYP PFNGLALPHAFUNCXOESPROC) (GLenum func, GLfixed ref); +typedef void (APIENTRYP PFNGLCLEARCOLORXOESPROC) (GLfixed red, GLfixed green, GLfixed blue, GLfixed alpha); +typedef void (APIENTRYP PFNGLCLEARDEPTHXOESPROC) (GLfixed depth); +typedef void (APIENTRYP PFNGLCLIPPLANEXOESPROC) (GLenum plane, const GLfixed *equation); +typedef void (APIENTRYP PFNGLCOLOR4XOESPROC) (GLfixed red, GLfixed green, GLfixed blue, GLfixed alpha); +typedef void (APIENTRYP PFNGLDEPTHRANGEXOESPROC) (GLfixed n, GLfixed f); +typedef void (APIENTRYP PFNGLFOGXOESPROC) (GLenum pname, GLfixed param); +typedef void (APIENTRYP PFNGLFOGXVOESPROC) (GLenum pname, const GLfixed *param); +typedef void (APIENTRYP PFNGLFRUSTUMXOESPROC) (GLfixed l, GLfixed r, GLfixed b, GLfixed t, GLfixed n, GLfixed f); +typedef void (APIENTRYP PFNGLGETCLIPPLANEXOESPROC) (GLenum plane, GLfixed *equation); +typedef void (APIENTRYP PFNGLGETFIXEDVOESPROC) (GLenum pname, GLfixed *params); +typedef void (APIENTRYP PFNGLGETTEXENVXVOESPROC) (GLenum target, GLenum pname, GLfixed *params); +typedef void (APIENTRYP PFNGLGETTEXPARAMETERXVOESPROC) (GLenum target, GLenum pname, GLfixed *params); +typedef void (APIENTRYP PFNGLLIGHTMODELXOESPROC) (GLenum pname, GLfixed param); +typedef void (APIENTRYP PFNGLLIGHTMODELXVOESPROC) (GLenum pname, const GLfixed *param); +typedef void (APIENTRYP PFNGLLIGHTXOESPROC) (GLenum light, GLenum pname, GLfixed param); +typedef void (APIENTRYP PFNGLLIGHTXVOESPROC) (GLenum light, GLenum pname, const GLfixed *params); +typedef void (APIENTRYP PFNGLLINEWIDTHXOESPROC) (GLfixed width); +typedef void (APIENTRYP PFNGLLOADMATRIXXOESPROC) (const GLfixed *m); +typedef void (APIENTRYP PFNGLMATERIALXOESPROC) (GLenum face, GLenum pname, GLfixed param); +typedef void (APIENTRYP PFNGLMATERIALXVOESPROC) (GLenum face, GLenum pname, const GLfixed *param); +typedef void (APIENTRYP PFNGLMULTMATRIXXOESPROC) (const GLfixed *m); +typedef void (APIENTRYP PFNGLMULTITEXCOORD4XOESPROC) (GLenum texture, GLfixed s, GLfixed t, GLfixed r, GLfixed q); +typedef void (APIENTRYP PFNGLNORMAL3XOESPROC) (GLfixed nx, GLfixed ny, GLfixed nz); +typedef void (APIENTRYP PFNGLORTHOXOESPROC) (GLfixed l, GLfixed r, GLfixed b, GLfixed t, GLfixed n, GLfixed f); +typedef void (APIENTRYP PFNGLPOINTPARAMETERXVOESPROC) (GLenum pname, const GLfixed *params); +typedef void (APIENTRYP PFNGLPOINTSIZEXOESPROC) (GLfixed size); +typedef void (APIENTRYP PFNGLPOLYGONOFFSETXOESPROC) (GLfixed factor, GLfixed units); +typedef void (APIENTRYP PFNGLROTATEXOESPROC) (GLfixed angle, GLfixed x, GLfixed y, GLfixed z); +typedef void (APIENTRYP PFNGLSAMPLECOVERAGEOESPROC) (GLfixed value, GLboolean invert); +typedef void (APIENTRYP PFNGLSCALEXOESPROC) (GLfixed x, GLfixed y, GLfixed z); +typedef void (APIENTRYP PFNGLTEXENVXOESPROC) (GLenum target, GLenum pname, GLfixed param); +typedef void (APIENTRYP PFNGLTEXENVXVOESPROC) (GLenum target, GLenum pname, const GLfixed *params); +typedef void (APIENTRYP PFNGLTEXPARAMETERXOESPROC) (GLenum target, GLenum pname, GLfixed param); +typedef void (APIENTRYP PFNGLTEXPARAMETERXVOESPROC) (GLenum target, GLenum pname, const GLfixed *params); +typedef void (APIENTRYP PFNGLTRANSLATEXOESPROC) (GLfixed x, GLfixed y, GLfixed z); +typedef void (APIENTRYP PFNGLACCUMXOESPROC) (GLenum op, GLfixed value); +typedef void (APIENTRYP PFNGLBITMAPXOESPROC) (GLsizei width, GLsizei height, GLfixed xorig, GLfixed yorig, GLfixed xmove, GLfixed ymove, const GLubyte *bitmap); +typedef void (APIENTRYP PFNGLBLENDCOLORXOESPROC) (GLfixed red, GLfixed green, GLfixed blue, GLfixed alpha); +typedef void (APIENTRYP PFNGLCLEARACCUMXOESPROC) (GLfixed red, GLfixed green, GLfixed blue, GLfixed alpha); +typedef void (APIENTRYP PFNGLCOLOR3XOESPROC) (GLfixed red, GLfixed green, GLfixed blue); +typedef void (APIENTRYP PFNGLCOLOR3XVOESPROC) (const GLfixed *components); +typedef void (APIENTRYP PFNGLCOLOR4XVOESPROC) (const GLfixed *components); +typedef void (APIENTRYP PFNGLCONVOLUTIONPARAMETERXOESPROC) (GLenum target, GLenum pname, GLfixed param); +typedef void (APIENTRYP PFNGLCONVOLUTIONPARAMETERXVOESPROC) (GLenum target, GLenum pname, const GLfixed *params); +typedef void (APIENTRYP PFNGLEVALCOORD1XOESPROC) (GLfixed u); +typedef void (APIENTRYP PFNGLEVALCOORD1XVOESPROC) (const GLfixed *coords); +typedef void (APIENTRYP PFNGLEVALCOORD2XOESPROC) (GLfixed u, GLfixed v); +typedef void (APIENTRYP PFNGLEVALCOORD2XVOESPROC) (const GLfixed *coords); +typedef void (APIENTRYP PFNGLFEEDBACKBUFFERXOESPROC) (GLsizei n, GLenum type, const GLfixed *buffer); +typedef void (APIENTRYP PFNGLGETCONVOLUTIONPARAMETERXVOESPROC) (GLenum target, GLenum pname, GLfixed *params); +typedef void (APIENTRYP PFNGLGETHISTOGRAMPARAMETERXVOESPROC) (GLenum target, GLenum pname, GLfixed *params); +typedef void (APIENTRYP PFNGLGETLIGHTXOESPROC) (GLenum light, GLenum pname, GLfixed *params); +typedef void (APIENTRYP PFNGLGETMAPXVOESPROC) (GLenum target, GLenum query, GLfixed *v); +typedef void (APIENTRYP PFNGLGETMATERIALXOESPROC) (GLenum face, GLenum pname, GLfixed param); +typedef void (APIENTRYP PFNGLGETPIXELMAPXVPROC) (GLenum map, GLint size, GLfixed *values); +typedef void (APIENTRYP PFNGLGETTEXGENXVOESPROC) (GLenum coord, GLenum pname, GLfixed *params); +typedef void (APIENTRYP PFNGLGETTEXLEVELPARAMETERXVOESPROC) (GLenum target, GLint level, GLenum pname, GLfixed *params); +typedef void (APIENTRYP PFNGLINDEXXOESPROC) (GLfixed component); +typedef void (APIENTRYP PFNGLINDEXXVOESPROC) (const GLfixed *component); +typedef void (APIENTRYP PFNGLLOADTRANSPOSEMATRIXXOESPROC) (const GLfixed *m); +typedef void (APIENTRYP PFNGLMAP1XOESPROC) (GLenum target, GLfixed u1, GLfixed u2, GLint stride, GLint order, GLfixed points); +typedef void (APIENTRYP PFNGLMAP2XOESPROC) (GLenum target, GLfixed u1, GLfixed u2, GLint ustride, GLint uorder, GLfixed v1, GLfixed v2, GLint vstride, GLint vorder, GLfixed points); +typedef void (APIENTRYP PFNGLMAPGRID1XOESPROC) (GLint n, GLfixed u1, GLfixed u2); +typedef void (APIENTRYP PFNGLMAPGRID2XOESPROC) (GLint n, GLfixed u1, GLfixed u2, GLfixed v1, GLfixed v2); +typedef void (APIENTRYP PFNGLMULTTRANSPOSEMATRIXXOESPROC) (const GLfixed *m); +typedef void (APIENTRYP PFNGLMULTITEXCOORD1XOESPROC) (GLenum texture, GLfixed s); +typedef void (APIENTRYP PFNGLMULTITEXCOORD1XVOESPROC) (GLenum texture, const GLfixed *coords); +typedef void (APIENTRYP PFNGLMULTITEXCOORD2XOESPROC) (GLenum texture, GLfixed s, GLfixed t); +typedef void (APIENTRYP PFNGLMULTITEXCOORD2XVOESPROC) (GLenum texture, const GLfixed *coords); +typedef void (APIENTRYP PFNGLMULTITEXCOORD3XOESPROC) (GLenum texture, GLfixed s, GLfixed t, GLfixed r); +typedef void (APIENTRYP PFNGLMULTITEXCOORD3XVOESPROC) (GLenum texture, const GLfixed *coords); +typedef void (APIENTRYP PFNGLMULTITEXCOORD4XVOESPROC) (GLenum texture, const GLfixed *coords); +typedef void (APIENTRYP PFNGLNORMAL3XVOESPROC) (const GLfixed *coords); +typedef void (APIENTRYP PFNGLPASSTHROUGHXOESPROC) (GLfixed token); +typedef void (APIENTRYP PFNGLPIXELMAPXPROC) (GLenum map, GLint size, const GLfixed *values); +typedef void (APIENTRYP PFNGLPIXELSTOREXPROC) (GLenum pname, GLfixed param); +typedef void (APIENTRYP PFNGLPIXELTRANSFERXOESPROC) (GLenum pname, GLfixed param); +typedef void (APIENTRYP PFNGLPIXELZOOMXOESPROC) (GLfixed xfactor, GLfixed yfactor); +typedef void (APIENTRYP PFNGLPRIORITIZETEXTURESXOESPROC) (GLsizei n, const GLuint *textures, const GLfixed *priorities); +typedef void (APIENTRYP PFNGLRASTERPOS2XOESPROC) (GLfixed x, GLfixed y); +typedef void (APIENTRYP PFNGLRASTERPOS2XVOESPROC) (const GLfixed *coords); +typedef void (APIENTRYP PFNGLRASTERPOS3XOESPROC) (GLfixed x, GLfixed y, GLfixed z); +typedef void (APIENTRYP PFNGLRASTERPOS3XVOESPROC) (const GLfixed *coords); +typedef void (APIENTRYP PFNGLRASTERPOS4XOESPROC) (GLfixed x, GLfixed y, GLfixed z, GLfixed w); +typedef void (APIENTRYP PFNGLRASTERPOS4XVOESPROC) (const GLfixed *coords); +typedef void (APIENTRYP PFNGLRECTXOESPROC) (GLfixed x1, GLfixed y1, GLfixed x2, GLfixed y2); +typedef void (APIENTRYP PFNGLRECTXVOESPROC) (const GLfixed *v1, const GLfixed *v2); +typedef void (APIENTRYP PFNGLTEXCOORD1XOESPROC) (GLfixed s); +typedef void (APIENTRYP PFNGLTEXCOORD1XVOESPROC) (const GLfixed *coords); +typedef void (APIENTRYP PFNGLTEXCOORD2XOESPROC) (GLfixed s, GLfixed t); +typedef void (APIENTRYP PFNGLTEXCOORD2XVOESPROC) (const GLfixed *coords); +typedef void (APIENTRYP PFNGLTEXCOORD3XOESPROC) (GLfixed s, GLfixed t, GLfixed r); +typedef void (APIENTRYP PFNGLTEXCOORD3XVOESPROC) (const GLfixed *coords); +typedef void (APIENTRYP PFNGLTEXCOORD4XOESPROC) (GLfixed s, GLfixed t, GLfixed r, GLfixed q); +typedef void (APIENTRYP PFNGLTEXCOORD4XVOESPROC) (const GLfixed *coords); +typedef void (APIENTRYP PFNGLTEXGENXOESPROC) (GLenum coord, GLenum pname, GLfixed param); +typedef void (APIENTRYP PFNGLTEXGENXVOESPROC) (GLenum coord, GLenum pname, const GLfixed *params); +typedef void (APIENTRYP PFNGLVERTEX2XOESPROC) (GLfixed x); +typedef void (APIENTRYP PFNGLVERTEX2XVOESPROC) (const GLfixed *coords); +typedef void (APIENTRYP PFNGLVERTEX3XOESPROC) (GLfixed x, GLfixed y); +typedef void (APIENTRYP PFNGLVERTEX3XVOESPROC) (const GLfixed *coords); +typedef void (APIENTRYP PFNGLVERTEX4XOESPROC) (GLfixed x, GLfixed y, GLfixed z); +typedef void (APIENTRYP PFNGLVERTEX4XVOESPROC) (const GLfixed *coords); +#ifdef GL_GLEXT_PROTOTYPES +GLAPI void APIENTRY glAlphaFuncxOES (GLenum func, GLfixed ref); +GLAPI void APIENTRY glClearColorxOES (GLfixed red, GLfixed green, GLfixed blue, GLfixed alpha); +GLAPI void APIENTRY glClearDepthxOES (GLfixed depth); +GLAPI void APIENTRY glClipPlanexOES (GLenum plane, const GLfixed *equation); +GLAPI void APIENTRY glColor4xOES (GLfixed red, GLfixed green, GLfixed blue, GLfixed alpha); +GLAPI void APIENTRY glDepthRangexOES (GLfixed n, GLfixed f); +GLAPI void APIENTRY glFogxOES (GLenum pname, GLfixed param); +GLAPI void APIENTRY glFogxvOES (GLenum pname, const GLfixed *param); +GLAPI void APIENTRY glFrustumxOES (GLfixed l, GLfixed r, GLfixed b, GLfixed t, GLfixed n, GLfixed f); +GLAPI void APIENTRY glGetClipPlanexOES (GLenum plane, GLfixed *equation); +GLAPI void APIENTRY glGetFixedvOES (GLenum pname, GLfixed *params); +GLAPI void APIENTRY glGetTexEnvxvOES (GLenum target, GLenum pname, GLfixed *params); +GLAPI void APIENTRY glGetTexParameterxvOES (GLenum target, GLenum pname, GLfixed *params); +GLAPI void APIENTRY glLightModelxOES (GLenum pname, GLfixed param); +GLAPI void APIENTRY glLightModelxvOES (GLenum pname, const GLfixed *param); +GLAPI void APIENTRY glLightxOES (GLenum light, GLenum pname, GLfixed param); +GLAPI void APIENTRY glLightxvOES (GLenum light, GLenum pname, const GLfixed *params); +GLAPI void APIENTRY glLineWidthxOES (GLfixed width); +GLAPI void APIENTRY glLoadMatrixxOES (const GLfixed *m); +GLAPI void APIENTRY glMaterialxOES (GLenum face, GLenum pname, GLfixed param); +GLAPI void APIENTRY glMaterialxvOES (GLenum face, GLenum pname, const GLfixed *param); +GLAPI void APIENTRY glMultMatrixxOES (const GLfixed *m); +GLAPI void APIENTRY glMultiTexCoord4xOES (GLenum texture, GLfixed s, GLfixed t, GLfixed r, GLfixed q); +GLAPI void APIENTRY glNormal3xOES (GLfixed nx, GLfixed ny, GLfixed nz); +GLAPI void APIENTRY glOrthoxOES (GLfixed l, GLfixed r, GLfixed b, GLfixed t, GLfixed n, GLfixed f); +GLAPI void APIENTRY glPointParameterxvOES (GLenum pname, const GLfixed *params); +GLAPI void APIENTRY glPointSizexOES (GLfixed size); +GLAPI void APIENTRY glPolygonOffsetxOES (GLfixed factor, GLfixed units); +GLAPI void APIENTRY glRotatexOES (GLfixed angle, GLfixed x, GLfixed y, GLfixed z); +GLAPI void APIENTRY glSampleCoverageOES (GLfixed value, GLboolean invert); +GLAPI void APIENTRY glScalexOES (GLfixed x, GLfixed y, GLfixed z); +GLAPI void APIENTRY glTexEnvxOES (GLenum target, GLenum pname, GLfixed param); +GLAPI void APIENTRY glTexEnvxvOES (GLenum target, GLenum pname, const GLfixed *params); +GLAPI void APIENTRY glTexParameterxOES (GLenum target, GLenum pname, GLfixed param); +GLAPI void APIENTRY glTexParameterxvOES (GLenum target, GLenum pname, const GLfixed *params); +GLAPI void APIENTRY glTranslatexOES (GLfixed x, GLfixed y, GLfixed z); +GLAPI void APIENTRY glAccumxOES (GLenum op, GLfixed value); +GLAPI void APIENTRY glBitmapxOES (GLsizei width, GLsizei height, GLfixed xorig, GLfixed yorig, GLfixed xmove, GLfixed ymove, const GLubyte *bitmap); +GLAPI void APIENTRY glBlendColorxOES (GLfixed red, GLfixed green, GLfixed blue, GLfixed alpha); +GLAPI void APIENTRY glClearAccumxOES (GLfixed red, GLfixed green, GLfixed blue, GLfixed alpha); +GLAPI void APIENTRY glColor3xOES (GLfixed red, GLfixed green, GLfixed blue); +GLAPI void APIENTRY glColor3xvOES (const GLfixed *components); +GLAPI void APIENTRY glColor4xvOES (const GLfixed *components); +GLAPI void APIENTRY glConvolutionParameterxOES (GLenum target, GLenum pname, GLfixed param); +GLAPI void APIENTRY glConvolutionParameterxvOES (GLenum target, GLenum pname, const GLfixed *params); +GLAPI void APIENTRY glEvalCoord1xOES (GLfixed u); +GLAPI void APIENTRY glEvalCoord1xvOES (const GLfixed *coords); +GLAPI void APIENTRY glEvalCoord2xOES (GLfixed u, GLfixed v); +GLAPI void APIENTRY glEvalCoord2xvOES (const GLfixed *coords); +GLAPI void APIENTRY glFeedbackBufferxOES (GLsizei n, GLenum type, const GLfixed *buffer); +GLAPI void APIENTRY glGetConvolutionParameterxvOES (GLenum target, GLenum pname, GLfixed *params); +GLAPI void APIENTRY glGetHistogramParameterxvOES (GLenum target, GLenum pname, GLfixed *params); +GLAPI void APIENTRY glGetLightxOES (GLenum light, GLenum pname, GLfixed *params); +GLAPI void APIENTRY glGetMapxvOES (GLenum target, GLenum query, GLfixed *v); +GLAPI void APIENTRY glGetMaterialxOES (GLenum face, GLenum pname, GLfixed param); +GLAPI void APIENTRY glGetPixelMapxv (GLenum map, GLint size, GLfixed *values); +GLAPI void APIENTRY glGetTexGenxvOES (GLenum coord, GLenum pname, GLfixed *params); +GLAPI void APIENTRY glGetTexLevelParameterxvOES (GLenum target, GLint level, GLenum pname, GLfixed *params); +GLAPI void APIENTRY glIndexxOES (GLfixed component); +GLAPI void APIENTRY glIndexxvOES (const GLfixed *component); +GLAPI void APIENTRY glLoadTransposeMatrixxOES (const GLfixed *m); +GLAPI void APIENTRY glMap1xOES (GLenum target, GLfixed u1, GLfixed u2, GLint stride, GLint order, GLfixed points); +GLAPI void APIENTRY glMap2xOES (GLenum target, GLfixed u1, GLfixed u2, GLint ustride, GLint uorder, GLfixed v1, GLfixed v2, GLint vstride, GLint vorder, GLfixed points); +GLAPI void APIENTRY glMapGrid1xOES (GLint n, GLfixed u1, GLfixed u2); +GLAPI void APIENTRY glMapGrid2xOES (GLint n, GLfixed u1, GLfixed u2, GLfixed v1, GLfixed v2); +GLAPI void APIENTRY glMultTransposeMatrixxOES (const GLfixed *m); +GLAPI void APIENTRY glMultiTexCoord1xOES (GLenum texture, GLfixed s); +GLAPI void APIENTRY glMultiTexCoord1xvOES (GLenum texture, const GLfixed *coords); +GLAPI void APIENTRY glMultiTexCoord2xOES (GLenum texture, GLfixed s, GLfixed t); +GLAPI void APIENTRY glMultiTexCoord2xvOES (GLenum texture, const GLfixed *coords); +GLAPI void APIENTRY glMultiTexCoord3xOES (GLenum texture, GLfixed s, GLfixed t, GLfixed r); +GLAPI void APIENTRY glMultiTexCoord3xvOES (GLenum texture, const GLfixed *coords); +GLAPI void APIENTRY glMultiTexCoord4xvOES (GLenum texture, const GLfixed *coords); +GLAPI void APIENTRY glNormal3xvOES (const GLfixed *coords); +GLAPI void APIENTRY glPassThroughxOES (GLfixed token); +GLAPI void APIENTRY glPixelMapx (GLenum map, GLint size, const GLfixed *values); +GLAPI void APIENTRY glPixelStorex (GLenum pname, GLfixed param); +GLAPI void APIENTRY glPixelTransferxOES (GLenum pname, GLfixed param); +GLAPI void APIENTRY glPixelZoomxOES (GLfixed xfactor, GLfixed yfactor); +GLAPI void APIENTRY glPrioritizeTexturesxOES (GLsizei n, const GLuint *textures, const GLfixed *priorities); +GLAPI void APIENTRY glRasterPos2xOES (GLfixed x, GLfixed y); +GLAPI void APIENTRY glRasterPos2xvOES (const GLfixed *coords); +GLAPI void APIENTRY glRasterPos3xOES (GLfixed x, GLfixed y, GLfixed z); +GLAPI void APIENTRY glRasterPos3xvOES (const GLfixed *coords); +GLAPI void APIENTRY glRasterPos4xOES (GLfixed x, GLfixed y, GLfixed z, GLfixed w); +GLAPI void APIENTRY glRasterPos4xvOES (const GLfixed *coords); +GLAPI void APIENTRY glRectxOES (GLfixed x1, GLfixed y1, GLfixed x2, GLfixed y2); +GLAPI void APIENTRY glRectxvOES (const GLfixed *v1, const GLfixed *v2); +GLAPI void APIENTRY glTexCoord1xOES (GLfixed s); +GLAPI void APIENTRY glTexCoord1xvOES (const GLfixed *coords); +GLAPI void APIENTRY glTexCoord2xOES (GLfixed s, GLfixed t); +GLAPI void APIENTRY glTexCoord2xvOES (const GLfixed *coords); +GLAPI void APIENTRY glTexCoord3xOES (GLfixed s, GLfixed t, GLfixed r); +GLAPI void APIENTRY glTexCoord3xvOES (const GLfixed *coords); +GLAPI void APIENTRY glTexCoord4xOES (GLfixed s, GLfixed t, GLfixed r, GLfixed q); +GLAPI void APIENTRY glTexCoord4xvOES (const GLfixed *coords); +GLAPI void APIENTRY glTexGenxOES (GLenum coord, GLenum pname, GLfixed param); +GLAPI void APIENTRY glTexGenxvOES (GLenum coord, GLenum pname, const GLfixed *params); +GLAPI void APIENTRY glVertex2xOES (GLfixed x); +GLAPI void APIENTRY glVertex2xvOES (const GLfixed *coords); +GLAPI void APIENTRY glVertex3xOES (GLfixed x, GLfixed y); +GLAPI void APIENTRY glVertex3xvOES (const GLfixed *coords); +GLAPI void APIENTRY glVertex4xOES (GLfixed x, GLfixed y, GLfixed z); +GLAPI void APIENTRY glVertex4xvOES (const GLfixed *coords); +#endif +#endif /* GL_OES_fixed_point */ + +#ifndef GL_OES_query_matrix +#define GL_OES_query_matrix 1 +typedef GLbitfield (APIENTRYP PFNGLQUERYMATRIXXOESPROC) (GLfixed *mantissa, GLint *exponent); +#ifdef GL_GLEXT_PROTOTYPES +GLAPI GLbitfield APIENTRY glQueryMatrixxOES (GLfixed *mantissa, GLint *exponent); +#endif +#endif /* GL_OES_query_matrix */ + +#ifndef GL_OES_read_format +#define GL_OES_read_format 1 +#define GL_IMPLEMENTATION_COLOR_READ_TYPE_OES 0x8B9A +#define GL_IMPLEMENTATION_COLOR_READ_FORMAT_OES 0x8B9B +#endif /* GL_OES_read_format */ + +#ifndef GL_OES_single_precision +#define GL_OES_single_precision 1 +typedef void (APIENTRYP PFNGLCLEARDEPTHFOESPROC) (GLclampf depth); +typedef void (APIENTRYP PFNGLCLIPPLANEFOESPROC) (GLenum plane, const GLfloat *equation); +typedef void (APIENTRYP PFNGLDEPTHRANGEFOESPROC) (GLclampf n, GLclampf f); +typedef void (APIENTRYP PFNGLFRUSTUMFOESPROC) (GLfloat l, GLfloat r, GLfloat b, GLfloat t, GLfloat n, GLfloat f); +typedef void (APIENTRYP PFNGLGETCLIPPLANEFOESPROC) (GLenum plane, GLfloat *equation); +typedef void (APIENTRYP PFNGLORTHOFOESPROC) (GLfloat l, GLfloat r, GLfloat b, GLfloat t, GLfloat n, GLfloat f); +#ifdef GL_GLEXT_PROTOTYPES +GLAPI void APIENTRY glClearDepthfOES (GLclampf depth); +GLAPI void APIENTRY glClipPlanefOES (GLenum plane, const GLfloat *equation); +GLAPI void APIENTRY glDepthRangefOES (GLclampf n, GLclampf f); +GLAPI void APIENTRY glFrustumfOES (GLfloat l, GLfloat r, GLfloat b, GLfloat t, GLfloat n, GLfloat f); +GLAPI void APIENTRY glGetClipPlanefOES (GLenum plane, GLfloat *equation); +GLAPI void APIENTRY glOrthofOES (GLfloat l, GLfloat r, GLfloat b, GLfloat t, GLfloat n, GLfloat f); +#endif +#endif /* GL_OES_single_precision */ + +#ifndef GL_3DFX_multisample +#define GL_3DFX_multisample 1 +#define GL_MULTISAMPLE_3DFX 0x86B2 +#define GL_SAMPLE_BUFFERS_3DFX 0x86B3 +#define GL_SAMPLES_3DFX 0x86B4 +#define GL_MULTISAMPLE_BIT_3DFX 0x20000000 +#endif /* GL_3DFX_multisample */ + +#ifndef GL_3DFX_tbuffer +#define GL_3DFX_tbuffer 1 +typedef void (APIENTRYP PFNGLTBUFFERMASK3DFXPROC) (GLuint mask); +#ifdef GL_GLEXT_PROTOTYPES +GLAPI void APIENTRY glTbufferMask3DFX (GLuint mask); +#endif +#endif /* GL_3DFX_tbuffer */ + +#ifndef GL_3DFX_texture_compression_FXT1 +#define GL_3DFX_texture_compression_FXT1 1 +#define GL_COMPRESSED_RGB_FXT1_3DFX 0x86B0 +#define GL_COMPRESSED_RGBA_FXT1_3DFX 0x86B1 +#endif /* GL_3DFX_texture_compression_FXT1 */ + +#ifndef GL_AMD_blend_minmax_factor +#define GL_AMD_blend_minmax_factor 1 +#define GL_FACTOR_MIN_AMD 0x901C +#define GL_FACTOR_MAX_AMD 0x901D +#endif /* GL_AMD_blend_minmax_factor */ + +#ifndef GL_AMD_conservative_depth +#define GL_AMD_conservative_depth 1 +#endif /* GL_AMD_conservative_depth */ + +#ifndef GL_AMD_debug_output +#define GL_AMD_debug_output 1 +typedef void (APIENTRY *GLDEBUGPROCAMD)(GLuint id,GLenum category,GLenum severity,GLsizei length,const GLchar *message,void *userParam); +#define GL_MAX_DEBUG_MESSAGE_LENGTH_AMD 0x9143 +#define GL_MAX_DEBUG_LOGGED_MESSAGES_AMD 0x9144 +#define GL_DEBUG_LOGGED_MESSAGES_AMD 0x9145 +#define GL_DEBUG_SEVERITY_HIGH_AMD 0x9146 +#define GL_DEBUG_SEVERITY_MEDIUM_AMD 0x9147 +#define GL_DEBUG_SEVERITY_LOW_AMD 0x9148 +#define GL_DEBUG_CATEGORY_API_ERROR_AMD 0x9149 +#define GL_DEBUG_CATEGORY_WINDOW_SYSTEM_AMD 0x914A +#define GL_DEBUG_CATEGORY_DEPRECATION_AMD 0x914B +#define GL_DEBUG_CATEGORY_UNDEFINED_BEHAVIOR_AMD 0x914C +#define GL_DEBUG_CATEGORY_PERFORMANCE_AMD 0x914D +#define GL_DEBUG_CATEGORY_SHADER_COMPILER_AMD 0x914E +#define GL_DEBUG_CATEGORY_APPLICATION_AMD 0x914F +#define GL_DEBUG_CATEGORY_OTHER_AMD 0x9150 +typedef void (APIENTRYP PFNGLDEBUGMESSAGEENABLEAMDPROC) (GLenum category, GLenum severity, GLsizei count, const GLuint *ids, GLboolean enabled); +typedef void (APIENTRYP PFNGLDEBUGMESSAGEINSERTAMDPROC) (GLenum category, GLenum severity, GLuint id, GLsizei length, const GLchar *buf); +typedef void (APIENTRYP PFNGLDEBUGMESSAGECALLBACKAMDPROC) (GLDEBUGPROCAMD callback, void *userParam); +typedef GLuint (APIENTRYP PFNGLGETDEBUGMESSAGELOGAMDPROC) (GLuint count, GLsizei bufsize, GLenum *categories, GLuint *severities, GLuint *ids, GLsizei *lengths, GLchar *message); +#ifdef GL_GLEXT_PROTOTYPES +GLAPI void APIENTRY glDebugMessageEnableAMD (GLenum category, GLenum severity, GLsizei count, const GLuint *ids, GLboolean enabled); +GLAPI void APIENTRY glDebugMessageInsertAMD (GLenum category, GLenum severity, GLuint id, GLsizei length, const GLchar *buf); +GLAPI void APIENTRY glDebugMessageCallbackAMD (GLDEBUGPROCAMD callback, void *userParam); +GLAPI GLuint APIENTRY glGetDebugMessageLogAMD (GLuint count, GLsizei bufsize, GLenum *categories, GLuint *severities, GLuint *ids, GLsizei *lengths, GLchar *message); +#endif +#endif /* GL_AMD_debug_output */ + +#ifndef GL_AMD_depth_clamp_separate +#define GL_AMD_depth_clamp_separate 1 +#define GL_DEPTH_CLAMP_NEAR_AMD 0x901E +#define GL_DEPTH_CLAMP_FAR_AMD 0x901F +#endif /* GL_AMD_depth_clamp_separate */ + +#ifndef GL_AMD_draw_buffers_blend +#define GL_AMD_draw_buffers_blend 1 +typedef void (APIENTRYP PFNGLBLENDFUNCINDEXEDAMDPROC) (GLuint buf, GLenum src, GLenum dst); +typedef void (APIENTRYP PFNGLBLENDFUNCSEPARATEINDEXEDAMDPROC) (GLuint buf, GLenum srcRGB, GLenum dstRGB, GLenum srcAlpha, GLenum dstAlpha); +typedef void (APIENTRYP PFNGLBLENDEQUATIONINDEXEDAMDPROC) (GLuint buf, GLenum mode); +typedef void (APIENTRYP PFNGLBLENDEQUATIONSEPARATEINDEXEDAMDPROC) (GLuint buf, GLenum modeRGB, GLenum modeAlpha); +#ifdef GL_GLEXT_PROTOTYPES +GLAPI void APIENTRY glBlendFuncIndexedAMD (GLuint buf, GLenum src, GLenum dst); +GLAPI void APIENTRY glBlendFuncSeparateIndexedAMD (GLuint buf, GLenum srcRGB, GLenum dstRGB, GLenum srcAlpha, GLenum dstAlpha); +GLAPI void APIENTRY glBlendEquationIndexedAMD (GLuint buf, GLenum mode); +GLAPI void APIENTRY glBlendEquationSeparateIndexedAMD (GLuint buf, GLenum modeRGB, GLenum modeAlpha); +#endif +#endif /* GL_AMD_draw_buffers_blend */ + +#ifndef GL_AMD_interleaved_elements +#define GL_AMD_interleaved_elements 1 +#define GL_VERTEX_ELEMENT_SWIZZLE_AMD 0x91A4 +#define GL_VERTEX_ID_SWIZZLE_AMD 0x91A5 +typedef void (APIENTRYP PFNGLVERTEXATTRIBPARAMETERIAMDPROC) (GLuint index, GLenum pname, GLint param); +#ifdef GL_GLEXT_PROTOTYPES +GLAPI void APIENTRY glVertexAttribParameteriAMD (GLuint index, GLenum pname, GLint param); +#endif +#endif /* GL_AMD_interleaved_elements */ + +#ifndef GL_AMD_multi_draw_indirect +#define GL_AMD_multi_draw_indirect 1 +typedef void (APIENTRYP PFNGLMULTIDRAWARRAYSINDIRECTAMDPROC) (GLenum mode, const void *indirect, GLsizei primcount, GLsizei stride); +typedef void (APIENTRYP PFNGLMULTIDRAWELEMENTSINDIRECTAMDPROC) (GLenum mode, GLenum type, const void *indirect, GLsizei primcount, GLsizei stride); +#ifdef GL_GLEXT_PROTOTYPES +GLAPI void APIENTRY glMultiDrawArraysIndirectAMD (GLenum mode, const void *indirect, GLsizei primcount, GLsizei stride); +GLAPI void APIENTRY glMultiDrawElementsIndirectAMD (GLenum mode, GLenum type, const void *indirect, GLsizei primcount, GLsizei stride); +#endif +#endif /* GL_AMD_multi_draw_indirect */ + +#ifndef GL_AMD_name_gen_delete +#define GL_AMD_name_gen_delete 1 +#define GL_DATA_BUFFER_AMD 0x9151 +#define GL_PERFORMANCE_MONITOR_AMD 0x9152 +#define GL_QUERY_OBJECT_AMD 0x9153 +#define GL_VERTEX_ARRAY_OBJECT_AMD 0x9154 +#define GL_SAMPLER_OBJECT_AMD 0x9155 +typedef void (APIENTRYP PFNGLGENNAMESAMDPROC) (GLenum identifier, GLuint num, GLuint *names); +typedef void (APIENTRYP PFNGLDELETENAMESAMDPROC) (GLenum identifier, GLuint num, const GLuint *names); +typedef GLboolean (APIENTRYP PFNGLISNAMEAMDPROC) (GLenum identifier, GLuint name); +#ifdef GL_GLEXT_PROTOTYPES +GLAPI void APIENTRY glGenNamesAMD (GLenum identifier, GLuint num, GLuint *names); +GLAPI void APIENTRY glDeleteNamesAMD (GLenum identifier, GLuint num, const GLuint *names); +GLAPI GLboolean APIENTRY glIsNameAMD (GLenum identifier, GLuint name); +#endif +#endif /* GL_AMD_name_gen_delete */ + +#ifndef GL_AMD_occlusion_query_event +#define GL_AMD_occlusion_query_event 1 +#define GL_OCCLUSION_QUERY_EVENT_MASK_AMD 0x874F +#define GL_QUERY_DEPTH_PASS_EVENT_BIT_AMD 0x00000001 +#define GL_QUERY_DEPTH_FAIL_EVENT_BIT_AMD 0x00000002 +#define GL_QUERY_STENCIL_FAIL_EVENT_BIT_AMD 0x00000004 +#define GL_QUERY_DEPTH_BOUNDS_FAIL_EVENT_BIT_AMD 0x00000008 +#define GL_QUERY_ALL_EVENT_BITS_AMD 0xFFFFFFFF +typedef void (APIENTRYP PFNGLQUERYOBJECTPARAMETERUIAMDPROC) (GLenum target, GLuint id, GLenum pname, GLuint param); +#ifdef GL_GLEXT_PROTOTYPES +GLAPI void APIENTRY glQueryObjectParameteruiAMD (GLenum target, GLuint id, GLenum pname, GLuint param); +#endif +#endif /* GL_AMD_occlusion_query_event */ + +#ifndef GL_AMD_performance_monitor +#define GL_AMD_performance_monitor 1 +#define GL_COUNTER_TYPE_AMD 0x8BC0 +#define GL_COUNTER_RANGE_AMD 0x8BC1 +#define GL_UNSIGNED_INT64_AMD 0x8BC2 +#define GL_PERCENTAGE_AMD 0x8BC3 +#define GL_PERFMON_RESULT_AVAILABLE_AMD 0x8BC4 +#define GL_PERFMON_RESULT_SIZE_AMD 0x8BC5 +#define GL_PERFMON_RESULT_AMD 0x8BC6 +typedef void (APIENTRYP PFNGLGETPERFMONITORGROUPSAMDPROC) (GLint *numGroups, GLsizei groupsSize, GLuint *groups); +typedef void (APIENTRYP PFNGLGETPERFMONITORCOUNTERSAMDPROC) (GLuint group, GLint *numCounters, GLint *maxActiveCounters, GLsizei counterSize, GLuint *counters); +typedef void (APIENTRYP PFNGLGETPERFMONITORGROUPSTRINGAMDPROC) (GLuint group, GLsizei bufSize, GLsizei *length, GLchar *groupString); +typedef void (APIENTRYP PFNGLGETPERFMONITORCOUNTERSTRINGAMDPROC) (GLuint group, GLuint counter, GLsizei bufSize, GLsizei *length, GLchar *counterString); +typedef void (APIENTRYP PFNGLGETPERFMONITORCOUNTERINFOAMDPROC) (GLuint group, GLuint counter, GLenum pname, void *data); +typedef void (APIENTRYP PFNGLGENPERFMONITORSAMDPROC) (GLsizei n, GLuint *monitors); +typedef void (APIENTRYP PFNGLDELETEPERFMONITORSAMDPROC) (GLsizei n, GLuint *monitors); +typedef void (APIENTRYP PFNGLSELECTPERFMONITORCOUNTERSAMDPROC) (GLuint monitor, GLboolean enable, GLuint group, GLint numCounters, GLuint *counterList); +typedef void (APIENTRYP PFNGLBEGINPERFMONITORAMDPROC) (GLuint monitor); +typedef void (APIENTRYP PFNGLENDPERFMONITORAMDPROC) (GLuint monitor); +typedef void (APIENTRYP PFNGLGETPERFMONITORCOUNTERDATAAMDPROC) (GLuint monitor, GLenum pname, GLsizei dataSize, GLuint *data, GLint *bytesWritten); +#ifdef GL_GLEXT_PROTOTYPES +GLAPI void APIENTRY glGetPerfMonitorGroupsAMD (GLint *numGroups, GLsizei groupsSize, GLuint *groups); +GLAPI void APIENTRY glGetPerfMonitorCountersAMD (GLuint group, GLint *numCounters, GLint *maxActiveCounters, GLsizei counterSize, GLuint *counters); +GLAPI void APIENTRY glGetPerfMonitorGroupStringAMD (GLuint group, GLsizei bufSize, GLsizei *length, GLchar *groupString); +GLAPI void APIENTRY glGetPerfMonitorCounterStringAMD (GLuint group, GLuint counter, GLsizei bufSize, GLsizei *length, GLchar *counterString); +GLAPI void APIENTRY glGetPerfMonitorCounterInfoAMD (GLuint group, GLuint counter, GLenum pname, void *data); +GLAPI void APIENTRY glGenPerfMonitorsAMD (GLsizei n, GLuint *monitors); +GLAPI void APIENTRY glDeletePerfMonitorsAMD (GLsizei n, GLuint *monitors); +GLAPI void APIENTRY glSelectPerfMonitorCountersAMD (GLuint monitor, GLboolean enable, GLuint group, GLint numCounters, GLuint *counterList); +GLAPI void APIENTRY glBeginPerfMonitorAMD (GLuint monitor); +GLAPI void APIENTRY glEndPerfMonitorAMD (GLuint monitor); +GLAPI void APIENTRY glGetPerfMonitorCounterDataAMD (GLuint monitor, GLenum pname, GLsizei dataSize, GLuint *data, GLint *bytesWritten); +#endif +#endif /* GL_AMD_performance_monitor */ + +#ifndef GL_AMD_pinned_memory +#define GL_AMD_pinned_memory 1 +#define GL_EXTERNAL_VIRTUAL_MEMORY_BUFFER_AMD 0x9160 +#endif /* GL_AMD_pinned_memory */ + +#ifndef GL_AMD_query_buffer_object +#define GL_AMD_query_buffer_object 1 +#define GL_QUERY_BUFFER_AMD 0x9192 +#define GL_QUERY_BUFFER_BINDING_AMD 0x9193 +#define GL_QUERY_RESULT_NO_WAIT_AMD 0x9194 +#endif /* GL_AMD_query_buffer_object */ + +#ifndef GL_AMD_sample_positions +#define GL_AMD_sample_positions 1 +#define GL_SUBSAMPLE_DISTANCE_AMD 0x883F +typedef void (APIENTRYP PFNGLSETMULTISAMPLEFVAMDPROC) (GLenum pname, GLuint index, const GLfloat *val); +#ifdef GL_GLEXT_PROTOTYPES +GLAPI void APIENTRY glSetMultisamplefvAMD (GLenum pname, GLuint index, const GLfloat *val); +#endif +#endif /* GL_AMD_sample_positions */ + +#ifndef GL_AMD_seamless_cubemap_per_texture +#define GL_AMD_seamless_cubemap_per_texture 1 +#endif /* GL_AMD_seamless_cubemap_per_texture */ + +#ifndef GL_AMD_shader_atomic_counter_ops +#define GL_AMD_shader_atomic_counter_ops 1 +#endif /* GL_AMD_shader_atomic_counter_ops */ + +#ifndef GL_AMD_shader_stencil_export +#define GL_AMD_shader_stencil_export 1 +#endif /* GL_AMD_shader_stencil_export */ + +#ifndef GL_AMD_shader_trinary_minmax +#define GL_AMD_shader_trinary_minmax 1 +#endif /* GL_AMD_shader_trinary_minmax */ + +#ifndef GL_AMD_sparse_texture +#define GL_AMD_sparse_texture 1 +#define GL_VIRTUAL_PAGE_SIZE_X_AMD 0x9195 +#define GL_VIRTUAL_PAGE_SIZE_Y_AMD 0x9196 +#define GL_VIRTUAL_PAGE_SIZE_Z_AMD 0x9197 +#define GL_MAX_SPARSE_TEXTURE_SIZE_AMD 0x9198 +#define GL_MAX_SPARSE_3D_TEXTURE_SIZE_AMD 0x9199 +#define GL_MAX_SPARSE_ARRAY_TEXTURE_LAYERS 0x919A +#define GL_MIN_SPARSE_LEVEL_AMD 0x919B +#define GL_MIN_LOD_WARNING_AMD 0x919C +#define GL_TEXTURE_STORAGE_SPARSE_BIT_AMD 0x00000001 +typedef void (APIENTRYP PFNGLTEXSTORAGESPARSEAMDPROC) (GLenum target, GLenum internalFormat, GLsizei width, GLsizei height, GLsizei depth, GLsizei layers, GLbitfield flags); +typedef void (APIENTRYP PFNGLTEXTURESTORAGESPARSEAMDPROC) (GLuint texture, GLenum target, GLenum internalFormat, GLsizei width, GLsizei height, GLsizei depth, GLsizei layers, GLbitfield flags); +#ifdef GL_GLEXT_PROTOTYPES +GLAPI void APIENTRY glTexStorageSparseAMD (GLenum target, GLenum internalFormat, GLsizei width, GLsizei height, GLsizei depth, GLsizei layers, GLbitfield flags); +GLAPI void APIENTRY glTextureStorageSparseAMD (GLuint texture, GLenum target, GLenum internalFormat, GLsizei width, GLsizei height, GLsizei depth, GLsizei layers, GLbitfield flags); +#endif +#endif /* GL_AMD_sparse_texture */ + +#ifndef GL_AMD_stencil_operation_extended +#define GL_AMD_stencil_operation_extended 1 +#define GL_SET_AMD 0x874A +#define GL_REPLACE_VALUE_AMD 0x874B +#define GL_STENCIL_OP_VALUE_AMD 0x874C +#define GL_STENCIL_BACK_OP_VALUE_AMD 0x874D +typedef void (APIENTRYP PFNGLSTENCILOPVALUEAMDPROC) (GLenum face, GLuint value); +#ifdef GL_GLEXT_PROTOTYPES +GLAPI void APIENTRY glStencilOpValueAMD (GLenum face, GLuint value); +#endif +#endif /* GL_AMD_stencil_operation_extended */ + +#ifndef GL_AMD_texture_texture4 +#define GL_AMD_texture_texture4 1 +#endif /* GL_AMD_texture_texture4 */ + +#ifndef GL_AMD_transform_feedback3_lines_triangles +#define GL_AMD_transform_feedback3_lines_triangles 1 +#endif /* GL_AMD_transform_feedback3_lines_triangles */ + +#ifndef GL_AMD_vertex_shader_layer +#define GL_AMD_vertex_shader_layer 1 +#endif /* GL_AMD_vertex_shader_layer */ + +#ifndef GL_AMD_vertex_shader_tessellator +#define GL_AMD_vertex_shader_tessellator 1 +#define GL_SAMPLER_BUFFER_AMD 0x9001 +#define GL_INT_SAMPLER_BUFFER_AMD 0x9002 +#define GL_UNSIGNED_INT_SAMPLER_BUFFER_AMD 0x9003 +#define GL_TESSELLATION_MODE_AMD 0x9004 +#define GL_TESSELLATION_FACTOR_AMD 0x9005 +#define GL_DISCRETE_AMD 0x9006 +#define GL_CONTINUOUS_AMD 0x9007 +typedef void (APIENTRYP PFNGLTESSELLATIONFACTORAMDPROC) (GLfloat factor); +typedef void (APIENTRYP PFNGLTESSELLATIONMODEAMDPROC) (GLenum mode); +#ifdef GL_GLEXT_PROTOTYPES +GLAPI void APIENTRY glTessellationFactorAMD (GLfloat factor); +GLAPI void APIENTRY glTessellationModeAMD (GLenum mode); +#endif +#endif /* GL_AMD_vertex_shader_tessellator */ + +#ifndef GL_AMD_vertex_shader_viewport_index +#define GL_AMD_vertex_shader_viewport_index 1 +#endif /* GL_AMD_vertex_shader_viewport_index */ + +#ifndef GL_APPLE_aux_depth_stencil +#define GL_APPLE_aux_depth_stencil 1 +#define GL_AUX_DEPTH_STENCIL_APPLE 0x8A14 +#endif /* GL_APPLE_aux_depth_stencil */ + +#ifndef GL_APPLE_client_storage +#define GL_APPLE_client_storage 1 +#define GL_UNPACK_CLIENT_STORAGE_APPLE 0x85B2 +#endif /* GL_APPLE_client_storage */ + +#ifndef GL_APPLE_element_array +#define GL_APPLE_element_array 1 +#define GL_ELEMENT_ARRAY_APPLE 0x8A0C +#define GL_ELEMENT_ARRAY_TYPE_APPLE 0x8A0D +#define GL_ELEMENT_ARRAY_POINTER_APPLE 0x8A0E +typedef void (APIENTRYP PFNGLELEMENTPOINTERAPPLEPROC) (GLenum type, const void *pointer); +typedef void (APIENTRYP PFNGLDRAWELEMENTARRAYAPPLEPROC) (GLenum mode, GLint first, GLsizei count); +typedef void (APIENTRYP PFNGLDRAWRANGEELEMENTARRAYAPPLEPROC) (GLenum mode, GLuint start, GLuint end, GLint first, GLsizei count); +typedef void (APIENTRYP PFNGLMULTIDRAWELEMENTARRAYAPPLEPROC) (GLenum mode, const GLint *first, const GLsizei *count, GLsizei primcount); +typedef void (APIENTRYP PFNGLMULTIDRAWRANGEELEMENTARRAYAPPLEPROC) (GLenum mode, GLuint start, GLuint end, const GLint *first, const GLsizei *count, GLsizei primcount); +#ifdef GL_GLEXT_PROTOTYPES +GLAPI void APIENTRY glElementPointerAPPLE (GLenum type, const void *pointer); +GLAPI void APIENTRY glDrawElementArrayAPPLE (GLenum mode, GLint first, GLsizei count); +GLAPI void APIENTRY glDrawRangeElementArrayAPPLE (GLenum mode, GLuint start, GLuint end, GLint first, GLsizei count); +GLAPI void APIENTRY glMultiDrawElementArrayAPPLE (GLenum mode, const GLint *first, const GLsizei *count, GLsizei primcount); +GLAPI void APIENTRY glMultiDrawRangeElementArrayAPPLE (GLenum mode, GLuint start, GLuint end, const GLint *first, const GLsizei *count, GLsizei primcount); +#endif +#endif /* GL_APPLE_element_array */ + +#ifndef GL_APPLE_fence +#define GL_APPLE_fence 1 +#define GL_DRAW_PIXELS_APPLE 0x8A0A +#define GL_FENCE_APPLE 0x8A0B +typedef void (APIENTRYP PFNGLGENFENCESAPPLEPROC) (GLsizei n, GLuint *fences); +typedef void (APIENTRYP PFNGLDELETEFENCESAPPLEPROC) (GLsizei n, const GLuint *fences); +typedef void (APIENTRYP PFNGLSETFENCEAPPLEPROC) (GLuint fence); +typedef GLboolean (APIENTRYP PFNGLISFENCEAPPLEPROC) (GLuint fence); +typedef GLboolean (APIENTRYP PFNGLTESTFENCEAPPLEPROC) (GLuint fence); +typedef void (APIENTRYP PFNGLFINISHFENCEAPPLEPROC) (GLuint fence); +typedef GLboolean (APIENTRYP PFNGLTESTOBJECTAPPLEPROC) (GLenum object, GLuint name); +typedef void (APIENTRYP PFNGLFINISHOBJECTAPPLEPROC) (GLenum object, GLint name); +#ifdef GL_GLEXT_PROTOTYPES +GLAPI void APIENTRY glGenFencesAPPLE (GLsizei n, GLuint *fences); +GLAPI void APIENTRY glDeleteFencesAPPLE (GLsizei n, const GLuint *fences); +GLAPI void APIENTRY glSetFenceAPPLE (GLuint fence); +GLAPI GLboolean APIENTRY glIsFenceAPPLE (GLuint fence); +GLAPI GLboolean APIENTRY glTestFenceAPPLE (GLuint fence); +GLAPI void APIENTRY glFinishFenceAPPLE (GLuint fence); +GLAPI GLboolean APIENTRY glTestObjectAPPLE (GLenum object, GLuint name); +GLAPI void APIENTRY glFinishObjectAPPLE (GLenum object, GLint name); +#endif +#endif /* GL_APPLE_fence */ + +#ifndef GL_APPLE_float_pixels +#define GL_APPLE_float_pixels 1 +#define GL_HALF_APPLE 0x140B +#define GL_RGBA_FLOAT32_APPLE 0x8814 +#define GL_RGB_FLOAT32_APPLE 0x8815 +#define GL_ALPHA_FLOAT32_APPLE 0x8816 +#define GL_INTENSITY_FLOAT32_APPLE 0x8817 +#define GL_LUMINANCE_FLOAT32_APPLE 0x8818 +#define GL_LUMINANCE_ALPHA_FLOAT32_APPLE 0x8819 +#define GL_RGBA_FLOAT16_APPLE 0x881A +#define GL_RGB_FLOAT16_APPLE 0x881B +#define GL_ALPHA_FLOAT16_APPLE 0x881C +#define GL_INTENSITY_FLOAT16_APPLE 0x881D +#define GL_LUMINANCE_FLOAT16_APPLE 0x881E +#define GL_LUMINANCE_ALPHA_FLOAT16_APPLE 0x881F +#define GL_COLOR_FLOAT_APPLE 0x8A0F +#endif /* GL_APPLE_float_pixels */ + +#ifndef GL_APPLE_flush_buffer_range +#define GL_APPLE_flush_buffer_range 1 +#define GL_BUFFER_SERIALIZED_MODIFY_APPLE 0x8A12 +#define GL_BUFFER_FLUSHING_UNMAP_APPLE 0x8A13 +typedef void (APIENTRYP PFNGLBUFFERPARAMETERIAPPLEPROC) (GLenum target, GLenum pname, GLint param); +typedef void (APIENTRYP PFNGLFLUSHMAPPEDBUFFERRANGEAPPLEPROC) (GLenum target, GLintptr offset, GLsizeiptr size); +#ifdef GL_GLEXT_PROTOTYPES +GLAPI void APIENTRY glBufferParameteriAPPLE (GLenum target, GLenum pname, GLint param); +GLAPI void APIENTRY glFlushMappedBufferRangeAPPLE (GLenum target, GLintptr offset, GLsizeiptr size); +#endif +#endif /* GL_APPLE_flush_buffer_range */ + +#ifndef GL_APPLE_object_purgeable +#define GL_APPLE_object_purgeable 1 +#define GL_BUFFER_OBJECT_APPLE 0x85B3 +#define GL_RELEASED_APPLE 0x8A19 +#define GL_VOLATILE_APPLE 0x8A1A +#define GL_RETAINED_APPLE 0x8A1B +#define GL_UNDEFINED_APPLE 0x8A1C +#define GL_PURGEABLE_APPLE 0x8A1D +typedef GLenum (APIENTRYP PFNGLOBJECTPURGEABLEAPPLEPROC) (GLenum objectType, GLuint name, GLenum option); +typedef GLenum (APIENTRYP PFNGLOBJECTUNPURGEABLEAPPLEPROC) (GLenum objectType, GLuint name, GLenum option); +typedef void (APIENTRYP PFNGLGETOBJECTPARAMETERIVAPPLEPROC) (GLenum objectType, GLuint name, GLenum pname, GLint *params); +#ifdef GL_GLEXT_PROTOTYPES +GLAPI GLenum APIENTRY glObjectPurgeableAPPLE (GLenum objectType, GLuint name, GLenum option); +GLAPI GLenum APIENTRY glObjectUnpurgeableAPPLE (GLenum objectType, GLuint name, GLenum option); +GLAPI void APIENTRY glGetObjectParameterivAPPLE (GLenum objectType, GLuint name, GLenum pname, GLint *params); +#endif +#endif /* GL_APPLE_object_purgeable */ + +#ifndef GL_APPLE_rgb_422 +#define GL_APPLE_rgb_422 1 +#define GL_RGB_422_APPLE 0x8A1F +#define GL_UNSIGNED_SHORT_8_8_APPLE 0x85BA +#define GL_UNSIGNED_SHORT_8_8_REV_APPLE 0x85BB +#define GL_RGB_RAW_422_APPLE 0x8A51 +#endif /* GL_APPLE_rgb_422 */ + +#ifndef GL_APPLE_row_bytes +#define GL_APPLE_row_bytes 1 +#define GL_PACK_ROW_BYTES_APPLE 0x8A15 +#define GL_UNPACK_ROW_BYTES_APPLE 0x8A16 +#endif /* GL_APPLE_row_bytes */ + +#ifndef GL_APPLE_specular_vector +#define GL_APPLE_specular_vector 1 +#define GL_LIGHT_MODEL_SPECULAR_VECTOR_APPLE 0x85B0 +#endif /* GL_APPLE_specular_vector */ + +#ifndef GL_APPLE_texture_range +#define GL_APPLE_texture_range 1 +#define GL_TEXTURE_RANGE_LENGTH_APPLE 0x85B7 +#define GL_TEXTURE_RANGE_POINTER_APPLE 0x85B8 +#define GL_TEXTURE_STORAGE_HINT_APPLE 0x85BC +#define GL_STORAGE_PRIVATE_APPLE 0x85BD +#define GL_STORAGE_CACHED_APPLE 0x85BE +#define GL_STORAGE_SHARED_APPLE 0x85BF +typedef void (APIENTRYP PFNGLTEXTURERANGEAPPLEPROC) (GLenum target, GLsizei length, const void *pointer); +typedef void (APIENTRYP PFNGLGETTEXPARAMETERPOINTERVAPPLEPROC) (GLenum target, GLenum pname, void **params); +#ifdef GL_GLEXT_PROTOTYPES +GLAPI void APIENTRY glTextureRangeAPPLE (GLenum target, GLsizei length, const void *pointer); +GLAPI void APIENTRY glGetTexParameterPointervAPPLE (GLenum target, GLenum pname, void **params); +#endif +#endif /* GL_APPLE_texture_range */ + +#ifndef GL_APPLE_transform_hint +#define GL_APPLE_transform_hint 1 +#define GL_TRANSFORM_HINT_APPLE 0x85B1 +#endif /* GL_APPLE_transform_hint */ + +#ifndef GL_APPLE_vertex_array_object +#define GL_APPLE_vertex_array_object 1 +#define GL_VERTEX_ARRAY_BINDING_APPLE 0x85B5 +typedef void (APIENTRYP PFNGLBINDVERTEXARRAYAPPLEPROC) (GLuint array); +typedef void (APIENTRYP PFNGLDELETEVERTEXARRAYSAPPLEPROC) (GLsizei n, const GLuint *arrays); +typedef void (APIENTRYP PFNGLGENVERTEXARRAYSAPPLEPROC) (GLsizei n, GLuint *arrays); +typedef GLboolean (APIENTRYP PFNGLISVERTEXARRAYAPPLEPROC) (GLuint array); +#ifdef GL_GLEXT_PROTOTYPES +GLAPI void APIENTRY glBindVertexArrayAPPLE (GLuint array); +GLAPI void APIENTRY glDeleteVertexArraysAPPLE (GLsizei n, const GLuint *arrays); +GLAPI void APIENTRY glGenVertexArraysAPPLE (GLsizei n, GLuint *arrays); +GLAPI GLboolean APIENTRY glIsVertexArrayAPPLE (GLuint array); +#endif +#endif /* GL_APPLE_vertex_array_object */ + +#ifndef GL_APPLE_vertex_array_range +#define GL_APPLE_vertex_array_range 1 +#define GL_VERTEX_ARRAY_RANGE_APPLE 0x851D +#define GL_VERTEX_ARRAY_RANGE_LENGTH_APPLE 0x851E +#define GL_VERTEX_ARRAY_STORAGE_HINT_APPLE 0x851F +#define GL_VERTEX_ARRAY_RANGE_POINTER_APPLE 0x8521 +#define GL_STORAGE_CLIENT_APPLE 0x85B4 +typedef void (APIENTRYP PFNGLVERTEXARRAYRANGEAPPLEPROC) (GLsizei length, void *pointer); +typedef void (APIENTRYP PFNGLFLUSHVERTEXARRAYRANGEAPPLEPROC) (GLsizei length, void *pointer); +typedef void (APIENTRYP PFNGLVERTEXARRAYPARAMETERIAPPLEPROC) (GLenum pname, GLint param); +#ifdef GL_GLEXT_PROTOTYPES +GLAPI void APIENTRY glVertexArrayRangeAPPLE (GLsizei length, void *pointer); +GLAPI void APIENTRY glFlushVertexArrayRangeAPPLE (GLsizei length, void *pointer); +GLAPI void APIENTRY glVertexArrayParameteriAPPLE (GLenum pname, GLint param); +#endif +#endif /* GL_APPLE_vertex_array_range */ + +#ifndef GL_APPLE_vertex_program_evaluators +#define GL_APPLE_vertex_program_evaluators 1 +#define GL_VERTEX_ATTRIB_MAP1_APPLE 0x8A00 +#define GL_VERTEX_ATTRIB_MAP2_APPLE 0x8A01 +#define GL_VERTEX_ATTRIB_MAP1_SIZE_APPLE 0x8A02 +#define GL_VERTEX_ATTRIB_MAP1_COEFF_APPLE 0x8A03 +#define GL_VERTEX_ATTRIB_MAP1_ORDER_APPLE 0x8A04 +#define GL_VERTEX_ATTRIB_MAP1_DOMAIN_APPLE 0x8A05 +#define GL_VERTEX_ATTRIB_MAP2_SIZE_APPLE 0x8A06 +#define GL_VERTEX_ATTRIB_MAP2_COEFF_APPLE 0x8A07 +#define GL_VERTEX_ATTRIB_MAP2_ORDER_APPLE 0x8A08 +#define GL_VERTEX_ATTRIB_MAP2_DOMAIN_APPLE 0x8A09 +typedef void (APIENTRYP PFNGLENABLEVERTEXATTRIBAPPLEPROC) (GLuint index, GLenum pname); +typedef void (APIENTRYP PFNGLDISABLEVERTEXATTRIBAPPLEPROC) (GLuint index, GLenum pname); +typedef GLboolean (APIENTRYP PFNGLISVERTEXATTRIBENABLEDAPPLEPROC) (GLuint index, GLenum pname); +typedef void (APIENTRYP PFNGLMAPVERTEXATTRIB1DAPPLEPROC) (GLuint index, GLuint size, GLdouble u1, GLdouble u2, GLint stride, GLint order, const GLdouble *points); +typedef void (APIENTRYP PFNGLMAPVERTEXATTRIB1FAPPLEPROC) (GLuint index, GLuint size, GLfloat u1, GLfloat u2, GLint stride, GLint order, const GLfloat *points); +typedef void (APIENTRYP PFNGLMAPVERTEXATTRIB2DAPPLEPROC) (GLuint index, GLuint size, GLdouble u1, GLdouble u2, GLint ustride, GLint uorder, GLdouble v1, GLdouble v2, GLint vstride, GLint vorder, const GLdouble *points); +typedef void (APIENTRYP PFNGLMAPVERTEXATTRIB2FAPPLEPROC) (GLuint index, GLuint size, GLfloat u1, GLfloat u2, GLint ustride, GLint uorder, GLfloat v1, GLfloat v2, GLint vstride, GLint vorder, const GLfloat *points); +#ifdef GL_GLEXT_PROTOTYPES +GLAPI void APIENTRY glEnableVertexAttribAPPLE (GLuint index, GLenum pname); +GLAPI void APIENTRY glDisableVertexAttribAPPLE (GLuint index, GLenum pname); +GLAPI GLboolean APIENTRY glIsVertexAttribEnabledAPPLE (GLuint index, GLenum pname); +GLAPI void APIENTRY glMapVertexAttrib1dAPPLE (GLuint index, GLuint size, GLdouble u1, GLdouble u2, GLint stride, GLint order, const GLdouble *points); +GLAPI void APIENTRY glMapVertexAttrib1fAPPLE (GLuint index, GLuint size, GLfloat u1, GLfloat u2, GLint stride, GLint order, const GLfloat *points); +GLAPI void APIENTRY glMapVertexAttrib2dAPPLE (GLuint index, GLuint size, GLdouble u1, GLdouble u2, GLint ustride, GLint uorder, GLdouble v1, GLdouble v2, GLint vstride, GLint vorder, const GLdouble *points); +GLAPI void APIENTRY glMapVertexAttrib2fAPPLE (GLuint index, GLuint size, GLfloat u1, GLfloat u2, GLint ustride, GLint uorder, GLfloat v1, GLfloat v2, GLint vstride, GLint vorder, const GLfloat *points); +#endif +#endif /* GL_APPLE_vertex_program_evaluators */ + +#ifndef GL_APPLE_ycbcr_422 +#define GL_APPLE_ycbcr_422 1 +#define GL_YCBCR_422_APPLE 0x85B9 +#endif /* GL_APPLE_ycbcr_422 */ + +#ifndef GL_ATI_draw_buffers +#define GL_ATI_draw_buffers 1 +#define GL_MAX_DRAW_BUFFERS_ATI 0x8824 +#define GL_DRAW_BUFFER0_ATI 0x8825 +#define GL_DRAW_BUFFER1_ATI 0x8826 +#define GL_DRAW_BUFFER2_ATI 0x8827 +#define GL_DRAW_BUFFER3_ATI 0x8828 +#define GL_DRAW_BUFFER4_ATI 0x8829 +#define GL_DRAW_BUFFER5_ATI 0x882A +#define GL_DRAW_BUFFER6_ATI 0x882B +#define GL_DRAW_BUFFER7_ATI 0x882C +#define GL_DRAW_BUFFER8_ATI 0x882D +#define GL_DRAW_BUFFER9_ATI 0x882E +#define GL_DRAW_BUFFER10_ATI 0x882F +#define GL_DRAW_BUFFER11_ATI 0x8830 +#define GL_DRAW_BUFFER12_ATI 0x8831 +#define GL_DRAW_BUFFER13_ATI 0x8832 +#define GL_DRAW_BUFFER14_ATI 0x8833 +#define GL_DRAW_BUFFER15_ATI 0x8834 +typedef void (APIENTRYP PFNGLDRAWBUFFERSATIPROC) (GLsizei n, const GLenum *bufs); +#ifdef GL_GLEXT_PROTOTYPES +GLAPI void APIENTRY glDrawBuffersATI (GLsizei n, const GLenum *bufs); +#endif +#endif /* GL_ATI_draw_buffers */ + +#ifndef GL_ATI_element_array +#define GL_ATI_element_array 1 +#define GL_ELEMENT_ARRAY_ATI 0x8768 +#define GL_ELEMENT_ARRAY_TYPE_ATI 0x8769 +#define GL_ELEMENT_ARRAY_POINTER_ATI 0x876A +typedef void (APIENTRYP PFNGLELEMENTPOINTERATIPROC) (GLenum type, const void *pointer); +typedef void (APIENTRYP PFNGLDRAWELEMENTARRAYATIPROC) (GLenum mode, GLsizei count); +typedef void (APIENTRYP PFNGLDRAWRANGEELEMENTARRAYATIPROC) (GLenum mode, GLuint start, GLuint end, GLsizei count); +#ifdef GL_GLEXT_PROTOTYPES +GLAPI void APIENTRY glElementPointerATI (GLenum type, const void *pointer); +GLAPI void APIENTRY glDrawElementArrayATI (GLenum mode, GLsizei count); +GLAPI void APIENTRY glDrawRangeElementArrayATI (GLenum mode, GLuint start, GLuint end, GLsizei count); +#endif +#endif /* GL_ATI_element_array */ + +#ifndef GL_ATI_envmap_bumpmap +#define GL_ATI_envmap_bumpmap 1 +#define GL_BUMP_ROT_MATRIX_ATI 0x8775 +#define GL_BUMP_ROT_MATRIX_SIZE_ATI 0x8776 +#define GL_BUMP_NUM_TEX_UNITS_ATI 0x8777 +#define GL_BUMP_TEX_UNITS_ATI 0x8778 +#define GL_DUDV_ATI 0x8779 +#define GL_DU8DV8_ATI 0x877A +#define GL_BUMP_ENVMAP_ATI 0x877B +#define GL_BUMP_TARGET_ATI 0x877C +typedef void (APIENTRYP PFNGLTEXBUMPPARAMETERIVATIPROC) (GLenum pname, const GLint *param); +typedef void (APIENTRYP PFNGLTEXBUMPPARAMETERFVATIPROC) (GLenum pname, const GLfloat *param); +typedef void (APIENTRYP PFNGLGETTEXBUMPPARAMETERIVATIPROC) (GLenum pname, GLint *param); +typedef void (APIENTRYP PFNGLGETTEXBUMPPARAMETERFVATIPROC) (GLenum pname, GLfloat *param); +#ifdef GL_GLEXT_PROTOTYPES +GLAPI void APIENTRY glTexBumpParameterivATI (GLenum pname, const GLint *param); +GLAPI void APIENTRY glTexBumpParameterfvATI (GLenum pname, const GLfloat *param); +GLAPI void APIENTRY glGetTexBumpParameterivATI (GLenum pname, GLint *param); +GLAPI void APIENTRY glGetTexBumpParameterfvATI (GLenum pname, GLfloat *param); +#endif +#endif /* GL_ATI_envmap_bumpmap */ + +#ifndef GL_ATI_fragment_shader +#define GL_ATI_fragment_shader 1 +#define GL_FRAGMENT_SHADER_ATI 0x8920 +#define GL_REG_0_ATI 0x8921 +#define GL_REG_1_ATI 0x8922 +#define GL_REG_2_ATI 0x8923 +#define GL_REG_3_ATI 0x8924 +#define GL_REG_4_ATI 0x8925 +#define GL_REG_5_ATI 0x8926 +#define GL_REG_6_ATI 0x8927 +#define GL_REG_7_ATI 0x8928 +#define GL_REG_8_ATI 0x8929 +#define GL_REG_9_ATI 0x892A +#define GL_REG_10_ATI 0x892B +#define GL_REG_11_ATI 0x892C +#define GL_REG_12_ATI 0x892D +#define GL_REG_13_ATI 0x892E +#define GL_REG_14_ATI 0x892F +#define GL_REG_15_ATI 0x8930 +#define GL_REG_16_ATI 0x8931 +#define GL_REG_17_ATI 0x8932 +#define GL_REG_18_ATI 0x8933 +#define GL_REG_19_ATI 0x8934 +#define GL_REG_20_ATI 0x8935 +#define GL_REG_21_ATI 0x8936 +#define GL_REG_22_ATI 0x8937 +#define GL_REG_23_ATI 0x8938 +#define GL_REG_24_ATI 0x8939 +#define GL_REG_25_ATI 0x893A +#define GL_REG_26_ATI 0x893B +#define GL_REG_27_ATI 0x893C +#define GL_REG_28_ATI 0x893D +#define GL_REG_29_ATI 0x893E +#define GL_REG_30_ATI 0x893F +#define GL_REG_31_ATI 0x8940 +#define GL_CON_0_ATI 0x8941 +#define GL_CON_1_ATI 0x8942 +#define GL_CON_2_ATI 0x8943 +#define GL_CON_3_ATI 0x8944 +#define GL_CON_4_ATI 0x8945 +#define GL_CON_5_ATI 0x8946 +#define GL_CON_6_ATI 0x8947 +#define GL_CON_7_ATI 0x8948 +#define GL_CON_8_ATI 0x8949 +#define GL_CON_9_ATI 0x894A +#define GL_CON_10_ATI 0x894B +#define GL_CON_11_ATI 0x894C +#define GL_CON_12_ATI 0x894D +#define GL_CON_13_ATI 0x894E +#define GL_CON_14_ATI 0x894F +#define GL_CON_15_ATI 0x8950 +#define GL_CON_16_ATI 0x8951 +#define GL_CON_17_ATI 0x8952 +#define GL_CON_18_ATI 0x8953 +#define GL_CON_19_ATI 0x8954 +#define GL_CON_20_ATI 0x8955 +#define GL_CON_21_ATI 0x8956 +#define GL_CON_22_ATI 0x8957 +#define GL_CON_23_ATI 0x8958 +#define GL_CON_24_ATI 0x8959 +#define GL_CON_25_ATI 0x895A +#define GL_CON_26_ATI 0x895B +#define GL_CON_27_ATI 0x895C +#define GL_CON_28_ATI 0x895D +#define GL_CON_29_ATI 0x895E +#define GL_CON_30_ATI 0x895F +#define GL_CON_31_ATI 0x8960 +#define GL_MOV_ATI 0x8961 +#define GL_ADD_ATI 0x8963 +#define GL_MUL_ATI 0x8964 +#define GL_SUB_ATI 0x8965 +#define GL_DOT3_ATI 0x8966 +#define GL_DOT4_ATI 0x8967 +#define GL_MAD_ATI 0x8968 +#define GL_LERP_ATI 0x8969 +#define GL_CND_ATI 0x896A +#define GL_CND0_ATI 0x896B +#define GL_DOT2_ADD_ATI 0x896C +#define GL_SECONDARY_INTERPOLATOR_ATI 0x896D +#define GL_NUM_FRAGMENT_REGISTERS_ATI 0x896E +#define GL_NUM_FRAGMENT_CONSTANTS_ATI 0x896F +#define GL_NUM_PASSES_ATI 0x8970 +#define GL_NUM_INSTRUCTIONS_PER_PASS_ATI 0x8971 +#define GL_NUM_INSTRUCTIONS_TOTAL_ATI 0x8972 +#define GL_NUM_INPUT_INTERPOLATOR_COMPONENTS_ATI 0x8973 +#define GL_NUM_LOOPBACK_COMPONENTS_ATI 0x8974 +#define GL_COLOR_ALPHA_PAIRING_ATI 0x8975 +#define GL_SWIZZLE_STR_ATI 0x8976 +#define GL_SWIZZLE_STQ_ATI 0x8977 +#define GL_SWIZZLE_STR_DR_ATI 0x8978 +#define GL_SWIZZLE_STQ_DQ_ATI 0x8979 +#define GL_SWIZZLE_STRQ_ATI 0x897A +#define GL_SWIZZLE_STRQ_DQ_ATI 0x897B +#define GL_RED_BIT_ATI 0x00000001 +#define GL_GREEN_BIT_ATI 0x00000002 +#define GL_BLUE_BIT_ATI 0x00000004 +#define GL_2X_BIT_ATI 0x00000001 +#define GL_4X_BIT_ATI 0x00000002 +#define GL_8X_BIT_ATI 0x00000004 +#define GL_HALF_BIT_ATI 0x00000008 +#define GL_QUARTER_BIT_ATI 0x00000010 +#define GL_EIGHTH_BIT_ATI 0x00000020 +#define GL_SATURATE_BIT_ATI 0x00000040 +#define GL_COMP_BIT_ATI 0x00000002 +#define GL_NEGATE_BIT_ATI 0x00000004 +#define GL_BIAS_BIT_ATI 0x00000008 +typedef GLuint (APIENTRYP PFNGLGENFRAGMENTSHADERSATIPROC) (GLuint range); +typedef void (APIENTRYP PFNGLBINDFRAGMENTSHADERATIPROC) (GLuint id); +typedef void (APIENTRYP PFNGLDELETEFRAGMENTSHADERATIPROC) (GLuint id); +typedef void (APIENTRYP PFNGLBEGINFRAGMENTSHADERATIPROC) (void); +typedef void (APIENTRYP PFNGLENDFRAGMENTSHADERATIPROC) (void); +typedef void (APIENTRYP PFNGLPASSTEXCOORDATIPROC) (GLuint dst, GLuint coord, GLenum swizzle); +typedef void (APIENTRYP PFNGLSAMPLEMAPATIPROC) (GLuint dst, GLuint interp, GLenum swizzle); +typedef void (APIENTRYP PFNGLCOLORFRAGMENTOP1ATIPROC) (GLenum op, GLuint dst, GLuint dstMask, GLuint dstMod, GLuint arg1, GLuint arg1Rep, GLuint arg1Mod); +typedef void (APIENTRYP PFNGLCOLORFRAGMENTOP2ATIPROC) (GLenum op, GLuint dst, GLuint dstMask, GLuint dstMod, GLuint arg1, GLuint arg1Rep, GLuint arg1Mod, GLuint arg2, GLuint arg2Rep, GLuint arg2Mod); +typedef void (APIENTRYP PFNGLCOLORFRAGMENTOP3ATIPROC) (GLenum op, GLuint dst, GLuint dstMask, GLuint dstMod, GLuint arg1, GLuint arg1Rep, GLuint arg1Mod, GLuint arg2, GLuint arg2Rep, GLuint arg2Mod, GLuint arg3, GLuint arg3Rep, GLuint arg3Mod); +typedef void (APIENTRYP PFNGLALPHAFRAGMENTOP1ATIPROC) (GLenum op, GLuint dst, GLuint dstMod, GLuint arg1, GLuint arg1Rep, GLuint arg1Mod); +typedef void (APIENTRYP PFNGLALPHAFRAGMENTOP2ATIPROC) (GLenum op, GLuint dst, GLuint dstMod, GLuint arg1, GLuint arg1Rep, GLuint arg1Mod, GLuint arg2, GLuint arg2Rep, GLuint arg2Mod); +typedef void (APIENTRYP PFNGLALPHAFRAGMENTOP3ATIPROC) (GLenum op, GLuint dst, GLuint dstMod, GLuint arg1, GLuint arg1Rep, GLuint arg1Mod, GLuint arg2, GLuint arg2Rep, GLuint arg2Mod, GLuint arg3, GLuint arg3Rep, GLuint arg3Mod); +typedef void (APIENTRYP PFNGLSETFRAGMENTSHADERCONSTANTATIPROC) (GLuint dst, const GLfloat *value); +#ifdef GL_GLEXT_PROTOTYPES +GLAPI GLuint APIENTRY glGenFragmentShadersATI (GLuint range); +GLAPI void APIENTRY glBindFragmentShaderATI (GLuint id); +GLAPI void APIENTRY glDeleteFragmentShaderATI (GLuint id); +GLAPI void APIENTRY glBeginFragmentShaderATI (void); +GLAPI void APIENTRY glEndFragmentShaderATI (void); +GLAPI void APIENTRY glPassTexCoordATI (GLuint dst, GLuint coord, GLenum swizzle); +GLAPI void APIENTRY glSampleMapATI (GLuint dst, GLuint interp, GLenum swizzle); +GLAPI void APIENTRY glColorFragmentOp1ATI (GLenum op, GLuint dst, GLuint dstMask, GLuint dstMod, GLuint arg1, GLuint arg1Rep, GLuint arg1Mod); +GLAPI void APIENTRY glColorFragmentOp2ATI (GLenum op, GLuint dst, GLuint dstMask, GLuint dstMod, GLuint arg1, GLuint arg1Rep, GLuint arg1Mod, GLuint arg2, GLuint arg2Rep, GLuint arg2Mod); +GLAPI void APIENTRY glColorFragmentOp3ATI (GLenum op, GLuint dst, GLuint dstMask, GLuint dstMod, GLuint arg1, GLuint arg1Rep, GLuint arg1Mod, GLuint arg2, GLuint arg2Rep, GLuint arg2Mod, GLuint arg3, GLuint arg3Rep, GLuint arg3Mod); +GLAPI void APIENTRY glAlphaFragmentOp1ATI (GLenum op, GLuint dst, GLuint dstMod, GLuint arg1, GLuint arg1Rep, GLuint arg1Mod); +GLAPI void APIENTRY glAlphaFragmentOp2ATI (GLenum op, GLuint dst, GLuint dstMod, GLuint arg1, GLuint arg1Rep, GLuint arg1Mod, GLuint arg2, GLuint arg2Rep, GLuint arg2Mod); +GLAPI void APIENTRY glAlphaFragmentOp3ATI (GLenum op, GLuint dst, GLuint dstMod, GLuint arg1, GLuint arg1Rep, GLuint arg1Mod, GLuint arg2, GLuint arg2Rep, GLuint arg2Mod, GLuint arg3, GLuint arg3Rep, GLuint arg3Mod); +GLAPI void APIENTRY glSetFragmentShaderConstantATI (GLuint dst, const GLfloat *value); +#endif +#endif /* GL_ATI_fragment_shader */ + +#ifndef GL_ATI_map_object_buffer +#define GL_ATI_map_object_buffer 1 +typedef void *(APIENTRYP PFNGLMAPOBJECTBUFFERATIPROC) (GLuint buffer); +typedef void (APIENTRYP PFNGLUNMAPOBJECTBUFFERATIPROC) (GLuint buffer); +#ifdef GL_GLEXT_PROTOTYPES +GLAPI void *APIENTRY glMapObjectBufferATI (GLuint buffer); +GLAPI void APIENTRY glUnmapObjectBufferATI (GLuint buffer); +#endif +#endif /* GL_ATI_map_object_buffer */ + +#ifndef GL_ATI_meminfo +#define GL_ATI_meminfo 1 +#define GL_VBO_FREE_MEMORY_ATI 0x87FB +#define GL_TEXTURE_FREE_MEMORY_ATI 0x87FC +#define GL_RENDERBUFFER_FREE_MEMORY_ATI 0x87FD +#endif /* GL_ATI_meminfo */ + +#ifndef GL_ATI_pixel_format_float +#define GL_ATI_pixel_format_float 1 +#define GL_RGBA_FLOAT_MODE_ATI 0x8820 +#define GL_COLOR_CLEAR_UNCLAMPED_VALUE_ATI 0x8835 +#endif /* GL_ATI_pixel_format_float */ + +#ifndef GL_ATI_pn_triangles +#define GL_ATI_pn_triangles 1 +#define GL_PN_TRIANGLES_ATI 0x87F0 +#define GL_MAX_PN_TRIANGLES_TESSELATION_LEVEL_ATI 0x87F1 +#define GL_PN_TRIANGLES_POINT_MODE_ATI 0x87F2 +#define GL_PN_TRIANGLES_NORMAL_MODE_ATI 0x87F3 +#define GL_PN_TRIANGLES_TESSELATION_LEVEL_ATI 0x87F4 +#define GL_PN_TRIANGLES_POINT_MODE_LINEAR_ATI 0x87F5 +#define GL_PN_TRIANGLES_POINT_MODE_CUBIC_ATI 0x87F6 +#define GL_PN_TRIANGLES_NORMAL_MODE_LINEAR_ATI 0x87F7 +#define GL_PN_TRIANGLES_NORMAL_MODE_QUADRATIC_ATI 0x87F8 +typedef void (APIENTRYP PFNGLPNTRIANGLESIATIPROC) (GLenum pname, GLint param); +typedef void (APIENTRYP PFNGLPNTRIANGLESFATIPROC) (GLenum pname, GLfloat param); +#ifdef GL_GLEXT_PROTOTYPES +GLAPI void APIENTRY glPNTrianglesiATI (GLenum pname, GLint param); +GLAPI void APIENTRY glPNTrianglesfATI (GLenum pname, GLfloat param); +#endif +#endif /* GL_ATI_pn_triangles */ + +#ifndef GL_ATI_separate_stencil +#define GL_ATI_separate_stencil 1 +#define GL_STENCIL_BACK_FUNC_ATI 0x8800 +#define GL_STENCIL_BACK_FAIL_ATI 0x8801 +#define GL_STENCIL_BACK_PASS_DEPTH_FAIL_ATI 0x8802 +#define GL_STENCIL_BACK_PASS_DEPTH_PASS_ATI 0x8803 +typedef void (APIENTRYP PFNGLSTENCILOPSEPARATEATIPROC) (GLenum face, GLenum sfail, GLenum dpfail, GLenum dppass); +typedef void (APIENTRYP PFNGLSTENCILFUNCSEPARATEATIPROC) (GLenum frontfunc, GLenum backfunc, GLint ref, GLuint mask); +#ifdef GL_GLEXT_PROTOTYPES +GLAPI void APIENTRY glStencilOpSeparateATI (GLenum face, GLenum sfail, GLenum dpfail, GLenum dppass); +GLAPI void APIENTRY glStencilFuncSeparateATI (GLenum frontfunc, GLenum backfunc, GLint ref, GLuint mask); +#endif +#endif /* GL_ATI_separate_stencil */ + +#ifndef GL_ATI_text_fragment_shader +#define GL_ATI_text_fragment_shader 1 +#define GL_TEXT_FRAGMENT_SHADER_ATI 0x8200 +#endif /* GL_ATI_text_fragment_shader */ + +#ifndef GL_ATI_texture_env_combine3 +#define GL_ATI_texture_env_combine3 1 +#define GL_MODULATE_ADD_ATI 0x8744 +#define GL_MODULATE_SIGNED_ADD_ATI 0x8745 +#define GL_MODULATE_SUBTRACT_ATI 0x8746 +#endif /* GL_ATI_texture_env_combine3 */ + +#ifndef GL_ATI_texture_float +#define GL_ATI_texture_float 1 +#define GL_RGBA_FLOAT32_ATI 0x8814 +#define GL_RGB_FLOAT32_ATI 0x8815 +#define GL_ALPHA_FLOAT32_ATI 0x8816 +#define GL_INTENSITY_FLOAT32_ATI 0x8817 +#define GL_LUMINANCE_FLOAT32_ATI 0x8818 +#define GL_LUMINANCE_ALPHA_FLOAT32_ATI 0x8819 +#define GL_RGBA_FLOAT16_ATI 0x881A +#define GL_RGB_FLOAT16_ATI 0x881B +#define GL_ALPHA_FLOAT16_ATI 0x881C +#define GL_INTENSITY_FLOAT16_ATI 0x881D +#define GL_LUMINANCE_FLOAT16_ATI 0x881E +#define GL_LUMINANCE_ALPHA_FLOAT16_ATI 0x881F +#endif /* GL_ATI_texture_float */ + +#ifndef GL_ATI_texture_mirror_once +#define GL_ATI_texture_mirror_once 1 +#define GL_MIRROR_CLAMP_ATI 0x8742 +#define GL_MIRROR_CLAMP_TO_EDGE_ATI 0x8743 +#endif /* GL_ATI_texture_mirror_once */ + +#ifndef GL_ATI_vertex_array_object +#define GL_ATI_vertex_array_object 1 +#define GL_STATIC_ATI 0x8760 +#define GL_DYNAMIC_ATI 0x8761 +#define GL_PRESERVE_ATI 0x8762 +#define GL_DISCARD_ATI 0x8763 +#define GL_OBJECT_BUFFER_SIZE_ATI 0x8764 +#define GL_OBJECT_BUFFER_USAGE_ATI 0x8765 +#define GL_ARRAY_OBJECT_BUFFER_ATI 0x8766 +#define GL_ARRAY_OBJECT_OFFSET_ATI 0x8767 +typedef GLuint (APIENTRYP PFNGLNEWOBJECTBUFFERATIPROC) (GLsizei size, const void *pointer, GLenum usage); +typedef GLboolean (APIENTRYP PFNGLISOBJECTBUFFERATIPROC) (GLuint buffer); +typedef void (APIENTRYP PFNGLUPDATEOBJECTBUFFERATIPROC) (GLuint buffer, GLuint offset, GLsizei size, const void *pointer, GLenum preserve); +typedef void (APIENTRYP PFNGLGETOBJECTBUFFERFVATIPROC) (GLuint buffer, GLenum pname, GLfloat *params); +typedef void (APIENTRYP PFNGLGETOBJECTBUFFERIVATIPROC) (GLuint buffer, GLenum pname, GLint *params); +typedef void (APIENTRYP PFNGLFREEOBJECTBUFFERATIPROC) (GLuint buffer); +typedef void (APIENTRYP PFNGLARRAYOBJECTATIPROC) (GLenum array, GLint size, GLenum type, GLsizei stride, GLuint buffer, GLuint offset); +typedef void (APIENTRYP PFNGLGETARRAYOBJECTFVATIPROC) (GLenum array, GLenum pname, GLfloat *params); +typedef void (APIENTRYP PFNGLGETARRAYOBJECTIVATIPROC) (GLenum array, GLenum pname, GLint *params); +typedef void (APIENTRYP PFNGLVARIANTARRAYOBJECTATIPROC) (GLuint id, GLenum type, GLsizei stride, GLuint buffer, GLuint offset); +typedef void (APIENTRYP PFNGLGETVARIANTARRAYOBJECTFVATIPROC) (GLuint id, GLenum pname, GLfloat *params); +typedef void (APIENTRYP PFNGLGETVARIANTARRAYOBJECTIVATIPROC) (GLuint id, GLenum pname, GLint *params); +#ifdef GL_GLEXT_PROTOTYPES +GLAPI GLuint APIENTRY glNewObjectBufferATI (GLsizei size, const void *pointer, GLenum usage); +GLAPI GLboolean APIENTRY glIsObjectBufferATI (GLuint buffer); +GLAPI void APIENTRY glUpdateObjectBufferATI (GLuint buffer, GLuint offset, GLsizei size, const void *pointer, GLenum preserve); +GLAPI void APIENTRY glGetObjectBufferfvATI (GLuint buffer, GLenum pname, GLfloat *params); +GLAPI void APIENTRY glGetObjectBufferivATI (GLuint buffer, GLenum pname, GLint *params); +GLAPI void APIENTRY glFreeObjectBufferATI (GLuint buffer); +GLAPI void APIENTRY glArrayObjectATI (GLenum array, GLint size, GLenum type, GLsizei stride, GLuint buffer, GLuint offset); +GLAPI void APIENTRY glGetArrayObjectfvATI (GLenum array, GLenum pname, GLfloat *params); +GLAPI void APIENTRY glGetArrayObjectivATI (GLenum array, GLenum pname, GLint *params); +GLAPI void APIENTRY glVariantArrayObjectATI (GLuint id, GLenum type, GLsizei stride, GLuint buffer, GLuint offset); +GLAPI void APIENTRY glGetVariantArrayObjectfvATI (GLuint id, GLenum pname, GLfloat *params); +GLAPI void APIENTRY glGetVariantArrayObjectivATI (GLuint id, GLenum pname, GLint *params); +#endif +#endif /* GL_ATI_vertex_array_object */ + +#ifndef GL_ATI_vertex_attrib_array_object +#define GL_ATI_vertex_attrib_array_object 1 +typedef void (APIENTRYP PFNGLVERTEXATTRIBARRAYOBJECTATIPROC) (GLuint index, GLint size, GLenum type, GLboolean normalized, GLsizei stride, GLuint buffer, GLuint offset); +typedef void (APIENTRYP PFNGLGETVERTEXATTRIBARRAYOBJECTFVATIPROC) (GLuint index, GLenum pname, GLfloat *params); +typedef void (APIENTRYP PFNGLGETVERTEXATTRIBARRAYOBJECTIVATIPROC) (GLuint index, GLenum pname, GLint *params); +#ifdef GL_GLEXT_PROTOTYPES +GLAPI void APIENTRY glVertexAttribArrayObjectATI (GLuint index, GLint size, GLenum type, GLboolean normalized, GLsizei stride, GLuint buffer, GLuint offset); +GLAPI void APIENTRY glGetVertexAttribArrayObjectfvATI (GLuint index, GLenum pname, GLfloat *params); +GLAPI void APIENTRY glGetVertexAttribArrayObjectivATI (GLuint index, GLenum pname, GLint *params); +#endif +#endif /* GL_ATI_vertex_attrib_array_object */ + +#ifndef GL_ATI_vertex_streams +#define GL_ATI_vertex_streams 1 +#define GL_MAX_VERTEX_STREAMS_ATI 0x876B +#define GL_VERTEX_STREAM0_ATI 0x876C +#define GL_VERTEX_STREAM1_ATI 0x876D +#define GL_VERTEX_STREAM2_ATI 0x876E +#define GL_VERTEX_STREAM3_ATI 0x876F +#define GL_VERTEX_STREAM4_ATI 0x8770 +#define GL_VERTEX_STREAM5_ATI 0x8771 +#define GL_VERTEX_STREAM6_ATI 0x8772 +#define GL_VERTEX_STREAM7_ATI 0x8773 +#define GL_VERTEX_SOURCE_ATI 0x8774 +typedef void (APIENTRYP PFNGLVERTEXSTREAM1SATIPROC) (GLenum stream, GLshort x); +typedef void (APIENTRYP PFNGLVERTEXSTREAM1SVATIPROC) (GLenum stream, const GLshort *coords); +typedef void (APIENTRYP PFNGLVERTEXSTREAM1IATIPROC) (GLenum stream, GLint x); +typedef void (APIENTRYP PFNGLVERTEXSTREAM1IVATIPROC) (GLenum stream, const GLint *coords); +typedef void (APIENTRYP PFNGLVERTEXSTREAM1FATIPROC) (GLenum stream, GLfloat x); +typedef void (APIENTRYP PFNGLVERTEXSTREAM1FVATIPROC) (GLenum stream, const GLfloat *coords); +typedef void (APIENTRYP PFNGLVERTEXSTREAM1DATIPROC) (GLenum stream, GLdouble x); +typedef void (APIENTRYP PFNGLVERTEXSTREAM1DVATIPROC) (GLenum stream, const GLdouble *coords); +typedef void (APIENTRYP PFNGLVERTEXSTREAM2SATIPROC) (GLenum stream, GLshort x, GLshort y); +typedef void (APIENTRYP PFNGLVERTEXSTREAM2SVATIPROC) (GLenum stream, const GLshort *coords); +typedef void (APIENTRYP PFNGLVERTEXSTREAM2IATIPROC) (GLenum stream, GLint x, GLint y); +typedef void (APIENTRYP PFNGLVERTEXSTREAM2IVATIPROC) (GLenum stream, const GLint *coords); +typedef void (APIENTRYP PFNGLVERTEXSTREAM2FATIPROC) (GLenum stream, GLfloat x, GLfloat y); +typedef void (APIENTRYP PFNGLVERTEXSTREAM2FVATIPROC) (GLenum stream, const GLfloat *coords); +typedef void (APIENTRYP PFNGLVERTEXSTREAM2DATIPROC) (GLenum stream, GLdouble x, GLdouble y); +typedef void (APIENTRYP PFNGLVERTEXSTREAM2DVATIPROC) (GLenum stream, const GLdouble *coords); +typedef void (APIENTRYP PFNGLVERTEXSTREAM3SATIPROC) (GLenum stream, GLshort x, GLshort y, GLshort z); +typedef void (APIENTRYP PFNGLVERTEXSTREAM3SVATIPROC) (GLenum stream, const GLshort *coords); +typedef void (APIENTRYP PFNGLVERTEXSTREAM3IATIPROC) (GLenum stream, GLint x, GLint y, GLint z); +typedef void (APIENTRYP PFNGLVERTEXSTREAM3IVATIPROC) (GLenum stream, const GLint *coords); +typedef void (APIENTRYP PFNGLVERTEXSTREAM3FATIPROC) (GLenum stream, GLfloat x, GLfloat y, GLfloat z); +typedef void (APIENTRYP PFNGLVERTEXSTREAM3FVATIPROC) (GLenum stream, const GLfloat *coords); +typedef void (APIENTRYP PFNGLVERTEXSTREAM3DATIPROC) (GLenum stream, GLdouble x, GLdouble y, GLdouble z); +typedef void (APIENTRYP PFNGLVERTEXSTREAM3DVATIPROC) (GLenum stream, const GLdouble *coords); +typedef void (APIENTRYP PFNGLVERTEXSTREAM4SATIPROC) (GLenum stream, GLshort x, GLshort y, GLshort z, GLshort w); +typedef void (APIENTRYP PFNGLVERTEXSTREAM4SVATIPROC) (GLenum stream, const GLshort *coords); +typedef void (APIENTRYP PFNGLVERTEXSTREAM4IATIPROC) (GLenum stream, GLint x, GLint y, GLint z, GLint w); +typedef void (APIENTRYP PFNGLVERTEXSTREAM4IVATIPROC) (GLenum stream, const GLint *coords); +typedef void (APIENTRYP PFNGLVERTEXSTREAM4FATIPROC) (GLenum stream, GLfloat x, GLfloat y, GLfloat z, GLfloat w); +typedef void (APIENTRYP PFNGLVERTEXSTREAM4FVATIPROC) (GLenum stream, const GLfloat *coords); +typedef void (APIENTRYP PFNGLVERTEXSTREAM4DATIPROC) (GLenum stream, GLdouble x, GLdouble y, GLdouble z, GLdouble w); +typedef void (APIENTRYP PFNGLVERTEXSTREAM4DVATIPROC) (GLenum stream, const GLdouble *coords); +typedef void (APIENTRYP PFNGLNORMALSTREAM3BATIPROC) (GLenum stream, GLbyte nx, GLbyte ny, GLbyte nz); +typedef void (APIENTRYP PFNGLNORMALSTREAM3BVATIPROC) (GLenum stream, const GLbyte *coords); +typedef void (APIENTRYP PFNGLNORMALSTREAM3SATIPROC) (GLenum stream, GLshort nx, GLshort ny, GLshort nz); +typedef void (APIENTRYP PFNGLNORMALSTREAM3SVATIPROC) (GLenum stream, const GLshort *coords); +typedef void (APIENTRYP PFNGLNORMALSTREAM3IATIPROC) (GLenum stream, GLint nx, GLint ny, GLint nz); +typedef void (APIENTRYP PFNGLNORMALSTREAM3IVATIPROC) (GLenum stream, const GLint *coords); +typedef void (APIENTRYP PFNGLNORMALSTREAM3FATIPROC) (GLenum stream, GLfloat nx, GLfloat ny, GLfloat nz); +typedef void (APIENTRYP PFNGLNORMALSTREAM3FVATIPROC) (GLenum stream, const GLfloat *coords); +typedef void (APIENTRYP PFNGLNORMALSTREAM3DATIPROC) (GLenum stream, GLdouble nx, GLdouble ny, GLdouble nz); +typedef void (APIENTRYP PFNGLNORMALSTREAM3DVATIPROC) (GLenum stream, const GLdouble *coords); +typedef void (APIENTRYP PFNGLCLIENTACTIVEVERTEXSTREAMATIPROC) (GLenum stream); +typedef void (APIENTRYP PFNGLVERTEXBLENDENVIATIPROC) (GLenum pname, GLint param); +typedef void (APIENTRYP PFNGLVERTEXBLENDENVFATIPROC) (GLenum pname, GLfloat param); +#ifdef GL_GLEXT_PROTOTYPES +GLAPI void APIENTRY glVertexStream1sATI (GLenum stream, GLshort x); +GLAPI void APIENTRY glVertexStream1svATI (GLenum stream, const GLshort *coords); +GLAPI void APIENTRY glVertexStream1iATI (GLenum stream, GLint x); +GLAPI void APIENTRY glVertexStream1ivATI (GLenum stream, const GLint *coords); +GLAPI void APIENTRY glVertexStream1fATI (GLenum stream, GLfloat x); +GLAPI void APIENTRY glVertexStream1fvATI (GLenum stream, const GLfloat *coords); +GLAPI void APIENTRY glVertexStream1dATI (GLenum stream, GLdouble x); +GLAPI void APIENTRY glVertexStream1dvATI (GLenum stream, const GLdouble *coords); +GLAPI void APIENTRY glVertexStream2sATI (GLenum stream, GLshort x, GLshort y); +GLAPI void APIENTRY glVertexStream2svATI (GLenum stream, const GLshort *coords); +GLAPI void APIENTRY glVertexStream2iATI (GLenum stream, GLint x, GLint y); +GLAPI void APIENTRY glVertexStream2ivATI (GLenum stream, const GLint *coords); +GLAPI void APIENTRY glVertexStream2fATI (GLenum stream, GLfloat x, GLfloat y); +GLAPI void APIENTRY glVertexStream2fvATI (GLenum stream, const GLfloat *coords); +GLAPI void APIENTRY glVertexStream2dATI (GLenum stream, GLdouble x, GLdouble y); +GLAPI void APIENTRY glVertexStream2dvATI (GLenum stream, const GLdouble *coords); +GLAPI void APIENTRY glVertexStream3sATI (GLenum stream, GLshort x, GLshort y, GLshort z); +GLAPI void APIENTRY glVertexStream3svATI (GLenum stream, const GLshort *coords); +GLAPI void APIENTRY glVertexStream3iATI (GLenum stream, GLint x, GLint y, GLint z); +GLAPI void APIENTRY glVertexStream3ivATI (GLenum stream, const GLint *coords); +GLAPI void APIENTRY glVertexStream3fATI (GLenum stream, GLfloat x, GLfloat y, GLfloat z); +GLAPI void APIENTRY glVertexStream3fvATI (GLenum stream, const GLfloat *coords); +GLAPI void APIENTRY glVertexStream3dATI (GLenum stream, GLdouble x, GLdouble y, GLdouble z); +GLAPI void APIENTRY glVertexStream3dvATI (GLenum stream, const GLdouble *coords); +GLAPI void APIENTRY glVertexStream4sATI (GLenum stream, GLshort x, GLshort y, GLshort z, GLshort w); +GLAPI void APIENTRY glVertexStream4svATI (GLenum stream, const GLshort *coords); +GLAPI void APIENTRY glVertexStream4iATI (GLenum stream, GLint x, GLint y, GLint z, GLint w); +GLAPI void APIENTRY glVertexStream4ivATI (GLenum stream, const GLint *coords); +GLAPI void APIENTRY glVertexStream4fATI (GLenum stream, GLfloat x, GLfloat y, GLfloat z, GLfloat w); +GLAPI void APIENTRY glVertexStream4fvATI (GLenum stream, const GLfloat *coords); +GLAPI void APIENTRY glVertexStream4dATI (GLenum stream, GLdouble x, GLdouble y, GLdouble z, GLdouble w); +GLAPI void APIENTRY glVertexStream4dvATI (GLenum stream, const GLdouble *coords); +GLAPI void APIENTRY glNormalStream3bATI (GLenum stream, GLbyte nx, GLbyte ny, GLbyte nz); +GLAPI void APIENTRY glNormalStream3bvATI (GLenum stream, const GLbyte *coords); +GLAPI void APIENTRY glNormalStream3sATI (GLenum stream, GLshort nx, GLshort ny, GLshort nz); +GLAPI void APIENTRY glNormalStream3svATI (GLenum stream, const GLshort *coords); +GLAPI void APIENTRY glNormalStream3iATI (GLenum stream, GLint nx, GLint ny, GLint nz); +GLAPI void APIENTRY glNormalStream3ivATI (GLenum stream, const GLint *coords); +GLAPI void APIENTRY glNormalStream3fATI (GLenum stream, GLfloat nx, GLfloat ny, GLfloat nz); +GLAPI void APIENTRY glNormalStream3fvATI (GLenum stream, const GLfloat *coords); +GLAPI void APIENTRY glNormalStream3dATI (GLenum stream, GLdouble nx, GLdouble ny, GLdouble nz); +GLAPI void APIENTRY glNormalStream3dvATI (GLenum stream, const GLdouble *coords); +GLAPI void APIENTRY glClientActiveVertexStreamATI (GLenum stream); +GLAPI void APIENTRY glVertexBlendEnviATI (GLenum pname, GLint param); +GLAPI void APIENTRY glVertexBlendEnvfATI (GLenum pname, GLfloat param); +#endif +#endif /* GL_ATI_vertex_streams */ + +#ifndef GL_EXT_422_pixels +#define GL_EXT_422_pixels 1 +#define GL_422_EXT 0x80CC +#define GL_422_REV_EXT 0x80CD +#define GL_422_AVERAGE_EXT 0x80CE +#define GL_422_REV_AVERAGE_EXT 0x80CF +#endif /* GL_EXT_422_pixels */ + +#ifndef GL_EXT_abgr +#define GL_EXT_abgr 1 +#define GL_ABGR_EXT 0x8000 +#endif /* GL_EXT_abgr */ + +#ifndef GL_EXT_bgra +#define GL_EXT_bgra 1 +#define GL_BGR_EXT 0x80E0 +#define GL_BGRA_EXT 0x80E1 +#endif /* GL_EXT_bgra */ + +#ifndef GL_EXT_bindable_uniform +#define GL_EXT_bindable_uniform 1 +#define GL_MAX_VERTEX_BINDABLE_UNIFORMS_EXT 0x8DE2 +#define GL_MAX_FRAGMENT_BINDABLE_UNIFORMS_EXT 0x8DE3 +#define GL_MAX_GEOMETRY_BINDABLE_UNIFORMS_EXT 0x8DE4 +#define GL_MAX_BINDABLE_UNIFORM_SIZE_EXT 0x8DED +#define GL_UNIFORM_BUFFER_EXT 0x8DEE +#define GL_UNIFORM_BUFFER_BINDING_EXT 0x8DEF +typedef void (APIENTRYP PFNGLUNIFORMBUFFEREXTPROC) (GLuint program, GLint location, GLuint buffer); +typedef GLint (APIENTRYP PFNGLGETUNIFORMBUFFERSIZEEXTPROC) (GLuint program, GLint location); +typedef GLintptr (APIENTRYP PFNGLGETUNIFORMOFFSETEXTPROC) (GLuint program, GLint location); +#ifdef GL_GLEXT_PROTOTYPES +GLAPI void APIENTRY glUniformBufferEXT (GLuint program, GLint location, GLuint buffer); +GLAPI GLint APIENTRY glGetUniformBufferSizeEXT (GLuint program, GLint location); +GLAPI GLintptr APIENTRY glGetUniformOffsetEXT (GLuint program, GLint location); +#endif +#endif /* GL_EXT_bindable_uniform */ + +#ifndef GL_EXT_blend_color +#define GL_EXT_blend_color 1 +#define GL_CONSTANT_COLOR_EXT 0x8001 +#define GL_ONE_MINUS_CONSTANT_COLOR_EXT 0x8002 +#define GL_CONSTANT_ALPHA_EXT 0x8003 +#define GL_ONE_MINUS_CONSTANT_ALPHA_EXT 0x8004 +#define GL_BLEND_COLOR_EXT 0x8005 +typedef void (APIENTRYP PFNGLBLENDCOLOREXTPROC) (GLfloat red, GLfloat green, GLfloat blue, GLfloat alpha); +#ifdef GL_GLEXT_PROTOTYPES +GLAPI void APIENTRY glBlendColorEXT (GLfloat red, GLfloat green, GLfloat blue, GLfloat alpha); +#endif +#endif /* GL_EXT_blend_color */ + +#ifndef GL_EXT_blend_equation_separate +#define GL_EXT_blend_equation_separate 1 +#define GL_BLEND_EQUATION_RGB_EXT 0x8009 +#define GL_BLEND_EQUATION_ALPHA_EXT 0x883D +typedef void (APIENTRYP PFNGLBLENDEQUATIONSEPARATEEXTPROC) (GLenum modeRGB, GLenum modeAlpha); +#ifdef GL_GLEXT_PROTOTYPES +GLAPI void APIENTRY glBlendEquationSeparateEXT (GLenum modeRGB, GLenum modeAlpha); +#endif +#endif /* GL_EXT_blend_equation_separate */ + +#ifndef GL_EXT_blend_func_separate +#define GL_EXT_blend_func_separate 1 +#define GL_BLEND_DST_RGB_EXT 0x80C8 +#define GL_BLEND_SRC_RGB_EXT 0x80C9 +#define GL_BLEND_DST_ALPHA_EXT 0x80CA +#define GL_BLEND_SRC_ALPHA_EXT 0x80CB +typedef void (APIENTRYP PFNGLBLENDFUNCSEPARATEEXTPROC) (GLenum sfactorRGB, GLenum dfactorRGB, GLenum sfactorAlpha, GLenum dfactorAlpha); +#ifdef GL_GLEXT_PROTOTYPES +GLAPI void APIENTRY glBlendFuncSeparateEXT (GLenum sfactorRGB, GLenum dfactorRGB, GLenum sfactorAlpha, GLenum dfactorAlpha); +#endif +#endif /* GL_EXT_blend_func_separate */ + +#ifndef GL_EXT_blend_logic_op +#define GL_EXT_blend_logic_op 1 +#endif /* GL_EXT_blend_logic_op */ + +#ifndef GL_EXT_blend_minmax +#define GL_EXT_blend_minmax 1 +#define GL_MIN_EXT 0x8007 +#define GL_MAX_EXT 0x8008 +#define GL_FUNC_ADD_EXT 0x8006 +#define GL_BLEND_EQUATION_EXT 0x8009 +typedef void (APIENTRYP PFNGLBLENDEQUATIONEXTPROC) (GLenum mode); +#ifdef GL_GLEXT_PROTOTYPES +GLAPI void APIENTRY glBlendEquationEXT (GLenum mode); +#endif +#endif /* GL_EXT_blend_minmax */ + +#ifndef GL_EXT_blend_subtract +#define GL_EXT_blend_subtract 1 +#define GL_FUNC_SUBTRACT_EXT 0x800A +#define GL_FUNC_REVERSE_SUBTRACT_EXT 0x800B +#endif /* GL_EXT_blend_subtract */ + +#ifndef GL_EXT_clip_volume_hint +#define GL_EXT_clip_volume_hint 1 +#define GL_CLIP_VOLUME_CLIPPING_HINT_EXT 0x80F0 +#endif /* GL_EXT_clip_volume_hint */ + +#ifndef GL_EXT_cmyka +#define GL_EXT_cmyka 1 +#define GL_CMYK_EXT 0x800C +#define GL_CMYKA_EXT 0x800D +#define GL_PACK_CMYK_HINT_EXT 0x800E +#define GL_UNPACK_CMYK_HINT_EXT 0x800F +#endif /* GL_EXT_cmyka */ + +#ifndef GL_EXT_color_subtable +#define GL_EXT_color_subtable 1 +typedef void (APIENTRYP PFNGLCOLORSUBTABLEEXTPROC) (GLenum target, GLsizei start, GLsizei count, GLenum format, GLenum type, const void *data); +typedef void (APIENTRYP PFNGLCOPYCOLORSUBTABLEEXTPROC) (GLenum target, GLsizei start, GLint x, GLint y, GLsizei width); +#ifdef GL_GLEXT_PROTOTYPES +GLAPI void APIENTRY glColorSubTableEXT (GLenum target, GLsizei start, GLsizei count, GLenum format, GLenum type, const void *data); +GLAPI void APIENTRY glCopyColorSubTableEXT (GLenum target, GLsizei start, GLint x, GLint y, GLsizei width); +#endif +#endif /* GL_EXT_color_subtable */ + +#ifndef GL_EXT_compiled_vertex_array +#define GL_EXT_compiled_vertex_array 1 +#define GL_ARRAY_ELEMENT_LOCK_FIRST_EXT 0x81A8 +#define GL_ARRAY_ELEMENT_LOCK_COUNT_EXT 0x81A9 +typedef void (APIENTRYP PFNGLLOCKARRAYSEXTPROC) (GLint first, GLsizei count); +typedef void (APIENTRYP PFNGLUNLOCKARRAYSEXTPROC) (void); +#ifdef GL_GLEXT_PROTOTYPES +GLAPI void APIENTRY glLockArraysEXT (GLint first, GLsizei count); +GLAPI void APIENTRY glUnlockArraysEXT (void); +#endif +#endif /* GL_EXT_compiled_vertex_array */ + +#ifndef GL_EXT_convolution +#define GL_EXT_convolution 1 +#define GL_CONVOLUTION_1D_EXT 0x8010 +#define GL_CONVOLUTION_2D_EXT 0x8011 +#define GL_SEPARABLE_2D_EXT 0x8012 +#define GL_CONVOLUTION_BORDER_MODE_EXT 0x8013 +#define GL_CONVOLUTION_FILTER_SCALE_EXT 0x8014 +#define GL_CONVOLUTION_FILTER_BIAS_EXT 0x8015 +#define GL_REDUCE_EXT 0x8016 +#define GL_CONVOLUTION_FORMAT_EXT 0x8017 +#define GL_CONVOLUTION_WIDTH_EXT 0x8018 +#define GL_CONVOLUTION_HEIGHT_EXT 0x8019 +#define GL_MAX_CONVOLUTION_WIDTH_EXT 0x801A +#define GL_MAX_CONVOLUTION_HEIGHT_EXT 0x801B +#define GL_POST_CONVOLUTION_RED_SCALE_EXT 0x801C +#define GL_POST_CONVOLUTION_GREEN_SCALE_EXT 0x801D +#define GL_POST_CONVOLUTION_BLUE_SCALE_EXT 0x801E +#define GL_POST_CONVOLUTION_ALPHA_SCALE_EXT 0x801F +#define GL_POST_CONVOLUTION_RED_BIAS_EXT 0x8020 +#define GL_POST_CONVOLUTION_GREEN_BIAS_EXT 0x8021 +#define GL_POST_CONVOLUTION_BLUE_BIAS_EXT 0x8022 +#define GL_POST_CONVOLUTION_ALPHA_BIAS_EXT 0x8023 +typedef void (APIENTRYP PFNGLCONVOLUTIONFILTER1DEXTPROC) (GLenum target, GLenum internalformat, GLsizei width, GLenum format, GLenum type, const void *image); +typedef void (APIENTRYP PFNGLCONVOLUTIONFILTER2DEXTPROC) (GLenum target, GLenum internalformat, GLsizei width, GLsizei height, GLenum format, GLenum type, const void *image); +typedef void (APIENTRYP PFNGLCONVOLUTIONPARAMETERFEXTPROC) (GLenum target, GLenum pname, GLfloat params); +typedef void (APIENTRYP PFNGLCONVOLUTIONPARAMETERFVEXTPROC) (GLenum target, GLenum pname, const GLfloat *params); +typedef void (APIENTRYP PFNGLCONVOLUTIONPARAMETERIEXTPROC) (GLenum target, GLenum pname, GLint params); +typedef void (APIENTRYP PFNGLCONVOLUTIONPARAMETERIVEXTPROC) (GLenum target, GLenum pname, const GLint *params); +typedef void (APIENTRYP PFNGLCOPYCONVOLUTIONFILTER1DEXTPROC) (GLenum target, GLenum internalformat, GLint x, GLint y, GLsizei width); +typedef void (APIENTRYP PFNGLCOPYCONVOLUTIONFILTER2DEXTPROC) (GLenum target, GLenum internalformat, GLint x, GLint y, GLsizei width, GLsizei height); +typedef void (APIENTRYP PFNGLGETCONVOLUTIONFILTEREXTPROC) (GLenum target, GLenum format, GLenum type, void *image); +typedef void (APIENTRYP PFNGLGETCONVOLUTIONPARAMETERFVEXTPROC) (GLenum target, GLenum pname, GLfloat *params); +typedef void (APIENTRYP PFNGLGETCONVOLUTIONPARAMETERIVEXTPROC) (GLenum target, GLenum pname, GLint *params); +typedef void (APIENTRYP PFNGLGETSEPARABLEFILTEREXTPROC) (GLenum target, GLenum format, GLenum type, void *row, void *column, void *span); +typedef void (APIENTRYP PFNGLSEPARABLEFILTER2DEXTPROC) (GLenum target, GLenum internalformat, GLsizei width, GLsizei height, GLenum format, GLenum type, const void *row, const void *column); +#ifdef GL_GLEXT_PROTOTYPES +GLAPI void APIENTRY glConvolutionFilter1DEXT (GLenum target, GLenum internalformat, GLsizei width, GLenum format, GLenum type, const void *image); +GLAPI void APIENTRY glConvolutionFilter2DEXT (GLenum target, GLenum internalformat, GLsizei width, GLsizei height, GLenum format, GLenum type, const void *image); +GLAPI void APIENTRY glConvolutionParameterfEXT (GLenum target, GLenum pname, GLfloat params); +GLAPI void APIENTRY glConvolutionParameterfvEXT (GLenum target, GLenum pname, const GLfloat *params); +GLAPI void APIENTRY glConvolutionParameteriEXT (GLenum target, GLenum pname, GLint params); +GLAPI void APIENTRY glConvolutionParameterivEXT (GLenum target, GLenum pname, const GLint *params); +GLAPI void APIENTRY glCopyConvolutionFilter1DEXT (GLenum target, GLenum internalformat, GLint x, GLint y, GLsizei width); +GLAPI void APIENTRY glCopyConvolutionFilter2DEXT (GLenum target, GLenum internalformat, GLint x, GLint y, GLsizei width, GLsizei height); +GLAPI void APIENTRY glGetConvolutionFilterEXT (GLenum target, GLenum format, GLenum type, void *image); +GLAPI void APIENTRY glGetConvolutionParameterfvEXT (GLenum target, GLenum pname, GLfloat *params); +GLAPI void APIENTRY glGetConvolutionParameterivEXT (GLenum target, GLenum pname, GLint *params); +GLAPI void APIENTRY glGetSeparableFilterEXT (GLenum target, GLenum format, GLenum type, void *row, void *column, void *span); +GLAPI void APIENTRY glSeparableFilter2DEXT (GLenum target, GLenum internalformat, GLsizei width, GLsizei height, GLenum format, GLenum type, const void *row, const void *column); +#endif +#endif /* GL_EXT_convolution */ + +#ifndef GL_EXT_coordinate_frame +#define GL_EXT_coordinate_frame 1 +#define GL_TANGENT_ARRAY_EXT 0x8439 +#define GL_BINORMAL_ARRAY_EXT 0x843A +#define GL_CURRENT_TANGENT_EXT 0x843B +#define GL_CURRENT_BINORMAL_EXT 0x843C +#define GL_TANGENT_ARRAY_TYPE_EXT 0x843E +#define GL_TANGENT_ARRAY_STRIDE_EXT 0x843F +#define GL_BINORMAL_ARRAY_TYPE_EXT 0x8440 +#define GL_BINORMAL_ARRAY_STRIDE_EXT 0x8441 +#define GL_TANGENT_ARRAY_POINTER_EXT 0x8442 +#define GL_BINORMAL_ARRAY_POINTER_EXT 0x8443 +#define GL_MAP1_TANGENT_EXT 0x8444 +#define GL_MAP2_TANGENT_EXT 0x8445 +#define GL_MAP1_BINORMAL_EXT 0x8446 +#define GL_MAP2_BINORMAL_EXT 0x8447 +typedef void (APIENTRYP PFNGLTANGENT3BEXTPROC) (GLbyte tx, GLbyte ty, GLbyte tz); +typedef void (APIENTRYP PFNGLTANGENT3BVEXTPROC) (const GLbyte *v); +typedef void (APIENTRYP PFNGLTANGENT3DEXTPROC) (GLdouble tx, GLdouble ty, GLdouble tz); +typedef void (APIENTRYP PFNGLTANGENT3DVEXTPROC) (const GLdouble *v); +typedef void (APIENTRYP PFNGLTANGENT3FEXTPROC) (GLfloat tx, GLfloat ty, GLfloat tz); +typedef void (APIENTRYP PFNGLTANGENT3FVEXTPROC) (const GLfloat *v); +typedef void (APIENTRYP PFNGLTANGENT3IEXTPROC) (GLint tx, GLint ty, GLint tz); +typedef void (APIENTRYP PFNGLTANGENT3IVEXTPROC) (const GLint *v); +typedef void (APIENTRYP PFNGLTANGENT3SEXTPROC) (GLshort tx, GLshort ty, GLshort tz); +typedef void (APIENTRYP PFNGLTANGENT3SVEXTPROC) (const GLshort *v); +typedef void (APIENTRYP PFNGLBINORMAL3BEXTPROC) (GLbyte bx, GLbyte by, GLbyte bz); +typedef void (APIENTRYP PFNGLBINORMAL3BVEXTPROC) (const GLbyte *v); +typedef void (APIENTRYP PFNGLBINORMAL3DEXTPROC) (GLdouble bx, GLdouble by, GLdouble bz); +typedef void (APIENTRYP PFNGLBINORMAL3DVEXTPROC) (const GLdouble *v); +typedef void (APIENTRYP PFNGLBINORMAL3FEXTPROC) (GLfloat bx, GLfloat by, GLfloat bz); +typedef void (APIENTRYP PFNGLBINORMAL3FVEXTPROC) (const GLfloat *v); +typedef void (APIENTRYP PFNGLBINORMAL3IEXTPROC) (GLint bx, GLint by, GLint bz); +typedef void (APIENTRYP PFNGLBINORMAL3IVEXTPROC) (const GLint *v); +typedef void (APIENTRYP PFNGLBINORMAL3SEXTPROC) (GLshort bx, GLshort by, GLshort bz); +typedef void (APIENTRYP PFNGLBINORMAL3SVEXTPROC) (const GLshort *v); +typedef void (APIENTRYP PFNGLTANGENTPOINTEREXTPROC) (GLenum type, GLsizei stride, const void *pointer); +typedef void (APIENTRYP PFNGLBINORMALPOINTEREXTPROC) (GLenum type, GLsizei stride, const void *pointer); +#ifdef GL_GLEXT_PROTOTYPES +GLAPI void APIENTRY glTangent3bEXT (GLbyte tx, GLbyte ty, GLbyte tz); +GLAPI void APIENTRY glTangent3bvEXT (const GLbyte *v); +GLAPI void APIENTRY glTangent3dEXT (GLdouble tx, GLdouble ty, GLdouble tz); +GLAPI void APIENTRY glTangent3dvEXT (const GLdouble *v); +GLAPI void APIENTRY glTangent3fEXT (GLfloat tx, GLfloat ty, GLfloat tz); +GLAPI void APIENTRY glTangent3fvEXT (const GLfloat *v); +GLAPI void APIENTRY glTangent3iEXT (GLint tx, GLint ty, GLint tz); +GLAPI void APIENTRY glTangent3ivEXT (const GLint *v); +GLAPI void APIENTRY glTangent3sEXT (GLshort tx, GLshort ty, GLshort tz); +GLAPI void APIENTRY glTangent3svEXT (const GLshort *v); +GLAPI void APIENTRY glBinormal3bEXT (GLbyte bx, GLbyte by, GLbyte bz); +GLAPI void APIENTRY glBinormal3bvEXT (const GLbyte *v); +GLAPI void APIENTRY glBinormal3dEXT (GLdouble bx, GLdouble by, GLdouble bz); +GLAPI void APIENTRY glBinormal3dvEXT (const GLdouble *v); +GLAPI void APIENTRY glBinormal3fEXT (GLfloat bx, GLfloat by, GLfloat bz); +GLAPI void APIENTRY glBinormal3fvEXT (const GLfloat *v); +GLAPI void APIENTRY glBinormal3iEXT (GLint bx, GLint by, GLint bz); +GLAPI void APIENTRY glBinormal3ivEXT (const GLint *v); +GLAPI void APIENTRY glBinormal3sEXT (GLshort bx, GLshort by, GLshort bz); +GLAPI void APIENTRY glBinormal3svEXT (const GLshort *v); +GLAPI void APIENTRY glTangentPointerEXT (GLenum type, GLsizei stride, const void *pointer); +GLAPI void APIENTRY glBinormalPointerEXT (GLenum type, GLsizei stride, const void *pointer); +#endif +#endif /* GL_EXT_coordinate_frame */ + +#ifndef GL_EXT_copy_texture +#define GL_EXT_copy_texture 1 +typedef void (APIENTRYP PFNGLCOPYTEXIMAGE1DEXTPROC) (GLenum target, GLint level, GLenum internalformat, GLint x, GLint y, GLsizei width, GLint border); +typedef void (APIENTRYP PFNGLCOPYTEXIMAGE2DEXTPROC) (GLenum target, GLint level, GLenum internalformat, GLint x, GLint y, GLsizei width, GLsizei height, GLint border); +typedef void (APIENTRYP PFNGLCOPYTEXSUBIMAGE1DEXTPROC) (GLenum target, GLint level, GLint xoffset, GLint x, GLint y, GLsizei width); +typedef void (APIENTRYP PFNGLCOPYTEXSUBIMAGE2DEXTPROC) (GLenum target, GLint level, GLint xoffset, GLint yoffset, GLint x, GLint y, GLsizei width, GLsizei height); +typedef void (APIENTRYP PFNGLCOPYTEXSUBIMAGE3DEXTPROC) (GLenum target, GLint level, GLint xoffset, GLint yoffset, GLint zoffset, GLint x, GLint y, GLsizei width, GLsizei height); +#ifdef GL_GLEXT_PROTOTYPES +GLAPI void APIENTRY glCopyTexImage1DEXT (GLenum target, GLint level, GLenum internalformat, GLint x, GLint y, GLsizei width, GLint border); +GLAPI void APIENTRY glCopyTexImage2DEXT (GLenum target, GLint level, GLenum internalformat, GLint x, GLint y, GLsizei width, GLsizei height, GLint border); +GLAPI void APIENTRY glCopyTexSubImage1DEXT (GLenum target, GLint level, GLint xoffset, GLint x, GLint y, GLsizei width); +GLAPI void APIENTRY glCopyTexSubImage2DEXT (GLenum target, GLint level, GLint xoffset, GLint yoffset, GLint x, GLint y, GLsizei width, GLsizei height); +GLAPI void APIENTRY glCopyTexSubImage3DEXT (GLenum target, GLint level, GLint xoffset, GLint yoffset, GLint zoffset, GLint x, GLint y, GLsizei width, GLsizei height); +#endif +#endif /* GL_EXT_copy_texture */ + +#ifndef GL_EXT_cull_vertex +#define GL_EXT_cull_vertex 1 +#define GL_CULL_VERTEX_EXT 0x81AA +#define GL_CULL_VERTEX_EYE_POSITION_EXT 0x81AB +#define GL_CULL_VERTEX_OBJECT_POSITION_EXT 0x81AC +typedef void (APIENTRYP PFNGLCULLPARAMETERDVEXTPROC) (GLenum pname, GLdouble *params); +typedef void (APIENTRYP PFNGLCULLPARAMETERFVEXTPROC) (GLenum pname, GLfloat *params); +#ifdef GL_GLEXT_PROTOTYPES +GLAPI void APIENTRY glCullParameterdvEXT (GLenum pname, GLdouble *params); +GLAPI void APIENTRY glCullParameterfvEXT (GLenum pname, GLfloat *params); +#endif +#endif /* GL_EXT_cull_vertex */ + +#ifndef GL_EXT_debug_label +#define GL_EXT_debug_label 1 +#define GL_PROGRAM_PIPELINE_OBJECT_EXT 0x8A4F +#define GL_PROGRAM_OBJECT_EXT 0x8B40 +#define GL_SHADER_OBJECT_EXT 0x8B48 +#define GL_BUFFER_OBJECT_EXT 0x9151 +#define GL_QUERY_OBJECT_EXT 0x9153 +#define GL_VERTEX_ARRAY_OBJECT_EXT 0x9154 +typedef void (APIENTRYP PFNGLLABELOBJECTEXTPROC) (GLenum type, GLuint object, GLsizei length, const GLchar *label); +typedef void (APIENTRYP PFNGLGETOBJECTLABELEXTPROC) (GLenum type, GLuint object, GLsizei bufSize, GLsizei *length, GLchar *label); +#ifdef GL_GLEXT_PROTOTYPES +GLAPI void APIENTRY glLabelObjectEXT (GLenum type, GLuint object, GLsizei length, const GLchar *label); +GLAPI void APIENTRY glGetObjectLabelEXT (GLenum type, GLuint object, GLsizei bufSize, GLsizei *length, GLchar *label); +#endif +#endif /* GL_EXT_debug_label */ + +#ifndef GL_EXT_debug_marker +#define GL_EXT_debug_marker 1 +typedef void (APIENTRYP PFNGLINSERTEVENTMARKEREXTPROC) (GLsizei length, const GLchar *marker); +typedef void (APIENTRYP PFNGLPUSHGROUPMARKEREXTPROC) (GLsizei length, const GLchar *marker); +typedef void (APIENTRYP PFNGLPOPGROUPMARKEREXTPROC) (void); +#ifdef GL_GLEXT_PROTOTYPES +GLAPI void APIENTRY glInsertEventMarkerEXT (GLsizei length, const GLchar *marker); +GLAPI void APIENTRY glPushGroupMarkerEXT (GLsizei length, const GLchar *marker); +GLAPI void APIENTRY glPopGroupMarkerEXT (void); +#endif +#endif /* GL_EXT_debug_marker */ + +#ifndef GL_EXT_depth_bounds_test +#define GL_EXT_depth_bounds_test 1 +#define GL_DEPTH_BOUNDS_TEST_EXT 0x8890 +#define GL_DEPTH_BOUNDS_EXT 0x8891 +typedef void (APIENTRYP PFNGLDEPTHBOUNDSEXTPROC) (GLclampd zmin, GLclampd zmax); +#ifdef GL_GLEXT_PROTOTYPES +GLAPI void APIENTRY glDepthBoundsEXT (GLclampd zmin, GLclampd zmax); +#endif +#endif /* GL_EXT_depth_bounds_test */ + +#ifndef GL_EXT_direct_state_access +#define GL_EXT_direct_state_access 1 +#define GL_PROGRAM_MATRIX_EXT 0x8E2D +#define GL_TRANSPOSE_PROGRAM_MATRIX_EXT 0x8E2E +#define GL_PROGRAM_MATRIX_STACK_DEPTH_EXT 0x8E2F +typedef void (APIENTRYP PFNGLMATRIXLOADFEXTPROC) (GLenum mode, const GLfloat *m); +typedef void (APIENTRYP PFNGLMATRIXLOADDEXTPROC) (GLenum mode, const GLdouble *m); +typedef void (APIENTRYP PFNGLMATRIXMULTFEXTPROC) (GLenum mode, const GLfloat *m); +typedef void (APIENTRYP PFNGLMATRIXMULTDEXTPROC) (GLenum mode, const GLdouble *m); +typedef void (APIENTRYP PFNGLMATRIXLOADIDENTITYEXTPROC) (GLenum mode); +typedef void (APIENTRYP PFNGLMATRIXROTATEFEXTPROC) (GLenum mode, GLfloat angle, GLfloat x, GLfloat y, GLfloat z); +typedef void (APIENTRYP PFNGLMATRIXROTATEDEXTPROC) (GLenum mode, GLdouble angle, GLdouble x, GLdouble y, GLdouble z); +typedef void (APIENTRYP PFNGLMATRIXSCALEFEXTPROC) (GLenum mode, GLfloat x, GLfloat y, GLfloat z); +typedef void (APIENTRYP PFNGLMATRIXSCALEDEXTPROC) (GLenum mode, GLdouble x, GLdouble y, GLdouble z); +typedef void (APIENTRYP PFNGLMATRIXTRANSLATEFEXTPROC) (GLenum mode, GLfloat x, GLfloat y, GLfloat z); +typedef void (APIENTRYP PFNGLMATRIXTRANSLATEDEXTPROC) (GLenum mode, GLdouble x, GLdouble y, GLdouble z); +typedef void (APIENTRYP PFNGLMATRIXFRUSTUMEXTPROC) (GLenum mode, GLdouble left, GLdouble right, GLdouble bottom, GLdouble top, GLdouble zNear, GLdouble zFar); +typedef void (APIENTRYP PFNGLMATRIXORTHOEXTPROC) (GLenum mode, GLdouble left, GLdouble right, GLdouble bottom, GLdouble top, GLdouble zNear, GLdouble zFar); +typedef void (APIENTRYP PFNGLMATRIXPOPEXTPROC) (GLenum mode); +typedef void (APIENTRYP PFNGLMATRIXPUSHEXTPROC) (GLenum mode); +typedef void (APIENTRYP PFNGLCLIENTATTRIBDEFAULTEXTPROC) (GLbitfield mask); +typedef void (APIENTRYP PFNGLPUSHCLIENTATTRIBDEFAULTEXTPROC) (GLbitfield mask); +typedef void (APIENTRYP PFNGLTEXTUREPARAMETERFEXTPROC) (GLuint texture, GLenum target, GLenum pname, GLfloat param); +typedef void (APIENTRYP PFNGLTEXTUREPARAMETERFVEXTPROC) (GLuint texture, GLenum target, GLenum pname, const GLfloat *params); +typedef void (APIENTRYP PFNGLTEXTUREPARAMETERIEXTPROC) (GLuint texture, GLenum target, GLenum pname, GLint param); +typedef void (APIENTRYP PFNGLTEXTUREPARAMETERIVEXTPROC) (GLuint texture, GLenum target, GLenum pname, const GLint *params); +typedef void (APIENTRYP PFNGLTEXTUREIMAGE1DEXTPROC) (GLuint texture, GLenum target, GLint level, GLint internalformat, GLsizei width, GLint border, GLenum format, GLenum type, const void *pixels); +typedef void (APIENTRYP PFNGLTEXTUREIMAGE2DEXTPROC) (GLuint texture, GLenum target, GLint level, GLint internalformat, GLsizei width, GLsizei height, GLint border, GLenum format, GLenum type, const void *pixels); +typedef void (APIENTRYP PFNGLTEXTURESUBIMAGE1DEXTPROC) (GLuint texture, GLenum target, GLint level, GLint xoffset, GLsizei width, GLenum format, GLenum type, const void *pixels); +typedef void (APIENTRYP PFNGLTEXTURESUBIMAGE2DEXTPROC) (GLuint texture, GLenum target, GLint level, GLint xoffset, GLint yoffset, GLsizei width, GLsizei height, GLenum format, GLenum type, const void *pixels); +typedef void (APIENTRYP PFNGLCOPYTEXTUREIMAGE1DEXTPROC) (GLuint texture, GLenum target, GLint level, GLenum internalformat, GLint x, GLint y, GLsizei width, GLint border); +typedef void (APIENTRYP PFNGLCOPYTEXTUREIMAGE2DEXTPROC) (GLuint texture, GLenum target, GLint level, GLenum internalformat, GLint x, GLint y, GLsizei width, GLsizei height, GLint border); +typedef void (APIENTRYP PFNGLCOPYTEXTURESUBIMAGE1DEXTPROC) (GLuint texture, GLenum target, GLint level, GLint xoffset, GLint x, GLint y, GLsizei width); +typedef void (APIENTRYP PFNGLCOPYTEXTURESUBIMAGE2DEXTPROC) (GLuint texture, GLenum target, GLint level, GLint xoffset, GLint yoffset, GLint x, GLint y, GLsizei width, GLsizei height); +typedef void (APIENTRYP PFNGLGETTEXTUREIMAGEEXTPROC) (GLuint texture, GLenum target, GLint level, GLenum format, GLenum type, void *pixels); +typedef void (APIENTRYP PFNGLGETTEXTUREPARAMETERFVEXTPROC) (GLuint texture, GLenum target, GLenum pname, GLfloat *params); +typedef void (APIENTRYP PFNGLGETTEXTUREPARAMETERIVEXTPROC) (GLuint texture, GLenum target, GLenum pname, GLint *params); +typedef void (APIENTRYP PFNGLGETTEXTURELEVELPARAMETERFVEXTPROC) (GLuint texture, GLenum target, GLint level, GLenum pname, GLfloat *params); +typedef void (APIENTRYP PFNGLGETTEXTURELEVELPARAMETERIVEXTPROC) (GLuint texture, GLenum target, GLint level, GLenum pname, GLint *params); +typedef void (APIENTRYP PFNGLTEXTUREIMAGE3DEXTPROC) (GLuint texture, GLenum target, GLint level, GLint internalformat, GLsizei width, GLsizei height, GLsizei depth, GLint border, GLenum format, GLenum type, const void *pixels); +typedef void (APIENTRYP PFNGLTEXTURESUBIMAGE3DEXTPROC) (GLuint texture, GLenum target, GLint level, GLint xoffset, GLint yoffset, GLint zoffset, GLsizei width, GLsizei height, GLsizei depth, GLenum format, GLenum type, const void *pixels); +typedef void (APIENTRYP PFNGLCOPYTEXTURESUBIMAGE3DEXTPROC) (GLuint texture, GLenum target, GLint level, GLint xoffset, GLint yoffset, GLint zoffset, GLint x, GLint y, GLsizei width, GLsizei height); +typedef void (APIENTRYP PFNGLBINDMULTITEXTUREEXTPROC) (GLenum texunit, GLenum target, GLuint texture); +typedef void (APIENTRYP PFNGLMULTITEXCOORDPOINTEREXTPROC) (GLenum texunit, GLint size, GLenum type, GLsizei stride, const void *pointer); +typedef void (APIENTRYP PFNGLMULTITEXENVFEXTPROC) (GLenum texunit, GLenum target, GLenum pname, GLfloat param); +typedef void (APIENTRYP PFNGLMULTITEXENVFVEXTPROC) (GLenum texunit, GLenum target, GLenum pname, const GLfloat *params); +typedef void (APIENTRYP PFNGLMULTITEXENVIEXTPROC) (GLenum texunit, GLenum target, GLenum pname, GLint param); +typedef void (APIENTRYP PFNGLMULTITEXENVIVEXTPROC) (GLenum texunit, GLenum target, GLenum pname, const GLint *params); +typedef void (APIENTRYP PFNGLMULTITEXGENDEXTPROC) (GLenum texunit, GLenum coord, GLenum pname, GLdouble param); +typedef void (APIENTRYP PFNGLMULTITEXGENDVEXTPROC) (GLenum texunit, GLenum coord, GLenum pname, const GLdouble *params); +typedef void (APIENTRYP PFNGLMULTITEXGENFEXTPROC) (GLenum texunit, GLenum coord, GLenum pname, GLfloat param); +typedef void (APIENTRYP PFNGLMULTITEXGENFVEXTPROC) (GLenum texunit, GLenum coord, GLenum pname, const GLfloat *params); +typedef void (APIENTRYP PFNGLMULTITEXGENIEXTPROC) (GLenum texunit, GLenum coord, GLenum pname, GLint param); +typedef void (APIENTRYP PFNGLMULTITEXGENIVEXTPROC) (GLenum texunit, GLenum coord, GLenum pname, const GLint *params); +typedef void (APIENTRYP PFNGLGETMULTITEXENVFVEXTPROC) (GLenum texunit, GLenum target, GLenum pname, GLfloat *params); +typedef void (APIENTRYP PFNGLGETMULTITEXENVIVEXTPROC) (GLenum texunit, GLenum target, GLenum pname, GLint *params); +typedef void (APIENTRYP PFNGLGETMULTITEXGENDVEXTPROC) (GLenum texunit, GLenum coord, GLenum pname, GLdouble *params); +typedef void (APIENTRYP PFNGLGETMULTITEXGENFVEXTPROC) (GLenum texunit, GLenum coord, GLenum pname, GLfloat *params); +typedef void (APIENTRYP PFNGLGETMULTITEXGENIVEXTPROC) (GLenum texunit, GLenum coord, GLenum pname, GLint *params); +typedef void (APIENTRYP PFNGLMULTITEXPARAMETERIEXTPROC) (GLenum texunit, GLenum target, GLenum pname, GLint param); +typedef void (APIENTRYP PFNGLMULTITEXPARAMETERIVEXTPROC) (GLenum texunit, GLenum target, GLenum pname, const GLint *params); +typedef void (APIENTRYP PFNGLMULTITEXPARAMETERFEXTPROC) (GLenum texunit, GLenum target, GLenum pname, GLfloat param); +typedef void (APIENTRYP PFNGLMULTITEXPARAMETERFVEXTPROC) (GLenum texunit, GLenum target, GLenum pname, const GLfloat *params); +typedef void (APIENTRYP PFNGLMULTITEXIMAGE1DEXTPROC) (GLenum texunit, GLenum target, GLint level, GLint internalformat, GLsizei width, GLint border, GLenum format, GLenum type, const void *pixels); +typedef void (APIENTRYP PFNGLMULTITEXIMAGE2DEXTPROC) (GLenum texunit, GLenum target, GLint level, GLint internalformat, GLsizei width, GLsizei height, GLint border, GLenum format, GLenum type, const void *pixels); +typedef void (APIENTRYP PFNGLMULTITEXSUBIMAGE1DEXTPROC) (GLenum texunit, GLenum target, GLint level, GLint xoffset, GLsizei width, GLenum format, GLenum type, const void *pixels); +typedef void (APIENTRYP PFNGLMULTITEXSUBIMAGE2DEXTPROC) (GLenum texunit, GLenum target, GLint level, GLint xoffset, GLint yoffset, GLsizei width, GLsizei height, GLenum format, GLenum type, const void *pixels); +typedef void (APIENTRYP PFNGLCOPYMULTITEXIMAGE1DEXTPROC) (GLenum texunit, GLenum target, GLint level, GLenum internalformat, GLint x, GLint y, GLsizei width, GLint border); +typedef void (APIENTRYP PFNGLCOPYMULTITEXIMAGE2DEXTPROC) (GLenum texunit, GLenum target, GLint level, GLenum internalformat, GLint x, GLint y, GLsizei width, GLsizei height, GLint border); +typedef void (APIENTRYP PFNGLCOPYMULTITEXSUBIMAGE1DEXTPROC) (GLenum texunit, GLenum target, GLint level, GLint xoffset, GLint x, GLint y, GLsizei width); +typedef void (APIENTRYP PFNGLCOPYMULTITEXSUBIMAGE2DEXTPROC) (GLenum texunit, GLenum target, GLint level, GLint xoffset, GLint yoffset, GLint x, GLint y, GLsizei width, GLsizei height); +typedef void (APIENTRYP PFNGLGETMULTITEXIMAGEEXTPROC) (GLenum texunit, GLenum target, GLint level, GLenum format, GLenum type, void *pixels); +typedef void (APIENTRYP PFNGLGETMULTITEXPARAMETERFVEXTPROC) (GLenum texunit, GLenum target, GLenum pname, GLfloat *params); +typedef void (APIENTRYP PFNGLGETMULTITEXPARAMETERIVEXTPROC) (GLenum texunit, GLenum target, GLenum pname, GLint *params); +typedef void (APIENTRYP PFNGLGETMULTITEXLEVELPARAMETERFVEXTPROC) (GLenum texunit, GLenum target, GLint level, GLenum pname, GLfloat *params); +typedef void (APIENTRYP PFNGLGETMULTITEXLEVELPARAMETERIVEXTPROC) (GLenum texunit, GLenum target, GLint level, GLenum pname, GLint *params); +typedef void (APIENTRYP PFNGLMULTITEXIMAGE3DEXTPROC) (GLenum texunit, GLenum target, GLint level, GLint internalformat, GLsizei width, GLsizei height, GLsizei depth, GLint border, GLenum format, GLenum type, const void *pixels); +typedef void (APIENTRYP PFNGLMULTITEXSUBIMAGE3DEXTPROC) (GLenum texunit, GLenum target, GLint level, GLint xoffset, GLint yoffset, GLint zoffset, GLsizei width, GLsizei height, GLsizei depth, GLenum format, GLenum type, const void *pixels); +typedef void (APIENTRYP PFNGLCOPYMULTITEXSUBIMAGE3DEXTPROC) (GLenum texunit, GLenum target, GLint level, GLint xoffset, GLint yoffset, GLint zoffset, GLint x, GLint y, GLsizei width, GLsizei height); +typedef void (APIENTRYP PFNGLENABLECLIENTSTATEINDEXEDEXTPROC) (GLenum array, GLuint index); +typedef void (APIENTRYP PFNGLDISABLECLIENTSTATEINDEXEDEXTPROC) (GLenum array, GLuint index); +typedef void (APIENTRYP PFNGLGETFLOATINDEXEDVEXTPROC) (GLenum target, GLuint index, GLfloat *data); +typedef void (APIENTRYP PFNGLGETDOUBLEINDEXEDVEXTPROC) (GLenum target, GLuint index, GLdouble *data); +typedef void (APIENTRYP PFNGLGETPOINTERINDEXEDVEXTPROC) (GLenum target, GLuint index, void **data); +typedef void (APIENTRYP PFNGLENABLEINDEXEDEXTPROC) (GLenum target, GLuint index); +typedef void (APIENTRYP PFNGLDISABLEINDEXEDEXTPROC) (GLenum target, GLuint index); +typedef GLboolean (APIENTRYP PFNGLISENABLEDINDEXEDEXTPROC) (GLenum target, GLuint index); +typedef void (APIENTRYP PFNGLGETINTEGERINDEXEDVEXTPROC) (GLenum target, GLuint index, GLint *data); +typedef void (APIENTRYP PFNGLGETBOOLEANINDEXEDVEXTPROC) (GLenum target, GLuint index, GLboolean *data); +typedef void (APIENTRYP PFNGLCOMPRESSEDTEXTUREIMAGE3DEXTPROC) (GLuint texture, GLenum target, GLint level, GLenum internalformat, GLsizei width, GLsizei height, GLsizei depth, GLint border, GLsizei imageSize, const void *bits); +typedef void (APIENTRYP PFNGLCOMPRESSEDTEXTUREIMAGE2DEXTPROC) (GLuint texture, GLenum target, GLint level, GLenum internalformat, GLsizei width, GLsizei height, GLint border, GLsizei imageSize, const void *bits); +typedef void (APIENTRYP PFNGLCOMPRESSEDTEXTUREIMAGE1DEXTPROC) (GLuint texture, GLenum target, GLint level, GLenum internalformat, GLsizei width, GLint border, GLsizei imageSize, const void *bits); +typedef void (APIENTRYP PFNGLCOMPRESSEDTEXTURESUBIMAGE3DEXTPROC) (GLuint texture, GLenum target, GLint level, GLint xoffset, GLint yoffset, GLint zoffset, GLsizei width, GLsizei height, GLsizei depth, GLenum format, GLsizei imageSize, const void *bits); +typedef void (APIENTRYP PFNGLCOMPRESSEDTEXTURESUBIMAGE2DEXTPROC) (GLuint texture, GLenum target, GLint level, GLint xoffset, GLint yoffset, GLsizei width, GLsizei height, GLenum format, GLsizei imageSize, const void *bits); +typedef void (APIENTRYP PFNGLCOMPRESSEDTEXTURESUBIMAGE1DEXTPROC) (GLuint texture, GLenum target, GLint level, GLint xoffset, GLsizei width, GLenum format, GLsizei imageSize, const void *bits); +typedef void (APIENTRYP PFNGLGETCOMPRESSEDTEXTUREIMAGEEXTPROC) (GLuint texture, GLenum target, GLint lod, void *img); +typedef void (APIENTRYP PFNGLCOMPRESSEDMULTITEXIMAGE3DEXTPROC) (GLenum texunit, GLenum target, GLint level, GLenum internalformat, GLsizei width, GLsizei height, GLsizei depth, GLint border, GLsizei imageSize, const void *bits); +typedef void (APIENTRYP PFNGLCOMPRESSEDMULTITEXIMAGE2DEXTPROC) (GLenum texunit, GLenum target, GLint level, GLenum internalformat, GLsizei width, GLsizei height, GLint border, GLsizei imageSize, const void *bits); +typedef void (APIENTRYP PFNGLCOMPRESSEDMULTITEXIMAGE1DEXTPROC) (GLenum texunit, GLenum target, GLint level, GLenum internalformat, GLsizei width, GLint border, GLsizei imageSize, const void *bits); +typedef void (APIENTRYP PFNGLCOMPRESSEDMULTITEXSUBIMAGE3DEXTPROC) (GLenum texunit, GLenum target, GLint level, GLint xoffset, GLint yoffset, GLint zoffset, GLsizei width, GLsizei height, GLsizei depth, GLenum format, GLsizei imageSize, const void *bits); +typedef void (APIENTRYP PFNGLCOMPRESSEDMULTITEXSUBIMAGE2DEXTPROC) (GLenum texunit, GLenum target, GLint level, GLint xoffset, GLint yoffset, GLsizei width, GLsizei height, GLenum format, GLsizei imageSize, const void *bits); +typedef void (APIENTRYP PFNGLCOMPRESSEDMULTITEXSUBIMAGE1DEXTPROC) (GLenum texunit, GLenum target, GLint level, GLint xoffset, GLsizei width, GLenum format, GLsizei imageSize, const void *bits); +typedef void (APIENTRYP PFNGLGETCOMPRESSEDMULTITEXIMAGEEXTPROC) (GLenum texunit, GLenum target, GLint lod, void *img); +typedef void (APIENTRYP PFNGLMATRIXLOADTRANSPOSEFEXTPROC) (GLenum mode, const GLfloat *m); +typedef void (APIENTRYP PFNGLMATRIXLOADTRANSPOSEDEXTPROC) (GLenum mode, const GLdouble *m); +typedef void (APIENTRYP PFNGLMATRIXMULTTRANSPOSEFEXTPROC) (GLenum mode, const GLfloat *m); +typedef void (APIENTRYP PFNGLMATRIXMULTTRANSPOSEDEXTPROC) (GLenum mode, const GLdouble *m); +typedef void (APIENTRYP PFNGLNAMEDBUFFERDATAEXTPROC) (GLuint buffer, GLsizeiptr size, const void *data, GLenum usage); +typedef void (APIENTRYP PFNGLNAMEDBUFFERSUBDATAEXTPROC) (GLuint buffer, GLintptr offset, GLsizeiptr size, const void *data); +typedef void *(APIENTRYP PFNGLMAPNAMEDBUFFEREXTPROC) (GLuint buffer, GLenum access); +typedef GLboolean (APIENTRYP PFNGLUNMAPNAMEDBUFFEREXTPROC) (GLuint buffer); +typedef void (APIENTRYP PFNGLGETNAMEDBUFFERPARAMETERIVEXTPROC) (GLuint buffer, GLenum pname, GLint *params); +typedef void (APIENTRYP PFNGLGETNAMEDBUFFERPOINTERVEXTPROC) (GLuint buffer, GLenum pname, void **params); +typedef void (APIENTRYP PFNGLGETNAMEDBUFFERSUBDATAEXTPROC) (GLuint buffer, GLintptr offset, GLsizeiptr size, void *data); +typedef void (APIENTRYP PFNGLPROGRAMUNIFORM1FEXTPROC) (GLuint program, GLint location, GLfloat v0); +typedef void (APIENTRYP PFNGLPROGRAMUNIFORM2FEXTPROC) (GLuint program, GLint location, GLfloat v0, GLfloat v1); +typedef void (APIENTRYP PFNGLPROGRAMUNIFORM3FEXTPROC) (GLuint program, GLint location, GLfloat v0, GLfloat v1, GLfloat v2); +typedef void (APIENTRYP PFNGLPROGRAMUNIFORM4FEXTPROC) (GLuint program, GLint location, GLfloat v0, GLfloat v1, GLfloat v2, GLfloat v3); +typedef void (APIENTRYP PFNGLPROGRAMUNIFORM1IEXTPROC) (GLuint program, GLint location, GLint v0); +typedef void (APIENTRYP PFNGLPROGRAMUNIFORM2IEXTPROC) (GLuint program, GLint location, GLint v0, GLint v1); +typedef void (APIENTRYP PFNGLPROGRAMUNIFORM3IEXTPROC) (GLuint program, GLint location, GLint v0, GLint v1, GLint v2); +typedef void (APIENTRYP PFNGLPROGRAMUNIFORM4IEXTPROC) (GLuint program, GLint location, GLint v0, GLint v1, GLint v2, GLint v3); +typedef void (APIENTRYP PFNGLPROGRAMUNIFORM1FVEXTPROC) (GLuint program, GLint location, GLsizei count, const GLfloat *value); +typedef void (APIENTRYP PFNGLPROGRAMUNIFORM2FVEXTPROC) (GLuint program, GLint location, GLsizei count, const GLfloat *value); +typedef void (APIENTRYP PFNGLPROGRAMUNIFORM3FVEXTPROC) (GLuint program, GLint location, GLsizei count, const GLfloat *value); +typedef void (APIENTRYP PFNGLPROGRAMUNIFORM4FVEXTPROC) (GLuint program, GLint location, GLsizei count, const GLfloat *value); +typedef void (APIENTRYP PFNGLPROGRAMUNIFORM1IVEXTPROC) (GLuint program, GLint location, GLsizei count, const GLint *value); +typedef void (APIENTRYP PFNGLPROGRAMUNIFORM2IVEXTPROC) (GLuint program, GLint location, GLsizei count, const GLint *value); +typedef void (APIENTRYP PFNGLPROGRAMUNIFORM3IVEXTPROC) (GLuint program, GLint location, GLsizei count, const GLint *value); +typedef void (APIENTRYP PFNGLPROGRAMUNIFORM4IVEXTPROC) (GLuint program, GLint location, GLsizei count, const GLint *value); +typedef void (APIENTRYP PFNGLPROGRAMUNIFORMMATRIX2FVEXTPROC) (GLuint program, GLint location, GLsizei count, GLboolean transpose, const GLfloat *value); +typedef void (APIENTRYP PFNGLPROGRAMUNIFORMMATRIX3FVEXTPROC) (GLuint program, GLint location, GLsizei count, GLboolean transpose, const GLfloat *value); +typedef void (APIENTRYP PFNGLPROGRAMUNIFORMMATRIX4FVEXTPROC) (GLuint program, GLint location, GLsizei count, GLboolean transpose, const GLfloat *value); +typedef void (APIENTRYP PFNGLPROGRAMUNIFORMMATRIX2X3FVEXTPROC) (GLuint program, GLint location, GLsizei count, GLboolean transpose, const GLfloat *value); +typedef void (APIENTRYP PFNGLPROGRAMUNIFORMMATRIX3X2FVEXTPROC) (GLuint program, GLint location, GLsizei count, GLboolean transpose, const GLfloat *value); +typedef void (APIENTRYP PFNGLPROGRAMUNIFORMMATRIX2X4FVEXTPROC) (GLuint program, GLint location, GLsizei count, GLboolean transpose, const GLfloat *value); +typedef void (APIENTRYP PFNGLPROGRAMUNIFORMMATRIX4X2FVEXTPROC) (GLuint program, GLint location, GLsizei count, GLboolean transpose, const GLfloat *value); +typedef void (APIENTRYP PFNGLPROGRAMUNIFORMMATRIX3X4FVEXTPROC) (GLuint program, GLint location, GLsizei count, GLboolean transpose, const GLfloat *value); +typedef void (APIENTRYP PFNGLPROGRAMUNIFORMMATRIX4X3FVEXTPROC) (GLuint program, GLint location, GLsizei count, GLboolean transpose, const GLfloat *value); +typedef void (APIENTRYP PFNGLTEXTUREBUFFEREXTPROC) (GLuint texture, GLenum target, GLenum internalformat, GLuint buffer); +typedef void (APIENTRYP PFNGLMULTITEXBUFFEREXTPROC) (GLenum texunit, GLenum target, GLenum internalformat, GLuint buffer); +typedef void (APIENTRYP PFNGLTEXTUREPARAMETERIIVEXTPROC) (GLuint texture, GLenum target, GLenum pname, const GLint *params); +typedef void (APIENTRYP PFNGLTEXTUREPARAMETERIUIVEXTPROC) (GLuint texture, GLenum target, GLenum pname, const GLuint *params); +typedef void (APIENTRYP PFNGLGETTEXTUREPARAMETERIIVEXTPROC) (GLuint texture, GLenum target, GLenum pname, GLint *params); +typedef void (APIENTRYP PFNGLGETTEXTUREPARAMETERIUIVEXTPROC) (GLuint texture, GLenum target, GLenum pname, GLuint *params); +typedef void (APIENTRYP PFNGLMULTITEXPARAMETERIIVEXTPROC) (GLenum texunit, GLenum target, GLenum pname, const GLint *params); +typedef void (APIENTRYP PFNGLMULTITEXPARAMETERIUIVEXTPROC) (GLenum texunit, GLenum target, GLenum pname, const GLuint *params); +typedef void (APIENTRYP PFNGLGETMULTITEXPARAMETERIIVEXTPROC) (GLenum texunit, GLenum target, GLenum pname, GLint *params); +typedef void (APIENTRYP PFNGLGETMULTITEXPARAMETERIUIVEXTPROC) (GLenum texunit, GLenum target, GLenum pname, GLuint *params); +typedef void (APIENTRYP PFNGLPROGRAMUNIFORM1UIEXTPROC) (GLuint program, GLint location, GLuint v0); +typedef void (APIENTRYP PFNGLPROGRAMUNIFORM2UIEXTPROC) (GLuint program, GLint location, GLuint v0, GLuint v1); +typedef void (APIENTRYP PFNGLPROGRAMUNIFORM3UIEXTPROC) (GLuint program, GLint location, GLuint v0, GLuint v1, GLuint v2); +typedef void (APIENTRYP PFNGLPROGRAMUNIFORM4UIEXTPROC) (GLuint program, GLint location, GLuint v0, GLuint v1, GLuint v2, GLuint v3); +typedef void (APIENTRYP PFNGLPROGRAMUNIFORM1UIVEXTPROC) (GLuint program, GLint location, GLsizei count, const GLuint *value); +typedef void (APIENTRYP PFNGLPROGRAMUNIFORM2UIVEXTPROC) (GLuint program, GLint location, GLsizei count, const GLuint *value); +typedef void (APIENTRYP PFNGLPROGRAMUNIFORM3UIVEXTPROC) (GLuint program, GLint location, GLsizei count, const GLuint *value); +typedef void (APIENTRYP PFNGLPROGRAMUNIFORM4UIVEXTPROC) (GLuint program, GLint location, GLsizei count, const GLuint *value); +typedef void (APIENTRYP PFNGLNAMEDPROGRAMLOCALPARAMETERS4FVEXTPROC) (GLuint program, GLenum target, GLuint index, GLsizei count, const GLfloat *params); +typedef void (APIENTRYP PFNGLNAMEDPROGRAMLOCALPARAMETERI4IEXTPROC) (GLuint program, GLenum target, GLuint index, GLint x, GLint y, GLint z, GLint w); +typedef void (APIENTRYP PFNGLNAMEDPROGRAMLOCALPARAMETERI4IVEXTPROC) (GLuint program, GLenum target, GLuint index, const GLint *params); +typedef void (APIENTRYP PFNGLNAMEDPROGRAMLOCALPARAMETERSI4IVEXTPROC) (GLuint program, GLenum target, GLuint index, GLsizei count, const GLint *params); +typedef void (APIENTRYP PFNGLNAMEDPROGRAMLOCALPARAMETERI4UIEXTPROC) (GLuint program, GLenum target, GLuint index, GLuint x, GLuint y, GLuint z, GLuint w); +typedef void (APIENTRYP PFNGLNAMEDPROGRAMLOCALPARAMETERI4UIVEXTPROC) (GLuint program, GLenum target, GLuint index, const GLuint *params); +typedef void (APIENTRYP PFNGLNAMEDPROGRAMLOCALPARAMETERSI4UIVEXTPROC) (GLuint program, GLenum target, GLuint index, GLsizei count, const GLuint *params); +typedef void (APIENTRYP PFNGLGETNAMEDPROGRAMLOCALPARAMETERIIVEXTPROC) (GLuint program, GLenum target, GLuint index, GLint *params); +typedef void (APIENTRYP PFNGLGETNAMEDPROGRAMLOCALPARAMETERIUIVEXTPROC) (GLuint program, GLenum target, GLuint index, GLuint *params); +typedef void (APIENTRYP PFNGLENABLECLIENTSTATEIEXTPROC) (GLenum array, GLuint index); +typedef void (APIENTRYP PFNGLDISABLECLIENTSTATEIEXTPROC) (GLenum array, GLuint index); +typedef void (APIENTRYP PFNGLGETFLOATI_VEXTPROC) (GLenum pname, GLuint index, GLfloat *params); +typedef void (APIENTRYP PFNGLGETDOUBLEI_VEXTPROC) (GLenum pname, GLuint index, GLdouble *params); +typedef void (APIENTRYP PFNGLGETPOINTERI_VEXTPROC) (GLenum pname, GLuint index, void **params); +typedef void (APIENTRYP PFNGLNAMEDPROGRAMSTRINGEXTPROC) (GLuint program, GLenum target, GLenum format, GLsizei len, const void *string); +typedef void (APIENTRYP PFNGLNAMEDPROGRAMLOCALPARAMETER4DEXTPROC) (GLuint program, GLenum target, GLuint index, GLdouble x, GLdouble y, GLdouble z, GLdouble w); +typedef void (APIENTRYP PFNGLNAMEDPROGRAMLOCALPARAMETER4DVEXTPROC) (GLuint program, GLenum target, GLuint index, const GLdouble *params); +typedef void (APIENTRYP PFNGLNAMEDPROGRAMLOCALPARAMETER4FEXTPROC) (GLuint program, GLenum target, GLuint index, GLfloat x, GLfloat y, GLfloat z, GLfloat w); +typedef void (APIENTRYP PFNGLNAMEDPROGRAMLOCALPARAMETER4FVEXTPROC) (GLuint program, GLenum target, GLuint index, const GLfloat *params); +typedef void (APIENTRYP PFNGLGETNAMEDPROGRAMLOCALPARAMETERDVEXTPROC) (GLuint program, GLenum target, GLuint index, GLdouble *params); +typedef void (APIENTRYP PFNGLGETNAMEDPROGRAMLOCALPARAMETERFVEXTPROC) (GLuint program, GLenum target, GLuint index, GLfloat *params); +typedef void (APIENTRYP PFNGLGETNAMEDPROGRAMIVEXTPROC) (GLuint program, GLenum target, GLenum pname, GLint *params); +typedef void (APIENTRYP PFNGLGETNAMEDPROGRAMSTRINGEXTPROC) (GLuint program, GLenum target, GLenum pname, void *string); +typedef void (APIENTRYP PFNGLNAMEDRENDERBUFFERSTORAGEEXTPROC) (GLuint renderbuffer, GLenum internalformat, GLsizei width, GLsizei height); +typedef void (APIENTRYP PFNGLGETNAMEDRENDERBUFFERPARAMETERIVEXTPROC) (GLuint renderbuffer, GLenum pname, GLint *params); +typedef void (APIENTRYP PFNGLNAMEDRENDERBUFFERSTORAGEMULTISAMPLEEXTPROC) (GLuint renderbuffer, GLsizei samples, GLenum internalformat, GLsizei width, GLsizei height); +typedef void (APIENTRYP PFNGLNAMEDRENDERBUFFERSTORAGEMULTISAMPLECOVERAGEEXTPROC) (GLuint renderbuffer, GLsizei coverageSamples, GLsizei colorSamples, GLenum internalformat, GLsizei width, GLsizei height); +typedef GLenum (APIENTRYP PFNGLCHECKNAMEDFRAMEBUFFERSTATUSEXTPROC) (GLuint framebuffer, GLenum target); +typedef void (APIENTRYP PFNGLNAMEDFRAMEBUFFERTEXTURE1DEXTPROC) (GLuint framebuffer, GLenum attachment, GLenum textarget, GLuint texture, GLint level); +typedef void (APIENTRYP PFNGLNAMEDFRAMEBUFFERTEXTURE2DEXTPROC) (GLuint framebuffer, GLenum attachment, GLenum textarget, GLuint texture, GLint level); +typedef void (APIENTRYP PFNGLNAMEDFRAMEBUFFERTEXTURE3DEXTPROC) (GLuint framebuffer, GLenum attachment, GLenum textarget, GLuint texture, GLint level, GLint zoffset); +typedef void (APIENTRYP PFNGLNAMEDFRAMEBUFFERRENDERBUFFEREXTPROC) (GLuint framebuffer, GLenum attachment, GLenum renderbuffertarget, GLuint renderbuffer); +typedef void (APIENTRYP PFNGLGETNAMEDFRAMEBUFFERATTACHMENTPARAMETERIVEXTPROC) (GLuint framebuffer, GLenum attachment, GLenum pname, GLint *params); +typedef void (APIENTRYP PFNGLGENERATETEXTUREMIPMAPEXTPROC) (GLuint texture, GLenum target); +typedef void (APIENTRYP PFNGLGENERATEMULTITEXMIPMAPEXTPROC) (GLenum texunit, GLenum target); +typedef void (APIENTRYP PFNGLFRAMEBUFFERDRAWBUFFEREXTPROC) (GLuint framebuffer, GLenum mode); +typedef void (APIENTRYP PFNGLFRAMEBUFFERDRAWBUFFERSEXTPROC) (GLuint framebuffer, GLsizei n, const GLenum *bufs); +typedef void (APIENTRYP PFNGLFRAMEBUFFERREADBUFFEREXTPROC) (GLuint framebuffer, GLenum mode); +typedef void (APIENTRYP PFNGLGETFRAMEBUFFERPARAMETERIVEXTPROC) (GLuint framebuffer, GLenum pname, GLint *params); +typedef void (APIENTRYP PFNGLNAMEDCOPYBUFFERSUBDATAEXTPROC) (GLuint readBuffer, GLuint writeBuffer, GLintptr readOffset, GLintptr writeOffset, GLsizeiptr size); +typedef void (APIENTRYP PFNGLNAMEDFRAMEBUFFERTEXTUREEXTPROC) (GLuint framebuffer, GLenum attachment, GLuint texture, GLint level); +typedef void (APIENTRYP PFNGLNAMEDFRAMEBUFFERTEXTURELAYEREXTPROC) (GLuint framebuffer, GLenum attachment, GLuint texture, GLint level, GLint layer); +typedef void (APIENTRYP PFNGLNAMEDFRAMEBUFFERTEXTUREFACEEXTPROC) (GLuint framebuffer, GLenum attachment, GLuint texture, GLint level, GLenum face); +typedef void (APIENTRYP PFNGLTEXTURERENDERBUFFEREXTPROC) (GLuint texture, GLenum target, GLuint renderbuffer); +typedef void (APIENTRYP PFNGLMULTITEXRENDERBUFFEREXTPROC) (GLenum texunit, GLenum target, GLuint renderbuffer); +typedef void (APIENTRYP PFNGLVERTEXARRAYVERTEXOFFSETEXTPROC) (GLuint vaobj, GLuint buffer, GLint size, GLenum type, GLsizei stride, GLintptr offset); +typedef void (APIENTRYP PFNGLVERTEXARRAYCOLOROFFSETEXTPROC) (GLuint vaobj, GLuint buffer, GLint size, GLenum type, GLsizei stride, GLintptr offset); +typedef void (APIENTRYP PFNGLVERTEXARRAYEDGEFLAGOFFSETEXTPROC) (GLuint vaobj, GLuint buffer, GLsizei stride, GLintptr offset); +typedef void (APIENTRYP PFNGLVERTEXARRAYINDEXOFFSETEXTPROC) (GLuint vaobj, GLuint buffer, GLenum type, GLsizei stride, GLintptr offset); +typedef void (APIENTRYP PFNGLVERTEXARRAYNORMALOFFSETEXTPROC) (GLuint vaobj, GLuint buffer, GLenum type, GLsizei stride, GLintptr offset); +typedef void (APIENTRYP PFNGLVERTEXARRAYTEXCOORDOFFSETEXTPROC) (GLuint vaobj, GLuint buffer, GLint size, GLenum type, GLsizei stride, GLintptr offset); +typedef void (APIENTRYP PFNGLVERTEXARRAYMULTITEXCOORDOFFSETEXTPROC) (GLuint vaobj, GLuint buffer, GLenum texunit, GLint size, GLenum type, GLsizei stride, GLintptr offset); +typedef void (APIENTRYP PFNGLVERTEXARRAYFOGCOORDOFFSETEXTPROC) (GLuint vaobj, GLuint buffer, GLenum type, GLsizei stride, GLintptr offset); +typedef void (APIENTRYP PFNGLVERTEXARRAYSECONDARYCOLOROFFSETEXTPROC) (GLuint vaobj, GLuint buffer, GLint size, GLenum type, GLsizei stride, GLintptr offset); +typedef void (APIENTRYP PFNGLVERTEXARRAYVERTEXATTRIBOFFSETEXTPROC) (GLuint vaobj, GLuint buffer, GLuint index, GLint size, GLenum type, GLboolean normalized, GLsizei stride, GLintptr offset); +typedef void (APIENTRYP PFNGLVERTEXARRAYVERTEXATTRIBIOFFSETEXTPROC) (GLuint vaobj, GLuint buffer, GLuint index, GLint size, GLenum type, GLsizei stride, GLintptr offset); +typedef void (APIENTRYP PFNGLENABLEVERTEXARRAYEXTPROC) (GLuint vaobj, GLenum array); +typedef void (APIENTRYP PFNGLDISABLEVERTEXARRAYEXTPROC) (GLuint vaobj, GLenum array); +typedef void (APIENTRYP PFNGLENABLEVERTEXARRAYATTRIBEXTPROC) (GLuint vaobj, GLuint index); +typedef void (APIENTRYP PFNGLDISABLEVERTEXARRAYATTRIBEXTPROC) (GLuint vaobj, GLuint index); +typedef void (APIENTRYP PFNGLGETVERTEXARRAYINTEGERVEXTPROC) (GLuint vaobj, GLenum pname, GLint *param); +typedef void (APIENTRYP PFNGLGETVERTEXARRAYPOINTERVEXTPROC) (GLuint vaobj, GLenum pname, void **param); +typedef void (APIENTRYP PFNGLGETVERTEXARRAYINTEGERI_VEXTPROC) (GLuint vaobj, GLuint index, GLenum pname, GLint *param); +typedef void (APIENTRYP PFNGLGETVERTEXARRAYPOINTERI_VEXTPROC) (GLuint vaobj, GLuint index, GLenum pname, void **param); +typedef void *(APIENTRYP PFNGLMAPNAMEDBUFFERRANGEEXTPROC) (GLuint buffer, GLintptr offset, GLsizeiptr length, GLbitfield access); +typedef void (APIENTRYP PFNGLFLUSHMAPPEDNAMEDBUFFERRANGEEXTPROC) (GLuint buffer, GLintptr offset, GLsizeiptr length); +typedef void (APIENTRYP PFNGLNAMEDBUFFERSTORAGEEXTPROC) (GLuint buffer, GLsizeiptr size, const void *data, GLbitfield flags); +typedef void (APIENTRYP PFNGLCLEARNAMEDBUFFERDATAEXTPROC) (GLuint buffer, GLenum internalformat, GLenum format, GLenum type, const void *data); +typedef void (APIENTRYP PFNGLCLEARNAMEDBUFFERSUBDATAEXTPROC) (GLuint buffer, GLenum internalformat, GLsizeiptr offset, GLsizeiptr size, GLenum format, GLenum type, const void *data); +typedef void (APIENTRYP PFNGLNAMEDFRAMEBUFFERPARAMETERIEXTPROC) (GLuint framebuffer, GLenum pname, GLint param); +typedef void (APIENTRYP PFNGLGETNAMEDFRAMEBUFFERPARAMETERIVEXTPROC) (GLuint framebuffer, GLenum pname, GLint *params); +typedef void (APIENTRYP PFNGLPROGRAMUNIFORM1DEXTPROC) (GLuint program, GLint location, GLdouble x); +typedef void (APIENTRYP PFNGLPROGRAMUNIFORM2DEXTPROC) (GLuint program, GLint location, GLdouble x, GLdouble y); +typedef void (APIENTRYP PFNGLPROGRAMUNIFORM3DEXTPROC) (GLuint program, GLint location, GLdouble x, GLdouble y, GLdouble z); +typedef void (APIENTRYP PFNGLPROGRAMUNIFORM4DEXTPROC) (GLuint program, GLint location, GLdouble x, GLdouble y, GLdouble z, GLdouble w); +typedef void (APIENTRYP PFNGLPROGRAMUNIFORM1DVEXTPROC) (GLuint program, GLint location, GLsizei count, const GLdouble *value); +typedef void (APIENTRYP PFNGLPROGRAMUNIFORM2DVEXTPROC) (GLuint program, GLint location, GLsizei count, const GLdouble *value); +typedef void (APIENTRYP PFNGLPROGRAMUNIFORM3DVEXTPROC) (GLuint program, GLint location, GLsizei count, const GLdouble *value); +typedef void (APIENTRYP PFNGLPROGRAMUNIFORM4DVEXTPROC) (GLuint program, GLint location, GLsizei count, const GLdouble *value); +typedef void (APIENTRYP PFNGLPROGRAMUNIFORMMATRIX2DVEXTPROC) (GLuint program, GLint location, GLsizei count, GLboolean transpose, const GLdouble *value); +typedef void (APIENTRYP PFNGLPROGRAMUNIFORMMATRIX3DVEXTPROC) (GLuint program, GLint location, GLsizei count, GLboolean transpose, const GLdouble *value); +typedef void (APIENTRYP PFNGLPROGRAMUNIFORMMATRIX4DVEXTPROC) (GLuint program, GLint location, GLsizei count, GLboolean transpose, const GLdouble *value); +typedef void (APIENTRYP PFNGLPROGRAMUNIFORMMATRIX2X3DVEXTPROC) (GLuint program, GLint location, GLsizei count, GLboolean transpose, const GLdouble *value); +typedef void (APIENTRYP PFNGLPROGRAMUNIFORMMATRIX2X4DVEXTPROC) (GLuint program, GLint location, GLsizei count, GLboolean transpose, const GLdouble *value); +typedef void (APIENTRYP PFNGLPROGRAMUNIFORMMATRIX3X2DVEXTPROC) (GLuint program, GLint location, GLsizei count, GLboolean transpose, const GLdouble *value); +typedef void (APIENTRYP PFNGLPROGRAMUNIFORMMATRIX3X4DVEXTPROC) (GLuint program, GLint location, GLsizei count, GLboolean transpose, const GLdouble *value); +typedef void (APIENTRYP PFNGLPROGRAMUNIFORMMATRIX4X2DVEXTPROC) (GLuint program, GLint location, GLsizei count, GLboolean transpose, const GLdouble *value); +typedef void (APIENTRYP PFNGLPROGRAMUNIFORMMATRIX4X3DVEXTPROC) (GLuint program, GLint location, GLsizei count, GLboolean transpose, const GLdouble *value); +typedef void (APIENTRYP PFNGLTEXTUREBUFFERRANGEEXTPROC) (GLuint texture, GLenum target, GLenum internalformat, GLuint buffer, GLintptr offset, GLsizeiptr size); +typedef void (APIENTRYP PFNGLTEXTURESTORAGE1DEXTPROC) (GLuint texture, GLenum target, GLsizei levels, GLenum internalformat, GLsizei width); +typedef void (APIENTRYP PFNGLTEXTURESTORAGE2DEXTPROC) (GLuint texture, GLenum target, GLsizei levels, GLenum internalformat, GLsizei width, GLsizei height); +typedef void (APIENTRYP PFNGLTEXTURESTORAGE3DEXTPROC) (GLuint texture, GLenum target, GLsizei levels, GLenum internalformat, GLsizei width, GLsizei height, GLsizei depth); +typedef void (APIENTRYP PFNGLTEXTURESTORAGE2DMULTISAMPLEEXTPROC) (GLuint texture, GLenum target, GLsizei samples, GLenum internalformat, GLsizei width, GLsizei height, GLboolean fixedsamplelocations); +typedef void (APIENTRYP PFNGLTEXTURESTORAGE3DMULTISAMPLEEXTPROC) (GLuint texture, GLenum target, GLsizei samples, GLenum internalformat, GLsizei width, GLsizei height, GLsizei depth, GLboolean fixedsamplelocations); +typedef void (APIENTRYP PFNGLVERTEXARRAYBINDVERTEXBUFFEREXTPROC) (GLuint vaobj, GLuint bindingindex, GLuint buffer, GLintptr offset, GLsizei stride); +typedef void (APIENTRYP PFNGLVERTEXARRAYVERTEXATTRIBFORMATEXTPROC) (GLuint vaobj, GLuint attribindex, GLint size, GLenum type, GLboolean normalized, GLuint relativeoffset); +typedef void (APIENTRYP PFNGLVERTEXARRAYVERTEXATTRIBIFORMATEXTPROC) (GLuint vaobj, GLuint attribindex, GLint size, GLenum type, GLuint relativeoffset); +typedef void (APIENTRYP PFNGLVERTEXARRAYVERTEXATTRIBLFORMATEXTPROC) (GLuint vaobj, GLuint attribindex, GLint size, GLenum type, GLuint relativeoffset); +typedef void (APIENTRYP PFNGLVERTEXARRAYVERTEXATTRIBBINDINGEXTPROC) (GLuint vaobj, GLuint attribindex, GLuint bindingindex); +typedef void (APIENTRYP PFNGLVERTEXARRAYVERTEXBINDINGDIVISOREXTPROC) (GLuint vaobj, GLuint bindingindex, GLuint divisor); +typedef void (APIENTRYP PFNGLVERTEXARRAYVERTEXATTRIBLOFFSETEXTPROC) (GLuint vaobj, GLuint buffer, GLuint index, GLint size, GLenum type, GLsizei stride, GLintptr offset); +typedef void (APIENTRYP PFNGLTEXTUREPAGECOMMITMENTEXTPROC) (GLuint texture, GLint level, GLint xoffset, GLint yoffset, GLint zoffset, GLsizei width, GLsizei height, GLsizei depth, GLboolean resident); +typedef void (APIENTRYP PFNGLVERTEXARRAYVERTEXATTRIBDIVISOREXTPROC) (GLuint vaobj, GLuint index, GLuint divisor); +#ifdef GL_GLEXT_PROTOTYPES +GLAPI void APIENTRY glMatrixLoadfEXT (GLenum mode, const GLfloat *m); +GLAPI void APIENTRY glMatrixLoaddEXT (GLenum mode, const GLdouble *m); +GLAPI void APIENTRY glMatrixMultfEXT (GLenum mode, const GLfloat *m); +GLAPI void APIENTRY glMatrixMultdEXT (GLenum mode, const GLdouble *m); +GLAPI void APIENTRY glMatrixLoadIdentityEXT (GLenum mode); +GLAPI void APIENTRY glMatrixRotatefEXT (GLenum mode, GLfloat angle, GLfloat x, GLfloat y, GLfloat z); +GLAPI void APIENTRY glMatrixRotatedEXT (GLenum mode, GLdouble angle, GLdouble x, GLdouble y, GLdouble z); +GLAPI void APIENTRY glMatrixScalefEXT (GLenum mode, GLfloat x, GLfloat y, GLfloat z); +GLAPI void APIENTRY glMatrixScaledEXT (GLenum mode, GLdouble x, GLdouble y, GLdouble z); +GLAPI void APIENTRY glMatrixTranslatefEXT (GLenum mode, GLfloat x, GLfloat y, GLfloat z); +GLAPI void APIENTRY glMatrixTranslatedEXT (GLenum mode, GLdouble x, GLdouble y, GLdouble z); +GLAPI void APIENTRY glMatrixFrustumEXT (GLenum mode, GLdouble left, GLdouble right, GLdouble bottom, GLdouble top, GLdouble zNear, GLdouble zFar); +GLAPI void APIENTRY glMatrixOrthoEXT (GLenum mode, GLdouble left, GLdouble right, GLdouble bottom, GLdouble top, GLdouble zNear, GLdouble zFar); +GLAPI void APIENTRY glMatrixPopEXT (GLenum mode); +GLAPI void APIENTRY glMatrixPushEXT (GLenum mode); +GLAPI void APIENTRY glClientAttribDefaultEXT (GLbitfield mask); +GLAPI void APIENTRY glPushClientAttribDefaultEXT (GLbitfield mask); +GLAPI void APIENTRY glTextureParameterfEXT (GLuint texture, GLenum target, GLenum pname, GLfloat param); +GLAPI void APIENTRY glTextureParameterfvEXT (GLuint texture, GLenum target, GLenum pname, const GLfloat *params); +GLAPI void APIENTRY glTextureParameteriEXT (GLuint texture, GLenum target, GLenum pname, GLint param); +GLAPI void APIENTRY glTextureParameterivEXT (GLuint texture, GLenum target, GLenum pname, const GLint *params); +GLAPI void APIENTRY glTextureImage1DEXT (GLuint texture, GLenum target, GLint level, GLint internalformat, GLsizei width, GLint border, GLenum format, GLenum type, const void *pixels); +GLAPI void APIENTRY glTextureImage2DEXT (GLuint texture, GLenum target, GLint level, GLint internalformat, GLsizei width, GLsizei height, GLint border, GLenum format, GLenum type, const void *pixels); +GLAPI void APIENTRY glTextureSubImage1DEXT (GLuint texture, GLenum target, GLint level, GLint xoffset, GLsizei width, GLenum format, GLenum type, const void *pixels); +GLAPI void APIENTRY glTextureSubImage2DEXT (GLuint texture, GLenum target, GLint level, GLint xoffset, GLint yoffset, GLsizei width, GLsizei height, GLenum format, GLenum type, const void *pixels); +GLAPI void APIENTRY glCopyTextureImage1DEXT (GLuint texture, GLenum target, GLint level, GLenum internalformat, GLint x, GLint y, GLsizei width, GLint border); +GLAPI void APIENTRY glCopyTextureImage2DEXT (GLuint texture, GLenum target, GLint level, GLenum internalformat, GLint x, GLint y, GLsizei width, GLsizei height, GLint border); +GLAPI void APIENTRY glCopyTextureSubImage1DEXT (GLuint texture, GLenum target, GLint level, GLint xoffset, GLint x, GLint y, GLsizei width); +GLAPI void APIENTRY glCopyTextureSubImage2DEXT (GLuint texture, GLenum target, GLint level, GLint xoffset, GLint yoffset, GLint x, GLint y, GLsizei width, GLsizei height); +GLAPI void APIENTRY glGetTextureImageEXT (GLuint texture, GLenum target, GLint level, GLenum format, GLenum type, void *pixels); +GLAPI void APIENTRY glGetTextureParameterfvEXT (GLuint texture, GLenum target, GLenum pname, GLfloat *params); +GLAPI void APIENTRY glGetTextureParameterivEXT (GLuint texture, GLenum target, GLenum pname, GLint *params); +GLAPI void APIENTRY glGetTextureLevelParameterfvEXT (GLuint texture, GLenum target, GLint level, GLenum pname, GLfloat *params); +GLAPI void APIENTRY glGetTextureLevelParameterivEXT (GLuint texture, GLenum target, GLint level, GLenum pname, GLint *params); +GLAPI void APIENTRY glTextureImage3DEXT (GLuint texture, GLenum target, GLint level, GLint internalformat, GLsizei width, GLsizei height, GLsizei depth, GLint border, GLenum format, GLenum type, const void *pixels); +GLAPI void APIENTRY glTextureSubImage3DEXT (GLuint texture, GLenum target, GLint level, GLint xoffset, GLint yoffset, GLint zoffset, GLsizei width, GLsizei height, GLsizei depth, GLenum format, GLenum type, const void *pixels); +GLAPI void APIENTRY glCopyTextureSubImage3DEXT (GLuint texture, GLenum target, GLint level, GLint xoffset, GLint yoffset, GLint zoffset, GLint x, GLint y, GLsizei width, GLsizei height); +GLAPI void APIENTRY glBindMultiTextureEXT (GLenum texunit, GLenum target, GLuint texture); +GLAPI void APIENTRY glMultiTexCoordPointerEXT (GLenum texunit, GLint size, GLenum type, GLsizei stride, const void *pointer); +GLAPI void APIENTRY glMultiTexEnvfEXT (GLenum texunit, GLenum target, GLenum pname, GLfloat param); +GLAPI void APIENTRY glMultiTexEnvfvEXT (GLenum texunit, GLenum target, GLenum pname, const GLfloat *params); +GLAPI void APIENTRY glMultiTexEnviEXT (GLenum texunit, GLenum target, GLenum pname, GLint param); +GLAPI void APIENTRY glMultiTexEnvivEXT (GLenum texunit, GLenum target, GLenum pname, const GLint *params); +GLAPI void APIENTRY glMultiTexGendEXT (GLenum texunit, GLenum coord, GLenum pname, GLdouble param); +GLAPI void APIENTRY glMultiTexGendvEXT (GLenum texunit, GLenum coord, GLenum pname, const GLdouble *params); +GLAPI void APIENTRY glMultiTexGenfEXT (GLenum texunit, GLenum coord, GLenum pname, GLfloat param); +GLAPI void APIENTRY glMultiTexGenfvEXT (GLenum texunit, GLenum coord, GLenum pname, const GLfloat *params); +GLAPI void APIENTRY glMultiTexGeniEXT (GLenum texunit, GLenum coord, GLenum pname, GLint param); +GLAPI void APIENTRY glMultiTexGenivEXT (GLenum texunit, GLenum coord, GLenum pname, const GLint *params); +GLAPI void APIENTRY glGetMultiTexEnvfvEXT (GLenum texunit, GLenum target, GLenum pname, GLfloat *params); +GLAPI void APIENTRY glGetMultiTexEnvivEXT (GLenum texunit, GLenum target, GLenum pname, GLint *params); +GLAPI void APIENTRY glGetMultiTexGendvEXT (GLenum texunit, GLenum coord, GLenum pname, GLdouble *params); +GLAPI void APIENTRY glGetMultiTexGenfvEXT (GLenum texunit, GLenum coord, GLenum pname, GLfloat *params); +GLAPI void APIENTRY glGetMultiTexGenivEXT (GLenum texunit, GLenum coord, GLenum pname, GLint *params); +GLAPI void APIENTRY glMultiTexParameteriEXT (GLenum texunit, GLenum target, GLenum pname, GLint param); +GLAPI void APIENTRY glMultiTexParameterivEXT (GLenum texunit, GLenum target, GLenum pname, const GLint *params); +GLAPI void APIENTRY glMultiTexParameterfEXT (GLenum texunit, GLenum target, GLenum pname, GLfloat param); +GLAPI void APIENTRY glMultiTexParameterfvEXT (GLenum texunit, GLenum target, GLenum pname, const GLfloat *params); +GLAPI void APIENTRY glMultiTexImage1DEXT (GLenum texunit, GLenum target, GLint level, GLint internalformat, GLsizei width, GLint border, GLenum format, GLenum type, const void *pixels); +GLAPI void APIENTRY glMultiTexImage2DEXT (GLenum texunit, GLenum target, GLint level, GLint internalformat, GLsizei width, GLsizei height, GLint border, GLenum format, GLenum type, const void *pixels); +GLAPI void APIENTRY glMultiTexSubImage1DEXT (GLenum texunit, GLenum target, GLint level, GLint xoffset, GLsizei width, GLenum format, GLenum type, const void *pixels); +GLAPI void APIENTRY glMultiTexSubImage2DEXT (GLenum texunit, GLenum target, GLint level, GLint xoffset, GLint yoffset, GLsizei width, GLsizei height, GLenum format, GLenum type, const void *pixels); +GLAPI void APIENTRY glCopyMultiTexImage1DEXT (GLenum texunit, GLenum target, GLint level, GLenum internalformat, GLint x, GLint y, GLsizei width, GLint border); +GLAPI void APIENTRY glCopyMultiTexImage2DEXT (GLenum texunit, GLenum target, GLint level, GLenum internalformat, GLint x, GLint y, GLsizei width, GLsizei height, GLint border); +GLAPI void APIENTRY glCopyMultiTexSubImage1DEXT (GLenum texunit, GLenum target, GLint level, GLint xoffset, GLint x, GLint y, GLsizei width); +GLAPI void APIENTRY glCopyMultiTexSubImage2DEXT (GLenum texunit, GLenum target, GLint level, GLint xoffset, GLint yoffset, GLint x, GLint y, GLsizei width, GLsizei height); +GLAPI void APIENTRY glGetMultiTexImageEXT (GLenum texunit, GLenum target, GLint level, GLenum format, GLenum type, void *pixels); +GLAPI void APIENTRY glGetMultiTexParameterfvEXT (GLenum texunit, GLenum target, GLenum pname, GLfloat *params); +GLAPI void APIENTRY glGetMultiTexParameterivEXT (GLenum texunit, GLenum target, GLenum pname, GLint *params); +GLAPI void APIENTRY glGetMultiTexLevelParameterfvEXT (GLenum texunit, GLenum target, GLint level, GLenum pname, GLfloat *params); +GLAPI void APIENTRY glGetMultiTexLevelParameterivEXT (GLenum texunit, GLenum target, GLint level, GLenum pname, GLint *params); +GLAPI void APIENTRY glMultiTexImage3DEXT (GLenum texunit, GLenum target, GLint level, GLint internalformat, GLsizei width, GLsizei height, GLsizei depth, GLint border, GLenum format, GLenum type, const void *pixels); +GLAPI void APIENTRY glMultiTexSubImage3DEXT (GLenum texunit, GLenum target, GLint level, GLint xoffset, GLint yoffset, GLint zoffset, GLsizei width, GLsizei height, GLsizei depth, GLenum format, GLenum type, const void *pixels); +GLAPI void APIENTRY glCopyMultiTexSubImage3DEXT (GLenum texunit, GLenum target, GLint level, GLint xoffset, GLint yoffset, GLint zoffset, GLint x, GLint y, GLsizei width, GLsizei height); +GLAPI void APIENTRY glEnableClientStateIndexedEXT (GLenum array, GLuint index); +GLAPI void APIENTRY glDisableClientStateIndexedEXT (GLenum array, GLuint index); +GLAPI void APIENTRY glGetFloatIndexedvEXT (GLenum target, GLuint index, GLfloat *data); +GLAPI void APIENTRY glGetDoubleIndexedvEXT (GLenum target, GLuint index, GLdouble *data); +GLAPI void APIENTRY glGetPointerIndexedvEXT (GLenum target, GLuint index, void **data); +GLAPI void APIENTRY glEnableIndexedEXT (GLenum target, GLuint index); +GLAPI void APIENTRY glDisableIndexedEXT (GLenum target, GLuint index); +GLAPI GLboolean APIENTRY glIsEnabledIndexedEXT (GLenum target, GLuint index); +GLAPI void APIENTRY glGetIntegerIndexedvEXT (GLenum target, GLuint index, GLint *data); +GLAPI void APIENTRY glGetBooleanIndexedvEXT (GLenum target, GLuint index, GLboolean *data); +GLAPI void APIENTRY glCompressedTextureImage3DEXT (GLuint texture, GLenum target, GLint level, GLenum internalformat, GLsizei width, GLsizei height, GLsizei depth, GLint border, GLsizei imageSize, const void *bits); +GLAPI void APIENTRY glCompressedTextureImage2DEXT (GLuint texture, GLenum target, GLint level, GLenum internalformat, GLsizei width, GLsizei height, GLint border, GLsizei imageSize, const void *bits); +GLAPI void APIENTRY glCompressedTextureImage1DEXT (GLuint texture, GLenum target, GLint level, GLenum internalformat, GLsizei width, GLint border, GLsizei imageSize, const void *bits); +GLAPI void APIENTRY glCompressedTextureSubImage3DEXT (GLuint texture, GLenum target, GLint level, GLint xoffset, GLint yoffset, GLint zoffset, GLsizei width, GLsizei height, GLsizei depth, GLenum format, GLsizei imageSize, const void *bits); +GLAPI void APIENTRY glCompressedTextureSubImage2DEXT (GLuint texture, GLenum target, GLint level, GLint xoffset, GLint yoffset, GLsizei width, GLsizei height, GLenum format, GLsizei imageSize, const void *bits); +GLAPI void APIENTRY glCompressedTextureSubImage1DEXT (GLuint texture, GLenum target, GLint level, GLint xoffset, GLsizei width, GLenum format, GLsizei imageSize, const void *bits); +GLAPI void APIENTRY glGetCompressedTextureImageEXT (GLuint texture, GLenum target, GLint lod, void *img); +GLAPI void APIENTRY glCompressedMultiTexImage3DEXT (GLenum texunit, GLenum target, GLint level, GLenum internalformat, GLsizei width, GLsizei height, GLsizei depth, GLint border, GLsizei imageSize, const void *bits); +GLAPI void APIENTRY glCompressedMultiTexImage2DEXT (GLenum texunit, GLenum target, GLint level, GLenum internalformat, GLsizei width, GLsizei height, GLint border, GLsizei imageSize, const void *bits); +GLAPI void APIENTRY glCompressedMultiTexImage1DEXT (GLenum texunit, GLenum target, GLint level, GLenum internalformat, GLsizei width, GLint border, GLsizei imageSize, const void *bits); +GLAPI void APIENTRY glCompressedMultiTexSubImage3DEXT (GLenum texunit, GLenum target, GLint level, GLint xoffset, GLint yoffset, GLint zoffset, GLsizei width, GLsizei height, GLsizei depth, GLenum format, GLsizei imageSize, const void *bits); +GLAPI void APIENTRY glCompressedMultiTexSubImage2DEXT (GLenum texunit, GLenum target, GLint level, GLint xoffset, GLint yoffset, GLsizei width, GLsizei height, GLenum format, GLsizei imageSize, const void *bits); +GLAPI void APIENTRY glCompressedMultiTexSubImage1DEXT (GLenum texunit, GLenum target, GLint level, GLint xoffset, GLsizei width, GLenum format, GLsizei imageSize, const void *bits); +GLAPI void APIENTRY glGetCompressedMultiTexImageEXT (GLenum texunit, GLenum target, GLint lod, void *img); +GLAPI void APIENTRY glMatrixLoadTransposefEXT (GLenum mode, const GLfloat *m); +GLAPI void APIENTRY glMatrixLoadTransposedEXT (GLenum mode, const GLdouble *m); +GLAPI void APIENTRY glMatrixMultTransposefEXT (GLenum mode, const GLfloat *m); +GLAPI void APIENTRY glMatrixMultTransposedEXT (GLenum mode, const GLdouble *m); +GLAPI void APIENTRY glNamedBufferDataEXT (GLuint buffer, GLsizeiptr size, const void *data, GLenum usage); +GLAPI void APIENTRY glNamedBufferSubDataEXT (GLuint buffer, GLintptr offset, GLsizeiptr size, const void *data); +GLAPI void *APIENTRY glMapNamedBufferEXT (GLuint buffer, GLenum access); +GLAPI GLboolean APIENTRY glUnmapNamedBufferEXT (GLuint buffer); +GLAPI void APIENTRY glGetNamedBufferParameterivEXT (GLuint buffer, GLenum pname, GLint *params); +GLAPI void APIENTRY glGetNamedBufferPointervEXT (GLuint buffer, GLenum pname, void **params); +GLAPI void APIENTRY glGetNamedBufferSubDataEXT (GLuint buffer, GLintptr offset, GLsizeiptr size, void *data); +GLAPI void APIENTRY glProgramUniform1fEXT (GLuint program, GLint location, GLfloat v0); +GLAPI void APIENTRY glProgramUniform2fEXT (GLuint program, GLint location, GLfloat v0, GLfloat v1); +GLAPI void APIENTRY glProgramUniform3fEXT (GLuint program, GLint location, GLfloat v0, GLfloat v1, GLfloat v2); +GLAPI void APIENTRY glProgramUniform4fEXT (GLuint program, GLint location, GLfloat v0, GLfloat v1, GLfloat v2, GLfloat v3); +GLAPI void APIENTRY glProgramUniform1iEXT (GLuint program, GLint location, GLint v0); +GLAPI void APIENTRY glProgramUniform2iEXT (GLuint program, GLint location, GLint v0, GLint v1); +GLAPI void APIENTRY glProgramUniform3iEXT (GLuint program, GLint location, GLint v0, GLint v1, GLint v2); +GLAPI void APIENTRY glProgramUniform4iEXT (GLuint program, GLint location, GLint v0, GLint v1, GLint v2, GLint v3); +GLAPI void APIENTRY glProgramUniform1fvEXT (GLuint program, GLint location, GLsizei count, const GLfloat *value); +GLAPI void APIENTRY glProgramUniform2fvEXT (GLuint program, GLint location, GLsizei count, const GLfloat *value); +GLAPI void APIENTRY glProgramUniform3fvEXT (GLuint program, GLint location, GLsizei count, const GLfloat *value); +GLAPI void APIENTRY glProgramUniform4fvEXT (GLuint program, GLint location, GLsizei count, const GLfloat *value); +GLAPI void APIENTRY glProgramUniform1ivEXT (GLuint program, GLint location, GLsizei count, const GLint *value); +GLAPI void APIENTRY glProgramUniform2ivEXT (GLuint program, GLint location, GLsizei count, const GLint *value); +GLAPI void APIENTRY glProgramUniform3ivEXT (GLuint program, GLint location, GLsizei count, const GLint *value); +GLAPI void APIENTRY glProgramUniform4ivEXT (GLuint program, GLint location, GLsizei count, const GLint *value); +GLAPI void APIENTRY glProgramUniformMatrix2fvEXT (GLuint program, GLint location, GLsizei count, GLboolean transpose, const GLfloat *value); +GLAPI void APIENTRY glProgramUniformMatrix3fvEXT (GLuint program, GLint location, GLsizei count, GLboolean transpose, const GLfloat *value); +GLAPI void APIENTRY glProgramUniformMatrix4fvEXT (GLuint program, GLint location, GLsizei count, GLboolean transpose, const GLfloat *value); +GLAPI void APIENTRY glProgramUniformMatrix2x3fvEXT (GLuint program, GLint location, GLsizei count, GLboolean transpose, const GLfloat *value); +GLAPI void APIENTRY glProgramUniformMatrix3x2fvEXT (GLuint program, GLint location, GLsizei count, GLboolean transpose, const GLfloat *value); +GLAPI void APIENTRY glProgramUniformMatrix2x4fvEXT (GLuint program, GLint location, GLsizei count, GLboolean transpose, const GLfloat *value); +GLAPI void APIENTRY glProgramUniformMatrix4x2fvEXT (GLuint program, GLint location, GLsizei count, GLboolean transpose, const GLfloat *value); +GLAPI void APIENTRY glProgramUniformMatrix3x4fvEXT (GLuint program, GLint location, GLsizei count, GLboolean transpose, const GLfloat *value); +GLAPI void APIENTRY glProgramUniformMatrix4x3fvEXT (GLuint program, GLint location, GLsizei count, GLboolean transpose, const GLfloat *value); +GLAPI void APIENTRY glTextureBufferEXT (GLuint texture, GLenum target, GLenum internalformat, GLuint buffer); +GLAPI void APIENTRY glMultiTexBufferEXT (GLenum texunit, GLenum target, GLenum internalformat, GLuint buffer); +GLAPI void APIENTRY glTextureParameterIivEXT (GLuint texture, GLenum target, GLenum pname, const GLint *params); +GLAPI void APIENTRY glTextureParameterIuivEXT (GLuint texture, GLenum target, GLenum pname, const GLuint *params); +GLAPI void APIENTRY glGetTextureParameterIivEXT (GLuint texture, GLenum target, GLenum pname, GLint *params); +GLAPI void APIENTRY glGetTextureParameterIuivEXT (GLuint texture, GLenum target, GLenum pname, GLuint *params); +GLAPI void APIENTRY glMultiTexParameterIivEXT (GLenum texunit, GLenum target, GLenum pname, const GLint *params); +GLAPI void APIENTRY glMultiTexParameterIuivEXT (GLenum texunit, GLenum target, GLenum pname, const GLuint *params); +GLAPI void APIENTRY glGetMultiTexParameterIivEXT (GLenum texunit, GLenum target, GLenum pname, GLint *params); +GLAPI void APIENTRY glGetMultiTexParameterIuivEXT (GLenum texunit, GLenum target, GLenum pname, GLuint *params); +GLAPI void APIENTRY glProgramUniform1uiEXT (GLuint program, GLint location, GLuint v0); +GLAPI void APIENTRY glProgramUniform2uiEXT (GLuint program, GLint location, GLuint v0, GLuint v1); +GLAPI void APIENTRY glProgramUniform3uiEXT (GLuint program, GLint location, GLuint v0, GLuint v1, GLuint v2); +GLAPI void APIENTRY glProgramUniform4uiEXT (GLuint program, GLint location, GLuint v0, GLuint v1, GLuint v2, GLuint v3); +GLAPI void APIENTRY glProgramUniform1uivEXT (GLuint program, GLint location, GLsizei count, const GLuint *value); +GLAPI void APIENTRY glProgramUniform2uivEXT (GLuint program, GLint location, GLsizei count, const GLuint *value); +GLAPI void APIENTRY glProgramUniform3uivEXT (GLuint program, GLint location, GLsizei count, const GLuint *value); +GLAPI void APIENTRY glProgramUniform4uivEXT (GLuint program, GLint location, GLsizei count, const GLuint *value); +GLAPI void APIENTRY glNamedProgramLocalParameters4fvEXT (GLuint program, GLenum target, GLuint index, GLsizei count, const GLfloat *params); +GLAPI void APIENTRY glNamedProgramLocalParameterI4iEXT (GLuint program, GLenum target, GLuint index, GLint x, GLint y, GLint z, GLint w); +GLAPI void APIENTRY glNamedProgramLocalParameterI4ivEXT (GLuint program, GLenum target, GLuint index, const GLint *params); +GLAPI void APIENTRY glNamedProgramLocalParametersI4ivEXT (GLuint program, GLenum target, GLuint index, GLsizei count, const GLint *params); +GLAPI void APIENTRY glNamedProgramLocalParameterI4uiEXT (GLuint program, GLenum target, GLuint index, GLuint x, GLuint y, GLuint z, GLuint w); +GLAPI void APIENTRY glNamedProgramLocalParameterI4uivEXT (GLuint program, GLenum target, GLuint index, const GLuint *params); +GLAPI void APIENTRY glNamedProgramLocalParametersI4uivEXT (GLuint program, GLenum target, GLuint index, GLsizei count, const GLuint *params); +GLAPI void APIENTRY glGetNamedProgramLocalParameterIivEXT (GLuint program, GLenum target, GLuint index, GLint *params); +GLAPI void APIENTRY glGetNamedProgramLocalParameterIuivEXT (GLuint program, GLenum target, GLuint index, GLuint *params); +GLAPI void APIENTRY glEnableClientStateiEXT (GLenum array, GLuint index); +GLAPI void APIENTRY glDisableClientStateiEXT (GLenum array, GLuint index); +GLAPI void APIENTRY glGetFloati_vEXT (GLenum pname, GLuint index, GLfloat *params); +GLAPI void APIENTRY glGetDoublei_vEXT (GLenum pname, GLuint index, GLdouble *params); +GLAPI void APIENTRY glGetPointeri_vEXT (GLenum pname, GLuint index, void **params); +GLAPI void APIENTRY glNamedProgramStringEXT (GLuint program, GLenum target, GLenum format, GLsizei len, const void *string); +GLAPI void APIENTRY glNamedProgramLocalParameter4dEXT (GLuint program, GLenum target, GLuint index, GLdouble x, GLdouble y, GLdouble z, GLdouble w); +GLAPI void APIENTRY glNamedProgramLocalParameter4dvEXT (GLuint program, GLenum target, GLuint index, const GLdouble *params); +GLAPI void APIENTRY glNamedProgramLocalParameter4fEXT (GLuint program, GLenum target, GLuint index, GLfloat x, GLfloat y, GLfloat z, GLfloat w); +GLAPI void APIENTRY glNamedProgramLocalParameter4fvEXT (GLuint program, GLenum target, GLuint index, const GLfloat *params); +GLAPI void APIENTRY glGetNamedProgramLocalParameterdvEXT (GLuint program, GLenum target, GLuint index, GLdouble *params); +GLAPI void APIENTRY glGetNamedProgramLocalParameterfvEXT (GLuint program, GLenum target, GLuint index, GLfloat *params); +GLAPI void APIENTRY glGetNamedProgramivEXT (GLuint program, GLenum target, GLenum pname, GLint *params); +GLAPI void APIENTRY glGetNamedProgramStringEXT (GLuint program, GLenum target, GLenum pname, void *string); +GLAPI void APIENTRY glNamedRenderbufferStorageEXT (GLuint renderbuffer, GLenum internalformat, GLsizei width, GLsizei height); +GLAPI void APIENTRY glGetNamedRenderbufferParameterivEXT (GLuint renderbuffer, GLenum pname, GLint *params); +GLAPI void APIENTRY glNamedRenderbufferStorageMultisampleEXT (GLuint renderbuffer, GLsizei samples, GLenum internalformat, GLsizei width, GLsizei height); +GLAPI void APIENTRY glNamedRenderbufferStorageMultisampleCoverageEXT (GLuint renderbuffer, GLsizei coverageSamples, GLsizei colorSamples, GLenum internalformat, GLsizei width, GLsizei height); +GLAPI GLenum APIENTRY glCheckNamedFramebufferStatusEXT (GLuint framebuffer, GLenum target); +GLAPI void APIENTRY glNamedFramebufferTexture1DEXT (GLuint framebuffer, GLenum attachment, GLenum textarget, GLuint texture, GLint level); +GLAPI void APIENTRY glNamedFramebufferTexture2DEXT (GLuint framebuffer, GLenum attachment, GLenum textarget, GLuint texture, GLint level); +GLAPI void APIENTRY glNamedFramebufferTexture3DEXT (GLuint framebuffer, GLenum attachment, GLenum textarget, GLuint texture, GLint level, GLint zoffset); +GLAPI void APIENTRY glNamedFramebufferRenderbufferEXT (GLuint framebuffer, GLenum attachment, GLenum renderbuffertarget, GLuint renderbuffer); +GLAPI void APIENTRY glGetNamedFramebufferAttachmentParameterivEXT (GLuint framebuffer, GLenum attachment, GLenum pname, GLint *params); +GLAPI void APIENTRY glGenerateTextureMipmapEXT (GLuint texture, GLenum target); +GLAPI void APIENTRY glGenerateMultiTexMipmapEXT (GLenum texunit, GLenum target); +GLAPI void APIENTRY glFramebufferDrawBufferEXT (GLuint framebuffer, GLenum mode); +GLAPI void APIENTRY glFramebufferDrawBuffersEXT (GLuint framebuffer, GLsizei n, const GLenum *bufs); +GLAPI void APIENTRY glFramebufferReadBufferEXT (GLuint framebuffer, GLenum mode); +GLAPI void APIENTRY glGetFramebufferParameterivEXT (GLuint framebuffer, GLenum pname, GLint *params); +GLAPI void APIENTRY glNamedCopyBufferSubDataEXT (GLuint readBuffer, GLuint writeBuffer, GLintptr readOffset, GLintptr writeOffset, GLsizeiptr size); +GLAPI void APIENTRY glNamedFramebufferTextureEXT (GLuint framebuffer, GLenum attachment, GLuint texture, GLint level); +GLAPI void APIENTRY glNamedFramebufferTextureLayerEXT (GLuint framebuffer, GLenum attachment, GLuint texture, GLint level, GLint layer); +GLAPI void APIENTRY glNamedFramebufferTextureFaceEXT (GLuint framebuffer, GLenum attachment, GLuint texture, GLint level, GLenum face); +GLAPI void APIENTRY glTextureRenderbufferEXT (GLuint texture, GLenum target, GLuint renderbuffer); +GLAPI void APIENTRY glMultiTexRenderbufferEXT (GLenum texunit, GLenum target, GLuint renderbuffer); +GLAPI void APIENTRY glVertexArrayVertexOffsetEXT (GLuint vaobj, GLuint buffer, GLint size, GLenum type, GLsizei stride, GLintptr offset); +GLAPI void APIENTRY glVertexArrayColorOffsetEXT (GLuint vaobj, GLuint buffer, GLint size, GLenum type, GLsizei stride, GLintptr offset); +GLAPI void APIENTRY glVertexArrayEdgeFlagOffsetEXT (GLuint vaobj, GLuint buffer, GLsizei stride, GLintptr offset); +GLAPI void APIENTRY glVertexArrayIndexOffsetEXT (GLuint vaobj, GLuint buffer, GLenum type, GLsizei stride, GLintptr offset); +GLAPI void APIENTRY glVertexArrayNormalOffsetEXT (GLuint vaobj, GLuint buffer, GLenum type, GLsizei stride, GLintptr offset); +GLAPI void APIENTRY glVertexArrayTexCoordOffsetEXT (GLuint vaobj, GLuint buffer, GLint size, GLenum type, GLsizei stride, GLintptr offset); +GLAPI void APIENTRY glVertexArrayMultiTexCoordOffsetEXT (GLuint vaobj, GLuint buffer, GLenum texunit, GLint size, GLenum type, GLsizei stride, GLintptr offset); +GLAPI void APIENTRY glVertexArrayFogCoordOffsetEXT (GLuint vaobj, GLuint buffer, GLenum type, GLsizei stride, GLintptr offset); +GLAPI void APIENTRY glVertexArraySecondaryColorOffsetEXT (GLuint vaobj, GLuint buffer, GLint size, GLenum type, GLsizei stride, GLintptr offset); +GLAPI void APIENTRY glVertexArrayVertexAttribOffsetEXT (GLuint vaobj, GLuint buffer, GLuint index, GLint size, GLenum type, GLboolean normalized, GLsizei stride, GLintptr offset); +GLAPI void APIENTRY glVertexArrayVertexAttribIOffsetEXT (GLuint vaobj, GLuint buffer, GLuint index, GLint size, GLenum type, GLsizei stride, GLintptr offset); +GLAPI void APIENTRY glEnableVertexArrayEXT (GLuint vaobj, GLenum array); +GLAPI void APIENTRY glDisableVertexArrayEXT (GLuint vaobj, GLenum array); +GLAPI void APIENTRY glEnableVertexArrayAttribEXT (GLuint vaobj, GLuint index); +GLAPI void APIENTRY glDisableVertexArrayAttribEXT (GLuint vaobj, GLuint index); +GLAPI void APIENTRY glGetVertexArrayIntegervEXT (GLuint vaobj, GLenum pname, GLint *param); +GLAPI void APIENTRY glGetVertexArrayPointervEXT (GLuint vaobj, GLenum pname, void **param); +GLAPI void APIENTRY glGetVertexArrayIntegeri_vEXT (GLuint vaobj, GLuint index, GLenum pname, GLint *param); +GLAPI void APIENTRY glGetVertexArrayPointeri_vEXT (GLuint vaobj, GLuint index, GLenum pname, void **param); +GLAPI void *APIENTRY glMapNamedBufferRangeEXT (GLuint buffer, GLintptr offset, GLsizeiptr length, GLbitfield access); +GLAPI void APIENTRY glFlushMappedNamedBufferRangeEXT (GLuint buffer, GLintptr offset, GLsizeiptr length); +GLAPI void APIENTRY glNamedBufferStorageEXT (GLuint buffer, GLsizeiptr size, const void *data, GLbitfield flags); +GLAPI void APIENTRY glClearNamedBufferDataEXT (GLuint buffer, GLenum internalformat, GLenum format, GLenum type, const void *data); +GLAPI void APIENTRY glClearNamedBufferSubDataEXT (GLuint buffer, GLenum internalformat, GLsizeiptr offset, GLsizeiptr size, GLenum format, GLenum type, const void *data); +GLAPI void APIENTRY glNamedFramebufferParameteriEXT (GLuint framebuffer, GLenum pname, GLint param); +GLAPI void APIENTRY glGetNamedFramebufferParameterivEXT (GLuint framebuffer, GLenum pname, GLint *params); +GLAPI void APIENTRY glProgramUniform1dEXT (GLuint program, GLint location, GLdouble x); +GLAPI void APIENTRY glProgramUniform2dEXT (GLuint program, GLint location, GLdouble x, GLdouble y); +GLAPI void APIENTRY glProgramUniform3dEXT (GLuint program, GLint location, GLdouble x, GLdouble y, GLdouble z); +GLAPI void APIENTRY glProgramUniform4dEXT (GLuint program, GLint location, GLdouble x, GLdouble y, GLdouble z, GLdouble w); +GLAPI void APIENTRY glProgramUniform1dvEXT (GLuint program, GLint location, GLsizei count, const GLdouble *value); +GLAPI void APIENTRY glProgramUniform2dvEXT (GLuint program, GLint location, GLsizei count, const GLdouble *value); +GLAPI void APIENTRY glProgramUniform3dvEXT (GLuint program, GLint location, GLsizei count, const GLdouble *value); +GLAPI void APIENTRY glProgramUniform4dvEXT (GLuint program, GLint location, GLsizei count, const GLdouble *value); +GLAPI void APIENTRY glProgramUniformMatrix2dvEXT (GLuint program, GLint location, GLsizei count, GLboolean transpose, const GLdouble *value); +GLAPI void APIENTRY glProgramUniformMatrix3dvEXT (GLuint program, GLint location, GLsizei count, GLboolean transpose, const GLdouble *value); +GLAPI void APIENTRY glProgramUniformMatrix4dvEXT (GLuint program, GLint location, GLsizei count, GLboolean transpose, const GLdouble *value); +GLAPI void APIENTRY glProgramUniformMatrix2x3dvEXT (GLuint program, GLint location, GLsizei count, GLboolean transpose, const GLdouble *value); +GLAPI void APIENTRY glProgramUniformMatrix2x4dvEXT (GLuint program, GLint location, GLsizei count, GLboolean transpose, const GLdouble *value); +GLAPI void APIENTRY glProgramUniformMatrix3x2dvEXT (GLuint program, GLint location, GLsizei count, GLboolean transpose, const GLdouble *value); +GLAPI void APIENTRY glProgramUniformMatrix3x4dvEXT (GLuint program, GLint location, GLsizei count, GLboolean transpose, const GLdouble *value); +GLAPI void APIENTRY glProgramUniformMatrix4x2dvEXT (GLuint program, GLint location, GLsizei count, GLboolean transpose, const GLdouble *value); +GLAPI void APIENTRY glProgramUniformMatrix4x3dvEXT (GLuint program, GLint location, GLsizei count, GLboolean transpose, const GLdouble *value); +GLAPI void APIENTRY glTextureBufferRangeEXT (GLuint texture, GLenum target, GLenum internalformat, GLuint buffer, GLintptr offset, GLsizeiptr size); +GLAPI void APIENTRY glTextureStorage1DEXT (GLuint texture, GLenum target, GLsizei levels, GLenum internalformat, GLsizei width); +GLAPI void APIENTRY glTextureStorage2DEXT (GLuint texture, GLenum target, GLsizei levels, GLenum internalformat, GLsizei width, GLsizei height); +GLAPI void APIENTRY glTextureStorage3DEXT (GLuint texture, GLenum target, GLsizei levels, GLenum internalformat, GLsizei width, GLsizei height, GLsizei depth); +GLAPI void APIENTRY glTextureStorage2DMultisampleEXT (GLuint texture, GLenum target, GLsizei samples, GLenum internalformat, GLsizei width, GLsizei height, GLboolean fixedsamplelocations); +GLAPI void APIENTRY glTextureStorage3DMultisampleEXT (GLuint texture, GLenum target, GLsizei samples, GLenum internalformat, GLsizei width, GLsizei height, GLsizei depth, GLboolean fixedsamplelocations); +GLAPI void APIENTRY glVertexArrayBindVertexBufferEXT (GLuint vaobj, GLuint bindingindex, GLuint buffer, GLintptr offset, GLsizei stride); +GLAPI void APIENTRY glVertexArrayVertexAttribFormatEXT (GLuint vaobj, GLuint attribindex, GLint size, GLenum type, GLboolean normalized, GLuint relativeoffset); +GLAPI void APIENTRY glVertexArrayVertexAttribIFormatEXT (GLuint vaobj, GLuint attribindex, GLint size, GLenum type, GLuint relativeoffset); +GLAPI void APIENTRY glVertexArrayVertexAttribLFormatEXT (GLuint vaobj, GLuint attribindex, GLint size, GLenum type, GLuint relativeoffset); +GLAPI void APIENTRY glVertexArrayVertexAttribBindingEXT (GLuint vaobj, GLuint attribindex, GLuint bindingindex); +GLAPI void APIENTRY glVertexArrayVertexBindingDivisorEXT (GLuint vaobj, GLuint bindingindex, GLuint divisor); +GLAPI void APIENTRY glVertexArrayVertexAttribLOffsetEXT (GLuint vaobj, GLuint buffer, GLuint index, GLint size, GLenum type, GLsizei stride, GLintptr offset); +GLAPI void APIENTRY glTexturePageCommitmentEXT (GLuint texture, GLint level, GLint xoffset, GLint yoffset, GLint zoffset, GLsizei width, GLsizei height, GLsizei depth, GLboolean resident); +GLAPI void APIENTRY glVertexArrayVertexAttribDivisorEXT (GLuint vaobj, GLuint index, GLuint divisor); +#endif +#endif /* GL_EXT_direct_state_access */ + +#ifndef GL_EXT_draw_buffers2 +#define GL_EXT_draw_buffers2 1 +typedef void (APIENTRYP PFNGLCOLORMASKINDEXEDEXTPROC) (GLuint index, GLboolean r, GLboolean g, GLboolean b, GLboolean a); +#ifdef GL_GLEXT_PROTOTYPES +GLAPI void APIENTRY glColorMaskIndexedEXT (GLuint index, GLboolean r, GLboolean g, GLboolean b, GLboolean a); +#endif +#endif /* GL_EXT_draw_buffers2 */ + +#ifndef GL_EXT_draw_instanced +#define GL_EXT_draw_instanced 1 +typedef void (APIENTRYP PFNGLDRAWARRAYSINSTANCEDEXTPROC) (GLenum mode, GLint start, GLsizei count, GLsizei primcount); +typedef void (APIENTRYP PFNGLDRAWELEMENTSINSTANCEDEXTPROC) (GLenum mode, GLsizei count, GLenum type, const void *indices, GLsizei primcount); +#ifdef GL_GLEXT_PROTOTYPES +GLAPI void APIENTRY glDrawArraysInstancedEXT (GLenum mode, GLint start, GLsizei count, GLsizei primcount); +GLAPI void APIENTRY glDrawElementsInstancedEXT (GLenum mode, GLsizei count, GLenum type, const void *indices, GLsizei primcount); +#endif +#endif /* GL_EXT_draw_instanced */ + +#ifndef GL_EXT_draw_range_elements +#define GL_EXT_draw_range_elements 1 +#define GL_MAX_ELEMENTS_VERTICES_EXT 0x80E8 +#define GL_MAX_ELEMENTS_INDICES_EXT 0x80E9 +typedef void (APIENTRYP PFNGLDRAWRANGEELEMENTSEXTPROC) (GLenum mode, GLuint start, GLuint end, GLsizei count, GLenum type, const void *indices); +#ifdef GL_GLEXT_PROTOTYPES +GLAPI void APIENTRY glDrawRangeElementsEXT (GLenum mode, GLuint start, GLuint end, GLsizei count, GLenum type, const void *indices); +#endif +#endif /* GL_EXT_draw_range_elements */ + +#ifndef GL_EXT_fog_coord +#define GL_EXT_fog_coord 1 +#define GL_FOG_COORDINATE_SOURCE_EXT 0x8450 +#define GL_FOG_COORDINATE_EXT 0x8451 +#define GL_FRAGMENT_DEPTH_EXT 0x8452 +#define GL_CURRENT_FOG_COORDINATE_EXT 0x8453 +#define GL_FOG_COORDINATE_ARRAY_TYPE_EXT 0x8454 +#define GL_FOG_COORDINATE_ARRAY_STRIDE_EXT 0x8455 +#define GL_FOG_COORDINATE_ARRAY_POINTER_EXT 0x8456 +#define GL_FOG_COORDINATE_ARRAY_EXT 0x8457 +typedef void (APIENTRYP PFNGLFOGCOORDFEXTPROC) (GLfloat coord); +typedef void (APIENTRYP PFNGLFOGCOORDFVEXTPROC) (const GLfloat *coord); +typedef void (APIENTRYP PFNGLFOGCOORDDEXTPROC) (GLdouble coord); +typedef void (APIENTRYP PFNGLFOGCOORDDVEXTPROC) (const GLdouble *coord); +typedef void (APIENTRYP PFNGLFOGCOORDPOINTEREXTPROC) (GLenum type, GLsizei stride, const void *pointer); +#ifdef GL_GLEXT_PROTOTYPES +GLAPI void APIENTRY glFogCoordfEXT (GLfloat coord); +GLAPI void APIENTRY glFogCoordfvEXT (const GLfloat *coord); +GLAPI void APIENTRY glFogCoorddEXT (GLdouble coord); +GLAPI void APIENTRY glFogCoorddvEXT (const GLdouble *coord); +GLAPI void APIENTRY glFogCoordPointerEXT (GLenum type, GLsizei stride, const void *pointer); +#endif +#endif /* GL_EXT_fog_coord */ + +#ifndef GL_EXT_framebuffer_blit +#define GL_EXT_framebuffer_blit 1 +#define GL_READ_FRAMEBUFFER_EXT 0x8CA8 +#define GL_DRAW_FRAMEBUFFER_EXT 0x8CA9 +#define GL_DRAW_FRAMEBUFFER_BINDING_EXT 0x8CA6 +#define GL_READ_FRAMEBUFFER_BINDING_EXT 0x8CAA +typedef void (APIENTRYP PFNGLBLITFRAMEBUFFEREXTPROC) (GLint srcX0, GLint srcY0, GLint srcX1, GLint srcY1, GLint dstX0, GLint dstY0, GLint dstX1, GLint dstY1, GLbitfield mask, GLenum filter); +#ifdef GL_GLEXT_PROTOTYPES +GLAPI void APIENTRY glBlitFramebufferEXT (GLint srcX0, GLint srcY0, GLint srcX1, GLint srcY1, GLint dstX0, GLint dstY0, GLint dstX1, GLint dstY1, GLbitfield mask, GLenum filter); +#endif +#endif /* GL_EXT_framebuffer_blit */ + +#ifndef GL_EXT_framebuffer_multisample +#define GL_EXT_framebuffer_multisample 1 +#define GL_RENDERBUFFER_SAMPLES_EXT 0x8CAB +#define GL_FRAMEBUFFER_INCOMPLETE_MULTISAMPLE_EXT 0x8D56 +#define GL_MAX_SAMPLES_EXT 0x8D57 +typedef void (APIENTRYP PFNGLRENDERBUFFERSTORAGEMULTISAMPLEEXTPROC) (GLenum target, GLsizei samples, GLenum internalformat, GLsizei width, GLsizei height); +#ifdef GL_GLEXT_PROTOTYPES +GLAPI void APIENTRY glRenderbufferStorageMultisampleEXT (GLenum target, GLsizei samples, GLenum internalformat, GLsizei width, GLsizei height); +#endif +#endif /* GL_EXT_framebuffer_multisample */ + +#ifndef GL_EXT_framebuffer_multisample_blit_scaled +#define GL_EXT_framebuffer_multisample_blit_scaled 1 +#define GL_SCALED_RESOLVE_FASTEST_EXT 0x90BA +#define GL_SCALED_RESOLVE_NICEST_EXT 0x90BB +#endif /* GL_EXT_framebuffer_multisample_blit_scaled */ + +#ifndef GL_EXT_framebuffer_object +#define GL_EXT_framebuffer_object 1 +#define GL_INVALID_FRAMEBUFFER_OPERATION_EXT 0x0506 +#define GL_MAX_RENDERBUFFER_SIZE_EXT 0x84E8 +#define GL_FRAMEBUFFER_BINDING_EXT 0x8CA6 +#define GL_RENDERBUFFER_BINDING_EXT 0x8CA7 +#define GL_FRAMEBUFFER_ATTACHMENT_OBJECT_TYPE_EXT 0x8CD0 +#define GL_FRAMEBUFFER_ATTACHMENT_OBJECT_NAME_EXT 0x8CD1 +#define GL_FRAMEBUFFER_ATTACHMENT_TEXTURE_LEVEL_EXT 0x8CD2 +#define GL_FRAMEBUFFER_ATTACHMENT_TEXTURE_CUBE_MAP_FACE_EXT 0x8CD3 +#define GL_FRAMEBUFFER_ATTACHMENT_TEXTURE_3D_ZOFFSET_EXT 0x8CD4 +#define GL_FRAMEBUFFER_COMPLETE_EXT 0x8CD5 +#define GL_FRAMEBUFFER_INCOMPLETE_ATTACHMENT_EXT 0x8CD6 +#define GL_FRAMEBUFFER_INCOMPLETE_MISSING_ATTACHMENT_EXT 0x8CD7 +#define GL_FRAMEBUFFER_INCOMPLETE_DIMENSIONS_EXT 0x8CD9 +#define GL_FRAMEBUFFER_INCOMPLETE_FORMATS_EXT 0x8CDA +#define GL_FRAMEBUFFER_INCOMPLETE_DRAW_BUFFER_EXT 0x8CDB +#define GL_FRAMEBUFFER_INCOMPLETE_READ_BUFFER_EXT 0x8CDC +#define GL_FRAMEBUFFER_UNSUPPORTED_EXT 0x8CDD +#define GL_MAX_COLOR_ATTACHMENTS_EXT 0x8CDF +#define GL_COLOR_ATTACHMENT0_EXT 0x8CE0 +#define GL_COLOR_ATTACHMENT1_EXT 0x8CE1 +#define GL_COLOR_ATTACHMENT2_EXT 0x8CE2 +#define GL_COLOR_ATTACHMENT3_EXT 0x8CE3 +#define GL_COLOR_ATTACHMENT4_EXT 0x8CE4 +#define GL_COLOR_ATTACHMENT5_EXT 0x8CE5 +#define GL_COLOR_ATTACHMENT6_EXT 0x8CE6 +#define GL_COLOR_ATTACHMENT7_EXT 0x8CE7 +#define GL_COLOR_ATTACHMENT8_EXT 0x8CE8 +#define GL_COLOR_ATTACHMENT9_EXT 0x8CE9 +#define GL_COLOR_ATTACHMENT10_EXT 0x8CEA +#define GL_COLOR_ATTACHMENT11_EXT 0x8CEB +#define GL_COLOR_ATTACHMENT12_EXT 0x8CEC +#define GL_COLOR_ATTACHMENT13_EXT 0x8CED +#define GL_COLOR_ATTACHMENT14_EXT 0x8CEE +#define GL_COLOR_ATTACHMENT15_EXT 0x8CEF +#define GL_DEPTH_ATTACHMENT_EXT 0x8D00 +#define GL_STENCIL_ATTACHMENT_EXT 0x8D20 +#define GL_FRAMEBUFFER_EXT 0x8D40 +#define GL_RENDERBUFFER_EXT 0x8D41 +#define GL_RENDERBUFFER_WIDTH_EXT 0x8D42 +#define GL_RENDERBUFFER_HEIGHT_EXT 0x8D43 +#define GL_RENDERBUFFER_INTERNAL_FORMAT_EXT 0x8D44 +#define GL_STENCIL_INDEX1_EXT 0x8D46 +#define GL_STENCIL_INDEX4_EXT 0x8D47 +#define GL_STENCIL_INDEX8_EXT 0x8D48 +#define GL_STENCIL_INDEX16_EXT 0x8D49 +#define GL_RENDERBUFFER_RED_SIZE_EXT 0x8D50 +#define GL_RENDERBUFFER_GREEN_SIZE_EXT 0x8D51 +#define GL_RENDERBUFFER_BLUE_SIZE_EXT 0x8D52 +#define GL_RENDERBUFFER_ALPHA_SIZE_EXT 0x8D53 +#define GL_RENDERBUFFER_DEPTH_SIZE_EXT 0x8D54 +#define GL_RENDERBUFFER_STENCIL_SIZE_EXT 0x8D55 +typedef GLboolean (APIENTRYP PFNGLISRENDERBUFFEREXTPROC) (GLuint renderbuffer); +typedef void (APIENTRYP PFNGLBINDRENDERBUFFEREXTPROC) (GLenum target, GLuint renderbuffer); +typedef void (APIENTRYP PFNGLDELETERENDERBUFFERSEXTPROC) (GLsizei n, const GLuint *renderbuffers); +typedef void (APIENTRYP PFNGLGENRENDERBUFFERSEXTPROC) (GLsizei n, GLuint *renderbuffers); +typedef void (APIENTRYP PFNGLRENDERBUFFERSTORAGEEXTPROC) (GLenum target, GLenum internalformat, GLsizei width, GLsizei height); +typedef void (APIENTRYP PFNGLGETRENDERBUFFERPARAMETERIVEXTPROC) (GLenum target, GLenum pname, GLint *params); +typedef GLboolean (APIENTRYP PFNGLISFRAMEBUFFEREXTPROC) (GLuint framebuffer); +typedef void (APIENTRYP PFNGLBINDFRAMEBUFFEREXTPROC) (GLenum target, GLuint framebuffer); +typedef void (APIENTRYP PFNGLDELETEFRAMEBUFFERSEXTPROC) (GLsizei n, const GLuint *framebuffers); +typedef void (APIENTRYP PFNGLGENFRAMEBUFFERSEXTPROC) (GLsizei n, GLuint *framebuffers); +typedef GLenum (APIENTRYP PFNGLCHECKFRAMEBUFFERSTATUSEXTPROC) (GLenum target); +typedef void (APIENTRYP PFNGLFRAMEBUFFERTEXTURE1DEXTPROC) (GLenum target, GLenum attachment, GLenum textarget, GLuint texture, GLint level); +typedef void (APIENTRYP PFNGLFRAMEBUFFERTEXTURE2DEXTPROC) (GLenum target, GLenum attachment, GLenum textarget, GLuint texture, GLint level); +typedef void (APIENTRYP PFNGLFRAMEBUFFERTEXTURE3DEXTPROC) (GLenum target, GLenum attachment, GLenum textarget, GLuint texture, GLint level, GLint zoffset); +typedef void (APIENTRYP PFNGLFRAMEBUFFERRENDERBUFFEREXTPROC) (GLenum target, GLenum attachment, GLenum renderbuffertarget, GLuint renderbuffer); +typedef void (APIENTRYP PFNGLGETFRAMEBUFFERATTACHMENTPARAMETERIVEXTPROC) (GLenum target, GLenum attachment, GLenum pname, GLint *params); +typedef void (APIENTRYP PFNGLGENERATEMIPMAPEXTPROC) (GLenum target); +#ifdef GL_GLEXT_PROTOTYPES +GLAPI GLboolean APIENTRY glIsRenderbufferEXT (GLuint renderbuffer); +GLAPI void APIENTRY glBindRenderbufferEXT (GLenum target, GLuint renderbuffer); +GLAPI void APIENTRY glDeleteRenderbuffersEXT (GLsizei n, const GLuint *renderbuffers); +GLAPI void APIENTRY glGenRenderbuffersEXT (GLsizei n, GLuint *renderbuffers); +GLAPI void APIENTRY glRenderbufferStorageEXT (GLenum target, GLenum internalformat, GLsizei width, GLsizei height); +GLAPI void APIENTRY glGetRenderbufferParameterivEXT (GLenum target, GLenum pname, GLint *params); +GLAPI GLboolean APIENTRY glIsFramebufferEXT (GLuint framebuffer); +GLAPI void APIENTRY glBindFramebufferEXT (GLenum target, GLuint framebuffer); +GLAPI void APIENTRY glDeleteFramebuffersEXT (GLsizei n, const GLuint *framebuffers); +GLAPI void APIENTRY glGenFramebuffersEXT (GLsizei n, GLuint *framebuffers); +GLAPI GLenum APIENTRY glCheckFramebufferStatusEXT (GLenum target); +GLAPI void APIENTRY glFramebufferTexture1DEXT (GLenum target, GLenum attachment, GLenum textarget, GLuint texture, GLint level); +GLAPI void APIENTRY glFramebufferTexture2DEXT (GLenum target, GLenum attachment, GLenum textarget, GLuint texture, GLint level); +GLAPI void APIENTRY glFramebufferTexture3DEXT (GLenum target, GLenum attachment, GLenum textarget, GLuint texture, GLint level, GLint zoffset); +GLAPI void APIENTRY glFramebufferRenderbufferEXT (GLenum target, GLenum attachment, GLenum renderbuffertarget, GLuint renderbuffer); +GLAPI void APIENTRY glGetFramebufferAttachmentParameterivEXT (GLenum target, GLenum attachment, GLenum pname, GLint *params); +GLAPI void APIENTRY glGenerateMipmapEXT (GLenum target); +#endif +#endif /* GL_EXT_framebuffer_object */ + +#ifndef GL_EXT_framebuffer_sRGB +#define GL_EXT_framebuffer_sRGB 1 +#define GL_FRAMEBUFFER_SRGB_EXT 0x8DB9 +#define GL_FRAMEBUFFER_SRGB_CAPABLE_EXT 0x8DBA +#endif /* GL_EXT_framebuffer_sRGB */ + +#ifndef GL_EXT_geometry_shader4 +#define GL_EXT_geometry_shader4 1 +#define GL_GEOMETRY_SHADER_EXT 0x8DD9 +#define GL_GEOMETRY_VERTICES_OUT_EXT 0x8DDA +#define GL_GEOMETRY_INPUT_TYPE_EXT 0x8DDB +#define GL_GEOMETRY_OUTPUT_TYPE_EXT 0x8DDC +#define GL_MAX_GEOMETRY_TEXTURE_IMAGE_UNITS_EXT 0x8C29 +#define GL_MAX_GEOMETRY_VARYING_COMPONENTS_EXT 0x8DDD +#define GL_MAX_VERTEX_VARYING_COMPONENTS_EXT 0x8DDE +#define GL_MAX_VARYING_COMPONENTS_EXT 0x8B4B +#define GL_MAX_GEOMETRY_UNIFORM_COMPONENTS_EXT 0x8DDF +#define GL_MAX_GEOMETRY_OUTPUT_VERTICES_EXT 0x8DE0 +#define GL_MAX_GEOMETRY_TOTAL_OUTPUT_COMPONENTS_EXT 0x8DE1 +#define GL_LINES_ADJACENCY_EXT 0x000A +#define GL_LINE_STRIP_ADJACENCY_EXT 0x000B +#define GL_TRIANGLES_ADJACENCY_EXT 0x000C +#define GL_TRIANGLE_STRIP_ADJACENCY_EXT 0x000D +#define GL_FRAMEBUFFER_INCOMPLETE_LAYER_TARGETS_EXT 0x8DA8 +#define GL_FRAMEBUFFER_INCOMPLETE_LAYER_COUNT_EXT 0x8DA9 +#define GL_FRAMEBUFFER_ATTACHMENT_LAYERED_EXT 0x8DA7 +#define GL_FRAMEBUFFER_ATTACHMENT_TEXTURE_LAYER_EXT 0x8CD4 +#define GL_PROGRAM_POINT_SIZE_EXT 0x8642 +typedef void (APIENTRYP PFNGLPROGRAMPARAMETERIEXTPROC) (GLuint program, GLenum pname, GLint value); +#ifdef GL_GLEXT_PROTOTYPES +GLAPI void APIENTRY glProgramParameteriEXT (GLuint program, GLenum pname, GLint value); +#endif +#endif /* GL_EXT_geometry_shader4 */ + +#ifndef GL_EXT_gpu_program_parameters +#define GL_EXT_gpu_program_parameters 1 +typedef void (APIENTRYP PFNGLPROGRAMENVPARAMETERS4FVEXTPROC) (GLenum target, GLuint index, GLsizei count, const GLfloat *params); +typedef void (APIENTRYP PFNGLPROGRAMLOCALPARAMETERS4FVEXTPROC) (GLenum target, GLuint index, GLsizei count, const GLfloat *params); +#ifdef GL_GLEXT_PROTOTYPES +GLAPI void APIENTRY glProgramEnvParameters4fvEXT (GLenum target, GLuint index, GLsizei count, const GLfloat *params); +GLAPI void APIENTRY glProgramLocalParameters4fvEXT (GLenum target, GLuint index, GLsizei count, const GLfloat *params); +#endif +#endif /* GL_EXT_gpu_program_parameters */ + +#ifndef GL_EXT_gpu_shader4 +#define GL_EXT_gpu_shader4 1 +#define GL_VERTEX_ATTRIB_ARRAY_INTEGER_EXT 0x88FD +#define GL_SAMPLER_1D_ARRAY_EXT 0x8DC0 +#define GL_SAMPLER_2D_ARRAY_EXT 0x8DC1 +#define GL_SAMPLER_BUFFER_EXT 0x8DC2 +#define GL_SAMPLER_1D_ARRAY_SHADOW_EXT 0x8DC3 +#define GL_SAMPLER_2D_ARRAY_SHADOW_EXT 0x8DC4 +#define GL_SAMPLER_CUBE_SHADOW_EXT 0x8DC5 +#define GL_UNSIGNED_INT_VEC2_EXT 0x8DC6 +#define GL_UNSIGNED_INT_VEC3_EXT 0x8DC7 +#define GL_UNSIGNED_INT_VEC4_EXT 0x8DC8 +#define GL_INT_SAMPLER_1D_EXT 0x8DC9 +#define GL_INT_SAMPLER_2D_EXT 0x8DCA +#define GL_INT_SAMPLER_3D_EXT 0x8DCB +#define GL_INT_SAMPLER_CUBE_EXT 0x8DCC +#define GL_INT_SAMPLER_2D_RECT_EXT 0x8DCD +#define GL_INT_SAMPLER_1D_ARRAY_EXT 0x8DCE +#define GL_INT_SAMPLER_2D_ARRAY_EXT 0x8DCF +#define GL_INT_SAMPLER_BUFFER_EXT 0x8DD0 +#define GL_UNSIGNED_INT_SAMPLER_1D_EXT 0x8DD1 +#define GL_UNSIGNED_INT_SAMPLER_2D_EXT 0x8DD2 +#define GL_UNSIGNED_INT_SAMPLER_3D_EXT 0x8DD3 +#define GL_UNSIGNED_INT_SAMPLER_CUBE_EXT 0x8DD4 +#define GL_UNSIGNED_INT_SAMPLER_2D_RECT_EXT 0x8DD5 +#define GL_UNSIGNED_INT_SAMPLER_1D_ARRAY_EXT 0x8DD6 +#define GL_UNSIGNED_INT_SAMPLER_2D_ARRAY_EXT 0x8DD7 +#define GL_UNSIGNED_INT_SAMPLER_BUFFER_EXT 0x8DD8 +#define GL_MIN_PROGRAM_TEXEL_OFFSET_EXT 0x8904 +#define GL_MAX_PROGRAM_TEXEL_OFFSET_EXT 0x8905 +typedef void (APIENTRYP PFNGLGETUNIFORMUIVEXTPROC) (GLuint program, GLint location, GLuint *params); +typedef void (APIENTRYP PFNGLBINDFRAGDATALOCATIONEXTPROC) (GLuint program, GLuint color, const GLchar *name); +typedef GLint (APIENTRYP PFNGLGETFRAGDATALOCATIONEXTPROC) (GLuint program, const GLchar *name); +typedef void (APIENTRYP PFNGLUNIFORM1UIEXTPROC) (GLint location, GLuint v0); +typedef void (APIENTRYP PFNGLUNIFORM2UIEXTPROC) (GLint location, GLuint v0, GLuint v1); +typedef void (APIENTRYP PFNGLUNIFORM3UIEXTPROC) (GLint location, GLuint v0, GLuint v1, GLuint v2); +typedef void (APIENTRYP PFNGLUNIFORM4UIEXTPROC) (GLint location, GLuint v0, GLuint v1, GLuint v2, GLuint v3); +typedef void (APIENTRYP PFNGLUNIFORM1UIVEXTPROC) (GLint location, GLsizei count, const GLuint *value); +typedef void (APIENTRYP PFNGLUNIFORM2UIVEXTPROC) (GLint location, GLsizei count, const GLuint *value); +typedef void (APIENTRYP PFNGLUNIFORM3UIVEXTPROC) (GLint location, GLsizei count, const GLuint *value); +typedef void (APIENTRYP PFNGLUNIFORM4UIVEXTPROC) (GLint location, GLsizei count, const GLuint *value); +#ifdef GL_GLEXT_PROTOTYPES +GLAPI void APIENTRY glGetUniformuivEXT (GLuint program, GLint location, GLuint *params); +GLAPI void APIENTRY glBindFragDataLocationEXT (GLuint program, GLuint color, const GLchar *name); +GLAPI GLint APIENTRY glGetFragDataLocationEXT (GLuint program, const GLchar *name); +GLAPI void APIENTRY glUniform1uiEXT (GLint location, GLuint v0); +GLAPI void APIENTRY glUniform2uiEXT (GLint location, GLuint v0, GLuint v1); +GLAPI void APIENTRY glUniform3uiEXT (GLint location, GLuint v0, GLuint v1, GLuint v2); +GLAPI void APIENTRY glUniform4uiEXT (GLint location, GLuint v0, GLuint v1, GLuint v2, GLuint v3); +GLAPI void APIENTRY glUniform1uivEXT (GLint location, GLsizei count, const GLuint *value); +GLAPI void APIENTRY glUniform2uivEXT (GLint location, GLsizei count, const GLuint *value); +GLAPI void APIENTRY glUniform3uivEXT (GLint location, GLsizei count, const GLuint *value); +GLAPI void APIENTRY glUniform4uivEXT (GLint location, GLsizei count, const GLuint *value); +#endif +#endif /* GL_EXT_gpu_shader4 */ + +#ifndef GL_EXT_histogram +#define GL_EXT_histogram 1 +#define GL_HISTOGRAM_EXT 0x8024 +#define GL_PROXY_HISTOGRAM_EXT 0x8025 +#define GL_HISTOGRAM_WIDTH_EXT 0x8026 +#define GL_HISTOGRAM_FORMAT_EXT 0x8027 +#define GL_HISTOGRAM_RED_SIZE_EXT 0x8028 +#define GL_HISTOGRAM_GREEN_SIZE_EXT 0x8029 +#define GL_HISTOGRAM_BLUE_SIZE_EXT 0x802A +#define GL_HISTOGRAM_ALPHA_SIZE_EXT 0x802B +#define GL_HISTOGRAM_LUMINANCE_SIZE_EXT 0x802C +#define GL_HISTOGRAM_SINK_EXT 0x802D +#define GL_MINMAX_EXT 0x802E +#define GL_MINMAX_FORMAT_EXT 0x802F +#define GL_MINMAX_SINK_EXT 0x8030 +#define GL_TABLE_TOO_LARGE_EXT 0x8031 +typedef void (APIENTRYP PFNGLGETHISTOGRAMEXTPROC) (GLenum target, GLboolean reset, GLenum format, GLenum type, void *values); +typedef void (APIENTRYP PFNGLGETHISTOGRAMPARAMETERFVEXTPROC) (GLenum target, GLenum pname, GLfloat *params); +typedef void (APIENTRYP PFNGLGETHISTOGRAMPARAMETERIVEXTPROC) (GLenum target, GLenum pname, GLint *params); +typedef void (APIENTRYP PFNGLGETMINMAXEXTPROC) (GLenum target, GLboolean reset, GLenum format, GLenum type, void *values); +typedef void (APIENTRYP PFNGLGETMINMAXPARAMETERFVEXTPROC) (GLenum target, GLenum pname, GLfloat *params); +typedef void (APIENTRYP PFNGLGETMINMAXPARAMETERIVEXTPROC) (GLenum target, GLenum pname, GLint *params); +typedef void (APIENTRYP PFNGLHISTOGRAMEXTPROC) (GLenum target, GLsizei width, GLenum internalformat, GLboolean sink); +typedef void (APIENTRYP PFNGLMINMAXEXTPROC) (GLenum target, GLenum internalformat, GLboolean sink); +typedef void (APIENTRYP PFNGLRESETHISTOGRAMEXTPROC) (GLenum target); +typedef void (APIENTRYP PFNGLRESETMINMAXEXTPROC) (GLenum target); +#ifdef GL_GLEXT_PROTOTYPES +GLAPI void APIENTRY glGetHistogramEXT (GLenum target, GLboolean reset, GLenum format, GLenum type, void *values); +GLAPI void APIENTRY glGetHistogramParameterfvEXT (GLenum target, GLenum pname, GLfloat *params); +GLAPI void APIENTRY glGetHistogramParameterivEXT (GLenum target, GLenum pname, GLint *params); +GLAPI void APIENTRY glGetMinmaxEXT (GLenum target, GLboolean reset, GLenum format, GLenum type, void *values); +GLAPI void APIENTRY glGetMinmaxParameterfvEXT (GLenum target, GLenum pname, GLfloat *params); +GLAPI void APIENTRY glGetMinmaxParameterivEXT (GLenum target, GLenum pname, GLint *params); +GLAPI void APIENTRY glHistogramEXT (GLenum target, GLsizei width, GLenum internalformat, GLboolean sink); +GLAPI void APIENTRY glMinmaxEXT (GLenum target, GLenum internalformat, GLboolean sink); +GLAPI void APIENTRY glResetHistogramEXT (GLenum target); +GLAPI void APIENTRY glResetMinmaxEXT (GLenum target); +#endif +#endif /* GL_EXT_histogram */ + +#ifndef GL_EXT_index_array_formats +#define GL_EXT_index_array_formats 1 +#define GL_IUI_V2F_EXT 0x81AD +#define GL_IUI_V3F_EXT 0x81AE +#define GL_IUI_N3F_V2F_EXT 0x81AF +#define GL_IUI_N3F_V3F_EXT 0x81B0 +#define GL_T2F_IUI_V2F_EXT 0x81B1 +#define GL_T2F_IUI_V3F_EXT 0x81B2 +#define GL_T2F_IUI_N3F_V2F_EXT 0x81B3 +#define GL_T2F_IUI_N3F_V3F_EXT 0x81B4 +#endif /* GL_EXT_index_array_formats */ + +#ifndef GL_EXT_index_func +#define GL_EXT_index_func 1 +#define GL_INDEX_TEST_EXT 0x81B5 +#define GL_INDEX_TEST_FUNC_EXT 0x81B6 +#define GL_INDEX_TEST_REF_EXT 0x81B7 +typedef void (APIENTRYP PFNGLINDEXFUNCEXTPROC) (GLenum func, GLclampf ref); +#ifdef GL_GLEXT_PROTOTYPES +GLAPI void APIENTRY glIndexFuncEXT (GLenum func, GLclampf ref); +#endif +#endif /* GL_EXT_index_func */ + +#ifndef GL_EXT_index_material +#define GL_EXT_index_material 1 +#define GL_INDEX_MATERIAL_EXT 0x81B8 +#define GL_INDEX_MATERIAL_PARAMETER_EXT 0x81B9 +#define GL_INDEX_MATERIAL_FACE_EXT 0x81BA +typedef void (APIENTRYP PFNGLINDEXMATERIALEXTPROC) (GLenum face, GLenum mode); +#ifdef GL_GLEXT_PROTOTYPES +GLAPI void APIENTRY glIndexMaterialEXT (GLenum face, GLenum mode); +#endif +#endif /* GL_EXT_index_material */ + +#ifndef GL_EXT_index_texture +#define GL_EXT_index_texture 1 +#endif /* GL_EXT_index_texture */ + +#ifndef GL_EXT_light_texture +#define GL_EXT_light_texture 1 +#define GL_FRAGMENT_MATERIAL_EXT 0x8349 +#define GL_FRAGMENT_NORMAL_EXT 0x834A +#define GL_FRAGMENT_COLOR_EXT 0x834C +#define GL_ATTENUATION_EXT 0x834D +#define GL_SHADOW_ATTENUATION_EXT 0x834E +#define GL_TEXTURE_APPLICATION_MODE_EXT 0x834F +#define GL_TEXTURE_LIGHT_EXT 0x8350 +#define GL_TEXTURE_MATERIAL_FACE_EXT 0x8351 +#define GL_TEXTURE_MATERIAL_PARAMETER_EXT 0x8352 +typedef void (APIENTRYP PFNGLAPPLYTEXTUREEXTPROC) (GLenum mode); +typedef void (APIENTRYP PFNGLTEXTURELIGHTEXTPROC) (GLenum pname); +typedef void (APIENTRYP PFNGLTEXTUREMATERIALEXTPROC) (GLenum face, GLenum mode); +#ifdef GL_GLEXT_PROTOTYPES +GLAPI void APIENTRY glApplyTextureEXT (GLenum mode); +GLAPI void APIENTRY glTextureLightEXT (GLenum pname); +GLAPI void APIENTRY glTextureMaterialEXT (GLenum face, GLenum mode); +#endif +#endif /* GL_EXT_light_texture */ + +#ifndef GL_EXT_misc_attribute +#define GL_EXT_misc_attribute 1 +#endif /* GL_EXT_misc_attribute */ + +#ifndef GL_EXT_multi_draw_arrays +#define GL_EXT_multi_draw_arrays 1 +typedef void (APIENTRYP PFNGLMULTIDRAWARRAYSEXTPROC) (GLenum mode, const GLint *first, const GLsizei *count, GLsizei primcount); +typedef void (APIENTRYP PFNGLMULTIDRAWELEMENTSEXTPROC) (GLenum mode, const GLsizei *count, GLenum type, const void *const*indices, GLsizei primcount); +#ifdef GL_GLEXT_PROTOTYPES +GLAPI void APIENTRY glMultiDrawArraysEXT (GLenum mode, const GLint *first, const GLsizei *count, GLsizei primcount); +GLAPI void APIENTRY glMultiDrawElementsEXT (GLenum mode, const GLsizei *count, GLenum type, const void *const*indices, GLsizei primcount); +#endif +#endif /* GL_EXT_multi_draw_arrays */ + +#ifndef GL_EXT_multisample +#define GL_EXT_multisample 1 +#define GL_MULTISAMPLE_EXT 0x809D +#define GL_SAMPLE_ALPHA_TO_MASK_EXT 0x809E +#define GL_SAMPLE_ALPHA_TO_ONE_EXT 0x809F +#define GL_SAMPLE_MASK_EXT 0x80A0 +#define GL_1PASS_EXT 0x80A1 +#define GL_2PASS_0_EXT 0x80A2 +#define GL_2PASS_1_EXT 0x80A3 +#define GL_4PASS_0_EXT 0x80A4 +#define GL_4PASS_1_EXT 0x80A5 +#define GL_4PASS_2_EXT 0x80A6 +#define GL_4PASS_3_EXT 0x80A7 +#define GL_SAMPLE_BUFFERS_EXT 0x80A8 +#define GL_SAMPLES_EXT 0x80A9 +#define GL_SAMPLE_MASK_VALUE_EXT 0x80AA +#define GL_SAMPLE_MASK_INVERT_EXT 0x80AB +#define GL_SAMPLE_PATTERN_EXT 0x80AC +#define GL_MULTISAMPLE_BIT_EXT 0x20000000 +typedef void (APIENTRYP PFNGLSAMPLEMASKEXTPROC) (GLclampf value, GLboolean invert); +typedef void (APIENTRYP PFNGLSAMPLEPATTERNEXTPROC) (GLenum pattern); +#ifdef GL_GLEXT_PROTOTYPES +GLAPI void APIENTRY glSampleMaskEXT (GLclampf value, GLboolean invert); +GLAPI void APIENTRY glSamplePatternEXT (GLenum pattern); +#endif +#endif /* GL_EXT_multisample */ + +#ifndef GL_EXT_packed_depth_stencil +#define GL_EXT_packed_depth_stencil 1 +#define GL_DEPTH_STENCIL_EXT 0x84F9 +#define GL_UNSIGNED_INT_24_8_EXT 0x84FA +#define GL_DEPTH24_STENCIL8_EXT 0x88F0 +#define GL_TEXTURE_STENCIL_SIZE_EXT 0x88F1 +#endif /* GL_EXT_packed_depth_stencil */ + +#ifndef GL_EXT_packed_float +#define GL_EXT_packed_float 1 +#define GL_R11F_G11F_B10F_EXT 0x8C3A +#define GL_UNSIGNED_INT_10F_11F_11F_REV_EXT 0x8C3B +#define GL_RGBA_SIGNED_COMPONENTS_EXT 0x8C3C +#endif /* GL_EXT_packed_float */ + +#ifndef GL_EXT_packed_pixels +#define GL_EXT_packed_pixels 1 +#define GL_UNSIGNED_BYTE_3_3_2_EXT 0x8032 +#define GL_UNSIGNED_SHORT_4_4_4_4_EXT 0x8033 +#define GL_UNSIGNED_SHORT_5_5_5_1_EXT 0x8034 +#define GL_UNSIGNED_INT_8_8_8_8_EXT 0x8035 +#define GL_UNSIGNED_INT_10_10_10_2_EXT 0x8036 +#endif /* GL_EXT_packed_pixels */ + +#ifndef GL_EXT_paletted_texture +#define GL_EXT_paletted_texture 1 +#define GL_COLOR_INDEX1_EXT 0x80E2 +#define GL_COLOR_INDEX2_EXT 0x80E3 +#define GL_COLOR_INDEX4_EXT 0x80E4 +#define GL_COLOR_INDEX8_EXT 0x80E5 +#define GL_COLOR_INDEX12_EXT 0x80E6 +#define GL_COLOR_INDEX16_EXT 0x80E7 +#define GL_TEXTURE_INDEX_SIZE_EXT 0x80ED +typedef void (APIENTRYP PFNGLCOLORTABLEEXTPROC) (GLenum target, GLenum internalFormat, GLsizei width, GLenum format, GLenum type, const void *table); +typedef void (APIENTRYP PFNGLGETCOLORTABLEEXTPROC) (GLenum target, GLenum format, GLenum type, void *data); +typedef void (APIENTRYP PFNGLGETCOLORTABLEPARAMETERIVEXTPROC) (GLenum target, GLenum pname, GLint *params); +typedef void (APIENTRYP PFNGLGETCOLORTABLEPARAMETERFVEXTPROC) (GLenum target, GLenum pname, GLfloat *params); +#ifdef GL_GLEXT_PROTOTYPES +GLAPI void APIENTRY glColorTableEXT (GLenum target, GLenum internalFormat, GLsizei width, GLenum format, GLenum type, const void *table); +GLAPI void APIENTRY glGetColorTableEXT (GLenum target, GLenum format, GLenum type, void *data); +GLAPI void APIENTRY glGetColorTableParameterivEXT (GLenum target, GLenum pname, GLint *params); +GLAPI void APIENTRY glGetColorTableParameterfvEXT (GLenum target, GLenum pname, GLfloat *params); +#endif +#endif /* GL_EXT_paletted_texture */ + +#ifndef GL_EXT_pixel_buffer_object +#define GL_EXT_pixel_buffer_object 1 +#define GL_PIXEL_PACK_BUFFER_EXT 0x88EB +#define GL_PIXEL_UNPACK_BUFFER_EXT 0x88EC +#define GL_PIXEL_PACK_BUFFER_BINDING_EXT 0x88ED +#define GL_PIXEL_UNPACK_BUFFER_BINDING_EXT 0x88EF +#endif /* GL_EXT_pixel_buffer_object */ + +#ifndef GL_EXT_pixel_transform +#define GL_EXT_pixel_transform 1 +#define GL_PIXEL_TRANSFORM_2D_EXT 0x8330 +#define GL_PIXEL_MAG_FILTER_EXT 0x8331 +#define GL_PIXEL_MIN_FILTER_EXT 0x8332 +#define GL_PIXEL_CUBIC_WEIGHT_EXT 0x8333 +#define GL_CUBIC_EXT 0x8334 +#define GL_AVERAGE_EXT 0x8335 +#define GL_PIXEL_TRANSFORM_2D_STACK_DEPTH_EXT 0x8336 +#define GL_MAX_PIXEL_TRANSFORM_2D_STACK_DEPTH_EXT 0x8337 +#define GL_PIXEL_TRANSFORM_2D_MATRIX_EXT 0x8338 +typedef void (APIENTRYP PFNGLPIXELTRANSFORMPARAMETERIEXTPROC) (GLenum target, GLenum pname, GLint param); +typedef void (APIENTRYP PFNGLPIXELTRANSFORMPARAMETERFEXTPROC) (GLenum target, GLenum pname, GLfloat param); +typedef void (APIENTRYP PFNGLPIXELTRANSFORMPARAMETERIVEXTPROC) (GLenum target, GLenum pname, const GLint *params); +typedef void (APIENTRYP PFNGLPIXELTRANSFORMPARAMETERFVEXTPROC) (GLenum target, GLenum pname, const GLfloat *params); +typedef void (APIENTRYP PFNGLGETPIXELTRANSFORMPARAMETERIVEXTPROC) (GLenum target, GLenum pname, GLint *params); +typedef void (APIENTRYP PFNGLGETPIXELTRANSFORMPARAMETERFVEXTPROC) (GLenum target, GLenum pname, GLfloat *params); +#ifdef GL_GLEXT_PROTOTYPES +GLAPI void APIENTRY glPixelTransformParameteriEXT (GLenum target, GLenum pname, GLint param); +GLAPI void APIENTRY glPixelTransformParameterfEXT (GLenum target, GLenum pname, GLfloat param); +GLAPI void APIENTRY glPixelTransformParameterivEXT (GLenum target, GLenum pname, const GLint *params); +GLAPI void APIENTRY glPixelTransformParameterfvEXT (GLenum target, GLenum pname, const GLfloat *params); +GLAPI void APIENTRY glGetPixelTransformParameterivEXT (GLenum target, GLenum pname, GLint *params); +GLAPI void APIENTRY glGetPixelTransformParameterfvEXT (GLenum target, GLenum pname, GLfloat *params); +#endif +#endif /* GL_EXT_pixel_transform */ + +#ifndef GL_EXT_pixel_transform_color_table +#define GL_EXT_pixel_transform_color_table 1 +#endif /* GL_EXT_pixel_transform_color_table */ + +#ifndef GL_EXT_point_parameters +#define GL_EXT_point_parameters 1 +#define GL_POINT_SIZE_MIN_EXT 0x8126 +#define GL_POINT_SIZE_MAX_EXT 0x8127 +#define GL_POINT_FADE_THRESHOLD_SIZE_EXT 0x8128 +#define GL_DISTANCE_ATTENUATION_EXT 0x8129 +typedef void (APIENTRYP PFNGLPOINTPARAMETERFEXTPROC) (GLenum pname, GLfloat param); +typedef void (APIENTRYP PFNGLPOINTPARAMETERFVEXTPROC) (GLenum pname, const GLfloat *params); +#ifdef GL_GLEXT_PROTOTYPES +GLAPI void APIENTRY glPointParameterfEXT (GLenum pname, GLfloat param); +GLAPI void APIENTRY glPointParameterfvEXT (GLenum pname, const GLfloat *params); +#endif +#endif /* GL_EXT_point_parameters */ + +#ifndef GL_EXT_polygon_offset +#define GL_EXT_polygon_offset 1 +#define GL_POLYGON_OFFSET_EXT 0x8037 +#define GL_POLYGON_OFFSET_FACTOR_EXT 0x8038 +#define GL_POLYGON_OFFSET_BIAS_EXT 0x8039 +typedef void (APIENTRYP PFNGLPOLYGONOFFSETEXTPROC) (GLfloat factor, GLfloat bias); +#ifdef GL_GLEXT_PROTOTYPES +GLAPI void APIENTRY glPolygonOffsetEXT (GLfloat factor, GLfloat bias); +#endif +#endif /* GL_EXT_polygon_offset */ + +#ifndef GL_EXT_provoking_vertex +#define GL_EXT_provoking_vertex 1 +#define GL_QUADS_FOLLOW_PROVOKING_VERTEX_CONVENTION_EXT 0x8E4C +#define GL_FIRST_VERTEX_CONVENTION_EXT 0x8E4D +#define GL_LAST_VERTEX_CONVENTION_EXT 0x8E4E +#define GL_PROVOKING_VERTEX_EXT 0x8E4F +typedef void (APIENTRYP PFNGLPROVOKINGVERTEXEXTPROC) (GLenum mode); +#ifdef GL_GLEXT_PROTOTYPES +GLAPI void APIENTRY glProvokingVertexEXT (GLenum mode); +#endif +#endif /* GL_EXT_provoking_vertex */ + +#ifndef GL_EXT_rescale_normal +#define GL_EXT_rescale_normal 1 +#define GL_RESCALE_NORMAL_EXT 0x803A +#endif /* GL_EXT_rescale_normal */ + +#ifndef GL_EXT_secondary_color +#define GL_EXT_secondary_color 1 +#define GL_COLOR_SUM_EXT 0x8458 +#define GL_CURRENT_SECONDARY_COLOR_EXT 0x8459 +#define GL_SECONDARY_COLOR_ARRAY_SIZE_EXT 0x845A +#define GL_SECONDARY_COLOR_ARRAY_TYPE_EXT 0x845B +#define GL_SECONDARY_COLOR_ARRAY_STRIDE_EXT 0x845C +#define GL_SECONDARY_COLOR_ARRAY_POINTER_EXT 0x845D +#define GL_SECONDARY_COLOR_ARRAY_EXT 0x845E +typedef void (APIENTRYP PFNGLSECONDARYCOLOR3BEXTPROC) (GLbyte red, GLbyte green, GLbyte blue); +typedef void (APIENTRYP PFNGLSECONDARYCOLOR3BVEXTPROC) (const GLbyte *v); +typedef void (APIENTRYP PFNGLSECONDARYCOLOR3DEXTPROC) (GLdouble red, GLdouble green, GLdouble blue); +typedef void (APIENTRYP PFNGLSECONDARYCOLOR3DVEXTPROC) (const GLdouble *v); +typedef void (APIENTRYP PFNGLSECONDARYCOLOR3FEXTPROC) (GLfloat red, GLfloat green, GLfloat blue); +typedef void (APIENTRYP PFNGLSECONDARYCOLOR3FVEXTPROC) (const GLfloat *v); +typedef void (APIENTRYP PFNGLSECONDARYCOLOR3IEXTPROC) (GLint red, GLint green, GLint blue); +typedef void (APIENTRYP PFNGLSECONDARYCOLOR3IVEXTPROC) (const GLint *v); +typedef void (APIENTRYP PFNGLSECONDARYCOLOR3SEXTPROC) (GLshort red, GLshort green, GLshort blue); +typedef void (APIENTRYP PFNGLSECONDARYCOLOR3SVEXTPROC) (const GLshort *v); +typedef void (APIENTRYP PFNGLSECONDARYCOLOR3UBEXTPROC) (GLubyte red, GLubyte green, GLubyte blue); +typedef void (APIENTRYP PFNGLSECONDARYCOLOR3UBVEXTPROC) (const GLubyte *v); +typedef void (APIENTRYP PFNGLSECONDARYCOLOR3UIEXTPROC) (GLuint red, GLuint green, GLuint blue); +typedef void (APIENTRYP PFNGLSECONDARYCOLOR3UIVEXTPROC) (const GLuint *v); +typedef void (APIENTRYP PFNGLSECONDARYCOLOR3USEXTPROC) (GLushort red, GLushort green, GLushort blue); +typedef void (APIENTRYP PFNGLSECONDARYCOLOR3USVEXTPROC) (const GLushort *v); +typedef void (APIENTRYP PFNGLSECONDARYCOLORPOINTEREXTPROC) (GLint size, GLenum type, GLsizei stride, const void *pointer); +#ifdef GL_GLEXT_PROTOTYPES +GLAPI void APIENTRY glSecondaryColor3bEXT (GLbyte red, GLbyte green, GLbyte blue); +GLAPI void APIENTRY glSecondaryColor3bvEXT (const GLbyte *v); +GLAPI void APIENTRY glSecondaryColor3dEXT (GLdouble red, GLdouble green, GLdouble blue); +GLAPI void APIENTRY glSecondaryColor3dvEXT (const GLdouble *v); +GLAPI void APIENTRY glSecondaryColor3fEXT (GLfloat red, GLfloat green, GLfloat blue); +GLAPI void APIENTRY glSecondaryColor3fvEXT (const GLfloat *v); +GLAPI void APIENTRY glSecondaryColor3iEXT (GLint red, GLint green, GLint blue); +GLAPI void APIENTRY glSecondaryColor3ivEXT (const GLint *v); +GLAPI void APIENTRY glSecondaryColor3sEXT (GLshort red, GLshort green, GLshort blue); +GLAPI void APIENTRY glSecondaryColor3svEXT (const GLshort *v); +GLAPI void APIENTRY glSecondaryColor3ubEXT (GLubyte red, GLubyte green, GLubyte blue); +GLAPI void APIENTRY glSecondaryColor3ubvEXT (const GLubyte *v); +GLAPI void APIENTRY glSecondaryColor3uiEXT (GLuint red, GLuint green, GLuint blue); +GLAPI void APIENTRY glSecondaryColor3uivEXT (const GLuint *v); +GLAPI void APIENTRY glSecondaryColor3usEXT (GLushort red, GLushort green, GLushort blue); +GLAPI void APIENTRY glSecondaryColor3usvEXT (const GLushort *v); +GLAPI void APIENTRY glSecondaryColorPointerEXT (GLint size, GLenum type, GLsizei stride, const void *pointer); +#endif +#endif /* GL_EXT_secondary_color */ + +#ifndef GL_EXT_separate_shader_objects +#define GL_EXT_separate_shader_objects 1 +#define GL_ACTIVE_PROGRAM_EXT 0x8B8D +typedef void (APIENTRYP PFNGLUSESHADERPROGRAMEXTPROC) (GLenum type, GLuint program); +typedef void (APIENTRYP PFNGLACTIVEPROGRAMEXTPROC) (GLuint program); +typedef GLuint (APIENTRYP PFNGLCREATESHADERPROGRAMEXTPROC) (GLenum type, const GLchar *string); +#ifdef GL_GLEXT_PROTOTYPES +GLAPI void APIENTRY glUseShaderProgramEXT (GLenum type, GLuint program); +GLAPI void APIENTRY glActiveProgramEXT (GLuint program); +GLAPI GLuint APIENTRY glCreateShaderProgramEXT (GLenum type, const GLchar *string); +#endif +#endif /* GL_EXT_separate_shader_objects */ + +#ifndef GL_EXT_separate_specular_color +#define GL_EXT_separate_specular_color 1 +#define GL_LIGHT_MODEL_COLOR_CONTROL_EXT 0x81F8 +#define GL_SINGLE_COLOR_EXT 0x81F9 +#define GL_SEPARATE_SPECULAR_COLOR_EXT 0x81FA +#endif /* GL_EXT_separate_specular_color */ + +#ifndef GL_EXT_shader_image_load_store +#define GL_EXT_shader_image_load_store 1 +#define GL_MAX_IMAGE_UNITS_EXT 0x8F38 +#define GL_MAX_COMBINED_IMAGE_UNITS_AND_FRAGMENT_OUTPUTS_EXT 0x8F39 +#define GL_IMAGE_BINDING_NAME_EXT 0x8F3A +#define GL_IMAGE_BINDING_LEVEL_EXT 0x8F3B +#define GL_IMAGE_BINDING_LAYERED_EXT 0x8F3C +#define GL_IMAGE_BINDING_LAYER_EXT 0x8F3D +#define GL_IMAGE_BINDING_ACCESS_EXT 0x8F3E +#define GL_IMAGE_1D_EXT 0x904C +#define GL_IMAGE_2D_EXT 0x904D +#define GL_IMAGE_3D_EXT 0x904E +#define GL_IMAGE_2D_RECT_EXT 0x904F +#define GL_IMAGE_CUBE_EXT 0x9050 +#define GL_IMAGE_BUFFER_EXT 0x9051 +#define GL_IMAGE_1D_ARRAY_EXT 0x9052 +#define GL_IMAGE_2D_ARRAY_EXT 0x9053 +#define GL_IMAGE_CUBE_MAP_ARRAY_EXT 0x9054 +#define GL_IMAGE_2D_MULTISAMPLE_EXT 0x9055 +#define GL_IMAGE_2D_MULTISAMPLE_ARRAY_EXT 0x9056 +#define GL_INT_IMAGE_1D_EXT 0x9057 +#define GL_INT_IMAGE_2D_EXT 0x9058 +#define GL_INT_IMAGE_3D_EXT 0x9059 +#define GL_INT_IMAGE_2D_RECT_EXT 0x905A +#define GL_INT_IMAGE_CUBE_EXT 0x905B +#define GL_INT_IMAGE_BUFFER_EXT 0x905C +#define GL_INT_IMAGE_1D_ARRAY_EXT 0x905D +#define GL_INT_IMAGE_2D_ARRAY_EXT 0x905E +#define GL_INT_IMAGE_CUBE_MAP_ARRAY_EXT 0x905F +#define GL_INT_IMAGE_2D_MULTISAMPLE_EXT 0x9060 +#define GL_INT_IMAGE_2D_MULTISAMPLE_ARRAY_EXT 0x9061 +#define GL_UNSIGNED_INT_IMAGE_1D_EXT 0x9062 +#define GL_UNSIGNED_INT_IMAGE_2D_EXT 0x9063 +#define GL_UNSIGNED_INT_IMAGE_3D_EXT 0x9064 +#define GL_UNSIGNED_INT_IMAGE_2D_RECT_EXT 0x9065 +#define GL_UNSIGNED_INT_IMAGE_CUBE_EXT 0x9066 +#define GL_UNSIGNED_INT_IMAGE_BUFFER_EXT 0x9067 +#define GL_UNSIGNED_INT_IMAGE_1D_ARRAY_EXT 0x9068 +#define GL_UNSIGNED_INT_IMAGE_2D_ARRAY_EXT 0x9069 +#define GL_UNSIGNED_INT_IMAGE_CUBE_MAP_ARRAY_EXT 0x906A +#define GL_UNSIGNED_INT_IMAGE_2D_MULTISAMPLE_EXT 0x906B +#define GL_UNSIGNED_INT_IMAGE_2D_MULTISAMPLE_ARRAY_EXT 0x906C +#define GL_MAX_IMAGE_SAMPLES_EXT 0x906D +#define GL_IMAGE_BINDING_FORMAT_EXT 0x906E +#define GL_VERTEX_ATTRIB_ARRAY_BARRIER_BIT_EXT 0x00000001 +#define GL_ELEMENT_ARRAY_BARRIER_BIT_EXT 0x00000002 +#define GL_UNIFORM_BARRIER_BIT_EXT 0x00000004 +#define GL_TEXTURE_FETCH_BARRIER_BIT_EXT 0x00000008 +#define GL_SHADER_IMAGE_ACCESS_BARRIER_BIT_EXT 0x00000020 +#define GL_COMMAND_BARRIER_BIT_EXT 0x00000040 +#define GL_PIXEL_BUFFER_BARRIER_BIT_EXT 0x00000080 +#define GL_TEXTURE_UPDATE_BARRIER_BIT_EXT 0x00000100 +#define GL_BUFFER_UPDATE_BARRIER_BIT_EXT 0x00000200 +#define GL_FRAMEBUFFER_BARRIER_BIT_EXT 0x00000400 +#define GL_TRANSFORM_FEEDBACK_BARRIER_BIT_EXT 0x00000800 +#define GL_ATOMIC_COUNTER_BARRIER_BIT_EXT 0x00001000 +#define GL_ALL_BARRIER_BITS_EXT 0xFFFFFFFF +typedef void (APIENTRYP PFNGLBINDIMAGETEXTUREEXTPROC) (GLuint index, GLuint texture, GLint level, GLboolean layered, GLint layer, GLenum access, GLint format); +typedef void (APIENTRYP PFNGLMEMORYBARRIEREXTPROC) (GLbitfield barriers); +#ifdef GL_GLEXT_PROTOTYPES +GLAPI void APIENTRY glBindImageTextureEXT (GLuint index, GLuint texture, GLint level, GLboolean layered, GLint layer, GLenum access, GLint format); +GLAPI void APIENTRY glMemoryBarrierEXT (GLbitfield barriers); +#endif +#endif /* GL_EXT_shader_image_load_store */ + +#ifndef GL_EXT_shader_integer_mix +#define GL_EXT_shader_integer_mix 1 +#endif /* GL_EXT_shader_integer_mix */ + +#ifndef GL_EXT_shadow_funcs +#define GL_EXT_shadow_funcs 1 +#endif /* GL_EXT_shadow_funcs */ + +#ifndef GL_EXT_shared_texture_palette +#define GL_EXT_shared_texture_palette 1 +#define GL_SHARED_TEXTURE_PALETTE_EXT 0x81FB +#endif /* GL_EXT_shared_texture_palette */ + +#ifndef GL_EXT_stencil_clear_tag +#define GL_EXT_stencil_clear_tag 1 +#define GL_STENCIL_TAG_BITS_EXT 0x88F2 +#define GL_STENCIL_CLEAR_TAG_VALUE_EXT 0x88F3 +typedef void (APIENTRYP PFNGLSTENCILCLEARTAGEXTPROC) (GLsizei stencilTagBits, GLuint stencilClearTag); +#ifdef GL_GLEXT_PROTOTYPES +GLAPI void APIENTRY glStencilClearTagEXT (GLsizei stencilTagBits, GLuint stencilClearTag); +#endif +#endif /* GL_EXT_stencil_clear_tag */ + +#ifndef GL_EXT_stencil_two_side +#define GL_EXT_stencil_two_side 1 +#define GL_STENCIL_TEST_TWO_SIDE_EXT 0x8910 +#define GL_ACTIVE_STENCIL_FACE_EXT 0x8911 +typedef void (APIENTRYP PFNGLACTIVESTENCILFACEEXTPROC) (GLenum face); +#ifdef GL_GLEXT_PROTOTYPES +GLAPI void APIENTRY glActiveStencilFaceEXT (GLenum face); +#endif +#endif /* GL_EXT_stencil_two_side */ + +#ifndef GL_EXT_stencil_wrap +#define GL_EXT_stencil_wrap 1 +#define GL_INCR_WRAP_EXT 0x8507 +#define GL_DECR_WRAP_EXT 0x8508 +#endif /* GL_EXT_stencil_wrap */ + +#ifndef GL_EXT_subtexture +#define GL_EXT_subtexture 1 +typedef void (APIENTRYP PFNGLTEXSUBIMAGE1DEXTPROC) (GLenum target, GLint level, GLint xoffset, GLsizei width, GLenum format, GLenum type, const void *pixels); +typedef void (APIENTRYP PFNGLTEXSUBIMAGE2DEXTPROC) (GLenum target, GLint level, GLint xoffset, GLint yoffset, GLsizei width, GLsizei height, GLenum format, GLenum type, const void *pixels); +#ifdef GL_GLEXT_PROTOTYPES +GLAPI void APIENTRY glTexSubImage1DEXT (GLenum target, GLint level, GLint xoffset, GLsizei width, GLenum format, GLenum type, const void *pixels); +GLAPI void APIENTRY glTexSubImage2DEXT (GLenum target, GLint level, GLint xoffset, GLint yoffset, GLsizei width, GLsizei height, GLenum format, GLenum type, const void *pixels); +#endif +#endif /* GL_EXT_subtexture */ + +#ifndef GL_EXT_texture +#define GL_EXT_texture 1 +#define GL_ALPHA4_EXT 0x803B +#define GL_ALPHA8_EXT 0x803C +#define GL_ALPHA12_EXT 0x803D +#define GL_ALPHA16_EXT 0x803E +#define GL_LUMINANCE4_EXT 0x803F +#define GL_LUMINANCE8_EXT 0x8040 +#define GL_LUMINANCE12_EXT 0x8041 +#define GL_LUMINANCE16_EXT 0x8042 +#define GL_LUMINANCE4_ALPHA4_EXT 0x8043 +#define GL_LUMINANCE6_ALPHA2_EXT 0x8044 +#define GL_LUMINANCE8_ALPHA8_EXT 0x8045 +#define GL_LUMINANCE12_ALPHA4_EXT 0x8046 +#define GL_LUMINANCE12_ALPHA12_EXT 0x8047 +#define GL_LUMINANCE16_ALPHA16_EXT 0x8048 +#define GL_INTENSITY_EXT 0x8049 +#define GL_INTENSITY4_EXT 0x804A +#define GL_INTENSITY8_EXT 0x804B +#define GL_INTENSITY12_EXT 0x804C +#define GL_INTENSITY16_EXT 0x804D +#define GL_RGB2_EXT 0x804E +#define GL_RGB4_EXT 0x804F +#define GL_RGB5_EXT 0x8050 +#define GL_RGB8_EXT 0x8051 +#define GL_RGB10_EXT 0x8052 +#define GL_RGB12_EXT 0x8053 +#define GL_RGB16_EXT 0x8054 +#define GL_RGBA2_EXT 0x8055 +#define GL_RGBA4_EXT 0x8056 +#define GL_RGB5_A1_EXT 0x8057 +#define GL_RGBA8_EXT 0x8058 +#define GL_RGB10_A2_EXT 0x8059 +#define GL_RGBA12_EXT 0x805A +#define GL_RGBA16_EXT 0x805B +#define GL_TEXTURE_RED_SIZE_EXT 0x805C +#define GL_TEXTURE_GREEN_SIZE_EXT 0x805D +#define GL_TEXTURE_BLUE_SIZE_EXT 0x805E +#define GL_TEXTURE_ALPHA_SIZE_EXT 0x805F +#define GL_TEXTURE_LUMINANCE_SIZE_EXT 0x8060 +#define GL_TEXTURE_INTENSITY_SIZE_EXT 0x8061 +#define GL_REPLACE_EXT 0x8062 +#define GL_PROXY_TEXTURE_1D_EXT 0x8063 +#define GL_PROXY_TEXTURE_2D_EXT 0x8064 +#define GL_TEXTURE_TOO_LARGE_EXT 0x8065 +#endif /* GL_EXT_texture */ + +#ifndef GL_EXT_texture3D +#define GL_EXT_texture3D 1 +#define GL_PACK_SKIP_IMAGES_EXT 0x806B +#define GL_PACK_IMAGE_HEIGHT_EXT 0x806C +#define GL_UNPACK_SKIP_IMAGES_EXT 0x806D +#define GL_UNPACK_IMAGE_HEIGHT_EXT 0x806E +#define GL_TEXTURE_3D_EXT 0x806F +#define GL_PROXY_TEXTURE_3D_EXT 0x8070 +#define GL_TEXTURE_DEPTH_EXT 0x8071 +#define GL_TEXTURE_WRAP_R_EXT 0x8072 +#define GL_MAX_3D_TEXTURE_SIZE_EXT 0x8073 +typedef void (APIENTRYP PFNGLTEXIMAGE3DEXTPROC) (GLenum target, GLint level, GLenum internalformat, GLsizei width, GLsizei height, GLsizei depth, GLint border, GLenum format, GLenum type, const void *pixels); +typedef void (APIENTRYP PFNGLTEXSUBIMAGE3DEXTPROC) (GLenum target, GLint level, GLint xoffset, GLint yoffset, GLint zoffset, GLsizei width, GLsizei height, GLsizei depth, GLenum format, GLenum type, const void *pixels); +#ifdef GL_GLEXT_PROTOTYPES +GLAPI void APIENTRY glTexImage3DEXT (GLenum target, GLint level, GLenum internalformat, GLsizei width, GLsizei height, GLsizei depth, GLint border, GLenum format, GLenum type, const void *pixels); +GLAPI void APIENTRY glTexSubImage3DEXT (GLenum target, GLint level, GLint xoffset, GLint yoffset, GLint zoffset, GLsizei width, GLsizei height, GLsizei depth, GLenum format, GLenum type, const void *pixels); +#endif +#endif /* GL_EXT_texture3D */ + +#ifndef GL_EXT_texture_array +#define GL_EXT_texture_array 1 +#define GL_TEXTURE_1D_ARRAY_EXT 0x8C18 +#define GL_PROXY_TEXTURE_1D_ARRAY_EXT 0x8C19 +#define GL_TEXTURE_2D_ARRAY_EXT 0x8C1A +#define GL_PROXY_TEXTURE_2D_ARRAY_EXT 0x8C1B +#define GL_TEXTURE_BINDING_1D_ARRAY_EXT 0x8C1C +#define GL_TEXTURE_BINDING_2D_ARRAY_EXT 0x8C1D +#define GL_MAX_ARRAY_TEXTURE_LAYERS_EXT 0x88FF +#define GL_COMPARE_REF_DEPTH_TO_TEXTURE_EXT 0x884E +#endif /* GL_EXT_texture_array */ + +#ifndef GL_EXT_texture_buffer_object +#define GL_EXT_texture_buffer_object 1 +#define GL_TEXTURE_BUFFER_EXT 0x8C2A +#define GL_MAX_TEXTURE_BUFFER_SIZE_EXT 0x8C2B +#define GL_TEXTURE_BINDING_BUFFER_EXT 0x8C2C +#define GL_TEXTURE_BUFFER_DATA_STORE_BINDING_EXT 0x8C2D +#define GL_TEXTURE_BUFFER_FORMAT_EXT 0x8C2E +typedef void (APIENTRYP PFNGLTEXBUFFEREXTPROC) (GLenum target, GLenum internalformat, GLuint buffer); +#ifdef GL_GLEXT_PROTOTYPES +GLAPI void APIENTRY glTexBufferEXT (GLenum target, GLenum internalformat, GLuint buffer); +#endif +#endif /* GL_EXT_texture_buffer_object */ + +#ifndef GL_EXT_texture_compression_latc +#define GL_EXT_texture_compression_latc 1 +#define GL_COMPRESSED_LUMINANCE_LATC1_EXT 0x8C70 +#define GL_COMPRESSED_SIGNED_LUMINANCE_LATC1_EXT 0x8C71 +#define GL_COMPRESSED_LUMINANCE_ALPHA_LATC2_EXT 0x8C72 +#define GL_COMPRESSED_SIGNED_LUMINANCE_ALPHA_LATC2_EXT 0x8C73 +#endif /* GL_EXT_texture_compression_latc */ + +#ifndef GL_EXT_texture_compression_rgtc +#define GL_EXT_texture_compression_rgtc 1 +#define GL_COMPRESSED_RED_RGTC1_EXT 0x8DBB +#define GL_COMPRESSED_SIGNED_RED_RGTC1_EXT 0x8DBC +#define GL_COMPRESSED_RED_GREEN_RGTC2_EXT 0x8DBD +#define GL_COMPRESSED_SIGNED_RED_GREEN_RGTC2_EXT 0x8DBE +#endif /* GL_EXT_texture_compression_rgtc */ + +#ifndef GL_EXT_texture_compression_s3tc +#define GL_EXT_texture_compression_s3tc 1 +#define GL_COMPRESSED_RGB_S3TC_DXT1_EXT 0x83F0 +#define GL_COMPRESSED_RGBA_S3TC_DXT1_EXT 0x83F1 +#define GL_COMPRESSED_RGBA_S3TC_DXT3_EXT 0x83F2 +#define GL_COMPRESSED_RGBA_S3TC_DXT5_EXT 0x83F3 +#endif /* GL_EXT_texture_compression_s3tc */ + +#ifndef GL_EXT_texture_cube_map +#define GL_EXT_texture_cube_map 1 +#define GL_NORMAL_MAP_EXT 0x8511 +#define GL_REFLECTION_MAP_EXT 0x8512 +#define GL_TEXTURE_CUBE_MAP_EXT 0x8513 +#define GL_TEXTURE_BINDING_CUBE_MAP_EXT 0x8514 +#define GL_TEXTURE_CUBE_MAP_POSITIVE_X_EXT 0x8515 +#define GL_TEXTURE_CUBE_MAP_NEGATIVE_X_EXT 0x8516 +#define GL_TEXTURE_CUBE_MAP_POSITIVE_Y_EXT 0x8517 +#define GL_TEXTURE_CUBE_MAP_NEGATIVE_Y_EXT 0x8518 +#define GL_TEXTURE_CUBE_MAP_POSITIVE_Z_EXT 0x8519 +#define GL_TEXTURE_CUBE_MAP_NEGATIVE_Z_EXT 0x851A +#define GL_PROXY_TEXTURE_CUBE_MAP_EXT 0x851B +#define GL_MAX_CUBE_MAP_TEXTURE_SIZE_EXT 0x851C +#endif /* GL_EXT_texture_cube_map */ + +#ifndef GL_EXT_texture_env_add +#define GL_EXT_texture_env_add 1 +#endif /* GL_EXT_texture_env_add */ + +#ifndef GL_EXT_texture_env_combine +#define GL_EXT_texture_env_combine 1 +#define GL_COMBINE_EXT 0x8570 +#define GL_COMBINE_RGB_EXT 0x8571 +#define GL_COMBINE_ALPHA_EXT 0x8572 +#define GL_RGB_SCALE_EXT 0x8573 +#define GL_ADD_SIGNED_EXT 0x8574 +#define GL_INTERPOLATE_EXT 0x8575 +#define GL_CONSTANT_EXT 0x8576 +#define GL_PRIMARY_COLOR_EXT 0x8577 +#define GL_PREVIOUS_EXT 0x8578 +#define GL_SOURCE0_RGB_EXT 0x8580 +#define GL_SOURCE1_RGB_EXT 0x8581 +#define GL_SOURCE2_RGB_EXT 0x8582 +#define GL_SOURCE0_ALPHA_EXT 0x8588 +#define GL_SOURCE1_ALPHA_EXT 0x8589 +#define GL_SOURCE2_ALPHA_EXT 0x858A +#define GL_OPERAND0_RGB_EXT 0x8590 +#define GL_OPERAND1_RGB_EXT 0x8591 +#define GL_OPERAND2_RGB_EXT 0x8592 +#define GL_OPERAND0_ALPHA_EXT 0x8598 +#define GL_OPERAND1_ALPHA_EXT 0x8599 +#define GL_OPERAND2_ALPHA_EXT 0x859A +#endif /* GL_EXT_texture_env_combine */ + +#ifndef GL_EXT_texture_env_dot3 +#define GL_EXT_texture_env_dot3 1 +#define GL_DOT3_RGB_EXT 0x8740 +#define GL_DOT3_RGBA_EXT 0x8741 +#endif /* GL_EXT_texture_env_dot3 */ + +#ifndef GL_EXT_texture_filter_anisotropic +#define GL_EXT_texture_filter_anisotropic 1 +#define GL_TEXTURE_MAX_ANISOTROPY_EXT 0x84FE +#define GL_MAX_TEXTURE_MAX_ANISOTROPY_EXT 0x84FF +#endif /* GL_EXT_texture_filter_anisotropic */ + +#ifndef GL_EXT_texture_integer +#define GL_EXT_texture_integer 1 +#define GL_RGBA32UI_EXT 0x8D70 +#define GL_RGB32UI_EXT 0x8D71 +#define GL_ALPHA32UI_EXT 0x8D72 +#define GL_INTENSITY32UI_EXT 0x8D73 +#define GL_LUMINANCE32UI_EXT 0x8D74 +#define GL_LUMINANCE_ALPHA32UI_EXT 0x8D75 +#define GL_RGBA16UI_EXT 0x8D76 +#define GL_RGB16UI_EXT 0x8D77 +#define GL_ALPHA16UI_EXT 0x8D78 +#define GL_INTENSITY16UI_EXT 0x8D79 +#define GL_LUMINANCE16UI_EXT 0x8D7A +#define GL_LUMINANCE_ALPHA16UI_EXT 0x8D7B +#define GL_RGBA8UI_EXT 0x8D7C +#define GL_RGB8UI_EXT 0x8D7D +#define GL_ALPHA8UI_EXT 0x8D7E +#define GL_INTENSITY8UI_EXT 0x8D7F +#define GL_LUMINANCE8UI_EXT 0x8D80 +#define GL_LUMINANCE_ALPHA8UI_EXT 0x8D81 +#define GL_RGBA32I_EXT 0x8D82 +#define GL_RGB32I_EXT 0x8D83 +#define GL_ALPHA32I_EXT 0x8D84 +#define GL_INTENSITY32I_EXT 0x8D85 +#define GL_LUMINANCE32I_EXT 0x8D86 +#define GL_LUMINANCE_ALPHA32I_EXT 0x8D87 +#define GL_RGBA16I_EXT 0x8D88 +#define GL_RGB16I_EXT 0x8D89 +#define GL_ALPHA16I_EXT 0x8D8A +#define GL_INTENSITY16I_EXT 0x8D8B +#define GL_LUMINANCE16I_EXT 0x8D8C +#define GL_LUMINANCE_ALPHA16I_EXT 0x8D8D +#define GL_RGBA8I_EXT 0x8D8E +#define GL_RGB8I_EXT 0x8D8F +#define GL_ALPHA8I_EXT 0x8D90 +#define GL_INTENSITY8I_EXT 0x8D91 +#define GL_LUMINANCE8I_EXT 0x8D92 +#define GL_LUMINANCE_ALPHA8I_EXT 0x8D93 +#define GL_RED_INTEGER_EXT 0x8D94 +#define GL_GREEN_INTEGER_EXT 0x8D95 +#define GL_BLUE_INTEGER_EXT 0x8D96 +#define GL_ALPHA_INTEGER_EXT 0x8D97 +#define GL_RGB_INTEGER_EXT 0x8D98 +#define GL_RGBA_INTEGER_EXT 0x8D99 +#define GL_BGR_INTEGER_EXT 0x8D9A +#define GL_BGRA_INTEGER_EXT 0x8D9B +#define GL_LUMINANCE_INTEGER_EXT 0x8D9C +#define GL_LUMINANCE_ALPHA_INTEGER_EXT 0x8D9D +#define GL_RGBA_INTEGER_MODE_EXT 0x8D9E +typedef void (APIENTRYP PFNGLTEXPARAMETERIIVEXTPROC) (GLenum target, GLenum pname, const GLint *params); +typedef void (APIENTRYP PFNGLTEXPARAMETERIUIVEXTPROC) (GLenum target, GLenum pname, const GLuint *params); +typedef void (APIENTRYP PFNGLGETTEXPARAMETERIIVEXTPROC) (GLenum target, GLenum pname, GLint *params); +typedef void (APIENTRYP PFNGLGETTEXPARAMETERIUIVEXTPROC) (GLenum target, GLenum pname, GLuint *params); +typedef void (APIENTRYP PFNGLCLEARCOLORIIEXTPROC) (GLint red, GLint green, GLint blue, GLint alpha); +typedef void (APIENTRYP PFNGLCLEARCOLORIUIEXTPROC) (GLuint red, GLuint green, GLuint blue, GLuint alpha); +#ifdef GL_GLEXT_PROTOTYPES +GLAPI void APIENTRY glTexParameterIivEXT (GLenum target, GLenum pname, const GLint *params); +GLAPI void APIENTRY glTexParameterIuivEXT (GLenum target, GLenum pname, const GLuint *params); +GLAPI void APIENTRY glGetTexParameterIivEXT (GLenum target, GLenum pname, GLint *params); +GLAPI void APIENTRY glGetTexParameterIuivEXT (GLenum target, GLenum pname, GLuint *params); +GLAPI void APIENTRY glClearColorIiEXT (GLint red, GLint green, GLint blue, GLint alpha); +GLAPI void APIENTRY glClearColorIuiEXT (GLuint red, GLuint green, GLuint blue, GLuint alpha); +#endif +#endif /* GL_EXT_texture_integer */ + +#ifndef GL_EXT_texture_lod_bias +#define GL_EXT_texture_lod_bias 1 +#define GL_MAX_TEXTURE_LOD_BIAS_EXT 0x84FD +#define GL_TEXTURE_FILTER_CONTROL_EXT 0x8500 +#define GL_TEXTURE_LOD_BIAS_EXT 0x8501 +#endif /* GL_EXT_texture_lod_bias */ + +#ifndef GL_EXT_texture_mirror_clamp +#define GL_EXT_texture_mirror_clamp 1 +#define GL_MIRROR_CLAMP_EXT 0x8742 +#define GL_MIRROR_CLAMP_TO_EDGE_EXT 0x8743 +#define GL_MIRROR_CLAMP_TO_BORDER_EXT 0x8912 +#endif /* GL_EXT_texture_mirror_clamp */ + +#ifndef GL_EXT_texture_object +#define GL_EXT_texture_object 1 +#define GL_TEXTURE_PRIORITY_EXT 0x8066 +#define GL_TEXTURE_RESIDENT_EXT 0x8067 +#define GL_TEXTURE_1D_BINDING_EXT 0x8068 +#define GL_TEXTURE_2D_BINDING_EXT 0x8069 +#define GL_TEXTURE_3D_BINDING_EXT 0x806A +typedef GLboolean (APIENTRYP PFNGLARETEXTURESRESIDENTEXTPROC) (GLsizei n, const GLuint *textures, GLboolean *residences); +typedef void (APIENTRYP PFNGLBINDTEXTUREEXTPROC) (GLenum target, GLuint texture); +typedef void (APIENTRYP PFNGLDELETETEXTURESEXTPROC) (GLsizei n, const GLuint *textures); +typedef void (APIENTRYP PFNGLGENTEXTURESEXTPROC) (GLsizei n, GLuint *textures); +typedef GLboolean (APIENTRYP PFNGLISTEXTUREEXTPROC) (GLuint texture); +typedef void (APIENTRYP PFNGLPRIORITIZETEXTURESEXTPROC) (GLsizei n, const GLuint *textures, const GLclampf *priorities); +#ifdef GL_GLEXT_PROTOTYPES +GLAPI GLboolean APIENTRY glAreTexturesResidentEXT (GLsizei n, const GLuint *textures, GLboolean *residences); +GLAPI void APIENTRY glBindTextureEXT (GLenum target, GLuint texture); +GLAPI void APIENTRY glDeleteTexturesEXT (GLsizei n, const GLuint *textures); +GLAPI void APIENTRY glGenTexturesEXT (GLsizei n, GLuint *textures); +GLAPI GLboolean APIENTRY glIsTextureEXT (GLuint texture); +GLAPI void APIENTRY glPrioritizeTexturesEXT (GLsizei n, const GLuint *textures, const GLclampf *priorities); +#endif +#endif /* GL_EXT_texture_object */ + +#ifndef GL_EXT_texture_perturb_normal +#define GL_EXT_texture_perturb_normal 1 +#define GL_PERTURB_EXT 0x85AE +#define GL_TEXTURE_NORMAL_EXT 0x85AF +typedef void (APIENTRYP PFNGLTEXTURENORMALEXTPROC) (GLenum mode); +#ifdef GL_GLEXT_PROTOTYPES +GLAPI void APIENTRY glTextureNormalEXT (GLenum mode); +#endif +#endif /* GL_EXT_texture_perturb_normal */ + +#ifndef GL_EXT_texture_sRGB +#define GL_EXT_texture_sRGB 1 +#define GL_SRGB_EXT 0x8C40 +#define GL_SRGB8_EXT 0x8C41 +#define GL_SRGB_ALPHA_EXT 0x8C42 +#define GL_SRGB8_ALPHA8_EXT 0x8C43 +#define GL_SLUMINANCE_ALPHA_EXT 0x8C44 +#define GL_SLUMINANCE8_ALPHA8_EXT 0x8C45 +#define GL_SLUMINANCE_EXT 0x8C46 +#define GL_SLUMINANCE8_EXT 0x8C47 +#define GL_COMPRESSED_SRGB_EXT 0x8C48 +#define GL_COMPRESSED_SRGB_ALPHA_EXT 0x8C49 +#define GL_COMPRESSED_SLUMINANCE_EXT 0x8C4A +#define GL_COMPRESSED_SLUMINANCE_ALPHA_EXT 0x8C4B +#define GL_COMPRESSED_SRGB_S3TC_DXT1_EXT 0x8C4C +#define GL_COMPRESSED_SRGB_ALPHA_S3TC_DXT1_EXT 0x8C4D +#define GL_COMPRESSED_SRGB_ALPHA_S3TC_DXT3_EXT 0x8C4E +#define GL_COMPRESSED_SRGB_ALPHA_S3TC_DXT5_EXT 0x8C4F +#endif /* GL_EXT_texture_sRGB */ + +#ifndef GL_EXT_texture_sRGB_decode +#define GL_EXT_texture_sRGB_decode 1 +#define GL_TEXTURE_SRGB_DECODE_EXT 0x8A48 +#define GL_DECODE_EXT 0x8A49 +#define GL_SKIP_DECODE_EXT 0x8A4A +#endif /* GL_EXT_texture_sRGB_decode */ + +#ifndef GL_EXT_texture_shared_exponent +#define GL_EXT_texture_shared_exponent 1 +#define GL_RGB9_E5_EXT 0x8C3D +#define GL_UNSIGNED_INT_5_9_9_9_REV_EXT 0x8C3E +#define GL_TEXTURE_SHARED_SIZE_EXT 0x8C3F +#endif /* GL_EXT_texture_shared_exponent */ + +#ifndef GL_EXT_texture_snorm +#define GL_EXT_texture_snorm 1 +#define GL_ALPHA_SNORM 0x9010 +#define GL_LUMINANCE_SNORM 0x9011 +#define GL_LUMINANCE_ALPHA_SNORM 0x9012 +#define GL_INTENSITY_SNORM 0x9013 +#define GL_ALPHA8_SNORM 0x9014 +#define GL_LUMINANCE8_SNORM 0x9015 +#define GL_LUMINANCE8_ALPHA8_SNORM 0x9016 +#define GL_INTENSITY8_SNORM 0x9017 +#define GL_ALPHA16_SNORM 0x9018 +#define GL_LUMINANCE16_SNORM 0x9019 +#define GL_LUMINANCE16_ALPHA16_SNORM 0x901A +#define GL_INTENSITY16_SNORM 0x901B +#define GL_RED_SNORM 0x8F90 +#define GL_RG_SNORM 0x8F91 +#define GL_RGB_SNORM 0x8F92 +#define GL_RGBA_SNORM 0x8F93 +#endif /* GL_EXT_texture_snorm */ + +#ifndef GL_EXT_texture_swizzle +#define GL_EXT_texture_swizzle 1 +#define GL_TEXTURE_SWIZZLE_R_EXT 0x8E42 +#define GL_TEXTURE_SWIZZLE_G_EXT 0x8E43 +#define GL_TEXTURE_SWIZZLE_B_EXT 0x8E44 +#define GL_TEXTURE_SWIZZLE_A_EXT 0x8E45 +#define GL_TEXTURE_SWIZZLE_RGBA_EXT 0x8E46 +#endif /* GL_EXT_texture_swizzle */ + +#ifndef GL_EXT_timer_query +#define GL_EXT_timer_query 1 +#define GL_TIME_ELAPSED_EXT 0x88BF +typedef void (APIENTRYP PFNGLGETQUERYOBJECTI64VEXTPROC) (GLuint id, GLenum pname, GLint64 *params); +typedef void (APIENTRYP PFNGLGETQUERYOBJECTUI64VEXTPROC) (GLuint id, GLenum pname, GLuint64 *params); +#ifdef GL_GLEXT_PROTOTYPES +GLAPI void APIENTRY glGetQueryObjecti64vEXT (GLuint id, GLenum pname, GLint64 *params); +GLAPI void APIENTRY glGetQueryObjectui64vEXT (GLuint id, GLenum pname, GLuint64 *params); +#endif +#endif /* GL_EXT_timer_query */ + +#ifndef GL_EXT_transform_feedback +#define GL_EXT_transform_feedback 1 +#define GL_TRANSFORM_FEEDBACK_BUFFER_EXT 0x8C8E +#define GL_TRANSFORM_FEEDBACK_BUFFER_START_EXT 0x8C84 +#define GL_TRANSFORM_FEEDBACK_BUFFER_SIZE_EXT 0x8C85 +#define GL_TRANSFORM_FEEDBACK_BUFFER_BINDING_EXT 0x8C8F +#define GL_INTERLEAVED_ATTRIBS_EXT 0x8C8C +#define GL_SEPARATE_ATTRIBS_EXT 0x8C8D +#define GL_PRIMITIVES_GENERATED_EXT 0x8C87 +#define GL_TRANSFORM_FEEDBACK_PRIMITIVES_WRITTEN_EXT 0x8C88 +#define GL_RASTERIZER_DISCARD_EXT 0x8C89 +#define GL_MAX_TRANSFORM_FEEDBACK_INTERLEAVED_COMPONENTS_EXT 0x8C8A +#define GL_MAX_TRANSFORM_FEEDBACK_SEPARATE_ATTRIBS_EXT 0x8C8B +#define GL_MAX_TRANSFORM_FEEDBACK_SEPARATE_COMPONENTS_EXT 0x8C80 +#define GL_TRANSFORM_FEEDBACK_VARYINGS_EXT 0x8C83 +#define GL_TRANSFORM_FEEDBACK_BUFFER_MODE_EXT 0x8C7F +#define GL_TRANSFORM_FEEDBACK_VARYING_MAX_LENGTH_EXT 0x8C76 +typedef void (APIENTRYP PFNGLBEGINTRANSFORMFEEDBACKEXTPROC) (GLenum primitiveMode); +typedef void (APIENTRYP PFNGLENDTRANSFORMFEEDBACKEXTPROC) (void); +typedef void (APIENTRYP PFNGLBINDBUFFERRANGEEXTPROC) (GLenum target, GLuint index, GLuint buffer, GLintptr offset, GLsizeiptr size); +typedef void (APIENTRYP PFNGLBINDBUFFEROFFSETEXTPROC) (GLenum target, GLuint index, GLuint buffer, GLintptr offset); +typedef void (APIENTRYP PFNGLBINDBUFFERBASEEXTPROC) (GLenum target, GLuint index, GLuint buffer); +typedef void (APIENTRYP PFNGLTRANSFORMFEEDBACKVARYINGSEXTPROC) (GLuint program, GLsizei count, const GLchar *const*varyings, GLenum bufferMode); +typedef void (APIENTRYP PFNGLGETTRANSFORMFEEDBACKVARYINGEXTPROC) (GLuint program, GLuint index, GLsizei bufSize, GLsizei *length, GLsizei *size, GLenum *type, GLchar *name); +#ifdef GL_GLEXT_PROTOTYPES +GLAPI void APIENTRY glBeginTransformFeedbackEXT (GLenum primitiveMode); +GLAPI void APIENTRY glEndTransformFeedbackEXT (void); +GLAPI void APIENTRY glBindBufferRangeEXT (GLenum target, GLuint index, GLuint buffer, GLintptr offset, GLsizeiptr size); +GLAPI void APIENTRY glBindBufferOffsetEXT (GLenum target, GLuint index, GLuint buffer, GLintptr offset); +GLAPI void APIENTRY glBindBufferBaseEXT (GLenum target, GLuint index, GLuint buffer); +GLAPI void APIENTRY glTransformFeedbackVaryingsEXT (GLuint program, GLsizei count, const GLchar *const*varyings, GLenum bufferMode); +GLAPI void APIENTRY glGetTransformFeedbackVaryingEXT (GLuint program, GLuint index, GLsizei bufSize, GLsizei *length, GLsizei *size, GLenum *type, GLchar *name); +#endif +#endif /* GL_EXT_transform_feedback */ + +#ifndef GL_EXT_vertex_array +#define GL_EXT_vertex_array 1 +#define GL_VERTEX_ARRAY_EXT 0x8074 +#define GL_NORMAL_ARRAY_EXT 0x8075 +#define GL_COLOR_ARRAY_EXT 0x8076 +#define GL_INDEX_ARRAY_EXT 0x8077 +#define GL_TEXTURE_COORD_ARRAY_EXT 0x8078 +#define GL_EDGE_FLAG_ARRAY_EXT 0x8079 +#define GL_VERTEX_ARRAY_SIZE_EXT 0x807A +#define GL_VERTEX_ARRAY_TYPE_EXT 0x807B +#define GL_VERTEX_ARRAY_STRIDE_EXT 0x807C +#define GL_VERTEX_ARRAY_COUNT_EXT 0x807D +#define GL_NORMAL_ARRAY_TYPE_EXT 0x807E +#define GL_NORMAL_ARRAY_STRIDE_EXT 0x807F +#define GL_NORMAL_ARRAY_COUNT_EXT 0x8080 +#define GL_COLOR_ARRAY_SIZE_EXT 0x8081 +#define GL_COLOR_ARRAY_TYPE_EXT 0x8082 +#define GL_COLOR_ARRAY_STRIDE_EXT 0x8083 +#define GL_COLOR_ARRAY_COUNT_EXT 0x8084 +#define GL_INDEX_ARRAY_TYPE_EXT 0x8085 +#define GL_INDEX_ARRAY_STRIDE_EXT 0x8086 +#define GL_INDEX_ARRAY_COUNT_EXT 0x8087 +#define GL_TEXTURE_COORD_ARRAY_SIZE_EXT 0x8088 +#define GL_TEXTURE_COORD_ARRAY_TYPE_EXT 0x8089 +#define GL_TEXTURE_COORD_ARRAY_STRIDE_EXT 0x808A +#define GL_TEXTURE_COORD_ARRAY_COUNT_EXT 0x808B +#define GL_EDGE_FLAG_ARRAY_STRIDE_EXT 0x808C +#define GL_EDGE_FLAG_ARRAY_COUNT_EXT 0x808D +#define GL_VERTEX_ARRAY_POINTER_EXT 0x808E +#define GL_NORMAL_ARRAY_POINTER_EXT 0x808F +#define GL_COLOR_ARRAY_POINTER_EXT 0x8090 +#define GL_INDEX_ARRAY_POINTER_EXT 0x8091 +#define GL_TEXTURE_COORD_ARRAY_POINTER_EXT 0x8092 +#define GL_EDGE_FLAG_ARRAY_POINTER_EXT 0x8093 +typedef void (APIENTRYP PFNGLARRAYELEMENTEXTPROC) (GLint i); +typedef void (APIENTRYP PFNGLCOLORPOINTEREXTPROC) (GLint size, GLenum type, GLsizei stride, GLsizei count, const void *pointer); +typedef void (APIENTRYP PFNGLDRAWARRAYSEXTPROC) (GLenum mode, GLint first, GLsizei count); +typedef void (APIENTRYP PFNGLEDGEFLAGPOINTEREXTPROC) (GLsizei stride, GLsizei count, const GLboolean *pointer); +typedef void (APIENTRYP PFNGLGETPOINTERVEXTPROC) (GLenum pname, void **params); +typedef void (APIENTRYP PFNGLINDEXPOINTEREXTPROC) (GLenum type, GLsizei stride, GLsizei count, const void *pointer); +typedef void (APIENTRYP PFNGLNORMALPOINTEREXTPROC) (GLenum type, GLsizei stride, GLsizei count, const void *pointer); +typedef void (APIENTRYP PFNGLTEXCOORDPOINTEREXTPROC) (GLint size, GLenum type, GLsizei stride, GLsizei count, const void *pointer); +typedef void (APIENTRYP PFNGLVERTEXPOINTEREXTPROC) (GLint size, GLenum type, GLsizei stride, GLsizei count, const void *pointer); +#ifdef GL_GLEXT_PROTOTYPES +GLAPI void APIENTRY glArrayElementEXT (GLint i); +GLAPI void APIENTRY glColorPointerEXT (GLint size, GLenum type, GLsizei stride, GLsizei count, const void *pointer); +GLAPI void APIENTRY glDrawArraysEXT (GLenum mode, GLint first, GLsizei count); +GLAPI void APIENTRY glEdgeFlagPointerEXT (GLsizei stride, GLsizei count, const GLboolean *pointer); +GLAPI void APIENTRY glGetPointervEXT (GLenum pname, void **params); +GLAPI void APIENTRY glIndexPointerEXT (GLenum type, GLsizei stride, GLsizei count, const void *pointer); +GLAPI void APIENTRY glNormalPointerEXT (GLenum type, GLsizei stride, GLsizei count, const void *pointer); +GLAPI void APIENTRY glTexCoordPointerEXT (GLint size, GLenum type, GLsizei stride, GLsizei count, const void *pointer); +GLAPI void APIENTRY glVertexPointerEXT (GLint size, GLenum type, GLsizei stride, GLsizei count, const void *pointer); +#endif +#endif /* GL_EXT_vertex_array */ + +#ifndef GL_EXT_vertex_array_bgra +#define GL_EXT_vertex_array_bgra 1 +#endif /* GL_EXT_vertex_array_bgra */ + +#ifndef GL_EXT_vertex_attrib_64bit +#define GL_EXT_vertex_attrib_64bit 1 +#define GL_DOUBLE_VEC2_EXT 0x8FFC +#define GL_DOUBLE_VEC3_EXT 0x8FFD +#define GL_DOUBLE_VEC4_EXT 0x8FFE +#define GL_DOUBLE_MAT2_EXT 0x8F46 +#define GL_DOUBLE_MAT3_EXT 0x8F47 +#define GL_DOUBLE_MAT4_EXT 0x8F48 +#define GL_DOUBLE_MAT2x3_EXT 0x8F49 +#define GL_DOUBLE_MAT2x4_EXT 0x8F4A +#define GL_DOUBLE_MAT3x2_EXT 0x8F4B +#define GL_DOUBLE_MAT3x4_EXT 0x8F4C +#define GL_DOUBLE_MAT4x2_EXT 0x8F4D +#define GL_DOUBLE_MAT4x3_EXT 0x8F4E +typedef void (APIENTRYP PFNGLVERTEXATTRIBL1DEXTPROC) (GLuint index, GLdouble x); +typedef void (APIENTRYP PFNGLVERTEXATTRIBL2DEXTPROC) (GLuint index, GLdouble x, GLdouble y); +typedef void (APIENTRYP PFNGLVERTEXATTRIBL3DEXTPROC) (GLuint index, GLdouble x, GLdouble y, GLdouble z); +typedef void (APIENTRYP PFNGLVERTEXATTRIBL4DEXTPROC) (GLuint index, GLdouble x, GLdouble y, GLdouble z, GLdouble w); +typedef void (APIENTRYP PFNGLVERTEXATTRIBL1DVEXTPROC) (GLuint index, const GLdouble *v); +typedef void (APIENTRYP PFNGLVERTEXATTRIBL2DVEXTPROC) (GLuint index, const GLdouble *v); +typedef void (APIENTRYP PFNGLVERTEXATTRIBL3DVEXTPROC) (GLuint index, const GLdouble *v); +typedef void (APIENTRYP PFNGLVERTEXATTRIBL4DVEXTPROC) (GLuint index, const GLdouble *v); +typedef void (APIENTRYP PFNGLVERTEXATTRIBLPOINTEREXTPROC) (GLuint index, GLint size, GLenum type, GLsizei stride, const void *pointer); +typedef void (APIENTRYP PFNGLGETVERTEXATTRIBLDVEXTPROC) (GLuint index, GLenum pname, GLdouble *params); +#ifdef GL_GLEXT_PROTOTYPES +GLAPI void APIENTRY glVertexAttribL1dEXT (GLuint index, GLdouble x); +GLAPI void APIENTRY glVertexAttribL2dEXT (GLuint index, GLdouble x, GLdouble y); +GLAPI void APIENTRY glVertexAttribL3dEXT (GLuint index, GLdouble x, GLdouble y, GLdouble z); +GLAPI void APIENTRY glVertexAttribL4dEXT (GLuint index, GLdouble x, GLdouble y, GLdouble z, GLdouble w); +GLAPI void APIENTRY glVertexAttribL1dvEXT (GLuint index, const GLdouble *v); +GLAPI void APIENTRY glVertexAttribL2dvEXT (GLuint index, const GLdouble *v); +GLAPI void APIENTRY glVertexAttribL3dvEXT (GLuint index, const GLdouble *v); +GLAPI void APIENTRY glVertexAttribL4dvEXT (GLuint index, const GLdouble *v); +GLAPI void APIENTRY glVertexAttribLPointerEXT (GLuint index, GLint size, GLenum type, GLsizei stride, const void *pointer); +GLAPI void APIENTRY glGetVertexAttribLdvEXT (GLuint index, GLenum pname, GLdouble *params); +#endif +#endif /* GL_EXT_vertex_attrib_64bit */ + +#ifndef GL_EXT_vertex_shader +#define GL_EXT_vertex_shader 1 +#define GL_VERTEX_SHADER_EXT 0x8780 +#define GL_VERTEX_SHADER_BINDING_EXT 0x8781 +#define GL_OP_INDEX_EXT 0x8782 +#define GL_OP_NEGATE_EXT 0x8783 +#define GL_OP_DOT3_EXT 0x8784 +#define GL_OP_DOT4_EXT 0x8785 +#define GL_OP_MUL_EXT 0x8786 +#define GL_OP_ADD_EXT 0x8787 +#define GL_OP_MADD_EXT 0x8788 +#define GL_OP_FRAC_EXT 0x8789 +#define GL_OP_MAX_EXT 0x878A +#define GL_OP_MIN_EXT 0x878B +#define GL_OP_SET_GE_EXT 0x878C +#define GL_OP_SET_LT_EXT 0x878D +#define GL_OP_CLAMP_EXT 0x878E +#define GL_OP_FLOOR_EXT 0x878F +#define GL_OP_ROUND_EXT 0x8790 +#define GL_OP_EXP_BASE_2_EXT 0x8791 +#define GL_OP_LOG_BASE_2_EXT 0x8792 +#define GL_OP_POWER_EXT 0x8793 +#define GL_OP_RECIP_EXT 0x8794 +#define GL_OP_RECIP_SQRT_EXT 0x8795 +#define GL_OP_SUB_EXT 0x8796 +#define GL_OP_CROSS_PRODUCT_EXT 0x8797 +#define GL_OP_MULTIPLY_MATRIX_EXT 0x8798 +#define GL_OP_MOV_EXT 0x8799 +#define GL_OUTPUT_VERTEX_EXT 0x879A +#define GL_OUTPUT_COLOR0_EXT 0x879B +#define GL_OUTPUT_COLOR1_EXT 0x879C +#define GL_OUTPUT_TEXTURE_COORD0_EXT 0x879D +#define GL_OUTPUT_TEXTURE_COORD1_EXT 0x879E +#define GL_OUTPUT_TEXTURE_COORD2_EXT 0x879F +#define GL_OUTPUT_TEXTURE_COORD3_EXT 0x87A0 +#define GL_OUTPUT_TEXTURE_COORD4_EXT 0x87A1 +#define GL_OUTPUT_TEXTURE_COORD5_EXT 0x87A2 +#define GL_OUTPUT_TEXTURE_COORD6_EXT 0x87A3 +#define GL_OUTPUT_TEXTURE_COORD7_EXT 0x87A4 +#define GL_OUTPUT_TEXTURE_COORD8_EXT 0x87A5 +#define GL_OUTPUT_TEXTURE_COORD9_EXT 0x87A6 +#define GL_OUTPUT_TEXTURE_COORD10_EXT 0x87A7 +#define GL_OUTPUT_TEXTURE_COORD11_EXT 0x87A8 +#define GL_OUTPUT_TEXTURE_COORD12_EXT 0x87A9 +#define GL_OUTPUT_TEXTURE_COORD13_EXT 0x87AA +#define GL_OUTPUT_TEXTURE_COORD14_EXT 0x87AB +#define GL_OUTPUT_TEXTURE_COORD15_EXT 0x87AC +#define GL_OUTPUT_TEXTURE_COORD16_EXT 0x87AD +#define GL_OUTPUT_TEXTURE_COORD17_EXT 0x87AE +#define GL_OUTPUT_TEXTURE_COORD18_EXT 0x87AF +#define GL_OUTPUT_TEXTURE_COORD19_EXT 0x87B0 +#define GL_OUTPUT_TEXTURE_COORD20_EXT 0x87B1 +#define GL_OUTPUT_TEXTURE_COORD21_EXT 0x87B2 +#define GL_OUTPUT_TEXTURE_COORD22_EXT 0x87B3 +#define GL_OUTPUT_TEXTURE_COORD23_EXT 0x87B4 +#define GL_OUTPUT_TEXTURE_COORD24_EXT 0x87B5 +#define GL_OUTPUT_TEXTURE_COORD25_EXT 0x87B6 +#define GL_OUTPUT_TEXTURE_COORD26_EXT 0x87B7 +#define GL_OUTPUT_TEXTURE_COORD27_EXT 0x87B8 +#define GL_OUTPUT_TEXTURE_COORD28_EXT 0x87B9 +#define GL_OUTPUT_TEXTURE_COORD29_EXT 0x87BA +#define GL_OUTPUT_TEXTURE_COORD30_EXT 0x87BB +#define GL_OUTPUT_TEXTURE_COORD31_EXT 0x87BC +#define GL_OUTPUT_FOG_EXT 0x87BD +#define GL_SCALAR_EXT 0x87BE +#define GL_VECTOR_EXT 0x87BF +#define GL_MATRIX_EXT 0x87C0 +#define GL_VARIANT_EXT 0x87C1 +#define GL_INVARIANT_EXT 0x87C2 +#define GL_LOCAL_CONSTANT_EXT 0x87C3 +#define GL_LOCAL_EXT 0x87C4 +#define GL_MAX_VERTEX_SHADER_INSTRUCTIONS_EXT 0x87C5 +#define GL_MAX_VERTEX_SHADER_VARIANTS_EXT 0x87C6 +#define GL_MAX_VERTEX_SHADER_INVARIANTS_EXT 0x87C7 +#define GL_MAX_VERTEX_SHADER_LOCAL_CONSTANTS_EXT 0x87C8 +#define GL_MAX_VERTEX_SHADER_LOCALS_EXT 0x87C9 +#define GL_MAX_OPTIMIZED_VERTEX_SHADER_INSTRUCTIONS_EXT 0x87CA +#define GL_MAX_OPTIMIZED_VERTEX_SHADER_VARIANTS_EXT 0x87CB +#define GL_MAX_OPTIMIZED_VERTEX_SHADER_LOCAL_CONSTANTS_EXT 0x87CC +#define GL_MAX_OPTIMIZED_VERTEX_SHADER_INVARIANTS_EXT 0x87CD +#define GL_MAX_OPTIMIZED_VERTEX_SHADER_LOCALS_EXT 0x87CE +#define GL_VERTEX_SHADER_INSTRUCTIONS_EXT 0x87CF +#define GL_VERTEX_SHADER_VARIANTS_EXT 0x87D0 +#define GL_VERTEX_SHADER_INVARIANTS_EXT 0x87D1 +#define GL_VERTEX_SHADER_LOCAL_CONSTANTS_EXT 0x87D2 +#define GL_VERTEX_SHADER_LOCALS_EXT 0x87D3 +#define GL_VERTEX_SHADER_OPTIMIZED_EXT 0x87D4 +#define GL_X_EXT 0x87D5 +#define GL_Y_EXT 0x87D6 +#define GL_Z_EXT 0x87D7 +#define GL_W_EXT 0x87D8 +#define GL_NEGATIVE_X_EXT 0x87D9 +#define GL_NEGATIVE_Y_EXT 0x87DA +#define GL_NEGATIVE_Z_EXT 0x87DB +#define GL_NEGATIVE_W_EXT 0x87DC +#define GL_ZERO_EXT 0x87DD +#define GL_ONE_EXT 0x87DE +#define GL_NEGATIVE_ONE_EXT 0x87DF +#define GL_NORMALIZED_RANGE_EXT 0x87E0 +#define GL_FULL_RANGE_EXT 0x87E1 +#define GL_CURRENT_VERTEX_EXT 0x87E2 +#define GL_MVP_MATRIX_EXT 0x87E3 +#define GL_VARIANT_VALUE_EXT 0x87E4 +#define GL_VARIANT_DATATYPE_EXT 0x87E5 +#define GL_VARIANT_ARRAY_STRIDE_EXT 0x87E6 +#define GL_VARIANT_ARRAY_TYPE_EXT 0x87E7 +#define GL_VARIANT_ARRAY_EXT 0x87E8 +#define GL_VARIANT_ARRAY_POINTER_EXT 0x87E9 +#define GL_INVARIANT_VALUE_EXT 0x87EA +#define GL_INVARIANT_DATATYPE_EXT 0x87EB +#define GL_LOCAL_CONSTANT_VALUE_EXT 0x87EC +#define GL_LOCAL_CONSTANT_DATATYPE_EXT 0x87ED +typedef void (APIENTRYP PFNGLBEGINVERTEXSHADEREXTPROC) (void); +typedef void (APIENTRYP PFNGLENDVERTEXSHADEREXTPROC) (void); +typedef void (APIENTRYP PFNGLBINDVERTEXSHADEREXTPROC) (GLuint id); +typedef GLuint (APIENTRYP PFNGLGENVERTEXSHADERSEXTPROC) (GLuint range); +typedef void (APIENTRYP PFNGLDELETEVERTEXSHADEREXTPROC) (GLuint id); +typedef void (APIENTRYP PFNGLSHADEROP1EXTPROC) (GLenum op, GLuint res, GLuint arg1); +typedef void (APIENTRYP PFNGLSHADEROP2EXTPROC) (GLenum op, GLuint res, GLuint arg1, GLuint arg2); +typedef void (APIENTRYP PFNGLSHADEROP3EXTPROC) (GLenum op, GLuint res, GLuint arg1, GLuint arg2, GLuint arg3); +typedef void (APIENTRYP PFNGLSWIZZLEEXTPROC) (GLuint res, GLuint in, GLenum outX, GLenum outY, GLenum outZ, GLenum outW); +typedef void (APIENTRYP PFNGLWRITEMASKEXTPROC) (GLuint res, GLuint in, GLenum outX, GLenum outY, GLenum outZ, GLenum outW); +typedef void (APIENTRYP PFNGLINSERTCOMPONENTEXTPROC) (GLuint res, GLuint src, GLuint num); +typedef void (APIENTRYP PFNGLEXTRACTCOMPONENTEXTPROC) (GLuint res, GLuint src, GLuint num); +typedef GLuint (APIENTRYP PFNGLGENSYMBOLSEXTPROC) (GLenum datatype, GLenum storagetype, GLenum range, GLuint components); +typedef void (APIENTRYP PFNGLSETINVARIANTEXTPROC) (GLuint id, GLenum type, const void *addr); +typedef void (APIENTRYP PFNGLSETLOCALCONSTANTEXTPROC) (GLuint id, GLenum type, const void *addr); +typedef void (APIENTRYP PFNGLVARIANTBVEXTPROC) (GLuint id, const GLbyte *addr); +typedef void (APIENTRYP PFNGLVARIANTSVEXTPROC) (GLuint id, const GLshort *addr); +typedef void (APIENTRYP PFNGLVARIANTIVEXTPROC) (GLuint id, const GLint *addr); +typedef void (APIENTRYP PFNGLVARIANTFVEXTPROC) (GLuint id, const GLfloat *addr); +typedef void (APIENTRYP PFNGLVARIANTDVEXTPROC) (GLuint id, const GLdouble *addr); +typedef void (APIENTRYP PFNGLVARIANTUBVEXTPROC) (GLuint id, const GLubyte *addr); +typedef void (APIENTRYP PFNGLVARIANTUSVEXTPROC) (GLuint id, const GLushort *addr); +typedef void (APIENTRYP PFNGLVARIANTUIVEXTPROC) (GLuint id, const GLuint *addr); +typedef void (APIENTRYP PFNGLVARIANTPOINTEREXTPROC) (GLuint id, GLenum type, GLuint stride, const void *addr); +typedef void (APIENTRYP PFNGLENABLEVARIANTCLIENTSTATEEXTPROC) (GLuint id); +typedef void (APIENTRYP PFNGLDISABLEVARIANTCLIENTSTATEEXTPROC) (GLuint id); +typedef GLuint (APIENTRYP PFNGLBINDLIGHTPARAMETEREXTPROC) (GLenum light, GLenum value); +typedef GLuint (APIENTRYP PFNGLBINDMATERIALPARAMETEREXTPROC) (GLenum face, GLenum value); +typedef GLuint (APIENTRYP PFNGLBINDTEXGENPARAMETEREXTPROC) (GLenum unit, GLenum coord, GLenum value); +typedef GLuint (APIENTRYP PFNGLBINDTEXTUREUNITPARAMETEREXTPROC) (GLenum unit, GLenum value); +typedef GLuint (APIENTRYP PFNGLBINDPARAMETEREXTPROC) (GLenum value); +typedef GLboolean (APIENTRYP PFNGLISVARIANTENABLEDEXTPROC) (GLuint id, GLenum cap); +typedef void (APIENTRYP PFNGLGETVARIANTBOOLEANVEXTPROC) (GLuint id, GLenum value, GLboolean *data); +typedef void (APIENTRYP PFNGLGETVARIANTINTEGERVEXTPROC) (GLuint id, GLenum value, GLint *data); +typedef void (APIENTRYP PFNGLGETVARIANTFLOATVEXTPROC) (GLuint id, GLenum value, GLfloat *data); +typedef void (APIENTRYP PFNGLGETVARIANTPOINTERVEXTPROC) (GLuint id, GLenum value, void **data); +typedef void (APIENTRYP PFNGLGETINVARIANTBOOLEANVEXTPROC) (GLuint id, GLenum value, GLboolean *data); +typedef void (APIENTRYP PFNGLGETINVARIANTINTEGERVEXTPROC) (GLuint id, GLenum value, GLint *data); +typedef void (APIENTRYP PFNGLGETINVARIANTFLOATVEXTPROC) (GLuint id, GLenum value, GLfloat *data); +typedef void (APIENTRYP PFNGLGETLOCALCONSTANTBOOLEANVEXTPROC) (GLuint id, GLenum value, GLboolean *data); +typedef void (APIENTRYP PFNGLGETLOCALCONSTANTINTEGERVEXTPROC) (GLuint id, GLenum value, GLint *data); +typedef void (APIENTRYP PFNGLGETLOCALCONSTANTFLOATVEXTPROC) (GLuint id, GLenum value, GLfloat *data); +#ifdef GL_GLEXT_PROTOTYPES +GLAPI void APIENTRY glBeginVertexShaderEXT (void); +GLAPI void APIENTRY glEndVertexShaderEXT (void); +GLAPI void APIENTRY glBindVertexShaderEXT (GLuint id); +GLAPI GLuint APIENTRY glGenVertexShadersEXT (GLuint range); +GLAPI void APIENTRY glDeleteVertexShaderEXT (GLuint id); +GLAPI void APIENTRY glShaderOp1EXT (GLenum op, GLuint res, GLuint arg1); +GLAPI void APIENTRY glShaderOp2EXT (GLenum op, GLuint res, GLuint arg1, GLuint arg2); +GLAPI void APIENTRY glShaderOp3EXT (GLenum op, GLuint res, GLuint arg1, GLuint arg2, GLuint arg3); +GLAPI void APIENTRY glSwizzleEXT (GLuint res, GLuint in, GLenum outX, GLenum outY, GLenum outZ, GLenum outW); +GLAPI void APIENTRY glWriteMaskEXT (GLuint res, GLuint in, GLenum outX, GLenum outY, GLenum outZ, GLenum outW); +GLAPI void APIENTRY glInsertComponentEXT (GLuint res, GLuint src, GLuint num); +GLAPI void APIENTRY glExtractComponentEXT (GLuint res, GLuint src, GLuint num); +GLAPI GLuint APIENTRY glGenSymbolsEXT (GLenum datatype, GLenum storagetype, GLenum range, GLuint components); +GLAPI void APIENTRY glSetInvariantEXT (GLuint id, GLenum type, const void *addr); +GLAPI void APIENTRY glSetLocalConstantEXT (GLuint id, GLenum type, const void *addr); +GLAPI void APIENTRY glVariantbvEXT (GLuint id, const GLbyte *addr); +GLAPI void APIENTRY glVariantsvEXT (GLuint id, const GLshort *addr); +GLAPI void APIENTRY glVariantivEXT (GLuint id, const GLint *addr); +GLAPI void APIENTRY glVariantfvEXT (GLuint id, const GLfloat *addr); +GLAPI void APIENTRY glVariantdvEXT (GLuint id, const GLdouble *addr); +GLAPI void APIENTRY glVariantubvEXT (GLuint id, const GLubyte *addr); +GLAPI void APIENTRY glVariantusvEXT (GLuint id, const GLushort *addr); +GLAPI void APIENTRY glVariantuivEXT (GLuint id, const GLuint *addr); +GLAPI void APIENTRY glVariantPointerEXT (GLuint id, GLenum type, GLuint stride, const void *addr); +GLAPI void APIENTRY glEnableVariantClientStateEXT (GLuint id); +GLAPI void APIENTRY glDisableVariantClientStateEXT (GLuint id); +GLAPI GLuint APIENTRY glBindLightParameterEXT (GLenum light, GLenum value); +GLAPI GLuint APIENTRY glBindMaterialParameterEXT (GLenum face, GLenum value); +GLAPI GLuint APIENTRY glBindTexGenParameterEXT (GLenum unit, GLenum coord, GLenum value); +GLAPI GLuint APIENTRY glBindTextureUnitParameterEXT (GLenum unit, GLenum value); +GLAPI GLuint APIENTRY glBindParameterEXT (GLenum value); +GLAPI GLboolean APIENTRY glIsVariantEnabledEXT (GLuint id, GLenum cap); +GLAPI void APIENTRY glGetVariantBooleanvEXT (GLuint id, GLenum value, GLboolean *data); +GLAPI void APIENTRY glGetVariantIntegervEXT (GLuint id, GLenum value, GLint *data); +GLAPI void APIENTRY glGetVariantFloatvEXT (GLuint id, GLenum value, GLfloat *data); +GLAPI void APIENTRY glGetVariantPointervEXT (GLuint id, GLenum value, void **data); +GLAPI void APIENTRY glGetInvariantBooleanvEXT (GLuint id, GLenum value, GLboolean *data); +GLAPI void APIENTRY glGetInvariantIntegervEXT (GLuint id, GLenum value, GLint *data); +GLAPI void APIENTRY glGetInvariantFloatvEXT (GLuint id, GLenum value, GLfloat *data); +GLAPI void APIENTRY glGetLocalConstantBooleanvEXT (GLuint id, GLenum value, GLboolean *data); +GLAPI void APIENTRY glGetLocalConstantIntegervEXT (GLuint id, GLenum value, GLint *data); +GLAPI void APIENTRY glGetLocalConstantFloatvEXT (GLuint id, GLenum value, GLfloat *data); +#endif +#endif /* GL_EXT_vertex_shader */ + +#ifndef GL_EXT_vertex_weighting +#define GL_EXT_vertex_weighting 1 +#define GL_MODELVIEW0_STACK_DEPTH_EXT 0x0BA3 +#define GL_MODELVIEW1_STACK_DEPTH_EXT 0x8502 +#define GL_MODELVIEW0_MATRIX_EXT 0x0BA6 +#define GL_MODELVIEW1_MATRIX_EXT 0x8506 +#define GL_VERTEX_WEIGHTING_EXT 0x8509 +#define GL_MODELVIEW0_EXT 0x1700 +#define GL_MODELVIEW1_EXT 0x850A +#define GL_CURRENT_VERTEX_WEIGHT_EXT 0x850B +#define GL_VERTEX_WEIGHT_ARRAY_EXT 0x850C +#define GL_VERTEX_WEIGHT_ARRAY_SIZE_EXT 0x850D +#define GL_VERTEX_WEIGHT_ARRAY_TYPE_EXT 0x850E +#define GL_VERTEX_WEIGHT_ARRAY_STRIDE_EXT 0x850F +#define GL_VERTEX_WEIGHT_ARRAY_POINTER_EXT 0x8510 +typedef void (APIENTRYP PFNGLVERTEXWEIGHTFEXTPROC) (GLfloat weight); +typedef void (APIENTRYP PFNGLVERTEXWEIGHTFVEXTPROC) (const GLfloat *weight); +typedef void (APIENTRYP PFNGLVERTEXWEIGHTPOINTEREXTPROC) (GLint size, GLenum type, GLsizei stride, const void *pointer); +#ifdef GL_GLEXT_PROTOTYPES +GLAPI void APIENTRY glVertexWeightfEXT (GLfloat weight); +GLAPI void APIENTRY glVertexWeightfvEXT (const GLfloat *weight); +GLAPI void APIENTRY glVertexWeightPointerEXT (GLint size, GLenum type, GLsizei stride, const void *pointer); +#endif +#endif /* GL_EXT_vertex_weighting */ + +#ifndef GL_EXT_x11_sync_object +#define GL_EXT_x11_sync_object 1 +#define GL_SYNC_X11_FENCE_EXT 0x90E1 +typedef GLsync (APIENTRYP PFNGLIMPORTSYNCEXTPROC) (GLenum external_sync_type, GLintptr external_sync, GLbitfield flags); +#ifdef GL_GLEXT_PROTOTYPES +GLAPI GLsync APIENTRY glImportSyncEXT (GLenum external_sync_type, GLintptr external_sync, GLbitfield flags); +#endif +#endif /* GL_EXT_x11_sync_object */ + +#ifndef GL_GREMEDY_frame_terminator +#define GL_GREMEDY_frame_terminator 1 +typedef void (APIENTRYP PFNGLFRAMETERMINATORGREMEDYPROC) (void); +#ifdef GL_GLEXT_PROTOTYPES +GLAPI void APIENTRY glFrameTerminatorGREMEDY (void); +#endif +#endif /* GL_GREMEDY_frame_terminator */ + +#ifndef GL_GREMEDY_string_marker +#define GL_GREMEDY_string_marker 1 +typedef void (APIENTRYP PFNGLSTRINGMARKERGREMEDYPROC) (GLsizei len, const void *string); +#ifdef GL_GLEXT_PROTOTYPES +GLAPI void APIENTRY glStringMarkerGREMEDY (GLsizei len, const void *string); +#endif +#endif /* GL_GREMEDY_string_marker */ + +#ifndef GL_HP_convolution_border_modes +#define GL_HP_convolution_border_modes 1 +#define GL_IGNORE_BORDER_HP 0x8150 +#define GL_CONSTANT_BORDER_HP 0x8151 +#define GL_REPLICATE_BORDER_HP 0x8153 +#define GL_CONVOLUTION_BORDER_COLOR_HP 0x8154 +#endif /* GL_HP_convolution_border_modes */ + +#ifndef GL_HP_image_transform +#define GL_HP_image_transform 1 +#define GL_IMAGE_SCALE_X_HP 0x8155 +#define GL_IMAGE_SCALE_Y_HP 0x8156 +#define GL_IMAGE_TRANSLATE_X_HP 0x8157 +#define GL_IMAGE_TRANSLATE_Y_HP 0x8158 +#define GL_IMAGE_ROTATE_ANGLE_HP 0x8159 +#define GL_IMAGE_ROTATE_ORIGIN_X_HP 0x815A +#define GL_IMAGE_ROTATE_ORIGIN_Y_HP 0x815B +#define GL_IMAGE_MAG_FILTER_HP 0x815C +#define GL_IMAGE_MIN_FILTER_HP 0x815D +#define GL_IMAGE_CUBIC_WEIGHT_HP 0x815E +#define GL_CUBIC_HP 0x815F +#define GL_AVERAGE_HP 0x8160 +#define GL_IMAGE_TRANSFORM_2D_HP 0x8161 +#define GL_POST_IMAGE_TRANSFORM_COLOR_TABLE_HP 0x8162 +#define GL_PROXY_POST_IMAGE_TRANSFORM_COLOR_TABLE_HP 0x8163 +typedef void (APIENTRYP PFNGLIMAGETRANSFORMPARAMETERIHPPROC) (GLenum target, GLenum pname, GLint param); +typedef void (APIENTRYP PFNGLIMAGETRANSFORMPARAMETERFHPPROC) (GLenum target, GLenum pname, GLfloat param); +typedef void (APIENTRYP PFNGLIMAGETRANSFORMPARAMETERIVHPPROC) (GLenum target, GLenum pname, const GLint *params); +typedef void (APIENTRYP PFNGLIMAGETRANSFORMPARAMETERFVHPPROC) (GLenum target, GLenum pname, const GLfloat *params); +typedef void (APIENTRYP PFNGLGETIMAGETRANSFORMPARAMETERIVHPPROC) (GLenum target, GLenum pname, GLint *params); +typedef void (APIENTRYP PFNGLGETIMAGETRANSFORMPARAMETERFVHPPROC) (GLenum target, GLenum pname, GLfloat *params); +#ifdef GL_GLEXT_PROTOTYPES +GLAPI void APIENTRY glImageTransformParameteriHP (GLenum target, GLenum pname, GLint param); +GLAPI void APIENTRY glImageTransformParameterfHP (GLenum target, GLenum pname, GLfloat param); +GLAPI void APIENTRY glImageTransformParameterivHP (GLenum target, GLenum pname, const GLint *params); +GLAPI void APIENTRY glImageTransformParameterfvHP (GLenum target, GLenum pname, const GLfloat *params); +GLAPI void APIENTRY glGetImageTransformParameterivHP (GLenum target, GLenum pname, GLint *params); +GLAPI void APIENTRY glGetImageTransformParameterfvHP (GLenum target, GLenum pname, GLfloat *params); +#endif +#endif /* GL_HP_image_transform */ + +#ifndef GL_HP_occlusion_test +#define GL_HP_occlusion_test 1 +#define GL_OCCLUSION_TEST_HP 0x8165 +#define GL_OCCLUSION_TEST_RESULT_HP 0x8166 +#endif /* GL_HP_occlusion_test */ + +#ifndef GL_HP_texture_lighting +#define GL_HP_texture_lighting 1 +#define GL_TEXTURE_LIGHTING_MODE_HP 0x8167 +#define GL_TEXTURE_POST_SPECULAR_HP 0x8168 +#define GL_TEXTURE_PRE_SPECULAR_HP 0x8169 +#endif /* GL_HP_texture_lighting */ + +#ifndef GL_IBM_cull_vertex +#define GL_IBM_cull_vertex 1 +#define GL_CULL_VERTEX_IBM 103050 +#endif /* GL_IBM_cull_vertex */ + +#ifndef GL_IBM_multimode_draw_arrays +#define GL_IBM_multimode_draw_arrays 1 +typedef void (APIENTRYP PFNGLMULTIMODEDRAWARRAYSIBMPROC) (const GLenum *mode, const GLint *first, const GLsizei *count, GLsizei primcount, GLint modestride); +typedef void (APIENTRYP PFNGLMULTIMODEDRAWELEMENTSIBMPROC) (const GLenum *mode, const GLsizei *count, GLenum type, const void *const*indices, GLsizei primcount, GLint modestride); +#ifdef GL_GLEXT_PROTOTYPES +GLAPI void APIENTRY glMultiModeDrawArraysIBM (const GLenum *mode, const GLint *first, const GLsizei *count, GLsizei primcount, GLint modestride); +GLAPI void APIENTRY glMultiModeDrawElementsIBM (const GLenum *mode, const GLsizei *count, GLenum type, const void *const*indices, GLsizei primcount, GLint modestride); +#endif +#endif /* GL_IBM_multimode_draw_arrays */ + +#ifndef GL_IBM_rasterpos_clip +#define GL_IBM_rasterpos_clip 1 +#define GL_RASTER_POSITION_UNCLIPPED_IBM 0x19262 +#endif /* GL_IBM_rasterpos_clip */ + +#ifndef GL_IBM_static_data +#define GL_IBM_static_data 1 +#define GL_ALL_STATIC_DATA_IBM 103060 +#define GL_STATIC_VERTEX_ARRAY_IBM 103061 +typedef void (APIENTRYP PFNGLFLUSHSTATICDATAIBMPROC) (GLenum target); +#ifdef GL_GLEXT_PROTOTYPES +GLAPI void APIENTRY glFlushStaticDataIBM (GLenum target); +#endif +#endif /* GL_IBM_static_data */ + +#ifndef GL_IBM_texture_mirrored_repeat +#define GL_IBM_texture_mirrored_repeat 1 +#define GL_MIRRORED_REPEAT_IBM 0x8370 +#endif /* GL_IBM_texture_mirrored_repeat */ + +#ifndef GL_IBM_vertex_array_lists +#define GL_IBM_vertex_array_lists 1 +#define GL_VERTEX_ARRAY_LIST_IBM 103070 +#define GL_NORMAL_ARRAY_LIST_IBM 103071 +#define GL_COLOR_ARRAY_LIST_IBM 103072 +#define GL_INDEX_ARRAY_LIST_IBM 103073 +#define GL_TEXTURE_COORD_ARRAY_LIST_IBM 103074 +#define GL_EDGE_FLAG_ARRAY_LIST_IBM 103075 +#define GL_FOG_COORDINATE_ARRAY_LIST_IBM 103076 +#define GL_SECONDARY_COLOR_ARRAY_LIST_IBM 103077 +#define GL_VERTEX_ARRAY_LIST_STRIDE_IBM 103080 +#define GL_NORMAL_ARRAY_LIST_STRIDE_IBM 103081 +#define GL_COLOR_ARRAY_LIST_STRIDE_IBM 103082 +#define GL_INDEX_ARRAY_LIST_STRIDE_IBM 103083 +#define GL_TEXTURE_COORD_ARRAY_LIST_STRIDE_IBM 103084 +#define GL_EDGE_FLAG_ARRAY_LIST_STRIDE_IBM 103085 +#define GL_FOG_COORDINATE_ARRAY_LIST_STRIDE_IBM 103086 +#define GL_SECONDARY_COLOR_ARRAY_LIST_STRIDE_IBM 103087 +typedef void (APIENTRYP PFNGLCOLORPOINTERLISTIBMPROC) (GLint size, GLenum type, GLint stride, const void **pointer, GLint ptrstride); +typedef void (APIENTRYP PFNGLSECONDARYCOLORPOINTERLISTIBMPROC) (GLint size, GLenum type, GLint stride, const void **pointer, GLint ptrstride); +typedef void (APIENTRYP PFNGLEDGEFLAGPOINTERLISTIBMPROC) (GLint stride, const GLboolean **pointer, GLint ptrstride); +typedef void (APIENTRYP PFNGLFOGCOORDPOINTERLISTIBMPROC) (GLenum type, GLint stride, const void **pointer, GLint ptrstride); +typedef void (APIENTRYP PFNGLINDEXPOINTERLISTIBMPROC) (GLenum type, GLint stride, const void **pointer, GLint ptrstride); +typedef void (APIENTRYP PFNGLNORMALPOINTERLISTIBMPROC) (GLenum type, GLint stride, const void **pointer, GLint ptrstride); +typedef void (APIENTRYP PFNGLTEXCOORDPOINTERLISTIBMPROC) (GLint size, GLenum type, GLint stride, const void **pointer, GLint ptrstride); +typedef void (APIENTRYP PFNGLVERTEXPOINTERLISTIBMPROC) (GLint size, GLenum type, GLint stride, const void **pointer, GLint ptrstride); +#ifdef GL_GLEXT_PROTOTYPES +GLAPI void APIENTRY glColorPointerListIBM (GLint size, GLenum type, GLint stride, const void **pointer, GLint ptrstride); +GLAPI void APIENTRY glSecondaryColorPointerListIBM (GLint size, GLenum type, GLint stride, const void **pointer, GLint ptrstride); +GLAPI void APIENTRY glEdgeFlagPointerListIBM (GLint stride, const GLboolean **pointer, GLint ptrstride); +GLAPI void APIENTRY glFogCoordPointerListIBM (GLenum type, GLint stride, const void **pointer, GLint ptrstride); +GLAPI void APIENTRY glIndexPointerListIBM (GLenum type, GLint stride, const void **pointer, GLint ptrstride); +GLAPI void APIENTRY glNormalPointerListIBM (GLenum type, GLint stride, const void **pointer, GLint ptrstride); +GLAPI void APIENTRY glTexCoordPointerListIBM (GLint size, GLenum type, GLint stride, const void **pointer, GLint ptrstride); +GLAPI void APIENTRY glVertexPointerListIBM (GLint size, GLenum type, GLint stride, const void **pointer, GLint ptrstride); +#endif +#endif /* GL_IBM_vertex_array_lists */ + +#ifndef GL_INGR_blend_func_separate +#define GL_INGR_blend_func_separate 1 +typedef void (APIENTRYP PFNGLBLENDFUNCSEPARATEINGRPROC) (GLenum sfactorRGB, GLenum dfactorRGB, GLenum sfactorAlpha, GLenum dfactorAlpha); +#ifdef GL_GLEXT_PROTOTYPES +GLAPI void APIENTRY glBlendFuncSeparateINGR (GLenum sfactorRGB, GLenum dfactorRGB, GLenum sfactorAlpha, GLenum dfactorAlpha); +#endif +#endif /* GL_INGR_blend_func_separate */ + +#ifndef GL_INGR_color_clamp +#define GL_INGR_color_clamp 1 +#define GL_RED_MIN_CLAMP_INGR 0x8560 +#define GL_GREEN_MIN_CLAMP_INGR 0x8561 +#define GL_BLUE_MIN_CLAMP_INGR 0x8562 +#define GL_ALPHA_MIN_CLAMP_INGR 0x8563 +#define GL_RED_MAX_CLAMP_INGR 0x8564 +#define GL_GREEN_MAX_CLAMP_INGR 0x8565 +#define GL_BLUE_MAX_CLAMP_INGR 0x8566 +#define GL_ALPHA_MAX_CLAMP_INGR 0x8567 +#endif /* GL_INGR_color_clamp */ + +#ifndef GL_INGR_interlace_read +#define GL_INGR_interlace_read 1 +#define GL_INTERLACE_READ_INGR 0x8568 +#endif /* GL_INGR_interlace_read */ + +#ifndef GL_INTEL_fragment_shader_ordering +#define GL_INTEL_fragment_shader_ordering 1 +#endif /* GL_INTEL_fragment_shader_ordering */ + +#ifndef GL_INTEL_map_texture +#define GL_INTEL_map_texture 1 +#define GL_TEXTURE_MEMORY_LAYOUT_INTEL 0x83FF +#define GL_LAYOUT_DEFAULT_INTEL 0 +#define GL_LAYOUT_LINEAR_INTEL 1 +#define GL_LAYOUT_LINEAR_CPU_CACHED_INTEL 2 +typedef void (APIENTRYP PFNGLSYNCTEXTUREINTELPROC) (GLuint texture); +typedef void (APIENTRYP PFNGLUNMAPTEXTURE2DINTELPROC) (GLuint texture, GLint level); +typedef void *(APIENTRYP PFNGLMAPTEXTURE2DINTELPROC) (GLuint texture, GLint level, GLbitfield access, GLint *stride, GLenum *layout); +#ifdef GL_GLEXT_PROTOTYPES +GLAPI void APIENTRY glSyncTextureINTEL (GLuint texture); +GLAPI void APIENTRY glUnmapTexture2DINTEL (GLuint texture, GLint level); +GLAPI void *APIENTRY glMapTexture2DINTEL (GLuint texture, GLint level, GLbitfield access, GLint *stride, GLenum *layout); +#endif +#endif /* GL_INTEL_map_texture */ + +#ifndef GL_INTEL_parallel_arrays +#define GL_INTEL_parallel_arrays 1 +#define GL_PARALLEL_ARRAYS_INTEL 0x83F4 +#define GL_VERTEX_ARRAY_PARALLEL_POINTERS_INTEL 0x83F5 +#define GL_NORMAL_ARRAY_PARALLEL_POINTERS_INTEL 0x83F6 +#define GL_COLOR_ARRAY_PARALLEL_POINTERS_INTEL 0x83F7 +#define GL_TEXTURE_COORD_ARRAY_PARALLEL_POINTERS_INTEL 0x83F8 +typedef void (APIENTRYP PFNGLVERTEXPOINTERVINTELPROC) (GLint size, GLenum type, const void **pointer); +typedef void (APIENTRYP PFNGLNORMALPOINTERVINTELPROC) (GLenum type, const void **pointer); +typedef void (APIENTRYP PFNGLCOLORPOINTERVINTELPROC) (GLint size, GLenum type, const void **pointer); +typedef void (APIENTRYP PFNGLTEXCOORDPOINTERVINTELPROC) (GLint size, GLenum type, const void **pointer); +#ifdef GL_GLEXT_PROTOTYPES +GLAPI void APIENTRY glVertexPointervINTEL (GLint size, GLenum type, const void **pointer); +GLAPI void APIENTRY glNormalPointervINTEL (GLenum type, const void **pointer); +GLAPI void APIENTRY glColorPointervINTEL (GLint size, GLenum type, const void **pointer); +GLAPI void APIENTRY glTexCoordPointervINTEL (GLint size, GLenum type, const void **pointer); +#endif +#endif /* GL_INTEL_parallel_arrays */ + +#ifndef GL_INTEL_performance_query +#define GL_INTEL_performance_query 1 +#define GL_PERFQUERY_SINGLE_CONTEXT_INTEL 0x00000000 +#define GL_PERFQUERY_GLOBAL_CONTEXT_INTEL 0x00000001 +#define GL_PERFQUERY_WAIT_INTEL 0x83FB +#define GL_PERFQUERY_FLUSH_INTEL 0x83FA +#define GL_PERFQUERY_DONOT_FLUSH_INTEL 0x83F9 +#define GL_PERFQUERY_COUNTER_EVENT_INTEL 0x94F0 +#define GL_PERFQUERY_COUNTER_DURATION_NORM_INTEL 0x94F1 +#define GL_PERFQUERY_COUNTER_DURATION_RAW_INTEL 0x94F2 +#define GL_PERFQUERY_COUNTER_THROUGHPUT_INTEL 0x94F3 +#define GL_PERFQUERY_COUNTER_RAW_INTEL 0x94F4 +#define GL_PERFQUERY_COUNTER_TIMESTAMP_INTEL 0x94F5 +#define GL_PERFQUERY_COUNTER_DATA_UINT32_INTEL 0x94F8 +#define GL_PERFQUERY_COUNTER_DATA_UINT64_INTEL 0x94F9 +#define GL_PERFQUERY_COUNTER_DATA_FLOAT_INTEL 0x94FA +#define GL_PERFQUERY_COUNTER_DATA_DOUBLE_INTEL 0x94FB +#define GL_PERFQUERY_COUNTER_DATA_BOOL32_INTEL 0x94FC +#define GL_PERFQUERY_QUERY_NAME_LENGTH_MAX_INTEL 0x94FD +#define GL_PERFQUERY_COUNTER_NAME_LENGTH_MAX_INTEL 0x94FE +#define GL_PERFQUERY_COUNTER_DESC_LENGTH_MAX_INTEL 0x94FF +#define GL_PERFQUERY_GPA_EXTENDED_COUNTERS_INTEL 0x9500 +typedef void (APIENTRYP PFNGLBEGINPERFQUERYINTELPROC) (GLuint queryHandle); +typedef void (APIENTRYP PFNGLCREATEPERFQUERYINTELPROC) (GLuint queryId, GLuint *queryHandle); +typedef void (APIENTRYP PFNGLDELETEPERFQUERYINTELPROC) (GLuint queryHandle); +typedef void (APIENTRYP PFNGLENDPERFQUERYINTELPROC) (GLuint queryHandle); +typedef void (APIENTRYP PFNGLGETFIRSTPERFQUERYIDINTELPROC) (GLuint *queryId); +typedef void (APIENTRYP PFNGLGETNEXTPERFQUERYIDINTELPROC) (GLuint queryId, GLuint *nextQueryId); +typedef void (APIENTRYP PFNGLGETPERFCOUNTERINFOINTELPROC) (GLuint queryId, GLuint counterId, GLuint counterNameLength, GLchar *counterName, GLuint counterDescLength, GLchar *counterDesc, GLuint *counterOffset, GLuint *counterDataSize, GLuint *counterTypeEnum, GLuint *counterDataTypeEnum, GLuint64 *rawCounterMaxValue); +typedef void (APIENTRYP PFNGLGETPERFQUERYDATAINTELPROC) (GLuint queryHandle, GLuint flags, GLsizei dataSize, GLvoid *data, GLuint *bytesWritten); +typedef void (APIENTRYP PFNGLGETPERFQUERYIDBYNAMEINTELPROC) (GLchar *queryName, GLuint *queryId); +typedef void (APIENTRYP PFNGLGETPERFQUERYINFOINTELPROC) (GLuint queryId, GLuint queryNameLength, GLchar *queryName, GLuint *dataSize, GLuint *noCounters, GLuint *noInstances, GLuint *capsMask); +#ifdef GL_GLEXT_PROTOTYPES +GLAPI void APIENTRY glBeginPerfQueryINTEL (GLuint queryHandle); +GLAPI void APIENTRY glCreatePerfQueryINTEL (GLuint queryId, GLuint *queryHandle); +GLAPI void APIENTRY glDeletePerfQueryINTEL (GLuint queryHandle); +GLAPI void APIENTRY glEndPerfQueryINTEL (GLuint queryHandle); +GLAPI void APIENTRY glGetFirstPerfQueryIdINTEL (GLuint *queryId); +GLAPI void APIENTRY glGetNextPerfQueryIdINTEL (GLuint queryId, GLuint *nextQueryId); +GLAPI void APIENTRY glGetPerfCounterInfoINTEL (GLuint queryId, GLuint counterId, GLuint counterNameLength, GLchar *counterName, GLuint counterDescLength, GLchar *counterDesc, GLuint *counterOffset, GLuint *counterDataSize, GLuint *counterTypeEnum, GLuint *counterDataTypeEnum, GLuint64 *rawCounterMaxValue); +GLAPI void APIENTRY glGetPerfQueryDataINTEL (GLuint queryHandle, GLuint flags, GLsizei dataSize, GLvoid *data, GLuint *bytesWritten); +GLAPI void APIENTRY glGetPerfQueryIdByNameINTEL (GLchar *queryName, GLuint *queryId); +GLAPI void APIENTRY glGetPerfQueryInfoINTEL (GLuint queryId, GLuint queryNameLength, GLchar *queryName, GLuint *dataSize, GLuint *noCounters, GLuint *noInstances, GLuint *capsMask); +#endif +#endif /* GL_INTEL_performance_query */ + +#ifndef GL_MESAX_texture_stack +#define GL_MESAX_texture_stack 1 +#define GL_TEXTURE_1D_STACK_MESAX 0x8759 +#define GL_TEXTURE_2D_STACK_MESAX 0x875A +#define GL_PROXY_TEXTURE_1D_STACK_MESAX 0x875B +#define GL_PROXY_TEXTURE_2D_STACK_MESAX 0x875C +#define GL_TEXTURE_1D_STACK_BINDING_MESAX 0x875D +#define GL_TEXTURE_2D_STACK_BINDING_MESAX 0x875E +#endif /* GL_MESAX_texture_stack */ + +#ifndef GL_MESA_pack_invert +#define GL_MESA_pack_invert 1 +#define GL_PACK_INVERT_MESA 0x8758 +#endif /* GL_MESA_pack_invert */ + +#ifndef GL_MESA_resize_buffers +#define GL_MESA_resize_buffers 1 +typedef void (APIENTRYP PFNGLRESIZEBUFFERSMESAPROC) (void); +#ifdef GL_GLEXT_PROTOTYPES +GLAPI void APIENTRY glResizeBuffersMESA (void); +#endif +#endif /* GL_MESA_resize_buffers */ + +#ifndef GL_MESA_window_pos +#define GL_MESA_window_pos 1 +typedef void (APIENTRYP PFNGLWINDOWPOS2DMESAPROC) (GLdouble x, GLdouble y); +typedef void (APIENTRYP PFNGLWINDOWPOS2DVMESAPROC) (const GLdouble *v); +typedef void (APIENTRYP PFNGLWINDOWPOS2FMESAPROC) (GLfloat x, GLfloat y); +typedef void (APIENTRYP PFNGLWINDOWPOS2FVMESAPROC) (const GLfloat *v); +typedef void (APIENTRYP PFNGLWINDOWPOS2IMESAPROC) (GLint x, GLint y); +typedef void (APIENTRYP PFNGLWINDOWPOS2IVMESAPROC) (const GLint *v); +typedef void (APIENTRYP PFNGLWINDOWPOS2SMESAPROC) (GLshort x, GLshort y); +typedef void (APIENTRYP PFNGLWINDOWPOS2SVMESAPROC) (const GLshort *v); +typedef void (APIENTRYP PFNGLWINDOWPOS3DMESAPROC) (GLdouble x, GLdouble y, GLdouble z); +typedef void (APIENTRYP PFNGLWINDOWPOS3DVMESAPROC) (const GLdouble *v); +typedef void (APIENTRYP PFNGLWINDOWPOS3FMESAPROC) (GLfloat x, GLfloat y, GLfloat z); +typedef void (APIENTRYP PFNGLWINDOWPOS3FVMESAPROC) (const GLfloat *v); +typedef void (APIENTRYP PFNGLWINDOWPOS3IMESAPROC) (GLint x, GLint y, GLint z); +typedef void (APIENTRYP PFNGLWINDOWPOS3IVMESAPROC) (const GLint *v); +typedef void (APIENTRYP PFNGLWINDOWPOS3SMESAPROC) (GLshort x, GLshort y, GLshort z); +typedef void (APIENTRYP PFNGLWINDOWPOS3SVMESAPROC) (const GLshort *v); +typedef void (APIENTRYP PFNGLWINDOWPOS4DMESAPROC) (GLdouble x, GLdouble y, GLdouble z, GLdouble w); +typedef void (APIENTRYP PFNGLWINDOWPOS4DVMESAPROC) (const GLdouble *v); +typedef void (APIENTRYP PFNGLWINDOWPOS4FMESAPROC) (GLfloat x, GLfloat y, GLfloat z, GLfloat w); +typedef void (APIENTRYP PFNGLWINDOWPOS4FVMESAPROC) (const GLfloat *v); +typedef void (APIENTRYP PFNGLWINDOWPOS4IMESAPROC) (GLint x, GLint y, GLint z, GLint w); +typedef void (APIENTRYP PFNGLWINDOWPOS4IVMESAPROC) (const GLint *v); +typedef void (APIENTRYP PFNGLWINDOWPOS4SMESAPROC) (GLshort x, GLshort y, GLshort z, GLshort w); +typedef void (APIENTRYP PFNGLWINDOWPOS4SVMESAPROC) (const GLshort *v); +#ifdef GL_GLEXT_PROTOTYPES +GLAPI void APIENTRY glWindowPos2dMESA (GLdouble x, GLdouble y); +GLAPI void APIENTRY glWindowPos2dvMESA (const GLdouble *v); +GLAPI void APIENTRY glWindowPos2fMESA (GLfloat x, GLfloat y); +GLAPI void APIENTRY glWindowPos2fvMESA (const GLfloat *v); +GLAPI void APIENTRY glWindowPos2iMESA (GLint x, GLint y); +GLAPI void APIENTRY glWindowPos2ivMESA (const GLint *v); +GLAPI void APIENTRY glWindowPos2sMESA (GLshort x, GLshort y); +GLAPI void APIENTRY glWindowPos2svMESA (const GLshort *v); +GLAPI void APIENTRY glWindowPos3dMESA (GLdouble x, GLdouble y, GLdouble z); +GLAPI void APIENTRY glWindowPos3dvMESA (const GLdouble *v); +GLAPI void APIENTRY glWindowPos3fMESA (GLfloat x, GLfloat y, GLfloat z); +GLAPI void APIENTRY glWindowPos3fvMESA (const GLfloat *v); +GLAPI void APIENTRY glWindowPos3iMESA (GLint x, GLint y, GLint z); +GLAPI void APIENTRY glWindowPos3ivMESA (const GLint *v); +GLAPI void APIENTRY glWindowPos3sMESA (GLshort x, GLshort y, GLshort z); +GLAPI void APIENTRY glWindowPos3svMESA (const GLshort *v); +GLAPI void APIENTRY glWindowPos4dMESA (GLdouble x, GLdouble y, GLdouble z, GLdouble w); +GLAPI void APIENTRY glWindowPos4dvMESA (const GLdouble *v); +GLAPI void APIENTRY glWindowPos4fMESA (GLfloat x, GLfloat y, GLfloat z, GLfloat w); +GLAPI void APIENTRY glWindowPos4fvMESA (const GLfloat *v); +GLAPI void APIENTRY glWindowPos4iMESA (GLint x, GLint y, GLint z, GLint w); +GLAPI void APIENTRY glWindowPos4ivMESA (const GLint *v); +GLAPI void APIENTRY glWindowPos4sMESA (GLshort x, GLshort y, GLshort z, GLshort w); +GLAPI void APIENTRY glWindowPos4svMESA (const GLshort *v); +#endif +#endif /* GL_MESA_window_pos */ + +#ifndef GL_MESA_ycbcr_texture +#define GL_MESA_ycbcr_texture 1 +#define GL_UNSIGNED_SHORT_8_8_MESA 0x85BA +#define GL_UNSIGNED_SHORT_8_8_REV_MESA 0x85BB +#define GL_YCBCR_MESA 0x8757 +#endif /* GL_MESA_ycbcr_texture */ + +#ifndef GL_NVX_conditional_render +#define GL_NVX_conditional_render 1 +typedef void (APIENTRYP PFNGLBEGINCONDITIONALRENDERNVXPROC) (GLuint id); +typedef void (APIENTRYP PFNGLENDCONDITIONALRENDERNVXPROC) (void); +#ifdef GL_GLEXT_PROTOTYPES +GLAPI void APIENTRY glBeginConditionalRenderNVX (GLuint id); +GLAPI void APIENTRY glEndConditionalRenderNVX (void); +#endif +#endif /* GL_NVX_conditional_render */ + +#ifndef GL_NV_bindless_multi_draw_indirect +#define GL_NV_bindless_multi_draw_indirect 1 +typedef void (APIENTRYP PFNGLMULTIDRAWARRAYSINDIRECTBINDLESSNVPROC) (GLenum mode, const void *indirect, GLsizei drawCount, GLsizei stride, GLint vertexBufferCount); +typedef void (APIENTRYP PFNGLMULTIDRAWELEMENTSINDIRECTBINDLESSNVPROC) (GLenum mode, GLenum type, const void *indirect, GLsizei drawCount, GLsizei stride, GLint vertexBufferCount); +#ifdef GL_GLEXT_PROTOTYPES +GLAPI void APIENTRY glMultiDrawArraysIndirectBindlessNV (GLenum mode, const void *indirect, GLsizei drawCount, GLsizei stride, GLint vertexBufferCount); +GLAPI void APIENTRY glMultiDrawElementsIndirectBindlessNV (GLenum mode, GLenum type, const void *indirect, GLsizei drawCount, GLsizei stride, GLint vertexBufferCount); +#endif +#endif /* GL_NV_bindless_multi_draw_indirect */ + +#ifndef GL_NV_bindless_texture +#define GL_NV_bindless_texture 1 +typedef GLuint64 (APIENTRYP PFNGLGETTEXTUREHANDLENVPROC) (GLuint texture); +typedef GLuint64 (APIENTRYP PFNGLGETTEXTURESAMPLERHANDLENVPROC) (GLuint texture, GLuint sampler); +typedef void (APIENTRYP PFNGLMAKETEXTUREHANDLERESIDENTNVPROC) (GLuint64 handle); +typedef void (APIENTRYP PFNGLMAKETEXTUREHANDLENONRESIDENTNVPROC) (GLuint64 handle); +typedef GLuint64 (APIENTRYP PFNGLGETIMAGEHANDLENVPROC) (GLuint texture, GLint level, GLboolean layered, GLint layer, GLenum format); +typedef void (APIENTRYP PFNGLMAKEIMAGEHANDLERESIDENTNVPROC) (GLuint64 handle, GLenum access); +typedef void (APIENTRYP PFNGLMAKEIMAGEHANDLENONRESIDENTNVPROC) (GLuint64 handle); +typedef void (APIENTRYP PFNGLUNIFORMHANDLEUI64NVPROC) (GLint location, GLuint64 value); +typedef void (APIENTRYP PFNGLUNIFORMHANDLEUI64VNVPROC) (GLint location, GLsizei count, const GLuint64 *value); +typedef void (APIENTRYP PFNGLPROGRAMUNIFORMHANDLEUI64NVPROC) (GLuint program, GLint location, GLuint64 value); +typedef void (APIENTRYP PFNGLPROGRAMUNIFORMHANDLEUI64VNVPROC) (GLuint program, GLint location, GLsizei count, const GLuint64 *values); +typedef GLboolean (APIENTRYP PFNGLISTEXTUREHANDLERESIDENTNVPROC) (GLuint64 handle); +typedef GLboolean (APIENTRYP PFNGLISIMAGEHANDLERESIDENTNVPROC) (GLuint64 handle); +#ifdef GL_GLEXT_PROTOTYPES +GLAPI GLuint64 APIENTRY glGetTextureHandleNV (GLuint texture); +GLAPI GLuint64 APIENTRY glGetTextureSamplerHandleNV (GLuint texture, GLuint sampler); +GLAPI void APIENTRY glMakeTextureHandleResidentNV (GLuint64 handle); +GLAPI void APIENTRY glMakeTextureHandleNonResidentNV (GLuint64 handle); +GLAPI GLuint64 APIENTRY glGetImageHandleNV (GLuint texture, GLint level, GLboolean layered, GLint layer, GLenum format); +GLAPI void APIENTRY glMakeImageHandleResidentNV (GLuint64 handle, GLenum access); +GLAPI void APIENTRY glMakeImageHandleNonResidentNV (GLuint64 handle); +GLAPI void APIENTRY glUniformHandleui64NV (GLint location, GLuint64 value); +GLAPI void APIENTRY glUniformHandleui64vNV (GLint location, GLsizei count, const GLuint64 *value); +GLAPI void APIENTRY glProgramUniformHandleui64NV (GLuint program, GLint location, GLuint64 value); +GLAPI void APIENTRY glProgramUniformHandleui64vNV (GLuint program, GLint location, GLsizei count, const GLuint64 *values); +GLAPI GLboolean APIENTRY glIsTextureHandleResidentNV (GLuint64 handle); +GLAPI GLboolean APIENTRY glIsImageHandleResidentNV (GLuint64 handle); +#endif +#endif /* GL_NV_bindless_texture */ + +#ifndef GL_NV_blend_equation_advanced +#define GL_NV_blend_equation_advanced 1 +#define GL_BLEND_OVERLAP_NV 0x9281 +#define GL_BLEND_PREMULTIPLIED_SRC_NV 0x9280 +#define GL_BLUE_NV 0x1905 +#define GL_COLORBURN_NV 0x929A +#define GL_COLORDODGE_NV 0x9299 +#define GL_CONJOINT_NV 0x9284 +#define GL_CONTRAST_NV 0x92A1 +#define GL_DARKEN_NV 0x9297 +#define GL_DIFFERENCE_NV 0x929E +#define GL_DISJOINT_NV 0x9283 +#define GL_DST_ATOP_NV 0x928F +#define GL_DST_IN_NV 0x928B +#define GL_DST_NV 0x9287 +#define GL_DST_OUT_NV 0x928D +#define GL_DST_OVER_NV 0x9289 +#define GL_EXCLUSION_NV 0x92A0 +#define GL_GREEN_NV 0x1904 +#define GL_HARDLIGHT_NV 0x929B +#define GL_HARDMIX_NV 0x92A9 +#define GL_HSL_COLOR_NV 0x92AF +#define GL_HSL_HUE_NV 0x92AD +#define GL_HSL_LUMINOSITY_NV 0x92B0 +#define GL_HSL_SATURATION_NV 0x92AE +#define GL_INVERT_OVG_NV 0x92B4 +#define GL_INVERT_RGB_NV 0x92A3 +#define GL_LIGHTEN_NV 0x9298 +#define GL_LINEARBURN_NV 0x92A5 +#define GL_LINEARDODGE_NV 0x92A4 +#define GL_LINEARLIGHT_NV 0x92A7 +#define GL_MINUS_CLAMPED_NV 0x92B3 +#define GL_MINUS_NV 0x929F +#define GL_MULTIPLY_NV 0x9294 +#define GL_OVERLAY_NV 0x9296 +#define GL_PINLIGHT_NV 0x92A8 +#define GL_PLUS_CLAMPED_ALPHA_NV 0x92B2 +#define GL_PLUS_CLAMPED_NV 0x92B1 +#define GL_PLUS_DARKER_NV 0x9292 +#define GL_PLUS_NV 0x9291 +#define GL_RED_NV 0x1903 +#define GL_SCREEN_NV 0x9295 +#define GL_SOFTLIGHT_NV 0x929C +#define GL_SRC_ATOP_NV 0x928E +#define GL_SRC_IN_NV 0x928A +#define GL_SRC_NV 0x9286 +#define GL_SRC_OUT_NV 0x928C +#define GL_SRC_OVER_NV 0x9288 +#define GL_UNCORRELATED_NV 0x9282 +#define GL_VIVIDLIGHT_NV 0x92A6 +#define GL_XOR_NV 0x1506 +typedef void (APIENTRYP PFNGLBLENDPARAMETERINVPROC) (GLenum pname, GLint value); +typedef void (APIENTRYP PFNGLBLENDBARRIERNVPROC) (void); +#ifdef GL_GLEXT_PROTOTYPES +GLAPI void APIENTRY glBlendParameteriNV (GLenum pname, GLint value); +GLAPI void APIENTRY glBlendBarrierNV (void); +#endif +#endif /* GL_NV_blend_equation_advanced */ + +#ifndef GL_NV_blend_equation_advanced_coherent +#define GL_NV_blend_equation_advanced_coherent 1 +#define GL_BLEND_ADVANCED_COHERENT_NV 0x9285 +#endif /* GL_NV_blend_equation_advanced_coherent */ + +#ifndef GL_NV_blend_square +#define GL_NV_blend_square 1 +#endif /* GL_NV_blend_square */ + +#ifndef GL_NV_compute_program5 +#define GL_NV_compute_program5 1 +#define GL_COMPUTE_PROGRAM_NV 0x90FB +#define GL_COMPUTE_PROGRAM_PARAMETER_BUFFER_NV 0x90FC +#endif /* GL_NV_compute_program5 */ + +#ifndef GL_NV_conditional_render +#define GL_NV_conditional_render 1 +#define GL_QUERY_WAIT_NV 0x8E13 +#define GL_QUERY_NO_WAIT_NV 0x8E14 +#define GL_QUERY_BY_REGION_WAIT_NV 0x8E15 +#define GL_QUERY_BY_REGION_NO_WAIT_NV 0x8E16 +typedef void (APIENTRYP PFNGLBEGINCONDITIONALRENDERNVPROC) (GLuint id, GLenum mode); +typedef void (APIENTRYP PFNGLENDCONDITIONALRENDERNVPROC) (void); +#ifdef GL_GLEXT_PROTOTYPES +GLAPI void APIENTRY glBeginConditionalRenderNV (GLuint id, GLenum mode); +GLAPI void APIENTRY glEndConditionalRenderNV (void); +#endif +#endif /* GL_NV_conditional_render */ + +#ifndef GL_NV_copy_depth_to_color +#define GL_NV_copy_depth_to_color 1 +#define GL_DEPTH_STENCIL_TO_RGBA_NV 0x886E +#define GL_DEPTH_STENCIL_TO_BGRA_NV 0x886F +#endif /* GL_NV_copy_depth_to_color */ + +#ifndef GL_NV_copy_image +#define GL_NV_copy_image 1 +typedef void (APIENTRYP PFNGLCOPYIMAGESUBDATANVPROC) (GLuint srcName, GLenum srcTarget, GLint srcLevel, GLint srcX, GLint srcY, GLint srcZ, GLuint dstName, GLenum dstTarget, GLint dstLevel, GLint dstX, GLint dstY, GLint dstZ, GLsizei width, GLsizei height, GLsizei depth); +#ifdef GL_GLEXT_PROTOTYPES +GLAPI void APIENTRY glCopyImageSubDataNV (GLuint srcName, GLenum srcTarget, GLint srcLevel, GLint srcX, GLint srcY, GLint srcZ, GLuint dstName, GLenum dstTarget, GLint dstLevel, GLint dstX, GLint dstY, GLint dstZ, GLsizei width, GLsizei height, GLsizei depth); +#endif +#endif /* GL_NV_copy_image */ + +#ifndef GL_NV_deep_texture3D +#define GL_NV_deep_texture3D 1 +#define GL_MAX_DEEP_3D_TEXTURE_WIDTH_HEIGHT_NV 0x90D0 +#define GL_MAX_DEEP_3D_TEXTURE_DEPTH_NV 0x90D1 +#endif /* GL_NV_deep_texture3D */ + +#ifndef GL_NV_depth_buffer_float +#define GL_NV_depth_buffer_float 1 +#define GL_DEPTH_COMPONENT32F_NV 0x8DAB +#define GL_DEPTH32F_STENCIL8_NV 0x8DAC +#define GL_FLOAT_32_UNSIGNED_INT_24_8_REV_NV 0x8DAD +#define GL_DEPTH_BUFFER_FLOAT_MODE_NV 0x8DAF +typedef void (APIENTRYP PFNGLDEPTHRANGEDNVPROC) (GLdouble zNear, GLdouble zFar); +typedef void (APIENTRYP PFNGLCLEARDEPTHDNVPROC) (GLdouble depth); +typedef void (APIENTRYP PFNGLDEPTHBOUNDSDNVPROC) (GLdouble zmin, GLdouble zmax); +#ifdef GL_GLEXT_PROTOTYPES +GLAPI void APIENTRY glDepthRangedNV (GLdouble zNear, GLdouble zFar); +GLAPI void APIENTRY glClearDepthdNV (GLdouble depth); +GLAPI void APIENTRY glDepthBoundsdNV (GLdouble zmin, GLdouble zmax); +#endif +#endif /* GL_NV_depth_buffer_float */ + +#ifndef GL_NV_depth_clamp +#define GL_NV_depth_clamp 1 +#define GL_DEPTH_CLAMP_NV 0x864F +#endif /* GL_NV_depth_clamp */ + +#ifndef GL_NV_draw_texture +#define GL_NV_draw_texture 1 +typedef void (APIENTRYP PFNGLDRAWTEXTURENVPROC) (GLuint texture, GLuint sampler, GLfloat x0, GLfloat y0, GLfloat x1, GLfloat y1, GLfloat z, GLfloat s0, GLfloat t0, GLfloat s1, GLfloat t1); +#ifdef GL_GLEXT_PROTOTYPES +GLAPI void APIENTRY glDrawTextureNV (GLuint texture, GLuint sampler, GLfloat x0, GLfloat y0, GLfloat x1, GLfloat y1, GLfloat z, GLfloat s0, GLfloat t0, GLfloat s1, GLfloat t1); +#endif +#endif /* GL_NV_draw_texture */ + +#ifndef GL_NV_evaluators +#define GL_NV_evaluators 1 +#define GL_EVAL_2D_NV 0x86C0 +#define GL_EVAL_TRIANGULAR_2D_NV 0x86C1 +#define GL_MAP_TESSELLATION_NV 0x86C2 +#define GL_MAP_ATTRIB_U_ORDER_NV 0x86C3 +#define GL_MAP_ATTRIB_V_ORDER_NV 0x86C4 +#define GL_EVAL_FRACTIONAL_TESSELLATION_NV 0x86C5 +#define GL_EVAL_VERTEX_ATTRIB0_NV 0x86C6 +#define GL_EVAL_VERTEX_ATTRIB1_NV 0x86C7 +#define GL_EVAL_VERTEX_ATTRIB2_NV 0x86C8 +#define GL_EVAL_VERTEX_ATTRIB3_NV 0x86C9 +#define GL_EVAL_VERTEX_ATTRIB4_NV 0x86CA +#define GL_EVAL_VERTEX_ATTRIB5_NV 0x86CB +#define GL_EVAL_VERTEX_ATTRIB6_NV 0x86CC +#define GL_EVAL_VERTEX_ATTRIB7_NV 0x86CD +#define GL_EVAL_VERTEX_ATTRIB8_NV 0x86CE +#define GL_EVAL_VERTEX_ATTRIB9_NV 0x86CF +#define GL_EVAL_VERTEX_ATTRIB10_NV 0x86D0 +#define GL_EVAL_VERTEX_ATTRIB11_NV 0x86D1 +#define GL_EVAL_VERTEX_ATTRIB12_NV 0x86D2 +#define GL_EVAL_VERTEX_ATTRIB13_NV 0x86D3 +#define GL_EVAL_VERTEX_ATTRIB14_NV 0x86D4 +#define GL_EVAL_VERTEX_ATTRIB15_NV 0x86D5 +#define GL_MAX_MAP_TESSELLATION_NV 0x86D6 +#define GL_MAX_RATIONAL_EVAL_ORDER_NV 0x86D7 +typedef void (APIENTRYP PFNGLMAPCONTROLPOINTSNVPROC) (GLenum target, GLuint index, GLenum type, GLsizei ustride, GLsizei vstride, GLint uorder, GLint vorder, GLboolean packed, const void *points); +typedef void (APIENTRYP PFNGLMAPPARAMETERIVNVPROC) (GLenum target, GLenum pname, const GLint *params); +typedef void (APIENTRYP PFNGLMAPPARAMETERFVNVPROC) (GLenum target, GLenum pname, const GLfloat *params); +typedef void (APIENTRYP PFNGLGETMAPCONTROLPOINTSNVPROC) (GLenum target, GLuint index, GLenum type, GLsizei ustride, GLsizei vstride, GLboolean packed, void *points); +typedef void (APIENTRYP PFNGLGETMAPPARAMETERIVNVPROC) (GLenum target, GLenum pname, GLint *params); +typedef void (APIENTRYP PFNGLGETMAPPARAMETERFVNVPROC) (GLenum target, GLenum pname, GLfloat *params); +typedef void (APIENTRYP PFNGLGETMAPATTRIBPARAMETERIVNVPROC) (GLenum target, GLuint index, GLenum pname, GLint *params); +typedef void (APIENTRYP PFNGLGETMAPATTRIBPARAMETERFVNVPROC) (GLenum target, GLuint index, GLenum pname, GLfloat *params); +typedef void (APIENTRYP PFNGLEVALMAPSNVPROC) (GLenum target, GLenum mode); +#ifdef GL_GLEXT_PROTOTYPES +GLAPI void APIENTRY glMapControlPointsNV (GLenum target, GLuint index, GLenum type, GLsizei ustride, GLsizei vstride, GLint uorder, GLint vorder, GLboolean packed, const void *points); +GLAPI void APIENTRY glMapParameterivNV (GLenum target, GLenum pname, const GLint *params); +GLAPI void APIENTRY glMapParameterfvNV (GLenum target, GLenum pname, const GLfloat *params); +GLAPI void APIENTRY glGetMapControlPointsNV (GLenum target, GLuint index, GLenum type, GLsizei ustride, GLsizei vstride, GLboolean packed, void *points); +GLAPI void APIENTRY glGetMapParameterivNV (GLenum target, GLenum pname, GLint *params); +GLAPI void APIENTRY glGetMapParameterfvNV (GLenum target, GLenum pname, GLfloat *params); +GLAPI void APIENTRY glGetMapAttribParameterivNV (GLenum target, GLuint index, GLenum pname, GLint *params); +GLAPI void APIENTRY glGetMapAttribParameterfvNV (GLenum target, GLuint index, GLenum pname, GLfloat *params); +GLAPI void APIENTRY glEvalMapsNV (GLenum target, GLenum mode); +#endif +#endif /* GL_NV_evaluators */ + +#ifndef GL_NV_explicit_multisample +#define GL_NV_explicit_multisample 1 +#define GL_SAMPLE_POSITION_NV 0x8E50 +#define GL_SAMPLE_MASK_NV 0x8E51 +#define GL_SAMPLE_MASK_VALUE_NV 0x8E52 +#define GL_TEXTURE_BINDING_RENDERBUFFER_NV 0x8E53 +#define GL_TEXTURE_RENDERBUFFER_DATA_STORE_BINDING_NV 0x8E54 +#define GL_TEXTURE_RENDERBUFFER_NV 0x8E55 +#define GL_SAMPLER_RENDERBUFFER_NV 0x8E56 +#define GL_INT_SAMPLER_RENDERBUFFER_NV 0x8E57 +#define GL_UNSIGNED_INT_SAMPLER_RENDERBUFFER_NV 0x8E58 +#define GL_MAX_SAMPLE_MASK_WORDS_NV 0x8E59 +typedef void (APIENTRYP PFNGLGETMULTISAMPLEFVNVPROC) (GLenum pname, GLuint index, GLfloat *val); +typedef void (APIENTRYP PFNGLSAMPLEMASKINDEXEDNVPROC) (GLuint index, GLbitfield mask); +typedef void (APIENTRYP PFNGLTEXRENDERBUFFERNVPROC) (GLenum target, GLuint renderbuffer); +#ifdef GL_GLEXT_PROTOTYPES +GLAPI void APIENTRY glGetMultisamplefvNV (GLenum pname, GLuint index, GLfloat *val); +GLAPI void APIENTRY glSampleMaskIndexedNV (GLuint index, GLbitfield mask); +GLAPI void APIENTRY glTexRenderbufferNV (GLenum target, GLuint renderbuffer); +#endif +#endif /* GL_NV_explicit_multisample */ + +#ifndef GL_NV_fence +#define GL_NV_fence 1 +#define GL_ALL_COMPLETED_NV 0x84F2 +#define GL_FENCE_STATUS_NV 0x84F3 +#define GL_FENCE_CONDITION_NV 0x84F4 +typedef void (APIENTRYP PFNGLDELETEFENCESNVPROC) (GLsizei n, const GLuint *fences); +typedef void (APIENTRYP PFNGLGENFENCESNVPROC) (GLsizei n, GLuint *fences); +typedef GLboolean (APIENTRYP PFNGLISFENCENVPROC) (GLuint fence); +typedef GLboolean (APIENTRYP PFNGLTESTFENCENVPROC) (GLuint fence); +typedef void (APIENTRYP PFNGLGETFENCEIVNVPROC) (GLuint fence, GLenum pname, GLint *params); +typedef void (APIENTRYP PFNGLFINISHFENCENVPROC) (GLuint fence); +typedef void (APIENTRYP PFNGLSETFENCENVPROC) (GLuint fence, GLenum condition); +#ifdef GL_GLEXT_PROTOTYPES +GLAPI void APIENTRY glDeleteFencesNV (GLsizei n, const GLuint *fences); +GLAPI void APIENTRY glGenFencesNV (GLsizei n, GLuint *fences); +GLAPI GLboolean APIENTRY glIsFenceNV (GLuint fence); +GLAPI GLboolean APIENTRY glTestFenceNV (GLuint fence); +GLAPI void APIENTRY glGetFenceivNV (GLuint fence, GLenum pname, GLint *params); +GLAPI void APIENTRY glFinishFenceNV (GLuint fence); +GLAPI void APIENTRY glSetFenceNV (GLuint fence, GLenum condition); +#endif +#endif /* GL_NV_fence */ + +#ifndef GL_NV_float_buffer +#define GL_NV_float_buffer 1 +#define GL_FLOAT_R_NV 0x8880 +#define GL_FLOAT_RG_NV 0x8881 +#define GL_FLOAT_RGB_NV 0x8882 +#define GL_FLOAT_RGBA_NV 0x8883 +#define GL_FLOAT_R16_NV 0x8884 +#define GL_FLOAT_R32_NV 0x8885 +#define GL_FLOAT_RG16_NV 0x8886 +#define GL_FLOAT_RG32_NV 0x8887 +#define GL_FLOAT_RGB16_NV 0x8888 +#define GL_FLOAT_RGB32_NV 0x8889 +#define GL_FLOAT_RGBA16_NV 0x888A +#define GL_FLOAT_RGBA32_NV 0x888B +#define GL_TEXTURE_FLOAT_COMPONENTS_NV 0x888C +#define GL_FLOAT_CLEAR_COLOR_VALUE_NV 0x888D +#define GL_FLOAT_RGBA_MODE_NV 0x888E +#endif /* GL_NV_float_buffer */ + +#ifndef GL_NV_fog_distance +#define GL_NV_fog_distance 1 +#define GL_FOG_DISTANCE_MODE_NV 0x855A +#define GL_EYE_RADIAL_NV 0x855B +#define GL_EYE_PLANE_ABSOLUTE_NV 0x855C +#endif /* GL_NV_fog_distance */ + +#ifndef GL_NV_fragment_program +#define GL_NV_fragment_program 1 +#define GL_MAX_FRAGMENT_PROGRAM_LOCAL_PARAMETERS_NV 0x8868 +#define GL_FRAGMENT_PROGRAM_NV 0x8870 +#define GL_MAX_TEXTURE_COORDS_NV 0x8871 +#define GL_MAX_TEXTURE_IMAGE_UNITS_NV 0x8872 +#define GL_FRAGMENT_PROGRAM_BINDING_NV 0x8873 +#define GL_PROGRAM_ERROR_STRING_NV 0x8874 +typedef void (APIENTRYP PFNGLPROGRAMNAMEDPARAMETER4FNVPROC) (GLuint id, GLsizei len, const GLubyte *name, GLfloat x, GLfloat y, GLfloat z, GLfloat w); +typedef void (APIENTRYP PFNGLPROGRAMNAMEDPARAMETER4FVNVPROC) (GLuint id, GLsizei len, const GLubyte *name, const GLfloat *v); +typedef void (APIENTRYP PFNGLPROGRAMNAMEDPARAMETER4DNVPROC) (GLuint id, GLsizei len, const GLubyte *name, GLdouble x, GLdouble y, GLdouble z, GLdouble w); +typedef void (APIENTRYP PFNGLPROGRAMNAMEDPARAMETER4DVNVPROC) (GLuint id, GLsizei len, const GLubyte *name, const GLdouble *v); +typedef void (APIENTRYP PFNGLGETPROGRAMNAMEDPARAMETERFVNVPROC) (GLuint id, GLsizei len, const GLubyte *name, GLfloat *params); +typedef void (APIENTRYP PFNGLGETPROGRAMNAMEDPARAMETERDVNVPROC) (GLuint id, GLsizei len, const GLubyte *name, GLdouble *params); +#ifdef GL_GLEXT_PROTOTYPES +GLAPI void APIENTRY glProgramNamedParameter4fNV (GLuint id, GLsizei len, const GLubyte *name, GLfloat x, GLfloat y, GLfloat z, GLfloat w); +GLAPI void APIENTRY glProgramNamedParameter4fvNV (GLuint id, GLsizei len, const GLubyte *name, const GLfloat *v); +GLAPI void APIENTRY glProgramNamedParameter4dNV (GLuint id, GLsizei len, const GLubyte *name, GLdouble x, GLdouble y, GLdouble z, GLdouble w); +GLAPI void APIENTRY glProgramNamedParameter4dvNV (GLuint id, GLsizei len, const GLubyte *name, const GLdouble *v); +GLAPI void APIENTRY glGetProgramNamedParameterfvNV (GLuint id, GLsizei len, const GLubyte *name, GLfloat *params); +GLAPI void APIENTRY glGetProgramNamedParameterdvNV (GLuint id, GLsizei len, const GLubyte *name, GLdouble *params); +#endif +#endif /* GL_NV_fragment_program */ + +#ifndef GL_NV_fragment_program2 +#define GL_NV_fragment_program2 1 +#define GL_MAX_PROGRAM_EXEC_INSTRUCTIONS_NV 0x88F4 +#define GL_MAX_PROGRAM_CALL_DEPTH_NV 0x88F5 +#define GL_MAX_PROGRAM_IF_DEPTH_NV 0x88F6 +#define GL_MAX_PROGRAM_LOOP_DEPTH_NV 0x88F7 +#define GL_MAX_PROGRAM_LOOP_COUNT_NV 0x88F8 +#endif /* GL_NV_fragment_program2 */ + +#ifndef GL_NV_fragment_program4 +#define GL_NV_fragment_program4 1 +#endif /* GL_NV_fragment_program4 */ + +#ifndef GL_NV_fragment_program_option +#define GL_NV_fragment_program_option 1 +#endif /* GL_NV_fragment_program_option */ + +#ifndef GL_NV_framebuffer_multisample_coverage +#define GL_NV_framebuffer_multisample_coverage 1 +#define GL_RENDERBUFFER_COVERAGE_SAMPLES_NV 0x8CAB +#define GL_RENDERBUFFER_COLOR_SAMPLES_NV 0x8E10 +#define GL_MAX_MULTISAMPLE_COVERAGE_MODES_NV 0x8E11 +#define GL_MULTISAMPLE_COVERAGE_MODES_NV 0x8E12 +typedef void (APIENTRYP PFNGLRENDERBUFFERSTORAGEMULTISAMPLECOVERAGENVPROC) (GLenum target, GLsizei coverageSamples, GLsizei colorSamples, GLenum internalformat, GLsizei width, GLsizei height); +#ifdef GL_GLEXT_PROTOTYPES +GLAPI void APIENTRY glRenderbufferStorageMultisampleCoverageNV (GLenum target, GLsizei coverageSamples, GLsizei colorSamples, GLenum internalformat, GLsizei width, GLsizei height); +#endif +#endif /* GL_NV_framebuffer_multisample_coverage */ + +#ifndef GL_NV_geometry_program4 +#define GL_NV_geometry_program4 1 +#define GL_GEOMETRY_PROGRAM_NV 0x8C26 +#define GL_MAX_PROGRAM_OUTPUT_VERTICES_NV 0x8C27 +#define GL_MAX_PROGRAM_TOTAL_OUTPUT_COMPONENTS_NV 0x8C28 +typedef void (APIENTRYP PFNGLPROGRAMVERTEXLIMITNVPROC) (GLenum target, GLint limit); +typedef void (APIENTRYP PFNGLFRAMEBUFFERTEXTUREEXTPROC) (GLenum target, GLenum attachment, GLuint texture, GLint level); +typedef void (APIENTRYP PFNGLFRAMEBUFFERTEXTURELAYEREXTPROC) (GLenum target, GLenum attachment, GLuint texture, GLint level, GLint layer); +typedef void (APIENTRYP PFNGLFRAMEBUFFERTEXTUREFACEEXTPROC) (GLenum target, GLenum attachment, GLuint texture, GLint level, GLenum face); +#ifdef GL_GLEXT_PROTOTYPES +GLAPI void APIENTRY glProgramVertexLimitNV (GLenum target, GLint limit); +GLAPI void APIENTRY glFramebufferTextureEXT (GLenum target, GLenum attachment, GLuint texture, GLint level); +GLAPI void APIENTRY glFramebufferTextureLayerEXT (GLenum target, GLenum attachment, GLuint texture, GLint level, GLint layer); +GLAPI void APIENTRY glFramebufferTextureFaceEXT (GLenum target, GLenum attachment, GLuint texture, GLint level, GLenum face); +#endif +#endif /* GL_NV_geometry_program4 */ + +#ifndef GL_NV_geometry_shader4 +#define GL_NV_geometry_shader4 1 +#endif /* GL_NV_geometry_shader4 */ + +#ifndef GL_NV_gpu_program4 +#define GL_NV_gpu_program4 1 +#define GL_MIN_PROGRAM_TEXEL_OFFSET_NV 0x8904 +#define GL_MAX_PROGRAM_TEXEL_OFFSET_NV 0x8905 +#define GL_PROGRAM_ATTRIB_COMPONENTS_NV 0x8906 +#define GL_PROGRAM_RESULT_COMPONENTS_NV 0x8907 +#define GL_MAX_PROGRAM_ATTRIB_COMPONENTS_NV 0x8908 +#define GL_MAX_PROGRAM_RESULT_COMPONENTS_NV 0x8909 +#define GL_MAX_PROGRAM_GENERIC_ATTRIBS_NV 0x8DA5 +#define GL_MAX_PROGRAM_GENERIC_RESULTS_NV 0x8DA6 +typedef void (APIENTRYP PFNGLPROGRAMLOCALPARAMETERI4INVPROC) (GLenum target, GLuint index, GLint x, GLint y, GLint z, GLint w); +typedef void (APIENTRYP PFNGLPROGRAMLOCALPARAMETERI4IVNVPROC) (GLenum target, GLuint index, const GLint *params); +typedef void (APIENTRYP PFNGLPROGRAMLOCALPARAMETERSI4IVNVPROC) (GLenum target, GLuint index, GLsizei count, const GLint *params); +typedef void (APIENTRYP PFNGLPROGRAMLOCALPARAMETERI4UINVPROC) (GLenum target, GLuint index, GLuint x, GLuint y, GLuint z, GLuint w); +typedef void (APIENTRYP PFNGLPROGRAMLOCALPARAMETERI4UIVNVPROC) (GLenum target, GLuint index, const GLuint *params); +typedef void (APIENTRYP PFNGLPROGRAMLOCALPARAMETERSI4UIVNVPROC) (GLenum target, GLuint index, GLsizei count, const GLuint *params); +typedef void (APIENTRYP PFNGLPROGRAMENVPARAMETERI4INVPROC) (GLenum target, GLuint index, GLint x, GLint y, GLint z, GLint w); +typedef void (APIENTRYP PFNGLPROGRAMENVPARAMETERI4IVNVPROC) (GLenum target, GLuint index, const GLint *params); +typedef void (APIENTRYP PFNGLPROGRAMENVPARAMETERSI4IVNVPROC) (GLenum target, GLuint index, GLsizei count, const GLint *params); +typedef void (APIENTRYP PFNGLPROGRAMENVPARAMETERI4UINVPROC) (GLenum target, GLuint index, GLuint x, GLuint y, GLuint z, GLuint w); +typedef void (APIENTRYP PFNGLPROGRAMENVPARAMETERI4UIVNVPROC) (GLenum target, GLuint index, const GLuint *params); +typedef void (APIENTRYP PFNGLPROGRAMENVPARAMETERSI4UIVNVPROC) (GLenum target, GLuint index, GLsizei count, const GLuint *params); +typedef void (APIENTRYP PFNGLGETPROGRAMLOCALPARAMETERIIVNVPROC) (GLenum target, GLuint index, GLint *params); +typedef void (APIENTRYP PFNGLGETPROGRAMLOCALPARAMETERIUIVNVPROC) (GLenum target, GLuint index, GLuint *params); +typedef void (APIENTRYP PFNGLGETPROGRAMENVPARAMETERIIVNVPROC) (GLenum target, GLuint index, GLint *params); +typedef void (APIENTRYP PFNGLGETPROGRAMENVPARAMETERIUIVNVPROC) (GLenum target, GLuint index, GLuint *params); +#ifdef GL_GLEXT_PROTOTYPES +GLAPI void APIENTRY glProgramLocalParameterI4iNV (GLenum target, GLuint index, GLint x, GLint y, GLint z, GLint w); +GLAPI void APIENTRY glProgramLocalParameterI4ivNV (GLenum target, GLuint index, const GLint *params); +GLAPI void APIENTRY glProgramLocalParametersI4ivNV (GLenum target, GLuint index, GLsizei count, const GLint *params); +GLAPI void APIENTRY glProgramLocalParameterI4uiNV (GLenum target, GLuint index, GLuint x, GLuint y, GLuint z, GLuint w); +GLAPI void APIENTRY glProgramLocalParameterI4uivNV (GLenum target, GLuint index, const GLuint *params); +GLAPI void APIENTRY glProgramLocalParametersI4uivNV (GLenum target, GLuint index, GLsizei count, const GLuint *params); +GLAPI void APIENTRY glProgramEnvParameterI4iNV (GLenum target, GLuint index, GLint x, GLint y, GLint z, GLint w); +GLAPI void APIENTRY glProgramEnvParameterI4ivNV (GLenum target, GLuint index, const GLint *params); +GLAPI void APIENTRY glProgramEnvParametersI4ivNV (GLenum target, GLuint index, GLsizei count, const GLint *params); +GLAPI void APIENTRY glProgramEnvParameterI4uiNV (GLenum target, GLuint index, GLuint x, GLuint y, GLuint z, GLuint w); +GLAPI void APIENTRY glProgramEnvParameterI4uivNV (GLenum target, GLuint index, const GLuint *params); +GLAPI void APIENTRY glProgramEnvParametersI4uivNV (GLenum target, GLuint index, GLsizei count, const GLuint *params); +GLAPI void APIENTRY glGetProgramLocalParameterIivNV (GLenum target, GLuint index, GLint *params); +GLAPI void APIENTRY glGetProgramLocalParameterIuivNV (GLenum target, GLuint index, GLuint *params); +GLAPI void APIENTRY glGetProgramEnvParameterIivNV (GLenum target, GLuint index, GLint *params); +GLAPI void APIENTRY glGetProgramEnvParameterIuivNV (GLenum target, GLuint index, GLuint *params); +#endif +#endif /* GL_NV_gpu_program4 */ + +#ifndef GL_NV_gpu_program5 +#define GL_NV_gpu_program5 1 +#define GL_MAX_GEOMETRY_PROGRAM_INVOCATIONS_NV 0x8E5A +#define GL_MIN_FRAGMENT_INTERPOLATION_OFFSET_NV 0x8E5B +#define GL_MAX_FRAGMENT_INTERPOLATION_OFFSET_NV 0x8E5C +#define GL_FRAGMENT_PROGRAM_INTERPOLATION_OFFSET_BITS_NV 0x8E5D +#define GL_MIN_PROGRAM_TEXTURE_GATHER_OFFSET_NV 0x8E5E +#define GL_MAX_PROGRAM_TEXTURE_GATHER_OFFSET_NV 0x8E5F +#define GL_MAX_PROGRAM_SUBROUTINE_PARAMETERS_NV 0x8F44 +#define GL_MAX_PROGRAM_SUBROUTINE_NUM_NV 0x8F45 +typedef void (APIENTRYP PFNGLPROGRAMSUBROUTINEPARAMETERSUIVNVPROC) (GLenum target, GLsizei count, const GLuint *params); +typedef void (APIENTRYP PFNGLGETPROGRAMSUBROUTINEPARAMETERUIVNVPROC) (GLenum target, GLuint index, GLuint *param); +#ifdef GL_GLEXT_PROTOTYPES +GLAPI void APIENTRY glProgramSubroutineParametersuivNV (GLenum target, GLsizei count, const GLuint *params); +GLAPI void APIENTRY glGetProgramSubroutineParameteruivNV (GLenum target, GLuint index, GLuint *param); +#endif +#endif /* GL_NV_gpu_program5 */ + +#ifndef GL_NV_gpu_program5_mem_extended +#define GL_NV_gpu_program5_mem_extended 1 +#endif /* GL_NV_gpu_program5_mem_extended */ + +#ifndef GL_NV_gpu_shader5 +#define GL_NV_gpu_shader5 1 +typedef int64_t GLint64EXT; +#define GL_INT64_NV 0x140E +#define GL_UNSIGNED_INT64_NV 0x140F +#define GL_INT8_NV 0x8FE0 +#define GL_INT8_VEC2_NV 0x8FE1 +#define GL_INT8_VEC3_NV 0x8FE2 +#define GL_INT8_VEC4_NV 0x8FE3 +#define GL_INT16_NV 0x8FE4 +#define GL_INT16_VEC2_NV 0x8FE5 +#define GL_INT16_VEC3_NV 0x8FE6 +#define GL_INT16_VEC4_NV 0x8FE7 +#define GL_INT64_VEC2_NV 0x8FE9 +#define GL_INT64_VEC3_NV 0x8FEA +#define GL_INT64_VEC4_NV 0x8FEB +#define GL_UNSIGNED_INT8_NV 0x8FEC +#define GL_UNSIGNED_INT8_VEC2_NV 0x8FED +#define GL_UNSIGNED_INT8_VEC3_NV 0x8FEE +#define GL_UNSIGNED_INT8_VEC4_NV 0x8FEF +#define GL_UNSIGNED_INT16_NV 0x8FF0 +#define GL_UNSIGNED_INT16_VEC2_NV 0x8FF1 +#define GL_UNSIGNED_INT16_VEC3_NV 0x8FF2 +#define GL_UNSIGNED_INT16_VEC4_NV 0x8FF3 +#define GL_UNSIGNED_INT64_VEC2_NV 0x8FF5 +#define GL_UNSIGNED_INT64_VEC3_NV 0x8FF6 +#define GL_UNSIGNED_INT64_VEC4_NV 0x8FF7 +#define GL_FLOAT16_NV 0x8FF8 +#define GL_FLOAT16_VEC2_NV 0x8FF9 +#define GL_FLOAT16_VEC3_NV 0x8FFA +#define GL_FLOAT16_VEC4_NV 0x8FFB +typedef void (APIENTRYP PFNGLUNIFORM1I64NVPROC) (GLint location, GLint64EXT x); +typedef void (APIENTRYP PFNGLUNIFORM2I64NVPROC) (GLint location, GLint64EXT x, GLint64EXT y); +typedef void (APIENTRYP PFNGLUNIFORM3I64NVPROC) (GLint location, GLint64EXT x, GLint64EXT y, GLint64EXT z); +typedef void (APIENTRYP PFNGLUNIFORM4I64NVPROC) (GLint location, GLint64EXT x, GLint64EXT y, GLint64EXT z, GLint64EXT w); +typedef void (APIENTRYP PFNGLUNIFORM1I64VNVPROC) (GLint location, GLsizei count, const GLint64EXT *value); +typedef void (APIENTRYP PFNGLUNIFORM2I64VNVPROC) (GLint location, GLsizei count, const GLint64EXT *value); +typedef void (APIENTRYP PFNGLUNIFORM3I64VNVPROC) (GLint location, GLsizei count, const GLint64EXT *value); +typedef void (APIENTRYP PFNGLUNIFORM4I64VNVPROC) (GLint location, GLsizei count, const GLint64EXT *value); +typedef void (APIENTRYP PFNGLUNIFORM1UI64NVPROC) (GLint location, GLuint64EXT x); +typedef void (APIENTRYP PFNGLUNIFORM2UI64NVPROC) (GLint location, GLuint64EXT x, GLuint64EXT y); +typedef void (APIENTRYP PFNGLUNIFORM3UI64NVPROC) (GLint location, GLuint64EXT x, GLuint64EXT y, GLuint64EXT z); +typedef void (APIENTRYP PFNGLUNIFORM4UI64NVPROC) (GLint location, GLuint64EXT x, GLuint64EXT y, GLuint64EXT z, GLuint64EXT w); +typedef void (APIENTRYP PFNGLUNIFORM1UI64VNVPROC) (GLint location, GLsizei count, const GLuint64EXT *value); +typedef void (APIENTRYP PFNGLUNIFORM2UI64VNVPROC) (GLint location, GLsizei count, const GLuint64EXT *value); +typedef void (APIENTRYP PFNGLUNIFORM3UI64VNVPROC) (GLint location, GLsizei count, const GLuint64EXT *value); +typedef void (APIENTRYP PFNGLUNIFORM4UI64VNVPROC) (GLint location, GLsizei count, const GLuint64EXT *value); +typedef void (APIENTRYP PFNGLGETUNIFORMI64VNVPROC) (GLuint program, GLint location, GLint64EXT *params); +typedef void (APIENTRYP PFNGLPROGRAMUNIFORM1I64NVPROC) (GLuint program, GLint location, GLint64EXT x); +typedef void (APIENTRYP PFNGLPROGRAMUNIFORM2I64NVPROC) (GLuint program, GLint location, GLint64EXT x, GLint64EXT y); +typedef void (APIENTRYP PFNGLPROGRAMUNIFORM3I64NVPROC) (GLuint program, GLint location, GLint64EXT x, GLint64EXT y, GLint64EXT z); +typedef void (APIENTRYP PFNGLPROGRAMUNIFORM4I64NVPROC) (GLuint program, GLint location, GLint64EXT x, GLint64EXT y, GLint64EXT z, GLint64EXT w); +typedef void (APIENTRYP PFNGLPROGRAMUNIFORM1I64VNVPROC) (GLuint program, GLint location, GLsizei count, const GLint64EXT *value); +typedef void (APIENTRYP PFNGLPROGRAMUNIFORM2I64VNVPROC) (GLuint program, GLint location, GLsizei count, const GLint64EXT *value); +typedef void (APIENTRYP PFNGLPROGRAMUNIFORM3I64VNVPROC) (GLuint program, GLint location, GLsizei count, const GLint64EXT *value); +typedef void (APIENTRYP PFNGLPROGRAMUNIFORM4I64VNVPROC) (GLuint program, GLint location, GLsizei count, const GLint64EXT *value); +typedef void (APIENTRYP PFNGLPROGRAMUNIFORM1UI64NVPROC) (GLuint program, GLint location, GLuint64EXT x); +typedef void (APIENTRYP PFNGLPROGRAMUNIFORM2UI64NVPROC) (GLuint program, GLint location, GLuint64EXT x, GLuint64EXT y); +typedef void (APIENTRYP PFNGLPROGRAMUNIFORM3UI64NVPROC) (GLuint program, GLint location, GLuint64EXT x, GLuint64EXT y, GLuint64EXT z); +typedef void (APIENTRYP PFNGLPROGRAMUNIFORM4UI64NVPROC) (GLuint program, GLint location, GLuint64EXT x, GLuint64EXT y, GLuint64EXT z, GLuint64EXT w); +typedef void (APIENTRYP PFNGLPROGRAMUNIFORM1UI64VNVPROC) (GLuint program, GLint location, GLsizei count, const GLuint64EXT *value); +typedef void (APIENTRYP PFNGLPROGRAMUNIFORM2UI64VNVPROC) (GLuint program, GLint location, GLsizei count, const GLuint64EXT *value); +typedef void (APIENTRYP PFNGLPROGRAMUNIFORM3UI64VNVPROC) (GLuint program, GLint location, GLsizei count, const GLuint64EXT *value); +typedef void (APIENTRYP PFNGLPROGRAMUNIFORM4UI64VNVPROC) (GLuint program, GLint location, GLsizei count, const GLuint64EXT *value); +#ifdef GL_GLEXT_PROTOTYPES +GLAPI void APIENTRY glUniform1i64NV (GLint location, GLint64EXT x); +GLAPI void APIENTRY glUniform2i64NV (GLint location, GLint64EXT x, GLint64EXT y); +GLAPI void APIENTRY glUniform3i64NV (GLint location, GLint64EXT x, GLint64EXT y, GLint64EXT z); +GLAPI void APIENTRY glUniform4i64NV (GLint location, GLint64EXT x, GLint64EXT y, GLint64EXT z, GLint64EXT w); +GLAPI void APIENTRY glUniform1i64vNV (GLint location, GLsizei count, const GLint64EXT *value); +GLAPI void APIENTRY glUniform2i64vNV (GLint location, GLsizei count, const GLint64EXT *value); +GLAPI void APIENTRY glUniform3i64vNV (GLint location, GLsizei count, const GLint64EXT *value); +GLAPI void APIENTRY glUniform4i64vNV (GLint location, GLsizei count, const GLint64EXT *value); +GLAPI void APIENTRY glUniform1ui64NV (GLint location, GLuint64EXT x); +GLAPI void APIENTRY glUniform2ui64NV (GLint location, GLuint64EXT x, GLuint64EXT y); +GLAPI void APIENTRY glUniform3ui64NV (GLint location, GLuint64EXT x, GLuint64EXT y, GLuint64EXT z); +GLAPI void APIENTRY glUniform4ui64NV (GLint location, GLuint64EXT x, GLuint64EXT y, GLuint64EXT z, GLuint64EXT w); +GLAPI void APIENTRY glUniform1ui64vNV (GLint location, GLsizei count, const GLuint64EXT *value); +GLAPI void APIENTRY glUniform2ui64vNV (GLint location, GLsizei count, const GLuint64EXT *value); +GLAPI void APIENTRY glUniform3ui64vNV (GLint location, GLsizei count, const GLuint64EXT *value); +GLAPI void APIENTRY glUniform4ui64vNV (GLint location, GLsizei count, const GLuint64EXT *value); +GLAPI void APIENTRY glGetUniformi64vNV (GLuint program, GLint location, GLint64EXT *params); +GLAPI void APIENTRY glProgramUniform1i64NV (GLuint program, GLint location, GLint64EXT x); +GLAPI void APIENTRY glProgramUniform2i64NV (GLuint program, GLint location, GLint64EXT x, GLint64EXT y); +GLAPI void APIENTRY glProgramUniform3i64NV (GLuint program, GLint location, GLint64EXT x, GLint64EXT y, GLint64EXT z); +GLAPI void APIENTRY glProgramUniform4i64NV (GLuint program, GLint location, GLint64EXT x, GLint64EXT y, GLint64EXT z, GLint64EXT w); +GLAPI void APIENTRY glProgramUniform1i64vNV (GLuint program, GLint location, GLsizei count, const GLint64EXT *value); +GLAPI void APIENTRY glProgramUniform2i64vNV (GLuint program, GLint location, GLsizei count, const GLint64EXT *value); +GLAPI void APIENTRY glProgramUniform3i64vNV (GLuint program, GLint location, GLsizei count, const GLint64EXT *value); +GLAPI void APIENTRY glProgramUniform4i64vNV (GLuint program, GLint location, GLsizei count, const GLint64EXT *value); +GLAPI void APIENTRY glProgramUniform1ui64NV (GLuint program, GLint location, GLuint64EXT x); +GLAPI void APIENTRY glProgramUniform2ui64NV (GLuint program, GLint location, GLuint64EXT x, GLuint64EXT y); +GLAPI void APIENTRY glProgramUniform3ui64NV (GLuint program, GLint location, GLuint64EXT x, GLuint64EXT y, GLuint64EXT z); +GLAPI void APIENTRY glProgramUniform4ui64NV (GLuint program, GLint location, GLuint64EXT x, GLuint64EXT y, GLuint64EXT z, GLuint64EXT w); +GLAPI void APIENTRY glProgramUniform1ui64vNV (GLuint program, GLint location, GLsizei count, const GLuint64EXT *value); +GLAPI void APIENTRY glProgramUniform2ui64vNV (GLuint program, GLint location, GLsizei count, const GLuint64EXT *value); +GLAPI void APIENTRY glProgramUniform3ui64vNV (GLuint program, GLint location, GLsizei count, const GLuint64EXT *value); +GLAPI void APIENTRY glProgramUniform4ui64vNV (GLuint program, GLint location, GLsizei count, const GLuint64EXT *value); +#endif +#endif /* GL_NV_gpu_shader5 */ + +#ifndef GL_NV_half_float +#define GL_NV_half_float 1 +typedef unsigned short GLhalfNV; +#define GL_HALF_FLOAT_NV 0x140B +typedef void (APIENTRYP PFNGLVERTEX2HNVPROC) (GLhalfNV x, GLhalfNV y); +typedef void (APIENTRYP PFNGLVERTEX2HVNVPROC) (const GLhalfNV *v); +typedef void (APIENTRYP PFNGLVERTEX3HNVPROC) (GLhalfNV x, GLhalfNV y, GLhalfNV z); +typedef void (APIENTRYP PFNGLVERTEX3HVNVPROC) (const GLhalfNV *v); +typedef void (APIENTRYP PFNGLVERTEX4HNVPROC) (GLhalfNV x, GLhalfNV y, GLhalfNV z, GLhalfNV w); +typedef void (APIENTRYP PFNGLVERTEX4HVNVPROC) (const GLhalfNV *v); +typedef void (APIENTRYP PFNGLNORMAL3HNVPROC) (GLhalfNV nx, GLhalfNV ny, GLhalfNV nz); +typedef void (APIENTRYP PFNGLNORMAL3HVNVPROC) (const GLhalfNV *v); +typedef void (APIENTRYP PFNGLCOLOR3HNVPROC) (GLhalfNV red, GLhalfNV green, GLhalfNV blue); +typedef void (APIENTRYP PFNGLCOLOR3HVNVPROC) (const GLhalfNV *v); +typedef void (APIENTRYP PFNGLCOLOR4HNVPROC) (GLhalfNV red, GLhalfNV green, GLhalfNV blue, GLhalfNV alpha); +typedef void (APIENTRYP PFNGLCOLOR4HVNVPROC) (const GLhalfNV *v); +typedef void (APIENTRYP PFNGLTEXCOORD1HNVPROC) (GLhalfNV s); +typedef void (APIENTRYP PFNGLTEXCOORD1HVNVPROC) (const GLhalfNV *v); +typedef void (APIENTRYP PFNGLTEXCOORD2HNVPROC) (GLhalfNV s, GLhalfNV t); +typedef void (APIENTRYP PFNGLTEXCOORD2HVNVPROC) (const GLhalfNV *v); +typedef void (APIENTRYP PFNGLTEXCOORD3HNVPROC) (GLhalfNV s, GLhalfNV t, GLhalfNV r); +typedef void (APIENTRYP PFNGLTEXCOORD3HVNVPROC) (const GLhalfNV *v); +typedef void (APIENTRYP PFNGLTEXCOORD4HNVPROC) (GLhalfNV s, GLhalfNV t, GLhalfNV r, GLhalfNV q); +typedef void (APIENTRYP PFNGLTEXCOORD4HVNVPROC) (const GLhalfNV *v); +typedef void (APIENTRYP PFNGLMULTITEXCOORD1HNVPROC) (GLenum target, GLhalfNV s); +typedef void (APIENTRYP PFNGLMULTITEXCOORD1HVNVPROC) (GLenum target, const GLhalfNV *v); +typedef void (APIENTRYP PFNGLMULTITEXCOORD2HNVPROC) (GLenum target, GLhalfNV s, GLhalfNV t); +typedef void (APIENTRYP PFNGLMULTITEXCOORD2HVNVPROC) (GLenum target, const GLhalfNV *v); +typedef void (APIENTRYP PFNGLMULTITEXCOORD3HNVPROC) (GLenum target, GLhalfNV s, GLhalfNV t, GLhalfNV r); +typedef void (APIENTRYP PFNGLMULTITEXCOORD3HVNVPROC) (GLenum target, const GLhalfNV *v); +typedef void (APIENTRYP PFNGLMULTITEXCOORD4HNVPROC) (GLenum target, GLhalfNV s, GLhalfNV t, GLhalfNV r, GLhalfNV q); +typedef void (APIENTRYP PFNGLMULTITEXCOORD4HVNVPROC) (GLenum target, const GLhalfNV *v); +typedef void (APIENTRYP PFNGLFOGCOORDHNVPROC) (GLhalfNV fog); +typedef void (APIENTRYP PFNGLFOGCOORDHVNVPROC) (const GLhalfNV *fog); +typedef void (APIENTRYP PFNGLSECONDARYCOLOR3HNVPROC) (GLhalfNV red, GLhalfNV green, GLhalfNV blue); +typedef void (APIENTRYP PFNGLSECONDARYCOLOR3HVNVPROC) (const GLhalfNV *v); +typedef void (APIENTRYP PFNGLVERTEXWEIGHTHNVPROC) (GLhalfNV weight); +typedef void (APIENTRYP PFNGLVERTEXWEIGHTHVNVPROC) (const GLhalfNV *weight); +typedef void (APIENTRYP PFNGLVERTEXATTRIB1HNVPROC) (GLuint index, GLhalfNV x); +typedef void (APIENTRYP PFNGLVERTEXATTRIB1HVNVPROC) (GLuint index, const GLhalfNV *v); +typedef void (APIENTRYP PFNGLVERTEXATTRIB2HNVPROC) (GLuint index, GLhalfNV x, GLhalfNV y); +typedef void (APIENTRYP PFNGLVERTEXATTRIB2HVNVPROC) (GLuint index, const GLhalfNV *v); +typedef void (APIENTRYP PFNGLVERTEXATTRIB3HNVPROC) (GLuint index, GLhalfNV x, GLhalfNV y, GLhalfNV z); +typedef void (APIENTRYP PFNGLVERTEXATTRIB3HVNVPROC) (GLuint index, const GLhalfNV *v); +typedef void (APIENTRYP PFNGLVERTEXATTRIB4HNVPROC) (GLuint index, GLhalfNV x, GLhalfNV y, GLhalfNV z, GLhalfNV w); +typedef void (APIENTRYP PFNGLVERTEXATTRIB4HVNVPROC) (GLuint index, const GLhalfNV *v); +typedef void (APIENTRYP PFNGLVERTEXATTRIBS1HVNVPROC) (GLuint index, GLsizei n, const GLhalfNV *v); +typedef void (APIENTRYP PFNGLVERTEXATTRIBS2HVNVPROC) (GLuint index, GLsizei n, const GLhalfNV *v); +typedef void (APIENTRYP PFNGLVERTEXATTRIBS3HVNVPROC) (GLuint index, GLsizei n, const GLhalfNV *v); +typedef void (APIENTRYP PFNGLVERTEXATTRIBS4HVNVPROC) (GLuint index, GLsizei n, const GLhalfNV *v); +#ifdef GL_GLEXT_PROTOTYPES +GLAPI void APIENTRY glVertex2hNV (GLhalfNV x, GLhalfNV y); +GLAPI void APIENTRY glVertex2hvNV (const GLhalfNV *v); +GLAPI void APIENTRY glVertex3hNV (GLhalfNV x, GLhalfNV y, GLhalfNV z); +GLAPI void APIENTRY glVertex3hvNV (const GLhalfNV *v); +GLAPI void APIENTRY glVertex4hNV (GLhalfNV x, GLhalfNV y, GLhalfNV z, GLhalfNV w); +GLAPI void APIENTRY glVertex4hvNV (const GLhalfNV *v); +GLAPI void APIENTRY glNormal3hNV (GLhalfNV nx, GLhalfNV ny, GLhalfNV nz); +GLAPI void APIENTRY glNormal3hvNV (const GLhalfNV *v); +GLAPI void APIENTRY glColor3hNV (GLhalfNV red, GLhalfNV green, GLhalfNV blue); +GLAPI void APIENTRY glColor3hvNV (const GLhalfNV *v); +GLAPI void APIENTRY glColor4hNV (GLhalfNV red, GLhalfNV green, GLhalfNV blue, GLhalfNV alpha); +GLAPI void APIENTRY glColor4hvNV (const GLhalfNV *v); +GLAPI void APIENTRY glTexCoord1hNV (GLhalfNV s); +GLAPI void APIENTRY glTexCoord1hvNV (const GLhalfNV *v); +GLAPI void APIENTRY glTexCoord2hNV (GLhalfNV s, GLhalfNV t); +GLAPI void APIENTRY glTexCoord2hvNV (const GLhalfNV *v); +GLAPI void APIENTRY glTexCoord3hNV (GLhalfNV s, GLhalfNV t, GLhalfNV r); +GLAPI void APIENTRY glTexCoord3hvNV (const GLhalfNV *v); +GLAPI void APIENTRY glTexCoord4hNV (GLhalfNV s, GLhalfNV t, GLhalfNV r, GLhalfNV q); +GLAPI void APIENTRY glTexCoord4hvNV (const GLhalfNV *v); +GLAPI void APIENTRY glMultiTexCoord1hNV (GLenum target, GLhalfNV s); +GLAPI void APIENTRY glMultiTexCoord1hvNV (GLenum target, const GLhalfNV *v); +GLAPI void APIENTRY glMultiTexCoord2hNV (GLenum target, GLhalfNV s, GLhalfNV t); +GLAPI void APIENTRY glMultiTexCoord2hvNV (GLenum target, const GLhalfNV *v); +GLAPI void APIENTRY glMultiTexCoord3hNV (GLenum target, GLhalfNV s, GLhalfNV t, GLhalfNV r); +GLAPI void APIENTRY glMultiTexCoord3hvNV (GLenum target, const GLhalfNV *v); +GLAPI void APIENTRY glMultiTexCoord4hNV (GLenum target, GLhalfNV s, GLhalfNV t, GLhalfNV r, GLhalfNV q); +GLAPI void APIENTRY glMultiTexCoord4hvNV (GLenum target, const GLhalfNV *v); +GLAPI void APIENTRY glFogCoordhNV (GLhalfNV fog); +GLAPI void APIENTRY glFogCoordhvNV (const GLhalfNV *fog); +GLAPI void APIENTRY glSecondaryColor3hNV (GLhalfNV red, GLhalfNV green, GLhalfNV blue); +GLAPI void APIENTRY glSecondaryColor3hvNV (const GLhalfNV *v); +GLAPI void APIENTRY glVertexWeighthNV (GLhalfNV weight); +GLAPI void APIENTRY glVertexWeighthvNV (const GLhalfNV *weight); +GLAPI void APIENTRY glVertexAttrib1hNV (GLuint index, GLhalfNV x); +GLAPI void APIENTRY glVertexAttrib1hvNV (GLuint index, const GLhalfNV *v); +GLAPI void APIENTRY glVertexAttrib2hNV (GLuint index, GLhalfNV x, GLhalfNV y); +GLAPI void APIENTRY glVertexAttrib2hvNV (GLuint index, const GLhalfNV *v); +GLAPI void APIENTRY glVertexAttrib3hNV (GLuint index, GLhalfNV x, GLhalfNV y, GLhalfNV z); +GLAPI void APIENTRY glVertexAttrib3hvNV (GLuint index, const GLhalfNV *v); +GLAPI void APIENTRY glVertexAttrib4hNV (GLuint index, GLhalfNV x, GLhalfNV y, GLhalfNV z, GLhalfNV w); +GLAPI void APIENTRY glVertexAttrib4hvNV (GLuint index, const GLhalfNV *v); +GLAPI void APIENTRY glVertexAttribs1hvNV (GLuint index, GLsizei n, const GLhalfNV *v); +GLAPI void APIENTRY glVertexAttribs2hvNV (GLuint index, GLsizei n, const GLhalfNV *v); +GLAPI void APIENTRY glVertexAttribs3hvNV (GLuint index, GLsizei n, const GLhalfNV *v); +GLAPI void APIENTRY glVertexAttribs4hvNV (GLuint index, GLsizei n, const GLhalfNV *v); +#endif +#endif /* GL_NV_half_float */ + +#ifndef GL_NV_light_max_exponent +#define GL_NV_light_max_exponent 1 +#define GL_MAX_SHININESS_NV 0x8504 +#define GL_MAX_SPOT_EXPONENT_NV 0x8505 +#endif /* GL_NV_light_max_exponent */ + +#ifndef GL_NV_multisample_coverage +#define GL_NV_multisample_coverage 1 +#define GL_COLOR_SAMPLES_NV 0x8E20 +#endif /* GL_NV_multisample_coverage */ + +#ifndef GL_NV_multisample_filter_hint +#define GL_NV_multisample_filter_hint 1 +#define GL_MULTISAMPLE_FILTER_HINT_NV 0x8534 +#endif /* GL_NV_multisample_filter_hint */ + +#ifndef GL_NV_occlusion_query +#define GL_NV_occlusion_query 1 +#define GL_PIXEL_COUNTER_BITS_NV 0x8864 +#define GL_CURRENT_OCCLUSION_QUERY_ID_NV 0x8865 +#define GL_PIXEL_COUNT_NV 0x8866 +#define GL_PIXEL_COUNT_AVAILABLE_NV 0x8867 +typedef void (APIENTRYP PFNGLGENOCCLUSIONQUERIESNVPROC) (GLsizei n, GLuint *ids); +typedef void (APIENTRYP PFNGLDELETEOCCLUSIONQUERIESNVPROC) (GLsizei n, const GLuint *ids); +typedef GLboolean (APIENTRYP PFNGLISOCCLUSIONQUERYNVPROC) (GLuint id); +typedef void (APIENTRYP PFNGLBEGINOCCLUSIONQUERYNVPROC) (GLuint id); +typedef void (APIENTRYP PFNGLENDOCCLUSIONQUERYNVPROC) (void); +typedef void (APIENTRYP PFNGLGETOCCLUSIONQUERYIVNVPROC) (GLuint id, GLenum pname, GLint *params); +typedef void (APIENTRYP PFNGLGETOCCLUSIONQUERYUIVNVPROC) (GLuint id, GLenum pname, GLuint *params); +#ifdef GL_GLEXT_PROTOTYPES +GLAPI void APIENTRY glGenOcclusionQueriesNV (GLsizei n, GLuint *ids); +GLAPI void APIENTRY glDeleteOcclusionQueriesNV (GLsizei n, const GLuint *ids); +GLAPI GLboolean APIENTRY glIsOcclusionQueryNV (GLuint id); +GLAPI void APIENTRY glBeginOcclusionQueryNV (GLuint id); +GLAPI void APIENTRY glEndOcclusionQueryNV (void); +GLAPI void APIENTRY glGetOcclusionQueryivNV (GLuint id, GLenum pname, GLint *params); +GLAPI void APIENTRY glGetOcclusionQueryuivNV (GLuint id, GLenum pname, GLuint *params); +#endif +#endif /* GL_NV_occlusion_query */ + +#ifndef GL_NV_packed_depth_stencil +#define GL_NV_packed_depth_stencil 1 +#define GL_DEPTH_STENCIL_NV 0x84F9 +#define GL_UNSIGNED_INT_24_8_NV 0x84FA +#endif /* GL_NV_packed_depth_stencil */ + +#ifndef GL_NV_parameter_buffer_object +#define GL_NV_parameter_buffer_object 1 +#define GL_MAX_PROGRAM_PARAMETER_BUFFER_BINDINGS_NV 0x8DA0 +#define GL_MAX_PROGRAM_PARAMETER_BUFFER_SIZE_NV 0x8DA1 +#define GL_VERTEX_PROGRAM_PARAMETER_BUFFER_NV 0x8DA2 +#define GL_GEOMETRY_PROGRAM_PARAMETER_BUFFER_NV 0x8DA3 +#define GL_FRAGMENT_PROGRAM_PARAMETER_BUFFER_NV 0x8DA4 +typedef void (APIENTRYP PFNGLPROGRAMBUFFERPARAMETERSFVNVPROC) (GLenum target, GLuint bindingIndex, GLuint wordIndex, GLsizei count, const GLfloat *params); +typedef void (APIENTRYP PFNGLPROGRAMBUFFERPARAMETERSIIVNVPROC) (GLenum target, GLuint bindingIndex, GLuint wordIndex, GLsizei count, const GLint *params); +typedef void (APIENTRYP PFNGLPROGRAMBUFFERPARAMETERSIUIVNVPROC) (GLenum target, GLuint bindingIndex, GLuint wordIndex, GLsizei count, const GLuint *params); +#ifdef GL_GLEXT_PROTOTYPES +GLAPI void APIENTRY glProgramBufferParametersfvNV (GLenum target, GLuint bindingIndex, GLuint wordIndex, GLsizei count, const GLfloat *params); +GLAPI void APIENTRY glProgramBufferParametersIivNV (GLenum target, GLuint bindingIndex, GLuint wordIndex, GLsizei count, const GLint *params); +GLAPI void APIENTRY glProgramBufferParametersIuivNV (GLenum target, GLuint bindingIndex, GLuint wordIndex, GLsizei count, const GLuint *params); +#endif +#endif /* GL_NV_parameter_buffer_object */ + +#ifndef GL_NV_parameter_buffer_object2 +#define GL_NV_parameter_buffer_object2 1 +#endif /* GL_NV_parameter_buffer_object2 */ + +#ifndef GL_NV_path_rendering +#define GL_NV_path_rendering 1 +#define GL_PATH_FORMAT_SVG_NV 0x9070 +#define GL_PATH_FORMAT_PS_NV 0x9071 +#define GL_STANDARD_FONT_NAME_NV 0x9072 +#define GL_SYSTEM_FONT_NAME_NV 0x9073 +#define GL_FILE_NAME_NV 0x9074 +#define GL_PATH_STROKE_WIDTH_NV 0x9075 +#define GL_PATH_END_CAPS_NV 0x9076 +#define GL_PATH_INITIAL_END_CAP_NV 0x9077 +#define GL_PATH_TERMINAL_END_CAP_NV 0x9078 +#define GL_PATH_JOIN_STYLE_NV 0x9079 +#define GL_PATH_MITER_LIMIT_NV 0x907A +#define GL_PATH_DASH_CAPS_NV 0x907B +#define GL_PATH_INITIAL_DASH_CAP_NV 0x907C +#define GL_PATH_TERMINAL_DASH_CAP_NV 0x907D +#define GL_PATH_DASH_OFFSET_NV 0x907E +#define GL_PATH_CLIENT_LENGTH_NV 0x907F +#define GL_PATH_FILL_MODE_NV 0x9080 +#define GL_PATH_FILL_MASK_NV 0x9081 +#define GL_PATH_FILL_COVER_MODE_NV 0x9082 +#define GL_PATH_STROKE_COVER_MODE_NV 0x9083 +#define GL_PATH_STROKE_MASK_NV 0x9084 +#define GL_COUNT_UP_NV 0x9088 +#define GL_COUNT_DOWN_NV 0x9089 +#define GL_PATH_OBJECT_BOUNDING_BOX_NV 0x908A +#define GL_CONVEX_HULL_NV 0x908B +#define GL_BOUNDING_BOX_NV 0x908D +#define GL_TRANSLATE_X_NV 0x908E +#define GL_TRANSLATE_Y_NV 0x908F +#define GL_TRANSLATE_2D_NV 0x9090 +#define GL_TRANSLATE_3D_NV 0x9091 +#define GL_AFFINE_2D_NV 0x9092 +#define GL_AFFINE_3D_NV 0x9094 +#define GL_TRANSPOSE_AFFINE_2D_NV 0x9096 +#define GL_TRANSPOSE_AFFINE_3D_NV 0x9098 +#define GL_UTF8_NV 0x909A +#define GL_UTF16_NV 0x909B +#define GL_BOUNDING_BOX_OF_BOUNDING_BOXES_NV 0x909C +#define GL_PATH_COMMAND_COUNT_NV 0x909D +#define GL_PATH_COORD_COUNT_NV 0x909E +#define GL_PATH_DASH_ARRAY_COUNT_NV 0x909F +#define GL_PATH_COMPUTED_LENGTH_NV 0x90A0 +#define GL_PATH_FILL_BOUNDING_BOX_NV 0x90A1 +#define GL_PATH_STROKE_BOUNDING_BOX_NV 0x90A2 +#define GL_SQUARE_NV 0x90A3 +#define GL_ROUND_NV 0x90A4 +#define GL_TRIANGULAR_NV 0x90A5 +#define GL_BEVEL_NV 0x90A6 +#define GL_MITER_REVERT_NV 0x90A7 +#define GL_MITER_TRUNCATE_NV 0x90A8 +#define GL_SKIP_MISSING_GLYPH_NV 0x90A9 +#define GL_USE_MISSING_GLYPH_NV 0x90AA +#define GL_PATH_ERROR_POSITION_NV 0x90AB +#define GL_PATH_FOG_GEN_MODE_NV 0x90AC +#define GL_ACCUM_ADJACENT_PAIRS_NV 0x90AD +#define GL_ADJACENT_PAIRS_NV 0x90AE +#define GL_FIRST_TO_REST_NV 0x90AF +#define GL_PATH_GEN_MODE_NV 0x90B0 +#define GL_PATH_GEN_COEFF_NV 0x90B1 +#define GL_PATH_GEN_COLOR_FORMAT_NV 0x90B2 +#define GL_PATH_GEN_COMPONENTS_NV 0x90B3 +#define GL_PATH_STENCIL_FUNC_NV 0x90B7 +#define GL_PATH_STENCIL_REF_NV 0x90B8 +#define GL_PATH_STENCIL_VALUE_MASK_NV 0x90B9 +#define GL_PATH_STENCIL_DEPTH_OFFSET_FACTOR_NV 0x90BD +#define GL_PATH_STENCIL_DEPTH_OFFSET_UNITS_NV 0x90BE +#define GL_PATH_COVER_DEPTH_FUNC_NV 0x90BF +#define GL_PATH_DASH_OFFSET_RESET_NV 0x90B4 +#define GL_MOVE_TO_RESETS_NV 0x90B5 +#define GL_MOVE_TO_CONTINUES_NV 0x90B6 +#define GL_CLOSE_PATH_NV 0x00 +#define GL_MOVE_TO_NV 0x02 +#define GL_RELATIVE_MOVE_TO_NV 0x03 +#define GL_LINE_TO_NV 0x04 +#define GL_RELATIVE_LINE_TO_NV 0x05 +#define GL_HORIZONTAL_LINE_TO_NV 0x06 +#define GL_RELATIVE_HORIZONTAL_LINE_TO_NV 0x07 +#define GL_VERTICAL_LINE_TO_NV 0x08 +#define GL_RELATIVE_VERTICAL_LINE_TO_NV 0x09 +#define GL_QUADRATIC_CURVE_TO_NV 0x0A +#define GL_RELATIVE_QUADRATIC_CURVE_TO_NV 0x0B +#define GL_CUBIC_CURVE_TO_NV 0x0C +#define GL_RELATIVE_CUBIC_CURVE_TO_NV 0x0D +#define GL_SMOOTH_QUADRATIC_CURVE_TO_NV 0x0E +#define GL_RELATIVE_SMOOTH_QUADRATIC_CURVE_TO_NV 0x0F +#define GL_SMOOTH_CUBIC_CURVE_TO_NV 0x10 +#define GL_RELATIVE_SMOOTH_CUBIC_CURVE_TO_NV 0x11 +#define GL_SMALL_CCW_ARC_TO_NV 0x12 +#define GL_RELATIVE_SMALL_CCW_ARC_TO_NV 0x13 +#define GL_SMALL_CW_ARC_TO_NV 0x14 +#define GL_RELATIVE_SMALL_CW_ARC_TO_NV 0x15 +#define GL_LARGE_CCW_ARC_TO_NV 0x16 +#define GL_RELATIVE_LARGE_CCW_ARC_TO_NV 0x17 +#define GL_LARGE_CW_ARC_TO_NV 0x18 +#define GL_RELATIVE_LARGE_CW_ARC_TO_NV 0x19 +#define GL_RESTART_PATH_NV 0xF0 +#define GL_DUP_FIRST_CUBIC_CURVE_TO_NV 0xF2 +#define GL_DUP_LAST_CUBIC_CURVE_TO_NV 0xF4 +#define GL_RECT_NV 0xF6 +#define GL_CIRCULAR_CCW_ARC_TO_NV 0xF8 +#define GL_CIRCULAR_CW_ARC_TO_NV 0xFA +#define GL_CIRCULAR_TANGENT_ARC_TO_NV 0xFC +#define GL_ARC_TO_NV 0xFE +#define GL_RELATIVE_ARC_TO_NV 0xFF +#define GL_BOLD_BIT_NV 0x01 +#define GL_ITALIC_BIT_NV 0x02 +#define GL_GLYPH_WIDTH_BIT_NV 0x01 +#define GL_GLYPH_HEIGHT_BIT_NV 0x02 +#define GL_GLYPH_HORIZONTAL_BEARING_X_BIT_NV 0x04 +#define GL_GLYPH_HORIZONTAL_BEARING_Y_BIT_NV 0x08 +#define GL_GLYPH_HORIZONTAL_BEARING_ADVANCE_BIT_NV 0x10 +#define GL_GLYPH_VERTICAL_BEARING_X_BIT_NV 0x20 +#define GL_GLYPH_VERTICAL_BEARING_Y_BIT_NV 0x40 +#define GL_GLYPH_VERTICAL_BEARING_ADVANCE_BIT_NV 0x80 +#define GL_GLYPH_HAS_KERNING_BIT_NV 0x100 +#define GL_FONT_X_MIN_BOUNDS_BIT_NV 0x00010000 +#define GL_FONT_Y_MIN_BOUNDS_BIT_NV 0x00020000 +#define GL_FONT_X_MAX_BOUNDS_BIT_NV 0x00040000 +#define GL_FONT_Y_MAX_BOUNDS_BIT_NV 0x00080000 +#define GL_FONT_UNITS_PER_EM_BIT_NV 0x00100000 +#define GL_FONT_ASCENDER_BIT_NV 0x00200000 +#define GL_FONT_DESCENDER_BIT_NV 0x00400000 +#define GL_FONT_HEIGHT_BIT_NV 0x00800000 +#define GL_FONT_MAX_ADVANCE_WIDTH_BIT_NV 0x01000000 +#define GL_FONT_MAX_ADVANCE_HEIGHT_BIT_NV 0x02000000 +#define GL_FONT_UNDERLINE_POSITION_BIT_NV 0x04000000 +#define GL_FONT_UNDERLINE_THICKNESS_BIT_NV 0x08000000 +#define GL_FONT_HAS_KERNING_BIT_NV 0x10000000 +#define GL_PRIMARY_COLOR_NV 0x852C +#define GL_SECONDARY_COLOR_NV 0x852D +typedef GLuint (APIENTRYP PFNGLGENPATHSNVPROC) (GLsizei range); +typedef void (APIENTRYP PFNGLDELETEPATHSNVPROC) (GLuint path, GLsizei range); +typedef GLboolean (APIENTRYP PFNGLISPATHNVPROC) (GLuint path); +typedef void (APIENTRYP PFNGLPATHCOMMANDSNVPROC) (GLuint path, GLsizei numCommands, const GLubyte *commands, GLsizei numCoords, GLenum coordType, const void *coords); +typedef void (APIENTRYP PFNGLPATHCOORDSNVPROC) (GLuint path, GLsizei numCoords, GLenum coordType, const void *coords); +typedef void (APIENTRYP PFNGLPATHSUBCOMMANDSNVPROC) (GLuint path, GLsizei commandStart, GLsizei commandsToDelete, GLsizei numCommands, const GLubyte *commands, GLsizei numCoords, GLenum coordType, const void *coords); +typedef void (APIENTRYP PFNGLPATHSUBCOORDSNVPROC) (GLuint path, GLsizei coordStart, GLsizei numCoords, GLenum coordType, const void *coords); +typedef void (APIENTRYP PFNGLPATHSTRINGNVPROC) (GLuint path, GLenum format, GLsizei length, const void *pathString); +typedef void (APIENTRYP PFNGLPATHGLYPHSNVPROC) (GLuint firstPathName, GLenum fontTarget, const void *fontName, GLbitfield fontStyle, GLsizei numGlyphs, GLenum type, const void *charcodes, GLenum handleMissingGlyphs, GLuint pathParameterTemplate, GLfloat emScale); +typedef void (APIENTRYP PFNGLPATHGLYPHRANGENVPROC) (GLuint firstPathName, GLenum fontTarget, const void *fontName, GLbitfield fontStyle, GLuint firstGlyph, GLsizei numGlyphs, GLenum handleMissingGlyphs, GLuint pathParameterTemplate, GLfloat emScale); +typedef void (APIENTRYP PFNGLWEIGHTPATHSNVPROC) (GLuint resultPath, GLsizei numPaths, const GLuint *paths, const GLfloat *weights); +typedef void (APIENTRYP PFNGLCOPYPATHNVPROC) (GLuint resultPath, GLuint srcPath); +typedef void (APIENTRYP PFNGLINTERPOLATEPATHSNVPROC) (GLuint resultPath, GLuint pathA, GLuint pathB, GLfloat weight); +typedef void (APIENTRYP PFNGLTRANSFORMPATHNVPROC) (GLuint resultPath, GLuint srcPath, GLenum transformType, const GLfloat *transformValues); +typedef void (APIENTRYP PFNGLPATHPARAMETERIVNVPROC) (GLuint path, GLenum pname, const GLint *value); +typedef void (APIENTRYP PFNGLPATHPARAMETERINVPROC) (GLuint path, GLenum pname, GLint value); +typedef void (APIENTRYP PFNGLPATHPARAMETERFVNVPROC) (GLuint path, GLenum pname, const GLfloat *value); +typedef void (APIENTRYP PFNGLPATHPARAMETERFNVPROC) (GLuint path, GLenum pname, GLfloat value); +typedef void (APIENTRYP PFNGLPATHDASHARRAYNVPROC) (GLuint path, GLsizei dashCount, const GLfloat *dashArray); +typedef void (APIENTRYP PFNGLPATHSTENCILFUNCNVPROC) (GLenum func, GLint ref, GLuint mask); +typedef void (APIENTRYP PFNGLPATHSTENCILDEPTHOFFSETNVPROC) (GLfloat factor, GLfloat units); +typedef void (APIENTRYP PFNGLSTENCILFILLPATHNVPROC) (GLuint path, GLenum fillMode, GLuint mask); +typedef void (APIENTRYP PFNGLSTENCILSTROKEPATHNVPROC) (GLuint path, GLint reference, GLuint mask); +typedef void (APIENTRYP PFNGLSTENCILFILLPATHINSTANCEDNVPROC) (GLsizei numPaths, GLenum pathNameType, const void *paths, GLuint pathBase, GLenum fillMode, GLuint mask, GLenum transformType, const GLfloat *transformValues); +typedef void (APIENTRYP PFNGLSTENCILSTROKEPATHINSTANCEDNVPROC) (GLsizei numPaths, GLenum pathNameType, const void *paths, GLuint pathBase, GLint reference, GLuint mask, GLenum transformType, const GLfloat *transformValues); +typedef void (APIENTRYP PFNGLPATHCOVERDEPTHFUNCNVPROC) (GLenum func); +typedef void (APIENTRYP PFNGLPATHCOLORGENNVPROC) (GLenum color, GLenum genMode, GLenum colorFormat, const GLfloat *coeffs); +typedef void (APIENTRYP PFNGLPATHTEXGENNVPROC) (GLenum texCoordSet, GLenum genMode, GLint components, const GLfloat *coeffs); +typedef void (APIENTRYP PFNGLPATHFOGGENNVPROC) (GLenum genMode); +typedef void (APIENTRYP PFNGLCOVERFILLPATHNVPROC) (GLuint path, GLenum coverMode); +typedef void (APIENTRYP PFNGLCOVERSTROKEPATHNVPROC) (GLuint path, GLenum coverMode); +typedef void (APIENTRYP PFNGLCOVERFILLPATHINSTANCEDNVPROC) (GLsizei numPaths, GLenum pathNameType, const void *paths, GLuint pathBase, GLenum coverMode, GLenum transformType, const GLfloat *transformValues); +typedef void (APIENTRYP PFNGLCOVERSTROKEPATHINSTANCEDNVPROC) (GLsizei numPaths, GLenum pathNameType, const void *paths, GLuint pathBase, GLenum coverMode, GLenum transformType, const GLfloat *transformValues); +typedef void (APIENTRYP PFNGLGETPATHPARAMETERIVNVPROC) (GLuint path, GLenum pname, GLint *value); +typedef void (APIENTRYP PFNGLGETPATHPARAMETERFVNVPROC) (GLuint path, GLenum pname, GLfloat *value); +typedef void (APIENTRYP PFNGLGETPATHCOMMANDSNVPROC) (GLuint path, GLubyte *commands); +typedef void (APIENTRYP PFNGLGETPATHCOORDSNVPROC) (GLuint path, GLfloat *coords); +typedef void (APIENTRYP PFNGLGETPATHDASHARRAYNVPROC) (GLuint path, GLfloat *dashArray); +typedef void (APIENTRYP PFNGLGETPATHMETRICSNVPROC) (GLbitfield metricQueryMask, GLsizei numPaths, GLenum pathNameType, const void *paths, GLuint pathBase, GLsizei stride, GLfloat *metrics); +typedef void (APIENTRYP PFNGLGETPATHMETRICRANGENVPROC) (GLbitfield metricQueryMask, GLuint firstPathName, GLsizei numPaths, GLsizei stride, GLfloat *metrics); +typedef void (APIENTRYP PFNGLGETPATHSPACINGNVPROC) (GLenum pathListMode, GLsizei numPaths, GLenum pathNameType, const void *paths, GLuint pathBase, GLfloat advanceScale, GLfloat kerningScale, GLenum transformType, GLfloat *returnedSpacing); +typedef void (APIENTRYP PFNGLGETPATHCOLORGENIVNVPROC) (GLenum color, GLenum pname, GLint *value); +typedef void (APIENTRYP PFNGLGETPATHCOLORGENFVNVPROC) (GLenum color, GLenum pname, GLfloat *value); +typedef void (APIENTRYP PFNGLGETPATHTEXGENIVNVPROC) (GLenum texCoordSet, GLenum pname, GLint *value); +typedef void (APIENTRYP PFNGLGETPATHTEXGENFVNVPROC) (GLenum texCoordSet, GLenum pname, GLfloat *value); +typedef GLboolean (APIENTRYP PFNGLISPOINTINFILLPATHNVPROC) (GLuint path, GLuint mask, GLfloat x, GLfloat y); +typedef GLboolean (APIENTRYP PFNGLISPOINTINSTROKEPATHNVPROC) (GLuint path, GLfloat x, GLfloat y); +typedef GLfloat (APIENTRYP PFNGLGETPATHLENGTHNVPROC) (GLuint path, GLsizei startSegment, GLsizei numSegments); +typedef GLboolean (APIENTRYP PFNGLPOINTALONGPATHNVPROC) (GLuint path, GLsizei startSegment, GLsizei numSegments, GLfloat distance, GLfloat *x, GLfloat *y, GLfloat *tangentX, GLfloat *tangentY); +#ifdef GL_GLEXT_PROTOTYPES +GLAPI GLuint APIENTRY glGenPathsNV (GLsizei range); +GLAPI void APIENTRY glDeletePathsNV (GLuint path, GLsizei range); +GLAPI GLboolean APIENTRY glIsPathNV (GLuint path); +GLAPI void APIENTRY glPathCommandsNV (GLuint path, GLsizei numCommands, const GLubyte *commands, GLsizei numCoords, GLenum coordType, const void *coords); +GLAPI void APIENTRY glPathCoordsNV (GLuint path, GLsizei numCoords, GLenum coordType, const void *coords); +GLAPI void APIENTRY glPathSubCommandsNV (GLuint path, GLsizei commandStart, GLsizei commandsToDelete, GLsizei numCommands, const GLubyte *commands, GLsizei numCoords, GLenum coordType, const void *coords); +GLAPI void APIENTRY glPathSubCoordsNV (GLuint path, GLsizei coordStart, GLsizei numCoords, GLenum coordType, const void *coords); +GLAPI void APIENTRY glPathStringNV (GLuint path, GLenum format, GLsizei length, const void *pathString); +GLAPI void APIENTRY glPathGlyphsNV (GLuint firstPathName, GLenum fontTarget, const void *fontName, GLbitfield fontStyle, GLsizei numGlyphs, GLenum type, const void *charcodes, GLenum handleMissingGlyphs, GLuint pathParameterTemplate, GLfloat emScale); +GLAPI void APIENTRY glPathGlyphRangeNV (GLuint firstPathName, GLenum fontTarget, const void *fontName, GLbitfield fontStyle, GLuint firstGlyph, GLsizei numGlyphs, GLenum handleMissingGlyphs, GLuint pathParameterTemplate, GLfloat emScale); +GLAPI void APIENTRY glWeightPathsNV (GLuint resultPath, GLsizei numPaths, const GLuint *paths, const GLfloat *weights); +GLAPI void APIENTRY glCopyPathNV (GLuint resultPath, GLuint srcPath); +GLAPI void APIENTRY glInterpolatePathsNV (GLuint resultPath, GLuint pathA, GLuint pathB, GLfloat weight); +GLAPI void APIENTRY glTransformPathNV (GLuint resultPath, GLuint srcPath, GLenum transformType, const GLfloat *transformValues); +GLAPI void APIENTRY glPathParameterivNV (GLuint path, GLenum pname, const GLint *value); +GLAPI void APIENTRY glPathParameteriNV (GLuint path, GLenum pname, GLint value); +GLAPI void APIENTRY glPathParameterfvNV (GLuint path, GLenum pname, const GLfloat *value); +GLAPI void APIENTRY glPathParameterfNV (GLuint path, GLenum pname, GLfloat value); +GLAPI void APIENTRY glPathDashArrayNV (GLuint path, GLsizei dashCount, const GLfloat *dashArray); +GLAPI void APIENTRY glPathStencilFuncNV (GLenum func, GLint ref, GLuint mask); +GLAPI void APIENTRY glPathStencilDepthOffsetNV (GLfloat factor, GLfloat units); +GLAPI void APIENTRY glStencilFillPathNV (GLuint path, GLenum fillMode, GLuint mask); +GLAPI void APIENTRY glStencilStrokePathNV (GLuint path, GLint reference, GLuint mask); +GLAPI void APIENTRY glStencilFillPathInstancedNV (GLsizei numPaths, GLenum pathNameType, const void *paths, GLuint pathBase, GLenum fillMode, GLuint mask, GLenum transformType, const GLfloat *transformValues); +GLAPI void APIENTRY glStencilStrokePathInstancedNV (GLsizei numPaths, GLenum pathNameType, const void *paths, GLuint pathBase, GLint reference, GLuint mask, GLenum transformType, const GLfloat *transformValues); +GLAPI void APIENTRY glPathCoverDepthFuncNV (GLenum func); +GLAPI void APIENTRY glPathColorGenNV (GLenum color, GLenum genMode, GLenum colorFormat, const GLfloat *coeffs); +GLAPI void APIENTRY glPathTexGenNV (GLenum texCoordSet, GLenum genMode, GLint components, const GLfloat *coeffs); +GLAPI void APIENTRY glPathFogGenNV (GLenum genMode); +GLAPI void APIENTRY glCoverFillPathNV (GLuint path, GLenum coverMode); +GLAPI void APIENTRY glCoverStrokePathNV (GLuint path, GLenum coverMode); +GLAPI void APIENTRY glCoverFillPathInstancedNV (GLsizei numPaths, GLenum pathNameType, const void *paths, GLuint pathBase, GLenum coverMode, GLenum transformType, const GLfloat *transformValues); +GLAPI void APIENTRY glCoverStrokePathInstancedNV (GLsizei numPaths, GLenum pathNameType, const void *paths, GLuint pathBase, GLenum coverMode, GLenum transformType, const GLfloat *transformValues); +GLAPI void APIENTRY glGetPathParameterivNV (GLuint path, GLenum pname, GLint *value); +GLAPI void APIENTRY glGetPathParameterfvNV (GLuint path, GLenum pname, GLfloat *value); +GLAPI void APIENTRY glGetPathCommandsNV (GLuint path, GLubyte *commands); +GLAPI void APIENTRY glGetPathCoordsNV (GLuint path, GLfloat *coords); +GLAPI void APIENTRY glGetPathDashArrayNV (GLuint path, GLfloat *dashArray); +GLAPI void APIENTRY glGetPathMetricsNV (GLbitfield metricQueryMask, GLsizei numPaths, GLenum pathNameType, const void *paths, GLuint pathBase, GLsizei stride, GLfloat *metrics); +GLAPI void APIENTRY glGetPathMetricRangeNV (GLbitfield metricQueryMask, GLuint firstPathName, GLsizei numPaths, GLsizei stride, GLfloat *metrics); +GLAPI void APIENTRY glGetPathSpacingNV (GLenum pathListMode, GLsizei numPaths, GLenum pathNameType, const void *paths, GLuint pathBase, GLfloat advanceScale, GLfloat kerningScale, GLenum transformType, GLfloat *returnedSpacing); +GLAPI void APIENTRY glGetPathColorGenivNV (GLenum color, GLenum pname, GLint *value); +GLAPI void APIENTRY glGetPathColorGenfvNV (GLenum color, GLenum pname, GLfloat *value); +GLAPI void APIENTRY glGetPathTexGenivNV (GLenum texCoordSet, GLenum pname, GLint *value); +GLAPI void APIENTRY glGetPathTexGenfvNV (GLenum texCoordSet, GLenum pname, GLfloat *value); +GLAPI GLboolean APIENTRY glIsPointInFillPathNV (GLuint path, GLuint mask, GLfloat x, GLfloat y); +GLAPI GLboolean APIENTRY glIsPointInStrokePathNV (GLuint path, GLfloat x, GLfloat y); +GLAPI GLfloat APIENTRY glGetPathLengthNV (GLuint path, GLsizei startSegment, GLsizei numSegments); +GLAPI GLboolean APIENTRY glPointAlongPathNV (GLuint path, GLsizei startSegment, GLsizei numSegments, GLfloat distance, GLfloat *x, GLfloat *y, GLfloat *tangentX, GLfloat *tangentY); +#endif +#endif /* GL_NV_path_rendering */ + +#ifndef GL_NV_pixel_data_range +#define GL_NV_pixel_data_range 1 +#define GL_WRITE_PIXEL_DATA_RANGE_NV 0x8878 +#define GL_READ_PIXEL_DATA_RANGE_NV 0x8879 +#define GL_WRITE_PIXEL_DATA_RANGE_LENGTH_NV 0x887A +#define GL_READ_PIXEL_DATA_RANGE_LENGTH_NV 0x887B +#define GL_WRITE_PIXEL_DATA_RANGE_POINTER_NV 0x887C +#define GL_READ_PIXEL_DATA_RANGE_POINTER_NV 0x887D +typedef void (APIENTRYP PFNGLPIXELDATARANGENVPROC) (GLenum target, GLsizei length, const void *pointer); +typedef void (APIENTRYP PFNGLFLUSHPIXELDATARANGENVPROC) (GLenum target); +#ifdef GL_GLEXT_PROTOTYPES +GLAPI void APIENTRY glPixelDataRangeNV (GLenum target, GLsizei length, const void *pointer); +GLAPI void APIENTRY glFlushPixelDataRangeNV (GLenum target); +#endif +#endif /* GL_NV_pixel_data_range */ + +#ifndef GL_NV_point_sprite +#define GL_NV_point_sprite 1 +#define GL_POINT_SPRITE_NV 0x8861 +#define GL_COORD_REPLACE_NV 0x8862 +#define GL_POINT_SPRITE_R_MODE_NV 0x8863 +typedef void (APIENTRYP PFNGLPOINTPARAMETERINVPROC) (GLenum pname, GLint param); +typedef void (APIENTRYP PFNGLPOINTPARAMETERIVNVPROC) (GLenum pname, const GLint *params); +#ifdef GL_GLEXT_PROTOTYPES +GLAPI void APIENTRY glPointParameteriNV (GLenum pname, GLint param); +GLAPI void APIENTRY glPointParameterivNV (GLenum pname, const GLint *params); +#endif +#endif /* GL_NV_point_sprite */ + +#ifndef GL_NV_present_video +#define GL_NV_present_video 1 +#define GL_FRAME_NV 0x8E26 +#define GL_FIELDS_NV 0x8E27 +#define GL_CURRENT_TIME_NV 0x8E28 +#define GL_NUM_FILL_STREAMS_NV 0x8E29 +#define GL_PRESENT_TIME_NV 0x8E2A +#define GL_PRESENT_DURATION_NV 0x8E2B +typedef void (APIENTRYP PFNGLPRESENTFRAMEKEYEDNVPROC) (GLuint video_slot, GLuint64EXT minPresentTime, GLuint beginPresentTimeId, GLuint presentDurationId, GLenum type, GLenum target0, GLuint fill0, GLuint key0, GLenum target1, GLuint fill1, GLuint key1); +typedef void (APIENTRYP PFNGLPRESENTFRAMEDUALFILLNVPROC) (GLuint video_slot, GLuint64EXT minPresentTime, GLuint beginPresentTimeId, GLuint presentDurationId, GLenum type, GLenum target0, GLuint fill0, GLenum target1, GLuint fill1, GLenum target2, GLuint fill2, GLenum target3, GLuint fill3); +typedef void (APIENTRYP PFNGLGETVIDEOIVNVPROC) (GLuint video_slot, GLenum pname, GLint *params); +typedef void (APIENTRYP PFNGLGETVIDEOUIVNVPROC) (GLuint video_slot, GLenum pname, GLuint *params); +typedef void (APIENTRYP PFNGLGETVIDEOI64VNVPROC) (GLuint video_slot, GLenum pname, GLint64EXT *params); +typedef void (APIENTRYP PFNGLGETVIDEOUI64VNVPROC) (GLuint video_slot, GLenum pname, GLuint64EXT *params); +#ifdef GL_GLEXT_PROTOTYPES +GLAPI void APIENTRY glPresentFrameKeyedNV (GLuint video_slot, GLuint64EXT minPresentTime, GLuint beginPresentTimeId, GLuint presentDurationId, GLenum type, GLenum target0, GLuint fill0, GLuint key0, GLenum target1, GLuint fill1, GLuint key1); +GLAPI void APIENTRY glPresentFrameDualFillNV (GLuint video_slot, GLuint64EXT minPresentTime, GLuint beginPresentTimeId, GLuint presentDurationId, GLenum type, GLenum target0, GLuint fill0, GLenum target1, GLuint fill1, GLenum target2, GLuint fill2, GLenum target3, GLuint fill3); +GLAPI void APIENTRY glGetVideoivNV (GLuint video_slot, GLenum pname, GLint *params); +GLAPI void APIENTRY glGetVideouivNV (GLuint video_slot, GLenum pname, GLuint *params); +GLAPI void APIENTRY glGetVideoi64vNV (GLuint video_slot, GLenum pname, GLint64EXT *params); +GLAPI void APIENTRY glGetVideoui64vNV (GLuint video_slot, GLenum pname, GLuint64EXT *params); +#endif +#endif /* GL_NV_present_video */ + +#ifndef GL_NV_primitive_restart +#define GL_NV_primitive_restart 1 +#define GL_PRIMITIVE_RESTART_NV 0x8558 +#define GL_PRIMITIVE_RESTART_INDEX_NV 0x8559 +typedef void (APIENTRYP PFNGLPRIMITIVERESTARTNVPROC) (void); +typedef void (APIENTRYP PFNGLPRIMITIVERESTARTINDEXNVPROC) (GLuint index); +#ifdef GL_GLEXT_PROTOTYPES +GLAPI void APIENTRY glPrimitiveRestartNV (void); +GLAPI void APIENTRY glPrimitiveRestartIndexNV (GLuint index); +#endif +#endif /* GL_NV_primitive_restart */ + +#ifndef GL_NV_register_combiners +#define GL_NV_register_combiners 1 +#define GL_REGISTER_COMBINERS_NV 0x8522 +#define GL_VARIABLE_A_NV 0x8523 +#define GL_VARIABLE_B_NV 0x8524 +#define GL_VARIABLE_C_NV 0x8525 +#define GL_VARIABLE_D_NV 0x8526 +#define GL_VARIABLE_E_NV 0x8527 +#define GL_VARIABLE_F_NV 0x8528 +#define GL_VARIABLE_G_NV 0x8529 +#define GL_CONSTANT_COLOR0_NV 0x852A +#define GL_CONSTANT_COLOR1_NV 0x852B +#define GL_SPARE0_NV 0x852E +#define GL_SPARE1_NV 0x852F +#define GL_DISCARD_NV 0x8530 +#define GL_E_TIMES_F_NV 0x8531 +#define GL_SPARE0_PLUS_SECONDARY_COLOR_NV 0x8532 +#define GL_UNSIGNED_IDENTITY_NV 0x8536 +#define GL_UNSIGNED_INVERT_NV 0x8537 +#define GL_EXPAND_NORMAL_NV 0x8538 +#define GL_EXPAND_NEGATE_NV 0x8539 +#define GL_HALF_BIAS_NORMAL_NV 0x853A +#define GL_HALF_BIAS_NEGATE_NV 0x853B +#define GL_SIGNED_IDENTITY_NV 0x853C +#define GL_SIGNED_NEGATE_NV 0x853D +#define GL_SCALE_BY_TWO_NV 0x853E +#define GL_SCALE_BY_FOUR_NV 0x853F +#define GL_SCALE_BY_ONE_HALF_NV 0x8540 +#define GL_BIAS_BY_NEGATIVE_ONE_HALF_NV 0x8541 +#define GL_COMBINER_INPUT_NV 0x8542 +#define GL_COMBINER_MAPPING_NV 0x8543 +#define GL_COMBINER_COMPONENT_USAGE_NV 0x8544 +#define GL_COMBINER_AB_DOT_PRODUCT_NV 0x8545 +#define GL_COMBINER_CD_DOT_PRODUCT_NV 0x8546 +#define GL_COMBINER_MUX_SUM_NV 0x8547 +#define GL_COMBINER_SCALE_NV 0x8548 +#define GL_COMBINER_BIAS_NV 0x8549 +#define GL_COMBINER_AB_OUTPUT_NV 0x854A +#define GL_COMBINER_CD_OUTPUT_NV 0x854B +#define GL_COMBINER_SUM_OUTPUT_NV 0x854C +#define GL_MAX_GENERAL_COMBINERS_NV 0x854D +#define GL_NUM_GENERAL_COMBINERS_NV 0x854E +#define GL_COLOR_SUM_CLAMP_NV 0x854F +#define GL_COMBINER0_NV 0x8550 +#define GL_COMBINER1_NV 0x8551 +#define GL_COMBINER2_NV 0x8552 +#define GL_COMBINER3_NV 0x8553 +#define GL_COMBINER4_NV 0x8554 +#define GL_COMBINER5_NV 0x8555 +#define GL_COMBINER6_NV 0x8556 +#define GL_COMBINER7_NV 0x8557 +typedef void (APIENTRYP PFNGLCOMBINERPARAMETERFVNVPROC) (GLenum pname, const GLfloat *params); +typedef void (APIENTRYP PFNGLCOMBINERPARAMETERFNVPROC) (GLenum pname, GLfloat param); +typedef void (APIENTRYP PFNGLCOMBINERPARAMETERIVNVPROC) (GLenum pname, const GLint *params); +typedef void (APIENTRYP PFNGLCOMBINERPARAMETERINVPROC) (GLenum pname, GLint param); +typedef void (APIENTRYP PFNGLCOMBINERINPUTNVPROC) (GLenum stage, GLenum portion, GLenum variable, GLenum input, GLenum mapping, GLenum componentUsage); +typedef void (APIENTRYP PFNGLCOMBINEROUTPUTNVPROC) (GLenum stage, GLenum portion, GLenum abOutput, GLenum cdOutput, GLenum sumOutput, GLenum scale, GLenum bias, GLboolean abDotProduct, GLboolean cdDotProduct, GLboolean muxSum); +typedef void (APIENTRYP PFNGLFINALCOMBINERINPUTNVPROC) (GLenum variable, GLenum input, GLenum mapping, GLenum componentUsage); +typedef void (APIENTRYP PFNGLGETCOMBINERINPUTPARAMETERFVNVPROC) (GLenum stage, GLenum portion, GLenum variable, GLenum pname, GLfloat *params); +typedef void (APIENTRYP PFNGLGETCOMBINERINPUTPARAMETERIVNVPROC) (GLenum stage, GLenum portion, GLenum variable, GLenum pname, GLint *params); +typedef void (APIENTRYP PFNGLGETCOMBINEROUTPUTPARAMETERFVNVPROC) (GLenum stage, GLenum portion, GLenum pname, GLfloat *params); +typedef void (APIENTRYP PFNGLGETCOMBINEROUTPUTPARAMETERIVNVPROC) (GLenum stage, GLenum portion, GLenum pname, GLint *params); +typedef void (APIENTRYP PFNGLGETFINALCOMBINERINPUTPARAMETERFVNVPROC) (GLenum variable, GLenum pname, GLfloat *params); +typedef void (APIENTRYP PFNGLGETFINALCOMBINERINPUTPARAMETERIVNVPROC) (GLenum variable, GLenum pname, GLint *params); +#ifdef GL_GLEXT_PROTOTYPES +GLAPI void APIENTRY glCombinerParameterfvNV (GLenum pname, const GLfloat *params); +GLAPI void APIENTRY glCombinerParameterfNV (GLenum pname, GLfloat param); +GLAPI void APIENTRY glCombinerParameterivNV (GLenum pname, const GLint *params); +GLAPI void APIENTRY glCombinerParameteriNV (GLenum pname, GLint param); +GLAPI void APIENTRY glCombinerInputNV (GLenum stage, GLenum portion, GLenum variable, GLenum input, GLenum mapping, GLenum componentUsage); +GLAPI void APIENTRY glCombinerOutputNV (GLenum stage, GLenum portion, GLenum abOutput, GLenum cdOutput, GLenum sumOutput, GLenum scale, GLenum bias, GLboolean abDotProduct, GLboolean cdDotProduct, GLboolean muxSum); +GLAPI void APIENTRY glFinalCombinerInputNV (GLenum variable, GLenum input, GLenum mapping, GLenum componentUsage); +GLAPI void APIENTRY glGetCombinerInputParameterfvNV (GLenum stage, GLenum portion, GLenum variable, GLenum pname, GLfloat *params); +GLAPI void APIENTRY glGetCombinerInputParameterivNV (GLenum stage, GLenum portion, GLenum variable, GLenum pname, GLint *params); +GLAPI void APIENTRY glGetCombinerOutputParameterfvNV (GLenum stage, GLenum portion, GLenum pname, GLfloat *params); +GLAPI void APIENTRY glGetCombinerOutputParameterivNV (GLenum stage, GLenum portion, GLenum pname, GLint *params); +GLAPI void APIENTRY glGetFinalCombinerInputParameterfvNV (GLenum variable, GLenum pname, GLfloat *params); +GLAPI void APIENTRY glGetFinalCombinerInputParameterivNV (GLenum variable, GLenum pname, GLint *params); +#endif +#endif /* GL_NV_register_combiners */ + +#ifndef GL_NV_register_combiners2 +#define GL_NV_register_combiners2 1 +#define GL_PER_STAGE_CONSTANTS_NV 0x8535 +typedef void (APIENTRYP PFNGLCOMBINERSTAGEPARAMETERFVNVPROC) (GLenum stage, GLenum pname, const GLfloat *params); +typedef void (APIENTRYP PFNGLGETCOMBINERSTAGEPARAMETERFVNVPROC) (GLenum stage, GLenum pname, GLfloat *params); +#ifdef GL_GLEXT_PROTOTYPES +GLAPI void APIENTRY glCombinerStageParameterfvNV (GLenum stage, GLenum pname, const GLfloat *params); +GLAPI void APIENTRY glGetCombinerStageParameterfvNV (GLenum stage, GLenum pname, GLfloat *params); +#endif +#endif /* GL_NV_register_combiners2 */ + +#ifndef GL_NV_shader_atomic_counters +#define GL_NV_shader_atomic_counters 1 +#endif /* GL_NV_shader_atomic_counters */ + +#ifndef GL_NV_shader_atomic_float +#define GL_NV_shader_atomic_float 1 +#endif /* GL_NV_shader_atomic_float */ + +#ifndef GL_NV_shader_buffer_load +#define GL_NV_shader_buffer_load 1 +#define GL_BUFFER_GPU_ADDRESS_NV 0x8F1D +#define GL_GPU_ADDRESS_NV 0x8F34 +#define GL_MAX_SHADER_BUFFER_ADDRESS_NV 0x8F35 +typedef void (APIENTRYP PFNGLMAKEBUFFERRESIDENTNVPROC) (GLenum target, GLenum access); +typedef void (APIENTRYP PFNGLMAKEBUFFERNONRESIDENTNVPROC) (GLenum target); +typedef GLboolean (APIENTRYP PFNGLISBUFFERRESIDENTNVPROC) (GLenum target); +typedef void (APIENTRYP PFNGLMAKENAMEDBUFFERRESIDENTNVPROC) (GLuint buffer, GLenum access); +typedef void (APIENTRYP PFNGLMAKENAMEDBUFFERNONRESIDENTNVPROC) (GLuint buffer); +typedef GLboolean (APIENTRYP PFNGLISNAMEDBUFFERRESIDENTNVPROC) (GLuint buffer); +typedef void (APIENTRYP PFNGLGETBUFFERPARAMETERUI64VNVPROC) (GLenum target, GLenum pname, GLuint64EXT *params); +typedef void (APIENTRYP PFNGLGETNAMEDBUFFERPARAMETERUI64VNVPROC) (GLuint buffer, GLenum pname, GLuint64EXT *params); +typedef void (APIENTRYP PFNGLGETINTEGERUI64VNVPROC) (GLenum value, GLuint64EXT *result); +typedef void (APIENTRYP PFNGLUNIFORMUI64NVPROC) (GLint location, GLuint64EXT value); +typedef void (APIENTRYP PFNGLUNIFORMUI64VNVPROC) (GLint location, GLsizei count, const GLuint64EXT *value); +typedef void (APIENTRYP PFNGLGETUNIFORMUI64VNVPROC) (GLuint program, GLint location, GLuint64EXT *params); +typedef void (APIENTRYP PFNGLPROGRAMUNIFORMUI64NVPROC) (GLuint program, GLint location, GLuint64EXT value); +typedef void (APIENTRYP PFNGLPROGRAMUNIFORMUI64VNVPROC) (GLuint program, GLint location, GLsizei count, const GLuint64EXT *value); +#ifdef GL_GLEXT_PROTOTYPES +GLAPI void APIENTRY glMakeBufferResidentNV (GLenum target, GLenum access); +GLAPI void APIENTRY glMakeBufferNonResidentNV (GLenum target); +GLAPI GLboolean APIENTRY glIsBufferResidentNV (GLenum target); +GLAPI void APIENTRY glMakeNamedBufferResidentNV (GLuint buffer, GLenum access); +GLAPI void APIENTRY glMakeNamedBufferNonResidentNV (GLuint buffer); +GLAPI GLboolean APIENTRY glIsNamedBufferResidentNV (GLuint buffer); +GLAPI void APIENTRY glGetBufferParameterui64vNV (GLenum target, GLenum pname, GLuint64EXT *params); +GLAPI void APIENTRY glGetNamedBufferParameterui64vNV (GLuint buffer, GLenum pname, GLuint64EXT *params); +GLAPI void APIENTRY glGetIntegerui64vNV (GLenum value, GLuint64EXT *result); +GLAPI void APIENTRY glUniformui64NV (GLint location, GLuint64EXT value); +GLAPI void APIENTRY glUniformui64vNV (GLint location, GLsizei count, const GLuint64EXT *value); +GLAPI void APIENTRY glGetUniformui64vNV (GLuint program, GLint location, GLuint64EXT *params); +GLAPI void APIENTRY glProgramUniformui64NV (GLuint program, GLint location, GLuint64EXT value); +GLAPI void APIENTRY glProgramUniformui64vNV (GLuint program, GLint location, GLsizei count, const GLuint64EXT *value); +#endif +#endif /* GL_NV_shader_buffer_load */ + +#ifndef GL_NV_shader_buffer_store +#define GL_NV_shader_buffer_store 1 +#define GL_SHADER_GLOBAL_ACCESS_BARRIER_BIT_NV 0x00000010 +#endif /* GL_NV_shader_buffer_store */ + +#ifndef GL_NV_shader_storage_buffer_object +#define GL_NV_shader_storage_buffer_object 1 +#endif /* GL_NV_shader_storage_buffer_object */ + +#ifndef GL_NV_tessellation_program5 +#define GL_NV_tessellation_program5 1 +#define GL_MAX_PROGRAM_PATCH_ATTRIBS_NV 0x86D8 +#define GL_TESS_CONTROL_PROGRAM_NV 0x891E +#define GL_TESS_EVALUATION_PROGRAM_NV 0x891F +#define GL_TESS_CONTROL_PROGRAM_PARAMETER_BUFFER_NV 0x8C74 +#define GL_TESS_EVALUATION_PROGRAM_PARAMETER_BUFFER_NV 0x8C75 +#endif /* GL_NV_tessellation_program5 */ + +#ifndef GL_NV_texgen_emboss +#define GL_NV_texgen_emboss 1 +#define GL_EMBOSS_LIGHT_NV 0x855D +#define GL_EMBOSS_CONSTANT_NV 0x855E +#define GL_EMBOSS_MAP_NV 0x855F +#endif /* GL_NV_texgen_emboss */ + +#ifndef GL_NV_texgen_reflection +#define GL_NV_texgen_reflection 1 +#define GL_NORMAL_MAP_NV 0x8511 +#define GL_REFLECTION_MAP_NV 0x8512 +#endif /* GL_NV_texgen_reflection */ + +#ifndef GL_NV_texture_barrier +#define GL_NV_texture_barrier 1 +typedef void (APIENTRYP PFNGLTEXTUREBARRIERNVPROC) (void); +#ifdef GL_GLEXT_PROTOTYPES +GLAPI void APIENTRY glTextureBarrierNV (void); +#endif +#endif /* GL_NV_texture_barrier */ + +#ifndef GL_NV_texture_compression_vtc +#define GL_NV_texture_compression_vtc 1 +#endif /* GL_NV_texture_compression_vtc */ + +#ifndef GL_NV_texture_env_combine4 +#define GL_NV_texture_env_combine4 1 +#define GL_COMBINE4_NV 0x8503 +#define GL_SOURCE3_RGB_NV 0x8583 +#define GL_SOURCE3_ALPHA_NV 0x858B +#define GL_OPERAND3_RGB_NV 0x8593 +#define GL_OPERAND3_ALPHA_NV 0x859B +#endif /* GL_NV_texture_env_combine4 */ + +#ifndef GL_NV_texture_expand_normal +#define GL_NV_texture_expand_normal 1 +#define GL_TEXTURE_UNSIGNED_REMAP_MODE_NV 0x888F +#endif /* GL_NV_texture_expand_normal */ + +#ifndef GL_NV_texture_multisample +#define GL_NV_texture_multisample 1 +#define GL_TEXTURE_COVERAGE_SAMPLES_NV 0x9045 +#define GL_TEXTURE_COLOR_SAMPLES_NV 0x9046 +typedef void (APIENTRYP PFNGLTEXIMAGE2DMULTISAMPLECOVERAGENVPROC) (GLenum target, GLsizei coverageSamples, GLsizei colorSamples, GLint internalFormat, GLsizei width, GLsizei height, GLboolean fixedSampleLocations); +typedef void (APIENTRYP PFNGLTEXIMAGE3DMULTISAMPLECOVERAGENVPROC) (GLenum target, GLsizei coverageSamples, GLsizei colorSamples, GLint internalFormat, GLsizei width, GLsizei height, GLsizei depth, GLboolean fixedSampleLocations); +typedef void (APIENTRYP PFNGLTEXTUREIMAGE2DMULTISAMPLENVPROC) (GLuint texture, GLenum target, GLsizei samples, GLint internalFormat, GLsizei width, GLsizei height, GLboolean fixedSampleLocations); +typedef void (APIENTRYP PFNGLTEXTUREIMAGE3DMULTISAMPLENVPROC) (GLuint texture, GLenum target, GLsizei samples, GLint internalFormat, GLsizei width, GLsizei height, GLsizei depth, GLboolean fixedSampleLocations); +typedef void (APIENTRYP PFNGLTEXTUREIMAGE2DMULTISAMPLECOVERAGENVPROC) (GLuint texture, GLenum target, GLsizei coverageSamples, GLsizei colorSamples, GLint internalFormat, GLsizei width, GLsizei height, GLboolean fixedSampleLocations); +typedef void (APIENTRYP PFNGLTEXTUREIMAGE3DMULTISAMPLECOVERAGENVPROC) (GLuint texture, GLenum target, GLsizei coverageSamples, GLsizei colorSamples, GLint internalFormat, GLsizei width, GLsizei height, GLsizei depth, GLboolean fixedSampleLocations); +#ifdef GL_GLEXT_PROTOTYPES +GLAPI void APIENTRY glTexImage2DMultisampleCoverageNV (GLenum target, GLsizei coverageSamples, GLsizei colorSamples, GLint internalFormat, GLsizei width, GLsizei height, GLboolean fixedSampleLocations); +GLAPI void APIENTRY glTexImage3DMultisampleCoverageNV (GLenum target, GLsizei coverageSamples, GLsizei colorSamples, GLint internalFormat, GLsizei width, GLsizei height, GLsizei depth, GLboolean fixedSampleLocations); +GLAPI void APIENTRY glTextureImage2DMultisampleNV (GLuint texture, GLenum target, GLsizei samples, GLint internalFormat, GLsizei width, GLsizei height, GLboolean fixedSampleLocations); +GLAPI void APIENTRY glTextureImage3DMultisampleNV (GLuint texture, GLenum target, GLsizei samples, GLint internalFormat, GLsizei width, GLsizei height, GLsizei depth, GLboolean fixedSampleLocations); +GLAPI void APIENTRY glTextureImage2DMultisampleCoverageNV (GLuint texture, GLenum target, GLsizei coverageSamples, GLsizei colorSamples, GLint internalFormat, GLsizei width, GLsizei height, GLboolean fixedSampleLocations); +GLAPI void APIENTRY glTextureImage3DMultisampleCoverageNV (GLuint texture, GLenum target, GLsizei coverageSamples, GLsizei colorSamples, GLint internalFormat, GLsizei width, GLsizei height, GLsizei depth, GLboolean fixedSampleLocations); +#endif +#endif /* GL_NV_texture_multisample */ + +#ifndef GL_NV_texture_rectangle +#define GL_NV_texture_rectangle 1 +#define GL_TEXTURE_RECTANGLE_NV 0x84F5 +#define GL_TEXTURE_BINDING_RECTANGLE_NV 0x84F6 +#define GL_PROXY_TEXTURE_RECTANGLE_NV 0x84F7 +#define GL_MAX_RECTANGLE_TEXTURE_SIZE_NV 0x84F8 +#endif /* GL_NV_texture_rectangle */ + +#ifndef GL_NV_texture_shader +#define GL_NV_texture_shader 1 +#define GL_OFFSET_TEXTURE_RECTANGLE_NV 0x864C +#define GL_OFFSET_TEXTURE_RECTANGLE_SCALE_NV 0x864D +#define GL_DOT_PRODUCT_TEXTURE_RECTANGLE_NV 0x864E +#define GL_RGBA_UNSIGNED_DOT_PRODUCT_MAPPING_NV 0x86D9 +#define GL_UNSIGNED_INT_S8_S8_8_8_NV 0x86DA +#define GL_UNSIGNED_INT_8_8_S8_S8_REV_NV 0x86DB +#define GL_DSDT_MAG_INTENSITY_NV 0x86DC +#define GL_SHADER_CONSISTENT_NV 0x86DD +#define GL_TEXTURE_SHADER_NV 0x86DE +#define GL_SHADER_OPERATION_NV 0x86DF +#define GL_CULL_MODES_NV 0x86E0 +#define GL_OFFSET_TEXTURE_MATRIX_NV 0x86E1 +#define GL_OFFSET_TEXTURE_SCALE_NV 0x86E2 +#define GL_OFFSET_TEXTURE_BIAS_NV 0x86E3 +#define GL_OFFSET_TEXTURE_2D_MATRIX_NV 0x86E1 +#define GL_OFFSET_TEXTURE_2D_SCALE_NV 0x86E2 +#define GL_OFFSET_TEXTURE_2D_BIAS_NV 0x86E3 +#define GL_PREVIOUS_TEXTURE_INPUT_NV 0x86E4 +#define GL_CONST_EYE_NV 0x86E5 +#define GL_PASS_THROUGH_NV 0x86E6 +#define GL_CULL_FRAGMENT_NV 0x86E7 +#define GL_OFFSET_TEXTURE_2D_NV 0x86E8 +#define GL_DEPENDENT_AR_TEXTURE_2D_NV 0x86E9 +#define GL_DEPENDENT_GB_TEXTURE_2D_NV 0x86EA +#define GL_DOT_PRODUCT_NV 0x86EC +#define GL_DOT_PRODUCT_DEPTH_REPLACE_NV 0x86ED +#define GL_DOT_PRODUCT_TEXTURE_2D_NV 0x86EE +#define GL_DOT_PRODUCT_TEXTURE_CUBE_MAP_NV 0x86F0 +#define GL_DOT_PRODUCT_DIFFUSE_CUBE_MAP_NV 0x86F1 +#define GL_DOT_PRODUCT_REFLECT_CUBE_MAP_NV 0x86F2 +#define GL_DOT_PRODUCT_CONST_EYE_REFLECT_CUBE_MAP_NV 0x86F3 +#define GL_HILO_NV 0x86F4 +#define GL_DSDT_NV 0x86F5 +#define GL_DSDT_MAG_NV 0x86F6 +#define GL_DSDT_MAG_VIB_NV 0x86F7 +#define GL_HILO16_NV 0x86F8 +#define GL_SIGNED_HILO_NV 0x86F9 +#define GL_SIGNED_HILO16_NV 0x86FA +#define GL_SIGNED_RGBA_NV 0x86FB +#define GL_SIGNED_RGBA8_NV 0x86FC +#define GL_SIGNED_RGB_NV 0x86FE +#define GL_SIGNED_RGB8_NV 0x86FF +#define GL_SIGNED_LUMINANCE_NV 0x8701 +#define GL_SIGNED_LUMINANCE8_NV 0x8702 +#define GL_SIGNED_LUMINANCE_ALPHA_NV 0x8703 +#define GL_SIGNED_LUMINANCE8_ALPHA8_NV 0x8704 +#define GL_SIGNED_ALPHA_NV 0x8705 +#define GL_SIGNED_ALPHA8_NV 0x8706 +#define GL_SIGNED_INTENSITY_NV 0x8707 +#define GL_SIGNED_INTENSITY8_NV 0x8708 +#define GL_DSDT8_NV 0x8709 +#define GL_DSDT8_MAG8_NV 0x870A +#define GL_DSDT8_MAG8_INTENSITY8_NV 0x870B +#define GL_SIGNED_RGB_UNSIGNED_ALPHA_NV 0x870C +#define GL_SIGNED_RGB8_UNSIGNED_ALPHA8_NV 0x870D +#define GL_HI_SCALE_NV 0x870E +#define GL_LO_SCALE_NV 0x870F +#define GL_DS_SCALE_NV 0x8710 +#define GL_DT_SCALE_NV 0x8711 +#define GL_MAGNITUDE_SCALE_NV 0x8712 +#define GL_VIBRANCE_SCALE_NV 0x8713 +#define GL_HI_BIAS_NV 0x8714 +#define GL_LO_BIAS_NV 0x8715 +#define GL_DS_BIAS_NV 0x8716 +#define GL_DT_BIAS_NV 0x8717 +#define GL_MAGNITUDE_BIAS_NV 0x8718 +#define GL_VIBRANCE_BIAS_NV 0x8719 +#define GL_TEXTURE_BORDER_VALUES_NV 0x871A +#define GL_TEXTURE_HI_SIZE_NV 0x871B +#define GL_TEXTURE_LO_SIZE_NV 0x871C +#define GL_TEXTURE_DS_SIZE_NV 0x871D +#define GL_TEXTURE_DT_SIZE_NV 0x871E +#define GL_TEXTURE_MAG_SIZE_NV 0x871F +#endif /* GL_NV_texture_shader */ + +#ifndef GL_NV_texture_shader2 +#define GL_NV_texture_shader2 1 +#define GL_DOT_PRODUCT_TEXTURE_3D_NV 0x86EF +#endif /* GL_NV_texture_shader2 */ + +#ifndef GL_NV_texture_shader3 +#define GL_NV_texture_shader3 1 +#define GL_OFFSET_PROJECTIVE_TEXTURE_2D_NV 0x8850 +#define GL_OFFSET_PROJECTIVE_TEXTURE_2D_SCALE_NV 0x8851 +#define GL_OFFSET_PROJECTIVE_TEXTURE_RECTANGLE_NV 0x8852 +#define GL_OFFSET_PROJECTIVE_TEXTURE_RECTANGLE_SCALE_NV 0x8853 +#define GL_OFFSET_HILO_TEXTURE_2D_NV 0x8854 +#define GL_OFFSET_HILO_TEXTURE_RECTANGLE_NV 0x8855 +#define GL_OFFSET_HILO_PROJECTIVE_TEXTURE_2D_NV 0x8856 +#define GL_OFFSET_HILO_PROJECTIVE_TEXTURE_RECTANGLE_NV 0x8857 +#define GL_DEPENDENT_HILO_TEXTURE_2D_NV 0x8858 +#define GL_DEPENDENT_RGB_TEXTURE_3D_NV 0x8859 +#define GL_DEPENDENT_RGB_TEXTURE_CUBE_MAP_NV 0x885A +#define GL_DOT_PRODUCT_PASS_THROUGH_NV 0x885B +#define GL_DOT_PRODUCT_TEXTURE_1D_NV 0x885C +#define GL_DOT_PRODUCT_AFFINE_DEPTH_REPLACE_NV 0x885D +#define GL_HILO8_NV 0x885E +#define GL_SIGNED_HILO8_NV 0x885F +#define GL_FORCE_BLUE_TO_ONE_NV 0x8860 +#endif /* GL_NV_texture_shader3 */ + +#ifndef GL_NV_transform_feedback +#define GL_NV_transform_feedback 1 +#define GL_BACK_PRIMARY_COLOR_NV 0x8C77 +#define GL_BACK_SECONDARY_COLOR_NV 0x8C78 +#define GL_TEXTURE_COORD_NV 0x8C79 +#define GL_CLIP_DISTANCE_NV 0x8C7A +#define GL_VERTEX_ID_NV 0x8C7B +#define GL_PRIMITIVE_ID_NV 0x8C7C +#define GL_GENERIC_ATTRIB_NV 0x8C7D +#define GL_TRANSFORM_FEEDBACK_ATTRIBS_NV 0x8C7E +#define GL_TRANSFORM_FEEDBACK_BUFFER_MODE_NV 0x8C7F +#define GL_MAX_TRANSFORM_FEEDBACK_SEPARATE_COMPONENTS_NV 0x8C80 +#define GL_ACTIVE_VARYINGS_NV 0x8C81 +#define GL_ACTIVE_VARYING_MAX_LENGTH_NV 0x8C82 +#define GL_TRANSFORM_FEEDBACK_VARYINGS_NV 0x8C83 +#define GL_TRANSFORM_FEEDBACK_BUFFER_START_NV 0x8C84 +#define GL_TRANSFORM_FEEDBACK_BUFFER_SIZE_NV 0x8C85 +#define GL_TRANSFORM_FEEDBACK_RECORD_NV 0x8C86 +#define GL_PRIMITIVES_GENERATED_NV 0x8C87 +#define GL_TRANSFORM_FEEDBACK_PRIMITIVES_WRITTEN_NV 0x8C88 +#define GL_RASTERIZER_DISCARD_NV 0x8C89 +#define GL_MAX_TRANSFORM_FEEDBACK_INTERLEAVED_COMPONENTS_NV 0x8C8A +#define GL_MAX_TRANSFORM_FEEDBACK_SEPARATE_ATTRIBS_NV 0x8C8B +#define GL_INTERLEAVED_ATTRIBS_NV 0x8C8C +#define GL_SEPARATE_ATTRIBS_NV 0x8C8D +#define GL_TRANSFORM_FEEDBACK_BUFFER_NV 0x8C8E +#define GL_TRANSFORM_FEEDBACK_BUFFER_BINDING_NV 0x8C8F +#define GL_LAYER_NV 0x8DAA +#define GL_NEXT_BUFFER_NV -2 +#define GL_SKIP_COMPONENTS4_NV -3 +#define GL_SKIP_COMPONENTS3_NV -4 +#define GL_SKIP_COMPONENTS2_NV -5 +#define GL_SKIP_COMPONENTS1_NV -6 +typedef void (APIENTRYP PFNGLBEGINTRANSFORMFEEDBACKNVPROC) (GLenum primitiveMode); +typedef void (APIENTRYP PFNGLENDTRANSFORMFEEDBACKNVPROC) (void); +typedef void (APIENTRYP PFNGLTRANSFORMFEEDBACKATTRIBSNVPROC) (GLuint count, const GLint *attribs, GLenum bufferMode); +typedef void (APIENTRYP PFNGLBINDBUFFERRANGENVPROC) (GLenum target, GLuint index, GLuint buffer, GLintptr offset, GLsizeiptr size); +typedef void (APIENTRYP PFNGLBINDBUFFEROFFSETNVPROC) (GLenum target, GLuint index, GLuint buffer, GLintptr offset); +typedef void (APIENTRYP PFNGLBINDBUFFERBASENVPROC) (GLenum target, GLuint index, GLuint buffer); +typedef void (APIENTRYP PFNGLTRANSFORMFEEDBACKVARYINGSNVPROC) (GLuint program, GLsizei count, const GLint *locations, GLenum bufferMode); +typedef void (APIENTRYP PFNGLACTIVEVARYINGNVPROC) (GLuint program, const GLchar *name); +typedef GLint (APIENTRYP PFNGLGETVARYINGLOCATIONNVPROC) (GLuint program, const GLchar *name); +typedef void (APIENTRYP PFNGLGETACTIVEVARYINGNVPROC) (GLuint program, GLuint index, GLsizei bufSize, GLsizei *length, GLsizei *size, GLenum *type, GLchar *name); +typedef void (APIENTRYP PFNGLGETTRANSFORMFEEDBACKVARYINGNVPROC) (GLuint program, GLuint index, GLint *location); +typedef void (APIENTRYP PFNGLTRANSFORMFEEDBACKSTREAMATTRIBSNVPROC) (GLsizei count, const GLint *attribs, GLsizei nbuffers, const GLint *bufstreams, GLenum bufferMode); +#ifdef GL_GLEXT_PROTOTYPES +GLAPI void APIENTRY glBeginTransformFeedbackNV (GLenum primitiveMode); +GLAPI void APIENTRY glEndTransformFeedbackNV (void); +GLAPI void APIENTRY glTransformFeedbackAttribsNV (GLuint count, const GLint *attribs, GLenum bufferMode); +GLAPI void APIENTRY glBindBufferRangeNV (GLenum target, GLuint index, GLuint buffer, GLintptr offset, GLsizeiptr size); +GLAPI void APIENTRY glBindBufferOffsetNV (GLenum target, GLuint index, GLuint buffer, GLintptr offset); +GLAPI void APIENTRY glBindBufferBaseNV (GLenum target, GLuint index, GLuint buffer); +GLAPI void APIENTRY glTransformFeedbackVaryingsNV (GLuint program, GLsizei count, const GLint *locations, GLenum bufferMode); +GLAPI void APIENTRY glActiveVaryingNV (GLuint program, const GLchar *name); +GLAPI GLint APIENTRY glGetVaryingLocationNV (GLuint program, const GLchar *name); +GLAPI void APIENTRY glGetActiveVaryingNV (GLuint program, GLuint index, GLsizei bufSize, GLsizei *length, GLsizei *size, GLenum *type, GLchar *name); +GLAPI void APIENTRY glGetTransformFeedbackVaryingNV (GLuint program, GLuint index, GLint *location); +GLAPI void APIENTRY glTransformFeedbackStreamAttribsNV (GLsizei count, const GLint *attribs, GLsizei nbuffers, const GLint *bufstreams, GLenum bufferMode); +#endif +#endif /* GL_NV_transform_feedback */ + +#ifndef GL_NV_transform_feedback2 +#define GL_NV_transform_feedback2 1 +#define GL_TRANSFORM_FEEDBACK_NV 0x8E22 +#define GL_TRANSFORM_FEEDBACK_BUFFER_PAUSED_NV 0x8E23 +#define GL_TRANSFORM_FEEDBACK_BUFFER_ACTIVE_NV 0x8E24 +#define GL_TRANSFORM_FEEDBACK_BINDING_NV 0x8E25 +typedef void (APIENTRYP PFNGLBINDTRANSFORMFEEDBACKNVPROC) (GLenum target, GLuint id); +typedef void (APIENTRYP PFNGLDELETETRANSFORMFEEDBACKSNVPROC) (GLsizei n, const GLuint *ids); +typedef void (APIENTRYP PFNGLGENTRANSFORMFEEDBACKSNVPROC) (GLsizei n, GLuint *ids); +typedef GLboolean (APIENTRYP PFNGLISTRANSFORMFEEDBACKNVPROC) (GLuint id); +typedef void (APIENTRYP PFNGLPAUSETRANSFORMFEEDBACKNVPROC) (void); +typedef void (APIENTRYP PFNGLRESUMETRANSFORMFEEDBACKNVPROC) (void); +typedef void (APIENTRYP PFNGLDRAWTRANSFORMFEEDBACKNVPROC) (GLenum mode, GLuint id); +#ifdef GL_GLEXT_PROTOTYPES +GLAPI void APIENTRY glBindTransformFeedbackNV (GLenum target, GLuint id); +GLAPI void APIENTRY glDeleteTransformFeedbacksNV (GLsizei n, const GLuint *ids); +GLAPI void APIENTRY glGenTransformFeedbacksNV (GLsizei n, GLuint *ids); +GLAPI GLboolean APIENTRY glIsTransformFeedbackNV (GLuint id); +GLAPI void APIENTRY glPauseTransformFeedbackNV (void); +GLAPI void APIENTRY glResumeTransformFeedbackNV (void); +GLAPI void APIENTRY glDrawTransformFeedbackNV (GLenum mode, GLuint id); +#endif +#endif /* GL_NV_transform_feedback2 */ + +#ifndef GL_NV_vdpau_interop +#define GL_NV_vdpau_interop 1 +typedef GLintptr GLvdpauSurfaceNV; +#define GL_SURFACE_STATE_NV 0x86EB +#define GL_SURFACE_REGISTERED_NV 0x86FD +#define GL_SURFACE_MAPPED_NV 0x8700 +#define GL_WRITE_DISCARD_NV 0x88BE +typedef void (APIENTRYP PFNGLVDPAUINITNVPROC) (const void *vdpDevice, const void *getProcAddress); +typedef void (APIENTRYP PFNGLVDPAUFININVPROC) (void); +typedef GLvdpauSurfaceNV (APIENTRYP PFNGLVDPAUREGISTERVIDEOSURFACENVPROC) (const void *vdpSurface, GLenum target, GLsizei numTextureNames, const GLuint *textureNames); +typedef GLvdpauSurfaceNV (APIENTRYP PFNGLVDPAUREGISTEROUTPUTSURFACENVPROC) (const void *vdpSurface, GLenum target, GLsizei numTextureNames, const GLuint *textureNames); +typedef GLboolean (APIENTRYP PFNGLVDPAUISSURFACENVPROC) (GLvdpauSurfaceNV surface); +typedef void (APIENTRYP PFNGLVDPAUUNREGISTERSURFACENVPROC) (GLvdpauSurfaceNV surface); +typedef void (APIENTRYP PFNGLVDPAUGETSURFACEIVNVPROC) (GLvdpauSurfaceNV surface, GLenum pname, GLsizei bufSize, GLsizei *length, GLint *values); +typedef void (APIENTRYP PFNGLVDPAUSURFACEACCESSNVPROC) (GLvdpauSurfaceNV surface, GLenum access); +typedef void (APIENTRYP PFNGLVDPAUMAPSURFACESNVPROC) (GLsizei numSurfaces, const GLvdpauSurfaceNV *surfaces); +typedef void (APIENTRYP PFNGLVDPAUUNMAPSURFACESNVPROC) (GLsizei numSurface, const GLvdpauSurfaceNV *surfaces); +#ifdef GL_GLEXT_PROTOTYPES +GLAPI void APIENTRY glVDPAUInitNV (const void *vdpDevice, const void *getProcAddress); +GLAPI void APIENTRY glVDPAUFiniNV (void); +GLAPI GLvdpauSurfaceNV APIENTRY glVDPAURegisterVideoSurfaceNV (const void *vdpSurface, GLenum target, GLsizei numTextureNames, const GLuint *textureNames); +GLAPI GLvdpauSurfaceNV APIENTRY glVDPAURegisterOutputSurfaceNV (const void *vdpSurface, GLenum target, GLsizei numTextureNames, const GLuint *textureNames); +GLAPI GLboolean APIENTRY glVDPAUIsSurfaceNV (GLvdpauSurfaceNV surface); +GLAPI void APIENTRY glVDPAUUnregisterSurfaceNV (GLvdpauSurfaceNV surface); +GLAPI void APIENTRY glVDPAUGetSurfaceivNV (GLvdpauSurfaceNV surface, GLenum pname, GLsizei bufSize, GLsizei *length, GLint *values); +GLAPI void APIENTRY glVDPAUSurfaceAccessNV (GLvdpauSurfaceNV surface, GLenum access); +GLAPI void APIENTRY glVDPAUMapSurfacesNV (GLsizei numSurfaces, const GLvdpauSurfaceNV *surfaces); +GLAPI void APIENTRY glVDPAUUnmapSurfacesNV (GLsizei numSurface, const GLvdpauSurfaceNV *surfaces); +#endif +#endif /* GL_NV_vdpau_interop */ + +#ifndef GL_NV_vertex_array_range +#define GL_NV_vertex_array_range 1 +#define GL_VERTEX_ARRAY_RANGE_NV 0x851D +#define GL_VERTEX_ARRAY_RANGE_LENGTH_NV 0x851E +#define GL_VERTEX_ARRAY_RANGE_VALID_NV 0x851F +#define GL_MAX_VERTEX_ARRAY_RANGE_ELEMENT_NV 0x8520 +#define GL_VERTEX_ARRAY_RANGE_POINTER_NV 0x8521 +typedef void (APIENTRYP PFNGLFLUSHVERTEXARRAYRANGENVPROC) (void); +typedef void (APIENTRYP PFNGLVERTEXARRAYRANGENVPROC) (GLsizei length, const void *pointer); +#ifdef GL_GLEXT_PROTOTYPES +GLAPI void APIENTRY glFlushVertexArrayRangeNV (void); +GLAPI void APIENTRY glVertexArrayRangeNV (GLsizei length, const void *pointer); +#endif +#endif /* GL_NV_vertex_array_range */ + +#ifndef GL_NV_vertex_array_range2 +#define GL_NV_vertex_array_range2 1 +#define GL_VERTEX_ARRAY_RANGE_WITHOUT_FLUSH_NV 0x8533 +#endif /* GL_NV_vertex_array_range2 */ + +#ifndef GL_NV_vertex_attrib_integer_64bit +#define GL_NV_vertex_attrib_integer_64bit 1 +typedef void (APIENTRYP PFNGLVERTEXATTRIBL1I64NVPROC) (GLuint index, GLint64EXT x); +typedef void (APIENTRYP PFNGLVERTEXATTRIBL2I64NVPROC) (GLuint index, GLint64EXT x, GLint64EXT y); +typedef void (APIENTRYP PFNGLVERTEXATTRIBL3I64NVPROC) (GLuint index, GLint64EXT x, GLint64EXT y, GLint64EXT z); +typedef void (APIENTRYP PFNGLVERTEXATTRIBL4I64NVPROC) (GLuint index, GLint64EXT x, GLint64EXT y, GLint64EXT z, GLint64EXT w); +typedef void (APIENTRYP PFNGLVERTEXATTRIBL1I64VNVPROC) (GLuint index, const GLint64EXT *v); +typedef void (APIENTRYP PFNGLVERTEXATTRIBL2I64VNVPROC) (GLuint index, const GLint64EXT *v); +typedef void (APIENTRYP PFNGLVERTEXATTRIBL3I64VNVPROC) (GLuint index, const GLint64EXT *v); +typedef void (APIENTRYP PFNGLVERTEXATTRIBL4I64VNVPROC) (GLuint index, const GLint64EXT *v); +typedef void (APIENTRYP PFNGLVERTEXATTRIBL1UI64NVPROC) (GLuint index, GLuint64EXT x); +typedef void (APIENTRYP PFNGLVERTEXATTRIBL2UI64NVPROC) (GLuint index, GLuint64EXT x, GLuint64EXT y); +typedef void (APIENTRYP PFNGLVERTEXATTRIBL3UI64NVPROC) (GLuint index, GLuint64EXT x, GLuint64EXT y, GLuint64EXT z); +typedef void (APIENTRYP PFNGLVERTEXATTRIBL4UI64NVPROC) (GLuint index, GLuint64EXT x, GLuint64EXT y, GLuint64EXT z, GLuint64EXT w); +typedef void (APIENTRYP PFNGLVERTEXATTRIBL1UI64VNVPROC) (GLuint index, const GLuint64EXT *v); +typedef void (APIENTRYP PFNGLVERTEXATTRIBL2UI64VNVPROC) (GLuint index, const GLuint64EXT *v); +typedef void (APIENTRYP PFNGLVERTEXATTRIBL3UI64VNVPROC) (GLuint index, const GLuint64EXT *v); +typedef void (APIENTRYP PFNGLVERTEXATTRIBL4UI64VNVPROC) (GLuint index, const GLuint64EXT *v); +typedef void (APIENTRYP PFNGLGETVERTEXATTRIBLI64VNVPROC) (GLuint index, GLenum pname, GLint64EXT *params); +typedef void (APIENTRYP PFNGLGETVERTEXATTRIBLUI64VNVPROC) (GLuint index, GLenum pname, GLuint64EXT *params); +typedef void (APIENTRYP PFNGLVERTEXATTRIBLFORMATNVPROC) (GLuint index, GLint size, GLenum type, GLsizei stride); +#ifdef GL_GLEXT_PROTOTYPES +GLAPI void APIENTRY glVertexAttribL1i64NV (GLuint index, GLint64EXT x); +GLAPI void APIENTRY glVertexAttribL2i64NV (GLuint index, GLint64EXT x, GLint64EXT y); +GLAPI void APIENTRY glVertexAttribL3i64NV (GLuint index, GLint64EXT x, GLint64EXT y, GLint64EXT z); +GLAPI void APIENTRY glVertexAttribL4i64NV (GLuint index, GLint64EXT x, GLint64EXT y, GLint64EXT z, GLint64EXT w); +GLAPI void APIENTRY glVertexAttribL1i64vNV (GLuint index, const GLint64EXT *v); +GLAPI void APIENTRY glVertexAttribL2i64vNV (GLuint index, const GLint64EXT *v); +GLAPI void APIENTRY glVertexAttribL3i64vNV (GLuint index, const GLint64EXT *v); +GLAPI void APIENTRY glVertexAttribL4i64vNV (GLuint index, const GLint64EXT *v); +GLAPI void APIENTRY glVertexAttribL1ui64NV (GLuint index, GLuint64EXT x); +GLAPI void APIENTRY glVertexAttribL2ui64NV (GLuint index, GLuint64EXT x, GLuint64EXT y); +GLAPI void APIENTRY glVertexAttribL3ui64NV (GLuint index, GLuint64EXT x, GLuint64EXT y, GLuint64EXT z); +GLAPI void APIENTRY glVertexAttribL4ui64NV (GLuint index, GLuint64EXT x, GLuint64EXT y, GLuint64EXT z, GLuint64EXT w); +GLAPI void APIENTRY glVertexAttribL1ui64vNV (GLuint index, const GLuint64EXT *v); +GLAPI void APIENTRY glVertexAttribL2ui64vNV (GLuint index, const GLuint64EXT *v); +GLAPI void APIENTRY glVertexAttribL3ui64vNV (GLuint index, const GLuint64EXT *v); +GLAPI void APIENTRY glVertexAttribL4ui64vNV (GLuint index, const GLuint64EXT *v); +GLAPI void APIENTRY glGetVertexAttribLi64vNV (GLuint index, GLenum pname, GLint64EXT *params); +GLAPI void APIENTRY glGetVertexAttribLui64vNV (GLuint index, GLenum pname, GLuint64EXT *params); +GLAPI void APIENTRY glVertexAttribLFormatNV (GLuint index, GLint size, GLenum type, GLsizei stride); +#endif +#endif /* GL_NV_vertex_attrib_integer_64bit */ + +#ifndef GL_NV_vertex_buffer_unified_memory +#define GL_NV_vertex_buffer_unified_memory 1 +#define GL_VERTEX_ATTRIB_ARRAY_UNIFIED_NV 0x8F1E +#define GL_ELEMENT_ARRAY_UNIFIED_NV 0x8F1F +#define GL_VERTEX_ATTRIB_ARRAY_ADDRESS_NV 0x8F20 +#define GL_VERTEX_ARRAY_ADDRESS_NV 0x8F21 +#define GL_NORMAL_ARRAY_ADDRESS_NV 0x8F22 +#define GL_COLOR_ARRAY_ADDRESS_NV 0x8F23 +#define GL_INDEX_ARRAY_ADDRESS_NV 0x8F24 +#define GL_TEXTURE_COORD_ARRAY_ADDRESS_NV 0x8F25 +#define GL_EDGE_FLAG_ARRAY_ADDRESS_NV 0x8F26 +#define GL_SECONDARY_COLOR_ARRAY_ADDRESS_NV 0x8F27 +#define GL_FOG_COORD_ARRAY_ADDRESS_NV 0x8F28 +#define GL_ELEMENT_ARRAY_ADDRESS_NV 0x8F29 +#define GL_VERTEX_ATTRIB_ARRAY_LENGTH_NV 0x8F2A +#define GL_VERTEX_ARRAY_LENGTH_NV 0x8F2B +#define GL_NORMAL_ARRAY_LENGTH_NV 0x8F2C +#define GL_COLOR_ARRAY_LENGTH_NV 0x8F2D +#define GL_INDEX_ARRAY_LENGTH_NV 0x8F2E +#define GL_TEXTURE_COORD_ARRAY_LENGTH_NV 0x8F2F +#define GL_EDGE_FLAG_ARRAY_LENGTH_NV 0x8F30 +#define GL_SECONDARY_COLOR_ARRAY_LENGTH_NV 0x8F31 +#define GL_FOG_COORD_ARRAY_LENGTH_NV 0x8F32 +#define GL_ELEMENT_ARRAY_LENGTH_NV 0x8F33 +#define GL_DRAW_INDIRECT_UNIFIED_NV 0x8F40 +#define GL_DRAW_INDIRECT_ADDRESS_NV 0x8F41 +#define GL_DRAW_INDIRECT_LENGTH_NV 0x8F42 +typedef void (APIENTRYP PFNGLBUFFERADDRESSRANGENVPROC) (GLenum pname, GLuint index, GLuint64EXT address, GLsizeiptr length); +typedef void (APIENTRYP PFNGLVERTEXFORMATNVPROC) (GLint size, GLenum type, GLsizei stride); +typedef void (APIENTRYP PFNGLNORMALFORMATNVPROC) (GLenum type, GLsizei stride); +typedef void (APIENTRYP PFNGLCOLORFORMATNVPROC) (GLint size, GLenum type, GLsizei stride); +typedef void (APIENTRYP PFNGLINDEXFORMATNVPROC) (GLenum type, GLsizei stride); +typedef void (APIENTRYP PFNGLTEXCOORDFORMATNVPROC) (GLint size, GLenum type, GLsizei stride); +typedef void (APIENTRYP PFNGLEDGEFLAGFORMATNVPROC) (GLsizei stride); +typedef void (APIENTRYP PFNGLSECONDARYCOLORFORMATNVPROC) (GLint size, GLenum type, GLsizei stride); +typedef void (APIENTRYP PFNGLFOGCOORDFORMATNVPROC) (GLenum type, GLsizei stride); +typedef void (APIENTRYP PFNGLVERTEXATTRIBFORMATNVPROC) (GLuint index, GLint size, GLenum type, GLboolean normalized, GLsizei stride); +typedef void (APIENTRYP PFNGLVERTEXATTRIBIFORMATNVPROC) (GLuint index, GLint size, GLenum type, GLsizei stride); +typedef void (APIENTRYP PFNGLGETINTEGERUI64I_VNVPROC) (GLenum value, GLuint index, GLuint64EXT *result); +#ifdef GL_GLEXT_PROTOTYPES +GLAPI void APIENTRY glBufferAddressRangeNV (GLenum pname, GLuint index, GLuint64EXT address, GLsizeiptr length); +GLAPI void APIENTRY glVertexFormatNV (GLint size, GLenum type, GLsizei stride); +GLAPI void APIENTRY glNormalFormatNV (GLenum type, GLsizei stride); +GLAPI void APIENTRY glColorFormatNV (GLint size, GLenum type, GLsizei stride); +GLAPI void APIENTRY glIndexFormatNV (GLenum type, GLsizei stride); +GLAPI void APIENTRY glTexCoordFormatNV (GLint size, GLenum type, GLsizei stride); +GLAPI void APIENTRY glEdgeFlagFormatNV (GLsizei stride); +GLAPI void APIENTRY glSecondaryColorFormatNV (GLint size, GLenum type, GLsizei stride); +GLAPI void APIENTRY glFogCoordFormatNV (GLenum type, GLsizei stride); +GLAPI void APIENTRY glVertexAttribFormatNV (GLuint index, GLint size, GLenum type, GLboolean normalized, GLsizei stride); +GLAPI void APIENTRY glVertexAttribIFormatNV (GLuint index, GLint size, GLenum type, GLsizei stride); +GLAPI void APIENTRY glGetIntegerui64i_vNV (GLenum value, GLuint index, GLuint64EXT *result); +#endif +#endif /* GL_NV_vertex_buffer_unified_memory */ + +#ifndef GL_NV_vertex_program +#define GL_NV_vertex_program 1 +#define GL_VERTEX_PROGRAM_NV 0x8620 +#define GL_VERTEX_STATE_PROGRAM_NV 0x8621 +#define GL_ATTRIB_ARRAY_SIZE_NV 0x8623 +#define GL_ATTRIB_ARRAY_STRIDE_NV 0x8624 +#define GL_ATTRIB_ARRAY_TYPE_NV 0x8625 +#define GL_CURRENT_ATTRIB_NV 0x8626 +#define GL_PROGRAM_LENGTH_NV 0x8627 +#define GL_PROGRAM_STRING_NV 0x8628 +#define GL_MODELVIEW_PROJECTION_NV 0x8629 +#define GL_IDENTITY_NV 0x862A +#define GL_INVERSE_NV 0x862B +#define GL_TRANSPOSE_NV 0x862C +#define GL_INVERSE_TRANSPOSE_NV 0x862D +#define GL_MAX_TRACK_MATRIX_STACK_DEPTH_NV 0x862E +#define GL_MAX_TRACK_MATRICES_NV 0x862F +#define GL_MATRIX0_NV 0x8630 +#define GL_MATRIX1_NV 0x8631 +#define GL_MATRIX2_NV 0x8632 +#define GL_MATRIX3_NV 0x8633 +#define GL_MATRIX4_NV 0x8634 +#define GL_MATRIX5_NV 0x8635 +#define GL_MATRIX6_NV 0x8636 +#define GL_MATRIX7_NV 0x8637 +#define GL_CURRENT_MATRIX_STACK_DEPTH_NV 0x8640 +#define GL_CURRENT_MATRIX_NV 0x8641 +#define GL_VERTEX_PROGRAM_POINT_SIZE_NV 0x8642 +#define GL_VERTEX_PROGRAM_TWO_SIDE_NV 0x8643 +#define GL_PROGRAM_PARAMETER_NV 0x8644 +#define GL_ATTRIB_ARRAY_POINTER_NV 0x8645 +#define GL_PROGRAM_TARGET_NV 0x8646 +#define GL_PROGRAM_RESIDENT_NV 0x8647 +#define GL_TRACK_MATRIX_NV 0x8648 +#define GL_TRACK_MATRIX_TRANSFORM_NV 0x8649 +#define GL_VERTEX_PROGRAM_BINDING_NV 0x864A +#define GL_PROGRAM_ERROR_POSITION_NV 0x864B +#define GL_VERTEX_ATTRIB_ARRAY0_NV 0x8650 +#define GL_VERTEX_ATTRIB_ARRAY1_NV 0x8651 +#define GL_VERTEX_ATTRIB_ARRAY2_NV 0x8652 +#define GL_VERTEX_ATTRIB_ARRAY3_NV 0x8653 +#define GL_VERTEX_ATTRIB_ARRAY4_NV 0x8654 +#define GL_VERTEX_ATTRIB_ARRAY5_NV 0x8655 +#define GL_VERTEX_ATTRIB_ARRAY6_NV 0x8656 +#define GL_VERTEX_ATTRIB_ARRAY7_NV 0x8657 +#define GL_VERTEX_ATTRIB_ARRAY8_NV 0x8658 +#define GL_VERTEX_ATTRIB_ARRAY9_NV 0x8659 +#define GL_VERTEX_ATTRIB_ARRAY10_NV 0x865A +#define GL_VERTEX_ATTRIB_ARRAY11_NV 0x865B +#define GL_VERTEX_ATTRIB_ARRAY12_NV 0x865C +#define GL_VERTEX_ATTRIB_ARRAY13_NV 0x865D +#define GL_VERTEX_ATTRIB_ARRAY14_NV 0x865E +#define GL_VERTEX_ATTRIB_ARRAY15_NV 0x865F +#define GL_MAP1_VERTEX_ATTRIB0_4_NV 0x8660 +#define GL_MAP1_VERTEX_ATTRIB1_4_NV 0x8661 +#define GL_MAP1_VERTEX_ATTRIB2_4_NV 0x8662 +#define GL_MAP1_VERTEX_ATTRIB3_4_NV 0x8663 +#define GL_MAP1_VERTEX_ATTRIB4_4_NV 0x8664 +#define GL_MAP1_VERTEX_ATTRIB5_4_NV 0x8665 +#define GL_MAP1_VERTEX_ATTRIB6_4_NV 0x8666 +#define GL_MAP1_VERTEX_ATTRIB7_4_NV 0x8667 +#define GL_MAP1_VERTEX_ATTRIB8_4_NV 0x8668 +#define GL_MAP1_VERTEX_ATTRIB9_4_NV 0x8669 +#define GL_MAP1_VERTEX_ATTRIB10_4_NV 0x866A +#define GL_MAP1_VERTEX_ATTRIB11_4_NV 0x866B +#define GL_MAP1_VERTEX_ATTRIB12_4_NV 0x866C +#define GL_MAP1_VERTEX_ATTRIB13_4_NV 0x866D +#define GL_MAP1_VERTEX_ATTRIB14_4_NV 0x866E +#define GL_MAP1_VERTEX_ATTRIB15_4_NV 0x866F +#define GL_MAP2_VERTEX_ATTRIB0_4_NV 0x8670 +#define GL_MAP2_VERTEX_ATTRIB1_4_NV 0x8671 +#define GL_MAP2_VERTEX_ATTRIB2_4_NV 0x8672 +#define GL_MAP2_VERTEX_ATTRIB3_4_NV 0x8673 +#define GL_MAP2_VERTEX_ATTRIB4_4_NV 0x8674 +#define GL_MAP2_VERTEX_ATTRIB5_4_NV 0x8675 +#define GL_MAP2_VERTEX_ATTRIB6_4_NV 0x8676 +#define GL_MAP2_VERTEX_ATTRIB7_4_NV 0x8677 +#define GL_MAP2_VERTEX_ATTRIB8_4_NV 0x8678 +#define GL_MAP2_VERTEX_ATTRIB9_4_NV 0x8679 +#define GL_MAP2_VERTEX_ATTRIB10_4_NV 0x867A +#define GL_MAP2_VERTEX_ATTRIB11_4_NV 0x867B +#define GL_MAP2_VERTEX_ATTRIB12_4_NV 0x867C +#define GL_MAP2_VERTEX_ATTRIB13_4_NV 0x867D +#define GL_MAP2_VERTEX_ATTRIB14_4_NV 0x867E +#define GL_MAP2_VERTEX_ATTRIB15_4_NV 0x867F +typedef GLboolean (APIENTRYP PFNGLAREPROGRAMSRESIDENTNVPROC) (GLsizei n, const GLuint *programs, GLboolean *residences); +typedef void (APIENTRYP PFNGLBINDPROGRAMNVPROC) (GLenum target, GLuint id); +typedef void (APIENTRYP PFNGLDELETEPROGRAMSNVPROC) (GLsizei n, const GLuint *programs); +typedef void (APIENTRYP PFNGLEXECUTEPROGRAMNVPROC) (GLenum target, GLuint id, const GLfloat *params); +typedef void (APIENTRYP PFNGLGENPROGRAMSNVPROC) (GLsizei n, GLuint *programs); +typedef void (APIENTRYP PFNGLGETPROGRAMPARAMETERDVNVPROC) (GLenum target, GLuint index, GLenum pname, GLdouble *params); +typedef void (APIENTRYP PFNGLGETPROGRAMPARAMETERFVNVPROC) (GLenum target, GLuint index, GLenum pname, GLfloat *params); +typedef void (APIENTRYP PFNGLGETPROGRAMIVNVPROC) (GLuint id, GLenum pname, GLint *params); +typedef void (APIENTRYP PFNGLGETPROGRAMSTRINGNVPROC) (GLuint id, GLenum pname, GLubyte *program); +typedef void (APIENTRYP PFNGLGETTRACKMATRIXIVNVPROC) (GLenum target, GLuint address, GLenum pname, GLint *params); +typedef void (APIENTRYP PFNGLGETVERTEXATTRIBDVNVPROC) (GLuint index, GLenum pname, GLdouble *params); +typedef void (APIENTRYP PFNGLGETVERTEXATTRIBFVNVPROC) (GLuint index, GLenum pname, GLfloat *params); +typedef void (APIENTRYP PFNGLGETVERTEXATTRIBIVNVPROC) (GLuint index, GLenum pname, GLint *params); +typedef void (APIENTRYP PFNGLGETVERTEXATTRIBPOINTERVNVPROC) (GLuint index, GLenum pname, void **pointer); +typedef GLboolean (APIENTRYP PFNGLISPROGRAMNVPROC) (GLuint id); +typedef void (APIENTRYP PFNGLLOADPROGRAMNVPROC) (GLenum target, GLuint id, GLsizei len, const GLubyte *program); +typedef void (APIENTRYP PFNGLPROGRAMPARAMETER4DNVPROC) (GLenum target, GLuint index, GLdouble x, GLdouble y, GLdouble z, GLdouble w); +typedef void (APIENTRYP PFNGLPROGRAMPARAMETER4DVNVPROC) (GLenum target, GLuint index, const GLdouble *v); +typedef void (APIENTRYP PFNGLPROGRAMPARAMETER4FNVPROC) (GLenum target, GLuint index, GLfloat x, GLfloat y, GLfloat z, GLfloat w); +typedef void (APIENTRYP PFNGLPROGRAMPARAMETER4FVNVPROC) (GLenum target, GLuint index, const GLfloat *v); +typedef void (APIENTRYP PFNGLPROGRAMPARAMETERS4DVNVPROC) (GLenum target, GLuint index, GLsizei count, const GLdouble *v); +typedef void (APIENTRYP PFNGLPROGRAMPARAMETERS4FVNVPROC) (GLenum target, GLuint index, GLsizei count, const GLfloat *v); +typedef void (APIENTRYP PFNGLREQUESTRESIDENTPROGRAMSNVPROC) (GLsizei n, const GLuint *programs); +typedef void (APIENTRYP PFNGLTRACKMATRIXNVPROC) (GLenum target, GLuint address, GLenum matrix, GLenum transform); +typedef void (APIENTRYP PFNGLVERTEXATTRIBPOINTERNVPROC) (GLuint index, GLint fsize, GLenum type, GLsizei stride, const void *pointer); +typedef void (APIENTRYP PFNGLVERTEXATTRIB1DNVPROC) (GLuint index, GLdouble x); +typedef void (APIENTRYP PFNGLVERTEXATTRIB1DVNVPROC) (GLuint index, const GLdouble *v); +typedef void (APIENTRYP PFNGLVERTEXATTRIB1FNVPROC) (GLuint index, GLfloat x); +typedef void (APIENTRYP PFNGLVERTEXATTRIB1FVNVPROC) (GLuint index, const GLfloat *v); +typedef void (APIENTRYP PFNGLVERTEXATTRIB1SNVPROC) (GLuint index, GLshort x); +typedef void (APIENTRYP PFNGLVERTEXATTRIB1SVNVPROC) (GLuint index, const GLshort *v); +typedef void (APIENTRYP PFNGLVERTEXATTRIB2DNVPROC) (GLuint index, GLdouble x, GLdouble y); +typedef void (APIENTRYP PFNGLVERTEXATTRIB2DVNVPROC) (GLuint index, const GLdouble *v); +typedef void (APIENTRYP PFNGLVERTEXATTRIB2FNVPROC) (GLuint index, GLfloat x, GLfloat y); +typedef void (APIENTRYP PFNGLVERTEXATTRIB2FVNVPROC) (GLuint index, const GLfloat *v); +typedef void (APIENTRYP PFNGLVERTEXATTRIB2SNVPROC) (GLuint index, GLshort x, GLshort y); +typedef void (APIENTRYP PFNGLVERTEXATTRIB2SVNVPROC) (GLuint index, const GLshort *v); +typedef void (APIENTRYP PFNGLVERTEXATTRIB3DNVPROC) (GLuint index, GLdouble x, GLdouble y, GLdouble z); +typedef void (APIENTRYP PFNGLVERTEXATTRIB3DVNVPROC) (GLuint index, const GLdouble *v); +typedef void (APIENTRYP PFNGLVERTEXATTRIB3FNVPROC) (GLuint index, GLfloat x, GLfloat y, GLfloat z); +typedef void (APIENTRYP PFNGLVERTEXATTRIB3FVNVPROC) (GLuint index, const GLfloat *v); +typedef void (APIENTRYP PFNGLVERTEXATTRIB3SNVPROC) (GLuint index, GLshort x, GLshort y, GLshort z); +typedef void (APIENTRYP PFNGLVERTEXATTRIB3SVNVPROC) (GLuint index, const GLshort *v); +typedef void (APIENTRYP PFNGLVERTEXATTRIB4DNVPROC) (GLuint index, GLdouble x, GLdouble y, GLdouble z, GLdouble w); +typedef void (APIENTRYP PFNGLVERTEXATTRIB4DVNVPROC) (GLuint index, const GLdouble *v); +typedef void (APIENTRYP PFNGLVERTEXATTRIB4FNVPROC) (GLuint index, GLfloat x, GLfloat y, GLfloat z, GLfloat w); +typedef void (APIENTRYP PFNGLVERTEXATTRIB4FVNVPROC) (GLuint index, const GLfloat *v); +typedef void (APIENTRYP PFNGLVERTEXATTRIB4SNVPROC) (GLuint index, GLshort x, GLshort y, GLshort z, GLshort w); +typedef void (APIENTRYP PFNGLVERTEXATTRIB4SVNVPROC) (GLuint index, const GLshort *v); +typedef void (APIENTRYP PFNGLVERTEXATTRIB4UBNVPROC) (GLuint index, GLubyte x, GLubyte y, GLubyte z, GLubyte w); +typedef void (APIENTRYP PFNGLVERTEXATTRIB4UBVNVPROC) (GLuint index, const GLubyte *v); +typedef void (APIENTRYP PFNGLVERTEXATTRIBS1DVNVPROC) (GLuint index, GLsizei count, const GLdouble *v); +typedef void (APIENTRYP PFNGLVERTEXATTRIBS1FVNVPROC) (GLuint index, GLsizei count, const GLfloat *v); +typedef void (APIENTRYP PFNGLVERTEXATTRIBS1SVNVPROC) (GLuint index, GLsizei count, const GLshort *v); +typedef void (APIENTRYP PFNGLVERTEXATTRIBS2DVNVPROC) (GLuint index, GLsizei count, const GLdouble *v); +typedef void (APIENTRYP PFNGLVERTEXATTRIBS2FVNVPROC) (GLuint index, GLsizei count, const GLfloat *v); +typedef void (APIENTRYP PFNGLVERTEXATTRIBS2SVNVPROC) (GLuint index, GLsizei count, const GLshort *v); +typedef void (APIENTRYP PFNGLVERTEXATTRIBS3DVNVPROC) (GLuint index, GLsizei count, const GLdouble *v); +typedef void (APIENTRYP PFNGLVERTEXATTRIBS3FVNVPROC) (GLuint index, GLsizei count, const GLfloat *v); +typedef void (APIENTRYP PFNGLVERTEXATTRIBS3SVNVPROC) (GLuint index, GLsizei count, const GLshort *v); +typedef void (APIENTRYP PFNGLVERTEXATTRIBS4DVNVPROC) (GLuint index, GLsizei count, const GLdouble *v); +typedef void (APIENTRYP PFNGLVERTEXATTRIBS4FVNVPROC) (GLuint index, GLsizei count, const GLfloat *v); +typedef void (APIENTRYP PFNGLVERTEXATTRIBS4SVNVPROC) (GLuint index, GLsizei count, const GLshort *v); +typedef void (APIENTRYP PFNGLVERTEXATTRIBS4UBVNVPROC) (GLuint index, GLsizei count, const GLubyte *v); +#ifdef GL_GLEXT_PROTOTYPES +GLAPI GLboolean APIENTRY glAreProgramsResidentNV (GLsizei n, const GLuint *programs, GLboolean *residences); +GLAPI void APIENTRY glBindProgramNV (GLenum target, GLuint id); +GLAPI void APIENTRY glDeleteProgramsNV (GLsizei n, const GLuint *programs); +GLAPI void APIENTRY glExecuteProgramNV (GLenum target, GLuint id, const GLfloat *params); +GLAPI void APIENTRY glGenProgramsNV (GLsizei n, GLuint *programs); +GLAPI void APIENTRY glGetProgramParameterdvNV (GLenum target, GLuint index, GLenum pname, GLdouble *params); +GLAPI void APIENTRY glGetProgramParameterfvNV (GLenum target, GLuint index, GLenum pname, GLfloat *params); +GLAPI void APIENTRY glGetProgramivNV (GLuint id, GLenum pname, GLint *params); +GLAPI void APIENTRY glGetProgramStringNV (GLuint id, GLenum pname, GLubyte *program); +GLAPI void APIENTRY glGetTrackMatrixivNV (GLenum target, GLuint address, GLenum pname, GLint *params); +GLAPI void APIENTRY glGetVertexAttribdvNV (GLuint index, GLenum pname, GLdouble *params); +GLAPI void APIENTRY glGetVertexAttribfvNV (GLuint index, GLenum pname, GLfloat *params); +GLAPI void APIENTRY glGetVertexAttribivNV (GLuint index, GLenum pname, GLint *params); +GLAPI void APIENTRY glGetVertexAttribPointervNV (GLuint index, GLenum pname, void **pointer); +GLAPI GLboolean APIENTRY glIsProgramNV (GLuint id); +GLAPI void APIENTRY glLoadProgramNV (GLenum target, GLuint id, GLsizei len, const GLubyte *program); +GLAPI void APIENTRY glProgramParameter4dNV (GLenum target, GLuint index, GLdouble x, GLdouble y, GLdouble z, GLdouble w); +GLAPI void APIENTRY glProgramParameter4dvNV (GLenum target, GLuint index, const GLdouble *v); +GLAPI void APIENTRY glProgramParameter4fNV (GLenum target, GLuint index, GLfloat x, GLfloat y, GLfloat z, GLfloat w); +GLAPI void APIENTRY glProgramParameter4fvNV (GLenum target, GLuint index, const GLfloat *v); +GLAPI void APIENTRY glProgramParameters4dvNV (GLenum target, GLuint index, GLsizei count, const GLdouble *v); +GLAPI void APIENTRY glProgramParameters4fvNV (GLenum target, GLuint index, GLsizei count, const GLfloat *v); +GLAPI void APIENTRY glRequestResidentProgramsNV (GLsizei n, const GLuint *programs); +GLAPI void APIENTRY glTrackMatrixNV (GLenum target, GLuint address, GLenum matrix, GLenum transform); +GLAPI void APIENTRY glVertexAttribPointerNV (GLuint index, GLint fsize, GLenum type, GLsizei stride, const void *pointer); +GLAPI void APIENTRY glVertexAttrib1dNV (GLuint index, GLdouble x); +GLAPI void APIENTRY glVertexAttrib1dvNV (GLuint index, const GLdouble *v); +GLAPI void APIENTRY glVertexAttrib1fNV (GLuint index, GLfloat x); +GLAPI void APIENTRY glVertexAttrib1fvNV (GLuint index, const GLfloat *v); +GLAPI void APIENTRY glVertexAttrib1sNV (GLuint index, GLshort x); +GLAPI void APIENTRY glVertexAttrib1svNV (GLuint index, const GLshort *v); +GLAPI void APIENTRY glVertexAttrib2dNV (GLuint index, GLdouble x, GLdouble y); +GLAPI void APIENTRY glVertexAttrib2dvNV (GLuint index, const GLdouble *v); +GLAPI void APIENTRY glVertexAttrib2fNV (GLuint index, GLfloat x, GLfloat y); +GLAPI void APIENTRY glVertexAttrib2fvNV (GLuint index, const GLfloat *v); +GLAPI void APIENTRY glVertexAttrib2sNV (GLuint index, GLshort x, GLshort y); +GLAPI void APIENTRY glVertexAttrib2svNV (GLuint index, const GLshort *v); +GLAPI void APIENTRY glVertexAttrib3dNV (GLuint index, GLdouble x, GLdouble y, GLdouble z); +GLAPI void APIENTRY glVertexAttrib3dvNV (GLuint index, const GLdouble *v); +GLAPI void APIENTRY glVertexAttrib3fNV (GLuint index, GLfloat x, GLfloat y, GLfloat z); +GLAPI void APIENTRY glVertexAttrib3fvNV (GLuint index, const GLfloat *v); +GLAPI void APIENTRY glVertexAttrib3sNV (GLuint index, GLshort x, GLshort y, GLshort z); +GLAPI void APIENTRY glVertexAttrib3svNV (GLuint index, const GLshort *v); +GLAPI void APIENTRY glVertexAttrib4dNV (GLuint index, GLdouble x, GLdouble y, GLdouble z, GLdouble w); +GLAPI void APIENTRY glVertexAttrib4dvNV (GLuint index, const GLdouble *v); +GLAPI void APIENTRY glVertexAttrib4fNV (GLuint index, GLfloat x, GLfloat y, GLfloat z, GLfloat w); +GLAPI void APIENTRY glVertexAttrib4fvNV (GLuint index, const GLfloat *v); +GLAPI void APIENTRY glVertexAttrib4sNV (GLuint index, GLshort x, GLshort y, GLshort z, GLshort w); +GLAPI void APIENTRY glVertexAttrib4svNV (GLuint index, const GLshort *v); +GLAPI void APIENTRY glVertexAttrib4ubNV (GLuint index, GLubyte x, GLubyte y, GLubyte z, GLubyte w); +GLAPI void APIENTRY glVertexAttrib4ubvNV (GLuint index, const GLubyte *v); +GLAPI void APIENTRY glVertexAttribs1dvNV (GLuint index, GLsizei count, const GLdouble *v); +GLAPI void APIENTRY glVertexAttribs1fvNV (GLuint index, GLsizei count, const GLfloat *v); +GLAPI void APIENTRY glVertexAttribs1svNV (GLuint index, GLsizei count, const GLshort *v); +GLAPI void APIENTRY glVertexAttribs2dvNV (GLuint index, GLsizei count, const GLdouble *v); +GLAPI void APIENTRY glVertexAttribs2fvNV (GLuint index, GLsizei count, const GLfloat *v); +GLAPI void APIENTRY glVertexAttribs2svNV (GLuint index, GLsizei count, const GLshort *v); +GLAPI void APIENTRY glVertexAttribs3dvNV (GLuint index, GLsizei count, const GLdouble *v); +GLAPI void APIENTRY glVertexAttribs3fvNV (GLuint index, GLsizei count, const GLfloat *v); +GLAPI void APIENTRY glVertexAttribs3svNV (GLuint index, GLsizei count, const GLshort *v); +GLAPI void APIENTRY glVertexAttribs4dvNV (GLuint index, GLsizei count, const GLdouble *v); +GLAPI void APIENTRY glVertexAttribs4fvNV (GLuint index, GLsizei count, const GLfloat *v); +GLAPI void APIENTRY glVertexAttribs4svNV (GLuint index, GLsizei count, const GLshort *v); +GLAPI void APIENTRY glVertexAttribs4ubvNV (GLuint index, GLsizei count, const GLubyte *v); +#endif +#endif /* GL_NV_vertex_program */ + +#ifndef GL_NV_vertex_program1_1 +#define GL_NV_vertex_program1_1 1 +#endif /* GL_NV_vertex_program1_1 */ + +#ifndef GL_NV_vertex_program2 +#define GL_NV_vertex_program2 1 +#endif /* GL_NV_vertex_program2 */ + +#ifndef GL_NV_vertex_program2_option +#define GL_NV_vertex_program2_option 1 +#endif /* GL_NV_vertex_program2_option */ + +#ifndef GL_NV_vertex_program3 +#define GL_NV_vertex_program3 1 +#endif /* GL_NV_vertex_program3 */ + +#ifndef GL_NV_vertex_program4 +#define GL_NV_vertex_program4 1 +#define GL_VERTEX_ATTRIB_ARRAY_INTEGER_NV 0x88FD +typedef void (APIENTRYP PFNGLVERTEXATTRIBI1IEXTPROC) (GLuint index, GLint x); +typedef void (APIENTRYP PFNGLVERTEXATTRIBI2IEXTPROC) (GLuint index, GLint x, GLint y); +typedef void (APIENTRYP PFNGLVERTEXATTRIBI3IEXTPROC) (GLuint index, GLint x, GLint y, GLint z); +typedef void (APIENTRYP PFNGLVERTEXATTRIBI4IEXTPROC) (GLuint index, GLint x, GLint y, GLint z, GLint w); +typedef void (APIENTRYP PFNGLVERTEXATTRIBI1UIEXTPROC) (GLuint index, GLuint x); +typedef void (APIENTRYP PFNGLVERTEXATTRIBI2UIEXTPROC) (GLuint index, GLuint x, GLuint y); +typedef void (APIENTRYP PFNGLVERTEXATTRIBI3UIEXTPROC) (GLuint index, GLuint x, GLuint y, GLuint z); +typedef void (APIENTRYP PFNGLVERTEXATTRIBI4UIEXTPROC) (GLuint index, GLuint x, GLuint y, GLuint z, GLuint w); +typedef void (APIENTRYP PFNGLVERTEXATTRIBI1IVEXTPROC) (GLuint index, const GLint *v); +typedef void (APIENTRYP PFNGLVERTEXATTRIBI2IVEXTPROC) (GLuint index, const GLint *v); +typedef void (APIENTRYP PFNGLVERTEXATTRIBI3IVEXTPROC) (GLuint index, const GLint *v); +typedef void (APIENTRYP PFNGLVERTEXATTRIBI4IVEXTPROC) (GLuint index, const GLint *v); +typedef void (APIENTRYP PFNGLVERTEXATTRIBI1UIVEXTPROC) (GLuint index, const GLuint *v); +typedef void (APIENTRYP PFNGLVERTEXATTRIBI2UIVEXTPROC) (GLuint index, const GLuint *v); +typedef void (APIENTRYP PFNGLVERTEXATTRIBI3UIVEXTPROC) (GLuint index, const GLuint *v); +typedef void (APIENTRYP PFNGLVERTEXATTRIBI4UIVEXTPROC) (GLuint index, const GLuint *v); +typedef void (APIENTRYP PFNGLVERTEXATTRIBI4BVEXTPROC) (GLuint index, const GLbyte *v); +typedef void (APIENTRYP PFNGLVERTEXATTRIBI4SVEXTPROC) (GLuint index, const GLshort *v); +typedef void (APIENTRYP PFNGLVERTEXATTRIBI4UBVEXTPROC) (GLuint index, const GLubyte *v); +typedef void (APIENTRYP PFNGLVERTEXATTRIBI4USVEXTPROC) (GLuint index, const GLushort *v); +typedef void (APIENTRYP PFNGLVERTEXATTRIBIPOINTEREXTPROC) (GLuint index, GLint size, GLenum type, GLsizei stride, const void *pointer); +typedef void (APIENTRYP PFNGLGETVERTEXATTRIBIIVEXTPROC) (GLuint index, GLenum pname, GLint *params); +typedef void (APIENTRYP PFNGLGETVERTEXATTRIBIUIVEXTPROC) (GLuint index, GLenum pname, GLuint *params); +#ifdef GL_GLEXT_PROTOTYPES +GLAPI void APIENTRY glVertexAttribI1iEXT (GLuint index, GLint x); +GLAPI void APIENTRY glVertexAttribI2iEXT (GLuint index, GLint x, GLint y); +GLAPI void APIENTRY glVertexAttribI3iEXT (GLuint index, GLint x, GLint y, GLint z); +GLAPI void APIENTRY glVertexAttribI4iEXT (GLuint index, GLint x, GLint y, GLint z, GLint w); +GLAPI void APIENTRY glVertexAttribI1uiEXT (GLuint index, GLuint x); +GLAPI void APIENTRY glVertexAttribI2uiEXT (GLuint index, GLuint x, GLuint y); +GLAPI void APIENTRY glVertexAttribI3uiEXT (GLuint index, GLuint x, GLuint y, GLuint z); +GLAPI void APIENTRY glVertexAttribI4uiEXT (GLuint index, GLuint x, GLuint y, GLuint z, GLuint w); +GLAPI void APIENTRY glVertexAttribI1ivEXT (GLuint index, const GLint *v); +GLAPI void APIENTRY glVertexAttribI2ivEXT (GLuint index, const GLint *v); +GLAPI void APIENTRY glVertexAttribI3ivEXT (GLuint index, const GLint *v); +GLAPI void APIENTRY glVertexAttribI4ivEXT (GLuint index, const GLint *v); +GLAPI void APIENTRY glVertexAttribI1uivEXT (GLuint index, const GLuint *v); +GLAPI void APIENTRY glVertexAttribI2uivEXT (GLuint index, const GLuint *v); +GLAPI void APIENTRY glVertexAttribI3uivEXT (GLuint index, const GLuint *v); +GLAPI void APIENTRY glVertexAttribI4uivEXT (GLuint index, const GLuint *v); +GLAPI void APIENTRY glVertexAttribI4bvEXT (GLuint index, const GLbyte *v); +GLAPI void APIENTRY glVertexAttribI4svEXT (GLuint index, const GLshort *v); +GLAPI void APIENTRY glVertexAttribI4ubvEXT (GLuint index, const GLubyte *v); +GLAPI void APIENTRY glVertexAttribI4usvEXT (GLuint index, const GLushort *v); +GLAPI void APIENTRY glVertexAttribIPointerEXT (GLuint index, GLint size, GLenum type, GLsizei stride, const void *pointer); +GLAPI void APIENTRY glGetVertexAttribIivEXT (GLuint index, GLenum pname, GLint *params); +GLAPI void APIENTRY glGetVertexAttribIuivEXT (GLuint index, GLenum pname, GLuint *params); +#endif +#endif /* GL_NV_vertex_program4 */ + +#ifndef GL_NV_video_capture +#define GL_NV_video_capture 1 +#define GL_VIDEO_BUFFER_NV 0x9020 +#define GL_VIDEO_BUFFER_BINDING_NV 0x9021 +#define GL_FIELD_UPPER_NV 0x9022 +#define GL_FIELD_LOWER_NV 0x9023 +#define GL_NUM_VIDEO_CAPTURE_STREAMS_NV 0x9024 +#define GL_NEXT_VIDEO_CAPTURE_BUFFER_STATUS_NV 0x9025 +#define GL_VIDEO_CAPTURE_TO_422_SUPPORTED_NV 0x9026 +#define GL_LAST_VIDEO_CAPTURE_STATUS_NV 0x9027 +#define GL_VIDEO_BUFFER_PITCH_NV 0x9028 +#define GL_VIDEO_COLOR_CONVERSION_MATRIX_NV 0x9029 +#define GL_VIDEO_COLOR_CONVERSION_MAX_NV 0x902A +#define GL_VIDEO_COLOR_CONVERSION_MIN_NV 0x902B +#define GL_VIDEO_COLOR_CONVERSION_OFFSET_NV 0x902C +#define GL_VIDEO_BUFFER_INTERNAL_FORMAT_NV 0x902D +#define GL_PARTIAL_SUCCESS_NV 0x902E +#define GL_SUCCESS_NV 0x902F +#define GL_FAILURE_NV 0x9030 +#define GL_YCBYCR8_422_NV 0x9031 +#define GL_YCBAYCR8A_4224_NV 0x9032 +#define GL_Z6Y10Z6CB10Z6Y10Z6CR10_422_NV 0x9033 +#define GL_Z6Y10Z6CB10Z6A10Z6Y10Z6CR10Z6A10_4224_NV 0x9034 +#define GL_Z4Y12Z4CB12Z4Y12Z4CR12_422_NV 0x9035 +#define GL_Z4Y12Z4CB12Z4A12Z4Y12Z4CR12Z4A12_4224_NV 0x9036 +#define GL_Z4Y12Z4CB12Z4CR12_444_NV 0x9037 +#define GL_VIDEO_CAPTURE_FRAME_WIDTH_NV 0x9038 +#define GL_VIDEO_CAPTURE_FRAME_HEIGHT_NV 0x9039 +#define GL_VIDEO_CAPTURE_FIELD_UPPER_HEIGHT_NV 0x903A +#define GL_VIDEO_CAPTURE_FIELD_LOWER_HEIGHT_NV 0x903B +#define GL_VIDEO_CAPTURE_SURFACE_ORIGIN_NV 0x903C +typedef void (APIENTRYP PFNGLBEGINVIDEOCAPTURENVPROC) (GLuint video_capture_slot); +typedef void (APIENTRYP PFNGLBINDVIDEOCAPTURESTREAMBUFFERNVPROC) (GLuint video_capture_slot, GLuint stream, GLenum frame_region, GLintptrARB offset); +typedef void (APIENTRYP PFNGLBINDVIDEOCAPTURESTREAMTEXTURENVPROC) (GLuint video_capture_slot, GLuint stream, GLenum frame_region, GLenum target, GLuint texture); +typedef void (APIENTRYP PFNGLENDVIDEOCAPTURENVPROC) (GLuint video_capture_slot); +typedef void (APIENTRYP PFNGLGETVIDEOCAPTUREIVNVPROC) (GLuint video_capture_slot, GLenum pname, GLint *params); +typedef void (APIENTRYP PFNGLGETVIDEOCAPTURESTREAMIVNVPROC) (GLuint video_capture_slot, GLuint stream, GLenum pname, GLint *params); +typedef void (APIENTRYP PFNGLGETVIDEOCAPTURESTREAMFVNVPROC) (GLuint video_capture_slot, GLuint stream, GLenum pname, GLfloat *params); +typedef void (APIENTRYP PFNGLGETVIDEOCAPTURESTREAMDVNVPROC) (GLuint video_capture_slot, GLuint stream, GLenum pname, GLdouble *params); +typedef GLenum (APIENTRYP PFNGLVIDEOCAPTURENVPROC) (GLuint video_capture_slot, GLuint *sequence_num, GLuint64EXT *capture_time); +typedef void (APIENTRYP PFNGLVIDEOCAPTURESTREAMPARAMETERIVNVPROC) (GLuint video_capture_slot, GLuint stream, GLenum pname, const GLint *params); +typedef void (APIENTRYP PFNGLVIDEOCAPTURESTREAMPARAMETERFVNVPROC) (GLuint video_capture_slot, GLuint stream, GLenum pname, const GLfloat *params); +typedef void (APIENTRYP PFNGLVIDEOCAPTURESTREAMPARAMETERDVNVPROC) (GLuint video_capture_slot, GLuint stream, GLenum pname, const GLdouble *params); +#ifdef GL_GLEXT_PROTOTYPES +GLAPI void APIENTRY glBeginVideoCaptureNV (GLuint video_capture_slot); +GLAPI void APIENTRY glBindVideoCaptureStreamBufferNV (GLuint video_capture_slot, GLuint stream, GLenum frame_region, GLintptrARB offset); +GLAPI void APIENTRY glBindVideoCaptureStreamTextureNV (GLuint video_capture_slot, GLuint stream, GLenum frame_region, GLenum target, GLuint texture); +GLAPI void APIENTRY glEndVideoCaptureNV (GLuint video_capture_slot); +GLAPI void APIENTRY glGetVideoCaptureivNV (GLuint video_capture_slot, GLenum pname, GLint *params); +GLAPI void APIENTRY glGetVideoCaptureStreamivNV (GLuint video_capture_slot, GLuint stream, GLenum pname, GLint *params); +GLAPI void APIENTRY glGetVideoCaptureStreamfvNV (GLuint video_capture_slot, GLuint stream, GLenum pname, GLfloat *params); +GLAPI void APIENTRY glGetVideoCaptureStreamdvNV (GLuint video_capture_slot, GLuint stream, GLenum pname, GLdouble *params); +GLAPI GLenum APIENTRY glVideoCaptureNV (GLuint video_capture_slot, GLuint *sequence_num, GLuint64EXT *capture_time); +GLAPI void APIENTRY glVideoCaptureStreamParameterivNV (GLuint video_capture_slot, GLuint stream, GLenum pname, const GLint *params); +GLAPI void APIENTRY glVideoCaptureStreamParameterfvNV (GLuint video_capture_slot, GLuint stream, GLenum pname, const GLfloat *params); +GLAPI void APIENTRY glVideoCaptureStreamParameterdvNV (GLuint video_capture_slot, GLuint stream, GLenum pname, const GLdouble *params); +#endif +#endif /* GL_NV_video_capture */ + +#ifndef GL_OML_interlace +#define GL_OML_interlace 1 +#define GL_INTERLACE_OML 0x8980 +#define GL_INTERLACE_READ_OML 0x8981 +#endif /* GL_OML_interlace */ + +#ifndef GL_OML_resample +#define GL_OML_resample 1 +#define GL_PACK_RESAMPLE_OML 0x8984 +#define GL_UNPACK_RESAMPLE_OML 0x8985 +#define GL_RESAMPLE_REPLICATE_OML 0x8986 +#define GL_RESAMPLE_ZERO_FILL_OML 0x8987 +#define GL_RESAMPLE_AVERAGE_OML 0x8988 +#define GL_RESAMPLE_DECIMATE_OML 0x8989 +#endif /* GL_OML_resample */ + +#ifndef GL_OML_subsample +#define GL_OML_subsample 1 +#define GL_FORMAT_SUBSAMPLE_24_24_OML 0x8982 +#define GL_FORMAT_SUBSAMPLE_244_244_OML 0x8983 +#endif /* GL_OML_subsample */ + +#ifndef GL_PGI_misc_hints +#define GL_PGI_misc_hints 1 +#define GL_PREFER_DOUBLEBUFFER_HINT_PGI 0x1A1F8 +#define GL_CONSERVE_MEMORY_HINT_PGI 0x1A1FD +#define GL_RECLAIM_MEMORY_HINT_PGI 0x1A1FE +#define GL_NATIVE_GRAPHICS_HANDLE_PGI 0x1A202 +#define GL_NATIVE_GRAPHICS_BEGIN_HINT_PGI 0x1A203 +#define GL_NATIVE_GRAPHICS_END_HINT_PGI 0x1A204 +#define GL_ALWAYS_FAST_HINT_PGI 0x1A20C +#define GL_ALWAYS_SOFT_HINT_PGI 0x1A20D +#define GL_ALLOW_DRAW_OBJ_HINT_PGI 0x1A20E +#define GL_ALLOW_DRAW_WIN_HINT_PGI 0x1A20F +#define GL_ALLOW_DRAW_FRG_HINT_PGI 0x1A210 +#define GL_ALLOW_DRAW_MEM_HINT_PGI 0x1A211 +#define GL_STRICT_DEPTHFUNC_HINT_PGI 0x1A216 +#define GL_STRICT_LIGHTING_HINT_PGI 0x1A217 +#define GL_STRICT_SCISSOR_HINT_PGI 0x1A218 +#define GL_FULL_STIPPLE_HINT_PGI 0x1A219 +#define GL_CLIP_NEAR_HINT_PGI 0x1A220 +#define GL_CLIP_FAR_HINT_PGI 0x1A221 +#define GL_WIDE_LINE_HINT_PGI 0x1A222 +#define GL_BACK_NORMALS_HINT_PGI 0x1A223 +typedef void (APIENTRYP PFNGLHINTPGIPROC) (GLenum target, GLint mode); +#ifdef GL_GLEXT_PROTOTYPES +GLAPI void APIENTRY glHintPGI (GLenum target, GLint mode); +#endif +#endif /* GL_PGI_misc_hints */ + +#ifndef GL_PGI_vertex_hints +#define GL_PGI_vertex_hints 1 +#define GL_VERTEX_DATA_HINT_PGI 0x1A22A +#define GL_VERTEX_CONSISTENT_HINT_PGI 0x1A22B +#define GL_MATERIAL_SIDE_HINT_PGI 0x1A22C +#define GL_MAX_VERTEX_HINT_PGI 0x1A22D +#define GL_COLOR3_BIT_PGI 0x00010000 +#define GL_COLOR4_BIT_PGI 0x00020000 +#define GL_EDGEFLAG_BIT_PGI 0x00040000 +#define GL_INDEX_BIT_PGI 0x00080000 +#define GL_MAT_AMBIENT_BIT_PGI 0x00100000 +#define GL_MAT_AMBIENT_AND_DIFFUSE_BIT_PGI 0x00200000 +#define GL_MAT_DIFFUSE_BIT_PGI 0x00400000 +#define GL_MAT_EMISSION_BIT_PGI 0x00800000 +#define GL_MAT_COLOR_INDEXES_BIT_PGI 0x01000000 +#define GL_MAT_SHININESS_BIT_PGI 0x02000000 +#define GL_MAT_SPECULAR_BIT_PGI 0x04000000 +#define GL_NORMAL_BIT_PGI 0x08000000 +#define GL_TEXCOORD1_BIT_PGI 0x10000000 +#define GL_TEXCOORD2_BIT_PGI 0x20000000 +#define GL_TEXCOORD3_BIT_PGI 0x40000000 +#define GL_TEXCOORD4_BIT_PGI 0x80000000 +#define GL_VERTEX23_BIT_PGI 0x00000004 +#define GL_VERTEX4_BIT_PGI 0x00000008 +#endif /* GL_PGI_vertex_hints */ + +#ifndef GL_REND_screen_coordinates +#define GL_REND_screen_coordinates 1 +#define GL_SCREEN_COORDINATES_REND 0x8490 +#define GL_INVERTED_SCREEN_W_REND 0x8491 +#endif /* GL_REND_screen_coordinates */ + +#ifndef GL_S3_s3tc +#define GL_S3_s3tc 1 +#define GL_RGB_S3TC 0x83A0 +#define GL_RGB4_S3TC 0x83A1 +#define GL_RGBA_S3TC 0x83A2 +#define GL_RGBA4_S3TC 0x83A3 +#define GL_RGBA_DXT5_S3TC 0x83A4 +#define GL_RGBA4_DXT5_S3TC 0x83A5 +#endif /* GL_S3_s3tc */ + +#ifndef GL_SGIS_detail_texture +#define GL_SGIS_detail_texture 1 +#define GL_DETAIL_TEXTURE_2D_SGIS 0x8095 +#define GL_DETAIL_TEXTURE_2D_BINDING_SGIS 0x8096 +#define GL_LINEAR_DETAIL_SGIS 0x8097 +#define GL_LINEAR_DETAIL_ALPHA_SGIS 0x8098 +#define GL_LINEAR_DETAIL_COLOR_SGIS 0x8099 +#define GL_DETAIL_TEXTURE_LEVEL_SGIS 0x809A +#define GL_DETAIL_TEXTURE_MODE_SGIS 0x809B +#define GL_DETAIL_TEXTURE_FUNC_POINTS_SGIS 0x809C +typedef void (APIENTRYP PFNGLDETAILTEXFUNCSGISPROC) (GLenum target, GLsizei n, const GLfloat *points); +typedef void (APIENTRYP PFNGLGETDETAILTEXFUNCSGISPROC) (GLenum target, GLfloat *points); +#ifdef GL_GLEXT_PROTOTYPES +GLAPI void APIENTRY glDetailTexFuncSGIS (GLenum target, GLsizei n, const GLfloat *points); +GLAPI void APIENTRY glGetDetailTexFuncSGIS (GLenum target, GLfloat *points); +#endif +#endif /* GL_SGIS_detail_texture */ + +#ifndef GL_SGIS_fog_function +#define GL_SGIS_fog_function 1 +#define GL_FOG_FUNC_SGIS 0x812A +#define GL_FOG_FUNC_POINTS_SGIS 0x812B +#define GL_MAX_FOG_FUNC_POINTS_SGIS 0x812C +typedef void (APIENTRYP PFNGLFOGFUNCSGISPROC) (GLsizei n, const GLfloat *points); +typedef void (APIENTRYP PFNGLGETFOGFUNCSGISPROC) (GLfloat *points); +#ifdef GL_GLEXT_PROTOTYPES +GLAPI void APIENTRY glFogFuncSGIS (GLsizei n, const GLfloat *points); +GLAPI void APIENTRY glGetFogFuncSGIS (GLfloat *points); +#endif +#endif /* GL_SGIS_fog_function */ + +#ifndef GL_SGIS_generate_mipmap +#define GL_SGIS_generate_mipmap 1 +#define GL_GENERATE_MIPMAP_SGIS 0x8191 +#define GL_GENERATE_MIPMAP_HINT_SGIS 0x8192 +#endif /* GL_SGIS_generate_mipmap */ + +#ifndef GL_SGIS_multisample +#define GL_SGIS_multisample 1 +#define GL_MULTISAMPLE_SGIS 0x809D +#define GL_SAMPLE_ALPHA_TO_MASK_SGIS 0x809E +#define GL_SAMPLE_ALPHA_TO_ONE_SGIS 0x809F +#define GL_SAMPLE_MASK_SGIS 0x80A0 +#define GL_1PASS_SGIS 0x80A1 +#define GL_2PASS_0_SGIS 0x80A2 +#define GL_2PASS_1_SGIS 0x80A3 +#define GL_4PASS_0_SGIS 0x80A4 +#define GL_4PASS_1_SGIS 0x80A5 +#define GL_4PASS_2_SGIS 0x80A6 +#define GL_4PASS_3_SGIS 0x80A7 +#define GL_SAMPLE_BUFFERS_SGIS 0x80A8 +#define GL_SAMPLES_SGIS 0x80A9 +#define GL_SAMPLE_MASK_VALUE_SGIS 0x80AA +#define GL_SAMPLE_MASK_INVERT_SGIS 0x80AB +#define GL_SAMPLE_PATTERN_SGIS 0x80AC +typedef void (APIENTRYP PFNGLSAMPLEMASKSGISPROC) (GLclampf value, GLboolean invert); +typedef void (APIENTRYP PFNGLSAMPLEPATTERNSGISPROC) (GLenum pattern); +#ifdef GL_GLEXT_PROTOTYPES +GLAPI void APIENTRY glSampleMaskSGIS (GLclampf value, GLboolean invert); +GLAPI void APIENTRY glSamplePatternSGIS (GLenum pattern); +#endif +#endif /* GL_SGIS_multisample */ + +#ifndef GL_SGIS_pixel_texture +#define GL_SGIS_pixel_texture 1 +#define GL_PIXEL_TEXTURE_SGIS 0x8353 +#define GL_PIXEL_FRAGMENT_RGB_SOURCE_SGIS 0x8354 +#define GL_PIXEL_FRAGMENT_ALPHA_SOURCE_SGIS 0x8355 +#define GL_PIXEL_GROUP_COLOR_SGIS 0x8356 +typedef void (APIENTRYP PFNGLPIXELTEXGENPARAMETERISGISPROC) (GLenum pname, GLint param); +typedef void (APIENTRYP PFNGLPIXELTEXGENPARAMETERIVSGISPROC) (GLenum pname, const GLint *params); +typedef void (APIENTRYP PFNGLPIXELTEXGENPARAMETERFSGISPROC) (GLenum pname, GLfloat param); +typedef void (APIENTRYP PFNGLPIXELTEXGENPARAMETERFVSGISPROC) (GLenum pname, const GLfloat *params); +typedef void (APIENTRYP PFNGLGETPIXELTEXGENPARAMETERIVSGISPROC) (GLenum pname, GLint *params); +typedef void (APIENTRYP PFNGLGETPIXELTEXGENPARAMETERFVSGISPROC) (GLenum pname, GLfloat *params); +#ifdef GL_GLEXT_PROTOTYPES +GLAPI void APIENTRY glPixelTexGenParameteriSGIS (GLenum pname, GLint param); +GLAPI void APIENTRY glPixelTexGenParameterivSGIS (GLenum pname, const GLint *params); +GLAPI void APIENTRY glPixelTexGenParameterfSGIS (GLenum pname, GLfloat param); +GLAPI void APIENTRY glPixelTexGenParameterfvSGIS (GLenum pname, const GLfloat *params); +GLAPI void APIENTRY glGetPixelTexGenParameterivSGIS (GLenum pname, GLint *params); +GLAPI void APIENTRY glGetPixelTexGenParameterfvSGIS (GLenum pname, GLfloat *params); +#endif +#endif /* GL_SGIS_pixel_texture */ + +#ifndef GL_SGIS_point_line_texgen +#define GL_SGIS_point_line_texgen 1 +#define GL_EYE_DISTANCE_TO_POINT_SGIS 0x81F0 +#define GL_OBJECT_DISTANCE_TO_POINT_SGIS 0x81F1 +#define GL_EYE_DISTANCE_TO_LINE_SGIS 0x81F2 +#define GL_OBJECT_DISTANCE_TO_LINE_SGIS 0x81F3 +#define GL_EYE_POINT_SGIS 0x81F4 +#define GL_OBJECT_POINT_SGIS 0x81F5 +#define GL_EYE_LINE_SGIS 0x81F6 +#define GL_OBJECT_LINE_SGIS 0x81F7 +#endif /* GL_SGIS_point_line_texgen */ + +#ifndef GL_SGIS_point_parameters +#define GL_SGIS_point_parameters 1 +#define GL_POINT_SIZE_MIN_SGIS 0x8126 +#define GL_POINT_SIZE_MAX_SGIS 0x8127 +#define GL_POINT_FADE_THRESHOLD_SIZE_SGIS 0x8128 +#define GL_DISTANCE_ATTENUATION_SGIS 0x8129 +typedef void (APIENTRYP PFNGLPOINTPARAMETERFSGISPROC) (GLenum pname, GLfloat param); +typedef void (APIENTRYP PFNGLPOINTPARAMETERFVSGISPROC) (GLenum pname, const GLfloat *params); +#ifdef GL_GLEXT_PROTOTYPES +GLAPI void APIENTRY glPointParameterfSGIS (GLenum pname, GLfloat param); +GLAPI void APIENTRY glPointParameterfvSGIS (GLenum pname, const GLfloat *params); +#endif +#endif /* GL_SGIS_point_parameters */ + +#ifndef GL_SGIS_sharpen_texture +#define GL_SGIS_sharpen_texture 1 +#define GL_LINEAR_SHARPEN_SGIS 0x80AD +#define GL_LINEAR_SHARPEN_ALPHA_SGIS 0x80AE +#define GL_LINEAR_SHARPEN_COLOR_SGIS 0x80AF +#define GL_SHARPEN_TEXTURE_FUNC_POINTS_SGIS 0x80B0 +typedef void (APIENTRYP PFNGLSHARPENTEXFUNCSGISPROC) (GLenum target, GLsizei n, const GLfloat *points); +typedef void (APIENTRYP PFNGLGETSHARPENTEXFUNCSGISPROC) (GLenum target, GLfloat *points); +#ifdef GL_GLEXT_PROTOTYPES +GLAPI void APIENTRY glSharpenTexFuncSGIS (GLenum target, GLsizei n, const GLfloat *points); +GLAPI void APIENTRY glGetSharpenTexFuncSGIS (GLenum target, GLfloat *points); +#endif +#endif /* GL_SGIS_sharpen_texture */ + +#ifndef GL_SGIS_texture4D +#define GL_SGIS_texture4D 1 +#define GL_PACK_SKIP_VOLUMES_SGIS 0x8130 +#define GL_PACK_IMAGE_DEPTH_SGIS 0x8131 +#define GL_UNPACK_SKIP_VOLUMES_SGIS 0x8132 +#define GL_UNPACK_IMAGE_DEPTH_SGIS 0x8133 +#define GL_TEXTURE_4D_SGIS 0x8134 +#define GL_PROXY_TEXTURE_4D_SGIS 0x8135 +#define GL_TEXTURE_4DSIZE_SGIS 0x8136 +#define GL_TEXTURE_WRAP_Q_SGIS 0x8137 +#define GL_MAX_4D_TEXTURE_SIZE_SGIS 0x8138 +#define GL_TEXTURE_4D_BINDING_SGIS 0x814F +typedef void (APIENTRYP PFNGLTEXIMAGE4DSGISPROC) (GLenum target, GLint level, GLenum internalformat, GLsizei width, GLsizei height, GLsizei depth, GLsizei size4d, GLint border, GLenum format, GLenum type, const void *pixels); +typedef void (APIENTRYP PFNGLTEXSUBIMAGE4DSGISPROC) (GLenum target, GLint level, GLint xoffset, GLint yoffset, GLint zoffset, GLint woffset, GLsizei width, GLsizei height, GLsizei depth, GLsizei size4d, GLenum format, GLenum type, const void *pixels); +#ifdef GL_GLEXT_PROTOTYPES +GLAPI void APIENTRY glTexImage4DSGIS (GLenum target, GLint level, GLenum internalformat, GLsizei width, GLsizei height, GLsizei depth, GLsizei size4d, GLint border, GLenum format, GLenum type, const void *pixels); +GLAPI void APIENTRY glTexSubImage4DSGIS (GLenum target, GLint level, GLint xoffset, GLint yoffset, GLint zoffset, GLint woffset, GLsizei width, GLsizei height, GLsizei depth, GLsizei size4d, GLenum format, GLenum type, const void *pixels); +#endif +#endif /* GL_SGIS_texture4D */ + +#ifndef GL_SGIS_texture_border_clamp +#define GL_SGIS_texture_border_clamp 1 +#define GL_CLAMP_TO_BORDER_SGIS 0x812D +#endif /* GL_SGIS_texture_border_clamp */ + +#ifndef GL_SGIS_texture_color_mask +#define GL_SGIS_texture_color_mask 1 +#define GL_TEXTURE_COLOR_WRITEMASK_SGIS 0x81EF +typedef void (APIENTRYP PFNGLTEXTURECOLORMASKSGISPROC) (GLboolean red, GLboolean green, GLboolean blue, GLboolean alpha); +#ifdef GL_GLEXT_PROTOTYPES +GLAPI void APIENTRY glTextureColorMaskSGIS (GLboolean red, GLboolean green, GLboolean blue, GLboolean alpha); +#endif +#endif /* GL_SGIS_texture_color_mask */ + +#ifndef GL_SGIS_texture_edge_clamp +#define GL_SGIS_texture_edge_clamp 1 +#define GL_CLAMP_TO_EDGE_SGIS 0x812F +#endif /* GL_SGIS_texture_edge_clamp */ + +#ifndef GL_SGIS_texture_filter4 +#define GL_SGIS_texture_filter4 1 +#define GL_FILTER4_SGIS 0x8146 +#define GL_TEXTURE_FILTER4_SIZE_SGIS 0x8147 +typedef void (APIENTRYP PFNGLGETTEXFILTERFUNCSGISPROC) (GLenum target, GLenum filter, GLfloat *weights); +typedef void (APIENTRYP PFNGLTEXFILTERFUNCSGISPROC) (GLenum target, GLenum filter, GLsizei n, const GLfloat *weights); +#ifdef GL_GLEXT_PROTOTYPES +GLAPI void APIENTRY glGetTexFilterFuncSGIS (GLenum target, GLenum filter, GLfloat *weights); +GLAPI void APIENTRY glTexFilterFuncSGIS (GLenum target, GLenum filter, GLsizei n, const GLfloat *weights); +#endif +#endif /* GL_SGIS_texture_filter4 */ + +#ifndef GL_SGIS_texture_lod +#define GL_SGIS_texture_lod 1 +#define GL_TEXTURE_MIN_LOD_SGIS 0x813A +#define GL_TEXTURE_MAX_LOD_SGIS 0x813B +#define GL_TEXTURE_BASE_LEVEL_SGIS 0x813C +#define GL_TEXTURE_MAX_LEVEL_SGIS 0x813D +#endif /* GL_SGIS_texture_lod */ + +#ifndef GL_SGIS_texture_select +#define GL_SGIS_texture_select 1 +#define GL_DUAL_ALPHA4_SGIS 0x8110 +#define GL_DUAL_ALPHA8_SGIS 0x8111 +#define GL_DUAL_ALPHA12_SGIS 0x8112 +#define GL_DUAL_ALPHA16_SGIS 0x8113 +#define GL_DUAL_LUMINANCE4_SGIS 0x8114 +#define GL_DUAL_LUMINANCE8_SGIS 0x8115 +#define GL_DUAL_LUMINANCE12_SGIS 0x8116 +#define GL_DUAL_LUMINANCE16_SGIS 0x8117 +#define GL_DUAL_INTENSITY4_SGIS 0x8118 +#define GL_DUAL_INTENSITY8_SGIS 0x8119 +#define GL_DUAL_INTENSITY12_SGIS 0x811A +#define GL_DUAL_INTENSITY16_SGIS 0x811B +#define GL_DUAL_LUMINANCE_ALPHA4_SGIS 0x811C +#define GL_DUAL_LUMINANCE_ALPHA8_SGIS 0x811D +#define GL_QUAD_ALPHA4_SGIS 0x811E +#define GL_QUAD_ALPHA8_SGIS 0x811F +#define GL_QUAD_LUMINANCE4_SGIS 0x8120 +#define GL_QUAD_LUMINANCE8_SGIS 0x8121 +#define GL_QUAD_INTENSITY4_SGIS 0x8122 +#define GL_QUAD_INTENSITY8_SGIS 0x8123 +#define GL_DUAL_TEXTURE_SELECT_SGIS 0x8124 +#define GL_QUAD_TEXTURE_SELECT_SGIS 0x8125 +#endif /* GL_SGIS_texture_select */ + +#ifndef GL_SGIX_async +#define GL_SGIX_async 1 +#define GL_ASYNC_MARKER_SGIX 0x8329 +typedef void (APIENTRYP PFNGLASYNCMARKERSGIXPROC) (GLuint marker); +typedef GLint (APIENTRYP PFNGLFINISHASYNCSGIXPROC) (GLuint *markerp); +typedef GLint (APIENTRYP PFNGLPOLLASYNCSGIXPROC) (GLuint *markerp); +typedef GLuint (APIENTRYP PFNGLGENASYNCMARKERSSGIXPROC) (GLsizei range); +typedef void (APIENTRYP PFNGLDELETEASYNCMARKERSSGIXPROC) (GLuint marker, GLsizei range); +typedef GLboolean (APIENTRYP PFNGLISASYNCMARKERSGIXPROC) (GLuint marker); +#ifdef GL_GLEXT_PROTOTYPES +GLAPI void APIENTRY glAsyncMarkerSGIX (GLuint marker); +GLAPI GLint APIENTRY glFinishAsyncSGIX (GLuint *markerp); +GLAPI GLint APIENTRY glPollAsyncSGIX (GLuint *markerp); +GLAPI GLuint APIENTRY glGenAsyncMarkersSGIX (GLsizei range); +GLAPI void APIENTRY glDeleteAsyncMarkersSGIX (GLuint marker, GLsizei range); +GLAPI GLboolean APIENTRY glIsAsyncMarkerSGIX (GLuint marker); +#endif +#endif /* GL_SGIX_async */ + +#ifndef GL_SGIX_async_histogram +#define GL_SGIX_async_histogram 1 +#define GL_ASYNC_HISTOGRAM_SGIX 0x832C +#define GL_MAX_ASYNC_HISTOGRAM_SGIX 0x832D +#endif /* GL_SGIX_async_histogram */ + +#ifndef GL_SGIX_async_pixel +#define GL_SGIX_async_pixel 1 +#define GL_ASYNC_TEX_IMAGE_SGIX 0x835C +#define GL_ASYNC_DRAW_PIXELS_SGIX 0x835D +#define GL_ASYNC_READ_PIXELS_SGIX 0x835E +#define GL_MAX_ASYNC_TEX_IMAGE_SGIX 0x835F +#define GL_MAX_ASYNC_DRAW_PIXELS_SGIX 0x8360 +#define GL_MAX_ASYNC_READ_PIXELS_SGIX 0x8361 +#endif /* GL_SGIX_async_pixel */ + +#ifndef GL_SGIX_blend_alpha_minmax +#define GL_SGIX_blend_alpha_minmax 1 +#define GL_ALPHA_MIN_SGIX 0x8320 +#define GL_ALPHA_MAX_SGIX 0x8321 +#endif /* GL_SGIX_blend_alpha_minmax */ + +#ifndef GL_SGIX_calligraphic_fragment +#define GL_SGIX_calligraphic_fragment 1 +#define GL_CALLIGRAPHIC_FRAGMENT_SGIX 0x8183 +#endif /* GL_SGIX_calligraphic_fragment */ + +#ifndef GL_SGIX_clipmap +#define GL_SGIX_clipmap 1 +#define GL_LINEAR_CLIPMAP_LINEAR_SGIX 0x8170 +#define GL_TEXTURE_CLIPMAP_CENTER_SGIX 0x8171 +#define GL_TEXTURE_CLIPMAP_FRAME_SGIX 0x8172 +#define GL_TEXTURE_CLIPMAP_OFFSET_SGIX 0x8173 +#define GL_TEXTURE_CLIPMAP_VIRTUAL_DEPTH_SGIX 0x8174 +#define GL_TEXTURE_CLIPMAP_LOD_OFFSET_SGIX 0x8175 +#define GL_TEXTURE_CLIPMAP_DEPTH_SGIX 0x8176 +#define GL_MAX_CLIPMAP_DEPTH_SGIX 0x8177 +#define GL_MAX_CLIPMAP_VIRTUAL_DEPTH_SGIX 0x8178 +#define GL_NEAREST_CLIPMAP_NEAREST_SGIX 0x844D +#define GL_NEAREST_CLIPMAP_LINEAR_SGIX 0x844E +#define GL_LINEAR_CLIPMAP_NEAREST_SGIX 0x844F +#endif /* GL_SGIX_clipmap */ + +#ifndef GL_SGIX_convolution_accuracy +#define GL_SGIX_convolution_accuracy 1 +#define GL_CONVOLUTION_HINT_SGIX 0x8316 +#endif /* GL_SGIX_convolution_accuracy */ + +#ifndef GL_SGIX_depth_pass_instrument +#define GL_SGIX_depth_pass_instrument 1 +#endif /* GL_SGIX_depth_pass_instrument */ + +#ifndef GL_SGIX_depth_texture +#define GL_SGIX_depth_texture 1 +#define GL_DEPTH_COMPONENT16_SGIX 0x81A5 +#define GL_DEPTH_COMPONENT24_SGIX 0x81A6 +#define GL_DEPTH_COMPONENT32_SGIX 0x81A7 +#endif /* GL_SGIX_depth_texture */ + +#ifndef GL_SGIX_flush_raster +#define GL_SGIX_flush_raster 1 +typedef void (APIENTRYP PFNGLFLUSHRASTERSGIXPROC) (void); +#ifdef GL_GLEXT_PROTOTYPES +GLAPI void APIENTRY glFlushRasterSGIX (void); +#endif +#endif /* GL_SGIX_flush_raster */ + +#ifndef GL_SGIX_fog_offset +#define GL_SGIX_fog_offset 1 +#define GL_FOG_OFFSET_SGIX 0x8198 +#define GL_FOG_OFFSET_VALUE_SGIX 0x8199 +#endif /* GL_SGIX_fog_offset */ + +#ifndef GL_SGIX_fragment_lighting +#define GL_SGIX_fragment_lighting 1 +#define GL_FRAGMENT_LIGHTING_SGIX 0x8400 +#define GL_FRAGMENT_COLOR_MATERIAL_SGIX 0x8401 +#define GL_FRAGMENT_COLOR_MATERIAL_FACE_SGIX 0x8402 +#define GL_FRAGMENT_COLOR_MATERIAL_PARAMETER_SGIX 0x8403 +#define GL_MAX_FRAGMENT_LIGHTS_SGIX 0x8404 +#define GL_MAX_ACTIVE_LIGHTS_SGIX 0x8405 +#define GL_CURRENT_RASTER_NORMAL_SGIX 0x8406 +#define GL_LIGHT_ENV_MODE_SGIX 0x8407 +#define GL_FRAGMENT_LIGHT_MODEL_LOCAL_VIEWER_SGIX 0x8408 +#define GL_FRAGMENT_LIGHT_MODEL_TWO_SIDE_SGIX 0x8409 +#define GL_FRAGMENT_LIGHT_MODEL_AMBIENT_SGIX 0x840A +#define GL_FRAGMENT_LIGHT_MODEL_NORMAL_INTERPOLATION_SGIX 0x840B +#define GL_FRAGMENT_LIGHT0_SGIX 0x840C +#define GL_FRAGMENT_LIGHT1_SGIX 0x840D +#define GL_FRAGMENT_LIGHT2_SGIX 0x840E +#define GL_FRAGMENT_LIGHT3_SGIX 0x840F +#define GL_FRAGMENT_LIGHT4_SGIX 0x8410 +#define GL_FRAGMENT_LIGHT5_SGIX 0x8411 +#define GL_FRAGMENT_LIGHT6_SGIX 0x8412 +#define GL_FRAGMENT_LIGHT7_SGIX 0x8413 +typedef void (APIENTRYP PFNGLFRAGMENTCOLORMATERIALSGIXPROC) (GLenum face, GLenum mode); +typedef void (APIENTRYP PFNGLFRAGMENTLIGHTFSGIXPROC) (GLenum light, GLenum pname, GLfloat param); +typedef void (APIENTRYP PFNGLFRAGMENTLIGHTFVSGIXPROC) (GLenum light, GLenum pname, const GLfloat *params); +typedef void (APIENTRYP PFNGLFRAGMENTLIGHTISGIXPROC) (GLenum light, GLenum pname, GLint param); +typedef void (APIENTRYP PFNGLFRAGMENTLIGHTIVSGIXPROC) (GLenum light, GLenum pname, const GLint *params); +typedef void (APIENTRYP PFNGLFRAGMENTLIGHTMODELFSGIXPROC) (GLenum pname, GLfloat param); +typedef void (APIENTRYP PFNGLFRAGMENTLIGHTMODELFVSGIXPROC) (GLenum pname, const GLfloat *params); +typedef void (APIENTRYP PFNGLFRAGMENTLIGHTMODELISGIXPROC) (GLenum pname, GLint param); +typedef void (APIENTRYP PFNGLFRAGMENTLIGHTMODELIVSGIXPROC) (GLenum pname, const GLint *params); +typedef void (APIENTRYP PFNGLFRAGMENTMATERIALFSGIXPROC) (GLenum face, GLenum pname, GLfloat param); +typedef void (APIENTRYP PFNGLFRAGMENTMATERIALFVSGIXPROC) (GLenum face, GLenum pname, const GLfloat *params); +typedef void (APIENTRYP PFNGLFRAGMENTMATERIALISGIXPROC) (GLenum face, GLenum pname, GLint param); +typedef void (APIENTRYP PFNGLFRAGMENTMATERIALIVSGIXPROC) (GLenum face, GLenum pname, const GLint *params); +typedef void (APIENTRYP PFNGLGETFRAGMENTLIGHTFVSGIXPROC) (GLenum light, GLenum pname, GLfloat *params); +typedef void (APIENTRYP PFNGLGETFRAGMENTLIGHTIVSGIXPROC) (GLenum light, GLenum pname, GLint *params); +typedef void (APIENTRYP PFNGLGETFRAGMENTMATERIALFVSGIXPROC) (GLenum face, GLenum pname, GLfloat *params); +typedef void (APIENTRYP PFNGLGETFRAGMENTMATERIALIVSGIXPROC) (GLenum face, GLenum pname, GLint *params); +typedef void (APIENTRYP PFNGLLIGHTENVISGIXPROC) (GLenum pname, GLint param); +#ifdef GL_GLEXT_PROTOTYPES +GLAPI void APIENTRY glFragmentColorMaterialSGIX (GLenum face, GLenum mode); +GLAPI void APIENTRY glFragmentLightfSGIX (GLenum light, GLenum pname, GLfloat param); +GLAPI void APIENTRY glFragmentLightfvSGIX (GLenum light, GLenum pname, const GLfloat *params); +GLAPI void APIENTRY glFragmentLightiSGIX (GLenum light, GLenum pname, GLint param); +GLAPI void APIENTRY glFragmentLightivSGIX (GLenum light, GLenum pname, const GLint *params); +GLAPI void APIENTRY glFragmentLightModelfSGIX (GLenum pname, GLfloat param); +GLAPI void APIENTRY glFragmentLightModelfvSGIX (GLenum pname, const GLfloat *params); +GLAPI void APIENTRY glFragmentLightModeliSGIX (GLenum pname, GLint param); +GLAPI void APIENTRY glFragmentLightModelivSGIX (GLenum pname, const GLint *params); +GLAPI void APIENTRY glFragmentMaterialfSGIX (GLenum face, GLenum pname, GLfloat param); +GLAPI void APIENTRY glFragmentMaterialfvSGIX (GLenum face, GLenum pname, const GLfloat *params); +GLAPI void APIENTRY glFragmentMaterialiSGIX (GLenum face, GLenum pname, GLint param); +GLAPI void APIENTRY glFragmentMaterialivSGIX (GLenum face, GLenum pname, const GLint *params); +GLAPI void APIENTRY glGetFragmentLightfvSGIX (GLenum light, GLenum pname, GLfloat *params); +GLAPI void APIENTRY glGetFragmentLightivSGIX (GLenum light, GLenum pname, GLint *params); +GLAPI void APIENTRY glGetFragmentMaterialfvSGIX (GLenum face, GLenum pname, GLfloat *params); +GLAPI void APIENTRY glGetFragmentMaterialivSGIX (GLenum face, GLenum pname, GLint *params); +GLAPI void APIENTRY glLightEnviSGIX (GLenum pname, GLint param); +#endif +#endif /* GL_SGIX_fragment_lighting */ + +#ifndef GL_SGIX_framezoom +#define GL_SGIX_framezoom 1 +#define GL_FRAMEZOOM_SGIX 0x818B +#define GL_FRAMEZOOM_FACTOR_SGIX 0x818C +#define GL_MAX_FRAMEZOOM_FACTOR_SGIX 0x818D +typedef void (APIENTRYP PFNGLFRAMEZOOMSGIXPROC) (GLint factor); +#ifdef GL_GLEXT_PROTOTYPES +GLAPI void APIENTRY glFrameZoomSGIX (GLint factor); +#endif +#endif /* GL_SGIX_framezoom */ + +#ifndef GL_SGIX_igloo_interface +#define GL_SGIX_igloo_interface 1 +typedef void (APIENTRYP PFNGLIGLOOINTERFACESGIXPROC) (GLenum pname, const void *params); +#ifdef GL_GLEXT_PROTOTYPES +GLAPI void APIENTRY glIglooInterfaceSGIX (GLenum pname, const void *params); +#endif +#endif /* GL_SGIX_igloo_interface */ + +#ifndef GL_SGIX_instruments +#define GL_SGIX_instruments 1 +#define GL_INSTRUMENT_BUFFER_POINTER_SGIX 0x8180 +#define GL_INSTRUMENT_MEASUREMENTS_SGIX 0x8181 +typedef GLint (APIENTRYP PFNGLGETINSTRUMENTSSGIXPROC) (void); +typedef void (APIENTRYP PFNGLINSTRUMENTSBUFFERSGIXPROC) (GLsizei size, GLint *buffer); +typedef GLint (APIENTRYP PFNGLPOLLINSTRUMENTSSGIXPROC) (GLint *marker_p); +typedef void (APIENTRYP PFNGLREADINSTRUMENTSSGIXPROC) (GLint marker); +typedef void (APIENTRYP PFNGLSTARTINSTRUMENTSSGIXPROC) (void); +typedef void (APIENTRYP PFNGLSTOPINSTRUMENTSSGIXPROC) (GLint marker); +#ifdef GL_GLEXT_PROTOTYPES +GLAPI GLint APIENTRY glGetInstrumentsSGIX (void); +GLAPI void APIENTRY glInstrumentsBufferSGIX (GLsizei size, GLint *buffer); +GLAPI GLint APIENTRY glPollInstrumentsSGIX (GLint *marker_p); +GLAPI void APIENTRY glReadInstrumentsSGIX (GLint marker); +GLAPI void APIENTRY glStartInstrumentsSGIX (void); +GLAPI void APIENTRY glStopInstrumentsSGIX (GLint marker); +#endif +#endif /* GL_SGIX_instruments */ + +#ifndef GL_SGIX_interlace +#define GL_SGIX_interlace 1 +#define GL_INTERLACE_SGIX 0x8094 +#endif /* GL_SGIX_interlace */ + +#ifndef GL_SGIX_ir_instrument1 +#define GL_SGIX_ir_instrument1 1 +#define GL_IR_INSTRUMENT1_SGIX 0x817F +#endif /* GL_SGIX_ir_instrument1 */ + +#ifndef GL_SGIX_list_priority +#define GL_SGIX_list_priority 1 +#define GL_LIST_PRIORITY_SGIX 0x8182 +typedef void (APIENTRYP PFNGLGETLISTPARAMETERFVSGIXPROC) (GLuint list, GLenum pname, GLfloat *params); +typedef void (APIENTRYP PFNGLGETLISTPARAMETERIVSGIXPROC) (GLuint list, GLenum pname, GLint *params); +typedef void (APIENTRYP PFNGLLISTPARAMETERFSGIXPROC) (GLuint list, GLenum pname, GLfloat param); +typedef void (APIENTRYP PFNGLLISTPARAMETERFVSGIXPROC) (GLuint list, GLenum pname, const GLfloat *params); +typedef void (APIENTRYP PFNGLLISTPARAMETERISGIXPROC) (GLuint list, GLenum pname, GLint param); +typedef void (APIENTRYP PFNGLLISTPARAMETERIVSGIXPROC) (GLuint list, GLenum pname, const GLint *params); +#ifdef GL_GLEXT_PROTOTYPES +GLAPI void APIENTRY glGetListParameterfvSGIX (GLuint list, GLenum pname, GLfloat *params); +GLAPI void APIENTRY glGetListParameterivSGIX (GLuint list, GLenum pname, GLint *params); +GLAPI void APIENTRY glListParameterfSGIX (GLuint list, GLenum pname, GLfloat param); +GLAPI void APIENTRY glListParameterfvSGIX (GLuint list, GLenum pname, const GLfloat *params); +GLAPI void APIENTRY glListParameteriSGIX (GLuint list, GLenum pname, GLint param); +GLAPI void APIENTRY glListParameterivSGIX (GLuint list, GLenum pname, const GLint *params); +#endif +#endif /* GL_SGIX_list_priority */ + +#ifndef GL_SGIX_pixel_texture +#define GL_SGIX_pixel_texture 1 +#define GL_PIXEL_TEX_GEN_SGIX 0x8139 +#define GL_PIXEL_TEX_GEN_MODE_SGIX 0x832B +typedef void (APIENTRYP PFNGLPIXELTEXGENSGIXPROC) (GLenum mode); +#ifdef GL_GLEXT_PROTOTYPES +GLAPI void APIENTRY glPixelTexGenSGIX (GLenum mode); +#endif +#endif /* GL_SGIX_pixel_texture */ + +#ifndef GL_SGIX_pixel_tiles +#define GL_SGIX_pixel_tiles 1 +#define GL_PIXEL_TILE_BEST_ALIGNMENT_SGIX 0x813E +#define GL_PIXEL_TILE_CACHE_INCREMENT_SGIX 0x813F +#define GL_PIXEL_TILE_WIDTH_SGIX 0x8140 +#define GL_PIXEL_TILE_HEIGHT_SGIX 0x8141 +#define GL_PIXEL_TILE_GRID_WIDTH_SGIX 0x8142 +#define GL_PIXEL_TILE_GRID_HEIGHT_SGIX 0x8143 +#define GL_PIXEL_TILE_GRID_DEPTH_SGIX 0x8144 +#define GL_PIXEL_TILE_CACHE_SIZE_SGIX 0x8145 +#endif /* GL_SGIX_pixel_tiles */ + +#ifndef GL_SGIX_polynomial_ffd +#define GL_SGIX_polynomial_ffd 1 +#define GL_TEXTURE_DEFORMATION_BIT_SGIX 0x00000001 +#define GL_GEOMETRY_DEFORMATION_BIT_SGIX 0x00000002 +#define GL_GEOMETRY_DEFORMATION_SGIX 0x8194 +#define GL_TEXTURE_DEFORMATION_SGIX 0x8195 +#define GL_DEFORMATIONS_MASK_SGIX 0x8196 +#define GL_MAX_DEFORMATION_ORDER_SGIX 0x8197 +typedef void (APIENTRYP PFNGLDEFORMATIONMAP3DSGIXPROC) (GLenum target, GLdouble u1, GLdouble u2, GLint ustride, GLint uorder, GLdouble v1, GLdouble v2, GLint vstride, GLint vorder, GLdouble w1, GLdouble w2, GLint wstride, GLint worder, const GLdouble *points); +typedef void (APIENTRYP PFNGLDEFORMATIONMAP3FSGIXPROC) (GLenum target, GLfloat u1, GLfloat u2, GLint ustride, GLint uorder, GLfloat v1, GLfloat v2, GLint vstride, GLint vorder, GLfloat w1, GLfloat w2, GLint wstride, GLint worder, const GLfloat *points); +typedef void (APIENTRYP PFNGLDEFORMSGIXPROC) (GLbitfield mask); +typedef void (APIENTRYP PFNGLLOADIDENTITYDEFORMATIONMAPSGIXPROC) (GLbitfield mask); +#ifdef GL_GLEXT_PROTOTYPES +GLAPI void APIENTRY glDeformationMap3dSGIX (GLenum target, GLdouble u1, GLdouble u2, GLint ustride, GLint uorder, GLdouble v1, GLdouble v2, GLint vstride, GLint vorder, GLdouble w1, GLdouble w2, GLint wstride, GLint worder, const GLdouble *points); +GLAPI void APIENTRY glDeformationMap3fSGIX (GLenum target, GLfloat u1, GLfloat u2, GLint ustride, GLint uorder, GLfloat v1, GLfloat v2, GLint vstride, GLint vorder, GLfloat w1, GLfloat w2, GLint wstride, GLint worder, const GLfloat *points); +GLAPI void APIENTRY glDeformSGIX (GLbitfield mask); +GLAPI void APIENTRY glLoadIdentityDeformationMapSGIX (GLbitfield mask); +#endif +#endif /* GL_SGIX_polynomial_ffd */ + +#ifndef GL_SGIX_reference_plane +#define GL_SGIX_reference_plane 1 +#define GL_REFERENCE_PLANE_SGIX 0x817D +#define GL_REFERENCE_PLANE_EQUATION_SGIX 0x817E +typedef void (APIENTRYP PFNGLREFERENCEPLANESGIXPROC) (const GLdouble *equation); +#ifdef GL_GLEXT_PROTOTYPES +GLAPI void APIENTRY glReferencePlaneSGIX (const GLdouble *equation); +#endif +#endif /* GL_SGIX_reference_plane */ + +#ifndef GL_SGIX_resample +#define GL_SGIX_resample 1 +#define GL_PACK_RESAMPLE_SGIX 0x842C +#define GL_UNPACK_RESAMPLE_SGIX 0x842D +#define GL_RESAMPLE_REPLICATE_SGIX 0x842E +#define GL_RESAMPLE_ZERO_FILL_SGIX 0x842F +#define GL_RESAMPLE_DECIMATE_SGIX 0x8430 +#endif /* GL_SGIX_resample */ + +#ifndef GL_SGIX_scalebias_hint +#define GL_SGIX_scalebias_hint 1 +#define GL_SCALEBIAS_HINT_SGIX 0x8322 +#endif /* GL_SGIX_scalebias_hint */ + +#ifndef GL_SGIX_shadow +#define GL_SGIX_shadow 1 +#define GL_TEXTURE_COMPARE_SGIX 0x819A +#define GL_TEXTURE_COMPARE_OPERATOR_SGIX 0x819B +#define GL_TEXTURE_LEQUAL_R_SGIX 0x819C +#define GL_TEXTURE_GEQUAL_R_SGIX 0x819D +#endif /* GL_SGIX_shadow */ + +#ifndef GL_SGIX_shadow_ambient +#define GL_SGIX_shadow_ambient 1 +#define GL_SHADOW_AMBIENT_SGIX 0x80BF +#endif /* GL_SGIX_shadow_ambient */ + +#ifndef GL_SGIX_sprite +#define GL_SGIX_sprite 1 +#define GL_SPRITE_SGIX 0x8148 +#define GL_SPRITE_MODE_SGIX 0x8149 +#define GL_SPRITE_AXIS_SGIX 0x814A +#define GL_SPRITE_TRANSLATION_SGIX 0x814B +#define GL_SPRITE_AXIAL_SGIX 0x814C +#define GL_SPRITE_OBJECT_ALIGNED_SGIX 0x814D +#define GL_SPRITE_EYE_ALIGNED_SGIX 0x814E +typedef void (APIENTRYP PFNGLSPRITEPARAMETERFSGIXPROC) (GLenum pname, GLfloat param); +typedef void (APIENTRYP PFNGLSPRITEPARAMETERFVSGIXPROC) (GLenum pname, const GLfloat *params); +typedef void (APIENTRYP PFNGLSPRITEPARAMETERISGIXPROC) (GLenum pname, GLint param); +typedef void (APIENTRYP PFNGLSPRITEPARAMETERIVSGIXPROC) (GLenum pname, const GLint *params); +#ifdef GL_GLEXT_PROTOTYPES +GLAPI void APIENTRY glSpriteParameterfSGIX (GLenum pname, GLfloat param); +GLAPI void APIENTRY glSpriteParameterfvSGIX (GLenum pname, const GLfloat *params); +GLAPI void APIENTRY glSpriteParameteriSGIX (GLenum pname, GLint param); +GLAPI void APIENTRY glSpriteParameterivSGIX (GLenum pname, const GLint *params); +#endif +#endif /* GL_SGIX_sprite */ + +#ifndef GL_SGIX_subsample +#define GL_SGIX_subsample 1 +#define GL_PACK_SUBSAMPLE_RATE_SGIX 0x85A0 +#define GL_UNPACK_SUBSAMPLE_RATE_SGIX 0x85A1 +#define GL_PIXEL_SUBSAMPLE_4444_SGIX 0x85A2 +#define GL_PIXEL_SUBSAMPLE_2424_SGIX 0x85A3 +#define GL_PIXEL_SUBSAMPLE_4242_SGIX 0x85A4 +#endif /* GL_SGIX_subsample */ + +#ifndef GL_SGIX_tag_sample_buffer +#define GL_SGIX_tag_sample_buffer 1 +typedef void (APIENTRYP PFNGLTAGSAMPLEBUFFERSGIXPROC) (void); +#ifdef GL_GLEXT_PROTOTYPES +GLAPI void APIENTRY glTagSampleBufferSGIX (void); +#endif +#endif /* GL_SGIX_tag_sample_buffer */ + +#ifndef GL_SGIX_texture_add_env +#define GL_SGIX_texture_add_env 1 +#define GL_TEXTURE_ENV_BIAS_SGIX 0x80BE +#endif /* GL_SGIX_texture_add_env */ + +#ifndef GL_SGIX_texture_coordinate_clamp +#define GL_SGIX_texture_coordinate_clamp 1 +#define GL_TEXTURE_MAX_CLAMP_S_SGIX 0x8369 +#define GL_TEXTURE_MAX_CLAMP_T_SGIX 0x836A +#define GL_TEXTURE_MAX_CLAMP_R_SGIX 0x836B +#endif /* GL_SGIX_texture_coordinate_clamp */ + +#ifndef GL_SGIX_texture_lod_bias +#define GL_SGIX_texture_lod_bias 1 +#define GL_TEXTURE_LOD_BIAS_S_SGIX 0x818E +#define GL_TEXTURE_LOD_BIAS_T_SGIX 0x818F +#define GL_TEXTURE_LOD_BIAS_R_SGIX 0x8190 +#endif /* GL_SGIX_texture_lod_bias */ + +#ifndef GL_SGIX_texture_multi_buffer +#define GL_SGIX_texture_multi_buffer 1 +#define GL_TEXTURE_MULTI_BUFFER_HINT_SGIX 0x812E +#endif /* GL_SGIX_texture_multi_buffer */ + +#ifndef GL_SGIX_texture_scale_bias +#define GL_SGIX_texture_scale_bias 1 +#define GL_POST_TEXTURE_FILTER_BIAS_SGIX 0x8179 +#define GL_POST_TEXTURE_FILTER_SCALE_SGIX 0x817A +#define GL_POST_TEXTURE_FILTER_BIAS_RANGE_SGIX 0x817B +#define GL_POST_TEXTURE_FILTER_SCALE_RANGE_SGIX 0x817C +#endif /* GL_SGIX_texture_scale_bias */ + +#ifndef GL_SGIX_vertex_preclip +#define GL_SGIX_vertex_preclip 1 +#define GL_VERTEX_PRECLIP_SGIX 0x83EE +#define GL_VERTEX_PRECLIP_HINT_SGIX 0x83EF +#endif /* GL_SGIX_vertex_preclip */ + +#ifndef GL_SGIX_ycrcb +#define GL_SGIX_ycrcb 1 +#define GL_YCRCB_422_SGIX 0x81BB +#define GL_YCRCB_444_SGIX 0x81BC +#endif /* GL_SGIX_ycrcb */ + +#ifndef GL_SGIX_ycrcb_subsample +#define GL_SGIX_ycrcb_subsample 1 +#endif /* GL_SGIX_ycrcb_subsample */ + +#ifndef GL_SGIX_ycrcba +#define GL_SGIX_ycrcba 1 +#define GL_YCRCB_SGIX 0x8318 +#define GL_YCRCBA_SGIX 0x8319 +#endif /* GL_SGIX_ycrcba */ + +#ifndef GL_SGI_color_matrix +#define GL_SGI_color_matrix 1 +#define GL_COLOR_MATRIX_SGI 0x80B1 +#define GL_COLOR_MATRIX_STACK_DEPTH_SGI 0x80B2 +#define GL_MAX_COLOR_MATRIX_STACK_DEPTH_SGI 0x80B3 +#define GL_POST_COLOR_MATRIX_RED_SCALE_SGI 0x80B4 +#define GL_POST_COLOR_MATRIX_GREEN_SCALE_SGI 0x80B5 +#define GL_POST_COLOR_MATRIX_BLUE_SCALE_SGI 0x80B6 +#define GL_POST_COLOR_MATRIX_ALPHA_SCALE_SGI 0x80B7 +#define GL_POST_COLOR_MATRIX_RED_BIAS_SGI 0x80B8 +#define GL_POST_COLOR_MATRIX_GREEN_BIAS_SGI 0x80B9 +#define GL_POST_COLOR_MATRIX_BLUE_BIAS_SGI 0x80BA +#define GL_POST_COLOR_MATRIX_ALPHA_BIAS_SGI 0x80BB +#endif /* GL_SGI_color_matrix */ + +#ifndef GL_SGI_color_table +#define GL_SGI_color_table 1 +#define GL_COLOR_TABLE_SGI 0x80D0 +#define GL_POST_CONVOLUTION_COLOR_TABLE_SGI 0x80D1 +#define GL_POST_COLOR_MATRIX_COLOR_TABLE_SGI 0x80D2 +#define GL_PROXY_COLOR_TABLE_SGI 0x80D3 +#define GL_PROXY_POST_CONVOLUTION_COLOR_TABLE_SGI 0x80D4 +#define GL_PROXY_POST_COLOR_MATRIX_COLOR_TABLE_SGI 0x80D5 +#define GL_COLOR_TABLE_SCALE_SGI 0x80D6 +#define GL_COLOR_TABLE_BIAS_SGI 0x80D7 +#define GL_COLOR_TABLE_FORMAT_SGI 0x80D8 +#define GL_COLOR_TABLE_WIDTH_SGI 0x80D9 +#define GL_COLOR_TABLE_RED_SIZE_SGI 0x80DA +#define GL_COLOR_TABLE_GREEN_SIZE_SGI 0x80DB +#define GL_COLOR_TABLE_BLUE_SIZE_SGI 0x80DC +#define GL_COLOR_TABLE_ALPHA_SIZE_SGI 0x80DD +#define GL_COLOR_TABLE_LUMINANCE_SIZE_SGI 0x80DE +#define GL_COLOR_TABLE_INTENSITY_SIZE_SGI 0x80DF +typedef void (APIENTRYP PFNGLCOLORTABLESGIPROC) (GLenum target, GLenum internalformat, GLsizei width, GLenum format, GLenum type, const void *table); +typedef void (APIENTRYP PFNGLCOLORTABLEPARAMETERFVSGIPROC) (GLenum target, GLenum pname, const GLfloat *params); +typedef void (APIENTRYP PFNGLCOLORTABLEPARAMETERIVSGIPROC) (GLenum target, GLenum pname, const GLint *params); +typedef void (APIENTRYP PFNGLCOPYCOLORTABLESGIPROC) (GLenum target, GLenum internalformat, GLint x, GLint y, GLsizei width); +typedef void (APIENTRYP PFNGLGETCOLORTABLESGIPROC) (GLenum target, GLenum format, GLenum type, void *table); +typedef void (APIENTRYP PFNGLGETCOLORTABLEPARAMETERFVSGIPROC) (GLenum target, GLenum pname, GLfloat *params); +typedef void (APIENTRYP PFNGLGETCOLORTABLEPARAMETERIVSGIPROC) (GLenum target, GLenum pname, GLint *params); +#ifdef GL_GLEXT_PROTOTYPES +GLAPI void APIENTRY glColorTableSGI (GLenum target, GLenum internalformat, GLsizei width, GLenum format, GLenum type, const void *table); +GLAPI void APIENTRY glColorTableParameterfvSGI (GLenum target, GLenum pname, const GLfloat *params); +GLAPI void APIENTRY glColorTableParameterivSGI (GLenum target, GLenum pname, const GLint *params); +GLAPI void APIENTRY glCopyColorTableSGI (GLenum target, GLenum internalformat, GLint x, GLint y, GLsizei width); +GLAPI void APIENTRY glGetColorTableSGI (GLenum target, GLenum format, GLenum type, void *table); +GLAPI void APIENTRY glGetColorTableParameterfvSGI (GLenum target, GLenum pname, GLfloat *params); +GLAPI void APIENTRY glGetColorTableParameterivSGI (GLenum target, GLenum pname, GLint *params); +#endif +#endif /* GL_SGI_color_table */ + +#ifndef GL_SGI_texture_color_table +#define GL_SGI_texture_color_table 1 +#define GL_TEXTURE_COLOR_TABLE_SGI 0x80BC +#define GL_PROXY_TEXTURE_COLOR_TABLE_SGI 0x80BD +#endif /* GL_SGI_texture_color_table */ + +#ifndef GL_SUNX_constant_data +#define GL_SUNX_constant_data 1 +#define GL_UNPACK_CONSTANT_DATA_SUNX 0x81D5 +#define GL_TEXTURE_CONSTANT_DATA_SUNX 0x81D6 +typedef void (APIENTRYP PFNGLFINISHTEXTURESUNXPROC) (void); +#ifdef GL_GLEXT_PROTOTYPES +GLAPI void APIENTRY glFinishTextureSUNX (void); +#endif +#endif /* GL_SUNX_constant_data */ + +#ifndef GL_SUN_convolution_border_modes +#define GL_SUN_convolution_border_modes 1 +#define GL_WRAP_BORDER_SUN 0x81D4 +#endif /* GL_SUN_convolution_border_modes */ + +#ifndef GL_SUN_global_alpha +#define GL_SUN_global_alpha 1 +#define GL_GLOBAL_ALPHA_SUN 0x81D9 +#define GL_GLOBAL_ALPHA_FACTOR_SUN 0x81DA +typedef void (APIENTRYP PFNGLGLOBALALPHAFACTORBSUNPROC) (GLbyte factor); +typedef void (APIENTRYP PFNGLGLOBALALPHAFACTORSSUNPROC) (GLshort factor); +typedef void (APIENTRYP PFNGLGLOBALALPHAFACTORISUNPROC) (GLint factor); +typedef void (APIENTRYP PFNGLGLOBALALPHAFACTORFSUNPROC) (GLfloat factor); +typedef void (APIENTRYP PFNGLGLOBALALPHAFACTORDSUNPROC) (GLdouble factor); +typedef void (APIENTRYP PFNGLGLOBALALPHAFACTORUBSUNPROC) (GLubyte factor); +typedef void (APIENTRYP PFNGLGLOBALALPHAFACTORUSSUNPROC) (GLushort factor); +typedef void (APIENTRYP PFNGLGLOBALALPHAFACTORUISUNPROC) (GLuint factor); +#ifdef GL_GLEXT_PROTOTYPES +GLAPI void APIENTRY glGlobalAlphaFactorbSUN (GLbyte factor); +GLAPI void APIENTRY glGlobalAlphaFactorsSUN (GLshort factor); +GLAPI void APIENTRY glGlobalAlphaFactoriSUN (GLint factor); +GLAPI void APIENTRY glGlobalAlphaFactorfSUN (GLfloat factor); +GLAPI void APIENTRY glGlobalAlphaFactordSUN (GLdouble factor); +GLAPI void APIENTRY glGlobalAlphaFactorubSUN (GLubyte factor); +GLAPI void APIENTRY glGlobalAlphaFactorusSUN (GLushort factor); +GLAPI void APIENTRY glGlobalAlphaFactoruiSUN (GLuint factor); +#endif +#endif /* GL_SUN_global_alpha */ + +#ifndef GL_SUN_mesh_array +#define GL_SUN_mesh_array 1 +#define GL_QUAD_MESH_SUN 0x8614 +#define GL_TRIANGLE_MESH_SUN 0x8615 +typedef void (APIENTRYP PFNGLDRAWMESHARRAYSSUNPROC) (GLenum mode, GLint first, GLsizei count, GLsizei width); +#ifdef GL_GLEXT_PROTOTYPES +GLAPI void APIENTRY glDrawMeshArraysSUN (GLenum mode, GLint first, GLsizei count, GLsizei width); +#endif +#endif /* GL_SUN_mesh_array */ + +#ifndef GL_SUN_slice_accum +#define GL_SUN_slice_accum 1 +#define GL_SLICE_ACCUM_SUN 0x85CC +#endif /* GL_SUN_slice_accum */ + +#ifndef GL_SUN_triangle_list +#define GL_SUN_triangle_list 1 +#define GL_RESTART_SUN 0x0001 +#define GL_REPLACE_MIDDLE_SUN 0x0002 +#define GL_REPLACE_OLDEST_SUN 0x0003 +#define GL_TRIANGLE_LIST_SUN 0x81D7 +#define GL_REPLACEMENT_CODE_SUN 0x81D8 +#define GL_REPLACEMENT_CODE_ARRAY_SUN 0x85C0 +#define GL_REPLACEMENT_CODE_ARRAY_TYPE_SUN 0x85C1 +#define GL_REPLACEMENT_CODE_ARRAY_STRIDE_SUN 0x85C2 +#define GL_REPLACEMENT_CODE_ARRAY_POINTER_SUN 0x85C3 +#define GL_R1UI_V3F_SUN 0x85C4 +#define GL_R1UI_C4UB_V3F_SUN 0x85C5 +#define GL_R1UI_C3F_V3F_SUN 0x85C6 +#define GL_R1UI_N3F_V3F_SUN 0x85C7 +#define GL_R1UI_C4F_N3F_V3F_SUN 0x85C8 +#define GL_R1UI_T2F_V3F_SUN 0x85C9 +#define GL_R1UI_T2F_N3F_V3F_SUN 0x85CA +#define GL_R1UI_T2F_C4F_N3F_V3F_SUN 0x85CB +typedef void (APIENTRYP PFNGLREPLACEMENTCODEUISUNPROC) (GLuint code); +typedef void (APIENTRYP PFNGLREPLACEMENTCODEUSSUNPROC) (GLushort code); +typedef void (APIENTRYP PFNGLREPLACEMENTCODEUBSUNPROC) (GLubyte code); +typedef void (APIENTRYP PFNGLREPLACEMENTCODEUIVSUNPROC) (const GLuint *code); +typedef void (APIENTRYP PFNGLREPLACEMENTCODEUSVSUNPROC) (const GLushort *code); +typedef void (APIENTRYP PFNGLREPLACEMENTCODEUBVSUNPROC) (const GLubyte *code); +typedef void (APIENTRYP PFNGLREPLACEMENTCODEPOINTERSUNPROC) (GLenum type, GLsizei stride, const void **pointer); +#ifdef GL_GLEXT_PROTOTYPES +GLAPI void APIENTRY glReplacementCodeuiSUN (GLuint code); +GLAPI void APIENTRY glReplacementCodeusSUN (GLushort code); +GLAPI void APIENTRY glReplacementCodeubSUN (GLubyte code); +GLAPI void APIENTRY glReplacementCodeuivSUN (const GLuint *code); +GLAPI void APIENTRY glReplacementCodeusvSUN (const GLushort *code); +GLAPI void APIENTRY glReplacementCodeubvSUN (const GLubyte *code); +GLAPI void APIENTRY glReplacementCodePointerSUN (GLenum type, GLsizei stride, const void **pointer); +#endif +#endif /* GL_SUN_triangle_list */ + +#ifndef GL_SUN_vertex +#define GL_SUN_vertex 1 +typedef void (APIENTRYP PFNGLCOLOR4UBVERTEX2FSUNPROC) (GLubyte r, GLubyte g, GLubyte b, GLubyte a, GLfloat x, GLfloat y); +typedef void (APIENTRYP PFNGLCOLOR4UBVERTEX2FVSUNPROC) (const GLubyte *c, const GLfloat *v); +typedef void (APIENTRYP PFNGLCOLOR4UBVERTEX3FSUNPROC) (GLubyte r, GLubyte g, GLubyte b, GLubyte a, GLfloat x, GLfloat y, GLfloat z); +typedef void (APIENTRYP PFNGLCOLOR4UBVERTEX3FVSUNPROC) (const GLubyte *c, const GLfloat *v); +typedef void (APIENTRYP PFNGLCOLOR3FVERTEX3FSUNPROC) (GLfloat r, GLfloat g, GLfloat b, GLfloat x, GLfloat y, GLfloat z); +typedef void (APIENTRYP PFNGLCOLOR3FVERTEX3FVSUNPROC) (const GLfloat *c, const GLfloat *v); +typedef void (APIENTRYP PFNGLNORMAL3FVERTEX3FSUNPROC) (GLfloat nx, GLfloat ny, GLfloat nz, GLfloat x, GLfloat y, GLfloat z); +typedef void (APIENTRYP PFNGLNORMAL3FVERTEX3FVSUNPROC) (const GLfloat *n, const GLfloat *v); +typedef void (APIENTRYP PFNGLCOLOR4FNORMAL3FVERTEX3FSUNPROC) (GLfloat r, GLfloat g, GLfloat b, GLfloat a, GLfloat nx, GLfloat ny, GLfloat nz, GLfloat x, GLfloat y, GLfloat z); +typedef void (APIENTRYP PFNGLCOLOR4FNORMAL3FVERTEX3FVSUNPROC) (const GLfloat *c, const GLfloat *n, const GLfloat *v); +typedef void (APIENTRYP PFNGLTEXCOORD2FVERTEX3FSUNPROC) (GLfloat s, GLfloat t, GLfloat x, GLfloat y, GLfloat z); +typedef void (APIENTRYP PFNGLTEXCOORD2FVERTEX3FVSUNPROC) (const GLfloat *tc, const GLfloat *v); +typedef void (APIENTRYP PFNGLTEXCOORD4FVERTEX4FSUNPROC) (GLfloat s, GLfloat t, GLfloat p, GLfloat q, GLfloat x, GLfloat y, GLfloat z, GLfloat w); +typedef void (APIENTRYP PFNGLTEXCOORD4FVERTEX4FVSUNPROC) (const GLfloat *tc, const GLfloat *v); +typedef void (APIENTRYP PFNGLTEXCOORD2FCOLOR4UBVERTEX3FSUNPROC) (GLfloat s, GLfloat t, GLubyte r, GLubyte g, GLubyte b, GLubyte a, GLfloat x, GLfloat y, GLfloat z); +typedef void (APIENTRYP PFNGLTEXCOORD2FCOLOR4UBVERTEX3FVSUNPROC) (const GLfloat *tc, const GLubyte *c, const GLfloat *v); +typedef void (APIENTRYP PFNGLTEXCOORD2FCOLOR3FVERTEX3FSUNPROC) (GLfloat s, GLfloat t, GLfloat r, GLfloat g, GLfloat b, GLfloat x, GLfloat y, GLfloat z); +typedef void (APIENTRYP PFNGLTEXCOORD2FCOLOR3FVERTEX3FVSUNPROC) (const GLfloat *tc, const GLfloat *c, const GLfloat *v); +typedef void (APIENTRYP PFNGLTEXCOORD2FNORMAL3FVERTEX3FSUNPROC) (GLfloat s, GLfloat t, GLfloat nx, GLfloat ny, GLfloat nz, GLfloat x, GLfloat y, GLfloat z); +typedef void (APIENTRYP PFNGLTEXCOORD2FNORMAL3FVERTEX3FVSUNPROC) (const GLfloat *tc, const GLfloat *n, const GLfloat *v); +typedef void (APIENTRYP PFNGLTEXCOORD2FCOLOR4FNORMAL3FVERTEX3FSUNPROC) (GLfloat s, GLfloat t, GLfloat r, GLfloat g, GLfloat b, GLfloat a, GLfloat nx, GLfloat ny, GLfloat nz, GLfloat x, GLfloat y, GLfloat z); +typedef void (APIENTRYP PFNGLTEXCOORD2FCOLOR4FNORMAL3FVERTEX3FVSUNPROC) (const GLfloat *tc, const GLfloat *c, const GLfloat *n, const GLfloat *v); +typedef void (APIENTRYP PFNGLTEXCOORD4FCOLOR4FNORMAL3FVERTEX4FSUNPROC) (GLfloat s, GLfloat t, GLfloat p, GLfloat q, GLfloat r, GLfloat g, GLfloat b, GLfloat a, GLfloat nx, GLfloat ny, GLfloat nz, GLfloat x, GLfloat y, GLfloat z, GLfloat w); +typedef void (APIENTRYP PFNGLTEXCOORD4FCOLOR4FNORMAL3FVERTEX4FVSUNPROC) (const GLfloat *tc, const GLfloat *c, const GLfloat *n, const GLfloat *v); +typedef void (APIENTRYP PFNGLREPLACEMENTCODEUIVERTEX3FSUNPROC) (GLuint rc, GLfloat x, GLfloat y, GLfloat z); +typedef void (APIENTRYP PFNGLREPLACEMENTCODEUIVERTEX3FVSUNPROC) (const GLuint *rc, const GLfloat *v); +typedef void (APIENTRYP PFNGLREPLACEMENTCODEUICOLOR4UBVERTEX3FSUNPROC) (GLuint rc, GLubyte r, GLubyte g, GLubyte b, GLubyte a, GLfloat x, GLfloat y, GLfloat z); +typedef void (APIENTRYP PFNGLREPLACEMENTCODEUICOLOR4UBVERTEX3FVSUNPROC) (const GLuint *rc, const GLubyte *c, const GLfloat *v); +typedef void (APIENTRYP PFNGLREPLACEMENTCODEUICOLOR3FVERTEX3FSUNPROC) (GLuint rc, GLfloat r, GLfloat g, GLfloat b, GLfloat x, GLfloat y, GLfloat z); +typedef void (APIENTRYP PFNGLREPLACEMENTCODEUICOLOR3FVERTEX3FVSUNPROC) (const GLuint *rc, const GLfloat *c, const GLfloat *v); +typedef void (APIENTRYP PFNGLREPLACEMENTCODEUINORMAL3FVERTEX3FSUNPROC) (GLuint rc, GLfloat nx, GLfloat ny, GLfloat nz, GLfloat x, GLfloat y, GLfloat z); +typedef void (APIENTRYP PFNGLREPLACEMENTCODEUINORMAL3FVERTEX3FVSUNPROC) (const GLuint *rc, const GLfloat *n, const GLfloat *v); +typedef void (APIENTRYP PFNGLREPLACEMENTCODEUICOLOR4FNORMAL3FVERTEX3FSUNPROC) (GLuint rc, GLfloat r, GLfloat g, GLfloat b, GLfloat a, GLfloat nx, GLfloat ny, GLfloat nz, GLfloat x, GLfloat y, GLfloat z); +typedef void (APIENTRYP PFNGLREPLACEMENTCODEUICOLOR4FNORMAL3FVERTEX3FVSUNPROC) (const GLuint *rc, const GLfloat *c, const GLfloat *n, const GLfloat *v); +typedef void (APIENTRYP PFNGLREPLACEMENTCODEUITEXCOORD2FVERTEX3FSUNPROC) (GLuint rc, GLfloat s, GLfloat t, GLfloat x, GLfloat y, GLfloat z); +typedef void (APIENTRYP PFNGLREPLACEMENTCODEUITEXCOORD2FVERTEX3FVSUNPROC) (const GLuint *rc, const GLfloat *tc, const GLfloat *v); +typedef void (APIENTRYP PFNGLREPLACEMENTCODEUITEXCOORD2FNORMAL3FVERTEX3FSUNPROC) (GLuint rc, GLfloat s, GLfloat t, GLfloat nx, GLfloat ny, GLfloat nz, GLfloat x, GLfloat y, GLfloat z); +typedef void (APIENTRYP PFNGLREPLACEMENTCODEUITEXCOORD2FNORMAL3FVERTEX3FVSUNPROC) (const GLuint *rc, const GLfloat *tc, const GLfloat *n, const GLfloat *v); +typedef void (APIENTRYP PFNGLREPLACEMENTCODEUITEXCOORD2FCOLOR4FNORMAL3FVERTEX3FSUNPROC) (GLuint rc, GLfloat s, GLfloat t, GLfloat r, GLfloat g, GLfloat b, GLfloat a, GLfloat nx, GLfloat ny, GLfloat nz, GLfloat x, GLfloat y, GLfloat z); +typedef void (APIENTRYP PFNGLREPLACEMENTCODEUITEXCOORD2FCOLOR4FNORMAL3FVERTEX3FVSUNPROC) (const GLuint *rc, const GLfloat *tc, const GLfloat *c, const GLfloat *n, const GLfloat *v); +#ifdef GL_GLEXT_PROTOTYPES +GLAPI void APIENTRY glColor4ubVertex2fSUN (GLubyte r, GLubyte g, GLubyte b, GLubyte a, GLfloat x, GLfloat y); +GLAPI void APIENTRY glColor4ubVertex2fvSUN (const GLubyte *c, const GLfloat *v); +GLAPI void APIENTRY glColor4ubVertex3fSUN (GLubyte r, GLubyte g, GLubyte b, GLubyte a, GLfloat x, GLfloat y, GLfloat z); +GLAPI void APIENTRY glColor4ubVertex3fvSUN (const GLubyte *c, const GLfloat *v); +GLAPI void APIENTRY glColor3fVertex3fSUN (GLfloat r, GLfloat g, GLfloat b, GLfloat x, GLfloat y, GLfloat z); +GLAPI void APIENTRY glColor3fVertex3fvSUN (const GLfloat *c, const GLfloat *v); +GLAPI void APIENTRY glNormal3fVertex3fSUN (GLfloat nx, GLfloat ny, GLfloat nz, GLfloat x, GLfloat y, GLfloat z); +GLAPI void APIENTRY glNormal3fVertex3fvSUN (const GLfloat *n, const GLfloat *v); +GLAPI void APIENTRY glColor4fNormal3fVertex3fSUN (GLfloat r, GLfloat g, GLfloat b, GLfloat a, GLfloat nx, GLfloat ny, GLfloat nz, GLfloat x, GLfloat y, GLfloat z); +GLAPI void APIENTRY glColor4fNormal3fVertex3fvSUN (const GLfloat *c, const GLfloat *n, const GLfloat *v); +GLAPI void APIENTRY glTexCoord2fVertex3fSUN (GLfloat s, GLfloat t, GLfloat x, GLfloat y, GLfloat z); +GLAPI void APIENTRY glTexCoord2fVertex3fvSUN (const GLfloat *tc, const GLfloat *v); +GLAPI void APIENTRY glTexCoord4fVertex4fSUN (GLfloat s, GLfloat t, GLfloat p, GLfloat q, GLfloat x, GLfloat y, GLfloat z, GLfloat w); +GLAPI void APIENTRY glTexCoord4fVertex4fvSUN (const GLfloat *tc, const GLfloat *v); +GLAPI void APIENTRY glTexCoord2fColor4ubVertex3fSUN (GLfloat s, GLfloat t, GLubyte r, GLubyte g, GLubyte b, GLubyte a, GLfloat x, GLfloat y, GLfloat z); +GLAPI void APIENTRY glTexCoord2fColor4ubVertex3fvSUN (const GLfloat *tc, const GLubyte *c, const GLfloat *v); +GLAPI void APIENTRY glTexCoord2fColor3fVertex3fSUN (GLfloat s, GLfloat t, GLfloat r, GLfloat g, GLfloat b, GLfloat x, GLfloat y, GLfloat z); +GLAPI void APIENTRY glTexCoord2fColor3fVertex3fvSUN (const GLfloat *tc, const GLfloat *c, const GLfloat *v); +GLAPI void APIENTRY glTexCoord2fNormal3fVertex3fSUN (GLfloat s, GLfloat t, GLfloat nx, GLfloat ny, GLfloat nz, GLfloat x, GLfloat y, GLfloat z); +GLAPI void APIENTRY glTexCoord2fNormal3fVertex3fvSUN (const GLfloat *tc, const GLfloat *n, const GLfloat *v); +GLAPI void APIENTRY glTexCoord2fColor4fNormal3fVertex3fSUN (GLfloat s, GLfloat t, GLfloat r, GLfloat g, GLfloat b, GLfloat a, GLfloat nx, GLfloat ny, GLfloat nz, GLfloat x, GLfloat y, GLfloat z); +GLAPI void APIENTRY glTexCoord2fColor4fNormal3fVertex3fvSUN (const GLfloat *tc, const GLfloat *c, const GLfloat *n, const GLfloat *v); +GLAPI void APIENTRY glTexCoord4fColor4fNormal3fVertex4fSUN (GLfloat s, GLfloat t, GLfloat p, GLfloat q, GLfloat r, GLfloat g, GLfloat b, GLfloat a, GLfloat nx, GLfloat ny, GLfloat nz, GLfloat x, GLfloat y, GLfloat z, GLfloat w); +GLAPI void APIENTRY glTexCoord4fColor4fNormal3fVertex4fvSUN (const GLfloat *tc, const GLfloat *c, const GLfloat *n, const GLfloat *v); +GLAPI void APIENTRY glReplacementCodeuiVertex3fSUN (GLuint rc, GLfloat x, GLfloat y, GLfloat z); +GLAPI void APIENTRY glReplacementCodeuiVertex3fvSUN (const GLuint *rc, const GLfloat *v); +GLAPI void APIENTRY glReplacementCodeuiColor4ubVertex3fSUN (GLuint rc, GLubyte r, GLubyte g, GLubyte b, GLubyte a, GLfloat x, GLfloat y, GLfloat z); +GLAPI void APIENTRY glReplacementCodeuiColor4ubVertex3fvSUN (const GLuint *rc, const GLubyte *c, const GLfloat *v); +GLAPI void APIENTRY glReplacementCodeuiColor3fVertex3fSUN (GLuint rc, GLfloat r, GLfloat g, GLfloat b, GLfloat x, GLfloat y, GLfloat z); +GLAPI void APIENTRY glReplacementCodeuiColor3fVertex3fvSUN (const GLuint *rc, const GLfloat *c, const GLfloat *v); +GLAPI void APIENTRY glReplacementCodeuiNormal3fVertex3fSUN (GLuint rc, GLfloat nx, GLfloat ny, GLfloat nz, GLfloat x, GLfloat y, GLfloat z); +GLAPI void APIENTRY glReplacementCodeuiNormal3fVertex3fvSUN (const GLuint *rc, const GLfloat *n, const GLfloat *v); +GLAPI void APIENTRY glReplacementCodeuiColor4fNormal3fVertex3fSUN (GLuint rc, GLfloat r, GLfloat g, GLfloat b, GLfloat a, GLfloat nx, GLfloat ny, GLfloat nz, GLfloat x, GLfloat y, GLfloat z); +GLAPI void APIENTRY glReplacementCodeuiColor4fNormal3fVertex3fvSUN (const GLuint *rc, const GLfloat *c, const GLfloat *n, const GLfloat *v); +GLAPI void APIENTRY glReplacementCodeuiTexCoord2fVertex3fSUN (GLuint rc, GLfloat s, GLfloat t, GLfloat x, GLfloat y, GLfloat z); +GLAPI void APIENTRY glReplacementCodeuiTexCoord2fVertex3fvSUN (const GLuint *rc, const GLfloat *tc, const GLfloat *v); +GLAPI void APIENTRY glReplacementCodeuiTexCoord2fNormal3fVertex3fSUN (GLuint rc, GLfloat s, GLfloat t, GLfloat nx, GLfloat ny, GLfloat nz, GLfloat x, GLfloat y, GLfloat z); +GLAPI void APIENTRY glReplacementCodeuiTexCoord2fNormal3fVertex3fvSUN (const GLuint *rc, const GLfloat *tc, const GLfloat *n, const GLfloat *v); +GLAPI void APIENTRY glReplacementCodeuiTexCoord2fColor4fNormal3fVertex3fSUN (GLuint rc, GLfloat s, GLfloat t, GLfloat r, GLfloat g, GLfloat b, GLfloat a, GLfloat nx, GLfloat ny, GLfloat nz, GLfloat x, GLfloat y, GLfloat z); +GLAPI void APIENTRY glReplacementCodeuiTexCoord2fColor4fNormal3fVertex3fvSUN (const GLuint *rc, const GLfloat *tc, const GLfloat *c, const GLfloat *n, const GLfloat *v); +#endif +#endif /* GL_SUN_vertex */ + +#ifndef GL_WIN_phong_shading +#define GL_WIN_phong_shading 1 +#define GL_PHONG_WIN 0x80EA +#define GL_PHONG_HINT_WIN 0x80EB +#endif /* GL_WIN_phong_shading */ + +#ifndef GL_WIN_specular_fog +#define GL_WIN_specular_fog 1 +#define GL_FOG_SPECULAR_TEXTURE_WIN 0x80EC +#endif /* GL_WIN_specular_fog */ + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/impeller/third_party/stb/stb/tests/caveview/glext_list.h b/impeller/third_party/stb/stb/tests/caveview/glext_list.h new file mode 100644 index 0000000000000..5cdbca51f29ab --- /dev/null +++ b/impeller/third_party/stb/stb/tests/caveview/glext_list.h @@ -0,0 +1,34 @@ +GLARB(ActiveTexture,ACTIVETEXTURE) +GLARB(ClientActiveTexture,CLIENTACTIVETEXTURE) +GLARB(MultiTexCoord2f,MULTITEXCOORD2F) +GLEXT(TexImage3D,TEXIMAGE3D) +GLEXT(TexSubImage3D,TEXSUBIMAGE3D) +GLEXT(GenerateMipmap,GENERATEMIPMAP) +GLARB(DebugMessageCallback,DEBUGMESSAGECALLBACK) + +GLCORE(VertexAttribIPointer,VERTEXATTRIBIPOINTER) + +GLEXT(BindFramebuffer,BINDFRAMEBUFFER) +GLEXT(DeleteFramebuffers,DELETEFRAMEBUFFERS) +GLEXT(GenFramebuffers,GENFRAMEBUFFERS) +GLEXT(CheckFramebufferStatus,CHECKFRAMEBUFFERSTATUS) +GLEXT(FramebufferTexture2D,FRAMEBUFFERTEXTURE2D) +GLEXT(BindRenderBuffer,BINDRENDERBUFFER) +GLEXT(RenderbufferStorage,RENDERBUFFERSTORAGE) +GLEXT(GenRenderbuffers,GENRENDERBUFFERS) +GLEXT(BindRenderbuffer,BINDRENDERBUFFER) +GLEXT(FramebufferRenderbuffer,FRAMEBUFFERRENDERBUFFER) +GLEXT(GenerateMipmap,GENERATEMIPMAP) + +GLARB(BindBuffer ,BINDBUFFER,) +GLARB(GenBuffers ,GENBUFFERS ) +GLARB(DeleteBuffers,DELETEBUFFERS) +GLARB(BufferData ,BUFFERDATA ) +GLARB(BufferSubData,BUFFERSUBDATA) +GLARB(MapBuffer ,MAPBUFFER ) +GLARB(UnmapBuffer ,UNMAPBUFFER ) +GLARB(TexBuffer ,TEXBUFFER ) + +GLEXT(NamedBufferStorage,NAMEDBUFFERSTORAGE) +GLE(BufferStorage,BUFFERSTORAGE) +GLE(GetStringi,GETSTRINGI) \ No newline at end of file diff --git a/impeller/third_party/stb/stb/tests/caveview/main.c b/impeller/third_party/stb/stb/tests/caveview/main.c new file mode 100644 index 0000000000000..e69de29bb2d1d diff --git a/impeller/third_party/stb/stb/tests/caveview/stb_gl.h b/impeller/third_party/stb/stb/tests/caveview/stb_gl.h new file mode 100644 index 0000000000000..6498e28d8d9ea --- /dev/null +++ b/impeller/third_party/stb/stb/tests/caveview/stb_gl.h @@ -0,0 +1,1103 @@ +// stbgl - v0.04 - Sean Barrett 2008 - public domain +// +// Note that the gl extensions support requires glext.h. In fact, it works +// if you just concatenate glext.h onto the end of this file. In that case, +// this file is covered by the SGI FreeB license, and is not public domain. +// +// Extension usage: +// +// 1. Make a file called something like "extlist.txt" which contains stuff like: +// GLE(ShaderSourceARB,SHADERSOURCEARB) +// GLE(Uniform1iARB,UNIFORM1IARB) +// GLARB(ActiveTexture,ACTIVETEXTURE) // same as GLE(ActiveTextureARB,ACTIVETEXTUREARB) +// GLARB(ClientActiveTexture,CLIENTACTIVETEXTURE) +// GLE(MultiTexCoord2f,MULTITEXCOORD2F) +// +// 2. To declare functions (to make a header file), do this: +// #define STB_GLEXT_DECLARE "extlist.txt" +// #include "stb_gl.h" +// +// A good way to do this is to define STB_GLEXT_DECLARE project-wide. +// +// 3. To define functions (implement), do this in some C file: +// #define STB_GLEXT_DEFINE "extlist.txt" +// #include "stb_gl.h" +// +// If you've already defined STB_GLEXT_DECLARE, you can just do: +// #define STB_GLEXT_DEFINE_DECLARE +// #include "stb_gl.h" +// +// 4. Now you need to initialize: +// +// stbgl_initExtensions(); + + +#ifndef INCLUDE_STB_GL_H +#define INCLUDE_STB_GL_H + +#define STB_GL + +#ifdef _WIN32 +#ifndef WINGDIAPI +#define CALLBACK __stdcall +#define WINGDIAPI __declspec(dllimport) +#define APIENTRY __stdcall +#endif +#endif //_WIN32 + +#include + +#include +#include + +#ifndef M_PI +#define M_PI 3.14159265358979323846f +#endif + +#ifdef __cplusplus +extern "C" { +#endif + +// like gluPerspective, but: +// fov is chosen to satisfy both hfov <= max_hfov & vfov <= max_vfov; +// set one to 179 or 0 to ignore it +// zoom is applied separately, so you can do linear zoom without +// mucking with trig with fov; 1 -> use exact fov +// 'aspect' is inferred from the current viewport, and ignores the +// possibility of non-square pixels +extern void stbgl_Perspective(float zoom, float max_hfov, float max_vfov, float znear, float zfar); +extern void stbgl_PerspectiveViewport(int x, int y, int w, int h, float zoom, float max_hfov, float max_vfov, float znear, float zfar); +extern void stbgl_initCamera_zup_facing_x(void); +extern void stbgl_initCamera_zup_facing_y(void); +extern void stbgl_positionCameraWithEulerAngles(float *loc, float *ang); +extern void stbgl_drawRect(float x0, float y0, float x1, float y1); +extern void stbgl_drawRectTC(float x0, float y0, float x1, float y1, float s0, float t0, float s1, float t1); +extern void stbgl_drawBox(float x, float y, float z, float sx, float sy, float sz, int cw); + +extern int stbgl_hasExtension(char *ext); +extern void stbgl_SimpleLight(int index, float bright, float x, float y, float z); +extern void stbgl_GlobalAmbient(float r, float g, float b); + +extern int stbgl_LoadTexture(char *filename, char *props); // only if stb_image is available + +extern int stbgl_TestTexture(int w); +extern int stbgl_TestTextureEx(int w, char *scale_table, int checks_log2, int r1,int g1,int b1, int r2, int b2, int g2); +extern unsigned int stbgl_rand(void); // internal, but exposed just in case; LCG, so use middle bits + +extern int stbgl_TexImage2D(int texid, int w, int h, void *data, char *props); +extern int stbgl_TexImage2D_Extra(int texid, int w, int h, void *data, int chan, char *props, int preserve_data); +// "props" is a series of characters (and blocks of characters), a la fopen()'s mode, +// e.g.: +// GLuint texid = stbgl_LoadTexture("myfile.jpg", "mbc") +// means: load the image "myfile.jpg", and do the following: +// generate mipmaps +// use bilinear filtering (not trilinear) +// use clamp-to-edge on both channels +// +// input descriptor: AT MOST ONE +// TEXT MEANING +// 1 1 channel of input (intensity/alpha) +// 2 2 channels of input (luminance, alpha) +// 3 3 channels of input (RGB) +// 4 4 channels of input (RGBA) +// l 1 channel of input (luminance) +// a 1 channel of input (alpha) +// la 2 channels of input (lum/alpha) +// rgb 3 channels of input (RGB) +// ycocg 3 channels of input (YCoCg - forces YCoCg output) +// ycocgj 4 channels of input (YCoCgJunk - forces YCoCg output) +// rgba 4 channels of input (RGBA) +// +// output descriptor: AT MOST ONE +// TEXT MEANING +// A 1 channel of output (alpha) +// I 1 channel of output (intensity) +// LA 2 channels of output (lum/alpha) +// RGB 3 channels of output (RGB) +// RGBA 4 channels of output (RGBA) +// DXT1 encode as a DXT1 texture (RGB unless input has RGBA) +// DXT3 encode as a DXT3 texture +// DXT5 encode as a DXT5 texture +// YCoCg encode as a DXT5 texture with Y in alpha, CoCg in RG +// D GL_DEPTH_COMPONENT +// NONE no input/output, don't call TexImage2D at all +// +// when reading from a file or using another interface with an explicit +// channel count, the input descriptor is ignored and instead the channel +// count is used as the input descriptor. if the file read is a DXT DDS, +// then it is passed directly to OpenGL in the file format. +// +// if an input descriptor is supplied but no output descriptor, the output +// is assumed to be the same as the input. if an output descriptor is supplied +// but no input descriptor, the input is assumed to be the same as the +// output. if neither is supplied, the input is assumed to be 4-channel. +// If DXT1 or YCoCG output is requested with no input, the input is assumed +// to be 4-channel but the alpha channel is ignored. +// +// filtering descriptor (default is no mipmaps) +// TEXT MEANING +// m generate mipmaps +// M mipmaps are provided, concatenated at end of data (from largest to smallest) +// t use trilinear filtering (default if mipmapped) +// b use bilinear filtering (default if not-mipmapped) +// n use nearest-neighbor sampling +// +// wrapping descriptor +// TEXT MEANING +// w wrap (default) +// c clamp-to-edge +// C GL_CLAMP (uses border color) +// +// If only one wrapping descriptor is supplied, it is applied to both channels. +// +// special: +// TEXT MEANING +// f input data is floats (default unsigned bytes) +// F input&output data is floats (default unsigned bytes) +// p explicitly pre-multiply the alpha +// P pad to power-of-two (default stretches) +// NP2 non-power-of-two +// + can overwrite the texture data with temp data +// ! free the texture data with "free" +// +// the properties string can also include spaces + +#ifdef __cplusplus +} +#endif + + +#ifdef STB_GL_IMPLEMENTATION +#include +#include +#include +#include + +int stbgl_hasExtension(char *ext) +{ + const char *s = glGetString(GL_EXTENSIONS); + for(;;) { + char *e = ext; + for (;;) { + if (*e == 0) { + if (*s == 0 || *s == ' ') return 1; + break; + } + if (*s != *e) + break; + ++s, ++e; + } + while (*s && *s != ' ') ++s; + if (!*s) return 0; + ++s; // skip space + } +} + +void stbgl_drawRect(float x0, float y0, float x1, float y1) +{ + glBegin(GL_POLYGON); + glTexCoord2f(0,0); glVertex2f(x0,y0); + glTexCoord2f(1,0); glVertex2f(x1,y0); + glTexCoord2f(1,1); glVertex2f(x1,y1); + glTexCoord2f(0,1); glVertex2f(x0,y1); + glEnd(); +} + +void stbgl_drawRectTC(float x0, float y0, float x1, float y1, float s0, float t0, float s1, float t1) +{ + glBegin(GL_POLYGON); + glTexCoord2f(s0,t0); glVertex2f(x0,y0); + glTexCoord2f(s1,t0); glVertex2f(x1,y0); + glTexCoord2f(s1,t1); glVertex2f(x1,y1); + glTexCoord2f(s0,t1); glVertex2f(x0,y1); + glEnd(); +} + +void stbgl_drawBox(float x, float y, float z, float sx, float sy, float sz, int cw) +{ + float x0,y0,z0,x1,y1,z1; + sx /=2, sy/=2, sz/=2; + x0 = x-sx; y0 = y-sy; z0 = z-sz; + x1 = x+sx; y1 = y+sy; z1 = z+sz; + + glBegin(GL_QUADS); + if (cw) { + glNormal3f(0,0,-1); + glTexCoord2f(0,0); glVertex3f(x0,y0,z0); + glTexCoord2f(1,0); glVertex3f(x1,y0,z0); + glTexCoord2f(1,1); glVertex3f(x1,y1,z0); + glTexCoord2f(0,1); glVertex3f(x0,y1,z0); + + glNormal3f(0,0,1); + glTexCoord2f(0,0); glVertex3f(x1,y0,z1); + glTexCoord2f(1,0); glVertex3f(x0,y0,z1); + glTexCoord2f(1,1); glVertex3f(x0,y1,z1); + glTexCoord2f(0,1); glVertex3f(x1,y1,z1); + + glNormal3f(-1,0,0); + glTexCoord2f(0,0); glVertex3f(x0,y1,z1); + glTexCoord2f(1,0); glVertex3f(x0,y0,z1); + glTexCoord2f(1,1); glVertex3f(x0,y0,z0); + glTexCoord2f(0,1); glVertex3f(x0,y1,z0); + + glNormal3f(1,0,0); + glTexCoord2f(0,0); glVertex3f(x1,y0,z1); + glTexCoord2f(1,0); glVertex3f(x1,y1,z1); + glTexCoord2f(1,1); glVertex3f(x1,y1,z0); + glTexCoord2f(0,1); glVertex3f(x1,y0,z0); + + glNormal3f(0,-1,0); + glTexCoord2f(0,0); glVertex3f(x0,y0,z1); + glTexCoord2f(1,0); glVertex3f(x1,y0,z1); + glTexCoord2f(1,1); glVertex3f(x1,y0,z0); + glTexCoord2f(0,1); glVertex3f(x0,y0,z0); + + glNormal3f(0,1,0); + glTexCoord2f(0,0); glVertex3f(x1,y1,z1); + glTexCoord2f(1,0); glVertex3f(x0,y1,z1); + glTexCoord2f(1,1); glVertex3f(x0,y1,z0); + glTexCoord2f(0,1); glVertex3f(x1,y1,z0); + } else { + glNormal3f(0,0,-1); + glTexCoord2f(0,0); glVertex3f(x0,y0,z0); + glTexCoord2f(0,1); glVertex3f(x0,y1,z0); + glTexCoord2f(1,1); glVertex3f(x1,y1,z0); + glTexCoord2f(1,0); glVertex3f(x1,y0,z0); + + glNormal3f(0,0,1); + glTexCoord2f(0,0); glVertex3f(x1,y0,z1); + glTexCoord2f(0,1); glVertex3f(x1,y1,z1); + glTexCoord2f(1,1); glVertex3f(x0,y1,z1); + glTexCoord2f(1,0); glVertex3f(x0,y0,z1); + + glNormal3f(-1,0,0); + glTexCoord2f(0,0); glVertex3f(x0,y1,z1); + glTexCoord2f(0,1); glVertex3f(x0,y1,z0); + glTexCoord2f(1,1); glVertex3f(x0,y0,z0); + glTexCoord2f(1,0); glVertex3f(x0,y0,z1); + + glNormal3f(1,0,0); + glTexCoord2f(0,0); glVertex3f(x1,y0,z1); + glTexCoord2f(0,1); glVertex3f(x1,y0,z0); + glTexCoord2f(1,1); glVertex3f(x1,y1,z0); + glTexCoord2f(1,0); glVertex3f(x1,y1,z1); + + glNormal3f(0,-1,0); + glTexCoord2f(0,0); glVertex3f(x0,y0,z1); + glTexCoord2f(0,1); glVertex3f(x0,y0,z0); + glTexCoord2f(1,1); glVertex3f(x1,y0,z0); + glTexCoord2f(1,0); glVertex3f(x1,y0,z1); + + glNormal3f(0,1,0); + glTexCoord2f(0,0); glVertex3f(x1,y1,z1); + glTexCoord2f(0,1); glVertex3f(x1,y1,z0); + glTexCoord2f(1,1); glVertex3f(x0,y1,z0); + glTexCoord2f(1,0); glVertex3f(x0,y1,z1); + } + glEnd(); +} + +void stbgl_SimpleLight(int index, float bright, float x, float y, float z) +{ + float d = (float) (1.0f/sqrt(x*x+y*y+z*z)); + float dir[4] = { x*d,y*d,z*d,0 }, zero[4] = { 0,0,0,0 }; + float c[4] = { bright,bright,bright,0 }; + GLuint light = GL_LIGHT0 + index; + glLightfv(light, GL_POSITION, dir); + glLightfv(light, GL_DIFFUSE, c); + glLightfv(light, GL_AMBIENT, zero); + glLightfv(light, GL_SPECULAR, zero); + glEnable(light); + glColorMaterial(GL_FRONT, GL_AMBIENT_AND_DIFFUSE); + glEnable(GL_COLOR_MATERIAL); +} + +void stbgl_GlobalAmbient(float r, float g, float b) +{ + float v[4] = { r,g,b,0 }; + glLightModelfv(GL_LIGHT_MODEL_AMBIENT, v); +} + + +#define stbgl_rad2deg(r) ((r)*180.0f / M_PI) +#define stbgl_deg2rad(r) ((r)/180.0f * M_PI) + +void stbgl_Perspective(float zoom, float max_hfov, float max_vfov, float znear, float zfar) +{ + float unit_width, unit_height, aspect, vfov; + int data[4],w,h; + glGetIntegerv(GL_VIEWPORT, data); + w = data[2]; + h = data[3]; + aspect = (float) w / h; + + if (max_hfov <= 0) max_hfov = 179; + if (max_vfov <= 0) max_vfov = 179; + + // convert max_hfov, max_vfov to worldspace width at depth=1 + unit_width = (float) tan(stbgl_deg2rad(max_hfov/2)) * 2; + unit_height = (float) tan(stbgl_deg2rad(max_vfov/2)) * 2; + // check if hfov = max_hfov is enough to satisfy it + if (unit_width <= aspect * unit_height) { + float height = unit_width / aspect; + vfov = (float) atan(( height/2) / zoom); + } else { + vfov = (float) atan((unit_height/2) / zoom); + } + vfov = (float) stbgl_rad2deg(vfov * 2); + gluPerspective(vfov, aspect, znear, zfar); +} + +void stbgl_PerspectiveViewport(int x, int y, int w, int h, float zoom, float min_hfov, float min_vfov, float znear, float zfar) +{ + if (znear <= 0.0001f) znear = 0.0001f; + glViewport(x,y,w,h); + glScissor(x,y,w,h); + glMatrixMode(GL_PROJECTION); + glLoadIdentity(); + stbgl_Perspective(zoom, min_hfov, min_vfov, znear, zfar); + glMatrixMode(GL_MODELVIEW); +} + +// point the camera along the positive X axis, Z-up +void stbgl_initCamera_zup_facing_x(void) +{ + glRotatef(-90, 1,0,0); + glRotatef( 90, 0,0,1); +} + +// point the camera along the positive Y axis, Z-up +void stbgl_initCamera_zup_facing_y(void) +{ + glRotatef(-90, 1,0,0); +} + +// setup a camera using Euler angles +void stbgl_positionCameraWithEulerAngles(float *loc, float *ang) +{ + glRotatef(-ang[1], 0,1,0); + glRotatef(-ang[0], 1,0,0); + glRotatef(-ang[2], 0,0,1); + glTranslatef(-loc[0], -loc[1], -loc[2]); +} + +static int stbgl_m(char *a, char *b) +{ + // skip first character + do { ++a,++b; } while (*b && *a == *b); + return *b == 0; +} + +#ifdef STBI_VERSION +#ifndef STBI_NO_STDIO +int stbgl_LoadTexture(char *filename, char *props) +{ + // @TODO: handle DDS files directly + int res; + void *data; + int w,h,c; + #ifndef STBI_NO_HDR + if (stbi_is_hdr(filename)) { + data = stbi_loadf(filename, &w, &h, &c, 0); + if (!data) return 0; + res = stbgl_TexImage2D_Extra(0, w,h,data, -c, props, 0); + free(data); + return res; + } + #endif + + data = stbi_load(filename, &w, &h, &c, 0); + if (!data) return 0; + res = stbgl_TexImage2D_Extra(0, w,h,data, c, props, 0); + free(data); + return res; +} +#endif +#endif // STBI_VERSION + +int stbgl_TexImage2D(int texid, int w, int h, void *data, char *props) +{ + return stbgl_TexImage2D_Extra(texid, w, h, data, 0, props,1); +} + +int stbgl_TestTexture(int w) +{ + char scale_table[] = { 10,20,30,30,35,40,5,18,25,13,7,5,3,3,2,2,2,2,1,1,1,1,1,0,0,0,0,0,0,0,0,0,0 }; + return stbgl_TestTextureEx(w, scale_table, 2, 140,130,200, 180,200,170); +} + +unsigned int stbgl_rand(void) +{ + static unsigned int stbgl__rand_seed = 3248980923; // random typing + return stbgl__rand_seed = stbgl__rand_seed * 2147001325 + 715136305; // BCPL generator +} + +// wish this could be smaller, since it's so frivolous +int stbgl_TestTextureEx(int w, char *scale_table, int checks_log2, int r1,int g1,int b1, int r2, int b2, int g2) +{ + int rt[2] = {r1,r2}, gt[2] = {g1,g2}, bt[2] = {b1,b2}; + signed char modded[256]; + int i,j, m = w-1, s,k,scale; + unsigned char *data = (unsigned char *) malloc(w*w*3); + assert((m & w) == 0); + data[0] = 128; + for (s=0; s < 16; ++s) if ((1 << s) == w) break; + assert(w == (1 << s)); + // plasma fractal noise + for (k=s-1; k >= 0; --k) { + int step = 1 << k; + // interpolate from "parents" + for (j=0; j < w; j += step*2) { + for (i=0; i < w; i += step*2) { + int i1 = i+step, j1=j+step; + int i2 = (i+step*2)&m, j2 = (j+step*2)&m; + int p00 = data[(j*w+i )*3], p01 = data[(j2*w+i )*3]; + int p10 = data[(j*w+i2)*3], p11 = data[(j2*w+i2)*3]; + data[(j*w+i1)*3] = (p00+p10)>>1; + data[(j1*w+i)*3] = (p00+p01)>>1; + data[(j1*w+i1)*3]= (p00+p01+p10+p11)>>2; + } + } + scale = scale_table[s-k+1]; + if (!scale) continue; // just interpolate down the remaining data + for (j=0,i=0; i < 256; i += 2, j == scale ? j=0 : ++j) + modded[i] = j, modded[i+1] = -j; // precompute i%scale (plus sign) + for (j=0; j < w; j += step) + for (i=0; i < w; i += step) { + int x = data[(j*w+i)*3] + modded[(stbgl_rand() >> 12) & 255]; + data[(j*w+i)*3] = x < 0 ? 0 : x > 255 ? 255 : x; + } + } + for (j=0; j < w; ++j) + for (i=0; i < w; ++i) { + int check = ((i^j) & (1 << (s-checks_log2))) == 0; + int v = data[(j*w+i)*3] >> 2; + data[(j*w+i)*3+0] = rt[check]-v; + data[(j*w+i)*3+1] = gt[check]-v; + data[(j*w+i)*3+2] = bt[check]-v; + } + return stbgl_TexImage2D(0, w, w, data, "3m!"); // 3 channels, mipmap, free +} + +#ifdef _WIN32 +#ifndef WINGDIAPI +typedef int (__stdcall *stbgl__voidfunc)(void); +__declspec(dllimport) stbgl__voidfunc wglGetProcAddress(char *); +#endif +#define STB__HAS_WGLPROC +static void (__stdcall *stbgl__CompressedTexImage2DARB)(int target, int level, + int internalformat, int width, + int height, int border, + int imageSize, void *data); +static void stbgl__initCompTex(void) +{ + *((void **) &stbgl__CompressedTexImage2DARB) = (void *) wglGetProcAddress("glCompressedTexImage2DARB"); +} +#else +static void (*stbgl__CompressedTexImage2DARB)(int target, int level, + int internalformat, int width, + int height, int border, + int imageSize, void *data); +static void stbgl__initCompTex(void) +{ +} +#endif // _WIN32 + +#define STBGL_COMPRESSED_RGB_S3TC_DXT1 0x83F0 +#define STBGL_COMPRESSED_RGBA_S3TC_DXT1 0x83F1 +#define STBGL_COMPRESSED_RGBA_S3TC_DXT3 0x83F2 +#define STBGL_COMPRESSED_RGBA_S3TC_DXT5 0x83F3 + +#ifdef STB_COMPRESS_DXT_BLOCK +static void stbgl__convert(uint8 *p, uint8 *q, int n, int input_desc, uint8 *end) +{ + int i; + switch (input_desc) { + case GL_RED: + case GL_LUMINANCE: for (i=0; i < n; ++i,p+=4) p[0] = p[1] = p[2] = q[0], p[3]=255, q+=1; break; + case GL_ALPHA: for (i=0; i < n; ++i,p+=4) p[0] = p[1] = p[2] = 0, p[3] = q[0], q+=1; break; + case GL_LUMINANCE_ALPHA: for (i=0; i < n; ++i,p+=4) p[0] = p[1] = p[2] = q[0], p[3]=q[1], q+=2; break; + case GL_RGB: for (i=0; i < n; ++i,p+=4) p[0]=q[0],p[1]=q[1],p[2]=q[2],p[3]=255,q+=3; break; + case GL_RGBA: memcpy(p, q, n*4); break; + case GL_INTENSITY: for (i=0; i < n; ++i,p+=4) p[0] = p[1] = p[2] = p[3] = q[0], q+=1; break; + } + assert(p <= end); +} + +static void stbgl__compress(uint8 *p, uint8 *rgba, int w, int h, int output_desc, uint8 *end) +{ + int i,j,y,y2; + int alpha = (output_desc == STBGL_COMPRESSED_RGBA_S3TC_DXT5); + for (j=0; j < w; j += 4) { + int x=4; + for (i=0; i < h; i += 4) { + uint8 block[16*4]; + if (i+3 >= w) x = w-i; + for (y=0; y < 4; ++y) { + if (j+y >= h) break; + memcpy(block+y*16, rgba + w*4*(j+y) + i*4, x*4); + } + if (x < 4) { + switch (x) { + case 0: assert(0); + case 1: + for (y2=0; y2 < y; ++y2) { + memcpy(block+y2*16+1*4, block+y2*16+0*4, 4); + memcpy(block+y2*16+2*4, block+y2*16+0*4, 8); + } + break; + case 2: + for (y2=0; y2 < y; ++y2) + memcpy(block+y2*16+2*4, block+y2*16+0*4, 8); + break; + case 3: + for (y2=0; y2 < y; ++y2) + memcpy(block+y2*16+3*4, block+y2*16+1*4, 4); + break; + } + } + y2 = 0; + for(; y<4; ++y,++y2) + memcpy(block+y*16, block+y2*16, 4*4); + stb_compress_dxt_block(p, block, alpha, 10); + p += alpha ? 16 : 8; + } + } + assert(p <= end); +} +#endif // STB_COMPRESS_DXT_BLOCK + +// use the reserved temporary-use enumerant range, since no +// OpenGL enumerants should fall in that range +enum +{ + STBGL_UNDEFINED = 0x6000, + STBGL_YCOCG, + STBGL_YCOCGJ, + STBGL_GEN_MIPMAPS, + STBGL_MIPMAPS, + STBGL_NO_DOWNLOAD, +}; + +#define STBGL_CLAMP_TO_EDGE 0x812F +#define STBGL_CLAMP_TO_BORDER 0x812D + +#define STBGL_DEPTH_COMPONENT16 0x81A5 +#define STBGL_DEPTH_COMPONENT24 0x81A6 +#define STBGL_DEPTH_COMPONENT32 0x81A7 + +int stbgl_TexImage2D_Extra(int texid, int w, int h, void *data, int chan, char *props, int preserve_data) +{ + static int has_s3tc = -1; // haven't checked yet + int free_data = 0, is_compressed = 0; + int pad_to_power_of_two = 0, non_power_of_two = 0; + int premultiply_alpha = 0; // @TODO + int float_tex = 0; // @TODO + int input_type = GL_UNSIGNED_BYTE; + int input_desc = STBGL_UNDEFINED; + int output_desc = STBGL_UNDEFINED; + int mipmaps = STBGL_UNDEFINED; + int filter = STBGL_UNDEFINED, mag_filter; + int wrap_s = STBGL_UNDEFINED, wrap_t = STBGL_UNDEFINED; + + // parse out the properties + if (props == NULL) props = ""; + while (*props) { + switch (*props) { + case '1' : input_desc = GL_LUMINANCE; break; + case '2' : input_desc = GL_LUMINANCE_ALPHA; break; + case '3' : input_desc = GL_RGB; break; + case '4' : input_desc = GL_RGBA; break; + case 'l' : if (props[1] == 'a') { input_desc = GL_LUMINANCE_ALPHA; ++props; } + else input_desc = GL_LUMINANCE; + break; + case 'a' : input_desc = GL_ALPHA; break; + case 'r' : if (stbgl_m(props, "rgba")) { input_desc = GL_RGBA; props += 3; break; } + if (stbgl_m(props, "rgb")) { input_desc = GL_RGB; props += 2; break; } + input_desc = GL_RED; + break; + case 'y' : if (stbgl_m(props, "ycocg")) { + if (props[5] == 'j') { props += 5; input_desc = STBGL_YCOCGJ; } + else { props += 4; input_desc = STBGL_YCOCG; } + break; + } + return 0; + case 'L' : if (props[1] == 'A') { output_desc = GL_LUMINANCE_ALPHA; ++props; } + else output_desc = GL_LUMINANCE; + break; + case 'I' : output_desc = GL_INTENSITY; break; + case 'A' : output_desc = GL_ALPHA; break; + case 'R' : if (stbgl_m(props, "RGBA")) { output_desc = GL_RGBA; props += 3; break; } + if (stbgl_m(props, "RGB")) { output_desc = GL_RGB; props += 2; break; } + output_desc = GL_RED; + break; + case 'Y' : if (stbgl_m(props, "YCoCg") || stbgl_m(props, "YCOCG")) { + props += 4; + output_desc = STBGL_YCOCG; + break; + } + return 0; + case 'D' : if (stbgl_m(props, "DXT")) { + switch (props[3]) { + case '1': output_desc = STBGL_COMPRESSED_RGB_S3TC_DXT1; break; + case '3': output_desc = STBGL_COMPRESSED_RGBA_S3TC_DXT3; break; + case '5': output_desc = STBGL_COMPRESSED_RGBA_S3TC_DXT5; break; + default: return 0; + } + props += 3; + } else if (stbgl_m(props, "D16")) { + output_desc = STBGL_DEPTH_COMPONENT16; + input_desc = GL_DEPTH_COMPONENT; + props += 2; + } else if (stbgl_m(props, "D24")) { + output_desc = STBGL_DEPTH_COMPONENT24; + input_desc = GL_DEPTH_COMPONENT; + props += 2; + } else if (stbgl_m(props, "D32")) { + output_desc = STBGL_DEPTH_COMPONENT32; + input_desc = GL_DEPTH_COMPONENT; + props += 2; + } else { + output_desc = GL_DEPTH_COMPONENT; + input_desc = GL_DEPTH_COMPONENT; + } + break; + case 'N' : if (stbgl_m(props, "NONE")) { + props += 3; + input_desc = STBGL_NO_DOWNLOAD; + output_desc = STBGL_NO_DOWNLOAD; + break; + } + if (stbgl_m(props, "NP2")) { + non_power_of_two = 1; + props += 2; + break; + } + return 0; + case 'm' : mipmaps = STBGL_GEN_MIPMAPS; break; + case 'M' : mipmaps = STBGL_MIPMAPS; break; + case 't' : filter = GL_LINEAR_MIPMAP_LINEAR; break; + case 'b' : filter = GL_LINEAR; break; + case 'n' : filter = GL_NEAREST; break; + case 'w' : if (wrap_s == STBGL_UNDEFINED) wrap_s = GL_REPEAT; else wrap_t = GL_REPEAT; break; + case 'C' : if (wrap_s == STBGL_UNDEFINED) wrap_s = STBGL_CLAMP_TO_BORDER; else wrap_t = STBGL_CLAMP_TO_BORDER; break; + case 'c' : if (wrap_s == STBGL_UNDEFINED) wrap_s = STBGL_CLAMP_TO_EDGE; else wrap_t = STBGL_CLAMP_TO_EDGE; break; + case 'f' : input_type = GL_FLOAT; break; + case 'F' : input_type = GL_FLOAT; float_tex = 1; break; + case 'p' : premultiply_alpha = 1; break; + case 'P' : pad_to_power_of_two = 1; break; + case '+' : preserve_data = 0; break; + case '!' : preserve_data = 0; free_data = 1; break; + case ' ' : break; + case '-' : break; + default : if (free_data) free(data); + return 0; + } + ++props; + } + + // override input_desc based on channel count + if (output_desc != STBGL_NO_DOWNLOAD) { + switch (abs(chan)) { + case 1: input_desc = GL_LUMINANCE; break; + case 2: input_desc = GL_LUMINANCE_ALPHA; break; + case 3: input_desc = GL_RGB; break; + case 4: input_desc = GL_RGBA; break; + case 0: break; + default: return 0; + } + } + + // override input_desc based on channel info + if (chan > 0) { input_type = GL_UNSIGNED_BYTE; } + if (chan < 0) { input_type = GL_FLOAT; } + + if (output_desc == GL_ALPHA) { + if (input_desc == GL_LUMINANCE) + input_desc = GL_ALPHA; + if (input_desc == GL_RGB) { + // force a presumably-mono image to alpha + // @TODO handle 'preserve_data' case? + if (data && !preserve_data && input_type == GL_UNSIGNED_BYTE) { + int i; + unsigned char *p = (unsigned char *) data, *q = p; + for (i=0; i < w*h; ++i) { + *q = (p[0] + 2*p[1] + p[2]) >> 2; + p += 3; + q += 1; + } + input_desc = GL_ALPHA; + } + } + } + + // set undefined input/output based on the other + if (input_desc == STBGL_UNDEFINED && output_desc == STBGL_UNDEFINED) { + input_desc = output_desc = GL_RGBA; + } else if (output_desc == STBGL_UNDEFINED) { + switch (input_desc) { + case GL_LUMINANCE: + case GL_ALPHA: + case GL_LUMINANCE_ALPHA: + case GL_RGB: + case GL_RGBA: + output_desc = input_desc; + break; + case GL_RED: + output_desc = GL_INTENSITY; + break; + case STBGL_YCOCG: + case STBGL_YCOCGJ: + output_desc = STBGL_YCOCG; + break; + default: assert(0); return 0; + } + } else if (input_desc == STBGL_UNDEFINED) { + switch (output_desc) { + case GL_LUMINANCE: + case GL_ALPHA: + case GL_LUMINANCE_ALPHA: + case GL_RGB: + case GL_RGBA: + input_desc = output_desc; + break; + case GL_INTENSITY: + input_desc = GL_RED; + break; + case STBGL_YCOCG: + case STBGL_COMPRESSED_RGB_S3TC_DXT1: + case STBGL_COMPRESSED_RGBA_S3TC_DXT3: + case STBGL_COMPRESSED_RGBA_S3TC_DXT5: + input_desc = GL_RGBA; + break; + } + } else { + if (output_desc == STBGL_COMPRESSED_RGB_S3TC_DXT1) { + // if input has alpha, force output alpha + switch (input_desc) { + case GL_ALPHA: + case GL_LUMINANCE_ALPHA: + case GL_RGBA: + output_desc = STBGL_COMPRESSED_RGBA_S3TC_DXT5; + break; + } + } + } + + switch(input_desc) { + case GL_LUMINANCE: + case GL_RED: + case GL_ALPHA: + chan = 1; + break; + case GL_LUMINANCE_ALPHA: + chan = 2; + break; + case GL_RGB: + chan = 3; + break; + case GL_RGBA: + chan = 4; + break; + } + + if (pad_to_power_of_two && ((w & (w-1)) || (h & (h-1)))) { + if (output_desc != STBGL_NO_DOWNLOAD && input_type == GL_UNSIGNED_BYTE && chan > 0) { + unsigned char *new_data; + int w2 = w, h2 = h, j; + while (w & (w-1)) + w = (w | (w>>1))+1; + while (h & (h-1)) + h = (h | (h>>1))+1; + new_data = malloc(w * h * chan); + for (j=0; j < h2; ++j) { + memcpy(new_data + j * w * chan, (char *) data+j*w2*chan, w2*chan); + memset(new_data + (j * w+w2) * chan, 0, (w-w2)*chan); + } + for (; j < h; ++j) + memset(new_data + j*w*chan, 0, w*chan); + if (free_data) + free(data); + data = new_data; + free_data = 1; + } + } + + switch (output_desc) { + case STBGL_COMPRESSED_RGB_S3TC_DXT1: + case STBGL_COMPRESSED_RGBA_S3TC_DXT1: + case STBGL_COMPRESSED_RGBA_S3TC_DXT3: + case STBGL_COMPRESSED_RGBA_S3TC_DXT5: + is_compressed = 1; + if (has_s3tc == -1) { + has_s3tc = stbgl_hasExtension("GL_EXT_texture_compression_s3tc"); + if (has_s3tc) stbgl__initCompTex(); + } + if (!has_s3tc) { + is_compressed = 0; + if (output_desc == STBGL_COMPRESSED_RGB_S3TC_DXT1) + output_desc = GL_RGB; + else + output_desc = GL_RGBA; + } + } + + if (output_desc == STBGL_YCOCG) { + assert(0); + output_desc = GL_RGB; // @TODO! + if (free_data) free(data); + return 0; + } + + mag_filter = 0; + if (mipmaps != STBGL_UNDEFINED) { + switch (filter) { + case STBGL_UNDEFINED: filter = GL_LINEAR_MIPMAP_LINEAR; break; + case GL_NEAREST : mag_filter = GL_NEAREST; filter = GL_LINEAR_MIPMAP_LINEAR; break; + case GL_LINEAR : filter = GL_LINEAR_MIPMAP_NEAREST; break; + } + } else { + if (filter == STBGL_UNDEFINED) + filter = GL_LINEAR; + } + + // update filtering + if (!mag_filter) { + if (filter == GL_NEAREST) + mag_filter = GL_NEAREST; + else + mag_filter = GL_LINEAR; + } + + // update wrap/clamp + if (wrap_s == STBGL_UNDEFINED) wrap_s = GL_REPEAT; + if (wrap_t == STBGL_UNDEFINED) wrap_t = wrap_s; + + // if no texture id, generate one + if (texid == 0) { + GLuint tex; + glGenTextures(1, &tex); + if (tex == 0) { if (free_data) free(data); return 0; } + texid = tex; + } + + if (data == NULL && mipmaps == STBGL_GEN_MIPMAPS) + mipmaps = STBGL_MIPMAPS; + + if (output_desc == STBGL_NO_DOWNLOAD) + mipmaps = STBGL_NO_DOWNLOAD; + + glBindTexture(GL_TEXTURE_2D, texid); + +#ifdef STB_COMPRESS_DXT_BLOCK + if (!is_compressed || !stbgl__CompressedTexImage2DARB || output_desc == STBGL_COMPRESSED_RGBA_S3TC_DXT3 || data == NULL) +#endif + { + switch (mipmaps) { + case STBGL_NO_DOWNLOAD: + break; + + case STBGL_UNDEFINED: + // check if actually power-of-two + if (non_power_of_two || ((w & (w-1)) == 0 && (h & (h-1)) == 0)) + glTexImage2D(GL_TEXTURE_2D, 0, output_desc, w, h, 0, input_desc, input_type, data); + else + gluBuild2DMipmaps(GL_TEXTURE_2D, output_desc, w, h, input_desc, input_type, data); + // not power of two, so use glu to resize (generates mipmaps needlessly) + break; + + case STBGL_MIPMAPS: { + int level = 0; + int size = input_type == GL_FLOAT ? sizeof(float) : 1; + if (data == NULL) size = 0; // reuse same block of memory for all mipmaps + assert((w & (w-1)) == 0 && (h & (h-1)) == 0); // verify power-of-two + while (w > 1 && h > 1) { + glTexImage2D(GL_TEXTURE_2D, level, output_desc, w, h, 0, input_desc, input_type, data); + data = (void *) ((char *) data + w * h * size * chan); + if (w > 1) w >>= 1; + if (h > 1) h >>= 1; + ++level; + } + break; + } + case STBGL_GEN_MIPMAPS: + gluBuild2DMipmaps(GL_TEXTURE_2D, output_desc, w, h, input_desc, input_type, data); + break; + + default: + assert(0); + if (free_data) free(data); + return 0; + } +#ifdef STB_COMPRESS_DXT_BLOCK + } else { + uint8 *out, *rgba=0, *end_out, *end_rgba; + int level = 0, alpha = (output_desc != STBGL_COMPRESSED_RGB_S3TC_DXT1); + int size = input_type == GL_FLOAT ? sizeof(float) : 1; + int osize = alpha ? 16 : 8; + if (!free_data && mipmaps == STBGL_GEN_MIPMAPS) { + uint8 *temp = malloc(w*h*chan); + if (!temp) { if (free_data) free(data); return 0; } + memcpy(temp, data, w*h*chan); + if (free_data) free(data); + free_data = 1; + data = temp; + } + if (chan != 4 || size != 1) { + rgba = malloc(w*h*4); + if (!rgba) return 0; + end_rgba = rgba+w*h*4; + } + out = malloc((w+3)*(h+3)/16*osize); // enough storage for the s3tc data + if (!out) return 0; + end_out = out + ((w+3)*(h+3))/16*osize; + + for(;;) { + if (chan != 4) + stbgl__convert(rgba, data, w*h, input_desc, end_rgba); + stbgl__compress(out, rgba ? rgba : data, w, h, output_desc, end_out); + stbgl__CompressedTexImage2DARB(GL_TEXTURE_2D, level, output_desc, w, h, 0, ((w+3)&~3)*((h+3)&~3)/16*osize, out); + //glTexImage2D(GL_TEXTURE_2D, level, alpha?GL_RGBA:GL_RGB, w, h, 0, GL_RGBA, GL_UNSIGNED_BYTE, rgba ? rgba : data); + + if (mipmaps == STBGL_UNDEFINED) break; + if (w <= 1 && h <= 1) break; + if (mipmaps == STBGL_MIPMAPS) data = (void *) ((char *) data + w * h * size * chan); + if (mipmaps == STBGL_GEN_MIPMAPS) { + int w2 = w>>1, h2=h>>1, i,j,k, s=w*chan; + uint8 *p = data, *q=data; + if (w == 1) { + for (j=0; j < h2; ++j) { + for (k=0; k < chan; ++k) + *p++ = (q[k] + q[s+k] + 1) >> 1; + q += s*2; + } + } else if (h == 1) { + for (i=0; i < w2; ++i) { + for (k=0; k < chan; ++k) + *p++ = (q[k] + q[k+chan] + 1) >> 1; + q += chan*2; + } + } else { + for (j=0; j < h2; ++j) { + for (i=0; i < w2; ++i) { + for (k=0; k < chan; ++k) + *p++ = (q[k] + q[k+chan] + q[s+k] + q[s+k+chan] + 2) >> 2; + q += chan*2; + } + q += s; + } + } + } + if (w > 1) w >>= 1; + if (h > 1) h >>= 1; + ++level; + } + if (out) free(out); + if (rgba) free(rgba); +#endif // STB_COMPRESS_DXT_BLOCK + } + + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, wrap_s); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, wrap_t); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, mag_filter); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, filter); + + if (free_data) free(data); + return texid; +} + +#endif // STB_DEFINE +#undef STB_EXTERN + +#endif //INCLUDE_STB_GL_H + +// Extension handling... must be outside the INCLUDE_ brackets + +#if defined(STB_GLEXT_DEFINE) || defined(STB_GLEXT_DECLARE) + +#ifndef STB_GLEXT_SKIP_DURING_RECURSION + +#ifndef GL_GLEXT_VERSION + + // First check if glext.h is concatenated on the end of this file + // (if it's concatenated on the beginning, we'll have GL_GLEXT_VERSION) + + #define STB_GLEXT_SKIP_DURING_RECURSION + #include __FILE__ + #undef STB_GLEXT_SKIP_DURING_RECURSION + + // now check if it's still undefined; if so, try going for it by name; + // if this errors, that's fine, since we can't compile without it + + #ifndef GL_GLEXT_VERSION + #include "glext.h" + #endif +#endif + +#define GLARB(a,b) GLE(a##ARB,b##ARB) +#define GLEXT(a,b) GLE(a##EXT,b##EXT) +#define GLNV(a,b) GLE(a##NV ,b##NV) +#define GLATI(a,b) GLE(a##ATI,b##ATI) +#define GLCORE(a,b) GLE(a,b) + +#ifdef STB_GLEXT_DEFINE_DECLARE +#define STB_GLEXT_DEFINE STB_GLEXT_DECLARE +#endif + +#if defined(STB_GLEXT_DECLARE) && defined(STB_GLEXT_DEFINE) +#undef STB_GLEXT_DECLARE +#endif + +#if defined(STB_GLEXT_DECLARE) && !defined(STB_GLEXT_DEFINE) + #define GLE(a,b) extern PFNGL##b##PROC gl##a; + + #ifdef __cplusplus + extern "C" { + #endif + + extern void stbgl_initExtensions(void); + + #include STB_GLEXT_DECLARE + + #ifdef __cplusplus + }; + #endif + +#else + + #ifndef STB_GLEXT_DEFINE + #error "Header file is screwed up somehow" + #endif + + #ifdef _WIN32 + #ifndef WINGDIAPI + #ifndef STB__HAS_WGLPROC + typedef int (__stdcall *stbgl__voidfunc)(void); + __declspec(dllimport) stbgl__voidfunc wglGetProcAddress(char *); + #endif + #endif + #define STBGL__GET_FUNC(x) wglGetProcAddress(x) + #endif + + #ifdef GLE + #undef GLE + #endif + + #define GLE(a,b) PFNGL##b##PROC gl##a; + #include STB_GLEXT_DEFINE + + #undef GLE + #define GLE(a,b) gl##a = (PFNGL##b##PROC) STBGL__GET_FUNC("gl" #a ); + + void stbgl_initExtensions(void) + { + #include STB_GLEXT_DEFINE + } + + #undef GLE + +#endif // STB_GLEXT_DECLARE + +#endif // STB_GLEXT_SKIP + +#endif // STB_GLEXT_DEFINE || STB_GLEXT_DECLARE diff --git a/impeller/third_party/stb/stb/tests/caveview/stb_glprog.h b/impeller/third_party/stb/stb/tests/caveview/stb_glprog.h new file mode 100644 index 0000000000000..8883a3ebe9124 --- /dev/null +++ b/impeller/third_party/stb/stb/tests/caveview/stb_glprog.h @@ -0,0 +1,504 @@ +// stb_glprog v0.02 public domain functions to reduce GLSL boilerplate +// http://nothings.org/stb/stb_glprog.h especially with GL1 + ARB extensions +// +// Following defines *before* including have following effects: +// +// STB_GLPROG_IMPLEMENTATION +// creates the implementation +// +// STB_GLPROG_STATIC +// forces the implementation to be static (private to file that creates it) +// +// STB_GLPROG_ARB +// uses ARB extension names for GLSL functions and enumerants instead of core names +// +// STB_GLPROG_ARB_DEFINE_EXTENSIONS +// instantiates function pointers needed, static to implementing file +// to avoid collisions (but will collide if implementing file also +// defines any; best to isolate this to its own file in this case). +// This will try to automatically #include glext.h, but if it's not +// in the default include directories you'll need to include it +// yourself and define the next macro. +// +// STB_GLPROG_SUPPRESS_GLEXT_INCLUDE +// disables the automatic #include of glext.h which is normally +// forced by STB_GLPROG_ARB_DEFINE_EXTENSIONS +// +// So, e.g., sample usage on an old Windows compiler: +// +// #define STB_GLPROG_IMPLEMENTATION +// #define STB_GLPROG_ARB_DEFINE_EXTENSIONS +// #include +// #include "gl/gl.h" +// #include "stb_glprog.h" +// +// Note though that the header-file version of this (when you don't define +// STB_GLPROG_IMPLEMENTATION) still uses GLint and such, so you basically +// can only include it in places where you're already including GL, especially +// on Windows where including "gl.h" requires (some of) "windows.h". +// +// See following comment blocks for function documentation. +// +// Version history: +// 2013-12-08 v0.02 slightly simplified API and reduced GL resource usage (@rygorous) +// 2013-12-08 v0.01 initial release + + +// header file section starts here +#if !defined(INCLUDE_STB_GLPROG_H) +#define INCLUDE_STB_GLPROG_H + +#ifndef STB_GLPROG_STATIC +#ifdef __cplusplus +extern "C" { +#endif + +////////////////////////////////////////////////////////////////////////////// + +///////////// SHADER CREATION + + +/// EASY API + +extern GLuint stbgl_create_program(char const **vertex_source, char const **frag_source, char const **binds, char *error, int error_buflen); +// This function returns a compiled program or 0 if there's an error. +// To free the created program, call stbgl_delete_program. +// +// stbgl_create_program( +// char **vertex_source, // NULL or one or more strings with the vertex shader source, with a final NULL +// char **frag_source, // NULL or one or more strings with the fragment shader source, with a final NULL +// char **binds, // NULL or zero or more strings with attribute bind names, with a final NULL +// char *error, // output location where compile error message is placed +// int error_buflen) // length of error output buffer +// +// Returns a GLuint with the GL program object handle. +// +// If an individual bind string is "", no name is bound to that slot (this +// allows you to create binds that aren't continuous integers starting at 0). +// +// If the vertex shader is NULL, then fixed-function vertex pipeline +// is used, if that's legal in your version of GL. +// +// If the fragment shader is NULL, then fixed-function fragment pipeline +// is used, if that's legal in your version of GL. + +extern void stgbl_delete_program(GLuint program); +// deletes a program created by stbgl_create_program or stbgl_link_program + + +/// FLEXIBLE API + +extern GLuint stbgl_compile_shader(GLenum type, char const **sources, int num_sources, char *error, int error_buflen); +// compiles a shader. returns the shader on success or 0 on failure. +// +// type either: GL_VERTEX_SHADER or GL_FRAGMENT_SHADER +// or GL_VERTEX_SHADER_ARB or GL_FRAGMENT_SHADER_ARB +// or STBGL_VERTEX_SHADER or STBGL_FRAGMENT_SHADER +// sources array of strings containing the shader source +// num_sources number of string in sources, or -1 meaning sources is NULL-terminated +// error string to output compiler error to +// error_buflen length of error buffer in chars + +extern GLuint stbgl_link_program(GLuint vertex_shader, GLuint fragment_shader, char const **binds, int num_binds, char *error, int error_buflen); +// links a shader. returns the linked program on success or 0 on failure. +// +// vertex_shader a compiled vertex shader from stbgl_compile_shader, or 0 for fixed-function (if legal) +// fragment_shader a compiled fragment shader from stbgl_compile_shader, or 0 for fixed-function (if legal) +// + +extern void stbgl_delete_shader(GLuint shader); +// deletes a shader created by stbgl_compile_shader + + +///////////// RENDERING WITH SHADERS + +extern GLint stbgl_find_uniform(GLuint prog, char *uniform); + +extern void stbgl_find_uniforms(GLuint prog, GLint *locations, char const **uniforms, int num_uniforms); +// Given the locations array that is num_uniforms long, fills out +// the locations of each of those uniforms for the specified program. +// If num_uniforms is -1, then uniforms[] must be NULL-terminated + +// the following functions just wrap the difference in naming between GL2+ and ARB, +// so you don't need them unless you're using both ARB and GL2+ in the same codebase, +// or you're relying on this lib to provide the extensions +extern void stbglUseProgram(GLuint program); +extern void stbglVertexAttribPointer(GLuint index, GLint size, GLenum type, GLboolean normalized, GLsizei stride, const GLvoid * pointer); +extern void stbglEnableVertexAttribArray(GLuint index); +extern void stbglDisableVertexAttribArray(GLuint index); +extern void stbglUniform1fv(GLint loc, GLsizei count, const GLfloat *v); +extern void stbglUniform2fv(GLint loc, GLsizei count, const GLfloat *v); +extern void stbglUniform3fv(GLint loc, GLsizei count, const GLfloat *v); +extern void stbglUniform4fv(GLint loc, GLsizei count, const GLfloat *v); +extern void stbglUniform1iv(GLint loc, GLsizei count, const GLint *v); +extern void stbglUniform2iv(GLint loc, GLsizei count, const GLint *v); +extern void stbglUniform3iv(GLint loc, GLsizei count, const GLint *v); +extern void stbglUniform4iv(GLint loc, GLsizei count, const GLint *v); +extern void stbglUniform1f(GLint loc, float v0); +extern void stbglUniform2f(GLint loc, float v0, float v1); +extern void stbglUniform3f(GLint loc, float v0, float v1, float v2); +extern void stbglUniform4f(GLint loc, float v0, float v1, float v2, float v3); +extern void stbglUniform1i(GLint loc, GLint v0); +extern void stbglUniform2i(GLint loc, GLint v0, GLint v1); +extern void stbglUniform3i(GLint loc, GLint v0, GLint v1, GLint v2); +extern void stbglUniform4i(GLint loc, GLint v0, GLint v1, GLint v2, GLint v3); + + +////////////// END OF FUNCTIONS + +////////////////////////////////////////////////////////////////////////////// + +#ifdef __cplusplus +} +#endif +#endif // STB_GLPROG_STATIC + +#ifdef STB_GLPROG_ARB +#define STBGL_VERTEX_SHADER GL_VERTEX_SHADER_ARB +#define STBGL_FRAGMENT_SHADER GL_FRAGMENT_SHADER_ARB +#else +#define STBGL_VERTEX_SHADER GL_VERTEX_SHADER +#define STBGL_FRAGMENT_SHADER GL_FRAGMENT_SHADER +#endif + +#endif // INCLUDE_STB_GLPROG_H + + +///////// header file section ends here + + +#ifdef STB_GLPROG_IMPLEMENTATION +#include // strncpy + +#ifdef STB_GLPROG_STATIC +#define STB_GLPROG_DECLARE static +#else +#define STB_GLPROG_DECLARE extern +#endif + +// check if user wants this file to define the GL extensions itself +#ifdef STB_GLPROG_ARB_DEFINE_EXTENSIONS +#define STB_GLPROG_ARB // make sure later code uses the extensions + +#ifndef STB_GLPROG_SUPPRESS_GLEXT_INCLUDE +#include "glext.h" +#endif + +#define STB_GLPROG_EXTENSIONS \ + STB_GLPROG_FUNC(ATTACHOBJECT , AttachObject ) \ + STB_GLPROG_FUNC(BINDATTRIBLOCATION , BindAttribLocation ) \ + STB_GLPROG_FUNC(COMPILESHADER , CompileShader ) \ + STB_GLPROG_FUNC(CREATEPROGRAMOBJECT , CreateProgramObject ) \ + STB_GLPROG_FUNC(CREATESHADEROBJECT , CreateShaderObject ) \ + STB_GLPROG_FUNC(DELETEOBJECT , DeleteObject ) \ + STB_GLPROG_FUNC(DETACHOBJECT , DetachObject ) \ + STB_GLPROG_FUNC(DISABLEVERTEXATTRIBARRAY, DisableVertexAttribArray) \ + STB_GLPROG_FUNC(ENABLEVERTEXATTRIBARRAY, EnableVertexAttribArray ) \ + STB_GLPROG_FUNC(GETATTACHEDOBJECTS , GetAttachedObjects ) \ + STB_GLPROG_FUNC(GETOBJECTPARAMETERIV, GetObjectParameteriv) \ + STB_GLPROG_FUNC(GETINFOLOG , GetInfoLog ) \ + STB_GLPROG_FUNC(GETUNIFORMLOCATION , GetUniformLocation ) \ + STB_GLPROG_FUNC(LINKPROGRAM , LinkProgram ) \ + STB_GLPROG_FUNC(SHADERSOURCE , ShaderSource ) \ + STB_GLPROG_FUNC(UNIFORM1F , Uniform1f ) \ + STB_GLPROG_FUNC(UNIFORM2F , Uniform2f ) \ + STB_GLPROG_FUNC(UNIFORM3F , Uniform3f ) \ + STB_GLPROG_FUNC(UNIFORM4F , Uniform4f ) \ + STB_GLPROG_FUNC(UNIFORM1I , Uniform1i ) \ + STB_GLPROG_FUNC(UNIFORM2I , Uniform2i ) \ + STB_GLPROG_FUNC(UNIFORM3I , Uniform3i ) \ + STB_GLPROG_FUNC(UNIFORM4I , Uniform4i ) \ + STB_GLPROG_FUNC(UNIFORM1FV , Uniform1fv ) \ + STB_GLPROG_FUNC(UNIFORM2FV , Uniform2fv ) \ + STB_GLPROG_FUNC(UNIFORM3FV , Uniform3fv ) \ + STB_GLPROG_FUNC(UNIFORM4FV , Uniform4fv ) \ + STB_GLPROG_FUNC(UNIFORM1IV , Uniform1iv ) \ + STB_GLPROG_FUNC(UNIFORM2IV , Uniform2iv ) \ + STB_GLPROG_FUNC(UNIFORM3IV , Uniform3iv ) \ + STB_GLPROG_FUNC(UNIFORM4IV , Uniform4iv ) \ + STB_GLPROG_FUNC(USEPROGRAMOBJECT , UseProgramObject ) \ + STB_GLPROG_FUNC(VERTEXATTRIBPOINTER , VertexAttribPointer ) + +// define the static function pointers + +#define STB_GLPROG_FUNC(x,y) static PFNGL##x##ARBPROC gl##y##ARB; +STB_GLPROG_EXTENSIONS +#undef STB_GLPROG_FUNC + +// define the GetProcAddress + +#ifdef _WIN32 +#ifndef WINGDIAPI +#ifndef STB__HAS_WGLPROC +typedef int (__stdcall *stbgl__voidfunc)(void); +static __declspec(dllimport) stbgl__voidfunc wglGetProcAddress(char *); +#endif +#endif +#define STBGL__GET_FUNC(x) wglGetProcAddress(x) +#else +#error "need to define how this platform gets extensions" +#endif + +// create a function that fills out the function pointers + +static void stb_glprog_init(void) +{ + static int initialized = 0; // not thread safe! + if (initialized) return; + #define STB_GLPROG_FUNC(x,y) gl##y##ARB = (PFNGL##x##ARBPROC) STBGL__GET_FUNC("gl" #y "ARB"); + STB_GLPROG_EXTENSIONS + #undef STB_GLPROG_FUNC +} +#undef STB_GLPROG_EXTENSIONS + +#else +static void stb_glprog_init(void) +{ +} +#endif + + +// define generic names for many of the gl functions or extensions for later use; +// note that in some cases there are two functions in core and one function in ARB +#ifdef STB_GLPROG_ARB +#define stbglCreateShader glCreateShaderObjectARB +#define stbglDeleteShader glDeleteObjectARB +#define stbglAttachShader glAttachObjectARB +#define stbglDetachShader glDetachObjectARB +#define stbglShaderSource glShaderSourceARB +#define stbglCompileShader glCompileShaderARB +#define stbglGetShaderStatus(a,b) glGetObjectParameterivARB(a, GL_OBJECT_COMPILE_STATUS_ARB, b) +#define stbglGetShaderInfoLog glGetInfoLogARB +#define stbglCreateProgram glCreateProgramObjectARB +#define stbglDeleteProgram glDeleteObjectARB +#define stbglLinkProgram glLinkProgramARB +#define stbglGetProgramStatus(a,b) glGetObjectParameterivARB(a, GL_OBJECT_LINK_STATUS_ARB, b) +#define stbglGetProgramInfoLog glGetInfoLogARB +#define stbglGetAttachedShaders glGetAttachedObjectsARB +#define stbglBindAttribLocation glBindAttribLocationARB +#define stbglGetUniformLocation glGetUniformLocationARB +#define stbgl_UseProgram glUseProgramObjectARB +#else +#define stbglCreateShader glCreateShader +#define stbglDeleteShader glDeleteShader +#define stbglAttachShader glAttachShader +#define stbglDetachShader glDetachShader +#define stbglShaderSource glShaderSource +#define stbglCompileShader glCompileShader +#define stbglGetShaderStatus(a,b) glGetShaderiv(a, GL_COMPILE_STATUS, b) +#define stbglGetShaderInfoLog glGetShaderInfoLog +#define stbglCreateProgram glCreateProgram +#define stbglDeleteProgram glDeleteProgram +#define stbglLinkProgram glLinkProgram +#define stbglGetProgramStatus(a,b) glGetProgramiv(a, GL_LINK_STATUS, b) +#define stbglGetProgramInfoLog glGetProgramInfoLog +#define stbglGetAttachedShaders glGetAttachedShaders +#define stbglBindAttribLocation glBindAttribLocation +#define stbglGetUniformLocation glGetUniformLocation +#define stbgl_UseProgram glUseProgram +#endif + + +// perform a safe strcat of 3 strings, given that we can't rely on portable snprintf +// if you need to break on error, this is the best place to place a breakpoint +static void stb_glprog_error(char *error, int error_buflen, char *str1, char *str2, char *str3) +{ + int n = strlen(str1); + strncpy(error, str1, error_buflen); + if (n < error_buflen && str2) { + strncpy(error+n, str2, error_buflen - n); + n += strlen(str2); + if (n < error_buflen && str3) { + strncpy(error+n, str3, error_buflen - n); + } + } + error[error_buflen-1] = 0; +} + +STB_GLPROG_DECLARE GLuint stbgl_compile_shader(GLenum type, char const **sources, int num_sources, char *error, int error_buflen) +{ + char *typename = (type == STBGL_VERTEX_SHADER ? "vertex" : "fragment"); + int len; + GLint result; + GLuint shader; + + // initialize the extensions if we haven't already + stb_glprog_init(); + + // allocate + + shader = stbglCreateShader(type); + if (!shader) { + stb_glprog_error(error, error_buflen, "Couldn't allocate shader object in stbgl_compile_shader for ", typename, NULL); + return 0; + } + + // compile + + // if num_sources is negative, assume source is NULL-terminated and count the non-NULL ones + if (num_sources < 0) + for (num_sources = 0; sources[num_sources] != NULL; ++num_sources) + ; + stbglShaderSource(shader, num_sources, sources, NULL); + stbglCompileShader(shader); + stbglGetShaderStatus(shader, &result); + if (result) + return shader; + + // errors + + stb_glprog_error(error, error_buflen, "Compile error for ", typename, " shader: "); + len = strlen(error); + if (len < error_buflen) + stbglGetShaderInfoLog(shader, error_buflen-len, NULL, error+len); + + stbglDeleteShader(shader); + return 0; +} + +STB_GLPROG_DECLARE GLuint stbgl_link_program(GLuint vertex_shader, GLuint fragment_shader, char const **binds, int num_binds, char *error, int error_buflen) +{ + int len; + GLint result; + + // allocate + + GLuint prog = stbglCreateProgram(); + if (!prog) { + stb_glprog_error(error, error_buflen, "Couldn't allocate program object in stbgl_link_program", NULL, NULL); + return 0; + } + + // attach + + if (vertex_shader) + stbglAttachShader(prog, vertex_shader); + if (fragment_shader) + stbglAttachShader(prog, fragment_shader); + + // attribute binds + + if (binds) { + int i; + // if num_binds is negative, then it is NULL terminated + if (num_binds < 0) + for (num_binds=0; binds[num_binds]; ++num_binds) + ; + for (i=0; i < num_binds; ++i) + if (binds[i] && binds[i][0]) // empty binds can be NULL or "" + stbglBindAttribLocation(prog, i, binds[i]); + } + + // link + + stbglLinkProgram(prog); + + // detach + + if (vertex_shader) + stbglDetachShader(prog, vertex_shader); + if (fragment_shader) + stbglDetachShader(prog, fragment_shader); + + // errors + + stbglGetProgramStatus(prog, &result); + if (result) + return prog; + + stb_glprog_error(error, error_buflen, "Link error: ", NULL, NULL); + len = strlen(error); + if (len < error_buflen) + stbglGetProgramInfoLog(prog, error_buflen-len, NULL, error+len); + + stbglDeleteProgram(prog); + return 0; +} + +STB_GLPROG_DECLARE GLuint stbgl_create_program(char const **vertex_source, char const **frag_source, char const **binds, char *error, int error_buflen) +{ + GLuint vertex, fragment, prog=0; + vertex = stbgl_compile_shader(STBGL_VERTEX_SHADER, vertex_source, -1, error, error_buflen); + if (vertex) { + fragment = stbgl_compile_shader(STBGL_FRAGMENT_SHADER, frag_source, -1, error, error_buflen); + if (fragment) + prog = stbgl_link_program(vertex, fragment, binds, -1, error, error_buflen); + if (fragment) + stbglDeleteShader(fragment); + stbglDeleteShader(vertex); + } + return prog; +} + +STB_GLPROG_DECLARE void stbgl_delete_shader(GLuint shader) +{ + stbglDeleteShader(shader); +} + +STB_GLPROG_DECLARE void stgbl_delete_program(GLuint program) +{ + stbglDeleteProgram(program); +} + +GLint stbgl_find_uniform(GLuint prog, char *uniform) +{ + return stbglGetUniformLocation(prog, uniform); +} + +STB_GLPROG_DECLARE void stbgl_find_uniforms(GLuint prog, GLint *locations, char const **uniforms, int num_uniforms) +{ + int i; + if (num_uniforms < 0) + num_uniforms = 999999; + for (i=0; i < num_uniforms && uniforms[i]; ++i) + locations[i] = stbglGetUniformLocation(prog, uniforms[i]); +} + +STB_GLPROG_DECLARE void stbglUseProgram(GLuint program) +{ + stbgl_UseProgram(program); +} + +#ifdef STB_GLPROG_ARB +#define STBGL_ARBIFY(name) name##ARB +#else +#define STBGL_ARBIFY(name) name +#endif + +STB_GLPROG_DECLARE void stbglVertexAttribPointer(GLuint index, GLint size, GLenum type, GLboolean normalized, GLsizei stride, const GLvoid * pointer) +{ + STBGL_ARBIFY(glVertexAttribPointer)(index, size, type, normalized, stride, pointer); +} + +STB_GLPROG_DECLARE void stbglEnableVertexAttribArray (GLuint index) { STBGL_ARBIFY(glEnableVertexAttribArray )(index); } +STB_GLPROG_DECLARE void stbglDisableVertexAttribArray(GLuint index) { STBGL_ARBIFY(glDisableVertexAttribArray)(index); } + +STB_GLPROG_DECLARE void stbglUniform1fv(GLint loc, GLsizei count, const GLfloat *v) { STBGL_ARBIFY(glUniform1fv)(loc,count,v); } +STB_GLPROG_DECLARE void stbglUniform2fv(GLint loc, GLsizei count, const GLfloat *v) { STBGL_ARBIFY(glUniform2fv)(loc,count,v); } +STB_GLPROG_DECLARE void stbglUniform3fv(GLint loc, GLsizei count, const GLfloat *v) { STBGL_ARBIFY(glUniform3fv)(loc,count,v); } +STB_GLPROG_DECLARE void stbglUniform4fv(GLint loc, GLsizei count, const GLfloat *v) { STBGL_ARBIFY(glUniform4fv)(loc,count,v); } + +STB_GLPROG_DECLARE void stbglUniform1iv(GLint loc, GLsizei count, const GLint *v) { STBGL_ARBIFY(glUniform1iv)(loc,count,v); } +STB_GLPROG_DECLARE void stbglUniform2iv(GLint loc, GLsizei count, const GLint *v) { STBGL_ARBIFY(glUniform2iv)(loc,count,v); } +STB_GLPROG_DECLARE void stbglUniform3iv(GLint loc, GLsizei count, const GLint *v) { STBGL_ARBIFY(glUniform3iv)(loc,count,v); } +STB_GLPROG_DECLARE void stbglUniform4iv(GLint loc, GLsizei count, const GLint *v) { STBGL_ARBIFY(glUniform4iv)(loc,count,v); } + +STB_GLPROG_DECLARE void stbglUniform1f(GLint loc, float v0) + { STBGL_ARBIFY(glUniform1f)(loc,v0); } +STB_GLPROG_DECLARE void stbglUniform2f(GLint loc, float v0, float v1) + { STBGL_ARBIFY(glUniform2f)(loc,v0,v1); } +STB_GLPROG_DECLARE void stbglUniform3f(GLint loc, float v0, float v1, float v2) + { STBGL_ARBIFY(glUniform3f)(loc,v0,v1,v2); } +STB_GLPROG_DECLARE void stbglUniform4f(GLint loc, float v0, float v1, float v2, float v3) + { STBGL_ARBIFY(glUniform4f)(loc,v0,v1,v2,v3); } + +STB_GLPROG_DECLARE void stbglUniform1i(GLint loc, GLint v0) + { STBGL_ARBIFY(glUniform1i)(loc,v0); } +STB_GLPROG_DECLARE void stbglUniform2i(GLint loc, GLint v0, GLint v1) + { STBGL_ARBIFY(glUniform2i)(loc,v0,v1); } +STB_GLPROG_DECLARE void stbglUniform3i(GLint loc, GLint v0, GLint v1, GLint v2) + { STBGL_ARBIFY(glUniform3i)(loc,v0,v1,v2); } +STB_GLPROG_DECLARE void stbglUniform4i(GLint loc, GLint v0, GLint v1, GLint v2, GLint v3) + { STBGL_ARBIFY(glUniform4i)(loc,v0,v1,v2,v3); } + +#endif diff --git a/impeller/third_party/stb/stb/tests/caveview/win32/SDL_windows_main.c b/impeller/third_party/stb/stb/tests/caveview/win32/SDL_windows_main.c new file mode 100644 index 0000000000000..32e316b55701c --- /dev/null +++ b/impeller/third_party/stb/stb/tests/caveview/win32/SDL_windows_main.c @@ -0,0 +1,224 @@ +/* + SDL_windows_main.c, placed in the public domain by Sam Lantinga 4/13/98 + + The WinMain function -- calls your program's main() function +*/ +#include "SDL_config.h" + +#ifdef __WIN32__ + +//#include "../../core/windows/SDL_windows.h" + +/* Include this so we define UNICODE properly */ +#if defined(__WIN32__) +#define WIN32_LEAN_AND_MEAN +#define STRICT +#ifndef UNICODE +#define UNICODE 1 +#endif +#undef _WIN32_WINNT +#define _WIN32_WINNT 0x501 /* Need 0x410 for AlphaBlend() and 0x500 for EnumDisplayDevices(), 0x501 for raw input */ +#endif + +#include + +/* Routines to convert from UTF8 to native Windows text */ +#if UNICODE +#define WIN_StringToUTF8(S) SDL_iconv_string("UTF-8", "UTF-16LE", (char *)(S), (SDL_wcslen(S)+1)*sizeof(WCHAR)) +#define WIN_UTF8ToString(S) (WCHAR *)SDL_iconv_string("UTF-16LE", "UTF-8", (char *)(S), SDL_strlen(S)+1) +#else +/* !!! FIXME: UTF8ToString() can just be a SDL_strdup() here. */ +#define WIN_StringToUTF8(S) SDL_iconv_string("UTF-8", "ASCII", (char *)(S), (SDL_strlen(S)+1)) +#define WIN_UTF8ToString(S) SDL_iconv_string("ASCII", "UTF-8", (char *)(S), SDL_strlen(S)+1) +#endif + +/* Sets an error message based on a given HRESULT */ +extern int WIN_SetErrorFromHRESULT(const char *prefix, HRESULT hr); + +/* Sets an error message based on GetLastError(). Always return -1. */ +extern int WIN_SetError(const char *prefix); + +/* Wrap up the oddities of CoInitialize() into a common function. */ +extern HRESULT WIN_CoInitialize(void); +extern void WIN_CoUninitialize(void); + +/* Returns SDL_TRUE if we're running on Windows Vista and newer */ +extern BOOL WIN_IsWindowsVistaOrGreater(); + +#include +#include + +/* Include the SDL main definition header */ +#include "SDL.h" +#include "SDL_main.h" + +#ifdef main +# undef main +#endif /* main */ + +static void +UnEscapeQuotes(char *arg) +{ + char *last = NULL; + + while (*arg) { + if (*arg == '"' && (last != NULL && *last == '\\')) { + char *c_curr = arg; + char *c_last = last; + + while (*c_curr) { + *c_last = *c_curr; + c_last = c_curr; + c_curr++; + } + *c_last = '\0'; + } + last = arg; + arg++; + } +} + +/* Parse a command line buffer into arguments */ +static int +ParseCommandLine(char *cmdline, char **argv) +{ + char *bufp; + char *lastp = NULL; + int argc, last_argc; + + argc = last_argc = 0; + for (bufp = cmdline; *bufp;) { + /* Skip leading whitespace */ + while (SDL_isspace(*bufp)) { + ++bufp; + } + /* Skip over argument */ + if (*bufp == '"') { + ++bufp; + if (*bufp) { + if (argv) { + argv[argc] = bufp; + } + ++argc; + } + /* Skip over word */ + lastp = bufp; + while (*bufp && (*bufp != '"' || *lastp == '\\')) { + lastp = bufp; + ++bufp; + } + } else { + if (*bufp) { + if (argv) { + argv[argc] = bufp; + } + ++argc; + } + /* Skip over word */ + while (*bufp && !SDL_isspace(*bufp)) { + ++bufp; + } + } + if (*bufp) { + if (argv) { + *bufp = '\0'; + } + ++bufp; + } + + /* Strip out \ from \" sequences */ + if (argv && last_argc != argc) { + UnEscapeQuotes(argv[last_argc]); + } + last_argc = argc; + } + if (argv) { + argv[argc] = NULL; + } + return (argc); +} + +/* Show an error message */ +static void +ShowError(const char *title, const char *message) +{ +/* If USE_MESSAGEBOX is defined, you need to link with user32.lib */ +#ifdef USE_MESSAGEBOX + MessageBox(NULL, message, title, MB_ICONEXCLAMATION | MB_OK); +#else + fprintf(stderr, "%s: %s\n", title, message); +#endif +} + +/* Pop up an out of memory message, returns to Windows */ +static BOOL +OutOfMemory(void) +{ + ShowError("Fatal Error", "Out of memory - aborting"); + return FALSE; +} + +#if defined(_MSC_VER) +/* The VC++ compiler needs main defined */ +#define console_main main +#endif + +/* This is where execution begins [console apps] */ +int +console_main(int argc, char *argv[]) +{ + int status; + + SDL_SetMainReady(); + + /* Run the application main() code */ + status = SDL_main(argc, argv); + + /* Exit cleanly, calling atexit() functions */ + exit(status); + + /* Hush little compiler, don't you cry... */ + return 0; +} + +/* This is where execution begins [windowed apps] */ +int WINAPI +WinMain(HINSTANCE hInst, HINSTANCE hPrev, LPSTR szCmdLine, int sw) +{ + char **argv; + int argc; + char *cmdline; + + /* Grab the command line */ + TCHAR *text = GetCommandLine(); +#if UNICODE + cmdline = SDL_iconv_string("UTF-8", "UCS-2-INTERNAL", (char *)(text), (SDL_wcslen(text)+1)*sizeof(WCHAR)); +#else + cmdline = SDL_strdup(text); +#endif + if (cmdline == NULL) { + return OutOfMemory(); + } + + /* Parse it into argv and argc */ + argc = ParseCommandLine(cmdline, NULL); + argv = SDL_stack_alloc(char *, argc + 1); + if (argv == NULL) { + return OutOfMemory(); + } + ParseCommandLine(cmdline, argv); + + /* Run the main program */ + console_main(argc, argv); + + SDL_stack_free(argv); + + SDL_free(cmdline); + + /* Hush little compiler, don't you cry... */ + return 0; +} + +#endif /* __WIN32__ */ + +/* vi: set ts=4 sw=4 expandtab: */ diff --git a/impeller/third_party/stb/stb/tests/herringbone.dsp b/impeller/third_party/stb/stb/tests/herringbone.dsp new file mode 100644 index 0000000000000..b82fee45aa8ca --- /dev/null +++ b/impeller/third_party/stb/stb/tests/herringbone.dsp @@ -0,0 +1,95 @@ +# Microsoft Developer Studio Project File - Name="herringbone" - Package Owner=<4> +# Microsoft Developer Studio Generated Build File, Format Version 6.00 +# ** DO NOT EDIT ** + +# TARGTYPE "Win32 (x86) Console Application" 0x0103 + +CFG=herringbone - Win32 Debug +!MESSAGE This is not a valid makefile. To build this project using NMAKE, +!MESSAGE use the Export Makefile command and run +!MESSAGE +!MESSAGE NMAKE /f "herringbone.mak". +!MESSAGE +!MESSAGE You can specify a configuration when running NMAKE +!MESSAGE by defining the macro CFG on the command line. For example: +!MESSAGE +!MESSAGE NMAKE /f "herringbone.mak" CFG="herringbone - Win32 Debug" +!MESSAGE +!MESSAGE Possible choices for configuration are: +!MESSAGE +!MESSAGE "herringbone - Win32 Release" (based on "Win32 (x86) Console Application") +!MESSAGE "herringbone - Win32 Debug" (based on "Win32 (x86) Console Application") +!MESSAGE + +# Begin Project +# PROP AllowPerConfigDependencies 0 +# PROP Scc_ProjName "" +# PROP Scc_LocalPath "" +CPP=cl.exe +RSC=rc.exe + +!IF "$(CFG)" == "herringbone - Win32 Release" + +# PROP BASE Use_MFC 0 +# PROP BASE Use_Debug_Libraries 0 +# PROP BASE Output_Dir "Release" +# PROP BASE Intermediate_Dir "Release" +# PROP BASE Target_Dir "" +# PROP Use_MFC 0 +# PROP Use_Debug_Libraries 0 +# PROP Output_Dir "Release" +# PROP Intermediate_Dir "Release" +# PROP Ignore_Export_Lib 0 +# PROP Target_Dir "" +# ADD BASE CPP /nologo /W3 /GX /O2 /D "WIN32" /D "NDEBUG" /D "_CONSOLE" /D "_MBCS" /YX /FD /c +# ADD CPP /nologo /W3 /GX /O2 /I ".." /D "WIN32" /D "NDEBUG" /D "_CONSOLE" /D "_MBCS" /YX /FD /c +# ADD BASE RSC /l 0x409 /d "NDEBUG" +# ADD RSC /l 0x409 /d "NDEBUG" +BSC32=bscmake.exe +# ADD BASE BSC32 /nologo +# ADD BSC32 /nologo +LINK32=link.exe +# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:console /machine:I386 +# ADD LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:console /machine:I386 + +!ELSEIF "$(CFG)" == "herringbone - Win32 Debug" + +# PROP BASE Use_MFC 0 +# PROP BASE Use_Debug_Libraries 1 +# PROP BASE Output_Dir "herringbone___Win32_Debug" +# PROP BASE Intermediate_Dir "herringbone___Win32_Debug" +# PROP BASE Target_Dir "" +# PROP Use_MFC 0 +# PROP Use_Debug_Libraries 1 +# PROP Output_Dir "Debug" +# PROP Intermediate_Dir "Debug" +# PROP Ignore_Export_Lib 0 +# PROP Target_Dir "" +# ADD BASE CPP /nologo /W3 /Gm /GX /ZI /Od /D "WIN32" /D "_DEBUG" /D "_CONSOLE" /D "_MBCS" /YX /FD /GZ /c +# ADD CPP /nologo /W3 /Gm /GX /ZI /Od /I ".." /D "WIN32" /D "_DEBUG" /D "_CONSOLE" /D "_MBCS" /FD /GZ /c +# SUBTRACT CPP /YX +# ADD BASE RSC /l 0x409 /d "_DEBUG" +# ADD RSC /l 0x409 /d "_DEBUG" +BSC32=bscmake.exe +# ADD BASE BSC32 /nologo +# ADD BSC32 /nologo +LINK32=link.exe +# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:console /debug /machine:I386 /pdbtype:sept +# ADD LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:console /debug /machine:I386 /pdbtype:sept + +!ENDIF + +# Begin Target + +# Name "herringbone - Win32 Release" +# Name "herringbone - Win32 Debug" +# Begin Source File + +SOURCE=.\herringbone_generator.c +# End Source File +# Begin Source File + +SOURCE=..\stb_herringbone_wang_tile.h +# End Source File +# End Target +# End Project diff --git a/impeller/third_party/stb/stb/tests/herringbone_generator.c b/impeller/third_party/stb/stb/tests/herringbone_generator.c new file mode 100644 index 0000000000000..cf2a99eb98454 --- /dev/null +++ b/impeller/third_party/stb/stb/tests/herringbone_generator.c @@ -0,0 +1,87 @@ +#define STB_HERRINGBONE_WANG_TILE_IMPLEMENTATION +#include "stb_herringbone_wang_tile.h" + +#define STB_IMAGE_WRITE_IMPLEMENTATION +#include "stb_image_write.h" + +// e 12 1 1 1 1 1 1 4 4 + +int main(int argc, char **argv) +{ + stbhw_config c = { 0 }; + int w,h, num_colors,i; + unsigned char *data; + + if (argc == 1) goto usage; + if (argc < 3) goto error; + + switch (argv[2][0]) { + case 'c': + if (argc < 8 || argc > 10) + goto error; + num_colors = 4; + c.is_corner = 1; + break; + + case 'e': + if (argc < 10 || argc > 12) + goto error; + num_colors = 6; + c.is_corner = 0; + break; + + default: + goto error; + } + + c.short_side_len = atoi(argv[3]); + for (i=0; i < num_colors; ++i) + c.num_color[i] = atoi(argv[4+i]); + + c.num_vary_x = 1; + c.num_vary_y = 1; + + if (argc > 4+i) + c.num_vary_x = atoi(argv[4+i]); + if (argc > 5+i) + c.num_vary_y = atoi(argv[5+i]); + + stbhw_get_template_size(&c, &w, &h); + + data = (unsigned char *) malloc(w*h*3); + + if (stbhw_make_template(&c, data, w, h, w*3)) + stbi_write_png(argv[1], w, h, 3, data, w*3); + else + fprintf(stderr, "Error: %s\n", stbhw_get_last_error()); + return 0; + + error: + fputs("Invalid command-line arguments\n\n", stderr); + usage: + fputs("Usage (see source for corner & edge type definitions):\n\n", stderr); + fputs("herringbone_generator {outfile} c {sidelen} {c0} {c1} {c2} {c3} [{vx} {vy}]\n" + " {outfile} -- filename that template will be written to as PNG\n" + " {sidelen} -- length of short side of rectangle in pixels\n" + " {c0} -- number of colors for corner type 0\n" + " {c1} -- number of colors for corner type 1\n" + " {c2} -- number of colors for corner type 2\n" + " {c3} -- number of colors for corner type 3\n" + " {vx} -- number of color-duplicating variations horizontally in template\n" + " {vy} -- number of color-duplicating variations vertically in template\n" + "\n" + , stderr); + fputs("herringbone_generator {outfile} e {sidelen} {e0} {e1} {e2} {e3} {e4} {e5} [{vx} {vy}]\n" + " {outfile} -- filename that template will be written to as PNG\n" + " {sidelen} -- length of short side of rectangle in pixels\n" + " {e0} -- number of colors for edge type 0\n" + " {e1} -- number of colors for edge type 1\n" + " {e2} -- number of colors for edge type 2\n" + " {e3} -- number of colors for edge type 3\n" + " {e4} -- number of colors for edge type 4\n" + " {e5} -- number of colors for edge type 5\n" + " {vx} -- number of color-duplicating variations horizontally in template\n" + " {vy} -- number of color-duplicating variations vertically in template\n" + , stderr); + return 1; +} diff --git a/impeller/third_party/stb/stb/tests/herringbone_map.c b/impeller/third_party/stb/stb/tests/herringbone_map.c new file mode 100644 index 0000000000000..22cc013100375 --- /dev/null +++ b/impeller/third_party/stb/stb/tests/herringbone_map.c @@ -0,0 +1,83 @@ +#include + +#define STB_HBWANG_MAX_X 500 +#define STB_HBWANG_MAX_Y 500 + +#define STB_HERRINGBONE_WANG_TILE_IMPLEMENTATION +#include "stb_herringbone_wang_tile.h" + +#define STB_IMAGE_IMPLEMENTATION +#include "stb_image.h" + +#define STB_IMAGE_WRITE_IMPLEMENTATION +#include "stb_image_write.h" + +int main(int argc, char **argv) +{ + if (argc < 5) { + fprintf(stderr, "Usage: herringbone_map {inputfile} {output-width} {output-height} {outputfile}\n"); + return 1; + } else { + char *filename = argv[1]; + int out_w = atoi(argv[2]); + int out_h = atoi(argv[3]); + char *outfile = argv[4]; + + unsigned char *pixels, *out_pixels; + stbhw_tileset ts; + int w,h; + + pixels = stbi_load(filename, &w, &h, 0, 3); + if (pixels == 0) { + fprintf(stderr, "Couldn't open input file '%s'\n", filename); + exit(1); + } + + if (!stbhw_build_tileset_from_image(&ts, pixels, w*3, w, h)) { + fprintf(stderr, "Error: %s\n", stbhw_get_last_error()); + return 1; + } + + free(pixels); + + #ifdef DEBUG_OUTPUT + { + int i,j,k; + // add blue borders to top-left edges of the tiles + int hstride = (ts.short_side_len*2)*3; + int vstride = (ts.short_side_len )*3; + for (i=0; i < ts.num_h_tiles; ++i) { + unsigned char *pix = ts.h_tiles[i]->pixels; + for (j=0; j < ts.short_side_len*2; ++j) + for (k=0; k < 3; ++k) + pix[j*3+k] = (pix[j*3+k]*0.5+100+k*75)/1.5; + for (j=1; j < ts.short_side_len; ++j) + for (k=0; k < 3; ++k) + pix[j*hstride+k] = (pix[j*hstride+k]*0.5+100+k*75)/1.5; + } + for (i=0; i < ts.num_v_tiles; ++i) { + unsigned char *pix = ts.v_tiles[i]->pixels; + for (j=0; j < ts.short_side_len; ++j) + for (k=0; k < 3; ++k) + pix[j*3+k] = (pix[j*3+k]*0.5+100+k*75)/1.5; + for (j=1; j < ts.short_side_len*2; ++j) + for (k=0; k < 3; ++k) + pix[j*vstride+k] = (pix[j*vstride+k]*0.5+100+k*75)/1.5; + } + } + #endif + + out_pixels = malloc(out_w * out_h * 3); + + if (!stbhw_generate_image(&ts, NULL, out_pixels, out_w*3, out_w, out_h)) { + fprintf(stderr, "Error: %s\n", stbhw_get_last_error()); + return 1; + } + + stbi_write_png(argv[4], out_w, out_h, 3, out_pixels, out_w*3); + free(out_pixels); + + stbhw_free_tileset(&ts); + return 0; + } +} \ No newline at end of file diff --git a/impeller/third_party/stb/stb/tests/herringbone_map.dsp b/impeller/third_party/stb/stb/tests/herringbone_map.dsp new file mode 100644 index 0000000000000..3e26d6d78cd1b --- /dev/null +++ b/impeller/third_party/stb/stb/tests/herringbone_map.dsp @@ -0,0 +1,94 @@ +# Microsoft Developer Studio Project File - Name="herringbone_map" - Package Owner=<4> +# Microsoft Developer Studio Generated Build File, Format Version 6.00 +# ** DO NOT EDIT ** + +# TARGTYPE "Win32 (x86) Console Application" 0x0103 + +CFG=herringbone_map - Win32 Debug +!MESSAGE This is not a valid makefile. To build this project using NMAKE, +!MESSAGE use the Export Makefile command and run +!MESSAGE +!MESSAGE NMAKE /f "herringbone_map.mak". +!MESSAGE +!MESSAGE You can specify a configuration when running NMAKE +!MESSAGE by defining the macro CFG on the command line. For example: +!MESSAGE +!MESSAGE NMAKE /f "herringbone_map.mak" CFG="herringbone_map - Win32 Debug" +!MESSAGE +!MESSAGE Possible choices for configuration are: +!MESSAGE +!MESSAGE "herringbone_map - Win32 Release" (based on "Win32 (x86) Console Application") +!MESSAGE "herringbone_map - Win32 Debug" (based on "Win32 (x86) Console Application") +!MESSAGE + +# Begin Project +# PROP AllowPerConfigDependencies 0 +# PROP Scc_ProjName "" +# PROP Scc_LocalPath "" +CPP=cl.exe +RSC=rc.exe + +!IF "$(CFG)" == "herringbone_map - Win32 Release" + +# PROP BASE Use_MFC 0 +# PROP BASE Use_Debug_Libraries 0 +# PROP BASE Output_Dir "Release" +# PROP BASE Intermediate_Dir "Release" +# PROP BASE Target_Dir "" +# PROP Use_MFC 0 +# PROP Use_Debug_Libraries 0 +# PROP Output_Dir "Release" +# PROP Intermediate_Dir "Release" +# PROP Ignore_Export_Lib 0 +# PROP Target_Dir "" +# ADD BASE CPP /nologo /W3 /GX /O2 /D "WIN32" /D "NDEBUG" /D "_CONSOLE" /D "_MBCS" /YX /FD /c +# ADD CPP /nologo /W3 /GX /O2 /I ".." /D "WIN32" /D "NDEBUG" /D "_CONSOLE" /D "_MBCS" /YX /FD /c +# ADD BASE RSC /l 0x409 /d "NDEBUG" +# ADD RSC /l 0x409 /d "NDEBUG" +BSC32=bscmake.exe +# ADD BASE BSC32 /nologo +# ADD BSC32 /nologo +LINK32=link.exe +# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:console /machine:I386 +# ADD LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:console /machine:I386 + +!ELSEIF "$(CFG)" == "herringbone_map - Win32 Debug" + +# PROP BASE Use_MFC 0 +# PROP BASE Use_Debug_Libraries 1 +# PROP BASE Output_Dir "herringbone_map___Win32_Debug" +# PROP BASE Intermediate_Dir "herringbone_map___Win32_Debug" +# PROP BASE Target_Dir "" +# PROP Use_MFC 0 +# PROP Use_Debug_Libraries 1 +# PROP Output_Dir "Debug" +# PROP Intermediate_Dir "Debug" +# PROP Target_Dir "" +# ADD BASE CPP /nologo /W3 /Gm /GX /ZI /Od /D "WIN32" /D "_DEBUG" /D "_CONSOLE" /D "_MBCS" /YX /FD /GZ /c +# ADD CPP /nologo /W3 /Gm /GX /ZI /Od /I ".." /D "WIN32" /D "_DEBUG" /D "_CONSOLE" /D "_MBCS" /FD /GZ /c +# SUBTRACT CPP /YX +# ADD BASE RSC /l 0x409 /d "_DEBUG" +# ADD RSC /l 0x409 /d "_DEBUG" +BSC32=bscmake.exe +# ADD BASE BSC32 /nologo +# ADD BSC32 /nologo +LINK32=link.exe +# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:console /debug /machine:I386 /pdbtype:sept +# ADD LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:console /debug /machine:I386 /pdbtype:sept + +!ENDIF + +# Begin Target + +# Name "herringbone_map - Win32 Release" +# Name "herringbone_map - Win32 Debug" +# Begin Source File + +SOURCE=.\herringbone_map.c +# End Source File +# Begin Source File + +SOURCE=..\stb_herringbone_wang_tile.h +# End Source File +# End Target +# End Project diff --git a/impeller/third_party/stb/stb/tests/image_test.c b/impeller/third_party/stb/stb/tests/image_test.c new file mode 100644 index 0000000000000..b4497443273bf --- /dev/null +++ b/impeller/third_party/stb/stb/tests/image_test.c @@ -0,0 +1,166 @@ +#define STB_IMAGE_WRITE_IMPLEMENTATION +#include "stb_image_write.h" + +#define STB_IMAGE_IMPLEMENTATION +#include "stb_image.h" + +#define STB_DEFINE +#include "stb.h" + +//#define PNGSUITE_PRIMARY + +#if 0 +void test_ycbcr(void) +{ + STBI_SIMD_ALIGN(unsigned char, y[256]); + STBI_SIMD_ALIGN(unsigned char, cb[256]); + STBI_SIMD_ALIGN(unsigned char, cr[256]); + STBI_SIMD_ALIGN(unsigned char, out1[256][4]); + STBI_SIMD_ALIGN(unsigned char, out2[256][4]); + + int i,j,k; + int count = 0, bigcount=0, total=0; + + for (i=0; i < 256; ++i) { + for (j=0; j < 256; ++j) { + for (k=0; k < 256; ++k) { + y [k] = k; + cb[k] = j; + cr[k] = i; + } + stbi__YCbCr_to_RGB_row(out1[0], y, cb, cr, 256, 4); + stbi__YCbCr_to_RGB_sse2(out2[0], y, cb, cr, 256, 4); + for (k=0; k < 256; ++k) { + // inaccurate proxy for values outside of RGB cube + if (out1[k][0] == 0 || out1[k][1] == 0 || out1[k][2] == 0 || out1[k][0] == 255 || out1[k][1] == 255 || out1[k][2] == 255) + continue; + ++total; + if (out1[k][0] != out2[k][0] || out1[k][1] != out2[k][1] || out1[k][2] != out2[k][2]) { + int dist1 = abs(out1[k][0] - out2[k][0]); + int dist2 = abs(out1[k][1] - out2[k][1]); + int dist3 = abs(out1[k][2] - out2[k][2]); + ++count; + if (out1[k][1] > out2[k][1]) + ++bigcount; + } + } + } + printf("So far: %d (%d big) of %d\n", count, bigcount, total); + } + printf("Final: %d (%d big) of %d\n", count, bigcount, total); +} +#endif + +float hdr_data[200][200][3]; + +void dummy_write(void *context, void *data, int len) +{ + static char dummy[1024]; + if (len > 1024) len = 1024; + memcpy(dummy, data, len); +} + +int main(int argc, char **argv) +{ + int w,h; + //test_ycbcr(); + + #if 0 + // test hdr asserts + for (h=0; h < 100; h += 2) + for (w=0; w < 200; ++w) + hdr_data[h][w][0] = (float) rand(), + hdr_data[h][w][1] = (float) rand(), + hdr_data[h][w][2] = (float) rand(); + + stbi_write_hdr("output/test.hdr", 200,200,3,hdr_data[0][0]); + #endif + + if (argc > 1) { + int i, n; + + for (i=1; i < argc; ++i) { + int res; + int w2,h2,n2; + unsigned char *data; + printf("%s\n", argv[i]); + res = stbi_info(argv[1], &w2, &h2, &n2); + data = stbi_load(argv[i], &w, &h, &n, 4); if (data) free(data); else printf("Failed &n\n"); + data = stbi_load(argv[i], &w, &h, 0, 1); if (data) free(data); else printf("Failed 1\n"); + data = stbi_load(argv[i], &w, &h, 0, 2); if (data) free(data); else printf("Failed 2\n"); + data = stbi_load(argv[i], &w, &h, 0, 3); if (data) free(data); else printf("Failed 3\n"); + data = stbi_load(argv[i], &w, &h, &n, 4); + assert(data); + assert(w == w2 && h == h2 && n == n2); + assert(res); + if (data) { + char fname[512]; + stb_splitpath(fname, argv[i], STB_FILE); + stbi_write_png(stb_sprintf("output/%s.png", fname), w, h, 4, data, w*4); + stbi_write_bmp(stb_sprintf("output/%s.bmp", fname), w, h, 4, data); + stbi_write_tga(stb_sprintf("output/%s.tga", fname), w, h, 4, data); + stbi_write_png_to_func(dummy_write,0, w, h, 4, data, w*4); + stbi_write_bmp_to_func(dummy_write,0, w, h, 4, data); + stbi_write_tga_to_func(dummy_write,0, w, h, 4, data); + free(data); + } else + printf("FAILED 4\n"); + } + } else { + int i, nope=0; + #ifdef PNGSUITE_PRIMARY + char **files = stb_readdir_files("pngsuite/primary"); + #else + char **files = stb_readdir_files("images"); + #endif + for (i=0; i < stb_arr_len(files); ++i) { + int n; + char **failed = NULL; + unsigned char *data; + printf("."); + //printf("%s\n", files[i]); + data = stbi_load(files[i], &w, &h, &n, 0); if (data) free(data); else stb_arr_push(failed, "&n"); + data = stbi_load(files[i], &w, &h, 0, 1); if (data) free(data); else stb_arr_push(failed, "1"); + data = stbi_load(files[i], &w, &h, 0, 2); if (data) free(data); else stb_arr_push(failed, "2"); + data = stbi_load(files[i], &w, &h, 0, 3); if (data) free(data); else stb_arr_push(failed, "3"); + data = stbi_load(files[i], &w, &h, 0, 4); if (data) ; else stb_arr_push(failed, "4"); + if (data) { + char fname[512]; + + #ifdef PNGSUITE_PRIMARY + int w2,h2; + unsigned char *data2; + stb_splitpath(fname, files[i], STB_FILE_EXT); + data2 = stbi_load(stb_sprintf("pngsuite/primary_check/%s", fname), &w2, &h2, 0, 4); + if (!data2) + printf("FAILED: couldn't load 'pngsuite/primary_check/%s\n", fname); + else { + if (w != w2 || h != w2 || 0 != memcmp(data, data2, w*h*4)) { + int x,y,c; + if (w == w2 && h == h2) + for (y=0; y < h; ++y) + for (x=0; x < w; ++x) + for (c=0; c < 4; ++c) + assert(data[y*w*4+x*4+c] == data2[y*w*4+x*4+c]); + printf("FAILED: %s loaded but didn't match PRIMARY_check 32-bit version\n", files[i]); + } + free(data2); + } + #else + stb_splitpath(fname, files[i], STB_FILE); + stbi_write_png(stb_sprintf("output/%s.png", fname), w, h, 4, data, w*4); + #endif + free(data); + } + if (failed) { + int j; + printf("FAILED: "); + for (j=0; j < stb_arr_len(failed); ++j) + printf("%s ", failed[j]); + printf(" -- %s\n", files[i]); + } + } + printf("Tested %d files.\n", i); + } + return 0; +} diff --git a/impeller/third_party/stb/stb/tests/image_test.dsp b/impeller/third_party/stb/stb/tests/image_test.dsp new file mode 100644 index 0000000000000..840ea16dbe893 --- /dev/null +++ b/impeller/third_party/stb/stb/tests/image_test.dsp @@ -0,0 +1,97 @@ +# Microsoft Developer Studio Project File - Name="image_test" - Package Owner=<4> +# Microsoft Developer Studio Generated Build File, Format Version 6.00 +# ** DO NOT EDIT ** + +# TARGTYPE "Win32 (x86) Console Application" 0x0103 + +CFG=image_test - Win32 Debug +!MESSAGE This is not a valid makefile. To build this project using NMAKE, +!MESSAGE use the Export Makefile command and run +!MESSAGE +!MESSAGE NMAKE /f "image_test.mak". +!MESSAGE +!MESSAGE You can specify a configuration when running NMAKE +!MESSAGE by defining the macro CFG on the command line. For example: +!MESSAGE +!MESSAGE NMAKE /f "image_test.mak" CFG="image_test - Win32 Debug" +!MESSAGE +!MESSAGE Possible choices for configuration are: +!MESSAGE +!MESSAGE "image_test - Win32 Release" (based on "Win32 (x86) Console Application") +!MESSAGE "image_test - Win32 Debug" (based on "Win32 (x86) Console Application") +!MESSAGE + +# Begin Project +# PROP AllowPerConfigDependencies 0 +# PROP Scc_ProjName "" +# PROP Scc_LocalPath "" +CPP=cl.exe +RSC=rc.exe + +!IF "$(CFG)" == "image_test - Win32 Release" + +# PROP BASE Use_MFC 0 +# PROP BASE Use_Debug_Libraries 0 +# PROP BASE Output_Dir "Release" +# PROP BASE Intermediate_Dir "Release" +# PROP BASE Target_Dir "" +# PROP Use_MFC 0 +# PROP Use_Debug_Libraries 0 +# PROP Output_Dir "Release" +# PROP Intermediate_Dir "Release" +# PROP Target_Dir "" +# ADD BASE CPP /nologo /W3 /GX /O2 /D "WIN32" /D "NDEBUG" /D "_CONSOLE" /D "_MBCS" /YX /FD /c +# ADD CPP /nologo /W3 /GX /O2 /I ".." /D "WIN32" /D "NDEBUG" /D "_CONSOLE" /D "_MBCS" /YX /FD /c +# ADD BASE RSC /l 0x409 /d "NDEBUG" +# ADD RSC /l 0x409 /d "NDEBUG" +BSC32=bscmake.exe +# ADD BASE BSC32 /nologo +# ADD BSC32 /nologo +LINK32=link.exe +# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:console /machine:I386 +# ADD LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:console /machine:I386 + +!ELSEIF "$(CFG)" == "image_test - Win32 Debug" + +# PROP BASE Use_MFC 0 +# PROP BASE Use_Debug_Libraries 1 +# PROP BASE Output_Dir "Debug" +# PROP BASE Intermediate_Dir "Debug" +# PROP BASE Target_Dir "" +# PROP Use_MFC 0 +# PROP Use_Debug_Libraries 1 +# PROP Output_Dir "Debug" +# PROP Intermediate_Dir "Debug\image_test" +# PROP Target_Dir "" +# ADD BASE CPP /nologo /W3 /Gm /GX /ZI /Od /D "WIN32" /D "_DEBUG" /D "_CONSOLE" /D "_MBCS" /YX /FD /GZ /c +# ADD CPP /nologo /W3 /Gm /GX /ZI /Od /I ".." /D "WIN32" /D "_DEBUG" /D "_CONSOLE" /D "_MBCS" /FD /GZ /c +# SUBTRACT CPP /YX +# ADD BASE RSC /l 0x409 /d "_DEBUG" +# ADD RSC /l 0x409 /d "_DEBUG" +BSC32=bscmake.exe +# ADD BASE BSC32 /nologo +# ADD BSC32 /nologo +LINK32=link.exe +# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:console /debug /machine:I386 /pdbtype:sept +# ADD LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:console /debug /machine:I386 /pdbtype:sept + +!ENDIF + +# Begin Target + +# Name "image_test - Win32 Release" +# Name "image_test - Win32 Debug" +# Begin Source File + +SOURCE=.\image_test.c +# End Source File +# Begin Source File + +SOURCE=..\stb_image.h +# End Source File +# Begin Source File + +SOURCE=..\stb_image_write.h +# End Source File +# End Target +# End Project diff --git a/impeller/third_party/stb/stb/tests/oversample/README.md b/impeller/third_party/stb/stb/tests/oversample/README.md new file mode 100644 index 0000000000000..cdfdfff5322cb --- /dev/null +++ b/impeller/third_party/stb/stb/tests/oversample/README.md @@ -0,0 +1,94 @@ +# Font character oversampling for rendering from atlas textures + +TL,DR: Run oversample.exe on a windows machine to see the +benefits of oversampling. It will try to use arial.ttf from the +Windows font directory unless you type the name of a .ttf file as +a command-line argument. + +## Benefits of oversampling + +Oversampling is a mechanism for improving subpixel rendering of characters. + +Improving subpixel has a few benefits: + +* With horizontal-oversampling, text can remain sharper while still being sub-pixel positioned for better kerning +* Horizontally-oversampled text significantly reduces aliasing when text animates horizontally +* Vertically-oversampled text significantly reduces aliasing when text animates vertically +* Text oversampled in both directions significantly reduces aliasing when text rotates + +## What text oversampling is + +A common strategy for rendering text is to cache character bitmaps +and reuse them. For hinted characters, every instance of a given +character is always identical, so this works fine. However, stb_truetype +doesn't do hinting. + +For anti-aliased characters, you can actually position the characters +with subpixel precision, and get different bitmaps based on that positioning +if you re-render the vector data. + +However, if you simply cache a single version of the bitmap and +draw it at different subpixel positions with a GPU, you will get +either the exact same result (if you use point-sampling on the +texture) or linear filtering. Linear filtering will cause a sub-pixel +positioned bitmap to blur further, causing a visible de-sharpening +of the character. (And, since the character wasn't hinted, it was +already blurrier than a hinted one would be, and now it gets even +more blurry.) + +You can avoid this by caching multiple variants of a character which +were rendered independently from the vector data. For example, you +might cache 3 versions of a char, at 0, 1/3, and 2/3rds of a pixel +horizontal offset, and always require characters to fall on integer +positions vertically. + +When creating a texture atlas for use on GPUs, which support bilinear +filtering, there is a better approach than caching several independent +positions, which is to allow lerping between the versions to allow +finer subpixel positioning. You can achieve these by interleaving +each of the cached bitmaps, but this turns out to be mathematically +equivalent to a simpler operation: oversampling and prefiltering the +characters. + +So, setting oversampling of 2x2 in stb_truetype is equivalent to caching +each character in 4 different variations, 1 for each subpixel position +in a 2x2 set. + +An advantage of this formulation is that no changes are required to +the rendering code; the exact same quad-rendering code works, it just +uses different texture coordinates. (Note this does potentially increase +texture bandwidth for text rendering since we end up minifying the texture +without using mipmapping, but you probably are not going to be fill-bound +by your text rendering.) + +## What about gamma? + +Gamma-correction for fonts just doesn't work. This doesn't seem to make +much sense -- it's physically correct, it simulates what we'd see if you +shrunk a font down really far, right? + +But you can play with it in the oversample.exe app. If you turn it on, +white-on-black fonts become too thick (i.e. they become too bright), and +black-on-white fonts become too thin (i.e. they are insufficiently dark). There is +no way to adjust the font's inherent thickness (i.e. by switching to +bold) to fix this for both; making the font thicker will make white +text worse, and making the font thinner will make black text worse. +Obviously you could use different fonts for light and dark cases, but +this doesn't seem like a very good way for fonts to work. + +Multiple people who have experimented with this independently (me, +Fabian Giesen,and Maxim Shemanarev of Anti-Grain Geometry) have all +concluded that correct gamma-correction does not produce the best +results for fonts. Font rendering just generally looks better without +gamma correction (or possibly with some arbitrary power stuck in +there, but it's not really correcting for gamma at that point). Maybe +this is in part a product of how we're used to fonts being on screens +which has changed how we expect them to look (e.g. perhaps hinting +oversharpens them and prevents the real-world thinning you'd see in +a black-on-white text). + +(AGG link on text rendering, including mention of gamma: + http://www.antigrain.com/research/font_rasterization/ ) + +Nevertheless, even if you turn on gamma-correction, you will find that +oversampling still helps in many cases for small fonts. diff --git a/impeller/third_party/stb/stb/tests/oversample/main.c b/impeller/third_party/stb/stb/tests/oversample/main.c new file mode 100644 index 0000000000000..bc6bd0f34e0c9 --- /dev/null +++ b/impeller/third_party/stb/stb/tests/oversample/main.c @@ -0,0 +1,332 @@ +#pragma warning(disable:4244; disable:4305; disable:4018) +#include +#include + +#define STB_WINMAIN +#include "stb_wingraph.h" + +#define STB_TRUETYPE_IMPLEMENTATION +#define STB_RECT_PACK_IMPLEMENTATION +#include "stb_rect_pack.h" +#include "stb_truetype.h" + +#ifndef WINGDIAPI +#define CALLBACK __stdcall +#define WINGDIAPI __declspec(dllimport) +#define APIENTRY __stdcall +#endif + +#include +#include + +#define GL_FRAMEBUFFER_SRGB_EXT 0x8DB9 + +#define SIZE_X 1024 +#define SIZE_Y 768 + +stbtt_packedchar chardata[6][128]; + +int sx=SIZE_X, sy=SIZE_Y; + +#define BITMAP_W 512 +#define BITMAP_H 512 +unsigned char temp_bitmap[BITMAP_W][BITMAP_H]; +unsigned char ttf_buffer[1 << 25]; +GLuint font_tex; + +float scale[2] = { 24.0f, 14.0f }; + +int sf[6] = { 0,1,2, 0,1,2 }; + +void load_fonts(void) +{ + stbtt_pack_context pc; + int i; + FILE *f; + char filename[256]; + char *win = getenv("windir"); + if (win == NULL) win = getenv("SystemRoot"); + + f = fopen(stb_wingraph_commandline, "rb"); + if (!f) { + if (win == NULL) + sprintf(filename, "arial.ttf", win); + else + sprintf(filename, "%s/fonts/arial.ttf", win); + f = fopen(filename, "rb"); + if (!f) exit(0); + } + + fread(ttf_buffer, 1, 1<<25, f); + + stbtt_PackBegin(&pc, temp_bitmap[0], BITMAP_W, BITMAP_H, 0, 1, NULL); + for (i=0; i < 2; ++i) { + stbtt_PackSetOversampling(&pc, 1, 1); + stbtt_PackFontRange(&pc, ttf_buffer, 0, scale[i], 32, 95, chardata[i*3+0]+32); + stbtt_PackSetOversampling(&pc, 2, 2); + stbtt_PackFontRange(&pc, ttf_buffer, 0, scale[i], 32, 95, chardata[i*3+1]+32); + stbtt_PackSetOversampling(&pc, 3, 1); + stbtt_PackFontRange(&pc, ttf_buffer, 0, scale[i], 32, 95, chardata[i*3+2]+32); + } + stbtt_PackEnd(&pc); + + glGenTextures(1, &font_tex); + glBindTexture(GL_TEXTURE_2D, font_tex); + glTexImage2D(GL_TEXTURE_2D, 0, GL_ALPHA, BITMAP_W, BITMAP_H, 0, GL_ALPHA, GL_UNSIGNED_BYTE, temp_bitmap); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); +} + +int black_on_white; + +void draw_init(void) +{ + glDisable(GL_CULL_FACE); + glDisable(GL_TEXTURE_2D); + glDisable(GL_LIGHTING); + glDisable(GL_DEPTH_TEST); + + glViewport(0,0,sx,sy); + if (black_on_white) + glClearColor(255,255,255,0); + else + glClearColor(0,0,0,0); + glClear(GL_COLOR_BUFFER_BIT); + + glMatrixMode(GL_PROJECTION); + glLoadIdentity(); + gluOrtho2D(0,sx,sy,0); + glMatrixMode(GL_MODELVIEW); + glLoadIdentity(); +} + + +void drawBoxTC(float x0, float y0, float x1, float y1, float s0, float t0, float s1, float t1) +{ + glTexCoord2f(s0,t0); glVertex2f(x0,y0); + glTexCoord2f(s1,t0); glVertex2f(x1,y0); + glTexCoord2f(s1,t1); glVertex2f(x1,y1); + glTexCoord2f(s0,t1); glVertex2f(x0,y1); +} + +int integer_align; + +void print(float x, float y, int font, char *text) +{ + glEnable(GL_TEXTURE_2D); + glBindTexture(GL_TEXTURE_2D, font_tex); + glBegin(GL_QUADS); + while (*text) { + stbtt_aligned_quad q; + stbtt_GetPackedQuad(chardata[font], BITMAP_W, BITMAP_H, *text++, &x, &y, &q, font ? 0 : integer_align); + drawBoxTC(q.x0,q.y0,q.x1,q.y1, q.s0,q.t0,q.s1,q.t1); + } + glEnd(); +} + +int font=3; +int translating; +int rotating=0; +int srgb=0; +float rotate_t, translate_t; +int show_tex; + +void draw_world(void) +{ + int sfont = sf[font]; + float x = 20; + glEnable(GL_BLEND); + glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); + + if (black_on_white) + glColor3f(0,0,0); + else + glColor3f(1,1,1); + + + print(80, 30, sfont, "Controls:"); + print(100, 60, sfont, "S: toggle font size"); + print(100, 85, sfont, "O: toggle oversampling"); + print(100,110, sfont, "T: toggle translation"); + print(100,135, sfont, "R: toggle rotation"); + print(100,160, sfont, "P: toggle pixel-snap (only non-oversampled)"); + print(100,185, sfont, "G: toggle srgb gamma-correction"); + if (black_on_white) + print(100,210, sfont, "B: toggle to white-on-black"); + else + print(100,210, sfont, "B: toggle to black-on-white"); + print(100,235, sfont, "V: view font texture"); + + print(80, 300, sfont, "Current font:"); + + if (!show_tex) { + if (font < 3) + print(100, 350, sfont, "Font height: 24 pixels"); + else + print(100, 350, sfont, "Font height: 14 pixels"); + } + + if (font%3==1) + print(100, 325, sfont, "2x2 oversampled text at 1:1"); + else if (font%3 == 2) + print(100, 325, sfont, "3x1 oversampled text at 1:1"); + else if (integer_align) + print(100, 325, sfont, "1:1 text, one texel = one pixel, snapped to integer coordinates"); + else + print(100, 325, sfont, "1:1 text, one texel = one pixel"); + + if (show_tex) { + glBegin(GL_QUADS); + drawBoxTC(200,400, 200+BITMAP_W,300+BITMAP_H, 0,0,1,1); + glEnd(); + } else { + glMatrixMode(GL_MODELVIEW); + glTranslatef(200,350,0); + + if (translating) + x += fmod(translate_t*8,30); + + if (rotating) { + glTranslatef(100,150,0); + glRotatef(rotate_t*2,0,0,1); + glTranslatef(-100,-150,0); + } + print(x,100, font, "This is a test"); + print(x,130, font, "Now is the time for all good men to come to the aid of their country."); + print(x,160, font, "The quick brown fox jumps over the lazy dog."); + print(x,190, font, "0123456789"); + } +} + +void draw(void) +{ + draw_init(); + draw_world(); + stbwingraph_SwapBuffers(NULL); +} + +static int initialized=0; +static float last_dt; + +int move[4]; +int raw_mouse_x, raw_mouse_y; + +int loopmode(float dt, int real, int in_client) +{ + float actual_dt = dt; + + if (!initialized) return 0; + + rotate_t += dt; + translate_t += dt; + +// music_sim(); + if (!real) + return 0; + + if (dt > 0.25) dt = 0.25; + if (dt < 0.01) dt = 0.01; + + draw(); + + return 0; +} + +int winproc(void *data, stbwingraph_event *e) +{ + switch (e->type) { + case STBWGE_create: + break; + + case STBWGE_char: + switch(e->key) { + case 27: + stbwingraph_ShowCursor(NULL,1); + return STBWINGRAPH_winproc_exit; + break; + case 'o': case 'O': + font = (font+1) % 3 + (font/3)*3; + break; + case 's': case 'S': + font = (font+3) % 6; + break; + case 't': case 'T': + translating = !translating; + translate_t = 0; + break; + case 'r': case 'R': + rotating = !rotating; + rotate_t = 0; + break; + case 'p': case 'P': + integer_align = !integer_align; + break; + case 'g': case 'G': + srgb = !srgb; + if (srgb) + glEnable(GL_FRAMEBUFFER_SRGB_EXT); + else + glDisable(GL_FRAMEBUFFER_SRGB_EXT); + break; + case 'v': case 'V': + show_tex = !show_tex; + break; + case 'b': case 'B': + black_on_white = !black_on_white; + break; + } + break; + + case STBWGE_mousemove: + raw_mouse_x = e->mx; + raw_mouse_y = e->my; + break; + +#if 0 + case STBWGE_mousewheel: do_mouse(e,0,0); break; + case STBWGE_leftdown: do_mouse(e, 1,0); break; + case STBWGE_leftup: do_mouse(e,-1,0); break; + case STBWGE_rightdown: do_mouse(e,0, 1); break; + case STBWGE_rightup: do_mouse(e,0,-1); break; +#endif + + case STBWGE_keydown: + if (e->key == VK_RIGHT) move[0] = 1; + if (e->key == VK_LEFT) move[1] = 1; + if (e->key == VK_UP) move[2] = 1; + if (e->key == VK_DOWN) move[3] = 1; + break; + case STBWGE_keyup: + if (e->key == VK_RIGHT) move[0] = 0; + if (e->key == VK_LEFT) move[1] = 0; + if (e->key == VK_UP) move[2] = 0; + if (e->key == VK_DOWN) move[3] = 0; + break; + + case STBWGE_size: + sx = e->width; + sy = e->height; + loopmode(0,1,0); + break; + + case STBWGE_draw: + if (initialized) + loopmode(0,1,0); + break; + + default: + return STBWINGRAPH_unprocessed; + } + return 0; +} + +void stbwingraph_main(void) +{ + stbwingraph_Priority(2); + stbwingraph_CreateWindow(1, winproc, NULL, "tt", SIZE_X,SIZE_Y, 0, 1, 0, 0); + stbwingraph_ShowCursor(NULL, 0); + load_fonts(); + initialized = 1; + stbwingraph_MainLoop(loopmode, 0.016f); // 30 fps = 0.33 +} + diff --git a/impeller/third_party/stb/stb/tests/oversample/oversample.dsp b/impeller/third_party/stb/stb/tests/oversample/oversample.dsp new file mode 100644 index 0000000000000..cc1edc3f5f19c --- /dev/null +++ b/impeller/third_party/stb/stb/tests/oversample/oversample.dsp @@ -0,0 +1,97 @@ +# Microsoft Developer Studio Project File - Name="oversample" - Package Owner=<4> +# Microsoft Developer Studio Generated Build File, Format Version 6.00 +# ** DO NOT EDIT ** + +# TARGTYPE "Win32 (x86) Application" 0x0101 + +CFG=oversample - Win32 Debug +!MESSAGE This is not a valid makefile. To build this project using NMAKE, +!MESSAGE use the Export Makefile command and run +!MESSAGE +!MESSAGE NMAKE /f "oversample.mak". +!MESSAGE +!MESSAGE You can specify a configuration when running NMAKE +!MESSAGE by defining the macro CFG on the command line. For example: +!MESSAGE +!MESSAGE NMAKE /f "oversample.mak" CFG="oversample - Win32 Debug" +!MESSAGE +!MESSAGE Possible choices for configuration are: +!MESSAGE +!MESSAGE "oversample - Win32 Release" (based on "Win32 (x86) Application") +!MESSAGE "oversample - Win32 Debug" (based on "Win32 (x86) Application") +!MESSAGE + +# Begin Project +# PROP AllowPerConfigDependencies 0 +# PROP Scc_ProjName "" +# PROP Scc_LocalPath "" +CPP=cl.exe +MTL=midl.exe +RSC=rc.exe + +!IF "$(CFG)" == "oversample - Win32 Release" + +# PROP BASE Use_MFC 0 +# PROP BASE Use_Debug_Libraries 0 +# PROP BASE Output_Dir "Release" +# PROP BASE Intermediate_Dir "Release" +# PROP BASE Target_Dir "" +# PROP Use_MFC 0 +# PROP Use_Debug_Libraries 0 +# PROP Output_Dir "Release" +# PROP Intermediate_Dir "Release" +# PROP Ignore_Export_Lib 0 +# PROP Target_Dir "" +# ADD BASE CPP /nologo /W3 /GX /O2 /D "WIN32" /D "NDEBUG" /D "_WINDOWS" /D "_MBCS" /YX /FD /c +# ADD CPP /nologo /W3 /WX /GX /O2 /I "c:\sean\prj\stb" /D "WIN32" /D "NDEBUG" /D "_WINDOWS" /D "_MBCS" /FD /c +# SUBTRACT CPP /YX +# ADD BASE MTL /nologo /D "NDEBUG" /mktyplib203 /win32 +# ADD MTL /nologo /D "NDEBUG" /mktyplib203 /win32 +# ADD BASE RSC /l 0x409 /d "NDEBUG" +# ADD RSC /l 0x409 /d "NDEBUG" +BSC32=bscmake.exe +# ADD BASE BSC32 /nologo +# ADD BSC32 /nologo +LINK32=link.exe +# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:windows /machine:I386 +# ADD LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:windows /machine:I386 +# SUBTRACT LINK32 /map /debug + +!ELSEIF "$(CFG)" == "oversample - Win32 Debug" + +# PROP BASE Use_MFC 0 +# PROP BASE Use_Debug_Libraries 1 +# PROP BASE Output_Dir "Debug" +# PROP BASE Intermediate_Dir "Debug" +# PROP BASE Target_Dir "" +# PROP Use_MFC 0 +# PROP Use_Debug_Libraries 1 +# PROP Output_Dir "Debug" +# PROP Intermediate_Dir "Debug" +# PROP Ignore_Export_Lib 0 +# PROP Target_Dir "" +# ADD BASE CPP /nologo /W3 /Gm /GX /ZI /Od /D "WIN32" /D "_DEBUG" /D "_WINDOWS" /D "_MBCS" /YX /FD /GZ /c +# ADD CPP /nologo /W3 /WX /Gm /GX /Zi /Od /I "c:\sean\prj\stb" /D "WIN32" /D "_DEBUG" /D "_WINDOWS" /D "_MBCS" /FD /GZ /c +# ADD BASE MTL /nologo /D "_DEBUG" /mktyplib203 /win32 +# ADD MTL /nologo /D "_DEBUG" /mktyplib203 /win32 +# ADD BASE RSC /l 0x409 /d "_DEBUG" +# ADD RSC /l 0x409 /d "_DEBUG" +BSC32=bscmake.exe +# ADD BASE BSC32 /nologo +# ADD BSC32 /nologo +LINK32=link.exe +# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:windows /debug /machine:I386 /pdbtype:sept +# ADD LINK32 kernel32.lib user32.lib gdi32.lib advapi32.lib winspool.lib comdlg32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:windows /incremental:no /debug /machine:I386 /pdbtype:sept + +!ENDIF + +# Begin Target + +# Name "oversample - Win32 Release" +# Name "oversample - Win32 Debug" +# Begin Source File + +SOURCE=.\main.c +# End Source File +# End Target +# End Project diff --git a/impeller/third_party/stb/stb/tests/oversample/oversample.dsw b/impeller/third_party/stb/stb/tests/oversample/oversample.dsw new file mode 100644 index 0000000000000..0f5aa7fae61ff --- /dev/null +++ b/impeller/third_party/stb/stb/tests/oversample/oversample.dsw @@ -0,0 +1,29 @@ +Microsoft Developer Studio Workspace File, Format Version 6.00 +# WARNING: DO NOT EDIT OR DELETE THIS WORKSPACE FILE! + +############################################################################### + +Project: "oversample"=.\oversample.dsp - Package Owner=<4> + +Package=<5> +{{{ +}}} + +Package=<4> +{{{ +}}} + +############################################################################### + +Global: + +Package=<5> +{{{ +}}} + +Package=<3> +{{{ +}}} + +############################################################################### + diff --git a/impeller/third_party/stb/stb/tests/oversample/oversample.exe b/impeller/third_party/stb/stb/tests/oversample/oversample.exe new file mode 100644 index 0000000000000000000000000000000000000000..004069318671fd3a7d13d393cd70a9c7b77a2207 GIT binary patch literal 54272 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 fYY$1 +#endif +#include +#include +#include +#include +#include +#endif + +typedef void * stbwingraph_hwnd; +typedef void * stbwingraph_hinstance; + +enum +{ + STBWINGRAPH_unprocessed = -(1 << 24), + STBWINGRAPH_do_not_show, + STBWINGRAPH_winproc_exit, + STBWINGRAPH_winproc_update, + STBWINGRAPH_update_exit, + STBWINGRAPH_update_pause, +}; + +typedef enum +{ + STBWGE__none=0, + + STBWGE_create, + STBWGE_create_postshow, + STBWGE_draw, + STBWGE_destroy, + STBWGE_char, + STBWGE_keydown, + STBWGE_syskeydown, + STBWGE_keyup, + STBWGE_syskeyup, + STBWGE_deactivate, + STBWGE_activate, + STBWGE_size, + + STBWGE_mousemove , + STBWGE_leftdown , STBWGE_leftup , + STBWGE_middledown, STBWGE_middleup, + STBWGE_rightdown , STBWGE_rightup , + STBWGE_mousewheel, +} stbwingraph_event_type; + +typedef struct +{ + stbwingraph_event_type type; + + // for input events (mouse, keyboard) + int mx,my; // mouse x & y + int dx,dy; + int shift, ctrl, alt; + + // for keyboard events + int key; + + // for STBWGE_size: + int width, height; + + // for STBWGE_crate + int did_share_lists; // if true, wglShareLists succeeded + + void *handle; + +} stbwingraph_event; + +typedef int (*stbwingraph_window_proc)(void *data, stbwingraph_event *event); + +extern stbwingraph_hinstance stbwingraph_app; +extern stbwingraph_hwnd stbwingraph_primary_window; +extern int stbwingraph_request_fullscreen; +extern int stbwingraph_request_windowed; + +STB_EXTERN void stbwingraph_ods(char *str, ...); +STB_EXTERN int stbwingraph_MessageBox(stbwingraph_hwnd win, unsigned int type, + char *caption, char *text, ...); +STB_EXTERN int stbwingraph_ChangeResolution(unsigned int w, unsigned int h, + unsigned int bits, int use_message_box); +STB_EXTERN int stbwingraph_SetPixelFormat(stbwingraph_hwnd win, int color_bits, + int alpha_bits, int depth_bits, int stencil_bits, int accum_bits); +STB_EXTERN int stbwingraph_DefineClass(void *hinstance, char *iconname); +STB_EXTERN void stbwingraph_SwapBuffers(void *win); +STB_EXTERN void stbwingraph_Priority(int n); + +STB_EXTERN void stbwingraph_MakeFonts(void *window, int font_base); +STB_EXTERN void stbwingraph_ShowWindow(void *window); +STB_EXTERN void *stbwingraph_CreateWindow(int primary, stbwingraph_window_proc func, void *data, char *text, int width, int height, int fullscreen, int resizeable, int dest_alpha, int stencil); +STB_EXTERN void *stbwingraph_CreateWindowSimple(stbwingraph_window_proc func, int width, int height); +STB_EXTERN void *stbwingraph_CreateWindowSimpleFull(stbwingraph_window_proc func, int fullscreen, int ww, int wh, int fw, int fh); +STB_EXTERN void stbwingraph_DestroyWindow(void *window); +STB_EXTERN void stbwingraph_ShowCursor(void *window, int visible); +STB_EXTERN float stbwingraph_GetTimestep(float minimum_time); +STB_EXTERN void stbwingraph_SetGLWindow(void *win); +typedef int (*stbwingraph_update)(float timestep, int real, int in_client); +STB_EXTERN int stbwingraph_MainLoop(stbwingraph_update func, float mintime); + +#ifdef STB_DEFINE +stbwingraph_hinstance stbwingraph_app; +stbwingraph_hwnd stbwingraph_primary_window; +int stbwingraph_request_fullscreen; +int stbwingraph_request_windowed; + +void stbwingraph_ods(char *str, ...) +{ + char buffer[1024]; + va_list v; + va_start(v,str); + vsprintf(buffer, str, v); + va_end(v); + OutputDebugString(buffer); +} + +int stbwingraph_MessageBox(stbwingraph_hwnd win, unsigned int type, char *caption, char *text, ...) +{ + va_list v; + char buffer[1024]; + va_start(v, text); + vsprintf(buffer, text, v); + va_end(v); + return MessageBox(win, buffer, caption, type); +} + +void stbwingraph_Priority(int n) +{ + int p; + switch (n) { + case -1: p = THREAD_PRIORITY_BELOW_NORMAL; break; + case 0: p = THREAD_PRIORITY_NORMAL; break; + case 1: p = THREAD_PRIORITY_ABOVE_NORMAL; break; + default: + if (n < 0) p = THREAD_PRIORITY_LOWEST; + else p = THREAD_PRIORITY_HIGHEST; + } + SetThreadPriority(GetCurrentThread(), p); +} + +static void stbwingraph_ResetResolution(void) +{ + ChangeDisplaySettings(NULL, 0); +} + +static void stbwingraph_RegisterResetResolution(void) +{ + static int done=0; + if (!done) { + done = 1; + atexit(stbwingraph_ResetResolution); + } +} + +int stbwingraph_ChangeResolution(unsigned int w, unsigned int h, unsigned int bits, int use_message_box) +{ + DEVMODE mode; + int res; + + int i, tries=0; + for (i=0; ; ++i) { + int success = EnumDisplaySettings(NULL, i, &mode); + if (!success) break; + if (mode.dmBitsPerPel == bits && mode.dmPelsWidth == w && mode.dmPelsHeight == h) { + ++tries; + success = ChangeDisplaySettings(&mode, CDS_FULLSCREEN); + if (success == DISP_CHANGE_SUCCESSFUL) { + stbwingraph_RegisterResetResolution(); + return TRUE; + } + break; + } + } + + if (!tries) { + if (use_message_box) + stbwingraph_MessageBox(stbwingraph_primary_window, MB_ICONERROR, NULL, "The resolution %d x %d x %d-bits is not supported.", w, h, bits); + return FALSE; + } + + // we tried but failed, so try explicitly doing it without specifying refresh rate + + // Win95 support logic + mode.dmBitsPerPel = bits; + mode.dmPelsWidth = w; + mode.dmPelsHeight = h; + mode.dmFields = DM_BITSPERPEL | DM_PELSWIDTH | DM_PELSHEIGHT; + + res = ChangeDisplaySettings(&mode, CDS_FULLSCREEN); + + switch (res) { + case DISP_CHANGE_SUCCESSFUL: + stbwingraph_RegisterResetResolution(); + return TRUE; + + case DISP_CHANGE_RESTART: + if (use_message_box) + stbwingraph_MessageBox(stbwingraph_primary_window, MB_ICONERROR, NULL, "Please set your desktop to %d-bit color and then try again."); + return FALSE; + + case DISP_CHANGE_FAILED: + if (use_message_box) + stbwingraph_MessageBox(stbwingraph_primary_window, MB_ICONERROR, NULL, "The hardware failed to change modes."); + return FALSE; + + case DISP_CHANGE_BADMODE: + if (use_message_box) + stbwingraph_MessageBox(stbwingraph_primary_window, MB_ICONERROR, NULL, "The resolution %d x %d x %d-bits is not supported.", w, h, bits); + return FALSE; + + default: + if (use_message_box) + stbwingraph_MessageBox(stbwingraph_primary_window, MB_ICONERROR, NULL, "An unknown error prevented a change to a %d x %d x %d-bit display.", w, h, bits); + return FALSE; + } +} + +int stbwingraph_SetPixelFormat(stbwingraph_hwnd win, int color_bits, int alpha_bits, int depth_bits, int stencil_bits, int accum_bits) +{ + HDC dc = GetDC(win); + PIXELFORMATDESCRIPTOR pfd = { sizeof(pfd) }; + int pixel_format; + + pfd.nVersion = 1; + pfd.dwFlags = PFD_SUPPORT_OPENGL | PFD_DRAW_TO_WINDOW | PFD_DOUBLEBUFFER; + pfd.dwLayerMask = PFD_MAIN_PLANE; + pfd.iPixelType = PFD_TYPE_RGBA; + pfd.cColorBits = color_bits; + pfd.cAlphaBits = alpha_bits; + pfd.cDepthBits = depth_bits; + pfd.cStencilBits = stencil_bits; + pfd.cAccumBits = accum_bits; + + pixel_format = ChoosePixelFormat(dc, &pfd); + if (!pixel_format) return FALSE; + + if (!DescribePixelFormat(dc, pixel_format, sizeof(PIXELFORMATDESCRIPTOR), &pfd)) + return FALSE; + SetPixelFormat(dc, pixel_format, &pfd); + + return TRUE; +} + +typedef struct +{ + // app data + stbwingraph_window_proc func; + void *data; + // creation parameters + int color, alpha, depth, stencil, accum; + HWND share_window; + HWND window; + // internal data + HGLRC rc; + HDC dc; + int hide_mouse; + int in_client; + int active; + int did_share_lists; + int mx,my; // last mouse positions +} stbwingraph__window; + +static void stbwingraph__inclient(stbwingraph__window *win, int state) +{ + if (state != win->in_client) { + win->in_client = state; + if (win->hide_mouse) + ShowCursor(!state); + } +} + +static void stbwingraph__key(stbwingraph_event *e, int type, int key, stbwingraph__window *z) +{ + e->type = type; + e->key = key; + e->shift = (GetKeyState(VK_SHIFT) < 0); + e->ctrl = (GetKeyState(VK_CONTROL) < 0); + e->alt = (GetKeyState(VK_MENU) < 0); + if (z) { + e->mx = z->mx; + e->my = z->my; + } else { + e->mx = e->my = 0; + } + e->dx = e->dy = 0; +} + +static void stbwingraph__mouse(stbwingraph_event *e, int type, WPARAM wparam, LPARAM lparam, int capture, void *wnd, stbwingraph__window *z) +{ + static int captured = 0; + e->type = type; + e->mx = (short) LOWORD(lparam); + e->my = (short) HIWORD(lparam); + if (!z || z->mx == -(1 << 30)) { + e->dx = e->dy = 0; + } else { + e->dx = e->mx - z->mx; + e->dy = e->my - z->my; + } + e->shift = (wparam & MK_SHIFT) != 0; + e->ctrl = (wparam & MK_CONTROL) != 0; + e->alt = (wparam & MK_ALT) != 0; + if (z) { + z->mx = e->mx; + z->my = e->my; + } + if (capture) { + if (!captured && capture == 1) + SetCapture(wnd); + captured += capture; + if (!captured && capture == -1) + ReleaseCapture(); + if (captured < 0) captured = 0; + } +} + +static void stbwingraph__mousewheel(stbwingraph_event *e, int type, WPARAM wparam, LPARAM lparam, int capture, void *wnd, stbwingraph__window *z) +{ + // lparam seems bogus! + static int captured = 0; + e->type = type; + if (z) { + e->mx = z->mx; + e->my = z->my; + } + e->dx = e->dy = 0; + e->shift = (wparam & MK_SHIFT) != 0; + e->ctrl = (wparam & MK_CONTROL) != 0; + e->alt = (GetKeyState(VK_MENU) < 0); + e->key = ((int) wparam >> 16); +} + +int stbwingraph_force_update; +static int WINAPI stbwingraph_WinProc(HWND wnd, UINT msg, WPARAM wparam, LPARAM lparam) +{ + int allow_default = TRUE; + stbwingraph_event e = { STBWGE__none }; + // the following line is wrong for 64-bit windows, but VC6 doesn't have GetWindowLongPtr + stbwingraph__window *z = (stbwingraph__window *) GetWindowLong(wnd, GWL_USERDATA); + + switch (msg) { + + case WM_CREATE: + { + LPCREATESTRUCT lpcs = (LPCREATESTRUCT) lparam; + assert(z == NULL); + z = (stbwingraph__window *) lpcs->lpCreateParams; + SetWindowLong(wnd, GWL_USERDATA, (LONG) z); + z->dc = GetDC(wnd); + if (stbwingraph_SetPixelFormat(wnd, z->color, z->alpha, z->depth, z->stencil, z->accum)) { + z->rc = wglCreateContext(z->dc); + if (z->rc) { + e.type = STBWGE_create; + z->did_share_lists = FALSE; + if (z->share_window) { + stbwingraph__window *y = (stbwingraph__window *) GetWindowLong(z->share_window, GWL_USERDATA); + if (wglShareLists(z->rc, y->rc)) + z->did_share_lists = TRUE; + } + wglMakeCurrent(z->dc, z->rc); + return 0; + } + } + return -1; + } + + case WM_PAINT: { + PAINTSTRUCT ps; + HDC hdc = BeginPaint(wnd, &ps); + SelectObject(hdc, GetStockObject(NULL_BRUSH)); + e.type = STBWGE_draw; + e.handle = wnd; + z->func(z->data, &e); + EndPaint(wnd, &ps); + return 0; + } + + case WM_DESTROY: + e.type = STBWGE_destroy; + e.handle = wnd; + if (z && z->func) + z->func(z->data, &e); + wglMakeCurrent(NULL, NULL) ; + if (z) { + if (z->rc) wglDeleteContext(z->rc); + z->dc = 0; + z->rc = 0; + } + if (wnd == stbwingraph_primary_window) + PostQuitMessage (0); + return 0; + + case WM_CHAR: stbwingraph__key(&e, STBWGE_char , wparam, z); break; + case WM_KEYDOWN: stbwingraph__key(&e, STBWGE_keydown, wparam, z); break; + case WM_KEYUP: stbwingraph__key(&e, STBWGE_keyup , wparam, z); break; + + case WM_NCMOUSEMOVE: stbwingraph__inclient(z,0); break; + case WM_MOUSEMOVE: stbwingraph__inclient(z,1); stbwingraph__mouse(&e, STBWGE_mousemove, wparam, lparam,0,wnd, z); break; + case WM_LBUTTONDOWN: stbwingraph__mouse(&e, STBWGE_leftdown, wparam, lparam,1,wnd, z); break; + case WM_MBUTTONDOWN: stbwingraph__mouse(&e, STBWGE_middledown, wparam, lparam,1,wnd, z); break; + case WM_RBUTTONDOWN: stbwingraph__mouse(&e, STBWGE_rightdown, wparam, lparam,1,wnd, z); break; + case WM_LBUTTONUP: stbwingraph__mouse(&e, STBWGE_leftup, wparam, lparam,-1,wnd, z); break; + case WM_MBUTTONUP: stbwingraph__mouse(&e, STBWGE_middleup, wparam, lparam,-1,wnd, z); break; + case WM_RBUTTONUP: stbwingraph__mouse(&e, STBWGE_rightup, wparam, lparam,-1,wnd, z); break; + case WM_MOUSEWHEEL: stbwingraph__mousewheel(&e, STBWGE_mousewheel, wparam, lparam,0,wnd, z); break; + + case WM_ACTIVATE: + allow_default = FALSE; + if (LOWORD(wparam)==WA_INACTIVE ) { + wglMakeCurrent(z->dc, NULL); + e.type = STBWGE_deactivate; + z->active = FALSE; + } else { + wglMakeCurrent(z->dc, z->rc); + e.type = STBWGE_activate; + z->active = TRUE; + } + e.handle = wnd; + z->func(z->data, &e); + return 0; + + case WM_SIZE: { + RECT rect; + allow_default = FALSE; + GetClientRect(wnd, &rect); + e.type = STBWGE_size; + e.width = rect.right; + e.height = rect.bottom; + e.handle = wnd; + z->func(z->data, &e); + return 0; + } + + default: + return DefWindowProc (wnd, msg, wparam, lparam); + } + + if (e.type != STBWGE__none) { + int n; + e.handle = wnd; + n = z->func(z->data, &e); + if (n == STBWINGRAPH_winproc_exit) { + PostQuitMessage(0); + n = 0; + } + if (n == STBWINGRAPH_winproc_update) { + stbwingraph_force_update = TRUE; + return 1; + } + if (n != STBWINGRAPH_unprocessed) + return n; + } + return DefWindowProc (wnd, msg, wparam, lparam); +} + +int stbwingraph_DefineClass(HINSTANCE hInstance, char *iconname) +{ + WNDCLASSEX wndclass; + + stbwingraph_app = hInstance; + + wndclass.cbSize = sizeof(wndclass); + wndclass.style = CS_OWNDC; + wndclass.lpfnWndProc = (WNDPROC) stbwingraph_WinProc; + wndclass.cbClsExtra = 0; + wndclass.cbWndExtra = 0; + wndclass.hInstance = hInstance; + wndclass.hIcon = LoadIcon(hInstance, iconname); + wndclass.hCursor = LoadCursor(NULL,IDC_ARROW); + wndclass.hbrBackground = GetStockObject(NULL_BRUSH); + wndclass.lpszMenuName = "zwingraph"; + wndclass.lpszClassName = "zwingraph"; + wndclass.hIconSm = NULL; + + if (!RegisterClassEx(&wndclass)) + return FALSE; + return TRUE; +} + +void stbwingraph_ShowWindow(void *window) +{ + stbwingraph_event e = { STBWGE_create_postshow }; + stbwingraph__window *z = (stbwingraph__window *) GetWindowLong(window, GWL_USERDATA); + ShowWindow(window, SW_SHOWNORMAL); + InvalidateRect(window, NULL, TRUE); + UpdateWindow(window); + e.handle = window; + z->func(z->data, &e); +} + +void *stbwingraph_CreateWindow(int primary, stbwingraph_window_proc func, void *data, char *text, + int width, int height, int fullscreen, int resizeable, int dest_alpha, int stencil) +{ + HWND win; + DWORD dwstyle; + stbwingraph__window *z = (stbwingraph__window *) malloc(sizeof(*z)); + + if (z == NULL) return NULL; + memset(z, 0, sizeof(*z)); + z->color = 24; + z->depth = 24; + z->alpha = dest_alpha; + z->stencil = stencil; + z->func = func; + z->data = data; + z->mx = -(1 << 30); + z->my = 0; + + if (primary) { + if (stbwingraph_request_windowed) + fullscreen = FALSE; + else if (stbwingraph_request_fullscreen) + fullscreen = TRUE; + } + + if (fullscreen) { + #ifdef STB_SIMPLE + stbwingraph_ChangeResolution(width, height, 32, 1); + #else + if (!stbwingraph_ChangeResolution(width, height, 32, 0)) + return NULL; + #endif + dwstyle = WS_POPUP | WS_CLIPSIBLINGS; + } else { + RECT rect; + dwstyle = WS_OVERLAPPED | WS_CAPTION | WS_SYSMENU | WS_MINIMIZEBOX; + if (resizeable) + dwstyle |= WS_SIZEBOX | WS_MAXIMIZEBOX; + rect.top = 0; + rect.left = 0; + rect.right = width; + rect.bottom = height; + AdjustWindowRect(&rect, dwstyle, FALSE); + width = rect.right - rect.left; + height = rect.bottom - rect.top; + } + + win = CreateWindow("zwingraph", text ? text : "sample", dwstyle, + CW_USEDEFAULT,0, width, height, + NULL, NULL, stbwingraph_app, z); + + if (win == NULL) return win; + + if (primary) { + if (stbwingraph_primary_window) + stbwingraph_DestroyWindow(stbwingraph_primary_window); + stbwingraph_primary_window = win; + } + + { + stbwingraph_event e = { STBWGE_create }; + stbwingraph__window *z = (stbwingraph__window *) GetWindowLong(win, GWL_USERDATA); + z->window = win; + e.did_share_lists = z->did_share_lists; + e.handle = win; + if (z->func(z->data, &e) != STBWINGRAPH_do_not_show) + stbwingraph_ShowWindow(win); + } + + return win; +} + +void *stbwingraph_CreateWindowSimple(stbwingraph_window_proc func, int width, int height) +{ + int fullscreen = 0; + #ifndef _DEBUG + if (width == 640 && height == 480) fullscreen = 1; + if (width == 800 && height == 600) fullscreen = 1; + if (width == 1024 && height == 768) fullscreen = 1; + if (width == 1280 && height == 1024) fullscreen = 1; + if (width == 1600 && height == 1200) fullscreen = 1; + //@TODO: widescreen widths + #endif + return stbwingraph_CreateWindow(1, func, NULL, NULL, width, height, fullscreen, 1, 0, 0); +} + +void *stbwingraph_CreateWindowSimpleFull(stbwingraph_window_proc func, int fullscreen, int ww, int wh, int fw, int fh) +{ + if (fullscreen == -1) { + #ifdef _DEBUG + fullscreen = 0; + #else + fullscreen = 1; + #endif + } + + if (fullscreen) { + if (fw) ww = fw; + if (fh) wh = fh; + } + return stbwingraph_CreateWindow(1, func, NULL, NULL, ww, wh, fullscreen, 1, 0, 0); +} + +void stbwingraph_DestroyWindow(void *window) +{ + stbwingraph__window *z = (stbwingraph__window *) GetWindowLong(window, GWL_USERDATA); + DestroyWindow(window); + free(z); + if (stbwingraph_primary_window == window) + stbwingraph_primary_window = NULL; +} + +void stbwingraph_ShowCursor(void *window, int visible) +{ + int hide; + stbwingraph__window *win; + if (!window) + window = stbwingraph_primary_window; + win = (stbwingraph__window *) GetWindowLong((HWND) window, GWL_USERDATA); + hide = !visible; + if (hide != win->hide_mouse) { + win->hide_mouse = hide; + if (!hide) + ShowCursor(TRUE); + else if (win->in_client) + ShowCursor(FALSE); + } +} + +float stbwingraph_GetTimestep(float minimum_time) +{ + float elapsedTime; + double thisTime; + static double lastTime = -1; + + if (lastTime == -1) + lastTime = timeGetTime() / 1000.0 - minimum_time; + + for(;;) { + thisTime = timeGetTime() / 1000.0; + elapsedTime = (float) (thisTime - lastTime); + if (elapsedTime >= minimum_time) { + lastTime = thisTime; + return elapsedTime; + } + #if 1 + Sleep(2); + #endif + } +} + +void stbwingraph_SetGLWindow(void *win) +{ + stbwingraph__window *z = (stbwingraph__window *) GetWindowLong(win, GWL_USERDATA); + if (z) + wglMakeCurrent(z->dc, z->rc); +} + +void stbwingraph_MakeFonts(void *window, int font_base) +{ + wglUseFontBitmaps(GetDC(window ? window : stbwingraph_primary_window), 0, 256, font_base); +} + +// returns 1 if WM_QUIT, 0 if 'func' returned 0 +int stbwingraph_MainLoop(stbwingraph_update func, float mintime) +{ + int needs_drawing = FALSE; + MSG msg; + + int is_animating = TRUE; + if (mintime <= 0) mintime = 0.01f; + + for(;;) { + int n; + + is_animating = TRUE; + // wait for a message if: (a) we're animating and there's already a message + // or (b) we're not animating + if (!is_animating || PeekMessage(&msg, NULL, 0, 0, PM_NOREMOVE)) { + stbwingraph_force_update = FALSE; + if (GetMessage(&msg, NULL, 0, 0)) { + TranslateMessage(&msg); + DispatchMessage(&msg); + } else { + return 1; // WM_QUIT + } + + // only force a draw for certain messages... + // if I don't do this, we peg at 50% for some reason... must + // be a bug somewhere, because we peg at 100% when rendering... + // very weird... looks like NVIDIA is pumping some messages + // through our pipeline? well, ok, I guess if we can get + // non-user-generated messages we have to do this + if (!stbwingraph_force_update) { + switch (msg.message) { + case WM_MOUSEMOVE: + case WM_NCMOUSEMOVE: + break; + case WM_CHAR: + case WM_KEYDOWN: + case WM_KEYUP: + case WM_LBUTTONDOWN: + case WM_MBUTTONDOWN: + case WM_RBUTTONDOWN: + case WM_LBUTTONUP: + case WM_MBUTTONUP: + case WM_RBUTTONUP: + case WM_TIMER: + case WM_SIZE: + case WM_ACTIVATE: + needs_drawing = TRUE; + break; + } + } else + needs_drawing = TRUE; + } + + // if another message, process that first + // @TODO: i don't think this is working, because I can't key ahead + // in the SVT demo app + if (PeekMessage(&msg, NULL, 0,0, PM_NOREMOVE)) + continue; + + // and now call update + if (needs_drawing || is_animating) { + int real=1, in_client=1; + if (stbwingraph_primary_window) { + stbwingraph__window *z = (stbwingraph__window *) GetWindowLong(stbwingraph_primary_window, GWL_USERDATA); + if (z && !z->active) { + real = 0; + } + if (z) + in_client = z->in_client; + } + + if (stbwingraph_primary_window) + stbwingraph_SetGLWindow(stbwingraph_primary_window); + n = func(stbwingraph_GetTimestep(mintime), real, in_client); + if (n == STBWINGRAPH_update_exit) + return 0; // update_quit + + is_animating = (n != STBWINGRAPH_update_pause); + + needs_drawing = FALSE; + } + } +} + +void stbwingraph_SwapBuffers(void *win) +{ + stbwingraph__window *z; + if (win == NULL) win = stbwingraph_primary_window; + z = (stbwingraph__window *) GetWindowLong(win, GWL_USERDATA); + if (z && z->dc) + SwapBuffers(z->dc); +} +#endif + +#ifdef STB_WINMAIN +void stbwingraph_main(void); + +char *stb_wingraph_commandline; + +int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow) +{ + { + char buffer[1024]; + // add spaces to either side of the string + buffer[0] = ' '; + strcpy(buffer+1, lpCmdLine); + strcat(buffer, " "); + if (strstr(buffer, " -reset ")) { + ChangeDisplaySettings(NULL, 0); + exit(0); + } + if (strstr(buffer, " -window ") || strstr(buffer, " -windowed ")) + stbwingraph_request_windowed = TRUE; + else if (strstr(buffer, " -full ") || strstr(buffer, " -fullscreen ")) + stbwingraph_request_fullscreen = TRUE; + } + stb_wingraph_commandline = lpCmdLine; + + stbwingraph_DefineClass(hInstance, "appicon"); + stbwingraph_main(); + + return 0; +} +#endif + +#undef STB_EXTERN +#ifdef STB_WINGRAPH_DISABLE_DEFINE_AT_END +#undef STB_DEFINE +#endif + +#endif // INCLUDE_STB_WINGRAPH_H diff --git a/impeller/third_party/stb/stb/tests/pngsuite/16bit/basi0g16.png b/impeller/third_party/stb/stb/tests/pngsuite/16bit/basi0g16.png new file mode 100644 index 0000000000000000000000000000000000000000..a9f28165efd4a6eaab890f3f46eaab0812614046 GIT binary patch literal 299 zcmV+`0o4A9P)kMw|y20kXn4P1fk&um!5HR4i(o|AwZ6~LzrfePT$2C@a*GO>pzD`W+JMY+o{~OMQc&lm=3iqj(vZoAg56v)&@pF xpiv;U26q|-VrxJe1@ZNXf_(7)M8WX7;00Eg?uiut|NZ~~002ovPDHLkV1i{YdSUv}5QRU_7&y#fp@D&gr9M`8f1y92FoiBVSa`IsurPtS ze{e7d3sdH>SD>hoE4z_nx$z0??7UgMea|aJNhAr@Zvg8Z*gUmKePu;z>y8jWkvlsq z1W-(qZPlsA;>PQ-xbb>e=+HyjR`72RpJL@9duyYiw{L8ondoV1Dstk)`yNe2E|Tn) zQn&|5Z4$O-o8S$zjgb_v!ImC3RAHrGc4#Vv!)L8;XfO3ToGLXjk#F*79m4!RxgIe_tfnTuJcqEWy)4g2yKb%DDu0za_YxN$~Sgf+CmT>X!ubOoF_W z03=B5#`sg62L3e6{2RJyFI>13c>MtF{pl)#xkJ@t(o7&`wRIOmYhI*_#{U5rtXn@HNfb7oz6Bu5!i$}Tx-z(a!?=gYPiFWah6BvhR7Y~@g_(Z#S%LK+P h+Qpp-{6C^y{2ine-m~8mME3vy002ovPDHLkV1lO|5~BbB literal 0 HcmV?d00001 diff --git a/impeller/third_party/stb/stb/tests/pngsuite/16bit/basi4a16.png b/impeller/third_party/stb/stb/tests/pngsuite/16bit/basi4a16.png new file mode 100644 index 0000000000000000000000000000000000000000..51192e731106e77cec52a3acda3d0c3cd54a16d5 GIT binary patch literal 2855 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 FV1na*GhqM# literal 0 HcmV?d00001 diff --git a/impeller/third_party/stb/stb/tests/pngsuite/16bit/basi6a16.png b/impeller/third_party/stb/stb/tests/pngsuite/16bit/basi6a16.png new file mode 100644 index 0000000000000000000000000000000000000000..4181533ad81b739fb7b4f3b024f632cd7baa85c5 GIT binary patch literal 4180 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 N1)i>cF6*2UngAzTID!BG literal 0 HcmV?d00001 diff --git a/impeller/third_party/stb/stb/tests/pngsuite/16bit/basn2c16.png b/impeller/third_party/stb/stb/tests/pngsuite/16bit/basn2c16.png new file mode 100644 index 0000000000000000000000000000000000000000..50c1cb91a0171e9f34991b079fe932f5f0bb16d6 GIT binary patch literal 302 zcmV+}0nz@6P)NTxus~-YE?|ew94KIo9tHTv?hhRR zwrA%J^h9UxCeRmyPjW#d?oxNFL9(uFDZ1gBle+D$rIj`J+5;}Xa zfF63WfGT3xy1iYa$zve>zUI)9x>;M1&07*qoM6N<$g8PGj A5dZ)H literal 0 HcmV?d00001 diff --git a/impeller/third_party/stb/stb/tests/pngsuite/16bit/basn4a16.png b/impeller/third_party/stb/stb/tests/pngsuite/16bit/basn4a16.png new file mode 100644 index 0000000000000000000000000000000000000000..8243644d0743ebd1fdacb7923069fefbb01dab0e GIT binary patch literal 2206 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 AY5)KL literal 0 HcmV?d00001 diff --git a/impeller/third_party/stb/stb/tests/pngsuite/16bit/bgai4a16.png b/impeller/third_party/stb/stb/tests/pngsuite/16bit/bgai4a16.png new file mode 100644 index 0000000000000000000000000000000000000000..51192e731106e77cec52a3acda3d0c3cd54a16d5 GIT binary patch literal 2855 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 FV1na*GhqM# literal 0 HcmV?d00001 diff --git a/impeller/third_party/stb/stb/tests/pngsuite/16bit/bgan6a16.png b/impeller/third_party/stb/stb/tests/pngsuite/16bit/bgan6a16.png new file mode 100644 index 0000000000000000000000000000000000000000..984a99525f5246cbc5d7083dd79006c7efa0ab0b GIT binary patch literal 3435 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 AY5)KL literal 0 HcmV?d00001 diff --git a/impeller/third_party/stb/stb/tests/pngsuite/16bit/bggn4a16.png b/impeller/third_party/stb/stb/tests/pngsuite/16bit/bggn4a16.png new file mode 100644 index 0000000000000000000000000000000000000000..13fd85ba193369dbd84a0dc07f73d3340485fa6a GIT binary patch literal 2220 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 TFaLS`e*&EE@`;w61 literal 0 HcmV?d00001 diff --git a/impeller/third_party/stb/stb/tests/pngsuite/16bit/oi1n0g16.png b/impeller/third_party/stb/stb/tests/pngsuite/16bit/oi1n0g16.png new file mode 100644 index 0000000000000000000000000000000000000000..e7c82f78eb954be5be51c35bd287e00018c69d82 GIT binary patch literal 167 zcmeAS@N?(olHy`uVBq!ia0vp^3Lq>1BpBEle`W(ImUKs7M+U~W1%@xC#RK_qo-U3d z6?3j$GUPg7z~dZf8U4Qg*qsHF4`@^yUDaY)e6!!)qG9=taAD!(1y)T7vK%uR?lygM zIK}cp><*ign#1-5wiApPcd>47oWOZOH-mqPPlA0u`y2l+k{0Ld8N_aQ--utQJ^wn$ N1)i>cF6*2UngAzTID!BG literal 0 HcmV?d00001 diff --git a/impeller/third_party/stb/stb/tests/pngsuite/16bit/oi1n2c16.png b/impeller/third_party/stb/stb/tests/pngsuite/16bit/oi1n2c16.png new file mode 100644 index 0000000000000000000000000000000000000000..50c1cb91a0171e9f34991b079fe932f5f0bb16d6 GIT binary patch literal 302 zcmV+}0nz@6P)NTxus~-YE?|ew94KIo9tHTv?hhRR zwrA%J^h9UxCeRmyPjW#d?oxNFL9(uFDZ1gBle+D$rIj`J+5;}Xa zfF63WfGT3xy1iYa$zve>zUI)9x>;M1&07*qoM6N<$g8PGj A5dZ)H literal 0 HcmV?d00001 diff --git a/impeller/third_party/stb/stb/tests/pngsuite/16bit/oi2n0g16.png b/impeller/third_party/stb/stb/tests/pngsuite/16bit/oi2n0g16.png new file mode 100644 index 0000000000000000000000000000000000000000..14d64c583db347908d4e107b49bdaf88e3857b8d GIT binary patch literal 179 zcmeAS@N?(olHy`uVBq!ia0vp^3Lq>1BpBEle`W(ImUKs7M+U~W1%@xC#RK^co-U3d z6?3j$GUPg7z~dZf8U4Qg*qsHF4`@^yUDaY)e6!!)qG9=taAD!(1y)T7vK%uR?lygM zIK}cp><*ign#1-5wiAr3HXJYpnk5G^i*Xn0_QnaE7j!fDm-r;u=d-`@{~~E|zMets XhWCy5O;z!=K$Q%hu6{1-oD!MJY5_^ zD&}0Bc95&tK)_}B1IPN)b=&{0{WeXGjd@a=TH53@RT6EkPi(I(=ofHeu+x7q>wQX5 zah-&%x^euuN1vn0w==yySE5m{$awnax1S1PFES>4-qaZX;>{9*dzc{lpeF5*>&z{m79Ma`yb}T3cx+N9l7QKY_b21tUy>hHS zj=$rVW!RSfocD`M`g^qt@_T0=(DrKPRwV_Ejec_{h{I9JdPiZ{w#YI8t;GR^e@i0d|ndJLIzJ) KKbLh*2~7a%CxW>E literal 0 HcmV?d00001 diff --git a/impeller/third_party/stb/stb/tests/pngsuite/16bit/oi4n0g16.png b/impeller/third_party/stb/stb/tests/pngsuite/16bit/oi4n0g16.png new file mode 100644 index 0000000000000000000000000000000000000000..69e73ede311c4a846a8f818330708658a1e0fe77 GIT binary patch literal 203 zcmeAS@N?(olHy`uVBq!ia0vp^3Lq>1BpBEle`W(ImUKs7M+U~W1%@xC#RK{Bo-U3d z6?3j$GUPg7z~dZf8U4Qg*qsHF4`@^yUDaY)e6!!)qCI!=eVEdQwQX5 zah-&%x^euuN1vn0w==yySE5m{$awnax1S1PFES>4-qaZX;>{Y_l!4PL3!@ULbQoiNtWY z=b;I!`Z?mk+pi~%T4uHFkQ%qs0PKuo=U#gjgXq@dQ#YX;uR zyDEzTqN3i8Ri+t9;=Io`4iKjuJ*jdoASoRATB86`e`o#L4-kt!*XOIIkPy|$Z!G18 zk=$_R1cm^jDHwGE*;OPH+pecw0=8F|cWD7)9r{V(>YZh7s}#xX@Y-V@a8$R# zz7Nzll=T^a-D91a7GNOU#!3RDRppIxfH+g4`7VUA7;JpD{z%Gl^hz`rR!C-7WjBz2w=IKp0oL zbO>k?lt*0#RAuh<9{?e*sV()uX|32M9JssnY{osHpuWUu5cv8Oo7WFKnf5e90$&!U zD|&#%lN#4PfLz=wKl=n%AbF;bH~a_iR2$1}rLn7htMc}f7XqbjYTgrI+dp1QF98{q zx~>x7qmaPfO~8-eOc`>46CZ0QegN!tCr*3|kka8h&MF@x8JDN?qySsUVf_vKy0476 z9%wM?M@--UJ8OATkeUmSDcRFHm6nLa%*FSV76{aF1);#sVqI_>Kql)G-?4S=NapNh zVg_*djmhC=Ag9J7dkM%L^-MbokZB}6AQ$H>b5|DQ7>`=5gAqs{oMFj;fyD8u{}=gK zMu71K5`$&sNDOfBa_Q}rTV!=k^EBM^c>q~R=6AduZXuc8qS|W*kl#6Wnxp&)iHoCb zXd@uZXIuUPD6CbzZUDK2+Pw*)4M@m|&Mp?KwVGjB$JzxnI1su>k|SW z;K-2xP8~qD0I!0IuDeLqZiu^T&h0G>BMn)Ym=lSRkOKQt;)MxF$jp%`={{CrJA+}Q z0NGudCGHuG7?!!Lm%ady&A}>J%1A4M0{%q_gjthe+%dX`L1TnYm#8ctAc9 z@}pa>>=HU6*)gvdIs;@`N_gX0SxCrj<`CfL)P#i0DJdZ9W{Ka!FtV*beIQ<%iDapc zzwQpuzunh$8PLB|-d+oAxGoVK2OOgUZCC)=Ex%&=XT>2Qmra%!>Ggb-Hfk2fkY^() zKsHtKl(~0px+MSwNPBxOXxkqksf(TP*$R+5i!_m(4oOIRA3ry`IR({7L&)Y`5g+dy zjA3LNm(5d>Pa+|cbrc|HP1XT<>~@^s)UP}#S04qW zJ7f!3yE~lF1UCm1Ag?RrHNta!r8Eu0$jtfWu@_WGT8_ru{1AB9l%}=?3U3!0mB7Z| zi!2)ef4yhpK|nM&HhOB*ztD(3v5YRNcL0EJE literal 0 HcmV?d00001 diff --git a/impeller/third_party/stb/stb/tests/pngsuite/16bit/tbbn2c16.png b/impeller/third_party/stb/stb/tests/pngsuite/16bit/tbbn2c16.png new file mode 100644 index 0000000000000000000000000000000000000000..dd3168e5c864a81f3284856f2b00fd1e52eb312e GIT binary patch literal 2041 zcmV{l69U>r1pj4E?y3$fw z3LPC~5Eu?Ow>bx*PBc1kWIARHF=os}<8u$TxvBB}f1oy5P`W{kzl6)>j^6kGfA2f- z`~RHyuLHPP3zER+gr^$apTp%;SiD#R;vJ2Rd{S@z+-2p1bTZD!RV+NOifLIU&uTs zaE81?yrhgg@q`IfRXqTjnr;GKFNW3KjX}4zVxZ4FgXd*s(ZFE9a5b7MKz26X)$9LG z&+U!?)zyVy3pqr7Bq1d50~sfsq>_}A+FGpRrY7tX9#13)1Tctg+c1b-yYP7603Ht= z!sEyY-tFzh#G0G016J#8z+$-u)Ys$P_ICWwmKHpkOd3!^xI`}9Pq-4f{bx0*+^SE_VlClnoi%|Y#7Z`h22w+;)xK8=@ykF38Wv&SbTQ0gnee+4}W(MW;&#YW1^ZP6=>`(a|rw5FIVVzI~X$*|S2# z#!gMe#Z=9B;j7ZcOd6B9%84Llg;62WOyR`&K*R&qyjI668Uj`Z|E0BdPrAU)mb3U z=~5vE2k|lQz4vDV3dQ)iLcy)1R&U>~RtxF%*YPf{2T4htok>Yuq?_=<#C@GZ=iYo5 z=;^^{Ll6SH>ZM@SDqJsaz8SmF;>CE>Y861E`I*cqfgzHR(9)8SfC~}N^;pQUV?snm zo;VR1X|s8~Hd|WSz(87>ka+ppv`GlL-0PLgx$e0gIE@Ozq2ur@DhdbZ&T-vi=%uA{ zke>b^i2=#U2|%mmwg3D$XH8&?$YhO;GMNy&cVi*2uK^!^h{x}~6Cx|i@6XCoC;|b6 zLP%p{c-`j{LaA(PQ!05pa651syuNY*7#3BC#C5-QYdp~Fal5x-1%_T%hj+WXu_HO1 zSQ$M%AtdlINl7uAQ&Rp+pr!_kf>jutJ}tzOC3gFgB?`sn&6ohpRG^~+6YzMHN?tF6 z)8Gvs_ItfP3Y9OUJ3g@%v-FJ~DU6%`p7 z)Olt$T~Sd!UsRM3M~+}20|P?D#rb@3aYB0gZM=f}n$%RcJ2lluI1TPj268{JSpMqw z(#Xj5ATJMVU8TZja7CF+c-LgQ0jykkmV}T1Z)>x&AAcP8jW9H!K(AMb#Kfwq#6%(9 zdJ7BLzaPqwJRVppB))>Rmzmkxnwc3qy>c2ne6jQN_2Kir{~kkZY{cD_MuP+Q&O34b zF&gnn<>h$S?_Ul|OJ@sx)&y8_ad~-hu@GD!97Re>K|xB&qDB4vixvs-(MMR6-Hvxh zNAcY06hf_bxzy_5Wv-p@`ik|vcQ5|p=ur%@xHuJLW+o7vbJnfHC+*q8Ezk(u?h8zy znS_T20^#98!p*WwR#+&LEn8-_E?XwV`1nk}hj4frZEhAqrE)q|DqbZx4c>IDBv*pr zVf@9&2+ll(g&73fVa*!eeB+b4x^SH6boqXx;$c0=OV$;L(@ za&nrQa&mYx$7%2ytRYH*og^*}=lHZV906rzC&2V{sF7S?0>KU+o}4^{l69U>r1pj4E?y3$fw z3LPC~5Eu?Ow>bx*PBc1kWIARHF=os}<8u$TxvBB}f1oy5P`W{kzl6)>j^6kGfA2f- z`~RHyuLHPP3zER+gr^$apTp%;SiD#R;vJ2Rd{S@z+-2p1bTZD!RV+NOifLIU&uTs zaE81?yrhgg@q`IfRXqTjnr;GKFNW3KjX}4zVxZ4FgXd*s(ZFE9a5b7MKz26X)$9LG z&+U!?)zyVy3pqr7Bq1d50~sfsq>_}A+FGpRrY7tX9#13)1Tctg+c1b-yYP7603Ht= z!sEyY-tFzh#G0G016J#8z+$-u)Ys$P_ICWwmKHpkOd3!^xI`}9Pq-4f{bx0*+^SE_VlClnoi%|Y#7Z`h22w+;)xK8=@ykF38Wv&SbTQ0gnee+4}W(MW;&#YW1^ZP6=>`(a|rw5FIVVzI~X$*|S2# z#!gMe#Z=9B;j7ZcOd6B9%84Llg;62WOyR`&K*R&qyjI668Uj`Z|E0BdPrAU)mb3U z=~5vE2k|lQz4vDV3dQ)iLcy)1R&U>~RtxF%*YPf{2T4htok>Yuq?_=<#C@GZ=iYo5 z=;^^{Ll6SH>ZM@SDqJsaz8SmF;>CE>Y861E`I*cqfgzHR(9)8SfC~}N^;pQUV?snm zo;VR1X|s8~Hd|WSz(87>ka+ppv`GlL-0PLgx$e0gIE@Ozq2ur@DhdbZ&T-vi=%uA{ zke>b^i2=#U2|%mmwg3D$XH8&?$YhO;GMNy&cVi*2uK^!^h{x}~6Cx|i@6XCoC;|b6 zLP%p{c-`j{LaA(PQ!05pa651syuNY*7#3BC#C5-QYdp~Fal5x-1%_T%hj+WXu_HO1 zSQ$M%AtdlINl7uAQ&Rp+pr!_kf>jutJ}tzOC3gFgB?`sn&6ohpRG^~+6YzMHN?tF6 z)8Gvs_ItfP3Y9OUJ3g@%v-FJ~DU6%`p7 z)Olt$T~Sd!UsRM3M~+}20|P?D#rb@3aYB0gZM=f}n$%RcJ2lluI1TPj268{JSpMqw z(#Xj5ATJMVU8TZja7CF+c-LgQ0jykkmV}T1Z)>x&AAcP8jW9H!K(AMb#Kfwq#6%(9 zdJ7BLzaPqwJRVppB))>Rmzmkxnwc3qy>c2ne6jQN_2Kir{~kkZY{cD_MuP+Q&O34b zF&gnn<>h$S?_Ul|OJ@sx)&y8_ad~-hu@GD!97Re>K|xB&qDB4vixvs-(MMR6-Hvxh zNAcY06hf_bxzy_5Wv-p@`ik|vcQ5|p=ur%@xHuJLW+o7vbJnfHC+*q8Ezk(u?h8zy znS_T20^#98!p*WwR#+&LEn8-_E?XwV`1nk}hj4frZEhAqrE)q|DqbZx4c>IDBv*pr zVf@9&2+ll(g&73fVa*!eeB+b4x^SH6boqXx;$c0=OV$;L(@ za&nrQa&mYx$7%2ytRYH*og^*}=lHZV906rzC&2V{sF7S?0>KU+o}4^X9JP)QBlsk;7PU%bhGrgYd5Q9YAv@pu5?I(QEIFDO9PIa zv;_;!pa1a#&4BOUPo0`ItE{ZJIBk{3V?$2;ETe5E7iFAugap!!JvpW;c(dAAQ8V< zZ&BY>NjD*)1qCrNK|y=>!VP%!s->l|vE8$;ud}nhz7j7eD@#re2?+@B@p<+PZorQp zO@$fmNmnXtCft}jxxc@;xf%mh0YPPDR#td8NVBJBWF$%~baiE3DVf#RJVAESq^VP< zPoFZSy}bqv6c$Ev8{HHsktow$V{lB`di z{%hX6Idl5@>guwxqN2jr>pYX5E|kJ^f&DqRx(f5PYbXXRS(0v=B3pVK=!U{clUf+^ zwk5aa8O@zLfBu36RaH?@k&$+SvH@a;con5$c|}DEZ2$iLjbgyIZE5`R>8TI9Qu-<< z%&Jo_i;I%V`!i?Gym|BImy|?C27OhSEj^vT%kqkchm?%||Dqzaq5gD9C*CIj-6lTnrHEYhC#6(Y5*#J=&#dFf1`ILEld3=?QfBAxDz@I+}XOr8) z_eUHoTv5Ut_PcOJ$k~z>P;}0k6%}PEBIC}VVH_mAk5_}&Lhw>xFqZ6`_b6dX!jw?c z%!-;Dfwv2)g69`EgOb#=Y2o48uO(Z#zd9_?))qGsnatzc{v3+|Jw5UA=}QBbWjreT z8u2D?TToHq>&cU+PVMgx3sZ_0b>T~KcQE76X3$TAY#mJXtlpDYGZ7 zBIi@!?b7Or6DLpZ>}crfArf4l~O;$zjSbkp>uelLq>cX#*pmXrhs+uBMi3p@E(ywgBe zjiXq{N literal 0 HcmV?d00001 diff --git a/impeller/third_party/stb/stb/tests/pngsuite/corrupt/xc9n2c08.png b/impeller/third_party/stb/stb/tests/pngsuite/corrupt/xc9n2c08.png new file mode 100644 index 0000000000000000000000000000000000000000..b11c2a7b4049475d967a8cc76b93ef1039684a3a GIT binary patch literal 145 zcmeAS@N?(olHy`uVBq!ia0vp^3LwnE2_&^8vlamc;6;z7cmE{-7; zb9B#amdK II;Vst0B-g)L;wH) literal 0 HcmV?d00001 diff --git a/impeller/third_party/stb/stb/tests/pngsuite/corrupt/xd0n2c08.png b/impeller/third_party/stb/stb/tests/pngsuite/corrupt/xd0n2c08.png new file mode 100644 index 0000000000000000000000000000000000000000..2f001610a85a662d8baa0c1f02c06adc02cfa20f GIT binary patch literal 145 zcmeAS@N?(olHy`uVBq!ia0vp^3Si6xB+q0lO9E0X>5jgR42*3H3|~x(2l72UT^vIy z=DfXnkdwiHhsmM!&BVXki-fIRw7)F;aqgk^7G4(X1}>2wG7H{5sr9LFXk_B|YL`FM q!Y-omVL^$7@=Axs-38@~4=^@u{29mZaNZkeCWEJ|pUXO@geCyFeJ}z5 literal 0 HcmV?d00001 diff --git a/impeller/third_party/stb/stb/tests/pngsuite/corrupt/xd3n2c08.png b/impeller/third_party/stb/stb/tests/pngsuite/corrupt/xd3n2c08.png new file mode 100644 index 0000000000000000000000000000000000000000..9e4a3ff7accf4453ddcd58318f7048b326b44ad2 GIT binary patch literal 145 zcmeAS@N?(olHy`uVBq!ia0vp^3LwnP1SGpp+}Q-ASkfJR9T^zg78t&m77yfmc)B=- zRLpsM^&lsM0S}Wy>zj#xw-*UpyJ&w|_~YC|?Jc}4)(u=DKV%lXeNyXF;n2v$@6|4U rsD)ibzj#xw-*UpyJ&w|_~YC|?Jc}4)(u=DKV%lXeNyXF;n2v$@6|4U rsD)ibc;6;z8n`u6{1- HoD!M<9fb>g literal 0 HcmV?d00001 diff --git a/impeller/third_party/stb/stb/tests/pngsuite/corrupt/xhdn0g08.png b/impeller/third_party/stb/stb/tests/pngsuite/corrupt/xhdn0g08.png new file mode 100644 index 0000000000000000000000000000000000000000..fcb8737fa2505b43955e995d95c3d6972b85fe32 GIT binary patch literal 138 zcmeAS@N?(olHy`uVBq!ia0vp^3Lwk@Bp957Lw$i1OS+@4BLidG0>c;6;(>fePZ!6K ziaE(C2`UUC949vOu(I~>b7=nfE^nVuX&@tFa7u7Oddi~87#=p(p8gM~{{27yqy1T- hp#)Io!PL|g7KVVQ{|ZeX-G~HP;_2$=vd$@?2>>eWDsTV* literal 0 HcmV?d00001 diff --git a/impeller/third_party/stb/stb/tests/pngsuite/corrupt/xlfn0g04.png b/impeller/third_party/stb/stb/tests/pngsuite/corrupt/xlfn0g04.png new file mode 100644 index 0000000000000000000000000000000000000000..d9ec53ed94b34f50eeabf3029465e1106725ea2a GIT binary patch literal 145 zcmeAS@N?(llHy`uVBqrfa0vp^3Lwk^Bp4c;6;z7cmE{-7; zb9B#azopr048WOXaE2J literal 0 HcmV?d00001 diff --git a/impeller/third_party/stb/stb/tests/pngsuite/corrupt/xs2n0g01.png b/impeller/third_party/stb/stb/tests/pngsuite/corrupt/xs2n0g01.png new file mode 100644 index 0000000000000000000000000000000000000000..b8147f2a84b861b559fd52fc0ded6971debdc6f1 GIT binary patch literal 164 zcmeAS^mFIslHy`uVBq!ia0vp^3Lwk~Bp9L@-6Me%OS+@4BLidG0>c;6;z7cmE{-7; zb9B#azopr0C@{E=>Px# literal 0 HcmV?d00001 diff --git a/impeller/third_party/stb/stb/tests/pngsuite/corrupt/xs4n0g01.png b/impeller/third_party/stb/stb/tests/pngsuite/corrupt/xs4n0g01.png new file mode 100644 index 0000000000000000000000000000000000000000..45237a1d294f7432c06ae45769727d9745250221 GIT binary patch literal 164 zcmeAS@Jr|AlHy`uVBq!ia0vp^3Lwk~Bp9L@-6Me%OS+@4BLidG0>c;6;z7cmE{-7; zb9B#azopr0E~z=2mk;8 literal 0 HcmV?d00001 diff --git a/impeller/third_party/stb/stb/tests/pngsuite/corrupt/xs7n0g01.png b/impeller/third_party/stb/stb/tests/pngsuite/corrupt/xs7n0g01.png new file mode 100644 index 0000000000000000000000000000000000000000..3f307f14ea5ed37b9f74b896a98ee4a5d2c9e111 GIT binary patch literal 164 zcmeAS@N?(oQs81>VBq!ia0vp^3Lwk~Bp9L@-6Me%OS+@4BLidG0>c;6;z7cmE{-7; zb9B#azopr0DK%Y?f?J) literal 0 HcmV?d00001 diff --git a/impeller/third_party/stb/stb/tests/pngsuite/primary/basi0g01.png b/impeller/third_party/stb/stb/tests/pngsuite/primary/basi0g01.png new file mode 100644 index 0000000000000000000000000000000000000000..556fa72704084920c07066054bb57adc5a250b27 GIT binary patch literal 217 zcmeAS@N?(olHy`uVBq!ia0vp^3Lwk~Bp7wr%FhF7mUKs7M+U~W1%@xC#RK^hJY5_^ zD(2|+8uA@7;Bmcwtm%lTL>0plJ;|yaa?KmnAMk`(G6*LxZMYGklX_CXw8pz)!~XsE z{R_l;B%iO@S+Mxi#d|k1W1jAhOum0pYrW6>o9eGz=B2dXkDg=peCAH+`yAT7-d7m? zpD=neuC5Ya_iW0gUZXDEgTe~DWM4fgQ!vM literal 0 HcmV?d00001 diff --git a/impeller/third_party/stb/stb/tests/pngsuite/primary/basi0g02.png b/impeller/third_party/stb/stb/tests/pngsuite/primary/basi0g02.png new file mode 100644 index 0000000000000000000000000000000000000000..ce09821ef101b65b2aa653931416c72923ccab09 GIT binary patch literal 154 zcmeAS@N?(olHy`uVBq!ia0vp^3Lwk`Bp9=o@yY{fmUKs7M+U~W1%@xC#RK_)o-U3d z6?2jUl3pY*JS=M4oiJze;et03Q>IK~Gc?O{N#DFKJ@&l7SH)M%%o7ftVn}g$DCX>; zkRs^i5p-N&*DHI8!;%tAJO+*|%*(hAE|_G=Fej+Nw{_diTA<+!p00i_>zopr0NF1v AeE`KLv!pxvIx;Y}EiimBEgr~U=jq}Y zQZXmBFHrE1fq;v+j0h02$h25#lnT63xZ-%g@gNW$aGWTh62d&wbH=%|b$8yp-97vE z;Z29Oxt6;AKCTefnOkLOe zceV31xk&buuP3aOvK_8v?mNu>)5yI*ti8x;zv2JpMH=2qx2(%fUX!Y@wwPzh+iJE; se9X+5d(#!(S{O|*V`01GC)_Bn-uQauWnXqNpvxINUHx3vIVCg!0H8fu|I&j2dyvG3;`TGSdQ=U^x!+b$IgQ- z%948O0A52IVpo?-TFZBK`N@L-n zZIKuYb8U;nSX9Mrl^A36r{K12&k3mj^Z%lhjX;s*Jg960iexztW-CxinU-MA*#;OZ zngOW30HtIErtRT^u_8M_CF3kj<3j{vMRtJ6I<2P3n{$R*t1rNmk`0g=AU|E-;g=04 zMQ4L1&-<_M{L=>-dBi{f3 literal 0 HcmV?d00001 diff --git a/impeller/third_party/stb/stb/tests/pngsuite/primary/basi3p02.png b/impeller/third_party/stb/stb/tests/pngsuite/primary/basi3p02.png new file mode 100644 index 0000000000000000000000000000000000000000..bb16b44b30907b832b678a3b441278df340edb9f GIT binary patch literal 193 zcmeAS@N?(olHy`uVBq!ia0vp^3LwnH%)r1{DZJ<-ki(Mh=EaktF(C3l8oA>Me*wxTz-(}y$c;d$?hLjJF#hg79QUtv`g1+0Ah)O6+%y3%0 kK!=4{>!bX^1(PfpwnWvwlrMbmdKI;Vst08#Hgh5!Hn literal 0 HcmV?d00001 diff --git a/impeller/third_party/stb/stb/tests/pngsuite/primary/basi3p04.png b/impeller/third_party/stb/stb/tests/pngsuite/primary/basi3p04.png new file mode 100644 index 0000000000000000000000000000000000000000..b4e888e2477d4fbaa3c8ed16a8007d40e58cd52c GIT binary patch literal 327 zcmeAS@N?(olHy`uVBq!ia0vp^3LwnF%)r3-EjT<7$YDu$^mSxlY+GRXVp=?q&s^-} z8N$NCQvPG>Vjx#Hz$e62iQzxP|Nk8f|CRnTFwFe_KaJro!~b%i7*N6gnRh{q-T!wp zFt`9&E|)fV?gff(^K@|xshE@eVr8<$zX=QdragS9+NR;%b~IQ*M^<9l2E!vd+h?C( zII@q&;mi?+BR6^0F7JwWQfc53?9iD|$he5*L_qV3*BrK+SKaw>*N06*_XNYzScwH+ zV%a3lGAx}cv7khXdCI+B1NO@fK9ddC9@xTtImqX>;oQaofA@?RGmTe=O`R?+y}#PL zSZx`DR(OCwFvHZ>kD4?aPVKE04`%2+-2h~U1|1S{c)Dz9BG-bV^7qFC7#KE}y~^Mz Rxc~|Y22WQ%mvv4FO#pr5f6M>? literal 0 HcmV?d00001 diff --git a/impeller/third_party/stb/stb/tests/pngsuite/primary/basi3p08.png b/impeller/third_party/stb/stb/tests/pngsuite/primary/basi3p08.png new file mode 100644 index 0000000000000000000000000000000000000000..50a6d1cac7a111d53cd3aec0d35dc4d09311baab GIT binary patch literal 1527 zcmWkt4Nw$y6n}dd;vwQ8DFIG$3@GI2c^hLQV*{=V2t>jrDk_FvDg&7$%b-@G;h|=k zpdN>Gek8Jdh#y$4REHQNu9O-n$@*~yld0Q~CgKPCdUtQ`_P_uC|GnSGdsmv8GRH?B zs0RR_gm`WqJ;%AfULF7pdq_&9mmwd-CdUF(;+S%8&r$#nNKHzgW#r%qssai!qCpSQ z0vL_7LIcnOu#UGNg#{Tj`hkLY32hvQ!1G1`Sw@Zn9(h>?HW2Wkq9XR7jEce`M@cLI zX9QKH0)U4(L{U}UX&T}n(htKT%SQskpw)^XiVOoBfYl1vBTI|Gh(?PU5N)D}JkUAF z*+A4fQ52$qu$3IJkUnIraqtk890gF3fMr1iHJ=i#wnVy*0swAiP?EIuK(HVK+H9oK zO8az3*a`*`R}fi5uu;&5F%xGAi8P7`R6^54fJ7UmSdgp%+jcbYbn0eLh5#4{O2a|z zFlncK3j#}*c@!wwN`=4x0@Wv9Pz46?iR#Xv1EQkRnl2%Lup5n_C@hN_Xp)3BL=u!O zgCs#6RU@v*uGicWd7h||*~k+*SQG^)HX|blsFAzW2%aZ!O(VrJ31D;t3x3pAg5Wqi zve-$Qc$9#ED~5p_E`A&DTySs_gXBPgvEi4GHiIsCXqG@a(*{? zqG+gzY2=omW89DcEUHMQNrOtO^VViET%@j9XuK5ipN55EJB^!6bHJ02)DH{^ z28HH;T8~zS28YEnJAZp0KzA^Ki%l=ATyc3-rZ)t5_Dwa^e3e*sHMuT3A|j#p=*X*F z#y0Vd=-McX6_VY&k>*>Z%e|vX1^&KXtnjHm_kp z{Oz$(O(8S=UuD^|XKQO8JeWScy}iAy&FQ@UT~n0V9QLiaO{Ytot3K50Bj4!g*tp@* zcGj*hPH>pOmLs;xabA)>UnI5F|W2|vHO`X8G8{qNq5UvQ>+yva2AorH*qN%N=1 zRO-F+*Y4O692_0}_3qrW zV_8*GU40<7^H$aV#hJ=gqrL3swI3}rKihD;tJ~gc@LGOoZ}}hHIjh6Ni;61tbzVDC zEf)+M(cfo^N={x^khe0+l%Bp^o)#S#*w$83a{u1Ivl?+Wcj0ty-sn>0W|&`1nH z58GA$jXh^Y`JN;7H@hZGKGv`#E%e2gv6o*jI5)Y=Gw4px%OQatp)Q!Xsl()5;fH&w40HKUbx zj4}D7oEu&UXI&wcy60YxT`|F*cQ@)MIhQzY_v!Om7VgvKXAK=bq{#ES%PVuszm5L= z&bJ*WO#!E!Apy*TI{`1@hW^84hM29NG>%Pa9pm_Xjc<61X<)<)m$})aVkqnKe7Wgq z-l@|1T-)pTTjSfo?;6Ga8{R(IDkmelNXRj)ZH9oA|PM{kg$m&gMT07S3iqx$gt>#1~b*D~%?ISJ;?`E{!%Qxx!#mpQpGpa(A8H z_iZ|MVh@5=q!mUuvSsYMxvKp95>weV2Kff8ej5&Q`t30L(azh))$_*RmQyw|G#2PE N22WQ%mvv4FO#oeUP~QLm literal 0 HcmV?d00001 diff --git a/impeller/third_party/stb/stb/tests/pngsuite/primary/basi6a08.png b/impeller/third_party/stb/stb/tests/pngsuite/primary/basi6a08.png new file mode 100644 index 0000000000000000000000000000000000000000..aecb32e0d9e347ccdcab5d7fdad2dde7aef9da8a GIT binary patch literal 361 zcmV-v0ha!WP)!N7@Xl>n0JWRT0P1ZZ0~F7IAF8qd>UAgssMo0sP&xyjf=W~LX-;!O00000NkvXX Hu0mjfr8|%> literal 0 HcmV?d00001 diff --git a/impeller/third_party/stb/stb/tests/pngsuite/primary/basn0g01.png b/impeller/third_party/stb/stb/tests/pngsuite/primary/basn0g01.png new file mode 100644 index 0000000000000000000000000000000000000000..1d722423aa5157fe2b029af4e2bb07f478ebe8bb GIT binary patch literal 164 zcmeAS@N?(olHy`uVBq!ia0vp^3Lwk~Bp9L@-6Me%OS+@4BLidG0>c;6;z7cmE{-7; zb9B#azopr0C;FL=l}o! literal 0 HcmV?d00001 diff --git a/impeller/third_party/stb/stb/tests/pngsuite/primary/basn0g02.png b/impeller/third_party/stb/stb/tests/pngsuite/primary/basn0g02.png new file mode 100644 index 0000000000000000000000000000000000000000..508332418fa86637d39e95268ec1d3658d120cae GIT binary patch literal 104 zcmeAS@N?(olHy`uVBq!ia0vp^3Lwk`Bp75C+I9jdmUKs7M+U~W1%@xC#RK{Bo-U3d z6?2jkIAXub_k1+y2sp$L+}6)%%qgEdVJADoUeEf*ZqCk?AR|0o{an^LB{Ts5miHSU literal 0 HcmV?d00001 diff --git a/impeller/third_party/stb/stb/tests/pngsuite/primary/basn0g04.png b/impeller/third_party/stb/stb/tests/pngsuite/primary/basn0g04.png new file mode 100644 index 0000000000000000000000000000000000000000..0bf3687863d8a1f53bef0aa24b841b21b0e04d9e GIT binary patch literal 145 zcmeAS@N?(olHy`uVBq!ia0vp^3Lwk^Bp4c;6;(>fePZ!6K ziaE(C2`UUC949vOu(I~>b7=nfE^nVuX&@tFa7u7Oddi~87#=p(p8gM~{{27yqy1T- hp#)Io!PL|g7KVVQ{|ZeX-G~HP;_2$=vd$@?2>|oDDeM3M literal 0 HcmV?d00001 diff --git a/impeller/third_party/stb/stb/tests/pngsuite/primary/basn2c08.png b/impeller/third_party/stb/stb/tests/pngsuite/primary/basn2c08.png new file mode 100644 index 0000000000000000000000000000000000000000..db5ad15865f56e48e4bae5b43661d2dbc4e847e3 GIT binary patch literal 145 zcmeAS@N?(olHy`uVBq!ia0vp^3LwnE1SJ1Ryj={WSkfJR9T^zg78t&m77yfmc)B=- zRLpsM^&lsM0S}Wy>zj#xw-*UpyJ&w|_~YC|?Jc}4)(u=DKV%lXeNyXF;n2v$@6|4U rsD)ibtrx; literal 0 HcmV?d00001 diff --git a/impeller/third_party/stb/stb/tests/pngsuite/primary/basn3p01.png b/impeller/third_party/stb/stb/tests/pngsuite/primary/basn3p01.png new file mode 100644 index 0000000000000000000000000000000000000000..b145c2b8eff1f4298e540bfae5c1351d015a3592 GIT binary patch literal 112 zcmeAS@N?(olHy`uVBq!ia0vp^3LwnL3?x0byx0z;SkfJR9T^zg78t&m77ygJ1^9%x zzWcAFl=eSI>XI5zMAXy8F{ENn@&k4zHj_up0tD0@h%3W?AY}Lt#0>va?X`CSX(dk=$B>FS$v@Vz>816FsF9(VN75txh7sS~8e>Vez z3y|e3}y1F=z>r=LEEca@LnZf*~BA0ROZ5~$d#4)lIa>n+tGAx#s~NCh;!h=Q?&7t5~~Lk>mL*|1x+L`Ivp6mn?kJ^kK$c6a~%|NrOteLPR)ru^*4 zh?oc>ipx)wHw!xP<||VqG2mh2yNQ1IZKr30Xr(I7PBXU z(o_;ftk^?X5O*qmM)+A^aUR*s!>r3PlcI=3mc_D63M-aHQVj83+hHDKi)~wG8A%eb zMRFVzAa0kL4aW(lxy(-(A3@r7{KRdCcI^9^ zBwSWlMY4uMp(p@%T`0C7K$rnonuoRmLY9xP#5bTx(RKJi zCYfG^*s^*-{Ro^e4Kgw{Dlmv)JpjQ5bKwF@CLI|%59=nhA>e#HJh5GNjRLr(&Jco- zL=roU($L^w70~)&xPh+uFV&-K!K0w3W-9Hy@g@HWXL3ML4|%5ULen8 zlT@|D2@;VI*iada4446_ptp_#Sul*Cx7Y6>PlSiq8TyN-q=y}cXZX&@20)p=ihIP{CfA$Z*4W@ zEr$zzJ<~t_(0G1&!T9U{_Iz>DPFy@Dgq4(i*+1}U*ZiG1m)32#<4sRFJ5!AP>yZ}L zzdq~5lB&|C6*=qgy?nj!is#SN$*Gwo6M?qJgKJtd1_tvb_xS3qJuRh&eH)f1f0J@< z)4uq(Z_li{H~+s=+3L-xjU7X)ZvEUC-CLA+t^7d7$jI)tsK~;c&XdnBOC5Txp}1)@ zusm-vWl2G`{dnN}K>pDE=;yi{rVB@Y3B-HrtGOePxNz#}j}FD`z3|nlwikX+ZZ56= zAY&lKTUG9w&2^VN89D}o5=^>%~&6ID5>`0fk%7K&zZcY&F#znJTW|E zB5&cFwM~ly35&;HdZq5*>W9rM<&V4Wr%mNt{B`@CxYpR8I;^7!?Tc=wu6t&Du2Ec< zKIiz-xXN3dttqGahAW20Qo6MzgSk7e_{!(?CKyw`*?j|NY=7wG%F`JaWZ(VOm8CUr z$$no_)1uMK%W6)nN!IrL6EB>puUj#i+1xg}<&qUPnY6#j4&NW^MpZNn{t+Cq+^l@L JEAySt{s-!kV`~5a literal 0 HcmV?d00001 diff --git a/impeller/third_party/stb/stb/tests/pngsuite/primary/basn4a08.png b/impeller/third_party/stb/stb/tests/pngsuite/primary/basn4a08.png new file mode 100644 index 0000000000000000000000000000000000000000..3e13052201c9f8b90172ea2491b18d6d121c2786 GIT binary patch literal 126 zcmeAS@N?(olHy`uVBq!ia0vp^3LwnE0wix1Z>k4UEa{HEjtq=#3k+XOiwE*eJzX3_ zD&{2rIe*{)W9ys@6GNprI`<_tn04&i_AxF_G>~EoF-$NKa6jCj#n!|2C_^IPFayJV XErr@6uUk(54Pfwe^>bP0l+XkK+-D@M literal 0 HcmV?d00001 diff --git a/impeller/third_party/stb/stb/tests/pngsuite/primary/basn6a08.png b/impeller/third_party/stb/stb/tests/pngsuite/primary/basn6a08.png new file mode 100644 index 0000000000000000000000000000000000000000..e6087387639b9cefaaafb696e29a8bd910ee0680 GIT binary patch literal 184 zcmeAS@N?(olHy`uVBq!ia0vp^3LwnE1|*BCs=fdzmUKs7M+U~W1%@xC#RK{Io-U3d z6?5KRGvsP8;BkAXo|56e@Vk8|OTXfgyGLCsWP@G=X#A1))wtQkXb@LjH|PH@<7U}X zR(Ix8Y%6~9Ml4|WGnG@SZHQ?Kc`Uubi9hC!9z&%<$E5#{7BHGEsCHnVVk5wElR+}T f%%Pr9`U69dg47%_{vAF*s~J39{an^LB{Ts5$SFGl literal 0 HcmV?d00001 diff --git a/impeller/third_party/stb/stb/tests/pngsuite/primary/bgai4a08.png b/impeller/third_party/stb/stb/tests/pngsuite/primary/bgai4a08.png new file mode 100644 index 0000000000000000000000000000000000000000..398132be5faadf83e159ac59c212213bcd43a894 GIT binary patch literal 214 zcmeAS@N?(olHy`uVBq!ia0vp^3LwnE!oa||uB7QDki(Mh=l@|1T-)pTTjSfo?;6Ga8{R(IDkmelNXRj)ZH9oA|PM{kg$m&gMT07S3iqx$gt>#1~b*D~%?ISJ;?`E{!%Qxx!#mpQpGpa(A8H z_iZ|MVh@5=q!mUuvSsYMxvKp95>weV2Kff8ej5&Q`t30L(azh))$_*RmQyw|G#2PE N22WQ%mvv4FO#oeUP~QLm literal 0 HcmV?d00001 diff --git a/impeller/third_party/stb/stb/tests/pngsuite/primary/bgan6a08.png b/impeller/third_party/stb/stb/tests/pngsuite/primary/bgan6a08.png new file mode 100644 index 0000000000000000000000000000000000000000..e6087387639b9cefaaafb696e29a8bd910ee0680 GIT binary patch literal 184 zcmeAS@N?(olHy`uVBq!ia0vp^3LwnE1|*BCs=fdzmUKs7M+U~W1%@xC#RK{Io-U3d z6?5KRGvsP8;BkAXo|56e@Vk8|OTXfgyGLCsWP@G=X#A1))wtQkXb@LjH|PH@<7U}X zR(Ix8Y%6~9Ml4|WGnG@SZHQ?Kc`Uubi9hC!9z&%<$E5#{7BHGEsCHnVVk5wElR+}T f%%Pr9`U69dg47%_{vAF*s~J39{an^LB{Ts5$SFGl literal 0 HcmV?d00001 diff --git a/impeller/third_party/stb/stb/tests/pngsuite/primary/bgbn4a08.png b/impeller/third_party/stb/stb/tests/pngsuite/primary/bgbn4a08.png new file mode 100644 index 0000000000000000000000000000000000000000..7cbefc3bff08a9d91666d6b0f8b5cb1c896b7987 GIT binary patch literal 140 zcmeAS@N?(olHy`uVBq!ia0vp^3LwnE0wix1Z>k4UEa{HEjtq=#3k+XOiwE+VlDyqr z7#LRdDjNZLrk*a2Ar*6y|C~Q?fU$K>hKZrl9G&};8q7NOZTlD(CmKkxg%~E72)G|^ k&|>Rhdz2v&aF~JNzLrAmk=Ly!fCeyly85}Sb4q9e08Xzba{vGU literal 0 HcmV?d00001 diff --git a/impeller/third_party/stb/stb/tests/pngsuite/primary/bgwn6a08.png b/impeller/third_party/stb/stb/tests/pngsuite/primary/bgwn6a08.png new file mode 100644 index 0000000000000000000000000000000000000000..a67ff205bba91cc8f391a0b59110ae6c038539ac GIT binary patch literal 202 zcmeAS@N?(olHy`uVBq!ia0vp^3LwnE1|*BCs=fdzmUKs7M+U~W1%@xC#RK_lN#5=* z4F5rJ!QSPQfg<^yE{-7;bKYJvrKjw}eL#0B;r2mf= uFq$o>c3_`kBfxT#K{CM1p`KCt14EF4)EqJX9X>#-89ZJ6T-G@yGywoV#zE8o literal 0 HcmV?d00001 diff --git a/impeller/third_party/stb/stb/tests/pngsuite/primary/s01i3p01.png b/impeller/third_party/stb/stb/tests/pngsuite/primary/s01i3p01.png new file mode 100644 index 0000000000000000000000000000000000000000..6c0fad1fc982e54aea994e12efd3fe3584cabdbc GIT binary patch literal 113 zcmeAS@N?(olHy`uVBq!ia0vp^j3CU&3?zc?q{RR^Ea{HEjtq=#3k+XOiwE+Vi=8|} zSXfxfe{5Y0;s*GHxH2&O@2a>I4&-uqx;TbZ%t=lFvY8kdJ=QNN1hN=BUHx3vIVCg! E01FBk(f|Me literal 0 HcmV?d00001 diff --git a/impeller/third_party/stb/stb/tests/pngsuite/primary/s01n3p01.png b/impeller/third_party/stb/stb/tests/pngsuite/primary/s01n3p01.png new file mode 100644 index 0000000000000000000000000000000000000000..cb2c8c78261e509e7ef2c352306618963954a84a GIT binary patch literal 113 zcmeAS@N?(olHy`uVBq!ia0vp^j3CU&3?x-=hn)gaEa{HEjtq=#3k+XOiwE+Vi=8|} zSXfxfe{5Y0;s*GHxH2&O@2a>I4&-uqx;TbZ%t=lFvY8kdJ=QNN1hN=BUHx3vIVCg! E02xdg0RR91 literal 0 HcmV?d00001 diff --git a/impeller/third_party/stb/stb/tests/pngsuite/primary/s02i3p01.png b/impeller/third_party/stb/stb/tests/pngsuite/primary/s02i3p01.png new file mode 100644 index 0000000000000000000000000000000000000000..2defaed911a29507f745bd7183a9819b29cc53de GIT binary patch literal 114 zcmeAS@N?(olHy`uVBq!ia0vp^Od!n2%)r2CU%&n%ki(Mh=)Kx2KC^NX4Aw1O`S11~vx9e?93ZfWi!(u6{1- HoD!M4t literal 0 HcmV?d00001 diff --git a/impeller/third_party/stb/stb/tests/pngsuite/primary/s02n3p01.png b/impeller/third_party/stb/stb/tests/pngsuite/primary/s02n3p01.png new file mode 100644 index 0000000000000000000000000000000000000000..2b1b669643540f182c425fb67869b7f97fe75f10 GIT binary patch literal 115 zcmeAS@N?(olHy`uVBq!ia0vp^Od!n23?w}&=BEQGmUKs7M+U~W1%@xC#RK`w#ZI0f zEG#VLKejFgaRYopTp9lVmyFR@1#)>jT^vIy<|HR1Bmj9V42<8zEkc1T22WQ%mvv4F FO#mnG8JGY7 literal 0 HcmV?d00001 diff --git a/impeller/third_party/stb/stb/tests/pngsuite/primary/s03i3p01.png b/impeller/third_party/stb/stb/tests/pngsuite/primary/s03i3p01.png new file mode 100644 index 0000000000000000000000000000000000000000..c23fdc463170faf97e53fccb4799386700b21a15 GIT binary patch literal 118 zcmeAS@N?(olHy`uVBq!ia0vp^%plANBpIb2ie~{iEa{HEjtq=#3k+XOiw8*-J9&n% zu&|W>*t!_VWee~Lab@_=@V}g49;WoqQ$(6ld^s L^>bP0l+XkKxV;@Z literal 0 HcmV?d00001 diff --git a/impeller/third_party/stb/stb/tests/pngsuite/primary/s03n3p01.png b/impeller/third_party/stb/stb/tests/pngsuite/primary/s03n3p01.png new file mode 100644 index 0000000000000000000000000000000000000000..6d96ee4f873baf1df3652a8d70994eeea799c30b GIT binary patch literal 120 zcmeAS@N?(olHy`uVBq!ia0vp^%plANB6FUp{{d1g>5jgR42*3H3|~x(2T2w?d4{mC zu$2GUx){i13-AeXW%$qVzno#?)1~u)B7B}Mjv*Cuk`odN5)y#?V+@X#hCJRt7K5j& KpUXO@geCw&86D36 literal 0 HcmV?d00001 diff --git a/impeller/third_party/stb/stb/tests/pngsuite/primary/s04i3p01.png b/impeller/third_party/stb/stb/tests/pngsuite/primary/s04i3p01.png new file mode 100644 index 0000000000000000000000000000000000000000..0e710c2c397e371e4feab66add6a9f9763ce0c27 GIT binary patch literal 126 zcmeAS@N?(olHy`uVBq!ia0vp^EFjFt%)r3-#KLAZh?DN<>&U>^w!rYkw0Iz&x!B1w zgoTBr{KwYCKrUN=Pl)S(hVuXa8D@QV-U$>D@pN$vshE?Tk-%_}nTL-@Si+8hL2?}n Uzopr0P-Lp3;+NC literal 0 HcmV?d00001 diff --git a/impeller/third_party/stb/stb/tests/pngsuite/primary/s04n3p01.png b/impeller/third_party/stb/stb/tests/pngsuite/primary/s04n3p01.png new file mode 100644 index 0000000000000000000000000000000000000000..956396c45b5103d3c38dd8906be14002e5bee48f GIT binary patch literal 121 zcmeAS@N?(olHy`uVBq!ia0vp^EFjFt3?wJp^Voto>5jgR42*3H3|~x(2lAPVojgNW zSXjz`Y+VfGvIY2rxc+A-|Noz1)_3QfKoNdV7srr_Imtf`7%+0!GcfRQFf%r7-v+XT N!PC{xWt~$(695jV9kT!c literal 0 HcmV?d00001 diff --git a/impeller/third_party/stb/stb/tests/pngsuite/primary/s05i3p02.png b/impeller/third_party/stb/stb/tests/pngsuite/primary/s05i3p02.png new file mode 100644 index 0000000000000000000000000000000000000000..d14cbd351ac11022eefcfa3bb2af528c3aadae41 GIT binary patch literal 134 zcmeAS@N?(olHy`uVBq!ia0vp^tRT$9%)r3d&i3yCki(Mh=b^8f!C{x5dr%K!>0dAc};RLn`vNXST-!El6+N0&!i n!bU<#VwswOk4HkpRtAR1>$j<%)r2S&bt2^ki(Mh==jv*Cuk}VDtG(2QH%-+V! svtII_@t;Hw#zXcz;mHztXBa0j9DK~v7~1r+6R3f~)78&qol`;+01$C3h5!Hn literal 0 HcmV?d00001 diff --git a/impeller/third_party/stb/stb/tests/pngsuite/primary/s07n3p02.png b/impeller/third_party/stb/stb/tests/pngsuite/primary/s07n3p02.png new file mode 100644 index 0000000000000000000000000000000000000000..6a582593d654c8d43aa8c8dfa8f6516e4f24c8c4 GIT binary patch literal 138 zcmeAS@N?(olHy`uVBq!ia0vp^>>$j<3?z5j>~{cCEa{HEjtq=#3k+XOiwE+Vi=8|} zSXfxfe{5Y08<>}%WQZXm_$N%<+Sv-f=TJo4b eYw%F`{a>D;u##)1Tx>4LCI(MeKbLh*2~7YjwJ1CQ literal 0 HcmV?d00001 diff --git a/impeller/third_party/stb/stb/tests/pngsuite/primary/s08i3p02.png b/impeller/third_party/stb/stb/tests/pngsuite/primary/s08i3p02.png new file mode 100644 index 0000000000000000000000000000000000000000..acf74f3fc4132609443b0555d56e5b314644bf23 GIT binary patch literal 149 zcmeAS@N?(olHy`uVBq!ia0vp^93afZ%)r2SE-kGO$YDu$^mSxlY+GRXVp=?q&s^-} z8N$NCQvPG>Vj!0%z$e6&;s5_~hX3XN8U8actlAYR0~A;Fba4!+n3J5qz{IFfcOc>T u0@fwp4bCJ+99*!q$#Hv=FS$q5Mw7orX>_}}cf hovEdPM`3y+BZF-itL;x80b!sr22WQ%mvv4FO#lIoCTIWv literal 0 HcmV?d00001 diff --git a/impeller/third_party/stb/stb/tests/pngsuite/primary/s09i3p02.png b/impeller/third_party/stb/stb/tests/pngsuite/primary/s09i3p02.png new file mode 100644 index 0000000000000000000000000000000000000000..0bfae8e45678282b23bed2760c0dbbd736be9df8 GIT binary patch literal 147 zcmeAS@N?(olHy`uVBq!ia0vp^oFL4^%)r3->c>4%AcrO0(btiIv2B6ji)rydK69~? zX9x=mOZktji-BC80G|+7hW`u<<^TUP{QqCh@IQEaktF()~}fr+~zg1bjT rBF;F5J&BF&_kSLqX%_`}QkWS^|FgS^^-Kp^#=zj|>gTe~DWM4fSkxz) literal 0 HcmV?d00001 diff --git a/impeller/third_party/stb/stb/tests/pngsuite/primary/s09n3p02.png b/impeller/third_party/stb/stb/tests/pngsuite/primary/s09n3p02.png new file mode 100644 index 0000000000000000000000000000000000000000..711ab8245189b4d5118b4dcd49ef9771bf924fb8 GIT binary patch literal 143 zcmeAS@N?(olHy`uVBq!ia0vp^oFL4^3?%3Nf7cA8SkfJR9T^zg78t&m77yez7dv@| zu&}U{|Jb@1$mI#}32|lk&%jXr|3Ab3|K$w-!x&P4;_{v@jv*Cuk`ox1e*fp;30o~G lp|iL#LdvQ6heDwO14Cvi>)|ZtdS#$e22WQ%mvv4FO#lN*C*c49 literal 0 HcmV?d00001 diff --git a/impeller/third_party/stb/stb/tests/pngsuite/primary/s32i3p04.png b/impeller/third_party/stb/stb/tests/pngsuite/primary/s32i3p04.png new file mode 100644 index 0000000000000000000000000000000000000000..0841910b72779aa7571cce45e56447eeb3de4520 GIT binary patch literal 355 zcmeAS@N?(olHy`uVBq!ia0vp^3LwnF%)r3-EjT<7$YDu$^mSxlY+GRXVp=?q&s^-} z8N$NCQvPG>Vjx#Nz$e5NNdIRjXZZggLNS#8F9$J!e4s=*NVput`nsRZ8c5&pba4!+ zm{U7pFJF^^h?~7@-J{?g64%>Ss_=bq+ht{1xSCJrO-tjMmhSi` nkfZu#wXNcYdz+%H|96_V1YK&G^7e=_(2oqBu6{1-oD!M>$PW;WtqGsLq6}$jpDV7@>y0l)ibBXvN}dz6L`X{!W|QK qt4`9uP~=9WliB7OneY3z7cem=9`<-$rKSgT2ZN`ppUXO@geCyfYFs4% literal 0 HcmV?d00001 diff --git a/impeller/third_party/stb/stb/tests/pngsuite/primary/s33i3p04.png b/impeller/third_party/stb/stb/tests/pngsuite/primary/s33i3p04.png new file mode 100644 index 0000000000000000000000000000000000000000..ab0dc14aba444d3f59f0bf77808ee7ee78ab5a48 GIT binary patch literal 385 zcmeAS@N?(olHy`uVBq!ia0vp^iXhCw%)r2S>FoPcKn_c~qpu?aW7`757t`W_eCA>& z&kz9yJIm7?|5Q?Gve>sQ=2z#A&*U>UfA#bB(&kXT4uKE5vkQo zUQ-!gbN(`3qRGqhce6z7%EakmS0Y8FPq}6+_@ir6xO2`=y*VFuJPs^Aejwdi=Zl4) zY)`yQC*Lu~AJ4vKi$Bd@7qys2yXwnUHmtVq{n7=4!I#bZtC8xk9FB%)(-P6#}_whaYKv zG;#N1*N-P`3zt?rkunQiXB;>sd*S~OJ*$Vjx#Nz$e5NNdIRjXZZggLNS#8F9$J!e4s=*NVput`nsRZ8c1LAba4!+ zm{Z%gm#f)8#BG1p-0Bz*xXo g-$=Io|APMvAEo}ki@M*T3G^LmdKI;Vst0JTexR{#J2 literal 0 HcmV?d00001 diff --git a/impeller/third_party/stb/stb/tests/pngsuite/primary/s34n3p04.png b/impeller/third_party/stb/stb/tests/pngsuite/primary/s34n3p04.png new file mode 100644 index 0000000000000000000000000000000000000000..9cbc68b3b9d5f263eb64bca9ad8bdfeae8205f63 GIT binary patch literal 248 zcmeAS@N?(olHy`uVBq!ia0vp^N+8U_3?xrvihTr9Ea{HEjtq=#3k+XOiwE+Vi=8|} zSXfxfe{5Y00D12$B>FS zQ_pYYZBP(ky%4X^8GYlzZKG+@7dEqqPBxHOE~fV1{h+qq6V*Svna^1j^)9?Ct99+6 zN12I`jtXnzk)}u!%RD37KjJGY{QS1{OXvRBqt2SFsin;wQV|yPQK!t*<5f${SKY|E by&mjqj=t@%(q8BXw3xxu)z4*}Q$iB}CDT{I literal 0 HcmV?d00001 diff --git a/impeller/third_party/stb/stb/tests/pngsuite/primary/s35i3p04.png b/impeller/third_party/stb/stb/tests/pngsuite/primary/s35i3p04.png new file mode 100644 index 0000000000000000000000000000000000000000..e2a5e0a6595f100edc1f79a3e0864ad6ea0d0121 GIT binary patch literal 399 zcmeAS@N?(olHy`uVBq!ia0vp^${@_b%)r1n`@_6HKn_c~qpu?aW7`757t`W_eCA>& z&kz9yJIm7?|5Q?Gve>sQ= z+doX6Y5h};;jD1&3&kR*!|w$e_FSCK!TZ;uv#nAtd0y%gzGN#yUNMtr9o7cw{d`v} znPPXp_t6M&TexoXtroF))1FRQrM1j3tI<%mZHpV5tekks+WAwqdfa)ug=wLE+m7oy zMQ7hT>LK{?oy%sWdO^0lW9vk3*7N!L?zPr!EpIN@4l($9_RGP?h0kp-%f1)C*BQ6t zSkfJR9T^zg78t&m77yez7dv@| zu&}U{|Jb@1$W;&U32_C|{~5{|{{M$i4CVjJK};YYC{Yd)E(fu`?q{2n^e{*@3Z79)POXW#oDtF1yr`=+itVv=OktFdG6g{K)mRoAX+EY7>z zmhXBt<&Eu!6r*o!cV_nNo&Ccp*=?%nxjo?-PXbeTw3r{SnJ?StNM`T3!o6nKZi}v&Xt(4rKl7t^ V?&lUg^#FQ_!PC{xWt~$(696v@lAZtn literal 0 HcmV?d00001 diff --git a/impeller/third_party/stb/stb/tests/pngsuite/primary/s36i3p04.png b/impeller/third_party/stb/stb/tests/pngsuite/primary/s36i3p04.png new file mode 100644 index 0000000000000000000000000000000000000000..eb61b6f9a325db7d967bd796d3a65494bf6b7754 GIT binary patch literal 356 zcmeAS@N?(olHy`uVBq!ia0vp^Dj>|l%)r2yqPf);$YDu$^mSxlY+GRXVp=?q&s^-} z8N$NCQvPG>Vjx#Nz$e5NNdIRjXZZggLNS#8F9$J!e4s=*NVput`nsRZ8c5&tba4!+ znA1CPFITgHh@1WGUzfMPP>Mb3d2r#rz};nBJ2KhD%L@z2iugh<9M4MfUF6B#_T+KJ zyS#u&i*AX_`$J4FwOGR3+lYu(xyz9^Us?I$Nzg8@_uT%B&fwPAv<_h$1 zKV4X~uHW!2nO zw^mgdcYZzlI&jNId!5cjyapMo+SY3yv3S|MG5=1ghQ&wM_et*;#P6@Vu`=r)^ENJ> nM6Fzb-Rz~M-&QV*`e$h`)grF|l3?zm1T2})pmUKs7M+U~W1%@xC#RK`w#ZI0f zEG#VLKejFga@7NTLR^9Le}-~~|NkKrL;3%55EIA;N|b|y%R#KK``N64bh)RCV@SoE zeX02TU7fN^(b&f@AC0IF4)lp>A zamtZ6X3lW8^&N+fpX;N5a1N0dE1b9+oO)I`@yuDpE^(oyK}Bq#(;+7|2{WUFjT0Lq lq)!ST`XV5f-^|R;z~ISp;_}2b`+!bh@O1TaS?83{1OP(LROkQz literal 0 HcmV?d00001 diff --git a/impeller/third_party/stb/stb/tests/pngsuite/primary/s37i3p04.png b/impeller/third_party/stb/stb/tests/pngsuite/primary/s37i3p04.png new file mode 100644 index 0000000000000000000000000000000000000000..6e2b1e9b79ba8ded32f713506b543f95c8720615 GIT binary patch literal 393 zcmeAS@N?(olHy`uVBq!ia0vp^svyk5%)r3tu$^}qki(Mh=sg+HIV-2>Eakt zF(-9`wNR6RK-+&o*^hxQcxC(!_~<1(WXyQ8cE!t#;Gj#lmM+`0{Ul?NhPm`7gP#(U z&TKlpT{>aX1vlpoG2ywrj9K?;d1~@l8+XmqNYD|GnW?j3_LDi&_Gk5ZpVL(C5Y*hb z;_)GkI~OeSB#!?*!4ts2={s4)Kw+AwsIZx0;sW~{6Jr*4M-?k-J?sdRs5Q&mv0%AZ zytIqJyc-jCPuY6yD{r}_O<9KFm9UNB@vD7vZe722&ATSp?!|BMy_))a()awI`Eg15 zcb}IZ6e>9H6slHm#xw|b7U^#ds^C5mec`FsvsuL_-L{JOW$W&>5V^;FS zsTXzmnjHk%9_laj{iXhZjaU6f3%5cQ1CLzmy47pf9C;MBe%0{;2EMB|HJ5HS=D#U8 zTcE<&;mf3XHLadi$}J7S6KsC%TdZ8VLMtWoP@;^%GPWsSI*kflz0dEPDy4JU==ewT z$8HrlSvDN~XJuI*Zalg3>X&(%0bi?r*mu~p# z*kbkGd%RM6vU;~}yPmpD-2FE9?b`>wofE(7z53qT-=ZIGukYz~KPva4hT&JMP+%DE SUKyZ=7(8A5T-G@yGywpP!HFRN literal 0 HcmV?d00001 diff --git a/impeller/third_party/stb/stb/tests/pngsuite/primary/s38i3p04.png b/impeller/third_party/stb/stb/tests/pngsuite/primary/s38i3p04.png new file mode 100644 index 0000000000000000000000000000000000000000..a0a8a140ad7ec7f78f5b8cb398f54233e790fe7c GIT binary patch literal 357 zcmeAS@N?(olHy`uVBq!ia0vp^Y9P$Q%)r2?D{_Pz$YDu$^mSxlY+GRXVp=?q&s^-} z8N$NCQvPG>Vjx#Nz$e5NNdIRjXZZggLNS#8F9$J!e4s=*NVput`nsRZ8c5&rba4!+ zn6q@EJ$I9XOxyf_5svYTT%(q*Y;ODGw$)5_M`m;PZW9x8Q*O>*8?9X5^PP9_c=!F& zj}P}&avWz``7H13GiA$f!58}0_y?X%x||YHuGu}U@%Fz$#r)47t&6p-nF}}c@lTBs z^1PZ{v*FFH8(9sEQqM(?B}LEJyE$O3_FS z$q5XNhu)=5Tjidh!fa-!aP~lxV%mWM7U!NuQRf`SX00}f3tT>iIb{)A35DHSJCk^B z9JyieHiKao|6$!5cY4l9TNo}lec>oiL(-j$qysxN6CA{uC#7^J8uHKH+k8^@!3HLV YPnp`y=hnD91lr2r>FVdQ&MBb@0IdL5LI3~& literal 0 HcmV?d00001 diff --git a/impeller/third_party/stb/stb/tests/pngsuite/primary/s39i3p04.png b/impeller/third_party/stb/stb/tests/pngsuite/primary/s39i3p04.png new file mode 100644 index 0000000000000000000000000000000000000000..04fee93eae400e745534756b48f5420cbe4a1d91 GIT binary patch literal 420 zcmeAS@N?(olHy`uVBq!ia0vp^>LAR*%)r3N*;O+E$YDu$^mSxlY+GRXVp=?q&s^-} z8N$NCQvPG>VvyPZpAc6d{hy(n;s1XK#Zdmg9K;0jffD5);c^h`>wY$Cpuv)!E{-7; zb8;sb7Bw3PxXwR5nPsxbIbI3B13h{Zl-LU%&pWl%I?8Km-rd09oy#9GScQZM32{6% zJn&)b?*IA=s?&@YhcsVH;1O9g(`9z%6oU2EhY-&tz!*HGHjC7g8ZRKA?r5euu=`tFBX7AyHo&^hw&tWyi;3*RQj<5NC7 zD$9QAmr-d~@vfp^>g%_&3Lb_R^iP%F$ND|O^6>H9g zXp(yF?QYZiJMI0h)x3XK|Fr(u@2&iL4zKdoR{mw=nP&Qp=ij3Tz`$bgboFyt=akR{ E0PqU5-v9sr literal 0 HcmV?d00001 diff --git a/impeller/third_party/stb/stb/tests/pngsuite/primary/s39n3p04.png b/impeller/third_party/stb/stb/tests/pngsuite/primary/s39n3p04.png new file mode 100644 index 0000000000000000000000000000000000000000..c750100d55fbd07d216bcc5af538a83b9f7772a3 GIT binary patch literal 352 zcmeAS@N?(olHy`uVBq!ia0vp^>LAR*3?%D%eb|5$OS+@4BLidG0>c;6;(>hTVkgfK z78aKBA6plL)CTy3xB}__4CM^}|3fH-^8e)^CXf%5Crf5MCykT z!yOH&Kayq_xt_}isYhMd#%sHX&9!mIgr_sq&#dB<+^ar$Z{X$9)vls_k8f1|*}8bi zpB=vzEdR6P)rUoYc6jZ*eQSQo)o-o)F5h}-kYN6lt?pKC%J$H%zv=qU-|SMRztaeP zGf~`5;@h^18+60tYao5g)5S5Q zV$RyWja;A|lo{eZ(@L`p} zS{LWU6$LrdObg}*USsKY346=g=`j6{lFBTGU2~>go4o6%r;zgB(0Rug*KVDo#;EA% pAydNHT#*-Kd$uF{lI;G>I4-d)zk<)z6+mAyc)I$ztaD0e0ssYck!}D0 literal 0 HcmV?d00001 diff --git a/impeller/third_party/stb/stb/tests/pngsuite/primary/s40n3p04.png b/impeller/third_party/stb/stb/tests/pngsuite/primary/s40n3p04.png new file mode 100644 index 0000000000000000000000000000000000000000..864b6b9673b3b331f2956ad2299b7854210cdb41 GIT binary patch literal 256 zcmeAS@N?(olHy`uVBq!ia0vp^8X(NV3?%C=ER6$FEa{HEjtq=#3k+XOiwE+Vi=8|} zSXfxfe{5Y0FS zXD@8zZ7|?*x#--bqd09rVn>dvna6z96-Ml=&4Mcg{<*&Jc=+gKnBVfCtuiaG{Fsq+ zN5I`d(XC(dB13ART()`KdT#Ey^-ixof4?!Sz`}fsxz-Y$_FSPy%NW9)eC5gyx=V3r k20fUweGf1AoYPn=%bN?oV&q3&xZP#{- zd#wGeV8okcyH@o)cboHb1rrf^jB^?sG2T2?@G1n)CJc@hyhCf)&E8%^0~1oF;v>7S z;6sBJiji@r0Yq=mmR;)Ojr>$W46R-Sau7HO7xZFtI1{|4z%-nLbW=B z3o0EAC)h$;8WIR1rohQ#w444G0#1m$Dj66xet|L#R>4C36e7xMM0T)(aSAa2XfV)D zcEU#my$(kCaRx~QL^LgQ_;=)3gHxFOXBZJd{maEJLqY3H<>rDuET^1Yth7z0>+1gi X*waT#qxC!3? zz50+^bb0-!+_LIFVlkOn3uCR$oXN=hfy)oYZM=LTO5_V)JX0F)aUb#`{Px3~8K z45Cm8G#cZ?2?d2q03|3C!N$f8gK>%=kzAFOs;a7HXJsOhsJOUTC=>!T78DfJ0p#W7 zO#|fS<`x6+csvb_-PzgMH*VaxejVY*&B(||Pfw37i%v;TNli^nPEKZQ-I|z~7@ruA z92Sce8ykD3^-OehbYx^?czAeNSXdrFNJvO(?(>_pT_EMw{BQ#m>$Sn!qg3OkVyw9#4k*05u5X^#Wxw8I%DBhvS=>0kTjoKsJnz zkHa;PN~LfWvNvv&lMD4H5CQz1ooNFD1Ml8_0AO}^ckkGN(bYw&Ct|UTmX?<3ckj?> zcNB^Yu-39J8}w6t>_9>;(#;tUCqEAdt`EKrw%G5dUucga(q-6VG498*{K%rij4wv2 zoEfW%jzHYO{1#TLQQ`9W=k@mzoI;b9QinWz_A0mp3_C3m`>c%tC$KR#Xus#^maPqG>$Ip&r<7PP}+lo3xrqrK0bX_;b9>)$CG zYiDOPWRFqn6UcGDpA~Oj9{Dz;6-NwxejzAm$n^KEA< zw~yjS7UP676`?(gNA_%Ti&?JTRiQ)ukN>niNvk-4K4Y-(eYokz+n%p00U^e=@{X9+ z)rI4_;#GQ*EM)A{+^B9WciOLZ)vuJAK75`z`)0L0X_*pJxWq7Myp(uq&jr<7|o zLnIyhWR@l$6GSYpXJsSmv@J23K`FjDRPrN<`4!1UJg1FhP}`#*W-iAZNy`g6qaeJ622^AeZriDs|F)fz44lCrgvCaK9(i)p7!%%LO z6LCs$SZ>MfJV}wPPOIFlR1QvYe%|MK-}ia{df)f+{c@b0>@`sOC;-qn<$!ZVI(j`f zDj^kjGph?}%2e!GEGVRlO(*lc0TkamJGz}*TU$#c5*r&IR@vH?l$6w=(b>AXMMXv1 zx04DB3jtEEU%$?+^=WBow*aI7Nl8fw2?+qjY&IL9hsk8d#>O5wLZs8_(b3V7k&zD74p)(r z6#W5wi5%j-eJnyYfy3casnmdg0P-cW&PSbeL&Go(2FJts`1l+vI)>xmtn;h^Zo*f9 zFF+!ZJUl$y-Q5WUf_a*`i;K%{;ch%0Z)Jtm(t1?CX%ln~ww zvI4CLn;>X`*DqdZX)#srsxB=p0rJcP5Q$0`7Z=q_)!{xs9cb~i;5IBQEI>6tCCtyy z&(6+*5T>W60ceneP(Tm>WPquush*y(ii(Z4cCD)F{LssxzP`RZfO2EwuCA_*j*dQn zArvZ!Mq?a5uB3Dcpag{?+Sxf^v1cR6WET~cs;a8FIk{LYE-o$>i9`TRH*Vah2Ph~g zm;uPo&o2hx^Z7bDJ92Y#uU)%#^(w-Tmz9;3nVA_=7L%5lmY$xTnwrYkv?)0`IX*ca z2^NbL7Z-QB?Q~2`OjJ}7l7z-JS_iG7g0sVN0Uz}3~&+1VKez@Wfj%a%*<6dWBL zfByUl>%J=rqtT`{H60urpc%{q%@q~D69^Qz2T+SJUKdaPBUi= zZ?YsBS+ans&A`7&dFaENu<+a*|IhOD!Fy5FU2($py#tmf$d>!3__F$7Rx`n0Xd`(x zXNPrB#Hf{`Do)y8Y}67x>Fw{qlr}T!YF_4_IUZux-oC*~jH=jeIQPaX!`r{y1WzBl z(m6wK7D&e$Zg(V>@2C;Wf~LRR?X8$9#=q?zc-|P5aetE9NbV#QTKGtM8suGqBBx#3 zxo@Q}hx+TEERS}!iYl~OZDE->d_uXdy?`@|M5G4?j`=QYAGR)#`a zvNJkHwy@!`Y;;2I`Sq)+{L9pEnB}|hM@{;rXnr^QKIxlagyn~gH+uDas6JD-A_hPD*sh|5-pZZezZNI zTBjQ-?c5``GX0PwW_dm>8`Y+1#_9&A`Q}k652Y3f(u)LcJ9%$iuabnh9D6vUApEqF nBzr1Qcj<%PHtn77gx$)ZQM<+~5{$JW7KKwMop80bl;r;b+-;m} literal 0 HcmV?d00001 diff --git a/impeller/third_party/stb/stb/tests/pngsuite/primary/tbrn2c08.png b/impeller/third_party/stb/stb/tests/pngsuite/primary/tbrn2c08.png new file mode 100644 index 0000000000000000000000000000000000000000..5cca0d621047cacf1637cf7d77e997d51cc6b15a GIT binary patch literal 1633 zcmV-n2A=teP))6_}Akuqmh*5X4X^rIbM;BH-v?$kU01p_ErCl=u4~ zL7fbz&H-~9!(}3aFu`RZW&unzb50QkgK2!RYsr=o3t~2tT^d3tzx&+X@9uj)%)Vj6 z{|IpB&Yj-g-pR?y{}^!Z-o3WAwzRagh=_<-fB!T`$I~7jJzDL9k&%A|7#|<+>gvkP z&5eqRQiO&cb#=`%Gt&SnAO*+-su`xq!J$j392p#Z_UzeG0aH^`SFc_zD=UkSkAGjG zNZGY3d;R({hG_tbfDeIqE;r1MUWVZftDkNW!h zs;jG$lamt?5{`w2W;;5Ta5(kAS>OWD3k(34fhLB@x3Eyz+ozkGX8`3uA)t^*Vq;_J zARVu%si6?50P_I-{r&0b>GXAy-pZYvJ_kMn+JSRiZt1pd#bWVg8=D~(>oza}`~v)> zM}7e)8HRFA`iDhFM^mix0K>z>DwQfHCnqyAGc`3;6%f#2ZhqFvO5^8uBq=H7FUwFU zT6XLhvbG*uwdz+cchJo2l$~8_P|)B15EmC05)$I?@88+kIS+9E{(W+tk&&UwQ56&v z(A`o(AH0rH_Tk~-0RaJGvDnww_wL=h^8j=?>6z)a6}IeCY}aO2b*Z|fq@=L0@WhD| zsSDdty^(jSiA*M&bM5WzEtks;bWvVj9&?GoHDC(zc6o7eG4)bKMMY_8X?Aw@u?6h# z;lshf`|a#hY<3Bkd&JHz$kWs3`0?Wg0OXPTF&CY{G^|Igi;9ZKH36!ss;DvOr&_J1 zx~1t&sZ>(M4;(lkk?c1!`x@w{b_L!AqU`OZt*xyF0OXE$lBa8(+d4*aQ!-UaQ=##! z_w+pDSx{e4UteEaTT3@Y0IErFaIj1kyl&n1z;8e!i?yF&L_nC8m3(Mu$N-?Zxp_n7 z2DAX*=>Wn$A?4)L>VvOzv*1+iR99Em(9l2wMRh0dROro{PXe>RH6x>UEG*vQa@TOV zQlU`t^yyOrfcExwL7@Om)cL>za2|4U%W^%=c<5%V3NB?X6h0+EjKsu5@-7xf85_?~ z_(n!?E-s$Cckkx$Tm=FtNq~V#ChO_xan5qaMSv9N2jF-3*7yqRh3Ek)Au1KoO;8fV zprIvzKyXeU{%KoVxrc`zkCz5CFib&h?McIcn>TN|Dcy8{RDkAq5^L3Kg|$LmUsXU< zBBJ4(W03sGWO5UeaSET!R&3c4%;9JOl9gkorlAz5VF1mVSE84$J}Q`0nqU|>1Di@4 ze5+F&_hR?T%1UBTp~Aw#cJ55qhku#R59RT^^+74-!fSSE~<7VgEJh?uUa0qm; z8Ez47t52=QkANEJ0t(=x^_iQy^73*TT0XxG7zGZwyYCSQ1YB+maEUU=$|_zAke{D# zTyIS3>>TO5U9}y3y6WrNG#}mt-ZU1(ApHmo3|zUggX&{s6zS#V<>Yh_y5_4hF$wDK z?p_S=`P`~WeyGw#>B+X26iZPz^D41kPE3=O0g4z6qf*N)=*PsGE4-+(Dp2{ zw2Zxeeeh*hh5=+_V`EuaSwV3@IIpj54kWn}jYdNZ8e`JrO`E(~ENUAnPWZ-+iMMY3 zY|zhwfLVQ0wY0S8#Df-Q7G&8n5<6nh7)vA)kx0bn+nAbqczMM?d^oX?lf?jYc4O#< zON|S4tujMN`;Hh?Jz7|PetuiGZY4pbYg^pItKUTv6B8tGw0Wp##Go?l literal 0 HcmV?d00001 diff --git a/impeller/third_party/stb/stb/tests/pngsuite/primary/tbwn3p08.png b/impeller/third_party/stb/stb/tests/pngsuite/primary/tbwn3p08.png new file mode 100644 index 0000000000000000000000000000000000000000..eacab7a144cb54be28f7a5952a2fc63fd883766e GIT binary patch literal 1496 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 literal 0 HcmV?d00001 diff --git a/impeller/third_party/stb/stb/tests/pngsuite/primary/tm3n3p02.png b/impeller/third_party/stb/stb/tests/pngsuite/primary/tm3n3p02.png new file mode 100644 index 0000000000000000000000000000000000000000..fb3ef1d0c5aa4658cf1c1383dc6e74fcf09116d3 GIT binary patch literal 116 zcmeAS@N?(olHy`uVBq!ia0vp^3LwnH3?%tPCZz)@o&cW^R|bavaI|&4^Ese6b4ie2 zFhl4n?w#tEK(2_Vi(^PdTyhFSQ-Fe;PETlaHzR|#xM-r7v7RGPp25@A&t;ucLK6T+ CZ5hr0 literal 0 HcmV?d00001 diff --git a/impeller/third_party/stb/stb/tests/pngsuite/primary/tp0n0g08.png b/impeller/third_party/stb/stb/tests/pngsuite/primary/tp0n0g08.png new file mode 100644 index 0000000000000000000000000000000000000000..333465fcdc9f10f7f25b2d999b558f5421983ecc GIT binary patch literal 719 zcmV;=0xe{x-64Dw5ISbDJfGPNXYF1fsnx~?7~M3eMD*m6rOMo0;2$rUW>-c(^**S*Ycd0zuX+u!1|S9WB+#F78QX<>lo? ziQz$h-hRnIWoQ!H=elq>2fX`s$g4Md}>$Ugu1e*ygBpcmqZ3DNb_5HNEsBtg8Fz$MsRgsRcUEt!R)#7 z@*=Hdg|%Ijgaqfp6x>M8@W|(*DezA>78ATBV zGiJ?6^pq7`pj8TmNNXtGQ7M(fr#kNOGpUKtO+a# zE}lAlTDXoRkAt?j2v0jgK~H>oU`AO)UQprWsr_L}qI~YALPA1E5ej}SPEJnGOv?!@ zojAEOR7Ob9QdC4?8%mP9keiy8ksVOlKd~`Xk)KCEK=mA2ntaoimYx|<+}mFjqR+?8 zZTJ8q(f(PLp6*}V-CGiDD=qBv7F)8vQWR0&(VZLQZ(jZjd%k$Tpt-FhJuqOwUu^jT z1pXapZA%Q=jx2U BP!RwC literal 0 HcmV?d00001 diff --git a/impeller/third_party/stb/stb/tests/pngsuite/primary/tp0n2c08.png b/impeller/third_party/stb/stb/tests/pngsuite/primary/tp0n2c08.png new file mode 100644 index 0000000000000000000000000000000000000000..fc6e42cb420fd08ecd78340256157fa5f8a22934 GIT binary patch literal 1594 zcmV-A2F3Y_P)u542F>)wJ;-zU<0Ta5X4X^rIbM; zBH-v?$kU01p_ErCl=u4~L7fbz&H-~9!(}3aFu`RZW&unzb50QkgK2zirzKlPEQpyV zJ88Z&UwhB*+;h+QpNot)Z1^7mZr{G$*Vi{SHT9YSckkY9Z*NaePmhd@j0*@zcXT}E z>DjB%+#enNPk@PuiSF+1yu7^V=xBLZ*b!IPd^0mOpafEZETD#AngxPxg<^DQ=;_m^ zO9f0%PhYumrM$d6AtB*?xjc2}&YX4Y${D5+CG4W_vSdOD(DVN&-oB_@QeZU}a320`R0!vG! zgF}Y7c_vT+6ajLvI4&-Z7Si(C+FA;c5-<-iFfc$*`Z-x=6;4i{1D^pMz*&|p+q$(x zB)Vj4J8WQZ3z!6c0e;dUzW@{rLwimBm&L@yP^|L+BO@b9r7|}+H!CYEEiFwM7}#lU ze#Y8b?eBj$IXU%j%aqGow{IV|u^C^v@>iA}GBZ1AZ=V(%{LdfavLmWItxn1d>CixU>w z(z&H&q%@~cmeiGMuLdu#(_V!Qg$)f2b#--gLIj|kgoK1hr6FtAeh>TxG#MD|V;DCe z+}c_;JUpxi(9+VfzG^*Mf$y{cVZV^}t ze*#wx4d1b}e2ZmQv#dlY6hC?LL=T{&qk~_>M>ADEa39m2fL{qvo7rko-xdG82;t3ZKJ~Z{8fjKg!o$ON?8wlCe~HHnv#{{d1tq7Cl~vHp z%#3~jNv`NUkyhKvAxIVA8=wbFDWm}$)k06v92)E2}b{Rj#QTCt*&@?&Ti zD zl4Z+2B9B~dgtK!fhf@n&;PLjfw||-Og6SEVLqkKcv9UCP1w{gUs+$RUkfbMoF`VR)x@ASCQaVB z(Z|4m%7&5?v0+2f&6_{#^|K%#LswL-t*u(|pn;hUX^xb{ju_O&VzJoG&5g&iH8u70 z_D*>4U~(ZRivi~B`tWs^S{JHXMW%x09Wf|-G_d^r{kLq{LV`-iwz!9v--{+ECrRLF z@=(%OV07*qoM6N<$f`h#14gdfE literal 0 HcmV?d00001 diff --git a/impeller/third_party/stb/stb/tests/pngsuite/primary/tp0n3p08.png b/impeller/third_party/stb/stb/tests/pngsuite/primary/tp0n3p08.png new file mode 100644 index 0000000000000000000000000000000000000000..69a69e5872234d96cab4cbb017a0933fbfedba81 GIT binary patch literal 1476 zcmWlXc~H^`6vv-Om}J3dn5OOMOgg5aIm=3#R_~R8*9gm+#ueDlIJqsFg@0;tH{|b8}HqQ5Jw$Ebasl zi9{wQqx{s={QP`?_T1du%K+m5+1c6W&Yc6O&&;#KYi za0xyG{0*U@p&=n5OeT{~rxS7s!NI{ht9Awk26}jqu-LA432IQH$P*->1%a<=E7fhf7KmpiNEL?|$g$1|@ z&=;ZU0fm$KWEP7BFz+mN_CD$D>KXyBfJUPQ1qHz$U@Noz`t>V3 z0Y5*#_4Rf5=AAr|%l))v3zbTRZXmD;`udA>dIa18XhRr(3s9%iK@&&<11kswKn|(| zCKfuzi4TRu*jp4GWDgcp#{666_U|?<$;@lL6fyrT=v@~MF43;U zZ$6b_)kkb?TQRlFx@L1DFHuvCdiGRV@qSb5o3SyX=dqWIo~SD}lgtU8-0hinIc|l^ zwZpyIy{+Kp3qku~jyuQS{wOJTffqk}ylg!rGf@@8WYp~qRTEe@i_6M#f}U2WR@4MfM)1HJRjPCMx~ z7uM(-p3*y>&A)fhi1A3;cS(L+J1;&pqWL&it>CX0-+eBqO{=g-lKzNGvG(`sNqtRF zeg3%mDzvL|F3Eho_0DyETn8H%dc!?dA!#0>Q=Vr9#{-Ga&|1#6CAW+Q0sfY$`N-&; z^0c=#@%4)8jy6X3lL6u3JAW=?c#tm>K2a$#(Q?|Uz}T^+pWADD@MB|q8>&Xr948aj zsY1|Z7&ad@u4wr4W%TE{WS7U&Rh^yI#(OsCtq$vQ3|A*M_uLm%-A~M)I@BiVUyS#YR$&g!`CNx58tIE z|3D@=NLJY$Q%;J12ZU?1w#&^2c5EzDN05DZLoa2szJ*SEwwg0|Mem<$Yn1cWcz^cq z-}lxrB6iz%k1R&3=>`|KS%};k)l(YskH!jI!g#-(&O>3hx)5(GX8X3fe_&q16?&Zr z)72bEymR&OySDOI3mZ0C3tSEiO=8X3u^BG}uBukDWrMyZ$LQ%Y4sr7@il=hRi%!IK N0G~qvls0mN@PBi2fm#3n literal 0 HcmV?d00001 diff --git a/impeller/third_party/stb/stb/tests/pngsuite/primary/tp1n3p08.png b/impeller/third_party/stb/stb/tests/pngsuite/primary/tp1n3p08.png new file mode 100644 index 0000000000000000000000000000000000000000..a6c9f35a86271c056e955f637a8769381eb03827 GIT binary patch literal 1483 zcmWlXdpOez7{_0ijw~Xj6ZLfH5mEEBP|@KrEtkTuS}m>SI;>1?8{6D(v$V!$$}kk^ z;zXQM92O%H=ShmnbXw(ZQ8_r3^Xq+{_x=9)zVGMz<@$KL>7tBL0HEvPj`Ks*k)Uu?Q@xn6z>Z3lMM-Q#6tfUZ$jg9xOySS8oYSdsZ?58S}Ku90Gh5`xl#{MR8%wt zP*_-43Lq2;*RS7_pPzr}(xrd%E)}>Vg+~H{0|EmB;S209 z+JEZQsgoy9!bij)vJ2CNNF)M$vKCsqg}Grc6c_?OKR+KIALs+qBGU~U&cS2w^z{7x z{X6{3T?w5=n_Ror-Q69U!8X)ZUHvP8K!G~|wFu*%2C7slr~p15FS4}-9)W zgA1TkDj@~(fB8jCjUG%S0R(${)B5}SM@HTPu)4duH*Un5nj+Sda5!dbYwOg=2pWA9 zg`xngv~oCl+wa{*R;b1Lc@x0-<4`CPvRn@w_UQh+Ac}Mk^OKb>f7-E19*W;}xs}~! zRlIciNyF__FM9f7*5I)-+cbPa1|k-TrQvsONlsb*)pnz$h0|UcNA3@z)MNfwE}Uv0 zIbFH+_Zk14HEp3y&g@1-ft^E-J#CK+WhE5TXruYlt&^-N^P3g#I=H!w`R}L=sT9s{ zCuD1uhCdG)aY*zhVd3F}HZMyY{g2C62iLwz8h1Q_?PFhSvbid>^O4P?YqRlh((eZd z#_aspCH=EBx%*CRO=qIG-J;L_AlZKBgiu)@&Tb|Ii=AapW?Zqp>gYA)sH#J5XB+h; zk9wYVqsyC_bvIrV9&w7WX>VWUAVpP~na{j&$UYNXX^m&}Uyx5RnnjA&4L3VdD!1H_ zD#Iq{ZueA8l;TIb`kpn$X5SsBHj?FpV!HrYcY~@^RN}pDGk;X^V&G~0qorYaOP*rv z^qN~4t3n!C(Ta6lUVkRto3S5$_!4xZm;=Uxwl-UlXk<^TD! zwrXx-kZ%7r>OqrnxrwlgbC>i*G{g?VCRx6GHc+-!v-|w+aMq(Z)3<$!sgDrNgW zt{66;txYft&kQV}Qtm74k`-qO{C2WMU5|#0wUn?oyC~|ghKx56YPk5`Xp_O#x8g2M X(5+n&l!#upArS?SL*BSr7fRay;V71Q literal 0 HcmV?d00001 diff --git a/impeller/third_party/stb/stb/tests/pngsuite/primary/z00n2c08.png b/impeller/third_party/stb/stb/tests/pngsuite/primary/z00n2c08.png new file mode 100644 index 0000000000000000000000000000000000000000..7669eb8385172325c399f3229cfe834f886fecb2 GIT binary patch literal 3172 zcmeAS@N?(olHy`uVBq!ia0vp^3LwnE1SJ1Ryj{$|z@zQy;uunKi&25+{%6Mj{~3NT zfY5g^iOI$zg{%daG8T~8Gz1(pwSa?x0cZixpELm=(-{~zY2*W_>0lpFKMoM4)4~E2 z)2SW@=%!OQ4zQR`#UR3NI<+joX*yLbz-Kz;kxamJ3L}}Y>68Q!QKpk^0kNi&7Y8Jm zPDUJ%W;&@sM6&55SwN2I#K!@drV|+lP)Gd0N=r*M+Cfdcn4n{+d)GIuBfe}0=haRfYbdY4|HeAW8JeNk!}u< z>b4FPx}gJ|u3eE@R|Qyfc?U*a*nwS_tjMJg1H3vP0MMC|F?z3LnBH6wQvU~l^-l+Q iz3w1HzgHBc=YRuMlTL2m+d~-u0000811+tMcYXzcm?aHZ#?gdL8ij-oNLTu@go{<42!=C2J(%l`2yyuQm;^LM9XM_s7MQs$ow Yp_Bdc41*5p0G-U>>FVdQ&MBb@0O=}Ly8r+H literal 0 HcmV?d00001 diff --git a/impeller/third_party/stb/stb/tests/pngsuite/primary/z09n2c08.png b/impeller/third_party/stb/stb/tests/pngsuite/primary/z09n2c08.png new file mode 100644 index 0000000000000000000000000000000000000000..5f191a78ee5601a45f1add2a3ad7a77b7b1ae0f1 GIT binary patch literal 224 zcmeAS@N?(olHy`uVBq!ia0vp^3LwnE1SJ1Ryj={WmV3H5hE&|zdeNJ&K|z4|!mJGk ze#?uM?J%qpNVJm;SDAGELf+biOrjbV{C6uqc^By}RQhy!A*198PSpVRZwtidR0X(h zmlOPO>811+tMcYXzcm?aHZ#?gdL8ij-oNLTu@go{<42!=C2J(%l`2yyuQm;^LM9XM_s7MQs$ow Yp_Bdct_kk@3Uo4qr>mdKI;Vst043R2BLDyZ literal 0 HcmV?d00001 diff --git a/impeller/third_party/stb/stb/tests/pngsuite/primary_check/basi0g01.png b/impeller/third_party/stb/stb/tests/pngsuite/primary_check/basi0g01.png new file mode 100644 index 0000000000000000000000000000000000000000..96ed62dbed7614edfd0d3cb58b1713c0a067eb9c GIT binary patch literal 391 zcmeAS@N?(olHy`uVBq!ia0vp^3LwnE1|*BCs=fdzmUKs7M+U~W1%@xC#RK_lN#5=* z4F5rJ!QSPQfg-0oT^vIq4j-NF$k*b)!7}^ozyGJ-oIDabg|X_u>_Q(?)khkuR!La7 zu*~chsLPP|}S^f!skB7DWF?ZskqvJ`CRNW>pLl^tak&^VnrV)#A>)9kel<=2?=1C*pefxc2Lag8WRNi0dVN-jzTQVd20h9bP0l+XkKm%fiA literal 0 HcmV?d00001 diff --git a/impeller/third_party/stb/stb/tests/pngsuite/primary_check/basi0g02.png b/impeller/third_party/stb/stb/tests/pngsuite/primary_check/basi0g02.png new file mode 100644 index 0000000000000000000000000000000000000000..bb5309885175369e7a44a82b3d71c503ee0e6230 GIT binary patch literal 283 zcmeAS@N?(olHy`uVBq!ia0vp^3LwnE1|*BCs=fdzmUKs7M+U~W1%@xC#RK_lN#5=* z4F5rJ!QSPQfg*99E{-7)hu==~<~m@&;gasP?#18!r0uz8iWB6Y>=YJ}>w6Iq^h#K8 z;k-L#;k9$^Z^>}_zukc`I~jKX*zo!^M()1+nFD*GaTS& zXpm=kzseGVKswJ)wB`Jv|saDBFsX&Us$iUD<*U(7U&^W}%*vi1t3dlCF zure?xzopr04@Jou>b%7 literal 0 HcmV?d00001 diff --git a/impeller/third_party/stb/stb/tests/pngsuite/primary_check/basi0g04.png b/impeller/third_party/stb/stb/tests/pngsuite/primary_check/basi0g04.png new file mode 100644 index 0000000000000000000000000000000000000000..2efd4876b56cb502102b73a24c678199e64f646d GIT binary patch literal 252 zcmeAS@N?(olHy`uVBq!ia0vp^3LwnE1|*BCs=fdzmUKs7M+U~W1%@xC#RK_lN#5=* z4F5rJ!QSPQfg<*vE{-7)hu>b>$jhL>!|d1`q41lZTUVH+t9bgmC4W3N6|Y@&sq(yy z+<{mBcCzhb&R}q0W{_gIz|g?PV8T$qppwOVi}ypuVxUo~C9V-ADTyViR>?)FK#IZ0 zz|ch3&`8(NIK;@<%D~bJ$TqOBGB7CQxvq+$AvZrIGp&-r(9+UE*T6*Az%<0j$jZRT a$^^(Y2Wqf8*4_!!z~JfX=d#Wzp$P!N$w>GB literal 0 HcmV?d00001 diff --git a/impeller/third_party/stb/stb/tests/pngsuite/primary_check/basi0g08.png b/impeller/third_party/stb/stb/tests/pngsuite/primary_check/basi0g08.png new file mode 100644 index 0000000000000000000000000000000000000000..23952137c9a6eff813346fa4db89480e77c87880 GIT binary patch literal 293 zcmeAS@N?(olHy`uVBq!ia0vp^3LwnE1|*BCs=fdzmUKs7M+U~W1%@xC#RK_lN#5=* z4F5rJ!QSPQfg%~6E{-7)hu>b?$jhL}!|Wh!$maCMUNossom zTJ?)FK#IZ0z|ch3&`8(NIK;@< z%D~bJ$TqOBGB7CQxvq+$AvZrIGp&-r(9+UE*T6*Az%<0j$jZRT$^^(Y2Wqf8*4_!! Oz~JfX=d#Wzp$P!Y-&#@t literal 0 HcmV?d00001 diff --git a/impeller/third_party/stb/stb/tests/pngsuite/primary_check/basi2c08.png b/impeller/third_party/stb/stb/tests/pngsuite/primary_check/basi2c08.png new file mode 100644 index 0000000000000000000000000000000000000000..64ef3f844312534c6c9dc0643ef8df95f07fb676 GIT binary patch literal 274 zcmeAS@N?(olHy`uVBq!ia0vp^3LwnE1|*BCs=fdzmUKs7M+U~W1%@xC#RK_lN#5=* z4F5rJ!QSPQfg+)vE{-7)hu>b=$a}zmhuKl;g514&?c7hAlVoQ+;=X1hFoDm(DQad) z_ERPn4v!Oz8sE}SF>?qgJow9e^q)^910xelO+CY x-29Zxv`Pj;OG^t~0~1{X(-0#gD+41dQy|+MsKNBi+hm{y22WQ%mvv4FO#tY{R$u@C literal 0 HcmV?d00001 diff --git a/impeller/third_party/stb/stb/tests/pngsuite/primary_check/basi3p01.png b/impeller/third_party/stb/stb/tests/pngsuite/primary_check/basi3p01.png new file mode 100644 index 0000000000000000000000000000000000000000..a8599e9a5504cf33f6de68849961c0eb59e77883 GIT binary patch literal 273 zcmeAS@N?(olHy`uVBq!ia0vp^3LwnE1|*BCs=fdzmUKs7M+U~W1%@xC#RK_lN#5=* z4F5rJ!QSPQfg&NEE{-7)hm&i*$G@~^7BcYM-+d<4!Z>(l literal 0 HcmV?d00001 diff --git a/impeller/third_party/stb/stb/tests/pngsuite/primary_check/basi3p02.png b/impeller/third_party/stb/stb/tests/pngsuite/primary_check/basi3p02.png new file mode 100644 index 0000000000000000000000000000000000000000..c911ea9cc2d8793dd5be36477cf563ef6f592650 GIT binary patch literal 286 zcmeAS@N?(olHy`uVBq!ia0vp^3LwnE1|*BCs=fdzmUKs7M+U~W1%@xC#RK_lN#5=* z4F5rJ!QSPQfg*{XE{-7)hu=K|~gNTDg(5*9HR@U8VWIC~tIe-6W>%TjgStl4D?f?JpO22gg!&d%3atmq~ zFuY}E&5+kPz}f!9M*YHa>7_u+R7+eVN>UO_QmvAUQh^kMk%6I!uAz~xp>c?jv6X?P z6_9OUVP#-Y$a7s4MMG|WN@iLmgQ2CRg|2~#u7PQYk&%^wk(DWsYYx=#DW_c=sDZ)L L)z4*}Q$iB}ib!A; literal 0 HcmV?d00001 diff --git a/impeller/third_party/stb/stb/tests/pngsuite/primary_check/basi3p04.png b/impeller/third_party/stb/stb/tests/pngsuite/primary_check/basi3p04.png new file mode 100644 index 0000000000000000000000000000000000000000..750ef69dc5a1f1433a83616453aa1144a11f8ea5 GIT binary patch literal 331 zcmeAS@N?(olHy`uVBq!ia0vp^3LwnE1|*BCs=fdzmUKs7M+U~W1%@xC#RK_lN#5=* z4F5rJ!QSPQfg*jLE{-7)hu=;=$lGAR!z_En)5<$Mfwe7()vY1)h1NswKM$_AwSVru z5~Xh46;*uT<2v4r*KT|h`#rZ${z~~4|AVY2RU3?~i{&7%6Eugk`*|>Tz=5t zT+U{T-$0kCmbgZgq$HN4S|t~y0x1R~149#CLnB>7;}9cbD+5a_Altyg%D|wI=ejD2 yhTQy=%(O}dLrY5wT>}$c1Je*ABP#Y~u z*B$5i8g-Kbl@=^GmZjsDF@@uXVnashqQHO7tU?NmX6z^Yeq=M;;pBB_u%jNt$P literal 0 HcmV?d00001 diff --git a/impeller/third_party/stb/stb/tests/pngsuite/primary_check/basi4a08.png b/impeller/third_party/stb/stb/tests/pngsuite/primary_check/basi4a08.png new file mode 100644 index 0000000000000000000000000000000000000000..1b7b3a5821bc99816cb5508e544986596a951e51 GIT binary patch literal 263 zcmeAS@N?(olHy`uVBq!ia0vp^3LwnE1|*BCs=fdzmUKs7M+U~W1%@xC#RK_lN#5=* z4F5rJ!QSPQfg)a>E{-7)hu>aW$i-m5;d;^B;LQL35xMKuui=T?+2m+2r{MY-k4yW^ zp4a+XIofu^dKxJHzuB$lLFB^RXv zDF!10Lla#?BV9w|5F=wN14}C)+rYxgz@U)lx+;o>-29Zxv`Pj;OG^t~0~1{X(-0#g cD+8b&Aln?MVbZ$}OP~e@Pgg&ebxsLQ07Pz2rT_o{ literal 0 HcmV?d00001 diff --git a/impeller/third_party/stb/stb/tests/pngsuite/primary_check/basi6a08.png b/impeller/third_party/stb/stb/tests/pngsuite/primary_check/basi6a08.png new file mode 100644 index 0000000000000000000000000000000000000000..c12484fc58fd2ea0ff4709c6c5093af224580faa GIT binary patch literal 298 zcmeAS@N?(olHy`uVBq!ia0vp^3LwnE1|*BCs=fdzmUKs7M+U~W1%@xC#RK_lN#5=* z4F5rJ!QSPQfg-t{E{-7)hu>Z^MG{133*2~v=nBdG1=3ba_Y#5JNMC9x#cD!C{XNHG{07@FuB8tEDu zhZq@K8CY5Y*#;I?1_p&Z*Huw8-vH$=8 literal 0 HcmV?d00001 diff --git a/impeller/third_party/stb/stb/tests/pngsuite/primary_check/basn0g01.png b/impeller/third_party/stb/stb/tests/pngsuite/primary_check/basn0g01.png new file mode 100644 index 0000000000000000000000000000000000000000..20f6404a201c49da0a7118c9484403cbb606132b GIT binary patch literal 391 zcmeAS@N?(olHy`uVBq!ia0vp^3LwnE1|*BCs=fdzmUKs7M+U~W1%@xC#RK_lN#5=* z4F5rJ!QSPQfg-0oT^vIq4j-NF$k*b)!7}^ozyGJ-oIDabg|X_u>_Q(?)khkuR!La7 zu*~chsLPP|}S^f!skB7DWF?ZskqvJ`CRNW>pLl^tak&^VnrV)#A>)9kel<=2?=1C*pefxc2Lag8WRNi0dVN-jzTQVd20h9=YJ}>w6Iq^h#K8 z;k-L#;k9$^Z^>}_zukc`I~jKX*zo!^M()1+nFD*GaTS& zXpm=kzseGVKswJ)wB`Jv|saDBFsX&Us$iUD<*U(7U&^W}%*vi1t3dlCF zure?xgTe~ HDWM4f9Rpd5 literal 0 HcmV?d00001 diff --git a/impeller/third_party/stb/stb/tests/pngsuite/primary_check/basn0g04.png b/impeller/third_party/stb/stb/tests/pngsuite/primary_check/basn0g04.png new file mode 100644 index 0000000000000000000000000000000000000000..166e7db2193ad12dc53cc431d58a9cf93b036ebd GIT binary patch literal 252 zcmeAS@N?(olHy`uVBq!ia0vp^3LwnE1|*BCs=fdzmUKs7M+U~W1%@xC#RK_lN#5=* z4F5rJ!QSPQfg<*vE{-7)hu>b>$jhL>!|d1`q41lZTUVH+t9bgmC4W3N6|Y@&sq(yy z+<{mBcCzhb&R}q0W{_gIz|g?PV8T$qppwOVi}ypuVxUo~C9V-ADTyViR>?)FK#IZ0 zz|ch3&`8(NIK;@<%D~bJ$TqOBGB7CQxvq+$AvZrIGp&-r(9+UE*T6*Az%<0j$jZRT a$`Hsk2Wlu&>wF8;z~JfX=d#Wzp$P!M(Mab?$jhL}!|Wh!$maCMUNossom zTJ?)FK#IZ0z|ch3&`8(NIK;@< z%D~bJ$TqOBGB7CQxvq+$AvZrIGp&-r(9+UE*T6*Az%<0j$jZRT%E&<1z#ORIwA+D9 Ppaup{S3j3^P6b=$a}zmhuKl;g514&?c7hAlVoQ+;=X1hFoDm(DQad) z_ERPn4v!Oz8sE}SF>?qgJow9e^q)^910xelO+CY z-29Zxv`Pj;OG^t~0~1{X(-0#gD+41dBQsqCbD)M5)4m1+H86O(`njxgN@xNA>`_*f literal 0 HcmV?d00001 diff --git a/impeller/third_party/stb/stb/tests/pngsuite/primary_check/basn3p01.png b/impeller/third_party/stb/stb/tests/pngsuite/primary_check/basn3p01.png new file mode 100644 index 0000000000000000000000000000000000000000..77c580b00ae6c173933e5f6f8ae3fd9d50c7cc26 GIT binary patch literal 273 zcmeAS@N?(olHy`uVBq!ia0vp^3LwnE1|*BCs=fdzmUKs7M+U~W1%@xC#RK_lN#5=* z4F5rJ!QSPQfg&NEE{-7)hm&i*$G@~^7BcYM-+d<4!Z>(lK|~gNTDg(5*9HR@U8VWIC~tIe-6W>%TjgStl4D?f?JpO22gg!&d%3atmq~ zFuY}E&5+kPz}f!9M*YHa>7_u+R7+eVN>UO_QmvAUQh^kMk%6I!uAz~xp>c?jv6X?P z6_9OUVP#-Y$a7s4MMG|WN@iLmgQ2CRg|2~#u7PQYk&%^wk(IH5u7NpF1N)l;-+>w! NJYD@<);T3K0RWJ$V7UMQ literal 0 HcmV?d00001 diff --git a/impeller/third_party/stb/stb/tests/pngsuite/primary_check/basn3p04.png b/impeller/third_party/stb/stb/tests/pngsuite/primary_check/basn3p04.png new file mode 100644 index 0000000000000000000000000000000000000000..f08c6e99d4b477e8ec7defd7f7cbb58cb815d2b9 GIT binary patch literal 331 zcmeAS@N?(olHy`uVBq!ia0vp^3LwnE1|*BCs=fdzmUKs7M+U~W1%@xC#RK_lN#5=* z4F5rJ!QSPQfg*jLE{-7)hu=;=$lGAR!z_En)5<$Mfwe7()vY1)h1NswKM$_AwSVru z5~Xh46;*uT<2v4r*KT|h`#rZ${z~~4|AVY2RU3?~i{&7%6Eugk`*|>Tz=5t zT+U{T-$0kCmbgZgq$HN4S|t~y0x1R~149#CLnB>7;}9cbD+5a_Altyg%D|wI=ejD2 zhTQy=%(O}dLrY5wT>}$c1Je*ABP#Y~u z*B$5i8g-Kbl@=^GmZjsDF@@uXVnashqQHO7tU?NmX6z^Yeq=M;;pBBE{-7)hu>aW$i-m5;d;^B;LQL35xMKuui=T?+2m+2r{MY-k4yW^ zp4a+XIofu^dKxJHzuB$lLFB^RXv zDF!10Lla#?BV9w|5F=wN14}C)+rYxgz@U)lx+;o>-29Zxv`Pj;OG^t~0~1{X(-0#g fD+41dV>4X?bD)M4o3Ceq8W=oX{an^LB{Ts5MD|b1 literal 0 HcmV?d00001 diff --git a/impeller/third_party/stb/stb/tests/pngsuite/primary_check/basn6a08.png b/impeller/third_party/stb/stb/tests/pngsuite/primary_check/basn6a08.png new file mode 100644 index 0000000000000000000000000000000000000000..1f54e565df630fc54cf973677e9b7344d639cf25 GIT binary patch literal 298 zcmeAS@N?(olHy`uVBq!ia0vp^3LwnE1|*BCs=fdzmUKs7M+U~W1%@xC#RK_lN#5=* z4F5rJ!QSPQfg-t{E{-7)hu>Z^MG{133*2~v=nBdG1=3ba_Y#5JNMC9x#cD!C{XNHG{07@FuB8tEDu zhZq@K8CY5Y*#;I?1_p&Z*Huw8E{-7)hu>aW$i-m5;d;^B;LQL35xMKuui=T?+2m+2r{MY-k4yW^ zp4a+XIofu^dKxJHzuB$lLFB^RXv zDF!10Lla#?BV9w|5F=wN14}C)+rYxgz@U)lx+;o>-29Zxv`Pj;OG^t~0~1{X(-0#g dD+6OI6Cl?dsNo{Zo9#di44$rjF6*2UngB;aPvrmr literal 0 HcmV?d00001 diff --git a/impeller/third_party/stb/stb/tests/pngsuite/primary_check/bgan6a08.png b/impeller/third_party/stb/stb/tests/pngsuite/primary_check/bgan6a08.png new file mode 100644 index 0000000000000000000000000000000000000000..6cb76f2b43ccd41aeca1eb05eb9863ba4b4129db GIT binary patch literal 298 zcmeAS@N?(olHy`uVBq!ia0vp^3LwnE1|*BCs=fdzmUKs7M+U~W1%@xC#RK_lN#5=* z4F5rJ!QSPQfg-t{E{-7)hu>Z^MG{133*2~v=nBdG1=3ba_Y#5JNMC9x#cD!C{XNHG{07@FuB8tEDu zhZq@K8CY5Y*#;I?1_p&Z*Huw8)WG2B>gTe~DWM4f4eVQZ literal 0 HcmV?d00001 diff --git a/impeller/third_party/stb/stb/tests/pngsuite/primary_check/bgbn4a08.png b/impeller/third_party/stb/stb/tests/pngsuite/primary_check/bgbn4a08.png new file mode 100644 index 0000000000000000000000000000000000000000..1086ccc09b46ecd656fecd5d459c657c15d5ae05 GIT binary patch literal 263 zcmeAS@N?(olHy`uVBq!ia0vp^3LwnE1|*BCs=fdzmUKs7M+U~W1%@xC#RK_lN#5=* zKpF^sI`6IrQeK`ejv*0;-(FhC#bChUdePkA%>Vxpx$D-i;fdSX%rmB{>MwFx^mZVxG7o`Fz z1|tJQ6J0|iT|?s#BV#KAODiDTz{1MFppfUfDvE~O{FKbJN(MtqOAB2C6I}z-5F;Zi c17j-_AlDqI;UdeM?LZ9-p00i_>zopr0M9&3#{d8T literal 0 HcmV?d00001 diff --git a/impeller/third_party/stb/stb/tests/pngsuite/primary_check/bgwn6a08.png b/impeller/third_party/stb/stb/tests/pngsuite/primary_check/bgwn6a08.png new file mode 100644 index 0000000000000000000000000000000000000000..03a0a303d69431d20dfc9d6c7e4ed346303eb62e GIT binary patch literal 298 zcmeAS@N?(olHy`uVBq!ia0vp^3LwnE1|*BCs=fdzmUKs7M+U~W1%@xC#RK_lN#5=* z4F5rJ!QSPQfg-t{E{-7)hu>Z^MG{133*2~v=nBdG1=3ba_Y#5JNMC9x#cD!C{XNHG{07@FuB8tEDu zhZq@K8CY5Y*#;I?1_p&Z*Huw8|fFnGH9xvXzm0Xkxq!^40 z3{7+mjdTr-LyU~A3@ojHYy%4`1A{`I>#8Uka`RI%(<&JZEiEl{4NPzm0Xkxq!^40 z3{7+mjdTr-LyU~A3@ojHYy%4`1A{`I>#8Uka`RI%(<&JZEiEl{4NP6%z+wC-k#Y9)WG2B>gTe~DWM4fysR}V literal 0 HcmV?d00001 diff --git a/impeller/third_party/stb/stb/tests/pngsuite/primary_check/s02i3p01.png b/impeller/third_party/stb/stb/tests/pngsuite/primary_check/s02i3p01.png new file mode 100644 index 0000000000000000000000000000000000000000..d84f40613e6b2aed5b1c3c0a638ea8599b57327c GIT binary patch literal 210 zcmeAS@N?(olHy`uVBq!ia0vp^Od!m`1|*BN@u~nRmUKs7M+U~W1%@xC#RK_lN#5=* z4F5rJ!QSPQfg+-wE{-7_*OOCz{Qqyy3p*F|> literal 0 HcmV?d00001 diff --git a/impeller/third_party/stb/stb/tests/pngsuite/primary_check/s03i3p01.png b/impeller/third_party/stb/stb/tests/pngsuite/primary_check/s03i3p01.png new file mode 100644 index 0000000000000000000000000000000000000000..51367f7f8e0bf32bbf096160b69349bf1ddf2c2c GIT binary patch literal 216 zcmeAS@N?(olHy`uVBq!ia0vp^%plCc1|-8Yw(bW~Ea{HEjtq=#3k+XOiwE-AlDyqr z82*Fcg1yTp14X1gT^vI=t|zDbIR9ZjBi9TGAov&VV9|5Jh@q;EwPMO`W;>uV)e_f; zl9a@fRIB8oR3OD*WMF8bYiOivXdGf>Y-M0+1!NmoSQ!`;@?2L%(U6;;l9^V?U}$M+ op=)5GYhW5;WMpMvY-MDiYhVu4pwhBB9H@c8)78&qol`;+0Pxj18vpuV)e_f; zl9a@fRIB8oR3OD*WMF8bYiOivXdGf>Y-M0+1!NmoSQ!`;@?2L%(U6;;l9^V?U}$M+ op=)5GYhW5;WMpMvY-MPsYhVu4aPs!dKA;8$Pgg&ebxsLQ00NFXK>z>% literal 0 HcmV?d00001 diff --git a/impeller/third_party/stb/stb/tests/pngsuite/primary_check/s04i3p01.png b/impeller/third_party/stb/stb/tests/pngsuite/primary_check/s04i3p01.png new file mode 100644 index 0000000000000000000000000000000000000000..ae326c1c4bbdb9c7e51e1054ac3d79df26bbeb00 GIT binary patch literal 221 zcmeAS@N?(olHy`uVBq!ia0vp^EFjFm1|(O0oL2|pq&xaLGBCC+FnlpB9>`})@^*J& z_z!{$_AZ|c6jAVWaSY+Op8V(k{D=0;LJ3I;AfVHpu!BJ;!R0y=gT!IBkCHlBQ-Nw# zOI#yLQW8s2t&)pUffR$0fuV`6p^>hkafp$zm4T%dkZoXLWnfUqb6pihLvDUbW?ChK rp{1pTu7QcJfoX`5k(Gh5m63t2fjLlvO3UhSpaup{S3j3^P6`})@^*J& z_z!{$_AZ|c6jAVWaSY+Op8V(k{D=0;LJ3I;AfVHpu!BJ;!R0y=gT!IBkCHlBQ-Nw# zOI#yLQW8s2t&)pUffR$0fuV`6p^>hkafp$zm4T%dkZoXLWnfUqb6pihLvDUbW?ChK sp{1pTu7QcJfoX`5k(Gh5m7$rgfjLma$=fsgfEpM)UHx3vIVCg!0P%4>xBvhE literal 0 HcmV?d00001 diff --git a/impeller/third_party/stb/stb/tests/pngsuite/primary_check/s05i3p02.png b/impeller/third_party/stb/stb/tests/pngsuite/primary_check/s05i3p02.png new file mode 100644 index 0000000000000000000000000000000000000000..fd41d1d8115a06d935284c91f40b932178007608 GIT binary patch literal 232 zcmeAS@N?(olHy`uVBq!ia0vp^tRT$61|)m))t&+=mUKs7M+U~W1%@xC#RK_lN#5=* z4F5rJ!QSPQfg;+TE{-7_*OUL8Kk#8bBku+SAP7^=`%DCCQ!R0gC`m~yNwrEYN(E93Mh1o^x`sx&hQ=XA##RQFRzS9ag_VIp zA^=`%DCCQ!R0gC`m~yNwrEYN(E93Mh1o^x`sx&hQ=XA##RQFRzS9ag_VIp zA;r0G@O1TaS?83{ F1OP&2LP-Ds literal 0 HcmV?d00001 diff --git a/impeller/third_party/stb/stb/tests/pngsuite/primary_check/s06i3p02.png b/impeller/third_party/stb/stb/tests/pngsuite/primary_check/s06i3p02.png new file mode 100644 index 0000000000000000000000000000000000000000..73a7b0c64ffebe6b62296460854bb97e53e878f0 GIT binary patch literal 239 zcmeAS@N?(olHy`uVBq!ia0vp^Y#_`5A|IT2?*XJ((j9#r85r9Z7`~Vm50Xssc6VX; z4}uH!E}sk(G4gbA4B@z*{OA0K|Mtvc2M!$of`~q?ANwaaHgcYOozRk?k&twNG2+1* zu8`Fdg^_#=dDplmi8BXw0d=dExJHzuB$lLFB^RXvDF!10Lla#?BV9w|5F=wN14}C) z+rYxgz@U)lx+;o>-29Zxv`Pj;OG^t~0~1{X(-0#gD+6OIBLiInbD##5met`v4Gf;H KelF{r5}E)-K1fFZ literal 0 HcmV?d00001 diff --git a/impeller/third_party/stb/stb/tests/pngsuite/primary_check/s06n3p02.png b/impeller/third_party/stb/stb/tests/pngsuite/primary_check/s06n3p02.png new file mode 100644 index 0000000000000000000000000000000000000000..e85eac8e8f8618ac6f320da65e51072f6de12003 GIT binary patch literal 239 zcmeAS@N?(olHy`uVBq!ia0vp^Y#_`5A|IT2?*XJ((j9#r85r9Z7`~Vm50Xssc6VX; z4}uH!E}sk(G4gbA4B@z*{OA0K|Mtvc2M!$of`~q?ANwaaHgcYOozRk?k&twNG2+1* zu8`Fdg^_#=dDplmi8BXw0d=dExJHzuB$lLFB^RXvDF!10Lla#?BV9w|5F=wN14}C) z+rYxgz@U)lx+;o>-29Zxv`Pj;OG^t~0~1{X(-0#gD+6OILm<~2sA1-lN0)&b7(8A5 KT-G@yGywoy97)yy literal 0 HcmV?d00001 diff --git a/impeller/third_party/stb/stb/tests/pngsuite/primary_check/s07i3p02.png b/impeller/third_party/stb/stb/tests/pngsuite/primary_check/s07i3p02.png new file mode 100644 index 0000000000000000000000000000000000000000..08f61804f4ba73b862d9bf31e5aada044dae1ee9 GIT binary patch literal 249 zcmeAS@N?(olHy`uVBq!ia0vp^>>$j+1|*LJg>$j+1|*LJgbP0l+XkKiN;L> literal 0 HcmV?d00001 diff --git a/impeller/third_party/stb/stb/tests/pngsuite/primary_check/s08i3p02.png b/impeller/third_party/stb/stb/tests/pngsuite/primary_check/s08i3p02.png new file mode 100644 index 0000000000000000000000000000000000000000..23d16c71e25c8df5e1f17bad4a7832e89bd4b8df GIT binary patch literal 255 zcmeAS@N?(olHy`uVBq!ia0vp^93afW1|*O0@9PFqEa{HEjtq=#3k+XOiwE-AlDyqr z82*Fcg1yTp14W!XT^vIsF1Pl3@-ZlIFtb0N|CICBtEvkUrh0oBCts49RH4D7@}@#( z1tUXc@}A#qshtJa`WyQn**tNc#`t4t_us6rWy|>f*Z%rB4QQHbiEBhjN@7W>RdP`( zkYX@0Ff`FMG}1LR4ly#eGO)A)vJEV(3=9f+uB)PG$jwj5Osixtw6wI)H89aNFby#> evNABXG6HHa2Wr?Xy{itWfx*+&&t;ucLK6TtHBZ<8 literal 0 HcmV?d00001 diff --git a/impeller/third_party/stb/stb/tests/pngsuite/primary_check/s08n3p02.png b/impeller/third_party/stb/stb/tests/pngsuite/primary_check/s08n3p02.png new file mode 100644 index 0000000000000000000000000000000000000000..4a46016665abd286a25d958ae4b1c5b9a470b46d GIT binary patch literal 255 zcmeAS@N?(olHy`uVBq!ia0vp^93afW1|*O0@9PFqEa{HEjtq=#3k+XOiwE-AlDyqr z82*Fcg1yTp14W!XT^vIsF1Pl3@-ZlIFtb0N|CICBtEvkUrh0oBCts49RH4D7@}@#( z1tUXc@}A#qshtJa`WyQn**tNc#`t4t_us6rWy|>f*Z%rB4QQHbiEBhjN@7W>RdP`( zkYX@0Ff`FMG}1LR4ly#eGO)A)vJEV(3=9f+uB)PG$jwj5Osixtw6wI)H89aNFby#> evNABXG6ZtXff{B$d2|`5fx*+&&t;ucLK6T;Pf-Z~ literal 0 HcmV?d00001 diff --git a/impeller/third_party/stb/stb/tests/pngsuite/primary_check/s09i3p02.png b/impeller/third_party/stb/stb/tests/pngsuite/primary_check/s09i3p02.png new file mode 100644 index 0000000000000000000000000000000000000000..ea14f9be0e000d9079a2305b26259234b6f9e810 GIT binary patch literal 263 zcmeAS@N?(olHy`uVBq!ia0vp^oFL4>1|%O$WD@{VEa{HEjtq=#3k+XOiwE-AlDyqr z82*Fcg1yTp14X<%T^vIsF85AwdcG!ScDmhme{?4J9NNta?@PFXlDefiG+N2H84PR{%Hg&|=ZpZJGjrlmkrRZCnW zN>UO_QmvAUQh^kMk%6I!uAz~xp>c?jv6X?P6_9OUVP#-Y$a7s4MMG|WN@iLmgQ2CR ng|2~#u7PQYk&%^wv6T@}gE>&cX6apZKn)C@u6{1-oD!M<7}iv9 literal 0 HcmV?d00001 diff --git a/impeller/third_party/stb/stb/tests/pngsuite/primary_check/s09n3p02.png b/impeller/third_party/stb/stb/tests/pngsuite/primary_check/s09n3p02.png new file mode 100644 index 0000000000000000000000000000000000000000..7a822537f73bf0cd78f93f717d98e3a658f5ae2b GIT binary patch literal 263 zcmeAS@N?(olHy`uVBq!ia0vp^oFL4>1|%O$WD@{VEa{HEjtq=#3k+XOiwE-AlDyqr z82*Fcg1yTp14X<%T^vIsF85AwdcG!ScDmhme{?4J9NNta?@PFXlDefiG+N2H84PR{%Hg&|=ZpZJGjrlmkrRZCnW zN>UO_QmvAUQh^kMk%6I!uAz~xp>c?jv6X?P6_9OUVP#-Y$a7s4MMG|WN@iLmgQ2CR ng|2~#u7PQYk&%^wv6Uf^YYx;f^U0&jKn)C@u6{1-oD!Mb3K)h=a~;X zoA_yLw#%*AR)m*4g=~I*oznCEXTim0e7<*qydRxMEjY%hjgc*~fP7 zxX%5s)HvAOMsvUYgbq88b3K)h=a~;X zoA_yLw#%*AR)m*4g=~I*oznCEXTim0e7<*qydRxMEjY%hjgc*~fP7 zxX%5s)HvAOMsvUYgbq88zopr0B;tb)Bpeg literal 0 HcmV?d00001 diff --git a/impeller/third_party/stb/stb/tests/pngsuite/primary_check/s33i3p04.png b/impeller/third_party/stb/stb/tests/pngsuite/primary_check/s33i3p04.png new file mode 100644 index 0000000000000000000000000000000000000000..0faaa74108eebe50057ae9499393d39730e86096 GIT binary patch literal 470 zcmeAS@N?(olHy`uVBq!ia0vp^iXhCv1|-9u9Lfh$Ea{HEjtq=#3k+XOiwE-AlDyqr z82*Fcg1yTpGcYhpdb&7xXE?#OpofoHSozcc#|tlsRr@bsYtYqFa9vsHSMAN4S> za7SqE>f91DQ`YmugRUh(2ZPxd&Ro)t6={6q!@$7d?Ic)W+T`>r%dA`JK}J(t=dHqA zCfdz>$^6g6=$b; z9(?oL*-OK0*~hA!idjuc6E1dSaGdTlQrM(!ZKm+kZ1C7HKHUXu_V$ literal 0 HcmV?d00001 diff --git a/impeller/third_party/stb/stb/tests/pngsuite/primary_check/s33n3p04.png b/impeller/third_party/stb/stb/tests/pngsuite/primary_check/s33n3p04.png new file mode 100644 index 0000000000000000000000000000000000000000..599171c7b1a3569a121ed3c7b751c66c92325751 GIT binary patch literal 470 zcmeAS@N?(olHy`uVBq!ia0vp^iXhCv1|-9u9Lfh$Ea{HEjtq=#3k+XOiwE-AlDyqr z82*Fcg1yTpGcYhpdb&7xXE?#OpofoHSozcc#|tlsRr@bsYtYqFa9vsHSMAN4S> za7SqE>f91DQ`YmugRUh(2ZPxd&Ro)t6={6q!@$7d?Ic)W+T`>r%dA`JK}J(t=dHqA zCfdz>$^6g6=$b; z9(?oL*-OK0*~hA!idjuc6E1dSaGdTlQrM(!ZKm+kZ1C7HKHUXu_Vh&(&(E z@>ArT>|ms#D119{?Ys5I^lenc7>y3iT^k82~$if4~t!7e0Iw{E%(~Yk1`V+9>#BW_||>lR{ury z6x?a$u{rKaRh&(&(E z@>ArT>|ms#D119{?Ys5I^lenc7>y3iT^k82~$if4~t!7e0Iw{E%(~Yk1`V+9>#BW_||>lR{ury z6x?a$u{rKaRxXlEs(F-fX7*OW%;{x7lNzp-*Rl&HuD9~zgcceXTF)( z&Zxk|DZ6Wih}}%q)EhffH@`I&O=e7(`MiF?4yDKt76zWzL)bPY+ zrdLb*5qDOehL-COXR~=SANOJF>&=hl4v?DcI3>!%aY7+e>s6x=A2#)js?K@44J1P( zEG!Im?K6@$mpr%n=z3XJ6Stb>-}9XH=hUm%p<`>G6Fdt}Vh}qbIGI$vbQB`?`BVP)t{QGbebpY&X%(esuzB9 z*w5JbH-q{5vYsMf=&6>tMwFx^mZVxG7o`Fz1|tJQ6J0|iT|?s#BV#KAODiDTz{1MF zppfUfDvE~O{FKbJN(MtqOAB2C6I}z-5F;Zi17j;=GhG97poZ>$^F9MLFnGH9xvXxXlEs(F-fX7*OW%;{x7lNzp-*Rl&HuD9~zgcceXTF)( z&Zxk|DZ6Wih}}%q)EhffH@`I&O=e7(`MiF?4yDKt76zWzL)bPY+ zrdLb*5qDOehL-COXR~=SANOJF>&=hl4v?DcI3>!%aY7+e>s6x=A2#)js?K@44J1P( zEG!Im?K6@$mpr%n=z3XJ6Stb>-}9XH=hUm%p<`>G6Fdt}Vh}qbIGI$vbQB`?`BVP)t{QGbebpY&X%(esuzB9 z*w5JbH-q{5vYsMf=&6>tMwFx^mZVxG7o`Fz1|tJQ6J0|iT|?s#BV#KAODiDTz{1MF zppfUfDvE~O{FKbJN(MtqOAB2C6I}z-5F;Zi17j;AAlDqIK|K4Z0Z;>jr>mdKI;Vst E00wZd8UO$Q literal 0 HcmV?d00001 diff --git a/impeller/third_party/stb/stb/tests/pngsuite/primary_check/s36i3p04.png b/impeller/third_party/stb/stb/tests/pngsuite/primary_check/s36i3p04.png new file mode 100644 index 0000000000000000000000000000000000000000..d61491fac8816dce998f63673cc1983e193534a6 GIT binary patch literal 448 zcmeAS@N?(olHy`uVBq!ia0vp^Dj>|k1|%Oc%$NbBSkfJR9T^zg78t&m77ygJC3(BM zF#HF>1$&oIW?*1s_H=O!i8%ardMsbF0Z;SXPV479wp+hDbUR%xedC@fXW*p8dQiPX zmWyG}CSjF@Dh^s6O*2O++{-vy2@$}Q#e_SOy&iI8)by!i!w^r{J&nb457exi80n#T{ zx43Y%@kp%TPZy6%4Ykdu;sRl!3(D69(sS985lsSC9V-ADTyViR>?)FK#IZ0 zz|ch3&`8(NIK;@<%D~bJ$TqOBGB7CQxvq+$AvZrIGp&-r(9+UE*T6*Az%<0j$jZRj a${5Hs2Wq%|@aa0B1_n=8KbLh*2~7aHMW@gJ literal 0 HcmV?d00001 diff --git a/impeller/third_party/stb/stb/tests/pngsuite/primary_check/s36n3p04.png b/impeller/third_party/stb/stb/tests/pngsuite/primary_check/s36n3p04.png new file mode 100644 index 0000000000000000000000000000000000000000..1f50479e48ebcbf5c106e1baa714a47fcbee2448 GIT binary patch literal 448 zcmeAS@N?(olHy`uVBq!ia0vp^Dj>|k1|%Oc%$NbBSkfJR9T^zg78t&m77ygJC3(BM zF#HF>1$&oIW?*1s_H=O!i8%ardMsbF0Z;SXPV479wp+hDbUR%xedC@fXW*p8dQiPX zmWyG}CSjF@Dh^s6O*2O++{-vy2@$}Q#e_SOy&iI8)by!i!w^r{J&nb457exi80n#T{ zx43Y%@kp%TPZy6%4Ykdu;sRl!3(D69(sS985lsSC9V-ADTyViR>?)FK#IZ0 zz|ch3&`8(NIK;@<%D~bJ$TqOBGB7CQxvq+$AvZrIGp&-r(9+UE*T6*Az%<0j$jZRj a$_U6c2Wk+{erf>Jz~JfX=d#Wzp$Pz@eWo`6 literal 0 HcmV?d00001 diff --git a/impeller/third_party/stb/stb/tests/pngsuite/primary_check/s37i3p04.png b/impeller/third_party/stb/stb/tests/pngsuite/primary_check/s37i3p04.png new file mode 100644 index 0000000000000000000000000000000000000000..2906fa311661d670be012c178ea9b6c57798af70 GIT binary patch literal 478 zcmeAS@N?(olHy`uVBq!ia0vp^svyk41|*NpQ(y*CEa{HEjtq=#3k+XOiwE-AlDyqr z82*Fcg1yTpGcYhJdb&7xXlJ(ll~0Z((TX8FB!7lPZTPirmv>)5lT&PgCM>|5e? zmIVzn?A8|H9T0ePg6j2gGc!Wi#S)F1S2)~iJJ`l53LZ+nBlwf`LYWRS1-T1^IpkF z$4Wxt??3*%yZbX1pFB2AJtU;{o6;(cHNS(7-;lezz43^7~~k zT7LiRLfw78b$lW>-VHw-Qgf{A{$Jh$TYs;~;lCyqv-|zygU!F6h=1Rw&U>>y=J%fo z?`GdsjW7O`s=!^WrvnPx64!{5l*E!$tK_0oAjM#0U}&OiXryas9Aac_WngIqWE)sm z85k7uTn9%6TtjYtN@iLmgQ2CRg|2~#u7PQYk&%^wv6YE|u7NpF!=u-WE&y#{@O1Ta JS?83{1OQL@!BYSL literal 0 HcmV?d00001 diff --git a/impeller/third_party/stb/stb/tests/pngsuite/primary_check/s37n3p04.png b/impeller/third_party/stb/stb/tests/pngsuite/primary_check/s37n3p04.png new file mode 100644 index 0000000000000000000000000000000000000000..8931b859daf0a13f2c648a566023d0cb1ecafd61 GIT binary patch literal 478 zcmeAS@N?(olHy`uVBq!ia0vp^svyk41|*NpQ(y*CEa{HEjtq=#3k+XOiwE-AlDyqr z82*Fcg1yTpGcYhJdb&7xXlJ(ll~0Z((TX8FB!7lPZTPirmv>)5lT&PgCM>|5e? zmIVzn?A8|H9T0ePg6j2gGc!Wi#S)F1S2)~iJJ`l53LZ+nBlwf`LYWRS1-T1^IpkF z$4Wxt??3*%yZbX1pFB2AJtU;{o6;(cHNS(7-;lezz43^7~~k zT7LiRLfw78b$lW>-VHw-Qgf{A{$Jh$TYs;~;lCyqv-|zygU!F6h=1Rw&U>>y=J%fo z?`GdsjW7O`s=!^WrvnPx64!{5l*E!$tK_0oAjM#0U}&OiXryas9Aac_WngIqWE)sm z85k7uTn9%6TtjYtN@iLmgQ2CRg|2~#u7PQYk&%^wv6Zobu7NpF!wbFDCxJFFc)I$z JtaD0e0suu(z?}d9 literal 0 HcmV?d00001 diff --git a/impeller/third_party/stb/stb/tests/pngsuite/primary_check/s38i3p04.png b/impeller/third_party/stb/stb/tests/pngsuite/primary_check/s38i3p04.png new file mode 100644 index 0000000000000000000000000000000000000000..becf5a1df6935d3524bed7d0b279df4e338b1b8a GIT binary patch literal 439 zcmeAS@N?(olHy`uVBq!ia0vp^Y9P$P1|(P5zFY^SSkfJR9T^zg78t&m77ygJC3(BM zF#HF>1$&oI28#Uhba4!cIQ(|{MZRVQ9#{YAe~b4wuHRSf__jqi&+Rnx^$7|BML}NP zj3rArJ$JB7Qs&&bV*dAK{%h+ER>(0uQD|KM?c;XWzIW`e`=U7QzdL_DsPn~^eHEj& zpjTx8!}+Dj+h#|yyIGz(_VewBwCN3Nw;S<=Ol8^ezA#7RxxDA&Cdu6uRvls8k>~e& zpX+UzrG4K*OqlJ`mYAKVZ(6Ie-*{Gfk!738hKnW#)oxs^ns@(^f2&1gR1ipoT}U S7hM2qVDNPHb6Mw<&;$Vcd#phK literal 0 HcmV?d00001 diff --git a/impeller/third_party/stb/stb/tests/pngsuite/primary_check/s38n3p04.png b/impeller/third_party/stb/stb/tests/pngsuite/primary_check/s38n3p04.png new file mode 100644 index 0000000000000000000000000000000000000000..43f8c983f111c2e7d7812f99aedfc787b68680f8 GIT binary patch literal 439 zcmeAS@N?(olHy`uVBq!ia0vp^Y9P$P1|(P5zFY^SSkfJR9T^zg78t&m77ygJC3(BM zF#HF>1$&oI28#Uhba4!cIQ(|{MZRVQ9#{YAe~b4wuHRSf__jqi&+Rnx^$7|BML}NP zj3rArJ$JB7Qs&&bV*dAK{%h+ER>(0uQD|KM?c;XWzIW`e`=U7QzdL_DsPn~^eHEj& zpjTx8!}+Dj+h#|yyIGz(_VewBwCN3Nw;S<=Ol8^ezA#7RxxDA&Cdu6uRvls8k>~e& zpX+UzrG4K*OqlJ`mYAKVZ(6Ie-*{Gfk!738hKnW#)oxs^ns@(^f2&1gR1ipoSNE St4{(oFnGH9xvXLAR)1|)kH2buyYmUKs7M+U~W1%@xC#RK_lN#5=* z4F5rJ!QSPQ85kJNJY5_^A`YLO5xwrPf`D^wr|o6k4O#c))s}iz#AvSl7=BdY>a2)j z1||WqEn8kDE`2)pW`f_`X%pVxFWSP(uz6kl8k1w2PD(Hcq!;%ZdS8%Q+HbJY4;?wdEGtk}LMy>|1O$GL5pd9-b^4bpV=o?Z# zS5tx_Zdg?{W@d+&HSX_ueM)Vc9Gkj-Q~jpJg_F#W@{|C>RJFu4q9i4;B-JXpC>2OC z7#SFv=o%X78XAWf8Cw}xS^?Px7FGrZg*?|)Q8eV{r(~v8G8kG~TId>>=o*-Y7#Ud^ c7+aYDHJAf6l$LD%3Dm&g>FVdQ&MBb@0C};)`v3p{ literal 0 HcmV?d00001 diff --git a/impeller/third_party/stb/stb/tests/pngsuite/primary_check/s39n3p04.png b/impeller/third_party/stb/stb/tests/pngsuite/primary_check/s39n3p04.png new file mode 100644 index 0000000000000000000000000000000000000000..d37d66d44129066477311007b621745cee75fd73 GIT binary patch literal 499 zcmeAS@N?(olHy`uVBq!ia0vp^>LAR)1|)kH2buyYmUKs7M+U~W1%@xC#RK_lN#5=* z4F5rJ!QSPQ85kJNJY5_^A`YLO5xwrPf`D^wr|o6k4O#c))s}iz#AvSl7=BdY>a2)j z1||WqEn8kDE`2)pW`f_`X%pVxFWSP(uz6kl8k1w2PD(Hcq!;%ZdS8%Q+HbJY4;?wdEGtk}LMy>|1O$GL5pd9-b^4bpV=o?Z# zS5tx_Zdg?{W@d+&HSX_ueM)Vc9Gkj-Q~jpJg_F#W@{|C>RJFu4q9i4;B-JXpC>2OC z7#SFv=o%X78XAWf8Cw}xS^?Px7FGrZg*?|)Q8eV{r(~v8G8kG~TId>>=o*-Y7#Ud^ c7+VzuQ<002!1)diDrSIp2&+xDP9Voi?+@_5alfNvva4X2} z+=@;G2P=;zKYJvJs->WRWJaZS5oNMZu=I%|~=4`WNbdjGPwtIhkafp$zm4T%dkZoXLWnfUqb6pihLvDUbW?ChK qp{1pTu7QcJfoX`5k(Gh5l?hOTIZ#7s$>yIx4Gf;HelF{r5}E*qORR7J literal 0 HcmV?d00001 diff --git a/impeller/third_party/stb/stb/tests/pngsuite/primary_check/s40n3p04.png b/impeller/third_party/stb/stb/tests/pngsuite/primary_check/s40n3p04.png new file mode 100644 index 0000000000000000000000000000000000000000..6f8596ca14510022eb45b8a6e05fca280b354e32 GIT binary patch literal 463 zcmeAS@N?(olHy`uVBq!ia0vp^8X(NU1|)m_?Z^dEEa{HEjtq=#3k+XOiwE-AlDyqr z82*Fcg1yTpGcYg;dAc};L>zuQ<002!1)diDrSIp2&+xDP9Voi?+@_5alfNvva4X2} z+=@;G2P=;zKYJvJs->WRWJaZS5oNMZu=I%|~=4`WNbdjGPwtIhkafp$zm4T%dkZoXLWnfUqb6pihLvDUbW?ChK rp{1pTu7QcJfoX`5k(Gh5l`&9*IZ#9W#$6A98W=oX{an^LB{Ts5ii51A literal 0 HcmV?d00001 diff --git a/impeller/third_party/stb/stb/tests/pngsuite/primary_check/tbbn0g04.png b/impeller/third_party/stb/stb/tests/pngsuite/primary_check/tbbn0g04.png new file mode 100644 index 0000000000000000000000000000000000000000..8d9f7d52b0446496c9db04c1ab90dd967dbdbf71 GIT binary patch literal 762 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 literal 0 HcmV?d00001 diff --git a/impeller/third_party/stb/stb/tests/pngsuite/primary_check/tbgn3p08.png b/impeller/third_party/stb/stb/tests/pngsuite/primary_check/tbgn3p08.png new file mode 100644 index 0000000000000000000000000000000000000000..fa5cdbc95a44cf76abbf44527dbbd7e12d72c268 GIT binary patch literal 1911 zcmV--2Z;EIP)%G*ochBz3{Q9u#ja*|cB~p&`lXidl&7RNso^yV4RuV$+bDI3KdVdjs zbUH0&W@bdWTz(-5ZO?N!lgWs0zfCedeVOIuWkj(;1j2vy1FWo$s{u~Gb}DH;;Ge8@82ghKaY~jQ3H&2Bw=9N$Oye}y+zO4Zxb0FMo|>$ zxdF=Mvbb^M2FYZSyLazm>N>8QH}PdMc<u>Q!_=1)6?852H~UJv~H{Ne(P6K^qF74E(1J_Ag-dgAcH~-5VmVR;wfu z2_`2er42J=v!Do1Oj{?kFzo~gpgZ}lNlc7dZofI=I7aW zvK}x z6yy3hN7s%Lc84(xgKD)(ypF&EU>F90K!EP2qgXpqb0 zC>AX)UVIl#^8;7Vv;vwI1Yl@rXlnpjmQgAS*ntuh`~@Bb9?@6qqheLKu_5C~8zmC!VeLZN`BX_%(q!i7Ijsq_GUL6)zgst?ii2&Iz3zF+R!R3{w^hr{fs z?|?PnL2GZWe-0r87sD6%YS&kM$(MXu_!J=oilPt<1~Ci+P16uUFhBnWmC6Wk8(IDb zJ9p0F@uYCOEA;l7#D-#919-jL@fu#J13$D>_~KVzpes6tZh!|^1-Gr$G3%?*Xdr~3 zS`9Kc_XbczQU1umgK6UNeiS9j?%gmx-n;2)-T)8?_^=o4;0NvjnbwI*(k1H6I<#+X zwW)3b09ac3HJ0TD&ePwY$KmjB<;p18?7Pg*k25k7e{8G%m;#>1yjwb zc8}445JF&CR_E@pELd2WXh%QI_U#V7`Q`|kHVC9}yRR`a65Rx_p$NBkb+ta2fCD@k z50&K#T9?L(w$j>f16Y=YEXy4Q3WeR&>jB_zba&@@^eBU2^Z;60U7bWC@ni*p!62%p zLKE=Az;^?%0`x`aLuIps8ab&p${F`8~iK46^%PUBd4M;FL8pP#t zNlye&6h#_z4z>V%u&cNW&yoj1fZj?6%^wx8EDOUhFin$m+JR*?fi&T;PItG1V9zO&u|@HhNeb_<}>g0frzR&hEF za=C79-W&sb^z{6Rk3W8oLx&DMb~4(efU2s}hrjz!`sBzbc-^fxeRMh68VIb`QY@vT zz}nhcJ0-KgGHYu#u3g)UVKfMb|IYdIr#NzCV#~2?%c(e#NJytopJrfSfcWirD|(~d zB(k|71ps}0w^5W8EDKS=+{B;I011*UEQJ~Ri zU|AMkZ-K6^i#Qx9UVXKTsi}{MM0P)QLVoI0yl!*zb7JzJlT`0jd2`#Fxb1EfMd9JY zhosYKOw;7Sg9jWwe3&z5&Y-I5GmYoZ3;>{MnmBv*Ea`L_S(cGynS1x{k3v)jE;^zxAPYQ0I1jNqEIN%+uQqpIe%e*pThnN3zbN{>Y)K>0000bbVXQnWMOn= zI%9HWVRU5xGB7bTEip1JF*8&$GdeIiIx#XWFgQ9eFmVjmB>(^bC3HntbYx+4Wjbwd xWNBu305UK!F)c7OEio`uF)}(bF*-0bEigAaFffPyJZAs^002ovPDHLkV1hJDb{GHv literal 0 HcmV?d00001 diff --git a/impeller/third_party/stb/stb/tests/pngsuite/primary_check/tbrn2c08.png b/impeller/third_party/stb/stb/tests/pngsuite/primary_check/tbrn2c08.png new file mode 100644 index 0000000000000000000000000000000000000000..bbe748fd32d611cece365935600b1cbc400ae247 GIT binary patch literal 1901 zcmV-z2a@=SP)>OdRT@iEaEE+vBlk=GTK0vsM})OQn6Jxr|2h{rsNi`#kfEB!uAS4EbkOe;a`1 z|3SKwv&gW(Wsc7p61@Yd#tXm;z}g&hC&=@Y7!l1&d`1441vy0X(#WL zUgv>SDkWxSW|*3qVsUX1TcJQhD1>)@9wnKi2xNg>K(i#_92j8l*|W5tKhM5_0Te}% zb_}q-zAk?F;Rj}BW_b4O8K$n|oS(=0^eOd`2&xcdffSJSct~}0kbV9fGn>T)3;??v z4%R;YnBI{Q1_uWT27}bq)k)g|6pKai=+Pq{K72?h6v8x3T(KDLnHfCu^SE^#H&6%c z1{^>Ph)NQ%)>d?%k5V*>oJi0F=s@WFd6akF!EU#sC<;A2Jp_Y6`uqFw`Fv6h!1VOA zxOwv?nx?UNuVZ|i&Y2kuDsI(09#-Fd7pu9MzDJMP8xG^KEOr4!U>(Q_x$#fujd3I)2ly0(@e9*>K$u`x{3 z#8zj+<#16hmnjqqluD(Y7`rA+(pF&EV45agueT~*15j62C%GMN z5o!w|8U=R;?y`SkKf|G691aJ8KmgM;Szli#pU-dKR+2Q~bA`6H zCEDAw1cSkv=W7O$)9J)rbd%F^Aj93hyS%sXUiDOVyPZHFfZy*Yo6VBTDMSqiSlUsaF6EzVG@TP0`THdS%|G!Buu4gs5JnQVAgh znpV%_$G-tGD9WEXemu_LU^lAT&Axqb;>12|Hk-69fY0Z{rMaL1SOQi-cj(-4+@Yuy z!2$%>g?$SEz{0|Zl*=mcS2{bRxZN%uJop8%*zcK}8>YX1aEocT0I1r=aF6EZERjeGU5^9PK#GP2okNEXy#YXR1HcR2 z;qHnT+F`N0Sjj>OJi=2Iw=4@?*NH}ZDyCtYrltalvWhIP0Zw3u!-x0acDtol0w{_i zwbr#l8E61s(nnx901Y6Dq5@#OOuuDW)d`nM`G7Ux-*~-6{Qfekssbm0hYSyQ^1ous zO4hx*8+*wP9w0thwaus0n1Lbzl0jrcs zGSkxsFpL6iZMXRL+iQIE(J!}M%eLK$2L}hGt5>hm(b2)d=Laj|8yVOX|8WUgTJ9su zs~hGF6AVVUbLWp7K0Nr!o$!@wd1GUvbmhtw&W)U-Bid2TfX%XPDS^+IqqX&K?AjIP z)Tst0CjP|!{d-=!A-{GlR}@9MaNz=d|LPN$CNGiC%G*ochBz3{Q9u#ja*|cB~p&`lXidl&7RNso^yV4RuV$+bDI3KdVdjs zbUH0&W@bdWTz(-5ZO?N!lgWs0zfCedeVOIuWkj(;1j2vy1FWo$s{u~Gb}DH;;Ge8@82ghKaY~jQ3H&2Bw=9N$Oye}y+zO4Zxb0FMo|>$ zxdF=Mvbb^M2FYZSyLazm>N>8QH}PdMc<u>Q!_=1)6?852H~UJv~H{Ne(P6K^qF74E(1J_Ag-dgAcH~-5VmVR;wfu z2_`2er42J=v!Do1Oj{?kFzo~gpgZ}lNlc7dZofI=I7aW zvK}x z6yy3hN7s%Lc84(xgKD)(ypF&EU>F90K!EP2qgXpqb0 zC>AX)UVIl#^8;7Vv;vwI1Yl@rXlnpjmQgAS*ntuh`~@Bb9?@6qqheLKu_5C~8zmC!VeLZN`BX_%(q!i7Ijsq_GUL6)zgst?ii2&Iz3zF+R!R3{w^hr{fs z?|?PnL2GZWe-0r87sD6%YS&kM$(MXu_!J=oilPt<1~Ci+P16uUFhBnWmC6Wk8(IDb zJ9p0F@uYCOEA;l7#D-#919-jL@fu#J13$D>_~KVzpes6tZh!|^1-Gr$G3%?*Xdr~3 zS`9Kc_XbczQU1umgK6UNeiS9j?%gmx-n;2)-T)8?_^=o4;0NvjnbwI*(k1H6I<#+X zwW)3b09ac3HJ0TD&ePwY$KmjB<;p18?7Pg*k25k7e{8G%m;#>1yjwb zc8}445JF&CR_E@pELd2WXh%QI_U#V7`Q`|kHVC9}yRR`a65Rx_p$NBkb+ta2fCD@k z50&K#T9?L(w$j>f16Y=YEXy4Q3WeR&>jB_zba&@@^eBU2^Z;60U7bWC@ni*p!62%p zLKE=Az;^?%0`x`aLuIps8ab&p${F`8~iK46^%PUBd4M;FL8pP#t zNlye&6h#_z4z>V%u&cNW&yoj1fZj?6%^wx8EDOUhFin$m+JR*?fi&T;PItG1V9zO&u|@HhNeb_<}>g0frzR&hEF za=C79-W&sb^z{6Rk3W8oLx&DMb~4(efU2s}hrjz!`sBzbc-^fxeRMh68VIb`QY@vT zz}nhcJ0-KgGHYu#u3g)UVKfMb|IYdIr#NzCV#~2?%c(e#NJytopJrfSfcWirD|(~d zB(k|71ps}0w^5W8EDKS=+{B;I011*UEQJ~Ri zU|AMkZ-K6^i#Qx9UVXKTsi}{MM0P)QLVoI0yl!*zb7JzJlT`0jd2`#Fxb1EfMd9JY zhosYKOw;7Sg9jWwe3&z5&Y-I5GmYoZ3;>{MnmBv*Ea`L_S(cGynS1x{k3v)jE;^zxAPYQ0I1jNqEIN%+uQqpIe%e*pThnN3zbN{>Y)K>0000bbVXQnWMOn= zI%9HWVRU5xGB7bTEip1JF*8&$GdeIiIx#XWFgQ9eFmVjmB>(^bC3HntbYx+4Wjbwd xWNBu305UK!F)c7OEio`uF)}(bF*-0bEigAaFffPyJZAs^002ovPDHLkV1jhUc%J|O literal 0 HcmV?d00001 diff --git a/impeller/third_party/stb/stb/tests/pngsuite/primary_check/tbyn3p08.png b/impeller/third_party/stb/stb/tests/pngsuite/primary_check/tbyn3p08.png new file mode 100644 index 0000000000000000000000000000000000000000..4fbdb36a33d7f3c3c82addd028dffed7a0d3f0ed GIT binary patch literal 1911 zcmV--2Z;EIP)%G*ochBz3{Q9u#ja*|cB~p&`lXidl&7RNso^yV4RuV$+bDI3KdVdjs zbUH0&W@bdWTz(-5ZO?N!lgWs0zfCedeVOIuWkj(;1j2vy1FWo$s{u~Gb}DH;;Ge8@82ghKaY~jQ3H&2Bw=9N$Oye}y+zO4Zxb0FMo|>$ zxdF=Mvbb^M2FYZSyLazm>N>8QH}PdMc<u>Q!_=1)6?852H~UJv~H{Ne(P6K^qF74E(1J_Ag-dgAcH~-5VmVR;wfu z2_`2er42J=v!Do1Oj{?kFzo~gpgZ}lNlc7dZofI=I7aW zvK}x z6yy3hN7s%Lc84(xgKD)(ypF&EU>F90K!EP2qgXpqb0 zC>AX)UVIl#^8;7Vv;vwI1Yl@rXlnpjmQgAS*ntuh`~@Bb9?@6qqheLKu_5C~8zmC!VeLZN`BX_%(q!i7Ijsq_GUL6)zgst?ii2&Iz3zF+R!R3{w^hr{fs z?|?PnL2GZWe-0r87sD6%YS&kM$(MXu_!J=oilPt<1~Ci+P16uUFhBnWmC6Wk8(IDb zJ9p0F@uYCOEA;l7#D-#919-jL@fu#J13$D>_~KVzpes6tZh!|^1-Gr$G3%?*Xdr~3 zS`9Kc_XbczQU1umgK6UNeiS9j?%gmx-n;2)-T)8?_^=o4;0NvjnbwI*(k1H6I<#+X zwW)3b09ac3HJ0TD&ePwY$KmjB<;p18?7Pg*k25k7e{8G%m;#>1yjwb zc8}445JF&CR_E@pELd2WXh%QI_U#V7`Q`|kHVC9}yRR`a65Rx_p$NBkb+ta2fCD@k z50&K#T9?L(w$j>f16Y=YEXy4Q3WeR&>jB_zba&@@^eBU2^Z;60U7bWC@ni*p!62%p zLKE=Az;^?%0`x`aLuIps8ab&p${F`8~iK46^%PUBd4M;FL8pP#t zNlye&6h#_z4z>V%u&cNW&yoj1fZj?6%^wx8EDOUhFin$m+JR*?fi&T;PItG1V9zO&u|@HhNeb_<}>g0frzR&hEF za=C79-W&sb^z{6Rk3W8oLx&DMb~4(efU2s}hrjz!`sBzbc-^fxeRMh68VIb`QY@vT zz}nhcJ0-KgGHYu#u3g)UVKfMb|IYdIr#NzCV#~2?%c(e#NJytopJrfSfcWirD|(~d zB(k|71ps}0w^5W8EDKS=+{B;I011*UEQJ~Ri zU|AMkZ-K6^i#Qx9UVXKTsi}{MM0P)QLVoI0yl!*zb7JzJlT`0jd2`#Fxb1EfMd9JY zhosYKOw;7Sg9jWwe3&z5&Y-I5GmYoZ3;>{MnmBv*Ea`L_S(cGynS1x{k3v)jE;^zxAPYQ0I1jNqEIN%+uQqpIe%e*pThnN3zbN{>Y)K>0000bbVXQnWMOn= zI%9HWVRU5xGB7bTEip1JF*8&$GdeIiIx#XWFgQ9eFmVjmB>(^bC3HntbYx+4Wjbwd xWNBu305UK!F)c7OEio`uF)}(bF*-0bEigAaFffPyJZAs^002ovPDHLkV1myucDn!o literal 0 HcmV?d00001 diff --git a/impeller/third_party/stb/stb/tests/pngsuite/primary_check/tm3n3p02.png b/impeller/third_party/stb/stb/tests/pngsuite/primary_check/tm3n3p02.png new file mode 100644 index 0000000000000000000000000000000000000000..babdebefb5f83c6d969414485fe31d9a3b8486b3 GIT binary patch literal 306 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 literal 0 HcmV?d00001 diff --git a/impeller/third_party/stb/stb/tests/pngsuite/primary_check/tp0n3p08.png b/impeller/third_party/stb/stb/tests/pngsuite/primary_check/tp0n3p08.png new file mode 100644 index 0000000000000000000000000000000000000000..e5a29d6d627bee342ee10dfc9140169e96a05163 GIT binary patch literal 1959 zcmV;Y2Uz%tP)r| z0M6{t%uG}ytJi7LDy?d@s!}gnX#%Q=N?Z&D2@M1nfD%x|1rn59pn`;i)=pE46e^T9 zTA3oDsDwdNldOhdAT%F1{%9Q%+d0R_=iP;kS(XqQF4jjnvR}*Zd4A{re4OVs96EGJ z@DrN!Q#uDQKJUgIJ9aQJF+ngG+qNYG7@m~_ZEbBtA`z}#yT-kH_ps-3m>xW!CK^RfCdmO>SxPUktf`6BZ@UF>BHX=um!hIjH8_Yf9>*~}%rYPgq=8wNi+p`O-wh8} z_HR}coMtoe_uixN<(CPCLIi_BIy*b(0aS8e|Ni|%A`v2y2uF?_p;#nQ3Oe{+hwTL1bCR@Anf725D+) z;^fJba{-2ihUo6@CZEqEgg}xc)<&aj7#>CeOhCcqV$|>F?y6O+i$rK18Nm-Yl1WG= znFhWugM9@oTkgSZu>c^1V0d_#fq?-wZQ3+n0k_+W*=(k&stQfh$mjDU*R8{M=MKW{ z=2j@gWMd;jD&=iF5a64c8GaUxvhnIwRv3-gfvl#HSi2Tpd=abNUU8%-3aM0zTrP*- z?`M2`d@g{)VM7Rkq9_#@x`okUt%h_tjUm|zl6EEY+n zQcO=zQz#VBG>t$YKyPpFTmYxjiK?r7T>CLGM~tu%=B?Sc@Y=j+nnpI8C7;hfv#f*= zmCvcEDbndQilU%t8k(lzcDp%!`tJu(FFOYNQsHv&J=krlr zU0o{4behr8QL@=QJw1OWli31%izHpgVz~vVJbEOv@#h<#4&e2AS)N}Gv%q)d@~j_1 z2*G9VWxibbC13Cb=hEj8LLkdB9*+l2)5v5p2qB0@-(+T{3Al|U{gV|dZeX{^u-Rr< zy}C%SG5B-&}(gDGQz0^ov%ZyR6|sE1e~ z20EaDQ+8II>$;AjC?pagBq;~{3y-IOEKedylNby}U=y1+dpLIN*x~>k9UZK?3xl0hCfWC_GU>*L5^aqgX65HfBNB3&0p&uR>j&1&^l+A$|*dLpZ#Fg>2qL zwBEPA5_H1qQLCzY&j%1fQQV^8W|Ya zfTpQ0;;N_>pBicn%dgSSS&GKeYKXp zzCD~je~xF_Gsp6djt-*HD4+fMGg{-VG-sL#C;^JaVg*15!Sdx!>gp`)-rYiXcRL?^ zaN?QHVF?G5PyhZYpYQ&hix)34Ha3P{(z;Rw27`gNwl=nI-Aa3V`;Wct(hepkPMl!- z_U(kj;Yt%yRTYE5!29p-(^bC3HntbYx+4WjbwdWNBu3 t05UK!F)c7OEio`uF)}(bF*-0bEigAaFffPyJZAs^002ovPDHLkV1m3UeQ^K) literal 0 HcmV?d00001 diff --git a/impeller/third_party/stb/stb/tests/pngsuite/primary_check/tp1n3p08.png b/impeller/third_party/stb/stb/tests/pngsuite/primary_check/tp1n3p08.png new file mode 100644 index 0000000000000000000000000000000000000000..9ecd40470c4d39cdedd3d6dcb0960f955a731b5c GIT binary patch literal 1911 zcmV--2Z;EIP)%G*ochBz3{Q9u#ja*|cB~p&`lXidl&7RNso^yV4RuV$+bDI3KdVdjs zbUH0&W@bdWTz(-5ZO?N!lgWs0zfCedeVOIuWkj(;1j2vy1FWo$s{u~Gb}DH;;Ge8@82ghKaY~jQ3H&2Bw=9N$Oye}y+zO4Zxb0FMo|>$ zxdF=Mvbb^M2FYZSyLazm>N>8QH}PdMc<u>Q!_=1)6?852H~UJv~H{Ne(P6K^qF74E(1J_Ag-dgAcH~-5VmVR;wfu z2_`2er42J=v!Do1Oj{?kFzo~gpgZ}lNlc7dZofI=I7aW zvK}x z6yy3hN7s%Lc84(xgKD)(ypF&EU>F90K!EP2qgXpqb0 zC>AX)UVIl#^8;7Vv;vwI1Yl@rXlnpjmQgAS*ntuh`~@Bb9?@6qqheLKu_5C~8zmC!VeLZN`BX_%(q!i7Ijsq_GUL6)zgst?ii2&Iz3zF+R!R3{w^hr{fs z?|?PnL2GZWe-0r87sD6%YS&kM$(MXu_!J=oilPt<1~Ci+P16uUFhBnWmC6Wk8(IDb zJ9p0F@uYCOEA;l7#D-#919-jL@fu#J13$D>_~KVzpes6tZh!|^1-Gr$G3%?*Xdr~3 zS`9Kc_XbczQU1umgK6UNeiS9j?%gmx-n;2)-T)8?_^=o4;0NvjnbwI*(k1H6I<#+X zwW)3b09ac3HJ0TD&ePwY$KmjB<;p18?7Pg*k25k7e{8G%m;#>1yjwb zc8}445JF&CR_E@pELd2WXh%QI_U#V7`Q`|kHVC9}yRR`a65Rx_p$NBkb+ta2fCD@k z50&K#T9?L(w$j>f16Y=YEXy4Q3WeR&>jB_zba&@@^eBU2^Z;60U7bWC@ni*p!62%p zLKE=Az;^?%0`x`aLuIps8ab&p${F`8~iK46^%PUBd4M;FL8pP#t zNlye&6h#_z4z>V%u&cNW&yoj1fZj?6%^wx8EDOUhFin$m+JR*?fi&T;PItG1V9zO&u|@HhNeb_<}>g0frzR&hEF za=C79-W&sb^z{6Rk3W8oLx&DMb~4(efU2s}hrjz!`sBzbc-^fxeRMh68VIb`QY@vT zz}nhcJ0-KgGHYu#u3g)UVKfMb|IYdIr#NzCV#~2?%c(e#NJytopJrfSfcWirD|(~d zB(k|71ps}0w^5W8EDKS=+{B;I011*UEQJ~Ri zU|AMkZ-K6^i#Qx9UVXKTsi}{MM0P)QLVoI0yl!*zb7JzJlT`0jd2`#Fxb1EfMd9JY zhosYKOw;7Sg9jWwe3&z5&Y-I5GmYoZ3;>{MnmBv*Ea`L_S(cGynS1x{k3v)jE;^zxAPYQ0I1jNqEIN%+uQqpIe%e*pThnN3zbN{>Y)K>0000bbVXQnWMOn= zI%9HWVRU5xGB7bTEip1JF*8&$GdeIiIx#XWFgQ9eFmVjmB>(^bC3HntbYx+4Wjbwd xWNBu305UK!F)c7OEio`uF)}(bF*-0bEigAaFffPyJZAs^002ovPDHLkV1jhUc%J|O literal 0 HcmV?d00001 diff --git a/impeller/third_party/stb/stb/tests/pngsuite/primary_check/z00n2c08.png b/impeller/third_party/stb/stb/tests/pngsuite/primary_check/z00n2c08.png new file mode 100644 index 0000000000000000000000000000000000000000..ecaa0d8bab72b7e37df0e032d9d28b2479f59ce1 GIT binary patch literal 422 zcmeAS@N?(olHy`uVBq!ia0vp^3LwnE1|*BCs=fdzmUKs7M+SzC{oH>NS%G}U;vjb? zhIQv;UV>C6dj$D1FjT2AFf_CjA5L~c#`DCET22U5qkch)aFYV-NFc4t5AgJ@`fBl-5Nx~maZQOnHuo_33xy9zG zO_LZ?7}9O4-%rWkQC^n9Vy9{3u!`}Vg^Y#LzXQGOAs>#-W&TrmoOes%a^5e6?Oexp z6gF7qNi?)c_fF`O?wQc89e&`rDD#PerMxE!TY1+!>=s?~n47Vr;33!629nO4bQ qXlZGoYha>lU>ag%WMyD#WeU__4%D#az+nZT1_n=8KbLh*2~7Z8tc<<@ literal 0 HcmV?d00001 diff --git a/impeller/third_party/stb/stb/tests/pngsuite/primary_check/z03n2c08.png b/impeller/third_party/stb/stb/tests/pngsuite/primary_check/z03n2c08.png new file mode 100644 index 0000000000000000000000000000000000000000..ecaa0d8bab72b7e37df0e032d9d28b2479f59ce1 GIT binary patch literal 422 zcmeAS@N?(olHy`uVBq!ia0vp^3LwnE1|*BCs=fdzmUKs7M+SzC{oH>NS%G}U;vjb? zhIQv;UV>C6dj$D1FjT2AFf_CjA5L~c#`DCET22U5qkch)aFYV-NFc4t5AgJ@`fBl-5Nx~maZQOnHuo_33xy9zG zO_LZ?7}9O4-%rWkQC^n9Vy9{3u!`}Vg^Y#LzXQGOAs>#-W&TrmoOes%a^5e6?Oexp z6gF7qNi?)c_fF`O?wQc89e&`rDD#PerMxE!TY1+!>=s?~n47Vr;33!629nO4bQ qXlZGoYha>lU>ag%WMyD#WeU__4%D#az+nZT1_n=8KbLh*2~7Z8tc<<@ literal 0 HcmV?d00001 diff --git a/impeller/third_party/stb/stb/tests/pngsuite/primary_check/z06n2c08.png b/impeller/third_party/stb/stb/tests/pngsuite/primary_check/z06n2c08.png new file mode 100644 index 0000000000000000000000000000000000000000..ecaa0d8bab72b7e37df0e032d9d28b2479f59ce1 GIT binary patch literal 422 zcmeAS@N?(olHy`uVBq!ia0vp^3LwnE1|*BCs=fdzmUKs7M+SzC{oH>NS%G}U;vjb? zhIQv;UV>C6dj$D1FjT2AFf_CjA5L~c#`DCET22U5qkch)aFYV-NFc4t5AgJ@`fBl-5Nx~maZQOnHuo_33xy9zG zO_LZ?7}9O4-%rWkQC^n9Vy9{3u!`}Vg^Y#LzXQGOAs>#-W&TrmoOes%a^5e6?Oexp z6gF7qNi?)c_fF`O?wQc89e&`rDD#PerMxE!TY1+!>=s?~n47Vr;33!629nO4bQ qXlZGoYha>lU>ag%WMyD#WeU__4%D#az+nZT1_n=8KbLh*2~7Z8tc<<@ literal 0 HcmV?d00001 diff --git a/impeller/third_party/stb/stb/tests/pngsuite/primary_check/z09n2c08.png b/impeller/third_party/stb/stb/tests/pngsuite/primary_check/z09n2c08.png new file mode 100644 index 0000000000000000000000000000000000000000..d869f992f80e61f073ea0f55a05aef941bfb6842 GIT binary patch literal 422 zcmeAS@N?(olHy`uVBq!ia0vp^3LwnE1|*BCs=fdzmUKs7M+SzC{oH>NS%G}U;vjb? zhIQv;UV>C6dj$D1FjT2AFf_CjA5L~c#`DCET22U5qkch)aFYV-NFc4t5AgJ@`fBl-5Nx~maZQOnHuo_33xy9zG zO_LZ?7}9O4-%rWkQC^n9Vy9{3u!`}Vg^Y#LzXQGOAs>#-W&TrmoOes%a^5e6?Oexp z6gF7qNi?)c_fF`O?wQc89e&`rDD#PerMxE!TY1+!>=s?~n47Vr;33!629nO4bQ qXlZGoYha>lU>ag%WMyD#WeQ}Q12tS1_$CF^z~JfX=d#Wzp$Pz2HjH2Z literal 0 HcmV?d00001 diff --git a/impeller/third_party/stb/stb/tests/pngsuite/unused/ccwn2c08.png b/impeller/third_party/stb/stb/tests/pngsuite/unused/ccwn2c08.png new file mode 100644 index 0000000000000000000000000000000000000000..47c24817b79c1e2df8eb3f4fe43798f249a063d6 GIT binary patch literal 1514 zcmV004R>004l5008;`004mK004C`008P>0026e000+ooVrmw000Gb zNkl1!`#jIX%gn1pggd$P9Zd(E8@>1o|NOaq`y>7F!|ZsTJzwK126G#Ly6%XM z@b-^5>QWhU%dqD1!_57RBTumVe+8~Hq9eTb8gGRJAwh?*OV}a2z_~^C|CZMU`+s$C zf=f^GMjIPqJ*EzYFsfNGT)Lap?PPm7_!7VJ=-g%pukx;sAJU$c-=j0zF+ofa5q!)a ze#6rhquT+FvGf6bpLReH((V#=343&VbR#+uVMHe)7;@qr4%Ccq1-RxEz0QX|?KUbv zg-D3?a5PfW@#w_lF{X>|p$bm?jc2~d`wI6~f#a;6WITuOqXKjXI#{2ULDi%_otUzR z>7gTt5je5J^Sd~Dle2ye@C#0R_#V2AZlgm~2OH2hsG43tH)a~4d#D*O2R?ta&G)tW z>8Ar@roZFTCSclIO*Ag1ArI+AG!Z7+1hzr17QOyU)Mwc7+NS`==$DuP*T?vqO>|IQ ztcPnUI`m>BLib<}*@i@om~~+l){c| zVM7=SBVi=G`3mjN^Uzb*0scX@ir!A!MZ4%0+DG}A03D)xh@t5bN9Y)dtvG|t!e{A2 zVJr-VH(rqM{a!t^`;)+*NnHXL-9oj{tt~?bm>$Z7h&)1VrgJrYXc=0@ma%1G8KuoD z&+_AUoChDj1~`crIu6P~xu_ZAq5Z4pRfm`!Wn^(jTpi2MGMYsT(bw1_m z-{DWcW?RoW2Sx1%q6`?p5#3d|Bp`vxK|FMT+rjJ7PN`Fx6q_I^ zHsSO@o_kdt`-!tWMURz18H)xjXp0u5EYvg!dKD%@(vVc71xZelk*1^>Sw>ormt<42 zhFmB#g+t{FiXi4}-?><#$4U#@DlI4r4PvoVssT2+X;jvbP04Gr zio7B#DN2fhGApO!Ash15FNBNxItO+uXX|?4s1n(L1}xIhR7?x<%j`IhKSXD!3`rZZ zhRl*T6jO>R>MU|4RaMv=y0F$9} zR4$TPn>VItC~Z@fNxj~;XU>`>Z{Pj*Eq?Q{1ADY{!yAorm%^@LYwChBA?vgK9NhuV zEWJQVYm2(9vPpCPqMTgf?}Pc|ffx6F>Cg}2g?nz4y9M0qW3N!>6l2^z@iOrMH$#`G zQl@pW(kx$*<@%1VVyS645OM(au{s2+Lb!Cm*v-(Kz~fjhVU4fw3PM64Z& QC;$Ke07*qoM6N<$g6Hha`2YX_ literal 0 HcmV?d00001 diff --git a/impeller/third_party/stb/stb/tests/pngsuite/unused/ccwn3p08.png b/impeller/third_party/stb/stb/tests/pngsuite/unused/ccwn3p08.png new file mode 100644 index 0000000000000000000000000000000000000000..8bb2c10981594a83b7a8981436225d0b2241d27b GIT binary patch literal 1554 zcmWkudo+{@9Q~wiYbxpxyQH*PvQk+jg_3M0kMUetMjk0^ibC6FT8+ng>|mu<;>-?$ zm5gBE76za z`V2rL-V2)_s6XvdI|05qg3CyUFdEKU;xM`LGPZ>qA>#tN;_w)^vQv<0j}{EP(no$i z`mo0!AQSgK>Bh#O1Uo1c%9qgC08SDbT*Rc~7NA^cjuaHHr)sr@p!{AFnbKlF+h0Dke2lZ=BJFa3_1Pd-&6R(7q2>7?e`@` z2+zSi67nJu@aa}f)U3dTH8`j}NbZC|_wRhnwqpDkdIZnq^Mw#AAXh1rk|hC1a$`e# zV-YlQO+>2x#2+~ArqX?p(+hMM6oVzgAuTW+d5|lVcJnG(%P3BD)?#ZSHf%!Zby(CP zbrt$P!C~!psuiF|p!y)PNs5DXbQ*5lJ!*k+RpmZhEtn@Gs0a|;pZ zu~mOaE5qA45~jygEACV%-f(amkFH*arp)TM<_{!(td=Z&Rf=QC&qd6qVjPAfD<3nC zh_J^_V+ezxPwstYijw1t$B&EU&4{COWt zVR8e2Z{dXU?a)tyb#R1Iirg-^pTWKhiHJp<1{^AoqaTColG2eP?A-^hB^piMsr0bp zJVwAA0PXX=m^&{c!SpeNZ7$J;AsYN{&O*xN0eV#q7~bv=)=g+*8hLJE5TG$3=Ore@ zlDBF%NJ@Yi>YMI%3XnaBS#bVnhFOQ!p^C+C=TZ+i`hL#|-?g9Ev8Jly(eTN>(I6qk zs-@Z6yPEsqewkrTVN==Wzva^UFWypuE-|9M5ejGMwBbk&+nr}rsz)C=6Mg;7!p`W{ z*3~{qezB>k(KS802c-H%MJtOML$`!vXDrv4dA&B^?EA>Laq)wJ`AA9-`)?bXpnYtZ z**8#nC9r6}lYe*V7X^NmwdatS!q&aTv2GK}t$pbuI?lXOqlte1o8O&f1YZsfs{L&1 zZoz%=oa51tY^i+zB#M=_j$sXzMB?(7N*Xj(?)<=@b}om6+jNTRdt1f!@iLHfQk z`Im$Q_8Q769>r)~P>^hRDA_iy_5h*$Kk5ptj>x+)kf| zUCti|wcqQxc@1b(iexLF45xdH9&YI{Rtz*|(b+mwZY$GfOU5Nji;Hn)EvInIyw&H6VI7pV{6m$_g05>{kkUx-_69w3hFaYQorBj zDX_=;D|R(DV{iZR3fwcX5XUT6UNQ*_n^{<5*q_TfRA5^dnUj}yFa2X1o3rN@lnb*< z!|vRg*cA5kc1HTa*M$`o>FG@i|HPJ@sFVg?HagxLeoW@3zx4@sz5b)(>a_fdH;*5O z)i?OfjnGg^KQwW7n2!*3?6#5{%QaA_=g+OQ^R1n-BcAPeo8I7%7X;;=;1%j1i zZ)bCl8v&97rVft|P_iC!Ccs>)Nfzav-)4vs=ljCt>^(xPQBFB!f~}2 z33c`L-70SCvR3s|4U=$oHH9TFzjO1%!~RfY+0z-VM(^3 zbZpsM^d4*^09(kX9^w*ZmI5XJmB^)Ql!H<7e6ZY) zc`+QeUEqwtD6lI*MoSDvI5{}WC480&PtuOH*-2AVaV?E<`JCD_tpv-j#9PIaBFEN! ya5NvsJwe(+v{{WEX}38N*-?K$b^NQk+{YgmGj*nOAn>;U0000d_tM6?Z=$-SxY}-ICRK&4y|1 zVwc#f!dHA1xV88oBj*}@g$RWy_hMZ-KIm-D*-?;qSET3(i^4(8Km2Jkn4dfq@Se&3 zTKj@moXkY~bFu#0PPnZ(e1e0$_)enlnWhXC=IqbUUkI-WEMadnm^fjTNZTC0CDs-P zO1_Hd{`ll}k%^K0x`I<0XS2iy+u$b!SFTK5^fGI1n`4Un+K*`hy-NEv><+L^T&{2S kcJ9aK_IoSqvM<+joQ+wPvG4a1ptl)3UHx3vIVCg!05sc)VE_OC literal 0 HcmV?d00001 diff --git a/impeller/third_party/stb/stb/tests/pngsuite/unused/cdsn2c08.png b/impeller/third_party/stb/stb/tests/pngsuite/unused/cdsn2c08.png new file mode 100644 index 0000000000000000000000000000000000000000..076c32cc082066dc870e1282b97c15ec22192407 GIT binary patch literal 232 zcmeAS@N?(olHy`uVBq!ia0vp^93afW1SGw4HSYi^mUKs7M+U~W1%@xC#RK`w#ZI0f zEG#VLKejFgaybh;B8!1EBN+Ru9=Qf&RC~HOhE&XPJr~G($U(&AVsKMqdZO+XCv8UV z2nJ4tMy~^h!kDxdYA>9+p~j^3=S&}?ojYTl4JO=Jai&15=FGw*d!IX*;+1!`qwm#r zn`WQ%3^IuS{KD+pZL<&+b@wcnUV-Cw0nJZSYySkqa&Hnkl_)Oke>c9oU4*NKQEP+# V1@X9R>wwN+@O1TaS?83{1OP3BOWptg literal 0 HcmV?d00001 diff --git a/impeller/third_party/stb/stb/tests/pngsuite/unused/cdun2c08.png b/impeller/third_party/stb/stb/tests/pngsuite/unused/cdun2c08.png new file mode 100644 index 0000000000000000000000000000000000000000..846033be6b4fdec08cc010aa0abb35428b6659d0 GIT binary patch literal 724 zcmV;_0xSKAP)9p$>iIc zM9BG}R?-GyZPG$D`^%XGf`^qAr06IYS+9p{fK&k*ZoATR1wD8|WCM=~607$j`vY7L zV8zwkB3t?J3uaPidN3!<*KK9Z0BA!^-Kg`Xnb*QIyQ{+A355#ayA z0cpp|nK|8N%npZU=nH)H(bhrG@~;!&M3?XV2(`}^e@baTp03|x0oH!)@bHo=j4KQ0 zuY|$dbUuo5Y|+T(H30UBc@<4A(dF}I3{b5$!14M1g8c&!K-bPLY`YQw0000Vz>816FsF9(VN75txh7sS~8e>Vez z3y|esg8)MVP)qc0!95k9*}y}5sLX|@B3D-GMbkBlwnNjonHwIy z9EcJJF%{78L==oIJhA+`7;;cFolQHd7RYGxEQK6edY*pw@2LCip7ZW~-{<*#JkP!5 zYYTEB!lT29C?YRceiiRU!SD1?JmuDki+F_~O=B_)K_Hn((}+hbb3KVH`hCQ#8wM+c zbFwxPsNG;gO(S62+sIEOu%KnpUADK`#I{M2L=HJ6NYwOv+-H@D_X=#9?oI+MS;V>y zl^XVO2yC09!Icb&nREm`f)AWc25B~!P{MT$qI?}Geug!qSF%UJX^=e(A@Tsq&y_e(IV5N zc2ombmUg;9iHZVhFq;*ili#)pv6lr^Wf$(E5sCtE*M(vW0)!C2(rmO95VCxjCB6Yo zimt;CGRgER#Fo_!>WATkX^@enQK3N$>j4Nhm?Bc~Cc@4FTut;EC7;Rd?q$9UQBAH#yE#JE|Q0|h>!DU_)gvGGGRzl1C&lfZjR|WWg|k-fq8#JmDTaBI#^STUG9+W$DsWo438O zx%khhimTsDdcDtYX^8l8`@k~2aals#tw5h$TvR^EzC~aJtyXxM{*Nd)reoq~rm{KwpXnEYf zqB*0lzd&-2CvWIFQhLa@dP&k(Dd*Pij(dA}O2xf7|D4KEZ$_?Z8(4n($A+lx;)H8u z)fq!W+gl8UebBY<#K*_xetq?5tDJr_srucF zRWSz=YaUiV+I4>B_%&@-PtL)FX`y5J^WLm!j19!cj=uCt?f&G4P0QquI_{@U+1wXV|N8?-P52vntW^|UZqc(l!@kKG^x7(XjPW24#8yrdL)E4&VZN1_vo8296 zO!#K>^qsN&VVBEKXIzkd_mj&@tKO3RzQo4)w=OTPI2+x=IZaHg(y>8-4$mKjGb gSyRUo_cYqm_AGEC_tp3R7W~7>%Px>RvflahUvG13wg3PC literal 0 HcmV?d00001 diff --git a/impeller/third_party/stb/stb/tests/pngsuite/unused/cm0n0g04.png b/impeller/third_party/stb/stb/tests/pngsuite/unused/cm0n0g04.png new file mode 100644 index 0000000000000000000000000000000000000000..9fba5db3b82ca7725816efca47adfb44d61292f0 GIT binary patch literal 292 zcmeAS@N?(olHy`uVBq!ia0vp^3Lwk^Bp4a_!xt_BxxKPq}K;y)F)+dv$%Po8B#%=u~U{}NXebp5O&tsV_+x6KC z`n6Ov-e|q7d~>yV!S#P~w_ZMf;qcvgkAe_q@|8lddEI`uLzP=J4Gp*J^)`wY2|W3b o#w`BCU_;}R^bC!qjU|5>nikk@kH_`o5%P%0-tcHxz2242t zZVAjc7P#3?WtxA)Gw_j&*7KvKZ>J=PsTO88@_NksIICmj$1M%HOyBFza{Sr);QoBh zi@mlwB0JLF_+GF!ImvlsMh zsc5{>dRh7AYV(5Y|Kx7HeE!1WyYn6eA`4jdmiWEU!L;8G5Gbb{58QQCkh zC%`R%`Nje_+o??Rk9Y_%RXc^_wWto*p8A(!cU{aKDbTOZt? z&v~)eR!3w<+8cjwuIKCvE|l~g&^Ym)^~t2`a?9Sjaa+F#*wwIpUv)*n^H^rfc767O zek~P^H(D<%-&}28aQ&a$t(VVVIDB{BqaehYe5FuqUbo-vP~{d)L&NQQy^W$p0#81q oF^fMj*wFYSJwsz@W656zxtoVvfBf&v2KtG?)78&qol`;+0Fu^nQ~&?~ literal 0 HcmV?d00001 diff --git a/impeller/third_party/stb/stb/tests/pngsuite/unused/cs3n2c16.png b/impeller/third_party/stb/stb/tests/pngsuite/unused/cs3n2c16.png new file mode 100644 index 0000000000000000000000000000000000000000..bf5fd20a20445fb7c9cf5d38a6ac4cd0fb28de29 GIT binary patch literal 214 zcmeAS@N?(olHy`uVBq!ia0vp^3Lq@N1SHpV7(M_}Ea{HEjtq=#3k+XOiwE+Vi=8|} zczJm*nD-t8a_c-@978JRyq$iK_lN@rGdt(YQy#z8YiT}?Tf!kXZS7nxn;(ocNRJS_ct9{9$oSsu1^4G$9qOxF^ITqWf!_v|1&zuJmL90#Z}{$h z;X$oW3rB}@4oBt#ju#pq{&7g{v2S8~pRDBMFgMZFh1uQVB)dVdT4qdrSvt@q44$rj JF6*2UngG#&N@f56 literal 0 HcmV?d00001 diff --git a/impeller/third_party/stb/stb/tests/pngsuite/unused/cs3n3p08.png b/impeller/third_party/stb/stb/tests/pngsuite/unused/cs3n3p08.png new file mode 100644 index 0000000000000000000000000000000000000000..f4a66237bfb3a113b7874d569367932762cd0ba8 GIT binary patch literal 259 zcmeAS@N?(olHy`uVBq!ia0vp^3LwnE3?yBabR7dyEa{HEjtq=#3k+XOiwE+Vi=8|} zn3vvNsN)kh^hn zQh3OQxBHhXtGq91lkmGBUgcb!!F~1uvkcz_zD4pSt$G)@WaeGq$X&o%(w=ugNaorF hmc0uYt(@e4Fe^D2KP>;UP7P=dgQu&X%Q~loCIF6>U!?#5 literal 0 HcmV?d00001 diff --git a/impeller/third_party/stb/stb/tests/pngsuite/unused/cs5n2c08.png b/impeller/third_party/stb/stb/tests/pngsuite/unused/cs5n2c08.png new file mode 100644 index 0000000000000000000000000000000000000000..40f947c33e477af3d4c13e3a56efcf32e6871be2 GIT binary patch literal 186 zcmeAS@N?(olHy`uVBq!ia0vp^3LwnE1SJ1Ryj={WSkfJR9T^zg78t&m77yez7dv@| zu(GmBsNHi0a+5q=978JRyq&sHu)%@n@Dyu(NA}wKUA0-b10Ep zFS_6slkGtcsjSnkCm!EZ->w_6&B5QGsqfG`#!m_?Kf_rn1^#&$H05(u3N+bpIQ|n& cya`qx_w<}dzJS2a44`ccp00i_>zopr06uR!@c;k- literal 0 HcmV?d00001 diff --git a/impeller/third_party/stb/stb/tests/pngsuite/unused/cs5n3p08.png b/impeller/third_party/stb/stb/tests/pngsuite/unused/cs5n3p08.png new file mode 100644 index 0000000000000000000000000000000000000000..dfd6e6e6ecfcf1d0be69730fb34292c8b6561376 GIT binary patch literal 271 zcmeAS@N?(olHy`uVBq!ia0vp^3LwnE3?yBabR7dyEa{HEjtq=#3k+XOiwE+Vi=8|} zSXo&m)b2S0xd{P2A+G-=8Jzwz9R1J0@V}bj|6Ydw{~3U6Ab;uq|M!3d14Huv|8p1^ z6#xH^0;;k4Zv_-!cm`I*@IOisq;U=dQ0cS(K&=cd|AA%zO#~X&0+Nz+*Khp_w9DJm z#WAE}PU}Hd#sdrt4F>Ptz55$&$0R-3;cHXa2ac?Rsv0cq2_h{`K_9r-92Fv1Rz46e wX^Ir!40F`E!P1!^p3)Rkz@>FiH{%HZu|hk>x7R-I1{%xY>FVdQ&MBb@0QH?@PXGV_ literal 0 HcmV?d00001 diff --git a/impeller/third_party/stb/stb/tests/pngsuite/unused/cs8n2c08.png b/impeller/third_party/stb/stb/tests/pngsuite/unused/cs8n2c08.png new file mode 100644 index 0000000000000000000000000000000000000000..8e01d3294ff71ea83918aa7f99589c7f88da81c4 GIT binary patch literal 149 zcmeAS@N?(olHy`uVBq!ia0vp^3LwnE1SJ1Ryj={WSkfJR9T^zg78t&m77yh6c)B=- zRLpsM)sdIMfX69t)`R5F>$8F{b!Pm_R9NudWu=&O_QeBy0tqv+c5v)_Q~Q&J!@#%m wUE>4C4G-c4A22Uq;csH)Zz^SCk$A@V`i@Ax+rlTSfaWrIy85}Sb4q9e0Oq;wP*-)CT0@c;jQ z2B6XdAldyud4`1l|LYkT9R35fGAR598n(G{b288_Z%-G;kcv622U!^p0CgC=d-v{d zv>lW5Rt15YCienPzJp3LSeh3I<}^i2;MQ`~516SSqSE9Pz!h~+uY+af0^y#f$PHY6 d2X!ls@@qb_WBltft&4Aruj!a10Ts~ zJwICdc1nVnYGHOGugAQPvpQCO+|rQC^u7Kp$Dgea?$77E*lVjJvLo${zc<%&_5~M8 z`VMHEc+dJ|(sj9IZ{4`9Uj*!GSii5jqTqQfvt_$JdqKaJipCqQmz8g>HZQpTPwv*s z=Pw+-JMU2t;!M6$C^oO#?{=tii>9IBcD>$4(ISB-AJUk`pBQXte3G7_v9z(|FN568 VL#{vmcV>h9>gnp|vd$@?2>`oyZOi}w literal 0 HcmV?d00001 diff --git a/impeller/third_party/stb/stb/tests/pngsuite/unused/ct1n0g04.png b/impeller/third_party/stb/stb/tests/pngsuite/unused/ct1n0g04.png new file mode 100644 index 0000000000000000000000000000000000000000..3ba110aa764ff4e5ee642b7c0788229a4924d383 GIT binary patch literal 792 zcmYL_OH30%7{`}afYlm95ECCT0X3y5D?Mmv3<;G|C`zfeL@oq|?R0nSc4yh0QW_6Z zB`Pss;_F}x)Pp8ol!HcNj8URV4;m%W;Nzf@CWQndoG|e@TZkTZXTJH*|M$)JANBiM zE&Mi~<2Z}A&3%-u!2Al?kGu3WUCWk2$<^WF^3J3j^H+OWy@|GT(;%h_;{2)(`~)QFCN!|B{t=iEQyFKl3pA11?%3{l4##YE*?fOXKY0?i7L zFo3{7)ZE}Gl#l^L9YKmhfD%BFK>?K!KL~Y9VoW0n(d%eJWA~VYNr1%#!bFckDgdu4 zDzOmrLLKq_3KS!I;xY@vzGk!o4JwE-xE;hXl>t;V83H!9sv>3WjHfzO4UGX*F|iId z1mIE>(2r?d2x(L{m{B2dvyUheJ;X-m&DXNm7#b4I?m*L#fmAAVlv*Mz2B#TAfP`tB ziCpvgh%5jIe5gBU(6u;n^MRPbh@e_iqm*c>R4p7Y3ndt&JeMUlL>pcHB)>|a4ucs$lG3@ulPNE1@BXy#w+=g z&%HGh>bR@`vHY&O>~* zIQ52(KeeX66=g2YR^D3jY&bCQe>8me$%!E?+f(2?ySG@oVB2L~v@IkS-pGsyV*?Kcce{q!`rdz)M R88w6Bye&TWor8U+{sI?(Ab literal 0 HcmV?d00001 diff --git a/impeller/third_party/stb/stb/tests/pngsuite/unused/cten0g04.png b/impeller/third_party/stb/stb/tests/pngsuite/unused/cten0g04.png new file mode 100644 index 0000000000000000000000000000000000000000..a6a56faf2b3e8655f88c028070ac174823ccefd1 GIT binary patch literal 742 zcmZXRO=uHA6vrnJKT;a(wL;~!h$N6MrYd;wkTqLtKw~wL_E6C2W|AE`J2UJ|4DsMW zRM3MWg?cGs!J~Lk1VIoBT0AKzdTK$zg5c4M2SMLvLo0aLnfLj7^Zt8tx;m8|*gHT7 z$yQE!=kSL3OXEQnzrM}mm2SDGU6Q)-p!j|10{X+eK5OgT3Wg4oBr&IDtJdb0eR}j} zAI6WyMQ+;$DoU#Q_6!$-g>Z>VsQCfq3y|ydLM7949bbj{$r*TvU2e=ME8UHFE3gS> z+ugF@KV|{SRMG~8iG#)BhaZPl2}upcFpn8O;@Wa06WqT67jU-bg2ri3O`u?yjSU{s z7Bk>Sj9S(JtH3g2!3GQ$iMVQGP6-u_+pfks?1)n+Ei_dWQtLoP`l8*yUK%l&7t~)k z&zCi*cOQE4;+q~Gm>jRUR_FwgCHgJ~NWY!s>! z&+&Z~HMnf`UW>0Wn)$uyI%0-N>s&dK^-zSVSeKe=+76NvQMSBte_@n^LY2+djZMVq zZ3}G^^T)Idtu?rFVds1`b3(7;*|Nq{$ee}L~xhLtN m^^F(rmq*W#)$5mU+Z5S&`t$7h&xh|JmsBRJ-s9sJul@x+i~KYI literal 0 HcmV?d00001 diff --git a/impeller/third_party/stb/stb/tests/pngsuite/unused/ctfn0g04.png b/impeller/third_party/stb/stb/tests/pngsuite/unused/ctfn0g04.png new file mode 100644 index 0000000000000000000000000000000000000000..353873ebbd0ca66c57ec7f6488e8f18a1c3517ac GIT binary patch literal 716 zcmZXRL1+^}6ow}dghq?tNtEi?QzFS~JO~9rW1}X-)@p**gXox@CNDF)v+m4n$W41t z#Dj>S9;8}`R}Y>7A|4{OAV@F0t5^`c)r&$!-zHWNVP}W8^WOLG|Ih82=~JVrT`5Ay zXt`88hdYf|5?|#0$K?!e$wpzSKoU2f=D$3+jP-7)&6*k*$+4INa>i&7LXjE2QMF+B zx4p|{Jei2w3f44LIP9x&0j++QTmUJ#M6jjrPF;DKsTYq{ajXKnB>NAH%plenk*N$M+~b-foIWHJh@4$U`PV!RWLc-i8>vZ zYmJf~ieQMENeRYSMs>jJvF4}ickq$@uJ-#~=k4KZ7t!O<*mhOTnKlbIZ?4%wub_=G zpi=KsN5hy&~?ec@1Ie>$uaZM$zGZ32aTXT=*4DPUBk9b(-q_gZk&Rz>KfHfjULdK5 zH;yEd?bZG3$(5cvLVGXUPbMZ8$oxB!=x!zCN4Q?tvGV2>GD-Qwbn*G|EAzhrgC78w literal 0 HcmV?d00001 diff --git a/impeller/third_party/stb/stb/tests/pngsuite/unused/ctgn0g04.png b/impeller/third_party/stb/stb/tests/pngsuite/unused/ctgn0g04.png new file mode 100644 index 0000000000000000000000000000000000000000..453f2b0a49ee544fcc6b297a59dd685a06285005 GIT binary patch literal 1182 zcma)5T}V@57(R*-XciSoc=e?OF6PLfpo_retRxc2LEc1;b=(f_kL{>MM01;(Fe5Xf zEW;>*KsS#z?6{55l|t}-Nl;fsffq#=K@ibZJ>R!Yx(T}2`Tn23_uY6`=gHdY`f5c{ zYCBFj&SGTZSA~x<^=Zw5p~_=FV^=ChXInnsxgZWTXOHgGbf1ca>Qm?mrFnv~RNyhC zE8yuK&~%lJwVF6SA@14-bZ;Su$l0PA*pU0OeD z8j@SB5xKOvgMQ1~14sIRF)k3>f}zV{&EwTeptF>x41jZhOOQz`G{@+It7 zq5r*E79uniV^~U#1TBk;1#FwRoMJp7F=Q|ZpC|cn5p+yWa&RSYG-H^6B1xDi66P=rqhKk9 zjX zV6q@FSka1Dl36}0OPELY;915{wj$|8Qs^d#OUh)SAWLbaQD~L|kr3iUf+qY8(>!LG zl3p2k4w1mJU>90&RA7;i>J~Lfi+y;)8$&u0PT|J(^txqlxhuPaefp3qT>e|!m(?|S z9B{IR3f=xk*}xVT_Dc=084&AKdz=y7C3;Hc2G?;}%>tAlWaKC_M-~mCfL{-W0-gq2 z+oe}uA*xMwr517fT$*1k{{bh(OZhr2(Uu{LkBj1n6h&sDDgTmfvp!7w0C&E_VRtr7 z&X9elvTs}4y7y&e-9mJWwf9loLRU>sYfatao0<1tj;;RQeotYipJg&1R`2evblkc& zt=)|G^=q%|e_c&vDuCong?_VK6m>q(xv*Kj1}5zZ0E5m^qKHu!ynhmu$Vm7IxEZTxmWRJtOe)8nzKoFYPwIw!K*RB7e%yF z@Pvro5`0PUQPvpnINCH_b^NYfLeNRNVQtgB!EokEQbcBf)_^0b>7mDF0+E zS1ONHR48xVEehSw6nHuI1 zi1Qsu5>)3!^d{h7A_y#l*kM_gy_ooqopK`X8Q03EeQC7humk5Lwq3+zxN zH!h@?2)YxXDI?9PD2VK17?dF4c6$^4GEKO z3p|9D^*}JNT7@ZQc;-ZOK=8YA0%9oy zs*K(bI-FqXKGv&E&WP@!2~|o$0lyv$1w6`;Y_sJHNzVYy6H1~P${jFi_z33btHz1 zF3grRu&4GtmL4{+t8(L6@usQu?YouYqOb1{)iP$O`Sxb_^IwmVi`h@t+vciUyZ!(g CG05To literal 0 HcmV?d00001 diff --git a/impeller/third_party/stb/stb/tests/pngsuite/unused/ctjn0g04.png b/impeller/third_party/stb/stb/tests/pngsuite/unused/ctjn0g04.png new file mode 100644 index 0000000000000000000000000000000000000000..a77b8d2fee4c377c20b8e751317d094cc2be3cf4 GIT binary patch literal 941 zcmZWn$xB;77$27w5yewMq=%qTvTX0TTOEITtVlMs?! zQ(g4}zdG@xg~SC8ec3O_n1t!KbW{qL<7NeP=JUpscJ>oaWV0xVW3II$>gcJ z_fgZ}RzA!so>ibTT7l3RJ(vP#dLKsmEMuq_C|DnIPUEw8uhw1t1EWw?)+7$Vz;TLs-nJd z+}PZFnUj8UQe5owNs^*uwPa*u-Yxo*$bn9ItiZd}V*Z_YZ^iJn%Q4lWAD-c literal 0 HcmV?d00001 diff --git a/impeller/third_party/stb/stb/tests/pngsuite/unused/ctzn0g04.png b/impeller/third_party/stb/stb/tests/pngsuite/unused/ctzn0g04.png new file mode 100644 index 0000000000000000000000000000000000000000..b4401c9cfca842537838cec131e416ce9d7b0c26 GIT binary patch literal 753 zcmeAS@N?(olHy`uVBq!ia0vp^3Lwk^Bp4&$Z`J6 zNzb#P8s2AuIsDFfoH=tXkmKALZ-1Q=`d*<1Teg^7-D<+j(4EM?Bq(Z%5YVvQAj4cz zi<65o3raHc^FYSw_HE=lWWeL{z3!;5&(i+~S|SAYC&V}_-LdfE3H>h@z9Dj7b$L0{ z?V!z*(zQ3RbHvuze{%AF`9ST7(P3V_yAM6v__yY~zu>c0ws!SHZY7mF>(Xu3=spqF zYyE1=?(N&QTWCLD_9?x`{4#-_N0n=i%(89VqQ<1I<@)8j^q#<%rc7M<{~y>rT+&tj z?Xk|)VE%yIyVWz+p51BFUYon-WoSjdd+w#BKfbWae+pyylvLFL^o9e-8^QT$CFO}l zso)^d^YYd4^bI_5KG-AVgLf~7rkwsp-f`cTpI5{UVGdHyeY?`*0udct2*HvZ)!BFOeE9Eakt zF(>xoL0({Ru?EO%IdFX7kX@*}flE2$(Fs;VMri}4oB+23<{Jy#Y^O5KKjInqNJi`V z(bBh562w#svm1Fm=6#&ivGU`VhFqra^=CQ$Y<+NlKIg?=TOE-dX>a_!xt_BxxKPq} zK;y)F)+dv$%Po8B#%=u~U{}NXebp5O&tsV_+x6KC`n6Ov-e|q7d~>yV!S#P~w_ZMf z;qcvgkAe_q@|8lddEI`uLzP=J4Gp*J^)`wY2|W3b#w`BCU_;}R^bC!qjU|5>N8=ISR z4CLwk<95#Vg>&ou?4IBK-OKf4I=h+Q-Yu4^`-jJ;=a<*F_w~o;*Jiu>-k0SMV?!`9 z1Vck`We6?}!N3q)7=l6z!b$|W5>ORnBHWgrk_!4FAm#v-gL4rOlDZRAQhFl5C-ouJ zT{shgf4PuS-IlHh@Hud2s6Ob305=7HTh$nC5#jVOMRm%l2)`fuP_1(!!tE!01i`Tg zNcxC^mI$IC_VFh;N<~=u^=Vc`@;_)KBFl?^KZrv^P&WiMLvUaSEP*Ak#4p5s%2-gQ R&b9ym002ovPDHLkV1h+qhR^^2 literal 0 HcmV?d00001 diff --git a/impeller/third_party/stb/stb/tests/pngsuite/unused/f00n2c08.png b/impeller/third_party/stb/stb/tests/pngsuite/unused/f00n2c08.png new file mode 100644 index 0000000000000000000000000000000000000000..d6a1ffff62eb0f1f1dcdb4ba2b1d30473d3adacd GIT binary patch literal 2475 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 literal 0 HcmV?d00001 diff --git a/impeller/third_party/stb/stb/tests/pngsuite/unused/f01n0g08.png b/impeller/third_party/stb/stb/tests/pngsuite/unused/f01n0g08.png new file mode 100644 index 0000000000000000000000000000000000000000..4a1107b4637642335b8ec7b8e1856dc0ba7dab70 GIT binary patch literal 321 zcmV-H0lxl;P)}T4b;N0E z3!a!0=lJ%5$Eg%oz?c7nM_Pz6fDfO-!!(E~0KEA*c%UmW2WDfu_({31E3v>N-{Hyc z!o4_%Y@;b2{kGgyCmt~K_ux)+Vvc)%F>XgE7N{D;Epg%*RfD)GPQ0RO5I3R|@2DEY z_29%Oss?dQ2JwUcSEewBLHsU3nZgtbQQmYZ7(-i7*cJ?I3zBU?bOg~6L`VDql~F8% T6Mw4;00000NkvXXu0mjfmy?8r literal 0 HcmV?d00001 diff --git a/impeller/third_party/stb/stb/tests/pngsuite/unused/f01n2c08.png b/impeller/third_party/stb/stb/tests/pngsuite/unused/f01n2c08.png new file mode 100644 index 0000000000000000000000000000000000000000..26fee958ce7e62a59843205ad34f86a525718595 GIT binary patch literal 1180 zcmV;N1Y`S&P)Qj4`pI zFjkU)u{F?Akfo*B!a}174J9nsV(#3X+5PQ?pp&O~&o{|^n{V!Obi%NV*_mQj=dbpv zm_B(tFHYo4wlii|=dbiix4xT4UZ{eGTtw$J**>tSy6N}n|vqif78L&$byRK9f1$SzrLzUf{!*^r3k$774rXm_7!8f1Q2+ z=C=8j8BA;b>93ttmB!R4Gqr2#)Il4ngL#p|9QHl^o9+4V^!;`CWzciKaJiBz*2s-A z%Q~e~hNeLo)I~S+8N27*(|0^}KLTZc)b*DYt70WpQX@6;tlY_+JY<94KKc3jyKkHx zc~$=bes_90V0+^67$}#lCtg&T@Kv!ADxnb@aaQcaP8xdm`scunQ;gUj^(pYH(^C;! zNn5y!L|KC#`k`FGPq->y@fF|j4L>V%d?yTrfgi+0@98t($EU{=wvxAS)ubd$_PkV3 z!A+>5imOt?H8kTouFDPFK!bmL?KbeO)596d+rNC?VU*+1#52gCK!FJ=R8*lsgBd#N zFko=|Nq}~GkP6E2D88M@l8GXUm_!xTsfy-kVs>rq&*Z3{>ZqRPX`b1u2fF{_*^dDC$unBQ(Ew)&lzW^YzR^hz^JI^CidP9to*K@y zNDItSJ$~`R-vM{)Cyb~i-{XO3q;i~yCk82yJm$&k5=WK56seJ#sF9XvnK_+adK+*z zf5tFI)>7;!5IcJyUb1-f+PNSFERrJ@DN!XcC2FQ7s?jpdPCtJOaPPm$D2$0wv{X9^ zWaZ)t@yIozCq+^qMJ$rnCC-%0l&Qw=-{Go9xBkqiT4p;6#CFmU*E%r}BWWdJiJY-a zF^Vx6Z~f)!f}4M1bjGY@*ij%CA&7a#dtxL;QaWQsUcZ|{DNGZ!QmbmEH8z2;34}4U zEce==fUtrOcu$DLNKC}F#?F@|D_EgSluD^gm7PG?1j0Nt7TXI1ibI5i_qeUYM`9wZ zHAc+m{muoO$dyvD%1$6`0-+Ag%UwbcF5n#U_<-~Hh>wJXPsDV-gPixfkS9{*C=fP* zFb&F3UG{zuavL5Oa2^-&5ueTk!)M~U--S34D@TE_2?QJDp|V`eAXY9N+By{R372qb zf6pNl!i29J1;Qo}(jX4E_qp%Fz4^!GobzySIy|o?)B5V>_Wt4V>G|dL?fv8P>$@@5`4A$18fipC zj4{?)>#TR)dmloGF-Am6L9i(ZNIDP1wql`V1IXeb3GF@N^}7L$OLT?eW035 z>s-(z0SrY%YCz?nO#+xF1yoYnBznj5E`aJnr^KQQpxV+di7ue_plgdhP^rb}TH<8U z-_)#002ovPDHLkV1l$} BmFfTh literal 0 HcmV?d00001 diff --git a/impeller/third_party/stb/stb/tests/pngsuite/unused/f02n2c08.png b/impeller/third_party/stb/stb/tests/pngsuite/unused/f02n2c08.png new file mode 100644 index 0000000000000000000000000000000000000000..e590f1234816946731c677d0b353e2db5a08d71b GIT binary patch literal 1729 zcmV;y20r-x*}n2M`}#}vjc?j3 z-?s05&wlVj`|(fh7r(S$|JMHaC;Q7^?O*?~d&{=IVSD>_5?Nc!ER3!g3n}x;oHDCS zE7M9tnNlW|No7L0po}Y{%BV7;)RZA*NEuWHlu$uIF{MyCN=M0+Oi>NpMWV~bQp%#T zpv)_?%8W9jOe<4Ly-ylf#ww|%3|Gvc5-LF-)9o>#n>V*ev}9aNS<tNOUgyIH` zG-q7aJ|<;Bs%p}n(NLyR>cV6>o0KtOR2fOBDK%kO8B8li!+3&t(p()kWp0LO#<-$= zOvf4WkAB#DDgP)1#^?QNplUNhH+K4K#)MH}M8lng?S#!1>#?oI+;=QHd&_Y* zvQ2ag-A04p0Z)8E@cI#Yj2@#AdWt&Kp)E8)6V#)Eh6mnu6Sfn!T5QC&9<$>3pR;Ah zn~`mxSA%AGv*B#jvEq6SSw}a}hv?(JmFIitK6;2Y(MP>%q^zct!qYa9w`0$dr1uwhW0Zhu#4`YC3A2lof%qhmB?1mex?MvA0uiC*F<|9wjtewjbMGY&*`j zoNYQ=i;4%x3bKm)ELbUlhe-cy&Q~LkkWkfBOvHTK5^pVU&KE8zqNF%!6-RN=jJt=f zyYIStuDcy|)=2IKN9FDBuxB>$Tlj7K4t|&RE`A?>fIq}HX*X$yq{F5)WCR(dJ%%60 zUobyOQYUH9nkJsLcn-IKTf$xH1ii7(+IyZFAaXuHLUf39$aL7GW-`)eOjt5$$&|Gk z7SC8bXKudx>~4AcfL#|Bg#}?=m=$J(8L0w9n3UeL9uLU|&_U86bQr0b)M$_5M@xoe zf}~Ds%Hl?G{lDey;l^hyT6)`NFITQS5g;L1KsJC5kPe`O$dJh}?HYch$0Xw<6ZZU{ zd$-?t0XK_VuxLS_2oUXUsK?Ay2?S&T8dk=j$q?;fe2=lO{-L*jZof!e$2D-%xH*gF z^n8Gvsp5t2h@l@X8zXow7$3|f20{E)r!yT=R8p8tOF$?p``zg4{drQ+FJ#q^ho zXy)BBQMAcvVzRW z3^h_v5Ric9p5w+1ZrERCgI8Y-UVAOLeLHyLjo{85+yt&}QC+$AHbz)d7DMt5 zd6zUtb0njk;WJuFoZ^&7I1!H8G(892_Pp&Rww>5!%hqFCjqSd(WoK`>;%;>AP46PR z$UY&RE}af}mn)!IyrepUKyIwR{8>j(~{{02>UU6F*}Ye$EIT~(g*w> XLp~IzS7kjt00000NkvXXu0mjfY;#J> literal 0 HcmV?d00001 diff --git a/impeller/third_party/stb/stb/tests/pngsuite/unused/f03n0g08.png b/impeller/third_party/stb/stb/tests/pngsuite/unused/f03n0g08.png new file mode 100644 index 0000000000000000000000000000000000000000..ed01e2923c92fd8b8786524c6d6841365a44b9de GIT binary patch literal 389 zcmV;00eb$4P)4Zd45D^U_MS@TiGz2aVLCXUNp&Fi;&<&!6t`aRYNwm-e(L$HR7llUs_2v2Lp?04i2#w>5 zCjFHgoS}>OqWhKs;-;4%0XoyCYd;H{ChLTAkL z`Nnmc1cXl84@#vy84@~$6^JKJlZ=Ubk`d9uF>y~YB0kwMamU0Lt#&uLFSbqGG0ztr z+6kZrX%qLj_#)n+{rCF=MU%cN0ReDq=R?P^VtFOGwjP)u6|4v*CxG_LFwq4OLVH6N jD*7N)_#jLSK#2GUV`p4Ky#5P000000NkvXXu0mjfx;dtp literal 0 HcmV?d00001 diff --git a/impeller/third_party/stb/stb/tests/pngsuite/unused/f03n2c08.png b/impeller/third_party/stb/stb/tests/pngsuite/unused/f03n2c08.png new file mode 100644 index 0000000000000000000000000000000000000000..758115059d78260f7485521b83a864d4a930be84 GIT binary patch literal 1291 zcmV+m1@!ufP)=&S9{TIAi*MDSax9$V&z4Gt?ul6?wozPIOm-?GqbzHVVL0@-rxKGKhOL8k5O_q zSFpv3t*o}I*V_9Zvg_8{M;@~qp0pb`*{3(#=eF8yFWQ%0wy%ua*LT{9H|_38yXP&t z_Z_?MUHjhq_P_`B;30eDsQu(qd+fM9al)Sb+AZ}0v>o^z(58vARqwd=u@6n@Pmke1XD_3x{N{!lS^S@Mz)_} zdJOa7^M*h)z+aJKlpUap>FUU*babUGt;6i!o}sK%&_Ml_0}Q1o&Mu`O$?Cq66Q@JT z3RxIYM!*w1@Du|XA%l7Q)eI$}3>DD;e|4H6l$4TCgo02|@B=KrQ z@Wkm5Gr226kv_(nB^X7=$b&B_9m1$G>KKWRgkdJ$$Rs9|g_2{8Ez5|#T1H2TVa9h$ z(j;+7%SwO->aXp@3<>@U&;J{qzZIUl8J_zyJbOJn`+IoiYIx>K`1Q0sy`|S$qy4Ql z0v=}L_DlonP=h(73bQba+*O)>Gz;H^EtdX#W}j_9D|OAoJhWjRA~5T>Ndu`v9p<2x zV$3LYKl;cP4%*<5UG=dYJ8U03Vjuo4ycMs0P50HI0S#zE(=pl(GY{sz&C+xa)e;>z z1~aNPzjM$1t-?9LR-*64P#mG-1*D8@fZ(*yqnYop#)_J&`RW&@^-8p-T}(?iQxc|RDc*( z<`4s3eE1|ejyabvT}B)cWe8L&9Y4_UCg9tx=>U_B7``SQ&NPmDBN;&XQlhP3n_*jascABYwWa1geh;>x-+i4(U_Y8$pQBrmp@_v#) zgF2F8aQOzn)E_eOLYJ|y`CQzx3-$nmLD=V&A(sP5g05dja_!i}_ zgIDirr|I{mZhX~}7YtBA6@@6ph=h6QI6&Kt{O(YUr$lPFM|Gd{aIa#L7cd2c1x1(E z(Q=Gs10<^TF-)&&>U0cv)zz`6SBo;LJ%(}H?`NdF>L`))0fxOAVsvH`hUsg)nABw~ zucMi=tL~(v8Fzi|4FCoh!(J`gSItz-2XxH%{{y9tm%W$Hmi_<$002ovPDHLkV1gMt BbC>`C literal 0 HcmV?d00001 diff --git a/impeller/third_party/stb/stb/tests/pngsuite/unused/f04n0g08.png b/impeller/third_party/stb/stb/tests/pngsuite/unused/f04n0g08.png new file mode 100644 index 0000000000000000000000000000000000000000..663fdae3e7d906df7379d92621a7a951313705cc GIT binary patch literal 269 zcmV+o0rLKdP){e2x4DAd1)!+NJ)f5B{T9psOdMM) z%F+|8FY)0+3~^>3vs1_&)hG|SqQp{zQhWvt^p*^yfsjOBNTM$!(HFu%2m|o|7A8yw TA(>!-00000NkvXXu0mjf-K%M) literal 0 HcmV?d00001 diff --git a/impeller/third_party/stb/stb/tests/pngsuite/unused/f04n2c08.png b/impeller/third_party/stb/stb/tests/pngsuite/unused/f04n2c08.png new file mode 100644 index 0000000000000000000000000000000000000000..3c8b5116e72a63a3de8e0c1892c147071d0269e7 GIT binary patch literal 985 zcmV;~119{5P)hvyJ!8utT1VEc>UN*1Tj!qJPN}IY>N=sRACLdrt)_N=Jy&nmRP0foi1T3q z1vs!^NS8tnTiC(|3Cm|k`D(`N4+4W;;{3xH5HR3=UuuoS4K!$rOa`(R z?R*&X`GvfR1n&u#ACUWVNrjF@+CUA>kh2DD{mP;k>FRtL8*-Oa0-8az@Zi?_D?6u0xgjeiedR*>(yJr`MN%ucekr3 zWi?2)HMNKXi#p_|&$f}^7s7eRdN|{jt0Vw}4uOnAlX>OyYx{;(h32S_hezhCE(Fab znDm|v`WGp9zt{od@3wLWe5y}twl%v9eNEpS4jt=K5jm_s#Yobzz%^iBf5O6mQ4IVB z1gSS#SEq}Q@Y^iry7D|7u$1$9vV7%>V~{k^6C{`$4zP=FjwZm`$rGS zvDv1V?}l%U4QyZyYY4D{53qy=jUGER5T*mg4DDFWy$X8(2FG?szK8L%=^pMOKo~6M z$j~Pc+wCNgX-nw*aZjf2VMtF87IxrAsgQ{`|D+q<6)@}&$53fBJv+1hHpj`E}Y00000NkvXX Hu0mjft5eU! literal 0 HcmV?d00001 diff --git a/impeller/third_party/stb/stb/tests/pngsuite/unused/f99n0g04.png b/impeller/third_party/stb/stb/tests/pngsuite/unused/f99n0g04.png new file mode 100644 index 0000000000000000000000000000000000000000..0b521c1d56ac6ba14d82547eea5c87dae391cfba GIT binary patch literal 426 zcmV;b0agBqP)R*H$WiHScT#vd?>UqJiKVd^cv0p+pyQ zGH|gKM+__s5&6}tHkZ;`DS5Y9+rZhQ zwP@N_D-FoTR{3vHO$x#=5Cz~@tXf4-D8*gDQ#65;*9uNSx9chJUE%)%R{#^#^^XN{toLo}DWI?N<>C;~ z04ksXP5}*|#RS)@U%=Ob*$h}N)l>-ed1V5+W2XZEdA?ey6A(g5buxi@h4uQqlhPtM zpG^V)m`*nvwSYxHS5VI&O?NvOiV039lVCCd(iGV5g^==n1tvHiO#xM<`sZb@9~=`L r4yJ(5#RO&P7r+GDZJ=PW2opR2brp<;D@5J500000NkvXXu0mjfFu#U? literal 0 HcmV?d00001 diff --git a/impeller/third_party/stb/stb/tests/pngsuite/unused/g03n2c08.png b/impeller/third_party/stb/stb/tests/pngsuite/unused/g03n2c08.png new file mode 100644 index 0000000000000000000000000000000000000000..a9354dbee6f36cf88f7b6277d3fd43b6cabfd84a GIT binary patch literal 370 zcmV-&0ge8NP)yO^U)m5Jsyp{zL>36n8GX#CQmg>mm9SE(8}cf*52*XGY_~ zh{+_6px=OtfYFFnf&gIz^!p-! z4E~_M*AoG%LANUc)ER`K2v7|=9TA|;pxqV$s(}np4O%S`KnAq|RD)(y1iYU?5QqRW z_=SEnxIkSs2+80F>Z$=A5dp}6`d>Z+8Ssq3bn47(xy-IHGcz-PeHq)$ literal 0 HcmV?d00001 diff --git a/impeller/third_party/stb/stb/tests/pngsuite/unused/g03n3p04.png b/impeller/third_party/stb/stb/tests/pngsuite/unused/g03n3p04.png new file mode 100644 index 0000000000000000000000000000000000000000..60396c95af19fc5f14aff9f83d391331a32b3dab GIT binary patch literal 214 zcmeAS@N?(olHy`uVBq!ia0vp^3LwnF3?v&v(vJfvmUKs7M+SzD9ea;@ZUFM-0(?ST zfi%Pa|Nj~OGn_cFb}hr*yC5EjfA|0Ywf_xwDRcl8Bzw9zhE&W+)?jB+RWM*uY zOI4Q~40T|+ae#?Kl#?gtjDxO+)}>1i-Z_XV1hX;(T`t)BOm^#Pkb^v3{an^LB{Ts5 DR8dIo literal 0 HcmV?d00001 diff --git a/impeller/third_party/stb/stb/tests/pngsuite/unused/g04n0g16.png b/impeller/third_party/stb/stb/tests/pngsuite/unused/g04n0g16.png new file mode 100644 index 0000000000000000000000000000000000000000..32395b76c9c0e1cb962743e8a9c8c41f319a26bd GIT binary patch literal 363 zcmV-x0hIoUP)k zMP!AUF&SCLg{ApYo}a%I2W<;z1p%J5#`A86=diAEu-(Goq6aWQ91jDiarl1YV72lz zSS}q6C}6R0GJYI*6ZWZ>;Ot> zZFbS1yTa-8J$*mz%z(DscaWy%bDwwTTMVko4oH&Ag@0}_C`%l49mFv}k^p3xQd%q3 z*T@Wt!e(&2nptb1gFLri;n!jYw;K*dqhSCu$TClZ{oV~g4L&I}i72HGV}Jkv002ov JPDHLkV1oB;ijV*R literal 0 HcmV?d00001 diff --git a/impeller/third_party/stb/stb/tests/pngsuite/unused/g04n2c08.png b/impeller/third_party/stb/stb/tests/pngsuite/unused/g04n2c08.png new file mode 100644 index 0000000000000000000000000000000000000000..a652b0ce87ed2824e2320154aeeb92915dab7be4 GIT binary patch literal 377 zcmV-<0fzpGP)=$qmFdR}P2oOfV zU|<4VgMQxxxCU`-0$hV$&jh#zHh>I1V1Ku30$c+d;Lf1aF#%*iSJZBs0Cxue-@pdA z2CbF}AcGU^M}sfOy9O~C5WqFSD>^`)3~T@e862Ae{Ka543z8O#a*afilwMz5gDA=u z(`i^Pv*IGJo|YG6Kr_1mR;%nWd2jp{WI%v#Gnh=mdi~I3QXrAR9su`drSUjyHa}Ju z8`J_uqi|tJ+HSMFZgOR)ZU*mlfZeWqG%Y-fK?ZMifZqlgyfy_OgO{cNWI%t*O&Od4 Xkxf^INQ0u+00000NkvXXu0mjffu*e-u`Y1b?z zzWXd(t64Wrb6MRl-MPm3%bnSi&5lTH;%2!xfw$vO?LWcx>5tgDx7P8NE*G2%a+Rm6 KpUXO@geCxuo>m_K literal 0 HcmV?d00001 diff --git a/impeller/third_party/stb/stb/tests/pngsuite/unused/g05n0g16.png b/impeller/third_party/stb/stb/tests/pngsuite/unused/g05n0g16.png new file mode 100644 index 0000000000000000000000000000000000000000..70b37f01e2b9e13a30a6e9d3c51a114a2f0953ca GIT binary patch literal 339 zcmV-Z0j&OsP)?t@NnF8DcSAbJ+I4}jD4==ze*zcJFZU7PBBkXpb0$0HQ1>69qAc{N%IA9o<0{k+y ztwZpB+XXm)2wVYpI0a#74`2wgEYEeVl>WRDLD#JdZnwEs3P4|pU@M>~u2)?HSOmDs z%O>jDD!>8DCayJ<2$}{h$mxDQ3z4SJr&0jRBM<=&(7Tr9be|rZe{e)l*V{)}8$|GW l@dAjTs?dUD3LXzX!4K_Ugk}Okv)}*#002ovPDHLkV1mh&gfRdB literal 0 HcmV?d00001 diff --git a/impeller/third_party/stb/stb/tests/pngsuite/unused/g05n2c08.png b/impeller/third_party/stb/stb/tests/pngsuite/unused/g05n2c08.png new file mode 100644 index 0000000000000000000000000000000000000000..932c1365364fead1166e4772f29dcb7657f73f39 GIT binary patch literal 350 zcmV-k0iphhP)yIS#@w5JksQ*g`}>M?;BdsB#iGXl_D}xBv~JKVfNIcci2&6=2B-$jrU)Q|6XY8W5%7NoGC(z`*F^vs1lyuu23P2l z!4dKa0rt=jWC9bvghS52BVSt=-A}B^lR{o z!EBZfhpyn1c^lB7D8J-F$KT*fK)J|Z=K<*ToSF6eZn4N8k6hj>3?mmdm%hv(n&R wWU%o7coi9}As&7nsP&a50$ri)KG u9IC*rureVboiWQXC@_w%oP86+Of7~h92M4HZx@7u9OCKf=d#Wzp$PyhYDC`v literal 0 HcmV?d00001 diff --git a/impeller/third_party/stb/stb/tests/pngsuite/unused/g07n0g16.png b/impeller/third_party/stb/stb/tests/pngsuite/unused/g07n0g16.png new file mode 100644 index 0000000000000000000000000000000000000000..d6a47c2d5746fc93d252900c0b2286878b14a71b GIT binary patch literal 321 zcmV-H0lxl;P)ic&7(nC-rHzraP|afnxc^RYsd`d`5u7poSz$l82kp{Tq3Z6JE6!C z<~cx~GsZc95JJcub)b0oLy=wE_B`cklP8(MI6l9^UC0cE!3;nQ`rgu@>+B3Z0>z=! T&@6K-00000NkvXXu0mjfZ=Zkq literal 0 HcmV?d00001 diff --git a/impeller/third_party/stb/stb/tests/pngsuite/unused/g07n2c08.png b/impeller/third_party/stb/stb/tests/pngsuite/unused/g07n2c08.png new file mode 100644 index 0000000000000000000000000000000000000000..597346460f1999fb225a46c941751a063ce744f4 GIT binary patch literal 340 zcmV-a0jvIrP)Ec1Z7JAAD^)rl zGc;g3%p!~-&*xWAO~jWXaK$Npe&EOP4FJlLpC7?~0$d0v3LyXpLqMLRlR!xgvP=k& z8i)Z>gESQaqy}ODGPs5U>}G%fNg@Ow1N4Z*fS)rkMhK9;MgMOA0e3K$8Wd6hG8n;r zXmA1Z-7-K1S_=WdAR39-HfGgLqn?XbwVVOhUIuloww+K0g9o7Nrm9jK0F(g&e1^=# z-V(Wa{fz-XY1a&35Di3ZYyEC(6%kphw4Tp(^SMg{u4DAwEbq7P7ygd;HOjyT;8iGt mb0`2ZIE4Zr1N1W+%HRPOfLawl;h_ou0000jY(XgVYP$Ug2i0{3Ntua3PevZO<6g^Ah?s$m6bVyVK1BpBEle`W(ImUKs7M+U~W1%@xC#RK_!JzX3_ zD(1YMYRK23AmC~*&8=hG7IXO0C1yqTPaJWFFL_6JBpnoMIXcsY>1ImurhiOn%Vt#m z5fC`n=)o~7+xK(flFC$FU60NsKN=pf&Dn9QJZi=3cwrm4I|rNBb9(vyap&(_JYhlR zCEhOE8{3?ktnW4_&R8ZOEj;gf4*xH^W5+UNujMpVzT34~{@9PX!h3Wk|Ka$Mox;Fy ze2R(Tv5(acSr+AHDoQLDVbJLg`uAs{=eP4));+&@o#p&$o;{4OIA-jB`e#}P$b+7) KelF{r5}E)9`D>*B literal 0 HcmV?d00001 diff --git a/impeller/third_party/stb/stb/tests/pngsuite/unused/g10n2c08.png b/impeller/third_party/stb/stb/tests/pngsuite/unused/g10n2c08.png new file mode 100644 index 0000000000000000000000000000000000000000..b3039970c10e786659f9752913c41b8a26b1179a GIT binary patch literal 285 zcmV+&0pk9NP)@();|)eY}Flu=NxRyTwAba19zW zNcaI2oGKYjCc{&aErrDdt_X+yXV}i(ARy1f{s-JoAU8t`q60wT=m4lx4WLj1#Goi5 z0}g9&GjO^Y?CP)v$sk{c!58?k4IU`q1^i?TP{4Hwg$wQL5H j&i)?k>vRoL{Z;$`)U8ca-rtX&00000NkvXXu0mjffk|^a literal 0 HcmV?d00001 diff --git a/impeller/third_party/stb/stb/tests/pngsuite/unused/g10n3p04.png b/impeller/third_party/stb/stb/tests/pngsuite/unused/g10n3p04.png new file mode 100644 index 0000000000000000000000000000000000000000..1b6a6be2ca571956ce45bae5470c03cd2266fd89 GIT binary patch literal 214 zcmeAS@N?(olHy`uVBq!ia0vp^3LwnF3?v&v(vJfvmUKs7M+U~W1%@xC#RK_r0X`wF zKzij$28RFt|F8NV62kBw2K)ELwKW(gS)7nLjn&=f#?aQDJu;O{JayK!wx3QIX10fSyM0LA|{qo zr@2pEmRO+mfXPAELrdbu&4xCml^Xi(yO|>!d_)=C9<`pFX(h-9w2r~k)z4*}Q$iB} Dmq$l3 literal 0 HcmV?d00001 diff --git a/impeller/third_party/stb/stb/tests/pngsuite/unused/g25n0g16.png b/impeller/third_party/stb/stb/tests/pngsuite/unused/g25n0g16.png new file mode 100644 index 0000000000000000000000000000000000000000..a9f6787c7ab50968e37514529f53a6249762ca17 GIT binary patch literal 383 zcmV-_0f7FAP)$PJx&n)kH3|svhZZ(r0ivJ>sO?q;pvG#q^Pdnj*zaWkW*`MH zgTvuZ0}6;^p~3Np8;yq0Knh?6r_-MX=QC+2p~2;X8_lNB;Cht-m;nW}T0(={4L90t zp~3y`2b5*NfC2^sT+`Rg3?2{M=yd!5X7GIa0W_Ea1$4W905hON>h)}1tpH{-dv8qr zYh?yig&X}o0Kj6ArtTUrpa891&a%3ygBrvY39Yr(8X(K&^KXDL&H#scy)e;z2g~Jm zWsI8yn*n_$yCu)3Q-D0T+mfUnB%1+k42L$a*EYNPyBsudfL{?b7>)b@WOU@^z{4wh)7YG#lngB zpO4to4PwXAUtl<^EZyItpI$A4c0ho^z$dGfn@-I=c}&df(}j0HTdM#fvR(&W;o}K& zG9bWk2p1G&K>eeU5^p%h%I3sGP!q+00000NkvXXu0mjf+0UW; literal 0 HcmV?d00001 diff --git a/impeller/third_party/stb/stb/tests/pngsuite/unused/g25n3p04.png b/impeller/third_party/stb/stb/tests/pngsuite/unused/g25n3p04.png new file mode 100644 index 0000000000000000000000000000000000000000..4f943c6175f31c3609bf4458fff9d3c591f112f7 GIT binary patch literal 215 zcmeAS@N?(olHy`uVBq!ia0vp^3LwnF3?v&v(vJfvmUKs7M+W8#6Mlqr_yhTJ0X`wF zK$<~Um*M|^0Re{pF+l47e;^$L6aWH-pup=-feKPQT^vIy=IlMUk+;Es$0ab@YvMKS z4NTlSPH?R`z_n#cTw`u=ph~f+$IqufMW?aPJ#nF}g2zjg8cRnT4C8a-ev#F_0RlCh$*z86iP| zaVyJO7(suuAQvVX1O;6NThywBgyle%p;kiF?Y!^7%*UPg-nqYX?l%`38+JvCR~8dd zB)L1WAJ5hPQxeAagYN1HJPOnCJ#h+k^;hS6n~B2RjrC1+_??J}9FY(yk@yB>bT*DpgTKuCNOVY>KHEazxSy09-hZpy3u#A`f8V zHsSyi48tL)AqHsN1Qs~Z7}}6FKO%n`+o-V&6GM>jhqkEH%nvXazo!T$>gO2@$wLLX z`|$=*dd46fKkdM#M|Gt4gY=kt2bIyaHXr#UDa%9SKVaIhyKeYwX|*h#PykDCr-AN)~w;?+^4yN zr-s8{bD3D{UA{K=ZMOCByHINGNoR9@dNzC}GcY)|Ju@)5BDQntP)ot4{Pc$K&wPET zb0n|#)BTaYxn$Ykj&#Mk)P>xtSgI$wXyNturRDT2Jr$Wxq-QF}D@JdO#pYwF-{qy3 za({X@R~7V)9!N#APsdv3V~y=a8^^ai{@Af>>)D}>Xm+S$ZFc#a+o9u`9GCs*$lY49 f5Z_z){0_hPQv2`7;*nR={=b*3Ye+n*O`ZD>%pJzM literal 0 HcmV?d00001 diff --git a/impeller/third_party/stb/stb/tests/pngsuite/unused/pp0n6a08.png b/impeller/third_party/stb/stb/tests/pngsuite/unused/pp0n6a08.png new file mode 100644 index 0000000000000000000000000000000000000000..4ed7a30e4d16e0527c2695bf0493ec1ba156d8c4 GIT binary patch literal 818 zcmW-fy=zoa5XDDVMV1W~5)ehSNEfm%xBU z2&4*#Vrk)5qd16#e?&m+j8-!K=3aRF?(EE*Ip@rKwzzO@s(ZRCB2&ZbgJs_5>p6am z-v^V)Z(c|4^l$d%(39=C{jFOfho3IqSefU45fw2J7YUK9NLAECT{J|qlBQxN=3*h1 zRqHBl;w~QIS*@WGCgBnxkswK`q)EDDND_bzG*~oKoi(S9sk&;YW{o8>Q**UY%VI&& zP2JT)J&P2zVH&Ox8VN^QHBHksLzAhn1`N`$gcdmlgAFuTG-nB_nB!()mZgueMDFHc zo+TF+B*QJjB2f=1YAxL|ERlyF(4-qX&<_a|!WuA0!;(^XFc@s0!OC*3(!`u_&oUBy zj3tWjNPI;uEJ%8WCqCjd2EdDQj7BY1;s-S8#tt|k7=^F~4AOK$3myyx8|bVRRW)g1 zPO>qKL?2^`BC{6Q)?X~zMr)sKNkyvpUh8dE7RTmJZMz9B%1uKpztJiMZ4%IY4r~eY z(8Q?Quih^4C60%K{>u7`U*G4t?=SSOE=^ya`TK7B-cIk^kKIxK>$!`kdXxRvvtNF0 z{=1(CZ$@{A&yUj9`FybVvBPCeK0F$)bCK1Jv!jQXxa@3n&V2eaJ~@4&3S>CHFnD=o G?eTv$Q+DJ4 literal 0 HcmV?d00001 diff --git a/impeller/third_party/stb/stb/tests/pngsuite/unused/ps1n0g08.png b/impeller/third_party/stb/stb/tests/pngsuite/unused/ps1n0g08.png new file mode 100644 index 0000000000000000000000000000000000000000..99625fa4ba1c6964446797075c47ee05dcae22f5 GIT binary patch literal 1456 zcmXxkK}(cj5Ww*#aAOfA1P>uWA_;XUl%aEMY}7)@A`%EeORi%bMFPEcC<37_1p^(# z58$OQAu`alqXdS>Zh3N=`9rY#yvw`)eV+Lp_Wd|qc|1KiH`!X79z5x<=5@Z_-CWx9 znZ?_Anb_(*>$NNI50=lrzHF^Cw=;aYw)1B9?#A0!o2|;IT3D6USe-R$6&j%t8le#y zp-~#8Q5vOD8l^EBqcIwzF&d+B8mDm@r*Rsm37Vh@nxF}qs77!JE@{yjc z!KJtqm*P@fc~mxr%WxSk!)3T~kZN!lF2iNG3|Ed>4KBlFxD1!!N<=ld442_DT!t$@ zR)foM87{+RxH7B;m*Fy8hRbl}HrY5X$K|*jm*dJqs=?*B9GByATscBDxEz<`a$Jrp z2doB{<8oY%%W>uS)!=emj>~a5u3W0Y<+vP|<8oY?Wb^-3|G~f&xB^#hQ1@{KuD}(z z0#|Nd4X(fyxB^$;%EPO{6}SRd;0jzhS~a)=SKtaFjV?4UmX1mAVr#hYeYu!5+e{Vh*?JeDyT{@Zhyfr@Ddogvjv;XPO$z}Yw k`0#ss`FehH`ek!G(S~Q2+xLI{T(5`g;L%F|=;7$yKLBk?PXGV_ literal 0 HcmV?d00001 diff --git a/impeller/third_party/stb/stb/tests/pngsuite/unused/ps1n2c16.png b/impeller/third_party/stb/stb/tests/pngsuite/unused/ps1n2c16.png new file mode 100644 index 0000000000000000000000000000000000000000..0c7a6b380e9a2e60c887a260d43a41553260fc74 GIT binary patch literal 1620 zcmXxlUue~37{Kx0Hvcggjqqa5wnZ0q2=d=kL4nm-M{-~`!ka?f!Rk2%2GSHdS8n`@?ayH~ad{hwEnjwy0Kqsa!OTO|EN93>3xU&cXi8TL<6T z_xS63cfMIH^CJ6I2&=LtR%6Yv3XRYRjnD{<&?t@4D2>u6jnX8VM3ZO|O`=IOMq@Na zV>Cu%G?^yTWSUHqX>v4zOK?dMjTF&H5sial#U;1|m*5gy{x}+3f=h4-F2Us=iUyb9 z5?q2yaQT+e;1XPdOK=G;KQJ0xf=h4-F2UvFc%!%!m*P@fipz(M2AASeT#8F^IVBog zic4`RF2&^!qQRxO6qn*sT%JdROK~YK#ih7>vuJQBF2$v|6qg_6O~RFMC0q$t!sUZR zgDc@mxDu{}%g2lcSHhKWC0q%YBcj2Ta3x#`SHk6QqrsJMC0q$t!sTH!xDu{}E8$AG zd>d~Jm*Fy8hRbmIA<^J6T!zbV87?0o8eE3Ua2YPc%WxSkPrUj672m;(E91(ze1mw7E91(zGOmovw~q!_#+7kpTp5=i9u2OH zE91(zGAr)-96iXX{yejIXKXmyW6~f=;W#C=Y~#RUr~DD=C<7}Pd4V( zH~-n#+;niNVPDrK+m6lmtvvO7t!H)hgZe|I>Uj6!dzWuN&{h0UyVC!@}`gd2y$Mt{4pIX~;?82+n?%}i3yBA9R2bMfB^Yr&uM<0CVz0;%J!>30d8(#j? tiKe%Q>ay~W(faXa_sTCXI{$_Ic%%2f9ZRP!&BY%;-^MMK?|Q3;?gI^1pmYEL literal 0 HcmV?d00001 diff --git a/impeller/third_party/stb/stb/tests/pngsuite/unused/ps2n0g08.png b/impeller/third_party/stb/stb/tests/pngsuite/unused/ps2n0g08.png new file mode 100644 index 0000000000000000000000000000000000000000..90b2979685772423e875e3504fcfee1d406d6a37 GIT binary patch literal 2320 zcmYk4K}%Fo7=}-vF^E!vi-@2W1+@s2p><45)If=Z1VNCbj)cC8aMZEGd0S>5Cs( zJyVm_Gd0qcmznjVR@Ql^~~0? zdZrdvOdOUJmK0Z{u%x&m$?BPHW%W!=R?o4*q*$@9zh`Rp)iX8wW@^~XkYryy^VYt4 zP9=Ty%sW{>) zq|8hTOUle7Sv}KLR?pOA^_=N)gPxgFR?pOA^_+de>Y2@C^-N7x&oK~I&$y7)Gc{Q~ z$Jzf|XMb5e<3v`^vD~r;U-oueZ@odkx4iv!b#3Khnd^M|I6XPJf2wok=+F7zj<`#D literal 0 HcmV?d00001 diff --git a/impeller/third_party/stb/stb/tests/pngsuite/unused/ps2n2c16.png b/impeller/third_party/stb/stb/tests/pngsuite/unused/ps2n2c16.png new file mode 100644 index 0000000000000000000000000000000000000000..a4a181e4ecfc378abcba1a2af79c0aa96eae57e1 GIT binary patch literal 2484 zcmYk4Z)ntY9LInAqm9Avh#oBGwjSvrRL)-t1*>k|sDbH-Jt?dmsdoql;x5t)bSqee zNKkNb>TLSJ5KOlMePH5(prGSe59(15hG-tNTI`k%!mfMs?)AmNe&6?fe)sx*KA#u& z(eTa}JKEQ`M^V%<^iukj#`&236U~k1#Si-y8b@m`^>Qj|nw;*d7DuC~tuQvcV`Oaq z;m7y9{pP+X(fEe`>OP`4q-;o~kjf#||4(sXlMo>hazvubuF(laZWRr1VAW|cg%5-gt86&BB` z!Q$D|`35{|PFOsv28(C!V;0Za3>MF-!Q$Bl%;H%tVDYRPES~M`!L7YNES}{A7SFca zV2ks|CmY^w52aHhhfn`p{I>o5Es69O3#ns29=&@1%e%#%wJo1aZp?mKE_6-~-s=Bl z`s2hv^z4%hCztjf8avwAzbPKA+^oI!&ei7MEBWNXbMa@jKb8-^{!>$?_SM8_b#b}* zNdElH+;jQ!w;oNtaA)^{mTlF=t<8T|cQj30tClb1raPu;LtQh^=X#&Wd{Eht%$(?1 zv-$;;WxjP*z>Dy^=4^c9w}F4(c<9=9i;ab& Np~0Q$O9Pqr{sYFSp`QQ% literal 0 HcmV?d00001 diff --git a/impeller/third_party/stb/stb/tests/resample_test.cpp b/impeller/third_party/stb/stb/tests/resample_test.cpp new file mode 100644 index 0000000000000..2c4a56ac0c4aa --- /dev/null +++ b/impeller/third_party/stb/stb/tests/resample_test.cpp @@ -0,0 +1,1116 @@ +#include +#include + +#if defined(_WIN32) && _MSC_VER > 1200 +#define STBIR_ASSERT(x) \ + if (!(x)) { \ + __debugbreak(); \ + } else +#else +#include +#define STBIR_ASSERT(x) assert(x) +#endif + +#define STBIR_MALLOC stbir_malloc +#define STBIR_FREE stbir_free + +class stbir_context { +public: + stbir_context() + { + size = 1000000; + memory = malloc(size); + } + + ~stbir_context() + { + free(memory); + } + + size_t size; + void* memory; +} g_context; + +void* stbir_malloc(size_t size, void* context) +{ + if (!context) + return malloc(size); + + stbir_context* real_context = (stbir_context*)context; + if (size > real_context->size) + return 0; + + return real_context->memory; +} + +void stbir_free(void* memory, void* context) +{ + if (!context) + free(memory); +} + +//#include +void stbir_progress(float p) +{ + //printf("%f\n", p); + STBIR_ASSERT(p >= 0 && p <= 1); +} + +#define STBIR_PROGRESS_REPORT stbir_progress + +#define STB_IMAGE_RESIZE_IMPLEMENTATION +#define STB_IMAGE_RESIZE_STATIC +#include "stb_image_resize.h" + +#define STB_IMAGE_WRITE_IMPLEMENTATION +#include "stb_image_write.h" + +#define STB_IMAGE_IMPLEMENTATION +#include "stb_image.h" + +#ifdef _WIN32 +#include +#endif + +#include + +#define MT_SIZE 624 +static size_t g_aiMT[MT_SIZE]; +static size_t g_iMTI = 0; + +// Mersenne Twister implementation from Wikipedia. +// Avoiding use of the system rand() to be sure that our tests generate the same test data on any system. +void mtsrand(size_t iSeed) +{ + g_aiMT[0] = iSeed; + for (size_t i = 1; i < MT_SIZE; i++) + { + size_t inner1 = g_aiMT[i - 1]; + size_t inner2 = (g_aiMT[i - 1] >> 30); + size_t inner = inner1 ^ inner2; + g_aiMT[i] = (0x6c078965 * inner) + i; + } + + g_iMTI = 0; +} + +size_t mtrand() +{ + if (g_iMTI == 0) + { + for (size_t i = 0; i < MT_SIZE; i++) + { + size_t y = (0x80000000 & (g_aiMT[i])) + (0x7fffffff & (g_aiMT[(i + 1) % MT_SIZE])); + g_aiMT[i] = g_aiMT[(i + 397) % MT_SIZE] ^ (y >> 1); + if ((y % 2) == 1) + g_aiMT[i] = g_aiMT[i] ^ 0x9908b0df; + } + } + + size_t y = g_aiMT[g_iMTI]; + y = y ^ (y >> 11); + y = y ^ ((y << 7) & (0x9d2c5680)); + y = y ^ ((y << 15) & (0xefc60000)); + y = y ^ (y >> 18); + + g_iMTI = (g_iMTI + 1) % MT_SIZE; + + return y; +} + + +inline float mtfrand() +{ + const int ninenine = 999999; + return (float)(mtrand() % ninenine)/ninenine; +} + +static void resizer(int argc, char **argv) +{ + unsigned char* input_pixels; + unsigned char* output_pixels; + int w, h; + int n; + int out_w, out_h; + input_pixels = stbi_load(argv[1], &w, &h, &n, 0); + out_w = w*3; + out_h = h*3; + output_pixels = (unsigned char*) malloc(out_w*out_h*n); + //stbir_resize_uint8_srgb(input_pixels, w, h, 0, output_pixels, out_w, out_h, 0, n, -1,0); + stbir_resize_uint8(input_pixels, w, h, 0, output_pixels, out_w, out_h, 0, n); + stbi_write_png("output.png", out_w, out_h, n, output_pixels, 0); + exit(0); +} + +static void performance(int argc, char **argv) +{ + unsigned char* input_pixels; + unsigned char* output_pixels; + int w, h, count; + int n, i; + int out_w, out_h, srgb=1; + input_pixels = stbi_load(argv[1], &w, &h, &n, 0); + #if 0 + out_w = w/4; out_h = h/4; count=100; // 1 + #elif 0 + out_w = w*2; out_h = h/4; count=20; // 2 // note this is structured pessimily, would be much faster to downsample vertically first + #elif 0 + out_w = w/4; out_h = h*2; count=50; // 3 + #elif 0 + out_w = w*3; out_h = h*3; count=2; srgb=0; // 4 + #else + out_w = w*3; out_h = h*3; count=2; // 5 // this is dominated by linear->sRGB conversion + #endif + + output_pixels = (unsigned char*) malloc(out_w*out_h*n); + for (i=0; i < count; ++i) + if (srgb) + stbir_resize_uint8_srgb(input_pixels, w, h, 0, output_pixels, out_w, out_h, 0, n,-1,0); + else + stbir_resize(input_pixels, w, h, 0, output_pixels, out_w, out_h, 0, STBIR_TYPE_UINT8, n,-1, 0, STBIR_EDGE_CLAMP, STBIR_EDGE_CLAMP, STBIR_FILTER_DEFAULT, STBIR_FILTER_DEFAULT, STBIR_COLORSPACE_LINEAR, NULL); + exit(0); +} + +void test_suite(int argc, char **argv); + +int main(int argc, char** argv) +{ + //resizer(argc, argv); + //performance(argc, argv); + + test_suite(argc, argv); + return 0; +} + +void resize_image(const char* filename, float width_percent, float height_percent, stbir_filter filter, stbir_edge edge, stbir_colorspace colorspace, const char* output_filename) +{ + int w, h, n; + + unsigned char* input_data = stbi_load(filename, &w, &h, &n, 0); + if (!input_data) + { + printf("Input image could not be loaded\n"); + return; + } + + int out_w = (int)(w * width_percent); + int out_h = (int)(h * height_percent); + + unsigned char* output_data = (unsigned char*)malloc(out_w * out_h * n); + + stbir_resize(input_data, w, h, 0, output_data, out_w, out_h, 0, STBIR_TYPE_UINT8, n, STBIR_ALPHA_CHANNEL_NONE, 0, edge, edge, filter, filter, colorspace, &g_context); + + stbi_image_free(input_data); + + stbi_write_png(output_filename, out_w, out_h, n, output_data, 0); + + free(output_data); +} + +template +void convert_image(const F* input, T* output, int length) +{ + double f = (pow(2.0, 8.0 * sizeof(T)) - 1) / (pow(2.0, 8.0 * sizeof(F)) - 1); + for (int i = 0; i < length; i++) + output[i] = (T)(((double)input[i]) * f); +} + +template +void test_format(const char* file, float width_percent, float height_percent, stbir_datatype type, stbir_colorspace colorspace) +{ + int w, h, n; + unsigned char* input_data = stbi_load(file, &w, &h, &n, 0); + + if (input_data == NULL) + return; + + + int new_w = (int)(w * width_percent); + int new_h = (int)(h * height_percent); + + T* T_data = (T*)malloc(w * h * n * sizeof(T)); + memset(T_data, 0, w*h*n*sizeof(T)); + convert_image(input_data, T_data, w * h * n); + + T* output_data = (T*)malloc(new_w * new_h * n * sizeof(T)); + + stbir_resize(T_data, w, h, 0, output_data, new_w, new_h, 0, type, n, STBIR_ALPHA_CHANNEL_NONE, 0, STBIR_EDGE_CLAMP, STBIR_EDGE_CLAMP, STBIR_FILTER_CATMULLROM, STBIR_FILTER_CATMULLROM, colorspace, &g_context); + + free(T_data); + stbi_image_free(input_data); + + unsigned char* char_data = (unsigned char*)malloc(new_w * new_h * n * sizeof(char)); + convert_image(output_data, char_data, new_w * new_h * n); + + char output[200]; + sprintf(output, "test-output/type-%d-%d-%d-%d-%s", type, colorspace, new_w, new_h, file); + stbi_write_png(output, new_w, new_h, n, char_data, 0); + + free(char_data); + free(output_data); +} + +void convert_image_float(const unsigned char* input, float* output, int length) +{ + for (int i = 0; i < length; i++) + output[i] = ((float)input[i])/255; +} + +void convert_image_float(const float* input, unsigned char* output, int length) +{ + for (int i = 0; i < length; i++) + output[i] = (unsigned char)(stbir__saturate(input[i]) * 255); +} + +void test_float(const char* file, float width_percent, float height_percent, stbir_datatype type, stbir_colorspace colorspace) +{ + int w, h, n; + unsigned char* input_data = stbi_load(file, &w, &h, &n, 0); + + if (input_data == NULL) + return; + + int new_w = (int)(w * width_percent); + int new_h = (int)(h * height_percent); + + float* T_data = (float*)malloc(w * h * n * sizeof(float)); + convert_image_float(input_data, T_data, w * h * n); + + float* output_data = (float*)malloc(new_w * new_h * n * sizeof(float)); + + stbir_resize_float_generic(T_data, w, h, 0, output_data, new_w, new_h, 0, n, STBIR_ALPHA_CHANNEL_NONE, 0, STBIR_EDGE_CLAMP, STBIR_FILTER_CATMULLROM, colorspace, &g_context); + + free(T_data); + stbi_image_free(input_data); + + unsigned char* char_data = (unsigned char*)malloc(new_w * new_h * n * sizeof(char)); + convert_image_float(output_data, char_data, new_w * new_h * n); + + char output[200]; + sprintf(output, "test-output/type-%d-%d-%d-%d-%s", type, colorspace, new_w, new_h, file); + stbi_write_png(output, new_w, new_h, n, char_data, 0); + + free(char_data); + free(output_data); +} + +void test_channels(const char* file, float width_percent, float height_percent, int channels) +{ + int w, h, n; + unsigned char* input_data = stbi_load(file, &w, &h, &n, 0); + + if (input_data == NULL) + return; + + int new_w = (int)(w * width_percent); + int new_h = (int)(h * height_percent); + + unsigned char* channels_data = (unsigned char*)malloc(w * h * channels * sizeof(unsigned char)); + + for (int i = 0; i < w * h; i++) + { + int input_position = i * n; + int output_position = i * channels; + + for (int c = 0; c < channels; c++) + channels_data[output_position + c] = input_data[input_position + stbir__min(c, n)]; + } + + unsigned char* output_data = (unsigned char*)malloc(new_w * new_h * channels * sizeof(unsigned char)); + + stbir_resize_uint8_srgb(channels_data, w, h, 0, output_data, new_w, new_h, 0, channels, STBIR_ALPHA_CHANNEL_NONE, 0); + + free(channels_data); + stbi_image_free(input_data); + + char output[200]; + sprintf(output, "test-output/channels-%d-%d-%d-%s", channels, new_w, new_h, file); + stbi_write_png(output, new_w, new_h, channels, output_data, 0); + + free(output_data); +} + +void test_subpixel(const char* file, float width_percent, float height_percent, float s1, float t1) +{ + int w, h, n; + unsigned char* input_data = stbi_load(file, &w, &h, &n, 0); + + if (input_data == NULL) + return; + + s1 = ((float)w - 1 + s1)/w; + t1 = ((float)h - 1 + t1)/h; + + int new_w = (int)(w * width_percent); + int new_h = (int)(h * height_percent); + + unsigned char* output_data = (unsigned char*)malloc(new_w * new_h * n * sizeof(unsigned char)); + + stbir_resize_region(input_data, w, h, 0, output_data, new_w, new_h, 0, STBIR_TYPE_UINT8, n, STBIR_ALPHA_CHANNEL_NONE, 0, STBIR_EDGE_CLAMP, STBIR_EDGE_CLAMP, STBIR_FILTER_BOX, STBIR_FILTER_CATMULLROM, STBIR_COLORSPACE_SRGB, &g_context, 0, 0, s1, t1); + + stbi_image_free(input_data); + + char output[200]; + sprintf(output, "test-output/subpixel-%d-%d-%f-%f-%s", new_w, new_h, s1, t1, file); + stbi_write_png(output, new_w, new_h, n, output_data, 0); + + free(output_data); +} + +void test_subpixel_region(const char* file, float width_percent, float height_percent, float s0, float t0, float s1, float t1) +{ + int w, h, n; + unsigned char* input_data = stbi_load(file, &w, &h, &n, 0); + + if (input_data == NULL) + return; + + int new_w = (int)(w * width_percent); + int new_h = (int)(h * height_percent); + + unsigned char* output_data = (unsigned char*)malloc(new_w * new_h * n * sizeof(unsigned char)); + + stbir_resize_region(input_data, w, h, 0, output_data, new_w, new_h, 0, STBIR_TYPE_UINT8, n, STBIR_ALPHA_CHANNEL_NONE, 0, STBIR_EDGE_CLAMP, STBIR_EDGE_CLAMP, STBIR_FILTER_BOX, STBIR_FILTER_CATMULLROM, STBIR_COLORSPACE_SRGB, &g_context, s0, t0, s1, t1); + + stbi_image_free(input_data); + + char output[200]; + sprintf(output, "test-output/subpixel-region-%d-%d-%f-%f-%f-%f-%s", new_w, new_h, s0, t0, s1, t1, file); + stbi_write_png(output, new_w, new_h, n, output_data, 0); + + free(output_data); +} + +void test_subpixel_command(const char* file, float width_percent, float height_percent, float x_scale, float y_scale, float x_offset, float y_offset) +{ + int w, h, n; + unsigned char* input_data = stbi_load(file, &w, &h, &n, 0); + + if (input_data == NULL) + return; + + int new_w = (int)(w * width_percent); + int new_h = (int)(h * height_percent); + + unsigned char* output_data = (unsigned char*)malloc(new_w * new_h * n * sizeof(unsigned char)); + + stbir_resize_subpixel(input_data, w, h, 0, output_data, new_w, new_h, 0, STBIR_TYPE_UINT8, n, STBIR_ALPHA_CHANNEL_NONE, 0, STBIR_EDGE_CLAMP, STBIR_EDGE_CLAMP, STBIR_FILTER_BOX, STBIR_FILTER_CATMULLROM, STBIR_COLORSPACE_SRGB, &g_context, x_scale, y_scale, x_offset, y_offset); + + stbi_image_free(input_data); + + char output[200]; + sprintf(output, "test-output/subpixel-command-%d-%d-%f-%f-%f-%f-%s", new_w, new_h, x_scale, y_scale, x_offset, y_offset, file); + stbi_write_png(output, new_w, new_h, n, output_data, 0); + + free(output_data); +} + +unsigned int* pixel(unsigned int* buffer, int x, int y, int c, int w, int n) +{ + return &buffer[y*w*n + x*n + c]; +} + +void test_premul() +{ + unsigned int input[2 * 2 * 4]; + unsigned int output[1 * 1 * 4]; + unsigned int output2[2 * 2 * 4]; + + memset(input, 0, sizeof(input)); + + // First a test to make sure premul is working properly. + + // Top left - solid red + *pixel(input, 0, 0, 0, 2, 4) = 255; + *pixel(input, 0, 0, 3, 2, 4) = 255; + + // Bottom left - solid red + *pixel(input, 0, 1, 0, 2, 4) = 255; + *pixel(input, 0, 1, 3, 2, 4) = 255; + + // Top right - transparent green + *pixel(input, 1, 0, 1, 2, 4) = 255; + *pixel(input, 1, 0, 3, 2, 4) = 25; + + // Bottom right - transparent green + *pixel(input, 1, 1, 1, 2, 4) = 255; + *pixel(input, 1, 1, 3, 2, 4) = 25; + + stbir_resize(input, 2, 2, 0, output, 1, 1, 0, STBIR_TYPE_UINT32, 4, 3, 0, STBIR_EDGE_CLAMP, STBIR_EDGE_CLAMP, STBIR_FILTER_BOX, STBIR_FILTER_BOX, STBIR_COLORSPACE_LINEAR, &g_context); + + float r = (float)255 / 4294967296; + float g = (float)255 / 4294967296; + float ra = (float)255 / 4294967296; + float ga = (float)25 / 4294967296; + float a = (ra + ga) / 2; + + STBIR_ASSERT(output[0] == (unsigned int)(r * ra / 2 / a * 4294967296 + 0.5f)); // 232 + STBIR_ASSERT(output[1] == (unsigned int)(g * ga / 2 / a * 4294967296 + 0.5f)); // 23 + STBIR_ASSERT(output[2] == 0); + STBIR_ASSERT(output[3] == (unsigned int)(a * 4294967296 + 0.5f)); // 140 + + // Now a test to make sure it doesn't clobber existing values. + + // Top right - completely transparent green + *pixel(input, 1, 0, 1, 2, 4) = 255; + *pixel(input, 1, 0, 3, 2, 4) = 0; + + // Bottom right - completely transparent green + *pixel(input, 1, 1, 1, 2, 4) = 255; + *pixel(input, 1, 1, 3, 2, 4) = 0; + + stbir_resize(input, 2, 2, 0, output2, 2, 2, 0, STBIR_TYPE_UINT32, 4, 3, 0, STBIR_EDGE_CLAMP, STBIR_EDGE_CLAMP, STBIR_FILTER_BOX, STBIR_FILTER_BOX, STBIR_COLORSPACE_LINEAR, &g_context); + + STBIR_ASSERT(*pixel(output2, 0, 0, 0, 2, 4) == 255); + STBIR_ASSERT(*pixel(output2, 0, 0, 1, 2, 4) == 0); + STBIR_ASSERT(*pixel(output2, 0, 0, 2, 2, 4) == 0); + STBIR_ASSERT(*pixel(output2, 0, 0, 3, 2, 4) == 255); + + STBIR_ASSERT(*pixel(output2, 0, 1, 0, 2, 4) == 255); + STBIR_ASSERT(*pixel(output2, 0, 1, 1, 2, 4) == 0); + STBIR_ASSERT(*pixel(output2, 0, 1, 2, 2, 4) == 0); + STBIR_ASSERT(*pixel(output2, 0, 1, 3, 2, 4) == 255); + + STBIR_ASSERT(*pixel(output2, 1, 0, 0, 2, 4) == 0); + STBIR_ASSERT(*pixel(output2, 1, 0, 1, 2, 4) == 255); + STBIR_ASSERT(*pixel(output2, 1, 0, 2, 2, 4) == 0); + STBIR_ASSERT(*pixel(output2, 1, 0, 3, 2, 4) == 0); + + STBIR_ASSERT(*pixel(output2, 1, 1, 0, 2, 4) == 0); + STBIR_ASSERT(*pixel(output2, 1, 1, 1, 2, 4) == 255); + STBIR_ASSERT(*pixel(output2, 1, 1, 2, 2, 4) == 0); + STBIR_ASSERT(*pixel(output2, 1, 1, 3, 2, 4) == 0); +} + +// test that splitting a pow-2 image into tiles produces identical results +void test_subpixel_1() +{ + unsigned char image[8 * 8]; + + mtsrand(0); + + for (int i = 0; i < sizeof(image); i++) + image[i] = mtrand() & 255; + + unsigned char output_data[16 * 16]; + + stbir_resize_region(image, 8, 8, 0, output_data, 16, 16, 0, STBIR_TYPE_UINT8, 1, STBIR_ALPHA_CHANNEL_NONE, 0, STBIR_EDGE_CLAMP, STBIR_EDGE_CLAMP, STBIR_FILTER_CATMULLROM, STBIR_FILTER_CATMULLROM, STBIR_COLORSPACE_SRGB, &g_context, 0, 0, 1, 1); + + unsigned char output_left[8 * 16]; + unsigned char output_right[8 * 16]; + + stbir_resize_region(image, 8, 8, 0, output_left, 8, 16, 0, STBIR_TYPE_UINT8, 1, STBIR_ALPHA_CHANNEL_NONE, 0, STBIR_EDGE_CLAMP, STBIR_EDGE_CLAMP, STBIR_FILTER_CATMULLROM, STBIR_FILTER_CATMULLROM, STBIR_COLORSPACE_SRGB, &g_context, 0, 0, 0.5f, 1); + stbir_resize_region(image, 8, 8, 0, output_right, 8, 16, 0, STBIR_TYPE_UINT8, 1, STBIR_ALPHA_CHANNEL_NONE, 0, STBIR_EDGE_CLAMP, STBIR_EDGE_CLAMP, STBIR_FILTER_CATMULLROM, STBIR_FILTER_CATMULLROM, STBIR_COLORSPACE_SRGB, &g_context, 0.5f, 0, 1, 1); + + for (int x = 0; x < 8; x++) + { + for (int y = 0; y < 16; y++) + { + STBIR_ASSERT(output_data[y * 16 + x] == output_left[y * 8 + x]); + STBIR_ASSERT(output_data[y * 16 + x + 8] == output_right[y * 8 + x]); + } + } + + stbir_resize_subpixel(image, 8, 8, 0, output_left, 8, 16, 0, STBIR_TYPE_UINT8, 1, STBIR_ALPHA_CHANNEL_NONE, 0, STBIR_EDGE_CLAMP, STBIR_EDGE_CLAMP, STBIR_FILTER_CATMULLROM, STBIR_FILTER_CATMULLROM, STBIR_COLORSPACE_SRGB, &g_context, 2, 2, 0, 0); + stbir_resize_subpixel(image, 8, 8, 0, output_right, 8, 16, 0, STBIR_TYPE_UINT8, 1, STBIR_ALPHA_CHANNEL_NONE, 0, STBIR_EDGE_CLAMP, STBIR_EDGE_CLAMP, STBIR_FILTER_CATMULLROM, STBIR_FILTER_CATMULLROM, STBIR_COLORSPACE_SRGB, &g_context, 2, 2, 8, 0); + + {for (int x = 0; x < 8; x++) + { + for (int y = 0; y < 16; y++) + { + STBIR_ASSERT(output_data[y * 16 + x] == output_left[y * 8 + x]); + STBIR_ASSERT(output_data[y * 16 + x + 8] == output_right[y * 8 + x]); + } + }} +} + +// test that replicating an image and using a subtile of it produces same results as wraparound +void test_subpixel_2() +{ + unsigned char image[8 * 8]; + + mtsrand(0); + + for (int i = 0; i < sizeof(image); i++) + image[i] = mtrand() & 255; + + unsigned char large_image[32 * 32]; + + for (int x = 0; x < 8; x++) + { + for (int y = 0; y < 8; y++) + { + for (int i = 0; i < 4; i++) + { + for (int j = 0; j < 4; j++) + large_image[j*4*8*8 + i*8 + y*4*8 + x] = image[y*8 + x]; + } + } + } + + unsigned char output_data_1[16 * 16]; + unsigned char output_data_2[16 * 16]; + + stbir_resize(image, 8, 8, 0, output_data_1, 16, 16, 0, STBIR_TYPE_UINT8, 1, STBIR_ALPHA_CHANNEL_NONE, 0, STBIR_EDGE_WRAP, STBIR_EDGE_WRAP, STBIR_FILTER_CATMULLROM, STBIR_FILTER_CATMULLROM, STBIR_COLORSPACE_SRGB, &g_context); + stbir_resize_region(large_image, 32, 32, 0, output_data_2, 16, 16, 0, STBIR_TYPE_UINT8, 1, STBIR_ALPHA_CHANNEL_NONE, 0, STBIR_EDGE_WRAP, STBIR_EDGE_WRAP, STBIR_FILTER_CATMULLROM, STBIR_FILTER_CATMULLROM, STBIR_COLORSPACE_SRGB, &g_context, 0.25f, 0.25f, 0.5f, 0.5f); + + {for (int x = 0; x < 16; x++) + { + for (int y = 0; y < 16; y++) + STBIR_ASSERT(output_data_1[y * 16 + x] == output_data_2[y * 16 + x]); + }} + + stbir_resize_subpixel(large_image, 32, 32, 0, output_data_2, 16, 16, 0, STBIR_TYPE_UINT8, 1, STBIR_ALPHA_CHANNEL_NONE, 0, STBIR_EDGE_WRAP, STBIR_EDGE_WRAP, STBIR_FILTER_CATMULLROM, STBIR_FILTER_CATMULLROM, STBIR_COLORSPACE_SRGB, &g_context, 2, 2, 16, 16); + + {for (int x = 0; x < 16; x++) + { + for (int y = 0; y < 16; y++) + STBIR_ASSERT(output_data_1[y * 16 + x] == output_data_2[y * 16 + x]); + }} +} + +// test that 0,0,1,1 subpixel produces same result as no-rect +void test_subpixel_3() +{ + unsigned char image[8 * 8]; + + mtsrand(0); + + for (int i = 0; i < sizeof(image); i++) + image[i] = mtrand() & 255; + + unsigned char output_data_1[32 * 32]; + unsigned char output_data_2[32 * 32]; + + stbir_resize_region(image, 8, 8, 0, output_data_1, 32, 32, 0, STBIR_TYPE_UINT8, 1, 0, STBIR_ALPHA_CHANNEL_NONE, STBIR_EDGE_CLAMP, STBIR_EDGE_CLAMP, STBIR_FILTER_CATMULLROM, STBIR_FILTER_CATMULLROM, STBIR_COLORSPACE_LINEAR, NULL, 0, 0, 1, 1); + stbir_resize_uint8(image, 8, 8, 0, output_data_2, 32, 32, 0, 1); + + for (int x = 0; x < 32; x++) + { + for (int y = 0; y < 32; y++) + STBIR_ASSERT(output_data_1[y * 32 + x] == output_data_2[y * 32 + x]); + } + + stbir_resize_subpixel(image, 8, 8, 0, output_data_1, 32, 32, 0, STBIR_TYPE_UINT8, 1, 0, STBIR_ALPHA_CHANNEL_NONE, STBIR_EDGE_CLAMP, STBIR_EDGE_CLAMP, STBIR_FILTER_CATMULLROM, STBIR_FILTER_CATMULLROM, STBIR_COLORSPACE_LINEAR, NULL, 4, 4, 0, 0); + + {for (int x = 0; x < 32; x++) + { + for (int y = 0; y < 32; y++) + STBIR_ASSERT(output_data_1[y * 32 + x] == output_data_2[y * 32 + x]); + }} +} + +// test that 1:1 resample using s,t=0,0,1,1 with bilinear produces original image +void test_subpixel_4() +{ + unsigned char image[8 * 8]; + + mtsrand(0); + + for (int i = 0; i < sizeof(image); i++) + image[i] = mtrand() & 255; + + unsigned char output[8 * 8]; + + stbir_resize_region(image, 8, 8, 0, output, 8, 8, 0, STBIR_TYPE_UINT8, 1, STBIR_ALPHA_CHANNEL_NONE, 0, STBIR_EDGE_CLAMP, STBIR_EDGE_CLAMP, STBIR_FILTER_TRIANGLE, STBIR_FILTER_TRIANGLE, STBIR_COLORSPACE_LINEAR, &g_context, 0, 0, 1, 1); + STBIR_ASSERT(memcmp(image, output, 8 * 8) == 0); + + stbir_resize_subpixel(image, 8, 8, 0, output, 8, 8, 0, STBIR_TYPE_UINT8, 1, STBIR_ALPHA_CHANNEL_NONE, 0, STBIR_EDGE_CLAMP, STBIR_EDGE_CLAMP, STBIR_FILTER_TRIANGLE, STBIR_FILTER_TRIANGLE, STBIR_COLORSPACE_LINEAR, &g_context, 1, 1, 0, 0); + STBIR_ASSERT(memcmp(image, output, 8 * 8) == 0); +} + +static unsigned int image88_int[8][8]; +static unsigned char image88 [8][8]; +static unsigned char output88[8][8]; +static unsigned char output44[4][4]; +static unsigned char output22[2][2]; +static unsigned char output11[1][1]; + +void resample_88(stbir_filter filter) +{ + stbir_resize_uint8_generic(image88[0],8,8,0, output88[0],8,8,0, 1,-1,0, STBIR_EDGE_CLAMP, filter, STBIR_COLORSPACE_LINEAR, NULL); + stbir_resize_uint8_generic(image88[0],8,8,0, output44[0],4,4,0, 1,-1,0, STBIR_EDGE_CLAMP, filter, STBIR_COLORSPACE_LINEAR, NULL); + stbir_resize_uint8_generic(image88[0],8,8,0, output22[0],2,2,0, 1,-1,0, STBIR_EDGE_CLAMP, filter, STBIR_COLORSPACE_LINEAR, NULL); + stbir_resize_uint8_generic(image88[0],8,8,0, output11[0],1,1,0, 1,-1,0, STBIR_EDGE_CLAMP, filter, STBIR_COLORSPACE_LINEAR, NULL); +} + +void verify_box(void) +{ + int i,j,t; + + resample_88(STBIR_FILTER_BOX); + + for (i=0; i < sizeof(image88); ++i) + STBIR_ASSERT(image88[0][i] == output88[0][i]); + + t = 0; + for (j=0; j < 4; ++j) + for (i=0; i < 4; ++i) { + int n = image88[j*2+0][i*2+0] + + image88[j*2+0][i*2+1] + + image88[j*2+1][i*2+0] + + image88[j*2+1][i*2+1]; + STBIR_ASSERT(output44[j][i] == ((n+2)>>2) || output44[j][i] == ((n+1)>>2)); // can't guarantee exact rounding due to numerical precision + t += n; + } + STBIR_ASSERT(output11[0][0] == ((t+32)>>6) || output11[0][0] == ((t+31)>>6)); // can't guarantee exact rounding due to numerical precision +} + +void verify_filter_normalized(stbir_filter filter, int output_size, unsigned int value) +{ + int i, j; + unsigned int output[64]; + + stbir_resize(image88_int[0], 8, 8, 0, output, output_size, output_size, 0, STBIR_TYPE_UINT32, 1, STBIR_ALPHA_CHANNEL_NONE, 0, STBIR_EDGE_CLAMP, STBIR_EDGE_CLAMP, filter, filter, STBIR_COLORSPACE_LINEAR, NULL); + + for (j = 0; j < output_size; ++j) + for (i = 0; i < output_size; ++i) + STBIR_ASSERT(value == output[j*output_size + i]); +} + +float round2(float f) +{ + return (float) floor(f+0.5f); // round() isn't C standard pre-C99 +} + +void test_filters(void) +{ + int i,j; + + mtsrand(0); + + for (i=0; i < sizeof(image88); ++i) + image88[0][i] = mtrand() & 255; + verify_box(); + + for (i=0; i < sizeof(image88); ++i) + image88[0][i] = 0; + image88[4][4] = 255; + verify_box(); + + for (j=0; j < 8; ++j) + for (i=0; i < 8; ++i) + image88[j][i] = (j^i)&1 ? 255 : 0; + verify_box(); + + for (j=0; j < 8; ++j) + for (i=0; i < 8; ++i) + image88[j][i] = i&2 ? 255 : 0; + verify_box(); + + int value = 64; + + for (j = 0; j < 8; ++j) + for (i = 0; i < 8; ++i) + image88_int[j][i] = value; + + verify_filter_normalized(STBIR_FILTER_BOX, 8, value); + verify_filter_normalized(STBIR_FILTER_TRIANGLE, 8, value); + verify_filter_normalized(STBIR_FILTER_CUBICBSPLINE, 8, value); + verify_filter_normalized(STBIR_FILTER_CATMULLROM, 8, value); + verify_filter_normalized(STBIR_FILTER_MITCHELL, 8, value); + + verify_filter_normalized(STBIR_FILTER_BOX, 4, value); + verify_filter_normalized(STBIR_FILTER_TRIANGLE, 4, value); + verify_filter_normalized(STBIR_FILTER_CUBICBSPLINE, 4, value); + verify_filter_normalized(STBIR_FILTER_CATMULLROM, 4, value); + verify_filter_normalized(STBIR_FILTER_MITCHELL, 4, value); + + verify_filter_normalized(STBIR_FILTER_BOX, 2, value); + verify_filter_normalized(STBIR_FILTER_TRIANGLE, 2, value); + verify_filter_normalized(STBIR_FILTER_CUBICBSPLINE, 2, value); + verify_filter_normalized(STBIR_FILTER_CATMULLROM, 2, value); + verify_filter_normalized(STBIR_FILTER_MITCHELL, 2, value); + + verify_filter_normalized(STBIR_FILTER_BOX, 1, value); + verify_filter_normalized(STBIR_FILTER_TRIANGLE, 1, value); + verify_filter_normalized(STBIR_FILTER_CUBICBSPLINE, 1, value); + verify_filter_normalized(STBIR_FILTER_CATMULLROM, 1, value); + verify_filter_normalized(STBIR_FILTER_MITCHELL, 1, value); + + { + // This test is designed to produce coefficients that are very badly denormalized. + unsigned int v = 556; + + unsigned int input[100 * 100]; + unsigned int output[11 * 11]; + + for (j = 0; j < 100 * 100; ++j) + input[j] = v; + + stbir_resize(input, 100, 100, 0, output, 11, 11, 0, STBIR_TYPE_UINT32, 1, STBIR_ALPHA_CHANNEL_NONE, 0, STBIR_EDGE_CLAMP, STBIR_EDGE_CLAMP, STBIR_FILTER_TRIANGLE, STBIR_FILTER_TRIANGLE, STBIR_COLORSPACE_LINEAR, NULL); + + for (j = 0; j < 11 * 11; ++j) + STBIR_ASSERT(v == output[j]); + } + + { + // Now test the trapezoid filter for downsampling. + unsigned int input[3 * 1]; + unsigned int output[2 * 1]; + + input[0] = 0; + input[1] = 255; + input[2] = 127; + + stbir_resize(input, 3, 1, 0, output, 2, 1, 0, STBIR_TYPE_UINT32, 1, STBIR_ALPHA_CHANNEL_NONE, 0, STBIR_EDGE_CLAMP, STBIR_EDGE_CLAMP, STBIR_FILTER_BOX, STBIR_FILTER_BOX, STBIR_COLORSPACE_LINEAR, NULL); + + STBIR_ASSERT(output[0] == (unsigned int)round2((float)(input[0] * 2 + input[1]) / 3)); + STBIR_ASSERT(output[1] == (unsigned int)round2((float)(input[2] * 2 + input[1]) / 3)); + + stbir_resize(input, 1, 3, 0, output, 1, 2, 0, STBIR_TYPE_UINT32, 1, STBIR_ALPHA_CHANNEL_NONE, 0, STBIR_EDGE_CLAMP, STBIR_EDGE_CLAMP, STBIR_FILTER_BOX, STBIR_FILTER_BOX, STBIR_COLORSPACE_LINEAR, NULL); + + STBIR_ASSERT(output[0] == (unsigned int)round2((float)(input[0] * 2 + input[1]) / 3)); + STBIR_ASSERT(output[1] == (unsigned int)round2((float)(input[2] * 2 + input[1]) / 3)); + } + + { + // Now test the trapezoid filter for upsampling. + unsigned int input[2 * 1]; + unsigned int output[3 * 1]; + + input[0] = 0; + input[1] = 255; + + stbir_resize(input, 2, 1, 0, output, 3, 1, 0, STBIR_TYPE_UINT32, 1, STBIR_ALPHA_CHANNEL_NONE, 0, STBIR_EDGE_CLAMP, STBIR_EDGE_CLAMP, STBIR_FILTER_BOX, STBIR_FILTER_BOX, STBIR_COLORSPACE_LINEAR, NULL); + + STBIR_ASSERT(output[0] == input[0]); + STBIR_ASSERT(output[1] == (input[0] + input[1]) / 2); + STBIR_ASSERT(output[2] == input[1]); + + stbir_resize(input, 1, 2, 0, output, 1, 3, 0, STBIR_TYPE_UINT32, 1, STBIR_ALPHA_CHANNEL_NONE, 0, STBIR_EDGE_CLAMP, STBIR_EDGE_CLAMP, STBIR_FILTER_BOX, STBIR_FILTER_BOX, STBIR_COLORSPACE_LINEAR, NULL); + + STBIR_ASSERT(output[0] == input[0]); + STBIR_ASSERT(output[1] == (input[0] + input[1]) / 2); + STBIR_ASSERT(output[2] == input[1]); + } + + // checkerboard + { + unsigned char input[64][64]; + unsigned char output[16][16]; + int i,j; + for (j=0; j < 64; ++j) + for (i=0; i < 64; ++i) + input[j][i] = (i^j)&1 ? 255 : 0; + stbir_resize_uint8_generic(input[0], 64, 64, 0, output[0],16,16,0, 1,-1,0,STBIR_EDGE_WRAP,STBIR_FILTER_DEFAULT,STBIR_COLORSPACE_LINEAR,0); + for (j=0; j < 16; ++j) + for (i=0; i < 16; ++i) + STBIR_ASSERT(output[j][i] == 128); + stbir_resize_uint8_srgb_edgemode(input[0], 64, 64, 0, output[0],16,16,0, 1,-1,0,STBIR_EDGE_WRAP); + for (j=0; j < 16; ++j) + for (i=0; i < 16; ++i) + STBIR_ASSERT(output[j][i] == 188); + + + } + + { + // Test trapezoid box filter + unsigned char input[2 * 1]; + unsigned char output[127 * 1]; + + input[0] = 0; + input[1] = 255; + + stbir_resize(input, 2, 1, 0, output, 127, 1, 0, STBIR_TYPE_UINT8, 1, STBIR_ALPHA_CHANNEL_NONE, 0, STBIR_EDGE_CLAMP, STBIR_EDGE_CLAMP, STBIR_FILTER_BOX, STBIR_FILTER_BOX, STBIR_COLORSPACE_LINEAR, NULL); + STBIR_ASSERT(output[0] == 0); + STBIR_ASSERT(output[127 / 2 - 1] == 0); + STBIR_ASSERT(output[127 / 2] == 128); + STBIR_ASSERT(output[127 / 2 + 1] == 255); + STBIR_ASSERT(output[126] == 255); + stbi_write_png("test-output/trapezoid-upsample-horizontal.png", 127, 1, 1, output, 0); + + stbir_resize(input, 1, 2, 0, output, 1, 127, 0, STBIR_TYPE_UINT8, 1, STBIR_ALPHA_CHANNEL_NONE, 0, STBIR_EDGE_CLAMP, STBIR_EDGE_CLAMP, STBIR_FILTER_BOX, STBIR_FILTER_BOX, STBIR_COLORSPACE_LINEAR, NULL); + STBIR_ASSERT(output[0] == 0); + STBIR_ASSERT(output[127 / 2 - 1] == 0); + STBIR_ASSERT(output[127 / 2] == 128); + STBIR_ASSERT(output[127 / 2 + 1] == 255); + STBIR_ASSERT(output[126] == 255); + stbi_write_png("test-output/trapezoid-upsample-vertical.png", 1, 127, 1, output, 0); + } +} + +#define UMAX32 4294967295U + +static void write32(char *filename, stbir_uint32 *output, int w, int h) +{ + stbir_uint8 *data = (stbir_uint8*) malloc(w*h*3); + for (int i=0; i < w*h*3; ++i) + data[i] = output[i]>>24; + stbi_write_png(filename, w, h, 3, data, 0); + free(data); +} + +static void test_32(void) +{ + int w=100,h=120,x,y, out_w,out_h; + stbir_uint32 *input = (stbir_uint32*) malloc(4 * 3 * w * h); + stbir_uint32 *output = (stbir_uint32*) malloc(4 * 3 * 3*w * 3*h); + for (y=0; y < h; ++y) { + for (x=0; x < w; ++x) { + input[y*3*w + x*3 + 0] = x * ( UMAX32/w ); + input[y*3*w + x*3 + 1] = y * ( UMAX32/h ); + input[y*3*w + x*3 + 2] = UMAX32/2; + } + } + out_w = w*33/16; + out_h = h*33/16; + stbir_resize(input,w,h,0,output,out_w,out_h,0,STBIR_TYPE_UINT32,3,-1,0,STBIR_EDGE_CLAMP,STBIR_EDGE_CLAMP,STBIR_FILTER_DEFAULT,STBIR_FILTER_DEFAULT,STBIR_COLORSPACE_LINEAR,NULL); + write32("test-output/seantest_1.png", output,out_w,out_h); + + out_w = w*16/33; + out_h = h*16/33; + stbir_resize(input,w,h,0,output,out_w,out_h,0,STBIR_TYPE_UINT32,3,-1,0,STBIR_EDGE_CLAMP,STBIR_EDGE_CLAMP,STBIR_FILTER_DEFAULT,STBIR_FILTER_DEFAULT,STBIR_COLORSPACE_LINEAR,NULL); + write32("test-output/seantest_2.png", output,out_w,out_h); +} + + +void test_suite(int argc, char **argv) +{ + int i; + char *barbara; + + _mkdir("test-output"); + + if (argc > 1) + barbara = argv[1]; + else + barbara = "barbara.png"; + + // check what cases we need normalization for +#if 1 + { + float x, y; + for (x = -1; x < 1; x += 0.05f) { + float sums[5] = { 0 }; + float o; + for (o = -5; o <= 5; ++o) { + sums[0] += stbir__filter_mitchell(x + o, 1); + sums[1] += stbir__filter_catmullrom(x + o, 1); + sums[2] += stbir__filter_cubic(x + o, 1); + sums[3] += stbir__filter_triangle(x + o, 1); + sums[4] += stbir__filter_trapezoid(x + o, 0.5f); + } + for (i = 0; i < 5; ++i) + STBIR_ASSERT(sums[i] >= 1.0 - 0.001 && sums[i] <= 1.0 + 0.001); + } + +#if 1 + for (y = 0.11f; y < 1; y += 0.01f) { // Step + for (x = -1; x < 1; x += 0.05f) { // Phase + float sums[5] = { 0 }; + float o; + for (o = -5; o <= 5; o += y) { + sums[0] += y * stbir__filter_mitchell(x + o, 1); + sums[1] += y * stbir__filter_catmullrom(x + o, 1); + sums[2] += y * stbir__filter_cubic(x + o, 1); + sums[4] += y * stbir__filter_trapezoid(x + o, 0.5f); + sums[3] += y * stbir__filter_triangle(x + o, 1); + } + for (i = 0; i < 3; ++i) + STBIR_ASSERT(sums[i] >= 1.0 - 0.0170 && sums[i] <= 1.0 + 0.0170); + } + } +#endif + } +#endif + +#if 0 // linear_to_srgb_uchar table + for (i=0; i < 256; ++i) { + float f = stbir__srgb_to_linear((i-0.5f)/255.0f); + printf("%9d, ", (int) ((f) * (1<<28))); + if ((i & 7) == 7) + printf("\n"); + } +#endif + + // old tests that hacky fix worked on - test that + // every uint8 maps to itself + for (i = 0; i < 256; i++) { + float f = stbir__srgb_to_linear(float(i) / 255); + int n = stbir__linear_to_srgb_uchar(f); + STBIR_ASSERT(n == i); + } + + // new tests that hacky fix failed for - test that + // values adjacent to uint8 round to nearest uint8 + for (i = 0; i < 256; i++) { + for (float y = -0.42f; y <= 0.42f; y += 0.01f) { + float f = stbir__srgb_to_linear((i+y) / 255.0f); + int n = stbir__linear_to_srgb_uchar(f); + STBIR_ASSERT(n == i); + } + } + + test_filters(); + + test_subpixel_1(); + test_subpixel_2(); + test_subpixel_3(); + test_subpixel_4(); + + test_premul(); + + test_32(); + + // Some tests to make sure errors don't pop up with strange filter/dimension combinations. + stbir_resize(image88, 8, 8, 0, output88, 4, 16, 0, STBIR_TYPE_UINT8, 1, STBIR_ALPHA_CHANNEL_NONE, 0, STBIR_EDGE_CLAMP, STBIR_EDGE_CLAMP, STBIR_FILTER_BOX, STBIR_FILTER_CATMULLROM, STBIR_COLORSPACE_SRGB, &g_context); + stbir_resize(image88, 8, 8, 0, output88, 4, 16, 0, STBIR_TYPE_UINT8, 1, STBIR_ALPHA_CHANNEL_NONE, 0, STBIR_EDGE_CLAMP, STBIR_EDGE_CLAMP, STBIR_FILTER_CATMULLROM, STBIR_FILTER_BOX, STBIR_COLORSPACE_SRGB, &g_context); + stbir_resize(image88, 8, 8, 0, output88, 16, 4, 0, STBIR_TYPE_UINT8, 1, STBIR_ALPHA_CHANNEL_NONE, 0, STBIR_EDGE_CLAMP, STBIR_EDGE_CLAMP, STBIR_FILTER_BOX, STBIR_FILTER_CATMULLROM, STBIR_COLORSPACE_SRGB, &g_context); + stbir_resize(image88, 8, 8, 0, output88, 16, 4, 0, STBIR_TYPE_UINT8, 1, STBIR_ALPHA_CHANNEL_NONE, 0, STBIR_EDGE_CLAMP, STBIR_EDGE_CLAMP, STBIR_FILTER_CATMULLROM, STBIR_FILTER_BOX, STBIR_COLORSPACE_SRGB, &g_context); + + int barbara_width, barbara_height, barbara_channels; + stbi_image_free(stbi_load(barbara, &barbara_width, &barbara_height, &barbara_channels, 0)); + + int res = 10; + // Downscaling + {for (int i = 0; i <= res; i++) + { + float t = (float)i/res; + float scale = 0.5; + float out_scale = 2.0f/3; + float x_shift = (barbara_width*out_scale - barbara_width*scale) * t; + float y_shift = (barbara_height*out_scale - barbara_height*scale) * t; + + test_subpixel_command(barbara, scale, scale, out_scale, out_scale, x_shift, y_shift); + }} + + // Upscaling + {for (int i = 0; i <= res; i++) + { + float t = (float)i/res; + float scale = 2; + float out_scale = 3; + float x_shift = (barbara_width*out_scale - barbara_width*scale) * t; + float y_shift = (barbara_height*out_scale - barbara_height*scale) * t; + + test_subpixel_command(barbara, scale, scale, out_scale, out_scale, x_shift, y_shift); + }} + + // Downscaling + {for (int i = 0; i <= res; i++) + { + float t = (float)i/res / 2; + test_subpixel_region(barbara, 0.25f, 0.25f, t, t, t+0.5f, t+0.5f); + }} + + // No scaling + {for (int i = 0; i <= res; i++) + { + float t = (float)i/res / 2; + test_subpixel_region(barbara, 0.5f, 0.5f, t, t, t+0.5f, t+0.5f); + }} + + // Upscaling + {for (int i = 0; i <= res; i++) + { + float t = (float)i/res / 2; + test_subpixel_region(barbara, 1, 1, t, t, t+0.5f, t+0.5f); + }} + + {for (i = 0; i < 10; i++) + test_subpixel(barbara, 0.5f, 0.5f, (float)i / 10, 1); + } + + {for (i = 0; i < 10; i++) + test_subpixel(barbara, 0.5f, 0.5f, 1, (float)i / 10); + } + + {for (i = 0; i < 10; i++) + test_subpixel(barbara, 2, 2, (float)i / 10, 1); + } + + {for (i = 0; i < 10; i++) + test_subpixel(barbara, 2, 2, 1, (float)i / 10); + } + + // Channels test + test_channels(barbara, 0.5f, 0.5f, 1); + test_channels(barbara, 0.5f, 0.5f, 2); + test_channels(barbara, 0.5f, 0.5f, 3); + test_channels(barbara, 0.5f, 0.5f, 4); + + test_channels(barbara, 2, 2, 1); + test_channels(barbara, 2, 2, 2); + test_channels(barbara, 2, 2, 3); + test_channels(barbara, 2, 2, 4); + + // filter tests + resize_image(barbara, 2, 2, STBIR_FILTER_BOX , STBIR_EDGE_CLAMP, STBIR_COLORSPACE_SRGB, "test-output/barbara-upsample-nearest.png"); + resize_image(barbara, 2, 2, STBIR_FILTER_TRIANGLE , STBIR_EDGE_CLAMP, STBIR_COLORSPACE_SRGB, "test-output/barbara-upsample-bilinear.png"); + resize_image(barbara, 2, 2, STBIR_FILTER_CUBICBSPLINE, STBIR_EDGE_CLAMP, STBIR_COLORSPACE_SRGB, "test-output/barbara-upsample-bicubic.png"); + resize_image(barbara, 2, 2, STBIR_FILTER_CATMULLROM , STBIR_EDGE_CLAMP, STBIR_COLORSPACE_SRGB, "test-output/barbara-upsample-catmullrom.png"); + resize_image(barbara, 2, 2, STBIR_FILTER_MITCHELL , STBIR_EDGE_CLAMP, STBIR_COLORSPACE_SRGB, "test-output/barbara-upsample-mitchell.png"); + + resize_image(barbara, 0.5f, 0.5f, STBIR_FILTER_BOX , STBIR_EDGE_CLAMP, STBIR_COLORSPACE_SRGB, "test-output/barbara-downsample-nearest.png"); + resize_image(barbara, 0.5f, 0.5f, STBIR_FILTER_TRIANGLE , STBIR_EDGE_CLAMP, STBIR_COLORSPACE_SRGB, "test-output/barbara-downsample-bilinear.png"); + resize_image(barbara, 0.5f, 0.5f, STBIR_FILTER_CUBICBSPLINE, STBIR_EDGE_CLAMP, STBIR_COLORSPACE_SRGB, "test-output/barbara-downsample-bicubic.png"); + resize_image(barbara, 0.5f, 0.5f, STBIR_FILTER_CATMULLROM , STBIR_EDGE_CLAMP, STBIR_COLORSPACE_SRGB, "test-output/barbara-downsample-catmullrom.png"); + resize_image(barbara, 0.5f, 0.5f, STBIR_FILTER_MITCHELL , STBIR_EDGE_CLAMP, STBIR_COLORSPACE_SRGB, "test-output/barbara-downsample-mitchell.png"); + + {for (i = 10; i < 100; i++) + { + char outname[200]; + sprintf(outname, "test-output/barbara-width-%d.jpg", i); + resize_image(barbara, (float)i / 100, 1, STBIR_FILTER_CATMULLROM, STBIR_EDGE_CLAMP, STBIR_COLORSPACE_SRGB, outname); + }} + + {for (i = 110; i < 500; i += 10) + { + char outname[200]; + sprintf(outname, "test-output/barbara-width-%d.jpg", i); + resize_image(barbara, (float)i / 100, 1, STBIR_FILTER_CATMULLROM, STBIR_EDGE_CLAMP, STBIR_COLORSPACE_SRGB, outname); + }} + + {for (i = 10; i < 100; i++) + { + char outname[200]; + sprintf(outname, "test-output/barbara-height-%d.jpg", i); + resize_image(barbara, 1, (float)i / 100, STBIR_FILTER_CATMULLROM, STBIR_EDGE_CLAMP, STBIR_COLORSPACE_SRGB, outname); + }} + + {for (i = 110; i < 500; i += 10) + { + char outname[200]; + sprintf(outname, "test-output/barbara-height-%d.jpg", i); + resize_image(barbara, 1, (float)i / 100, STBIR_FILTER_CATMULLROM, STBIR_EDGE_CLAMP, STBIR_COLORSPACE_SRGB, outname); + }} + + {for (i = 50; i < 200; i += 10) + { + char outname[200]; + sprintf(outname, "test-output/barbara-width-height-%d.jpg", i); + resize_image(barbara, 100 / (float)i, (float)i / 100, STBIR_FILTER_CATMULLROM, STBIR_EDGE_CLAMP, STBIR_COLORSPACE_SRGB, outname); + }} + + test_format(barbara, 0.5, 2.0, STBIR_TYPE_UINT16, STBIR_COLORSPACE_SRGB); + test_format(barbara, 0.5, 2.0, STBIR_TYPE_UINT16, STBIR_COLORSPACE_LINEAR); + test_format(barbara, 2.0, 0.5, STBIR_TYPE_UINT16, STBIR_COLORSPACE_SRGB); + test_format(barbara, 2.0, 0.5, STBIR_TYPE_UINT16, STBIR_COLORSPACE_LINEAR); + + test_format(barbara, 0.5, 2.0, STBIR_TYPE_UINT32, STBIR_COLORSPACE_SRGB); + test_format(barbara, 0.5, 2.0, STBIR_TYPE_UINT32, STBIR_COLORSPACE_LINEAR); + test_format(barbara, 2.0, 0.5, STBIR_TYPE_UINT32, STBIR_COLORSPACE_SRGB); + test_format(barbara, 2.0, 0.5, STBIR_TYPE_UINT32, STBIR_COLORSPACE_LINEAR); + + test_float(barbara, 0.5, 2.0, STBIR_TYPE_FLOAT, STBIR_COLORSPACE_SRGB); + test_float(barbara, 0.5, 2.0, STBIR_TYPE_FLOAT, STBIR_COLORSPACE_LINEAR); + test_float(barbara, 2.0, 0.5, STBIR_TYPE_FLOAT, STBIR_COLORSPACE_SRGB); + test_float(barbara, 2.0, 0.5, STBIR_TYPE_FLOAT, STBIR_COLORSPACE_LINEAR); + + // Edge behavior tests + resize_image("hgradient.png", 2, 2, STBIR_FILTER_CATMULLROM, STBIR_EDGE_CLAMP, STBIR_COLORSPACE_LINEAR, "test-output/hgradient-clamp.png"); + resize_image("hgradient.png", 2, 2, STBIR_FILTER_CATMULLROM, STBIR_EDGE_WRAP, STBIR_COLORSPACE_LINEAR, "test-output/hgradient-wrap.png"); + + resize_image("vgradient.png", 2, 2, STBIR_FILTER_CATMULLROM, STBIR_EDGE_CLAMP, STBIR_COLORSPACE_LINEAR, "test-output/vgradient-clamp.png"); + resize_image("vgradient.png", 2, 2, STBIR_FILTER_CATMULLROM, STBIR_EDGE_WRAP, STBIR_COLORSPACE_LINEAR, "test-output/vgradient-wrap.png"); + + resize_image("1px-border.png", 2, 2, STBIR_FILTER_CATMULLROM, STBIR_EDGE_REFLECT, STBIR_COLORSPACE_LINEAR, "test-output/1px-border-reflect.png"); + resize_image("1px-border.png", 2, 2, STBIR_FILTER_CATMULLROM, STBIR_EDGE_CLAMP, STBIR_COLORSPACE_LINEAR, "test-output/1px-border-clamp.png"); + + // sRGB tests + resize_image("gamma_colors.jpg", .5f, .5f, STBIR_FILTER_CATMULLROM, STBIR_EDGE_REFLECT, STBIR_COLORSPACE_SRGB, "test-output/gamma_colors.jpg"); + resize_image("gamma_2.2.jpg", .5f, .5f, STBIR_FILTER_CATMULLROM, STBIR_EDGE_REFLECT, STBIR_COLORSPACE_SRGB, "test-output/gamma_2.2.jpg"); + resize_image("gamma_dalai_lama_gray.jpg", .5f, .5f, STBIR_FILTER_CATMULLROM, STBIR_EDGE_REFLECT, STBIR_COLORSPACE_SRGB, "test-output/gamma_dalai_lama_gray.jpg"); +} diff --git a/impeller/third_party/stb/stb/tests/resample_test_c.c b/impeller/third_party/stb/stb/tests/resample_test_c.c new file mode 100644 index 0000000000000..50520c60a9358 --- /dev/null +++ b/impeller/third_party/stb/stb/tests/resample_test_c.c @@ -0,0 +1,5 @@ +#define STB_IMAGE_RESIZE_IMPLEMENTATION +#define STB_IMAGE_RESIZE_STATIC +#include "stb_image_resize.h" + +// Just to make sure it will build properly with a c compiler diff --git a/impeller/third_party/stb/stb/tests/resize.dsp b/impeller/third_party/stb/stb/tests/resize.dsp new file mode 100644 index 0000000000000..0aa1bbaf9cae1 --- /dev/null +++ b/impeller/third_party/stb/stb/tests/resize.dsp @@ -0,0 +1,94 @@ +# Microsoft Developer Studio Project File - Name="resize" - Package Owner=<4> +# Microsoft Developer Studio Generated Build File, Format Version 6.00 +# ** DO NOT EDIT ** + +# TARGTYPE "Win32 (x86) Console Application" 0x0103 + +CFG=resize - Win32 Debug +!MESSAGE This is not a valid makefile. To build this project using NMAKE, +!MESSAGE use the Export Makefile command and run +!MESSAGE +!MESSAGE NMAKE /f "resize.mak". +!MESSAGE +!MESSAGE You can specify a configuration when running NMAKE +!MESSAGE by defining the macro CFG on the command line. For example: +!MESSAGE +!MESSAGE NMAKE /f "resize.mak" CFG="resize - Win32 Debug" +!MESSAGE +!MESSAGE Possible choices for configuration are: +!MESSAGE +!MESSAGE "resize - Win32 Release" (based on "Win32 (x86) Console Application") +!MESSAGE "resize - Win32 Debug" (based on "Win32 (x86) Console Application") +!MESSAGE + +# Begin Project +# PROP AllowPerConfigDependencies 0 +# PROP Scc_ProjName "" +# PROP Scc_LocalPath "" +CPP=cl.exe +RSC=rc.exe + +!IF "$(CFG)" == "resize - Win32 Release" + +# PROP BASE Use_MFC 0 +# PROP BASE Use_Debug_Libraries 0 +# PROP BASE Output_Dir "Release" +# PROP BASE Intermediate_Dir "Release" +# PROP BASE Target_Dir "" +# PROP Use_MFC 0 +# PROP Use_Debug_Libraries 0 +# PROP Output_Dir "Release" +# PROP Intermediate_Dir "Release" +# PROP Ignore_Export_Lib 0 +# PROP Target_Dir "" +# ADD BASE CPP /nologo /W3 /GX /O2 /D "WIN32" /D "NDEBUG" /D "_CONSOLE" /D "_MBCS" /YX /FD /c +# ADD CPP /nologo /G6 /W3 /GX /Z7 /O2 /I ".." /D "WIN32" /D "NDEBUG" /D "_CONSOLE" /D "_MBCS" /YX /FD /c +# ADD BASE RSC /l 0x409 /d "NDEBUG" +# ADD RSC /l 0x409 /d "NDEBUG" +BSC32=bscmake.exe +# ADD BASE BSC32 /nologo +# ADD BSC32 /nologo +LINK32=link.exe +# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:console /machine:I386 +# ADD LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:console /debug /machine:I386 + +!ELSEIF "$(CFG)" == "resize - Win32 Debug" + +# PROP BASE Use_MFC 0 +# PROP BASE Use_Debug_Libraries 1 +# PROP BASE Output_Dir "Debug" +# PROP BASE Intermediate_Dir "Debug" +# PROP BASE Target_Dir "" +# PROP Use_MFC 0 +# PROP Use_Debug_Libraries 1 +# PROP Output_Dir "Debug" +# PROP Intermediate_Dir "Debug" +# PROP Target_Dir "" +# ADD BASE CPP /nologo /W3 /Gm /GX /ZI /Od /D "WIN32" /D "_DEBUG" /D "_CONSOLE" /D "_MBCS" /YX /FD /GZ /c +# ADD CPP /nologo /W3 /WX /Gm /GX /ZI /Od /I ".." /D "WIN32" /D "_DEBUG" /D "_CONSOLE" /D "_MBCS" /FD /GZ /c +# SUBTRACT CPP /YX +# ADD BASE RSC /l 0x409 /d "_DEBUG" +# ADD RSC /l 0x409 /d "_DEBUG" +BSC32=bscmake.exe +# ADD BASE BSC32 /nologo +# ADD BSC32 /nologo +LINK32=link.exe +# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:console /debug /machine:I386 /pdbtype:sept +# ADD LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:console /debug /machine:I386 /pdbtype:sept + +!ENDIF + +# Begin Target + +# Name "resize - Win32 Release" +# Name "resize - Win32 Debug" +# Begin Source File + +SOURCE=.\resample_test.cpp +# End Source File +# Begin Source File + +SOURCE=..\stb_image_resize.h +# End Source File +# End Target +# End Project diff --git a/impeller/third_party/stb/stb/tests/stb.c b/impeller/third_party/stb/stb/tests/stb.c new file mode 100644 index 0000000000000..e0731d6c30b2e --- /dev/null +++ b/impeller/third_party/stb/stb/tests/stb.c @@ -0,0 +1,3323 @@ +/* + * Unit tests for "stb.h" + */ + +//#include +#include +#include +#include +#include +#include + +#ifdef _WIN32 +#include +#endif + +#define STB_STUA +//#define STB_FASTMALLOC +#ifdef _DEBUG +#define STB_MALLOC_WRAPPER_DEBUG +#endif +#define STB_NPTR +#define STB_DEFINE +#include "stb.h" + +//#include "stb_file.h" +//#include "stb_pixel32.h" + +//#define DEBUG_BLOCK +#ifdef DEBUG_BLOCK +#include +#endif + +#ifdef STB_FASTMALLOC +#error "can't use FASTMALLOC with threads" +#endif + +int count; +void c(int truth, char *error) +{ + if (!truth) { + fprintf(stderr, "Test failed: %s\n", error); + ++count; + } +} + + +#if 0 +void show(void) +{ + #ifdef _WIN32 + SYSTEM_INFO x; + GetSystemInfo(&x); + printf("%d\n", x.dwPageSize); + #endif +} +#endif + +void test_classes(void) +{ + unsigned char size_base[32], size_shift[32]; + int class_to_pages[256]; + int class_to_size[256], cl; + int lg, size, wasted_pages; + int kAlignShift = 3; + int kAlignment = 1 << kAlignShift; + int kMaxSize = 8 * 4096; + int kPageShift = 12; + int kPageSize = (1 << kPageShift); + int next_class = 1; + int alignshift = kAlignShift; + int last_lg = -1; + + for (lg = 0; lg < kAlignShift; lg++) { + size_base[lg] = 1; + size_shift[lg] = kAlignShift; + } + + for (size = kAlignment; size <= kMaxSize; size += (1 << alignshift)) { + int lg = stb_log2_floor(size); + if (lg > last_lg) { + // Increase alignment every so often. + // + // Since we double the alignment every time size doubles and + // size >= 128, this means that space wasted due to alignment is + // at most 16/128 i.e., 12.5%. Plus we cap the alignment at 256 + // bytes, so the space wasted as a percentage starts falling for + // sizes > 2K. + if ((lg >= 7) && (alignshift < 8)) { + alignshift++; + } + size_base[lg] = next_class - ((size-1) >> alignshift); + size_shift[lg] = alignshift; + } + + class_to_size[next_class] = size; + last_lg = lg; + + next_class++; + } + + // Initialize the number of pages we should allocate to split into + // small objects for a given class. + wasted_pages = 0; + for (cl = 1; cl < next_class; cl++) { + // Allocate enough pages so leftover is less than 1/8 of total. + // This bounds wasted space to at most 12.5%. + size_t psize = kPageSize; + const size_t s = class_to_size[cl]; + while ((psize % s) > (psize >> 3)) { + psize += kPageSize; + } + class_to_pages[cl] = psize >> kPageShift; + wasted_pages += psize; + } + + printf("TCMalloc can waste as much as %d memory on one-shot allocations\n", wasted_pages); + + + return; +} + + +void test_script(void) +{ + stua_run_script( + "var g = (2+3)*5 + 3*(2+1) + ((7)); \n" + "func sprint(x) _print(x) _print(' ') x end;\n" + "func foo(y) var q = func(x) sprint(x) end; q end;\n " + "var z=foo(5); z(77);\n" + "func counter(z) func(x) z=z+1 end end\n" + "var q=counter(0), p=counter(5);\n" + "sprint(q()) sprint(p()) sprint(q()) sprint(p()) sprint(q()) sprint(p())\n" + "var x=2222;\n" + "if 1 == 2 then 3333 else 4444 end; => x; sprint(x);\n" + "var x1 = sprint(1.5e3); \n" + "var x2 = sprint(.5); \n" + "var x3 = sprint(1.); \n" + "var x4 = sprint(1.e3); \n" + "var x5 = sprint(1e3); \n" + "var x6 = sprint(0.5e3); \n" + "var x7 = sprint(.5e3); \n" + " func sum(x,y) x+y end \n" + " func sumfunc(a) sum+{x=a} end \n" + " var q = sumfunc(3) \n" + " var p = sumfunc(20) \n" + " var d = sprint(q(5)) - sprint(q(8)) \n" + " var e = sprint(p(5)) - sprint(p(8)) \n" + " func test3(x) \n" + " sprint(x) \n" + " x = x+3 \n" + " sprint(x) \n" + " x+5 \n" + " end \n" + " var y = test3(4); \n" + " func fib(x) \n" + " if x < 3 then \n" + " 1 \n" + " else \n" + " fib(x-1) + fib(x-2); \n" + " end \n" + " end \n" + " \n" + " func fib2(x) \n" + " var a=1 \n" + " var b=1 \n" + " sprint(a) \n" + " sprint(b) \n" + " while x > 2 do \n" + " var c=a+b \n" + " a=b \n" + " b=c \n" + " sprint(b) \n" + " x=x-1 \n" + " end \n" + " b \n" + " end \n" + " \n" + " func assign(z) \n" + " var y = { 'this', 'is', 'a', 'lame', 'day', 'to', 'die'} \n" + " y[3] = z \n" + " var i = 0 \n" + " while y[i] != nil do \n" + " sprint(y[i]) \n" + " i = i+1 \n" + " end \n" + " end \n" + " \n" + " sprint(fib(12)); \n" + " assign(\"good\"); \n" + " fib2(20); \n" + " sprint('ok'); \n" + " sprint(-5); \n" + " // final comment with no newline" + ); +} + +#ifdef STB_THREADS +extern void __stdcall Sleep(unsigned long); + +void * thread_1(void *x) +{ + Sleep(80); + printf("thread 1\n"); fflush(stdout); + return (void *) 2; +} + +void * thread_2(void *y) +{ + stb_work(thread_1, NULL, y); + Sleep(50); + printf("thread 2\n"); fflush(stdout); + return (void *) 3; +} + +stb_semaphore stest; +stb_mutex mutex; +volatile int tc1, tc2; + +void *thread_3(void *p) +{ + stb_mutex_begin(mutex); + ++tc1; + stb_mutex_end(mutex); + stb_sem_waitfor(stest); + stb_mutex_begin(mutex); + ++tc2; + stb_mutex_end(mutex); + return NULL; +} + +void test_threads(void) +{ + volatile int a=0,b=0; + //stb_work_numthreads(2); + stb_work(thread_2, (void *) &a, (void *) &b); + while (a==0 || b==0) { + Sleep(10); + //printf("a=%d b=%d\n", a, b); + } + c(a==2 && b == 3, "stb_thread"); + stb_work_numthreads(4); + stest = stb_sem_new(8); + mutex = stb_mutex_new(); + stb_work(thread_3, NULL, NULL); + stb_work(thread_3, NULL, NULL); + stb_work(thread_3, NULL, NULL); + stb_work(thread_3, NULL, NULL); + stb_work(thread_3, NULL, NULL); + stb_work(thread_3, NULL, NULL); + stb_work(thread_3, NULL, NULL); + stb_work(thread_3, NULL, NULL); + while (tc1 < 4) + Sleep(10); + c(tc1 == 4, "stb_work 1"); + stb_sem_release(stest); + stb_sem_release(stest); + stb_sem_release(stest); + stb_sem_release(stest); + stb_sem_release(stest); + stb_sem_release(stest); + stb_sem_release(stest); + stb_sem_release(stest); + Sleep(40); + while (tc1 != 8 || tc2 != 8) + Sleep(10); + c(tc1 == 8 && tc2 == 8, "stb_work 2"); + stb_work_numthreads(2); + stb_work(thread_3, NULL, NULL); + stb_work(thread_3, NULL, NULL); + stb_work(thread_3, NULL, NULL); + stb_work(thread_3, NULL, NULL); + while (tc1 < 10) + Sleep(10); + c(tc1 == 10, "stb_work 1"); + stb_sem_release(stest); + stb_sem_release(stest); + stb_sem_release(stest); + stb_sem_release(stest); + + Sleep(100); + stb_sem_delete(stest); + stb_mutex_delete(mutex); +} +#else +void test_threads(void) +{ +} +#endif + +void *thread4(void *p) +{ + return NULL; +} + +#ifdef STB_THREADS +stb_threadqueue *tq; +stb_sync synch; +stb_mutex msum; + +volatile int thread_sum; + +void *consume1(void *p) +{ + volatile int *q = (volatile int *) p; + for(;;) { + int z; + stb_threadq_get_block(tq, &z); + stb_mutex_begin(msum); + thread_sum += z; + *q += z; + stb_mutex_end(msum); + stb_sync_reach(synch); + } +} + +void test_threads2(void) +{ + int array[256],i,n=0; + volatile int which[4]; + synch = stb_sync_new(); + stb_sync_set_target(synch,2); + stb_work_reach(thread4, NULL, NULL, synch); + stb_sync_reach_and_wait(synch); + printf("ok\n"); + + tq = stb_threadq_new(4, 1, TRUE,TRUE); + msum = stb_mutex_new(); + thread_sum = 0; + stb_sync_set_target(synch, 65); + for (i=0; i < 4; ++i) { + which[i] = 0; + stb_create_thread(consume1, (int *) &which[i]); + } + for (i=1; i <= 64; ++i) { + array[i] = i; + n += i; + stb_threadq_add_block(tq, &array[i]); + } + stb_sync_reach_and_wait(synch); + stb_barrier(); + c(thread_sum == n, "stb_threadq 1"); + c(which[0] + which[1] + which[2] + which[3] == n, "stb_threadq 2"); + printf("(Distribution: %d %d %d %d)\n", which[0], which[1], which[2], which[3]); + + stb_sync_delete(synch); + stb_threadq_delete(tq); + stb_mutex_delete(msum); +} +#else +void test_threads2(void) +{ +} +#endif + +char tc[] = "testing compression test quick test voila woohoo what the hell"; + +char storage1[1 << 23]; +int test_compression(char *buffer, int length) +{ + char *storage2; + int c_len = stb_compress(storage1, buffer, length); + int dc_len; + printf("Compressed %d to %d\n", length, c_len); + dc_len = stb_decompress_length(storage1); + storage2 = malloc(dc_len); + dc_len = stb_decompress(storage2, storage1, c_len); + if (dc_len != length) { free(storage2); return -1; } + if (memcmp(buffer, storage2, length) != 0) { free(storage2); return -1; } + free(storage2); + return c_len; +} + +#if 0 +int test_en_compression(char *buffer, int length) +{ + int c_len = stb_en_compress(storage1, buffer, length); + int dc_len; + printf("Encompressed %d to %d\n", length, c_len); + dc_len = stb_en_decompress(storage2, storage1, c_len); + if (dc_len != length) return -1; + if (memcmp(buffer, storage2, length) != 0) return -1; + return c_len; +} +#endif + +#define STR_x "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxx" +#define STR_y "yyyyyyyyyyyyyyyyyy" + +#define STR_xy STR_x STR_y +#define STR_xyyxy STR_xy STR_y STR_xy + +#define STR_1 "testing" +#define STR_2 STR_xyyxy STR_xy STR_xyyxy STR_xyyxy STR_xy STR_xyyxy +#define STR_3 "buh" + +char buffer[] = STR_1 "\r\n" STR_2 STR_2 STR_2 "\n" STR_3; +char str1[] = STR_1; +char str2[] = STR_2 STR_2 STR_2; +char str3[] = STR_3; + +int sum(short *s) +{ + int i,total=0; + for (i=0; i < stb_arr_len(s); ++i) + total += s[i]; + return total; +} + +stb_uint stb_adler32_old(stb_uint adler32, stb_uchar *buffer, stb_uint buflen) +{ + const stb_uint ADLER_MOD = 65521; + stb_uint s1 = adler32 & 0xffff; + stb_uint s2 = adler32 >> 16; + + while (buflen-- > 0) { // NOTE: much faster implementations are possible! + s1 += *buffer++; if (s1 > ADLER_MOD) s1 -= ADLER_MOD; + s2 += s1 ; if (s2 > ADLER_MOD) s2 -= ADLER_MOD; + } + return (s2 << 16) + s1; +} + +static int sample_test[3][5] = +{ + { 1,2,3,4,5 }, + { 6,7,8,9,10, }, + { 11,12,13,14,15 }, +}; + +typedef struct { unsigned short x,y,z; } struct1; +typedef struct { double a; int x,y,z; } struct2; + +char *args_raw[] = { "foo", "-dxrf", "bar", "-ts" }; +char *args[8]; + +void do_compressor(int,char**); +void test_sha1(void); + +int alloc_num, alloc_size; +void dumpfunc(void *ptr, int sz, char *file, int line) +{ + printf("%p (%6d) -- %3d:%s\n", ptr, sz, line, file); + alloc_size += sz; + alloc_num += 1; +} + +char *expects(stb_matcher *m, char *s, int result, int len, char *str) +{ + int res2,len2=0; + res2 = stb_lex(m, s, &len2); + c(result == res2 && len == len2, str); + return s + len; +} + +void test_lex(void) +{ + stb_matcher *m = stb_lex_matcher(); + // tok_en5 .3 20.1 20. .20 .1 + char *s = "tok_en5.3 20.1 20. .20.1"; + + stb_lex_item(m, "[a-zA-Z_][a-zA-Z0-9_]*", 1 ); + stb_lex_item(m, "[0-9]*\\.?[0-9]*" , 2 ); + stb_lex_item(m, "[\r\n\t ]+" , 3 ); + stb_lex_item(m, "." , -99 ); + s=expects(m,s,1,7, "stb_lex 1"); + s=expects(m,s,2,2, "stb_lex 2"); + s=expects(m,s,3,1, "stb_lex 3"); + s=expects(m,s,2,4, "stb_lex 4"); + s=expects(m,s,3,1, "stb_lex 5"); + s=expects(m,s,2,3, "stb_lex 6"); + s=expects(m,s,3,1, "stb_lex 7"); + s=expects(m,s,2,3, "stb_lex 8"); + s=expects(m,s,2,2, "stb_lex 9"); + s=expects(m,s,0,0, "stb_lex 10"); + stb_matcher_free(m); +} + +typedef struct Btest +{ + struct Btest stb_bst_fields(btest_); + int v; +} Btest; + +stb_bst(Btest, btest_, BT2,bt2,v, int, a - b) + +void bst_test(void) +{ + Btest *root = NULL, *t; + int items[500], sorted[500]; + int i,j,z; + for (z=0; z < 10; ++z) { + for (i=0; i < 500; ++i) + items[i] = stb_rand() & 0xfffffff; + + // check for collisions, and retrry if so + memcpy(sorted, items, sizeof(sorted)); + qsort(sorted, 500, sizeof(sorted[0]), stb_intcmp(0)); + for (i=1; i < 500; ++i) + if (sorted[i-1] == sorted[i]) + break; + if (i != 500) { --z; break; } + + for (i=0; i < 500; ++i) { + t = malloc(sizeof(*t)); + t->v = items[i]; + root = btest_insert(root, t); + #ifdef STB_DEBUG + btest__validate(root,1); + #endif + for (j=0; j <= i; ++j) + c(btest_find(root, items[j]) != NULL, "stb_bst 1"); + for ( ; j < 500; ++j) + c(btest_find(root, items[j]) == NULL, "stb_bst 2"); + } + + t = btest_first(root); + for (i=0; i < 500; ++i) + t = btest_next(root,t); + c(t == NULL, "stb_bst 5"); + t = btest_last(root); + for (i=0; i < 500; ++i) + t = btest_prev(root,t); + c(t == NULL, "stb_bst 6"); + + memcpy(sorted, items, sizeof(sorted)); + qsort(sorted, 500, sizeof(sorted[0]), stb_intcmp(0)); + t = btest_first(root); + for (i=0; i < 500; ++i) { + assert(t->v == sorted[i]); + t = btest_next(root, t); + } + assert(t == NULL); + + if (z==1) + stb_reverse(items, 500, sizeof(items[0])); + else if (z) + stb_shuffle(items, 500, sizeof(items[0]), stb_rand()); + + for (i=0; i < 500; ++i) { + t = btest_find(root, items[i]); + assert(t != NULL); + root = btest_remove(root, t); + c(btest_find(root, items[i]) == NULL, "stb_bst 5"); + #ifdef STB_DEBUG + btest__validate(root, 1); + #endif + for (j=0; j <= i; ++j) + c(btest_find(root, items[j]) == NULL, "stb_bst 3"); + for ( ; j < 500; ++j) + c(btest_find(root, items[j]) != NULL, "stb_bst 4"); + free(t); + } + } +} + +extern void stu_uninit(void); + +stb_define_sort(sort_int, int, *a < *b) + +stb_rand_define(prime_rand, 1) +void test_packed_floats(void); +void test_parser_generator(void); + +void rec_print(stb_dirtree2 *d, int depth) +{ + int i; + for (i=0; i < depth; ++i) printf(" "); + printf("%s (%d)\n", d->relpath, stb_arr_len(d->files)); + for (i=0; i < stb_arr_len(d->subdirs); ++i) + rec_print(d->subdirs[i], depth+1); + d->weight = (float) stb_arr_len(d->files); +} + +#ifdef MAIN_TEST +int main(int argc, char **argv) +{ + char *z; + stb__wchar buffer7[1024],buffer9[1024]; + char buffer8[4096]; + FILE *f; + char *p1 = "foo/bar\\baz/test.xyz"; + char *p2 = "foo/.bar"; + char *p3 = "foo.bar"; + char *p4 = "foo/bar"; + char *wildcards[] = { "*foo*", "*bar", "baz", "*1*2*3*", "*/CVS/repository", "*oof*" }; + char **s; + char buf[256], *p; + int n,len2,*q,i; + stb_matcher *mt=NULL; + + if (argc > 1) { + do_compressor(argc,argv); + return 0; + } + test_classes(); + //show(); + + //stb_malloc_check_counter(2,2); + //_CrtSetBreakAlloc(10398); + + stbprint("Checking {!if} the {$fancy} print function {#works}? - should\n"); + stbprint(" - align\n"); + stbprint("But {#3this}} {one}} - shouldn't\n"); + + #if 0 + { + int i; + char **s = stb_readdir_recursive("/sean", NULL); + stb_dirtree *d = stb_dirtree_from_files_relative("", s, stb_arr_len(s)); + stb_dirtree **e; + rec_print(d, 0); + e = stb_summarize_tree(d,12,4); + for (i=0; i < stb_arr_len(e); ++i) { + printf("%s\n", e[i]->fullpath); + } + stb_arr_free(e); + + stb_fatal("foo"); + } + #endif + + stb_("Started stb.c"); + test_threads2(); + test_threads(); + + for (i=0; i < 1023 && 5+77*i < 0xd800; ++i) + buffer7[i] = 5+77*i; + buffer7[i++] = 0xd801; + buffer7[i++] = 0xdc02; + buffer7[i++] = 0xdbff; + buffer7[i++] = 0xdfff; + buffer7[i] = 0; + p = stb_to_utf8(buffer8, buffer7, sizeof(buffer8)); + c(p != NULL, "stb_to_utf8"); + if (p != NULL) { + stb_from_utf8(buffer9, buffer8, sizeof(buffer9)/2); + c(!memcmp(buffer7, buffer9, i*2), "stb_from_utf8"); + } + + z = "foo.*[bd]ak?r"; + c( stb_regex(z, "muggle man food is barfy") == 1, "stb_regex 1"); + c( stb_regex("foo.*bar", "muggle man food is farfy") == 0, "stb_regex 2"); + c( stb_regex("[^a-zA-Z]foo[^a-zA-Z]", "dfoobar xfood") == 0, "stb_regex 3"); + c( stb_regex(z, "muman foob is bakrfy") == 1, "stb_regex 4"); + z = "foo.*[bd]bk?r"; + c( stb_regex(z, "muman foob is bakrfy") == 0, "stb_regex 5"); + c( stb_regex(z, "muman foob is bbkrfy") == 1, "stb_regex 6"); + + stb_regex(NULL,NULL); + + #if 0 + test_parser_generator(); + stb_wrapper_listall(dumpfunc); + if (alloc_num) + printf("Memory still in use: %d allocations of %d bytes.\n", alloc_num, alloc_size); + #endif + + test_script(); + p = stb_file("sieve.stua", NULL); + if (p) { + stua_run_script(p); + free(p); + } + stua_uninit(); + + //stb_wrapper_listall(dumpfunc); + printf("Memory still in use: %d allocations of %d bytes.\n", alloc_num, alloc_size); + + c(stb_alloc_count_alloc == stb_alloc_count_free, "stb_alloc 0"); + + bst_test(); + + c(stb_alloc_count_alloc == stb_alloc_count_free, "stb_alloc 0"); + +#if 0 + // stb_block + { + int inuse=0, freespace=0; + int *x = malloc(10000*sizeof(*x)); + stb_block *b = stb_block_new(1, 10000); + #define BLOCK_COUNT 1000 + int *p = malloc(sizeof(*p) * BLOCK_COUNT); + int *l = malloc(sizeof(*l) * BLOCK_COUNT); + int i, n, k = 0; + + memset(x, 0, 10000 * sizeof(*x)); + + n = 0; + while (n < BLOCK_COUNT && k < 1000) { + l[n] = 16 + (rand() & 31); + p[n] = stb_block_alloc(b, l[n], 0); + if (p[n] == 0) + break; + inuse += l[n]; + + freespace = 0; + for (i=0; i < b->len; ++i) + freespace += b->freelist[i].len; + assert(freespace + inuse == 9999); + + for (i=0; i < l[n]; ++i) + x[ p[n]+i ] = p[n]; + ++n; + + if (k > 20) { + int sz; + i = (stb_rand() % n); + sz = l[i]; + stb_block_free(b, p[i], sz); + inuse -= sz; + p[i] = p[n-1]; + l[i] = l[n-1]; + --n; + + freespace = 0; + for (i=0; i < b->len; ++i) + freespace += b->freelist[i].len; + assert(freespace + inuse == 9999); + } + + + ++k; + + // validate + if ((k % 50) == 0) { + int j; + for (j=0; j < n; ++j) { + for (i=0; i < l[j]; ++i) + assert(x[ p[j]+i ] == p[j]); + } + } + + if ((k % 200) == 0) { + stb_block_compact_freelist(b); + } + } + + for (i=0; i < n; ++i) + stb_block_free(b, p[i], l[i]); + + stb_block_destroy(b); + free(p); + free(l); + free(x); + } + + blockfile_test(); +#endif + + mt = stb_lex_matcher(); + for (i=0; i < 5; ++i) + stb_lex_item_wild(mt, wildcards[i], i+1); + + c(1==stb_lex(mt, "this is a foo in the middle",NULL), "stb_matcher_match 1"); + c(0==stb_lex(mt, "this is a bar in the middle",NULL), "stb_matcher_match 2"); + c(0==stb_lex(mt, "this is a baz in the middle",NULL), "stb_matcher_match 3"); + c(2==stb_lex(mt, "this is a bar",NULL), "stb_matcher_match 4"); + c(0==stb_lex(mt, "this is a baz",NULL), "stb_matcher_match 5"); + c(3==stb_lex(mt, "baz",NULL), "stb_matcher_match 6"); + c(4==stb_lex(mt, "1_2_3_4",NULL), "stb_matcher_match 7"); + c(0==stb_lex(mt, "1 3 3 3 3 2 ",NULL), "stb_matcher_match 8"); + c(4==stb_lex(mt, "1 3 3 3 2 3 ",NULL), "stb_matcher_match 9"); + c(5==stb_lex(mt, "C:/sean/prj/old/gdmag/mipmap/hqp/adol-c/CVS/Repository",NULL), "stb_matcher_match 10"); + stb_matcher_free(mt); + + { + #define SSIZE 500000 + static int arr[SSIZE],arr2[SSIZE]; + int i,good; + for (i=0; i < SSIZE; ++i) + arr2[i] = stb_rand(); + memcpy(arr,arr2,sizeof(arr)); + printf("stb_define_sort:\n"); + sort_int(arr, SSIZE); + good = 1; + for (i=0; i+1 < SSIZE; ++i) + if (arr[i] > arr[i+1]) + good = 0; + c(good, "stb_define_sort"); + printf("qsort:\n"); + qsort(arr2, SSIZE, sizeof(arr2[0]), stb_intcmp(0)); + printf("done\n"); + // check for bugs + memset(arr, 0, sizeof(arr[0]) * 1000); + sort_int(arr, 1000); + } + + + c(stb_alloc_count_alloc == stb_alloc_count_free, "stb_alloc -2"); + + c( stb_is_prime( 2), "stb_is_prime 1"); + c( stb_is_prime( 3), "stb_is_prime 2"); + c( stb_is_prime( 5), "stb_is_prime 3"); + c( stb_is_prime( 7), "stb_is_prime 4"); + c(!stb_is_prime( 9), "stb_is_prime 5"); + c( stb_is_prime(11), "stb_is_prime 6"); + c(!stb_is_prime(25), "stb_is_prime 7"); + c(!stb_is_prime(27), "stb_is_prime 8"); + c( stb_is_prime(29), "stb_is_prime 9"); + c( stb_is_prime(31), "stb_is_prime a"); + c(!stb_is_prime(33), "stb_is_prime b"); + c(!stb_is_prime(35), "stb_is_prime c"); + c(!stb_is_prime(36), "stb_is_prime d"); + + for (n=7; n < 64; n += 3) { + int i; + stb_perfect s; + unsigned int *p = malloc(n * sizeof(*p)); + for (i=0; i < n; ++i) + p[i] = i*i; + c(stb_perfect_create(&s, p, n), "stb_perfect_hash 1"); + stb_perfect_destroy(&s); + for (i=0; i < n; ++i) + p[i] = stb_rand(); + c(stb_perfect_create(&s, p, n), "stb_perfect_hash 2"); + stb_perfect_destroy(&s); + for (i=0; i < n; ++i) + p[i] = (0x80000000 >> stb_log2_ceil(n>>1)) * i; + c(stb_perfect_create(&s, p, n), "stb_perfect_hash 2"); + stb_perfect_destroy(&s); + for (i=0; i < n; ++i) + p[i] = (int) malloc(1024); + c(stb_perfect_create(&s, p, n), "stb_perfect_hash 3"); + stb_perfect_destroy(&s); + for (i=0; i < n; ++i) + free((void *) p[i]); + free(p); + } + printf("Maximum attempts required to find perfect hash: %d\n", + stb_perfect_hash_max_failures); + + p = "abcdefghijklmnopqrstuvwxyz"; + c(stb_ischar('c', p), "stb_ischar 1"); + c(stb_ischar('x', p), "stb_ischar 2"); + c(!stb_ischar('#', p), "stb_ischar 3"); + c(!stb_ischar('X', p), "stb_ischar 4"); + p = "0123456789"; + c(!stb_ischar('c', p), "stb_ischar 5"); + c(!stb_ischar('x', p), "stb_ischar 6"); + c(!stb_ischar('#', p), "stb_ischar 7"); + c(!stb_ischar('X', p), "stb_ischar 8"); + p = "#####"; + c(!stb_ischar('c', p), "stb_ischar a"); + c(!stb_ischar('x', p), "stb_ischar b"); + c(stb_ischar('#', p), "stb_ischar c"); + c(!stb_ischar('X', p), "stb_ischar d"); + p = "xXyY"; + c(!stb_ischar('c', p), "stb_ischar e"); + c(stb_ischar('x', p), "stb_ischar f"); + c(!stb_ischar('#', p), "stb_ischar g"); + c(stb_ischar('X', p), "stb_ischar h"); + + c(stb_alloc_count_alloc == stb_alloc_count_free, "stb_alloc 1"); + + q = stb_wordwrapalloc(15, "How now brown cow. Testinglishously. Okey dokey"); + // How now brown + // cow. Testinglis + // hously. Okey + // dokey + c(stb_arr_len(q) == 8, "stb_wordwrap 8"); + c(q[2] == 14 && q[3] == 15, "stb_wordwrap 9"); + c(q[4] == 29 && q[5] == 12, "stb_wordwrap 10"); + stb_arr_free(q); + + q = stb_wordwrapalloc(20, "How now brown cow. Testinglishously. Okey dokey"); + // How now brown cow. + // Testinglishously. + // Okey dokey + c(stb_arr_len(q) == 6, "stb_wordwrap 1"); + c(q[0] == 0 && q[1] == 18, "stb_wordwrap 2"); + c(q[2] == 19 && q[3] == 17, "stb_wordwrap 3"); + c(q[4] == 37 && q[5] == 10, "stb_wordwrap 4"); + stb_arr_free(q); + + q = stb_wordwrapalloc(12, "How now brown cow. Testinglishously. Okey dokey"); + // How now + // brown cow. + // Testinglisho + // usly. Okey + // dokey + c(stb_arr_len(q) == 10, "stb_wordwrap 5"); + c(q[4] == 19 && q[5] == 12, "stb_wordwrap 6"); + c(q[6] == 31 && q[3] == 10, "stb_wordwrap 7"); + stb_arr_free(q); + + //test_script(); + + //test_packed_floats(); + + c(stb_alloc_count_alloc == stb_alloc_count_free, "stb_alloc 0"); + if (stb_alloc_count_alloc != stb_alloc_count_free) { + printf("%d allocs, %d frees\n", stb_alloc_count_alloc, stb_alloc_count_free); + } + test_lex(); + + mt = stb_regex_matcher(".*foo.*bar.*"); + c(stb_matcher_match(mt, "foobarx") == 1, "stb_matcher_match 1"); + c(stb_matcher_match(mt, "foobar") == 1, "stb_matcher_match 2"); + c(stb_matcher_match(mt, "foo bar") == 1, "stb_matcher_match 3"); + c(stb_matcher_match(mt, "fo foo ba ba bar ba") == 1, "stb_matcher_match 4"); + c(stb_matcher_match(mt, "fo oo oo ba ba bar foo") == 0, "stb_matcher_match 5"); + stb_free(mt); + + mt = stb_regex_matcher(".*foo.?bar.*"); + c(stb_matcher_match(mt, "abfoobarx") == 1, "stb_matcher_match 6"); + c(stb_matcher_match(mt, "abfoobar") == 1, "stb_matcher_match 7"); + c(stb_matcher_match(mt, "abfoo bar") == 1, "stb_matcher_match 8"); + c(stb_matcher_match(mt, "abfoo bar") == 0, "stb_matcher_match 9"); + c(stb_matcher_match(mt, "abfo foo ba ba bar ba") == 0, "stb_matcher_match 10"); + c(stb_matcher_match(mt, "abfo oo oo ba ba bar foo") == 0, "stb_matcher_match 11"); + stb_free(mt); + + mt = stb_regex_matcher(".*m((foo|bar)*baz)m.*"); + c(stb_matcher_match(mt, "abfoobarx") == 0, "stb_matcher_match 12"); + c(stb_matcher_match(mt, "a mfoofoofoobazm d") == 1, "stb_matcher_match 13"); + c(stb_matcher_match(mt, "a mfoobarbazfoom d") == 0, "stb_matcher_match 14"); + c(stb_matcher_match(mt, "a mbarbarfoobarbazm d") == 1, "stb_matcher_match 15"); + c(stb_matcher_match(mt, "a mfoobarfoo bazm d") == 0, "stb_matcher_match 16"); + c(stb_matcher_match(mt, "a mm foobarfoobarfoobar ") == 0, "stb_matcher_match 17"); + stb_free(mt); + + mt = stb_regex_matcher("f*|z"); + c(stb_matcher_match(mt, "fz") == 0, "stb_matcher_match 0a"); + c(stb_matcher_match(mt, "ff") == 1, "stb_matcher_match 0b"); + c(stb_matcher_match(mt, "z") == 1, "stb_matcher_match 0c"); + stb_free(mt); + + mt = stb_regex_matcher("m(f|z*)n"); + c(stb_matcher_match(mt, "mfzn") == 0, "stb_matcher_match 0d"); + c(stb_matcher_match(mt, "mffn") == 0, "stb_matcher_match 0e"); + c(stb_matcher_match(mt, "mzn") == 1, "stb_matcher_match 0f"); + c(stb_matcher_match(mt, "mn") == 1, "stb_matcher_match 0g"); + c(stb_matcher_match(mt, "mzfn") == 0, "stb_matcher_match 0f"); + + c(stb_matcher_find(mt, "manmanmannnnnnnmmmmmmmmm ") == 0, "stb_matcher_find 1"); + c(stb_matcher_find(mt, "manmanmannnnnnnmmmmmmmmm ") == 0, "stb_matcher_find 2"); + c(stb_matcher_find(mt, "manmanmannnnnnnmmmmmmmmmffzzz ") == 0, "stb_matcher_find 3"); + c(stb_matcher_find(mt, "manmanmannnnnnnmmmmmmmmmnfzzz ") == 1, "stb_matcher_find 4"); + c(stb_matcher_find(mt, "mmmfn aanmannnnnnnmmmmmm fzzz ") == 1, "stb_matcher_find 5"); + c(stb_matcher_find(mt, "mmmzzn anmannnnnnnmmmmmm fzzz ") == 1, "stb_matcher_find 6"); + c(stb_matcher_find(mt, "mm anmannnnnnnmmmmmm fzmzznzz ") == 1, "stb_matcher_find 7"); + c(stb_matcher_find(mt, "mm anmannnnnnnmmmmmm fzmzzfnzz ") == 0, "stb_matcher_find 8"); + c(stb_matcher_find(mt, "manmfnmannnnnnnmmmmmmmmmffzzz ") == 1, "stb_matcher_find 9"); + stb_free(mt); + + mt = stb_regex_matcher(".*m((foo|bar)*|baz)m.*"); + c(stb_matcher_match(mt, "abfoobarx") == 0, "stb_matcher_match 18"); + c(stb_matcher_match(mt, "a mfoofoofoobazm d") == 0, "stb_matcher_match 19"); + c(stb_matcher_match(mt, "a mfoobarbazfoom d") == 0, "stb_matcher_match 20"); + c(stb_matcher_match(mt, "a mbazm d") == 1, "stb_matcher_match 21"); + c(stb_matcher_match(mt, "a mfoobarfoom d") == 1, "stb_matcher_match 22"); + c(stb_matcher_match(mt, "a mm foobarfoobarfoobar ") == 1, "stb_matcher_match 23"); + stb_free(mt); + + mt = stb_regex_matcher("[a-fA-F]..[^]a-zA-Z]"); + c(stb_matcher_match(mt, "Axx1") == 1, "stb_matcher_match 24"); + c(stb_matcher_match(mt, "Fxx1") == 1, "stb_matcher_match 25"); + c(stb_matcher_match(mt, "Bxx]") == 0, "stb_matcher_match 26"); + c(stb_matcher_match(mt, "Cxxz") == 0, "stb_matcher_match 27"); + c(stb_matcher_match(mt, "gxx[") == 0, "stb_matcher_match 28"); + c(stb_matcher_match(mt, "-xx0") == 0, "stb_matcher_match 29"); + stb_free(mt); + + c(stb_wildmatch("foo*bar", "foobarx") == 0, "stb_wildmatch 0a"); + c(stb_wildmatch("foo*bar", "foobar") == 1, "stb_wildmatch 1a"); + c(stb_wildmatch("foo*bar", "foo bar") == 1, "stb_wildmatch 2a"); + c(stb_wildmatch("foo*bar", "fo foo ba ba bar ba") == 0, "stb_wildmatch 3a"); + c(stb_wildmatch("foo*bar", "fo oo oo ba ba ar foo") == 0, "stb_wildmatch 4a"); + + c(stb_wildmatch("*foo*bar*", "foobar") == 1, "stb_wildmatch 1b"); + c(stb_wildmatch("*foo*bar*", "foo bar") == 1, "stb_wildmatch 2b"); + c(stb_wildmatch("*foo*bar*", "fo foo ba ba bar ba") == 1, "stb_wildmatch 3b"); + c(stb_wildmatch("*foo*bar*", "fo oo oo ba ba ar foo") == 0, "stb_wildmatch 4b"); + + c(stb_wildmatch("foo*bar*", "foobarx") == 1, "stb_wildmatch 1c"); + c(stb_wildmatch("foo*bar*", "foobabar") == 1, "stb_wildmatch 2c"); + c(stb_wildmatch("foo*bar*", "fo foo ba ba bar ba") == 0, "stb_wildmatch 3c"); + c(stb_wildmatch("foo*bar*", "fo oo oo ba ba ar foo") == 0, "stb_wildmatch 4c"); + + c(stb_wildmatch("*foo*bar", "foobar") == 1, "stb_wildmatch 1d"); + c(stb_wildmatch("*foo*bar", "foo bar") == 1, "stb_wildmatch 2d"); + c(stb_wildmatch("*foo*bar", "fo foo ba ba bar ba") == 0, "stb_wildmatch 3d"); + c(stb_wildmatch("*foo*bar", "fo oo oo ba ba ar foo") == 0, "stb_wildmatch 4d"); + + c(stb_wildfind("foo*bar", "xyfoobarx") == 2, "stb_wildfind 0a"); + c(stb_wildfind("foo*bar", "aaafoobar") == 3, "stb_wildfind 1a"); + c(stb_wildfind("foo*bar", "foo bar") == 0, "stb_wildfind 2a"); + c(stb_wildfind("foo*bar", "fo foo ba ba bar ba") == 3, "stb_wildfind 3a"); + c(stb_wildfind("foo*bar", "fo oo oo ba ba ar foo") == -1, "stb_wildfind 4a"); + + c(stb_wildmatch("*foo*;*bar*", "foobar") == 1, "stb_wildmatch 1e"); + c(stb_wildmatch("*foo*;*bar*", "afooa") == 1, "stb_wildmatch 2e"); + c(stb_wildmatch("*foo*;*bar*", "abara") == 1, "stb_wildmatch 3e"); + c(stb_wildmatch("*foo*;*bar*", "abaza") == 0, "stb_wildmatch 4e"); + c(stb_wildmatch("*foo*;*bar*", "foboar") == 0, "stb_wildmatch 5e"); + + test_sha1(); + + n = sizeof(args_raw)/sizeof(args_raw[0]); + memcpy(args, args_raw, sizeof(args_raw)); + s = stb_getopt(&n, args); + c(n >= 1 && !strcmp(args[1], "bar" ), "stb_getopt 1"); + c(stb_arr_len(s) >= 2 && !strcmp(s[2] , "r" ), "stb_getopt 2"); + stb_getopt_free(s); + + n = sizeof(args_raw)/sizeof(args_raw[0]); + memcpy(args, args_raw, sizeof(args_raw)); + s = stb_getopt_param(&n, args, "f"); + c(stb_arr_len(s) >= 3 && !strcmp(s[3] , "fbar"), "stb_getopt 3"); + stb_getopt_free(s); + + n = sizeof(args_raw)/sizeof(args_raw[0]); + memcpy(args, args_raw, sizeof(args_raw)); + s = stb_getopt_param(&n, args, "x"); + c(stb_arr_len(s) >= 2 && !strcmp(s[1] , "xrf" ), "stb_getopt 4"); + stb_getopt_free(s); + + n = sizeof(args_raw)/sizeof(args_raw[0]); + memcpy(args, args_raw, sizeof(args_raw)); + s = stb_getopt_param(&n, args, "s"); + c(s == NULL && n == 0 , "stb_getopt 5"); + stb_getopt_free(s); + +#if 0 + c(*stb_csample_int(sample_test[0], 1, 5, 5, 3, -1, -1) == 1, "stb_csample_int 1"); + c(*stb_csample_int(sample_test[0], 1, 5, 5, 3, 1, -3) == 2, "stb_csample_int 2"); + c(*stb_csample_int(sample_test[0], 1, 5, 5, 3, 12, -2) == 5, "stb_csample_int 3"); + c(*stb_csample_int(sample_test[0], 1, 5, 5, 3, 15, 1) == 10, "stb_csample_int 4"); + c(*stb_csample_int(sample_test[0], 1, 5, 5, 3, 5, 4) == 15, "stb_csample_int 5"); + c(*stb_csample_int(sample_test[0], 1, 5, 5, 3, 3, 3) == 14, "stb_csample_int 6"); + c(*stb_csample_int(sample_test[0], 1, 5, 5, 3, -2, 5) == 11, "stb_csample_int 7"); + c(*stb_csample_int(sample_test[0], 1, 5, 5, 3, -7, 0) == 1, "stb_csample_int 8"); + c(*stb_csample_int(sample_test[0], 1, 5, 5, 3, 2, 1) == 8, "stb_csample_int 9"); +#endif + + c(!strcmp(stb_splitpath(buf, p1, STB_PATH ), "foo/bar\\baz/"), "stb_splitpath 1"); + c(!strcmp(stb_splitpath(buf, p1, STB_FILE ), "test"), "stb_splitpath 2"); + c(!strcmp(stb_splitpath(buf, p1, STB_EXT ), ".xyz"), "stb_splitpath 3"); + c(!strcmp(stb_splitpath(buf, p1, STB_PATH_FILE ), "foo/bar\\baz/test"), "stb_splitpath 4"); + c(!strcmp(stb_splitpath(buf, p1, STB_FILE_EXT ), "test.xyz"), "stb_splitpath 5"); + + c(!strcmp(stb_splitpath(buf, p2, STB_PATH ), "foo/"), "stb_splitpath 6"); + c(!strcmp(stb_splitpath(buf, p2, STB_FILE ), ""), "stb_splitpath 7"); + c(!strcmp(stb_splitpath(buf, p2, STB_EXT ), ".bar"), "stb_splitpath 8"); + c(!strcmp(stb_splitpath(buf, p2, STB_PATH_FILE ), "foo/"), "stb_splitpath 9"); + c(!strcmp(stb_splitpath(buf, p2, STB_FILE_EXT ), ".bar"), "stb_splitpath 10"); + + c(!strcmp(stb_splitpath(buf, p3, STB_PATH ), "./"), "stb_splitpath 11"); + c(!strcmp(stb_splitpath(buf, p3, STB_FILE ), "foo"), "stb_splitpath 12"); + c(!strcmp(stb_splitpath(buf, p3, STB_EXT ), ".bar"), "stb_splitpath 13"); + c(!strcmp(stb_splitpath(buf, p3, STB_PATH_FILE ), "foo"), "stb_splitpath 14"); + + c(!strcmp(stb_splitpath(buf, p4, STB_PATH ), "foo/"), "stb_splitpath 16"); + c(!strcmp(stb_splitpath(buf, p4, STB_FILE ), "bar"), "stb_splitpath 17"); + c(!strcmp(stb_splitpath(buf, p4, STB_EXT ), ""), "stb_splitpath 18"); + c(!strcmp(stb_splitpath(buf, p4, STB_PATH_FILE ), "foo/bar"), "stb_splitpath 19"); + c(!strcmp(stb_splitpath(buf, p4, STB_FILE_EXT ), "bar"), "stb_splitpath 20"); + + c(!strcmp(p=stb_dupreplace("testfootffooo foo fox", "foo", "brap"), "testbraptfbrapo brap fox"), "stb_dupreplace 1"); free(p); + c(!strcmp(p=stb_dupreplace("testfootffooo foo fox", "foo", "" ), "testtfo fox" ), "stb_dupreplace 2"); free(p); + c(!strcmp(p=stb_dupreplace("abacab", "a", "aba"), "abababacabab" ), "stb_dupreplace 3"); free(p); + + +#if 0 + m = stb_mml_parse("xy<&f>"); + c(m != NULL, "stb_mml_parse 1"); + if (m) { + c(!strcmp(m->child[0]->child[0]->child[1]->tag, "d"), "stb_mml_parse 2"); + c(!strcmp(m->child[0]->child[1]->leaf_data, "<&f>"), "stb_mml_parse 3"); + } + if (m) + stb_mml_free(m); + c(stb_alloc_count_alloc == stb_alloc_count_free, "stb_alloc 1"); + if (stb_alloc_count_alloc != stb_alloc_count_free) { + printf("%d allocs, %d frees\n", stb_alloc_count_alloc, stb_alloc_count_free); + } +#endif + + c(stb_linear_remap(3.0f,0,8,1,2) == 1.375, "stb_linear_remap()"); + + c(stb_bitreverse(0x1248fec8) == 0x137f1248, "stb_bitreverse() 1"); + c(stb_bitreverse8(0x4e) == 0x72, "stb_bitreverse8() 1"); + c(stb_bitreverse8(0x31) == 0x8c, "stb_bitreverse8() 2"); + for (n=1; n < 255; ++n) { + unsigned int m = stb_bitreverse8((uint8) n); + c(stb_bitreverse8((uint8) m) == (unsigned int) n, "stb_bitreverse8() 3"); + } + + for (n=2; n <= 31; ++n) { + c(stb_is_pow2 ((1 << n) ) == 1 , "stb_is_pow2() 1"); + c(stb_is_pow2 ((1 << n)+1) == 0 , "stb_is_pow2() 2"); + c(stb_is_pow2 ((1 << n)-1) == 0 , "stb_is_pow2() 3"); + + c(stb_log2_floor((1 << n) ) == n , "stb_log2_floor() 1"); + c(stb_log2_floor((1 << n)+1) == n , "stb_log2_floor() 2"); + c(stb_log2_floor((1 << n)-1) == n-1, "stb_log2_floor() 3"); + + c(stb_log2_ceil ((1 << n) ) == n , "stb_log2_ceil() 1"); + c(stb_log2_ceil ((1 << n)+1) == n+1, "stb_log2_ceil() 2"); + c(stb_log2_ceil ((1 << n)-1) == n , "stb_log2_ceil() 3"); + + c(stb_bitreverse(1 << n) == 1U << (31-n), "stb_bitreverse() 2"); + } + + c(stb_log2_floor(0) == -1, "stb_log2_floor() 4"); + c(stb_log2_ceil (0) == -1, "stb_log2_ceil () 4"); + + c(stb_log2_floor(-1) == 31, "stb_log2_floor() 5"); + c(stb_log2_ceil (-1) == 32, "stb_log2_ceil () 5"); + + c(stb_bitcount(0xffffffff) == 32, "stb_bitcount() 1"); + c(stb_bitcount(0xaaaaaaaa) == 16, "stb_bitcount() 2"); + c(stb_bitcount(0x55555555) == 16, "stb_bitcount() 3"); + c(stb_bitcount(0x00000000) == 0, "stb_bitcount() 4"); + + c(stb_lowbit8(0xf0) == 4, "stb_lowbit8 1"); + c(stb_lowbit8(0x10) == 4, "stb_lowbit8 2"); + c(stb_lowbit8(0xf3) == 0, "stb_lowbit8 3"); + c(stb_lowbit8(0xf8) == 3, "stb_lowbit8 4"); + c(stb_lowbit8(0x60) == 5, "stb_lowbit8 5"); + + for (n=0; n < sizeof(buf); ++n) + buf[n] = 0; + + for (n = 0; n < 200000; ++n) { + unsigned int k = stb_rand(); + int i,z=0; + for (i=0; i < 32; ++i) + if (k & (1 << i)) ++z; + c(stb_bitcount(k) == z, "stb_bitcount() 5"); + + buf[k >> 24] = 1; + + if (k != 0) { + if (stb_is_pow2(k)) { + c(stb_log2_floor(k) == stb_log2_ceil(k), "stb_is_pow2() 1"); + c(k == 1U << stb_log2_floor(k), "stb_is_pow2() 2"); + } else { + c(stb_log2_floor(k) == stb_log2_ceil(k)-1, "stb_is_pow2() 3"); + } + } + + c(stb_bitreverse(stb_bitreverse(n)) == (uint32) n, "stb_bitreverse() 3"); + } + + // make sure reasonable coverage from stb_rand() + for (n=0; n < sizeof(buf); ++n) + c(buf[n] != 0, "stb_rand()"); + + for (n=0; n < sizeof(buf); ++n) + buf[n] = 0; + + for (n=0; n < 60000; ++n) { + float z = (float) stb_frand(); + int n = (int) (z * sizeof(buf)); + c(z >= 0 && z < 1, "stb_frand() 1"); + c(n >= 0 && n < sizeof(buf), "stb_frand() 2"); + buf[n] = 1; + } + + // make sure reasonable coverage from stb_frand(), + // e.g. that the range remap isn't incorrect + for (n=0; n < sizeof(buf); ++n) + c(buf[n] != 0, "stb_frand()"); + + + // stb_arr + { + short *s = NULL; + + c(sum(s) == 0, "stb_arr 1"); + + stb_arr_add(s); s[0] = 3; + stb_arr_push(s,7); + + c( stb_arr_valid(s,1), "stb_arr 2"); + c(!stb_arr_valid(s,2), "stb_arr 3"); + + // force a realloc + stb_arr_push(s,0); + stb_arr_push(s,0); + stb_arr_push(s,0); + stb_arr_push(s,0); + + c(sum(s) == 10, "stb_arr 4"); + stb_arr_push(s,0); + s[0] = 1; s[1] = 5; s[2] = 20; + c(sum(s) == 26, "stb_arr 5"); + stb_arr_setlen(s,2); + c(sum(s) == 6, "stb_arr 6"); + stb_arr_setlen(s,1); + c(sum(s) == 1, "stb_arr 7"); + stb_arr_setlen(s,0); + c(sum(s) == 0, "stb_arr 8"); + + stb_arr_push(s,3); + stb_arr_push(s,4); + stb_arr_push(s,5); + stb_arr_push(s,6); + stb_arr_push(s,7); + stb_arr_deleten(s,1,3); + c(stb_arr_len(s)==2 && sum(s) == 10, "stb_arr_9"); + + stb_arr_push(s,2); + // 3 7 2 + stb_arr_insertn(s,2,2); + // 3 7 x x 2 + s[2] = 5; + s[3] = 6; + c(s[0]==3 && s[1] == 7 && s[2] == 5 && s[3] == 6 && s[4] == 2, "stb_arr 10"); + stb_arr_free(s); + } + + #if 1 + f= stb_fopen("data/stb.test", "wb"); + fwrite(buffer, 1, sizeof(buffer)-1, f); + stb_fclose(f, stb_keep_yes); + #ifndef WIN32 + sleep(1); // andLinux has some synchronization problem here + #endif + #else + f= fopen("data/stb.test", "wb"); + fwrite(buffer, 1, sizeof(buffer)-1, f); + fclose(f); + #endif + if (!stb_fexists("data/stb.test")) { + fprintf(stderr, "Error: couldn't open file just written, or stb_fexists() is broken.\n"); + } + + f = fopen("data/stb.test", "rb"); + // f = NULL; // test stb_fatal() + if (!f) { stb_fatal("Error: couldn't open file just written\n"); } + else { + char temp[4]; + int len1 = stb_filelen(f), len2; + int n1,n2; + if (fread(temp,1,4,f) == 0) { + int n = ferror(f); + if (n) { stb_fatal("Error reading from stream: %d", n); } + if (feof(f)) stb_fatal("Weird, read 0 bytes and hit eof"); + stb_fatal("Read 0, but neither feof nor ferror is true"); + } + fclose(f); + p = stb_file("data/stb.test", &len2); + if (p == NULL) stb_fatal("Error: stb_file() failed"); + c(len1 == sizeof(buffer)-1, "stb_filelen()"); + c(len2 == sizeof(buffer)-1, "stb_file():n"); + c(memcmp(p, buffer, sizeof(buffer)-1) == 0, "stb_file()"); + c(strcmp(p, buffer)==0, "stb_file() terminated"); + free(p); + + s = stb_stringfile("data/stb.test", &n1); + c(n1 == 3, "stb_stringfile():n"); + n2 = 0; + while (s[n2]) ++n2; + c(n1 == n2, "stb_stringfile():n length matches the non-NULL strings"); + if (n2 == 3) { + c(strcmp(s[0],str1)==0, "stb_stringfile()[0]"); + c(strcmp(s[1],str2)==0, "stb_stringfile()[1]"); + c(strcmp(s[2],str3)==0, "stb_stringfile()[2] (no terminating newlines)"); + } + free(s); + + f = fopen("data/stb.test", "rb"); + stb_fgets(buf, sizeof(buf), f); + //c(strcmp(buf, str1)==0, "stb_fgets()"); + p = stb_fgets_malloc(f); + n1 = strlen(p); + n2 = strlen(str2); + c(strcmp(p, str2)==0, "stb_fgets_malloc()"); + free(p); + stb_fgets(buf, sizeof(buf), f); + c(strcmp(buf, str3)==0, "stb_fgets()3"); + } + + c( stb_prefix("foobar", "foo"), "stb_prefix() 1"); + c(!stb_prefix("foo", "foobar"), "stb_prefix() 2"); + c( stb_prefix("foob", "foob" ), "stb_prefix() 3"); + + stb_strncpy(buf, "foobar", 6); c(strcmp(buf,"fooba" )==0, "stb_strncpy() 1"); + stb_strncpy(buf, "foobar", 8); c(strcmp(buf,"foobar")==0, "stb_strncpy() 2"); + + c(!strcmp(p=stb_duplower("FooBar"), "foobar"), "stb_duplower()"); free(p); + strcpy(buf, "FooBar"); + stb_tolower(buf); + c(!strcmp(buf, "foobar"), "stb_tolower()"); + + p = stb_strtok(buf, "foo=ba*r", "#=*"); + c(!strcmp(buf, "foo" ), "stb_strtok() 1"); + c(!strcmp(p , "ba*r"), "stb_strtok() 2"); + p = stb_strtok(buf, "foobar", "#=*"); + c(*p == 0, "stb_strtok() 3"); + + c(!strcmp(stb_skipwhite(" \t\n foo"), "foo"), "stb_skipwhite()"); + + s = stb_tokens("foo == ba*r", "#=*", NULL); + c(!strcmp(s[0], "foo "), "stb_tokens() 1"); + c(!strcmp(s[1], " ba"), "stb_tokens() 2"); + c(!strcmp(s[2], "r"), "stb_tokens() 3"); + c(s[3] == 0, "stb_tokens() 4"); + free(s); + + s = stb_tokens_allowempty("foo == ba*r", "#=*", NULL); + c(!strcmp(s[0], "foo "), "stb_tokens_allowempty() 1"); + c(!strcmp(s[1], "" ), "stb_tokens_allowempty() 2"); + c(!strcmp(s[2], " ba"), "stb_tokens_allowempty() 3"); + c(!strcmp(s[3], "r"), "stb_tokens_allowempty() 4"); + c(s[4] == 0, "stb_tokens_allowempty() 5"); + free(s); + + s = stb_tokens_stripwhite("foo == ba*r", "#=*", NULL); + c(!strcmp(s[0], "foo"), "stb_tokens_stripwhite() 1"); + c(!strcmp(s[1], "" ), "stb_tokens_stripwhite() 2"); + c(!strcmp(s[2], "ba"), "stb_tokens_stripwhite() 3"); + c(!strcmp(s[3], "r"), "stb_tokens_stripwhite() 4"); + c(s[4] == 0, "stb_tokens_stripwhite() 5"); + free(s); + + s = stb_tokens_quoted("foo =\"=\" ba*\"\"r \" foo\" bah ", "#=*", NULL); + c(!strcmp(s[0], "foo"), "stb_tokens_quoted() 1"); + c(!strcmp(s[1], "= ba"), "stb_tokens_quoted() 2"); + c(!strcmp(s[2], "\"r foo bah"), "stb_tokens_quoted() 3"); + c(s[3] == 0, "stb_tokens_quoted() 4"); + free(s); + + + p = stb_file("stb.h", &len2); + if (p) { + uint32 z = stb_adler32_old(1, p, len2); + uint32 x = stb_adler32 (1, p, len2); + c(z == x, "stb_adler32() 1"); + memset(p,0xff,len2); + z = stb_adler32_old((65520<<16) + 65520, p, len2); + x = stb_adler32 ((65520<<16) + 65520, p, len2); + c(z == x, "stb_adler32() 2"); + free(p); + } + + // stb_hheap + { + #define HHEAP_COUNT 100000 + void **p = malloc(sizeof(*p) * HHEAP_COUNT); + int i, j; + #if 0 + stb_hheap *h2, *h = stb_newhheap(sizeof(struct1),0); + + for (i=0; i < HHEAP_COUNT; ++i) + p[i] = stb_halloc(h); + stb_shuffle(p, HHEAP_COUNT, sizeof(*p), stb_rand()); + for (i=0; i < HHEAP_COUNT; ++i) + stb_hfree(p[i]); + + c(h->num_alloc == 0, "stb_hheap 1"); + stb_delhheap(h); + + h = stb_newhheap(sizeof(struct1),0); + h2 = stb_newhheap(sizeof(struct2),8); + + for (i=0; i < HHEAP_COUNT; ++i) { + if (i & 1) + p[i] = stb_halloc(h); + else { + p[i] = stb_halloc(h2); + c((((int) p[i]) & 4) == 0, "stb_hheap 2"); + } + } + + stb_shuffle(p, HHEAP_COUNT, sizeof(*p), stb_rand()); + for (i=0; i < HHEAP_COUNT; ++i) + stb_hfree(p[i]); + + c(h->num_alloc == 0, "stb_hheap 3"); + c(h2->num_alloc == 0, "stb_hheap 4"); + + stb_delhheap(h); + stb_delhheap(h2); + #else + for (i=0; i < HHEAP_COUNT; ++i) + p[i] = malloc(32); + stb_shuffle(p, HHEAP_COUNT, sizeof(*p), stb_rand()); + for (i=0; i < HHEAP_COUNT; ++i) + free(p[i]); + #endif + + // now use the same array of pointers to do pointer set operations + for (j=100; j < HHEAP_COUNT; j += 25000) { + stb_ps *ps = NULL; + for (i=0; i < j; ++i) + ps = stb_ps_add(ps, p[i]); + + for (i=0; i < HHEAP_COUNT; ++i) + c(stb_ps_find(ps, p[i]) == (i < j), "stb_ps 1"); + c(stb_ps_count(ps) == j, "stb_ps 1b"); + + for (i=j; i < HHEAP_COUNT; ++i) + ps = stb_ps_add(ps, p[i]); + + for (i=0; i < j; ++i) + ps = stb_ps_remove(ps, p[i]); + + for (i=0; i < HHEAP_COUNT; ++i) + c(stb_ps_find(ps, p[i]) == !(i < j), "stb_ps 2"); + + stb_ps_delete(ps); + } + + #define HHEAP_COUNT2 100 + // now use the same array of pointers to do pointer set operations + for (j=1; j < 40; ++j) { + stb_ps *ps = NULL; + for (i=0; i < j; ++i) + ps = stb_ps_add(ps, p[i]); + + for (i=0; i < HHEAP_COUNT2; ++i) + c(stb_ps_find(ps, p[i]) == (i < j), "stb_ps 3"); + c(stb_ps_count(ps) == j, "stb_ps 3b"); + + for (i=j; i < HHEAP_COUNT2; ++i) + ps = stb_ps_add(ps, p[i]); + + for (i=0; i < j; ++i) + ps = stb_ps_remove(ps, p[i]); + + for (i=0; i < HHEAP_COUNT2; ++i) + c(stb_ps_find(ps, p[i]) == !(i < j), "stb_ps 4"); + + stb_ps_delete(ps); + } + + free(p); + } + + + n = test_compression(tc, sizeof(tc)); + c(n >= 0, "stb_compress()/stb_decompress() 1"); + + p = stb_file("stb.h", &len2); + if (p) { + FILE *f = fopen("data/stb_h.z", "wb"); + if (stb_compress_stream_start(f)) { + int i; + void *q; + int len3; + + for (i=0; i < len2; ) { + int n = stb_rand() % 10; + if (n <= 6) n = 1 + stb_rand()%16; + else if (n <= 8) n = 20 + stb_rand() % 1000; + else n = 15000; + if (i + n > len2) n = len2 - i; + stb_write(p + i, n); + i += n; + } + stb_compress_stream_end(1); + + q = stb_decompress_fromfile("data/stb_h.z", &len3); + c(len3 == len2, "stb_compress_stream 2"); + if (len2 == len3) + c(!memcmp(p,q,len2), "stb_compress_stream 3"); + if (q) free(q); + } else { + c(0, "stb_compress_stream 1"); + } + + free(p); + stb_compress_window(65536*4); + } + + p = stb_file("stb.h", &len2); + if (p) { + n = test_compression(p, len2); + c(n >= 0, "stb_compress()/stb_decompress() 2"); + #if 0 + n = test_en_compression(p, len2); + c(n >= 0, "stb_en_compress()/stb_en_decompress() 2"); + #endif + free(p); + } else { + fprintf(stderr, "No stb.h to compression test.\n"); + } + + p = stb_file("data/test.bmp", &len2); + if (p) { + n = test_compression(p, len2); + c(n == 106141, "stb_compress()/stb_decompress() 4"); + #if 0 + n = test_en_compression(p, len2); + c(n >= 0, "stb_en_compress()/stb_en_decompress() 4"); + #endif + free(p); + } + + // the hardcoded compressed lengths being verified _could_ + // change if you changed the compresser parameters; but pure + // performance optimizations shouldn't change them + p = stb_file("data/cantrbry.zip", &len2); + if (p) { + n = test_compression(p, len2); + c(n == 642787, "stb_compress()/stb_decompress() 3"); + #if 0 + n = test_en_compression(p, len2); + c(n >= 0, "stb_en_compress()/stb_en_decompress() 3"); + #endif + free(p); + } + + p = stb_file("data/bible.txt", &len2); + if (p) { + n = test_compression(p, len2); + c(n == 2022520, "stb_compress()/stb_decompress() 4"); + #if 0 + n = test_en_compression(p, len2); + c(n >= 0, "stb_en_compress()/stb_en_decompress() 4"); + #endif + free(p); + } + + { + int len = 1 << 25, o=0; // 32MB + char *buffer = malloc(len); + int i; + for (i=0; i < 8192; ++i) + buffer[o++] = (char) stb_rand(); + for (i=0; i < (1 << 15); ++i) + buffer[o++] = 1; + for (i=0; i < 64; ++i) + buffer[o++] = buffer[i]; + for (i=0; i < (1 << 21); ++i) + buffer[o++] = 2; + for (i=0; i < 64; ++i) + buffer[o++] = buffer[i]; + for (i=0; i < (1 << 21); ++i) + buffer[o++] = 3; + for (i=0; i < 8192; ++i) + buffer[o++] = buffer[i]; + for (i=0; i < (1 << 21); ++i) + buffer[o++] = 4; + assert(o < len); + stb_compress_window(1 << 24); + i = test_compression(buffer, len); + c(n >= 0, "stb_compress() 6"); + free(buffer); + } + + #ifdef STB_THREADS + stb_thread_cleanup(); + #endif + stb_ischar(0,NULL); + stb_wrapper_listall(dumpfunc); + printf("Memory still in use: %d allocations of %d bytes.\n", alloc_num, alloc_size); + + // force some memory checking + for (n=1; n < 20; ++n) + malloc(1 << n); + + printf("Finished stb.c with %d errors.\n", count); + + #ifdef _MSC_VER + if (count) + __asm int 3; + #endif + + return 0; +} + +#endif + + + + + +// NIST test vectors + +struct +{ + int length; + char *message; + char *digest; +} sha1_tests[] = +{ + 24, +"616263", +"a9993e364706816aba3e25717850c26c9cd0d89d", + + 1304, +"ec29561244ede706b6eb30a1c371d74450a105c3f9735f7fa9fe38cf67f304a5736a106e" +"92e17139a6813b1c81a4f3d3fb9546ab4296fa9f722826c066869edacd73b25480351858" +"13e22634a9da44000d95a281ff9f264ecce0a931222162d021cca28db5f3c2aa24945ab1" +"e31cb413ae29810fd794cad5dfaf29ec43cb38d198fe4ae1da2359780221405bd6712a53" +"05da4b1b737fce7cd21c0eb7728d08235a9011", +"970111c4e77bcc88cc20459c02b69b4aa8f58217", + + 2096, +"5fc2c3f6a7e79dc94be526e5166a238899d54927ce470018fbfd668fd9dd97cbf64e2c91" +"584d01da63be3cc9fdff8adfefc3ac728e1e335b9cdc87f069172e323d094b47fa1e652a" +"fe4d6aa147a9f46fda33cacb65f3aa12234746b9007a8c85fe982afed7815221e43dba55" +"3d8fe8a022cdac1b99eeeea359e5a9d2e72e382dffa6d19f359f4f27dc3434cd27daeeda" +"8e38594873398678065fbb23665aba9309d946135da0e4a4afdadff14db18e85e71dd93c" +"3bf9faf7f25c8194c4269b1ee3d9934097ab990025d9c3aaf63d5109f52335dd3959d38a" +"e485050e4bbb6235574fc0102be8f7a306d6e8de6ba6becf80f37415b57f9898a5824e77" +"414197422be3d36a6080", + "0423dc76a8791107d14e13f5265b343f24cc0f19", + + 2888, +"0f865f46a8f3aed2da18482aa09a8f390dc9da07d51d1bd10fe0bf5f3928d5927d08733d" +"32075535a6d1c8ac1b2dc6ba0f2f633dc1af68e3f0fa3d85e6c60cb7b56c239dc1519a00" +"7ea536a07b518ecca02a6c31b46b76f021620ef3fc6976804018380e5ab9c558ebfc5cb1" +"c9ed2d974722bf8ab6398f1f2b82fa5083f85c16a5767a3a07271d67743f00850ce8ec42" +"8c7f22f1cf01f99895c0c844845b06a06cecb0c6cf83eb55a1d4ebc44c2c13f6f7aa5e0e" +"08abfd84e7864279057abc471ee4a45dbbb5774afa24e51791a0eada11093b88681fe30b" +"aa3b2e94113dc63342c51ca5d1a6096d0897b626e42cb91761058008f746f35465465540" +"ad8c6b8b60f7e1461b3ce9e6529625984cb8c7d46f07f735be067588a0117f23e34ff578" +"00e2bbe9a1605fde6087fb15d22c5d3ac47566b8c448b0cee40373e5ba6eaa21abee7136" +"6afbb27dbbd300477d70c371e7b8963812f5ed4fb784fb2f3bd1d3afe883cdd47ef32bea" +"ea", + "6692a71d73e00f27df976bc56df4970650d90e45", + + 3680, +"4893f1c763625f2c6ce53aacf28026f14b3cd8687e1a1d3b60a81e80fcd1e2b038f9145a" +"b64a0718f948f7c3c9ac92e3d86fb669a5257da1a18c776291653688338210a3242120f1" +"01788e8acc9110db9258b1554bf3d26602516ea93606a25a7f566c0c758fb39ecd9d876b" +"c5d8abc1c3205095382c2474cb1f8bbdb45c2c0e659cb0fc703ec607a5de6bcc7a28687d" +"b1ee1c8f34797bb2441d5706d210df8c2d7d65dbded36414d063c117b52a51f7a4eb9cac" +"0782e008b47459ed5acac0bc1f20121087f992ad985511b33c866d18e63f585478ee5a5e" +"654b19d81231d98683ae3f0533565aba43dce408d7e3c4c6be11d8f05165f29c9dcb2030" +"c4ee31d3a04e7421aa92c3231a1fc07e50e95fea7389a5e65891afaba51cf55e36a9d089" +"bf293accb356d5d06547307d6e41456d4ed146a056179971c56521c83109bf922866186e" +"184a99a96c7bb96df8937e35970e438412a2b8d744cf2ad87cb605d4232e976f9f151697" +"76e4e5b6b786132c966b25fc56d815c56c819af5e159aa39f8a93d38115f5580cda93bc0" +"73c30b39920e726fe861b72483a3f886269ab7a8eefe952f35d25c4eb7f443f4f3f26e43" +"d51fb54591e6a6dad25fcdf5142033084e5624bdd51435e77dea86b8", + "dc5859dd5163c4354d5d577b855fa98e37f04384", + + 4472, +"cf494c18a4e17bf03910631471bca5ba7edea8b9a63381e3463517961749848eb03abefd" +"4ce676dece3740860255f57c261a558aa9c7f11432f549a9e4ce31d8e17c79450ce2ccfc" +"148ad904aedfb138219d7052088520495355dadd90f72e6f69f9c6176d3d45f113f275b7" +"fbc2a295784d41384cd7d629b23d1459a22e45fd5097ec9bf65fa965d3555ec77367903c" +"32141065fc24da5c56963d46a2da3c279e4035fb2fb1c0025d9dda5b9e3443d457d92401" +"a0d3f58b48469ecb1862dc975cdbe75ca099526db8b0329b03928206f084c633c04eef5e" +"8e377f118d30edf592504be9d2802651ec78aeb02aea167a03fc3e23e5fc907c324f283f" +"89ab37e84687a9c74ccf055402db95c29ba2c8d79b2bd4fa96459f8e3b78e07e923b8119" +"8267492196ecb71e01c331f8df245ec5bdf8d0e05c91e63bb299f0f6324895304dda721d" +"39410458f117c87b7dd6a0ee734b79fcbe482b2c9e9aa0cef03a39d4b0c86de3bc34b4aa" +"dabfa373fd2258f7c40c187744d237080762382f547a36adb117839ca72f8ebbc5a20a07" +"e86f4c8bb923f5787698d278f6db0040e76e54645bb0f97083995b34b9aa445fc4244550" +"58795828dd00c32471ec402a307f5aa1b37b1a86d6dae3bcbfbe9ba41cab0beeabf489af" +"0073d4b3837d3f14b815120bc3602d072b5aeefcdec655fe756b660eba7dcf34675acbce" +"317746270599424b9248791a0780449c1eabbb9459cc1e588bfd74df9b1b711c85c09d8a" +"a171b309281947e8f4b6ac438753158f4f36fa", + "4c17926feb6e87f5bca7890d8a5cde744f231dab", + + 5264, +"8236153781bd2f1b81ffe0def1beb46f5a70191142926651503f1b3bb1016acdb9e7f7ac" +"ced8dd168226f118ff664a01a8800116fd023587bfba52a2558393476f5fc69ce9c65001" +"f23e70476d2cc81c97ea19caeb194e224339bcb23f77a83feac5096f9b3090c51a6ee6d2" +"04b735aa71d7e996d380b80822e4dfd43683af9c7442498cacbea64842dfda238cb09992" +"7c6efae07fdf7b23a4e4456e0152b24853fe0d5de4179974b2b9d4a1cdbefcbc01d8d311" +"b5dda059136176ea698ab82acf20dd490be47130b1235cb48f8a6710473cfc923e222d94" +"b582f9ae36d4ca2a32d141b8e8cc36638845fbc499bce17698c3fecae2572dbbd4705524" +"30d7ef30c238c2124478f1f780483839b4fb73d63a9460206824a5b6b65315b21e3c2f24" +"c97ee7c0e78faad3df549c7ca8ef241876d9aafe9a309f6da352bec2caaa92ee8dca3928" +"99ba67dfed90aef33d41fc2494b765cb3e2422c8e595dabbfaca217757453fb322a13203" +"f425f6073a9903e2dc5818ee1da737afc345f0057744e3a56e1681c949eb12273a3bfc20" +"699e423b96e44bd1ff62e50a848a890809bfe1611c6787d3d741103308f849a790f9c015" +"098286dbacfc34c1718b2c2b77e32194a75dda37954a320fa68764027852855a7e5b5274" +"eb1e2cbcd27161d98b59ad245822015f48af82a45c0ed59be94f9af03d9736048570d6e3" +"ef63b1770bc98dfb77de84b1bb1708d872b625d9ab9b06c18e5dbbf34399391f0f8aa26e" +"c0dac7ff4cb8ec97b52bcb942fa6db2385dcd1b3b9d567aaeb425d567b0ebe267235651a" +"1ed9bf78fd93d3c1dd077fe340bb04b00529c58f45124b717c168d07e9826e33376988bc" +"5cf62845c2009980a4dfa69fbc7e5a0b1bb20a5958ca967aec68eb31dd8fccca9afcd30a" +"26bab26279f1bf6724ff", + "11863b483809ef88413ca9b0084ac4a5390640af", + + 6056, +"31ec3c3636618c7141441294fde7e72366a407fa7ec6a64a41a7c8dfda150ca417fac868" +"1b3c5be253e3bff3ab7a5e2c01b72790d95ee09b5362be835b4d33bd20e307c3c702aa15" +"60cdc97d190a1f98b1c78e9230446e31d60d25155167f73e33ed20cea27b2010514b57ba" +"b05ed16f601e6388ea41f714b0f0241d2429022e37623c11156f66dd0fa59131d8401dba" +"f502cffb6f1d234dcb53e4243b5cf9951688821586a524848123a06afa76ab8058bcfa72" +"27a09ce30d7e8cb100c8877bb7a81b615ee6010b8e0daced7cc922c971940b757a9107de" +"60b8454dda3452e902092e7e06faa57c20aadc43c8012b9d28d12a8cd0ba0f47ab4b377f" +"316902e6dff5e4f2e4a9b9de1e4359f344e66d0565bd814091e15a25d67d89cf6e30407b" +"36b2654762bbe53a6f204b855a3f9108109e351825cf9080c89764c5f74fb4afef89d804" +"e7f7d097fd89d98171d63eaf11bd719df44c5a606be0efea358e058af2c265b2da2623fd" +"afc62b70f0711d0150625b55672060cea6a253c590b7db1427a536d8a51085756d1e6ada" +"41d9d506b5d51bcae41249d16123b7df7190e056777a70feaf7d9f051fdbbe45cbd60fc6" +"295dda84d4ebbd7284ad44be3ee3ba57c8883ead603519b8ad434e3bf630734a9243c00a" +"a07366b8f88621ec6176111f0418c66b20ff9a93009f43432aaea899dad0f4e3ae72e9ab" +"a3f678f140118eb7117230c357a5caa0fe36c4e6cf1957bbe7499f9a68b0f1536e476e53" +"457ed826d9dea53a6ded52e69052faaa4d3927b9a3f9e8b435f424b941bf2d9cd6849874" +"42a44d5acaa0da6d9f390d1a0dd6c19af427f8bb7c082ae405a8dd535dea76aa360b4faa" +"d786093e113424bb75b8cc66c41af637a7b2acdca048a501417919cf9c5cd3b2fa668860" +"d08b6717eea6f125fa1b0bae1dbb52aafce8ae2deaf92aeb5be003fb9c09fedbc286ffb5" +"e16ad8e07e725faa46ebc35500cf205fc03250075ddc050c263814b8d16d141db4ca289f" +"386719b28a09a8e5934722202beb3429899b016dfeb972fee487cdd8d18f8a681042624f" +"51", + "f43937922444421042f76756fbed0338b354516f", + + 6848, +"21b9a9686ec200456c414f2e6963e2d59e8b57e654eced3d4b57fe565b51c9045c697566" +"44c953178f0a64a6e44d1b46f58763c6a71ce4c373b0821c0b3927a64159c32125ec916b" +"6edd9bf41c3d80725b9675d6a97c8a7e3b662fac9dbcf6379a319a805b5341a8d360fe00" +"5a5c9ac1976094fea43566d66d220aee5901bd1f2d98036b2d27eb36843e94b2e5d1f09c" +"738ec826de6e0034cf8b1dca873104c5c33704cae290177d491d65f307c50a69b5c81936" +"a050e1fe2b4a6f296e73549323b6a885c3b54ee5eca67aa90660719126b590163203909e" +"470608f157f033f017bcf48518bf17d63380dabe2bc9ac7d8efe34aedcae957aeb68f10c" +"8ad02c4465f1f2b029d5fbb8e8538d18be294394b54b0ee6e67a79fce11731604f3ac4f8" +"d6ffa9ef3d2081f3d1c99ca107a7bf3d624324a7978ec38af0bcd0d7ee568572328b212b" +"9dc831efb7880e3f4d6ca7e25f8e80d73913fb8edfffd758ae4df61b4140634a92f49314" +"6138ebdcdaa083ea72d52a601230aa6f77874dcad9479f5bcac3763662cc30cb99823c5f" +"f469dcbd64c028286b0e579580fd3a17b56b099b97bf62d555798f7a250e08b0e4f238c3" +"fcf684198bd48a68c208a6268be2bb416eda3011b523388bce8357b7f26122640420461a" +"bcabcb5004519adfa2d43db718bce7d0c8f1b4645c89315c65df1f0842e5741244bba3b5" +"10801d2a446818635d0e8ffcd80c8a6f97ca9f878793b91780ee18eb6c2b99ffac3c38ef" +"b7c6d3af0478317c2b9c421247eba8209ea677f984e2398c7c243696a12df2164417f602" +"d7a1d33809c865b73397550ff33fe116166ae0ddbccd00e2b6fc538733830ac39c328018" +"bcb87ac52474ad3cce8780d6002e14c6734f814cb551632bcc31965c1cd23d048b9509a4" +"e22ab88f76a6dba209d5dd2febd1413a64d32be8574a22341f2a14e4bd879abb35627ef1" +"35c37be0f80843006a7cc7158d2bb2a71bf536b36de20ca09bb5b674e5c408485106e6fa" +"966e4f2139779b46f6010051615b5b41cda12d206d48e436b9f75d7e1398a656abb0087a" +"a0eb453368fc1ecc71a31846080f804d7b67ad6a7aa48579c3a1435eff7577f4e6004d46" +"aac4130293f6f62ae6d50c0d0c3b9876f0728923a94843785966a27555dd3ce68602e7d9" +"0f7c7c552f9bda4969ec2dc3e30a70620db6300e822a93e633ab9a7a", + "5d4d18b24b877092188a44be4f2e80ab1d41e795", + + 7640, +"1c87f48f4409c3682e2cf34c63286dd52701b6c14e08669851a6dc8fa15530ad3bef692c" +"7d2bf02238644561069df19bdec3bccae5311fce877afc58c7628d08d32d9bd2dc1df0a6" +"24360e505944219d211f33bff62e9ff2342ac86070240a420ccaf14908e6a93c1b27b6e2" +"0324e522199e83692805cc4c7f3ea66f45a490a50d4dd558aa8e052c45c1a5dfad452674" +"edc7149024c09024913f004ceee90577ff3eaec96a1eebbdc98b440ffeb0cad9c6224efc" +"9267d2c192b53dc012fb53010926e362ef9d4238d00df9399f6cbb9acc389a7418007a6c" +"a926c59359e3608b548bdeece213f4e581d02d273781dffe26905ec161956f6dfe1c008d" +"6da8165d08f8062eea88e80c055b499f6ff8204ffdb303ab132d9b0cba1e5675f3525bbe" +"4cf2c3f2b00506f58336b36aefd865d37827f2fad7d1e59105b52f1596ea19f848037dfe" +"dc9136e824ead5505e2995d4c0769276548835430667f333fc77375125b29c1b1535602c" +"10fe161864f49a98fc274ae7335a736be6bf0a98cd019d120b87881103f86c0a6efadd8c" +"aa405b6855c384141b4f8751cc42dc0cb2913382210baaa84fe242ca66679472d815c08b" +"f3d1a7c6b5705a3de17ad157522de1eb90c568a8a1fbcbb422cca293967bb14bfdd91bc5" +"a9c4d2774dee524057e08f937f3e2bd8a04ced0fc7b16fb78a7b16ee9c6447d99e53d846" +"3726c59066af25c317fc5c01f5dc9125809e63a55f1cd7bdf7f995ca3c2655f4c7ab940f" +"2aa48bc3808961eb48b3a03c731ce627bb67dd0037206c5f2c442fc72704258548c6a9db" +"e16da45e40da009dc8b2600347620eff8361346116b550087cb9e2ba6b1d6753622e8b22" +"85589b90a8e93902aa17530104455699a1829efef153327639b2ae722d5680fec035575c" +"3b48d9ec8c8e9550e15338cc76b203f3ab597c805a8c6482676deabc997a1e4ba857a889" +"97ceba32431443c53d4d662dd5532aa177b373c93bf93122b72ed7a3189e0fa171dfabf0" +"520edf4b9d5caef595c9a3a13830c190cf84bcf9c3596aadb2a674fbc2c951d135cb7525" +"3ee6c59313444f48440a381e4b95f5086403beb19ff640603394931f15d36b1cc9f3924f" +"794c965d4449bfbdd8b543194335bdf70616dc986b49582019ac2bf8e68cfd71ec67e0aa" +"dff63db39e6a0ef207f74ec6108fae6b13f08a1e6ae01b813cb7ee40961f95f5be189c49" +"c43fbf5c594f5968e4e820a1d38f105f2ff7a57e747e4d059ffb1d0788b7c3c772b9bc1f" +"e147c723aca999015230d22c917730b935e902092f83e0a8e6db9a75d2626e0346e67e40" +"8d5b815439dab8ccb8ea23f828fff6916c4047", + "32e0f5d40ceec1fbe45ddd151c76c0b3fef1c938", + + 8432, +"084f04f8d44b333dca539ad2f45f1d94065fbb1d86d2ccf32f9486fe98f7c64011160ec0" +"cd66c9c7478ed74fde7945b9c2a95cbe14cedea849978cc2d0c8eb0df48d4834030dfac2" +"b043e793b6094a88be76b37f836a4f833467693f1aa331b97a5bbc3dbd694d96ce19d385" +"c439b26bc16fc64919d0a5eab7ad255fbdb01fac6b2872c142a24aac69b9a20c4f2f07c9" +"923c9f0220256b479c11c90903193d4e8f9e70a9dbdf796a49ca5c12a113d00afa844694" +"de942601a93a5c2532031308ad63c0ded048633935f50a7e000e9695c1efc1e59c426080" +"a7d1e69a93982a408f1f6a4769078f82f6e2b238b548e0d4af271adfa15aa02c5d7d7052" +"6e00095ffb7b74cbee4185ab54385f2707e8362e8bd1596937026f6d95e700340b6338ce" +"ba1ee854a621ce1e17a016354016200b1f98846aa46254ab15b7a128b1e840f494b2cdc9" +"daccf14107c1e149a7fc27d33121a5cc31a4d74ea6945816a9b7a83850dc2c11d26d767e" +"ec44c74b83bfd2ef8a17c37626ed80be10262fe63cf9f804b8460c16d62ae63c8dd0d124" +"1d8aaac5f220e750cb68d8631b162d80afd6b9bf929875bf2e2bc8e2b30e05babd8336be" +"31e41842673a66a68f0c5acd4d7572d0a77970f42199a4da26a56df6aad2fe420e0d5e34" +"448eb2ed33afbfb35dffaba1bf92039df89c038bae3e11c02ea08aba5240c10ea88a45a1" +"d0a8631b269bec99a28b39a3fc5b6b5d1381f7018f15638cc5274ab8dc56a62b2e9e4fee" +"f172be20170b17ec72ff67b81c15299f165810222f6a001a281b5df1153a891206aca89e" +"e7baa761a5af7c0493a3af840b9219e358b1ec1dd301f35d4d241b71ad70337bda42f0ea" +"dc9434a93ed28f96b6ea073608a314a7272fefd69d030cf22ee6e520b848fa705ed6160f" +"e54bd3bf5e89608506e882a16aced9c3cf80657cd03749f34977ced9749caa9f52b683e6" +"4d96af371b293ef4e5053a8ea9422df9dd8be45d5574730f660e79bf4cbaa5f3c93a79b4" +"0f0e4e86e0fd999ef4f26c509b0940c7a3eaf1f87c560ad89aff43cd1b9d4863aa3ebc41" +"a3dd7e5b77372b6953dae497fc7f517efe99e553052e645e8be6a3aeb362900c75ce712d" +"fcba712c4c25583728db9a883302939655ef118d603e13fcf421d0cea0f8fb7c49224681" +"d013250defa7d4fd64b69b0b52e95142e4cc1fb6332486716a82a3b02818b25025ccd283" +"198b07c7d9e08519c3c52c655db94f423912b9dc1c95f2315e44be819477e7ff6d2e3ccd" +"daa6da27722aaadf142c2b09ce9472f7fd586f68b64d71fc653decebb4397bf7af30219f" +"25c1d496514e3c73b952b8aa57f4a2bbf7dcd4a9e0456aaeb653ca2d9fa7e2e8a532b173" +"5c4609e9c4f393dd70901393e898ed704db8e9b03b253357f333a66aba24495e7c3d1ad1" +"b5200b7892554b59532ac63af3bdef590b57bd5df4fbf38d2b3fa540fa5bf89455802963" +"036bd173fe3967ed1b7d", + "ee976e4ad3cad933b283649eff9ffdb41fcccb18", + + 9224, +"bd8320703d0cac96a96aeefa3abf0f757456bf42b3e56f62070fc03e412d3b8f4e4e427b" +"c47c4600bb423b96de6b4910c20bc5c476c45feb5b429d4b35088813836fa5060ceb26db" +"bb9162e4acd683ef879a7e6a0d6549caf0f0482de8e7083d03ed2f583de1b3ef505f4b2c" +"cd8a23d86c09d47ba05093c56f21a82c815223d777d0cabb7ee4550423b5deb6690f9394" +"1862ae41590ea7a580dda79229d141a786215d75f77e74e1db9a03c9a7eb39eb35adf302" +"5e26eb31ca2d2ca507edca77d9e7cfcfd136784f2117a2afafa87fa468f08d07d720c933" +"f61820af442d260d172a0a113494ca169d33a3aeaacdcc895b356398ed85a871aba769f6" +"071abd31e9f2f5834721d0fef6f6ee0fc0e38760b6835dfcc7dbefb592e1f0c3793af7ad" +"f748786d3364f3cfd5686b1a18711af220e3637d8fad08c553ce9d5dc1183d48e8337b16" +"1fe69b50e1920316dbffec07425b5d616a805a699576590e0939f5c965bce6c7342d314a" +"c37b9c4d30166567c4f633f182de4d6b00e20a1c762789f915eaa1c89ac31b85222b1f05" +"403dedd94db9ce75ff4e49923d1999d032695fa0a1c595617830c3c9a7ab758732fcec26" +"85ae14350959b6a5f423ef726587e186b055a8daf6fa8fdefa02841b2fdbca1616dcee78" +"c685fc6dcc09f24a36097572eba3c37a3eabe98bc23836085f63ef71a54b4488615d83b2" +"6ed28c9fce78852df9b6cf8a75ca3899a7567298e91bc4ffdd04ffab0066b43b8286a4bb" +"555c78808496b252c6e0e4d153631f11f68baf88630e052acc2af5d2af2e22e4f23bb630" +"314c561a577455f86b6727bcad3c19d3e271404dec30af3d9dd0ed63cd9fa708aadfa12a" +"500ef2d99a6b71e137b56ba90036975b88004b45f577ef800f0fb3cf97577dc9da37253b" +"8675e5c8bb7e0bd26564f19eca232fb25f280f82e014424c9fbdd1411d7556e5d7906bb8" +"62206316ba03385cd820c54c82ed35b36735bc486b1885d84053eba036c1ebfb5422d93d" +"a71c53deda7f74db07cd4959cdfa898ba37080d76b564d344b124dd7b80cd70ed3b52a6c" +"f9c9a32695d134bd39eb11ddeecdac86c808e469bd8a7995b667c452e7d9a54d5c85bcf6" +"d5ffdc27d491bc06f438f02c7cf018073431587c78ba08d18a8daccb2d3b26136f612ade" +"c673f3cd5eb83412b29652d55a10d0d6238d0b5365db272c917349450aff062c36191cfc" +"d45660819083f89cd42ecae9e26934a020cafeb9b2b68d544edf59574c0ca159fd195dbf" +"3e3e74244d942fffdbd4ed7f626219bab88b5a07e50b09a832d3e8ad82091114e54f2c35" +"6b48e55e36589ebad3ac6077cb7b1827748b00670df65bbf0a2e65caad3f8a97d654d64e" +"1c7dad171cafbc37110d2f7ca66524dc08fe60593e914128bd95f41137bfe819b5ca835f" +"e5741344b5c907ce20a35f4f48726141c6398e753ed9d46d3692050628c78859d5014fe4" +"dd3708e58d4d9807f8dac540492e32fa579491717ad4145c9efc24cf95605660b2e09b89" +"9369b74d3ebff41e707917ff314d93e6ac8dfd643ef2c087cd9912005b4b2681da01a369" +"42a756a3e22123cbf38c429373c6a8663130c24b24b2690b000013960b1c46a32d1d5397" +"47", + "2df09b10933afedfcd3f2532dfd29e7cb6213859", + + 10016, +"7a94978bec7f5034b12c96b86498068db28cd2726b676f54d81d8d7350804cc106bead8a" +"252b465a1f413b1c41e5697f8cece49ec0dea4dfb9fa7b1bfe7a4a00981875b420d094bb" +"1ce86c1b8c2e1dbebf819c176b926409fdec69042e324e71d7a8d75006f5a11f512811fe" +"6af88a12f450e327950b18994dfc3f740631beda6c78bca5fe23d54e6509120e05cd1842" +"d3639f1466cf26585030e5b4aefe0404fe900afc31e1980f0193579085342f1803c1ba27" +"0568f80eaf92440c4f2186b736f6ab9dc7b7522ccdcfc8cf12b6375a2d721aa89b5ef482" +"112a42c31123aebabcb485d0e72d6b6b70c44e12d2da98d1f87fa9df4f37847e1ffec823" +"1b8be3d737d282ddb9cc4b95937acfa0f028ba450def4d134a7d0fc88119bf7296e18cd4" +"4f56890b661b5b72ddfa34c29228067e13caf08eb3b7fd29de800df9a9ae137aad4a81a4" +"16a301c9f74b66c0e163e243b3187996b36eb569de3d9c007d78df91f9b554eef0eaa663" +"88754ce20460b75d95e2d0747229a1502a5652cf39ca58e1daa0e9321d7ab3093981cd70" +"23a7ee956030dd70177028a66ad619ad0629e631f91228b7c5db8e81b276d3b168c1edb1" +"bc0888d1cbcbb23245c2d8e40c1ff14bfe13f9c70e93a1939a5c45eef9351e795374b9e1" +"b5c3a7bd642477ba7233e1f590ab44a8232c53099a3c0a6ffe8be8b7ca7b58e6fedf700f" +"6f03dd7861ee1ef857e3f1a32a2e0baa591d0c7ca04cb231cc254d29cda873f00d68f465" +"00d6101cfdc2e8004c1f333d8007325d06ffe6b0ff7b80f24ba51928e65aa3cb78752028" +"27511207b089328bb60264595a2cebfc0b84d9899f5eca7ea3e1d2f0f053b4e67f975500" +"7ff3705ca4178ab9c15b29dd99494135f35befbcec05691d91f6361cad9c9a32e0e65577" +"f14d8dc66515081b51d09e3f6c25eea868cf519a83e80c935968cae6fce949a646ad53c5" +"6ee1f07dda23daef3443310bc04670afedb1a0132a04cb64fa84b4af4b3dc501044849cd" +"dd4adb8d733d1eac9c73afa4f7d75864c87787f4033ffe5ba707cbc14dd17bd1014b8b61" +"509c1f55a25cf6c0cbe49e4ddcc9e4de3fa38f7203134e4c7404ee52ef30d0b3f4e69bcc" +"7d0b2e4d8e60d9970e02cc69d537cfbc066734eb9f690a174e0194ca87a6fadad3883d91" +"6bd1700a052b26deee832701590d67e6f78938eac7c4beef3061a3474dd90dd588c1cd6e" +"6a4cda85b110fd08a30dcd85a3ebde910283366a17a100db920885600db7578be46bcfa6" +"4765ba9a8d6d5010cb1766d5a645e48365ed785e4b1d8c7c233c76291c92ef89d70bc77f" +"bf37d7ce9996367e5b13b08242ce73971f1e0c6ff2d7920fb9c821768a888a7fe0734908" +"33efb854cbf482aed5cb594fb715ec82a110130664164db488666d6198279006c1aa521f" +"9cf04250476c934eba0914fd586f62d6c5825b8cf82cd7ef915d93106c506ea6760fd8b0" +"bf39875cd1036b28417de54783173446026330ef701c3a6e5b6873b2025a2c1666bb9e41" +"a40adb4a81c1052047dabe2ad092df2ae06d6d67b87ac90be7d826ca647940c4da264cad" +"43c32a2bb8d5e27f87414e6887561444a80ed879ce91af13e0fbd6af1b5fa497ad0cbd2e" +"7f0f898f52f9e4710de2174e55ad07c45f8ead3b02cac6c811becc51e72324f2439099a0" +"5740090c1b165ecae7dec0b341d60a88f46d7ad8624aac231a90c93fad61fcfbbea12503" +"59fcd203862a6b0f1d71ac43db6c58a6b60c2c546edc12dd658998e8", + "f32e70862a16e3e8b199e9d81a9949d66f812cad", + + 10808, +"88dd7f273acbe799219c23184782ac0b07bade2bc46b4f8adbd25ed3d59c0fd3e2931638" +"837d31998641bbb7374c7f03d533ca60439ac4290054ff7659cc519bdda3dff2129a7bdb" +"66b3300068931ade382b7b813c970c8e15469187d25cb04b635403dc50ea6c65ab38a97c" +"431f28a41ae81c16192bd0c103f03b8fa815d6ea5bf0aa7fa534ad413b194eb12eb74f5d" +"62b3d3a7411eb8c8b09a261542bf6880acbdfb617a42e577009e482992253712f8d4c8bd" +"1c386bad068c7aa10a22111640041f0c35dabd0de00ebf6cd82f89cbc49325df12419278" +"ec0d5ebb670577b2fe0c3e0840c5dd0dc5b3da00669eed8ead380f968b00d42f4967faec" +"c131425fce1f7edb01cbec7e96d3c26fa6390a659e0ab069ef3edadc07e077bb816f1b22" +"98830a0fe2b393693bb79f41feca89577c5230e0a6c34b860dc1fdb10d85aa054481082c" +"494779d59ba798fcd817116c3059b7831857d0364352b354ce3b960fbb61a1b8a04d47ca" +"a0ead52a9bea4bada2646cdbaec211f391dac22f2c5b8748e36bfc3d4e8ea45131ca7f52" +"af09df21babe776fcecbb5c5dfa352c790ab27b9a5e74242bbd23970368dbefd7c3c74d1" +"61ae01c7e13c65b415f38aa660f51b69ea1c9a504fe1ad31987cb9b26a4db2c37d7b326c" +"50dbc8c91b13925306ff0e6098532dee7282a99c3ddf99f9e1024301f76e31e58271870b" +"d94b9356e892a6a798d422a48c7fd5b80efe855a4925cc93b8cf27badec5498338e2b538" +"70758b45d3e7a2fa059ed88df320a65e0a7cf87fa7e63b74cea1b7371e221f8004726642" +"30d4d57945a85b23d58f248c8cd06ccfabfa969ab8cb78317451fab60e4fdfa796e2e2a8" +"b46405839a91266d37e8d38bae545fb4060c357923b86d62f5d59d7bef5af20fbb9c7fb4" +"2c6fd487748ed3b9973dbf4b1f2c9615129fa10d21cc49c622842c37c01670be71715765" +"a98814634efbdee66bf3420f284dbd3efafc8a9117a8b9a72d9b81aa53ded78c409f3f90" +"bad6e30d5229e26f4f0cea7ee82c09e3b60ec0e768f35a7fb9007b869f9bfc49c518f648" +"3c951d3b6e22505453266ec4e7fe6a80dbe6a2458a1d6cd93044f2955607412091009c7d" +"6cd81648a3b0603c92bfdff9ec3c0104b07ed2105962ca7c56ede91cb932073c337665e2" +"409387549f9a46da05bc21c5126bd4b084bc2c06ab1019c51df30581aa4464ab92978c13" +"f6d7c7ac8d30a78f982b9a43181bbe3c3eb9f7a1230b3e53b98a3c2a028317827fbe8cf6" +"ec5e3e6b2a084d517d472b25f72fab3a34415bba488f14e7f621cfa72396ba40890e8c60" +"b04815601a0819c9bebc5e18b95e04be3f9c156bd7375d8cc8a97c13ce0a3976123419fa" +"592631317ca638c1182be06886f9663d0e8e6839573df8f52219eeb5381482a6a1681a64" +"173660bfbb6d98bf06ee31e601ee99b4b99b5671ed0253260b3077ed5b977c6a79b4ff9a" +"08efd3cba5c39bec1a1e9807d40bbf0c988e0fd071cf2155ed7b014c88683cd869783a95" +"4cbfced9c0e80c3a92d45b508985cbbc533ba868c0dc4f112e99400345cf7524e42bf234" +"5a129e53da4051c429af2ef09aba33ae3c820ec1529132a203bd2b81534f2e865265f55c" +"9395caf0e0d3e1762c95eaaec935e765dc963b3e0d0a04b28373ab560fa9ba5ca71ced5d" +"17bb8b56f314f6f0d0bc8104b3f1835eca7eaac15adf912cf9a6945cfd1de392342dd596" +"d67e7ffcb7e086a6c1ea318aa2e0c2b5c2da079078232c637de0d317a1f26640bc1dac5b" +"e8699b53edc86e4bfdfaf797a2ae350bf4ea29790face675c4d2e85b8f37a694c91f6a14" +"1fd561274392ee6ee1a14424d5c134a69bcb4333079400f03615952fc4c99bf03f5733a8" +"dc71524269fc5c648371f5f3098314d9d10258", + "08632c75676571a5db5971f5d99cb8de6bf1792a", + + 11600, +"85d43615942fcaa449329fd1fe9efb17545eb252cac752228f1e9d90955a3cf4e72cb116" +"3c3d8e93ccb7e4826206ff58b3e05009ee82ab70943db3f18a32925d6d5aed1525c91673" +"bd33846571af815b09bb236466807d935b5816a8be8e9becbe65d05d765bcc0bc3ae66c2" +"5320ebe9fff712aa5b4931548b76b0fd58f6be6b83554435587b1725873172e130e1a3ca" +"3d9d0425f4632d79cca0683780f266a0633230e4f3b25f87b0c390092f7b13c66ab5e31b" +"5a58dbcac8dd26a0600bf85507057bb36e870dfae76da8847875a1a52e4596d5b4b0a211" +"2435d27e1dc8dd5016d60feaf2838746d436a2983457b72e3357059b2bf1e9148bb0551a" +"e2b27d5a39abd3d1a62c36331e26668e8baabc2a1ef218b5e7a51a9ca35795bcd54f403a" +"188eafafb30d82896e45ddaea4f418629a1fb76a0f539c7114317bac1e2a8fba5a868bce" +"40abd40f6b9ced3fa8c0329b4de5ca03cc84d75b8746ef31e6c8d0a0a79b4f747690928e" +"be327f8bbe9374a0df4c39c845bf3322a49fda9455b36db5a9d6e4ea7d4326cf0e0f7cd8" +"0ff74538f95cec01a38c188d1243221e9272ccc1053e30787c4cf697043cca6fc3730d2a" +"431ecbf60d73ee667a3ab114c68d578c66dc1c659b346cb148c053980190353f6499bfef" +"acfd1d73838d6dc1188c74dd72b690fb0481eee481a3fd9af1d4233f05d5ae33a7b10d7d" +"d643406cb1f88d7dd1d77580dcbee6f757eeb2bfbcc940f2cddb820b2718264b1a64115c" +"b85909352c44b13d4e70bbb374a8594d8af7f41f65b221bf54b8d1a7f8f9c7da563550cb" +"2b062e7a7f21d5e07dd9da8d82e5a89074627597551c745718094c2eb316ca077526d27f" +"9a589c461d891dc7cd1bc20ba3f464da53c97924219c87a0f683dfb3b3ac8793c59e78ac" +"fac109439221ac599a6fd8d2754946d6bcba60784805f7958c9e34ff287ad1dbbc888848" +"fa80cc4200dbb8c5e4224535906cbffdd0237a77a906c10ced740f9c0ce7821f2dbf8c8d" +"7d41ecfcc7dfdc0846b98c78b765d01fb1eb15ff39149ab592e5dd1152665304bba85bbf" +"4705751985aaaf31245361554d561a2337e3daeef58a826492fd886d5f18ef568c1e772e" +"f6461170407695e3254eb7bf0c683811ddde5960140d959114998f08bdb24a104095987d" +"3255d590e0dbd41ae32b1ae4f4ea4a4f011de1388034231e034756870c9f2d9f23788723" +"27055a7de2b5e931dfb53e7780b6d4294bf094e08567025b026db9203b681565a1d52f30" +"318d0ebe49471b22ba5fd62e1ed6c8966c99b853c9062246a1ace51ef7523c7bf93bef53" +"d8a9cb96d6a04f0da1eca888df66e0380a72525a7ecc6115d08569a66248f6ba34e2341b" +"fd01a78f7b3c1cfe0754e0d26cba2fa3f951ef14d5749ff8933b8aba06fa40fb570b467c" +"54ce0d3f0bed21e998e5a36b3bc2f9e1ae29c4bab59c121af6fad67c0b45959cd6a86194" +"14b90b4535fb95f86ca7e64502acc135eff4f8a3abe9dde84238fab7a7d402454a3f07ad" +"ec05ec94b2891e0879037fae6acaa31dcecf3f85236ade946f5ad69ad4077beb65099285" +"38ee09f2bc38e5704da67b5006b5e39cd765aafcd740c7dadb99d0c547126e1324610fcb" +"7353dac2c110e803fca2b17485b1c4b78690bc4f867e6f043b2568889f67985a465a48eb" +"ee915200589e915756d4968d26529c3ffe3dbe70e84c682ad08a0c68db571634fbb0210d" +"c1b16b8b725886465c8c51f36a5e27d0f78e5643e051d3bddd512ce511f6bdf3dfe42759" +"00c5fea9d248c2b3f36911ed0ff41a19f6445521f251724657ea8f795b3ead0928a1657f" +"308dd7c7c1e7e490d9849df43becfa5cc25ed09ef614fd69ddc7e5e3147623901d647876" +"fb60077ffc48c51ed7d02b35f6802e3715fc708a0c88b82fe9cba0a442d38d09ca5ae483" +"21487bdef1794e7636bf7457dd2b51a391880c34d229438347e5fec8555fe263f08ba87b" +"b16dcde529248a477628067d13d0cb3bf51776f4d39fb3fbc5f669e91019323e40360e4b" +"78b6584f077bf9e03b66", + "ab7213f6becb980d40dc89fbda0ca39f225a2d33", + + 12392, +"7ae3ca60b3a96be914d24980fb5652eb68451fed5fa47abe8e771db8301fbd5331e64753" +"93d96a4010d6551701e5c23f7ecb33bec7dd7bade21381e9865d410c383a139cb4863082" +"8e9372bd197c5b5788b6599853e8487bddfd395e537772fdd706b6a1de59c695d63427da" +"0dc3261bce2e1ae3cd6de90ec45ecd7e5f14580f5672b6ccd8f9336330dffcd6a3612a74" +"975afc08fb136450e25dc6b071ddfc28fca89d846c107fd2e4bd7a19a4ff6f482d62896d" +"a583c3277e23ab5e537a653112cdf2306043b3cc39f5280bd744fe81d66f497b95650e7d" +"dfd704efcb929b13e00c3e3a7d3cd53878af8f1506d9de05dba9c39a92604b394ea25acb" +"a2cda7b4ae8b08098ba3f0fdea15359df76517be84377f33631c844313ac335aa0d590fe" +"c472d805521f0905d44ca40d7391b292184105acd142c083761c1a038c4f5ed869ea3696" +"99592a37817f64cb4205b66be1f1de6fa47a08e1bf1a94312fe61a29e71bab242af95a7b" +"38d4fb412c682b30256d91e2a46b634535d02b495240cbdb842cbe17cba6a2b94073f3d5" +"f9621ac92ddda66f98bed997216466b4bb0579d58945f8d7450808d9e285d4f1709d8a1d" +"416aa57d4a1a72bfcbfecdda33de2cff3e90e0cc60c897c4663224fc5bbe8316a83c1773" +"802837a57bc7e9238173ed41ea32fe5fe38e546014a16d5e80700d9bac7a84bb03902f31" +"79e641f86f6bc383d656daf69801499633fb367ea7593195934c72bc9bf9624c0c845ebf" +"c36eb7ad4b22fdfb45ca7d4f0d6708c69a21f6eaa6db6bde0f0bd9dc7ec9c6e24626d0a7" +"8fbeeed4b391f871e80e6a9d207165832d4ff689296f9bca15dc03c7c0381659ea5335eb" +"aafdc3e50d18e46b00f1844870d09c25afcdb0ff1ae69dd8f94f91aca6095ba6f2b6e594" +"c4acfe9903485d21b684e31a6acc2162d40e1a7bb8114a860a07e76f5265666555f2418d" +"f11ef8f7499656d12215f5da8d7d041ac72648d15d7661ad93b24f3f071334b0921d5bb0" +"6f2c7ab09f5034518b5ae21cec379373e87d51c77d44a70c2337606aadeb9036716fd920" +"a824e7ae18ce3de9f0ec3456f3454027d8c476b3f1854b240c309f6f9786fa8a073915d9" +"7a019ce99aec3260c5f6b6346cd9c41cb9267f4475958e45289965548238c6b9f91a8784" +"b4e0957ba8b73956012c9a2fc3428434b1c1679f6ed2a3e8e2c90238df428622046f668e" +"e2b053f55e64ffd45600e05a885e3264af573bacee93d23d72a0222b5442ac80bc0a8b79" +"4c2afcf3bc881d20c111f57e3450b50a703f3db1fc5de2076a006f3b7eed694b93269874" +"3b03c2ed2684bad445e69a692e744c7ac3a04f1e0e52b7a6708076d1fbffdb3f1c995828" +"7d5f884e29407030f2db06811092efd80ae08da9daec39744c5ecd3ca771663b8f4968d4" +"2a88c2c9821c73ae2a5a4d9e2551f82c03583b9c4dea775423b4748d24eb604e8ee3159b" +"a6de9bea5b22eed6264e011734ed02b2c74ce06dda890b8604ed7ba49e7bf30e28c9871b" +"e90f5cead67eaf52b5d3181c822b10701219b28ef6f6bebfa278e38acf863e2a1d4b1e40" +"fd8a0ac6ce31054446301046148bf10dc3ae3385e2026e7762bdc8003ffebc4263191a59" +"c72f4f90db03e7d52808506b33bfe1dfa53f1a3daa152e83974fbe56cfd4e8f4e7f7806a" +"084b9d0795b858100eced0b5355a72446f37779d6c67ade60a627b8077ae1f3996b03bc3" +"a5c290651c8609f0d879fbf578cbab35086e1159dd6ddbe3bf7fb5654edcc8f09e4f80d0" +"258c9376d7c53fb68f78d333b18b70170d9a11070790c956f5744c78c986b1baf08b7631" +"7a65c5f07ae6f57eb0e65488659324d29709e3735623d0426e90aa8c4629bb080881150c" +"02be1c004da84414ac001c2eb6138c26388f5a36d594f3acef0e69e2cb43b870efa84da0" +"cff9c923a9880202aed64ad76260f53c45bb1584b3e388a909d13586094b924680006a1d" +"25d4dd36c579a8ec9d3fa63c082d977a5a5021440b5314b51850f2daa6e6af6ae88cb5b1" +"44242bceb1d4771e641101f8abfc3a9b19f2de64e35e76458ad22072ba57925d73015de5" +"66c66fcaa28fdc656f90de967ad51afd331e246d74ed469d63dd7d219935c59984bd9629" +"09d1af296eb3121d782650e7d038063bab5fa854aac77de5ffebeb53d263f521e3fc02ac" +"70", + "b0e15d39025f5263e3efa255c1868d4a37041382", + + 13184, +"fa922061131282d91217a9ff07463843ae34ff7f8c28b6d93b23f1ea031d5020aa92f660" +"8c3d3df0ee24a8958fd41af880ee454e36e26438defb2de8f09c018607c967d2f0e8b80a" +"00c91c0eabe5b4c253e319b45e6106ff8bf0516f866020e5ba3f59fd669c5aeff310ebb3" +"85007069d01c64f72d2b02f4ec0b45c5ecf313056afcb52b17e08b666d01fecc42adb5b4" +"9ea00c60cacac2e0a953f1324bdd44aec00964a22a3cb33916a33da10d74ec6c6577fb37" +"5dc6ac8a6ad13e00cba419a8636d4daac8383a2e98fe90790cde7b59cfaa17c410a52abc" +"d68b127593d2fcbafd30578d195d890e981ae09e6772cb4382404a4e09f1a33c958b57db" +"ccee54ae335b6c91443206a0c100135647b844f226417a1f70317fd350d9f3789d81894a" +"aff4730072401aaeb8b713ead4394e2e64b6917d6eee2549af7bd0952f12035719065320" +"ca0d2dfe2847c6a2357c52bee4a676b12bafff66597bd479aa29299c1896f63a7523a85a" +"b7b916c5930ab66b4d191103cefc74f2f7e0e96e354f65e355ae43959a0af1880d14ea9d" +"1569e4fd47174aba7f5decb430b3f6baf80a1ef27855227b62487250d3602970e423423c" +"7ca90920685bcf75adfbe2a61ce5bd9228947b32f567927cb1a5bd8727c03aef91d6367b" +"ae7d86fd15c0977ac965a88b0d7236037aefb8d24eec8d2a07c633e031a7b9147c4c7714" +"110bfc7e261448a5d0f73c3619664e1c533c81a0acbf95d502227a33f84f0b8249e3f9fa" +"5c7905a8192b7313fc56bb20679e81333d32c797ac5162204a0eaa0e64507635921c485b" +"8f17c4e2484667a733197529e2a833eed83c57229b11bd820b5a5b78f1867787dbc217ea" +"28bfba785fb545cbc5a840a12eea428213e1aaa4e50a900ba13efcf4a5345574c2481c5d" +"927ada610bba567a55630c89d905db3d9b67fe36c9cc3d6a947664c83e69f51c74711a33" +"df66dd3ff6af9b7c1605b614d4798b4192b9a4b1508f2e2ec5aaad7eaea1ee8867353db9" +"b8d7d9a6f16aa5f339492073238c979082879aee7f94ac4be8133eaacbaedfb044e2ad4e" +"93ba0fa071dea615a5cd80d1d2678f4f93ae5a4bc9cdf3df345a29ec41d8febb23805ce4" +"2541036f3f05c63ae736f79a29802045fad9f370cabf843458c1b636ca41f387fd7821c9" +"1abbd1946afcb9186b936403233f28a5b467595131a6bc07b0873e51a08de66b5d7709a6" +"02c1bd0e7f6e8f4beb0579c51bda0e0c738ef876fcd9a40ab7873c9c31c1d63a588eebc7" +"8d9a0ae6fa35cd1a269e0d2bc68252cbd7c08d79e96f0aa6be22a016136a2b8abe9d3c9c" +"f9d60eeafe3dbc76d489b24d68c36167df4c38cf2b21cf03dc5e659e39018c3490f1237e" +"ca3f85b742ab0045d86a899c4126ad60a147cbc95b71814c274d6478668df41eb32acfb4" +"bbf024fb4e3d6be0b60653a0471afc3037ab67dcb00a2b2e24b26911e1880136e56106b7" +"f3c570fbe6f311d94624cb001914ff96fbbf481f71686aa17be0850568058fc1ee8900b4" +"7af5cf51c5ed9e00a8b532c131f42513f6b8df14a9bbc2e9ede5a560681184d41a147552" +"edfbdef98d95e6a7793229d25ba9b0b395a020aa1c0731de89e662246d59ec22e5d8f4b4" +"6fbc048efcffbc234744c5c66417070f9c751c81788f04691ccb1a09d60c46f6f73375bf" +"e2e646cf6290069541a8dfe216374c925e94d06ece72e851e81d3e8acd011f82526c2f9f" +"55955c6752dc10e93153ab58627e30fa2c573e4042954337982eec1f741be058c85bad86" +"bf3a02ed96d3201dadd48bd4de8105200dfcbcc400c3c3dd717abfc562ebe338b14b1eb5" +"ecbe9227661e49c58bf8233770d813faafc78b05711135adcc4ce4c65095ca0bdc1debc0" +"b6e5d195dbc582ce94b3afa14a422edf9b69abd7ae869a78c3a26fb50ef7122ec5af8d0c" +"78ef082ca114f8817c3d93b31809870caea2eb9533fa767c2954efb9ba07e4f1077e9f9b" +"be845661eabea2c91079321477a7c167c7234528d63d6aabbe723e0e337b2e61138a310a" +"3fd04368aa4215b7af9d0334a8a74681bcb86b4af87a0329a1ed9dc7c9aef14521785eda" +"0eeb97bdff8c9945fd0ee04e84d0dae091a69c0bfcdcd4150878fed839c0db6565fc1fed" +"0e7d6ae2efde7a59d58a9fb3b07e6f7cea51ba93f771c18b2eafa252d7fe171085776052" +"a6a17e6858f0a20b7e8be54413523989bf20a028a84d9ce98b78e6ee0b8362df49de5344" +"b409cc322354672a21ea383e870d047551a3af71aaf2f44f49a859cf001e61b592dd036f" +"c6625bf7b91ea0fb78c1563cceb8c4345bf4a9fbe6ee2b6bf5e81083", + "8b6d59106f04300cb57c7c961945cd77f3536b0a", + + 13976, +"162cca41155de90f6e4b76a34261be6666ef65bdb92b5831b47604ce42e0c6c8d2eda265" +"ab9a3716809bf2e745e7831a41768d0f6349a268d9ac6e6adfb832a5d51b75d7951cf60e" +"03d9e40de6d351f1f6ade5143531cf32839401ca6dfb9dc7473daa607aeb0c3d1e8eb3db" +"cc2f1231ad1dd394d7eac9d8dab726b895b1ee774fdcabc8031063ecfa41c71a9f03ad23" +"904cc056f17c76a1059c43faffe30dfd157fdfd7d792e162bf7a889109550a0fc4c41523" +"2af0c0d72dcbc2595299e1a1c2aeae549f7970e994c15e0ab02f113d740d38c32a4d8ec0" +"79cd099d37d954ab7ef2800902cdf7c7a19fb14b3c98aaf4c6ad93fe9a9bc7a61229828e" +"55ad4d6270d1bdbca9975d450f9be91e5699bd7ee22e8c9c22e355cf1f6793f3551cb510" +"c1d5cd363bdf8cab063e6e49a6383221f1188d64692c1f84c910a696de2e72fb9886193f" +"61ab6b41ad0ea894d37ff1261bf1fd1f187e0d0c38ab223d99ec6d6b1e6b079fc305e24e" +"2d9500c98676e2d587434495d6e107b193c06fb12d5d8eaa7b0c001d08f4d91cae5bdcae" +"6624ee755e95ca8e4c5ef5b903d7f5ba438abeffd6f16d82d88138f157e7a50d1c91fb50" +"c770f6d222fcbf6daf791b1f8379e3b157a3b496ddb2e71650c1c4ac4fc5f2aceb5b3228" +"ffc44e15c02d4baa9434e60928a93f21bc91cfd3c2719f53a8c9bcd2f2dee65a8bbc88f9" +"5d7ced211fc3b04f6e8b74eb2026d66fd57fa0cccea43f0a0381782e6fee5660afed674d" +"cb2c28cb54d2bdbbaf78e534b0742ede6b5e659b46cd516d5362a194dd0822f6417935c4" +"ff05815b118fe5687cd8b050240015cfe449d9dfde1f4fdb105586e429b2c1849aac2791" +"ef73bc54603190eba39037ec057e784bb92d497e705dfcde2addb3514b4f1926f12d5440" +"850935779019b23bd0f2977a8c9478c424a7eaaeec04f3743a77bee2bec3937412e707bc" +"92a070046e2f9c35fe5cc3f755bbb91a182e683591ab7e8cff40633730546e81522f588f" +"07bdf142b78e115d2a22d2eb5664fcdb7574c1ee5ba9abd307d7d29078cd5223c222fc69" +"60324c40cc639be84dad96b01059efce7b08538ebef89bafab834609c7e82774a14e5be6" +"62067edba6111efa8ae270f5066442b17e3f31a793581c8a3f96d92921ec26981594e28a" +"08987d020b97ad2ba5c662836e35fd3fd954bcec52b579528913959d0d942fbf1c4b9910" +"ba010c3700359a4eb7616541257f0f7727cc71b580cc903f718ecc408a315b6bbfa7f6e3" +"beb9d258804bd2731ee2fb75e763281baf1effc4690a23d5f952ab5d4311d4f5885af2eb" +"f27cad9f6d84692cb903064bbd11ca751f919b4811b7722c6ec80c360521e34d357b5c8b" +"ba6d42e5c632730f53add99ab8aa9c607b6796216753086ede158bc670d04900aca66ce8" +"357bd72d19fb147b5fde8ee4df6a0184573a2e65ba3fd3a0cb04dac5eb36d17d2f639a6e" +"b602645f3ab4da9de4c9999d6506e8e242a5a3216f9e79a4202558ecdc74249ad3caaf90" +"71b4e653338b48b3ba3e9daf1e51e49384268d63f37ce87c6335de79175cdf542d661bcd" +"74b8f5107d6ab492f54b7c3c31257ecb0b426b77ed2e2ed22bbfdaf49653e1d54e5988fa" +"d71397546f9955659f22b3a4117fc823a1e87d6fb6fb8ab7d302a1316975e8baf0c0adbd" +"35455655f6a596b6ac3be7c9a8ea34166119d5e70dfbc1aa6e14ff98eff95e94ef576656" +"5d368ec8857fb0b029bcb990d420a5ca6bc7ab08053eb4dbfc4612a345d56faefc5e03a4" +"43520b224de776a5b618e1aa16edc513d5fcefcd413031b0ddc958a6fca45d108fbde065" +"3cf2d11cb00a71cd35f57993875598b4e33e2384623a0986859105d511c717c21d6534bf" +"69fd3d7cf1682e4fc25298d90df951e77a316996beac61bb7078988118c906548af92cfe" +"72cd4b102ffad584e5e721a0cdb5621ed07dda8955d84bea57a5afa4ba06289ddfac3a9e" +"765538fd9392fc7904cedb65e38cd90967f01845ff819777a22d199f608e62c13e6ba98b" +"40824b38c784bdb41d62c4014fc7e8d93be52695e975e54d1ff92b412f451177143d74a6" +"bde0ee53a986043ab465a1ef315ac4c538e775ef4178fde5f2ea560a364de18b8fe9578a" +"ad80027c3fd32dcf0967d9d03789b1cdf19040762f626289cf3af8afe5a8e0a152d9258e" +"981872c1ec95cd7f8d65812e55cb5cbd8db61b3f068a23d9652372dfbf18d43a663c5a0d" +"026b0898e383ce5c95b0ba7fb5ed6b7304c7c9d3ba64f38d1dc579465148ccfa7271f2e3" +"e0e97e9ddac7c0874f0f396cf07851638a734df393687b7b0343afd1652ff32a2da17b3a" +"4c99d79c02256c73f32625527e5666594a8a42a12135eddb022e743371b3ab7b12ad6785" +"7635eed03558ac673d17280769b2368056276d5d72f5dbc75525f8a7558bd90b544aa6cb" +"dd964e6c70be79441969bfdf471f17a2dc0c92", + "6144c4786145852e2a01b20604c369d1b9721019", + + 14768, +"c9bed88d93806b89c2d028866842e6542ab88c895228c96c1f9f05125f8697c7402538b0" +"6465b7ae33daef847500f73d20c598c86e4804e633e1c4466e61f3ed1e9baadc5723bbed" +"9455a2ff4f99b852cfe6aa3442852ade0b18e4995ddab4250928165a9441de108d4a293d" +"1d95935de022aa17f366a31d4f4c4c54557a4235a9d56473444787ddc5c06c87087aef24" +"fa8280b7ac74d76ba685e4be7dc705e5a8a97c6c8fbd201ee5bf522438d23371c60c155d" +"93352f8fb8cc9421fe4b66ffabad46909c2c1099944fc55ed424c90aecca4f50d0331153" +"2e2844c3ff8ecb495de7ab26941cbf177b79ad7b05f918b713c417da8cf6e67db0a2dcee" +"a9179d8d636191759e13955f4244f0c4f2d88842e3015641ef0417d6e54144e8246e4591" +"6823e2c6e39bfa3b90b97781c44981710689f2ce20e70a26760d65f9971b291e12338461" +"8b3b56710dde2afaa2d46b0e2164d5c9482729350a0e256b2aa6b3fb099b618ebd7c11ca" +"62bdf176b502aedfdf9be57a8e4adbca4a4d6d8407984af2f6635f95a1e4930e375eb53f" +"245ab2ade5340c281bda87afded1268e537955c9819168bd60fd440533c75c9b1865e03f" +"de3a301d165f97aa6da236cf39cf3e49512f6350224f8d76ff02d0d3b9a99e5f70b23b9f" +"a85f72849fc98790df246c3a0f4437940e60d42b4317f72e2eb055d343a614f7f9648005" +"1e4dff186dff476462d9ced24dbb82eaa60cbbf6a0026e64001da36d30f529f48f3688b1" +"0ce9378ef3f50f5106e5007cd0eb037136254fda4f20d048769bd51a9d8d09a1e469a482" +"6aa0e25b6267b5a96abcb6e919a362fdd7b683d2f2dcec40ee5969311c07f6066ee22f36" +"89ca08381c85bea470040e9541e7a451cd43d62c2aa292a9dc4b95e3a7c4de2ba29663f3" +"8d5002eb64ceba6934bb1b0e2e55fba7fa706b514ebeeae1be4dd882d6512da066246a05" +"1d8bd042593bd0513e9cc47806ccdc7097e75bc75b8603834c85cd084e0ade3cc2c2b7e8" +"586eac62249f9769f5bdcd50e24e515f257548762db9adf3ee0846d67cfcd723d85d9588" +"09e6dd406f4c2637557c356fc52490a2a0763429ee298a1c72c098bb810e740c15faffc6" +"1e80cf6e18f86dc0e29bc150ce43ca71f5729356cd966277fd8b32366f6263c3a761b13d" +"544a631a25e1c4c8dea8d794abed47ccb4069d20f1dcb54e40a673ffb5f7b2eb31fb7d44" +"36fd8252f92dc35bb9a18fc55099b17e0807e79caf4f9641ee4bbbc2d6922508bcfae236" +"475bf78bc796548bc8d60659e816af68e5e43352fa64b5086c97c22c60ddcbbbefb9d9ef" +"7cd57c64454604793910f4f90aedb4fb824a86061a93bb79c9b0272a1ad0d24e8165f099" +"ef6f14a6a4fea09845f280022e061804090d7ab79f7bddcbef264b6f7d4e9971eddb9ca7" +"d0e79a8dbe7cff2fa59f514a608d66ae8c44d5e69745aa1b19995e366812064567d3ca20" +"9e12994c901d1b1f489be7253615f7c339b5581afd4d262e879ab8480ecb18990d3db61f" +"96895dcde9c065e645f52baafefcbe34d072dba373fd1c786fd56c3f3284be7260eaff9a" +"6a8348b762ed59e20ea443313b1164db53c3989c32fcae5b366f190b9548e8cff46df961" +"350369b490354ed8e530a91f5072967eff45c63540862fb2deab02b3ae05deac65414368" +"ac3549f277da92b692947de47cba9c1579526931e31c3490c1d3605f9bafcf468c2e9b47" +"981407ea40b0b59754621943095a2d4f4ba266ac545fe7447e54f69555a7ac9ff1e8f001" +"834fa65f2d4523061726e4d3bf4680519032dc21b7389e9f3229e4c2295d354482f8b803" +"b06ca3a8cb3ff786e60f6bc59dd3a5bfed63b0aa493bab78e97bbefb6633534d84de826f" +"4e2ccc3069050d50a2caace6c9de15ffc2656988d94b736e5688df0351a3a6a4c875cd99" +"ef304f3cc7a0585df2b0b3e6c62f86bba0d43de47b80c4eec1c4f98e60a36188219919cf" +"36dc10ee11e174a67d226ad9e71f02a7fca26ad67a4862773f3defc6a747545314063e5f" +"ce7a3f890ec57daa5532acfd027739832437c8a58dcbe11c2842e60e8ca64979d081fbd5" +"a1a028f59317212fb5869abc689a156171d69e4f4c93b949c3459904c00192d3603cd184" +"48d64b843c57f34aee7830f313e58e2abc41b44be46a96c845ffebcb7120e21d1d751046" +"c072adf65dd901a39c8019742054be5e159ea88d0885ee05fcd4c189bafe5abb68603186" +"5dc570b9342fa7f41fd5c1c87e68371ab19a83c82ae1d890c678102d5da8e6c29845657c" +"027ba07362cba4d24950ab38e747925e22ce8df9eaec1ae2c6d23374b360c8352feb6cb9" +"913e4fc49bde6caf5293030d0d234a8ecd616023cc668262591f812de208738e5336a9e6" +"9f9be2479b86be1e1369761518dfc93797ed3a55308878a944581eba50bc9c7f7a0e75c7" +"6a28acd95b277857726f3f684eefc215e0a696f47d65d30431d710d957c08ef96682b385" +"0ee5ba1c8417aafc1af2846a127ec155b4b7fb369e90eb3a5c3793a3389bbc6b532ca32b" +"f5e1f03c2280e71c6e1ae21312d4ff163eee16ebb1fdee8e887bb0d453829b4e6ed5fa70" +"8f2053f29b81e277be46", + "a757ead499a6ec3d8ab9814f839117354ae563c8" +}; + +void test_sha1(void) +{ + unsigned char buffer[4000]; + int i; + for (i=0; i < sizeof(sha1_tests) / sizeof(sha1_tests[0]); ++i) { + stb_uint len = sha1_tests[i].length / 8; + unsigned char digest[20], fdig[20]; + unsigned int h; + assert(len <= sizeof(buffer)); + assert(strlen(sha1_tests[i].message) == len*2); + assert(strlen(sha1_tests[i].digest) == 20 * 2); + for (h=0; h < len; ++h) { + char v[3]; + v[0] = sha1_tests[i].message[h*2]; + v[1] = sha1_tests[i].message[h*2+1]; + v[2] = 0; + buffer[h] = (unsigned char) strtol(v, NULL, 16); + } + stb_sha1(digest, buffer, len); + for (h=0; h < 20; ++h) { + char v[3]; + int res; + v[0] = sha1_tests[i].digest[h*2]; + v[1] = sha1_tests[i].digest[h*2+1]; + v[2] = 0; + res = digest[h] == strtol(v, NULL, 16); + c(res, sha1_tests[i].digest); + if (!res) + break; + } + { + int z; + FILE *f = fopen("data/test.bin", "wb"); + if (!f) stb_fatal("Couldn't write to test.bin"); + fwrite(buffer, len, 1, f); + fclose(f); + #ifdef _WIN32 + z = stb_sha1_file(fdig, "data/test.bin"); + if (!z) stb_fatal("Couldn't digest test.bin"); + c(memcmp(digest, fdig, 20)==0, "stb_sh1_file"); + #endif + } + } +} + + +#if 0 + +stb__obj zero, one; + +void test_packed_floats(void) +{ + stb__obj *p; + float x,y,*q; + clock_t a,b,c; + int i; + stb_float_init(); + for (i=-10; i < 10; ++i) { + float f = (float) pow(10,i); + float g = f * 10; + float delta = (g - f) / 10000; + while (f < g) { + stb__obj z = stb_float(f); + float k = stb_getfloat(z); + float p = stb_getfloat_table(z); + assert((z & 1) == 1); + assert(f == k); + assert(k == p); + f += delta; + } + } + + zero = stb_float(0); + one = stb_float(1); + + p = malloc(8192 * 4); + for (i=0; i < 8192; ++i) + p[i] = stb_rand(); + for (i=0; i < 8192; ++i) + if ((stb_rand() & 31) < 28) + p[i] = zero; + + q = malloc(4 * 1024); + + a = clock(); + + x = y = 0; + for (i=0; i < 200000000; ++i) + q[i&1023] = stb_getfloat_table(p[i&8191]); + b = clock(); + for (i=0; i < 200000000; ++i) + q[i&1023] = stb_getfloat_table2(p[i&8191]); + c = clock(); + free(p); + + free(q); + + printf("Table: %d\nIFs: %d\n", b-a, c-b); +} +#endif + + +void do_compressor(int argc,char**argv) +{ + char *p; + int len; + + int window; + if (argc == 2) { + p = stb_file(argv[1], &len); + if (p) { + int dlen, clen = stb_compress_tofile("data/dummy.bin", p, len); + char *q = stb_decompress_fromfile("data/dummy.bin", &dlen); + + if (len != dlen) { + printf("FAILED %d -> %d\n", len, clen); + } else { + int z = memcmp(q,p,dlen); + if (z != 0) + printf("FAILED %d -> %d\n", len, clen); + else + printf("%d -> %d\n", len, clen); + } + } + return; + } + + window = atoi(argv[1]); + if (window && argc == 4) { + p = stb_file(argv[3], &len); + if (p) { + stb_compress_hashsize(window); + stb_compress_tofile(argv[2], p, len); + } + } else if (argc == 3) { + p = stb_decompress_fromfile(argv[2], &len); + if (p) { + FILE *f = fopen(argv[1], "wb"); + fwrite(p,1,len,f); + fclose(f); + } else { + fprintf(stderr, "FAILED.\n"); + } + } else { + fprintf(stderr, "Usage: stb \n" + " or stb \n"); + } +} + +#if 0 +// naive backtracking implementation +int wildmatch(char *expr, char *candidate) +{ + while(*expr) { + if (*expr == '?') { + if (!*candidate) return 0; + ++candidate; + ++expr; + } else if (*expr == '*') { + ++expr; + while (*expr == '*' || *expr =='?') ++expr; + // '*' at end of expression matches anything + if (!*expr) return 1; + // now scan candidate 'til first match + while (*candidate) { + if (*candidate == *expr) { + // check this candidate + if (stb_wildmatch(expr+1, candidate+1)) + return 1; + // if not, then backtrack + } + ++candidate; + } + } else { + if (*expr != *candidate) + return 0; + ++expr, ++candidate; + } + } + return *candidate != 0; +} + +int stb_matcher_find_slow(stb_matcher *m, char *str) +{ + int result = 1; + int i,j,y,z; + uint16 *previous = NULL; + uint16 *current = NULL; + uint16 *temp; + + stb_arr_setsize(previous, 4); + stb_arr_setsize(current, 4); + + previous = stb__add_if_inactive(m, previous, m->start_node); + previous = stb__eps_closure(m,previous); + if (stb__clear_goalcheck(m, previous)) + goto done; + + while (*str) { + y = stb_arr_len(previous); + for (i=0; i < y; ++i) { + stb_nfa_node *n = &m->nodes[previous[i]]; + z = stb_arr_len(n->out); + for (j=0; j < z; ++j) { + if (n->out[j].match == *str) + current = stb__add_if_inactive(m, current, n->out[j].node); + else if (n->out[j].match == -1) { + if (*str != '\n') + current = stb__add_if_inactive(m, current, n->out[j].node); + } else if (n->out[j].match < -1) { + int z = -n->out[j].match - 2; + if (m->charset[(uint8) *str] & (1 << z)) + current = stb__add_if_inactive(m, current, n->out[j].node); + } + } + } + ++str; + stb_arr_setlen(previous, 0); + + temp = previous; + previous = current; + current = temp; + + if (!m->match_start) + previous = stb__add_if_inactive(m, previous, m->start_node); + previous = stb__eps_closure(m,previous); + if (stb__clear_goalcheck(m, previous)) + goto done; + } + + result=0; + +done: + stb_arr_free(previous); + stb_arr_free(current); + + return result; +} +#endif + + + + + + +////////////////////////////////////////////////////////////////////////// +// +// stb_parser +// +// Generates an LR(1) parser from a grammar, and can parse with it + + + +// Symbol representations +// +// Client: Internal: +// - c=0 e aka epsilon +// - c=1 $ aka end of string +// > 0 2<=c= 0 ? encode_term(x) : encode_nonterm(x)) + +stb_bitset **compute_first(short ** productions) +{ + int i, changed; + stb_bitset **first = malloc(sizeof(*first) * num_symbols); + + assert(symset); + for (i=0; i < num_symbols; ++i) + first[i] = stb_bitset_new(0, symset); + + for (i=END; i < first_nonterm; ++i) + stb_bitset_setbit(first[i], i); + + for (i=0; i < stb_arr_len(productions); ++i) { + if (productions[i][2] == 0) { + int nt = encode_nonterm(productions[i][0]); + stb_bitset_setbit(first[nt], EPS); + } + } + + do { + changed = 0; + for (i=0; i < stb_arr_len(productions); ++i) { + int j, nt = encode_nonterm(productions[i][0]); + for (j=2; productions[i][j]; ++j) { + int z = encode_symbol(productions[i][j]); + changed |= stb_bitset_unioneq_changed(first[nt], first[z], symset); + if (!stb_bitset_testbit(first[z], EPS)) + break; + } + if (!productions[i][j] && !stb_bitset_testbit(first[nt], EPS)) { + stb_bitset_setbit(first[nt], EPS); + changed = 1; + } + } + } while (changed); + return first; +} + +stb_bitset **compute_follow(short ** productions, stb_bitset **first, int start) +{ + int i,j,changed; + stb_bitset **follow = malloc(sizeof(*follow) * num_symbols); + + assert(symset); + for (i=0; i < num_symbols; ++i) + follow[i] = (i >= first_nonterm ? stb_bitset_new(0, symset) : NULL); + + stb_bitset_setbit(follow[start], END); + do { + changed = 0; + for (i=0; i < stb_arr_len(productions); ++i) { + int nt = encode_nonterm(productions[i][0]); + for (j=2; productions[i][j]; ++j) { + if (productions[i][j] < 0) { + int k,z = encode_nonterm(productions[i][j]); + for (k=j+1; productions[i][k]; ++k) { + int q = encode_symbol(productions[i][k]); + changed |= stb_bitset_unioneq_changed(follow[z], first[q], symset); + if (!stb_bitset_testbit(first[q], EPS)) + break; + } + if (!productions[i][k] == 0) + changed |= stb_bitset_unioneq_changed(follow[z], follow[nt], symset); + } + } + } + } while (changed); + + for (i=first_nonterm; i < num_symbols; ++i) + stb_bitset_clearbit(follow[i], EPS); + + return follow; +} + +void first_for_prod_plus_sym(stb_bitset **first, stb_bitset *out, short *prod, int symbol) +{ + stb_bitset_clearall(out, symset); + for(;*prod;++prod) { + int z = encode_symbol(*prod); + stb_bitset_unioneq_changed(out, first[z], symset); + if (!stb_bitset_testbit(first[z], EPS)) + return; + } + stb_bitset_unioneq_changed(out, first[symbol], symset); +} + +#define Item(p,c,t) ((void *) (((t) << 18) + ((c) << 12) + ((p) << 2))) +#define ItemProd(i) ((((uint32) (i)) >> 2) & 1023) +#define ItemCursor(i) ((((uint32) (i)) >> 12) & 63) +#define ItemLookahead(i) (((uint32) (i)) >> 18) + +static void pc(stb_ps *p) +{ +} + +typedef struct +{ + short *prod; + int prod_num; +} ProdRef; + +typedef struct +{ + stb_bitset **first; + stb_bitset **follow; + short ** prod; + ProdRef ** prod_by_nt; +} Grammar; + +stb_ps *itemset_closure(Grammar g, stb_ps *set) +{ + stb_bitset *lookahead; + int changed,i,j,k, list_len; + if (set == NULL) return set; + lookahead = stb_bitset_new(0, symset); + do { + void **list = stb_ps_getlist(set, &list_len); + changed = 0; + for (i=0; i < list_len; ++i) { + ProdRef *prod; + int nt, *looklist; + int p = ItemProd(list[i]), c = ItemCursor(list[i]), t = ItemLookahead(list[i]); + if (g.prod[p][c] >= 0) continue; + nt = encode_nonterm(g.prod[p][c]); + first_for_prod_plus_sym(g.first, lookahead, g.prod[p]+c+1, t); + looklist = stb_bitset_getlist(lookahead, 1, first_nonterm); + + prod = g.prod_by_nt[nt]; + for (j=0; j < stb_arr_len(prod); ++j) { + assert(prod[j].prod[0] == g.prod[p][c]); + // matched production; now iterate terminals + for (k=0; k < stb_arr_len(looklist); ++k) { + void *item = Item(prod[j].prod_num,2,looklist[k]); + if (!stb_ps_find(set, item)) { + changed = 1; + set = stb_ps_add(set, item); + pc(set); + } + } + } + stb_arr_free(looklist); + } + free(list); + } while (changed); + free(lookahead); + return set; +} + +stb_ps *itemset_goto(Grammar g, stb_ps *set, int sym) +{ + int i, listlen; + void **list = stb_ps_fastlist(set, &listlen); + stb_ps *out = NULL; + for (i=0; i < listlen; ++i) { + int p,c; + if (!stb_ps_fastlist_valid(list[i])) continue; + p = ItemProd(list[i]), c = ItemCursor(list[i]); + if (encode_symbol(g.prod[p][c]) == sym) { + void *z = Item(p,c+1,ItemLookahead(list[i])); + if (!stb_ps_find(out, z)) + out = stb_ps_add(out, z); + pc(out); + } + } + return itemset_closure(g, out); +} + +void itemset_all_nextsym(Grammar g, stb_bitset *out, stb_ps *set) +{ + int i, listlen; + void **list = stb_ps_fastlist(set, &listlen); + stb_bitset_clearall(out, symset); + pc(set); + for (i=0; i < listlen; ++i) { + if (stb_ps_fastlist_valid(list[i])) { + int p = ItemProd(list[i]); + int c = ItemCursor(list[i]); + if (g.prod[p][c]) + stb_bitset_setbit(out, encode_symbol(g.prod[p][c])); + } + } +} + +stb_ps ** generate_items(Grammar g, int start_prod) +{ + stb_ps ** all=NULL; + int i,j,k; + stb_bitset *try = stb_bitset_new(0,symset); + stb_ps *set = NULL; + void *item = Item(start_prod, 2, END); + set = stb_ps_add(set, item); + pc(set); + set = itemset_closure(g, set); + pc(set); + stb_arr_push(all, set); + for (i = 0; i < stb_arr_len(all); ++i) { + // only try symbols that appear in all[i]... there's a smarter way to do this, + // which is to take all[i], and divide it up by symbol + pc(all[i]); + itemset_all_nextsym(g, try, all[i]); + for (j = 1; j < num_symbols; ++j) { + if (stb_bitset_testbit(try, j)) { + stb_ps *out; + if (stb_arr_len(all) > 4) pc(all[4]); + if (i == 1 && j == 29) { + if (stb_arr_len(all) > 4) pc(all[4]); + out = itemset_goto(g, all[i], j); + if (stb_arr_len(all) > 4) pc(all[4]); + } else + out = itemset_goto(g, all[i], j); + pc(out); + if (stb_arr_len(all) > 4) pc(all[4]); + if (out != NULL) { + // add it to the array if it's not already there + for (k=0; k < stb_arr_len(all); ++k) + if (stb_ps_eq(all[k], out)) + break; + if (k == stb_arr_len(all)) { + stb_arr_push(all, out); + pc(out); + if (stb_arr_len(all) > 4) pc(all[4]); + } else + stb_ps_delete(out); + } + } + } + } + free(try); + return all; +} + +typedef struct +{ + int num_stack; + int function; +} Reduction; + +typedef struct +{ + short *encode_term; + Reduction *reductions; + short **action_goto; // terminals are action, nonterminals are goto + int start; + int end_term; +} Parser; + +enum +{ + A_error, A_accept, A_shift, A_reduce, A_conflict +}; + +typedef struct +{ + uint8 type; + uint8 cursor; + short prod; + short value; +} Action; + +Parser *parser_create(short **productions, int num_prod, int start_nt, int end_term) +{ + short *mini_rule = malloc(4 * sizeof(mini_rule[0])); + Action *actions; + Grammar g; + stb_ps ** sets; + Parser *p = malloc(sizeof(*p)); + int i,j,n; + stb_bitset *mapped; + int min_s=0, max_s=0, termset, ntset, num_states, num_reductions, init_prod; + + int synth_start; + + // remap sparse terminals and nonterminals + + for (i=0; i < num_prod; ++i) { + for (j=2; productions[i][j]; ++j) { + if (productions[i][j] < min_s) min_s = productions[i][j]; + if (productions[i][j] > max_s) max_s = productions[i][j]; + } + } + synth_start = --min_s; + + termset = (max_s + 32) >> 5; + ntset = (~min_s + 32) >> 5; + memset(encode_term, 0, sizeof(encode_term)); + memset(encode_nonterm, 0, sizeof(encode_nonterm)); + + mapped = stb_bitset_new(0, termset); + n = 2; + for (i=0; i < num_prod; ++i) + for (j=2; productions[i][j]; ++j) + if (productions[i][j] > 0) + if (!stb_bitset_testbit(mapped, productions[i][j])) { + stb_bitset_setbit(mapped, productions[i][j]); + encode_term[productions[i][j]] = n++; + } + free(mapped); + + first_nonterm = n; + + mapped = stb_bitset_new(0, ntset); + for (i=0; i < num_prod; ++i) + for (j=2; productions[i][j]; ++j) + if (productions[i][j] < 0) + if (!stb_bitset_testbit(mapped, ~productions[i][j])) { + stb_bitset_setbit(mapped, ~productions[i][j]); + encode_nonterm[~productions[i][j]] = n++; + } + free(mapped); + + // add a special start state for internal processing + p->start = n++; + encode_nonterm[synth_start] = p->start; + mini_rule[0] = synth_start; + mini_rule[1] = -32768; + mini_rule[2] = start_nt; + mini_rule[3] = 0; + + p->end_term = end_term; + + num_symbols = n; + + // create tables + g.prod = NULL; + g.prod_by_nt = malloc(num_symbols * sizeof(g.prod_by_nt[0])); + for (i=0; i < num_symbols; ++i) + g.prod_by_nt[i] = NULL; + + for (i=0; i < num_prod; ++i) { + stb_arr_push(g.prod, productions[i]); + } + init_prod = stb_arr_len(g.prod); + stb_arr_push(g.prod, mini_rule); + + num_reductions = stb_arr_len(g.prod); + p->reductions = malloc(num_reductions * sizeof(*p->reductions)); + + symset = (num_symbols + 31) >> 5; + g.first = compute_first(g.prod); + g.follow = compute_follow(g.prod, g.first, p->start); + + for (i=0; i < stb_arr_len(g.prod); ++i) { + ProdRef pr = { g.prod[i], i }; + stb_arr_push(g.prod_by_nt[encode_nonterm(g.prod[i][0])], pr); + } + + sets = generate_items(g, init_prod); + + num_states = stb_arr_len(sets); + // now generate tables + + actions = malloc(sizeof(*actions) * first_nonterm); + p->action_goto = (short **) stb_array_block_alloc(num_states, sizeof(short) * num_symbols); + for (i=0; i < num_states; ++i) { + int j,n; + void **list = stb_ps_getlist(sets[i], &n); + memset(actions, 0, sizeof(*actions) * first_nonterm); + for (j=0; j < n; ++j) { + int p = ItemProd(list[j]), c = ItemCursor(list[j]), t = ItemLookahead(list[j]); + if (g.prod[p][c] == 0) { + if (p == init_prod) { + // @TODO: check for conflicts + assert(actions[t].type == A_error || actions[t].type == A_accept); + actions[t].type = A_accept; + } else { + // reduce production p + if (actions[t].type == A_reduce) { + // is it the same reduction we already have? + if (actions[t].prod != p) { + // no, it's a reduce-reduce conflict! + printf("Reduce-reduce conflict for rule %d and %d, lookahead %d\n", p, actions[t].prod, t); + // @TODO: use precedence + actions[t].type = A_conflict; + } + } else if (actions[t].type == A_shift) { + printf("Shift-reduce conflict for rule %d and %d, lookahead %d\n", actions[t].prod, p, t); + actions[t].type = A_conflict; + } else if (actions[t].type == A_accept) { + assert(0); + } else if (actions[t].type == A_error) { + actions[t].type = A_reduce; + actions[t].prod = p; + } + } + } else if (g.prod[p][c] > 0) { + int a = encode_symbol(g.prod[p][c]), k; + stb_ps *out = itemset_goto(g, sets[i], a); + for (k=0; k < stb_arr_len(sets); ++k) + if (stb_ps_eq(sets[k], out)) + break; + assert(k < stb_arr_len(sets)); + // shift k + if (actions[a].type == A_shift) { + if (actions[a].value != k) { + printf("Shift-shift conflict! Rule %d and %d with lookahead %d/%d\n", actions[a].prod, p, a,t); + actions[a].type = A_conflict; + } + } else if (actions[a].type == A_reduce) { + printf("Shift-reduce conflict for rule %d and %d, lookahead %d/%d\n", p, actions[a].prod, a,t); + actions[a].type = A_conflict; + } else if (actions[a].type == A_accept) { + assert(0); + } else if (actions[a].type == A_error) { + actions[a].type = A_shift; + actions[a].prod = p; + actions[a].cursor = c; + actions[a].value = k; + } + } + } + // @TODO: recompile actions into p->action_goto + } + + free(mini_rule); + stb_pointer_array_free(g.first , num_symbols); free(g.first ); + stb_pointer_array_free(g.follow, num_symbols); free(g.follow); + stb_arr_free(g.prod); + for (i=0; i < num_symbols; ++i) + stb_arr_free(g.prod_by_nt[i]); + free(g.prod_by_nt); + for (i=0; i < stb_arr_len(sets); ++i) + stb_ps_delete(sets[i]); + stb_arr_free(sets); + + return p; +} + +void parser_destroy(Parser *p) +{ + free(p); +} + +#if 0 +enum nonterm +{ + N_globals = -50, + N_global, N_vardef, N_varinitlist, N_varinit, N_funcdef, N_optid, N_optparamlist, + N_paramlist, N_param, N_optinit, N_optcomma, N_statements, N_statement, + N_optexpr, N_assign, N_if, N_ifcore, N_else, N_dictdef, N_dictdef2, + N_dictdefitem, N_expr, + N__last +}; + +short grammar[][10] = +{ + { N_globals , 0, N_globals, N_global }, + { N_globals , 0 }, + { N_global , 0, N_vardef }, + { N_global , 0, N_funcdef }, + { N_vardef , 0, ST_var, N_varinitlist, }, + { N_varinitlist, 0, N_varinitlist, ',', N_varinit }, + { N_varinitlist, 0, N_varinit, }, + { N_varinit , 0, ST_id, N_optinit, }, + { N_funcdef , 0, ST_func, N_optid, '(', N_optparamlist, ')', N_statements, ST_end }, + { N_optid , 0, ST_id }, + { N_optid , 0, }, + { N_optparamlist, 0, }, + { N_optparamlist, 0, N_paramlist, N_optcomma }, + { N_paramlist , 0, N_paramlist, ',', N_param }, + { N_paramlist , 0, N_param }, + { N_param , 0, ST_id, N_optinit }, + { N_optinit , 0, '=', N_expr }, + { N_optinit , 0, }, + { N_optcomma , 0, ',' }, + { N_optcomma , 0, }, + { N_statements , 0, N_statements, N_statement }, + { N_statement , 0, N_statement, ';' }, + { N_statement , 0, N_varinit }, + { N_statement , 0, ST_return, N_expr }, + { N_statement , 0, ST_break , N_optexpr }, + { N_optexpr , 0, N_expr }, + { N_optexpr , 0, }, + { N_statement , 0, ST_continue }, + { N_statement , 0, N_assign }, + { N_assign , 0, N_expr, '=', N_assign }, + //{ N_assign , 0, N_expr }, + { N_statement , 0, ST_while, N_expr, N_statements, ST_end }, + { N_statement , 0, ST_if, N_if, }, + { N_if , 0, N_ifcore, ST_end, }, + { N_ifcore , 0, N_expr, ST_then, N_statements, N_else, ST_end }, + { N_else , 0, ST_elseif, N_ifcore }, + { N_else , 0, ST_else, N_statements }, + { N_else , 0, }, + { N_dictdef , 0, N_dictdef2, N_optcomma }, + { N_dictdef2 , 0, N_dictdef2, ',', N_dictdefitem }, + { N_dictdef2 , 0, N_dictdefitem }, + { N_dictdefitem, 0, ST_id, '=', N_expr }, + { N_dictdefitem, 0, N_expr }, + { N_expr , 0, ST_number }, + { N_expr , 0, ST_string }, + { N_expr , 0, ST_id }, + { N_expr , 0, N_funcdef }, + { N_expr , 0, '-', N_expr }, + { N_expr , 0, '{', N_dictdef, '}' }, + { N_expr , 0, '(', N_expr, ')' }, + { N_expr , 0, N_expr, '.', ST_id }, + { N_expr , 0, N_expr, '[', N_expr, ']' }, + { N_expr , 0, N_expr, '(', N_dictdef, ')' }, +#if 0 +#define BINOP(op) { N_expr, 0, N_expr, op, N_expr } + BINOP(ST_and), BINOP(ST_or), BINOP(ST_eq), BINOP(ST_ne), + BINOP(ST_le), BINOP(ST_ge), BINOP('>') , BINOP('<' ), + BINOP('&'), BINOP('|'), BINOP('^'), BINOP('+'), BINOP('-'), + BINOP('*'), BINOP('/'), BINOP('%'), +#undef BINOP +#endif +}; + +short *grammar_list[stb_arrcount(grammar)]; + +void test_parser_generator(void) +{ + Parser *p; + int i; + assert(N__last <= 0); + for (i=0; i < stb_arrcount(grammar); ++i) + grammar_list[i] = grammar[i]; + p = parser_create(grammar_list, stb_arrcount(grammar), N_globals, 0); + parser_destroy(p); +} +#endif + + +#if 0 +// stb_threadtest.c + + +#include +#define STB_DEFINE +//#define STB_THREAD_TEST +#include "../stb.h" + +#define NUM_WORK 100 + +void *work_consumer(void *p) +{ + stb__thread_sleep(20); + return NULL; +} + +int pass; +stb_threadqueue *tq1, *tq2, *tq3, *tq4; +volatile float t1,t2; + +// with windows.h +// Worked correctly with 100,000,000 enqueue/dequeue WAITLESS +// (770 passes, 170000 per pass) +// Worked correctly with 2,500,000 enqueue/dequeue !WAITLESS +// (15 passes, 170000 per pass) +// Worked correctly with 1,500,000 enqueue/dequeue WAITLESS && STB_THREAD_TEST +// (9 passes, 170000 per pass) +// without windows.h +// Worked correctly with 1,000,000 enqueue/dequeue WAITLESS && STB_THREAD_TEST +// (6 passes, 170000 per pass) +// Worked correctly with 500,000 enqueue/dequeue !WAITLESS && STB_THREAD_TEST +// (3 passes, 170000 per pass) +// Worked correctly with 1,000,000 enqueue/dequeue WAITLESS +// (15 passes, 170000 per pass) +#define WAITLESS + +volatile int table[1000*1000*10]; + +void wait(int n) +{ +#ifndef WAITLESS + int j; + float y; + for (j=0; j < n; ++j) + y += 1 / (t1+j); + t2 = y; +#endif +} + +void *tq1_consumer(void *p) +{ + for(;;) { + int z; + float y = 0; + stb_threadq_get_block(tq1, &z); + wait(5000); + table[z] = pass; + } +} + +void *tq2_consumer(void *p) +{ + for(;;) { + int z; + if (stb_threadq_get(tq2, &z)) + table[z] = pass; + wait(1000); + } +} + +void *tq3_consumer(void *p) +{ + for(;;) { + int z; + stb_threadq_get_block(tq3, &z); + table[z] = pass; + wait(500); + } +} + +void *tq4_consumer(void *p) +{ + for (;;) { + int z; + stb_threadq_get_block(tq4, &z); + table[z] = pass; + wait(500); + } +} + +typedef struct +{ + int start, end; + stb_threadqueue *tq; + int delay; +} write_data; + +void *writer(void *q) +{ + int i; + write_data *p = (write_data *) q; + for (i=p->start; i < p->end; ++i) { + stb_threadq_add_block(p->tq, &i); + #ifndef WAITLESS + if (p->delay) stb__thread_sleep(p->delay); + else { + int j; + float z = 0; + for (j=0; j <= 20; ++j) + z += 1 / (t1+j); + t2 = z; + } + #endif + } + return NULL; +} + +write_data info[256]; +int pos; + +void start_writer(int z, int count, stb_threadqueue *tq, int delay) +{ + info[z].start = pos; + info[z].end = pos+count; + info[z].tq = tq; + info[z].delay = delay; + stb_create_thread(writer, &info[z]); + pos += count; +} + +int main(int argc, char **argv) +{ + int i; + stb_sync s = stb_sync_new(); + stb_sync_set_target(s, NUM_WORK+1); + stb_work_numthreads(2); + for (i=0; i < NUM_WORK; ++i) { + stb_work_reach(work_consumer, NULL, NULL, s); + } + printf("Started stb_work test.\n"); + + t1 = 1; + + // create the queues + tq1 = stb_threadq_new(4, 4, TRUE , TRUE); + tq2 = stb_threadq_new(4, 4, TRUE , FALSE); + tq3 = stb_threadq_new(4, 4, FALSE, TRUE); + tq4 = stb_threadq_new(4, 4, FALSE, FALSE); + + // start the consumers + stb_create_thread(tq1_consumer, NULL); + stb_create_thread(tq1_consumer, NULL); + stb_create_thread(tq1_consumer, NULL); + + stb_create_thread(tq2_consumer, NULL); + + stb_create_thread(tq3_consumer, NULL); + stb_create_thread(tq3_consumer, NULL); + stb_create_thread(tq3_consumer, NULL); + stb_create_thread(tq3_consumer, NULL); + stb_create_thread(tq3_consumer, NULL); + stb_create_thread(tq3_consumer, NULL); + stb_create_thread(tq3_consumer, NULL); + + stb_create_thread(tq4_consumer, NULL); + + for (pass=1; pass <= 5000; ++pass) { + int z = 0; + int last_n = -1; + int identical = 0; + pos = 0; + start_writer(z++, 50000, tq1, 0); + start_writer(z++, 50000, tq1, 0); + start_writer(z++, 50000, tq1, 0); + + start_writer(z++, 5000, tq2, 1); + start_writer(z++, 3000, tq2, 3); + start_writer(z++, 2000, tq2, 5); + + start_writer(z++, 5000, tq3, 3); + + start_writer(z++, 5000, tq4, 3); + #ifndef WAITLESS + stb__thread_sleep(8000); + #endif + for(;;) { + int n =0; + for (i=0; i < pos; ++i) { + if (table[i] == pass) + ++n; + } + if (n == pos) break; + if (n == last_n) { + ++identical; + if (identical == 3) { + printf("Problem slots:\n"); + for (i=0; i < pos; ++i) { + if (table[i] != pass) printf("%d ", i); + } + printf("\n"); + } else { + if (identical < 3) + printf("Processed %d of %d\n", n, pos); + else + printf("."); + } + } else { + identical = 0; + printf("Processed %d of %d\n", n, pos); + } + last_n = n; + #ifdef WAITLESS + stb__thread_sleep(750); + #else + stb__thread_sleep(3000); + #endif + } + printf("Finished pass %d\n", pass); + } + + stb_sync_reach_and_wait(s); + printf("stb_work test completed ok.\n"); + return 0; +} +#endif + + +#if 0 +////////////////////////////////////////////////////////////////////////////// +// +// collapse tree leaves up to parents until we only have N nodes +// useful for cmirror summaries + +typedef struct stb_summary_tree +{ + struct stb_summary_tree **children; + int num_children; + float weight; +} stb_summary_tree; + +STB_EXTERN void *stb_summarize_tree(void *tree, int limit, float reweight); + +#ifdef STB_DEFINE + +typedef struct stb_summary_tree2 +{ + STB__ARR(struct stb_summary_tree2 *) children; + int num_children; + float weight; + float weight_with_all_children; + float makes_target_weight; + float weight_at_target; + stb_summary_tree *original; + struct stb_summary_tree2 *target; + STB__ARR(struct stb_summary_tree2 *) targeters; +} stb_summary_tree2; + +static stb_summary_tree2 *stb__summarize_clone(stb_summary_tree *t) +{ + int i; + stb_summary_tree2 *s; + s = (stb_summary_tree2 *) malloc(sizeof(*s)); + s->original = t; + s->weight = t->weight; + s->weight_with_all_children = 0; + s->weight_at_target = 0; + s->target = NULL; + s->targeters = NULL; + s->num_children = t->num_children; + s->children = NULL; + for (i=0; i < s->num_children; ++i) + stb_arr_push(s->children, stb__summarize_clone(t->children[i])); + return s; +} + +static float stb__summarize_compute_targets(stb_summary_tree2 *parent, stb_summary_tree2 *node, float reweight, float weight) +{ + float total = 0; + if (node->weight == 0 && node->num_children == 1 && parent) { + node->target = parent; + return stb__summarize_compute_targets(parent, node->children[0], reweight, weight*reweight); + } else { + float total=0; + int i; + for (i=0; i < node->num_children; ++i) + total += stb__summarize_compute_targets(node, node->children[i], reweight, reweight); + node->weight_with_all_children = total + node->weight; + if (parent && node->weight_with_all_children) { + node->target = parent; + node->weight_at_target = node->weight_with_all_children * weight; + node->makes_target_weight = node->weight_at_target + parent->weight; + stb_arr_push(parent->targeters, node); + } else { + node->target = NULL; + node->weight_at_target = node->weight; + node->makes_target_weight = 0; + } + return node->weight_with_all_children * weight; + } +} + +static stb_summary_tree2 ** stb__summarize_make_array(STB__ARR(stb_summary_tree2 *) all, stb_summary_tree2 *tree) +{ + int i; + stb_arr_push(all, tree); + for (i=0; i < tree->num_children; ++i) + all = stb__summarize_make_array(all, tree->children[i]); + return all; +} + +typedef stb_summary_tree2 * stb__stree2; +stb_define_sort(stb__summarysort, stb__stree2, (*a)->makes_target_weight < (*b)->makes_target_weight) + +void *stb_summarize_tree(void *tree, int limit, float reweight) +{ + int i,j,k; + STB__ARR(stb_summary_tree *) ret=NULL; + STB__ARR(stb_summary_tree2 *) all=NULL; + + // first clone the tree so we can manipulate it + stb_summary_tree2 *t = stb__summarize_clone((stb_summary_tree *) tree); + if (reweight < 1) reweight = 1; + + // now compute how far up the tree each node would get pushed + // there's no value in pushing a node up to an empty node with + // only one child, so we keep pushing it up + stb__summarize_compute_targets(NULL, t, reweight, 1); + + all = stb__summarize_make_array(all, t); + + // now we want to iteratively find the smallest 'makes_target_weight', + // update that, and then fix all the others (which will be all descendents) + // to do this efficiently, we need a heap or a sorted binary tree + // what we have is an array. maybe we can insertion sort the array? + stb__summarysort(all, stb_arr_len(all)); + + for (i=0; i < stb_arr_len(all) - limit; ++i) { + stb_summary_tree2 *src, *dest; + src = all[i]; + dest = all[i]->target; + if (src->makes_target_weight == 0) continue; + assert(dest != NULL); + + for (k=0; k < stb_arr_len(all); ++k) + if (all[k] == dest) + break; + assert(k != stb_arr_len(all)); + assert(i < k); + + // move weight from all[i] to target + src->weight = dest->makes_target_weight; + src->weight = 0; + src->makes_target_weight = 0; + // recompute effect of other descendents + for (j=0; j < stb_arr_len(dest->targeters); ++j) { + if (dest->targeters[j]->weight) { + dest->targeters[j]->makes_target_weight = dest->weight + dest->targeters[j]->weight_at_target; + assert(dest->targeters[j]->makes_target_weight <= dest->weight_with_all_children); + } + } + STB_(stb__summarysort,_ins_sort)(all+i, stb_arr_len(all)-i); + } + // now the elements in [ i..stb_arr_len(all) ) are the relevant ones + for (; i < stb_arr_len(all); ++i) + stb_arr_push(ret, all[i]->original); + + // now free all our temp data + for (i=0; i < stb_arr_len(all); ++i) { + stb_arr_free(all[i]->children); + free(all[i]); + } + stb_arr_free(all); + return ret; +} +#endif + +#endif diff --git a/impeller/third_party/stb/stb/tests/stb.dsp b/impeller/third_party/stb/stb/tests/stb.dsp new file mode 100644 index 0000000000000..68c18069e3f48 --- /dev/null +++ b/impeller/third_party/stb/stb/tests/stb.dsp @@ -0,0 +1,192 @@ +# Microsoft Developer Studio Project File - Name="stb" - Package Owner=<4> +# Microsoft Developer Studio Generated Build File, Format Version 6.00 +# ** DO NOT EDIT ** + +# TARGTYPE "Win32 (x86) Console Application" 0x0103 + +CFG=stb - Win32 Debug +!MESSAGE This is not a valid makefile. To build this project using NMAKE, +!MESSAGE use the Export Makefile command and run +!MESSAGE +!MESSAGE NMAKE /f "stb.mak". +!MESSAGE +!MESSAGE You can specify a configuration when running NMAKE +!MESSAGE by defining the macro CFG on the command line. For example: +!MESSAGE +!MESSAGE NMAKE /f "stb.mak" CFG="stb - Win32 Debug" +!MESSAGE +!MESSAGE Possible choices for configuration are: +!MESSAGE +!MESSAGE "stb - Win32 Release" (based on "Win32 (x86) Console Application") +!MESSAGE "stb - Win32 Debug" (based on "Win32 (x86) Console Application") +!MESSAGE + +# Begin Project +# PROP AllowPerConfigDependencies 0 +# PROP Scc_ProjName "" +# PROP Scc_LocalPath "" +CPP=cl.exe +RSC=rc.exe + +!IF "$(CFG)" == "stb - Win32 Release" + +# PROP BASE Use_MFC 0 +# PROP BASE Use_Debug_Libraries 0 +# PROP BASE Output_Dir "Release" +# PROP BASE Intermediate_Dir "Release" +# PROP BASE Target_Dir "" +# PROP Use_MFC 0 +# PROP Use_Debug_Libraries 0 +# PROP Output_Dir "Release" +# PROP Intermediate_Dir "Release" +# PROP Ignore_Export_Lib 0 +# PROP Target_Dir "" +# ADD BASE CPP /nologo /W3 /GX /O2 /D "WIN32" /D "NDEBUG" /D "_CONSOLE" /D "_MBCS" /YX /FD /c +# ADD CPP /nologo /G6 /MT /W3 /GX /Z7 /O2 /Ob2 /I ".." /D "WIN32" /D "NDEBUG" /D "_CONSOLE" /D "_MBCS" /D "TT_TEST" /FD /c +# ADD BASE RSC /l 0x409 /d "NDEBUG" +# ADD RSC /l 0x409 /d "NDEBUG" +BSC32=bscmake.exe +# ADD BASE BSC32 /nologo +# ADD BSC32 /nologo +LINK32=link.exe +# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:console /machine:I386 +# ADD LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:console /debug /machine:I386 + +!ELSEIF "$(CFG)" == "stb - Win32 Debug" + +# PROP BASE Use_MFC 0 +# PROP BASE Use_Debug_Libraries 1 +# PROP BASE Output_Dir "Debug" +# PROP BASE Intermediate_Dir "Debug" +# PROP BASE Target_Dir "" +# PROP Use_MFC 0 +# PROP Use_Debug_Libraries 1 +# PROP Output_Dir "Debug" +# PROP Intermediate_Dir "Debug\stb" +# PROP Ignore_Export_Lib 0 +# PROP Target_Dir "" +# ADD BASE CPP /nologo /W3 /Gm /GX /ZI /Od /D "WIN32" /D "_DEBUG" /D "_CONSOLE" /D "_MBCS" /YX /FD /GZ /c +# ADD CPP /nologo /MTd /W3 /GX /Zi /Od /I ".." /D "WIN32" /D "_DEBUG" /D "_CONSOLE" /D "_MBCS" /D "MAIN_TEST" /FR /FD /GZ /c +# SUBTRACT CPP /YX +# ADD BASE RSC /l 0x409 /d "_DEBUG" +# ADD RSC /l 0x409 /d "_DEBUG" +BSC32=bscmake.exe +# ADD BASE BSC32 /nologo +# ADD BSC32 /nologo +LINK32=link.exe +# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:console /debug /machine:I386 /pdbtype:sept +# ADD LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:console /incremental:no /debug /machine:I386 /pdbtype:sept +# SUBTRACT LINK32 /force + +!ENDIF + +# Begin Target + +# Name "stb - Win32 Release" +# Name "stb - Win32 Debug" +# Begin Source File + +SOURCE=..\docs\other_libs.md +# End Source File +# Begin Source File + +SOURCE=.\stb.c +# End Source File +# Begin Source File + +SOURCE=..\stb.h +# End Source File +# Begin Source File + +SOURCE=..\stb_c_lexer.h +# End Source File +# Begin Source File + +SOURCE=..\stb_divide.h +# End Source File +# Begin Source File + +SOURCE=..\stb_dxt.h +# End Source File +# Begin Source File + +SOURCE=..\stb_easy_font.h +# End Source File +# Begin Source File + +SOURCE=..\stb_herringbone_wang_tile.h +# End Source File +# Begin Source File + +SOURCE=..\stb_image.h +# End Source File +# Begin Source File + +SOURCE=..\stb_image_resize.h +# End Source File +# Begin Source File + +SOURCE=..\stb_image_write.h +# End Source File +# Begin Source File + +SOURCE=..\stb_leakcheck.h +# End Source File +# Begin Source File + +SOURCE=..\stb_malloc.h +# End Source File +# Begin Source File + +SOURCE=..\stb_perlin.h +# End Source File +# Begin Source File + +SOURCE=..\stb_rect_pack.h +# End Source File +# Begin Source File + +SOURCE=..\stb_textedit.h +# End Source File +# Begin Source File + +SOURCE=..\stb_tilemap_editor.h +# End Source File +# Begin Source File + +SOURCE=..\stb_truetype.h +# End Source File +# Begin Source File + +SOURCE=..\stb_vorbis.c +# End Source File +# Begin Source File + +SOURCE=..\stb_voxel_render.h +# End Source File +# Begin Source File + +SOURCE=..\stretchy_buffer.h +# End Source File +# Begin Source File + +SOURCE=.\stretchy_buffer_test.c +# End Source File +# Begin Source File + +SOURCE=.\test_c_compilation.c +# End Source File +# Begin Source File + +SOURCE=.\test_truetype.c +# End Source File +# Begin Source File + +SOURCE=.\test_vorbis.c +# End Source File +# Begin Source File + +SOURCE=.\textedit_sample.c +# End Source File +# End Target +# End Project diff --git a/impeller/third_party/stb/stb/tests/stb.dsw b/impeller/third_party/stb/stb/tests/stb.dsw new file mode 100644 index 0000000000000..f73f9b1817a15 --- /dev/null +++ b/impeller/third_party/stb/stb/tests/stb.dsw @@ -0,0 +1,161 @@ +Microsoft Developer Studio Workspace File, Format Version 6.00 +# WARNING: DO NOT EDIT OR DELETE THIS WORKSPACE FILE! + +############################################################################### + +Project: "c_lexer_test"=.\c_lexer_test.dsp - Package Owner=<4> + +Package=<5> +{{{ +}}} + +Package=<4> +{{{ +}}} + +############################################################################### + +Project: "herringbone"=.\herringbone.dsp - Package Owner=<4> + +Package=<5> +{{{ +}}} + +Package=<4> +{{{ +}}} + +############################################################################### + +Project: "herringbone_map"=.\herringbone_map.dsp - Package Owner=<4> + +Package=<5> +{{{ +}}} + +Package=<4> +{{{ +}}} + +############################################################################### + +Project: "image_test"=.\image_test.dsp - Package Owner=<4> + +Package=<5> +{{{ +}}} + +Package=<4> +{{{ +}}} + +############################################################################### + +Project: "make_readme"=..\tools\make_readme.dsp - Package Owner=<4> + +Package=<5> +{{{ +}}} + +Package=<4> +{{{ +}}} + +############################################################################### + +Project: "resize"=.\resize.dsp - Package Owner=<4> + +Package=<5> +{{{ +}}} + +Package=<4> +{{{ +}}} + +############################################################################### + +Project: "stb"=.\stb.dsp - Package Owner=<4> + +Package=<5> +{{{ +}}} + +Package=<4> +{{{ + Begin Project Dependency + Project_Dep_Name stb_cpp + End Project Dependency + Begin Project Dependency + Project_Dep_Name image_test + End Project Dependency + Begin Project Dependency + Project_Dep_Name stretch_test + End Project Dependency + Begin Project Dependency + Project_Dep_Name c_lexer_test + End Project Dependency +}}} + +############################################################################### + +Project: "stb_cpp"=.\stb_cpp.dsp - Package Owner=<4> + +Package=<5> +{{{ +}}} + +Package=<4> +{{{ +}}} + +############################################################################### + +Project: "stretch_test"=.\stretch_test.dsp - Package Owner=<4> + +Package=<5> +{{{ +}}} + +Package=<4> +{{{ +}}} + +############################################################################### + +Project: "unicode"=..\tools\unicode\unicode.dsp - Package Owner=<4> + +Package=<5> +{{{ +}}} + +Package=<4> +{{{ +}}} + +############################################################################### + +Project: "vorbseek"=.\vorbseek\vorbseek.dsp - Package Owner=<4> + +Package=<5> +{{{ +}}} + +Package=<4> +{{{ +}}} + +############################################################################### + +Global: + +Package=<5> +{{{ +}}} + +Package=<3> +{{{ +}}} + +############################################################################### + diff --git a/impeller/third_party/stb/stb/tests/stb_cpp.cpp b/impeller/third_party/stb/stb/tests/stb_cpp.cpp new file mode 100644 index 0000000000000..57f2cfa14bcef --- /dev/null +++ b/impeller/third_party/stb/stb/tests/stb_cpp.cpp @@ -0,0 +1,82 @@ +#define WIN32_MEAN_AND_LEAN +#define WIN32_LEAN_AND_MEAN +//#include +#include +#define STB_STUA +#define STB_DEFINE +#define STB_NPTR +#define STB_ONLY +#include "stb.h" +//#include "stb_file.h" + +int count; +void c(int truth, char *error) +{ + if (!truth) { + fprintf(stderr, "Test failed: %s\n", error); + ++count; + } +} + +char *expects(stb_matcher *m, char *s, int result, int len, char *str) +{ + int res2,len2=0; + res2 = stb_lex(m, s, &len2); + c(result == res2 && len == len2, str); + return s + len; +} + +void test_lex(void) +{ + stb_matcher *m = stb_lex_matcher(); + // tok_en5 .3 20.1 20. .20 .1 + char *s = "tok_en5.3 20.1 20. .20.1"; + + stb_lex_item(m, "[a-zA-Z_][a-zA-Z0-9_]*", 1 ); + stb_lex_item(m, "[0-9]*\\.?[0-9]*" , 2 ); + stb_lex_item(m, "[\r\n\t ]+" , 3 ); + stb_lex_item(m, "." , -99 ); + s=expects(m,s,1,7, "stb_lex 1"); + s=expects(m,s,2,2, "stb_lex 2"); + s=expects(m,s,3,1, "stb_lex 3"); + s=expects(m,s,2,4, "stb_lex 4"); + s=expects(m,s,3,1, "stb_lex 5"); + s=expects(m,s,2,3, "stb_lex 6"); + s=expects(m,s,3,1, "stb_lex 7"); + s=expects(m,s,2,3, "stb_lex 8"); + s=expects(m,s,2,2, "stb_lex 9"); + s=expects(m,s,0,0, "stb_lex 10"); + stb_matcher_free(m); +} + +int main(int argc, char **argv) +{ + char *p; + p = "abcdefghijklmnopqrstuvwxyz"; + c(stb_ischar('c', p), "stb_ischar 1"); + c(stb_ischar('x', p), "stb_ischar 2"); + c(!stb_ischar('#', p), "stb_ischar 3"); + c(!stb_ischar('X', p), "stb_ischar 4"); + p = "0123456789"; + c(!stb_ischar('c', p), "stb_ischar 5"); + c(!stb_ischar('x', p), "stb_ischar 6"); + c(!stb_ischar('#', p), "stb_ischar 7"); + c(!stb_ischar('X', p), "stb_ischar 8"); + p = "#####"; + c(!stb_ischar('c', p), "stb_ischar a"); + c(!stb_ischar('x', p), "stb_ischar b"); + c(stb_ischar('#', p), "stb_ischar c"); + c(!stb_ischar('X', p), "stb_ischar d"); + p = "xXyY"; + c(!stb_ischar('c', p), "stb_ischar e"); + c(stb_ischar('x', p), "stb_ischar f"); + c(!stb_ischar('#', p), "stb_ischar g"); + c(stb_ischar('X', p), "stb_ischar h"); + + test_lex(); + + if (count) { + _getch(); + } + return 0; +} diff --git a/impeller/third_party/stb/stb/tests/stb_cpp.dsp b/impeller/third_party/stb/stb/tests/stb_cpp.dsp new file mode 100644 index 0000000000000..8bf9975aa73d1 --- /dev/null +++ b/impeller/third_party/stb/stb/tests/stb_cpp.dsp @@ -0,0 +1,98 @@ +# Microsoft Developer Studio Project File - Name="stb_cpp" - Package Owner=<4> +# Microsoft Developer Studio Generated Build File, Format Version 6.00 +# ** DO NOT EDIT ** + +# TARGTYPE "Win32 (x86) Console Application" 0x0103 + +CFG=stb_cpp - Win32 Debug +!MESSAGE This is not a valid makefile. To build this project using NMAKE, +!MESSAGE use the Export Makefile command and run +!MESSAGE +!MESSAGE NMAKE /f "stb_cpp.mak". +!MESSAGE +!MESSAGE You can specify a configuration when running NMAKE +!MESSAGE by defining the macro CFG on the command line. For example: +!MESSAGE +!MESSAGE NMAKE /f "stb_cpp.mak" CFG="stb_cpp - Win32 Debug" +!MESSAGE +!MESSAGE Possible choices for configuration are: +!MESSAGE +!MESSAGE "stb_cpp - Win32 Release" (based on "Win32 (x86) Console Application") +!MESSAGE "stb_cpp - Win32 Debug" (based on "Win32 (x86) Console Application") +!MESSAGE + +# Begin Project +# PROP AllowPerConfigDependencies 0 +# PROP Scc_ProjName "" +# PROP Scc_LocalPath "" +CPP=cl.exe +RSC=rc.exe + +!IF "$(CFG)" == "stb_cpp - Win32 Release" + +# PROP BASE Use_MFC 0 +# PROP BASE Use_Debug_Libraries 0 +# PROP BASE Output_Dir "Release" +# PROP BASE Intermediate_Dir "Release" +# PROP BASE Target_Dir "" +# PROP Use_MFC 0 +# PROP Use_Debug_Libraries 0 +# PROP Output_Dir "Release" +# PROP Intermediate_Dir "Release" +# PROP Target_Dir "" +# ADD BASE CPP /nologo /W3 /GX /O2 /D "WIN32" /D "NDEBUG" /D "_CONSOLE" /D "_MBCS" /YX /FD /c +# ADD CPP /nologo /MT /W3 /GX /O2 /I ".." /D "WIN32" /D "NDEBUG" /D "_CONSOLE" /D "_MBCS" /YX /FD /c +# ADD BASE RSC /l 0x409 /d "NDEBUG" +# ADD RSC /l 0x409 /d "NDEBUG" +BSC32=bscmake.exe +# ADD BASE BSC32 /nologo +# ADD BSC32 /nologo +LINK32=link.exe +# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:console /machine:I386 +# ADD LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:console /machine:I386 + +!ELSEIF "$(CFG)" == "stb_cpp - Win32 Debug" + +# PROP BASE Use_MFC 0 +# PROP BASE Use_Debug_Libraries 1 +# PROP BASE Output_Dir "Debug" +# PROP BASE Intermediate_Dir "Debug" +# PROP BASE Target_Dir "" +# PROP Use_MFC 0 +# PROP Use_Debug_Libraries 1 +# PROP Output_Dir "Debug" +# PROP Intermediate_Dir "Debug\stb_cpp" +# PROP Ignore_Export_Lib 0 +# PROP Target_Dir "" +# ADD BASE CPP /nologo /W3 /Gm /GX /ZI /Od /D "WIN32" /D "_DEBUG" /D "_CONSOLE" /D "_MBCS" /YX /FD /GZ /c +# ADD CPP /nologo /MTd /W3 /GX /Zd /Od /I ".." /D "WIN32" /D "_DEBUG" /D "_CONSOLE" /D "_MBCS" /FD /GZ /c +# SUBTRACT CPP /YX +# ADD BASE RSC /l 0x409 /d "_DEBUG" +# ADD RSC /l 0x409 /d "_DEBUG" +BSC32=bscmake.exe +# ADD BASE BSC32 /nologo +# ADD BSC32 /nologo +LINK32=link.exe +# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:console /debug /machine:I386 /pdbtype:sept +# ADD LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:console /debug /machine:I386 /pdbtype:sept + +!ENDIF + +# Begin Target + +# Name "stb_cpp - Win32 Release" +# Name "stb_cpp - Win32 Debug" +# Begin Source File + +SOURCE=.\stb_cpp.cpp +# End Source File +# Begin Source File + +SOURCE=..\stb_vorbis.c +# End Source File +# Begin Source File + +SOURCE=.\test_cpp_compilation.cpp +# End Source File +# End Target +# End Project diff --git a/impeller/third_party/stb/stb/tests/stretch_test.c b/impeller/third_party/stb/stb/tests/stretch_test.c new file mode 100644 index 0000000000000..8caf43f645cdf --- /dev/null +++ b/impeller/third_party/stb/stb/tests/stretch_test.c @@ -0,0 +1,28 @@ +// check that stb_truetype compiles with no stb_rect_pack.h +#define STB_TRUETYPE_IMPLEMENTATION +#include "stb_truetype.h" + +#include "stretchy_buffer.h" +#include + +int main(int arg, char **argv) +{ + int i; + int *arr = NULL; + + for (i=0; i < 1000000; ++i) + sb_push(arr, i); + + assert(sb_count(arr) == 1000000); + for (i=0; i < 1000000; ++i) + assert(arr[i] == i); + + sb_free(arr); + arr = NULL; + + for (i=0; i < 1000; ++i) + sb_add(arr, 1000); + assert(sb_count(arr) == 1000000); + + return 0; +} \ No newline at end of file diff --git a/impeller/third_party/stb/stb/tests/stretch_test.dsp b/impeller/third_party/stb/stb/tests/stretch_test.dsp new file mode 100644 index 0000000000000..dd0442c26c57f --- /dev/null +++ b/impeller/third_party/stb/stb/tests/stretch_test.dsp @@ -0,0 +1,89 @@ +# Microsoft Developer Studio Project File - Name="stretch_test" - Package Owner=<4> +# Microsoft Developer Studio Generated Build File, Format Version 6.00 +# ** DO NOT EDIT ** + +# TARGTYPE "Win32 (x86) Console Application" 0x0103 + +CFG=stretch_test - Win32 Debug +!MESSAGE This is not a valid makefile. To build this project using NMAKE, +!MESSAGE use the Export Makefile command and run +!MESSAGE +!MESSAGE NMAKE /f "stretch_test.mak". +!MESSAGE +!MESSAGE You can specify a configuration when running NMAKE +!MESSAGE by defining the macro CFG on the command line. For example: +!MESSAGE +!MESSAGE NMAKE /f "stretch_test.mak" CFG="stretch_test - Win32 Debug" +!MESSAGE +!MESSAGE Possible choices for configuration are: +!MESSAGE +!MESSAGE "stretch_test - Win32 Release" (based on "Win32 (x86) Console Application") +!MESSAGE "stretch_test - Win32 Debug" (based on "Win32 (x86) Console Application") +!MESSAGE + +# Begin Project +# PROP AllowPerConfigDependencies 0 +# PROP Scc_ProjName "" +# PROP Scc_LocalPath "" +CPP=cl.exe +RSC=rc.exe + +!IF "$(CFG)" == "stretch_test - Win32 Release" + +# PROP BASE Use_MFC 0 +# PROP BASE Use_Debug_Libraries 0 +# PROP BASE Output_Dir "Release" +# PROP BASE Intermediate_Dir "Release" +# PROP BASE Target_Dir "" +# PROP Use_MFC 0 +# PROP Use_Debug_Libraries 0 +# PROP Output_Dir "Release" +# PROP Intermediate_Dir "Release" +# PROP Target_Dir "" +# ADD BASE CPP /nologo /W3 /GX /O2 /D "WIN32" /D "NDEBUG" /D "_CONSOLE" /D "_MBCS" /YX /FD /c +# ADD CPP /nologo /W3 /GX /O2 /I "..\.." /I ".." /D "WIN32" /D "NDEBUG" /D "_CONSOLE" /D "_MBCS" /D "TT_TEST" /YX /FD /c +# ADD BASE RSC /l 0x409 /d "NDEBUG" +# ADD RSC /l 0x409 /d "NDEBUG" +BSC32=bscmake.exe +# ADD BASE BSC32 /nologo +# ADD BSC32 /nologo +LINK32=link.exe +# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:console /machine:I386 +# ADD LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:console /machine:I386 + +!ELSEIF "$(CFG)" == "stretch_test - Win32 Debug" + +# PROP BASE Use_MFC 0 +# PROP BASE Use_Debug_Libraries 1 +# PROP BASE Output_Dir "stretch_test___Win32_Debug" +# PROP BASE Intermediate_Dir "stretch_test___Win32_Debug" +# PROP BASE Target_Dir "" +# PROP Use_MFC 0 +# PROP Use_Debug_Libraries 1 +# PROP Output_Dir "Debug" +# PROP Intermediate_Dir "Debug\stretch_test" +# PROP Target_Dir "" +# ADD BASE CPP /nologo /W3 /Gm /GX /ZI /Od /D "WIN32" /D "_DEBUG" /D "_CONSOLE" /D "_MBCS" /YX /FD /GZ /c +# ADD CPP /nologo /W3 /Gm /GX /ZI /Od /I ".." /D "WIN32" /D "_DEBUG" /D "_CONSOLE" /D "_MBCS" /FD /GZ /c +# SUBTRACT CPP /YX +# ADD BASE RSC /l 0x409 /d "_DEBUG" +# ADD RSC /l 0x409 /d "_DEBUG" +BSC32=bscmake.exe +# ADD BASE BSC32 /nologo +# ADD BSC32 /nologo +LINK32=link.exe +# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:console /debug /machine:I386 /pdbtype:sept +# ADD LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:console /debug /machine:I386 /pdbtype:sept + +!ENDIF + +# Begin Target + +# Name "stretch_test - Win32 Release" +# Name "stretch_test - Win32 Debug" +# Begin Source File + +SOURCE=.\stretch_test.c +# End Source File +# End Target +# End Project diff --git a/impeller/third_party/stb/stb/tests/stretchy_buffer_test.c b/impeller/third_party/stb/stb/tests/stretchy_buffer_test.c new file mode 100644 index 0000000000000..5ced5bc3abdc2 --- /dev/null +++ b/impeller/third_party/stb/stb/tests/stretchy_buffer_test.c @@ -0,0 +1 @@ +#include "stretchy_buffer.h" \ No newline at end of file diff --git a/impeller/third_party/stb/stb/tests/test_c_compilation.c b/impeller/third_party/stb/stb/tests/test_c_compilation.c new file mode 100644 index 0000000000000..de25330ae6432 --- /dev/null +++ b/impeller/third_party/stb/stb/tests/test_c_compilation.c @@ -0,0 +1,30 @@ +#define STB_PERLIN_IMPLEMENTATION +#define STB_IMAGE_WRITE_IMPLEMENTATION +#define STB_DXT_IMPLEMENATION +#define STB_C_LEXER_IMPLEMENTATIOn +#define STB_DIVIDE_IMPLEMENTATION +#define STB_IMAGE_IMPLEMENTATION +#define STB_HERRINGBONE_WANG_TILE_IMEPLEMENTATIOn +#define STB_IMAGE_RESIZE_IMPLEMENTATION +#define STB_RECT_PACK_IMPLEMENTATION +#define STB_VOXEL_RENDER_IMPLEMENTATION +#define STB_EASY_FONT_IMPLEMENTATION + +#include "stb_easy_font.h" +#include "stb_herringbone_wang_tile.h" +#include "stb_image.h" +#include "stb_image_write.h" +#include "stb_perlin.h" +#include "stb_dxt.h" +#include "stb_c_lexer.h" +#include "stb_divide.h" +#include "stb_image_resize.h" +#include "stb_rect_pack.h" + +#define STBVOX_CONFIG_MODE 1 +#include "stb_voxel_render.h" + +#define STBTE_DRAW_RECT(x0,y0,x1,y1,color) 0 +#define STBTE_DRAW_TILE(x,y,id,highlight,data) 0 +#define STB_TILEMAP_EDITOR_IMPLEMENTATION +#include "stb_tilemap_editor.h" diff --git a/impeller/third_party/stb/stb/tests/test_cpp_compilation.cpp b/impeller/third_party/stb/stb/tests/test_cpp_compilation.cpp new file mode 100644 index 0000000000000..97bd2c2b94ede --- /dev/null +++ b/impeller/third_party/stb/stb/tests/test_cpp_compilation.cpp @@ -0,0 +1,137 @@ +#define STB_TRUETYPE_IMPLEMENTATION +#define STB_PERLIN_IMPLEMENTATION +#define STB_IMAGE_WRITE_IMPLEMENTATION +#define STB_DXT_IMPLEMENATION +#define STB_C_LEXER_IMPLEMENTATIOn +#define STB_DIVIDE_IMPLEMENTATION +#define STB_IMAGE_IMPLEMENTATION +#define STB_HERRINGBONE_WANG_TILE_IMPLEMENTATION +#define STB_RECT_PACK_IMPLEMENTATION +#define STB_VOXEL_RENDER_IMPLEMENTATION + +#define STBI_MALLOC my_malloc +#define STBI_FREE my_free +#define STBI_REALLOC my_realloc + +void *my_malloc(size_t) { return 0; } +void *my_realloc(void *, size_t) { return 0; } +void my_free(void *) { } + +#include "stb_image.h" +#include "stb_rect_pack.h" +#include "stb_truetype.h" +#include "stb_image_write.h" +#include "stb_perlin.h" +#include "stb_dxt.h" +#include "stb_c_lexer.h" +#include "stb_divide.h" +#include "stb_herringbone_wang_tile.h" + +#define STBVOX_CONFIG_MODE 1 +#include "stb_voxel_render.h" + +#define STBTE_DRAW_RECT(x0,y0,x1,y1,color) do ; while(0) +#define STBTE_DRAW_TILE(x,y,id,highlight,data) do ; while(0) +#define STB_TILEMAP_EDITOR_IMPLEMENTATION +#include "stb_tilemap_editor.h" + +#include "stb_easy_font.h" + +#define STB_LEAKCHECK_IMPLEMENTATION +#include "stb_leakcheck.h" + +#define STB_IMAGE_RESIZE_IMPLEMENTATION +#include "stb_image_resize.h" + +#include "stretchy_buffer.h" + + + +//////////////////////////////////////////////////////////// +// +// text edit + +#include +#include // memmove +#include // isspace + +#define STB_TEXTEDIT_CHARTYPE char +#define STB_TEXTEDIT_STRING text_control + +// get the base type +#include "stb_textedit.h" + +// define our editor structure +typedef struct +{ + char *string; + int stringlen; + STB_TexteditState state; +} text_control; + +// define the functions we need +void layout_func(StbTexteditRow *row, STB_TEXTEDIT_STRING *str, int start_i) +{ + int remaining_chars = str->stringlen - start_i; + row->num_chars = remaining_chars > 20 ? 20 : remaining_chars; // should do real word wrap here + row->x0 = 0; + row->x1 = 20; // need to account for actual size of characters + row->baseline_y_delta = 1.25; + row->ymin = -1; + row->ymax = 0; +} + +int delete_chars(STB_TEXTEDIT_STRING *str, int pos, int num) +{ + memmove(&str->string[pos], &str->string[pos+num], str->stringlen - (pos+num)); + str->stringlen -= num; + return 1; // always succeeds +} + +int insert_chars(STB_TEXTEDIT_STRING *str, int pos, STB_TEXTEDIT_CHARTYPE *newtext, int num) +{ + str->string = (char *) realloc(str->string, str->stringlen + num); + memmove(&str->string[pos+num], &str->string[pos], str->stringlen - pos); + memcpy(&str->string[pos], newtext, num); + str->stringlen += num; + return 1; // always succeeds +} + +// define all the #defines needed + +#define KEYDOWN_BIT 0x80000000 + +#define STB_TEXTEDIT_STRINGLEN(tc) ((tc)->stringlen) +#define STB_TEXTEDIT_LAYOUTROW layout_func +#define STB_TEXTEDIT_GETWIDTH(tc,n,i) (1) // quick hack for monospaced +#define STB_TEXTEDIT_KEYTOTEXT(key) (((key) & KEYDOWN_BIT) ? 0 : (key)) +#define STB_TEXTEDIT_GETCHAR(tc,i) ((tc)->string[i]) +#define STB_TEXTEDIT_NEWLINE '\n' +#define STB_TEXTEDIT_IS_SPACE(ch) isspace(ch) +#define STB_TEXTEDIT_DELETECHARS delete_chars +#define STB_TEXTEDIT_INSERTCHARS insert_chars + +#define STB_TEXTEDIT_K_SHIFT 0x40000000 +#define STB_TEXTEDIT_K_CONTROL 0x20000000 +#define STB_TEXTEDIT_K_LEFT (KEYDOWN_BIT | 1) // actually use VK_LEFT, SDLK_LEFT, etc +#define STB_TEXTEDIT_K_RIGHT (KEYDOWN_BIT | 2) // VK_RIGHT +#define STB_TEXTEDIT_K_UP (KEYDOWN_BIT | 3) // VK_UP +#define STB_TEXTEDIT_K_DOWN (KEYDOWN_BIT | 4) // VK_DOWN +#define STB_TEXTEDIT_K_LINESTART (KEYDOWN_BIT | 5) // VK_HOME +#define STB_TEXTEDIT_K_LINEEND (KEYDOWN_BIT | 6) // VK_END +#define STB_TEXTEDIT_K_TEXTSTART (STB_TEXTEDIT_K_LINESTART | STB_TEXTEDIT_K_CONTROL) +#define STB_TEXTEDIT_K_TEXTEND (STB_TEXTEDIT_K_LINEEND | STB_TEXTEDIT_K_CONTROL) +#define STB_TEXTEDIT_K_DELETE (KEYDOWN_BIT | 7) // VK_DELETE +#define STB_TEXTEDIT_K_BACKSPACE (KEYDOWN_BIT | 8) // VK_BACKSPACE +#define STB_TEXTEDIT_K_UNDO (KEYDOWN_BIT | STB_TEXTEDIT_K_CONTROL | 'z') +#define STB_TEXTEDIT_K_REDO (KEYDOWN_BIT | STB_TEXTEDIT_K_CONTROL | 'y') +#define STB_TEXTEDIT_K_INSERT (KEYDOWN_BIT | 9) // VK_INSERT +#define STB_TEXTEDIT_K_WORDLEFT (STB_TEXTEDIT_K_LEFT | STB_TEXTEDIT_K_CONTROL) +#define STB_TEXTEDIT_K_WORDRIGHT (STB_TEXTEDIT_K_RIGHT | STB_TEXTEDIT_K_CONTROL) +#define STB_TEXTEDIT_K_PGUP (KEYDOWN_BIT | 10) // VK_PGUP -- not implemented +#define STB_TEXTEDIT_K_PGDOWN (KEYDOWN_BIT | 11) // VK_PGDOWN -- not implemented + +#define STB_TEXTEDIT_IMPLEMENTATION +#include "stb_textedit.h" + + diff --git a/impeller/third_party/stb/stb/tests/test_truetype.c b/impeller/third_party/stb/stb/tests/test_truetype.c new file mode 100644 index 0000000000000..4b7815232fc98 --- /dev/null +++ b/impeller/third_party/stb/stb/tests/test_truetype.c @@ -0,0 +1,95 @@ +#include "stb_rect_pack.h" +#define STB_TRUETYPE_IMPLEMENTATION +#include "stb_truetype.h" +#include "stb_image_write.h" + +#include + +char ttf_buffer[1<<25]; +unsigned char output[512*100]; + +#ifdef TT_TEST + +void debug(void) +{ + stbtt_fontinfo font; + fread(ttf_buffer, 1, 1<<25, fopen("c:/x/lm/LiberationMono-Regular.ttf", "rb")); + stbtt_InitFont(&font, ttf_buffer, 0); + + stbtt_MakeGlyphBitmap(&font, output, 6, 9, 512, 5.172414E-03f, 5.172414E-03f, 54); +} + +#define BITMAP_W 256 +#define BITMAP_H 512 +unsigned char temp_bitmap[BITMAP_H][BITMAP_W]; +stbtt_bakedchar cdata[256*2]; // ASCII 32..126 is 95 glyphs +stbtt_packedchar pdata[256*2]; + +int main(int argc, char **argv) +{ + stbtt_fontinfo font; + unsigned char *bitmap; + int w,h,i,j,c = (argc > 1 ? atoi(argv[1]) : 34807), s = (argc > 2 ? atoi(argv[2]) : 32); + + //debug(); + + // @TODO: why is minglui.ttc failing? + fread(ttf_buffer, 1, 1<<25, fopen(argc > 3 ? argv[3] : "c:/windows/fonts/mingliu.ttc", "rb")); + + //fread(ttf_buffer, 1, 1<<25, fopen(argc > 3 ? argv[3] : "c:/x/DroidSansMono.ttf", "rb")); + { + static stbtt_pack_context pc; + static stbtt_packedchar cd[256]; + static unsigned char atlas[1024*1024]; + + stbtt_PackBegin(&pc, atlas, 1024,1024,1024,1,NULL); + stbtt_PackFontRange(&pc, ttf_buffer, 0, 32.0, 0, 256, cd); + stbtt_PackEnd(&pc); + } + +#if 0 + stbtt_BakeFontBitmap(ttf_buffer,stbtt_GetFontOffsetForIndex(ttf_buffer,0), 40.0, temp_bitmap[0],BITMAP_W,BITMAP_H, 32,96, cdata); // no guarantee this fits! + stbi_write_png("fonttest1.png", BITMAP_W, BITMAP_H, 1, temp_bitmap, 0); + + { + stbtt_pack_context pc; + stbtt_PackBegin(&pc, temp_bitmap[0], BITMAP_W, BITMAP_H, 0, 1, NULL); + stbtt_PackFontRange(&pc, ttf_buffer, 0, 20.0, 32, 95, pdata); + stbtt_PackFontRange(&pc, ttf_buffer, 0, 20.0, 0xa0, 0x100-0xa0, pdata); + stbtt_PackEnd(&pc); + stbi_write_png("fonttest2.png", BITMAP_W, BITMAP_H, 1, temp_bitmap, 0); + } + + { + stbtt_pack_context pc; + stbtt_pack_range pr[2]; + stbtt_PackBegin(&pc, temp_bitmap[0], BITMAP_W, BITMAP_H, 0, 1, NULL); + + pr[0].chardata_for_range = pdata; + pr[0].first_unicode_char_in_range = 32; + pr[0].num_chars_in_range = 95; + pr[0].font_size = 20.0f; + pr[1].chardata_for_range = pdata+256; + pr[1].first_unicode_char_in_range = 0xa0; + pr[1].num_chars_in_range = 0x100 - 0xa0; + pr[1].font_size = 20.0f; + + stbtt_PackSetOversampling(&pc, 2, 2); + stbtt_PackFontRanges(&pc, ttf_buffer, 0, pr, 2); + stbtt_PackEnd(&pc); + stbi_write_png("fonttest3.png", BITMAP_W, BITMAP_H, 1, temp_bitmap, 0); + } + return 0; +#endif + + stbtt_InitFont(&font, ttf_buffer, stbtt_GetFontOffsetForIndex(ttf_buffer,0)); + bitmap = stbtt_GetCodepointBitmap(&font, 0,stbtt_ScaleForPixelHeight(&font, (float)s), c, &w, &h, 0,0); + + for (j=0; j < h; ++j) { + for (i=0; i < w; ++i) + putchar(" .:ioVM@"[bitmap[j*w+i]>>5]); + putchar('\n'); + } + return 0; +} +#endif diff --git a/impeller/third_party/stb/stb/tests/test_vorbis.c b/impeller/third_party/stb/stb/tests/test_vorbis.c new file mode 100644 index 0000000000000..d54ed23171fda --- /dev/null +++ b/impeller/third_party/stb/stb/tests/test_vorbis.c @@ -0,0 +1,18 @@ +#define STB_VORBIS_HEADER_ONLY +#include "stb_vorbis.c" +#include "stb.h" + +extern void stb_vorbis_dumpmem(void); + +#ifdef VORBIS_TEST +int main(int argc, char **argv) +{ + size_t memlen; + unsigned char *mem = stb_fileu("c:/x/sketch008.ogg", &memlen); + int chan, samplerate; + short *output; + int samples = stb_vorbis_decode_memory(mem, memlen, &chan, &samplerate, &output); + stb_filewrite("c:/x/sketch008.raw", output, samples*4); + return 0; +} +#endif diff --git a/impeller/third_party/stb/stb/tests/textedit_sample.c b/impeller/third_party/stb/stb/tests/textedit_sample.c new file mode 100644 index 0000000000000..04dc31a887bd3 --- /dev/null +++ b/impeller/third_party/stb/stb/tests/textedit_sample.c @@ -0,0 +1,84 @@ +// I haven't actually tested this yet, this is just to make sure it compiles + +#include +#include // memmove +#include // isspace + +#define STB_TEXTEDIT_CHARTYPE char +#define STB_TEXTEDIT_STRING text_control + +// get the base type +#include "stb_textedit.h" + +// define our editor structure +typedef struct +{ + char *string; + int stringlen; + STB_TexteditState state; +} text_control; + +// define the functions we need +void layout_func(StbTexteditRow *row, STB_TEXTEDIT_STRING *str, int start_i) +{ + int remaining_chars = str->stringlen - start_i; + row->num_chars = remaining_chars > 20 ? 20 : remaining_chars; // should do real word wrap here + row->x0 = 0; + row->x1 = 20; // need to account for actual size of characters + row->baseline_y_delta = 1.25; + row->ymin = -1; + row->ymax = 0; +} + +int delete_chars(STB_TEXTEDIT_STRING *str, int pos, int num) +{ + memmove(&str->string[pos], &str->string[pos+num], str->stringlen - (pos+num)); + str->stringlen -= num; + return 1; // always succeeds +} + +int insert_chars(STB_TEXTEDIT_STRING *str, int pos, STB_TEXTEDIT_CHARTYPE *newtext, int num) +{ + str->string = realloc(str->string, str->stringlen + num); + memmove(&str->string[pos+num], &str->string[pos], str->stringlen - pos); + memcpy(&str->string[pos], newtext, num); + str->stringlen += num; + return 1; // always succeeds +} + +// define all the #defines needed + +#define KEYDOWN_BIT 0x80000000 + +#define STB_TEXTEDIT_STRINGLEN(tc) ((tc)->stringlen) +#define STB_TEXTEDIT_LAYOUTROW layout_func +#define STB_TEXTEDIT_GETWIDTH(tc,n,i) (1) // quick hack for monospaced +#define STB_TEXTEDIT_KEYTOTEXT(key) (((key) & KEYDOWN_BIT) ? 0 : (key)) +#define STB_TEXTEDIT_GETCHAR(tc,i) ((tc)->string[i]) +#define STB_TEXTEDIT_NEWLINE '\n' +#define STB_TEXTEDIT_IS_SPACE(ch) isspace(ch) +#define STB_TEXTEDIT_DELETECHARS delete_chars +#define STB_TEXTEDIT_INSERTCHARS insert_chars + +#define STB_TEXTEDIT_K_SHIFT 0x40000000 +#define STB_TEXTEDIT_K_CONTROL 0x20000000 +#define STB_TEXTEDIT_K_LEFT (KEYDOWN_BIT | 1) // actually use VK_LEFT, SDLK_LEFT, etc +#define STB_TEXTEDIT_K_RIGHT (KEYDOWN_BIT | 2) // VK_RIGHT +#define STB_TEXTEDIT_K_UP (KEYDOWN_BIT | 3) // VK_UP +#define STB_TEXTEDIT_K_DOWN (KEYDOWN_BIT | 4) // VK_DOWN +#define STB_TEXTEDIT_K_LINESTART (KEYDOWN_BIT | 5) // VK_HOME +#define STB_TEXTEDIT_K_LINEEND (KEYDOWN_BIT | 6) // VK_END +#define STB_TEXTEDIT_K_TEXTSTART (STB_TEXTEDIT_K_LINESTART | STB_TEXTEDIT_K_CONTROL) +#define STB_TEXTEDIT_K_TEXTEND (STB_TEXTEDIT_K_LINEEND | STB_TEXTEDIT_K_CONTROL) +#define STB_TEXTEDIT_K_DELETE (KEYDOWN_BIT | 7) // VK_DELETE +#define STB_TEXTEDIT_K_BACKSPACE (KEYDOWN_BIT | 8) // VK_BACKSPACE +#define STB_TEXTEDIT_K_UNDO (KEYDOWN_BIT | STB_TEXTEDIT_K_CONTROL | 'z') +#define STB_TEXTEDIT_K_REDO (KEYDOWN_BIT | STB_TEXTEDIT_K_CONTROL | 'y') +#define STB_TEXTEDIT_K_INSERT (KEYDOWN_BIT | 9) // VK_INSERT +#define STB_TEXTEDIT_K_WORDLEFT (STB_TEXTEDIT_K_LEFT | STB_TEXTEDIT_K_CONTROL) +#define STB_TEXTEDIT_K_WORDRIGHT (STB_TEXTEDIT_K_RIGHT | STB_TEXTEDIT_K_CONTROL) +#define STB_TEXTEDIT_K_PGUP (KEYDOWN_BIT | 10) // VK_PGUP -- not implemented +#define STB_TEXTEDIT_K_PGDOWN (KEYDOWN_BIT | 11) // VK_PGDOWN -- not implemented + +#define STB_TEXTEDIT_IMPLEMENTATION +#include "stb_textedit.h" diff --git a/impeller/third_party/stb/stb/tests/tilemap_editor_integration_example.c b/impeller/third_party/stb/stb/tests/tilemap_editor_integration_example.c new file mode 100644 index 0000000000000..ea5dee775c87e --- /dev/null +++ b/impeller/third_party/stb/stb/tests/tilemap_editor_integration_example.c @@ -0,0 +1,193 @@ +// This isn't compilable as-is, as it was extracted from a working +// integration-in-a-game and makes reference to symbols from that game. + +#include +#include +#include "game.h" +#include "SDL.h" +#include "stb_tilemap_editor.h" + +extern void editor_draw_tile(int x, int y, unsigned short tile, int mode, float *props); +extern void editor_draw_rect(int x0, int y0, int x1, int y1, unsigned char r, unsigned char g, unsigned char b); + +static int is_platform(short *tiles); +static unsigned int prop_type(int n, short *tiles); +static char *prop_name(int n, short *tiles); +static float prop_range(int n, short *tiles, int is_max); +static int allow_link(short *src, short *dest); + +#define STBTE_MAX_PROPERTIES 8 + +#define STBTE_PROP_TYPE(n, tiledata, p) prop_type(n,tiledata) +#define STBTE_PROP_NAME(n, tiledata, p) prop_name(n,tiledata) +#define STBTE_PROP_MIN(n, tiledata, p) prop_range(n,tiledata,0) +#define STBTE_PROP_MAX(n, tiledata, p) prop_range(n,tiledata,1) +#define STBTE_PROP_FLOAT_SCALE(n,td,p) (0.1) + +#define STBTE_ALLOW_LINK(srctile, srcprop, desttile, destprop) \ + allow_link(srctile, desttile) + +#define STBTE_LINK_COLOR(srctile, srcprop, desttile, destprop) \ + (is_platform(srctile) ? 0xff80ff : 0x808040) + +#define STBTE_DRAW_RECT(x0,y0,x1,y1,c) \ + editor_draw_rect(x0,y0,x1,y1,(c)>>16,((c)>>8)&255,(c)&255) + +#define STBTE_DRAW_TILE(x,y,id,highlight,props) \ + editor_draw_tile(x,y,id,highlight,props) + + + +#define STB_TILEMAP_EDITOR_IMPLEMENTATION +#include "stb_tilemap_editor.h" + +stbte_tilemap *edit_map; + +void editor_key(enum stbte_action act) +{ + stbte_action(edit_map, act); +} + +void editor_process_sdl_event(SDL_Event *e) +{ + switch (e->type) { + case SDL_MOUSEMOTION: + case SDL_MOUSEBUTTONDOWN: + case SDL_MOUSEBUTTONUP: + case SDL_MOUSEWHEEL: + stbte_mouse_sdl(edit_map, e, 1.0f/editor_scale,1.0f/editor_scale,0,0); + break; + + case SDL_KEYDOWN: + if (in_editor) { + switch (e->key.keysym.sym) { + case SDLK_RIGHT: editor_key(STBTE_scroll_right); break; + case SDLK_LEFT : editor_key(STBTE_scroll_left ); break; + case SDLK_UP : editor_key(STBTE_scroll_up ); break; + case SDLK_DOWN : editor_key(STBTE_scroll_down ); break; + } + switch (e->key.keysym.scancode) { + case SDL_SCANCODE_S: editor_key(STBTE_tool_select); break; + case SDL_SCANCODE_B: editor_key(STBTE_tool_brush ); break; + case SDL_SCANCODE_E: editor_key(STBTE_tool_erase ); break; + case SDL_SCANCODE_R: editor_key(STBTE_tool_rectangle ); break; + case SDL_SCANCODE_I: editor_key(STBTE_tool_eyedropper); break; + case SDL_SCANCODE_L: editor_key(STBTE_tool_link); break; + case SDL_SCANCODE_G: editor_key(STBTE_act_toggle_grid); break; + } + if ((e->key.keysym.mod & KMOD_CTRL) && !(e->key.keysym.mod & ~KMOD_CTRL)) { + switch (e->key.keysym.scancode) { + case SDL_SCANCODE_X: editor_key(STBTE_act_cut ); break; + case SDL_SCANCODE_C: editor_key(STBTE_act_copy ); break; + case SDL_SCANCODE_V: editor_key(STBTE_act_paste); break; + case SDL_SCANCODE_Z: editor_key(STBTE_act_undo ); break; + case SDL_SCANCODE_Y: editor_key(STBTE_act_redo ); break; + } + } + } + break; + } +} + +void editor_init(void) +{ + int i; + edit_map = stbte_create_map(20,14, 8, 16,16, 100); + + stbte_set_background_tile(edit_map, T_empty); + + for (i=0; i < T__num_types; ++i) { + if (i != T_reserved1 && i != T_entry && i != T_doorframe) + stbte_define_tile(edit_map, 0+i, 1, "Background"); + } + stbte_define_tile(edit_map, 256+O_player , 8, "Char"); + stbte_define_tile(edit_map, 256+O_robot , 8, "Char"); + for (i=O_lockeddoor; i < O__num_types-2; ++i) + if (i == O_platform || i == O_vplatform) + stbte_define_tile(edit_map, 256+i, 4, "Object"); + else + stbte_define_tile(edit_map, 256+i, 2, "Object"); + + //stbte_set_layername(edit_map, 0, "background"); + //stbte_set_layername(edit_map, 1, "objects"); + //stbte_set_layername(edit_map, 2, "platforms"); + //stbte_set_layername(edit_map, 3, "characters"); +} + +static int is_platform(short *tiles) +{ + // platforms are only on layer #2 + return tiles[2] == 256 + O_platform || tiles[2] == 256 + O_vplatform; +} + +static int is_object(short *tiles) +{ + return (tiles[1] >= 256 || tiles[2] >= 256 || tiles[3] >= 256); +} + +static unsigned int prop_type(int n, short *tiles) +{ + if (is_platform(tiles)) { + static unsigned int platform_types[STBTE_MAX_PROPERTIES] = { + STBTE_PROP_bool, // phantom + STBTE_PROP_int, // x_adjust + STBTE_PROP_int, // y_adjust + STBTE_PROP_float, // width + STBTE_PROP_float, // lspeed + STBTE_PROP_float, // rspeed + STBTE_PROP_bool, // autoreturn + STBTE_PROP_bool, // one-shot + // remainder get 0, means 'no property in this slot' + }; + return platform_types[n]; + } else if (is_object(tiles)) { + if (n == 0) + return STBTE_PROP_bool; + } + return 0; +} + +static char *prop_name(int n, short *tiles) +{ + if (is_platform(tiles)) { + static char *platform_vars[STBTE_MAX_PROPERTIES] = { + "phantom", + "x_adjust", + "y_adjust", + "width", + "lspeed", + "rspeed", + "autoreturn", + "one-shot", + }; + return platform_vars[n]; + } + return "phantom"; +} + +static float prop_range(int n, short *tiles, int is_max) +{ + if (is_platform(tiles)) { + static float ranges[8][2] = { + { 0, 1 }, // phantom-flag, range is ignored + { -15, 15 }, // x_adjust + { -15, 15 }, // y_adjust + { 0, 6 }, // width + { 0, 10 }, // lspeed + { 0, 10 }, // rspeed + { 0, 1 }, // autoreturn, range is ignored + { 0, 1 }, // one-shot, range is ignored + }; + return ranges[n][is_max]; + } + return 0; +} + +static int allow_link(short *src, short *dest) +{ + if (is_platform(src)) + return dest[1] == 256+O_lever; + if (src[1] == 256+O_endpoint) + return is_platform(dest); + return 0; +} diff --git a/impeller/third_party/stb/stb/tests/vorbseek/vorbseek.c b/impeller/third_party/stb/stb/tests/vorbseek/vorbseek.c new file mode 100644 index 0000000000000..f3460ad7f6905 --- /dev/null +++ b/impeller/third_party/stb/stb/tests/vorbseek/vorbseek.c @@ -0,0 +1,125 @@ +#include +#include +#include +#include + +#define STB_VORBIS_HEADER_ONLY +#include "stb_vorbis.c" + +#define SAMPLES_TO_TEST 3000 + +int test_count [5] = { 5000, 3000, 2000, 50000, 50000 }; +int test_spacing[5] = { 1, 111, 3337, 7779, 72717 }; + +int try_seeking(stb_vorbis *v, unsigned int pos, short *output, unsigned int num_samples) +{ + int count; + short samples[SAMPLES_TO_TEST*2]; + assert(pos <= num_samples); + + if (!stb_vorbis_seek(v, pos)) { + fprintf(stderr, "Seek to %u returned error from stb_vorbis\n", pos); + return 0; + } + + count = stb_vorbis_get_samples_short_interleaved(v, 2, samples, SAMPLES_TO_TEST*2); + + if (count > (int) (num_samples - pos)) { + fprintf(stderr, "Seek to %u allowed decoding %d samples when only %d should have been valid.\n", + pos, count, (int) (num_samples - pos)); + return 0; + } + + if (count < SAMPLES_TO_TEST && count < (int) (num_samples - pos)) { + fprintf(stderr, "Seek to %u only decoded %d samples of %d attempted when at least %d should have been valid.\n", + pos, count, SAMPLES_TO_TEST, num_samples - pos); + return 0; + } + + if (0 != memcmp(samples, output + pos*2, count*2)) { + int k; + for (k=0; k < SAMPLES_TO_TEST*2; ++k) { + if (samples[k] != output[k]) { + fprintf(stderr, "Seek to %u produced incorrect samples starting at sample %u (short #%d in buffer).\n", + pos, pos + (k/2), k); + break; + } + } + assert(k != SAMPLES_TO_TEST*2); + return 0; + } + + return 1; +} + +int main(int argc, char **argv) +{ + int num_chan, samprate; + int i, j, test, phase; + short *output; + + if (argc == 1) { + fprintf(stderr, "Usage: vorbseek {vorbisfile} [{vorbisfile]*]\n"); + fprintf(stderr, "Tests various seek offsets to make sure they're sample exact.\n"); + return 0; + } + + #if 0 + { + // check that outofmem occurs correctly + stb_vorbis_alloc va; + va.alloc_buffer = malloc(1024*1024); + for (i=0; i < 1024*1024; i += 10) { + int error=0; + stb_vorbis *v; + va.alloc_buffer_length_in_bytes = i; + v = stb_vorbis_open_filename(argv[1], &error, &va); + if (v != NULL) + break; + printf("Error %d at %d\n", error, i); + } + } + #endif + + for (j=1; j < argc; ++j) { + unsigned int successes=0, attempts = 0; + unsigned int num_samples = stb_vorbis_decode_filename(argv[j], &num_chan, &samprate, &output); + + break; + + if (num_samples == 0xffffffff) { + fprintf(stderr, "Error: couldn't open file or not vorbis file: %s\n", argv[j]); + goto fail; + } + + if (num_chan != 2) { + fprintf(stderr, "vorbseek testing only works with files with 2 channels, %s has %d\n", argv[j], num_chan); + goto fail; + } + + for (test=0; test < 5; ++test) { + int error; + stb_vorbis *v = stb_vorbis_open_filename(argv[j], &error, NULL); + if (v == NULL) { + fprintf(stderr, "Couldn't re-open %s for test #%d\n", argv[j], test); + goto fail; + } + for (phase=0; phase < 3; ++phase) { + unsigned int base = phase == 0 ? 0 : phase == 1 ? num_samples - test_count[test]*test_spacing[test] : num_samples/3; + for (i=0; i < test_count[test]; ++i) { + unsigned int pos = base + i*test_spacing[test]; + if (pos > num_samples) // this also catches underflows + continue; + successes += try_seeking(v, pos, output, num_samples); + attempts += 1; + } + } + stb_vorbis_close(v); + } + printf("%d of %d seeks failed in %s (%d samples)\n", attempts-successes, attempts, argv[j], num_samples); + free(output); + } + return 0; + fail: + return 1; +} \ No newline at end of file diff --git a/impeller/third_party/stb/stb/tests/vorbseek/vorbseek.dsp b/impeller/third_party/stb/stb/tests/vorbseek/vorbseek.dsp new file mode 100644 index 0000000000000..5eaf5795ffb7d --- /dev/null +++ b/impeller/third_party/stb/stb/tests/vorbseek/vorbseek.dsp @@ -0,0 +1,96 @@ +# Microsoft Developer Studio Project File - Name="vorbseek" - Package Owner=<4> +# Microsoft Developer Studio Generated Build File, Format Version 6.00 +# ** DO NOT EDIT ** + +# TARGTYPE "Win32 (x86) Console Application" 0x0103 + +CFG=vorbseek - Win32 Debug +!MESSAGE This is not a valid makefile. To build this project using NMAKE, +!MESSAGE use the Export Makefile command and run +!MESSAGE +!MESSAGE NMAKE /f "vorbseek.mak". +!MESSAGE +!MESSAGE You can specify a configuration when running NMAKE +!MESSAGE by defining the macro CFG on the command line. For example: +!MESSAGE +!MESSAGE NMAKE /f "vorbseek.mak" CFG="vorbseek - Win32 Debug" +!MESSAGE +!MESSAGE Possible choices for configuration are: +!MESSAGE +!MESSAGE "vorbseek - Win32 Release" (based on "Win32 (x86) Console Application") +!MESSAGE "vorbseek - Win32 Debug" (based on "Win32 (x86) Console Application") +!MESSAGE + +# Begin Project +# PROP AllowPerConfigDependencies 0 +# PROP Scc_ProjName "" +# PROP Scc_LocalPath "" +CPP=cl.exe +RSC=rc.exe + +!IF "$(CFG)" == "vorbseek - Win32 Release" + +# PROP BASE Use_MFC 0 +# PROP BASE Use_Debug_Libraries 0 +# PROP BASE Output_Dir "Release" +# PROP BASE Intermediate_Dir "Release" +# PROP BASE Target_Dir "" +# PROP Use_MFC 0 +# PROP Use_Debug_Libraries 0 +# PROP Output_Dir "Release" +# PROP Intermediate_Dir "Release" +# PROP Ignore_Export_Lib 0 +# PROP Target_Dir "" +# ADD BASE CPP /nologo /W3 /GX /O2 /D "WIN32" /D "NDEBUG" /D "_CONSOLE" /D "_MBCS" /YX /FD /c +# ADD CPP /nologo /W3 /GX /Zd /O2 /I "..\.." /D "WIN32" /D "NDEBUG" /D "_CONSOLE" /D "_MBCS" /FD /c +# SUBTRACT CPP /YX +# ADD BASE RSC /l 0x409 /d "NDEBUG" +# ADD RSC /l 0x409 /d "NDEBUG" +BSC32=bscmake.exe +# ADD BASE BSC32 /nologo +# ADD BSC32 /nologo +LINK32=link.exe +# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:console /machine:I386 +# ADD LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:console /debug /machine:I386 + +!ELSEIF "$(CFG)" == "vorbseek - Win32 Debug" + +# PROP BASE Use_MFC 0 +# PROP BASE Use_Debug_Libraries 1 +# PROP BASE Output_Dir "Debug" +# PROP BASE Intermediate_Dir "Debug" +# PROP BASE Target_Dir "" +# PROP Use_MFC 0 +# PROP Use_Debug_Libraries 1 +# PROP Output_Dir "Debug" +# PROP Intermediate_Dir "Debug" +# PROP Ignore_Export_Lib 0 +# PROP Target_Dir "" +# ADD BASE CPP /nologo /W3 /Gm /GX /ZI /Od /D "WIN32" /D "_DEBUG" /D "_CONSOLE" /D "_MBCS" /YX /FD /GZ /c +# ADD CPP /nologo /W3 /Gm /GX /ZI /Od /I "..\.." /D "WIN32" /D "_DEBUG" /D "_CONSOLE" /D "_MBCS" /FD /GZ /c +# SUBTRACT CPP /YX +# ADD BASE RSC /l 0x409 /d "_DEBUG" +# ADD RSC /l 0x409 /d "_DEBUG" +BSC32=bscmake.exe +# ADD BASE BSC32 /nologo +# ADD BSC32 /nologo +LINK32=link.exe +# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:console /debug /machine:I386 /pdbtype:sept +# ADD LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:console /debug /machine:I386 /pdbtype:sept + +!ENDIF + +# Begin Target + +# Name "vorbseek - Win32 Release" +# Name "vorbseek - Win32 Debug" +# Begin Source File + +SOURCE=..\..\stb_vorbis.c +# End Source File +# Begin Source File + +SOURCE=.\vorbseek.c +# End Source File +# End Target +# End Project diff --git a/impeller/third_party/stb/stb/tools/README.footer.md b/impeller/third_party/stb/stb/tools/README.footer.md new file mode 100644 index 0000000000000..b9a52bf4cc1a5 --- /dev/null +++ b/impeller/third_party/stb/stb/tools/README.footer.md @@ -0,0 +1,100 @@ + +FAQ +--- + +#### What's the license? + +These libraries are in the public domain (or the equivalent where that is not +possible). You can do anything you want with them. You have no legal obligation +to do anything else, although I appreciate attribution. + +#### Are there other single-file public-domain/open source libraries with minimal dependencies out there? + +[Yes.](https://github.com/nothings/stb/blob/master/docs/other_libs.md) + +#### If I wrap an stb library in a new library, does the new library have to be public domain? + +No. + +#### Some of these libraries seem redundant to existing open source libraries. Are they better somehow? + +Generally they're only better in that they're easier to integrate, +easier to use, and easier to release (single file; good API; no +attribution requirement). They may be less featureful, slower, +and/or use more memory. If you're already using an equivalent +library, there's probably no good reason to switch. + +###### Can I link directly to the table of stb libraries? + +You can use [this URL](https://github.com/nothings/stb#stb_libs) to link directly to that list. + +#### Why do you list "lines of code"? It's a terrible metric. + +Just to give you some idea of the internal complexity of the library, +to help you manage your expectations, or to let you know what you're +getting into. While not all the libraries are written in the same +style, they're certainly similar styles, and so comparisons between +the libraries are probably still meaningful. + +Note though that the lines do include both the implementation, the +part that corresponds to a header file, and the documentation. + +#### Why single-file headers? + +Windows doesn't have standard directories where libraries +live. That makes deploying libraries in Windows a lot more +painful than open source developers on Unix-derivates generally +realize. (It also makes library dependencies a lot worse in Windows.) + +There's also a common problem in Windows where a library was built +against a different version of the runtime library, which causes +link conflicts and confusion. Shipping the libs as headers means +you normally just compile them straight into your project without +making libraries, thus sidestepping that problem. + +Making them a single file makes it very easy to just +drop them into a project that needs them. (Of course you can +still put them in a proper shared library tree if you want.) + +Why not two files, one a header and one an implementation? +The difference between 10 files and 9 files is not a big deal, +but the difference between 2 files and 1 file is a big deal. +You don't need to zip or tar the files up, you don't have to +remember to attach *two* files, etc. + +#### Why "stb"? Is this something to do with Set-Top Boxes? + +No, they are just the initials for my name, Sean T. Barrett. +This was not chosen out of egomania, but as a moderately sane +way of namespacing the filenames and source function names. + +#### Will you add more image types to stb_image.h? + +If people submit them, I generally add them, but the goal of stb_image +is less for applications like image viewer apps (which need to support +every type of image under the sun) and more for things like games which +can choose what images to use, so I may decline to add them if they're +too rare or if the size of implementation vs. apparent benefit is too low. + +#### Do you have any advice on how to create my own single-file library? + +Yes. https://github.com/nothings/stb/blob/master/docs/stb_howto.txt + +#### Why public domain? + +I prefer it over GPL, LGPL, BSD, zlib, etc. for many reasons. +Some of them are listed here: +https://github.com/nothings/stb/blob/master/docs/why_public_domain.md + +#### Why C? + +Primarily, because I use C, not C++. But it does also make it easier +for other people to use them from other languages. + +#### Why not C99? stdint.h, declare-anywhere, etc. + +I still use MSVC 6 (1998) as my IDE because it has better human factors +for me than later versions of MSVC. + + + diff --git a/impeller/third_party/stb/stb/tools/README.header.md b/impeller/third_party/stb/stb/tools/README.header.md new file mode 100644 index 0000000000000..71e765bd05872 --- /dev/null +++ b/impeller/third_party/stb/stb/tools/README.header.md @@ -0,0 +1,7 @@ +stb +=== + +single-file public domain libraries for C/C++ + +library | lastest version | category | LoC | description +--------------------- | ---- | -------- | --- | -------------------------------- diff --git a/impeller/third_party/stb/stb/tools/README.list b/impeller/third_party/stb/stb/tools/README.list new file mode 100644 index 0000000000000..5ad58a1af178c --- /dev/null +++ b/impeller/third_party/stb/stb/tools/README.list @@ -0,0 +1,18 @@ +stb_vorbis.c | audio | decode ogg vorbis files from file/memory to float/16-bit signed output +stb_image.h | graphics | image loading/decoding from file/memory: JPG, PNG, TGA, BMP, PSD, GIF, HDR, PIC +stb_truetype.h | graphics | parse, decode, and rasterize characters from truetype fonts +stb_image_write.h | graphics | image writing to disk: PNG, TGA, BMP +stb_image_resize.h | graphics | resize images larger/smaller with good quality +stb_rect_pack.h | graphics | simple 2D rectangle packer with decent quality +stretchy_buffer.h | utility | typesafe dynamic array for C (i.e. approximation to vector<>), doesn't compile as C++ +stb_textedit.h | user interface | guts of a text editor for games etc implementing them from scratch +stb_voxel_render.h | 3D graphics | Minecraft-esque voxel rendering "engine" with many more features +stb_dxt.h | 3D graphics | Fabian "ryg" Giesen's real-time DXT compressor +stb_perlin.h | 3D graphics | revised Perlin noise (3D input, 1D output) +stb_easy_font.h | 3D graphics | quick-and-dirty easy-to-deploy bitmap font for printing frame rate, etc +stb_tilemap_editor.h | game dev | embeddable tilemap editor +stb_herringbone_wang_tile.h | game dev | herringbone Wang tile map generator +stb_c_lexer.h | parsing | simplify writing parsers for C-like languages +stb_divide.h | math | more useful 32-bit modulus e.g. "euclidean divide" +stb.h | misc | helper functions for C, mostly redundant in C++; basically author's personal stuff +stb_leakcheck.h | misc | quick-and-dirty malloc/free leak-checking diff --git a/impeller/third_party/stb/stb/tools/easy_font_maker.c b/impeller/third_party/stb/stb/tools/easy_font_maker.c new file mode 100644 index 0000000000000..f1b4836d3c4bd --- /dev/null +++ b/impeller/third_party/stb/stb/tools/easy_font_maker.c @@ -0,0 +1,211 @@ +// This program was used to encode the data for stb_simple_font.h + +#define STB_DEFINE +#include "stb.h" +#define STB_IMAGE_IMPLEMENTATION +#include "stb_image.h" + +int w,h; +uint8 *data; + +int last_x[2], last_y[2]; +int num_seg[2], non_empty; +#if 0 +typedef struct +{ + unsigned short first_segment; + unsigned char advance; +} chardata; + +typedef struct +{ + unsigned char x:4; + unsigned char y:4; + unsigned char len:3; + unsigned char dir:1; +} segment; + +segment *segments; + +void add_seg(int x, int y, int len, int horizontal) +{ + segment s; + s.x = x; + s.y = y; + s.len = len; + s.dir = horizontal; + assert(s.x == x); + assert(s.y == y); + assert(s.len == len); + stb_arr_push(segments, s); +} +#else +typedef struct +{ + unsigned char first_segment:8; + unsigned char first_v_segment:8; + unsigned char advance:5; + unsigned char voff:1; +} chardata; + +#define X_LIMIT 1 +#define LEN_LIMIT 7 + +typedef struct +{ + unsigned char dx:1; + unsigned char y:4; + unsigned char len:3; +} segment; + +segment *segments; +segment *vsegments; + +void add_seg(int x, int y, int len, int horizontal) +{ + segment s; + + while (x - last_x[horizontal] > X_LIMIT) { + add_seg(last_x[horizontal] + X_LIMIT, 0, 0, horizontal); + } + while (len > LEN_LIMIT) { + add_seg(x, y, LEN_LIMIT, horizontal); + len -= LEN_LIMIT; + x += LEN_LIMIT*horizontal; + y += LEN_LIMIT*!horizontal; + } + + s.dx = x - last_x[horizontal]; + s.y = y; + s.len = len; + non_empty += len != 0; + //assert(s.x == x); + assert(s.y == y); + assert(s.len == len); + ++num_seg[horizontal]; + if (horizontal) + stb_arr_push(segments, s); + else + stb_arr_push(vsegments, s); + last_x[horizontal] = x; +} + +void print_segments(segment *s) +{ + int i, hpos; + printf(" "); + hpos = 4; + for (i=0; i < stb_arr_len(s); ++i) { + // repack for portability + unsigned char seg = s[i].len + s[i].dx*8 + s[i].y*16; + hpos += printf("%d,", seg); + if (hpos > 72 && i+1 < stb_arr_len(s)) { + hpos = 4; + printf("\n "); + } + } + printf("\n"); +} + +#endif + +chardata charinfo[128]; + +int parse_char(int x, chardata *c, int offset) +{ + int start_x = x, end_x, top_y = 0, y; + + c->first_segment = stb_arr_len(segments); + c->first_v_segment = stb_arr_len(vsegments) - offset; + assert(c->first_segment == stb_arr_len(segments)); + assert(c->first_v_segment + offset == stb_arr_len(vsegments)); + + // find advance distance + end_x = x+1; + while (data[end_x*3] == 255) + ++end_x; + c->advance = end_x - start_x + 1; + + last_x[0] = last_x[1] = 0; + last_y[0] = last_y[1] = 0; + + for (y=2; y < h; ++y) { + for (x=start_x; x < end_x; ++x) { + if (data[y*3*w+x*3+1] < 255) { + top_y = y; + break; + } + } + if (top_y) + break; + } + c->voff = top_y > 2; + if (top_y > 2) + top_y = 3; + + for (x=start_x; x < end_x; ++x) { + int y; + for (y=2; y < h; ++y) { + if (data[y*3*w+x*3+1] < 255) { + if (data[y*3*w+x*3+0] == 255) { // red + int len=0; + while (y+len < h && data[(y+len)*3*w+x*3+0] == 255 && data[(y+len)*3*w+x*3+1] == 0) { + data[(y+len)*3*w+x*3+0] = 0; + ++len; + } + add_seg(x-start_x,y-top_y,len,0); + } + if (data[y*3*w+x*3+2] == 255) { // blue + int len=0; + while (x+len < end_x && data[y*3*w+(x+len)*3+2] == 255 && data[y*3*w+(x+len)*3+1] == 0) { + data[y*3*w+(x+len)*3+2] = 0; + ++len; + } + add_seg(x-start_x,y-top_y,len,1); + } + } + } + } + return end_x; +} + + +int main(int argc, char **argv) +{ + int c, x=0; + data = stbi_load("easy_font_raw.png", &w, &h, 0, 3); + for (c=32; c < 127; ++c) { + x = parse_char(x, &charinfo[c], 0); + printf("%3d -- %3d %3d\n", c, charinfo[c].first_segment, charinfo[c].first_v_segment); + } + printf("===\n"); + printf("%d %d %d\n", num_seg[0], num_seg[1], non_empty); + printf("%d\n", sizeof(segments[0]) * stb_arr_len(segments)); + printf("%d\n", sizeof(segments[0]) * stb_arr_len(segments) + sizeof(segments[0]) * stb_arr_len(vsegments) + sizeof(charinfo[32])*95); + + printf("struct {\n" + " unsigned char advance;\n" + " unsigned char h_seg;\n" + " unsigned char v_seg;\n" + "} stb_easy_font_charinfo[96] = {\n"); + charinfo[c].first_segment = stb_arr_len(segments); + charinfo[c].first_v_segment = stb_arr_len(vsegments); + for (c=32; c < 128; ++c) { + if ((c & 3) == 0) printf(" "); + printf("{ %2d,%3d,%3d },", + charinfo[c].advance + 16*charinfo[c].voff, + charinfo[c].first_segment, + charinfo[c].first_v_segment); + if ((c & 3) == 3) printf("\n"); else printf(" "); + } + printf("};\n\n"); + + printf("unsigned char stb_easy_font_hseg[%d] = {\n", stb_arr_len(segments)); + print_segments(segments); + printf("};\n\n"); + + printf("unsigned char stb_easy_font_vseg[%d] = {\n", stb_arr_len(vsegments)); + print_segments(vsegments); + printf("};\n"); + return 0; +} diff --git a/impeller/third_party/stb/stb/tools/make_readme.c b/impeller/third_party/stb/stb/tools/make_readme.c new file mode 100644 index 0000000000000..224f28974fd8d --- /dev/null +++ b/impeller/third_party/stb/stb/tools/make_readme.c @@ -0,0 +1,61 @@ +#define STB_DEFINE +#include "../stb.h" + +int main(int argc, char **argv) +{ + int i; + int hlen, flen, listlen, total_lines = 0; + char *header = stb_file("README.header.md", &hlen); // stb_file - read file into malloc()ed buffer + char *footer = stb_file("README.footer.md", &flen); // stb_file - read file into malloc()ed buffer + char **list = stb_stringfile("README.list", &listlen); // stb_stringfile - read file lines into malloced array of strings + + FILE *f = fopen("../README.md", "wb"); + + fprintf(f, "\n\n"); + fwrite(header, 1, hlen, f); + + for (i=0; i < listlen; ++i) { + int num,j; + char **tokens = stb_tokens_stripwhite(list[i], "|", &num); // stb_tokens -- tokenize string into malloced array of strings + int num_lines; + char **lines = stb_stringfile(stb_sprintf("../%s", tokens[0]), &num_lines); + char *s1, *s2,*s3; + s1 = strchr(lines[0], '-'); + if (!s1) stb_fatal("Couldn't find '-' before version number in %s", tokens[0]); // stb_fatal -- print error message & exit + s2 = strchr(s1+2, '-'); + if (!s2) stb_fatal("Couldn't find '-' after version number in %s", tokens[0]); // stb_fatal -- print error message & exit + *s2 = 0; + s1 += 1; + s1 = stb_trimwhite(s1); // stb_trimwhite -- advance pointer to after whitespace & delete trailing whitespace + if (*s1 == 'v') ++s1; + s3 = tokens[0]; + stb_trimwhite(s3); + if (strlen(s3) < 21) { + fprintf(f, "**%s** | %s", tokens[0], s1); + } else { + char buffer[256]; + strncpy(buffer, s3, 18); + buffer[18] = 0; + strcat(buffer, "..."); + fprintf(f, "**%s** | %s", buffer, s1); + } + s1 = stb_trimwhite(tokens[1]); // stb_trimwhite -- advance pointer to after whitespace & delete trailing whitespace + s2 = stb_dupreplace(s1, " ", " "); // stb_dupreplace -- search & replace string and malloc result + fprintf(f, " | %s", s2); + free(s2); + fprintf(f, " | %d", num_lines); + total_lines += num_lines; + for (j=2; j < num; ++j) + fprintf(f, " | %s", tokens[j]); + fprintf(f, "\n"); + } + + fprintf(f, "\n"); + fprintf(f, "Total libraries: %d \n", listlen); + fprintf(f, "Total lines of C code: %d\n\n", total_lines); + + fwrite(footer, 1, flen, f); + fclose(f); + + return 0; +} diff --git a/impeller/third_party/stb/stb/tools/make_readme.dsp b/impeller/third_party/stb/stb/tools/make_readme.dsp new file mode 100644 index 0000000000000..232dd86944c68 --- /dev/null +++ b/impeller/third_party/stb/stb/tools/make_readme.dsp @@ -0,0 +1,97 @@ +# Microsoft Developer Studio Project File - Name="make_readme" - Package Owner=<4> +# Microsoft Developer Studio Generated Build File, Format Version 6.00 +# ** DO NOT EDIT ** + +# TARGTYPE "Win32 (x86) Console Application" 0x0103 + +CFG=make_readme - Win32 Debug +!MESSAGE This is not a valid makefile. To build this project using NMAKE, +!MESSAGE use the Export Makefile command and run +!MESSAGE +!MESSAGE NMAKE /f "make_readme.mak". +!MESSAGE +!MESSAGE You can specify a configuration when running NMAKE +!MESSAGE by defining the macro CFG on the command line. For example: +!MESSAGE +!MESSAGE NMAKE /f "make_readme.mak" CFG="make_readme - Win32 Debug" +!MESSAGE +!MESSAGE Possible choices for configuration are: +!MESSAGE +!MESSAGE "make_readme - Win32 Release" (based on "Win32 (x86) Console Application") +!MESSAGE "make_readme - Win32 Debug" (based on "Win32 (x86) Console Application") +!MESSAGE + +# Begin Project +# PROP AllowPerConfigDependencies 0 +# PROP Scc_ProjName "" +# PROP Scc_LocalPath "" +CPP=cl.exe +RSC=rc.exe + +!IF "$(CFG)" == "make_readme - Win32 Release" + +# PROP BASE Use_MFC 0 +# PROP BASE Use_Debug_Libraries 0 +# PROP BASE Output_Dir "Release" +# PROP BASE Intermediate_Dir "Release" +# PROP BASE Target_Dir "" +# PROP Use_MFC 0 +# PROP Use_Debug_Libraries 0 +# PROP Output_Dir "Release" +# PROP Intermediate_Dir "Release" +# PROP Target_Dir "" +# ADD BASE CPP /nologo /W3 /GX /O2 /D "WIN32" /D "NDEBUG" /D "_CONSOLE" /D "_MBCS" /YX /FD /c +# ADD CPP /nologo /W3 /GX /O2 /D "WIN32" /D "NDEBUG" /D "_CONSOLE" /D "_MBCS" /YX /FD /c +# ADD BASE RSC /l 0x409 /d "NDEBUG" +# ADD RSC /l 0x409 /d "NDEBUG" +BSC32=bscmake.exe +# ADD BASE BSC32 /nologo +# ADD BSC32 /nologo +LINK32=link.exe +# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:console /machine:I386 +# ADD LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:console /machine:I386 + +!ELSEIF "$(CFG)" == "make_readme - Win32 Debug" + +# PROP BASE Use_MFC 0 +# PROP BASE Use_Debug_Libraries 1 +# PROP BASE Output_Dir "Debug" +# PROP BASE Intermediate_Dir "Debug" +# PROP BASE Target_Dir "" +# PROP Use_MFC 0 +# PROP Use_Debug_Libraries 1 +# PROP Output_Dir "Debug" +# PROP Intermediate_Dir "Debug\make_readme" +# PROP Target_Dir "" +# ADD BASE CPP /nologo /W3 /Gm /GX /ZI /Od /D "WIN32" /D "_DEBUG" /D "_CONSOLE" /D "_MBCS" /YX /FD /GZ /c +# ADD CPP /nologo /W3 /Gm /GX /ZI /Od /D "WIN32" /D "_DEBUG" /D "_CONSOLE" /D "_MBCS" /FD /GZ /c +# SUBTRACT CPP /YX +# ADD BASE RSC /l 0x409 /d "_DEBUG" +# ADD RSC /l 0x409 /d "_DEBUG" +BSC32=bscmake.exe +# ADD BASE BSC32 /nologo +# ADD BSC32 /nologo +LINK32=link.exe +# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:console /debug /machine:I386 /pdbtype:sept +# ADD LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:console /debug /machine:I386 /pdbtype:sept + +!ENDIF + +# Begin Target + +# Name "make_readme - Win32 Release" +# Name "make_readme - Win32 Debug" +# Begin Source File + +SOURCE=.\make_readme.c +# End Source File +# Begin Source File + +SOURCE=.\README.header.md +# End Source File +# Begin Source File + +SOURCE=.\README.list +# End Source File +# End Target +# End Project diff --git a/impeller/third_party/stb/stb/tools/mr.bat b/impeller/third_party/stb/stb/tools/mr.bat new file mode 100644 index 0000000000000..475bc4f4ff407 --- /dev/null +++ b/impeller/third_party/stb/stb/tools/mr.bat @@ -0,0 +1 @@ +debug\make_readme diff --git a/impeller/third_party/stb/stb/tools/unicode.c b/impeller/third_party/stb/stb/tools/unicode.c new file mode 100644 index 0000000000000..8b9d8da91b45a --- /dev/null +++ b/impeller/third_party/stb/stb/tools/unicode.c @@ -0,0 +1,749 @@ +#define STB_DEFINE +#include "../stb.h" + +// create unicode mappings +// +// Two kinds of mappings: +// map to a number +// map to a bit +// +// For mapping to a number, we use the following strategy: +// +// User supplies: +// 1. a table of numbers (for now we use uint16, so full Unicode table is 4MB) +// 2. a "don't care" value +// 3. define a 'fallback' value (typically 0) +// 4. define a fast-path range (typically 0..255 or 0..1023) [@TODO: automate detecting this] +// +// Code: +// 1. Determine range of *end* of unicode codepoints (U+10FFFF and down) which +// all have the same value (or don't care). If large enough, emit this as a +// special case in the code. +// 2. Repeat above, limited to at most U+FFFF. +// 3. Cluster the data into intervals of 8,16,32,64,128,256 numeric values. +// 3a. If all the values in an interval are fallback/dont-care, no further processing +// 3b. Find the "trimmed range" outside which all the values are the fallback or don't care +// 3c. Find the "special trimmed range" outside which all the values are some constant or don't care +// 4. Pack the clusters into continuous memory, and find previous instances of +// the cluster. Repeat for trimmed & special-trimmed. In the first case, find +// previous instances of the cluster (allow don't-care to match in either +// direction), both aligned and mis-aligned; in the latter, starting where +// things start or mis-aligned. Build an index table specifiying the +// location of each cluster (and its length). Allow an extra indirection here; +// the full-sized index can index a smaller table which has the actual offset +// (and lengths). +// 5. Associate with each packed continuous memory above the amount of memory +// required to store the data w/ smallest datatype (of uint8, uint16, uint32). +// Discard the continuous memory. Recurse on each index table, but avoid the +// smaller packing. +// +// For mapping to a bit, we pack the results for 8 characters into a byte, and then apply +// the above strategy. Note that there may be more optimal approaches with e.g. packing +// 8 different bits into a single structure, though, which we should explore eventually. + + +// currently we limit *indices* to being 2^16, and we pack them as +// index + end_trim*2^16 + start_trim*2^24; specials have to go in a separate table +typedef uint32 uval; +#define UVAL_DONT_CARE_DEFAULT 0xffffffff + +typedef struct +{ + uval *input; + uint32 dont_care; + uint32 fallback; + int fastpath; + int length; + int depth; + int has_sign; + int splittable; + int replace_fallback_with_codepoint; + size_t input_size; + size_t inherited_storage; +} table; + +typedef struct +{ + int split_log2; + table result; // index into not-returned table + int storage; +} output; + +typedef struct +{ + table t; + char **output_name; +} info; + +typedef struct +{ + size_t path; + size_t size; +} result; + +typedef struct +{ + uint8 trim_end; + uint8 trim_start; + uint8 special; + uint8 aligned; + uint8 indirect; + + uint16 overhead; // add some forced overhead for each mode to avoid getting complex encoding when it doesn't save much + +} mode_info; + +mode_info modes[] = +{ + { 0,0,0,0,0, 32, }, + { 0,0,0,0,1, 100, }, + { 0,0,0,1,0, 32, }, + { 0,0,0,1,1, 100, }, + { 0,0,1,0,1, 100, }, + { 0,0,1,1,0, 32, }, + { 0,0,1,1,1, 200, }, + { 1,0,0,0,0, 100, }, + { 1,0,0,0,1, 120, }, + { 1,1,0,0,0, 100, }, + { 1,1,0,0,1, 130, }, + { 1,0,1,0,0, 130, }, + { 1,0,1,0,1, 180, }, + { 1,1,1,0,0, 180, }, + { 1,1,1,0,1, 200, }, +}; + +#define MODECOUNT (sizeof(modes)/sizeof(modes[0])) +#define CLUSTERSIZECOUNT 6 // 8,16, 32,64, 128,256 + +size_t size_for_max_number(uint32 number) +{ + if (number == 0) return 0; + if (number < 256) return 1; + if (number < 256*256) return 2; + if (number < 256*256*256) return 3; + return 4; +} + +size_t size_for_max_number_aligned(uint32 number) +{ + size_t n = size_for_max_number(number); + return n == 3 ? 4 : n; +} + +uval get_data(uval *data, int offset, uval *end) +{ + if (data + offset >= end) + return 0; + else + return data[offset]; +} + +int safe_len(uval *data, int len, uval *end) +{ + if (len > end - data) + return end - data; + return len; +} + +uval tempdata[256]; +int dirty=0; + +size_t find_packed(uval **packed, uval *data, int len, int aligned, int fastpath, uval *end, int offset, int replace) +{ + int packlen = stb_arr_len(*packed); + int i,p; + + if (data+len > end || replace) { + int safelen = safe_len(data, len, end); + memset(tempdata, 0, dirty*sizeof(tempdata[0])); + memcpy(tempdata, data, safelen * sizeof(data[0])); + data = tempdata; + dirty = len; + } + if (replace) { + int i; + int safelen = safe_len(data, len, end); + for (i=0; i < safelen; ++i) + if (data[i] == 0) + data[i] = offset+i; + } + + if (len <= 0) + return 0; + if (!fastpath) { + if (aligned) { + for (i=0; i < packlen; i += len) + if ((*packed)[i] == data[0] && 0==memcmp(&(*packed)[i], data, len * sizeof(uval))) + return i / len; + } else { + for (i=0; i < packlen-len+1; i += 1 ) + if ((*packed)[i] == data[0] && 0==memcmp(&(*packed)[i], data, len * sizeof(uval))) + return i; + } + } + p = stb_arr_len(*packed); + for (i=0; i < len; ++i) + stb_arr_push(*packed, data[i]); + return p; +} + +void output_table(char *name1, char *name2, uval *data, int length, int sign, char **names) +{ + char temp[20]; + uval maxv = 0; + int bytes, numlen, at_newline; + int linelen = 79; // @TODO: make table more readable by choosing a length that's a multiple? + int i,pos, do_split=0; + for (i=0; i < length; ++i) + if (sign) + maxv = stb_max(maxv, (uval)abs((int)data[i])); + else + maxv = stb_max(maxv, data[i]); + bytes = size_for_max_number_aligned(maxv); + sprintf(temp, "%d", maxv); + numlen=strlen(temp); + if (sign) + ++numlen; + + if (bytes == 0) + return; + + printf("uint%d %s%s[%d] = {\n", bytes*8, name1, name2, length); + at_newline = 1; + for (i=0; i < length; ++i) { + if (pos + numlen + 2 > linelen) { + printf("\n"); + at_newline = 1; + pos = 0; + } + if (at_newline) { + printf(" "); + pos = 2; + at_newline = 0; + } else { + printf(" "); + ++pos; + } + printf("%*d,", numlen, data[i]); + pos += numlen+1; + } + if (!at_newline) printf("\n"); + printf("};\n"); +} + +void output_table_with_trims(char *name1, char *name2, uval *data, int length) +{ + uval maxt=0, maxp=0; + int i,d,s,e, count; + // split the table into two pieces + uval *trims = NULL; + + if (length == 0) + return; + + for (i=0; i < stb_arr_len(data); ++i) { + stb_arr_push(trims, data[i] >> 16); + data[i] &= 0xffff; + maxt = stb_max(maxt, trims[i]); + maxp = stb_max(maxp, data[i]); + } + + d=s=e=1; + if (maxt >= 256) { + // need to output start & end values + if (maxp >= 256) { + // can pack into a single table + printf("struct { uint16 val; uint8 start, end; } %s%s[%d] = {\n", name1, name2, length); + } else { + output_table(name1, name2, data, length, 0, 0); + d=0; + printf("struct { uint8 start, end; } %s%s_trim[%d] = {\n", name1, name2, length); + } + } else if (maxt > 0) { + if (maxp >= 256) { + output_table(name1, name2, data, length, 0, 0); + output_table(name1, stb_sprintf("%s_end", name2), trims, length, 0, 0); + return; + } else { + printf("struct { uint8 val, end; } %s%s[%d] = {\n", name1, name2, length); + s=0; + } + } else { + output_table(name1, name2, data, length, 0, 0); + return; + } + // d or s can be zero (but not both), e is always present and last + count = d + s + e; + assert(count >= 2 && count <= 3); + + { + char temp[60]; + uval maxv = 0; + int numlen, at_newline, len; + int linelen = 79; // @TODO: make table more readable by choosing a length that's a multiple? + int i,pos, do_split=0; + numlen = 0; + for (i=0; i < length; ++i) { + if (count == 2) + sprintf(temp, "{%d,%d}", d ? data[i] : (trims[i]>>8), trims[i]&255); + else + sprintf(temp, "{%d,%d,%d}", data[i], trims[i]>>8, trims[i]&255); + len = strlen(temp); + numlen = stb_max(len, numlen); + } + + at_newline = 1; + for (i=0; i < length; ++i) { + if (pos + numlen + 2 > linelen) { + printf("\n"); + at_newline = 1; + pos = 0; + } + if (at_newline) { + printf(" "); + pos = 2; + at_newline = 0; + } else { + printf(" "); + ++pos; + } + if (count == 2) + sprintf(temp, "{%d,%d}", d ? data[i] : (trims[i]>>8), trims[i]&255); + else + sprintf(temp, "{%d,%d,%d}", data[i], trims[i]>>8, trims[i]&255); + printf("%*s,", numlen, temp); + pos += numlen+1; + } + if (!at_newline) printf("\n"); + printf("};\n"); + } +} + +int weight=1; + +table pack_for_mode(table *t, int mode, char *table_name) +{ + size_t extra_size; + int i; + uval maxv; + mode_info mi = modes[mode % MODECOUNT]; + int size = 8 << (mode / MODECOUNT); + table newtab; + uval *packed = NULL; + uval *index = NULL; + uval *indirect = NULL; + uval *specials = NULL; + newtab.dont_care = UVAL_DONT_CARE_DEFAULT; + if (table_name) + printf("// clusters of %d\n", size); + for (i=0; i < t->length; i += size) { + uval newval; + int fastpath = (i < t->fastpath); + if (mi.special) { + int end_trim = size-1; + int start_trim = 0; + uval special; + // @TODO: pick special from start or end instead of only end depending on which is longer + for(;;) { + special = t->input[i + end_trim]; + if (special != t->dont_care || end_trim == 0) + break; + --end_trim; + } + // at this point, special==inp[end_trim], and end_trim >= 0 + if (special == t->dont_care && !fastpath) { + // entire block is don't care, so OUTPUT don't care + stb_arr_push(index, newtab.dont_care); + continue; + } else { + uval pos, trim; + if (mi.trim_end && !fastpath) { + while (end_trim >= 0) { + if (t->input[i + end_trim] == special || t->input[i + end_trim] == t->dont_care) + --end_trim; + else + break; + } + } + + if (mi.trim_start && !fastpath) { + while (start_trim < end_trim) { + if (t->input[i + start_trim] == special || t->input[i + start_trim] == t->dont_care) + ++start_trim; + else + break; + } + } + + // end_trim points to the last character we have to output + + // find the first match, or add it + pos = find_packed(&packed, &t->input[i+start_trim], end_trim-start_trim+1, mi.aligned, fastpath, &t->input[t->length], i+start_trim, t->replace_fallback_with_codepoint); + + // encode as a uval + if (!mi.trim_end) { + if (end_trim == 0) + pos = special; + else + pos = pos | 0x80000000; + } else { + assert(end_trim < size && end_trim >= -1); + if (!fastpath) assert(end_trim < size-1); // special always matches last one + assert(end_trim < size && end_trim+1 >= 0); + if (!fastpath) assert(end_trim+1 < size); + + if (mi.trim_start) + trim = start_trim*256 + (end_trim+1); + else + trim = end_trim+1; + + assert(pos < 65536); // @TODO: if this triggers, just bail on this search path + pos = pos + (trim << 16); + } + + newval = pos; + + stb_arr_push(specials, special); + } + } else if (mi.trim_end) { + int end_trim = size-1; + int start_trim = 0; + uval pos, trim; + + while (end_trim >= 0 && !fastpath) + if (t->input[i + end_trim] == t->fallback || t->input[i + end_trim] == t->dont_care) + --end_trim; + else + break; + + if (mi.trim_start && !fastpath) { + while (start_trim < end_trim) { + if (t->input[i + start_trim] == t->fallback || t->input[i + start_trim] == t->dont_care) + ++start_trim; + else + break; + } + } + + // end_trim points to the last character we have to output, and can be -1 + ++end_trim; // make exclusive at end + + if (end_trim == 0 && size == 256) + start_trim = end_trim = 1; // we can't make encode a length from 0..256 in 8 bits, so restrict end_trim to 1..256 + + // find the first match, or add it + pos = find_packed(&packed, &t->input[i+start_trim], end_trim - start_trim, mi.aligned, fastpath, &t->input[t->length], i+start_trim, t->replace_fallback_with_codepoint); + + assert(end_trim <= size && end_trim >= 0); + if (size == 256) + assert(end_trim-1 < 256 && end_trim-1 >= 0); + else + assert(end_trim < 256 && end_trim >= 0); + if (size == 256) + --end_trim; + + if (mi.trim_start) + trim = start_trim*256 + end_trim; + else + trim = end_trim; + + assert(pos < 65536); // @TODO: if this triggers, just bail on this search path + pos = pos + (trim << 16); + + newval = pos; + } else { + newval = find_packed(&packed, &t->input[i], size, mi.aligned, fastpath, &t->input[t->length], i, t->replace_fallback_with_codepoint); + } + + if (mi.indirect) { + int j; + for (j=0; j < stb_arr_len(indirect); ++j) + if (indirect[j] == newval) + break; + if (j == stb_arr_len(indirect)) + stb_arr_push(indirect, newval); + stb_arr_push(index, j); + } else { + stb_arr_push(index, newval); + } + } + + // total up the new size for everything but the index table + extra_size = mi.overhead * weight; // not the actual overhead cost; a penalty to avoid excessive complexity + extra_size += 150; // per indirection + if (table_name) + extra_size = 0; + + if (t->has_sign) { + // 'packed' contains two values, which should be packed positive & negative for size + uval maxv2; + for (i=0; i < stb_arr_len(packed); ++i) + if (packed[i] & 0x80000000) + maxv2 = stb_max(maxv2, packed[i]); + else + maxv = stb_max(maxv, packed[i]); + maxv = stb_max(maxv, maxv2) << 1; + } else { + maxv = 0; + for (i=0; i < stb_arr_len(packed); ++i) + if (packed[i] > maxv && packed[i] != t->dont_care) + maxv = packed[i]; + } + extra_size += stb_arr_len(packed) * (t->splittable ? size_for_max_number(maxv) : size_for_max_number_aligned(maxv)); + if (table_name) { + if (t->splittable) + output_table_with_trims(table_name, "", packed, stb_arr_len(packed)); + else + output_table(table_name, "", packed, stb_arr_len(packed), t->has_sign, NULL); + } + + maxv = 0; + for (i=0; i < stb_arr_len(specials); ++i) + if (specials[i] > maxv) + maxv = specials[i]; + extra_size += stb_arr_len(specials) * size_for_max_number_aligned(maxv); + if (table_name) + output_table(table_name, "_default", specials, stb_arr_len(specials), 0, NULL); + + maxv = 0; + for (i=0; i < stb_arr_len(indirect); ++i) + if (indirect[i] > maxv) + maxv = indirect[i]; + extra_size += stb_arr_len(indirect) * size_for_max_number(maxv); + + if (table_name && stb_arr_len(indirect)) { + if (mi.trim_end) + output_table_with_trims(table_name, "_index", indirect, stb_arr_len(indirect)); + else { + assert(0); // this case should only trigger in very extreme circumstances + output_table(table_name, "_index", indirect, stb_arr_len(indirect), 0, NULL); + } + mi.trim_end = mi.special = 0; + } + + if (table_name) + printf("// above tables should be %d bytes\n", extra_size); + + maxv = 0; + for (i=0; i < stb_arr_len(index); ++i) + if (index[i] > maxv && index[i] != t->dont_care) + maxv = index[i]; + newtab.splittable = mi.trim_end; + newtab.input_size = newtab.splittable ? size_for_max_number(maxv) : size_for_max_number_aligned(maxv); + newtab.input = index; + newtab.length = stb_arr_len(index); + newtab.inherited_storage = t->inherited_storage + extra_size; + newtab.fastpath = 0; + newtab.depth = t->depth+1; + stb_arr_free(indirect); + stb_arr_free(packed); + stb_arr_free(specials); + + return newtab; +} + +result pack_table(table *t, size_t path, int min_storage) +{ + int i; + result best; + best.size = t->inherited_storage + t->input_size * t->length; + best.path = path; + + if ((int) t->inherited_storage > min_storage) { + best.size = stb_max(best.size, t->inherited_storage); + return best; + } + + if (t->length <= 256 || t->depth >= 4) { + //printf("%08x: %7d\n", best.path, best.size); + return best; + } + + path <<= 7; + for (i=0; i < MODECOUNT * CLUSTERSIZECOUNT; ++i) { + table newtab; + result r; + newtab = pack_for_mode(t, i, 0); + r = pack_table(&newtab, path+i+1, min_storage); + if (r.size < best.size) + best = r; + stb_arr_free(newtab.input); + //printf("Size: %6d + %6d\n", newtab.inherited_storage, newtab.input_size * newtab.length); + } + return best; +} + +int pack_table_by_modes(table *t, int *modes) +{ + table s = *t; + while (*modes > -1) { + table newtab; + newtab = pack_for_mode(&s, *modes, 0); + if (s.input != t->input) + stb_arr_free(s.input); + s = newtab; + ++modes; + } + return s.inherited_storage + s.input_size * s.length; +} + +int strip_table(table *t, int exceptions) +{ + uval terminal_value; + int p = t->length-1; + while (t->input[p] == t->dont_care) + --p; + terminal_value = t->input[p]; + + while (p >= 0x10000) { + if (t->input[p] != terminal_value && t->input[p] != t->dont_care) { + if (exceptions) + --exceptions; + else + break; + } + --p; + } + return p+1; // p is a character we must output +} + +void optimize_table(table *t, char *table_name) +{ + int modelist[3] = { 85, -1 }; + int modes[8]; + int num_modes = 0; + int decent_size; + result r; + size_t path; + table s; + + // strip tail end of table + int orig_length = t->length; + int threshhold = 0xffff; + int p = strip_table(t, 2); + int len_saved = t->length - p; + if (len_saved >= threshhold) { + t->length = p; + while (p > 0x10000) { + p = strip_table(t, 0); + len_saved = t->length - p; + if (len_saved < 0x10000) + break; + len_saved = orig_length - p; + if (len_saved < threshhold) + break; + threshhold *= 2; + } + } + + t->depth = 1; + + + // find size of table if we use path 86 + decent_size = pack_table_by_modes(t, modelist); + + + #if 1 + // find best packing of remainder of table by exploring tree of packings + r = pack_table(t, 0, decent_size); + // use the computed 'path' to evaluate and output tree + path = r.path; + #else + path = 86;//90;//132097; + #endif + + while (path) { + modes[num_modes++] = (path & 127) - 1; + path >>= 7; + } + + printf("// modes: %d\n", r.path); + s = *t; + while (num_modes > 0) { + char name[256]; + sprintf(name, "%s_%d", table_name, num_modes+1); + --num_modes; + s = pack_for_mode(&s, modes[num_modes], name); + } + // output the final table as-is + if (s.splittable) + output_table_with_trims(table_name, "_1", s.input, s.length); + else + output_table(table_name, "_1", s.input, s.length, 0, NULL); +} + +uval unicode_table[0x110000]; + +typedef struct +{ + uval lo,hi; +} char_range; + +char_range get_range(char *str) +{ + char_range cr; + char *p; + cr.lo = strtol(str, &p, 16); + p = stb_skipwhite(p); + if (*p == '.') + cr.hi = strtol(p+2, NULL, 16); + else + cr.hi = cr.lo; + return cr; +} + +char *skip_semi(char *s, int count) +{ + while (count) { + s = strchr(s, ';'); + assert(s != NULL); + ++s; + --count; + } + return s; +} + +int main(int argc, char **argv) +{ + table t; + uval maxv=0; + int i,n=0; + char **s = stb_stringfile("../../data/UnicodeData.txt", &n); + assert(s); + for (i=0; i < n; ++i) { + if (s[i][0] == '#' || s[i][0] == '\n' || s[i][0] == 0) + ; + else { + char_range cr = get_range(s[i]); + char *t = skip_semi(s[i], 13); + uval j, v; + if (*t == ';' || *t == '\n' || *t == 0) + v = 0; + else { + v = strtol(t, NULL, 16); + if (v < 65536) { + maxv = stb_max(v, maxv); + for (j=cr.lo; j <= cr.hi; ++j) { + unicode_table[j] = v; + //printf("%06x => %06x\n", j, v); + } + } + } + } + } + + t.depth = 0; + t.dont_care = UVAL_DONT_CARE_DEFAULT; + t.fallback = 0; + t.fastpath = 256; + t.inherited_storage = 0; + t.has_sign = 0; + t.splittable = 0; + t.input = unicode_table; + t.input_size = size_for_max_number(maxv); + t.length = 0x110000; + t.replace_fallback_with_codepoint = 1; + + optimize_table(&t, "stbu_upppercase"); + return 0; +} diff --git a/impeller/third_party/stb/stb/tools/unicode/unicode.dsp b/impeller/third_party/stb/stb/tools/unicode/unicode.dsp new file mode 100644 index 0000000000000..78e6a5ba67bf0 --- /dev/null +++ b/impeller/third_party/stb/stb/tools/unicode/unicode.dsp @@ -0,0 +1,88 @@ +# Microsoft Developer Studio Project File - Name="unicode" - Package Owner=<4> +# Microsoft Developer Studio Generated Build File, Format Version 6.00 +# ** DO NOT EDIT ** + +# TARGTYPE "Win32 (x86) Console Application" 0x0103 + +CFG=unicode - Win32 Debug +!MESSAGE This is not a valid makefile. To build this project using NMAKE, +!MESSAGE use the Export Makefile command and run +!MESSAGE +!MESSAGE NMAKE /f "unicode.mak". +!MESSAGE +!MESSAGE You can specify a configuration when running NMAKE +!MESSAGE by defining the macro CFG on the command line. For example: +!MESSAGE +!MESSAGE NMAKE /f "unicode.mak" CFG="unicode - Win32 Debug" +!MESSAGE +!MESSAGE Possible choices for configuration are: +!MESSAGE +!MESSAGE "unicode - Win32 Release" (based on "Win32 (x86) Console Application") +!MESSAGE "unicode - Win32 Debug" (based on "Win32 (x86) Console Application") +!MESSAGE + +# Begin Project +# PROP AllowPerConfigDependencies 0 +# PROP Scc_ProjName "" +# PROP Scc_LocalPath "" +CPP=cl.exe +RSC=rc.exe + +!IF "$(CFG)" == "unicode - Win32 Release" + +# PROP BASE Use_MFC 0 +# PROP BASE Use_Debug_Libraries 0 +# PROP BASE Output_Dir "Release" +# PROP BASE Intermediate_Dir "Release" +# PROP BASE Target_Dir "" +# PROP Use_MFC 0 +# PROP Use_Debug_Libraries 0 +# PROP Output_Dir "Release" +# PROP Intermediate_Dir "Release" +# PROP Target_Dir "" +# ADD BASE CPP /nologo /W3 /GX /O2 /D "WIN32" /D "NDEBUG" /D "_CONSOLE" /D "_MBCS" /YX /FD /c +# ADD CPP /nologo /W3 /GX /O2 /D "WIN32" /D "NDEBUG" /D "_CONSOLE" /D "_MBCS" /YX /FD /c +# ADD BASE RSC /l 0x409 /d "NDEBUG" +# ADD RSC /l 0x409 /d "NDEBUG" +BSC32=bscmake.exe +# ADD BASE BSC32 /nologo +# ADD BSC32 /nologo +LINK32=link.exe +# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:console /machine:I386 +# ADD LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:console /machine:I386 + +!ELSEIF "$(CFG)" == "unicode - Win32 Debug" + +# PROP BASE Use_MFC 0 +# PROP BASE Use_Debug_Libraries 1 +# PROP BASE Output_Dir "Debug" +# PROP BASE Intermediate_Dir "Debug" +# PROP BASE Target_Dir "" +# PROP Use_MFC 0 +# PROP Use_Debug_Libraries 1 +# PROP Output_Dir "Debug" +# PROP Intermediate_Dir "Debug" +# PROP Target_Dir "" +# ADD BASE CPP /nologo /W3 /Gm /GX /ZI /Od /D "WIN32" /D "_DEBUG" /D "_CONSOLE" /D "_MBCS" /YX /FD /GZ /c +# ADD CPP /nologo /W3 /Gm /GX /ZI /Od /D "WIN32" /D "_DEBUG" /D "_CONSOLE" /D "_MBCS" /YX /FD /GZ /c +# ADD BASE RSC /l 0x409 /d "_DEBUG" +# ADD RSC /l 0x409 /d "_DEBUG" +BSC32=bscmake.exe +# ADD BASE BSC32 /nologo +# ADD BSC32 /nologo +LINK32=link.exe +# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:console /debug /machine:I386 /pdbtype:sept +# ADD LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:console /debug /machine:I386 /pdbtype:sept + +!ENDIF + +# Begin Target + +# Name "unicode - Win32 Release" +# Name "unicode - Win32 Debug" +# Begin Source File + +SOURCE=..\unicode.c +# End Source File +# End Target +# End Project From 8e10a5341da1572e4bb59cc15733f3da97b45d11 Mon Sep 17 00:00:00 2001 From: Chinmay Garde Date: Sat, 1 May 2021 20:02:30 -0700 Subject: [PATCH 009/433] Wire up metal library generation. --- impeller/host/BUILD.gn | 11 ++++- impeller/host/Info.plist | 30 ----------- impeller/tools/metal/build_metal_library.py | 55 +++++++++++++++++++++ impeller/tools/metal/metal_library.gni | 36 ++++++++++++++ 4 files changed, 101 insertions(+), 31 deletions(-) delete mode 100644 impeller/host/Info.plist create mode 100644 impeller/tools/metal/build_metal_library.py create mode 100644 impeller/tools/metal/metal_library.gni diff --git a/impeller/host/BUILD.gn b/impeller/host/BUILD.gn index ae7b48e4034c6..87c6992c5e2af 100644 --- a/impeller/host/BUILD.gn +++ b/impeller/host/BUILD.gn @@ -3,6 +3,12 @@ # found in the LICENSE file. import("//flutter/common/config.gni") +import("//flutter/impeller/tools/metal/metal_library.gni") + +metal_library("impeller_host_shaders") { + name = "impeller_host" + sources = [ "shaders.metal" ] +} executable("host") { cflags_objc = flutter_cflags_objc_arc @@ -26,5 +32,8 @@ executable("host") { "ModelIO.framework", ] - deps = [ "//flutter/impeller/impeller" ] + deps = [ + ":impeller_host_shaders", + "//flutter/impeller/impeller", + ] } diff --git a/impeller/host/Info.plist b/impeller/host/Info.plist deleted file mode 100644 index cfbbdb70c466b..0000000000000 --- a/impeller/host/Info.plist +++ /dev/null @@ -1,30 +0,0 @@ - - - - - CFBundleDevelopmentRegion - $(DEVELOPMENT_LANGUAGE) - CFBundleExecutable - $(EXECUTABLE_NAME) - CFBundleIconFile - - CFBundleIdentifier - $(PRODUCT_BUNDLE_IDENTIFIER) - CFBundleInfoDictionaryVersion - 6.0 - CFBundleName - $(PRODUCT_NAME) - CFBundlePackageType - $(PRODUCT_BUNDLE_PACKAGE_TYPE) - CFBundleShortVersionString - 1.0 - CFBundleVersion - 1 - LSMinimumSystemVersion - $(MACOSX_DEPLOYMENT_TARGET) - NSMainStoryboardFile - Main - NSPrincipalClass - NSApplication - - diff --git a/impeller/tools/metal/build_metal_library.py b/impeller/tools/metal/build_metal_library.py new file mode 100644 index 0000000000000..4ef75a4ee00b9 --- /dev/null +++ b/impeller/tools/metal/build_metal_library.py @@ -0,0 +1,55 @@ +# Copyright 2013 The Flutter Authors. All rights reserved. +# Use of this source code is governed by a BSD-style license that can be +# found in the LICENSE file. + +import sys + +import argparse +import errno +import os +import subprocess + +def MakeDirectories(path): + try: + os.makedirs(path) + except OSError as exc: + if exc.errno == errno.EEXIST and os.path.isdir(path): + pass + else: + raise + +def Main(): + parser = argparse.ArgumentParser() + parser.add_argument("--output", + type=str, required=True, + help="The location to generate the Metal library to.") + parser.add_argument("--depfile", + type=str, required=True, + help="The location of the depfile.") + parser.add_argument("--source", + type=str, action="append", required=True, + help="The source file to compile. Can be specified multiple times.") + + args = parser.parse_args() + + MakeDirectories(os.path.dirname(args.depfile)) + + command = [ + "xcrun", + "metal", + # Both user and system header will be tracked. + "-MMD", + "-MF", + args.depfile, + "-o", + args.output + ] + + command += args.source + + subprocess.check_call(command) + +if __name__ == '__main__': + if sys.platform != 'darwin': + raise Exception("This script only runs on Mac") + Main() diff --git a/impeller/tools/metal/metal_library.gni b/impeller/tools/metal/metal_library.gni new file mode 100644 index 0000000000000..2b70e56bc90be --- /dev/null +++ b/impeller/tools/metal/metal_library.gni @@ -0,0 +1,36 @@ +# Copyright 2013 The Flutter Authors. All rights reserved. +# Use of this source code is governed by a BSD-style license that can be +# found in the LICENSE file. + +template("metal_library") { + assert(defined(invoker.name), "Metal library name must be specified.") + assert(defined(invoker.sources), "Metal source files must be specified.") + + metal_library_name = invoker.name + + action("$target_name") { + inputs = invoker.sources + + metal_library_path = "$target_out_dir/shaders/$metal_library_name.metallib" + + outputs = [ metal_library_path ] + + script = "//flutter/impeller/tools/metal/build_metal_library.py" + + depfile = "$target_gen_dir/shader_deps/$metal_library_name.depfile" + + args = [ + "--output", + rebase_path(metal_library_path, root_out_dir), + "--depfile", + rebase_path(depfile), + ] + + foreach(source, invoker.sources) { + args += [ + "--source", + rebase_path(source, root_out_dir), + ] + } + } +} From e1cec68ddb990b9d623bcc993c42f20311cd6ec1 Mon Sep 17 00:00:00 2001 From: Chinmay Garde Date: Sat, 1 May 2021 20:17:07 -0700 Subject: [PATCH 010/433] Fix runtime shader loading. --- impeller/host/BUILD.gn | 9 ++++-- ...ler.m => impeller_host_view_controller.mm} | 0 ...peller_renderer.m => impeller_renderer.mm} | 13 +++++++- impeller/host/{main.m => main.mm} | 0 impeller/host/shaders_location.cc | 31 +++++++++++++++++++ impeller/host/shaders_location.h | 12 +++++++ impeller/tools/metal/metal_library.gni | 2 +- 7 files changed, 62 insertions(+), 5 deletions(-) rename impeller/host/{impeller_host_view_controller.m => impeller_host_view_controller.mm} (100%) rename impeller/host/{impeller_renderer.m => impeller_renderer.mm} (96%) rename impeller/host/{main.m => main.mm} (100%) create mode 100644 impeller/host/shaders_location.cc create mode 100644 impeller/host/shaders_location.h diff --git a/impeller/host/BUILD.gn b/impeller/host/BUILD.gn index 87c6992c5e2af..aead6039aa9f3 100644 --- a/impeller/host/BUILD.gn +++ b/impeller/host/BUILD.gn @@ -19,10 +19,12 @@ executable("host") { sources = [ "ShaderTypes.h", "impeller_host_view_controller.h", - "impeller_host_view_controller.m", + "impeller_host_view_controller.mm", "impeller_renderer.h", - "impeller_renderer.m", - "main.m", + "impeller_renderer.mm", + "main.mm", + "shaders_location.cc", + "shaders_location.h", ] libs = [ @@ -34,6 +36,7 @@ executable("host") { deps = [ ":impeller_host_shaders", + "//flutter/fml", "//flutter/impeller/impeller", ] } diff --git a/impeller/host/impeller_host_view_controller.m b/impeller/host/impeller_host_view_controller.mm similarity index 100% rename from impeller/host/impeller_host_view_controller.m rename to impeller/host/impeller_host_view_controller.mm diff --git a/impeller/host/impeller_renderer.m b/impeller/host/impeller_renderer.mm similarity index 96% rename from impeller/host/impeller_renderer.m rename to impeller/host/impeller_renderer.mm index 36cd725a58c4e..c113879ddc692 100644 --- a/impeller/host/impeller_renderer.m +++ b/impeller/host/impeller_renderer.mm @@ -5,7 +5,9 @@ #import #import +#include "flutter/fml/logging.h" #import "impeller_renderer.h" +#import "shaders_location.h" // Include header shared between C code here, which executes Metal API commands, // and .metal files @@ -82,7 +84,16 @@ - (void)_loadMetalWithView:(nonnull MTKView*)view { _mtlVertexDescriptor.layouts[BufferIndexMeshGenerics].stepFunction = MTLVertexStepFunctionPerVertex; - id defaultLibrary = [_device newDefaultLibrary]; + auto shader_library_path = + impeller::ImpellerShadersLocation("impeller_host.metallib"); + + FML_CHECK(shader_library_path.has_value()); + + NSError* shader_library_error = nil; + + id defaultLibrary = + [_device newLibraryWithFile:@(shader_library_path.value().c_str()) + error:&shader_library_error]; id vertexFunction = [defaultLibrary newFunctionWithName:@"vertexShader"]; diff --git a/impeller/host/main.m b/impeller/host/main.mm similarity index 100% rename from impeller/host/main.m rename to impeller/host/main.mm diff --git a/impeller/host/shaders_location.cc b/impeller/host/shaders_location.cc new file mode 100644 index 0000000000000..3e9fb5518ca99 --- /dev/null +++ b/impeller/host/shaders_location.cc @@ -0,0 +1,31 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "shaders_location.h" + +#include "flutter/fml/file.h" +#include "flutter/fml/paths.h" + +namespace impeller { + +std::optional ImpellerShadersLocation(std::string library_name) { + auto executable_directory = fml::paths::GetExecutableDirectoryPath(); + + if (!executable_directory.first) { + FML_LOG(ERROR) << "Shaders directory could not be found."; + return std::nullopt; + } + + auto path = fml::paths::JoinPaths( + {executable_directory.second, "shaders", library_name}); + + if (!fml::IsFile(path)) { + FML_LOG(ERROR) << "The shader library does not exist: " << path; + return std::nullopt; + } + + return path; +} + +} // namespace impeller diff --git a/impeller/host/shaders_location.h b/impeller/host/shaders_location.h new file mode 100644 index 0000000000000..f449810835e1a --- /dev/null +++ b/impeller/host/shaders_location.h @@ -0,0 +1,12 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include +#include + +namespace impeller { + +std::optional ImpellerShadersLocation(std::string library_name); + +} // namespace impeller diff --git a/impeller/tools/metal/metal_library.gni b/impeller/tools/metal/metal_library.gni index 2b70e56bc90be..ca077e8a3c312 100644 --- a/impeller/tools/metal/metal_library.gni +++ b/impeller/tools/metal/metal_library.gni @@ -11,7 +11,7 @@ template("metal_library") { action("$target_name") { inputs = invoker.sources - metal_library_path = "$target_out_dir/shaders/$metal_library_name.metallib" + metal_library_path = "$root_out_dir/shaders/$metal_library_name.metallib" outputs = [ metal_library_path ] From cec022742589d55fae49cd65dded905fad2774b9 Mon Sep 17 00:00:00 2001 From: Chinmay Garde Date: Tue, 4 May 2021 11:51:52 -0700 Subject: [PATCH 011/433] Add GLFW reference --- impeller/host/BUILD.gn | 1 + impeller/host/main.mm | 17 ++++++++++------- 2 files changed, 11 insertions(+), 7 deletions(-) diff --git a/impeller/host/BUILD.gn b/impeller/host/BUILD.gn index aead6039aa9f3..483d1f391dce8 100644 --- a/impeller/host/BUILD.gn +++ b/impeller/host/BUILD.gn @@ -38,5 +38,6 @@ executable("host") { ":impeller_host_shaders", "//flutter/fml", "//flutter/impeller/impeller", + "//third_party/glfw", ] } diff --git a/impeller/host/main.mm b/impeller/host/main.mm index 77cd7b0eaa3fd..12aaa3c93c53b 100644 --- a/impeller/host/main.mm +++ b/impeller/host/main.mm @@ -6,7 +6,8 @@ #include "impeller_host_view_controller.h" -@interface ImpellerAppDelegate : NSObject { +@interface ImpellerAppDelegate + : NSObject { NSWindow* window_; ImpellerHostViewController* view_controller_; } @@ -17,11 +18,11 @@ @implementation ImpellerAppDelegate : NSObject - (id)init { if (self = [super init]) { view_controller_ = [[ImpellerHostViewController alloc] init]; - window_ = - [[NSWindow alloc] initWithContentRect:NSMakeRect(0, 0, 800, 600) - styleMask:NSWindowStyleMaskTitled | NSWindowStyleMaskClosable - backing:NSBackingStoreBuffered - defer:NO]; + window_ = [[NSWindow alloc] + initWithContentRect:NSMakeRect(0, 0, 800, 600) + styleMask:NSWindowStyleMaskTitled | NSWindowStyleMaskClosable + backing:NSBackingStoreBuffered + defer:NO]; [window_ setContentViewController:view_controller_]; } return self; @@ -48,7 +49,9 @@ int main(int argc, const char* argv[]) { [item.submenu addItem:[[NSMenuItem alloc] initWithTitle:[@"Quit " - stringByAppendingString:[NSProcessInfo processInfo].processName] + stringByAppendingString:[NSProcessInfo + processInfo] + .processName] action:@selector(terminate:) keyEquivalent:@"q"]]; ImpellerAppDelegate* appDelegate = [[ImpellerAppDelegate alloc] init]; From e08a56ad067be6f1f3283bd511fc209c2ca26a97 Mon Sep 17 00:00:00 2001 From: Chinmay Garde Date: Tue, 4 May 2021 15:52:46 -0700 Subject: [PATCH 012/433] Fix Window sizing. --- impeller/host/BUILD.gn | 4 +++- impeller/host/impeller_host_view_controller.mm | 4 ++-- impeller/host/impeller_renderer.mm | 7 +++---- impeller/host/main.mm | 6 ++++-- 4 files changed, 12 insertions(+), 9 deletions(-) diff --git a/impeller/host/BUILD.gn b/impeller/host/BUILD.gn index 483d1f391dce8..4161a8f818f4b 100644 --- a/impeller/host/BUILD.gn +++ b/impeller/host/BUILD.gn @@ -14,6 +14,8 @@ executable("host") { cflags_objc = flutter_cflags_objc_arc cflags_objcc = flutter_cflags_objcc_arc + cflags_objcc += [ "-Wno-auto-var-id" ] + output_name = "impeller" sources = [ @@ -31,6 +33,7 @@ executable("host") { "AppKit.framework", "Metal.framework", "MetalKit.framework", + "QuartzCore.framework", "ModelIO.framework", ] @@ -38,6 +41,5 @@ executable("host") { ":impeller_host_shaders", "//flutter/fml", "//flutter/impeller/impeller", - "//third_party/glfw", ] } diff --git a/impeller/host/impeller_host_view_controller.mm b/impeller/host/impeller_host_view_controller.mm index 06a7b3b959930..188030b29e587 100644 --- a/impeller/host/impeller_host_view_controller.mm +++ b/impeller/host/impeller_host_view_controller.mm @@ -12,9 +12,9 @@ @implementation ImpellerHostViewController { } - (void)loadView { - view_ = [[MTKView alloc] init]; + view_ = [[MTKView alloc] initWithFrame:NSMakeRect(0, 0, 800, 600)]; view_.autoresizingMask = NSViewWidthSizable | NSViewHeightSizable; - [self setView:view_]; + self.view = view_; } - (void)viewDidLoad { diff --git a/impeller/host/impeller_renderer.mm b/impeller/host/impeller_renderer.mm index c113879ddc692..0111eaa8f04a9 100644 --- a/impeller/host/impeller_renderer.mm +++ b/impeller/host/impeller_renderer.mm @@ -218,7 +218,6 @@ - (void)_updateGameState { - (void)drawInMTKView:(nonnull MTKView*)view { /// Per frame updates here - dispatch_semaphore_wait(_inFlightSemaphore, DISPATCH_TIME_FOREVER); id commandBuffer = [_commandQueue commandBuffer]; @@ -234,9 +233,8 @@ - (void)drawInMTKView:(nonnull MTKView*)view { [self _updateGameState]; /// Delay getting the currentRenderPassDescriptor until we absolutely need it - /// to avoid - /// holding onto the drawable and blocking the display pipeline any longer - /// than necessary + /// to avoid holding onto the drawable and blocking the display pipeline any + /// longer than necessary MTLRenderPassDescriptor* renderPassDescriptor = view.currentRenderPassDescriptor; @@ -293,6 +291,7 @@ - (void)drawInMTKView:(nonnull MTKView*)view { } - (void)mtkView:(nonnull MTKView*)view drawableSizeWillChange:(CGSize)size { + NSLog(@"Drawable sized did change: %@", NSStringFromSize(size)); float aspect = size.width / (float)size.height; _projectionMatrix = matrix_perspective_right_hand(65.0f * (M_PI / 180.0f), aspect, 0.1f, 100.0f); diff --git a/impeller/host/main.mm b/impeller/host/main.mm index 12aaa3c93c53b..c16c53b72ad8a 100644 --- a/impeller/host/main.mm +++ b/impeller/host/main.mm @@ -19,16 +19,18 @@ - (id)init { if (self = [super init]) { view_controller_ = [[ImpellerHostViewController alloc] init]; window_ = [[NSWindow alloc] - initWithContentRect:NSMakeRect(0, 0, 800, 600) + initWithContentRect:NSMakeRect(200, 200, 800, 600) styleMask:NSWindowStyleMaskTitled | NSWindowStyleMaskClosable backing:NSBackingStoreBuffered defer:NO]; + window_.styleMask |= NSWindowStyleMaskResizable; [window_ setContentViewController:view_controller_]; } return self; } - (void)applicationWillFinishLaunching:(NSNotification*)notification { + [window_ makeMainWindow]; [window_ setTitle:@"Impeller Host"]; [window_ makeKeyAndOrderFront:self]; } @@ -43,7 +45,7 @@ int main(int argc, const char* argv[]) { NSApplication* app = [NSApplication sharedApplication]; [app setActivationPolicy:NSApplicationActivationPolicyRegular]; NSMenuItem* item = [[NSMenuItem alloc] init]; - NSApp.mainMenu = [[NSMenu alloc] init]; + app.mainMenu = [[NSMenu alloc] init]; item.submenu = [[NSMenu alloc] init]; [app.mainMenu addItem:item]; [item.submenu From 12bd13afe3d0c1a48988e1891d7d3ec95c240cde Mon Sep 17 00:00:00 2001 From: Chinmay Garde Date: Tue, 4 May 2021 16:17:16 -0700 Subject: [PATCH 013/433] Wire up asset resolution. --- impeller/host/BUILD.gn | 8 ++++++++ impeller/host/assets/ColorMap.png | Bin 0 -> 37539 bytes impeller/host/assets_location.cc | 31 +++++++++++++++++++++++++++++ impeller/host/assets_location.h | 11 ++++++++++ impeller/host/impeller_renderer.mm | 9 ++++++--- 5 files changed, 56 insertions(+), 3 deletions(-) create mode 100644 impeller/host/assets/ColorMap.png create mode 100644 impeller/host/assets_location.cc create mode 100644 impeller/host/assets_location.h diff --git a/impeller/host/BUILD.gn b/impeller/host/BUILD.gn index 4161a8f818f4b..3864069888d9e 100644 --- a/impeller/host/BUILD.gn +++ b/impeller/host/BUILD.gn @@ -4,12 +4,17 @@ import("//flutter/common/config.gni") import("//flutter/impeller/tools/metal/metal_library.gni") +import("//flutter/testing/testing.gni") metal_library("impeller_host_shaders") { name = "impeller_host" sources = [ "shaders.metal" ] } +test_fixtures("impeller_host_fixtures") { + fixtures = [ "assets/ColorMap.png" ] +} + executable("host") { cflags_objc = flutter_cflags_objc_arc cflags_objcc = flutter_cflags_objcc_arc @@ -20,6 +25,8 @@ executable("host") { sources = [ "ShaderTypes.h", + "assets_location.cc", + "assets_location.h", "impeller_host_view_controller.h", "impeller_host_view_controller.mm", "impeller_renderer.h", @@ -38,6 +45,7 @@ executable("host") { ] deps = [ + ":impeller_host_fixtures", ":impeller_host_shaders", "//flutter/fml", "//flutter/impeller/impeller", diff --git a/impeller/host/assets/ColorMap.png b/impeller/host/assets/ColorMap.png new file mode 100644 index 0000000000000000000000000000000000000000..ddf9519d4619fb02becb1157e8b8704d10a01547 GIT binary patch literal 37539 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 r$)xH}R>*L<@bp14E`X~(RdGy#_RQ<+j#?*o1V0 + +namespace impeller { + +std::string GetAssetLocation(const char* asset_path); + +} // namespace impeller diff --git a/impeller/host/impeller_renderer.mm b/impeller/host/impeller_renderer.mm index 0111eaa8f04a9..f54f7ae82d2e2 100644 --- a/impeller/host/impeller_renderer.mm +++ b/impeller/host/impeller_renderer.mm @@ -5,6 +5,7 @@ #import #import +#import "assets_location.h" #include "flutter/fml/logging.h" #import "impeller_renderer.h" #import "shaders_location.h" @@ -178,9 +179,11 @@ - (void)_loadAssets { MTKTextureLoaderOptionTextureStorageMode : @(MTLStorageModePrivate) }; - _colorMap = [textureLoader newTextureWithName:@"ColorMap" - scaleFactor:1.0 - bundle:nil + auto color_map_data = [NSData + dataWithContentsOfFile:@(impeller::GetAssetLocation("ColorMap.png") + .c_str())]; + + _colorMap = [textureLoader newTextureWithData:color_map_data options:textureLoaderOptions error:&error]; From 289520eadf4948a05f2d0a10410b46eac6dbb219 Mon Sep 17 00:00:00 2001 From: Chinmay Garde Date: Wed, 5 May 2021 12:53:13 -0700 Subject: [PATCH 014/433] Fix copyright headers. --- impeller/impeller/entity/Color.cc | 19 ++++--- impeller/impeller/entity/Color.h | 7 ++- impeller/impeller/entity/Entity.cc | 12 ++--- impeller/impeller/entity/Entity.h | 57 +++----------------- impeller/impeller/geometry/Matrix.cc | 7 ++- impeller/impeller/geometry/Matrix.h | 7 ++- impeller/impeller/geometry/Path.cc | 7 ++- impeller/impeller/geometry/Path.h | 7 ++- impeller/impeller/geometry/PathBuilder.cc | 7 ++- impeller/impeller/geometry/PathBuilder.h | 7 ++- impeller/impeller/geometry/PathComponent.cc | 7 ++- impeller/impeller/geometry/PathComponent.h | 7 ++- impeller/impeller/geometry/Point.cc | 17 ++++-- impeller/impeller/geometry/Point.h | 21 ++++---- impeller/impeller/geometry/Quaternion.cc | 7 ++- impeller/impeller/geometry/Quaternion.h | 7 ++- impeller/impeller/geometry/Rect.cc | 7 ++- impeller/impeller/geometry/Rect.h | 7 ++- impeller/impeller/geometry/Shear.cc | 7 ++- impeller/impeller/geometry/Shear.h | 7 ++- impeller/impeller/geometry/Size.cc | 7 ++- impeller/impeller/geometry/Size.h | 7 ++- impeller/impeller/geometry/Vector.cc | 7 ++- impeller/impeller/geometry/Vector.h | 7 ++- impeller/impeller/image/Image.cc | 7 ++- impeller/impeller/image/Image.h | 7 ++- impeller/impeller/image/ImageResult.cc | 7 ++- impeller/impeller/image/ImageResult.h | 7 ++- impeller/third_party/stb/STBImplementation.c | 7 ++- 29 files changed, 116 insertions(+), 178 deletions(-) diff --git a/impeller/impeller/entity/Color.cc b/impeller/impeller/entity/Color.cc index 502e668824dac..311d1ab5174ee 100644 --- a/impeller/impeller/entity/Color.cc +++ b/impeller/impeller/entity/Color.cc @@ -1,7 +1,6 @@ -/* - * This source file is part of the Radar project. - * Licensed under the MIT License. See LICENSE file for details. - */ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. #include "Color.h" #include @@ -28,8 +27,9 @@ ColorHSB ColorHSB::FromRGB(Color rgb) { v = fmax(R, G); v = fmax(v, B); - if (v == x) + if (v == x) { return ColorHSB(0.0, 0.0, v, rgb.alpha); + } f = (R == x) ? G - B : ((G == x) ? B - R : R - G); i = (R == x) ? 3 : ((G == x) ? 5 : 1); @@ -48,18 +48,21 @@ Color ColorHSB::toRGBA() const { int64_t i = 0; - if (h == 0) + if (h == 0) { h = 0.01; + } - if (h == 0.0) + if (h == 0.0) { return Color(v, v, v, alpha); + } i = static_cast(floor(h)); f = h - i; - if (!(i & 1)) + if (!(i & 1)) { f = 1 - f; + } m = v * (1 - s); n = v * (1 - s * f); diff --git a/impeller/impeller/entity/Color.h b/impeller/impeller/entity/Color.h index a625dd281ddce..e00d8e428dd3d 100644 --- a/impeller/impeller/entity/Color.h +++ b/impeller/impeller/entity/Color.h @@ -1,7 +1,6 @@ -/* - * This source file is part of the Radar project. - * Licensed under the MIT License. See LICENSE file for details. - */ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. #pragma once diff --git a/impeller/impeller/entity/Entity.cc b/impeller/impeller/entity/Entity.cc index 772ceef394eea..ba2670c5c1a03 100644 --- a/impeller/impeller/entity/Entity.cc +++ b/impeller/impeller/entity/Entity.cc @@ -1,17 +1,13 @@ -/* - * This source file is part of the Radar project. - * Licensed under the MIT License. See LICENSE file for details. - */ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. #include "Entity.h" namespace rl { namespace entity { -Entity::Entity() - : _anchorPoint(geom::Point(0.5, 0.5)), _opacity(1.0), _strokeSize(1.0) {} - -Entity::Entity(Entity&&) = default; +Entity::Entity() = default; Entity::~Entity() = default; diff --git a/impeller/impeller/entity/Entity.h b/impeller/impeller/entity/Entity.h index c1e9e4d557b24..b827798985b67 100644 --- a/impeller/impeller/entity/Entity.h +++ b/impeller/impeller/entity/Entity.h @@ -1,7 +1,6 @@ -/* - * This source file is part of the Radar project. - * Licensed under the MIT License. See LICENSE file for details. - */ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. #pragma once @@ -16,50 +15,8 @@ namespace entity { class Entity { public: - using PropertyMaskType = uint16_t; - - enum class Property : PropertyMaskType { - None, - - AddedTo, - RemovedFrom, - Bounds, - Position, - AnchorPoint, - Transformation, - BackgroundColor, - Contents, - Path, - Opacity, - StrokeSize, - StrokeColor, - MakeRoot, - - Sentinel, - }; - -#define RL_MASK(x) x##Mask = (1 << static_cast(Property::x)) - enum PropertyMask { - RL_MASK(AddedTo), - RL_MASK(RemovedFrom), - RL_MASK(Bounds), - RL_MASK(Position), - RL_MASK(AnchorPoint), - RL_MASK(Transformation), - RL_MASK(BackgroundColor), - RL_MASK(Contents), - RL_MASK(Path), - RL_MASK(Opacity), - RL_MASK(StrokeSize), - RL_MASK(StrokeColor), - RL_MASK(MakeRoot), - }; -#undef RL_MASK - Entity(); - Entity(Entity&& entity); - ~Entity(); /** @@ -187,14 +144,14 @@ class Entity { private: geom::Rect _bounds; geom::Point _position; - geom::Point _anchorPoint; + geom::Point _anchorPoint = {0.5, 0.5}; geom::Matrix _transformation; Color _backgroundColor; geom::Path _path; - double _opacity; - Color _strokeColor; - double _strokeSize; + double _opacity = 1.0; + Color _strokeColor = Color::Black(); + double _strokeSize = 1.0; FML_DISALLOW_COPY_AND_ASSIGN(Entity); }; diff --git a/impeller/impeller/geometry/Matrix.cc b/impeller/impeller/geometry/Matrix.cc index 8909df29bd41f..9fe2161373d95 100644 --- a/impeller/impeller/geometry/Matrix.cc +++ b/impeller/impeller/geometry/Matrix.cc @@ -1,7 +1,6 @@ -/* - * This source file is part of the Radar project. - * Licensed under the MIT License. See LICENSE file for details. - */ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. #include "Matrix.h" #include diff --git a/impeller/impeller/geometry/Matrix.h b/impeller/impeller/geometry/Matrix.h index add1b681b507c..58d224a6847c4 100644 --- a/impeller/impeller/geometry/Matrix.h +++ b/impeller/impeller/geometry/Matrix.h @@ -1,7 +1,6 @@ -/* - * This source file is part of the Radar project. - * Licensed under the MIT License. See LICENSE file for details. - */ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. #pragma once diff --git a/impeller/impeller/geometry/Path.cc b/impeller/impeller/geometry/Path.cc index 8651108f9c276..b8c9ec6e5e6f1 100644 --- a/impeller/impeller/geometry/Path.cc +++ b/impeller/impeller/geometry/Path.cc @@ -1,7 +1,6 @@ -/* - * This source file is part of the Radar project. - * Licensed under the MIT License. See LICENSE file for details. - */ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. #include "Path.h" diff --git a/impeller/impeller/geometry/Path.h b/impeller/impeller/geometry/Path.h index d36b730074a74..8d2abcacae008 100644 --- a/impeller/impeller/geometry/Path.h +++ b/impeller/impeller/geometry/Path.h @@ -1,7 +1,6 @@ -/* - * This source file is part of the Radar project. - * Licensed under the MIT License. See LICENSE file for details. - */ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. #pragma once diff --git a/impeller/impeller/geometry/PathBuilder.cc b/impeller/impeller/geometry/PathBuilder.cc index 35a8b718ed200..5bfe23dbeb9e9 100644 --- a/impeller/impeller/geometry/PathBuilder.cc +++ b/impeller/impeller/geometry/PathBuilder.cc @@ -1,7 +1,6 @@ -/* - * This source file is part of the Radar project. - * Licensed under the MIT License. See LICENSE file for details. - */ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. #include "PathBuilder.h" diff --git a/impeller/impeller/geometry/PathBuilder.h b/impeller/impeller/geometry/PathBuilder.h index ba023508b9c9e..161aabc5ff7d7 100644 --- a/impeller/impeller/geometry/PathBuilder.h +++ b/impeller/impeller/geometry/PathBuilder.h @@ -1,7 +1,6 @@ -/* - * This source file is part of the Radar project. - * Licensed under the MIT License. See LICENSE file for details. - */ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. #pragma once diff --git a/impeller/impeller/geometry/PathComponent.cc b/impeller/impeller/geometry/PathComponent.cc index f1ddeac7643d3..2540e3082b27b 100644 --- a/impeller/impeller/geometry/PathComponent.cc +++ b/impeller/impeller/geometry/PathComponent.cc @@ -1,7 +1,6 @@ -/* - * This source file is part of the Radar project. - * Licensed under the MIT License. See LICENSE file for details. - */ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. #include "PathComponent.h" #include diff --git a/impeller/impeller/geometry/PathComponent.h b/impeller/impeller/geometry/PathComponent.h index 0acf40266d630..b1746b22ab025 100644 --- a/impeller/impeller/geometry/PathComponent.h +++ b/impeller/impeller/geometry/PathComponent.h @@ -1,7 +1,6 @@ -/* - * This source file is part of the Radar project. - * Licensed under the MIT License. See LICENSE file for details. - */ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. #pragma once diff --git a/impeller/impeller/geometry/Point.cc b/impeller/impeller/geometry/Point.cc index b7cfd3805f26b..9b8d6976f2a28 100644 --- a/impeller/impeller/geometry/Point.cc +++ b/impeller/impeller/geometry/Point.cc @@ -1,7 +1,6 @@ -/* - * This source file is part of the Radar project. - * Licensed under the MIT License. See LICENSE file for details. - */ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. #include "Point.h" #include @@ -22,5 +21,15 @@ void Point::fromString(const std::string& str) { stream >> y; } +double Point::distanceSquared(const Point& p) const { + double dx = p.x - x; + double dy = p.y - y; + return dx * dx + dy * dy; +} + +double Point::distance(const Point& p) const { + return sqrt(distanceSquared(p)); +} + } // namespace geom } // namespace rl diff --git a/impeller/impeller/geometry/Point.h b/impeller/impeller/geometry/Point.h index 77f3cc37e4195..1e246b1dd847d 100644 --- a/impeller/impeller/geometry/Point.h +++ b/impeller/impeller/geometry/Point.h @@ -1,7 +1,6 @@ -/* - * This source file is part of the Radar project. - * Licensed under the MIT License. See LICENSE file for details. - */ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. #pragma once @@ -16,13 +15,15 @@ struct Point { double x; double y; - Point() : x(0.0), y(0.0) {} - Point(double x, double y) : x(x), y(y) {} + constexpr Point() : x(0.0), y(0.0) {} + + constexpr Point(double x, double y) : x(x), y(y) {} /* * Operator overloads */ bool operator==(const Point& p) const { return p.x == x && p.y == y; } + bool operator!=(const Point& p) const { return p.x != x || p.y != y; } Point operator-() const { return {-x, -y}; } @@ -41,13 +42,9 @@ struct Point { Point operator/(const Point& p) const { return {x / p.x, y / p.y}; } Point operator/(const Size& s) const { return {x / s.width, y / s.height}; } - double distanceSquared(const Point& p) const { - double dx = p.x - x; - double dy = p.y - y; - return dx * dx + dy * dy; - } + double distanceSquared(const Point& p) const; - double distance(const Point& p) const { return sqrt(distanceSquared(p)); } + double distance(const Point& p) const; std::string toString() const; diff --git a/impeller/impeller/geometry/Quaternion.cc b/impeller/impeller/geometry/Quaternion.cc index bae8cdd541184..58ef20c05140c 100644 --- a/impeller/impeller/geometry/Quaternion.cc +++ b/impeller/impeller/geometry/Quaternion.cc @@ -1,7 +1,6 @@ -/* - * This source file is part of the Radar project. - * Licensed under the MIT License. See LICENSE file for details. - */ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. #include "Quaternion.h" #include diff --git a/impeller/impeller/geometry/Quaternion.h b/impeller/impeller/geometry/Quaternion.h index 2b97e4da8153b..ec5e6eb608471 100644 --- a/impeller/impeller/geometry/Quaternion.h +++ b/impeller/impeller/geometry/Quaternion.h @@ -1,7 +1,6 @@ -/* - * This source file is part of the Radar project. - * Licensed under the MIT License. See LICENSE file for details. - */ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. #pragma once diff --git a/impeller/impeller/geometry/Rect.cc b/impeller/impeller/geometry/Rect.cc index 878270f17c79f..a718e746fcce1 100644 --- a/impeller/impeller/geometry/Rect.cc +++ b/impeller/impeller/geometry/Rect.cc @@ -1,7 +1,6 @@ -/* - * This source file is part of the Radar project. - * Licensed under the MIT License. See LICENSE file for details. - */ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. #include "Rect.h" #include diff --git a/impeller/impeller/geometry/Rect.h b/impeller/impeller/geometry/Rect.h index 21abc492a03eb..1cc5e39f1cfed 100644 --- a/impeller/impeller/geometry/Rect.h +++ b/impeller/impeller/geometry/Rect.h @@ -1,7 +1,6 @@ -/* - * This source file is part of the Radar project. - * Licensed under the MIT License. See LICENSE file for details. - */ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. #pragma once diff --git a/impeller/impeller/geometry/Shear.cc b/impeller/impeller/geometry/Shear.cc index dd880a9761832..fc0cae787a02e 100644 --- a/impeller/impeller/geometry/Shear.cc +++ b/impeller/impeller/geometry/Shear.cc @@ -1,7 +1,6 @@ -/* - * This source file is part of the Radar project. - * Licensed under the MIT License. See LICENSE file for details. - */ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. #include "Shear.h" #include diff --git a/impeller/impeller/geometry/Shear.h b/impeller/impeller/geometry/Shear.h index f229d08699780..dd5d402bc9eea 100644 --- a/impeller/impeller/geometry/Shear.h +++ b/impeller/impeller/geometry/Shear.h @@ -1,7 +1,6 @@ -/* - * This source file is part of the Radar project. - * Licensed under the MIT License. See LICENSE file for details. - */ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. #pragma once diff --git a/impeller/impeller/geometry/Size.cc b/impeller/impeller/geometry/Size.cc index 6bc5febf7415b..d6f7f9da19d25 100644 --- a/impeller/impeller/geometry/Size.cc +++ b/impeller/impeller/geometry/Size.cc @@ -1,7 +1,6 @@ -/* - * This source file is part of the Radar project. - * Licensed under the MIT License. See LICENSE file for details. - */ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. #include "Size.h" #include diff --git a/impeller/impeller/geometry/Size.h b/impeller/impeller/geometry/Size.h index b2f2ab82299e5..bc064fb9b2fad 100644 --- a/impeller/impeller/geometry/Size.h +++ b/impeller/impeller/geometry/Size.h @@ -1,7 +1,6 @@ -/* - * This source file is part of the Radar project. - * Licensed under the MIT License. See LICENSE file for details. - */ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. #pragma once diff --git a/impeller/impeller/geometry/Vector.cc b/impeller/impeller/geometry/Vector.cc index ad20c487e65bc..2d33a39bdf587 100644 --- a/impeller/impeller/geometry/Vector.cc +++ b/impeller/impeller/geometry/Vector.cc @@ -1,7 +1,6 @@ -/* - * This source file is part of the Radar project. - * Licensed under the MIT License. See LICENSE file for details. - */ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. #include "Vector.h" #include diff --git a/impeller/impeller/geometry/Vector.h b/impeller/impeller/geometry/Vector.h index 0457d1911a41c..69c4324622971 100644 --- a/impeller/impeller/geometry/Vector.h +++ b/impeller/impeller/geometry/Vector.h @@ -1,7 +1,6 @@ -/* - * This source file is part of the Radar project. - * Licensed under the MIT License. See LICENSE file for details. - */ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. #pragma once diff --git a/impeller/impeller/image/Image.cc b/impeller/impeller/image/Image.cc index bf0bf186cc008..79b0c19f69161 100644 --- a/impeller/impeller/image/Image.cc +++ b/impeller/impeller/image/Image.cc @@ -1,7 +1,6 @@ -/* - * This source file is part of the Radar project. - * Licensed under the MIT License. See LICENSE file for details. - */ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. #include "Image.h" #include diff --git a/impeller/impeller/image/Image.h b/impeller/impeller/image/Image.h index 40b5471cef84e..cf4641b5b6edd 100644 --- a/impeller/impeller/image/Image.h +++ b/impeller/impeller/image/Image.h @@ -1,7 +1,6 @@ -/* - * This source file is part of the Radar project. - * Licensed under the MIT License. See LICENSE file for details. - */ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. #pragma once diff --git a/impeller/impeller/image/ImageResult.cc b/impeller/impeller/image/ImageResult.cc index 09c4ea10eb728..66783483ed2e8 100644 --- a/impeller/impeller/image/ImageResult.cc +++ b/impeller/impeller/image/ImageResult.cc @@ -1,7 +1,6 @@ -/* - * This source file is part of the Radar project. - * Licensed under the MIT License. See LICENSE file for details. - */ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. #include "ImageResult.h" diff --git a/impeller/impeller/image/ImageResult.h b/impeller/impeller/image/ImageResult.h index 8943dc02d7c9e..28dfc18f32e64 100644 --- a/impeller/impeller/image/ImageResult.h +++ b/impeller/impeller/image/ImageResult.h @@ -1,7 +1,6 @@ -/* - * This source file is part of the Radar project. - * Licensed under the MIT License. See LICENSE file for details. - */ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. #pragma once diff --git a/impeller/third_party/stb/STBImplementation.c b/impeller/third_party/stb/STBImplementation.c index b8063c168d537..1751a6251425d 100644 --- a/impeller/third_party/stb/STBImplementation.c +++ b/impeller/third_party/stb/STBImplementation.c @@ -1,7 +1,6 @@ -/* - * This source file is part of the Radar project. - * Licensed under the MIT License. See LICENSE file for details. - */ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. #define STB_IMAGE_IMPLEMENTATION From 7790536c6d1c1e8d10cfd4b01d725e290082d322 Mon Sep 17 00:00:00 2001 From: Chinmay Garde Date: Wed, 5 May 2021 15:03:56 -0700 Subject: [PATCH 015/433] Formatting. --- impeller/impeller/entity/Entity.cc | 106 ++++++++++++++-------------- impeller/impeller/entity/Entity.h | 62 ++++++++-------- impeller/impeller/geometry/Matrix.h | 2 +- impeller/impeller/geometry/Point.h | 6 ++ 4 files changed, 91 insertions(+), 85 deletions(-) diff --git a/impeller/impeller/entity/Entity.cc b/impeller/impeller/entity/Entity.cc index ba2670c5c1a03..db360cf41dfb5 100644 --- a/impeller/impeller/entity/Entity.cc +++ b/impeller/impeller/entity/Entity.cc @@ -11,61 +11,61 @@ Entity::Entity() = default; Entity::~Entity() = default; -geom::Rect Entity::frame() const { - geom::Point origin(_position.x - (_bounds.size.width * _anchorPoint.x), - _position.y - (_bounds.size.height * _anchorPoint.y)); +geom::Rect Entity::GetFrame() const { + geom::Point origin(position_.x - (bounds_.size.width * anchor_point_.x), + position_.y - (bounds_.size.height * anchor_point_.y)); - return geom::Rect(origin, _bounds.size); + return geom::Rect(origin, bounds_.size); } -void Entity::setFrame(const geom::Rect& frame) { - setBounds(geom::Rect(_bounds.origin, frame.size)); - setPosition( - geom::Point(frame.origin.x + (_anchorPoint.x * frame.size.width), - frame.origin.y + (_anchorPoint.y * frame.size.height))); +void Entity::SetFrame(const geom::Rect& frame) { + SetBounds(geom::Rect(bounds_.origin, frame.size)); + SetPosition( + geom::Point(frame.origin.x + (anchor_point_.x * frame.size.width), + frame.origin.y + (anchor_point_.y * frame.size.height))); } -const geom::Rect& Entity::bounds() const { - return _bounds; +const geom::Rect& Entity::GetBounds() const { + return bounds_; } -void Entity::setBounds(const geom::Rect& bounds) { - _bounds = bounds; +void Entity::SetBounds(const geom::Rect& bounds) { + bounds_ = bounds; } -const geom::Point& Entity::position() const { - return _position; +const geom::Point& Entity::GetPosition() const { + return position_; } -void Entity::setPosition(const geom::Point& position) { - _position = position; +void Entity::SetPosition(const geom::Point& position) { + position_ = position; } -const geom::Point& Entity::anchorPoint() const { - return _anchorPoint; +const geom::Point& Entity::GetAnchorPoint() const { + return anchor_point_; } -void Entity::setAnchorPoint(const geom::Point& anchorPoint) { - _anchorPoint = anchorPoint; +void Entity::SetAnchorPoint(const geom::Point& anchorPoint) { + anchor_point_ = anchorPoint; } -const geom::Matrix& Entity::transformation() const { - return _transformation; +const geom::Matrix& Entity::GetTransformation() const { + return transformation_; } -void Entity::setTransformation(const geom::Matrix& transformation) { - _transformation = transformation; +void Entity::SetTransformation(const geom::Matrix& transformation) { + transformation_ = transformation; } -geom::Matrix Entity::modelMatrix() const { +geom::Matrix Entity::GetModelMatrix() const { /* * The translation accounts for the offset in the origin of the bounds * of the entity and its position about its anchor point. */ auto translation = geom::Matrix::Translation( - {-_bounds.origin.x + _position.x - (_bounds.size.width * _anchorPoint.x), - -_bounds.origin.y + _position.y - - (_bounds.size.height * _anchorPoint.y)}); + {-bounds_.origin.x + position_.x - (bounds_.size.width * anchor_point_.x), + -bounds_.origin.y + position_.y - + (bounds_.size.height * anchor_point_.y)}); /* * The transformation of an entity is applied about is anchor point. However, @@ -73,56 +73,56 @@ geom::Matrix Entity::modelMatrix() const { * matrix adjustment and also the two matrix multiplications. */ - if (_transformation.isIdentity()) { + if (transformation_.isIdentity()) { return translation; } auto anchorAdjustment = - geom::Matrix::Translation({-_anchorPoint.x * _bounds.size.width, - -_anchorPoint.y * _bounds.size.height}); + geom::Matrix::Translation({-anchor_point_.x * bounds_.size.width, + -anchor_point_.y * bounds_.size.height}); - return translation * anchorAdjustment.invert() * _transformation * + return translation * anchorAdjustment.invert() * transformation_ * anchorAdjustment; } -const Color& Entity::backgroundColor() const { - return _backgroundColor; +const Color& Entity::GetBackgroundColor() const { + return background_color_; } -void Entity::setBackgroundColor(const Color& backgroundColor) { - _backgroundColor = backgroundColor; +void Entity::SetBackgroundColor(const Color& backgroundColor) { + background_color_ = backgroundColor; } -const double& Entity::opacity() const { - return _opacity; +const double& Entity::GetOpacity() const { + return opacity_; } -void Entity::setOpacity(double opacity) { - _opacity = opacity; +void Entity::SetOpacity(double opacity) { + opacity_ = opacity; } -const Color& Entity::strokeColor() const { - return _strokeColor; +const Color& Entity::GetStrokeColor() const { + return stroke_color_; } -void Entity::setStrokeColor(const Color& strokeColor) { - _strokeColor = strokeColor; +void Entity::SetStrokeColor(const Color& strokeColor) { + stroke_color_ = strokeColor; } -double Entity::strokeSize() const { - return _strokeSize; +double Entity::GetStrokeSize() const { + return stroke_size_; } -void Entity::setStrokeSize(double strokeSize) { - _strokeSize = strokeSize; +void Entity::SetStrokeSize(double strokeSize) { + stroke_size_ = strokeSize; } -const geom::Path& Entity::path() const { - return _path; +const geom::Path& Entity::GetPath() const { + return path_; } -void Entity::setPath(geom::Path path) { - _path = std::move(path); +void Entity::SetPath(geom::Path path) { + path_ = std::move(path); } } // namespace entity diff --git a/impeller/impeller/entity/Entity.h b/impeller/impeller/entity/Entity.h index b827798985b67..4df2150a24f5c 100644 --- a/impeller/impeller/entity/Entity.h +++ b/impeller/impeller/entity/Entity.h @@ -26,14 +26,14 @@ class Entity { * * @return the frame of the entity */ - geom::Rect frame() const; + geom::Rect GetFrame() const; /** * Set the frame of the entity * * @param frame the new frame */ - void setFrame(const geom::Rect& frame); + void SetFrame(const geom::Rect& frame); /** * The bounds specifies the origin and size of the entity in its own @@ -41,14 +41,14 @@ class Entity { * * @return the bounds of the entity */ - const geom::Rect& bounds() const; + const geom::Rect& GetBounds() const; /** * Set the bounds of the entity * * @param bounds the new bounds */ - void setBounds(const geom::Rect& bounds); + void SetBounds(const geom::Rect& bounds); /** * The position specifies the coordinates of the anchor position of the @@ -56,63 +56,63 @@ class Entity { * * @return the position of the entity */ - const geom::Point& position() const; + const geom::Point& GetPosition() const; /** * Sets the position of the entity * * @param point the new position */ - void setPosition(const geom::Point& point); + void SetPosition(const geom::Point& point); /** * The position of the anchor point within this node in unit space * * @return the anchor point */ - const geom::Point& anchorPoint() const; + const geom::Point& GetAnchorPoint() const; /** * Sets the new anchor point of this node * * @param anchorPoint the new anchor point */ - void setAnchorPoint(const geom::Point& anchorPoint); + void SetAnchorPoint(const geom::Point& anchorPoint); /** * The transformation that is applied to the entity about its anchor point * * @return the transformation applied to the node */ - const geom::Matrix& transformation() const; + const geom::Matrix& GetTransformation() const; /** * Sets the transformation of the entity * * @param transformation the new transformation */ - void setTransformation(const geom::Matrix& transformation); + void SetTransformation(const geom::Matrix& transformation); /** * The model matrix of the entity * * @return the view matrix */ - geom::Matrix modelMatrix() const; + geom::Matrix GetModelMatrix() const; /** * The background color of the entity * * @return the background color */ - const Color& backgroundColor() const; + const Color& GetBackgroundColor() const; /** * Set the new background color of the entity * * @param backgroundColor the new background color */ - void setBackgroundColor(const Color& backgroundColor); + void SetBackgroundColor(const Color& backgroundColor); /** * The opacity of the entity. 0.0 is fully transparent and 1.0 is fully @@ -120,38 +120,38 @@ class Entity { * * @return the opacity of the entity */ - const double& opacity() const; + const double& GetOpacity() const; /** * Set the new opacity of the entity * * @param opacity the new opacity */ - void setOpacity(double opacity); + void SetOpacity(double opacity); - const Color& strokeColor() const; + const Color& GetStrokeColor() const; - void setStrokeColor(const Color& strokeColor); + void SetStrokeColor(const Color& strokeColor); - double strokeSize() const; + double GetStrokeSize() const; - void setStrokeSize(double strokeSize); + void SetStrokeSize(double strokeSize); - const geom::Path& path() const; + const geom::Path& GetPath() const; - void setPath(geom::Path path); + void SetPath(geom::Path path); private: - geom::Rect _bounds; - geom::Point _position; - geom::Point _anchorPoint = {0.5, 0.5}; - geom::Matrix _transformation; - Color _backgroundColor; - - geom::Path _path; - double _opacity = 1.0; - Color _strokeColor = Color::Black(); - double _strokeSize = 1.0; + geom::Rect bounds_; + geom::Point position_; + geom::Point anchor_point_ = {0.5, 0.5}; + geom::Matrix transformation_; + Color background_color_; + + geom::Path path_; + double opacity_ = 1.0; + Color stroke_color_ = Color::Black(); + double stroke_size_ = 1.0; FML_DISALLOW_COPY_AND_ASSIGN(Entity); }; diff --git a/impeller/impeller/geometry/Matrix.h b/impeller/impeller/geometry/Matrix.h index 58d224a6847c4..721ed839d1ef1 100644 --- a/impeller/impeller/geometry/Matrix.h +++ b/impeller/impeller/geometry/Matrix.h @@ -287,7 +287,7 @@ struct Matrix { static_assert(sizeof(struct Matrix) == sizeof(double) * 16, "The matrix must be of consistent size."); -static inline Vector4 operator*(const Vector4& v, const Matrix& m) { +inline Vector4 operator*(const Vector4& v, const Matrix& m) { return Vector4(v.x * m.m[0] + v.y * m.m[4] + v.z * m.m[8] + v.w * m.m[12], v.x * m.m[1] + v.y * m.m[5] + v.z * m.m[9] + v.w * m.m[13], v.x * m.m[2] + v.y * m.m[6] + v.z * m.m[10] + v.w * m.m[14], diff --git a/impeller/impeller/geometry/Point.h b/impeller/impeller/geometry/Point.h index 1e246b1dd847d..988d2fd16ecd8 100644 --- a/impeller/impeller/geometry/Point.h +++ b/impeller/impeller/geometry/Point.h @@ -29,17 +29,23 @@ struct Point { Point operator-() const { return {-x, -y}; } Point operator+(const Point& p) const { return {x + p.x, y + p.y}; } + Point operator+(const Size& s) const { return {x + s.width, y + s.height}; } Point operator-(const Point& p) const { return {x - p.x, y - p.y}; } + Point operator-(const Size& s) const { return {x - s.width, y - s.height}; } Point operator*(double scale) const { return {x * scale, y * scale}; } + Point operator*(const Point& p) const { return {x * p.x, y * p.y}; } + Point operator*(const Size& s) const { return {x * s.width, y * s.height}; } Point operator/(double d) const { return {x / d, y / d}; } + Point operator/(const Point& p) const { return {x / p.x, y / p.y}; } + Point operator/(const Size& s) const { return {x / s.width, y / s.height}; } double distanceSquared(const Point& p) const; From 784087fc2e450a6b90fb8c04a14c036b87855607 Mon Sep 17 00:00:00 2001 From: Chinmay Garde Date: Wed, 5 May 2021 15:38:26 -0700 Subject: [PATCH 016/433] Apply clang-tidy guidelines. --- impeller/impeller/entity/Entity.cc | 10 +- impeller/impeller/geometry/Matrix.cc | 89 +++++------ impeller/impeller/geometry/Matrix.h | 105 ++++++------- impeller/impeller/geometry/Path.cc | 12 +- impeller/impeller/geometry/PathBuilder.cc | 156 ++++++++++---------- impeller/impeller/geometry/PathBuilder.h | 40 ++--- impeller/impeller/geometry/PathComponent.cc | 44 +++--- impeller/impeller/geometry/PathComponent.h | 22 +-- impeller/impeller/geometry/Point.cc | 10 +- impeller/impeller/geometry/Point.h | 14 +- impeller/impeller/geometry/Quaternion.cc | 8 +- impeller/impeller/geometry/Quaternion.h | 22 +-- impeller/impeller/geometry/Rect.cc | 10 +- impeller/impeller/geometry/Rect.h | 16 +- impeller/impeller/geometry/Shear.cc | 2 +- impeller/impeller/geometry/Shear.h | 2 +- impeller/impeller/geometry/Size.cc | 4 +- impeller/impeller/geometry/Size.h | 10 +- impeller/impeller/geometry/Vector.cc | 4 +- impeller/impeller/geometry/Vector.h | 44 +++--- impeller/impeller/image/Image.cc | 14 +- impeller/impeller/image/Image.h | 6 +- impeller/impeller/image/ImageResult.cc | 24 +-- impeller/impeller/image/ImageResult.h | 16 +- 24 files changed, 345 insertions(+), 339 deletions(-) diff --git a/impeller/impeller/entity/Entity.cc b/impeller/impeller/entity/Entity.cc index db360cf41dfb5..bebb91578739a 100644 --- a/impeller/impeller/entity/Entity.cc +++ b/impeller/impeller/entity/Entity.cc @@ -62,7 +62,7 @@ geom::Matrix Entity::GetModelMatrix() const { * The translation accounts for the offset in the origin of the bounds * of the entity and its position about its anchor point. */ - auto translation = geom::Matrix::Translation( + auto translation = geom::Matrix::MakeTranslation( {-bounds_.origin.x + position_.x - (bounds_.size.width * anchor_point_.x), -bounds_.origin.y + position_.y - (bounds_.size.height * anchor_point_.y)}); @@ -73,15 +73,15 @@ geom::Matrix Entity::GetModelMatrix() const { * matrix adjustment and also the two matrix multiplications. */ - if (transformation_.isIdentity()) { + if (transformation_.IsIdentity()) { return translation; } auto anchorAdjustment = - geom::Matrix::Translation({-anchor_point_.x * bounds_.size.width, - -anchor_point_.y * bounds_.size.height}); + geom::Matrix::MakeTranslation({-anchor_point_.x * bounds_.size.width, + -anchor_point_.y * bounds_.size.height}); - return translation * anchorAdjustment.invert() * transformation_ * + return translation * anchorAdjustment.Invert() * transformation_ * anchorAdjustment; } diff --git a/impeller/impeller/geometry/Matrix.cc b/impeller/impeller/geometry/Matrix.cc index 9fe2161373d95..95294b77f67ad 100644 --- a/impeller/impeller/geometry/Matrix.cc +++ b/impeller/impeller/geometry/Matrix.cc @@ -85,12 +85,12 @@ Matrix::Matrix(const Decomposition& d) : Matrix() { } } -Matrix Matrix::Orthographic(double left, - double right, - double bottom, - double top, - double nearZ, - double farZ) { +Matrix Matrix::MakeOrthographic(double left, + double right, + double bottom, + double top, + double nearZ, + double farZ) { double ral = right + left; double rsl = right - left; double tab = top + bottom; @@ -106,14 +106,15 @@ Matrix Matrix::Orthographic(double left, // clang-format on } -Matrix Matrix::Orthographic(const Size& size) { - return Matrix::Orthographic(0, size.width, size.height, 0, -INT_MAX, INT_MAX); +Matrix Matrix::MakeOrthographic(const Size& size) { + return Matrix::MakeOrthographic(0, size.width, size.height, 0, -INT_MAX, + INT_MAX); } -Matrix Matrix::Perspective(double fov, - double aspect, - double nearZ, - double farZ) { +Matrix Matrix::MakePerspective(double fov, + double aspect, + double nearZ, + double farZ) { double cotan = 1.0 / tan(fov / 2.0); return Matrix(cotan / aspect, 0.0, 0.0, 0.0, // @@ -123,16 +124,16 @@ Matrix Matrix::Perspective(double fov, ); } -Matrix Matrix::LookAt(const Vector3& eye, - const Vector3& center, - const Vector3& up) { - auto n = (eye - center).normalize(); - auto u = (up.cross(n)).normalize(); - auto v = n.cross(u); +Matrix Matrix::MakeLookAt(const Vector3& eye, + const Vector3& center, + const Vector3& up) { + auto n = (eye - center).Normalize(); + auto u = (up.Cross(n)).Normalize(); + auto v = n.Cross(u); return {u.x, v.x, n.x, 0.0, // u.y, v.y, n.y, 0.0, // u.z, v.z, n.z, 0.0, // - (-u).dot(eye), (-v).dot(eye), (-n).dot(eye), 1.0}; + (-u).Dot(eye), (-v).Dot(eye), (-n).Dot(eye), 1.0}; } Matrix Matrix::operator+(const Matrix& o) const { @@ -144,7 +145,7 @@ Matrix Matrix::operator+(const Matrix& o) const { ); } -std::string Matrix::toString() const { +std::string Matrix::ToString() const { std::stringstream stream; for (int i = 0, limit = 16; i < limit; i++) { stream << m[i]; @@ -155,7 +156,7 @@ std::string Matrix::toString() const { return stream.str(); } -void Matrix::fromString(const std::string& str) { +void Matrix::FromString(const std::string& str) { std::stringstream stream(str); for (int i = 0; i < 16; i++) { stream >> m[i]; @@ -163,7 +164,7 @@ void Matrix::fromString(const std::string& str) { } } -Matrix Matrix::invert() const { +Matrix Matrix::Invert() const { Matrix tmp{ m[5] * m[10] * m[15] - m[5] * m[11] * m[14] - m[9] * m[6] * m[15] + m[9] * m[7] * m[14] + m[13] * m[6] * m[11] - m[13] * m[7] * m[10], @@ -228,7 +229,7 @@ Matrix Matrix::invert() const { tmp.m[12] * det, tmp.m[13] * det, tmp.m[14] * det, tmp.m[15] * det}; } -Matrix Matrix::transpose() const { +Matrix Matrix::Transpose() const { return { m[0], m[4], m[8], m[12], // m[1], m[5], m[9], m[13], // @@ -237,7 +238,7 @@ Matrix Matrix::transpose() const { }; } -double Matrix::determinant() const { +double Matrix::GetDeterminant() const { auto a00 = e[0][0]; auto a01 = e[0][1]; auto a02 = e[0][2]; @@ -275,7 +276,7 @@ double Matrix::determinant() const { * Adapted for Radar from Graphics Gems: * http://www.realtimerendering.com/resources/GraphicsGems/gemsii/unmatrix.c */ -Matrix::DecompositionResult Matrix::decompose() const { +Matrix::DecompositionResult Matrix::Decompose() const { /* * Normalize the matrix. */ @@ -302,7 +303,7 @@ Matrix::DecompositionResult Matrix::decompose() const { perpectiveMatrix.e[3][3] = 1; - if (perpectiveMatrix.determinant() == 0.0) { + if (perpectiveMatrix.GetDeterminant() == 0.0) { return {false, {}}; } @@ -327,7 +328,7 @@ Matrix::DecompositionResult Matrix::decompose() const { * prhs by the inverse. */ - result.perspective = rightHandSide * perpectiveMatrix.invert().transpose(); + result.perspective = rightHandSide * perpectiveMatrix.Invert().Transpose(); /* * Clear the perspective partition. @@ -359,35 +360,35 @@ Matrix::DecompositionResult Matrix::decompose() const { /* * Compute X scale factor and normalize first row. */ - result.scale.x = row[0].length(); - row[0] = row[0].normalize(); + result.scale.x = row[0].Length(); + row[0] = row[0].Normalize(); /* * Compute XY shear factor and make 2nd row orthogonal to 1st. */ - result.shear.xy = row[0].dot(row[1]); + result.shear.xy = row[0].Dot(row[1]); row[1] = Vector3::Combine(row[1], 1.0, row[0], -result.shear.xy); /* * Compute Y scale and normalize 2nd row. */ - result.scale.y = row[1].length(); - row[1] = row[1].normalize(); + result.scale.y = row[1].Length(); + row[1] = row[1].Normalize(); result.shear.xy /= result.scale.y; /* * Compute XZ and YZ shears, orthogonalize 3rd row. */ - result.shear.xz = row[0].dot(row[2]); + result.shear.xz = row[0].Dot(row[2]); row[2] = Vector3::Combine(row[2], 1.0, row[0], -result.shear.xz); - result.shear.yz = row[1].dot(row[2]); + result.shear.yz = row[1].Dot(row[2]); row[2] = Vector3::Combine(row[2], 1.0, row[1], -result.shear.yz); /* * Next, get Z scale and normalize 3rd row. */ - result.scale.z = row[2].length(); - row[2] = row[2].normalize(); + result.scale.z = row[2].Length(); + row[2] = row[2].Normalize(); result.shear.xz /= result.scale.z; result.shear.yz /= result.scale.z; @@ -397,7 +398,7 @@ Matrix::DecompositionResult Matrix::decompose() const { * Check for a coordinate system flip. If the determinant * is -1, then negate the matrix and the scaling factors. */ - if (row[0].dot(row[1].cross(row[2])) < 0) { + if (row[0].Dot(row[1].Cross(row[2])) < 0) { result.scale.x *= -1; result.scale.y *= -1; result.scale.z *= -1; @@ -436,7 +437,7 @@ Matrix::DecompositionResult Matrix::decompose() const { return DecompositionResult(true, result); } -uint64_t Matrix::Decomposition::componentsMask() const { +uint64_t Matrix::Decomposition::GetComponentsMask() const { uint64_t mask = 0; Quaternion noRotation(0.0, 0.0, 0.0, 1.0); @@ -467,14 +468,14 @@ uint64_t Matrix::Decomposition::componentsMask() const { return mask; } -std::string Matrix::Decomposition::toString() const { +std::string Matrix::Decomposition::ToString() const { std::stringstream stream; - stream << "Translation: " << translation.toString() << std::endl; - stream << "Scale: " << scale.toString() << std::endl; - stream << "Shear: " << shear.toString() << std::endl; - stream << "Perspective: " << perspective.toString() << std::endl; - stream << "Rotation: " << rotation.toString() << std::endl; + stream << "Translation: " << translation.ToString() << std::endl; + stream << "Scale: " << scale.ToString() << std::endl; + stream << "Shear: " << shear.ToString() << std::endl; + stream << "Perspective: " << perspective.ToString() << std::endl; + stream << "Rotation: " << rotation.ToString() << std::endl; return stream.str(); } diff --git a/impeller/impeller/geometry/Matrix.h b/impeller/impeller/geometry/Matrix.h index 721ed839d1ef1..8fb236373b5e9 100644 --- a/impeller/impeller/geometry/Matrix.h +++ b/impeller/impeller/geometry/Matrix.h @@ -6,6 +6,7 @@ #include #include + #include "Point.h" #include "Quaternion.h" #include "Shear.h" @@ -37,9 +38,9 @@ struct Matrix { Rotation = 1 << 4, }; - uint64_t componentsMask() const; + uint64_t GetComponentsMask() const; - std::string toString() const; + std::string ToString() const; }; using DecompositionResult = @@ -66,7 +67,7 @@ struct Matrix { Matrix(const Decomposition& decomposition); - static Matrix Translation(const Vector3& t) { + static Matrix MakeTranslation(const Vector3& t) { // clang-format off return Matrix(1.0, 0.0, 0.0, 0.0, 0.0, 1.0, 0.0, 0.0, @@ -75,7 +76,7 @@ struct Matrix { // clang-format on } - static Matrix Scale(const Vector3& s) { + static Matrix MakeScale(const Vector3& s) { // clang-format off return Matrix(s.x, 0.0, 0.0, 0.0, 0.0, s.y, 0.0, 0.0, @@ -84,8 +85,8 @@ struct Matrix { // clang-format on } - static Matrix Rotation(double radians, const Vector4& r) { - const Vector4 v = r.normalize(); + static Matrix MakeRotation(double radians, const Vector4& r) { + const Vector4 v = r.Normalize(); const double cosine = cos(radians); const double cosp = 1.0f - cosine; @@ -115,7 +116,7 @@ struct Matrix { // clang-format on } - static Matrix RotationX(double radians) { + static Matrix MakeRotationX(double radians) { double cosine = cos(radians); double sine = sin(radians); // clang-format off @@ -128,7 +129,7 @@ struct Matrix { // clang-format on } - static Matrix RotationY(double radians) { + static Matrix MakeRotationY(double radians) { double cosine = cos(radians); double sine = sin(radians); @@ -142,7 +143,7 @@ struct Matrix { // clang-format on } - static Matrix RotationZ(double radians) { + static Matrix MakeRotationZ(double radians) { double cosine = cos(radians); double sine = sin(radians); @@ -156,7 +157,35 @@ struct Matrix { // clang-format on } - Matrix translate(const Vector3& t) const { + static Matrix MakeOrthographic(double left, + double right, + double bottom, + double top, + double nearZ, + double farZ); + + static Matrix MakeOrthographic(const Size& size); + + /** + * Specify a viewing frustum in the worlds coordinate system. + * + * @param fov angle of the field of view (in radians). + * @param aspect aspect ratio. + * @param nearZ near clipping plane. + * @param farZ far clipping plane. + * + * @return the perspective projection matrix. + */ + static Matrix MakePerspective(double fov, + double aspect, + double nearZ, + double farZ); + + static Matrix MakeLookAt(const Vector3& eye, + const Vector3& center, + const Vector3& up); + + Matrix Translate(const Vector3& t) const { // clang-format off return Matrix(m[0], m[1], m[2], m[3], m[4], m[5], m[6], m[7], @@ -168,7 +197,7 @@ struct Matrix { // clang-format on } - Matrix scale(const Vector3& s) const { + Matrix Scale(const Vector3& s) const { // clang-format off return Matrix(m[0] * s.x, m[1] * s.x, m[2] * s.x , m[3] * s.x, m[4] * s.y, m[5] * s.y, m[6] * s.y , m[7] * s.y, @@ -177,7 +206,7 @@ struct Matrix { // clang-format on } - Matrix multiply(const Matrix& o) const { + Matrix Multiply(const Matrix& o) const { // clang-format off return Matrix( m[0] * o.m[0] + m[4] * o.m[1] + m[8] * o.m[2] + m[12] * o.m[3], @@ -199,18 +228,18 @@ struct Matrix { // clang-format on } - Matrix transpose() const; + Matrix Transpose() const; - Matrix invert() const; + Matrix Invert() const; - double determinant() const; + double GetDeterminant() const; - bool isAffine() const { + bool IsAffine() const { return (m[2] == 0 && m[3] == 0 && m[6] == 0 && m[7] == 0 && m[8] == 0 && m[9] == 0 && m[10] == 1 && m[11] == 0 && m[14] == 0 && m[15] == 1); } - bool isIdentity() const { + bool IsIdentity() const { return ( // clang-format off m[0] == 1.0 && m[1] == 0.0 && m[2] == 0.0 && m[3] == 0.0 && @@ -221,7 +250,7 @@ struct Matrix { ); } - DecompositionResult decompose() const; + DecompositionResult Decompose() const; bool operator==(const Matrix& m) const { // clang-format off @@ -241,47 +270,19 @@ struct Matrix { // clang-format on } - Matrix operator+(const Vector3& t) const { return translate(t); } + Matrix operator+(const Vector3& t) const { return Translate(t); } - Matrix operator-(const Vector3& t) const { return translate(-t); } + Matrix operator-(const Vector3& t) const { return Translate(-t); } - Matrix operator*(const Vector3& s) const { return scale(s); } + Matrix operator*(const Vector3& s) const { return Scale(s); } - Matrix operator*(const Matrix& m) const { return multiply(m); } + Matrix operator*(const Matrix& m) const { return Multiply(m); } Matrix operator+(const Matrix& m) const; - static Matrix Orthographic(double left, - double right, - double bottom, - double top, - double nearZ, - double farZ); - - static Matrix Orthographic(const Size& size); - - /** - * Specify a viewing frustum in the worlds coordinate system. - * - * @param fov angle of the field of view (in radians). - * @param aspect aspect ratio. - * @param nearZ near clipping plane. - * @param farZ far clipping plane. - * - * @return the perspective projection matrix. - */ - static Matrix Perspective(double fov, - double aspect, - double nearZ, - double farZ); - - static Matrix LookAt(const Vector3& eye, - const Vector3& center, - const Vector3& up); - - std::string toString() const; + std::string ToString() const; - void fromString(const std::string& str); + void FromString(const std::string& str); }; static_assert(sizeof(struct Matrix) == sizeof(double) * 16, diff --git a/impeller/impeller/geometry/Path.cc b/impeller/impeller/geometry/Path.cc index b8c9ec6e5e6f1..2b6dee74da69c 100644 --- a/impeller/impeller/geometry/Path.cc +++ b/impeller/impeller/geometry/Path.cc @@ -153,17 +153,17 @@ void Path::smoothPoints(SmoothPointsEnumerator enumerator, for (const auto& component : _components) { switch (component.type) { case ComponentType::Linear: { - if (!enumerator(_linears[component.index].smoothPoints())) { + if (!enumerator(_linears[component.index].SmoothPoints())) { return; } } break; case ComponentType::Quadratic: { - if (!enumerator(_quads[component.index].smoothPoints(approximation))) { + if (!enumerator(_quads[component.index].SmoothPoints(approximation))) { return; } } break; case ComponentType::Cubic: { - if (!enumerator(_cubics[component.index].smoothPoints(approximation))) { + if (!enumerator(_cubics[component.index].SmoothPoints(approximation))) { return; } } break; @@ -175,15 +175,15 @@ Rect Path::boundingBox() const { Rect box; for (const auto& linear : _linears) { - box = box.withPoints(linear.extrema()); + box = box.WithPoints(linear.Extrema()); } for (const auto& quad : _quads) { - box = box.withPoints(quad.extrema()); + box = box.WithPoints(quad.Sxtrema()); } for (const auto& cubic : _cubics) { - box = box.withPoints(cubic.extrema()); + box = box.WithPoints(cubic.Extrema()); } return box; diff --git a/impeller/impeller/geometry/PathBuilder.cc b/impeller/impeller/geometry/PathBuilder.cc index 5bfe23dbeb9e9..3f85ef9dce544 100644 --- a/impeller/impeller/geometry/PathBuilder.cc +++ b/impeller/impeller/geometry/PathBuilder.cc @@ -13,129 +13,129 @@ PathBuilder::PathBuilder() = default; PathBuilder::~PathBuilder() = default; -Path PathBuilder::path() const { - return _prototype; +Path PathBuilder::CreatePath() const { + return prototype_; } -PathBuilder& PathBuilder::moveTo(Point point, bool relative) { - _current = relative ? _current + point : point; - _subpathStart = _current; +PathBuilder& PathBuilder::MoveTo(Point point, bool relative) { + current_ = relative ? current_ + point : point; + subpath_start_ = current_; return *this; } -PathBuilder& PathBuilder::close() { - lineTo(_subpathStart); +PathBuilder& PathBuilder::Close() { + LineTo(subpath_start_); return *this; } -PathBuilder& PathBuilder::lineTo(Point point, bool relative) { - point = relative ? _current + point : point; - _prototype.addLinearComponent(_current, point); - _current = point; +PathBuilder& PathBuilder::LineTo(Point point, bool relative) { + point = relative ? current_ + point : point; + prototype_.addLinearComponent(current_, point); + current_ = point; return *this; } -PathBuilder& PathBuilder::horizontalLineTo(double x, bool relative) { +PathBuilder& PathBuilder::HorizontalLineTo(double x, bool relative) { Point endpoint = - relative ? Point{_current.x + x, _current.y} : Point{x, _current.y}; - _prototype.addLinearComponent(_current, endpoint); - _current = endpoint; + relative ? Point{current_.x + x, current_.y} : Point{x, current_.y}; + prototype_.addLinearComponent(current_, endpoint); + current_ = endpoint; return *this; } -PathBuilder& PathBuilder::verticalLineTo(double y, bool relative) { +PathBuilder& PathBuilder::VerticalLineTo(double y, bool relative) { Point endpoint = - relative ? Point{_current.x, _current.y + y} : Point{_current.x, y}; - _prototype.addLinearComponent(_current, endpoint); - _current = endpoint; + relative ? Point{current_.x, current_.y + y} : Point{current_.x, y}; + prototype_.addLinearComponent(current_, endpoint); + current_ = endpoint; return *this; } -PathBuilder& PathBuilder::quadraticCurveTo(Point point, +PathBuilder& PathBuilder::QuadraticCurveTo(Point point, Point controlPoint, bool relative) { - point = relative ? _current + point : point; - controlPoint = relative ? _current + controlPoint : controlPoint; - _prototype.addQuadraticComponent(_current, controlPoint, point); - _current = point; + point = relative ? current_ + point : point; + controlPoint = relative ? current_ + controlPoint : controlPoint; + prototype_.addQuadraticComponent(current_, controlPoint, point); + current_ = point; return *this; } -Point PathBuilder::reflectedQuadraticControlPoint1() const { +Point PathBuilder::ReflectedQuadraticControlPoint1() const { /* * If there is no previous command or if the previous command was not a * quadratic, assume the control point is coincident with the current point. */ - if (_prototype.componentCount() == 0) { - return _current; + if (prototype_.componentCount() == 0) { + return current_; } QuadraticPathComponent quad; - if (!_prototype.quadraticComponentAtIndex(_prototype.componentCount() - 1, + if (!prototype_.quadraticComponentAtIndex(prototype_.componentCount() - 1, quad)) { - return _current; + return current_; } /* * The control point is assumed to be the reflection of the control point on * the previous command relative to the current point. */ - return (_current * 2.0) - quad.cp; + return (current_ * 2.0) - quad.cp; } -PathBuilder& PathBuilder::smoothQuadraticCurveTo(Point point, bool relative) { - point = relative ? _current + point : point; +PathBuilder& PathBuilder::SmoothQuadraticCurveTo(Point point, bool relative) { + point = relative ? current_ + point : point; /* * The reflected control point is absolute and we made the endpoint absolute * too. So there the last argument is always false (i.e, not relative). */ - quadraticCurveTo(point, reflectedQuadraticControlPoint1(), false); + QuadraticCurveTo(point, ReflectedQuadraticControlPoint1(), false); return *this; } -PathBuilder& PathBuilder::cubicCurveTo(Point point, +PathBuilder& PathBuilder::CubicCurveTo(Point point, Point controlPoint1, Point controlPoint2, bool relative) { - controlPoint1 = relative ? _current + controlPoint1 : controlPoint1; - controlPoint2 = relative ? _current + controlPoint2 : controlPoint2; - point = relative ? _current + point : point; - _prototype.addCubicComponent(_current, controlPoint1, controlPoint2, point); - _current = point; + controlPoint1 = relative ? current_ + controlPoint1 : controlPoint1; + controlPoint2 = relative ? current_ + controlPoint2 : controlPoint2; + point = relative ? current_ + point : point; + prototype_.addCubicComponent(current_, controlPoint1, controlPoint2, point); + current_ = point; return *this; } -Point PathBuilder::reflectedCubicControlPoint1() const { +Point PathBuilder::ReflectedCubicControlPoint1() const { /* * If there is no previous command or if the previous command was not a * cubic, assume the first control point is coincident with the current * point. */ - if (_prototype.componentCount() == 0) { - return _current; + if (prototype_.componentCount() == 0) { + return current_; } CubicPathComponent cubic; - if (!_prototype.cubicComponentAtIndex(_prototype.componentCount() - 1, + if (!prototype_.cubicComponentAtIndex(prototype_.componentCount() - 1, cubic)) { - return _current; + return current_; } /* * The first control point is assumed to be the reflection of the second * control point on the previous command relative to the current point. */ - return (_current * 2.0) - cubic.cp2; + return (current_ * 2.0) - cubic.cp2; } -PathBuilder& PathBuilder::smoothCubicCurveTo(Point point, +PathBuilder& PathBuilder::SmoothCubicCurveTo(Point point, Point controlPoint2, bool relative) { - auto controlPoint1 = reflectedCubicControlPoint1(); - controlPoint2 = relative ? _current + controlPoint2 : controlPoint2; - auto endpoint = relative ? _current + point : point; + auto controlPoint1 = ReflectedCubicControlPoint1(); + controlPoint2 = relative ? current_ + controlPoint2 : controlPoint2; + auto endpoint = relative ? current_ + point : point; - cubicCurveTo(endpoint, // endpoint + CubicCurveTo(endpoint, // endpoint controlPoint1, // control point 1 controlPoint2, // control point 2 false // relative since all points are already absolute @@ -143,15 +143,15 @@ PathBuilder& PathBuilder::smoothCubicCurveTo(Point point, return *this; } -PathBuilder& PathBuilder::addRect(Rect rect) { - _current = rect.origin; +PathBuilder& PathBuilder::AddRect(Rect rect) { + current_ = rect.origin; auto topLeft = rect.origin; auto bottomLeft = rect.origin + Point{0.0, rect.size.height}; auto bottomRight = rect.origin + Point{rect.size.width, rect.size.height}; auto topRight = rect.origin + Point{rect.size.width, 0.0}; - _prototype.addLinearComponent(topLeft, bottomLeft) + prototype_.addLinearComponent(topLeft, bottomLeft) .addLinearComponent(bottomLeft, bottomRight) .addLinearComponent(bottomRight, topRight) .addLinearComponent(topRight, topLeft); @@ -159,34 +159,34 @@ PathBuilder& PathBuilder::addRect(Rect rect) { return *this; } -PathBuilder& PathBuilder::addCircle(const Point& center, double radius) { - _current = center + Point{0.0, radius}; +PathBuilder& PathBuilder::AddCircle(const Point& center, double radius) { + current_ = center + Point{0.0, radius}; const double diameter = radius * 2.0; const double magic = kArcApproximationMagic * radius; - _prototype.addCubicComponent( + prototype_.addCubicComponent( {center.x + radius, center.y}, // {center.x + radius + magic, center.y}, // {center.x + diameter, center.y + radius - magic}, // {center.x + diameter, center.y + radius} // ); - _prototype.addCubicComponent( + prototype_.addCubicComponent( {center.x + diameter, center.y + radius}, // {center.x + diameter, center.y + radius + magic}, // {center.x + radius + magic, center.y + diameter}, // {center.x + radius, center.y + diameter} // ); - _prototype.addCubicComponent( + prototype_.addCubicComponent( {center.x + radius, center.y + diameter}, // {center.x + radius - magic, center.y + diameter}, // {center.x, center.y + radius + magic}, // {center.x, center.y + radius} // ); - _prototype.addCubicComponent({center.x, center.y + radius}, // + prototype_.addCubicComponent({center.x, center.y + radius}, // {center.x, center.y + radius - magic}, // {center.x + radius - magic, center.y}, // {center.x + radius, center.y} // @@ -195,13 +195,13 @@ PathBuilder& PathBuilder::addCircle(const Point& center, double radius) { return *this; } -PathBuilder& PathBuilder::addRoundedRect(Rect rect, double radius) { - return radius == 0.0 ? addRect(rect) - : addRoundedRect(rect, {radius, radius, radius, radius}); +PathBuilder& PathBuilder::AddRoundedRect(Rect rect, double radius) { + return radius == 0.0 ? AddRect(rect) + : AddRoundedRect(rect, {radius, radius, radius, radius}); } -PathBuilder& PathBuilder::addRoundedRect(Rect rect, RoundingRadii radii) { - _current = rect.origin + Point{radii.topLeft, 0.0}; +PathBuilder& PathBuilder::AddRoundedRect(Rect rect, RoundingRadii radii) { + current_ = rect.origin + Point{radii.topLeft, 0.0}; const double magicTopRight = kArcApproximationMagic * radii.topRight; const double magicBottomRight = kArcApproximationMagic * radii.bottomRight; @@ -211,14 +211,14 @@ PathBuilder& PathBuilder::addRoundedRect(Rect rect, RoundingRadii radii) { /* * Top line. */ - _prototype.addLinearComponent( + prototype_.addLinearComponent( {rect.origin.x + radii.topLeft, rect.origin.y}, {rect.origin.x + rect.size.width - radii.topRight, rect.origin.y}); /* * Top right arc. */ - _prototype.addCubicComponent( + prototype_.addCubicComponent( {rect.origin.x + rect.size.width - radii.topRight, rect.origin.y}, {rect.origin.x + rect.size.width - radii.topRight + magicTopRight, rect.origin.y}, @@ -229,7 +229,7 @@ PathBuilder& PathBuilder::addRoundedRect(Rect rect, RoundingRadii radii) { /* * Right line. */ - _prototype.addLinearComponent( + prototype_.addLinearComponent( {rect.origin.x + rect.size.width, rect.origin.y + radii.topRight}, {rect.origin.x + rect.size.width, rect.origin.y + rect.size.height - radii.bottomRight}); @@ -237,7 +237,7 @@ PathBuilder& PathBuilder::addRoundedRect(Rect rect, RoundingRadii radii) { /* * Bottom right arc. */ - _prototype.addCubicComponent( + prototype_.addCubicComponent( {rect.origin.x + rect.size.width, rect.origin.y + rect.size.height - radii.bottomRight}, {rect.origin.x + rect.size.width, @@ -250,7 +250,7 @@ PathBuilder& PathBuilder::addRoundedRect(Rect rect, RoundingRadii radii) { /* * Bottom line. */ - _prototype.addLinearComponent( + prototype_.addLinearComponent( {rect.origin.x + rect.size.width - radii.bottomRight, rect.origin.y + rect.size.height}, {rect.origin.x + radii.bottomLeft, rect.origin.y + rect.size.height}); @@ -258,7 +258,7 @@ PathBuilder& PathBuilder::addRoundedRect(Rect rect, RoundingRadii radii) { /* * Bottom left arc. */ - _prototype.addCubicComponent( + prototype_.addCubicComponent( {rect.origin.x + radii.bottomLeft, rect.origin.y + rect.size.height}, {rect.origin.x + radii.bottomLeft - magicBottomLeft, rect.origin.y + rect.size.height}, @@ -269,14 +269,14 @@ PathBuilder& PathBuilder::addRoundedRect(Rect rect, RoundingRadii radii) { /* * Left line. */ - _prototype.addLinearComponent( + prototype_.addLinearComponent( {rect.origin.x, rect.origin.y + rect.size.height - radii.bottomLeft}, {rect.origin.x, rect.origin.y + radii.topLeft}); /* * Top left arc. */ - _prototype.addCubicComponent( + prototype_.addCubicComponent( {rect.origin.x, rect.origin.y + radii.topLeft}, {rect.origin.x, rect.origin.y + radii.topLeft - magicTopLeft}, {rect.origin.x + radii.topLeft - magicTopLeft, rect.origin.y}, @@ -285,35 +285,35 @@ PathBuilder& PathBuilder::addRoundedRect(Rect rect, RoundingRadii radii) { return *this; } -PathBuilder& PathBuilder::addEllipse(const Point& center, const Size& radius) { - _current = center + Point{0.0, radius.height}; +PathBuilder& PathBuilder::AddEllipse(const Point& center, const Size& radius) { + current_ = center + Point{0.0, radius.height}; const Size diameter = {radius.width * 2.0, radius.height * 2.0}; const Size magic = {kArcApproximationMagic * radius.width, kArcApproximationMagic * radius.height}; - _prototype.addCubicComponent( + prototype_.addCubicComponent( {center.x + radius.width, center.y}, // {center.x + radius.width + magic.width, center.y}, // {center.x + diameter.width, center.y + radius.height - magic.height}, // {center.x + diameter.width, center.y + radius.height} // ); - _prototype.addCubicComponent( + prototype_.addCubicComponent( {center.x + diameter.width, center.y + radius.height}, // {center.x + diameter.width, center.y + radius.height + magic.height}, // {center.x + radius.width + magic.width, center.y + diameter.height}, // {center.x + radius.width, center.y + diameter.height} // ); - _prototype.addCubicComponent( + prototype_.addCubicComponent( {center.x + radius.width, center.y + diameter.height}, // {center.x + radius.width - magic.width, center.y + diameter.height}, // {center.x, center.y + radius.height + magic.height}, // {center.x, center.y + radius.height} // ); - _prototype.addCubicComponent( + prototype_.addCubicComponent( {center.x, center.y + radius.height}, // {center.x, center.y + radius.height - magic.height}, // {center.x + radius.width - magic.width, center.y}, // diff --git a/impeller/impeller/geometry/PathBuilder.h b/impeller/impeller/geometry/PathBuilder.h index 161aabc5ff7d7..504a6d5275992 100644 --- a/impeller/impeller/geometry/PathBuilder.h +++ b/impeller/impeller/geometry/PathBuilder.h @@ -17,40 +17,40 @@ class PathBuilder { ~PathBuilder(); - Path path() const; + Path CreatePath() const; - PathBuilder& moveTo(Point point, bool relative = false); + PathBuilder& MoveTo(Point point, bool relative = false); - PathBuilder& close(); + PathBuilder& Close(); - PathBuilder& lineTo(Point point, bool relative = false); + PathBuilder& LineTo(Point point, bool relative = false); - PathBuilder& horizontalLineTo(double x, bool relative = false); + PathBuilder& HorizontalLineTo(double x, bool relative = false); - PathBuilder& verticalLineTo(double y, bool relative = false); + PathBuilder& VerticalLineTo(double y, bool relative = false); - PathBuilder& quadraticCurveTo(Point point, + PathBuilder& QuadraticCurveTo(Point point, Point controlPoint, bool relative = false); - PathBuilder& smoothQuadraticCurveTo(Point point, bool relative = false); + PathBuilder& SmoothQuadraticCurveTo(Point point, bool relative = false); - PathBuilder& cubicCurveTo(Point point, + PathBuilder& CubicCurveTo(Point point, Point controlPoint1, Point controlPoint2, bool relative = false); - PathBuilder& smoothCubicCurveTo(Point point, + PathBuilder& SmoothCubicCurveTo(Point point, Point controlPoint2, bool relative = false); - PathBuilder& addRect(Rect rect); + PathBuilder& AddRect(Rect rect); - PathBuilder& addRoundedRect(Rect rect, double radius); + PathBuilder& AddRoundedRect(Rect rect, double radius); - PathBuilder& addCircle(const Point& center, double radius); + PathBuilder& AddCircle(const Point& center, double radius); - PathBuilder& addEllipse(const Point& center, const Size& size); + PathBuilder& AddEllipse(const Point& center, const Size& size); struct RoundingRadii { double topLeft; @@ -71,16 +71,16 @@ class PathBuilder { bottomRight(pBottomRight) {} }; - PathBuilder& addRoundedRect(Rect rect, RoundingRadii radii); + PathBuilder& AddRoundedRect(Rect rect, RoundingRadii radii); private: - Point _subpathStart; - Point _current; - Path _prototype; + Point subpath_start_; + Point current_; + Path prototype_; - Point reflectedQuadraticControlPoint1() const; + Point ReflectedQuadraticControlPoint1() const; - Point reflectedCubicControlPoint1() const; + Point ReflectedCubicControlPoint1() const; FML_DISALLOW_COPY_AND_ASSIGN(PathBuilder); }; diff --git a/impeller/impeller/geometry/PathComponent.cc b/impeller/impeller/geometry/PathComponent.cc index 2540e3082b27b..990f8c73b985a 100644 --- a/impeller/impeller/geometry/PathComponent.cc +++ b/impeller/impeller/geometry/PathComponent.cc @@ -56,54 +56,54 @@ static inline double CubicSolveDerivative(double t, 3 * p3 * t * t; } -Point LinearPathComponent::solve(double time) const { +Point LinearPathComponent::Solve(double time) const { return { LinearSolve(time, p1.x, p2.x), // x LinearSolve(time, p1.y, p2.y), // y }; } -std::vector LinearPathComponent::smoothPoints() const { +std::vector LinearPathComponent::SmoothPoints() const { return {p1, p2}; } -std::vector LinearPathComponent::extrema() const { +std::vector LinearPathComponent::Extrema() const { return {p1, p2}; } -Point QuadraticPathComponent::solve(double time) const { +Point QuadraticPathComponent::Solve(double time) const { return { QuadraticSolve(time, p1.x, cp.x, p2.x), // x QuadraticSolve(time, p1.y, cp.y, p2.y), // y }; } -Point QuadraticPathComponent::solveDerivative(double time) const { +Point QuadraticPathComponent::SolveDerivative(double time) const { return { QuadraticSolveDerivative(time, p1.x, cp.x, p2.x), // x QuadraticSolveDerivative(time, p1.y, cp.y, p2.y), // y }; } -std::vector QuadraticPathComponent::smoothPoints( +std::vector QuadraticPathComponent::SmoothPoints( const SmoothingApproximation& approximation) const { CubicPathComponent elevated(*this); - return elevated.smoothPoints(approximation); + return elevated.SmoothPoints(approximation); } -std::vector QuadraticPathComponent::extrema() const { +std::vector QuadraticPathComponent::Sxtrema() const { CubicPathComponent elevated(*this); - return elevated.extrema(); + return elevated.Extrema(); } -Point CubicPathComponent::solve(double time) const { +Point CubicPathComponent::Solve(double time) const { return { CubicSolve(time, p1.x, cp1.x, cp2.x, p2.x), // x CubicSolve(time, p1.y, cp1.y, cp2.y, p2.y), // y }; } -Point CubicPathComponent::solveDerivative(double time) const { +Point CubicPathComponent::SolveDerivative(double time) const { return { CubicSolveDerivative(time, p1.x, cp1.x, cp2.x, p2.x), // x CubicSolveDerivative(time, p1.y, cp1.y, cp2.y, p2.y), // y @@ -157,8 +157,8 @@ static void CubicPathSmoothenRecursive(const SmoothingApproximation& approx, */ k = d.x * d.x + d.y * d.y; if (k == 0) { - d2 = p1.distanceSquared(p2); - d3 = p4.distanceSquared(p3); + d2 = p1.GetDistanceSquared(p2); + d3 = p4.GetDistanceSquared(p3); } else { k = 1.0 / k; da1 = p2.x - p1.x; @@ -176,19 +176,19 @@ static void CubicPathSmoothenRecursive(const SmoothingApproximation& approx, } if (d2 <= 0) { - d2 = p2.distanceSquared(p1); + d2 = p2.GetDistanceSquared(p1); } else if (d2 >= 1) { - d2 = p2.distanceSquared(p4); + d2 = p2.GetDistanceSquared(p4); } else { - d2 = p2.distanceSquared({p1.x + d2 * d.x, p1.y + d2 * d.y}); + d2 = p2.GetDistanceSquared({p1.x + d2 * d.x, p1.y + d2 * d.y}); } if (d3 <= 0) { - d3 = p3.distanceSquared(p1); + d3 = p3.GetDistanceSquared(p1); } else if (d3 >= 1) { - d3 = p3.distanceSquared(p4); + d3 = p3.GetDistanceSquared(p4); } else { - d3 = p3.distanceSquared({p1.x + d3 * d.x, p1.y + d3 * d.y}); + d3 = p3.GetDistanceSquared({p1.x + d3 * d.x, p1.y + d3 * d.y}); } } @@ -334,7 +334,7 @@ static void CubicPathSmoothenRecursive(const SmoothingApproximation& approx, CubicPathSmoothenRecursive(approx, points, p1234, p234, p34, p4, level + 1); } -std::vector CubicPathComponent::smoothPoints( +std::vector CubicPathComponent::SmoothPoints( const SmoothingApproximation& approximation) const { std::vector points; points.emplace_back(p1); @@ -398,7 +398,7 @@ static void CubicPathBoundingPopulateValues(std::vector& values, } } -std::vector CubicPathComponent::extrema() const { +std::vector CubicPathComponent::Extrema() const { /* * As described in: https://pomax.github.io/bezierinfo/#extremities */ @@ -410,7 +410,7 @@ std::vector CubicPathComponent::extrema() const { std::vector points; for (const auto& value : values) { - points.emplace_back(solve(value)); + points.emplace_back(Solve(value)); } return points; diff --git a/impeller/impeller/geometry/PathComponent.h b/impeller/impeller/geometry/PathComponent.h index b1746b22ab025..139adf0ea56ed 100644 --- a/impeller/impeller/geometry/PathComponent.h +++ b/impeller/impeller/geometry/PathComponent.h @@ -39,11 +39,11 @@ struct LinearPathComponent { LinearPathComponent(Point ap1, Point ap2) : p1(ap1), p2(ap2) {} - Point solve(double time) const; + Point Solve(double time) const; - std::vector smoothPoints() const; + std::vector SmoothPoints() const; - std::vector extrema() const; + std::vector Extrema() const; bool operator==(const LinearPathComponent& other) const { return p1 == other.p1 && p2 == other.p2; @@ -60,14 +60,14 @@ struct QuadraticPathComponent { QuadraticPathComponent(Point ap1, Point acp, Point ap2) : p1(ap1), cp(acp), p2(ap2) {} - Point solve(double time) const; + Point Solve(double time) const; - Point solveDerivative(double time) const; + Point SolveDerivative(double time) const; - std::vector smoothPoints( + std::vector SmoothPoints( const SmoothingApproximation& approximation) const; - std::vector extrema() const; + std::vector Sxtrema() const; bool operator==(const QuadraticPathComponent& other) const { return p1 == other.p1 && cp == other.cp && p2 == other.p2; @@ -91,14 +91,14 @@ struct CubicPathComponent { CubicPathComponent(Point ap1, Point acp1, Point acp2, Point ap2) : p1(ap1), cp1(acp1), cp2(acp2), p2(ap2) {} - Point solve(double time) const; + Point Solve(double time) const; - Point solveDerivative(double time) const; + Point SolveDerivative(double time) const; - std::vector smoothPoints( + std::vector SmoothPoints( const SmoothingApproximation& approximation) const; - std::vector extrema() const; + std::vector Extrema() const; bool operator==(const CubicPathComponent& other) const { return p1 == other.p1 && cp1 == other.cp1 && cp2 == other.cp2 && diff --git a/impeller/impeller/geometry/Point.cc b/impeller/impeller/geometry/Point.cc index 9b8d6976f2a28..72a0b9db4574d 100644 --- a/impeller/impeller/geometry/Point.cc +++ b/impeller/impeller/geometry/Point.cc @@ -8,27 +8,27 @@ namespace rl { namespace geom { -std::string Point::toString() const { +std::string Point::ToString() const { std::stringstream stream; stream << x << "," << y; return stream.str(); } -void Point::fromString(const std::string& str) { +void Point::FromString(const std::string& str) { std::stringstream stream(str); stream >> x; stream.ignore(); stream >> y; } -double Point::distanceSquared(const Point& p) const { +double Point::GetDistanceSquared(const Point& p) const { double dx = p.x - x; double dy = p.y - y; return dx * dx + dy * dy; } -double Point::distance(const Point& p) const { - return sqrt(distanceSquared(p)); +double Point::GetDistance(const Point& p) const { + return sqrt(GetDistanceSquared(p)); } } // namespace geom diff --git a/impeller/impeller/geometry/Point.h b/impeller/impeller/geometry/Point.h index 988d2fd16ecd8..a099d373aa84e 100644 --- a/impeller/impeller/geometry/Point.h +++ b/impeller/impeller/geometry/Point.h @@ -12,10 +12,10 @@ namespace rl { namespace geom { struct Point { - double x; - double y; + double x = 0.0; + double y = 0.0; - constexpr Point() : x(0.0), y(0.0) {} + constexpr Point() = default; constexpr Point(double x, double y) : x(x), y(y) {} @@ -48,13 +48,13 @@ struct Point { Point operator/(const Size& s) const { return {x / s.width, y / s.height}; } - double distanceSquared(const Point& p) const; + double GetDistanceSquared(const Point& p) const; - double distance(const Point& p) const; + double GetDistance(const Point& p) const; - std::string toString() const; + std::string ToString() const; - void fromString(const std::string& str); + void FromString(const std::string& str); }; } // namespace geom diff --git a/impeller/impeller/geometry/Quaternion.cc b/impeller/impeller/geometry/Quaternion.cc index 58ef20c05140c..34303396051d4 100644 --- a/impeller/impeller/geometry/Quaternion.cc +++ b/impeller/impeller/geometry/Quaternion.cc @@ -8,8 +8,8 @@ namespace rl { namespace geom { -Quaternion Quaternion::slerp(const Quaternion& to, double time) const { - double cosine = dot(to); +Quaternion Quaternion::Slerp(const Quaternion& to, double time) const { + double cosine = Dot(to); if (fabs(cosine) < 1.0 - 1e-3 /* epsilon */) { /* * Spherical Interpolation. @@ -24,11 +24,11 @@ Quaternion Quaternion::slerp(const Quaternion& to, double time) const { /* * Linear Interpolation. */ - return (*this * (1.0 - time) + to * time).normalize(); + return (*this * (1.0 - time) + to * time).Normalize(); } } -std::string Quaternion::toString() const { +std::string Quaternion::ToString() const { std::stringstream stream; stream << "{" << x << ", " << ", " << y << ", " << z << ", " << w << "}"; diff --git a/impeller/impeller/geometry/Quaternion.h b/impeller/impeller/geometry/Quaternion.h index ec5e6eb608471..07507b8eb3a55 100644 --- a/impeller/impeller/geometry/Quaternion.h +++ b/impeller/impeller/geometry/Quaternion.h @@ -12,15 +12,15 @@ namespace geom { struct Quaternion { union { struct { - double x; - double y; - double z; - double w; + double x = 0.0; + double y = 0.0; + double z = 0.0; + double w = 1.0; }; double e[4]; }; - Quaternion() : x(0.0), y(0.0), z(0.0), w(1.0) {} + Quaternion() {} Quaternion(double px, double py, double pz, double pw) : x(px), y(py), z(pz), w(pw) {} @@ -33,18 +33,18 @@ struct Quaternion { w = cos(angle * 0.5); } - double dot(const Quaternion& q) const { + double Dot(const Quaternion& q) const { return x * q.x + y * q.y + z * q.z + w * q.w; } - double length() const { return sqrt(x * x + y * y + z * z + w * w); } + double Length() const { return sqrt(x * x + y * y + z * z + w * w); } - Quaternion normalize() const { - auto m = 1.0 / length(); + Quaternion Normalize() const { + auto m = 1.0 / Length(); return {x * m, y * m, z * m, w * m}; } - Quaternion slerp(const Quaternion& to, double time) const; + Quaternion Slerp(const Quaternion& to, double time) const; Quaternion operator*(const Quaternion& o) const { return { @@ -75,7 +75,7 @@ struct Quaternion { return x != o.x || y != o.y || z != o.z || w != o.w; } - std::string toString() const; + std::string ToString() const; }; } // namespace geom diff --git a/impeller/impeller/geometry/Rect.cc b/impeller/impeller/geometry/Rect.cc index a718e746fcce1..cfcc4d9296e5e 100644 --- a/impeller/impeller/geometry/Rect.cc +++ b/impeller/impeller/geometry/Rect.cc @@ -8,7 +8,7 @@ namespace rl { namespace geom { -Rect Rect::withPoint(const Point& p) const { +Rect Rect::WithPoint(const Point& p) const { Rect copy = *this; if (p.x < origin.x) { copy.origin.x = p.x; @@ -31,22 +31,22 @@ Rect Rect::withPoint(const Point& p) const { return copy; } -Rect Rect::withPoints(const std::vector& points) const { +Rect Rect::WithPoints(const std::vector& points) const { Rect box = *this; for (const auto& point : points) { - box = box.withPoint(point); + box = box.WithPoint(point); } return box; } -std::string Rect::toString() const { +std::string Rect::ToString() const { std::stringstream stream; stream << origin.x << "," << origin.y << "," << size.width << "," << size.height; return stream.str(); } -void Rect::fromString(const std::string& str) { +void Rect::FromString(const std::string& str) { std::stringstream stream(str); stream >> origin.x; stream.ignore(); diff --git a/impeller/impeller/geometry/Rect.h b/impeller/impeller/geometry/Rect.h index 1cc5e39f1cfed..04223c47a04b5 100644 --- a/impeller/impeller/geometry/Rect.h +++ b/impeller/impeller/geometry/Rect.h @@ -16,11 +16,15 @@ struct Rect { Size size; Rect() : origin({0.0, 0.0}), size({0.0, 0.0}) {} + Rect(Size size) : origin({0.0, 0.0}), size(size) {} + Rect(Point origin, Size size) : origin(origin), size(size) {} + Rect(const double components[4]) : origin(components[0], components[1]), size(components[2], components[3]) {} + Rect(double x, double y, double width, double height) : origin(x, y), size(width, height) {} @@ -51,20 +55,20 @@ struct Rect { return origin == r.origin && size == r.size; } - bool contains(const Point& p) const { + bool Contains(const Point& p) const { return p.x >= origin.x && p.x <= size.width && p.y >= origin.y && p.y <= size.height; } - bool isZero() const { return size.isZero(); } + bool IsZero() const { return size.IsZero(); } - Rect withPoint(const Point& p) const; + Rect WithPoint(const Point& p) const; - Rect withPoints(const std::vector& points) const; + Rect WithPoints(const std::vector& points) const; - std::string toString() const; + std::string ToString() const; - void fromString(const std::string& str); + void FromString(const std::string& str); }; } // namespace geom diff --git a/impeller/impeller/geometry/Shear.cc b/impeller/impeller/geometry/Shear.cc index fc0cae787a02e..ed77e6f841938 100644 --- a/impeller/impeller/geometry/Shear.cc +++ b/impeller/impeller/geometry/Shear.cc @@ -8,7 +8,7 @@ namespace rl { namespace geom { -std::string Shear::toString() const { +std::string Shear::ToString() const { std::stringstream stream; stream << "{" << xy << ", " << xz << ", " << yz << "}"; return stream.str(); diff --git a/impeller/impeller/geometry/Shear.h b/impeller/impeller/geometry/Shear.h index dd5d402bc9eea..a3ba44ea65a2b 100644 --- a/impeller/impeller/geometry/Shear.h +++ b/impeller/impeller/geometry/Shear.h @@ -29,7 +29,7 @@ struct Shear { bool operator!=(const Shear& o) const { return !(*this == o); } - std::string toString() const; + std::string ToString() const; }; } // namespace geom diff --git a/impeller/impeller/geometry/Size.cc b/impeller/impeller/geometry/Size.cc index d6f7f9da19d25..91ea783ed5830 100644 --- a/impeller/impeller/geometry/Size.cc +++ b/impeller/impeller/geometry/Size.cc @@ -8,13 +8,13 @@ namespace rl { namespace geom { -std::string Size::toString() const { +std::string Size::ToString() const { std::stringstream stream; stream << width << "," << height; return stream.str(); } -void Size::fromString(const std::string& str) { +void Size::FromString(const std::string& str) { std::stringstream stream(str); stream >> width; stream.ignore(); diff --git a/impeller/impeller/geometry/Size.h b/impeller/impeller/geometry/Size.h index bc064fb9b2fad..62fa7074cf934 100644 --- a/impeller/impeller/geometry/Size.h +++ b/impeller/impeller/geometry/Size.h @@ -38,20 +38,20 @@ struct Size { return {width - s.width, height - s.height}; } - Size unionWith(const Size& o) const { + Size Union(const Size& o) const { return { std::max(width, o.width), std::max(height, o.height), }; } - bool isZero() const { return width * height == 0.0; } + bool IsZero() const { return width * height == 0.0; } - bool isPositive() const { return width > 0.0 && height > 0.0; } + bool IsPositive() const { return width > 0.0 && height > 0.0; } - std::string toString() const; + std::string ToString() const; - void fromString(const std::string& str); + void FromString(const std::string& str); }; } // namespace geom diff --git a/impeller/impeller/geometry/Vector.cc b/impeller/impeller/geometry/Vector.cc index 2d33a39bdf587..4e113b24746b4 100644 --- a/impeller/impeller/geometry/Vector.cc +++ b/impeller/impeller/geometry/Vector.cc @@ -8,13 +8,13 @@ namespace rl { namespace geom { -std::string Vector3::toString() const { +std::string Vector3::ToString() const { std::stringstream stream; stream << "{" << x << ", " << y << ", " << z << "}"; return stream.str(); } -std::string Vector4::toString() const { +std::string Vector4::ToString() const { std::stringstream stream; stream << "{" << x << ", " << y << ", " << z << ", " << w << "}"; return stream.str(); diff --git a/impeller/impeller/geometry/Vector.h b/impeller/impeller/geometry/Vector.h index 69c4324622971..4abbd7088ebb7 100644 --- a/impeller/impeller/geometry/Vector.h +++ b/impeller/impeller/geometry/Vector.h @@ -15,20 +15,20 @@ namespace geom { struct Vector3 { union { struct { - double x; - double y; - double z; + double x = 0.0; + double y = 0.0; + double z = 0.0; }; double e[3]; }; - Vector3() : x(0.0), y(0.0), z(0.0) {} + Vector3() {} - Vector3(const Point& p) : x(p.x), y(p.y), z(0.0) {} + Vector3(const Point& p) : x(p.x), y(p.y) {} - Vector3(const Size& s) : x(s.width), y(s.height), z(0.0) {} + Vector3(const Size& s) : x(s.width), y(s.height) {} - Vector3(double x, double y) : x(x), y(y), z(0.0) {} + Vector3(double x, double y) : x(x), y(y) {} Vector3(double x, double y, double z) : x(x), y(y), z(z) {} @@ -37,18 +37,18 @@ struct Vector3 { * * @return the calculated length. */ - double length() const { return sqrt(x * x + y * y + z * z); } + double Length() const { return sqrt(x * x + y * y + z * z); } - Vector3 normalize() const { - const auto len = length(); + Vector3 Normalize() const { + const auto len = Length(); return {x / len, y / len, z / len}; } - double dot(const Vector3& other) const { + double Dot(const Vector3& other) const { return ((x * other.x) + (y * other.y) + (z * other.z)); } - Vector3 cross(const Vector3& other) const { + Vector3 Cross(const Vector3& other) const { return { (y * other.z) - (z * other.y), // (z * other.x) - (x * other.z), // @@ -95,29 +95,29 @@ struct Vector3 { }; } - std::string toString() const; + std::string ToString() const; }; struct Vector4 { union { struct { - double x; - double y; - double z; - double w; + double x = 0.0; + double y = 0.0; + double z = 0.0; + double w = 1.0; }; double e[4]; }; - Vector4() : x(0.0), y(0.0), z(0.0), w(1.0) {} + Vector4() {} Vector4(double x, double y, double z, double w) : x(x), y(y), z(z), w(w) {} - Vector4(const Vector3& v) : x(v.x), y(v.y), z(v.z), w(1.0) {} + Vector4(const Vector3& v) : x(v.x), y(v.y), z(v.z) {} - Vector4(const Point& p) : x(p.x), y(p.y), z(0.0), w(1.0) {} + Vector4(const Point& p) : x(p.x), y(p.y) {} - Vector4 normalize() const { + Vector4 Normalize() const { const double inverse = 1.0 / sqrt(x * x + y * y + z * z + w * w); return Vector4(x * inverse, y * inverse, z * inverse, w * inverse); } @@ -138,7 +138,7 @@ struct Vector4 { return Vector4(x - v.x, y - v.y, z - v.z, w - v.w); } - std::string toString() const; + std::string ToString() const; }; } // namespace geom diff --git a/impeller/impeller/image/Image.cc b/impeller/impeller/image/Image.cc index 79b0c19f69161..c9fa5d80a3aad 100644 --- a/impeller/impeller/image/Image.cc +++ b/impeller/impeller/image/Image.cc @@ -9,12 +9,12 @@ namespace rl { namespace image { Image::Image(std::shared_ptr sourceAllocation) - : _source(std::move(sourceAllocation)) {} + : source_(std::move(sourceAllocation)) {} Image::~Image() = default; -ImageResult Image::decode() const { - if (!_source) { +ImageResult Image::Decode() const { + if (!source_) { return {}; } @@ -23,8 +23,8 @@ ImageResult Image::decode() const { int comps = 0; stbi_uc* decoded = - stbi_load_from_memory(_source->GetMapping(), // Source Data - _source->GetSize(), // Source Data Size + stbi_load_from_memory(source_->GetMapping(), // Source Data + source_->GetSize(), // Source Data Size &width, // Out: Width &height, // Out: Height &comps, // Out: Components @@ -79,8 +79,8 @@ ImageResult Image::decode() const { }; } -bool Image::isValid() const { - return static_cast(_source); +bool Image::IsValid() const { + return static_cast(source_); } } // namespace image diff --git a/impeller/impeller/image/Image.h b/impeller/impeller/image/Image.h index cf4641b5b6edd..b51ad8dae6a99 100644 --- a/impeller/impeller/image/Image.h +++ b/impeller/impeller/image/Image.h @@ -20,12 +20,12 @@ class Image { ~Image(); - ImageResult decode() const; + ImageResult Decode() const; - bool isValid() const; + bool IsValid() const; private: - std::shared_ptr _source; + std::shared_ptr source_; }; } // namespace image diff --git a/impeller/impeller/image/ImageResult.cc b/impeller/impeller/image/ImageResult.cc index 66783483ed2e8..f68e2107dd5aa 100644 --- a/impeller/impeller/image/ImageResult.cc +++ b/impeller/impeller/image/ImageResult.cc @@ -12,27 +12,27 @@ ImageResult::ImageResult() = default; ImageResult::ImageResult(geom::Size size, Components components, std::shared_ptr allocation) - : _success(true), - _size(size), - _components(components), - _allocation(std::move(allocation)) {} + : success_(true), + size_(size), + components_(components), + allocation_(std::move(allocation)) {} ImageResult::~ImageResult() = default; -bool ImageResult::wasSuccessful() const { - return _success; +bool ImageResult::WasSuccessful() const { + return success_; } -const geom::Size& ImageResult::size() const { - return _size; +const geom::Size& ImageResult::GetSize() const { + return size_; } -ImageResult::Components ImageResult::components() const { - return _components; +ImageResult::Components ImageResult::GetComponents() const { + return components_; } -const std::shared_ptr& ImageResult::allocation() const { - return _allocation; +const std::shared_ptr& ImageResult::Allocation() const { + return allocation_; } } // namespace image diff --git a/impeller/impeller/image/ImageResult.h b/impeller/impeller/image/ImageResult.h index 28dfc18f32e64..624f67507e5e4 100644 --- a/impeller/impeller/image/ImageResult.h +++ b/impeller/impeller/image/ImageResult.h @@ -31,19 +31,19 @@ class ImageResult { ~ImageResult(); - const geom::Size& size() const; + const geom::Size& GetSize() const; - bool wasSuccessful() const; + bool WasSuccessful() const; - Components components() const; + Components GetComponents() const; - const std::shared_ptr& allocation() const; + const std::shared_ptr& Allocation() const; private: - bool _success = false; - geom::Size _size; - Components _components = Components::Invalid; - std::shared_ptr _allocation; + bool success_ = false; + geom::Size size_; + Components components_ = Components::Invalid; + std::shared_ptr allocation_; FML_DISALLOW_COPY_AND_ASSIGN(ImageResult); }; From 03ec357d5080e530537be40ac4df00b516fdff53 Mon Sep 17 00:00:00 2001 From: Chinmay Garde Date: Wed, 5 May 2021 15:44:18 -0700 Subject: [PATCH 017/433] More clang-tidy fixes. --- impeller/.clang-tidy | 20 ++++ impeller/impeller/entity/Color.cc | 4 +- impeller/impeller/entity/Color.h | 14 +-- impeller/impeller/geometry/Path.cc | 108 +++++++++++----------- impeller/impeller/geometry/Path.h | 45 ++++----- impeller/impeller/geometry/PathBuilder.cc | 62 ++++++------- 6 files changed, 138 insertions(+), 115 deletions(-) create mode 100644 impeller/.clang-tidy diff --git a/impeller/.clang-tidy b/impeller/.clang-tidy new file mode 100644 index 0000000000000..17cf0e2daf811 --- /dev/null +++ b/impeller/.clang-tidy @@ -0,0 +1,20 @@ +--- +Checks: 'clang-diagnostic-*,clang-analyzer-*,google-*,readability-identifier-naming,-google-explicit-constructor,cppcoreguidelines-prefer-member-initializer,modernize-use-default-member-init' +WarningsAsErrors: '' +HeaderFilterRegex: '' +AnalyzeTemporaryDtors: false +FormatStyle: none +CheckOptions: + - key: readability-identifier-naming.PrivateMemberCase + value: 'lower_case' + - key: readability-identifier-naming.PrivateMemberSuffix + value: '_' + - key: readability-identifier-naming.PublicMethodCase + value: 'CamelCase' + - key: readability-identifier-naming.PrivateMethodCase + value: 'CamelCase' + - key: cppcoreguidelines-prefer-member-initializer.UseAssignment + value: true + - key: modernize-use-default-member-init.UseAssignment + value: true +... diff --git a/impeller/impeller/entity/Color.cc b/impeller/impeller/entity/Color.cc index 311d1ab5174ee..175a327a3063f 100644 --- a/impeller/impeller/entity/Color.cc +++ b/impeller/impeller/entity/Color.cc @@ -94,13 +94,13 @@ std::string ColorHSB::toString() const { Color::Color(const ColorHSB& hsbColor) : Color(hsbColor.toRGBA()) {} -std::string Color::toString() const { +std::string Color::ToString() const { std::stringstream stream; stream << red << "," << green << "," << blue << "," << alpha; return stream.str(); } -void Color::fromString(const std::string& str) { +void Color::FromString(const std::string& str) { std::stringstream stream(str); stream >> red; stream.ignore(); diff --git a/impeller/impeller/entity/Color.h b/impeller/impeller/entity/Color.h index e00d8e428dd3d..ab53f60f151e2 100644 --- a/impeller/impeller/entity/Color.h +++ b/impeller/impeller/entity/Color.h @@ -19,24 +19,24 @@ struct Color { /** * The red color component (0 to 1) */ - double red; + double red = 0.0; /** * The green color component (0 to 1) */ - double green; + double green = 0.0; /** * The blue color component (0 to 1) */ - double blue; + double blue = 0.0; /** * The alpha component of the color (0 to 1) */ - double alpha; + double alpha = 0.0; - Color() : red(0.0), green(0.0), blue(0.0), alpha(0.0) {} + Color() {} Color(const ColorHSB& hsbColor); @@ -50,9 +50,9 @@ struct Color { Color operator+(const Color& other) const; - std::string toString() const; + std::string ToString() const; - void fromString(const std::string& str); + void FromString(const std::string& str); static Color White() { return {1.0, 1.0, 1.0, 1.0}; } diff --git a/impeller/impeller/geometry/Path.cc b/impeller/impeller/geometry/Path.cc index 2b6dee74da69c..f5ac80ecd6ac7 100644 --- a/impeller/impeller/geometry/Path.cc +++ b/impeller/impeller/geometry/Path.cc @@ -11,47 +11,47 @@ Path::Path() = default; Path::~Path() = default; -size_t Path::componentCount() const { - return _components.size(); +size_t Path::GetComponentCount() const { + return components_.size(); } -Path& Path::addLinearComponent(Point p1, Point p2) { - _linears.emplace_back(p1, p2); - _components.emplace_back(ComponentType::Linear, _linears.size() - 1); +Path& Path::AddLinearComponent(Point p1, Point p2) { + linears_.emplace_back(p1, p2); + components_.emplace_back(ComponentType::Linear, linears_.size() - 1); return *this; } -Path& Path::addQuadraticComponent(Point p1, Point cp, Point p2) { - _quads.emplace_back(p1, cp, p2); - _components.emplace_back(ComponentType::Quadratic, _quads.size() - 1); +Path& Path::AddQuadraticComponent(Point p1, Point cp, Point p2) { + quads_.emplace_back(p1, cp, p2); + components_.emplace_back(ComponentType::Quadratic, quads_.size() - 1); return *this; } -Path& Path::addCubicComponent(Point p1, Point cp1, Point cp2, Point p2) { - _cubics.emplace_back(p1, cp1, cp2, p2); - _components.emplace_back(ComponentType::Cubic, _cubics.size() - 1); +Path& Path::AddCubicComponent(Point p1, Point cp1, Point cp2, Point p2) { + cubics_.emplace_back(p1, cp1, cp2, p2); + components_.emplace_back(ComponentType::Cubic, cubics_.size() - 1); return *this; } -void Path::enumerateComponents(Applier linearApplier, +void Path::EnumerateComponents(Applier linearApplier, Applier quadApplier, Applier cubicApplier) const { size_t currentIndex = 0; - for (const auto& component : _components) { + for (const auto& component : components_) { switch (component.type) { case ComponentType::Linear: if (linearApplier) { - linearApplier(currentIndex, _linears[component.index]); + linearApplier(currentIndex, linears_[component.index]); } break; case ComponentType::Quadratic: if (quadApplier) { - quadApplier(currentIndex, _quads[component.index]); + quadApplier(currentIndex, quads_[component.index]); } break; case ComponentType::Cubic: if (cubicApplier) { - cubicApplier(currentIndex, _cubics[component.index]); + cubicApplier(currentIndex, cubics_[component.index]); } break; } @@ -59,111 +59,113 @@ void Path::enumerateComponents(Applier linearApplier, } } -bool Path::linearComponentAtIndex(size_t index, - LinearPathComponent& linear) const { - if (index >= _components.size()) { +bool Path::GetLinearComponentAtIndex(size_t index, + LinearPathComponent& linear) const { + if (index >= components_.size()) { return false; } - if (_components[index].type != ComponentType::Linear) { + if (components_[index].type != ComponentType::Linear) { return false; } - linear = _linears[_components[index].index]; + linear = linears_[components_[index].index]; return true; } -bool Path::quadraticComponentAtIndex(size_t index, - QuadraticPathComponent& quadratic) const { - if (index >= _components.size()) { +bool Path::GetQuadraticComponentAtIndex( + size_t index, + QuadraticPathComponent& quadratic) const { + if (index >= components_.size()) { return false; } - if (_components[index].type != ComponentType::Quadratic) { + if (components_[index].type != ComponentType::Quadratic) { return false; } - quadratic = _quads[_components[index].index]; + quadratic = quads_[components_[index].index]; return true; } -bool Path::cubicComponentAtIndex(size_t index, - CubicPathComponent& cubic) const { - if (index >= _components.size()) { +bool Path::GetCubicComponentAtIndex(size_t index, + CubicPathComponent& cubic) const { + if (index >= components_.size()) { return false; } - if (_components[index].type != ComponentType::Cubic) { + if (components_[index].type != ComponentType::Cubic) { return false; } - cubic = _cubics[_components[index].index]; + cubic = cubics_[components_[index].index]; return true; } -bool Path::updateLinearComponentAtIndex(size_t index, +bool Path::UpdateLinearComponentAtIndex(size_t index, const LinearPathComponent& linear) { - if (index >= _components.size()) { + if (index >= components_.size()) { return false; } - if (_components[index].type != ComponentType::Linear) { + if (components_[index].type != ComponentType::Linear) { return false; } - _linears[_components[index].index] = linear; + linears_[components_[index].index] = linear; return true; } -bool Path::updateQuadraticComponentAtIndex( +bool Path::UpdateQuadraticComponentAtIndex( size_t index, const QuadraticPathComponent& quadratic) { - if (index >= _components.size()) { + if (index >= components_.size()) { return false; } - if (_components[index].type != ComponentType::Quadratic) { + if (components_[index].type != ComponentType::Quadratic) { return false; } - _quads[_components[index].index] = quadratic; + quads_[components_[index].index] = quadratic; return true; } -bool Path::updateCubicComponentAtIndex(size_t index, +bool Path::UpdateCubicComponentAtIndex(size_t index, CubicPathComponent& cubic) { - if (index >= _components.size()) { + if (index >= components_.size()) { return false; } - if (_components[index].type != ComponentType::Cubic) { + if (components_[index].type != ComponentType::Cubic) { return false; } - _cubics[_components[index].index] = cubic; + cubics_[components_[index].index] = cubic; return true; } -void Path::smoothPoints(SmoothPointsEnumerator enumerator, - const SmoothingApproximation& approximation) const { +void Path::EnumerateSmoothPoints( + SmoothPointsEnumerator enumerator, + const SmoothingApproximation& approximation) const { if (enumerator == nullptr) { return; } - for (const auto& component : _components) { + for (const auto& component : components_) { switch (component.type) { case ComponentType::Linear: { - if (!enumerator(_linears[component.index].SmoothPoints())) { + if (!enumerator(linears_[component.index].SmoothPoints())) { return; } } break; case ComponentType::Quadratic: { - if (!enumerator(_quads[component.index].SmoothPoints(approximation))) { + if (!enumerator(quads_[component.index].SmoothPoints(approximation))) { return; } } break; case ComponentType::Cubic: { - if (!enumerator(_cubics[component.index].SmoothPoints(approximation))) { + if (!enumerator(cubics_[component.index].SmoothPoints(approximation))) { return; } } break; @@ -171,18 +173,18 @@ void Path::smoothPoints(SmoothPointsEnumerator enumerator, } } -Rect Path::boundingBox() const { +Rect Path::GetBoundingBox() const { Rect box; - for (const auto& linear : _linears) { + for (const auto& linear : linears_) { box = box.WithPoints(linear.Extrema()); } - for (const auto& quad : _quads) { + for (const auto& quad : quads_) { box = box.WithPoints(quad.Sxtrema()); } - for (const auto& cubic : _cubics) { + for (const auto& cubic : cubics_) { box = box.WithPoints(cubic.Extrema()); } diff --git a/impeller/impeller/geometry/Path.h b/impeller/impeller/geometry/Path.h index 8d2abcacae008..66839563af1d8 100644 --- a/impeller/impeller/geometry/Path.h +++ b/impeller/impeller/geometry/Path.h @@ -23,56 +23,57 @@ class Path { ~Path(); - size_t componentCount() const; + size_t GetComponentCount() const; - Path& addLinearComponent(Point p1, Point p2); + Path& AddLinearComponent(Point p1, Point p2); - Path& addQuadraticComponent(Point p1, Point cp, Point p2); + Path& AddQuadraticComponent(Point p1, Point cp, Point p2); - Path& addCubicComponent(Point p1, Point cp1, Point cp2, Point p2); + Path& AddCubicComponent(Point p1, Point cp1, Point cp2, Point p2); template using Applier = std::function; - void enumerateComponents(Applier linearApplier, + void EnumerateComponents(Applier linearApplier, Applier quadApplier, Applier cubicApplier) const; - bool linearComponentAtIndex(size_t index, LinearPathComponent& linear) const; + bool GetLinearComponentAtIndex(size_t index, + LinearPathComponent& linear) const; - bool quadraticComponentAtIndex(size_t index, - QuadraticPathComponent& quadratic) const; + bool GetQuadraticComponentAtIndex(size_t index, + QuadraticPathComponent& quadratic) const; - bool cubicComponentAtIndex(size_t index, CubicPathComponent& cubic) const; + bool GetCubicComponentAtIndex(size_t index, CubicPathComponent& cubic) const; - bool updateLinearComponentAtIndex(size_t index, + bool UpdateLinearComponentAtIndex(size_t index, const LinearPathComponent& linear); - bool updateQuadraticComponentAtIndex(size_t index, + bool UpdateQuadraticComponentAtIndex(size_t index, const QuadraticPathComponent& quadratic); - bool updateCubicComponentAtIndex(size_t index, CubicPathComponent& cubic); + bool UpdateCubicComponentAtIndex(size_t index, CubicPathComponent& cubic); using SmoothPointsEnumerator = std::function points)>; - void smoothPoints(SmoothPointsEnumerator enumerator, - const SmoothingApproximation& approximation) const; + void EnumerateSmoothPoints(SmoothPointsEnumerator enumerator, + const SmoothingApproximation& approximation) const; - Rect boundingBox() const; + Rect GetBoundingBox() const; private: struct ComponentIndexPair { - ComponentType type; - size_t index; + ComponentType type = ComponentType::Linear; + size_t index = 0; - ComponentIndexPair() : type(ComponentType::Linear), index(0) {} + ComponentIndexPair() {} ComponentIndexPair(ComponentType aType, size_t aIndex) : type(aType), index(aIndex) {} }; - std::vector _components; - std::vector _linears; - std::vector _quads; - std::vector _cubics; + std::vector components_; + std::vector linears_; + std::vector quads_; + std::vector cubics_; }; } // namespace geom diff --git a/impeller/impeller/geometry/PathBuilder.cc b/impeller/impeller/geometry/PathBuilder.cc index 3f85ef9dce544..f2ad2a9890a5e 100644 --- a/impeller/impeller/geometry/PathBuilder.cc +++ b/impeller/impeller/geometry/PathBuilder.cc @@ -30,7 +30,7 @@ PathBuilder& PathBuilder::Close() { PathBuilder& PathBuilder::LineTo(Point point, bool relative) { point = relative ? current_ + point : point; - prototype_.addLinearComponent(current_, point); + prototype_.AddLinearComponent(current_, point); current_ = point; return *this; } @@ -38,7 +38,7 @@ PathBuilder& PathBuilder::LineTo(Point point, bool relative) { PathBuilder& PathBuilder::HorizontalLineTo(double x, bool relative) { Point endpoint = relative ? Point{current_.x + x, current_.y} : Point{x, current_.y}; - prototype_.addLinearComponent(current_, endpoint); + prototype_.AddLinearComponent(current_, endpoint); current_ = endpoint; return *this; } @@ -46,7 +46,7 @@ PathBuilder& PathBuilder::HorizontalLineTo(double x, bool relative) { PathBuilder& PathBuilder::VerticalLineTo(double y, bool relative) { Point endpoint = relative ? Point{current_.x, current_.y + y} : Point{current_.x, y}; - prototype_.addLinearComponent(current_, endpoint); + prototype_.AddLinearComponent(current_, endpoint); current_ = endpoint; return *this; } @@ -56,7 +56,7 @@ PathBuilder& PathBuilder::QuadraticCurveTo(Point point, bool relative) { point = relative ? current_ + point : point; controlPoint = relative ? current_ + controlPoint : controlPoint; - prototype_.addQuadraticComponent(current_, controlPoint, point); + prototype_.AddQuadraticComponent(current_, controlPoint, point); current_ = point; return *this; } @@ -66,13 +66,13 @@ Point PathBuilder::ReflectedQuadraticControlPoint1() const { * If there is no previous command or if the previous command was not a * quadratic, assume the control point is coincident with the current point. */ - if (prototype_.componentCount() == 0) { + if (prototype_.GetComponentCount() == 0) { return current_; } QuadraticPathComponent quad; - if (!prototype_.quadraticComponentAtIndex(prototype_.componentCount() - 1, - quad)) { + if (!prototype_.GetQuadraticComponentAtIndex( + prototype_.GetComponentCount() - 1, quad)) { return current_; } @@ -100,7 +100,7 @@ PathBuilder& PathBuilder::CubicCurveTo(Point point, controlPoint1 = relative ? current_ + controlPoint1 : controlPoint1; controlPoint2 = relative ? current_ + controlPoint2 : controlPoint2; point = relative ? current_ + point : point; - prototype_.addCubicComponent(current_, controlPoint1, controlPoint2, point); + prototype_.AddCubicComponent(current_, controlPoint1, controlPoint2, point); current_ = point; return *this; } @@ -111,13 +111,13 @@ Point PathBuilder::ReflectedCubicControlPoint1() const { * cubic, assume the first control point is coincident with the current * point. */ - if (prototype_.componentCount() == 0) { + if (prototype_.GetComponentCount() == 0) { return current_; } CubicPathComponent cubic; - if (!prototype_.cubicComponentAtIndex(prototype_.componentCount() - 1, - cubic)) { + if (!prototype_.GetCubicComponentAtIndex(prototype_.GetComponentCount() - 1, + cubic)) { return current_; } @@ -151,10 +151,10 @@ PathBuilder& PathBuilder::AddRect(Rect rect) { auto bottomRight = rect.origin + Point{rect.size.width, rect.size.height}; auto topRight = rect.origin + Point{rect.size.width, 0.0}; - prototype_.addLinearComponent(topLeft, bottomLeft) - .addLinearComponent(bottomLeft, bottomRight) - .addLinearComponent(bottomRight, topRight) - .addLinearComponent(topRight, topLeft); + prototype_.AddLinearComponent(topLeft, bottomLeft) + .AddLinearComponent(bottomLeft, bottomRight) + .AddLinearComponent(bottomRight, topRight) + .AddLinearComponent(topRight, topLeft); return *this; } @@ -165,28 +165,28 @@ PathBuilder& PathBuilder::AddCircle(const Point& center, double radius) { const double diameter = radius * 2.0; const double magic = kArcApproximationMagic * radius; - prototype_.addCubicComponent( + prototype_.AddCubicComponent( {center.x + radius, center.y}, // {center.x + radius + magic, center.y}, // {center.x + diameter, center.y + radius - magic}, // {center.x + diameter, center.y + radius} // ); - prototype_.addCubicComponent( + prototype_.AddCubicComponent( {center.x + diameter, center.y + radius}, // {center.x + diameter, center.y + radius + magic}, // {center.x + radius + magic, center.y + diameter}, // {center.x + radius, center.y + diameter} // ); - prototype_.addCubicComponent( + prototype_.AddCubicComponent( {center.x + radius, center.y + diameter}, // {center.x + radius - magic, center.y + diameter}, // {center.x, center.y + radius + magic}, // {center.x, center.y + radius} // ); - prototype_.addCubicComponent({center.x, center.y + radius}, // + prototype_.AddCubicComponent({center.x, center.y + radius}, // {center.x, center.y + radius - magic}, // {center.x + radius - magic, center.y}, // {center.x + radius, center.y} // @@ -211,14 +211,14 @@ PathBuilder& PathBuilder::AddRoundedRect(Rect rect, RoundingRadii radii) { /* * Top line. */ - prototype_.addLinearComponent( + prototype_.AddLinearComponent( {rect.origin.x + radii.topLeft, rect.origin.y}, {rect.origin.x + rect.size.width - radii.topRight, rect.origin.y}); /* * Top right arc. */ - prototype_.addCubicComponent( + prototype_.AddCubicComponent( {rect.origin.x + rect.size.width - radii.topRight, rect.origin.y}, {rect.origin.x + rect.size.width - radii.topRight + magicTopRight, rect.origin.y}, @@ -229,7 +229,7 @@ PathBuilder& PathBuilder::AddRoundedRect(Rect rect, RoundingRadii radii) { /* * Right line. */ - prototype_.addLinearComponent( + prototype_.AddLinearComponent( {rect.origin.x + rect.size.width, rect.origin.y + radii.topRight}, {rect.origin.x + rect.size.width, rect.origin.y + rect.size.height - radii.bottomRight}); @@ -237,7 +237,7 @@ PathBuilder& PathBuilder::AddRoundedRect(Rect rect, RoundingRadii radii) { /* * Bottom right arc. */ - prototype_.addCubicComponent( + prototype_.AddCubicComponent( {rect.origin.x + rect.size.width, rect.origin.y + rect.size.height - radii.bottomRight}, {rect.origin.x + rect.size.width, @@ -250,7 +250,7 @@ PathBuilder& PathBuilder::AddRoundedRect(Rect rect, RoundingRadii radii) { /* * Bottom line. */ - prototype_.addLinearComponent( + prototype_.AddLinearComponent( {rect.origin.x + rect.size.width - radii.bottomRight, rect.origin.y + rect.size.height}, {rect.origin.x + radii.bottomLeft, rect.origin.y + rect.size.height}); @@ -258,7 +258,7 @@ PathBuilder& PathBuilder::AddRoundedRect(Rect rect, RoundingRadii radii) { /* * Bottom left arc. */ - prototype_.addCubicComponent( + prototype_.AddCubicComponent( {rect.origin.x + radii.bottomLeft, rect.origin.y + rect.size.height}, {rect.origin.x + radii.bottomLeft - magicBottomLeft, rect.origin.y + rect.size.height}, @@ -269,14 +269,14 @@ PathBuilder& PathBuilder::AddRoundedRect(Rect rect, RoundingRadii radii) { /* * Left line. */ - prototype_.addLinearComponent( + prototype_.AddLinearComponent( {rect.origin.x, rect.origin.y + rect.size.height - radii.bottomLeft}, {rect.origin.x, rect.origin.y + radii.topLeft}); /* * Top left arc. */ - prototype_.addCubicComponent( + prototype_.AddCubicComponent( {rect.origin.x, rect.origin.y + radii.topLeft}, {rect.origin.x, rect.origin.y + radii.topLeft - magicTopLeft}, {rect.origin.x + radii.topLeft - magicTopLeft, rect.origin.y}, @@ -292,28 +292,28 @@ PathBuilder& PathBuilder::AddEllipse(const Point& center, const Size& radius) { const Size magic = {kArcApproximationMagic * radius.width, kArcApproximationMagic * radius.height}; - prototype_.addCubicComponent( + prototype_.AddCubicComponent( {center.x + radius.width, center.y}, // {center.x + radius.width + magic.width, center.y}, // {center.x + diameter.width, center.y + radius.height - magic.height}, // {center.x + diameter.width, center.y + radius.height} // ); - prototype_.addCubicComponent( + prototype_.AddCubicComponent( {center.x + diameter.width, center.y + radius.height}, // {center.x + diameter.width, center.y + radius.height + magic.height}, // {center.x + radius.width + magic.width, center.y + diameter.height}, // {center.x + radius.width, center.y + diameter.height} // ); - prototype_.addCubicComponent( + prototype_.AddCubicComponent( {center.x + radius.width, center.y + diameter.height}, // {center.x + radius.width - magic.width, center.y + diameter.height}, // {center.x, center.y + radius.height + magic.height}, // {center.x, center.y + radius.height} // ); - prototype_.addCubicComponent( + prototype_.AddCubicComponent( {center.x, center.y + radius.height}, // {center.x, center.y + radius.height - magic.height}, // {center.x + radius.width - magic.width, center.y}, // From 58ccbe7bab6a0b395f73b5265092ad8a56c67f5d Mon Sep 17 00:00:00 2001 From: Chinmay Garde Date: Wed, 5 May 2021 21:07:48 -0700 Subject: [PATCH 018/433] More cleanups. --- impeller/impeller/geometry/Shear.h | 8 ++++---- impeller/impeller/geometry/Size.h | 6 +++--- 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/impeller/impeller/geometry/Shear.h b/impeller/impeller/geometry/Shear.h index a3ba44ea65a2b..8f785a296946d 100644 --- a/impeller/impeller/geometry/Shear.h +++ b/impeller/impeller/geometry/Shear.h @@ -12,14 +12,14 @@ namespace geom { struct Shear { union { struct { - double xy; - double xz; - double yz; + double xy = 0.0; + double xz = 0.0; + double yz = 0.0; }; double e[3]; }; - Shear() : xy(0.0), xz(0.0), yz(0.0) {} + Shear() {} Shear(double xy, double xz, double yz) : xy(xy), xz(xz), yz(yz) {} diff --git a/impeller/impeller/geometry/Size.h b/impeller/impeller/geometry/Size.h index 62fa7074cf934..8951c7a028d16 100644 --- a/impeller/impeller/geometry/Size.h +++ b/impeller/impeller/geometry/Size.h @@ -10,10 +10,10 @@ namespace rl { namespace geom { struct Size { - double width; - double height; + double width = 0.0; + double height = 0.0; - Size() : width(0.0), height(0.0) {} + Size() {} Size(double width, double height) : width(width), height(height) {} From b4664288b2f4adc2617e49ef1dfc7254b4b9c42a Mon Sep 17 00:00:00 2001 From: Chinmay Garde Date: Wed, 5 May 2021 21:31:20 -0700 Subject: [PATCH 019/433] Patch namespace. --- impeller/impeller/entity/Color.cc | 12 ++--- impeller/impeller/entity/Color.h | 10 ++-- impeller/impeller/entity/Entity.cc | 51 ++++++++++----------- impeller/impeller/entity/Entity.h | 42 ++++++++--------- impeller/impeller/geometry/Matrix.cc | 6 +-- impeller/impeller/geometry/Matrix.h | 6 +-- impeller/impeller/geometry/Path.cc | 6 +-- impeller/impeller/geometry/Path.h | 7 ++- impeller/impeller/geometry/PathBuilder.cc | 6 +-- impeller/impeller/geometry/PathBuilder.h | 17 +++---- impeller/impeller/geometry/PathComponent.cc | 6 +-- impeller/impeller/geometry/PathComponent.h | 6 +-- impeller/impeller/geometry/Point.cc | 6 +-- impeller/impeller/geometry/Point.h | 6 +-- impeller/impeller/geometry/Quaternion.cc | 6 +-- impeller/impeller/geometry/Quaternion.h | 6 +-- impeller/impeller/geometry/Rect.cc | 6 +-- impeller/impeller/geometry/Rect.h | 6 +-- impeller/impeller/geometry/Shear.cc | 6 +-- impeller/impeller/geometry/Shear.h | 6 +-- impeller/impeller/geometry/Size.cc | 6 +-- impeller/impeller/geometry/Size.h | 6 +-- impeller/impeller/geometry/Vector.cc | 6 +-- impeller/impeller/geometry/Vector.h | 6 +-- impeller/impeller/image/Image.cc | 13 ++---- impeller/impeller/image/Image.h | 6 +-- impeller/impeller/image/ImageResult.cc | 10 ++-- impeller/impeller/image/ImageResult.h | 12 ++--- 28 files changed, 115 insertions(+), 173 deletions(-) diff --git a/impeller/impeller/entity/Color.cc b/impeller/impeller/entity/Color.cc index 175a327a3063f..ebc908ae0d744 100644 --- a/impeller/impeller/entity/Color.cc +++ b/impeller/impeller/entity/Color.cc @@ -7,8 +7,7 @@ #include #include -namespace rl { -namespace entity { +namespace impeller { ColorHSB ColorHSB::FromRGB(Color rgb) { double R = rgb.red; @@ -37,7 +36,7 @@ ColorHSB ColorHSB::FromRGB(Color rgb) { return ColorHSB(((i - f / (v - x)) / 6.0), (v - x) / v, v, rgb.alpha); } -Color ColorHSB::toRGBA() const { +Color ColorHSB::ToRGBA() const { double h = hue * 6.0; double s = saturation; double v = brightness; @@ -85,14 +84,14 @@ Color ColorHSB::toRGBA() const { return Color(0, 0, 0, alpha); } -std::string ColorHSB::toString() const { +std::string ColorHSB::ToString() const { std::stringstream stream; stream << "{" << hue << ", " << saturation << ", " << brightness << ", " << alpha << "}"; return stream.str(); } -Color::Color(const ColorHSB& hsbColor) : Color(hsbColor.toRGBA()) {} +Color::Color(const ColorHSB& hsbColor) : Color(hsbColor.ToRGBA()) {} std::string Color::ToString() const { std::stringstream stream; @@ -111,5 +110,4 @@ void Color::FromString(const std::string& str) { stream >> alpha; } -} // namespace entity -} // namespace rl +} // namespace impeller diff --git a/impeller/impeller/entity/Color.h b/impeller/impeller/entity/Color.h index ab53f60f151e2..1d7372feb089e 100644 --- a/impeller/impeller/entity/Color.h +++ b/impeller/impeller/entity/Color.h @@ -7,8 +7,7 @@ #include #include -namespace rl { -namespace entity { +namespace impeller { struct ColorHSB; @@ -662,10 +661,9 @@ struct ColorHSB { static ColorHSB FromRGB(Color rgb); - Color toRGBA() const; + Color ToRGBA() const; - std::string toString() const; + std::string ToString() const; }; -} // namespace entity -} // namespace rl +} // namespace impeller diff --git a/impeller/impeller/entity/Entity.cc b/impeller/impeller/entity/Entity.cc index bebb91578739a..968f178ac7f30 100644 --- a/impeller/impeller/entity/Entity.cc +++ b/impeller/impeller/entity/Entity.cc @@ -4,65 +4,63 @@ #include "Entity.h" -namespace rl { -namespace entity { +namespace impeller { Entity::Entity() = default; Entity::~Entity() = default; -geom::Rect Entity::GetFrame() const { - geom::Point origin(position_.x - (bounds_.size.width * anchor_point_.x), - position_.y - (bounds_.size.height * anchor_point_.y)); +Rect Entity::GetFrame() const { + Point origin(position_.x - (bounds_.size.width * anchor_point_.x), + position_.y - (bounds_.size.height * anchor_point_.y)); - return geom::Rect(origin, bounds_.size); + return Rect(origin, bounds_.size); } -void Entity::SetFrame(const geom::Rect& frame) { - SetBounds(geom::Rect(bounds_.origin, frame.size)); - SetPosition( - geom::Point(frame.origin.x + (anchor_point_.x * frame.size.width), - frame.origin.y + (anchor_point_.y * frame.size.height))); +void Entity::SetFrame(const Rect& frame) { + SetBounds(Rect(bounds_.origin, frame.size)); + SetPosition(Point(frame.origin.x + (anchor_point_.x * frame.size.width), + frame.origin.y + (anchor_point_.y * frame.size.height))); } -const geom::Rect& Entity::GetBounds() const { +const Rect& Entity::GetBounds() const { return bounds_; } -void Entity::SetBounds(const geom::Rect& bounds) { +void Entity::SetBounds(const Rect& bounds) { bounds_ = bounds; } -const geom::Point& Entity::GetPosition() const { +const Point& Entity::GetPosition() const { return position_; } -void Entity::SetPosition(const geom::Point& position) { +void Entity::SetPosition(const Point& position) { position_ = position; } -const geom::Point& Entity::GetAnchorPoint() const { +const Point& Entity::GetAnchorPoint() const { return anchor_point_; } -void Entity::SetAnchorPoint(const geom::Point& anchorPoint) { +void Entity::SetAnchorPoint(const Point& anchorPoint) { anchor_point_ = anchorPoint; } -const geom::Matrix& Entity::GetTransformation() const { +const Matrix& Entity::GetTransformation() const { return transformation_; } -void Entity::SetTransformation(const geom::Matrix& transformation) { +void Entity::SetTransformation(const Matrix& transformation) { transformation_ = transformation; } -geom::Matrix Entity::GetModelMatrix() const { +Matrix Entity::GetModelMatrix() const { /* * The translation accounts for the offset in the origin of the bounds * of the entity and its position about its anchor point. */ - auto translation = geom::Matrix::MakeTranslation( + auto translation = Matrix::MakeTranslation( {-bounds_.origin.x + position_.x - (bounds_.size.width * anchor_point_.x), -bounds_.origin.y + position_.y - (bounds_.size.height * anchor_point_.y)}); @@ -78,8 +76,8 @@ geom::Matrix Entity::GetModelMatrix() const { } auto anchorAdjustment = - geom::Matrix::MakeTranslation({-anchor_point_.x * bounds_.size.width, - -anchor_point_.y * bounds_.size.height}); + Matrix::MakeTranslation({-anchor_point_.x * bounds_.size.width, + -anchor_point_.y * bounds_.size.height}); return translation * anchorAdjustment.Invert() * transformation_ * anchorAdjustment; @@ -117,13 +115,12 @@ void Entity::SetStrokeSize(double strokeSize) { stroke_size_ = strokeSize; } -const geom::Path& Entity::GetPath() const { +const Path& Entity::GetPath() const { return path_; } -void Entity::SetPath(geom::Path path) { +void Entity::SetPath(Path path) { path_ = std::move(path); } -} // namespace entity -} // namespace rl +} // namespace impeller diff --git a/impeller/impeller/entity/Entity.h b/impeller/impeller/entity/Entity.h index 4df2150a24f5c..0eb64a429f789 100644 --- a/impeller/impeller/entity/Entity.h +++ b/impeller/impeller/entity/Entity.h @@ -10,8 +10,7 @@ #include "Path.h" #include "Rect.h" -namespace rl { -namespace entity { +namespace impeller { class Entity { public: @@ -26,14 +25,14 @@ class Entity { * * @return the frame of the entity */ - geom::Rect GetFrame() const; + Rect GetFrame() const; /** * Set the frame of the entity * * @param frame the new frame */ - void SetFrame(const geom::Rect& frame); + void SetFrame(const Rect& frame); /** * The bounds specifies the origin and size of the entity in its own @@ -41,14 +40,14 @@ class Entity { * * @return the bounds of the entity */ - const geom::Rect& GetBounds() const; + const Rect& GetBounds() const; /** * Set the bounds of the entity * * @param bounds the new bounds */ - void SetBounds(const geom::Rect& bounds); + void SetBounds(const Rect& bounds); /** * The position specifies the coordinates of the anchor position of the @@ -56,49 +55,49 @@ class Entity { * * @return the position of the entity */ - const geom::Point& GetPosition() const; + const Point& GetPosition() const; /** * Sets the position of the entity * * @param point the new position */ - void SetPosition(const geom::Point& point); + void SetPosition(const Point& point); /** * The position of the anchor point within this node in unit space * * @return the anchor point */ - const geom::Point& GetAnchorPoint() const; + const Point& GetAnchorPoint() const; /** * Sets the new anchor point of this node * * @param anchorPoint the new anchor point */ - void SetAnchorPoint(const geom::Point& anchorPoint); + void SetAnchorPoint(const Point& anchorPoint); /** * The transformation that is applied to the entity about its anchor point * * @return the transformation applied to the node */ - const geom::Matrix& GetTransformation() const; + const Matrix& GetTransformation() const; /** * Sets the transformation of the entity * * @param transformation the new transformation */ - void SetTransformation(const geom::Matrix& transformation); + void SetTransformation(const Matrix& transformation); /** * The model matrix of the entity * * @return the view matrix */ - geom::Matrix GetModelMatrix() const; + Matrix GetModelMatrix() const; /** * The background color of the entity @@ -137,18 +136,18 @@ class Entity { void SetStrokeSize(double strokeSize); - const geom::Path& GetPath() const; + const Path& GetPath() const; - void SetPath(geom::Path path); + void SetPath(Path path); private: - geom::Rect bounds_; - geom::Point position_; - geom::Point anchor_point_ = {0.5, 0.5}; - geom::Matrix transformation_; + Rect bounds_; + Point position_; + Point anchor_point_ = {0.5, 0.5}; + Matrix transformation_; Color background_color_; - geom::Path path_; + Path path_; double opacity_ = 1.0; Color stroke_color_ = Color::Black(); double stroke_size_ = 1.0; @@ -156,5 +155,4 @@ class Entity { FML_DISALLOW_COPY_AND_ASSIGN(Entity); }; -} // namespace entity -} // namespace rl +} // namespace impeller diff --git a/impeller/impeller/geometry/Matrix.cc b/impeller/impeller/geometry/Matrix.cc index 95294b77f67ad..648b9ce40af11 100644 --- a/impeller/impeller/geometry/Matrix.cc +++ b/impeller/impeller/geometry/Matrix.cc @@ -6,8 +6,7 @@ #include #include -namespace rl { -namespace geom { +namespace impeller { Matrix::Matrix(const Decomposition& d) : Matrix() { /* @@ -480,5 +479,4 @@ std::string Matrix::Decomposition::ToString() const { return stream.str(); } -} // namespace geom -} // namespace rl +} // namespace impeller diff --git a/impeller/impeller/geometry/Matrix.h b/impeller/impeller/geometry/Matrix.h index 8fb236373b5e9..c2a7869ffe735 100644 --- a/impeller/impeller/geometry/Matrix.h +++ b/impeller/impeller/geometry/Matrix.h @@ -13,8 +13,7 @@ #include "Size.h" #include "Vector.h" -namespace rl { -namespace geom { +namespace impeller { struct Matrix { union { @@ -295,5 +294,4 @@ inline Vector4 operator*(const Vector4& v, const Matrix& m) { v.x * m.m[3] + v.y * m.m[7] + v.z * m.m[11] + v.w * m.m[15]); } -} // namespace geom -} // namespace rl +} // namespace impeller diff --git a/impeller/impeller/geometry/Path.cc b/impeller/impeller/geometry/Path.cc index f5ac80ecd6ac7..750435311d878 100644 --- a/impeller/impeller/geometry/Path.cc +++ b/impeller/impeller/geometry/Path.cc @@ -4,8 +4,7 @@ #include "Path.h" -namespace rl { -namespace geom { +namespace impeller { Path::Path() = default; @@ -191,5 +190,4 @@ Rect Path::GetBoundingBox() const { return box; } -} // namespace geom -} // namespace rl +} // namespace impeller diff --git a/impeller/impeller/geometry/Path.h b/impeller/impeller/geometry/Path.h index 66839563af1d8..1c25776bb1747 100644 --- a/impeller/impeller/geometry/Path.h +++ b/impeller/impeller/geometry/Path.h @@ -6,10 +6,10 @@ #include #include + #include "PathComponent.h" -namespace rl { -namespace geom { +namespace impeller { class Path { public: @@ -76,5 +76,4 @@ class Path { std::vector cubics_; }; -} // namespace geom -} // namespace rl +} // namespace impeller diff --git a/impeller/impeller/geometry/PathBuilder.cc b/impeller/impeller/geometry/PathBuilder.cc index f2ad2a9890a5e..8c429478ca51b 100644 --- a/impeller/impeller/geometry/PathBuilder.cc +++ b/impeller/impeller/geometry/PathBuilder.cc @@ -4,8 +4,7 @@ #include "PathBuilder.h" -namespace rl { -namespace geom { +namespace impeller { static const double kArcApproximationMagic = 0.551915024494; @@ -323,5 +322,4 @@ PathBuilder& PathBuilder::AddEllipse(const Point& center, const Size& radius) { return *this; } -} // namespace geom -} // namespace rl +} // namespace impeller diff --git a/impeller/impeller/geometry/PathBuilder.h b/impeller/impeller/geometry/PathBuilder.h index 504a6d5275992..9ae4929ee32fb 100644 --- a/impeller/impeller/geometry/PathBuilder.h +++ b/impeller/impeller/geometry/PathBuilder.h @@ -8,8 +8,7 @@ #include "Rect.h" #include "flutter/fml/macros.h" -namespace rl { -namespace geom { +namespace impeller { class PathBuilder { public: @@ -53,13 +52,12 @@ class PathBuilder { PathBuilder& AddEllipse(const Point& center, const Size& size); struct RoundingRadii { - double topLeft; - double bottomLeft; - double topRight; - double bottomRight; + double topLeft = 0.0; + double bottomLeft = 0.0; + double topRight = 0.0; + double bottomRight = 0.0; - RoundingRadii() - : topLeft(0.0), bottomLeft(0.0), topRight(0.0), bottomRight(0.0) {} + RoundingRadii() {} RoundingRadii(double pTopLeft, double pBottomLeft, @@ -85,5 +83,4 @@ class PathBuilder { FML_DISALLOW_COPY_AND_ASSIGN(PathBuilder); }; -} // namespace geom -} // namespace rl +} // namespace impeller diff --git a/impeller/impeller/geometry/PathComponent.cc b/impeller/impeller/geometry/PathComponent.cc index 990f8c73b985a..270299ecec40f 100644 --- a/impeller/impeller/geometry/PathComponent.cc +++ b/impeller/impeller/geometry/PathComponent.cc @@ -5,8 +5,7 @@ #include "PathComponent.h" #include -namespace rl { -namespace geom { +namespace impeller { static const size_t kRecursionLimit = 32; static const double kCurveCollinearityEpsilon = 1e-30; @@ -416,5 +415,4 @@ std::vector CubicPathComponent::Extrema() const { return points; } -} // namespace geom -} // namespace rl +} // namespace impeller diff --git a/impeller/impeller/geometry/PathComponent.h b/impeller/impeller/geometry/PathComponent.h index 139adf0ea56ed..87e9ffe044060 100644 --- a/impeller/impeller/geometry/PathComponent.h +++ b/impeller/impeller/geometry/PathComponent.h @@ -8,8 +8,7 @@ #include "Rect.h" -namespace rl { -namespace geom { +namespace impeller { struct SmoothingApproximation { const double scale; @@ -106,5 +105,4 @@ struct CubicPathComponent { } }; -} // namespace geom -} // namespace rl +} // namespace impeller diff --git a/impeller/impeller/geometry/Point.cc b/impeller/impeller/geometry/Point.cc index 72a0b9db4574d..e1f9e31ab485f 100644 --- a/impeller/impeller/geometry/Point.cc +++ b/impeller/impeller/geometry/Point.cc @@ -5,8 +5,7 @@ #include "Point.h" #include -namespace rl { -namespace geom { +namespace impeller { std::string Point::ToString() const { std::stringstream stream; @@ -31,5 +30,4 @@ double Point::GetDistance(const Point& p) const { return sqrt(GetDistanceSquared(p)); } -} // namespace geom -} // namespace rl +} // namespace impeller diff --git a/impeller/impeller/geometry/Point.h b/impeller/impeller/geometry/Point.h index a099d373aa84e..6ce9ca1da13f8 100644 --- a/impeller/impeller/geometry/Point.h +++ b/impeller/impeller/geometry/Point.h @@ -8,8 +8,7 @@ #include #include "Size.h" -namespace rl { -namespace geom { +namespace impeller { struct Point { double x = 0.0; @@ -57,5 +56,4 @@ struct Point { void FromString(const std::string& str); }; -} // namespace geom -} // namespace rl +} // namespace impeller diff --git a/impeller/impeller/geometry/Quaternion.cc b/impeller/impeller/geometry/Quaternion.cc index 34303396051d4..5cfe6c62ca4dc 100644 --- a/impeller/impeller/geometry/Quaternion.cc +++ b/impeller/impeller/geometry/Quaternion.cc @@ -5,8 +5,7 @@ #include "Quaternion.h" #include -namespace rl { -namespace geom { +namespace impeller { Quaternion Quaternion::Slerp(const Quaternion& to, double time) const { double cosine = Dot(to); @@ -35,5 +34,4 @@ std::string Quaternion::ToString() const { return stream.str(); } -} // namespace geom -} // namespace rl +} // namespace impeller diff --git a/impeller/impeller/geometry/Quaternion.h b/impeller/impeller/geometry/Quaternion.h index 07507b8eb3a55..528c93ee38610 100644 --- a/impeller/impeller/geometry/Quaternion.h +++ b/impeller/impeller/geometry/Quaternion.h @@ -6,8 +6,7 @@ #include "Vector.h" -namespace rl { -namespace geom { +namespace impeller { struct Quaternion { union { @@ -78,5 +77,4 @@ struct Quaternion { std::string ToString() const; }; -} // namespace geom -} // namespace rl +} // namespace impeller diff --git a/impeller/impeller/geometry/Rect.cc b/impeller/impeller/geometry/Rect.cc index cfcc4d9296e5e..83f17f9efba68 100644 --- a/impeller/impeller/geometry/Rect.cc +++ b/impeller/impeller/geometry/Rect.cc @@ -5,8 +5,7 @@ #include "Rect.h" #include -namespace rl { -namespace geom { +namespace impeller { Rect Rect::WithPoint(const Point& p) const { Rect copy = *this; @@ -57,5 +56,4 @@ void Rect::FromString(const std::string& str) { stream >> size.height; } -} // namespace geom -} // namespace rl +} // namespace impeller diff --git a/impeller/impeller/geometry/Rect.h b/impeller/impeller/geometry/Rect.h index 04223c47a04b5..f3d39976b20f0 100644 --- a/impeller/impeller/geometry/Rect.h +++ b/impeller/impeller/geometry/Rect.h @@ -8,8 +8,7 @@ #include "Point.h" #include "Size.h" -namespace rl { -namespace geom { +namespace impeller { struct Rect { Point origin; @@ -71,5 +70,4 @@ struct Rect { void FromString(const std::string& str); }; -} // namespace geom -} // namespace rl +} // namespace impeller diff --git a/impeller/impeller/geometry/Shear.cc b/impeller/impeller/geometry/Shear.cc index ed77e6f841938..fb15e47935c71 100644 --- a/impeller/impeller/geometry/Shear.cc +++ b/impeller/impeller/geometry/Shear.cc @@ -5,8 +5,7 @@ #include "Shear.h" #include -namespace rl { -namespace geom { +namespace impeller { std::string Shear::ToString() const { std::stringstream stream; @@ -14,5 +13,4 @@ std::string Shear::ToString() const { return stream.str(); } -} // namespace geom -} // namespace rl +} // namespace impeller diff --git a/impeller/impeller/geometry/Shear.h b/impeller/impeller/geometry/Shear.h index 8f785a296946d..220342a8b6538 100644 --- a/impeller/impeller/geometry/Shear.h +++ b/impeller/impeller/geometry/Shear.h @@ -6,8 +6,7 @@ #include -namespace rl { -namespace geom { +namespace impeller { struct Shear { union { @@ -32,5 +31,4 @@ struct Shear { std::string ToString() const; }; -} // namespace geom -} // namespace rl +} // namespace impeller diff --git a/impeller/impeller/geometry/Size.cc b/impeller/impeller/geometry/Size.cc index 91ea783ed5830..0d151a8f522ff 100644 --- a/impeller/impeller/geometry/Size.cc +++ b/impeller/impeller/geometry/Size.cc @@ -5,8 +5,7 @@ #include "Size.h" #include -namespace rl { -namespace geom { +namespace impeller { std::string Size::ToString() const { std::stringstream stream; @@ -21,5 +20,4 @@ void Size::FromString(const std::string& str) { stream >> height; } -} // namespace geom -} // namespace rl +} // namespace impeller diff --git a/impeller/impeller/geometry/Size.h b/impeller/impeller/geometry/Size.h index 8951c7a028d16..25faa9600e73a 100644 --- a/impeller/impeller/geometry/Size.h +++ b/impeller/impeller/geometry/Size.h @@ -6,8 +6,7 @@ #include -namespace rl { -namespace geom { +namespace impeller { struct Size { double width = 0.0; @@ -54,5 +53,4 @@ struct Size { void FromString(const std::string& str); }; -} // namespace geom -} // namespace rl +} // namespace impeller diff --git a/impeller/impeller/geometry/Vector.cc b/impeller/impeller/geometry/Vector.cc index 4e113b24746b4..4765bef6cc926 100644 --- a/impeller/impeller/geometry/Vector.cc +++ b/impeller/impeller/geometry/Vector.cc @@ -5,8 +5,7 @@ #include "Vector.h" #include -namespace rl { -namespace geom { +namespace impeller { std::string Vector3::ToString() const { std::stringstream stream; @@ -20,5 +19,4 @@ std::string Vector4::ToString() const { return stream.str(); } -} // namespace geom -} // namespace rl +} // namespace impeller diff --git a/impeller/impeller/geometry/Vector.h b/impeller/impeller/geometry/Vector.h index 4abbd7088ebb7..d5713498da8a2 100644 --- a/impeller/impeller/geometry/Vector.h +++ b/impeller/impeller/geometry/Vector.h @@ -9,8 +9,7 @@ #include "Point.h" #include "Size.h" -namespace rl { -namespace geom { +namespace impeller { struct Vector3 { union { @@ -141,5 +140,4 @@ struct Vector4 { std::string ToString() const; }; -} // namespace geom -} // namespace rl +} // namespace impeller diff --git a/impeller/impeller/image/Image.cc b/impeller/impeller/image/Image.cc index c9fa5d80a3aad..cd3aac1a18676 100644 --- a/impeller/impeller/image/Image.cc +++ b/impeller/impeller/image/Image.cc @@ -5,8 +5,7 @@ #include "Image.h" #include -namespace rl { -namespace image { +namespace impeller { Image::Image(std::shared_ptr sourceAllocation) : source_(std::move(sourceAllocation)) {} @@ -72,10 +71,9 @@ ImageResult Image::Decode() const { } return ImageResult{ - geom::Size{static_cast(width), - static_cast(height)}, // size - components, // components - std::move(destinationAllocation) // allocation + Size{static_cast(width), static_cast(height)}, // size + components, // components + std::move(destinationAllocation) // allocation }; } @@ -83,5 +81,4 @@ bool Image::IsValid() const { return static_cast(source_); } -} // namespace image -} // namespace rl +} // namespace impeller diff --git a/impeller/impeller/image/Image.h b/impeller/impeller/image/Image.h index b51ad8dae6a99..aa533f3a6271d 100644 --- a/impeller/impeller/image/Image.h +++ b/impeller/impeller/image/Image.h @@ -9,8 +9,7 @@ #include "flutter/fml/macros.h" #include "flutter/fml/mapping.h" -namespace rl { -namespace image { +namespace impeller { class ImageSource; @@ -28,5 +27,4 @@ class Image { std::shared_ptr source_; }; -} // namespace image -} // namespace rl +} // namespace impeller diff --git a/impeller/impeller/image/ImageResult.cc b/impeller/impeller/image/ImageResult.cc index f68e2107dd5aa..4b789e4f99830 100644 --- a/impeller/impeller/image/ImageResult.cc +++ b/impeller/impeller/image/ImageResult.cc @@ -4,12 +4,11 @@ #include "ImageResult.h" -namespace rl { -namespace image { +namespace impeller { ImageResult::ImageResult() = default; -ImageResult::ImageResult(geom::Size size, +ImageResult::ImageResult(Size size, Components components, std::shared_ptr allocation) : success_(true), @@ -23,7 +22,7 @@ bool ImageResult::WasSuccessful() const { return success_; } -const geom::Size& ImageResult::GetSize() const { +const Size& ImageResult::GetSize() const { return size_; } @@ -35,5 +34,4 @@ const std::shared_ptr& ImageResult::Allocation() const { return allocation_; } -} // namespace image -} // namespace rl +} // namespace impeller diff --git a/impeller/impeller/image/ImageResult.h b/impeller/impeller/image/ImageResult.h index 624f67507e5e4..0e9985344aa07 100644 --- a/impeller/impeller/image/ImageResult.h +++ b/impeller/impeller/image/ImageResult.h @@ -10,8 +10,7 @@ #include "flutter/fml/macros.h" #include "flutter/fml/mapping.h" -namespace rl { -namespace image { +namespace impeller { class ImageResult { public: @@ -25,13 +24,13 @@ class ImageResult { ImageResult(); - ImageResult(geom::Size size, + ImageResult(Size size, Components components, std::shared_ptr allocation); ~ImageResult(); - const geom::Size& GetSize() const; + const Size& GetSize() const; bool WasSuccessful() const; @@ -41,12 +40,11 @@ class ImageResult { private: bool success_ = false; - geom::Size size_; + Size size_; Components components_ = Components::Invalid; std::shared_ptr allocation_; FML_DISALLOW_COPY_AND_ASSIGN(ImageResult); }; -} // namespace image -} // namespace rl +} // namespace impeller From 8653284e09096553d372ff4b459ed32f8b6252e6 Mon Sep 17 00:00:00 2001 From: Chinmay Garde Date: Wed, 5 May 2021 21:54:28 -0700 Subject: [PATCH 020/433] Fix file name casing. --- impeller/impeller/BUILD.gn | 56 +++++++++---------- .../impeller/entity/{Color.cc => color.cc} | 2 +- impeller/impeller/entity/{Color.h => color.h} | 0 .../impeller/entity/{Entity.cc => entity.cc} | 2 +- .../impeller/entity/{Entity.h => entity.h} | 10 ++-- .../geometry/{Matrix.cc => matrix.cc} | 2 +- .../impeller/geometry/{Matrix.h => matrix.h} | 10 ++-- .../impeller/geometry/{Path.cc => path.cc} | 2 +- impeller/impeller/geometry/{Path.h => path.h} | 2 +- .../{PathBuilder.cc => path_builder.cc} | 2 +- .../{PathBuilder.h => path_builder.h} | 4 +- .../{PathComponent.cc => path_component.cc} | 2 +- .../{PathComponent.h => path_component.h} | 2 +- .../impeller/geometry/{Point.cc => point.cc} | 2 +- .../impeller/geometry/{Point.h => point.h} | 2 +- .../geometry/{Quaternion.cc => quaternion.cc} | 2 +- .../geometry/{Quaternion.h => quaternion.h} | 2 +- .../impeller/geometry/{Rect.cc => rect.cc} | 2 +- impeller/impeller/geometry/{Rect.h => rect.h} | 4 +- .../impeller/geometry/{Shear.cc => shear.cc} | 2 +- .../impeller/geometry/{Shear.h => shear.h} | 0 .../impeller/geometry/{Size.cc => size.cc} | 2 +- impeller/impeller/geometry/{Size.h => size.h} | 0 .../geometry/{Vector.cc => vector.cc} | 2 +- .../impeller/geometry/{Vector.h => vector.h} | 4 +- .../impeller/image/{Image.cc => image.cc} | 2 +- impeller/impeller/image/{Image.h => image.h} | 4 +- .../image/{ImageResult.cc => image_result.cc} | 2 +- .../image/{ImageResult.h => image_result.h} | 2 +- 29 files changed, 65 insertions(+), 65 deletions(-) rename impeller/impeller/entity/{Color.cc => color.cc} (99%) rename impeller/impeller/entity/{Color.h => color.h} (100%) rename impeller/impeller/entity/{Entity.cc => entity.cc} (99%) rename impeller/impeller/entity/{Entity.h => entity.h} (97%) rename impeller/impeller/geometry/{Matrix.cc => matrix.cc} (99%) rename impeller/impeller/geometry/{Matrix.h => matrix.h} (98%) rename impeller/impeller/geometry/{Path.cc => path.cc} (99%) rename impeller/impeller/geometry/{Path.h => path.h} (98%) rename impeller/impeller/geometry/{PathBuilder.cc => path_builder.cc} (99%) rename impeller/impeller/geometry/{PathBuilder.h => path_builder.h} (98%) rename impeller/impeller/geometry/{PathComponent.cc => path_component.cc} (99%) rename impeller/impeller/geometry/{PathComponent.h => path_component.h} (99%) rename impeller/impeller/geometry/{Point.cc => point.cc} (97%) rename impeller/impeller/geometry/{Point.h => point.h} (98%) rename impeller/impeller/geometry/{Quaternion.cc => quaternion.cc} (97%) rename impeller/impeller/geometry/{Quaternion.h => quaternion.h} (98%) rename impeller/impeller/geometry/{Rect.cc => rect.cc} (98%) rename impeller/impeller/geometry/{Rect.h => rect.h} (98%) rename impeller/impeller/geometry/{Shear.cc => shear.cc} (95%) rename impeller/impeller/geometry/{Shear.h => shear.h} (100%) rename impeller/impeller/geometry/{Size.cc => size.cc} (96%) rename impeller/impeller/geometry/{Size.h => size.h} (100%) rename impeller/impeller/geometry/{Vector.cc => vector.cc} (96%) rename impeller/impeller/geometry/{Vector.h => vector.h} (98%) rename impeller/impeller/image/{Image.cc => image.cc} (99%) rename impeller/impeller/image/{Image.h => image.h} (92%) rename impeller/impeller/image/{ImageResult.cc => image_result.cc} (97%) rename impeller/impeller/image/{ImageResult.h => image_result.h} (98%) diff --git a/impeller/impeller/BUILD.gn b/impeller/impeller/BUILD.gn index 306f295025d78..4932aea7dcb53 100644 --- a/impeller/impeller/BUILD.gn +++ b/impeller/impeller/BUILD.gn @@ -15,40 +15,40 @@ source_set("impeller") { ] geometry_sources = [ - "geometry/Matrix.cc", - "geometry/Matrix.h", - "geometry/Path.cc", - "geometry/Path.h", - "geometry/PathBuilder.cc", - "geometry/PathBuilder.h", - "geometry/PathComponent.cc", - "geometry/PathComponent.h", - "geometry/Point.cc", - "geometry/Point.h", - "geometry/Quaternion.cc", - "geometry/Quaternion.h", - "geometry/Rect.cc", - "geometry/Rect.h", - "geometry/Shear.cc", - "geometry/Shear.h", - "geometry/Size.cc", - "geometry/Size.h", - "geometry/Vector.cc", - "geometry/Vector.h", + "geometry/matrix.cc", + "geometry/matrix.h", + "geometry/path.cc", + "geometry/path.h", + "geometry/path_builder.cc", + "geometry/path_builder.h", + "geometry/path_component.cc", + "geometry/path_component.h", + "geometry/point.cc", + "geometry/point.h", + "geometry/quaternion.cc", + "geometry/quaternion.h", + "geometry/rect.cc", + "geometry/rect.h", + "geometry/shear.cc", + "geometry/shear.h", + "geometry/size.cc", + "geometry/size.h", + "geometry/vector.cc", + "geometry/vector.h", ] entity_sources = [ - "entity/Color.cc", - "entity/Color.h", - "entity/Entity.cc", - "entity/Entity.h", + "entity/color.cc", + "entity/color.h", + "entity/entity.cc", + "entity/entity.h", ] image_sources = [ - "image/Image.cc", - "image/Image.h", - "image/ImageResult.cc", - "image/ImageResult.h", + "image/image.cc", + "image/image.h", + "image/image_result.cc", + "image/image_result.h", ] sources = geometry_sources + entity_sources + image_sources diff --git a/impeller/impeller/entity/Color.cc b/impeller/impeller/entity/color.cc similarity index 99% rename from impeller/impeller/entity/Color.cc rename to impeller/impeller/entity/color.cc index ebc908ae0d744..9b36f24977ce0 100644 --- a/impeller/impeller/entity/Color.cc +++ b/impeller/impeller/entity/color.cc @@ -2,7 +2,7 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -#include "Color.h" +#include "color.h" #include #include #include diff --git a/impeller/impeller/entity/Color.h b/impeller/impeller/entity/color.h similarity index 100% rename from impeller/impeller/entity/Color.h rename to impeller/impeller/entity/color.h diff --git a/impeller/impeller/entity/Entity.cc b/impeller/impeller/entity/entity.cc similarity index 99% rename from impeller/impeller/entity/Entity.cc rename to impeller/impeller/entity/entity.cc index 968f178ac7f30..9f26fa047dab3 100644 --- a/impeller/impeller/entity/Entity.cc +++ b/impeller/impeller/entity/entity.cc @@ -2,7 +2,7 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -#include "Entity.h" +#include "entity.h" namespace impeller { diff --git a/impeller/impeller/entity/Entity.h b/impeller/impeller/entity/entity.h similarity index 97% rename from impeller/impeller/entity/Entity.h rename to impeller/impeller/entity/entity.h index 0eb64a429f789..fae9a82f94aa4 100644 --- a/impeller/impeller/entity/Entity.h +++ b/impeller/impeller/entity/entity.h @@ -4,11 +4,11 @@ #pragma once -#include "Color.h" -#include "Image.h" -#include "Matrix.h" -#include "Path.h" -#include "Rect.h" +#include "color.h" +#include "image.h" +#include "matrix.h" +#include "path.h" +#include "rect.h" namespace impeller { diff --git a/impeller/impeller/geometry/Matrix.cc b/impeller/impeller/geometry/matrix.cc similarity index 99% rename from impeller/impeller/geometry/Matrix.cc rename to impeller/impeller/geometry/matrix.cc index 648b9ce40af11..302d07d06ecae 100644 --- a/impeller/impeller/geometry/Matrix.cc +++ b/impeller/impeller/geometry/matrix.cc @@ -2,7 +2,7 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -#include "Matrix.h" +#include "matrix.h" #include #include diff --git a/impeller/impeller/geometry/Matrix.h b/impeller/impeller/geometry/matrix.h similarity index 98% rename from impeller/impeller/geometry/Matrix.h rename to impeller/impeller/geometry/matrix.h index c2a7869ffe735..d35c59f622113 100644 --- a/impeller/impeller/geometry/Matrix.h +++ b/impeller/impeller/geometry/matrix.h @@ -7,11 +7,11 @@ #include #include -#include "Point.h" -#include "Quaternion.h" -#include "Shear.h" -#include "Size.h" -#include "Vector.h" +#include "point.h" +#include "quaternion.h" +#include "shear.h" +#include "size.h" +#include "vector.h" namespace impeller { diff --git a/impeller/impeller/geometry/Path.cc b/impeller/impeller/geometry/path.cc similarity index 99% rename from impeller/impeller/geometry/Path.cc rename to impeller/impeller/geometry/path.cc index 750435311d878..80def8458ec87 100644 --- a/impeller/impeller/geometry/Path.cc +++ b/impeller/impeller/geometry/path.cc @@ -2,7 +2,7 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -#include "Path.h" +#include "path.h" namespace impeller { diff --git a/impeller/impeller/geometry/Path.h b/impeller/impeller/geometry/path.h similarity index 98% rename from impeller/impeller/geometry/Path.h rename to impeller/impeller/geometry/path.h index 1c25776bb1747..9f55a14d0c433 100644 --- a/impeller/impeller/geometry/Path.h +++ b/impeller/impeller/geometry/path.h @@ -7,7 +7,7 @@ #include #include -#include "PathComponent.h" +#include "path_component.h" namespace impeller { diff --git a/impeller/impeller/geometry/PathBuilder.cc b/impeller/impeller/geometry/path_builder.cc similarity index 99% rename from impeller/impeller/geometry/PathBuilder.cc rename to impeller/impeller/geometry/path_builder.cc index 8c429478ca51b..47c42d005ef80 100644 --- a/impeller/impeller/geometry/PathBuilder.cc +++ b/impeller/impeller/geometry/path_builder.cc @@ -2,7 +2,7 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -#include "PathBuilder.h" +#include "path_builder.h" namespace impeller { diff --git a/impeller/impeller/geometry/PathBuilder.h b/impeller/impeller/geometry/path_builder.h similarity index 98% rename from impeller/impeller/geometry/PathBuilder.h rename to impeller/impeller/geometry/path_builder.h index 9ae4929ee32fb..177bd077a815e 100644 --- a/impeller/impeller/geometry/PathBuilder.h +++ b/impeller/impeller/geometry/path_builder.h @@ -4,9 +4,9 @@ #pragma once -#include "Path.h" -#include "Rect.h" #include "flutter/fml/macros.h" +#include "path.h" +#include "rect.h" namespace impeller { diff --git a/impeller/impeller/geometry/PathComponent.cc b/impeller/impeller/geometry/path_component.cc similarity index 99% rename from impeller/impeller/geometry/PathComponent.cc rename to impeller/impeller/geometry/path_component.cc index 270299ecec40f..3d5dad45133d4 100644 --- a/impeller/impeller/geometry/PathComponent.cc +++ b/impeller/impeller/geometry/path_component.cc @@ -2,7 +2,7 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -#include "PathComponent.h" +#include "path_component.h" #include namespace impeller { diff --git a/impeller/impeller/geometry/PathComponent.h b/impeller/impeller/geometry/path_component.h similarity index 99% rename from impeller/impeller/geometry/PathComponent.h rename to impeller/impeller/geometry/path_component.h index 87e9ffe044060..c4486da7cc009 100644 --- a/impeller/impeller/geometry/PathComponent.h +++ b/impeller/impeller/geometry/path_component.h @@ -6,7 +6,7 @@ #include -#include "Rect.h" +#include "rect.h" namespace impeller { diff --git a/impeller/impeller/geometry/Point.cc b/impeller/impeller/geometry/point.cc similarity index 97% rename from impeller/impeller/geometry/Point.cc rename to impeller/impeller/geometry/point.cc index e1f9e31ab485f..a0c942afd01ff 100644 --- a/impeller/impeller/geometry/Point.cc +++ b/impeller/impeller/geometry/point.cc @@ -2,7 +2,7 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -#include "Point.h" +#include "point.h" #include namespace impeller { diff --git a/impeller/impeller/geometry/Point.h b/impeller/impeller/geometry/point.h similarity index 98% rename from impeller/impeller/geometry/Point.h rename to impeller/impeller/geometry/point.h index 6ce9ca1da13f8..33f9e6d948bdb 100644 --- a/impeller/impeller/geometry/Point.h +++ b/impeller/impeller/geometry/point.h @@ -6,7 +6,7 @@ #include #include -#include "Size.h" +#include "size.h" namespace impeller { diff --git a/impeller/impeller/geometry/Quaternion.cc b/impeller/impeller/geometry/quaternion.cc similarity index 97% rename from impeller/impeller/geometry/Quaternion.cc rename to impeller/impeller/geometry/quaternion.cc index 5cfe6c62ca4dc..b00e350ed9691 100644 --- a/impeller/impeller/geometry/Quaternion.cc +++ b/impeller/impeller/geometry/quaternion.cc @@ -2,7 +2,7 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -#include "Quaternion.h" +#include "quaternion.h" #include namespace impeller { diff --git a/impeller/impeller/geometry/Quaternion.h b/impeller/impeller/geometry/quaternion.h similarity index 98% rename from impeller/impeller/geometry/Quaternion.h rename to impeller/impeller/geometry/quaternion.h index 528c93ee38610..00b9e0c1d904c 100644 --- a/impeller/impeller/geometry/Quaternion.h +++ b/impeller/impeller/geometry/quaternion.h @@ -4,7 +4,7 @@ #pragma once -#include "Vector.h" +#include "vector.h" namespace impeller { diff --git a/impeller/impeller/geometry/Rect.cc b/impeller/impeller/geometry/rect.cc similarity index 98% rename from impeller/impeller/geometry/Rect.cc rename to impeller/impeller/geometry/rect.cc index 83f17f9efba68..72d82d48a40d5 100644 --- a/impeller/impeller/geometry/Rect.cc +++ b/impeller/impeller/geometry/rect.cc @@ -2,7 +2,7 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -#include "Rect.h" +#include "rect.h" #include namespace impeller { diff --git a/impeller/impeller/geometry/Rect.h b/impeller/impeller/geometry/rect.h similarity index 98% rename from impeller/impeller/geometry/Rect.h rename to impeller/impeller/geometry/rect.h index f3d39976b20f0..7a16acfb06381 100644 --- a/impeller/impeller/geometry/Rect.h +++ b/impeller/impeller/geometry/rect.h @@ -5,8 +5,8 @@ #pragma once #include -#include "Point.h" -#include "Size.h" +#include "point.h" +#include "size.h" namespace impeller { diff --git a/impeller/impeller/geometry/Shear.cc b/impeller/impeller/geometry/shear.cc similarity index 95% rename from impeller/impeller/geometry/Shear.cc rename to impeller/impeller/geometry/shear.cc index fb15e47935c71..fed0fc9be0eb6 100644 --- a/impeller/impeller/geometry/Shear.cc +++ b/impeller/impeller/geometry/shear.cc @@ -2,7 +2,7 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -#include "Shear.h" +#include "shear.h" #include namespace impeller { diff --git a/impeller/impeller/geometry/Shear.h b/impeller/impeller/geometry/shear.h similarity index 100% rename from impeller/impeller/geometry/Shear.h rename to impeller/impeller/geometry/shear.h diff --git a/impeller/impeller/geometry/Size.cc b/impeller/impeller/geometry/size.cc similarity index 96% rename from impeller/impeller/geometry/Size.cc rename to impeller/impeller/geometry/size.cc index 0d151a8f522ff..da888d7a1342f 100644 --- a/impeller/impeller/geometry/Size.cc +++ b/impeller/impeller/geometry/size.cc @@ -2,7 +2,7 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -#include "Size.h" +#include "size.h" #include namespace impeller { diff --git a/impeller/impeller/geometry/Size.h b/impeller/impeller/geometry/size.h similarity index 100% rename from impeller/impeller/geometry/Size.h rename to impeller/impeller/geometry/size.h diff --git a/impeller/impeller/geometry/Vector.cc b/impeller/impeller/geometry/vector.cc similarity index 96% rename from impeller/impeller/geometry/Vector.cc rename to impeller/impeller/geometry/vector.cc index 4765bef6cc926..fecc8476d3f3f 100644 --- a/impeller/impeller/geometry/Vector.cc +++ b/impeller/impeller/geometry/vector.cc @@ -2,7 +2,7 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -#include "Vector.h" +#include "vector.h" #include namespace impeller { diff --git a/impeller/impeller/geometry/Vector.h b/impeller/impeller/geometry/vector.h similarity index 98% rename from impeller/impeller/geometry/Vector.h rename to impeller/impeller/geometry/vector.h index d5713498da8a2..9360d7fe5d4cc 100644 --- a/impeller/impeller/geometry/Vector.h +++ b/impeller/impeller/geometry/vector.h @@ -6,8 +6,8 @@ #include #include -#include "Point.h" -#include "Size.h" +#include "point.h" +#include "size.h" namespace impeller { diff --git a/impeller/impeller/image/Image.cc b/impeller/impeller/image/image.cc similarity index 99% rename from impeller/impeller/image/Image.cc rename to impeller/impeller/image/image.cc index cd3aac1a18676..b8776eb8c4ad2 100644 --- a/impeller/impeller/image/Image.cc +++ b/impeller/impeller/image/image.cc @@ -2,7 +2,7 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -#include "Image.h" +#include "image.h" #include namespace impeller { diff --git a/impeller/impeller/image/Image.h b/impeller/impeller/image/image.h similarity index 92% rename from impeller/impeller/image/Image.h rename to impeller/impeller/image/image.h index aa533f3a6271d..aee6c35467c15 100644 --- a/impeller/impeller/image/Image.h +++ b/impeller/impeller/image/image.h @@ -4,10 +4,10 @@ #pragma once -#include "ImageResult.h" -#include "Size.h" #include "flutter/fml/macros.h" #include "flutter/fml/mapping.h" +#include "image_result.h" +#include "size.h" namespace impeller { diff --git a/impeller/impeller/image/ImageResult.cc b/impeller/impeller/image/image_result.cc similarity index 97% rename from impeller/impeller/image/ImageResult.cc rename to impeller/impeller/image/image_result.cc index 4b789e4f99830..937a3b946ea80 100644 --- a/impeller/impeller/image/ImageResult.cc +++ b/impeller/impeller/image/image_result.cc @@ -2,7 +2,7 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -#include "ImageResult.h" +#include "image_result.h" namespace impeller { diff --git a/impeller/impeller/image/ImageResult.h b/impeller/impeller/image/image_result.h similarity index 98% rename from impeller/impeller/image/ImageResult.h rename to impeller/impeller/image/image_result.h index 0e9985344aa07..45b7386dbd16b 100644 --- a/impeller/impeller/image/ImageResult.h +++ b/impeller/impeller/image/image_result.h @@ -6,9 +6,9 @@ #include -#include "Size.h" #include "flutter/fml/macros.h" #include "flutter/fml/mapping.h" +#include "size.h" namespace impeller { From 7ad455ea316794169935d73890505b91a356d0f1 Mon Sep 17 00:00:00 2001 From: Chinmay Garde Date: Fri, 7 May 2021 15:10:00 -0700 Subject: [PATCH 021/433] Wire up renderer. --- impeller/host/impeller_renderer.mm | 13 +++++- impeller/impeller/BUILD.gn | 23 +++++++++- impeller/impeller/compositor/context.h | 31 +++++++++++++ impeller/impeller/compositor/context.mm | 35 ++++++++++++++ .../impeller/compositor/pipeline_library.h | 21 +++++++++ .../impeller/compositor/pipeline_library.mm | 0 impeller/impeller/compositor/renderer.h | 35 ++++++++++++++ impeller/impeller/compositor/renderer.mm | 46 +++++++++++++++++++ impeller/impeller/compositor/shader_library.h | 21 +++++++++ .../impeller/compositor/shader_library.mm | 0 impeller/impeller/compositor/surface.cc | 0 impeller/impeller/compositor/surface.h | 21 +++++++++ 12 files changed, 244 insertions(+), 2 deletions(-) create mode 100644 impeller/impeller/compositor/context.h create mode 100644 impeller/impeller/compositor/context.mm create mode 100644 impeller/impeller/compositor/pipeline_library.h create mode 100644 impeller/impeller/compositor/pipeline_library.mm create mode 100644 impeller/impeller/compositor/renderer.h create mode 100644 impeller/impeller/compositor/renderer.mm create mode 100644 impeller/impeller/compositor/shader_library.h create mode 100644 impeller/impeller/compositor/shader_library.mm create mode 100644 impeller/impeller/compositor/surface.cc create mode 100644 impeller/impeller/compositor/surface.h diff --git a/impeller/host/impeller_renderer.mm b/impeller/host/impeller_renderer.mm index f54f7ae82d2e2..87a3906b0ae60 100644 --- a/impeller/host/impeller_renderer.mm +++ b/impeller/host/impeller_renderer.mm @@ -7,6 +7,7 @@ #import "assets_location.h" #include "flutter/fml/logging.h" +#import "impeller/compositor/renderer.h" #import "impeller_renderer.h" #import "shaders_location.h" @@ -40,6 +41,7 @@ @implementation ImpellerRenderer { float _rotation; MTKMesh* _mesh; + impeller::Renderer renderer_; } - (nonnull instancetype)initWithMetalKitView:(nonnull MTKView*)view { @@ -51,6 +53,10 @@ - (nonnull instancetype)initWithMetalKitView:(nonnull MTKView*)view { [self _loadAssets]; } + if (!renderer_.IsValid()) { + FML_LOG(ERROR) << "Impeller Renderer is not valid."; + } + return self; } @@ -220,6 +226,7 @@ - (void)_updateGameState { } - (void)drawInMTKView:(nonnull MTKView*)view { + renderer_.Render(); /// Per frame updates here dispatch_semaphore_wait(_inFlightSemaphore, DISPATCH_TIME_FOREVER); @@ -294,7 +301,8 @@ - (void)drawInMTKView:(nonnull MTKView*)view { } - (void)mtkView:(nonnull MTKView*)view drawableSizeWillChange:(CGSize)size { - NSLog(@"Drawable sized did change: %@", NSStringFromSize(size)); + renderer_.SurfaceSizeDidChange({size.width, size.height}); + float aspect = size.width / (float)size.height; _projectionMatrix = matrix_perspective_right_hand(65.0f * (M_PI / 180.0f), aspect, 0.1f, 100.0f); @@ -302,11 +310,13 @@ - (void)mtkView:(nonnull MTKView*)view drawableSizeWillChange:(CGSize)size { #pragma mark Matrix Math Utilities +// NOLINTNEXTLINE matrix_float4x4 matrix4x4_translation(float tx, float ty, float tz) { return (matrix_float4x4){ {{1, 0, 0, 0}, {0, 1, 0, 0}, {0, 0, 1, 0}, {tx, ty, tz, 1}}}; } +// NOLINTNEXTLINE static matrix_float4x4 matrix4x4_rotation(float radians, vector_float3 axis) { axis = vector_normalize(axis); float ct = cosf(radians); @@ -321,6 +331,7 @@ static matrix_float4x4 matrix4x4_rotation(float radians, vector_float3 axis) { {0, 0, 0, 1}}}; } +// NOLINTNEXTLINE matrix_float4x4 matrix_perspective_right_hand(float fovyRadians, float aspect, float nearZ, diff --git a/impeller/impeller/BUILD.gn b/impeller/impeller/BUILD.gn index 4932aea7dcb53..93a7fc481880e 100644 --- a/impeller/impeller/BUILD.gn +++ b/impeller/impeller/BUILD.gn @@ -4,7 +4,13 @@ import("//flutter/common/config.gni") +config("impeller_config") { + include_dirs = [ ".." ] +} + source_set("impeller") { + public_configs = [ ":impeller_config" ] + cflags_objc = flutter_cflags_objc_arc cflags_objcc = flutter_cflags_objcc_arc @@ -12,6 +18,20 @@ source_set("impeller") { "entity", "geometry", "image", + "compositor", + ] + + compositor_sources = [ + "compositor/context.h", + "compositor/context.mm", + "compositor/pipeline_library.h", + "compositor/pipeline_library.mm", + "compositor/renderer.h", + "compositor/renderer.mm", + "compositor/shader_library.h", + "compositor/shader_library.mm", + "compositor/surface.cc", + "compositor/surface.h", ] geometry_sources = [ @@ -51,7 +71,8 @@ source_set("impeller") { "image/image_result.h", ] - sources = geometry_sources + entity_sources + image_sources + sources = + compositor_sources + geometry_sources + entity_sources + image_sources deps = [ "//flutter/fml", diff --git a/impeller/impeller/compositor/context.h b/impeller/impeller/compositor/context.h new file mode 100644 index 0000000000000..ad126c3d6a11b --- /dev/null +++ b/impeller/impeller/compositor/context.h @@ -0,0 +1,31 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#pragma once + +#include + +#include "flutter/fml/macros.h" + +namespace impeller { + +class Context { + public: + Context(); + + ~Context(); + + bool IsValid() const; + + private: + id device_; + id render_queue_; + id transfer_queue_; + + bool is_valid_ = false; + + FML_DISALLOW_COPY_AND_ASSIGN(Context); +}; + +} // namespace impeller diff --git a/impeller/impeller/compositor/context.mm b/impeller/impeller/compositor/context.mm new file mode 100644 index 0000000000000..cba0ba9105f83 --- /dev/null +++ b/impeller/impeller/compositor/context.mm @@ -0,0 +1,35 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "context.h" + +#include "flutter/fml/logging.h" + +namespace impeller { + +Context::Context() : device_(::MTLCreateSystemDefaultDevice()) { + if (!device_) { + return; + } + + render_queue_ = device_.newCommandQueue; + transfer_queue_ = device_.newCommandQueue; + + if (!render_queue_ || !transfer_queue_) { + return; + } + + render_queue_.label = @"Impeller Render Queue"; + transfer_queue_.label = @"Impeller Transfer Queue"; + + is_valid_ = true; +} + +Context::~Context() = default; + +bool Context::IsValid() const { + return is_valid_; +} + +} // namespace impeller diff --git a/impeller/impeller/compositor/pipeline_library.h b/impeller/impeller/compositor/pipeline_library.h new file mode 100644 index 0000000000000..a20ff2a08cd3c --- /dev/null +++ b/impeller/impeller/compositor/pipeline_library.h @@ -0,0 +1,21 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#pragma once + +#include "flutter/fml/macros.h" + +namespace impeller { + +class PipelineLibrary { + public: + PipelineLibrary(); + + ~PipelineLibrary(); + + private: + FML_DISALLOW_COPY_AND_ASSIGN(PipelineLibrary); +}; + +} // namespace impeller diff --git a/impeller/impeller/compositor/pipeline_library.mm b/impeller/impeller/compositor/pipeline_library.mm new file mode 100644 index 0000000000000..e69de29bb2d1d diff --git a/impeller/impeller/compositor/renderer.h b/impeller/impeller/compositor/renderer.h new file mode 100644 index 0000000000000..5a66c0fd06cde --- /dev/null +++ b/impeller/impeller/compositor/renderer.h @@ -0,0 +1,35 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#pragma once + +#include "context.h" +#include "flutter/fml/macros.h" +#include "impeller/geometry/size.h" + +namespace impeller { + +class Renderer { + public: + Renderer(); + + ~Renderer(); + + bool IsValid() const; + + bool SurfaceSizeDidChange(Size size); + + bool Render(); + + private: + Context context_; + Size size_; + bool is_valid_ = false; + + bool ShouldRender() const; + + FML_DISALLOW_COPY_AND_ASSIGN(Renderer); +}; + +} // namespace impeller diff --git a/impeller/impeller/compositor/renderer.mm b/impeller/impeller/compositor/renderer.mm new file mode 100644 index 0000000000000..adbd310930b3f --- /dev/null +++ b/impeller/impeller/compositor/renderer.mm @@ -0,0 +1,46 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "renderer.h" + +#include "flutter/fml/logging.h" + +namespace impeller { + +Renderer::Renderer() { + if (!context_.IsValid()) { + return; + } + + is_valid_ = true; +} + +Renderer::~Renderer() = default; + +bool Renderer::IsValid() const { + return is_valid_; +} + +bool Renderer::ShouldRender() const { + return IsValid() && !size_.IsZero(); +} + +bool Renderer::SurfaceSizeDidChange(Size size) { + if (size_ == size) { + return true; + } + + size_ = size; + return true; +} + +bool Renderer::Render() { + if (!ShouldRender()) { + return false; + } + + return true; +} + +} // namespace impeller diff --git a/impeller/impeller/compositor/shader_library.h b/impeller/impeller/compositor/shader_library.h new file mode 100644 index 0000000000000..6d62e86a9731d --- /dev/null +++ b/impeller/impeller/compositor/shader_library.h @@ -0,0 +1,21 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#pragma once + +#include "flutter/fml/macros.h" + +namespace impeller { + +class ShaderLibrary { + public: + ShaderLibrary(); + + ~ShaderLibrary(); + + private: + FML_DISALLOW_COPY_AND_ASSIGN(ShaderLibrary); +}; + +} // namespace impeller diff --git a/impeller/impeller/compositor/shader_library.mm b/impeller/impeller/compositor/shader_library.mm new file mode 100644 index 0000000000000..e69de29bb2d1d diff --git a/impeller/impeller/compositor/surface.cc b/impeller/impeller/compositor/surface.cc new file mode 100644 index 0000000000000..e69de29bb2d1d diff --git a/impeller/impeller/compositor/surface.h b/impeller/impeller/compositor/surface.h new file mode 100644 index 0000000000000..106548a31f0da --- /dev/null +++ b/impeller/impeller/compositor/surface.h @@ -0,0 +1,21 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#pragma once + +#include "flutter/fml/macros.h" + +namespace impeller { + +class Surface { + public: + Surface(); + + ~Surface(); + + private: + FML_DISALLOW_COPY_AND_ASSIGN(Surface); +}; + +} // namespace impeller From dbe5752e9d956c4a514d867d203240291bb3d3d7 Mon Sep 17 00:00:00 2001 From: Chinmay Garde Date: Sat, 8 May 2021 17:49:33 -0700 Subject: [PATCH 022/433] Wire up render loop. --- impeller/impeller/BUILD.gn | 2 +- impeller/impeller/compositor/context.h | 5 ++- impeller/impeller/compositor/context.mm | 8 ++++ impeller/impeller/compositor/renderer.h | 8 +++- impeller/impeller/compositor/renderer.mm | 14 +++++-- impeller/impeller/compositor/surface.cc | 0 impeller/impeller/compositor/surface.h | 15 ++++++- impeller/impeller/compositor/surface.mm | 52 ++++++++++++++++++++++++ 8 files changed, 95 insertions(+), 9 deletions(-) delete mode 100644 impeller/impeller/compositor/surface.cc create mode 100644 impeller/impeller/compositor/surface.mm diff --git a/impeller/impeller/BUILD.gn b/impeller/impeller/BUILD.gn index 93a7fc481880e..8e7fb5c88bb72 100644 --- a/impeller/impeller/BUILD.gn +++ b/impeller/impeller/BUILD.gn @@ -30,8 +30,8 @@ source_set("impeller") { "compositor/renderer.mm", "compositor/shader_library.h", "compositor/shader_library.mm", - "compositor/surface.cc", "compositor/surface.h", + "compositor/surface.mm", ] geometry_sources = [ diff --git a/impeller/impeller/compositor/context.h b/impeller/impeller/compositor/context.h index ad126c3d6a11b..163239a7c3479 100644 --- a/impeller/impeller/compositor/context.h +++ b/impeller/impeller/compositor/context.h @@ -18,11 +18,14 @@ class Context { bool IsValid() const; + id GetRenderQueue() const; + + id GetTransferQueue() const; + private: id device_; id render_queue_; id transfer_queue_; - bool is_valid_ = false; FML_DISALLOW_COPY_AND_ASSIGN(Context); diff --git a/impeller/impeller/compositor/context.mm b/impeller/impeller/compositor/context.mm index cba0ba9105f83..e64025f84c065 100644 --- a/impeller/impeller/compositor/context.mm +++ b/impeller/impeller/compositor/context.mm @@ -32,4 +32,12 @@ return is_valid_; } +id Context::GetRenderQueue() const { + return render_queue_; +} + +id Context::GetTransferQueue() const { + return transfer_queue_; +} + } // namespace impeller diff --git a/impeller/impeller/compositor/renderer.h b/impeller/impeller/compositor/renderer.h index 5a66c0fd06cde..8fc32a5e89dae 100644 --- a/impeller/impeller/compositor/renderer.h +++ b/impeller/impeller/compositor/renderer.h @@ -4,8 +4,11 @@ #pragma once -#include "context.h" +#include + #include "flutter/fml/macros.h" +#include "impeller/compositor/context.h" +#include "impeller/compositor/surface.h" #include "impeller/geometry/size.h" namespace impeller { @@ -23,7 +26,8 @@ class Renderer { bool Render(); private: - Context context_; + std::shared_ptr context_; + std::unique_ptr surface_; Size size_; bool is_valid_ = false; diff --git a/impeller/impeller/compositor/renderer.mm b/impeller/impeller/compositor/renderer.mm index adbd310930b3f..5d5b8ac45af92 100644 --- a/impeller/impeller/compositor/renderer.mm +++ b/impeller/impeller/compositor/renderer.mm @@ -2,14 +2,20 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -#include "renderer.h" +#include "impeller/compositor/renderer.h" #include "flutter/fml/logging.h" namespace impeller { -Renderer::Renderer() { - if (!context_.IsValid()) { +Renderer::Renderer() + : context_(std::make_shared()), + surface_(std::make_unique(context_)) { + if (!context_->IsValid()) { + return; + } + + if (!surface_->IsValid()) { return; } @@ -40,7 +46,7 @@ return false; } - return true; + return surface_->Render(); } } // namespace impeller diff --git a/impeller/impeller/compositor/surface.cc b/impeller/impeller/compositor/surface.cc deleted file mode 100644 index e69de29bb2d1d..0000000000000 diff --git a/impeller/impeller/compositor/surface.h b/impeller/impeller/compositor/surface.h index 106548a31f0da..a172a0e6b301c 100644 --- a/impeller/impeller/compositor/surface.h +++ b/impeller/impeller/compositor/surface.h @@ -4,17 +4,30 @@ #pragma once +#include + +#include + #include "flutter/fml/macros.h" +#include "impeller/compositor/context.h" namespace impeller { class Surface { public: - Surface(); + Surface(std::shared_ptr context); ~Surface(); + bool IsValid() const; + + bool Render() const; + private: + std::shared_ptr context_; + dispatch_semaphore_t frames_in_flight_sema_; + bool is_valid_ = false; + FML_DISALLOW_COPY_AND_ASSIGN(Surface); }; diff --git a/impeller/impeller/compositor/surface.mm b/impeller/impeller/compositor/surface.mm new file mode 100644 index 0000000000000..7cfa292a11315 --- /dev/null +++ b/impeller/impeller/compositor/surface.mm @@ -0,0 +1,52 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "impeller/compositor/surface.h" + +#include "flutter/fml/logging.h" + +namespace impeller { + +constexpr size_t kMaxFramesInFlight = 3u; + +Surface::Surface(std::shared_ptr context) + : context_(std::move(context)), + frames_in_flight_sema_(::dispatch_semaphore_create(kMaxFramesInFlight)) { + if (!context_ || !context_->IsValid()) { + return; + } + + is_valid_ = true; +} + +Surface::~Surface() = default; + +bool Surface::IsValid() const { + return is_valid_; +} + +bool Surface::Render() const { + if (!IsValid()) { + return false; + } + + auto command_buffer = [context_->GetRenderQueue() commandBuffer]; + + if (!command_buffer) { + return false; + } + + ::dispatch_semaphore_wait(frames_in_flight_sema_, DISPATCH_TIME_FOREVER); + + __block dispatch_semaphore_t block_sema = frames_in_flight_sema_; + [command_buffer addCompletedHandler:^(id buffer) { + ::dispatch_semaphore_signal(block_sema); + }]; + + [command_buffer commit]; + + return true; +} + +} // namespace impeller From 39ccb23ed834d2dbeae21bb993c81c2a59c087c3 Mon Sep 17 00:00:00 2001 From: Chinmay Garde Date: Sun, 9 May 2021 00:22:26 -0700 Subject: [PATCH 023/433] Stub out pipeline descriptors and builders. --- impeller/impeller/BUILD.gn | 4 ++++ .../impeller/compositor/pipeline_builder.h | 24 +++++++++++++++++++ .../impeller/compositor/pipeline_builder.mm | 13 ++++++++++ .../impeller/compositor/pipeline_descriptor.h | 21 ++++++++++++++++ .../compositor/pipeline_descriptor.mm | 13 ++++++++++ 5 files changed, 75 insertions(+) create mode 100644 impeller/impeller/compositor/pipeline_builder.h create mode 100644 impeller/impeller/compositor/pipeline_builder.mm create mode 100644 impeller/impeller/compositor/pipeline_descriptor.h create mode 100644 impeller/impeller/compositor/pipeline_descriptor.mm diff --git a/impeller/impeller/BUILD.gn b/impeller/impeller/BUILD.gn index 8e7fb5c88bb72..4807e1a4d87a8 100644 --- a/impeller/impeller/BUILD.gn +++ b/impeller/impeller/BUILD.gn @@ -24,6 +24,10 @@ source_set("impeller") { compositor_sources = [ "compositor/context.h", "compositor/context.mm", + "compositor/pipeline_builder.h", + "compositor/pipeline_builder.mm", + "compositor/pipeline_descriptor.h", + "compositor/pipeline_descriptor.mm", "compositor/pipeline_library.h", "compositor/pipeline_library.mm", "compositor/renderer.h", diff --git a/impeller/impeller/compositor/pipeline_builder.h b/impeller/impeller/compositor/pipeline_builder.h new file mode 100644 index 0000000000000..d2e1c7002e048 --- /dev/null +++ b/impeller/impeller/compositor/pipeline_builder.h @@ -0,0 +1,24 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#pragma once + +#include "flutter/fml/macros.h" +#include "impeller/compositor/pipeline_descriptor.h" + +namespace impeller { + +class PipelineBuilder { + public: + PipelineBuilder(); + + ~PipelineBuilder(); + + PipelineDescriptor Build() const; + + private: + FML_DISALLOW_COPY_AND_ASSIGN(PipelineBuilder); +}; + +} // namespace impeller diff --git a/impeller/impeller/compositor/pipeline_builder.mm b/impeller/impeller/compositor/pipeline_builder.mm new file mode 100644 index 0000000000000..4f954b6da68ea --- /dev/null +++ b/impeller/impeller/compositor/pipeline_builder.mm @@ -0,0 +1,13 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "impeller/compositor/pipeline_builder.h" + +namespace impeller { + +PipelineBuilder::PipelineBuilder() = default; + +PipelineBuilder::~PipelineBuilder() = default; + +} // namespace impeller diff --git a/impeller/impeller/compositor/pipeline_descriptor.h b/impeller/impeller/compositor/pipeline_descriptor.h new file mode 100644 index 0000000000000..093536dae569f --- /dev/null +++ b/impeller/impeller/compositor/pipeline_descriptor.h @@ -0,0 +1,21 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#pragma once + +#include "flutter/fml/macros.h" + +namespace impeller { + +class PipelineDescriptor { + public: + PipelineDescriptor(); + + ~PipelineDescriptor(); + + private: + FML_DISALLOW_COPY_AND_ASSIGN(PipelineDescriptor); +}; + +} // namespace impeller diff --git a/impeller/impeller/compositor/pipeline_descriptor.mm b/impeller/impeller/compositor/pipeline_descriptor.mm new file mode 100644 index 0000000000000..ab8c8281e565e --- /dev/null +++ b/impeller/impeller/compositor/pipeline_descriptor.mm @@ -0,0 +1,13 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "impeller/compositor/pipeline_descriptor.h" + +namespace impeller { + +PipelineDescriptor::PipelineDescriptor() = default; + +PipelineDescriptor::~PipelineDescriptor() = default; + +} // namespace impeller From 377948b29dec074960212711c3f234882360baf6 Mon Sep 17 00:00:00 2001 From: Chinmay Garde Date: Sun, 9 May 2021 18:24:11 -0700 Subject: [PATCH 024/433] Start wiring up impellerc --- impeller/compiler/BUILD.gn | 15 ++++++++++++ impeller/compiler/impellerc_main.cc | 23 +++++++++++++++++++ impeller/compiler/tools/compiler.gni | 7 ++++++ .../impeller/compositor/shaders/box/box.frag | 12 ++++++++++ .../impeller/compositor/shaders/box/box.vert | 18 +++++++++++++++ 5 files changed, 75 insertions(+) create mode 100644 impeller/compiler/BUILD.gn create mode 100644 impeller/compiler/impellerc_main.cc create mode 100644 impeller/compiler/tools/compiler.gni create mode 100644 impeller/impeller/compositor/shaders/box/box.frag create mode 100644 impeller/impeller/compositor/shaders/box/box.vert diff --git a/impeller/compiler/BUILD.gn b/impeller/compiler/BUILD.gn new file mode 100644 index 0000000000000..84ca00e7c4cf7 --- /dev/null +++ b/impeller/compiler/BUILD.gn @@ -0,0 +1,15 @@ +# Copyright 2013 The Flutter Authors. All rights reserved. +# Use of this source code is governed by a BSD-style license that can be +# found in the LICENSE file. + +executable("compiler") { + output_name = "impellerc" + + sources = [ "impellerc_main.cc" ] + + deps = [ + "//flutter/fml", + "//flutter/runtime:libdart", + "//third_party/shaderc_flutter", + ] +} diff --git a/impeller/compiler/impellerc_main.cc b/impeller/compiler/impellerc_main.cc new file mode 100644 index 0000000000000..619bdd212f30f --- /dev/null +++ b/impeller/compiler/impellerc_main.cc @@ -0,0 +1,23 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "flutter/fml/command_line.h" +#include "flutter/fml/macros.h" +#include "third_party/shaderc/libshaderc/include/shaderc/shaderc.hpp" + +namespace impeller { +namespace compiler { + +bool Main(const fml::CommandLine& command_line) { + return false; +} + +} // namespace compiler +} // namespace impeller + +int main(int argc, char const* argv[]) { + return impeller::compiler::Main(fml::CommandLineFromArgcArgv(argc, argv)) + ? EXIT_SUCCESS + : EXIT_FAILURE; +} diff --git a/impeller/compiler/tools/compiler.gni b/impeller/compiler/tools/compiler.gni new file mode 100644 index 0000000000000..d73f5f6463239 --- /dev/null +++ b/impeller/compiler/tools/compiler.gni @@ -0,0 +1,7 @@ +# Copyright 2013 The Flutter Authors. All rights reserved. +# Use of this source code is governed by a BSD-style license that can be +# found in the LICENSE file. + +template("impeller_shaders") { + assert(defined(invoker.shaders), "Impeller shaders must be specified.") +} diff --git a/impeller/impeller/compositor/shaders/box/box.frag b/impeller/impeller/compositor/shaders/box/box.frag new file mode 100644 index 0000000000000..69036b6cf4012 --- /dev/null +++ b/impeller/impeller/compositor/shaders/box/box.frag @@ -0,0 +1,12 @@ +#version 450 +#extension GL_ARB_separate_shader_objects : enable + +layout(binding = 1) uniform sampler2D textureSampler; + +layout(location = 0) in vec2 inTextureCoord; + +layout(location = 0) out vec4 outColor; + +void main() { + outColor = texture(textureSampler, inTextureCoord); +} diff --git a/impeller/impeller/compositor/shaders/box/box.vert b/impeller/impeller/compositor/shaders/box/box.vert new file mode 100644 index 0000000000000..ae31781c1b71f --- /dev/null +++ b/impeller/impeller/compositor/shaders/box/box.vert @@ -0,0 +1,18 @@ +#version 450 +#extension GL_ARB_separate_shader_objects : enable + +layout(binding = 0) uniform UniformBufferObject { + mat4 model; + mat4 view; + mat4 projection; +} ubo; + +layout(location = 0) in vec3 inPosition; +layout(location = 1) in vec2 inTextureCoord; + +layout(location = 0) out vec2 outTextureCoord; + +void main() { + gl_Position = ubo.projection * ubo.view * ubo.model * vec4(inPosition, 1.0); + outTextureCoord = inTextureCoord; +} From 34327b6fdd6d486b742321f3919e8d7f36ebc602 Mon Sep 17 00:00:00 2001 From: Chinmay Garde Date: Mon, 10 May 2021 13:58:12 -0700 Subject: [PATCH 025/433] Wire up GLSL to spirv compiler. --- impeller/compiler/BUILD.gn | 6 +- impeller/compiler/compiler.cc | 84 ++++++++++++++++++++++++++ impeller/compiler/compiler.h | 45 ++++++++++++++ impeller/compiler/fixtures/sample.frag | 23 +++++++ 4 files changed, 157 insertions(+), 1 deletion(-) create mode 100644 impeller/compiler/compiler.cc create mode 100644 impeller/compiler/compiler.h create mode 100644 impeller/compiler/fixtures/sample.frag diff --git a/impeller/compiler/BUILD.gn b/impeller/compiler/BUILD.gn index 84ca00e7c4cf7..f22c416536b0e 100644 --- a/impeller/compiler/BUILD.gn +++ b/impeller/compiler/BUILD.gn @@ -5,7 +5,11 @@ executable("compiler") { output_name = "impellerc" - sources = [ "impellerc_main.cc" ] + sources = [ + "compiler.cc", + "compiler.h", + "impellerc_main.cc", + ] deps = [ "//flutter/fml", diff --git a/impeller/compiler/compiler.cc b/impeller/compiler/compiler.cc new file mode 100644 index 0000000000000..d9b6ac0e47e7d --- /dev/null +++ b/impeller/compiler/compiler.cc @@ -0,0 +1,84 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "flutter/impeller/compiler/compiler.h" + +#include + +namespace impeller { +namespace compiler { + +class Includer final : public shaderc::CompileOptions::IncluderInterface { + public: + Includer() = default; + + // |shaderc::CompileOptions::IncluderInterface| + ~Includer() override = default; + + // |shaderc::CompileOptions::IncluderInterface| + shaderc_include_result* GetInclude(const char* requested_source, + shaderc_include_type type, + const char* requesting_source, + size_t include_depth) override { + return nullptr; + } + + // |shaderc::CompileOptions::IncluderInterface| + void ReleaseInclude(shaderc_include_result* data) override {} + + private: + FML_DISALLOW_COPY_AND_ASSIGN(Includer); +}; + +static shaderc_shader_kind ToShaderCShaderKind(Compiler::SourceType type) { + switch (type) { + case Compiler::SourceType::kVertexShader: + return shaderc_shader_kind::shaderc_vertex_shader; + case Compiler::SourceType::kFragmentShader: + return shaderc_shader_kind::shaderc_fragment_shader; + } + return shaderc_shader_kind::shaderc_vertex_shader; +} + +Compiler::Compiler(const fml::Mapping& source_mapping, + SourceOptions source_options) { + if (source_mapping.GetMapping() == nullptr) { + return; + } + + shaderc::CompileOptions options; + options.SetGenerateDebugInfo(); + options.SetOptimizationLevel( + shaderc_optimization_level::shaderc_optimization_level_size); + options.SetSourceLanguage( + shaderc_source_language::shaderc_source_language_glsl); + options.SetTargetEnvironment( + shaderc_target_env::shaderc_target_env_vulkan, + shaderc_env_version::shaderc_env_version_opengl_4_5); + options.SetTargetSpirv(shaderc_spirv_version::shaderc_spirv_version_1_3); + options.SetAutoBindUniforms(true); + options.SetAutoMapLocations(true); + options.SetIncluder(std::make_unique()); + + shaderc::Compiler compiler; + if (!compiler.IsValid()) { + return; + } + + result_ = compiler.CompileGlslToSpv( + reinterpret_cast( + source_mapping.GetMapping()), // source_text + source_mapping.GetSize(), // source_text_size + ToShaderCShaderKind(source_options.type), // shader_kind + source_options.file_name.c_str(), // input_file_name + source_options.entry_point_name.c_str(), // entry_point_name + options // options + ); + is_valid_ = true; +} + +Compiler::~Compiler() = default; + +} // namespace compiler +} // namespace impeller diff --git a/impeller/compiler/compiler.h b/impeller/compiler/compiler.h new file mode 100644 index 0000000000000..192ec68ffa65c --- /dev/null +++ b/impeller/compiler/compiler.h @@ -0,0 +1,45 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#pragma once + +#include + +#include "flutter/fml/macros.h" +#include "flutter/fml/mapping.h" +#include "shaderc/shaderc.hpp" + +namespace impeller { +namespace compiler { + +class Compiler { + public: + enum class SourceType { + kVertexShader, + kFragmentShader, + }; + + static SourceType SourceTypeFromFileName(const std::string& file_name); + + struct SourceOptions { + SourceType type = SourceType::kVertexShader; + std::string file_name = "main.glsl"; + std::string entry_point_name = "main"; + }; + + Compiler(const fml::Mapping& source_mapping, SourceOptions options); + + ~Compiler(); + + bool IsValid() const; + + private: + shaderc::SpvCompilationResult result_; + bool is_valid_ = false; + + FML_DISALLOW_COPY_AND_ASSIGN(Compiler); +}; + +} // namespace compiler +} // namespace impeller diff --git a/impeller/compiler/fixtures/sample.frag b/impeller/compiler/fixtures/sample.frag new file mode 100644 index 0000000000000..4c6fa55dfb044 --- /dev/null +++ b/impeller/compiler/fixtures/sample.frag @@ -0,0 +1,23 @@ +#version 450 + +struct Hello { + vec4 stuff; + vec2 more_stuff; + float dunno; +}; + +uniform Uniforms { + Hello myHello; + vec4 hi; + Hello goodbyeHello; +}; + +uniform sampler2D textureSampler; + +in vec2 inTextureCoord; + +out vec4 outColor; + +void main() { + outColor = texture(textureSampler, inTextureCoord * myHello.more_stuff); +} From 1bd6006170d87aaf56b43efd4dddf29d6451d9fa Mon Sep 17 00:00:00 2001 From: Chinmay Garde Date: Mon, 10 May 2021 17:09:14 -0700 Subject: [PATCH 026/433] Wire up one-shot conversion of GLSL to MSL. --- impeller/compiler/BUILD.gn | 35 ++++- impeller/compiler/compiler.cc | 169 +++++++++++++++++++++--- impeller/compiler/compiler.h | 24 +++- impeller/compiler/compiler_unittests.cc | 34 +++++ impeller/compiler/logger.h | 40 ++++++ 5 files changed, 281 insertions(+), 21 deletions(-) create mode 100644 impeller/compiler/compiler_unittests.cc create mode 100644 impeller/compiler/logger.h diff --git a/impeller/compiler/BUILD.gn b/impeller/compiler/BUILD.gn index f22c416536b0e..68c7e50f33965 100644 --- a/impeller/compiler/BUILD.gn +++ b/impeller/compiler/BUILD.gn @@ -2,18 +2,45 @@ # Use of this source code is governed by a BSD-style license that can be # found in the LICENSE file. -executable("compiler") { - output_name = "impellerc" +import("//flutter/testing/testing.gni") +source_set("compiler_lib") { sources = [ "compiler.cc", "compiler.h", - "impellerc_main.cc", + "logger.h", ] - deps = [ + public_deps = [ "//flutter/fml", "//flutter/runtime:libdart", "//third_party/shaderc_flutter", + "//third_party/spirv_cross_flutter", + ] +} + +executable("compiler") { + output_name = "impellerc" + + sources = [ "impellerc_main.cc" ] + + deps = [ ":compiler_lib" ] +} + +test_fixtures("compiler_test_fixtures") { + fixtures = [ "fixtures/sample.frag" ] +} + +executable("compiler_unittests") { + testonly = true + + output_name = "impellerc_unittests" + + sources = [ "compiler_unittests.cc" ] + + deps = [ + ":compiler_lib", + ":compiler_test_fixtures", + "//flutter/testing", ] } diff --git a/impeller/compiler/compiler.cc b/impeller/compiler/compiler.cc index d9b6ac0e47e7d..0d209fa1589e8 100644 --- a/impeller/compiler/compiler.cc +++ b/impeller/compiler/compiler.cc @@ -4,11 +4,18 @@ #include "flutter/impeller/compiler/compiler.h" +#include "flutter/impeller/compiler/logger.h" + #include namespace impeller { namespace compiler { +#define COMPILER_ERROR \ + ::impeller::compiler::AutoLogger(error_stream_) << GetSourcePrefix() + +#define COMPILER_ERROR_NO_PREFIX ::impeller::compiler::AutoLogger(error_stream_) + class Includer final : public shaderc::CompileOptions::IncluderInterface { public: Includer() = default; @@ -21,29 +28,73 @@ class Includer final : public shaderc::CompileOptions::IncluderInterface { shaderc_include_type type, const char* requesting_source, size_t include_depth) override { + FML_CHECK(false); return nullptr; } // |shaderc::CompileOptions::IncluderInterface| - void ReleaseInclude(shaderc_include_result* data) override {} + void ReleaseInclude(shaderc_include_result* data) override { + FML_CHECK(false); + } private: FML_DISALLOW_COPY_AND_ASSIGN(Includer); }; +static std::string ShaderCErrorToString(shaderc_compilation_status status) { + switch (status) { + case shaderc_compilation_status::shaderc_compilation_status_success: + return "Success"; + case shaderc_compilation_status::shaderc_compilation_status_invalid_stage: + return "Invalid Shader Stage Specified"; + case shaderc_compilation_status:: + shaderc_compilation_status_compilation_error: + return "Compilation Error"; + case shaderc_compilation_status::shaderc_compilation_status_internal_error: + return "Internal Error"; + case shaderc_compilation_status:: + shaderc_compilation_status_null_result_object: + return "Internal error. Null Result Object"; + case shaderc_compilation_status:: + shaderc_compilation_status_invalid_assembly: + return "Invalid Assembly"; + case shaderc_compilation_status:: + shaderc_compilation_status_validation_error: + return "Validation Error"; + case shaderc_compilation_status:: + shaderc_compilation_status_transformation_error: + return "Transformation Error"; + case shaderc_compilation_status:: + shaderc_compilation_status_configuration_error: + return "Configuration Error"; + } + return "Unknown Internal Error"; +} + static shaderc_shader_kind ToShaderCShaderKind(Compiler::SourceType type) { switch (type) { case Compiler::SourceType::kVertexShader: return shaderc_shader_kind::shaderc_vertex_shader; case Compiler::SourceType::kFragmentShader: return shaderc_shader_kind::shaderc_fragment_shader; + case Compiler::SourceType::kUnknown: + break; } - return shaderc_shader_kind::shaderc_vertex_shader; + return shaderc_shader_kind::shaderc_glsl_infer_from_source; } Compiler::Compiler(const fml::Mapping& source_mapping, - SourceOptions source_options) { + SourceOptions source_options) + : options_(source_options) { if (source_mapping.GetMapping() == nullptr) { + COMPILER_ERROR << "Could not read shader source."; + return; + } + + auto shader_kind = ToShaderCShaderKind(source_options.type); + + if (shader_kind == shaderc_shader_kind::shaderc_glsl_infer_from_source) { + COMPILER_ERROR << "Could not figure out shader stage."; return; } @@ -55,30 +106,118 @@ Compiler::Compiler(const fml::Mapping& source_mapping, shaderc_source_language::shaderc_source_language_glsl); options.SetTargetEnvironment( shaderc_target_env::shaderc_target_env_vulkan, - shaderc_env_version::shaderc_env_version_opengl_4_5); + shaderc_env_version::shaderc_env_version_vulkan_1_1); options.SetTargetSpirv(shaderc_spirv_version::shaderc_spirv_version_1_3); options.SetAutoBindUniforms(true); options.SetAutoMapLocations(true); options.SetIncluder(std::make_unique()); - shaderc::Compiler compiler; - if (!compiler.IsValid()) { + shaderc::Compiler spv_compiler; + if (!spv_compiler.IsValid()) { + COMPILER_ERROR << "Could not initialize the GLSL to SPIRV compiler."; + return; + } + + spv_result_ = std::make_shared( + spv_compiler.CompileGlslToSpv( + reinterpret_cast( + source_mapping.GetMapping()), // source_text + source_mapping.GetSize(), // source_text_size + shader_kind, // shader_kind + source_options.file_name.c_str(), // input_file_name + source_options.entry_point_name.c_str(), // entry_point_name + options // options + )); + + if (spv_result_->GetCompilationStatus() != + shaderc_compilation_status::shaderc_compilation_status_success) { + COMPILER_ERROR << "GLSL to SPIRV failed; " + << ShaderCErrorToString(spv_result_->GetCompilationStatus()) + << ". " << spv_result_->GetNumErrors() << " error(s) and " + << spv_result_->GetNumWarnings() << " warning(s)."; + if (spv_result_->GetNumErrors() > 0 || spv_result_->GetNumWarnings() > 0) { + COMPILER_ERROR_NO_PREFIX << spv_result_->GetErrorMessage(); + } + return; + } + + spirv_cross::CompilerMSL msl_compiler( + spv_result_->cbegin(), spv_result_->cend() - spv_result_->cbegin()); + msl_string_ = std::make_shared(msl_compiler.compile()); + + if (!msl_string_) { + COMPILER_ERROR << "Could not generate MSL from SPIRV"; return; } - result_ = compiler.CompileGlslToSpv( - reinterpret_cast( - source_mapping.GetMapping()), // source_text - source_mapping.GetSize(), // source_text_size - ToShaderCShaderKind(source_options.type), // shader_kind - source_options.file_name.c_str(), // input_file_name - source_options.entry_point_name.c_str(), // entry_point_name - options // options - ); is_valid_ = true; } Compiler::~Compiler() = default; +std::unique_ptr Compiler::GetSPIRVAssembly() const { + if (!spv_result_) { + return nullptr; + } + const auto data_length = + (spv_result_->cend() - spv_result_->cbegin()) * + sizeof(decltype(spv_result_)::element_type::element_type); + + return std::make_unique( + reinterpret_cast(spv_result_->cbegin()), data_length, + [result = spv_result_](auto, auto) mutable { result.reset(); }); +} + +std::unique_ptr Compiler::GetMSLShaderSource() const { + if (!msl_string_) { + return nullptr; + } + + return std::make_unique( + reinterpret_cast(msl_string_->c_str()), + msl_string_->length(), + [string = msl_string_](auto, auto) mutable { string.reset(); }); +} + +bool Compiler::IsValid() const { + return is_valid_; +} + +static bool StringEndWith(const std::string& string, + const std::string& suffix) { + if (suffix.size() > string.size()) { + return false; + } + + if (suffix.empty() || suffix.empty()) { + return false; + } + + return string.rfind(suffix) == (string.size() - suffix.size()); +} + +Compiler::SourceType Compiler::SourceTypeFromFileName( + const std::string& file_name) { + if (StringEndWith(file_name, ".vert")) { + return Compiler::SourceType::kVertexShader; + } + + if (StringEndWith(file_name, ".frag")) { + return Compiler::SourceType::kFragmentShader; + } + + return Compiler::SourceType::kUnknown; +} + +std::string Compiler::GetSourcePrefix() const { + std::stringstream stream; + stream << options_.file_name << ": "; + return stream.str(); +} + +std::string Compiler::GetErrorMessages() const { + return error_stream_.str(); +} + } // namespace compiler } // namespace impeller diff --git a/impeller/compiler/compiler.h b/impeller/compiler/compiler.h index 192ec68ffa65c..6d7647792c90e 100644 --- a/impeller/compiler/compiler.h +++ b/impeller/compiler/compiler.h @@ -4,11 +4,13 @@ #pragma once +#include #include #include "flutter/fml/macros.h" #include "flutter/fml/mapping.h" #include "shaderc/shaderc.hpp" +#include "third_party/spirv_cross/spirv_msl.hpp" namespace impeller { namespace compiler { @@ -16,6 +18,7 @@ namespace compiler { class Compiler { public: enum class SourceType { + kUnknown, kVertexShader, kFragmentShader, }; @@ -23,9 +26,15 @@ class Compiler { static SourceType SourceTypeFromFileName(const std::string& file_name); struct SourceOptions { - SourceType type = SourceType::kVertexShader; + SourceType type = SourceType::kUnknown; + std::shared_ptr working_directory; std::string file_name = "main.glsl"; std::string entry_point_name = "main"; + + SourceOptions() = default; + + SourceOptions(const std::string& file_name) + : type(SourceTypeFromFileName(file_name)), file_name(file_name) {} }; Compiler(const fml::Mapping& source_mapping, SourceOptions options); @@ -34,10 +43,21 @@ class Compiler { bool IsValid() const; + std::unique_ptr GetSPIRVAssembly() const; + + std::unique_ptr GetMSLShaderSource() const; + + std::string GetErrorMessages() const; + private: - shaderc::SpvCompilationResult result_; + SourceOptions options_; + std::shared_ptr spv_result_; + std::shared_ptr msl_string_; + std::stringstream error_stream_; bool is_valid_ = false; + std::string GetSourcePrefix() const; + FML_DISALLOW_COPY_AND_ASSIGN(Compiler); }; diff --git a/impeller/compiler/compiler_unittests.cc b/impeller/compiler/compiler_unittests.cc new file mode 100644 index 0000000000000..a0cc4f758e617 --- /dev/null +++ b/impeller/compiler/compiler_unittests.cc @@ -0,0 +1,34 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "flutter/impeller/compiler/compiler.h" +#include "flutter/testing/testing.h" +#include "gtest/gtest.h" + +namespace impeller { +namespace compiler { +namespace testing { + +TEST(CompilerTest, ShaderKindMatchingIsSuccessful) { + ASSERT_EQ(Compiler::SourceTypeFromFileName("hello.vert"), + Compiler::SourceType::kVertexShader); + ASSERT_EQ(Compiler::SourceTypeFromFileName("hello.frag"), + Compiler::SourceType::kFragmentShader); + ASSERT_EQ(Compiler::SourceTypeFromFileName("hello.msl"), + Compiler::SourceType::kUnknown); + ASSERT_EQ(Compiler::SourceTypeFromFileName("hello.glsl"), + Compiler::SourceType::kUnknown); +} + +TEST(CompilerTest, CanCompileSample) { + auto fixture = flutter::testing::OpenFixtureAsMapping("sample.frag"); + ASSERT_NE(fixture->GetMapping(), nullptr); + Compiler::SourceOptions options("sample.frag"); + Compiler compiler(*fixture.get(), options); + ASSERT_TRUE(compiler.IsValid()); +} + +} // namespace testing +} // namespace compiler +} // namespace impeller diff --git a/impeller/compiler/logger.h b/impeller/compiler/logger.h new file mode 100644 index 0000000000000..cf4ab47f2494a --- /dev/null +++ b/impeller/compiler/logger.h @@ -0,0 +1,40 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#pragma once + +#include +#include + +#include "flutter/fml/logging.h" +#include "flutter/fml/macros.h" + +namespace impeller { +namespace compiler { + +class AutoLogger { + public: + AutoLogger(std::stringstream& logger) : logger_(logger) {} + + ~AutoLogger() { + logger_ << std::endl; + logger_.flush(); + + FML_DLOG(INFO) << logger_.str(); + } + + template + AutoLogger& operator<<(const T& object) { + logger_ << object; + return *this; + } + + private: + std::stringstream& logger_; + + FML_DISALLOW_COPY_AND_ASSIGN(AutoLogger); +}; + +} // namespace compiler +} // namespace impeller From 23c7b40bc9e4e16151dc934f0a83400b65a265cb Mon Sep 17 00:00:00 2001 From: Chinmay Garde Date: Tue, 11 May 2021 14:47:37 -0700 Subject: [PATCH 027/433] Implement includer interface. --- impeller/compiler/BUILD.gn | 5 +- impeller/compiler/compiler.cc | 82 +++++++++++++++++++++++-- impeller/compiler/compiler_unittests.cc | 9 +++ impeller/compiler/fixtures/sample.frag | 7 +-- impeller/compiler/fixtures/types.h | 6 ++ 5 files changed, 97 insertions(+), 12 deletions(-) create mode 100644 impeller/compiler/fixtures/types.h diff --git a/impeller/compiler/BUILD.gn b/impeller/compiler/BUILD.gn index 68c7e50f33965..b1b3b4b24b0b2 100644 --- a/impeller/compiler/BUILD.gn +++ b/impeller/compiler/BUILD.gn @@ -28,7 +28,10 @@ executable("compiler") { } test_fixtures("compiler_test_fixtures") { - fixtures = [ "fixtures/sample.frag" ] + fixtures = [ + "fixtures/sample.frag", + "fixtures/types.h", + ] } executable("compiler_unittests") { diff --git a/impeller/compiler/compiler.cc b/impeller/compiler/compiler.cc index 0d209fa1589e8..0c2c2bda59dae 100644 --- a/impeller/compiler/compiler.cc +++ b/impeller/compiler/compiler.cc @@ -16,9 +16,18 @@ namespace compiler { #define COMPILER_ERROR_NO_PREFIX ::impeller::compiler::AutoLogger(error_stream_) +struct IncluderData { + std::string file_name; + std::unique_ptr mapping; + + IncluderData(std::string p_file_name, std::unique_ptr p_mapping) + : file_name(std::move(p_file_name)), mapping(std::move(p_mapping)) {} +}; + class Includer final : public shaderc::CompileOptions::IncluderInterface { public: - Includer() = default; + Includer(std::shared_ptr working_directory) + : working_directory_(std::move(working_directory)) {} // |shaderc::CompileOptions::IncluderInterface| ~Includer() override = default; @@ -28,16 +37,54 @@ class Includer final : public shaderc::CompileOptions::IncluderInterface { shaderc_include_type type, const char* requesting_source, size_t include_depth) override { - FML_CHECK(false); - return nullptr; + auto result = std::make_unique(); + + // Default initialize to failed inclusion. + result->source_name = ""; + result->source_name_length = 0; + + constexpr const char* kFileNotFoundMessage = "Included file not found."; + result->content = kFileNotFoundMessage; + result->content_length = ::strlen(kFileNotFoundMessage); + result->user_data = nullptr; + + if (!working_directory_ || !working_directory_->is_valid()) { + return result.release(); + } + + if (requested_source == nullptr) { + return result.release(); + } + + auto file = + fml::FileMapping::CreateReadOnly(*working_directory_, requested_source); + + if (!file || file->GetMapping() == nullptr) { + return result.release(); + } + + auto includer_data = + std::make_unique(requested_source, std::move(file)); + + result->source_name = includer_data->file_name.c_str(); + result->source_name_length = includer_data->file_name.length(); + result->content = reinterpret_castcontent)>( + includer_data->mapping->GetMapping()); + result->content_length = includer_data->mapping->GetSize(); + result->user_data = includer_data.release(); + + return result.release(); } // |shaderc::CompileOptions::IncluderInterface| void ReleaseInclude(shaderc_include_result* data) override { - FML_CHECK(false); + delete reinterpret_cast(data->user_data); + delete data; } private: + std::shared_ptr working_directory_; + FML_DISALLOW_COPY_AND_ASSIGN(Includer); }; @@ -104,13 +151,14 @@ Compiler::Compiler(const fml::Mapping& source_mapping, shaderc_optimization_level::shaderc_optimization_level_size); options.SetSourceLanguage( shaderc_source_language::shaderc_source_language_glsl); + options.SetForcedVersionProfile(450, shaderc_profile::shaderc_profile_core); options.SetTargetEnvironment( shaderc_target_env::shaderc_target_env_vulkan, shaderc_env_version::shaderc_env_version_vulkan_1_1); options.SetTargetSpirv(shaderc_spirv_version::shaderc_spirv_version_1_3); options.SetAutoBindUniforms(true); options.SetAutoMapLocations(true); - options.SetIncluder(std::make_unique()); + options.SetIncluder(std::make_unique(options_.working_directory)); shaderc::Compiler spv_compiler; if (!spv_compiler.IsValid()) { @@ -118,6 +166,7 @@ Compiler::Compiler(const fml::Mapping& source_mapping, return; } + // SPIRV Generation. spv_result_ = std::make_shared( spv_compiler.CompileGlslToSpv( reinterpret_cast( @@ -141,8 +190,16 @@ Compiler::Compiler(const fml::Mapping& source_mapping, return; } + // MSL Generation. spirv_cross::CompilerMSL msl_compiler( spv_result_->cbegin(), spv_result_->cend() - spv_result_->cbegin()); + + { + spirv_cross::CompilerMSL::Options msl_options; + msl_options.platform = spirv_cross::CompilerMSL::Options::Platform::macOS; + msl_compiler.set_msl_options(msl_options); + } + msl_string_ = std::make_shared(msl_compiler.compile()); if (!msl_string_) { @@ -150,6 +207,21 @@ Compiler::Compiler(const fml::Mapping& source_mapping, return; } + const auto resources = msl_compiler.get_shader_resources(); + + for (const auto& stage_input : resources.stage_inputs) { + FML_LOG(ERROR) << stage_input.name; + } + + for (const auto& stage_output : resources.stage_outputs) { + FML_LOG(ERROR) << stage_output.name; + } + + for (const auto& uniform_buffer : resources.uniform_buffers) { + FML_LOG(ERROR) << uniform_buffer.name; + auto type = msl_compiler.get_type(uniform_buffer.base_type_id); + } + is_valid_ = true; } diff --git a/impeller/compiler/compiler_unittests.cc b/impeller/compiler/compiler_unittests.cc index a0cc4f758e617..0939936459090 100644 --- a/impeller/compiler/compiler_unittests.cc +++ b/impeller/compiler/compiler_unittests.cc @@ -25,8 +25,17 @@ TEST(CompilerTest, CanCompileSample) { auto fixture = flutter::testing::OpenFixtureAsMapping("sample.frag"); ASSERT_NE(fixture->GetMapping(), nullptr); Compiler::SourceOptions options("sample.frag"); + options.working_directory = std::make_shared( + flutter::testing::OpenFixturesDirectory()); Compiler compiler(*fixture.get(), options); ASSERT_TRUE(compiler.IsValid()); + + auto desktop = fml::OpenDirectory("/Users/chinmaygarde/Desktop", false, + fml::FilePermission::kRead); + fml::WriteAtomically(desktop, "sample.frag.spirv", + *compiler.GetSPIRVAssembly()); + fml::WriteAtomically(desktop, "sample.frag.metal", + *compiler.GetMSLShaderSource()); } } // namespace testing diff --git a/impeller/compiler/fixtures/sample.frag b/impeller/compiler/fixtures/sample.frag index 4c6fa55dfb044..21aaed4215e53 100644 --- a/impeller/compiler/fixtures/sample.frag +++ b/impeller/compiler/fixtures/sample.frag @@ -1,10 +1,5 @@ -#version 450 -struct Hello { - vec4 stuff; - vec2 more_stuff; - float dunno; -}; +#include "types.h" uniform Uniforms { Hello myHello; diff --git a/impeller/compiler/fixtures/types.h b/impeller/compiler/fixtures/types.h new file mode 100644 index 0000000000000..e45148586b85a --- /dev/null +++ b/impeller/compiler/fixtures/types.h @@ -0,0 +1,6 @@ + +struct Hello { + vec4 stuff; + vec2 more_stuff; + float dunno; +}; From 4c43a7c7b04cb0c3e053c3e9fdeb18480a4905ac Mon Sep 17 00:00:00 2001 From: Chinmay Garde Date: Tue, 11 May 2021 19:51:02 -0700 Subject: [PATCH 028/433] Wire up reflection. --- impeller/compiler/BUILD.gn | 2 + impeller/compiler/compiler.cc | 25 +++++------ impeller/compiler/compiler.h | 2 + impeller/compiler/fixtures/sample.frag | 2 +- impeller/compiler/fixtures/types.h | 2 + impeller/compiler/reflector.cc | 62 ++++++++++++++++++++++++++ impeller/compiler/reflector.h | 28 ++++++++++++ 7 files changed, 109 insertions(+), 14 deletions(-) create mode 100644 impeller/compiler/reflector.cc create mode 100644 impeller/compiler/reflector.h diff --git a/impeller/compiler/BUILD.gn b/impeller/compiler/BUILD.gn index b1b3b4b24b0b2..640563aabea7f 100644 --- a/impeller/compiler/BUILD.gn +++ b/impeller/compiler/BUILD.gn @@ -9,6 +9,8 @@ source_set("compiler_lib") { "compiler.cc", "compiler.h", "logger.h", + "reflector.cc", + "reflector.h", ] public_deps = [ diff --git a/impeller/compiler/compiler.cc b/impeller/compiler/compiler.cc index 0c2c2bda59dae..1037af643f6d7 100644 --- a/impeller/compiler/compiler.cc +++ b/impeller/compiler/compiler.cc @@ -146,9 +146,14 @@ Compiler::Compiler(const fml::Mapping& source_mapping, } shaderc::CompileOptions options; + + // Make sure reflection is as effective as possible. The generated shaders + // will be processed later by backend specific compilers. So optimizations + // here are irrelevant and get in the way of generating reflection code. options.SetGenerateDebugInfo(); options.SetOptimizationLevel( - shaderc_optimization_level::shaderc_optimization_level_size); + shaderc_optimization_level::shaderc_optimization_level_zero); + options.SetSourceLanguage( shaderc_source_language::shaderc_source_language_glsl); options.SetForcedVersionProfile(450, shaderc_profile::shaderc_profile_core); @@ -156,8 +161,10 @@ Compiler::Compiler(const fml::Mapping& source_mapping, shaderc_target_env::shaderc_target_env_vulkan, shaderc_env_version::shaderc_env_version_vulkan_1_1); options.SetTargetSpirv(shaderc_spirv_version::shaderc_spirv_version_1_3); + options.SetAutoBindUniforms(true); options.SetAutoMapLocations(true); + options.SetIncluder(std::make_unique(options_.working_directory)); shaderc::Compiler spv_compiler; @@ -207,19 +214,11 @@ Compiler::Compiler(const fml::Mapping& source_mapping, return; } - const auto resources = msl_compiler.get_shader_resources(); + reflector_ = std::make_unique(msl_compiler); - for (const auto& stage_input : resources.stage_inputs) { - FML_LOG(ERROR) << stage_input.name; - } - - for (const auto& stage_output : resources.stage_outputs) { - FML_LOG(ERROR) << stage_output.name; - } - - for (const auto& uniform_buffer : resources.uniform_buffers) { - FML_LOG(ERROR) << uniform_buffer.name; - auto type = msl_compiler.get_type(uniform_buffer.base_type_id); + if (!reflector_->IsValid()) { + COMPILER_ERROR << "Could not complete reflection on generated shader."; + return; } is_valid_ = true; diff --git a/impeller/compiler/compiler.h b/impeller/compiler/compiler.h index 6d7647792c90e..87d0e59b12129 100644 --- a/impeller/compiler/compiler.h +++ b/impeller/compiler/compiler.h @@ -9,6 +9,7 @@ #include "flutter/fml/macros.h" #include "flutter/fml/mapping.h" +#include "flutter/impeller/compiler/reflector.h" #include "shaderc/shaderc.hpp" #include "third_party/spirv_cross/spirv_msl.hpp" @@ -54,6 +55,7 @@ class Compiler { std::shared_ptr spv_result_; std::shared_ptr msl_string_; std::stringstream error_stream_; + std::unique_ptr reflector_; bool is_valid_ = false; std::string GetSourcePrefix() const; diff --git a/impeller/compiler/fixtures/sample.frag b/impeller/compiler/fixtures/sample.frag index 21aaed4215e53..7c20b20b57f42 100644 --- a/impeller/compiler/fixtures/sample.frag +++ b/impeller/compiler/fixtures/sample.frag @@ -14,5 +14,5 @@ in vec2 inTextureCoord; out vec4 outColor; void main() { - outColor = texture(textureSampler, inTextureCoord * myHello.more_stuff); + outColor = texture(textureSampler, inTextureCoord * goodbyeHello.yet_more_stuff); } diff --git a/impeller/compiler/fixtures/types.h b/impeller/compiler/fixtures/types.h index e45148586b85a..448c3310722d8 100644 --- a/impeller/compiler/fixtures/types.h +++ b/impeller/compiler/fixtures/types.h @@ -3,4 +3,6 @@ struct Hello { vec4 stuff; vec2 more_stuff; float dunno; + float this_bit_is_not_used; + vec2 yet_more_stuff; }; diff --git a/impeller/compiler/reflector.cc b/impeller/compiler/reflector.cc new file mode 100644 index 0000000000000..191d56825ea62 --- /dev/null +++ b/impeller/compiler/reflector.cc @@ -0,0 +1,62 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "flutter/impeller/compiler/reflector.h" + +#include "flutter/fml/logging.h" + +namespace impeller { +namespace compiler { + +std::string SPIRVTypeToString(const spirv_cross::CompilerMSL& compiler, + const spirv_cross::SPIRType& type, + size_t indent = 0) { + std::string pad(indent, '-'); + + std::stringstream stream; + stream << std::endl; + + // stream << pad << "Type ID: " << type.identifier << std::endl; + + if (auto identifier = compiler.get_name(type.basetype); !identifier.empty()) { + stream << pad << "OpName: " << identifier << std::endl; + } + + for (const auto& member : type.member_types) { + stream << SPIRVTypeToString(compiler, compiler.get_type(member), + indent += 2); + } + + return stream.str(); +} + +Reflector::Reflector(const spirv_cross::CompilerMSL& compiler) { + auto resources = compiler.get_shader_resources(); + + for (const auto& stage_input : resources.stage_inputs) { + FML_LOG(ERROR) << stage_input.name; + } + + for (const auto& stage_output : resources.stage_outputs) { + FML_LOG(ERROR) << stage_output.name; + } + + for (const auto& uniform_buffer : resources.uniform_buffers) { + FML_LOG(ERROR) << uniform_buffer.name; + auto type = compiler.get_type(uniform_buffer.type_id); + + FML_LOG(ERROR) << SPIRVTypeToString(compiler, type); + } + + is_valid_ = true; +} + +Reflector::~Reflector() = default; + +bool Reflector::IsValid() const { + return is_valid_; +} + +} // namespace compiler +} // namespace impeller diff --git a/impeller/compiler/reflector.h b/impeller/compiler/reflector.h new file mode 100644 index 0000000000000..3d91130a7cc5d --- /dev/null +++ b/impeller/compiler/reflector.h @@ -0,0 +1,28 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#pragma once + +#include "flutter/fml/macros.h" +#include "third_party/spirv_cross/spirv_msl.hpp" + +namespace impeller { +namespace compiler { + +class Reflector { + public: + Reflector(const spirv_cross::CompilerMSL& compiler); + + ~Reflector(); + + bool IsValid() const; + + private: + bool is_valid_ = false; + + FML_DISALLOW_COPY_AND_ASSIGN(Reflector); +}; + +} // namespace compiler +} // namespace impeller From 2ef436e1f63e9db5848ce55266dc197ec6625338 Mon Sep 17 00:00:00 2001 From: Chinmay Garde Date: Wed, 12 May 2021 12:19:02 -0700 Subject: [PATCH 029/433] Wire up rudimentary reflection. --- impeller/compiler/BUILD.gn | 2 + impeller/compiler/compiler.cc | 2 +- impeller/compiler/compiler_unittests.cc | 45 +++++-- impeller/compiler/fixtures/sample.frag | 4 +- impeller/compiler/fixtures/sample.vert | 23 ++++ impeller/compiler/reflector.cc | 117 ++++++++++++++---- impeller/compiler/reflector.h | 5 +- impeller/impeller/BUILD.gn | 7 ++ .../impeller/compositor/shaders/box/box.frag | 11 +- .../impeller/compositor/shaders/box/box.vert | 11 +- 10 files changed, 167 insertions(+), 60 deletions(-) create mode 100644 impeller/compiler/fixtures/sample.vert diff --git a/impeller/compiler/BUILD.gn b/impeller/compiler/BUILD.gn index 640563aabea7f..4ec2db0dfb12c 100644 --- a/impeller/compiler/BUILD.gn +++ b/impeller/compiler/BUILD.gn @@ -18,6 +18,7 @@ source_set("compiler_lib") { "//flutter/runtime:libdart", "//third_party/shaderc_flutter", "//third_party/spirv_cross_flutter", + "//third_party/spirv_reflect_flutter", ] } @@ -32,6 +33,7 @@ executable("compiler") { test_fixtures("compiler_test_fixtures") { fixtures = [ "fixtures/sample.frag", + "fixtures/sample.vert", "fixtures/types.h", ] } diff --git a/impeller/compiler/compiler.cc b/impeller/compiler/compiler.cc index 1037af643f6d7..0ed0886b7f037 100644 --- a/impeller/compiler/compiler.cc +++ b/impeller/compiler/compiler.cc @@ -214,7 +214,7 @@ Compiler::Compiler(const fml::Mapping& source_mapping, return; } - reflector_ = std::make_unique(msl_compiler); + reflector_ = std::make_unique(*GetSPIRVAssembly()); if (!reflector_->IsValid()) { COMPILER_ERROR << "Could not complete reflection on generated shader."; diff --git a/impeller/compiler/compiler_unittests.cc b/impeller/compiler/compiler_unittests.cc index 0939936459090..4ff37a35f34a5 100644 --- a/impeller/compiler/compiler_unittests.cc +++ b/impeller/compiler/compiler_unittests.cc @@ -10,7 +10,35 @@ namespace impeller { namespace compiler { namespace testing { -TEST(CompilerTest, ShaderKindMatchingIsSuccessful) { +class CompilerTest : public ::testing::Test { + public: + CompilerTest() + : directory_(fml::OpenDirectory("/Users/chinmaygarde/Desktop/shaders", + false, + fml::FilePermission::kRead)) { + FML_CHECK(directory_.is_valid()); + } + + ~CompilerTest() {} + + void WriteCompilerIntermediates(const Compiler& compiler, + const std::string& base_name) { + ASSERT_TRUE(compiler.IsValid()); + fml::WriteAtomically(directory_, + std::string{base_name + std::string{".spirv"}}.c_str(), + *compiler.GetSPIRVAssembly()); + fml::WriteAtomically(directory_, + std::string{base_name + std::string{".metal"}}.c_str(), + *compiler.GetMSLShaderSource()); + } + + private: + fml::UniqueFD directory_; + + FML_DISALLOW_COPY_AND_ASSIGN(CompilerTest); +}; + +TEST_F(CompilerTest, ShaderKindMatchingIsSuccessful) { ASSERT_EQ(Compiler::SourceTypeFromFileName("hello.vert"), Compiler::SourceType::kVertexShader); ASSERT_EQ(Compiler::SourceTypeFromFileName("hello.frag"), @@ -21,21 +49,16 @@ TEST(CompilerTest, ShaderKindMatchingIsSuccessful) { Compiler::SourceType::kUnknown); } -TEST(CompilerTest, CanCompileSample) { - auto fixture = flutter::testing::OpenFixtureAsMapping("sample.frag"); +TEST_F(CompilerTest, CanCompileSample) { + constexpr const char* kShaderFixtureName = "sample.frag"; + auto fixture = flutter::testing::OpenFixtureAsMapping(kShaderFixtureName); ASSERT_NE(fixture->GetMapping(), nullptr); - Compiler::SourceOptions options("sample.frag"); + Compiler::SourceOptions options(kShaderFixtureName); options.working_directory = std::make_shared( flutter::testing::OpenFixturesDirectory()); Compiler compiler(*fixture.get(), options); ASSERT_TRUE(compiler.IsValid()); - - auto desktop = fml::OpenDirectory("/Users/chinmaygarde/Desktop", false, - fml::FilePermission::kRead); - fml::WriteAtomically(desktop, "sample.frag.spirv", - *compiler.GetSPIRVAssembly()); - fml::WriteAtomically(desktop, "sample.frag.metal", - *compiler.GetMSLShaderSource()); + WriteCompilerIntermediates(compiler, kShaderFixtureName); } } // namespace testing diff --git a/impeller/compiler/fixtures/sample.frag b/impeller/compiler/fixtures/sample.frag index 7c20b20b57f42..eef7915f18e76 100644 --- a/impeller/compiler/fixtures/sample.frag +++ b/impeller/compiler/fixtures/sample.frag @@ -9,9 +9,9 @@ uniform Uniforms { uniform sampler2D textureSampler; -in vec2 inTextureCoord; +layout(location = 3) in vec2 inTextureCoord; -out vec4 outColor; +layout(location = 4) out vec4 outColor; void main() { outColor = texture(textureSampler, inTextureCoord * goodbyeHello.yet_more_stuff); diff --git a/impeller/compiler/fixtures/sample.vert b/impeller/compiler/fixtures/sample.vert new file mode 100644 index 0000000000000..727406ac868d1 --- /dev/null +++ b/impeller/compiler/fixtures/sample.vert @@ -0,0 +1,23 @@ +#version 450 +#extension GL_ARB_separate_shader_objects : enable + +// Uniforms + +layout(set = 0, binding = 0) uniform UniformBufferObject { + mat4 mvp; +} ubo; + +// In + +layout(location = 0) in vec3 inPosition; +layout(location = 1) in vec3 inNormal; +layout(location = 2) in vec2 inTextureCoords; + +// Out + +layout(location = 0) out vec2 outTextureCoords; + +void main() { + gl_Position = ubo.mvp * vec4(inPosition, 1.0); + outTextureCoords = inTextureCoords; +} diff --git a/impeller/compiler/reflector.cc b/impeller/compiler/reflector.cc index 191d56825ea62..cc1d82c404fad 100644 --- a/impeller/compiler/reflector.cc +++ b/impeller/compiler/reflector.cc @@ -4,49 +4,114 @@ #include "flutter/impeller/compiler/reflector.h" +#include + #include "flutter/fml/logging.h" namespace impeller { namespace compiler { -std::string SPIRVTypeToString(const spirv_cross::CompilerMSL& compiler, - const spirv_cross::SPIRType& type, - size_t indent = 0) { - std::string pad(indent, '-'); - +std::string SpvReflectInterfaceVariableToString( + const SpvReflectInterfaceVariable& var) { std::stringstream stream; - stream << std::endl; - - // stream << pad << "Type ID: " << type.identifier << std::endl; - - if (auto identifier = compiler.get_name(type.basetype); !identifier.empty()) { - stream << pad << "OpName: " << identifier << std::endl; + stream << "--- Interface Variable" << std::endl; + stream << "spirv_id=" << var.spirv_id << std::endl; + if (var.name) { + stream << "name=" << var.name << std::endl; } - - for (const auto& member : type.member_types) { - stream << SPIRVTypeToString(compiler, compiler.get_type(member), - indent += 2); + stream << "location=" << var.location << std::endl; + stream << "storage_class=" << var.storage_class << std::endl; + if (var.semantic) { + stream << "semantic=" << var.semantic << std::endl; } - + stream << "decoration_flags=" << var.decoration_flags << std::endl; + stream << "built_in=" << var.built_in << std::endl; + // stream << "numeric=" << var.numeric << std::endl; + // stream << "array=" << var.array << std::endl; + stream << "member_count=" << var.member_count << std::endl; + // stream << "members=" << var.members << std::endl; + stream << "format=" << var.format << std::endl; + stream << "type_description=" << var.type_description << std::endl; return stream.str(); } -Reflector::Reflector(const spirv_cross::CompilerMSL& compiler) { - auto resources = compiler.get_shader_resources(); +std::string SpvReflectDescriptorBindingToString( + const SpvReflectDescriptorBinding& des) { + std::stringstream stream; + stream << "--- Descriptor Set Binding" << std::endl; + stream << "spirv_id=" << des.spirv_id << std::endl; + if (des.name) { + stream << "name=" << des.name << std::endl; + } + stream << "binding=" << des.binding << std::endl; + stream << "input_attachment_index=" << des.input_attachment_index + << std::endl; + stream << "set=" << des.set << std::endl; + stream << "descriptor_type=" << des.descriptor_type << std::endl; + stream << "resource_type=" << des.resource_type << std::endl; + // stream << "image=" << des.image << std::endl; + // stream << "block=" << des.block << std::endl; + // stream << "array=" << des.array << std::endl; + stream << "count=" << des.count << std::endl; + stream << "accessed=" << des.accessed << std::endl; + stream << "uav_counter_id=" << des.uav_counter_id << std::endl; + stream << "uav_counter_binding=" << des.uav_counter_binding << std::endl; + stream << "type_description=" << des.type_description << std::endl; + return stream.str(); +} - for (const auto& stage_input : resources.stage_inputs) { - FML_LOG(ERROR) << stage_input.name; +Reflector::Reflector(const fml::Mapping& spirv_binary) { + if (spirv_binary.GetMapping() == nullptr) { + return; } - for (const auto& stage_output : resources.stage_outputs) { - FML_LOG(ERROR) << stage_output.name; + spv_reflect::ShaderModule module(spirv_binary.GetSize(), + spirv_binary.GetMapping()); + + if (module.GetResult() != SpvReflectResult::SPV_REFLECT_RESULT_SUCCESS) { + return; } - for (const auto& uniform_buffer : resources.uniform_buffers) { - FML_LOG(ERROR) << uniform_buffer.name; - auto type = compiler.get_type(uniform_buffer.type_id); + FML_LOG(ERROR) << "~~~~~~~~~~~~~ Interface Variables ~~~~~~~~~~~~~"; + + { + uint32_t count = 0; + if (module.EnumerateInterfaceVariables(&count, nullptr) != + SpvReflectResult::SPV_REFLECT_RESULT_SUCCESS) { + return; + } + + std::vector input_variables; + input_variables.resize(count); + if (module.EnumerateInterfaceVariables(&count, input_variables.data()) != + SpvReflectResult::SPV_REFLECT_RESULT_SUCCESS) { + return; + } + + for (const auto& input_variable : input_variables) { + FML_LOG(ERROR) << SpvReflectInterfaceVariableToString(*input_variable); + } + } - FML_LOG(ERROR) << SPIRVTypeToString(compiler, type); + FML_LOG(ERROR) << "~~~~~~~~~~~~~ Descriptor Bindings ~~~~~~~~~~~~~"; + + { + uint32_t count = 0; + if (module.EnumerateDescriptorBindings(&count, nullptr) != + SpvReflectResult::SPV_REFLECT_RESULT_SUCCESS) { + return; + } + + std::vector input_variables; + input_variables.resize(count); + if (module.EnumerateDescriptorBindings(&count, input_variables.data()) != + SpvReflectResult::SPV_REFLECT_RESULT_SUCCESS) { + return; + } + + for (const auto& input_variable : input_variables) { + FML_LOG(ERROR) << SpvReflectDescriptorBindingToString(*input_variable); + } } is_valid_ = true; diff --git a/impeller/compiler/reflector.h b/impeller/compiler/reflector.h index 3d91130a7cc5d..185d5580966ea 100644 --- a/impeller/compiler/reflector.h +++ b/impeller/compiler/reflector.h @@ -5,14 +5,15 @@ #pragma once #include "flutter/fml/macros.h" -#include "third_party/spirv_cross/spirv_msl.hpp" +#include "flutter/fml/mapping.h" +#include "third_party/spirv_reflect/spirv_reflect.h" namespace impeller { namespace compiler { class Reflector { public: - Reflector(const spirv_cross::CompilerMSL& compiler); + Reflector(const fml::Mapping& spirv_binary); ~Reflector(); diff --git a/impeller/impeller/BUILD.gn b/impeller/impeller/BUILD.gn index 4807e1a4d87a8..414a45dfef10d 100644 --- a/impeller/impeller/BUILD.gn +++ b/impeller/impeller/BUILD.gn @@ -8,6 +8,13 @@ config("impeller_config") { include_dirs = [ ".." ] } +impeller_shaders("shaders") { + shaders = [ + "compositor/shaders/box/box.frag", + "compositor/shaders/box/box.vert", + ] +} + source_set("impeller") { public_configs = [ ":impeller_config" ] diff --git a/impeller/impeller/compositor/shaders/box/box.frag b/impeller/impeller/compositor/shaders/box/box.frag index 69036b6cf4012..1b9f713acecd0 100644 --- a/impeller/impeller/compositor/shaders/box/box.frag +++ b/impeller/impeller/compositor/shaders/box/box.frag @@ -1,12 +1,5 @@ -#version 450 -#extension GL_ARB_separate_shader_objects : enable - -layout(binding = 1) uniform sampler2D textureSampler; - -layout(location = 0) in vec2 inTextureCoord; - -layout(location = 0) out vec4 outColor; +out vec4 outColor; void main() { - outColor = texture(textureSampler, inTextureCoord); + outColor = vec4(1.0); } diff --git a/impeller/impeller/compositor/shaders/box/box.vert b/impeller/impeller/compositor/shaders/box/box.vert index ae31781c1b71f..76733c345ddbb 100644 --- a/impeller/impeller/compositor/shaders/box/box.vert +++ b/impeller/impeller/compositor/shaders/box/box.vert @@ -1,18 +1,11 @@ -#version 450 -#extension GL_ARB_separate_shader_objects : enable - -layout(binding = 0) uniform UniformBufferObject { +uniform UniformBufferObject { mat4 model; mat4 view; mat4 projection; } ubo; -layout(location = 0) in vec3 inPosition; -layout(location = 1) in vec2 inTextureCoord; - -layout(location = 0) out vec2 outTextureCoord; +in vec3 inPosition; void main() { gl_Position = ubo.projection * ubo.view * ubo.model * vec4(inPosition, 1.0); - outTextureCoord = inTextureCoord; } From 6d6042a183618a03ca10d6ca4fc353a10a60fc5b Mon Sep 17 00:00:00 2001 From: Chinmay Garde Date: Wed, 12 May 2021 14:56:07 -0700 Subject: [PATCH 030/433] Wire up compiler frontend. --- impeller/compiler/BUILD.gn | 10 +++-- impeller/compiler/impellerc_main.cc | 53 ++++++++++++++++++++++++- impeller/compiler/switches.cc | 59 ++++++++++++++++++++++++++++ impeller/compiler/switches.h | 35 +++++++++++++++++ impeller/compiler/tools/compiler.gni | 28 ++++++++++++- impeller/impeller/BUILD.gn | 2 + 6 files changed, 182 insertions(+), 5 deletions(-) create mode 100644 impeller/compiler/switches.cc create mode 100644 impeller/compiler/switches.h diff --git a/impeller/compiler/BUILD.gn b/impeller/compiler/BUILD.gn index 4ec2db0dfb12c..aaacc2a27c44b 100644 --- a/impeller/compiler/BUILD.gn +++ b/impeller/compiler/BUILD.gn @@ -11,6 +11,8 @@ source_set("compiler_lib") { "logger.h", "reflector.cc", "reflector.h", + "switches.cc", + "switches.h", ] public_deps = [ @@ -22,14 +24,16 @@ source_set("compiler_lib") { ] } -executable("compiler") { - output_name = "impellerc" - +executable("impellerc") { sources = [ "impellerc_main.cc" ] deps = [ ":compiler_lib" ] } +group("compiler") { + deps = [ ":impellerc" ] +} + test_fixtures("compiler_test_fixtures") { fixtures = [ "fixtures/sample.frag", diff --git a/impeller/compiler/impellerc_main.cc b/impeller/compiler/impellerc_main.cc index 619bdd212f30f..822bc4c025636 100644 --- a/impeller/compiler/impellerc_main.cc +++ b/impeller/compiler/impellerc_main.cc @@ -3,14 +3,65 @@ // found in the LICENSE file. #include "flutter/fml/command_line.h" +#include "flutter/fml/file.h" #include "flutter/fml/macros.h" +#include "flutter/fml/mapping.h" +#include "flutter/impeller/compiler/compiler.h" +#include "flutter/impeller/compiler/switches.h" #include "third_party/shaderc/libshaderc/include/shaderc/shaderc.hpp" namespace impeller { namespace compiler { bool Main(const fml::CommandLine& command_line) { - return false; + if (command_line.HasOption("help")) { + Switches::PrintHelp(std::cout); + return true; + } + + Switches switches(command_line); + if (!switches.AreValid(std::cerr)) { + std::cerr << "Invalid flags specified." << std::endl; + Switches::PrintHelp(std::cerr); + return false; + } + + auto source_file_mapping = fml::FileMapping::CreateReadOnly( + *switches.working_directory, switches.source_file_name); + if (!source_file_mapping) { + std::cerr << "Could not open input file." << std::endl; + return false; + } + + Compiler::SourceOptions options; + options.type = Compiler::SourceTypeFromFileName(switches.source_file_name); + options.working_directory = switches.working_directory; + options.file_name = switches.source_file_name; + + Compiler compiler(*source_file_mapping, options); + if (!compiler.IsValid()) { + std::cerr << "Compilation failed." << std::endl; + std::cerr << compiler.GetErrorMessages() << std::endl; + return false; + } + + if (!fml::WriteAtomically(*switches.working_directory, + switches.spirv_file_name.c_str(), + *compiler.GetSPIRVAssembly())) { + std::cerr << "Could not write file to " << switches.spirv_file_name + << std::endl; + return false; + } + + if (!fml::WriteAtomically(*switches.working_directory, + switches.metal_file_name.c_str(), + *compiler.GetMSLShaderSource())) { + std::cerr << "Could not write file to " << switches.spirv_file_name + << std::endl; + return false; + } + + return true; } } // namespace compiler diff --git a/impeller/compiler/switches.cc b/impeller/compiler/switches.cc new file mode 100644 index 0000000000000..5cb06593d00f1 --- /dev/null +++ b/impeller/compiler/switches.cc @@ -0,0 +1,59 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "flutter/impeller/compiler/switches.h" + +#include + +#include "flutter/fml/file.h" + +namespace impeller { +namespace compiler { + +void Switches::PrintHelp(std::ostream& stream) { + stream << std::endl << "Valid Argument are:" << std::endl; + stream << "--input=" << std::endl; + stream << "--metal=" << std::endl; + stream << "--spirv=" << std::endl; +} + +Switches::Switches() = default; + +Switches::~Switches() = default; + +Switches::Switches(const fml::CommandLine& command_line) + : working_directory(std::make_shared( + fml::OpenDirectory(std::filesystem::current_path().native().c_str(), + false, // create if necessary, + fml::FilePermission::kRead))), + source_file_name(command_line.GetOptionValueWithDefault("input", "")), + metal_file_name(command_line.GetOptionValueWithDefault("metal", "")), + spirv_file_name(command_line.GetOptionValueWithDefault("spirv", "")) {} + +bool Switches::AreValid(std::ostream& explain) const { + bool valid = true; + if (!working_directory || !working_directory->is_valid()) { + explain << "Could not figure out working directory." << std::endl; + valid = false; + } + + if (source_file_name.empty()) { + explain << "Input file name was empty." << std::endl; + valid = false; + } + + if (metal_file_name.empty()) { + explain << "Metal file name was empty." << std::endl; + valid = false; + } + + if (spirv_file_name.empty()) { + explain << "Spirv file name was empty." << std::endl; + valid = false; + } + return valid; +} + +} // namespace compiler +} // namespace impeller diff --git a/impeller/compiler/switches.h b/impeller/compiler/switches.h new file mode 100644 index 0000000000000..0259f25308125 --- /dev/null +++ b/impeller/compiler/switches.h @@ -0,0 +1,35 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#pragma once + +#include +#include + +#include "flutter/fml/command_line.h" +#include "flutter/fml/macros.h" +#include "flutter/fml/unique_fd.h" + +namespace impeller { +namespace compiler { + +struct Switches { + std::shared_ptr working_directory; + std::string source_file_name; + std::string metal_file_name; + std::string spirv_file_name; + + Switches(); + + ~Switches(); + + Switches(const fml::CommandLine& command_line); + + bool AreValid(std::ostream& explain) const; + + static void PrintHelp(std::ostream& stream); +}; + +} // namespace compiler +} // namespace impeller diff --git a/impeller/compiler/tools/compiler.gni b/impeller/compiler/tools/compiler.gni index d73f5f6463239..2ec27e44790f2 100644 --- a/impeller/compiler/tools/compiler.gni +++ b/impeller/compiler/tools/compiler.gni @@ -2,6 +2,32 @@ # Use of this source code is governed by a BSD-style license that can be # found in the LICENSE file. +import("//build/compiled_action.gni") + template("impeller_shaders") { - assert(defined(invoker.shaders), "Impeller shaders must be specified.") + compiled_action_foreach(target_name) { + assert(defined(invoker.shaders), "Impeller shaders must be specified.") + + tool = "//flutter/impeller/compiler:impellerc" + + sources = invoker.shaders + + metal_intermediate = "$target_gen_dir/{{source_file_part}}.metal" + spirv_intermediate = "$target_gen_dir/{{source_file_part}}.spirv" + + outputs = [ + metal_intermediate, + spirv_intermediate, + ] + + source_path = "{{source}}" + metal_intermediate_path = rebase_path(metal_intermediate, root_build_dir) + spirv_intermediate_path = rebase_path(spirv_intermediate, root_build_dir) + + args = [ + "--input=$source_path", + "--metal=$metal_intermediate_path", + "--spirv=$spirv_intermediate_path", + ] + } } diff --git a/impeller/impeller/BUILD.gn b/impeller/impeller/BUILD.gn index 414a45dfef10d..1199d9a593794 100644 --- a/impeller/impeller/BUILD.gn +++ b/impeller/impeller/BUILD.gn @@ -3,6 +3,7 @@ # found in the LICENSE file. import("//flutter/common/config.gni") +import("//flutter/impeller/compiler/tools/compiler.gni") config("impeller_config") { include_dirs = [ ".." ] @@ -86,6 +87,7 @@ source_set("impeller") { compositor_sources + geometry_sources + entity_sources + image_sources deps = [ + ":shaders", "//flutter/fml", "//flutter/impeller/third_party/stb", "//flutter/runtime:libdart", # For tracing, seems to be pulled in by FML. From 0895b3f7e1c7a1152e9f2e93eba1ca4dad453a9b Mon Sep 17 00:00:00 2001 From: Chinmay Garde Date: Thu, 13 May 2021 12:18:06 -0700 Subject: [PATCH 031/433] Wire up support for include dirs. --- impeller/compiler/compiler.cc | 48 +++++++++++++++++-- impeller/compiler/compiler.h | 1 + impeller/compiler/impellerc_main.cc | 1 + impeller/compiler/logger.h | 2 - impeller/compiler/switches.cc | 18 ++++++- impeller/compiler/switches.h | 1 + impeller/compiler/tools/compiler.gni | 1 + .../impeller/compositor/shaders/box/box.vert | 13 +++-- .../impeller/compositor/shaders/box/types.h | 9 ++++ 9 files changed, 82 insertions(+), 12 deletions(-) create mode 100644 impeller/impeller/compositor/shaders/box/types.h diff --git a/impeller/compiler/compiler.cc b/impeller/compiler/compiler.cc index 0ed0886b7f037..435874df6b792 100644 --- a/impeller/compiler/compiler.cc +++ b/impeller/compiler/compiler.cc @@ -26,12 +26,49 @@ struct IncluderData { class Includer final : public shaderc::CompileOptions::IncluderInterface { public: - Includer(std::shared_ptr working_directory) - : working_directory_(std::move(working_directory)) {} + Includer(std::shared_ptr working_directory, + std::vector> include_dirs) + : working_directory_(std::move(working_directory)), + include_dirs_(std::move(include_dirs)) {} // |shaderc::CompileOptions::IncluderInterface| ~Includer() override = default; + std::unique_ptr TryOpenMapping( + const std::shared_ptr& des, + const char* requested_source) const { + if (!des || !des->is_valid()) { + return nullptr; + } + + if (requested_source == nullptr) { + return nullptr; + } + + std::string source(requested_source); + if (source.empty()) { + return nullptr; + } + + return fml::FileMapping::CreateReadOnly(*des, requested_source); + } + + std::unique_ptr FindFirstMapping( + const char* requested_source) const { + // Always try the working directory first no matter what the include + // directories are. + if (auto mapping = TryOpenMapping(working_directory_, requested_source)) { + return mapping; + } + + for (const auto& include_dir : include_dirs_) { + if (auto mapping = TryOpenMapping(include_dir, requested_source)) { + return mapping; + } + } + return nullptr; + } + // |shaderc::CompileOptions::IncluderInterface| shaderc_include_result* GetInclude(const char* requested_source, shaderc_include_type type, @@ -56,8 +93,7 @@ class Includer final : public shaderc::CompileOptions::IncluderInterface { return result.release(); } - auto file = - fml::FileMapping::CreateReadOnly(*working_directory_, requested_source); + auto file = FindFirstMapping(requested_source); if (!file || file->GetMapping() == nullptr) { return result.release(); @@ -84,6 +120,7 @@ class Includer final : public shaderc::CompileOptions::IncluderInterface { private: std::shared_ptr working_directory_; + std::vector> include_dirs_; FML_DISALLOW_COPY_AND_ASSIGN(Includer); }; @@ -165,7 +202,8 @@ Compiler::Compiler(const fml::Mapping& source_mapping, options.SetAutoBindUniforms(true); options.SetAutoMapLocations(true); - options.SetIncluder(std::make_unique(options_.working_directory)); + options.SetIncluder(std::make_unique(options_.working_directory, + options_.include_dirs)); shaderc::Compiler spv_compiler; if (!spv_compiler.IsValid()) { diff --git a/impeller/compiler/compiler.h b/impeller/compiler/compiler.h index 87d0e59b12129..a162cd98e9f05 100644 --- a/impeller/compiler/compiler.h +++ b/impeller/compiler/compiler.h @@ -29,6 +29,7 @@ class Compiler { struct SourceOptions { SourceType type = SourceType::kUnknown; std::shared_ptr working_directory; + std::vector> include_dirs; std::string file_name = "main.glsl"; std::string entry_point_name = "main"; diff --git a/impeller/compiler/impellerc_main.cc b/impeller/compiler/impellerc_main.cc index 822bc4c025636..4142bd6365943 100644 --- a/impeller/compiler/impellerc_main.cc +++ b/impeller/compiler/impellerc_main.cc @@ -37,6 +37,7 @@ bool Main(const fml::CommandLine& command_line) { options.type = Compiler::SourceTypeFromFileName(switches.source_file_name); options.working_directory = switches.working_directory; options.file_name = switches.source_file_name; + options.include_dirs = switches.include_directories; Compiler compiler(*source_file_mapping, options); if (!compiler.IsValid()) { diff --git a/impeller/compiler/logger.h b/impeller/compiler/logger.h index cf4ab47f2494a..b4938a5f46c8b 100644 --- a/impeller/compiler/logger.h +++ b/impeller/compiler/logger.h @@ -20,8 +20,6 @@ class AutoLogger { ~AutoLogger() { logger_ << std::endl; logger_.flush(); - - FML_DLOG(INFO) << logger_.str(); } template diff --git a/impeller/compiler/switches.cc b/impeller/compiler/switches.cc index 5cb06593d00f1..e7f2a8b5d6d5f 100644 --- a/impeller/compiler/switches.cc +++ b/impeller/compiler/switches.cc @@ -16,6 +16,7 @@ void Switches::PrintHelp(std::ostream& stream) { stream << "--input=" << std::endl; stream << "--metal=" << std::endl; stream << "--spirv=" << std::endl; + stream << "[Multiple] --include=" << std::endl; } Switches::Switches() = default; @@ -29,7 +30,22 @@ Switches::Switches(const fml::CommandLine& command_line) fml::FilePermission::kRead))), source_file_name(command_line.GetOptionValueWithDefault("input", "")), metal_file_name(command_line.GetOptionValueWithDefault("metal", "")), - spirv_file_name(command_line.GetOptionValueWithDefault("spirv", "")) {} + spirv_file_name(command_line.GetOptionValueWithDefault("spirv", "")) { + if (!working_directory || !working_directory->is_valid()) { + return; + } + for (const auto& include_dir_path : command_line.GetOptionValues("include")) { + if (!include_dir_path.data()) { + continue; + } + auto dir = std::make_shared(fml::OpenDirectoryReadOnly( + *working_directory, include_dir_path.data())); + if (!dir || !dir->is_valid()) { + continue; + } + include_directories.emplace_back(std::move(dir)); + } +} bool Switches::AreValid(std::ostream& explain) const { bool valid = true; diff --git a/impeller/compiler/switches.h b/impeller/compiler/switches.h index 0259f25308125..afd437a4572d8 100644 --- a/impeller/compiler/switches.h +++ b/impeller/compiler/switches.h @@ -16,6 +16,7 @@ namespace compiler { struct Switches { std::shared_ptr working_directory; + std::vector> include_directories; std::string source_file_name; std::string metal_file_name; std::string spirv_file_name; diff --git a/impeller/compiler/tools/compiler.gni b/impeller/compiler/tools/compiler.gni index 2ec27e44790f2..92bdc70df8969 100644 --- a/impeller/compiler/tools/compiler.gni +++ b/impeller/compiler/tools/compiler.gni @@ -28,6 +28,7 @@ template("impeller_shaders") { "--input=$source_path", "--metal=$metal_intermediate_path", "--spirv=$spirv_intermediate_path", + "--include={{source_dir}}", ] } } diff --git a/impeller/impeller/compositor/shaders/box/box.vert b/impeller/impeller/compositor/shaders/box/box.vert index 76733c345ddbb..e86bae3da2063 100644 --- a/impeller/impeller/compositor/shaders/box/box.vert +++ b/impeller/impeller/compositor/shaders/box/box.vert @@ -1,11 +1,16 @@ +#include "types.h" + uniform UniformBufferObject { - mat4 model; - mat4 view; - mat4 projection; + Uniforms uniforms; } ubo; in vec3 inPosition; +in float stuff; + +out float outStuff; + void main() { - gl_Position = ubo.projection * ubo.view * ubo.model * vec4(inPosition, 1.0); + gl_Position = ubo.uniforms.projection * ubo.uniforms.view * ubo.uniforms.model * vec4(inPosition, 1.0); + outStuff = stuff; } diff --git a/impeller/impeller/compositor/shaders/box/types.h b/impeller/impeller/compositor/shaders/box/types.h new file mode 100644 index 0000000000000..d8775956bacd3 --- /dev/null +++ b/impeller/impeller/compositor/shaders/box/types.h @@ -0,0 +1,9 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +struct Uniforms { + mat4 model; + mat4 view; + mat4 projection; +}; From 2f1166fcb832ee8e448396e27ddafd470e8e6e65 Mon Sep 17 00:00:00 2001 From: Chinmay Garde Date: Thu, 13 May 2021 12:36:24 -0700 Subject: [PATCH 032/433] Add missing stage. --- impeller/compiler/compiler.cc | 1 + 1 file changed, 1 insertion(+) diff --git a/impeller/compiler/compiler.cc b/impeller/compiler/compiler.cc index 435874df6b792..8dea97f465285 100644 --- a/impeller/compiler/compiler.cc +++ b/impeller/compiler/compiler.cc @@ -121,6 +121,7 @@ class Includer final : public shaderc::CompileOptions::IncluderInterface { private: std::shared_ptr working_directory_; std::vector> include_dirs_; + std::string included_file_paths_; FML_DISALLOW_COPY_AND_ASSIGN(Includer); }; From 15e4918ae4271997f0a6e7679e81ce85f91bd67c Mon Sep 17 00:00:00 2001 From: Chinmay Garde Date: Fri, 14 May 2021 11:27:30 -0700 Subject: [PATCH 033/433] Wire up depfile support and Metal shader linking in GN rules. --- impeller/compiler/BUILD.gn | 1 + impeller/compiler/compiler.cc | 92 ++++++-- impeller/compiler/compiler.h | 12 +- impeller/compiler/fixtures/common_types.h | 16 ++ impeller/compiler/fixtures/types.h | 11 +- impeller/compiler/impellerc_main.cc | 10 + impeller/compiler/include_dir.h | 21 ++ impeller/compiler/reflector.cc | 200 +++++++++--------- impeller/compiler/switches.cc | 14 +- impeller/compiler/switches.h | 4 +- impeller/compiler/tools/compiler.gni | 23 +- impeller/impeller/BUILD.gn | 1 + .../impeller/compositor/shaders/box/box.frag | 2 + .../impeller/compositor/shaders/box/box.vert | 7 +- impeller/tools/metal/metal_library.gni | 10 + 15 files changed, 293 insertions(+), 131 deletions(-) create mode 100644 impeller/compiler/fixtures/common_types.h create mode 100644 impeller/compiler/include_dir.h diff --git a/impeller/compiler/BUILD.gn b/impeller/compiler/BUILD.gn index aaacc2a27c44b..540ccc7328842 100644 --- a/impeller/compiler/BUILD.gn +++ b/impeller/compiler/BUILD.gn @@ -8,6 +8,7 @@ source_set("compiler_lib") { sources = [ "compiler.cc", "compiler.h", + "include_dir.h", "logger.h", "reflector.cc", "reflector.h", diff --git a/impeller/compiler/compiler.cc b/impeller/compiler/compiler.cc index 8dea97f465285..d7c4b0afad1fa 100644 --- a/impeller/compiler/compiler.cc +++ b/impeller/compiler/compiler.cc @@ -4,9 +4,11 @@ #include "flutter/impeller/compiler/compiler.h" -#include "flutter/impeller/compiler/logger.h" - #include +#include + +#include "flutter/fml/paths.h" +#include "flutter/impeller/compiler/logger.h" namespace impeller { namespace compiler { @@ -27,17 +29,19 @@ struct IncluderData { class Includer final : public shaderc::CompileOptions::IncluderInterface { public: Includer(std::shared_ptr working_directory, - std::vector> include_dirs) + std::vector include_dirs, + std::function on_file_included) : working_directory_(std::move(working_directory)), - include_dirs_(std::move(include_dirs)) {} + include_dirs_(std::move(include_dirs)), + on_file_included_(std::move(on_file_included)) {} // |shaderc::CompileOptions::IncluderInterface| ~Includer() override = default; std::unique_ptr TryOpenMapping( - const std::shared_ptr& des, - const char* requested_source) const { - if (!des || !des->is_valid()) { + const IncludeDir& dir, + const char* requested_source) { + if (!dir.dir || !dir.dir->is_valid()) { return nullptr; } @@ -50,15 +54,27 @@ class Includer final : public shaderc::CompileOptions::IncluderInterface { return nullptr; } - return fml::FileMapping::CreateReadOnly(*des, requested_source); + auto mapping = fml::FileMapping::CreateReadOnly(*dir.dir, requested_source); + if (!mapping || !mapping->IsValid()) { + return nullptr; + } + + on_file_included_(fml::paths::JoinPaths({dir.name, requested_source})); + + return mapping; } std::unique_ptr FindFirstMapping( - const char* requested_source) const { + const char* requested_source) { // Always try the working directory first no matter what the include // directories are. - if (auto mapping = TryOpenMapping(working_directory_, requested_source)) { - return mapping; + { + IncludeDir dir; + dir.name = "."; + dir.dir = working_directory_; + if (auto mapping = TryOpenMapping(dir, requested_source)) { + return mapping; + } } for (const auto& include_dir : include_dirs_) { @@ -120,8 +136,8 @@ class Includer final : public shaderc::CompileOptions::IncluderInterface { private: std::shared_ptr working_directory_; - std::vector> include_dirs_; - std::string included_file_paths_; + std::vector include_dirs_; + std::function on_file_included_; FML_DISALLOW_COPY_AND_ASSIGN(Includer); }; @@ -203,8 +219,14 @@ Compiler::Compiler(const fml::Mapping& source_mapping, options.SetAutoBindUniforms(true); options.SetAutoMapLocations(true); - options.SetIncluder(std::make_unique(options_.working_directory, - options_.include_dirs)); + options.AddMacroDefinition("IMPELLER_DEVICE"); + + std::vector included_file_names; + options.SetIncluder(std::make_unique( + options_.working_directory, options_.include_dirs, + [&included_file_names](auto included_name) { + included_file_names.emplace_back(std::move(included_name)); + })); shaderc::Compiler spv_compiler; if (!spv_compiler.IsValid()) { @@ -234,6 +256,8 @@ Compiler::Compiler(const fml::Mapping& source_mapping, COMPILER_ERROR_NO_PREFIX << spv_result_->GetErrorMessage(); } return; + } else { + included_file_names_ = std::move(included_file_names); } // MSL Generation. @@ -329,5 +353,43 @@ std::string Compiler::GetErrorMessages() const { return error_stream_.str(); } +const std::vector& Compiler::GetIncludedFileNames() const { + return included_file_names_; +} + +static std::string JoinStrings(std::vector items, + std::string separator) { + std::stringstream stream; + for (size_t i = 0, count = items.size(); i < count; i++) { + const auto is_last = (i == count - 1); + + stream << items[i]; + if (!is_last) { + stream << separator; + } + } + return stream.str(); +} + +std::string Compiler::GetDependencyNames(std::string separator) const { + std::vector dependencies = included_file_names_; + dependencies.push_back(options_.file_name); + return JoinStrings(dependencies, separator); +} + +std::unique_ptr Compiler::CreateDepfileContents( + std::initializer_list targets_names) const { + const auto targets = JoinStrings(targets_names, " "); + const auto dependencies = GetDependencyNames(" "); + + std::stringstream stream; + stream << targets << ":\n\t" << dependencies << "\n"; + + auto contents = std::make_shared(stream.str()); + return std::make_unique( + reinterpret_cast(contents->data()), contents->size(), + [contents](auto, auto) {}); +} + } // namespace compiler } // namespace impeller diff --git a/impeller/compiler/compiler.h b/impeller/compiler/compiler.h index a162cd98e9f05..d9d64708a1887 100644 --- a/impeller/compiler/compiler.h +++ b/impeller/compiler/compiler.h @@ -4,11 +4,13 @@ #pragma once +#include #include #include #include "flutter/fml/macros.h" #include "flutter/fml/mapping.h" +#include "flutter/impeller/compiler/include_dir.h" #include "flutter/impeller/compiler/reflector.h" #include "shaderc/shaderc.hpp" #include "third_party/spirv_cross/spirv_msl.hpp" @@ -29,7 +31,7 @@ class Compiler { struct SourceOptions { SourceType type = SourceType::kUnknown; std::shared_ptr working_directory; - std::vector> include_dirs; + std::vector include_dirs; std::string file_name = "main.glsl"; std::string entry_point_name = "main"; @@ -51,16 +53,24 @@ class Compiler { std::string GetErrorMessages() const; + const std::vector& GetIncludedFileNames() const; + + std::unique_ptr CreateDepfileContents( + std::initializer_list targets) const; + private: SourceOptions options_; std::shared_ptr spv_result_; std::shared_ptr msl_string_; std::stringstream error_stream_; std::unique_ptr reflector_; + std::vector included_file_names_; bool is_valid_ = false; std::string GetSourcePrefix() const; + std::string GetDependencyNames(std::string separator) const; + FML_DISALLOW_COPY_AND_ASSIGN(Compiler); }; diff --git a/impeller/compiler/fixtures/common_types.h b/impeller/compiler/fixtures/common_types.h new file mode 100644 index 0000000000000..ad4d31f94b426 --- /dev/null +++ b/impeller/compiler/fixtures/common_types.h @@ -0,0 +1,16 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifdef IMPELLER_DEVICE + +#define Vector3 vec3 +#define Vector4 vec4 +#define Matrix mat4 + +#else // IMPELLER_DEVICE + +#include "flutter/impeller/impeller/geometry/matrix.h" +#include "flutter/impeller/impeller/geometry/vector.h" + +#endif // IMPELLER_DEVICE diff --git a/impeller/compiler/fixtures/types.h b/impeller/compiler/fixtures/types.h index 448c3310722d8..86572a665512f 100644 --- a/impeller/compiler/fixtures/types.h +++ b/impeller/compiler/fixtures/types.h @@ -1,8 +1,13 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "common_types.h" struct Hello { - vec4 stuff; - vec2 more_stuff; + Vector4 stuff; + Vector3 more_stuff; float dunno; float this_bit_is_not_used; - vec2 yet_more_stuff; + Vector4 yet_more_stuff; }; diff --git a/impeller/compiler/impellerc_main.cc b/impeller/compiler/impellerc_main.cc index 4142bd6365943..6443d6e1313e9 100644 --- a/impeller/compiler/impellerc_main.cc +++ b/impeller/compiler/impellerc_main.cc @@ -62,6 +62,16 @@ bool Main(const fml::CommandLine& command_line) { return false; } + if (!switches.depfile_path.empty()) { + if (!fml::WriteAtomically( + *switches.working_directory, switches.depfile_path.c_str(), + *compiler.CreateDepfileContents({switches.metal_file_name}))) { + std::cerr << "Could not write depfile to " << switches.depfile_path + << std::endl; + return false; + } + } + return true; } diff --git a/impeller/compiler/include_dir.h b/impeller/compiler/include_dir.h new file mode 100644 index 0000000000000..0c7927a47ffef --- /dev/null +++ b/impeller/compiler/include_dir.h @@ -0,0 +1,21 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#pragma once + +#include +#include + +#include "flutter/fml/unique_fd.h" + +namespace impeller { +namespace compiler { + +struct IncludeDir { + std::shared_ptr dir; + std::string name; +}; + +} // namespace compiler +} // namespace impeller diff --git a/impeller/compiler/reflector.cc b/impeller/compiler/reflector.cc index cc1d82c404fad..347ba8d21da03 100644 --- a/impeller/compiler/reflector.cc +++ b/impeller/compiler/reflector.cc @@ -11,108 +11,108 @@ namespace impeller { namespace compiler { -std::string SpvReflectInterfaceVariableToString( - const SpvReflectInterfaceVariable& var) { - std::stringstream stream; - stream << "--- Interface Variable" << std::endl; - stream << "spirv_id=" << var.spirv_id << std::endl; - if (var.name) { - stream << "name=" << var.name << std::endl; - } - stream << "location=" << var.location << std::endl; - stream << "storage_class=" << var.storage_class << std::endl; - if (var.semantic) { - stream << "semantic=" << var.semantic << std::endl; - } - stream << "decoration_flags=" << var.decoration_flags << std::endl; - stream << "built_in=" << var.built_in << std::endl; - // stream << "numeric=" << var.numeric << std::endl; - // stream << "array=" << var.array << std::endl; - stream << "member_count=" << var.member_count << std::endl; - // stream << "members=" << var.members << std::endl; - stream << "format=" << var.format << std::endl; - stream << "type_description=" << var.type_description << std::endl; - return stream.str(); -} - -std::string SpvReflectDescriptorBindingToString( - const SpvReflectDescriptorBinding& des) { - std::stringstream stream; - stream << "--- Descriptor Set Binding" << std::endl; - stream << "spirv_id=" << des.spirv_id << std::endl; - if (des.name) { - stream << "name=" << des.name << std::endl; - } - stream << "binding=" << des.binding << std::endl; - stream << "input_attachment_index=" << des.input_attachment_index - << std::endl; - stream << "set=" << des.set << std::endl; - stream << "descriptor_type=" << des.descriptor_type << std::endl; - stream << "resource_type=" << des.resource_type << std::endl; - // stream << "image=" << des.image << std::endl; - // stream << "block=" << des.block << std::endl; - // stream << "array=" << des.array << std::endl; - stream << "count=" << des.count << std::endl; - stream << "accessed=" << des.accessed << std::endl; - stream << "uav_counter_id=" << des.uav_counter_id << std::endl; - stream << "uav_counter_binding=" << des.uav_counter_binding << std::endl; - stream << "type_description=" << des.type_description << std::endl; - return stream.str(); -} +// std::string SpvReflectInterfaceVariableToString( +// const SpvReflectInterfaceVariable& var) { +// std::stringstream stream; +// stream << "--- Interface Variable" << std::endl; +// stream << "spirv_id=" << var.spirv_id << std::endl; +// if (var.name) { +// stream << "name=" << var.name << std::endl; +// } +// stream << "location=" << var.location << std::endl; +// stream << "storage_class=" << var.storage_class << std::endl; +// if (var.semantic) { +// stream << "semantic=" << var.semantic << std::endl; +// } +// stream << "decoration_flags=" << var.decoration_flags << std::endl; +// stream << "built_in=" << var.built_in << std::endl; +// // stream << "numeric=" << var.numeric << std::endl; +// // stream << "array=" << var.array << std::endl; +// stream << "member_count=" << var.member_count << std::endl; +// // stream << "members=" << var.members << std::endl; +// stream << "format=" << var.format << std::endl; +// stream << "type_description=" << var.type_description << std::endl; +// return stream.str(); +// } + +// std::string SpvReflectDescriptorBindingToString( +// const SpvReflectDescriptorBinding& des) { +// std::stringstream stream; +// stream << "--- Descriptor Set Binding" << std::endl; +// stream << "spirv_id=" << des.spirv_id << std::endl; +// if (des.name) { +// stream << "name=" << des.name << std::endl; +// } +// stream << "binding=" << des.binding << std::endl; +// stream << "input_attachment_index=" << des.input_attachment_index +// << std::endl; +// stream << "set=" << des.set << std::endl; +// stream << "descriptor_type=" << des.descriptor_type << std::endl; +// stream << "resource_type=" << des.resource_type << std::endl; +// // stream << "image=" << des.image << std::endl; +// // stream << "block=" << des.block << std::endl; +// // stream << "array=" << des.array << std::endl; +// stream << "count=" << des.count << std::endl; +// stream << "accessed=" << des.accessed << std::endl; +// stream << "uav_counter_id=" << des.uav_counter_id << std::endl; +// stream << "uav_counter_binding=" << des.uav_counter_binding << std::endl; +// stream << "type_description=" << des.type_description << std::endl; +// return stream.str(); +// } Reflector::Reflector(const fml::Mapping& spirv_binary) { - if (spirv_binary.GetMapping() == nullptr) { - return; - } - - spv_reflect::ShaderModule module(spirv_binary.GetSize(), - spirv_binary.GetMapping()); - - if (module.GetResult() != SpvReflectResult::SPV_REFLECT_RESULT_SUCCESS) { - return; - } - - FML_LOG(ERROR) << "~~~~~~~~~~~~~ Interface Variables ~~~~~~~~~~~~~"; - - { - uint32_t count = 0; - if (module.EnumerateInterfaceVariables(&count, nullptr) != - SpvReflectResult::SPV_REFLECT_RESULT_SUCCESS) { - return; - } - - std::vector input_variables; - input_variables.resize(count); - if (module.EnumerateInterfaceVariables(&count, input_variables.data()) != - SpvReflectResult::SPV_REFLECT_RESULT_SUCCESS) { - return; - } - - for (const auto& input_variable : input_variables) { - FML_LOG(ERROR) << SpvReflectInterfaceVariableToString(*input_variable); - } - } - - FML_LOG(ERROR) << "~~~~~~~~~~~~~ Descriptor Bindings ~~~~~~~~~~~~~"; - - { - uint32_t count = 0; - if (module.EnumerateDescriptorBindings(&count, nullptr) != - SpvReflectResult::SPV_REFLECT_RESULT_SUCCESS) { - return; - } - - std::vector input_variables; - input_variables.resize(count); - if (module.EnumerateDescriptorBindings(&count, input_variables.data()) != - SpvReflectResult::SPV_REFLECT_RESULT_SUCCESS) { - return; - } - - for (const auto& input_variable : input_variables) { - FML_LOG(ERROR) << SpvReflectDescriptorBindingToString(*input_variable); - } - } + // if (spirv_binary.GetMapping() == nullptr) { + // return; + // } + + // spv_reflect::ShaderModule module(spirv_binary.GetSize(), + // spirv_binary.GetMapping()); + + // if (module.GetResult() != SpvReflectResult::SPV_REFLECT_RESULT_SUCCESS) { + // return; + // } + + // FML_LOG(ERROR) << "~~~~~~~~~~~~~ Interface Variables ~~~~~~~~~~~~~"; + + // { + // uint32_t count = 0; + // if (module.EnumerateInterfaceVariables(&count, nullptr) != + // SpvReflectResult::SPV_REFLECT_RESULT_SUCCESS) { + // return; + // } + + // std::vector input_variables; + // input_variables.resize(count); + // if (module.EnumerateInterfaceVariables(&count, input_variables.data()) != + // SpvReflectResult::SPV_REFLECT_RESULT_SUCCESS) { + // return; + // } + + // for (const auto& input_variable : input_variables) { + // FML_LOG(ERROR) << SpvReflectInterfaceVariableToString(*input_variable); + // } + // } + + // FML_LOG(ERROR) << "~~~~~~~~~~~~~ Descriptor Bindings ~~~~~~~~~~~~~"; + + // { + // uint32_t count = 0; + // if (module.EnumerateDescriptorBindings(&count, nullptr) != + // SpvReflectResult::SPV_REFLECT_RESULT_SUCCESS) { + // return; + // } + + // std::vector input_variables; + // input_variables.resize(count); + // if (module.EnumerateDescriptorBindings(&count, input_variables.data()) != + // SpvReflectResult::SPV_REFLECT_RESULT_SUCCESS) { + // return; + // } + + // for (const auto& input_variable : input_variables) { + // FML_LOG(ERROR) << SpvReflectDescriptorBindingToString(*input_variable); + // } + // } is_valid_ = true; } diff --git a/impeller/compiler/switches.cc b/impeller/compiler/switches.cc index e7f2a8b5d6d5f..2ea0e8549dbd4 100644 --- a/impeller/compiler/switches.cc +++ b/impeller/compiler/switches.cc @@ -16,7 +16,8 @@ void Switches::PrintHelp(std::ostream& stream) { stream << "--input=" << std::endl; stream << "--metal=" << std::endl; stream << "--spirv=" << std::endl; - stream << "[Multiple] --include=" << std::endl; + stream << "[optional,multiple] --include=" << std::endl; + stream << "[optional] --depfile=" << std::endl; } Switches::Switches() = default; @@ -30,10 +31,12 @@ Switches::Switches(const fml::CommandLine& command_line) fml::FilePermission::kRead))), source_file_name(command_line.GetOptionValueWithDefault("input", "")), metal_file_name(command_line.GetOptionValueWithDefault("metal", "")), - spirv_file_name(command_line.GetOptionValueWithDefault("spirv", "")) { + spirv_file_name(command_line.GetOptionValueWithDefault("spirv", "")), + depfile_path(command_line.GetOptionValueWithDefault("depfile", "")) { if (!working_directory || !working_directory->is_valid()) { return; } + for (const auto& include_dir_path : command_line.GetOptionValues("include")) { if (!include_dir_path.data()) { continue; @@ -43,7 +46,12 @@ Switches::Switches(const fml::CommandLine& command_line) if (!dir || !dir->is_valid()) { continue; } - include_directories.emplace_back(std::move(dir)); + + IncludeDir dir_entry; + dir_entry.name = include_dir_path; + dir_entry.dir = std::move(dir); + + include_directories.emplace_back(std::move(dir_entry)); } } diff --git a/impeller/compiler/switches.h b/impeller/compiler/switches.h index afd437a4572d8..d9e1df1d92a75 100644 --- a/impeller/compiler/switches.h +++ b/impeller/compiler/switches.h @@ -10,16 +10,18 @@ #include "flutter/fml/command_line.h" #include "flutter/fml/macros.h" #include "flutter/fml/unique_fd.h" +#include "flutter/impeller/compiler/include_dir.h" namespace impeller { namespace compiler { struct Switches { std::shared_ptr working_directory; - std::vector> include_directories; + std::vector include_directories; std::string source_file_name; std::string metal_file_name; std::string spirv_file_name; + std::string depfile_path; Switches(); diff --git a/impeller/compiler/tools/compiler.gni b/impeller/compiler/tools/compiler.gni index 92bdc70df8969..e3230b4244ee2 100644 --- a/impeller/compiler/tools/compiler.gni +++ b/impeller/compiler/tools/compiler.gni @@ -3,11 +3,14 @@ # found in the LICENSE file. import("//build/compiled_action.gni") +import("//flutter/impeller/tools/metal/metal_library.gni") template("impeller_shaders") { - compiled_action_foreach(target_name) { - assert(defined(invoker.shaders), "Impeller shaders must be specified.") + assert(defined(invoker.shaders), "Impeller shaders must be specified.") + assert(defined(invoker.name), "Name of the shader library must be specified.") + metal_sources_target_name = "metal_sources_$target_name" + compiled_action_foreach(metal_sources_target_name) { tool = "//flutter/impeller/compiler:impellerc" sources = invoker.shaders @@ -15,20 +18,28 @@ template("impeller_shaders") { metal_intermediate = "$target_gen_dir/{{source_file_part}}.metal" spirv_intermediate = "$target_gen_dir/{{source_file_part}}.spirv" - outputs = [ - metal_intermediate, - spirv_intermediate, - ] + outputs = [ metal_intermediate ] + depfile_path = "$target_gen_dir/{{source_file_part}}.d" source_path = "{{source}}" metal_intermediate_path = rebase_path(metal_intermediate, root_build_dir) spirv_intermediate_path = rebase_path(spirv_intermediate, root_build_dir) + depfile_intermediate_path = rebase_path(depfile_path, root_build_dir) + + depfile = depfile_path args = [ "--input=$source_path", "--metal=$metal_intermediate_path", "--spirv=$spirv_intermediate_path", "--include={{source_dir}}", + "--depfile=$depfile_intermediate_path", ] } + + metal_library(target_name) { + name = invoker.name + sources = get_target_outputs(":$metal_sources_target_name") + deps = [ ":$metal_sources_target_name" ] + } } diff --git a/impeller/impeller/BUILD.gn b/impeller/impeller/BUILD.gn index 1199d9a593794..aa4ff9845b060 100644 --- a/impeller/impeller/BUILD.gn +++ b/impeller/impeller/BUILD.gn @@ -10,6 +10,7 @@ config("impeller_config") { } impeller_shaders("shaders") { + name = "impeller" shaders = [ "compositor/shaders/box/box.frag", "compositor/shaders/box/box.vert", diff --git a/impeller/impeller/compositor/shaders/box/box.frag b/impeller/impeller/compositor/shaders/box/box.frag index 1b9f713acecd0..333c266ada884 100644 --- a/impeller/impeller/compositor/shaders/box/box.frag +++ b/impeller/impeller/compositor/shaders/box/box.frag @@ -1,3 +1,5 @@ +#include "types.h" + out vec4 outColor; void main() { diff --git a/impeller/impeller/compositor/shaders/box/box.vert b/impeller/impeller/compositor/shaders/box/box.vert index e86bae3da2063..b3a757b5a44d9 100644 --- a/impeller/impeller/compositor/shaders/box/box.vert +++ b/impeller/impeller/compositor/shaders/box/box.vert @@ -6,11 +6,14 @@ uniform UniformBufferObject { in vec3 inPosition; -in float stuff; +in vec4 inAnotherPosition; + +layout(location = 3) in float stuff; out float outStuff; void main() { - gl_Position = ubo.uniforms.projection * ubo.uniforms.view * ubo.uniforms.model * vec4(inPosition, 1.0); + gl_Position = ubo.uniforms.projection * ubo.uniforms.view * ubo.uniforms.model * vec4(inPosition, 1.0) * inAnotherPosition; outStuff = stuff; } + diff --git a/impeller/tools/metal/metal_library.gni b/impeller/tools/metal/metal_library.gni index ca077e8a3c312..8c2f4c98e5fe7 100644 --- a/impeller/tools/metal/metal_library.gni +++ b/impeller/tools/metal/metal_library.gni @@ -9,6 +9,16 @@ template("metal_library") { metal_library_name = invoker.name action("$target_name") { + forward_variables_from(invoker, + "*", + [ + "inputs", + "outputs", + "script", + "depfile", + "args", + ]) + inputs = invoker.sources metal_library_path = "$root_out_dir/shaders/$metal_library_name.metallib" From 4f7f0e881d140db9c46a04d104864ebb7d126257 Mon Sep 17 00:00:00 2001 From: Chinmay Garde Date: Fri, 14 May 2021 12:04:45 -0700 Subject: [PATCH 034/433] Rename entrypoint to avoid collissions. --- impeller/compiler/compiler.cc | 40 +++++++++++++++++++++++++++++ impeller/compiler/compiler.h | 3 +++ impeller/compiler/impellerc_main.cc | 3 +++ 3 files changed, 46 insertions(+) diff --git a/impeller/compiler/compiler.cc b/impeller/compiler/compiler.cc index d7c4b0afad1fa..6d3b2b7e9472c 100644 --- a/impeller/compiler/compiler.cc +++ b/impeller/compiler/compiler.cc @@ -4,6 +4,7 @@ #include "flutter/impeller/compiler/compiler.h" +#include #include #include @@ -184,6 +185,19 @@ static shaderc_shader_kind ToShaderCShaderKind(Compiler::SourceType type) { return shaderc_shader_kind::shaderc_glsl_infer_from_source; } +static spv::ExecutionModel ToExecutionModel(Compiler::SourceType type) { + switch (type) { + case Compiler::SourceType::kVertexShader: + return spv::ExecutionModel::ExecutionModelVertex; + case Compiler::SourceType::kFragmentShader: + return spv::ExecutionModel::ExecutionModelFragment; + case Compiler::SourceType::kUnknown: + break; + } + return spv::ExecutionModel::ExecutionModelMax; + ; +} + Compiler::Compiler(const fml::Mapping& source_mapping, SourceOptions source_options) : options_(source_options) { @@ -264,6 +278,11 @@ Compiler::Compiler(const fml::Mapping& source_mapping, spirv_cross::CompilerMSL msl_compiler( spv_result_->cbegin(), spv_result_->cend() - spv_result_->cbegin()); + { + msl_compiler.rename_entry_point("main", options_.entry_point_name, + ToExecutionModel(options_.type)); + } + { spirv_cross::CompilerMSL::Options msl_options; msl_options.platform = spirv_cross::CompilerMSL::Options::Platform::macOS; @@ -343,6 +362,27 @@ Compiler::SourceType Compiler::SourceTypeFromFileName( return Compiler::SourceType::kUnknown; } +std::string Compiler::EntryPointFromSourceName(const std::string& file_name, + SourceType type) { + std::stringstream stream; + stream << "__impeller_"; + std::filesystem::path file_path(file_name); + stream << file_path.stem().native() << "_"; + switch (type) { + case SourceType::kUnknown: + stream << "unknown"; + break; + case SourceType::kVertexShader: + stream << "vertex"; + break; + case SourceType::kFragmentShader: + stream << "fragment"; + break; + } + stream << "_main__"; + return stream.str(); +} + std::string Compiler::GetSourcePrefix() const { std::stringstream stream; stream << options_.file_name << ": "; diff --git a/impeller/compiler/compiler.h b/impeller/compiler/compiler.h index d9d64708a1887..7c2b7f0e8d6dc 100644 --- a/impeller/compiler/compiler.h +++ b/impeller/compiler/compiler.h @@ -28,6 +28,9 @@ class Compiler { static SourceType SourceTypeFromFileName(const std::string& file_name); + static std::string EntryPointFromSourceName(const std::string& file_name, + SourceType type); + struct SourceOptions { SourceType type = SourceType::kUnknown; std::shared_ptr working_directory; diff --git a/impeller/compiler/impellerc_main.cc b/impeller/compiler/impellerc_main.cc index 6443d6e1313e9..31c32fe0e0741 100644 --- a/impeller/compiler/impellerc_main.cc +++ b/impeller/compiler/impellerc_main.cc @@ -38,6 +38,9 @@ bool Main(const fml::CommandLine& command_line) { options.working_directory = switches.working_directory; options.file_name = switches.source_file_name; options.include_dirs = switches.include_directories; + options.entry_point_name = Compiler::EntryPointFromSourceName( + switches.source_file_name, + Compiler::SourceTypeFromFileName(switches.source_file_name)); Compiler compiler(*source_file_mapping, options); if (!compiler.IsValid()) { From 67814f5bd47a242991742e7465872d215c38ad5c Mon Sep 17 00:00:00 2001 From: Chinmay Garde Date: Fri, 14 May 2021 13:43:20 -0700 Subject: [PATCH 035/433] Explicitly specify GLSL version. --- impeller/compiler/compiler.cc | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/impeller/compiler/compiler.cc b/impeller/compiler/compiler.cc index 6d3b2b7e9472c..f95cbe4a4c4ea 100644 --- a/impeller/compiler/compiler.cc +++ b/impeller/compiler/compiler.cc @@ -222,9 +222,11 @@ Compiler::Compiler(const fml::Mapping& source_mapping, options.SetOptimizationLevel( shaderc_optimization_level::shaderc_optimization_level_zero); + // Expects GLSL 4.60 (Core Profile). + // https://www.khronos.org/registry/OpenGL/specs/gl/GLSLangSpec.4.60.pdf options.SetSourceLanguage( shaderc_source_language::shaderc_source_language_glsl); - options.SetForcedVersionProfile(450, shaderc_profile::shaderc_profile_core); + options.SetForcedVersionProfile(460, shaderc_profile::shaderc_profile_core); options.SetTargetEnvironment( shaderc_target_env::shaderc_target_env_vulkan, shaderc_env_version::shaderc_env_version_vulkan_1_1); From 9f45e4059d943337aed2974c3f9e6b016af49116 Mon Sep 17 00:00:00 2001 From: Chinmay Garde Date: Sat, 15 May 2021 14:09:12 -0700 Subject: [PATCH 036/433] Wire up shader libraries. --- impeller/host/impeller_renderer.mm | 10 +++-- impeller/host/shaders_location.cc | 16 ++++---- impeller/host/shaders_location.h | 2 + impeller/impeller/BUILD.gn | 6 +++ impeller/impeller/compositor/context.h | 15 +++++-- impeller/impeller/compositor/context.mm | 26 +++++++++++- .../impeller/compositor/pipeline_builder.h | 16 +++++++- impeller/impeller/compositor/renderer.h | 2 +- impeller/impeller/compositor/renderer.mm | 4 +- impeller/impeller/compositor/shader_library.h | 40 ++++++++++++++++++- .../impeller/compositor/shader_library.mm | 32 +++++++++++++++ impeller/impeller/compositor/surface.h | 2 +- .../impeller/compositor/vertex_descriptor.h | 21 ++++++++++ .../impeller/compositor/vertex_descriptor.mm | 13 ++++++ 14 files changed, 181 insertions(+), 24 deletions(-) create mode 100644 impeller/impeller/compositor/vertex_descriptor.h create mode 100644 impeller/impeller/compositor/vertex_descriptor.mm diff --git a/impeller/host/impeller_renderer.mm b/impeller/host/impeller_renderer.mm index 87a3906b0ae60..51ea49a43c11c 100644 --- a/impeller/host/impeller_renderer.mm +++ b/impeller/host/impeller_renderer.mm @@ -41,7 +41,7 @@ @implementation ImpellerRenderer { float _rotation; MTKMesh* _mesh; - impeller::Renderer renderer_; + std::unique_ptr renderer_; } - (nonnull instancetype)initWithMetalKitView:(nonnull MTKView*)view { @@ -53,7 +53,9 @@ - (nonnull instancetype)initWithMetalKitView:(nonnull MTKView*)view { [self _loadAssets]; } - if (!renderer_.IsValid()) { + renderer_ = std::make_unique( + impeller::ImpellerShadersDirectory()); + if (!renderer_->IsValid()) { FML_LOG(ERROR) << "Impeller Renderer is not valid."; } @@ -226,7 +228,7 @@ - (void)_updateGameState { } - (void)drawInMTKView:(nonnull MTKView*)view { - renderer_.Render(); + renderer_->Render(); /// Per frame updates here dispatch_semaphore_wait(_inFlightSemaphore, DISPATCH_TIME_FOREVER); @@ -301,7 +303,7 @@ - (void)drawInMTKView:(nonnull MTKView*)view { } - (void)mtkView:(nonnull MTKView*)view drawableSizeWillChange:(CGSize)size { - renderer_.SurfaceSizeDidChange({size.width, size.height}); + renderer_->SurfaceSizeDidChange({size.width, size.height}); float aspect = size.width / (float)size.height; _projectionMatrix = matrix_perspective_right_hand(65.0f * (M_PI / 180.0f), diff --git a/impeller/host/shaders_location.cc b/impeller/host/shaders_location.cc index 3e9fb5518ca99..6ce60a7ef6c7a 100644 --- a/impeller/host/shaders_location.cc +++ b/impeller/host/shaders_location.cc @@ -9,16 +9,16 @@ namespace impeller { -std::optional ImpellerShadersLocation(std::string library_name) { - auto executable_directory = fml::paths::GetExecutableDirectoryPath(); - - if (!executable_directory.first) { - FML_LOG(ERROR) << "Shaders directory could not be found."; - return std::nullopt; +std::string ImpellerShadersDirectory() { + auto path_result = fml::paths::GetExecutableDirectoryPath(); + if (!path_result.first) { + return {}; } + return fml::paths::JoinPaths({path_result.second, "shaders"}); +} - auto path = fml::paths::JoinPaths( - {executable_directory.second, "shaders", library_name}); +std::optional ImpellerShadersLocation(std::string library_name) { + auto path = fml::paths::JoinPaths({ImpellerShadersDirectory(), library_name}); if (!fml::IsFile(path)) { FML_LOG(ERROR) << "The shader library does not exist: " << path; diff --git a/impeller/host/shaders_location.h b/impeller/host/shaders_location.h index f449810835e1a..cd26132f1271f 100644 --- a/impeller/host/shaders_location.h +++ b/impeller/host/shaders_location.h @@ -7,6 +7,8 @@ namespace impeller { +std::string ImpellerShadersDirectory(); + std::optional ImpellerShadersLocation(std::string library_name); } // namespace impeller diff --git a/impeller/impeller/BUILD.gn b/impeller/impeller/BUILD.gn index aa4ff9845b060..e8b1abe59d292 100644 --- a/impeller/impeller/BUILD.gn +++ b/impeller/impeller/BUILD.gn @@ -23,6 +23,10 @@ source_set("impeller") { cflags_objc = flutter_cflags_objc_arc cflags_objcc = flutter_cflags_objcc_arc + if (is_clang) { + cflags_cc = [ "-Wno-unused-private-field" ] + } + include_dirs = [ "entity", "geometry", @@ -45,6 +49,8 @@ source_set("impeller") { "compositor/shader_library.mm", "compositor/surface.h", "compositor/surface.mm", + "compositor/vertex_descriptor.h", + "compositor/vertex_descriptor.mm", ] geometry_sources = [ diff --git a/impeller/impeller/compositor/context.h b/impeller/impeller/compositor/context.h index 163239a7c3479..dc007ba4d7ecb 100644 --- a/impeller/impeller/compositor/context.h +++ b/impeller/impeller/compositor/context.h @@ -6,13 +6,17 @@ #include +#include + #include "flutter/fml/macros.h" namespace impeller { +class ShaderLibrary; + class Context { public: - Context(); + Context(std::string shaders_directory); ~Context(); @@ -22,10 +26,13 @@ class Context { id GetTransferQueue() const; + std::shared_ptr GetShaderLibrary() const; + private: - id device_; - id render_queue_; - id transfer_queue_; + id device_ = nullptr; + id render_queue_ = nullptr; + id transfer_queue_ = nullptr; + std::shared_ptr shader_library_; bool is_valid_ = false; FML_DISALLOW_COPY_AND_ASSIGN(Context); diff --git a/impeller/impeller/compositor/context.mm b/impeller/impeller/compositor/context.mm index e64025f84c065..702e9a4ffa175 100644 --- a/impeller/impeller/compositor/context.mm +++ b/impeller/impeller/compositor/context.mm @@ -5,14 +5,19 @@ #include "context.h" #include "flutter/fml/logging.h" +#include "flutter/fml/paths.h" +#include "impeller/compositor/shader_library.h" namespace impeller { -Context::Context() : device_(::MTLCreateSystemDefaultDevice()) { +Context::Context(std::string shaders_directory) + : device_(::MTLCreateSystemDefaultDevice()) { + // Setup device. if (!device_) { return; } + // Setup command queues. render_queue_ = device_.newCommandQueue; transfer_queue_ = device_.newCommandQueue; @@ -23,6 +28,25 @@ render_queue_.label = @"Impeller Render Queue"; transfer_queue_.label = @"Impeller Transfer Queue"; + // Setup the shader library. + { + NSError* shader_library_error = nil; + auto shader_library_path = + fml::paths::JoinPaths({shaders_directory, "impeller.metallib"}); + id library = + [device_ newLibraryWithFile:@(shader_library_path.c_str()) + error:&shader_library_error]; + if (!library) { + FML_LOG(ERROR) << "Could not create shader library: " + << shader_library_error.localizedDescription.UTF8String; + return; + } + + // std::make_shared disallowed because of private friend ctor. + shader_library_ = + std::shared_ptr(new ShaderLibrary(library)); + } + is_valid_ = true; } diff --git a/impeller/impeller/compositor/pipeline_builder.h b/impeller/impeller/compositor/pipeline_builder.h index d2e1c7002e048..0afa12fdb44d6 100644 --- a/impeller/impeller/compositor/pipeline_builder.h +++ b/impeller/impeller/compositor/pipeline_builder.h @@ -4,8 +4,12 @@ #pragma once +#include + #include "flutter/fml/macros.h" #include "impeller/compositor/pipeline_descriptor.h" +#include "impeller/compositor/shader_library.h" +#include "impeller/compositor/vertex_descriptor.h" namespace impeller { @@ -15,7 +19,17 @@ class PipelineBuilder { ~PipelineBuilder(); - PipelineDescriptor Build() const; + PipelineBuilder& SetLabel(); + + PipelineBuilder& SetSampleCountCount(size_t samples); + + PipelineBuilder& SetVertexFunction(std::shared_ptr function); + + PipelineBuilder& SetFragmentFunction( + std::shared_ptr function); + + PipelineBuilder& SetVertexDescriptor( + std::shared_ptr vertex_descriptor); private: FML_DISALLOW_COPY_AND_ASSIGN(PipelineBuilder); diff --git a/impeller/impeller/compositor/renderer.h b/impeller/impeller/compositor/renderer.h index 8fc32a5e89dae..7765af8764ea0 100644 --- a/impeller/impeller/compositor/renderer.h +++ b/impeller/impeller/compositor/renderer.h @@ -15,7 +15,7 @@ namespace impeller { class Renderer { public: - Renderer(); + Renderer(std::string shaders_directory); ~Renderer(); diff --git a/impeller/impeller/compositor/renderer.mm b/impeller/impeller/compositor/renderer.mm index 5d5b8ac45af92..8ff3f03f53fb3 100644 --- a/impeller/impeller/compositor/renderer.mm +++ b/impeller/impeller/compositor/renderer.mm @@ -8,8 +8,8 @@ namespace impeller { -Renderer::Renderer() - : context_(std::make_shared()), +Renderer::Renderer(std::string shaders_directory) + : context_(std::make_shared(std::move(shaders_directory))), surface_(std::make_unique(context_)) { if (!context_->IsValid()) { return; diff --git a/impeller/impeller/compositor/shader_library.h b/impeller/impeller/compositor/shader_library.h index 6d62e86a9731d..dede6505e29f8 100644 --- a/impeller/impeller/compositor/shader_library.h +++ b/impeller/impeller/compositor/shader_library.h @@ -4,17 +4,53 @@ #pragma once +#include + +#include +#include + #include "flutter/fml/macros.h" namespace impeller { -class ShaderLibrary { +class Context; + +enum class ShaderStage { + kVertex, + kFragment, +}; + +class ShaderFunction { public: - ShaderLibrary(); + ~ShaderFunction(); + + ShaderStage GetStage() const; + + private: + friend class ShaderLibrary; + + id function_ = nullptr; + ShaderStage stage_; + ShaderFunction(id function, ShaderStage stage); + + FML_DISALLOW_COPY_AND_ASSIGN(ShaderFunction); +}; + +class ShaderLibrary { + public: ~ShaderLibrary(); + std::shared_ptr GetFunction(const std::string& name, + ShaderStage stage); + private: + friend class Context; + + id library_ = nullptr; + + ShaderLibrary(id library); + FML_DISALLOW_COPY_AND_ASSIGN(ShaderLibrary); }; diff --git a/impeller/impeller/compositor/shader_library.mm b/impeller/impeller/compositor/shader_library.mm index e69de29bb2d1d..821ebc50d88fc 100644 --- a/impeller/impeller/compositor/shader_library.mm +++ b/impeller/impeller/compositor/shader_library.mm @@ -0,0 +1,32 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "impeller/compositor/shader_library.h" + +namespace impeller { + +ShaderFunction::ShaderFunction(id function, ShaderStage stage) + : function_(function), stage_(stage) {} + +ShaderFunction::~ShaderFunction() = default; + +ShaderStage ShaderFunction::GetStage() const { + return stage_; +} + +ShaderLibrary::ShaderLibrary(id library) : library_(library) {} + +ShaderLibrary::~ShaderLibrary() = default; + +std::shared_ptr ShaderLibrary::GetFunction( + const std::string& name, + ShaderStage stage) { + auto function = [library_ newFunctionWithName:@(name.c_str())]; + if (!function) { + return nullptr; + } + return std::shared_ptr(new ShaderFunction(function, stage)); +} + +} // namespace impeller diff --git a/impeller/impeller/compositor/surface.h b/impeller/impeller/compositor/surface.h index a172a0e6b301c..fed78ce1528f1 100644 --- a/impeller/impeller/compositor/surface.h +++ b/impeller/impeller/compositor/surface.h @@ -25,7 +25,7 @@ class Surface { private: std::shared_ptr context_; - dispatch_semaphore_t frames_in_flight_sema_; + dispatch_semaphore_t frames_in_flight_sema_ = nullptr; bool is_valid_ = false; FML_DISALLOW_COPY_AND_ASSIGN(Surface); diff --git a/impeller/impeller/compositor/vertex_descriptor.h b/impeller/impeller/compositor/vertex_descriptor.h new file mode 100644 index 0000000000000..bda80d5a19eda --- /dev/null +++ b/impeller/impeller/compositor/vertex_descriptor.h @@ -0,0 +1,21 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#pragma once + +#include "flutter/fml/macros.h" + +namespace impeller { + +class VertexDescriptor { + public: + VertexDescriptor(); + + ~VertexDescriptor(); + + private: + FML_DISALLOW_COPY_AND_ASSIGN(VertexDescriptor); +}; + +} // namespace impeller diff --git a/impeller/impeller/compositor/vertex_descriptor.mm b/impeller/impeller/compositor/vertex_descriptor.mm new file mode 100644 index 0000000000000..f146a9265cb2f --- /dev/null +++ b/impeller/impeller/compositor/vertex_descriptor.mm @@ -0,0 +1,13 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "impeller/compositor/vertex_descriptor.h" + +namespace impeller { + +VertexDescriptor::VertexDescriptor() = default; + +VertexDescriptor::~VertexDescriptor() = default; + +} // namespace impeller From df91fb20c252439fbd624cce12e1b26ebda058a6 Mon Sep 17 00:00:00 2001 From: Chinmay Garde Date: Sun, 16 May 2021 14:34:34 -0700 Subject: [PATCH 037/433] Cleanup reflector. --- impeller/compiler/reflector.cc | 104 +-------------------------------- impeller/compiler/reflector.h | 4 +- 2 files changed, 3 insertions(+), 105 deletions(-) diff --git a/impeller/compiler/reflector.cc b/impeller/compiler/reflector.cc index 347ba8d21da03..4e32f26432044 100644 --- a/impeller/compiler/reflector.cc +++ b/impeller/compiler/reflector.cc @@ -11,109 +11,7 @@ namespace impeller { namespace compiler { -// std::string SpvReflectInterfaceVariableToString( -// const SpvReflectInterfaceVariable& var) { -// std::stringstream stream; -// stream << "--- Interface Variable" << std::endl; -// stream << "spirv_id=" << var.spirv_id << std::endl; -// if (var.name) { -// stream << "name=" << var.name << std::endl; -// } -// stream << "location=" << var.location << std::endl; -// stream << "storage_class=" << var.storage_class << std::endl; -// if (var.semantic) { -// stream << "semantic=" << var.semantic << std::endl; -// } -// stream << "decoration_flags=" << var.decoration_flags << std::endl; -// stream << "built_in=" << var.built_in << std::endl; -// // stream << "numeric=" << var.numeric << std::endl; -// // stream << "array=" << var.array << std::endl; -// stream << "member_count=" << var.member_count << std::endl; -// // stream << "members=" << var.members << std::endl; -// stream << "format=" << var.format << std::endl; -// stream << "type_description=" << var.type_description << std::endl; -// return stream.str(); -// } - -// std::string SpvReflectDescriptorBindingToString( -// const SpvReflectDescriptorBinding& des) { -// std::stringstream stream; -// stream << "--- Descriptor Set Binding" << std::endl; -// stream << "spirv_id=" << des.spirv_id << std::endl; -// if (des.name) { -// stream << "name=" << des.name << std::endl; -// } -// stream << "binding=" << des.binding << std::endl; -// stream << "input_attachment_index=" << des.input_attachment_index -// << std::endl; -// stream << "set=" << des.set << std::endl; -// stream << "descriptor_type=" << des.descriptor_type << std::endl; -// stream << "resource_type=" << des.resource_type << std::endl; -// // stream << "image=" << des.image << std::endl; -// // stream << "block=" << des.block << std::endl; -// // stream << "array=" << des.array << std::endl; -// stream << "count=" << des.count << std::endl; -// stream << "accessed=" << des.accessed << std::endl; -// stream << "uav_counter_id=" << des.uav_counter_id << std::endl; -// stream << "uav_counter_binding=" << des.uav_counter_binding << std::endl; -// stream << "type_description=" << des.type_description << std::endl; -// return stream.str(); -// } - -Reflector::Reflector(const fml::Mapping& spirv_binary) { - // if (spirv_binary.GetMapping() == nullptr) { - // return; - // } - - // spv_reflect::ShaderModule module(spirv_binary.GetSize(), - // spirv_binary.GetMapping()); - - // if (module.GetResult() != SpvReflectResult::SPV_REFLECT_RESULT_SUCCESS) { - // return; - // } - - // FML_LOG(ERROR) << "~~~~~~~~~~~~~ Interface Variables ~~~~~~~~~~~~~"; - - // { - // uint32_t count = 0; - // if (module.EnumerateInterfaceVariables(&count, nullptr) != - // SpvReflectResult::SPV_REFLECT_RESULT_SUCCESS) { - // return; - // } - - // std::vector input_variables; - // input_variables.resize(count); - // if (module.EnumerateInterfaceVariables(&count, input_variables.data()) != - // SpvReflectResult::SPV_REFLECT_RESULT_SUCCESS) { - // return; - // } - - // for (const auto& input_variable : input_variables) { - // FML_LOG(ERROR) << SpvReflectInterfaceVariableToString(*input_variable); - // } - // } - - // FML_LOG(ERROR) << "~~~~~~~~~~~~~ Descriptor Bindings ~~~~~~~~~~~~~"; - - // { - // uint32_t count = 0; - // if (module.EnumerateDescriptorBindings(&count, nullptr) != - // SpvReflectResult::SPV_REFLECT_RESULT_SUCCESS) { - // return; - // } - - // std::vector input_variables; - // input_variables.resize(count); - // if (module.EnumerateDescriptorBindings(&count, input_variables.data()) != - // SpvReflectResult::SPV_REFLECT_RESULT_SUCCESS) { - // return; - // } - - // for (const auto& input_variable : input_variables) { - // FML_LOG(ERROR) << SpvReflectDescriptorBindingToString(*input_variable); - // } - // } - +Reflector::Reflector(const spirv_cross::CompilerMSL& compiler) { is_valid_ = true; } diff --git a/impeller/compiler/reflector.h b/impeller/compiler/reflector.h index 185d5580966ea..06722b8b9dfb7 100644 --- a/impeller/compiler/reflector.h +++ b/impeller/compiler/reflector.h @@ -6,14 +6,14 @@ #include "flutter/fml/macros.h" #include "flutter/fml/mapping.h" -#include "third_party/spirv_reflect/spirv_reflect.h" +#include "third_party/spirv_cross/spirv_msl.hpp" namespace impeller { namespace compiler { class Reflector { public: - Reflector(const fml::Mapping& spirv_binary); + Reflector(const spirv_cross::CompilerMSL& compiler); ~Reflector(); From 1729084a631ba110bc07949f2124a2edefbc5d53 Mon Sep 17 00:00:00 2001 From: Chinmay Garde Date: Tue, 18 May 2021 15:07:34 -0700 Subject: [PATCH 038/433] Setup reflection JSON. --- impeller/compiler/BUILD.gn | 2 + impeller/compiler/compiler.cc | 6 +- impeller/compiler/compiler.h | 2 + impeller/compiler/compiler_unittests.cc | 5 +- impeller/compiler/fixtures/sample.vert | 27 +- impeller/compiler/fixtures/types.h | 12 +- impeller/compiler/impellerc_main.cc | 31 +++ impeller/compiler/reflector.cc | 241 +++++++++++++++++- impeller/compiler/reflector.h | 11 + impeller/compiler/switches.cc | 10 + impeller/compiler/switches.h | 3 + impeller/compiler/tools/compiler.gni | 12 + .../impeller/compositor/shaders/box/box.vert | 13 +- 13 files changed, 343 insertions(+), 32 deletions(-) diff --git a/impeller/compiler/BUILD.gn b/impeller/compiler/BUILD.gn index 540ccc7328842..6b8bba531a020 100644 --- a/impeller/compiler/BUILD.gn +++ b/impeller/compiler/BUILD.gn @@ -19,6 +19,8 @@ source_set("compiler_lib") { public_deps = [ "//flutter/fml", "//flutter/runtime:libdart", + "//third_party/inja", + "//third_party/rapidjson", "//third_party/shaderc_flutter", "//third_party/spirv_cross_flutter", "//third_party/spirv_reflect_flutter", diff --git a/impeller/compiler/compiler.cc b/impeller/compiler/compiler.cc index f95cbe4a4c4ea..a6a4c59bae17f 100644 --- a/impeller/compiler/compiler.cc +++ b/impeller/compiler/compiler.cc @@ -298,7 +298,7 @@ Compiler::Compiler(const fml::Mapping& source_mapping, return; } - reflector_ = std::make_unique(*GetSPIRVAssembly()); + reflector_ = std::make_unique(msl_compiler); if (!reflector_->IsValid()) { COMPILER_ERROR << "Could not complete reflection on generated shader."; @@ -433,5 +433,9 @@ std::unique_ptr Compiler::CreateDepfileContents( [contents](auto, auto) {}); } +const Reflector* Compiler::GetReflector() const { + return reflector_.get(); +} + } // namespace compiler } // namespace impeller diff --git a/impeller/compiler/compiler.h b/impeller/compiler/compiler.h index 7c2b7f0e8d6dc..7ff3883ff8093 100644 --- a/impeller/compiler/compiler.h +++ b/impeller/compiler/compiler.h @@ -61,6 +61,8 @@ class Compiler { std::unique_ptr CreateDepfileContents( std::initializer_list targets) const; + const Reflector* GetReflector() const; + private: SourceOptions options_; std::shared_ptr spv_result_; diff --git a/impeller/compiler/compiler_unittests.cc b/impeller/compiler/compiler_unittests.cc index 4ff37a35f34a5..d496312c68556 100644 --- a/impeller/compiler/compiler_unittests.cc +++ b/impeller/compiler/compiler_unittests.cc @@ -50,13 +50,16 @@ TEST_F(CompilerTest, ShaderKindMatchingIsSuccessful) { } TEST_F(CompilerTest, CanCompileSample) { - constexpr const char* kShaderFixtureName = "sample.frag"; + constexpr const char* kShaderFixtureName = "sample.vert"; auto fixture = flutter::testing::OpenFixtureAsMapping(kShaderFixtureName); ASSERT_NE(fixture->GetMapping(), nullptr); Compiler::SourceOptions options(kShaderFixtureName); options.working_directory = std::make_shared( flutter::testing::OpenFixturesDirectory()); Compiler compiler(*fixture.get(), options); + if (!compiler.IsValid()) { + FML_LOG(ERROR) << compiler.GetErrorMessages(); + } ASSERT_TRUE(compiler.IsValid()); WriteCompilerIntermediates(compiler, kShaderFixtureName); } diff --git a/impeller/compiler/fixtures/sample.vert b/impeller/compiler/fixtures/sample.vert index 727406ac868d1..ba455ccb8660d 100644 --- a/impeller/compiler/fixtures/sample.vert +++ b/impeller/compiler/fixtures/sample.vert @@ -1,23 +1,20 @@ -#version 450 -#extension GL_ARB_separate_shader_objects : enable +#include "types.h" -// Uniforms - -layout(set = 0, binding = 0) uniform UniformBufferObject { - mat4 mvp; +uniform UniformBufferObject { + Uniforms uniforms; } ubo; -// In - -layout(location = 0) in vec3 inPosition; -layout(location = 1) in vec3 inNormal; -layout(location = 2) in vec2 inTextureCoords; +uniform sampler2D world; -// Out +in vec2 inPosition; +in vec3 inPosition22; +in vec4 inAnotherPosition; +in float stuff; -layout(location = 0) out vec2 outTextureCoords; +out vec4 outStuff; void main() { - gl_Position = ubo.mvp * vec4(inPosition, 1.0); - outTextureCoords = inTextureCoords; + gl_Position = ubo.uniforms.projection * ubo.uniforms.view * ubo.uniforms.model * vec4(inPosition22, 1.0) * inAnotherPosition; + outStuff = texture(world, inPosition); } + diff --git a/impeller/compiler/fixtures/types.h b/impeller/compiler/fixtures/types.h index 86572a665512f..d8775956bacd3 100644 --- a/impeller/compiler/fixtures/types.h +++ b/impeller/compiler/fixtures/types.h @@ -2,12 +2,8 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -#include "common_types.h" - -struct Hello { - Vector4 stuff; - Vector3 more_stuff; - float dunno; - float this_bit_is_not_used; - Vector4 yet_more_stuff; +struct Uniforms { + mat4 model; + mat4 view; + mat4 projection; }; diff --git a/impeller/compiler/impellerc_main.cc b/impeller/compiler/impellerc_main.cc index 31c32fe0e0741..bd7f885babe84 100644 --- a/impeller/compiler/impellerc_main.cc +++ b/impeller/compiler/impellerc_main.cc @@ -65,6 +65,37 @@ bool Main(const fml::CommandLine& command_line) { return false; } + if (!switches.reflection_json_name.empty()) { + if (!fml::WriteAtomically(*switches.working_directory, + switches.reflection_json_name.c_str(), + *compiler.GetReflector()->GetReflectionJSON())) { + std::cerr << "Could not write reflection json to " + << switches.reflection_json_name << std::endl; + return false; + } + } + + if (!switches.reflection_header_name.empty()) { + if (!fml::WriteAtomically( + *switches.working_directory, + switches.reflection_header_name.c_str(), + *compiler.GetReflector()->GetReflectionHeader())) { + std::cerr << "Could not write reflection header to " + << switches.reflection_header_name << std::endl; + return false; + } + } + + if (!switches.reflection_cc_name.empty()) { + if (!fml::WriteAtomically(*switches.working_directory, + switches.reflection_cc_name.c_str(), + *compiler.GetReflector()->GetReflectionCC())) { + std::cerr << "Could not write reflection CC to " + << switches.reflection_cc_name << std::endl; + return false; + } + } + if (!switches.depfile_path.empty()) { if (!fml::WriteAtomically( *switches.working_directory, switches.depfile_path.c_str(), diff --git a/impeller/compiler/reflector.cc b/impeller/compiler/reflector.cc index 4e32f26432044..8b14a2d557ab5 100644 --- a/impeller/compiler/reflector.cc +++ b/impeller/compiler/reflector.cc @@ -4,14 +4,241 @@ #include "flutter/impeller/compiler/reflector.h" +#include #include +#include "flutter/fml/closure.h" #include "flutter/fml/logging.h" +#include "inja/inja.hpp" +#include "rapidjson/document.h" +#include "rapidjson/prettywriter.h" +#include "rapidjson/rapidjson.h" +#include "rapidjson/stringbuffer.h" namespace impeller { namespace compiler { -Reflector::Reflector(const spirv_cross::CompilerMSL& compiler) { +using Writer = rapidjson::PrettyWriter; + +static std::string BaseTypeToString(spirv_cross::SPIRType::BaseType type) { + using Type = spirv_cross::SPIRType::BaseType; + switch (type) { + case Type::Void: + return "Void"; + case Type::Boolean: + return "Boolean"; + case Type::SByte: + return "SByte"; + case Type::UByte: + return "UByte"; + case Type::Short: + return "Short"; + case Type::UShort: + return "UShort"; + case Type::Int: + return "Int"; + case Type::UInt: + return "UInt"; + case Type::Int64: + return "Int64"; + case Type::UInt64: + return "UInt64"; + case Type::AtomicCounter: + return "AtomicCounter"; + case Type::Half: + return "Half"; + case Type::Float: + return "Float"; + case Type::Double: + return "Double"; + case Type::Struct: + return "Struct"; + case Type::Image: + return "Image"; + case Type::SampledImage: + return "SampledImage"; + case Type::Sampler: + return "Sampler"; + case Type::AccelerationStructure: + return "AccelerationStructure"; + case Type::RayQuery: + return "RayQuery"; + default: + return "unknown"; + } +} + +static bool ReflectType(Writer& writer, + const spirv_cross::CompilerMSL& compiler, + const spirv_cross::SPIRType& type) { + writer.Key("type"); + writer.StartObject(); + + writer.Key("type_name"); + writer.String(BaseTypeToString(type.basetype)); + + writer.Key("member_types"); + writer.StartArray(); + for (const auto& member : type.member_types) { + if (!ReflectType(writer, compiler, compiler.get_type(member))) { + return false; + } + } + writer.EndArray(); + + writer.EndObject(); + return true; +} + +static bool ReflectBaseResource(Writer& writer, + const spirv_cross::CompilerMSL& compiler, + const spirv_cross::Resource& res) { + writer.Key("name"); + writer.String(compiler.get_name(res.id)); + + writer.Key("descriptor_set"); + writer.Uint64(compiler.get_decoration( + res.id, spv::Decoration::DecorationDescriptorSet)); + + writer.Key("binding"); + writer.Uint64( + compiler.get_decoration(res.id, spv::Decoration::DecorationBinding)); + + writer.Key("location"); + writer.Uint64( + compiler.get_decoration(res.id, spv::Decoration::DecorationLocation)); + + if (!ReflectType(writer, compiler, compiler.get_type(res.type_id))) { + return false; + } + + return true; +} + +static bool ReflectStageInput(Writer& writer, + const spirv_cross::CompilerMSL& compiler, + const spirv_cross::Resource& input) { + writer.StartObject(); + + if (!ReflectBaseResource(writer, compiler, input)) { + return false; + } + + writer.EndObject(); + return true; +} + +static bool ReflectUniformBuffer(Writer& writer, + const spirv_cross::CompilerMSL& compiler, + const spirv_cross::Resource& buffer) { + writer.StartObject(); + + if (!ReflectBaseResource(writer, compiler, buffer)) { + return false; + } + + writer.Key("index"); + writer.Uint64( + compiler.get_decoration(buffer.id, spv::Decoration::DecorationIndex)); + + writer.EndObject(); + return true; +} + +static std::string ExecutionModelToString(spv::ExecutionModel model) { + switch (model) { + case spv::ExecutionModel::ExecutionModelVertex: + return "vertex"; + case spv::ExecutionModel::ExecutionModelFragment: + return "fragment"; + default: + return "unsupported"; + } +} + +static std::shared_ptr ReflectTemplateArguments( + const spirv_cross::CompilerMSL& compiler) { + auto buffer = std::make_shared(); + Writer writer(*buffer); + + writer.StartObject(); // root + + { + const auto& entrypoints = compiler.get_entry_points_and_stages(); + if (entrypoints.size() != 1) { + FML_LOG(ERROR) << "Incorrect number of entrypoints in the shader. Found " + << entrypoints.size() << " but expected 1."; + return nullptr; + } + + writer.Key("entrypoint"); + writer.String(entrypoints.front().name); + + writer.Key("shader_stage"); + writer.String(ExecutionModelToString(entrypoints.front().execution_model)); + } + + { + writer.Key("uniform_buffers"); + writer.StartArray(); + for (const auto& uniform_buffer : + compiler.get_shader_resources().uniform_buffers) { + if (!ReflectUniformBuffer(writer, compiler, uniform_buffer)) { + FML_LOG(ERROR) << "Could not reflect uniform buffer."; + return nullptr; + } + } + writer.EndArray(); + } + + { + writer.Key("stage_inputs"); + writer.StartArray(); + for (const auto& input : compiler.get_shader_resources().stage_inputs) { + if (!ReflectStageInput(writer, compiler, input)) { + FML_LOG(ERROR) << "Could not reflect uniform buffer."; + return nullptr; + } + } + writer.EndArray(); + } + + writer.EndObject(); // root + + return std::make_shared( + buffer->GetString(), buffer->GetSize(), [buffer]() {}); +} + +static std::shared_ptr ConstructReflectionHeader( + const spirv_cross::CompilerMSL& compiler, + const fml::Mapping* reflection_args) { + return nullptr; +} + +static std::shared_ptr ConstructReflectionCC( + const spirv_cross::CompilerMSL& compiler, + const fml::Mapping* reflection_args) { + return nullptr; +} + +Reflector::Reflector(const spirv_cross::CompilerMSL& compiler) + : template_arguments_(ReflectTemplateArguments(compiler)), + reflection_header_( + ConstructReflectionHeader(compiler, template_arguments_.get())), + reflection_cc_( + ConstructReflectionCC(compiler, template_arguments_.get())) { + if (!template_arguments_) { + return; + } + + if (!reflection_header_) { + return; + } + + if (!reflection_cc_) { + return; + } + is_valid_ = true; } @@ -21,5 +248,17 @@ bool Reflector::IsValid() const { return is_valid_; } +std::shared_ptr Reflector::GetReflectionJSON() const { + return template_arguments_; +} + +std::shared_ptr Reflector::GetReflectionHeader() const { + return nullptr; +} + +std::shared_ptr Reflector::GetReflectionCC() const { + return nullptr; +} + } // namespace compiler } // namespace impeller diff --git a/impeller/compiler/reflector.h b/impeller/compiler/reflector.h index 06722b8b9dfb7..a68e1a9602689 100644 --- a/impeller/compiler/reflector.h +++ b/impeller/compiler/reflector.h @@ -4,6 +4,8 @@ #pragma once +#include + #include "flutter/fml/macros.h" #include "flutter/fml/mapping.h" #include "third_party/spirv_cross/spirv_msl.hpp" @@ -19,7 +21,16 @@ class Reflector { bool IsValid() const; + std::shared_ptr GetReflectionJSON() const; + + std::shared_ptr GetReflectionHeader() const; + + std::shared_ptr GetReflectionCC() const; + private: + std::shared_ptr template_arguments_; + std::shared_ptr reflection_header_; + std::shared_ptr reflection_cc_; bool is_valid_ = false; FML_DISALLOW_COPY_AND_ASSIGN(Reflector); diff --git a/impeller/compiler/switches.cc b/impeller/compiler/switches.cc index 2ea0e8549dbd4..9b01363fd875b 100644 --- a/impeller/compiler/switches.cc +++ b/impeller/compiler/switches.cc @@ -16,6 +16,10 @@ void Switches::PrintHelp(std::ostream& stream) { stream << "--input=" << std::endl; stream << "--metal=" << std::endl; stream << "--spirv=" << std::endl; + stream << "[optional] --reflection-json=" << std::endl; + stream << "[optional] --reflection-header=" + << std::endl; + stream << "[optional] --reflection-cc=" << std::endl; stream << "[optional,multiple] --include=" << std::endl; stream << "[optional] --depfile=" << std::endl; } @@ -32,6 +36,12 @@ Switches::Switches(const fml::CommandLine& command_line) source_file_name(command_line.GetOptionValueWithDefault("input", "")), metal_file_name(command_line.GetOptionValueWithDefault("metal", "")), spirv_file_name(command_line.GetOptionValueWithDefault("spirv", "")), + reflection_json_name( + command_line.GetOptionValueWithDefault("reflection-json", "")), + reflection_header_name( + command_line.GetOptionValueWithDefault("reflection-header", "")), + reflection_cc_name( + command_line.GetOptionValueWithDefault("reflection-cc", "")), depfile_path(command_line.GetOptionValueWithDefault("depfile", "")) { if (!working_directory || !working_directory->is_valid()) { return; diff --git a/impeller/compiler/switches.h b/impeller/compiler/switches.h index d9e1df1d92a75..7028d4ab4e95f 100644 --- a/impeller/compiler/switches.h +++ b/impeller/compiler/switches.h @@ -21,6 +21,9 @@ struct Switches { std::string source_file_name; std::string metal_file_name; std::string spirv_file_name; + std::string reflection_json_name; + std::string reflection_header_name; + std::string reflection_cc_name; std::string depfile_path; Switches(); diff --git a/impeller/compiler/tools/compiler.gni b/impeller/compiler/tools/compiler.gni index e3230b4244ee2..e6d5c65e1ca65 100644 --- a/impeller/compiler/tools/compiler.gni +++ b/impeller/compiler/tools/compiler.gni @@ -17,6 +17,9 @@ template("impeller_shaders") { metal_intermediate = "$target_gen_dir/{{source_file_part}}.metal" spirv_intermediate = "$target_gen_dir/{{source_file_part}}.spirv" + reflection_json_intermediate = "$target_gen_dir/{{source_file_part}}.json" + reflection_header_intermediate = "$target_gen_dir/{{source_file_part}}.h" + reflection_cc_intermediate = "$target_gen_dir/{{source_file_part}}.cc" outputs = [ metal_intermediate ] @@ -26,12 +29,21 @@ template("impeller_shaders") { spirv_intermediate_path = rebase_path(spirv_intermediate, root_build_dir) depfile_intermediate_path = rebase_path(depfile_path, root_build_dir) + reflection_json_path = + rebase_path(reflection_json_intermediate, root_build_dir) + reflection_header_path = + rebase_path(reflection_header_intermediate, root_build_dir) + reflection_cc_path = rebase_path(reflection_cc_intermediate, root_build_dir) + depfile = depfile_path args = [ "--input=$source_path", "--metal=$metal_intermediate_path", "--spirv=$spirv_intermediate_path", + "--reflection-json=$reflection_json_path", + "--reflection-header=$reflection_header_path", + "--reflection-cc=$reflection_cc_path", "--include={{source_dir}}", "--depfile=$depfile_intermediate_path", ] diff --git a/impeller/impeller/compositor/shaders/box/box.vert b/impeller/impeller/compositor/shaders/box/box.vert index b3a757b5a44d9..e8839eb27c293 100644 --- a/impeller/impeller/compositor/shaders/box/box.vert +++ b/impeller/impeller/compositor/shaders/box/box.vert @@ -4,16 +4,17 @@ uniform UniformBufferObject { Uniforms uniforms; } ubo; -in vec3 inPosition; +uniform sampler2D world; +in vec2 inPosition; +in vec3 inPosition2; in vec4 inAnotherPosition; +in float stuff; -layout(location = 3) in float stuff; - -out float outStuff; +out vec4 outStuff; void main() { - gl_Position = ubo.uniforms.projection * ubo.uniforms.view * ubo.uniforms.model * vec4(inPosition, 1.0) * inAnotherPosition; - outStuff = stuff; + gl_Position = ubo.uniforms.projection * ubo.uniforms.view * ubo.uniforms.model * vec4(inPosition2, 1.0) * inAnotherPosition; + outStuff = texture(world, inPosition); } From 4b124aeeab3f8763462f8155c1a91d0b85b5165e Mon Sep 17 00:00:00 2001 From: Chinmay Garde Date: Wed, 19 May 2021 13:26:12 -0700 Subject: [PATCH 039/433] Fix code generation for stage inputs. --- impeller/compiler/BUILD.gn | 3 + impeller/compiler/code_gen_template.h | 50 ++++++++++++++ impeller/compiler/compiler.cc | 6 +- impeller/compiler/compiler.h | 4 +- impeller/compiler/impellerc_main.cc | 13 +++- impeller/compiler/reflector.cc | 66 +++++++++++++------ impeller/compiler/reflector.h | 8 ++- impeller/compiler/tools/compiler.gni | 38 +++++++++-- impeller/compiler/utilities.cc | 41 ++++++++++++ impeller/compiler/utilities.h | 20 ++++++ impeller/impeller/BUILD.gn | 13 ++++ impeller/impeller/compositor/shader_library.h | 6 +- impeller/impeller/compositor/shader_types.cc | 11 ++++ impeller/impeller/compositor/shader_types.h | 21 ++++++ .../impeller/compositor/shaders/box/box.vert | 12 ++-- 15 files changed, 271 insertions(+), 41 deletions(-) create mode 100644 impeller/compiler/code_gen_template.h create mode 100644 impeller/compiler/utilities.cc create mode 100644 impeller/compiler/utilities.h create mode 100644 impeller/impeller/compositor/shader_types.cc create mode 100644 impeller/impeller/compositor/shader_types.h diff --git a/impeller/compiler/BUILD.gn b/impeller/compiler/BUILD.gn index 6b8bba531a020..14616f310b92d 100644 --- a/impeller/compiler/BUILD.gn +++ b/impeller/compiler/BUILD.gn @@ -6,6 +6,7 @@ import("//flutter/testing/testing.gni") source_set("compiler_lib") { sources = [ + "code_gen_template.h", "compiler.cc", "compiler.h", "include_dir.h", @@ -14,6 +15,8 @@ source_set("compiler_lib") { "reflector.h", "switches.cc", "switches.h", + "utilities.cc", + "utilities.h", ] public_deps = [ diff --git a/impeller/compiler/code_gen_template.h b/impeller/compiler/code_gen_template.h new file mode 100644 index 0000000000000..34c218128a3cb --- /dev/null +++ b/impeller/compiler/code_gen_template.h @@ -0,0 +1,50 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include + +namespace impeller { +namespace compiler { + +constexpr std::string_view kReflectionHeaderTemplate = R"~~( +// THIS FILE IS GENERATED BY impellerc. +// DO NOT EDIT OR CHECK THIS INTO SOURCE CONTROL + +#include "shader_types.h" + +namespace impeller { +namespace shader { + +struct {{camel_case(shader_name)}} { + static constexpr std::string_view kEntrypointName = "{{entrypoint}}"; + static constexpr ShaderStage kShaderStage = {{shader_stage}}; + + // Stage Inputs +{% for stage_input in stage_inputs %} + // Stage input {{stage_input.name}} + static constexpr ShaderStageInput kInput{{camel_case(stage_input.name)}} = {"{{stage_input.name}}", {{stage_input.location}}}; +{% endfor %} +}; // struct {{camel_case(shader_name)}} + +} // namespace shader +} // namespace impeller +)~~"; + +constexpr std::string_view kReflectionCCTemplate = R"~~( +// THIS FILE IS GENERATED BY impellerc. +// DO NOT EDIT OR CHECK THIS INTO SOURCE CONTROL + +#include "{{header_file_name}}" + +namespace impeller { +namespace shader { + + + +} // namespace shader +} // namespace impeller +)~~"; + +} // namespace compiler +} // namespace impeller diff --git a/impeller/compiler/compiler.cc b/impeller/compiler/compiler.cc index a6a4c59bae17f..6b67cd4be9fe7 100644 --- a/impeller/compiler/compiler.cc +++ b/impeller/compiler/compiler.cc @@ -199,7 +199,8 @@ static spv::ExecutionModel ToExecutionModel(Compiler::SourceType type) { } Compiler::Compiler(const fml::Mapping& source_mapping, - SourceOptions source_options) + SourceOptions source_options, + Reflector::Options reflector_options) : options_(source_options) { if (source_mapping.GetMapping() == nullptr) { COMPILER_ERROR << "Could not read shader source."; @@ -298,7 +299,8 @@ Compiler::Compiler(const fml::Mapping& source_mapping, return; } - reflector_ = std::make_unique(msl_compiler); + reflector_ = + std::make_unique(std::move(reflector_options), msl_compiler); if (!reflector_->IsValid()) { COMPILER_ERROR << "Could not complete reflection on generated shader."; diff --git a/impeller/compiler/compiler.h b/impeller/compiler/compiler.h index 7ff3883ff8093..3c1dd4e4ffd67 100644 --- a/impeller/compiler/compiler.h +++ b/impeller/compiler/compiler.h @@ -44,7 +44,9 @@ class Compiler { : type(SourceTypeFromFileName(file_name)), file_name(file_name) {} }; - Compiler(const fml::Mapping& source_mapping, SourceOptions options); + Compiler(const fml::Mapping& source_mapping, + SourceOptions options, + Reflector::Options reflector_options); ~Compiler(); diff --git a/impeller/compiler/impellerc_main.cc b/impeller/compiler/impellerc_main.cc index bd7f885babe84..23132fd08277b 100644 --- a/impeller/compiler/impellerc_main.cc +++ b/impeller/compiler/impellerc_main.cc @@ -2,12 +2,15 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. +#include + #include "flutter/fml/command_line.h" #include "flutter/fml/file.h" #include "flutter/fml/macros.h" #include "flutter/fml/mapping.h" #include "flutter/impeller/compiler/compiler.h" #include "flutter/impeller/compiler/switches.h" +#include "flutter/impeller/compiler/utilities.h" #include "third_party/shaderc/libshaderc/include/shaderc/shaderc.hpp" namespace impeller { @@ -42,7 +45,15 @@ bool Main(const fml::CommandLine& command_line) { switches.source_file_name, Compiler::SourceTypeFromFileName(switches.source_file_name)); - Compiler compiler(*source_file_mapping, options); + Reflector::Options reflector_options; + reflector_options.shader_name = + InferShaderNameFromPath(switches.source_file_name); + reflector_options.header_file_name = + std::filesystem::path{switches.reflection_header_name} + .filename() + .native(); + + Compiler compiler(*source_file_mapping, options, reflector_options); if (!compiler.IsValid()) { std::cerr << "Compilation failed." << std::endl; std::cerr << compiler.GetErrorMessages() << std::endl; diff --git a/impeller/compiler/reflector.cc b/impeller/compiler/reflector.cc index 8b14a2d557ab5..e1d91c862bff3 100644 --- a/impeller/compiler/reflector.cc +++ b/impeller/compiler/reflector.cc @@ -9,6 +9,8 @@ #include "flutter/fml/closure.h" #include "flutter/fml/logging.h" +#include "flutter/impeller/compiler/code_gen_template.h" +#include "flutter/impeller/compiler/utilities.h" #include "inja/inja.hpp" #include "rapidjson/document.h" #include "rapidjson/prettywriter.h" @@ -148,15 +150,16 @@ static bool ReflectUniformBuffer(Writer& writer, static std::string ExecutionModelToString(spv::ExecutionModel model) { switch (model) { case spv::ExecutionModel::ExecutionModelVertex: - return "vertex"; + return "ShaderStage::kVertex"; case spv::ExecutionModel::ExecutionModelFragment: - return "fragment"; + return "ShaderStage::kFragment"; default: - return "unsupported"; + return "ShaderStage::kUnsupported"; } } static std::shared_ptr ReflectTemplateArguments( + const Reflector::Options& options, const spirv_cross::CompilerMSL& compiler) { auto buffer = std::make_shared(); Writer writer(*buffer); @@ -174,8 +177,14 @@ static std::shared_ptr ReflectTemplateArguments( writer.Key("entrypoint"); writer.String(entrypoints.front().name); + writer.Key("shader_name"); + writer.String(options.shader_name); + writer.Key("shader_stage"); writer.String(ExecutionModelToString(entrypoints.front().execution_model)); + + writer.Key("header_file_name"); + writer.String(options.header_file_name); } { @@ -206,27 +215,46 @@ static std::shared_ptr ReflectTemplateArguments( writer.EndObject(); // root return std::make_shared( - buffer->GetString(), buffer->GetSize(), [buffer]() {}); + reinterpret_cast(buffer->GetString()), buffer->GetSize(), + [buffer](auto, auto) {}); } -static std::shared_ptr ConstructReflectionHeader( +static std::shared_ptr InflateTemplate( const spirv_cross::CompilerMSL& compiler, + const std::string_view& tmpl, const fml::Mapping* reflection_args) { - return nullptr; -} + if (!reflection_args) { + return nullptr; + } -static std::shared_ptr ConstructReflectionCC( - const spirv_cross::CompilerMSL& compiler, - const fml::Mapping* reflection_args) { - return nullptr; + inja::Environment env; + env.set_trim_blocks(true); + env.set_lstrip_blocks(true); + + env.add_callback("camel_case", 1u, [](inja::Arguments& args) { + return ConvertToCamelCase(args.at(0u)->get()); + }); + + auto template_data = inja::json::parse( + reinterpret_cast(reflection_args->GetMapping())); + + auto inflated_template = + std::make_shared(env.render(tmpl, template_data)); + + return std::make_shared( + reinterpret_cast(inflated_template->data()), + inflated_template->size(), [inflated_template](auto, auto) {}); } -Reflector::Reflector(const spirv_cross::CompilerMSL& compiler) - : template_arguments_(ReflectTemplateArguments(compiler)), - reflection_header_( - ConstructReflectionHeader(compiler, template_arguments_.get())), - reflection_cc_( - ConstructReflectionCC(compiler, template_arguments_.get())) { +Reflector::Reflector(Options options, const spirv_cross::CompilerMSL& compiler) + : options_(std::move(options)), + template_arguments_(ReflectTemplateArguments(options_, compiler)), + reflection_header_(InflateTemplate(compiler, + kReflectionHeaderTemplate, + template_arguments_.get())), + reflection_cc_(InflateTemplate(compiler, + kReflectionCCTemplate, + template_arguments_.get())) { if (!template_arguments_) { return; } @@ -253,11 +281,11 @@ std::shared_ptr Reflector::GetReflectionJSON() const { } std::shared_ptr Reflector::GetReflectionHeader() const { - return nullptr; + return reflection_header_; } std::shared_ptr Reflector::GetReflectionCC() const { - return nullptr; + return reflection_cc_; } } // namespace compiler diff --git a/impeller/compiler/reflector.h b/impeller/compiler/reflector.h index a68e1a9602689..337488a05ad27 100644 --- a/impeller/compiler/reflector.h +++ b/impeller/compiler/reflector.h @@ -15,7 +15,12 @@ namespace compiler { class Reflector { public: - Reflector(const spirv_cross::CompilerMSL& compiler); + struct Options { + std::string shader_name; + std::string header_file_name; + }; + + Reflector(Options options, const spirv_cross::CompilerMSL& compiler); ~Reflector(); @@ -28,6 +33,7 @@ class Reflector { std::shared_ptr GetReflectionCC() const; private: + const Options options_; std::shared_ptr template_arguments_; std::shared_ptr reflection_header_; std::shared_ptr reflection_cc_; diff --git a/impeller/compiler/tools/compiler.gni b/impeller/compiler/tools/compiler.gni index e6d5c65e1ca65..1aeaf371ae008 100644 --- a/impeller/compiler/tools/compiler.gni +++ b/impeller/compiler/tools/compiler.gni @@ -9,8 +9,8 @@ template("impeller_shaders") { assert(defined(invoker.shaders), "Impeller shaders must be specified.") assert(defined(invoker.name), "Name of the shader library must be specified.") - metal_sources_target_name = "metal_sources_$target_name" - compiled_action_foreach(metal_sources_target_name) { + impellerc_target_name = "impellerc_$target_name" + compiled_action_foreach(impellerc_target_name) { tool = "//flutter/impeller/compiler:impellerc" sources = invoker.shaders @@ -21,7 +21,11 @@ template("impeller_shaders") { reflection_header_intermediate = "$target_gen_dir/{{source_file_part}}.h" reflection_cc_intermediate = "$target_gen_dir/{{source_file_part}}.cc" - outputs = [ metal_intermediate ] + outputs = [ + metal_intermediate, + reflection_header_intermediate, + reflection_cc_intermediate, + ] depfile_path = "$target_gen_dir/{{source_file_part}}.d" source_path = "{{source}}" @@ -49,9 +53,31 @@ template("impeller_shaders") { ] } - metal_library(target_name) { + metal_library_target_name = "metal_library_$target_name" + metal_library(metal_library_target_name) { name = invoker.name - sources = get_target_outputs(":$metal_sources_target_name") - deps = [ ":$metal_sources_target_name" ] + sources = filter_include(get_target_outputs(":$impellerc_target_name"), + [ "*.metal" ]) + deps = [ ":$impellerc_target_name" ] + } + + shader_glue_target_name = "glue_$target_name" + source_set(shader_glue_target_name) { + sources = filter_include(get_target_outputs(":$impellerc_target_name"), + [ + "*.h", + "*.cc", + ]) + deps = [ + ":$impellerc_target_name", + "//flutter/impeller/impeller:impeller_shader_glue_lib", + ] + } + + group(target_name) { + deps = [ + ":$metal_library_target_name", + ":$shader_glue_target_name", + ] } } diff --git a/impeller/compiler/utilities.cc b/impeller/compiler/utilities.cc new file mode 100644 index 0000000000000..3ba9ef533dd7e --- /dev/null +++ b/impeller/compiler/utilities.cc @@ -0,0 +1,41 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "flutter/impeller/compiler/utilities.h" + +#include +#include + +namespace impeller { +namespace compiler { + +std::string InferShaderNameFromPath(std::string_view path) { + return std::filesystem::path{path}.stem().string(); +} + +std::string ConvertToCamelCase(std::string_view string) { + if (string.empty()) { + return ""; + } + + std::stringstream stream; + bool next_upper = true; + for (size_t i = 0, count = string.length(); i < count; i++) { + auto ch = string.data()[i]; + if (next_upper) { + next_upper = false; + stream << static_cast(std::toupper(ch)); + continue; + } + if (ch == '_') { + next_upper = true; + continue; + } + stream << ch; + } + return stream.str(); +} + +} // namespace compiler +} // namespace impeller diff --git a/impeller/compiler/utilities.h b/impeller/compiler/utilities.h new file mode 100644 index 0000000000000..eb9d393aed22b --- /dev/null +++ b/impeller/compiler/utilities.h @@ -0,0 +1,20 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#pragma once + +#include +#include + +#include "flutter/fml/macros.h" + +namespace impeller { +namespace compiler { + +std::string InferShaderNameFromPath(std::string_view path); + +std::string ConvertToCamelCase(std::string_view string); + +} // namespace compiler +} // namespace impeller diff --git a/impeller/impeller/BUILD.gn b/impeller/impeller/BUILD.gn index e8b1abe59d292..5eabf09e707fc 100644 --- a/impeller/impeller/BUILD.gn +++ b/impeller/impeller/BUILD.gn @@ -17,6 +17,18 @@ impeller_shaders("shaders") { ] } +config("impeller_shader_glue_lib_config") { + include_dirs = [ "compositor" ] +} + +source_set("impeller_shader_glue_lib") { + public_configs = [ ":impeller_shader_glue_lib_config" ] + + public = [ "compositor/shader_types.h" ] + + sources = [ "compositor/shader_types.cc" ] +} + source_set("impeller") { public_configs = [ ":impeller_config" ] @@ -94,6 +106,7 @@ source_set("impeller") { compositor_sources + geometry_sources + entity_sources + image_sources deps = [ + ":impeller_shader_glue_lib", ":shaders", "//flutter/fml", "//flutter/impeller/third_party/stb", diff --git a/impeller/impeller/compositor/shader_library.h b/impeller/impeller/compositor/shader_library.h index dede6505e29f8..065f68840472b 100644 --- a/impeller/impeller/compositor/shader_library.h +++ b/impeller/impeller/compositor/shader_library.h @@ -10,16 +10,12 @@ #include #include "flutter/fml/macros.h" +#include "flutter/impeller/impeller/compositor/shader_types.h" namespace impeller { class Context; -enum class ShaderStage { - kVertex, - kFragment, -}; - class ShaderFunction { public: ~ShaderFunction(); diff --git a/impeller/impeller/compositor/shader_types.cc b/impeller/impeller/compositor/shader_types.cc new file mode 100644 index 0000000000000..9ded4c67f62bf --- /dev/null +++ b/impeller/impeller/compositor/shader_types.cc @@ -0,0 +1,11 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "flutter/impeller/impeller/compositor/shader_types.h" + +namespace impeller { + +// + +} // namespace impeller diff --git a/impeller/impeller/compositor/shader_types.h b/impeller/impeller/compositor/shader_types.h new file mode 100644 index 0000000000000..f3b9d1bf9f570 --- /dev/null +++ b/impeller/impeller/compositor/shader_types.h @@ -0,0 +1,21 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include +#include + +namespace impeller { + +enum class ShaderStage { + kUnknown, + kVertex, + kFragment, +}; + +struct ShaderStageInput { + const char* name; + std::size_t location; +}; + +} // namespace impeller diff --git a/impeller/impeller/compositor/shaders/box/box.vert b/impeller/impeller/compositor/shaders/box/box.vert index e8839eb27c293..4daa2967f87e0 100644 --- a/impeller/impeller/compositor/shaders/box/box.vert +++ b/impeller/impeller/compositor/shaders/box/box.vert @@ -6,15 +6,15 @@ uniform UniformBufferObject { uniform sampler2D world; -in vec2 inPosition; -in vec3 inPosition2; -in vec4 inAnotherPosition; +in vec2 position; +in vec3 position2; +in vec4 anotherPosition; in float stuff; -out vec4 outStuff; +out vec4 otherStuff; void main() { - gl_Position = ubo.uniforms.projection * ubo.uniforms.view * ubo.uniforms.model * vec4(inPosition2, 1.0) * inAnotherPosition; - outStuff = texture(world, inPosition); + gl_Position = ubo.uniforms.projection * ubo.uniforms.view * ubo.uniforms.model * vec4(position2, 1.0) * anotherPosition; + otherStuff = texture(world, position); } From e27eace8c2fead8a1d1027908ba7404de6b96feb Mon Sep 17 00:00:00 2001 From: Chinmay Garde Date: Wed, 19 May 2021 16:15:32 -0700 Subject: [PATCH 040/433] Modularize build rules. --- impeller/compiler/compiler.cc | 3 +- impeller/compiler/tools/compiler.gni | 2 +- impeller/host/BUILD.gn | 2 +- impeller/impeller/BUILD.gn | 108 ++---------------- impeller/impeller/compositor/BUILD.gn | 31 +++++ impeller/impeller/compositor/shader_library.h | 2 +- .../impeller/compositor/shaders/box/box.frag | 7 -- .../impeller/compositor/shaders/box/box.vert | 20 ---- impeller/impeller/entity/BUILD.gn | 16 +++ impeller/impeller/entity/entity.cc | 2 +- impeller/impeller/entity/entity.h | 10 +- impeller/impeller/geometry/BUILD.gn | 30 +++++ impeller/impeller/image/BUILD.gn | 16 +++ impeller/impeller/image/image.h | 2 +- impeller/impeller/image/image_result.h | 2 +- impeller/impeller/primitives/BUILD.gn | 24 ++++ impeller/impeller/primitives/box.cc | 0 .../box/types.h => primitives/box.frag} | 12 +- impeller/impeller/primitives/box.h | 0 impeller/impeller/primitives/box.vert | 18 +++ impeller/impeller/shader_glue/BUILD.gn | 20 ++++ .../shader_types.cc | 2 +- .../shader_types.h | 0 impeller/tools/impeller.gni | 42 +++++++ 24 files changed, 225 insertions(+), 146 deletions(-) create mode 100644 impeller/impeller/compositor/BUILD.gn delete mode 100644 impeller/impeller/compositor/shaders/box/box.frag delete mode 100644 impeller/impeller/compositor/shaders/box/box.vert create mode 100644 impeller/impeller/entity/BUILD.gn create mode 100644 impeller/impeller/geometry/BUILD.gn create mode 100644 impeller/impeller/image/BUILD.gn create mode 100644 impeller/impeller/primitives/BUILD.gn create mode 100644 impeller/impeller/primitives/box.cc rename impeller/impeller/{compositor/shaders/box/types.h => primitives/box.frag} (69%) create mode 100644 impeller/impeller/primitives/box.h create mode 100644 impeller/impeller/primitives/box.vert create mode 100644 impeller/impeller/shader_glue/BUILD.gn rename impeller/impeller/{compositor => shader_glue}/shader_types.cc (77%) rename impeller/impeller/{compositor => shader_glue}/shader_types.h (100%) create mode 100644 impeller/tools/impeller.gni diff --git a/impeller/compiler/compiler.cc b/impeller/compiler/compiler.cc index 6b67cd4be9fe7..146c09d1b614b 100644 --- a/impeller/compiler/compiler.cc +++ b/impeller/compiler/compiler.cc @@ -203,7 +203,8 @@ Compiler::Compiler(const fml::Mapping& source_mapping, Reflector::Options reflector_options) : options_(source_options) { if (source_mapping.GetMapping() == nullptr) { - COMPILER_ERROR << "Could not read shader source."; + COMPILER_ERROR + << "Could not read shader source or shader source was empty."; return; } diff --git a/impeller/compiler/tools/compiler.gni b/impeller/compiler/tools/compiler.gni index 1aeaf371ae008..43d68089f98a4 100644 --- a/impeller/compiler/tools/compiler.gni +++ b/impeller/compiler/tools/compiler.gni @@ -70,7 +70,7 @@ template("impeller_shaders") { ]) deps = [ ":$impellerc_target_name", - "//flutter/impeller/impeller:impeller_shader_glue_lib", + "//flutter/impeller/impeller/shader_glue", ] } diff --git a/impeller/host/BUILD.gn b/impeller/host/BUILD.gn index 3864069888d9e..7055d10c03e8e 100644 --- a/impeller/host/BUILD.gn +++ b/impeller/host/BUILD.gn @@ -48,6 +48,6 @@ executable("host") { ":impeller_host_fixtures", ":impeller_host_shaders", "//flutter/fml", - "//flutter/impeller/impeller", + "//flutter/impeller/impeller/primitives", ] } diff --git a/impeller/impeller/BUILD.gn b/impeller/impeller/BUILD.gn index 5eabf09e707fc..224a761694440 100644 --- a/impeller/impeller/BUILD.gn +++ b/impeller/impeller/BUILD.gn @@ -5,111 +5,17 @@ import("//flutter/common/config.gni") import("//flutter/impeller/compiler/tools/compiler.gni") -config("impeller_config") { - include_dirs = [ ".." ] +config("impeller_public_config") { + include_dirs = [ "//flutter/impeller" ] } -impeller_shaders("shaders") { - name = "impeller" - shaders = [ - "compositor/shaders/box/box.frag", - "compositor/shaders/box/box.vert", - ] -} - -config("impeller_shader_glue_lib_config") { - include_dirs = [ "compositor" ] -} - -source_set("impeller_shader_glue_lib") { - public_configs = [ ":impeller_shader_glue_lib_config" ] - - public = [ "compositor/shader_types.h" ] - - sources = [ "compositor/shader_types.cc" ] -} - -source_set("impeller") { - public_configs = [ ":impeller_config" ] - - cflags_objc = flutter_cflags_objc_arc - cflags_objcc = flutter_cflags_objcc_arc - - if (is_clang) { - cflags_cc = [ "-Wno-unused-private-field" ] - } - - include_dirs = [ +group("impeller") { + deps = [ + "compositor", "entity", "geometry", "image", - "compositor", - ] - - compositor_sources = [ - "compositor/context.h", - "compositor/context.mm", - "compositor/pipeline_builder.h", - "compositor/pipeline_builder.mm", - "compositor/pipeline_descriptor.h", - "compositor/pipeline_descriptor.mm", - "compositor/pipeline_library.h", - "compositor/pipeline_library.mm", - "compositor/renderer.h", - "compositor/renderer.mm", - "compositor/shader_library.h", - "compositor/shader_library.mm", - "compositor/surface.h", - "compositor/surface.mm", - "compositor/vertex_descriptor.h", - "compositor/vertex_descriptor.mm", - ] - - geometry_sources = [ - "geometry/matrix.cc", - "geometry/matrix.h", - "geometry/path.cc", - "geometry/path.h", - "geometry/path_builder.cc", - "geometry/path_builder.h", - "geometry/path_component.cc", - "geometry/path_component.h", - "geometry/point.cc", - "geometry/point.h", - "geometry/quaternion.cc", - "geometry/quaternion.h", - "geometry/rect.cc", - "geometry/rect.h", - "geometry/shear.cc", - "geometry/shear.h", - "geometry/size.cc", - "geometry/size.h", - "geometry/vector.cc", - "geometry/vector.h", - ] - - entity_sources = [ - "entity/color.cc", - "entity/color.h", - "entity/entity.cc", - "entity/entity.h", - ] - - image_sources = [ - "image/image.cc", - "image/image.h", - "image/image_result.cc", - "image/image_result.h", - ] - - sources = - compositor_sources + geometry_sources + entity_sources + image_sources - - deps = [ - ":impeller_shader_glue_lib", - ":shaders", - "//flutter/fml", - "//flutter/impeller/third_party/stb", - "//flutter/runtime:libdart", # For tracing, seems to be pulled in by FML. + "primitives", + "shader_glue", ] } diff --git a/impeller/impeller/compositor/BUILD.gn b/impeller/impeller/compositor/BUILD.gn new file mode 100644 index 0000000000000..9c946b391f70d --- /dev/null +++ b/impeller/impeller/compositor/BUILD.gn @@ -0,0 +1,31 @@ +# Copyright 2013 The Flutter Authors. All rights reserved. +# Use of this source code is governed by a BSD-style license that can be +# found in the LICENSE file. + +import("//flutter/impeller/tools/impeller.gni") + +impeller_component("compositor") { + sources = [ + "context.h", + "context.mm", + "pipeline_builder.h", + "pipeline_builder.mm", + "pipeline_descriptor.h", + "pipeline_descriptor.mm", + "pipeline_library.h", + "pipeline_library.mm", + "renderer.h", + "renderer.mm", + "shader_library.h", + "shader_library.mm", + "surface.h", + "surface.mm", + "vertex_descriptor.h", + "vertex_descriptor.mm", + ] + + public_deps = [ + "../geometry", + "../shader_glue", + ] +} diff --git a/impeller/impeller/compositor/shader_library.h b/impeller/impeller/compositor/shader_library.h index 065f68840472b..03e592a75fff9 100644 --- a/impeller/impeller/compositor/shader_library.h +++ b/impeller/impeller/compositor/shader_library.h @@ -10,7 +10,7 @@ #include #include "flutter/fml/macros.h" -#include "flutter/impeller/impeller/compositor/shader_types.h" +#include "impeller/shader_glue/shader_types.h" namespace impeller { diff --git a/impeller/impeller/compositor/shaders/box/box.frag b/impeller/impeller/compositor/shaders/box/box.frag deleted file mode 100644 index 333c266ada884..0000000000000 --- a/impeller/impeller/compositor/shaders/box/box.frag +++ /dev/null @@ -1,7 +0,0 @@ -#include "types.h" - -out vec4 outColor; - -void main() { - outColor = vec4(1.0); -} diff --git a/impeller/impeller/compositor/shaders/box/box.vert b/impeller/impeller/compositor/shaders/box/box.vert deleted file mode 100644 index 4daa2967f87e0..0000000000000 --- a/impeller/impeller/compositor/shaders/box/box.vert +++ /dev/null @@ -1,20 +0,0 @@ -#include "types.h" - -uniform UniformBufferObject { - Uniforms uniforms; -} ubo; - -uniform sampler2D world; - -in vec2 position; -in vec3 position2; -in vec4 anotherPosition; -in float stuff; - -out vec4 otherStuff; - -void main() { - gl_Position = ubo.uniforms.projection * ubo.uniforms.view * ubo.uniforms.model * vec4(position2, 1.0) * anotherPosition; - otherStuff = texture(world, position); -} - diff --git a/impeller/impeller/entity/BUILD.gn b/impeller/impeller/entity/BUILD.gn new file mode 100644 index 0000000000000..c5e9c728de0f0 --- /dev/null +++ b/impeller/impeller/entity/BUILD.gn @@ -0,0 +1,16 @@ +# Copyright 2013 The Flutter Authors. All rights reserved. +# Use of this source code is governed by a BSD-style license that can be +# found in the LICENSE file. + +import("//flutter/impeller/tools/impeller.gni") + +impeller_component("entity") { + sources = [ + "color.cc", + "color.h", + "entity.cc", + "entity.h", + ] + + public_deps = [ "../image" ] +} diff --git a/impeller/impeller/entity/entity.cc b/impeller/impeller/entity/entity.cc index 9f26fa047dab3..b837161ffa8e7 100644 --- a/impeller/impeller/entity/entity.cc +++ b/impeller/impeller/entity/entity.cc @@ -2,7 +2,7 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -#include "entity.h" +#include "impeller/entity/entity.h" namespace impeller { diff --git a/impeller/impeller/entity/entity.h b/impeller/impeller/entity/entity.h index fae9a82f94aa4..dc2561458e73c 100644 --- a/impeller/impeller/entity/entity.h +++ b/impeller/impeller/entity/entity.h @@ -4,11 +4,11 @@ #pragma once -#include "color.h" -#include "image.h" -#include "matrix.h" -#include "path.h" -#include "rect.h" +#include "impeller/entity/color.h" +#include "impeller/geometry/matrix.h" +#include "impeller/geometry/path.h" +#include "impeller/geometry/rect.h" +#include "impeller/image/image.h" namespace impeller { diff --git a/impeller/impeller/geometry/BUILD.gn b/impeller/impeller/geometry/BUILD.gn new file mode 100644 index 0000000000000..8bf782d54c476 --- /dev/null +++ b/impeller/impeller/geometry/BUILD.gn @@ -0,0 +1,30 @@ +# Copyright 2013 The Flutter Authors. All rights reserved. +# Use of this source code is governed by a BSD-style license that can be +# found in the LICENSE file. + +import("//flutter/impeller/tools/impeller.gni") + +impeller_component("geometry") { + sources = [ + "matrix.cc", + "matrix.h", + "path.cc", + "path.h", + "path_builder.cc", + "path_builder.h", + "path_component.cc", + "path_component.h", + "point.cc", + "point.h", + "quaternion.cc", + "quaternion.h", + "rect.cc", + "rect.h", + "shear.cc", + "shear.h", + "size.cc", + "size.h", + "vector.cc", + "vector.h", + ] +} diff --git a/impeller/impeller/image/BUILD.gn b/impeller/impeller/image/BUILD.gn new file mode 100644 index 0000000000000..eb0cb3c46f9e1 --- /dev/null +++ b/impeller/impeller/image/BUILD.gn @@ -0,0 +1,16 @@ +# Copyright 2013 The Flutter Authors. All rights reserved. +# Use of this source code is governed by a BSD-style license that can be +# found in the LICENSE file. + +import("//flutter/impeller/tools/impeller.gni") + +impeller_component("image") { + sources = [ + "image.cc", + "image.h", + "image_result.cc", + "image_result.h", + ] + + public_deps = [ "../geometry" ] +} diff --git a/impeller/impeller/image/image.h b/impeller/impeller/image/image.h index aee6c35467c15..4bd0d32d60ffa 100644 --- a/impeller/impeller/image/image.h +++ b/impeller/impeller/image/image.h @@ -7,7 +7,7 @@ #include "flutter/fml/macros.h" #include "flutter/fml/mapping.h" #include "image_result.h" -#include "size.h" +#include "impeller/geometry/size.h" namespace impeller { diff --git a/impeller/impeller/image/image_result.h b/impeller/impeller/image/image_result.h index 45b7386dbd16b..7f5e4b7e449a7 100644 --- a/impeller/impeller/image/image_result.h +++ b/impeller/impeller/image/image_result.h @@ -8,7 +8,7 @@ #include "flutter/fml/macros.h" #include "flutter/fml/mapping.h" -#include "size.h" +#include "impeller/geometry/size.h" namespace impeller { diff --git a/impeller/impeller/primitives/BUILD.gn b/impeller/impeller/primitives/BUILD.gn new file mode 100644 index 0000000000000..4926dcf32b6e3 --- /dev/null +++ b/impeller/impeller/primitives/BUILD.gn @@ -0,0 +1,24 @@ +# Copyright 2013 The Flutter Authors. All rights reserved. +# Use of this source code is governed by a BSD-style license that can be +# found in the LICENSE file. + +import("//flutter/impeller/tools/impeller.gni") + +impeller_shaders("shaders") { + name = "impeller" + shaders = [ + "box.vert", + "box.frag", + ] +} + +impeller_component("primitives") { + sources = [ + "box.cc", + "box.h", + ] + + public_deps = [ "../compositor" ] + + deps = [ ":shaders" ] +} diff --git a/impeller/impeller/primitives/box.cc b/impeller/impeller/primitives/box.cc new file mode 100644 index 0000000000000..e69de29bb2d1d diff --git a/impeller/impeller/compositor/shaders/box/types.h b/impeller/impeller/primitives/box.frag similarity index 69% rename from impeller/impeller/compositor/shaders/box/types.h rename to impeller/impeller/primitives/box.frag index d8775956bacd3..2c45c349d1799 100644 --- a/impeller/impeller/compositor/shaders/box/types.h +++ b/impeller/impeller/primitives/box.frag @@ -2,8 +2,10 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -struct Uniforms { - mat4 model; - mat4 view; - mat4 projection; -}; +in vec3 color; + +out vec3 fragColor; + +void main() { + fragColor = color; +} diff --git a/impeller/impeller/primitives/box.h b/impeller/impeller/primitives/box.h new file mode 100644 index 0000000000000..e69de29bb2d1d diff --git a/impeller/impeller/primitives/box.vert b/impeller/impeller/primitives/box.vert new file mode 100644 index 0000000000000..c5eb2d124dda6 --- /dev/null +++ b/impeller/impeller/primitives/box.vert @@ -0,0 +1,18 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +uniform UniformBuffer{ + mat4 mvp; +} uniforms; + +in vec3 vertexPosition; +in vec3 vertexColor; + +out vec3 color; + +void main() +{ + gl_Position = uniforms.mvp * vec4(vertexPosition, 1.0); + color = vertexColor; +} diff --git a/impeller/impeller/shader_glue/BUILD.gn b/impeller/impeller/shader_glue/BUILD.gn new file mode 100644 index 0000000000000..59019a9fa61ad --- /dev/null +++ b/impeller/impeller/shader_glue/BUILD.gn @@ -0,0 +1,20 @@ +# Copyright 2013 The Flutter Authors. All rights reserved. +# Use of this source code is governed by a BSD-style license that can be +# found in the LICENSE file. + +import("//flutter/impeller/tools/impeller.gni") + +config("shader_glue_public_config") { + include_dirs = [ "." ] +} + +impeller_component("shader_glue") { + public_configs = [ ":shader_glue_public_config" ] + + public = [ "shader_types.h" ] + + sources = [ + "shader_types.cc", + "shader_types.h", + ] +} diff --git a/impeller/impeller/compositor/shader_types.cc b/impeller/impeller/shader_glue/shader_types.cc similarity index 77% rename from impeller/impeller/compositor/shader_types.cc rename to impeller/impeller/shader_glue/shader_types.cc index 9ded4c67f62bf..e616cfecc7f57 100644 --- a/impeller/impeller/compositor/shader_types.cc +++ b/impeller/impeller/shader_glue/shader_types.cc @@ -2,7 +2,7 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -#include "flutter/impeller/impeller/compositor/shader_types.h" +#include "impeller/shader_glue/shader_types.h" namespace impeller { diff --git a/impeller/impeller/compositor/shader_types.h b/impeller/impeller/shader_glue/shader_types.h similarity index 100% rename from impeller/impeller/compositor/shader_types.h rename to impeller/impeller/shader_glue/shader_types.h diff --git a/impeller/tools/impeller.gni b/impeller/tools/impeller.gni new file mode 100644 index 0000000000000..00dc95333ef66 --- /dev/null +++ b/impeller/tools/impeller.gni @@ -0,0 +1,42 @@ +# Copyright 2013 The Flutter Authors. All rights reserved. +# Use of this source code is governed by a BSD-style license that can be +# found in the LICENSE file. + +import("//flutter/common/config.gni") +import("//flutter/impeller/compiler/tools/compiler.gni") + +template("impeller_component") { + source_set(target_name) { + forward_variables_from(invoker, "*") + + if (!defined(invoker.public_configs)) { + public_configs = [] + } + + if (!defined(invoker.cflags_objc)) { + cflags_objc = [] + } + + if (!defined(invoker.cflags_objcc)) { + cflags_objcc = [] + } + + if (!defined(invoker.deps)) { + deps = [] + } + + objc_warning_flags = [ "-Wno-unused-private-field" ] + + cflags_objc += flutter_cflags_objc_arc + objc_warning_flags + cflags_objcc += flutter_cflags_objcc_arc + objc_warning_flags + + public_configs += [ "//flutter/impeller/impeller:impeller_public_config" ] + + deps += [ + "//flutter/fml", + + # For tracing, seems to be pulled in by FML. Must be removed. + "//flutter/runtime:libdart", + ] + } +} From 38e0e9471e250ddd222f01e0d393d4ef8920f888 Mon Sep 17 00:00:00 2001 From: Chinmay Garde Date: Thu, 20 May 2021 14:11:32 -0700 Subject: [PATCH 041/433] Disambiguate stages. --- impeller/compiler/code_gen_template.h | 6 +++--- impeller/compiler/compiler.cc | 1 - impeller/compiler/reflector.cc | 22 +++++++++++++++++--- impeller/compiler/tools/compiler.gni | 13 +++++++++++- impeller/impeller/primitives/BUILD.gn | 4 ++-- impeller/impeller/primitives/box.cc | 14 +++++++++++++ impeller/impeller/primitives/box.h | 9 ++++++++ impeller/impeller/primitives/box.vert | 8 +++++-- impeller/impeller/shader_glue/shader_types.h | 2 ++ 9 files changed, 67 insertions(+), 12 deletions(-) diff --git a/impeller/compiler/code_gen_template.h b/impeller/compiler/code_gen_template.h index 34c218128a3cb..259bdd98a4d67 100644 --- a/impeller/compiler/code_gen_template.h +++ b/impeller/compiler/code_gen_template.h @@ -16,16 +16,16 @@ constexpr std::string_view kReflectionHeaderTemplate = R"~~( namespace impeller { namespace shader { -struct {{camel_case(shader_name)}} { +struct {{camel_case(shader_name)}}{{camel_case(shader_stage)}}Info { static constexpr std::string_view kEntrypointName = "{{entrypoint}}"; - static constexpr ShaderStage kShaderStage = {{shader_stage}}; + static constexpr ShaderStage kShaderStage = {{to_shader_stage(shader_stage)}}; // Stage Inputs {% for stage_input in stage_inputs %} // Stage input {{stage_input.name}} static constexpr ShaderStageInput kInput{{camel_case(stage_input.name)}} = {"{{stage_input.name}}", {{stage_input.location}}}; {% endfor %} -}; // struct {{camel_case(shader_name)}} +}; // struct {{camel_case(shader_name)}}{{camel_case(shader_stage)}}Info } // namespace shader } // namespace impeller diff --git a/impeller/compiler/compiler.cc b/impeller/compiler/compiler.cc index 146c09d1b614b..33dcd59a9af61 100644 --- a/impeller/compiler/compiler.cc +++ b/impeller/compiler/compiler.cc @@ -263,7 +263,6 @@ Compiler::Compiler(const fml::Mapping& source_mapping, source_options.entry_point_name.c_str(), // entry_point_name options // options )); - if (spv_result_->GetCompilationStatus() != shaderc_compilation_status::shaderc_compilation_status_success) { COMPILER_ERROR << "GLSL to SPIRV failed; " diff --git a/impeller/compiler/reflector.cc b/impeller/compiler/reflector.cc index e1d91c862bff3..0aa50c57b00d4 100644 --- a/impeller/compiler/reflector.cc +++ b/impeller/compiler/reflector.cc @@ -150,14 +150,26 @@ static bool ReflectUniformBuffer(Writer& writer, static std::string ExecutionModelToString(spv::ExecutionModel model) { switch (model) { case spv::ExecutionModel::ExecutionModelVertex: - return "ShaderStage::kVertex"; + return "vertex"; case spv::ExecutionModel::ExecutionModelFragment: - return "ShaderStage::kFragment"; + return "fragment"; default: - return "ShaderStage::kUnsupported"; + return "unsupported"; } } +static std::string StringToShaderStage(std::string str) { + if (str == "vertex") { + return "ShaderStage::kVertex"; + } + + if (str == "fragment") { + return "ShaderStage::kFragment"; + } + + return "ShaderStage::kUnknown"; +} + static std::shared_ptr ReflectTemplateArguments( const Reflector::Options& options, const spirv_cross::CompilerMSL& compiler) { @@ -235,6 +247,10 @@ static std::shared_ptr InflateTemplate( return ConvertToCamelCase(args.at(0u)->get()); }); + env.add_callback("to_shader_stage", 1u, [](inja::Arguments& args) { + return StringToShaderStage(args.at(0u)->get()); + }); + auto template_data = inja::json::parse( reinterpret_cast(reflection_args->GetMapping())); diff --git a/impeller/compiler/tools/compiler.gni b/impeller/compiler/tools/compiler.gni index 43d68089f98a4..10ffad9079b93 100644 --- a/impeller/compiler/tools/compiler.gni +++ b/impeller/compiler/tools/compiler.gni @@ -62,7 +62,18 @@ template("impeller_shaders") { } shader_glue_target_name = "glue_$target_name" + shader_glue_config_name = "glue_config_$target_name" + + config(shader_glue_config_name) { + # Contains the generated header headers. + include_dirs = [ target_gen_dir ] + } + source_set(shader_glue_target_name) { + public_configs = [ ":$shader_glue_config_name" ] + + public = + filter_include(get_target_outputs(":$impellerc_target_name"), [ "*.h" ]) sources = filter_include(get_target_outputs(":$impellerc_target_name"), [ "*.h", @@ -75,7 +86,7 @@ template("impeller_shaders") { } group(target_name) { - deps = [ + public_deps = [ ":$metal_library_target_name", ":$shader_glue_target_name", ] diff --git a/impeller/impeller/primitives/BUILD.gn b/impeller/impeller/primitives/BUILD.gn index 4926dcf32b6e3..699b5d167b773 100644 --- a/impeller/impeller/primitives/BUILD.gn +++ b/impeller/impeller/primitives/BUILD.gn @@ -4,7 +4,7 @@ import("//flutter/impeller/tools/impeller.gni") -impeller_shaders("shaders") { +impeller_shaders("impeller_shaders") { name = "impeller" shaders = [ "box.vert", @@ -20,5 +20,5 @@ impeller_component("primitives") { public_deps = [ "../compositor" ] - deps = [ ":shaders" ] + deps = [ ":impeller_shaders" ] } diff --git a/impeller/impeller/primitives/box.cc b/impeller/impeller/primitives/box.cc index e69de29bb2d1d..aa8e1b179ec64 100644 --- a/impeller/impeller/primitives/box.cc +++ b/impeller/impeller/primitives/box.cc @@ -0,0 +1,14 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "impeller/primitives/box.h" + +#include "box.frag.h" +#include "box.vert.h" + +namespace impeller { + +void RenderBox() {} + +} // namespace impeller diff --git a/impeller/impeller/primitives/box.h b/impeller/impeller/primitives/box.h index e69de29bb2d1d..5e558de1d31ed 100644 --- a/impeller/impeller/primitives/box.h +++ b/impeller/impeller/primitives/box.h @@ -0,0 +1,9 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +namespace impeller { + +void RenderBox(); + +} // namespace impeller diff --git a/impeller/impeller/primitives/box.vert b/impeller/impeller/primitives/box.vert index c5eb2d124dda6..6a50f78228f6b 100644 --- a/impeller/impeller/primitives/box.vert +++ b/impeller/impeller/primitives/box.vert @@ -2,10 +2,14 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -uniform UniformBuffer{ +uniform UniformBuffer { mat4 mvp; } uniforms; +uniform UniformBuffer2 { + uniform mat4 longGOP; +} uniforms2; + in vec3 vertexPosition; in vec3 vertexColor; @@ -13,6 +17,6 @@ out vec3 color; void main() { - gl_Position = uniforms.mvp * vec4(vertexPosition, 1.0); + gl_Position = uniforms2.longGOP * uniforms.mvp * vec4(vertexPosition, 1.0); color = vertexColor; } diff --git a/impeller/impeller/shader_glue/shader_types.h b/impeller/impeller/shader_glue/shader_types.h index f3b9d1bf9f570..f3c3adbd59c85 100644 --- a/impeller/impeller/shader_glue/shader_types.h +++ b/impeller/impeller/shader_glue/shader_types.h @@ -2,6 +2,8 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. +#pragma once + #include #include From a9341313e0406d7f2423fd5ebb45fc3138837c9d Mon Sep 17 00:00:00 2001 From: Chinmay Garde Date: Fri, 21 May 2021 10:51:50 -0700 Subject: [PATCH 042/433] Add box shader sample. --- impeller/compiler/code_gen_template.h | 1 + impeller/impeller/compositor/context.mm | 4 +++ .../impeller/compositor/pipeline_builder.h | 17 ++++++---- .../impeller/compositor/pipeline_builder.mm | 31 +++++++++++++++++++ impeller/impeller/compositor/shader_library.h | 3 +- .../impeller/compositor/shader_library.mm | 4 +-- impeller/impeller/primitives/BUILD.gn | 2 +- impeller/impeller/primitives/box.cc | 14 --------- impeller/impeller/primitives/box.h | 4 ++- impeller/impeller/primitives/box.mm | 26 ++++++++++++++++ 10 files changed, 81 insertions(+), 25 deletions(-) delete mode 100644 impeller/impeller/primitives/box.cc create mode 100644 impeller/impeller/primitives/box.mm diff --git a/impeller/compiler/code_gen_template.h b/impeller/compiler/code_gen_template.h index 259bdd98a4d67..ade82b13e8ca5 100644 --- a/impeller/compiler/code_gen_template.h +++ b/impeller/compiler/code_gen_template.h @@ -17,6 +17,7 @@ namespace impeller { namespace shader { struct {{camel_case(shader_name)}}{{camel_case(shader_stage)}}Info { + static constexpr std::string_view kLabel = "{{camel_case(shader_name)}}"; static constexpr std::string_view kEntrypointName = "{{entrypoint}}"; static constexpr ShaderStage kShaderStage = {{to_shader_stage(shader_stage)}}; diff --git a/impeller/impeller/compositor/context.mm b/impeller/impeller/compositor/context.mm index 702e9a4ffa175..cbba8d7c5bd21 100644 --- a/impeller/impeller/compositor/context.mm +++ b/impeller/impeller/compositor/context.mm @@ -64,4 +64,8 @@ return transfer_queue_; } +std::shared_ptr Context::GetShaderLibrary() const { + return shader_library_; +} + } // namespace impeller diff --git a/impeller/impeller/compositor/pipeline_builder.h b/impeller/impeller/compositor/pipeline_builder.h index 0afa12fdb44d6..5db78f14642b6 100644 --- a/impeller/impeller/compositor/pipeline_builder.h +++ b/impeller/impeller/compositor/pipeline_builder.h @@ -4,7 +4,10 @@ #pragma once +#include #include +#include +#include #include "flutter/fml/macros.h" #include "impeller/compositor/pipeline_descriptor.h" @@ -19,19 +22,21 @@ class PipelineBuilder { ~PipelineBuilder(); - PipelineBuilder& SetLabel(); + PipelineBuilder& SetLabel(const std::string_view& label); - PipelineBuilder& SetSampleCountCount(size_t samples); + PipelineBuilder& SetSampleCount(size_t samples); - PipelineBuilder& SetVertexFunction(std::shared_ptr function); - - PipelineBuilder& SetFragmentFunction( - std::shared_ptr function); + PipelineBuilder& AddStageEntrypoint(std::shared_ptr function); PipelineBuilder& SetVertexDescriptor( std::shared_ptr vertex_descriptor); private: + std::string label_; + size_t sample_count_ = 1; + std::unordered_map> entrypoints_; + std::shared_ptr vertex_descriptor_; + FML_DISALLOW_COPY_AND_ASSIGN(PipelineBuilder); }; diff --git a/impeller/impeller/compositor/pipeline_builder.mm b/impeller/impeller/compositor/pipeline_builder.mm index 4f954b6da68ea..cc603842852db 100644 --- a/impeller/impeller/compositor/pipeline_builder.mm +++ b/impeller/impeller/compositor/pipeline_builder.mm @@ -10,4 +10,35 @@ PipelineBuilder::~PipelineBuilder() = default; +PipelineBuilder& PipelineBuilder::SetLabel(const std::string_view& label) { + label_ = {label.data(), label.size()}; + return *this; +} + +PipelineBuilder& PipelineBuilder::SetSampleCount(size_t samples) { + sample_count_ = samples; + return *this; +} + +PipelineBuilder& PipelineBuilder::AddStageEntrypoint( + std::shared_ptr function) { + if (!function) { + return *this; + } + + if (function->GetStage() == ShaderStage::kUnknown) { + return *this; + } + + entrypoints_[function->GetStage()] = std::move(function); + + return *this; +} + +PipelineBuilder& PipelineBuilder::SetVertexDescriptor( + std::shared_ptr vertex_descriptor) { + vertex_descriptor_ = std::move(vertex_descriptor); + return *this; +} + } // namespace impeller diff --git a/impeller/impeller/compositor/shader_library.h b/impeller/impeller/compositor/shader_library.h index 03e592a75fff9..3a7daed01c4b2 100644 --- a/impeller/impeller/compositor/shader_library.h +++ b/impeller/impeller/compositor/shader_library.h @@ -8,6 +8,7 @@ #include #include +#include #include "flutter/fml/macros.h" #include "impeller/shader_glue/shader_types.h" @@ -37,7 +38,7 @@ class ShaderLibrary { public: ~ShaderLibrary(); - std::shared_ptr GetFunction(const std::string& name, + std::shared_ptr GetFunction(const std::string_view& name, ShaderStage stage); private: diff --git a/impeller/impeller/compositor/shader_library.mm b/impeller/impeller/compositor/shader_library.mm index 821ebc50d88fc..70d90d8e7c0d1 100644 --- a/impeller/impeller/compositor/shader_library.mm +++ b/impeller/impeller/compositor/shader_library.mm @@ -20,9 +20,9 @@ ShaderLibrary::~ShaderLibrary() = default; std::shared_ptr ShaderLibrary::GetFunction( - const std::string& name, + const std::string_view& name, ShaderStage stage) { - auto function = [library_ newFunctionWithName:@(name.c_str())]; + auto function = [library_ newFunctionWithName:@(name.data())]; if (!function) { return nullptr; } diff --git a/impeller/impeller/primitives/BUILD.gn b/impeller/impeller/primitives/BUILD.gn index 699b5d167b773..c871d10def2b3 100644 --- a/impeller/impeller/primitives/BUILD.gn +++ b/impeller/impeller/primitives/BUILD.gn @@ -14,8 +14,8 @@ impeller_shaders("impeller_shaders") { impeller_component("primitives") { sources = [ - "box.cc", "box.h", + "box.mm", ] public_deps = [ "../compositor" ] diff --git a/impeller/impeller/primitives/box.cc b/impeller/impeller/primitives/box.cc deleted file mode 100644 index aa8e1b179ec64..0000000000000 --- a/impeller/impeller/primitives/box.cc +++ /dev/null @@ -1,14 +0,0 @@ -// Copyright 2013 The Flutter Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#include "impeller/primitives/box.h" - -#include "box.frag.h" -#include "box.vert.h" - -namespace impeller { - -void RenderBox() {} - -} // namespace impeller diff --git a/impeller/impeller/primitives/box.h b/impeller/impeller/primitives/box.h index 5e558de1d31ed..08a079f08bcf7 100644 --- a/impeller/impeller/primitives/box.h +++ b/impeller/impeller/primitives/box.h @@ -2,8 +2,10 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. +#include "impeller/compositor/context.h" + namespace impeller { -void RenderBox(); +void RenderBox(std::shared_ptr context); } // namespace impeller diff --git a/impeller/impeller/primitives/box.mm b/impeller/impeller/primitives/box.mm new file mode 100644 index 0000000000000..8e5895cf937aa --- /dev/null +++ b/impeller/impeller/primitives/box.mm @@ -0,0 +1,26 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "impeller/primitives/box.h" + +#include "box.frag.h" +#include "box.vert.h" +#include "impeller/compositor/pipeline_builder.h" + +namespace impeller { + +void RenderBox(std::shared_ptr context) { + PipelineBuilder builder; + + auto fragment_function = context->GetShaderLibrary()->GetFunction( + shader::BoxFragmentInfo::kEntrypointName, ShaderStage::kFragment); + auto vertex_function = context->GetShaderLibrary()->GetFunction( + shader::BoxVertexInfo::kEntrypointName, ShaderStage::kVertex); + + builder.AddStageEntrypoint(vertex_function); + builder.AddStageEntrypoint(fragment_function); + builder.SetLabel(shader::BoxVertexInfo::kLabel); +} + +} // namespace impeller From 24484bcc0b49819d2b5f9c20b63f82f1879ab124 Mon Sep 17 00:00:00 2001 From: Chinmay Garde Date: Sat, 22 May 2021 15:18:49 -0700 Subject: [PATCH 043/433] Wire up pipeline library resolution. --- impeller/impeller/compositor/BUILD.gn | 4 +- impeller/impeller/compositor/context.h | 4 ++ impeller/impeller/compositor/pipeline.h | 35 ++++++++++ impeller/impeller/compositor/pipeline.mm | 17 +++++ .../impeller/compositor/pipeline_builder.h | 43 ------------ .../impeller/compositor/pipeline_builder.mm | 44 ------------- .../impeller/compositor/pipeline_descriptor.h | 36 ++++++++++ .../compositor/pipeline_descriptor.mm | 65 +++++++++++++++++++ .../impeller/compositor/pipeline_library.h | 19 +++++- .../impeller/compositor/pipeline_library.mm | 45 +++++++++++++ impeller/impeller/compositor/shader_library.h | 35 +++++++++- .../impeller/compositor/shader_library.mm | 18 ++++- impeller/impeller/primitives/box.mm | 8 +-- 13 files changed, 275 insertions(+), 98 deletions(-) create mode 100644 impeller/impeller/compositor/pipeline.h create mode 100644 impeller/impeller/compositor/pipeline.mm delete mode 100644 impeller/impeller/compositor/pipeline_builder.h delete mode 100644 impeller/impeller/compositor/pipeline_builder.mm diff --git a/impeller/impeller/compositor/BUILD.gn b/impeller/impeller/compositor/BUILD.gn index 9c946b391f70d..1765dfbe643ce 100644 --- a/impeller/impeller/compositor/BUILD.gn +++ b/impeller/impeller/compositor/BUILD.gn @@ -8,8 +8,8 @@ impeller_component("compositor") { sources = [ "context.h", "context.mm", - "pipeline_builder.h", - "pipeline_builder.mm", + "pipeline.h", + "pipeline.mm", "pipeline_descriptor.h", "pipeline_descriptor.mm", "pipeline_library.h", diff --git a/impeller/impeller/compositor/context.h b/impeller/impeller/compositor/context.h index dc007ba4d7ecb..6dee81f7b4836 100644 --- a/impeller/impeller/compositor/context.h +++ b/impeller/impeller/compositor/context.h @@ -9,6 +9,7 @@ #include #include "flutter/fml/macros.h" +#include "impeller/compositor/pipeline_library.h" namespace impeller { @@ -28,11 +29,14 @@ class Context { std::shared_ptr GetShaderLibrary() const; + std::shared_ptr GetPipelineLibrary() const; + private: id device_ = nullptr; id render_queue_ = nullptr; id transfer_queue_ = nullptr; std::shared_ptr shader_library_; + std::shared_ptr pipeline_library_; bool is_valid_ = false; FML_DISALLOW_COPY_AND_ASSIGN(Context); diff --git a/impeller/impeller/compositor/pipeline.h b/impeller/impeller/compositor/pipeline.h new file mode 100644 index 0000000000000..e3f75d7b4e801 --- /dev/null +++ b/impeller/impeller/compositor/pipeline.h @@ -0,0 +1,35 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#pragma once + +#include "flutter/fml/macros.h" + +#include + +namespace impeller { + +class PipelineLibrary; + +class Pipeline { + public: + enum class Type { + kUnknown, + kRender, + }; + + ~Pipeline(); + + private: + friend class PipelineLibrary; + + Type type_ = Type::kUnknown; + id state_; + + Pipeline(id state); + + FML_DISALLOW_COPY_AND_ASSIGN(Pipeline); +}; + +} // namespace impeller diff --git a/impeller/impeller/compositor/pipeline.mm b/impeller/impeller/compositor/pipeline.mm new file mode 100644 index 0000000000000..a682a77d0c0f7 --- /dev/null +++ b/impeller/impeller/compositor/pipeline.mm @@ -0,0 +1,17 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "flutter/impeller/impeller/compositor/pipeline.h" + +namespace impeller { + +Pipeline::Pipeline(id state) : state_(state) { + if (state_ != nil) { + type_ = Type::kRender; + } +} + +Pipeline::~Pipeline() = default; + +} // namespace impeller diff --git a/impeller/impeller/compositor/pipeline_builder.h b/impeller/impeller/compositor/pipeline_builder.h deleted file mode 100644 index 5db78f14642b6..0000000000000 --- a/impeller/impeller/compositor/pipeline_builder.h +++ /dev/null @@ -1,43 +0,0 @@ -// Copyright 2013 The Flutter Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#pragma once - -#include -#include -#include -#include - -#include "flutter/fml/macros.h" -#include "impeller/compositor/pipeline_descriptor.h" -#include "impeller/compositor/shader_library.h" -#include "impeller/compositor/vertex_descriptor.h" - -namespace impeller { - -class PipelineBuilder { - public: - PipelineBuilder(); - - ~PipelineBuilder(); - - PipelineBuilder& SetLabel(const std::string_view& label); - - PipelineBuilder& SetSampleCount(size_t samples); - - PipelineBuilder& AddStageEntrypoint(std::shared_ptr function); - - PipelineBuilder& SetVertexDescriptor( - std::shared_ptr vertex_descriptor); - - private: - std::string label_; - size_t sample_count_ = 1; - std::unordered_map> entrypoints_; - std::shared_ptr vertex_descriptor_; - - FML_DISALLOW_COPY_AND_ASSIGN(PipelineBuilder); -}; - -} // namespace impeller diff --git a/impeller/impeller/compositor/pipeline_builder.mm b/impeller/impeller/compositor/pipeline_builder.mm deleted file mode 100644 index cc603842852db..0000000000000 --- a/impeller/impeller/compositor/pipeline_builder.mm +++ /dev/null @@ -1,44 +0,0 @@ -// Copyright 2013 The Flutter Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#include "impeller/compositor/pipeline_builder.h" - -namespace impeller { - -PipelineBuilder::PipelineBuilder() = default; - -PipelineBuilder::~PipelineBuilder() = default; - -PipelineBuilder& PipelineBuilder::SetLabel(const std::string_view& label) { - label_ = {label.data(), label.size()}; - return *this; -} - -PipelineBuilder& PipelineBuilder::SetSampleCount(size_t samples) { - sample_count_ = samples; - return *this; -} - -PipelineBuilder& PipelineBuilder::AddStageEntrypoint( - std::shared_ptr function) { - if (!function) { - return *this; - } - - if (function->GetStage() == ShaderStage::kUnknown) { - return *this; - } - - entrypoints_[function->GetStage()] = std::move(function); - - return *this; -} - -PipelineBuilder& PipelineBuilder::SetVertexDescriptor( - std::shared_ptr vertex_descriptor) { - vertex_descriptor_ = std::move(vertex_descriptor); - return *this; -} - -} // namespace impeller diff --git a/impeller/impeller/compositor/pipeline_descriptor.h b/impeller/impeller/compositor/pipeline_descriptor.h index 093536dae569f..0b5869e756fd2 100644 --- a/impeller/impeller/compositor/pipeline_descriptor.h +++ b/impeller/impeller/compositor/pipeline_descriptor.h @@ -4,17 +4,53 @@ #pragma once +#include + +#include +#include +#include +#include +#include +#include + #include "flutter/fml/macros.h" +#include "impeller/shader_glue/shader_types.h" namespace impeller { +class ShaderFunction; +class VertexDescriptor; + class PipelineDescriptor { public: + struct HashEqual { + std::size_t operator()(const PipelineDescriptor& des) const; + bool operator()(const PipelineDescriptor& d1, + const PipelineDescriptor& d2) const; + }; + PipelineDescriptor(); ~PipelineDescriptor(); + PipelineDescriptor& SetLabel(const std::string_view& label); + + PipelineDescriptor& SetSampleCount(size_t samples); + + PipelineDescriptor& AddStageEntrypoint( + std::shared_ptr function); + + PipelineDescriptor& SetVertexDescriptor( + std::shared_ptr vertex_descriptor); + + MTLRenderPipelineDescriptor* GetMTLRenderPipelineDescriptor() const; + private: + std::string label_; + size_t sample_count_ = 1; + std::map> entrypoints_; + std::shared_ptr vertex_descriptor_; + FML_DISALLOW_COPY_AND_ASSIGN(PipelineDescriptor); }; diff --git a/impeller/impeller/compositor/pipeline_descriptor.mm b/impeller/impeller/compositor/pipeline_descriptor.mm index ab8c8281e565e..a9d199763cb8d 100644 --- a/impeller/impeller/compositor/pipeline_descriptor.mm +++ b/impeller/impeller/compositor/pipeline_descriptor.mm @@ -4,10 +4,75 @@ #include "impeller/compositor/pipeline_descriptor.h" +#include "flutter/fml/hash_combine.h" +#include "impeller/compositor/shader_library.h" + namespace impeller { PipelineDescriptor::PipelineDescriptor() = default; PipelineDescriptor::~PipelineDescriptor() = default; +std::size_t PipelineDescriptor::HashEqual::operator()( + const PipelineDescriptor& des) const { + auto seed = fml::HashCombine(); + fml::HashCombineSeed(seed, des.label_); + for (const auto& entry : des.entrypoints_) { + fml::HashCombineSeed(seed, entry) + } +} + +bool PipelineDescriptor::HashEqual::operator()( + const PipelineDescriptor& d1, + const PipelineDescriptor& d2) const {} + +PipelineDescriptor& PipelineDescriptor::SetLabel( + const std::string_view& label) { + label_ = {label.data(), label.size()}; + return *this; +} + +PipelineDescriptor& PipelineDescriptor::SetSampleCount(size_t samples) { + sample_count_ = samples; + return *this; +} + +PipelineDescriptor& PipelineDescriptor::AddStageEntrypoint( + std::shared_ptr function) { + if (!function) { + return *this; + } + + if (function->GetStage() == ShaderStage::kUnknown) { + return *this; + } + + entrypoints_[function->GetStage()] = std::move(function); + + return *this; +} + +PipelineDescriptor& PipelineDescriptor::SetVertexDescriptor( + std::shared_ptr vertex_descriptor) { + vertex_descriptor_ = std::move(vertex_descriptor); + return *this; +} + +MTLRenderPipelineDescriptor* +PipelineDescriptor::GetMTLRenderPipelineDescriptor() const { + auto descriptor = [[MTLRenderPipelineDescriptor alloc] init]; + descriptor.label = @(label_.c_str()); + descriptor.sampleCount = sample_count_; + + for (const auto& entry : entrypoints_) { + if (entry.first == ShaderStage::kVertex) { + descriptor.vertexFunction = entry.second->GetMTLFunction(); + } + if (entry.first == ShaderStage::kFragment) { + descriptor.fragmentFunction = entry.second->GetMTLFunction(); + } + } + return descriptor; +} + } // namespace impeller diff --git a/impeller/impeller/compositor/pipeline_library.h b/impeller/impeller/compositor/pipeline_library.h index a20ff2a08cd3c..1740df363ef67 100644 --- a/impeller/impeller/compositor/pipeline_library.h +++ b/impeller/impeller/compositor/pipeline_library.h @@ -4,17 +4,34 @@ #pragma once +#include + +#include +#include + #include "flutter/fml/macros.h" +#include "impeller/compositor/pipeline.h" +#include "impeller/compositor/pipeline_descriptor.h" namespace impeller { class PipelineLibrary { public: - PipelineLibrary(); + PipelineLibrary(id device); ~PipelineLibrary(); + std::future> GetRenderPipeline( + PipelineDescriptor descriptor); + private: + using Pipelines = std::unordered_map, + PipelineDescriptor::HashEqual, + PipelineDescriptor::HashEqual>; + id device_; + Pipelines pipelines_; + FML_DISALLOW_COPY_AND_ASSIGN(PipelineLibrary); }; diff --git a/impeller/impeller/compositor/pipeline_library.mm b/impeller/impeller/compositor/pipeline_library.mm index e69de29bb2d1d..26abd1b303508 100644 --- a/impeller/impeller/compositor/pipeline_library.mm +++ b/impeller/impeller/compositor/pipeline_library.mm @@ -0,0 +1,45 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "impeller/compositor/pipeline_library.h" + +#include + +#include "flutter/fml/logging.h" + +namespace impeller { + +PipelineLibrary::PipelineLibrary(id device) : device_(device) {} + +PipelineLibrary::~PipelineLibrary() = default; + +std::future> PipelineLibrary::GetRenderPipeline( + PipelineDescriptor descriptor) { + auto promise = std::make_shared>>(); + auto future = promise->get_future(); + if (auto found = pipelines_.find(descriptor); found != pipelines_.end()) { + promise->set_value(nullptr); + return future; + } + + auto completion_handler = + ^(id _Nullable render_pipeline_state, + NSError* _Nullable error) { + if (error != nil) { + FML_LOG(ERROR) << "Could not create render pipeline: " + << error.localizedDescription.UTF8String; + promise->set_value(nullptr); + } else { + promise->set_value( + std::shared_ptr(new Pipeline(render_pipeline_state))); + } + }; + [device_ + newRenderPipelineStateWithDescriptor:descriptor + .GetMTLRenderPipelineDescriptor() + completionHandler:completion_handler]; + return future; +} + +} // namespace impeller diff --git a/impeller/impeller/compositor/shader_library.h b/impeller/impeller/compositor/shader_library.h index 3a7daed01c4b2..76c280f5d9d64 100644 --- a/impeller/impeller/compositor/shader_library.h +++ b/impeller/impeller/compositor/shader_library.h @@ -9,7 +9,9 @@ #include #include #include +#include +#include "flutter/fml/hash_combine.h" #include "flutter/fml/macros.h" #include "impeller/shader_glue/shader_types.h" @@ -23,6 +25,8 @@ class ShaderFunction { ShaderStage GetStage() const; + id GetMTLFunction() const; + private: friend class ShaderLibrary; @@ -38,13 +42,40 @@ class ShaderLibrary { public: ~ShaderLibrary(); - std::shared_ptr GetFunction(const std::string_view& name, - ShaderStage stage); + std::shared_ptr GetFunction( + const std::string_view& name, + ShaderStage stage); private: friend class Context; + struct ShaderKey { + std::string name; + ShaderStage stage = ShaderStage::kUnknown; + + ShaderKey(const std::string_view& p_name, ShaderStage p_stage) + : name({p_name.data(), p_name.size()}), stage(p_stage) {} + + struct Hash { + size_t operator()(const ShaderKey& key) const { + return fml::HashCombine(key.name, key.stage); + } + }; + + struct Equal { + constexpr bool operator()(const ShaderKey& k1, + const ShaderKey& k2) const { + return k1.stage == k2.stage && k1.name == k2.name; + } + }; + }; + id library_ = nullptr; + using Functions = std::unordered_map, + ShaderKey::Hash, + ShaderKey::Equal>; + Functions functions_; ShaderLibrary(id library); diff --git a/impeller/impeller/compositor/shader_library.mm b/impeller/impeller/compositor/shader_library.mm index 70d90d8e7c0d1..20b6f881ba3dd 100644 --- a/impeller/impeller/compositor/shader_library.mm +++ b/impeller/impeller/compositor/shader_library.mm @@ -11,6 +11,10 @@ ShaderFunction::~ShaderFunction() = default; +id ShaderFunction::GetMTLFunction() const { + return function_; +} + ShaderStage ShaderFunction::GetStage() const { return stage_; } @@ -19,14 +23,24 @@ ShaderLibrary::~ShaderLibrary() = default; -std::shared_ptr ShaderLibrary::GetFunction( +std::shared_ptr ShaderLibrary::GetFunction( const std::string_view& name, ShaderStage stage) { + ShaderKey key(name, stage); + + if (auto found = functions_.find(key); found != functions_.end()) { + return found->second; + } + auto function = [library_ newFunctionWithName:@(name.data())]; if (!function) { return nullptr; } - return std::shared_ptr(new ShaderFunction(function, stage)); + + auto func = + std::shared_ptr(new ShaderFunction(function, stage)); + functions_[key] = func; + return func; } } // namespace impeller diff --git a/impeller/impeller/primitives/box.mm b/impeller/impeller/primitives/box.mm index 8e5895cf937aa..e3e3fa9d7b067 100644 --- a/impeller/impeller/primitives/box.mm +++ b/impeller/impeller/primitives/box.mm @@ -6,21 +6,21 @@ #include "box.frag.h" #include "box.vert.h" -#include "impeller/compositor/pipeline_builder.h" +#include "impeller/compositor/pipeline_descriptor.h" +#include "impeller/compositor/shader_library.h" namespace impeller { void RenderBox(std::shared_ptr context) { - PipelineBuilder builder; - auto fragment_function = context->GetShaderLibrary()->GetFunction( shader::BoxFragmentInfo::kEntrypointName, ShaderStage::kFragment); auto vertex_function = context->GetShaderLibrary()->GetFunction( shader::BoxVertexInfo::kEntrypointName, ShaderStage::kVertex); + PipelineDescriptor builder; + builder.SetLabel(shader::BoxVertexInfo::kLabel); builder.AddStageEntrypoint(vertex_function); builder.AddStageEntrypoint(fragment_function); - builder.SetLabel(shader::BoxVertexInfo::kLabel); } } // namespace impeller From 88e7ff9097d702c9d4397800666f2ecc9afa9889 Mon Sep 17 00:00:00 2001 From: Chinmay Garde Date: Sat, 22 May 2021 17:56:37 -0700 Subject: [PATCH 044/433] Pipeline descriptor comparators. --- impeller/impeller/compositor/BUILD.gn | 2 + impeller/impeller/compositor/comparable.cc | 11 +++ impeller/impeller/compositor/comparable.h | 85 +++++++++++++++++++ .../impeller/compositor/pipeline_descriptor.h | 17 ++-- .../compositor/pipeline_descriptor.mm | 29 +++++-- .../impeller/compositor/pipeline_library.h | 4 +- .../impeller/compositor/pipeline_library.mm | 4 + impeller/impeller/compositor/shader_library.h | 11 ++- .../impeller/compositor/vertex_descriptor.h | 11 ++- 9 files changed, 150 insertions(+), 24 deletions(-) create mode 100644 impeller/impeller/compositor/comparable.cc create mode 100644 impeller/impeller/compositor/comparable.h diff --git a/impeller/impeller/compositor/BUILD.gn b/impeller/impeller/compositor/BUILD.gn index 1765dfbe643ce..35aa61cd0b23f 100644 --- a/impeller/impeller/compositor/BUILD.gn +++ b/impeller/impeller/compositor/BUILD.gn @@ -6,6 +6,8 @@ import("//flutter/impeller/tools/impeller.gni") impeller_component("compositor") { sources = [ + "comparable.cc", + "comparable.h", "context.h", "context.mm", "pipeline.h", diff --git a/impeller/impeller/compositor/comparable.cc b/impeller/impeller/compositor/comparable.cc new file mode 100644 index 0000000000000..86c8e18740cc2 --- /dev/null +++ b/impeller/impeller/compositor/comparable.cc @@ -0,0 +1,11 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "impeller/compositor/comparable.h" + +namespace impeller { + +// + +} // namespace impeller diff --git a/impeller/impeller/compositor/comparable.h b/impeller/impeller/compositor/comparable.h new file mode 100644 index 0000000000000..b546153aaff3d --- /dev/null +++ b/impeller/impeller/compositor/comparable.h @@ -0,0 +1,85 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#pragma once + +#include +#include +#include +#include +#include + +#include "flutter/fml/hash_combine.h" +#include "flutter/fml/macros.h" + +namespace impeller { + +class ComparableBase {}; + +template +class Comparable : ComparableBase { + public: + virtual std::size_t GetHash() const = 0; + + virtual bool IsEqual(const Type& other) const = 0; +}; + +template < + class ComparableType, + class = std::enable_if_t>> +struct ComparableHash { + std::size_t operator()(const ComparableType& object) const { + return object.GetHash(); + } +}; + +template < + class ComparableType, + class = std::enable_if_t>> +struct ComparableEqual { + bool operator()(const ComparableType& lhs, const ComparableType& rhs) const { + return lhs.IsEqual(rhs); + } +}; + +template < + class ComparableType, + class = std::enable_if_t>> +bool DeepComparePointer(const std::shared_ptr& lhs, + const std::shared_ptr& rhs) { + if (lhs == rhs) { + return true; + } + + if (lhs && rhs) { + return lhs->IsEqual(*rhs); + } + + return false; +} + +template < + class Key, + class ComparableType, + class = std::enable_if_t>> +bool DeepCompareMap(const std::map>& lhs, + const std::map>& rhs) { + if (lhs.size() != rhs.size()) { + return false; + } + + for (auto i = lhs.begin(), j = rhs.begin(); i != lhs.end(); i++, j++) { + if (i->first != j->first) { + return false; + } + + if (!DeepComparePointer(i->second, j->second)) { + return false; + } + } + + return true; +} + +} // namespace impeller diff --git a/impeller/impeller/compositor/pipeline_descriptor.h b/impeller/impeller/compositor/pipeline_descriptor.h index 0b5869e756fd2..0370aee54e7fd 100644 --- a/impeller/impeller/compositor/pipeline_descriptor.h +++ b/impeller/impeller/compositor/pipeline_descriptor.h @@ -14,6 +14,7 @@ #include #include "flutter/fml/macros.h" +#include "impeller/compositor/comparable.h" #include "impeller/shader_glue/shader_types.h" namespace impeller { @@ -21,14 +22,8 @@ namespace impeller { class ShaderFunction; class VertexDescriptor; -class PipelineDescriptor { +class PipelineDescriptor : public Comparable { public: - struct HashEqual { - std::size_t operator()(const PipelineDescriptor& des) const; - bool operator()(const PipelineDescriptor& d1, - const PipelineDescriptor& d2) const; - }; - PipelineDescriptor(); ~PipelineDescriptor(); @@ -45,13 +40,17 @@ class PipelineDescriptor { MTLRenderPipelineDescriptor* GetMTLRenderPipelineDescriptor() const; + // Comparable + std::size_t GetHash() const override; + + // Comparable + bool IsEqual(const PipelineDescriptor& other) const override; + private: std::string label_; size_t sample_count_ = 1; std::map> entrypoints_; std::shared_ptr vertex_descriptor_; - - FML_DISALLOW_COPY_AND_ASSIGN(PipelineDescriptor); }; } // namespace impeller diff --git a/impeller/impeller/compositor/pipeline_descriptor.mm b/impeller/impeller/compositor/pipeline_descriptor.mm index a9d199763cb8d..bcfb38c67f609 100644 --- a/impeller/impeller/compositor/pipeline_descriptor.mm +++ b/impeller/impeller/compositor/pipeline_descriptor.mm @@ -4,8 +4,8 @@ #include "impeller/compositor/pipeline_descriptor.h" -#include "flutter/fml/hash_combine.h" #include "impeller/compositor/shader_library.h" +#include "impeller/compositor/vertex_descriptor.h" namespace impeller { @@ -13,18 +13,29 @@ PipelineDescriptor::~PipelineDescriptor() = default; -std::size_t PipelineDescriptor::HashEqual::operator()( - const PipelineDescriptor& des) const { +// Comparable +std::size_t PipelineDescriptor::GetHash() const { auto seed = fml::HashCombine(); - fml::HashCombineSeed(seed, des.label_); - for (const auto& entry : des.entrypoints_) { - fml::HashCombineSeed(seed, entry) + fml::HashCombineSeed(seed, label_); + fml::HashCombineSeed(seed, sample_count_); + for (const auto& entry : entrypoints_) { + fml::HashCombineSeed(seed, entry.first); + if (auto second = entry.second) { + fml::HashCombineSeed(seed, second->GetHash()); + } } + if (vertex_descriptor_) { + fml::HashCombineSeed(seed, vertex_descriptor_->GetHash()); + } + return seed; } -bool PipelineDescriptor::HashEqual::operator()( - const PipelineDescriptor& d1, - const PipelineDescriptor& d2) const {} +// Comparable +bool PipelineDescriptor::IsEqual(const PipelineDescriptor& other) const { + return label_ == other.label_ && sample_count_ == other.sample_count_ && + DeepCompareMap(entrypoints_, other.entrypoints_) && + DeepComparePointer(vertex_descriptor_, other.vertex_descriptor_); +} PipelineDescriptor& PipelineDescriptor::SetLabel( const std::string_view& label) { diff --git a/impeller/impeller/compositor/pipeline_library.h b/impeller/impeller/compositor/pipeline_library.h index 1740df363ef67..aa0ae7b5947d0 100644 --- a/impeller/impeller/compositor/pipeline_library.h +++ b/impeller/impeller/compositor/pipeline_library.h @@ -27,8 +27,8 @@ class PipelineLibrary { private: using Pipelines = std::unordered_map, - PipelineDescriptor::HashEqual, - PipelineDescriptor::HashEqual>; + ComparableHash, + ComparableEqual>; id device_; Pipelines pipelines_; diff --git a/impeller/impeller/compositor/pipeline_library.mm b/impeller/impeller/compositor/pipeline_library.mm index 26abd1b303508..c70fcdcef26de 100644 --- a/impeller/impeller/compositor/pipeline_library.mm +++ b/impeller/impeller/compositor/pipeline_library.mm @@ -23,6 +23,10 @@ return future; } + // WIP Pipeline is not saved. + + pipelines_[descriptor] = nullptr; + auto completion_handler = ^(id _Nullable render_pipeline_state, NSError* _Nullable error) { diff --git a/impeller/impeller/compositor/shader_library.h b/impeller/impeller/compositor/shader_library.h index 76c280f5d9d64..1a96ebad00212 100644 --- a/impeller/impeller/compositor/shader_library.h +++ b/impeller/impeller/compositor/shader_library.h @@ -13,20 +13,27 @@ #include "flutter/fml/hash_combine.h" #include "flutter/fml/macros.h" +#include "impeller/compositor/comparable.h" #include "impeller/shader_glue/shader_types.h" namespace impeller { class Context; -class ShaderFunction { +class ShaderFunction : public Comparable { public: - ~ShaderFunction(); + virtual ~ShaderFunction(); ShaderStage GetStage() const; id GetMTLFunction() const; + // Comparable + std::size_t GetHash() const override; + + // Comparable + bool IsEqual(const ShaderFunction& other) const override; + private: friend class ShaderLibrary; diff --git a/impeller/impeller/compositor/vertex_descriptor.h b/impeller/impeller/compositor/vertex_descriptor.h index bda80d5a19eda..641886e4f4b67 100644 --- a/impeller/impeller/compositor/vertex_descriptor.h +++ b/impeller/impeller/compositor/vertex_descriptor.h @@ -5,14 +5,21 @@ #pragma once #include "flutter/fml/macros.h" +#include "impeller/compositor/comparable.h" namespace impeller { -class VertexDescriptor { +class VertexDescriptor : public Comparable { public: VertexDescriptor(); - ~VertexDescriptor(); + virtual ~VertexDescriptor(); + + // Comparable + std::size_t GetHash() const override; + + // Comparable + bool IsEqual(const VertexDescriptor& other) const override; private: FML_DISALLOW_COPY_AND_ASSIGN(VertexDescriptor); From dbfe261b8d7e9fa280cea31c794fd87c193084eb Mon Sep 17 00:00:00 2001 From: Chinmay Garde Date: Sat, 22 May 2021 18:01:55 -0700 Subject: [PATCH 045/433] Move shader function to its own TU --- impeller/impeller/compositor/BUILD.gn | 2 + .../impeller/compositor/shader_function.h | 41 +++++++++++++++++++ .../impeller/compositor/shader_function.mm | 22 ++++++++++ impeller/impeller/compositor/shader_library.h | 29 +------------ .../impeller/compositor/shader_library.mm | 13 ------ 5 files changed, 66 insertions(+), 41 deletions(-) create mode 100644 impeller/impeller/compositor/shader_function.h create mode 100644 impeller/impeller/compositor/shader_function.mm diff --git a/impeller/impeller/compositor/BUILD.gn b/impeller/impeller/compositor/BUILD.gn index 35aa61cd0b23f..a23f1db99e4db 100644 --- a/impeller/impeller/compositor/BUILD.gn +++ b/impeller/impeller/compositor/BUILD.gn @@ -18,6 +18,8 @@ impeller_component("compositor") { "pipeline_library.mm", "renderer.h", "renderer.mm", + "shader_function.h", + "shader_function.mm", "shader_library.h", "shader_library.mm", "surface.h", diff --git a/impeller/impeller/compositor/shader_function.h b/impeller/impeller/compositor/shader_function.h new file mode 100644 index 0000000000000..2adaf90b908b5 --- /dev/null +++ b/impeller/impeller/compositor/shader_function.h @@ -0,0 +1,41 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#pragma once + +#include + +#include "flutter/fml/hash_combine.h" +#include "flutter/fml/macros.h" +#include "impeller/compositor/comparable.h" +#include "impeller/shader_glue/shader_types.h" + +namespace impeller { + +class ShaderFunction : public Comparable { + public: + virtual ~ShaderFunction(); + + ShaderStage GetStage() const; + + id GetMTLFunction() const; + + // Comparable + std::size_t GetHash() const override; + + // Comparable + bool IsEqual(const ShaderFunction& other) const override; + + private: + friend class ShaderLibrary; + + id function_ = nullptr; + ShaderStage stage_; + + ShaderFunction(id function, ShaderStage stage); + + FML_DISALLOW_COPY_AND_ASSIGN(ShaderFunction); +}; + +} // namespace impeller diff --git a/impeller/impeller/compositor/shader_function.mm b/impeller/impeller/compositor/shader_function.mm new file mode 100644 index 0000000000000..1a2653aaeb6f4 --- /dev/null +++ b/impeller/impeller/compositor/shader_function.mm @@ -0,0 +1,22 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "impeller/compositor/shader_function.h" + +namespace impeller { + +ShaderFunction::ShaderFunction(id function, ShaderStage stage) + : function_(function), stage_(stage) {} + +ShaderFunction::~ShaderFunction() = default; + +id ShaderFunction::GetMTLFunction() const { + return function_; +} + +ShaderStage ShaderFunction::GetStage() const { + return stage_; +} + +} // namespace impeller diff --git a/impeller/impeller/compositor/shader_library.h b/impeller/impeller/compositor/shader_library.h index 1a96ebad00212..06595e786d6d9 100644 --- a/impeller/impeller/compositor/shader_library.h +++ b/impeller/impeller/compositor/shader_library.h @@ -11,40 +11,13 @@ #include #include -#include "flutter/fml/hash_combine.h" #include "flutter/fml/macros.h" -#include "impeller/compositor/comparable.h" -#include "impeller/shader_glue/shader_types.h" +#include "impeller/compositor/shader_function.h" namespace impeller { class Context; -class ShaderFunction : public Comparable { - public: - virtual ~ShaderFunction(); - - ShaderStage GetStage() const; - - id GetMTLFunction() const; - - // Comparable - std::size_t GetHash() const override; - - // Comparable - bool IsEqual(const ShaderFunction& other) const override; - - private: - friend class ShaderLibrary; - - id function_ = nullptr; - ShaderStage stage_; - - ShaderFunction(id function, ShaderStage stage); - - FML_DISALLOW_COPY_AND_ASSIGN(ShaderFunction); -}; - class ShaderLibrary { public: ~ShaderLibrary(); diff --git a/impeller/impeller/compositor/shader_library.mm b/impeller/impeller/compositor/shader_library.mm index 20b6f881ba3dd..852bb88bf0b6e 100644 --- a/impeller/impeller/compositor/shader_library.mm +++ b/impeller/impeller/compositor/shader_library.mm @@ -6,19 +6,6 @@ namespace impeller { -ShaderFunction::ShaderFunction(id function, ShaderStage stage) - : function_(function), stage_(stage) {} - -ShaderFunction::~ShaderFunction() = default; - -id ShaderFunction::GetMTLFunction() const { - return function_; -} - -ShaderStage ShaderFunction::GetStage() const { - return stage_; -} - ShaderLibrary::ShaderLibrary(id library) : library_(library) {} ShaderLibrary::~ShaderLibrary() = default; From b634eab416d346c7e281fb029343d8cad9f425ca Mon Sep 17 00:00:00 2001 From: Chinmay Garde Date: Sat, 22 May 2021 18:29:45 -0700 Subject: [PATCH 046/433] Shader function quality. --- impeller/impeller/compositor/comparable.cc | 3 ++- impeller/impeller/compositor/comparable.h | 21 +++++++++++++++++++ .../impeller/compositor/pipeline_library.h | 10 ++++++--- .../impeller/compositor/pipeline_library.mm | 15 ++++++++----- .../impeller/compositor/shader_function.h | 11 +++++++--- .../impeller/compositor/shader_function.mm | 21 +++++++++++++++++-- impeller/impeller/compositor/shader_library.h | 5 ++++- .../impeller/compositor/shader_library.mm | 4 ++-- .../impeller/compositor/vertex_descriptor.h | 4 ++-- .../impeller/compositor/vertex_descriptor.mm | 14 +++++++++++++ 10 files changed, 89 insertions(+), 19 deletions(-) diff --git a/impeller/impeller/compositor/comparable.cc b/impeller/impeller/compositor/comparable.cc index 86c8e18740cc2..f58bdf6b8c6b6 100644 --- a/impeller/impeller/compositor/comparable.cc +++ b/impeller/impeller/compositor/comparable.cc @@ -6,6 +6,7 @@ namespace impeller { -// +static std::atomic_size_t sLastID; +UniqueID::UniqueID() : id(++sLastID) {} } // namespace impeller diff --git a/impeller/impeller/compositor/comparable.h b/impeller/impeller/compositor/comparable.h index b546153aaff3d..0e6b0e8c180a9 100644 --- a/impeller/impeller/compositor/comparable.h +++ b/impeller/impeller/compositor/comparable.h @@ -15,6 +15,16 @@ namespace impeller { +struct UniqueID { + size_t id; + + UniqueID(); + + constexpr bool operator==(const UniqueID& other) const { + return id == other.id; + } +}; + class ComparableBase {}; template @@ -83,3 +93,14 @@ bool DeepCompareMap(const std::map>& lhs, } } // namespace impeller + +namespace std { + +template <> +struct hash { + constexpr std::size_t operator()(const impeller::UniqueID& id) { + return id.id; + } +}; + +} // namespace std diff --git a/impeller/impeller/compositor/pipeline_library.h b/impeller/impeller/compositor/pipeline_library.h index aa0ae7b5947d0..e3b000a48325a 100644 --- a/impeller/impeller/compositor/pipeline_library.h +++ b/impeller/impeller/compositor/pipeline_library.h @@ -7,6 +7,7 @@ #include #include +#include #include #include "flutter/fml/macros.h" @@ -15,10 +16,8 @@ namespace impeller { -class PipelineLibrary { +class PipelineLibrary : public std::enable_shared_from_this { public: - PipelineLibrary(id device); - ~PipelineLibrary(); std::future> GetRenderPipeline( @@ -32,6 +31,11 @@ class PipelineLibrary { id device_; Pipelines pipelines_; + PipelineLibrary(id device); + + void SavePipeline(PipelineDescriptor descriptor, + std::shared_ptr pipeline); + FML_DISALLOW_COPY_AND_ASSIGN(PipelineLibrary); }; diff --git a/impeller/impeller/compositor/pipeline_library.mm b/impeller/impeller/compositor/pipeline_library.mm index c70fcdcef26de..6ede19f03fd20 100644 --- a/impeller/impeller/compositor/pipeline_library.mm +++ b/impeller/impeller/compositor/pipeline_library.mm @@ -23,9 +23,7 @@ return future; } - // WIP Pipeline is not saved. - - pipelines_[descriptor] = nullptr; + auto thiz = shared_from_this(); auto completion_handler = ^(id _Nullable render_pipeline_state, @@ -35,8 +33,10 @@ << error.localizedDescription.UTF8String; promise->set_value(nullptr); } else { - promise->set_value( - std::shared_ptr(new Pipeline(render_pipeline_state))); + auto new_pipeline = + std::shared_ptr(new Pipeline(render_pipeline_state)); + promise->set_value(new_pipeline); + this->SavePipeline(descriptor, new_pipeline); } }; [device_ @@ -46,4 +46,9 @@ return future; } +void PipelineLibrary::SavePipeline(PipelineDescriptor descriptor, + std::shared_ptr pipeline) { + pipelines_[descriptor] = std::move(pipeline); +} + } // namespace impeller diff --git a/impeller/impeller/compositor/shader_function.h b/impeller/impeller/compositor/shader_function.h index 2adaf90b908b5..c78601673dcf7 100644 --- a/impeller/impeller/compositor/shader_function.h +++ b/impeller/impeller/compositor/shader_function.h @@ -21,19 +21,24 @@ class ShaderFunction : public Comparable { id GetMTLFunction() const; - // Comparable + // |Comparable| std::size_t GetHash() const override; - // Comparable + // |Comparable| bool IsEqual(const ShaderFunction& other) const override; private: friend class ShaderLibrary; + UniqueID parent_library_id_; id function_ = nullptr; + std::string name_; ShaderStage stage_; - ShaderFunction(id function, ShaderStage stage); + ShaderFunction(UniqueID parent_library_id, + id function, + std::string name, + ShaderStage stage); FML_DISALLOW_COPY_AND_ASSIGN(ShaderFunction); }; diff --git a/impeller/impeller/compositor/shader_function.mm b/impeller/impeller/compositor/shader_function.mm index 1a2653aaeb6f4..58c9d65d87622 100644 --- a/impeller/impeller/compositor/shader_function.mm +++ b/impeller/impeller/compositor/shader_function.mm @@ -6,8 +6,14 @@ namespace impeller { -ShaderFunction::ShaderFunction(id function, ShaderStage stage) - : function_(function), stage_(stage) {} +ShaderFunction::ShaderFunction(UniqueID parent_library_id, + id function, + std::string name, + ShaderStage stage) + : parent_library_id_(parent_library_id), + function_(function), + name_(std::move(name)), + stage_(stage) {} ShaderFunction::~ShaderFunction() = default; @@ -19,4 +25,15 @@ return stage_; } +// |Comparable| +std::size_t ShaderFunction::GetHash() const { + return fml::HashCombine(parent_library_id_, name_, stage_); +} + +// |Comparable| +bool ShaderFunction::IsEqual(const ShaderFunction& other) const { + return parent_library_id_ == other.parent_library_id_ && + name_ == other.name_ && stage_ == other.stage_; +} + } // namespace impeller diff --git a/impeller/impeller/compositor/shader_library.h b/impeller/impeller/compositor/shader_library.h index 06595e786d6d9..4a9b76c4e19a9 100644 --- a/impeller/impeller/compositor/shader_library.h +++ b/impeller/impeller/compositor/shader_library.h @@ -12,6 +12,7 @@ #include #include "flutter/fml/macros.h" +#include "impeller/compositor/comparable.h" #include "impeller/compositor/shader_function.h" namespace impeller { @@ -50,11 +51,13 @@ class ShaderLibrary { }; }; - id library_ = nullptr; using Functions = std::unordered_map, ShaderKey::Hash, ShaderKey::Equal>; + + UniqueID library_id_; + id library_ = nullptr; Functions functions_; ShaderLibrary(id library); diff --git a/impeller/impeller/compositor/shader_library.mm b/impeller/impeller/compositor/shader_library.mm index 852bb88bf0b6e..a7009934bddab 100644 --- a/impeller/impeller/compositor/shader_library.mm +++ b/impeller/impeller/compositor/shader_library.mm @@ -24,8 +24,8 @@ return nullptr; } - auto func = - std::shared_ptr(new ShaderFunction(function, stage)); + auto func = std::shared_ptr(new ShaderFunction( + library_id_, function, {name.data(), name.size()}, stage)); functions_[key] = func; return func; } diff --git a/impeller/impeller/compositor/vertex_descriptor.h b/impeller/impeller/compositor/vertex_descriptor.h index 641886e4f4b67..7be7c10819495 100644 --- a/impeller/impeller/compositor/vertex_descriptor.h +++ b/impeller/impeller/compositor/vertex_descriptor.h @@ -15,10 +15,10 @@ class VertexDescriptor : public Comparable { virtual ~VertexDescriptor(); - // Comparable + //| Comparable| std::size_t GetHash() const override; - // Comparable + // |Comparable| bool IsEqual(const VertexDescriptor& other) const override; private: diff --git a/impeller/impeller/compositor/vertex_descriptor.mm b/impeller/impeller/compositor/vertex_descriptor.mm index f146a9265cb2f..9758e235186eb 100644 --- a/impeller/impeller/compositor/vertex_descriptor.mm +++ b/impeller/impeller/compositor/vertex_descriptor.mm @@ -4,10 +4,24 @@ #include "impeller/compositor/vertex_descriptor.h" +#include "flutter/fml/logging.h" + namespace impeller { VertexDescriptor::VertexDescriptor() = default; VertexDescriptor::~VertexDescriptor() = default; +//| Comparable| +std::size_t VertexDescriptor::GetHash() const { + FML_CHECK(false); + return 0; +} + +// |Comparable| +bool VertexDescriptor::IsEqual(const VertexDescriptor& other) const { + FML_CHECK(false); + return false; +} + } // namespace impeller From 0959c0257226a2747bfbaa00ac17a6fe9327cbd3 Mon Sep 17 00:00:00 2001 From: Chinmay Garde Date: Sun, 23 May 2021 00:17:05 -0700 Subject: [PATCH 047/433] Simple types can be conveyed to runtime. --- impeller/compiler/code_gen_template.h | 9 +++- impeller/compiler/reflector.cc | 51 ++++++++++--------- .../impeller/compositor/vertex_descriptor.mm | 2 +- impeller/impeller/primitives/box.vert | 7 +++ impeller/impeller/shader_glue/shader_types.h | 28 +++++++++- 5 files changed, 71 insertions(+), 26 deletions(-) diff --git a/impeller/compiler/code_gen_template.h b/impeller/compiler/code_gen_template.h index ade82b13e8ca5..41834bd598f50 100644 --- a/impeller/compiler/code_gen_template.h +++ b/impeller/compiler/code_gen_template.h @@ -24,7 +24,14 @@ struct {{camel_case(shader_name)}}{{camel_case(shader_stage)}}Info { // Stage Inputs {% for stage_input in stage_inputs %} // Stage input {{stage_input.name}} - static constexpr ShaderStageInput kInput{{camel_case(stage_input.name)}} = {"{{stage_input.name}}", {{stage_input.location}}}; + static constexpr ShaderStageInput kInput{{camel_case(stage_input.name)}} = { + "{{stage_input.name}}", // name + {{stage_input.location}}u, // attribute location + {{stage_input.type.type_name}}, // type + {{stage_input.type.bit_width}}u, // bit width + {{stage_input.type.vec_size}}u, // vec size + {{stage_input.type.columns}}u // number of columns + }; {% endfor %} }; // struct {{camel_case(shader_name)}}{{camel_case(shader_stage)}}Info diff --git a/impeller/compiler/reflector.cc b/impeller/compiler/reflector.cc index 0aa50c57b00d4..7f656f666ebde 100644 --- a/impeller/compiler/reflector.cc +++ b/impeller/compiler/reflector.cc @@ -26,47 +26,43 @@ static std::string BaseTypeToString(spirv_cross::SPIRType::BaseType type) { using Type = spirv_cross::SPIRType::BaseType; switch (type) { case Type::Void: - return "Void"; + return "ShaderType::kVoid"; case Type::Boolean: - return "Boolean"; + return "ShaderType::kBoolean"; case Type::SByte: - return "SByte"; + return "ShaderType::kSignedByte"; case Type::UByte: - return "UByte"; + return "ShaderType::kUnsignedByte"; case Type::Short: - return "Short"; + return "ShaderType::kSignedShort"; case Type::UShort: - return "UShort"; + return "ShaderType::kUnsignedShort"; case Type::Int: - return "Int"; + return "ShaderType::kSignedInt"; case Type::UInt: - return "UInt"; + return "ShaderType::kUnsignedInt"; case Type::Int64: - return "Int64"; + return "ShaderType::kSignedInt64"; case Type::UInt64: - return "UInt64"; + return "ShaderType::kUnsignedInt64"; case Type::AtomicCounter: - return "AtomicCounter"; + return "ShaderType::kAtomicCounter"; case Type::Half: - return "Half"; + return "ShaderType::kHalfFloat"; case Type::Float: - return "Float"; + return "ShaderType::kFloat"; case Type::Double: - return "Double"; + return "ShaderType::kDouble"; case Type::Struct: - return "Struct"; + return "ShaderType::kStruct"; case Type::Image: - return "Image"; + return "ShaderType::kImage"; case Type::SampledImage: - return "SampledImage"; + return "ShaderType::kSampledImage"; case Type::Sampler: - return "Sampler"; - case Type::AccelerationStructure: - return "AccelerationStructure"; - case Type::RayQuery: - return "RayQuery"; + return "ShaderType::kSampler"; default: - return "unknown"; + return "ShaderType::kUnknown"; } } @@ -79,6 +75,15 @@ static bool ReflectType(Writer& writer, writer.Key("type_name"); writer.String(BaseTypeToString(type.basetype)); + writer.Key("bit_width"); + writer.Uint64(type.width); + + writer.Key("vec_size"); + writer.Uint64(type.vecsize); + + writer.Key("columns"); + writer.Uint64(type.columns); + writer.Key("member_types"); writer.StartArray(); for (const auto& member : type.member_types) { diff --git a/impeller/impeller/compositor/vertex_descriptor.mm b/impeller/impeller/compositor/vertex_descriptor.mm index 9758e235186eb..4ad3df8a29a3a 100644 --- a/impeller/impeller/compositor/vertex_descriptor.mm +++ b/impeller/impeller/compositor/vertex_descriptor.mm @@ -12,7 +12,7 @@ VertexDescriptor::~VertexDescriptor() = default; -//| Comparable| +// |Comparable| std::size_t VertexDescriptor::GetHash() const { FML_CHECK(false); return 0; diff --git a/impeller/impeller/primitives/box.vert b/impeller/impeller/primitives/box.vert index 6a50f78228f6b..dd932aeaff3a9 100644 --- a/impeller/impeller/primitives/box.vert +++ b/impeller/impeller/primitives/box.vert @@ -10,8 +10,15 @@ uniform UniformBuffer2 { uniform mat4 longGOP; } uniforms2; +struct Position { + vec3 position; + vec3 color; +}; + in vec3 vertexPosition; in vec3 vertexColor; +in dvec4 hello; +in uvec4 hello2; out vec3 color; diff --git a/impeller/impeller/shader_glue/shader_types.h b/impeller/impeller/shader_glue/shader_types.h index f3c3adbd59c85..a280577540a17 100644 --- a/impeller/impeller/shader_glue/shader_types.h +++ b/impeller/impeller/shader_glue/shader_types.h @@ -15,9 +15,35 @@ enum class ShaderStage { kFragment, }; +enum class ShaderType { + kUnknown, + kVoid, + kBoolean, + kSignedByte, + kUnsignedByte, + kSignedShort, + kUnsignedShort, + kSignedInt, + kUnsignedInt, + kSignedInt64, + kUnsignedInt64, + kAtomicCounter, + kHalfFloat, + kFloat, + kDouble, + kStruct, + kImage, + kSampledImage, + kSampler, +}; + struct ShaderStageInput { const char* name; - std::size_t location; + size_t location; + ShaderType type; + size_t bit_width; + size_t vec_size; + size_t columns; }; } // namespace impeller From 6a5d0b1c9fb3122533c14c157b03d089d437f650 Mon Sep 17 00:00:00 2001 From: Chinmay Garde Date: Sun, 23 May 2021 00:34:29 -0700 Subject: [PATCH 048/433] Minor formatting updates. --- impeller/compiler/code_gen_template.h | 12 ++++++------ impeller/impeller/primitives/box.vert | 1 + 2 files changed, 7 insertions(+), 6 deletions(-) diff --git a/impeller/compiler/code_gen_template.h b/impeller/compiler/code_gen_template.h index 41834bd598f50..88185ad9bf8bc 100644 --- a/impeller/compiler/code_gen_template.h +++ b/impeller/compiler/code_gen_template.h @@ -25,12 +25,12 @@ struct {{camel_case(shader_name)}}{{camel_case(shader_stage)}}Info { {% for stage_input in stage_inputs %} // Stage input {{stage_input.name}} static constexpr ShaderStageInput kInput{{camel_case(stage_input.name)}} = { - "{{stage_input.name}}", // name - {{stage_input.location}}u, // attribute location - {{stage_input.type.type_name}}, // type - {{stage_input.type.bit_width}}u, // bit width - {{stage_input.type.vec_size}}u, // vec size - {{stage_input.type.columns}}u // number of columns + "{{stage_input.name}}", // name + {{stage_input.location}}u, // attribute location + {{stage_input.type.type_name}}, // type + {{stage_input.type.bit_width}}u, // bit width of type + {{stage_input.type.vec_size}}u, // vec size + {{stage_input.type.columns}}u // number of columns }; {% endfor %} }; // struct {{camel_case(shader_name)}}{{camel_case(shader_stage)}}Info diff --git a/impeller/impeller/primitives/box.vert b/impeller/impeller/primitives/box.vert index dd932aeaff3a9..eda8247d193ec 100644 --- a/impeller/impeller/primitives/box.vert +++ b/impeller/impeller/primitives/box.vert @@ -18,6 +18,7 @@ struct Position { in vec3 vertexPosition; in vec3 vertexColor; in dvec4 hello; +in dmat4x2 hello12; in uvec4 hello2; out vec3 color; From d148ad1bca68670bebc95e54f01d5ee99e3aa661 Mon Sep 17 00:00:00 2001 From: Chinmay Garde Date: Sun, 23 May 2021 18:26:26 -0700 Subject: [PATCH 049/433] Parse MTL vertex input formats from reflection info. --- impeller/compiler/code_gen_template.h | 9 +- .../impeller/compositor/vertex_descriptor.h | 24 ++- .../impeller/compositor/vertex_descriptor.mm | 196 +++++++++++++++++- 3 files changed, 223 insertions(+), 6 deletions(-) diff --git a/impeller/compiler/code_gen_template.h b/impeller/compiler/code_gen_template.h index 88185ad9bf8bc..ff03f6789e548 100644 --- a/impeller/compiler/code_gen_template.h +++ b/impeller/compiler/code_gen_template.h @@ -24,7 +24,7 @@ struct {{camel_case(shader_name)}}{{camel_case(shader_stage)}}Info { // Stage Inputs {% for stage_input in stage_inputs %} // Stage input {{stage_input.name}} - static constexpr ShaderStageInput kInput{{camel_case(stage_input.name)}} = { + static constexpr auto kInput{{camel_case(stage_input.name)}} = ShaderStageInput { "{{stage_input.name}}", // name {{stage_input.location}}u, // attribute location {{stage_input.type.type_name}}, // type @@ -33,6 +33,13 @@ struct {{camel_case(shader_name)}}{{camel_case(shader_stage)}}Info { {{stage_input.type.columns}}u // number of columns }; {% endfor %} + + static constexpr const ShaderStageInput* kAllShaderStageInputs[] = { +{% for stage_input in stage_inputs %} + &kInput{{camel_case(stage_input.name)}}, // {{stage_input.name}} +{% endfor %} + }; + }; // struct {{camel_case(shader_name)}}{{camel_case(shader_stage)}}Info } // namespace shader diff --git a/impeller/impeller/compositor/vertex_descriptor.h b/impeller/impeller/compositor/vertex_descriptor.h index 7be7c10819495..1796ccf38b1e9 100644 --- a/impeller/impeller/compositor/vertex_descriptor.h +++ b/impeller/impeller/compositor/vertex_descriptor.h @@ -4,17 +4,26 @@ #pragma once +#include + +#include + #include "flutter/fml/macros.h" #include "impeller/compositor/comparable.h" +#include "impeller/shader_glue/shader_types.h" namespace impeller { -class VertexDescriptor : public Comparable { +class VertexDescriptor final : public Comparable { public: VertexDescriptor(); virtual ~VertexDescriptor(); + bool SetStageInputs(const ShaderStageInput* stage_inputs[], size_t count); + + MTLVertexDescriptor* GetMTLVertexDescriptor() const; + //| Comparable| std::size_t GetHash() const override; @@ -22,6 +31,19 @@ class VertexDescriptor : public Comparable { bool IsEqual(const VertexDescriptor& other) const override; private: + struct StageInput { + size_t location; + MTLVertexFormat format; + + StageInput(size_t p_location, MTLVertexFormat p_format) + : location(p_location), format(p_format) {} + + constexpr bool operator==(const StageInput& other) const { + return location == other.location && format == other.format; + } + }; + std::vector stage_inputs_; + FML_DISALLOW_COPY_AND_ASSIGN(VertexDescriptor); }; diff --git a/impeller/impeller/compositor/vertex_descriptor.mm b/impeller/impeller/compositor/vertex_descriptor.mm index 4ad3df8a29a3a..ed5fc797ef632 100644 --- a/impeller/impeller/compositor/vertex_descriptor.mm +++ b/impeller/impeller/compositor/vertex_descriptor.mm @@ -12,16 +12,204 @@ VertexDescriptor::~VertexDescriptor() = default; +static MTLVertexFormat ReadStageInputFormat(const ShaderStageInput& input) { + if (input.columns != 1) { + // All matrix types are unsupported as vertex inputs. + return MTLVertexFormatInvalid; + } + + switch (input.type) { + case ShaderType::kFloat: { + if (input.bit_width == sizeof(float)) { + switch (input.vec_size) { + case 1: + return MTLVertexFormatFloat; + case 2: + return MTLVertexFormatFloat2; + case 3: + return MTLVertexFormatFloat3; + case 4: + return MTLVertexFormatFloat4; + } + } + return MTLVertexFormatInvalid; + } + case ShaderType::kHalfFloat: { + if (input.bit_width == sizeof(float) / 2) { + switch (input.vec_size) { + case 1: + return MTLVertexFormatHalf; + case 2: + return MTLVertexFormatHalf2; + case 3: + return MTLVertexFormatHalf3; + case 4: + return MTLVertexFormatHalf4; + } + } + return MTLVertexFormatInvalid; + } + case ShaderType::kDouble: { + // Unsupported. + return MTLVertexFormatInvalid; + } + case ShaderType::kBoolean: { + if (input.bit_width == sizeof(bool) && input.vec_size == 1) { + return MTLVertexFormatChar; + } + return MTLVertexFormatInvalid; + } + case ShaderType::kSignedByte: { + if (input.bit_width == sizeof(char)) { + switch (input.vec_size) { + case 1: + return MTLVertexFormatChar; + case 2: + return MTLVertexFormatChar2; + case 3: + return MTLVertexFormatChar3; + case 4: + return MTLVertexFormatChar4; + } + } + return MTLVertexFormatInvalid; + } + case ShaderType::kUnsignedByte: { + if (input.bit_width == sizeof(char)) { + switch (input.vec_size) { + case 1: + return MTLVertexFormatUChar; + case 2: + return MTLVertexFormatUChar2; + case 3: + return MTLVertexFormatUChar3; + case 4: + return MTLVertexFormatUChar4; + } + } + return MTLVertexFormatInvalid; + } + case ShaderType::kSignedShort: { + if (input.bit_width == sizeof(short)) { + switch (input.vec_size) { + case 1: + return MTLVertexFormatShort; + case 2: + return MTLVertexFormatShort2; + case 3: + return MTLVertexFormatShort3; + case 4: + return MTLVertexFormatShort4; + } + } + return MTLVertexFormatInvalid; + } + case ShaderType::kUnsignedShort: { + if (input.bit_width == sizeof(ushort)) { + switch (input.vec_size) { + case 1: + return MTLVertexFormatUShort; + case 2: + return MTLVertexFormatUShort2; + case 3: + return MTLVertexFormatUShort3; + case 4: + return MTLVertexFormatUShort4; + } + } + return MTLVertexFormatInvalid; + } + case ShaderType::kSignedInt: { + if (input.bit_width == sizeof(int32_t)) { + switch (input.vec_size) { + case 1: + return MTLVertexFormatInt; + case 2: + return MTLVertexFormatInt2; + case 3: + return MTLVertexFormatInt3; + case 4: + return MTLVertexFormatInt4; + } + } + return MTLVertexFormatInvalid; + } + case ShaderType::kUnsignedInt: { + if (input.bit_width == sizeof(uint32_t)) { + switch (input.vec_size) { + case 1: + return MTLVertexFormatUInt; + case 2: + return MTLVertexFormatUInt2; + case 3: + return MTLVertexFormatUInt3; + case 4: + return MTLVertexFormatUInt4; + } + } + return MTLVertexFormatInvalid; + } + case ShaderType::kSignedInt64: { + // Unsupported. + return MTLVertexFormatInvalid; + } + case ShaderType::kUnsignedInt64: { + // Unsupported. + return MTLVertexFormatInvalid; + } + case ShaderType::kAtomicCounter: + case ShaderType::kStruct: + case ShaderType::kImage: + case ShaderType::kSampledImage: + case ShaderType::kUnknown: + case ShaderType::kVoid: + case ShaderType::kSampler: + return MTLVertexFormatInvalid; + } +} + +bool VertexDescriptor::SetStageInputs(const ShaderStageInput* stage_inputs[], + size_t count) { + stage_inputs_.clear(); + + for (size_t i = 0; i < count; i++) { + const ShaderStageInput* input = stage_inputs[i]; + auto vertex_format = ReadStageInputFormat(*input); + if (vertex_format == MTLVertexFormatInvalid) { + FML_LOG(ERROR) << "Format for input " << input->name << " not supported."; + return false; + } + stage_inputs_.emplace_back(StageInput{input->location, vertex_format}); + } + + return true; +} + +MTLVertexDescriptor* VertexDescriptor::GetMTLVertexDescriptor() const { + auto descriptor = [MTLVertexDescriptor vertexDescriptor]; + + for (const auto& input : stage_inputs_) { + auto attrib = descriptor.attributes[input.location]; + attrib.format = input.format; + attrib.offset = 0u; + attrib.bufferIndex = 0u; + } + + return descriptor; +} + // |Comparable| std::size_t VertexDescriptor::GetHash() const { - FML_CHECK(false); - return 0; + auto seed = fml::HashCombine(); + for (const auto& input : stage_inputs_) { + fml::HashCombineSeed(seed, input.location, input.format); + } + return seed; } // |Comparable| bool VertexDescriptor::IsEqual(const VertexDescriptor& other) const { - FML_CHECK(false); - return false; + return stage_inputs_ == other.stage_inputs_; } } // namespace impeller From b305eabb44ab03cd1f9fea956ee586c5c3204843 Mon Sep 17 00:00:00 2001 From: Chinmay Garde Date: Mon, 24 May 2021 00:22:31 -0700 Subject: [PATCH 050/433] Dump stage outputs. --- impeller/compiler/reflector.cc | 14 +++++++++++++- 1 file changed, 13 insertions(+), 1 deletion(-) diff --git a/impeller/compiler/reflector.cc b/impeller/compiler/reflector.cc index 7f656f666ebde..f7f2d87e7c78f 100644 --- a/impeller/compiler/reflector.cc +++ b/impeller/compiler/reflector.cc @@ -222,7 +222,19 @@ static std::shared_ptr ReflectTemplateArguments( writer.StartArray(); for (const auto& input : compiler.get_shader_resources().stage_inputs) { if (!ReflectStageInput(writer, compiler, input)) { - FML_LOG(ERROR) << "Could not reflect uniform buffer."; + FML_LOG(ERROR) << "Could not reflect stage input."; + return nullptr; + } + } + writer.EndArray(); + } + + { + writer.Key("stage_outputs"); + writer.StartArray(); + for (const auto& output : compiler.get_shader_resources().stage_outputs) { + if (!ReflectStageInput(writer, compiler, output)) { + FML_LOG(ERROR) << "Could not reflect stage output."; return nullptr; } } From c0197d1eaba08f32cfce21c9ff9dca7ef11ac933 Mon Sep 17 00:00:00 2001 From: Chinmay Garde Date: Mon, 24 May 2021 02:39:52 -0700 Subject: [PATCH 051/433] Parse member names. --- impeller/compiler/compiler.cc | 13 ++-- impeller/compiler/compiler.h | 1 + impeller/compiler/reflector.cc | 90 +++++++++++++++++++++------ impeller/compiler/reflector.h | 5 +- impeller/impeller/primitives/box.vert | 26 +++++--- 5 files changed, 101 insertions(+), 34 deletions(-) diff --git a/impeller/compiler/compiler.cc b/impeller/compiler/compiler.cc index 33dcd59a9af61..57af0f7b6bb77 100644 --- a/impeller/compiler/compiler.cc +++ b/impeller/compiler/compiler.cc @@ -278,8 +278,13 @@ Compiler::Compiler(const fml::Mapping& source_mapping, } // MSL Generation. - spirv_cross::CompilerMSL msl_compiler( - spv_result_->cbegin(), spv_result_->cend() - spv_result_->cbegin()); + spirv_cross::Parser parser(spv_result_->cbegin(), + spv_result_->cend() - spv_result_->cbegin()); + // The parser and compiler must be run separately because the parser contains + // meta information (like type member names) that are useful for reflection. + parser.parse(); + const auto& parsed_ir = parser.get_parsed_ir(); + spirv_cross::CompilerMSL msl_compiler(parsed_ir); { msl_compiler.rename_entry_point("main", options_.entry_point_name, @@ -299,8 +304,8 @@ Compiler::Compiler(const fml::Mapping& source_mapping, return; } - reflector_ = - std::make_unique(std::move(reflector_options), msl_compiler); + reflector_ = std::make_unique(std::move(reflector_options), + parsed_ir, msl_compiler); if (!reflector_->IsValid()) { COMPILER_ERROR << "Could not complete reflection on generated shader."; diff --git a/impeller/compiler/compiler.h b/impeller/compiler/compiler.h index 3c1dd4e4ffd67..7dec9d39ac303 100644 --- a/impeller/compiler/compiler.h +++ b/impeller/compiler/compiler.h @@ -14,6 +14,7 @@ #include "flutter/impeller/compiler/reflector.h" #include "shaderc/shaderc.hpp" #include "third_party/spirv_cross/spirv_msl.hpp" +#include "third_party/spirv_cross/spirv_parser.hpp" namespace impeller { namespace compiler { diff --git a/impeller/compiler/reflector.cc b/impeller/compiler/reflector.cc index f7f2d87e7c78f..a2d31b36043b4 100644 --- a/impeller/compiler/reflector.cc +++ b/impeller/compiler/reflector.cc @@ -4,6 +4,7 @@ #include "flutter/impeller/compiler/reflector.h" +#include #include #include @@ -66,9 +67,47 @@ static std::string BaseTypeToString(spirv_cross::SPIRType::BaseType type) { } } +static std::optional GetMemberNameAtIndexIfExists( + const spirv_cross::ParsedIR& ir, + const spirv_cross::CompilerMSL& compiler, + const spirv_cross::SPIRType& type, + size_t index) { + if (type.type_alias != 0) { + return GetMemberNameAtIndexIfExists( + ir, compiler, compiler.get_type(type.type_alias), index); + } + + if (auto found = ir.meta.find(type.self); found != ir.meta.end()) { + const auto& members = found->second.members; + if (index < members.size() && !members[index].alias.empty()) { + return members[index].alias; + } + } + return std::nullopt; +} + +static std::string GetMemberNameAtIndex( + const spirv_cross::ParsedIR& ir, + const spirv_cross::CompilerMSL& compiler, + const spirv_cross::SPIRType& type, + size_t index) { + if (auto name = GetMemberNameAtIndexIfExists(ir, compiler, type, index); + name.has_value()) { + return name.value(); + } + + static std::atomic_size_t sUnnamedMembersID; + std::stringstream stream; + stream << "unnamed_" << sUnnamedMembersID++; + return stream.str(); +} + static bool ReflectType(Writer& writer, + const spirv_cross::ParsedIR& ir, const spirv_cross::CompilerMSL& compiler, - const spirv_cross::SPIRType& type) { + const spirv_cross::TypeID& type_id) { + const auto type = compiler.get_type(type_id); + writer.Key("type"); writer.StartObject(); @@ -84,24 +123,32 @@ static bool ReflectType(Writer& writer, writer.Key("columns"); writer.Uint64(type.columns); - writer.Key("member_types"); - writer.StartArray(); - for (const auto& member : type.member_types) { - if (!ReflectType(writer, compiler, compiler.get_type(member))) { - return false; + if (!type.member_types.empty()) { + writer.Key("member"); + writer.StartArray(); + for (size_t i = 0; i < type.member_types.size(); i++) { + writer.StartObject(); + { + writer.Key("type_id"); + writer.Uint64(type.member_types[i]); + writer.Key("member_name"); + writer.String(GetMemberNameAtIndex(ir, compiler, type, i)); + } + writer.EndObject(); } + writer.EndArray(); } - writer.EndArray(); writer.EndObject(); return true; } static bool ReflectBaseResource(Writer& writer, + const spirv_cross::ParsedIR& ir, const spirv_cross::CompilerMSL& compiler, const spirv_cross::Resource& res) { writer.Key("name"); - writer.String(compiler.get_name(res.id)); + writer.String(res.name); writer.Key("descriptor_set"); writer.Uint64(compiler.get_decoration( @@ -115,19 +162,20 @@ static bool ReflectBaseResource(Writer& writer, writer.Uint64( compiler.get_decoration(res.id, spv::Decoration::DecorationLocation)); - if (!ReflectType(writer, compiler, compiler.get_type(res.type_id))) { + if (!ReflectType(writer, ir, compiler, res.type_id)) { return false; } return true; } -static bool ReflectStageInput(Writer& writer, - const spirv_cross::CompilerMSL& compiler, - const spirv_cross::Resource& input) { +static bool ReflectStageIO(Writer& writer, + const spirv_cross::ParsedIR& ir, + const spirv_cross::CompilerMSL& compiler, + const spirv_cross::Resource& io) { writer.StartObject(); - if (!ReflectBaseResource(writer, compiler, input)) { + if (!ReflectBaseResource(writer, ir, compiler, io)) { return false; } @@ -136,11 +184,12 @@ static bool ReflectStageInput(Writer& writer, } static bool ReflectUniformBuffer(Writer& writer, + const spirv_cross::ParsedIR& ir, const spirv_cross::CompilerMSL& compiler, const spirv_cross::Resource& buffer) { writer.StartObject(); - if (!ReflectBaseResource(writer, compiler, buffer)) { + if (!ReflectBaseResource(writer, ir, compiler, buffer)) { return false; } @@ -177,6 +226,7 @@ static std::string StringToShaderStage(std::string str) { static std::shared_ptr ReflectTemplateArguments( const Reflector::Options& options, + const spirv_cross::ParsedIR& ir, const spirv_cross::CompilerMSL& compiler) { auto buffer = std::make_shared(); Writer writer(*buffer); @@ -209,7 +259,7 @@ static std::shared_ptr ReflectTemplateArguments( writer.StartArray(); for (const auto& uniform_buffer : compiler.get_shader_resources().uniform_buffers) { - if (!ReflectUniformBuffer(writer, compiler, uniform_buffer)) { + if (!ReflectUniformBuffer(writer, ir, compiler, uniform_buffer)) { FML_LOG(ERROR) << "Could not reflect uniform buffer."; return nullptr; } @@ -221,7 +271,7 @@ static std::shared_ptr ReflectTemplateArguments( writer.Key("stage_inputs"); writer.StartArray(); for (const auto& input : compiler.get_shader_resources().stage_inputs) { - if (!ReflectStageInput(writer, compiler, input)) { + if (!ReflectStageIO(writer, ir, compiler, input)) { FML_LOG(ERROR) << "Could not reflect stage input."; return nullptr; } @@ -233,7 +283,7 @@ static std::shared_ptr ReflectTemplateArguments( writer.Key("stage_outputs"); writer.StartArray(); for (const auto& output : compiler.get_shader_resources().stage_outputs) { - if (!ReflectStageInput(writer, compiler, output)) { + if (!ReflectStageIO(writer, ir, compiler, output)) { FML_LOG(ERROR) << "Could not reflect stage output."; return nullptr; } @@ -279,9 +329,11 @@ static std::shared_ptr InflateTemplate( inflated_template->size(), [inflated_template](auto, auto) {}); } -Reflector::Reflector(Options options, const spirv_cross::CompilerMSL& compiler) +Reflector::Reflector(Options options, + const spirv_cross::ParsedIR& ir, + const spirv_cross::CompilerMSL& compiler) : options_(std::move(options)), - template_arguments_(ReflectTemplateArguments(options_, compiler)), + template_arguments_(ReflectTemplateArguments(options_, ir, compiler)), reflection_header_(InflateTemplate(compiler, kReflectionHeaderTemplate, template_arguments_.get())), diff --git a/impeller/compiler/reflector.h b/impeller/compiler/reflector.h index 337488a05ad27..7346b9895df94 100644 --- a/impeller/compiler/reflector.h +++ b/impeller/compiler/reflector.h @@ -9,6 +9,7 @@ #include "flutter/fml/macros.h" #include "flutter/fml/mapping.h" #include "third_party/spirv_cross/spirv_msl.hpp" +#include "third_party/spirv_cross/spirv_parser.hpp" namespace impeller { namespace compiler { @@ -20,7 +21,9 @@ class Reflector { std::string header_file_name; }; - Reflector(Options options, const spirv_cross::CompilerMSL& compiler); + Reflector(Options options, + const spirv_cross::ParsedIR& ir, + const spirv_cross::CompilerMSL& compiler); ~Reflector(); diff --git a/impeller/impeller/primitives/box.vert b/impeller/impeller/primitives/box.vert index eda8247d193ec..0a9d4524abd7d 100644 --- a/impeller/impeller/primitives/box.vert +++ b/impeller/impeller/primitives/box.vert @@ -2,19 +2,25 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -uniform UniformBuffer { - mat4 mvp; -} uniforms; +struct MyAwesomeStruct { + float mvp0; + mat4x2 mvp1; + mat4 mvp2; + mat4 mvp3; +}; + +uniform UniformBuffer1 { + MyAwesomeStruct s1; + MyAwesomeStruct s2; + MyAwesomeStruct s3; +} uniforms1; uniform UniformBuffer2 { - uniform mat4 longGOP; + MyAwesomeStruct s11; + float hello12; + MyAwesomeStruct s13; } uniforms2; -struct Position { - vec3 position; - vec3 color; -}; - in vec3 vertexPosition; in vec3 vertexColor; in dvec4 hello; @@ -25,6 +31,6 @@ out vec3 color; void main() { - gl_Position = uniforms2.longGOP * uniforms.mvp * vec4(vertexPosition, 1.0); + gl_Position = uniforms1.s1.mvp2 * uniforms2.s13.mvp3 * vec4(vertexPosition, 1.0); color = vertexColor; } From 843dfb4ca74020ab19d6348e6eec126e02bbcd96 Mon Sep 17 00:00:00 2001 From: Chinmay Garde Date: Mon, 24 May 2021 21:36:42 -0700 Subject: [PATCH 052/433] Setup vertex attributes. --- impeller/compiler/code_gen_template.h | 4 +- impeller/host/impeller_renderer.mm | 7 +++- impeller/impeller/compositor/context.mm | 10 +++++ .../compositor/pipeline_descriptor.mm | 5 +++ .../impeller/compositor/pipeline_library.h | 4 ++ impeller/impeller/compositor/renderer.h | 2 + impeller/impeller/compositor/renderer.mm | 4 ++ .../impeller/compositor/vertex_descriptor.h | 8 ++-- .../impeller/compositor/vertex_descriptor.mm | 36 ++++++++++------ impeller/impeller/primitives/box.h | 2 +- impeller/impeller/primitives/box.mm | 42 +++++++++++++++---- impeller/impeller/primitives/box.vert | 33 +++------------ impeller/impeller/shader_glue/shader_types.h | 1 + 13 files changed, 101 insertions(+), 57 deletions(-) diff --git a/impeller/compiler/code_gen_template.h b/impeller/compiler/code_gen_template.h index ff03f6789e548..5948b2963121e 100644 --- a/impeller/compiler/code_gen_template.h +++ b/impeller/compiler/code_gen_template.h @@ -34,9 +34,9 @@ struct {{camel_case(shader_name)}}{{camel_case(shader_stage)}}Info { }; {% endfor %} - static constexpr const ShaderStageInput* kAllShaderStageInputs[] = { + static constexpr std::array kAllShaderStageInputs[] = { {% for stage_input in stage_inputs %} - &kInput{{camel_case(stage_input.name)}}, // {{stage_input.name}} + { &kInput{{camel_case(stage_input.name)}} }, // {{stage_input.name}} {% endfor %} }; diff --git a/impeller/host/impeller_renderer.mm b/impeller/host/impeller_renderer.mm index 51ea49a43c11c..7129193df18e7 100644 --- a/impeller/host/impeller_renderer.mm +++ b/impeller/host/impeller_renderer.mm @@ -8,9 +8,9 @@ #import "assets_location.h" #include "flutter/fml/logging.h" #import "impeller/compositor/renderer.h" +#import "impeller/primitives/box.h" #import "impeller_renderer.h" #import "shaders_location.h" - // Include header shared between C code here, which executes Metal API commands, // and .metal files #import "ShaderTypes.h" @@ -55,10 +55,15 @@ - (nonnull instancetype)initWithMetalKitView:(nonnull MTKView*)view { renderer_ = std::make_unique( impeller::ImpellerShadersDirectory()); + if (!renderer_->IsValid()) { FML_LOG(ERROR) << "Impeller Renderer is not valid."; } + if (!impeller::RenderBox(renderer_->GetContext())) { + FML_LOG(ERROR) << "Could not render box."; + } + return self; } diff --git a/impeller/impeller/compositor/context.mm b/impeller/impeller/compositor/context.mm index cbba8d7c5bd21..81f9fa0d90d16 100644 --- a/impeller/impeller/compositor/context.mm +++ b/impeller/impeller/compositor/context.mm @@ -47,6 +47,12 @@ std::shared_ptr(new ShaderLibrary(library)); } + // Setup the pipeline library. + { // + pipeline_library_ = + std::shared_ptr(new PipelineLibrary(device_)); + } + is_valid_ = true; } @@ -68,4 +74,8 @@ return shader_library_; } +std::shared_ptr Context::GetPipelineLibrary() const { + return pipeline_library_; +} + } // namespace impeller diff --git a/impeller/impeller/compositor/pipeline_descriptor.mm b/impeller/impeller/compositor/pipeline_descriptor.mm index bcfb38c67f609..7cb9098f6c765 100644 --- a/impeller/impeller/compositor/pipeline_descriptor.mm +++ b/impeller/impeller/compositor/pipeline_descriptor.mm @@ -83,6 +83,11 @@ descriptor.fragmentFunction = entry.second->GetMTLFunction(); } } + + if (vertex_descriptor_) { + descriptor.vertexDescriptor = vertex_descriptor_->GetMTLVertexDescriptor(); + } + return descriptor; } diff --git a/impeller/impeller/compositor/pipeline_library.h b/impeller/impeller/compositor/pipeline_library.h index e3b000a48325a..bf8b7eda553d6 100644 --- a/impeller/impeller/compositor/pipeline_library.h +++ b/impeller/impeller/compositor/pipeline_library.h @@ -16,6 +16,8 @@ namespace impeller { +class Context; + class PipelineLibrary : public std::enable_shared_from_this { public: ~PipelineLibrary(); @@ -24,6 +26,8 @@ class PipelineLibrary : public std::enable_shared_from_this { PipelineDescriptor descriptor); private: + friend Context; + using Pipelines = std::unordered_map, ComparableHash, diff --git a/impeller/impeller/compositor/renderer.h b/impeller/impeller/compositor/renderer.h index 7765af8764ea0..3afa939242343 100644 --- a/impeller/impeller/compositor/renderer.h +++ b/impeller/impeller/compositor/renderer.h @@ -25,6 +25,8 @@ class Renderer { bool Render(); + std::shared_ptr GetContext() const; + private: std::shared_ptr context_; std::unique_ptr surface_; diff --git a/impeller/impeller/compositor/renderer.mm b/impeller/impeller/compositor/renderer.mm index 8ff3f03f53fb3..0b5cf2696b5e7 100644 --- a/impeller/impeller/compositor/renderer.mm +++ b/impeller/impeller/compositor/renderer.mm @@ -49,4 +49,8 @@ return surface_->Render(); } +std::shared_ptr Renderer::GetContext() const { + return context_; +} + } // namespace impeller diff --git a/impeller/impeller/compositor/vertex_descriptor.h b/impeller/impeller/compositor/vertex_descriptor.h index 1796ccf38b1e9..ae785c58257b6 100644 --- a/impeller/impeller/compositor/vertex_descriptor.h +++ b/impeller/impeller/compositor/vertex_descriptor.h @@ -20,7 +20,8 @@ class VertexDescriptor final : public Comparable { virtual ~VertexDescriptor(); - bool SetStageInputs(const ShaderStageInput* stage_inputs[], size_t count); + bool SetStageInputs(const ShaderStageInput* const stage_inputs[], + size_t count); MTLVertexDescriptor* GetMTLVertexDescriptor() const; @@ -34,9 +35,10 @@ class VertexDescriptor final : public Comparable { struct StageInput { size_t location; MTLVertexFormat format; + size_t stride; - StageInput(size_t p_location, MTLVertexFormat p_format) - : location(p_location), format(p_format) {} + StageInput(size_t p_location, MTLVertexFormat p_format, size_t p_stride) + : location(p_location), format(p_format), stride(p_stride) {} constexpr bool operator==(const StageInput& other) const { return location == other.location && format == other.format; diff --git a/impeller/impeller/compositor/vertex_descriptor.mm b/impeller/impeller/compositor/vertex_descriptor.mm index ed5fc797ef632..448a3b19a3621 100644 --- a/impeller/impeller/compositor/vertex_descriptor.mm +++ b/impeller/impeller/compositor/vertex_descriptor.mm @@ -20,7 +20,7 @@ static MTLVertexFormat ReadStageInputFormat(const ShaderStageInput& input) { switch (input.type) { case ShaderType::kFloat: { - if (input.bit_width == sizeof(float)) { + if (input.bit_width == 8 * sizeof(float)) { switch (input.vec_size) { case 1: return MTLVertexFormatFloat; @@ -35,7 +35,7 @@ static MTLVertexFormat ReadStageInputFormat(const ShaderStageInput& input) { return MTLVertexFormatInvalid; } case ShaderType::kHalfFloat: { - if (input.bit_width == sizeof(float) / 2) { + if (input.bit_width == 8 * sizeof(float) / 2) { switch (input.vec_size) { case 1: return MTLVertexFormatHalf; @@ -54,13 +54,13 @@ static MTLVertexFormat ReadStageInputFormat(const ShaderStageInput& input) { return MTLVertexFormatInvalid; } case ShaderType::kBoolean: { - if (input.bit_width == sizeof(bool) && input.vec_size == 1) { + if (input.bit_width == 8 * sizeof(bool) && input.vec_size == 1) { return MTLVertexFormatChar; } return MTLVertexFormatInvalid; } case ShaderType::kSignedByte: { - if (input.bit_width == sizeof(char)) { + if (input.bit_width == 8 * sizeof(char)) { switch (input.vec_size) { case 1: return MTLVertexFormatChar; @@ -75,7 +75,7 @@ static MTLVertexFormat ReadStageInputFormat(const ShaderStageInput& input) { return MTLVertexFormatInvalid; } case ShaderType::kUnsignedByte: { - if (input.bit_width == sizeof(char)) { + if (input.bit_width == 8 * sizeof(char)) { switch (input.vec_size) { case 1: return MTLVertexFormatUChar; @@ -90,7 +90,7 @@ static MTLVertexFormat ReadStageInputFormat(const ShaderStageInput& input) { return MTLVertexFormatInvalid; } case ShaderType::kSignedShort: { - if (input.bit_width == sizeof(short)) { + if (input.bit_width == 8 * sizeof(short)) { switch (input.vec_size) { case 1: return MTLVertexFormatShort; @@ -105,7 +105,7 @@ static MTLVertexFormat ReadStageInputFormat(const ShaderStageInput& input) { return MTLVertexFormatInvalid; } case ShaderType::kUnsignedShort: { - if (input.bit_width == sizeof(ushort)) { + if (input.bit_width == 8 * sizeof(ushort)) { switch (input.vec_size) { case 1: return MTLVertexFormatUShort; @@ -120,7 +120,7 @@ static MTLVertexFormat ReadStageInputFormat(const ShaderStageInput& input) { return MTLVertexFormatInvalid; } case ShaderType::kSignedInt: { - if (input.bit_width == sizeof(int32_t)) { + if (input.bit_width == 8 * sizeof(int32_t)) { switch (input.vec_size) { case 1: return MTLVertexFormatInt; @@ -135,7 +135,7 @@ static MTLVertexFormat ReadStageInputFormat(const ShaderStageInput& input) { return MTLVertexFormatInvalid; } case ShaderType::kUnsignedInt: { - if (input.bit_width == sizeof(uint32_t)) { + if (input.bit_width == 8 * sizeof(uint32_t)) { switch (input.vec_size) { case 1: return MTLVertexFormatUInt; @@ -168,8 +168,9 @@ static MTLVertexFormat ReadStageInputFormat(const ShaderStageInput& input) { } } -bool VertexDescriptor::SetStageInputs(const ShaderStageInput* stage_inputs[], - size_t count) { +bool VertexDescriptor::SetStageInputs( + const ShaderStageInput* const stage_inputs[], + size_t count) { stage_inputs_.clear(); for (size_t i = 0; i < count; i++) { @@ -179,7 +180,8 @@ static MTLVertexFormat ReadStageInputFormat(const ShaderStageInput& input) { FML_LOG(ERROR) << "Format for input " << input->name << " not supported."; return false; } - stage_inputs_.emplace_back(StageInput{input->location, vertex_format}); + stage_inputs_.emplace_back( + StageInput{input->location, vertex_format, input->bit_width / 8}); } return true; @@ -188,13 +190,21 @@ static MTLVertexFormat ReadStageInputFormat(const ShaderStageInput& input) { MTLVertexDescriptor* VertexDescriptor::GetMTLVertexDescriptor() const { auto descriptor = [MTLVertexDescriptor vertexDescriptor]; + size_t stride = 0u; for (const auto& input : stage_inputs_) { auto attrib = descriptor.attributes[input.location]; attrib.format = input.format; - attrib.offset = 0u; + attrib.offset = stride; + // All vertex inputs are interleaved and tightly packed in one buffer at + // zero index. attrib.bufferIndex = 0u; + stride += input.stride; } + descriptor.layouts[0].stride = stride; + descriptor.layouts[0].stepRate = 1u; + descriptor.layouts[0].stepFunction = MTLVertexStepFunctionPerVertex; + return descriptor; } diff --git a/impeller/impeller/primitives/box.h b/impeller/impeller/primitives/box.h index 08a079f08bcf7..32b27760067f0 100644 --- a/impeller/impeller/primitives/box.h +++ b/impeller/impeller/primitives/box.h @@ -6,6 +6,6 @@ namespace impeller { -void RenderBox(std::shared_ptr context); +bool RenderBox(std::shared_ptr context); } // namespace impeller diff --git a/impeller/impeller/primitives/box.mm b/impeller/impeller/primitives/box.mm index e3e3fa9d7b067..251b93a698ea7 100644 --- a/impeller/impeller/primitives/box.mm +++ b/impeller/impeller/primitives/box.mm @@ -4,23 +4,47 @@ #include "impeller/primitives/box.h" +#include + #include "box.frag.h" #include "box.vert.h" #include "impeller/compositor/pipeline_descriptor.h" #include "impeller/compositor/shader_library.h" +#include "impeller/compositor/vertex_descriptor.h" namespace impeller { -void RenderBox(std::shared_ptr context) { - auto fragment_function = context->GetShaderLibrary()->GetFunction( - shader::BoxFragmentInfo::kEntrypointName, ShaderStage::kFragment); - auto vertex_function = context->GetShaderLibrary()->GetFunction( - shader::BoxVertexInfo::kEntrypointName, ShaderStage::kVertex); +bool RenderBox(std::shared_ptr context) { + PipelineDescriptor desc; + desc.SetLabel(shader::BoxVertexInfo::kLabel); + + { + auto fragment_function = context->GetShaderLibrary()->GetFunction( + shader::BoxFragmentInfo::kEntrypointName, ShaderStage::kFragment); + auto vertex_function = context->GetShaderLibrary()->GetFunction( + shader::BoxVertexInfo::kEntrypointName, ShaderStage::kVertex); + + desc.AddStageEntrypoint(vertex_function); + desc.AddStageEntrypoint(fragment_function); + } + + { + auto vertex_descriptor = std::make_shared(); + if (!vertex_descriptor->SetStageInputs( + shader::BoxVertexInfo::kAllShaderStageInputs->data(), + shader::BoxVertexInfo::kAllShaderStageInputs->size())) { + return false; + } + desc.SetVertexDescriptor(std::move(vertex_descriptor)); + } + + auto pipeline = + context->GetPipelineLibrary()->GetRenderPipeline(std::move(desc)).get(); + if (!pipeline) { + return false; + } - PipelineDescriptor builder; - builder.SetLabel(shader::BoxVertexInfo::kLabel); - builder.AddStageEntrypoint(vertex_function); - builder.AddStageEntrypoint(fragment_function); + return true; } } // namespace impeller diff --git a/impeller/impeller/primitives/box.vert b/impeller/impeller/primitives/box.vert index 0a9d4524abd7d..ce7ea2c38a9ee 100644 --- a/impeller/impeller/primitives/box.vert +++ b/impeller/impeller/primitives/box.vert @@ -2,35 +2,12 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -struct MyAwesomeStruct { - float mvp0; - mat4x2 mvp1; - mat4 mvp2; - mat4 mvp3; -}; - -uniform UniformBuffer1 { - MyAwesomeStruct s1; - MyAwesomeStruct s2; - MyAwesomeStruct s3; -} uniforms1; - -uniform UniformBuffer2 { - MyAwesomeStruct s11; - float hello12; - MyAwesomeStruct s13; -} uniforms2; +uniform UniformBuffer { + mat4 mvp; +} uniforms; in vec3 vertexPosition; -in vec3 vertexColor; -in dvec4 hello; -in dmat4x2 hello12; -in uvec4 hello2; - -out vec3 color; -void main() -{ - gl_Position = uniforms1.s1.mvp2 * uniforms2.s13.mvp3 * vec4(vertexPosition, 1.0); - color = vertexColor; +void main() { + gl_Position = uniforms.mvp * vec4(vertexPosition, 1.0); } diff --git a/impeller/impeller/shader_glue/shader_types.h b/impeller/impeller/shader_glue/shader_types.h index a280577540a17..b9c5233e0c995 100644 --- a/impeller/impeller/shader_glue/shader_types.h +++ b/impeller/impeller/shader_glue/shader_types.h @@ -4,6 +4,7 @@ #pragma once +#include #include #include From 9fe1c3fe9cea887b738108ac3958dfbcc41a53a5 Mon Sep 17 00:00:00 2001 From: Chinmay Garde Date: Tue, 25 May 2021 13:09:24 -0700 Subject: [PATCH 053/433] Dump shader outputs. --- impeller/compiler/code_gen_template.h | 35 ++++++++++++++++--- impeller/host/main.mm | 3 +- .../impeller/compositor/vertex_descriptor.h | 2 +- .../impeller/compositor/vertex_descriptor.mm | 6 ++-- impeller/impeller/primitives/box.frag | 3 +- impeller/impeller/primitives/box.mm | 4 +-- impeller/impeller/primitives/box.vert | 4 +++ impeller/impeller/shader_glue/shader_types.h | 2 +- 8 files changed, 44 insertions(+), 15 deletions(-) diff --git a/impeller/compiler/code_gen_template.h b/impeller/compiler/code_gen_template.h index 5948b2963121e..f958346a7df59 100644 --- a/impeller/compiler/code_gen_template.h +++ b/impeller/compiler/code_gen_template.h @@ -17,14 +17,19 @@ namespace impeller { namespace shader { struct {{camel_case(shader_name)}}{{camel_case(shader_stage)}}Info { + // =========================================================================== + // Stage Info ================================================================ + // =========================================================================== static constexpr std::string_view kLabel = "{{camel_case(shader_name)}}"; static constexpr std::string_view kEntrypointName = "{{entrypoint}}"; static constexpr ShaderStage kShaderStage = {{to_shader_stage(shader_stage)}}; - // Stage Inputs + // =========================================================================== + // Stage Inputs ============================================================== + // =========================================================================== {% for stage_input in stage_inputs %} - // Stage input {{stage_input.name}} - static constexpr auto kInput{{camel_case(stage_input.name)}} = ShaderStageInput { + + static constexpr auto kInput{{camel_case(stage_input.name)}} = ShaderStageIOSlot { // {{stage_input.name}} "{{stage_input.name}}", // name {{stage_input.location}}u, // attribute location {{stage_input.type.type_name}}, // type @@ -34,9 +39,29 @@ struct {{camel_case(shader_name)}}{{camel_case(shader_stage)}}Info { }; {% endfor %} - static constexpr std::array kAllShaderStageInputs[] = { + static constexpr std::array kAllShaderStageInputs = { {% for stage_input in stage_inputs %} - { &kInput{{camel_case(stage_input.name)}} }, // {{stage_input.name}} + &kInput{{camel_case(stage_input.name)}}, // {{stage_input.name}} +{% endfor %} + }; + + // =========================================================================== + // Stage Outputs ============================================================= + // =========================================================================== +{% for stage_output in stage_outputs %} + static constexpr auto kOutput{{camel_case(stage_output.name)}} = ShaderStageIOSlot { // {{stage_output.name}} + "{{stage_output.name}}", // name + {{stage_output.location}}u, // attribute location + {{stage_output.type.type_name}}, // type + {{stage_output.type.bit_width}}u, // bit width of type + {{stage_output.type.vec_size}}u, // vec size + {{stage_output.type.columns}}u // number of columns + }; +{% endfor %} + + static constexpr std::array kAllShaderStageOutputs = { +{% for stage_output in stage_outputs %} + &kOutput{{camel_case(stage_output.name)}}, // {{stage_output.name}} {% endfor %} }; diff --git a/impeller/host/main.mm b/impeller/host/main.mm index c16c53b72ad8a..db234115d801b 100644 --- a/impeller/host/main.mm +++ b/impeller/host/main.mm @@ -29,8 +29,7 @@ - (id)init { return self; } -- (void)applicationWillFinishLaunching:(NSNotification*)notification { - [window_ makeMainWindow]; +- (void)applicationDidFinishLaunching:(NSNotification*)notification { [window_ setTitle:@"Impeller Host"]; [window_ makeKeyAndOrderFront:self]; } diff --git a/impeller/impeller/compositor/vertex_descriptor.h b/impeller/impeller/compositor/vertex_descriptor.h index ae785c58257b6..43f8ba9ed1f3c 100644 --- a/impeller/impeller/compositor/vertex_descriptor.h +++ b/impeller/impeller/compositor/vertex_descriptor.h @@ -20,7 +20,7 @@ class VertexDescriptor final : public Comparable { virtual ~VertexDescriptor(); - bool SetStageInputs(const ShaderStageInput* const stage_inputs[], + bool SetStageInputs(const ShaderStageIOSlot* const stage_inputs[], size_t count); MTLVertexDescriptor* GetMTLVertexDescriptor() const; diff --git a/impeller/impeller/compositor/vertex_descriptor.mm b/impeller/impeller/compositor/vertex_descriptor.mm index 448a3b19a3621..e592c4f865efd 100644 --- a/impeller/impeller/compositor/vertex_descriptor.mm +++ b/impeller/impeller/compositor/vertex_descriptor.mm @@ -12,7 +12,7 @@ VertexDescriptor::~VertexDescriptor() = default; -static MTLVertexFormat ReadStageInputFormat(const ShaderStageInput& input) { +static MTLVertexFormat ReadStageInputFormat(const ShaderStageIOSlot& input) { if (input.columns != 1) { // All matrix types are unsupported as vertex inputs. return MTLVertexFormatInvalid; @@ -169,12 +169,12 @@ static MTLVertexFormat ReadStageInputFormat(const ShaderStageInput& input) { } bool VertexDescriptor::SetStageInputs( - const ShaderStageInput* const stage_inputs[], + const ShaderStageIOSlot* const stage_inputs[], size_t count) { stage_inputs_.clear(); for (size_t i = 0; i < count; i++) { - const ShaderStageInput* input = stage_inputs[i]; + const ShaderStageIOSlot* input = stage_inputs[i]; auto vertex_format = ReadStageInputFormat(*input); if (vertex_format == MTLVertexFormatInvalid) { FML_LOG(ERROR) << "Format for input " << input->name << " not supported."; diff --git a/impeller/impeller/primitives/box.frag b/impeller/impeller/primitives/box.frag index 2c45c349d1799..8f1aad5edf8aa 100644 --- a/impeller/impeller/primitives/box.frag +++ b/impeller/impeller/primitives/box.frag @@ -3,9 +3,10 @@ // found in the LICENSE file. in vec3 color; +in vec3 color2; out vec3 fragColor; void main() { - fragColor = color; + fragColor = color * color2; } diff --git a/impeller/impeller/primitives/box.mm b/impeller/impeller/primitives/box.mm index 251b93a698ea7..02b832aab8464 100644 --- a/impeller/impeller/primitives/box.mm +++ b/impeller/impeller/primitives/box.mm @@ -31,8 +31,8 @@ bool RenderBox(std::shared_ptr context) { { auto vertex_descriptor = std::make_shared(); if (!vertex_descriptor->SetStageInputs( - shader::BoxVertexInfo::kAllShaderStageInputs->data(), - shader::BoxVertexInfo::kAllShaderStageInputs->size())) { + shader::BoxVertexInfo::kAllShaderStageInputs.data(), + shader::BoxVertexInfo::kAllShaderStageInputs.size())) { return false; } desc.SetVertexDescriptor(std::move(vertex_descriptor)); diff --git a/impeller/impeller/primitives/box.vert b/impeller/impeller/primitives/box.vert index ce7ea2c38a9ee..6fa5eb17d074d 100644 --- a/impeller/impeller/primitives/box.vert +++ b/impeller/impeller/primitives/box.vert @@ -8,6 +8,10 @@ uniform UniformBuffer { in vec3 vertexPosition; +out vec3 color; +out vec3 color2; + void main() { gl_Position = uniforms.mvp * vec4(vertexPosition, 1.0); + color = vec3(1.0, 1.0, 1.0); } diff --git a/impeller/impeller/shader_glue/shader_types.h b/impeller/impeller/shader_glue/shader_types.h index b9c5233e0c995..a33fe4175663b 100644 --- a/impeller/impeller/shader_glue/shader_types.h +++ b/impeller/impeller/shader_glue/shader_types.h @@ -38,7 +38,7 @@ enum class ShaderType { kSampler, }; -struct ShaderStageInput { +struct ShaderStageIOSlot { const char* name; size_t location; ShaderType type; From 8caa5e4b000b946cbf7c9790eecd253aafcecb35 Mon Sep 17 00:00:00 2001 From: Chinmay Garde Date: Tue, 25 May 2021 14:51:27 -0700 Subject: [PATCH 054/433] Cleanup vertex descriptor API. --- impeller/impeller/compositor/vertex_descriptor.h | 6 ++++++ impeller/impeller/primitives/box.mm | 6 ++++-- 2 files changed, 10 insertions(+), 2 deletions(-) diff --git a/impeller/impeller/compositor/vertex_descriptor.h b/impeller/impeller/compositor/vertex_descriptor.h index 43f8ba9ed1f3c..8c2c0bb938f88 100644 --- a/impeller/impeller/compositor/vertex_descriptor.h +++ b/impeller/impeller/compositor/vertex_descriptor.h @@ -20,6 +20,12 @@ class VertexDescriptor final : public Comparable { virtual ~VertexDescriptor(); + template + bool SetStageInputs( + const std::array& inputs) { + return SetStageInputs(inputs.data(), inputs.size()); + } + bool SetStageInputs(const ShaderStageIOSlot* const stage_inputs[], size_t count); diff --git a/impeller/impeller/primitives/box.mm b/impeller/impeller/primitives/box.mm index 02b832aab8464..f303313df5993 100644 --- a/impeller/impeller/primitives/box.mm +++ b/impeller/impeller/primitives/box.mm @@ -8,6 +8,7 @@ #include "box.frag.h" #include "box.vert.h" +#include "flutter/fml/logging.h" #include "impeller/compositor/pipeline_descriptor.h" #include "impeller/compositor/shader_library.h" #include "impeller/compositor/vertex_descriptor.h" @@ -31,8 +32,8 @@ bool RenderBox(std::shared_ptr context) { { auto vertex_descriptor = std::make_shared(); if (!vertex_descriptor->SetStageInputs( - shader::BoxVertexInfo::kAllShaderStageInputs.data(), - shader::BoxVertexInfo::kAllShaderStageInputs.size())) { + shader::BoxVertexInfo::kAllShaderStageInputs)) { + FML_LOG(ERROR) << "Could not configure vertex descriptor."; return false; } desc.SetVertexDescriptor(std::move(vertex_descriptor)); @@ -41,6 +42,7 @@ bool RenderBox(std::shared_ptr context) { auto pipeline = context->GetPipelineLibrary()->GetRenderPipeline(std::move(desc)).get(); if (!pipeline) { + FML_LOG(ERROR) << "Could not create the render pipeline."; return false; } From 06e1188fa4a97386576e38b4692b01fda2ebcfaf Mon Sep 17 00:00:00 2001 From: Chinmay Garde Date: Wed, 26 May 2021 12:19:09 -0700 Subject: [PATCH 055/433] Wire up color attachment descriptors. --- impeller/impeller/compositor/BUILD.gn | 8 ++ impeller/impeller/compositor/allocator.h | 44 ++++++++ impeller/impeller/compositor/allocator.mm | 37 +++++++ impeller/impeller/compositor/buffer.h | 32 ++++++ impeller/impeller/compositor/buffer.mm | 17 +++ impeller/impeller/compositor/formats.cc | 11 ++ impeller/impeller/compositor/formats.h | 104 ++++++++++++++++++ impeller/impeller/compositor/formats_metal.h | 103 +++++++++++++++++ impeller/impeller/compositor/formats_metal.mm | 33 ++++++ .../impeller/compositor/pipeline_descriptor.h | 7 ++ .../compositor/pipeline_descriptor.mm | 20 +++- 11 files changed, 415 insertions(+), 1 deletion(-) create mode 100644 impeller/impeller/compositor/allocator.h create mode 100644 impeller/impeller/compositor/allocator.mm create mode 100644 impeller/impeller/compositor/buffer.h create mode 100644 impeller/impeller/compositor/buffer.mm create mode 100644 impeller/impeller/compositor/formats.cc create mode 100644 impeller/impeller/compositor/formats.h create mode 100644 impeller/impeller/compositor/formats_metal.h create mode 100644 impeller/impeller/compositor/formats_metal.mm diff --git a/impeller/impeller/compositor/BUILD.gn b/impeller/impeller/compositor/BUILD.gn index a23f1db99e4db..f725371a42011 100644 --- a/impeller/impeller/compositor/BUILD.gn +++ b/impeller/impeller/compositor/BUILD.gn @@ -6,10 +6,18 @@ import("//flutter/impeller/tools/impeller.gni") impeller_component("compositor") { sources = [ + "allocator.h", + "allocator.mm", + "buffer.h", + "buffer.mm", "comparable.cc", "comparable.h", "context.h", "context.mm", + "formats.cc", + "formats.h", + "formats_metal.h", + "formats_metal.mm", "pipeline.h", "pipeline.mm", "pipeline_descriptor.h", diff --git a/impeller/impeller/compositor/allocator.h b/impeller/impeller/compositor/allocator.h new file mode 100644 index 0000000000000..8ad4913152c61 --- /dev/null +++ b/impeller/impeller/compositor/allocator.h @@ -0,0 +1,44 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#pragma once + +#include + +#include + +#include "flutter/fml/macros.h" + +namespace impeller { + +enum class StorageMode { + kHostCoherent, + kDevicePrivate, +}; + +class Context; +class Buffer; + +class Allocator { + public: + ~Allocator(); + + std::shared_ptr CreateBuffer(size_t length, std::string label); + + private: + friend class Context; + + // In the prototype, we are going to be allocating resources directly with the + // MTLDevice APIs. But, in the future, this could be backed by named heaps + // with specific limits. + id device_; + StorageMode mode_; + std::string allocator_label_; + + Allocator(id device, StorageMode type, std::string label); + + FML_DISALLOW_COPY_AND_ASSIGN(Allocator); +}; + +} // namespace impeller diff --git a/impeller/impeller/compositor/allocator.mm b/impeller/impeller/compositor/allocator.mm new file mode 100644 index 0000000000000..496160ef08143 --- /dev/null +++ b/impeller/impeller/compositor/allocator.mm @@ -0,0 +1,37 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "impeller/compositor/allocator.h" + +#include "impeller/compositor/buffer.h" + +namespace impeller { + +Allocator::Allocator(id device, StorageMode type, std::string label) + : device_(device), mode_(type), allocator_label_(std::move(label)) {} + +Allocator::~Allocator() = default; + +static MTLResourceOptions ResourceOptionsFromStorageType(StorageMode type) { + switch (type) { + case StorageMode::kHostCoherent: + return MTLResourceStorageModeManaged; + case StorageMode::kDevicePrivate: + return MTLResourceStorageModePrivate; + } +} + +std::shared_ptr Allocator::CreateBuffer(size_t length, + std::string label) { + auto buffer = + [device_ newBufferWithLength:length + options:ResourceOptionsFromStorageType(mode_)]; + if (!buffer) { + return nullptr; + } + buffer.label = @(label.c_str()); + return std::shared_ptr(new Buffer(buffer, length, mode_, label)); +} + +} // namespace impeller diff --git a/impeller/impeller/compositor/buffer.h b/impeller/impeller/compositor/buffer.h new file mode 100644 index 0000000000000..ecaeeb32e3e4c --- /dev/null +++ b/impeller/impeller/compositor/buffer.h @@ -0,0 +1,32 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#pragma once + +#include "flutter/fml/macros.h" +#include "impeller/compositor/allocator.h" + +namespace impeller { + +class Buffer { + public: + ~Buffer(); + + private: + friend class Allocator; + + const id buffer_; + const size_t size_; + const StorageMode mode_; + const std::string label_; + + Buffer(id buffer, + size_t size, + StorageMode mode, + std::string label); + + FML_DISALLOW_COPY_AND_ASSIGN(Buffer); +}; + +} // namespace impeller diff --git a/impeller/impeller/compositor/buffer.mm b/impeller/impeller/compositor/buffer.mm new file mode 100644 index 0000000000000..01c5fed7fe1ac --- /dev/null +++ b/impeller/impeller/compositor/buffer.mm @@ -0,0 +1,17 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "impeller/compositor/buffer.h" + +namespace impeller { + +Buffer::Buffer(id buffer, + size_t size, + StorageMode mode, + std::string label) + : buffer_(buffer), size_(size), mode_(mode), label_(std::move(label)) {} + +Buffer::~Buffer() = default; + +} // namespace impeller diff --git a/impeller/impeller/compositor/formats.cc b/impeller/impeller/compositor/formats.cc new file mode 100644 index 0000000000000..c992bc4abc566 --- /dev/null +++ b/impeller/impeller/compositor/formats.cc @@ -0,0 +1,11 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "impeller/compositor/formats.h" + +namespace impeller { + +// + +} // namespace impeller diff --git a/impeller/impeller/compositor/formats.h b/impeller/impeller/compositor/formats.h new file mode 100644 index 0000000000000..a91076610f16e --- /dev/null +++ b/impeller/impeller/compositor/formats.h @@ -0,0 +1,104 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#pragma once + +#include +#include + +#include "flutter/fml/hash_combine.h" +#include "flutter/fml/macros.h" + +namespace impeller { + +enum class PixelFormat { + kUnknown, +}; + +enum class BlendFactor { + kZero, + kOne, + kSourceColor, + kOneMinusSourceColor, + kSourceAlpha, + kOneMinusSourceAlpha, + kDestinationColor, + kOneMinusDestinationColor, + kDestinationAlpha, + kOneMinusDestinationAlpha, + kSourceAlphaSaturated, + kBlendColor, + kOneMinusBlendColor, + kBlendAlpha, + kOneMinusBlendAlpha, +}; + +enum class BlendOperation { + kAdd, + kSubtract, + kReverseSubtract, + kMin, + kMax, +}; + +enum class ColorWriteMask : uint64_t { + kNone = 0, + kRed = 1 << 0, + kGreen = 1 << 1, + kBlue = 1 << 2, + kAlpha = 1 << 3, + kAll = kRed | kGreen | kBlue, +}; + +struct ColorAttachmentDescriptor { + PixelFormat format = PixelFormat::kUnknown; + bool blending_enabled = false; + + //---------------------------------------------------------------------------- + /// Blending at specific color attachments follows the pseudocode: + /// ``` + /// if (blending_enabled) { + /// final_color.rgb = (src_color_blend_factor * new_color.rgb) + /// + /// (dst_color_blend_factor * old_color.rgb); + /// final_color.a = (src_alpha_blend_factor * new_color.a) + /// + /// (dst_alpha_blend_factor * old_color.a); + /// } else { + /// final_color = new_color; + /// } + /// final_color = final_color & write_mask; + /// ``` + + BlendFactor src_color_blend_factor; + BlendOperation color_blend_op; + BlendFactor dst_color_blend_factor; + + BlendFactor src_alpha_blend_factor; + BlendOperation alpha_blend_op; + BlendFactor dst_alpha_blend_factor; + + std::underlying_type_t write_mask; + + constexpr bool operator==(const ColorAttachmentDescriptor& o) const { + return format == o.format && // + blending_enabled == o.blending_enabled && // + src_color_blend_factor == o.src_color_blend_factor && // + color_blend_op == o.color_blend_op && // + dst_color_blend_factor == o.dst_color_blend_factor && // + src_alpha_blend_factor == o.src_alpha_blend_factor && // + alpha_blend_op == o.alpha_blend_op && // + dst_alpha_blend_factor == o.dst_alpha_blend_factor && // + write_mask == o.write_mask; + } + + constexpr size_t Hash() const { + return fml::HashCombine(format, blending_enabled, src_color_blend_factor, + color_blend_op, dst_color_blend_factor, + src_alpha_blend_factor, alpha_blend_op, + dst_alpha_blend_factor, write_mask); + } +}; + +} // namespace impeller diff --git a/impeller/impeller/compositor/formats_metal.h b/impeller/impeller/compositor/formats_metal.h new file mode 100644 index 0000000000000..1a3dfa039bbee --- /dev/null +++ b/impeller/impeller/compositor/formats_metal.h @@ -0,0 +1,103 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#pragma once + +#include + +#include "flutter/fml/macros.h" +#include "impeller/compositor/formats.h" + +namespace impeller { + +constexpr MTLPixelFormat ToMTLPixelFormat(PixelFormat format) { + switch (format) { + case PixelFormat::kUnknown: + return MTLPixelFormatInvalid; + } + return MTLPixelFormatInvalid; +}; + +constexpr MTLBlendFactor ToMTLBlendFactor(BlendFactor type) { + switch (type) { + case BlendFactor::kZero: + return MTLBlendFactorZero; + case BlendFactor::kOne: + return MTLBlendFactorOne; + case BlendFactor::kSourceColor: + return MTLBlendFactorSourceColor; + case BlendFactor::kOneMinusSourceColor: + return MTLBlendFactorOneMinusSourceColor; + case BlendFactor::kSourceAlpha: + return MTLBlendFactorSourceAlpha; + case BlendFactor::kOneMinusSourceAlpha: + return MTLBlendFactorOneMinusSourceAlpha; + case BlendFactor::kDestinationColor: + return MTLBlendFactorDestinationColor; + case BlendFactor::kOneMinusDestinationColor: + return MTLBlendFactorOneMinusDestinationColor; + case BlendFactor::kDestinationAlpha: + return MTLBlendFactorDestinationAlpha; + case BlendFactor::kOneMinusDestinationAlpha: + return MTLBlendFactorOneMinusDestinationAlpha; + case BlendFactor::kSourceAlphaSaturated: + return MTLBlendFactorSourceAlphaSaturated; + case BlendFactor::kBlendColor: + return MTLBlendFactorBlendColor; + case BlendFactor::kOneMinusBlendColor: + return MTLBlendFactorOneMinusBlendColor; + case BlendFactor::kBlendAlpha: + return MTLBlendFactorBlendAlpha; + case BlendFactor::kOneMinusBlendAlpha: + return MTLBlendFactorOneMinusBlendAlpha; + } + return MTLBlendFactorZero; +}; + +constexpr MTLBlendOperation ToMTLBlendOperation(BlendOperation type) { + switch (type) { + case BlendOperation::kAdd: + return MTLBlendOperationAdd; + case BlendOperation::kSubtract: + return MTLBlendOperationSubtract; + case BlendOperation::kReverseSubtract: + return MTLBlendOperationReverseSubtract; + case BlendOperation::kMin: + return MTLBlendOperationMin; + case BlendOperation::kMax: + return MTLBlendOperationMax; + } + return MTLBlendOperationAdd; +}; + +constexpr MTLColorWriteMask ToMTLColorWriteMask( + std::underlying_type_t type) { + using UnderlyingType = decltype(type); + + MTLColorWriteMask mask = MTLColorWriteMaskNone; + + if (type & static_cast(ColorWriteMask::kRed)) { + mask |= MTLColorWriteMaskRed; + } + + if (type & static_cast(ColorWriteMask::kGreen)) { + mask |= MTLColorWriteMaskGreen; + } + + if (type & static_cast(ColorWriteMask::kBlue)) { + mask |= MTLColorWriteMaskBlue; + } + + if (type & static_cast(ColorWriteMask::kAlpha)) { + mask |= MTLColorWriteMaskAlpha; + } + + return mask; +}; + +MTLRenderPipelineColorAttachmentDescriptor* +ToMTLRenderPipelineColorAttachmentDescriptor( + ColorAttachmentDescriptor descriptor); + +} // namespace impeller diff --git a/impeller/impeller/compositor/formats_metal.mm b/impeller/impeller/compositor/formats_metal.mm new file mode 100644 index 0000000000000..6efd3115b4e23 --- /dev/null +++ b/impeller/impeller/compositor/formats_metal.mm @@ -0,0 +1,33 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "impeller/compositor/formats_metal.h" + +namespace impeller { + +MTLRenderPipelineColorAttachmentDescriptor* +ToMTLRenderPipelineColorAttachmentDescriptor( + ColorAttachmentDescriptor descriptor) { + auto des = [[MTLRenderPipelineColorAttachmentDescriptor alloc] init]; + des.pixelFormat = ToMTLPixelFormat(descriptor.format); + + des.blendingEnabled = descriptor.blending_enabled; + + des.sourceRGBBlendFactor = + ToMTLBlendFactor(descriptor.src_color_blend_factor); + des.rgbBlendOperation = ToMTLBlendOperation(descriptor.color_blend_op); + des.destinationRGBBlendFactor = + ToMTLBlendFactor(descriptor.dst_color_blend_factor); + + des.sourceAlphaBlendFactor = + ToMTLBlendFactor(descriptor.src_alpha_blend_factor); + des.alphaBlendOperation = ToMTLBlendOperation(descriptor.alpha_blend_op); + des.destinationAlphaBlendFactor = + ToMTLBlendFactor(descriptor.dst_alpha_blend_factor); + + des.writeMask = ToMTLColorWriteMask(descriptor.write_mask); + return des; +} + +} // namespace impeller diff --git a/impeller/impeller/compositor/pipeline_descriptor.h b/impeller/impeller/compositor/pipeline_descriptor.h index 0370aee54e7fd..8cddcc00651cb 100644 --- a/impeller/impeller/compositor/pipeline_descriptor.h +++ b/impeller/impeller/compositor/pipeline_descriptor.h @@ -13,8 +13,10 @@ #include #include +#include "flutter/fml/hash_combine.h" #include "flutter/fml/macros.h" #include "impeller/compositor/comparable.h" +#include "impeller/compositor/formats.h" #include "impeller/shader_glue/shader_types.h" namespace impeller { @@ -38,6 +40,10 @@ class PipelineDescriptor : public Comparable { PipelineDescriptor& SetVertexDescriptor( std::shared_ptr vertex_descriptor); + PipelineDescriptor& SetColorAttachmentDescriptor( + size_t index, + ColorAttachmentDescriptor desc); + MTLRenderPipelineDescriptor* GetMTLRenderPipelineDescriptor() const; // Comparable @@ -50,6 +56,7 @@ class PipelineDescriptor : public Comparable { std::string label_; size_t sample_count_ = 1; std::map> entrypoints_; + std::map color_attachment_descriptors_; std::shared_ptr vertex_descriptor_; }; diff --git a/impeller/impeller/compositor/pipeline_descriptor.mm b/impeller/impeller/compositor/pipeline_descriptor.mm index 7cb9098f6c765..b133df4a72410 100644 --- a/impeller/impeller/compositor/pipeline_descriptor.mm +++ b/impeller/impeller/compositor/pipeline_descriptor.mm @@ -4,6 +4,7 @@ #include "impeller/compositor/pipeline_descriptor.h" +#include "impeller/compositor/formats_metal.h" #include "impeller/compositor/shader_library.h" #include "impeller/compositor/vertex_descriptor.h" @@ -27,6 +28,10 @@ if (vertex_descriptor_) { fml::HashCombineSeed(seed, vertex_descriptor_->GetHash()); } + for (const auto& des : color_attachment_descriptors_) { + fml::HashCombineSeed(seed, des.first); + fml::HashCombineSeed(seed, des.second.Hash()); + } return seed; } @@ -34,7 +39,8 @@ bool PipelineDescriptor::IsEqual(const PipelineDescriptor& other) const { return label_ == other.label_ && sample_count_ == other.sample_count_ && DeepCompareMap(entrypoints_, other.entrypoints_) && - DeepComparePointer(vertex_descriptor_, other.vertex_descriptor_); + DeepComparePointer(vertex_descriptor_, other.vertex_descriptor_) && + color_attachment_descriptors_ == other.color_attachment_descriptors_; } PipelineDescriptor& PipelineDescriptor::SetLabel( @@ -69,6 +75,13 @@ return *this; } +PipelineDescriptor& PipelineDescriptor::SetColorAttachmentDescriptor( + size_t index, + ColorAttachmentDescriptor desc) { + color_attachment_descriptors_[index] = std::move(desc); + return *this; +} + MTLRenderPipelineDescriptor* PipelineDescriptor::GetMTLRenderPipelineDescriptor() const { auto descriptor = [[MTLRenderPipelineDescriptor alloc] init]; @@ -88,6 +101,11 @@ descriptor.vertexDescriptor = vertex_descriptor_->GetMTLVertexDescriptor(); } + for (const auto& item : color_attachment_descriptors_) { + descriptor.colorAttachments[item.first] = + ToMTLRenderPipelineColorAttachmentDescriptor(item.second); + } + return descriptor; } From 2c2c057c081d2aa11e50a7760b5f1f258cc8b69a Mon Sep 17 00:00:00 2001 From: Chinmay Garde Date: Wed, 26 May 2021 12:40:55 -0700 Subject: [PATCH 056/433] Patch depth stencil pixel format. --- .../impeller/compositor/pipeline_descriptor.h | 3 +++ .../compositor/pipeline_descriptor.mm | 21 +++++++++++++++---- 2 files changed, 20 insertions(+), 4 deletions(-) diff --git a/impeller/impeller/compositor/pipeline_descriptor.h b/impeller/impeller/compositor/pipeline_descriptor.h index 8cddcc00651cb..e565f231997b6 100644 --- a/impeller/impeller/compositor/pipeline_descriptor.h +++ b/impeller/impeller/compositor/pipeline_descriptor.h @@ -44,6 +44,8 @@ class PipelineDescriptor : public Comparable { size_t index, ColorAttachmentDescriptor desc); + PipelineDescriptor& SetDepthStencilPixelFormat(PixelFormat format); + MTLRenderPipelineDescriptor* GetMTLRenderPipelineDescriptor() const; // Comparable @@ -58,6 +60,7 @@ class PipelineDescriptor : public Comparable { std::map> entrypoints_; std::map color_attachment_descriptors_; std::shared_ptr vertex_descriptor_; + PixelFormat depth_stencil_pixel_format_ = PixelFormat::kUnknown; }; } // namespace impeller diff --git a/impeller/impeller/compositor/pipeline_descriptor.mm b/impeller/impeller/compositor/pipeline_descriptor.mm index b133df4a72410..b983f523bcb7e 100644 --- a/impeller/impeller/compositor/pipeline_descriptor.mm +++ b/impeller/impeller/compositor/pipeline_descriptor.mm @@ -25,13 +25,14 @@ fml::HashCombineSeed(seed, second->GetHash()); } } - if (vertex_descriptor_) { - fml::HashCombineSeed(seed, vertex_descriptor_->GetHash()); - } for (const auto& des : color_attachment_descriptors_) { fml::HashCombineSeed(seed, des.first); fml::HashCombineSeed(seed, des.second.Hash()); } + if (vertex_descriptor_) { + fml::HashCombineSeed(seed, vertex_descriptor_->GetHash()); + } + fml::HashCombineSeed(seed, depth_stencil_pixel_format_); return seed; } @@ -39,8 +40,9 @@ bool PipelineDescriptor::IsEqual(const PipelineDescriptor& other) const { return label_ == other.label_ && sample_count_ == other.sample_count_ && DeepCompareMap(entrypoints_, other.entrypoints_) && + color_attachment_descriptors_ == other.color_attachment_descriptors_ && DeepComparePointer(vertex_descriptor_, other.vertex_descriptor_) && - color_attachment_descriptors_ == other.color_attachment_descriptors_; + depth_stencil_pixel_format_ == other.depth_stencil_pixel_format_; } PipelineDescriptor& PipelineDescriptor::SetLabel( @@ -82,6 +84,12 @@ return *this; } +PipelineDescriptor& PipelineDescriptor::SetDepthStencilPixelFormat( + PixelFormat format) { + depth_stencil_pixel_format_ = format; + return *this; +} + MTLRenderPipelineDescriptor* PipelineDescriptor::GetMTLRenderPipelineDescriptor() const { auto descriptor = [[MTLRenderPipelineDescriptor alloc] init]; @@ -106,6 +114,11 @@ ToMTLRenderPipelineColorAttachmentDescriptor(item.second); } + descriptor.depthAttachmentPixelFormat = + ToMTLPixelFormat(depth_stencil_pixel_format_); + descriptor.stencilAttachmentPixelFormat = + ToMTLPixelFormat(depth_stencil_pixel_format_); + return descriptor; } From 6ca7c8db1d18faccf0b393c19784822c05818eec Mon Sep 17 00:00:00 2001 From: Chinmay Garde Date: Wed, 26 May 2021 19:05:19 -0700 Subject: [PATCH 057/433] Setup depth stencil state. --- impeller/impeller/compositor/formats.h | 108 ++++++++++++++++++ impeller/impeller/compositor/formats_metal.h | 51 +++++++++ impeller/impeller/compositor/formats_metal.mm | 43 +++++++ impeller/impeller/compositor/pipeline.h | 4 +- impeller/impeller/compositor/pipeline.mm | 4 +- .../impeller/compositor/pipeline_descriptor.h | 18 +++ .../compositor/pipeline_descriptor.mm | 39 ++++++- .../impeller/compositor/pipeline_library.mm | 5 +- 8 files changed, 267 insertions(+), 5 deletions(-) diff --git a/impeller/impeller/compositor/formats.h b/impeller/impeller/compositor/formats.h index a91076610f16e..fd52c4a325cf5 100644 --- a/impeller/impeller/compositor/formats.h +++ b/impeller/impeller/compositor/formats.h @@ -5,6 +5,7 @@ #pragma once #include +#include #include #include "flutter/fml/hash_combine.h" @@ -101,4 +102,111 @@ struct ColorAttachmentDescriptor { } }; +enum class CompareFunction { + kNever, + kLess, + kEqual, + kLessEqual, + kGreater, + kNotEqual, + kGreaterEqual, + kAlways, +}; + +enum class StencilOperation { + kKeep, + kZero, + kReplace, + kIncrementClamp, + kDecrementClamp, + kInvert, + kIncrementWrap, + kDecrementWrap, +}; + +struct DepthAttachmentDescriptor { + //---------------------------------------------------------------------------- + /// Indicates how to compare the value with that in the depth buffer. + /// + CompareFunction depth_compare; + //---------------------------------------------------------------------------- + /// Indicates when writes must be performed to the depth buffer. + /// + bool depth_write_enabled; + + constexpr bool operator==(const DepthAttachmentDescriptor& o) const { + return depth_compare == o.depth_compare && + depth_write_enabled == o.depth_write_enabled; + } + + constexpr size_t GetHash() const { + return fml::HashCombine(depth_compare, depth_write_enabled); + } +}; + +struct StencilAttachmentDescriptor { + //---------------------------------------------------------------------------- + /// Indicates the operation to perform between the reference value and the + /// value in the stencil buffer. Both values have the read_mask applied to + /// them before performing this operation. + /// + CompareFunction stencil_compare; + //---------------------------------------------------------------------------- + /// Indicates what to do when the stencil test has failed. + /// + StencilOperation stencil_failure; + //---------------------------------------------------------------------------- + /// Indicates what to do when the stencil test passes but the depth test + /// fails. + /// + StencilOperation depth_failure; + //---------------------------------------------------------------------------- + /// Indicates what to do when both the stencil and depth tests pass. + /// + StencilOperation depth_stencil_pass; + //---------------------------------------------------------------------------- + /// The mask applied to the reference and stencil buffer values before + /// performing the stencil_compare operation. + /// + uint32_t read_mask; + //---------------------------------------------------------------------------- + /// The mask applied to the new stencil value before it is written into the + /// stencil buffer. + /// + uint32_t write_mask; + + constexpr bool operator==(const StencilAttachmentDescriptor& o) const { + return stencil_compare == o.stencil_compare && + stencil_failure == o.stencil_failure && + depth_failure == o.depth_failure && + depth_stencil_pass == o.depth_stencil_pass && + read_mask == o.read_mask && write_mask == o.write_mask; + } + + constexpr size_t GetHash() const { + return fml::HashCombine(stencil_compare, stencil_failure, depth_failure, + depth_stencil_pass, read_mask); + } +}; + } // namespace impeller + +namespace std { + +template <> +struct hash { + constexpr std::size_t operator()( + const impeller::DepthAttachmentDescriptor& des) const { + return des.GetHash(); + } +}; + +template <> +struct hash { + constexpr std::size_t operator()( + const impeller::StencilAttachmentDescriptor& des) const { + return des.GetHash(); + } +}; + +} // namespace std diff --git a/impeller/impeller/compositor/formats_metal.h b/impeller/impeller/compositor/formats_metal.h index 1a3dfa039bbee..de69023df3675 100644 --- a/impeller/impeller/compositor/formats_metal.h +++ b/impeller/impeller/compositor/formats_metal.h @@ -4,6 +4,8 @@ #pragma once +#include + #include #include "flutter/fml/macros.h" @@ -96,8 +98,57 @@ constexpr MTLColorWriteMask ToMTLColorWriteMask( return mask; }; +constexpr MTLCompareFunction ToMTLCompareFunction(CompareFunction func) { + switch (func) { + case CompareFunction::kNever: + return MTLCompareFunctionNever; + case CompareFunction::kLess: + return MTLCompareFunctionLess; + case CompareFunction::kEqual: + return MTLCompareFunctionEqual; + case CompareFunction::kLessEqual: + return MTLCompareFunctionLessEqual; + case CompareFunction::kGreater: + return MTLCompareFunctionGreater; + case CompareFunction::kNotEqual: + return MTLCompareFunctionNotEqual; + case CompareFunction::kGreaterEqual: + return MTLCompareFunctionGreaterEqual; + case CompareFunction::kAlways: + return MTLCompareFunctionAlways; + } + return MTLCompareFunctionAlways; +}; + +constexpr MTLStencilOperation ToMTLStencilOperation(StencilOperation op) { + switch (op) { + case StencilOperation::kKeep: + return MTLStencilOperationKeep; + case StencilOperation::kZero: + return MTLStencilOperationZero; + case StencilOperation::kReplace: + return MTLStencilOperationReplace; + case StencilOperation::kIncrementClamp: + return MTLStencilOperationIncrementClamp; + case StencilOperation::kDecrementClamp: + return MTLStencilOperationDecrementClamp; + case StencilOperation::kInvert: + return MTLStencilOperationInvert; + case StencilOperation::kIncrementWrap: + return MTLStencilOperationIncrementWrap; + case StencilOperation::kDecrementWrap: + return MTLStencilOperationDecrementWrap; + } + return MTLStencilOperationKeep; +}; + MTLRenderPipelineColorAttachmentDescriptor* ToMTLRenderPipelineColorAttachmentDescriptor( ColorAttachmentDescriptor descriptor); +MTLDepthStencilDescriptor* ToMTLDepthStencilDescriptor( + std::optional depth, + std::optional front, + std::optional back); + } // namespace impeller diff --git a/impeller/impeller/compositor/formats_metal.mm b/impeller/impeller/compositor/formats_metal.mm index 6efd3115b4e23..d5411ba008c00 100644 --- a/impeller/impeller/compositor/formats_metal.mm +++ b/impeller/impeller/compositor/formats_metal.mm @@ -30,4 +30,47 @@ return des; } +MTLStencilDescriptor* ToMTLStencilDescriptor( + const StencilAttachmentDescriptor& descriptor) { + auto des = [[MTLStencilDescriptor alloc] init]; + des.stencilCompareFunction = ToMTLCompareFunction(descriptor.stencil_compare); + des.stencilFailureOperation = + ToMTLStencilOperation(descriptor.stencil_failure); + des.depthFailureOperation = ToMTLStencilOperation(descriptor.depth_failure); + des.depthStencilPassOperation = + ToMTLStencilOperation(descriptor.depth_stencil_pass); + + des.readMask = descriptor.read_mask; + des.writeMask = descriptor.write_mask; + + return des; +} + +MTLDepthStencilDescriptor* ToMTLDepthStencilDescriptor( + std::optional depth, + std::optional front, + std::optional back) { + if (!depth) { + depth = DepthAttachmentDescriptor{ + // Always pass the depth test. + .depth_compare = CompareFunction::kAlways, + .depth_write_enabled = false, + }; + } + + auto des = [[MTLDepthStencilDescriptor alloc] init]; + + des.depthCompareFunction = ToMTLCompareFunction(depth->depth_compare); + des.depthWriteEnabled = depth->depth_write_enabled; + + if (front.has_value()) { + des.frontFaceStencil = ToMTLStencilDescriptor(front.value()); + } + if (back.has_value()) { + des.backFaceStencil = ToMTLStencilDescriptor(back.value()); + } + + return des; +} + } // namespace impeller diff --git a/impeller/impeller/compositor/pipeline.h b/impeller/impeller/compositor/pipeline.h index e3f75d7b4e801..064a2d04d73ba 100644 --- a/impeller/impeller/compositor/pipeline.h +++ b/impeller/impeller/compositor/pipeline.h @@ -26,8 +26,10 @@ class Pipeline { Type type_ = Type::kUnknown; id state_; + id depth_stencil_state_; - Pipeline(id state); + Pipeline(id state, + id depth_stencil_state); FML_DISALLOW_COPY_AND_ASSIGN(Pipeline); }; diff --git a/impeller/impeller/compositor/pipeline.mm b/impeller/impeller/compositor/pipeline.mm index a682a77d0c0f7..c0822ad2720ed 100644 --- a/impeller/impeller/compositor/pipeline.mm +++ b/impeller/impeller/compositor/pipeline.mm @@ -6,7 +6,9 @@ namespace impeller { -Pipeline::Pipeline(id state) : state_(state) { +Pipeline::Pipeline(id state, + id depth_stencil_state) + : state_(state), depth_stencil_state_(depth_stencil_state) { if (state_ != nil) { type_ = Type::kRender; } diff --git a/impeller/impeller/compositor/pipeline_descriptor.h b/impeller/impeller/compositor/pipeline_descriptor.h index e565f231997b6..3c68996c78de4 100644 --- a/impeller/impeller/compositor/pipeline_descriptor.h +++ b/impeller/impeller/compositor/pipeline_descriptor.h @@ -44,10 +44,23 @@ class PipelineDescriptor : public Comparable { size_t index, ColorAttachmentDescriptor desc); + PipelineDescriptor& SetDepthStencilAttachmentDescriptor( + DepthAttachmentDescriptor desc); + + PipelineDescriptor& SetStencilAttachmentDescriptors( + StencilAttachmentDescriptor front_and_back); + + PipelineDescriptor& SetStencilAttachmentDescriptors( + StencilAttachmentDescriptor front, + StencilAttachmentDescriptor back); + PipelineDescriptor& SetDepthStencilPixelFormat(PixelFormat format); MTLRenderPipelineDescriptor* GetMTLRenderPipelineDescriptor() const; + id CreateDepthStencilDescriptor( + id device) const; + // Comparable std::size_t GetHash() const override; @@ -61,6 +74,11 @@ class PipelineDescriptor : public Comparable { std::map color_attachment_descriptors_; std::shared_ptr vertex_descriptor_; PixelFormat depth_stencil_pixel_format_ = PixelFormat::kUnknown; + std::optional depth_attachment_descriptor_; + std::optional + front_stencil_attachment_descriptor_; + std::optional + back_stencil_attachment_descriptor_; }; } // namespace impeller diff --git a/impeller/impeller/compositor/pipeline_descriptor.mm b/impeller/impeller/compositor/pipeline_descriptor.mm index b983f523bcb7e..916c2ec4637e4 100644 --- a/impeller/impeller/compositor/pipeline_descriptor.mm +++ b/impeller/impeller/compositor/pipeline_descriptor.mm @@ -33,6 +33,9 @@ fml::HashCombineSeed(seed, vertex_descriptor_->GetHash()); } fml::HashCombineSeed(seed, depth_stencil_pixel_format_); + fml::HashCombineSeed(seed, depth_attachment_descriptor_); + fml::HashCombineSeed(seed, front_stencil_attachment_descriptor_); + fml::HashCombineSeed(seed, back_stencil_attachment_descriptor_); return seed; } @@ -42,7 +45,12 @@ DeepCompareMap(entrypoints_, other.entrypoints_) && color_attachment_descriptors_ == other.color_attachment_descriptors_ && DeepComparePointer(vertex_descriptor_, other.vertex_descriptor_) && - depth_stencil_pixel_format_ == other.depth_stencil_pixel_format_; + depth_stencil_pixel_format_ == other.depth_stencil_pixel_format_ && + depth_attachment_descriptor_ == other.depth_attachment_descriptor_ && + front_stencil_attachment_descriptor_ == + other.front_stencil_attachment_descriptor_ && + back_stencil_attachment_descriptor_ == + other.back_stencil_attachment_descriptor_; } PipelineDescriptor& PipelineDescriptor::SetLabel( @@ -90,6 +98,25 @@ return *this; } +PipelineDescriptor& PipelineDescriptor::SetDepthStencilAttachmentDescriptor( + DepthAttachmentDescriptor desc) { + depth_attachment_descriptor_ = desc; + return *this; +} + +PipelineDescriptor& PipelineDescriptor::SetStencilAttachmentDescriptors( + StencilAttachmentDescriptor front_and_back) { + return SetStencilAttachmentDescriptors(front_and_back, front_and_back); +} + +PipelineDescriptor& PipelineDescriptor::SetStencilAttachmentDescriptors( + StencilAttachmentDescriptor front, + StencilAttachmentDescriptor back) { + front_stencil_attachment_descriptor_ = front; + back_stencil_attachment_descriptor_ = back; + return *this; +} + MTLRenderPipelineDescriptor* PipelineDescriptor::GetMTLRenderPipelineDescriptor() const { auto descriptor = [[MTLRenderPipelineDescriptor alloc] init]; @@ -122,4 +149,14 @@ return descriptor; } +id PipelineDescriptor::CreateDepthStencilDescriptor( + id device) const { + auto descriptor = + ToMTLDepthStencilDescriptor(depth_attachment_descriptor_, // + front_stencil_attachment_descriptor_, // + back_stencil_attachment_descriptor_ // + ); + return [device newDepthStencilStateWithDescriptor:descriptor]; +} + } // namespace impeller diff --git a/impeller/impeller/compositor/pipeline_library.mm b/impeller/impeller/compositor/pipeline_library.mm index 6ede19f03fd20..1c6ff24727797 100644 --- a/impeller/impeller/compositor/pipeline_library.mm +++ b/impeller/impeller/compositor/pipeline_library.mm @@ -33,8 +33,9 @@ << error.localizedDescription.UTF8String; promise->set_value(nullptr); } else { - auto new_pipeline = - std::shared_ptr(new Pipeline(render_pipeline_state)); + auto new_pipeline = std::shared_ptr( + new Pipeline(render_pipeline_state, + descriptor.CreateDepthStencilDescriptor(device_))); promise->set_value(new_pipeline); this->SavePipeline(descriptor, new_pipeline); } From b30b3a59edcc1b095e43af3ac6f7379d413f1bc9 Mon Sep 17 00:00:00 2001 From: Chinmay Garde Date: Fri, 28 May 2021 15:23:35 -0700 Subject: [PATCH 058/433] WIP on host buffer allocations. --- impeller/impeller/compositor/BUILD.gn | 2 + impeller/impeller/compositor/buffer.h | 54 ++++++++++++++++ impeller/impeller/compositor/buffer.mm | 63 +++++++++++++++++++ .../impeller/compositor/command_buffer.cc | 13 ++++ impeller/impeller/compositor/command_buffer.h | 32 ++++++++++ impeller/impeller/compositor/range.cc | 11 ++++ impeller/impeller/compositor/range.h | 18 ++++++ 7 files changed, 193 insertions(+) create mode 100644 impeller/impeller/compositor/command_buffer.cc create mode 100644 impeller/impeller/compositor/command_buffer.h create mode 100644 impeller/impeller/compositor/range.cc create mode 100644 impeller/impeller/compositor/range.h diff --git a/impeller/impeller/compositor/BUILD.gn b/impeller/impeller/compositor/BUILD.gn index f725371a42011..6d1bec6b42f82 100644 --- a/impeller/impeller/compositor/BUILD.gn +++ b/impeller/impeller/compositor/BUILD.gn @@ -24,6 +24,8 @@ impeller_component("compositor") { "pipeline_descriptor.mm", "pipeline_library.h", "pipeline_library.mm", + "range.cc", + "range.h", "renderer.h", "renderer.mm", "shader_function.h", diff --git a/impeller/impeller/compositor/buffer.h b/impeller/impeller/compositor/buffer.h index ecaeeb32e3e4c..867b97187468e 100644 --- a/impeller/impeller/compositor/buffer.h +++ b/impeller/impeller/compositor/buffer.h @@ -4,11 +4,36 @@ #pragma once +#include + #include "flutter/fml/macros.h" #include "impeller/compositor/allocator.h" +#include "impeller/compositor/range.h" namespace impeller { +class BufferBase { + public: + virtual uint8_t* GetMapping() const = 0; + + virtual size_t GetLength() const = 0; +}; + +class BufferView : public BufferBase { + public: + uint8_t* GetMapping() const { + return parent_->GetMapping() + range_in_parent_.offset; + } + + size_t GetLength() const { return range_in_parent_.length; } + + private: + std::shared_ptr parent_; + Range range_in_parent_; + + FML_DISALLOW_COPY_AND_ASSIGN(BufferView); +}; + class Buffer { public: ~Buffer(); @@ -29,4 +54,33 @@ class Buffer { FML_DISALLOW_COPY_AND_ASSIGN(Buffer); }; +class HostBuffer : public std::enable_shared_from_this, + public BufferBase { + public: + std::shared_ptr Create(); + + std::shared_ptr Emplace(size_t length); + + ~HostBuffer(); + + uint8_t* GetMapping() const override { return buffer_; } + + size_t GetLength() const override { return length_; } + + [[nodiscard]] bool Truncate(size_t length); + + private: + uint8_t* buffer_ = nullptr; + size_t length_ = 0; + size_t reserved_ = 0; + + [[nodiscard]] bool Reserve(size_t reserved); + + [[nodiscard]] bool ReserveNPOT(size_t reserved); + + HostBuffer(); + + FML_DISALLOW_COPY_AND_ASSIGN(HostBuffer); +}; + } // namespace impeller diff --git a/impeller/impeller/compositor/buffer.mm b/impeller/impeller/compositor/buffer.mm index 01c5fed7fe1ac..60025aafb19bb 100644 --- a/impeller/impeller/compositor/buffer.mm +++ b/impeller/impeller/compositor/buffer.mm @@ -4,6 +4,8 @@ #include "impeller/compositor/buffer.h" +#include "flutter/fml/logging.h" + namespace impeller { Buffer::Buffer(id buffer, @@ -14,4 +16,65 @@ Buffer::~Buffer() = default; +std::shared_ptr HostBuffer::Create() { + return std::shared_ptr(new HostBuffer()); +} + +HostBuffer::HostBuffer() = default; + +HostBuffer::~HostBuffer() { + ::free(buffer_); +} + +std::shared_ptr HostBuffer::Emplace(size_t length) { + if (!Truncate(length_ + length)) { + return nullptr; + } +} + +bool HostBuffer::Truncate(size_t length) { + if (!ReserveNPOT(length)) { + return false; + } + length_ = length; + return true; +} + +static uint32_t NextPowerOfTwoSize(uint32_t x) { + if (x == 0) { + return 1; + } + + --x; + + x |= x >> 1; + x |= x >> 2; + x |= x >> 4; + x |= x >> 8; + x |= x >> 16; + + return x + 1; +} + +bool HostBuffer::ReserveNPOT(size_t reserved) { + return Reserve(NextPowerOfTwoSize(reserved)); +} + +bool HostBuffer::Reserve(size_t reserved) { + if (reserved == reserved_) { + return true; + } + auto new_allocation = ::realloc(buffer_, reserved); + if (!new_allocation) { + // If new length is zero, a minimum non-zero sized allocation is returned. + // So this check will not trip and this routine will indicate success as + // expected. + FML_LOG(ERROR) << "Allocation failed. Out of memory."; + return false; + } + buffer_ = static_cast(new_allocation); + reserved_ = reserved; + return true; +} + } // namespace impeller diff --git a/impeller/impeller/compositor/command_buffer.cc b/impeller/impeller/compositor/command_buffer.cc new file mode 100644 index 0000000000000..9e877858dcb08 --- /dev/null +++ b/impeller/impeller/compositor/command_buffer.cc @@ -0,0 +1,13 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "impeller/compositor/command_buffer.h" + +namespace impeller { + +Class::Class() = default; + +Class::~Class() = default; + +} // namespace impeller diff --git a/impeller/impeller/compositor/command_buffer.h b/impeller/impeller/compositor/command_buffer.h new file mode 100644 index 0000000000000..06f99c9589323 --- /dev/null +++ b/impeller/impeller/compositor/command_buffer.h @@ -0,0 +1,32 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#pragma once + +#include + +#include "flutter/fml/macros.h" + +namespace impeller { + +class CommandBuffer { + public: + ~CommandBuffer(); + + enum class CommitResult { + kPending, + kError, + kCompleted, + }; + + using CommitCallback = std::function; + void Commit(CommitCallback callback); + + private: + CommandBuffer(); + + FML_DISALLOW_COPY_AND_ASSIGN(CommandBuffer); +}; + +} // namespace impeller diff --git a/impeller/impeller/compositor/range.cc b/impeller/impeller/compositor/range.cc new file mode 100644 index 0000000000000..a6fe921a10180 --- /dev/null +++ b/impeller/impeller/compositor/range.cc @@ -0,0 +1,11 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "impeller/compositor/range.h" + +namespace impeller { + +// + +} // namespace impeller diff --git a/impeller/impeller/compositor/range.h b/impeller/impeller/compositor/range.h new file mode 100644 index 0000000000000..a12005fa62e6d --- /dev/null +++ b/impeller/impeller/compositor/range.h @@ -0,0 +1,18 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#pragma once + +#include + +#include "flutter/fml/macros.h" + +namespace impeller { + +struct Range { + size_t offset = 0; + size_t length = 0; +}; + +} // namespace impeller From 0126920592f171b4678d7c6c3b801266033a1572 Mon Sep 17 00:00:00 2001 From: Chinmay Garde Date: Tue, 1 Jun 2021 14:05:06 -0700 Subject: [PATCH 059/433] Patch buffer views. --- impeller/impeller/compositor/buffer.h | 24 ++++++++++++++++++++---- impeller/impeller/compositor/buffer.mm | 3 +++ 2 files changed, 23 insertions(+), 4 deletions(-) diff --git a/impeller/impeller/compositor/buffer.h b/impeller/impeller/compositor/buffer.h index 867b97187468e..a098721638595 100644 --- a/impeller/impeller/compositor/buffer.h +++ b/impeller/impeller/compositor/buffer.h @@ -12,25 +12,39 @@ namespace impeller { +class HostBuffer; + class BufferBase { public: + ~BufferBase() = default; + virtual uint8_t* GetMapping() const = 0; virtual size_t GetLength() const = 0; }; -class BufferView : public BufferBase { +class BufferView final : public BufferBase { public: - uint8_t* GetMapping() const { + // |BufferBase| + ~BufferView() = default; + + // |BufferBase| + uint8_t* GetMapping() const override { return parent_->GetMapping() + range_in_parent_.offset; } - size_t GetLength() const { return range_in_parent_.length; } + // |BufferBase| + size_t GetLength() const override { return range_in_parent_.length; } private: + friend HostBuffer; + std::shared_ptr parent_; Range range_in_parent_; + BufferView(std::shared_ptr parent, Range range_in_parent) + : parent_(std::move(parent)), range_in_parent_(range_in_parent) {} + FML_DISALLOW_COPY_AND_ASSIGN(BufferView); }; @@ -61,10 +75,12 @@ class HostBuffer : public std::enable_shared_from_this, std::shared_ptr Emplace(size_t length); - ~HostBuffer(); + virtual ~HostBuffer(); + // |BufferBase| uint8_t* GetMapping() const override { return buffer_; } + // |BufferBase| size_t GetLength() const override { return length_; } [[nodiscard]] bool Truncate(size_t length); diff --git a/impeller/impeller/compositor/buffer.mm b/impeller/impeller/compositor/buffer.mm index 60025aafb19bb..cb9ba12e05ac9 100644 --- a/impeller/impeller/compositor/buffer.mm +++ b/impeller/impeller/compositor/buffer.mm @@ -27,9 +27,12 @@ } std::shared_ptr HostBuffer::Emplace(size_t length) { + auto old_length = length_; if (!Truncate(length_ + length)) { return nullptr; } + return std::shared_ptr( + new BufferView(shared_from_this(), Range{old_length, length})); } bool HostBuffer::Truncate(size_t length) { From 295ff710658836e7370e26bfb6e40869d33a0b36 Mon Sep 17 00:00:00 2001 From: Chinmay Garde Date: Tue, 1 Jun 2021 14:53:18 -0700 Subject: [PATCH 060/433] Reorganize project structure. --- impeller/BUILD.gn | 21 ++- impeller/compiler/fixtures/common_types.h | 16 --- impeller/compiler/tools/compiler.gni | 94 ------------ impeller/{impeller => }/compositor/BUILD.gn | 4 + .../{impeller => }/compositor/allocator.h | 0 .../{impeller => }/compositor/allocator.mm | 0 impeller/{impeller => }/compositor/buffer.h | 0 impeller/{impeller => }/compositor/buffer.mm | 0 impeller/compositor/buffer_unittests.cc | 5 + .../compositor/command_buffer.cc | 0 .../compositor/command_buffer.h | 0 .../{impeller => }/compositor/comparable.cc | 0 .../{impeller => }/compositor/comparable.h | 0 impeller/{impeller => }/compositor/context.h | 0 impeller/{impeller => }/compositor/context.mm | 0 impeller/{impeller => }/compositor/formats.cc | 0 impeller/{impeller => }/compositor/formats.h | 0 .../{impeller => }/compositor/formats_metal.h | 0 .../compositor/formats_metal.mm | 0 impeller/{impeller => }/compositor/pipeline.h | 0 .../{impeller => }/compositor/pipeline.mm | 2 +- .../compositor/pipeline_descriptor.h | 0 .../compositor/pipeline_descriptor.mm | 0 .../compositor/pipeline_library.h | 0 .../compositor/pipeline_library.mm | 0 impeller/{impeller => }/compositor/range.cc | 0 impeller/{impeller => }/compositor/range.h | 0 impeller/{impeller => }/compositor/renderer.h | 0 .../{impeller => }/compositor/renderer.mm | 0 .../compositor/shader_function.h | 0 .../compositor/shader_function.mm | 0 .../compositor/shader_library.h | 0 .../compositor/shader_library.mm | 0 impeller/{impeller => }/compositor/surface.h | 0 impeller/{impeller => }/compositor/surface.mm | 0 .../compositor/vertex_descriptor.h | 0 .../compositor/vertex_descriptor.mm | 0 impeller/{impeller => }/entity/BUILD.gn | 0 impeller/{impeller => }/entity/color.cc | 0 impeller/{impeller => }/entity/color.h | 0 impeller/{impeller => }/entity/entity.cc | 0 impeller/{impeller => }/entity/entity.h | 0 impeller/{impeller => }/geometry/BUILD.gn | 0 impeller/{impeller => }/geometry/matrix.cc | 0 impeller/{impeller => }/geometry/matrix.h | 0 impeller/{impeller => }/geometry/path.cc | 0 impeller/{impeller => }/geometry/path.h | 0 .../{impeller => }/geometry/path_builder.cc | 0 .../{impeller => }/geometry/path_builder.h | 0 .../{impeller => }/geometry/path_component.cc | 0 .../{impeller => }/geometry/path_component.h | 0 impeller/{impeller => }/geometry/point.cc | 0 impeller/{impeller => }/geometry/point.h | 0 .../{impeller => }/geometry/quaternion.cc | 0 impeller/{impeller => }/geometry/quaternion.h | 0 impeller/{impeller => }/geometry/rect.cc | 0 impeller/{impeller => }/geometry/rect.h | 0 impeller/{impeller => }/geometry/shear.cc | 0 impeller/{impeller => }/geometry/shear.h | 0 impeller/{impeller => }/geometry/size.cc | 0 impeller/{impeller => }/geometry/size.h | 0 impeller/{impeller => }/geometry/vector.cc | 0 impeller/{impeller => }/geometry/vector.h | 0 impeller/host/BUILD.gn | 5 +- impeller/{impeller => }/image/BUILD.gn | 0 impeller/{impeller => }/image/image.cc | 0 impeller/{impeller => }/image/image.h | 0 impeller/{impeller => }/image/image_result.cc | 0 impeller/{impeller => }/image/image_result.h | 0 impeller/impeller/BUILD.gn | 21 --- impeller/{impeller => }/primitives/BUILD.gn | 0 impeller/{impeller => }/primitives/box.frag | 0 impeller/{impeller => }/primitives/box.h | 0 impeller/{impeller => }/primitives/box.mm | 0 impeller/{impeller => }/primitives/box.vert | 0 impeller/{impeller => }/shader_glue/BUILD.gn | 0 .../shader_glue/shader_types.cc | 0 .../{impeller => }/shader_glue/shader_types.h | 0 .../tools/{metal => }/build_metal_library.py | 0 impeller/tools/impeller.gni | 135 +++++++++++++++++- impeller/tools/metal/metal_library.gni | 46 ------ 81 files changed, 165 insertions(+), 184 deletions(-) delete mode 100644 impeller/compiler/fixtures/common_types.h delete mode 100644 impeller/compiler/tools/compiler.gni rename impeller/{impeller => }/compositor/BUILD.gn (92%) rename impeller/{impeller => }/compositor/allocator.h (100%) rename impeller/{impeller => }/compositor/allocator.mm (100%) rename impeller/{impeller => }/compositor/buffer.h (100%) rename impeller/{impeller => }/compositor/buffer.mm (100%) create mode 100644 impeller/compositor/buffer_unittests.cc rename impeller/{impeller => }/compositor/command_buffer.cc (100%) rename impeller/{impeller => }/compositor/command_buffer.h (100%) rename impeller/{impeller => }/compositor/comparable.cc (100%) rename impeller/{impeller => }/compositor/comparable.h (100%) rename impeller/{impeller => }/compositor/context.h (100%) rename impeller/{impeller => }/compositor/context.mm (100%) rename impeller/{impeller => }/compositor/formats.cc (100%) rename impeller/{impeller => }/compositor/formats.h (100%) rename impeller/{impeller => }/compositor/formats_metal.h (100%) rename impeller/{impeller => }/compositor/formats_metal.mm (100%) rename impeller/{impeller => }/compositor/pipeline.h (100%) rename impeller/{impeller => }/compositor/pipeline.mm (89%) rename impeller/{impeller => }/compositor/pipeline_descriptor.h (100%) rename impeller/{impeller => }/compositor/pipeline_descriptor.mm (100%) rename impeller/{impeller => }/compositor/pipeline_library.h (100%) rename impeller/{impeller => }/compositor/pipeline_library.mm (100%) rename impeller/{impeller => }/compositor/range.cc (100%) rename impeller/{impeller => }/compositor/range.h (100%) rename impeller/{impeller => }/compositor/renderer.h (100%) rename impeller/{impeller => }/compositor/renderer.mm (100%) rename impeller/{impeller => }/compositor/shader_function.h (100%) rename impeller/{impeller => }/compositor/shader_function.mm (100%) rename impeller/{impeller => }/compositor/shader_library.h (100%) rename impeller/{impeller => }/compositor/shader_library.mm (100%) rename impeller/{impeller => }/compositor/surface.h (100%) rename impeller/{impeller => }/compositor/surface.mm (100%) rename impeller/{impeller => }/compositor/vertex_descriptor.h (100%) rename impeller/{impeller => }/compositor/vertex_descriptor.mm (100%) rename impeller/{impeller => }/entity/BUILD.gn (100%) rename impeller/{impeller => }/entity/color.cc (100%) rename impeller/{impeller => }/entity/color.h (100%) rename impeller/{impeller => }/entity/entity.cc (100%) rename impeller/{impeller => }/entity/entity.h (100%) rename impeller/{impeller => }/geometry/BUILD.gn (100%) rename impeller/{impeller => }/geometry/matrix.cc (100%) rename impeller/{impeller => }/geometry/matrix.h (100%) rename impeller/{impeller => }/geometry/path.cc (100%) rename impeller/{impeller => }/geometry/path.h (100%) rename impeller/{impeller => }/geometry/path_builder.cc (100%) rename impeller/{impeller => }/geometry/path_builder.h (100%) rename impeller/{impeller => }/geometry/path_component.cc (100%) rename impeller/{impeller => }/geometry/path_component.h (100%) rename impeller/{impeller => }/geometry/point.cc (100%) rename impeller/{impeller => }/geometry/point.h (100%) rename impeller/{impeller => }/geometry/quaternion.cc (100%) rename impeller/{impeller => }/geometry/quaternion.h (100%) rename impeller/{impeller => }/geometry/rect.cc (100%) rename impeller/{impeller => }/geometry/rect.h (100%) rename impeller/{impeller => }/geometry/shear.cc (100%) rename impeller/{impeller => }/geometry/shear.h (100%) rename impeller/{impeller => }/geometry/size.cc (100%) rename impeller/{impeller => }/geometry/size.h (100%) rename impeller/{impeller => }/geometry/vector.cc (100%) rename impeller/{impeller => }/geometry/vector.h (100%) rename impeller/{impeller => }/image/BUILD.gn (100%) rename impeller/{impeller => }/image/image.cc (100%) rename impeller/{impeller => }/image/image.h (100%) rename impeller/{impeller => }/image/image_result.cc (100%) rename impeller/{impeller => }/image/image_result.h (100%) delete mode 100644 impeller/impeller/BUILD.gn rename impeller/{impeller => }/primitives/BUILD.gn (100%) rename impeller/{impeller => }/primitives/box.frag (100%) rename impeller/{impeller => }/primitives/box.h (100%) rename impeller/{impeller => }/primitives/box.mm (100%) rename impeller/{impeller => }/primitives/box.vert (100%) rename impeller/{impeller => }/shader_glue/BUILD.gn (100%) rename impeller/{impeller => }/shader_glue/shader_types.cc (100%) rename impeller/{impeller => }/shader_glue/shader_types.h (100%) rename impeller/tools/{metal => }/build_metal_library.py (100%) delete mode 100644 impeller/tools/metal/metal_library.gni diff --git a/impeller/BUILD.gn b/impeller/BUILD.gn index cf0528736c4aa..af545df2c7f75 100644 --- a/impeller/BUILD.gn +++ b/impeller/BUILD.gn @@ -2,9 +2,28 @@ # Use of this source code is governed by a BSD-style license that can be # found in the LICENSE file. +config("impeller_public_config") { + include_dirs = [ ".." ] +} + group("impeller") { deps = [ + "compiler", + "compositor", + "entity", + "geometry", "host", - "impeller", + "image", + "primitives", + "shader_glue", + ] +} + +group("unittests") { + testonly = true + + deps = [ + "compiler:compiler_unittests", + "compositor:compositor_unittests", ] } diff --git a/impeller/compiler/fixtures/common_types.h b/impeller/compiler/fixtures/common_types.h deleted file mode 100644 index ad4d31f94b426..0000000000000 --- a/impeller/compiler/fixtures/common_types.h +++ /dev/null @@ -1,16 +0,0 @@ -// Copyright 2013 The Flutter Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#ifdef IMPELLER_DEVICE - -#define Vector3 vec3 -#define Vector4 vec4 -#define Matrix mat4 - -#else // IMPELLER_DEVICE - -#include "flutter/impeller/impeller/geometry/matrix.h" -#include "flutter/impeller/impeller/geometry/vector.h" - -#endif // IMPELLER_DEVICE diff --git a/impeller/compiler/tools/compiler.gni b/impeller/compiler/tools/compiler.gni deleted file mode 100644 index 10ffad9079b93..0000000000000 --- a/impeller/compiler/tools/compiler.gni +++ /dev/null @@ -1,94 +0,0 @@ -# Copyright 2013 The Flutter Authors. All rights reserved. -# Use of this source code is governed by a BSD-style license that can be -# found in the LICENSE file. - -import("//build/compiled_action.gni") -import("//flutter/impeller/tools/metal/metal_library.gni") - -template("impeller_shaders") { - assert(defined(invoker.shaders), "Impeller shaders must be specified.") - assert(defined(invoker.name), "Name of the shader library must be specified.") - - impellerc_target_name = "impellerc_$target_name" - compiled_action_foreach(impellerc_target_name) { - tool = "//flutter/impeller/compiler:impellerc" - - sources = invoker.shaders - - metal_intermediate = "$target_gen_dir/{{source_file_part}}.metal" - spirv_intermediate = "$target_gen_dir/{{source_file_part}}.spirv" - reflection_json_intermediate = "$target_gen_dir/{{source_file_part}}.json" - reflection_header_intermediate = "$target_gen_dir/{{source_file_part}}.h" - reflection_cc_intermediate = "$target_gen_dir/{{source_file_part}}.cc" - - outputs = [ - metal_intermediate, - reflection_header_intermediate, - reflection_cc_intermediate, - ] - - depfile_path = "$target_gen_dir/{{source_file_part}}.d" - source_path = "{{source}}" - metal_intermediate_path = rebase_path(metal_intermediate, root_build_dir) - spirv_intermediate_path = rebase_path(spirv_intermediate, root_build_dir) - depfile_intermediate_path = rebase_path(depfile_path, root_build_dir) - - reflection_json_path = - rebase_path(reflection_json_intermediate, root_build_dir) - reflection_header_path = - rebase_path(reflection_header_intermediate, root_build_dir) - reflection_cc_path = rebase_path(reflection_cc_intermediate, root_build_dir) - - depfile = depfile_path - - args = [ - "--input=$source_path", - "--metal=$metal_intermediate_path", - "--spirv=$spirv_intermediate_path", - "--reflection-json=$reflection_json_path", - "--reflection-header=$reflection_header_path", - "--reflection-cc=$reflection_cc_path", - "--include={{source_dir}}", - "--depfile=$depfile_intermediate_path", - ] - } - - metal_library_target_name = "metal_library_$target_name" - metal_library(metal_library_target_name) { - name = invoker.name - sources = filter_include(get_target_outputs(":$impellerc_target_name"), - [ "*.metal" ]) - deps = [ ":$impellerc_target_name" ] - } - - shader_glue_target_name = "glue_$target_name" - shader_glue_config_name = "glue_config_$target_name" - - config(shader_glue_config_name) { - # Contains the generated header headers. - include_dirs = [ target_gen_dir ] - } - - source_set(shader_glue_target_name) { - public_configs = [ ":$shader_glue_config_name" ] - - public = - filter_include(get_target_outputs(":$impellerc_target_name"), [ "*.h" ]) - sources = filter_include(get_target_outputs(":$impellerc_target_name"), - [ - "*.h", - "*.cc", - ]) - deps = [ - ":$impellerc_target_name", - "//flutter/impeller/impeller/shader_glue", - ] - } - - group(target_name) { - public_deps = [ - ":$metal_library_target_name", - ":$shader_glue_target_name", - ] - } -} diff --git a/impeller/impeller/compositor/BUILD.gn b/impeller/compositor/BUILD.gn similarity index 92% rename from impeller/impeller/compositor/BUILD.gn rename to impeller/compositor/BUILD.gn index 6d1bec6b42f82..9f18dd4b2d5e8 100644 --- a/impeller/impeller/compositor/BUILD.gn +++ b/impeller/compositor/BUILD.gn @@ -43,3 +43,7 @@ impeller_component("compositor") { "../shader_glue", ] } + +executable("compositor_unittests") { + sources = [ "buffer_unittests.cc" ] +} diff --git a/impeller/impeller/compositor/allocator.h b/impeller/compositor/allocator.h similarity index 100% rename from impeller/impeller/compositor/allocator.h rename to impeller/compositor/allocator.h diff --git a/impeller/impeller/compositor/allocator.mm b/impeller/compositor/allocator.mm similarity index 100% rename from impeller/impeller/compositor/allocator.mm rename to impeller/compositor/allocator.mm diff --git a/impeller/impeller/compositor/buffer.h b/impeller/compositor/buffer.h similarity index 100% rename from impeller/impeller/compositor/buffer.h rename to impeller/compositor/buffer.h diff --git a/impeller/impeller/compositor/buffer.mm b/impeller/compositor/buffer.mm similarity index 100% rename from impeller/impeller/compositor/buffer.mm rename to impeller/compositor/buffer.mm diff --git a/impeller/compositor/buffer_unittests.cc b/impeller/compositor/buffer_unittests.cc new file mode 100644 index 0000000000000..2b409d838d21d --- /dev/null +++ b/impeller/compositor/buffer_unittests.cc @@ -0,0 +1,5 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "gtest/gtest.h" diff --git a/impeller/impeller/compositor/command_buffer.cc b/impeller/compositor/command_buffer.cc similarity index 100% rename from impeller/impeller/compositor/command_buffer.cc rename to impeller/compositor/command_buffer.cc diff --git a/impeller/impeller/compositor/command_buffer.h b/impeller/compositor/command_buffer.h similarity index 100% rename from impeller/impeller/compositor/command_buffer.h rename to impeller/compositor/command_buffer.h diff --git a/impeller/impeller/compositor/comparable.cc b/impeller/compositor/comparable.cc similarity index 100% rename from impeller/impeller/compositor/comparable.cc rename to impeller/compositor/comparable.cc diff --git a/impeller/impeller/compositor/comparable.h b/impeller/compositor/comparable.h similarity index 100% rename from impeller/impeller/compositor/comparable.h rename to impeller/compositor/comparable.h diff --git a/impeller/impeller/compositor/context.h b/impeller/compositor/context.h similarity index 100% rename from impeller/impeller/compositor/context.h rename to impeller/compositor/context.h diff --git a/impeller/impeller/compositor/context.mm b/impeller/compositor/context.mm similarity index 100% rename from impeller/impeller/compositor/context.mm rename to impeller/compositor/context.mm diff --git a/impeller/impeller/compositor/formats.cc b/impeller/compositor/formats.cc similarity index 100% rename from impeller/impeller/compositor/formats.cc rename to impeller/compositor/formats.cc diff --git a/impeller/impeller/compositor/formats.h b/impeller/compositor/formats.h similarity index 100% rename from impeller/impeller/compositor/formats.h rename to impeller/compositor/formats.h diff --git a/impeller/impeller/compositor/formats_metal.h b/impeller/compositor/formats_metal.h similarity index 100% rename from impeller/impeller/compositor/formats_metal.h rename to impeller/compositor/formats_metal.h diff --git a/impeller/impeller/compositor/formats_metal.mm b/impeller/compositor/formats_metal.mm similarity index 100% rename from impeller/impeller/compositor/formats_metal.mm rename to impeller/compositor/formats_metal.mm diff --git a/impeller/impeller/compositor/pipeline.h b/impeller/compositor/pipeline.h similarity index 100% rename from impeller/impeller/compositor/pipeline.h rename to impeller/compositor/pipeline.h diff --git a/impeller/impeller/compositor/pipeline.mm b/impeller/compositor/pipeline.mm similarity index 89% rename from impeller/impeller/compositor/pipeline.mm rename to impeller/compositor/pipeline.mm index c0822ad2720ed..f14bfa8d16d61 100644 --- a/impeller/impeller/compositor/pipeline.mm +++ b/impeller/compositor/pipeline.mm @@ -2,7 +2,7 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -#include "flutter/impeller/impeller/compositor/pipeline.h" +#include "impeller/compositor/pipeline.h" namespace impeller { diff --git a/impeller/impeller/compositor/pipeline_descriptor.h b/impeller/compositor/pipeline_descriptor.h similarity index 100% rename from impeller/impeller/compositor/pipeline_descriptor.h rename to impeller/compositor/pipeline_descriptor.h diff --git a/impeller/impeller/compositor/pipeline_descriptor.mm b/impeller/compositor/pipeline_descriptor.mm similarity index 100% rename from impeller/impeller/compositor/pipeline_descriptor.mm rename to impeller/compositor/pipeline_descriptor.mm diff --git a/impeller/impeller/compositor/pipeline_library.h b/impeller/compositor/pipeline_library.h similarity index 100% rename from impeller/impeller/compositor/pipeline_library.h rename to impeller/compositor/pipeline_library.h diff --git a/impeller/impeller/compositor/pipeline_library.mm b/impeller/compositor/pipeline_library.mm similarity index 100% rename from impeller/impeller/compositor/pipeline_library.mm rename to impeller/compositor/pipeline_library.mm diff --git a/impeller/impeller/compositor/range.cc b/impeller/compositor/range.cc similarity index 100% rename from impeller/impeller/compositor/range.cc rename to impeller/compositor/range.cc diff --git a/impeller/impeller/compositor/range.h b/impeller/compositor/range.h similarity index 100% rename from impeller/impeller/compositor/range.h rename to impeller/compositor/range.h diff --git a/impeller/impeller/compositor/renderer.h b/impeller/compositor/renderer.h similarity index 100% rename from impeller/impeller/compositor/renderer.h rename to impeller/compositor/renderer.h diff --git a/impeller/impeller/compositor/renderer.mm b/impeller/compositor/renderer.mm similarity index 100% rename from impeller/impeller/compositor/renderer.mm rename to impeller/compositor/renderer.mm diff --git a/impeller/impeller/compositor/shader_function.h b/impeller/compositor/shader_function.h similarity index 100% rename from impeller/impeller/compositor/shader_function.h rename to impeller/compositor/shader_function.h diff --git a/impeller/impeller/compositor/shader_function.mm b/impeller/compositor/shader_function.mm similarity index 100% rename from impeller/impeller/compositor/shader_function.mm rename to impeller/compositor/shader_function.mm diff --git a/impeller/impeller/compositor/shader_library.h b/impeller/compositor/shader_library.h similarity index 100% rename from impeller/impeller/compositor/shader_library.h rename to impeller/compositor/shader_library.h diff --git a/impeller/impeller/compositor/shader_library.mm b/impeller/compositor/shader_library.mm similarity index 100% rename from impeller/impeller/compositor/shader_library.mm rename to impeller/compositor/shader_library.mm diff --git a/impeller/impeller/compositor/surface.h b/impeller/compositor/surface.h similarity index 100% rename from impeller/impeller/compositor/surface.h rename to impeller/compositor/surface.h diff --git a/impeller/impeller/compositor/surface.mm b/impeller/compositor/surface.mm similarity index 100% rename from impeller/impeller/compositor/surface.mm rename to impeller/compositor/surface.mm diff --git a/impeller/impeller/compositor/vertex_descriptor.h b/impeller/compositor/vertex_descriptor.h similarity index 100% rename from impeller/impeller/compositor/vertex_descriptor.h rename to impeller/compositor/vertex_descriptor.h diff --git a/impeller/impeller/compositor/vertex_descriptor.mm b/impeller/compositor/vertex_descriptor.mm similarity index 100% rename from impeller/impeller/compositor/vertex_descriptor.mm rename to impeller/compositor/vertex_descriptor.mm diff --git a/impeller/impeller/entity/BUILD.gn b/impeller/entity/BUILD.gn similarity index 100% rename from impeller/impeller/entity/BUILD.gn rename to impeller/entity/BUILD.gn diff --git a/impeller/impeller/entity/color.cc b/impeller/entity/color.cc similarity index 100% rename from impeller/impeller/entity/color.cc rename to impeller/entity/color.cc diff --git a/impeller/impeller/entity/color.h b/impeller/entity/color.h similarity index 100% rename from impeller/impeller/entity/color.h rename to impeller/entity/color.h diff --git a/impeller/impeller/entity/entity.cc b/impeller/entity/entity.cc similarity index 100% rename from impeller/impeller/entity/entity.cc rename to impeller/entity/entity.cc diff --git a/impeller/impeller/entity/entity.h b/impeller/entity/entity.h similarity index 100% rename from impeller/impeller/entity/entity.h rename to impeller/entity/entity.h diff --git a/impeller/impeller/geometry/BUILD.gn b/impeller/geometry/BUILD.gn similarity index 100% rename from impeller/impeller/geometry/BUILD.gn rename to impeller/geometry/BUILD.gn diff --git a/impeller/impeller/geometry/matrix.cc b/impeller/geometry/matrix.cc similarity index 100% rename from impeller/impeller/geometry/matrix.cc rename to impeller/geometry/matrix.cc diff --git a/impeller/impeller/geometry/matrix.h b/impeller/geometry/matrix.h similarity index 100% rename from impeller/impeller/geometry/matrix.h rename to impeller/geometry/matrix.h diff --git a/impeller/impeller/geometry/path.cc b/impeller/geometry/path.cc similarity index 100% rename from impeller/impeller/geometry/path.cc rename to impeller/geometry/path.cc diff --git a/impeller/impeller/geometry/path.h b/impeller/geometry/path.h similarity index 100% rename from impeller/impeller/geometry/path.h rename to impeller/geometry/path.h diff --git a/impeller/impeller/geometry/path_builder.cc b/impeller/geometry/path_builder.cc similarity index 100% rename from impeller/impeller/geometry/path_builder.cc rename to impeller/geometry/path_builder.cc diff --git a/impeller/impeller/geometry/path_builder.h b/impeller/geometry/path_builder.h similarity index 100% rename from impeller/impeller/geometry/path_builder.h rename to impeller/geometry/path_builder.h diff --git a/impeller/impeller/geometry/path_component.cc b/impeller/geometry/path_component.cc similarity index 100% rename from impeller/impeller/geometry/path_component.cc rename to impeller/geometry/path_component.cc diff --git a/impeller/impeller/geometry/path_component.h b/impeller/geometry/path_component.h similarity index 100% rename from impeller/impeller/geometry/path_component.h rename to impeller/geometry/path_component.h diff --git a/impeller/impeller/geometry/point.cc b/impeller/geometry/point.cc similarity index 100% rename from impeller/impeller/geometry/point.cc rename to impeller/geometry/point.cc diff --git a/impeller/impeller/geometry/point.h b/impeller/geometry/point.h similarity index 100% rename from impeller/impeller/geometry/point.h rename to impeller/geometry/point.h diff --git a/impeller/impeller/geometry/quaternion.cc b/impeller/geometry/quaternion.cc similarity index 100% rename from impeller/impeller/geometry/quaternion.cc rename to impeller/geometry/quaternion.cc diff --git a/impeller/impeller/geometry/quaternion.h b/impeller/geometry/quaternion.h similarity index 100% rename from impeller/impeller/geometry/quaternion.h rename to impeller/geometry/quaternion.h diff --git a/impeller/impeller/geometry/rect.cc b/impeller/geometry/rect.cc similarity index 100% rename from impeller/impeller/geometry/rect.cc rename to impeller/geometry/rect.cc diff --git a/impeller/impeller/geometry/rect.h b/impeller/geometry/rect.h similarity index 100% rename from impeller/impeller/geometry/rect.h rename to impeller/geometry/rect.h diff --git a/impeller/impeller/geometry/shear.cc b/impeller/geometry/shear.cc similarity index 100% rename from impeller/impeller/geometry/shear.cc rename to impeller/geometry/shear.cc diff --git a/impeller/impeller/geometry/shear.h b/impeller/geometry/shear.h similarity index 100% rename from impeller/impeller/geometry/shear.h rename to impeller/geometry/shear.h diff --git a/impeller/impeller/geometry/size.cc b/impeller/geometry/size.cc similarity index 100% rename from impeller/impeller/geometry/size.cc rename to impeller/geometry/size.cc diff --git a/impeller/impeller/geometry/size.h b/impeller/geometry/size.h similarity index 100% rename from impeller/impeller/geometry/size.h rename to impeller/geometry/size.h diff --git a/impeller/impeller/geometry/vector.cc b/impeller/geometry/vector.cc similarity index 100% rename from impeller/impeller/geometry/vector.cc rename to impeller/geometry/vector.cc diff --git a/impeller/impeller/geometry/vector.h b/impeller/geometry/vector.h similarity index 100% rename from impeller/impeller/geometry/vector.h rename to impeller/geometry/vector.h diff --git a/impeller/host/BUILD.gn b/impeller/host/BUILD.gn index 7055d10c03e8e..7a37170566825 100644 --- a/impeller/host/BUILD.gn +++ b/impeller/host/BUILD.gn @@ -2,8 +2,7 @@ # Use of this source code is governed by a BSD-style license that can be # found in the LICENSE file. -import("//flutter/common/config.gni") -import("//flutter/impeller/tools/metal/metal_library.gni") +import("//flutter/impeller/tools/impeller.gni") import("//flutter/testing/testing.gni") metal_library("impeller_host_shaders") { @@ -47,7 +46,7 @@ executable("host") { deps = [ ":impeller_host_fixtures", ":impeller_host_shaders", + "../primitives", "//flutter/fml", - "//flutter/impeller/impeller/primitives", ] } diff --git a/impeller/impeller/image/BUILD.gn b/impeller/image/BUILD.gn similarity index 100% rename from impeller/impeller/image/BUILD.gn rename to impeller/image/BUILD.gn diff --git a/impeller/impeller/image/image.cc b/impeller/image/image.cc similarity index 100% rename from impeller/impeller/image/image.cc rename to impeller/image/image.cc diff --git a/impeller/impeller/image/image.h b/impeller/image/image.h similarity index 100% rename from impeller/impeller/image/image.h rename to impeller/image/image.h diff --git a/impeller/impeller/image/image_result.cc b/impeller/image/image_result.cc similarity index 100% rename from impeller/impeller/image/image_result.cc rename to impeller/image/image_result.cc diff --git a/impeller/impeller/image/image_result.h b/impeller/image/image_result.h similarity index 100% rename from impeller/impeller/image/image_result.h rename to impeller/image/image_result.h diff --git a/impeller/impeller/BUILD.gn b/impeller/impeller/BUILD.gn deleted file mode 100644 index 224a761694440..0000000000000 --- a/impeller/impeller/BUILD.gn +++ /dev/null @@ -1,21 +0,0 @@ -# Copyright 2013 The Flutter Authors. All rights reserved. -# Use of this source code is governed by a BSD-style license that can be -# found in the LICENSE file. - -import("//flutter/common/config.gni") -import("//flutter/impeller/compiler/tools/compiler.gni") - -config("impeller_public_config") { - include_dirs = [ "//flutter/impeller" ] -} - -group("impeller") { - deps = [ - "compositor", - "entity", - "geometry", - "image", - "primitives", - "shader_glue", - ] -} diff --git a/impeller/impeller/primitives/BUILD.gn b/impeller/primitives/BUILD.gn similarity index 100% rename from impeller/impeller/primitives/BUILD.gn rename to impeller/primitives/BUILD.gn diff --git a/impeller/impeller/primitives/box.frag b/impeller/primitives/box.frag similarity index 100% rename from impeller/impeller/primitives/box.frag rename to impeller/primitives/box.frag diff --git a/impeller/impeller/primitives/box.h b/impeller/primitives/box.h similarity index 100% rename from impeller/impeller/primitives/box.h rename to impeller/primitives/box.h diff --git a/impeller/impeller/primitives/box.mm b/impeller/primitives/box.mm similarity index 100% rename from impeller/impeller/primitives/box.mm rename to impeller/primitives/box.mm diff --git a/impeller/impeller/primitives/box.vert b/impeller/primitives/box.vert similarity index 100% rename from impeller/impeller/primitives/box.vert rename to impeller/primitives/box.vert diff --git a/impeller/impeller/shader_glue/BUILD.gn b/impeller/shader_glue/BUILD.gn similarity index 100% rename from impeller/impeller/shader_glue/BUILD.gn rename to impeller/shader_glue/BUILD.gn diff --git a/impeller/impeller/shader_glue/shader_types.cc b/impeller/shader_glue/shader_types.cc similarity index 100% rename from impeller/impeller/shader_glue/shader_types.cc rename to impeller/shader_glue/shader_types.cc diff --git a/impeller/impeller/shader_glue/shader_types.h b/impeller/shader_glue/shader_types.h similarity index 100% rename from impeller/impeller/shader_glue/shader_types.h rename to impeller/shader_glue/shader_types.h diff --git a/impeller/tools/metal/build_metal_library.py b/impeller/tools/build_metal_library.py similarity index 100% rename from impeller/tools/metal/build_metal_library.py rename to impeller/tools/build_metal_library.py diff --git a/impeller/tools/impeller.gni b/impeller/tools/impeller.gni index 00dc95333ef66..ece8c977452c5 100644 --- a/impeller/tools/impeller.gni +++ b/impeller/tools/impeller.gni @@ -2,8 +2,8 @@ # Use of this source code is governed by a BSD-style license that can be # found in the LICENSE file. +import("//build/compiled_action.gni") import("//flutter/common/config.gni") -import("//flutter/impeller/compiler/tools/compiler.gni") template("impeller_component") { source_set(target_name) { @@ -30,7 +30,7 @@ template("impeller_component") { cflags_objc += flutter_cflags_objc_arc + objc_warning_flags cflags_objcc += flutter_cflags_objcc_arc + objc_warning_flags - public_configs += [ "//flutter/impeller/impeller:impeller_public_config" ] + public_configs += [ "//flutter/impeller:impeller_public_config" ] deps += [ "//flutter/fml", @@ -40,3 +40,134 @@ template("impeller_component") { ] } } + +template("metal_library") { + assert(defined(invoker.name), "Metal library name must be specified.") + assert(defined(invoker.sources), "Metal source files must be specified.") + + metal_library_name = invoker.name + + action("$target_name") { + forward_variables_from(invoker, + "*", + [ + "inputs", + "outputs", + "script", + "depfile", + "args", + ]) + + inputs = invoker.sources + + metal_library_path = "$root_out_dir/shaders/$metal_library_name.metallib" + + outputs = [ metal_library_path ] + + script = "//flutter/impeller/tools/build_metal_library.py" + + depfile = "$target_gen_dir/shader_deps/$metal_library_name.depfile" + + args = [ + "--output", + rebase_path(metal_library_path, root_out_dir), + "--depfile", + rebase_path(depfile), + ] + + foreach(source, invoker.sources) { + args += [ + "--source", + rebase_path(source, root_out_dir), + ] + } + } +} + +template("impeller_shaders") { + assert(defined(invoker.shaders), "Impeller shaders must be specified.") + assert(defined(invoker.name), "Name of the shader library must be specified.") + + impellerc_target_name = "impellerc_$target_name" + compiled_action_foreach(impellerc_target_name) { + tool = "//flutter/impeller/compiler:impellerc" + + sources = invoker.shaders + + metal_intermediate = "$target_gen_dir/{{source_file_part}}.metal" + spirv_intermediate = "$target_gen_dir/{{source_file_part}}.spirv" + reflection_json_intermediate = "$target_gen_dir/{{source_file_part}}.json" + reflection_header_intermediate = "$target_gen_dir/{{source_file_part}}.h" + reflection_cc_intermediate = "$target_gen_dir/{{source_file_part}}.cc" + + outputs = [ + metal_intermediate, + reflection_header_intermediate, + reflection_cc_intermediate, + ] + + depfile_path = "$target_gen_dir/{{source_file_part}}.d" + source_path = "{{source}}" + metal_intermediate_path = rebase_path(metal_intermediate, root_build_dir) + spirv_intermediate_path = rebase_path(spirv_intermediate, root_build_dir) + depfile_intermediate_path = rebase_path(depfile_path, root_build_dir) + + reflection_json_path = + rebase_path(reflection_json_intermediate, root_build_dir) + reflection_header_path = + rebase_path(reflection_header_intermediate, root_build_dir) + reflection_cc_path = rebase_path(reflection_cc_intermediate, root_build_dir) + + depfile = depfile_path + + args = [ + "--input=$source_path", + "--metal=$metal_intermediate_path", + "--spirv=$spirv_intermediate_path", + "--reflection-json=$reflection_json_path", + "--reflection-header=$reflection_header_path", + "--reflection-cc=$reflection_cc_path", + "--include={{source_dir}}", + "--depfile=$depfile_intermediate_path", + ] + } + + metal_library_target_name = "metal_library_$target_name" + metal_library(metal_library_target_name) { + name = invoker.name + sources = filter_include(get_target_outputs(":$impellerc_target_name"), + [ "*.metal" ]) + deps = [ ":$impellerc_target_name" ] + } + + shader_glue_target_name = "glue_$target_name" + shader_glue_config_name = "glue_config_$target_name" + + config(shader_glue_config_name) { + # Contains the generated header headers. + include_dirs = [ target_gen_dir ] + } + + source_set(shader_glue_target_name) { + public_configs = [ ":$shader_glue_config_name" ] + + public = + filter_include(get_target_outputs(":$impellerc_target_name"), [ "*.h" ]) + sources = filter_include(get_target_outputs(":$impellerc_target_name"), + [ + "*.h", + "*.cc", + ]) + deps = [ + ":$impellerc_target_name", + "//flutter/impeller/shader_glue", + ] + } + + group(target_name) { + public_deps = [ + ":$metal_library_target_name", + ":$shader_glue_target_name", + ] + } +} diff --git a/impeller/tools/metal/metal_library.gni b/impeller/tools/metal/metal_library.gni deleted file mode 100644 index 8c2f4c98e5fe7..0000000000000 --- a/impeller/tools/metal/metal_library.gni +++ /dev/null @@ -1,46 +0,0 @@ -# Copyright 2013 The Flutter Authors. All rights reserved. -# Use of this source code is governed by a BSD-style license that can be -# found in the LICENSE file. - -template("metal_library") { - assert(defined(invoker.name), "Metal library name must be specified.") - assert(defined(invoker.sources), "Metal source files must be specified.") - - metal_library_name = invoker.name - - action("$target_name") { - forward_variables_from(invoker, - "*", - [ - "inputs", - "outputs", - "script", - "depfile", - "args", - ]) - - inputs = invoker.sources - - metal_library_path = "$root_out_dir/shaders/$metal_library_name.metallib" - - outputs = [ metal_library_path ] - - script = "//flutter/impeller/tools/metal/build_metal_library.py" - - depfile = "$target_gen_dir/shader_deps/$metal_library_name.depfile" - - args = [ - "--output", - rebase_path(metal_library_path, root_out_dir), - "--depfile", - rebase_path(depfile), - ] - - foreach(source, invoker.sources) { - args += [ - "--source", - rebase_path(source, root_out_dir), - ] - } - } -} From 089863f922e73d184b891c550d2e5180e5c7513d Mon Sep 17 00:00:00 2001 From: Chinmay Garde Date: Wed, 2 Jun 2021 15:18:44 -0700 Subject: [PATCH 061/433] Begin wiring up render passes. --- impeller/BUILD.gn | 3 +- impeller/compiler/BUILD.gn | 4 +- impeller/compiler/compiler_unittests.cc | 7 +-- impeller/compositor/BUILD.gn | 12 ++++- impeller/compositor/formats.h | 11 +++++ impeller/compositor/render_pass.h | 48 +++++++++++++++++++ impeller/compositor/render_pass.mm | 13 +++++ impeller/compositor/texture.h | 21 ++++++++ .../{buffer_unittests.cc => texture.mm} | 10 +++- 9 files changed, 120 insertions(+), 9 deletions(-) create mode 100644 impeller/compositor/render_pass.h create mode 100644 impeller/compositor/render_pass.mm create mode 100644 impeller/compositor/texture.h rename impeller/compositor/{buffer_unittests.cc => texture.mm} (52%) diff --git a/impeller/BUILD.gn b/impeller/BUILD.gn index af545df2c7f75..8bcef454c6868 100644 --- a/impeller/BUILD.gn +++ b/impeller/BUILD.gn @@ -19,11 +19,12 @@ group("impeller") { ] } -group("unittests") { +executable("impeller_unittests") { testonly = true deps = [ "compiler:compiler_unittests", "compositor:compositor_unittests", + "//flutter/testing", ] } diff --git a/impeller/compiler/BUILD.gn b/impeller/compiler/BUILD.gn index 14616f310b92d..b9179446d4aec 100644 --- a/impeller/compiler/BUILD.gn +++ b/impeller/compiler/BUILD.gn @@ -48,7 +48,7 @@ test_fixtures("compiler_test_fixtures") { ] } -executable("compiler_unittests") { +source_set("compiler_unittests") { testonly = true output_name = "impellerc_unittests" @@ -58,6 +58,6 @@ executable("compiler_unittests") { deps = [ ":compiler_lib", ":compiler_test_fixtures", - "//flutter/testing", + "//flutter/testing:testing_lib", ] } diff --git a/impeller/compiler/compiler_unittests.cc b/impeller/compiler/compiler_unittests.cc index d496312c68556..a5782923789de 100644 --- a/impeller/compiler/compiler_unittests.cc +++ b/impeller/compiler/compiler_unittests.cc @@ -53,10 +53,11 @@ TEST_F(CompilerTest, CanCompileSample) { constexpr const char* kShaderFixtureName = "sample.vert"; auto fixture = flutter::testing::OpenFixtureAsMapping(kShaderFixtureName); ASSERT_NE(fixture->GetMapping(), nullptr); - Compiler::SourceOptions options(kShaderFixtureName); - options.working_directory = std::make_shared( + Compiler::SourceOptions compiler_options(kShaderFixtureName); + compiler_options.working_directory = std::make_shared( flutter::testing::OpenFixturesDirectory()); - Compiler compiler(*fixture.get(), options); + Reflector::Options reflector_options; + Compiler compiler(*fixture.get(), compiler_options, reflector_options); if (!compiler.IsValid()) { FML_LOG(ERROR) << compiler.GetErrorMessages(); } diff --git a/impeller/compositor/BUILD.gn b/impeller/compositor/BUILD.gn index 9f18dd4b2d5e8..8ed28803c7aaf 100644 --- a/impeller/compositor/BUILD.gn +++ b/impeller/compositor/BUILD.gn @@ -26,6 +26,8 @@ impeller_component("compositor") { "pipeline_library.mm", "range.cc", "range.h", + "render_pass.h", + "render_pass.mm", "renderer.h", "renderer.mm", "shader_function.h", @@ -34,6 +36,8 @@ impeller_component("compositor") { "shader_library.mm", "surface.h", "surface.mm", + "texture.h", + "texture.mm", "vertex_descriptor.h", "vertex_descriptor.mm", ] @@ -44,6 +48,10 @@ impeller_component("compositor") { ] } -executable("compositor_unittests") { - sources = [ "buffer_unittests.cc" ] +source_set("compositor_unittests") { + testonly = true + + sources = [] + + deps = [ "//flutter/testing:testing_lib" ] } diff --git a/impeller/compositor/formats.h b/impeller/compositor/formats.h index fd52c4a325cf5..6b40e39e9b767 100644 --- a/impeller/compositor/formats.h +++ b/impeller/compositor/formats.h @@ -43,6 +43,17 @@ enum class BlendOperation { kMax, }; +enum class LoadAction { + kDontCare, + kLoad, + kClear, +}; + +enum class StoreAction { + kDontCare, + kStore, +}; + enum class ColorWriteMask : uint64_t { kNone = 0, kRed = 1 << 0, diff --git a/impeller/compositor/render_pass.h b/impeller/compositor/render_pass.h new file mode 100644 index 0000000000000..78e5f3e645ca2 --- /dev/null +++ b/impeller/compositor/render_pass.h @@ -0,0 +1,48 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#pragma once + +#include "flutter/fml/macros.h" +#include "impeller/compositor/formats.h" +#include "impeller/compositor/texture.h" +#include "impeller/entity/color.h" + +namespace impeller { + +struct RenderPassAttachment { + std::shared_ptr texture; + LoadAction load_action = LoadAction::kDontCare; + StoreAction store_action = StoreAction::kDontCare; +}; + +struct ColorRenderPassAttachment : public RenderPassAttachment { + Color clear_color = Color::BlackTransparent(); +}; + +struct DepthRenderPassAttachment : public RenderPassAttachment { + double clear_depth = 0.0; +}; + +struct StencilRenderPassAttachment : public RenderPassAttachment { + uint32_t clear_stencil = 0; +}; + +class RenderPassDescriptor { + public: + private: + FML_DISALLOW_COPY_AND_ASSIGN(RenderPassDescriptor); +}; + +class RenderPass { + public: + RenderPass(); + + ~RenderPass(); + + private: + FML_DISALLOW_COPY_AND_ASSIGN(RenderPass); +}; + +} // namespace impeller diff --git a/impeller/compositor/render_pass.mm b/impeller/compositor/render_pass.mm new file mode 100644 index 0000000000000..1bb1d3c935980 --- /dev/null +++ b/impeller/compositor/render_pass.mm @@ -0,0 +1,13 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "impeller/compositor/render_pass.h" + +namespace impeller { + +RenderPass::RenderPass() = default; + +RenderPass::~RenderPass() = default; + +} // namespace impeller diff --git a/impeller/compositor/texture.h b/impeller/compositor/texture.h new file mode 100644 index 0000000000000..0157c9d8559e6 --- /dev/null +++ b/impeller/compositor/texture.h @@ -0,0 +1,21 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#pragma once + +#include "flutter/fml/macros.h" + +namespace impeller { + +class Texture { + public: + Texture(); + + ~Texture(); + + private: + FML_DISALLOW_COPY_AND_ASSIGN(Texture); +}; + +} // namespace impeller diff --git a/impeller/compositor/buffer_unittests.cc b/impeller/compositor/texture.mm similarity index 52% rename from impeller/compositor/buffer_unittests.cc rename to impeller/compositor/texture.mm index 2b409d838d21d..c42458f4176cc 100644 --- a/impeller/compositor/buffer_unittests.cc +++ b/impeller/compositor/texture.mm @@ -2,4 +2,12 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -#include "gtest/gtest.h" +#include "impeller/compositor/texture.h" + +namespace impeller { + +Texture::Texture() = default; + +Texture::~Texture() = default; + +} // namespace impeller From 758ee1ad359905915b90d508ead2836d311b0ad5 Mon Sep 17 00:00:00 2001 From: Chinmay Garde Date: Thu, 3 Jun 2021 12:51:52 -0700 Subject: [PATCH 062/433] Finish wiring up render passes. --- impeller/compositor/render_pass.h | 27 +++++++++++++++++++--- impeller/compositor/render_pass.mm | 31 +++++++++++++++++++++++++- impeller/entity/BUILD.gn | 2 -- impeller/geometry/BUILD.gn | 2 ++ impeller/{entity => geometry}/color.cc | 3 ++- impeller/{entity => geometry}/color.h | 0 6 files changed, 58 insertions(+), 7 deletions(-) rename impeller/{entity => geometry}/color.cc (98%) rename impeller/{entity => geometry}/color.h (100%) diff --git a/impeller/compositor/render_pass.h b/impeller/compositor/render_pass.h index 78e5f3e645ca2..d7444a3159e67 100644 --- a/impeller/compositor/render_pass.h +++ b/impeller/compositor/render_pass.h @@ -4,10 +4,12 @@ #pragma once +#include + #include "flutter/fml/macros.h" #include "impeller/compositor/formats.h" #include "impeller/compositor/texture.h" -#include "impeller/entity/color.h" +#include "impeller/geometry/color.h" namespace impeller { @@ -15,6 +17,8 @@ struct RenderPassAttachment { std::shared_ptr texture; LoadAction load_action = LoadAction::kDontCare; StoreAction store_action = StoreAction::kDontCare; + + constexpr operator bool() const { return static_cast(texture); } }; struct ColorRenderPassAttachment : public RenderPassAttachment { @@ -31,17 +35,34 @@ struct StencilRenderPassAttachment : public RenderPassAttachment { class RenderPassDescriptor { public: + RenderPassDescriptor(); + + ~RenderPassDescriptor(); + + RenderPassDescriptor& SetColorAttachment(ColorRenderPassAttachment attachment, + size_t index); + + RenderPassDescriptor& SetDepthAttachment( + DepthRenderPassAttachment attachment); + + RenderPassDescriptor& SetStencilAttachment( + StencilRenderPassAttachment attachment); + private: - FML_DISALLOW_COPY_AND_ASSIGN(RenderPassDescriptor); + std::map color_; + std::optional depth_; + std::optional stencil_; }; class RenderPass { public: - RenderPass(); + RenderPass(RenderPassDescriptor desc); ~RenderPass(); private: + RenderPassDescriptor desc_; + FML_DISALLOW_COPY_AND_ASSIGN(RenderPass); }; diff --git a/impeller/compositor/render_pass.mm b/impeller/compositor/render_pass.mm index 1bb1d3c935980..05dad0b13d902 100644 --- a/impeller/compositor/render_pass.mm +++ b/impeller/compositor/render_pass.mm @@ -6,7 +6,36 @@ namespace impeller { -RenderPass::RenderPass() = default; +RenderPassDescriptor::RenderPassDescriptor() = default; + +RenderPassDescriptor::~RenderPassDescriptor() = default; + +RenderPassDescriptor& RenderPassDescriptor::SetColorAttachment( + ColorRenderPassAttachment attachment, + size_t index) { + if (attachment) { + color_[index] = attachment; + } + return *this; +} + +RenderPassDescriptor& RenderPassDescriptor::SetDepthAttachment( + DepthRenderPassAttachment attachment) { + if (attachment) { + depth_ = std::move(attachment); + } + return *this; +} + +RenderPassDescriptor& RenderPassDescriptor::SetStencilAttachment( + StencilRenderPassAttachment attachment) { + if (attachment) { + stencil_ = std::move(attachment); + } + return *this; +} + +RenderPass::RenderPass(RenderPassDescriptor desc) : desc_(std::move(desc)) {} RenderPass::~RenderPass() = default; diff --git a/impeller/entity/BUILD.gn b/impeller/entity/BUILD.gn index c5e9c728de0f0..b3372f090207f 100644 --- a/impeller/entity/BUILD.gn +++ b/impeller/entity/BUILD.gn @@ -6,8 +6,6 @@ import("//flutter/impeller/tools/impeller.gni") impeller_component("entity") { sources = [ - "color.cc", - "color.h", "entity.cc", "entity.h", ] diff --git a/impeller/geometry/BUILD.gn b/impeller/geometry/BUILD.gn index 8bf782d54c476..f987641c93dfe 100644 --- a/impeller/geometry/BUILD.gn +++ b/impeller/geometry/BUILD.gn @@ -6,6 +6,8 @@ import("//flutter/impeller/tools/impeller.gni") impeller_component("geometry") { sources = [ + "color.cc", + "color.h", "matrix.cc", "matrix.h", "path.cc", diff --git a/impeller/entity/color.cc b/impeller/geometry/color.cc similarity index 98% rename from impeller/entity/color.cc rename to impeller/geometry/color.cc index 9b36f24977ce0..3680f2b5f3148 100644 --- a/impeller/entity/color.cc +++ b/impeller/geometry/color.cc @@ -2,7 +2,8 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -#include "color.h" +#include "impeller/geometry/color.h" + #include #include #include diff --git a/impeller/entity/color.h b/impeller/geometry/color.h similarity index 100% rename from impeller/entity/color.h rename to impeller/geometry/color.h From a84926de77203650cf5fb5cfcfd96d9ccacb6003 Mon Sep 17 00:00:00 2001 From: Chinmay Garde Date: Fri, 4 Jun 2021 16:27:02 -0700 Subject: [PATCH 063/433] Start on the entity model. --- impeller/compositor/BUILD.gn | 4 +++ impeller/compositor/command.h | 32 +++++++++++++++++++ impeller/compositor/command.mm | 7 ++++ impeller/compositor/renderer.h | 11 +++++-- impeller/compositor/renderer.mm | 3 +- impeller/compositor/sampler.h | 21 ++++++++++++ .../box.h => compositor/sampler.mm} | 6 ++-- impeller/entity/BUILD.gn | 7 +++- impeller/entity/entity.h | 2 +- impeller/entity/entity_renderer.h | 29 +++++++++++++++++ impeller/entity/entity_renderer.mm | 23 +++++++++++++ impeller/host/BUILD.gn | 1 + impeller/host/impeller_renderer.mm | 24 ++++++-------- impeller/image/BUILD.gn | 2 ++ impeller/primitives/BUILD.gn | 6 ++-- impeller/primitives/box_primitive.h | 26 +++++++++++++++ .../primitives/{box.mm => box_primitive.mm} | 17 +++++++--- impeller/primitives/primitive.h | 31 ++++++++++++++++++ impeller/primitives/primitive.mm | 18 +++++++++++ 19 files changed, 241 insertions(+), 29 deletions(-) create mode 100644 impeller/compositor/command.h create mode 100644 impeller/compositor/command.mm create mode 100644 impeller/compositor/sampler.h rename impeller/{primitives/box.h => compositor/sampler.mm} (67%) create mode 100644 impeller/entity/entity_renderer.h create mode 100644 impeller/entity/entity_renderer.mm create mode 100644 impeller/primitives/box_primitive.h rename impeller/primitives/{box.mm => box_primitive.mm} (82%) create mode 100644 impeller/primitives/primitive.h create mode 100644 impeller/primitives/primitive.mm diff --git a/impeller/compositor/BUILD.gn b/impeller/compositor/BUILD.gn index 8ed28803c7aaf..6bc76c7c2a5fe 100644 --- a/impeller/compositor/BUILD.gn +++ b/impeller/compositor/BUILD.gn @@ -10,6 +10,8 @@ impeller_component("compositor") { "allocator.mm", "buffer.h", "buffer.mm", + "command.h", + "command.mm", "comparable.cc", "comparable.h", "context.h", @@ -30,6 +32,8 @@ impeller_component("compositor") { "render_pass.mm", "renderer.h", "renderer.mm", + "sampler.h", + "sampler.mm", "shader_function.h", "shader_function.mm", "shader_library.h", diff --git a/impeller/compositor/command.h b/impeller/compositor/command.h new file mode 100644 index 0000000000000..c4d0d8da36836 --- /dev/null +++ b/impeller/compositor/command.h @@ -0,0 +1,32 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#pragma once + +#include +#include + +#include "flutter/fml/macros.h" +#include "impeller/compositor/buffer.h" +#include "impeller/compositor/pipeline.h" + +namespace impeller { + +class Texture; +class Sampler; + +struct Bindings { + std::map buffers; + std::map> textures; + std::map> samplers; +}; + +struct Command { + std::shared_ptr pipeline; + Bindings vertex_bindings; + Bindings fragment_bindings; + BufferView index_buffer; +}; + +} // namespace impeller diff --git a/impeller/compositor/command.mm b/impeller/compositor/command.mm new file mode 100644 index 0000000000000..95f37c799ff8f --- /dev/null +++ b/impeller/compositor/command.mm @@ -0,0 +1,7 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "impeller/compositor/command.h" + +namespace impeller {} // namespace impeller diff --git a/impeller/compositor/renderer.h b/impeller/compositor/renderer.h index 3afa939242343..bf199e6fb73b6 100644 --- a/impeller/compositor/renderer.h +++ b/impeller/compositor/renderer.h @@ -15,9 +15,7 @@ namespace impeller { class Renderer { public: - Renderer(std::string shaders_directory); - - ~Renderer(); + virtual ~Renderer(); bool IsValid() const; @@ -27,6 +25,13 @@ class Renderer { std::shared_ptr GetContext() const; + protected: + Renderer(std::string shaders_directory); + + virtual bool OnRender() = 0; + + virtual bool OnSurfaceSizeDidChange(Size size) = 0; + private: std::shared_ptr context_; std::unique_ptr surface_; diff --git a/impeller/compositor/renderer.mm b/impeller/compositor/renderer.mm index 0b5cf2696b5e7..6cba3970752e1 100644 --- a/impeller/compositor/renderer.mm +++ b/impeller/compositor/renderer.mm @@ -38,7 +38,8 @@ } size_ = size; - return true; + + return OnSurfaceSizeDidChange(size_); } bool Renderer::Render() { diff --git a/impeller/compositor/sampler.h b/impeller/compositor/sampler.h new file mode 100644 index 0000000000000..de73319410c64 --- /dev/null +++ b/impeller/compositor/sampler.h @@ -0,0 +1,21 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#pragma once + +#include "flutter/fml/macros.h" + +namespace impeller { + +class Sampler { + public: + Sampler(); + + ~Sampler(); + + private: + FML_DISALLOW_COPY_AND_ASSIGN(Sampler); +}; + +} // namespace impeller diff --git a/impeller/primitives/box.h b/impeller/compositor/sampler.mm similarity index 67% rename from impeller/primitives/box.h rename to impeller/compositor/sampler.mm index 32b27760067f0..14678d946a153 100644 --- a/impeller/primitives/box.h +++ b/impeller/compositor/sampler.mm @@ -2,10 +2,12 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -#include "impeller/compositor/context.h" +#include "impeller/compositor/sampler.h" namespace impeller { -bool RenderBox(std::shared_ptr context); +Sampler::Sampler() = default; + +Sampler::~Sampler() = default; } // namespace impeller diff --git a/impeller/entity/BUILD.gn b/impeller/entity/BUILD.gn index b3372f090207f..66d048226bd0b 100644 --- a/impeller/entity/BUILD.gn +++ b/impeller/entity/BUILD.gn @@ -8,7 +8,12 @@ impeller_component("entity") { sources = [ "entity.cc", "entity.h", + "entity_renderer.h", + "entity_renderer.mm", ] - public_deps = [ "../image" ] + public_deps = [ + "../compositor", + "../image", + ] } diff --git a/impeller/entity/entity.h b/impeller/entity/entity.h index dc2561458e73c..1ab3047e15e52 100644 --- a/impeller/entity/entity.h +++ b/impeller/entity/entity.h @@ -4,7 +4,7 @@ #pragma once -#include "impeller/entity/color.h" +#include "impeller/geometry/color.h" #include "impeller/geometry/matrix.h" #include "impeller/geometry/path.h" #include "impeller/geometry/rect.h" diff --git a/impeller/entity/entity_renderer.h b/impeller/entity/entity_renderer.h new file mode 100644 index 0000000000000..07113c77865ff --- /dev/null +++ b/impeller/entity/entity_renderer.h @@ -0,0 +1,29 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#pragma once + +#include "flutter/fml/macros.h" +#include "impeller/compositor/renderer.h" +#include "impeller/entity/entity.h" + +namespace impeller { + +class EntityRenderer final : public Renderer { + public: + EntityRenderer(std::string shaders_directory); + + ~EntityRenderer() override; + + private: + std::shared_ptr root_; + + bool OnRender() override; + + bool OnSurfaceSizeDidChange(Size size) override; + + FML_DISALLOW_COPY_AND_ASSIGN(EntityRenderer); +}; + +} // namespace impeller diff --git a/impeller/entity/entity_renderer.mm b/impeller/entity/entity_renderer.mm new file mode 100644 index 0000000000000..914e364f20fda --- /dev/null +++ b/impeller/entity/entity_renderer.mm @@ -0,0 +1,23 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "impeller/entity/entity_renderer.h" + +namespace impeller { + +EntityRenderer::EntityRenderer(std::string shaders_directory) + : Renderer(std::move(shaders_directory)), + root_(std::make_shared()) {} + +EntityRenderer::~EntityRenderer() = default; + +bool EntityRenderer::OnRender() { + return false; +} + +bool EntityRenderer::OnSurfaceSizeDidChange(Size size) { + return false; +} + +} // namespace impeller diff --git a/impeller/host/BUILD.gn b/impeller/host/BUILD.gn index 7a37170566825..dcde833a5897b 100644 --- a/impeller/host/BUILD.gn +++ b/impeller/host/BUILD.gn @@ -46,6 +46,7 @@ executable("host") { deps = [ ":impeller_host_fixtures", ":impeller_host_shaders", + "../entity", "../primitives", "//flutter/fml", ] diff --git a/impeller/host/impeller_renderer.mm b/impeller/host/impeller_renderer.mm index 7129193df18e7..1617193e8634f 100644 --- a/impeller/host/impeller_renderer.mm +++ b/impeller/host/impeller_renderer.mm @@ -4,16 +4,14 @@ #import #import +#import "ShaderTypes.h" -#import "assets_location.h" +#include "assets_location.h" #include "flutter/fml/logging.h" -#import "impeller/compositor/renderer.h" -#import "impeller/primitives/box.h" -#import "impeller_renderer.h" -#import "shaders_location.h" -// Include header shared between C code here, which executes Metal API commands, -// and .metal files -#import "ShaderTypes.h" +#include "impeller/entity/entity_renderer.h" +#include "impeller/primitives/box_primitive.h" +#include "impeller_renderer.h" +#include "shaders_location.h" static const NSUInteger kMaxBuffersInFlight = 3; @@ -53,17 +51,13 @@ - (nonnull instancetype)initWithMetalKitView:(nonnull MTKView*)view { [self _loadAssets]; } - renderer_ = std::make_unique( + renderer_ = std::make_unique( impeller::ImpellerShadersDirectory()); if (!renderer_->IsValid()) { FML_LOG(ERROR) << "Impeller Renderer is not valid."; } - if (!impeller::RenderBox(renderer_->GetContext())) { - FML_LOG(ERROR) << "Could not render box."; - } - return self; } @@ -233,7 +227,9 @@ - (void)_updateGameState { } - (void)drawInMTKView:(nonnull MTKView*)view { - renderer_->Render(); + if (!renderer_->Render()) { + FML_LOG(ERROR) << "Could not render."; + } /// Per frame updates here dispatch_semaphore_wait(_inFlightSemaphore, DISPATCH_TIME_FOREVER); diff --git a/impeller/image/BUILD.gn b/impeller/image/BUILD.gn index eb0cb3c46f9e1..aff4b5104c3d9 100644 --- a/impeller/image/BUILD.gn +++ b/impeller/image/BUILD.gn @@ -12,5 +12,7 @@ impeller_component("image") { "image_result.h", ] + deps = [ "../third_party/stb" ] + public_deps = [ "../geometry" ] } diff --git a/impeller/primitives/BUILD.gn b/impeller/primitives/BUILD.gn index c871d10def2b3..da8d8fa95fed3 100644 --- a/impeller/primitives/BUILD.gn +++ b/impeller/primitives/BUILD.gn @@ -14,8 +14,10 @@ impeller_shaders("impeller_shaders") { impeller_component("primitives") { sources = [ - "box.h", - "box.mm", + "box_primitive.h", + "box_primitive.mm", + "primitive.h", + "primitive.mm", ] public_deps = [ "../compositor" ] diff --git a/impeller/primitives/box_primitive.h b/impeller/primitives/box_primitive.h new file mode 100644 index 0000000000000..9071899403331 --- /dev/null +++ b/impeller/primitives/box_primitive.h @@ -0,0 +1,26 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "impeller/compositor/context.h" +#include "impeller/primitives/primitive.h" + +namespace impeller { + +class BoxPrimitive final : public Primitive { + public: + BoxPrimitive(std::shared_ptr context); + + ~BoxPrimitive() override; + + virtual bool Encode(RenderPass& pass) const override; + + private: + bool is_valid_ = false; + + FML_DISALLOW_COPY_AND_ASSIGN(BoxPrimitive); +}; + +bool RenderBox(std::shared_ptr context); + +} // namespace impeller diff --git a/impeller/primitives/box.mm b/impeller/primitives/box_primitive.mm similarity index 82% rename from impeller/primitives/box.mm rename to impeller/primitives/box_primitive.mm index f303313df5993..cbe589374744c 100644 --- a/impeller/primitives/box.mm +++ b/impeller/primitives/box_primitive.mm @@ -2,7 +2,7 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -#include "impeller/primitives/box.h" +#include "impeller/primitives/box_primitive.h" #include @@ -15,7 +15,8 @@ namespace impeller { -bool RenderBox(std::shared_ptr context) { +BoxPrimitive::BoxPrimitive(std::shared_ptr context) + : Primitive(context) { PipelineDescriptor desc; desc.SetLabel(shader::BoxVertexInfo::kLabel); @@ -34,7 +35,7 @@ bool RenderBox(std::shared_ptr context) { if (!vertex_descriptor->SetStageInputs( shader::BoxVertexInfo::kAllShaderStageInputs)) { FML_LOG(ERROR) << "Could not configure vertex descriptor."; - return false; + return; } desc.SetVertexDescriptor(std::move(vertex_descriptor)); } @@ -43,10 +44,16 @@ bool RenderBox(std::shared_ptr context) { context->GetPipelineLibrary()->GetRenderPipeline(std::move(desc)).get(); if (!pipeline) { FML_LOG(ERROR) << "Could not create the render pipeline."; - return false; + return; } - return true; + is_valid_ = true; +} + +BoxPrimitive::~BoxPrimitive() = default; + +bool BoxPrimitive::Encode(RenderPass& pass) const { + return false; } } // namespace impeller diff --git a/impeller/primitives/primitive.h b/impeller/primitives/primitive.h new file mode 100644 index 0000000000000..916342b755b5e --- /dev/null +++ b/impeller/primitives/primitive.h @@ -0,0 +1,31 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#pragma once + +#include + +#include "flutter/fml/macros.h" +#include "impeller/compositor/context.h" +#include "impeller/compositor/render_pass.h" + +namespace impeller { + +class Primitive { + public: + Primitive(std::shared_ptr); + + virtual ~Primitive(); + + std::shared_ptr GetContext() const; + + virtual bool Encode(RenderPass& pass) const = 0; + + private: + std::shared_ptr context_; + + FML_DISALLOW_COPY_AND_ASSIGN(Primitive); +}; + +} // namespace impeller diff --git a/impeller/primitives/primitive.mm b/impeller/primitives/primitive.mm new file mode 100644 index 0000000000000..ef24844db99ef --- /dev/null +++ b/impeller/primitives/primitive.mm @@ -0,0 +1,18 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "impeller/primitives/primitive.h" + +namespace impeller { + +Primitive::Primitive(std::shared_ptr context) + : context_(std::move(context)) {} + +Primitive::~Primitive() = default; + +std::shared_ptr Primitive::GetContext() const { + return context_; +} + +} // namespace impeller From f2c361cdc481f48bcdcb336ddebcbbf8a625aa91 Mon Sep 17 00:00:00 2001 From: Chinmay Garde Date: Sun, 6 Jun 2021 13:44:08 -0700 Subject: [PATCH 064/433] Wire up render pass creation. --- impeller/compositor/BUILD.gn | 2 + impeller/compositor/command_buffer.cc | 13 ---- impeller/compositor/command_buffer.h | 18 +++++- impeller/compositor/command_buffer.mm | 62 ++++++++++++++++++ impeller/compositor/context.h | 7 +- impeller/compositor/context.mm | 22 ++++--- impeller/compositor/formats_metal.h | 28 ++++++++ impeller/compositor/render_pass.h | 19 ++++-- impeller/compositor/render_pass.mm | 92 ++++++++++++++++++++++++++- impeller/compositor/surface.mm | 13 ++-- impeller/compositor/texture.h | 6 ++ impeller/compositor/texture.mm | 4 ++ 12 files changed, 247 insertions(+), 39 deletions(-) delete mode 100644 impeller/compositor/command_buffer.cc create mode 100644 impeller/compositor/command_buffer.mm diff --git a/impeller/compositor/BUILD.gn b/impeller/compositor/BUILD.gn index 6bc76c7c2a5fe..2288e13e96441 100644 --- a/impeller/compositor/BUILD.gn +++ b/impeller/compositor/BUILD.gn @@ -12,6 +12,8 @@ impeller_component("compositor") { "buffer.mm", "command.h", "command.mm", + "command_buffer.h", + "command_buffer.mm", "comparable.cc", "comparable.h", "context.h", diff --git a/impeller/compositor/command_buffer.cc b/impeller/compositor/command_buffer.cc deleted file mode 100644 index 9e877858dcb08..0000000000000 --- a/impeller/compositor/command_buffer.cc +++ /dev/null @@ -1,13 +0,0 @@ -// Copyright 2013 The Flutter Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#include "impeller/compositor/command_buffer.h" - -namespace impeller { - -Class::Class() = default; - -Class::~Class() = default; - -} // namespace impeller diff --git a/impeller/compositor/command_buffer.h b/impeller/compositor/command_buffer.h index 06f99c9589323..3233312ef1343 100644 --- a/impeller/compositor/command_buffer.h +++ b/impeller/compositor/command_buffer.h @@ -4,16 +4,24 @@ #pragma once +#include + #include +#include #include "flutter/fml/macros.h" +#include "impeller/compositor/render_pass.h" namespace impeller { +class Context; + class CommandBuffer { public: ~CommandBuffer(); + bool IsValid() const; + enum class CommitResult { kPending, kError, @@ -23,8 +31,16 @@ class CommandBuffer { using CommitCallback = std::function; void Commit(CommitCallback callback); + std::shared_ptr CreateRenderPass( + const RenderPassDescriptor& desc) const; + private: - CommandBuffer(); + friend class Context; + + id buffer_ = nullptr; + bool is_valid_ = false; + + CommandBuffer(id queue); FML_DISALLOW_COPY_AND_ASSIGN(CommandBuffer); }; diff --git a/impeller/compositor/command_buffer.mm b/impeller/compositor/command_buffer.mm new file mode 100644 index 0000000000000..cd6ffa29daab3 --- /dev/null +++ b/impeller/compositor/command_buffer.mm @@ -0,0 +1,62 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "impeller/compositor/command_buffer.h" + +namespace impeller { + +CommandBuffer::CommandBuffer(id queue) + : buffer_([queue commandBuffer]) {} + +CommandBuffer::~CommandBuffer() = default; + +bool CommandBuffer::IsValid() const { + return is_valid_; +} + +static CommandBuffer::CommitResult ToCommitResult( + MTLCommandBufferStatus status) { + switch (status) { + case MTLCommandBufferStatusCompleted: + return CommandBuffer::CommitResult::kCompleted; + case MTLCommandBufferStatusEnqueued: + return CommandBuffer::CommitResult::kPending; + default: + break; + } + return CommandBuffer::CommitResult::kError; +} + +void CommandBuffer::Commit(CommitCallback callback) { + if (!callback) { + callback = [](auto) {}; + } + + if (!buffer_) { + callback(CommitResult::kError); + return; + } + + [buffer_ addCompletedHandler:^(id buffer) { + callback(ToCommitResult(buffer.status)); + }]; + [buffer_ commit]; + buffer_ = nil; +} + +std::shared_ptr CommandBuffer::CreateRenderPass( + const RenderPassDescriptor& desc) const { + if (!buffer_) { + return nullptr; + } + + auto pass = std::shared_ptr(new RenderPass(buffer_, desc)); + if (!pass->IsValid()) { + return nullptr; + } + + return pass; +} + +} // namespace impeller diff --git a/impeller/compositor/context.h b/impeller/compositor/context.h index 6dee81f7b4836..fa4e4b3e2de09 100644 --- a/impeller/compositor/context.h +++ b/impeller/compositor/context.h @@ -14,6 +14,7 @@ namespace impeller { class ShaderLibrary; +class CommandBuffer; class Context { public: @@ -23,14 +24,12 @@ class Context { bool IsValid() const; - id GetRenderQueue() const; - - id GetTransferQueue() const; - std::shared_ptr GetShaderLibrary() const; std::shared_ptr GetPipelineLibrary() const; + std::shared_ptr CreateRenderCommandBuffer() const; + private: id device_ = nullptr; id render_queue_ = nullptr; diff --git a/impeller/compositor/context.mm b/impeller/compositor/context.mm index 81f9fa0d90d16..fd8b85a6b71f0 100644 --- a/impeller/compositor/context.mm +++ b/impeller/compositor/context.mm @@ -6,6 +6,7 @@ #include "flutter/fml/logging.h" #include "flutter/fml/paths.h" +#include "impeller/compositor/command_buffer.h" #include "impeller/compositor/shader_library.h" namespace impeller { @@ -62,14 +63,6 @@ return is_valid_; } -id Context::GetRenderQueue() const { - return render_queue_; -} - -id Context::GetTransferQueue() const { - return transfer_queue_; -} - std::shared_ptr Context::GetShaderLibrary() const { return shader_library_; } @@ -78,4 +71,17 @@ return pipeline_library_; } +std::shared_ptr Context::CreateRenderCommandBuffer() const { + if (!IsValid()) { + return nullptr; + } + + auto buffer = + std::shared_ptr(new CommandBuffer(render_queue_)); + if (!buffer->IsValid()) { + return nullptr; + } + return buffer; +} + } // namespace impeller diff --git a/impeller/compositor/formats_metal.h b/impeller/compositor/formats_metal.h index de69023df3675..a93ab76771ec1 100644 --- a/impeller/compositor/formats_metal.h +++ b/impeller/compositor/formats_metal.h @@ -10,6 +10,7 @@ #include "flutter/fml/macros.h" #include "impeller/compositor/formats.h" +#include "impeller/geometry/color.h" namespace impeller { @@ -142,6 +143,33 @@ constexpr MTLStencilOperation ToMTLStencilOperation(StencilOperation op) { return MTLStencilOperationKeep; }; +constexpr MTLLoadAction ToMTLLoadAction(LoadAction action) { + switch (action) { + case LoadAction::kDontCare: + return MTLLoadActionDontCare; + case LoadAction::kLoad: + return MTLLoadActionLoad; + case LoadAction::kClear: + return MTLLoadActionClear; + } + + return MTLLoadActionDontCare; +} + +constexpr MTLStoreAction ToMTLStoreAction(StoreAction action) { + switch (action) { + case StoreAction::kDontCare: + return MTLStoreActionDontCare; + case StoreAction::kStore: + return MTLStoreActionStore; + } + return MTLStoreActionDontCare; +} + +constexpr MTLClearColor ToMTLClearColor(const Color& color) { + return MTLClearColorMake(color.red, color.green, color.blue, color.alpha); +} + MTLRenderPipelineColorAttachmentDescriptor* ToMTLRenderPipelineColorAttachmentDescriptor( ColorAttachmentDescriptor descriptor); diff --git a/impeller/compositor/render_pass.h b/impeller/compositor/render_pass.h index d7444a3159e67..c01cbbb56dc2a 100644 --- a/impeller/compositor/render_pass.h +++ b/impeller/compositor/render_pass.h @@ -4,6 +4,8 @@ #pragma once +#include + #include #include "flutter/fml/macros.h" @@ -13,6 +15,8 @@ namespace impeller { +class CommandBuffer; + struct RenderPassAttachment { std::shared_ptr texture; LoadAction load_action = LoadAction::kDontCare; @@ -48,20 +52,27 @@ class RenderPassDescriptor { RenderPassDescriptor& SetStencilAttachment( StencilRenderPassAttachment attachment); + MTLRenderPassDescriptor* ToMTLRenderPassDescriptor() const; + private: - std::map color_; + std::map colors_; std::optional depth_; std::optional stencil_; }; class RenderPass { public: - RenderPass(RenderPassDescriptor desc); - ~RenderPass(); + bool IsValid() const; + private: - RenderPassDescriptor desc_; + friend class CommandBuffer; + + id pass_ = nil; + bool is_valid_ = false; + + RenderPass(id buffer, const RenderPassDescriptor& desc); FML_DISALLOW_COPY_AND_ASSIGN(RenderPass); }; diff --git a/impeller/compositor/render_pass.mm b/impeller/compositor/render_pass.mm index 05dad0b13d902..06d9c81b98f6b 100644 --- a/impeller/compositor/render_pass.mm +++ b/impeller/compositor/render_pass.mm @@ -4,6 +4,9 @@ #include "impeller/compositor/render_pass.h" +#include "flutter/fml/logging.h" +#include "impeller/compositor/formats_metal.h" + namespace impeller { RenderPassDescriptor::RenderPassDescriptor() = default; @@ -14,7 +17,7 @@ ColorRenderPassAttachment attachment, size_t index) { if (attachment) { - color_[index] = attachment; + colors_[index] = attachment; } return *this; } @@ -35,8 +38,93 @@ return *this; } -RenderPass::RenderPass(RenderPassDescriptor desc) : desc_(std::move(desc)) {} +static bool ConfigureAttachment(const RenderPassAttachment& desc, + MTLRenderPassAttachmentDescriptor* attachment) { + if (!desc.texture) { + return false; + } + + attachment.texture = desc.texture->GetMTLTexture(); + attachment.loadAction = ToMTLLoadAction(desc.load_action); + attachment.storeAction = ToMTLStoreAction(desc.store_action); + return true; +} + +static bool ConfigureColorAttachment( + const ColorRenderPassAttachment& desc, + MTLRenderPassColorAttachmentDescriptor* attachment) { + if (!ConfigureAttachment(desc, attachment)) { + return false; + } + attachment.clearColor = ToMTLClearColor(desc.clear_color); + return true; +} + +static bool ConfigureDepthAttachment( + const DepthRenderPassAttachment& desc, + MTLRenderPassDepthAttachmentDescriptor* attachment) { + if (!ConfigureAttachment(desc, attachment)) { + return false; + } + attachment.clearDepth = desc.clear_depth; + return true; +} + +static bool ConfigureStencilAttachment( + const StencilRenderPassAttachment& desc, + MTLRenderPassStencilAttachmentDescriptor* attachment) { + if (!ConfigureAttachment(desc, attachment)) { + return false; + } + attachment.clearStencil = desc.clear_stencil; + return true; +} + +MTLRenderPassDescriptor* RenderPassDescriptor::ToMTLRenderPassDescriptor() + const { + auto result = [MTLRenderPassDescriptor renderPassDescriptor]; + + for (const auto& color : colors_) { + if (!ConfigureColorAttachment(color.second, + result.colorAttachments[color.first])) { + FML_LOG(ERROR) << "Could not configure color attachment at index " + << color.first; + return nil; + } + } + + if (depth_.has_value() && + !ConfigureDepthAttachment(depth_.value(), result.depthAttachment)) { + return nil; + } + + if (stencil_.has_value() && + !ConfigureStencilAttachment(stencil_.value(), result.stencilAttachment)) { + return nil; + } + + return result; +} + +RenderPass::RenderPass(id buffer, + const RenderPassDescriptor& desc) { + auto descriptor = desc.ToMTLRenderPassDescriptor(); + if (!descriptor) { + return; + } + + pass_ = [buffer renderCommandEncoderWithDescriptor:descriptor]; + if (!pass_) { + return; + } + + is_valid_ = true; +} RenderPass::~RenderPass() = default; +bool RenderPass::IsValid() const { + return is_valid_; +} + } // namespace impeller diff --git a/impeller/compositor/surface.mm b/impeller/compositor/surface.mm index 7cfa292a11315..adbd20c1d511f 100644 --- a/impeller/compositor/surface.mm +++ b/impeller/compositor/surface.mm @@ -5,6 +5,7 @@ #include "impeller/compositor/surface.h" #include "flutter/fml/logging.h" +#include "impeller/compositor/command_buffer.h" namespace impeller { @@ -31,7 +32,7 @@ return false; } - auto command_buffer = [context_->GetRenderQueue() commandBuffer]; + auto command_buffer = context_->CreateRenderCommandBuffer(); if (!command_buffer) { return false; @@ -39,12 +40,10 @@ ::dispatch_semaphore_wait(frames_in_flight_sema_, DISPATCH_TIME_FOREVER); - __block dispatch_semaphore_t block_sema = frames_in_flight_sema_; - [command_buffer addCompletedHandler:^(id buffer) { - ::dispatch_semaphore_signal(block_sema); - }]; - - [command_buffer commit]; + command_buffer->Commit( + [sema = frames_in_flight_sema_](CommandBuffer::CommitResult) { + ::dispatch_semaphore_signal(sema); + }); return true; } diff --git a/impeller/compositor/texture.h b/impeller/compositor/texture.h index 0157c9d8559e6..8e7c55c5b6217 100644 --- a/impeller/compositor/texture.h +++ b/impeller/compositor/texture.h @@ -4,6 +4,8 @@ #pragma once +#include + #include "flutter/fml/macros.h" namespace impeller { @@ -14,7 +16,11 @@ class Texture { ~Texture(); + id GetMTLTexture() const; + private: + id texture_; + FML_DISALLOW_COPY_AND_ASSIGN(Texture); }; diff --git a/impeller/compositor/texture.mm b/impeller/compositor/texture.mm index c42458f4176cc..dc9ea9130cd4d 100644 --- a/impeller/compositor/texture.mm +++ b/impeller/compositor/texture.mm @@ -10,4 +10,8 @@ Texture::~Texture() = default; +id Texture::GetMTLTexture() const { + return texture_; +} + } // namespace impeller From 0fd3dcab6aad19e2aec4db497a39f43e9da25f50 Mon Sep 17 00:00:00 2001 From: Chinmay Garde Date: Sun, 6 Jun 2021 15:34:52 -0700 Subject: [PATCH 065/433] Wire up surface from render pass descriptor. --- impeller/compositor/command_buffer.mm | 7 ++- impeller/compositor/formats_metal.h | 31 ++++++++++++ impeller/compositor/formats_metal.mm | 69 +++++++++++++++++++++++++++ impeller/compositor/render_pass.h | 2 + impeller/compositor/render_pass.mm | 7 +++ impeller/compositor/renderer.h | 15 +++--- impeller/compositor/renderer.mm | 40 ++++++++-------- impeller/compositor/surface.h | 10 ++-- impeller/compositor/surface.mm | 31 +----------- impeller/compositor/texture.h | 2 +- impeller/compositor/texture.mm | 2 +- impeller/entity/entity_renderer.h | 2 - impeller/entity/entity_renderer.mm | 8 ++-- impeller/geometry/size.h | 24 ++++++---- impeller/host/impeller_renderer.mm | 14 +++--- 15 files changed, 173 insertions(+), 91 deletions(-) diff --git a/impeller/compositor/command_buffer.mm b/impeller/compositor/command_buffer.mm index cd6ffa29daab3..c636bab2ca8b9 100644 --- a/impeller/compositor/command_buffer.mm +++ b/impeller/compositor/command_buffer.mm @@ -7,7 +7,12 @@ namespace impeller { CommandBuffer::CommandBuffer(id queue) - : buffer_([queue commandBuffer]) {} + : buffer_([queue commandBuffer]) { + if (!buffer_) { + return; + } + is_valid_ = true; +} CommandBuffer::~CommandBuffer() = default; diff --git a/impeller/compositor/formats_metal.h b/impeller/compositor/formats_metal.h index a93ab76771ec1..ba99c92f2cb38 100644 --- a/impeller/compositor/formats_metal.h +++ b/impeller/compositor/formats_metal.h @@ -14,6 +14,8 @@ namespace impeller { +class RenderPassDescriptor; + constexpr MTLPixelFormat ToMTLPixelFormat(PixelFormat format) { switch (format) { case PixelFormat::kUnknown: @@ -156,6 +158,21 @@ constexpr MTLLoadAction ToMTLLoadAction(LoadAction action) { return MTLLoadActionDontCare; } +constexpr LoadAction FromMTLLoadAction(MTLLoadAction action) { + switch (action) { + case MTLLoadActionDontCare: + return LoadAction::kDontCare; + case MTLLoadActionLoad: + return LoadAction::kLoad; + case MTLLoadActionClear: + return LoadAction::kClear; + default: + break; + } + + return LoadAction::kDontCare; +} + constexpr MTLStoreAction ToMTLStoreAction(StoreAction action) { switch (action) { case StoreAction::kDontCare: @@ -166,10 +183,24 @@ constexpr MTLStoreAction ToMTLStoreAction(StoreAction action) { return MTLStoreActionDontCare; } +constexpr StoreAction FromMTLStoreAction(MTLStoreAction action) { + switch (action) { + case MTLStoreActionDontCare: + return StoreAction::kDontCare; + case MTLStoreActionStore: + return StoreAction::kStore; + default: + break; + } + return StoreAction::kDontCare; +} + constexpr MTLClearColor ToMTLClearColor(const Color& color) { return MTLClearColorMake(color.red, color.green, color.blue, color.alpha); } +RenderPassDescriptor FromMTLRenderPassDescriptor(MTLRenderPassDescriptor*); + MTLRenderPipelineColorAttachmentDescriptor* ToMTLRenderPipelineColorAttachmentDescriptor( ColorAttachmentDescriptor descriptor); diff --git a/impeller/compositor/formats_metal.mm b/impeller/compositor/formats_metal.mm index d5411ba008c00..f83a054a02b8b 100644 --- a/impeller/compositor/formats_metal.mm +++ b/impeller/compositor/formats_metal.mm @@ -4,6 +4,10 @@ #include "impeller/compositor/formats_metal.h" +#include + +#include "impeller/compositor/render_pass.h" + namespace impeller { MTLRenderPipelineColorAttachmentDescriptor* @@ -73,4 +77,69 @@ return des; } +static bool ConfigureRenderPassAttachment( + RenderPassAttachment& attachment, + MTLRenderPassAttachmentDescriptor* desc) { + if (!attachment) { + return false; + } + + if (!desc.texture) { + return false; + } + + attachment.texture = std::make_shared(desc.texture); + attachment.load_action = FromMTLLoadAction(desc.loadAction); + attachment.store_action = FromMTLStoreAction(desc.storeAction); + + return true; +} + +static ColorRenderPassAttachment FromMTLRenderPassColorAttachmentDescriptor( + MTLRenderPassColorAttachmentDescriptor* desc) { + ColorRenderPassAttachment attachment; + ConfigureRenderPassAttachment(attachment, desc); + auto clear = desc.clearColor; + attachment.clear_color = + Color{clear.red, clear.green, clear.blue, clear.alpha}; + return attachment; +} + +static DepthRenderPassAttachment FromMTLRenderPassDepthAttachmentDescriptor( + MTLRenderPassDepthAttachmentDescriptor* desc) { + DepthRenderPassAttachment attachment; + ConfigureRenderPassAttachment(attachment, desc); + attachment.clear_depth = desc.clearDepth; + return attachment; +} + +static StencilRenderPassAttachment FromMTLRenderPassStencilAttachmentDescriptor( + MTLRenderPassStencilAttachmentDescriptor* desc) { + StencilRenderPassAttachment attachment; + ConfigureRenderPassAttachment(attachment, desc); + attachment.clear_stencil = desc.clearStencil; + return attachment; +} + +RenderPassDescriptor FromMTLRenderPassDescriptor( + MTLRenderPassDescriptor* desc) { + RenderPassDescriptor result; + if (!desc) { + return result; + } + + // From https://developer.apple.com/metal/Metal-Feature-Set-Tables.pdf + constexpr size_t kMaxPossibleColorAttachments = 8u; + for (size_t i = 0; i < kMaxPossibleColorAttachments; i++) { + result.SetColorAttachment( + FromMTLRenderPassColorAttachmentDescriptor(desc.colorAttachments[i]), + i); + } + result.SetDepthAttachment( + FromMTLRenderPassDepthAttachmentDescriptor(desc.depthAttachment)); + result.SetStencilAttachment( + FromMTLRenderPassStencilAttachmentDescriptor(desc.stencilAttachment)); + return result; +} + } // namespace impeller diff --git a/impeller/compositor/render_pass.h b/impeller/compositor/render_pass.h index c01cbbb56dc2a..b5ab8e713f86b 100644 --- a/impeller/compositor/render_pass.h +++ b/impeller/compositor/render_pass.h @@ -43,6 +43,8 @@ class RenderPassDescriptor { ~RenderPassDescriptor(); + bool HasColorAttachment(size_t index) const; + RenderPassDescriptor& SetColorAttachment(ColorRenderPassAttachment attachment, size_t index); diff --git a/impeller/compositor/render_pass.mm b/impeller/compositor/render_pass.mm index 06d9c81b98f6b..0850266035c00 100644 --- a/impeller/compositor/render_pass.mm +++ b/impeller/compositor/render_pass.mm @@ -13,6 +13,13 @@ RenderPassDescriptor::~RenderPassDescriptor() = default; +bool RenderPassDescriptor::HasColorAttachment(size_t index) const { + if (auto found = colors_.find(index); found != colors_.end()) { + return true; + } + return false; +} + RenderPassDescriptor& RenderPassDescriptor::SetColorAttachment( ColorRenderPassAttachment attachment, size_t index) { diff --git a/impeller/compositor/renderer.h b/impeller/compositor/renderer.h index bf199e6fb73b6..7c1cc9df44fa7 100644 --- a/impeller/compositor/renderer.h +++ b/impeller/compositor/renderer.h @@ -4,24 +4,25 @@ #pragma once +#include + #include #include "flutter/fml/macros.h" #include "impeller/compositor/context.h" -#include "impeller/compositor/surface.h" #include "impeller/geometry/size.h" namespace impeller { +class Surface; + class Renderer { public: virtual ~Renderer(); bool IsValid() const; - bool SurfaceSizeDidChange(Size size); - - bool Render(); + bool Render(const Surface& surface); std::shared_ptr GetContext() const; @@ -30,16 +31,12 @@ class Renderer { virtual bool OnRender() = 0; - virtual bool OnSurfaceSizeDidChange(Size size) = 0; - private: + dispatch_semaphore_t frames_in_flight_sema_ = nullptr; std::shared_ptr context_; - std::unique_ptr surface_; Size size_; bool is_valid_ = false; - bool ShouldRender() const; - FML_DISALLOW_COPY_AND_ASSIGN(Renderer); }; diff --git a/impeller/compositor/renderer.mm b/impeller/compositor/renderer.mm index 6cba3970752e1..6ca114d36a149 100644 --- a/impeller/compositor/renderer.mm +++ b/impeller/compositor/renderer.mm @@ -5,20 +5,20 @@ #include "impeller/compositor/renderer.h" #include "flutter/fml/logging.h" +#include "impeller/compositor/command_buffer.h" +#include "impeller/compositor/surface.h" namespace impeller { +constexpr size_t kMaxFramesInFlight = 3u; + Renderer::Renderer(std::string shaders_directory) - : context_(std::make_shared(std::move(shaders_directory))), - surface_(std::make_unique(context_)) { + : frames_in_flight_sema_(::dispatch_semaphore_create(kMaxFramesInFlight)), + context_(std::make_shared(std::move(shaders_directory))) { if (!context_->IsValid()) { return; } - if (!surface_->IsValid()) { - return; - } - is_valid_ = true; } @@ -28,26 +28,28 @@ return is_valid_; } -bool Renderer::ShouldRender() const { - return IsValid() && !size_.IsZero(); -} - -bool Renderer::SurfaceSizeDidChange(Size size) { - if (size_ == size) { - return true; +bool Renderer::Render(const Surface& surface) { + if (!IsValid()) { + return false; } - size_ = size; + if (!surface.IsValid()) { + return false; + } - return OnSurfaceSizeDidChange(size_); -} + auto command_buffer = context_->CreateRenderCommandBuffer(); -bool Renderer::Render() { - if (!ShouldRender()) { + if (!command_buffer) { return false; } - return surface_->Render(); + ::dispatch_semaphore_wait(frames_in_flight_sema_, DISPATCH_TIME_FOREVER); + + command_buffer->Commit( + [sema = frames_in_flight_sema_](CommandBuffer::CommitResult) { + ::dispatch_semaphore_signal(sema); + }); + return true; } std::shared_ptr Renderer::GetContext() const { diff --git a/impeller/compositor/surface.h b/impeller/compositor/surface.h index fed78ce1528f1..496443cbeed5e 100644 --- a/impeller/compositor/surface.h +++ b/impeller/compositor/surface.h @@ -4,28 +4,26 @@ #pragma once -#include +#include #include #include "flutter/fml/macros.h" #include "impeller/compositor/context.h" +#include "impeller/compositor/render_pass.h" namespace impeller { class Surface { public: - Surface(std::shared_ptr context); + Surface(RenderPassDescriptor desc); ~Surface(); bool IsValid() const; - bool Render() const; - private: - std::shared_ptr context_; - dispatch_semaphore_t frames_in_flight_sema_ = nullptr; + RenderPassDescriptor desc_; bool is_valid_ = false; FML_DISALLOW_COPY_AND_ASSIGN(Surface); diff --git a/impeller/compositor/surface.mm b/impeller/compositor/surface.mm index adbd20c1d511f..009333a1373da 100644 --- a/impeller/compositor/surface.mm +++ b/impeller/compositor/surface.mm @@ -5,19 +5,13 @@ #include "impeller/compositor/surface.h" #include "flutter/fml/logging.h" -#include "impeller/compositor/command_buffer.h" namespace impeller { -constexpr size_t kMaxFramesInFlight = 3u; - -Surface::Surface(std::shared_ptr context) - : context_(std::move(context)), - frames_in_flight_sema_(::dispatch_semaphore_create(kMaxFramesInFlight)) { - if (!context_ || !context_->IsValid()) { +Surface::Surface(RenderPassDescriptor desc) { + if (desc.HasColorAttachment(0)) { return; } - is_valid_ = true; } @@ -27,25 +21,4 @@ return is_valid_; } -bool Surface::Render() const { - if (!IsValid()) { - return false; - } - - auto command_buffer = context_->CreateRenderCommandBuffer(); - - if (!command_buffer) { - return false; - } - - ::dispatch_semaphore_wait(frames_in_flight_sema_, DISPATCH_TIME_FOREVER); - - command_buffer->Commit( - [sema = frames_in_flight_sema_](CommandBuffer::CommitResult) { - ::dispatch_semaphore_signal(sema); - }); - - return true; -} - } // namespace impeller diff --git a/impeller/compositor/texture.h b/impeller/compositor/texture.h index 8e7c55c5b6217..7827c98c94a4d 100644 --- a/impeller/compositor/texture.h +++ b/impeller/compositor/texture.h @@ -12,7 +12,7 @@ namespace impeller { class Texture { public: - Texture(); + Texture(id texture); ~Texture(); diff --git a/impeller/compositor/texture.mm b/impeller/compositor/texture.mm index dc9ea9130cd4d..69e1d4a8d664c 100644 --- a/impeller/compositor/texture.mm +++ b/impeller/compositor/texture.mm @@ -6,7 +6,7 @@ namespace impeller { -Texture::Texture() = default; +Texture::Texture(id texture) : texture_(texture) {} Texture::~Texture() = default; diff --git a/impeller/entity/entity_renderer.h b/impeller/entity/entity_renderer.h index 07113c77865ff..629ef14ed6112 100644 --- a/impeller/entity/entity_renderer.h +++ b/impeller/entity/entity_renderer.h @@ -21,8 +21,6 @@ class EntityRenderer final : public Renderer { bool OnRender() override; - bool OnSurfaceSizeDidChange(Size size) override; - FML_DISALLOW_COPY_AND_ASSIGN(EntityRenderer); }; diff --git a/impeller/entity/entity_renderer.mm b/impeller/entity/entity_renderer.mm index 914e364f20fda..161a857dfe8d9 100644 --- a/impeller/entity/entity_renderer.mm +++ b/impeller/entity/entity_renderer.mm @@ -8,7 +8,9 @@ EntityRenderer::EntityRenderer(std::string shaders_directory) : Renderer(std::move(shaders_directory)), - root_(std::make_shared()) {} + root_(std::make_shared()) { + root_->SetBackgroundColor(Color::DarkGray()); +} EntityRenderer::~EntityRenderer() = default; @@ -16,8 +18,4 @@ return false; } -bool EntityRenderer::OnSurfaceSizeDidChange(Size size) { - return false; -} - } // namespace impeller diff --git a/impeller/geometry/size.h b/impeller/geometry/size.h index 25faa9600e73a..0ff85993fa485 100644 --- a/impeller/geometry/size.h +++ b/impeller/geometry/size.h @@ -12,41 +12,45 @@ struct Size { double width = 0.0; double height = 0.0; - Size() {} + constexpr Size() {} - Size(double width, double height) : width(width), height(height) {} + constexpr Size(double width, double height) : width(width), height(height) {} /* * Operator overloads */ - Size operator*(double scale) const { return {width * scale, height * scale}; } + constexpr Size operator*(double scale) const { + return {width * scale, height * scale}; + } - bool operator==(const Size& s) const { + constexpr bool operator==(const Size& s) const { return s.width == width && s.height == height; } - bool operator!=(const Size& s) const { + constexpr bool operator!=(const Size& s) const { return s.width != width || s.height != height; } - Size operator+(const Size& s) const { + constexpr Size operator+(const Size& s) const { return {width + s.width, height + s.height}; } - Size operator-(const Size& s) const { + constexpr Size operator-(const Size& s) const { return {width - s.width, height - s.height}; } - Size Union(const Size& o) const { + constexpr Size Union(const Size& o) const { return { std::max(width, o.width), std::max(height, o.height), }; } - bool IsZero() const { return width * height == 0.0; } + constexpr bool IsZero() const { return width * height == 0.0; } + + constexpr bool IsPositive() const { return width * height > 0.0; } - bool IsPositive() const { return width > 0.0 && height > 0.0; } + constexpr bool IsEmpty() { return !IsPositive(); } std::string ToString() const; diff --git a/impeller/host/impeller_renderer.mm b/impeller/host/impeller_renderer.mm index 1617193e8634f..c9e654f11d2fb 100644 --- a/impeller/host/impeller_renderer.mm +++ b/impeller/host/impeller_renderer.mm @@ -8,6 +8,8 @@ #include "assets_location.h" #include "flutter/fml/logging.h" +#include "impeller/compositor/formats_metal.h" +#include "impeller/compositor/surface.h" #include "impeller/entity/entity_renderer.h" #include "impeller/primitives/box_primitive.h" #include "impeller_renderer.h" @@ -53,11 +55,7 @@ - (nonnull instancetype)initWithMetalKitView:(nonnull MTKView*)view { renderer_ = std::make_unique( impeller::ImpellerShadersDirectory()); - - if (!renderer_->IsValid()) { - FML_LOG(ERROR) << "Impeller Renderer is not valid."; - } - + FML_CHECK(renderer_->IsValid()) << "Impeller Renderer is not valid."; return self; } @@ -227,7 +225,9 @@ - (void)_updateGameState { } - (void)drawInMTKView:(nonnull MTKView*)view { - if (!renderer_->Render()) { + impeller::Surface surface( + impeller::FromMTLRenderPassDescriptor(view.currentRenderPassDescriptor)); + if (!renderer_->Render(surface)) { FML_LOG(ERROR) << "Could not render."; } /// Per frame updates here @@ -304,8 +304,6 @@ - (void)drawInMTKView:(nonnull MTKView*)view { } - (void)mtkView:(nonnull MTKView*)view drawableSizeWillChange:(CGSize)size { - renderer_->SurfaceSizeDidChange({size.width, size.height}); - float aspect = size.width / (float)size.height; _projectionMatrix = matrix_perspective_right_hand(65.0f * (M_PI / 180.0f), aspect, 0.1f, 100.0f); From bd3974a4e9049948d912bf05113adec1d7001281 Mon Sep 17 00:00:00 2001 From: Chinmay Garde Date: Sun, 6 Jun 2021 15:36:45 -0700 Subject: [PATCH 066/433] Add constexpr decorations to impeller::Rect. --- impeller/geometry/rect.h | 24 ++++++++++++------------ 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/impeller/geometry/rect.h b/impeller/geometry/rect.h index 7a16acfb06381..7439e1896e746 100644 --- a/impeller/geometry/rect.h +++ b/impeller/geometry/rect.h @@ -14,52 +14,52 @@ struct Rect { Point origin; Size size; - Rect() : origin({0.0, 0.0}), size({0.0, 0.0}) {} + constexpr Rect() : origin({0.0, 0.0}), size({0.0, 0.0}) {} - Rect(Size size) : origin({0.0, 0.0}), size(size) {} + constexpr Rect(Size size) : origin({0.0, 0.0}), size(size) {} - Rect(Point origin, Size size) : origin(origin), size(size) {} + constexpr Rect(Point origin, Size size) : origin(origin), size(size) {} - Rect(const double components[4]) + constexpr Rect(const double components[4]) : origin(components[0], components[1]), size(components[2], components[3]) {} - Rect(double x, double y, double width, double height) + constexpr Rect(double x, double y, double width, double height) : origin(x, y), size(width, height) {} /* * Operator overloads */ - Rect operator+(const Rect& r) const { + constexpr Rect operator+(const Rect& r) const { return Rect({origin.x + r.origin.x, origin.y + r.origin.y}, {size.width + r.size.width, size.height + r.size.height}); } - Rect operator-(const Rect& r) const { + constexpr Rect operator-(const Rect& r) const { return Rect({origin.x - r.origin.x, origin.y - r.origin.y}, {size.width - r.size.width, size.height - r.size.height}); } - Rect operator*(double scale) const { + constexpr Rect operator*(double scale) const { return Rect({origin.x * scale, origin.y * scale}, {size.width * scale, size.height * scale}); } - Rect operator*(const Rect& r) const { + constexpr Rect operator*(const Rect& r) const { return Rect({origin.x * r.origin.x, origin.y * r.origin.y}, {size.width * r.size.width, size.height * r.size.height}); } - bool operator==(const Rect& r) const { + constexpr bool operator==(const Rect& r) const { return origin == r.origin && size == r.size; } - bool Contains(const Point& p) const { + constexpr bool Contains(const Point& p) const { return p.x >= origin.x && p.x <= size.width && p.y >= origin.y && p.y <= size.height; } - bool IsZero() const { return size.IsZero(); } + constexpr bool IsZero() const { return size.IsZero(); } Rect WithPoint(const Point& p) const; From 09944dbd839915edbc0ba515c52d9c057f1bc7e8 Mon Sep 17 00:00:00 2001 From: Chinmay Garde Date: Sun, 6 Jun 2021 16:29:48 -0700 Subject: [PATCH 067/433] Multi-encodable render passes. --- impeller/compositor/formats_metal.mm | 11 +---------- impeller/compositor/render_pass.h | 5 ++++- impeller/compositor/render_pass.mm | 24 +++++++++++++++--------- impeller/compositor/renderer.mm | 7 +++++++ impeller/compositor/surface.h | 2 ++ impeller/compositor/surface.mm | 8 ++++++-- impeller/compositor/texture.h | 3 +++ impeller/compositor/texture.mm | 5 +++++ impeller/geometry/size.h | 13 +++++++++++++ impeller/host/impeller_renderer.mm | 1 + 10 files changed, 57 insertions(+), 22 deletions(-) diff --git a/impeller/compositor/formats_metal.mm b/impeller/compositor/formats_metal.mm index f83a054a02b8b..69a2919226116 100644 --- a/impeller/compositor/formats_metal.mm +++ b/impeller/compositor/formats_metal.mm @@ -80,19 +80,10 @@ static bool ConfigureRenderPassAttachment( RenderPassAttachment& attachment, MTLRenderPassAttachmentDescriptor* desc) { - if (!attachment) { - return false; - } - - if (!desc.texture) { - return false; - } - attachment.texture = std::make_shared(desc.texture); attachment.load_action = FromMTLLoadAction(desc.loadAction); attachment.store_action = FromMTLStoreAction(desc.storeAction); - - return true; + return attachment; } static ColorRenderPassAttachment FromMTLRenderPassColorAttachmentDescriptor( diff --git a/impeller/compositor/render_pass.h b/impeller/compositor/render_pass.h index b5ab8e713f86b..2c3bda7d0ab94 100644 --- a/impeller/compositor/render_pass.h +++ b/impeller/compositor/render_pass.h @@ -68,10 +68,13 @@ class RenderPass { bool IsValid() const; + bool Encode() const; + private: friend class CommandBuffer; - id pass_ = nil; + id buffer_ = nil; + MTLRenderPassDescriptor* desc_ = nil; bool is_valid_ = false; RenderPass(id buffer, const RenderPassDescriptor& desc); diff --git a/impeller/compositor/render_pass.mm b/impeller/compositor/render_pass.mm index 0850266035c00..a84f645c7fdaa 100644 --- a/impeller/compositor/render_pass.mm +++ b/impeller/compositor/render_pass.mm @@ -114,17 +114,11 @@ static bool ConfigureStencilAttachment( } RenderPass::RenderPass(id buffer, - const RenderPassDescriptor& desc) { - auto descriptor = desc.ToMTLRenderPassDescriptor(); - if (!descriptor) { + const RenderPassDescriptor& desc) + : buffer_(buffer), desc_(desc.ToMTLRenderPassDescriptor()) { + if (!buffer_ || !desc_) { return; } - - pass_ = [buffer renderCommandEncoderWithDescriptor:descriptor]; - if (!pass_) { - return; - } - is_valid_ = true; } @@ -134,4 +128,16 @@ static bool ConfigureStencilAttachment( return is_valid_; } +bool RenderPass::Encode() const { + if (!IsValid()) { + return false; + } + auto pass = [buffer_ renderCommandEncoderWithDescriptor:desc_]; + if (!pass) { + return false; + } + [pass endEncoding]; + return true; +} + } // namespace impeller diff --git a/impeller/compositor/renderer.mm b/impeller/compositor/renderer.mm index 6ca114d36a149..942e1c5af480c 100644 --- a/impeller/compositor/renderer.mm +++ b/impeller/compositor/renderer.mm @@ -43,6 +43,13 @@ return false; } + auto render_pass = + command_buffer->CreateRenderPass(surface.GetRenderPassDescriptor()); + if (!render_pass) { + return false; + } + render_pass->Encode(); + ::dispatch_semaphore_wait(frames_in_flight_sema_, DISPATCH_TIME_FOREVER); command_buffer->Commit( diff --git a/impeller/compositor/surface.h b/impeller/compositor/surface.h index 496443cbeed5e..f4e01d25fa938 100644 --- a/impeller/compositor/surface.h +++ b/impeller/compositor/surface.h @@ -22,6 +22,8 @@ class Surface { bool IsValid() const; + const RenderPassDescriptor& GetRenderPassDescriptor() const; + private: RenderPassDescriptor desc_; bool is_valid_ = false; diff --git a/impeller/compositor/surface.mm b/impeller/compositor/surface.mm index 009333a1373da..b88c450d3db21 100644 --- a/impeller/compositor/surface.mm +++ b/impeller/compositor/surface.mm @@ -8,8 +8,8 @@ namespace impeller { -Surface::Surface(RenderPassDescriptor desc) { - if (desc.HasColorAttachment(0)) { +Surface::Surface(RenderPassDescriptor desc) : desc_(std::move(desc)) { + if (!desc_.HasColorAttachment(0)) { return; } is_valid_ = true; @@ -21,4 +21,8 @@ return is_valid_; } +const RenderPassDescriptor& Surface::GetRenderPassDescriptor() const { + return desc_; +} + } // namespace impeller diff --git a/impeller/compositor/texture.h b/impeller/compositor/texture.h index 7827c98c94a4d..e986184bcae7a 100644 --- a/impeller/compositor/texture.h +++ b/impeller/compositor/texture.h @@ -7,6 +7,7 @@ #include #include "flutter/fml/macros.h" +#include "impeller/geometry/size.h" namespace impeller { @@ -16,6 +17,8 @@ class Texture { ~Texture(); + Size GetSize() const; + id GetMTLTexture() const; private: diff --git a/impeller/compositor/texture.mm b/impeller/compositor/texture.mm index 69e1d4a8d664c..fec9cf47c6c7c 100644 --- a/impeller/compositor/texture.mm +++ b/impeller/compositor/texture.mm @@ -10,6 +10,11 @@ Texture::~Texture() = default; +Size Texture::GetSize() const { + return {static_cast(texture_.width), + static_cast(texture_.height)}; +} + id Texture::GetMTLTexture() const { return texture_; } diff --git a/impeller/geometry/size.h b/impeller/geometry/size.h index 0ff85993fa485..6df32feeb8f24 100644 --- a/impeller/geometry/size.h +++ b/impeller/geometry/size.h @@ -4,6 +4,7 @@ #pragma once +#include #include namespace impeller { @@ -16,6 +17,11 @@ struct Size { constexpr Size(double width, double height) : width(width), height(height) {} + static constexpr Size Infinite() { + return Size{std::numeric_limits::max(), + std::numeric_limits::max()}; + } + /* * Operator overloads */ @@ -46,6 +52,13 @@ struct Size { }; } + constexpr Size Intersection(const Size& o) const { + return { + std::min(width, o.width), + std::min(height, o.height), + }; + } + constexpr bool IsZero() const { return width * height == 0.0; } constexpr bool IsPositive() const { return width * height > 0.0; } diff --git a/impeller/host/impeller_renderer.mm b/impeller/host/impeller_renderer.mm index c9e654f11d2fb..d792bd9d7792e 100644 --- a/impeller/host/impeller_renderer.mm +++ b/impeller/host/impeller_renderer.mm @@ -230,6 +230,7 @@ - (void)drawInMTKView:(nonnull MTKView*)view { if (!renderer_->Render(surface)) { FML_LOG(ERROR) << "Could not render."; } + /// Per frame updates here dispatch_semaphore_wait(_inFlightSemaphore, DISPATCH_TIME_FOREVER); From 3ec157c2bef3d4539c8ee9d63cf91169947a56ac Mon Sep 17 00:00:00 2001 From: Chinmay Garde Date: Mon, 7 Jun 2021 12:30:14 -0700 Subject: [PATCH 068/433] Allow render passes to record commands. --- impeller/compositor/buffer.h | 4 ++-- impeller/compositor/command.h | 2 ++ impeller/compositor/pipeline.h | 3 +++ impeller/compositor/pipeline.mm | 10 ++++++++-- impeller/compositor/render_pass.h | 5 +++++ impeller/compositor/render_pass.mm | 9 +++++++++ 6 files changed, 29 insertions(+), 4 deletions(-) diff --git a/impeller/compositor/buffer.h b/impeller/compositor/buffer.h index a098721638595..39f534e61bdbb 100644 --- a/impeller/compositor/buffer.h +++ b/impeller/compositor/buffer.h @@ -28,6 +28,8 @@ class BufferView final : public BufferBase { // |BufferBase| ~BufferView() = default; + const BufferBase* GetBuffer() const { return parent_.get(); } + // |BufferBase| uint8_t* GetMapping() const override { return parent_->GetMapping() + range_in_parent_.offset; @@ -44,8 +46,6 @@ class BufferView final : public BufferBase { BufferView(std::shared_ptr parent, Range range_in_parent) : parent_(std::move(parent)), range_in_parent_(range_in_parent) {} - - FML_DISALLOW_COPY_AND_ASSIGN(BufferView); }; class Buffer { diff --git a/impeller/compositor/command.h b/impeller/compositor/command.h index c4d0d8da36836..68c10dbc7ae2d 100644 --- a/impeller/compositor/command.h +++ b/impeller/compositor/command.h @@ -27,6 +27,8 @@ struct Command { Bindings vertex_bindings; Bindings fragment_bindings; BufferView index_buffer; + + constexpr operator bool() const { return pipeline && pipeline->IsValid(); } }; } // namespace impeller diff --git a/impeller/compositor/pipeline.h b/impeller/compositor/pipeline.h index 064a2d04d73ba..0fef90ac03d9e 100644 --- a/impeller/compositor/pipeline.h +++ b/impeller/compositor/pipeline.h @@ -21,12 +21,15 @@ class Pipeline { ~Pipeline(); + bool IsValid() const; + private: friend class PipelineLibrary; Type type_ = Type::kUnknown; id state_; id depth_stencil_state_; + bool is_valid_ = false; Pipeline(id state, id depth_stencil_state); diff --git a/impeller/compositor/pipeline.mm b/impeller/compositor/pipeline.mm index f14bfa8d16d61..94954f850a4a2 100644 --- a/impeller/compositor/pipeline.mm +++ b/impeller/compositor/pipeline.mm @@ -9,11 +9,17 @@ Pipeline::Pipeline(id state, id depth_stencil_state) : state_(state), depth_stencil_state_(depth_stencil_state) { - if (state_ != nil) { - type_ = Type::kRender; + if (!state_) { + return; } + type_ = Type::kRender; + is_valid_ = true; } Pipeline::~Pipeline() = default; +bool Pipeline::IsValid() const { + return is_valid_; +} + } // namespace impeller diff --git a/impeller/compositor/render_pass.h b/impeller/compositor/render_pass.h index 2c3bda7d0ab94..811e56bb940a1 100644 --- a/impeller/compositor/render_pass.h +++ b/impeller/compositor/render_pass.h @@ -7,8 +7,10 @@ #include #include +#include #include "flutter/fml/macros.h" +#include "impeller/compositor/command.h" #include "impeller/compositor/formats.h" #include "impeller/compositor/texture.h" #include "impeller/geometry/color.h" @@ -68,6 +70,8 @@ class RenderPass { bool IsValid() const; + bool Record(Command command); + bool Encode() const; private: @@ -75,6 +79,7 @@ class RenderPass { id buffer_ = nil; MTLRenderPassDescriptor* desc_ = nil; + std::vector commands_; bool is_valid_ = false; RenderPass(id buffer, const RenderPassDescriptor& desc); diff --git a/impeller/compositor/render_pass.mm b/impeller/compositor/render_pass.mm index a84f645c7fdaa..e697a9d615e9d 100644 --- a/impeller/compositor/render_pass.mm +++ b/impeller/compositor/render_pass.mm @@ -140,4 +140,13 @@ static bool ConfigureStencilAttachment( return true; } +bool RenderPass::Record(Command command) { + if (!command) { + return false; + } + + commands_.emplace_back(std::move(command)); + return true; +} + } // namespace impeller From a714936ac5a36c12c38594e11b8275cf60e9369d Mon Sep 17 00:00:00 2001 From: Chinmay Garde Date: Mon, 7 Jun 2021 12:36:01 -0700 Subject: [PATCH 069/433] Constexpr correctness for colors. --- impeller/geometry/color.h | 310 +++++++++++++++++++------------------- 1 file changed, 156 insertions(+), 154 deletions(-) diff --git a/impeller/geometry/color.h b/impeller/geometry/color.h index 1d7372feb089e..2b442d114c3ef 100644 --- a/impeller/geometry/color.h +++ b/impeller/geometry/color.h @@ -35,599 +35,601 @@ struct Color { */ double alpha = 0.0; - Color() {} + constexpr Color() {} Color(const ColorHSB& hsbColor); - Color(double r, double g, double b, double a) + constexpr Color(double r, double g, double b, double a) : red(r), green(g), blue(b), alpha(a) {} - bool operator==(const Color& c) const { + constexpr bool operator==(const Color& c) const { return red == c.red && green == c.green && blue == c.blue && alpha == c.alpha; } - Color operator+(const Color& other) const; - std::string ToString() const; void FromString(const std::string& str); - static Color White() { return {1.0, 1.0, 1.0, 1.0}; } + static constexpr Color White() { return {1.0, 1.0, 1.0, 1.0}; } - static Color Black() { return {0.0, 0.0, 0.0, 1.0}; } + static constexpr Color Black() { return {0.0, 0.0, 0.0, 1.0}; } - static Color WhiteTransparent() { return {1.0, 1.0, 1.0, 0.0}; } + static constexpr Color WhiteTransparent() { return {1.0, 1.0, 1.0, 0.0}; } - static Color BlackTransparent() { return {0.0, 0.0, 0.0, 0.0}; } + static constexpr Color BlackTransparent() { return {0.0, 0.0, 0.0, 0.0}; } - static Color Red() { return {1.0, 0.0, 0.0, 1.0}; } + static constexpr Color Red() { return {1.0, 0.0, 0.0, 1.0}; } - static Color Green() { return {0.0, 1.0, 0.0, 1.0}; } + static constexpr Color Green() { return {0.0, 1.0, 0.0, 1.0}; } - static Color Blue() { return {0.0, 0.0, 1.0, 1.0}; } + static constexpr Color Blue() { return {0.0, 0.0, 1.0, 1.0}; } - static Color AliceBlue() { + static constexpr Color AliceBlue() { return {240.0 / 255.0, 248.0 / 255.0, 255.0 / 255.0, 1.0}; } - static Color AntiqueWhite() { + static constexpr Color AntiqueWhite() { return {250.0 / 255.0, 235.0 / 255.0, 215.0 / 255.0, 1.0}; } - static Color Aqua() { + static constexpr Color Aqua() { return {0.0 / 255.0, 255.0 / 255.0, 255.0 / 255.0, 1.0}; } - static Color AquaMarine() { + static constexpr Color AquaMarine() { return {127.0 / 255.0, 255.0 / 255.0, 212.0 / 255.0, 1.0}; } - static Color Azure() { + static constexpr Color Azure() { return {240.0 / 255.0, 255.0 / 255.0, 255.0 / 255.0, 1.0}; } - static Color Beige() { + static constexpr Color Beige() { return {245.0 / 255.0, 245.0 / 255.0, 220.0 / 255.0, 1.0}; } - static Color Bisque() { + static constexpr Color Bisque() { return {255.0 / 255.0, 228.0 / 255.0, 196.0 / 255.0, 1.0}; } - static Color BlanchedAlmond() { + static constexpr Color BlanchedAlmond() { return {255.0 / 255.0, 235.0 / 255.0, 205.0 / 255.0, 1.0}; } - static Color BlueViolet() { + static constexpr Color BlueViolet() { return {138.0 / 255.0, 43.0 / 255.0, 226.0 / 255.0, 1.0}; } - static Color Brown() { + static constexpr Color Brown() { return {165.0 / 255.0, 42.0 / 255.0, 42.0 / 255.0, 1.0}; } - static Color BurlyWood() { + static constexpr Color BurlyWood() { return {222.0 / 255.0, 184.0 / 255.0, 135.0 / 255.0, 1.0}; } - static Color CadetBlue() { + static constexpr Color CadetBlue() { return {95.0 / 255.0, 158.0 / 255.0, 160.0 / 255.0, 1.0}; } - static Color Chartreuse() { + static constexpr Color Chartreuse() { return {127.0 / 255.0, 255.0 / 255.0, 0.0 / 255.0, 1.0}; } - static Color Chocolate() { + static constexpr Color Chocolate() { return {210.0 / 255.0, 105.0 / 255.0, 30.0 / 255.0, 1.0}; } - static Color Coral() { + static constexpr Color Coral() { return {255.0 / 255.0, 127.0 / 255.0, 80.0 / 255.0, 1.0}; } - static Color CornflowerBlue() { + static constexpr Color CornflowerBlue() { return {100.0 / 255.0, 149.0 / 255.0, 237.0 / 255.0, 1.0}; } - static Color Cornsilk() { + static constexpr Color Cornsilk() { return {255.0 / 255.0, 248.0 / 255.0, 220.0 / 255.0, 1.0}; } - static Color Crimson() { + static constexpr Color Crimson() { return {220.0 / 255.0, 20.0 / 255.0, 60.0 / 255.0, 1.0}; } - static Color Cyan() { + static constexpr Color Cyan() { return {0.0 / 255.0, 255.0 / 255.0, 255.0 / 255.0, 1.0}; } - static Color DarkBlue() { + static constexpr Color DarkBlue() { return {0.0 / 255.0, 0.0 / 255.0, 139.0 / 255.0, 1.0}; } - static Color DarkCyan() { + static constexpr Color DarkCyan() { return {0.0 / 255.0, 139.0 / 255.0, 139.0 / 255.0, 1.0}; } - static Color DarkGoldenrod() { + static constexpr Color DarkGoldenrod() { return {184.0 / 255.0, 134.0 / 255.0, 11.0 / 255.0, 1.0}; } - static Color DarkGray() { + static constexpr Color DarkGray() { return {169.0 / 255.0, 169.0 / 255.0, 169.0 / 255.0, 1.0}; } - static Color DarkGreen() { + static constexpr Color DarkGreen() { return {0.0 / 255.0, 100.0 / 255.0, 0.0 / 255.0, 1.0}; } - static Color DarkGrey() { + static constexpr Color DarkGrey() { return {169.0 / 255.0, 169.0 / 255.0, 169.0 / 255.0, 1.0}; } - static Color DarkKhaki() { + static constexpr Color DarkKhaki() { return {189.0 / 255.0, 183.0 / 255.0, 107.0 / 255.0, 1.0}; } - static Color DarkMagenta() { + static constexpr Color DarkMagenta() { return {139.0 / 255.0, 0.0 / 255.0, 139.0 / 255.0, 1.0}; } - static Color DarkOliveGreen() { + static constexpr Color DarkOliveGreen() { return {85.0 / 255.0, 107.0 / 255.0, 47.0 / 255.0, 1.0}; } - static Color DarkOrange() { + static constexpr Color DarkOrange() { return {255.0 / 255.0, 140.0 / 255.0, 0.0 / 255.0, 1.0}; } - static Color DarkOrchid() { + static constexpr Color DarkOrchid() { return {153.0 / 255.0, 50.0 / 255.0, 204.0 / 255.0, 1.0}; } - static Color DarkRed() { + static constexpr Color DarkRed() { return {139.0 / 255.0, 0.0 / 255.0, 0.0 / 255.0, 1.0}; } - static Color DarkSalmon() { + static constexpr Color DarkSalmon() { return {233.0 / 255.0, 150.0 / 255.0, 122.0 / 255.0, 1.0}; } - static Color DarkSeagreen() { + static constexpr Color DarkSeagreen() { return {143.0 / 255.0, 188.0 / 255.0, 143.0 / 255.0, 1.0}; } - static Color DarkSlateBlue() { + static constexpr Color DarkSlateBlue() { return {72.0 / 255.0, 61.0 / 255.0, 139.0 / 255.0, 1.0}; } - static Color DarkSlateGray() { + static constexpr Color DarkSlateGray() { return {47.0 / 255.0, 79.0 / 255.0, 79.0 / 255.0, 1.0}; } - static Color DarkSlateGrey() { + static constexpr Color DarkSlateGrey() { return {47.0 / 255.0, 79.0 / 255.0, 79.0 / 255.0, 1.0}; } - static Color DarkTurquoise() { + static constexpr Color DarkTurquoise() { return {0.0 / 255.0, 206.0 / 255.0, 209.0 / 255.0, 1.0}; } - static Color DarkViolet() { + static constexpr Color DarkViolet() { return {148.0 / 255.0, 0.0 / 255.0, 211.0 / 255.0, 1.0}; } - static Color DeepPink() { + static constexpr Color DeepPink() { return {255.0 / 255.0, 20.0 / 255.0, 147.0 / 255.0, 1.0}; } - static Color DeepSkyBlue() { + static constexpr Color DeepSkyBlue() { return {0.0 / 255.0, 191.0 / 255.0, 255.0 / 255.0, 1.0}; } - static Color DimGray() { + static constexpr Color DimGray() { return {105.0 / 255.0, 105.0 / 255.0, 105.0 / 255.0, 1.0}; } - static Color DimGrey() { + static constexpr Color DimGrey() { return {105.0 / 255.0, 105.0 / 255.0, 105.0 / 255.0, 1.0}; } - static Color DodgerBlue() { + static constexpr Color DodgerBlue() { return {30.0 / 255.0, 144.0 / 255.0, 255.0 / 255.0, 1.0}; } - static Color Firebrick() { + static constexpr Color Firebrick() { return {178.0 / 255.0, 34.0 / 255.0, 34.0 / 255.0, 1.0}; } - static Color FloralWhite() { + static constexpr Color FloralWhite() { return {255.0 / 255.0, 250.0 / 255.0, 240.0 / 255.0, 1.0}; } - static Color ForestGreen() { + static constexpr Color ForestGreen() { return {34.0 / 255.0, 139.0 / 255.0, 34.0 / 255.0, 1.0}; } - static Color Fuchsia() { + static constexpr Color Fuchsia() { return {255.0 / 255.0, 0.0 / 255.0, 255.0 / 255.0, 1.0}; } - static Color Gainsboro() { + static constexpr Color Gainsboro() { return {220.0 / 255.0, 220.0 / 255.0, 220.0 / 255.0, 1.0}; } - static Color Ghostwhite() { + static constexpr Color Ghostwhite() { return {248.0 / 255.0, 248.0 / 255.0, 255.0 / 255.0, 1.0}; } - static Color Gold() { + static constexpr Color Gold() { return {255.0 / 255.0, 215.0 / 255.0, 0.0 / 255.0, 1.0}; } - static Color Goldenrod() { + static constexpr Color Goldenrod() { return {218.0 / 255.0, 165.0 / 255.0, 32.0 / 255.0, 1.0}; } - static Color Gray() { + static constexpr Color Gray() { return {128.0 / 255.0, 128.0 / 255.0, 128.0 / 255.0, 1.0}; } - static Color GreenYellow() { + static constexpr Color GreenYellow() { return {173.0 / 255.0, 255.0 / 255.0, 47.0 / 255.0, 1.0}; } - static Color Grey() { + static constexpr Color Grey() { return {128.0 / 255.0, 128.0 / 255.0, 128.0 / 255.0, 1.0}; } - static Color Honeydew() { + static constexpr Color Honeydew() { return {240.0 / 255.0, 255.0 / 255.0, 240.0 / 255.0, 1.0}; } - static Color HotPink() { + static constexpr Color HotPink() { return {255.0 / 255.0, 105.0 / 255.0, 180.0 / 255.0, 1.0}; } - static Color IndianRed() { + static constexpr Color IndianRed() { return {205.0 / 255.0, 92.0 / 255.0, 92.0 / 255.0, 1.0}; } - static Color Indigo() { + static constexpr Color Indigo() { return {75.0 / 255.0, 0.0 / 255.0, 130.0 / 255.0, 1.0}; } - static Color Ivory() { + static constexpr Color Ivory() { return {255.0 / 255.0, 255.0 / 255.0, 240.0 / 255.0, 1.0}; } - static Color Khaki() { + static constexpr Color Khaki() { return {240.0 / 255.0, 230.0 / 255.0, 140.0 / 255.0, 1.0}; } - static Color Lavender() { + static constexpr Color Lavender() { return {230.0 / 255.0, 230.0 / 255.0, 250.0 / 255.0, 1.0}; } - static Color LavenderBlush() { + static constexpr Color LavenderBlush() { return {255.0 / 255.0, 240.0 / 255.0, 245.0 / 255.0, 1.0}; } - static Color LawnGreen() { + static constexpr Color LawnGreen() { return {124.0 / 255.0, 252.0 / 255.0, 0.0 / 255.0, 1.0}; } - static Color LemonChiffon() { + static constexpr Color LemonChiffon() { return {255.0 / 255.0, 250.0 / 255.0, 205.0 / 255.0, 1.0}; } - static Color LightBlue() { + static constexpr Color LightBlue() { return {173.0 / 255.0, 216.0 / 255.0, 230.0 / 255.0, 1.0}; } - static Color LightCoral() { + static constexpr Color LightCoral() { return {240.0 / 255.0, 128.0 / 255.0, 128.0 / 255.0, 1.0}; } - static Color LightCyan() { + static constexpr Color LightCyan() { return {224.0 / 255.0, 255.0 / 255.0, 255.0 / 255.0, 1.0}; } - static Color LightGoldenrodYellow() { + static constexpr Color LightGoldenrodYellow() { return {50.0 / 255.0, 250.0 / 255.0, 210.0 / 255.0, 1.0}; } - static Color LightGray() { + static constexpr Color LightGray() { return {211.0 / 255.0, 211.0 / 255.0, 211.0 / 255.0, 1.0}; } - static Color LightGreen() { + static constexpr Color LightGreen() { return {144.0 / 255.0, 238.0 / 255.0, 144.0 / 255.0, 1.0}; } - static Color LightGrey() { + static constexpr Color LightGrey() { return {211.0 / 255.0, 211.0 / 255.0, 211.0 / 255.0, 1.0}; } - static Color LightPink() { + static constexpr Color LightPink() { return {255.0 / 255.0, 182.0 / 255.0, 193.0 / 255.0, 1.0}; } - static Color LightSalmon() { + static constexpr Color LightSalmon() { return {255.0 / 255.0, 160.0 / 255.0, 122.0 / 255.0, 1.0}; } - static Color LightSeaGreen() { + static constexpr Color LightSeaGreen() { return {32.0 / 255.0, 178.0 / 255.0, 170.0 / 255.0, 1.0}; } - static Color LightSkyBlue() { + static constexpr Color LightSkyBlue() { return {135.0 / 255.0, 206.0 / 255.0, 250.0 / 255.0, 1.0}; } - static Color LightSlateGray() { + static constexpr Color LightSlateGray() { return {119.0 / 255.0, 136.0 / 255.0, 153.0 / 255.0, 1.0}; } - static Color LightSlateGrey() { + static constexpr Color LightSlateGrey() { return {119.0 / 255.0, 136.0 / 255.0, 153.0 / 255.0, 1.0}; } - static Color LightSteelBlue() { + static constexpr Color LightSteelBlue() { return {176.0 / 255.0, 196.0 / 255.0, 222.0 / 255.0, 1.0}; } - static Color LightYellow() { + static constexpr Color LightYellow() { return {255.0 / 255.0, 255.0 / 255.0, 224.0 / 255.0, 1.0}; } - static Color Lime() { return {0.0 / 255.0, 255.0 / 255.0, 0.0 / 255.0, 1.0}; } + static constexpr Color Lime() { + return {0.0 / 255.0, 255.0 / 255.0, 0.0 / 255.0, 1.0}; + } - static Color LimeGreen() { + static constexpr Color LimeGreen() { return {50.0 / 255.0, 205.0 / 255.0, 50.0 / 255.0, 1.0}; } - static Color Linen() { + static constexpr Color Linen() { return {250.0 / 255.0, 240.0 / 255.0, 230.0 / 255.0, 1.0}; } - static Color Magenta() { + static constexpr Color Magenta() { return {255.0 / 255.0, 0.0 / 255.0, 255.0 / 255.0, 1.0}; } - static Color Maroon() { + static constexpr Color Maroon() { return {128.0 / 255.0, 0.0 / 255.0, 0.0 / 255.0, 1.0}; } - static Color MediumAquamarine() { + static constexpr Color MediumAquamarine() { return {102.0 / 255.0, 205.0 / 255.0, 170.0 / 255.0, 1.0}; } - static Color MediumBlue() { + static constexpr Color MediumBlue() { return {0.0 / 255.0, 0.0 / 255.0, 205.0 / 255.0, 1.0}; } - static Color MediumOrchid() { + static constexpr Color MediumOrchid() { return {186.0 / 255.0, 85.0 / 255.0, 211.0 / 255.0, 1.0}; } - static Color MediumPurple() { + static constexpr Color MediumPurple() { return {147.0 / 255.0, 112.0 / 255.0, 219.0 / 255.0, 1.0}; } - static Color MediumSeagreen() { + static constexpr Color MediumSeagreen() { return {60.0 / 255.0, 179.0 / 255.0, 113.0 / 255.0, 1.0}; } - static Color MediumSlateBlue() { + static constexpr Color MediumSlateBlue() { return {123.0 / 255.0, 104.0 / 255.0, 238.0 / 255.0, 1.0}; } - static Color MediumSpringGreen() { + static constexpr Color MediumSpringGreen() { return {0.0 / 255.0, 250.0 / 255.0, 154.0 / 255.0, 1.0}; } - static Color MediumTurquoise() { + static constexpr Color MediumTurquoise() { return {72.0 / 255.0, 209.0 / 255.0, 204.0 / 255.0, 1.0}; } - static Color MediumVioletRed() { + static constexpr Color MediumVioletRed() { return {199.0 / 255.0, 21.0 / 255.0, 133.0 / 255.0, 1.0}; } - static Color MidnightBlue() { + static constexpr Color MidnightBlue() { return {25.0 / 255.0, 25.0 / 255.0, 112.0 / 255.0, 1.0}; } - static Color MintCream() { + static constexpr Color MintCream() { return {245.0 / 255.0, 255.0 / 255.0, 250.0 / 255.0, 1.0}; } - static Color MistyRose() { + static constexpr Color MistyRose() { return {255.0 / 255.0, 228.0 / 255.0, 225.0 / 255.0, 1.0}; } - static Color Moccasin() { + static constexpr Color Moccasin() { return {255.0 / 255.0, 228.0 / 255.0, 181.0 / 255.0, 1.0}; } - static Color NavajoWhite() { + static constexpr Color NavajoWhite() { return {255.0 / 255.0, 222.0 / 255.0, 173.0 / 255.0, 1.0}; } - static Color Navy() { return {0.0 / 255.0, 0.0 / 255.0, 128.0 / 255.0, 1.0}; } + static constexpr Color Navy() { + return {0.0 / 255.0, 0.0 / 255.0, 128.0 / 255.0, 1.0}; + } - static Color OldLace() { + static constexpr Color OldLace() { return {253.0 / 255.0, 245.0 / 255.0, 230.0 / 255.0, 1.0}; } - static Color Olive() { + static constexpr Color Olive() { return {128.0 / 255.0, 128.0 / 255.0, 0.0 / 255.0, 1.0}; } - static Color OliveDrab() { + static constexpr Color OliveDrab() { return {107.0 / 255.0, 142.0 / 255.0, 35.0 / 255.0, 1.0}; } - static Color Orange() { + static constexpr Color Orange() { return {255.0 / 255.0, 165.0 / 255.0, 0.0 / 255.0, 1.0}; } - static Color OrangeRed() { + static constexpr Color OrangeRed() { return {255.0 / 255.0, 69.0 / 255.0, 0.0 / 255.0, 1.0}; } - static Color Orchid() { + static constexpr Color Orchid() { return {218.0 / 255.0, 112.0 / 255.0, 214.0 / 255.0, 1.0}; } - static Color PaleGoldenrod() { + static constexpr Color PaleGoldenrod() { return {238.0 / 255.0, 232.0 / 255.0, 170.0 / 255.0, 1.0}; } - static Color PaleGreen() { + static constexpr Color PaleGreen() { return {152.0 / 255.0, 251.0 / 255.0, 152.0 / 255.0, 1.0}; } - static Color PaleTurquoise() { + static constexpr Color PaleTurquoise() { return {175.0 / 255.0, 238.0 / 255.0, 238.0 / 255.0, 1.0}; } - static Color PaleVioletRed() { + static constexpr Color PaleVioletRed() { return {219.0 / 255.0, 112.0 / 255.0, 147.0 / 255.0, 1.0}; } - static Color PapayaWhip() { + static constexpr Color PapayaWhip() { return {255.0 / 255.0, 239.0 / 255.0, 213.0 / 255.0, 1.0}; } - static Color Peachpuff() { + static constexpr Color Peachpuff() { return {255.0 / 255.0, 218.0 / 255.0, 185.0 / 255.0, 1.0}; } - static Color Peru() { + static constexpr Color Peru() { return {205.0 / 255.0, 133.0 / 255.0, 63.0 / 255.0, 1.0}; } - static Color Pink() { + static constexpr Color Pink() { return {255.0 / 255.0, 192.0 / 255.0, 203.0 / 255.0, 1.0}; } - static Color Plum() { + static constexpr Color Plum() { return {221.0 / 255.0, 160.0 / 255.0, 221.0 / 255.0, 1.0}; } - static Color PowderBlue() { + static constexpr Color PowderBlue() { return {176.0 / 255.0, 224.0 / 255.0, 230.0 / 255.0, 1.0}; } - static Color Purple() { + static constexpr Color Purple() { return {128.0 / 255.0, 0.0 / 255.0, 128.0 / 255.0, 1.0}; } - static Color RosyBrown() { + static constexpr Color RosyBrown() { return {188.0 / 255.0, 143.0 / 255.0, 143.0 / 255.0, 1.0}; } - static Color RoyalBlue() { + static constexpr Color RoyalBlue() { return {65.0 / 255.0, 105.0 / 255.0, 225.0 / 255.0, 1.0}; } - static Color SaddleBrown() { + static constexpr Color SaddleBrown() { return {139.0 / 255.0, 69.0 / 255.0, 19.0 / 255.0, 1.0}; } - static Color Salmon() { + static constexpr Color Salmon() { return {250.0 / 255.0, 128.0 / 255.0, 114.0 / 255.0, 1.0}; } - static Color SandyBrown() { + static constexpr Color SandyBrown() { return {244.0 / 255.0, 164.0 / 255.0, 96.0 / 255.0, 1.0}; } - static Color Seagreen() { + static constexpr Color Seagreen() { return {46.0 / 255.0, 139.0 / 255.0, 87.0 / 255.0, 1.0}; } - static Color Seashell() { + static constexpr Color Seashell() { return {255.0 / 255.0, 245.0 / 255.0, 238.0 / 255.0, 1.0}; } - static Color Sienna() { + static constexpr Color Sienna() { return {160.0 / 255.0, 82.0 / 255.0, 45.0 / 255.0, 1.0}; } - static Color Silver() { + static constexpr Color Silver() { return {192.0 / 255.0, 192.0 / 255.0, 192.0 / 255.0, 1.0}; } - static Color SkyBlue() { + static constexpr Color SkyBlue() { return {135.0 / 255.0, 206.0 / 255.0, 235.0 / 255.0, 1.0}; } - static Color SlateBlue() { + static constexpr Color SlateBlue() { return {106.0 / 255.0, 90.0 / 255.0, 205.0 / 255.0, 1.0}; } - static Color SlateGray() { + static constexpr Color SlateGray() { return {112.0 / 255.0, 128.0 / 255.0, 144.0 / 255.0, 1.0}; } - static Color SlateGrey() { + static constexpr Color SlateGrey() { return {112.0 / 255.0, 128.0 / 255.0, 144.0 / 255.0, 1.0}; } - static Color Snow() { + static constexpr Color Snow() { return {255.0 / 255.0, 250.0 / 255.0, 250.0 / 255.0, 1.0}; } - static Color SpringGreen() { + static constexpr Color SpringGreen() { return {0.0 / 255.0, 255.0 / 255.0, 127.0 / 255.0, 1.0}; } - static Color SteelBlue() { + static constexpr Color SteelBlue() { return {70.0 / 255.0, 130.0 / 255.0, 180.0 / 255.0, 1.0}; } - static Color Tan() { + static constexpr Color Tan() { return {210.0 / 255.0, 180.0 / 255.0, 140.0 / 255.0, 1.0}; } - static Color Teal() { + static constexpr Color Teal() { return {0.0 / 255.0, 128.0 / 255.0, 128.0 / 255.0, 1.0}; } - static Color Thistle() { + static constexpr Color Thistle() { return {216.0 / 255.0, 191.0 / 255.0, 216.0 / 255.0, 1.0}; } - static Color Tomato() { + static constexpr Color Tomato() { return {255.0 / 255.0, 99.0 / 255.0, 71.0 / 255.0, 1.0}; } - static Color Turquoise() { + static constexpr Color Turquoise() { return {64.0 / 255.0, 224.0 / 255.0, 208.0 / 255.0, 1.0}; } - static Color Violet() { + static constexpr Color Violet() { return {238.0 / 255.0, 130.0 / 255.0, 238.0 / 255.0, 1.0}; } - static Color Wheat() { + static constexpr Color Wheat() { return {245.0 / 255.0, 222.0 / 255.0, 179.0 / 255.0, 1.0}; } - static Color Whitesmoke() { + static constexpr Color Whitesmoke() { return {245.0 / 255.0, 245.0 / 255.0, 245.0 / 255.0, 1.0}; } - static Color Yellow() { + static constexpr Color Yellow() { return {255.0 / 255.0, 255.0 / 255.0, 0.0 / 255.0, 1.0}; } - static Color YellowGreen() { + static constexpr Color YellowGreen() { return {154.0 / 255.0, 205.0 / 255.0, 50.0 / 255.0, 1.0}; } }; From ab0258b4aaa123171ed0a9cd170d9f110b077cf3 Mon Sep 17 00:00:00 2001 From: Chinmay Garde Date: Mon, 7 Jun 2021 15:05:42 -0700 Subject: [PATCH 070/433] WIP on buffer management. --- impeller/compositor/BUILD.gn | 6 +++ impeller/compositor/allocator.mm | 1 + impeller/compositor/buffer.h | 53 -------------------- impeller/compositor/buffer.mm | 74 +-------------------------- impeller/compositor/buffer_view.h | 0 impeller/compositor/buffer_view.mm | 0 impeller/compositor/command.h | 3 +- impeller/compositor/command.mm | 8 ++- impeller/compositor/device_buffer.h | 36 +++++++++++++ impeller/compositor/device_buffer.mm | 17 +++++++ impeller/compositor/host_buffer.h | 46 +++++++++++++++++ impeller/compositor/host_buffer.mm | 75 ++++++++++++++++++++++++++++ impeller/compositor/pipeline.h | 6 ++- impeller/compositor/pipeline.mm | 12 ++++- impeller/compositor/render_pass.h | 2 + impeller/compositor/render_pass.mm | 48 +++++++++++++++++- 16 files changed, 255 insertions(+), 132 deletions(-) create mode 100644 impeller/compositor/buffer_view.h create mode 100644 impeller/compositor/buffer_view.mm create mode 100644 impeller/compositor/device_buffer.h create mode 100644 impeller/compositor/device_buffer.mm create mode 100644 impeller/compositor/host_buffer.h create mode 100644 impeller/compositor/host_buffer.mm diff --git a/impeller/compositor/BUILD.gn b/impeller/compositor/BUILD.gn index 2288e13e96441..23bd49504e170 100644 --- a/impeller/compositor/BUILD.gn +++ b/impeller/compositor/BUILD.gn @@ -10,6 +10,8 @@ impeller_component("compositor") { "allocator.mm", "buffer.h", "buffer.mm", + "buffer_view.h", + "buffer_view.mm", "command.h", "command.mm", "command_buffer.h", @@ -18,10 +20,14 @@ impeller_component("compositor") { "comparable.h", "context.h", "context.mm", + "device_buffer.h", + "device_buffer.mm", "formats.cc", "formats.h", "formats_metal.h", "formats_metal.mm", + "host_buffer.h", + "host_buffer.mm", "pipeline.h", "pipeline.mm", "pipeline_descriptor.h", diff --git a/impeller/compositor/allocator.mm b/impeller/compositor/allocator.mm index 496160ef08143..27b86c9b2113b 100644 --- a/impeller/compositor/allocator.mm +++ b/impeller/compositor/allocator.mm @@ -5,6 +5,7 @@ #include "impeller/compositor/allocator.h" #include "impeller/compositor/buffer.h" +#include "impeller/compositor/device_buffer.h" namespace impeller { diff --git a/impeller/compositor/buffer.h b/impeller/compositor/buffer.h index 39f534e61bdbb..824a96ba3752b 100644 --- a/impeller/compositor/buffer.h +++ b/impeller/compositor/buffer.h @@ -28,8 +28,6 @@ class BufferView final : public BufferBase { // |BufferBase| ~BufferView() = default; - const BufferBase* GetBuffer() const { return parent_.get(); } - // |BufferBase| uint8_t* GetMapping() const override { return parent_->GetMapping() + range_in_parent_.offset; @@ -48,55 +46,4 @@ class BufferView final : public BufferBase { : parent_(std::move(parent)), range_in_parent_(range_in_parent) {} }; -class Buffer { - public: - ~Buffer(); - - private: - friend class Allocator; - - const id buffer_; - const size_t size_; - const StorageMode mode_; - const std::string label_; - - Buffer(id buffer, - size_t size, - StorageMode mode, - std::string label); - - FML_DISALLOW_COPY_AND_ASSIGN(Buffer); -}; - -class HostBuffer : public std::enable_shared_from_this, - public BufferBase { - public: - std::shared_ptr Create(); - - std::shared_ptr Emplace(size_t length); - - virtual ~HostBuffer(); - - // |BufferBase| - uint8_t* GetMapping() const override { return buffer_; } - - // |BufferBase| - size_t GetLength() const override { return length_; } - - [[nodiscard]] bool Truncate(size_t length); - - private: - uint8_t* buffer_ = nullptr; - size_t length_ = 0; - size_t reserved_ = 0; - - [[nodiscard]] bool Reserve(size_t reserved); - - [[nodiscard]] bool ReserveNPOT(size_t reserved); - - HostBuffer(); - - FML_DISALLOW_COPY_AND_ASSIGN(HostBuffer); -}; - } // namespace impeller diff --git a/impeller/compositor/buffer.mm b/impeller/compositor/buffer.mm index cb9ba12e05ac9..9e4c11e91a15d 100644 --- a/impeller/compositor/buffer.mm +++ b/impeller/compositor/buffer.mm @@ -4,80 +4,8 @@ #include "impeller/compositor/buffer.h" -#include "flutter/fml/logging.h" - namespace impeller { -Buffer::Buffer(id buffer, - size_t size, - StorageMode mode, - std::string label) - : buffer_(buffer), size_(size), mode_(mode), label_(std::move(label)) {} - -Buffer::~Buffer() = default; - -std::shared_ptr HostBuffer::Create() { - return std::shared_ptr(new HostBuffer()); -} - -HostBuffer::HostBuffer() = default; - -HostBuffer::~HostBuffer() { - ::free(buffer_); -} - -std::shared_ptr HostBuffer::Emplace(size_t length) { - auto old_length = length_; - if (!Truncate(length_ + length)) { - return nullptr; - } - return std::shared_ptr( - new BufferView(shared_from_this(), Range{old_length, length})); -} - -bool HostBuffer::Truncate(size_t length) { - if (!ReserveNPOT(length)) { - return false; - } - length_ = length; - return true; -} - -static uint32_t NextPowerOfTwoSize(uint32_t x) { - if (x == 0) { - return 1; - } - - --x; - - x |= x >> 1; - x |= x >> 2; - x |= x >> 4; - x |= x >> 8; - x |= x >> 16; - - return x + 1; -} - -bool HostBuffer::ReserveNPOT(size_t reserved) { - return Reserve(NextPowerOfTwoSize(reserved)); -} - -bool HostBuffer::Reserve(size_t reserved) { - if (reserved == reserved_) { - return true; - } - auto new_allocation = ::realloc(buffer_, reserved); - if (!new_allocation) { - // If new length is zero, a minimum non-zero sized allocation is returned. - // So this check will not trip and this routine will indicate success as - // expected. - FML_LOG(ERROR) << "Allocation failed. Out of memory."; - return false; - } - buffer_ = static_cast(new_allocation); - reserved_ = reserved; - return true; -} +// } // namespace impeller diff --git a/impeller/compositor/buffer_view.h b/impeller/compositor/buffer_view.h new file mode 100644 index 0000000000000..e69de29bb2d1d diff --git a/impeller/compositor/buffer_view.mm b/impeller/compositor/buffer_view.mm new file mode 100644 index 0000000000000..e69de29bb2d1d diff --git a/impeller/compositor/command.h b/impeller/compositor/command.h index 68c10dbc7ae2d..1def25cd977d7 100644 --- a/impeller/compositor/command.h +++ b/impeller/compositor/command.h @@ -19,7 +19,7 @@ class Sampler; struct Bindings { std::map buffers; std::map> textures; - std::map> samplers; + std::map> samplers; }; struct Command { @@ -27,6 +27,7 @@ struct Command { Bindings vertex_bindings; Bindings fragment_bindings; BufferView index_buffer; + size_t index_count = 0u; constexpr operator bool() const { return pipeline && pipeline->IsValid(); } }; diff --git a/impeller/compositor/command.mm b/impeller/compositor/command.mm index 95f37c799ff8f..7170c57f8450f 100644 --- a/impeller/compositor/command.mm +++ b/impeller/compositor/command.mm @@ -4,4 +4,10 @@ #include "impeller/compositor/command.h" -namespace impeller {} // namespace impeller +#include + +namespace impeller { + +// + +} // namespace impeller diff --git a/impeller/compositor/device_buffer.h b/impeller/compositor/device_buffer.h new file mode 100644 index 0000000000000..a5741b752c298 --- /dev/null +++ b/impeller/compositor/device_buffer.h @@ -0,0 +1,36 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#pragma once + +#include + +#include + +#include "flutter/fml/macros.h" +#include "impeller/compositor/allocator.h" + +namespace impeller { + +class Buffer { + public: + ~Buffer(); + + private: + friend class Allocator; + + const id buffer_; + const size_t size_; + const StorageMode mode_; + const std::string label_; + + Buffer(id buffer, + size_t size, + StorageMode mode, + std::string label); + + FML_DISALLOW_COPY_AND_ASSIGN(Buffer); +}; + +} // namespace impeller diff --git a/impeller/compositor/device_buffer.mm b/impeller/compositor/device_buffer.mm new file mode 100644 index 0000000000000..e54cea72fa10f --- /dev/null +++ b/impeller/compositor/device_buffer.mm @@ -0,0 +1,17 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "impeller/compositor/device_buffer.h" + +namespace impeller { + +Buffer::Buffer(id buffer, + size_t size, + StorageMode mode, + std::string label) + : buffer_(buffer), size_(size), mode_(mode), label_(std::move(label)) {} + +Buffer::~Buffer() = default; + +} // namespace impeller diff --git a/impeller/compositor/host_buffer.h b/impeller/compositor/host_buffer.h new file mode 100644 index 0000000000000..f2a192023c596 --- /dev/null +++ b/impeller/compositor/host_buffer.h @@ -0,0 +1,46 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#pragma once + +#include + +#include "flutter/fml/macros.h" +#include "impeller/compositor/buffer.h" +#include "impeller/compositor/host_buffer.h" + +namespace impeller { + +class HostBuffer : public std::enable_shared_from_this, + public BufferBase { + public: + std::shared_ptr Create(); + + std::shared_ptr Emplace(size_t length); + + virtual ~HostBuffer(); + + // |BufferBase| + uint8_t* GetMapping() const override { return buffer_; } + + // |BufferBase| + size_t GetLength() const override { return length_; } + + [[nodiscard]] bool Truncate(size_t length); + + private: + uint8_t* buffer_ = nullptr; + size_t length_ = 0; + size_t reserved_ = 0; + + [[nodiscard]] bool Reserve(size_t reserved); + + [[nodiscard]] bool ReserveNPOT(size_t reserved); + + HostBuffer(); + + FML_DISALLOW_COPY_AND_ASSIGN(HostBuffer); +}; + +} // namespace impeller diff --git a/impeller/compositor/host_buffer.mm b/impeller/compositor/host_buffer.mm new file mode 100644 index 0000000000000..1925c4a9c4ddc --- /dev/null +++ b/impeller/compositor/host_buffer.mm @@ -0,0 +1,75 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "impeller/compositor/host_buffer.h" + +#include "flutter/fml/logging.h" + +namespace impeller { + +std::shared_ptr HostBuffer::Create() { + return std::shared_ptr(new HostBuffer()); +} + +HostBuffer::HostBuffer() = default; + +HostBuffer::~HostBuffer() { + ::free(buffer_); +} + +std::shared_ptr HostBuffer::Emplace(size_t length) { + auto old_length = length_; + if (!Truncate(length_ + length)) { + return nullptr; + } + return std::shared_ptr( + new BufferView(shared_from_this(), Range{old_length, length})); +} + +bool HostBuffer::Truncate(size_t length) { + if (!ReserveNPOT(length)) { + return false; + } + length_ = length; + return true; +} + +static uint32_t NextPowerOfTwoSize(uint32_t x) { + if (x == 0) { + return 1; + } + + --x; + + x |= x >> 1; + x |= x >> 2; + x |= x >> 4; + x |= x >> 8; + x |= x >> 16; + + return x + 1; +} + +bool HostBuffer::ReserveNPOT(size_t reserved) { + return Reserve(NextPowerOfTwoSize(reserved)); +} + +bool HostBuffer::Reserve(size_t reserved) { + if (reserved == reserved_) { + return true; + } + auto new_allocation = ::realloc(buffer_, reserved); + if (!new_allocation) { + // If new length is zero, a minimum non-zero sized allocation is returned. + // So this check will not trip and this routine will indicate success as + // expected. + FML_LOG(ERROR) << "Allocation failed. Out of memory."; + return false; + } + buffer_ = static_cast(new_allocation); + reserved_ = reserved; + return true; +} + +} // namespace impeller diff --git a/impeller/compositor/pipeline.h b/impeller/compositor/pipeline.h index 0fef90ac03d9e..af95b46478750 100644 --- a/impeller/compositor/pipeline.h +++ b/impeller/compositor/pipeline.h @@ -23,11 +23,15 @@ class Pipeline { bool IsValid() const; + id GetMTLRenderPipelineState() const; + + id GetMTLDepthStencilState() const; + private: friend class PipelineLibrary; Type type_ = Type::kUnknown; - id state_; + id pipeline_state_; id depth_stencil_state_; bool is_valid_ = false; diff --git a/impeller/compositor/pipeline.mm b/impeller/compositor/pipeline.mm index 94954f850a4a2..00edc74271326 100644 --- a/impeller/compositor/pipeline.mm +++ b/impeller/compositor/pipeline.mm @@ -8,8 +8,8 @@ Pipeline::Pipeline(id state, id depth_stencil_state) - : state_(state), depth_stencil_state_(depth_stencil_state) { - if (!state_) { + : pipeline_state_(state), depth_stencil_state_(depth_stencil_state) { + if (!pipeline_state_) { return; } type_ = Type::kRender; @@ -22,4 +22,12 @@ return is_valid_; } +id Pipeline::GetMTLRenderPipelineState() const { + return pipeline_state_; +} + +id Pipeline::GetMTLDepthStencilState() const { + return depth_stencil_state_; +} + } // namespace impeller diff --git a/impeller/compositor/render_pass.h b/impeller/compositor/render_pass.h index 811e56bb940a1..54ee77ff905f5 100644 --- a/impeller/compositor/render_pass.h +++ b/impeller/compositor/render_pass.h @@ -84,6 +84,8 @@ class RenderPass { RenderPass(id buffer, const RenderPassDescriptor& desc); + bool EncodeCommands(id pass) const; + FML_DISALLOW_COPY_AND_ASSIGN(RenderPass); }; diff --git a/impeller/compositor/render_pass.mm b/impeller/compositor/render_pass.mm index e697a9d615e9d..fe2ad25fb4b81 100644 --- a/impeller/compositor/render_pass.mm +++ b/impeller/compositor/render_pass.mm @@ -4,8 +4,10 @@ #include "impeller/compositor/render_pass.h" +#include "flutter/fml/closure.h" #include "flutter/fml/logging.h" #include "impeller/compositor/formats_metal.h" +#include "impeller/shader_glue/shader_types.h" namespace impeller { @@ -136,7 +138,51 @@ static bool ConfigureStencilAttachment( if (!pass) { return false; } - [pass endEncoding]; + // Success or failure, the pass must end. The buffer can only process one pass + // at a time. + fml::ScopedCleanupClosure auto_end([pass]() { [pass endEncoding]; }); + + return EncodeCommands(pass); +} + +void Bind(ShaderStage stage, const BufferView& view) {} + +void Bind(ShaderStage stage, const Texture& view) {} + +void Bind(ShaderStage stage, const Sampler& view) {} + +bool RenderPass::EncodeCommands(id pass) const { + // There a numerous opportunities here to ensure bindings are not repeated. + // Stuff like setting the vertex buffer bindings over and over when just the + // offsets could be updated (as recommended in best practices). + + auto bind_stage_resources = [](const Bindings& bindings, ShaderStage stage) { + for (const auto buffer : bindings.buffers) { + Bind(stage, buffer.second); + } + for (const auto texture : bindings.textures) { + Bind(stage, *texture.second); + } + for (const auto sampler : bindings.samplers) { + Bind(stage, *sampler.second); + } + }; + for (const auto& command : commands_) { + [pass setRenderPipelineState:command.pipeline->GetMTLRenderPipelineState()]; + [pass setDepthStencilState:command.pipeline->GetMTLDepthStencilState()]; + [pass setFrontFacingWinding:MTLWindingClockwise]; + [pass setCullMode:MTLCullModeBack]; + bind_stage_resources(command.vertex_bindings, ShaderStage::kVertex); + bind_stage_resources(command.fragment_bindings, ShaderStage::kFragment); + // [pass drawIndexedPrimitives:MTLPrimitiveTypeTriangleStrip + // indexCount:command.index_count + // indexType:MTLIndexTypeUInt32 + // indexBuffer:command.index_buffer + // indexBufferOffset:(NSUInteger)indexBufferOffset + // instanceCount:1u + // baseVertex:0u + // baseInstance:0u]; + } return true; } From 8ffa314b84e6c5612e6cd559c1707afc89667fe7 Mon Sep 17 00:00:00 2001 From: Chinmay Garde Date: Mon, 7 Jun 2021 17:37:18 -0700 Subject: [PATCH 071/433] Cleanup buffer management wire up transients allocator. --- impeller/compositor/allocator.h | 13 +++-- impeller/compositor/allocator.mm | 42 +++++++++++++--- impeller/compositor/buffer.h | 39 +++------------ impeller/compositor/buffer.mm | 2 +- impeller/compositor/buffer_view.h | 20 ++++++++ impeller/compositor/buffer_view.mm | 11 +++++ impeller/compositor/command.h | 2 +- impeller/compositor/context.h | 4 ++ impeller/compositor/context.mm | 13 +++++ impeller/compositor/device_buffer.h | 26 +++++++--- impeller/compositor/device_buffer.mm | 43 +++++++++++++--- impeller/compositor/host_buffer.h | 24 ++++----- impeller/compositor/host_buffer.mm | 27 ++++++++-- impeller/compositor/render_pass.h | 5 +- impeller/compositor/render_pass.mm | 74 +++++++++++++++++++++++----- impeller/compositor/renderer.mm | 2 +- 16 files changed, 256 insertions(+), 91 deletions(-) diff --git a/impeller/compositor/allocator.h b/impeller/compositor/allocator.h index 8ad4913152c61..a494b70dd8617 100644 --- a/impeller/compositor/allocator.h +++ b/impeller/compositor/allocator.h @@ -18,13 +18,18 @@ enum class StorageMode { }; class Context; -class Buffer; +class DeviceBuffer; class Allocator { public: ~Allocator(); - std::shared_ptr CreateBuffer(size_t length, std::string label); + bool IsValid() const; + + std::shared_ptr CreateBuffer(StorageMode mode, size_t length); + + std::shared_ptr CreateBufferWithCopy(const uint8_t* buffer, + size_t length); private: friend class Context; @@ -33,10 +38,10 @@ class Allocator { // MTLDevice APIs. But, in the future, this could be backed by named heaps // with specific limits. id device_; - StorageMode mode_; std::string allocator_label_; + bool is_valid_ = false; - Allocator(id device, StorageMode type, std::string label); + Allocator(id device, std::string label); FML_DISALLOW_COPY_AND_ASSIGN(Allocator); }; diff --git a/impeller/compositor/allocator.mm b/impeller/compositor/allocator.mm index 27b86c9b2113b..2cd0f4f57eb72 100644 --- a/impeller/compositor/allocator.mm +++ b/impeller/compositor/allocator.mm @@ -4,16 +4,27 @@ #include "impeller/compositor/allocator.h" +#include "flutter/fml/logging.h" #include "impeller/compositor/buffer.h" #include "impeller/compositor/device_buffer.h" namespace impeller { -Allocator::Allocator(id device, StorageMode type, std::string label) - : device_(device), mode_(type), allocator_label_(std::move(label)) {} +Allocator::Allocator(id device, std::string label) + : device_(device), allocator_label_(std::move(label)) { + if (!device_) { + return; + } + + is_valid_ = true; +} Allocator::~Allocator() = default; +bool Allocator::IsValid() const { + return is_valid_; +} + static MTLResourceOptions ResourceOptionsFromStorageType(StorageMode type) { switch (type) { case StorageMode::kHostCoherent: @@ -23,16 +34,33 @@ static MTLResourceOptions ResourceOptionsFromStorageType(StorageMode type) { } } -std::shared_ptr Allocator::CreateBuffer(size_t length, - std::string label) { +std::shared_ptr Allocator::CreateBufferWithCopy( + const uint8_t* buffer, + size_t length) { + auto new_buffer = CreateBuffer(StorageMode::kHostCoherent, length); + + if (!new_buffer) { + return nullptr; + } + + auto entire_range = Range{0, length}; + + if (!new_buffer->CopyHostBuffer(buffer, entire_range)) { + return nullptr; + } + + return new_buffer; +} + +std::shared_ptr Allocator::CreateBuffer(StorageMode mode, + size_t length) { auto buffer = [device_ newBufferWithLength:length - options:ResourceOptionsFromStorageType(mode_)]; + options:ResourceOptionsFromStorageType(mode)]; if (!buffer) { return nullptr; } - buffer.label = @(label.c_str()); - return std::shared_ptr(new Buffer(buffer, length, mode_, label)); + return std::shared_ptr(new DeviceBuffer(buffer, length, mode)); } } // namespace impeller diff --git a/impeller/compositor/buffer.h b/impeller/compositor/buffer.h index 824a96ba3752b..d29e32e1be70b 100644 --- a/impeller/compositor/buffer.h +++ b/impeller/compositor/buffer.h @@ -6,44 +6,17 @@ #include -#include "flutter/fml/macros.h" -#include "impeller/compositor/allocator.h" -#include "impeller/compositor/range.h" - namespace impeller { -class HostBuffer; - -class BufferBase { - public: - ~BufferBase() = default; - - virtual uint8_t* GetMapping() const = 0; - - virtual size_t GetLength() const = 0; -}; +class DeviceBuffer; +class Allocator; -class BufferView final : public BufferBase { +class Buffer { public: - // |BufferBase| - ~BufferView() = default; - - // |BufferBase| - uint8_t* GetMapping() const override { - return parent_->GetMapping() + range_in_parent_.offset; - } - - // |BufferBase| - size_t GetLength() const override { return range_in_parent_.length; } - - private: - friend HostBuffer; - - std::shared_ptr parent_; - Range range_in_parent_; + virtual ~Buffer(); - BufferView(std::shared_ptr parent, Range range_in_parent) - : parent_(std::move(parent)), range_in_parent_(range_in_parent) {} + virtual std::shared_ptr GetDeviceBuffer( + Allocator& allocator) const = 0; }; } // namespace impeller diff --git a/impeller/compositor/buffer.mm b/impeller/compositor/buffer.mm index 9e4c11e91a15d..fe8f4a860f922 100644 --- a/impeller/compositor/buffer.mm +++ b/impeller/compositor/buffer.mm @@ -6,6 +6,6 @@ namespace impeller { -// +Buffer::~Buffer() = default; } // namespace impeller diff --git a/impeller/compositor/buffer_view.h b/impeller/compositor/buffer_view.h index e69de29bb2d1d..aa07cf30ceff1 100644 --- a/impeller/compositor/buffer_view.h +++ b/impeller/compositor/buffer_view.h @@ -0,0 +1,20 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#pragma once + +#include "flutter/fml/macros.h" +#include "impeller/compositor/buffer.h" +#include "impeller/compositor/range.h" + +namespace impeller { + +struct BufferView { + std::shared_ptr buffer; + Range range; + + constexpr operator bool() const { return static_cast(buffer); } +}; + +} // namespace impeller diff --git a/impeller/compositor/buffer_view.mm b/impeller/compositor/buffer_view.mm index e69de29bb2d1d..c7962504f638b 100644 --- a/impeller/compositor/buffer_view.mm +++ b/impeller/compositor/buffer_view.mm @@ -0,0 +1,11 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "impeller/compositor/buffer_view.h" + +namespace impeller { + +// + +} // namespace impeller diff --git a/impeller/compositor/command.h b/impeller/compositor/command.h index 1def25cd977d7..0676c4427979b 100644 --- a/impeller/compositor/command.h +++ b/impeller/compositor/command.h @@ -8,7 +8,7 @@ #include #include "flutter/fml/macros.h" -#include "impeller/compositor/buffer.h" +#include "impeller/compositor/buffer_view.h" #include "impeller/compositor/pipeline.h" namespace impeller { diff --git a/impeller/compositor/context.h b/impeller/compositor/context.h index fa4e4b3e2de09..77dd15a826077 100644 --- a/impeller/compositor/context.h +++ b/impeller/compositor/context.h @@ -15,6 +15,7 @@ namespace impeller { class ShaderLibrary; class CommandBuffer; +class Allocator; class Context { public: @@ -24,6 +25,8 @@ class Context { bool IsValid() const; + std::shared_ptr GetTransientsAllocator() const; + std::shared_ptr GetShaderLibrary() const; std::shared_ptr GetPipelineLibrary() const; @@ -36,6 +39,7 @@ class Context { id transfer_queue_ = nullptr; std::shared_ptr shader_library_; std::shared_ptr pipeline_library_; + std::shared_ptr transients_allocator_; bool is_valid_ = false; FML_DISALLOW_COPY_AND_ASSIGN(Context); diff --git a/impeller/compositor/context.mm b/impeller/compositor/context.mm index fd8b85a6b71f0..2ec6cb2fbe68f 100644 --- a/impeller/compositor/context.mm +++ b/impeller/compositor/context.mm @@ -6,6 +6,7 @@ #include "flutter/fml/logging.h" #include "flutter/fml/paths.h" +#include "impeller/compositor/allocator.h" #include "impeller/compositor/command_buffer.h" #include "impeller/compositor/shader_library.h" @@ -54,6 +55,14 @@ std::shared_ptr(new PipelineLibrary(device_)); } + { + transients_allocator_ = std::shared_ptr( + new Allocator(device_, "Impeller Transients Allocator")); + if (!transients_allocator_) { + return; + } + } + is_valid_ = true; } @@ -84,4 +93,8 @@ return buffer; } +std::shared_ptr Context::GetTransientsAllocator() const { + return transients_allocator_; +} + } // namespace impeller diff --git a/impeller/compositor/device_buffer.h b/impeller/compositor/device_buffer.h index a5741b752c298..a484db77766c4 100644 --- a/impeller/compositor/device_buffer.h +++ b/impeller/compositor/device_buffer.h @@ -6,16 +6,26 @@ #include +#include #include #include "flutter/fml/macros.h" #include "impeller/compositor/allocator.h" +#include "impeller/compositor/buffer.h" +#include "impeller/compositor/range.h" namespace impeller { -class Buffer { +class DeviceBuffer final : public Buffer, + public std::enable_shared_from_this { public: - ~Buffer(); + ~DeviceBuffer(); + + [[nodiscard]] bool CopyHostBuffer(const uint8_t* source, + Range source_range, + size_t offset = 0u); + + id GetMTLBuffer() const; private: friend class Allocator; @@ -23,14 +33,14 @@ class Buffer { const id buffer_; const size_t size_; const StorageMode mode_; - const std::string label_; - Buffer(id buffer, - size_t size, - StorageMode mode, - std::string label); + DeviceBuffer(id buffer, size_t size, StorageMode mode); + + // |Buffer| + std::shared_ptr GetDeviceBuffer( + Allocator& allocator) const override; - FML_DISALLOW_COPY_AND_ASSIGN(Buffer); + FML_DISALLOW_COPY_AND_ASSIGN(DeviceBuffer); }; } // namespace impeller diff --git a/impeller/compositor/device_buffer.mm b/impeller/compositor/device_buffer.mm index e54cea72fa10f..7ecf61ea76d07 100644 --- a/impeller/compositor/device_buffer.mm +++ b/impeller/compositor/device_buffer.mm @@ -4,14 +4,45 @@ #include "impeller/compositor/device_buffer.h" +#include + namespace impeller { -Buffer::Buffer(id buffer, - size_t size, - StorageMode mode, - std::string label) - : buffer_(buffer), size_(size), mode_(mode), label_(std::move(label)) {} +DeviceBuffer::DeviceBuffer(id buffer, size_t size, StorageMode mode) + : buffer_(buffer), size_(size), mode_(mode) {} + +DeviceBuffer::~DeviceBuffer() = default; + +id DeviceBuffer::GetMTLBuffer() const { + return buffer_; +} + +[[nodiscard]] bool DeviceBuffer::CopyHostBuffer(const uint8_t* source, + Range source_range, + size_t offset) { + if (offset + source_range.length > size_) { + // Out of bounds of this buffer. + return false; + } + + auto dest = static_cast(buffer_.contents); + + if (!dest) { + // Probably StorageMode::kDevicePrivate. + return false; + } + + ::memcpy(dest + offset, source + source_range.offset, source_range.length); + + [buffer_ didModifyRange:NSMakeRange(offset, source_range.length)]; + + return true; +} -Buffer::~Buffer() = default; +// |Buffer| +std::shared_ptr DeviceBuffer::GetDeviceBuffer( + Allocator& allocator) const { + return shared_from_this(); +} } // namespace impeller diff --git a/impeller/compositor/host_buffer.h b/impeller/compositor/host_buffer.h index f2a192023c596..e643b44935cf6 100644 --- a/impeller/compositor/host_buffer.h +++ b/impeller/compositor/host_buffer.h @@ -8,31 +8,33 @@ #include "flutter/fml/macros.h" #include "impeller/compositor/buffer.h" -#include "impeller/compositor/host_buffer.h" +#include "impeller/compositor/buffer_view.h" namespace impeller { -class HostBuffer : public std::enable_shared_from_this, - public BufferBase { +class HostBuffer final : public std::enable_shared_from_this, + public Buffer { public: - std::shared_ptr Create(); - - std::shared_ptr Emplace(size_t length); - + // |Buffer| virtual ~HostBuffer(); - // |BufferBase| - uint8_t* GetMapping() const override { return buffer_; } + static std::shared_ptr Create(); - // |BufferBase| - size_t GetLength() const override { return length_; } + BufferView Emplace(const void* buffer, size_t length); [[nodiscard]] bool Truncate(size_t length); private: + mutable std::shared_ptr device_buffer_; uint8_t* buffer_ = nullptr; size_t length_ = 0; size_t reserved_ = 0; + size_t generation_ = 1u; + mutable size_t device_buffer_generation_ = 0u; + + // |Buffer| + std::shared_ptr GetDeviceBuffer( + Allocator& allocator) const override; [[nodiscard]] bool Reserve(size_t reserved); diff --git a/impeller/compositor/host_buffer.mm b/impeller/compositor/host_buffer.mm index 1925c4a9c4ddc..c381fc72555bc 100644 --- a/impeller/compositor/host_buffer.mm +++ b/impeller/compositor/host_buffer.mm @@ -6,6 +6,9 @@ #include "flutter/fml/logging.h" +#include "impeller/compositor/allocator.h" +#include "impeller/compositor/buffer_view.h" + namespace impeller { std::shared_ptr HostBuffer::Create() { @@ -18,13 +21,15 @@ ::free(buffer_); } -std::shared_ptr HostBuffer::Emplace(size_t length) { +BufferView HostBuffer::Emplace(const void* buffer, size_t length) { auto old_length = length_; if (!Truncate(length_ + length)) { - return nullptr; + return {}; } - return std::shared_ptr( - new BufferView(shared_from_this(), Range{old_length, length})); + FML_DCHECK(buffer_); + generation_++; + ::memmove(buffer_, buffer, length); + return BufferView{shared_from_this(), Range{old_length, length}}; } bool HostBuffer::Truncate(size_t length) { @@ -72,4 +77,18 @@ static uint32_t NextPowerOfTwoSize(uint32_t x) { return true; } +std::shared_ptr HostBuffer::GetDeviceBuffer( + Allocator& allocator) const { + if (generation_ == device_buffer_generation_) { + return device_buffer_; + } + auto new_buffer = allocator.CreateBufferWithCopy(buffer_, length_); + if (!new_buffer) { + return nullptr; + } + device_buffer_generation_ = generation_; + device_buffer_ = std::move(new_buffer); + return device_buffer_; +} + } // namespace impeller diff --git a/impeller/compositor/render_pass.h b/impeller/compositor/render_pass.h index 54ee77ff905f5..f97bd1550fcaa 100644 --- a/impeller/compositor/render_pass.h +++ b/impeller/compositor/render_pass.h @@ -72,7 +72,7 @@ class RenderPass { bool Record(Command command); - bool Encode() const; + bool Encode(Allocator& transients_allocator) const; private: friend class CommandBuffer; @@ -84,7 +84,8 @@ class RenderPass { RenderPass(id buffer, const RenderPassDescriptor& desc); - bool EncodeCommands(id pass) const; + bool EncodeCommands(Allocator& transients_allocator, + id pass) const; FML_DISALLOW_COPY_AND_ASSIGN(RenderPass); }; diff --git a/impeller/compositor/render_pass.mm b/impeller/compositor/render_pass.mm index fe2ad25fb4b81..d9d5604e4184e 100644 --- a/impeller/compositor/render_pass.mm +++ b/impeller/compositor/render_pass.mm @@ -6,6 +6,7 @@ #include "flutter/fml/closure.h" #include "flutter/fml/logging.h" +#include "impeller/compositor/device_buffer.h" #include "impeller/compositor/formats_metal.h" #include "impeller/shader_glue/shader_types.h" @@ -130,7 +131,7 @@ static bool ConfigureStencilAttachment( return is_valid_; } -bool RenderPass::Encode() const { +bool RenderPass::Encode(Allocator& transients_allocator) const { if (!IsValid()) { return false; } @@ -142,38 +143,85 @@ static bool ConfigureStencilAttachment( // at a time. fml::ScopedCleanupClosure auto_end([pass]() { [pass endEncoding]; }); - return EncodeCommands(pass); + return EncodeCommands(transients_allocator, pass); } -void Bind(ShaderStage stage, const BufferView& view) {} +static bool Bind(id pass, + Allocator& allocator, + ShaderStage stage, + size_t bind_index, + const BufferView& view) { + if (!view.buffer) { + return false; + } + + auto device_buffer = view.buffer->GetDeviceBuffer(allocator); + if (!device_buffer) { + return false; + } -void Bind(ShaderStage stage, const Texture& view) {} + auto buffer = device_buffer->GetMTLBuffer(); + // The Metal call is a void return and we don't want to make it on nil. + if (!buffer) { + return false; + } -void Bind(ShaderStage stage, const Sampler& view) {} + [pass setVertexBuffer:buffer offset:view.range.offset atIndex:bind_index]; + return true; +} -bool RenderPass::EncodeCommands(id pass) const { +static bool Bind(Allocator& allocator, + ShaderStage stage, + size_t bind_index, + const Texture& view) { + FML_CHECK(false); + return false; +} + +static bool Bind(Allocator& allocator, + ShaderStage stage, + size_t bind_index, + const Sampler& view) { + FML_CHECK(false); + return false; +} + +bool RenderPass::EncodeCommands(Allocator& allocator, + id pass) const { // There a numerous opportunities here to ensure bindings are not repeated. // Stuff like setting the vertex buffer bindings over and over when just the // offsets could be updated (as recommended in best practices). - - auto bind_stage_resources = [](const Bindings& bindings, ShaderStage stage) { + auto bind_stage_resources = [&allocator, pass](const Bindings& bindings, + ShaderStage stage) -> bool { for (const auto buffer : bindings.buffers) { - Bind(stage, buffer.second); + if (!Bind(pass, allocator, stage, buffer.first, buffer.second)) { + return false; + } } for (const auto texture : bindings.textures) { - Bind(stage, *texture.second); + if (!Bind(allocator, stage, texture.first, *texture.second)) { + return false; + } } for (const auto sampler : bindings.samplers) { - Bind(stage, *sampler.second); + if (!Bind(allocator, stage, sampler.first, *sampler.second)) { + return false; + } } + return true; }; for (const auto& command : commands_) { [pass setRenderPipelineState:command.pipeline->GetMTLRenderPipelineState()]; [pass setDepthStencilState:command.pipeline->GetMTLDepthStencilState()]; [pass setFrontFacingWinding:MTLWindingClockwise]; [pass setCullMode:MTLCullModeBack]; - bind_stage_resources(command.vertex_bindings, ShaderStage::kVertex); - bind_stage_resources(command.fragment_bindings, ShaderStage::kFragment); + if (!bind_stage_resources(command.vertex_bindings, ShaderStage::kVertex)) { + return false; + } + if (!bind_stage_resources(command.fragment_bindings, + ShaderStage::kFragment)) { + return false; + } // [pass drawIndexedPrimitives:MTLPrimitiveTypeTriangleStrip // indexCount:command.index_count // indexType:MTLIndexTypeUInt32 diff --git a/impeller/compositor/renderer.mm b/impeller/compositor/renderer.mm index 942e1c5af480c..b0b02a1ae82bb 100644 --- a/impeller/compositor/renderer.mm +++ b/impeller/compositor/renderer.mm @@ -48,7 +48,7 @@ if (!render_pass) { return false; } - render_pass->Encode(); + render_pass->Encode(*context_->GetTransientsAllocator()); ::dispatch_semaphore_wait(frames_in_flight_sema_, DISPATCH_TIME_FOREVER); From 1880d1647474874a594c18aef1da7f1022053d57 Mon Sep 17 00:00:00 2001 From: Chinmay Garde Date: Wed, 9 Jun 2021 15:50:50 -0700 Subject: [PATCH 072/433] Pipeline state objects pass validation. --- impeller/compiler/code_gen_template.h | 2 ++ impeller/compositor/formats.h | 2 ++ impeller/compositor/formats_metal.h | 4 +++ impeller/compositor/host_buffer.h | 2 +- impeller/compositor/pipeline_descriptor.h | 7 +++-- impeller/compositor/pipeline_descriptor.mm | 21 ++++++++++----- impeller/compositor/render_pass.h | 4 +-- impeller/compositor/render_pass.mm | 4 +-- impeller/compositor/renderer.h | 6 +++-- impeller/compositor/renderer.mm | 10 +++++-- impeller/entity/BUILD.gn | 1 + impeller/entity/entity_renderer.h | 9 ++++++- impeller/entity/entity_renderer.mm | 27 +++++++++++++++++-- impeller/primitives/BUILD.gn | 7 ++--- impeller/primitives/box.frag | 4 +-- impeller/primitives/box_primitive.h | 13 +++++++++ impeller/primitives/box_primitive.mm | 31 +++++++++++++++++++--- impeller/primitives/primitive.h | 5 ++++ impeller/tools/impeller.gni | 14 +++++++--- 19 files changed, 140 insertions(+), 33 deletions(-) diff --git a/impeller/compiler/code_gen_template.h b/impeller/compiler/code_gen_template.h index f958346a7df59..66eaac368686d 100644 --- a/impeller/compiler/code_gen_template.h +++ b/impeller/compiler/code_gen_template.h @@ -11,6 +11,8 @@ constexpr std::string_view kReflectionHeaderTemplate = R"~~( // THIS FILE IS GENERATED BY impellerc. // DO NOT EDIT OR CHECK THIS INTO SOURCE CONTROL +#pragma once + #include "shader_types.h" namespace impeller { diff --git a/impeller/compositor/formats.h b/impeller/compositor/formats.h index 6b40e39e9b767..1b162db588203 100644 --- a/impeller/compositor/formats.h +++ b/impeller/compositor/formats.h @@ -15,6 +15,8 @@ namespace impeller { enum class PixelFormat { kUnknown, + kPixelFormat_B8G8R8A8_UNormInt_SRGB, + kPixelFormat_D32_Float_S8_UNormInt, }; enum class BlendFactor { diff --git a/impeller/compositor/formats_metal.h b/impeller/compositor/formats_metal.h index ba99c92f2cb38..5e7126f5a3822 100644 --- a/impeller/compositor/formats_metal.h +++ b/impeller/compositor/formats_metal.h @@ -20,6 +20,10 @@ constexpr MTLPixelFormat ToMTLPixelFormat(PixelFormat format) { switch (format) { case PixelFormat::kUnknown: return MTLPixelFormatInvalid; + case PixelFormat::kPixelFormat_B8G8R8A8_UNormInt_SRGB: + return MTLPixelFormatBGRA8Unorm_sRGB; + case PixelFormat::kPixelFormat_D32_Float_S8_UNormInt: + return MTLPixelFormatDepth32Float_Stencil8; } return MTLPixelFormatInvalid; }; diff --git a/impeller/compositor/host_buffer.h b/impeller/compositor/host_buffer.h index e643b44935cf6..072da359ffde7 100644 --- a/impeller/compositor/host_buffer.h +++ b/impeller/compositor/host_buffer.h @@ -26,11 +26,11 @@ class HostBuffer final : public std::enable_shared_from_this, private: mutable std::shared_ptr device_buffer_; + mutable size_t device_buffer_generation_ = 0u; uint8_t* buffer_ = nullptr; size_t length_ = 0; size_t reserved_ = 0; size_t generation_ = 1u; - mutable size_t device_buffer_generation_ = 0u; // |Buffer| std::shared_ptr GetDeviceBuffer( diff --git a/impeller/compositor/pipeline_descriptor.h b/impeller/compositor/pipeline_descriptor.h index 3c68996c78de4..295c3751c1296 100644 --- a/impeller/compositor/pipeline_descriptor.h +++ b/impeller/compositor/pipeline_descriptor.h @@ -54,7 +54,9 @@ class PipelineDescriptor : public Comparable { StencilAttachmentDescriptor front, StencilAttachmentDescriptor back); - PipelineDescriptor& SetDepthStencilPixelFormat(PixelFormat format); + PipelineDescriptor& SetDepthPixelFormat(PixelFormat format); + + PipelineDescriptor& SetStencilPixelFormat(PixelFormat format); MTLRenderPipelineDescriptor* GetMTLRenderPipelineDescriptor() const; @@ -73,7 +75,8 @@ class PipelineDescriptor : public Comparable { std::map> entrypoints_; std::map color_attachment_descriptors_; std::shared_ptr vertex_descriptor_; - PixelFormat depth_stencil_pixel_format_ = PixelFormat::kUnknown; + PixelFormat depth_pixel_format_ = PixelFormat::kUnknown; + PixelFormat stencil_pixel_format_ = PixelFormat::kUnknown; std::optional depth_attachment_descriptor_; std::optional front_stencil_attachment_descriptor_; diff --git a/impeller/compositor/pipeline_descriptor.mm b/impeller/compositor/pipeline_descriptor.mm index 916c2ec4637e4..1aa9d5e781c42 100644 --- a/impeller/compositor/pipeline_descriptor.mm +++ b/impeller/compositor/pipeline_descriptor.mm @@ -32,7 +32,8 @@ if (vertex_descriptor_) { fml::HashCombineSeed(seed, vertex_descriptor_->GetHash()); } - fml::HashCombineSeed(seed, depth_stencil_pixel_format_); + fml::HashCombineSeed(seed, depth_pixel_format_); + fml::HashCombineSeed(seed, stencil_pixel_format_); fml::HashCombineSeed(seed, depth_attachment_descriptor_); fml::HashCombineSeed(seed, front_stencil_attachment_descriptor_); fml::HashCombineSeed(seed, back_stencil_attachment_descriptor_); @@ -45,7 +46,8 @@ DeepCompareMap(entrypoints_, other.entrypoints_) && color_attachment_descriptors_ == other.color_attachment_descriptors_ && DeepComparePointer(vertex_descriptor_, other.vertex_descriptor_) && - depth_stencil_pixel_format_ == other.depth_stencil_pixel_format_ && + stencil_pixel_format_ == other.stencil_pixel_format_ && + depth_pixel_format_ == other.depth_pixel_format_ && depth_attachment_descriptor_ == other.depth_attachment_descriptor_ && front_stencil_attachment_descriptor_ == other.front_stencil_attachment_descriptor_ && @@ -92,9 +94,15 @@ return *this; } -PipelineDescriptor& PipelineDescriptor::SetDepthStencilPixelFormat( +PipelineDescriptor& PipelineDescriptor::SetDepthPixelFormat( PixelFormat format) { - depth_stencil_pixel_format_ = format; + depth_pixel_format_ = format; + return *this; +} + +PipelineDescriptor& PipelineDescriptor::SetStencilPixelFormat( + PixelFormat format) { + stencil_pixel_format_ = format; return *this; } @@ -141,10 +149,9 @@ ToMTLRenderPipelineColorAttachmentDescriptor(item.second); } - descriptor.depthAttachmentPixelFormat = - ToMTLPixelFormat(depth_stencil_pixel_format_); + descriptor.depthAttachmentPixelFormat = ToMTLPixelFormat(depth_pixel_format_); descriptor.stencilAttachmentPixelFormat = - ToMTLPixelFormat(depth_stencil_pixel_format_); + ToMTLPixelFormat(stencil_pixel_format_); return descriptor; } diff --git a/impeller/compositor/render_pass.h b/impeller/compositor/render_pass.h index f97bd1550fcaa..efb4606512e6f 100644 --- a/impeller/compositor/render_pass.h +++ b/impeller/compositor/render_pass.h @@ -70,9 +70,9 @@ class RenderPass { bool IsValid() const; - bool Record(Command command); + bool RecordCommand(Command command); - bool Encode(Allocator& transients_allocator) const; + bool FinishEncoding(Allocator& transients_allocator) const; private: friend class CommandBuffer; diff --git a/impeller/compositor/render_pass.mm b/impeller/compositor/render_pass.mm index d9d5604e4184e..a69a339328d46 100644 --- a/impeller/compositor/render_pass.mm +++ b/impeller/compositor/render_pass.mm @@ -131,7 +131,7 @@ static bool ConfigureStencilAttachment( return is_valid_; } -bool RenderPass::Encode(Allocator& transients_allocator) const { +bool RenderPass::FinishEncoding(Allocator& transients_allocator) const { if (!IsValid()) { return false; } @@ -234,7 +234,7 @@ static bool Bind(Allocator& allocator, return true; } -bool RenderPass::Record(Command command) { +bool RenderPass::RecordCommand(Command command) { if (!command) { return false; } diff --git a/impeller/compositor/renderer.h b/impeller/compositor/renderer.h index 7c1cc9df44fa7..8a03bdc1a9ff7 100644 --- a/impeller/compositor/renderer.h +++ b/impeller/compositor/renderer.h @@ -15,6 +15,7 @@ namespace impeller { class Surface; +class RenderPass; class Renderer { public: @@ -29,12 +30,13 @@ class Renderer { protected: Renderer(std::string shaders_directory); - virtual bool OnRender() = 0; + virtual bool OnIsValid() const = 0; + + virtual bool OnRender(RenderPass& pass) = 0; private: dispatch_semaphore_t frames_in_flight_sema_ = nullptr; std::shared_ptr context_; - Size size_; bool is_valid_ = false; FML_DISALLOW_COPY_AND_ASSIGN(Renderer); diff --git a/impeller/compositor/renderer.mm b/impeller/compositor/renderer.mm index b0b02a1ae82bb..551dab2380fd0 100644 --- a/impeller/compositor/renderer.mm +++ b/impeller/compositor/renderer.mm @@ -25,7 +25,7 @@ Renderer::~Renderer() = default; bool Renderer::IsValid() const { - return is_valid_; + return is_valid_ && OnIsValid(); } bool Renderer::Render(const Surface& surface) { @@ -48,7 +48,12 @@ if (!render_pass) { return false; } - render_pass->Encode(*context_->GetTransientsAllocator()); + + if (!OnRender(*render_pass)) { + return false; + } + + render_pass->FinishEncoding(*GetContext()->GetTransientsAllocator()); ::dispatch_semaphore_wait(frames_in_flight_sema_, DISPATCH_TIME_FOREVER); @@ -56,6 +61,7 @@ [sema = frames_in_flight_sema_](CommandBuffer::CommitResult) { ::dispatch_semaphore_signal(sema); }); + return true; } diff --git a/impeller/entity/BUILD.gn b/impeller/entity/BUILD.gn index 66d048226bd0b..66e2b7a3db158 100644 --- a/impeller/entity/BUILD.gn +++ b/impeller/entity/BUILD.gn @@ -15,5 +15,6 @@ impeller_component("entity") { public_deps = [ "../compositor", "../image", + "../primitives", ] } diff --git a/impeller/entity/entity_renderer.h b/impeller/entity/entity_renderer.h index 629ef14ed6112..ac1b7566dbced 100644 --- a/impeller/entity/entity_renderer.h +++ b/impeller/entity/entity_renderer.h @@ -7,6 +7,7 @@ #include "flutter/fml/macros.h" #include "impeller/compositor/renderer.h" #include "impeller/entity/entity.h" +#include "impeller/primitives/box_primitive.h" namespace impeller { @@ -18,8 +19,14 @@ class EntityRenderer final : public Renderer { private: std::shared_ptr root_; + std::shared_ptr box_primitive_; + bool is_valid_ = false; - bool OnRender() override; + // |Renderer| + bool OnIsValid() const override; + + // |Renderer| + bool OnRender(RenderPass& pass) override; FML_DISALLOW_COPY_AND_ASSIGN(EntityRenderer); }; diff --git a/impeller/entity/entity_renderer.mm b/impeller/entity/entity_renderer.mm index 161a857dfe8d9..00e414419b27e 100644 --- a/impeller/entity/entity_renderer.mm +++ b/impeller/entity/entity_renderer.mm @@ -3,6 +3,9 @@ // found in the LICENSE file. #include "impeller/entity/entity_renderer.h" +#include "impeller/compositor/command.h" +#include "impeller/primitives/box.frag.h" +#include "impeller/primitives/box.vert.h" namespace impeller { @@ -10,12 +13,32 @@ : Renderer(std::move(shaders_directory)), root_(std::make_shared()) { root_->SetBackgroundColor(Color::DarkGray()); + + auto context = GetContext(); + + if (!context) { + return; + } + + box_primitive_ = std::make_shared(context); + if (!box_primitive_) { + return; + } + + is_valid_ = true; } EntityRenderer::~EntityRenderer() = default; -bool EntityRenderer::OnRender() { - return false; +bool EntityRenderer::OnIsValid() const { + return is_valid_; +} + +bool EntityRenderer::OnRender(RenderPass& pass) { + Command cmd; + cmd.pipeline = box_primitive_->GetPipeline(); + pass.RecordCommand(std::move(cmd)); + return true; } } // namespace impeller diff --git a/impeller/primitives/BUILD.gn b/impeller/primitives/BUILD.gn index da8d8fa95fed3..343891557640a 100644 --- a/impeller/primitives/BUILD.gn +++ b/impeller/primitives/BUILD.gn @@ -20,7 +20,8 @@ impeller_component("primitives") { "primitive.mm", ] - public_deps = [ "../compositor" ] - - deps = [ ":impeller_shaders" ] + public_deps = [ + ":impeller_shaders", + "../compositor", + ] } diff --git a/impeller/primitives/box.frag b/impeller/primitives/box.frag index 8f1aad5edf8aa..28a6c85d15d36 100644 --- a/impeller/primitives/box.frag +++ b/impeller/primitives/box.frag @@ -5,8 +5,8 @@ in vec3 color; in vec3 color2; -out vec3 fragColor; +layout(location = 0) out vec4 fragColor; void main() { - fragColor = color * color2; + fragColor = vec4(color * color2, 1.0); } diff --git a/impeller/primitives/box_primitive.h b/impeller/primitives/box_primitive.h index 9071899403331..83b8dbfc78141 100644 --- a/impeller/primitives/box_primitive.h +++ b/impeller/primitives/box_primitive.h @@ -2,7 +2,11 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. +#pragma once + #include "impeller/compositor/context.h" +#include "impeller/primitives/box.frag.h" +#include "impeller/primitives/box.vert.h" #include "impeller/primitives/primitive.h" namespace impeller { @@ -11,11 +15,20 @@ class BoxPrimitive final : public Primitive { public: BoxPrimitive(std::shared_ptr context); + // |Primitive| + std::shared_ptr GetPipeline() const override; + + // |Primitive| ~BoxPrimitive() override; + // |Primitive| + virtual bool IsValid() const override; + + // |Primitive| virtual bool Encode(RenderPass& pass) const override; private: + std::shared_ptr pipeline_ = 0; bool is_valid_ = false; FML_DISALLOW_COPY_AND_ASSIGN(BoxPrimitive); diff --git a/impeller/primitives/box_primitive.mm b/impeller/primitives/box_primitive.mm index cbe589374744c..6d35ae629518a 100644 --- a/impeller/primitives/box_primitive.mm +++ b/impeller/primitives/box_primitive.mm @@ -6,8 +6,6 @@ #include -#include "box.frag.h" -#include "box.vert.h" #include "flutter/fml/logging.h" #include "impeller/compositor/pipeline_descriptor.h" #include "impeller/compositor/shader_library.h" @@ -40,9 +38,26 @@ desc.SetVertexDescriptor(std::move(vertex_descriptor)); } - auto pipeline = + { + // Configure the sole color attachments pixel format. + ColorAttachmentDescriptor color0; + color0.format = PixelFormat::kPixelFormat_B8G8R8A8_UNormInt_SRGB; + desc.SetColorAttachmentDescriptor(0u, std::move(color0)); + } + + { + // Configure the stencil attachment. + // TODO(wip): Make this configurable if possible as the D32 compoment is + // wasted. + const auto combined_depth_stencil_format = + PixelFormat::kPixelFormat_D32_Float_S8_UNormInt; + desc.SetDepthPixelFormat(combined_depth_stencil_format); + desc.SetStencilPixelFormat(combined_depth_stencil_format); + } + + pipeline_ = context->GetPipelineLibrary()->GetRenderPipeline(std::move(desc)).get(); - if (!pipeline) { + if (!pipeline_) { FML_LOG(ERROR) << "Could not create the render pipeline."; return; } @@ -52,6 +67,14 @@ BoxPrimitive::~BoxPrimitive() = default; +std::shared_ptr BoxPrimitive::GetPipeline() const { + return pipeline_; +} + +bool BoxPrimitive::IsValid() const { + return is_valid_; +} + bool BoxPrimitive::Encode(RenderPass& pass) const { return false; } diff --git a/impeller/primitives/primitive.h b/impeller/primitives/primitive.h index 916342b755b5e..755abde8df49d 100644 --- a/impeller/primitives/primitive.h +++ b/impeller/primitives/primitive.h @@ -8,6 +8,7 @@ #include "flutter/fml/macros.h" #include "impeller/compositor/context.h" +#include "impeller/compositor/pipeline.h" #include "impeller/compositor/render_pass.h" namespace impeller { @@ -16,8 +17,12 @@ class Primitive { public: Primitive(std::shared_ptr); + virtual std::shared_ptr GetPipeline() const = 0; + virtual ~Primitive(); + virtual bool IsValid() const = 0; + std::shared_ptr GetContext() const; virtual bool Encode(RenderPass& pass) const = 0; diff --git a/impeller/tools/impeller.gni b/impeller/tools/impeller.gni index ece8c977452c5..44f3a7ee6e3ef 100644 --- a/impeller/tools/impeller.gni +++ b/impeller/tools/impeller.gni @@ -107,7 +107,7 @@ template("impeller_shaders") { ] depfile_path = "$target_gen_dir/{{source_file_part}}.d" - source_path = "{{source}}" + metal_intermediate_path = rebase_path(metal_intermediate, root_build_dir) spirv_intermediate_path = rebase_path(spirv_intermediate, root_build_dir) depfile_intermediate_path = rebase_path(depfile_path, root_build_dir) @@ -121,7 +121,7 @@ template("impeller_shaders") { depfile = depfile_path args = [ - "--input=$source_path", + "--input={{source}}", "--metal=$metal_intermediate_path", "--spirv=$spirv_intermediate_path", "--reflection-json=$reflection_json_path", @@ -144,8 +144,15 @@ template("impeller_shaders") { shader_glue_config_name = "glue_config_$target_name" config(shader_glue_config_name) { + impeller_root_gen_dir = get_path_info( + get_label_info("//flutter/impeller:impeller", "target_gen_dir"), + "dir") + # Contains the generated header headers. - include_dirs = [ target_gen_dir ] + include_dirs = [ + target_gen_dir, + impeller_root_gen_dir, + ] } source_set(shader_glue_target_name) { @@ -158,6 +165,7 @@ template("impeller_shaders") { "*.h", "*.cc", ]) + deps = [ ":$impellerc_target_name", "//flutter/impeller/shader_glue", From 9f111db65db8663b910159deae9259696d4cefc8 Mon Sep 17 00:00:00 2001 From: Chinmay Garde Date: Fri, 11 Jun 2021 14:21:17 -0700 Subject: [PATCH 073/433] Generate uniform IO slot. --- impeller/compiler/code_gen_template.h | 16 ++++++++++++++++ impeller/primitives/box_primitive.mm | 2 +- 2 files changed, 17 insertions(+), 1 deletion(-) diff --git a/impeller/compiler/code_gen_template.h b/impeller/compiler/code_gen_template.h index 66eaac368686d..61124296c9ba9 100644 --- a/impeller/compiler/code_gen_template.h +++ b/impeller/compiler/code_gen_template.h @@ -26,6 +26,22 @@ struct {{camel_case(shader_name)}}{{camel_case(shader_stage)}}Info { static constexpr std::string_view kEntrypointName = "{{entrypoint}}"; static constexpr ShaderStage kShaderStage = {{to_shader_stage(shader_stage)}}; + // =========================================================================== + // Stage Uniforms ============================================================ + // =========================================================================== + +{% for uniform in uniform_buffers %} + + static constexpr auto kUniform{{camel_case(uniform.name)}} = ShaderStageIOSlot { // {{uniform.name}} + "{{uniform.name}}", // name + {{uniform.location}}u, // attribute location + {{uniform.type.type_name}}, // type + {{uniform.type.bit_width}}u, // bit width of type + {{uniform.type.vec_size}}u, // vec size + {{uniform.type.columns}}u // number of columns + }; +{% endfor %} + // =========================================================================== // Stage Inputs ============================================================== // =========================================================================== diff --git a/impeller/primitives/box_primitive.mm b/impeller/primitives/box_primitive.mm index 6d35ae629518a..e967440222c72 100644 --- a/impeller/primitives/box_primitive.mm +++ b/impeller/primitives/box_primitive.mm @@ -47,7 +47,7 @@ { // Configure the stencil attachment. - // TODO(wip): Make this configurable if possible as the D32 compoment is + // TODO(wip): Make this configurable if possible as the D32 component is // wasted. const auto combined_depth_stencil_format = PixelFormat::kPixelFormat_D32_Float_S8_UNormInt; From e8011272801bdd7007156d2f9beb2b005c6fbafc Mon Sep 17 00:00:00 2001 From: Chinmay Garde Date: Fri, 11 Jun 2021 15:29:29 -0700 Subject: [PATCH 074/433] Prep for swapping JSON parser. --- impeller/compiler/code_gen_template.h | 1 - impeller/compiler/reflector.cc | 28 +++++++++++++++++++++++---- 2 files changed, 24 insertions(+), 5 deletions(-) diff --git a/impeller/compiler/code_gen_template.h b/impeller/compiler/code_gen_template.h index 61124296c9ba9..a6e262ecb0a8b 100644 --- a/impeller/compiler/code_gen_template.h +++ b/impeller/compiler/code_gen_template.h @@ -29,7 +29,6 @@ struct {{camel_case(shader_name)}}{{camel_case(shader_stage)}}Info { // =========================================================================== // Stage Uniforms ============================================================ // =========================================================================== - {% for uniform in uniform_buffers %} static constexpr auto kUniform{{camel_case(uniform.name)}} = ShaderStageIOSlot { // {{uniform.name}} diff --git a/impeller/compiler/reflector.cc b/impeller/compiler/reflector.cc index a2d31b36043b4..a84d69ff8c925 100644 --- a/impeller/compiler/reflector.cc +++ b/impeller/compiler/reflector.cc @@ -123,6 +123,7 @@ static bool ReflectType(Writer& writer, writer.Key("columns"); writer.Uint64(type.columns); + // Member types should only be present if the base type is a struct. if (!type.member_types.empty()) { writer.Key("member"); writer.StartArray(); @@ -254,11 +255,12 @@ static std::shared_ptr ReflectTemplateArguments( writer.String(options.header_file_name); } + const auto all_shader_resources = compiler.get_shader_resources(); + { writer.Key("uniform_buffers"); writer.StartArray(); - for (const auto& uniform_buffer : - compiler.get_shader_resources().uniform_buffers) { + for (const auto& uniform_buffer : all_shader_resources.uniform_buffers) { if (!ReflectUniformBuffer(writer, ir, compiler, uniform_buffer)) { FML_LOG(ERROR) << "Could not reflect uniform buffer."; return nullptr; @@ -270,7 +272,7 @@ static std::shared_ptr ReflectTemplateArguments( { writer.Key("stage_inputs"); writer.StartArray(); - for (const auto& input : compiler.get_shader_resources().stage_inputs) { + for (const auto& input : all_shader_resources.stage_inputs) { if (!ReflectStageIO(writer, ir, compiler, input)) { FML_LOG(ERROR) << "Could not reflect stage input."; return nullptr; @@ -282,7 +284,7 @@ static std::shared_ptr ReflectTemplateArguments( { writer.Key("stage_outputs"); writer.StartArray(); - for (const auto& output : compiler.get_shader_resources().stage_outputs) { + for (const auto& output : all_shader_resources.stage_outputs) { if (!ReflectStageIO(writer, ir, compiler, output)) { FML_LOG(ERROR) << "Could not reflect stage output."; return nullptr; @@ -291,6 +293,24 @@ static std::shared_ptr ReflectTemplateArguments( writer.EndArray(); } + { + auto reflect_types = + [&](const spirv_cross::SmallVector resources) + -> bool { + for (const auto& resource : resources) { + } + return true; + }; + writer.Key("type_definitions"); + writer.StartArray(); + if (!reflect_types(all_shader_resources.uniform_buffers) || + !reflect_types(all_shader_resources.stage_inputs) || + !reflect_types(all_shader_resources.stage_outputs)) { + return nullptr; + } + writer.EndArray(); + } + writer.EndObject(); // root return std::make_shared( From a7a3325e84f353e71a6afc307ad51e3f73c262d6 Mon Sep 17 00:00:00 2001 From: Chinmay Garde Date: Fri, 11 Jun 2021 16:03:59 -0700 Subject: [PATCH 075/433] Refactor reflector. --- impeller/compiler/compiler.cc | 16 +-- impeller/compiler/reflector.cc | 184 ++++++++++++++++----------------- impeller/compiler/reflector.h | 15 ++- 3 files changed, 115 insertions(+), 100 deletions(-) diff --git a/impeller/compiler/compiler.cc b/impeller/compiler/compiler.cc index 57af0f7b6bb77..7856a9a082519 100644 --- a/impeller/compiler/compiler.cc +++ b/impeller/compiler/compiler.cc @@ -283,21 +283,25 @@ Compiler::Compiler(const fml::Mapping& source_mapping, // The parser and compiler must be run separately because the parser contains // meta information (like type member names) that are useful for reflection. parser.parse(); - const auto& parsed_ir = parser.get_parsed_ir(); - spirv_cross::CompilerMSL msl_compiler(parsed_ir); + + const auto parsed_ir = + std::make_shared(parser.get_parsed_ir()); + + const auto msl_compiler = + std::make_shared(*parsed_ir); { - msl_compiler.rename_entry_point("main", options_.entry_point_name, - ToExecutionModel(options_.type)); + msl_compiler->rename_entry_point("main", options_.entry_point_name, + ToExecutionModel(options_.type)); } { spirv_cross::CompilerMSL::Options msl_options; msl_options.platform = spirv_cross::CompilerMSL::Options::Platform::macOS; - msl_compiler.set_msl_options(msl_options); + msl_compiler->set_msl_options(msl_options); } - msl_string_ = std::make_shared(msl_compiler.compile()); + msl_string_ = std::make_shared(msl_compiler->compile()); if (!msl_string_) { COMPILER_ERROR << "Could not generate MSL from SPIRV"; diff --git a/impeller/compiler/reflector.cc b/impeller/compiler/reflector.cc index a84d69ff8c925..e5599d2c4e638 100644 --- a/impeller/compiler/reflector.cc +++ b/impeller/compiler/reflector.cc @@ -21,10 +21,12 @@ namespace impeller { namespace compiler { +using namespace spirv_cross; + using Writer = rapidjson::PrettyWriter; -static std::string BaseTypeToString(spirv_cross::SPIRType::BaseType type) { - using Type = spirv_cross::SPIRType::BaseType; +static std::string BaseTypeToString(SPIRType::BaseType type) { + using Type = SPIRType::BaseType; switch (type) { case Type::Void: return "ShaderType::kVoid"; @@ -68,9 +70,9 @@ static std::string BaseTypeToString(spirv_cross::SPIRType::BaseType type) { } static std::optional GetMemberNameAtIndexIfExists( - const spirv_cross::ParsedIR& ir, - const spirv_cross::CompilerMSL& compiler, - const spirv_cross::SPIRType& type, + const ParsedIR& ir, + const CompilerMSL& compiler, + const SPIRType& type, size_t index) { if (type.type_alias != 0) { return GetMemberNameAtIndexIfExists( @@ -86,11 +88,10 @@ static std::optional GetMemberNameAtIndexIfExists( return std::nullopt; } -static std::string GetMemberNameAtIndex( - const spirv_cross::ParsedIR& ir, - const spirv_cross::CompilerMSL& compiler, - const spirv_cross::SPIRType& type, - size_t index) { +static std::string GetMemberNameAtIndex(const ParsedIR& ir, + const CompilerMSL& compiler, + const SPIRType& type, + size_t index) { if (auto name = GetMemberNameAtIndexIfExists(ir, compiler, type, index); name.has_value()) { return name.value(); @@ -103,9 +104,9 @@ static std::string GetMemberNameAtIndex( } static bool ReflectType(Writer& writer, - const spirv_cross::ParsedIR& ir, - const spirv_cross::CompilerMSL& compiler, - const spirv_cross::TypeID& type_id) { + const ParsedIR& ir, + const CompilerMSL& compiler, + const TypeID& type_id) { const auto type = compiler.get_type(type_id); writer.Key("type"); @@ -145,9 +146,9 @@ static bool ReflectType(Writer& writer, } static bool ReflectBaseResource(Writer& writer, - const spirv_cross::ParsedIR& ir, - const spirv_cross::CompilerMSL& compiler, - const spirv_cross::Resource& res) { + const ParsedIR& ir, + const CompilerMSL& compiler, + const Resource& res) { writer.Key("name"); writer.String(res.name); @@ -163,6 +164,10 @@ static bool ReflectBaseResource(Writer& writer, writer.Uint64( compiler.get_decoration(res.id, spv::Decoration::DecorationLocation)); + writer.Key("index"); + writer.Uint64( + compiler.get_decoration(res.id, spv::Decoration::DecorationIndex)); + if (!ReflectType(writer, ir, compiler, res.type_id)) { return false; } @@ -171,9 +176,9 @@ static bool ReflectBaseResource(Writer& writer, } static bool ReflectStageIO(Writer& writer, - const spirv_cross::ParsedIR& ir, - const spirv_cross::CompilerMSL& compiler, - const spirv_cross::Resource& io) { + const ParsedIR& ir, + const CompilerMSL& compiler, + const Resource& io) { writer.StartObject(); if (!ReflectBaseResource(writer, ir, compiler, io)) { @@ -185,19 +190,15 @@ static bool ReflectStageIO(Writer& writer, } static bool ReflectUniformBuffer(Writer& writer, - const spirv_cross::ParsedIR& ir, - const spirv_cross::CompilerMSL& compiler, - const spirv_cross::Resource& buffer) { + const ParsedIR& ir, + const CompilerMSL& compiler, + const Resource& buffer) { writer.StartObject(); if (!ReflectBaseResource(writer, ir, compiler, buffer)) { return false; } - writer.Key("index"); - writer.Uint64( - compiler.get_decoration(buffer.id, spv::Decoration::DecorationIndex)); - writer.EndObject(); return true; } @@ -225,17 +226,60 @@ static std::string StringToShaderStage(std::string str) { return "ShaderStage::kUnknown"; } -static std::shared_ptr ReflectTemplateArguments( - const Reflector::Options& options, - const spirv_cross::ParsedIR& ir, - const spirv_cross::CompilerMSL& compiler) { +Reflector::Reflector(Options options, + std::shared_ptr ir, + std::shared_ptr compiler) + : options_(std::move(options)), + ir_(std::move(ir)), + compiler_(std::move(compiler)) { + if (!ir_ || !compiler_) { + return; + } + + template_arguments_ = GenerateTemplateArguments(); + if (!template_arguments_) { + return; + } + + reflection_header_ = GenerateReflectionHeader(); + if (!reflection_header_) { + return; + } + + reflection_cc_ = GenerateReflectionCC(); + if (!reflection_cc_) { + return; + } + + is_valid_ = true; +} + +Reflector::~Reflector() = default; + +bool Reflector::IsValid() const { + return is_valid_; +} + +std::shared_ptr Reflector::GetReflectionJSON() const { + return template_arguments_; +} + +std::shared_ptr Reflector::GetReflectionHeader() const { + return reflection_header_; +} + +std::shared_ptr Reflector::GetReflectionCC() const { + return reflection_cc_; +} + +std::shared_ptr Reflector::GenerateTemplateArguments() const { auto buffer = std::make_shared(); Writer writer(*buffer); writer.StartObject(); // root { - const auto& entrypoints = compiler.get_entry_points_and_stages(); + const auto& entrypoints = compiler_->get_entry_points_and_stages(); if (entrypoints.size() != 1) { FML_LOG(ERROR) << "Incorrect number of entrypoints in the shader. Found " << entrypoints.size() << " but expected 1."; @@ -246,22 +290,22 @@ static std::shared_ptr ReflectTemplateArguments( writer.String(entrypoints.front().name); writer.Key("shader_name"); - writer.String(options.shader_name); + writer.String(options_.shader_name); writer.Key("shader_stage"); writer.String(ExecutionModelToString(entrypoints.front().execution_model)); writer.Key("header_file_name"); - writer.String(options.header_file_name); + writer.String(options_.header_file_name); } - const auto all_shader_resources = compiler.get_shader_resources(); + const auto all_shader_resources = compiler_->get_shader_resources(); { writer.Key("uniform_buffers"); writer.StartArray(); for (const auto& uniform_buffer : all_shader_resources.uniform_buffers) { - if (!ReflectUniformBuffer(writer, ir, compiler, uniform_buffer)) { + if (!ReflectUniformBuffer(writer, *ir_, *compiler_, uniform_buffer)) { FML_LOG(ERROR) << "Could not reflect uniform buffer."; return nullptr; } @@ -273,7 +317,7 @@ static std::shared_ptr ReflectTemplateArguments( writer.Key("stage_inputs"); writer.StartArray(); for (const auto& input : all_shader_resources.stage_inputs) { - if (!ReflectStageIO(writer, ir, compiler, input)) { + if (!ReflectStageIO(writer, *ir_, *compiler_, input)) { FML_LOG(ERROR) << "Could not reflect stage input."; return nullptr; } @@ -285,7 +329,7 @@ static std::shared_ptr ReflectTemplateArguments( writer.Key("stage_outputs"); writer.StartArray(); for (const auto& output : all_shader_resources.stage_outputs) { - if (!ReflectStageIO(writer, ir, compiler, output)) { + if (!ReflectStageIO(writer, *ir_, *compiler_, output)) { FML_LOG(ERROR) << "Could not reflect stage output."; return nullptr; } @@ -294,9 +338,7 @@ static std::shared_ptr ReflectTemplateArguments( } { - auto reflect_types = - [&](const spirv_cross::SmallVector resources) - -> bool { + auto reflect_types = [&](const SmallVector resources) -> bool { for (const auto& resource : resources) { } return true; @@ -318,14 +360,16 @@ static std::shared_ptr ReflectTemplateArguments( [buffer](auto, auto) {}); } -static std::shared_ptr InflateTemplate( - const spirv_cross::CompilerMSL& compiler, - const std::string_view& tmpl, - const fml::Mapping* reflection_args) { - if (!reflection_args) { - return nullptr; - } +std::shared_ptr Reflector::GenerateReflectionHeader() const { + return InflateTemplate(kReflectionHeaderTemplate); +} +std::shared_ptr Reflector::GenerateReflectionCC() const { + return InflateTemplate(kReflectionCCTemplate); +} + +std::shared_ptr Reflector::InflateTemplate( + const std::string_view& tmpl) const { inja::Environment env; env.set_trim_blocks(true); env.set_lstrip_blocks(true); @@ -339,7 +383,7 @@ static std::shared_ptr InflateTemplate( }); auto template_data = inja::json::parse( - reinterpret_cast(reflection_args->GetMapping())); + reinterpret_cast(template_arguments_->GetMapping())); auto inflated_template = std::make_shared(env.render(tmpl, template_data)); @@ -349,49 +393,5 @@ static std::shared_ptr InflateTemplate( inflated_template->size(), [inflated_template](auto, auto) {}); } -Reflector::Reflector(Options options, - const spirv_cross::ParsedIR& ir, - const spirv_cross::CompilerMSL& compiler) - : options_(std::move(options)), - template_arguments_(ReflectTemplateArguments(options_, ir, compiler)), - reflection_header_(InflateTemplate(compiler, - kReflectionHeaderTemplate, - template_arguments_.get())), - reflection_cc_(InflateTemplate(compiler, - kReflectionCCTemplate, - template_arguments_.get())) { - if (!template_arguments_) { - return; - } - - if (!reflection_header_) { - return; - } - - if (!reflection_cc_) { - return; - } - - is_valid_ = true; -} - -Reflector::~Reflector() = default; - -bool Reflector::IsValid() const { - return is_valid_; -} - -std::shared_ptr Reflector::GetReflectionJSON() const { - return template_arguments_; -} - -std::shared_ptr Reflector::GetReflectionHeader() const { - return reflection_header_; -} - -std::shared_ptr Reflector::GetReflectionCC() const { - return reflection_cc_; -} - } // namespace compiler } // namespace impeller diff --git a/impeller/compiler/reflector.h b/impeller/compiler/reflector.h index 7346b9895df94..fe84381175fec 100644 --- a/impeller/compiler/reflector.h +++ b/impeller/compiler/reflector.h @@ -22,8 +22,8 @@ class Reflector { }; Reflector(Options options, - const spirv_cross::ParsedIR& ir, - const spirv_cross::CompilerMSL& compiler); + std::shared_ptr ir, + std::shared_ptr compiler); ~Reflector(); @@ -37,11 +37,22 @@ class Reflector { private: const Options options_; + const std::shared_ptr ir_; + const std::shared_ptr compiler_; std::shared_ptr template_arguments_; std::shared_ptr reflection_header_; std::shared_ptr reflection_cc_; bool is_valid_ = false; + std::shared_ptr GenerateTemplateArguments() const; + + std::shared_ptr GenerateReflectionHeader() const; + + std::shared_ptr GenerateReflectionCC() const; + + std::shared_ptr InflateTemplate( + const std::string_view& tmpl) const; + FML_DISALLOW_COPY_AND_ASSIGN(Reflector); }; From 7c747bc919cb2459f7927b26fdea574a8793ca14 Mon Sep 17 00:00:00 2001 From: Chinmay Garde Date: Fri, 11 Jun 2021 16:52:39 -0700 Subject: [PATCH 076/433] Swap JSON handling in code generator. --- impeller/compiler/reflector.cc | 281 +++++++++++---------------------- impeller/compiler/reflector.h | 15 +- 2 files changed, 107 insertions(+), 189 deletions(-) diff --git a/impeller/compiler/reflector.cc b/impeller/compiler/reflector.cc index e5599d2c4e638..3abe20f8722f7 100644 --- a/impeller/compiler/reflector.cc +++ b/impeller/compiler/reflector.cc @@ -12,19 +12,12 @@ #include "flutter/fml/logging.h" #include "flutter/impeller/compiler/code_gen_template.h" #include "flutter/impeller/compiler/utilities.h" -#include "inja/inja.hpp" -#include "rapidjson/document.h" -#include "rapidjson/prettywriter.h" -#include "rapidjson/rapidjson.h" -#include "rapidjson/stringbuffer.h" namespace impeller { namespace compiler { using namespace spirv_cross; -using Writer = rapidjson::PrettyWriter; - static std::string BaseTypeToString(SPIRType::BaseType type) { using Type = SPIRType::BaseType; switch (type) { @@ -88,10 +81,10 @@ static std::optional GetMemberNameAtIndexIfExists( return std::nullopt; } -static std::string GetMemberNameAtIndex(const ParsedIR& ir, - const CompilerMSL& compiler, - const SPIRType& type, - size_t index) { +std::string GetMemberNameAtIndex(const ParsedIR& ir, + const CompilerMSL& compiler, + const SPIRType& type, + size_t index) { if (auto name = GetMemberNameAtIndexIfExists(ir, compiler, type, index); name.has_value()) { return name.value(); @@ -103,106 +96,6 @@ static std::string GetMemberNameAtIndex(const ParsedIR& ir, return stream.str(); } -static bool ReflectType(Writer& writer, - const ParsedIR& ir, - const CompilerMSL& compiler, - const TypeID& type_id) { - const auto type = compiler.get_type(type_id); - - writer.Key("type"); - writer.StartObject(); - - writer.Key("type_name"); - writer.String(BaseTypeToString(type.basetype)); - - writer.Key("bit_width"); - writer.Uint64(type.width); - - writer.Key("vec_size"); - writer.Uint64(type.vecsize); - - writer.Key("columns"); - writer.Uint64(type.columns); - - // Member types should only be present if the base type is a struct. - if (!type.member_types.empty()) { - writer.Key("member"); - writer.StartArray(); - for (size_t i = 0; i < type.member_types.size(); i++) { - writer.StartObject(); - { - writer.Key("type_id"); - writer.Uint64(type.member_types[i]); - writer.Key("member_name"); - writer.String(GetMemberNameAtIndex(ir, compiler, type, i)); - } - writer.EndObject(); - } - writer.EndArray(); - } - - writer.EndObject(); - return true; -} - -static bool ReflectBaseResource(Writer& writer, - const ParsedIR& ir, - const CompilerMSL& compiler, - const Resource& res) { - writer.Key("name"); - writer.String(res.name); - - writer.Key("descriptor_set"); - writer.Uint64(compiler.get_decoration( - res.id, spv::Decoration::DecorationDescriptorSet)); - - writer.Key("binding"); - writer.Uint64( - compiler.get_decoration(res.id, spv::Decoration::DecorationBinding)); - - writer.Key("location"); - writer.Uint64( - compiler.get_decoration(res.id, spv::Decoration::DecorationLocation)); - - writer.Key("index"); - writer.Uint64( - compiler.get_decoration(res.id, spv::Decoration::DecorationIndex)); - - if (!ReflectType(writer, ir, compiler, res.type_id)) { - return false; - } - - return true; -} - -static bool ReflectStageIO(Writer& writer, - const ParsedIR& ir, - const CompilerMSL& compiler, - const Resource& io) { - writer.StartObject(); - - if (!ReflectBaseResource(writer, ir, compiler, io)) { - return false; - } - - writer.EndObject(); - return true; -} - -static bool ReflectUniformBuffer(Writer& writer, - const ParsedIR& ir, - const CompilerMSL& compiler, - const Resource& buffer) { - writer.StartObject(); - - if (!ReflectBaseResource(writer, ir, compiler, buffer)) { - return false; - } - - writer.EndObject(); - return true; -} - static std::string ExecutionModelToString(spv::ExecutionModel model) { switch (model) { case spv::ExecutionModel::ExecutionModelVertex: @@ -236,8 +129,11 @@ Reflector::Reflector(Options options, return; } - template_arguments_ = GenerateTemplateArguments(); - if (!template_arguments_) { + if (auto template_arguments = GenerateTemplateArguments(); + template_arguments.has_value()) { + template_arguments_ = + std::make_unique(std::move(template_arguments.value())); + } else { return; } @@ -261,7 +157,16 @@ bool Reflector::IsValid() const { } std::shared_ptr Reflector::GetReflectionJSON() const { - return template_arguments_; + if (!is_valid_) { + return nullptr; + } + + auto json_string = + std::make_shared(template_arguments_->dump(2u)); + + return std::make_shared( + reinterpret_cast(json_string->data()), + json_string->size(), [json_string](auto, auto) {}); } std::shared_ptr Reflector::GetReflectionHeader() const { @@ -272,92 +177,48 @@ std::shared_ptr Reflector::GetReflectionCC() const { return reflection_cc_; } -std::shared_ptr Reflector::GenerateTemplateArguments() const { - auto buffer = std::make_shared(); - Writer writer(*buffer); - - writer.StartObject(); // root +std::optional Reflector::GenerateTemplateArguments() const { + nlohmann::json root; { const auto& entrypoints = compiler_->get_entry_points_and_stages(); if (entrypoints.size() != 1) { FML_LOG(ERROR) << "Incorrect number of entrypoints in the shader. Found " << entrypoints.size() << " but expected 1."; - return nullptr; + return std::nullopt; } - writer.Key("entrypoint"); - writer.String(entrypoints.front().name); - - writer.Key("shader_name"); - writer.String(options_.shader_name); - - writer.Key("shader_stage"); - writer.String(ExecutionModelToString(entrypoints.front().execution_model)); - - writer.Key("header_file_name"); - writer.String(options_.header_file_name); + root["entrypoint"] = entrypoints.front().name; + root["shader_name"] = options_.shader_name; + root["shader_stage"] = + ExecutionModelToString(entrypoints.front().execution_model); + root["header_file_name"] = options_.header_file_name; } - const auto all_shader_resources = compiler_->get_shader_resources(); + const auto shader_resources = compiler_->get_shader_resources(); - { - writer.Key("uniform_buffers"); - writer.StartArray(); - for (const auto& uniform_buffer : all_shader_resources.uniform_buffers) { - if (!ReflectUniformBuffer(writer, *ir_, *compiler_, uniform_buffer)) { - FML_LOG(ERROR) << "Could not reflect uniform buffer."; - return nullptr; - } - } - writer.EndArray(); + if (auto uniform_buffers = ReflectResources(shader_resources.uniform_buffers); + uniform_buffers.has_value()) { + root["uniform_buffers"] = std::move(uniform_buffers.value()); + } else { + return std::nullopt; } - { - writer.Key("stage_inputs"); - writer.StartArray(); - for (const auto& input : all_shader_resources.stage_inputs) { - if (!ReflectStageIO(writer, *ir_, *compiler_, input)) { - FML_LOG(ERROR) << "Could not reflect stage input."; - return nullptr; - } - } - writer.EndArray(); + if (auto stage_inputs = ReflectResources(shader_resources.stage_inputs); + stage_inputs.has_value()) { + root["stage_inputs"] = std::move(stage_inputs.value()); + } else { + return std::nullopt; } - { - writer.Key("stage_outputs"); - writer.StartArray(); - for (const auto& output : all_shader_resources.stage_outputs) { - if (!ReflectStageIO(writer, *ir_, *compiler_, output)) { - FML_LOG(ERROR) << "Could not reflect stage output."; - return nullptr; - } - } - writer.EndArray(); - } - - { - auto reflect_types = [&](const SmallVector resources) -> bool { - for (const auto& resource : resources) { - } - return true; - }; - writer.Key("type_definitions"); - writer.StartArray(); - if (!reflect_types(all_shader_resources.uniform_buffers) || - !reflect_types(all_shader_resources.stage_inputs) || - !reflect_types(all_shader_resources.stage_outputs)) { - return nullptr; - } - writer.EndArray(); + if (auto stage_outputs = ReflectResources(shader_resources.stage_outputs); + stage_outputs.has_value()) { + root["stage_outputs"] = std::move(stage_outputs.value()); + } else { + return std::nullopt; } - writer.EndObject(); // root - - return std::make_shared( - reinterpret_cast(buffer->GetString()), buffer->GetSize(), - [buffer](auto, auto) {}); + return root; } std::shared_ptr Reflector::GenerateReflectionHeader() const { @@ -382,16 +243,62 @@ std::shared_ptr Reflector::InflateTemplate( return StringToShaderStage(args.at(0u)->get()); }); - auto template_data = inja::json::parse( - reinterpret_cast(template_arguments_->GetMapping())); - auto inflated_template = - std::make_shared(env.render(tmpl, template_data)); + std::make_shared(env.render(tmpl, *template_arguments_)); return std::make_shared( reinterpret_cast(inflated_template->data()), inflated_template->size(), [inflated_template](auto, auto) {}); } +std::optional Reflector::ReflectResource( + const spirv_cross::Resource& resource) const { + nlohmann::json::object_t result; + + result["name"] = resource.name; + result["descriptor_set"] = compiler_->get_decoration( + resource.id, spv::Decoration::DecorationDescriptorSet); + result["binding"] = compiler_->get_decoration( + resource.id, spv::Decoration::DecorationBinding); + result["location"] = compiler_->get_decoration( + resource.id, spv::Decoration::DecorationLocation); + result["index"] = + compiler_->get_decoration(resource.id, spv::Decoration::DecorationIndex); + auto type = ReflectType(resource.type_id); + if (!type.has_value()) { + return std::nullopt; + } + result["type"] = std::move(type.value()); + return result; +} + +std::optional Reflector::ReflectType( + const spirv_cross::TypeID& type_id) const { + nlohmann::json::object_t result; + + const auto type = compiler_->get_type(type_id); + + result["type_name"] = BaseTypeToString(type.basetype); + result["bit_width"] = type.width; + result["vec_size"] = type.vecsize; + result["columns"] = type.columns; + + return result; +} + +std::optional Reflector::ReflectResources( + const spirv_cross::SmallVector& resources) const { + nlohmann::json::array_t result; + result.reserve(resources.size()); + for (const auto& resource : resources) { + if (auto reflected = ReflectResource(resource); reflected.has_value()) { + result.emplace_back(std::move(reflected.value())); + } else { + return std::nullopt; + } + } + return result; +} + } // namespace compiler } // namespace impeller diff --git a/impeller/compiler/reflector.h b/impeller/compiler/reflector.h index fe84381175fec..027f7e49fe6a0 100644 --- a/impeller/compiler/reflector.h +++ b/impeller/compiler/reflector.h @@ -5,9 +5,11 @@ #pragma once #include +#include #include "flutter/fml/macros.h" #include "flutter/fml/mapping.h" +#include "inja/inja.hpp" #include "third_party/spirv_cross/spirv_msl.hpp" #include "third_party/spirv_cross/spirv_parser.hpp" @@ -39,12 +41,12 @@ class Reflector { const Options options_; const std::shared_ptr ir_; const std::shared_ptr compiler_; - std::shared_ptr template_arguments_; + std::unique_ptr template_arguments_; std::shared_ptr reflection_header_; std::shared_ptr reflection_cc_; bool is_valid_ = false; - std::shared_ptr GenerateTemplateArguments() const; + std::optional GenerateTemplateArguments() const; std::shared_ptr GenerateReflectionHeader() const; @@ -53,6 +55,15 @@ class Reflector { std::shared_ptr InflateTemplate( const std::string_view& tmpl) const; + std::optional ReflectResource( + const spirv_cross::Resource& resource) const; + + std::optional ReflectResources( + const spirv_cross::SmallVector& resources) const; + + std::optional ReflectType( + const spirv_cross::TypeID& type_id) const; + FML_DISALLOW_COPY_AND_ASSIGN(Reflector); }; From ab373ca8670b17925da89e976071175e19a84f55 Mon Sep 17 00:00:00 2001 From: Chinmay Garde Date: Sat, 12 Jun 2021 16:05:20 -0700 Subject: [PATCH 077/433] Cycle over all struct definitions. --- impeller/compiler/reflector.cc | 89 +++++++++++++++++++++------------- impeller/compiler/reflector.h | 10 ++++ impeller/primitives/box.vert | 4 ++ 3 files changed, 69 insertions(+), 34 deletions(-) diff --git a/impeller/compiler/reflector.cc b/impeller/compiler/reflector.cc index 3abe20f8722f7..cdeabeb36dd1b 100644 --- a/impeller/compiler/reflector.cc +++ b/impeller/compiler/reflector.cc @@ -62,40 +62,6 @@ static std::string BaseTypeToString(SPIRType::BaseType type) { } } -static std::optional GetMemberNameAtIndexIfExists( - const ParsedIR& ir, - const CompilerMSL& compiler, - const SPIRType& type, - size_t index) { - if (type.type_alias != 0) { - return GetMemberNameAtIndexIfExists( - ir, compiler, compiler.get_type(type.type_alias), index); - } - - if (auto found = ir.meta.find(type.self); found != ir.meta.end()) { - const auto& members = found->second.members; - if (index < members.size() && !members[index].alias.empty()) { - return members[index].alias; - } - } - return std::nullopt; -} - -std::string GetMemberNameAtIndex(const ParsedIR& ir, - const CompilerMSL& compiler, - const SPIRType& type, - size_t index) { - if (auto name = GetMemberNameAtIndexIfExists(ir, compiler, type, index); - name.has_value()) { - return name.value(); - } - - static std::atomic_size_t sUnnamedMembersID; - std::stringstream stream; - stream << "unnamed_" << sUnnamedMembersID++; - return stream.str(); -} - static std::string ExecutionModelToString(spv::ExecutionModel model) { switch (model) { case spv::ExecutionModel::ExecutionModelVertex: @@ -218,6 +184,14 @@ std::optional Reflector::GenerateTemplateArguments() const { return std::nullopt; } + auto& struct_definitions = root["struct_definitions"] = + nlohmann::json::array_t{}; + ir_->for_each_typed_id([&](uint32_t, const SPIRType& type) { + if (auto struc = ReflectStructDefinition(type.self); struc.has_value()) { + struct_definitions.emplace_back(std::move(struc.value())); + } + }); + return root; } @@ -300,5 +274,52 @@ std::optional Reflector::ReflectResources( return result; } +std::optional Reflector::ReflectStructDefinition( + const TypeID& type_id) const { + const auto& type = compiler_->get_type(type_id); + if (type.basetype != SPIRType::BaseType::Struct) { + return std::nullopt; + } + + nlohmann::json::object_t struc; + struc["name"] = compiler_->get_name(type_id); + struc["members"] = nlohmann::json::array_t{}; + for (size_t i = 0; i < type.member_types.size(); i++) { + auto& member = struc["members"].emplace_back(nlohmann::json::object_t{}); + member["name"] = GetMemberNameAtIndex(type, i); + } + return struc; +} + +std::optional Reflector::GetMemberNameAtIndexIfExists( + const spirv_cross::SPIRType& parent_type, + size_t index) const { + if (parent_type.type_alias != 0) { + return GetMemberNameAtIndexIfExists( + compiler_->get_type(parent_type.type_alias), index); + } + + if (auto found = ir_->meta.find(parent_type.self); found != ir_->meta.end()) { + const auto& members = found->second.members; + if (index < members.size() && !members[index].alias.empty()) { + return members[index].alias; + } + } + return std::nullopt; +} + +std::string Reflector::GetMemberNameAtIndex( + const spirv_cross::SPIRType& parent_type, + size_t index) const { + if (auto name = GetMemberNameAtIndexIfExists(parent_type, index); + name.has_value()) { + return name.value(); + } + static std::atomic_size_t sUnnamedMembersID; + std::stringstream stream; + stream << "unnamed_" << sUnnamedMembersID++; + return stream.str(); +} + } // namespace compiler } // namespace impeller diff --git a/impeller/compiler/reflector.h b/impeller/compiler/reflector.h index 027f7e49fe6a0..bdaf747c21a6f 100644 --- a/impeller/compiler/reflector.h +++ b/impeller/compiler/reflector.h @@ -64,6 +64,16 @@ class Reflector { std::optional ReflectType( const spirv_cross::TypeID& type_id) const; + std::optional ReflectStructDefinition( + const spirv_cross::TypeID& type_id) const; + + std::optional GetMemberNameAtIndexIfExists( + const spirv_cross::SPIRType& parent_type, + size_t index) const; + + std::string GetMemberNameAtIndex(const spirv_cross::SPIRType& parent_type, + size_t index) const; + FML_DISALLOW_COPY_AND_ASSIGN(Reflector); }; diff --git a/impeller/primitives/box.vert b/impeller/primitives/box.vert index 6fa5eb17d074d..5cdfb045ec5e5 100644 --- a/impeller/primitives/box.vert +++ b/impeller/primitives/box.vert @@ -4,6 +4,10 @@ uniform UniformBuffer { mat4 mvp; + mat4 mvp2; + mat4 mvp3; + mat4 mvp4; + mat4 mvp5; } uniforms; in vec3 vertexPosition; From 784d000012cb706022563fcbc2e94d7e2434daf1 Mon Sep 17 00:00:00 2001 From: Chinmay Garde Date: Sun, 13 Jun 2021 17:30:21 -0700 Subject: [PATCH 078/433] Start dumping struct offset information. --- impeller/compiler/reflector.cc | 15 ++++++++++++++- 1 file changed, 14 insertions(+), 1 deletion(-) diff --git a/impeller/compiler/reflector.cc b/impeller/compiler/reflector.cc index cdeabeb36dd1b..c8d2947032a69 100644 --- a/impeller/compiler/reflector.cc +++ b/impeller/compiler/reflector.cc @@ -281,13 +281,26 @@ std::optional Reflector::ReflectStructDefinition( return std::nullopt; } + const auto struct_name = compiler_->get_name(type_id); + if (struct_name.find("_RESERVED_IDENTIFIER_") != std::string::npos) { + return std::nullopt; + } + nlohmann::json::object_t struc; - struc["name"] = compiler_->get_name(type_id); + struc["name"] = struct_name; struc["members"] = nlohmann::json::array_t{}; + size_t total_size = 0u; for (size_t i = 0; i < type.member_types.size(); i++) { + const auto& member_type = compiler_->get_type(type.member_types[i]); + const auto byte_length = + (member_type.width * member_type.vecsize * member_type.columns) / 8u; auto& member = struc["members"].emplace_back(nlohmann::json::object_t{}); member["name"] = GetMemberNameAtIndex(type, i); + member["byte_length"] = byte_length; + member["offset"] = total_size; + total_size += byte_length; } + struc["byte_length"] = total_size; return struc; } From 2c0f0704260ef308859a8d6b51cce247dcd66536 Mon Sep 17 00:00:00 2001 From: Chinmay Garde Date: Mon, 14 Jun 2021 12:51:38 -0700 Subject: [PATCH 079/433] Begin wiring up struct definitions. --- impeller/compiler/BUILD.gn | 1 - impeller/compiler/code_gen_template.h | 14 ++++++++++++++ impeller/compiler/reflector.cc | 24 ++++++++++++++++++++++++ impeller/primitives/box.vert | 4 +++- impeller/shader_glue/BUILD.gn | 2 ++ impeller/shader_glue/shader_types.h | 8 ++++++++ 6 files changed, 51 insertions(+), 2 deletions(-) diff --git a/impeller/compiler/BUILD.gn b/impeller/compiler/BUILD.gn index b9179446d4aec..25cd7cfbdb39a 100644 --- a/impeller/compiler/BUILD.gn +++ b/impeller/compiler/BUILD.gn @@ -26,7 +26,6 @@ source_set("compiler_lib") { "//third_party/rapidjson", "//third_party/shaderc_flutter", "//third_party/spirv_cross_flutter", - "//third_party/spirv_reflect_flutter", ] } diff --git a/impeller/compiler/code_gen_template.h b/impeller/compiler/code_gen_template.h index a6e262ecb0a8b..a8e4922538de5 100644 --- a/impeller/compiler/code_gen_template.h +++ b/impeller/compiler/code_gen_template.h @@ -26,6 +26,20 @@ struct {{camel_case(shader_name)}}{{camel_case(shader_stage)}}Info { static constexpr std::string_view kEntrypointName = "{{entrypoint}}"; static constexpr ShaderStage kShaderStage = {{to_shader_stage(shader_stage)}}; + + // =========================================================================== + // Struct Definitions ======================================================== + // =========================================================================== +{% for def in struct_definitions %} + + struct {{def.name}} { +{% for member in def.members %} + {{member.type}} {{member.name}}; +{% endfor %} + }; // struct {{def.name}} + static_assert(sizeof({{def.name}}) == {{def.byte_length}}, "Size of {{def.name}} does not match {{def.byte_length}} in the shader."); +{% endfor %} + // =========================================================================== // Stage Uniforms ============================================================ // =========================================================================== diff --git a/impeller/compiler/reflector.cc b/impeller/compiler/reflector.cc index c8d2947032a69..336e5c5cc05e5 100644 --- a/impeller/compiler/reflector.cc +++ b/impeller/compiler/reflector.cc @@ -6,6 +6,7 @@ #include #include +#include #include #include "flutter/fml/closure.h" @@ -186,7 +187,14 @@ std::optional Reflector::GenerateTemplateArguments() const { auto& struct_definitions = root["struct_definitions"] = nlohmann::json::array_t{}; + std::set known_structs; ir_->for_each_typed_id([&](uint32_t, const SPIRType& type) { + if (known_structs.find(type.self) != known_structs.end()) { + // Iterating over types this way leads to duplicates which may cause + // duplicate struct definitions. + return; + } + known_structs.insert(type.self); if (auto struc = ReflectStructDefinition(type.self); struc.has_value()) { struct_definitions.emplace_back(std::move(struc.value())); } @@ -274,6 +282,21 @@ std::optional Reflector::ReflectResources( return result; } +std::string GetHostStructMemberType(const SPIRType& type) { + if (type.width == 32 && type.columns == 1 && type.vecsize == 1) { + return "float"; + } + + if (type.width == 32 && type.columns == 4 && type.vecsize == 4) { + return "Matrix"; + } + + const auto byte_length = (type.width * type.vecsize * type.columns) / 8u; + std::stringstream stream; + stream << "Padding<" << byte_length << ">"; + return stream.str(); +} + std::optional Reflector::ReflectStructDefinition( const TypeID& type_id) const { const auto& type = compiler_->get_type(type_id); @@ -296,6 +319,7 @@ std::optional Reflector::ReflectStructDefinition( (member_type.width * member_type.vecsize * member_type.columns) / 8u; auto& member = struc["members"].emplace_back(nlohmann::json::object_t{}); member["name"] = GetMemberNameAtIndex(type, i); + member["type"] = GetHostStructMemberType(member_type); member["byte_length"] = byte_length; member["offset"] = total_size; total_size += byte_length; diff --git a/impeller/primitives/box.vert b/impeller/primitives/box.vert index 5cdfb045ec5e5..0f4cab992a832 100644 --- a/impeller/primitives/box.vert +++ b/impeller/primitives/box.vert @@ -4,9 +4,11 @@ uniform UniformBuffer { mat4 mvp; - mat4 mvp2; + mat4x2 mvp2; mat4 mvp3; mat4 mvp4; + bool foo; + float hello; mat4 mvp5; } uniforms; diff --git a/impeller/shader_glue/BUILD.gn b/impeller/shader_glue/BUILD.gn index 59019a9fa61ad..f9802b3d153d5 100644 --- a/impeller/shader_glue/BUILD.gn +++ b/impeller/shader_glue/BUILD.gn @@ -17,4 +17,6 @@ impeller_component("shader_glue") { "shader_types.cc", "shader_types.h", ] + + deps = [ "../geometry" ] } diff --git a/impeller/shader_glue/shader_types.h b/impeller/shader_glue/shader_types.h index a33fe4175663b..f96eefd4e0901 100644 --- a/impeller/shader_glue/shader_types.h +++ b/impeller/shader_glue/shader_types.h @@ -8,6 +8,8 @@ #include #include +#include "impeller/geometry/matrix.h" + namespace impeller { enum class ShaderStage { @@ -47,4 +49,10 @@ struct ShaderStageIOSlot { size_t columns; }; +template +struct Padding { + private: + uint8_t pad_[Size]; +}; + } // namespace impeller From 3744a0d147e6461b1465db50fa738ca0926e4b6a Mon Sep 17 00:00:00 2001 From: Chinmay Garde Date: Tue, 15 Jun 2021 13:23:04 -0700 Subject: [PATCH 080/433] Switch scalar type to float. --- impeller/compositor/formats_metal.mm | 3 +- impeller/compositor/texture.mm | 4 +- impeller/geometry/BUILD.gn | 1 + impeller/geometry/color.h | 24 ++++--- impeller/geometry/matrix.cc | 38 ++++++------ impeller/geometry/matrix.h | 93 ++++++++++++++-------------- impeller/geometry/path_builder.cc | 24 +++---- impeller/geometry/path_builder.h | 29 ++++----- impeller/geometry/path_component.cc | 88 +++++++++++++------------- impeller/geometry/path_component.h | 27 ++++---- impeller/geometry/point.cc | 10 --- impeller/geometry/point.h | 65 ++++++++++++------- impeller/geometry/rect.h | 14 +++-- impeller/geometry/scalar.h | 11 ++++ impeller/geometry/size.h | 19 +++--- impeller/geometry/vector.h | 84 +++++++++++++------------ impeller/image/image.cc | 2 +- 17 files changed, 289 insertions(+), 247 deletions(-) create mode 100644 impeller/geometry/scalar.h diff --git a/impeller/compositor/formats_metal.mm b/impeller/compositor/formats_metal.mm index 69a2919226116..1e9ea463f882c 100644 --- a/impeller/compositor/formats_metal.mm +++ b/impeller/compositor/formats_metal.mm @@ -92,7 +92,8 @@ static ColorRenderPassAttachment FromMTLRenderPassColorAttachmentDescriptor( ConfigureRenderPassAttachment(attachment, desc); auto clear = desc.clearColor; attachment.clear_color = - Color{clear.red, clear.green, clear.blue, clear.alpha}; + Color{static_cast(clear.red), static_cast(clear.green), + static_cast(clear.blue), static_cast(clear.alpha)}; return attachment; } diff --git a/impeller/compositor/texture.mm b/impeller/compositor/texture.mm index fec9cf47c6c7c..aab70094cd0cd 100644 --- a/impeller/compositor/texture.mm +++ b/impeller/compositor/texture.mm @@ -11,8 +11,8 @@ Texture::~Texture() = default; Size Texture::GetSize() const { - return {static_cast(texture_.width), - static_cast(texture_.height)}; + return {static_cast(texture_.width), + static_cast(texture_.height)}; } id Texture::GetMTLTexture() const { diff --git a/impeller/geometry/BUILD.gn b/impeller/geometry/BUILD.gn index f987641c93dfe..c18b8ff5d86f0 100644 --- a/impeller/geometry/BUILD.gn +++ b/impeller/geometry/BUILD.gn @@ -22,6 +22,7 @@ impeller_component("geometry") { "quaternion.h", "rect.cc", "rect.h", + "scalar.h", "shear.cc", "shear.h", "size.cc", diff --git a/impeller/geometry/color.h b/impeller/geometry/color.h index 2b442d114c3ef..3e0104dd61d58 100644 --- a/impeller/geometry/color.h +++ b/impeller/geometry/color.h @@ -7,6 +7,8 @@ #include #include +#include "impeller/geometry/scalar.h" + namespace impeller { struct ColorHSB; @@ -18,28 +20,28 @@ struct Color { /** * The red color component (0 to 1) */ - double red = 0.0; + Scalar red = 0.0; /** * The green color component (0 to 1) */ - double green = 0.0; + Scalar green = 0.0; /** * The blue color component (0 to 1) */ - double blue = 0.0; + Scalar blue = 0.0; /** * The alpha component of the color (0 to 1) */ - double alpha = 0.0; + Scalar alpha = 0.0; constexpr Color() {} Color(const ColorHSB& hsbColor); - constexpr Color(double r, double g, double b, double a) + constexpr Color(Scalar r, Scalar g, Scalar b, Scalar a) : red(r), green(g), blue(b), alpha(a) {} constexpr bool operator==(const Color& c) const { @@ -641,24 +643,24 @@ struct ColorHSB { /** * The hue of the color (0 to 1) */ - double hue; + Scalar hue; /** * The saturation of the color (0 to 1) */ - double saturation; + Scalar saturation; /** * The brightness of the color (0 to 1) */ - double brightness; + Scalar brightness; /** * The alpha of the color (0 to 1) */ - double alpha; + Scalar alpha; - ColorHSB(double h, double s, double b, double a) + constexpr ColorHSB(Scalar h, Scalar s, Scalar b, Scalar a) : hue(h), saturation(s), brightness(b), alpha(a) {} static ColorHSB FromRGB(Color rgb); @@ -668,4 +670,6 @@ struct ColorHSB { std::string ToString() const; }; +static_assert(sizeof(Color) == 4 * sizeof(Scalar)); + } // namespace impeller diff --git a/impeller/geometry/matrix.cc b/impeller/geometry/matrix.cc index 302d07d06ecae..3d8a457ef9610 100644 --- a/impeller/geometry/matrix.cc +++ b/impeller/geometry/matrix.cc @@ -84,18 +84,18 @@ Matrix::Matrix(const Decomposition& d) : Matrix() { } } -Matrix Matrix::MakeOrthographic(double left, - double right, - double bottom, - double top, - double nearZ, - double farZ) { - double ral = right + left; - double rsl = right - left; - double tab = top + bottom; - double tsb = top - bottom; - double fan = farZ + nearZ; - double fsn = farZ - nearZ; +Matrix Matrix::MakeOrthographic(Scalar left, + Scalar right, + Scalar bottom, + Scalar top, + Scalar nearZ, + Scalar farZ) { + Scalar ral = right + left; + Scalar rsl = right - left; + Scalar tab = top + bottom; + Scalar tsb = top - bottom; + Scalar fan = farZ + nearZ; + Scalar fsn = farZ - nearZ; // clang-format off return Matrix(2.0 / rsl, 0.0, 0.0, 0.0, @@ -110,11 +110,11 @@ Matrix Matrix::MakeOrthographic(const Size& size) { INT_MAX); } -Matrix Matrix::MakePerspective(double fov, - double aspect, - double nearZ, - double farZ) { - double cotan = 1.0 / tan(fov / 2.0); +Matrix Matrix::MakePerspective(Scalar fov, + Scalar aspect, + Scalar nearZ, + Scalar farZ) { + Scalar cotan = 1.0 / tan(fov / 2.0); return Matrix(cotan / aspect, 0.0, 0.0, 0.0, // 0.0, cotan, 0.0, 0.0, // @@ -213,7 +213,7 @@ Matrix Matrix::Invert() const { m[0] * m[5] * m[10] - m[0] * m[6] * m[9] - m[4] * m[1] * m[10] + m[4] * m[2] * m[9] + m[8] * m[1] * m[6] - m[8] * m[2] * m[5]}; - double det = + Scalar det = m[0] * tmp.m[0] + m[1] * tmp.m[4] + m[2] * tmp.m[8] + m[3] * tmp.m[12]; if (det == 0) { @@ -237,7 +237,7 @@ Matrix Matrix::Transpose() const { }; } -double Matrix::GetDeterminant() const { +Scalar Matrix::GetDeterminant() const { auto a00 = e[0][0]; auto a01 = e[0][1]; auto a02 = e[0][2]; diff --git a/impeller/geometry/matrix.h b/impeller/geometry/matrix.h index d35c59f622113..f33f1755babff 100644 --- a/impeller/geometry/matrix.h +++ b/impeller/geometry/matrix.h @@ -7,18 +7,19 @@ #include #include -#include "point.h" -#include "quaternion.h" -#include "shear.h" -#include "size.h" -#include "vector.h" +#include "impeller/geometry/point.h" +#include "impeller/geometry/quaternion.h" +#include "impeller/geometry/scalar.h" +#include "impeller/geometry/shear.h" +#include "impeller/geometry/size.h" +#include "impeller/geometry/vector.h" namespace impeller { struct Matrix { union { - double m[16]; - double e[4][4]; + Scalar m[16]; + Scalar e[4][4]; Vector4 vec[4]; }; @@ -45,7 +46,7 @@ struct Matrix { using DecompositionResult = std::pair; - Matrix() + constexpr Matrix() // clang-format off : vec{ Vector4(1.0, 0.0, 0.0, 0.0), Vector4(0.0, 1.0, 0.0, 0.0), @@ -54,10 +55,10 @@ struct Matrix { // clang-format on // clang-format off - Matrix(double m0, double m1, double m2, double m3, - double m4, double m5, double m6, double m7, - double m8, double m9, double m10, double m11, - double m12, double m13, double m14, double m15) + constexpr Matrix(Scalar m0, Scalar m1, Scalar m2, Scalar m3, + Scalar m4, Scalar m5, Scalar m6, Scalar m7, + Scalar m8, Scalar m9, Scalar m10, Scalar m11, + Scalar m12, Scalar m13, Scalar m14, Scalar m15) : vec{Vector4(m0, m1, m2, m3), Vector4(m4, m5, m6, m7), Vector4(m8, m9, m10, m11), @@ -66,7 +67,7 @@ struct Matrix { Matrix(const Decomposition& decomposition); - static Matrix MakeTranslation(const Vector3& t) { + static constexpr Matrix MakeTranslation(const Vector3& t) { // clang-format off return Matrix(1.0, 0.0, 0.0, 0.0, 0.0, 1.0, 0.0, 0.0, @@ -75,7 +76,7 @@ struct Matrix { // clang-format on } - static Matrix MakeScale(const Vector3& s) { + static constexpr Matrix MakeScale(const Vector3& s) { // clang-format off return Matrix(s.x, 0.0, 0.0, 0.0, 0.0, s.y, 0.0, 0.0, @@ -84,12 +85,12 @@ struct Matrix { // clang-format on } - static Matrix MakeRotation(double radians, const Vector4& r) { + static Matrix MakeRotation(Scalar radians, const Vector4& r) { const Vector4 v = r.Normalize(); - const double cosine = cos(radians); - const double cosp = 1.0f - cosine; - const double sine = sin(radians); + const Scalar cosine = cos(radians); + const Scalar cosp = 1.0f - cosine; + const Scalar sine = sin(radians); // clang-format off return Matrix( @@ -115,9 +116,9 @@ struct Matrix { // clang-format on } - static Matrix MakeRotationX(double radians) { - double cosine = cos(radians); - double sine = sin(radians); + static Matrix MakeRotationX(Scalar radians) { + Scalar cosine = cos(radians); + Scalar sine = sin(radians); // clang-format off return Matrix( 1.0, 0.0, 0.0, 0.0, @@ -128,9 +129,9 @@ struct Matrix { // clang-format on } - static Matrix MakeRotationY(double radians) { - double cosine = cos(radians); - double sine = sin(radians); + static Matrix MakeRotationY(Scalar radians) { + Scalar cosine = cos(radians); + Scalar sine = sin(radians); // clang-format off return Matrix( @@ -142,9 +143,9 @@ struct Matrix { // clang-format on } - static Matrix MakeRotationZ(double radians) { - double cosine = cos(radians); - double sine = sin(radians); + static Matrix MakeRotationZ(Scalar radians) { + Scalar cosine = cos(radians); + Scalar sine = sin(radians); // clang-format off return Matrix ( @@ -156,12 +157,12 @@ struct Matrix { // clang-format on } - static Matrix MakeOrthographic(double left, - double right, - double bottom, - double top, - double nearZ, - double farZ); + static Matrix MakeOrthographic(Scalar left, + Scalar right, + Scalar bottom, + Scalar top, + Scalar nearZ, + Scalar farZ); static Matrix MakeOrthographic(const Size& size); @@ -175,16 +176,16 @@ struct Matrix { * * @return the perspective projection matrix. */ - static Matrix MakePerspective(double fov, - double aspect, - double nearZ, - double farZ); + static Matrix MakePerspective(Scalar fov, + Scalar aspect, + Scalar nearZ, + Scalar farZ); static Matrix MakeLookAt(const Vector3& eye, const Vector3& center, const Vector3& up); - Matrix Translate(const Vector3& t) const { + constexpr Matrix Translate(const Vector3& t) const { // clang-format off return Matrix(m[0], m[1], m[2], m[3], m[4], m[5], m[6], m[7], @@ -196,7 +197,7 @@ struct Matrix { // clang-format on } - Matrix Scale(const Vector3& s) const { + constexpr Matrix Scale(const Vector3& s) const { // clang-format off return Matrix(m[0] * s.x, m[1] * s.x, m[2] * s.x , m[3] * s.x, m[4] * s.y, m[5] * s.y, m[6] * s.y , m[7] * s.y, @@ -205,7 +206,7 @@ struct Matrix { // clang-format on } - Matrix Multiply(const Matrix& o) const { + constexpr Matrix Multiply(const Matrix& o) const { // clang-format off return Matrix( m[0] * o.m[0] + m[4] * o.m[1] + m[8] * o.m[2] + m[12] * o.m[3], @@ -231,14 +232,14 @@ struct Matrix { Matrix Invert() const; - double GetDeterminant() const; + Scalar GetDeterminant() const; - bool IsAffine() const { + constexpr bool IsAffine() const { return (m[2] == 0 && m[3] == 0 && m[6] == 0 && m[7] == 0 && m[8] == 0 && m[9] == 0 && m[10] == 1 && m[11] == 0 && m[14] == 0 && m[15] == 1); } - bool IsIdentity() const { + constexpr bool IsIdentity() const { return ( // clang-format off m[0] == 1.0 && m[1] == 0.0 && m[2] == 0.0 && m[3] == 0.0 && @@ -251,7 +252,7 @@ struct Matrix { DecompositionResult Decompose() const; - bool operator==(const Matrix& m) const { + constexpr bool operator==(const Matrix& m) const { // clang-format off return vec[0] == m.vec[0] && vec[1] == m.vec[1] @@ -260,7 +261,7 @@ struct Matrix { // clang-format on } - bool operator!=(const Matrix& m) const { + constexpr bool operator!=(const Matrix& m) const { // clang-format off return vec[0] != m.vec[0] || vec[1] != m.vec[1] @@ -284,7 +285,7 @@ struct Matrix { void FromString(const std::string& str); }; -static_assert(sizeof(struct Matrix) == sizeof(double) * 16, +static_assert(sizeof(struct Matrix) == sizeof(Scalar) * 16, "The matrix must be of consistent size."); inline Vector4 operator*(const Vector4& v, const Matrix& m) { diff --git a/impeller/geometry/path_builder.cc b/impeller/geometry/path_builder.cc index 47c42d005ef80..b6d419bede8c6 100644 --- a/impeller/geometry/path_builder.cc +++ b/impeller/geometry/path_builder.cc @@ -6,7 +6,7 @@ namespace impeller { -static const double kArcApproximationMagic = 0.551915024494; +static const Scalar kArcApproximationMagic = 0.551915024494; PathBuilder::PathBuilder() = default; @@ -34,7 +34,7 @@ PathBuilder& PathBuilder::LineTo(Point point, bool relative) { return *this; } -PathBuilder& PathBuilder::HorizontalLineTo(double x, bool relative) { +PathBuilder& PathBuilder::HorizontalLineTo(Scalar x, bool relative) { Point endpoint = relative ? Point{current_.x + x, current_.y} : Point{x, current_.y}; prototype_.AddLinearComponent(current_, endpoint); @@ -42,7 +42,7 @@ PathBuilder& PathBuilder::HorizontalLineTo(double x, bool relative) { return *this; } -PathBuilder& PathBuilder::VerticalLineTo(double y, bool relative) { +PathBuilder& PathBuilder::VerticalLineTo(Scalar y, bool relative) { Point endpoint = relative ? Point{current_.x, current_.y + y} : Point{current_.x, y}; prototype_.AddLinearComponent(current_, endpoint); @@ -158,11 +158,11 @@ PathBuilder& PathBuilder::AddRect(Rect rect) { return *this; } -PathBuilder& PathBuilder::AddCircle(const Point& center, double radius) { +PathBuilder& PathBuilder::AddCircle(const Point& center, Scalar radius) { current_ = center + Point{0.0, radius}; - const double diameter = radius * 2.0; - const double magic = kArcApproximationMagic * radius; + const Scalar diameter = radius * 2.0; + const Scalar magic = kArcApproximationMagic * radius; prototype_.AddCubicComponent( {center.x + radius, center.y}, // @@ -194,7 +194,7 @@ PathBuilder& PathBuilder::AddCircle(const Point& center, double radius) { return *this; } -PathBuilder& PathBuilder::AddRoundedRect(Rect rect, double radius) { +PathBuilder& PathBuilder::AddRoundedRect(Rect rect, Scalar radius) { return radius == 0.0 ? AddRect(rect) : AddRoundedRect(rect, {radius, radius, radius, radius}); } @@ -202,10 +202,10 @@ PathBuilder& PathBuilder::AddRoundedRect(Rect rect, double radius) { PathBuilder& PathBuilder::AddRoundedRect(Rect rect, RoundingRadii radii) { current_ = rect.origin + Point{radii.topLeft, 0.0}; - const double magicTopRight = kArcApproximationMagic * radii.topRight; - const double magicBottomRight = kArcApproximationMagic * radii.bottomRight; - const double magicBottomLeft = kArcApproximationMagic * radii.bottomLeft; - const double magicTopLeft = kArcApproximationMagic * radii.topLeft; + const Scalar magicTopRight = kArcApproximationMagic * radii.topRight; + const Scalar magicBottomRight = kArcApproximationMagic * radii.bottomRight; + const Scalar magicBottomLeft = kArcApproximationMagic * radii.bottomLeft; + const Scalar magicTopLeft = kArcApproximationMagic * radii.topLeft; /* * Top line. @@ -287,7 +287,7 @@ PathBuilder& PathBuilder::AddRoundedRect(Rect rect, RoundingRadii radii) { PathBuilder& PathBuilder::AddEllipse(const Point& center, const Size& radius) { current_ = center + Point{0.0, radius.height}; - const Size diameter = {radius.width * 2.0, radius.height * 2.0}; + const Size diameter = {radius.width * 2.0f, radius.height * 2.0f}; const Size magic = {kArcApproximationMagic * radius.width, kArcApproximationMagic * radius.height}; diff --git a/impeller/geometry/path_builder.h b/impeller/geometry/path_builder.h index 177bd077a815e..786f4c26ab035 100644 --- a/impeller/geometry/path_builder.h +++ b/impeller/geometry/path_builder.h @@ -5,8 +5,9 @@ #pragma once #include "flutter/fml/macros.h" -#include "path.h" -#include "rect.h" +#include "impeller/geometry/path.h" +#include "impeller/geometry/rect.h" +#include "impeller/geometry/scalar.h" namespace impeller { @@ -24,9 +25,9 @@ class PathBuilder { PathBuilder& LineTo(Point point, bool relative = false); - PathBuilder& HorizontalLineTo(double x, bool relative = false); + PathBuilder& HorizontalLineTo(Scalar x, bool relative = false); - PathBuilder& VerticalLineTo(double y, bool relative = false); + PathBuilder& VerticalLineTo(Scalar y, bool relative = false); PathBuilder& QuadraticCurveTo(Point point, Point controlPoint, @@ -45,24 +46,24 @@ class PathBuilder { PathBuilder& AddRect(Rect rect); - PathBuilder& AddRoundedRect(Rect rect, double radius); + PathBuilder& AddRoundedRect(Rect rect, Scalar radius); - PathBuilder& AddCircle(const Point& center, double radius); + PathBuilder& AddCircle(const Point& center, Scalar radius); PathBuilder& AddEllipse(const Point& center, const Size& size); struct RoundingRadii { - double topLeft = 0.0; - double bottomLeft = 0.0; - double topRight = 0.0; - double bottomRight = 0.0; + Scalar topLeft = 0.0; + Scalar bottomLeft = 0.0; + Scalar topRight = 0.0; + Scalar bottomRight = 0.0; RoundingRadii() {} - RoundingRadii(double pTopLeft, - double pBottomLeft, - double pTopRight, - double pBottomRight) + RoundingRadii(Scalar pTopLeft, + Scalar pBottomLeft, + Scalar pTopRight, + Scalar pBottomRight) : topLeft(pTopLeft), bottomLeft(pBottomLeft), topRight(pTopRight), diff --git a/impeller/geometry/path_component.cc b/impeller/geometry/path_component.cc index 3d5dad45133d4..f7055c39b7d5e 100644 --- a/impeller/geometry/path_component.cc +++ b/impeller/geometry/path_component.cc @@ -8,54 +8,54 @@ namespace impeller { static const size_t kRecursionLimit = 32; -static const double kCurveCollinearityEpsilon = 1e-30; -static const double kCurveAngleToleranceEpsilon = 0.01; +static const Scalar kCurveCollinearityEpsilon = 1e-30; +static const Scalar kCurveAngleToleranceEpsilon = 0.01; /* * Based on: https://en.wikipedia.org/wiki/B%C3%A9zier_curve#Specific_cases */ -static inline double LinearSolve(double t, double p0, double p1) { +static inline Scalar LinearSolve(Scalar t, Scalar p0, Scalar p1) { return p0 + t * (p1 - p0); } -static inline double QuadraticSolve(double t, double p0, double p1, double p2) { +static inline Scalar QuadraticSolve(Scalar t, Scalar p0, Scalar p1, Scalar p2) { return (1 - t) * (1 - t) * p0 + // 2 * (1 - t) * t * p1 + // t * t * p2; } -static inline double QuadraticSolveDerivative(double t, - double p0, - double p1, - double p2) { +static inline Scalar QuadraticSolveDerivative(Scalar t, + Scalar p0, + Scalar p1, + Scalar p2) { return 2 * (1 - t) * (p1 - p0) + // 2 * t * (p2 - p1); } -static inline double CubicSolve(double t, - double p0, - double p1, - double p2, - double p3) { +static inline Scalar CubicSolve(Scalar t, + Scalar p0, + Scalar p1, + Scalar p2, + Scalar p3) { return (1 - t) * (1 - t) * (1 - t) * p0 + // 3 * (1 - t) * (1 - t) * t * p1 + // 3 * (1 - t) * t * t * p2 + // t * t * t * p3; } -static inline double CubicSolveDerivative(double t, - double p0, - double p1, - double p2, - double p3) { +static inline Scalar CubicSolveDerivative(Scalar t, + Scalar p0, + Scalar p1, + Scalar p2, + Scalar p3) { return -3 * p0 * (1 - t) * (1 - t) + // p1 * (3 * (1 - t) * (1 - t) - 6 * (1 - t) * t) + p2 * (6 * (1 - t) * t - 3 * t * t) + // 3 * p3 * t * t; } -Point LinearPathComponent::Solve(double time) const { +Point LinearPathComponent::Solve(Scalar time) const { return { LinearSolve(time, p1.x, p2.x), // x LinearSolve(time, p1.y, p2.y), // y @@ -70,14 +70,14 @@ std::vector LinearPathComponent::Extrema() const { return {p1, p2}; } -Point QuadraticPathComponent::Solve(double time) const { +Point QuadraticPathComponent::Solve(Scalar time) const { return { QuadraticSolve(time, p1.x, cp.x, p2.x), // x QuadraticSolve(time, p1.y, cp.y, p2.y), // y }; } -Point QuadraticPathComponent::SolveDerivative(double time) const { +Point QuadraticPathComponent::SolveDerivative(Scalar time) const { return { QuadraticSolveDerivative(time, p1.x, cp.x, p2.x), // x QuadraticSolveDerivative(time, p1.y, cp.y, p2.y), // y @@ -95,14 +95,14 @@ std::vector QuadraticPathComponent::Sxtrema() const { return elevated.Extrema(); } -Point CubicPathComponent::Solve(double time) const { +Point CubicPathComponent::Solve(Scalar time) const { return { CubicSolve(time, p1.x, cp1.x, cp2.x, p2.x), // x CubicSolve(time, p1.y, cp1.y, cp2.y, p2.y), // y }; } -Point CubicPathComponent::SolveDerivative(double time) const { +Point CubicPathComponent::SolveDerivative(Scalar time) const { return { CubicSolveDerivative(time, p1.x, cp1.x, cp2.x, p2.x), // x CubicSolveDerivative(time, p1.y, cp1.y, cp2.y, p2.y), // y @@ -141,12 +141,12 @@ static void CubicPathSmoothenRecursive(const SmoothingApproximation& approx, * Attempt approximation using single straight line. */ auto d = p4 - p1; - double d2 = fabs(((p2.x - p4.x) * d.y - (p2.y - p4.y) * d.x)); - double d3 = fabs(((p3.x - p4.x) * d.y - (p3.y - p4.y) * d.x)); + Scalar d2 = fabs(((p2.x - p4.x) * d.y - (p2.y - p4.y) * d.x)); + Scalar d3 = fabs(((p3.x - p4.x) * d.y - (p3.y - p4.y) * d.x)); - double da1 = 0; - double da2 = 0; - double k = 0; + Scalar da1 = 0; + Scalar da2 = 0; + Scalar k = 0; switch ((static_cast(d2 > kCurveCollinearityEpsilon) << 1) + static_cast(d3 > kCurveCollinearityEpsilon)) { @@ -342,22 +342,22 @@ std::vector CubicPathComponent::SmoothPoints( return points; } -static inline bool NearEqual(double a, double b, double epsilon) { +static inline bool NearEqual(Scalar a, Scalar b, Scalar epsilon) { return (a > (b - epsilon)) && (a < (b + epsilon)); } -static inline bool NearZero(double a) { +static inline bool NearZero(Scalar a) { return NearEqual(a, 0.0, 1e-12); } -static void CubicPathBoundingPopulateValues(std::vector& values, - double p1, - double p2, - double p3, - double p4) { - const double a = 3.0 * (-p1 + 3.0 * p2 - 3.0 * p3 + p4); - const double b = 6.0 * (p1 - 2.0 * p2 + p3); - const double c = 3.0 * (p2 - p1); +static void CubicPathBoundingPopulateValues(std::vector& values, + Scalar p1, + Scalar p2, + Scalar p3, + Scalar p4) { + const Scalar a = 3.0 * (-p1 + 3.0 * p2 - 3.0 * p3 + p4); + const Scalar b = 6.0 * (p1 - 2.0 * p2 + p3); + const Scalar c = 3.0 * (p2 - p1); /* * Boundary conditions. @@ -367,30 +367,30 @@ static void CubicPathBoundingPopulateValues(std::vector& values, return; } - double t = -c / b; + Scalar t = -c / b; if (t >= 0.0 && t <= 1.0) { values.emplace_back(t); } return; } - double b2Minus4AC = (b * b) - (4.0 * a * c); + Scalar b2Minus4AC = (b * b) - (4.0 * a * c); if (b2Minus4AC < 0.0) { return; } - double rootB2Minus4AC = ::sqrt(b2Minus4AC); + Scalar rootB2Minus4AC = ::sqrt(b2Minus4AC); { - double t = (-b + rootB2Minus4AC) / (2.0 * a); + Scalar t = (-b + rootB2Minus4AC) / (2.0 * a); if (t >= 0.0 && t <= 1.0) { values.emplace_back(t); } } { - double t = (-b - rootB2Minus4AC) / (2.0 * a); + Scalar t = (-b - rootB2Minus4AC) / (2.0 * a); if (t >= 0.0 && t <= 1.0) { values.emplace_back(t); } @@ -401,7 +401,7 @@ std::vector CubicPathComponent::Extrema() const { /* * As described in: https://pomax.github.io/bezierinfo/#extremities */ - std::vector values; + std::vector values; CubicPathBoundingPopulateValues(values, p1.x, cp1.x, cp2.x, p2.x); CubicPathBoundingPopulateValues(values, p1.y, cp1.y, cp2.y, p2.y); diff --git a/impeller/geometry/path_component.h b/impeller/geometry/path_component.h index c4486da7cc009..c9e1e23730b83 100644 --- a/impeller/geometry/path_component.h +++ b/impeller/geometry/path_component.h @@ -6,24 +6,25 @@ #include -#include "rect.h" +#include "impeller/geometry/rect.h" +#include "impeller/geometry/scalar.h" namespace impeller { struct SmoothingApproximation { - const double scale; - const double angleTolerance; - const double cuspLimit; - const double distanceToleranceSquare; + const Scalar scale; + const Scalar angleTolerance; + const Scalar cuspLimit; + const Scalar distanceToleranceSquare; SmoothingApproximation(/* default */) : SmoothingApproximation(1.0 /* scale */, 0.0 /* angle tolerance */, 0.0 /* cusp limit */) {} - SmoothingApproximation(double pScale, - double pAngleTolerance, - double pCuspLimit) + SmoothingApproximation(Scalar pScale, + Scalar pAngleTolerance, + Scalar pCuspLimit) : scale(pScale), angleTolerance(pAngleTolerance), cuspLimit(pCuspLimit), @@ -38,7 +39,7 @@ struct LinearPathComponent { LinearPathComponent(Point ap1, Point ap2) : p1(ap1), p2(ap2) {} - Point Solve(double time) const; + Point Solve(Scalar time) const; std::vector SmoothPoints() const; @@ -59,9 +60,9 @@ struct QuadraticPathComponent { QuadraticPathComponent(Point ap1, Point acp, Point ap2) : p1(ap1), cp(acp), p2(ap2) {} - Point Solve(double time) const; + Point Solve(Scalar time) const; - Point SolveDerivative(double time) const; + Point SolveDerivative(Scalar time) const; std::vector SmoothPoints( const SmoothingApproximation& approximation) const; @@ -90,9 +91,9 @@ struct CubicPathComponent { CubicPathComponent(Point ap1, Point acp1, Point acp2, Point ap2) : p1(ap1), cp1(acp1), cp2(acp2), p2(ap2) {} - Point Solve(double time) const; + Point Solve(Scalar time) const; - Point SolveDerivative(double time) const; + Point SolveDerivative(Scalar time) const; std::vector SmoothPoints( const SmoothingApproximation& approximation) const; diff --git a/impeller/geometry/point.cc b/impeller/geometry/point.cc index a0c942afd01ff..ca72d765501b1 100644 --- a/impeller/geometry/point.cc +++ b/impeller/geometry/point.cc @@ -20,14 +20,4 @@ void Point::FromString(const std::string& str) { stream >> y; } -double Point::GetDistanceSquared(const Point& p) const { - double dx = p.x - x; - double dy = p.y - y; - return dx * dx + dy * dy; -} - -double Point::GetDistance(const Point& p) const { - return sqrt(GetDistanceSquared(p)); -} - } // namespace impeller diff --git a/impeller/geometry/point.h b/impeller/geometry/point.h index 33f9e6d948bdb..ba46e8283ccd2 100644 --- a/impeller/geometry/point.h +++ b/impeller/geometry/point.h @@ -6,54 +6,75 @@ #include #include -#include "size.h" + +#include "impeller/geometry/scalar.h" +#include "impeller/geometry/size.h" namespace impeller { struct Point { - double x = 0.0; - double y = 0.0; + Scalar x = 0.0; + Scalar y = 0.0; constexpr Point() = default; - constexpr Point(double x, double y) : x(x), y(y) {} + constexpr Point(Scalar x, Scalar y) : x(x), y(y) {} - /* - * Operator overloads - */ - bool operator==(const Point& p) const { return p.x == x && p.y == y; } + constexpr bool operator==(const Point& p) const { + return p.x == x && p.y == y; + } - bool operator!=(const Point& p) const { return p.x != x || p.y != y; } + constexpr bool operator!=(const Point& p) const { + return p.x != x || p.y != y; + } - Point operator-() const { return {-x, -y}; } + constexpr Point operator-() const { return {-x, -y}; } - Point operator+(const Point& p) const { return {x + p.x, y + p.y}; } + constexpr Point operator+(const Point& p) const { return {x + p.x, y + p.y}; } - Point operator+(const Size& s) const { return {x + s.width, y + s.height}; } + constexpr Point operator+(const Size& s) const { + return {x + s.width, y + s.height}; + } - Point operator-(const Point& p) const { return {x - p.x, y - p.y}; } + constexpr Point operator-(const Point& p) const { return {x - p.x, y - p.y}; } - Point operator-(const Size& s) const { return {x - s.width, y - s.height}; } + constexpr Point operator-(const Size& s) const { + return {x - s.width, y - s.height}; + } - Point operator*(double scale) const { return {x * scale, y * scale}; } + constexpr Point operator*(Scalar scale) const { + return {x * scale, y * scale}; + } - Point operator*(const Point& p) const { return {x * p.x, y * p.y}; } + constexpr Point operator*(const Point& p) const { return {x * p.x, y * p.y}; } - Point operator*(const Size& s) const { return {x * s.width, y * s.height}; } + constexpr Point operator*(const Size& s) const { + return {x * s.width, y * s.height}; + } - Point operator/(double d) const { return {x / d, y / d}; } + constexpr Point operator/(Scalar d) const { return {x / d, y / d}; } - Point operator/(const Point& p) const { return {x / p.x, y / p.y}; } + constexpr Point operator/(const Point& p) const { return {x / p.x, y / p.y}; } - Point operator/(const Size& s) const { return {x / s.width, y / s.height}; } + constexpr Point operator/(const Size& s) const { + return {x / s.width, y / s.height}; + } - double GetDistanceSquared(const Point& p) const; + constexpr Scalar GetDistanceSquared(const Point& p) const { + double dx = p.x - x; + double dy = p.y - y; + return dx * dx + dy * dy; + } - double GetDistance(const Point& p) const; + constexpr Scalar GetDistance(const Point& p) const { + return sqrt(GetDistanceSquared(p)); + } std::string ToString() const; void FromString(const std::string& str); }; +static_assert(sizeof(Point) == 2 * sizeof(Scalar)); + } // namespace impeller diff --git a/impeller/geometry/rect.h b/impeller/geometry/rect.h index 7439e1896e746..21b5a905f1a5d 100644 --- a/impeller/geometry/rect.h +++ b/impeller/geometry/rect.h @@ -5,8 +5,10 @@ #pragma once #include -#include "point.h" -#include "size.h" + +#include "impeller/geometry/point.h" +#include "impeller/geometry/scalar.h" +#include "impeller/geometry/size.h" namespace impeller { @@ -20,11 +22,11 @@ struct Rect { constexpr Rect(Point origin, Size size) : origin(origin), size(size) {} - constexpr Rect(const double components[4]) + constexpr Rect(const Scalar components[4]) : origin(components[0], components[1]), size(components[2], components[3]) {} - constexpr Rect(double x, double y, double width, double height) + constexpr Rect(Scalar x, Scalar y, Scalar width, Scalar height) : origin(x, y), size(width, height) {} /* @@ -40,7 +42,7 @@ struct Rect { {size.width - r.size.width, size.height - r.size.height}); } - constexpr Rect operator*(double scale) const { + constexpr Rect operator*(Scalar scale) const { return Rect({origin.x * scale, origin.y * scale}, {size.width * scale, size.height * scale}); } @@ -70,4 +72,6 @@ struct Rect { void FromString(const std::string& str); }; +static_assert(sizeof(Rect) == 4 * sizeof(Scalar)); + } // namespace impeller diff --git a/impeller/geometry/scalar.h b/impeller/geometry/scalar.h new file mode 100644 index 0000000000000..bf494586e7cdf --- /dev/null +++ b/impeller/geometry/scalar.h @@ -0,0 +1,11 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#pragma once + +namespace impeller { + +using Scalar = float; + +} // namespace impeller diff --git a/impeller/geometry/size.h b/impeller/geometry/size.h index 6df32feeb8f24..04bce608a5795 100644 --- a/impeller/geometry/size.h +++ b/impeller/geometry/size.h @@ -7,25 +7,24 @@ #include #include +#include "impeller/geometry/scalar.h" + namespace impeller { struct Size { - double width = 0.0; - double height = 0.0; + Scalar width = 0.0; + Scalar height = 0.0; constexpr Size() {} - constexpr Size(double width, double height) : width(width), height(height) {} + constexpr Size(Scalar width, Scalar height) : width(width), height(height) {} static constexpr Size Infinite() { - return Size{std::numeric_limits::max(), - std::numeric_limits::max()}; + return Size{std::numeric_limits::max(), + std::numeric_limits::max()}; } - /* - * Operator overloads - */ - constexpr Size operator*(double scale) const { + constexpr Size operator*(Scalar scale) const { return {width * scale, height * scale}; } @@ -70,4 +69,6 @@ struct Size { void FromString(const std::string& str); }; +static_assert(sizeof(Size) == 2 * sizeof(Scalar)); + } // namespace impeller diff --git a/impeller/geometry/vector.h b/impeller/geometry/vector.h index 9360d7fe5d4cc..b7625ffd96a67 100644 --- a/impeller/geometry/vector.h +++ b/impeller/geometry/vector.h @@ -6,48 +6,50 @@ #include #include -#include "point.h" -#include "size.h" + +#include "impeller/geometry/point.h" +#include "impeller/geometry/scalar.h" +#include "impeller/geometry/size.h" namespace impeller { struct Vector3 { union { struct { - double x = 0.0; - double y = 0.0; - double z = 0.0; + Scalar x = 0.0; + Scalar y = 0.0; + Scalar z = 0.0; }; - double e[3]; + Scalar e[3]; }; - Vector3() {} + constexpr Vector3(){}; - Vector3(const Point& p) : x(p.x), y(p.y) {} + constexpr Vector3(const Point& p) : x(p.x), y(p.y) {} - Vector3(const Size& s) : x(s.width), y(s.height) {} + constexpr Vector3(const Size& s) : x(s.width), y(s.height) {} - Vector3(double x, double y) : x(x), y(y) {} + constexpr Vector3(Scalar x, Scalar y) : x(x), y(y) {} - Vector3(double x, double y, double z) : x(x), y(y), z(z) {} + constexpr Vector3(Scalar x, Scalar y, Scalar z) : x(x), y(y), z(z) {} /** * The length (or magnitude of the vector). * * @return the calculated length. */ - double Length() const { return sqrt(x * x + y * y + z * z); } + constexpr Scalar Length() const { return sqrt(x * x + y * y + z * z); } - Vector3 Normalize() const { + constexpr Vector3 Normalize() const { const auto len = Length(); return {x / len, y / len, z / len}; } - double Dot(const Vector3& other) const { + constexpr Scalar Dot(const Vector3& other) const { return ((x * other.x) + (y * other.y) + (z * other.z)); } - Vector3 Cross(const Vector3& other) const { + constexpr Vector3 Cross(const Vector3& other) const { return { (y * other.z) - (z * other.y), // (z * other.x) - (x * other.z), // @@ -55,21 +57,21 @@ struct Vector3 { }; } - bool operator==(const Vector3& v) const { + constexpr bool operator==(const Vector3& v) const { return v.x == x && v.y == y && v.z == z; } - bool operator!=(const Vector3& v) const { + constexpr bool operator!=(const Vector3& v) const { return v.x != x || v.y != y || v.z != z; } - Vector3 operator-() const { return Vector3(-x, -y, -z); } + constexpr Vector3 operator-() const { return Vector3(-x, -y, -z); } - Vector3 operator+(const Vector3& v) const { + constexpr Vector3 operator+(const Vector3& v) const { return Vector3(x + v.x, y + v.y, z + v.z); } - Vector3 operator-(const Vector3& v) const { + constexpr Vector3 operator-(const Vector3& v) const { return Vector3(x - v.x, y - v.y, z - v.z); } @@ -83,10 +85,10 @@ struct Vector3 { * * @return the combined vector. */ - static inline Vector3 Combine(const Vector3& a, - double aScale, - const Vector3& b, - double bScale) { + static constexpr Vector3 Combine(const Vector3& a, + Scalar aScale, + const Vector3& b, + Scalar bScale) { return { aScale * a.x + bScale * b.x, // aScale * a.y + bScale * b.y, // @@ -100,44 +102,48 @@ struct Vector3 { struct Vector4 { union { struct { - double x = 0.0; - double y = 0.0; - double z = 0.0; - double w = 1.0; + Scalar x = 0.0; + Scalar y = 0.0; + Scalar z = 0.0; + Scalar w = 1.0; }; - double e[4]; + Scalar e[4]; }; - Vector4() {} + constexpr Vector4() {} - Vector4(double x, double y, double z, double w) : x(x), y(y), z(z), w(w) {} + constexpr Vector4(Scalar x, Scalar y, Scalar z, Scalar w) + : x(x), y(y), z(z), w(w) {} - Vector4(const Vector3& v) : x(v.x), y(v.y), z(v.z) {} + constexpr Vector4(const Vector3& v) : x(v.x), y(v.y), z(v.z) {} - Vector4(const Point& p) : x(p.x), y(p.y) {} + constexpr Vector4(const Point& p) : x(p.x), y(p.y) {} - Vector4 Normalize() const { - const double inverse = 1.0 / sqrt(x * x + y * y + z * z + w * w); + constexpr Vector4 Normalize() const { + const Scalar inverse = 1.0 / sqrt(x * x + y * y + z * z + w * w); return Vector4(x * inverse, y * inverse, z * inverse, w * inverse); } - bool operator==(const Vector4& v) const { + constexpr bool operator==(const Vector4& v) const { return (x == v.x) && (y == v.y) && (z == v.z) && (w == v.w); } - bool operator!=(const Vector4& v) const { + constexpr bool operator!=(const Vector4& v) const { return (x != v.x) || (y != v.y) || (z != v.z) || (w != v.w); } - Vector4 operator+(const Vector4& v) const { + constexpr Vector4 operator+(const Vector4& v) const { return Vector4(x + v.x, y + v.y, z + v.z, w + v.w); } - Vector4 operator-(const Vector4& v) const { + constexpr Vector4 operator-(const Vector4& v) const { return Vector4(x - v.x, y - v.y, z - v.z, w - v.w); } std::string ToString() const; }; +static_assert(sizeof(Vector3) == 3 * sizeof(Scalar)); +static_assert(sizeof(Vector4) == 4 * sizeof(Scalar)); + } // namespace impeller diff --git a/impeller/image/image.cc b/impeller/image/image.cc index b8776eb8c4ad2..aa0f1f9de1d9b 100644 --- a/impeller/image/image.cc +++ b/impeller/image/image.cc @@ -71,7 +71,7 @@ ImageResult Image::Decode() const { } return ImageResult{ - Size{static_cast(width), static_cast(height)}, // size + Size{static_cast(width), static_cast(height)}, // size components, // components std::move(destinationAllocation) // allocation }; From c21d438d3a5b5221f9dfd7776c3af3cddaa73826 Mon Sep 17 00:00:00 2001 From: Chinmay Garde Date: Tue, 15 Jun 2021 13:29:07 -0700 Subject: [PATCH 081/433] Remove stringification in impeller/geometry. --- impeller/geometry/color.cc | 24 ------------------------ impeller/geometry/color.h | 9 --------- impeller/geometry/matrix.cc | 34 ++-------------------------------- impeller/geometry/matrix.h | 6 ------ impeller/geometry/point.cc | 13 +------------ impeller/geometry/point.h | 4 ---- impeller/geometry/rect.cc | 18 ------------------ impeller/geometry/rect.h | 4 ---- impeller/geometry/scalar.h | 2 ++ impeller/geometry/size.cc | 13 +------------ impeller/geometry/size.h | 4 ---- 11 files changed, 6 insertions(+), 125 deletions(-) diff --git a/impeller/geometry/color.cc b/impeller/geometry/color.cc index 3680f2b5f3148..fb052a80f583e 100644 --- a/impeller/geometry/color.cc +++ b/impeller/geometry/color.cc @@ -85,30 +85,6 @@ Color ColorHSB::ToRGBA() const { return Color(0, 0, 0, alpha); } -std::string ColorHSB::ToString() const { - std::stringstream stream; - stream << "{" << hue << ", " << saturation << ", " << brightness << ", " - << alpha << "}"; - return stream.str(); -} - Color::Color(const ColorHSB& hsbColor) : Color(hsbColor.ToRGBA()) {} -std::string Color::ToString() const { - std::stringstream stream; - stream << red << "," << green << "," << blue << "," << alpha; - return stream.str(); -} - -void Color::FromString(const std::string& str) { - std::stringstream stream(str); - stream >> red; - stream.ignore(); - stream >> green; - stream.ignore(); - stream >> blue; - stream.ignore(); - stream >> alpha; -} - } // namespace impeller diff --git a/impeller/geometry/color.h b/impeller/geometry/color.h index 3e0104dd61d58..ede6432835b83 100644 --- a/impeller/geometry/color.h +++ b/impeller/geometry/color.h @@ -4,9 +4,6 @@ #pragma once -#include -#include - #include "impeller/geometry/scalar.h" namespace impeller { @@ -49,10 +46,6 @@ struct Color { alpha == c.alpha; } - std::string ToString() const; - - void FromString(const std::string& str); - static constexpr Color White() { return {1.0, 1.0, 1.0, 1.0}; } static constexpr Color Black() { return {0.0, 0.0, 0.0, 1.0}; } @@ -666,8 +659,6 @@ struct ColorHSB { static ColorHSB FromRGB(Color rgb); Color ToRGBA() const; - - std::string ToString() const; }; static_assert(sizeof(Color) == 4 * sizeof(Scalar)); diff --git a/impeller/geometry/matrix.cc b/impeller/geometry/matrix.cc index 3d8a457ef9610..b783df7955c90 100644 --- a/impeller/geometry/matrix.cc +++ b/impeller/geometry/matrix.cc @@ -2,7 +2,8 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -#include "matrix.h" +#include "impeller/geometry/matrix.h" + #include #include @@ -144,25 +145,6 @@ Matrix Matrix::operator+(const Matrix& o) const { ); } -std::string Matrix::ToString() const { - std::stringstream stream; - for (int i = 0, limit = 16; i < limit; i++) { - stream << m[i]; - if (i != limit - 1) { - stream << ","; - } - } - return stream.str(); -} - -void Matrix::FromString(const std::string& str) { - std::stringstream stream(str); - for (int i = 0; i < 16; i++) { - stream >> m[i]; - stream.ignore(); - } -} - Matrix Matrix::Invert() const { Matrix tmp{ m[5] * m[10] * m[15] - m[5] * m[11] * m[14] - m[9] * m[6] * m[15] + @@ -467,16 +449,4 @@ uint64_t Matrix::Decomposition::GetComponentsMask() const { return mask; } -std::string Matrix::Decomposition::ToString() const { - std::stringstream stream; - - stream << "Translation: " << translation.ToString() << std::endl; - stream << "Scale: " << scale.ToString() << std::endl; - stream << "Shear: " << shear.ToString() << std::endl; - stream << "Perspective: " << perspective.ToString() << std::endl; - stream << "Rotation: " << rotation.ToString() << std::endl; - - return stream.str(); -} - } // namespace impeller diff --git a/impeller/geometry/matrix.h b/impeller/geometry/matrix.h index f33f1755babff..9156b211a6aff 100644 --- a/impeller/geometry/matrix.h +++ b/impeller/geometry/matrix.h @@ -39,8 +39,6 @@ struct Matrix { }; uint64_t GetComponentsMask() const; - - std::string ToString() const; }; using DecompositionResult = @@ -279,10 +277,6 @@ struct Matrix { Matrix operator*(const Matrix& m) const { return Multiply(m); } Matrix operator+(const Matrix& m) const; - - std::string ToString() const; - - void FromString(const std::string& str); }; static_assert(sizeof(struct Matrix) == sizeof(Scalar) * 16, diff --git a/impeller/geometry/point.cc b/impeller/geometry/point.cc index ca72d765501b1..a00f1bf57cffb 100644 --- a/impeller/geometry/point.cc +++ b/impeller/geometry/point.cc @@ -7,17 +7,6 @@ namespace impeller { -std::string Point::ToString() const { - std::stringstream stream; - stream << x << "," << y; - return stream.str(); -} - -void Point::FromString(const std::string& str) { - std::stringstream stream(str); - stream >> x; - stream.ignore(); - stream >> y; -} +// } // namespace impeller diff --git a/impeller/geometry/point.h b/impeller/geometry/point.h index ba46e8283ccd2..10c25d996eec8 100644 --- a/impeller/geometry/point.h +++ b/impeller/geometry/point.h @@ -69,10 +69,6 @@ struct Point { constexpr Scalar GetDistance(const Point& p) const { return sqrt(GetDistanceSquared(p)); } - - std::string ToString() const; - - void FromString(const std::string& str); }; static_assert(sizeof(Point) == 2 * sizeof(Scalar)); diff --git a/impeller/geometry/rect.cc b/impeller/geometry/rect.cc index 72d82d48a40d5..9d7e28fbcca4a 100644 --- a/impeller/geometry/rect.cc +++ b/impeller/geometry/rect.cc @@ -38,22 +38,4 @@ Rect Rect::WithPoints(const std::vector& points) const { return box; } -std::string Rect::ToString() const { - std::stringstream stream; - stream << origin.x << "," << origin.y << "," << size.width << "," - << size.height; - return stream.str(); -} - -void Rect::FromString(const std::string& str) { - std::stringstream stream(str); - stream >> origin.x; - stream.ignore(); - stream >> origin.y; - stream.ignore(); - stream >> size.width; - stream.ignore(); - stream >> size.height; -} - } // namespace impeller diff --git a/impeller/geometry/rect.h b/impeller/geometry/rect.h index 21b5a905f1a5d..52ad8a54ee7b3 100644 --- a/impeller/geometry/rect.h +++ b/impeller/geometry/rect.h @@ -66,10 +66,6 @@ struct Rect { Rect WithPoint(const Point& p) const; Rect WithPoints(const std::vector& points) const; - - std::string ToString() const; - - void FromString(const std::string& str); }; static_assert(sizeof(Rect) == 4 * sizeof(Scalar)); diff --git a/impeller/geometry/scalar.h b/impeller/geometry/scalar.h index bf494586e7cdf..b7fe63c63f3a9 100644 --- a/impeller/geometry/scalar.h +++ b/impeller/geometry/scalar.h @@ -4,6 +4,8 @@ #pragma once +#include + namespace impeller { using Scalar = float; diff --git a/impeller/geometry/size.cc b/impeller/geometry/size.cc index da888d7a1342f..13bd3aebce283 100644 --- a/impeller/geometry/size.cc +++ b/impeller/geometry/size.cc @@ -7,17 +7,6 @@ namespace impeller { -std::string Size::ToString() const { - std::stringstream stream; - stream << width << "," << height; - return stream.str(); -} - -void Size::FromString(const std::string& str) { - std::stringstream stream(str); - stream >> width; - stream.ignore(); - stream >> height; -} +// } // namespace impeller diff --git a/impeller/geometry/size.h b/impeller/geometry/size.h index 04bce608a5795..40fd2c673b290 100644 --- a/impeller/geometry/size.h +++ b/impeller/geometry/size.h @@ -63,10 +63,6 @@ struct Size { constexpr bool IsPositive() const { return width * height > 0.0; } constexpr bool IsEmpty() { return !IsPositive(); } - - std::string ToString() const; - - void FromString(const std::string& str); }; static_assert(sizeof(Size) == 2 * sizeof(Scalar)); From 079b3aa4e3331b759cf51183b8bb309a9f121129 Mon Sep 17 00:00:00 2001 From: Chinmay Garde Date: Tue, 15 Jun 2021 13:44:56 -0700 Subject: [PATCH 082/433] Add some sanity checks to generated bindings code. --- impeller/compiler/code_gen_template.h | 19 ++++++++++++++----- 1 file changed, 14 insertions(+), 5 deletions(-) diff --git a/impeller/compiler/code_gen_template.h b/impeller/compiler/code_gen_template.h index a8e4922538de5..65c512e391820 100644 --- a/impeller/compiler/code_gen_template.h +++ b/impeller/compiler/code_gen_template.h @@ -7,8 +7,8 @@ namespace impeller { namespace compiler { -constexpr std::string_view kReflectionHeaderTemplate = R"~~( -// THIS FILE IS GENERATED BY impellerc. +constexpr std::string_view kReflectionHeaderTemplate = + R"~~(// THIS FILE IS GENERATED BY impellerc. // DO NOT EDIT OR CHECK THIS INTO SOURCE CONTROL #pragma once @@ -37,7 +37,6 @@ struct {{camel_case(shader_name)}}{{camel_case(shader_stage)}}Info { {{member.type}} {{member.name}}; {% endfor %} }; // struct {{def.name}} - static_assert(sizeof({{def.name}}) == {{def.byte_length}}, "Size of {{def.name}} does not match {{def.byte_length}} in the shader."); {% endfor %} // =========================================================================== @@ -102,15 +101,25 @@ struct {{camel_case(shader_name)}}{{camel_case(shader_stage)}}Info { } // namespace impeller )~~"; -constexpr std::string_view kReflectionCCTemplate = R"~~( -// THIS FILE IS GENERATED BY impellerc. +constexpr std::string_view kReflectionCCTemplate = + R"~~(// THIS FILE IS GENERATED BY impellerc. // DO NOT EDIT OR CHECK THIS INTO SOURCE CONTROL #include "{{header_file_name}}" +#include + namespace impeller { namespace shader { +using Info = {{camel_case(shader_name)}}{{camel_case(shader_stage)}}Info; + +{% for def in struct_definitions %} +// Sanity checks for {{def.name}} +static_assert(std::is_standard_layout_v); +static_assert(sizeof(Info::{{def.name}}) == {{def.byte_length}}, + "{{def.name}} size should match that in shader."); +{% endfor %} } // namespace shader From 4b04d9c448b9f1fa9abf058cb15599dd0a28c72e Mon Sep 17 00:00:00 2001 From: Chinmay Garde Date: Wed, 16 Jun 2021 14:15:16 -0700 Subject: [PATCH 083/433] Reflect all known struct types. --- impeller/compiler/BUILD.gn | 1 + impeller/compiler/code_gen_template.h | 8 +- impeller/compiler/reflector.cc | 154 ++++++++++++++++++++++---- impeller/compiler/reflector.h | 12 +- impeller/compositor/render_pass.h | 4 + impeller/compositor/render_pass.mm | 8 +- impeller/primitives/box.vert | 6 - 7 files changed, 160 insertions(+), 33 deletions(-) diff --git a/impeller/compiler/BUILD.gn b/impeller/compiler/BUILD.gn index 25cd7cfbdb39a..f60c6e12de1f3 100644 --- a/impeller/compiler/BUILD.gn +++ b/impeller/compiler/BUILD.gn @@ -20,6 +20,7 @@ source_set("compiler_lib") { ] public_deps = [ + "../geometry", "//flutter/fml", "//flutter/runtime:libdart", "//third_party/inja", diff --git a/impeller/compiler/code_gen_template.h b/impeller/compiler/code_gen_template.h index 65c512e391820..bd03dacfd8bf9 100644 --- a/impeller/compiler/code_gen_template.h +++ b/impeller/compiler/code_gen_template.h @@ -32,7 +32,7 @@ struct {{camel_case(shader_name)}}{{camel_case(shader_stage)}}Info { // =========================================================================== {% for def in struct_definitions %} - struct {{def.name}} { + struct alignas(16) {{def.name}} { {% for member in def.members %} {{member.type}} {{member.name}}; {% endfor %} @@ -117,8 +117,10 @@ using Info = {{camel_case(shader_name)}}{{camel_case(shader_stage)}}Info; {% for def in struct_definitions %} // Sanity checks for {{def.name}} static_assert(std::is_standard_layout_v); -static_assert(sizeof(Info::{{def.name}}) == {{def.byte_length}}, - "{{def.name}} size should match that in shader."); +static_assert(sizeof(Info::{{def.name}}) == {{def.byte_length}}); +{% for member in def.members %} +static_assert(offsetof(Info::{{def.name}}, {{member.name}}) == {{member.offset}}); +{% endfor %} {% endfor %} diff --git a/impeller/compiler/reflector.cc b/impeller/compiler/reflector.cc index 336e5c5cc05e5..8f46111d922fa 100644 --- a/impeller/compiler/reflector.cc +++ b/impeller/compiler/reflector.cc @@ -13,6 +13,8 @@ #include "flutter/fml/logging.h" #include "flutter/impeller/compiler/code_gen_template.h" #include "flutter/impeller/compiler/utilities.h" +#include "flutter/impeller/geometry/matrix.h" +#include "flutter/impeller/geometry/scalar.h" namespace impeller { namespace compiler { @@ -282,19 +284,120 @@ std::optional Reflector::ReflectResources( return result; } -std::string GetHostStructMemberType(const SPIRType& type) { - if (type.width == 32 && type.columns == 1 && type.vecsize == 1) { - return "float"; - } +static std::string TypeNameWithPaddingOfSize(size_t size) { + std::stringstream stream; + stream << "Padding<" << size << ">"; + return stream.str(); +} - if (type.width == 32 && type.columns == 4 && type.vecsize == 4) { - return "Matrix"; +struct KnownType { + std::string name; + size_t byte_size = 0; +}; + +static std::optional ReadKnownScalarType(SPIRType::BaseType type) { + switch (type) { + case SPIRType::BaseType::Boolean: + return KnownType{ + .name = "bool", + .byte_size = sizeof(bool), + }; + case SPIRType::BaseType::Float: + return KnownType{ + .name = "Scalar", + .byte_size = sizeof(Scalar), + }; + case SPIRType::BaseType::UInt: + return KnownType{ + .name = "uint32_t", + .byte_size = sizeof(uint32_t), + }; + case SPIRType::BaseType::Int: + return KnownType{ + .name = "int32_t", + .byte_size = sizeof(int32_t), + }; + default: + break; } + return std::nullopt; +} - const auto byte_length = (type.width * type.vecsize * type.columns) / 8u; - std::stringstream stream; - stream << "Padding<" << byte_length << ">"; - return stream.str(); +std::vector Reflector::ReadStructMembers( + const spirv_cross::TypeID& type_id) const { + const auto& struct_type = compiler_->get_type(type_id); + FML_CHECK(struct_type.basetype == SPIRType::BaseType::Struct); + + std::vector result; + + size_t total_byte_length = 0; + for (size_t i = 0; i < struct_type.member_types.size(); i++) { + const auto& member = compiler_->get_type(struct_type.member_types[i]); + + // Tightly packed 4x4 Matrix is special cased as we know how to work with + // those. + if (member.basetype == SPIRType::BaseType::Float && // + member.width == sizeof(Scalar) * 8 && // + member.columns == 4 && // + member.vecsize == 4 // + ) { + result.emplace_back(StructMember{ + .type = "Matrix", + .name = GetMemberNameAtIndex(struct_type, i), + .offset = total_byte_length, + .byte_length = sizeof(Matrix), + }); + total_byte_length += sizeof(Matrix); + continue; + } + + // Other single isolated scalars. + { + auto maybe_known_type = ReadKnownScalarType(member.basetype); + if (maybe_known_type.has_value() && // + member.columns == 1 && // + member.vecsize == 1 // + ) { + // Add the type directly. + result.emplace_back(StructMember{ + .type = maybe_known_type.value().name, + .name = GetMemberNameAtIndex(struct_type, i), + .offset = total_byte_length, + .byte_length = maybe_known_type.value().byte_size, + }); + total_byte_length += maybe_known_type.value().byte_size; + + // Consider any excess padding. + const auto padding = + (member.width / 8u) - maybe_known_type.value().byte_size; + if (padding != 0) { + result.emplace_back(StructMember{ + .type = TypeNameWithPaddingOfSize(padding), + .name = GetMemberNameAtIndex(struct_type, i, "_pad"), + .offset = total_byte_length, + .byte_length = padding, + }); + total_byte_length += padding; + } + continue; + } + } + + // Catch all for unknown types. Just add the necessary padding to the struct + // and move on. + { + const size_t byte_length = + (member.width * member.columns * member.vecsize) / 8u; + result.emplace_back(StructMember{ + .type = TypeNameWithPaddingOfSize(byte_length), + .name = GetMemberNameAtIndex(struct_type, i), + .offset = total_byte_length, + .byte_length = byte_length, + }); + total_byte_length += byte_length; + } + } + return result; } std::optional Reflector::ReflectStructDefinition( @@ -311,20 +414,26 @@ std::optional Reflector::ReflectStructDefinition( nlohmann::json::object_t struc; struc["name"] = struct_name; - struc["members"] = nlohmann::json::array_t{}; + size_t total_size = 0u; - for (size_t i = 0; i < type.member_types.size(); i++) { - const auto& member_type = compiler_->get_type(type.member_types[i]); - const auto byte_length = + for (const auto& member_type_id : type.member_types) { + const auto& member_type = compiler_->get_type(member_type_id); + total_size += (member_type.width * member_type.vecsize * member_type.columns) / 8u; - auto& member = struc["members"].emplace_back(nlohmann::json::object_t{}); - member["name"] = GetMemberNameAtIndex(type, i); - member["type"] = GetHostStructMemberType(member_type); - member["byte_length"] = byte_length; - member["offset"] = total_size; - total_size += byte_length; } struc["byte_length"] = total_size; + + size_t members_size = 0u; + struc["members"] = nlohmann::json::array_t{}; + const auto struct_members = ReadStructMembers(type_id); + for (const auto& struct_member : struct_members) { + auto& member = struc["members"].emplace_back(nlohmann::json::object_t{}); + member["name"] = struct_member.name; + member["type"] = struct_member.type; + member["offset"] = struct_member.offset; + member["byte_length"] = struct_member.byte_length; + } + return struc; } @@ -347,14 +456,15 @@ std::optional Reflector::GetMemberNameAtIndexIfExists( std::string Reflector::GetMemberNameAtIndex( const spirv_cross::SPIRType& parent_type, - size_t index) const { + size_t index, + std::string suffix) const { if (auto name = GetMemberNameAtIndexIfExists(parent_type, index); name.has_value()) { return name.value(); } static std::atomic_size_t sUnnamedMembersID; std::stringstream stream; - stream << "unnamed_" << sUnnamedMembersID++; + stream << "unnamed_" << sUnnamedMembersID++ << suffix; return stream.str(); } diff --git a/impeller/compiler/reflector.h b/impeller/compiler/reflector.h index bdaf747c21a6f..0b46b8528367e 100644 --- a/impeller/compiler/reflector.h +++ b/impeller/compiler/reflector.h @@ -72,7 +72,17 @@ class Reflector { size_t index) const; std::string GetMemberNameAtIndex(const spirv_cross::SPIRType& parent_type, - size_t index) const; + size_t index, + std::string suffix = "") const; + + struct StructMember { + std::string type; + std::string name; + size_t offset; + size_t byte_length; + }; + std::vector ReadStructMembers( + const spirv_cross::TypeID& type_id) const; FML_DISALLOW_COPY_AND_ASSIGN(Reflector); }; diff --git a/impeller/compositor/render_pass.h b/impeller/compositor/render_pass.h index efb4606512e6f..a0cfe6a8b7939 100644 --- a/impeller/compositor/render_pass.h +++ b/impeller/compositor/render_pass.h @@ -12,6 +12,7 @@ #include "flutter/fml/macros.h" #include "impeller/compositor/command.h" #include "impeller/compositor/formats.h" +#include "impeller/compositor/host_buffer.h" #include "impeller/compositor/texture.h" #include "impeller/geometry/color.h" @@ -70,6 +71,8 @@ class RenderPass { bool IsValid() const; + HostBuffer& GetHostBuffer(); + bool RecordCommand(Command command); bool FinishEncoding(Allocator& transients_allocator) const; @@ -80,6 +83,7 @@ class RenderPass { id buffer_ = nil; MTLRenderPassDescriptor* desc_ = nil; std::vector commands_; + std::shared_ptr transients_buffer_; bool is_valid_ = false; RenderPass(id buffer, const RenderPassDescriptor& desc); diff --git a/impeller/compositor/render_pass.mm b/impeller/compositor/render_pass.mm index a69a339328d46..b6d4a9699a0e3 100644 --- a/impeller/compositor/render_pass.mm +++ b/impeller/compositor/render_pass.mm @@ -118,7 +118,9 @@ static bool ConfigureStencilAttachment( RenderPass::RenderPass(id buffer, const RenderPassDescriptor& desc) - : buffer_(buffer), desc_(desc.ToMTLRenderPassDescriptor()) { + : buffer_(buffer), + desc_(desc.ToMTLRenderPassDescriptor()), + transients_buffer_(HostBuffer::Create()) { if (!buffer_ || !desc_) { return; } @@ -127,6 +129,10 @@ static bool ConfigureStencilAttachment( RenderPass::~RenderPass() = default; +HostBuffer& RenderPass::GetHostBuffer() { + return *transients_buffer_; +} + bool RenderPass::IsValid() const { return is_valid_; } diff --git a/impeller/primitives/box.vert b/impeller/primitives/box.vert index 0f4cab992a832..6fa5eb17d074d 100644 --- a/impeller/primitives/box.vert +++ b/impeller/primitives/box.vert @@ -4,12 +4,6 @@ uniform UniformBuffer { mat4 mvp; - mat4x2 mvp2; - mat4 mvp3; - mat4 mvp4; - bool foo; - float hello; - mat4 mvp5; } uniforms; in vec3 vertexPosition; From c1c1e2b4c65b077e4f85b200da78a34bd5ca93ba Mon Sep 17 00:00:00 2001 From: Chinmay Garde Date: Fri, 18 Jun 2021 14:00:37 -0700 Subject: [PATCH 084/433] Tests for host buffers. --- impeller/compositor/BUILD.gn | 9 ++- impeller/compositor/host_buffer.h | 14 ++++- impeller/compositor/host_buffer.mm | 20 +++++- impeller/compositor/host_buffer_unittests.mm | 64 ++++++++++++++++++++ impeller/compositor/range.h | 9 +++ impeller/compositor/render_pass.h | 2 +- impeller/compositor/render_pass.mm | 2 +- impeller/entity/entity_renderer.mm | 6 ++ 8 files changed, 120 insertions(+), 6 deletions(-) create mode 100644 impeller/compositor/host_buffer_unittests.mm diff --git a/impeller/compositor/BUILD.gn b/impeller/compositor/BUILD.gn index 23bd49504e170..2bcb9053a03e9 100644 --- a/impeller/compositor/BUILD.gn +++ b/impeller/compositor/BUILD.gn @@ -58,12 +58,17 @@ impeller_component("compositor") { "../geometry", "../shader_glue", ] + + libs = [ "Metal.framework" ] } source_set("compositor_unittests") { testonly = true - sources = [] + sources = [ "host_buffer_unittests.mm" ] - deps = [ "//flutter/testing:testing_lib" ] + deps = [ + ":compositor", + "//flutter/testing:testing_lib", + ] } diff --git a/impeller/compositor/host_buffer.h b/impeller/compositor/host_buffer.h index 072da359ffde7..59447e0e40d6d 100644 --- a/impeller/compositor/host_buffer.h +++ b/impeller/compositor/host_buffer.h @@ -5,6 +5,7 @@ #pragma once #include +#include #include "flutter/fml/macros.h" #include "impeller/compositor/buffer.h" @@ -20,7 +21,14 @@ class HostBuffer final : public std::enable_shared_from_this, static std::shared_ptr Create(); - BufferView Emplace(const void* buffer, size_t length); + size_t GetLength() const; + + size_t GetReservedLength() const; + + template >> + [[nodiscard]] BufferView Emplace(const T& t) { + return Emplace(reinterpret_cast(&t), sizeof(T), alignof(T)); + } [[nodiscard]] bool Truncate(size_t length); @@ -36,6 +44,10 @@ class HostBuffer final : public std::enable_shared_from_this, std::shared_ptr GetDeviceBuffer( Allocator& allocator) const override; + [[nodiscard]] BufferView Emplace(const void* buffer, + size_t length, + size_t align); + [[nodiscard]] bool Reserve(size_t reserved); [[nodiscard]] bool ReserveNPOT(size_t reserved); diff --git a/impeller/compositor/host_buffer.mm b/impeller/compositor/host_buffer.mm index c381fc72555bc..2b6b312e840b0 100644 --- a/impeller/compositor/host_buffer.mm +++ b/impeller/compositor/host_buffer.mm @@ -4,6 +4,8 @@ #include "impeller/compositor/host_buffer.h" +#include + #include "flutter/fml/logging.h" #include "impeller/compositor/allocator.h" @@ -21,7 +23,17 @@ ::free(buffer_); } -BufferView HostBuffer::Emplace(const void* buffer, size_t length) { +size_t HostBuffer::GetLength() const { + return length_; +} + +size_t HostBuffer::GetReservedLength() const { + return reserved_; +} + +BufferView HostBuffer::Emplace(const void* buffer, + size_t length, + size_t align) { auto old_length = length_; if (!Truncate(length_ + length)) { return {}; @@ -61,9 +73,13 @@ static uint32_t NextPowerOfTwoSize(uint32_t x) { } bool HostBuffer::Reserve(size_t reserved) { + // Reserve at least one page of data. + reserved = std::max(4096u, reserved); + if (reserved == reserved_) { return true; } + auto new_allocation = ::realloc(buffer_, reserved); if (!new_allocation) { // If new length is zero, a minimum non-zero sized allocation is returned. @@ -72,8 +88,10 @@ static uint32_t NextPowerOfTwoSize(uint32_t x) { FML_LOG(ERROR) << "Allocation failed. Out of memory."; return false; } + buffer_ = static_cast(new_allocation); reserved_ = reserved; + return true; } diff --git a/impeller/compositor/host_buffer_unittests.mm b/impeller/compositor/host_buffer_unittests.mm new file mode 100644 index 0000000000000..597e43ead144b --- /dev/null +++ b/impeller/compositor/host_buffer_unittests.mm @@ -0,0 +1,64 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "flutter/testing/testing.h" +#include "impeller/compositor/host_buffer.h" + +namespace impeller { +namespace testing { + +TEST(HostBufferTest, TestInitialization) { + ASSERT_TRUE(HostBuffer::Create()); + // Newly allocated buffers don't touch the heap till they have to. + ASSERT_EQ(HostBuffer::Create()->GetLength(), 0u); + ASSERT_EQ(HostBuffer::Create()->GetReservedLength(), 0u); +} + +TEST(HostBufferTest, CanEmplace) { + struct Length2 { + uint8_t pad[2]; + }; + static_assert(sizeof(Length2) == 2u); + + auto buffer = HostBuffer::Create(); + + for (size_t i = 0; i < 12500; i++) { + auto view = buffer->Emplace(Length2{}); + ASSERT_TRUE(view); + ASSERT_EQ(buffer->GetLength(), (i + 1) * sizeof(Length2)); + ASSERT_EQ(view.range, Range(i * sizeof(Length2), 2u)); + } +} + +TEST(HostBufferTest, CanEmplaceWithAlignment) { + struct Length2 { + uint8_t pad[2]; + }; + static_assert(sizeof(Length2) == 2); + struct alignas(16) Align16 { + uint8_t pad[2]; + }; + static_assert(alignof(Align16) == 16); + static_assert(sizeof(Align16) == 16); + + auto buffer = HostBuffer::Create(); + ASSERT_TRUE(buffer); + + { + auto view = buffer->Emplace(Length2{}); + ASSERT_TRUE(view); + ASSERT_EQ(buffer->GetLength(), 2u); + ASSERT_EQ(view.range, Range(0u, 2u)); + } + + { + auto view = buffer->Emplace(Align16{}); + ASSERT_TRUE(view); + ASSERT_EQ(buffer->GetLength(), 32u); + ASSERT_EQ(view.range, Range(16u, 16u)); + } +} + +} // namespace testing +} // namespace impeller diff --git a/impeller/compositor/range.h b/impeller/compositor/range.h index a12005fa62e6d..e1a28878136ce 100644 --- a/impeller/compositor/range.h +++ b/impeller/compositor/range.h @@ -13,6 +13,15 @@ namespace impeller { struct Range { size_t offset = 0; size_t length = 0; + + constexpr Range() {} + + constexpr Range(size_t p_offset, size_t p_length) + : offset(p_offset), length(p_length) {} + + constexpr bool operator==(const Range& o) const { + return offset == o.offset && length == o.length; + } }; } // namespace impeller diff --git a/impeller/compositor/render_pass.h b/impeller/compositor/render_pass.h index a0cfe6a8b7939..96d681de9d25f 100644 --- a/impeller/compositor/render_pass.h +++ b/impeller/compositor/render_pass.h @@ -71,7 +71,7 @@ class RenderPass { bool IsValid() const; - HostBuffer& GetHostBuffer(); + HostBuffer& GetTransientsBuffer(); bool RecordCommand(Command command); diff --git a/impeller/compositor/render_pass.mm b/impeller/compositor/render_pass.mm index b6d4a9699a0e3..8f9a5da4c457f 100644 --- a/impeller/compositor/render_pass.mm +++ b/impeller/compositor/render_pass.mm @@ -129,7 +129,7 @@ static bool ConfigureStencilAttachment( RenderPass::~RenderPass() = default; -HostBuffer& RenderPass::GetHostBuffer() { +HostBuffer& RenderPass::GetTransientsBuffer() { return *transients_buffer_; } diff --git a/impeller/entity/entity_renderer.mm b/impeller/entity/entity_renderer.mm index 00e414419b27e..e01aeb05a936a 100644 --- a/impeller/entity/entity_renderer.mm +++ b/impeller/entity/entity_renderer.mm @@ -35,8 +35,14 @@ } bool EntityRenderer::OnRender(RenderPass& pass) { + shader::BoxVertexInfo::UniformBuffer uniforms; + uniforms.mvp = Matrix::MakeOrthographic({800, 600}); + Command cmd; cmd.pipeline = box_primitive_->GetPipeline(); + cmd.vertex_bindings + .buffers[shader::BoxVertexInfo::kUniformUniformBuffer.location] = + pass.GetTransientsBuffer().Emplace(uniforms); pass.RecordCommand(std::move(cmd)); return true; } From ac9c9f02b8771ffc25ea7607cae3197940d1750d Mon Sep 17 00:00:00 2001 From: Chinmay Garde Date: Fri, 18 Jun 2021 14:40:07 -0700 Subject: [PATCH 085/433] Add support for emplace with struct alignments. --- impeller/compositor/host_buffer.h | 2 ++ impeller/compositor/host_buffer.mm | 19 ++++++++++++++++++- impeller/compositor/host_buffer_unittests.mm | 17 ++++++++++++++++- 3 files changed, 36 insertions(+), 2 deletions(-) diff --git a/impeller/compositor/host_buffer.h b/impeller/compositor/host_buffer.h index 59447e0e40d6d..8b76ac141ae4b 100644 --- a/impeller/compositor/host_buffer.h +++ b/impeller/compositor/host_buffer.h @@ -48,6 +48,8 @@ class HostBuffer final : public std::enable_shared_from_this, size_t length, size_t align); + [[nodiscard]] BufferView Emplace(const void* buffer, size_t length); + [[nodiscard]] bool Reserve(size_t reserved); [[nodiscard]] bool ReserveNPOT(size_t reserved); diff --git a/impeller/compositor/host_buffer.mm b/impeller/compositor/host_buffer.mm index 2b6b312e840b0..ee62f5a219d1a 100644 --- a/impeller/compositor/host_buffer.mm +++ b/impeller/compositor/host_buffer.mm @@ -34,13 +34,30 @@ BufferView HostBuffer::Emplace(const void* buffer, size_t length, size_t align) { + if (align == 0 || (length_ % align) == 0) { + return Emplace(buffer, length); + } + + { + auto pad = Emplace(nullptr, align - (length_ % align)); + if (!pad) { + return {}; + } + } + + return Emplace(buffer, length); +} + +BufferView HostBuffer::Emplace(const void* buffer, size_t length) { auto old_length = length_; if (!Truncate(length_ + length)) { return {}; } FML_DCHECK(buffer_); generation_++; - ::memmove(buffer_, buffer, length); + if (buffer) { + ::memmove(buffer_, buffer, length); + } return BufferView{shared_from_this(), Range{old_length, length}}; } diff --git a/impeller/compositor/host_buffer_unittests.mm b/impeller/compositor/host_buffer_unittests.mm index 597e43ead144b..5b678a29bebc3 100644 --- a/impeller/compositor/host_buffer_unittests.mm +++ b/impeller/compositor/host_buffer_unittests.mm @@ -55,8 +55,23 @@ struct alignas(16) Align16 { { auto view = buffer->Emplace(Align16{}); ASSERT_TRUE(view); + ASSERT_EQ(view.range.offset, 16u); + ASSERT_EQ(view.range.length, 16u); ASSERT_EQ(buffer->GetLength(), 32u); - ASSERT_EQ(view.range, Range(16u, 16u)); + } + { + auto view = buffer->Emplace(Length2{}); + ASSERT_TRUE(view); + ASSERT_EQ(buffer->GetLength(), 34u); + ASSERT_EQ(view.range, Range(32u, 2u)); + } + + { + auto view = buffer->Emplace(Align16{}); + ASSERT_TRUE(view); + ASSERT_EQ(view.range.offset, 48u); + ASSERT_EQ(view.range.length, 16u); + ASSERT_EQ(buffer->GetLength(), 64u); } } From 5a60423a6104d6d4d071d2fdb5154ad75732f0ff Mon Sep 17 00:00:00 2001 From: Chinmay Garde Date: Fri, 18 Jun 2021 14:47:34 -0700 Subject: [PATCH 086/433] Minor updates to pass return values. --- impeller/compositor/render_pass.h | 4 ++-- impeller/compositor/renderer.mm | 9 +++++++-- impeller/entity/entity_renderer.mm | 4 +++- 3 files changed, 12 insertions(+), 5 deletions(-) diff --git a/impeller/compositor/render_pass.h b/impeller/compositor/render_pass.h index 96d681de9d25f..e3ad6cbfd6dde 100644 --- a/impeller/compositor/render_pass.h +++ b/impeller/compositor/render_pass.h @@ -73,9 +73,9 @@ class RenderPass { HostBuffer& GetTransientsBuffer(); - bool RecordCommand(Command command); + [[nodiscard]] bool RecordCommand(Command command); - bool FinishEncoding(Allocator& transients_allocator) const; + [[nodiscard]] bool FinishEncoding(Allocator& transients_allocator) const; private: friend class CommandBuffer; diff --git a/impeller/compositor/renderer.mm b/impeller/compositor/renderer.mm index 551dab2380fd0..eb08cd0a2dcec 100644 --- a/impeller/compositor/renderer.mm +++ b/impeller/compositor/renderer.mm @@ -53,13 +53,18 @@ return false; } - render_pass->FinishEncoding(*GetContext()->GetTransientsAllocator()); + if (!render_pass->FinishEncoding(*GetContext()->GetTransientsAllocator())) { + return false; + } ::dispatch_semaphore_wait(frames_in_flight_sema_, DISPATCH_TIME_FOREVER); command_buffer->Commit( - [sema = frames_in_flight_sema_](CommandBuffer::CommitResult) { + [sema = frames_in_flight_sema_](CommandBuffer::CommitResult result) { ::dispatch_semaphore_signal(sema); + if (result != CommandBuffer::CommitResult::kCompleted) { + FML_LOG(ERROR) << "Could not commit command buffer."; + } }); return true; diff --git a/impeller/entity/entity_renderer.mm b/impeller/entity/entity_renderer.mm index e01aeb05a936a..5f096d3810db9 100644 --- a/impeller/entity/entity_renderer.mm +++ b/impeller/entity/entity_renderer.mm @@ -43,7 +43,9 @@ cmd.vertex_bindings .buffers[shader::BoxVertexInfo::kUniformUniformBuffer.location] = pass.GetTransientsBuffer().Emplace(uniforms); - pass.RecordCommand(std::move(cmd)); + if (!pass.RecordCommand(std::move(cmd))) { + return false; + } return true; } From f7ece33388c3753c4a0ec56e2ef4dfef6a86bc43 Mon Sep 17 00:00:00 2001 From: Chinmay Garde Date: Sat, 19 Jun 2021 17:00:33 -0700 Subject: [PATCH 087/433] Wire up vertex buffers. --- impeller/compositor/BUILD.gn | 2 ++ impeller/compositor/host_buffer.h | 8 ++--- impeller/compositor/render_pass.mm | 28 ++++++++++++----- impeller/compositor/vertex_buffer.h | 39 ++++++++++++++++++++++++ impeller/compositor/vertex_buffer.mm | 45 ++++++++++++++++++++++++++++ impeller/entity/entity_renderer.mm | 13 ++++++++ impeller/primitives/box.frag | 5 +--- impeller/primitives/box.vert | 4 --- 8 files changed, 124 insertions(+), 20 deletions(-) create mode 100644 impeller/compositor/vertex_buffer.h create mode 100644 impeller/compositor/vertex_buffer.mm diff --git a/impeller/compositor/BUILD.gn b/impeller/compositor/BUILD.gn index 2bcb9053a03e9..513852ee7b498 100644 --- a/impeller/compositor/BUILD.gn +++ b/impeller/compositor/BUILD.gn @@ -50,6 +50,8 @@ impeller_component("compositor") { "surface.mm", "texture.h", "texture.mm", + "vertex_buffer.h", + "vertex_buffer.mm", "vertex_descriptor.h", "vertex_descriptor.mm", ] diff --git a/impeller/compositor/host_buffer.h b/impeller/compositor/host_buffer.h index 8b76ac141ae4b..b3046b46c39ad 100644 --- a/impeller/compositor/host_buffer.h +++ b/impeller/compositor/host_buffer.h @@ -30,6 +30,10 @@ class HostBuffer final : public std::enable_shared_from_this, return Emplace(reinterpret_cast(&t), sizeof(T), alignof(T)); } + [[nodiscard]] BufferView Emplace(const void* buffer, + size_t length, + size_t align); + [[nodiscard]] bool Truncate(size_t length); private: @@ -44,10 +48,6 @@ class HostBuffer final : public std::enable_shared_from_this, std::shared_ptr GetDeviceBuffer( Allocator& allocator) const override; - [[nodiscard]] BufferView Emplace(const void* buffer, - size_t length, - size_t align); - [[nodiscard]] BufferView Emplace(const void* buffer, size_t length); [[nodiscard]] bool Reserve(size_t reserved); diff --git a/impeller/compositor/render_pass.mm b/impeller/compositor/render_pass.mm index 8f9a5da4c457f..3ec468f0974e7 100644 --- a/impeller/compositor/render_pass.mm +++ b/impeller/compositor/render_pass.mm @@ -228,14 +228,26 @@ static bool Bind(Allocator& allocator, ShaderStage::kFragment)) { return false; } - // [pass drawIndexedPrimitives:MTLPrimitiveTypeTriangleStrip - // indexCount:command.index_count - // indexType:MTLIndexTypeUInt32 - // indexBuffer:command.index_buffer - // indexBufferOffset:(NSUInteger)indexBufferOffset - // instanceCount:1u - // baseVertex:0u - // baseInstance:0u]; + auto index_buffer = command.index_buffer.buffer; + if (!index_buffer) { + return false; + } + auto device_buffer = index_buffer->GetDeviceBuffer(allocator); + if (!device_buffer) { + return false; + } + auto mtl_index_buffer = device_buffer->GetMTLBuffer(); + if (!mtl_index_buffer) { + return false; + } + [pass drawIndexedPrimitives:MTLPrimitiveTypeTriangleStrip + indexCount:command.index_count + indexType:MTLIndexTypeUInt32 + indexBuffer:mtl_index_buffer + indexBufferOffset:command.index_buffer.range.offset + instanceCount:1u + baseVertex:0u + baseInstance:0u]; } return true; } diff --git a/impeller/compositor/vertex_buffer.h b/impeller/compositor/vertex_buffer.h new file mode 100644 index 0000000000000..1d7891507c8fd --- /dev/null +++ b/impeller/compositor/vertex_buffer.h @@ -0,0 +1,39 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#pragma once + +#include +#include +#include + +#include "flutter/fml/macros.h" +#include "impeller/compositor/host_buffer.h" +#include "impeller/geometry/vector.h" + +namespace impeller { + +class VertexBufferBuilder { + public: + VertexBufferBuilder(); + + ~VertexBufferBuilder(); + + VertexBufferBuilder& AddVertices(std::initializer_list vertices); + + BufferView CreateVertexBuffer(HostBuffer& buffer) const; + + BufferView CreateIndexBuffer(HostBuffer& buffer) const; + + size_t GetIndexCount() const; + + private: + // This is a placeholder till vertex de-duplication can be implemented. The + // current implementation is a very dumb placeholder. + std::vector vertices_; + + FML_DISALLOW_COPY_AND_ASSIGN(VertexBufferBuilder); +}; + +} // namespace impeller diff --git a/impeller/compositor/vertex_buffer.mm b/impeller/compositor/vertex_buffer.mm new file mode 100644 index 0000000000000..e9ba41eb2addc --- /dev/null +++ b/impeller/compositor/vertex_buffer.mm @@ -0,0 +1,45 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "impeller/compositor/vertex_buffer.h" + +namespace impeller { + +VertexBufferBuilder::VertexBufferBuilder() = default; + +VertexBufferBuilder::~VertexBufferBuilder() = default; + +size_t VertexBufferBuilder::GetIndexCount() const { + return vertices_.size(); +} + +VertexBufferBuilder& VertexBufferBuilder::AddVertices( + std::initializer_list vertices) { + for (const auto& vertex : vertices) { + vertices_.push_back(vertex); + } + return *this; +} + +BufferView VertexBufferBuilder::CreateVertexBuffer(HostBuffer& buffer) const { + return buffer.Emplace( + vertices_.data(), + vertices_.size() * sizeof(decltype(vertices_)::value_type), + alignof(decltype(vertices_)::value_type)); +} + +BufferView VertexBufferBuilder::CreateIndexBuffer(HostBuffer& buffer) const { + // Soooo dumb! We don't actually need an index buffer right now. But we will + // once de-duplication is done. + std::vector index_buffer; + for (size_t i = 0; i < vertices_.size(); i++) { + index_buffer.push_back(i); + } + return buffer.Emplace( + index_buffer.data(), + index_buffer.size() * sizeof(decltype(index_buffer)::value_type), + alignof(decltype(index_buffer)::value_type)); +} + +} // namespace impeller diff --git a/impeller/entity/entity_renderer.mm b/impeller/entity/entity_renderer.mm index 5f096d3810db9..6f7cdce438444 100644 --- a/impeller/entity/entity_renderer.mm +++ b/impeller/entity/entity_renderer.mm @@ -4,6 +4,7 @@ #include "impeller/entity/entity_renderer.h" #include "impeller/compositor/command.h" +#include "impeller/compositor/vertex_buffer.h" #include "impeller/primitives/box.frag.h" #include "impeller/primitives/box.vert.h" @@ -37,12 +38,24 @@ bool EntityRenderer::OnRender(RenderPass& pass) { shader::BoxVertexInfo::UniformBuffer uniforms; uniforms.mvp = Matrix::MakeOrthographic({800, 600}); + VertexBufferBuilder vertices; + vertices.AddVertices({ + {-0.5, 0.5}, // + {0.5, 0.5}, // + {0.5, -0.5}, // + {-0.5, 0.5}, // + }); Command cmd; cmd.pipeline = box_primitive_->GetPipeline(); cmd.vertex_bindings .buffers[shader::BoxVertexInfo::kUniformUniformBuffer.location] = pass.GetTransientsBuffer().Emplace(uniforms); + cmd.vertex_bindings + .buffers[shader::BoxVertexInfo::kInputVertexPosition.location] = + vertices.CreateVertexBuffer(pass.GetTransientsBuffer()); + cmd.index_buffer = vertices.CreateIndexBuffer(pass.GetTransientsBuffer()); + cmd.index_count = vertices.GetIndexCount(); if (!pass.RecordCommand(std::move(cmd))) { return false; } diff --git a/impeller/primitives/box.frag b/impeller/primitives/box.frag index 28a6c85d15d36..a5560b6a4f5b2 100644 --- a/impeller/primitives/box.frag +++ b/impeller/primitives/box.frag @@ -2,11 +2,8 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -in vec3 color; -in vec3 color2; - layout(location = 0) out vec4 fragColor; void main() { - fragColor = vec4(color * color2, 1.0); + fragColor = vec4(1.0, 1.0, 1.0, 1.0); } diff --git a/impeller/primitives/box.vert b/impeller/primitives/box.vert index 6fa5eb17d074d..ce7ea2c38a9ee 100644 --- a/impeller/primitives/box.vert +++ b/impeller/primitives/box.vert @@ -8,10 +8,6 @@ uniform UniformBuffer { in vec3 vertexPosition; -out vec3 color; -out vec3 color2; - void main() { gl_Position = uniforms.mvp * vec4(vertexPosition, 1.0); - color = vec3(1.0, 1.0, 1.0); } From df663261682df71a28123df4f029c924efdc27f7 Mon Sep 17 00:00:00 2001 From: Chinmay Garde Date: Sun, 20 Jun 2021 03:19:22 -0700 Subject: [PATCH 088/433] Add debug markers to render passes. --- impeller/compositor/command.h | 2 ++ impeller/compositor/host_buffer.h | 1 + impeller/compositor/host_buffer.mm | 2 +- impeller/compositor/render_pass.mm | 8 ++++++++ impeller/entity/entity_renderer.mm | 1 + 5 files changed, 13 insertions(+), 1 deletion(-) diff --git a/impeller/compositor/command.h b/impeller/compositor/command.h index 0676c4427979b..e7c058b008048 100644 --- a/impeller/compositor/command.h +++ b/impeller/compositor/command.h @@ -6,6 +6,7 @@ #include #include +#include #include "flutter/fml/macros.h" #include "impeller/compositor/buffer_view.h" @@ -28,6 +29,7 @@ struct Command { Bindings fragment_bindings; BufferView index_buffer; size_t index_count = 0u; + std::string label; constexpr operator bool() const { return pipeline && pipeline->IsValid(); } }; diff --git a/impeller/compositor/host_buffer.h b/impeller/compositor/host_buffer.h index b3046b46c39ad..4c73d7dbbebd6 100644 --- a/impeller/compositor/host_buffer.h +++ b/impeller/compositor/host_buffer.h @@ -4,6 +4,7 @@ #pragma once +#include #include #include diff --git a/impeller/compositor/host_buffer.mm b/impeller/compositor/host_buffer.mm index ee62f5a219d1a..5cae5bfb658de 100644 --- a/impeller/compositor/host_buffer.mm +++ b/impeller/compositor/host_buffer.mm @@ -102,7 +102,7 @@ static uint32_t NextPowerOfTwoSize(uint32_t x) { // If new length is zero, a minimum non-zero sized allocation is returned. // So this check will not trip and this routine will indicate success as // expected. - FML_LOG(ERROR) << "Allocation failed. Out of memory."; + FML_LOG(ERROR) << "Allocation failed. Out of host memory."; return false; } diff --git a/impeller/compositor/render_pass.mm b/impeller/compositor/render_pass.mm index 3ec468f0974e7..6ec92c0a11d7d 100644 --- a/impeller/compositor/render_pass.mm +++ b/impeller/compositor/render_pass.mm @@ -216,7 +216,15 @@ static bool Bind(Allocator& allocator, } return true; }; + fml::closure pop_debug_marker = [pass]() { [pass popDebugGroup]; }; for (const auto& command : commands_) { + fml::ScopedCleanupClosure auto_pop_debug_marker(pop_debug_marker); + if (!command.label.empty()) { + [pass pushDebugGroup:@(command.label.c_str())]; + } else { + auto_pop_debug_marker.Release(); + } + [pass setRenderPipelineState:command.pipeline->GetMTLRenderPipelineState()]; [pass setDepthStencilState:command.pipeline->GetMTLDepthStencilState()]; [pass setFrontFacingWinding:MTLWindingClockwise]; diff --git a/impeller/entity/entity_renderer.mm b/impeller/entity/entity_renderer.mm index 6f7cdce438444..03aecd113e1c5 100644 --- a/impeller/entity/entity_renderer.mm +++ b/impeller/entity/entity_renderer.mm @@ -47,6 +47,7 @@ }); Command cmd; + cmd.label = "Simple Box"; cmd.pipeline = box_primitive_->GetPipeline(); cmd.vertex_bindings .buffers[shader::BoxVertexInfo::kUniformUniformBuffer.location] = From d3471fe15133dab203946e7636aff374cc871fb3 Mon Sep 17 00:00:00 2001 From: Chinmay Garde Date: Sun, 20 Jun 2021 03:32:31 -0700 Subject: [PATCH 089/433] Name device buffers. --- impeller/compositor/device_buffer.h | 4 ++++ impeller/compositor/device_buffer.mm | 17 +++++++++++++++++ impeller/compositor/host_buffer.h | 4 ++++ impeller/compositor/host_buffer.mm | 6 ++++++ 4 files changed, 31 insertions(+) diff --git a/impeller/compositor/device_buffer.h b/impeller/compositor/device_buffer.h index a484db77766c4..b49684a58c65f 100644 --- a/impeller/compositor/device_buffer.h +++ b/impeller/compositor/device_buffer.h @@ -27,6 +27,10 @@ class DeviceBuffer final : public Buffer, id GetMTLBuffer() const; + bool SetLabel(const std::string& label); + + bool SetLabel(const std::string& label, Range range); + private: friend class Allocator; diff --git a/impeller/compositor/device_buffer.mm b/impeller/compositor/device_buffer.mm index 7ecf61ea76d07..0475e5e9fc8b7 100644 --- a/impeller/compositor/device_buffer.mm +++ b/impeller/compositor/device_buffer.mm @@ -45,4 +45,21 @@ return shared_from_this(); } +bool DeviceBuffer::SetLabel(const std::string& label) { + if (label.empty()) { + return false; + } + [buffer_ setLabel:@(label.c_str())]; + return true; +} + +bool DeviceBuffer::SetLabel(const std::string& label, Range range) { + if (label.empty()) { + return false; + } + [buffer_ addDebugMarker:@(label.c_str()) + range:NSMakeRange(range.offset, range.length)]; + return true; +} + } // namespace impeller diff --git a/impeller/compositor/host_buffer.h b/impeller/compositor/host_buffer.h index 4c73d7dbbebd6..75cd731ec23f1 100644 --- a/impeller/compositor/host_buffer.h +++ b/impeller/compositor/host_buffer.h @@ -6,6 +6,7 @@ #include #include +#include #include #include "flutter/fml/macros.h" @@ -22,6 +23,8 @@ class HostBuffer final : public std::enable_shared_from_this, static std::shared_ptr Create(); + void SetLabel(std::string label); + size_t GetLength() const; size_t GetReservedLength() const; @@ -44,6 +47,7 @@ class HostBuffer final : public std::enable_shared_from_this, size_t length_ = 0; size_t reserved_ = 0; size_t generation_ = 1u; + std::string label_; // |Buffer| std::shared_ptr GetDeviceBuffer( diff --git a/impeller/compositor/host_buffer.mm b/impeller/compositor/host_buffer.mm index 5cae5bfb658de..da32b1b1b452c 100644 --- a/impeller/compositor/host_buffer.mm +++ b/impeller/compositor/host_buffer.mm @@ -10,6 +10,7 @@ #include "impeller/compositor/allocator.h" #include "impeller/compositor/buffer_view.h" +#include "impeller/compositor/device_buffer.h" namespace impeller { @@ -23,6 +24,10 @@ ::free(buffer_); } +void HostBuffer::SetLabel(std::string label) { + label_ = std::move(label); +} + size_t HostBuffer::GetLength() const { return length_; } @@ -121,6 +126,7 @@ static uint32_t NextPowerOfTwoSize(uint32_t x) { if (!new_buffer) { return nullptr; } + new_buffer->SetLabel(label_); device_buffer_generation_ = generation_; device_buffer_ = std::move(new_buffer); return device_buffer_; From cb6ab8b1ad8c5d009444f1a3c98d182164b7d8b0 Mon Sep 17 00:00:00 2001 From: Chinmay Garde Date: Sun, 20 Jun 2021 03:54:10 -0700 Subject: [PATCH 090/433] Embed shader and driver sources in compiled Metal AIR files. --- impeller/tools/build_metal_library.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/impeller/tools/build_metal_library.py b/impeller/tools/build_metal_library.py index 4ef75a4ee00b9..e6ac18880fbd1 100644 --- a/impeller/tools/build_metal_library.py +++ b/impeller/tools/build_metal_library.py @@ -37,6 +37,8 @@ def Main(): command = [ "xcrun", "metal", + "-MO", + "-gline-tables-only", # Both user and system header will be tracked. "-MMD", "-MF", From 73ba703cca8268bd0d5cca8824dd3ed920c162b4 Mon Sep 17 00:00:00 2001 From: Chinmay Garde Date: Sun, 20 Jun 2021 03:59:46 -0700 Subject: [PATCH 091/433] Add FIXME to address removing debug information from generated shaders. --- impeller/tools/build_metal_library.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/impeller/tools/build_metal_library.py b/impeller/tools/build_metal_library.py index e6ac18880fbd1..e9ba9bcc86b1a 100644 --- a/impeller/tools/build_metal_library.py +++ b/impeller/tools/build_metal_library.py @@ -37,6 +37,8 @@ def Main(): command = [ "xcrun", "metal", + # TODO: Embeds both sources and driver options in the output. This aids in + # debugging but should be removed from release builds. "-MO", "-gline-tables-only", # Both user and system header will be tracked. From cea3a9bebac70ddb132ebffb8699b2afc3a34e0e Mon Sep 17 00:00:00 2001 From: Chinmay Garde Date: Mon, 21 Jun 2021 13:25:08 -0700 Subject: [PATCH 092/433] Fix uniform alignment on a per platform basis. --- impeller/compositor/BUILD.gn | 2 ++ impeller/compositor/host_buffer.h | 7 +++++++ impeller/compositor/platform.h | 24 ++++++++++++++++++++++++ impeller/compositor/platform.mm | 11 +++++++++++ impeller/compositor/render_pass.h | 3 +++ impeller/compositor/render_pass.mm | 10 ++++++++++ impeller/entity/entity_renderer.mm | 10 ++++++---- 7 files changed, 63 insertions(+), 4 deletions(-) create mode 100644 impeller/compositor/platform.h create mode 100644 impeller/compositor/platform.mm diff --git a/impeller/compositor/BUILD.gn b/impeller/compositor/BUILD.gn index 513852ee7b498..b5b5407222cbf 100644 --- a/impeller/compositor/BUILD.gn +++ b/impeller/compositor/BUILD.gn @@ -34,6 +34,8 @@ impeller_component("compositor") { "pipeline_descriptor.mm", "pipeline_library.h", "pipeline_library.mm", + "platform.h", + "platform.mm", "range.cc", "range.h", "render_pass.h", diff --git a/impeller/compositor/host_buffer.h b/impeller/compositor/host_buffer.h index 75cd731ec23f1..8ac5fc77bc401 100644 --- a/impeller/compositor/host_buffer.h +++ b/impeller/compositor/host_buffer.h @@ -12,6 +12,7 @@ #include "flutter/fml/macros.h" #include "impeller/compositor/buffer.h" #include "impeller/compositor/buffer_view.h" +#include "impeller/compositor/platform.h" namespace impeller { @@ -29,6 +30,12 @@ class HostBuffer final : public std::enable_shared_from_this, size_t GetReservedLength() const; + template >> + [[nodiscard]] BufferView EmplaceUniform(const T& t) { + return Emplace(reinterpret_cast(&t), sizeof(T), + std::max(alignof(T), DefaultUniformAlignment())); + } + template >> [[nodiscard]] BufferView Emplace(const T& t) { return Emplace(reinterpret_cast(&t), sizeof(T), alignof(T)); diff --git a/impeller/compositor/platform.h b/impeller/compositor/platform.h new file mode 100644 index 0000000000000..9044c8b78bc8b --- /dev/null +++ b/impeller/compositor/platform.h @@ -0,0 +1,24 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#pragma once + +#include + +#include "flutter/fml/build_config.h" +#include "flutter/fml/macros.h" + +namespace impeller { + +constexpr size_t DefaultUniformAlignment() { +#if OS_IOS + return 16u; +#elif OS_MACOSX + return 256u; +#else +#error "Unsupported platform". +#endif +} + +} // namespace impeller diff --git a/impeller/compositor/platform.mm b/impeller/compositor/platform.mm new file mode 100644 index 0000000000000..1ce3def0ad608 --- /dev/null +++ b/impeller/compositor/platform.mm @@ -0,0 +1,11 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "impeller/compositor/platform.h" + +namespace impeller { + +// + +} // namespace impeller diff --git a/impeller/compositor/render_pass.h b/impeller/compositor/render_pass.h index e3ad6cbfd6dde..877746110e991 100644 --- a/impeller/compositor/render_pass.h +++ b/impeller/compositor/render_pass.h @@ -71,6 +71,8 @@ class RenderPass { bool IsValid() const; + void SetLabel(std::string label); + HostBuffer& GetTransientsBuffer(); [[nodiscard]] bool RecordCommand(Command command); @@ -84,6 +86,7 @@ class RenderPass { MTLRenderPassDescriptor* desc_ = nil; std::vector commands_; std::shared_ptr transients_buffer_; + std::string label_; bool is_valid_ = false; RenderPass(id buffer, const RenderPassDescriptor& desc); diff --git a/impeller/compositor/render_pass.mm b/impeller/compositor/render_pass.mm index 6ec92c0a11d7d..cffd28d2a2fb6 100644 --- a/impeller/compositor/render_pass.mm +++ b/impeller/compositor/render_pass.mm @@ -137,14 +137,24 @@ static bool ConfigureStencilAttachment( return is_valid_; } +void RenderPass::SetLabel(std::string label) { + label_ = std::move(label); +} + bool RenderPass::FinishEncoding(Allocator& transients_allocator) const { if (!IsValid()) { return false; } auto pass = [buffer_ renderCommandEncoderWithDescriptor:desc_]; + if (!pass) { return false; } + + if (!label_.empty()) { + [pass setLabel:@(label_.c_str())]; + } + // Success or failure, the pass must end. The buffer can only process one pass // at a time. fml::ScopedCleanupClosure auto_end([pass]() { [pass endEncoding]; }); diff --git a/impeller/entity/entity_renderer.mm b/impeller/entity/entity_renderer.mm index 03aecd113e1c5..42659daa9ad91 100644 --- a/impeller/entity/entity_renderer.mm +++ b/impeller/entity/entity_renderer.mm @@ -36,6 +36,8 @@ } bool EntityRenderer::OnRender(RenderPass& pass) { + pass.SetLabel("EntityRenderer"); + shader::BoxVertexInfo::UniformBuffer uniforms; uniforms.mvp = Matrix::MakeOrthographic({800, 600}); VertexBufferBuilder vertices; @@ -47,14 +49,14 @@ }); Command cmd; - cmd.label = "Simple Box"; + cmd.label = "Box"; cmd.pipeline = box_primitive_->GetPipeline(); - cmd.vertex_bindings - .buffers[shader::BoxVertexInfo::kUniformUniformBuffer.location] = - pass.GetTransientsBuffer().Emplace(uniforms); cmd.vertex_bindings .buffers[shader::BoxVertexInfo::kInputVertexPosition.location] = vertices.CreateVertexBuffer(pass.GetTransientsBuffer()); + cmd.vertex_bindings + .buffers[shader::BoxVertexInfo::kUniformUniformBuffer.location] = + pass.GetTransientsBuffer().EmplaceUniform(uniforms); cmd.index_buffer = vertices.CreateIndexBuffer(pass.GetTransientsBuffer()); cmd.index_count = vertices.GetIndexCount(); if (!pass.RecordCommand(std::move(cmd))) { From 7c8ff5c8d4a2609de7573f91c28d76e6188c58fa Mon Sep 17 00:00:00 2001 From: Chinmay Garde Date: Mon, 21 Jun 2021 15:01:40 -0700 Subject: [PATCH 093/433] Debug and fix buffer management. --- impeller/compiler/compiler.cc | 3 +-- impeller/compositor/device_buffer.mm | 2 +- impeller/compositor/host_buffer.mm | 2 +- impeller/compositor/render_pass.mm | 2 ++ impeller/compositor/vertex_descriptor.h | 9 +++++---- impeller/compositor/vertex_descriptor.mm | 5 +++-- impeller/entity/entity_renderer.mm | 14 ++++++-------- impeller/primitives/box.vert | 2 +- 8 files changed, 20 insertions(+), 19 deletions(-) diff --git a/impeller/compiler/compiler.cc b/impeller/compiler/compiler.cc index 7856a9a082519..9bd9e076d8e24 100644 --- a/impeller/compiler/compiler.cc +++ b/impeller/compiler/compiler.cc @@ -378,7 +378,6 @@ Compiler::SourceType Compiler::SourceTypeFromFileName( std::string Compiler::EntryPointFromSourceName(const std::string& file_name, SourceType type) { std::stringstream stream; - stream << "__impeller_"; std::filesystem::path file_path(file_name); stream << file_path.stem().native() << "_"; switch (type) { @@ -392,7 +391,7 @@ std::string Compiler::EntryPointFromSourceName(const std::string& file_name, stream << "fragment"; break; } - stream << "_main__"; + stream << "_main"; return stream.str(); } diff --git a/impeller/compositor/device_buffer.mm b/impeller/compositor/device_buffer.mm index 0475e5e9fc8b7..ed089d5347297 100644 --- a/impeller/compositor/device_buffer.mm +++ b/impeller/compositor/device_buffer.mm @@ -32,7 +32,7 @@ return false; } - ::memcpy(dest + offset, source + source_range.offset, source_range.length); + ::memmove(dest + offset, source + source_range.offset, source_range.length); [buffer_ didModifyRange:NSMakeRange(offset, source_range.length)]; diff --git a/impeller/compositor/host_buffer.mm b/impeller/compositor/host_buffer.mm index da32b1b1b452c..7784ad141f617 100644 --- a/impeller/compositor/host_buffer.mm +++ b/impeller/compositor/host_buffer.mm @@ -61,7 +61,7 @@ FML_DCHECK(buffer_); generation_++; if (buffer) { - ::memmove(buffer_, buffer, length); + ::memmove(buffer_ + old_length, buffer, length); } return BufferView{shared_from_this(), Range{old_length, length}}; } diff --git a/impeller/compositor/render_pass.mm b/impeller/compositor/render_pass.mm index cffd28d2a2fb6..f7566cdadd009 100644 --- a/impeller/compositor/render_pass.mm +++ b/impeller/compositor/render_pass.mm @@ -258,6 +258,8 @@ static bool Bind(Allocator& allocator, if (!mtl_index_buffer) { return false; } + FML_DCHECK(command.index_count * sizeof(uint32_t) == + command.index_buffer.range.length); [pass drawIndexedPrimitives:MTLPrimitiveTypeTriangleStrip indexCount:command.index_count indexType:MTLIndexTypeUInt32 diff --git a/impeller/compositor/vertex_descriptor.h b/impeller/compositor/vertex_descriptor.h index 8c2c0bb938f88..ca96a21e52f27 100644 --- a/impeller/compositor/vertex_descriptor.h +++ b/impeller/compositor/vertex_descriptor.h @@ -41,13 +41,14 @@ class VertexDescriptor final : public Comparable { struct StageInput { size_t location; MTLVertexFormat format; - size_t stride; + size_t length; - StageInput(size_t p_location, MTLVertexFormat p_format, size_t p_stride) - : location(p_location), format(p_format), stride(p_stride) {} + StageInput(size_t p_location, MTLVertexFormat p_format, size_t p_length) + : location(p_location), format(p_format), length(p_length) {} constexpr bool operator==(const StageInput& other) const { - return location == other.location && format == other.format; + return location == other.location && format == other.format && + length == other.length; } }; std::vector stage_inputs_; diff --git a/impeller/compositor/vertex_descriptor.mm b/impeller/compositor/vertex_descriptor.mm index e592c4f865efd..6c68fc544da2b 100644 --- a/impeller/compositor/vertex_descriptor.mm +++ b/impeller/compositor/vertex_descriptor.mm @@ -181,7 +181,8 @@ static MTLVertexFormat ReadStageInputFormat(const ShaderStageIOSlot& input) { return false; } stage_inputs_.emplace_back( - StageInput{input->location, vertex_format, input->bit_width / 8}); + StageInput{input->location, vertex_format, + (input->bit_width * input->vec_size) / 8}); } return true; @@ -198,7 +199,7 @@ static MTLVertexFormat ReadStageInputFormat(const ShaderStageIOSlot& input) { // All vertex inputs are interleaved and tightly packed in one buffer at // zero index. attrib.bufferIndex = 0u; - stride += input.stride; + stride += input.length; } descriptor.layouts[0].stride = stride; diff --git a/impeller/entity/entity_renderer.mm b/impeller/entity/entity_renderer.mm index 42659daa9ad91..51fb100442935 100644 --- a/impeller/entity/entity_renderer.mm +++ b/impeller/entity/entity_renderer.mm @@ -42,20 +42,18 @@ uniforms.mvp = Matrix::MakeOrthographic({800, 600}); VertexBufferBuilder vertices; vertices.AddVertices({ - {-0.5, 0.5}, // - {0.5, 0.5}, // - {0.5, -0.5}, // - {-0.5, 0.5}, // + {-0.5, 0.5, 1.0}, // + {0.5, 0.5, 1.0}, // + {0.5, -0.5, 1.0}, // + {-0.5, -0.5, 1.0}, // }); Command cmd; cmd.label = "Box"; cmd.pipeline = box_primitive_->GetPipeline(); - cmd.vertex_bindings - .buffers[shader::BoxVertexInfo::kInputVertexPosition.location] = + cmd.vertex_bindings.buffers[0u] = vertices.CreateVertexBuffer(pass.GetTransientsBuffer()); - cmd.vertex_bindings - .buffers[shader::BoxVertexInfo::kUniformUniformBuffer.location] = + cmd.vertex_bindings.buffers[1u] = pass.GetTransientsBuffer().EmplaceUniform(uniforms); cmd.index_buffer = vertices.CreateIndexBuffer(pass.GetTransientsBuffer()); cmd.index_count = vertices.GetIndexCount(); diff --git a/impeller/primitives/box.vert b/impeller/primitives/box.vert index ce7ea2c38a9ee..1b1fa9bf79abe 100644 --- a/impeller/primitives/box.vert +++ b/impeller/primitives/box.vert @@ -9,5 +9,5 @@ uniform UniformBuffer { in vec3 vertexPosition; void main() { - gl_Position = uniforms.mvp * vec4(vertexPosition, 1.0); + gl_Position = vec4(vertexPosition, 1.0); } From e4de8384db71ae6cf1c26e41cfe76e3defb7f388 Mon Sep 17 00:00:00 2001 From: Chinmay Garde Date: Tue, 22 Jun 2021 12:43:14 -0700 Subject: [PATCH 094/433] Add support for primitive types. --- impeller/compositor/BUILD.gn | 4 ++-- impeller/compositor/command.h | 2 ++ impeller/compositor/formats.h | 10 ++++++++++ impeller/compositor/formats_metal.h | 16 ++++++++++++++++ impeller/compositor/render_pass.mm | 2 +- impeller/compositor/renderer.mm | 4 ++++ impeller/compositor/surface.h | 8 +++++++- impeller/compositor/surface.mm | 14 +++++++++++++- ...{vertex_buffer.h => vertex_buffer_builder.h} | 0 ...ertex_buffer.mm => vertex_buffer_builder.mm} | 2 +- impeller/entity/entity_renderer.mm | 17 +++++++++++------ impeller/host/impeller_renderer.mm | 8 ++++++-- 12 files changed, 73 insertions(+), 14 deletions(-) rename impeller/compositor/{vertex_buffer.h => vertex_buffer_builder.h} (100%) rename impeller/compositor/{vertex_buffer.mm => vertex_buffer_builder.mm} (96%) diff --git a/impeller/compositor/BUILD.gn b/impeller/compositor/BUILD.gn index b5b5407222cbf..ea5e89187c36f 100644 --- a/impeller/compositor/BUILD.gn +++ b/impeller/compositor/BUILD.gn @@ -52,8 +52,8 @@ impeller_component("compositor") { "surface.mm", "texture.h", "texture.mm", - "vertex_buffer.h", - "vertex_buffer.mm", + "vertex_buffer_builder.h", + "vertex_buffer_builder.mm", "vertex_descriptor.h", "vertex_descriptor.mm", ] diff --git a/impeller/compositor/command.h b/impeller/compositor/command.h index e7c058b008048..3e0a455d635c4 100644 --- a/impeller/compositor/command.h +++ b/impeller/compositor/command.h @@ -10,6 +10,7 @@ #include "flutter/fml/macros.h" #include "impeller/compositor/buffer_view.h" +#include "impeller/compositor/formats.h" #include "impeller/compositor/pipeline.h" namespace impeller { @@ -30,6 +31,7 @@ struct Command { BufferView index_buffer; size_t index_count = 0u; std::string label; + PrimitiveType primitive_type; constexpr operator bool() const { return pipeline && pipeline->IsValid(); } }; diff --git a/impeller/compositor/formats.h b/impeller/compositor/formats.h index 1b162db588203..986274185fbd5 100644 --- a/impeller/compositor/formats.h +++ b/impeller/compositor/formats.h @@ -56,6 +56,16 @@ enum class StoreAction { kStore, }; +enum class PrimitiveType { + kTriange, + kTriangeStrip, + kLine, + kLineStrip, + kPoint, + // Triangle fans are implementation dependent and need extra extensions + // checks. Hence, they are not supported here. +}; + enum class ColorWriteMask : uint64_t { kNone = 0, kRed = 1 << 0, diff --git a/impeller/compositor/formats_metal.h b/impeller/compositor/formats_metal.h index 5e7126f5a3822..d68934f43ab7a 100644 --- a/impeller/compositor/formats_metal.h +++ b/impeller/compositor/formats_metal.h @@ -64,6 +64,22 @@ constexpr MTLBlendFactor ToMTLBlendFactor(BlendFactor type) { return MTLBlendFactorZero; }; +constexpr MTLPrimitiveType ToMTLPrimitiveType(PrimitiveType type) { + switch (type) { + case PrimitiveType::kTriange: + return MTLPrimitiveTypeTriangle; + case PrimitiveType::kTriangeStrip: + return MTLPrimitiveTypeTriangleStrip; + case PrimitiveType::kLine: + return MTLPrimitiveTypeLine; + case PrimitiveType::kLineStrip: + return MTLPrimitiveTypeLineStrip; + case PrimitiveType::kPoint: + return MTLPrimitiveTypePoint; + } + return MTLPrimitiveTypePoint; +} + constexpr MTLBlendOperation ToMTLBlendOperation(BlendOperation type) { switch (type) { case BlendOperation::kAdd: diff --git a/impeller/compositor/render_pass.mm b/impeller/compositor/render_pass.mm index f7566cdadd009..0d7f586d2e086 100644 --- a/impeller/compositor/render_pass.mm +++ b/impeller/compositor/render_pass.mm @@ -260,7 +260,7 @@ static bool Bind(Allocator& allocator, } FML_DCHECK(command.index_count * sizeof(uint32_t) == command.index_buffer.range.length); - [pass drawIndexedPrimitives:MTLPrimitiveTypeTriangleStrip + [pass drawIndexedPrimitives:ToMTLPrimitiveType(command.primitive_type) indexCount:command.index_count indexType:MTLIndexTypeUInt32 indexBuffer:mtl_index_buffer diff --git a/impeller/compositor/renderer.mm b/impeller/compositor/renderer.mm index eb08cd0a2dcec..20c0c4b42b0e8 100644 --- a/impeller/compositor/renderer.mm +++ b/impeller/compositor/renderer.mm @@ -53,6 +53,10 @@ return false; } + if (!surface.Present()) { + return false; + } + if (!render_pass->FinishEncoding(*GetContext()->GetTransientsAllocator())) { return false; } diff --git a/impeller/compositor/surface.h b/impeller/compositor/surface.h index f4e01d25fa938..0d91604be14db 100644 --- a/impeller/compositor/surface.h +++ b/impeller/compositor/surface.h @@ -6,6 +6,7 @@ #include +#include #include #include "flutter/fml/macros.h" @@ -16,16 +17,21 @@ namespace impeller { class Surface { public: - Surface(RenderPassDescriptor desc); + Surface(RenderPassDescriptor desc, + std::function present_callback); ~Surface(); + bool Present() const; + bool IsValid() const; const RenderPassDescriptor& GetRenderPassDescriptor() const; private: RenderPassDescriptor desc_; + std::function present_callback_; + bool is_valid_ = false; FML_DISALLOW_COPY_AND_ASSIGN(Surface); diff --git a/impeller/compositor/surface.mm b/impeller/compositor/surface.mm index b88c450d3db21..dea5e3327a761 100644 --- a/impeller/compositor/surface.mm +++ b/impeller/compositor/surface.mm @@ -8,7 +8,9 @@ namespace impeller { -Surface::Surface(RenderPassDescriptor desc) : desc_(std::move(desc)) { +Surface::Surface(RenderPassDescriptor desc, + std::function present_callback) + : desc_(std::move(desc)), present_callback_(present_callback) { if (!desc_.HasColorAttachment(0)) { return; } @@ -17,6 +19,16 @@ Surface::~Surface() = default; +bool Surface::Present() const { + auto callback = present_callback_; + + if (!callback) { + return true; + } + + return callback(); +} + bool Surface::IsValid() const { return is_valid_; } diff --git a/impeller/compositor/vertex_buffer.h b/impeller/compositor/vertex_buffer_builder.h similarity index 100% rename from impeller/compositor/vertex_buffer.h rename to impeller/compositor/vertex_buffer_builder.h diff --git a/impeller/compositor/vertex_buffer.mm b/impeller/compositor/vertex_buffer_builder.mm similarity index 96% rename from impeller/compositor/vertex_buffer.mm rename to impeller/compositor/vertex_buffer_builder.mm index e9ba41eb2addc..12c72d189a75c 100644 --- a/impeller/compositor/vertex_buffer.mm +++ b/impeller/compositor/vertex_buffer_builder.mm @@ -2,7 +2,7 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -#include "impeller/compositor/vertex_buffer.h" +#include "impeller/compositor/vertex_buffer_builder.h" namespace impeller { diff --git a/impeller/entity/entity_renderer.mm b/impeller/entity/entity_renderer.mm index 51fb100442935..cec785103ee16 100644 --- a/impeller/entity/entity_renderer.mm +++ b/impeller/entity/entity_renderer.mm @@ -3,8 +3,9 @@ // found in the LICENSE file. #include "impeller/entity/entity_renderer.h" + #include "impeller/compositor/command.h" -#include "impeller/compositor/vertex_buffer.h" +#include "impeller/compositor/vertex_buffer_builder.h" #include "impeller/primitives/box.frag.h" #include "impeller/primitives/box.vert.h" @@ -40,23 +41,27 @@ shader::BoxVertexInfo::UniformBuffer uniforms; uniforms.mvp = Matrix::MakeOrthographic({800, 600}); - VertexBufferBuilder vertices; - vertices.AddVertices({ + VertexBufferBuilder vertex_builder; + vertex_builder.AddVertices({ {-0.5, 0.5, 1.0}, // {0.5, 0.5, 1.0}, // {0.5, -0.5, 1.0}, // + {0.5, -0.5, 1.0}, // {-0.5, -0.5, 1.0}, // + {-0.5, 0.5, 1.0}, // }); Command cmd; cmd.label = "Box"; cmd.pipeline = box_primitive_->GetPipeline(); cmd.vertex_bindings.buffers[0u] = - vertices.CreateVertexBuffer(pass.GetTransientsBuffer()); + vertex_builder.CreateVertexBuffer(pass.GetTransientsBuffer()); cmd.vertex_bindings.buffers[1u] = pass.GetTransientsBuffer().EmplaceUniform(uniforms); - cmd.index_buffer = vertices.CreateIndexBuffer(pass.GetTransientsBuffer()); - cmd.index_count = vertices.GetIndexCount(); + cmd.index_buffer = + vertex_builder.CreateIndexBuffer(pass.GetTransientsBuffer()); + cmd.index_count = vertex_builder.GetIndexCount(); + cmd.primitive_type = PrimitiveType::kTriange; if (!pass.RecordCommand(std::move(cmd))) { return false; } diff --git a/impeller/host/impeller_renderer.mm b/impeller/host/impeller_renderer.mm index d792bd9d7792e..a874a6f35c813 100644 --- a/impeller/host/impeller_renderer.mm +++ b/impeller/host/impeller_renderer.mm @@ -226,11 +226,15 @@ - (void)_updateGameState { - (void)drawInMTKView:(nonnull MTKView*)view { impeller::Surface surface( - impeller::FromMTLRenderPassDescriptor(view.currentRenderPassDescriptor)); + impeller::FromMTLRenderPassDescriptor(view.currentRenderPassDescriptor), + [drawable = view.currentDrawable]() { + [drawable present]; + return true; + }); if (!renderer_->Render(surface)) { FML_LOG(ERROR) << "Could not render."; } - + return; /// Per frame updates here dispatch_semaphore_wait(_inFlightSemaphore, DISPATCH_TIME_FOREVER); From a46ad15d8eea1a76d3c39f7c3af0c4cb4dcaf537 Mon Sep 17 00:00:00 2001 From: Chinmay Garde Date: Tue, 22 Jun 2021 13:18:37 -0700 Subject: [PATCH 095/433] Better names for device objects. --- impeller/BUILD.gn | 1 + impeller/base/BUILD.gn | 17 +++++++++++++++++ impeller/base/base.h | 7 +++++++ impeller/base/config.h | 20 ++++++++++++++++++++ impeller/base/strings.cc | 21 +++++++++++++++++++++ impeller/base/strings.h | 16 ++++++++++++++++ impeller/compositor/BUILD.gn | 1 + impeller/compositor/render_pass.mm | 2 ++ impeller/entity/entity_renderer.mm | 2 +- impeller/primitives/box_primitive.mm | 3 ++- 10 files changed, 88 insertions(+), 2 deletions(-) create mode 100644 impeller/base/BUILD.gn create mode 100644 impeller/base/base.h create mode 100644 impeller/base/config.h create mode 100644 impeller/base/strings.cc create mode 100644 impeller/base/strings.h diff --git a/impeller/BUILD.gn b/impeller/BUILD.gn index 8bcef454c6868..31d5db29906e8 100644 --- a/impeller/BUILD.gn +++ b/impeller/BUILD.gn @@ -8,6 +8,7 @@ config("impeller_public_config") { group("impeller") { deps = [ + "base", "compiler", "compositor", "entity", diff --git a/impeller/base/BUILD.gn b/impeller/base/BUILD.gn new file mode 100644 index 0000000000000..3f875529f1408 --- /dev/null +++ b/impeller/base/BUILD.gn @@ -0,0 +1,17 @@ +# Copyright 2013 The Flutter Authors. All rights reserved. +# Use of this source code is governed by a BSD-style license that can be +# found in the LICENSE file. + +import("../tools/impeller.gni") + +impeller_component("base") { + # Only the umbrella header may be imported. + public = [ "base.h" ] + + sources = [ + "base.h", + "config.h", + "strings.cc", + "strings.h", + ] +} diff --git a/impeller/base/base.h b/impeller/base/base.h new file mode 100644 index 0000000000000..dc6e687dcfb70 --- /dev/null +++ b/impeller/base/base.h @@ -0,0 +1,7 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#pragma once + +#include "impeller/base/strings.h" diff --git a/impeller/base/config.h b/impeller/base/config.h new file mode 100644 index 0000000000000..c0077d983b892 --- /dev/null +++ b/impeller/base/config.h @@ -0,0 +1,20 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#pragma once + +#include "flutter/fml/build_config.h" + +#if defined(__GNUC__) || defined(__clang__) +#define IMPELLER_COMPILER_CLANG 1 +#else // defined(__GNUC__) || defined(__clang__) +#define IMPELLER_COMPILER_CLANG 0 +#endif // defined(__GNUC__) || defined(__clang__) + +#if IMPELLER_COMPILER_CLANG +#define IMPELLER_PRINTF_FORMAT(format_number, args_number) \ + __attribute__((format(printf, format_number, args_number))) +#else // IMPELLER_COMPILER_CLANG +#define IMPELLER_PRINTF_FORMAT(format_number, args_number) +#endif // IMPELLER_COMPILER_CLANG diff --git a/impeller/base/strings.cc b/impeller/base/strings.cc new file mode 100644 index 0000000000000..1b0762f451870 --- /dev/null +++ b/impeller/base/strings.cc @@ -0,0 +1,21 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "impeller/base/strings.h" + +#include + +namespace impeller { + +IMPELLER_PRINTF_FORMAT(1, 2) +std::string SPrintF(const char* format, ...) { + va_list list; + va_start(list, format); + char buffer[64] = {0}; + ::vsnprintf(buffer, sizeof(buffer), format, list); + va_end(list); + return buffer; +} + +} // namespace impeller diff --git a/impeller/base/strings.h b/impeller/base/strings.h new file mode 100644 index 0000000000000..179ae3f6da748 --- /dev/null +++ b/impeller/base/strings.h @@ -0,0 +1,16 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#pragma once + +#include + +#include "impeller/base/config.h" + +namespace impeller { + +IMPELLER_PRINTF_FORMAT(1, 2) +std::string SPrintF(const char* format, ...); + +} // namespace impeller diff --git a/impeller/compositor/BUILD.gn b/impeller/compositor/BUILD.gn index ea5e89187c36f..5fa6a67aa164d 100644 --- a/impeller/compositor/BUILD.gn +++ b/impeller/compositor/BUILD.gn @@ -59,6 +59,7 @@ impeller_component("compositor") { ] public_deps = [ + "../base", "../geometry", "../shader_glue", ] diff --git a/impeller/compositor/render_pass.mm b/impeller/compositor/render_pass.mm index 0d7f586d2e086..ff1a19ea7c7ab 100644 --- a/impeller/compositor/render_pass.mm +++ b/impeller/compositor/render_pass.mm @@ -6,6 +6,7 @@ #include "flutter/fml/closure.h" #include "flutter/fml/logging.h" +#include "impeller/base/base.h" #include "impeller/compositor/device_buffer.h" #include "impeller/compositor/formats_metal.h" #include "impeller/shader_glue/shader_types.h" @@ -139,6 +140,7 @@ static bool ConfigureStencilAttachment( void RenderPass::SetLabel(std::string label) { label_ = std::move(label); + transients_buffer_->SetLabel(SPrintF("%s Transients", label_.c_str())); } bool RenderPass::FinishEncoding(Allocator& transients_allocator) const { diff --git a/impeller/entity/entity_renderer.mm b/impeller/entity/entity_renderer.mm index cec785103ee16..808886c88a0c8 100644 --- a/impeller/entity/entity_renderer.mm +++ b/impeller/entity/entity_renderer.mm @@ -37,7 +37,7 @@ } bool EntityRenderer::OnRender(RenderPass& pass) { - pass.SetLabel("EntityRenderer"); + pass.SetLabel("EntityRenderer Render Pass"); shader::BoxVertexInfo::UniformBuffer uniforms; uniforms.mvp = Matrix::MakeOrthographic({800, 600}); diff --git a/impeller/primitives/box_primitive.mm b/impeller/primitives/box_primitive.mm index e967440222c72..f80019680d032 100644 --- a/impeller/primitives/box_primitive.mm +++ b/impeller/primitives/box_primitive.mm @@ -7,6 +7,7 @@ #include #include "flutter/fml/logging.h" +#include "impeller/base/base.h" #include "impeller/compositor/pipeline_descriptor.h" #include "impeller/compositor/shader_library.h" #include "impeller/compositor/vertex_descriptor.h" @@ -16,7 +17,7 @@ BoxPrimitive::BoxPrimitive(std::shared_ptr context) : Primitive(context) { PipelineDescriptor desc; - desc.SetLabel(shader::BoxVertexInfo::kLabel); + desc.SetLabel(SPrintF("%s Pipeline", shader::BoxVertexInfo::kLabel.data())); { auto fragment_function = context->GetShaderLibrary()->GetFunction( From 813bbbbcb91cae40ef5c467c5bf1de625284e429 Mon Sep 17 00:00:00 2001 From: Chinmay Garde Date: Tue, 22 Jun 2021 14:00:01 -0700 Subject: [PATCH 096/433] Add default values for blending. --- impeller/compositor/formats.h | 15 ++++++++------- impeller/primitives/box_primitive.mm | 1 + 2 files changed, 9 insertions(+), 7 deletions(-) diff --git a/impeller/compositor/formats.h b/impeller/compositor/formats.h index 986274185fbd5..2d0ff5ae1311a 100644 --- a/impeller/compositor/formats.h +++ b/impeller/compositor/formats.h @@ -95,15 +95,16 @@ struct ColorAttachmentDescriptor { /// final_color = final_color & write_mask; /// ``` - BlendFactor src_color_blend_factor; - BlendOperation color_blend_op; - BlendFactor dst_color_blend_factor; + BlendFactor src_color_blend_factor = BlendFactor::kOne; + BlendOperation color_blend_op = BlendOperation::kAdd; + BlendFactor dst_color_blend_factor = BlendFactor::kZero; - BlendFactor src_alpha_blend_factor; - BlendOperation alpha_blend_op; - BlendFactor dst_alpha_blend_factor; + BlendFactor src_alpha_blend_factor = BlendFactor::kOne; + BlendOperation alpha_blend_op = BlendOperation::kAdd; + BlendFactor dst_alpha_blend_factor = BlendFactor::kZero; - std::underlying_type_t write_mask; + std::underlying_type_t write_mask = + static_cast(ColorWriteMask::kAll); constexpr bool operator==(const ColorAttachmentDescriptor& o) const { return format == o.format && // diff --git a/impeller/primitives/box_primitive.mm b/impeller/primitives/box_primitive.mm index f80019680d032..74f7cc82a1c8a 100644 --- a/impeller/primitives/box_primitive.mm +++ b/impeller/primitives/box_primitive.mm @@ -43,6 +43,7 @@ // Configure the sole color attachments pixel format. ColorAttachmentDescriptor color0; color0.format = PixelFormat::kPixelFormat_B8G8R8A8_UNormInt_SRGB; + desc.SetColorAttachmentDescriptor(0u, std::move(color0)); } From 6a7f81d4f944c0e3131f1d69169a77e02cda404f Mon Sep 17 00:00:00 2001 From: Chinmay Garde Date: Tue, 22 Jun 2021 14:16:30 -0700 Subject: [PATCH 097/433] Set stencil and depth defaults. --- impeller/compositor/formats.h | 36 +++++++++++++++++++++-------- impeller/compositor/formats_metal.h | 2 +- 2 files changed, 27 insertions(+), 11 deletions(-) diff --git a/impeller/compositor/formats.h b/impeller/compositor/formats.h index 2d0ff5ae1311a..033d34d55c4b8 100644 --- a/impeller/compositor/formats.h +++ b/impeller/compositor/formats.h @@ -127,24 +127,40 @@ struct ColorAttachmentDescriptor { }; enum class CompareFunction { + /// Comparison test never passes. kNever, + /// Comparison test passes always passes. + kAlways, + /// Comparison test passes if new_value < current_value. kLess, + /// Comparison test passes if new_value == current_value. kEqual, + /// Comparison test passes if new_value <= current_value. kLessEqual, + /// Comparison test passes if new_value > current_value. kGreater, + /// Comparison test passes if new_value != current_value. kNotEqual, + /// Comparison test passes if new_value >= current_value. kGreaterEqual, - kAlways, }; enum class StencilOperation { + /// Don't modify the current stencil value. kKeep, + /// Reset the stencil value to zero. kZero, - kReplace, + /// Reset the stencil value to the reference value. + kSetToReferneceValue, + /// Increment the current stencil value by 1. Clamp it to the maximum. kIncrementClamp, + /// Decrement the current stencil value by 1. Clamp it to zero. kDecrementClamp, + /// Perform a logical bitwise invert on the current stencil value. kInvert, + /// Increment the current stencil value by 1. If at maxium, set to zero. kIncrementWrap, + /// Decrement the current stencil value by 1. If at zero, set to maximum. kDecrementWrap, }; @@ -152,11 +168,11 @@ struct DepthAttachmentDescriptor { //---------------------------------------------------------------------------- /// Indicates how to compare the value with that in the depth buffer. /// - CompareFunction depth_compare; + CompareFunction depth_compare = CompareFunction::kAlways; //---------------------------------------------------------------------------- /// Indicates when writes must be performed to the depth buffer. /// - bool depth_write_enabled; + bool depth_write_enabled = false; constexpr bool operator==(const DepthAttachmentDescriptor& o) const { return depth_compare == o.depth_compare && @@ -174,30 +190,30 @@ struct StencilAttachmentDescriptor { /// value in the stencil buffer. Both values have the read_mask applied to /// them before performing this operation. /// - CompareFunction stencil_compare; + CompareFunction stencil_compare = CompareFunction::kAlways; //---------------------------------------------------------------------------- /// Indicates what to do when the stencil test has failed. /// - StencilOperation stencil_failure; + StencilOperation stencil_failure = StencilOperation::kKeep; //---------------------------------------------------------------------------- /// Indicates what to do when the stencil test passes but the depth test /// fails. /// - StencilOperation depth_failure; + StencilOperation depth_failure = StencilOperation::kKeep; //---------------------------------------------------------------------------- /// Indicates what to do when both the stencil and depth tests pass. /// - StencilOperation depth_stencil_pass; + StencilOperation depth_stencil_pass = StencilOperation::kKeep; //---------------------------------------------------------------------------- /// The mask applied to the reference and stencil buffer values before /// performing the stencil_compare operation. /// - uint32_t read_mask; + uint32_t read_mask = ~0; //---------------------------------------------------------------------------- /// The mask applied to the new stencil value before it is written into the /// stencil buffer. /// - uint32_t write_mask; + uint32_t write_mask = ~0; constexpr bool operator==(const StencilAttachmentDescriptor& o) const { return stencil_compare == o.stencil_compare && diff --git a/impeller/compositor/formats_metal.h b/impeller/compositor/formats_metal.h index d68934f43ab7a..a1d63def21472 100644 --- a/impeller/compositor/formats_metal.h +++ b/impeller/compositor/formats_metal.h @@ -149,7 +149,7 @@ constexpr MTLStencilOperation ToMTLStencilOperation(StencilOperation op) { return MTLStencilOperationKeep; case StencilOperation::kZero: return MTLStencilOperationZero; - case StencilOperation::kReplace: + case StencilOperation::kSetToReferneceValue: return MTLStencilOperationReplace; case StencilOperation::kIncrementClamp: return MTLStencilOperationIncrementClamp; From 752d4650a2ae7f0ebb813a7e02a8946798c85220 Mon Sep 17 00:00:00 2001 From: Chinmay Garde Date: Tue, 22 Jun 2021 16:45:11 -0700 Subject: [PATCH 098/433] Reserve a slot for vertex buffer bindings. --- impeller/compiler/code_gen_template.h | 2 +- impeller/compiler/reflector.cc | 48 ++++++++++++++++++++++++ impeller/compositor/render_pass.mm | 1 - impeller/compositor/vertex_descriptor.h | 2 + impeller/compositor/vertex_descriptor.mm | 22 ++++++++--- impeller/entity/entity_renderer.mm | 6 ++- impeller/primitives/box.frag | 6 ++- impeller/primitives/box.vert | 8 +++- impeller/primitives/box_primitive.h | 3 ++ impeller/primitives/box_primitive.mm | 5 +++ 10 files changed, 90 insertions(+), 13 deletions(-) diff --git a/impeller/compiler/code_gen_template.h b/impeller/compiler/code_gen_template.h index bd03dacfd8bf9..a5c31a77b1d1b 100644 --- a/impeller/compiler/code_gen_template.h +++ b/impeller/compiler/code_gen_template.h @@ -32,7 +32,7 @@ struct {{camel_case(shader_name)}}{{camel_case(shader_stage)}}Info { // =========================================================================== {% for def in struct_definitions %} - struct alignas(16) {{def.name}} { + struct {{def.name}} { {% for member in def.members %} {{member.type}} {{member.name}}; {% endfor %} diff --git a/impeller/compiler/reflector.cc b/impeller/compiler/reflector.cc index 8f46111d922fa..b04f94d8f5fa4 100644 --- a/impeller/compiler/reflector.cc +++ b/impeller/compiler/reflector.cc @@ -351,6 +351,54 @@ std::vector Reflector::ReadStructMembers( continue; } + // Tightly packed Point + if (member.basetype == SPIRType::BaseType::Float && // + member.width == sizeof(float) * 8 && // + member.columns == 1 && // + member.vecsize == 2 // + ) { + result.emplace_back(StructMember{ + .type = "Point", + .name = GetMemberNameAtIndex(struct_type, i), + .offset = total_byte_length, + .byte_length = sizeof(Point), + }); + total_byte_length += sizeof(Point); + continue; + } + + // Tightly packed Vector3 + if (member.basetype == SPIRType::BaseType::Float && // + member.width == sizeof(float) * 8 && // + member.columns == 1 && // + member.vecsize == 3 // + ) { + result.emplace_back(StructMember{ + .type = "Vector3", + .name = GetMemberNameAtIndex(struct_type, i), + .offset = total_byte_length, + .byte_length = sizeof(Vector3), + }); + total_byte_length += sizeof(Vector3); + continue; + } + + // Tightly packed Vector4 + if (member.basetype == SPIRType::BaseType::Float && // + member.width == sizeof(float) * 8 && // + member.columns == 1 && // + member.vecsize == 4 // + ) { + result.emplace_back(StructMember{ + .type = "Vector4", + .name = GetMemberNameAtIndex(struct_type, i), + .offset = total_byte_length, + .byte_length = sizeof(Vector4), + }); + total_byte_length += sizeof(Vector4); + continue; + } + // Other single isolated scalars. { auto maybe_known_type = ReadKnownScalarType(member.basetype); diff --git a/impeller/compositor/render_pass.mm b/impeller/compositor/render_pass.mm index ff1a19ea7c7ab..b9ef2678f9bd3 100644 --- a/impeller/compositor/render_pass.mm +++ b/impeller/compositor/render_pass.mm @@ -236,7 +236,6 @@ static bool Bind(Allocator& allocator, } else { auto_pop_debug_marker.Release(); } - [pass setRenderPipelineState:command.pipeline->GetMTLRenderPipelineState()]; [pass setDepthStencilState:command.pipeline->GetMTLDepthStencilState()]; [pass setFrontFacingWinding:MTLWindingClockwise]; diff --git a/impeller/compositor/vertex_descriptor.h b/impeller/compositor/vertex_descriptor.h index ca96a21e52f27..f05885d403ff4 100644 --- a/impeller/compositor/vertex_descriptor.h +++ b/impeller/compositor/vertex_descriptor.h @@ -31,6 +31,8 @@ class VertexDescriptor final : public Comparable { MTLVertexDescriptor* GetMTLVertexDescriptor() const; + size_t GetVertexBufferIndex() const; + //| Comparable| std::size_t GetHash() const override; diff --git a/impeller/compositor/vertex_descriptor.mm b/impeller/compositor/vertex_descriptor.mm index 6c68fc544da2b..3ab58eab7d01f 100644 --- a/impeller/compositor/vertex_descriptor.mm +++ b/impeller/compositor/vertex_descriptor.mm @@ -188,23 +188,33 @@ static MTLVertexFormat ReadStageInputFormat(const ShaderStageIOSlot& input) { return true; } +size_t VertexDescriptor::GetVertexBufferIndex() const { + static constexpr size_t kReservedVertexBufferIndex = + 30u; // The final slot available. + return kReservedVertexBufferIndex; +} + MTLVertexDescriptor* VertexDescriptor::GetMTLVertexDescriptor() const { auto descriptor = [MTLVertexDescriptor vertexDescriptor]; + const size_t vertex_buffer_index = GetVertexBufferIndex(); + size_t stride = 0u; for (const auto& input : stage_inputs_) { auto attrib = descriptor.attributes[input.location]; attrib.format = input.format; attrib.offset = stride; - // All vertex inputs are interleaved and tightly packed in one buffer at - // zero index. - attrib.bufferIndex = 0u; + // All vertex inputs are interleaved and tightly packed in one buffer at a + // reserved index. + attrib.bufferIndex = vertex_buffer_index; stride += input.length; } - descriptor.layouts[0].stride = stride; - descriptor.layouts[0].stepRate = 1u; - descriptor.layouts[0].stepFunction = MTLVertexStepFunctionPerVertex; + // Since it's all in one buffer, indicate its layout. + auto vertex_layout = descriptor.layouts[vertex_buffer_index]; + vertex_layout.stride = stride; + vertex_layout.stepRate = 1u; + vertex_layout.stepFunction = MTLVertexStepFunctionPerVertex; return descriptor; } diff --git a/impeller/entity/entity_renderer.mm b/impeller/entity/entity_renderer.mm index 808886c88a0c8..67cbf4abb1e34 100644 --- a/impeller/entity/entity_renderer.mm +++ b/impeller/entity/entity_renderer.mm @@ -54,9 +54,11 @@ Command cmd; cmd.label = "Box"; cmd.pipeline = box_primitive_->GetPipeline(); - cmd.vertex_bindings.buffers[0u] = + // Vertex buffer. + cmd.vertex_bindings.buffers[box_primitive_->GetVertexBufferIndex()] = vertex_builder.CreateVertexBuffer(pass.GetTransientsBuffer()); - cmd.vertex_bindings.buffers[1u] = + // Uniform buffer. + cmd.vertex_bindings.buffers[0u] = pass.GetTransientsBuffer().EmplaceUniform(uniforms); cmd.index_buffer = vertex_builder.CreateIndexBuffer(pass.GetTransientsBuffer()); diff --git a/impeller/primitives/box.frag b/impeller/primitives/box.frag index a5560b6a4f5b2..a6823bf6d14d6 100644 --- a/impeller/primitives/box.frag +++ b/impeller/primitives/box.frag @@ -2,8 +2,10 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -layout(location = 0) out vec4 fragColor; +in vec3 color; + +out vec4 fragColor; void main() { - fragColor = vec4(1.0, 1.0, 1.0, 1.0); + fragColor = vec4(0.0, 1.0, 0.0, 1.0); } diff --git a/impeller/primitives/box.vert b/impeller/primitives/box.vert index 1b1fa9bf79abe..1af7f41b3a087 100644 --- a/impeller/primitives/box.vert +++ b/impeller/primitives/box.vert @@ -6,8 +6,14 @@ uniform UniformBuffer { mat4 mvp; } uniforms; +uniform UB2 { + vec3 color; +} ub2; + in vec3 vertexPosition; +out vec3 color; void main() { - gl_Position = vec4(vertexPosition, 1.0); + gl_Position = uniforms.mvp * vec4(vertexPosition, 1.0); + color = ub2.color; } diff --git a/impeller/primitives/box_primitive.h b/impeller/primitives/box_primitive.h index 83b8dbfc78141..fcb19f7994867 100644 --- a/impeller/primitives/box_primitive.h +++ b/impeller/primitives/box_primitive.h @@ -27,8 +27,11 @@ class BoxPrimitive final : public Primitive { // |Primitive| virtual bool Encode(RenderPass& pass) const override; + size_t GetVertexBufferIndex() const; + private: std::shared_ptr pipeline_ = 0; + size_t vertex_buffer_index_ = 0; bool is_valid_ = false; FML_DISALLOW_COPY_AND_ASSIGN(BoxPrimitive); diff --git a/impeller/primitives/box_primitive.mm b/impeller/primitives/box_primitive.mm index 74f7cc82a1c8a..485179770ea5d 100644 --- a/impeller/primitives/box_primitive.mm +++ b/impeller/primitives/box_primitive.mm @@ -31,6 +31,7 @@ { auto vertex_descriptor = std::make_shared(); + vertex_buffer_index_ = vertex_descriptor->GetVertexBufferIndex(); if (!vertex_descriptor->SetStageInputs( shader::BoxVertexInfo::kAllShaderStageInputs)) { FML_LOG(ERROR) << "Could not configure vertex descriptor."; @@ -67,6 +68,10 @@ is_valid_ = true; } +size_t BoxPrimitive::GetVertexBufferIndex() const { + return vertex_buffer_index_; +} + BoxPrimitive::~BoxPrimitive() = default; std::shared_ptr BoxPrimitive::GetPipeline() const { From fe95510a7f8b25289942c4147f4c59a515566cea Mon Sep 17 00:00:00 2001 From: Chinmay Garde Date: Tue, 22 Jun 2021 17:10:12 -0700 Subject: [PATCH 099/433] Shader uniform slots get their own glue type. --- impeller/compiler/code_gen_template.h | 10 +++------- impeller/compositor/formats.h | 2 ++ impeller/entity/entity_renderer.mm | 6 +++--- impeller/geometry/vector.h | 6 ++++++ impeller/primitives/box.frag | 2 +- impeller/primitives/box.vert | 9 +++------ impeller/shader_glue/shader_types.h | 7 +++++++ 7 files changed, 25 insertions(+), 17 deletions(-) diff --git a/impeller/compiler/code_gen_template.h b/impeller/compiler/code_gen_template.h index a5c31a77b1d1b..3236e510717ab 100644 --- a/impeller/compiler/code_gen_template.h +++ b/impeller/compiler/code_gen_template.h @@ -44,13 +44,9 @@ struct {{camel_case(shader_name)}}{{camel_case(shader_stage)}}Info { // =========================================================================== {% for uniform in uniform_buffers %} - static constexpr auto kUniform{{camel_case(uniform.name)}} = ShaderStageIOSlot { // {{uniform.name}} - "{{uniform.name}}", // name - {{uniform.location}}u, // attribute location - {{uniform.type.type_name}}, // type - {{uniform.type.bit_width}}u, // bit width of type - {{uniform.type.vec_size}}u, // vec size - {{uniform.type.columns}}u // number of columns + static constexpr auto kUniform{{camel_case(uniform.name)}} = ShaderUniformSlot<{{uniform.name}}> { // {{uniform.name}} + "{{uniform.name}}", // name + {{uniform.binding}}u, // binding }; {% endfor %} diff --git a/impeller/compositor/formats.h b/impeller/compositor/formats.h index 033d34d55c4b8..1dda35737a42f 100644 --- a/impeller/compositor/formats.h +++ b/impeller/compositor/formats.h @@ -92,6 +92,8 @@ struct ColorAttachmentDescriptor { /// } else { /// final_color = new_color; /// } + /// // IMPORTANT: The write mask is applied irrespective of whether + /// // blending_enabled is set. /// final_color = final_color & write_mask; /// ``` diff --git a/impeller/entity/entity_renderer.mm b/impeller/entity/entity_renderer.mm index 67cbf4abb1e34..aa4c60bbe1bf2 100644 --- a/impeller/entity/entity_renderer.mm +++ b/impeller/entity/entity_renderer.mm @@ -40,6 +40,7 @@ pass.SetLabel("EntityRenderer Render Pass"); shader::BoxVertexInfo::UniformBuffer uniforms; + uniforms.color = Color::Blue(); uniforms.mvp = Matrix::MakeOrthographic({800, 600}); VertexBufferBuilder vertex_builder; vertex_builder.AddVertices({ @@ -54,11 +55,10 @@ Command cmd; cmd.label = "Box"; cmd.pipeline = box_primitive_->GetPipeline(); - // Vertex buffer. cmd.vertex_bindings.buffers[box_primitive_->GetVertexBufferIndex()] = vertex_builder.CreateVertexBuffer(pass.GetTransientsBuffer()); - // Uniform buffer. - cmd.vertex_bindings.buffers[0u] = + cmd.vertex_bindings + .buffers[shader::BoxVertexInfo::kUniformUniformBuffer.binding] = pass.GetTransientsBuffer().EmplaceUniform(uniforms); cmd.index_buffer = vertex_builder.CreateIndexBuffer(pass.GetTransientsBuffer()); diff --git a/impeller/geometry/vector.h b/impeller/geometry/vector.h index b7625ffd96a67..6c5071bfc15d9 100644 --- a/impeller/geometry/vector.h +++ b/impeller/geometry/vector.h @@ -7,6 +7,7 @@ #include #include +#include "impeller/geometry/color.h" #include "impeller/geometry/point.h" #include "impeller/geometry/scalar.h" #include "impeller/geometry/size.h" @@ -25,6 +26,8 @@ struct Vector3 { constexpr Vector3(){}; + constexpr Vector3(const Color& c) : x(c.red), y(c.green), z(c.blue) {} + constexpr Vector3(const Point& p) : x(p.x), y(p.y) {} constexpr Vector3(const Size& s) : x(s.width), y(s.height) {} @@ -112,6 +115,9 @@ struct Vector4 { constexpr Vector4() {} + constexpr Vector4(const Color& c) + : x(c.red), y(c.green), z(c.blue), w(c.alpha) {} + constexpr Vector4(Scalar x, Scalar y, Scalar z, Scalar w) : x(x), y(y), z(z), w(w) {} diff --git a/impeller/primitives/box.frag b/impeller/primitives/box.frag index a6823bf6d14d6..b00ed258eb805 100644 --- a/impeller/primitives/box.frag +++ b/impeller/primitives/box.frag @@ -7,5 +7,5 @@ in vec3 color; out vec4 fragColor; void main() { - fragColor = vec4(0.0, 1.0, 0.0, 1.0); + fragColor = vec4(color, 1.0); } diff --git a/impeller/primitives/box.vert b/impeller/primitives/box.vert index 1af7f41b3a087..fef762838f780 100644 --- a/impeller/primitives/box.vert +++ b/impeller/primitives/box.vert @@ -4,16 +4,13 @@ uniform UniformBuffer { mat4 mvp; -} uniforms; - -uniform UB2 { vec3 color; -} ub2; +} uniforms; in vec3 vertexPosition; out vec3 color; void main() { - gl_Position = uniforms.mvp * vec4(vertexPosition, 1.0); - color = ub2.color; + gl_Position = vec4(vertexPosition, 1.0); + color = uniforms.color; } diff --git a/impeller/shader_glue/shader_types.h b/impeller/shader_glue/shader_types.h index f96eefd4e0901..be9108aaa8bbd 100644 --- a/impeller/shader_glue/shader_types.h +++ b/impeller/shader_glue/shader_types.h @@ -40,6 +40,13 @@ enum class ShaderType { kSampler, }; +template +struct ShaderUniformSlot { + using Type = T; + const char* name; + size_t binding; +}; + struct ShaderStageIOSlot { const char* name; size_t location; From b0c9f3b29b899ca5f47e34f64a777e046a7e4ac3 Mon Sep 17 00:00:00 2001 From: Chinmay Garde Date: Wed, 23 Jun 2021 15:01:38 -0700 Subject: [PATCH 100/433] Work on matrix utils. --- impeller/entity/entity_renderer.mm | 12 +++++------- impeller/geometry/matrix.cc | 22 ++++++---------------- impeller/geometry/matrix.h | 21 ++++++++++++++++++++- impeller/primitives/box.vert | 2 +- 4 files changed, 32 insertions(+), 25 deletions(-) diff --git a/impeller/entity/entity_renderer.mm b/impeller/entity/entity_renderer.mm index aa4c60bbe1bf2..bd23b9fa3df56 100644 --- a/impeller/entity/entity_renderer.mm +++ b/impeller/entity/entity_renderer.mm @@ -41,15 +41,13 @@ shader::BoxVertexInfo::UniformBuffer uniforms; uniforms.color = Color::Blue(); - uniforms.mvp = Matrix::MakeOrthographic({800, 600}); + uniforms.mvp = + Matrix::MakeOrthographic({800, 600}).Scale({200.0, 200.0, 1.0}); VertexBufferBuilder vertex_builder; vertex_builder.AddVertices({ - {-0.5, 0.5, 1.0}, // - {0.5, 0.5, 1.0}, // - {0.5, -0.5, 1.0}, // - {0.5, -0.5, 1.0}, // - {-0.5, -0.5, 1.0}, // - {-0.5, 0.5, 1.0}, // + {0, 0.5, 0.0}, // + {0.5, -0.5, 0.0}, // + {-0.5, -0.5, 0.0}, // }); Command cmd; diff --git a/impeller/geometry/matrix.cc b/impeller/geometry/matrix.cc index b783df7955c90..fd24147d849b6 100644 --- a/impeller/geometry/matrix.cc +++ b/impeller/geometry/matrix.cc @@ -95,20 +95,19 @@ Matrix Matrix::MakeOrthographic(Scalar left, Scalar rsl = right - left; Scalar tab = top + bottom; Scalar tsb = top - bottom; - Scalar fan = farZ + nearZ; + // Scalar fan = farZ + nearZ; Scalar fsn = farZ - nearZ; // clang-format off - return Matrix(2.0 / rsl, 0.0, 0.0, 0.0, - 0.0, 2.0 / tsb, 0.0, 0.0, - 0.0, 0.0, -2.0 / fsn, 0.0, - -ral / rsl, -tab / tsb, -fan / fsn, 1.0); + return Matrix(2.0 / rsl, 0.0, 0.0, 0.0, + 0.0, 2.0 / tsb, 0.0, 0.0, + 0.0, 0.0, -1.0 / fsn, 0.0, + -ral / rsl, -tab / tsb, -nearZ / fsn, 1.0); // clang-format on } Matrix Matrix::MakeOrthographic(const Size& size) { - return Matrix::MakeOrthographic(0, size.width, size.height, 0, -INT_MAX, - INT_MAX); + return Matrix::MakeOrthographic(0, size.width, size.height, 0, 0.0, 1.0); } Matrix Matrix::MakePerspective(Scalar fov, @@ -210,15 +209,6 @@ Matrix Matrix::Invert() const { tmp.m[12] * det, tmp.m[13] * det, tmp.m[14] * det, tmp.m[15] * det}; } -Matrix Matrix::Transpose() const { - return { - m[0], m[4], m[8], m[12], // - m[1], m[5], m[9], m[13], // - m[2], m[6], m[10], m[14], // - m[3], m[7], m[11], m[15], // - }; -} - Scalar Matrix::GetDeterminant() const { auto a00 = e[0][0]; auto a01 = e[0][1]; diff --git a/impeller/geometry/matrix.h b/impeller/geometry/matrix.h index 9156b211a6aff..62dadf76b9038 100644 --- a/impeller/geometry/matrix.h +++ b/impeller/geometry/matrix.h @@ -16,6 +16,18 @@ namespace impeller { +//------------------------------------------------------------------------------ +/// @brief A 4x4 matrix using column-major storage. +/// +/// Utility methods that need to make assumptions about normalized +/// device coordinates use the following convention: +/// * Left-handed coordinate system. Positive rotation is +/// clockwise about axis of rotation. +/// * Lower left corner is -1.0, -1.0. +/// * Upper left corner is 1.0, 1.0. +/// * Visible z-space is from 0.0 to 1.0. +/// * NOTE: This is not the same as OpenGL conventions! +/// * Center is at (0.0, 0.0, 0.5). struct Matrix { union { Scalar m[16]; @@ -226,7 +238,14 @@ struct Matrix { // clang-format on } - Matrix Transpose() const; + constexpr Matrix Transpose() const { + return { + m[0], m[4], m[8], m[12], // + m[1], m[5], m[9], m[13], // + m[2], m[6], m[10], m[14], // + m[3], m[7], m[11], m[15], // + }; + } Matrix Invert() const; diff --git a/impeller/primitives/box.vert b/impeller/primitives/box.vert index fef762838f780..c5bd284b52602 100644 --- a/impeller/primitives/box.vert +++ b/impeller/primitives/box.vert @@ -11,6 +11,6 @@ in vec3 vertexPosition; out vec3 color; void main() { - gl_Position = vec4(vertexPosition, 1.0); + gl_Position = uniforms.mvp * vec4(vertexPosition, 1.0); color = uniforms.color; } From c63c1e63b5749162b27d1409d597317dae6bd518 Mon Sep 17 00:00:00 2001 From: Chinmay Garde Date: Wed, 23 Jun 2021 16:21:43 -0700 Subject: [PATCH 101/433] Reason about conventions when dealing with NDC. --- impeller/compositor/render_pass.h | 3 ++ impeller/compositor/render_pass.mm | 11 +++++++ impeller/compositor/renderer.h | 2 +- impeller/compositor/renderer.mm | 2 +- impeller/compositor/surface.h | 3 ++ impeller/compositor/surface.mm | 9 +++++- impeller/entity/entity_renderer.h | 2 +- impeller/entity/entity_renderer.mm | 16 ++++++---- impeller/geometry/matrix.cc | 50 ------------------------------ impeller/geometry/matrix.h | 41 ++++++------------------ 10 files changed, 48 insertions(+), 91 deletions(-) diff --git a/impeller/compositor/render_pass.h b/impeller/compositor/render_pass.h index 877746110e991..ef31b70865a35 100644 --- a/impeller/compositor/render_pass.h +++ b/impeller/compositor/render_pass.h @@ -15,6 +15,7 @@ #include "impeller/compositor/host_buffer.h" #include "impeller/compositor/texture.h" #include "impeller/geometry/color.h" +#include "impeller/geometry/size.h" namespace impeller { @@ -48,6 +49,8 @@ class RenderPassDescriptor { bool HasColorAttachment(size_t index) const; + std::optional GetColorAttachmentSize(size_t index) const; + RenderPassDescriptor& SetColorAttachment(ColorRenderPassAttachment attachment, size_t index); diff --git a/impeller/compositor/render_pass.mm b/impeller/compositor/render_pass.mm index b9ef2678f9bd3..7257863d47bf2 100644 --- a/impeller/compositor/render_pass.mm +++ b/impeller/compositor/render_pass.mm @@ -24,6 +24,17 @@ return false; } +std::optional RenderPassDescriptor::GetColorAttachmentSize( + size_t index) const { + auto found = colors_.find(index); + + if (found == colors_.end()) { + return std::nullopt; + } + + return found->second.texture->GetSize(); +} + RenderPassDescriptor& RenderPassDescriptor::SetColorAttachment( ColorRenderPassAttachment attachment, size_t index) { diff --git a/impeller/compositor/renderer.h b/impeller/compositor/renderer.h index 8a03bdc1a9ff7..85b1f9e8130de 100644 --- a/impeller/compositor/renderer.h +++ b/impeller/compositor/renderer.h @@ -32,7 +32,7 @@ class Renderer { virtual bool OnIsValid() const = 0; - virtual bool OnRender(RenderPass& pass) = 0; + virtual bool OnRender(const Surface& surface, RenderPass& pass) = 0; private: dispatch_semaphore_t frames_in_flight_sema_ = nullptr; diff --git a/impeller/compositor/renderer.mm b/impeller/compositor/renderer.mm index 20c0c4b42b0e8..b5dfcf8290199 100644 --- a/impeller/compositor/renderer.mm +++ b/impeller/compositor/renderer.mm @@ -49,7 +49,7 @@ return false; } - if (!OnRender(*render_pass)) { + if (!OnRender(surface, *render_pass)) { return false; } diff --git a/impeller/compositor/surface.h b/impeller/compositor/surface.h index 0d91604be14db..7a92188e80181 100644 --- a/impeller/compositor/surface.h +++ b/impeller/compositor/surface.h @@ -22,6 +22,8 @@ class Surface { ~Surface(); + const Size& GetSize() const; + bool Present() const; bool IsValid() const; @@ -31,6 +33,7 @@ class Surface { private: RenderPassDescriptor desc_; std::function present_callback_; + Size size_; bool is_valid_ = false; diff --git a/impeller/compositor/surface.mm b/impeller/compositor/surface.mm index dea5e3327a761..40f57d9e00cb2 100644 --- a/impeller/compositor/surface.mm +++ b/impeller/compositor/surface.mm @@ -11,14 +11,21 @@ Surface::Surface(RenderPassDescriptor desc, std::function present_callback) : desc_(std::move(desc)), present_callback_(present_callback) { - if (!desc_.HasColorAttachment(0)) { + if (auto size = desc_.GetColorAttachmentSize(0u); size.has_value()) { + size_ = size.value(); + } else { return; } + is_valid_ = true; } Surface::~Surface() = default; +const Size& Surface::GetSize() const { + return size_; +} + bool Surface::Present() const { auto callback = present_callback_; diff --git a/impeller/entity/entity_renderer.h b/impeller/entity/entity_renderer.h index ac1b7566dbced..0d00e6ffe918a 100644 --- a/impeller/entity/entity_renderer.h +++ b/impeller/entity/entity_renderer.h @@ -26,7 +26,7 @@ class EntityRenderer final : public Renderer { bool OnIsValid() const override; // |Renderer| - bool OnRender(RenderPass& pass) override; + bool OnRender(const Surface& surface, RenderPass& pass) override; FML_DISALLOW_COPY_AND_ASSIGN(EntityRenderer); }; diff --git a/impeller/entity/entity_renderer.mm b/impeller/entity/entity_renderer.mm index bd23b9fa3df56..663dd1670afb4 100644 --- a/impeller/entity/entity_renderer.mm +++ b/impeller/entity/entity_renderer.mm @@ -4,7 +4,9 @@ #include "impeller/entity/entity_renderer.h" +#include "flutter/fml/time/time_point.h" #include "impeller/compositor/command.h" +#include "impeller/compositor/surface.h" #include "impeller/compositor/vertex_buffer_builder.h" #include "impeller/primitives/box.frag.h" #include "impeller/primitives/box.vert.h" @@ -36,18 +38,20 @@ return is_valid_; } -bool EntityRenderer::OnRender(RenderPass& pass) { +bool EntityRenderer::OnRender(const Surface& surface, RenderPass& pass) { pass.SetLabel("EntityRenderer Render Pass"); shader::BoxVertexInfo::UniformBuffer uniforms; uniforms.color = Color::Blue(); - uniforms.mvp = - Matrix::MakeOrthographic({800, 600}).Scale({200.0, 200.0, 1.0}); + + uniforms.mvp = Matrix::MakeOrthographic(surface.GetSize()); + VertexBufferBuilder vertex_builder; + vertex_builder.AddVertices({ - {0, 0.5, 0.0}, // - {0.5, -0.5, 0.0}, // - {-0.5, -0.5, 0.0}, // + {0, 0, 0.0}, // + {800, 0.0, 0.0}, // + {0.0, 600, 0.0}, // }); Command cmd; diff --git a/impeller/geometry/matrix.cc b/impeller/geometry/matrix.cc index fd24147d849b6..662987053c735 100644 --- a/impeller/geometry/matrix.cc +++ b/impeller/geometry/matrix.cc @@ -85,56 +85,6 @@ Matrix::Matrix(const Decomposition& d) : Matrix() { } } -Matrix Matrix::MakeOrthographic(Scalar left, - Scalar right, - Scalar bottom, - Scalar top, - Scalar nearZ, - Scalar farZ) { - Scalar ral = right + left; - Scalar rsl = right - left; - Scalar tab = top + bottom; - Scalar tsb = top - bottom; - // Scalar fan = farZ + nearZ; - Scalar fsn = farZ - nearZ; - - // clang-format off - return Matrix(2.0 / rsl, 0.0, 0.0, 0.0, - 0.0, 2.0 / tsb, 0.0, 0.0, - 0.0, 0.0, -1.0 / fsn, 0.0, - -ral / rsl, -tab / tsb, -nearZ / fsn, 1.0); - // clang-format on -} - -Matrix Matrix::MakeOrthographic(const Size& size) { - return Matrix::MakeOrthographic(0, size.width, size.height, 0, 0.0, 1.0); -} - -Matrix Matrix::MakePerspective(Scalar fov, - Scalar aspect, - Scalar nearZ, - Scalar farZ) { - Scalar cotan = 1.0 / tan(fov / 2.0); - - return Matrix(cotan / aspect, 0.0, 0.0, 0.0, // - 0.0, cotan, 0.0, 0.0, // - 0.0, 0.0, (farZ + nearZ) / (nearZ - farZ), -1.0, // - 0.0, 0.0, (2.0 * farZ * nearZ) / (nearZ - farZ), 0.0 // - ); -} - -Matrix Matrix::MakeLookAt(const Vector3& eye, - const Vector3& center, - const Vector3& up) { - auto n = (eye - center).Normalize(); - auto u = (up.Cross(n)).Normalize(); - auto v = n.Cross(u); - return {u.x, v.x, n.x, 0.0, // - u.y, v.y, n.y, 0.0, // - u.z, v.z, n.z, 0.0, // - (-u).Dot(eye), (-v).Dot(eye), (-n).Dot(eye), 1.0}; -} - Matrix Matrix::operator+(const Matrix& o) const { return Matrix( m[0] + o.m[0], m[1] + o.m[1], m[2] + o.m[2], m[3] + o.m[3], // diff --git a/impeller/geometry/matrix.h b/impeller/geometry/matrix.h index 62dadf76b9038..c2c5310acb6cd 100644 --- a/impeller/geometry/matrix.h +++ b/impeller/geometry/matrix.h @@ -20,14 +20,14 @@ namespace impeller { /// @brief A 4x4 matrix using column-major storage. /// /// Utility methods that need to make assumptions about normalized -/// device coordinates use the following convention: +/// device coordinates must use the following convention: /// * Left-handed coordinate system. Positive rotation is /// clockwise about axis of rotation. /// * Lower left corner is -1.0, -1.0. /// * Upper left corner is 1.0, 1.0. /// * Visible z-space is from 0.0 to 1.0. -/// * NOTE: This is not the same as OpenGL conventions! -/// * Center is at (0.0, 0.0, 0.5). +/// * This is NOT the same as OpenGL! Be careful. +/// * NDC origin is at (0.0, 0.0, 0.5). struct Matrix { union { Scalar m[16]; @@ -167,34 +167,6 @@ struct Matrix { // clang-format on } - static Matrix MakeOrthographic(Scalar left, - Scalar right, - Scalar bottom, - Scalar top, - Scalar nearZ, - Scalar farZ); - - static Matrix MakeOrthographic(const Size& size); - - /** - * Specify a viewing frustum in the worlds coordinate system. - * - * @param fov angle of the field of view (in radians). - * @param aspect aspect ratio. - * @param nearZ near clipping plane. - * @param farZ far clipping plane. - * - * @return the perspective projection matrix. - */ - static Matrix MakePerspective(Scalar fov, - Scalar aspect, - Scalar nearZ, - Scalar farZ); - - static Matrix MakeLookAt(const Vector3& eye, - const Vector3& center, - const Vector3& up); - constexpr Matrix Translate(const Vector3& t) const { // clang-format off return Matrix(m[0], m[1], m[2], m[3], @@ -296,6 +268,13 @@ struct Matrix { Matrix operator*(const Matrix& m) const { return Multiply(m); } Matrix operator+(const Matrix& m) const; + + static constexpr Matrix MakeOrthographic(const Size& size) { + // Per assumptions about NDC documented above. + const auto scale = MakeScale({1.0f / size.width, -1.0f / size.height, 1.0}); + const auto translate = MakeTranslation({-1.0, 1.0, 0.5}); + return translate * scale; + } }; static_assert(sizeof(struct Matrix) == sizeof(Scalar) * 16, From f29e505f3e7a9161cf78aef58454017a2bd58ef6 Mon Sep 17 00:00:00 2001 From: Chinmay Garde Date: Sun, 27 Jun 2021 15:17:36 -0700 Subject: [PATCH 102/433] Generate struct for per vertex data. --- impeller/compiler/reflector.cc | 178 +++++++++++++++++++++++------ impeller/compiler/reflector.h | 29 +++-- impeller/entity/entity_renderer.mm | 1 - impeller/primitives/box.frag | 4 +- impeller/primitives/box.vert | 7 +- 5 files changed, 171 insertions(+), 48 deletions(-) diff --git a/impeller/compiler/reflector.cc b/impeller/compiler/reflector.cc index b04f94d8f5fa4..7d88825a8340d 100644 --- a/impeller/compiler/reflector.cc +++ b/impeller/compiler/reflector.cc @@ -149,14 +149,14 @@ std::shared_ptr Reflector::GetReflectionCC() const { std::optional Reflector::GenerateTemplateArguments() const { nlohmann::json root; - { - const auto& entrypoints = compiler_->get_entry_points_and_stages(); - if (entrypoints.size() != 1) { - FML_LOG(ERROR) << "Incorrect number of entrypoints in the shader. Found " - << entrypoints.size() << " but expected 1."; - return std::nullopt; - } + const auto& entrypoints = compiler_->get_entry_points_and_stages(); + if (entrypoints.size() != 1) { + FML_LOG(ERROR) << "Incorrect number of entrypoints in the shader. Found " + << entrypoints.size() << " but expected 1."; + return std::nullopt; + } + { root["entrypoint"] = entrypoints.front().name; root["shader_name"] = options_.shader_name; root["shader_stage"] = @@ -187,20 +187,31 @@ std::optional Reflector::GenerateTemplateArguments() const { return std::nullopt; } - auto& struct_definitions = root["struct_definitions"] = - nlohmann::json::array_t{}; - std::set known_structs; - ir_->for_each_typed_id([&](uint32_t, const SPIRType& type) { - if (known_structs.find(type.self) != known_structs.end()) { - // Iterating over types this way leads to duplicates which may cause - // duplicate struct definitions. - return; - } - known_structs.insert(type.self); - if (auto struc = ReflectStructDefinition(type.self); struc.has_value()) { - struct_definitions.emplace_back(std::move(struc.value())); + { + auto& struct_definitions = root["struct_definitions"] = + nlohmann::json::array_t{}; + std::set known_structs; + ir_->for_each_typed_id([&](uint32_t, const SPIRType& type) { + if (known_structs.find(type.self) != known_structs.end()) { + // Iterating over types this way leads to duplicates which may cause + // duplicate struct definitions. + return; + } + known_structs.insert(type.self); + if (auto struc = ReflectStructDefinition(type.self); struc.has_value()) { + struct_definitions.emplace_back(EmitStructDefinition(struc.value())); + } + }); + + if (entrypoints.front().execution_model == + spv::ExecutionModel::ExecutionModelVertex) { + if (auto struc = + ReflectPerVertexStructDefinition(shader_resources.stage_inputs); + struc.has_value()) { + struct_definitions.emplace_back(EmitStructDefinition(struc.value())); + } } - }); + } return root; } @@ -448,7 +459,7 @@ std::vector Reflector::ReadStructMembers( return result; } -std::optional Reflector::ReflectStructDefinition( +std::optional Reflector::ReflectStructDefinition( const TypeID& type_id) const { const auto& type = compiler_->get_type(type_id); if (type.basetype != SPIRType::BaseType::Struct) { @@ -460,28 +471,125 @@ std::optional Reflector::ReflectStructDefinition( return std::nullopt; } - nlohmann::json::object_t struc; - struc["name"] = struct_name; - size_t total_size = 0u; for (const auto& member_type_id : type.member_types) { const auto& member_type = compiler_->get_type(member_type_id); total_size += (member_type.width * member_type.vecsize * member_type.columns) / 8u; } - struc["byte_length"] = total_size; - - size_t members_size = 0u; - struc["members"] = nlohmann::json::array_t{}; - const auto struct_members = ReadStructMembers(type_id); - for (const auto& struct_member : struct_members) { - auto& member = struc["members"].emplace_back(nlohmann::json::object_t{}); - member["name"] = struct_member.name; - member["type"] = struct_member.type; - member["offset"] = struct_member.offset; - member["byte_length"] = struct_member.byte_length; + + StructDefinition struc; + struc.name = struct_name; + struc.byte_length = total_size; + struc.members = ReadStructMembers(type_id); + return struc; +} + +nlohmann::json::object_t Reflector::EmitStructDefinition( + std::optional struc) const { + nlohmann::json::object_t result; + result["name"] = struc->name; + result["byte_length"] = struc->byte_length; + auto& members = result["members"] = nlohmann::json::array_t{}; + for (const auto& struc_member : struc->members) { + auto& member = members.emplace_back(nlohmann::json::object_t{}); + member["name"] = struc_member.name; + member["type"] = struc_member.type; + member["offset"] = struc_member.offset; + member["byte_length"] = struc_member.byte_length; + } + return result; +} + +struct VertexType { + std::string type_name; + std::string variable_name; + size_t byte_length = 0u; +}; + +static VertexType VertexTypeFromInputResource(const CompilerMSL& compiler, + const Resource* resource) { + VertexType result; + result.variable_name = resource->name; + auto type = compiler.get_type(resource->type_id); + const auto total_size = type.columns * type.vecsize * type.width / 8u; + result.byte_length = total_size; + + if (type.basetype == SPIRType::BaseType::Float && type.columns == 1u && + type.vecsize == 2u && type.width == sizeof(float) * 8u) { + result.type_name = "Point"; + } else if (type.basetype == SPIRType::BaseType::Float && type.columns == 1u && + type.vecsize == 4u && type.width == sizeof(float) * 8u) { + result.type_name = "Vector4"; + } else if (type.basetype == SPIRType::BaseType::Float && type.columns == 1u && + type.vecsize == 3u && type.width == sizeof(float) * 8u) { + result.type_name = "Vector3"; + } else { + // Catch all unknown padding. + result.type_name = TypeNameWithPaddingOfSize(total_size); + } + + return result; +} + +std::optional +Reflector::ReflectPerVertexStructDefinition( + const SmallVector& stage_inputs) const { + // Avoid emitting a zero sized structure. The code gen templates assume a + // non-zero size. + if (stage_inputs.empty()) { + return std::nullopt; } + // Validate locations are contiguous and there are no duplicates. + std::set locations; + for (const auto& input : stage_inputs) { + auto location = compiler_->get_decoration( + input.id, spv::Decoration::DecorationLocation); + if (locations.count(location) != 0) { + // Duplicate location. Bail. + return std::nullopt; + } + locations.insert(location); + } + + for (size_t i = 0; i < locations.size(); i++) { + if (locations.count(i) != 1) { + // Locations are not contiguous. Bail. + return std::nullopt; + } + } + + auto input_for_location = [&](uint32_t queried_location) -> const Resource* { + for (const auto& input : stage_inputs) { + auto location = compiler_->get_decoration( + input.id, spv::Decoration::DecorationLocation); + if (location == queried_location) { + return &input; + } + } + // This really cannot happen with all the validation above. + return nullptr; + }; + + StructDefinition struc; + struc.name = "PerVertexData"; + struc.byte_length = 0u; + for (size_t i = 0; i < locations.size(); i++) { + auto resource = input_for_location(i); + if (resource == nullptr) { + return std::nullopt; + } + const auto vertex_type = VertexTypeFromInputResource(*compiler_, resource); + + StructMember member; + member.name = vertex_type.variable_name; + member.type = vertex_type.type_name; + member.byte_length = vertex_type.byte_length; + member.offset = struc.byte_length; + struc.byte_length += vertex_type.byte_length; + struc.members.emplace_back(std::move(member)); + } return struc; } diff --git a/impeller/compiler/reflector.h b/impeller/compiler/reflector.h index 0b46b8528367e..615423675ee17 100644 --- a/impeller/compiler/reflector.h +++ b/impeller/compiler/reflector.h @@ -38,8 +38,22 @@ class Reflector { std::shared_ptr GetReflectionCC() const; private: + struct StructMember { + std::string type; + std::string name; + size_t offset = 0u; + size_t byte_length = 0u; + }; + + struct StructDefinition { + std::string name; + size_t byte_length = 0u; + std::vector members; + }; + const Options options_; const std::shared_ptr ir_; + // TODO: There is no reason this needs to the MSL subtype. const std::shared_ptr compiler_; std::unique_ptr template_arguments_; std::shared_ptr reflection_header_; @@ -64,9 +78,16 @@ class Reflector { std::optional ReflectType( const spirv_cross::TypeID& type_id) const; - std::optional ReflectStructDefinition( + nlohmann::json::object_t EmitStructDefinition( + std::optional struc) const; + + std::optional ReflectStructDefinition( const spirv_cross::TypeID& type_id) const; + std::optional ReflectPerVertexStructDefinition( + const spirv_cross::SmallVector& stage_inputs) + const; + std::optional GetMemberNameAtIndexIfExists( const spirv_cross::SPIRType& parent_type, size_t index) const; @@ -75,12 +96,6 @@ class Reflector { size_t index, std::string suffix = "") const; - struct StructMember { - std::string type; - std::string name; - size_t offset; - size_t byte_length; - }; std::vector ReadStructMembers( const spirv_cross::TypeID& type_id) const; diff --git a/impeller/entity/entity_renderer.mm b/impeller/entity/entity_renderer.mm index 663dd1670afb4..9595a0188da48 100644 --- a/impeller/entity/entity_renderer.mm +++ b/impeller/entity/entity_renderer.mm @@ -42,7 +42,6 @@ pass.SetLabel("EntityRenderer Render Pass"); shader::BoxVertexInfo::UniformBuffer uniforms; - uniforms.color = Color::Blue(); uniforms.mvp = Matrix::MakeOrthographic(surface.GetSize()); diff --git a/impeller/primitives/box.frag b/impeller/primitives/box.frag index b00ed258eb805..e3a23d1c266ff 100644 --- a/impeller/primitives/box.frag +++ b/impeller/primitives/box.frag @@ -2,10 +2,10 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -in vec3 color; +in vec4 color; out vec4 fragColor; void main() { - fragColor = vec4(color, 1.0); + fragColor = color; } diff --git a/impeller/primitives/box.vert b/impeller/primitives/box.vert index c5bd284b52602..8058ada175273 100644 --- a/impeller/primitives/box.vert +++ b/impeller/primitives/box.vert @@ -4,13 +4,14 @@ uniform UniformBuffer { mat4 mvp; - vec3 color; } uniforms; in vec3 vertexPosition; -out vec3 color; +in vec4 vertexColor; + +out vec4 color; void main() { gl_Position = uniforms.mvp * vec4(vertexPosition, 1.0); - color = uniforms.color; + color = vertexColor; } From f777caf3580e73ed74d1a5004fa7e288d1f874ce Mon Sep 17 00:00:00 2001 From: Chinmay Garde Date: Sun, 27 Jun 2021 15:45:25 -0700 Subject: [PATCH 103/433] Make vertex buffer builders templated so they can work with different per vertex types. --- impeller/compiler/reflector.cc | 18 +++---- impeller/compositor/render_pass.mm | 4 ++ impeller/compositor/vertex_buffer_builder.h | 50 ++++++++++++++------ impeller/compositor/vertex_buffer_builder.mm | 36 +------------- impeller/entity/entity_renderer.mm | 8 ++-- impeller/primitives/box.frag | 4 +- impeller/primitives/box.vert | 8 ++-- 7 files changed, 60 insertions(+), 68 deletions(-) diff --git a/impeller/compiler/reflector.cc b/impeller/compiler/reflector.cc index 7d88825a8340d..8e9768dd6a7ed 100644 --- a/impeller/compiler/reflector.cc +++ b/impeller/compiler/reflector.cc @@ -190,6 +190,15 @@ std::optional Reflector::GenerateTemplateArguments() const { { auto& struct_definitions = root["struct_definitions"] = nlohmann::json::array_t{}; + if (entrypoints.front().execution_model == + spv::ExecutionModel::ExecutionModelVertex) { + if (auto struc = + ReflectPerVertexStructDefinition(shader_resources.stage_inputs); + struc.has_value()) { + struct_definitions.emplace_back(EmitStructDefinition(struc.value())); + } + } + std::set known_structs; ir_->for_each_typed_id([&](uint32_t, const SPIRType& type) { if (known_structs.find(type.self) != known_structs.end()) { @@ -202,15 +211,6 @@ std::optional Reflector::GenerateTemplateArguments() const { struct_definitions.emplace_back(EmitStructDefinition(struc.value())); } }); - - if (entrypoints.front().execution_model == - spv::ExecutionModel::ExecutionModelVertex) { - if (auto struc = - ReflectPerVertexStructDefinition(shader_resources.stage_inputs); - struc.has_value()) { - struct_definitions.emplace_back(EmitStructDefinition(struc.value())); - } - } } return root; diff --git a/impeller/compositor/render_pass.mm b/impeller/compositor/render_pass.mm index 7257863d47bf2..8f770d0656170 100644 --- a/impeller/compositor/render_pass.mm +++ b/impeller/compositor/render_pass.mm @@ -241,6 +241,10 @@ static bool Bind(Allocator& allocator, }; fml::closure pop_debug_marker = [pass]() { [pass popDebugGroup]; }; for (const auto& command : commands_) { + if (command.index_count == 0u) { + continue; + } + fml::ScopedCleanupClosure auto_pop_debug_marker(pop_debug_marker); if (!command.label.empty()) { [pass pushDebugGroup:@(command.label.c_str())]; diff --git a/impeller/compositor/vertex_buffer_builder.h b/impeller/compositor/vertex_buffer_builder.h index 1d7891507c8fd..c809a7758c0b2 100644 --- a/impeller/compositor/vertex_buffer_builder.h +++ b/impeller/compositor/vertex_buffer_builder.h @@ -14,26 +14,48 @@ namespace impeller { +template class VertexBufferBuilder { public: - VertexBufferBuilder(); - - ~VertexBufferBuilder(); - - VertexBufferBuilder& AddVertices(std::initializer_list vertices); - - BufferView CreateVertexBuffer(HostBuffer& buffer) const; - - BufferView CreateIndexBuffer(HostBuffer& buffer) const; - - size_t GetIndexCount() const; + using VertexType = VertexType_; + using IndexType = IndexType_; + + VertexBufferBuilder() = default; + + ~VertexBufferBuilder() = default; + + VertexBufferBuilder& AddVertices( + std::initializer_list vertices) { + for (const auto& vertex : vertices) { + vertices_.push_back(vertex); + } + return *this; + } + + BufferView CreateVertexBuffer(HostBuffer& buffer) const { + return buffer.Emplace(vertices_.data(), + vertices_.size() * sizeof(VertexType), + alignof(VertexType)); + } + + BufferView CreateIndexBuffer(HostBuffer& buffer) const { + // So dumb! We don't actually need an index buffer right now. But we will + // once de-duplication is done. So assume this is always done. + std::vector index_buffer; + for (size_t i = 0; i < vertices_.size(); i++) { + index_buffer.push_back(i); + } + return buffer.Emplace(index_buffer.data(), + index_buffer.size() * sizeof(IndexType), + alignof(IndexType)); + } + + size_t GetIndexCount() const { return vertices_.size(); } private: // This is a placeholder till vertex de-duplication can be implemented. The // current implementation is a very dumb placeholder. - std::vector vertices_; - - FML_DISALLOW_COPY_AND_ASSIGN(VertexBufferBuilder); + std::vector vertices_; }; } // namespace impeller diff --git a/impeller/compositor/vertex_buffer_builder.mm b/impeller/compositor/vertex_buffer_builder.mm index 12c72d189a75c..0e548006bcc1c 100644 --- a/impeller/compositor/vertex_buffer_builder.mm +++ b/impeller/compositor/vertex_buffer_builder.mm @@ -6,40 +6,6 @@ namespace impeller { -VertexBufferBuilder::VertexBufferBuilder() = default; - -VertexBufferBuilder::~VertexBufferBuilder() = default; - -size_t VertexBufferBuilder::GetIndexCount() const { - return vertices_.size(); -} - -VertexBufferBuilder& VertexBufferBuilder::AddVertices( - std::initializer_list vertices) { - for (const auto& vertex : vertices) { - vertices_.push_back(vertex); - } - return *this; -} - -BufferView VertexBufferBuilder::CreateVertexBuffer(HostBuffer& buffer) const { - return buffer.Emplace( - vertices_.data(), - vertices_.size() * sizeof(decltype(vertices_)::value_type), - alignof(decltype(vertices_)::value_type)); -} - -BufferView VertexBufferBuilder::CreateIndexBuffer(HostBuffer& buffer) const { - // Soooo dumb! We don't actually need an index buffer right now. But we will - // once de-duplication is done. - std::vector index_buffer; - for (size_t i = 0; i < vertices_.size(); i++) { - index_buffer.push_back(i); - } - return buffer.Emplace( - index_buffer.data(), - index_buffer.size() * sizeof(decltype(index_buffer)::value_type), - alignof(decltype(index_buffer)::value_type)); -} +// } // namespace impeller diff --git a/impeller/entity/entity_renderer.mm b/impeller/entity/entity_renderer.mm index 9595a0188da48..030a790c29f85 100644 --- a/impeller/entity/entity_renderer.mm +++ b/impeller/entity/entity_renderer.mm @@ -45,12 +45,12 @@ uniforms.mvp = Matrix::MakeOrthographic(surface.GetSize()); - VertexBufferBuilder vertex_builder; + VertexBufferBuilder vertex_builder; vertex_builder.AddVertices({ - {0, 0, 0.0}, // - {800, 0.0, 0.0}, // - {0.0, 600, 0.0}, // + {{0, 0, 0.0}, {Color::Red()}}, // + {{800, 0.0, 0.0}, {Color::Green()}}, // + {{0.0, 600, 0.0}, {Color::Blue()}}, // }); Command cmd; diff --git a/impeller/primitives/box.frag b/impeller/primitives/box.frag index e3a23d1c266ff..0263c965f294b 100644 --- a/impeller/primitives/box.frag +++ b/impeller/primitives/box.frag @@ -4,8 +4,8 @@ in vec4 color; -out vec4 fragColor; +out vec4 frag_color; void main() { - fragColor = color; + frag_color = color; } diff --git a/impeller/primitives/box.vert b/impeller/primitives/box.vert index 8058ada175273..431c4c04d1f07 100644 --- a/impeller/primitives/box.vert +++ b/impeller/primitives/box.vert @@ -6,12 +6,12 @@ uniform UniformBuffer { mat4 mvp; } uniforms; -in vec3 vertexPosition; -in vec4 vertexColor; +in vec3 vertex_position; +in vec4 vertex_color; out vec4 color; void main() { - gl_Position = uniforms.mvp * vec4(vertexPosition, 1.0); - color = vertexColor; + gl_Position = uniforms.mvp * vec4(vertex_position, 1.0); + color = vertex_color; } From f27cfabc5dcf97aec4c8484269adde11e1844f4b Mon Sep 17 00:00:00 2001 From: Chinmay Garde Date: Tue, 29 Jun 2021 14:02:06 -0700 Subject: [PATCH 104/433] Make device resident vertex buffers easier to use. --- impeller/compositor/BUILD.gn | 2 + impeller/compositor/buffer_view.h | 2 +- impeller/compositor/context.h | 11 +++++ impeller/compositor/context.mm | 10 ++++ impeller/compositor/device_buffer.h | 3 ++ impeller/compositor/device_buffer.mm | 7 +++ impeller/compositor/vertex_buffer.h | 21 ++++++++ impeller/compositor/vertex_buffer.mm | 11 +++++ impeller/compositor/vertex_buffer_builder.h | 55 ++++++++++++++++++--- impeller/entity/entity_renderer.h | 2 + impeller/entity/entity_renderer.mm | 29 ++++++----- 11 files changed, 133 insertions(+), 20 deletions(-) create mode 100644 impeller/compositor/vertex_buffer.h create mode 100644 impeller/compositor/vertex_buffer.mm diff --git a/impeller/compositor/BUILD.gn b/impeller/compositor/BUILD.gn index 5fa6a67aa164d..ebb93652f2c83 100644 --- a/impeller/compositor/BUILD.gn +++ b/impeller/compositor/BUILD.gn @@ -52,6 +52,8 @@ impeller_component("compositor") { "surface.mm", "texture.h", "texture.mm", + "vertex_buffer.h", + "vertex_buffer.mm", "vertex_buffer_builder.h", "vertex_buffer_builder.mm", "vertex_descriptor.h", diff --git a/impeller/compositor/buffer_view.h b/impeller/compositor/buffer_view.h index aa07cf30ceff1..835d8e134ffe7 100644 --- a/impeller/compositor/buffer_view.h +++ b/impeller/compositor/buffer_view.h @@ -11,7 +11,7 @@ namespace impeller { struct BufferView { - std::shared_ptr buffer; + std::shared_ptr buffer; Range range; constexpr operator bool() const { return static_cast(buffer); } diff --git a/impeller/compositor/context.h b/impeller/compositor/context.h index 77dd15a826077..5d40a251b4690 100644 --- a/impeller/compositor/context.h +++ b/impeller/compositor/context.h @@ -25,6 +25,16 @@ class Context { bool IsValid() const; + //---------------------------------------------------------------------------- + /// @return An allocator suitable for allocations that persist between + /// frames. + /// + std::shared_ptr GetPermanentsAllocator() const; + + //---------------------------------------------------------------------------- + /// @return An allocator suitable for allocations that used only for one + /// frame or render pass. + /// std::shared_ptr GetTransientsAllocator() const; std::shared_ptr GetShaderLibrary() const; @@ -39,6 +49,7 @@ class Context { id transfer_queue_ = nullptr; std::shared_ptr shader_library_; std::shared_ptr pipeline_library_; + std::shared_ptr permanents_allocator_; std::shared_ptr transients_allocator_; bool is_valid_ = false; diff --git a/impeller/compositor/context.mm b/impeller/compositor/context.mm index 2ec6cb2fbe68f..4b222b013a504 100644 --- a/impeller/compositor/context.mm +++ b/impeller/compositor/context.mm @@ -61,6 +61,12 @@ if (!transients_allocator_) { return; } + + permanents_allocator_ = std::shared_ptr( + new Allocator(device_, "Impeller Permanents Allocator")); + if (!permanents_allocator_) { + return; + } } is_valid_ = true; @@ -93,6 +99,10 @@ return buffer; } +std::shared_ptr Context::GetPermanentsAllocator() const { + return permanents_allocator_; +} + std::shared_ptr Context::GetTransientsAllocator() const { return transients_allocator_; } diff --git a/impeller/compositor/device_buffer.h b/impeller/compositor/device_buffer.h index b49684a58c65f..cb0f94919956b 100644 --- a/impeller/compositor/device_buffer.h +++ b/impeller/compositor/device_buffer.h @@ -12,6 +12,7 @@ #include "flutter/fml/macros.h" #include "impeller/compositor/allocator.h" #include "impeller/compositor/buffer.h" +#include "impeller/compositor/buffer_view.h" #include "impeller/compositor/range.h" namespace impeller { @@ -31,6 +32,8 @@ class DeviceBuffer final : public Buffer, bool SetLabel(const std::string& label, Range range); + BufferView AsBufferView() const; + private: friend class Allocator; diff --git a/impeller/compositor/device_buffer.mm b/impeller/compositor/device_buffer.mm index ed089d5347297..ff4a1a30888c7 100644 --- a/impeller/compositor/device_buffer.mm +++ b/impeller/compositor/device_buffer.mm @@ -62,4 +62,11 @@ return true; } +BufferView DeviceBuffer::AsBufferView() const { + BufferView view; + view.buffer = shared_from_this(); + view.range = {0u, size_}; + return view; +} + } // namespace impeller diff --git a/impeller/compositor/vertex_buffer.h b/impeller/compositor/vertex_buffer.h new file mode 100644 index 0000000000000..be29c5f3d194c --- /dev/null +++ b/impeller/compositor/vertex_buffer.h @@ -0,0 +1,21 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#pragma once + +#include "impeller/compositor/buffer_view.h" + +namespace impeller { + +struct VertexBuffer { + BufferView vertex_buffer; + BufferView index_buffer; + size_t index_count = 0u; + + constexpr operator bool() const { + return static_cast(vertex_buffer) && static_cast(index_buffer); + } +}; + +} // namespace impeller diff --git a/impeller/compositor/vertex_buffer.mm b/impeller/compositor/vertex_buffer.mm new file mode 100644 index 0000000000000..62568a5e197c4 --- /dev/null +++ b/impeller/compositor/vertex_buffer.mm @@ -0,0 +1,11 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "impeller/compositor/vertex_buffer.h" + +namespace impeller { + +// + +} // namespace impeller diff --git a/impeller/compositor/vertex_buffer_builder.h b/impeller/compositor/vertex_buffer_builder.h index c809a7758c0b2..ab490612911cb 100644 --- a/impeller/compositor/vertex_buffer_builder.h +++ b/impeller/compositor/vertex_buffer_builder.h @@ -9,7 +9,11 @@ #include #include "flutter/fml/macros.h" +#include "impeller/compositor/allocator.h" +#include "impeller/compositor/device_buffer.h" +#include "impeller/compositor/formats.h" #include "impeller/compositor/host_buffer.h" +#include "impeller/compositor/vertex_buffer.h" #include "impeller/geometry/vector.h" namespace impeller { @@ -32,30 +36,67 @@ class VertexBufferBuilder { return *this; } - BufferView CreateVertexBuffer(HostBuffer& buffer) const { + VertexBuffer CreateVertexBuffer(HostBuffer& host) const { + VertexBuffer buffer; + buffer.vertex_buffer = CreateVertexBufferView(host); + buffer.index_buffer = CreateIndexBufferView(host); + buffer.index_count = GetIndexCount(); + return buffer; + }; + + VertexBuffer CreateVertexBuffer(Allocator& device_allocator) const { + VertexBuffer buffer; + // This can be merged into a single allocation. + buffer.vertex_buffer = CreateVertexBufferView(device_allocator); + buffer.index_buffer = CreateIndexBufferView(device_allocator); + buffer.index_count = GetIndexCount(); + return buffer; + }; + + private: + // This is a placeholder till vertex de-duplication can be implemented. The + // current implementation is a very dumb placeholder. + std::vector vertices_; + + BufferView CreateVertexBufferView(HostBuffer& buffer) const { return buffer.Emplace(vertices_.data(), vertices_.size() * sizeof(VertexType), alignof(VertexType)); } - BufferView CreateIndexBuffer(HostBuffer& buffer) const { + BufferView CreateVertexBufferView(Allocator& allocator) const { + auto buffer = allocator.CreateBufferWithCopy( + reinterpret_cast(vertices_.data()), + vertices_.size() * sizeof(VertexType)); + return buffer ? buffer->AsBufferView() : BufferView{}; + } + + std::vector CreateIndexBuffer() const { // So dumb! We don't actually need an index buffer right now. But we will // once de-duplication is done. So assume this is always done. std::vector index_buffer; for (size_t i = 0; i < vertices_.size(); i++) { index_buffer.push_back(i); } + return index_buffer; + } + + BufferView CreateIndexBufferView(HostBuffer& buffer) const { + const auto index_buffer = CreateIndexBuffer(); return buffer.Emplace(index_buffer.data(), index_buffer.size() * sizeof(IndexType), alignof(IndexType)); } - size_t GetIndexCount() const { return vertices_.size(); } + BufferView CreateIndexBufferView(Allocator& allocator) const { + const auto index_buffer = CreateIndexBuffer(); + auto buffer = allocator.CreateBufferWithCopy( + reinterpret_cast(index_buffer.data()), + index_buffer.size() * sizeof(IndexType)); + return buffer ? buffer->AsBufferView() : BufferView{}; + } - private: - // This is a placeholder till vertex de-duplication can be implemented. The - // current implementation is a very dumb placeholder. - std::vector vertices_; + size_t GetIndexCount() const { return vertices_.size(); } }; } // namespace impeller diff --git a/impeller/entity/entity_renderer.h b/impeller/entity/entity_renderer.h index 0d00e6ffe918a..bd91dcb678066 100644 --- a/impeller/entity/entity_renderer.h +++ b/impeller/entity/entity_renderer.h @@ -6,6 +6,7 @@ #include "flutter/fml/macros.h" #include "impeller/compositor/renderer.h" +#include "impeller/compositor/vertex_buffer.h" #include "impeller/entity/entity.h" #include "impeller/primitives/box_primitive.h" @@ -20,6 +21,7 @@ class EntityRenderer final : public Renderer { private: std::shared_ptr root_; std::shared_ptr box_primitive_; + VertexBuffer vertex_buffer_; bool is_valid_ = false; // |Renderer| diff --git a/impeller/entity/entity_renderer.mm b/impeller/entity/entity_renderer.mm index 030a790c29f85..eb4cbcd3c747b 100644 --- a/impeller/entity/entity_renderer.mm +++ b/impeller/entity/entity_renderer.mm @@ -29,6 +29,20 @@ return; } + VertexBufferBuilder vertex_builder; + + vertex_builder.AddVertices({ + {{0, 0, 0.0}, {Color::Red()}}, // + {{800, 0.0, 0.0}, {Color::Green()}}, // + {{0.0, 600, 0.0}, {Color::Blue()}}, // + }); + + vertex_buffer_ = + vertex_builder.CreateVertexBuffer(*context->GetPermanentsAllocator()); + if (!vertex_buffer_) { + return; + } + is_valid_ = true; } @@ -45,25 +59,16 @@ uniforms.mvp = Matrix::MakeOrthographic(surface.GetSize()); - VertexBufferBuilder vertex_builder; - - vertex_builder.AddVertices({ - {{0, 0, 0.0}, {Color::Red()}}, // - {{800, 0.0, 0.0}, {Color::Green()}}, // - {{0.0, 600, 0.0}, {Color::Blue()}}, // - }); - Command cmd; cmd.label = "Box"; cmd.pipeline = box_primitive_->GetPipeline(); cmd.vertex_bindings.buffers[box_primitive_->GetVertexBufferIndex()] = - vertex_builder.CreateVertexBuffer(pass.GetTransientsBuffer()); + vertex_buffer_.vertex_buffer; cmd.vertex_bindings .buffers[shader::BoxVertexInfo::kUniformUniformBuffer.binding] = pass.GetTransientsBuffer().EmplaceUniform(uniforms); - cmd.index_buffer = - vertex_builder.CreateIndexBuffer(pass.GetTransientsBuffer()); - cmd.index_count = vertex_builder.GetIndexCount(); + cmd.index_buffer = vertex_buffer_.index_buffer; + cmd.index_count = vertex_buffer_.index_count; cmd.primitive_type = PrimitiveType::kTriange; if (!pass.RecordCommand(std::move(cmd))) { return false; From fd4a29e17242d371e576630fdf8800b9611e1925 Mon Sep 17 00:00:00 2001 From: Chinmay Garde Date: Tue, 29 Jun 2021 14:55:22 -0700 Subject: [PATCH 105/433] Better labeling in vertex buffer builder. --- impeller/compositor/vertex_buffer_builder.h | 40 ++++++++++++++++----- impeller/entity/entity_renderer.mm | 3 +- 2 files changed, 34 insertions(+), 9 deletions(-) diff --git a/impeller/compositor/vertex_buffer_builder.h b/impeller/compositor/vertex_buffer_builder.h index ab490612911cb..12b093ca2e896 100644 --- a/impeller/compositor/vertex_buffer_builder.h +++ b/impeller/compositor/vertex_buffer_builder.h @@ -9,6 +9,7 @@ #include #include "flutter/fml/macros.h" +#include "impeller/base/base.h" #include "impeller/compositor/allocator.h" #include "impeller/compositor/device_buffer.h" #include "impeller/compositor/formats.h" @@ -28,6 +29,8 @@ class VertexBufferBuilder { ~VertexBufferBuilder() = default; + void SetLabel(std::string label) { label_ = std::move(label); } + VertexBufferBuilder& AddVertices( std::initializer_list vertices) { for (const auto& vertex : vertices) { @@ -57,18 +60,29 @@ class VertexBufferBuilder { // This is a placeholder till vertex de-duplication can be implemented. The // current implementation is a very dumb placeholder. std::vector vertices_; + std::string label_; BufferView CreateVertexBufferView(HostBuffer& buffer) const { - return buffer.Emplace(vertices_.data(), - vertices_.size() * sizeof(VertexType), - alignof(VertexType)); + auto view = + buffer.Emplace(vertices_.data(), vertices_.size() * sizeof(VertexType), + alignof(VertexType)); + if (!label_.empty()) { + view.SetLabel(SPrintF("%s Vertices"), label_.c_str()); + } + return view; } BufferView CreateVertexBufferView(Allocator& allocator) const { auto buffer = allocator.CreateBufferWithCopy( reinterpret_cast(vertices_.data()), vertices_.size() * sizeof(VertexType)); - return buffer ? buffer->AsBufferView() : BufferView{}; + if (!buffer) { + return {}; + } + if (!label_.empty()) { + buffer->SetLabel(SPrintF("%s Vertices", label_.c_str())); + } + return buffer->AsBufferView(); } std::vector CreateIndexBuffer() const { @@ -83,9 +97,13 @@ class VertexBufferBuilder { BufferView CreateIndexBufferView(HostBuffer& buffer) const { const auto index_buffer = CreateIndexBuffer(); - return buffer.Emplace(index_buffer.data(), - index_buffer.size() * sizeof(IndexType), - alignof(IndexType)); + auto view = buffer.Emplace(index_buffer.data(), + index_buffer.size() * sizeof(IndexType), + alignof(IndexType)); + if (!label_.empty()) { + view.SetLabel(SPrintF("%s Indices"), label_); + } + return view; } BufferView CreateIndexBufferView(Allocator& allocator) const { @@ -93,7 +111,13 @@ class VertexBufferBuilder { auto buffer = allocator.CreateBufferWithCopy( reinterpret_cast(index_buffer.data()), index_buffer.size() * sizeof(IndexType)); - return buffer ? buffer->AsBufferView() : BufferView{}; + if (!buffer) { + return {}; + } + if (!label_.empty()) { + buffer->SetLabel(SPrintF("%s Indices", label_.c_str())); + } + return buffer->AsBufferView(); } size_t GetIndexCount() const { return vertices_.size(); } diff --git a/impeller/entity/entity_renderer.mm b/impeller/entity/entity_renderer.mm index eb4cbcd3c747b..06df740256dc3 100644 --- a/impeller/entity/entity_renderer.mm +++ b/impeller/entity/entity_renderer.mm @@ -30,7 +30,7 @@ } VertexBufferBuilder vertex_builder; - + vertex_builder.SetLabel("Box"); vertex_builder.AddVertices({ {{0, 0, 0.0}, {Color::Red()}}, // {{800, 0.0, 0.0}, {Color::Green()}}, // @@ -39,6 +39,7 @@ vertex_buffer_ = vertex_builder.CreateVertexBuffer(*context->GetPermanentsAllocator()); + if (!vertex_buffer_) { return; } From bb60ed4bfbc4fc21c40226d0cfdacc407ceb5c02 Mon Sep 17 00:00:00 2001 From: Chinmay Garde Date: Tue, 29 Jun 2021 17:27:59 -0700 Subject: [PATCH 106/433] Wire up samplers. --- impeller/compositor/BUILD.gn | 2 + impeller/compositor/context.h | 4 ++ impeller/compositor/context.mm | 11 ++++ impeller/compositor/formats.h | 17 ++++++ impeller/compositor/formats_metal.h | 23 ++++++++ impeller/compositor/pipeline_library.h | 2 +- impeller/compositor/sampler.h | 12 ++++- impeller/compositor/sampler.mm | 6 ++- impeller/compositor/sampler_descriptor.h | 64 +++++++++++++++++++++++ impeller/compositor/sampler_descriptor.mm | 43 +++++++++++++++ impeller/entity/entity_renderer.mm | 10 ++-- impeller/primitives/box.frag | 10 +++- impeller/primitives/box.vert | 3 ++ 13 files changed, 200 insertions(+), 7 deletions(-) create mode 100644 impeller/compositor/sampler_descriptor.h create mode 100644 impeller/compositor/sampler_descriptor.mm diff --git a/impeller/compositor/BUILD.gn b/impeller/compositor/BUILD.gn index ebb93652f2c83..3e2eec64d6db7 100644 --- a/impeller/compositor/BUILD.gn +++ b/impeller/compositor/BUILD.gn @@ -44,6 +44,8 @@ impeller_component("compositor") { "renderer.mm", "sampler.h", "sampler.mm", + "sampler_descriptor.h", + "sampler_descriptor.mm", "shader_function.h", "shader_function.mm", "shader_library.h", diff --git a/impeller/compositor/context.h b/impeller/compositor/context.h index 5d40a251b4690..19200cfcc7816 100644 --- a/impeller/compositor/context.h +++ b/impeller/compositor/context.h @@ -14,6 +14,7 @@ namespace impeller { class ShaderLibrary; +class SamplerLibrary; class CommandBuffer; class Allocator; @@ -39,6 +40,8 @@ class Context { std::shared_ptr GetShaderLibrary() const; + std::shared_ptr GetSamplerLibrary() const; + std::shared_ptr GetPipelineLibrary() const; std::shared_ptr CreateRenderCommandBuffer() const; @@ -49,6 +52,7 @@ class Context { id transfer_queue_ = nullptr; std::shared_ptr shader_library_; std::shared_ptr pipeline_library_; + std::shared_ptr sampler_library_; std::shared_ptr permanents_allocator_; std::shared_ptr transients_allocator_; bool is_valid_ = false; diff --git a/impeller/compositor/context.mm b/impeller/compositor/context.mm index 4b222b013a504..f02e99f8da639 100644 --- a/impeller/compositor/context.mm +++ b/impeller/compositor/context.mm @@ -8,6 +8,7 @@ #include "flutter/fml/paths.h" #include "impeller/compositor/allocator.h" #include "impeller/compositor/command_buffer.h" +#include "impeller/compositor/sampler_descriptor.h" #include "impeller/compositor/shader_library.h" namespace impeller { @@ -55,6 +56,12 @@ std::shared_ptr(new PipelineLibrary(device_)); } + // Setup the sampler library. + { // + sampler_library_ = + std::shared_ptr(new SamplerLibrary(device_)); + } + { transients_allocator_ = std::shared_ptr( new Allocator(device_, "Impeller Transients Allocator")); @@ -86,6 +93,10 @@ return pipeline_library_; } +std::shared_ptr Context::GetSamplerLibrary() const { + return sampler_library_; +} + std::shared_ptr Context::CreateRenderCommandBuffer() const { if (!IsValid()) { return nullptr; diff --git a/impeller/compositor/formats.h b/impeller/compositor/formats.h index 1dda35737a42f..c970edc1886de 100644 --- a/impeller/compositor/formats.h +++ b/impeller/compositor/formats.h @@ -66,6 +66,23 @@ enum class PrimitiveType { // checks. Hence, they are not supported here. }; +enum class MinMagFilter { + /// Select nearest to the sample point. Most widely supported. + kNearest, + /// Select two points and linearly interpolate between them. Some formats may + /// not support this. + kLinear, +}; + +enum class SamplerAddressMode { + kClampToEdge, + kRepeat, + kMirror, + // More modes are almost always supported but they are usually behind + // extensions checks. The ones current in these structs are safe (always + // supported) defaults. +}; + enum class ColorWriteMask : uint64_t { kNone = 0, kRed = 1 << 0, diff --git a/impeller/compositor/formats_metal.h b/impeller/compositor/formats_metal.h index a1d63def21472..970b1ad53887f 100644 --- a/impeller/compositor/formats_metal.h +++ b/impeller/compositor/formats_metal.h @@ -215,6 +215,29 @@ constexpr StoreAction FromMTLStoreAction(MTLStoreAction action) { return StoreAction::kDontCare; } +constexpr MTLSamplerMinMagFilter ToMTLSamplerMinMagFilter(MinMagFilter filter) { + switch (filter) { + case MinMagFilter::kNearest: + return MTLSamplerMinMagFilterNearest; + case MinMagFilter::kLinear: + return MTLSamplerMinMagFilterLinear; + } + return MTLSamplerMinMagFilterNearest; +} + +constexpr MTLSamplerAddressMode ToMTLSamplerAddressMode( + SamplerAddressMode mode) { + switch (mode) { + case SamplerAddressMode::kClampToEdge: + return MTLSamplerAddressModeClampToEdge; + case SamplerAddressMode::kRepeat: + return MTLSamplerAddressModeRepeat; + case SamplerAddressMode::kMirror: + return MTLSamplerAddressModeMirrorRepeat; + } + return MTLSamplerAddressModeClampToEdge; +} + constexpr MTLClearColor ToMTLClearColor(const Color& color) { return MTLClearColorMake(color.red, color.green, color.blue, color.alpha); } diff --git a/impeller/compositor/pipeline_library.h b/impeller/compositor/pipeline_library.h index bf8b7eda553d6..b350ac9134231 100644 --- a/impeller/compositor/pipeline_library.h +++ b/impeller/compositor/pipeline_library.h @@ -32,7 +32,7 @@ class PipelineLibrary : public std::enable_shared_from_this { std::shared_ptr, ComparableHash, ComparableEqual>; - id device_; + id device_ = nullptr; Pipelines pipelines_; PipelineLibrary(id device); diff --git a/impeller/compositor/sampler.h b/impeller/compositor/sampler.h index de73319410c64..b1d875c954e2b 100644 --- a/impeller/compositor/sampler.h +++ b/impeller/compositor/sampler.h @@ -4,17 +4,27 @@ #pragma once +#include + #include "flutter/fml/macros.h" namespace impeller { +class SamplerLibrary; + class Sampler { public: - Sampler(); + bool IsValid() const; ~Sampler(); private: + friend SamplerLibrary; + + id state_ = nullptr; + + Sampler(id state); + FML_DISALLOW_COPY_AND_ASSIGN(Sampler); }; diff --git a/impeller/compositor/sampler.mm b/impeller/compositor/sampler.mm index 14678d946a153..82c23788992d9 100644 --- a/impeller/compositor/sampler.mm +++ b/impeller/compositor/sampler.mm @@ -6,8 +6,12 @@ namespace impeller { -Sampler::Sampler() = default; +Sampler::Sampler(id state) : state_(state) {} Sampler::~Sampler() = default; +bool Sampler::IsValid() const { + return state_; +} + } // namespace impeller diff --git a/impeller/compositor/sampler_descriptor.h b/impeller/compositor/sampler_descriptor.h new file mode 100644 index 0000000000000..b07bacfe82a49 --- /dev/null +++ b/impeller/compositor/sampler_descriptor.h @@ -0,0 +1,64 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#pragma once + +#include + +#include + +#include "flutter/fml/macros.h" +#include "impeller/compositor/comparable.h" +#include "impeller/compositor/formats.h" + +namespace impeller { + +class Sampler; +class Context; + +struct SamplerDescriptor final : public Comparable { + MinMagFilter min_filter = MinMagFilter::kNearest; + MinMagFilter mag_filter = MinMagFilter::kNearest; + + SamplerAddressMode width_address_mode = SamplerAddressMode::kClampToEdge; + SamplerAddressMode height_address_mode = SamplerAddressMode::kClampToEdge; + SamplerAddressMode depth_address_mode = SamplerAddressMode::kClampToEdge; + + // Comparable + std::size_t GetHash() const override { + return fml::HashCombine(min_filter, mag_filter, width_address_mode, + height_address_mode, depth_address_mode); + } + + // Comparable + bool IsEqual(const SamplerDescriptor& o) const override { + return min_filter == o.min_filter && mag_filter == o.mag_filter && + width_address_mode == o.width_address_mode && + height_address_mode == o.height_address_mode && + depth_address_mode == o.depth_address_mode; + } +}; + +class SamplerLibrary { + public: + ~SamplerLibrary(); + + std::shared_ptr GetSampler(SamplerDescriptor descriptor); + + private: + friend Context; + + using Samplers = std::unordered_map, + ComparableHash, + ComparableEqual>; + id device_ = nullptr; + Samplers samplers_; + + SamplerLibrary(id device); + + FML_DISALLOW_COPY_AND_ASSIGN(SamplerLibrary); +}; + +} // namespace impeller diff --git a/impeller/compositor/sampler_descriptor.mm b/impeller/compositor/sampler_descriptor.mm new file mode 100644 index 0000000000000..b59dfae8908f7 --- /dev/null +++ b/impeller/compositor/sampler_descriptor.mm @@ -0,0 +1,43 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "impeller/compositor/sampler_descriptor.h" + +#include "impeller/compositor/formats_metal.h" +#include "impeller/compositor/sampler.h" + +namespace impeller { + +SamplerLibrary::SamplerLibrary(id device) : device_(device) {} + +SamplerLibrary::~SamplerLibrary() = default; + +std::shared_ptr SamplerLibrary::GetSampler( + SamplerDescriptor descriptor) { + auto found = samplers_.find(descriptor); + if (found != samplers_.end()) { + return found->second; + } + if (!device_) { + return nullptr; + } + auto desc = [[MTLSamplerDescriptor alloc] init]; + desc.minFilter = ToMTLSamplerMinMagFilter(descriptor.min_filter); + desc.magFilter = ToMTLSamplerMinMagFilter(descriptor.mag_filter); + desc.sAddressMode = MTLSamplerAddressMode(descriptor.width_address_mode); + desc.rAddressMode = MTLSamplerAddressMode(descriptor.depth_address_mode); + desc.tAddressMode = MTLSamplerAddressMode(descriptor.height_address_mode); + auto mtl_sampler = [device_ newSamplerStateWithDescriptor:desc]; + if (!mtl_sampler) { + return nullptr; + } + auto sampler = std::shared_ptr(new Sampler(mtl_sampler)); + if (!sampler->IsValid()) { + return nullptr; + } + samplers_[descriptor] = sampler; + return sampler; +} + +} // namespace impeller diff --git a/impeller/entity/entity_renderer.mm b/impeller/entity/entity_renderer.mm index 06df740256dc3..896faddf9bb6d 100644 --- a/impeller/entity/entity_renderer.mm +++ b/impeller/entity/entity_renderer.mm @@ -32,9 +32,13 @@ VertexBufferBuilder vertex_builder; vertex_builder.SetLabel("Box"); vertex_builder.AddVertices({ - {{0, 0, 0.0}, {Color::Red()}}, // - {{800, 0.0, 0.0}, {Color::Green()}}, // - {{0.0, 600, 0.0}, {Color::Blue()}}, // + {{100, 100, 0.0}, {Color::Red()}}, // 1 + {{800, 100, 0.0}, {Color::Green()}}, // 2 + {{800, 800, 0.0}, {Color::Blue()}}, // 3 + + {{100, 100, 0.0}, {Color::Cyan()}}, // 1 + {{800, 800, 0.0}, {Color::White()}}, // 3 + {{100, 800, 0.0}, {Color::Purple()}}, // 4 }); vertex_buffer_ = diff --git a/impeller/primitives/box.frag b/impeller/primitives/box.frag index 0263c965f294b..4a32a059ea362 100644 --- a/impeller/primitives/box.frag +++ b/impeller/primitives/box.frag @@ -3,9 +3,17 @@ // found in the LICENSE file. in vec4 color; +in vec2 interpolated_texture_coordinates; + +uniform sampler2D contents; +// uniform sampler2D texture_sampler2; +// uniform sampler2D texture_sampler3; out vec4 frag_color; void main() { - frag_color = color; + vec4 color1 = texture(contents, interpolated_texture_coordinates); + // vec4 color2 = texture(texture_sampler2, interpolated_texture_coordinates); + // vec4 color3 = texture(texture_sampler3, interpolated_texture_coordinates); + frag_color = color1; } diff --git a/impeller/primitives/box.vert b/impeller/primitives/box.vert index 431c4c04d1f07..e764b6267b58d 100644 --- a/impeller/primitives/box.vert +++ b/impeller/primitives/box.vert @@ -8,10 +8,13 @@ uniform UniformBuffer { in vec3 vertex_position; in vec4 vertex_color; +in vec2 texture_coordinates; out vec4 color; +out vec2 interpolated_texture_coordinates; void main() { gl_Position = uniforms.mvp * vec4(vertex_position, 1.0); color = vertex_color; + interpolated_texture_coordinates = texture_coordinates; } From b7864bf67a824f280b14b436b3e8b96d6ee683b7 Mon Sep 17 00:00:00 2001 From: Chinmay Garde Date: Wed, 30 Jun 2021 12:39:44 -0700 Subject: [PATCH 107/433] Generate reflection info for samplers and images. --- impeller/compiler/code_gen_template.h | 15 ++++++ impeller/compiler/reflector.cc | 21 ++++++++ impeller/compositor/command.h | 4 +- impeller/compositor/render_pass.mm | 69 +++++++++++++++++++++++---- impeller/compositor/sampler.h | 2 + impeller/compositor/sampler.mm | 4 ++ impeller/compositor/texture.h | 2 + impeller/compositor/texture.mm | 4 ++ impeller/entity/entity_renderer.mm | 13 +++++ impeller/primitives/box.frag | 14 +++--- impeller/tools/build_metal_library.py | 3 ++ 11 files changed, 132 insertions(+), 19 deletions(-) diff --git a/impeller/compiler/code_gen_template.h b/impeller/compiler/code_gen_template.h index 3236e510717ab..961481c5a3afc 100644 --- a/impeller/compiler/code_gen_template.h +++ b/impeller/compiler/code_gen_template.h @@ -71,6 +71,21 @@ struct {{camel_case(shader_name)}}{{camel_case(shader_stage)}}Info { {% endfor %} }; + // =========================================================================== + // Sampled Images ============================================================ + // =========================================================================== +{% for sampled_image in sampled_images %} + + static constexpr auto kInput{{camel_case(sampled_image.name)}} = ShaderStageIOSlot { // {{sampled_image.name}} + "{{sampled_image.name}}", // name + {{sampled_image.location}}u, // attribute location + {{sampled_image.type.type_name}}, // type + {{sampled_image.type.bit_width}}u, // bit width of type + {{sampled_image.type.vec_size}}u, // vec size + {{sampled_image.type.columns}}u // number of columns + }; +{% endfor %} + // =========================================================================== // Stage Outputs ============================================================= // =========================================================================== diff --git a/impeller/compiler/reflector.cc b/impeller/compiler/reflector.cc index 8e9768dd6a7ed..2a5abbbccab30 100644 --- a/impeller/compiler/reflector.cc +++ b/impeller/compiler/reflector.cc @@ -180,6 +180,27 @@ std::optional Reflector::GenerateTemplateArguments() const { return std::nullopt; } + { + auto combined_sampled_images = + ReflectResources(shader_resources.sampled_images); + auto images = ReflectResources(shader_resources.separate_images); + auto samplers = ReflectResources(shader_resources.separate_samplers); + if (!combined_sampled_images.has_value() || !images.has_value() || + !samplers.has_value()) { + return std::nullopt; + } + auto& stage_inputs = root["sampled_images"] = nlohmann::json::array_t{}; + for (auto value : combined_sampled_images.value()) { + stage_inputs.emplace_back(std::move(value)); + } + for (auto value : images.value()) { + stage_inputs.emplace_back(std::move(value)); + } + for (auto value : samplers.value()) { + stage_inputs.emplace_back(std::move(value)); + } + } + if (auto stage_outputs = ReflectResources(shader_resources.stage_outputs); stage_outputs.has_value()) { root["stage_outputs"] = std::move(stage_outputs.value()); diff --git a/impeller/compositor/command.h b/impeller/compositor/command.h index 3e0a455d635c4..6c7d2e6d46854 100644 --- a/impeller/compositor/command.h +++ b/impeller/compositor/command.h @@ -20,8 +20,8 @@ class Sampler; struct Bindings { std::map buffers; - std::map> textures; - std::map> samplers; + std::map> textures; + std::map> samplers; }; struct Command { diff --git a/impeller/compositor/render_pass.mm b/impeller/compositor/render_pass.mm index 8f770d0656170..5e77e88545035 100644 --- a/impeller/compositor/render_pass.mm +++ b/impeller/compositor/render_pass.mm @@ -9,6 +9,7 @@ #include "impeller/base/base.h" #include "impeller/compositor/device_buffer.h" #include "impeller/compositor/formats_metal.h" +#include "impeller/compositor/sampler.h" #include "impeller/shader_glue/shader_types.h" namespace impeller { @@ -195,23 +196,71 @@ static bool Bind(id pass, return false; } - [pass setVertexBuffer:buffer offset:view.range.offset atIndex:bind_index]; - return true; + switch (stage) { + case ShaderStage::kVertex: + [pass setVertexBuffer:buffer offset:view.range.offset atIndex:bind_index]; + return true; + case ShaderStage::kFragment: + [pass setFragmentBuffer:buffer + offset:view.range.offset + atIndex:bind_index]; + return true; + default: + FML_DLOG(ERROR) << "Cannot bind buffer to unknown shader stage."; + return false; + } + + return false; } -static bool Bind(Allocator& allocator, +static bool Bind(id pass, + Allocator& allocator, ShaderStage stage, size_t bind_index, - const Texture& view) { - FML_CHECK(false); + const Texture& texture) { + if (!texture.IsValid()) { + return false; + } + + switch (stage) { + case ShaderStage::kVertex: + [pass setVertexTexture:texture.GetMTLTexture() atIndex:bind_index]; + return true; + case ShaderStage::kFragment: + [pass setFragmentTexture:texture.GetMTLTexture() atIndex:bind_index]; + return true; + default: + FML_DLOG(ERROR) << "Cannot bind buffer to unknown shader stage."; + return false; + } + return false; } -static bool Bind(Allocator& allocator, +static bool Bind(id pass, + Allocator& allocator, ShaderStage stage, size_t bind_index, - const Sampler& view) { - FML_CHECK(false); + const Sampler& sampler) { + if (!sampler.IsValid()) { + return false; + } + + switch (stage) { + case ShaderStage::kVertex: + [pass setVertexSamplerState:sampler.GetMTLSamplerState() + atIndex:bind_index]; + + return true; + case ShaderStage::kFragment: + [pass setFragmentSamplerState:sampler.GetMTLSamplerState() + atIndex:bind_index]; + return true; + default: + FML_DLOG(ERROR) << "Cannot bind buffer to unknown shader stage."; + return false; + } + return false; } @@ -228,12 +277,12 @@ static bool Bind(Allocator& allocator, } } for (const auto texture : bindings.textures) { - if (!Bind(allocator, stage, texture.first, *texture.second)) { + if (!Bind(pass, allocator, stage, texture.first, *texture.second)) { return false; } } for (const auto sampler : bindings.samplers) { - if (!Bind(allocator, stage, sampler.first, *sampler.second)) { + if (!Bind(pass, allocator, stage, sampler.first, *sampler.second)) { return false; } } diff --git a/impeller/compositor/sampler.h b/impeller/compositor/sampler.h index b1d875c954e2b..da7fdd37f5238 100644 --- a/impeller/compositor/sampler.h +++ b/impeller/compositor/sampler.h @@ -18,6 +18,8 @@ class Sampler { ~Sampler(); + id GetMTLSamplerState() const; + private: friend SamplerLibrary; diff --git a/impeller/compositor/sampler.mm b/impeller/compositor/sampler.mm index 82c23788992d9..26f09d0fbe799 100644 --- a/impeller/compositor/sampler.mm +++ b/impeller/compositor/sampler.mm @@ -14,4 +14,8 @@ return state_; } +id Sampler::GetMTLSamplerState() const { + return state_; +} + } // namespace impeller diff --git a/impeller/compositor/texture.h b/impeller/compositor/texture.h index e986184bcae7a..ee0e8c9ba84ef 100644 --- a/impeller/compositor/texture.h +++ b/impeller/compositor/texture.h @@ -17,6 +17,8 @@ class Texture { ~Texture(); + bool IsValid() const; + Size GetSize() const; id GetMTLTexture() const; diff --git a/impeller/compositor/texture.mm b/impeller/compositor/texture.mm index aab70094cd0cd..eca5825930e37 100644 --- a/impeller/compositor/texture.mm +++ b/impeller/compositor/texture.mm @@ -19,4 +19,8 @@ return texture_; } +bool Texture::IsValid() const { + return texture_; +} + } // namespace impeller diff --git a/impeller/entity/entity_renderer.mm b/impeller/entity/entity_renderer.mm index 896faddf9bb6d..b9d4af5e71839 100644 --- a/impeller/entity/entity_renderer.mm +++ b/impeller/entity/entity_renderer.mm @@ -6,6 +6,7 @@ #include "flutter/fml/time/time_point.h" #include "impeller/compositor/command.h" +#include "impeller/compositor/sampler_descriptor.h" #include "impeller/compositor/surface.h" #include "impeller/compositor/vertex_buffer_builder.h" #include "impeller/primitives/box.frag.h" @@ -67,11 +68,23 @@ Command cmd; cmd.label = "Box"; cmd.pipeline = box_primitive_->GetPipeline(); + cmd.vertex_bindings.buffers[box_primitive_->GetVertexBufferIndex()] = vertex_buffer_.vertex_buffer; cmd.vertex_bindings .buffers[shader::BoxVertexInfo::kUniformUniformBuffer.binding] = pass.GetTransientsBuffer().EmplaceUniform(uniforms); + + cmd.fragment_bindings + .samplers[shader::BoxFragmentInfo::kInputContents1.location] = + GetContext()->GetSamplerLibrary()->GetSampler({}); + cmd.fragment_bindings + .samplers[shader::BoxFragmentInfo::kInputContents2.location] = + GetContext()->GetSamplerLibrary()->GetSampler({}); + cmd.fragment_bindings + .samplers[shader::BoxFragmentInfo::kInputContents3.location] = + GetContext()->GetSamplerLibrary()->GetSampler({}); + cmd.index_buffer = vertex_buffer_.index_buffer; cmd.index_count = vertex_buffer_.index_count; cmd.primitive_type = PrimitiveType::kTriange; diff --git a/impeller/primitives/box.frag b/impeller/primitives/box.frag index 4a32a059ea362..485250812779e 100644 --- a/impeller/primitives/box.frag +++ b/impeller/primitives/box.frag @@ -5,15 +5,15 @@ in vec4 color; in vec2 interpolated_texture_coordinates; -uniform sampler2D contents; -// uniform sampler2D texture_sampler2; -// uniform sampler2D texture_sampler3; +uniform sampler2D contents1; +uniform sampler2D contents2; +uniform sampler2D contents3; out vec4 frag_color; void main() { - vec4 color1 = texture(contents, interpolated_texture_coordinates); - // vec4 color2 = texture(texture_sampler2, interpolated_texture_coordinates); - // vec4 color3 = texture(texture_sampler3, interpolated_texture_coordinates); - frag_color = color1; + vec4 color1 = texture(contents1, interpolated_texture_coordinates); + vec4 color2 = texture(contents2, interpolated_texture_coordinates); + vec4 color3 = texture(contents3, interpolated_texture_coordinates); + frag_color = color3; } diff --git a/impeller/tools/build_metal_library.py b/impeller/tools/build_metal_library.py index e9ba9bcc86b1a..fa06b8f45d07e 100644 --- a/impeller/tools/build_metal_library.py +++ b/impeller/tools/build_metal_library.py @@ -41,6 +41,9 @@ def Main(): # debugging but should be removed from release builds. "-MO", "-gline-tables-only", + # These warnings are from generated code and would make no sense to the GLSL + # author. + "-Wno-unused-variable", # Both user and system header will be tracked. "-MMD", "-MF", From 2cf9de52d4fb6cc7a2e334057b09f3d0f21c6900 Mon Sep 17 00:00:00 2001 From: Chinmay Garde Date: Wed, 30 Jun 2021 13:49:43 -0700 Subject: [PATCH 108/433] Cleanup code-gen template to avoid empty comments. --- impeller/compiler/code_gen_template.h | 16 ++++++++++------ impeller/compiler/reflector.cc | 22 +++++++++++++--------- 2 files changed, 23 insertions(+), 15 deletions(-) diff --git a/impeller/compiler/code_gen_template.h b/impeller/compiler/code_gen_template.h index 961481c5a3afc..65a55ec43ab4d 100644 --- a/impeller/compiler/code_gen_template.h +++ b/impeller/compiler/code_gen_template.h @@ -25,8 +25,7 @@ struct {{camel_case(shader_name)}}{{camel_case(shader_stage)}}Info { static constexpr std::string_view kLabel = "{{camel_case(shader_name)}}"; static constexpr std::string_view kEntrypointName = "{{entrypoint}}"; static constexpr ShaderStage kShaderStage = {{to_shader_stage(shader_stage)}}; - - +{% if length(struct_definitions) > 0 %} // =========================================================================== // Struct Definitions ======================================================== // =========================================================================== @@ -38,7 +37,8 @@ struct {{camel_case(shader_name)}}{{camel_case(shader_stage)}}Info { {% endfor %} }; // struct {{def.name}} {% endfor %} - +{% endif %} +{% if length(uniform_buffers) > 0 %} // =========================================================================== // Stage Uniforms ============================================================ // =========================================================================== @@ -49,10 +49,11 @@ struct {{camel_case(shader_name)}}{{camel_case(shader_stage)}}Info { {{uniform.binding}}u, // binding }; {% endfor %} - +{% endif %} // =========================================================================== // Stage Inputs ============================================================== // =========================================================================== +{% if length(stage_inputs) > 0 %} {% for stage_input in stage_inputs %} static constexpr auto kInput{{camel_case(stage_input.name)}} = ShaderStageIOSlot { // {{stage_input.name}} @@ -64,6 +65,7 @@ struct {{camel_case(shader_name)}}{{camel_case(shader_stage)}}Info { {{stage_input.type.columns}}u // number of columns }; {% endfor %} +{% endif %} static constexpr std::array kAllShaderStageInputs = { {% for stage_input in stage_inputs %} @@ -71,6 +73,7 @@ struct {{camel_case(shader_name)}}{{camel_case(shader_stage)}}Info { {% endfor %} }; +{% if length(sampled_images) > 0 %} // =========================================================================== // Sampled Images ============================================================ // =========================================================================== @@ -85,10 +88,11 @@ struct {{camel_case(shader_name)}}{{camel_case(shader_stage)}}Info { {{sampled_image.type.columns}}u // number of columns }; {% endfor %} - +{% endif %} // =========================================================================== // Stage Outputs ============================================================= // =========================================================================== +{% if length(stage_outputs) > 0 %} {% for stage_output in stage_outputs %} static constexpr auto kOutput{{camel_case(stage_output.name)}} = ShaderStageIOSlot { // {{stage_output.name}} "{{stage_output.name}}", // name @@ -99,12 +103,12 @@ struct {{camel_case(shader_name)}}{{camel_case(shader_stage)}}Info { {{stage_output.type.columns}}u // number of columns }; {% endfor %} - static constexpr std::array kAllShaderStageOutputs = { {% for stage_output in stage_outputs %} &kOutput{{camel_case(stage_output.name)}}, // {{stage_output.name}} {% endfor %} }; +{% endif %} }; // struct {{camel_case(shader_name)}}{{camel_case(shader_stage)}}Info diff --git a/impeller/compiler/reflector.cc b/impeller/compiler/reflector.cc index 2a5abbbccab30..db6c340073215 100644 --- a/impeller/compiler/reflector.cc +++ b/impeller/compiler/reflector.cc @@ -173,11 +173,15 @@ std::optional Reflector::GenerateTemplateArguments() const { return std::nullopt; } - if (auto stage_inputs = ReflectResources(shader_resources.stage_inputs); - stage_inputs.has_value()) { - root["stage_inputs"] = std::move(stage_inputs.value()); - } else { - return std::nullopt; + { + auto& stage_inputs = root["stage_inputs"] = nlohmann::json::array_t{}; + if (auto stage_inputs_json = + ReflectResources(shader_resources.stage_inputs); + stage_inputs_json.has_value()) { + stage_inputs = std::move(stage_inputs_json.value()); + } else { + return std::nullopt; + } } { @@ -189,15 +193,15 @@ std::optional Reflector::GenerateTemplateArguments() const { !samplers.has_value()) { return std::nullopt; } - auto& stage_inputs = root["sampled_images"] = nlohmann::json::array_t{}; + auto& sampled_images = root["sampled_images"] = nlohmann::json::array_t{}; for (auto value : combined_sampled_images.value()) { - stage_inputs.emplace_back(std::move(value)); + sampled_images.emplace_back(std::move(value)); } for (auto value : images.value()) { - stage_inputs.emplace_back(std::move(value)); + sampled_images.emplace_back(std::move(value)); } for (auto value : samplers.value()) { - stage_inputs.emplace_back(std::move(value)); + sampled_images.emplace_back(std::move(value)); } } From a512418afae21e7cac059d9d65cb27226d4b29d1 Mon Sep 17 00:00:00 2001 From: Chinmay Garde Date: Thu, 1 Jul 2021 11:32:00 -0700 Subject: [PATCH 109/433] Note the descriptor set and binding in IO slot. --- impeller/compiler/code_gen_template.h | 6 ++++++ impeller/entity/entity_renderer.mm | 18 +++++++++--------- impeller/shader_glue/shader_types.h | 2 ++ 3 files changed, 17 insertions(+), 9 deletions(-) diff --git a/impeller/compiler/code_gen_template.h b/impeller/compiler/code_gen_template.h index 65a55ec43ab4d..3edca285eb800 100644 --- a/impeller/compiler/code_gen_template.h +++ b/impeller/compiler/code_gen_template.h @@ -59,6 +59,8 @@ struct {{camel_case(shader_name)}}{{camel_case(shader_stage)}}Info { static constexpr auto kInput{{camel_case(stage_input.name)}} = ShaderStageIOSlot { // {{stage_input.name}} "{{stage_input.name}}", // name {{stage_input.location}}u, // attribute location + {{stage_input.descriptor_set}}u, // attribute set + {{stage_input.binding}}u, // attribute binding {{stage_input.type.type_name}}, // type {{stage_input.type.bit_width}}u, // bit width of type {{stage_input.type.vec_size}}u, // vec size @@ -82,6 +84,8 @@ struct {{camel_case(shader_name)}}{{camel_case(shader_stage)}}Info { static constexpr auto kInput{{camel_case(sampled_image.name)}} = ShaderStageIOSlot { // {{sampled_image.name}} "{{sampled_image.name}}", // name {{sampled_image.location}}u, // attribute location + {{sampled_image.descriptor_set}}u, // attribute set + {{sampled_image.binding}}u, // attribute binding {{sampled_image.type.type_name}}, // type {{sampled_image.type.bit_width}}u, // bit width of type {{sampled_image.type.vec_size}}u, // vec size @@ -97,6 +101,8 @@ struct {{camel_case(shader_name)}}{{camel_case(shader_stage)}}Info { static constexpr auto kOutput{{camel_case(stage_output.name)}} = ShaderStageIOSlot { // {{stage_output.name}} "{{stage_output.name}}", // name {{stage_output.location}}u, // attribute location + {{stage_output.descriptor_set}}u, // attribute set + {{stage_output.binding}}u, // attribute binding {{stage_output.type.type_name}}, // type {{stage_output.type.bit_width}}u, // bit width of type {{stage_output.type.vec_size}}u, // vec size diff --git a/impeller/entity/entity_renderer.mm b/impeller/entity/entity_renderer.mm index b9d4af5e71839..08c55bb56defa 100644 --- a/impeller/entity/entity_renderer.mm +++ b/impeller/entity/entity_renderer.mm @@ -33,13 +33,13 @@ VertexBufferBuilder vertex_builder; vertex_builder.SetLabel("Box"); vertex_builder.AddVertices({ - {{100, 100, 0.0}, {Color::Red()}}, // 1 - {{800, 100, 0.0}, {Color::Green()}}, // 2 - {{800, 800, 0.0}, {Color::Blue()}}, // 3 + {{100, 100, 0.0}, {Color::Red()}, {0.0, 0.0}}, // 1 + {{800, 100, 0.0}, {Color::Green()}, {1.0, 0.0}}, // 2 + {{800, 800, 0.0}, {Color::Blue()}, {1.0, 1.0}}, // 3 - {{100, 100, 0.0}, {Color::Cyan()}}, // 1 - {{800, 800, 0.0}, {Color::White()}}, // 3 - {{100, 800, 0.0}, {Color::Purple()}}, // 4 + {{100, 100, 0.0}, {Color::Cyan()}, {0.0, 0.0}}, // 1 + {{800, 800, 0.0}, {Color::White()}, {1.0, 1.0}}, // 3 + {{100, 800, 0.0}, {Color::Purple()}, {0.0, 1.0}}, // 4 }); vertex_buffer_ = @@ -76,13 +76,13 @@ pass.GetTransientsBuffer().EmplaceUniform(uniforms); cmd.fragment_bindings - .samplers[shader::BoxFragmentInfo::kInputContents1.location] = + .samplers[shader::BoxFragmentInfo::kInputContents1.binding] = GetContext()->GetSamplerLibrary()->GetSampler({}); cmd.fragment_bindings - .samplers[shader::BoxFragmentInfo::kInputContents2.location] = + .samplers[shader::BoxFragmentInfo::kInputContents2.binding] = GetContext()->GetSamplerLibrary()->GetSampler({}); cmd.fragment_bindings - .samplers[shader::BoxFragmentInfo::kInputContents3.location] = + .samplers[shader::BoxFragmentInfo::kInputContents3.binding] = GetContext()->GetSamplerLibrary()->GetSampler({}); cmd.index_buffer = vertex_buffer_.index_buffer; diff --git a/impeller/shader_glue/shader_types.h b/impeller/shader_glue/shader_types.h index be9108aaa8bbd..d13ab4d2ce1bd 100644 --- a/impeller/shader_glue/shader_types.h +++ b/impeller/shader_glue/shader_types.h @@ -50,6 +50,8 @@ struct ShaderUniformSlot { struct ShaderStageIOSlot { const char* name; size_t location; + size_t set; + size_t binding; ShaderType type; size_t bit_width; size_t vec_size; From 2163276065e3e9f0baa0f4d144d65480b2ad9c55 Mon Sep 17 00:00:00 2001 From: Chinmay Garde Date: Thu, 1 Jul 2021 11:55:24 -0700 Subject: [PATCH 110/433] Cleanup test targets. --- impeller/BUILD.gn | 1 + impeller/compiler/BUILD.gn | 15 ++---- impeller/compiler/compiler_unittests.cc | 53 +++++++++----------- impeller/compiler/fixtures/sample.frag | 18 ------- impeller/fixtures/BUILD.gn | 13 +++++ impeller/{compiler => }/fixtures/sample.vert | 0 impeller/{compiler => }/fixtures/types.h | 0 7 files changed, 42 insertions(+), 58 deletions(-) delete mode 100644 impeller/compiler/fixtures/sample.frag create mode 100644 impeller/fixtures/BUILD.gn rename impeller/{compiler => }/fixtures/sample.vert (100%) rename impeller/{compiler => }/fixtures/types.h (100%) diff --git a/impeller/BUILD.gn b/impeller/BUILD.gn index 31d5db29906e8..01e77b316d3b1 100644 --- a/impeller/BUILD.gn +++ b/impeller/BUILD.gn @@ -26,6 +26,7 @@ executable("impeller_unittests") { deps = [ "compiler:compiler_unittests", "compositor:compositor_unittests", + "fixtures", "//flutter/testing", ] } diff --git a/impeller/compiler/BUILD.gn b/impeller/compiler/BUILD.gn index f60c6e12de1f3..9b13cf0a76c93 100644 --- a/impeller/compiler/BUILD.gn +++ b/impeller/compiler/BUILD.gn @@ -2,9 +2,9 @@ # Use of this source code is governed by a BSD-style license that can be # found in the LICENSE file. -import("//flutter/testing/testing.gni") +import("//flutter/impeller/tools/impeller.gni") -source_set("compiler_lib") { +impeller_component("compiler_lib") { sources = [ "code_gen_template.h", "compiler.cc", @@ -40,15 +40,7 @@ group("compiler") { deps = [ ":impellerc" ] } -test_fixtures("compiler_test_fixtures") { - fixtures = [ - "fixtures/sample.frag", - "fixtures/sample.vert", - "fixtures/types.h", - ] -} - -source_set("compiler_unittests") { +impeller_component("compiler_unittests") { testonly = true output_name = "impellerc_unittests" @@ -57,7 +49,6 @@ source_set("compiler_unittests") { deps = [ ":compiler_lib", - ":compiler_test_fixtures", "//flutter/testing:testing_lib", ] } diff --git a/impeller/compiler/compiler_unittests.cc b/impeller/compiler/compiler_unittests.cc index a5782923789de..bdaabc4e76f33 100644 --- a/impeller/compiler/compiler_unittests.cc +++ b/impeller/compiler/compiler_unittests.cc @@ -5,6 +5,7 @@ #include "flutter/impeller/compiler/compiler.h" #include "flutter/testing/testing.h" #include "gtest/gtest.h" +#include "third_party/inja/include/inja/inja.hpp" namespace impeller { namespace compiler { @@ -13,27 +14,35 @@ namespace testing { class CompilerTest : public ::testing::Test { public: CompilerTest() - : directory_(fml::OpenDirectory("/Users/chinmaygarde/Desktop/shaders", - false, - fml::FilePermission::kRead)) { - FML_CHECK(directory_.is_valid()); + : intermediates_directory_( + fml::OpenDirectory(flutter::testing::GetFixturesPath(), + false, + fml::FilePermission::kRead)) { + FML_CHECK(intermediates_directory_.is_valid()); } - ~CompilerTest() {} + ~CompilerTest() = default; - void WriteCompilerIntermediates(const Compiler& compiler, - const std::string& base_name) { - ASSERT_TRUE(compiler.IsValid()); - fml::WriteAtomically(directory_, - std::string{base_name + std::string{".spirv"}}.c_str(), - *compiler.GetSPIRVAssembly()); - fml::WriteAtomically(directory_, - std::string{base_name + std::string{".metal"}}.c_str(), - *compiler.GetMSLShaderSource()); + bool CanCompileFixture(const char* fixture_name) const { + auto fixture = flutter::testing::OpenFixtureAsMapping(fixture_name); + if (!fixture->GetMapping()) { + FML_LOG(ERROR) << "Could not find shader in fixtures: " << fixture_name; + return false; + } + Compiler::SourceOptions compiler_options(fixture_name); + compiler_options.working_directory = std::make_shared( + flutter::testing::OpenFixturesDirectory()); + Reflector::Options reflector_options; + Compiler compiler(*fixture.get(), compiler_options, reflector_options); + if (!compiler.IsValid()) { + FML_LOG(ERROR) << "Compilation failed: " << compiler.GetErrorMessages(); + return false; + } + return true; } private: - fml::UniqueFD directory_; + fml::UniqueFD intermediates_directory_; FML_DISALLOW_COPY_AND_ASSIGN(CompilerTest); }; @@ -50,19 +59,7 @@ TEST_F(CompilerTest, ShaderKindMatchingIsSuccessful) { } TEST_F(CompilerTest, CanCompileSample) { - constexpr const char* kShaderFixtureName = "sample.vert"; - auto fixture = flutter::testing::OpenFixtureAsMapping(kShaderFixtureName); - ASSERT_NE(fixture->GetMapping(), nullptr); - Compiler::SourceOptions compiler_options(kShaderFixtureName); - compiler_options.working_directory = std::make_shared( - flutter::testing::OpenFixturesDirectory()); - Reflector::Options reflector_options; - Compiler compiler(*fixture.get(), compiler_options, reflector_options); - if (!compiler.IsValid()) { - FML_LOG(ERROR) << compiler.GetErrorMessages(); - } - ASSERT_TRUE(compiler.IsValid()); - WriteCompilerIntermediates(compiler, kShaderFixtureName); + ASSERT_TRUE(CanCompileFixture("sample.vert")); } } // namespace testing diff --git a/impeller/compiler/fixtures/sample.frag b/impeller/compiler/fixtures/sample.frag deleted file mode 100644 index eef7915f18e76..0000000000000 --- a/impeller/compiler/fixtures/sample.frag +++ /dev/null @@ -1,18 +0,0 @@ - -#include "types.h" - -uniform Uniforms { - Hello myHello; - vec4 hi; - Hello goodbyeHello; -}; - -uniform sampler2D textureSampler; - -layout(location = 3) in vec2 inTextureCoord; - -layout(location = 4) out vec4 outColor; - -void main() { - outColor = texture(textureSampler, inTextureCoord * goodbyeHello.yet_more_stuff); -} diff --git a/impeller/fixtures/BUILD.gn b/impeller/fixtures/BUILD.gn new file mode 100644 index 0000000000000..519515d5141f2 --- /dev/null +++ b/impeller/fixtures/BUILD.gn @@ -0,0 +1,13 @@ +# Copyright 2013 The Flutter Authors. All rights reserved. +# Use of this source code is governed by a BSD-style license that can be +# found in the LICENSE file. + +import("//flutter/impeller/tools/impeller.gni") +import("//flutter/testing/testing.gni") + +test_fixtures("fixtures") { + fixtures = [ + "sample.vert", + "types.h", + ] +} diff --git a/impeller/compiler/fixtures/sample.vert b/impeller/fixtures/sample.vert similarity index 100% rename from impeller/compiler/fixtures/sample.vert rename to impeller/fixtures/sample.vert diff --git a/impeller/compiler/fixtures/types.h b/impeller/fixtures/types.h similarity index 100% rename from impeller/compiler/fixtures/types.h rename to impeller/fixtures/types.h From 26ff7078c20d85002398ed5f1ea92b46c374edbc Mon Sep 17 00:00:00 2001 From: Chinmay Garde Date: Fri, 2 Jul 2021 15:47:09 -0700 Subject: [PATCH 111/433] Wire up playground WSI. --- impeller/BUILD.gn | 3 + impeller/compiler/compiler_unittests.cc | 1 - impeller/compositor/context.h | 2 + impeller/compositor/context.mm | 23 ++++- impeller/compositor/shader_library.mm | 4 + impeller/compositor/surface.mm | 2 +- impeller/entity/BUILD.gn | 8 ++ impeller/entity/entity_unittests.mm | 16 +++ impeller/fixtures/BUILD.gn | 1 + impeller/fixtures/image.png | Bin 0 -> 514968 bytes impeller/playground/BUILD.gn | 27 +++++ impeller/playground/playground.h | 23 +++++ impeller/playground/playground.mm | 103 ++++++++++++++++++++ impeller/primitives/BUILD.gn | 4 + impeller/primitives/primitives_unittests.mm | 3 + 15 files changed, 214 insertions(+), 6 deletions(-) create mode 100644 impeller/entity/entity_unittests.mm create mode 100644 impeller/fixtures/image.png create mode 100644 impeller/playground/BUILD.gn create mode 100644 impeller/playground/playground.h create mode 100644 impeller/playground/playground.mm create mode 100644 impeller/primitives/primitives_unittests.mm diff --git a/impeller/BUILD.gn b/impeller/BUILD.gn index 01e77b316d3b1..4a46e0e8a6d1b 100644 --- a/impeller/BUILD.gn +++ b/impeller/BUILD.gn @@ -26,7 +26,10 @@ executable("impeller_unittests") { deps = [ "compiler:compiler_unittests", "compositor:compositor_unittests", + "entity:entity_unittests", "fixtures", + "playground", + "primitives:primitives_unittests", "//flutter/testing", ] } diff --git a/impeller/compiler/compiler_unittests.cc b/impeller/compiler/compiler_unittests.cc index bdaabc4e76f33..5c97bfc45a67a 100644 --- a/impeller/compiler/compiler_unittests.cc +++ b/impeller/compiler/compiler_unittests.cc @@ -5,7 +5,6 @@ #include "flutter/impeller/compiler/compiler.h" #include "flutter/testing/testing.h" #include "gtest/gtest.h" -#include "third_party/inja/include/inja/inja.hpp" namespace impeller { namespace compiler { diff --git a/impeller/compositor/context.h b/impeller/compositor/context.h index 19200cfcc7816..66e13b7cdabc7 100644 --- a/impeller/compositor/context.h +++ b/impeller/compositor/context.h @@ -46,6 +46,8 @@ class Context { std::shared_ptr CreateRenderCommandBuffer() const; + id GetMTLDevice() const; + private: id device_ = nullptr; id render_queue_ = nullptr; diff --git a/impeller/compositor/context.mm b/impeller/compositor/context.mm index f02e99f8da639..fd93e198a4789 100644 --- a/impeller/compositor/context.mm +++ b/impeller/compositor/context.mm @@ -4,6 +4,7 @@ #include "context.h" +#include "flutter/fml/file.h" #include "flutter/fml/logging.h" #include "flutter/fml/paths.h" #include "impeller/compositor/allocator.h" @@ -36,10 +37,20 @@ NSError* shader_library_error = nil; auto shader_library_path = fml::paths::JoinPaths({shaders_directory, "impeller.metallib"}); - id library = - [device_ newLibraryWithFile:@(shader_library_path.c_str()) - error:&shader_library_error]; - if (!library) { + + auto library_exists = fml::IsFile(shader_library_path); + + if (!library_exists) { + FML_LOG(ERROR) << "Shader library does not exist at path '" + << shader_library_path + << "'. No piplines can be created in this context."; + } + auto library = + library_exists + ? [device_ newLibraryWithFile:@(shader_library_path.c_str()) + error:&shader_library_error] + : [device_ newDefaultLibrary]; + if (!library && shader_library_error) { FML_LOG(ERROR) << "Could not create shader library: " << shader_library_error.localizedDescription.UTF8String; return; @@ -118,4 +129,8 @@ return transients_allocator_; } +id Context::GetMTLDevice() const { + return device_; +} + } // namespace impeller diff --git a/impeller/compositor/shader_library.mm b/impeller/compositor/shader_library.mm index a7009934bddab..65db43c6ea22a 100644 --- a/impeller/compositor/shader_library.mm +++ b/impeller/compositor/shader_library.mm @@ -13,6 +13,10 @@ std::shared_ptr ShaderLibrary::GetFunction( const std::string_view& name, ShaderStage stage) { + if (!library_) { + return nullptr; + } + ShaderKey key(name, stage); if (auto found = functions_.find(key); found != functions_.end()) { diff --git a/impeller/compositor/surface.mm b/impeller/compositor/surface.mm index 40f57d9e00cb2..a872e8a467fc0 100644 --- a/impeller/compositor/surface.mm +++ b/impeller/compositor/surface.mm @@ -30,7 +30,7 @@ auto callback = present_callback_; if (!callback) { - return true; + return false; } return callback(); diff --git a/impeller/entity/BUILD.gn b/impeller/entity/BUILD.gn index 66e2b7a3db158..c3fa607152a7b 100644 --- a/impeller/entity/BUILD.gn +++ b/impeller/entity/BUILD.gn @@ -18,3 +18,11 @@ impeller_component("entity") { "../primitives", ] } + +impeller_component("entity_unittests") { + testonly = true + + sources = [ "entity_unittests.mm" ] + + deps = [ "../playground" ] +} diff --git a/impeller/entity/entity_unittests.mm b/impeller/entity/entity_unittests.mm new file mode 100644 index 0000000000000..b53534d35e8d1 --- /dev/null +++ b/impeller/entity/entity_unittests.mm @@ -0,0 +1,16 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "flutter/testing/testing.h" +#include "impeller/playground/playground.h" + +namespace impeller { +namespace testing { + +TEST_F(Playground, CanCreateEntity) { + OpenPlaygroundHere([]() { return true; }); +} + +} // namespace testing +} // namespace impeller diff --git a/impeller/fixtures/BUILD.gn b/impeller/fixtures/BUILD.gn index 519515d5141f2..cd2fc9ad088a0 100644 --- a/impeller/fixtures/BUILD.gn +++ b/impeller/fixtures/BUILD.gn @@ -9,5 +9,6 @@ test_fixtures("fixtures") { fixtures = [ "sample.vert", "types.h", + "image.png", ] } diff --git a/impeller/fixtures/image.png b/impeller/fixtures/image.png new file mode 100644 index 0000000000000000000000000000000000000000..6937b1541f1eac929ae61fabdd6df0533904f7e0 GIT binary patch literal 514968 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

-!~H~_HE}U~#h!KLP$od(gG85i!`;$4 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

-yP4E_%@QRXaH9q+bL;@@lrb?_V^7}8HZ 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 W#|S4TIG+mu0000 closure); + + private: + FML_DISALLOW_COPY_AND_ASSIGN(Playground); +}; + +} // namespace impeller diff --git a/impeller/playground/playground.mm b/impeller/playground/playground.mm new file mode 100644 index 0000000000000..13e31fd2820ac --- /dev/null +++ b/impeller/playground/playground.mm @@ -0,0 +1,103 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "impeller/playground/playground.h" +#include "flutter/testing/testing.h" +#include "impeller/compositor/context.h" +#include "impeller/compositor/render_pass.h" +#include "impeller/compositor/surface.h" + +#define GLFW_INCLUDE_NONE +#import "third_party/glfw/include/GLFW/glfw3.h" +#define GLFW_EXPOSE_NATIVE_COCOA +#import "third_party/glfw/include/GLFW/glfw3native.h" + +#include +#include + +namespace impeller { + +Playground::Playground() = default; + +Playground::~Playground() = default; + +bool Playground::OpenPlaygroundHere(std::function closure) { + if (!closure) { + return true; + } + + Context context(flutter::testing::GetFixturesPath()); + + if (!context.IsValid()) { + return false; + } + + if (::glfwInit() != GLFW_TRUE) { + return false; + } + fml::ScopedCleanupClosure terminate([]() { ::glfwTerminate(); }); + + ::glfwWindowHint(GLFW_CLIENT_API, GLFW_NO_API); + + auto window = ::glfwCreateWindow(800, 600, "Impeller Playground", NULL, NULL); + if (!window) { + return false; + } + + fml::ScopedCleanupClosure close_window( + [window]() { ::glfwDestroyWindow(window); }); + + NSWindow* cocoa_window = ::glfwGetCocoaWindow(window); + CAMetalLayer* layer = [CAMetalLayer layer]; + layer.device = context.GetMTLDevice(); + layer.pixelFormat = MTLPixelFormatBGRA8Unorm; + cocoa_window.contentView.layer = layer; + cocoa_window.contentView.wantsLayer = YES; + + while (true) { + ::glfwWaitEventsTimeout(1.0 / 30.0); + + if (::glfwWindowShouldClose(window)) { + return true; + } + + auto current_drawable = [layer nextDrawable]; + + if (!current_drawable) { + FML_LOG(ERROR) << "Could not acquire current drawable."; + return false; + } + + ColorRenderPassAttachment color0; + color0.texture = std::make_shared(current_drawable.texture); + color0.clear_color = Color::SkyBlue(); + color0.load_action = LoadAction::kClear; + color0.store_action = StoreAction::kStore; + + RenderPassDescriptor desc; + desc.SetColorAttachment(color0, 0u); + + Surface surface(desc, [current_drawable, closure]() { + if (!closure()) { + return false; + } + [current_drawable present]; + return true; + }); + + if (!surface.IsValid()) { + FML_LOG(ERROR) << "Could not wrap surface to render to into."; + return false; + } + + if (!surface.Present()) { + FML_LOG(ERROR) << "Could not render into playground surface."; + return false; + } + } + + return true; +} + +} // namespace impeller diff --git a/impeller/primitives/BUILD.gn b/impeller/primitives/BUILD.gn index 343891557640a..07a3d2e3a901c 100644 --- a/impeller/primitives/BUILD.gn +++ b/impeller/primitives/BUILD.gn @@ -25,3 +25,7 @@ impeller_component("primitives") { "../compositor", ] } + +impeller_component("primitives_unittests") { + sources = [ "primitives_unittests.mm" ] +} diff --git a/impeller/primitives/primitives_unittests.mm b/impeller/primitives/primitives_unittests.mm new file mode 100644 index 0000000000000..e7217c7d7b3fa --- /dev/null +++ b/impeller/primitives/primitives_unittests.mm @@ -0,0 +1,3 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. From f7900609ca3037603f35a255f7daf344f85fa6aa Mon Sep 17 00:00:00 2001 From: Chinmay Garde Date: Fri, 2 Jul 2021 16:00:21 -0700 Subject: [PATCH 112/433] Key callbacks to quit from a playground. --- impeller/playground/playground.mm | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/impeller/playground/playground.mm b/impeller/playground/playground.mm index 13e31fd2820ac..b87d3a4c9fdb0 100644 --- a/impeller/playground/playground.mm +++ b/impeller/playground/playground.mm @@ -22,6 +22,16 @@ Playground::~Playground() = default; +static void PlaygroundKeyCallback(GLFWwindow* window, + int key, + int scancode, + int action, + int mods) { + if ((key == GLFW_KEY_ESCAPE || key == GLFW_KEY_Q) && action == GLFW_RELEASE) { + ::glfwSetWindowShouldClose(window, GLFW_TRUE); + } +} + bool Playground::OpenPlaygroundHere(std::function closure) { if (!closure) { return true; @@ -45,6 +55,9 @@ return false; } + ::glfwSetWindowUserPointer(window, this); + ::glfwSetKeyCallback(window, &PlaygroundKeyCallback); + fml::ScopedCleanupClosure close_window( [window]() { ::glfwDestroyWindow(window); }); From 5dbd0e8c4bdd9c62de629bd58c9e2e4e37ca573f Mon Sep 17 00:00:00 2001 From: Chinmay Garde Date: Sat, 3 Jul 2021 00:07:56 -0700 Subject: [PATCH 113/433] Update render pass default. --- impeller/compositor/render_pass.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/impeller/compositor/render_pass.h b/impeller/compositor/render_pass.h index ef31b70865a35..7c0a089760e6b 100644 --- a/impeller/compositor/render_pass.h +++ b/impeller/compositor/render_pass.h @@ -24,7 +24,7 @@ class CommandBuffer; struct RenderPassAttachment { std::shared_ptr texture; LoadAction load_action = LoadAction::kDontCare; - StoreAction store_action = StoreAction::kDontCare; + StoreAction store_action = StoreAction::kStore; constexpr operator bool() const { return static_cast(texture); } }; From cb88c66c8310747b9f2f60f510db617edd8d0977 Mon Sep 17 00:00:00 2001 From: Chinmay Garde Date: Sat, 3 Jul 2021 00:57:56 -0700 Subject: [PATCH 114/433] A pipeline builder utility to create pipeline state objects from reflected shader information. --- impeller/BUILD.gn | 1 - impeller/compiler/code_gen_template.h | 21 +- impeller/compositor/BUILD.gn | 2 + impeller/compositor/pipeline_builder.h | 107 ++++++ .../pipeline_builder.mm} | 6 +- impeller/compositor/pipeline_descriptor.h | 2 +- impeller/compositor/vertex_descriptor.h | 5 +- impeller/compositor/vertex_descriptor.mm | 8 +- impeller/entity/BUILD.gn | 5 +- impeller/entity/entity_renderer.mm | 19 +- impeller/host/BUILD.gn | 53 --- impeller/host/ShaderTypes.h | 39 -- impeller/host/Shaders.metal | 50 --- impeller/host/assets/ColorMap.png | Bin 37539 -> 0 bytes impeller/host/assets_location.cc | 31 -- impeller/host/impeller_host_view_controller.h | 12 - .../host/impeller_host_view_controller.mm | 35 -- impeller/host/impeller_renderer.h | 11 - impeller/host/impeller_renderer.mm | 353 ------------------ impeller/host/main.mm | 62 --- impeller/host/shaders_location.cc | 31 -- impeller/host/shaders_location.h | 14 - impeller/playground/BUILD.gn | 1 + impeller/primitives/box_primitive.mm | 53 +-- 24 files changed, 144 insertions(+), 777 deletions(-) create mode 100644 impeller/compositor/pipeline_builder.h rename impeller/{host/assets_location.h => compositor/pipeline_builder.mm} (65%) delete mode 100644 impeller/host/BUILD.gn delete mode 100644 impeller/host/ShaderTypes.h delete mode 100644 impeller/host/Shaders.metal delete mode 100644 impeller/host/assets/ColorMap.png delete mode 100644 impeller/host/assets_location.cc delete mode 100644 impeller/host/impeller_host_view_controller.h delete mode 100644 impeller/host/impeller_host_view_controller.mm delete mode 100644 impeller/host/impeller_renderer.h delete mode 100644 impeller/host/impeller_renderer.mm delete mode 100644 impeller/host/main.mm delete mode 100644 impeller/host/shaders_location.cc delete mode 100644 impeller/host/shaders_location.h diff --git a/impeller/BUILD.gn b/impeller/BUILD.gn index 4a46e0e8a6d1b..c7aeb4641fdef 100644 --- a/impeller/BUILD.gn +++ b/impeller/BUILD.gn @@ -13,7 +13,6 @@ group("impeller") { "compositor", "entity", "geometry", - "host", "image", "primitives", "shader_glue", diff --git a/impeller/compiler/code_gen_template.h b/impeller/compiler/code_gen_template.h index 3edca285eb800..a93b8fd3bdc4d 100644 --- a/impeller/compiler/code_gen_template.h +++ b/impeller/compiler/code_gen_template.h @@ -16,9 +16,8 @@ constexpr std::string_view kReflectionHeaderTemplate = #include "shader_types.h" namespace impeller { -namespace shader { -struct {{camel_case(shader_name)}}{{camel_case(shader_stage)}}Info { +struct {{camel_case(shader_name)}}{{camel_case(shader_stage)}}Shader { // =========================================================================== // Stage Info ================================================================ // =========================================================================== @@ -116,10 +115,9 @@ struct {{camel_case(shader_name)}}{{camel_case(shader_stage)}}Info { }; {% endif %} -}; // struct {{camel_case(shader_name)}}{{camel_case(shader_stage)}}Info +}; // struct {{camel_case(shader_name)}}{{camel_case(shader_stage)}}Shader -} // namespace shader -} // namespace impeller +} // namespace impeller )~~"; constexpr std::string_view kReflectionCCTemplate = @@ -131,22 +129,19 @@ constexpr std::string_view kReflectionCCTemplate = #include namespace impeller { -namespace shader { -using Info = {{camel_case(shader_name)}}{{camel_case(shader_stage)}}Info; +using Shader = {{camel_case(shader_name)}}{{camel_case(shader_stage)}}Shader; {% for def in struct_definitions %} // Sanity checks for {{def.name}} -static_assert(std::is_standard_layout_v); -static_assert(sizeof(Info::{{def.name}}) == {{def.byte_length}}); +static_assert(std::is_standard_layout_v); +static_assert(sizeof(Shader::{{def.name}}) == {{def.byte_length}}); {% for member in def.members %} -static_assert(offsetof(Info::{{def.name}}, {{member.name}}) == {{member.offset}}); +static_assert(offsetof(Shader::{{def.name}}, {{member.name}}) == {{member.offset}}); {% endfor %} {% endfor %} - -} // namespace shader -} // namespace impeller +} // namespace impeller )~~"; } // namespace compiler diff --git a/impeller/compositor/BUILD.gn b/impeller/compositor/BUILD.gn index 3e2eec64d6db7..310417449052d 100644 --- a/impeller/compositor/BUILD.gn +++ b/impeller/compositor/BUILD.gn @@ -30,6 +30,8 @@ impeller_component("compositor") { "host_buffer.mm", "pipeline.h", "pipeline.mm", + "pipeline_builder.h", + "pipeline_builder.mm", "pipeline_descriptor.h", "pipeline_descriptor.mm", "pipeline_library.h", diff --git a/impeller/compositor/pipeline_builder.h b/impeller/compositor/pipeline_builder.h new file mode 100644 index 0000000000000..a5249c09ec7b9 --- /dev/null +++ b/impeller/compositor/pipeline_builder.h @@ -0,0 +1,107 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#pragma once + +#include "flutter/fml/logging.h" +#include "flutter/fml/macros.h" +#include "impeller/compositor/context.h" +#include "impeller/compositor/pipeline_descriptor.h" +#include "impeller/compositor/shader_library.h" +#include "impeller/compositor/vertex_descriptor.h" + +namespace impeller { + +//------------------------------------------------------------------------------ +/// @brief A utility for creating pipelines from reflected shader +/// information. +/// +/// @tparam VertexShader_ The reflected vertex shader information. Found +/// in a generated header file called +/// .vert.h. +/// @tparam FragmentShader_ The reflected fragment shader information. +/// Found in a generated header file called +/// .frag.h. +/// +template +struct PipelineBuilder { + public: + using VertexShader = VertexShader_; + using FragmentShader = FragmentShader_; + + static constexpr size_t kVertexBufferIndex = + VertexDescriptor::kReservedVertexBufferIndex; + + //---------------------------------------------------------------------------- + /// @brief Create a default pipeline descriptor using the combination + /// reflected shader information. The descriptor can be configured + /// further before a pipline state object is created using it. + /// + /// @param[in] context The context + /// + /// @return If the combination of reflected shader information is + /// compatible and the requisite functions can be found in the + /// context, a pipeine descriptor. + /// + static std::optional MakeDefaultPipelineDescriptor( + const Context& context) { + PipelineDescriptor desc; + + // Setup debug instrumentation. + desc.SetLabel(SPrintF("%s Pipeline", VertexShader::kLabel.data())); + + // Resolve pipeline entrypoints. + { + auto vertex_function = context.GetShaderLibrary()->GetFunction( + VertexShader::kEntrypointName, ShaderStage::kVertex); + auto fragment_function = context.GetShaderLibrary()->GetFunction( + FragmentShader::kEntrypointName, ShaderStage::kFragment); + + if (!vertex_function || !fragment_function) { + FML_LOG(ERROR) << "Could not resolve pipeline entrypoint(s)."; + return std::nullopt; + } + + desc.AddStageEntrypoint(std::move(vertex_function)); + desc.AddStageEntrypoint(std::move(fragment_function)); + } + + // Setup the vertex descriptor from reflected information. + { + auto vertex_descriptor = std::make_shared(); + if (!vertex_descriptor->SetStageInputs( + VertexShader::kAllShaderStageInputs)) { + FML_LOG(ERROR) << "Could not configure vertex descriptor."; + return std::nullopt; + } + desc.SetVertexDescriptor(std::move(vertex_descriptor)); + } + + // Setup fragment shader output descriptions. + { + // Configure the sole color attachments pixel format. + // TODO(csg): This can be easily reflected but we are sticking to the + // convention that the first stage output is the color output. + ColorAttachmentDescriptor color0; + color0.format = PixelFormat::kPixelFormat_B8G8R8A8_UNormInt_SRGB; + desc.SetColorAttachmentDescriptor(0u, std::move(color0)); + } + + // Setup depth and stencil attachment descriptions. + { + // Configure the stencil attachment. + // TODO(csg): Make this configurable if possible as the D32 component is + // wasted. This can even be moved out of the "default" descriptor + // construction as a case can be made that this is caller responsibility. + const auto combined_depth_stencil_format = + PixelFormat::kPixelFormat_D32_Float_S8_UNormInt; + desc.SetDepthPixelFormat(combined_depth_stencil_format); + desc.SetStencilPixelFormat(combined_depth_stencil_format); + } + + return desc; + } +}; + +} // namespace impeller diff --git a/impeller/host/assets_location.h b/impeller/compositor/pipeline_builder.mm similarity index 65% rename from impeller/host/assets_location.h rename to impeller/compositor/pipeline_builder.mm index ea139a5a6d601..dbea9cc563015 100644 --- a/impeller/host/assets_location.h +++ b/impeller/compositor/pipeline_builder.mm @@ -2,10 +2,10 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -#include +#include "impeller/compositor/pipeline_builder.h" namespace impeller { -std::string GetAssetLocation(const char* asset_path); +// -} // namespace impeller +} // namespace impeller diff --git a/impeller/compositor/pipeline_descriptor.h b/impeller/compositor/pipeline_descriptor.h index 295c3751c1296..1d6a1d2e01553 100644 --- a/impeller/compositor/pipeline_descriptor.h +++ b/impeller/compositor/pipeline_descriptor.h @@ -24,7 +24,7 @@ namespace impeller { class ShaderFunction; class VertexDescriptor; -class PipelineDescriptor : public Comparable { +class PipelineDescriptor final : public Comparable { public: PipelineDescriptor(); diff --git a/impeller/compositor/vertex_descriptor.h b/impeller/compositor/vertex_descriptor.h index f05885d403ff4..d6c97de75e69e 100644 --- a/impeller/compositor/vertex_descriptor.h +++ b/impeller/compositor/vertex_descriptor.h @@ -16,6 +16,9 @@ namespace impeller { class VertexDescriptor final : public Comparable { public: + static constexpr size_t kReservedVertexBufferIndex = + 30u; // The final slot available. Regular buffer indices go up from 0. + VertexDescriptor(); virtual ~VertexDescriptor(); @@ -31,8 +34,6 @@ class VertexDescriptor final : public Comparable { MTLVertexDescriptor* GetMTLVertexDescriptor() const; - size_t GetVertexBufferIndex() const; - //| Comparable| std::size_t GetHash() const override; diff --git a/impeller/compositor/vertex_descriptor.mm b/impeller/compositor/vertex_descriptor.mm index 3ab58eab7d01f..9707db0e7eab5 100644 --- a/impeller/compositor/vertex_descriptor.mm +++ b/impeller/compositor/vertex_descriptor.mm @@ -188,16 +188,10 @@ static MTLVertexFormat ReadStageInputFormat(const ShaderStageIOSlot& input) { return true; } -size_t VertexDescriptor::GetVertexBufferIndex() const { - static constexpr size_t kReservedVertexBufferIndex = - 30u; // The final slot available. - return kReservedVertexBufferIndex; -} - MTLVertexDescriptor* VertexDescriptor::GetMTLVertexDescriptor() const { auto descriptor = [MTLVertexDescriptor vertexDescriptor]; - const size_t vertex_buffer_index = GetVertexBufferIndex(); + const size_t vertex_buffer_index = kReservedVertexBufferIndex; size_t stride = 0u; for (const auto& input : stage_inputs_) { diff --git a/impeller/entity/BUILD.gn b/impeller/entity/BUILD.gn index c3fa607152a7b..bd19c0ed5ab75 100644 --- a/impeller/entity/BUILD.gn +++ b/impeller/entity/BUILD.gn @@ -24,5 +24,8 @@ impeller_component("entity_unittests") { sources = [ "entity_unittests.mm" ] - deps = [ "../playground" ] + deps = [ + ":entity", + "../playground", + ] } diff --git a/impeller/entity/entity_renderer.mm b/impeller/entity/entity_renderer.mm index 08c55bb56defa..22878ab8781e0 100644 --- a/impeller/entity/entity_renderer.mm +++ b/impeller/entity/entity_renderer.mm @@ -30,7 +30,7 @@ return; } - VertexBufferBuilder vertex_builder; + VertexBufferBuilder vertex_builder; vertex_builder.SetLabel("Box"); vertex_builder.AddVertices({ {{100, 100, 0.0}, {Color::Red()}, {0.0, 0.0}}, // 1 @@ -61,30 +61,23 @@ bool EntityRenderer::OnRender(const Surface& surface, RenderPass& pass) { pass.SetLabel("EntityRenderer Render Pass"); - shader::BoxVertexInfo::UniformBuffer uniforms; + BoxVertexShader::UniformBuffer uniforms; uniforms.mvp = Matrix::MakeOrthographic(surface.GetSize()); Command cmd; cmd.label = "Box"; cmd.pipeline = box_primitive_->GetPipeline(); - cmd.vertex_bindings.buffers[box_primitive_->GetVertexBufferIndex()] = vertex_buffer_.vertex_buffer; - cmd.vertex_bindings - .buffers[shader::BoxVertexInfo::kUniformUniformBuffer.binding] = + cmd.vertex_bindings.buffers[BoxVertexShader::kUniformUniformBuffer.binding] = pass.GetTransientsBuffer().EmplaceUniform(uniforms); - - cmd.fragment_bindings - .samplers[shader::BoxFragmentInfo::kInputContents1.binding] = + cmd.fragment_bindings.samplers[BoxFragmentShader::kInputContents1.binding] = GetContext()->GetSamplerLibrary()->GetSampler({}); - cmd.fragment_bindings - .samplers[shader::BoxFragmentInfo::kInputContents2.binding] = + cmd.fragment_bindings.samplers[BoxFragmentShader::kInputContents2.binding] = GetContext()->GetSamplerLibrary()->GetSampler({}); - cmd.fragment_bindings - .samplers[shader::BoxFragmentInfo::kInputContents3.binding] = + cmd.fragment_bindings.samplers[BoxFragmentShader::kInputContents3.binding] = GetContext()->GetSamplerLibrary()->GetSampler({}); - cmd.index_buffer = vertex_buffer_.index_buffer; cmd.index_count = vertex_buffer_.index_count; cmd.primitive_type = PrimitiveType::kTriange; diff --git a/impeller/host/BUILD.gn b/impeller/host/BUILD.gn deleted file mode 100644 index dcde833a5897b..0000000000000 --- a/impeller/host/BUILD.gn +++ /dev/null @@ -1,53 +0,0 @@ -# Copyright 2013 The Flutter Authors. All rights reserved. -# Use of this source code is governed by a BSD-style license that can be -# found in the LICENSE file. - -import("//flutter/impeller/tools/impeller.gni") -import("//flutter/testing/testing.gni") - -metal_library("impeller_host_shaders") { - name = "impeller_host" - sources = [ "shaders.metal" ] -} - -test_fixtures("impeller_host_fixtures") { - fixtures = [ "assets/ColorMap.png" ] -} - -executable("host") { - cflags_objc = flutter_cflags_objc_arc - cflags_objcc = flutter_cflags_objcc_arc - - cflags_objcc += [ "-Wno-auto-var-id" ] - - output_name = "impeller" - - sources = [ - "ShaderTypes.h", - "assets_location.cc", - "assets_location.h", - "impeller_host_view_controller.h", - "impeller_host_view_controller.mm", - "impeller_renderer.h", - "impeller_renderer.mm", - "main.mm", - "shaders_location.cc", - "shaders_location.h", - ] - - libs = [ - "AppKit.framework", - "Metal.framework", - "MetalKit.framework", - "QuartzCore.framework", - "ModelIO.framework", - ] - - deps = [ - ":impeller_host_fixtures", - ":impeller_host_shaders", - "../entity", - "../primitives", - "//flutter/fml", - ] -} diff --git a/impeller/host/ShaderTypes.h b/impeller/host/ShaderTypes.h deleted file mode 100644 index 34a71c46198c9..0000000000000 --- a/impeller/host/ShaderTypes.h +++ /dev/null @@ -1,39 +0,0 @@ -// Copyright 2013 The Flutter Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#ifndef ShaderTypes_h -#define ShaderTypes_h - -#ifdef __METAL_VERSION__ -#define NS_ENUM(_type, _name) \ - enum _name : _type _name; \ - enum _name : _type -#define NSInteger metal::int32_t -#else -#import -#endif - -#include - -typedef NS_ENUM(NSInteger, BufferIndex) { - BufferIndexMeshPositions = 0, - BufferIndexMeshGenerics = 1, - BufferIndexUniforms = 2 -}; - -typedef NS_ENUM(NSInteger, VertexAttribute) { - VertexAttributePosition = 0, - VertexAttributeTexcoord = 1, -}; - -typedef NS_ENUM(NSInteger, TextureIndex) { - TextureIndexColor = 0, -}; - -typedef struct { - matrix_float4x4 projectionMatrix; - matrix_float4x4 modelViewMatrix; -} Uniforms; - -#endif /* ShaderTypes_h */ diff --git a/impeller/host/Shaders.metal b/impeller/host/Shaders.metal deleted file mode 100644 index a2e0ac5eb139d..0000000000000 --- a/impeller/host/Shaders.metal +++ /dev/null @@ -1,50 +0,0 @@ -// Copyright 2013 The Flutter Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -// File for Metal kernel and shader functions - -#include -#include - -// Including header shared between this Metal shader code and Swift/C code executing Metal API commands -#import "ShaderTypes.h" - -using namespace metal; - -typedef struct -{ - float3 position [[attribute(VertexAttributePosition)]]; - float2 texCoord [[attribute(VertexAttributeTexcoord)]]; -} Vertex; - -typedef struct -{ - float4 position [[position]]; - float2 texCoord; -} ColorInOut; - -vertex ColorInOut vertexShader(Vertex in [[stage_in]], - constant Uniforms & uniforms [[ buffer(BufferIndexUniforms) ]]) -{ - ColorInOut out; - - float4 position = float4(in.position, 1.0); - out.position = uniforms.projectionMatrix * uniforms.modelViewMatrix * position; - out.texCoord = in.texCoord; - - return out; -} - -fragment float4 fragmentShader(ColorInOut in [[stage_in]], - constant Uniforms & uniforms [[ buffer(BufferIndexUniforms) ]], - texture2d colorMap [[ texture(TextureIndexColor) ]]) -{ - constexpr sampler colorSampler(mip_filter::linear, - mag_filter::linear, - min_filter::linear); - - half4 colorSample = colorMap.sample(colorSampler, in.texCoord.xy); - - return float4(colorSample); -} diff --git a/impeller/host/assets/ColorMap.png b/impeller/host/assets/ColorMap.png deleted file mode 100644 index ddf9519d4619fb02becb1157e8b8704d10a01547..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 37539 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 r$)xH}R>*L<@bp14E`X~(RdGy#_RQ<+j#?*o1V0 -#import -#import -#import "impeller_renderer.h" - -@interface ImpellerHostViewController : NSViewController - -@end diff --git a/impeller/host/impeller_host_view_controller.mm b/impeller/host/impeller_host_view_controller.mm deleted file mode 100644 index 188030b29e587..0000000000000 --- a/impeller/host/impeller_host_view_controller.mm +++ /dev/null @@ -1,35 +0,0 @@ -// Copyright 2013 The Flutter Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#import "impeller_host_view_controller.h" - -#import "impeller_renderer.h" - -@implementation ImpellerHostViewController { - MTKView* view_; - ImpellerRenderer* renderer_; -} - -- (void)loadView { - view_ = [[MTKView alloc] initWithFrame:NSMakeRect(0, 0, 800, 600)]; - view_.autoresizingMask = NSViewWidthSizable | NSViewHeightSizable; - self.view = view_; -} - -- (void)viewDidLoad { - [super viewDidLoad]; - view_.device = MTLCreateSystemDefaultDevice(); - - if (!view_.device) { - NSLog(@"Metal is not supported on this device"); - self.view = [[NSView alloc] initWithFrame:self.view.frame]; - return; - } - - renderer_ = [[ImpellerRenderer alloc] initWithMetalKitView:view_]; - [renderer_ mtkView:view_ drawableSizeWillChange:view_.bounds.size]; - view_.delegate = renderer_; -} - -@end diff --git a/impeller/host/impeller_renderer.h b/impeller/host/impeller_renderer.h deleted file mode 100644 index 09ea51284832c..0000000000000 --- a/impeller/host/impeller_renderer.h +++ /dev/null @@ -1,11 +0,0 @@ -// Copyright 2013 The Flutter Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#import - -@interface ImpellerRenderer : NSObject - -- (nonnull instancetype)initWithMetalKitView:(nonnull MTKView*)view; - -@end diff --git a/impeller/host/impeller_renderer.mm b/impeller/host/impeller_renderer.mm deleted file mode 100644 index a874a6f35c813..0000000000000 --- a/impeller/host/impeller_renderer.mm +++ /dev/null @@ -1,353 +0,0 @@ -// Copyright 2013 The Flutter Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#import -#import -#import "ShaderTypes.h" - -#include "assets_location.h" -#include "flutter/fml/logging.h" -#include "impeller/compositor/formats_metal.h" -#include "impeller/compositor/surface.h" -#include "impeller/entity/entity_renderer.h" -#include "impeller/primitives/box_primitive.h" -#include "impeller_renderer.h" -#include "shaders_location.h" - -static const NSUInteger kMaxBuffersInFlight = 3; - -static const size_t kAlignedUniformsSize = (sizeof(Uniforms) & ~0xFF) + 0x100; - -@implementation ImpellerRenderer { - dispatch_semaphore_t _inFlightSemaphore; - id _device; - id _commandQueue; - - id _dynamicUniformBuffer; - id _pipelineState; - id _depthState; - id _colorMap; - MTLVertexDescriptor* _mtlVertexDescriptor; - - uint32_t _uniformBufferOffset; - - uint8_t _uniformBufferIndex; - - void* _uniformBufferAddress; - - matrix_float4x4 _projectionMatrix; - - float _rotation; - - MTKMesh* _mesh; - std::unique_ptr renderer_; -} - -- (nonnull instancetype)initWithMetalKitView:(nonnull MTKView*)view { - self = [super init]; - if (self) { - _device = view.device; - _inFlightSemaphore = dispatch_semaphore_create(kMaxBuffersInFlight); - [self _loadMetalWithView:view]; - [self _loadAssets]; - } - - renderer_ = std::make_unique( - impeller::ImpellerShadersDirectory()); - FML_CHECK(renderer_->IsValid()) << "Impeller Renderer is not valid."; - return self; -} - -- (void)_loadMetalWithView:(nonnull MTKView*)view { - /// Load Metal state objects and initialize renderer dependent view properties - - view.depthStencilPixelFormat = MTLPixelFormatDepth32Float_Stencil8; - view.colorPixelFormat = MTLPixelFormatBGRA8Unorm_sRGB; - view.sampleCount = 1; - - _mtlVertexDescriptor = [[MTLVertexDescriptor alloc] init]; - - _mtlVertexDescriptor.attributes[VertexAttributePosition].format = - MTLVertexFormatFloat3; - _mtlVertexDescriptor.attributes[VertexAttributePosition].offset = 0; - _mtlVertexDescriptor.attributes[VertexAttributePosition].bufferIndex = - BufferIndexMeshPositions; - - _mtlVertexDescriptor.attributes[VertexAttributeTexcoord].format = - MTLVertexFormatFloat2; - _mtlVertexDescriptor.attributes[VertexAttributeTexcoord].offset = 0; - _mtlVertexDescriptor.attributes[VertexAttributeTexcoord].bufferIndex = - BufferIndexMeshGenerics; - - _mtlVertexDescriptor.layouts[BufferIndexMeshPositions].stride = 12; - _mtlVertexDescriptor.layouts[BufferIndexMeshPositions].stepRate = 1; - _mtlVertexDescriptor.layouts[BufferIndexMeshPositions].stepFunction = - MTLVertexStepFunctionPerVertex; - - _mtlVertexDescriptor.layouts[BufferIndexMeshGenerics].stride = 8; - _mtlVertexDescriptor.layouts[BufferIndexMeshGenerics].stepRate = 1; - _mtlVertexDescriptor.layouts[BufferIndexMeshGenerics].stepFunction = - MTLVertexStepFunctionPerVertex; - - auto shader_library_path = - impeller::ImpellerShadersLocation("impeller_host.metallib"); - - FML_CHECK(shader_library_path.has_value()); - - NSError* shader_library_error = nil; - - id defaultLibrary = - [_device newLibraryWithFile:@(shader_library_path.value().c_str()) - error:&shader_library_error]; - - id vertexFunction = - [defaultLibrary newFunctionWithName:@"vertexShader"]; - - id fragmentFunction = - [defaultLibrary newFunctionWithName:@"fragmentShader"]; - - MTLRenderPipelineDescriptor* pipelineStateDescriptor = - [[MTLRenderPipelineDescriptor alloc] init]; - pipelineStateDescriptor.label = @"MyPipeline"; - pipelineStateDescriptor.sampleCount = view.sampleCount; - pipelineStateDescriptor.vertexFunction = vertexFunction; - pipelineStateDescriptor.fragmentFunction = fragmentFunction; - pipelineStateDescriptor.vertexDescriptor = _mtlVertexDescriptor; - pipelineStateDescriptor.colorAttachments[0].pixelFormat = - view.colorPixelFormat; - pipelineStateDescriptor.depthAttachmentPixelFormat = - view.depthStencilPixelFormat; - pipelineStateDescriptor.stencilAttachmentPixelFormat = - view.depthStencilPixelFormat; - - NSError* error = NULL; - _pipelineState = - [_device newRenderPipelineStateWithDescriptor:pipelineStateDescriptor - error:&error]; - if (!_pipelineState) { - NSLog(@"Failed to created pipeline state, error %@", error); - } - - MTLDepthStencilDescriptor* depthStateDesc = - [[MTLDepthStencilDescriptor alloc] init]; - depthStateDesc.depthCompareFunction = MTLCompareFunctionLess; - depthStateDesc.depthWriteEnabled = YES; - _depthState = [_device newDepthStencilStateWithDescriptor:depthStateDesc]; - - NSUInteger uniformBufferSize = kAlignedUniformsSize * kMaxBuffersInFlight; - - _dynamicUniformBuffer = - [_device newBufferWithLength:uniformBufferSize - options:MTLResourceStorageModeShared]; - - _dynamicUniformBuffer.label = @"UniformBuffer"; - - _commandQueue = [_device newCommandQueue]; -} - -- (void)_loadAssets { - /// Load assets into metal objects - - NSError* error; - - MTKMeshBufferAllocator* metalAllocator = - [[MTKMeshBufferAllocator alloc] initWithDevice:_device]; - - MDLMesh* mdlMesh = [MDLMesh newBoxWithDimensions:(vector_float3){4, 4, 4} - segments:(vector_uint3){2, 2, 2} - geometryType:MDLGeometryTypeTriangles - inwardNormals:NO - allocator:metalAllocator]; - - MDLVertexDescriptor* mdlVertexDescriptor = - MTKModelIOVertexDescriptorFromMetal(_mtlVertexDescriptor); - - mdlVertexDescriptor.attributes[VertexAttributePosition].name = - MDLVertexAttributePosition; - mdlVertexDescriptor.attributes[VertexAttributeTexcoord].name = - MDLVertexAttributeTextureCoordinate; - - mdlMesh.vertexDescriptor = mdlVertexDescriptor; - - _mesh = [[MTKMesh alloc] initWithMesh:mdlMesh device:_device error:&error]; - - if (!_mesh || error) { - NSLog(@"Error creating MetalKit mesh %@", error.localizedDescription); - } - - MTKTextureLoader* textureLoader = - [[MTKTextureLoader alloc] initWithDevice:_device]; - - NSDictionary* textureLoaderOptions = @{ - MTKTextureLoaderOptionTextureUsage : @(MTLTextureUsageShaderRead), - MTKTextureLoaderOptionTextureStorageMode : @(MTLStorageModePrivate) - }; - - auto color_map_data = [NSData - dataWithContentsOfFile:@(impeller::GetAssetLocation("ColorMap.png") - .c_str())]; - - _colorMap = [textureLoader newTextureWithData:color_map_data - options:textureLoaderOptions - error:&error]; - - if (!_colorMap || error) { - NSLog(@"Error creating texture %@", error.localizedDescription); - } -} - -- (void)_updateDynamicBufferState { - /// Update the state of our uniform buffers before rendering - - _uniformBufferIndex = (_uniformBufferIndex + 1) % kMaxBuffersInFlight; - - _uniformBufferOffset = kAlignedUniformsSize * _uniformBufferIndex; - - _uniformBufferAddress = - ((uint8_t*)_dynamicUniformBuffer.contents) + _uniformBufferOffset; -} - -- (void)_updateGameState { - /// Update any game state before encoding renderint commands to our drawable - - Uniforms* uniforms = (Uniforms*)_uniformBufferAddress; - - uniforms->projectionMatrix = _projectionMatrix; - - vector_float3 rotationAxis = {1, 1, 0}; - matrix_float4x4 modelMatrix = matrix4x4_rotation(_rotation, rotationAxis); - matrix_float4x4 viewMatrix = matrix4x4_translation(0.0, 0.0, -8.0); - - uniforms->modelViewMatrix = matrix_multiply(viewMatrix, modelMatrix); - - _rotation += .01; -} - -- (void)drawInMTKView:(nonnull MTKView*)view { - impeller::Surface surface( - impeller::FromMTLRenderPassDescriptor(view.currentRenderPassDescriptor), - [drawable = view.currentDrawable]() { - [drawable present]; - return true; - }); - if (!renderer_->Render(surface)) { - FML_LOG(ERROR) << "Could not render."; - } - return; - /// Per frame updates here - dispatch_semaphore_wait(_inFlightSemaphore, DISPATCH_TIME_FOREVER); - - id commandBuffer = [_commandQueue commandBuffer]; - commandBuffer.label = @"MyCommand"; - - __block dispatch_semaphore_t block_sema = _inFlightSemaphore; - [commandBuffer addCompletedHandler:^(id buffer) { - dispatch_semaphore_signal(block_sema); - }]; - - [self _updateDynamicBufferState]; - - [self _updateGameState]; - - /// Delay getting the currentRenderPassDescriptor until we absolutely need it - /// to avoid holding onto the drawable and blocking the display pipeline any - /// longer than necessary - MTLRenderPassDescriptor* renderPassDescriptor = - view.currentRenderPassDescriptor; - - if (renderPassDescriptor != nil) { - /// Final pass rendering code here - - id renderEncoder = - [commandBuffer renderCommandEncoderWithDescriptor:renderPassDescriptor]; - renderEncoder.label = @"MyRenderEncoder"; - - [renderEncoder pushDebugGroup:@"DrawBox"]; - - [renderEncoder setFrontFacingWinding:MTLWindingCounterClockwise]; - [renderEncoder setCullMode:MTLCullModeBack]; - [renderEncoder setRenderPipelineState:_pipelineState]; - [renderEncoder setDepthStencilState:_depthState]; - - [renderEncoder setVertexBuffer:_dynamicUniformBuffer - offset:_uniformBufferOffset - atIndex:BufferIndexUniforms]; - - [renderEncoder setFragmentBuffer:_dynamicUniformBuffer - offset:_uniformBufferOffset - atIndex:BufferIndexUniforms]; - - for (NSUInteger bufferIndex = 0; bufferIndex < _mesh.vertexBuffers.count; - bufferIndex++) { - MTKMeshBuffer* vertexBuffer = _mesh.vertexBuffers[bufferIndex]; - if ((NSNull*)vertexBuffer != [NSNull null]) { - [renderEncoder setVertexBuffer:vertexBuffer.buffer - offset:vertexBuffer.offset - atIndex:bufferIndex]; - } - } - - [renderEncoder setFragmentTexture:_colorMap atIndex:TextureIndexColor]; - - for (MTKSubmesh* submesh in _mesh.submeshes) { - [renderEncoder drawIndexedPrimitives:submesh.primitiveType - indexCount:submesh.indexCount - indexType:submesh.indexType - indexBuffer:submesh.indexBuffer.buffer - indexBufferOffset:submesh.indexBuffer.offset]; - } - - [renderEncoder popDebugGroup]; - - [renderEncoder endEncoding]; - - [commandBuffer presentDrawable:view.currentDrawable]; - } - - [commandBuffer commit]; -} - -- (void)mtkView:(nonnull MTKView*)view drawableSizeWillChange:(CGSize)size { - float aspect = size.width / (float)size.height; - _projectionMatrix = matrix_perspective_right_hand(65.0f * (M_PI / 180.0f), - aspect, 0.1f, 100.0f); -} - -#pragma mark Matrix Math Utilities - -// NOLINTNEXTLINE -matrix_float4x4 matrix4x4_translation(float tx, float ty, float tz) { - return (matrix_float4x4){ - {{1, 0, 0, 0}, {0, 1, 0, 0}, {0, 0, 1, 0}, {tx, ty, tz, 1}}}; -} - -// NOLINTNEXTLINE -static matrix_float4x4 matrix4x4_rotation(float radians, vector_float3 axis) { - axis = vector_normalize(axis); - float ct = cosf(radians); - float st = sinf(radians); - float ci = 1 - ct; - float x = axis.x, y = axis.y, z = axis.z; - - return (matrix_float4x4){ - {{ct + x * x * ci, y * x * ci + z * st, z * x * ci - y * st, 0}, - {x * y * ci - z * st, ct + y * y * ci, z * y * ci + x * st, 0}, - {x * z * ci + y * st, y * z * ci - x * st, ct + z * z * ci, 0}, - {0, 0, 0, 1}}}; -} - -// NOLINTNEXTLINE -matrix_float4x4 matrix_perspective_right_hand(float fovyRadians, - float aspect, - float nearZ, - float farZ) { - float ys = 1 / tanf(fovyRadians * 0.5); - float xs = ys / aspect; - float zs = farZ / (nearZ - farZ); - - return (matrix_float4x4){ - {{xs, 0, 0, 0}, {0, ys, 0, 0}, {0, 0, zs, -1}, {0, 0, nearZ * zs, 0}}}; -} - -@end diff --git a/impeller/host/main.mm b/impeller/host/main.mm deleted file mode 100644 index db234115d801b..0000000000000 --- a/impeller/host/main.mm +++ /dev/null @@ -1,62 +0,0 @@ -// Copyright 2013 The Flutter Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#import - -#include "impeller_host_view_controller.h" - -@interface ImpellerAppDelegate - : NSObject { - NSWindow* window_; - ImpellerHostViewController* view_controller_; -} -@end - -@implementation ImpellerAppDelegate : NSObject - -- (id)init { - if (self = [super init]) { - view_controller_ = [[ImpellerHostViewController alloc] init]; - window_ = [[NSWindow alloc] - initWithContentRect:NSMakeRect(200, 200, 800, 600) - styleMask:NSWindowStyleMaskTitled | NSWindowStyleMaskClosable - backing:NSBackingStoreBuffered - defer:NO]; - window_.styleMask |= NSWindowStyleMaskResizable; - [window_ setContentViewController:view_controller_]; - } - return self; -} - -- (void)applicationDidFinishLaunching:(NSNotification*)notification { - [window_ setTitle:@"Impeller Host"]; - [window_ makeKeyAndOrderFront:self]; -} - -- (BOOL)applicationShouldTerminateAfterLastWindowClosed:(NSApplication*)sender { - return YES; -} - -@end - -int main(int argc, const char* argv[]) { - NSApplication* app = [NSApplication sharedApplication]; - [app setActivationPolicy:NSApplicationActivationPolicyRegular]; - NSMenuItem* item = [[NSMenuItem alloc] init]; - app.mainMenu = [[NSMenu alloc] init]; - item.submenu = [[NSMenu alloc] init]; - [app.mainMenu addItem:item]; - [item.submenu - addItem:[[NSMenuItem alloc] - initWithTitle:[@"Quit " - stringByAppendingString:[NSProcessInfo - processInfo] - .processName] - action:@selector(terminate:) - keyEquivalent:@"q"]]; - ImpellerAppDelegate* appDelegate = [[ImpellerAppDelegate alloc] init]; - [app setDelegate:appDelegate]; - [app run]; - return 0; -} diff --git a/impeller/host/shaders_location.cc b/impeller/host/shaders_location.cc deleted file mode 100644 index 6ce60a7ef6c7a..0000000000000 --- a/impeller/host/shaders_location.cc +++ /dev/null @@ -1,31 +0,0 @@ -// Copyright 2013 The Flutter Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#include "shaders_location.h" - -#include "flutter/fml/file.h" -#include "flutter/fml/paths.h" - -namespace impeller { - -std::string ImpellerShadersDirectory() { - auto path_result = fml::paths::GetExecutableDirectoryPath(); - if (!path_result.first) { - return {}; - } - return fml::paths::JoinPaths({path_result.second, "shaders"}); -} - -std::optional ImpellerShadersLocation(std::string library_name) { - auto path = fml::paths::JoinPaths({ImpellerShadersDirectory(), library_name}); - - if (!fml::IsFile(path)) { - FML_LOG(ERROR) << "The shader library does not exist: " << path; - return std::nullopt; - } - - return path; -} - -} // namespace impeller diff --git a/impeller/host/shaders_location.h b/impeller/host/shaders_location.h deleted file mode 100644 index cd26132f1271f..0000000000000 --- a/impeller/host/shaders_location.h +++ /dev/null @@ -1,14 +0,0 @@ -// Copyright 2013 The Flutter Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#include -#include - -namespace impeller { - -std::string ImpellerShadersDirectory(); - -std::optional ImpellerShadersLocation(std::string library_name); - -} // namespace impeller diff --git a/impeller/playground/BUILD.gn b/impeller/playground/BUILD.gn index d4fa603a0b173..0a912bef5d8a2 100644 --- a/impeller/playground/BUILD.gn +++ b/impeller/playground/BUILD.gn @@ -14,6 +14,7 @@ impeller_component("playground") { ] public_deps = [ + "../compositor", "//flutter/testing", "//third_party/glfw", ] diff --git a/impeller/primitives/box_primitive.mm b/impeller/primitives/box_primitive.mm index 485179770ea5d..556a05dbc414c 100644 --- a/impeller/primitives/box_primitive.mm +++ b/impeller/primitives/box_primitive.mm @@ -8,6 +8,7 @@ #include "flutter/fml/logging.h" #include "impeller/base/base.h" +#include "impeller/compositor/pipeline_builder.h" #include "impeller/compositor/pipeline_descriptor.h" #include "impeller/compositor/shader_library.h" #include "impeller/compositor/vertex_descriptor.h" @@ -16,55 +17,17 @@ BoxPrimitive::BoxPrimitive(std::shared_ptr context) : Primitive(context) { - PipelineDescriptor desc; - desc.SetLabel(SPrintF("%s Pipeline", shader::BoxVertexInfo::kLabel.data())); - - { - auto fragment_function = context->GetShaderLibrary()->GetFunction( - shader::BoxFragmentInfo::kEntrypointName, ShaderStage::kFragment); - auto vertex_function = context->GetShaderLibrary()->GetFunction( - shader::BoxVertexInfo::kEntrypointName, ShaderStage::kVertex); - - desc.AddStageEntrypoint(vertex_function); - desc.AddStageEntrypoint(fragment_function); - } - - { - auto vertex_descriptor = std::make_shared(); - vertex_buffer_index_ = vertex_descriptor->GetVertexBufferIndex(); - if (!vertex_descriptor->SetStageInputs( - shader::BoxVertexInfo::kAllShaderStageInputs)) { - FML_LOG(ERROR) << "Could not configure vertex descriptor."; - return; - } - desc.SetVertexDescriptor(std::move(vertex_descriptor)); - } - - { - // Configure the sole color attachments pixel format. - ColorAttachmentDescriptor color0; - color0.format = PixelFormat::kPixelFormat_B8G8R8A8_UNormInt_SRGB; - - desc.SetColorAttachmentDescriptor(0u, std::move(color0)); - } - - { - // Configure the stencil attachment. - // TODO(wip): Make this configurable if possible as the D32 component is - // wasted. - const auto combined_depth_stencil_format = - PixelFormat::kPixelFormat_D32_Float_S8_UNormInt; - desc.SetDepthPixelFormat(combined_depth_stencil_format); - desc.SetStencilPixelFormat(combined_depth_stencil_format); + using Builder = PipelineBuilder; + auto pipeline_descriptor = Builder::MakeDefaultPipelineDescriptor(*context); + if (!pipeline_descriptor.has_value()) { + return; } - - pipeline_ = - context->GetPipelineLibrary()->GetRenderPipeline(std::move(desc)).get(); + pipeline_ = context->GetPipelineLibrary() + ->GetRenderPipeline(pipeline_descriptor.value()) + .get(); if (!pipeline_) { - FML_LOG(ERROR) << "Could not create the render pipeline."; return; } - is_valid_ = true; } From 810db2a2f2eb78b90c09e424941cdb60b0862d69 Mon Sep 17 00:00:00 2001 From: Chinmay Garde Date: Sat, 3 Jul 2021 01:14:59 -0700 Subject: [PATCH 115/433] Use the pipeline builder for the box primitive. --- impeller/compositor/pipeline_builder.h | 4 +- impeller/compositor/pipeline_library.h | 3 ++ impeller/compositor/pipeline_library.mm | 10 +++++ impeller/entity/entity_renderer.h | 3 +- impeller/entity/entity_renderer.mm | 14 +++++-- impeller/primitives/BUILD.gn | 2 - impeller/primitives/box_primitive.h | 42 -------------------- impeller/primitives/box_primitive.mm | 52 ------------------------- 8 files changed, 26 insertions(+), 104 deletions(-) delete mode 100644 impeller/primitives/box_primitive.h delete mode 100644 impeller/primitives/box_primitive.mm diff --git a/impeller/compositor/pipeline_builder.h b/impeller/compositor/pipeline_builder.h index a5249c09ec7b9..4191e938ccc7c 100644 --- a/impeller/compositor/pipeline_builder.h +++ b/impeller/compositor/pipeline_builder.h @@ -14,8 +14,8 @@ namespace impeller { //------------------------------------------------------------------------------ -/// @brief A utility for creating pipelines from reflected shader -/// information. +/// @brief An optional (but highly recommended) utility for creating +/// pipelines from reflected shader information. /// /// @tparam VertexShader_ The reflected vertex shader information. Found /// in a generated header file called diff --git a/impeller/compositor/pipeline_library.h b/impeller/compositor/pipeline_library.h index b350ac9134231..43ecfa8347745 100644 --- a/impeller/compositor/pipeline_library.h +++ b/impeller/compositor/pipeline_library.h @@ -22,6 +22,9 @@ class PipelineLibrary : public std::enable_shared_from_this { public: ~PipelineLibrary(); + std::future> GetRenderPipeline( + std::optional descriptor); + std::future> GetRenderPipeline( PipelineDescriptor descriptor); diff --git a/impeller/compositor/pipeline_library.mm b/impeller/compositor/pipeline_library.mm index 1c6ff24727797..d799ce7034ceb 100644 --- a/impeller/compositor/pipeline_library.mm +++ b/impeller/compositor/pipeline_library.mm @@ -14,6 +14,16 @@ PipelineLibrary::~PipelineLibrary() = default; +std::future> PipelineLibrary::GetRenderPipeline( + std::optional descriptor) { + if (descriptor.has_value()) { + return GetRenderPipeline(std::move(descriptor.value())); + } + auto promise = std::make_shared>>(); + promise->set_value(nullptr); + return promise->get_future(); +} + std::future> PipelineLibrary::GetRenderPipeline( PipelineDescriptor descriptor) { auto promise = std::make_shared>>(); diff --git a/impeller/entity/entity_renderer.h b/impeller/entity/entity_renderer.h index bd91dcb678066..cf1cb1beaf82e 100644 --- a/impeller/entity/entity_renderer.h +++ b/impeller/entity/entity_renderer.h @@ -8,7 +8,6 @@ #include "impeller/compositor/renderer.h" #include "impeller/compositor/vertex_buffer.h" #include "impeller/entity/entity.h" -#include "impeller/primitives/box_primitive.h" namespace impeller { @@ -20,7 +19,7 @@ class EntityRenderer final : public Renderer { private: std::shared_ptr root_; - std::shared_ptr box_primitive_; + std::shared_ptr box_pipeline_; VertexBuffer vertex_buffer_; bool is_valid_ = false; diff --git a/impeller/entity/entity_renderer.mm b/impeller/entity/entity_renderer.mm index 22878ab8781e0..2f0c9a1b12ce4 100644 --- a/impeller/entity/entity_renderer.mm +++ b/impeller/entity/entity_renderer.mm @@ -6,6 +6,7 @@ #include "flutter/fml/time/time_point.h" #include "impeller/compositor/command.h" +#include "impeller/compositor/pipeline_builder.h" #include "impeller/compositor/sampler_descriptor.h" #include "impeller/compositor/surface.h" #include "impeller/compositor/vertex_buffer_builder.h" @@ -25,8 +26,13 @@ return; } - box_primitive_ = std::make_shared(context); - if (!box_primitive_) { + using BoxPipelineBuilder = + PipelineBuilder; + auto desc = BoxPipelineBuilder::MakeDefaultPipelineDescriptor(*context); + box_pipeline_ = + context->GetPipelineLibrary()->GetRenderPipeline(std::move(desc)).get(); + + if (!box_pipeline_) { return; } @@ -67,8 +73,8 @@ Command cmd; cmd.label = "Box"; - cmd.pipeline = box_primitive_->GetPipeline(); - cmd.vertex_bindings.buffers[box_primitive_->GetVertexBufferIndex()] = + cmd.pipeline = box_pipeline_; + cmd.vertex_bindings.buffers[VertexDescriptor::kReservedVertexBufferIndex] = vertex_buffer_.vertex_buffer; cmd.vertex_bindings.buffers[BoxVertexShader::kUniformUniformBuffer.binding] = pass.GetTransientsBuffer().EmplaceUniform(uniforms); diff --git a/impeller/primitives/BUILD.gn b/impeller/primitives/BUILD.gn index 07a3d2e3a901c..a3a046d68c425 100644 --- a/impeller/primitives/BUILD.gn +++ b/impeller/primitives/BUILD.gn @@ -14,8 +14,6 @@ impeller_shaders("impeller_shaders") { impeller_component("primitives") { sources = [ - "box_primitive.h", - "box_primitive.mm", "primitive.h", "primitive.mm", ] diff --git a/impeller/primitives/box_primitive.h b/impeller/primitives/box_primitive.h deleted file mode 100644 index fcb19f7994867..0000000000000 --- a/impeller/primitives/box_primitive.h +++ /dev/null @@ -1,42 +0,0 @@ -// Copyright 2013 The Flutter Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#pragma once - -#include "impeller/compositor/context.h" -#include "impeller/primitives/box.frag.h" -#include "impeller/primitives/box.vert.h" -#include "impeller/primitives/primitive.h" - -namespace impeller { - -class BoxPrimitive final : public Primitive { - public: - BoxPrimitive(std::shared_ptr context); - - // |Primitive| - std::shared_ptr GetPipeline() const override; - - // |Primitive| - ~BoxPrimitive() override; - - // |Primitive| - virtual bool IsValid() const override; - - // |Primitive| - virtual bool Encode(RenderPass& pass) const override; - - size_t GetVertexBufferIndex() const; - - private: - std::shared_ptr pipeline_ = 0; - size_t vertex_buffer_index_ = 0; - bool is_valid_ = false; - - FML_DISALLOW_COPY_AND_ASSIGN(BoxPrimitive); -}; - -bool RenderBox(std::shared_ptr context); - -} // namespace impeller diff --git a/impeller/primitives/box_primitive.mm b/impeller/primitives/box_primitive.mm deleted file mode 100644 index 556a05dbc414c..0000000000000 --- a/impeller/primitives/box_primitive.mm +++ /dev/null @@ -1,52 +0,0 @@ -// Copyright 2013 The Flutter Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#include "impeller/primitives/box_primitive.h" - -#include - -#include "flutter/fml/logging.h" -#include "impeller/base/base.h" -#include "impeller/compositor/pipeline_builder.h" -#include "impeller/compositor/pipeline_descriptor.h" -#include "impeller/compositor/shader_library.h" -#include "impeller/compositor/vertex_descriptor.h" - -namespace impeller { - -BoxPrimitive::BoxPrimitive(std::shared_ptr context) - : Primitive(context) { - using Builder = PipelineBuilder; - auto pipeline_descriptor = Builder::MakeDefaultPipelineDescriptor(*context); - if (!pipeline_descriptor.has_value()) { - return; - } - pipeline_ = context->GetPipelineLibrary() - ->GetRenderPipeline(pipeline_descriptor.value()) - .get(); - if (!pipeline_) { - return; - } - is_valid_ = true; -} - -size_t BoxPrimitive::GetVertexBufferIndex() const { - return vertex_buffer_index_; -} - -BoxPrimitive::~BoxPrimitive() = default; - -std::shared_ptr BoxPrimitive::GetPipeline() const { - return pipeline_; -} - -bool BoxPrimitive::IsValid() const { - return is_valid_; -} - -bool BoxPrimitive::Encode(RenderPass& pass) const { - return false; -} - -} // namespace impeller From 43d90bbc9d9839dc6a9e65b37a28bf13f1b3a390 Mon Sep 17 00:00:00 2001 From: Chinmay Garde Date: Sat, 3 Jul 2021 15:29:16 -0700 Subject: [PATCH 116/433] Fully wire up the box primitive in the playground. --- impeller/compositor/formats.h | 1 + impeller/compositor/formats_metal.h | 2 + impeller/compositor/pipeline_builder.h | 11 +-- impeller/compositor/renderer.h | 17 ++-- impeller/compositor/renderer.mm | 13 ++- impeller/compositor/surface.h | 8 +- impeller/compositor/surface.mm | 17 +--- impeller/entity/BUILD.gn | 2 - impeller/entity/entity_renderer.h | 35 -------- impeller/entity/entity_renderer.mm | 96 --------------------- impeller/entity/entity_unittests.mm | 5 +- impeller/playground/playground.h | 7 +- impeller/playground/playground.mm | 46 +++++----- impeller/primitives/BUILD.gn | 5 ++ impeller/primitives/box.frag | 10 +-- impeller/primitives/box.vert | 3 - impeller/primitives/primitives_unittests.mm | 71 +++++++++++++++ 17 files changed, 136 insertions(+), 213 deletions(-) delete mode 100644 impeller/entity/entity_renderer.h delete mode 100644 impeller/entity/entity_renderer.mm diff --git a/impeller/compositor/formats.h b/impeller/compositor/formats.h index c970edc1886de..0037fd8702237 100644 --- a/impeller/compositor/formats.h +++ b/impeller/compositor/formats.h @@ -15,6 +15,7 @@ namespace impeller { enum class PixelFormat { kUnknown, + kPixelFormat_B8G8R8A8_UNormInt, kPixelFormat_B8G8R8A8_UNormInt_SRGB, kPixelFormat_D32_Float_S8_UNormInt, }; diff --git a/impeller/compositor/formats_metal.h b/impeller/compositor/formats_metal.h index 970b1ad53887f..08aa2a8fadc53 100644 --- a/impeller/compositor/formats_metal.h +++ b/impeller/compositor/formats_metal.h @@ -20,6 +20,8 @@ constexpr MTLPixelFormat ToMTLPixelFormat(PixelFormat format) { switch (format) { case PixelFormat::kUnknown: return MTLPixelFormatInvalid; + case PixelFormat::kPixelFormat_B8G8R8A8_UNormInt: + return MTLPixelFormatBGRA8Unorm; case PixelFormat::kPixelFormat_B8G8R8A8_UNormInt_SRGB: return MTLPixelFormatBGRA8Unorm_sRGB; case PixelFormat::kPixelFormat_D32_Float_S8_UNormInt: diff --git a/impeller/compositor/pipeline_builder.h b/impeller/compositor/pipeline_builder.h index 4191e938ccc7c..fe973e743fb3c 100644 --- a/impeller/compositor/pipeline_builder.h +++ b/impeller/compositor/pipeline_builder.h @@ -6,6 +6,7 @@ #include "flutter/fml/logging.h" #include "flutter/fml/macros.h" +#include "impeller/base/base.h" #include "impeller/compositor/context.h" #include "impeller/compositor/pipeline_descriptor.h" #include "impeller/compositor/shader_library.h" @@ -84,7 +85,7 @@ struct PipelineBuilder { // TODO(csg): This can be easily reflected but we are sticking to the // convention that the first stage output is the color output. ColorAttachmentDescriptor color0; - color0.format = PixelFormat::kPixelFormat_B8G8R8A8_UNormInt_SRGB; + color0.format = PixelFormat::kPixelFormat_B8G8R8A8_UNormInt; desc.SetColorAttachmentDescriptor(0u, std::move(color0)); } @@ -94,10 +95,10 @@ struct PipelineBuilder { // TODO(csg): Make this configurable if possible as the D32 component is // wasted. This can even be moved out of the "default" descriptor // construction as a case can be made that this is caller responsibility. - const auto combined_depth_stencil_format = - PixelFormat::kPixelFormat_D32_Float_S8_UNormInt; - desc.SetDepthPixelFormat(combined_depth_stencil_format); - desc.SetStencilPixelFormat(combined_depth_stencil_format); + // const auto combined_depth_stencil_format = + // PixelFormat::kPixelFormat_D32_Float_S8_UNormInt; + // desc.SetDepthPixelFormat(combined_depth_stencil_format); + // desc.SetStencilPixelFormat(combined_depth_stencil_format); } return desc; diff --git a/impeller/compositor/renderer.h b/impeller/compositor/renderer.h index 85b1f9e8130de..61a1258c31432 100644 --- a/impeller/compositor/renderer.h +++ b/impeller/compositor/renderer.h @@ -6,6 +6,7 @@ #include +#include #include #include "flutter/fml/macros.h" @@ -19,20 +20,18 @@ class RenderPass; class Renderer { public: - virtual ~Renderer(); + using RenderCallback = + std::function; - bool IsValid() const; - - bool Render(const Surface& surface); + Renderer(std::string shaders_directory); - std::shared_ptr GetContext() const; + ~Renderer(); - protected: - Renderer(std::string shaders_directory); + bool IsValid() const; - virtual bool OnIsValid() const = 0; + bool Render(const Surface& surface, RenderCallback callback) const; - virtual bool OnRender(const Surface& surface, RenderPass& pass) = 0; + std::shared_ptr GetContext() const; private: dispatch_semaphore_t frames_in_flight_sema_ = nullptr; diff --git a/impeller/compositor/renderer.mm b/impeller/compositor/renderer.mm index b5dfcf8290199..8b55e4b7c8a4e 100644 --- a/impeller/compositor/renderer.mm +++ b/impeller/compositor/renderer.mm @@ -25,10 +25,11 @@ Renderer::~Renderer() = default; bool Renderer::IsValid() const { - return is_valid_ && OnIsValid(); + return is_valid_; } -bool Renderer::Render(const Surface& surface) { +bool Renderer::Render(const Surface& surface, + RenderCallback render_callback) const { if (!IsValid()) { return false; } @@ -44,16 +45,12 @@ } auto render_pass = - command_buffer->CreateRenderPass(surface.GetRenderPassDescriptor()); + command_buffer->CreateRenderPass(surface.GetTargetRenderPassDescriptor()); if (!render_pass) { return false; } - if (!OnRender(surface, *render_pass)) { - return false; - } - - if (!surface.Present()) { + if (render_callback && !render_callback(surface, *render_pass)) { return false; } diff --git a/impeller/compositor/surface.h b/impeller/compositor/surface.h index 7a92188e80181..edb0e50a13260 100644 --- a/impeller/compositor/surface.h +++ b/impeller/compositor/surface.h @@ -17,22 +17,18 @@ namespace impeller { class Surface { public: - Surface(RenderPassDescriptor desc, - std::function present_callback); + Surface(RenderPassDescriptor target_desc); ~Surface(); const Size& GetSize() const; - bool Present() const; - bool IsValid() const; - const RenderPassDescriptor& GetRenderPassDescriptor() const; + const RenderPassDescriptor& GetTargetRenderPassDescriptor() const; private: RenderPassDescriptor desc_; - std::function present_callback_; Size size_; bool is_valid_ = false; diff --git a/impeller/compositor/surface.mm b/impeller/compositor/surface.mm index a872e8a467fc0..dfcd330d31517 100644 --- a/impeller/compositor/surface.mm +++ b/impeller/compositor/surface.mm @@ -8,9 +8,8 @@ namespace impeller { -Surface::Surface(RenderPassDescriptor desc, - std::function present_callback) - : desc_(std::move(desc)), present_callback_(present_callback) { +Surface::Surface(RenderPassDescriptor target_desc) + : desc_(std::move(target_desc)) { if (auto size = desc_.GetColorAttachmentSize(0u); size.has_value()) { size_ = size.value(); } else { @@ -26,21 +25,11 @@ return size_; } -bool Surface::Present() const { - auto callback = present_callback_; - - if (!callback) { - return false; - } - - return callback(); -} - bool Surface::IsValid() const { return is_valid_; } -const RenderPassDescriptor& Surface::GetRenderPassDescriptor() const { +const RenderPassDescriptor& Surface::GetTargetRenderPassDescriptor() const { return desc_; } diff --git a/impeller/entity/BUILD.gn b/impeller/entity/BUILD.gn index bd19c0ed5ab75..a321fa03bb65e 100644 --- a/impeller/entity/BUILD.gn +++ b/impeller/entity/BUILD.gn @@ -8,8 +8,6 @@ impeller_component("entity") { sources = [ "entity.cc", "entity.h", - "entity_renderer.h", - "entity_renderer.mm", ] public_deps = [ diff --git a/impeller/entity/entity_renderer.h b/impeller/entity/entity_renderer.h deleted file mode 100644 index cf1cb1beaf82e..0000000000000 --- a/impeller/entity/entity_renderer.h +++ /dev/null @@ -1,35 +0,0 @@ -// Copyright 2013 The Flutter Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#pragma once - -#include "flutter/fml/macros.h" -#include "impeller/compositor/renderer.h" -#include "impeller/compositor/vertex_buffer.h" -#include "impeller/entity/entity.h" - -namespace impeller { - -class EntityRenderer final : public Renderer { - public: - EntityRenderer(std::string shaders_directory); - - ~EntityRenderer() override; - - private: - std::shared_ptr root_; - std::shared_ptr box_pipeline_; - VertexBuffer vertex_buffer_; - bool is_valid_ = false; - - // |Renderer| - bool OnIsValid() const override; - - // |Renderer| - bool OnRender(const Surface& surface, RenderPass& pass) override; - - FML_DISALLOW_COPY_AND_ASSIGN(EntityRenderer); -}; - -} // namespace impeller diff --git a/impeller/entity/entity_renderer.mm b/impeller/entity/entity_renderer.mm deleted file mode 100644 index 2f0c9a1b12ce4..0000000000000 --- a/impeller/entity/entity_renderer.mm +++ /dev/null @@ -1,96 +0,0 @@ -// Copyright 2013 The Flutter Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#include "impeller/entity/entity_renderer.h" - -#include "flutter/fml/time/time_point.h" -#include "impeller/compositor/command.h" -#include "impeller/compositor/pipeline_builder.h" -#include "impeller/compositor/sampler_descriptor.h" -#include "impeller/compositor/surface.h" -#include "impeller/compositor/vertex_buffer_builder.h" -#include "impeller/primitives/box.frag.h" -#include "impeller/primitives/box.vert.h" - -namespace impeller { - -EntityRenderer::EntityRenderer(std::string shaders_directory) - : Renderer(std::move(shaders_directory)), - root_(std::make_shared()) { - root_->SetBackgroundColor(Color::DarkGray()); - - auto context = GetContext(); - - if (!context) { - return; - } - - using BoxPipelineBuilder = - PipelineBuilder; - auto desc = BoxPipelineBuilder::MakeDefaultPipelineDescriptor(*context); - box_pipeline_ = - context->GetPipelineLibrary()->GetRenderPipeline(std::move(desc)).get(); - - if (!box_pipeline_) { - return; - } - - VertexBufferBuilder vertex_builder; - vertex_builder.SetLabel("Box"); - vertex_builder.AddVertices({ - {{100, 100, 0.0}, {Color::Red()}, {0.0, 0.0}}, // 1 - {{800, 100, 0.0}, {Color::Green()}, {1.0, 0.0}}, // 2 - {{800, 800, 0.0}, {Color::Blue()}, {1.0, 1.0}}, // 3 - - {{100, 100, 0.0}, {Color::Cyan()}, {0.0, 0.0}}, // 1 - {{800, 800, 0.0}, {Color::White()}, {1.0, 1.0}}, // 3 - {{100, 800, 0.0}, {Color::Purple()}, {0.0, 1.0}}, // 4 - }); - - vertex_buffer_ = - vertex_builder.CreateVertexBuffer(*context->GetPermanentsAllocator()); - - if (!vertex_buffer_) { - return; - } - - is_valid_ = true; -} - -EntityRenderer::~EntityRenderer() = default; - -bool EntityRenderer::OnIsValid() const { - return is_valid_; -} - -bool EntityRenderer::OnRender(const Surface& surface, RenderPass& pass) { - pass.SetLabel("EntityRenderer Render Pass"); - - BoxVertexShader::UniformBuffer uniforms; - - uniforms.mvp = Matrix::MakeOrthographic(surface.GetSize()); - - Command cmd; - cmd.label = "Box"; - cmd.pipeline = box_pipeline_; - cmd.vertex_bindings.buffers[VertexDescriptor::kReservedVertexBufferIndex] = - vertex_buffer_.vertex_buffer; - cmd.vertex_bindings.buffers[BoxVertexShader::kUniformUniformBuffer.binding] = - pass.GetTransientsBuffer().EmplaceUniform(uniforms); - cmd.fragment_bindings.samplers[BoxFragmentShader::kInputContents1.binding] = - GetContext()->GetSamplerLibrary()->GetSampler({}); - cmd.fragment_bindings.samplers[BoxFragmentShader::kInputContents2.binding] = - GetContext()->GetSamplerLibrary()->GetSampler({}); - cmd.fragment_bindings.samplers[BoxFragmentShader::kInputContents3.binding] = - GetContext()->GetSamplerLibrary()->GetSampler({}); - cmd.index_buffer = vertex_buffer_.index_buffer; - cmd.index_count = vertex_buffer_.index_count; - cmd.primitive_type = PrimitiveType::kTriange; - if (!pass.RecordCommand(std::move(cmd))) { - return false; - } - return true; -} - -} // namespace impeller diff --git a/impeller/entity/entity_unittests.mm b/impeller/entity/entity_unittests.mm index b53534d35e8d1..939d907173bf2 100644 --- a/impeller/entity/entity_unittests.mm +++ b/impeller/entity/entity_unittests.mm @@ -3,14 +3,11 @@ // found in the LICENSE file. #include "flutter/testing/testing.h" -#include "impeller/playground/playground.h" namespace impeller { namespace testing { -TEST_F(Playground, CanCreateEntity) { - OpenPlaygroundHere([]() { return true; }); -} +// } // namespace testing } // namespace impeller diff --git a/impeller/playground/playground.h b/impeller/playground/playground.h index b340f83f48a95..081e24a9e9187 100644 --- a/impeller/playground/playground.h +++ b/impeller/playground/playground.h @@ -5,6 +5,7 @@ #include "flutter/fml/closure.h" #include "flutter/fml/macros.h" #include "gtest/gtest.h" +#include "impeller/compositor/renderer.h" namespace impeller { @@ -14,9 +15,13 @@ class Playground : public ::testing::Test { ~Playground(); - bool OpenPlaygroundHere(std::function closure); + std::shared_ptr GetContext() const; + + bool OpenPlaygroundHere(Renderer::RenderCallback render_callback); private: + Renderer renderer_; + FML_DISALLOW_COPY_AND_ASSIGN(Playground); }; diff --git a/impeller/playground/playground.mm b/impeller/playground/playground.mm index b87d3a4c9fdb0..ea7e46f1d434d 100644 --- a/impeller/playground/playground.mm +++ b/impeller/playground/playground.mm @@ -3,9 +3,11 @@ // found in the LICENSE file. #include "impeller/playground/playground.h" +#include "flutter/fml/paths.h" #include "flutter/testing/testing.h" #include "impeller/compositor/context.h" #include "impeller/compositor/render_pass.h" +#include "impeller/compositor/renderer.h" #include "impeller/compositor/surface.h" #define GLFW_INCLUDE_NONE @@ -18,10 +20,22 @@ namespace impeller { -Playground::Playground() = default; +static std::string ShaderLibraryDirectory() { + auto path_result = fml::paths::GetExecutableDirectoryPath(); + if (!path_result.first) { + return {}; + } + return fml::paths::JoinPaths({path_result.second, "shaders"}); +} + +Playground::Playground() : renderer_(ShaderLibraryDirectory()) {} Playground::~Playground() = default; +std::shared_ptr Playground::GetContext() const { + return renderer_.IsValid() ? renderer_.GetContext() : nullptr; +} + static void PlaygroundKeyCallback(GLFWwindow* window, int key, int scancode, @@ -32,14 +46,12 @@ static void PlaygroundKeyCallback(GLFWwindow* window, } } -bool Playground::OpenPlaygroundHere(std::function closure) { - if (!closure) { +bool Playground::OpenPlaygroundHere(Renderer::RenderCallback render_callback) { + if (!render_callback) { return true; } - Context context(flutter::testing::GetFixturesPath()); - - if (!context.IsValid()) { + if (!renderer_.IsValid()) { return false; } @@ -50,7 +62,8 @@ static void PlaygroundKeyCallback(GLFWwindow* window, ::glfwWindowHint(GLFW_CLIENT_API, GLFW_NO_API); - auto window = ::glfwCreateWindow(800, 600, "Impeller Playground", NULL, NULL); + auto window = ::glfwCreateWindow( + 800, 600, "Impeller Playground (Press ESC or 'q' to quit)", NULL, NULL); if (!window) { return false; } @@ -63,7 +76,7 @@ static void PlaygroundKeyCallback(GLFWwindow* window, NSWindow* cocoa_window = ::glfwGetCocoaWindow(window); CAMetalLayer* layer = [CAMetalLayer layer]; - layer.device = context.GetMTLDevice(); + layer.device = renderer_.GetContext()->GetMTLDevice(); layer.pixelFormat = MTLPixelFormatBGRA8Unorm; cocoa_window.contentView.layer = layer; cocoa_window.contentView.wantsLayer = YES; @@ -91,23 +104,14 @@ static void PlaygroundKeyCallback(GLFWwindow* window, RenderPassDescriptor desc; desc.SetColorAttachment(color0, 0u); - Surface surface(desc, [current_drawable, closure]() { - if (!closure()) { - return false; - } - [current_drawable present]; - return true; - }); + Surface surface(desc); - if (!surface.IsValid()) { - FML_LOG(ERROR) << "Could not wrap surface to render to into."; + if (!renderer_.Render(surface, render_callback)) { + FML_LOG(ERROR) << "Could not render into the surface."; return false; } - if (!surface.Present()) { - FML_LOG(ERROR) << "Could not render into playground surface."; - return false; - } + [current_drawable present]; } return true; diff --git a/impeller/primitives/BUILD.gn b/impeller/primitives/BUILD.gn index a3a046d68c425..18acdaddd6426 100644 --- a/impeller/primitives/BUILD.gn +++ b/impeller/primitives/BUILD.gn @@ -25,5 +25,10 @@ impeller_component("primitives") { } impeller_component("primitives_unittests") { + testonly = true sources = [ "primitives_unittests.mm" ] + deps = [ + ":primitives", + "../playground", + ] } diff --git a/impeller/primitives/box.frag b/impeller/primitives/box.frag index 485250812779e..0263c965f294b 100644 --- a/impeller/primitives/box.frag +++ b/impeller/primitives/box.frag @@ -3,17 +3,9 @@ // found in the LICENSE file. in vec4 color; -in vec2 interpolated_texture_coordinates; - -uniform sampler2D contents1; -uniform sampler2D contents2; -uniform sampler2D contents3; out vec4 frag_color; void main() { - vec4 color1 = texture(contents1, interpolated_texture_coordinates); - vec4 color2 = texture(contents2, interpolated_texture_coordinates); - vec4 color3 = texture(contents3, interpolated_texture_coordinates); - frag_color = color3; + frag_color = color; } diff --git a/impeller/primitives/box.vert b/impeller/primitives/box.vert index e764b6267b58d..431c4c04d1f07 100644 --- a/impeller/primitives/box.vert +++ b/impeller/primitives/box.vert @@ -8,13 +8,10 @@ uniform UniformBuffer { in vec3 vertex_position; in vec4 vertex_color; -in vec2 texture_coordinates; out vec4 color; -out vec2 interpolated_texture_coordinates; void main() { gl_Position = uniforms.mvp * vec4(vertex_position, 1.0); color = vertex_color; - interpolated_texture_coordinates = texture_coordinates; } diff --git a/impeller/primitives/primitives_unittests.mm b/impeller/primitives/primitives_unittests.mm index e7217c7d7b3fa..922c7ad8866f5 100644 --- a/impeller/primitives/primitives_unittests.mm +++ b/impeller/primitives/primitives_unittests.mm @@ -1,3 +1,74 @@ // Copyright 2013 The Flutter Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. + +#include "flutter/fml/time/time_point.h" +#include "flutter/testing/testing.h" +#include "impeller/compositor/command.h" +#include "impeller/compositor/pipeline_builder.h" +#include "impeller/compositor/renderer.h" +#include "impeller/compositor/sampler_descriptor.h" +#include "impeller/compositor/surface.h" +#include "impeller/compositor/vertex_buffer_builder.h" +#include "impeller/playground/playground.h" +#include "impeller/primitives/box.frag.h" +#include "impeller/primitives/box.vert.h" + +namespace impeller { +namespace testing { + +using PrimitivesTest = Playground; + +TEST_F(PrimitivesTest, CanCreateBoxPrimitive) { + auto context = GetContext(); + ASSERT_TRUE(context); + using BoxPipelineBuilder = + PipelineBuilder; + auto desc = BoxPipelineBuilder::MakeDefaultPipelineDescriptor(*context); + auto box_pipeline = + context->GetPipelineLibrary()->GetRenderPipeline(std::move(desc)).get(); + ASSERT_TRUE(box_pipeline); + + VertexBufferBuilder vertex_builder; + vertex_builder.SetLabel("Box"); + vertex_builder.AddVertices({ + {{100, 100, 0.0}, {Color::Red()}}, // 1 + {{800, 100, 0.0}, {Color::Green()}}, // 2 + {{800, 800, 0.0}, {Color::Blue()}}, // 3 + + {{100, 100, 0.0}, {Color::Cyan()}}, // 1 + {{800, 800, 0.0}, {Color::White()}}, // 3 + {{100, 800, 0.0}, {Color::Purple()}}, // 4 + }); + auto vertex_buffer = + vertex_builder.CreateVertexBuffer(*context->GetPermanentsAllocator()); + ASSERT_TRUE(vertex_buffer); + Renderer::RenderCallback callback = [&](const Surface& surface, + RenderPass& pass) { + pass.SetLabel("EntityRenderer Render Pass"); + + BoxVertexShader::UniformBuffer uniforms; + + uniforms.mvp = Matrix::MakeOrthographic(surface.GetSize()); + + Command cmd; + cmd.label = "Box"; + cmd.pipeline = box_pipeline; + cmd.vertex_bindings.buffers[VertexDescriptor::kReservedVertexBufferIndex] = + vertex_buffer.vertex_buffer; + cmd.vertex_bindings + .buffers[BoxVertexShader::kUniformUniformBuffer.binding] = + pass.GetTransientsBuffer().EmplaceUniform(uniforms); + cmd.index_buffer = vertex_buffer.index_buffer; + cmd.index_count = vertex_buffer.index_count; + cmd.primitive_type = PrimitiveType::kTriange; + if (!pass.RecordCommand(std::move(cmd))) { + return false; + } + return true; + }; + OpenPlaygroundHere(callback); +} + +} // namespace testing +} // namespace impeller From 7b05023c3cd02328767e477e689c22c3350bc66b Mon Sep 17 00:00:00 2001 From: Chinmay Garde Date: Sat, 3 Jul 2021 15:37:44 -0700 Subject: [PATCH 117/433] Minor QOL improvements in the playground. --- impeller/playground/playground.mm | 12 +++++++++++- impeller/primitives/primitives_unittests.mm | 2 -- 2 files changed, 11 insertions(+), 3 deletions(-) diff --git a/impeller/playground/playground.mm b/impeller/playground/playground.mm index ea7e46f1d434d..8571633dbae84 100644 --- a/impeller/playground/playground.mm +++ b/impeller/playground/playground.mm @@ -61,6 +61,10 @@ static void PlaygroundKeyCallback(GLFWwindow* window, fml::ScopedCleanupClosure terminate([]() { ::glfwTerminate(); }); ::glfwWindowHint(GLFW_CLIENT_API, GLFW_NO_API); + // Recreation of the target render buffer is not setup in the playground yet. + // So prevent users from resizing and getting confused that their math is + // wrong. + ::glfwWindowHint(GLFW_RESIZABLE, false); auto window = ::glfwCreateWindow( 800, 600, "Impeller Playground (Press ESC or 'q' to quit)", NULL, NULL); @@ -106,7 +110,13 @@ static void PlaygroundKeyCallback(GLFWwindow* window, Surface surface(desc); - if (!renderer_.Render(surface, render_callback)) { + Renderer::RenderCallback wrapped_callback = + [render_callback](const auto& surface, auto& pass) { + pass.SetLabel("Playground Main Render Pass"); + return render_callback(surface, pass); + }; + + if (!renderer_.Render(surface, wrapped_callback)) { FML_LOG(ERROR) << "Could not render into the surface."; return false; } diff --git a/impeller/primitives/primitives_unittests.mm b/impeller/primitives/primitives_unittests.mm index 922c7ad8866f5..16e1c2e0e5153 100644 --- a/impeller/primitives/primitives_unittests.mm +++ b/impeller/primitives/primitives_unittests.mm @@ -45,8 +45,6 @@ ASSERT_TRUE(vertex_buffer); Renderer::RenderCallback callback = [&](const Surface& surface, RenderPass& pass) { - pass.SetLabel("EntityRenderer Render Pass"); - BoxVertexShader::UniformBuffer uniforms; uniforms.mvp = Matrix::MakeOrthographic(surface.GetSize()); From 6f0c5ea46f2fb30078f4948f6544d8a23c75ea2a Mon Sep 17 00:00:00 2001 From: Chinmay Garde Date: Sat, 3 Jul 2021 15:46:04 -0700 Subject: [PATCH 118/433] Minor: Better playground title. --- impeller/playground/playground.mm | 16 +++++++++++++--- 1 file changed, 13 insertions(+), 3 deletions(-) diff --git a/impeller/playground/playground.mm b/impeller/playground/playground.mm index 8571633dbae84..eb564748c3eb2 100644 --- a/impeller/playground/playground.mm +++ b/impeller/playground/playground.mm @@ -2,13 +2,15 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -#include "impeller/playground/playground.h" +#include + #include "flutter/fml/paths.h" #include "flutter/testing/testing.h" #include "impeller/compositor/context.h" #include "impeller/compositor/render_pass.h" #include "impeller/compositor/renderer.h" #include "impeller/compositor/surface.h" +#include "impeller/playground/playground.h" #define GLFW_INCLUDE_NONE #import "third_party/glfw/include/GLFW/glfw3.h" @@ -46,6 +48,13 @@ static void PlaygroundKeyCallback(GLFWwindow* window, } } +static std::string GetWindowTitle(const std::string& test_name) { + std::stringstream stream; + stream << "Impeller Playground for '" << test_name + << "' (Press ESC or 'q' to quit)"; + return stream.str(); +} + bool Playground::OpenPlaygroundHere(Renderer::RenderCallback render_callback) { if (!render_callback) { return true; @@ -66,8 +75,9 @@ static void PlaygroundKeyCallback(GLFWwindow* window, // wrong. ::glfwWindowHint(GLFW_RESIZABLE, false); - auto window = ::glfwCreateWindow( - 800, 600, "Impeller Playground (Press ESC or 'q' to quit)", NULL, NULL); + auto window_title = GetWindowTitle(flutter::testing::GetCurrentTestName()); + auto window = + ::glfwCreateWindow(1920, 1080, window_title.c_str(), NULL, NULL); if (!window) { return false; } From d36edd1aa4ab8016152467a3230e07a32de1e555 Mon Sep 17 00:00:00 2001 From: Chinmay Garde Date: Mon, 5 Jul 2021 14:37:11 -0700 Subject: [PATCH 119/433] Wire up texture creation from buffers. --- impeller/BUILD.gn | 2 +- impeller/compositor/BUILD.gn | 6 ++- impeller/compositor/device_buffer.h | 4 ++ impeller/compositor/device_buffer.mm | 31 ++++++++++++- .../compositor/device_buffer_unittests.mm | 15 ++++++ impeller/compositor/formats.h | 17 +++++++ impeller/compositor/texture.h | 28 +++++++++++ impeller/geometry/BUILD.gn | 9 ++++ impeller/geometry/geometry_unittests.cc | 23 ++++++++++ impeller/geometry/size.h | 46 +++++++++++++------ impeller/primitives/primitives_unittests.mm | 2 +- 11 files changed, 164 insertions(+), 19 deletions(-) create mode 100644 impeller/compositor/device_buffer_unittests.mm create mode 100644 impeller/geometry/geometry_unittests.cc diff --git a/impeller/BUILD.gn b/impeller/BUILD.gn index c7aeb4641fdef..c87d59175baee 100644 --- a/impeller/BUILD.gn +++ b/impeller/BUILD.gn @@ -27,8 +27,8 @@ executable("impeller_unittests") { "compositor:compositor_unittests", "entity:entity_unittests", "fixtures", + "geometry:geometry_unittests", "playground", "primitives:primitives_unittests", - "//flutter/testing", ] } diff --git a/impeller/compositor/BUILD.gn b/impeller/compositor/BUILD.gn index 310417449052d..9e5aa68940133 100644 --- a/impeller/compositor/BUILD.gn +++ b/impeller/compositor/BUILD.gn @@ -76,10 +76,14 @@ impeller_component("compositor") { source_set("compositor_unittests") { testonly = true - sources = [ "host_buffer_unittests.mm" ] + sources = [ + "device_buffer_unittests.mm", + "host_buffer_unittests.mm", + ] deps = [ ":compositor", + "../playground", "//flutter/testing:testing_lib", ] } diff --git a/impeller/compositor/device_buffer.h b/impeller/compositor/device_buffer.h index cb0f94919956b..f8c93b09c2c94 100644 --- a/impeller/compositor/device_buffer.h +++ b/impeller/compositor/device_buffer.h @@ -14,6 +14,7 @@ #include "impeller/compositor/buffer.h" #include "impeller/compositor/buffer_view.h" #include "impeller/compositor/range.h" +#include "impeller/compositor/texture.h" namespace impeller { @@ -26,6 +27,9 @@ class DeviceBuffer final : public Buffer, Range source_range, size_t offset = 0u); + std::shared_ptr MakeTexture(TextureDescriptor desc, + size_t offset = 0u) const; + id GetMTLBuffer() const; bool SetLabel(const std::string& label); diff --git a/impeller/compositor/device_buffer.mm b/impeller/compositor/device_buffer.mm index ff4a1a30888c7..364e4063cff4b 100644 --- a/impeller/compositor/device_buffer.mm +++ b/impeller/compositor/device_buffer.mm @@ -4,7 +4,8 @@ #include "impeller/compositor/device_buffer.h" -#include +#include "flutter/fml/logging.h" +#include "impeller/compositor/formats_metal.h" namespace impeller { @@ -17,6 +18,34 @@ return buffer_; } +std::shared_ptr DeviceBuffer::MakeTexture(TextureDescriptor desc, + size_t offset) const { + if (!desc.IsValid() || !buffer_) { + return nullptr; + } + + // Avoid overruns. + if (offset + desc.GetSizeOfBaseMipLevel() > size_) { + FML_DLOG(ERROR) << "Avoiding buffer overrun when creating texture."; + return nullptr; + } + + auto mtl_desc = [[MTLTextureDescriptor alloc] init]; + mtl_desc.pixelFormat = ToMTLPixelFormat(desc.format); + mtl_desc.width = desc.size.width; + mtl_desc.height = desc.size.height; + mtl_desc.mipmapLevelCount = desc.mip_count; + + auto texture = [buffer_ newTextureWithDescriptor:mtl_desc + offset:offset + bytesPerRow:desc.GetBytesPerRow()]; + if (!texture) { + return nullptr; + } + + return std::make_shared(texture); +} + [[nodiscard]] bool DeviceBuffer::CopyHostBuffer(const uint8_t* source, Range source_range, size_t offset) { diff --git a/impeller/compositor/device_buffer_unittests.mm b/impeller/compositor/device_buffer_unittests.mm new file mode 100644 index 0000000000000..a502b36f56555 --- /dev/null +++ b/impeller/compositor/device_buffer_unittests.mm @@ -0,0 +1,15 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "flutter/testing/testing.h" +#include "impeller/compositor/device_buffer.h" +#include "impeller/playground/playground.h" + +namespace impeller { +namespace testing { + +using DeviceBufferTest = Playground; + +} // namespace testing +} // namespace impeller diff --git a/impeller/compositor/formats.h b/impeller/compositor/formats.h index 0037fd8702237..56fc305ab488d 100644 --- a/impeller/compositor/formats.h +++ b/impeller/compositor/formats.h @@ -93,6 +93,23 @@ enum class ColorWriteMask : uint64_t { kAll = kRed | kGreen | kBlue, }; +constexpr size_t BytesPerPixelForPixelFormat(PixelFormat format) { + switch (format) { + case PixelFormat::kUnknown: + return 0u; + case PixelFormat::kPixelFormat_B8G8R8A8_UNormInt: + return 4u; + case PixelFormat::kPixelFormat_B8G8R8A8_UNormInt_SRGB: + return 4u; + case PixelFormat::kPixelFormat_D32_Float_S8_UNormInt: + // This is an esoteric format and implementations may use 64 bits. + // Impeller doesn't work with these natively and this return is only here + // for completeness. The 40 bits is as documented. + return 5u; + } + return 0u; +} + struct ColorAttachmentDescriptor { PixelFormat format = PixelFormat::kUnknown; bool blending_enabled = false; diff --git a/impeller/compositor/texture.h b/impeller/compositor/texture.h index ee0e8c9ba84ef..e64d2c7dfcfbc 100644 --- a/impeller/compositor/texture.h +++ b/impeller/compositor/texture.h @@ -7,10 +7,38 @@ #include #include "flutter/fml/macros.h" +#include "impeller/compositor/formats.h" #include "impeller/geometry/size.h" namespace impeller { +struct TextureDescriptor { + PixelFormat format = PixelFormat::kUnknown; + ISize size; + size_t mip_count = 1u; // Size::MipCount is usually appropriate. + + constexpr size_t GetSizeOfBaseMipLevel() const { + if (!IsValid()) { + return 0u; + } + return size.Area() * BytesPerPixelForPixelFormat(format); + } + + constexpr size_t GetBytesPerRow() const { + if (!IsValid()) { + return 0u; + } + return size.width * BytesPerPixelForPixelFormat(format); + } + + bool IsValid() const { + return format != PixelFormat::kUnknown && // + size.IsPositive() && // + mip_count >= 1u // + ; + } +}; + class Texture { public: Texture(id texture); diff --git a/impeller/geometry/BUILD.gn b/impeller/geometry/BUILD.gn index c18b8ff5d86f0..2d6d5a22a5466 100644 --- a/impeller/geometry/BUILD.gn +++ b/impeller/geometry/BUILD.gn @@ -31,3 +31,12 @@ impeller_component("geometry") { "vector.h", ] } + +impeller_component("geometry_unittests") { + testonly = true + sources = [ "geometry_unittests.cc" ] + deps = [ + ":geometry", + "//flutter/testing", + ] +} diff --git a/impeller/geometry/geometry_unittests.cc b/impeller/geometry/geometry_unittests.cc new file mode 100644 index 0000000000000..bed8c9987f3a7 --- /dev/null +++ b/impeller/geometry/geometry_unittests.cc @@ -0,0 +1,23 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "flutter/testing/testing.h" +#include "impeller/geometry/size.h" + +namespace impeller { +namespace testing { + +TEST(GeometryTest, CanGenerateMipCounts) { + ASSERT_EQ((Size{128, 128}.MipCount()), 7u); + ASSERT_EQ((Size{128, 256}.MipCount()), 8u); + ASSERT_EQ((Size{128, 130}.MipCount()), 8u); + ASSERT_EQ((Size{128, 257}.MipCount()), 9u); + ASSERT_EQ((Size{257, 128}.MipCount()), 9u); + ASSERT_EQ((Size{128, 0}.MipCount()), 1u); + ASSERT_EQ((Size{128, -25}.MipCount()), 1u); + ASSERT_EQ((Size{-128, 25}.MipCount()), 1u); +} + +} // namespace testing +} // namespace impeller diff --git a/impeller/geometry/size.h b/impeller/geometry/size.h index 40fd2c673b290..ac32ffdd9ff26 100644 --- a/impeller/geometry/size.h +++ b/impeller/geometry/size.h @@ -4,6 +4,7 @@ #pragma once +#include #include #include @@ -11,60 +12,75 @@ namespace impeller { -struct Size { - Scalar width = 0.0; - Scalar height = 0.0; +template +struct TSize { + using Type = T; - constexpr Size() {} + Type width = {}; + Type height = {}; - constexpr Size(Scalar width, Scalar height) : width(width), height(height) {} + constexpr TSize() {} - static constexpr Size Infinite() { - return Size{std::numeric_limits::max(), - std::numeric_limits::max()}; + constexpr TSize(Type width, Type height) : width(width), height(height) {} + + static constexpr TSize Infinite() { + return TSize{std::numeric_limits::max(), + std::numeric_limits::max()}; } - constexpr Size operator*(Scalar scale) const { + constexpr TSize operator*(Type scale) const { return {width * scale, height * scale}; } - constexpr bool operator==(const Size& s) const { + constexpr bool operator==(const TSize& s) const { return s.width == width && s.height == height; } - constexpr bool operator!=(const Size& s) const { + constexpr bool operator!=(const TSize& s) const { return s.width != width || s.height != height; } - constexpr Size operator+(const Size& s) const { + constexpr TSize operator+(const TSize& s) const { return {width + s.width, height + s.height}; } - constexpr Size operator-(const Size& s) const { + constexpr TSize operator-(const TSize& s) const { return {width - s.width, height - s.height}; } - constexpr Size Union(const Size& o) const { + constexpr TSize Union(const TSize& o) const { return { std::max(width, o.width), std::max(height, o.height), }; } - constexpr Size Intersection(const Size& o) const { + constexpr TSize Intersection(const TSize& o) const { return { std::min(width, o.width), std::min(height, o.height), }; } + constexpr Type Area() const { return width * height; } + constexpr bool IsZero() const { return width * height == 0.0; } constexpr bool IsPositive() const { return width * height > 0.0; } constexpr bool IsEmpty() { return !IsPositive(); } + + constexpr size_t MipCount() const { + if (!IsPositive()) { + return 1u; + } + return std::max(ceil(log2(width)), ceil(log2(height))); + } }; +using Size = TSize; +using ISize = TSize; + static_assert(sizeof(Size) == 2 * sizeof(Scalar)); } // namespace impeller diff --git a/impeller/primitives/primitives_unittests.mm b/impeller/primitives/primitives_unittests.mm index 16e1c2e0e5153..bd65d6f28bc61 100644 --- a/impeller/primitives/primitives_unittests.mm +++ b/impeller/primitives/primitives_unittests.mm @@ -65,7 +65,7 @@ } return true; }; - OpenPlaygroundHere(callback); + // OpenPlaygroundHere(callback); } } // namespace testing From 7092b1d8315e55c4c4973dcf99c8a35bf9ce9fe4 Mon Sep 17 00:00:00 2001 From: Chinmay Garde Date: Mon, 5 Jul 2021 15:00:22 -0700 Subject: [PATCH 120/433] Add support for memory render targets. --- impeller/compositor/allocator.h | 30 +++++++++++++++++++++++++++- impeller/compositor/allocator.mm | 29 +++++++++++++++++++++++++-- impeller/compositor/device_buffer.mm | 10 ++++++++-- 3 files changed, 64 insertions(+), 5 deletions(-) diff --git a/impeller/compositor/allocator.h b/impeller/compositor/allocator.h index a494b70dd8617..86c9a08e51f37 100644 --- a/impeller/compositor/allocator.h +++ b/impeller/compositor/allocator.h @@ -12,9 +12,35 @@ namespace impeller { +//------------------------------------------------------------------------------ +/// @brief Specified where the allocation resides and how it is used. +/// enum class StorageMode { - kHostCoherent, + //---------------------------------------------------------------------------- + /// Allocations can be mapped onto the hosts address space and also be used by + /// the device. + /// + kHostVisible, + //---------------------------------------------------------------------------- + /// Allocations can only be used by the device. This location is optimal for + /// use by the device. If the host needs to access these allocations, the + /// transfer queue must be used to transfer this allocation onto the a host + /// visible buffer. + /// kDevicePrivate, + //---------------------------------------------------------------------------- + /// Used by the device for temporary render targets. These allocations cannot + /// be transferred from and to other allocations using the transfer queue. + /// Render pass cannot initialize the contents of these buffers using load and + /// store actions. + /// + /// These allocations reside in tile memory which has higher bandwidth, lower + /// latency and lower power consumption. The total device memory usage is + /// also lower as a separate allocation does not need to be created in + /// device memory. Prefer using these allocations for intermediates like depth + /// and stencil buffers. + /// + kDeviceTransient, }; class Context; @@ -31,6 +57,8 @@ class Allocator { std::shared_ptr CreateBufferWithCopy(const uint8_t* buffer, size_t length); + static bool RequiresExplicitHostSynchronization(StorageMode mode); + private: friend class Context; diff --git a/impeller/compositor/allocator.mm b/impeller/compositor/allocator.mm index 2cd0f4f57eb72..5d86910986235 100644 --- a/impeller/compositor/allocator.mm +++ b/impeller/compositor/allocator.mm @@ -4,6 +4,7 @@ #include "impeller/compositor/allocator.h" +#include "flutter/fml/build_config.h" #include "flutter/fml/logging.h" #include "impeller/compositor/buffer.h" #include "impeller/compositor/device_buffer.h" @@ -27,17 +28,41 @@ static MTLResourceOptions ResourceOptionsFromStorageType(StorageMode type) { switch (type) { - case StorageMode::kHostCoherent: + case StorageMode::kHostVisible: +#if OS_IOS + return MTLStorageModeShared; +#else return MTLResourceStorageModeManaged; +#endif case StorageMode::kDevicePrivate: return MTLResourceStorageModePrivate; + case StorageMode::kDeviceTransient: +#if OS_IOS + return MTLStorageModeMemoryless; +#else + return MTLResourceStorageModePrivate; +#endif } } +bool Allocator::RequiresExplicitHostSynchronization(StorageMode mode) { + if (mode != StorageMode::kHostVisible) { + return false; + } + +#if OS_IOS + // StorageMode::kHostVisible is MTLStorageModeShared already. + return false; +#else + // StorageMode::kHostVisible is MTLResourceStorageModeManaged. + return true; +#endif +} + std::shared_ptr Allocator::CreateBufferWithCopy( const uint8_t* buffer, size_t length) { - auto new_buffer = CreateBuffer(StorageMode::kHostCoherent, length); + auto new_buffer = CreateBuffer(StorageMode::kHostVisible, length); if (!new_buffer) { return nullptr; diff --git a/impeller/compositor/device_buffer.mm b/impeller/compositor/device_buffer.mm index 364e4063cff4b..157814f22bd0b 100644 --- a/impeller/compositor/device_buffer.mm +++ b/impeller/compositor/device_buffer.mm @@ -49,6 +49,11 @@ [[nodiscard]] bool DeviceBuffer::CopyHostBuffer(const uint8_t* source, Range source_range, size_t offset) { + if (mode_ != StorageMode::kHostVisible) { + // One of the storage modes where a transfer queue must be used. + return false; + } + if (offset + source_range.length > size_) { // Out of bounds of this buffer. return false; @@ -57,13 +62,14 @@ auto dest = static_cast(buffer_.contents); if (!dest) { - // Probably StorageMode::kDevicePrivate. return false; } ::memmove(dest + offset, source + source_range.offset, source_range.length); - [buffer_ didModifyRange:NSMakeRange(offset, source_range.length)]; + if (Allocator::RequiresExplicitHostSynchronization(mode_)) { + [buffer_ didModifyRange:NSMakeRange(offset, source_range.length)]; + } return true; } From e015f4a62d614cbe4b3af17a55495c0ae1366536 Mon Sep 17 00:00:00 2001 From: Chinmay Garde Date: Mon, 5 Jul 2021 15:05:01 -0700 Subject: [PATCH 121/433] Add unit-test target stubs. --- impeller/BUILD.gn | 3 +++ impeller/base/BUILD.gn | 12 +++++++++--- impeller/image/BUILD.gn | 9 +++++++++ impeller/shader_glue/BUILD.gn | 9 +++++++++ 4 files changed, 30 insertions(+), 3 deletions(-) diff --git a/impeller/BUILD.gn b/impeller/BUILD.gn index c87d59175baee..15ce867a7cfa6 100644 --- a/impeller/BUILD.gn +++ b/impeller/BUILD.gn @@ -23,12 +23,15 @@ executable("impeller_unittests") { testonly = true deps = [ + "base:base_unittests", "compiler:compiler_unittests", "compositor:compositor_unittests", "entity:entity_unittests", "fixtures", "geometry:geometry_unittests", + "image:image_unittests", "playground", "primitives:primitives_unittests", + "shader_glue:shader_glue_unittests", ] } diff --git a/impeller/base/BUILD.gn b/impeller/base/BUILD.gn index 3f875529f1408..d7be2840bb102 100644 --- a/impeller/base/BUILD.gn +++ b/impeller/base/BUILD.gn @@ -5,9 +5,6 @@ import("../tools/impeller.gni") impeller_component("base") { - # Only the umbrella header may be imported. - public = [ "base.h" ] - sources = [ "base.h", "config.h", @@ -15,3 +12,12 @@ impeller_component("base") { "strings.h", ] } + +impeller_component("base_unittests") { + testonly = true + sources = [] + deps = [ + ":base", + "//flutter/testing", + ] +} diff --git a/impeller/image/BUILD.gn b/impeller/image/BUILD.gn index aff4b5104c3d9..a54fcb97330bd 100644 --- a/impeller/image/BUILD.gn +++ b/impeller/image/BUILD.gn @@ -16,3 +16,12 @@ impeller_component("image") { public_deps = [ "../geometry" ] } + +impeller_component("image_unittests") { + testonly = true + sources = [] + deps = [ + ":image", + "//flutter/testing", + ] +} diff --git a/impeller/shader_glue/BUILD.gn b/impeller/shader_glue/BUILD.gn index f9802b3d153d5..259ab959de89a 100644 --- a/impeller/shader_glue/BUILD.gn +++ b/impeller/shader_glue/BUILD.gn @@ -20,3 +20,12 @@ impeller_component("shader_glue") { deps = [ "../geometry" ] } + +impeller_component("shader_glue_unittests") { + testonly = true + sources = [] + deps = [ + ":shader_glue", + "//flutter/testing", + ] +} From 232d848580fa36c0e7c58e150cb7ae6d07e4e72f Mon Sep 17 00:00:00 2001 From: Chinmay Garde Date: Mon, 5 Jul 2021 16:24:18 -0700 Subject: [PATCH 122/433] Create textures from buffers. --- impeller/compositor/BUILD.gn | 2 + impeller/compositor/allocator.h | 4 ++ impeller/compositor/allocator.mm | 5 +++ impeller/compositor/device_buffer.h | 12 +++++ impeller/compositor/device_buffer.mm | 4 +- impeller/compositor/texture.h | 28 +----------- impeller/compositor/texture_descriptor.h | 45 +++++++++++++++++++ impeller/compositor/texture_descriptor.mm | 50 +++++++++++++++++++++ impeller/image/image.cc | 20 ++++----- impeller/image/image.h | 2 +- impeller/image/image_result.cc | 20 +++++---- impeller/image/image_result.h | 12 ++--- impeller/playground/playground.mm | 3 +- impeller/primitives/BUILD.gn | 1 + impeller/primitives/box.frag | 5 ++- impeller/primitives/box.vert | 3 ++ impeller/primitives/primitives_unittests.mm | 30 ++++++++++--- 17 files changed, 183 insertions(+), 63 deletions(-) create mode 100644 impeller/compositor/texture_descriptor.h create mode 100644 impeller/compositor/texture_descriptor.mm diff --git a/impeller/compositor/BUILD.gn b/impeller/compositor/BUILD.gn index 9e5aa68940133..b9a5e5dc964ab 100644 --- a/impeller/compositor/BUILD.gn +++ b/impeller/compositor/BUILD.gn @@ -56,6 +56,8 @@ impeller_component("compositor") { "surface.mm", "texture.h", "texture.mm", + "texture_descriptor.h", + "texture_descriptor.mm", "vertex_buffer.h", "vertex_buffer.mm", "vertex_buffer_builder.h", diff --git a/impeller/compositor/allocator.h b/impeller/compositor/allocator.h index 86c9a08e51f37..9f306180b8ead 100644 --- a/impeller/compositor/allocator.h +++ b/impeller/compositor/allocator.h @@ -9,6 +9,7 @@ #include #include "flutter/fml/macros.h" +#include "flutter/fml/mapping.h" namespace impeller { @@ -57,6 +58,9 @@ class Allocator { std::shared_ptr CreateBufferWithCopy(const uint8_t* buffer, size_t length); + std::shared_ptr CreateBufferWithCopy( + const fml::Mapping& mapping); + static bool RequiresExplicitHostSynchronization(StorageMode mode); private: diff --git a/impeller/compositor/allocator.mm b/impeller/compositor/allocator.mm index 5d86910986235..a94d171d9f284 100644 --- a/impeller/compositor/allocator.mm +++ b/impeller/compositor/allocator.mm @@ -77,6 +77,11 @@ static MTLResourceOptions ResourceOptionsFromStorageType(StorageMode type) { return new_buffer; } +std::shared_ptr Allocator::CreateBufferWithCopy( + const fml::Mapping& mapping) { + return CreateBufferWithCopy(mapping.GetMapping(), mapping.GetSize()); +} + std::shared_ptr Allocator::CreateBuffer(StorageMode mode, size_t length) { auto buffer = diff --git a/impeller/compositor/device_buffer.h b/impeller/compositor/device_buffer.h index f8c93b09c2c94..6918d04dbbb99 100644 --- a/impeller/compositor/device_buffer.h +++ b/impeller/compositor/device_buffer.h @@ -27,6 +27,18 @@ class DeviceBuffer final : public Buffer, Range source_range, size_t offset = 0u); + //---------------------------------------------------------------------------- + /// @brief Create a texture whose contents are the same as that of this + /// buffer. Changes to either the contents of the texture or the + /// buffer will be shared. When using buffer backed textures, + /// implementations may have to disable certain optimizations. + /// + /// @param[in] desc The description of the texture. + /// @param[in] offset The offset of the texture data within buffer. + /// + /// @return The texture whose contents are backed by (a part of) this + /// buffer. + /// std::shared_ptr MakeTexture(TextureDescriptor desc, size_t offset = 0u) const; diff --git a/impeller/compositor/device_buffer.mm b/impeller/compositor/device_buffer.mm index 157814f22bd0b..cbc84aa076850 100644 --- a/impeller/compositor/device_buffer.mm +++ b/impeller/compositor/device_buffer.mm @@ -65,7 +65,9 @@ return false; } - ::memmove(dest + offset, source + source_range.offset, source_range.length); + if (source) { + ::memmove(dest + offset, source + source_range.offset, source_range.length); + } if (Allocator::RequiresExplicitHostSynchronization(mode_)) { [buffer_ didModifyRange:NSMakeRange(offset, source_range.length)]; diff --git a/impeller/compositor/texture.h b/impeller/compositor/texture.h index e64d2c7dfcfbc..f1767b90fb532 100644 --- a/impeller/compositor/texture.h +++ b/impeller/compositor/texture.h @@ -8,37 +8,11 @@ #include "flutter/fml/macros.h" #include "impeller/compositor/formats.h" +#include "impeller/compositor/texture_descriptor.h" #include "impeller/geometry/size.h" namespace impeller { -struct TextureDescriptor { - PixelFormat format = PixelFormat::kUnknown; - ISize size; - size_t mip_count = 1u; // Size::MipCount is usually appropriate. - - constexpr size_t GetSizeOfBaseMipLevel() const { - if (!IsValid()) { - return 0u; - } - return size.Area() * BytesPerPixelForPixelFormat(format); - } - - constexpr size_t GetBytesPerRow() const { - if (!IsValid()) { - return 0u; - } - return size.width * BytesPerPixelForPixelFormat(format); - } - - bool IsValid() const { - return format != PixelFormat::kUnknown && // - size.IsPositive() && // - mip_count >= 1u // - ; - } -}; - class Texture { public: Texture(id texture); diff --git a/impeller/compositor/texture_descriptor.h b/impeller/compositor/texture_descriptor.h new file mode 100644 index 0000000000000..c3b7f653fbd31 --- /dev/null +++ b/impeller/compositor/texture_descriptor.h @@ -0,0 +1,45 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#pragma once + +#include + +#include "impeller/compositor/formats.h" +#include "impeller/geometry/size.h" +#include "impeller/image/image.h" + +namespace impeller { + +struct TextureDescriptor { + PixelFormat format = PixelFormat::kUnknown; + ISize size; + size_t mip_count = 1u; // Size::MipCount is usually appropriate. + + constexpr size_t GetSizeOfBaseMipLevel() const { + if (!IsValid()) { + return 0u; + } + return size.Area() * BytesPerPixelForPixelFormat(format); + } + + constexpr size_t GetBytesPerRow() const { + if (!IsValid()) { + return 0u; + } + return size.width * BytesPerPixelForPixelFormat(format); + } + + bool IsValid() const { + return format != PixelFormat::kUnknown && // + size.IsPositive() && // + mip_count >= 1u // + ; + } + + static std::optional MakeFromImageResult( + const ImageResult& result); +}; + +} // namespace impeller diff --git a/impeller/compositor/texture_descriptor.mm b/impeller/compositor/texture_descriptor.mm new file mode 100644 index 0000000000000..18021e8e78be5 --- /dev/null +++ b/impeller/compositor/texture_descriptor.mm @@ -0,0 +1,50 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "impeller/compositor/texture_descriptor.h" + +namespace impeller { + +std::optional FormatForImageResultComponents( + ImageResult::Components comp) { + switch (comp) { + case ImageResult::Components::Invalid: + return std::nullopt; + case ImageResult::Components::Grey: + return std::nullopt; + case ImageResult::Components::GreyAlpha: + return std::nullopt; + case ImageResult::Components::RGB: + return std::nullopt; + case ImageResult::Components::RGBA: + return PixelFormat::kPixelFormat_B8G8R8A8_UNormInt; + } + return std::nullopt; +} + +std::optional TextureDescriptor::MakeFromImageResult( + const ImageResult& result) { + if (!result.IsValid()) { + return std::nullopt; + } + + const auto pixel_format = + FormatForImageResultComponents(result.GetComponents()); + if (!pixel_format.has_value()) { + return std::nullopt; + } + + TextureDescriptor desc; + desc.format = pixel_format.value(); + desc.size = result.GetSize(); + desc.mip_count = result.GetSize().MipCount(); + + if (!desc.IsValid()) { + return std::nullopt; + } + + return desc; +} + +} // namespace impeller diff --git a/impeller/image/image.cc b/impeller/image/image.cc index aa0f1f9de1d9b..edcb4b3bf137e 100644 --- a/impeller/image/image.cc +++ b/impeller/image/image.cc @@ -22,19 +22,19 @@ ImageResult Image::Decode() const { int comps = 0; stbi_uc* decoded = - stbi_load_from_memory(source_->GetMapping(), // Source Data - source_->GetSize(), // Source Data Size - &width, // Out: Width - &height, // Out: Height - &comps, // Out: Components - STBI_default); + ::stbi_load_from_memory(source_->GetMapping(), // Source Data + source_->GetSize(), // Source Data Size + &width, // Out: Width + &height, // Out: Height + &comps, // Out: Components + STBI_default); if (decoded == nullptr) { FML_LOG(ERROR) << "Could not decode image from host memory."; return {}; } - auto destinationAllocation = std::make_shared( + auto dest_allocation = std::make_shared( decoded, // bytes width * height * comps * sizeof(stbi_uc), // byte size [](const uint8_t* data, size_t size) { @@ -71,9 +71,9 @@ ImageResult Image::Decode() const { } return ImageResult{ - Size{static_cast(width), static_cast(height)}, // size - components, // components - std::move(destinationAllocation) // allocation + ISize{width, height}, // size + components, // components + std::move(dest_allocation) // allocation }; } diff --git a/impeller/image/image.h b/impeller/image/image.h index 4bd0d32d60ffa..612e76763bd45 100644 --- a/impeller/image/image.h +++ b/impeller/image/image.h @@ -19,7 +19,7 @@ class Image { ~Image(); - ImageResult Decode() const; + [[nodiscard]] ImageResult Decode() const; bool IsValid() const; diff --git a/impeller/image/image_result.cc b/impeller/image/image_result.cc index 937a3b946ea80..a778de0e388e7 100644 --- a/impeller/image/image_result.cc +++ b/impeller/image/image_result.cc @@ -8,21 +8,23 @@ namespace impeller { ImageResult::ImageResult() = default; -ImageResult::ImageResult(Size size, +ImageResult::ImageResult(ISize size, Components components, std::shared_ptr allocation) - : success_(true), - size_(size), - components_(components), - allocation_(std::move(allocation)) {} + : size_(size), components_(components), allocation_(std::move(allocation)) { + if (!allocation_ || !size.IsPositive()) { + return; + } + is_valid_ = true; +} ImageResult::~ImageResult() = default; -bool ImageResult::WasSuccessful() const { - return success_; +bool ImageResult::IsValid() const { + return is_valid_; } -const Size& ImageResult::GetSize() const { +const ISize& ImageResult::GetSize() const { return size_; } @@ -30,7 +32,7 @@ ImageResult::Components ImageResult::GetComponents() const { return components_; } -const std::shared_ptr& ImageResult::Allocation() const { +const std::shared_ptr& ImageResult::GetAllocation() const { return allocation_; } diff --git a/impeller/image/image_result.h b/impeller/image/image_result.h index 7f5e4b7e449a7..7b0c23354a8ab 100644 --- a/impeller/image/image_result.h +++ b/impeller/image/image_result.h @@ -24,25 +24,25 @@ class ImageResult { ImageResult(); - ImageResult(Size size, + ImageResult(ISize size, Components components, std::shared_ptr allocation); ~ImageResult(); - const Size& GetSize() const; + const ISize& GetSize() const; - bool WasSuccessful() const; + bool IsValid() const; Components GetComponents() const; - const std::shared_ptr& Allocation() const; + const std::shared_ptr& GetAllocation() const; private: - bool success_ = false; - Size size_; + ISize size_; Components components_ = Components::Invalid; std::shared_ptr allocation_; + bool is_valid_ = false; FML_DISALLOW_COPY_AND_ASSIGN(ImageResult); }; diff --git a/impeller/playground/playground.mm b/impeller/playground/playground.mm index eb564748c3eb2..2fe879e8b8c77 100644 --- a/impeller/playground/playground.mm +++ b/impeller/playground/playground.mm @@ -76,8 +76,7 @@ static void PlaygroundKeyCallback(GLFWwindow* window, ::glfwWindowHint(GLFW_RESIZABLE, false); auto window_title = GetWindowTitle(flutter::testing::GetCurrentTestName()); - auto window = - ::glfwCreateWindow(1920, 1080, window_title.c_str(), NULL, NULL); + auto window = ::glfwCreateWindow(1024, 768, window_title.c_str(), NULL, NULL); if (!window) { return false; } diff --git a/impeller/primitives/BUILD.gn b/impeller/primitives/BUILD.gn index 18acdaddd6426..8abe52c1ab0f4 100644 --- a/impeller/primitives/BUILD.gn +++ b/impeller/primitives/BUILD.gn @@ -29,6 +29,7 @@ impeller_component("primitives_unittests") { sources = [ "primitives_unittests.mm" ] deps = [ ":primitives", + "../image", "../playground", ] } diff --git a/impeller/primitives/box.frag b/impeller/primitives/box.frag index 0263c965f294b..c1d0500b372c9 100644 --- a/impeller/primitives/box.frag +++ b/impeller/primitives/box.frag @@ -3,9 +3,12 @@ // found in the LICENSE file. in vec4 color; +in vec2 interporlated_texture_coordinates; out vec4 frag_color; +uniform sampler2D contents_texture; + void main() { - frag_color = color; + frag_color = texture(contents_texture, interporlated_texture_coordinates); } diff --git a/impeller/primitives/box.vert b/impeller/primitives/box.vert index 431c4c04d1f07..87d0e537e756c 100644 --- a/impeller/primitives/box.vert +++ b/impeller/primitives/box.vert @@ -8,10 +8,13 @@ uniform UniformBuffer { in vec3 vertex_position; in vec4 vertex_color; +in vec2 texture_coordinates; out vec4 color; +out vec2 interporlated_texture_coordinates; void main() { gl_Position = uniforms.mvp * vec4(vertex_position, 1.0); color = vertex_color; + interporlated_texture_coordinates = texture_coordinates; } diff --git a/impeller/primitives/primitives_unittests.mm b/impeller/primitives/primitives_unittests.mm index bd65d6f28bc61..a87b9b19f0fa1 100644 --- a/impeller/primitives/primitives_unittests.mm +++ b/impeller/primitives/primitives_unittests.mm @@ -10,6 +10,7 @@ #include "impeller/compositor/sampler_descriptor.h" #include "impeller/compositor/surface.h" #include "impeller/compositor/vertex_buffer_builder.h" +#include "impeller/image/image.h" #include "impeller/playground/playground.h" #include "impeller/primitives/box.frag.h" #include "impeller/primitives/box.vert.h" @@ -29,20 +30,37 @@ context->GetPipelineLibrary()->GetRenderPipeline(std::move(desc)).get(); ASSERT_TRUE(box_pipeline); + // Vertex buffer. VertexBufferBuilder vertex_builder; vertex_builder.SetLabel("Box"); vertex_builder.AddVertices({ - {{100, 100, 0.0}, {Color::Red()}}, // 1 - {{800, 100, 0.0}, {Color::Green()}}, // 2 - {{800, 800, 0.0}, {Color::Blue()}}, // 3 + {{100, 100, 0.0}, {Color::Red()}, {0.0, 0.0}}, // 1 + {{800, 100, 0.0}, {Color::Green()}, {1.0, 0.0}}, // 2 + {{800, 800, 0.0}, {Color::Blue()}, {1.0, 1.0}}, // 3 - {{100, 100, 0.0}, {Color::Cyan()}}, // 1 - {{800, 800, 0.0}, {Color::White()}}, // 3 - {{100, 800, 0.0}, {Color::Purple()}}, // 4 + {{100, 100, 0.0}, {Color::Cyan()}, {0.0, 0.0}}, // 1 + {{800, 800, 0.0}, {Color::White()}, {1.0, 1.0}}, // 3 + {{100, 800, 0.0}, {Color::Purple()}, {0.0, 1.0}}, // 4 }); auto vertex_buffer = vertex_builder.CreateVertexBuffer(*context->GetPermanentsAllocator()); ASSERT_TRUE(vertex_buffer); + + // Contents texture. + Image image(flutter::testing::OpenFixtureAsMapping("image.png")); + auto result = image.Decode(); + ASSERT_TRUE(result.IsValid()); + auto device_image_allocation = + context->GetPermanentsAllocator()->CreateBufferWithCopy( + *result.GetAllocation()); + device_image_allocation->SetLabel("Bay Bridge"); + ASSERT_TRUE(device_image_allocation); + auto texture_descriptor = TextureDescriptor::MakeFromImageResult(result); + ASSERT_TRUE(texture_descriptor.has_value()); + auto texture = + device_image_allocation->MakeTexture(texture_descriptor.value()); + ASSERT_TRUE(texture); + Renderer::RenderCallback callback = [&](const Surface& surface, RenderPass& pass) { BoxVertexShader::UniformBuffer uniforms; From ea5680aab16f9d7000baf31c11bc427d7392d68a Mon Sep 17 00:00:00 2001 From: Chinmay Garde Date: Tue, 6 Jul 2021 16:16:30 -0700 Subject: [PATCH 123/433] WIP on replacing texture contents. --- impeller/compositor/allocator.h | 5 ++ impeller/compositor/allocator.mm | 58 +++++++++++++++---- impeller/compositor/device_buffer.mm | 10 +--- impeller/compositor/formats_metal.h | 5 +- impeller/compositor/formats_metal.mm | 62 +++------------------ impeller/compositor/render_pass.h | 2 +- impeller/compositor/render_pass.mm | 2 +- impeller/compositor/surface.h | 4 +- impeller/compositor/surface.mm | 2 +- impeller/compositor/texture.h | 16 +++++- impeller/compositor/texture.mm | 36 ++++++++++-- impeller/geometry/matrix.h | 4 +- impeller/playground/playground.mm | 10 +++- impeller/primitives/primitives_unittests.mm | 17 +++--- 14 files changed, 132 insertions(+), 101 deletions(-) diff --git a/impeller/compositor/allocator.h b/impeller/compositor/allocator.h index 9f306180b8ead..4b3222026ebbb 100644 --- a/impeller/compositor/allocator.h +++ b/impeller/compositor/allocator.h @@ -10,6 +10,7 @@ #include "flutter/fml/macros.h" #include "flutter/fml/mapping.h" +#include "impeller/compositor/texture_descriptor.h" namespace impeller { @@ -46,6 +47,7 @@ enum class StorageMode { class Context; class DeviceBuffer; +class Texture; class Allocator { public: @@ -55,6 +57,9 @@ class Allocator { std::shared_ptr CreateBuffer(StorageMode mode, size_t length); + std::shared_ptr CreateTexture(StorageMode mode, + const TextureDescriptor& desc); + std::shared_ptr CreateBufferWithCopy(const uint8_t* buffer, size_t length); diff --git a/impeller/compositor/allocator.mm b/impeller/compositor/allocator.mm index a94d171d9f284..99cb0c1c1e1bc 100644 --- a/impeller/compositor/allocator.mm +++ b/impeller/compositor/allocator.mm @@ -8,6 +8,7 @@ #include "flutter/fml/logging.h" #include "impeller/compositor/buffer.h" #include "impeller/compositor/device_buffer.h" +#include "impeller/compositor/formats_metal.h" namespace impeller { @@ -26,11 +27,11 @@ return is_valid_; } -static MTLResourceOptions ResourceOptionsFromStorageType(StorageMode type) { +static MTLResourceOptions ToMTLResourceOptions(StorageMode type) { switch (type) { case StorageMode::kHostVisible: #if OS_IOS - return MTLStorageModeShared; + return MTLResourceStorageModeShared; #else return MTLResourceStorageModeManaged; #endif @@ -38,11 +39,33 @@ static MTLResourceOptions ResourceOptionsFromStorageType(StorageMode type) { return MTLResourceStorageModePrivate; case StorageMode::kDeviceTransient: #if OS_IOS - return MTLStorageModeMemoryless; + return MTLResourceStorageModeMemoryless; #else return MTLResourceStorageModePrivate; #endif } + + return MTLResourceStorageModePrivate; +} + +static MTLStorageMode ToMTLStorageMode(StorageMode mode) { + switch (mode) { + case StorageMode::kHostVisible: +#if OS_IOS + return MTLStorageModeShared; +#else + return MTLStorageModeManaged; +#endif + case StorageMode::kDevicePrivate: + return MTLStorageModePrivate; + case StorageMode::kDeviceTransient: +#if OS_IOS + return MTLStorageModeMemoryless; +#else + return MTLStorageModePrivate; +#endif + } + return MTLStorageModeShared; } bool Allocator::RequiresExplicitHostSynchronization(StorageMode mode) { @@ -59,6 +82,16 @@ static MTLResourceOptions ResourceOptionsFromStorageType(StorageMode type) { #endif } +std::shared_ptr Allocator::CreateBuffer(StorageMode mode, + size_t length) { + auto buffer = [device_ newBufferWithLength:length + options:ToMTLResourceOptions(mode)]; + if (!buffer) { + return nullptr; + } + return std::shared_ptr(new DeviceBuffer(buffer, length, mode)); +} + std::shared_ptr Allocator::CreateBufferWithCopy( const uint8_t* buffer, size_t length) { @@ -82,15 +115,20 @@ static MTLResourceOptions ResourceOptionsFromStorageType(StorageMode type) { return CreateBufferWithCopy(mapping.GetMapping(), mapping.GetSize()); } -std::shared_ptr Allocator::CreateBuffer(StorageMode mode, - size_t length) { - auto buffer = - [device_ newBufferWithLength:length - options:ResourceOptionsFromStorageType(mode)]; - if (!buffer) { +std::shared_ptr Allocator::CreateTexture( + StorageMode mode, + const TextureDescriptor& desc) { + if (!IsValid()) { return nullptr; } - return std::shared_ptr(new DeviceBuffer(buffer, length, mode)); + + auto mtl_texture_desc = ToMTLTextureDescriptor(desc); + mtl_texture_desc.storageMode = ToMTLStorageMode(mode); + auto texture = [device_ newTextureWithDescriptor:mtl_texture_desc]; + if (!texture) { + return nullptr; + } + return std::make_shared(desc, texture); } } // namespace impeller diff --git a/impeller/compositor/device_buffer.mm b/impeller/compositor/device_buffer.mm index cbc84aa076850..ceb8c17f7de65 100644 --- a/impeller/compositor/device_buffer.mm +++ b/impeller/compositor/device_buffer.mm @@ -30,20 +30,14 @@ return nullptr; } - auto mtl_desc = [[MTLTextureDescriptor alloc] init]; - mtl_desc.pixelFormat = ToMTLPixelFormat(desc.format); - mtl_desc.width = desc.size.width; - mtl_desc.height = desc.size.height; - mtl_desc.mipmapLevelCount = desc.mip_count; - - auto texture = [buffer_ newTextureWithDescriptor:mtl_desc + auto texture = [buffer_ newTextureWithDescriptor:ToMTLTextureDescriptor(desc) offset:offset bytesPerRow:desc.GetBytesPerRow()]; if (!texture) { return nullptr; } - return std::make_shared(texture); + return std::make_shared(desc, texture); } [[nodiscard]] bool DeviceBuffer::CopyHostBuffer(const uint8_t* source, diff --git a/impeller/compositor/formats_metal.h b/impeller/compositor/formats_metal.h index 08aa2a8fadc53..cea758a5b7cd9 100644 --- a/impeller/compositor/formats_metal.h +++ b/impeller/compositor/formats_metal.h @@ -10,6 +10,7 @@ #include "flutter/fml/macros.h" #include "impeller/compositor/formats.h" +#include "impeller/compositor/texture_descriptor.h" #include "impeller/geometry/color.h" namespace impeller { @@ -244,8 +245,6 @@ constexpr MTLClearColor ToMTLClearColor(const Color& color) { return MTLClearColorMake(color.red, color.green, color.blue, color.alpha); } -RenderPassDescriptor FromMTLRenderPassDescriptor(MTLRenderPassDescriptor*); - MTLRenderPipelineColorAttachmentDescriptor* ToMTLRenderPipelineColorAttachmentDescriptor( ColorAttachmentDescriptor descriptor); @@ -255,4 +254,6 @@ MTLDepthStencilDescriptor* ToMTLDepthStencilDescriptor( std::optional front, std::optional back); +MTLTextureDescriptor* ToMTLTextureDescriptor(const TextureDescriptor& desc); + } // namespace impeller diff --git a/impeller/compositor/formats_metal.mm b/impeller/compositor/formats_metal.mm index 1e9ea463f882c..cef3c06ba679c 100644 --- a/impeller/compositor/formats_metal.mm +++ b/impeller/compositor/formats_metal.mm @@ -77,61 +77,13 @@ return des; } -static bool ConfigureRenderPassAttachment( - RenderPassAttachment& attachment, - MTLRenderPassAttachmentDescriptor* desc) { - attachment.texture = std::make_shared(desc.texture); - attachment.load_action = FromMTLLoadAction(desc.loadAction); - attachment.store_action = FromMTLStoreAction(desc.storeAction); - return attachment; -} - -static ColorRenderPassAttachment FromMTLRenderPassColorAttachmentDescriptor( - MTLRenderPassColorAttachmentDescriptor* desc) { - ColorRenderPassAttachment attachment; - ConfigureRenderPassAttachment(attachment, desc); - auto clear = desc.clearColor; - attachment.clear_color = - Color{static_cast(clear.red), static_cast(clear.green), - static_cast(clear.blue), static_cast(clear.alpha)}; - return attachment; -} - -static DepthRenderPassAttachment FromMTLRenderPassDepthAttachmentDescriptor( - MTLRenderPassDepthAttachmentDescriptor* desc) { - DepthRenderPassAttachment attachment; - ConfigureRenderPassAttachment(attachment, desc); - attachment.clear_depth = desc.clearDepth; - return attachment; -} - -static StencilRenderPassAttachment FromMTLRenderPassStencilAttachmentDescriptor( - MTLRenderPassStencilAttachmentDescriptor* desc) { - StencilRenderPassAttachment attachment; - ConfigureRenderPassAttachment(attachment, desc); - attachment.clear_stencil = desc.clearStencil; - return attachment; -} - -RenderPassDescriptor FromMTLRenderPassDescriptor( - MTLRenderPassDescriptor* desc) { - RenderPassDescriptor result; - if (!desc) { - return result; - } - - // From https://developer.apple.com/metal/Metal-Feature-Set-Tables.pdf - constexpr size_t kMaxPossibleColorAttachments = 8u; - for (size_t i = 0; i < kMaxPossibleColorAttachments; i++) { - result.SetColorAttachment( - FromMTLRenderPassColorAttachmentDescriptor(desc.colorAttachments[i]), - i); - } - result.SetDepthAttachment( - FromMTLRenderPassDepthAttachmentDescriptor(desc.depthAttachment)); - result.SetStencilAttachment( - FromMTLRenderPassStencilAttachmentDescriptor(desc.stencilAttachment)); - return result; +MTLTextureDescriptor* ToMTLTextureDescriptor(const TextureDescriptor& desc) { + auto mtl_desc = [[MTLTextureDescriptor alloc] init]; + mtl_desc.pixelFormat = ToMTLPixelFormat(desc.format); + mtl_desc.width = desc.size.width; + mtl_desc.height = desc.size.height; + mtl_desc.mipmapLevelCount = desc.mip_count; + return mtl_desc; } } // namespace impeller diff --git a/impeller/compositor/render_pass.h b/impeller/compositor/render_pass.h index 7c0a089760e6b..70688bc3548bf 100644 --- a/impeller/compositor/render_pass.h +++ b/impeller/compositor/render_pass.h @@ -49,7 +49,7 @@ class RenderPassDescriptor { bool HasColorAttachment(size_t index) const; - std::optional GetColorAttachmentSize(size_t index) const; + std::optional GetColorAttachmentSize(size_t index) const; RenderPassDescriptor& SetColorAttachment(ColorRenderPassAttachment attachment, size_t index); diff --git a/impeller/compositor/render_pass.mm b/impeller/compositor/render_pass.mm index 5e77e88545035..206aecab551e1 100644 --- a/impeller/compositor/render_pass.mm +++ b/impeller/compositor/render_pass.mm @@ -25,7 +25,7 @@ return false; } -std::optional RenderPassDescriptor::GetColorAttachmentSize( +std::optional RenderPassDescriptor::GetColorAttachmentSize( size_t index) const { auto found = colors_.find(index); diff --git a/impeller/compositor/surface.h b/impeller/compositor/surface.h index edb0e50a13260..b71b209602d85 100644 --- a/impeller/compositor/surface.h +++ b/impeller/compositor/surface.h @@ -21,7 +21,7 @@ class Surface { ~Surface(); - const Size& GetSize() const; + const ISize& GetSize() const; bool IsValid() const; @@ -29,7 +29,7 @@ class Surface { private: RenderPassDescriptor desc_; - Size size_; + ISize size_; bool is_valid_ = false; diff --git a/impeller/compositor/surface.mm b/impeller/compositor/surface.mm index dfcd330d31517..6e16d21cf2552 100644 --- a/impeller/compositor/surface.mm +++ b/impeller/compositor/surface.mm @@ -21,7 +21,7 @@ Surface::~Surface() = default; -const Size& Surface::GetSize() const { +const ISize& Surface::GetSize() const { return size_; } diff --git a/impeller/compositor/texture.h b/impeller/compositor/texture.h index f1767b90fb532..a4fe87f818ad7 100644 --- a/impeller/compositor/texture.h +++ b/impeller/compositor/texture.h @@ -4,6 +4,8 @@ #pragma once +#include + #include #include "flutter/fml/macros.h" @@ -15,18 +17,26 @@ namespace impeller { class Texture { public: - Texture(id texture); + Texture(TextureDescriptor desc, id texture); ~Texture(); + void SetLabel(const std::string_view& label); + + [[nodiscard]] bool SetContents(const uint8_t* contents, + size_t length, + size_t mip_level = 0u); + bool IsValid() const; - Size GetSize() const; + ISize GetSize() const; id GetMTLTexture() const; private: - id texture_; + const TextureDescriptor desc_; + id texture_ = nullptr; + bool is_valid_ = false; FML_DISALLOW_COPY_AND_ASSIGN(Texture); }; diff --git a/impeller/compositor/texture.mm b/impeller/compositor/texture.mm index eca5825930e37..b1dff469be823 100644 --- a/impeller/compositor/texture.mm +++ b/impeller/compositor/texture.mm @@ -6,13 +6,39 @@ namespace impeller { -Texture::Texture(id texture) : texture_(texture) {} +Texture::Texture(TextureDescriptor desc, id texture) + : desc_(std::move(desc)), texture_(texture) { + if (!desc_.IsValid() || !texture_) { + return; + } + + is_valid_ = true; +} Texture::~Texture() = default; -Size Texture::GetSize() const { - return {static_cast(texture_.width), - static_cast(texture_.height)}; +bool Texture::SetContents(const uint8_t* contents, + size_t length, + size_t mip_level) { + if (!IsValid() || !contents) { + return false; + } + + FML_CHECK(false); + return false; + + // MTLRegionMake2D(NSUInteger x, NSUInteger y, NSUInteger width, + // NSUInteger height) + + // [texture_ replaceRegion:(MTLRegion) + // mipmapLevel:(NSUInteger)withBytes:(nonnull const + // void*)bytesPerRow + // :(NSUInteger)]; +} + +ISize Texture::GetSize() const { + return {static_cast(texture_.width), + static_cast(texture_.height)}; } id Texture::GetMTLTexture() const { @@ -20,7 +46,7 @@ } bool Texture::IsValid() const { - return texture_; + return is_valid_; } } // namespace impeller diff --git a/impeller/geometry/matrix.h b/impeller/geometry/matrix.h index c2c5310acb6cd..e90cdb594e6ba 100644 --- a/impeller/geometry/matrix.h +++ b/impeller/geometry/matrix.h @@ -269,9 +269,9 @@ struct Matrix { Matrix operator+(const Matrix& m) const; - static constexpr Matrix MakeOrthographic(const Size& size) { + static constexpr Matrix MakeOrthographic(Scalar width, Scalar height) { // Per assumptions about NDC documented above. - const auto scale = MakeScale({1.0f / size.width, -1.0f / size.height, 1.0}); + const auto scale = MakeScale({1.0f / width, -1.0f / height, 1.0}); const auto translate = MakeTranslation({-1.0, 1.0, 0.5}); return translate * scale; } diff --git a/impeller/playground/playground.mm b/impeller/playground/playground.mm index 2fe879e8b8c77..387beb894a32f 100644 --- a/impeller/playground/playground.mm +++ b/impeller/playground/playground.mm @@ -7,6 +7,7 @@ #include "flutter/fml/paths.h" #include "flutter/testing/testing.h" #include "impeller/compositor/context.h" +#include "impeller/compositor/formats_metal.h" #include "impeller/compositor/render_pass.h" #include "impeller/compositor/renderer.h" #include "impeller/compositor/surface.h" @@ -108,8 +109,15 @@ static void PlaygroundKeyCallback(GLFWwindow* window, return false; } + TextureDescriptor color0_desc; + color0_desc.format = PixelFormat::kPixelFormat_B8G8R8A8_UNormInt; + color0_desc.size = { + static_cast(current_drawable.texture.width), + static_cast(current_drawable.texture.height)}; + ColorRenderPassAttachment color0; - color0.texture = std::make_shared(current_drawable.texture); + color0.texture = + std::make_shared(color0_desc, current_drawable.texture); color0.clear_color = Color::SkyBlue(); color0.load_action = LoadAction::kClear; color0.store_action = StoreAction::kStore; diff --git a/impeller/primitives/primitives_unittests.mm b/impeller/primitives/primitives_unittests.mm index a87b9b19f0fa1..2715cd3938462 100644 --- a/impeller/primitives/primitives_unittests.mm +++ b/impeller/primitives/primitives_unittests.mm @@ -50,23 +50,20 @@ Image image(flutter::testing::OpenFixtureAsMapping("image.png")); auto result = image.Decode(); ASSERT_TRUE(result.IsValid()); - auto device_image_allocation = - context->GetPermanentsAllocator()->CreateBufferWithCopy( - *result.GetAllocation()); - device_image_allocation->SetLabel("Bay Bridge"); - ASSERT_TRUE(device_image_allocation); auto texture_descriptor = TextureDescriptor::MakeFromImageResult(result); ASSERT_TRUE(texture_descriptor.has_value()); - auto texture = - device_image_allocation->MakeTexture(texture_descriptor.value()); + auto texture = context->GetPermanentsAllocator()->CreateTexture( + StorageMode::kHostVisible, texture_descriptor.value()); ASSERT_TRUE(texture); + auto uploaded = texture->SetContents(result.GetAllocation()->GetMapping(), + result.GetAllocation()->GetSize()); + ASSERT_TRUE(uploaded); Renderer::RenderCallback callback = [&](const Surface& surface, RenderPass& pass) { BoxVertexShader::UniformBuffer uniforms; - - uniforms.mvp = Matrix::MakeOrthographic(surface.GetSize()); - + uniforms.mvp = Matrix::MakeOrthographic(surface.GetSize().width, + surface.GetSize().height); Command cmd; cmd.label = "Box"; cmd.pipeline = box_pipeline; From 3192bf4984e72ca49f2625ebd273880dbf394952 Mon Sep 17 00:00:00 2001 From: Chinmay Garde Date: Tue, 6 Jul 2021 16:44:53 -0700 Subject: [PATCH 124/433] Geometry utils for unsigned types. --- impeller/geometry/geometry_unittests.cc | 27 ++++++++ impeller/geometry/point.h | 58 ++++++++++------ impeller/geometry/rect.cc | 31 +-------- impeller/geometry/rect.h | 90 +++++++++++++++++-------- impeller/geometry/size.h | 5 ++ 5 files changed, 132 insertions(+), 79 deletions(-) diff --git a/impeller/geometry/geometry_unittests.cc b/impeller/geometry/geometry_unittests.cc index bed8c9987f3a7..414c49622e5bb 100644 --- a/impeller/geometry/geometry_unittests.cc +++ b/impeller/geometry/geometry_unittests.cc @@ -3,6 +3,8 @@ // found in the LICENSE file. #include "flutter/testing/testing.h" +#include "impeller/geometry/point.h" +#include "impeller/geometry/rect.h" #include "impeller/geometry/size.h" namespace impeller { @@ -19,5 +21,30 @@ TEST(GeometryTest, CanGenerateMipCounts) { ASSERT_EQ((Size{-128, 25}.MipCount()), 1u); } +TEST(GeometryTest, CanConvertTTypesExplicitly) { + { + Point p1(1.0, 2.0); + IPoint p2 = static_cast(p1); + ASSERT_EQ(p2.x, 1u); + ASSERT_EQ(p2.y, 2u); + } + + { + Size s1(1.0, 2.0); + ISize s2 = static_cast(s1); + ASSERT_EQ(s2.width, 1u); + ASSERT_EQ(s2.height, 2u); + } + + { + Rect r1(1.0, 2.0, 3.0, 4.0); + IRect r2 = static_cast(r1); + ASSERT_EQ(r2.origin.x, 1u); + ASSERT_EQ(r2.origin.y, 2u); + ASSERT_EQ(r2.size.width, 3u); + ASSERT_EQ(r2.size.height, 4u); + } +} + } // namespace testing } // namespace impeller diff --git a/impeller/geometry/point.h b/impeller/geometry/point.h index 10c25d996eec8..ec3e4b5adc888 100644 --- a/impeller/geometry/point.h +++ b/impeller/geometry/point.h @@ -12,65 +12,81 @@ namespace impeller { -struct Point { - Scalar x = 0.0; - Scalar y = 0.0; +template +struct TPoint { + using Type = T; - constexpr Point() = default; + Type x = {}; + Type y = {}; - constexpr Point(Scalar x, Scalar y) : x(x), y(y) {} + constexpr TPoint() = default; - constexpr bool operator==(const Point& p) const { + template + explicit constexpr TPoint(const TPoint& other) + : TPoint(static_cast(other.x), static_cast(other.y)) {} + + constexpr TPoint(Type x, Type y) : x(x), y(y) {} + + constexpr bool operator==(const TPoint& p) const { return p.x == x && p.y == y; } - constexpr bool operator!=(const Point& p) const { + constexpr bool operator!=(const TPoint& p) const { return p.x != x || p.y != y; } - constexpr Point operator-() const { return {-x, -y}; } + constexpr TPoint operator-() const { return {-x, -y}; } - constexpr Point operator+(const Point& p) const { return {x + p.x, y + p.y}; } + constexpr TPoint operator+(const TPoint& p) const { + return {x + p.x, y + p.y}; + } - constexpr Point operator+(const Size& s) const { + constexpr TPoint operator+(const TSize& s) const { return {x + s.width, y + s.height}; } - constexpr Point operator-(const Point& p) const { return {x - p.x, y - p.y}; } + constexpr TPoint operator-(const TPoint& p) const { + return {x - p.x, y - p.y}; + } - constexpr Point operator-(const Size& s) const { + constexpr TPoint operator-(const TSize& s) const { return {x - s.width, y - s.height}; } - constexpr Point operator*(Scalar scale) const { + constexpr TPoint operator*(Type scale) const { return {x * scale, y * scale}; } - constexpr Point operator*(const Point& p) const { return {x * p.x, y * p.y}; } + constexpr TPoint operator*(const TPoint& p) const { + return {x * p.x, y * p.y}; + } - constexpr Point operator*(const Size& s) const { + constexpr TPoint operator*(const TSize& s) const { return {x * s.width, y * s.height}; } - constexpr Point operator/(Scalar d) const { return {x / d, y / d}; } + constexpr TPoint operator/(Type d) const { return {x / d, y / d}; } - constexpr Point operator/(const Point& p) const { return {x / p.x, y / p.y}; } + constexpr TPoint operator/(const TPoint& p) const { + return {x / p.x, y / p.y}; + } - constexpr Point operator/(const Size& s) const { + constexpr TPoint operator/(const TSize& s) const { return {x / s.width, y / s.height}; } - constexpr Scalar GetDistanceSquared(const Point& p) const { + constexpr Type GetDistanceSquared(const TPoint& p) const { double dx = p.x - x; double dy = p.y - y; return dx * dx + dy * dy; } - constexpr Scalar GetDistance(const Point& p) const { + constexpr Type GetDistance(const TPoint& p) const { return sqrt(GetDistanceSquared(p)); } }; -static_assert(sizeof(Point) == 2 * sizeof(Scalar)); +using Point = TPoint; +using IPoint = TPoint; } // namespace impeller diff --git a/impeller/geometry/rect.cc b/impeller/geometry/rect.cc index 9d7e28fbcca4a..6b0192ad6f437 100644 --- a/impeller/geometry/rect.cc +++ b/impeller/geometry/rect.cc @@ -7,35 +7,6 @@ namespace impeller { -Rect Rect::WithPoint(const Point& p) const { - Rect copy = *this; - if (p.x < origin.x) { - copy.origin.x = p.x; - copy.size.width += (origin.x - p.x); - } - - if (p.y < origin.y) { - copy.origin.y = p.y; - copy.size.height += (origin.y - p.y); - } - - if (p.x > (size.width + origin.x)) { - copy.size.width += p.x - (size.width + origin.x); - } - - if (p.y > (size.height + origin.y)) { - copy.size.height += p.y - (size.height + origin.y); - } - - return copy; -} - -Rect Rect::WithPoints(const std::vector& points) const { - Rect box = *this; - for (const auto& point : points) { - box = box.WithPoint(point); - } - return box; -} +// } // namespace impeller diff --git a/impeller/geometry/rect.h b/impeller/geometry/rect.h index 52ad8a54ee7b3..02a51a3642f01 100644 --- a/impeller/geometry/rect.h +++ b/impeller/geometry/rect.h @@ -12,62 +12,96 @@ namespace impeller { -struct Rect { - Point origin; - Size size; +template +struct TRect { + using Type = T; - constexpr Rect() : origin({0.0, 0.0}), size({0.0, 0.0}) {} + TPoint origin; + TSize size; - constexpr Rect(Size size) : origin({0.0, 0.0}), size(size) {} + constexpr TRect() : origin({0.0, 0.0}), size({0.0, 0.0}) {} - constexpr Rect(Point origin, Size size) : origin(origin), size(size) {} + constexpr TRect(TSize size) : origin({0.0, 0.0}), size(size) {} - constexpr Rect(const Scalar components[4]) + constexpr TRect(TPoint origin, TSize size) + : origin(origin), size(size) {} + + constexpr TRect(const Type components[4]) : origin(components[0], components[1]), size(components[2], components[3]) {} - constexpr Rect(Scalar x, Scalar y, Scalar width, Scalar height) + constexpr TRect(Type x, Type y, Type width, Type height) : origin(x, y), size(width, height) {} - /* - * Operator overloads - */ - constexpr Rect operator+(const Rect& r) const { - return Rect({origin.x + r.origin.x, origin.y + r.origin.y}, - {size.width + r.size.width, size.height + r.size.height}); + template + constexpr explicit TRect(const TRect& other) + : origin(static_cast>(other.origin)), + size(static_cast>(other.size)) {} + + constexpr TRect operator+(const TRect& r) const { + return TRect({origin.x + r.origin.x, origin.y + r.origin.y}, + {size.width + r.size.width, size.height + r.size.height}); } - constexpr Rect operator-(const Rect& r) const { - return Rect({origin.x - r.origin.x, origin.y - r.origin.y}, - {size.width - r.size.width, size.height - r.size.height}); + constexpr TRect operator-(const TRect& r) const { + return TRect({origin.x - r.origin.x, origin.y - r.origin.y}, + {size.width - r.size.width, size.height - r.size.height}); } - constexpr Rect operator*(Scalar scale) const { - return Rect({origin.x * scale, origin.y * scale}, - {size.width * scale, size.height * scale}); + constexpr TRect operator*(Type scale) const { + return TRect({origin.x * scale, origin.y * scale}, + {size.width * scale, size.height * scale}); } - constexpr Rect operator*(const Rect& r) const { - return Rect({origin.x * r.origin.x, origin.y * r.origin.y}, - {size.width * r.size.width, size.height * r.size.height}); + constexpr TRect operator*(const TRect& r) const { + return TRect({origin.x * r.origin.x, origin.y * r.origin.y}, + {size.width * r.size.width, size.height * r.size.height}); } - constexpr bool operator==(const Rect& r) const { + constexpr bool operator==(const TRect& r) const { return origin == r.origin && size == r.size; } - constexpr bool Contains(const Point& p) const { + constexpr bool Contains(const TPoint& p) const { return p.x >= origin.x && p.x <= size.width && p.y >= origin.y && p.y <= size.height; } constexpr bool IsZero() const { return size.IsZero(); } - Rect WithPoint(const Point& p) const; + constexpr TRect WithPoint(const TPoint& p) const { + TRect copy = *this; + if (p.x < origin.x) { + copy.origin.x = p.x; + copy.size.width += (origin.x - p.x); + } + + if (p.y < origin.y) { + copy.origin.y = p.y; + copy.size.height += (origin.y - p.y); + } + + if (p.x > (size.width + origin.x)) { + copy.size.width += p.x - (size.width + origin.x); + } - Rect WithPoints(const std::vector& points) const; + if (p.y > (size.height + origin.y)) { + copy.size.height += p.y - (size.height + origin.y); + } + + return copy; + } + + constexpr TRect WithPoints(const std::vector>& points) const { + TRect box = *this; + for (const auto& point : points) { + box = box.WithPoint(point); + } + return box; + } }; -static_assert(sizeof(Rect) == 4 * sizeof(Scalar)); +using Rect = TRect; +using IRect = TRect; } // namespace impeller diff --git a/impeller/geometry/size.h b/impeller/geometry/size.h index ac32ffdd9ff26..2f0957660cd2e 100644 --- a/impeller/geometry/size.h +++ b/impeller/geometry/size.h @@ -23,6 +23,11 @@ struct TSize { constexpr TSize(Type width, Type height) : width(width), height(height) {} + template + explicit constexpr TSize(const TSize& other) + : TSize(static_cast(other.width), static_cast(other.height)) { + } + static constexpr TSize Infinite() { return TSize{std::numeric_limits::max(), std::numeric_limits::max()}; From 3a105e8dd5059318a71a7a6cc7176c57ab5fab2d Mon Sep 17 00:00:00 2001 From: Chinmay Garde Date: Tue, 6 Jul 2021 17:05:06 -0700 Subject: [PATCH 125/433] Wire up texture sampling in unit-tests. --- impeller/compositor/texture.h | 4 +-- impeller/compositor/texture.mm | 34 ++++++++++++++------- impeller/primitives/primitives_unittests.mm | 12 ++++++-- 3 files changed, 34 insertions(+), 16 deletions(-) diff --git a/impeller/compositor/texture.h b/impeller/compositor/texture.h index a4fe87f818ad7..e9b9241909f57 100644 --- a/impeller/compositor/texture.h +++ b/impeller/compositor/texture.h @@ -23,9 +23,7 @@ class Texture { void SetLabel(const std::string_view& label); - [[nodiscard]] bool SetContents(const uint8_t* contents, - size_t length, - size_t mip_level = 0u); + [[nodiscard]] bool SetContents(const uint8_t* contents, size_t length); bool IsValid() const; diff --git a/impeller/compositor/texture.mm b/impeller/compositor/texture.mm index b1dff469be823..0723d8dad5da7 100644 --- a/impeller/compositor/texture.mm +++ b/impeller/compositor/texture.mm @@ -12,28 +12,40 @@ return; } + if (desc_.size != GetSize()) { + FML_DLOG(ERROR) + << "The texture and its descriptor disagree about its size."; + return; + } + is_valid_ = true; } Texture::~Texture() = default; -bool Texture::SetContents(const uint8_t* contents, - size_t length, - size_t mip_level) { +bool Texture::SetContents(const uint8_t* contents, size_t length) { if (!IsValid() || !contents) { return false; } - FML_CHECK(false); - return false; + // Out of bounds access. + if (length != desc_.GetSizeOfBaseMipLevel()) { + return false; + } - // MTLRegionMake2D(NSUInteger x, NSUInteger y, NSUInteger width, - // NSUInteger height) + // TODO(csg): Perhaps the storage mode should be added to the texture + // descriptor so that invalid region replacements on potentially non-host + // visible textures are disallowed. The annoying bit about the API below is + // that there seems to be no error handling guidance. + const auto region = + MTLRegionMake2D(0u, 0u, desc_.size.width, desc_.size.height); + [texture_ replaceRegion:region // + mipmapLevel:0u // + withBytes:contents // + bytesPerRow:desc_.GetBytesPerRow() // + ]; - // [texture_ replaceRegion:(MTLRegion) - // mipmapLevel:(NSUInteger)withBytes:(nonnull const - // void*)bytesPerRow - // :(NSUInteger)]; + return true; } ISize Texture::GetSize() const { diff --git a/impeller/primitives/primitives_unittests.mm b/impeller/primitives/primitives_unittests.mm index 2715cd3938462..653db6b1d1122 100644 --- a/impeller/primitives/primitives_unittests.mm +++ b/impeller/primitives/primitives_unittests.mm @@ -58,7 +58,8 @@ auto uploaded = texture->SetContents(result.GetAllocation()->GetMapping(), result.GetAllocation()->GetSize()); ASSERT_TRUE(uploaded); - + auto sampler = context->GetSamplerLibrary()->GetSampler({}); + ASSERT_TRUE(sampler); Renderer::RenderCallback callback = [&](const Surface& surface, RenderPass& pass) { BoxVertexShader::UniformBuffer uniforms; @@ -67,11 +68,18 @@ Command cmd; cmd.label = "Box"; cmd.pipeline = box_pipeline; + cmd.vertex_bindings.buffers[VertexDescriptor::kReservedVertexBufferIndex] = vertex_buffer.vertex_buffer; cmd.vertex_bindings .buffers[BoxVertexShader::kUniformUniformBuffer.binding] = pass.GetTransientsBuffer().EmplaceUniform(uniforms); + + cmd.fragment_bindings + .textures[BoxFragmentShader::kInputContentsTexture.location] = texture; + cmd.fragment_bindings + .samplers[BoxFragmentShader::kInputContentsTexture.location] = sampler; + cmd.index_buffer = vertex_buffer.index_buffer; cmd.index_count = vertex_buffer.index_count; cmd.primitive_type = PrimitiveType::kTriange; @@ -80,7 +88,7 @@ } return true; }; - // OpenPlaygroundHere(callback); + OpenPlaygroundHere(callback); } } // namespace testing From 0a1ccc8c6a8c7a39f83a6f2bb2b1933593ef9352 Mon Sep 17 00:00:00 2001 From: Chinmay Garde Date: Tue, 6 Jul 2021 17:18:20 -0700 Subject: [PATCH 126/433] Add support for more pixel formats. --- impeller/compositor/formats.h | 5 ++++- impeller/compositor/formats_metal.h | 4 ++++ impeller/compositor/texture.mm | 4 ++++ impeller/compositor/texture_descriptor.mm | 2 +- impeller/primitives/primitives_unittests.mm | 1 + 5 files changed, 14 insertions(+), 2 deletions(-) diff --git a/impeller/compositor/formats.h b/impeller/compositor/formats.h index 56fc305ab488d..fd90ee07898c0 100644 --- a/impeller/compositor/formats.h +++ b/impeller/compositor/formats.h @@ -15,6 +15,8 @@ namespace impeller { enum class PixelFormat { kUnknown, + kPixelFormat_R8G8B8A8_UNormInt, + kPixelFormat_R8G8B8A8_UNormInt_SRGB, kPixelFormat_B8G8R8A8_UNormInt, kPixelFormat_B8G8R8A8_UNormInt_SRGB, kPixelFormat_D32_Float_S8_UNormInt, @@ -97,8 +99,9 @@ constexpr size_t BytesPerPixelForPixelFormat(PixelFormat format) { switch (format) { case PixelFormat::kUnknown: return 0u; + case PixelFormat::kPixelFormat_R8G8B8A8_UNormInt: + case PixelFormat::kPixelFormat_R8G8B8A8_UNormInt_SRGB: case PixelFormat::kPixelFormat_B8G8R8A8_UNormInt: - return 4u; case PixelFormat::kPixelFormat_B8G8R8A8_UNormInt_SRGB: return 4u; case PixelFormat::kPixelFormat_D32_Float_S8_UNormInt: diff --git a/impeller/compositor/formats_metal.h b/impeller/compositor/formats_metal.h index cea758a5b7cd9..af38261604b3b 100644 --- a/impeller/compositor/formats_metal.h +++ b/impeller/compositor/formats_metal.h @@ -27,6 +27,10 @@ constexpr MTLPixelFormat ToMTLPixelFormat(PixelFormat format) { return MTLPixelFormatBGRA8Unorm_sRGB; case PixelFormat::kPixelFormat_D32_Float_S8_UNormInt: return MTLPixelFormatDepth32Float_Stencil8; + case PixelFormat::kPixelFormat_R8G8B8A8_UNormInt: + return MTLPixelFormatRGBA8Unorm; + case PixelFormat::kPixelFormat_R8G8B8A8_UNormInt_SRGB: + return MTLPixelFormatRGBA8Unorm_sRGB; } return MTLPixelFormatInvalid; }; diff --git a/impeller/compositor/texture.mm b/impeller/compositor/texture.mm index 0723d8dad5da7..f9f71d581ce08 100644 --- a/impeller/compositor/texture.mm +++ b/impeller/compositor/texture.mm @@ -23,6 +23,10 @@ Texture::~Texture() = default; +void Texture::SetLabel(const std::string_view& label) { + [texture_ setLabel:@(label.data())]; +} + bool Texture::SetContents(const uint8_t* contents, size_t length) { if (!IsValid() || !contents) { return false; diff --git a/impeller/compositor/texture_descriptor.mm b/impeller/compositor/texture_descriptor.mm index 18021e8e78be5..78f7cf853203d 100644 --- a/impeller/compositor/texture_descriptor.mm +++ b/impeller/compositor/texture_descriptor.mm @@ -18,7 +18,7 @@ case ImageResult::Components::RGB: return std::nullopt; case ImageResult::Components::RGBA: - return PixelFormat::kPixelFormat_B8G8R8A8_UNormInt; + return PixelFormat::kPixelFormat_R8G8B8A8_UNormInt; } return std::nullopt; } diff --git a/impeller/primitives/primitives_unittests.mm b/impeller/primitives/primitives_unittests.mm index 653db6b1d1122..582e7a835ff7c 100644 --- a/impeller/primitives/primitives_unittests.mm +++ b/impeller/primitives/primitives_unittests.mm @@ -55,6 +55,7 @@ auto texture = context->GetPermanentsAllocator()->CreateTexture( StorageMode::kHostVisible, texture_descriptor.value()); ASSERT_TRUE(texture); + texture->SetLabel("Bay Bridge"); auto uploaded = texture->SetContents(result.GetAllocation()->GetMapping(), result.GetAllocation()->GetSize()); ASSERT_TRUE(uploaded); From df4478ab4d3f4155d097b140d0f4624c22293ccb Mon Sep 17 00:00:00 2001 From: Chinmay Garde Date: Thu, 8 Jul 2021 16:22:38 -0700 Subject: [PATCH 127/433] Add missing dependency reference to compositor. --- impeller/compositor/BUILD.gn | 1 + 1 file changed, 1 insertion(+) diff --git a/impeller/compositor/BUILD.gn b/impeller/compositor/BUILD.gn index b9a5e5dc964ab..1b15d184f31b3 100644 --- a/impeller/compositor/BUILD.gn +++ b/impeller/compositor/BUILD.gn @@ -69,6 +69,7 @@ impeller_component("compositor") { public_deps = [ "../base", "../geometry", + "../image", "../shader_glue", ] From 73fdbc43aff098325b5931d51ef36148beb4f6ca Mon Sep 17 00:00:00 2001 From: Chinmay Garde Date: Thu, 8 Jul 2021 17:42:36 -0700 Subject: [PATCH 128/433] Split allocation into its base file. --- impeller/base/BUILD.gn | 2 + impeller/base/allocation.cc | 82 ++++++++++++++++++++ impeller/base/allocation.h | 38 +++++++++ impeller/compositor/host_buffer.h | 19 +---- impeller/compositor/host_buffer.mm | 77 ++---------------- impeller/compositor/texture_descriptor.mm | 1 + impeller/fixtures/BUILD.gn | 6 +- impeller/fixtures/airplane.jpg | Bin 0 -> 71385 bytes impeller/fixtures/bay_bridge.jpg | Bin 0 -> 138780 bytes impeller/fixtures/boston.jpg | Bin 0 -> 199554 bytes impeller/fixtures/embarcadero.jpg | Bin 0 -> 115292 bytes impeller/fixtures/image.png | Bin 514968 -> 0 bytes impeller/fixtures/kalimba.jpg | Bin 0 -> 68061 bytes impeller/primitives/primitives_unittests.mm | 2 +- 14 files changed, 141 insertions(+), 86 deletions(-) create mode 100644 impeller/base/allocation.cc create mode 100644 impeller/base/allocation.h create mode 100644 impeller/fixtures/airplane.jpg create mode 100644 impeller/fixtures/bay_bridge.jpg create mode 100644 impeller/fixtures/boston.jpg create mode 100644 impeller/fixtures/embarcadero.jpg delete mode 100644 impeller/fixtures/image.png create mode 100644 impeller/fixtures/kalimba.jpg diff --git a/impeller/base/BUILD.gn b/impeller/base/BUILD.gn index d7be2840bb102..3a510cac0d85d 100644 --- a/impeller/base/BUILD.gn +++ b/impeller/base/BUILD.gn @@ -6,6 +6,8 @@ import("../tools/impeller.gni") impeller_component("base") { sources = [ + "allocation.cc", + "allocation.h", "base.h", "config.h", "strings.cc", diff --git a/impeller/base/allocation.cc b/impeller/base/allocation.cc new file mode 100644 index 0000000000000..d4f98ad4ff38a --- /dev/null +++ b/impeller/base/allocation.cc @@ -0,0 +1,82 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "impeller/base/allocation.h" + +#include + +#include "flutter/fml/logging.h" + +namespace impeller { + +Allocation::Allocation() = default; + +Allocation::~Allocation() { + ::free(buffer_); +} + +uint8_t* Allocation::GetBuffer() const { + return buffer_; +} + +size_t Allocation::GetLength() const { + return length_; +} + +size_t Allocation::GetReservedLength() const { + return reserved_; +} + +bool Allocation::Truncate(size_t length) { + if (!ReserveNPOT(length)) { + return false; + } + length_ = length; + return true; +} + +static uint32_t NextPowerOfTwoSize(uint32_t x) { + if (x == 0) { + return 1; + } + + --x; + + x |= x >> 1; + x |= x >> 2; + x |= x >> 4; + x |= x >> 8; + x |= x >> 16; + + return x + 1; +} + +bool Allocation::ReserveNPOT(size_t reserved) { + return Reserve(NextPowerOfTwoSize(reserved)); +} + +bool Allocation::Reserve(size_t reserved) { + // Reserve at least one page of data. + reserved = std::max(4096u, reserved); + + if (reserved == reserved_) { + return true; + } + + auto new_allocation = ::realloc(buffer_, reserved); + if (!new_allocation) { + // If new length is zero, a minimum non-zero sized allocation is returned. + // So this check will not trip and this routine will indicate success as + // expected. + FML_LOG(ERROR) << "Allocation failed. Out of host memory."; + return false; + } + + buffer_ = static_cast(new_allocation); + reserved_ = reserved; + + return true; +} + +} // namespace impeller diff --git a/impeller/base/allocation.h b/impeller/base/allocation.h new file mode 100644 index 0000000000000..a58974e8f4ca5 --- /dev/null +++ b/impeller/base/allocation.h @@ -0,0 +1,38 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include +#include + +#include "flutter/fml/macros.h" + +namespace impeller { + +class Allocation { + public: + Allocation(); + + ~Allocation(); + + uint8_t* GetBuffer() const; + + size_t GetLength() const; + + size_t GetReservedLength() const; + + [[nodiscard]] bool Truncate(size_t length); + + private: + uint8_t* buffer_ = nullptr; + size_t length_ = 0; + size_t reserved_ = 0; + + [[nodiscard]] bool Reserve(size_t reserved); + + [[nodiscard]] bool ReserveNPOT(size_t reserved); + + FML_DISALLOW_COPY_AND_ASSIGN(Allocation); +}; + +} // namespace impeller diff --git a/impeller/compositor/host_buffer.h b/impeller/compositor/host_buffer.h index 8ac5fc77bc401..0f61453683278 100644 --- a/impeller/compositor/host_buffer.h +++ b/impeller/compositor/host_buffer.h @@ -10,6 +10,7 @@ #include #include "flutter/fml/macros.h" +#include "impeller/base/allocation.h" #include "impeller/compositor/buffer.h" #include "impeller/compositor/buffer_view.h" #include "impeller/compositor/platform.h" @@ -17,19 +18,16 @@ namespace impeller { class HostBuffer final : public std::enable_shared_from_this, + public Allocation, public Buffer { public: + static std::shared_ptr Create(); + // |Buffer| virtual ~HostBuffer(); - static std::shared_ptr Create(); - void SetLabel(std::string label); - size_t GetLength() const; - - size_t GetReservedLength() const; - template >> [[nodiscard]] BufferView EmplaceUniform(const T& t) { return Emplace(reinterpret_cast(&t), sizeof(T), @@ -45,14 +43,9 @@ class HostBuffer final : public std::enable_shared_from_this, size_t length, size_t align); - [[nodiscard]] bool Truncate(size_t length); - private: mutable std::shared_ptr device_buffer_; mutable size_t device_buffer_generation_ = 0u; - uint8_t* buffer_ = nullptr; - size_t length_ = 0; - size_t reserved_ = 0; size_t generation_ = 1u; std::string label_; @@ -62,10 +55,6 @@ class HostBuffer final : public std::enable_shared_from_this, [[nodiscard]] BufferView Emplace(const void* buffer, size_t length); - [[nodiscard]] bool Reserve(size_t reserved); - - [[nodiscard]] bool ReserveNPOT(size_t reserved); - HostBuffer(); FML_DISALLOW_COPY_AND_ASSIGN(HostBuffer); diff --git a/impeller/compositor/host_buffer.mm b/impeller/compositor/host_buffer.mm index 7784ad141f617..112406e413f0c 100644 --- a/impeller/compositor/host_buffer.mm +++ b/impeller/compositor/host_buffer.mm @@ -20,31 +20,21 @@ HostBuffer::HostBuffer() = default; -HostBuffer::~HostBuffer() { - ::free(buffer_); -} +HostBuffer::~HostBuffer() = default; void HostBuffer::SetLabel(std::string label) { label_ = std::move(label); } -size_t HostBuffer::GetLength() const { - return length_; -} - -size_t HostBuffer::GetReservedLength() const { - return reserved_; -} - BufferView HostBuffer::Emplace(const void* buffer, size_t length, size_t align) { - if (align == 0 || (length_ % align) == 0) { + if (align == 0 || (GetLength() % align) == 0) { return Emplace(buffer, length); } { - auto pad = Emplace(nullptr, align - (length_ % align)); + auto pad = Emplace(nullptr, align - (GetLength() % align)); if (!pad) { return {}; } @@ -54,75 +44,24 @@ } BufferView HostBuffer::Emplace(const void* buffer, size_t length) { - auto old_length = length_; - if (!Truncate(length_ + length)) { + auto old_length = GetLength(); + if (!Truncate(GetLength() + length)) { return {}; } - FML_DCHECK(buffer_); + FML_DCHECK(GetBuffer()); generation_++; if (buffer) { - ::memmove(buffer_ + old_length, buffer, length); + ::memmove(GetBuffer() + old_length, buffer, length); } return BufferView{shared_from_this(), Range{old_length, length}}; } -bool HostBuffer::Truncate(size_t length) { - if (!ReserveNPOT(length)) { - return false; - } - length_ = length; - return true; -} - -static uint32_t NextPowerOfTwoSize(uint32_t x) { - if (x == 0) { - return 1; - } - - --x; - - x |= x >> 1; - x |= x >> 2; - x |= x >> 4; - x |= x >> 8; - x |= x >> 16; - - return x + 1; -} - -bool HostBuffer::ReserveNPOT(size_t reserved) { - return Reserve(NextPowerOfTwoSize(reserved)); -} - -bool HostBuffer::Reserve(size_t reserved) { - // Reserve at least one page of data. - reserved = std::max(4096u, reserved); - - if (reserved == reserved_) { - return true; - } - - auto new_allocation = ::realloc(buffer_, reserved); - if (!new_allocation) { - // If new length is zero, a minimum non-zero sized allocation is returned. - // So this check will not trip and this routine will indicate success as - // expected. - FML_LOG(ERROR) << "Allocation failed. Out of host memory."; - return false; - } - - buffer_ = static_cast(new_allocation); - reserved_ = reserved; - - return true; -} - std::shared_ptr HostBuffer::GetDeviceBuffer( Allocator& allocator) const { if (generation_ == device_buffer_generation_) { return device_buffer_; } - auto new_buffer = allocator.CreateBufferWithCopy(buffer_, length_); + auto new_buffer = allocator.CreateBufferWithCopy(GetBuffer(), GetLength()); if (!new_buffer) { return nullptr; } diff --git a/impeller/compositor/texture_descriptor.mm b/impeller/compositor/texture_descriptor.mm index 78f7cf853203d..0822b10fe8cc2 100644 --- a/impeller/compositor/texture_descriptor.mm +++ b/impeller/compositor/texture_descriptor.mm @@ -32,6 +32,7 @@ const auto pixel_format = FormatForImageResultComponents(result.GetComponents()); if (!pixel_format.has_value()) { + FML_DLOG(ERROR) << "Unknown image format."; return std::nullopt; } diff --git a/impeller/fixtures/BUILD.gn b/impeller/fixtures/BUILD.gn index cd2fc9ad088a0..c55f7cad7e55a 100644 --- a/impeller/fixtures/BUILD.gn +++ b/impeller/fixtures/BUILD.gn @@ -9,6 +9,10 @@ test_fixtures("fixtures") { fixtures = [ "sample.vert", "types.h", - "image.png", + "airplane.jpg", + "bay_bridge.jpg", + "boston.jpg", + "embarcadero.jpg", + "kalimba.jpg", ] } diff --git a/impeller/fixtures/airplane.jpg b/impeller/fixtures/airplane.jpg new file mode 100644 index 0000000000000000000000000000000000000000..7a7fb4485a880b6303b5aa8f23bc4ebcaaadf887 GIT binary patch literal 71385 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 literal 0 HcmV?d00001 diff --git a/impeller/fixtures/bay_bridge.jpg b/impeller/fixtures/bay_bridge.jpg new file mode 100644 index 0000000000000000000000000000000000000000..6f5d3f43f5e235678e56a63cc85b0e1375c9c36c GIT binary patch literal 138780 zcmeFaXFwFqvM{_P38H`mNfHH>yi3lKg9OPLg=K*SmYkKKC`k~>859r%1SCgA$vNjJ zS#p*r`^_Rg&vVW__rCXg|Go^&bWL}4b#--Bb?Lm zI-AWyOFL&fCrdkfI&KawkbtCuGR8Uxbr7O3Kxn|}f-pgJ$PLhS2{SuW7|84^ifiOo5D^aOJsQ9?5eSV44V?%L z*?^V?6k!A735@8)fr*8IegzGLjf0DK6%B-rj`7b@AOiyv>&kf{AqX7}0|Ols{R%b) za6xxML&qS(1YyzN086M{AvR%fct!#Ve}_$4D9QLuy?2J2%yjFvs_o1R?w(HEG(c8m^c_12%tYi7?>!IvD5&LuQ)slhrBD?BKg)k!zf9r zZtA$reVa$>Ia!1TQyR3W4%?}ZcUIc(re-7{9dZh0J{ih8X3kptJL$#scklJj?Y@Z0 zDEU{GWwrli`oHD*e_$Co2Es?F84)@WNF21Yc-NZLm1Rw9B)3p|TStS`j?@*np8|4f z&hwUoZz*Y2ndbGAx`4HpgRA0lH5-kTv<6U@kErB#)+1!k&N^7?@3Jb>{7jZfu#N^E zP{kRAeUT%T9bA4+al6rAF1U;^#gEjDT5G~V$D#51TySYKNhylDekRL?KyG$$^=lR+ z2-&-{4+iQ1CFev|{;>1%g6v&eR2v4FEF*?mUj%YZ9HdOZn(c}`yw_Bm{U_0nlMdTi zab`bS8a)gD;k!*ENTGB^Er@k*)kRNo=eojS#m{3$Znn4WYc;uu#bn8;-i3&3Ry&r6 zbwxSa!IxFp__~~#oh)~a#XD-@l`w(U*Y$3!dck?qEbt`YYG z)+cOzad~?3Do@^I1TemoU5Xv;crE&|LXyvt&Z6J;ZPgRGkmDbVkXPF`zVi@OT;A(LG)nF>%iu+P4y(~s?OL9U+-#3APSP3(iu~#Gu>*vdlR@^GvhVXkN z>sBN*_N^>+stn%P*Ow)l*LGTN*qNIO6Hg?KffIMqbtFq(Piq$M5j)in2HUAeZ=4dR z?%F?EsITKV&U)43wWeN&9n@3t&b)x;J7U1hJn-|?or+f4#Ve7vy2lFMUjl_sZ_^+_ zDzdZ>5smv;1a0H{v~gp%62}a}Nl!!W;5Mn7g+_#|Shajs6YJ*CGlOal`ySU(?G#bn zJZWdq$MSI`02hi!hNVOuXD%CR>A0D z1>85{ZPXH@qd4|SVIj4zl1$5!zD)?6t8=kawr{9ojAgQ$QUWjX2Ad;fP-mUZ3Y#OZ z(Z?ER#0d&s5!>e!IMSb%+GRfr!(3>LN_j<*-5&9*+XYQ=r+{|H{<-JJIq8?QTpq$| zy)?ZX6ig8tL#$tjZZz>Gd!$v+sU7x~9eQ(?1}0|*;d(Xs`vM{;|87wU-D>uaXwBfA<<-BY$k`FB<`rc`mrTnS}2qsSU?n zLt;{6ViK0yY#pQ$Y|0?22o+M*)53*k*&e~|iUasDa5tf`m|4tMtSLAGqT-V;rH~+& zofhJWHgFM|+0^|q168m!C4!X_K9;0&JK@+L=T<;KT~Skh?Mgl~e`ktlhFnyP2g7h| zjL0I);Rata?hS!E;RYk2SS(hPU?%dT2b0GJ{qJ>=AXb+JUm|5y)x)u*+Ukl49}b@M zA7WeeY|nV_WJ7CvpIiEgofe^qtS(flRc4fjUpGEI41W_gFdB0FoOowJz}tG6wJ#xz z_H#?SAO0iVgKQ5pFY%5^a(63x&eK>mUuS3-Jj~NSI3;XRG;Wiokl>Vk770?4lcCi; z`-B8Jmv@xwXdAl?hj5D+x?CL;Aup)k55TXcd0}HOZ`0(M&iS-<@{Uh@Y^~bq;4ths z@AL3ge+J>WhCnZQ9asI}{#YiU3luWt_dW(}pR%8scn2YT+9R#N%1j@tXrkFxF>+d3 z_dN+ee)XLu7!%;nx~Y|qyylzAQhWS$2Gu^_xB6x~kR9>Nk3=WyRE~0? z+_U8vdggQTzDhe5@67lp@4IEe?ciP2Kvl|K*vl!S!(JnA9&K9Kr-#Btv`V#|YCGZ) zX|bI#KZQ0D1Vp0_w#RA$UgX4d^W2XckJz0#orwJz`_n;q=k5!y`-F8Cr6q8RH#P8q zD-T_d{7xa-_pJ(5kpsI-|Ii!2`5E3MwR=s%0eIA#q=q+1_=rh3af5T%ZvSk=4bi`L z0-sac%Ql5^c|DGvy^Ti|M7r=(HK5TiNQ600ba?OTEmrv+7gi2)W%WLVi5BZRU1oQ^ zh-a>@DwYHzEt>{ebm(-i7ZEvdH=;9lcu^cBs|W8ju13~-nvOT7t!EXE}RF7^564r!BH2CqsO29=fH~T!#d8{`_=Z-oKys*X^vf zkR1=Ft}uGGrn+e-BnVt+k{4GW^|^jiy`QW$dg?Ll&!T=LXnwMblvd&(tgm4$#_w0` z7CCbuH)q?=1X`gVL)|~=2iDtkPP26wJf3&c4v4OMa31(13R#qW%0+@yN!l@RynL3t zqd8g5R(GY8)|+)-cnvFZxiN_Ao|T_0fV^ls%*8i98>Svo)vh)Agq?04wEJ+)%`6o_ z3RL$_`VTsm(B^f`mpFGLIXfGXpf};UT+4R`r_ZkUF`o(Eb7HTot%;pGG#y@q>>xq7 z3a4j={m+gHa#FYR2?od?9Qw@IBSBAV^^JX^y|sM~I?vwU@)_n5yR%;;F74B0-PUHX zGu^GGUZWCu7YTy8ulUTmpEWTq#~!6_4EiveMV@J^#1k;qIk-^hClE6weMRgF+@FBd z|C;xqt!KU?miM6(ZVeN8ZeU=)j>(ZD$&=yv;vvTNwy~aWVCFl9$DEbO!Cg_5vH;U8 zFgZST(5{>Huba)=Zq&hQ)WNKKStp{UWpbzNt_n{o`^4l*j4MCd$Cw@YH>xcBq&@$CN)e3a@fI;Pr7%SHQKCYvzi@6Szsdl0OS>BNPXj-S1DDy((>Rte9Mub z4@pRzZRS4QB&?VV_O|D%Lc;R-1{-Mo}IfA+wtm%jBv4 z;6y|1vwZRuZNXE$`W2deS`Gr$UB?C=jw-*i)O#@pT(pmXzC<~HH974mTZ0bupOI}r zC!XyiL8)g)Qo2`YJJ_X@KZ|OAM}odig6kf77yp9$R7rbVG&b)NcRZN$u=n{MSHAsK zBhE9Ja~CeL+tK%|WGDvW^G&xtXeFsu@6_k2PaNK(2Rnw-ooO{6ZOf0_J7Fo0-+Snex2qo3KcN6hKhY?J z-SZZUcz2{g_iC5>zSGn)@tN#B)x&Wd*05E4sCxOH%RPEwPxTrzpPU9WXls711#)Ot zhZRy`pM)s3Hu3@)&X6EMH;-W9Do?*;z!}+0?9@4t} zOmP=xlp7A&fRRU7=UAA$gDcO!`eo3+&uO!n_wKc+E~}gab%@5x`-k|3l^U^)uUA<8 zN3|ASlqU?oF@&Hec*nDKGQQw!I~rgaUav5wV>km`2+Q0j`c!UOXYomvu`dZvTbHs; z6|Q|R91d--qzS(!^aAIt<9KXy>lZfR(Zjb|-II~O`c$1f9NdoJPNMDccM>1HihP4T z^2xi-?+f0$ZFO7PB_yau@@VwlI}Zi%b+z_0yq)i}y0BxpeIEnnFOoju>&*^1`rf!? zs=3a!Uk+b{wXz~X!hAJ8>sqNfaz029tW{Yh=-74g=*@>BMi`r`q>%^^Xx z>bph9#y-P=zmgTV!j5Q`7&&BK{Dd5|-a0C#rS0f+cb_0bf}V#O9L~QInbtdX);t|B zHa6{tAwfQHA)8dMG=iCDj!rb+r<29R?;cUwQL%nlMS{o+%xJgEuwwgZcWWnkNNVSS zyhbsohU#H!7BDx5LX5=3--T`Yw5lGvW*<2V>K=6w_m*7EhHsL5`a&=emzsMVp;*f~ zzbAqOEi7NJ-wTbqGt%WpJJdk?@SyouA3+!4v6%H>+Ivw^$#wDX7AApuPU1tcQ^4wf zc=!AR0^qjtUN-8y_?r8|JtwWv@L^8O{H*^2i}`Eb2y;;y`G~Qf(r_)Na7#54?ij4A zNKjR53>xcvyYHlnl5BKl7+rn#H(SbIF6KpgaWv|(h!@p)il^)9<))+g{$?P{y=@=9>!DJXh||rUgcV4)+tM^$&4w|YoyQ`) zlSSVkVr?8zX*FN{1wX@9?LZ0;fb3zZk9g-?mQ}#n^$q${``0jxq}p7bQeWEW`qR9p zNKoAf!BH+vDw+5=5r3!U}ZdF9c4_BgM0MTqTA1pQw7 z%-OQ4f}{_-NQ?NwiSjQU@{it;XLie`u7#h_)1slzACN&8?nZf3D^$oIdJg~mSpQ3} z$H!Lt`b?7dyUmAT!1T*|_K1tN?r@<)JnR$+I#}hUo%A+0EB`zQ4WEzOvfBrssA4Npz9@>zhbG@SLb!z(#}PQ>Mfpaor%9- zK7;k>7!IEn*c_DoMEBuX?n}%qpTU{Iu<1aXtZ;a=Q&g$=Jp8HnK(N8m-Cz%l)?0_( zF|TV^rtSf1T6BL+j7=3#VkBsiH!DnICsJS0r{=gbqjt+)nRcr#bw+$h&RcC+$L%;E z4uP13r))|L&`C?E8_N%T$f~~E5?Oe_CNrJHbWGk9KsD-7EpI`sq*K=AGoq4pXoBUt zC_1;PbJ|gE`SfTJE9)0KN8;IaZM(TZrKY9hMr6-!Dn8yczE&YcqfX#!mJe@;$4Qmf zL<({zKiA3GwEcWj+Of*H(bf>f^hHcGG4?q1=4i_{=2nc_+apG0q15PW^O+mN9mJlh zw!Y2HFPjG!Xwph8Gj_D8J)o8HVVuj1@5*#*!WM}vZjvKhp6KRfT>1DmV`if8%tgKl zkGc(5og5U&$bUcm7Mh$h<`a|M_{6?9)JpaE&~E$8T1@@yqt8^Kb+sSu0_?eT8i!|R zKP%dH3fZUNnAJzGC`U6NO`iHp73C&`bryi-t-SNz8&Mt{^1O}b?%tjNRE%i@m~fh% z*ZDS1cE+x<3WN;1rI^aU4T9@9nBP~^zt%pl@NU~0eFo6pft|%Jc^@8;NH_~g%-FE> zYiUs_I$4uFYB^Ou22nK{HHDfQ56+a#GlbB0oim8>w-;Pt+fOnkza+YN93dIEf zj!)|Z-rVSzRu2JzR@}ZSV`227DElNj3!9}%77sCd2^pwzN=RGAPfTR=Z$FF4fzD4~ z6+EG!pxIbx5qz|Zy(}mH(PxQnxPA}tU@x=w+J(w!0YkBFd7NzVNE8W@T=5CP1}$t^ zHxD02$aouCZe}kVTYl2fRUZ7DVGbuC@1go(wef_3ASi6-d-%J>{f`K3yhR4^LIU=6^WWzhSWQ_!U;fCxHuEG-{eiIN z-uKy%4_}#-1wS0twzb6dC@Cj;UJ3v3K)4<8Ubd{t+1T_oYa&<9bhZD=C&GCrYU`6b z_s}yq3$EUkpKkOs1lFeX=JVHrSNnja?2vdpWOLIRSeGC{o5R**=Bgd*#&i?%gPgjw z`^@1;kU#FNV1fa_1V1NxLxJvZ>{G3a+I-2&mCJtgs(RnFlM4oi<|M}?Jx4^hP_Y&5 zvZ?NBk8~b|J2bDWl18GPm4@754_~bGE*6on7L zKI7SuT|7bd5p3ZmRoteFZdgAXdzf1zga-K1B>5m`nP~g8dbczW_sNq<-a*-UKc3zQ zFt%O9Ghp>wyAfI2uB<+-y!#24#X!F^Q=v=!8=+t!^UEOgFK>Yvti^bm=c{L&?Yr!f zW|y5wiN{ny*h=Yx-SOxmU}NZPF|^?E0*=-%-z6hVdQxzP~;G zl-YFL0$m{s**lBfP8rwZU-`{PF8(ZsHeqL9(kUy;_^JEZG!1QWdyG!wXXdgJ($bfW z4a=BBJpC<_%_LTOqha@RSQG?VrMP$a7hDS#ja0gZUl|xocKk$w^k%8kn^MxXITVo~ zJmAw#tp#h;NxUrIWOF$Cnhl2?$9s#Ec1Y^l#-<^Xk6PFkADcgXvbF=e>t(D}*J<~( zI%D)(t`+|QtzKnp`>JiPLHmP1C|S&QhcUkfm1w=v``Zg!+rJ7%F--5DS)1f-9({Cb zZ@sZn8>9WeHd-rYOJyP?$~t>~Zuq6RE7jUgJGLz05@FL~#O&tI*v`g6M}(2bGyK<2 zqKm`3m8)9AX;tScKO{E$^Z^#XhXg^GW1-pBLkTub3JX29w+Clpc$ftdYmEN1vWiZc z;Lhy=&hqD<{Vg?KGWKaF~w0G;@rPELNl!shAYg|LQE+Bv5jNK=ScU= zJ0&1Q-#p`4wN9_DZ)^Nj_6Y1?xnm|-`SqJSVG5=4a5ug}w!FJ^LeVeo9SamKxE-AN zlel}cZoDrlIqmG(wB2BQ-K<*P_Zi-vjnQBJD&}j1rK6!?yq5e^@*zJbC?fsnt*R5g z)AVRy-kXZ~_5GG4U+g1110rT35<3FH~YK6`xAuDA>qZxCn#BjHUnSeG^UYz4R7Z2S~@TU&ROSz7nItzvbC zC8_%<+jM8NnIHkj&mZ`%?&Z8^t#_*i?^IbbS{L6f3z4(wYzdrIS4@liNdgwM{iQE| zb!ITc+K#lI=SQTIyaJSzk4?b&5D7v=<(k26c#yTo;}t$B{b04-?PZThKQ2;SlPS3$ z7?A~&Y&c~-5&0yEd95>F0r%ER`t+(fI>xAAk_4jCCvHFM)JU9D9w(Q`J1@Tebh{h! zLGyZkeGuE;zCeGP&TDP(bq-crK~?N)Hn~}6exY;KgOf|rmZN!eA{p+jbm?T^oYa8X z+0zQ~T{8$p^1HJHrNY2#3uiH$$iCO);v76kklOcqZlq-egs=6;;77~R^Bbg+%`Avg ztxCtkO0T5mbRV*@u<^4-Vv`R?#$&7oaWC2t){9pLK;>n#+TSF}XH{%!5>&*=Rl`n` zsP9t(>mkz?IcwT3=~0hb`TlqQE7$mbF7*#C44Oeysjo6t`DPP_zB*eMpL9Wj489+N z=csEAV*{)UW@jZc^rK39-`f1kH;V7(!v|J>EhhqYn{q@O zD}Dnm>!zzJLrkBGUr&u2SKnm6-KQwoN~Lrbp>;nkKJk|^YxH5^?S@;UN$NAVUlTDI z6o8LneENjm4*C?pN40mOVGXjts}N=nOZGO={_2q+E-aOn{`{~sEC+CJfQY4zP|2^ zV{=B$PwSIxgakQtR1Up<(Wz+^9ZO{W&+HiTmQNwGMEssjZ`yB+NidMAaCgj;IkGC! zrR5CM<0u)$=s}>o5?-}&A0O*h>RsZ^t*6G|~jT&WeBuTX2qh82hgagYg)|I}9N zM7u>mP^2Qo%mFrxhj!L~)=$Y^v|in1G-_jV`!TN;JG*G9$*!>Ntvgr8*y?Bzb@X<9 zVP7ZNde|qnn~mKB>(*S8;lQb!*RwiFG#RPf2iLw;h9)zc-ip7QS(+z%;zfZAy+Pu^ zCr2OjDX40f@4bWlFr;mh+xP05=tCXf_0vim_#DxFn^AV@44dR0*)xeABID|?CO@_r zt2w7fpQTu~h4HRzEsxV+&FR}c3X?#BFnuyU{h_BFhr91jrrsf5*2C-Rxl>1EsP_gcp!Dh2r^YH~f29c`8@4>Vk-z2iC1 z%nl0Yc(5ItYlm+K3&aP?S2~=~ooeQ(`V@hQ0{Jo>>8>x_7<>2L%nGl^Y23S#J!}pb&{q6!9&=cVPKde$Q!4>wF&;6WzBC#Hhq<+1J}wsIEA9 ztvJ&EGJSZRpqfhGW~_YojC`y=wD8R7z@#+ms?SdMOz69**GLfj%c)kaynAXbsg3Lx zB*>f8?)k~DB~rWK*<8f}f0j=t*u7wBH%8l6eK56ZkVq$>l?#$b3nXA;MvY< zM2Uv2g76{1Uk6cbf%`To5_iF#iJ0-gpgQExLT(fBmhuhrD6l24^W)~FpBoOg$W z?GCBF3aPXKfBl*Fkzpc(e}uGUGJzJU+L^aNAG<=RrB}d8t3MOM{#}}bcJ534e3q5j zJRMT2irn1bQbR4|;KCK^x^j+$J%`fF&-m_q0H*IVi~xdOsb-ZDuG;Q+`5U`wkJ z#pBGmkcO77OdOgQ2aVY09oLqLJg6Hf6A&21Dc{$a37=C%pRsz%<&gEPHis6U+NOe% z@5{*HtFMK5UPY2NN+dL6$*A<(2+edpQ`nygd!rfG}d3x8RPC^0E2FCAaJs=e&zLL^( zF>(Y+Myr3XB21PF`alsM0f*K#s$#LhjTxQ%d*vnQ=clnX`fqE}XAw&)y#4;mbNDIU z^XE85RM_axqYfJ74RwHO?lYyC6@Xro2#ociv^QU1>|Q>g6U3P#qWh<0mNPUn4OZ(F zSY=ajOe|z?A#yvIM84)j>*?60VJg!sZ!291@qRizr8rI3jnSbaOHkD*->V<}C*g`>aO$FwLIDc7Ae=k!Xu0=-}+& z^f#f9?=a<|)ls9(%xhuUc~q2zdBTrh#=ook(;tP@MIiL{zF9y*N$3js8~#nI$o#LI zWG1Zv)E|GUFo*}mtpney$mpIqdVJd;Q&kz0y?@ucfMWDvfywt~mNS_~!==MR?lX;4 z7TUGi+33a7)m=YNaN0LplXtTI^u&1dR8g~@G)Fi;ndxleAN=AxRaCWXudoBY9hU0C9rJiPjfP}RWsbEAylk$Qg|@4)jP{c+2EpB=N{~IST1%OQ6@&4ATY`w zCDJ|Leob=mK+pe&6g7UCcF$Jye0(!)P-35!S~c+T?#{h-f3t_eWwY%dTZsVW?63XY zId87sz%Jya@IkYhQ?K}<@T3P1F^zAcXkpD*86-T68!Qk@)v(?Ck}R(k{z#j9y#@Fk z_Z5q-h1!JIAza@&1EYD~`UU2VOxxkFGc$6;FZ z#Lc+-W%^zVK1}d~SoyB}dG9u?mX^1zKfKL$RN{_>8VYgT!-9fY1;?ui9p81jr>Hv2 zw}S=M7*p<8Vcj~t3t2T|PVsoslHk;#ml+$sf*I(ymEE?`8s1vf&Fz6VL=~ogqao$p zc(QAjK9ojbn;&Y?=Gw_lj+vc}r!wtr&>KdxR@{yR#dnzI+AWR{UAZc7|`jmmRHzT*`E!9)#i^Y}P0eHB^%(Sq53gi- zsoiebzoW2t+b*tF%Hm!$ym9+6L|bP;_?~kjCzt$7zxFKh=_Kc@&2+EaM+Gyh@yI&h)WK2u9_BM& zY0)cxgxnVo<#H%wcoR!0@`)e!M(DbvI7Y*b4$RPNLKs_JDssVN0cHBJX&&0GGL`cs z`Ew?ZyhqW?qCFxAri;n4cKT9t;!Md#4vTIG`Ogm`ev+I*WYCty^!g9=926bSG|Fco zRmz27{A02Q*FQ4e_w3OM=u=U-TP92v=Zhw(d2M1-@enNrT(aAKtenu<(p5yc*{UGU z^79*0iFvghrUM}ZJFQbI4&yR)5k1=-wSM+2@#=CFMn`4!FQVCzxFuMb;WzV^-&fzu zlh6Ydv^a~q-p~}iDz!7A3Ua-kdVTpG3CRJ4g1YYF=XVsh-(0PD?3qCRjX72HlmGof zs&0-kVMr@ojFl#MUT3My@T%TDsHH3!44j2NVcigUXqJax+&-lzqu7$$Fp!&JQ{Vix z|6aTW=`GowZ^xQ!!BA_*U>23rXD7GWh0N?C1!^MB`1Zwk8M2_wfnv8|Ym#NDFjmgn z4^?~Q=K855v=MwJ0!u8pE;uE^XBanJVPtaY%QqncYS^L&BcvT3wVkGqe2P>zd>`pCi6S+dyQdTaS!g&GnLDYudGCiml&e$ zX4!zHhw~$R9q~g{Is!k+h_0x}$AG1b#?&E&=;>L?p(42C45J+K23|sE3X+Y~ZT#6F zu9I)=xNv1E`Q&$pV*;Zpr~R+W2HS!V!3NCn5P5C7NVEaGnbIs||4S$4??i=J$q!&jb_$GJZJ56dMIJ2+xz!?Xslr=scAD#kU+0e3kmR-4Tc7W?dze zOd&0N-&+ZzcwNKyy{d7^v&2Y_$tapsHoPX@tK{k81hU)Ntyt=PBN=VjqZ=jq-X9gE z*n;8LvLCwdV86x4)YJ-IWr50CJ;!A&)m)!Q0Dc7HDd%`ypA^s(K`4%2h;t$^B_E^x zrfe?oG1Yb^lipDD%GlTyIZ}pdJm+J_cnGdeye-9?!+pVY$4&Joo8jg(>>rl5MNdN% zPK>Z1Te?}t?8&uni&*Z?yY~d2>Pjx(3rW`V`Xkgobwn@In6G zxQjSm&f5LA%c{xt4Ig`Y8gKW+SYR^j;AuYW-)JQxk|5JVqbBl*P`(L5h(s#WjE%3V z=St_@za~$bP3tOO5;d-0T7(ecA55F~n`I)2RDbeDemT1ATd1xjrRG9#L+{SCxp@6+ zst3pcfFw=P`D&a(gtXbOC6&m+~{L)o+t}f&Zr=*NK+#Cc_P+$S!07zFs1Ze0W zEC7lCPC@|21=2X+q(s9)1?qxO@vT%Ba1H?9MZ-AH2f~g)U;rQ0R1t7O0GI?wM}Sip zfUg7T8gRxv{|z4lFgyVByes)`=Kmy(nWJHIkF=py9#@ANzp zmre;4)9h$xhoF;^qJwZiIKXswl}(^pg8n6% z`QPpsK?%=)_p=D-Q1S&_1;7k}zxxTW2D)2 z-u#wD9e{*+0S^H%S_IyC|5pGQJ>qw}I{=Jvk$;TBzktz!hOsVi@BkP);x|92vBJ54 zQM}<^z=QydcY%KcC_sDr8y_`>*VBHtMG52+T)+$fOqlj}A3^EZ=e(h4qFunKx@Z}{ zaR3m6{tp=A0!HCrX8fkl0#wGj$Y%laulxfa=N~ZcKVZCnzz~2AN`?@C4o?O;h!}v? zQ49eG1#qI;x_XhKV%mkk5Fs!(9Rwl-;SqueqDKF}iy6hP&)*tyNNv~n1hQGBqHu^ZvwT3In$ZKEa0}H z44c)p40LcaQ3f4eWiDlVDVQZ(-qR7L;i;kt^|XcxnlXrr(TTVVxg+cmFlQ4wcZ7|t zlaRY8!#Qyw07hkVGSHo)I9rP{{Emg!Rene(W#e8|oZ2@Tepb93WxKL)gLGntvV(xS$8vkeP|I$zN#y9Yr&!khz^B!UPyAIKspN#%W~_ zvtXbDv`R<`X6vM2Yi0=X@6G9Evx&EL;f_l2+zYOi)xEjvqx*{dPK=*g1 z6x`WEl;K}?FZh>YI~QALM*suZx8Ph&l$C{~?Vv6wx1%5}$^hsuhZ)>Vh=<>tTfoee zkKK%q&zzkHY6@l-Ffro>9@*dr3xau}=H{k6f9k6`!U4B$V)IWr+z>tqFb`OYms?7J ziyy+nEhWRt%P-5z$H&Jd!6yUxQ&+*($r;#W0rsbD`2V`?f7-8*k{$G1K|~q;-lgHZ z36u>B$(uOIxI4pa&0uE06aowZL_iwMBgrKrDJjXr2j-KKfJg~)^9%Ax%5p*Yr6o|_ z9xw`^Tb9mF7juV%jq?R$14lV#AsZ8v$-A?g!OTruY@8Xy{^hAF5l+ z13vz5ssIfA?I|t@Yrq`-YT|#U_8SvL?QaUejWgWY26jQ>|IF{-lKN}!jQ^hNKcoTu z05nGa+@3xD1K)J#PG896+{8s0U}kVI)GuA3zcSRMSO)jXPjO9OlsFPA<8=U+Yl zHwtDjN4V?XB2G=b)${+(2meNMvNLyfGjW7TSODhu zznwdNSA|*v`iOEGoabFSSGo%|`S&sVeJ}l6u(Bx8olmvEW59p=f&a7p{;p&9e`EWo z!9Hj3AIqsfS-=_%1^%@-6#KJ?yG$=7a4CUH30z9xQUaF}_Su4x!psIAk0jnlC4aez(GunDeS#ll7Vdii;}bSW-N(QGr{te0y6L+N5Qqeg0ka344tRyd z-$cV1FAj2M^v58&iyPeM)BT)(r>xQH>iq)=~501jrH$H2cY z3PcM8E_9f0QST-=fAs(=MI8-X7vjQ03w#VTeVz%z!2I2VD>$a-eQh|uMYVQbe%=dm z`nSLhsx{yZ1%Dq{Q06z^@CiXYzIY&C;AIDdz|Gh5VsiQ@f62#yOwaG|3BR2a1)7Zm zUOIw-2_TsQ!T}JzT|csZZCLz~sdAv|Ys54r7)9gTH5^m=V1J-LnCEw(9)S6CO2CEm z**OpBpu6aQvUq;?;hc#p>c0sCWx)FofU$h+j{<=gNuXM9;8HHS{iZ0VTBukYTRc`d zjJL3>+18fkRpb7ywf4AP&3!xd+s_$sA|wGS>Np`NZt(msxberaL{S8RSSS-v2^#RO z3t%)U{|pwO92i9S#OHDcicQH;NGOdv?>88q7z7MC1~GYJnf?wR_fqz%XFi`{j)P-4 zdwXL?(|cAL3Myl#&xw5wC%43nHETYGHa}ZQQoZHG%JAj%`=i2GmHYyA%;&eCW4`gn zm1h73gG&m754dkZASFyLB{Y<{F@Y0w<%20Rz>qEq2G~PM9z^bcF$ieB zs4?S03k2kXE#Ozhia$D<_+vs&Dt+OLRkhV-D{OW_SugTc=~ABew45oTT7|eWVV>v7 zXWQb`yh&*5ghy=N5$~rIswp?IXG@dax3=^{`t!Ls+QvA_jaI!8uX41T%jopN$*o!j zG~-FRp53N@p@1oi11O&YZ~^{F(D+b%T^tvxi0%t|?9cERm5E{xcnbzSiqWqqIbskt zD4CLDfE*c=UZw_>bXDhb7td5$8ge<{%WCsE|Mcv9?GnIR*bg1I z|CR63x;sfL*q_k#t!rSfKy*(WvQ(*;AG7;In1;4yp@1#jTF3DFp8KTsy*f|Y#DNaD zov_Mq_`3UMm%USZzkqs2D@()v6h`tQ)XQrZy(6xU&vU1gSM4^@?H3s@Z1x^T=KWo^iR3K7#OyCChh3{F zH7TK=MY;V_`>nP%PTTuW%f^O;ryP%MRr|JcL^eG>gnD)hDN(OhPkHVN2^n2q6#6vv z*lg3urJotX%#jpX%xrU$Lwn-5Jc6sZ!apQvs$?1Io7Y2+VK5E=dsYv&i zWLuGy2zjp6OvMS<+2Bm`t?)=pb zFR2d4=9cD`i4jkN6i-f34;TgcFK}Tiuql^u*5X;{!clI{tSgy~Q2w3L*u+I)Z_`pc zH}lN$ftu!l;-^K4Tl`7!rh;xp!0Q%|`9(h0nP= zeQRpv%LFJ zMXGp?KhWG1X65OVaIBg*r;GO>lP@J`$@mo@h0y3%{e0`ZPXwPJL0tO;T%Ao{UL7P& z%kKKfFBNb$wl^%lDo9)QlI!@z*;$x}DAns{t)3AU=?~ecP(J~~i`|xKG*;Y)Q#Fj) zlc%v0@y9>YKHKBnF;t#vTYBTgDV&|4WNc<$@(h?paL55WjyoTEU}6IN%Uhf)I4}@M zkQg<~#DN;5Fq`Oo0U_8B+q`mQWN7)cn!oTxXk~fIb38Z2j*7vE`-fo|G;iSx$3)dZ zs?_Ysby~{7QjUr}75?Hm54(rW8pfz-@pg0sMZPRiQbzlp5nzU}F7MX0qw)P-7K&ESt zd~ib5sostdrTKfcVtcgW3we&#`FvubOEi(~-q4`DOaOKg8vEK4g4Rg9% z1|t=H&5~zo^l_}HM+}Yk^-Q*OcBMXMJF&X8-vaqP&jS-7_unkx~;S-}I zqWm0@<`|Bv@U3#_7+cgncC%8kYmbv>uVOdJ@T1WZbaLzS5bt99r^Wv4bVehNSGV%x z_l&tq0KaIKE*8J{nyPBXW7Xhpd!`q0$l77olR@xav&GBAfNsP0IvT?_TGCF}TAHhc zS~C@;-Yr#OnAEvVTrW13xw+wKk$QYH)p4i{5@u*4dZ*KiG3Iobfd6VHO|#LbNAkP0 zD!Xjf*X@flA)?$Jzr+;7Al4><~YBiS%_=nedT}>4pINilB!Qhle&D!Tv0~%`eb1oG$U-a{( z*)3);K42!&Bg`X-QQ>Y_PpCzkY`xQapZ1eYZgLJ=={32Ec7fQci1PmH@{PH5PSZ_inf_v%ylG4;&ZXn(pkV#Ri{qB_fn=kuo(aMz(*s+NBPg@u#dl_}l~4r+b2Ox!u>2t>05@ ziiOv?8FGqRXHhtJr#X0KQMBu4-f|k5n^)^LOy<`bzP_^F)t+4}^rTB8FTQ`HbetkT zpv#M~i$8cHb6r(==E>_jF(q3`Pb~QRYKHrcRy()&gw_`TF{MN=eu-S7-p`NlatH_P zZ27c&N$B&JERU!ep-tY==$+vkyu-}i*PT5d#d@a3F6qJ(+t(_lGWn?q(vGHV?p5V= zgvsyPNp|)+8M!f%G^tJ55phIa{RFJ(e9zakz)fRbECaHc&fT#ff^mL;zz|id2E$-MtbW6xd%S& zr8c$Y*u;Qnn&d_Ayh?{4B+H7CzLEoV%XUAp7O=8LtW+Q7|23d@(#d!1il zndo~cH+1D<`FI8AF>2w3AAT*+M*0~a10txcE?c)ft7k)S^~ESu>*FzYils{psKtO1e!)<{4WM-du--#(z5?U&FG1G1M63{P5 zRc>+GF~lLyE1tZ%sUDFxvOBG{_$79+s#AN=eD}v`fm+AT(DQ2galw@E#k25k!z;z` zk3%NOe#v$jM@#RH4uo?kS3^>PF_c-#7nbX>WYg9`IE&uo9@-s_uWLJA^dLWI&i2T9 ze^lm@m&7(?J-O@(RI|wOutn$|35>_=Kh08zRbHY=U4#rO@7WsC^MZtayC#&hfJqD- z_!ysX#DC^m8Wk{yN6J@1I9v(Wc&P9Vctx`?E!=7dygl*;%~m{V6OJFH%y!o<2rawg zS!H>weJT?QnR4SN;CJc~>F+!3=QDox)sxg=tn>AkcCVXVzlixeO$0XV%8n=fIVsKs zC|58K&a;_8b^PXAd+PnXA~L`(?UOgLLamno6HRvzEg-|Ui@B`D9((LpHdVU~JMLVsY~L&3+w=DPzW)TFoxu%; ztoFHIKY6j_yqsUry2mNH>9Lx+B=p)ozoU}EtAKdY{0OGgJIwthk#SIDwdeGNvVZ(4 ze?r;V=j(_{v7 zp#c-!n?yL-yi9A*L|bv;Nr*Amng^8gN#>9F?*PG}Wd76xL&3y}Ww*F5P9u(+8a=1| zeIh-n=||leG!Dw8D{h8J(1W(c<=s!krNYy^dtrhx#()_ICvjYHB{lkC?DK=-iycL? zQ)XLQhp~c!ttaiR(e1dZljU-a{?pCs?_hf-!()p+0h*I%O`n)3_5e?3R1S#oTVl7~ z-O*5ok_Gm~M6=9SIkiHsHJysSjogznS~N~wyv42jCS`YW)#=mGYIJ^!bJwGEkM1<6 zp>dYe$6{KR>fBy|*VC_-oYkU)Em$jOPE_0oQdT?!_C-0xb=c)TfA_-3*h#XfF^(^j z7nOUlQITXVgV5Ook6Q~FR;LOa(DE;Rxk_yJY?ypJS!}3VekWCLC&Vhr@RYrk_?jYY zi~{(21hsm)O95(AeiPRqk5WH|wFC|5gzUS6r&G4wca(>AYs^*BJ^Dp&VvKK-){beo z8rs1+oikPGdmKG&kBzP0b$;KqFFr`w?ex0Sxofi*Ca@RTNpHq0d?r;qyX%sOe=R1< zZiyyraoYmbxdQBf*jMJNZ|!NGB`)?B2`4L-B^jF-u&bN|ZMYjwP)Hg8lZO$Yj*nHU z`BUY-S8reoeND0%@*)miKkTqpV@=uZ9XC2X=sXZneQWa7y6iabU2V^?txJ=0f-(}s zud}FFwN~^0k@Xf(Z9QN7Af@mvQi@Bl;%#s(Zp8@>0fH6@5{kQ*Qrsm-uoldx}V)m%~Q{IK4w)gLqIzw#Lv(XlnOurwZUC2JJXaqdVSd&6AiOE@Y@Pda$UeR^-n z{8~3|Zz)-IY{#H&6*M(JcG7s1F;;Qc?zr7@<=6c7jk9Xy><-i`B++Xu@nRW{2+Wvj zD6Q7xKKeI+nG5c(!>p4=21I^3c1;#HrP8CChL3I!$=1n~9}OE<@5Zt{Hyc|+*rxiP zjqf!z(~r2M`?R*T3oBaPKT6e3&0gq_P`T)f2w!;oiEg(py>7GLZF6UPjN0)Fp|(Tp zMU7#vp5m9!AGj@K*k|06W+vV9r{62Jc}FG0G$yY)_+E3QmzG{Gn%G^=HzD7eGVo$moR1DHQha7$h$92MrS&%sCVnv z>Ll+}SdsgR|AO-@;clkm?7{nkrcD2&rgG%?hy4g%idY^U&~01+D7cKS)@=$6+kmPj z*0(8MdAk-hkK4IA8^HL-XN8q#+F3H|S3Gwg?ai;rkG>n{BNG^lJ8cpRQl;bCv{n-P z>;vxNv(nH!{%nuQOk>!%g37|=C43lEFT=6AFV%(%9gyHgrFcd@_CzB|bSZ+lV*eL8;F z7;jnR_idQcb@~(XW(8`+dEtfdDu}&o=dK1Lv*N!f3WNLay%r7%M`lbf7Bi5~1J}&I zI6es}EtlGA70x(QS&YjblerkZ3E-?JFB>h8`r+O}1>U1}-h18Vs{C_m@T5WIfIsf) zgk@K7C2jP#idYTVqOYow7srd0(`vmdfdjFGA6_qBL_uwfkCB-+Y3n*y8R$~K<(@su zdGvh7BJSe}6_Z^%B-W>1>iEu*Eyve@OG8_2)Vofx{v-KwJ6W3vZO^J>BH_xTD zjrrvFCf6d#>}j7e{R}-uOp1rOOq=y+MeM*Sq9PL z>G9FKIb^qig?8EV#=od@4BMt?(t&F1Rjb>p9R+e@nTrzzf&`nlV2dXXz7efRWPl*oa2qX^t#&urJ?Wb zN$%yNP^%HEWw`aOo}XK3y?_V0tNtrGt}HeKJEsbHeS70x$o9@`p_?7cw4@gmh_sY3 zhOr+;CCA?KxP$B2RhRI>xo|IZV_ximtH9OV?S8Sc={JJ^?bym6c5@*!f|9kxD1yQK zK{wr`9Z$TDI>YOtbu;T}u_g1uhD65DPy5l-t~K?bEIz;YbzZ%;u1A$c!?UMH%hYZb z;}g#FD<`=5o)#ed&vgxHDHHprPCwdh+DF!YukbhV`K4|cJ{n|k{e-#-bBJ8pDlX<8 zc)Ii+3yd}yQX8{O&-=KNy?}JwzsQ<4yun;8PdlllVgp7W-f7nDctyMM`XzL@Upu7; zrrfz1`L3K@6BRfeC+yhgc4`gAw-12a_m4cSSQ8-zyY zZss&I_jjtO#ByQ`El9>l$2<9Y?C~OViiEbedI!4MooxDfinWJSY^yI{KQ!BV+1XoP z_%<|+xEa?EgObblQpd@?oW@{S`c~P$-wpQGYb?E7^6ySJHG3I#j=XPQ!&@ zNH!Z#X8EqMHO|A^Zp^|F1%a5G>O`4O8#awVI;wrgSOhP9P852!Zz%mOd%&#|S4(Gw zhn01GnKupI6as%yBsMs>ZFd|;FM{g9hO3Mb5&A|$!xvj~m(>0gLg}`wIzK&+NVM%N z6*~GfO;SsXN7~91SWDipS2`8Axdy7-81n@Q@06}dSxaJ7?DCs-q*soV@a-fXStED(>o~zsD=NzOa)P}t7n_cXj6V^K z)qU6{pTSrg;3Q4IPlat^jzm#gI3E&oYXzCVPK96!M4IoChbj~&d{&mG2-W?D)6MEi zxA2o7G~R*4wUQ#7LFQ-5yJ5WkNI2f$f!%h=+HB)aEw9Dp+_#ft+weK_PsiHZZXMqe1dp zA5pq-P@XAC^Aa(AMv=h$h9ZGNgMx=@RX4a31y-&FD!pt5^ugucjeh&@+rJ0iqYz4< z@c!cgk%uDzbt8$vmlEACC2L-fIWm0ZRFnt~_{Xh0(OsO1^8ZLSPAP2^B>RKcf$|yk zwfr-bm(NhTyT_G2Ajt_lP9#V9#06nI6`xTgwMUz|il!l+Ghz@&Mf!x8!X3lC-VAuD0K6kx=U+9TL=))qra=IWxu)d zoDcqa3VETYLq7jI4(%U=U>d9cX#TNU{L;}>*_>fCp&c&?Vx?oHk(d2HP%z@P5|OME zk`yDL$A~P9S0Ip=Ldz|BDIZD4DWQvk`2~4CRy<^Zw2TDu^jLTjSU-@zA}kHg8avgJ zLRF{)M>;*Hb;fSo{BrN}D45@zyehg+p{qHjh3eFk**a}1J)8!a~!k=I~ zZE;8^W5CzMAizifAxR{fa5$X;n3z{anL#T&v^%teNkc0?Mi zeJZ{^k-bkXXNFM3sGk6X5QD%G)wDZBB9#>z6>Z?75{)hp)dab9>J8zenrbT*0+O_J z!>8k_ux!9zElR(@tFWN`MJ#l=cnqas4ASxP@gM^EForPs!brMY23ajxB(`|z+#&!i zk`QM28LUB$hxQ3Epc@9wUZ@Y74}-C}Z>&_JZaw+5k~9)gA@YNU{Gg!F{3}tuAzwiO z3mLo7)+J6thlJ}AVhBc{npYxq72h#XirM&4cfG2QO=Xr1efL`{2Sw7!OR(lgUc zJozrOBtA@^9Av9qOrA{1SQ@A?>*|IEp-4(WY8m;a3tFU;A~Dh?ewILahI|3u|B+`) zg5nvMG^LEC72!h~Guzn=@Q#6@C#+kgl_T1Rp0x?-T1TGKkWoEglD!OpHf*VJ>HAH&W9O zX=|kAF}b^WQ7{!S6~JJvPwBhy%j5$lDiAj_rQm$&v!zBn6$ai=Xlw@#9a$lgkJUhO z&Bzn*{%>Ui4a&3reP>VI`?!&<^D8=$(koh*>m9o`%c-rmS#{OvLW8$1TV@mLB{_!F zg1P;Bm$O18UIr?Ld|H>D4K1#}agx#(_o@X>!h+BBpvgKBsbd*&lu3}^exkg(hGT-x z`PGOnBR^5!fi5X`;Wl5};~5wHU6qvW(PYr1kk>_JtIxZjxRc(7MVxJ{9%r(73g*bQ zBBJRqPDRE31Zk~KESWfD>*z0etx?55FEEc^tdL8nh4LANQ&EzFq?41gIan~GcU zz2cgy#-z;U30gL2lwbsojfR0i77+Fs8K6E}5u&00Tfu?s30S4j=bWhQg=O7#kTaTX z1YOGme6l-Iaf9Hoa>Yi^ayfQeOG9bTptMO~N_76Ckc|G_#evZ7xhMEy6)eYk>~)yQ zydvr~;Akxz8U1pn{X+;QrBUj+`~97($I=#HHa$V5oFzu^^62JKA$IKB>eks%VaGUi zq}|DmNsBw&&KB}>=d91*Qf+6ZdS1UfZUx#Hng-{K$bWC6r#)gLWsjp5)!UU)WfX|3 z1srGhw4tEas#z>kj2eNnPLBoWTMP;n<@X9;jBNUR)Q;9g7_wGp*13I>iAjU~ zylAT(LzIuS(hpY`)06J~0!v5ixeyeq&*)iY#A(bW67-3AlM&Q&VTy zUJJl^VZ;4s%O=n=QXnNe02?AQXwOn`hof$!)}}l%laO&zFQW z`?GJR(&?yn74`I(+gsC8u%^u1DGjAi*NWv0&wJ>nVLmPrNc_Q8O}~<8W}oe-shqd0 z0U|kjW`EaaLCR~ALZbpFC}pLD#jMIwSuB3ZhzGl7#(E5#3YjTxT^8!iUFaPqt**{e z*7lR#%6KKG8oe`oaL?ve`z_~t&(b7Y8Tsh*x&Kh-)EX|DHhb!MgxN&idnA37;x6t5 z|1{uX<$4(k23F#^DLW1~^k8#ePQRYk1|-nG>_?O^FkliBbW6lar?1EFrSzQ99*QaK z`FLIUI14FC?T^i*{#jqSk*;-~&BmI;q3Gy!V> z*^m6&&=jyK_Um|T;(HM9v%jo25Atv|ELn_TAG8e2eLSq@uH*gW0B>seF@416TGlbt zw_G$_Id=%Jx?umXR1jQk+}trK7gpalJ=;=T%Uq?Y&mL^Ydd@a9+!?)!!^!E^)a<~w z=z~*h4~c4-HHBOaPWO#g4n(Vp@m0)em~G5sWxwWv_mqg)LUG#It3z08Qj!cYi}b3h zxpv~H45ZshG9)2}C;I{w+iFXP zOM7*6N;~ET!)i;b7! z8XoBBD2D_PyQJG2^7q>C!`l*2TS{$Mg7PZjymVM2=wCzD)yb+3OkmN8tE|z!Tt;rn z@VTQLgcj>Pg71DLd{WhJWXEgaIyP;V?JHj-X@aO3Dh1XdqWKs;yJ|DKADN?-Yma}w25HFQ8J7PbyuVOmsdG^yyBmI|qW z?(7C=vNLkUP^;H^0N%~T!{eHv-0Ue^?CClJ0&I-vXdCcxzO|sPOpX?2t=zU)PX&W# zq7A8VtQ~I>nKHpO^XgpQSfx3cW)SV^`ME~Ntok2w+n?OXALcl#D{$PJS=2$(#Kq=1 zjxbnM*6UP{c@4v;ckJx{*|k;f*AZj(y2(y5F=oZ2r6pv9#Oh0*B|O(@{88p7W<;IW zGg&A**xyCBP{0k2)7qh&2nx`)DUE`~eOWb>w<7_o$jlB_+qAPrwg~6&69{0wRQR>{ ziI|U>#Z}$q3T`Qwq7Bi4p7U^%wA>zY_S9Rt@r~{OwuPS0dqb1srd*puN8G4oC=wet zi6HtuGFvMty1zAQWG7OKtMZZ5Q@pY!iSLe0o9N4jHu(wHJ zT!5X4+K$m|mU$n-RZUNCD6nKZfdMB$_ALRCfdYI&_TGYkF1NXKz?w+qNIeLLbv3FV^rw|Yj9%A zAXB(iWy_KoWtH|S!Xx!oa3E#PIHhF2A(!N0Aa{L+ zj`|nn4nzKN5UoN$fh_ybLZ<#&!cCQdEb-B0&f-Ej;ae)!S9gR7f$%cWyiRl{#ffTO64eGT8v5rF=d_1bdA}F)OT_3(@D+QP4?%mhrb>q_aqI5Yj!)oo3cCGJ~(oEclwN0}r1gd-)m6hrJ}V`=0f$ z4RM%)KI3|G7Wm}^@p4ds?;68(Ly`E8U8r2e2TVyTZ=&&t`B07GulUoCI23LXcH-|t z6YIm@W%99q=pARl*88#`QWUl4kEFA}NA@lL>yyr$zqY5AY&VxXJ?6@MeGMdriO&)r z1vuENl&YBp*4m~Y?{7W|J8lmLv+*`EC>)es(X-K^o@YgoU(B~T#xp)w`5|xw(TziY zd#gVAMec?4gdYAgLEBDSllJfrwX~As37&=0U@3P7HEt;ZNeYNd;<+sDXE7n%U zPY3uN?X8dZR~@};&h?z3e(%ri#zjJv+|eOg+DSfIu_?N-3V(Kr&O5=?956GjTJDF1ru?SsU1AiOUcp5;c#A#&=8 zL}qggZ37*T57>+pHf){S{Y5dS=<8Ela@GzaC7`9-E!u<7-)Fj#i+PnL9kB_;ThvwR zEiGQW9m%tMK^inZ7b8|A4=Nsv_c{-mWmUkp!ppk+Hc1fr14{ElXiV-l>Mu&ucw6Gi zPHG21;$#H(^j!0@3f=gZ_OxOBHLLiNNL<8{dmpLUy)un;|}F(Q>ZCx|_iMR<~lJ;hFIYhm*Lo0mNOxaNEZt z&vKsy3p*^O3C1_<+HWPT-tYW*%|V+fY|Zm-8XGRtuW`BipsL~V-P%iPhMp~Hn^-z@ zKBblAXvyvg8R9MiIPu7m>9yr1$7N=I9w$Z0YO-lWTh#J|jPW$$37Kb~60lkJ)Okn5 zNzmP~fgc&Wx0egz*^nzdDT@zMD*KZY;D*q(+xR8l>{S)Vik59vp_^BWseZO>j_rpg zJ&(bO)5XM}L#MI{DeV}Gpi?U_{k*+)qA4RLJ@*L(Kofjjeqlr_Ij^-jYT^F@1$Y7# zlTump0pYkU`!o9W+w!SLCU2RMI1k%Ct|RQM2R*|D{*Wx*jRMGW@7l7DOX*_Gc~2XB z!`gLN&r{=k)mtZKwA|yjTpMgXV#Q>%j;fuP+Lp_?{RjyuKu(q_L%KfYWDfSpo;61^8y1xQ<+%c^7V&%^zLiI(WP!JQcsCzLXR|<`@~Pp9 z?M0T{E*F4JjX`99Cl%RoY2`RvvitwTlAiAQE}-X;UNEy?PN-wmSaXusxGnm9&T%5a zZnDMT#>b_5v0=$z=E0@qcT!Z27jU_i+UaLk_4c%ZblUe4IDf9{jx)YO^f~9+D#J#C z;c;e1-~AR96{T4;$X!I;qElZug!=l;hNiQyim%RSv#=y=Bbp~aOxrfo#A`ny7W1$zfCQbhw} zu7>1aPFTFwstbRA`$m^>lddtk;7pGCI%|XA4D{|WN1ILCVYrbbpE~(W>0q`fZ%@oS z+PTRn3Ah2=lN&~ zUJY7%DK9x^Ok9rTH7w`tiF=)-R6KQP4m;zw3Q^kc~)F>}vlo+O$iKtu!lz z9#4cg`98yVof)Z9&6O4;UQhem!{15#ve)S@J$+nO%arpkD60-qYPi~x#KSB*llQIs z_3b|OMW>}X4wp@wKL;MEi4z#@!1~(a#j{3w*u_e=am0&rK@YBLKcFcN!i?x6C9z+( z8GG6mG?agM8TbJ*Xi7L^xD4JmOfzIXYN(hV`YszD%y|KOcvQ9XpTf+O6Qo~i9wXb& zKOeQ};W@$6vAQ|>i_*gsL~Xvr6h8Dvw)|=z30ie(<#$L82jCPNStr>$e=0%8VLg7; z_wWq}G9vI(E?7M>7WVKk@*aDZH6lFrJnJb3tvvmiy2Bye!H$%M3U2+d7`S zhanph{fJ!=$EO0Aytu(PKYQ)0?x7IR25T(_W~E8?`uaenkB>!}up0`s3b-aQ;j&u2vEESX(v0a&oNU~#-(CfdwNKk} zu-w!-JZz>t_J-su^H9kGQv9y2Rrj^k!XP6~aaJHKdSA=jJ4EA(EQ@b(%zBuDx*}1K zA(nLlvl%nYhFrCW$B*CI(O&$v>Mr`uzHoZw?ImzRj4|aN#@otus5fN2IUH2>7dBq^H1f zB=@e|b4d0tN;3m*>$KDvQs?f0@K-_>8)^G>$;rRrAm`Sz3cFP4$fBmGQNavkf4`cL+Pda!&H z8Xt4*O|5$yqJLGvBieEw*UBjKlJuztl6}mL+ib$owv78cxGpvJzpJu9B`?-lthcpK zFLWIzkqx$g;;`{boGf=)n71Wm;sg{k#sXIv_`}IK{`nmI$k*$rHzE>`QE!X`VA@+% zKCQ}1qp$jMY>-XVmjQiE#KTeQb61Wdf~u50#M`jz#ogEGu3Ziz?l{Ao*ZA%U5g#z9G}3h z0%W8#vO$=3O=i9=6p=K{S&k~ZX^}CoE@EF?nob`&jJhlj+kPy)4%qXD9;95WESOCq zZI|)b1nlNf2gZ+&x)0p~OMLJ6DF;{B&Zz|rSGk~DuV#HVMiPKc!i-yw+r>M{p6|^r zW62~7>wg?=lGQ4yXRj?f@B5MLc*s%q1*h#6HnUuT3z*d(L&UiSYwE)-W$RaU8!wJu zE8chkS6gnL2d43#qT6{^i5nM-Jei}_aJ8v``rgOyjX00W-HYI{W*~A+yFpJ#fsd-% zZFsZXoGtdgskOWU#p1<&UF*VA>TxiW`LmVz_-^Q>ZJt}pHmm*BL>qm2%&2+~R#*~U zh~+V~o@v9ip?=cGmP0T?ht3dj7egf~VEt=!ZSG>n_i!;MO(CX-$6n_uBum{3 z-R`oW(f#mueFFTVYBdetD z5q22e(KkfOeMj(XXWD4t#@frK`(n(%TjH{y)q5B*ur*<{bs}iybXgy|lbw8aVSS*l zK$z8q^|o$}sbB-G`H%L-s^XFNu<@Zh`eN>k3v6%ktJ*}6PshBo<&x7~y-r=SENuGTW_k5h!cr`faz`e1=({mz&N1bc<;NWePP zJtj^5Hp$e+fj?wvstsmXgP1kguoA3p$IwQ`8dZ;^oHL%Pr^K{%+Wz%MTvitoGL_pj zB1MJ)puKCOwU>8yEotcZFV)qdfOJh!0&v zjy>**^l`nJG~Y)~J=}ukXNQwIfUC(F zU_;|ne-GR==+&sqkQn0Zd26{?&YEf2+6p_lp5ex-g4e-`hy9D@VgXsig5_6IkuDp* zPrrk8%O0EDyK8a7{H9s~W3I;~F81*r1}!}~J~5pIhaq|OO_m=oZ$kGu2!cfvL+InQ z>+xM?bUj_e5w>M&M9t>C7sdgna?M4pbT=k@Cxlbfbl#F}30KwMinrdgYZYk`6sB$dEfXnnD$sw)EZKQ0oq_^08@XEGCXFq z*~sQE%KNN_ZTYgCH`kv}n?!2yi#NEdYJ1DW2&1hTw2OD{x9IBOg|j?fZj1^kP$G7v zSu}aim#|@1T7=d4cl|Owe?qsGg`BU<4kvX<2_82SQWvC|zgA{froBtt3q+~<_$L;T z?)|2GR{1u`f1URio>9_P;|*~W|9}XSkwr+{t@Q7jR+bTgi(dkYJ`4WcwQs^CN?BeH z$1N@oO=+Zf2|Tw=1rm8K;5iy7(CJ`aKvkt%=R9A8K{*ISzm>UU`1uyA|M}q3apuh6 z+x0R7=k39T`dyHl?pndvPOWMfASd3^b_9ObZ?V+aw;(kkchT6NH}59mG}Qp=W8xbu z;NTEXAMw?!-%g88Yuu?*{scYvi(*Jc)ub80Sy`pT5{$86xsm434{A;M-PAnO5+zW6 z#VJ1RCy1+@_;O45S#@c2*niS-F| z%;%Uo>rKem@cX)*sZ4mLgUEuFAAOcbP&n%5$S2l~ib;cdvGp6N0Uv53i>Pze7g<%$ zCt8-pa>EAm_LjN=z__9N#su@r#R)nUUV0zWoS;s3LUo>{MplG<>K;xvCYx=vMIuVq zLxkbBTwT0PV40)Z^(3bFGG0L=*xAcp8b^ltZ|v#C)29Q^hz0&MBlPWT9);ZeECn9m z)G*%mu^N0CEi>DQO_AT+=k{m`sX0E$9R-J*d6qsyeK1N(0+^b-AO z3cOFEJNCM+jXvazx2Oo~zQ|I$)w?m-65(*=4@;s}-LKh+v}ob@lq{R)82U(+H@5zI z6`^AjcW03PXsP3MCD7VDer@}dcc$K@cb#^(a{7xG30S=FNsDU5{`#(jChlEj+lFXC z()!&8&Cd-z&xWD5og7gbe$v%j0J-h5D@?_AiQuVZXp;0%U!F5w^_hUcD#nYwu zrha?OHXlxl7A~k{O}Y?&Y!A-^KqtD+M1IX&V_P$LnbK3+q?|S2D&Tfk)9WLr(2!)} zFtq{8dxUrv?P5W+IiMg0=62Uzp>NJyRy!-?S+#bTVYn`?-lqI3;lgiG#aF#H<#(!b zZF|qI9Eo@O{f}2ft$cesH|E>2cX9z7ZXxlvyj_7(6HAlDv0Jlf%ZLvJ@#Nb=$un%L zUIBBq1%j{cFn*)R1T#Dn%Ox5jZARZ}dn`KRS!BBM@(*^+xIf^zVLnpbJFA5kuXwj>k~@0P}ot2~cARU>(h|4Z>w>B&jwwdPs|x^jP*80GI12 z0)Xt4Ad(VVs~p~JhjFNc$HaX=817?@%ad4zDS7~FpBJSt^#WZVcI;69_7>T`w^l=MTjp>w#01tjw zm7}D=v+Tx}rbXtJaTa(mZC4Drk_AUaO?%jo`B1d6m%k&$mKge#)hgquwcLU=hvJ3N zo7#`%!=Oz++9I3tsUYFGDLbP0%r0BL%1|AZj_^f{?}5bEMQ`HtJYs*iC3FWAvYR!v z(6lhl1JAYxPt;$T!42rVQ}Vb<0;Lb6Jzdvo;rF{s!<`D|ct2|yimizT*i`Xcf&Y~j z3OkTA59b5DL74)DXIiP$+=C54j(KR~Uz^Qt*s95LDjL+ZB8ZgbeKrcd=Dk|!wC;oY zugS@V4)*nGzs8rMgeW!*A=o)&mC z%@olsS^ohxL7&%EcCxdD5v4z0f{7S-v>2Gx#LHW0lqURI8x}HzoSJ}afIkd{^;o4- zbtro|>LA1B@ysM2iqj#FBHk*GgQ4RA+bF4+Hx(4g^t-7tE$t=Rc_YdJGPO&R_9Gj6fe7Z$j4 z#d;Pw-NPrv)I#zJVKR`Bx9KHg(M8=GZ(r*uGsi&z>$&G(FpU%}amd%#nH%A5vgOM= zkUo+;V8d-yEF>f(tnWI;*ppkg|J5^@Dl*CQ6F#%?UQ#FP5g9vy$ZVUTfcu2m8B0pC zqc3CRseuLlT0hPq=FsK-*|rYh$FcJAT=)jZ`QCtNgJ3N#wzcOu(Ja@L7#(%|n~V|B z&6B)8&C@g2d#;WB=-j`fT}}!_aek=B%F$000phP1H8G-G?g8#uKeq)4)=(Y)nUy{2^h9^5j=_WV%F>CZBn{*?nb)0QT^crc(jqnb6 zD+wCD^6qt{O)GB<<> z@@XFL{zJTMETNLL#LT;2F_eXw3^@ea>I`Abp@HN{A=|q6sOr=d&b;6AC4(9Ai$em) zyJR$M3c7z$O_LtbFO3p6CSx0E?(_{L9N; zE!{M$6Sl`qCj=u}2bw>VOMuXiK8cyaNj!j_x>jR6DGiz4!+!Zf=j&{^PdJDO`n-_i z4XVsDS6o6CHs;?ciGzt#Kn2>fuKu`@p3eneYiP%n>GAgfm%xT9_=}eU8NJ|7UyH}_ zDXftpmG?Brdh;Q&i|_fiUid)qtui?#&BsZS_xlZL%tLL|HbSNJNuS1ugud7W*b-?Z z=f|oOWV1BxP51)$hNaf_ifHPa!7-b##Vh4M<(D!eugDNNJ*nI8Z*(*azHl%7SX?$R zzS7&aYSc5R#|G~cj5}0($xGhx7~1RqW~OUezQI;ZoHr9qBwJBvhAMD2MsEATVR70T z%St-T*L+Q_mnO^*eM#lMAw;@XzlgQy)0$dD-YLwg{*4ZiTYOu%7nFz*GOv^)aO)Jt z)``ZofTm*G38RC6M7AIcFy>_^Bxesz$|DQIFH+=`Z6eMMPV@UJyXG(t1cu=NiC2fdCNZu{!8H*U3S zqFZmlH?b^%4qz8(Th)L9pz3?HHmLGbm7FxPdL}{k8-;_RT6u^;2ceJHW*no2ok|hA zitEAoe9~XdV#+ybq_fi)jFqU0g^E+Y$vL~Ryv!YQz+zB%;N}Hjd}!n5XexBqjhy)+ zG`abfea$VekGKl*BW>?j0|p(ei;=6yTrp)P4(cJ6UD6SxF3`Zs9{Da<720%Gf<9HF z9avRo^4X9K*txr#L=?-#lw~{fHvH{W)CO?>#f-1zEJrDj8*rs{U+gO*mYqqrpZ#zW zvEE~djnwx&)I%OJ{!@f@?N!E^Kh7%keBM@WA^(8Wwsvtv(!+$vg1H{hn+|4~w%kE9 zvCKAO6{>ZE#c7a?-cnTrHb-c!X}t@Q*W%=*-?qD$ip;@6l2S zFjsofp_qgD!HP)^M$oAiM&He-Wm!g^474V7v>|evP_Rq+mRmPI!c3{z&m=wWS}n7t zcnr~il=l*=M~8(AYMxy@|DsC-%YO-IHQIw=1a3aFcQ8}u_4O-D(eon!1*sVhV1L=y z4FEF|0I-y5!+3nZWUJ8aPgUiCg76%fbK`H@SV2GASf3l=;*txA;fC0=^uE^6<-mh{ zVvi}jPp9XTH?#Ic#{1hg(bE>juSGwb909o4n7G(E-&}y5$zMgAPkpFI5#PAM6MfVbcBQ)gtF&`J!qI>G9R2zV3(syc9BJRNY`WViH0w)Ejn)r2)_t}uYlkIUHxH{F*l{{B5DUT^s z7fY*Xx5!l-AmCrbu_?n0{IXH-iR42ZcWigKg{P9MD{o1OVkq%Zl*foMsnlYO-K&fS zG2FmkXUL+YyTg?O-$Z7%*66MAnvT(x>rq!LP{g}ukj%-pXUaVi0Mt;_64tzI+>H5d z!;7ul-p8fPU;$QK3m(2=sJZT(F_0_VQsdSFTBp2ovE7Qo+Rqisc3}GTb%>phc}p>j zP=jb?IJHf0ByVaP92w3=Pz31JV^G76g96uY+t{Pitsj`la(?aKx|Ktll67=?re|9X zrPdY@tp_Wuw{08|$h|lsa{g~A@bbqJ65@3VCjSixE?eJDi)_yyaL7v>u8DJG)-YFh zQmO4Fk~EwO_t8^OY^Z_#mkoLlKw8r76#{|Co1T8M2l-7&N%`seZ9C6*7xr)uV5br4 zX>!mn4a!}3phNPT$DTjWaSY=<5@eJnX4|hhcFCagMew5XMI8URv2Tao zfIMZND1Cp^$G}Q|8G#<9MV_HQ5v*=(l#(m7(uK2?n~yz7WRO4wEaoE6kMoefC}Y1# zbdTMMp*Ohi{2%_JR9fC@5}cu!Q#%imA@|Mfs#rP}*%kR-ko=p(`_(F#|LeF0Z_Qs6 zGhCColeZA^T;y;dXZ)W`Uac;TooePh%Lt?j+1L7u68-;{tdhs|F_U@yYgXGizcSS= zIhCn@1Ihniailus?-fx}Qbx^Gm(xwq@w9*VGd2alN1aL>3}z566xq6R3LYq#cmKb# zcb{73A4Db(l_*Y-AoI~JQQiG6o60I5kG9x7iDpek_YHqTkFJdRxh%mc>E{;qjNJ|S zF1f&?%Sk21@nS}d*!a_54CD*|2;R7d$N2a+5;jH}{a?@`USA&R?rdZ{M9w%Ng&aRL z?k`FRENg;Zg^?W#9g8_*@5d_~eYcVqA-nUqWaeMAI_Mk~Ce@<9bL%Z#l51qf=)L)> z5g#Fz{w`wUp;-T+6p#5>y6b!mPd@c%a#~4y^|gG*+YbZka#pD2W#6OJ^uZCHwDJw9 zB<+;*A8gz67ub0hx@H<`l(^~H2LL{-SL65C+hw!`lMstwOD+-*hn#xbu6_<>aWv+>p*WZ3cm5&+rui7o_rbwoN6YGoN{CgI0I&lwS3(8wW)2)#lEstrK?1AcpdpzSfT5Y2cHWZ3gp_tp~W1ntvD`|HZ?2^?W}h>gqW| zFoQA|mJfKjn_F^$#oAoD?z}rvavhhPEcK--#eVmFoQQ(>?erglFKW4K!qF5|U51~)be$kpKFyeEf=FZ|X)B}bY`54<%7zAywmbS>`S$v0VaJ=@2Kn}{zNJCp> z);%%||A`U(LaAcK;FPhtRxcRLjzF|N8uk)o8-wieeP#Ii^9svk1csOy%mkqOXC$zn z#UE6-C+PxJOq_`9m^9cx+k>+D_O>b8b;!G;6&V`#DVY?TDhiBMU}7{St>a`VRv;s(b3V7QrX0RV3=P=W=m6=%x|muK2DIC)e39YqNBggvjWIT z>`pzWY6~c@ST-AZ%d_5N@UixJa8M~}&(G^OMc=jj*o|X|XyX4{YU8$deNVRzx zH%OSXttqhilf8+)VOm5}qf-2VP;2u%;LLX;99$C`TAcU8AB zz+iRep`mxQCxT;+^e~fDQUnVfGIZMfFV<9cQwAq`_|AV{|L>;S&t#lBTC@7pIin+jdD79cR`IjqPodJ9f;2?I zAKDp2>g&M={NmhwwHMRA7)D_r+)wqAuk)DI)%&`M-Ig&Iqg2t_0xyO*FxIE%S=0V( z`k)|U%LP*qa z8EKJPJ*HleVCua>QT>fHjuv(ay`jV+a4URfzP7Q#^?H3l%E8bty<|15c#h*TLz6oq zW7tR&tQoNAtViP5?>zCmzUXJXehKUu!G1G#61-7wk<3p^7evd6-@8|DfvGa3{cqvYzN$x17K6CjCY#fM|L=uiXs-u;STEPmK>o_Y?_yDuV{JlJ9m zp%Q2%JQDULGbq66ESSeeGW`iFo;P=~fJ^7%JZnjtrT_WtGRYE2U}yGJ=vs*MUF|$Z zmDrc)e$B264umkm)0LWF9*O=iMTjnek7y-QLeer0YugO5nHk5Y1w;{zGnVOfAYn=- z8#;>xGFzYd@f&LRob9$+ZZCtix(DSIU{zr;p32tMQJpm~w0M(pQD*Arr_-`QLvBivpk_-(o$cx3fod}X6k;}y!KTpuy!b=jF7-#C&F)8OwcvRLvsb2F+&l5>Jfltek>RmhrSy(f#y5jM(-;b)sp*sdQ2qgB;Ii8tD{(H zo;G5aVwyqeaBLs0x7jUuIwy!@F%765%_HLX`P+3|0X@;fuVYG{nx~D*dRt}lY z0K7bsX!y*cB-idj{-w;k@|P?F1|xx{AO;G;S}L1(#o$`!!UpNb*13hMY35h+gPG|O zO+@u=Zu77h1H%IZq|vgzzpO%MCqjOkB9WCZtIXW>u^tb9DqJVtTWj0dBrSu`f{kG? z!|9TaLxvE$Op$m_Y--rf$|5W*2IjkGKu%BRN9BS8&ecv+m&%b`s~cveXMq`zgQ#EE zwK`k8Gw>Zfex6l|+u|DmqZ%S2C+n2V?P&(lvoL4t4*0wKWP(d(iPYJv7i&gy_v-Z9 zAlUUgA;u(-7S2u#EryTAL4(I&*L+mKO7=32hQth6Ef7eYyI_+5Wr#0e<+2_SHE;t7 zZ6o(ocpzHomyp89{6AAfBgq+$NTHUL4sLQ_EIJ6r-KzH(jBw9v+cWJDv~2DNz>EPW z9cHPR*lIg${bhniyCTAz7M(Z`o-Cknbm436;VgXsL4uyn6j-~{NsKe7V+RvA;y16x z)1OC#@4@i z45DR(p+>Kf@6&A;ujmg*jp+0=472%-4$+!O>P%2bZlQ_FWXz2PQ<+(^>%{Zin7UW;UPi$<VQL%WD?>S8G?9U`M|ZOcF8Q+3q=f6pcY1X zJN68#Cb|@|)J1e05v_%xn_L_!f;;Ig%!>DW!MVtMEHWZfI%2{p{*ICut?}Xfd2~v< zSdv(Vi628tHb~xFD=TX;!chGoZ31U0)DGp%L_LpS7!o1GtBVIs>eg5{Ysh;qsh{(c z*F==$LM3%~^*IErMr;d+KC@CN%S5MuvN}K`q->Fz(Uj zHy}J^b=6w&mSB+v!uzXWc5`2ZrnZ$0afu-WsVNkgCcPI-s^N_%N{i8sx5k#&foi_v z?nk>=jj(v?lL_cD^OHU$N>i4v@YZFm^;2r7qki2(ffsoF ziL~Ym^CALi(u){TInD%*3Z6yCY4a7W2oiNW+ArKf-A=q%h}Y}AZ&=nT**t9Bo5rst zpk>K;|7AWa^=@#P5~O9=d%;i?5z;hO{h5awW!!(RSJJ>yBSPecYyx4DEeVqCR3l$hoMmFf-qze$KZWOA(GdI;%*0N_2oEqX ztL0f{c*~W=9piGx1+C2|=D~k1!)q)j{{;@rXZ!l(g(vdz$Fq4!o^k>yG>OYwn>-6EHdq=@kxm1M13A$XMT2Z3$ z1qY*$JtJ$$KSMwGmjv9j7pHA5`0qT~-el=eS0fr)`HRki3@ws5<)~iI)}!+f4tV_9 zcAC!Ary2%DnGM?K9n|wraAdiBOlkJLwauESD|*gct{O`AwQ4wsszEYYMy>`NtmeFX z!En8L<~JNA_etqedh1=e6%`T00*(CZl)2R@5t0(BMc8xOB1;bPL=nM^9QuTsNw0$& z1t44q-3;61K4Ht^Ta{4RV(boP?)q7QP|0?5i}sTZ2Jh{9b0o62dCnT!x2>o{r?t(B z=iEiQcS#qXzo1(=m|P}`vECyQjP8avg`Vab>0!Zf&IznL$9%UN%AQh}rIwT21c}0h z^I;M4g0GEKImWPlD6EQr)7=09(_xzmZu7)VXl%bGzA|r~pqM=n$Fi@~CeLfS+LhHbZ zQhCOMYAcL!E?in_QJ0>yS*%MjY^5V^BB!OZDiIO%rSmNyX@ex>pM%Y)pvvUZbM@Ji zyR${ri@7UsF?wbL_RB44f^-oF9b1cLm@m&oBC(r!MDuX-IZq3*o0Lv{HC^JLc9aH- zsR{(Y1w@rTTNpZkwq!v^lyS5Ty>=a~*Jm1d>MqG^-&$w+#-d`!1G+$nCm{&)sv|*E z1=PwY%WL~tr~qP#9+39NN`o7^v}zMl;J${LRU{&VLtKT5+mlPFb!D)s_=xN3 zj1BXq3Tw6D3Y4@p%%R1#MJ?{+>Fm0#JXGx&ks1Q#20~*G3TI8s!Qiw-+6l!4J`+Rp zhz|X1&l-yvBD2bT%fDE_5j|llAZrFTE-oGpE*5YBA8aftN*oPp4j_z_xj2nF9<7T7 zr=%-ae8PVR^Kh}T)v*6!Ee&NX9eU0E#VWa@W|my{Pzii<9Cr1lo;wb-e!#@}i)@hE zdv8qdjDl!NhkS&T2g!sz&1O%oi>`sqY1%cb8}W*TQ{Z;XW|)lPPpflpRX=s#`81)c&-13@nE4%x_$tjYmeU?x`K5Fsm;ed5A}) zMivRNlD@buR&ANS)5+6LH#Jpw#oe1!x>JFV%lG*&Rz3H77q3-PFoSDqYHErW*Ntb9 z5zj%gn{y6(g}y5T7uMHDooURqEYF4|6!CU?xTbSI7iYgQqcEOc_`r6xfV?7N#LD+8 z(Qe=>kEa18#7Nm^N{vhdp;%l~SQ$Me89fgeKE?3bHtSNKkWctMt7|I0%#8H#e)6Ot zMIC#hfzztTMgGhvTk0>CnWe-o3$xjEep04Uk>$)8^8nuLUEb2V+H{e9U$}aXd$4Db zormNb56M%YkD|Mip2ZJk-ezhW2#if&&ZY=sEc zcg;cTt)#GYFqk3TPAEp7Ah`&3AFDV6$K}}7*u)cZex8nKK9LTZ4mwMuS$xePFzS0k zK6<(Fj6=ggi~VlJ>UW%%+)v3kFY%n|8Lo?NX0Gp26yNUO z{R})k5IZTDiepcF(#QWN3BwW;cQ6&lZ9GzZ;NiR~@F(B7^4huZ8u#Vax9~cNwpaAg zFSq_=|BCKaO6uDp1+he`xT9vLGqVF*F^Iv#}xq4L|^bh+sriFjA zcdsdPHZ~NQJ8npxQs-0UhiG_cHQ@cG92=o0jb;%O+xL1QW>OT}srz~3==$OJEnLic zc?!cx&P&yq-vp~e(*qm=<>H45Rnvyw=91d)X~aQY`%~z9LeqOo>hjrLEl27ALIr zG#ivtR&H4tvPy9P%u4(ZW^q6`_&}1Pe=v)MO@%`#0l=&VJ06X?xVZ(8MU__4g$|Tw z861E1AJ*dhAJ)Dddw^=XEV6BNR5kp;qhA&kr?XYI-T1kA_8eWCXlLKIpSLD3>BM@@>BRJUBe6$lA)k?cSjLess+=9R>srx0 z$d*_}+5r@Wcw)vEAzfZVmQRzSN<`9h+JL|~-QjbE8$&oN_ww)8uCSF;(2s>OI!KUK zPm|ECfCXwWU~PH9GKSN9As=U*tzWuIyRP|q^02+{uDh!p;NE8`=ALlGmkipmu||=s z=A0V9{a+>M>i!W|g-8%);?oGDGUG`VVwCl0Z_y*zO6^z^g&%w(=uq16g9CMZOHJSJ)W~qK)e_1eVL6-H9Sfy$ z>yKWZZvLu~EtX-Lc@0GGmCePDhmwK|+&J?`oCu zIk_yn?I|3>hgX2+gFjNxTXq_Y1P22w>U5O&DCA(TSu1R_`5?sCyi+{ADBu+0Hrw-m zefURrW~r7CyhD^D!$Gp&6^Mb%2s|*jyHVSg@CA|8#}3PE9;@;HU*-R3lSqF(HYSP%zp%2yL%ufVr*fqeknE@ zq1UX|y`58(QyHtzanR*)v^kpf7xqZ*a7N2jSZ0gAN-)4iVSo^}l4D~;bfxZ6gectU zK;k3m=Vw(oD-@c&JyaXd##*jZAkK;dMft=zmNS+jMuevlU=a^Qk)S<=#X8=#h=JP* z35kP!f^p^d#o6Ph!_YG;5(gFi?JlLgPK#rEI;Elo63fVYI<}=RcJo(K?2)4MJVy6= zg}6f`K4cSv!KnZ-{=SAqRQ@VPah!?ZUR&5+A9IeSwPMxivD&cpA7q4fj@3n057S27q&x))W!rx!z97eZdw#}3;_Umeg}dm< zgmdE!g1j(Rv9%y0quC2`Tx>fN>1wF>Qp(^VK3;mth`rWJBzgRrTW+BJ= z^0~fAPu{Iadi+DalZP9Lcq+P!M75FkXejsh4YFjlWOFqLcgU#*+f5|(T-WjqL=F{r zgZ(;A96fz1i3jBs&h+~f5z^bo>qqbH)1nZlI9?&^cX&nec|#VeQvKTNJfHI=FvfZB zcnbG=tC0`nWJ5GaQ?%IERobjIy$Bc{>2JIp-wV1a{9S~GqP0$=UM9Z@=-P4&^c0 z9e@995qTJZE6ZtzM>6+6$bJYs zw(KcddtNXfUBI45p#6j16cduB&B1>|GdvL&$(?WT?<%;YPubRsJN!nG#{%p}4%EH6 z48N6Oxyu1=s@~p`8}LyB<;-5C{|Yhn_}?S{{>EOg0bW}Ft3&Qy*7#m9@V^@Le?Juj z_ij9pD}Vdxdt|Q-UGgN~Cf`?-_x_I@`%9I$+2b%rSmLcoThcG^Z;~0h??k;SuGjUC z4P>C`361Gvv;3ynb3CZ?iQtpVV^7XOPqo=53wwn4Ia^=f!nwMRjVPfOXwG@YLAJDd__3e&Hy$`aJ*WInS$= z(9%1(TD2d*F~{^Hk|}7An2_zhlVza;_#%DFD3a$oLT$q4bn03Wa*Hyp%4=Y*xsF@B zG}E{poAv8EdQ$q#QF2pm)lrGHTt#Yu&Fm!V{)oHmJ(I>2-i3hd`cYP=9$2n=agT3= z3fDZn*$D4Nj3mdgh{Q(J@A2Yby+lGTN@;)Da((t3c&&1X9DF+%kwxU*yVjehMp}^0 zC;5-p2m{5ZNe#Bnq`&b)LPCUJ!DC21a=d_urgSe{qk8mP97bDF#n8=Eyx}wj@JA0q_nD26I zFVg8fFtHi~Ax3a;afxy9@QH!f7+etW{(?h=%OOEcsS%9FDQOYH?wXKCQ}fWxU6V_y z(=yR~XGlDM&LxQUOug1ht7{mz2LkNyf z+3<&D%cUsS-bENT?&7wwNlUeLsYTFKTH=e(Gh~}g{DyhF4^hjMI;W+Q4j%|b)2wyu z;GdoisRIY?#5QUmV9!L-0ao?oIKzz0V1)&)KEH_S{e5}jM`5kI;Aei_a67-Z7s`DP z;nOFvF~p+o1Is^r+k$EbVR2QXIKUotkFV8dk?yY0^7ZPgi;6E@{Wln=``r&$o6Co- z(x_UKX}naDWq!JHt%QDO7mh|Mm$2Fdm9;d&53T ziQcIKM-a= zc4S6undlvWh#H8HS*Tf1d*B0;fms}$UIXR$Feo6O^p=+Fbv8FUC1$pC!<>OG{E$iy+t-ecNoh{Lp8#X!%E`14NQ{bnxD~` z;Cw3|EAdlFt5rN%BND^zk^D@-SJTBmVUkUgMc5?{?yuUkc>iKhHz&^HB4%qb~1;3NlxwEhS;9VVutWG** zxJS3#qXzK1{V&!Py!|0aZB;4Xt1Coa_hHRx1|)ZR(;u=WmA0QE`LJcft3ZE`Il-It zeXf@!#V~U}l570Sz7Rh~iRGH~VC_omi|@smMyav_K!COl5CcsBXEfLaDV^ zNgg&|_Rs>F&DzrV^|Xkq&H>19Q8rhH)cW?>cg;GEpAaPW7t24h^U?DRe_cqJu10p= z3CpC^nu0^#9B9_Ipoyb6qVf&LI|D)$RqaTRNXj~v7q zlvr3yJ7$Iy<8N9`l!iO3V~iY2swZzI*6N+sNP|qtm zubc}9mv)<`@}x(W9aHr8j#EhnM^CPfREOT|+`Bi>Qj?;leOyPA`Cseqbm5OB<&k$% zlZ}sn(NUogO@2%-8Xvpm$W=EXT=7MJPu^n8PbT}Mjh7MeM3Mkym&G5M1SPtAXgqQO zHd9Db_E78QqY@&$|FOHI$5Aw!o??fGiAEeOoXtHP5_)WS( z{K~A;+(F=?7ya(isVRhuO_CY|bH&%Z2@w9(%>!S*7m=O%q7o~_abUHC$+fd2ZNvFp zzx~2TKbxPq%dIIedw5y(zCR%_bmhtv_!CnWN4QU-v#Z>X{VOt5YVqP|q)9kiyz%G3 zg8k`t{lgRfJvY>f7m1Trv`BHze)Mu6~kQtfy2p-6Xeh zfQ;R)DhWA4U3u?(pqjgSbf>o_NJa(w^nLKqK4lxtaTc66-gI8!995-lbucXl3;2>< zd_pY$?x(08BeP)~<0ouA!WLy$v)%oLiIII`8}z|!_Y=EV-jJiLEBy;8MS^Go(frMe zJPp#-xA!Vjb1uZ=5s&jXSu*`|akt|v`(^I+y4U0+O5}|;8j#~$j`)T?H}=2hW1hbn zJ-#Gfb)%!iBK03B4%z>S$BYD)fU>LU`^lyp_>`i_!CXX+z>!q3!GmiZR^88*+1FY{ z-XDeQ;wOym)%-y?EmMP9PlWp7E?7cuhkRd%4BE$9msW&$501LCuZjsAB*Cs*Gctq9 z0Zu7;b@J~it@bf{1OrM?;u2IiPJ3#iw0cqHH_cUdegCp_S~{@EVxL6!(X~8nP|8o* z^Oa(gz!#};`)Vg29v-z2*wVgb=?EZm00qxqrEqxfZ6~koMtpa?ICa()V;g|v@6pgH zWdcnggBN1c^%%Ft-t2Luo9(G}6^_zwnMkcZu^U_r>8^k0$d`f432O2>nB6? z$noN!7NJ%}pjt`$me=QDqs*BrpC>bSFkCHzad3{P;9v_W(oR#w3FT@f*CxrdA{b$q zvg&=OXa;bv@i+JXgzldrzb|Ku(NGiLpC=gRzwmL;$`z;tq++D{;J#Z^@DoSUi^+EY ziP7PmN{ak<;ZncTpA*vR9WgOU=9Fq0iT)m6qOST1h(*&m@;-if?R;I==7A#J4Yb`o z>0!-liNZ~jJ^3T2(Yox)lf3cpF8=>msr*MjlW~-$o7`aQP2GP;qQ85vklHsbV78~+ zFR^iI$G1fsD7GW$@+)s9=QCA5M6&s9*t=3lU%-Qgla>`2eseeT_uzs`GikrTGUP4T zD&4)Ome#@T)$N9TMYR@zg~&5P#5IvjY;K~g=E zx3vl4mX)H>ED_q5mLr@|g-V&bAN?{O)(uPCcdHr$6wgrpXls($>o+H$42ySCGbcj1 zLwa2gLgfiKetIgY@s_D_As$J?`fa2_WJE-5jVr)O_N>ush=g? zis6J~13YRO|Ec4`%Y6CwKY0MDY5`PbdndOUTYKYZ0Jq?b_T*2Q_b&yUh1C8W?(;#I za(+Z&`G=E(%DF#vipcQkgMv}o4T4Y7#3*xjBgAsh!{;(;MiHuES5Dc+p-nL~nc3n^ z*uM(AjQh0iHFoOy*)G#RfQeC!l_E+<0VoPC9;NwC8b<_Rdg)2tgy~QHz4~ZsfE&YA z&F{pK5ynsb$B*Pg+6e@4DbD1Om@ za8}+hGY6l(Nsk-yR@Io#r`=@G4^R_-ALS5y!RNa$i8Jb;4=O#Bw9MWAVy=H7WOd5*t2Cr z4+~9o>Ms6A65s}**Ek?R-MioJTfEOMKbZpt$NT>?ICV=5KP_I1u&t3B7=|Yp95Uw$&BT%b3?zf0Sol-8zVmAR_zVS3?8 zdT8O@1aJ&@=i>G3zU9O~-S6h{#o#%XQ`enqy%J9x!uV{+Hzi%4$Tpo@>t==ZSE1$t z0Q^-m7y*bI*iDpEs=8O+CD%wWI&52QUWC`VK!z7xa>U}Cvmmz^{W z2s!PY@O?nV6$5ePk2VMa9@0$yGZ!Ml^{8vRSL^V==_h@Fa{uwl`va0^uX#c6Z?f1g ztTf$)oSWmXup6|JXsdn}9y;Yp_3WyKeR=6`T%>J}_ZLf~K0BEYTa=Q@)Q66Mg?8sE z&UwFco+mPMil$;m67t+n+@TG4O<^_n8VIW>b27>d;Lizl70MJ?JM$|i(c;GZIk+W)qae?X3fghKI6aF=~db+sFwKiJSgbkPyl~a>zSFNSL%xTMt zXR28?zOxC0IK$OcISha~W2ARNOZA%o>+$0llG_!LDdd;J2XHD?4&^}Pw@%|PxJQ`< zy|u4!2S2_k;~hWo1I6h@+p{B7zCaYCpH~9@>a_QCpP!=D1;{pedJT7pA5d3w5$Krp z&I4{`Ch5Q1?*xrJ(c(_0P3Nv+0BkTEChd?ud^_&530M^{`713z?L)k^cJ}yye#+aT zt2`_wOth*&g435m~ZOzr&SI*4`Xet5MFGf5Mn zS-bH50~k;He|VIQ<2{d?2ps#utfr3`o{A3>-WT+u8Wm91F!`GFf+Hm@n1FoB`&TxB z^bC8|s5lGkTc?-%;~D$8HysNEYj;-zKqSgQ$=@4-^DZ{?@5$93L1Uq7k{^Tv(I`AG zdAxglNkq!7CN(VG zZB4)JgozqOffgGHPW@-A-YM)wjd^F(Pn6IaZSX>`CNP{F|E8b}9X%u1z=zy>j&ute zxt@Y19PLeBLu=(fLwv;lV)55n-S(& z{>r(E>%V?l{wU}f6AtE6mwgC{!yv+r@0;3{h@kuW{O-A}3pnv3Jc(f7$?wJkJm{fg z;8qw;KJK^jH4yEgBGase<>JCOW*&j2EqQ}~pnxqNBaxjo8YH#;)*SsVA0aQNh`Yf6 zYOH!liMBW^d=%b1{LFGaM4)epmI!78*cpwV{z-FkaMcNdhK+i)nSeM8`B~y!!X`+t zbJ@zwf&Bh$Qi1;0pRNjhA`UXf0nrnk0- zUy;MweOSGxuy%zD0rnYkxiO|jqnE7{zLypcGjgCpie<687?>eh7z(7ON;YG4l}#N1 zm~lxDo1^PUzi6L3TI8xvkp9S>;HK+w{W(ZZl|bt8(4<-qlg4KFk7}aGESe7y zgmkrM7v4=6x;5yVO;C-u9!9{l96)oRHpMcs@0E2 zzDH6$WF=To{TE67=$twZYfV~h{X<(J+U6BQizW)qyZdq(OXeodho!fPoCDvcK!xb&icQU`n`|SHs+EmjsvNzhiJeh z;&l@5mSu=fn^V5KJvkhwMm(aa?6=dy2QU##9Wh**T8?CjYSjCTG_WsNE{P)b%!v?4 zV@J2|5mq;x;VH$|Fpr#=91a2T#;l}jMsCVeieI-YSS$&-poHX2o1b5aR5!b?`6Si0 zKZ2%?`A4-3x{_wFXI1UfkYuRaYe>8=RoAd$5%tiJ*0vdP>h~wghLcN8@8x5K+m!kB zN?kTd8V@heFwNS;^_F8VX}>QsV>Cg2rXF7<+DEtQJTAm1M8Y&)&)LC!Qp?u=V*P>* z$se(=Rvs+6LR;Goo0lvz4`oIbDZ?@>vI2FOo{G(gcRJbgXtv6;*xt8;nsCNIjiFq0 zz?mbuh?_#Dh}!1;n3tV?rjR2i=Xu%X0=$@vOupUcS9{cI?vzjW!F6?*EdNhEJXE6g z6(RFuj0I)npGvB<-^%)U4BbPymvy8%JorS?YDQfVyO-QK=$1*F@Gbf03{xJ)xfq;~ z=ktyX&Yo!??=Ad`E4k(yNDe;~r2NBLpeYpXkzj%RY{l*(#yoATM#L=rJcc@(>X+f; z80p92jh|lwd8Z@w*|4#fl+?~(7H<<~8YaFqZBl7LcGS`#$uh5|c)xgVl(O~LIiEH8 zqSmW!AR|WR^riRzO#8G(v)&A-3yqBvi@YSA{$wQ`U1b}Pl@>o6-JZc2Q{=DYE%ZDS z4D~tJ4zZuq-PYu$R3mD`_I!(4i(zcVMkkN5dJb6k zZO7=gGA_S?*fhdw4HH6XzU0M)H7a*mYakml8p$)|F!IJ>U$QxLN(kgVHM$tXM#2=5 zc+kbn2pR@^vMVF%{l0Gdo(H25)e~y(vSeL4q^a)PTOEkcT)@1C+&aBSzR6cGh2sF} zHHYXGrYA$6(`UwuP-Q>#TsJwmeOknuHvUI6zX5;R{T_3I!ud7gqES)gg~ghW?wek& zl$4Lj;{O?gL>t4|G13KR3*C3XJ;oCxgx>qRV7qYxhTvj>sV#h z$CYBYn$TrqD9lGxO)B2z)o{b4=+Z#rIa4w-%yr3IvTQ|uGn0w4QOV>L&+KA0RA$)E z$zHh9d@x)86-yICv#Q^A6pHN0F5#^jV9@7O}~vy!Ixlfjf*X zNV%BUkbgz=z!F_|noRV2L@-_C9A?BK6W>pHj0#M=CH8OY;PvF{(-k{uSD6Ygnk5QY zG{$*T#&oFp2G3!S8YM_yNqXGGAY&WUT2F^STMu<6Jkq)MI-7^>R^Kr!eGO*T_irVV|a2#JVt2Mtjoy;XYr%`i#1ZI`Fp@(kcH(J(!sTtuHrGK zFpvepf^F|g#~A27UH+()R{8lO;;s+L2g3pjCA(Od@>iO%%i^}eGUoZe`;9v-e&tb6KJ>w<0aFLa6*wHtC-)u*LBDb; z(ELEXmb6XZvbs>tp_@|}mhEU2JsEgWOO@5ehoFMvWH>@2-=#wYe?kH*5-d3UMc{5P z;LS@gy2eFg10liAX%AGvZVqv~vNdY#LZtCZB+^45pr;TX(wJg8@Fg#yM^MU3rV{H; zVBQn02&oL&b%~|AxU|mh5VUt%E*hUko5jP;E!Te8;#4DcOK);rvP`OVNdh|jV17}9 zL}TAS&2XPgd8j~ILN0m1-Ci^FX)r0zzPFj3_t2)IKZJ~eQaWA}nqdfpYhW`o$l;JRBxI%+%B7-)CNN@U#Cev=*)->+Q|$Va2T5*Og&& z0&8kbj~;+Fje*f6_n@gmwn3S6nu%!l_TQ}qp!EFpo%TZ=$MP6?*n?4iiLs@PtU1p{ z;wyifrF{#}p{s(%KX0R5VyvGPolfCzBpzq~d4_TFuwLU*e0J56naO#s8AS(HU*XWT zhY_W2I_uTVJTyt!QGc1u;;KW4+vh0#uH+ucQo;!;ngUjBC-GEt%^Ra}b89#nWzovppi6EqYR_Z$1)k+Bh#4f7D{Pgk?s<<`|6zTzL+4eyHasKg z3M6WsTG;No64bE>phH7ma_JZHx|YnUW6PjiyyK;ba{4Zx26sSYBt?YFO=jyN+h)-C zWkWPESNwek7)tVn{qIuxg~D=uHYK0m2D3@I&^8+Qx19#dnWCw_^QmGDlU19Q2frJb zr+jqRBlH}k*mxc9nd5N zaMpV)(H;V7Qz%xi*1XGIUhtacadiK|GEb>98RS7eyiE8KLrN;RojP^R>s*b*v6zU!&hyJxkq0FOy(4eUnk8GB{UQa#*QiWBoj7+ zR0B#af^UY7X(uBc>~H40r%-RYIOUpkOSCw2OWFI|z&Zs#BgN4XA`Lw#>$ady+je^) zPuunpUfDM5IXi1;F$rA;A5R?#v~{WYr)3%6yhGl7t3Fv=9$&N%cm6##aZodsLKmNY zJ@X-QcNRt#e~+qYrTm#}BZctsnojino~BLiOpHxdbX&s^Qb~X8YvaDQ8$LwrUBXn0 ztV-T@&w(;cY$wT2jx)`afj_^>dHW8c63Z8PO5H?!M8ZPYztx~X%TMd^&s)T8lviZ> zXk+H18;z1Gw)aey#Y&hD%jdr>p9Lf8bi*z)C8n{;gFk(sbz>%W- zxkq3*q({0}A)A*UMuLzQRzf})->)WY4dIe@TavHGVXIhfY- zg_7xRa_L80gCfEX@j3PA2jm`&hyNOh!#z+ba-maZ_LU%;dN@FA4c2TR?#{Nj}jB! zwl3~P<<{cMOw~4Y;it5Y;(ET84#b$29-w;y+IiFGKk6#JgR9i}oLUfW#v^2EoE(2S zZo-2@CHEf7B&)glVqhpcYX^U8A&JSX)-B`SeLHcC{cm6R1MDOLo%%%v!G!BTh;^w?^IA(znKY4>VZpl)Znu3pE}V{Rx$@SDGilu_~$H3f5iNK@Fpizkzt7P zSN@!zTbYy2H*xGz)gGi{91QwWSEo)cwB)Lh#=~v@i!T+Wgy0bso(2t}m?#;Jgz1A{ zpwH4hmmFQ?oagajx|hG!B3gTq+mdauR_nqMt=?+<@t@xSgBv{6KK%ZJ$Ddx>?<*Hy zy)CF02-hoc-;f6^ltRaV1j?JowM15f0-#lGD_mUf1gJa=^{LZtI`BqTNC9{}PCOf% z?fr>0om)3EV(A|b4glq~ejE#0pt_DN@vufU_@V&`f+NOUtmA}}<7)M=Z>bS4Ro zP@G^bis~qnF7-Adj!W-5zxI&gcP5vEAO2_Rj)94t83#6~;rDAgGkhKNH^XDkQT@j# zasM}S%OWMCBHj_rY3$1%2HN1S17%THvpOCubVo1CU+NKdAl|P+HW|%MLhh{aAZn5o zA1_9Ez>1gdFAZZ?jxlu}>rWQ=m)}A~R!s27Xlhsdv^drDY`=2wzj?Z4Oy>UdwpsCh zRuuWNglWv0XiIhcvgYZ@DWd575+va`Ki%l@D{v4RTw|5Cs zE5h`eiGhp2*zZCI=ND#%e7V1j`|Zn@z&sqN7CzM>ohrN9Ht3#S>;Yot%sdLL_g;aO zhxlovU=H^CAKK0a@Hi9&N^xvuG@803x%HH+uF>;4L_1yh+jw2Wmi=rig{HoU#a(MMH@r#KJ9O*Qr!dAD=I)DnT+IxSd$ILVO~Q%D}2C`B>W>T;oJ$pW9X^k6kN zKEu8subn+j#$FyevF*NGhc>TsjGU9dm0rcBtA4LQez~l5gL~8x?!#)o+M2K};*sn4 z8vYNE^Of%cBa&Ia$mum0W=i51A!4@|sBi?RBOE~%Izh=_PV~ObMR&Vwbr^>h8j6Gc z-3jY7bzVLULpYP_!4fsiKlp5MDN-|00$r{3Td-dO!&dJLKU1u`Osol*J~cL{=aGFS zLfPhH6ri&LH`TKp>`|=#+8mj<+N#R3bYGYCBJ8cXe;0zgcZ+tJ=y}qBW)=X}DU)Q4 zzo8RS`^oIW{6-f)G&1Su#WxQz9&h>DsobR_;cG$=$9)EH(KxZy(|4{f+D(6i(vYU? zoIKUJBbi!OnjwScoY`nO&N(gDlE=7>r#$a^A!`t-(Q)!7rI?Ckif3}HOoQ8&#Fg$x zJU>Bd7|C0jdx2XdU*IbY;`7Z$j|zBtmK9rpCjWMKF}luGrn?t0N(M@ZGfSG1?>iO4Ael~DHK#_T}5XZ*>U|X zCCt)#4p7R-cdcYuIFsAeCUmQFK-o6zckSLY;ut5t5o0%Y4p8uWQGa*bmm0Gs3v6;n z_DnjmHcu|j|6*CZwAz}}?AV7MJZmEd3z36Is(57|exO*_rQL7lQj=)_j4$Y`qik0I z0=2F;xZVPgP{-5;ZI!S${09$p%!Hgv?PO{FU_n^~J;bHmT8Iw8t4fThdX9mPeja8; zq<|n3Wyw3f7^VvteyG3`K?)CMvgflGwgxFSnnX44unwB}mkOuD82VNEa0EZ<*cCrS zqwI>i#an@8;j5`O$DQA}@!ew{_B{SoAdBmkicMCZ;$IzeI`dEPJ-u$iv|ZWWv~~$e ztUK3O$kS^I9{heL(|Tk6IS@{(PX5udc`b-hc^KSgzgrV3q!0j{7H|z>i{jUfexahi zl?_8xLA%=Rz&Z|O`1DC(7C9AiuPt=8yuTc@N_zFXq*JfahH(@^w0=q?b`e)!31P7k zpG&^pwIiPVeUGkWoi1*EY|0KcJs7!9(fPk|0Pa?GCD%p5&?hvAz^t0%shU_J&nlR!0_R#77!s6sU53p|bxK~;Cx(aeCu z>ccCMzgTcsPb~Ba$kuvf05H7Am%Q!I)WSv7DE~qQI@EINMALvu4e{}1#VEq3>*L#OG z6?|QzX+Q`8LMVowK&S#zq_@yQ6$nk5)KEpFt29Fhy>}3hCQ4Ph0v4)N5mXQm6cnV0 zhzLmc%kTZ}``-KA``mx>JUN*;XYZNG>^XbZtX0pRJe}w9n?N2}s#|5WZdeP$TsOJ( zt4@VKiLkf*v`*sw#aodV*;`cOTA4-%4K7XAcpgf2CUng(Cnw#kY_>>_owB|`^XITG zDdoG(lY*`)u2(;4o=VQ;2^a1N(ltH}v%0g*9@w-yt7ctN#6}aR_XgronNh$F6ureD znJ->Qqv!SW2A}x&#W=yGj_2A*j62j6@cb!x&`YS^yCLOJqHZutf4|1+Qjy1R2aI1s zVIR=Kc8AcVX3a&-HraIE$ZKD3r23XYZXWcj>!9(|^&&`WFjznN?I3wGR9nsF>1^G* za&_wlJ6Mvp9h*G)_-~wDTz?&@fM-;Qa?8GA%)`2>*LK<>LX^DkusBNcwsP5VfbZDI z_?B6}(BH3%rD;*Oep&D2v*T?0d#P5zb?fPY6^*U-XoO^5Gfk&8!EU`i^oGPs7`iRJ zBFa48K%ToLQJszFr04^9Zs~F1YZ#5i#Y^giD}9q4_gXHpYgehpp(X(S)Vr1Pdas!W z5+ek|e|d!e-s(uM;6qu_;1vh`j_ZPE@A&mq6KyWNt6)Y{THoi%|8}e_X_LXOr_J&9 zF}rmFdCuzQV^!nug$_dpn#gKAA1rR!e)Xz&p1X~n&QeE0DD-KsIk3pXzE?ZNr{YOKN> z{BL@+J+0}Vw(WpR&yznlZm~HOR08KzE;cn=7@qBiV?&Y=LN_38_OFbZuNI3WxBFXb zq==U%pTVZ-} z@3wf(QXNl&_+VS31G+IjPK(CHA+P$n@}z??ksU`D3YGrre6AKwt_b{zXH%Z8UfLGb ztEzCg^>m<~v#Z{&fp@yuO6F9YjW>^H`YJQcF~|Ez8=b`{hg@UW{B36$JA5cBP5ruc z%BAhbnlN1_=)HaXx8Vtb(DBe`XR)^y^%DGjH2_$;_2W<%hE-S z{bF)a&$rp%oW_R61C|bJdi_Q31-|e)yIxa`;GA`xK#Hc7$zwFE- zhx)yXTvk$z)yD)G-B^g;aSbfJ8Nb)vUpXr-{hI+}XKhdD*o*5 zeo!-nw=ZR@!zz5XI~q4@k>&67ZAIzps;R-92Nn~xT;yOfnu-?6;1>+LSZ#+uU6 zhcL=T=H$_q`QOM=i4 zEII>IEa_NiXL%vJ?N;?&MZ9{nVl-yV?t!?xZwp^Ch!pB9K-~+;o_VF$w!L$e0=2P5 zHRCty@w88@3nOi6GeWz&j(Zn%#&<-2FtfK2W_YLTJ|38VvVle=Pka+^q_c>1S8fi?2>k~LQ_a)baqwU7xL=No_h{p)s+|?5VYySSy>aWS zmHWL)AcGA=)N#+DmBvDAlf7QV!>f`j&SqC8X~dT$e-bFtM(gLVe?Nf8rrefT@-J(sCyv! zL~pka=P6!?1}|FIdsqm0{I-#_)^mA#=ei0k#cSFxTry9~dfzuoecFfJx1uh|TRhdF zU{tm7*o@m29A;x)-X19WYqu-?lDqaa$CC3zp?9`7Jp|GC-vUlEuX|?AQw5N=xVHLHrcu4dKs1uCA$RdPZVkFCIq`kK`G92 zHjtY=y2`%lpX+22@vQeIHZK(OJ-(__ea|6U{8#SK`&Id?{sb6mJ2`++uuHNNwRhG3 zW8DnKA+9hw&c%x1`ktuXVy<=rp^*?EZZvOqSg)ml^79$x84TL-Y{qh5 zpy~@g7}Q~JssKF|e?b&aWf`xu9c=o-PULWg`@(9-(|m=*?FQb0-xs5U8uqQ3eiQs` zMjO`~UOlhrNUqQC&z#?8F~1l-PfiRdOcH1WYG^_w=$BCe|0NqX-SDy0p&~ zMP0q!SSWfSYYrSK5Ly0W25w5;MlVM(>8K{P~^;)L_LYH8PEBnoKO`DgSM>g-8*j(o8&Js!AZ;LX_M%G7r z_%_vbTtqtKQmm~t~c0fNDl=_EWEOhsjIM4 zqpm64M%f?7d$j9a&FcWAcq#rRJpXlAr*`{dB>T-v#l0T91?x7?#Jj}PZzfl-hln>6 zHdt%a*mTZJSv?OHRXLth_pPAu3ucGvQ@NUcz~a@X4bE0~*YNCk$ur|Mx=3r`*piF% zTa}y9?9d^VIbQTC;1|0?XWsQW+_4~S9IIz2uLm)>u1EYL$54y|Rr$tTJineMyvEUr zX~!zi{JHC71^1MFIp3Hgvt9_xT;0rj><#CH9l#-CpN_>-57H{`%vvUo0Xo4c}upB+T60n2v$T$hf9H#^&Z^peq4 z-m+=?YjYtQ%g|rm?#Vg@!T?LH?DCHY`QLBXcqkj!B$yWM7E zWg-k=p2{Ald%1q@w)S)#3FtSlO~d6dO?dMIQFmjM&G1F3(xQ-GICW<*?kl@HyL*?G z{_DEt-}ZGfMAb$^6Lmjr*-y9k&7VzcxsgN$8G6>PJ|E&8C976i(RaU_Hqfsz4^b6> zZRFWkKr1*pULdB~*K6R2RM%2u)YX|hhI5RrNg^RwYrDqf3;W(pzwq|!93KPhxLWw&X3p@ab6Q;1{!=jnJE+*ImtbcWJwHosd~7@YpQF!`i2r7o08z;! z{!0WJz=Dy{BDe-3VzMflcY3Fm&C$PIq;UcNqy4Ojp=v+VQLtt*HvRnPSb(CAifV%v zv>_G0?jiUOFaV06d*B<4{6<{Wm7cI?b2zOAp=t2iWm;Z;P`J`y;sXrlc<% zlL<+EohT#3*a!Y7Wh!F-qdts}@uBxdFFo&a#y`M+EMm(piI){#1Y5;`Lsr$u7#$Kz z4cLBto<+b#Wbs|R;pN)O4`I(=&^I53a1T?Sm2$jz$UGV{%(SF!LX4H-glZG%A8a9p zyj?R+CNq}=;IY8V>ULQBHkBw!X-l_E^Yr9Z8@sV(RH&)5>ml*Cu5NkuUI6HMbL?8a z0Me4>=FZDD)8jh7lbWX;O}~ zNA<3Sbn~MXyP8!pZAI;ure`XT)H7q!gtH#pGesaY4o8TY*sa&h&!r8`cpc#M{(SgQ zcQ>WGMPkCYXc1D$09?#UuH1cd(6mPCIV~;s3n(AVn5TySBPH@KRA>A7_c3+FjoA>&*I1eIm5){zoDLf;S~}Z;qBahr`=fWt zuUO9&*KeU?2Uu4>cyrhp)?VtY$YCn{ zAE8Z;O-~f%@v~5$gM=)_Xcic|$uyZqEnX>!l^)urhZGmkYQcq-w4uE%=1@w!st)|_ zq&A36cDe~k-H&DJmM#_=nlT89nmg3rygmotD=`;Pqw|ceZR-d%_9)l=+GG6wQ1iws z@8G9Pr#y=-E^>@1DEdEU*v(j?8u5ZaKrxVW_(3FL zb#_eWFlMa80Da+0!8a^Kf5iQwXOQmWM2oEm3{=|whu+~A{~usRVaKy~vvjH+f@Ubt zG5q_1X3?8t$+Ca~kp=#Qt$WoYDEJ3Z;FaW3ka4H!V^gP^ewX)^a1g@c?@Ruc#W%d& zfpq;vQc=C|(o|84YG`_Ri#DUJ?%bt{18)-!l@eiq_O=oTM86QNYZdm5Q)^UzG@6#% z_^5c_V_xH4mw6ca94R`^f{l}lEfI{LW!50_Tg~h48@Xocz#GPpftBVUq-uYy<}MCV zK_}NG$0+_ZGnTGQ%oA&--Wr|a^;typlc(ZcdNdi6^uhr_#||j?9OhvdleN2Q8obpLnUCwH$TnyeA?ol z+oKDj`%czGB34BrhEWlM{NX2bZr^4nto~}XK3X36B;w8aS;7BYq*iZ&td*7D z?|t-SK(+-s`H&^QpNRm@ZMSE91dZ%iu`x530Tb_O#jvq!{B8RglzuD#H$9e9hxJS-S60tb>*#DRN{(p=Xajn77n!}=!FI7jycJQ)j@YHUN zz<-M<-r~0s2`lO*?tYCDJ75awQd62=%;?XeaUqY71m>1?XzLbnex#5AZ9ETWa|>kw z*@?_X@2Y#q^|c&iy$XaJAYs-dl7(WK89>9I;)=O#;+%MQfP!)vHb5JNO{1{0ZwEev z9z3od^*COsSRm=O9dh{6-(z{UK+~!Ga&_k{ey!={#>ntADz!;tD-MI$J-L3tF9B@X zZ1*mG@UTBm{Z-LZy2Bu$iLa3}>als3)|Y$~`Jr}VhY5<0%Lga}G4VR0M&hBl!6IQX z^Z>6u3qEnzbds@9rc#m?g#?TzXE-v)ZxRd3$;_vhrTd`-N!qs92V3it#cidUJ{*Cv zhEEkQ%#?+jxg*VS0BMDY*Aui2H z-&xbM_-(%@u==wo{{!?$bf8zwu7B6>KZ`Ar(r5VEvu3>W?0%t0e@^S~)6Q15eTTX6 z&ePK{*YHyVEs;rzn*}cmQgFUe;PW*J;T{$tiEs?x)1)?jc+Vc=I>!n%ISB-g(x|$C zt#}}uHIQ67_AUCR3oAFLE4=3~s778ZPw|M2i3NND>n@Bjk%EJwKY4voR@U@N*mx>s z7|^MV$WDLPJR;PJHM_d4s6VY-LB-_dG$~!=zd62Q<^^71*;zHtdc8-|aIF2g9BJiP zTE@IOBC7b#sk5Lpr|ikMR)%jnTjs4h1z(x`Y1d*`-(hv#$MVCJZ%spw|7z}-_d}r~ z@?VAb_%}E`yaE(uc6bHhG<3MQ78;3oQ&Z#Kw$vw6GG=~H-kGO8&Dym8>-o0h#hRzt z+|sKnok2$OK|vV2muqmkEqK59<=U5+XNTvGi!pEqdOmM*l~e^={g5S69XBecczy^f7}|pJJO`_Cq88e!aT{a0M#&AV)_HMt0_p zkt(@FR6!Ez8$rwatx4`P=xBU)e_cqed|8ki(y{w}0oMF4Z;_^D!aB9NyroR>UWAOI zNw;DlrIUi$R-L4$+(*G-bnHQXcMD`4)w|PTcUO(;pRQ$$l}g=}qH9d~^I7gF$ds|^ zg+Z2mSAxY?;~d1zfVEi{m_Z|FGUjtf}b8bz9@3!Z2-?~jeaj-C9@uo9n-yr#tQ1Q zeROzl>0jMgYh{=bIMg~_`*!jjg~NGB)^3r?KUtQux{rw*?~?nL#%i~`?KlIJJ}KEN zq~4x1tz9yCTu>!IGgc57IpOc@Qy?|)m&w#@X8ELXEIZISDT`F&WcK6Llafbp7FhS#Oqa&AXuQY%~PzZ z^E&R&?9_(?*QkI4?O&fhb~A5d;t?zhrqbA_S1_zXrfYKr0-`}1O-mx!k_!BTFTXpp za@C0{LK2wr!bJaf;i^j_bDisF9cM+i7$&ID@y;->XIpQdp3SU|s5fNg{OR0SE)U&0 zk(@e+=IM6xQ+PA0N+q?6nr%$M`)NPs@;jMe|3S25B!kQ)fsC~C(?m=9)`~i&a zRZoxRgU-%e34?S~_1LUK%wcS*=QxiZ;$ zH)*Wn0&B?MsF*=c=8Kx1Or!O#nox&R?ys*#Ikv_-kDG!F4&0m#gM!pq%dUcWdNjv*ODBL4Y3~zKR*!i9@Nayh5+GAqv`C|*t%_M0^V`y-{*`D{mzIt2 z83NI4fKPPxXX|{BuUqi2CwweeF@~;^$TxyRcivSk)R|P)pJ63`r*`&@52pvj_su-% zQLisZz5RQQ+S0|hurH^S@eD^uFFmmyE_x!f>>0S_uG*@ntOnVLStufVOIV+QAoNu2z2zYa_pviXDcKfjES3qCOuEQ^R6YdQOqQ~cBHlmAM= zCEZm{%_6vTD_TY_6!nRGjbka`N}@CWuT5%gwBBu1@Oj54!>>kn502gHQ0dm>cKW>`e7zL`1{2 zhUZn>@}0_fTmE-;D&-N<9Y#TrTRJrKsp(JNAfh;(9itxgiaGlPso&e>52xqnFZdZ| zYHDz{$bb82>utA&zQzedf#(Tzhq zvZU9qzCGpe3ew4b#d7qsCGg-^Cn3GN%*#^t#2Cc+_tfC^tR|D`8+9|+Uh|2CU`_g; zIm*YzbQ#ON(}%A_&rA82;b=5EKdEFLZ6^>rWe#DB`vd&&ZIv1XM%~?|!S~dV9@=wp z2L(#32JmZrOiuL1uZS{Jg@q&7qhKY%akvQf*tou(@awa5)|rsc(_nv*7wyok-0x>q$f1UR3&lO!2jHtqTI^(63^;? z)9brFUo_#&0);cMi+ zZ4P{M(J=AIP*@*(Q0gHy!zS_RThyo34@qzD=TZ7x1$C}81Rg9yHaK| zKjtrJ%>6AbI#~Ni9Y`X&tHYFc%8a>$hhz0cV+CEF5iZ6X-l@zR>hr~an4Bh;x&~e{ zNA_(Pon&KzkEpa0Jk;l3$~2i^dd!r5p>L4kAo8s6OV>K0`%g4tWguEp@>=igY2tY@ z-65$Tj#d;Q$O+KRdd%V`cLF!0$~Rud9{_rK`1ozlRe?4+p}TAPI2Bru6YH+-SEsTS z3pF4`_`cuRhI-GpEM=Btb=HOFlcVy!e?_7>`RAR3G+1-W!e{)kE?ZYc4(S-md{2f% zsHAak7P42?OFDV3iT1_ochjc#EdS-rD)6J%pMBu|(>Yr2$qa$!vxH+o9i|()eH+!*k8*I`!EBM7buOjJYZPDeAjA zFrL$bNUsFIzEW=8IenN3z}{NU$b~iA;*&VyP(Nd5CB)JXS*VHX(}x+u7$#6)8n`N} zhqC{+U-raE`sedp$HX3Og}1-bg?iqnEykdajhsD4-i(ZVdO8;dbv^FTd0cYvvdn3+ z^We#IYLo?UUY~bNdE&CfoA?@W-w@qVaHDQc)wASIG>m2M<}2Cb75*K`sNIN1iWjUZ zhNKTUKraVNzMAV|Z}z#tnJtEc<1lI$i~6U>l~`9YmPcke_xxRQ6#6| zXc9Kri%#!C z_!1SK6aAYmW&FC8K2q@uNkr%BDouj%?krDy~!wZSi46R<`JTNui zzqjSchLI%saB{`(EQyg3xnn^|KW+ULFZr4RqFHmIs+nPgkB-AV`juwkUmdSRRDiVh zavyaB;m-;d1Y&5xKc>kocjL@E5AHtS8hPZ29)ASYJ?N=CHb_>`=WVi`Qze!}%~$Y; zo$N|6ZS%FT4M2R4u*}X+!P%mJBH7A^PO@qr$>X#VxpAAbvsN45pj=wsLG^+a?^g$M zx8E>yDn7z}GvPoxG)@)TCUh%v-eY{d9Wk|oVw zlAQucqAq)8y9IV{5d6VgJf3hSejw%Mu1sk{OHfaAcFAuD5LjNG>D5+kah1Odpwax+ zGXLA9hHDeQh>^-CsPr$aatr4XsCxlSdUFrwUi`@#_z?A=9QUYkYjyVJpJpx5Wwsm< z@XxdFQQc*o`^8m1axS8#$1(cq(nv)-v#;0`$^guD{7m-*nCtGfI=-~>Gn;VWONr_Z z)>d#8nP>Qnbgujzrh6b+X`-$oAjC;pkvOWh+Y%ZxBNOmcR zy!C%F{ZtXlQs@KTK5NaFqSxil-uWTNm$sc`=*I3kqEa;o)gEtMiUCknX+BcpYwm*t z7l}mkniE>V!_7!+;_-%X0x3%zKO(kAIxw|merMNZMzL3vDJm7ZoJ;fEoAW$rGS`Px zeu?SD8YJl^|eVaU}F7$VDh*}_43lJij8 z@PT^gSgX_`H;xQ;ZuZ}m7))Nx43AHUv1%-pk`{g?uvQe{*)V|jwP**JFDfNE(E~S- zWd%%fqq0s?{>OWw`ZG@z5Y#l$7pshnZG0hE+|zy6Ez^yT^2|j*ZcI7tO>8Z7f`SCT zJFVgs=?iduF|^_V6xZmAJps2!x#DFFQLirS+tLiR+9S{__+*G!^UiN9wd$@#f= zMF6A1&QczYlbPkCoK|egJzA&XKNF>{MrpOj;TVu*ayM|ALz#rfE$rnc?Ty$)$O&%X zU)paa5OwYrZLIT6)TB-_lomMIO*Y3mIN*9`wq7+*a$0!FSk;+c`L?pw$yvm)wXokL_~*-eaPR5=rPQStq4CApcD^c8L|2sWu5^G z)ZEzKdNhIb&|5dX#3I}GrX1%-JO{1e99qC}p65z9#`Ku~-5vSELcOQMX$Pc*Chni2 z`f~{)@5{0gD#_O2+Ky*gf9U#i5W9Do=_7~j(R|DLmz+g9>G`8lGBkdA8pz?)+kl;1Nt|fa!)ONZiILcJ zMoEuZTXS%vsZY4xHieVd;l-<40?u~oCHI@Mwg%3#zZc+hani8^kiSN>gQ*TfvoCd) zC>#PoQ-6ehR2dS9uz2XzIvg~cOKI1f)|kD-A(y~?Ka9;Cu9uNhRwR}CPK?!e`= z$^9~`ZpR7mV!jqGjuptb$|hEL%s-NQf|Ud3!&5|T%#;uA{P~WzQ!8T@i5wo|-(sv+ zw0k@ytG|kJ$XRHcIx>WkPDD^-D0A0O+%x%4w|Y$z*Ko(XNjPMOdgz9MG@Rd%Mkr`0 z2-l`d*?R!2=8q>D%v)yX{uScuLDy(h2A{J5;4LjlNH%(#uXL!&&?%6(xA}&GOeg{- z>t!0@Q{P(k(2)V4s3{PXS7kZ*wtt#Lx2g%aG`s#?-(zTATW0Uuvk;h)Mp<(3d3G*c zi1WgPOtaJ9omB9Kluz_xxr+$I<)p*g41U(XSBk9n*?+3&bH~@_$0tP0oAD!b_&u0+ zCc;j}`=Y>$#80sXNB9U}2>&Hi<9Obiu54b zLWernl{_?moO`+HlFld#*=2(lty>0MFFJHQm1*qvHXwMbPSE7RDzy(^8>*3Ht@y5; z$>VRiA7z1~nH6x=Iq6OlB{!*H=N^^~q*Idd_qbMmw+LC@b6pS4$gvHn2K~Szy-B3u zLzh&xJFeW|Z4c7zM=jqV>wlcL6KayP{CHoo9l%&xFeeY6W=PjWx6K|15`2jPx`c;^(4)b$d~>I zMhqP6Kk4*;)YHk?KeE{~lBtVDGjiGrHocxGt1oW8Cz~2d*ME=M+eqelq+X&r|+KHVBZN}RE!gr2lUBF(3Db4n`r1NaVGDfm@;A|6Bj!@t?fTRD4>Ezs!&EF z$skig?7ka5!4HrKu7tp3c?rT`*}`QK>-IeVbw4BRK7S;o)g@yk4l~W#XQT^)?BP;V zLkuTc$fANd{NMPOspGn7`Y?!~jab2`G&0PqLy zTD)Z8j?1F4lO_Puj=cM>{nJQ3z2jvz*`JvozTmDfGk;3K+b$I5X&jZ|)tlds87z?m zR!}!os-|8#X0{22sORK$26-$CZk6Ovy90c;D2JlK+aqI_HIVdIHvmc=Gx>yzHpW`! zmx!z?aB9cAU3g?>gP~Q#@eB{2EM5Y)T^w6^8U@0rgiBL=$_{rFFt6iF6a$RV5 zxA2Peh_t+4QZB7x#;6-)hYPF5jVsfeG^3Ac%14Ne#G|&sP|Or7eY&ok;cM#pJT86q zwynDvd|gOMp?2V{W4Q4A-Su}3C+MLxuV|-rW1>yyBUM&gfxliubfg;rEHrF>{o(t# z;(n1R;IQxH{>x{kmjDqMf0N?J7R&|ZYfW1P@zEp3DyJi`@xhARjt69!r=xdvt-O^u zcL}!4Gp-U5Z{~>;H4+-4{K zAK-=hKY(^gI#9vt9{{^vRtGrOO-e<3jwK;8d({N7JtzqmgZdDT7_a)W0g|*|bTXDK zC|ym;hBHE<5w!qR^Pxv4FRMq3to7kz|2smUlmWNF^GO^41ZWh-V)C=zY#-*GZ?HiKMjl`UH;|b$^g{C(w(+5S)*X8bdn@TGF|@ASl>k8@JxI;P z+ge5eKdw)4J=;gW&cGU8$xW=l^^l8(i(`fiO$J4HD)b?^YnPF%Q?elstOTP_sKjgu zL}3$j3TcR(b3=O(30U-wZjavz`74AggpBb0N-q|i`yG{^#S%vTE=8&~Ec+ajgQkZP zR)va|pqNY&V+~S1E!zz@&{9ra9rl3`SeB*`@7P|lurs^Z3(me%x#|VRfU1u)EK)WZ1-nbb_R39-c%5C?bNa7 z(ya<~1cbpeB7@i|(neFdEY#x~GO0q+U`e$FUdV?k5yM2wg3NV#L|z0%5>D_9WyWG} zr*iFFHGZx0az<)XfpECbTB_=TC(?kLxcu)SkQXCtqRgV}e)47kkLrNK$LMV?@oc4K z7BrXq(wpjs&uN|9Qul<|l-+E<3vfp#syHQu>QJs!5%55hG~so>N}fcZDtm!qk#Y0>$;sejc2x!E-0;g8~x4kZX$ogN*k;S+l_R zl#d{D+S>#`XHUG91wNZ;9g~ZAadDqq5MToAvsA z$Cw6oB3->%hzMyeC*`P0UHFCbnpw@nNa$-_&tNY~vW+53N-(9+x_&YWTG{cXz zdYvy4v2GCx@rDL>%wAdPe|%>S?I}bFGcv)f;8v+dvz|YKYLPk6k5EA~HMDJ2EpptQ zeEh^1j+1j226l5M!+FQ~1Yl0s5B=vO zD1&0*kqNJ0F?+KFvKkihdI^wI@ao8=31MUzo2}LLH8$%Gh{X*rh9W5I10-;nG>OA- z)9)Xi8l;hLnFygxWE!hRA>o6VKClvRvOpZyy}9btLXND3N<-?I+guVH_}!%?B#RCw zr)>UPlom`fr8Rhv_~BF4B&!%n)twfbU7TeS(j8NzpRSHTc|*D&$Ik|uA)h8i4Sp=k zj{8$!!kNi*{uVgSnhSeEEL$vxY)PTElzYKG6YgRSxE(~QfObG`jCg!H3g;us7OG?> zK!+@BvIi{XD8^2+kJaSXAA&{pbB!SYLm@@tcOlKiBY_Va@Jgm0Mqn!-BJ1Ambpsk4t+YAU zYgTNFuePksp=%+O(K|~tq!<)LnHPd<;z%-kS1LSGm%>%0_i#WUOPvKha21*PLj+MY z>+1>f9GZRY^v;HPom7a(4n7vIQ>53UDeIBmzIoQ6nZyf}L}q zCC=n~L_$AU?*l#)`fR1x>nl$hAw}m3C_sTZR}DN{+&(#e1-VAiGl3XyqTI@N5JnyL zmU~#>zTtuHWpB-CnCG&)byH-vL3?kZwM6$ydIM090($*gNz~p=9_`!1m6%^k#tyVU zEtVm!v&cfo%`{vr0MlG3I%{ZE1b{{+Jp?9&4z+ro(J0t9=@nY4-|6jgk$P)-XeMg#* z(TpseqgI|NDil-}j>>kEVR8$v@W%^4Z%o~H`HJ-RC1ldJj)rLMYf=&$cSH+~E06($ zg1wLr$-d0GNG%;)@x3g7=7>>)WRco-JRk>(VXt?4!jzkivfTdi+Wm?=&Vv)9&^(6A)#xC1t zYK-TUXVU`QL|~k$`30^Bw%b96-b9MbckJrp%LWW3EvFoPfszHWz_xM-n$b$Ds zsbs@&7hO#vSYS0k);pHtZAcZf?fZc2f*hXSKV zM?#hm@O&S>pwT2u&h1ysVapYTE`%K~Qc1AFY^ru>0qzeP@D3i7_<>2$qCX@D@~~$! za#2UMr=yhS6}>J+1WCt)(ZszrhX^e`kse-c39cziLYhX(3_vAW40f~wEK#KFrPg2T zS=V||V!d6ox`W{@i7bw-w0*_4zvl$cD;5p~$nvA6T9#L9qh|(B%>|&Zba{r$;)Q55 z|9baw14!xXck~|-u`Pyi*iqrq#kOD~$BO8$@X{uArwdWgn*#E~3G2MWiFjo#^oR2H zo9K?&>HFkiZY|Y#=@~Azz>&CiOcq{~6j44o5UML^C{J4<_ej@Z z7|PWwx0TuUfoKdBcDk#TSFIy0QT+D`7u3iMy9$Vze_4%CmU|n^A1{ni$%SR2=&{Op zN&5r1EW|OXaH;K{q1Z8b@^?xNr?6vjYO78FH2qOUH{(q=Mpco?Y@v@FT=9+izxhXE z5ZHE43b9!@g4vJ_?Ro9VMyF3AX&};T`W5$py-$aS05vSdQ|S`6*>ImFOh2iykDFB%7oT+tj;~BIxuKhqU%?AF8h{z-G?aqCD~0g zkS)b4HyS%oO(d*J=FyS^dYOA^2hllOg^%mD&@%UWIXBAsuPlL>NZ=Dw26m{6D8dJT zd#%+iiVA#yO|Q;}xcEDSxb}f4y8H$GQ#pI+1HJy*nnRN%J210W6jKAY1jpwZ+8~59 z+k5>=99I+GY9s?j_N`Z9YRP>JQBCvq<6`O(9-1> zv#V^fBR{M7r|3kU*Q8BO%yzM5EVGhi+$>$L$PF6E(@9xwgGT}^Pb)}agsD*^z&wQ6 zQsonNyc`;%l`MVjj=}4|ltirY4q=s^g{jga(;XxmD{k%1vA@K}3imS!@eOH0Sa7Cd z8?b!><(P5`Ig175#UO#YOV1^XoAvJh07Zse!DN9v2gmY1=0-Px7liqAl_pIp-T4+X ztJa^ljG2dL0=!-BR)Nn{)v8ywasGNuTqO+Z33beTc%(h7RGLE&aE7m@81a% z0&Zq~o^4Sw)V|-)I(Hv&TcZ4!r8jmA#v&YXFcx-v&>PDEmqtnV1IZB>K2xSgh^*Ky z1(>G`fc&ViK-BBY@v<8$wESZ&DYf}KK;8zz0hbezd_jV`xWQwkdiLUm>am_MWf?fz zx_baq6Sr7MYb#wqVwW_#{K1Xx6|xLXX=3~Zt#!tEIj>i)!gnNg$1t%H=@XAfJTpxP z*ClR9geh`ySMFs!i#KZmcQcNS-|dkSiCKa(7INR!T1xHK5`EBif-JinHM6pIyumHU zMOUs`bKBz?asNVXeuPLod~XY8`j+qyVDM+^)$Oy-$(jqd4d!1N=&R1$VfYyS@(G3=8yihrsxOPn^PX0Lc%h3G7h7q9)C}71aVqQy;0X( zs(K!lap>GTAb1tI{S`&g@mXf_I$$|xnMYBz>QO)cTQiQj?HsT9>>?n|k%6zZdU?Ky zmhunayc|z0aVvdl{=Wt3(kH1a+g1INDg|0?`nU)|fNmw)n4yDX(y3{I-3 z{FNhXKGxwsq&~SH9`^sK;r?9av#S1np&)cxlD@~)q*o$Bq>cS7Ivz#+<$nzSKdPzr z^Up7BEF!M4VkgY*tp@UcO#I*G|NniSe_~VVzOEzr2@hlWkP~y>_21t8AGiI#FS;Mk z>K|S|=QXLOn^IlZU7!1X7WdyR{az`zMR?x{om@f(Y z_o!uL=KdeGNd5dL01y?nM)j=it{z~lY z#q$H>5xiOFXjSk(fH_^h-4-HOTZh_!nsuC_+!rEbH<6}-!8I4~rZRG36sY&|$B)Mr z2uLZ}h*saw&DJ6U=WFMFpnf>s)l)F1a{{Wo*H4czsp%j;2RkSR|3Yq9nLI%Rz4?TUdbQutZ0nNc@ zqQU|aBnv|nF&(jciEUD5fszZ}AuF5)tQFL7sDrR;hG{}FJuWg(3>r=^Ce~CyM~n}Y zb}g7i3jUr~%z78zr7b-$n+n`99JPYPbIsUsFY~wcVy0?`h0$x-IH=rPkF6EJdmEY9 zIq4)_JS!N&>K>FBoTj#`3Sl6=QJvcKn?i2q!Rc{84Fm<%7ZFjS$pRiNQhu05ugA52P@i{UnIP=JnMbh9ic&I8;i(zB={?e|xJhINR}j>KOSc~= zCox{>O2eD`bG8DVu=hFXNXE(Caw+aQ7nf5*E5pcxLQbEg$z3t(Nr&?DPNK}9Gj zR16Fm7ph45UtGNhR1@9yK0GOq1OlWWhMquxps1k-q$D9gLNC4wh)OR43J5CJ5F$17 zNRti%7L;ahH0jb+#6}mfAw`OSUwrSqzi+MotgM+~&N+LZ#>_Kk_I`FAG0g7TNTyiW z%R*eKo^VHY%l)P_Mb>OD@dMqspC>#@R7X1@;Vv@=3qAQZVpTC3VI7iJ)xT(#k|FK{ z2Q-SFS!pW>bFEo?*gQVXiye&4pr%x`YT<}un;Kcc<~9tvL5Epn4X2l_Rlm<)hLUDk**eSu$l69Bc-m>cOOkB=HlUS+?OmLv5{D?mLMK@+9o2m z3yBJh1~+!elqQ=K(=uo(;EPZ{i%dLLOA8SFCU`yCy#jR+9Y~4if_-(F!;=9*x4vtR zT|R6oJR*YV&vlAGb$bT*94W+34r@l7My+Ywh#vIKv|{>jZ4SNSjn6YI&`AY{8gtUH z<2xAX7N_ocd$BWoM83nNFG)jo(tQGDSS;YpAW-5!-n~ zAwF_BrllEuE5+S2$sGFQZ1FuW--@btE1te{1(S0e;+fPv;R{a=Ti)o2O6plSX^>mT zDp~|GiFv%by_%4PAgej26A}Ee2)QvDjhBN$*HkdfUlQ(&!tMAmlj_DOY^V zipJ;yahg2tzPtJ!L|htn);iLjP&rUT1G~ zbK|l~YhL8X10ol$z3f@&71~2GkY2Ii;B)}V7jmJNtOn0_q+wzMY406kpx>4I;lUo> z)iZ^$tx8uHA`yC?S+0UWxxReH5D6R>d$8`kY;SRF!-wUDyZlVBrEX^$D>}Sq;0}4W zJ`;OGtXU4VgzDt<=vWmJ+s>;_(Gn@Uy z-L#tn5ME{H2Y@xY3kO578Mx4~fl_d>ChNa39ozmUXs?i5CP#%y_jTWWUoA_v9jqMF=$>PD3^TD_Uq1GUL9 z<#(|L{_fh3x;ygKg{1`;&{ODHv_l?&!?MLEdFSav)%eY`;bII%wIt&wN$*j!FYuN+;ec^ict3C{@lQ97@FYqH2VHerocb&!{ziqXrifi;pX^kgUuEj$M zh@0e=y8i+ykD7yj01!gb=2NK^p~J;9wnxviUM$A~7u@1w&eic%O802PX)?b@r(Ldj zPcYEHX?i`6QC#HM{C5xpr6nX{5zZBeZ)yd5drIO;(I(1L&#%T)Q>inG{JWBYA~qlh z!~MzrVMJnDvRttswj{4y63s@QCU*Ma57^xYlVv8MRZ&1Zg~iCorRBjRi;ElOTx0O6 zkvEX?F&vt%7Fwa`uy>2c3lF+DYd8aBC5uM(pb<_8123eEH<(SyM3=5wdH8%m<$}Gy zmj~Okh;D)Jj+4;pb8&Ry59?G7@{F7XE6C%tqkx7vIN%TQ= zbW1CR2nT@D!tHOIP6`uQrgb52(dq;XLXCU)&sbYH3#KuI$j7s8M4Y-1?~9omG}Pk3 z_%;)ufdn~Oa?1P(2QFe217GE!C|A~AJ^z0qelkg+`K5yL`Dxe$1~rG z@h_qkxZ!Tzk_-D71C>b-@VxV59z@hwI*hZMt}U03*)~(8BZQ1Me6_A^qk+$hj73#A z>|{h{7#WA0___oco+0@~1$vA`|18FJ%R;;uM#ORLheYNf74ev!k~)*TQ4on(Sz<5%qr>5_R?n z?wA6ZyHj28iN?z&Yh2^k&$r$ETG+z3!C z(OdmsOw{;}ay#)e2uA2`U45ec{;Tkw2{|ila%iH%@3dUGWmP|)hgCvK;R?4w)3O|C zpyp({eN`sUNWV>DSHGmo#=fd&4!}>q0=0^9W}`|w$Vgm z`7}wfGdmO<9}+=1x?nqkOqe6HZ&vfm%+F ztbu|##wr^Pr!_C`d77S8|659#6QxyU#?C2e1>=c^IYHN~c%~82Dq}L8UF9)l0f6L8 zfPS+`tjRrd4^Otc=g3wEtN^2pqLu)j2KjbA&X(npKJjt;YSQdg3XO`Td3~!Do_H18hpYFJJt(~_jRFesRb`XjO&0>2uL{)cCZ2=S?*s$$`WxcUiA1U)G zQn zC^>zuM(|~O!6)w@Dqe=Kr_DS zIdb4M+~Er-6uhxZ9VG3(b#q=epQrT6Z!^QFDZf1b7FSC|*_>3Aab8&XeKc;*HZ8Q& zN%8w}|45etohx-z9uQ1pZ{~%*GZ5S75xQL(dbd>l`Y@1_ zorB()A>Pg+fXfS6o+#`{9(B>4`xx|qTdBNYMU5YNQcT&-A4*P5Kjl037zcLo4I$YN-J} zN}bNbp%8t}4odHoJCws1dD7}?N-u=!nH%rb+0naJfBE=1);*>{o@&J_?^d(3tzdbu z&0mnY-lq3>yZ3hr>#L&14!#Od$GW|l<<5$dTqIQUrj8Yx1*tnX0x@g?@$ewQXcFrz zRWlPSM208Z&n7TIYPujjcH30@wDfEa7js?2Vo{-|(aP&)PD0MPx{KWQ@w+qy@kx1B z*4S7jnnW%)u&I>e>=4qyD-SC<=~Ie3`q+iSU(9~{$V8HhcI~<%Zh`?c(p|1Rjq!nt z-`B>EJB}dRxjh=WEmY(Vq8pgX?5`_I*H_fvDPH4|9+>#*ee@NvL9h}H>|k=}AMsMx zr*4=9GybxPPCWm#))vL@QV2djI_I4#gm@aUpW+q9Fi{s|5Utv>#*&_rlL9)fAr-(vI4G? z9gHlau8(I`H4+e`g#=1^7LocF=Jd`;ZYF-}0;#Gck;6ViieX$uQ6XH9+ zpoZ|t>iIb>`SCTwS6poec(QLXi3Z&Xt9ITOZr+{gCedwD4g#};fvGIKJUWhw7?xDI zdX#CY1dNs#rP7>)Z_zQ`O+qs1X0d8u6O3rhSp8+}lV;6GkexXsw&$YoXCZdt8(y*D|NzYkX!F&Lf&h( zCgN?9W80num%Ch(j!E@_eiMzo%^2-R9#hbW(8y`26$_6W>y#G~Sb#_-JZ-Nw|4 z3+K`y7W!$Uh)>SJ&w*Zf58>%D1Dig06Vi6}{`MCa-fX5_fonGjksu;?@KEWC1amxm zN%NWv3u(5{yXlTXc3R%No9c22RA0RRY9S`Z;S{N(b4h-Ga0`L>T~zNdwqBMF-cfWT z;K1}_=z@Uf<+$p54;chqN$C{tt{iYuu{)HQf_`#ZWw)eVd?~BQ%9=(%Snc3qvu5Ba z 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

LVtT*|o<8=!2(lCzh3NF~ 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%0Dhp5K3s&&xW;oZGh(Fmk(M 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⁡FM#KWcCM3GO7R)4v5+l*n&&>S9d<54U` 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 BBwPRh literal 0 HcmV?d00001 diff --git a/impeller/fixtures/image.png b/impeller/fixtures/image.png deleted file mode 100644 index 6937b1541f1eac929ae61fabdd6df0533904f7e0..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 514968 zcmZVl1yo!?vn~u{0fJj_CjMg5kRfm012H|2+Vj&?R;mXN=`ig{v5{ra{T#bqTGNLAY zfq3bBvXYkmA}1|P|HTn(Ze?SJgv1(WVq`=l$H6*iY;0sSILgk3<>>Y`G&J(7kuS7! ztaGrlkG`)nJylO{ot$7DRlOT2U$v{jTCz^kUWd$7^B~T4&F7MZ0xe8{Ze_jwf}_ZT zq%$f0TP9AyGm^s(M=90UsHi4bv1NJ&Uy;7MAuTd~mINU$I!PWoV|t>7W}z9=A(y;< z?TB6tMp{G}q6m}ZBR51^1j@;79?07KZlmvnlT%Zc60Dn=+DUcRnfk@ggcA|KGY!A! zB&KDn{#wVxDO7(o-fAjF^NI534msA?jc@{mjPXu; zgvbO{CMM5p4-QUd1_e3Z3cDdXj_)QzYFa7Ij4t$eBlEH`IEu( z^Yiu9%Zcm7P3?W1_UlwgFi~R$r;hdA7q@DfY0H@_DIvXop)rw=L#&X{UMS?3M)}f^ zkWk};kuYAaL@(`AF3SJqqQvH+{$Coo`agnFYSMCYFIP1aM>8`!Crhw%6{R7|i>f&* zb!}&DB}JeK*p|!K6#U(c%iY%gKM*8Qci;YWBe}(;0yhqVE`lj ze~CC-i!o{|eW8~IJDSl8aB*{SGm2x;)6VGWRIsNyvUM2|m4+p@*#SQp>x?iB8{}}~-v2r)F(fMR$ z`{JG#8{+(Y{G$IQ|NqDNAB+DBsr^4lfe!-z8~MLD|G!8LCo@NBu}+@5E@Kw0*Mq@aq;ZUXU)Z)uA)xDTL9#haGpiMp8p1`xle0QEMi zhhltuEWvj|z&>j4Cp(ODUNrdq8cG)`K-`yslkJk2R-_|LI03O~nD_SiR>#`OiEW?g zsg*$cojqGm^;lsOA8xi2Vsz{l$Kdq|BB|+&Fl|7tT|V;xZy9Ppa>!84l5z3&;wW%l zN*bVc69;zuJkK}5x)0gc)I`jlYjOo1pS7~OvEsdSLo~qOp9$X(nJ+{* zJbf;I$7-^T`DTbcngL@=U@m2;E<@Kg&BM{&e&K4h%^LroO`S-?+)|c^7Hnu$)$`#7 za(}ZW0`Is1Jge%qoYh@nblk@*ojfm{)X{le6~IM(rw!WOm&)~4e$<>h!M7^B7n0}Z zzh1k#;V*3!SHOD4bLs-x9$lTie(LQq{~d0%k)&i2-;W-FBa_tLGRAb2j8^e+;VYp)&}j|3WSuBk?q3nU|(At%?iB{>)-A@J05fD!4D5> zHs=Z@MopF&Urp~^%RM$@lXeBQRc3k@0H|5+1UFAI7t%3QlOX40+Ck^iNPo8q%*S^ob z;GE|RRnIGrVw$Y%hOOxuS4Xeq=$LJ}wxM|)ks!mK#YG(vwU{TlAJ9x|?1*!C|7Zyk zbwf{nLVdN+bo=QjhOMzxyO?j&yq$O>Yz+SjHn;ix{xIZvX>Z)H?r|mJDu7Pn2YUt; ze6eOeQE;i=%`Hsg$3Vz{ZP#5D_fqo2B6Lb`38oF!s<5lSoPFc0(luvc+8$0b#*oke zu3zdu{Ca;i?pyP?yLQt8U-ewWdkp`#Qgc%GeChTKxu1-os}C7Bs9g!*ds1Fn-H;Lq z+EcT+N|m@J-b?dNd~iHD{~Z&yZLt6z%5x6ZXmGJq)x?So#1l&^4&y`Elav3PZVTa(MLcygYLek@6WHR>*U5^OX>)U0wj( z;y5AR;z7usD;>6M#%IcG7pdWq5O~7zQL^T?Z#@kS!%|Y8EBr3W^&I-`J_y`C?{hTo zlka^g@=S4)c<|ZtVN1g1d0OKBWNG6`?tZ$%eTH^YJS&??3x+Z?6FuR5NqFVIASz%l zvyu?pRWm;kB3^Ffxpd+>jc!w3#@IAhZ*>&7rY*hK`S18dm2QURlC&HCx0|?i@Ok}> zU*J@$W2MjhbnNG2s^#4pW+lnbxm-q5T&R+X&bJR*%Zs`{rG0RO;sShDa8>g z4@y0A=k-;I;q^!S(98Zz@HR-gh(Gy#Ef0pNpN(!3tA5*>E})-1K2^4lV;bRQ?k-;3<(DC9eX&7Ey{A6Mt9>w| zTId+g)L6nXsUtPBXD^PBqqc}D5fJvAG_xV}i|8Nf7_gY%evtk!|BXV*DGXcg#)FilG&n4A2H?N9A zZvNVy*fcby-Cp58pRd_iFVbRH$J5{nvv(6*+J(}P!Uk8{p0 z!Rxd@3dE3Udst>1YL-;nmwIPA(tJ3_WRqP!{$)Q zh&-I&QSDWC)k?TfzSDJ~Bi`N*sg^7{_#8*pHSfa~<}d4JXnm{D(%i;9`q;<_&fS5# z%UflKeOI?@*n2D2W|jevI$4WM*&l-+jdCel{E5KViALo|GwvMM!*D>r7t+kewe|8-80|2+B%a)f{-uD`0i#lMG zRGZ~C44=&36ME|k6}pRXT7Z4m?GJ18vCX&DU+fgt3wXk&ZZlygbK`>}5WgQU&Wqvu z2$DV9x|Qd?3X_c1UIIe~YNnIh`r6u=xw-iz>gX|r{UwkxFx#@hhA4|;vokaEP%rb> zxB``;7^?9>`+8r+llc&F#%I7J1+V=2ulC&;iswVKHs?}t6YNB*>{jP^t8Dk2&ph~} z*h0&NAC4)70bIGD<6U@01N;jpiYyT0WX$(uV_(+SjGsvtr`1r&Li+OsgLPjA`C|B)rVUndH=)3X|vJ5BKgrd)F4292PHhxSz#JeycpMS zXCL!vTMaT|(i0-I;g39av`+e@RFjc1f2a=#qaTRT@7ux%X~(CH zQ3M%1AzKcPSx^=@?^j%jZktQ<1YQqxs|m{wMC?<6WK%3Mb5=xi1m7IBL|GB*3}UsY zzFeiDY#I)4wL{0z%yNuu=~yDAi;26Nz}!vx7bf4VUBD5uBW3?$Xdu z&8R>Dw;IBIS{spLp62KBYBB@BlQSUAHcPa64BhQ$26`UqyzKiOD>dfYg2)GPoRAr! zL2=a%t22Vddr8Xp?O=%~ka((8SM1<3Lj%S8Dyd~=!);dFV7rg2ctLzcO4ymLR6hI*Gmy>S_8J<~V zd-as21E%lAbO`+Mx}v`0yrE;-XKUVv288(axb=KT*C279a8k#jVpw}5SYD66+JT#$ zbQMqdzGRFM58C}TAJu1Z^*FP3ucAJ$3E0M9XeN4S!b6P=D(JrXsZFFo{>3JXuKD1h zPoRk`r4`RTr+7zrlI3?V!|~?#D0MxAmxo=59HrM02hA~@_r1e%``x7e1!PWuX7po4 zxR$4@>A(GBCEcGoBK=u3G8+4XrMR0yeW&3*RtkkHs`1z|PF`|U%{&GkQG8zh9leR= z`O-!VK*%VI&BqS6Luj-M;#gyG!6Azm#Y4aeM!GLalN5+0lY$S^kTI{XFE_2F*o29X z&G3$CxE!8O$_ckICFXg9UN^=Y?e2Jfz5iwCOEY1O3u4bjo@`N3iUa|B;wajc!% ze&`g|Rj-Kb{qgibqN*CWCK>;3%@p zBSEb^U6w1``^B8cP{L~$@~Cjev49mCnOIlY7Rxx_H_zv%wyS8>Zd;iNXs zvkbxaES?Z!j*#?}t}W>&`(GEhxuCmuwp8Q!26(g~Vv;|m81c`)l%i7l2Ou4Nz2T7( zeM)7!`O|~2AXux_i&W#E%UuvdC$+$_hbsbOs+P=-zoUy}4oLa**+9i>-j77ZQvFdK z*}aqTyj$FhM5<`eTEbZ98+Mgkak=`ml0SF9NEhlIfPk&gyn+9hb8)Jgv6#d ztslIubiHUDxJan()s;$ahQ=%mi4Mp5J=L4f-D@^_Qt1N6C9^46<~Kzq36eZU{~LspRbMG6}ziX=i8!e|GO1 z-cHW7&0(r~9ZoQUgVkgI4BvN1+kP{Q!9|ZPgf9JZXerNuDW@N-LkdQ$3dt$!br6al zbW-fBrAU7e7clRG@=vU*!OBPy2>FesZ(LCNBR+mWNoL~IsE7Qu*~#N1CEPIkCVnQF z9;M)q^Bn+5Qh9dstNgya&51+dKF)04gWi`xIIvyD&o^LPD{A|DniK{}78kLkijYfB z!wd*f#^GTs=RVAVyfw9-{Hy`#aVu0z5wj1pQ)DS+?dT$sO?0rw7ygEY)HBE7@U=sh z9r#fm8ePVU|HL?MupfHufY7}|BONer1qjX7?S~TK?wlUTE%oHthlRLqFLBi<*}A;8 zasJeCYHbMiy*_!my!tola1VMOm9Wlv*yby0a7~+QU$t9qQ@WQ5kz{)8(kib%(sCe3 zeG(N1g2xSQwzx~{vjSH5&!TUbC7l*EW>cg}Tv%#J)ra(+NSd85?>BF&qtsEWgv)#Q zfBCNC?vlB|F#qu}#?<&`{xQ4co7PP0*Kplz?`ECDm7$xb2wFA|{P*aY0beJ-T__@v z0q)JZnz`0J`VoHE0H=@}dHJjmt#KIarYEBw`k_m6?7(L0npr-so`#M1nd;%#|7 z#Z!=Y)HR5E@)o{F z7;Vlvdyq$6T&$JKJGWa47TLmYzoTW!#)e}Fk=c(ukFDKiz%vXVX9ZdwE>-!(;l?dZ zE_IJ1AUBu=TzLBDJ)E6xd1&TOzV&c~0)>lXlfT*@3?V7yrtF;R2b6(R_!Vuw+`T?d z$?)q&wb8jTVdfCkc`4yx)VuYvw;sz zmNyAs4qNIs91UL;98d1$CA?PqhAc|O$YiUwwD0g&QG|6=e>YtyX64;GHk6-(&Mtp7 ztoXPzgu3t=8r&*}0|XRcaL)!<#Fm>-1Q^yoTFrcVOdmjFPF21F`w?RxYC?g|WUp8Z zSkKJaNIDGE4bD$5j#MU21M(jxT@HClACk}WXfv}|`N~}zWL&4$D;1#mmbpFovpU?K zrg0xuH>%ND4xpW@Ea62qR&g%>X>Bbl|i>qN0L(nU#BAta{(!C&?`-w3KG9 z9p5n4&D9%rj5^mSAS+J>sU0)P);y*)*GBQ<=&P7Ye%pU55lN5aw@pF&Fg>KOqHL$6 ztdpo1c_!{{pmNQ7m$-n+3Q8*qV;?4MoQp>}bYWo2du{qfRzLbpS4HSsQ7fVd?p8s} z4A04n#S>DMVS|BaQ_}Aa-0&xY(KR0w3l5=iC*6U9db{;=&z4PwN zPsN!^eI8tB4P!ShJ|=VvYjDTmF;!|_3@;dzp-%APHQ%rNoz5NGY8YSMfm2Sl^}lNtQA?Pr_R)+r;1fMr<3zMqOd!mP-qOJ?FvMPOpyL2|qK z>A%`<#=Q<>zf)(ZijGEeMZB(CQH$8^;w||1yMa+@E)I$LeCxG8^1*|(+Bc>J0jj~5 zvq4vN_$ReFk8>e#EHFZu>?z^-pJ6>+4c7?SSXtR+=?1WJ70sZqoc{k4%}^1V>~fPmLfQrRF1&& zpZ)GtY{%%$chI>}r#tfUy zO;wvbC0fZ5+U>Dadyy@4j|WBq!DJDAwl(a=Zk(y9l;%3r^{g{^50%aY7Spq*&cSBc zb#0wYC^AxG@?H8dFFYG0e&81W=KR4l@zeA=`r2U(FL&$Lim!djWk}?D)!32IHT@Nr zH;Z?;LALYP9kqdJ)hZ`Mn@2ltTlDeBx*~6SM339!Q<#JCHO(p$e<^ZpZ%5(Jf8kL({%vj^yjB+?1Z9u5TW#zbP6BA(FUSZ_ zgUT@qvVVWrvD971G@hWZnNgtqi>H;7_Q~bfrzRQ0Pk#X>ZOv$M*_B^-dlC*F{)7

-!~H~_HE}U~#h!KLP$od(gG85i!`;$4 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

-yP4E_%@QRXaH9q+bL;@@lrb?_V^7}8HZ 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 %Mr47Rc@;U^YR8K-$f6p!PCj3 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 I@;z~mz) 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 Date: Thu, 8 Jul 2021 17:49:15 -0700 Subject: [PATCH 129/433] Update image class names. --- impeller/compositor/texture_descriptor.h | 2 +- impeller/compositor/texture_descriptor.mm | 14 ++-- impeller/image/BUILD.gn | 4 +- impeller/image/compressed_image.cc | 86 ++++++++++++++++++++ impeller/image/compressed_image.h | 30 +++++++ impeller/image/image.cc | 87 +++++---------------- impeller/image/image.h | 32 ++++++-- impeller/image/image_result.cc | 39 --------- impeller/image/image_result.h | 50 ------------ impeller/primitives/primitives_unittests.mm | 4 +- 10 files changed, 176 insertions(+), 172 deletions(-) create mode 100644 impeller/image/compressed_image.cc create mode 100644 impeller/image/compressed_image.h delete mode 100644 impeller/image/image_result.cc delete mode 100644 impeller/image/image_result.h diff --git a/impeller/compositor/texture_descriptor.h b/impeller/compositor/texture_descriptor.h index c3b7f653fbd31..53b8f316a712f 100644 --- a/impeller/compositor/texture_descriptor.h +++ b/impeller/compositor/texture_descriptor.h @@ -39,7 +39,7 @@ struct TextureDescriptor { } static std::optional MakeFromImageResult( - const ImageResult& result); + const Image& result); }; } // namespace impeller diff --git a/impeller/compositor/texture_descriptor.mm b/impeller/compositor/texture_descriptor.mm index 0822b10fe8cc2..70120f615baef 100644 --- a/impeller/compositor/texture_descriptor.mm +++ b/impeller/compositor/texture_descriptor.mm @@ -7,24 +7,24 @@ namespace impeller { std::optional FormatForImageResultComponents( - ImageResult::Components comp) { + Image::Components comp) { switch (comp) { - case ImageResult::Components::Invalid: + case Image::Components::Invalid: return std::nullopt; - case ImageResult::Components::Grey: + case Image::Components::Grey: return std::nullopt; - case ImageResult::Components::GreyAlpha: + case Image::Components::GreyAlpha: return std::nullopt; - case ImageResult::Components::RGB: + case Image::Components::RGB: return std::nullopt; - case ImageResult::Components::RGBA: + case Image::Components::RGBA: return PixelFormat::kPixelFormat_R8G8B8A8_UNormInt; } return std::nullopt; } std::optional TextureDescriptor::MakeFromImageResult( - const ImageResult& result) { + const Image& result) { if (!result.IsValid()) { return std::nullopt; } diff --git a/impeller/image/BUILD.gn b/impeller/image/BUILD.gn index a54fcb97330bd..f19ec4666f2c1 100644 --- a/impeller/image/BUILD.gn +++ b/impeller/image/BUILD.gn @@ -6,10 +6,10 @@ import("//flutter/impeller/tools/impeller.gni") impeller_component("image") { sources = [ + "compressed_image.cc", + "compressed_image.h", "image.cc", "image.h", - "image_result.cc", - "image_result.h", ] deps = [ "../third_party/stb" ] diff --git a/impeller/image/compressed_image.cc b/impeller/image/compressed_image.cc new file mode 100644 index 0000000000000..719ba6311aee7 --- /dev/null +++ b/impeller/image/compressed_image.cc @@ -0,0 +1,86 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "impeller/image/compressed_image.h" + +#include + +namespace impeller { + +CompressedImage::CompressedImage( + std::shared_ptr sourceAllocation) + : source_(std::move(sourceAllocation)) {} + +CompressedImage::~CompressedImage() = default; + +Image CompressedImage::Decode() const { + if (!source_) { + return {}; + } + + int width = 0; + int height = 0; + int comps = 0; + + stbi_uc* decoded = + ::stbi_load_from_memory(source_->GetMapping(), // Source Data + source_->GetSize(), // Source Data Size + &width, // Out: Width + &height, // Out: Height + &comps, // Out: Components + STBI_default); + + if (decoded == nullptr) { + FML_LOG(ERROR) << "Could not decode image from host memory."; + return {}; + } + + auto dest_allocation = std::make_shared( + decoded, // bytes + width * height * comps * sizeof(stbi_uc), // byte size + [](const uint8_t* data, size_t size) { + ::stbi_image_free(const_cast(data)); + } // release proc + ); + + /* + * Make sure we got a valid component set. + */ + auto components = Image::Components::Invalid; + + switch (comps) { + case STBI_grey: + components = Image::Components::Grey; + break; + case STBI_grey_alpha: + components = Image::Components::GreyAlpha; + break; + case STBI_rgb: + components = Image::Components::RGB; + break; + case STBI_rgb_alpha: + components = Image::Components::RGBA; + break; + default: + components = Image::Components::Invalid; + break; + } + + if (components == Image::Components::Invalid) { + FML_LOG(ERROR) << "Could not detect image components when decoding."; + return {}; + } + + return Image{ + ISize{width, height}, // size + components, // components + std::move(dest_allocation) // allocation + }; +} + +bool CompressedImage::IsValid() const { + return static_cast(source_); +} + +} // namespace impeller diff --git a/impeller/image/compressed_image.h b/impeller/image/compressed_image.h new file mode 100644 index 0000000000000..a30c32276b96a --- /dev/null +++ b/impeller/image/compressed_image.h @@ -0,0 +1,30 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#pragma once + +#include "flutter/fml/macros.h" +#include "flutter/fml/mapping.h" +#include "impeller/geometry/size.h" +#include "impeller/image/image.h" + +namespace impeller { + +class ImageSource; + +class CompressedImage { + public: + CompressedImage(std::shared_ptr sourceAllocation); + + ~CompressedImage(); + + [[nodiscard]] Image Decode() const; + + bool IsValid() const; + + private: + std::shared_ptr source_; +}; + +} // namespace impeller diff --git a/impeller/image/image.cc b/impeller/image/image.cc index edcb4b3bf137e..7ea8b9013afc0 100644 --- a/impeller/image/image.cc +++ b/impeller/image/image.cc @@ -2,83 +2,38 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -#include "image.h" -#include +#include "impeller/image/image.h" namespace impeller { -Image::Image(std::shared_ptr sourceAllocation) - : source_(std::move(sourceAllocation)) {} +Image::Image() = default; -Image::~Image() = default; - -ImageResult Image::Decode() const { - if (!source_) { - return {}; - } - - int width = 0; - int height = 0; - int comps = 0; - - stbi_uc* decoded = - ::stbi_load_from_memory(source_->GetMapping(), // Source Data - source_->GetSize(), // Source Data Size - &width, // Out: Width - &height, // Out: Height - &comps, // Out: Components - STBI_default); - - if (decoded == nullptr) { - FML_LOG(ERROR) << "Could not decode image from host memory."; - return {}; +Image::Image(ISize size, + Components components, + std::shared_ptr allocation) + : size_(size), components_(components), allocation_(std::move(allocation)) { + if (!allocation_ || !size.IsPositive()) { + return; } + is_valid_ = true; +} - auto dest_allocation = std::make_shared( - decoded, // bytes - width * height * comps * sizeof(stbi_uc), // byte size - [](const uint8_t* data, size_t size) { - ::stbi_image_free(const_cast(data)); - } // release proc - ); - - /* - * Make sure we got a valid component set. - */ - auto components = ImageResult::Components::Invalid; +Image::~Image() = default; - switch (comps) { - case STBI_grey: - components = ImageResult::Components::Grey; - break; - case STBI_grey_alpha: - components = ImageResult::Components::GreyAlpha; - break; - case STBI_rgb: - components = ImageResult::Components::RGB; - break; - case STBI_rgb_alpha: - components = ImageResult::Components::RGBA; - break; - default: - components = ImageResult::Components::Invalid; - break; - } +bool Image::IsValid() const { + return is_valid_; +} - if (components == ImageResult::Components::Invalid) { - FML_LOG(ERROR) << "Could not detect image components when decoding."; - return {}; - } +const ISize& Image::GetSize() const { + return size_; +} - return ImageResult{ - ISize{width, height}, // size - components, // components - std::move(dest_allocation) // allocation - }; +Image::Components Image::GetComponents() const { + return components_; } -bool Image::IsValid() const { - return static_cast(source_); +const std::shared_ptr& Image::GetAllocation() const { + return allocation_; } } // namespace impeller diff --git a/impeller/image/image.h b/impeller/image/image.h index 612e76763bd45..30013542a5518 100644 --- a/impeller/image/image.h +++ b/impeller/image/image.h @@ -4,27 +4,47 @@ #pragma once +#include + #include "flutter/fml/macros.h" #include "flutter/fml/mapping.h" -#include "image_result.h" #include "impeller/geometry/size.h" namespace impeller { -class ImageSource; - class Image { public: - Image(std::shared_ptr sourceAllocation); + enum class Components { + Invalid, + Grey, + GreyAlpha, + RGB, + RGBA, + }; + + Image(); + + Image(ISize size, + Components components, + std::shared_ptr allocation); ~Image(); - [[nodiscard]] ImageResult Decode() const; + const ISize& GetSize() const; bool IsValid() const; + Components GetComponents() const; + + const std::shared_ptr& GetAllocation() const; + private: - std::shared_ptr source_; + ISize size_; + Components components_ = Components::Invalid; + std::shared_ptr allocation_; + bool is_valid_ = false; + + FML_DISALLOW_COPY_AND_ASSIGN(Image); }; } // namespace impeller diff --git a/impeller/image/image_result.cc b/impeller/image/image_result.cc deleted file mode 100644 index a778de0e388e7..0000000000000 --- a/impeller/image/image_result.cc +++ /dev/null @@ -1,39 +0,0 @@ -// Copyright 2013 The Flutter Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#include "image_result.h" - -namespace impeller { - -ImageResult::ImageResult() = default; - -ImageResult::ImageResult(ISize size, - Components components, - std::shared_ptr allocation) - : size_(size), components_(components), allocation_(std::move(allocation)) { - if (!allocation_ || !size.IsPositive()) { - return; - } - is_valid_ = true; -} - -ImageResult::~ImageResult() = default; - -bool ImageResult::IsValid() const { - return is_valid_; -} - -const ISize& ImageResult::GetSize() const { - return size_; -} - -ImageResult::Components ImageResult::GetComponents() const { - return components_; -} - -const std::shared_ptr& ImageResult::GetAllocation() const { - return allocation_; -} - -} // namespace impeller diff --git a/impeller/image/image_result.h b/impeller/image/image_result.h deleted file mode 100644 index 7b0c23354a8ab..0000000000000 --- a/impeller/image/image_result.h +++ /dev/null @@ -1,50 +0,0 @@ -// Copyright 2013 The Flutter Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#pragma once - -#include - -#include "flutter/fml/macros.h" -#include "flutter/fml/mapping.h" -#include "impeller/geometry/size.h" - -namespace impeller { - -class ImageResult { - public: - enum class Components { - Invalid, - Grey, - GreyAlpha, - RGB, - RGBA, - }; - - ImageResult(); - - ImageResult(ISize size, - Components components, - std::shared_ptr allocation); - - ~ImageResult(); - - const ISize& GetSize() const; - - bool IsValid() const; - - Components GetComponents() const; - - const std::shared_ptr& GetAllocation() const; - - private: - ISize size_; - Components components_ = Components::Invalid; - std::shared_ptr allocation_; - bool is_valid_ = false; - - FML_DISALLOW_COPY_AND_ASSIGN(ImageResult); -}; - -} // namespace impeller diff --git a/impeller/primitives/primitives_unittests.mm b/impeller/primitives/primitives_unittests.mm index 378d33edcded6..922c6af7c6a47 100644 --- a/impeller/primitives/primitives_unittests.mm +++ b/impeller/primitives/primitives_unittests.mm @@ -10,6 +10,7 @@ #include "impeller/compositor/sampler_descriptor.h" #include "impeller/compositor/surface.h" #include "impeller/compositor/vertex_buffer_builder.h" +#include "impeller/image/compressed_image.h" #include "impeller/image/image.h" #include "impeller/playground/playground.h" #include "impeller/primitives/box.frag.h" @@ -47,7 +48,8 @@ ASSERT_TRUE(vertex_buffer); // Contents texture. - Image image(flutter::testing::OpenFixtureAsMapping("bay_bridge.jpg")); + CompressedImage image( + flutter::testing::OpenFixtureAsMapping("bay_bridge.jpg")); auto result = image.Decode(); ASSERT_TRUE(result.IsValid()); auto texture_descriptor = TextureDescriptor::MakeFromImageResult(result); From 80bdd0d8c8523161242f7f1a171ac5dc55a1e0b2 Mon Sep 17 00:00:00 2001 From: Chinmay Garde Date: Thu, 8 Jul 2021 17:53:42 -0700 Subject: [PATCH 130/433] Update enum name. --- impeller/compositor/texture_descriptor.mm | 13 ++++++------- impeller/image/compressed_image.cc | 14 +++++++------- impeller/image/image.cc | 4 ++-- impeller/image/image.h | 8 ++++---- 4 files changed, 19 insertions(+), 20 deletions(-) diff --git a/impeller/compositor/texture_descriptor.mm b/impeller/compositor/texture_descriptor.mm index 70120f615baef..5bdcc81e507c7 100644 --- a/impeller/compositor/texture_descriptor.mm +++ b/impeller/compositor/texture_descriptor.mm @@ -6,18 +6,17 @@ namespace impeller { -std::optional FormatForImageResultComponents( - Image::Components comp) { +std::optional FormatForImageResultComponents(Image::Format comp) { switch (comp) { - case Image::Components::Invalid: + case Image::Format::Invalid: return std::nullopt; - case Image::Components::Grey: + case Image::Format::Grey: return std::nullopt; - case Image::Components::GreyAlpha: + case Image::Format::GreyAlpha: return std::nullopt; - case Image::Components::RGB: + case Image::Format::RGB: return std::nullopt; - case Image::Components::RGBA: + case Image::Format::RGBA: return PixelFormat::kPixelFormat_R8G8B8A8_UNormInt; } return std::nullopt; diff --git a/impeller/image/compressed_image.cc b/impeller/image/compressed_image.cc index 719ba6311aee7..6076be69888b3 100644 --- a/impeller/image/compressed_image.cc +++ b/impeller/image/compressed_image.cc @@ -47,27 +47,27 @@ Image CompressedImage::Decode() const { /* * Make sure we got a valid component set. */ - auto components = Image::Components::Invalid; + auto components = Image::Format::Invalid; switch (comps) { case STBI_grey: - components = Image::Components::Grey; + components = Image::Format::Grey; break; case STBI_grey_alpha: - components = Image::Components::GreyAlpha; + components = Image::Format::GreyAlpha; break; case STBI_rgb: - components = Image::Components::RGB; + components = Image::Format::RGB; break; case STBI_rgb_alpha: - components = Image::Components::RGBA; + components = Image::Format::RGBA; break; default: - components = Image::Components::Invalid; + components = Image::Format::Invalid; break; } - if (components == Image::Components::Invalid) { + if (components == Image::Format::Invalid) { FML_LOG(ERROR) << "Could not detect image components when decoding."; return {}; } diff --git a/impeller/image/image.cc b/impeller/image/image.cc index 7ea8b9013afc0..e8b15cfa5cab0 100644 --- a/impeller/image/image.cc +++ b/impeller/image/image.cc @@ -9,7 +9,7 @@ namespace impeller { Image::Image() = default; Image::Image(ISize size, - Components components, + Format components, std::shared_ptr allocation) : size_(size), components_(components), allocation_(std::move(allocation)) { if (!allocation_ || !size.IsPositive()) { @@ -28,7 +28,7 @@ const ISize& Image::GetSize() const { return size_; } -Image::Components Image::GetComponents() const { +Image::Format Image::GetComponents() const { return components_; } diff --git a/impeller/image/image.h b/impeller/image/image.h index 30013542a5518..3c3271f5eb5e3 100644 --- a/impeller/image/image.h +++ b/impeller/image/image.h @@ -14,7 +14,7 @@ namespace impeller { class Image { public: - enum class Components { + enum class Format { Invalid, Grey, GreyAlpha, @@ -25,7 +25,7 @@ class Image { Image(); Image(ISize size, - Components components, + Format components, std::shared_ptr allocation); ~Image(); @@ -34,13 +34,13 @@ class Image { bool IsValid() const; - Components GetComponents() const; + Format GetComponents() const; const std::shared_ptr& GetAllocation() const; private: ISize size_; - Components components_ = Components::Invalid; + Format components_ = Format::Invalid; std::shared_ptr allocation_; bool is_valid_ = false; From 24ad470e2ee3cad95add236d3e7f11d0d1edbca1 Mon Sep 17 00:00:00 2001 From: Chinmay Garde Date: Thu, 8 Jul 2021 18:44:44 -0700 Subject: [PATCH 131/433] Perform software conversion for unsupported pixel formats. --- impeller/base/allocation.cc | 10 +-- impeller/base/allocation.h | 2 +- impeller/compositor/texture_descriptor.mm | 3 +- impeller/image/image.cc | 92 +++++++++++++++++++-- impeller/image/image.h | 11 +-- impeller/primitives/primitives_unittests.mm | 12 +-- 6 files changed, 106 insertions(+), 24 deletions(-) diff --git a/impeller/base/allocation.cc b/impeller/base/allocation.cc index d4f98ad4ff38a..e41d0f9e6cc5b 100644 --- a/impeller/base/allocation.cc +++ b/impeller/base/allocation.cc @@ -28,8 +28,9 @@ size_t Allocation::GetReservedLength() const { return reserved_; } -bool Allocation::Truncate(size_t length) { - if (!ReserveNPOT(length)) { +bool Allocation::Truncate(size_t length, bool npot) { + const auto reserved = npot ? ReserveNPOT(length) : Reserve(length); + if (!reserved) { return false; } length_ = length; @@ -53,13 +54,12 @@ static uint32_t NextPowerOfTwoSize(uint32_t x) { } bool Allocation::ReserveNPOT(size_t reserved) { + // Reserve at least one page of data. + reserved = std::max(4096u, reserved); return Reserve(NextPowerOfTwoSize(reserved)); } bool Allocation::Reserve(size_t reserved) { - // Reserve at least one page of data. - reserved = std::max(4096u, reserved); - if (reserved == reserved_) { return true; } diff --git a/impeller/base/allocation.h b/impeller/base/allocation.h index a58974e8f4ca5..b4bef69baeace 100644 --- a/impeller/base/allocation.h +++ b/impeller/base/allocation.h @@ -21,7 +21,7 @@ class Allocation { size_t GetReservedLength() const; - [[nodiscard]] bool Truncate(size_t length); + [[nodiscard]] bool Truncate(size_t length, bool npot = true); private: uint8_t* buffer_ = nullptr; diff --git a/impeller/compositor/texture_descriptor.mm b/impeller/compositor/texture_descriptor.mm index 5bdcc81e507c7..1c092b2902b03 100644 --- a/impeller/compositor/texture_descriptor.mm +++ b/impeller/compositor/texture_descriptor.mm @@ -28,8 +28,7 @@ return std::nullopt; } - const auto pixel_format = - FormatForImageResultComponents(result.GetComponents()); + const auto pixel_format = FormatForImageResultComponents(result.GetFormat()); if (!pixel_format.has_value()) { FML_DLOG(ERROR) << "Unknown image format."; return std::nullopt; diff --git a/impeller/image/image.cc b/impeller/image/image.cc index e8b15cfa5cab0..443f1edbf0df4 100644 --- a/impeller/image/image.cc +++ b/impeller/image/image.cc @@ -4,15 +4,20 @@ #include "impeller/image/image.h" +#include + +#include "flutter/fml/mapping.h" +#include "impeller/base/allocation.h" + namespace impeller { Image::Image() = default; Image::Image(ISize size, - Format components, + Format format, std::shared_ptr allocation) - : size_(size), components_(components), allocation_(std::move(allocation)) { - if (!allocation_ || !size.IsPositive()) { + : size_(size), format_(format), allocation_(std::move(allocation)) { + if (!allocation_ || !size.IsPositive() || format_ == Format::Invalid) { return; } is_valid_ = true; @@ -28,12 +33,89 @@ const ISize& Image::GetSize() const { return size_; } -Image::Format Image::GetComponents() const { - return components_; +Image::Format Image::GetFormat() const { + return format_; } const std::shared_ptr& Image::GetAllocation() const { return allocation_; } +static size_t GetBytesPerPixel(Image::Format format) { + switch (format) { + case Image::Format::Invalid: + return 0u; + case Image::Format::Grey: + return 1u; + case Image::Format::GreyAlpha: + return 1u; + case Image::Format::RGB: + return 3u; + case Image::Format::RGBA: + return 4; + } + return 0u; +} + +Image Image::ConvertToRGBA() const { + if (!is_valid_) { + return {}; + } + + if (format_ == Format::RGBA) { + return Image{size_, format_, allocation_}; + } + + const auto bpp = GetBytesPerPixel(format_); + const auto source_byte_size = size_.Area() * bpp; + if (allocation_->GetSize() < source_byte_size) { + return {}; + } + + auto rgba_allocation = std::make_shared(); + if (!rgba_allocation->Truncate(size_.Area() * 4u, false)) { + return {}; + } + + const uint8_t* source = allocation_->GetMapping(); + uint8_t* dest = rgba_allocation->GetBuffer(); + + for (size_t i = 0, j = 0; i < source_byte_size; i += bpp, j += 4u) { + switch (format_) { + case Image::Format::Grey: + dest[j + 0] = source[i]; + dest[j + 1] = source[i]; + dest[j + 2] = source[i]; + dest[j + 3] = std::numeric_limits::max(); + break; + case Image::Format::GreyAlpha: + dest[j + 0] = std::numeric_limits::max(); + dest[j + 1] = std::numeric_limits::max(); + dest[j + 2] = std::numeric_limits::max(); + dest[j + 3] = source[i]; + break; + case Image::Format::RGB: + dest[j + 0] = source[i + 0]; + dest[j + 1] = source[i + 1]; + dest[j + 2] = source[i + 2]; + dest[j + 3] = std::numeric_limits::max(); + break; + case Image::Format::Invalid: + case Image::Format::RGBA: + // Should never happen. The necessary checks have already been + // performed. + FML_CHECK(false); + break; + } + } + + return Image{ + size_, Format::RGBA, + std::make_shared( + rgba_allocation->GetBuffer(), // + rgba_allocation->GetLength(), // + [rgba_allocation](auto, auto) {}) // + }; +} + } // namespace impeller diff --git a/impeller/image/image.h b/impeller/image/image.h index 3c3271f5eb5e3..f888ab2445457 100644 --- a/impeller/image/image.h +++ b/impeller/image/image.h @@ -5,6 +5,7 @@ #pragma once #include +#include #include "flutter/fml/macros.h" #include "flutter/fml/mapping.h" @@ -25,7 +26,7 @@ class Image { Image(); Image(ISize size, - Format components, + Format format, std::shared_ptr allocation); ~Image(); @@ -34,17 +35,17 @@ class Image { bool IsValid() const; - Format GetComponents() const; + Format GetFormat() const; const std::shared_ptr& GetAllocation() const; + Image ConvertToRGBA() const; + private: ISize size_; - Format components_ = Format::Invalid; + Format format_ = Format::Invalid; std::shared_ptr allocation_; bool is_valid_ = false; - - FML_DISALLOW_COPY_AND_ASSIGN(Image); }; } // namespace impeller diff --git a/impeller/primitives/primitives_unittests.mm b/impeller/primitives/primitives_unittests.mm index 922c6af7c6a47..2c81ea01dc3fb 100644 --- a/impeller/primitives/primitives_unittests.mm +++ b/impeller/primitives/primitives_unittests.mm @@ -48,18 +48,18 @@ ASSERT_TRUE(vertex_buffer); // Contents texture. - CompressedImage image( + CompressedImage compressed_image( flutter::testing::OpenFixtureAsMapping("bay_bridge.jpg")); - auto result = image.Decode(); - ASSERT_TRUE(result.IsValid()); - auto texture_descriptor = TextureDescriptor::MakeFromImageResult(result); + auto image = compressed_image.Decode().ConvertToRGBA(); + ASSERT_TRUE(image.IsValid()); + auto texture_descriptor = TextureDescriptor::MakeFromImageResult(image); ASSERT_TRUE(texture_descriptor.has_value()); auto texture = context->GetPermanentsAllocator()->CreateTexture( StorageMode::kHostVisible, texture_descriptor.value()); ASSERT_TRUE(texture); texture->SetLabel("Bay Bridge"); - auto uploaded = texture->SetContents(result.GetAllocation()->GetMapping(), - result.GetAllocation()->GetSize()); + auto uploaded = texture->SetContents(image.GetAllocation()->GetMapping(), + image.GetAllocation()->GetSize()); ASSERT_TRUE(uploaded); auto sampler = context->GetSamplerLibrary()->GetSampler({}); ASSERT_TRUE(sampler); From 4abbc6724c320f1ffc0b0edafd6f3a9a0092e08d Mon Sep 17 00:00:00 2001 From: Chinmay Garde Date: Fri, 9 Jul 2021 15:10:57 -0700 Subject: [PATCH 132/433] Make sure texture bindings are correctly propagated. --- impeller/compiler/code_gen_template.h | 13 +++----- impeller/compiler/reflector.cc | 8 +++++ impeller/playground/playground.h | 4 +++ impeller/playground/playground.mm | 35 +++++++++++++++++++++ impeller/primitives/box.frag | 11 +++++-- impeller/primitives/primitives_unittests.mm | 34 ++++++++++---------- impeller/shader_glue/shader_types.h | 10 ++++++ 7 files changed, 88 insertions(+), 27 deletions(-) diff --git a/impeller/compiler/code_gen_template.h b/impeller/compiler/code_gen_template.h index a93b8fd3bdc4d..326a4464773a8 100644 --- a/impeller/compiler/code_gen_template.h +++ b/impeller/compiler/code_gen_template.h @@ -80,15 +80,10 @@ struct {{camel_case(shader_name)}}{{camel_case(shader_stage)}}Shader { // =========================================================================== {% for sampled_image in sampled_images %} - static constexpr auto kInput{{camel_case(sampled_image.name)}} = ShaderStageIOSlot { // {{sampled_image.name}} - "{{sampled_image.name}}", // name - {{sampled_image.location}}u, // attribute location - {{sampled_image.descriptor_set}}u, // attribute set - {{sampled_image.binding}}u, // attribute binding - {{sampled_image.type.type_name}}, // type - {{sampled_image.type.bit_width}}u, // bit width of type - {{sampled_image.type.vec_size}}u, // vec size - {{sampled_image.type.columns}}u // number of columns + static constexpr auto kSampledImage{{camel_case(sampled_image.name)}} = SampledImageSlot { // {{sampled_image.name}} + "{{sampled_image.name}}", // name + {{sampled_image.msl_res_0}}u, // auto primary + {{sampled_image.msl_res_1}}u, // auto secondary }; {% endfor %} {% endif %} diff --git a/impeller/compiler/reflector.cc b/impeller/compiler/reflector.cc index db6c340073215..efdb1f3c90d91 100644 --- a/impeller/compiler/reflector.cc +++ b/impeller/compiler/reflector.cc @@ -284,6 +284,14 @@ std::optional Reflector::ReflectResource( resource.id, spv::Decoration::DecorationLocation); result["index"] = compiler_->get_decoration(resource.id, spv::Decoration::DecorationIndex); + result["msl_res_0"] = + compiler_->get_automatic_msl_resource_binding(resource.id); + result["msl_res_1"] = + compiler_->get_automatic_msl_resource_binding_secondary(resource.id); + result["msl_res_2"] = + compiler_->get_automatic_msl_resource_binding_tertiary(resource.id); + result["msl_res_3"] = + compiler_->get_automatic_msl_resource_binding_quaternary(resource.id); auto type = ReflectType(resource.type_id); if (!type.has_value()) { return std::nullopt; diff --git a/impeller/playground/playground.h b/impeller/playground/playground.h index 081e24a9e9187..dfd6179be2037 100644 --- a/impeller/playground/playground.h +++ b/impeller/playground/playground.h @@ -6,6 +6,7 @@ #include "flutter/fml/macros.h" #include "gtest/gtest.h" #include "impeller/compositor/renderer.h" +#include "impeller/compositor/texture.h" namespace impeller { @@ -19,6 +20,9 @@ class Playground : public ::testing::Test { bool OpenPlaygroundHere(Renderer::RenderCallback render_callback); + std::shared_ptr CreateTextureForFixture( + const char* fixture_name) const; + private: Renderer renderer_; diff --git a/impeller/playground/playground.mm b/impeller/playground/playground.mm index 387beb894a32f..3bf2b117dec1f 100644 --- a/impeller/playground/playground.mm +++ b/impeller/playground/playground.mm @@ -6,11 +6,13 @@ #include "flutter/fml/paths.h" #include "flutter/testing/testing.h" +#include "impeller/compositor/allocator.h" #include "impeller/compositor/context.h" #include "impeller/compositor/formats_metal.h" #include "impeller/compositor/render_pass.h" #include "impeller/compositor/renderer.h" #include "impeller/compositor/surface.h" +#include "impeller/image/compressed_image.h" #include "impeller/playground/playground.h" #define GLFW_INCLUDE_NONE @@ -144,4 +146,37 @@ static void PlaygroundKeyCallback(GLFWwindow* window, return true; } +std::shared_ptr Playground::CreateTextureForFixture( + const char* fixture_name) const { + CompressedImage compressed_image( + flutter::testing::OpenFixtureAsMapping(fixture_name)); + auto image = compressed_image.Decode().ConvertToRGBA(); + if (!image.IsValid()) { + FML_LOG(ERROR) << "Could not find fixture named " << fixture_name; + return nullptr; + } + auto texture_descriptor = TextureDescriptor::MakeFromImageResult(image); + if (!texture_descriptor.has_value()) { + FML_LOG(ERROR) << "Could not figure out texture descriptor for fixture " + << fixture_name; + return nullptr; + } + auto texture = + renderer_.GetContext()->GetPermanentsAllocator()->CreateTexture( + StorageMode::kHostVisible, texture_descriptor.value()); + if (!texture) { + FML_LOG(ERROR) << "Could not allocate texture for fixture " << fixture_name; + return nullptr; + } + texture->SetLabel(fixture_name); + auto uploaded = texture->SetContents(image.GetAllocation()->GetMapping(), + image.GetAllocation()->GetSize()); + if (!uploaded) { + FML_LOG(ERROR) << "Could not upload texture to device memory for fixture " + << fixture_name; + return nullptr; + } + return texture; +} + } // namespace impeller diff --git a/impeller/primitives/box.frag b/impeller/primitives/box.frag index c1d0500b372c9..2e07bddef3398 100644 --- a/impeller/primitives/box.frag +++ b/impeller/primitives/box.frag @@ -2,13 +2,20 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. +uniform FrameInfo { + float current_time; +} frame; + in vec4 color; in vec2 interporlated_texture_coordinates; out vec4 frag_color; -uniform sampler2D contents_texture; +uniform sampler2D contents1_texture; +uniform sampler2D contents2_texture; void main() { - frag_color = texture(contents_texture, interporlated_texture_coordinates); + vec4 tex1 = texture(contents1_texture, interporlated_texture_coordinates); + vec4 tex2 = texture(contents2_texture, interporlated_texture_coordinates); + frag_color = mix(tex1, tex2, frame.current_time - floor(frame.current_time)); } diff --git a/impeller/primitives/primitives_unittests.mm b/impeller/primitives/primitives_unittests.mm index 2c81ea01dc3fb..2aae7c5c213aa 100644 --- a/impeller/primitives/primitives_unittests.mm +++ b/impeller/primitives/primitives_unittests.mm @@ -47,20 +47,9 @@ vertex_builder.CreateVertexBuffer(*context->GetPermanentsAllocator()); ASSERT_TRUE(vertex_buffer); - // Contents texture. - CompressedImage compressed_image( - flutter::testing::OpenFixtureAsMapping("bay_bridge.jpg")); - auto image = compressed_image.Decode().ConvertToRGBA(); - ASSERT_TRUE(image.IsValid()); - auto texture_descriptor = TextureDescriptor::MakeFromImageResult(image); - ASSERT_TRUE(texture_descriptor.has_value()); - auto texture = context->GetPermanentsAllocator()->CreateTexture( - StorageMode::kHostVisible, texture_descriptor.value()); - ASSERT_TRUE(texture); - texture->SetLabel("Bay Bridge"); - auto uploaded = texture->SetContents(image.GetAllocation()->GetMapping(), - image.GetAllocation()->GetSize()); - ASSERT_TRUE(uploaded); + auto bridge = CreateTextureForFixture("bay_bridge.jpg"); + auto boston = CreateTextureForFixture("boston.jpg"); + ASSERT_TRUE(bridge && boston); auto sampler = context->GetSamplerLibrary()->GetSampler({}); ASSERT_TRUE(sampler); Renderer::RenderCallback callback = [&](const Surface& surface, @@ -78,10 +67,23 @@ CompressedImage compressed_image( .buffers[BoxVertexShader::kUniformUniformBuffer.binding] = pass.GetTransientsBuffer().EmplaceUniform(uniforms); + BoxFragmentShader::FrameInfo frame_info; + frame_info.current_time = fml::TimePoint::Now().ToEpochDelta().ToSecondsF(); cmd.fragment_bindings - .textures[BoxFragmentShader::kInputContentsTexture.location] = texture; + .buffers[BoxFragmentShader::kUniformFrameInfo.binding] = + pass.GetTransientsBuffer().EmplaceUniform(frame_info); cmd.fragment_bindings - .samplers[BoxFragmentShader::kInputContentsTexture.location] = sampler; + .textures[BoxFragmentShader::kSampledImageContents1Texture + .texture_index] = bridge; + cmd.fragment_bindings + .samplers[BoxFragmentShader::kSampledImageContents1Texture + .sampler_index] = sampler; + cmd.fragment_bindings + .textures[BoxFragmentShader::kSampledImageContents2Texture + .texture_index] = boston; + cmd.fragment_bindings + .samplers[BoxFragmentShader::kSampledImageContents2Texture + .sampler_index] = sampler; cmd.index_buffer = vertex_buffer.index_buffer; cmd.index_count = vertex_buffer.index_count; diff --git a/impeller/shader_glue/shader_types.h b/impeller/shader_glue/shader_types.h index d13ab4d2ce1bd..9045829681720 100644 --- a/impeller/shader_glue/shader_types.h +++ b/impeller/shader_glue/shader_types.h @@ -58,6 +58,16 @@ struct ShaderStageIOSlot { size_t columns; }; +struct SampledImageSlot { + const char* name; + size_t texture_index; + size_t sampler_index; + + constexpr bool HasTexture() const { return texture_index < 32u; } + + constexpr bool HasSampler() const { return sampler_index < 32u; } +}; + template struct Padding { private: From 7f53d1cc97986954fd1a73cf9653e3ad6012f9e2 Mon Sep 17 00:00:00 2001 From: Chinmay Garde Date: Fri, 9 Jul 2021 16:56:54 -0700 Subject: [PATCH 133/433] Fix struct alignment for uniform buffers. --- impeller/compiler/BUILD.gn | 1 + impeller/compiler/reflector.cc | 75 +++++++++++++++--------------- impeller/compositor/host_buffer.mm | 3 +- impeller/image/BUILD.gn | 5 +- impeller/primitives/box.frag | 3 +- 5 files changed, 45 insertions(+), 42 deletions(-) diff --git a/impeller/compiler/BUILD.gn b/impeller/compiler/BUILD.gn index 9b13cf0a76c93..dcef761963d5e 100644 --- a/impeller/compiler/BUILD.gn +++ b/impeller/compiler/BUILD.gn @@ -20,6 +20,7 @@ impeller_component("compiler_lib") { ] public_deps = [ + "../base", "../geometry", "//flutter/fml", "//flutter/runtime:libdart", diff --git a/impeller/compiler/reflector.cc b/impeller/compiler/reflector.cc index efdb1f3c90d91..ef451f1b8e732 100644 --- a/impeller/compiler/reflector.cc +++ b/impeller/compiler/reflector.cc @@ -15,6 +15,7 @@ #include "flutter/impeller/compiler/utilities.h" #include "flutter/impeller/geometry/matrix.h" #include "flutter/impeller/geometry/scalar.h" +#include "impeller/base/strings.h" namespace impeller { namespace compiler { @@ -374,9 +375,26 @@ std::vector Reflector::ReadStructMembers( std::vector result; - size_t total_byte_length = 0; + size_t current_byte_offset = 0; + for (size_t i = 0; i < struct_type.member_types.size(); i++) { const auto& member = compiler_->get_type(struct_type.member_types[i]); + const auto struct_member_offset = + compiler_->type_struct_member_offset(struct_type, i); + + if (struct_member_offset > current_byte_offset) { + const auto alignment_pad = struct_member_offset - current_byte_offset; + result.emplace_back(StructMember{ + .type = TypeNameWithPaddingOfSize(alignment_pad), + .name = SPrintF("_align_%s", + GetMemberNameAtIndex(struct_type, i).c_str()), + .offset = current_byte_offset, + .byte_length = alignment_pad, + }); + current_byte_offset += alignment_pad; + } + + FML_CHECK(current_byte_offset == struct_member_offset); // Tightly packed 4x4 Matrix is special cased as we know how to work with // those. @@ -388,14 +406,14 @@ std::vector Reflector::ReadStructMembers( result.emplace_back(StructMember{ .type = "Matrix", .name = GetMemberNameAtIndex(struct_type, i), - .offset = total_byte_length, + .offset = struct_member_offset, .byte_length = sizeof(Matrix), }); - total_byte_length += sizeof(Matrix); + current_byte_offset += sizeof(Matrix); continue; } - // Tightly packed Point + // Tightly packed Point (vec2). if (member.basetype == SPIRType::BaseType::Float && // member.width == sizeof(float) * 8 && // member.columns == 1 && // @@ -404,14 +422,14 @@ std::vector Reflector::ReadStructMembers( result.emplace_back(StructMember{ .type = "Point", .name = GetMemberNameAtIndex(struct_type, i), - .offset = total_byte_length, + .offset = struct_member_offset, .byte_length = sizeof(Point), }); - total_byte_length += sizeof(Point); + current_byte_offset += sizeof(Point); continue; } - // Tightly packed Vector3 + // Tightly packed Vector3. if (member.basetype == SPIRType::BaseType::Float && // member.width == sizeof(float) * 8 && // member.columns == 1 && // @@ -420,14 +438,14 @@ std::vector Reflector::ReadStructMembers( result.emplace_back(StructMember{ .type = "Vector3", .name = GetMemberNameAtIndex(struct_type, i), - .offset = total_byte_length, + .offset = struct_member_offset, .byte_length = sizeof(Vector3), }); - total_byte_length += sizeof(Vector3); + current_byte_offset += sizeof(Vector3); continue; } - // Tightly packed Vector4 + // Tightly packed Vector4. if (member.basetype == SPIRType::BaseType::Float && // member.width == sizeof(float) * 8 && // member.columns == 1 && // @@ -436,14 +454,14 @@ std::vector Reflector::ReadStructMembers( result.emplace_back(StructMember{ .type = "Vector4", .name = GetMemberNameAtIndex(struct_type, i), - .offset = total_byte_length, + .offset = struct_member_offset, .byte_length = sizeof(Vector4), }); - total_byte_length += sizeof(Vector4); + current_byte_offset += sizeof(Vector4); continue; } - // Other single isolated scalars. + // Other isolated scalars (like bool, int, float/Scalar, etc..). { auto maybe_known_type = ReadKnownScalarType(member.basetype); if (maybe_known_type.has_value() && // @@ -454,23 +472,10 @@ std::vector Reflector::ReadStructMembers( result.emplace_back(StructMember{ .type = maybe_known_type.value().name, .name = GetMemberNameAtIndex(struct_type, i), - .offset = total_byte_length, + .offset = struct_member_offset, .byte_length = maybe_known_type.value().byte_size, }); - total_byte_length += maybe_known_type.value().byte_size; - - // Consider any excess padding. - const auto padding = - (member.width / 8u) - maybe_known_type.value().byte_size; - if (padding != 0) { - result.emplace_back(StructMember{ - .type = TypeNameWithPaddingOfSize(padding), - .name = GetMemberNameAtIndex(struct_type, i, "_pad"), - .offset = total_byte_length, - .byte_length = padding, - }); - total_byte_length += padding; - } + current_byte_offset += maybe_known_type.value().byte_size; continue; } } @@ -483,10 +488,11 @@ std::vector Reflector::ReadStructMembers( result.emplace_back(StructMember{ .type = TypeNameWithPaddingOfSize(byte_length), .name = GetMemberNameAtIndex(struct_type, i), - .offset = total_byte_length, + .offset = struct_member_offset, .byte_length = byte_length, }); - total_byte_length += byte_length; + current_byte_offset += byte_length; + continue; } } return result; @@ -504,16 +510,9 @@ std::optional Reflector::ReflectStructDefinition( return std::nullopt; } - size_t total_size = 0u; - for (const auto& member_type_id : type.member_types) { - const auto& member_type = compiler_->get_type(member_type_id); - total_size += - (member_type.width * member_type.vecsize * member_type.columns) / 8u; - } - StructDefinition struc; struc.name = struct_name; - struc.byte_length = total_size; + struc.byte_length = compiler_->get_declared_struct_size(type); struc.members = ReadStructMembers(type_id); return struc; } diff --git a/impeller/compositor/host_buffer.mm b/impeller/compositor/host_buffer.mm index 112406e413f0c..25e1724e4f2a0 100644 --- a/impeller/compositor/host_buffer.mm +++ b/impeller/compositor/host_buffer.mm @@ -45,10 +45,9 @@ BufferView HostBuffer::Emplace(const void* buffer, size_t length) { auto old_length = GetLength(); - if (!Truncate(GetLength() + length)) { + if (!Truncate(old_length + length)) { return {}; } - FML_DCHECK(GetBuffer()); generation_++; if (buffer) { ::memmove(GetBuffer() + old_length, buffer, length); diff --git a/impeller/image/BUILD.gn b/impeller/image/BUILD.gn index f19ec4666f2c1..35d7c0643c985 100644 --- a/impeller/image/BUILD.gn +++ b/impeller/image/BUILD.gn @@ -14,7 +14,10 @@ impeller_component("image") { deps = [ "../third_party/stb" ] - public_deps = [ "../geometry" ] + public_deps = [ + "../base", + "../geometry", + ] } impeller_component("image_unittests") { diff --git a/impeller/primitives/box.frag b/impeller/primitives/box.frag index 2e07bddef3398..a172ceab44243 100644 --- a/impeller/primitives/box.frag +++ b/impeller/primitives/box.frag @@ -4,6 +4,7 @@ uniform FrameInfo { float current_time; + vec2 cursor_position; } frame; in vec4 color; @@ -17,5 +18,5 @@ uniform sampler2D contents2_texture; void main() { vec4 tex1 = texture(contents1_texture, interporlated_texture_coordinates); vec4 tex2 = texture(contents2_texture, interporlated_texture_coordinates); - frag_color = mix(tex1, tex2, frame.current_time - floor(frame.current_time)); + frag_color = mix(tex1, tex2, frame.cursor_position.x); } From 8e3a6657a21583937ee45aeadfe1c3c42b9b8b99 Mon Sep 17 00:00:00 2001 From: Chinmay Garde Date: Sat, 10 Jul 2021 17:07:29 -0700 Subject: [PATCH 134/433] Wire up window position in the playground. --- impeller/compiler/code_gen_template.h | 4 ++-- impeller/geometry/color.h | 11 +++++++++++ impeller/playground/playground.h | 7 +++++++ impeller/playground/playground.mm | 21 ++++++++++++++++++++- impeller/primitives/box.frag | 3 ++- impeller/primitives/primitives_unittests.mm | 4 ++++ 6 files changed, 46 insertions(+), 4 deletions(-) diff --git a/impeller/compiler/code_gen_template.h b/impeller/compiler/code_gen_template.h index 326a4464773a8..71347134d5c46 100644 --- a/impeller/compiler/code_gen_template.h +++ b/impeller/compiler/code_gen_template.h @@ -82,8 +82,8 @@ struct {{camel_case(shader_name)}}{{camel_case(shader_stage)}}Shader { static constexpr auto kSampledImage{{camel_case(sampled_image.name)}} = SampledImageSlot { // {{sampled_image.name}} "{{sampled_image.name}}", // name - {{sampled_image.msl_res_0}}u, // auto primary - {{sampled_image.msl_res_1}}u, // auto secondary + {{sampled_image.msl_res_0}}u, // texture + {{sampled_image.msl_res_1}}u, // sampler }; {% endfor %} {% endif %} diff --git a/impeller/geometry/color.h b/impeller/geometry/color.h index ede6432835b83..0b2031ea411be 100644 --- a/impeller/geometry/color.h +++ b/impeller/geometry/color.h @@ -4,6 +4,8 @@ #pragma once +#include + #include "impeller/geometry/scalar.h" namespace impeller { @@ -627,6 +629,15 @@ struct Color { static constexpr Color YellowGreen() { return {154.0 / 255.0, 205.0 / 255.0, 50.0 / 255.0, 1.0}; } + + static Color Random() { + return { + static_cast((std::rand() % 255) / 255.0), // + static_cast((std::rand() % 255) / 255.0), // + static_cast((std::rand() % 255) / 255.0), // + 1.0 // + }; + } }; /** diff --git a/impeller/playground/playground.h b/impeller/playground/playground.h index dfd6179be2037..1d345111d87db 100644 --- a/impeller/playground/playground.h +++ b/impeller/playground/playground.h @@ -16,6 +16,10 @@ class Playground : public ::testing::Test { ~Playground(); + Point GetCursorPosition() const; + + ISize GetWindowSize() const; + std::shared_ptr GetContext() const; bool OpenPlaygroundHere(Renderer::RenderCallback render_callback); @@ -25,6 +29,9 @@ class Playground : public ::testing::Test { private: Renderer renderer_; + Point cursor_position_; + + void SetCursorPosition(Point pos); FML_DISALLOW_COPY_AND_ASSIGN(Playground); }; diff --git a/impeller/playground/playground.mm b/impeller/playground/playground.mm index 3bf2b117dec1f..bc4392acdf3b0 100644 --- a/impeller/playground/playground.mm +++ b/impeller/playground/playground.mm @@ -58,6 +58,18 @@ static void PlaygroundKeyCallback(GLFWwindow* window, return stream.str(); } +Point Playground::GetCursorPosition() const { + return cursor_position_; +} + +ISize Playground::GetWindowSize() const { + return {1024, 768}; +} + +void Playground::SetCursorPosition(Point pos) { + cursor_position_ = pos; +} + bool Playground::OpenPlaygroundHere(Renderer::RenderCallback render_callback) { if (!render_callback) { return true; @@ -79,13 +91,20 @@ static void PlaygroundKeyCallback(GLFWwindow* window, ::glfwWindowHint(GLFW_RESIZABLE, false); auto window_title = GetWindowTitle(flutter::testing::GetCurrentTestName()); - auto window = ::glfwCreateWindow(1024, 768, window_title.c_str(), NULL, NULL); + auto window = + ::glfwCreateWindow(GetWindowSize().width, GetWindowSize().height, + window_title.c_str(), NULL, NULL); if (!window) { return false; } ::glfwSetWindowUserPointer(window, this); ::glfwSetKeyCallback(window, &PlaygroundKeyCallback); + ::glfwSetCursorPosCallback(window, [](GLFWwindow* window, double x, + double y) { + reinterpret_cast(::glfwGetWindowUserPointer(window)) + ->SetCursorPosition({static_cast(x), static_cast(y)}); + }); fml::ScopedCleanupClosure close_window( [window]() { ::glfwDestroyWindow(window); }); diff --git a/impeller/primitives/box.frag b/impeller/primitives/box.frag index a172ceab44243..906bdefb7d8c4 100644 --- a/impeller/primitives/box.frag +++ b/impeller/primitives/box.frag @@ -5,6 +5,7 @@ uniform FrameInfo { float current_time; vec2 cursor_position; + vec2 window_size; } frame; in vec4 color; @@ -18,5 +19,5 @@ uniform sampler2D contents2_texture; void main() { vec4 tex1 = texture(contents1_texture, interporlated_texture_coordinates); vec4 tex2 = texture(contents2_texture, interporlated_texture_coordinates); - frag_color = mix(tex1, tex2, frame.cursor_position.x); + frag_color = mix(tex1, tex2, clamp(frame.cursor_position.x / frame.window_size.x, 0.0, 1.0)); } diff --git a/impeller/primitives/primitives_unittests.mm b/impeller/primitives/primitives_unittests.mm index 2aae7c5c213aa..c34ac02efa6a3 100644 --- a/impeller/primitives/primitives_unittests.mm +++ b/impeller/primitives/primitives_unittests.mm @@ -69,6 +69,10 @@ BoxFragmentShader::FrameInfo frame_info; frame_info.current_time = fml::TimePoint::Now().ToEpochDelta().ToSecondsF(); + frame_info.cursor_position = GetCursorPosition(); + frame_info.window_size.x = GetWindowSize().width; + frame_info.window_size.y = GetWindowSize().height; + cmd.fragment_bindings .buffers[BoxFragmentShader::kUniformFrameInfo.binding] = pass.GetTransientsBuffer().EmplaceUniform(frame_info); From f064dc26d306b3c9b83e5459517a5d931fd5c1c0 Mon Sep 17 00:00:00 2001 From: Chinmay Garde Date: Sat, 10 Jul 2021 17:59:13 -0700 Subject: [PATCH 135/433] Start working on the bind prototypes. --- impeller/compiler/reflector.cc | 51 ++++++++++++++++++++++++++++++++++ impeller/compiler/reflector.h | 22 ++++++++++++++- 2 files changed, 72 insertions(+), 1 deletion(-) diff --git a/impeller/compiler/reflector.cc b/impeller/compiler/reflector.cc index ef451f1b8e732..3492c4ea6e366 100644 --- a/impeller/compiler/reflector.cc +++ b/impeller/compiler/reflector.cc @@ -239,6 +239,8 @@ std::optional Reflector::GenerateTemplateArguments() const { }); } + root["bind_prototype"] = EmitBindPrototypes(shader_resources); + return root; } @@ -656,5 +658,54 @@ std::string Reflector::GetMemberNameAtIndex( return stream.str(); } +std::vector Reflector::ReflectBindPrototypes( + const spirv_cross::ShaderResources& resources) const { + std::vector prototypes; + for (const auto& sampled_image : resources.sampled_images) { + auto& proto = prototypes.emplace_back(BindPrototype{}); + proto.return_type = "void"; + proto.name = + SPrintF("Bind%s", ConvertToCamelCase(sampled_image.name).c_str()); + { + std::stringstream stream; + stream << "Bind combined image sampler for resource named " + << sampled_image.name << "."; + proto.docstring = stream.str(); + } + proto.args.push_back(BindPrototypeArgument{ + .type_name = "Command&", + .argument_name = "command", + }); + proto.args.push_back(BindPrototypeArgument{ + .type_name = "Texture&", + .argument_name = "texture", + }); + proto.args.push_back(BindPrototypeArgument{ + .type_name = "Sampler&", + .argument_name = "sampler", + }); + } + return prototypes; +} + +nlohmann::json::array_t Reflector::EmitBindPrototypes( + const spirv_cross::ShaderResources& resources) const { + const auto prototypes = ReflectBindPrototypes(resources); + nlohmann::json::array_t result; + for (const auto& res : prototypes) { + auto& item = result.emplace_back(nlohmann::json::object_t{}); + item["return_type"] = res.return_type; + item["name"] = res.name; + item["docstring"] = res.docstring; + auto& args = item["args"] = nlohmann::json::array_t{}; + for (const auto& arg : res.args) { + auto& json_arg = args.emplace_back(nlohmann::json::object_t{}); + json_arg["type_name"] = arg.type_name; + json_arg["argument_name"] = arg.argument_name; + } + } + return result; +} + } // namespace compiler } // namespace impeller diff --git a/impeller/compiler/reflector.h b/impeller/compiler/reflector.h index 615423675ee17..ee94d8afae158 100644 --- a/impeller/compiler/reflector.h +++ b/impeller/compiler/reflector.h @@ -51,9 +51,21 @@ class Reflector { std::vector members; }; + struct BindPrototypeArgument { + std::string type_name; + std::string argument_name; + }; + + struct BindPrototype { + std::string name; + std::string return_type; + std::string docstring; + std::vector args; + }; + const Options options_; const std::shared_ptr ir_; - // TODO: There is no reason this needs to the MSL subtype. + // TODO(csg): There is no reason this needs to the MSL subtype. const std::shared_ptr compiler_; std::unique_ptr template_arguments_; std::shared_ptr reflection_header_; @@ -84,6 +96,12 @@ class Reflector { std::optional ReflectStructDefinition( const spirv_cross::TypeID& type_id) const; + std::vector ReflectBindPrototypes( + const spirv_cross::ShaderResources& resources) const; + + nlohmann::json::array_t EmitBindPrototypes( + const spirv_cross::ShaderResources& resources) const; + std::optional ReflectPerVertexStructDefinition( const spirv_cross::SmallVector& stage_inputs) const; @@ -99,6 +117,8 @@ class Reflector { std::vector ReadStructMembers( const spirv_cross::TypeID& type_id) const; + bool IsVertexShader() const; + FML_DISALLOW_COPY_AND_ASSIGN(Reflector); }; From b51d7c07d4964ebfcfd57cfb7274fb009630a0e7 Mon Sep 17 00:00:00 2001 From: Chinmay Garde Date: Sat, 10 Jul 2021 17:59:54 -0700 Subject: [PATCH 136/433] Fix spelling mistake. --- impeller/compositor/formats.h | 4 ++-- impeller/compositor/formats_metal.h | 4 ++-- impeller/primitives/primitives_unittests.mm | 2 +- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/impeller/compositor/formats.h b/impeller/compositor/formats.h index fd90ee07898c0..711d168456576 100644 --- a/impeller/compositor/formats.h +++ b/impeller/compositor/formats.h @@ -60,8 +60,8 @@ enum class StoreAction { }; enum class PrimitiveType { - kTriange, - kTriangeStrip, + kTriangle, + kTriangleStrip, kLine, kLineStrip, kPoint, diff --git a/impeller/compositor/formats_metal.h b/impeller/compositor/formats_metal.h index af38261604b3b..6a4a1addaa758 100644 --- a/impeller/compositor/formats_metal.h +++ b/impeller/compositor/formats_metal.h @@ -73,9 +73,9 @@ constexpr MTLBlendFactor ToMTLBlendFactor(BlendFactor type) { constexpr MTLPrimitiveType ToMTLPrimitiveType(PrimitiveType type) { switch (type) { - case PrimitiveType::kTriange: + case PrimitiveType::kTriangle: return MTLPrimitiveTypeTriangle; - case PrimitiveType::kTriangeStrip: + case PrimitiveType::kTriangleStrip: return MTLPrimitiveTypeTriangleStrip; case PrimitiveType::kLine: return MTLPrimitiveTypeLine; diff --git a/impeller/primitives/primitives_unittests.mm b/impeller/primitives/primitives_unittests.mm index c34ac02efa6a3..fb67740639831 100644 --- a/impeller/primitives/primitives_unittests.mm +++ b/impeller/primitives/primitives_unittests.mm @@ -91,7 +91,7 @@ cmd.index_buffer = vertex_buffer.index_buffer; cmd.index_count = vertex_buffer.index_count; - cmd.primitive_type = PrimitiveType::kTriange; + cmd.primitive_type = PrimitiveType::kTriangle; if (!pass.RecordCommand(std::move(cmd))) { return false; } From 35005664526099e1a42a959947649eedfebcc343 Mon Sep 17 00:00:00 2001 From: Chinmay Garde Date: Sun, 11 Jul 2021 17:49:59 -0700 Subject: [PATCH 137/433] Generate code for simple named binders. --- impeller/compiler/code_gen_template.h | 27 +++++- impeller/compiler/reflector.cc | 68 +++++++++++++-- impeller/compiler/reflector.h | 2 - impeller/compositor/command.h | 32 +++++++- impeller/compositor/command.mm | 91 ++++++++++++++++++++- impeller/compositor/texture_descriptor.h | 3 - impeller/compositor/texture_descriptor.mm | 40 +-------- impeller/playground/playground.mm | 19 +++-- impeller/primitives/primitives_unittests.mm | 36 +++----- impeller/tools/impeller.gni | 3 +- 10 files changed, 231 insertions(+), 90 deletions(-) diff --git a/impeller/compiler/code_gen_template.h b/impeller/compiler/code_gen_template.h index 71347134d5c46..4148ccb068deb 100644 --- a/impeller/compiler/code_gen_template.h +++ b/impeller/compiler/code_gen_template.h @@ -13,7 +13,13 @@ constexpr std::string_view kReflectionHeaderTemplate = #pragma once -#include "shader_types.h" +// Note: The nogncheck decorations are only to make GN not mad at the template. +// There are no GN rule violations in the generated file itself. +#include "impeller/compositor/buffer_view.h" // nogncheck +#include "impeller/compositor/command.h" // nogncheck +#include "impeller/compositor/sampler.h" // nogncheck +#include "impeller/compositor/texture.h" // nogncheck +#include "impeller/shader_glue/shader_types.h" // nogncheck namespace impeller { @@ -43,7 +49,7 @@ struct {{camel_case(shader_name)}}{{camel_case(shader_stage)}}Shader { // =========================================================================== {% for uniform in uniform_buffers %} - static constexpr auto kUniform{{camel_case(uniform.name)}} = ShaderUniformSlot<{{uniform.name}}> { // {{uniform.name}} + static constexpr auto kResource{{camel_case(uniform.name)}} = ShaderUniformSlot<{{uniform.name}}> { // {{uniform.name}} "{{uniform.name}}", // name {{uniform.binding}}u, // binding }; @@ -80,7 +86,7 @@ struct {{camel_case(shader_name)}}{{camel_case(shader_stage)}}Shader { // =========================================================================== {% for sampled_image in sampled_images %} - static constexpr auto kSampledImage{{camel_case(sampled_image.name)}} = SampledImageSlot { // {{sampled_image.name}} + static constexpr auto kResource{{camel_case(sampled_image.name)}} = SampledImageSlot { // {{sampled_image.name}} "{{sampled_image.name}}", // name {{sampled_image.msl_res_0}}u, // texture {{sampled_image.msl_res_1}}u, // sampler @@ -110,6 +116,21 @@ struct {{camel_case(shader_name)}}{{camel_case(shader_stage)}}Shader { }; {% endif %} +{% for proto in bind_prototypes %} + /// {{proto.docstring}} + static {{proto.return_type}} Bind{{proto.name}}({% for arg in proto.args %} +{{arg.type_name}} {{arg.argument_name}}{% if not loop.is_last %}, {% endif %} +{% endfor %}) { + return {{ proto.args.0.argument_name }}.BindResource({% for arg in proto.args %} + {% if loop.is_first %} +{{to_shader_stage(shader_stage)}}, kResource{{ proto.name }}, {% else %} +std::move({{ arg.argument_name }}){% if not loop.is_last %}, {% endif %} + {% endif %} + {% endfor %}); + } + +{% endfor %} + }; // struct {{camel_case(shader_name)}}{{camel_case(shader_stage)}}Shader } // namespace impeller diff --git a/impeller/compiler/reflector.cc b/impeller/compiler/reflector.cc index 3492c4ea6e366..70c443aba942e 100644 --- a/impeller/compiler/reflector.cc +++ b/impeller/compiler/reflector.cc @@ -239,7 +239,7 @@ std::optional Reflector::GenerateTemplateArguments() const { }); } - root["bind_prototype"] = EmitBindPrototypes(shader_resources); + root["bind_prototypes"] = EmitBindPrototypes(shader_resources); return root; } @@ -661,11 +661,29 @@ std::string Reflector::GetMemberNameAtIndex( std::vector Reflector::ReflectBindPrototypes( const spirv_cross::ShaderResources& resources) const { std::vector prototypes; + for (const auto& uniform_buffer : resources.uniform_buffers) { + auto& proto = prototypes.emplace_back(BindPrototype{}); + proto.return_type = "bool"; + proto.name = ConvertToCamelCase(uniform_buffer.name); + { + std::stringstream stream; + stream << "Bind uniform buffer for resource named " << uniform_buffer.name + << "."; + proto.docstring = stream.str(); + } + proto.args.push_back(BindPrototypeArgument{ + .type_name = "Command&", + .argument_name = "command", + }); + proto.args.push_back(BindPrototypeArgument{ + .type_name = "BufferView", + .argument_name = "view", + }); + } for (const auto& sampled_image : resources.sampled_images) { auto& proto = prototypes.emplace_back(BindPrototype{}); - proto.return_type = "void"; - proto.name = - SPrintF("Bind%s", ConvertToCamelCase(sampled_image.name).c_str()); + proto.return_type = "bool"; + proto.name = ConvertToCamelCase(sampled_image.name); { std::stringstream stream; stream << "Bind combined image sampler for resource named " @@ -677,11 +695,49 @@ std::vector Reflector::ReflectBindPrototypes( .argument_name = "command", }); proto.args.push_back(BindPrototypeArgument{ - .type_name = "Texture&", + .type_name = "std::shared_ptr", + .argument_name = "texture", + }); + proto.args.push_back(BindPrototypeArgument{ + .type_name = "std::shared_ptr", + .argument_name = "sampler", + }); + } + for (const auto& separate_image : resources.separate_images) { + auto& proto = prototypes.emplace_back(BindPrototype{}); + proto.return_type = "bool"; + proto.name = ConvertToCamelCase(separate_image.name); + { + std::stringstream stream; + stream << "Bind separate image for resource named " << separate_image.name + << "."; + proto.docstring = stream.str(); + } + proto.args.push_back(BindPrototypeArgument{ + .type_name = "Command&", + .argument_name = "command", + }); + proto.args.push_back(BindPrototypeArgument{ + .type_name = "std::shared_ptr", .argument_name = "texture", }); + } + for (const auto& separate_sampler : resources.separate_samplers) { + auto& proto = prototypes.emplace_back(BindPrototype{}); + proto.return_type = "bool"; + proto.name = ConvertToCamelCase(separate_sampler.name); + { + std::stringstream stream; + stream << "Bind separate sampler for resource named " + << separate_sampler.name << "."; + proto.docstring = stream.str(); + } + proto.args.push_back(BindPrototypeArgument{ + .type_name = "Command&", + .argument_name = "command", + }); proto.args.push_back(BindPrototypeArgument{ - .type_name = "Sampler&", + .type_name = "std::shared_ptr", .argument_name = "sampler", }); } diff --git a/impeller/compiler/reflector.h b/impeller/compiler/reflector.h index ee94d8afae158..b71a84aad5f18 100644 --- a/impeller/compiler/reflector.h +++ b/impeller/compiler/reflector.h @@ -117,8 +117,6 @@ class Reflector { std::vector ReadStructMembers( const spirv_cross::TypeID& type_id) const; - bool IsVertexShader() const; - FML_DISALLOW_COPY_AND_ASSIGN(Reflector); }; diff --git a/impeller/compositor/command.h b/impeller/compositor/command.h index 6c7d2e6d46854..1e0cd4a4a2fcb 100644 --- a/impeller/compositor/command.h +++ b/impeller/compositor/command.h @@ -8,16 +8,18 @@ #include #include +#include "flutter/fml/logging.h" #include "flutter/fml/macros.h" #include "impeller/compositor/buffer_view.h" #include "impeller/compositor/formats.h" #include "impeller/compositor/pipeline.h" +#include "impeller/compositor/sampler.h" +#include "impeller/compositor/texture.h" +#include "impeller/compositor/vertex_buffer.h" +#include "impeller/shader_glue/shader_types.h" namespace impeller { -class Texture; -class Sampler; - struct Bindings { std::map buffers; std::map> textures; @@ -33,6 +35,30 @@ struct Command { std::string label; PrimitiveType primitive_type; + bool BindVertices(const VertexBuffer& buffer); + + template + bool BindResource(ShaderStage stage, + const ShaderUniformSlot slot, + BufferView view) { + return BindResource(stage, slot.binding, std::move(view)); + } + + bool BindResource(ShaderStage stage, size_t binding, BufferView view); + + bool BindResource(ShaderStage stage, + const SampledImageSlot& slot, + std::shared_ptr texture); + + bool BindResource(ShaderStage stage, + const SampledImageSlot& slot, + std::shared_ptr sampler); + + bool BindResource(ShaderStage stage, + const SampledImageSlot& slot, + std::shared_ptr texture, + std::shared_ptr sampler); + constexpr operator bool() const { return pipeline && pipeline->IsValid(); } }; diff --git a/impeller/compositor/command.mm b/impeller/compositor/command.mm index 7170c57f8450f..3e505af69bd68 100644 --- a/impeller/compositor/command.mm +++ b/impeller/compositor/command.mm @@ -6,8 +6,97 @@ #include +#include "impeller/compositor/vertex_descriptor.h" + namespace impeller { -// +bool Command::BindVertices(const VertexBuffer& buffer) { + vertex_bindings.buffers[VertexDescriptor::kReservedVertexBufferIndex] = + buffer.vertex_buffer; + index_buffer = buffer.index_buffer; + index_count = buffer.index_count; + return true; +} + +bool Command::BindResource(ShaderStage stage, size_t binding, BufferView view) { + if (!view) { + return false; + } + + switch (stage) { + case ShaderStage::kVertex: + vertex_bindings.buffers[binding] = view; + return true; + case ShaderStage::kFragment: + fragment_bindings.buffers[binding] = view; + return true; + case ShaderStage::kUnknown: + return false; + } + + return false; +} + +bool Command::BindResource(ShaderStage stage, + const SampledImageSlot& slot, + std::shared_ptr texture) { + if (!texture || !texture->IsValid()) { + return false; + } + + if (!slot.HasTexture()) { + FML_DLOG(WARNING) << "Texture named " << slot.name + << " is not actually used in the shader."; + return true; + } + + switch (stage) { + case ShaderStage::kVertex: + vertex_bindings.textures[slot.texture_index] = texture; + return true; + case ShaderStage::kFragment: + fragment_bindings.textures[slot.texture_index] = texture; + return true; + case ShaderStage::kUnknown: + return false; + } + + return false; +} + +bool Command::BindResource(ShaderStage stage, + const SampledImageSlot& slot, + std::shared_ptr sampler) { + if (!sampler || !sampler->IsValid()) { + return false; + } + + if (!slot.HasSampler()) { + FML_DLOG(WARNING) << "Sampler named " << slot.name + << " is not actually used in the shader."; + return true; + } + + switch (stage) { + case ShaderStage::kVertex: + vertex_bindings.samplers[slot.sampler_index] = sampler; + return true; + case ShaderStage::kFragment: + fragment_bindings.samplers[slot.sampler_index] = sampler; + return true; + case ShaderStage::kUnknown: + return false; + } + + return false; +} + +bool Command::BindResource(ShaderStage stage, + const SampledImageSlot& slot, + std::shared_ptr texture, + std::shared_ptr sampler) { + return BindResource(stage, slot, texture) && + BindResource(stage, slot, sampler); +} } // namespace impeller diff --git a/impeller/compositor/texture_descriptor.h b/impeller/compositor/texture_descriptor.h index 53b8f316a712f..07e52ca645bdd 100644 --- a/impeller/compositor/texture_descriptor.h +++ b/impeller/compositor/texture_descriptor.h @@ -37,9 +37,6 @@ struct TextureDescriptor { mip_count >= 1u // ; } - - static std::optional MakeFromImageResult( - const Image& result); }; } // namespace impeller diff --git a/impeller/compositor/texture_descriptor.mm b/impeller/compositor/texture_descriptor.mm index 1c092b2902b03..c72e4dfd022c4 100644 --- a/impeller/compositor/texture_descriptor.mm +++ b/impeller/compositor/texture_descriptor.mm @@ -6,44 +6,6 @@ namespace impeller { -std::optional FormatForImageResultComponents(Image::Format comp) { - switch (comp) { - case Image::Format::Invalid: - return std::nullopt; - case Image::Format::Grey: - return std::nullopt; - case Image::Format::GreyAlpha: - return std::nullopt; - case Image::Format::RGB: - return std::nullopt; - case Image::Format::RGBA: - return PixelFormat::kPixelFormat_R8G8B8A8_UNormInt; - } - return std::nullopt; -} - -std::optional TextureDescriptor::MakeFromImageResult( - const Image& result) { - if (!result.IsValid()) { - return std::nullopt; - } - - const auto pixel_format = FormatForImageResultComponents(result.GetFormat()); - if (!pixel_format.has_value()) { - FML_DLOG(ERROR) << "Unknown image format."; - return std::nullopt; - } - - TextureDescriptor desc; - desc.format = pixel_format.value(); - desc.size = result.GetSize(); - desc.mip_count = result.GetSize().MipCount(); - - if (!desc.IsValid()) { - return std::nullopt; - } - - return desc; -} +// } // namespace impeller diff --git a/impeller/playground/playground.mm b/impeller/playground/playground.mm index bc4392acdf3b0..fabe1b714254c 100644 --- a/impeller/playground/playground.mm +++ b/impeller/playground/playground.mm @@ -169,20 +169,25 @@ static void PlaygroundKeyCallback(GLFWwindow* window, const char* fixture_name) const { CompressedImage compressed_image( flutter::testing::OpenFixtureAsMapping(fixture_name)); + // The decoded image is immediately converted into RGBA as that format is + // known to be supported everywhere. For image sources that don't need 32 bit + // pixel strides, this is overkill. Since this is a test fixture we aren't + // necessarily trying to eke out memory savings here and instead favor + // simplicity. auto image = compressed_image.Decode().ConvertToRGBA(); if (!image.IsValid()) { FML_LOG(ERROR) << "Could not find fixture named " << fixture_name; return nullptr; } - auto texture_descriptor = TextureDescriptor::MakeFromImageResult(image); - if (!texture_descriptor.has_value()) { - FML_LOG(ERROR) << "Could not figure out texture descriptor for fixture " - << fixture_name; - return nullptr; - } + + auto texture_descriptor = TextureDescriptor{}; + texture_descriptor.format = PixelFormat::kPixelFormat_R8G8B8A8_UNormInt; + texture_descriptor.size = image.GetSize(); + texture_descriptor.mip_count = 1u; + auto texture = renderer_.GetContext()->GetPermanentsAllocator()->CreateTexture( - StorageMode::kHostVisible, texture_descriptor.value()); + StorageMode::kHostVisible, texture_descriptor); if (!texture) { FML_LOG(ERROR) << "Could not allocate texture for fixture " << fixture_name; return nullptr; diff --git a/impeller/primitives/primitives_unittests.mm b/impeller/primitives/primitives_unittests.mm index fb67740639831..fae9e3a754452 100644 --- a/impeller/primitives/primitives_unittests.mm +++ b/impeller/primitives/primitives_unittests.mm @@ -54,18 +54,17 @@ ASSERT_TRUE(sampler); Renderer::RenderCallback callback = [&](const Surface& surface, RenderPass& pass) { - BoxVertexShader::UniformBuffer uniforms; - uniforms.mvp = Matrix::MakeOrthographic(surface.GetSize().width, - surface.GetSize().height); Command cmd; cmd.label = "Box"; cmd.pipeline = box_pipeline; - cmd.vertex_bindings.buffers[VertexDescriptor::kReservedVertexBufferIndex] = - vertex_buffer.vertex_buffer; - cmd.vertex_bindings - .buffers[BoxVertexShader::kUniformUniformBuffer.binding] = - pass.GetTransientsBuffer().EmplaceUniform(uniforms); + cmd.BindVertices(vertex_buffer); + + BoxVertexShader::UniformBuffer uniforms; + uniforms.mvp = Matrix::MakeOrthographic(surface.GetSize().width, + surface.GetSize().height); + BoxVertexShader::BindUniformBuffer( + cmd, pass.GetTransientsBuffer().EmplaceUniform(uniforms)); BoxFragmentShader::FrameInfo frame_info; frame_info.current_time = fml::TimePoint::Now().ToEpochDelta().ToSecondsF(); @@ -73,24 +72,11 @@ frame_info.window_size.x = GetWindowSize().width; frame_info.window_size.y = GetWindowSize().height; - cmd.fragment_bindings - .buffers[BoxFragmentShader::kUniformFrameInfo.binding] = - pass.GetTransientsBuffer().EmplaceUniform(frame_info); - cmd.fragment_bindings - .textures[BoxFragmentShader::kSampledImageContents1Texture - .texture_index] = bridge; - cmd.fragment_bindings - .samplers[BoxFragmentShader::kSampledImageContents1Texture - .sampler_index] = sampler; - cmd.fragment_bindings - .textures[BoxFragmentShader::kSampledImageContents2Texture - .texture_index] = boston; - cmd.fragment_bindings - .samplers[BoxFragmentShader::kSampledImageContents2Texture - .sampler_index] = sampler; + BoxFragmentShader::BindFrameInfo( + cmd, pass.GetTransientsBuffer().EmplaceUniform(frame_info)); + BoxFragmentShader::BindContents1Texture(cmd, boston, sampler); + BoxFragmentShader::BindContents2Texture(cmd, bridge, sampler); - cmd.index_buffer = vertex_buffer.index_buffer; - cmd.index_count = vertex_buffer.index_count; cmd.primitive_type = PrimitiveType::kTriangle; if (!pass.RecordCommand(std::move(cmd))) { return false; diff --git a/impeller/tools/impeller.gni b/impeller/tools/impeller.gni index 44f3a7ee6e3ef..9d7b1e3ec1ddf 100644 --- a/impeller/tools/impeller.gni +++ b/impeller/tools/impeller.gni @@ -98,7 +98,7 @@ template("impeller_shaders") { spirv_intermediate = "$target_gen_dir/{{source_file_part}}.spirv" reflection_json_intermediate = "$target_gen_dir/{{source_file_part}}.json" reflection_header_intermediate = "$target_gen_dir/{{source_file_part}}.h" - reflection_cc_intermediate = "$target_gen_dir/{{source_file_part}}.cc" + reflection_cc_intermediate = "$target_gen_dir/{{source_file_part}}.mm" outputs = [ metal_intermediate, @@ -164,6 +164,7 @@ template("impeller_shaders") { [ "*.h", "*.cc", + "*.mm", ]) deps = [ From 7e33eec30eee3a6a7024bbc1d0903731793fb9e7 Mon Sep 17 00:00:00 2001 From: Chinmay Garde Date: Sun, 11 Jul 2021 18:06:28 -0700 Subject: [PATCH 138/433] Zero initialize command primitive type --- impeller/compositor/command.h | 2 +- impeller/playground/playground.mm | 1 + 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/impeller/compositor/command.h b/impeller/compositor/command.h index 1e0cd4a4a2fcb..f9b3b4f1ffbfd 100644 --- a/impeller/compositor/command.h +++ b/impeller/compositor/command.h @@ -33,7 +33,7 @@ struct Command { BufferView index_buffer; size_t index_count = 0u; std::string label; - PrimitiveType primitive_type; + PrimitiveType primitive_type = PrimitiveType::kTriangle; bool BindVertices(const VertexBuffer& buffer); diff --git a/impeller/playground/playground.mm b/impeller/playground/playground.mm index fabe1b714254c..8b836e239850d 100644 --- a/impeller/playground/playground.mm +++ b/impeller/playground/playground.mm @@ -193,6 +193,7 @@ CompressedImage compressed_image( return nullptr; } texture->SetLabel(fixture_name); + auto uploaded = texture->SetContents(image.GetAllocation()->GetMapping(), image.GetAllocation()->GetSize()); if (!uploaded) { From 6f39cbd2a616dc3d42884b248d70c023390fc200 Mon Sep 17 00:00:00 2001 From: Chinmay Garde Date: Mon, 12 Jul 2021 15:10:56 -0700 Subject: [PATCH 139/433] Port Geometry tests. --- impeller/geometry/BUILD.gn | 5 +- impeller/geometry/geometry_unittests.cc | 219 ++++++++++++++++++++ impeller/geometry/geometry_unittests.h | 123 +++++++++++ impeller/primitives/box.frag | 9 +- impeller/primitives/box.vert | 3 - impeller/primitives/primitives_unittests.mm | 17 +- 6 files changed, 358 insertions(+), 18 deletions(-) create mode 100644 impeller/geometry/geometry_unittests.h diff --git a/impeller/geometry/BUILD.gn b/impeller/geometry/BUILD.gn index 2d6d5a22a5466..f57a99bfe6ad2 100644 --- a/impeller/geometry/BUILD.gn +++ b/impeller/geometry/BUILD.gn @@ -34,7 +34,10 @@ impeller_component("geometry") { impeller_component("geometry_unittests") { testonly = true - sources = [ "geometry_unittests.cc" ] + sources = [ + "geometry_unittests.cc", + "geometry_unittests.h", + ] deps = [ ":geometry", "//flutter/testing", diff --git a/impeller/geometry/geometry_unittests.cc b/impeller/geometry/geometry_unittests.cc index 414c49622e5bb..31694ed0065f6 100644 --- a/impeller/geometry/geometry_unittests.cc +++ b/impeller/geometry/geometry_unittests.cc @@ -2,7 +2,10 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. +#include "impeller/geometry/geometry_unittests.h" #include "flutter/testing/testing.h" +#include "impeller/geometry/path.h" +#include "impeller/geometry/path_builder.h" #include "impeller/geometry/point.h" #include "impeller/geometry/rect.h" #include "impeller/geometry/size.h" @@ -10,6 +13,222 @@ namespace impeller { namespace testing { +TEST(GeometryTest, RotationMatrix) { + auto rotation = Matrix::MakeRotationZ(M_PI_4); + auto expect = Matrix{0.707, 0.707, 0, 0, // + -0.707, 0.707, 0, 0, // + 0, 0, 1, 0, // + 0, 0, 0, 1}; + ASSERT_MATRIX_NEAR(rotation, expect); +} + +TEST(GeometryTest, InvertMultMatrix) { + auto rotation = Matrix::MakeRotationZ(M_PI_4); + auto invert = rotation.Invert(); + auto expect = Matrix{0.707, -0.707, 0, 0, // + 0.707, 0.707, 0, 0, // + 0, 0, 1, 0, // + 0, 0, 0, 1}; + ASSERT_MATRIX_NEAR(invert, expect); +} + +TEST(GeometryTest, MutliplicationMatrix) { + auto rotation = Matrix::MakeRotationZ(M_PI_4); + auto invert = rotation.Invert(); + ASSERT_MATRIX_NEAR(rotation * invert, Matrix{}); +} + +TEST(GeometryTest, DeterminantTest) { + auto matrix = Matrix{3, 4, 14, 155, 2, 1, 3, 4, 2, 3, 2, 1, 1, 2, 4, 2}; + ASSERT_EQ(matrix.GetDeterminant(), -1889); +} + +TEST(GeometryTest, InvertMatrix) { + auto inverted = Matrix{10, -9, -12, 8, // + 7, -12, 11, 22, // + -10, 10, 3, 6, // + -2, 22, 2, 1} + .Invert(); + + auto result = Matrix{ + 438.0 / 85123.0, 1751.0 / 85123.0, -7783.0 / 85123.0, 4672.0 / 85123.0, + 393.0 / 85123.0, -178.0 / 85123.0, -570.0 / 85123.0, 4192 / 85123.0, + -5230.0 / 85123.0, 2802.0 / 85123.0, -3461.0 / 85123.0, 962.0 / 85123.0, + 2690.0 / 85123.0, 1814.0 / 85123.0, 3896.0 / 85123.0, 319.0 / 85123.0}; + + ASSERT_MATRIX_NEAR(inverted, result); +} + +TEST(GeometryTest, TestDecomposition) { + auto rotated = Matrix::MakeRotationZ(M_PI_4); + + auto result = rotated.Decompose(); + + ASSERT_TRUE(result.first); + + Matrix::Decomposition res = result.second; + + auto quaternion = Quaternion{{0.0, 0.0, 1.0}, M_PI_4}; + ASSERT_QUATERNION_NEAR(res.rotation, quaternion); +} + +TEST(GeometryTest, TestDecomposition2) { + auto rotated = Matrix::MakeRotationZ(M_PI_4); + auto scaled = Matrix::MakeScale({2.0, 3.0, 1.0}); + auto translated = Matrix::MakeTranslation({-200, 750, 20}); + + auto result = (translated * rotated * scaled).Decompose(); + + ASSERT_TRUE(result.first); + + Matrix::Decomposition res = result.second; + + auto quaternion = Quaternion{{0.0, 0.0, 1.0}, M_PI_4}; + + ASSERT_QUATERNION_NEAR(res.rotation, quaternion); + + ASSERT_FLOAT_EQ(res.translation.x, -200); + ASSERT_FLOAT_EQ(res.translation.y, 750); + ASSERT_FLOAT_EQ(res.translation.z, 20); + + ASSERT_FLOAT_EQ(res.scale.x, 2); + ASSERT_FLOAT_EQ(res.scale.y, 3); + ASSERT_FLOAT_EQ(res.scale.z, 1); +} + +TEST(GeometryTest, TestRecomposition) { + /* + * Decomposition. + */ + auto rotated = Matrix::MakeRotationZ(M_PI_4); + + auto result = rotated.Decompose(); + + ASSERT_TRUE(result.first); + + Matrix::Decomposition res = result.second; + + auto quaternion = Quaternion{{0.0, 0.0, 1.0}, M_PI_4}; + + ASSERT_QUATERNION_NEAR(res.rotation, quaternion); + + /* + * Recomposition. + */ + ASSERT_MATRIX_NEAR(rotated, Matrix{res}); +} + +TEST(GeometryTest, TestRecomposition2) { + auto matrix = Matrix::MakeTranslation({100, 100, 100}) * + Matrix::MakeRotationZ(M_PI_4) * + Matrix::MakeScale({2.0, 2.0, 2.0}); + + auto result = matrix.Decompose(); + + ASSERT_TRUE(result.first); + + ASSERT_MATRIX_NEAR(matrix, Matrix{result.second}); +} + +TEST(GeometryTest, QuaternionLerp) { + auto q1 = Quaternion{{0.0, 0.0, 1.0}, 0.0}; + auto q2 = Quaternion{{0.0, 0.0, 1.0}, M_PI_4}; + + auto q3 = q1.Slerp(q2, 0.5); + + auto expected = Quaternion{{0.0, 0.0, 1.0}, M_PI_4 / 2.0}; + + ASSERT_QUATERNION_NEAR(q3, expected); +} + +TEST(GeometryTest, RectWithPoint) { + auto rect = Rect{}; + + auto expected = Rect{}; + + ASSERT_RECT_NEAR(rect, expected); + + rect = rect.WithPoint({100, 100}); + expected = Rect{0, 0, 100, 100}; + ASSERT_RECT_NEAR(rect, expected); + + rect = rect.WithPoint({-11, -12}); + expected = Rect{-11, -12, 111, 112}; + ASSERT_RECT_NEAR(rect, expected); + + rect = rect.WithPoint({55, 65}); + expected = Rect{-11, -12, 111, 112}; + ASSERT_RECT_NEAR(rect, expected); + + rect = rect.WithPoint({-25, 0}); + expected = Rect{-25, -12, 125, 112}; + ASSERT_RECT_NEAR(rect, expected); + + rect = rect.WithPoint({0, -25}); + expected = Rect{-25, -25, 125, 125}; + ASSERT_RECT_NEAR(rect, expected); + + rect = rect.WithPoint({125, 135}); + expected = Rect{-25, -25, 150, 160}; + ASSERT_RECT_NEAR(rect, expected); +} + +TEST(GeometryTest, SimplePath) { + Path path; + + path.AddLinearComponent({0, 0}, {100, 100}) + .AddQuadraticComponent({100, 100}, {200, 200}, {300, 300}) + .AddCubicComponent({300, 300}, {400, 400}, {500, 500}, {600, 600}); + + ASSERT_EQ(path.GetComponentCount(), 3u); + + path.EnumerateComponents( + [](size_t index, const LinearPathComponent& linear) { + Point p1(0, 0); + Point p2(100, 100); + ASSERT_EQ(index, 0u); + ASSERT_EQ(linear.p1, p1); + ASSERT_EQ(linear.p2, p2); + }, + [](size_t index, const QuadraticPathComponent& quad) { + Point p1(100, 100); + Point cp(200, 200); + Point p2(300, 300); + ASSERT_EQ(index, 1u); + ASSERT_EQ(quad.p1, p1); + ASSERT_EQ(quad.cp, cp); + ASSERT_EQ(quad.p2, p2); + }, + [](size_t index, const CubicPathComponent& cubic) { + Point p1(300, 300); + Point cp1(400, 400); + Point cp2(500, 500); + Point p2(600, 600); + ASSERT_EQ(index, 2u); + ASSERT_EQ(cubic.p1, p1); + ASSERT_EQ(cubic.cp1, cp1); + ASSERT_EQ(cubic.cp2, cp2); + ASSERT_EQ(cubic.p2, p2); + }); +} + +TEST(GeometryTest, BoundingBoxCubic) { + Path path; + path.AddCubicComponent({120, 160}, {25, 200}, {220, 260}, {220, 40}); + auto box = path.GetBoundingBox(); + Rect expected(0, 0, 220, 198.862); + ASSERT_RECT_NEAR(box, expected); +} + +TEST(GeometryTest, BoundingBoxOfCompositePathIsCorrect) { + PathBuilder builder; + builder.AddRoundedRect({{10, 10}, {300, 300}}, {50, 50, 50, 50}); + auto path = builder.CreatePath(); + auto actual = path.GetBoundingBox(); + Rect expected(0, 0, 310, 310); + ASSERT_RECT_NEAR(actual, expected); +} + TEST(GeometryTest, CanGenerateMipCounts) { ASSERT_EQ((Size{128, 128}.MipCount()), 7u); ASSERT_EQ((Size{128, 256}.MipCount()), 8u); diff --git a/impeller/geometry/geometry_unittests.h b/impeller/geometry/geometry_unittests.h new file mode 100644 index 0000000000000..e247facd523e3 --- /dev/null +++ b/impeller/geometry/geometry_unittests.h @@ -0,0 +1,123 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include + +#include "gtest/gtest.h" +#include "impeller/geometry/matrix.h" +#include "impeller/geometry/point.h" +#include "impeller/geometry/rect.h" +#include "impeller/geometry/size.h" +#include "impeller/geometry/vector.h" + +namespace std { + +inline std::ostream& operator<<(std::ostream& out, const impeller::Matrix& m) { + out << "("; + for (size_t i = 0; i < 4u; i++) { + for (size_t j = 0; j < 4u; j++) { + out << m.e[i][j] << ","; + } + out << std::endl; + } + out << ")"; + return out; +} + +inline std::ostream& operator<<(std::ostream& out, + const impeller::Quaternion& q) { + out << "(" << q.x << ", " << q.y << ", " << q.z << ", " << q.w << ")"; + return out; +} + +template +inline std::ostream& operator<<(std::ostream& out, + const impeller::TSize& s) { + out << "(" << s.width << ", " << s.height << ")"; + return out; +} + +template +inline std::ostream& operator<<(std::ostream& out, + const impeller::TPoint& p) { + out << "(" << p.x << ", " << p.y << ")"; + return out; +} + +template +inline std::ostream& operator<<(std::ostream& out, + const impeller::TRect& r) { + out << "(" << r.origin << ", " << r.size << ")"; + return out; +} + +} // namespace std + +inline bool NumberNear(double a, double b) { + static const double epsilon = 1e-3; + return (a > (b - epsilon)) && (a < (b + epsilon)); +} + +inline ::testing::AssertionResult MatrixNear(impeller::Matrix a, + impeller::Matrix b) { + auto equal = NumberNear(a.m[0], b.m[0]) // + && NumberNear(a.m[1], b.m[1]) // + && NumberNear(a.m[2], b.m[2]) // + && NumberNear(a.m[3], b.m[3]) // + && NumberNear(a.m[4], b.m[4]) // + && NumberNear(a.m[5], b.m[5]) // + && NumberNear(a.m[6], b.m[6]) // + && NumberNear(a.m[7], b.m[7]) // + && NumberNear(a.m[8], b.m[8]) // + && NumberNear(a.m[9], b.m[9]) // + && NumberNear(a.m[10], b.m[10]) // + && NumberNear(a.m[11], b.m[11]) // + && NumberNear(a.m[12], b.m[12]) // + && NumberNear(a.m[13], b.m[13]) // + && NumberNear(a.m[14], b.m[14]) // + && NumberNear(a.m[15], b.m[15]); + + return equal ? ::testing::AssertionSuccess() + : ::testing::AssertionFailure() << "Matrixes are not equal."; +} + +inline ::testing::AssertionResult QuaternionNear(impeller::Quaternion a, + impeller::Quaternion b) { + auto equal = NumberNear(a.x, b.x) && NumberNear(a.y, b.y) && + NumberNear(a.z, b.z) && NumberNear(a.w, b.w); + + return equal ? ::testing::AssertionSuccess() + : ::testing::AssertionFailure() << "Quaternions are not equal."; +} + +inline ::testing::AssertionResult RectNear(impeller::Rect a, impeller::Rect b) { + auto equal = NumberNear(a.origin.x, b.origin.x) && + NumberNear(a.origin.y, b.origin.y) && + NumberNear(a.size.width, b.size.width) && + NumberNear(a.size.height, b.size.height); + + return equal ? ::testing::AssertionSuccess() + : ::testing::AssertionFailure() << "Rects are not equal."; +} + +inline ::testing::AssertionResult PointNear(impeller::Point a, + impeller::Point b) { + auto equal = NumberNear(a.x, b.x) && NumberNear(a.y, b.y); + + return equal ? ::testing::AssertionSuccess() + : ::testing::AssertionFailure() << "Points are not equal."; +} + +inline ::testing::AssertionResult SizeNear(impeller::Size a, impeller::Size b) { + auto equal = NumberNear(a.width, b.width) && NumberNear(a.height, b.height); + + return equal ? ::testing::AssertionSuccess() + : ::testing::AssertionFailure() << "Sizes are not equal."; +} + +#define ASSERT_MATRIX_NEAR(a, b) ASSERT_PRED2(&::MatrixNear, a, b) +#define ASSERT_QUATERNION_NEAR(a, b) ASSERT_PRED2(&::QuaternionNear, a, b) +#define ASSERT_RECT_NEAR(a, b) ASSERT_PRED2(&::RectNear, a, b) +#define ASSERT_POINT_NEAR(a, b) ASSERT_PRED2(&::PointNear, a, b) +#define ASSERT_SIZE_NEAR(a, b) ASSERT_PRED2(&::SizeNear, a, b) diff --git a/impeller/primitives/box.frag b/impeller/primitives/box.frag index 906bdefb7d8c4..47a184644de90 100644 --- a/impeller/primitives/box.frag +++ b/impeller/primitives/box.frag @@ -8,16 +8,15 @@ uniform FrameInfo { vec2 window_size; } frame; -in vec4 color; in vec2 interporlated_texture_coordinates; out vec4 frag_color; -uniform sampler2D contents1_texture; -uniform sampler2D contents2_texture; +uniform sampler2D contents1; +uniform sampler2D contents2; void main() { - vec4 tex1 = texture(contents1_texture, interporlated_texture_coordinates); - vec4 tex2 = texture(contents2_texture, interporlated_texture_coordinates); + vec4 tex1 = texture(contents1, interporlated_texture_coordinates); + vec4 tex2 = texture(contents2, interporlated_texture_coordinates); frag_color = mix(tex1, tex2, clamp(frame.cursor_position.x / frame.window_size.x, 0.0, 1.0)); } diff --git a/impeller/primitives/box.vert b/impeller/primitives/box.vert index 87d0e537e756c..742d1e34c8a6b 100644 --- a/impeller/primitives/box.vert +++ b/impeller/primitives/box.vert @@ -7,14 +7,11 @@ uniform UniformBuffer { } uniforms; in vec3 vertex_position; -in vec4 vertex_color; in vec2 texture_coordinates; -out vec4 color; out vec2 interporlated_texture_coordinates; void main() { gl_Position = uniforms.mvp * vec4(vertex_position, 1.0); - color = vertex_color; interporlated_texture_coordinates = texture_coordinates; } diff --git a/impeller/primitives/primitives_unittests.mm b/impeller/primitives/primitives_unittests.mm index fae9e3a754452..8748a95d32359 100644 --- a/impeller/primitives/primitives_unittests.mm +++ b/impeller/primitives/primitives_unittests.mm @@ -35,13 +35,12 @@ VertexBufferBuilder vertex_builder; vertex_builder.SetLabel("Box"); vertex_builder.AddVertices({ - {{100, 100, 0.0}, {Color::Red()}, {0.0, 0.0}}, // 1 - {{800, 100, 0.0}, {Color::Green()}, {1.0, 0.0}}, // 2 - {{800, 800, 0.0}, {Color::Blue()}, {1.0, 1.0}}, // 3 - - {{100, 100, 0.0}, {Color::Cyan()}, {0.0, 0.0}}, // 1 - {{800, 800, 0.0}, {Color::White()}, {1.0, 1.0}}, // 3 - {{100, 800, 0.0}, {Color::Purple()}, {0.0, 1.0}}, // 4 + {{100, 100, 0.0}, {0.0, 0.0}}, // 1 + {{800, 100, 0.0}, {1.0, 0.0}}, // 2 + {{800, 800, 0.0}, {1.0, 1.0}}, // 3 + {{100, 100, 0.0}, {0.0, 0.0}}, // 1 + {{800, 800, 0.0}, {1.0, 1.0}}, // 3 + {{100, 800, 0.0}, {0.0, 1.0}}, // 4 }); auto vertex_buffer = vertex_builder.CreateVertexBuffer(*context->GetPermanentsAllocator()); @@ -74,8 +73,8 @@ BoxFragmentShader::BindFrameInfo( cmd, pass.GetTransientsBuffer().EmplaceUniform(frame_info)); - BoxFragmentShader::BindContents1Texture(cmd, boston, sampler); - BoxFragmentShader::BindContents2Texture(cmd, bridge, sampler); + BoxFragmentShader::BindContents1(cmd, boston, sampler); + BoxFragmentShader::BindContents2(cmd, bridge, sampler); cmd.primitive_type = PrimitiveType::kTriangle; if (!pass.RecordCommand(std::move(cmd))) { From 218cae7da18dea639cac3be35d584460d903125b Mon Sep 17 00:00:00 2001 From: Chinmay Garde Date: Thu, 15 Jul 2021 12:31:31 -0700 Subject: [PATCH 140/433] Wire up texture usage. --- impeller/compositor/BUILD.gn | 2 +- impeller/compositor/formats.h | 9 +++++++++ impeller/compositor/formats_metal.mm | 13 +++++++++++++ impeller/compositor/texture_descriptor.h | 2 ++ impeller/playground/BUILD.gn | 2 +- 5 files changed, 26 insertions(+), 2 deletions(-) diff --git a/impeller/compositor/BUILD.gn b/impeller/compositor/BUILD.gn index 1b15d184f31b3..0a9c708e14d7f 100644 --- a/impeller/compositor/BUILD.gn +++ b/impeller/compositor/BUILD.gn @@ -73,7 +73,7 @@ impeller_component("compositor") { "../shader_glue", ] - libs = [ "Metal.framework" ] + frameworks = [ "Metal.framework" ] } source_set("compositor_unittests") { diff --git a/impeller/compositor/formats.h b/impeller/compositor/formats.h index 711d168456576..6de3d173be780 100644 --- a/impeller/compositor/formats.h +++ b/impeller/compositor/formats.h @@ -59,6 +59,15 @@ enum class StoreAction { kStore, }; +using TextureUsageMask = uint64_t; + +enum class TextureUsage : TextureUsageMask { + kUnknown, + kShaderRead, + kShaderWrite, + kRenderTarget, +}; + enum class PrimitiveType { kTriangle, kTriangleStrip, diff --git a/impeller/compositor/formats_metal.mm b/impeller/compositor/formats_metal.mm index cef3c06ba679c..d0bffb5b79f61 100644 --- a/impeller/compositor/formats_metal.mm +++ b/impeller/compositor/formats_metal.mm @@ -83,6 +83,19 @@ mtl_desc.width = desc.size.width; mtl_desc.height = desc.size.height; mtl_desc.mipmapLevelCount = desc.mip_count; + mtl_desc.usage = MTLTextureUsageUnknown; + if (desc.usage & static_cast(TextureUsage::kUnknown)) { + mtl_desc.usage |= MTLTextureUsageUnknown; + } + if (desc.usage & static_cast(TextureUsage::kShaderRead)) { + mtl_desc.usage |= MTLTextureUsageShaderRead; + } + if (desc.usage & static_cast(TextureUsage::kShaderWrite)) { + mtl_desc.usage |= MTLTextureUsageShaderWrite; + } + if (desc.usage & static_cast(TextureUsage::kRenderTarget)) { + mtl_desc.usage |= MTLTextureUsageRenderTarget; + } return mtl_desc; } diff --git a/impeller/compositor/texture_descriptor.h b/impeller/compositor/texture_descriptor.h index 07e52ca645bdd..fc647cbbb93c2 100644 --- a/impeller/compositor/texture_descriptor.h +++ b/impeller/compositor/texture_descriptor.h @@ -16,6 +16,8 @@ struct TextureDescriptor { PixelFormat format = PixelFormat::kUnknown; ISize size; size_t mip_count = 1u; // Size::MipCount is usually appropriate. + TextureUsageMask usage = + static_cast(TextureUsage::kShaderRead); constexpr size_t GetSizeOfBaseMipLevel() const { if (!IsValid()) { diff --git a/impeller/playground/BUILD.gn b/impeller/playground/BUILD.gn index 0a912bef5d8a2..5c142a125c8ae 100644 --- a/impeller/playground/BUILD.gn +++ b/impeller/playground/BUILD.gn @@ -20,7 +20,7 @@ impeller_component("playground") { ] if (is_mac) { - libs = [ + frameworks = [ "AppKit.framework", "QuartzCore.framework", ] From 0df88cec24c094e45b04ca19f662bd26380a97c5 Mon Sep 17 00:00:00 2001 From: Chinmay Garde Date: Fri, 16 Jul 2021 12:08:44 -0700 Subject: [PATCH 141/433] Add test to check rendering to multiple primitives. --- impeller/geometry/matrix.h | 7 +- impeller/primitives/primitives_unittests.mm | 85 ++++++++++++++++++++- 2 files changed, 87 insertions(+), 5 deletions(-) diff --git a/impeller/geometry/matrix.h b/impeller/geometry/matrix.h index e90cdb594e6ba..7be4168497db8 100644 --- a/impeller/geometry/matrix.h +++ b/impeller/geometry/matrix.h @@ -269,9 +269,12 @@ struct Matrix { Matrix operator+(const Matrix& m) const; - static constexpr Matrix MakeOrthographic(Scalar width, Scalar height) { + template + static constexpr Matrix MakeOrthographic(TSize size) { // Per assumptions about NDC documented above. - const auto scale = MakeScale({1.0f / width, -1.0f / height, 1.0}); + const auto scale = + MakeScale({1.0f / static_cast(size.width), + -1.0f / static_cast(size.height), 1.0}); const auto translate = MakeTranslation({-1.0, 1.0, 0.5}); return translate * scale; } diff --git a/impeller/primitives/primitives_unittests.mm b/impeller/primitives/primitives_unittests.mm index 8748a95d32359..3c2d7eafa63ea 100644 --- a/impeller/primitives/primitives_unittests.mm +++ b/impeller/primitives/primitives_unittests.mm @@ -60,8 +60,7 @@ cmd.BindVertices(vertex_buffer); BoxVertexShader::UniformBuffer uniforms; - uniforms.mvp = Matrix::MakeOrthographic(surface.GetSize().width, - surface.GetSize().height); + uniforms.mvp = Matrix::MakeOrthographic(surface.GetSize()); BoxVertexShader::BindUniformBuffer( cmd, pass.GetTransientsBuffer().EmplaceUniform(uniforms)); @@ -82,7 +81,87 @@ } return true; }; - OpenPlaygroundHere(callback); + // OpenPlaygroundHere(callback); +} + +TEST_F(PrimitivesTest, CanRenderMultiplePrimitives) { + auto context = GetContext(); + ASSERT_TRUE(context); + using BoxPipelineBuilder = + PipelineBuilder; + auto desc = BoxPipelineBuilder::MakeDefaultPipelineDescriptor(*context); + auto box_pipeline = + context->GetPipelineLibrary()->GetRenderPipeline(std::move(desc)).get(); + ASSERT_TRUE(box_pipeline); + + // Vertex buffer. + VertexBufferBuilder vertex_builder; + vertex_builder.SetLabel("Box"); + vertex_builder.AddVertices({ + {{100, 100, 0.0}, {0.0, 0.0}}, // 1 + {{800, 100, 0.0}, {1.0, 0.0}}, // 2 + {{800, 800, 0.0}, {1.0, 1.0}}, // 3 + {{100, 100, 0.0}, {0.0, 0.0}}, // 1 + {{800, 800, 0.0}, {1.0, 1.0}}, // 3 + {{100, 800, 0.0}, {0.0, 1.0}}, // 4 + }); + auto vertex_buffer = + vertex_builder.CreateVertexBuffer(*context->GetPermanentsAllocator()); + ASSERT_TRUE(vertex_buffer); + + auto bridge = CreateTextureForFixture("bay_bridge.jpg"); + auto boston = CreateTextureForFixture("boston.jpg"); + ASSERT_TRUE(bridge && boston); + auto sampler = context->GetSamplerLibrary()->GetSampler({}); + ASSERT_TRUE(sampler); + + Renderer::RenderCallback callback = [&](const Surface& surface, + RenderPass& pass) { + Command cmd; + cmd.label = "Box"; + cmd.pipeline = box_pipeline; + + cmd.BindVertices(vertex_buffer); + + BoxFragmentShader::FrameInfo frame_info; + frame_info.current_time = fml::TimePoint::Now().ToEpochDelta().ToSecondsF(); + frame_info.cursor_position = GetCursorPosition(); + frame_info.window_size.x = GetWindowSize().width; + frame_info.window_size.y = GetWindowSize().height; + + BoxFragmentShader::BindFrameInfo( + cmd, pass.GetTransientsBuffer().EmplaceUniform(frame_info)); + BoxFragmentShader::BindContents1(cmd, boston, sampler); + BoxFragmentShader::BindContents2(cmd, bridge, sampler); + + cmd.primitive_type = PrimitiveType::kTriangle; + + { + BoxVertexShader::UniformBuffer uniforms; + uniforms.mvp = Matrix::MakeOrthographic(surface.GetSize()); + BoxVertexShader::BindUniformBuffer( + cmd, pass.GetTransientsBuffer().EmplaceUniform(uniforms)); + + if (!pass.RecordCommand(cmd)) { + return false; + } + } + + { + BoxVertexShader::UniformBuffer uniforms; + uniforms.mvp = Matrix::MakeOrthographic(surface.GetSize()) * + Matrix::MakeTranslation({100.0, 100.0, 0.0}); + BoxVertexShader::BindUniformBuffer( + cmd, pass.GetTransientsBuffer().EmplaceUniform(uniforms)); + + if (!pass.RecordCommand(cmd)) { + return false; + } + } + + return true; + }; + // OpenPlaygroundHere(callback); } } // namespace testing From 22eaf2f824eff574c5ae3e997adb7f173c9c4a0e Mon Sep 17 00:00:00 2001 From: Chinmay Garde Date: Fri, 16 Jul 2021 13:26:28 -0700 Subject: [PATCH 142/433] Add stress test. --- impeller/compositor/pipeline_builder.h | 4 +- impeller/primitives/BUILD.gn | 4 +- .../primitives/{box.frag => box_fade.frag} | 0 .../primitives/{box.vert => box_fade.vert} | 0 impeller/primitives/primitives_unittests.mm | 76 +++++++++---------- 5 files changed, 38 insertions(+), 46 deletions(-) rename impeller/primitives/{box.frag => box_fade.frag} (100%) rename impeller/primitives/{box.vert => box_fade.vert} (100%) diff --git a/impeller/compositor/pipeline_builder.h b/impeller/compositor/pipeline_builder.h index fe973e743fb3c..a0f7513686f19 100644 --- a/impeller/compositor/pipeline_builder.h +++ b/impeller/compositor/pipeline_builder.h @@ -37,13 +37,13 @@ struct PipelineBuilder { //---------------------------------------------------------------------------- /// @brief Create a default pipeline descriptor using the combination /// reflected shader information. The descriptor can be configured - /// further before a pipline state object is created using it. + /// further before a pipeline state object is created using it. /// /// @param[in] context The context /// /// @return If the combination of reflected shader information is /// compatible and the requisite functions can be found in the - /// context, a pipeine descriptor. + /// context, a pipeline descriptor. /// static std::optional MakeDefaultPipelineDescriptor( const Context& context) { diff --git a/impeller/primitives/BUILD.gn b/impeller/primitives/BUILD.gn index 8abe52c1ab0f4..6d50bbf25e9f7 100644 --- a/impeller/primitives/BUILD.gn +++ b/impeller/primitives/BUILD.gn @@ -7,8 +7,8 @@ import("//flutter/impeller/tools/impeller.gni") impeller_shaders("impeller_shaders") { name = "impeller" shaders = [ - "box.vert", - "box.frag", + "box_fade.vert", + "box_fade.frag", ] } diff --git a/impeller/primitives/box.frag b/impeller/primitives/box_fade.frag similarity index 100% rename from impeller/primitives/box.frag rename to impeller/primitives/box_fade.frag diff --git a/impeller/primitives/box.vert b/impeller/primitives/box_fade.vert similarity index 100% rename from impeller/primitives/box.vert rename to impeller/primitives/box_fade.vert diff --git a/impeller/primitives/primitives_unittests.mm b/impeller/primitives/primitives_unittests.mm index 3c2d7eafa63ea..3cb8fbc0d283f 100644 --- a/impeller/primitives/primitives_unittests.mm +++ b/impeller/primitives/primitives_unittests.mm @@ -13,8 +13,8 @@ #include "impeller/image/compressed_image.h" #include "impeller/image/image.h" #include "impeller/playground/playground.h" -#include "impeller/primitives/box.frag.h" -#include "impeller/primitives/box.vert.h" +#include "impeller/primitives/box_fade.frag.h" +#include "impeller/primitives/box_fade.vert.h" namespace impeller { namespace testing { @@ -22,17 +22,18 @@ using PrimitivesTest = Playground; TEST_F(PrimitivesTest, CanCreateBoxPrimitive) { + using VS = BoxFadeVertexShader; + using FS = BoxFadeFragmentShader; auto context = GetContext(); ASSERT_TRUE(context); - using BoxPipelineBuilder = - PipelineBuilder; + using BoxPipelineBuilder = PipelineBuilder; auto desc = BoxPipelineBuilder::MakeDefaultPipelineDescriptor(*context); auto box_pipeline = context->GetPipelineLibrary()->GetRenderPipeline(std::move(desc)).get(); ASSERT_TRUE(box_pipeline); // Vertex buffer. - VertexBufferBuilder vertex_builder; + VertexBufferBuilder vertex_builder; vertex_builder.SetLabel("Box"); vertex_builder.AddVertices({ {{100, 100, 0.0}, {0.0, 0.0}}, // 1 @@ -59,21 +60,21 @@ cmd.BindVertices(vertex_buffer); - BoxVertexShader::UniformBuffer uniforms; + VS::UniformBuffer uniforms; uniforms.mvp = Matrix::MakeOrthographic(surface.GetSize()); - BoxVertexShader::BindUniformBuffer( - cmd, pass.GetTransientsBuffer().EmplaceUniform(uniforms)); + VS::BindUniformBuffer(cmd, + pass.GetTransientsBuffer().EmplaceUniform(uniforms)); - BoxFragmentShader::FrameInfo frame_info; + FS::FrameInfo frame_info; frame_info.current_time = fml::TimePoint::Now().ToEpochDelta().ToSecondsF(); frame_info.cursor_position = GetCursorPosition(); frame_info.window_size.x = GetWindowSize().width; frame_info.window_size.y = GetWindowSize().height; - BoxFragmentShader::BindFrameInfo( - cmd, pass.GetTransientsBuffer().EmplaceUniform(frame_info)); - BoxFragmentShader::BindContents1(cmd, boston, sampler); - BoxFragmentShader::BindContents2(cmd, bridge, sampler); + FS::BindFrameInfo(cmd, + pass.GetTransientsBuffer().EmplaceUniform(frame_info)); + FS::BindContents1(cmd, boston, sampler); + FS::BindContents2(cmd, bridge, sampler); cmd.primitive_type = PrimitiveType::kTriangle; if (!pass.RecordCommand(std::move(cmd))) { @@ -85,17 +86,18 @@ } TEST_F(PrimitivesTest, CanRenderMultiplePrimitives) { + using VS = BoxFadeVertexShader; + using FS = BoxFadeFragmentShader; auto context = GetContext(); ASSERT_TRUE(context); - using BoxPipelineBuilder = - PipelineBuilder; + using BoxPipelineBuilder = PipelineBuilder; auto desc = BoxPipelineBuilder::MakeDefaultPipelineDescriptor(*context); auto box_pipeline = context->GetPipelineLibrary()->GetRenderPipeline(std::move(desc)).get(); ASSERT_TRUE(box_pipeline); // Vertex buffer. - VertexBufferBuilder vertex_builder; + VertexBufferBuilder vertex_builder; vertex_builder.SetLabel("Box"); vertex_builder.AddVertices({ {{100, 100, 0.0}, {0.0, 0.0}}, // 1 @@ -123,45 +125,35 @@ cmd.BindVertices(vertex_buffer); - BoxFragmentShader::FrameInfo frame_info; + FS::FrameInfo frame_info; frame_info.current_time = fml::TimePoint::Now().ToEpochDelta().ToSecondsF(); frame_info.cursor_position = GetCursorPosition(); frame_info.window_size.x = GetWindowSize().width; frame_info.window_size.y = GetWindowSize().height; - BoxFragmentShader::BindFrameInfo( - cmd, pass.GetTransientsBuffer().EmplaceUniform(frame_info)); - BoxFragmentShader::BindContents1(cmd, boston, sampler); - BoxFragmentShader::BindContents2(cmd, bridge, sampler); + FS::BindFrameInfo(cmd, + pass.GetTransientsBuffer().EmplaceUniform(frame_info)); + FS::BindContents1(cmd, boston, sampler); + FS::BindContents2(cmd, bridge, sampler); cmd.primitive_type = PrimitiveType::kTriangle; - { - BoxVertexShader::UniformBuffer uniforms; - uniforms.mvp = Matrix::MakeOrthographic(surface.GetSize()); - BoxVertexShader::BindUniformBuffer( - cmd, pass.GetTransientsBuffer().EmplaceUniform(uniforms)); - - if (!pass.RecordCommand(cmd)) { - return false; - } - } - - { - BoxVertexShader::UniformBuffer uniforms; - uniforms.mvp = Matrix::MakeOrthographic(surface.GetSize()) * - Matrix::MakeTranslation({100.0, 100.0, 0.0}); - BoxVertexShader::BindUniformBuffer( - cmd, pass.GetTransientsBuffer().EmplaceUniform(uniforms)); - - if (!pass.RecordCommand(cmd)) { - return false; + for (size_t i = 0; i < 50; i++) { + for (size_t j = 0; j < 50; j++) { + VS::UniformBuffer uniforms; + uniforms.mvp = Matrix::MakeOrthographic(surface.GetSize()) * + Matrix::MakeTranslation({i * 50.0f, j * 50.0f, 0.0f}); + VS::BindUniformBuffer( + cmd, pass.GetTransientsBuffer().EmplaceUniform(uniforms)); + if (!pass.RecordCommand(cmd)) { + return false; + } } } return true; }; - // OpenPlaygroundHere(callback); + OpenPlaygroundHere(callback); } } // namespace testing From 5b8abf402a48acc9bc389985e16db0138dca3ed1 Mon Sep 17 00:00:00 2001 From: Chinmay Garde Date: Fri, 16 Jul 2021 14:42:30 -0700 Subject: [PATCH 143/433] Add pass bindings cache. Frame insights are empty. --- impeller/compositor/render_pass.mm | 220 +++++++++++++++++++++-------- 1 file changed, 161 insertions(+), 59 deletions(-) diff --git a/impeller/compositor/render_pass.mm b/impeller/compositor/render_pass.mm index 206aecab551e1..f390d0cacf769 100644 --- a/impeller/compositor/render_pass.mm +++ b/impeller/compositor/render_pass.mm @@ -4,6 +4,9 @@ #include "impeller/compositor/render_pass.h" +#include +#include + #include "flutter/fml/closure.h" #include "flutter/fml/logging.h" #include "impeller/base/base.h" @@ -176,7 +179,147 @@ static bool ConfigureStencilAttachment( return EncodeCommands(transients_allocator, pass); } -static bool Bind(id pass, +//----------------------------------------------------------------------------- +/// @brief Ensures that bindings on the pass are not redundantly set or +/// updated. Avoids making the driver do additional checks and makes +/// the frame insights during profiling and instrumentation not +/// complain about the same. +/// +/// There should be no change to rendering if this caching was +/// absent. +/// +struct PassBindingsCache { + PassBindingsCache(id pass) : pass_(pass) {} + + PassBindingsCache(const PassBindingsCache&) = delete; + + PassBindingsCache(PassBindingsCache&&) = delete; + + void SetRenderPipelineState(id pipeline) { + if (pipeline == pipeline_) { + return; + } + pipeline_ = pipeline; + [pass_ setRenderPipelineState:pipeline_]; + } + + void SetDepthStencilState(id depth_stencil) { + if (depth_stencil_ == depth_stencil) { + return; + } + depth_stencil_ = depth_stencil; + [pass_ setDepthStencilState:depth_stencil_]; + } + + bool SetBuffer(ShaderStage stage, + uint64_t index, + uint64_t offset, + id buffer) { + auto& buffers_map = buffers_[stage]; + auto found = buffers_map.find(index); + if (found != buffers_map.end() && found->second.buffer == buffer) { + // The right buffer is bound. Check if its offset needs to be updated. + if (found->second.offset == offset) { + // Buffer and its offset is identical. Nothing to do. + return true; + } + + // Only the offset needs to be updated. + found->second.offset = offset; + + switch (stage) { + case ShaderStage::kVertex: + [pass_ setVertexBufferOffset:offset atIndex:index]; + return true; + case ShaderStage::kFragment: + [pass_ setFragmentBufferOffset:offset atIndex:index]; + return true; + default: + FML_DCHECK(false) + << "Cannot update buffer offset of an unknown stage."; + return false; + } + return true; + } + buffers_map[index] = {buffer, offset}; + switch (stage) { + case ShaderStage::kVertex: + [pass_ setVertexBuffer:buffer offset:offset atIndex:index]; + return true; + case ShaderStage::kFragment: + [pass_ setFragmentBuffer:buffer offset:offset atIndex:index]; + return true; + default: + FML_DCHECK(false) << "Cannot bind buffer to unknown shader stage."; + return false; + } + return false; + } + + bool SetTexture(ShaderStage stage, uint64_t index, id texture) { + auto& texture_map = textures_[stage]; + auto found = texture_map.find(index); + if (found != texture_map.end() && found->second == texture) { + // Already bound. + return true; + } + texture_map[index] = texture; + switch (stage) { + case ShaderStage::kVertex: + [pass_ setVertexTexture:texture atIndex:index]; + return true; + case ShaderStage::kFragment: + [pass_ setFragmentTexture:texture atIndex:index]; + return true; + default: + FML_DCHECK(false) << "Cannot bind buffer to unknown shader stage."; + return false; + } + return false; + } + + bool SetSampler(ShaderStage stage, + uint64_t index, + id sampler) { + auto& sampler_map = samplers_[stage]; + auto found = sampler_map.find(index); + if (found != sampler_map.end() && found->second == sampler) { + // Already bound. + return true; + } + sampler_map[index] = sampler; + switch (stage) { + case ShaderStage::kVertex: + [pass_ setVertexSamplerState:sampler atIndex:index]; + return true; + case ShaderStage::kFragment: + [pass_ setFragmentSamplerState:sampler atIndex:index]; + return true; + default: + FML_DCHECK(false) << "Cannot bind buffer to unknown shader stage."; + return false; + } + return false; + } + + private: + struct BufferOffsetPair { + id buffer = nullptr; + size_t offset = 0u; + }; + using BufferMap = std::map; + using TextureMap = std::map>; + using SamplerMap = std::map>; + + const id pass_; + id pipeline_ = nullptr; + id depth_stencil_ = nullptr; + std::map buffers_; + std::map textures_; + std::map samplers_; +}; + +static bool Bind(PassBindingsCache& pass, Allocator& allocator, ShaderStage stage, size_t bind_index, @@ -196,25 +339,10 @@ static bool Bind(id pass, return false; } - switch (stage) { - case ShaderStage::kVertex: - [pass setVertexBuffer:buffer offset:view.range.offset atIndex:bind_index]; - return true; - case ShaderStage::kFragment: - [pass setFragmentBuffer:buffer - offset:view.range.offset - atIndex:bind_index]; - return true; - default: - FML_DLOG(ERROR) << "Cannot bind buffer to unknown shader stage."; - return false; - } - - return false; + return pass.SetBuffer(stage, bind_index, view.range.offset, buffer); } -static bool Bind(id pass, - Allocator& allocator, +static bool Bind(PassBindingsCache& pass, ShaderStage stage, size_t bind_index, const Texture& texture) { @@ -222,23 +350,10 @@ static bool Bind(id pass, return false; } - switch (stage) { - case ShaderStage::kVertex: - [pass setVertexTexture:texture.GetMTLTexture() atIndex:bind_index]; - return true; - case ShaderStage::kFragment: - [pass setFragmentTexture:texture.GetMTLTexture() atIndex:bind_index]; - return true; - default: - FML_DLOG(ERROR) << "Cannot bind buffer to unknown shader stage."; - return false; - } - - return false; + return pass.SetTexture(stage, bind_index, texture.GetMTLTexture()); } -static bool Bind(id pass, - Allocator& allocator, +static bool Bind(PassBindingsCache& pass, ShaderStage stage, size_t bind_index, const Sampler& sampler) { @@ -246,48 +361,33 @@ static bool Bind(id pass, return false; } - switch (stage) { - case ShaderStage::kVertex: - [pass setVertexSamplerState:sampler.GetMTLSamplerState() - atIndex:bind_index]; - - return true; - case ShaderStage::kFragment: - [pass setFragmentSamplerState:sampler.GetMTLSamplerState() - atIndex:bind_index]; - return true; - default: - FML_DLOG(ERROR) << "Cannot bind buffer to unknown shader stage."; - return false; - } - - return false; + return pass.SetSampler(stage, bind_index, sampler.GetMTLSamplerState()); } bool RenderPass::EncodeCommands(Allocator& allocator, id pass) const { - // There a numerous opportunities here to ensure bindings are not repeated. - // Stuff like setting the vertex buffer bindings over and over when just the - // offsets could be updated (as recommended in best practices). - auto bind_stage_resources = [&allocator, pass](const Bindings& bindings, - ShaderStage stage) -> bool { + PassBindingsCache pass_bindings(pass); + auto bind_stage_resources = [&allocator, &pass_bindings]( + const Bindings& bindings, + ShaderStage stage) -> bool { for (const auto buffer : bindings.buffers) { - if (!Bind(pass, allocator, stage, buffer.first, buffer.second)) { + if (!Bind(pass_bindings, allocator, stage, buffer.first, buffer.second)) { return false; } } for (const auto texture : bindings.textures) { - if (!Bind(pass, allocator, stage, texture.first, *texture.second)) { + if (!Bind(pass_bindings, stage, texture.first, *texture.second)) { return false; } } for (const auto sampler : bindings.samplers) { - if (!Bind(pass, allocator, stage, sampler.first, *sampler.second)) { + if (!Bind(pass_bindings, stage, sampler.first, *sampler.second)) { return false; } } return true; }; + fml::closure pop_debug_marker = [pass]() { [pass popDebugGroup]; }; for (const auto& command : commands_) { if (command.index_count == 0u) { @@ -300,8 +400,10 @@ static bool Bind(id pass, } else { auto_pop_debug_marker.Release(); } - [pass setRenderPipelineState:command.pipeline->GetMTLRenderPipelineState()]; - [pass setDepthStencilState:command.pipeline->GetMTLDepthStencilState()]; + pass_bindings.SetRenderPipelineState( + command.pipeline->GetMTLRenderPipelineState()); + pass_bindings.SetDepthStencilState( + command.pipeline->GetMTLDepthStencilState()); [pass setFrontFacingWinding:MTLWindingClockwise]; [pass setCullMode:MTLCullModeBack]; if (!bind_stage_resources(command.vertex_bindings, ShaderStage::kVertex)) { From 68f876f413ba0ad803899e257fc2e7a10ba4d2c3 Mon Sep 17 00:00:00 2001 From: Chinmay Garde Date: Sun, 18 Jul 2021 14:16:56 -0700 Subject: [PATCH 144/433] Add render to texture pass. --- impeller/compositor/pipeline_descriptor.h | 3 + impeller/compositor/pipeline_descriptor.mm | 7 ++ impeller/compositor/render_pass.h | 12 ++- impeller/compositor/render_pass.mm | 2 +- impeller/compositor/renderer.mm | 2 +- impeller/primitives/primitives_unittests.mm | 94 ++++++++++++++++++++- 6 files changed, 116 insertions(+), 4 deletions(-) diff --git a/impeller/compositor/pipeline_descriptor.h b/impeller/compositor/pipeline_descriptor.h index 1d6a1d2e01553..f81da73fe1d0e 100644 --- a/impeller/compositor/pipeline_descriptor.h +++ b/impeller/compositor/pipeline_descriptor.h @@ -44,6 +44,9 @@ class PipelineDescriptor final : public Comparable { size_t index, ColorAttachmentDescriptor desc); + const ColorAttachmentDescriptor* GetColorAttachmentDescriptor( + size_t index) const; + PipelineDescriptor& SetDepthStencilAttachmentDescriptor( DepthAttachmentDescriptor desc); diff --git a/impeller/compositor/pipeline_descriptor.mm b/impeller/compositor/pipeline_descriptor.mm index 1aa9d5e781c42..b74394874464c 100644 --- a/impeller/compositor/pipeline_descriptor.mm +++ b/impeller/compositor/pipeline_descriptor.mm @@ -94,6 +94,13 @@ return *this; } +const ColorAttachmentDescriptor* +PipelineDescriptor::GetColorAttachmentDescriptor(size_t index) const { + auto found = color_attachment_descriptors_.find(index); + return found == color_attachment_descriptors_.end() ? nullptr + : &found->second; +} + PipelineDescriptor& PipelineDescriptor::SetDepthPixelFormat( PixelFormat format) { depth_pixel_format_ = format; diff --git a/impeller/compositor/render_pass.h b/impeller/compositor/render_pass.h index 70688bc3548bf..67d405ab7ceff 100644 --- a/impeller/compositor/render_pass.h +++ b/impeller/compositor/render_pass.h @@ -80,7 +80,17 @@ class RenderPass { [[nodiscard]] bool RecordCommand(Command command); - [[nodiscard]] bool FinishEncoding(Allocator& transients_allocator) const; + //---------------------------------------------------------------------------- + /// @brief Commit the recorded commands to the underlying command buffer. + /// Any completion handlers must on the underlying command buffer + /// must have already been added by this point. + /// + /// @param transients_allocator The transients allocator. + /// + /// @return If the commands were committed to the underlying command + /// buffer. + /// + [[nodiscard]] bool Commit(Allocator& transients_allocator) const; private: friend class CommandBuffer; diff --git a/impeller/compositor/render_pass.mm b/impeller/compositor/render_pass.mm index f390d0cacf769..d835c2ad72c00 100644 --- a/impeller/compositor/render_pass.mm +++ b/impeller/compositor/render_pass.mm @@ -158,7 +158,7 @@ static bool ConfigureStencilAttachment( transients_buffer_->SetLabel(SPrintF("%s Transients", label_.c_str())); } -bool RenderPass::FinishEncoding(Allocator& transients_allocator) const { +bool RenderPass::Commit(Allocator& transients_allocator) const { if (!IsValid()) { return false; } diff --git a/impeller/compositor/renderer.mm b/impeller/compositor/renderer.mm index 8b55e4b7c8a4e..ed13c1ce89e04 100644 --- a/impeller/compositor/renderer.mm +++ b/impeller/compositor/renderer.mm @@ -54,7 +54,7 @@ return false; } - if (!render_pass->FinishEncoding(*GetContext()->GetTransientsAllocator())) { + if (!render_pass->Commit(*GetContext()->GetTransientsAllocator())) { return false; } diff --git a/impeller/primitives/primitives_unittests.mm b/impeller/primitives/primitives_unittests.mm index 3cb8fbc0d283f..6a55d5c0d08c6 100644 --- a/impeller/primitives/primitives_unittests.mm +++ b/impeller/primitives/primitives_unittests.mm @@ -5,6 +5,7 @@ #include "flutter/fml/time/time_point.h" #include "flutter/testing/testing.h" #include "impeller/compositor/command.h" +#include "impeller/compositor/command_buffer.h" #include "impeller/compositor/pipeline_builder.h" #include "impeller/compositor/renderer.h" #include "impeller/compositor/sampler_descriptor.h" @@ -153,7 +154,98 @@ return true; }; - OpenPlaygroundHere(callback); + // OpenPlaygroundHere(callback); +} + +TEST_F(PrimitivesTest, CanRenderToTexture) { + using VS = BoxFadeVertexShader; + using FS = BoxFadeFragmentShader; + auto context = GetContext(); + ASSERT_TRUE(context); + using BoxPipelineBuilder = PipelineBuilder; + auto pipeline_desc = + BoxPipelineBuilder::MakeDefaultPipelineDescriptor(*context); + auto box_pipeline = context->GetPipelineLibrary() + ->GetRenderPipeline(std::move(pipeline_desc)) + .get(); + ASSERT_TRUE(box_pipeline); + + VertexBufferBuilder vertex_builder; + vertex_builder.SetLabel("Box"); + vertex_builder.AddVertices({ + {{100, 100, 0.0}, {0.0, 0.0}}, // 1 + {{800, 100, 0.0}, {1.0, 0.0}}, // 2 + {{800, 800, 0.0}, {1.0, 1.0}}, // 3 + {{100, 100, 0.0}, {0.0, 0.0}}, // 1 + {{800, 800, 0.0}, {1.0, 1.0}}, // 3 + {{100, 800, 0.0}, {0.0, 1.0}}, // 4 + }); + auto vertex_buffer = + vertex_builder.CreateVertexBuffer(*context->GetPermanentsAllocator()); + ASSERT_TRUE(vertex_buffer); + + auto bridge = CreateTextureForFixture("bay_bridge.jpg"); + auto boston = CreateTextureForFixture("boston.jpg"); + ASSERT_TRUE(bridge && boston); + auto sampler = context->GetSamplerLibrary()->GetSampler({}); + ASSERT_TRUE(sampler); + + std::shared_ptr r2t_pass; + + { + ColorRenderPassAttachment color0; + color0.load_action = LoadAction::kClear; + color0.store_action = StoreAction::kStore; + + TextureDescriptor texture_descriptor; + ASSERT_NE(pipeline_desc->GetColorAttachmentDescriptor(0u), nullptr); + texture_descriptor.format = + pipeline_desc->GetColorAttachmentDescriptor(0u)->format; + texture_descriptor.size = {400, 400}; + texture_descriptor.mip_count = 1u; + texture_descriptor.usage = + static_cast(TextureUsage::kRenderTarget); + + color0.texture = context->GetPermanentsAllocator()->CreateTexture( + StorageMode::kHostVisible, texture_descriptor); + + ASSERT_TRUE(color0); + + color0.texture->SetLabel("r2t_target"); + + RenderPassDescriptor r2t_desc; + r2t_desc.SetColorAttachment(color0, 0u); + auto cmd_buffer = context->CreateRenderCommandBuffer(); + r2t_pass = cmd_buffer->CreateRenderPass(r2t_desc); + ASSERT_TRUE(r2t_pass && r2t_pass->IsValid()); + } + + Command cmd; + cmd.label = "Box"; + cmd.pipeline = box_pipeline; + + cmd.BindVertices(vertex_buffer); + + FS::FrameInfo frame_info; + frame_info.current_time = fml::TimePoint::Now().ToEpochDelta().ToSecondsF(); + frame_info.cursor_position = GetCursorPosition(); + frame_info.window_size.x = GetWindowSize().width; + frame_info.window_size.y = GetWindowSize().height; + + FS::BindFrameInfo(cmd, + r2t_pass->GetTransientsBuffer().EmplaceUniform(frame_info)); + FS::BindContents1(cmd, boston, sampler); + FS::BindContents2(cmd, bridge, sampler); + + cmd.primitive_type = PrimitiveType::kTriangle; + + VS::UniformBuffer uniforms; + uniforms.mvp = Matrix::MakeOrthographic(ISize{1024, 768}) * + Matrix::MakeTranslation({50.0f, 50.0f, 0.0f}); + VS::BindUniformBuffer( + cmd, r2t_pass->GetTransientsBuffer().EmplaceUniform(uniforms)); + ASSERT_TRUE(r2t_pass->RecordCommand(std::move(cmd))); + ASSERT_TRUE(r2t_pass->Commit(*context->GetTransientsAllocator())); } } // namespace testing From 5318e56ee8c2da1173914cef86a5cd7dd2520372 Mon Sep 17 00:00:00 2001 From: Chinmay Garde Date: Sun, 18 Jul 2021 14:29:24 -0700 Subject: [PATCH 145/433] Move render pass descriptor to their own TU --- impeller/compositor/BUILD.gn | 2 + impeller/compositor/formats.h | 44 ++++++++++---- impeller/compositor/formats_metal.h | 10 ++-- impeller/compositor/pipeline_builder.h | 2 +- impeller/compositor/render_pass.h | 48 +-------------- impeller/compositor/render_pass.mm | 47 --------------- impeller/compositor/render_pass_descriptor.h | 45 ++++++++++++++ impeller/compositor/render_pass_descriptor.mm | 58 +++++++++++++++++++ impeller/playground/playground.mm | 4 +- 9 files changed, 148 insertions(+), 112 deletions(-) create mode 100644 impeller/compositor/render_pass_descriptor.h create mode 100644 impeller/compositor/render_pass_descriptor.mm diff --git a/impeller/compositor/BUILD.gn b/impeller/compositor/BUILD.gn index 0a9c708e14d7f..a82dff11109d8 100644 --- a/impeller/compositor/BUILD.gn +++ b/impeller/compositor/BUILD.gn @@ -42,6 +42,8 @@ impeller_component("compositor") { "range.h", "render_pass.h", "render_pass.mm", + "render_pass_descriptor.h", + "render_pass_descriptor.mm", "renderer.h", "renderer.mm", "sampler.h", diff --git a/impeller/compositor/formats.h b/impeller/compositor/formats.h index 6de3d173be780..fac00782edb02 100644 --- a/impeller/compositor/formats.h +++ b/impeller/compositor/formats.h @@ -6,20 +6,24 @@ #include #include +#include #include #include "flutter/fml/hash_combine.h" #include "flutter/fml/macros.h" +#include "impeller/geometry/color.h" namespace impeller { +class Texture; + enum class PixelFormat { kUnknown, - kPixelFormat_R8G8B8A8_UNormInt, - kPixelFormat_R8G8B8A8_UNormInt_SRGB, - kPixelFormat_B8G8R8A8_UNormInt, - kPixelFormat_B8G8R8A8_UNormInt_SRGB, - kPixelFormat_D32_Float_S8_UNormInt, + kR8G8B8A8_UNormInt, + kR8G8B8A8_UNormInt_SRGB, + kB8G8R8A8_UNormInt, + kB8G8R8A8_UNormInt_SRGB, + kD32_Float_S8_UNormInt, }; enum class BlendFactor { @@ -108,12 +112,12 @@ constexpr size_t BytesPerPixelForPixelFormat(PixelFormat format) { switch (format) { case PixelFormat::kUnknown: return 0u; - case PixelFormat::kPixelFormat_R8G8B8A8_UNormInt: - case PixelFormat::kPixelFormat_R8G8B8A8_UNormInt_SRGB: - case PixelFormat::kPixelFormat_B8G8R8A8_UNormInt: - case PixelFormat::kPixelFormat_B8G8R8A8_UNormInt_SRGB: + case PixelFormat::kR8G8B8A8_UNormInt: + case PixelFormat::kR8G8B8A8_UNormInt_SRGB: + case PixelFormat::kB8G8R8A8_UNormInt: + case PixelFormat::kB8G8R8A8_UNormInt_SRGB: return 4u; - case PixelFormat::kPixelFormat_D32_Float_S8_UNormInt: + case PixelFormat::kD32_Float_S8_UNormInt: // This is an esoteric format and implementations may use 64 bits. // Impeller doesn't work with these natively and this return is only here // for completeness. The 40 bits is as documented. @@ -278,6 +282,26 @@ struct StencilAttachmentDescriptor { } }; +struct RenderPassAttachment { + std::shared_ptr texture = nullptr; + LoadAction load_action = LoadAction::kDontCare; + StoreAction store_action = StoreAction::kStore; + + constexpr operator bool() const { return static_cast(texture); } +}; + +struct ColorRenderPassAttachment : public RenderPassAttachment { + Color clear_color = Color::BlackTransparent(); +}; + +struct DepthRenderPassAttachment : public RenderPassAttachment { + double clear_depth = 0.0; +}; + +struct StencilRenderPassAttachment : public RenderPassAttachment { + uint32_t clear_stencil = 0; +}; + } // namespace impeller namespace std { diff --git a/impeller/compositor/formats_metal.h b/impeller/compositor/formats_metal.h index 6a4a1addaa758..e0acf04462b38 100644 --- a/impeller/compositor/formats_metal.h +++ b/impeller/compositor/formats_metal.h @@ -21,15 +21,15 @@ constexpr MTLPixelFormat ToMTLPixelFormat(PixelFormat format) { switch (format) { case PixelFormat::kUnknown: return MTLPixelFormatInvalid; - case PixelFormat::kPixelFormat_B8G8R8A8_UNormInt: + case PixelFormat::kB8G8R8A8_UNormInt: return MTLPixelFormatBGRA8Unorm; - case PixelFormat::kPixelFormat_B8G8R8A8_UNormInt_SRGB: + case PixelFormat::kB8G8R8A8_UNormInt_SRGB: return MTLPixelFormatBGRA8Unorm_sRGB; - case PixelFormat::kPixelFormat_D32_Float_S8_UNormInt: + case PixelFormat::kD32_Float_S8_UNormInt: return MTLPixelFormatDepth32Float_Stencil8; - case PixelFormat::kPixelFormat_R8G8B8A8_UNormInt: + case PixelFormat::kR8G8B8A8_UNormInt: return MTLPixelFormatRGBA8Unorm; - case PixelFormat::kPixelFormat_R8G8B8A8_UNormInt_SRGB: + case PixelFormat::kR8G8B8A8_UNormInt_SRGB: return MTLPixelFormatRGBA8Unorm_sRGB; } return MTLPixelFormatInvalid; diff --git a/impeller/compositor/pipeline_builder.h b/impeller/compositor/pipeline_builder.h index a0f7513686f19..cfc110b32b65b 100644 --- a/impeller/compositor/pipeline_builder.h +++ b/impeller/compositor/pipeline_builder.h @@ -85,7 +85,7 @@ struct PipelineBuilder { // TODO(csg): This can be easily reflected but we are sticking to the // convention that the first stage output is the color output. ColorAttachmentDescriptor color0; - color0.format = PixelFormat::kPixelFormat_B8G8R8A8_UNormInt; + color0.format = PixelFormat::kB8G8R8A8_UNormInt; desc.SetColorAttachmentDescriptor(0u, std::move(color0)); } diff --git a/impeller/compositor/render_pass.h b/impeller/compositor/render_pass.h index 67d405ab7ceff..bf1129d9866fa 100644 --- a/impeller/compositor/render_pass.h +++ b/impeller/compositor/render_pass.h @@ -13,6 +13,7 @@ #include "impeller/compositor/command.h" #include "impeller/compositor/formats.h" #include "impeller/compositor/host_buffer.h" +#include "impeller/compositor/render_pass_descriptor.h" #include "impeller/compositor/texture.h" #include "impeller/geometry/color.h" #include "impeller/geometry/size.h" @@ -21,53 +22,6 @@ namespace impeller { class CommandBuffer; -struct RenderPassAttachment { - std::shared_ptr texture; - LoadAction load_action = LoadAction::kDontCare; - StoreAction store_action = StoreAction::kStore; - - constexpr operator bool() const { return static_cast(texture); } -}; - -struct ColorRenderPassAttachment : public RenderPassAttachment { - Color clear_color = Color::BlackTransparent(); -}; - -struct DepthRenderPassAttachment : public RenderPassAttachment { - double clear_depth = 0.0; -}; - -struct StencilRenderPassAttachment : public RenderPassAttachment { - uint32_t clear_stencil = 0; -}; - -class RenderPassDescriptor { - public: - RenderPassDescriptor(); - - ~RenderPassDescriptor(); - - bool HasColorAttachment(size_t index) const; - - std::optional GetColorAttachmentSize(size_t index) const; - - RenderPassDescriptor& SetColorAttachment(ColorRenderPassAttachment attachment, - size_t index); - - RenderPassDescriptor& SetDepthAttachment( - DepthRenderPassAttachment attachment); - - RenderPassDescriptor& SetStencilAttachment( - StencilRenderPassAttachment attachment); - - MTLRenderPassDescriptor* ToMTLRenderPassDescriptor() const; - - private: - std::map colors_; - std::optional depth_; - std::optional stencil_; -}; - class RenderPass { public: ~RenderPass(); diff --git a/impeller/compositor/render_pass.mm b/impeller/compositor/render_pass.mm index d835c2ad72c00..188b6e19b0ba2 100644 --- a/impeller/compositor/render_pass.mm +++ b/impeller/compositor/render_pass.mm @@ -17,53 +17,6 @@ namespace impeller { -RenderPassDescriptor::RenderPassDescriptor() = default; - -RenderPassDescriptor::~RenderPassDescriptor() = default; - -bool RenderPassDescriptor::HasColorAttachment(size_t index) const { - if (auto found = colors_.find(index); found != colors_.end()) { - return true; - } - return false; -} - -std::optional RenderPassDescriptor::GetColorAttachmentSize( - size_t index) const { - auto found = colors_.find(index); - - if (found == colors_.end()) { - return std::nullopt; - } - - return found->second.texture->GetSize(); -} - -RenderPassDescriptor& RenderPassDescriptor::SetColorAttachment( - ColorRenderPassAttachment attachment, - size_t index) { - if (attachment) { - colors_[index] = attachment; - } - return *this; -} - -RenderPassDescriptor& RenderPassDescriptor::SetDepthAttachment( - DepthRenderPassAttachment attachment) { - if (attachment) { - depth_ = std::move(attachment); - } - return *this; -} - -RenderPassDescriptor& RenderPassDescriptor::SetStencilAttachment( - StencilRenderPassAttachment attachment) { - if (attachment) { - stencil_ = std::move(attachment); - } - return *this; -} - static bool ConfigureAttachment(const RenderPassAttachment& desc, MTLRenderPassAttachmentDescriptor* attachment) { if (!desc.texture) { diff --git a/impeller/compositor/render_pass_descriptor.h b/impeller/compositor/render_pass_descriptor.h new file mode 100644 index 0000000000000..baab8445137d6 --- /dev/null +++ b/impeller/compositor/render_pass_descriptor.h @@ -0,0 +1,45 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#pragma once + +#include +#include + +#include + +#include "flutter/fml/macros.h" +#include "impeller/compositor/formats.h" +#include "impeller/geometry/size.h" + +namespace impeller { + +class RenderPassDescriptor { + public: + RenderPassDescriptor(); + + ~RenderPassDescriptor(); + + bool HasColorAttachment(size_t index) const; + + std::optional GetColorAttachmentSize(size_t index) const; + + RenderPassDescriptor& SetColorAttachment(ColorRenderPassAttachment attachment, + size_t index); + + RenderPassDescriptor& SetDepthAttachment( + DepthRenderPassAttachment attachment); + + RenderPassDescriptor& SetStencilAttachment( + StencilRenderPassAttachment attachment); + + MTLRenderPassDescriptor* ToMTLRenderPassDescriptor() const; + + private: + std::map colors_; + std::optional depth_; + std::optional stencil_; +}; + +} // namespace impeller diff --git a/impeller/compositor/render_pass_descriptor.mm b/impeller/compositor/render_pass_descriptor.mm new file mode 100644 index 0000000000000..b8802582e87cf --- /dev/null +++ b/impeller/compositor/render_pass_descriptor.mm @@ -0,0 +1,58 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "impeller/compositor/render_pass_descriptor.h" + +#include "impeller/compositor/texture.h" + +namespace impeller { + +RenderPassDescriptor::RenderPassDescriptor() = default; + +RenderPassDescriptor::~RenderPassDescriptor() = default; + +bool RenderPassDescriptor::HasColorAttachment(size_t index) const { + if (auto found = colors_.find(index); found != colors_.end()) { + return true; + } + return false; +} + +std::optional RenderPassDescriptor::GetColorAttachmentSize( + size_t index) const { + auto found = colors_.find(index); + + if (found == colors_.end()) { + return std::nullopt; + } + + return found->second.texture->GetSize(); +} + +RenderPassDescriptor& RenderPassDescriptor::SetColorAttachment( + ColorRenderPassAttachment attachment, + size_t index) { + if (attachment) { + colors_[index] = attachment; + } + return *this; +} + +RenderPassDescriptor& RenderPassDescriptor::SetDepthAttachment( + DepthRenderPassAttachment attachment) { + if (attachment) { + depth_ = std::move(attachment); + } + return *this; +} + +RenderPassDescriptor& RenderPassDescriptor::SetStencilAttachment( + StencilRenderPassAttachment attachment) { + if (attachment) { + stencil_ = std::move(attachment); + } + return *this; +} + +} // namespace impeller diff --git a/impeller/playground/playground.mm b/impeller/playground/playground.mm index 8b836e239850d..1825aa1be8b5d 100644 --- a/impeller/playground/playground.mm +++ b/impeller/playground/playground.mm @@ -131,7 +131,7 @@ static void PlaygroundKeyCallback(GLFWwindow* window, } TextureDescriptor color0_desc; - color0_desc.format = PixelFormat::kPixelFormat_B8G8R8A8_UNormInt; + color0_desc.format = PixelFormat::kB8G8R8A8_UNormInt; color0_desc.size = { static_cast(current_drawable.texture.width), static_cast(current_drawable.texture.height)}; @@ -181,7 +181,7 @@ CompressedImage compressed_image( } auto texture_descriptor = TextureDescriptor{}; - texture_descriptor.format = PixelFormat::kPixelFormat_R8G8B8A8_UNormInt; + texture_descriptor.format = PixelFormat::kR8G8B8A8_UNormInt; texture_descriptor.size = image.GetSize(); texture_descriptor.mip_count = 1u; From 717dea8be16262bb5c7ed479acab3fc545d2c2d8 Mon Sep 17 00:00:00 2001 From: Chinmay Garde Date: Sun, 18 Jul 2021 14:38:30 -0700 Subject: [PATCH 146/433] Rename render pass attachment structs. --- impeller/compositor/formats.h | 6 +++--- impeller/compositor/render_pass.mm | 6 +++--- impeller/compositor/render_pass_descriptor.h | 12 ++++++------ impeller/compositor/render_pass_descriptor.mm | 6 +++--- impeller/playground/playground.mm | 2 +- impeller/primitives/primitives_unittests.mm | 2 +- 6 files changed, 17 insertions(+), 17 deletions(-) diff --git a/impeller/compositor/formats.h b/impeller/compositor/formats.h index fac00782edb02..062a890d0e856 100644 --- a/impeller/compositor/formats.h +++ b/impeller/compositor/formats.h @@ -290,15 +290,15 @@ struct RenderPassAttachment { constexpr operator bool() const { return static_cast(texture); } }; -struct ColorRenderPassAttachment : public RenderPassAttachment { +struct RenderPassColorAttachment : public RenderPassAttachment { Color clear_color = Color::BlackTransparent(); }; -struct DepthRenderPassAttachment : public RenderPassAttachment { +struct RenderPassDepthAttachment : public RenderPassAttachment { double clear_depth = 0.0; }; -struct StencilRenderPassAttachment : public RenderPassAttachment { +struct RenderPassStencilAttachment : public RenderPassAttachment { uint32_t clear_stencil = 0; }; diff --git a/impeller/compositor/render_pass.mm b/impeller/compositor/render_pass.mm index 188b6e19b0ba2..6cb6e5876378d 100644 --- a/impeller/compositor/render_pass.mm +++ b/impeller/compositor/render_pass.mm @@ -30,7 +30,7 @@ static bool ConfigureAttachment(const RenderPassAttachment& desc, } static bool ConfigureColorAttachment( - const ColorRenderPassAttachment& desc, + const RenderPassColorAttachment& desc, MTLRenderPassColorAttachmentDescriptor* attachment) { if (!ConfigureAttachment(desc, attachment)) { return false; @@ -40,7 +40,7 @@ static bool ConfigureColorAttachment( } static bool ConfigureDepthAttachment( - const DepthRenderPassAttachment& desc, + const RenderPassDepthAttachment& desc, MTLRenderPassDepthAttachmentDescriptor* attachment) { if (!ConfigureAttachment(desc, attachment)) { return false; @@ -50,7 +50,7 @@ static bool ConfigureDepthAttachment( } static bool ConfigureStencilAttachment( - const StencilRenderPassAttachment& desc, + const RenderPassStencilAttachment& desc, MTLRenderPassStencilAttachmentDescriptor* attachment) { if (!ConfigureAttachment(desc, attachment)) { return false; diff --git a/impeller/compositor/render_pass_descriptor.h b/impeller/compositor/render_pass_descriptor.h index baab8445137d6..a8a3fe93c94b6 100644 --- a/impeller/compositor/render_pass_descriptor.h +++ b/impeller/compositor/render_pass_descriptor.h @@ -25,21 +25,21 @@ class RenderPassDescriptor { std::optional GetColorAttachmentSize(size_t index) const; - RenderPassDescriptor& SetColorAttachment(ColorRenderPassAttachment attachment, + RenderPassDescriptor& SetColorAttachment(RenderPassColorAttachment attachment, size_t index); RenderPassDescriptor& SetDepthAttachment( - DepthRenderPassAttachment attachment); + RenderPassDepthAttachment attachment); RenderPassDescriptor& SetStencilAttachment( - StencilRenderPassAttachment attachment); + RenderPassStencilAttachment attachment); MTLRenderPassDescriptor* ToMTLRenderPassDescriptor() const; private: - std::map colors_; - std::optional depth_; - std::optional stencil_; + std::map colors_; + std::optional depth_; + std::optional stencil_; }; } // namespace impeller diff --git a/impeller/compositor/render_pass_descriptor.mm b/impeller/compositor/render_pass_descriptor.mm index b8802582e87cf..a39523e2828ce 100644 --- a/impeller/compositor/render_pass_descriptor.mm +++ b/impeller/compositor/render_pass_descriptor.mm @@ -31,7 +31,7 @@ } RenderPassDescriptor& RenderPassDescriptor::SetColorAttachment( - ColorRenderPassAttachment attachment, + RenderPassColorAttachment attachment, size_t index) { if (attachment) { colors_[index] = attachment; @@ -40,7 +40,7 @@ } RenderPassDescriptor& RenderPassDescriptor::SetDepthAttachment( - DepthRenderPassAttachment attachment) { + RenderPassDepthAttachment attachment) { if (attachment) { depth_ = std::move(attachment); } @@ -48,7 +48,7 @@ } RenderPassDescriptor& RenderPassDescriptor::SetStencilAttachment( - StencilRenderPassAttachment attachment) { + RenderPassStencilAttachment attachment) { if (attachment) { stencil_ = std::move(attachment); } diff --git a/impeller/playground/playground.mm b/impeller/playground/playground.mm index 1825aa1be8b5d..e70f8237d7521 100644 --- a/impeller/playground/playground.mm +++ b/impeller/playground/playground.mm @@ -136,7 +136,7 @@ static void PlaygroundKeyCallback(GLFWwindow* window, static_cast(current_drawable.texture.width), static_cast(current_drawable.texture.height)}; - ColorRenderPassAttachment color0; + RenderPassColorAttachment color0; color0.texture = std::make_shared(color0_desc, current_drawable.texture); color0.clear_color = Color::SkyBlue(); diff --git a/impeller/primitives/primitives_unittests.mm b/impeller/primitives/primitives_unittests.mm index 6a55d5c0d08c6..3e7400311a4e7 100644 --- a/impeller/primitives/primitives_unittests.mm +++ b/impeller/primitives/primitives_unittests.mm @@ -193,7 +193,7 @@ std::shared_ptr r2t_pass; { - ColorRenderPassAttachment color0; + RenderPassColorAttachment color0; color0.load_action = LoadAction::kClear; color0.store_action = StoreAction::kStore; From 1e04adf004c1d20df0dc328c1f90e648bcfd33ed Mon Sep 17 00:00:00 2001 From: Chinmay Garde Date: Sun, 18 Jul 2021 14:50:03 -0700 Subject: [PATCH 147/433] Better disambiguate attachment descriptors for pipelines and stencils. --- impeller/compositor/formats.h | 20 ++++++++++---------- impeller/compositor/formats_metal.h | 8 ++++---- impeller/compositor/formats_metal.mm | 12 ++++++------ impeller/compositor/pipeline_builder.h | 2 +- impeller/compositor/pipeline_descriptor.h | 22 ++++++++++------------ impeller/compositor/pipeline_descriptor.mm | 14 +++++++------- 6 files changed, 38 insertions(+), 40 deletions(-) diff --git a/impeller/compositor/formats.h b/impeller/compositor/formats.h index 062a890d0e856..2d51ecb71178b 100644 --- a/impeller/compositor/formats.h +++ b/impeller/compositor/formats.h @@ -126,7 +126,7 @@ constexpr size_t BytesPerPixelForPixelFormat(PixelFormat format) { return 0u; } -struct ColorAttachmentDescriptor { +struct PipelineColorAttachment { PixelFormat format = PixelFormat::kUnknown; bool blending_enabled = false; @@ -159,7 +159,7 @@ struct ColorAttachmentDescriptor { std::underlying_type_t write_mask = static_cast(ColorWriteMask::kAll); - constexpr bool operator==(const ColorAttachmentDescriptor& o) const { + constexpr bool operator==(const PipelineColorAttachment& o) const { return format == o.format && // blending_enabled == o.blending_enabled && // src_color_blend_factor == o.src_color_blend_factor && // @@ -217,7 +217,7 @@ enum class StencilOperation { kDecrementWrap, }; -struct DepthAttachmentDescriptor { +struct PipelineDepthAttachment { //---------------------------------------------------------------------------- /// Indicates how to compare the value with that in the depth buffer. /// @@ -227,7 +227,7 @@ struct DepthAttachmentDescriptor { /// bool depth_write_enabled = false; - constexpr bool operator==(const DepthAttachmentDescriptor& o) const { + constexpr bool operator==(const PipelineDepthAttachment& o) const { return depth_compare == o.depth_compare && depth_write_enabled == o.depth_write_enabled; } @@ -237,7 +237,7 @@ struct DepthAttachmentDescriptor { } }; -struct StencilAttachmentDescriptor { +struct PipelineStencilAttachment { //---------------------------------------------------------------------------- /// Indicates the operation to perform between the reference value and the /// value in the stencil buffer. Both values have the read_mask applied to @@ -268,7 +268,7 @@ struct StencilAttachmentDescriptor { /// uint32_t write_mask = ~0; - constexpr bool operator==(const StencilAttachmentDescriptor& o) const { + constexpr bool operator==(const PipelineStencilAttachment& o) const { return stencil_compare == o.stencil_compare && stencil_failure == o.stencil_failure && depth_failure == o.depth_failure && @@ -307,17 +307,17 @@ struct RenderPassStencilAttachment : public RenderPassAttachment { namespace std { template <> -struct hash { +struct hash { constexpr std::size_t operator()( - const impeller::DepthAttachmentDescriptor& des) const { + const impeller::PipelineDepthAttachment& des) const { return des.GetHash(); } }; template <> -struct hash { +struct hash { constexpr std::size_t operator()( - const impeller::StencilAttachmentDescriptor& des) const { + const impeller::PipelineStencilAttachment& des) const { return des.GetHash(); } }; diff --git a/impeller/compositor/formats_metal.h b/impeller/compositor/formats_metal.h index e0acf04462b38..e713b841f2633 100644 --- a/impeller/compositor/formats_metal.h +++ b/impeller/compositor/formats_metal.h @@ -251,12 +251,12 @@ constexpr MTLClearColor ToMTLClearColor(const Color& color) { MTLRenderPipelineColorAttachmentDescriptor* ToMTLRenderPipelineColorAttachmentDescriptor( - ColorAttachmentDescriptor descriptor); + PipelineColorAttachment descriptor); MTLDepthStencilDescriptor* ToMTLDepthStencilDescriptor( - std::optional depth, - std::optional front, - std::optional back); + std::optional depth, + std::optional front, + std::optional back); MTLTextureDescriptor* ToMTLTextureDescriptor(const TextureDescriptor& desc); diff --git a/impeller/compositor/formats_metal.mm b/impeller/compositor/formats_metal.mm index d0bffb5b79f61..b3b7b472a181d 100644 --- a/impeller/compositor/formats_metal.mm +++ b/impeller/compositor/formats_metal.mm @@ -12,7 +12,7 @@ MTLRenderPipelineColorAttachmentDescriptor* ToMTLRenderPipelineColorAttachmentDescriptor( - ColorAttachmentDescriptor descriptor) { + PipelineColorAttachment descriptor) { auto des = [[MTLRenderPipelineColorAttachmentDescriptor alloc] init]; des.pixelFormat = ToMTLPixelFormat(descriptor.format); @@ -35,7 +35,7 @@ } MTLStencilDescriptor* ToMTLStencilDescriptor( - const StencilAttachmentDescriptor& descriptor) { + const PipelineStencilAttachment& descriptor) { auto des = [[MTLStencilDescriptor alloc] init]; des.stencilCompareFunction = ToMTLCompareFunction(descriptor.stencil_compare); des.stencilFailureOperation = @@ -51,11 +51,11 @@ } MTLDepthStencilDescriptor* ToMTLDepthStencilDescriptor( - std::optional depth, - std::optional front, - std::optional back) { + std::optional depth, + std::optional front, + std::optional back) { if (!depth) { - depth = DepthAttachmentDescriptor{ + depth = PipelineDepthAttachment{ // Always pass the depth test. .depth_compare = CompareFunction::kAlways, .depth_write_enabled = false, diff --git a/impeller/compositor/pipeline_builder.h b/impeller/compositor/pipeline_builder.h index cfc110b32b65b..dd5dd22e088c2 100644 --- a/impeller/compositor/pipeline_builder.h +++ b/impeller/compositor/pipeline_builder.h @@ -84,7 +84,7 @@ struct PipelineBuilder { // Configure the sole color attachments pixel format. // TODO(csg): This can be easily reflected but we are sticking to the // convention that the first stage output is the color output. - ColorAttachmentDescriptor color0; + PipelineColorAttachment color0; color0.format = PixelFormat::kB8G8R8A8_UNormInt; desc.SetColorAttachmentDescriptor(0u, std::move(color0)); } diff --git a/impeller/compositor/pipeline_descriptor.h b/impeller/compositor/pipeline_descriptor.h index f81da73fe1d0e..0e60d4585c402 100644 --- a/impeller/compositor/pipeline_descriptor.h +++ b/impeller/compositor/pipeline_descriptor.h @@ -42,20 +42,20 @@ class PipelineDescriptor final : public Comparable { PipelineDescriptor& SetColorAttachmentDescriptor( size_t index, - ColorAttachmentDescriptor desc); + PipelineColorAttachment desc); - const ColorAttachmentDescriptor* GetColorAttachmentDescriptor( + const PipelineColorAttachment* GetColorAttachmentDescriptor( size_t index) const; PipelineDescriptor& SetDepthStencilAttachmentDescriptor( - DepthAttachmentDescriptor desc); + PipelineDepthAttachment desc); PipelineDescriptor& SetStencilAttachmentDescriptors( - StencilAttachmentDescriptor front_and_back); + PipelineStencilAttachment front_and_back); PipelineDescriptor& SetStencilAttachmentDescriptors( - StencilAttachmentDescriptor front, - StencilAttachmentDescriptor back); + PipelineStencilAttachment front, + PipelineStencilAttachment back); PipelineDescriptor& SetDepthPixelFormat(PixelFormat format); @@ -76,15 +76,13 @@ class PipelineDescriptor final : public Comparable { std::string label_; size_t sample_count_ = 1; std::map> entrypoints_; - std::map color_attachment_descriptors_; + std::map color_attachment_descriptors_; std::shared_ptr vertex_descriptor_; PixelFormat depth_pixel_format_ = PixelFormat::kUnknown; PixelFormat stencil_pixel_format_ = PixelFormat::kUnknown; - std::optional depth_attachment_descriptor_; - std::optional - front_stencil_attachment_descriptor_; - std::optional - back_stencil_attachment_descriptor_; + std::optional depth_attachment_descriptor_; + std::optional front_stencil_attachment_descriptor_; + std::optional back_stencil_attachment_descriptor_; }; } // namespace impeller diff --git a/impeller/compositor/pipeline_descriptor.mm b/impeller/compositor/pipeline_descriptor.mm index b74394874464c..91fc29bf0f055 100644 --- a/impeller/compositor/pipeline_descriptor.mm +++ b/impeller/compositor/pipeline_descriptor.mm @@ -89,13 +89,13 @@ PipelineDescriptor& PipelineDescriptor::SetColorAttachmentDescriptor( size_t index, - ColorAttachmentDescriptor desc) { + PipelineColorAttachment desc) { color_attachment_descriptors_[index] = std::move(desc); return *this; } -const ColorAttachmentDescriptor* -PipelineDescriptor::GetColorAttachmentDescriptor(size_t index) const { +const PipelineColorAttachment* PipelineDescriptor::GetColorAttachmentDescriptor( + size_t index) const { auto found = color_attachment_descriptors_.find(index); return found == color_attachment_descriptors_.end() ? nullptr : &found->second; @@ -114,19 +114,19 @@ } PipelineDescriptor& PipelineDescriptor::SetDepthStencilAttachmentDescriptor( - DepthAttachmentDescriptor desc) { + PipelineDepthAttachment desc) { depth_attachment_descriptor_ = desc; return *this; } PipelineDescriptor& PipelineDescriptor::SetStencilAttachmentDescriptors( - StencilAttachmentDescriptor front_and_back) { + PipelineStencilAttachment front_and_back) { return SetStencilAttachmentDescriptors(front_and_back, front_and_back); } PipelineDescriptor& PipelineDescriptor::SetStencilAttachmentDescriptors( - StencilAttachmentDescriptor front, - StencilAttachmentDescriptor back) { + PipelineStencilAttachment front, + PipelineStencilAttachment back) { front_stencil_attachment_descriptor_ = front; back_stencil_attachment_descriptor_ = back; return *this; From 4ec5cae621767b1cec4830b3c7710ea6a6cb73ca Mon Sep 17 00:00:00 2001 From: Chinmay Garde Date: Sun, 18 Jul 2021 14:59:17 -0700 Subject: [PATCH 148/433] Rename vertex descriptor to pipeline vertex descriptor. --- impeller/compositor/command.mm | 3 ++- impeller/compositor/pipeline_builder.h | 4 ++-- impeller/compositor/pipeline_descriptor.h | 6 +++--- impeller/compositor/pipeline_descriptor.mm | 2 +- impeller/compositor/vertex_descriptor.h | 23 +++++++++++++++------- impeller/compositor/vertex_descriptor.mm | 13 ++++++------ 6 files changed, 31 insertions(+), 20 deletions(-) diff --git a/impeller/compositor/command.mm b/impeller/compositor/command.mm index 3e505af69bd68..bc7427eed574e 100644 --- a/impeller/compositor/command.mm +++ b/impeller/compositor/command.mm @@ -11,7 +11,8 @@ namespace impeller { bool Command::BindVertices(const VertexBuffer& buffer) { - vertex_bindings.buffers[VertexDescriptor::kReservedVertexBufferIndex] = + vertex_bindings + .buffers[PipelineVertexDescriptor::kReservedVertexBufferIndex] = buffer.vertex_buffer; index_buffer = buffer.index_buffer; index_count = buffer.index_count; diff --git a/impeller/compositor/pipeline_builder.h b/impeller/compositor/pipeline_builder.h index dd5dd22e088c2..9a84e4e63c310 100644 --- a/impeller/compositor/pipeline_builder.h +++ b/impeller/compositor/pipeline_builder.h @@ -32,7 +32,7 @@ struct PipelineBuilder { using FragmentShader = FragmentShader_; static constexpr size_t kVertexBufferIndex = - VertexDescriptor::kReservedVertexBufferIndex; + PipelineVertexDescriptor::kReservedVertexBufferIndex; //---------------------------------------------------------------------------- /// @brief Create a default pipeline descriptor using the combination @@ -70,7 +70,7 @@ struct PipelineBuilder { // Setup the vertex descriptor from reflected information. { - auto vertex_descriptor = std::make_shared(); + auto vertex_descriptor = std::make_shared(); if (!vertex_descriptor->SetStageInputs( VertexShader::kAllShaderStageInputs)) { FML_LOG(ERROR) << "Could not configure vertex descriptor."; diff --git a/impeller/compositor/pipeline_descriptor.h b/impeller/compositor/pipeline_descriptor.h index 0e60d4585c402..a048da412f502 100644 --- a/impeller/compositor/pipeline_descriptor.h +++ b/impeller/compositor/pipeline_descriptor.h @@ -22,7 +22,7 @@ namespace impeller { class ShaderFunction; -class VertexDescriptor; +class PipelineVertexDescriptor; class PipelineDescriptor final : public Comparable { public: @@ -38,7 +38,7 @@ class PipelineDescriptor final : public Comparable { std::shared_ptr function); PipelineDescriptor& SetVertexDescriptor( - std::shared_ptr vertex_descriptor); + std::shared_ptr vertex_descriptor); PipelineDescriptor& SetColorAttachmentDescriptor( size_t index, @@ -77,7 +77,7 @@ class PipelineDescriptor final : public Comparable { size_t sample_count_ = 1; std::map> entrypoints_; std::map color_attachment_descriptors_; - std::shared_ptr vertex_descriptor_; + std::shared_ptr vertex_descriptor_; PixelFormat depth_pixel_format_ = PixelFormat::kUnknown; PixelFormat stencil_pixel_format_ = PixelFormat::kUnknown; std::optional depth_attachment_descriptor_; diff --git a/impeller/compositor/pipeline_descriptor.mm b/impeller/compositor/pipeline_descriptor.mm index 91fc29bf0f055..9713ee8540739 100644 --- a/impeller/compositor/pipeline_descriptor.mm +++ b/impeller/compositor/pipeline_descriptor.mm @@ -82,7 +82,7 @@ } PipelineDescriptor& PipelineDescriptor::SetVertexDescriptor( - std::shared_ptr vertex_descriptor) { + std::shared_ptr vertex_descriptor) { vertex_descriptor_ = std::move(vertex_descriptor); return *this; } diff --git a/impeller/compositor/vertex_descriptor.h b/impeller/compositor/vertex_descriptor.h index d6c97de75e69e..1fb20ca4e328f 100644 --- a/impeller/compositor/vertex_descriptor.h +++ b/impeller/compositor/vertex_descriptor.h @@ -14,14 +14,23 @@ namespace impeller { -class VertexDescriptor final : public Comparable { +//------------------------------------------------------------------------------ +/// @brief Describes the format and layout of vertices expected by the +/// pipeline. While it is possible to construct these descriptors +/// manually, it would be tedious to do so. These are usually +/// constructed using shader information reflected using +/// `impellerc`. The usage of this class is indirectly via +/// `PipelineBuilder`. +/// +class PipelineVertexDescriptor final + : public Comparable { public: static constexpr size_t kReservedVertexBufferIndex = 30u; // The final slot available. Regular buffer indices go up from 0. - VertexDescriptor(); + PipelineVertexDescriptor(); - virtual ~VertexDescriptor(); + virtual ~PipelineVertexDescriptor(); template bool SetStageInputs( @@ -32,13 +41,13 @@ class VertexDescriptor final : public Comparable { bool SetStageInputs(const ShaderStageIOSlot* const stage_inputs[], size_t count); - MTLVertexDescriptor* GetMTLVertexDescriptor() const; - //| Comparable| std::size_t GetHash() const override; // |Comparable| - bool IsEqual(const VertexDescriptor& other) const override; + bool IsEqual(const PipelineVertexDescriptor& other) const override; + + MTLVertexDescriptor* GetMTLVertexDescriptor() const; private: struct StageInput { @@ -56,7 +65,7 @@ class VertexDescriptor final : public Comparable { }; std::vector stage_inputs_; - FML_DISALLOW_COPY_AND_ASSIGN(VertexDescriptor); + FML_DISALLOW_COPY_AND_ASSIGN(PipelineVertexDescriptor); }; } // namespace impeller diff --git a/impeller/compositor/vertex_descriptor.mm b/impeller/compositor/vertex_descriptor.mm index 9707db0e7eab5..6a729b4df1e35 100644 --- a/impeller/compositor/vertex_descriptor.mm +++ b/impeller/compositor/vertex_descriptor.mm @@ -8,9 +8,9 @@ namespace impeller { -VertexDescriptor::VertexDescriptor() = default; +PipelineVertexDescriptor::PipelineVertexDescriptor() = default; -VertexDescriptor::~VertexDescriptor() = default; +PipelineVertexDescriptor::~PipelineVertexDescriptor() = default; static MTLVertexFormat ReadStageInputFormat(const ShaderStageIOSlot& input) { if (input.columns != 1) { @@ -168,7 +168,7 @@ static MTLVertexFormat ReadStageInputFormat(const ShaderStageIOSlot& input) { } } -bool VertexDescriptor::SetStageInputs( +bool PipelineVertexDescriptor::SetStageInputs( const ShaderStageIOSlot* const stage_inputs[], size_t count) { stage_inputs_.clear(); @@ -188,7 +188,7 @@ static MTLVertexFormat ReadStageInputFormat(const ShaderStageIOSlot& input) { return true; } -MTLVertexDescriptor* VertexDescriptor::GetMTLVertexDescriptor() const { +MTLVertexDescriptor* PipelineVertexDescriptor::GetMTLVertexDescriptor() const { auto descriptor = [MTLVertexDescriptor vertexDescriptor]; const size_t vertex_buffer_index = kReservedVertexBufferIndex; @@ -214,7 +214,7 @@ static MTLVertexFormat ReadStageInputFormat(const ShaderStageIOSlot& input) { } // |Comparable| -std::size_t VertexDescriptor::GetHash() const { +std::size_t PipelineVertexDescriptor::GetHash() const { auto seed = fml::HashCombine(); for (const auto& input : stage_inputs_) { fml::HashCombineSeed(seed, input.location, input.format); @@ -223,7 +223,8 @@ static MTLVertexFormat ReadStageInputFormat(const ShaderStageIOSlot& input) { } // |Comparable| -bool VertexDescriptor::IsEqual(const VertexDescriptor& other) const { +bool PipelineVertexDescriptor::IsEqual( + const PipelineVertexDescriptor& other) const { return stage_inputs_ == other.stage_inputs_; } From c6a42563039404c40988dbcf688b875e0be8acff Mon Sep 17 00:00:00 2001 From: Chinmay Garde Date: Mon, 19 Jul 2021 13:56:08 -0700 Subject: [PATCH 149/433] Add some READMEs. --- impeller/base/README.md | 4 ++++ impeller/compiler/README.md | 4 ++++ impeller/compositor/render_pass.mm | 1 + impeller/fixtures/README.md | 4 ++++ impeller/geometry/README.md | 4 ++++ impeller/image/README.md | 4 ++++ impeller/playground/README.md | 4 ++++ 7 files changed, 25 insertions(+) create mode 100644 impeller/base/README.md create mode 100644 impeller/compiler/README.md create mode 100644 impeller/fixtures/README.md create mode 100644 impeller/geometry/README.md create mode 100644 impeller/image/README.md create mode 100644 impeller/playground/README.md diff --git a/impeller/base/README.md b/impeller/base/README.md new file mode 100644 index 0000000000000..16aa5184f3d6d --- /dev/null +++ b/impeller/base/README.md @@ -0,0 +1,4 @@ +# The Impeller Base Library +--------------------------- + +Contains a number of utilities that should probably go in the base library in the buildroot but whose use is not extensive enough to warrant a move yet. diff --git a/impeller/compiler/README.md b/impeller/compiler/README.md new file mode 100644 index 0000000000000..de117bac8c482 --- /dev/null +++ b/impeller/compiler/README.md @@ -0,0 +1,4 @@ +# The Impeller Shader Compiler & Reflector +------------------------------------------ + +Host side tooling that consumes [GLSL 4.60 (Core Profile)](https://www.khronos.org/registry/OpenGL/specs/gl/GLSLangSpec.4.60.pdf) shaders and generates libraries suitable for consumption by an Impeller backend. Along with said libraries, the reflector generates code and meta-data to construct rendering and compute pipelines at runtime. diff --git a/impeller/compositor/render_pass.mm b/impeller/compositor/render_pass.mm index 6cb6e5876378d..2aeb1e6a2ab7b 100644 --- a/impeller/compositor/render_pass.mm +++ b/impeller/compositor/render_pass.mm @@ -380,6 +380,7 @@ static bool Bind(PassBindingsCache& pass, } FML_DCHECK(command.index_count * sizeof(uint32_t) == command.index_buffer.range.length); + // Returns void. All error checking must be done by this point. [pass drawIndexedPrimitives:ToMTLPrimitiveType(command.primitive_type) indexCount:command.index_count indexType:MTLIndexTypeUInt32 diff --git a/impeller/fixtures/README.md b/impeller/fixtures/README.md new file mode 100644 index 0000000000000..de960be9cb2ba --- /dev/null +++ b/impeller/fixtures/README.md @@ -0,0 +1,4 @@ +# The Impeller Fixtures Set +--------------------------- + +Unlike other targets in the buildroot, all Impeller unit-tests use the same fixture set and are invoked using a single test harness (`impeller_unittest`). This is for convenience but also to make working with shader libraries easier. diff --git a/impeller/geometry/README.md b/impeller/geometry/README.md new file mode 100644 index 0000000000000..b14384fac0a3c --- /dev/null +++ b/impeller/geometry/README.md @@ -0,0 +1,4 @@ +# The Impeller Geometry Library +------------------------------- + +Set of utilities used by most graphics operations. While the utilities themselves are rendering backend agnostic, the layout and packing of the various POD structs is arranged such that these can be copied into device memory directly. The supported operations also mimic GLSL to some extent. For this reason, the Impeller shader compiler and reflector uses these utilities in generated code. diff --git a/impeller/image/README.md b/impeller/image/README.md new file mode 100644 index 0000000000000..8745eb6f47ff8 --- /dev/null +++ b/impeller/image/README.md @@ -0,0 +1,4 @@ +The Impeller Image Library +-------------------------- + +Set of utilities for working with texture information. The library is indepenent of the rendering subsystem. diff --git a/impeller/playground/README.md b/impeller/playground/README.md new file mode 100644 index 0000000000000..2e93f644a3315 --- /dev/null +++ b/impeller/playground/README.md @@ -0,0 +1,4 @@ +The Impeller Playground +----------------------- + +An extension of the testing fixtures set, provides utilities for interactive experimentation with the Impeller rendering subsystem. One the test author is satisfied with the behavior of component as verified in the playground, pixel test assertions can be added to before committing the new test case. Meant to provide a gentle-er on-ramp to testing Impeller components. The WSI in the playground allows for points at which third-party profiling and instrumentation tools can be used to examine isolated test cases. From cd27980b11388b6ebe4aa0617e2b7d00a80e21d0 Mon Sep 17 00:00:00 2001 From: Chinmay Garde Date: Mon, 19 Jul 2021 14:58:55 -0700 Subject: [PATCH 150/433] Merge shader_glue into compositor. --- impeller/BUILD.gn | 2 - impeller/base/README.md | 1 - impeller/compiler/README.md | 1 - impeller/compiler/code_gen_template.h | 2 +- impeller/compositor/BUILD.gn | 3 +- impeller/compositor/command.h | 2 +- impeller/compositor/pipeline_descriptor.h | 2 +- impeller/compositor/render_pass.mm | 2 +- impeller/compositor/shader_function.h | 2 +- impeller/compositor/vertex_descriptor.h | 2 +- impeller/fixtures/README.md | 1 - impeller/geometry/README.md | 1 - impeller/image/README.md | 1 - impeller/playground/README.md | 1 - impeller/shader_glue/BUILD.gn | 31 --------- impeller/shader_glue/shader_types.cc | 11 ---- impeller/shader_glue/shader_types.h | 77 ----------------------- impeller/tools/impeller.gni | 2 +- 18 files changed, 9 insertions(+), 135 deletions(-) delete mode 100644 impeller/shader_glue/BUILD.gn delete mode 100644 impeller/shader_glue/shader_types.cc delete mode 100644 impeller/shader_glue/shader_types.h diff --git a/impeller/BUILD.gn b/impeller/BUILD.gn index 15ce867a7cfa6..8ba0af5630e97 100644 --- a/impeller/BUILD.gn +++ b/impeller/BUILD.gn @@ -15,7 +15,6 @@ group("impeller") { "geometry", "image", "primitives", - "shader_glue", ] } @@ -32,6 +31,5 @@ executable("impeller_unittests") { "image:image_unittests", "playground", "primitives:primitives_unittests", - "shader_glue:shader_glue_unittests", ] } diff --git a/impeller/base/README.md b/impeller/base/README.md index 16aa5184f3d6d..563bbb0bd10a4 100644 --- a/impeller/base/README.md +++ b/impeller/base/README.md @@ -1,4 +1,3 @@ # The Impeller Base Library ---------------------------- Contains a number of utilities that should probably go in the base library in the buildroot but whose use is not extensive enough to warrant a move yet. diff --git a/impeller/compiler/README.md b/impeller/compiler/README.md index de117bac8c482..6430d4cb0dfb8 100644 --- a/impeller/compiler/README.md +++ b/impeller/compiler/README.md @@ -1,4 +1,3 @@ # The Impeller Shader Compiler & Reflector ------------------------------------------- Host side tooling that consumes [GLSL 4.60 (Core Profile)](https://www.khronos.org/registry/OpenGL/specs/gl/GLSLangSpec.4.60.pdf) shaders and generates libraries suitable for consumption by an Impeller backend. Along with said libraries, the reflector generates code and meta-data to construct rendering and compute pipelines at runtime. diff --git a/impeller/compiler/code_gen_template.h b/impeller/compiler/code_gen_template.h index 4148ccb068deb..a5a01b0165035 100644 --- a/impeller/compiler/code_gen_template.h +++ b/impeller/compiler/code_gen_template.h @@ -18,8 +18,8 @@ constexpr std::string_view kReflectionHeaderTemplate = #include "impeller/compositor/buffer_view.h" // nogncheck #include "impeller/compositor/command.h" // nogncheck #include "impeller/compositor/sampler.h" // nogncheck +#include "impeller/compositor/shader_types.h" // nogncheck #include "impeller/compositor/texture.h" // nogncheck -#include "impeller/shader_glue/shader_types.h" // nogncheck namespace impeller { diff --git a/impeller/compositor/BUILD.gn b/impeller/compositor/BUILD.gn index a82dff11109d8..891141ee0a725 100644 --- a/impeller/compositor/BUILD.gn +++ b/impeller/compositor/BUILD.gn @@ -54,6 +54,8 @@ impeller_component("compositor") { "shader_function.mm", "shader_library.h", "shader_library.mm", + "shader_types.cc", + "shader_types.h", "surface.h", "surface.mm", "texture.h", @@ -72,7 +74,6 @@ impeller_component("compositor") { "../base", "../geometry", "../image", - "../shader_glue", ] frameworks = [ "Metal.framework" ] diff --git a/impeller/compositor/command.h b/impeller/compositor/command.h index f9b3b4f1ffbfd..6f8dcd640f71f 100644 --- a/impeller/compositor/command.h +++ b/impeller/compositor/command.h @@ -14,9 +14,9 @@ #include "impeller/compositor/formats.h" #include "impeller/compositor/pipeline.h" #include "impeller/compositor/sampler.h" +#include "impeller/compositor/shader_types.h" #include "impeller/compositor/texture.h" #include "impeller/compositor/vertex_buffer.h" -#include "impeller/shader_glue/shader_types.h" namespace impeller { diff --git a/impeller/compositor/pipeline_descriptor.h b/impeller/compositor/pipeline_descriptor.h index a048da412f502..1c297f7c327d3 100644 --- a/impeller/compositor/pipeline_descriptor.h +++ b/impeller/compositor/pipeline_descriptor.h @@ -17,7 +17,7 @@ #include "flutter/fml/macros.h" #include "impeller/compositor/comparable.h" #include "impeller/compositor/formats.h" -#include "impeller/shader_glue/shader_types.h" +#include "impeller/compositor/shader_types.h" namespace impeller { diff --git a/impeller/compositor/render_pass.mm b/impeller/compositor/render_pass.mm index 2aeb1e6a2ab7b..7d6b1fae02982 100644 --- a/impeller/compositor/render_pass.mm +++ b/impeller/compositor/render_pass.mm @@ -13,7 +13,7 @@ #include "impeller/compositor/device_buffer.h" #include "impeller/compositor/formats_metal.h" #include "impeller/compositor/sampler.h" -#include "impeller/shader_glue/shader_types.h" +#include "impeller/compositor/shader_types.h" namespace impeller { diff --git a/impeller/compositor/shader_function.h b/impeller/compositor/shader_function.h index c78601673dcf7..f3230a72bde14 100644 --- a/impeller/compositor/shader_function.h +++ b/impeller/compositor/shader_function.h @@ -9,7 +9,7 @@ #include "flutter/fml/hash_combine.h" #include "flutter/fml/macros.h" #include "impeller/compositor/comparable.h" -#include "impeller/shader_glue/shader_types.h" +#include "impeller/compositor/shader_types.h" namespace impeller { diff --git a/impeller/compositor/vertex_descriptor.h b/impeller/compositor/vertex_descriptor.h index 1fb20ca4e328f..62ddbe35dba49 100644 --- a/impeller/compositor/vertex_descriptor.h +++ b/impeller/compositor/vertex_descriptor.h @@ -10,7 +10,7 @@ #include "flutter/fml/macros.h" #include "impeller/compositor/comparable.h" -#include "impeller/shader_glue/shader_types.h" +#include "impeller/compositor/shader_types.h" namespace impeller { diff --git a/impeller/fixtures/README.md b/impeller/fixtures/README.md index de960be9cb2ba..362166c02e0ff 100644 --- a/impeller/fixtures/README.md +++ b/impeller/fixtures/README.md @@ -1,4 +1,3 @@ # The Impeller Fixtures Set ---------------------------- Unlike other targets in the buildroot, all Impeller unit-tests use the same fixture set and are invoked using a single test harness (`impeller_unittest`). This is for convenience but also to make working with shader libraries easier. diff --git a/impeller/geometry/README.md b/impeller/geometry/README.md index b14384fac0a3c..eb154c30eeb88 100644 --- a/impeller/geometry/README.md +++ b/impeller/geometry/README.md @@ -1,4 +1,3 @@ # The Impeller Geometry Library -------------------------------- Set of utilities used by most graphics operations. While the utilities themselves are rendering backend agnostic, the layout and packing of the various POD structs is arranged such that these can be copied into device memory directly. The supported operations also mimic GLSL to some extent. For this reason, the Impeller shader compiler and reflector uses these utilities in generated code. diff --git a/impeller/image/README.md b/impeller/image/README.md index 8745eb6f47ff8..b1400c70d8d0a 100644 --- a/impeller/image/README.md +++ b/impeller/image/README.md @@ -1,4 +1,3 @@ The Impeller Image Library --------------------------- Set of utilities for working with texture information. The library is indepenent of the rendering subsystem. diff --git a/impeller/playground/README.md b/impeller/playground/README.md index 2e93f644a3315..d5d11dd30492a 100644 --- a/impeller/playground/README.md +++ b/impeller/playground/README.md @@ -1,4 +1,3 @@ The Impeller Playground ------------------------ An extension of the testing fixtures set, provides utilities for interactive experimentation with the Impeller rendering subsystem. One the test author is satisfied with the behavior of component as verified in the playground, pixel test assertions can be added to before committing the new test case. Meant to provide a gentle-er on-ramp to testing Impeller components. The WSI in the playground allows for points at which third-party profiling and instrumentation tools can be used to examine isolated test cases. diff --git a/impeller/shader_glue/BUILD.gn b/impeller/shader_glue/BUILD.gn deleted file mode 100644 index 259ab959de89a..0000000000000 --- a/impeller/shader_glue/BUILD.gn +++ /dev/null @@ -1,31 +0,0 @@ -# Copyright 2013 The Flutter Authors. All rights reserved. -# Use of this source code is governed by a BSD-style license that can be -# found in the LICENSE file. - -import("//flutter/impeller/tools/impeller.gni") - -config("shader_glue_public_config") { - include_dirs = [ "." ] -} - -impeller_component("shader_glue") { - public_configs = [ ":shader_glue_public_config" ] - - public = [ "shader_types.h" ] - - sources = [ - "shader_types.cc", - "shader_types.h", - ] - - deps = [ "../geometry" ] -} - -impeller_component("shader_glue_unittests") { - testonly = true - sources = [] - deps = [ - ":shader_glue", - "//flutter/testing", - ] -} diff --git a/impeller/shader_glue/shader_types.cc b/impeller/shader_glue/shader_types.cc deleted file mode 100644 index e616cfecc7f57..0000000000000 --- a/impeller/shader_glue/shader_types.cc +++ /dev/null @@ -1,11 +0,0 @@ -// Copyright 2013 The Flutter Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#include "impeller/shader_glue/shader_types.h" - -namespace impeller { - -// - -} // namespace impeller diff --git a/impeller/shader_glue/shader_types.h b/impeller/shader_glue/shader_types.h deleted file mode 100644 index 9045829681720..0000000000000 --- a/impeller/shader_glue/shader_types.h +++ /dev/null @@ -1,77 +0,0 @@ -// Copyright 2013 The Flutter Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#pragma once - -#include -#include -#include - -#include "impeller/geometry/matrix.h" - -namespace impeller { - -enum class ShaderStage { - kUnknown, - kVertex, - kFragment, -}; - -enum class ShaderType { - kUnknown, - kVoid, - kBoolean, - kSignedByte, - kUnsignedByte, - kSignedShort, - kUnsignedShort, - kSignedInt, - kUnsignedInt, - kSignedInt64, - kUnsignedInt64, - kAtomicCounter, - kHalfFloat, - kFloat, - kDouble, - kStruct, - kImage, - kSampledImage, - kSampler, -}; - -template -struct ShaderUniformSlot { - using Type = T; - const char* name; - size_t binding; -}; - -struct ShaderStageIOSlot { - const char* name; - size_t location; - size_t set; - size_t binding; - ShaderType type; - size_t bit_width; - size_t vec_size; - size_t columns; -}; - -struct SampledImageSlot { - const char* name; - size_t texture_index; - size_t sampler_index; - - constexpr bool HasTexture() const { return texture_index < 32u; } - - constexpr bool HasSampler() const { return sampler_index < 32u; } -}; - -template -struct Padding { - private: - uint8_t pad_[Size]; -}; - -} // namespace impeller diff --git a/impeller/tools/impeller.gni b/impeller/tools/impeller.gni index 9d7b1e3ec1ddf..4af9af2666f8d 100644 --- a/impeller/tools/impeller.gni +++ b/impeller/tools/impeller.gni @@ -169,7 +169,7 @@ template("impeller_shaders") { deps = [ ":$impellerc_target_name", - "//flutter/impeller/shader_glue", + "//flutter/impeller/compositor", ] } From e5efe70455a897682c3ea8c57e7a5d64ec3b1b25 Mon Sep 17 00:00:00 2001 From: Chinmay Garde Date: Mon, 19 Jul 2021 15:13:57 -0700 Subject: [PATCH 151/433] Move shader types into compositor and document format convention. --- impeller/compositor/formats.h | 28 +++++++++++ impeller/compositor/shader_types.cc | 11 +++++ impeller/compositor/shader_types.h | 77 +++++++++++++++++++++++++++++ impeller/geometry/matrix.cc | 2 +- 4 files changed, 117 insertions(+), 1 deletion(-) create mode 100644 impeller/compositor/shader_types.cc create mode 100644 impeller/compositor/shader_types.h diff --git a/impeller/compositor/formats.h b/impeller/compositor/formats.h index 2d51ecb71178b..d694bfbbb76f2 100644 --- a/impeller/compositor/formats.h +++ b/impeller/compositor/formats.h @@ -17,12 +17,40 @@ namespace impeller { class Texture; +//------------------------------------------------------------------------------ +/// @brief The Pixel formats supported by Impeller. The naming convention +/// denotes the usage of the component, the bit width of that +/// component, and then one or more qualifiers to its +/// interpretation. +/// +/// For instance, `kR8G8B8A8_UNormInt_SRGB` is a 32 bits-per-pixel +/// format ordered in RGBA with 8 bits per component with each +/// component expressed as an unsigned normalized integer and a +/// conversion from sRGB to linear color space. +/// +/// Key: +/// R -> Red Component +/// G -> Green Component +/// B -> Blue Component +/// D -> Depth Component +/// S -> Stencil Component +/// U -> Unsigned (Lack of this denotes a signed component) +/// Norm -> Normalized +/// SRGB -> sRGB to linear interpretation +/// +/// While the effective bit width of the pixel can be determined by +/// adding up the widths of each component, only the non-esoteric +/// formats are tightly packed. Do not assume tight packing for the +/// esoteric formats and use blit passes to convert to a +/// non-esoteric pass. +/// enum class PixelFormat { kUnknown, kR8G8B8A8_UNormInt, kR8G8B8A8_UNormInt_SRGB, kB8G8R8A8_UNormInt, kB8G8R8A8_UNormInt_SRGB, + // Esoteric formats only used as render targets. kD32_Float_S8_UNormInt, }; diff --git a/impeller/compositor/shader_types.cc b/impeller/compositor/shader_types.cc new file mode 100644 index 0000000000000..7cc75876667b7 --- /dev/null +++ b/impeller/compositor/shader_types.cc @@ -0,0 +1,11 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "impeller/compositor/shader_types.h" + +namespace impeller { + +// + +} // namespace impeller diff --git a/impeller/compositor/shader_types.h b/impeller/compositor/shader_types.h new file mode 100644 index 0000000000000..9045829681720 --- /dev/null +++ b/impeller/compositor/shader_types.h @@ -0,0 +1,77 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#pragma once + +#include +#include +#include + +#include "impeller/geometry/matrix.h" + +namespace impeller { + +enum class ShaderStage { + kUnknown, + kVertex, + kFragment, +}; + +enum class ShaderType { + kUnknown, + kVoid, + kBoolean, + kSignedByte, + kUnsignedByte, + kSignedShort, + kUnsignedShort, + kSignedInt, + kUnsignedInt, + kSignedInt64, + kUnsignedInt64, + kAtomicCounter, + kHalfFloat, + kFloat, + kDouble, + kStruct, + kImage, + kSampledImage, + kSampler, +}; + +template +struct ShaderUniformSlot { + using Type = T; + const char* name; + size_t binding; +}; + +struct ShaderStageIOSlot { + const char* name; + size_t location; + size_t set; + size_t binding; + ShaderType type; + size_t bit_width; + size_t vec_size; + size_t columns; +}; + +struct SampledImageSlot { + const char* name; + size_t texture_index; + size_t sampler_index; + + constexpr bool HasTexture() const { return texture_index < 32u; } + + constexpr bool HasSampler() const { return sampler_index < 32u; } +}; + +template +struct Padding { + private: + uint8_t pad_[Size]; +}; + +} // namespace impeller diff --git a/impeller/geometry/matrix.cc b/impeller/geometry/matrix.cc index 662987053c735..420be5fd6dcc9 100644 --- a/impeller/geometry/matrix.cc +++ b/impeller/geometry/matrix.cc @@ -194,7 +194,7 @@ Scalar Matrix::GetDeterminant() const { } /* - * Adapted for Radar from Graphics Gems: + * Adapted for Impeller from Graphics Gems: * http://www.realtimerendering.com/resources/GraphicsGems/gemsii/unmatrix.c */ Matrix::DecompositionResult Matrix::Decompose() const { From 74a3ac926598a3597e828dc2ba98c2d5d462f076 Mon Sep 17 00:00:00 2001 From: Chinmay Garde Date: Tue, 20 Jul 2021 13:00:51 -0700 Subject: [PATCH 152/433] Start stubbing out the aiks interfaces. --- impeller/aiks/BUILD.gn | 28 +++++++++++ impeller/aiks/canvas.cc | 49 ++++++++++++++++++ impeller/aiks/canvas.h | 44 ++++++++++++++++ impeller/aiks/paint.cc | 17 +++++++ impeller/aiks/paint.h | 27 ++++++++++ impeller/compositor/texture_descriptor.h | 2 +- impeller/entity/entity.h | 2 +- impeller/geometry/constants.cc | 0 impeller/geometry/constants.h | 46 +++++++++++++++++ impeller/geometry/geometry_unittests.cc | 22 +++++--- impeller/geometry/matrix.h | 29 +++++------ impeller/geometry/scalar.h | 24 +++++++++ impeller/image/BUILD.gn | 4 +- impeller/image/compressed_image.cc | 18 +++---- impeller/image/compressed_image.h | 4 +- .../image/{image.cc => decompressed_image.cc} | 50 ++++++++++--------- .../image/{image.h => decompressed_image.h} | 14 +++--- impeller/primitives/primitives_unittests.mm | 2 +- 18 files changed, 314 insertions(+), 68 deletions(-) create mode 100644 impeller/aiks/BUILD.gn create mode 100644 impeller/aiks/canvas.cc create mode 100644 impeller/aiks/canvas.h create mode 100644 impeller/aiks/paint.cc create mode 100644 impeller/aiks/paint.h create mode 100644 impeller/geometry/constants.cc create mode 100644 impeller/geometry/constants.h rename impeller/image/{image.cc => decompressed_image.cc} (65%) rename impeller/image/{image.h => decompressed_image.h} (75%) diff --git a/impeller/aiks/BUILD.gn b/impeller/aiks/BUILD.gn new file mode 100644 index 0000000000000..c2276fe759793 --- /dev/null +++ b/impeller/aiks/BUILD.gn @@ -0,0 +1,28 @@ +# Copyright 2013 The Flutter Authors. All rights reserved. +# Use of this source code is governed by a BSD-style license that can be +# found in the LICENSE file. + +import("../tools/impeller.gni") + +impeller_component("aiks") { + sources = [ + "canvas.cc", + "canvas.h", + "paint.cc", + "paint.h", + ] + + public_deps = [ + ":base", + ":entity", + ] +} + +impeller_component("aiks_unittests") { + testonly = true + sources = [] + deps = [ + ":aiks", + "//flutter/testing", + ] +} diff --git a/impeller/aiks/canvas.cc b/impeller/aiks/canvas.cc new file mode 100644 index 0000000000000..eb34836057b46 --- /dev/null +++ b/impeller/aiks/canvas.cc @@ -0,0 +1,49 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "impeller/aiks/canvas.h" + +#include "flutter/fml/logging.h" + +namespace impeller { + +Canvas::Canvas() { + xformation_stack_.push(Matrix{}); +} + +Canvas::~Canvas() = default; + +void Canvas::Save() { + FML_DCHECK(xformation_stack_.size() > 0); + xformation_stack_.push(xformation_stack_.top()); +} + +bool Canvas::Restore() { + FML_DCHECK(xformation_stack_.size() > 0); + if (xformation_stack_.size() == 1) { + return false; + } + xformation_stack_.pop(); + return true; +} + +void Canvas::Concat(const Matrix& xformation) { + xformation_stack_.top() = xformation_stack_.top() * xformation; +} + +void Canvas::Translate(const Size& offset) { + Concat(Matrix::MakeTranslation(offset)); +} + +void Canvas::Scale(const Size& scale) { + Concat(Matrix::MakeScale(scale)); +} + +void Canvas::Rotate(Radians radians) { + Concat(Matrix::MakeRotationZ(radians)); +} + +void Canvas::DrawPath(Path path, Paint paint) {} + +} // namespace impeller diff --git a/impeller/aiks/canvas.h b/impeller/aiks/canvas.h new file mode 100644 index 0000000000000..c4e928d574909 --- /dev/null +++ b/impeller/aiks/canvas.h @@ -0,0 +1,44 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#pragma once + +#include + +#include "flutter/fml/macros.h" +#include "impeller/aiks/paint.h" +#include "impeller/geometry/matrix.h" +#include "impeller/geometry/path.h" + +namespace impeller { + +class Canvas { + public: + Canvas(); + + ~Canvas(); + + void Save(); + + bool Restore(); + + size_t GetSaveCount() const; + + void Concat(const Matrix& xformation); + + void Translate(const Size& offset); + + void Scale(const Size& scale); + + void Rotate(Radians radians); + + void DrawPath(Path path, Paint paint); + + private: + std::stack xformation_stack_; + + FML_DISALLOW_COPY_AND_ASSIGN(Canvas); +}; + +} // namespace impeller diff --git a/impeller/aiks/paint.cc b/impeller/aiks/paint.cc new file mode 100644 index 0000000000000..a9613dbd63aba --- /dev/null +++ b/impeller/aiks/paint.cc @@ -0,0 +1,17 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "impeller/aiks/paint.h" + +namespace impeller { + +Paint::Paint() = default; + +Paint::~Paint() = default; + +void Paint::SetColor(Color color) { + color_ = std::move(color); +} + +} // namespace impeller diff --git a/impeller/aiks/paint.h b/impeller/aiks/paint.h new file mode 100644 index 0000000000000..ae72142cb94af --- /dev/null +++ b/impeller/aiks/paint.h @@ -0,0 +1,27 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#pragma once + +#include "flutter/fml/macros.h" + +#include "impeller/geometry/color.h" + +namespace impeller { + +class Paint { + public: + Paint(); + + ~Paint(); + + void SetColor(Color color); + + private: + Color color_; + + FML_DISALLOW_COPY_AND_ASSIGN(Paint); +}; + +} // namespace impeller diff --git a/impeller/compositor/texture_descriptor.h b/impeller/compositor/texture_descriptor.h index fc647cbbb93c2..c5ba247664aab 100644 --- a/impeller/compositor/texture_descriptor.h +++ b/impeller/compositor/texture_descriptor.h @@ -8,7 +8,7 @@ #include "impeller/compositor/formats.h" #include "impeller/geometry/size.h" -#include "impeller/image/image.h" +#include "impeller/image/decompressed_image.h" namespace impeller { diff --git a/impeller/entity/entity.h b/impeller/entity/entity.h index 1ab3047e15e52..9554cc21e68d4 100644 --- a/impeller/entity/entity.h +++ b/impeller/entity/entity.h @@ -8,7 +8,7 @@ #include "impeller/geometry/matrix.h" #include "impeller/geometry/path.h" #include "impeller/geometry/rect.h" -#include "impeller/image/image.h" +#include "impeller/image/decompressed_image.h" namespace impeller { diff --git a/impeller/geometry/constants.cc b/impeller/geometry/constants.cc new file mode 100644 index 0000000000000..e69de29bb2d1d diff --git a/impeller/geometry/constants.h b/impeller/geometry/constants.h new file mode 100644 index 0000000000000..8385b7eccb06e --- /dev/null +++ b/impeller/geometry/constants.h @@ -0,0 +1,46 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +namespace impeller { + +// e +constexpr float kE = 2.7182818284590452354; + +// log_2 e +constexpr float kLog2_E = 1.4426950408889634074; + +// log_10 e +constexpr float kLog10_E = 0.43429448190325182765; + +// log_e 2 +constexpr float klogE_2 = 0.69314718055994530942; + +// log_e 10 +constexpr float klogE_10 = 2.30258509299404568402; + +// pi */ +constexpr float kPi = 3.14159265358979323846; + +// pi/2 +constexpr float kPiOver2 = 1.57079632679489661923; + +// pi/4 +constexpr float kPiOver4 = 0.78539816339744830962; + +// 1/pi +constexpr float k1OverPi = 0.31830988618379067154; + +// 2/pi +constexpr float k2OverPi = 0.63661977236758134308; + +// 2/sqrt(pi) +constexpr float k2OverSqrtPi = 1.12837916709551257390; + +// sqrt(2) +constexpr float kSqrt2 = 1.41421356237309504880; + +// 1/sqrt(2) +constexpr float k1OverSqrt2 = 0.70710678118654752440; + +} // namespace impeller diff --git a/impeller/geometry/geometry_unittests.cc b/impeller/geometry/geometry_unittests.cc index 31694ed0065f6..13b39bd34a1d0 100644 --- a/impeller/geometry/geometry_unittests.cc +++ b/impeller/geometry/geometry_unittests.cc @@ -14,7 +14,7 @@ namespace impeller { namespace testing { TEST(GeometryTest, RotationMatrix) { - auto rotation = Matrix::MakeRotationZ(M_PI_4); + auto rotation = Matrix::MakeRotationZ(Radians{M_PI_4}); auto expect = Matrix{0.707, 0.707, 0, 0, // -0.707, 0.707, 0, 0, // 0, 0, 1, 0, // @@ -23,7 +23,7 @@ TEST(GeometryTest, RotationMatrix) { } TEST(GeometryTest, InvertMultMatrix) { - auto rotation = Matrix::MakeRotationZ(M_PI_4); + auto rotation = Matrix::MakeRotationZ(Radians{M_PI_4}); auto invert = rotation.Invert(); auto expect = Matrix{0.707, -0.707, 0, 0, // 0.707, 0.707, 0, 0, // @@ -33,7 +33,7 @@ TEST(GeometryTest, InvertMultMatrix) { } TEST(GeometryTest, MutliplicationMatrix) { - auto rotation = Matrix::MakeRotationZ(M_PI_4); + auto rotation = Matrix::MakeRotationZ(Radians{M_PI_4}); auto invert = rotation.Invert(); ASSERT_MATRIX_NEAR(rotation * invert, Matrix{}); } @@ -60,7 +60,7 @@ TEST(GeometryTest, InvertMatrix) { } TEST(GeometryTest, TestDecomposition) { - auto rotated = Matrix::MakeRotationZ(M_PI_4); + auto rotated = Matrix::MakeRotationZ(Radians{M_PI_4}); auto result = rotated.Decompose(); @@ -73,7 +73,7 @@ TEST(GeometryTest, TestDecomposition) { } TEST(GeometryTest, TestDecomposition2) { - auto rotated = Matrix::MakeRotationZ(M_PI_4); + auto rotated = Matrix::MakeRotationZ(Radians{M_PI_4}); auto scaled = Matrix::MakeScale({2.0, 3.0, 1.0}); auto translated = Matrix::MakeTranslation({-200, 750, 20}); @@ -100,7 +100,7 @@ TEST(GeometryTest, TestRecomposition) { /* * Decomposition. */ - auto rotated = Matrix::MakeRotationZ(M_PI_4); + auto rotated = Matrix::MakeRotationZ(Radians{M_PI_4}); auto result = rotated.Decompose(); @@ -120,7 +120,7 @@ TEST(GeometryTest, TestRecomposition) { TEST(GeometryTest, TestRecomposition2) { auto matrix = Matrix::MakeTranslation({100, 100, 100}) * - Matrix::MakeRotationZ(M_PI_4) * + Matrix::MakeRotationZ(Radians{M_PI_4}) * Matrix::MakeScale({2.0, 2.0, 2.0}); auto result = matrix.Decompose(); @@ -265,5 +265,13 @@ TEST(GeometryTest, CanConvertTTypesExplicitly) { } } +TEST(GeometryTest, CanConvertBetweenDegressAndRadians) { + { + auto deg = Degrees{90.0}; + Radians rad = deg; + ASSERT_FLOAT_EQ(rad.radians, kPiOver2); + } +} + } // namespace testing } // namespace impeller diff --git a/impeller/geometry/matrix.h b/impeller/geometry/matrix.h index 7be4168497db8..58bc83669d161 100644 --- a/impeller/geometry/matrix.h +++ b/impeller/geometry/matrix.h @@ -53,6 +53,7 @@ struct Matrix { uint64_t GetComponentsMask() const; }; + // TODO(csg): Radar restructions of C++11 don't exist. Use optionals instead. using DecompositionResult = std::pair; @@ -126,9 +127,9 @@ struct Matrix { // clang-format on } - static Matrix MakeRotationX(Scalar radians) { - Scalar cosine = cos(radians); - Scalar sine = sin(radians); + static Matrix MakeRotationX(Radians r) { + Scalar cosine = cos(r.radians); + Scalar sine = sin(r.radians); // clang-format off return Matrix( 1.0, 0.0, 0.0, 0.0, @@ -139,9 +140,9 @@ struct Matrix { // clang-format on } - static Matrix MakeRotationY(Scalar radians) { - Scalar cosine = cos(radians); - Scalar sine = sin(radians); + static Matrix MakeRotationY(Radians r) { + Scalar cosine = cos(r.radians); + Scalar sine = sin(r.radians); // clang-format off return Matrix( @@ -153,9 +154,9 @@ struct Matrix { // clang-format on } - static Matrix MakeRotationZ(Scalar radians) { - Scalar cosine = cos(radians); - Scalar sine = sin(radians); + static Matrix MakeRotationZ(Radians r) { + Scalar cosine = cos(r.radians); + Scalar sine = sin(r.radians); // clang-format off return Matrix ( @@ -172,8 +173,8 @@ struct Matrix { return Matrix(m[0], m[1], m[2], m[3], m[4], m[5], m[6], m[7], m[8], m[9], m[10], m[11], - m[0] * t.x + m[4] * t.y + m[8] * t.z + m[12], - m[1] * t.x + m[5] * t.y + m[9] * t.z + m[13], + m[0] * t.x + m[4] * t.y + m[8] * t.z + m[12], + m[1] * t.x + m[5] * t.y + m[9] * t.z + m[13], m[2] * t.x + m[6] * t.y + m[10] * t.z + m[14], m[15]); // clang-format on @@ -181,10 +182,10 @@ struct Matrix { constexpr Matrix Scale(const Vector3& s) const { // clang-format off - return Matrix(m[0] * s.x, m[1] * s.x, m[2] * s.x , m[3] * s.x, - m[4] * s.y, m[5] * s.y, m[6] * s.y , m[7] * s.y, + return Matrix(m[0] * s.x, m[1] * s.x, m[2] * s.x, m[3] * s.x, + m[4] * s.y, m[5] * s.y, m[6] * s.y, m[7] * s.y, m[8] * s.z, m[9] * s.z, m[10] * s.z, m[11] * s.z, - m[12] , m[13] , m[14] , m[15]); + m[12] , m[13] , m[14] , m[15] ); // clang-format on } diff --git a/impeller/geometry/scalar.h b/impeller/geometry/scalar.h index b7fe63c63f3a9..9e8cee0d58d50 100644 --- a/impeller/geometry/scalar.h +++ b/impeller/geometry/scalar.h @@ -6,8 +6,32 @@ #include +#include "impeller/geometry/constants.h" + namespace impeller { using Scalar = float; +struct Degrees; + +struct Radians { + Scalar radians = 0.0; + + constexpr Radians() = default; + + explicit constexpr Radians(Scalar p_radians) : radians(p_radians) {} +}; + +struct Degrees { + Scalar degrees = 0.0; + + constexpr Degrees() = default; + + explicit constexpr Degrees(Scalar p_degrees) : degrees(p_degrees) {} + + constexpr operator Radians() const { + return Radians{degrees * kPi / 180.0f}; + }; +}; + } // namespace impeller diff --git a/impeller/image/BUILD.gn b/impeller/image/BUILD.gn index 35d7c0643c985..5475257810b84 100644 --- a/impeller/image/BUILD.gn +++ b/impeller/image/BUILD.gn @@ -8,8 +8,8 @@ impeller_component("image") { sources = [ "compressed_image.cc", "compressed_image.h", - "image.cc", - "image.h", + "decompressed_image.cc", + "decompressed_image.h", ] deps = [ "../third_party/stb" ] diff --git a/impeller/image/compressed_image.cc b/impeller/image/compressed_image.cc index 6076be69888b3..2c78e7aba682a 100644 --- a/impeller/image/compressed_image.cc +++ b/impeller/image/compressed_image.cc @@ -14,7 +14,7 @@ CompressedImage::CompressedImage( CompressedImage::~CompressedImage() = default; -Image CompressedImage::Decode() const { +DecompressedImage CompressedImage::Decode() const { if (!source_) { return {}; } @@ -47,32 +47,32 @@ Image CompressedImage::Decode() const { /* * Make sure we got a valid component set. */ - auto components = Image::Format::Invalid; + auto components = DecompressedImage::Format::Invalid; switch (comps) { case STBI_grey: - components = Image::Format::Grey; + components = DecompressedImage::Format::Grey; break; case STBI_grey_alpha: - components = Image::Format::GreyAlpha; + components = DecompressedImage::Format::GreyAlpha; break; case STBI_rgb: - components = Image::Format::RGB; + components = DecompressedImage::Format::RGB; break; case STBI_rgb_alpha: - components = Image::Format::RGBA; + components = DecompressedImage::Format::RGBA; break; default: - components = Image::Format::Invalid; + components = DecompressedImage::Format::Invalid; break; } - if (components == Image::Format::Invalid) { + if (components == DecompressedImage::Format::Invalid) { FML_LOG(ERROR) << "Could not detect image components when decoding."; return {}; } - return Image{ + return DecompressedImage{ ISize{width, height}, // size components, // components std::move(dest_allocation) // allocation diff --git a/impeller/image/compressed_image.h b/impeller/image/compressed_image.h index a30c32276b96a..7b79096f3d39e 100644 --- a/impeller/image/compressed_image.h +++ b/impeller/image/compressed_image.h @@ -7,7 +7,7 @@ #include "flutter/fml/macros.h" #include "flutter/fml/mapping.h" #include "impeller/geometry/size.h" -#include "impeller/image/image.h" +#include "impeller/image/decompressed_image.h" namespace impeller { @@ -19,7 +19,7 @@ class CompressedImage { ~CompressedImage(); - [[nodiscard]] Image Decode() const; + [[nodiscard]] DecompressedImage Decode() const; bool IsValid() const; diff --git a/impeller/image/image.cc b/impeller/image/decompressed_image.cc similarity index 65% rename from impeller/image/image.cc rename to impeller/image/decompressed_image.cc index 443f1edbf0df4..6cf9c5460785f 100644 --- a/impeller/image/image.cc +++ b/impeller/image/decompressed_image.cc @@ -2,7 +2,7 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -#include "impeller/image/image.h" +#include "impeller/image/decompressed_image.h" #include @@ -11,11 +11,12 @@ namespace impeller { -Image::Image() = default; +DecompressedImage::DecompressedImage() = default; -Image::Image(ISize size, - Format format, - std::shared_ptr allocation) +DecompressedImage::DecompressedImage( + ISize size, + Format format, + std::shared_ptr allocation) : size_(size), format_(format), allocation_(std::move(allocation)) { if (!allocation_ || !size.IsPositive() || format_ == Format::Invalid) { return; @@ -23,47 +24,48 @@ Image::Image(ISize size, is_valid_ = true; } -Image::~Image() = default; +DecompressedImage::~DecompressedImage() = default; -bool Image::IsValid() const { +bool DecompressedImage::IsValid() const { return is_valid_; } -const ISize& Image::GetSize() const { +const ISize& DecompressedImage::GetSize() const { return size_; } -Image::Format Image::GetFormat() const { +DecompressedImage::Format DecompressedImage::GetFormat() const { return format_; } -const std::shared_ptr& Image::GetAllocation() const { +const std::shared_ptr& DecompressedImage::GetAllocation() + const { return allocation_; } -static size_t GetBytesPerPixel(Image::Format format) { +static size_t GetBytesPerPixel(DecompressedImage::Format format) { switch (format) { - case Image::Format::Invalid: + case DecompressedImage::Format::Invalid: return 0u; - case Image::Format::Grey: + case DecompressedImage::Format::Grey: return 1u; - case Image::Format::GreyAlpha: + case DecompressedImage::Format::GreyAlpha: return 1u; - case Image::Format::RGB: + case DecompressedImage::Format::RGB: return 3u; - case Image::Format::RGBA: + case DecompressedImage::Format::RGBA: return 4; } return 0u; } -Image Image::ConvertToRGBA() const { +DecompressedImage DecompressedImage::ConvertToRGBA() const { if (!is_valid_) { return {}; } if (format_ == Format::RGBA) { - return Image{size_, format_, allocation_}; + return DecompressedImage{size_, format_, allocation_}; } const auto bpp = GetBytesPerPixel(format_); @@ -82,26 +84,26 @@ Image Image::ConvertToRGBA() const { for (size_t i = 0, j = 0; i < source_byte_size; i += bpp, j += 4u) { switch (format_) { - case Image::Format::Grey: + case DecompressedImage::Format::Grey: dest[j + 0] = source[i]; dest[j + 1] = source[i]; dest[j + 2] = source[i]; dest[j + 3] = std::numeric_limits::max(); break; - case Image::Format::GreyAlpha: + case DecompressedImage::Format::GreyAlpha: dest[j + 0] = std::numeric_limits::max(); dest[j + 1] = std::numeric_limits::max(); dest[j + 2] = std::numeric_limits::max(); dest[j + 3] = source[i]; break; - case Image::Format::RGB: + case DecompressedImage::Format::RGB: dest[j + 0] = source[i + 0]; dest[j + 1] = source[i + 1]; dest[j + 2] = source[i + 2]; dest[j + 3] = std::numeric_limits::max(); break; - case Image::Format::Invalid: - case Image::Format::RGBA: + case DecompressedImage::Format::Invalid: + case DecompressedImage::Format::RGBA: // Should never happen. The necessary checks have already been // performed. FML_CHECK(false); @@ -109,7 +111,7 @@ Image Image::ConvertToRGBA() const { } } - return Image{ + return DecompressedImage{ size_, Format::RGBA, std::make_shared( rgba_allocation->GetBuffer(), // diff --git a/impeller/image/image.h b/impeller/image/decompressed_image.h similarity index 75% rename from impeller/image/image.h rename to impeller/image/decompressed_image.h index f888ab2445457..c71af79d2670e 100644 --- a/impeller/image/image.h +++ b/impeller/image/decompressed_image.h @@ -13,7 +13,7 @@ namespace impeller { -class Image { +class DecompressedImage { public: enum class Format { Invalid, @@ -23,13 +23,13 @@ class Image { RGBA, }; - Image(); + DecompressedImage(); - Image(ISize size, - Format format, - std::shared_ptr allocation); + DecompressedImage(ISize size, + Format format, + std::shared_ptr allocation); - ~Image(); + ~DecompressedImage(); const ISize& GetSize() const; @@ -39,7 +39,7 @@ class Image { const std::shared_ptr& GetAllocation() const; - Image ConvertToRGBA() const; + DecompressedImage ConvertToRGBA() const; private: ISize size_; diff --git a/impeller/primitives/primitives_unittests.mm b/impeller/primitives/primitives_unittests.mm index 3e7400311a4e7..a8e55faf0c1f3 100644 --- a/impeller/primitives/primitives_unittests.mm +++ b/impeller/primitives/primitives_unittests.mm @@ -12,7 +12,7 @@ #include "impeller/compositor/surface.h" #include "impeller/compositor/vertex_buffer_builder.h" #include "impeller/image/compressed_image.h" -#include "impeller/image/image.h" +#include "impeller/image/decompressed_image.h" #include "impeller/playground/playground.h" #include "impeller/primitives/box_fade.frag.h" #include "impeller/primitives/box_fade.vert.h" From 0bbe0cfa199d6605dd144498c5bf19cb259dab13 Mon Sep 17 00:00:00 2001 From: Chinmay Garde Date: Tue, 20 Jul 2021 13:14:08 -0700 Subject: [PATCH 153/433] Cleanup unused entries in entity. --- impeller/.clang-tidy | 4 ++ impeller/aiks/BUILD.gn | 2 + impeller/entity/entity.cc | 75 +------------------------ impeller/entity/entity.h | 85 +---------------------------- impeller/geometry/path_component.cc | 1 + 5 files changed, 9 insertions(+), 158 deletions(-) diff --git a/impeller/.clang-tidy b/impeller/.clang-tidy index 17cf0e2daf811..28a352346af53 100644 --- a/impeller/.clang-tidy +++ b/impeller/.clang-tidy @@ -7,6 +7,10 @@ FormatStyle: none CheckOptions: - key: readability-identifier-naming.PrivateMemberCase value: 'lower_case' + - key: readability-identifier-naming.EnumConstantCase + value: 'CamelCase' + - key: readability-identifier-naming.EnumConstantPrefix + value: 'k' - key: readability-identifier-naming.PrivateMemberSuffix value: '_' - key: readability-identifier-naming.PublicMethodCase diff --git a/impeller/aiks/BUILD.gn b/impeller/aiks/BUILD.gn index c2276fe759793..c6fe348cd657b 100644 --- a/impeller/aiks/BUILD.gn +++ b/impeller/aiks/BUILD.gn @@ -10,6 +10,8 @@ impeller_component("aiks") { "canvas.h", "paint.cc", "paint.h", + "image.cc" + "image.h" ] public_deps = [ diff --git a/impeller/entity/entity.cc b/impeller/entity/entity.cc index b837161ffa8e7..510a72d0e986a 100644 --- a/impeller/entity/entity.cc +++ b/impeller/entity/entity.cc @@ -10,43 +10,6 @@ Entity::Entity() = default; Entity::~Entity() = default; -Rect Entity::GetFrame() const { - Point origin(position_.x - (bounds_.size.width * anchor_point_.x), - position_.y - (bounds_.size.height * anchor_point_.y)); - - return Rect(origin, bounds_.size); -} - -void Entity::SetFrame(const Rect& frame) { - SetBounds(Rect(bounds_.origin, frame.size)); - SetPosition(Point(frame.origin.x + (anchor_point_.x * frame.size.width), - frame.origin.y + (anchor_point_.y * frame.size.height))); -} - -const Rect& Entity::GetBounds() const { - return bounds_; -} - -void Entity::SetBounds(const Rect& bounds) { - bounds_ = bounds; -} - -const Point& Entity::GetPosition() const { - return position_; -} - -void Entity::SetPosition(const Point& position) { - position_ = position; -} - -const Point& Entity::GetAnchorPoint() const { - return anchor_point_; -} - -void Entity::SetAnchorPoint(const Point& anchorPoint) { - anchor_point_ = anchorPoint; -} - const Matrix& Entity::GetTransformation() const { return transformation_; } @@ -55,34 +18,6 @@ void Entity::SetTransformation(const Matrix& transformation) { transformation_ = transformation; } -Matrix Entity::GetModelMatrix() const { - /* - * The translation accounts for the offset in the origin of the bounds - * of the entity and its position about its anchor point. - */ - auto translation = Matrix::MakeTranslation( - {-bounds_.origin.x + position_.x - (bounds_.size.width * anchor_point_.x), - -bounds_.origin.y + position_.y - - (bounds_.size.height * anchor_point_.y)}); - - /* - * The transformation of an entity is applied about is anchor point. However, - * if the transformation is identity, we can avoid having to calculate the - * matrix adjustment and also the two matrix multiplications. - */ - - if (transformation_.IsIdentity()) { - return translation; - } - - auto anchorAdjustment = - Matrix::MakeTranslation({-anchor_point_.x * bounds_.size.width, - -anchor_point_.y * bounds_.size.height}); - - return translation * anchorAdjustment.Invert() * transformation_ * - anchorAdjustment; -} - const Color& Entity::GetBackgroundColor() const { return background_color_; } @@ -91,14 +26,6 @@ void Entity::SetBackgroundColor(const Color& backgroundColor) { background_color_ = backgroundColor; } -const double& Entity::GetOpacity() const { - return opacity_; -} - -void Entity::SetOpacity(double opacity) { - opacity_ = opacity; -} - const Color& Entity::GetStrokeColor() const { return stroke_color_; } @@ -112,7 +39,7 @@ double Entity::GetStrokeSize() const { } void Entity::SetStrokeSize(double strokeSize) { - stroke_size_ = strokeSize; + stroke_size_ = std::max(strokeSize, 0.0); } const Path& Entity::GetPath() const { diff --git a/impeller/entity/entity.h b/impeller/entity/entity.h index 9554cc21e68d4..2c35974498796 100644 --- a/impeller/entity/entity.h +++ b/impeller/entity/entity.h @@ -18,66 +18,6 @@ class Entity { ~Entity(); - /** - * The frame specifies the origin and size of the entity in the coordinate - * space of its parent. This is a computed property derived from the bounds - * of the entity and its position. - * - * @return the frame of the entity - */ - Rect GetFrame() const; - - /** - * Set the frame of the entity - * - * @param frame the new frame - */ - void SetFrame(const Rect& frame); - - /** - * The bounds specifies the origin and size of the entity in its own - * coordinate space. - * - * @return the bounds of the entity - */ - const Rect& GetBounds() const; - - /** - * Set the bounds of the entity - * - * @param bounds the new bounds - */ - void SetBounds(const Rect& bounds); - - /** - * The position specifies the coordinates of the anchor position of the - * entity relative to its parent - * - * @return the position of the entity - */ - const Point& GetPosition() const; - - /** - * Sets the position of the entity - * - * @param point the new position - */ - void SetPosition(const Point& point); - - /** - * The position of the anchor point within this node in unit space - * - * @return the anchor point - */ - const Point& GetAnchorPoint() const; - - /** - * Sets the new anchor point of this node - * - * @param anchorPoint the new anchor point - */ - void SetAnchorPoint(const Point& anchorPoint); - /** * The transformation that is applied to the entity about its anchor point * @@ -92,13 +32,6 @@ class Entity { */ void SetTransformation(const Matrix& transformation); - /** - * The model matrix of the entity - * - * @return the view matrix - */ - Matrix GetModelMatrix() const; - /** * The background color of the entity * @@ -113,21 +46,6 @@ class Entity { */ void SetBackgroundColor(const Color& backgroundColor); - /** - * The opacity of the entity. 0.0 is fully transparent and 1.0 is fully - * opaque. Default it 1.0. - * - * @return the opacity of the entity - */ - const double& GetOpacity() const; - - /** - * Set the new opacity of the entity - * - * @param opacity the new opacity - */ - void SetOpacity(double opacity); - const Color& GetStrokeColor() const; void SetStrokeColor(const Color& strokeColor); @@ -148,8 +66,7 @@ class Entity { Color background_color_; Path path_; - double opacity_ = 1.0; - Color stroke_color_ = Color::Black(); + Color stroke_color_; double stroke_size_ = 1.0; FML_DISALLOW_COPY_AND_ASSIGN(Entity); diff --git a/impeller/geometry/path_component.cc b/impeller/geometry/path_component.cc index f7055c39b7d5e..58a1b46915d9b 100644 --- a/impeller/geometry/path_component.cc +++ b/impeller/geometry/path_component.cc @@ -3,6 +3,7 @@ // found in the LICENSE file. #include "path_component.h" + #include namespace impeller { From bf48c02446af4934bd78c93488540309f9eb9611 Mon Sep 17 00:00:00 2001 From: Chinmay Garde Date: Tue, 20 Jul 2021 13:14:18 -0700 Subject: [PATCH 154/433] Add aiks image stubs. --- impeller/aiks/image.cc | 13 +++++++++++++ impeller/aiks/image.h | 21 +++++++++++++++++++++ 2 files changed, 34 insertions(+) create mode 100644 impeller/aiks/image.cc create mode 100644 impeller/aiks/image.h diff --git a/impeller/aiks/image.cc b/impeller/aiks/image.cc new file mode 100644 index 0000000000000..bb1ea3a81218c --- /dev/null +++ b/impeller/aiks/image.cc @@ -0,0 +1,13 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "flutter/impeller/aiks/image.h" + +namespace impeller { + +Image::Image() = default; + +Image::~Image() = default; + +} // namespace impeller diff --git a/impeller/aiks/image.h b/impeller/aiks/image.h new file mode 100644 index 0000000000000..48a2bc2ed6f21 --- /dev/null +++ b/impeller/aiks/image.h @@ -0,0 +1,21 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#pragma once + +#include "flutter/fml/macros.h" + +namespace impeller { + +class Image { + public: + Image(); + + ~Image(); + + private: + FML_DISALLOW_COPY_AND_ASSIGN(Image); +}; + +} // namespace impeller From 6a52466df40950990487daffa862e37925375502 Mon Sep 17 00:00:00 2001 From: Chinmay Garde Date: Tue, 20 Jul 2021 13:19:34 -0700 Subject: [PATCH 155/433] Fix clang-tidy warnings about enum names. --- impeller/compositor/formats.h | 22 +++++++++++----------- impeller/compositor/formats_metal.h | 10 +++++----- impeller/compositor/pipeline_builder.h | 2 +- impeller/geometry/color.cc | 24 ++++++++++++------------ impeller/image/compressed_image.cc | 14 +++++++------- impeller/image/decompressed_image.cc | 26 +++++++++++++------------- impeller/image/decompressed_image.h | 12 ++++++------ impeller/playground/playground.mm | 4 ++-- 8 files changed, 57 insertions(+), 57 deletions(-) diff --git a/impeller/compositor/formats.h b/impeller/compositor/formats.h index d694bfbbb76f2..24a137ee3effb 100644 --- a/impeller/compositor/formats.h +++ b/impeller/compositor/formats.h @@ -23,7 +23,7 @@ class Texture; /// component, and then one or more qualifiers to its /// interpretation. /// -/// For instance, `kR8G8B8A8_UNormInt_SRGB` is a 32 bits-per-pixel +/// For instance, `kR8G8B8A8UNormIntSRGB` is a 32 bits-per-pixel /// format ordered in RGBA with 8 bits per component with each /// component expressed as an unsigned normalized integer and a /// conversion from sRGB to linear color space. @@ -46,12 +46,12 @@ class Texture; /// enum class PixelFormat { kUnknown, - kR8G8B8A8_UNormInt, - kR8G8B8A8_UNormInt_SRGB, - kB8G8R8A8_UNormInt, - kB8G8R8A8_UNormInt_SRGB, + kR8G8B8A8UNormInt, + kR8G8B8A8UNormIntSRGB, + kB8G8R8A8UNormInt, + kB8G8R8A8UNormIntSRGB, // Esoteric formats only used as render targets. - kD32_Float_S8_UNormInt, + kD32FloatS8UNormInt, }; enum class BlendFactor { @@ -140,12 +140,12 @@ constexpr size_t BytesPerPixelForPixelFormat(PixelFormat format) { switch (format) { case PixelFormat::kUnknown: return 0u; - case PixelFormat::kR8G8B8A8_UNormInt: - case PixelFormat::kR8G8B8A8_UNormInt_SRGB: - case PixelFormat::kB8G8R8A8_UNormInt: - case PixelFormat::kB8G8R8A8_UNormInt_SRGB: + case PixelFormat::kR8G8B8A8UNormInt: + case PixelFormat::kR8G8B8A8UNormIntSRGB: + case PixelFormat::kB8G8R8A8UNormInt: + case PixelFormat::kB8G8R8A8UNormIntSRGB: return 4u; - case PixelFormat::kD32_Float_S8_UNormInt: + case PixelFormat::kD32FloatS8UNormInt: // This is an esoteric format and implementations may use 64 bits. // Impeller doesn't work with these natively and this return is only here // for completeness. The 40 bits is as documented. diff --git a/impeller/compositor/formats_metal.h b/impeller/compositor/formats_metal.h index e713b841f2633..f82bbbeee3bf0 100644 --- a/impeller/compositor/formats_metal.h +++ b/impeller/compositor/formats_metal.h @@ -21,15 +21,15 @@ constexpr MTLPixelFormat ToMTLPixelFormat(PixelFormat format) { switch (format) { case PixelFormat::kUnknown: return MTLPixelFormatInvalid; - case PixelFormat::kB8G8R8A8_UNormInt: + case PixelFormat::kB8G8R8A8UNormInt: return MTLPixelFormatBGRA8Unorm; - case PixelFormat::kB8G8R8A8_UNormInt_SRGB: + case PixelFormat::kB8G8R8A8UNormIntSRGB: return MTLPixelFormatBGRA8Unorm_sRGB; - case PixelFormat::kD32_Float_S8_UNormInt: + case PixelFormat::kD32FloatS8UNormInt: return MTLPixelFormatDepth32Float_Stencil8; - case PixelFormat::kR8G8B8A8_UNormInt: + case PixelFormat::kR8G8B8A8UNormInt: return MTLPixelFormatRGBA8Unorm; - case PixelFormat::kR8G8B8A8_UNormInt_SRGB: + case PixelFormat::kR8G8B8A8UNormIntSRGB: return MTLPixelFormatRGBA8Unorm_sRGB; } return MTLPixelFormatInvalid; diff --git a/impeller/compositor/pipeline_builder.h b/impeller/compositor/pipeline_builder.h index 9a84e4e63c310..dfc0dffb0b796 100644 --- a/impeller/compositor/pipeline_builder.h +++ b/impeller/compositor/pipeline_builder.h @@ -85,7 +85,7 @@ struct PipelineBuilder { // TODO(csg): This can be easily reflected but we are sticking to the // convention that the first stage output is the color output. PipelineColorAttachment color0; - color0.format = PixelFormat::kB8G8R8A8_UNormInt; + color0.format = PixelFormat::kB8G8R8A8UNormInt; desc.SetColorAttachmentDescriptor(0u, std::move(color0)); } diff --git a/impeller/geometry/color.cc b/impeller/geometry/color.cc index fb052a80f583e..cc45636cdcfd4 100644 --- a/impeller/geometry/color.cc +++ b/impeller/geometry/color.cc @@ -11,13 +11,13 @@ namespace impeller { ColorHSB ColorHSB::FromRGB(Color rgb) { - double R = rgb.red; - double G = rgb.green; - double B = rgb.blue; + Scalar R = rgb.red; + Scalar G = rgb.green; + Scalar B = rgb.blue; - double v = 0.0; - double x = 0.0; - double f = 0.0; + Scalar v = 0.0; + Scalar x = 0.0; + Scalar f = 0.0; int64_t i = 0; @@ -38,13 +38,13 @@ ColorHSB ColorHSB::FromRGB(Color rgb) { } Color ColorHSB::ToRGBA() const { - double h = hue * 6.0; - double s = saturation; - double v = brightness; + Scalar h = hue * 6.0; + Scalar s = saturation; + Scalar v = brightness; - double m = 0.0; - double n = 0.0; - double f = 0.0; + Scalar m = 0.0; + Scalar n = 0.0; + Scalar f = 0.0; int64_t i = 0; diff --git a/impeller/image/compressed_image.cc b/impeller/image/compressed_image.cc index 2c78e7aba682a..c47ed4b2ddf7c 100644 --- a/impeller/image/compressed_image.cc +++ b/impeller/image/compressed_image.cc @@ -47,27 +47,27 @@ DecompressedImage CompressedImage::Decode() const { /* * Make sure we got a valid component set. */ - auto components = DecompressedImage::Format::Invalid; + auto components = DecompressedImage::Format::kInvalid; switch (comps) { case STBI_grey: - components = DecompressedImage::Format::Grey; + components = DecompressedImage::Format::kGrey; break; case STBI_grey_alpha: - components = DecompressedImage::Format::GreyAlpha; + components = DecompressedImage::Format::kGreyAlpha; break; case STBI_rgb: - components = DecompressedImage::Format::RGB; + components = DecompressedImage::Format::kRGB; break; case STBI_rgb_alpha: - components = DecompressedImage::Format::RGBA; + components = DecompressedImage::Format::kRGBA; break; default: - components = DecompressedImage::Format::Invalid; + components = DecompressedImage::Format::kInvalid; break; } - if (components == DecompressedImage::Format::Invalid) { + if (components == DecompressedImage::Format::kInvalid) { FML_LOG(ERROR) << "Could not detect image components when decoding."; return {}; } diff --git a/impeller/image/decompressed_image.cc b/impeller/image/decompressed_image.cc index 6cf9c5460785f..c31c184c32961 100644 --- a/impeller/image/decompressed_image.cc +++ b/impeller/image/decompressed_image.cc @@ -18,7 +18,7 @@ DecompressedImage::DecompressedImage( Format format, std::shared_ptr allocation) : size_(size), format_(format), allocation_(std::move(allocation)) { - if (!allocation_ || !size.IsPositive() || format_ == Format::Invalid) { + if (!allocation_ || !size.IsPositive() || format_ == Format::kInvalid) { return; } is_valid_ = true; @@ -45,15 +45,15 @@ const std::shared_ptr& DecompressedImage::GetAllocation() static size_t GetBytesPerPixel(DecompressedImage::Format format) { switch (format) { - case DecompressedImage::Format::Invalid: + case DecompressedImage::Format::kInvalid: return 0u; - case DecompressedImage::Format::Grey: + case DecompressedImage::Format::kGrey: return 1u; - case DecompressedImage::Format::GreyAlpha: + case DecompressedImage::Format::kGreyAlpha: return 1u; - case DecompressedImage::Format::RGB: + case DecompressedImage::Format::kRGB: return 3u; - case DecompressedImage::Format::RGBA: + case DecompressedImage::Format::kRGBA: return 4; } return 0u; @@ -64,7 +64,7 @@ DecompressedImage DecompressedImage::ConvertToRGBA() const { return {}; } - if (format_ == Format::RGBA) { + if (format_ == Format::kRGBA) { return DecompressedImage{size_, format_, allocation_}; } @@ -84,26 +84,26 @@ DecompressedImage DecompressedImage::ConvertToRGBA() const { for (size_t i = 0, j = 0; i < source_byte_size; i += bpp, j += 4u) { switch (format_) { - case DecompressedImage::Format::Grey: + case DecompressedImage::Format::kGrey: dest[j + 0] = source[i]; dest[j + 1] = source[i]; dest[j + 2] = source[i]; dest[j + 3] = std::numeric_limits::max(); break; - case DecompressedImage::Format::GreyAlpha: + case DecompressedImage::Format::kGreyAlpha: dest[j + 0] = std::numeric_limits::max(); dest[j + 1] = std::numeric_limits::max(); dest[j + 2] = std::numeric_limits::max(); dest[j + 3] = source[i]; break; - case DecompressedImage::Format::RGB: + case DecompressedImage::Format::kRGB: dest[j + 0] = source[i + 0]; dest[j + 1] = source[i + 1]; dest[j + 2] = source[i + 2]; dest[j + 3] = std::numeric_limits::max(); break; - case DecompressedImage::Format::Invalid: - case DecompressedImage::Format::RGBA: + case DecompressedImage::Format::kInvalid: + case DecompressedImage::Format::kRGBA: // Should never happen. The necessary checks have already been // performed. FML_CHECK(false); @@ -112,7 +112,7 @@ DecompressedImage DecompressedImage::ConvertToRGBA() const { } return DecompressedImage{ - size_, Format::RGBA, + size_, Format::kRGBA, std::make_shared( rgba_allocation->GetBuffer(), // rgba_allocation->GetLength(), // diff --git a/impeller/image/decompressed_image.h b/impeller/image/decompressed_image.h index c71af79d2670e..56bf9b15c5c4c 100644 --- a/impeller/image/decompressed_image.h +++ b/impeller/image/decompressed_image.h @@ -16,11 +16,11 @@ namespace impeller { class DecompressedImage { public: enum class Format { - Invalid, - Grey, - GreyAlpha, - RGB, - RGBA, + kInvalid, + kGrey, + kGreyAlpha, + kRGB, + kRGBA, }; DecompressedImage(); @@ -43,7 +43,7 @@ class DecompressedImage { private: ISize size_; - Format format_ = Format::Invalid; + Format format_ = Format::kInvalid; std::shared_ptr allocation_; bool is_valid_ = false; }; diff --git a/impeller/playground/playground.mm b/impeller/playground/playground.mm index e70f8237d7521..bef2d41a49107 100644 --- a/impeller/playground/playground.mm +++ b/impeller/playground/playground.mm @@ -131,7 +131,7 @@ static void PlaygroundKeyCallback(GLFWwindow* window, } TextureDescriptor color0_desc; - color0_desc.format = PixelFormat::kB8G8R8A8_UNormInt; + color0_desc.format = PixelFormat::kB8G8R8A8UNormInt; color0_desc.size = { static_cast(current_drawable.texture.width), static_cast(current_drawable.texture.height)}; @@ -181,7 +181,7 @@ CompressedImage compressed_image( } auto texture_descriptor = TextureDescriptor{}; - texture_descriptor.format = PixelFormat::kR8G8B8A8_UNormInt; + texture_descriptor.format = PixelFormat::kR8G8B8A8UNormInt; texture_descriptor.size = image.GetSize(); texture_descriptor.mip_count = 1u; From 7cc15e8ab31d99c5b5d87c5b861d2749b719568c Mon Sep 17 00:00:00 2001 From: Chinmay Garde Date: Tue, 20 Jul 2021 13:30:09 -0700 Subject: [PATCH 156/433] Fixup tidy checks in impeller/geometry. --- impeller/geometry/matrix.cc | 10 ++++---- impeller/geometry/matrix.h | 10 ++++---- impeller/geometry/path.cc | 32 +++++++++++------------ impeller/geometry/path.h | 10 ++++---- impeller/geometry/path_component.cc | 40 +++++++++++++++-------------- impeller/geometry/path_component.h | 22 ++++++++-------- 6 files changed, 63 insertions(+), 61 deletions(-) diff --git a/impeller/geometry/matrix.cc b/impeller/geometry/matrix.cc index 420be5fd6dcc9..5f77fcced0ff4 100644 --- a/impeller/geometry/matrix.cc +++ b/impeller/geometry/matrix.cc @@ -363,27 +363,27 @@ uint64_t Matrix::Decomposition::GetComponentsMask() const { Quaternion noRotation(0.0, 0.0, 0.0, 1.0); if (rotation != noRotation) { - mask = mask | static_cast(Component::Rotation); + mask = mask | static_cast(Component::kRotation); } Vector4 defaultPerspective(0.0, 0.0, 0.0, 1.0); if (perspective != defaultPerspective) { - mask = mask | static_cast(Component::Perspective); + mask = mask | static_cast(Component::kPerspective); } Shear noShear(0.0, 0.0, 0.0); if (shear != noShear) { - mask = mask | static_cast(Component::Shear); + mask = mask | static_cast(Component::kShear); } Vector3 defaultScale(1.0, 1.0, 1.0); if (scale != defaultScale) { - mask = mask | static_cast(Component::Scale); + mask = mask | static_cast(Component::kScale); } Vector3 defaultTranslation(0.0, 0.0, 0.0); if (translation != defaultTranslation) { - mask = mask | static_cast(Component::Translation); + mask = mask | static_cast(Component::kTranslation); } return mask; diff --git a/impeller/geometry/matrix.h b/impeller/geometry/matrix.h index 58bc83669d161..4d603845cc462 100644 --- a/impeller/geometry/matrix.h +++ b/impeller/geometry/matrix.h @@ -43,11 +43,11 @@ struct Matrix { Quaternion rotation; enum class Component { - Translation = 1 << 0, - Scale = 1 << 1, - Shear = 1 << 2, - Perspective = 1 << 3, - Rotation = 1 << 4, + kTranslation = 1 << 0, + kScale = 1 << 1, + kShear = 1 << 2, + kPerspective = 1 << 3, + kRotation = 1 << 4, }; uint64_t GetComponentsMask() const; diff --git a/impeller/geometry/path.cc b/impeller/geometry/path.cc index 80def8458ec87..818bb6d0ef030 100644 --- a/impeller/geometry/path.cc +++ b/impeller/geometry/path.cc @@ -16,19 +16,19 @@ size_t Path::GetComponentCount() const { Path& Path::AddLinearComponent(Point p1, Point p2) { linears_.emplace_back(p1, p2); - components_.emplace_back(ComponentType::Linear, linears_.size() - 1); + components_.emplace_back(ComponentType::kLinear, linears_.size() - 1); return *this; } Path& Path::AddQuadraticComponent(Point p1, Point cp, Point p2) { quads_.emplace_back(p1, cp, p2); - components_.emplace_back(ComponentType::Quadratic, quads_.size() - 1); + components_.emplace_back(ComponentType::kQuadratic, quads_.size() - 1); return *this; } Path& Path::AddCubicComponent(Point p1, Point cp1, Point cp2, Point p2) { cubics_.emplace_back(p1, cp1, cp2, p2); - components_.emplace_back(ComponentType::Cubic, cubics_.size() - 1); + components_.emplace_back(ComponentType::kCubic, cubics_.size() - 1); return *this; } @@ -38,17 +38,17 @@ void Path::EnumerateComponents(Applier linearApplier, size_t currentIndex = 0; for (const auto& component : components_) { switch (component.type) { - case ComponentType::Linear: + case ComponentType::kLinear: if (linearApplier) { linearApplier(currentIndex, linears_[component.index]); } break; - case ComponentType::Quadratic: + case ComponentType::kQuadratic: if (quadApplier) { quadApplier(currentIndex, quads_[component.index]); } break; - case ComponentType::Cubic: + case ComponentType::kCubic: if (cubicApplier) { cubicApplier(currentIndex, cubics_[component.index]); } @@ -64,7 +64,7 @@ bool Path::GetLinearComponentAtIndex(size_t index, return false; } - if (components_[index].type != ComponentType::Linear) { + if (components_[index].type != ComponentType::kLinear) { return false; } @@ -79,7 +79,7 @@ bool Path::GetQuadraticComponentAtIndex( return false; } - if (components_[index].type != ComponentType::Quadratic) { + if (components_[index].type != ComponentType::kQuadratic) { return false; } @@ -93,7 +93,7 @@ bool Path::GetCubicComponentAtIndex(size_t index, return false; } - if (components_[index].type != ComponentType::Cubic) { + if (components_[index].type != ComponentType::kCubic) { return false; } @@ -107,7 +107,7 @@ bool Path::UpdateLinearComponentAtIndex(size_t index, return false; } - if (components_[index].type != ComponentType::Linear) { + if (components_[index].type != ComponentType::kLinear) { return false; } @@ -122,7 +122,7 @@ bool Path::UpdateQuadraticComponentAtIndex( return false; } - if (components_[index].type != ComponentType::Quadratic) { + if (components_[index].type != ComponentType::kQuadratic) { return false; } @@ -136,7 +136,7 @@ bool Path::UpdateCubicComponentAtIndex(size_t index, return false; } - if (components_[index].type != ComponentType::Cubic) { + if (components_[index].type != ComponentType::kCubic) { return false; } @@ -153,17 +153,17 @@ void Path::EnumerateSmoothPoints( for (const auto& component : components_) { switch (component.type) { - case ComponentType::Linear: { + case ComponentType::kLinear: { if (!enumerator(linears_[component.index].SmoothPoints())) { return; } } break; - case ComponentType::Quadratic: { + case ComponentType::kQuadratic: { if (!enumerator(quads_[component.index].SmoothPoints(approximation))) { return; } } break; - case ComponentType::Cubic: { + case ComponentType::kCubic: { if (!enumerator(cubics_[component.index].SmoothPoints(approximation))) { return; } @@ -180,7 +180,7 @@ Rect Path::GetBoundingBox() const { } for (const auto& quad : quads_) { - box = box.WithPoints(quad.Sxtrema()); + box = box.WithPoints(quad.Extrema()); } for (const auto& cubic : cubics_) { diff --git a/impeller/geometry/path.h b/impeller/geometry/path.h index 9f55a14d0c433..5560011ebeb36 100644 --- a/impeller/geometry/path.h +++ b/impeller/geometry/path.h @@ -13,10 +13,10 @@ namespace impeller { class Path { public: - enum class ComponentType : uint8_t { - Linear, - Quadratic, - Cubic, + enum class ComponentType { + kLinear, + kQuadratic, + kCubic, }; Path(); @@ -61,7 +61,7 @@ class Path { private: struct ComponentIndexPair { - ComponentType type = ComponentType::Linear; + ComponentType type = ComponentType::kLinear; size_t index = 0; ComponentIndexPair() {} diff --git a/impeller/geometry/path_component.cc b/impeller/geometry/path_component.cc index 58a1b46915d9b..554d7672dc6a6 100644 --- a/impeller/geometry/path_component.cc +++ b/impeller/geometry/path_component.cc @@ -91,7 +91,7 @@ std::vector QuadraticPathComponent::SmoothPoints( return elevated.SmoothPoints(approximation); } -std::vector QuadraticPathComponent::Sxtrema() const { +std::vector QuadraticPathComponent::Extrema() const { CubicPathComponent elevated(*this); return elevated.Extrema(); } @@ -193,12 +193,12 @@ static void CubicPathSmoothenRecursive(const SmoothingApproximation& approx, } if (d2 > d3) { - if (d2 < approx.distanceToleranceSquare) { + if (d2 < approx.distance_tolerance_square) { points.emplace_back(p2); return; } } else { - if (d3 < approx.distanceToleranceSquare) { + if (d3 < approx.distance_tolerance_square) { points.emplace_back(p3); return; } @@ -208,8 +208,9 @@ static void CubicPathSmoothenRecursive(const SmoothingApproximation& approx, /* * p1, p2, p4 are collinear, p3 is significant. */ - if (d3 * d3 <= approx.distanceToleranceSquare * (d.x * d.x + d.y * d.y)) { - if (approx.angleTolerance < kCurveAngleToleranceEpsilon) { + if (d3 * d3 <= + approx.distance_tolerance_square * (d.x * d.x + d.y * d.y)) { + if (approx.angle_tolerance < kCurveAngleToleranceEpsilon) { points.emplace_back(p23); return; } @@ -224,14 +225,14 @@ static void CubicPathSmoothenRecursive(const SmoothingApproximation& approx, da1 = 2.0 * M_PI - da1; } - if (da1 < approx.angleTolerance) { + if (da1 < approx.angle_tolerance) { points.emplace_back(p2); points.emplace_back(p3); return; } - if (approx.cuspLimit != 0.0) { - if (da1 > approx.cuspLimit) { + if (approx.cusp_limit != 0.0) { + if (da1 > approx.cusp_limit) { points.emplace_back(p3); return; } @@ -243,8 +244,9 @@ static void CubicPathSmoothenRecursive(const SmoothingApproximation& approx, /* * p1,p3,p4 are collinear, p2 is significant. */ - if (d2 * d2 <= approx.distanceToleranceSquare * (d.x * d.x + d.y * d.y)) { - if (approx.angleTolerance < kCurveAngleToleranceEpsilon) { + if (d2 * d2 <= + approx.distance_tolerance_square * (d.x * d.x + d.y * d.y)) { + if (approx.angle_tolerance < kCurveAngleToleranceEpsilon) { points.emplace_back(p23); return; } @@ -259,14 +261,14 @@ static void CubicPathSmoothenRecursive(const SmoothingApproximation& approx, da1 = 2.0 * M_PI - da1; } - if (da1 < approx.angleTolerance) { + if (da1 < approx.angle_tolerance) { points.emplace_back(p2); points.emplace_back(p3); return; } - if (approx.cuspLimit != 0.0) { - if (da1 > approx.cuspLimit) { + if (approx.cusp_limit != 0.0) { + if (da1 > approx.cusp_limit) { points.emplace_back(p2); return; } @@ -279,12 +281,12 @@ static void CubicPathSmoothenRecursive(const SmoothingApproximation& approx, * Regular case. */ if ((d2 + d3) * (d2 + d3) <= - approx.distanceToleranceSquare * (d.x * d.x + d.y * d.y)) { + approx.distance_tolerance_square * (d.x * d.x + d.y * d.y)) { /* * If the curvature doesn't exceed the distance_tolerance value * we tend to finish subdivisions. */ - if (approx.angleTolerance < kCurveAngleToleranceEpsilon) { + if (approx.angle_tolerance < kCurveAngleToleranceEpsilon) { points.emplace_back(p23); return; } @@ -304,7 +306,7 @@ static void CubicPathSmoothenRecursive(const SmoothingApproximation& approx, da2 = 2.0 * M_PI - da2; } - if (da1 + da2 < approx.angleTolerance) { + if (da1 + da2 < approx.angle_tolerance) { /* * Finally we can stop the recursion. */ @@ -312,13 +314,13 @@ static void CubicPathSmoothenRecursive(const SmoothingApproximation& approx, return; } - if (approx.cuspLimit != 0.0) { - if (da1 > approx.cuspLimit) { + if (approx.cusp_limit != 0.0) { + if (da1 > approx.cusp_limit) { points.emplace_back(p2); return; } - if (da2 > approx.cuspLimit) { + if (da2 > approx.cusp_limit) { points.emplace_back(p3); return; } diff --git a/impeller/geometry/path_component.h b/impeller/geometry/path_component.h index c9e1e23730b83..636e9b1fa05e5 100644 --- a/impeller/geometry/path_component.h +++ b/impeller/geometry/path_component.h @@ -13,22 +13,22 @@ namespace impeller { struct SmoothingApproximation { const Scalar scale; - const Scalar angleTolerance; - const Scalar cuspLimit; - const Scalar distanceToleranceSquare; + const Scalar angle_tolerance; + const Scalar cusp_limit; + const Scalar distance_tolerance_square; SmoothingApproximation(/* default */) : SmoothingApproximation(1.0 /* scale */, 0.0 /* angle tolerance */, 0.0 /* cusp limit */) {} - SmoothingApproximation(Scalar pScale, - Scalar pAngleTolerance, - Scalar pCuspLimit) - : scale(pScale), - angleTolerance(pAngleTolerance), - cuspLimit(pCuspLimit), - distanceToleranceSquare(0.5 * pScale * 0.5 * pScale) {} + SmoothingApproximation(Scalar p_scale, + Scalar p_angle_tolerance, + Scalar p_cusp_limit) + : scale(p_scale), + angle_tolerance(p_angle_tolerance), + cusp_limit(p_cusp_limit), + distance_tolerance_square(0.5 * p_scale * 0.5 * p_scale) {} }; struct LinearPathComponent { @@ -67,7 +67,7 @@ struct QuadraticPathComponent { std::vector SmoothPoints( const SmoothingApproximation& approximation) const; - std::vector Sxtrema() const; + std::vector Extrema() const; bool operator==(const QuadraticPathComponent& other) const { return p1 == other.p1 && cp == other.cp && p2 == other.p2; From 8fe7f538d56176d90d02b465cbcf45b91edd3cd1 Mon Sep 17 00:00:00 2001 From: Chinmay Garde Date: Tue, 20 Jul 2021 13:41:45 -0700 Subject: [PATCH 157/433] Rename utilities for adaptive subdivision. --- impeller/geometry/path.cc | 40 ++++++++++++++--------------- impeller/geometry/path.h | 5 ++-- impeller/geometry/path_component.cc | 8 +++--- impeller/geometry/path_component.h | 6 ++--- 4 files changed, 28 insertions(+), 31 deletions(-) diff --git a/impeller/geometry/path.cc b/impeller/geometry/path.cc index 818bb6d0ef030..1d444b5fb6fe6 100644 --- a/impeller/geometry/path.cc +++ b/impeller/geometry/path.cc @@ -144,32 +144,30 @@ bool Path::UpdateCubicComponentAtIndex(size_t index, return true; } -void Path::EnumerateSmoothPoints( - SmoothPointsEnumerator enumerator, - const SmoothingApproximation& approximation) const { - if (enumerator == nullptr) { - return; - } +static void AddPoints(std::vector& dest, const std::vector& src) { + dest.reserve(dest.size() + src.size()); + dest.insert(dest.end(), src.begin(), src.end()); +} +std::vector Path::SubdivideAdaptively( + const SmoothingApproximation& approximation) const { + std::vector points; for (const auto& component : components_) { switch (component.type) { - case ComponentType::kLinear: { - if (!enumerator(linears_[component.index].SmoothPoints())) { - return; - } - } break; - case ComponentType::kQuadratic: { - if (!enumerator(quads_[component.index].SmoothPoints(approximation))) { - return; - } - } break; - case ComponentType::kCubic: { - if (!enumerator(cubics_[component.index].SmoothPoints(approximation))) { - return; - } - } break; + case ComponentType::kLinear: + AddPoints(points, linears_[component.index].SubdivideAdaptively()); + break; + case ComponentType::kQuadratic: + AddPoints(points, + quads_[component.index].SubdivideAdaptively(approximation)); + break; + case ComponentType::kCubic: + AddPoints(points, + cubics_[component.index].SubdivideAdaptively(approximation)); + break; } } + return points; } Rect Path::GetBoundingBox() const { diff --git a/impeller/geometry/path.h b/impeller/geometry/path.h index 5560011ebeb36..18c7b4a602db9 100644 --- a/impeller/geometry/path.h +++ b/impeller/geometry/path.h @@ -53,9 +53,8 @@ class Path { bool UpdateCubicComponentAtIndex(size_t index, CubicPathComponent& cubic); - using SmoothPointsEnumerator = std::function points)>; - void EnumerateSmoothPoints(SmoothPointsEnumerator enumerator, - const SmoothingApproximation& approximation) const; + std::vector SubdivideAdaptively( + const SmoothingApproximation& approximation) const; Rect GetBoundingBox() const; diff --git a/impeller/geometry/path_component.cc b/impeller/geometry/path_component.cc index 554d7672dc6a6..02c72428036ec 100644 --- a/impeller/geometry/path_component.cc +++ b/impeller/geometry/path_component.cc @@ -63,7 +63,7 @@ Point LinearPathComponent::Solve(Scalar time) const { }; } -std::vector LinearPathComponent::SmoothPoints() const { +std::vector LinearPathComponent::SubdivideAdaptively() const { return {p1, p2}; } @@ -85,10 +85,10 @@ Point QuadraticPathComponent::SolveDerivative(Scalar time) const { }; } -std::vector QuadraticPathComponent::SmoothPoints( +std::vector QuadraticPathComponent::SubdivideAdaptively( const SmoothingApproximation& approximation) const { CubicPathComponent elevated(*this); - return elevated.SmoothPoints(approximation); + return elevated.SubdivideAdaptively(approximation); } std::vector QuadraticPathComponent::Extrema() const { @@ -336,7 +336,7 @@ static void CubicPathSmoothenRecursive(const SmoothingApproximation& approx, CubicPathSmoothenRecursive(approx, points, p1234, p234, p34, p4, level + 1); } -std::vector CubicPathComponent::SmoothPoints( +std::vector CubicPathComponent::SubdivideAdaptively( const SmoothingApproximation& approximation) const { std::vector points; points.emplace_back(p1); diff --git a/impeller/geometry/path_component.h b/impeller/geometry/path_component.h index 636e9b1fa05e5..effc7d9822a02 100644 --- a/impeller/geometry/path_component.h +++ b/impeller/geometry/path_component.h @@ -41,7 +41,7 @@ struct LinearPathComponent { Point Solve(Scalar time) const; - std::vector SmoothPoints() const; + std::vector SubdivideAdaptively() const; std::vector Extrema() const; @@ -64,7 +64,7 @@ struct QuadraticPathComponent { Point SolveDerivative(Scalar time) const; - std::vector SmoothPoints( + std::vector SubdivideAdaptively( const SmoothingApproximation& approximation) const; std::vector Extrema() const; @@ -95,7 +95,7 @@ struct CubicPathComponent { Point SolveDerivative(Scalar time) const; - std::vector SmoothPoints( + std::vector SubdivideAdaptively( const SmoothingApproximation& approximation) const; std::vector Extrema() const; From 845766fc6b6540a0de5cd88873f50cc2421c9d5b Mon Sep 17 00:00:00 2001 From: Chinmay Garde Date: Wed, 21 Jul 2021 14:20:19 -0700 Subject: [PATCH 158/433] Add some aiks test stubs. --- impeller/BUILD.gn | 2 + impeller/aiks/BUILD.gn | 12 ++-- impeller/aiks/aiks_unittests.cc | 38 +++++++++++ impeller/aiks/canvas.cc | 12 +++- impeller/aiks/canvas.h | 7 +- impeller/compiler/reflector.cc | 109 +++++++++++++++++--------------- impeller/entity/entity.h | 23 ------- impeller/geometry/matrix.h | 5 +- 8 files changed, 124 insertions(+), 84 deletions(-) create mode 100644 impeller/aiks/aiks_unittests.cc diff --git a/impeller/BUILD.gn b/impeller/BUILD.gn index 8ba0af5630e97..167ba3b42b26b 100644 --- a/impeller/BUILD.gn +++ b/impeller/BUILD.gn @@ -8,6 +8,7 @@ config("impeller_public_config") { group("impeller") { deps = [ + "aiks", "base", "compiler", "compositor", @@ -22,6 +23,7 @@ executable("impeller_unittests") { testonly = true deps = [ + "aiks:aiks_unittests", "base:base_unittests", "compiler:compiler_unittests", "compositor:compositor_unittests", diff --git a/impeller/aiks/BUILD.gn b/impeller/aiks/BUILD.gn index c6fe348cd657b..c4b8829a14b46 100644 --- a/impeller/aiks/BUILD.gn +++ b/impeller/aiks/BUILD.gn @@ -8,23 +8,25 @@ impeller_component("aiks") { sources = [ "canvas.cc", "canvas.h", + "image.cc", + "image.h", "paint.cc", "paint.h", - "image.cc" - "image.h" ] public_deps = [ - ":base", - ":entity", + "../base", + "../entity", + "../geometry", ] } impeller_component("aiks_unittests") { testonly = true - sources = [] + sources = [ "aiks_unittests.cc" ] deps = [ ":aiks", + "../geometry:geometry_unittests", "//flutter/testing", ] } diff --git a/impeller/aiks/aiks_unittests.cc b/impeller/aiks/aiks_unittests.cc new file mode 100644 index 0000000000000..5825cbc9ebc6b --- /dev/null +++ b/impeller/aiks/aiks_unittests.cc @@ -0,0 +1,38 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "flutter/testing/testing.h" +#include "impeller/aiks/canvas.h" +#include "impeller/geometry/geometry_unittests.h" + +namespace impeller { +namespace testing { + +TEST(AiksTest, CanvasCTMCanBeUpdated) { + Canvas canvas; + Matrix identity; + ASSERT_MATRIX_NEAR(canvas.GetCurrentTransformation(), identity); + canvas.Translate(Size{100, 100}); + ASSERT_MATRIX_NEAR(canvas.GetCurrentTransformation(), + Matrix::MakeTranslation({100.0, 100.0, 0.0})); +} + +TEST(AiksTest, CanvasCanPushPopCTM) { + Canvas canvas; + ASSERT_EQ(canvas.GetSaveCount(), 1u); + ASSERT_EQ(canvas.Restore(), false); + + canvas.Translate(Size{100, 100}); + canvas.Save(); + ASSERT_EQ(canvas.GetSaveCount(), 2u); + ASSERT_MATRIX_NEAR(canvas.GetCurrentTransformation(), + Matrix::MakeTranslation({100.0, 100.0, 0.0})); + ASSERT_TRUE(canvas.Restore()); + ASSERT_EQ(canvas.GetSaveCount(), 1u); + ASSERT_MATRIX_NEAR(canvas.GetCurrentTransformation(), + Matrix::MakeTranslation({100.0, 100.0, 0.0})); +} + +} // namespace testing +} // namespace impeller diff --git a/impeller/aiks/canvas.cc b/impeller/aiks/canvas.cc index eb34836057b46..4de595e6debb6 100644 --- a/impeller/aiks/canvas.cc +++ b/impeller/aiks/canvas.cc @@ -32,11 +32,15 @@ void Canvas::Concat(const Matrix& xformation) { xformation_stack_.top() = xformation_stack_.top() * xformation; } -void Canvas::Translate(const Size& offset) { +const Matrix& Canvas::GetCurrentTransformation() const { + return xformation_stack_.top(); +} + +void Canvas::Translate(const Vector3& offset) { Concat(Matrix::MakeTranslation(offset)); } -void Canvas::Scale(const Size& scale) { +void Canvas::Scale(const Vector3& scale) { Concat(Matrix::MakeScale(scale)); } @@ -44,6 +48,10 @@ void Canvas::Rotate(Radians radians) { Concat(Matrix::MakeRotationZ(radians)); } +size_t Canvas::GetSaveCount() const { + return xformation_stack_.size(); +} + void Canvas::DrawPath(Path path, Paint paint) {} } // namespace impeller diff --git a/impeller/aiks/canvas.h b/impeller/aiks/canvas.h index c4e928d574909..86f2ed048be1f 100644 --- a/impeller/aiks/canvas.h +++ b/impeller/aiks/canvas.h @@ -10,6 +10,7 @@ #include "impeller/aiks/paint.h" #include "impeller/geometry/matrix.h" #include "impeller/geometry/path.h" +#include "impeller/geometry/vector.h" namespace impeller { @@ -25,11 +26,13 @@ class Canvas { size_t GetSaveCount() const; + const Matrix& GetCurrentTransformation() const; + void Concat(const Matrix& xformation); - void Translate(const Size& offset); + void Translate(const Vector3& offset); - void Scale(const Size& scale); + void Scale(const Vector3& scale); void Rotate(Radians radians); diff --git a/impeller/compiler/reflector.cc b/impeller/compiler/reflector.cc index 70c443aba942e..e97d7f7c39021 100644 --- a/impeller/compiler/reflector.cc +++ b/impeller/compiler/reflector.cc @@ -20,10 +20,8 @@ namespace impeller { namespace compiler { -using namespace spirv_cross; - -static std::string BaseTypeToString(SPIRType::BaseType type) { - using Type = SPIRType::BaseType; +static std::string BaseTypeToString(spirv_cross::SPIRType::BaseType type) { + using Type = spirv_cross::SPIRType::BaseType; switch (type) { case Type::Void: return "ShaderType::kVoid"; @@ -90,8 +88,8 @@ static std::string StringToShaderStage(std::string str) { } Reflector::Reflector(Options options, - std::shared_ptr ir, - std::shared_ptr compiler) + std::shared_ptr ir, + std::shared_ptr compiler) : options_(std::move(options)), ir_(std::move(ir)), compiler_(std::move(compiler)) { @@ -226,17 +224,20 @@ std::optional Reflector::GenerateTemplateArguments() const { } std::set known_structs; - ir_->for_each_typed_id([&](uint32_t, const SPIRType& type) { - if (known_structs.find(type.self) != known_structs.end()) { - // Iterating over types this way leads to duplicates which may cause - // duplicate struct definitions. - return; - } - known_structs.insert(type.self); - if (auto struc = ReflectStructDefinition(type.self); struc.has_value()) { - struct_definitions.emplace_back(EmitStructDefinition(struc.value())); - } - }); + ir_->for_each_typed_id( + [&](uint32_t, const spirv_cross::SPIRType& type) { + if (known_structs.find(type.self) != known_structs.end()) { + // Iterating over types this way leads to duplicates which may cause + // duplicate struct definitions. + return; + } + known_structs.insert(type.self); + if (auto struc = ReflectStructDefinition(type.self); + struc.has_value()) { + struct_definitions.emplace_back( + EmitStructDefinition(struc.value())); + } + }); } root["bind_prototypes"] = EmitBindPrototypes(shader_resources); @@ -342,24 +343,25 @@ struct KnownType { size_t byte_size = 0; }; -static std::optional ReadKnownScalarType(SPIRType::BaseType type) { +static std::optional ReadKnownScalarType( + spirv_cross::SPIRType::BaseType type) { switch (type) { - case SPIRType::BaseType::Boolean: + case spirv_cross::SPIRType::BaseType::Boolean: return KnownType{ .name = "bool", .byte_size = sizeof(bool), }; - case SPIRType::BaseType::Float: + case spirv_cross::SPIRType::BaseType::Float: return KnownType{ .name = "Scalar", .byte_size = sizeof(Scalar), }; - case SPIRType::BaseType::UInt: + case spirv_cross::SPIRType::BaseType::UInt: return KnownType{ .name = "uint32_t", .byte_size = sizeof(uint32_t), }; - case SPIRType::BaseType::Int: + case spirv_cross::SPIRType::BaseType::Int: return KnownType{ .name = "int32_t", .byte_size = sizeof(int32_t), @@ -373,7 +375,7 @@ static std::optional ReadKnownScalarType(SPIRType::BaseType type) { std::vector Reflector::ReadStructMembers( const spirv_cross::TypeID& type_id) const { const auto& struct_type = compiler_->get_type(type_id); - FML_CHECK(struct_type.basetype == SPIRType::BaseType::Struct); + FML_CHECK(struct_type.basetype == spirv_cross::SPIRType::BaseType::Struct); std::vector result; @@ -400,10 +402,10 @@ std::vector Reflector::ReadStructMembers( // Tightly packed 4x4 Matrix is special cased as we know how to work with // those. - if (member.basetype == SPIRType::BaseType::Float && // - member.width == sizeof(Scalar) * 8 && // - member.columns == 4 && // - member.vecsize == 4 // + if (member.basetype == spirv_cross::SPIRType::BaseType::Float && // + member.width == sizeof(Scalar) * 8 && // + member.columns == 4 && // + member.vecsize == 4 // ) { result.emplace_back(StructMember{ .type = "Matrix", @@ -416,10 +418,10 @@ std::vector Reflector::ReadStructMembers( } // Tightly packed Point (vec2). - if (member.basetype == SPIRType::BaseType::Float && // - member.width == sizeof(float) * 8 && // - member.columns == 1 && // - member.vecsize == 2 // + if (member.basetype == spirv_cross::SPIRType::BaseType::Float && // + member.width == sizeof(float) * 8 && // + member.columns == 1 && // + member.vecsize == 2 // ) { result.emplace_back(StructMember{ .type = "Point", @@ -432,10 +434,10 @@ std::vector Reflector::ReadStructMembers( } // Tightly packed Vector3. - if (member.basetype == SPIRType::BaseType::Float && // - member.width == sizeof(float) * 8 && // - member.columns == 1 && // - member.vecsize == 3 // + if (member.basetype == spirv_cross::SPIRType::BaseType::Float && // + member.width == sizeof(float) * 8 && // + member.columns == 1 && // + member.vecsize == 3 // ) { result.emplace_back(StructMember{ .type = "Vector3", @@ -448,10 +450,10 @@ std::vector Reflector::ReadStructMembers( } // Tightly packed Vector4. - if (member.basetype == SPIRType::BaseType::Float && // - member.width == sizeof(float) * 8 && // - member.columns == 1 && // - member.vecsize == 4 // + if (member.basetype == spirv_cross::SPIRType::BaseType::Float && // + member.width == sizeof(float) * 8 && // + member.columns == 1 && // + member.vecsize == 4 // ) { result.emplace_back(StructMember{ .type = "Vector4", @@ -501,9 +503,9 @@ std::vector Reflector::ReadStructMembers( } std::optional Reflector::ReflectStructDefinition( - const TypeID& type_id) const { + const spirv_cross::TypeID& type_id) const { const auto& type = compiler_->get_type(type_id); - if (type.basetype != SPIRType::BaseType::Struct) { + if (type.basetype != spirv_cross::SPIRType::BaseType::Struct) { return std::nullopt; } @@ -541,22 +543,26 @@ struct VertexType { size_t byte_length = 0u; }; -static VertexType VertexTypeFromInputResource(const CompilerMSL& compiler, - const Resource* resource) { +static VertexType VertexTypeFromInputResource( + const spirv_cross::CompilerMSL& compiler, + const spirv_cross::Resource* resource) { VertexType result; result.variable_name = resource->name; auto type = compiler.get_type(resource->type_id); const auto total_size = type.columns * type.vecsize * type.width / 8u; result.byte_length = total_size; - if (type.basetype == SPIRType::BaseType::Float && type.columns == 1u && - type.vecsize == 2u && type.width == sizeof(float) * 8u) { + if (type.basetype == spirv_cross::SPIRType::BaseType::Float && + type.columns == 1u && type.vecsize == 2u && + type.width == sizeof(float) * 8u) { result.type_name = "Point"; - } else if (type.basetype == SPIRType::BaseType::Float && type.columns == 1u && - type.vecsize == 4u && type.width == sizeof(float) * 8u) { + } else if (type.basetype == spirv_cross::SPIRType::BaseType::Float && + type.columns == 1u && type.vecsize == 4u && + type.width == sizeof(float) * 8u) { result.type_name = "Vector4"; - } else if (type.basetype == SPIRType::BaseType::Float && type.columns == 1u && - type.vecsize == 3u && type.width == sizeof(float) * 8u) { + } else if (type.basetype == spirv_cross::SPIRType::BaseType::Float && + type.columns == 1u && type.vecsize == 3u && + type.width == sizeof(float) * 8u) { result.type_name = "Vector3"; } else { // Catch all unknown padding. @@ -568,7 +574,7 @@ static VertexType VertexTypeFromInputResource(const CompilerMSL& compiler, std::optional Reflector::ReflectPerVertexStructDefinition( - const SmallVector& stage_inputs) const { + const spirv_cross::SmallVector& stage_inputs) const { // Avoid emitting a zero sized structure. The code gen templates assume a // non-zero size. if (stage_inputs.empty()) { @@ -594,7 +600,8 @@ Reflector::ReflectPerVertexStructDefinition( } } - auto input_for_location = [&](uint32_t queried_location) -> const Resource* { + auto input_for_location = + [&](uint32_t queried_location) -> const spirv_cross::Resource* { for (const auto& input : stage_inputs) { auto location = compiler_->get_decoration( input.id, spv::Decoration::DecorationLocation); diff --git a/impeller/entity/entity.h b/impeller/entity/entity.h index 2c35974498796..cf0bc3d9209f8 100644 --- a/impeller/entity/entity.h +++ b/impeller/entity/entity.h @@ -18,32 +18,12 @@ class Entity { ~Entity(); - /** - * The transformation that is applied to the entity about its anchor point - * - * @return the transformation applied to the node - */ const Matrix& GetTransformation() const; - /** - * Sets the transformation of the entity - * - * @param transformation the new transformation - */ void SetTransformation(const Matrix& transformation); - /** - * The background color of the entity - * - * @return the background color - */ const Color& GetBackgroundColor() const; - /** - * Set the new background color of the entity - * - * @param backgroundColor the new background color - */ void SetBackgroundColor(const Color& backgroundColor); const Color& GetStrokeColor() const; @@ -59,9 +39,6 @@ class Entity { void SetPath(Path path); private: - Rect bounds_; - Point position_; - Point anchor_point_ = {0.5, 0.5}; Matrix transformation_; Color background_color_; diff --git a/impeller/geometry/matrix.h b/impeller/geometry/matrix.h index 4d603845cc462..f88eafb6e0504 100644 --- a/impeller/geometry/matrix.h +++ b/impeller/geometry/matrix.h @@ -53,10 +53,13 @@ struct Matrix { uint64_t GetComponentsMask() const; }; - // TODO(csg): Radar restructions of C++11 don't exist. Use optionals instead. + // TODO(csg): Radar restrictions of C++11 don't exist. Use optionals instead. using DecompositionResult = std::pair; + //---------------------------------------------------------------------------- + /// Construts a default identity matrix. + /// constexpr Matrix() // clang-format off : vec{ Vector4(1.0, 0.0, 0.0, 0.0), From 9f0660fabfd6546154fa55b767f3acc1963f5dd8 Mon Sep 17 00:00:00 2001 From: Chinmay Garde Date: Tue, 10 Aug 2021 14:38:14 -0700 Subject: [PATCH 159/433] Stub all canvas related aiks containers. --- impeller/BUILD.gn | 2 +- impeller/aiks/BUILD.gn | 2 ++ impeller/aiks/canvas.cc | 20 +++++++++++++++++++- impeller/aiks/canvas.h | 11 +++++++++++ impeller/aiks/paint.h | 2 -- impeller/aiks/picture.cc | 13 +++++++++++++ impeller/aiks/picture.h | 21 +++++++++++++++++++++ impeller/geometry/rect.h | 7 +++++++ 8 files changed, 74 insertions(+), 4 deletions(-) create mode 100644 impeller/aiks/picture.cc create mode 100644 impeller/aiks/picture.h diff --git a/impeller/BUILD.gn b/impeller/BUILD.gn index 167ba3b42b26b..1d77a23fc650c 100644 --- a/impeller/BUILD.gn +++ b/impeller/BUILD.gn @@ -7,7 +7,7 @@ config("impeller_public_config") { } group("impeller") { - deps = [ + public_deps = [ "aiks", "base", "compiler", diff --git a/impeller/aiks/BUILD.gn b/impeller/aiks/BUILD.gn index c4b8829a14b46..2f735582de3ef 100644 --- a/impeller/aiks/BUILD.gn +++ b/impeller/aiks/BUILD.gn @@ -12,6 +12,8 @@ impeller_component("aiks") { "image.h", "paint.cc", "paint.h", + "picture.cc", + "picture.h", ] public_deps = [ diff --git a/impeller/aiks/canvas.cc b/impeller/aiks/canvas.cc index 4de595e6debb6..f3b32fe523950 100644 --- a/impeller/aiks/canvas.cc +++ b/impeller/aiks/canvas.cc @@ -52,6 +52,24 @@ size_t Canvas::GetSaveCount() const { return xformation_stack_.size(); } -void Canvas::DrawPath(Path path, Paint paint) {} +void Canvas::DrawPath(Path path, Paint paint) { + FML_LOG(ERROR) << "WIP"; +} + +void Canvas::SaveLayer(const Paint& paint, std::optional bounds) { + FML_LOG(ERROR) << "WIP"; +} + +void Canvas::ClipPath(Path path) { + FML_LOG(ERROR) << "WIP"; +} + +void Canvas::DrawShadow(Path path, Color color, Scalar elevation) { + FML_LOG(ERROR) << "WIP"; +} + +void Canvas::DrawPicture(std::shared_ptr picture) { + FML_LOG(ERROR) << "WIP"; +} } // namespace impeller diff --git a/impeller/aiks/canvas.h b/impeller/aiks/canvas.h index 86f2ed048be1f..bf5ff3b895c53 100644 --- a/impeller/aiks/canvas.h +++ b/impeller/aiks/canvas.h @@ -4,10 +4,13 @@ #pragma once +#include +#include #include #include "flutter/fml/macros.h" #include "impeller/aiks/paint.h" +#include "impeller/aiks/picture.h" #include "impeller/geometry/matrix.h" #include "impeller/geometry/path.h" #include "impeller/geometry/vector.h" @@ -22,6 +25,8 @@ class Canvas { void Save(); + void SaveLayer(const Paint& paint, std::optional bounds = std::nullopt); + bool Restore(); size_t GetSaveCount() const; @@ -38,6 +43,12 @@ class Canvas { void DrawPath(Path path, Paint paint); + void ClipPath(Path path); + + void DrawShadow(Path path, Color color, Scalar elevation); + + void DrawPicture(std::shared_ptr picture); + private: std::stack xformation_stack_; diff --git a/impeller/aiks/paint.h b/impeller/aiks/paint.h index ae72142cb94af..01e736ca0802f 100644 --- a/impeller/aiks/paint.h +++ b/impeller/aiks/paint.h @@ -20,8 +20,6 @@ class Paint { private: Color color_; - - FML_DISALLOW_COPY_AND_ASSIGN(Paint); }; } // namespace impeller diff --git a/impeller/aiks/picture.cc b/impeller/aiks/picture.cc new file mode 100644 index 0000000000000..14217674b4c28 --- /dev/null +++ b/impeller/aiks/picture.cc @@ -0,0 +1,13 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "impeller/aiks/picture.h" + +namespace impeller { + +Picture::Picture() = default; + +Picture::~Picture() = default; + +} // namespace impeller diff --git a/impeller/aiks/picture.h b/impeller/aiks/picture.h new file mode 100644 index 0000000000000..3ced1ed0c25fb --- /dev/null +++ b/impeller/aiks/picture.h @@ -0,0 +1,21 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#pragma once + +#include "flutter/fml/macros.h" + +namespace impeller { + +class Picture { + public: + Picture(); + + ~Picture(); + + private: + FML_DISALLOW_COPY_AND_ASSIGN(Picture); +}; + +} // namespace impeller diff --git a/impeller/geometry/rect.h b/impeller/geometry/rect.h index 02a51a3642f01..deebcfccb853c 100644 --- a/impeller/geometry/rect.h +++ b/impeller/geometry/rect.h @@ -33,6 +33,13 @@ struct TRect { constexpr TRect(Type x, Type y, Type width, Type height) : origin(x, y), size(width, height) {} + constexpr static TRect MakeLTRB(Type left, + Type top, + Type right, + Type bottom) { + return TRect(left, top, right - left, bottom - top); + } + template constexpr explicit TRect(const TRect& other) : origin(static_cast>(other.origin)), From f905c6dd89f112d6b6c2283cd5926c1d9095dcbc Mon Sep 17 00:00:00 2001 From: Chinmay Garde Date: Wed, 11 Aug 2021 14:50:23 -0700 Subject: [PATCH 160/433] Stub out picture recorders and operations. --- impeller/aiks/BUILD.gn | 4 ++++ impeller/aiks/canvas.cc | 23 +++++++++-------------- impeller/aiks/canvas.h | 6 ++++++ impeller/aiks/picture.cc | 5 ++++- impeller/aiks/picture.h | 9 ++++++++- impeller/aiks/picture_operation.cc | 13 +++++++++++++ impeller/aiks/picture_operation.h | 21 +++++++++++++++++++++ impeller/aiks/picture_recorder.cc | 23 +++++++++++++++++++++++ impeller/aiks/picture_recorder.h | 27 +++++++++++++++++++++++++++ 9 files changed, 115 insertions(+), 16 deletions(-) create mode 100644 impeller/aiks/picture_operation.cc create mode 100644 impeller/aiks/picture_operation.h create mode 100644 impeller/aiks/picture_recorder.cc create mode 100644 impeller/aiks/picture_recorder.h diff --git a/impeller/aiks/BUILD.gn b/impeller/aiks/BUILD.gn index 2f735582de3ef..aa14b0af7cc8b 100644 --- a/impeller/aiks/BUILD.gn +++ b/impeller/aiks/BUILD.gn @@ -14,6 +14,10 @@ impeller_component("aiks") { "paint.h", "picture.cc", "picture.h", + "picture_operation.cc", + "picture_operation.h", + "picture_recorder.cc", + "picture_recorder.h", ] public_deps = [ diff --git a/impeller/aiks/canvas.cc b/impeller/aiks/canvas.cc index f3b32fe523950..633230b9ffb65 100644 --- a/impeller/aiks/canvas.cc +++ b/impeller/aiks/canvas.cc @@ -5,6 +5,7 @@ #include "impeller/aiks/canvas.h" #include "flutter/fml/logging.h" +#include "impeller/aiks/picture_operation.h" namespace impeller { @@ -52,24 +53,18 @@ size_t Canvas::GetSaveCount() const { return xformation_stack_.size(); } -void Canvas::DrawPath(Path path, Paint paint) { - FML_LOG(ERROR) << "WIP"; -} +void Canvas::DrawPath(Path path, Paint paint) {} -void Canvas::SaveLayer(const Paint& paint, std::optional bounds) { - FML_LOG(ERROR) << "WIP"; -} +void Canvas::SaveLayer(const Paint& paint, std::optional bounds) {} -void Canvas::ClipPath(Path path) { - FML_LOG(ERROR) << "WIP"; -} +void Canvas::ClipPath(Path path) {} -void Canvas::DrawShadow(Path path, Color color, Scalar elevation) { - FML_LOG(ERROR) << "WIP"; -} +void Canvas::DrawShadow(Path path, Color color, Scalar elevation) {} + +void Canvas::DrawPicture(std::shared_ptr picture) {} -void Canvas::DrawPicture(std::shared_ptr picture) { - FML_LOG(ERROR) << "WIP"; +std::shared_ptr Canvas::EndRecordingAsPicture() { + return std::make_shared(std::move(ops_)); } } // namespace impeller diff --git a/impeller/aiks/canvas.h b/impeller/aiks/canvas.h index bf5ff3b895c53..e35d0f837a73a 100644 --- a/impeller/aiks/canvas.h +++ b/impeller/aiks/canvas.h @@ -7,6 +7,7 @@ #include #include #include +#include #include "flutter/fml/macros.h" #include "impeller/aiks/paint.h" @@ -17,6 +18,8 @@ namespace impeller { +class PictureOperation; + class Canvas { public: Canvas(); @@ -49,8 +52,11 @@ class Canvas { void DrawPicture(std::shared_ptr picture); + std::shared_ptr EndRecordingAsPicture(); + private: std::stack xformation_stack_; + std::vector> ops_; FML_DISALLOW_COPY_AND_ASSIGN(Canvas); }; diff --git a/impeller/aiks/picture.cc b/impeller/aiks/picture.cc index 14217674b4c28..9f7174bb45701 100644 --- a/impeller/aiks/picture.cc +++ b/impeller/aiks/picture.cc @@ -4,9 +4,12 @@ #include "impeller/aiks/picture.h" +#include "impeller/aiks/picture_operation.h" + namespace impeller { -Picture::Picture() = default; +Picture::Picture(std::vector> operations) + : ops_(std::move(operations)) {} Picture::~Picture() = default; diff --git a/impeller/aiks/picture.h b/impeller/aiks/picture.h index 3ced1ed0c25fb..9b43516129c61 100644 --- a/impeller/aiks/picture.h +++ b/impeller/aiks/picture.h @@ -4,17 +4,24 @@ #pragma once +#include +#include + #include "flutter/fml/macros.h" namespace impeller { +class PictureOperation; + class Picture { public: - Picture(); + Picture(std::vector> operations); ~Picture(); private: + std::vector> ops_; + FML_DISALLOW_COPY_AND_ASSIGN(Picture); }; diff --git a/impeller/aiks/picture_operation.cc b/impeller/aiks/picture_operation.cc new file mode 100644 index 0000000000000..ff7ad1f7fab2a --- /dev/null +++ b/impeller/aiks/picture_operation.cc @@ -0,0 +1,13 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "flutter/impeller/aiks/picture_operation.h" + +namespace impeller { + +PictureOperation::PictureOperation() = default; + +PictureOperation::~PictureOperation() = default; + +} // namespace impeller diff --git a/impeller/aiks/picture_operation.h b/impeller/aiks/picture_operation.h new file mode 100644 index 0000000000000..cfb63891d54ed --- /dev/null +++ b/impeller/aiks/picture_operation.h @@ -0,0 +1,21 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#pragma once + +#include "flutter/fml/macros.h" + +namespace impeller { + +class PictureOperation { + public: + PictureOperation(); + + ~PictureOperation(); + + private: + FML_DISALLOW_COPY_AND_ASSIGN(PictureOperation); +}; + +} // namespace impeller diff --git a/impeller/aiks/picture_recorder.cc b/impeller/aiks/picture_recorder.cc new file mode 100644 index 0000000000000..44d76f4451b07 --- /dev/null +++ b/impeller/aiks/picture_recorder.cc @@ -0,0 +1,23 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "flutter/impeller/aiks/picture_recorder.h" + +#include "flutter/impeller/aiks/canvas.h" + +namespace impeller { + +PictureRecorder::PictureRecorder() : canvas_(std::make_shared()) {} + +PictureRecorder::~PictureRecorder() = default; + +std::shared_ptr PictureRecorder::GetCanvas() const { + return canvas_; +} + +std::shared_ptr PictureRecorder::EndRecordingAsPicture() { + return canvas_->EndRecordingAsPicture(); +} + +} // namespace impeller diff --git a/impeller/aiks/picture_recorder.h b/impeller/aiks/picture_recorder.h new file mode 100644 index 0000000000000..16c4a72aa34be --- /dev/null +++ b/impeller/aiks/picture_recorder.h @@ -0,0 +1,27 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#pragma once + +#include "flutter/fml/macros.h" +#include "impeller/aiks/canvas.h" +#include "impeller/aiks/picture.h" + +namespace impeller { + +class PictureRecorder { + public: + PictureRecorder(); + + ~PictureRecorder(); + + std::shared_ptr GetCanvas() const; + + std::shared_ptr EndRecordingAsPicture(); + + private: + std::shared_ptr canvas_; +}; + +} // namespace impeller From 1d874811852285b8dfd105de86489de5b90c6bf5 Mon Sep 17 00:00:00 2001 From: Chinmay Garde Date: Tue, 17 Aug 2021 23:19:53 -0700 Subject: [PATCH 161/433] WIP on wiring up the aiks entity renderer. --- impeller/aiks/BUILD.gn | 4 +-- impeller/aiks/canvas.cc | 30 ++++++++++++++---- impeller/aiks/canvas.h | 8 ++--- impeller/aiks/paint.cc | 8 +---- impeller/aiks/paint.h | 12 ++----- impeller/aiks/picture.cc | 7 ++--- impeller/aiks/picture.h | 15 ++------- impeller/aiks/picture_operation.cc | 13 -------- impeller/aiks/picture_operation.h | 21 ------------- impeller/aiks/picture_recorder.cc | 2 +- impeller/aiks/picture_recorder.h | 2 +- impeller/aiks/picture_renderer.h | 36 +++++++++++++++++++++ impeller/aiks/picture_renderer.mm | 38 ++++++++++++++++++++++ impeller/compositor/context.h | 3 +- impeller/compositor/context.mm | 5 +-- impeller/compositor/renderer.h | 2 +- impeller/compositor/renderer.mm | 5 +-- impeller/entity/BUILD.gn | 15 +++++++++ impeller/entity/entity.cc | 30 ++++++++++++++++++ impeller/entity/entity.h | 11 +++++-- impeller/entity/entity_renderer.h | 38 ++++++++++++++++++++++ impeller/entity/entity_renderer.mm | 42 +++++++++++++++++++++++++ impeller/entity/entity_renderer_impl.h | 40 +++++++++++++++++++++++ impeller/entity/entity_renderer_impl.mm | 33 +++++++++++++++++++ impeller/entity/path.vert | 13 ++++++++ impeller/entity/solid_fill.frag | 9 ++++++ impeller/geometry/color.h | 2 ++ 27 files changed, 354 insertions(+), 90 deletions(-) delete mode 100644 impeller/aiks/picture_operation.cc delete mode 100644 impeller/aiks/picture_operation.h create mode 100644 impeller/aiks/picture_renderer.h create mode 100644 impeller/aiks/picture_renderer.mm create mode 100644 impeller/entity/entity_renderer.h create mode 100644 impeller/entity/entity_renderer.mm create mode 100644 impeller/entity/entity_renderer_impl.h create mode 100644 impeller/entity/entity_renderer_impl.mm create mode 100644 impeller/entity/path.vert create mode 100644 impeller/entity/solid_fill.frag diff --git a/impeller/aiks/BUILD.gn b/impeller/aiks/BUILD.gn index aa14b0af7cc8b..5d3d66f7dfab8 100644 --- a/impeller/aiks/BUILD.gn +++ b/impeller/aiks/BUILD.gn @@ -14,10 +14,10 @@ impeller_component("aiks") { "paint.h", "picture.cc", "picture.h", - "picture_operation.cc", - "picture_operation.h", "picture_recorder.cc", "picture_recorder.h", + "picture_renderer.h", + "picture_renderer.mm", ] public_deps = [ diff --git a/impeller/aiks/canvas.cc b/impeller/aiks/canvas.cc index 633230b9ffb65..8da1754be2158 100644 --- a/impeller/aiks/canvas.cc +++ b/impeller/aiks/canvas.cc @@ -4,8 +4,9 @@ #include "impeller/aiks/canvas.h" +#include + #include "flutter/fml/logging.h" -#include "impeller/aiks/picture_operation.h" namespace impeller { @@ -53,18 +54,35 @@ size_t Canvas::GetSaveCount() const { return xformation_stack_.size(); } -void Canvas::DrawPath(Path path, Paint paint) {} +void Canvas::DrawPath(Path path, Paint paint) { + Entity entity; + entity.SetTransformation(GetCurrentTransformation()); + entity.SetPath(std::move(path)); + entity.SetBackgroundColor(paint.color); + ops_.emplace_back(std::move(entity)); +} void Canvas::SaveLayer(const Paint& paint, std::optional bounds) {} -void Canvas::ClipPath(Path path) {} +void Canvas::ClipPath(Path path) { + Entity entity; + entity.SetTransformation(GetCurrentTransformation()); + entity.SetPath(std::move(path)); + entity.SetIsClip(true); + ops_.emplace_back(std::move(entity)); +} void Canvas::DrawShadow(Path path, Color color, Scalar elevation) {} -void Canvas::DrawPicture(std::shared_ptr picture) {} +void Canvas::DrawPicture(const Picture& picture) { + std::copy(std::begin(picture.entities), std::end(picture.entities), + std::back_inserter(ops_)); +} -std::shared_ptr Canvas::EndRecordingAsPicture() { - return std::make_shared(std::move(ops_)); +Picture Canvas::EndRecordingAsPicture() { + Picture picture; + picture.entities = std::move(ops_); + return picture; } } // namespace impeller diff --git a/impeller/aiks/canvas.h b/impeller/aiks/canvas.h index e35d0f837a73a..b7513493ac2bf 100644 --- a/impeller/aiks/canvas.h +++ b/impeller/aiks/canvas.h @@ -18,7 +18,7 @@ namespace impeller { -class PictureOperation; +class Entity; class Canvas { public: @@ -50,13 +50,13 @@ class Canvas { void DrawShadow(Path path, Color color, Scalar elevation); - void DrawPicture(std::shared_ptr picture); + void DrawPicture(const Picture& picture); - std::shared_ptr EndRecordingAsPicture(); + Picture EndRecordingAsPicture(); private: std::stack xformation_stack_; - std::vector> ops_; + std::vector ops_; FML_DISALLOW_COPY_AND_ASSIGN(Canvas); }; diff --git a/impeller/aiks/paint.cc b/impeller/aiks/paint.cc index a9613dbd63aba..05279a4ecc5be 100644 --- a/impeller/aiks/paint.cc +++ b/impeller/aiks/paint.cc @@ -6,12 +6,6 @@ namespace impeller { -Paint::Paint() = default; - -Paint::~Paint() = default; - -void Paint::SetColor(Color color) { - color_ = std::move(color); -} +// } // namespace impeller diff --git a/impeller/aiks/paint.h b/impeller/aiks/paint.h index 01e736ca0802f..805f719e3ad66 100644 --- a/impeller/aiks/paint.h +++ b/impeller/aiks/paint.h @@ -10,16 +10,8 @@ namespace impeller { -class Paint { - public: - Paint(); - - ~Paint(); - - void SetColor(Color color); - - private: - Color color_; +struct Paint { + Color color; }; } // namespace impeller diff --git a/impeller/aiks/picture.cc b/impeller/aiks/picture.cc index 9f7174bb45701..22e00e5cf8735 100644 --- a/impeller/aiks/picture.cc +++ b/impeller/aiks/picture.cc @@ -4,13 +4,10 @@ #include "impeller/aiks/picture.h" -#include "impeller/aiks/picture_operation.h" +#include "impeller/entity/entity.h" namespace impeller { -Picture::Picture(std::vector> operations) - : ops_(std::move(operations)) {} - -Picture::~Picture() = default; +// } // namespace impeller diff --git a/impeller/aiks/picture.h b/impeller/aiks/picture.h index 9b43516129c61..d65d272c48907 100644 --- a/impeller/aiks/picture.h +++ b/impeller/aiks/picture.h @@ -8,21 +8,12 @@ #include #include "flutter/fml/macros.h" +#include "impeller/entity/entity.h" namespace impeller { -class PictureOperation; - -class Picture { - public: - Picture(std::vector> operations); - - ~Picture(); - - private: - std::vector> ops_; - - FML_DISALLOW_COPY_AND_ASSIGN(Picture); +struct Picture { + std::vector entities; }; } // namespace impeller diff --git a/impeller/aiks/picture_operation.cc b/impeller/aiks/picture_operation.cc deleted file mode 100644 index ff7ad1f7fab2a..0000000000000 --- a/impeller/aiks/picture_operation.cc +++ /dev/null @@ -1,13 +0,0 @@ -// Copyright 2013 The Flutter Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#include "flutter/impeller/aiks/picture_operation.h" - -namespace impeller { - -PictureOperation::PictureOperation() = default; - -PictureOperation::~PictureOperation() = default; - -} // namespace impeller diff --git a/impeller/aiks/picture_operation.h b/impeller/aiks/picture_operation.h deleted file mode 100644 index cfb63891d54ed..0000000000000 --- a/impeller/aiks/picture_operation.h +++ /dev/null @@ -1,21 +0,0 @@ -// Copyright 2013 The Flutter Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#pragma once - -#include "flutter/fml/macros.h" - -namespace impeller { - -class PictureOperation { - public: - PictureOperation(); - - ~PictureOperation(); - - private: - FML_DISALLOW_COPY_AND_ASSIGN(PictureOperation); -}; - -} // namespace impeller diff --git a/impeller/aiks/picture_recorder.cc b/impeller/aiks/picture_recorder.cc index 44d76f4451b07..c4040ca19e522 100644 --- a/impeller/aiks/picture_recorder.cc +++ b/impeller/aiks/picture_recorder.cc @@ -16,7 +16,7 @@ std::shared_ptr PictureRecorder::GetCanvas() const { return canvas_; } -std::shared_ptr PictureRecorder::EndRecordingAsPicture() { +Picture PictureRecorder::EndRecordingAsPicture() { return canvas_->EndRecordingAsPicture(); } diff --git a/impeller/aiks/picture_recorder.h b/impeller/aiks/picture_recorder.h index 16c4a72aa34be..739792a2df1b5 100644 --- a/impeller/aiks/picture_recorder.h +++ b/impeller/aiks/picture_recorder.h @@ -18,7 +18,7 @@ class PictureRecorder { std::shared_ptr GetCanvas() const; - std::shared_ptr EndRecordingAsPicture(); + Picture EndRecordingAsPicture(); private: std::shared_ptr canvas_; diff --git a/impeller/aiks/picture_renderer.h b/impeller/aiks/picture_renderer.h new file mode 100644 index 0000000000000..83ffcc04bd8ef --- /dev/null +++ b/impeller/aiks/picture_renderer.h @@ -0,0 +1,36 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#pragma once + +#include "flutter/fml/macros.h" +#include "impeller/entity/entity_renderer.h" + +namespace impeller { + +class Surface; +class RenderPass; +class Context; +struct Picture; + +class PictureRenderer { + public: + PictureRenderer(std::shared_ptr context); + + ~PictureRenderer(); + + bool IsValid() const; + + [[nodiscard]] bool Render(const Surface& surface, + const RenderPass& onscreen_pass, + const Picture& picture); + + private: + EntityRenderer entity_renderer_; + bool is_valid_ = false; + + FML_DISALLOW_COPY_AND_ASSIGN(PictureRenderer); +}; + +} // namespace impeller diff --git a/impeller/aiks/picture_renderer.mm b/impeller/aiks/picture_renderer.mm new file mode 100644 index 0000000000000..826da4cd74c13 --- /dev/null +++ b/impeller/aiks/picture_renderer.mm @@ -0,0 +1,38 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "impeller/aiks/picture_renderer.h" + +#include "impeller/aiks/picture.h" + +namespace impeller { + +PictureRenderer::PictureRenderer(std::shared_ptr context) + : entity_renderer_(std::move(context)) { + if (!entity_renderer_.IsValid()) { + return; + } + is_valid_ = true; +} + +PictureRenderer::~PictureRenderer() = default; + +bool PictureRenderer::IsValid() const { + return is_valid_; +} + +bool PictureRenderer::Render(const Surface& surface, + const RenderPass& onscreen_pass, + const Picture& picture) { + if (!IsValid()) { + return false; + } + + return entity_renderer_.RenderEntities(surface, // + onscreen_pass, // + picture.entities // + ); +} + +} // namespace impeller diff --git a/impeller/compositor/context.h b/impeller/compositor/context.h index 66e13b7cdabc7..64355e1517ed0 100644 --- a/impeller/compositor/context.h +++ b/impeller/compositor/context.h @@ -7,6 +7,7 @@ #include #include +#include #include "flutter/fml/macros.h" #include "impeller/compositor/pipeline_library.h" @@ -20,7 +21,7 @@ class Allocator; class Context { public: - Context(std::string shaders_directory); + Context(std::string shaders_directory, std::string main_library_file_name); ~Context(); diff --git a/impeller/compositor/context.mm b/impeller/compositor/context.mm index fd93e198a4789..82a4773612c86 100644 --- a/impeller/compositor/context.mm +++ b/impeller/compositor/context.mm @@ -14,7 +14,8 @@ namespace impeller { -Context::Context(std::string shaders_directory) +Context::Context(std::string shaders_directory, + std::string main_library_file_name) : device_(::MTLCreateSystemDefaultDevice()) { // Setup device. if (!device_) { @@ -36,7 +37,7 @@ { NSError* shader_library_error = nil; auto shader_library_path = - fml::paths::JoinPaths({shaders_directory, "impeller.metallib"}); + fml::paths::JoinPaths({shaders_directory, main_library_file_name}); auto library_exists = fml::IsFile(shader_library_path); diff --git a/impeller/compositor/renderer.h b/impeller/compositor/renderer.h index 61a1258c31432..89b95c108b18f 100644 --- a/impeller/compositor/renderer.h +++ b/impeller/compositor/renderer.h @@ -23,7 +23,7 @@ class Renderer { using RenderCallback = std::function; - Renderer(std::string shaders_directory); + Renderer(std::string shaders_directory, std::string main_library_file); ~Renderer(); diff --git a/impeller/compositor/renderer.mm b/impeller/compositor/renderer.mm index ed13c1ce89e04..0bed94141f72c 100644 --- a/impeller/compositor/renderer.mm +++ b/impeller/compositor/renderer.mm @@ -12,9 +12,10 @@ constexpr size_t kMaxFramesInFlight = 3u; -Renderer::Renderer(std::string shaders_directory) +Renderer::Renderer(std::string shaders_directory, std::string main_library_file) : frames_in_flight_sema_(::dispatch_semaphore_create(kMaxFramesInFlight)), - context_(std::make_shared(std::move(shaders_directory))) { + context_(std::make_shared(std::move(shaders_directory), + std::move(main_library_file))) { if (!context_->IsValid()) { return; } diff --git a/impeller/entity/BUILD.gn b/impeller/entity/BUILD.gn index a321fa03bb65e..fece2147865e1 100644 --- a/impeller/entity/BUILD.gn +++ b/impeller/entity/BUILD.gn @@ -4,12 +4,27 @@ import("//flutter/impeller/tools/impeller.gni") +impeller_shaders("entity_shaders") { + name = "entity" + + shaders = [ + "path.vert", + "solid_fill.frag", + ] +} + impeller_component("entity") { sources = [ "entity.cc", "entity.h", + "entity_renderer.h", + "entity_renderer.mm", + "entity_renderer_impl.h", + "entity_renderer_impl.mm", ] + deps = [ ":entity_shaders" ] + public_deps = [ "../compositor", "../image", diff --git a/impeller/entity/entity.cc b/impeller/entity/entity.cc index 510a72d0e986a..75c9a5e722231 100644 --- a/impeller/entity/entity.cc +++ b/impeller/entity/entity.cc @@ -50,4 +50,34 @@ void Entity::SetPath(Path path) { path_ = std::move(path); } +void Entity::SetIsClip(bool is_clip) { + is_clip_ = is_clip; +} + +bool Entity::IsClip() const { + return is_clip_; +} + +bool Entity::HasStroke() const { + return stroke_size_ > 0.0 && !stroke_color_.IsTransparent(); +} + +bool Entity::HasRenderableContents() const { + const bool has_empty_path = path_.GetBoundingBox().IsZero(); + + if (has_empty_path) { + return false; + } + + if (HasStroke()) { + return true; + } + + if (!background_color_.IsTransparent()) { + return true; + } + + return false; +} + } // namespace impeller diff --git a/impeller/entity/entity.h b/impeller/entity/entity.h index cf0bc3d9209f8..5b3fedee7617b 100644 --- a/impeller/entity/entity.h +++ b/impeller/entity/entity.h @@ -38,6 +38,14 @@ class Entity { void SetPath(Path path); + void SetIsClip(bool is_clip); + + bool IsClip() const; + + bool HasStroke() const; + + bool HasRenderableContents() const; + private: Matrix transformation_; Color background_color_; @@ -45,8 +53,7 @@ class Entity { Path path_; Color stroke_color_; double stroke_size_ = 1.0; - - FML_DISALLOW_COPY_AND_ASSIGN(Entity); + bool is_clip_ = false; }; } // namespace impeller diff --git a/impeller/entity/entity_renderer.h b/impeller/entity/entity_renderer.h new file mode 100644 index 0000000000000..4da3477011a52 --- /dev/null +++ b/impeller/entity/entity_renderer.h @@ -0,0 +1,38 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#pragma once + +#include + +#include "flutter/fml/macros.h" +#include "impeller/entity/entity.h" + +namespace impeller { + +class Surface; +class RenderPass; +class Context; +class EntityRendererImpl; + +class EntityRenderer { + public: + EntityRenderer(std::shared_ptr context); + + ~EntityRenderer(); + + bool IsValid() const; + + [[nodiscard]] bool RenderEntities(const Surface& surface, + const RenderPass& onscreen_pass, + const std::vector& entities) const; + + private: + std::unique_ptr renderer_; + bool is_valid_ = false; + + FML_DISALLOW_COPY_AND_ASSIGN(EntityRenderer); +}; + +} // namespace impeller diff --git a/impeller/entity/entity_renderer.mm b/impeller/entity/entity_renderer.mm new file mode 100644 index 0000000000000..c2a5cc7c2bfea --- /dev/null +++ b/impeller/entity/entity_renderer.mm @@ -0,0 +1,42 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "flutter/impeller/entity/entity_renderer.h" + +#include "flutter/fml/trace_event.h" +#include "flutter/impeller/entity/entity_renderer_impl.h" + +namespace impeller { + +EntityRenderer::EntityRenderer(std::shared_ptr context) + : renderer_(std::make_unique(std::move(context))) { + if (!renderer_->IsValid()) { + return; + } + is_valid_ = true; +} + +EntityRenderer::~EntityRenderer() = default; + +bool EntityRenderer::IsValid() const { + return is_valid_; +} + +bool EntityRenderer::RenderEntities(const Surface& surface, + const RenderPass& onscreen_pass, + const std::vector& entities) const { + if (!IsValid()) { + return false; + } + + for (const auto& entity : entities) { + if (!renderer_->RenderEntity(surface, onscreen_pass, entity)) { + return false; + } + } + + return true; +} + +} // namespace impeller diff --git a/impeller/entity/entity_renderer_impl.h b/impeller/entity/entity_renderer_impl.h new file mode 100644 index 0000000000000..e2fcef772b215 --- /dev/null +++ b/impeller/entity/entity_renderer_impl.h @@ -0,0 +1,40 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#pragma once + +#include + +#include "flutter/fml/macros.h" +#include "impeller/compositor/context.h" +#include "impeller/compositor/render_pass.h" +#include "impeller/compositor/surface.h" + +namespace impeller { + +class Entity; + +// TODO(csg): Only present to hide Objective-C++ interface in the headers. Once +// the backend is separated into its own TU, this can be merged with +// EntityRenderer. +class EntityRendererImpl { + public: + EntityRendererImpl(std::shared_ptr context); + + ~EntityRendererImpl(); + + bool IsValid() const; + + [[nodiscard]] bool RenderEntity(const Surface& surface, + const RenderPass& onscreen_pass, + const Entity& entities); + + private: + std::shared_ptr context_; + bool is_valid_ = false; + + FML_DISALLOW_COPY_AND_ASSIGN(EntityRendererImpl); +}; + +} // namespace impeller diff --git a/impeller/entity/entity_renderer_impl.mm b/impeller/entity/entity_renderer_impl.mm new file mode 100644 index 0000000000000..ee37f9e608e7a --- /dev/null +++ b/impeller/entity/entity_renderer_impl.mm @@ -0,0 +1,33 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "impeller/entity/entity_renderer_impl.h" + +namespace impeller { + +EntityRendererImpl::EntityRendererImpl(std::shared_ptr context) + : context_(std::move(context)) { + if (!context_ || !context_->IsValid()) { + return; + } + is_valid_ = true; +} + +EntityRendererImpl::~EntityRendererImpl() = default; + +bool EntityRendererImpl::IsValid() const { + return is_valid_; +} + +bool EntityRendererImpl::RenderEntity(const Surface& surface, + const RenderPass& onscreen_pass, + const Entity& entity) { + if (!entity.HasRenderableContents()) { + return true; + } + + return true; +} + +} // namespace impeller diff --git a/impeller/entity/path.vert b/impeller/entity/path.vert new file mode 100644 index 0000000000000..ba38d88acd176 --- /dev/null +++ b/impeller/entity/path.vert @@ -0,0 +1,13 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +uniform FrameInfo { + mat4 mvp; +} frame_info; + +in vec2 vertices; + +void main() { + gl_Position = frame_info.mvp * vec4(vertices, 0.0, 1.0); +} diff --git a/impeller/entity/solid_fill.frag b/impeller/entity/solid_fill.frag new file mode 100644 index 0000000000000..ba7a8555c21da --- /dev/null +++ b/impeller/entity/solid_fill.frag @@ -0,0 +1,9 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +out vec4 frag_color; + +void main() { + frag_color = vec4(1.0, 1.0, 1.0, 1.0); +} diff --git a/impeller/geometry/color.h b/impeller/geometry/color.h index 0b2031ea411be..dea534b2a3e10 100644 --- a/impeller/geometry/color.h +++ b/impeller/geometry/color.h @@ -638,6 +638,8 @@ struct Color { 1.0 // }; } + + constexpr bool IsTransparent() const { return alpha == 0.0; } }; /** From 2d0c186c35ac6b790ec6dd77a7593de8f6c2315a Mon Sep 17 00:00:00 2001 From: Chinmay Garde Date: Wed, 18 Aug 2021 00:09:23 -0700 Subject: [PATCH 162/433] Add support for typed pipeline futures. --- impeller/compositor/context.h | 2 +- impeller/compositor/context.mm | 1 + impeller/compositor/pipeline.h | 34 +++++++++++++++++++++++-- impeller/compositor/pipeline.mm | 15 +++++++++++ impeller/compositor/pipeline_library.h | 6 ++--- impeller/entity/entity.cc | 4 +++ impeller/entity/entity_renderer_impl.h | 12 +++++++-- impeller/entity/entity_renderer_impl.mm | 3 +++ 8 files changed, 68 insertions(+), 9 deletions(-) diff --git a/impeller/compositor/context.h b/impeller/compositor/context.h index 64355e1517ed0..0da2ce6da01ff 100644 --- a/impeller/compositor/context.h +++ b/impeller/compositor/context.h @@ -10,13 +10,13 @@ #include #include "flutter/fml/macros.h" -#include "impeller/compositor/pipeline_library.h" namespace impeller { class ShaderLibrary; class SamplerLibrary; class CommandBuffer; +class PipelineLibrary; class Allocator; class Context { diff --git a/impeller/compositor/context.mm b/impeller/compositor/context.mm index 82a4773612c86..c9d1891e8f83e 100644 --- a/impeller/compositor/context.mm +++ b/impeller/compositor/context.mm @@ -9,6 +9,7 @@ #include "flutter/fml/paths.h" #include "impeller/compositor/allocator.h" #include "impeller/compositor/command_buffer.h" +#include "impeller/compositor/pipeline_library.h" #include "impeller/compositor/sampler_descriptor.h" #include "impeller/compositor/shader_library.h" diff --git a/impeller/compositor/pipeline.h b/impeller/compositor/pipeline.h index af95b46478750..59c4ddbd92fef 100644 --- a/impeller/compositor/pipeline.h +++ b/impeller/compositor/pipeline.h @@ -4,13 +4,20 @@ #pragma once -#include "flutter/fml/macros.h" - #include +#include + +#include "flutter/fml/macros.h" +#include "impeller/compositor/context.h" +#include "impeller/compositor/pipeline_builder.h" + namespace impeller { class PipelineLibrary; +class Pipeline; + +using PipelineFuture = std::future>; class Pipeline { public: @@ -41,4 +48,27 @@ class Pipeline { FML_DISALLOW_COPY_AND_ASSIGN(Pipeline); }; +PipelineFuture CreatePipelineFuture(const Context& context, + std::optional desc); + +template +class PipelineT { + public: + using VertexShader = VertexShader_; + using FragmentShader = FragmentShader_; + using Builder = PipelineBuilder; + + explicit PipelineT(const Context& context) + : pipeline_future_(CreatePipelineFuture( + context, + Builder::MakeDefaultPipelineDescriptor(context))) {} + + const Pipeline* WaitAndGet() { return pipeline_future_.get().get(); } + + private: + PipelineFuture pipeline_future_; + + FML_DISALLOW_COPY_AND_ASSIGN(PipelineT); +}; + } // namespace impeller diff --git a/impeller/compositor/pipeline.mm b/impeller/compositor/pipeline.mm index 00edc74271326..7a60c55a021d9 100644 --- a/impeller/compositor/pipeline.mm +++ b/impeller/compositor/pipeline.mm @@ -4,6 +4,9 @@ #include "impeller/compositor/pipeline.h" +#include "impeller/compositor/context.h" +#include "impeller/compositor/pipeline_library.h" + namespace impeller { Pipeline::Pipeline(id state, @@ -30,4 +33,16 @@ return depth_stencil_state_; } +PipelineFuture CreatePipelineFuture(const Context& context, + std::optional desc) { + if (!context.IsValid()) { + std::promise> promise; + auto future = promise.get_future(); + promise.set_value(nullptr); + return future; + } + + return context.GetPipelineLibrary()->GetRenderPipeline(std::move(desc)); +} + } // namespace impeller diff --git a/impeller/compositor/pipeline_library.h b/impeller/compositor/pipeline_library.h index 43ecfa8347745..5e72c18b99420 100644 --- a/impeller/compositor/pipeline_library.h +++ b/impeller/compositor/pipeline_library.h @@ -6,7 +6,6 @@ #include -#include #include #include @@ -22,11 +21,10 @@ class PipelineLibrary : public std::enable_shared_from_this { public: ~PipelineLibrary(); - std::future> GetRenderPipeline( + PipelineFuture GetRenderPipeline( std::optional descriptor); - std::future> GetRenderPipeline( - PipelineDescriptor descriptor); + PipelineFuture GetRenderPipeline(PipelineDescriptor descriptor); private: friend Context; diff --git a/impeller/entity/entity.cc b/impeller/entity/entity.cc index 75c9a5e722231..f6adb865111f8 100644 --- a/impeller/entity/entity.cc +++ b/impeller/entity/entity.cc @@ -69,6 +69,10 @@ bool Entity::HasRenderableContents() const { return false; } + if (IsClip()) { + return true; + } + if (HasStroke()) { return true; } diff --git a/impeller/entity/entity_renderer_impl.h b/impeller/entity/entity_renderer_impl.h index e2fcef772b215..1f2ffa778189b 100644 --- a/impeller/entity/entity_renderer_impl.h +++ b/impeller/entity/entity_renderer_impl.h @@ -8,13 +8,16 @@ #include "flutter/fml/macros.h" #include "impeller/compositor/context.h" +#include "impeller/compositor/pipeline.h" +#include "impeller/compositor/pipeline_builder.h" #include "impeller/compositor/render_pass.h" #include "impeller/compositor/surface.h" +#include "impeller/entity/entity.h" +#include "impeller/entity/path.vert.h" +#include "impeller/entity/solid_fill.frag.h" namespace impeller { -class Entity; - // TODO(csg): Only present to hide Objective-C++ interface in the headers. Once // the backend is separated into its own TU, this can be merged with // EntityRenderer. @@ -31,7 +34,12 @@ class EntityRendererImpl { const Entity& entities); private: + using SolidFillPipeline = + PipelineT; + std::shared_ptr context_; + std::unique_ptr solid_fill_pipeline_; + bool is_valid_ = false; FML_DISALLOW_COPY_AND_ASSIGN(EntityRendererImpl); diff --git a/impeller/entity/entity_renderer_impl.mm b/impeller/entity/entity_renderer_impl.mm index ee37f9e608e7a..e148d4024ddc9 100644 --- a/impeller/entity/entity_renderer_impl.mm +++ b/impeller/entity/entity_renderer_impl.mm @@ -11,6 +11,9 @@ if (!context_ || !context_->IsValid()) { return; } + + solid_fill_pipeline_ = std::make_unique(*context); + is_valid_ = true; } From 9d53bf9dcf81edab04e053a9cc26dafd14c9bb82 Mon Sep 17 00:00:00 2001 From: Chinmay Garde Date: Wed, 18 Aug 2021 01:02:21 -0700 Subject: [PATCH 163/433] Setup render command for solid fill. --- impeller/aiks/picture_renderer.h | 2 +- impeller/aiks/picture_renderer.mm | 2 +- impeller/compositor/pipeline.h | 2 +- impeller/entity/entity.cc | 6 +++++- impeller/entity/entity.h | 2 ++ impeller/entity/entity_renderer.h | 2 +- impeller/entity/entity_renderer.mm | 2 +- impeller/entity/entity_renderer_impl.h | 2 +- impeller/entity/entity_renderer_impl.mm | 21 +++++++++++++++++++-- 9 files changed, 32 insertions(+), 9 deletions(-) diff --git a/impeller/aiks/picture_renderer.h b/impeller/aiks/picture_renderer.h index 83ffcc04bd8ef..3e3ab2939ca63 100644 --- a/impeller/aiks/picture_renderer.h +++ b/impeller/aiks/picture_renderer.h @@ -23,7 +23,7 @@ class PictureRenderer { bool IsValid() const; [[nodiscard]] bool Render(const Surface& surface, - const RenderPass& onscreen_pass, + RenderPass& onscreen_pass, const Picture& picture); private: diff --git a/impeller/aiks/picture_renderer.mm b/impeller/aiks/picture_renderer.mm index 826da4cd74c13..ad613a0ccd703 100644 --- a/impeller/aiks/picture_renderer.mm +++ b/impeller/aiks/picture_renderer.mm @@ -23,7 +23,7 @@ } bool PictureRenderer::Render(const Surface& surface, - const RenderPass& onscreen_pass, + RenderPass& onscreen_pass, const Picture& picture) { if (!IsValid()) { return false; diff --git a/impeller/compositor/pipeline.h b/impeller/compositor/pipeline.h index 59c4ddbd92fef..35f1351af7635 100644 --- a/impeller/compositor/pipeline.h +++ b/impeller/compositor/pipeline.h @@ -63,7 +63,7 @@ class PipelineT { context, Builder::MakeDefaultPipelineDescriptor(context))) {} - const Pipeline* WaitAndGet() { return pipeline_future_.get().get(); } + std::shared_ptr WaitAndGet() { return pipeline_future_.get(); } private: PipelineFuture pipeline_future_; diff --git a/impeller/entity/entity.cc b/impeller/entity/entity.cc index f6adb865111f8..51acf97e5a093 100644 --- a/impeller/entity/entity.cc +++ b/impeller/entity/entity.cc @@ -62,6 +62,10 @@ bool Entity::HasStroke() const { return stroke_size_ > 0.0 && !stroke_color_.IsTransparent(); } +bool Entity::HasContents() const { + return !background_color_.IsTransparent(); +} + bool Entity::HasRenderableContents() const { const bool has_empty_path = path_.GetBoundingBox().IsZero(); @@ -77,7 +81,7 @@ bool Entity::HasRenderableContents() const { return true; } - if (!background_color_.IsTransparent()) { + if (HasContents()) { return true; } diff --git a/impeller/entity/entity.h b/impeller/entity/entity.h index 5b3fedee7617b..43dd3f3fdc407 100644 --- a/impeller/entity/entity.h +++ b/impeller/entity/entity.h @@ -44,6 +44,8 @@ class Entity { bool HasStroke() const; + bool HasContents() const; + bool HasRenderableContents() const; private: diff --git a/impeller/entity/entity_renderer.h b/impeller/entity/entity_renderer.h index 4da3477011a52..ed6629b5ec19d 100644 --- a/impeller/entity/entity_renderer.h +++ b/impeller/entity/entity_renderer.h @@ -25,7 +25,7 @@ class EntityRenderer { bool IsValid() const; [[nodiscard]] bool RenderEntities(const Surface& surface, - const RenderPass& onscreen_pass, + RenderPass& onscreen_pass, const std::vector& entities) const; private: diff --git a/impeller/entity/entity_renderer.mm b/impeller/entity/entity_renderer.mm index c2a5cc7c2bfea..87df107395afc 100644 --- a/impeller/entity/entity_renderer.mm +++ b/impeller/entity/entity_renderer.mm @@ -24,7 +24,7 @@ } bool EntityRenderer::RenderEntities(const Surface& surface, - const RenderPass& onscreen_pass, + RenderPass& onscreen_pass, const std::vector& entities) const { if (!IsValid()) { return false; diff --git a/impeller/entity/entity_renderer_impl.h b/impeller/entity/entity_renderer_impl.h index 1f2ffa778189b..c63d063e0210d 100644 --- a/impeller/entity/entity_renderer_impl.h +++ b/impeller/entity/entity_renderer_impl.h @@ -30,7 +30,7 @@ class EntityRendererImpl { bool IsValid() const; [[nodiscard]] bool RenderEntity(const Surface& surface, - const RenderPass& onscreen_pass, + RenderPass& onscreen_pass, const Entity& entities); private: diff --git a/impeller/entity/entity_renderer_impl.mm b/impeller/entity/entity_renderer_impl.mm index e148d4024ddc9..bbd4cd42cbcf9 100644 --- a/impeller/entity/entity_renderer_impl.mm +++ b/impeller/entity/entity_renderer_impl.mm @@ -12,7 +12,7 @@ return; } - solid_fill_pipeline_ = std::make_unique(*context); + solid_fill_pipeline_ = std::make_unique(*context_); is_valid_ = true; } @@ -24,12 +24,29 @@ } bool EntityRendererImpl::RenderEntity(const Surface& surface, - const RenderPass& onscreen_pass, + RenderPass& pass, const Entity& entity) { if (!entity.HasRenderableContents()) { return true; } + if (entity.HasContents()) { + using CurrentPipeline = decltype(solid_fill_pipeline_)::element_type; + using VS = CurrentPipeline::VertexShader; + + Command cmd; + cmd.pipeline = solid_fill_pipeline_->WaitAndGet(); + if (cmd.pipeline == nullptr) { + return false; + } + + VS::FrameInfo frame_info; + frame_info.mvp = Matrix::MakeOrthographic(surface.GetSize()) * + entity.GetTransformation(); + VS::BindFrameInfo(cmd, + pass.GetTransientsBuffer().EmplaceUniform(frame_info)); + } + return true; } From 3f388b23d0415aef2aa6e3cef67b15ba3473d1ee Mon Sep 17 00:00:00 2001 From: Chinmay Garde Date: Wed, 18 Aug 2021 16:35:40 -0700 Subject: [PATCH 164/433] Make sure only valid pipeline futures are awaited upon. --- impeller/aiks/paint.h | 1 + impeller/compositor/pipeline.h | 13 ++++++++++++- impeller/entity/entity_renderer_impl.mm | 3 +-- 3 files changed, 14 insertions(+), 3 deletions(-) diff --git a/impeller/aiks/paint.h b/impeller/aiks/paint.h index 805f719e3ad66..6b90aa1c433b7 100644 --- a/impeller/aiks/paint.h +++ b/impeller/aiks/paint.h @@ -12,6 +12,7 @@ namespace impeller { struct Paint { Color color; + Scalar stroke_width = 0.0; }; } // namespace impeller diff --git a/impeller/compositor/pipeline.h b/impeller/compositor/pipeline.h index 35f1351af7635..e0f1ab5b0df3f 100644 --- a/impeller/compositor/pipeline.h +++ b/impeller/compositor/pipeline.h @@ -63,10 +63,21 @@ class PipelineT { context, Builder::MakeDefaultPipelineDescriptor(context))) {} - std::shared_ptr WaitAndGet() { return pipeline_future_.get(); } + std::shared_ptr WaitAndGet() { + if (did_wait_) { + return pipeline_; + } + did_wait_ = true; + if (pipeline_future_.valid()) { + pipeline_ = pipeline_future_.get(); + } + return pipeline_; + } private: PipelineFuture pipeline_future_; + std::shared_ptr pipeline_; + bool did_wait_ = false; FML_DISALLOW_COPY_AND_ASSIGN(PipelineT); }; diff --git a/impeller/entity/entity_renderer_impl.mm b/impeller/entity/entity_renderer_impl.mm index bbd4cd42cbcf9..68e9af231f9e2 100644 --- a/impeller/entity/entity_renderer_impl.mm +++ b/impeller/entity/entity_renderer_impl.mm @@ -31,8 +31,7 @@ } if (entity.HasContents()) { - using CurrentPipeline = decltype(solid_fill_pipeline_)::element_type; - using VS = CurrentPipeline::VertexShader; + using VS = SolidFillPipeline::VertexShader; Command cmd; cmd.pipeline = solid_fill_pipeline_->WaitAndGet(); From 1a551b5759d413dcd96fad600612ca3f56d2f53a Mon Sep 17 00:00:00 2001 From: Chinmay Garde Date: Wed, 18 Aug 2021 16:48:50 -0700 Subject: [PATCH 165/433] Cleanup vertex buffer builder API. --- impeller/compositor/vertex_buffer_builder.h | 12 ++++++++++-- impeller/entity/entity_renderer_impl.mm | 3 +++ 2 files changed, 13 insertions(+), 2 deletions(-) diff --git a/impeller/compositor/vertex_buffer_builder.h b/impeller/compositor/vertex_buffer_builder.h index 12b093ca2e896..db2dffdf850fa 100644 --- a/impeller/compositor/vertex_buffer_builder.h +++ b/impeller/compositor/vertex_buffer_builder.h @@ -31,10 +31,18 @@ class VertexBufferBuilder { void SetLabel(std::string label) { label_ = std::move(label); } + void Reserve(size_t count) { return vertices_.reserve(count); } + + VertexBufferBuilder& AppendVertex(VertexType_ vertex) { + vertices_.emplace_back(std::move(vertex)); + return *this; + } + VertexBufferBuilder& AddVertices( std::initializer_list vertices) { - for (const auto& vertex : vertices) { - vertices_.push_back(vertex); + vertices_.reserve(vertices.size()); + for (auto& vertex : vertices) { + vertices_.emplace_back(std::move(vertex)); } return *this; } diff --git a/impeller/entity/entity_renderer_impl.mm b/impeller/entity/entity_renderer_impl.mm index 68e9af231f9e2..4d763a7d122cb 100644 --- a/impeller/entity/entity_renderer_impl.mm +++ b/impeller/entity/entity_renderer_impl.mm @@ -3,6 +3,7 @@ // found in the LICENSE file. #include "impeller/entity/entity_renderer_impl.h" +#include "impeller/compositor/vertex_buffer_builder.h" namespace impeller { @@ -39,6 +40,8 @@ return false; } + VertexBufferBuilder builder; + VS::FrameInfo frame_info; frame_info.mvp = Matrix::MakeOrthographic(surface.GetSize()) * entity.GetTransformation(); From 980a9fa6829a50ef0d90df435403780a19094bf3 Mon Sep 17 00:00:00 2001 From: Chinmay Garde Date: Wed, 18 Aug 2021 18:45:15 -0700 Subject: [PATCH 166/433] Add a tessellator. --- impeller/compositor/BUILD.gn | 3 + impeller/compositor/formats.h | 5 + impeller/compositor/tessellator.cc | 114 ++++++++++++++++++++ impeller/compositor/tessellator.h | 43 ++++++++ impeller/geometry/path_component.h | 8 +- impeller/playground/playground.mm | 3 +- impeller/primitives/primitives_unittests.mm | 72 +++++++++++++ 7 files changed, 243 insertions(+), 5 deletions(-) create mode 100644 impeller/compositor/tessellator.cc create mode 100644 impeller/compositor/tessellator.h diff --git a/impeller/compositor/BUILD.gn b/impeller/compositor/BUILD.gn index 891141ee0a725..62ee7c9040d9f 100644 --- a/impeller/compositor/BUILD.gn +++ b/impeller/compositor/BUILD.gn @@ -58,6 +58,8 @@ impeller_component("compositor") { "shader_types.h", "surface.h", "surface.mm", + "tessellator.cc", + "tessellator.h", "texture.h", "texture.mm", "texture_descriptor.h", @@ -91,5 +93,6 @@ source_set("compositor_unittests") { ":compositor", "../playground", "//flutter/testing:testing_lib", + "//third_party/libtess2", ] } diff --git a/impeller/compositor/formats.h b/impeller/compositor/formats.h index 24a137ee3effb..2f868733b4c1f 100644 --- a/impeller/compositor/formats.h +++ b/impeller/compositor/formats.h @@ -100,6 +100,11 @@ enum class TextureUsage : TextureUsageMask { kRenderTarget, }; +enum class WindingOrder { + kClockwise, + kCounterClockwise, +}; + enum class PrimitiveType { kTriangle, kTriangleStrip, diff --git a/impeller/compositor/tessellator.cc b/impeller/compositor/tessellator.cc new file mode 100644 index 0000000000000..386f72e7c3a6f --- /dev/null +++ b/impeller/compositor/tessellator.cc @@ -0,0 +1,114 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "impeller/compositor/tessellator.h" + +#include "third_party/libtess2/Include/tesselator.h" + +namespace impeller { + +Tessellator::Tessellator() {} + +Tessellator::~Tessellator() {} + +void Tessellator::SetFillType(FillType winding) { + fill_type_ = winding; +} + +Tessellator::FillType Tessellator::GetFillType() const { + return fill_type_; +} + +static int ToTessWindingRule(Tessellator::FillType fill_type) { + switch (fill_type) { + case Tessellator::FillType::kOdd: + return TESS_WINDING_ODD; + case Tessellator::FillType::kNonZero: + return TESS_WINDING_NONZERO; + case Tessellator::FillType::kPositive: + return TESS_WINDING_POSITIVE; + case Tessellator::FillType::kNegative: + return TESS_WINDING_NEGATIVE; + case Tessellator::FillType::kAbsGeqTwo: + return TESS_WINDING_ABS_GEQ_TWO; + } + return TESS_WINDING_ODD; +} + +static void DestroyTessellator(TESStesselator* tessellator) { + if (tessellator != nullptr) { + ::tessDeleteTess(tessellator); + } +} + +bool Tessellator::Tessellate(const std::vector& contours, + VertexCallback callback) const { + if (!callback) { + return false; + } + + using CTessellator = + std::unique_ptr; + + CTessellator tessellator( + ::tessNewTess(nullptr /* the default ::malloc based allocator */), + DestroyTessellator); + + if (!tessellator) { + return false; + } + + constexpr int kVertexSize = 2; + constexpr int kPolygonSize = 3; + + //---------------------------------------------------------------------------- + /// Feed contour information to the tessellator. + /// + static_assert(sizeof(Point) == 2 * sizeof(float)); + ::tessAddContour(tessellator.get(), // the C tessellator + kVertexSize, // + contours.data(), // + sizeof(Point), // + contours.size() // + ); + + //---------------------------------------------------------------------------- + /// Let's tessellate. + /// + auto result = ::tessTesselate(tessellator.get(), // tessellator + ToTessWindingRule(fill_type_), // winding + TESS_POLYGONS, // element type + kPolygonSize, // polygon size + kVertexSize, // vertex size + nullptr // normal (null is automatic) + ); + + if (result != 1) { + return false; + } + + std::vector points; + std::vector indices; + + int vertexItemCount = tessGetVertexCount(tessellator.get()) * kVertexSize; + auto vertices = tessGetVertices(tessellator.get()); + for (int i = 0; i < vertexItemCount; i += 2) { + points.emplace_back(vertices[i], vertices[i + 1]); + } + + int elementItemCount = tessGetElementCount(tessellator.get()) * kPolygonSize; + auto elements = tessGetElements(tessellator.get()); + for (int i = 0; i < elementItemCount; i++) { + indices.emplace_back(elements[i]); + } + + for (auto index : indices) { + auto vtx = points[index]; + callback(vtx); + } + + return true; +} + +} // namespace impeller diff --git a/impeller/compositor/tessellator.h b/impeller/compositor/tessellator.h new file mode 100644 index 0000000000000..24f05c3878963 --- /dev/null +++ b/impeller/compositor/tessellator.h @@ -0,0 +1,43 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#pragma once + +#include +#include + +#include "flutter/fml/macros.h" +#include "impeller/geometry/point.h" + +namespace impeller { + +class Tessellator { + public: + enum class FillType { + kOdd, // The default winding order. + kNonZero, + kPositive, + kNegative, + kAbsGeqTwo, + }; + + Tessellator(); + + ~Tessellator(); + + void SetFillType(FillType winding); + + FillType GetFillType() const; + + using VertexCallback = std::function; + [[nodiscard]] bool Tessellate(const std::vector& vertices, + VertexCallback callback) const; + + private: + FillType fill_type_ = FillType::kOdd; + + FML_DISALLOW_COPY_AND_ASSIGN(Tessellator); +}; + +} // namespace impeller diff --git a/impeller/geometry/path_component.h b/impeller/geometry/path_component.h index effc7d9822a02..cda5258eaa192 100644 --- a/impeller/geometry/path_component.h +++ b/impeller/geometry/path_component.h @@ -12,10 +12,10 @@ namespace impeller { struct SmoothingApproximation { - const Scalar scale; - const Scalar angle_tolerance; - const Scalar cusp_limit; - const Scalar distance_tolerance_square; + Scalar scale; + Scalar angle_tolerance; + Scalar cusp_limit; + Scalar distance_tolerance_square; SmoothingApproximation(/* default */) : SmoothingApproximation(1.0 /* scale */, diff --git a/impeller/playground/playground.mm b/impeller/playground/playground.mm index bef2d41a49107..3373594c05945 100644 --- a/impeller/playground/playground.mm +++ b/impeller/playground/playground.mm @@ -33,7 +33,8 @@ return fml::paths::JoinPaths({path_result.second, "shaders"}); } -Playground::Playground() : renderer_(ShaderLibraryDirectory()) {} +Playground::Playground() + : renderer_(ShaderLibraryDirectory(), "impeller.metallib") {} Playground::~Playground() = default; diff --git a/impeller/primitives/primitives_unittests.mm b/impeller/primitives/primitives_unittests.mm index a8e55faf0c1f3..e9690fbfae1a4 100644 --- a/impeller/primitives/primitives_unittests.mm +++ b/impeller/primitives/primitives_unittests.mm @@ -7,10 +7,13 @@ #include "impeller/compositor/command.h" #include "impeller/compositor/command_buffer.h" #include "impeller/compositor/pipeline_builder.h" +#include "impeller/compositor/pipeline_library.h" #include "impeller/compositor/renderer.h" #include "impeller/compositor/sampler_descriptor.h" #include "impeller/compositor/surface.h" +#include "impeller/compositor/tessellator.h" #include "impeller/compositor/vertex_buffer_builder.h" +#include "impeller/geometry/path_builder.h" #include "impeller/image/compressed_image.h" #include "impeller/image/decompressed_image.h" #include "impeller/playground/playground.h" @@ -248,5 +251,74 @@ ASSERT_TRUE(r2t_pass->Commit(*context->GetTransientsAllocator())); } +TEST_F(PrimitivesTest, CanRenderPath) { + auto path = PathBuilder{}.AddRect({10, 10, 100, 100}).CreatePath(); + ASSERT_FALSE(path.GetBoundingBox().IsZero()); + + using BoxPipeline = PipelineT; + using VS = BoxFadeVertexShader; + using FS = BoxFadeFragmentShader; + + BoxPipeline box_pipeline(*GetContext()); + + // Vertex buffer. + VertexBufferBuilder vertex_builder; + vertex_builder.SetLabel("Box"); + + Tessellator tessellator; + ASSERT_TRUE(tessellator.Tessellate( + path.SubdivideAdaptively({}), [&vertex_builder](Point point) { + VS::PerVertexData vtx; + vtx.vertex_position = {point.x, point.y, 0.0}; + vtx.texture_coordinates = {0.5, 0.5}; + vertex_builder.AppendVertex(vtx); + })); + + auto context = GetContext(); + + auto vertex_buffer = + vertex_builder.CreateVertexBuffer(*context->GetPermanentsAllocator()); + ASSERT_TRUE(vertex_buffer); + + auto bridge = CreateTextureForFixture("bay_bridge.jpg"); + auto boston = CreateTextureForFixture("boston.jpg"); + ASSERT_TRUE(bridge && boston); + auto sampler = context->GetSamplerLibrary()->GetSampler({}); + ASSERT_TRUE(sampler); + + Renderer::RenderCallback callback = [&](const Surface& surface, + RenderPass& pass) { + Command cmd; + cmd.label = "Box"; + cmd.pipeline = box_pipeline.WaitAndGet(); + + cmd.BindVertices(vertex_buffer); + + FS::FrameInfo frame_info; + frame_info.current_time = fml::TimePoint::Now().ToEpochDelta().ToSecondsF(); + frame_info.cursor_position = GetCursorPosition(); + frame_info.window_size.x = GetWindowSize().width; + frame_info.window_size.y = GetWindowSize().height; + + FS::BindFrameInfo(cmd, + pass.GetTransientsBuffer().EmplaceUniform(frame_info)); + FS::BindContents1(cmd, boston, sampler); + FS::BindContents2(cmd, bridge, sampler); + + cmd.primitive_type = PrimitiveType::kTriangle; + + VS::UniformBuffer uniforms; + uniforms.mvp = Matrix::MakeOrthographic(surface.GetSize()); + VS::BindUniformBuffer(cmd, + pass.GetTransientsBuffer().EmplaceUniform(uniforms)); + if (!pass.RecordCommand(cmd)) { + return false; + } + + return true; + }; + OpenPlaygroundHere(callback); +} + } // namespace testing } // namespace impeller From 67da487f5be7e394ba3fc4cb07776d91a910c8cf Mon Sep 17 00:00:00 2001 From: Chinmay Garde Date: Wed, 18 Aug 2021 19:26:59 -0700 Subject: [PATCH 167/433] Fix circle path components. --- impeller/compositor/command.h | 1 + impeller/compositor/render_pass.mm | 4 +- impeller/compositor/tessellator.cc | 5 ++ impeller/compositor/tessellator.h | 3 + impeller/geometry/path_builder.cc | 71 ++++++++++++--------- impeller/primitives/primitives_unittests.mm | 3 +- 6 files changed, 54 insertions(+), 33 deletions(-) diff --git a/impeller/compositor/command.h b/impeller/compositor/command.h index 6f8dcd640f71f..e03fd955f4e54 100644 --- a/impeller/compositor/command.h +++ b/impeller/compositor/command.h @@ -34,6 +34,7 @@ struct Command { size_t index_count = 0u; std::string label; PrimitiveType primitive_type = PrimitiveType::kTriangle; + WindingOrder winding = WindingOrder::kClockwise; bool BindVertices(const VertexBuffer& buffer); diff --git a/impeller/compositor/render_pass.mm b/impeller/compositor/render_pass.mm index 7d6b1fae02982..458f5277688a0 100644 --- a/impeller/compositor/render_pass.mm +++ b/impeller/compositor/render_pass.mm @@ -357,7 +357,9 @@ static bool Bind(PassBindingsCache& pass, command.pipeline->GetMTLRenderPipelineState()); pass_bindings.SetDepthStencilState( command.pipeline->GetMTLDepthStencilState()); - [pass setFrontFacingWinding:MTLWindingClockwise]; + [pass setFrontFacingWinding:command.winding == WindingOrder::kClockwise + ? MTLWindingClockwise + : MTLWindingCounterClockwise]; [pass setCullMode:MTLCullModeBack]; if (!bind_stage_resources(command.vertex_bindings, ShaderStage::kVertex)) { return false; diff --git a/impeller/compositor/tessellator.cc b/impeller/compositor/tessellator.cc index 386f72e7c3a6f..66bb326f1ade4 100644 --- a/impeller/compositor/tessellator.cc +++ b/impeller/compositor/tessellator.cc @@ -88,6 +88,7 @@ bool Tessellator::Tessellate(const std::vector& contours, return false; } + // TODO(csg): This copy can be elided entirely for the current use case. std::vector points; std::vector indices; @@ -111,4 +112,8 @@ bool Tessellator::Tessellate(const std::vector& contours, return true; } +WindingOrder Tessellator::GetFrontFaceWinding() const { + return WindingOrder::kClockwise; +} + } // namespace impeller diff --git a/impeller/compositor/tessellator.h b/impeller/compositor/tessellator.h index 24f05c3878963..71eceb40ecb37 100644 --- a/impeller/compositor/tessellator.h +++ b/impeller/compositor/tessellator.h @@ -8,6 +8,7 @@ #include #include "flutter/fml/macros.h" +#include "impeller/compositor/formats.h" #include "impeller/geometry/point.h" namespace impeller { @@ -30,6 +31,8 @@ class Tessellator { FillType GetFillType() const; + WindingOrder GetFrontFaceWinding() const; + using VertexCallback = std::function; [[nodiscard]] bool Tessellate(const std::vector& vertices, VertexCallback callback) const; diff --git a/impeller/geometry/path_builder.cc b/impeller/geometry/path_builder.cc index b6d419bede8c6..52da21e3a8c9c 100644 --- a/impeller/geometry/path_builder.cc +++ b/impeller/geometry/path_builder.cc @@ -145,50 +145,59 @@ PathBuilder& PathBuilder::SmoothCubicCurveTo(Point point, PathBuilder& PathBuilder::AddRect(Rect rect) { current_ = rect.origin; - auto topLeft = rect.origin; - auto bottomLeft = rect.origin + Point{0.0, rect.size.height}; - auto bottomRight = rect.origin + Point{rect.size.width, rect.size.height}; - auto topRight = rect.origin + Point{rect.size.width, 0.0}; + auto tl = rect.origin; + auto bl = rect.origin + Point{0.0, rect.size.height}; + auto br = rect.origin + Point{rect.size.width, rect.size.height}; + auto tr = rect.origin + Point{rect.size.width, 0.0}; - prototype_.AddLinearComponent(topLeft, bottomLeft) - .AddLinearComponent(bottomLeft, bottomRight) - .AddLinearComponent(bottomRight, topRight) - .AddLinearComponent(topRight, topLeft); + prototype_.AddLinearComponent(tl, tr) + .AddLinearComponent(tr, br) + .AddLinearComponent(br, bl) + .AddLinearComponent(bl, tl); return *this; } -PathBuilder& PathBuilder::AddCircle(const Point& center, Scalar radius) { - current_ = center + Point{0.0, radius}; +PathBuilder& PathBuilder::AddCircle(const Point& c, Scalar r) { + current_ = c + Point{0.0, r}; - const Scalar diameter = radius * 2.0; - const Scalar magic = kArcApproximationMagic * radius; + // m for magic + const auto m = kArcApproximationMagic * r; - prototype_.AddCubicComponent( - {center.x + radius, center.y}, // - {center.x + radius + magic, center.y}, // - {center.x + diameter, center.y + radius - magic}, // - {center.x + diameter, center.y + radius} // + //---------------------------------------------------------------------------- + /// Top right arc. + /// + prototype_.AddCubicComponent({c.x, c.y - r}, // p1 + {c.x + m, c.y - r}, // cp1 + {c.x + r, c.y - m}, // cp2 + {c.x + r, c.y} // p2 ); - prototype_.AddCubicComponent( - {center.x + diameter, center.y + radius}, // - {center.x + diameter, center.y + radius + magic}, // - {center.x + radius + magic, center.y + diameter}, // - {center.x + radius, center.y + diameter} // + //---------------------------------------------------------------------------- + /// Bottom right arc. + /// + prototype_.AddCubicComponent({c.x + r, c.y}, // p1 + {c.x + r, c.y + m}, // cp1 + {c.x + m, c.y + r}, // cp2 + {c.x, c.y + r} // p2 ); - prototype_.AddCubicComponent( - {center.x + radius, center.y + diameter}, // - {center.x + radius - magic, center.y + diameter}, // - {center.x, center.y + radius + magic}, // - {center.x, center.y + radius} // + //---------------------------------------------------------------------------- + /// Bottom left arc. + /// + prototype_.AddCubicComponent({c.x, c.y + r}, // p1 + {c.x - m, c.y + r}, // cp1 + {c.x - r, c.y + m}, // cp2 + {c.x - r, c.y} // p2 ); - prototype_.AddCubicComponent({center.x, center.y + radius}, // - {center.x, center.y + radius - magic}, // - {center.x + radius - magic, center.y}, // - {center.x + radius, center.y} // + //---------------------------------------------------------------------------- + /// Top left arc. + /// + prototype_.AddCubicComponent({c.x - r, c.y}, // p1 + {c.x - r, c.y - m}, // cp1 + {c.x - m, c.y - r}, // cp2 + {c.x, c.y - r} // p2 ); return *this; diff --git a/impeller/primitives/primitives_unittests.mm b/impeller/primitives/primitives_unittests.mm index e9690fbfae1a4..d8dfee10c2cf0 100644 --- a/impeller/primitives/primitives_unittests.mm +++ b/impeller/primitives/primitives_unittests.mm @@ -252,7 +252,7 @@ } TEST_F(PrimitivesTest, CanRenderPath) { - auto path = PathBuilder{}.AddRect({10, 10, 100, 100}).CreatePath(); + auto path = PathBuilder{}.AddCircle({550, 550}, 500).CreatePath(); ASSERT_FALSE(path.GetBoundingBox().IsZero()); using BoxPipeline = PipelineT; @@ -306,6 +306,7 @@ FS::BindContents2(cmd, bridge, sampler); cmd.primitive_type = PrimitiveType::kTriangle; + cmd.winding = tessellator.GetFrontFaceWinding(); VS::UniformBuffer uniforms; uniforms.mvp = Matrix::MakeOrthographic(surface.GetSize()); From 5d2cadb1ef11cb1c1a20504337dc241bad147f03 Mon Sep 17 00:00:00 2001 From: Chinmay Garde Date: Wed, 18 Aug 2021 19:31:47 -0700 Subject: [PATCH 168/433] Cleanup path builder rounded rect implementation. --- impeller/geometry/path_builder.cc | 113 +++++++------------- impeller/geometry/path_builder.h | 2 - impeller/primitives/primitives_unittests.mm | 2 +- 3 files changed, 39 insertions(+), 78 deletions(-) diff --git a/impeller/geometry/path_builder.cc b/impeller/geometry/path_builder.cc index 52da21e3a8c9c..c24c4b83de043 100644 --- a/impeller/geometry/path_builder.cc +++ b/impeller/geometry/path_builder.cc @@ -211,124 +211,87 @@ PathBuilder& PathBuilder::AddRoundedRect(Rect rect, Scalar radius) { PathBuilder& PathBuilder::AddRoundedRect(Rect rect, RoundingRadii radii) { current_ = rect.origin + Point{radii.topLeft, 0.0}; - const Scalar magicTopRight = kArcApproximationMagic * radii.topRight; - const Scalar magicBottomRight = kArcApproximationMagic * radii.bottomRight; - const Scalar magicBottomLeft = kArcApproximationMagic * radii.bottomLeft; - const Scalar magicTopLeft = kArcApproximationMagic * radii.topLeft; + const Scalar magic_top_right = kArcApproximationMagic * radii.topRight; + const Scalar magic_bottom_right = kArcApproximationMagic * radii.bottomRight; + const Scalar magic_bottom_left = kArcApproximationMagic * radii.bottomLeft; + const Scalar magic_top_left = kArcApproximationMagic * radii.topLeft; - /* - * Top line. - */ + //---------------------------------------------------------------------------- + /// Top line. + /// prototype_.AddLinearComponent( {rect.origin.x + radii.topLeft, rect.origin.y}, {rect.origin.x + rect.size.width - radii.topRight, rect.origin.y}); - /* - * Top right arc. - */ + //---------------------------------------------------------------------------- + /// Top right arc. + /// prototype_.AddCubicComponent( {rect.origin.x + rect.size.width - radii.topRight, rect.origin.y}, - {rect.origin.x + rect.size.width - radii.topRight + magicTopRight, + {rect.origin.x + rect.size.width - radii.topRight + magic_top_right, rect.origin.y}, {rect.origin.x + rect.size.width, - rect.origin.y + radii.topRight - magicTopRight}, + rect.origin.y + radii.topRight - magic_top_right}, {rect.origin.x + rect.size.width, rect.origin.y + radii.topRight}); - /* - * Right line. - */ + //---------------------------------------------------------------------------- + /// Right line. + /// prototype_.AddLinearComponent( {rect.origin.x + rect.size.width, rect.origin.y + radii.topRight}, {rect.origin.x + rect.size.width, rect.origin.y + rect.size.height - radii.bottomRight}); - /* - * Bottom right arc. - */ + //---------------------------------------------------------------------------- + /// Bottom right arc. + /// prototype_.AddCubicComponent( {rect.origin.x + rect.size.width, rect.origin.y + rect.size.height - radii.bottomRight}, - {rect.origin.x + rect.size.width, - rect.origin.y + rect.size.height - radii.bottomRight + magicBottomRight}, - {rect.origin.x + rect.size.width - radii.bottomRight + magicBottomRight, + {rect.origin.x + rect.size.width, rect.origin.y + rect.size.height - + radii.bottomRight + + magic_bottom_right}, + {rect.origin.x + rect.size.width - radii.bottomRight + magic_bottom_right, rect.origin.y + rect.size.height}, {rect.origin.x + rect.size.width - radii.bottomRight, rect.origin.y + rect.size.height}); - /* - * Bottom line. - */ + //---------------------------------------------------------------------------- + /// Bottom line. + /// prototype_.AddLinearComponent( {rect.origin.x + rect.size.width - radii.bottomRight, rect.origin.y + rect.size.height}, {rect.origin.x + radii.bottomLeft, rect.origin.y + rect.size.height}); - /* - * Bottom left arc. - */ + //---------------------------------------------------------------------------- + /// Bottom left arc. + /// prototype_.AddCubicComponent( {rect.origin.x + radii.bottomLeft, rect.origin.y + rect.size.height}, - {rect.origin.x + radii.bottomLeft - magicBottomLeft, + {rect.origin.x + radii.bottomLeft - magic_bottom_left, rect.origin.y + rect.size.height}, {rect.origin.x, - rect.origin.y + rect.size.height - radii.bottomLeft + magicBottomLeft}, + rect.origin.y + rect.size.height - radii.bottomLeft + magic_bottom_left}, {rect.origin.x, rect.origin.y + rect.size.height - radii.bottomLeft}); - /* - * Left line. - */ + //---------------------------------------------------------------------------- + /// Left line. + /// prototype_.AddLinearComponent( {rect.origin.x, rect.origin.y + rect.size.height - radii.bottomLeft}, {rect.origin.x, rect.origin.y + radii.topLeft}); - /* - * Top left arc. - */ + //---------------------------------------------------------------------------- + /// Top left arc. + /// prototype_.AddCubicComponent( {rect.origin.x, rect.origin.y + radii.topLeft}, - {rect.origin.x, rect.origin.y + radii.topLeft - magicTopLeft}, - {rect.origin.x + radii.topLeft - magicTopLeft, rect.origin.y}, + {rect.origin.x, rect.origin.y + radii.topLeft - magic_top_left}, + {rect.origin.x + radii.topLeft - magic_top_left, rect.origin.y}, {rect.origin.x + radii.topLeft, rect.origin.y}); return *this; } -PathBuilder& PathBuilder::AddEllipse(const Point& center, const Size& radius) { - current_ = center + Point{0.0, radius.height}; - - const Size diameter = {radius.width * 2.0f, radius.height * 2.0f}; - const Size magic = {kArcApproximationMagic * radius.width, - kArcApproximationMagic * radius.height}; - - prototype_.AddCubicComponent( - {center.x + radius.width, center.y}, // - {center.x + radius.width + magic.width, center.y}, // - {center.x + diameter.width, center.y + radius.height - magic.height}, // - {center.x + diameter.width, center.y + radius.height} // - ); - - prototype_.AddCubicComponent( - {center.x + diameter.width, center.y + radius.height}, // - {center.x + diameter.width, center.y + radius.height + magic.height}, // - {center.x + radius.width + magic.width, center.y + diameter.height}, // - {center.x + radius.width, center.y + diameter.height} // - ); - - prototype_.AddCubicComponent( - {center.x + radius.width, center.y + diameter.height}, // - {center.x + radius.width - magic.width, center.y + diameter.height}, // - {center.x, center.y + radius.height + magic.height}, // - {center.x, center.y + radius.height} // - ); - - prototype_.AddCubicComponent( - {center.x, center.y + radius.height}, // - {center.x, center.y + radius.height - magic.height}, // - {center.x + radius.width - magic.width, center.y}, // - {center.x + radius.width, center.y} // - ); - - return *this; -} - } // namespace impeller diff --git a/impeller/geometry/path_builder.h b/impeller/geometry/path_builder.h index 786f4c26ab035..a058e9250d042 100644 --- a/impeller/geometry/path_builder.h +++ b/impeller/geometry/path_builder.h @@ -50,8 +50,6 @@ class PathBuilder { PathBuilder& AddCircle(const Point& center, Scalar radius); - PathBuilder& AddEllipse(const Point& center, const Size& size); - struct RoundingRadii { Scalar topLeft = 0.0; Scalar bottomLeft = 0.0; diff --git a/impeller/primitives/primitives_unittests.mm b/impeller/primitives/primitives_unittests.mm index d8dfee10c2cf0..2e4b359296d2f 100644 --- a/impeller/primitives/primitives_unittests.mm +++ b/impeller/primitives/primitives_unittests.mm @@ -252,7 +252,7 @@ } TEST_F(PrimitivesTest, CanRenderPath) { - auto path = PathBuilder{}.AddCircle({550, 550}, 500).CreatePath(); + auto path = PathBuilder{}.AddRoundedRect({10, 10, 200, 200}, 50).CreatePath(); ASSERT_FALSE(path.GetBoundingBox().IsZero()); using BoxPipeline = PipelineT; From 5e6491cb2de32fd701c3eec3ca743fdd67245870 Mon Sep 17 00:00:00 2001 From: Chinmay Garde Date: Fri, 20 Aug 2021 04:05:59 -0700 Subject: [PATCH 169/433] Add more path builder ops. --- impeller/aiks/canvas.cc | 13 ++- impeller/compositor/BUILD.gn | 3 +- impeller/compositor/render_pass.mm | 3 +- impeller/entity/entity_renderer.mm | 22 ++++- impeller/entity/entity_renderer_impl.h | 12 ++- impeller/entity/entity_renderer_impl.mm | 46 +++++++++-- impeller/entity/path.vert | 4 + impeller/entity/solid_fill.frag | 4 +- impeller/geometry/path.h | 2 +- impeller/geometry/path_builder.cc | 92 +++++++++++---------- impeller/geometry/path_builder.h | 4 + impeller/primitives/primitives_unittests.mm | 2 +- 12 files changed, 144 insertions(+), 63 deletions(-) diff --git a/impeller/aiks/canvas.cc b/impeller/aiks/canvas.cc index 8da1754be2158..eef923e1a618a 100644 --- a/impeller/aiks/canvas.cc +++ b/impeller/aiks/canvas.cc @@ -54,7 +54,12 @@ size_t Canvas::GetSaveCount() const { return xformation_stack_.size(); } +void AssertionBreak() {} + void Canvas::DrawPath(Path path, Paint paint) { + if (path.GetBoundingBox().IsZero()) { + AssertionBreak(); + } Entity entity; entity.SetTransformation(GetCurrentTransformation()); entity.SetPath(std::move(path)); @@ -75,8 +80,12 @@ void Canvas::ClipPath(Path path) { void Canvas::DrawShadow(Path path, Color color, Scalar elevation) {} void Canvas::DrawPicture(const Picture& picture) { - std::copy(std::begin(picture.entities), std::end(picture.entities), - std::back_inserter(ops_)); + for (const auto& entity : picture.entities) { + auto new_entity = entity; + new_entity.SetTransformation(GetCurrentTransformation() * + new_entity.GetTransformation()); + ops_.emplace_back(std::move(new_entity)); + } } Picture Canvas::EndRecordingAsPicture() { diff --git a/impeller/compositor/BUILD.gn b/impeller/compositor/BUILD.gn index 62ee7c9040d9f..c29c5b976c34a 100644 --- a/impeller/compositor/BUILD.gn +++ b/impeller/compositor/BUILD.gn @@ -78,6 +78,8 @@ impeller_component("compositor") { "../image", ] + deps = [ "//third_party/libtess2" ] + frameworks = [ "Metal.framework" ] } @@ -93,6 +95,5 @@ source_set("compositor_unittests") { ":compositor", "../playground", "//flutter/testing:testing_lib", - "//third_party/libtess2", ] } diff --git a/impeller/compositor/render_pass.mm b/impeller/compositor/render_pass.mm index 458f5277688a0..bcbbb36fbc016 100644 --- a/impeller/compositor/render_pass.mm +++ b/impeller/compositor/render_pass.mm @@ -344,6 +344,7 @@ static bool Bind(PassBindingsCache& pass, fml::closure pop_debug_marker = [pass]() { [pass popDebugGroup]; }; for (const auto& command : commands_) { if (command.index_count == 0u) { + FML_DLOG(ERROR) << "Zero index count in render pass command."; continue; } @@ -360,7 +361,7 @@ static bool Bind(PassBindingsCache& pass, [pass setFrontFacingWinding:command.winding == WindingOrder::kClockwise ? MTLWindingClockwise : MTLWindingCounterClockwise]; - [pass setCullMode:MTLCullModeBack]; + [pass setCullMode:MTLCullModeNone]; if (!bind_stage_resources(command.vertex_bindings, ShaderStage::kVertex)) { return false; } diff --git a/impeller/entity/entity_renderer.mm b/impeller/entity/entity_renderer.mm index 87df107395afc..06b0a439cafbf 100644 --- a/impeller/entity/entity_renderer.mm +++ b/impeller/entity/entity_renderer.mm @@ -30,13 +30,29 @@ return false; } + size_t success = 0u; + size_t failure = 0u; + size_t skipped = 0u; + for (const auto& entity : entities) { - if (!renderer_->RenderEntity(surface, onscreen_pass, entity)) { - return false; + auto result = renderer_->RenderEntity(surface, onscreen_pass, entity); + switch (result) { + case EntityRendererImpl::RenderResult::kSuccess: + success++; + break; + case EntityRendererImpl::RenderResult::kFailure: + failure++; + break; + case EntityRendererImpl::RenderResult::kSkipped: + skipped++; + break; } } - return true; + FML_LOG(ERROR) << "Success " << success << " failure " << failure + << " skipped " << skipped << " total " << entities.size(); + + return failure == 0; } } // namespace impeller diff --git a/impeller/entity/entity_renderer_impl.h b/impeller/entity/entity_renderer_impl.h index c63d063e0210d..1bf525227d7e4 100644 --- a/impeller/entity/entity_renderer_impl.h +++ b/impeller/entity/entity_renderer_impl.h @@ -29,9 +29,15 @@ class EntityRendererImpl { bool IsValid() const; - [[nodiscard]] bool RenderEntity(const Surface& surface, - RenderPass& onscreen_pass, - const Entity& entities); + enum class RenderResult { + kSkipped, + kSuccess, + kFailure, + }; + + [[nodiscard]] RenderResult RenderEntity(const Surface& surface, + RenderPass& onscreen_pass, + const Entity& entities); private: using SolidFillPipeline = diff --git a/impeller/entity/entity_renderer_impl.mm b/impeller/entity/entity_renderer_impl.mm index 4d763a7d122cb..531fbfc15fc08 100644 --- a/impeller/entity/entity_renderer_impl.mm +++ b/impeller/entity/entity_renderer_impl.mm @@ -3,6 +3,9 @@ // found in the LICENSE file. #include "impeller/entity/entity_renderer_impl.h" + +#include "flutter/fml/trace_event.h" +#include "impeller/compositor/tessellator.h" #include "impeller/compositor/vertex_buffer_builder.h" namespace impeller { @@ -24,32 +27,59 @@ return is_valid_; } -bool EntityRendererImpl::RenderEntity(const Surface& surface, - RenderPass& pass, - const Entity& entity) { +EntityRendererImpl::RenderResult EntityRendererImpl::RenderEntity( + const Surface& surface, + RenderPass& pass, + const Entity& entity) { if (!entity.HasRenderableContents()) { - return true; + if (entity.GetPath().GetBoundingBox().IsZero()) { + FML_LOG(ERROR) << "Skipped because bounds box zero."; + } + return RenderResult::kSkipped; } - if (entity.HasContents()) { + if (entity.HasContents() && !entity.IsClip()) { using VS = SolidFillPipeline::VertexShader; Command cmd; + cmd.label = "SolidFill"; cmd.pipeline = solid_fill_pipeline_->WaitAndGet(); if (cmd.pipeline == nullptr) { - return false; + return RenderResult::kFailure; + } + + VertexBufferBuilder vtx_builder; + { + TRACE_EVENT0("flutter", "Tesselate"); + auto tesselation_result = Tessellator{}.Tessellate( + entity.GetPath().SubdivideAdaptively(), [&vtx_builder](auto point) { + VS::PerVertexData vtx; + vtx.vertices = point; + vtx_builder.AppendVertex(vtx); + }); + if (!tesselation_result) { + return RenderResult::kFailure; + } } - VertexBufferBuilder builder; + cmd.BindVertices( + vtx_builder.CreateVertexBuffer(*context_->GetPermanentsAllocator())); VS::FrameInfo frame_info; frame_info.mvp = Matrix::MakeOrthographic(surface.GetSize()) * entity.GetTransformation(); + frame_info.color = entity.GetBackgroundColor(); VS::BindFrameInfo(cmd, pass.GetTransientsBuffer().EmplaceUniform(frame_info)); + + cmd.primitive_type = PrimitiveType::kTriangle; + + if (!pass.RecordCommand(std::move(cmd))) { + return RenderResult::kFailure; + } } - return true; + return RenderResult::kSuccess; } } // namespace impeller diff --git a/impeller/entity/path.vert b/impeller/entity/path.vert index ba38d88acd176..9d29d7eeee634 100644 --- a/impeller/entity/path.vert +++ b/impeller/entity/path.vert @@ -4,10 +4,14 @@ uniform FrameInfo { mat4 mvp; + vec4 color; } frame_info; in vec2 vertices; +out vec4 color; + void main() { gl_Position = frame_info.mvp * vec4(vertices, 0.0, 1.0); + color = frame_info.color; } diff --git a/impeller/entity/solid_fill.frag b/impeller/entity/solid_fill.frag index ba7a8555c21da..0263c965f294b 100644 --- a/impeller/entity/solid_fill.frag +++ b/impeller/entity/solid_fill.frag @@ -2,8 +2,10 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. +in vec4 color; + out vec4 frag_color; void main() { - frag_color = vec4(1.0, 1.0, 1.0, 1.0); + frag_color = color; } diff --git a/impeller/geometry/path.h b/impeller/geometry/path.h index 18c7b4a602db9..4952ecab16ded 100644 --- a/impeller/geometry/path.h +++ b/impeller/geometry/path.h @@ -54,7 +54,7 @@ class Path { bool UpdateCubicComponentAtIndex(size_t index, CubicPathComponent& cubic); std::vector SubdivideAdaptively( - const SmoothingApproximation& approximation) const; + const SmoothingApproximation& approximation = {}) const; Rect GetBoundingBox() const; diff --git a/impeller/geometry/path_builder.cc b/impeller/geometry/path_builder.cc index c24c4b83de043..835e15ad349b0 100644 --- a/impeller/geometry/path_builder.cc +++ b/impeller/geometry/path_builder.cc @@ -159,48 +159,7 @@ PathBuilder& PathBuilder::AddRect(Rect rect) { } PathBuilder& PathBuilder::AddCircle(const Point& c, Scalar r) { - current_ = c + Point{0.0, r}; - - // m for magic - const auto m = kArcApproximationMagic * r; - - //---------------------------------------------------------------------------- - /// Top right arc. - /// - prototype_.AddCubicComponent({c.x, c.y - r}, // p1 - {c.x + m, c.y - r}, // cp1 - {c.x + r, c.y - m}, // cp2 - {c.x + r, c.y} // p2 - ); - - //---------------------------------------------------------------------------- - /// Bottom right arc. - /// - prototype_.AddCubicComponent({c.x + r, c.y}, // p1 - {c.x + r, c.y + m}, // cp1 - {c.x + m, c.y + r}, // cp2 - {c.x, c.y + r} // p2 - ); - - //---------------------------------------------------------------------------- - /// Bottom left arc. - /// - prototype_.AddCubicComponent({c.x, c.y + r}, // p1 - {c.x - m, c.y + r}, // cp1 - {c.x - r, c.y + m}, // cp2 - {c.x - r, c.y} // p2 - ); - - //---------------------------------------------------------------------------- - /// Top left arc. - /// - prototype_.AddCubicComponent({c.x - r, c.y}, // p1 - {c.x - r, c.y - m}, // cp1 - {c.x - m, c.y - r}, // cp2 - {c.x, c.y - r} // p2 - ); - - return *this; + return AddOval(Rect{c.x - r, c.y - r, 2.0f * r, 2.0f * r}); } PathBuilder& PathBuilder::AddRoundedRect(Rect rect, Scalar radius) { @@ -294,4 +253,53 @@ PathBuilder& PathBuilder::AddRoundedRect(Rect rect, RoundingRadii radii) { return *this; } +PathBuilder& PathBuilder::AddOval(const Rect& container) { + const Point r = {container.size.width * 0.5f, container.size.height * 0.5f}; + const Point c = {container.origin.x + (container.size.width * 0.5f), + container.origin.y + (container.size.height * 0.5f)}; + const Point m = {kArcApproximationMagic * r.x, kArcApproximationMagic * r.y}; + + //---------------------------------------------------------------------------- + /// Top right arc. + /// + prototype_.AddCubicComponent({c.x, c.y - r.y}, // p1 + {c.x + m.x, c.y - r.y}, // cp1 + {c.x + r.x, c.y - m.y}, // cp2 + {c.x + r.x, c.y} // p2 + ); + + //---------------------------------------------------------------------------- + /// Bottom right arc. + /// + prototype_.AddCubicComponent({c.x + r.x, c.y}, // p1 + {c.x + r.x, c.y + m.y}, // cp1 + {c.x + m.x, c.y + r.y}, // cp2 + {c.x, c.y + r.y} // p2 + ); + + //---------------------------------------------------------------------------- + /// Bottom left arc. + /// + prototype_.AddCubicComponent({c.x, c.y + r.y}, // p1 + {c.x - m.x, c.y + r.y}, // cp1 + {c.x - r.x, c.y + m.y}, // cp2 + {c.x - r.x, c.y} // p2 + ); + + //---------------------------------------------------------------------------- + /// Top left arc. + /// + prototype_.AddCubicComponent({c.x - r.x, c.y}, // p1 + {c.x - r.x, c.y - m.y}, // cp1 + {c.x - m.x, c.y - r.y}, // cp2 + {c.x, c.y - r.y} // p2 + ); + + return *this; +} + +const Path& PathBuilder::GetCurrentPath() const { + return prototype_; +} + } // namespace impeller diff --git a/impeller/geometry/path_builder.h b/impeller/geometry/path_builder.h index a058e9250d042..36e391fa81f01 100644 --- a/impeller/geometry/path_builder.h +++ b/impeller/geometry/path_builder.h @@ -19,6 +19,8 @@ class PathBuilder { Path CreatePath() const; + const Path& GetCurrentPath() const; + PathBuilder& MoveTo(Point point, bool relative = false); PathBuilder& Close(); @@ -50,6 +52,8 @@ class PathBuilder { PathBuilder& AddCircle(const Point& center, Scalar radius); + PathBuilder& AddOval(const Rect& rect); + struct RoundingRadii { Scalar topLeft = 0.0; Scalar bottomLeft = 0.0; diff --git a/impeller/primitives/primitives_unittests.mm b/impeller/primitives/primitives_unittests.mm index 2e4b359296d2f..d8dfee10c2cf0 100644 --- a/impeller/primitives/primitives_unittests.mm +++ b/impeller/primitives/primitives_unittests.mm @@ -252,7 +252,7 @@ } TEST_F(PrimitivesTest, CanRenderPath) { - auto path = PathBuilder{}.AddRoundedRect({10, 10, 200, 200}, 50).CreatePath(); + auto path = PathBuilder{}.AddCircle({550, 550}, 500).CreatePath(); ASSERT_FALSE(path.GetBoundingBox().IsZero()); using BoxPipeline = PipelineT; From f65dee2e8f8295efe14f047076cf7cc87a632a9f Mon Sep 17 00:00:00 2001 From: Chinmay Garde Date: Sat, 21 Aug 2021 19:31:55 -0700 Subject: [PATCH 170/433] Update defaults to be closer to Skia. --- impeller/aiks/canvas.cc | 30 +++++++++++++++++++------- impeller/aiks/canvas.h | 3 +++ impeller/compositor/formats.h | 8 +++---- impeller/compositor/pipeline_builder.h | 1 + impeller/compositor/tessellator.h | 6 +++--- 5 files changed, 33 insertions(+), 15 deletions(-) diff --git a/impeller/aiks/canvas.cc b/impeller/aiks/canvas.cc index eef923e1a618a..0701dee3861f1 100644 --- a/impeller/aiks/canvas.cc +++ b/impeller/aiks/canvas.cc @@ -11,7 +11,11 @@ namespace impeller { Canvas::Canvas() { - xformation_stack_.push(Matrix{}); + xformation_stack_.push({}); + + Paint default_paint; + default_paint.color = Color::White(); + paint_stack_.push(default_paint); } Canvas::~Canvas() = default; @@ -19,6 +23,7 @@ Canvas::~Canvas() = default; void Canvas::Save() { FML_DCHECK(xformation_stack_.size() > 0); xformation_stack_.push(xformation_stack_.top()); + paint_stack_.push(paint_stack_.top()); } bool Canvas::Restore() { @@ -27,11 +32,12 @@ bool Canvas::Restore() { return false; } xformation_stack_.pop(); + paint_stack_.pop(); return true; } void Canvas::Concat(const Matrix& xformation) { - xformation_stack_.top() = xformation_stack_.top() * xformation; + xformation_stack_.top() = xformation * xformation_stack_.top(); } const Matrix& Canvas::GetCurrentTransformation() const { @@ -54,20 +60,28 @@ size_t Canvas::GetSaveCount() const { return xformation_stack_.size(); } -void AssertionBreak() {} +void Canvas::RestoreToCount(size_t count) { + while (GetSaveCount() > count) { + if (!Restore()) { + return; + } + } +} void Canvas::DrawPath(Path path, Paint paint) { - if (path.GetBoundingBox().IsZero()) { - AssertionBreak(); - } + Color color = paint.color; + color.alpha *= paint_stack_.top().color.alpha; Entity entity; entity.SetTransformation(GetCurrentTransformation()); entity.SetPath(std::move(path)); - entity.SetBackgroundColor(paint.color); + entity.SetBackgroundColor(color); ops_.emplace_back(std::move(entity)); } -void Canvas::SaveLayer(const Paint& paint, std::optional bounds) {} +void Canvas::SaveLayer(const Paint& paint, std::optional bounds) { + Save(); + paint_stack_.top() = paint; +} void Canvas::ClipPath(Path path) { Entity entity; diff --git a/impeller/aiks/canvas.h b/impeller/aiks/canvas.h index b7513493ac2bf..6cb9fcec37ba9 100644 --- a/impeller/aiks/canvas.h +++ b/impeller/aiks/canvas.h @@ -34,6 +34,8 @@ class Canvas { size_t GetSaveCount() const; + void RestoreToCount(size_t count); + const Matrix& GetCurrentTransformation() const; void Concat(const Matrix& xformation); @@ -56,6 +58,7 @@ class Canvas { private: std::stack xformation_stack_; + std::stack paint_stack_; std::vector ops_; FML_DISALLOW_COPY_AND_ASSIGN(Canvas); diff --git a/impeller/compositor/formats.h b/impeller/compositor/formats.h index 2f868733b4c1f..da2858a1afb6c 100644 --- a/impeller/compositor/formats.h +++ b/impeller/compositor/formats.h @@ -181,13 +181,13 @@ struct PipelineColorAttachment { /// final_color = final_color & write_mask; /// ``` - BlendFactor src_color_blend_factor = BlendFactor::kOne; + BlendFactor src_color_blend_factor = BlendFactor::kSourceAlpha; BlendOperation color_blend_op = BlendOperation::kAdd; - BlendFactor dst_color_blend_factor = BlendFactor::kZero; + BlendFactor dst_color_blend_factor = BlendFactor::kOneMinusSourceAlpha; - BlendFactor src_alpha_blend_factor = BlendFactor::kOne; + BlendFactor src_alpha_blend_factor = BlendFactor::kSourceAlpha; BlendOperation alpha_blend_op = BlendOperation::kAdd; - BlendFactor dst_alpha_blend_factor = BlendFactor::kZero; + BlendFactor dst_alpha_blend_factor = BlendFactor::kOneMinusSourceAlpha; std::underlying_type_t write_mask = static_cast(ColorWriteMask::kAll); diff --git a/impeller/compositor/pipeline_builder.h b/impeller/compositor/pipeline_builder.h index dfc0dffb0b796..ec67662805e2e 100644 --- a/impeller/compositor/pipeline_builder.h +++ b/impeller/compositor/pipeline_builder.h @@ -86,6 +86,7 @@ struct PipelineBuilder { // convention that the first stage output is the color output. PipelineColorAttachment color0; color0.format = PixelFormat::kB8G8R8A8UNormInt; + color0.blending_enabled = true; desc.SetColorAttachmentDescriptor(0u, std::move(color0)); } diff --git a/impeller/compositor/tessellator.h b/impeller/compositor/tessellator.h index 71eceb40ecb37..118bc1bdd162e 100644 --- a/impeller/compositor/tessellator.h +++ b/impeller/compositor/tessellator.h @@ -16,8 +16,8 @@ namespace impeller { class Tessellator { public: enum class FillType { - kOdd, // The default winding order. - kNonZero, + kNonZero, // The default winding order. + kOdd, kPositive, kNegative, kAbsGeqTwo, @@ -38,7 +38,7 @@ class Tessellator { VertexCallback callback) const; private: - FillType fill_type_ = FillType::kOdd; + FillType fill_type_ = FillType::kNonZero; FML_DISALLOW_COPY_AND_ASSIGN(Tessellator); }; From 8086735f9da53cf429cc7d73632966056d9a440c Mon Sep 17 00:00:00 2001 From: Chinmay Garde Date: Sat, 21 Aug 2021 19:35:12 -0700 Subject: [PATCH 171/433] Add docstring to PipelineColorAttachment. --- impeller/compositor/formats.h | 40 +++++++++++++++++++---------------- 1 file changed, 22 insertions(+), 18 deletions(-) diff --git a/impeller/compositor/formats.h b/impeller/compositor/formats.h index da2858a1afb6c..59d24535f6f78 100644 --- a/impeller/compositor/formats.h +++ b/impeller/compositor/formats.h @@ -159,28 +159,32 @@ constexpr size_t BytesPerPixelForPixelFormat(PixelFormat format) { return 0u; } +//------------------------------------------------------------------------------ +/// @brief Describe the color attachment that will be used with this +/// pipeline. +/// +/// Blending at specific color attachments follows the pseudocode: +/// ``` +/// if (blending_enabled) { +/// final_color.rgb = (src_color_blend_factor * new_color.rgb) +/// +/// (dst_color_blend_factor * old_color.rgb); +/// final_color.a = (src_alpha_blend_factor * new_color.a) +/// +/// (dst_alpha_blend_factor * old_color.a); +/// } else { +/// final_color = new_color; +/// } +/// // IMPORTANT: The write mask is applied irrespective of whether +/// // blending_enabled is set. +/// final_color = final_color & write_mask; +/// ``` +/// +/// The default blend mode is 1 - source alpha. struct PipelineColorAttachment { PixelFormat format = PixelFormat::kUnknown; bool blending_enabled = false; - //---------------------------------------------------------------------------- - /// Blending at specific color attachments follows the pseudocode: - /// ``` - /// if (blending_enabled) { - /// final_color.rgb = (src_color_blend_factor * new_color.rgb) - /// - /// (dst_color_blend_factor * old_color.rgb); - /// final_color.a = (src_alpha_blend_factor * new_color.a) - /// - /// (dst_alpha_blend_factor * old_color.a); - /// } else { - /// final_color = new_color; - /// } - /// // IMPORTANT: The write mask is applied irrespective of whether - /// // blending_enabled is set. - /// final_color = final_color & write_mask; - /// ``` - BlendFactor src_color_blend_factor = BlendFactor::kSourceAlpha; BlendOperation color_blend_op = BlendOperation::kAdd; BlendFactor dst_color_blend_factor = BlendFactor::kOneMinusSourceAlpha; From f8dec7627b7639eeecb042b064481be4004fdc40 Mon Sep 17 00:00:00 2001 From: Chinmay Garde Date: Sun, 22 Aug 2021 19:55:27 -0700 Subject: [PATCH 172/433] Implement gradient contents renderer. --- impeller/aiks/canvas.cc | 3 + impeller/aiks/paint.h | 2 + impeller/compositor/vertex_buffer_builder.h | 6 +- impeller/entity/BUILD.gn | 8 +- impeller/entity/content_renderer.h | 39 ++++++++ impeller/entity/content_renderer.mm | 37 ++++++++ impeller/entity/contents.h | 61 +++++++++++++ impeller/entity/contents.mm | 88 +++++++++++++++++++ impeller/entity/entity.cc | 11 +++ impeller/entity/entity.h | 6 ++ impeller/entity/entity_renderer.mm | 23 +---- impeller/entity/entity_renderer_impl.h | 6 +- impeller/entity/entity_renderer_impl.mm | 17 ++-- impeller/entity/gradient_fill.frag | 24 +++++ impeller/entity/gradient_fill.vert | 16 ++++ .../entity/{path.vert => solid_fill.vert} | 0 16 files changed, 317 insertions(+), 30 deletions(-) create mode 100644 impeller/entity/content_renderer.h create mode 100644 impeller/entity/content_renderer.mm create mode 100644 impeller/entity/contents.h create mode 100644 impeller/entity/contents.mm create mode 100644 impeller/entity/gradient_fill.frag create mode 100644 impeller/entity/gradient_fill.vert rename impeller/entity/{path.vert => solid_fill.vert} (100%) diff --git a/impeller/aiks/canvas.cc b/impeller/aiks/canvas.cc index 0701dee3861f1..edfda32088e45 100644 --- a/impeller/aiks/canvas.cc +++ b/impeller/aiks/canvas.cc @@ -71,10 +71,13 @@ void Canvas::RestoreToCount(size_t count) { void Canvas::DrawPath(Path path, Paint paint) { Color color = paint.color; color.alpha *= paint_stack_.top().color.alpha; + Entity entity; entity.SetTransformation(GetCurrentTransformation()); entity.SetPath(std::move(path)); entity.SetBackgroundColor(color); + entity.SetContents(std::move(paint.contents)); + ops_.emplace_back(std::move(entity)); } diff --git a/impeller/aiks/paint.h b/impeller/aiks/paint.h index 6b90aa1c433b7..c3b947b8d8469 100644 --- a/impeller/aiks/paint.h +++ b/impeller/aiks/paint.h @@ -6,6 +6,7 @@ #include "flutter/fml/macros.h" +#include "impeller/entity/contents.h" #include "impeller/geometry/color.h" namespace impeller { @@ -13,6 +14,7 @@ namespace impeller { struct Paint { Color color; Scalar stroke_width = 0.0; + std::shared_ptr contents; }; } // namespace impeller diff --git a/impeller/compositor/vertex_buffer_builder.h b/impeller/compositor/vertex_buffer_builder.h index db2dffdf850fa..1d5a2669025b8 100644 --- a/impeller/compositor/vertex_buffer_builder.h +++ b/impeller/compositor/vertex_buffer_builder.h @@ -47,10 +47,10 @@ class VertexBufferBuilder { return *this; } - VertexBuffer CreateVertexBuffer(HostBuffer& host) const { + VertexBuffer CreateVertexBuffer(HostBuffer& host_buffer) const { VertexBuffer buffer; - buffer.vertex_buffer = CreateVertexBufferView(host); - buffer.index_buffer = CreateIndexBufferView(host); + buffer.vertex_buffer = CreateVertexBufferView(host_buffer); + buffer.index_buffer = CreateIndexBufferView(host_buffer); buffer.index_count = GetIndexCount(); return buffer; }; diff --git a/impeller/entity/BUILD.gn b/impeller/entity/BUILD.gn index fece2147865e1..ef9cc7aa89ad9 100644 --- a/impeller/entity/BUILD.gn +++ b/impeller/entity/BUILD.gn @@ -8,13 +8,19 @@ impeller_shaders("entity_shaders") { name = "entity" shaders = [ - "path.vert", + "gradient_fill.frag", + "gradient_fill.vert", "solid_fill.frag", + "solid_fill.vert", ] } impeller_component("entity") { sources = [ + "content_renderer.h", + "content_renderer.mm", + "contents.h", + "contents.mm", "entity.cc", "entity.h", "entity_renderer.h", diff --git a/impeller/entity/content_renderer.h b/impeller/entity/content_renderer.h new file mode 100644 index 0000000000000..6b113d86c516b --- /dev/null +++ b/impeller/entity/content_renderer.h @@ -0,0 +1,39 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#pragma once + +#include + +#include "flutter/fml/macros.h" +#include "impeller/compositor/pipeline.h" +#include "impeller/entity/gradient_fill.frag.h" +#include "impeller/entity/gradient_fill.vert.h" + +namespace impeller { + +using GradientFillPipeline = + PipelineT; + +class ContentRenderer { + public: + ContentRenderer(std::shared_ptr context); + + ~ContentRenderer(); + + bool IsValid() const; + + std::shared_ptr GetGradientFillPipeline() const; + + std::shared_ptr GetContext() const; + + private: + std::shared_ptr context_; + std::unique_ptr gradient_fill_pipeline_; + bool is_valid_ = false; + + FML_DISALLOW_COPY_AND_ASSIGN(ContentRenderer); +}; + +} // namespace impeller diff --git a/impeller/entity/content_renderer.mm b/impeller/entity/content_renderer.mm new file mode 100644 index 0000000000000..4ea53ec0c4b7b --- /dev/null +++ b/impeller/entity/content_renderer.mm @@ -0,0 +1,37 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "impeller/entity/content_renderer.h" + +namespace impeller { + +ContentRenderer::ContentRenderer(std::shared_ptr context) + : context_(std::move(context)) { + if (!context_ || !context_->IsValid()) { + return; + } + + gradient_fill_pipeline_ = std::make_unique(*context_); + + is_valid_ = true; +} + +ContentRenderer::~ContentRenderer() = default; + +bool ContentRenderer::IsValid() const { + return is_valid_; +} + +std::shared_ptr ContentRenderer::GetContext() const { + return context_; +} + +std::shared_ptr ContentRenderer::GetGradientFillPipeline() const { + if (!IsValid()) { + return nullptr; + } + return gradient_fill_pipeline_->WaitAndGet(); +} + +} // namespace impeller diff --git a/impeller/entity/contents.h b/impeller/entity/contents.h new file mode 100644 index 0000000000000..a2a55bf404289 --- /dev/null +++ b/impeller/entity/contents.h @@ -0,0 +1,61 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#pragma once + +#include + +#include "flutter/fml/macros.h" +#include "impeller/geometry/color.h" +#include "impeller/geometry/point.h" + +namespace impeller { + +class ContentRenderer; +class Entity; +class Surface; +class RenderPass; + +class Contents { + public: + Contents(); + + ~Contents(); + + virtual bool Render(const ContentRenderer& renderer, + const Entity& entity, + const Surface& surface, + RenderPass& pass) const = 0; + + private: + FML_DISALLOW_COPY_AND_ASSIGN(Contents); +}; + +class LinearGradientContents final : public Contents { + public: + LinearGradientContents(); + + ~LinearGradientContents(); + + // |Contents| + bool Render(const ContentRenderer& renderer, + const Entity& entity, + const Surface& surface, + RenderPass& pass) const override; + + void SetEndPoints(Point start_point, Point end_point); + + void SetColors(std::vector colors); + + const std::vector& GetColors() const; + + private: + Point start_point_; + Point end_point_; + std::vector colors_; + + FML_DISALLOW_COPY_AND_ASSIGN(LinearGradientContents); +}; + +} // namespace impeller diff --git a/impeller/entity/contents.mm b/impeller/entity/contents.mm new file mode 100644 index 0000000000000..9f57215f3bb59 --- /dev/null +++ b/impeller/entity/contents.mm @@ -0,0 +1,88 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "impeller/entity/contents.h" + +#include "flutter/fml/logging.h" +#include "impeller/compositor/render_pass.h" +#include "impeller/compositor/surface.h" +#include "impeller/compositor/tessellator.h" +#include "impeller/compositor/vertex_buffer_builder.h" +#include "impeller/entity/content_renderer.h" +#include "impeller/entity/entity.h" + +namespace impeller { + +Contents::Contents() = default; + +Contents::~Contents() = default; + +LinearGradientContents::LinearGradientContents() = default; + +LinearGradientContents::~LinearGradientContents() = default; + +void LinearGradientContents::SetEndPoints(Point start_point, Point end_point) { + start_point_ = start_point; + end_point_ = end_point; +} + +void LinearGradientContents::SetColors(std::vector colors) { + colors_ = std::move(colors); + if (colors_.empty()) { + colors_.push_back(Color::Black()); + colors_.push_back(Color::Black()); + } else if (colors_.size() < 2u) { + colors_.push_back(colors_.back()); + } +} + +const std::vector& LinearGradientContents::GetColors() const { + return colors_; +} + +// |Contents| +bool LinearGradientContents::Render(const ContentRenderer& renderer, + const Entity& entity, + const Surface& surface, + RenderPass& pass) const { + using VS = GradientFillPipeline::VertexShader; + using FS = GradientFillPipeline::FragmentShader; + + auto vertices_builder = VertexBufferBuilder(); + { + auto result = + Tessellator{}.Tessellate(entity.GetPath().SubdivideAdaptively(), + [&vertices_builder](Point point) { + VS::PerVertexData vtx; + vtx.vertices = point; + vertices_builder.AppendVertex(vtx); + }); + if (!result) { + return false; + } + } + + VS::FrameInfo frame_info; + frame_info.mvp = + Matrix::MakeOrthographic(surface.GetSize()) * entity.GetTransformation(); + + FS::GradientInfo gradient_info; + gradient_info.start_point = start_point_; + gradient_info.end_point = end_point_; + gradient_info.start_color = colors_[0]; + gradient_info.end_color = colors_[1]; + + Command cmd; + cmd.label = "LinearGradientFill"; + cmd.pipeline = renderer.GetGradientFillPipeline(); + cmd.BindVertices(vertices_builder.CreateVertexBuffer( + *renderer.GetContext()->GetPermanentsAllocator())); + cmd.primitive_type = PrimitiveType::kTriangle; + FS::BindGradientInfo( + cmd, pass.GetTransientsBuffer().EmplaceUniform(gradient_info)); + VS::BindFrameInfo(cmd, pass.GetTransientsBuffer().EmplaceUniform(frame_info)); + return pass.RecordCommand(std::move(cmd)); +} + +} // namespace impeller diff --git a/impeller/entity/entity.cc b/impeller/entity/entity.cc index 51acf97e5a093..186d8e1aacc24 100644 --- a/impeller/entity/entity.cc +++ b/impeller/entity/entity.cc @@ -58,11 +58,22 @@ bool Entity::IsClip() const { return is_clip_; } +void Entity::SetContents(std::shared_ptr contents) { + contents_ = std::move(contents); +} + +const std::shared_ptr& Entity::GetContents() const { + return contents_; +} + bool Entity::HasStroke() const { return stroke_size_ > 0.0 && !stroke_color_.IsTransparent(); } bool Entity::HasContents() const { + if (contents_) { + return true; + } return !background_color_.IsTransparent(); } diff --git a/impeller/entity/entity.h b/impeller/entity/entity.h index 43dd3f3fdc407..ed3942fc8aedf 100644 --- a/impeller/entity/entity.h +++ b/impeller/entity/entity.h @@ -4,6 +4,7 @@ #pragma once +#include "impeller/entity/contents.h" #include "impeller/geometry/color.h" #include "impeller/geometry/matrix.h" #include "impeller/geometry/path.h" @@ -48,9 +49,14 @@ class Entity { bool HasRenderableContents() const; + void SetContents(std::shared_ptr contents); + + const std::shared_ptr& GetContents() const; + private: Matrix transformation_; Color background_color_; + std::shared_ptr contents_; Path path_; Color stroke_color_; diff --git a/impeller/entity/entity_renderer.mm b/impeller/entity/entity_renderer.mm index 06b0a439cafbf..a352ce4f4efb3 100644 --- a/impeller/entity/entity_renderer.mm +++ b/impeller/entity/entity_renderer.mm @@ -30,29 +30,14 @@ return false; } - size_t success = 0u; - size_t failure = 0u; - size_t skipped = 0u; - for (const auto& entity : entities) { - auto result = renderer_->RenderEntity(surface, onscreen_pass, entity); - switch (result) { - case EntityRendererImpl::RenderResult::kSuccess: - success++; - break; - case EntityRendererImpl::RenderResult::kFailure: - failure++; - break; - case EntityRendererImpl::RenderResult::kSkipped: - skipped++; - break; + if (renderer_->RenderEntity(surface, onscreen_pass, entity) == + EntityRendererImpl::RenderResult::kFailure) { + return false; } } - FML_LOG(ERROR) << "Success " << success << " failure " << failure - << " skipped " << skipped << " total " << entities.size(); - - return failure == 0; + return true; } } // namespace impeller diff --git a/impeller/entity/entity_renderer_impl.h b/impeller/entity/entity_renderer_impl.h index 1bf525227d7e4..1f40114aac2ac 100644 --- a/impeller/entity/entity_renderer_impl.h +++ b/impeller/entity/entity_renderer_impl.h @@ -12,9 +12,10 @@ #include "impeller/compositor/pipeline_builder.h" #include "impeller/compositor/render_pass.h" #include "impeller/compositor/surface.h" +#include "impeller/entity/content_renderer.h" #include "impeller/entity/entity.h" -#include "impeller/entity/path.vert.h" #include "impeller/entity/solid_fill.frag.h" +#include "impeller/entity/solid_fill.vert.h" namespace impeller { @@ -41,10 +42,11 @@ class EntityRendererImpl { private: using SolidFillPipeline = - PipelineT; + PipelineT; std::shared_ptr context_; std::unique_ptr solid_fill_pipeline_; + std::unique_ptr content_renderer_; bool is_valid_ = false; diff --git a/impeller/entity/entity_renderer_impl.mm b/impeller/entity/entity_renderer_impl.mm index 531fbfc15fc08..13caea3acbafb 100644 --- a/impeller/entity/entity_renderer_impl.mm +++ b/impeller/entity/entity_renderer_impl.mm @@ -16,6 +16,11 @@ return; } + content_renderer_ = std::make_unique(context_); + if (!content_renderer_->IsValid()) { + return; + } + solid_fill_pipeline_ = std::make_unique(*context_); is_valid_ = true; @@ -32,13 +37,10 @@ RenderPass& pass, const Entity& entity) { if (!entity.HasRenderableContents()) { - if (entity.GetPath().GetBoundingBox().IsZero()) { - FML_LOG(ERROR) << "Skipped because bounds box zero."; - } return RenderResult::kSkipped; } - if (entity.HasContents() && !entity.IsClip()) { + if (entity.HasContents() && !entity.IsClip() && !entity.GetContents()) { using VS = SolidFillPipeline::VertexShader; Command cmd; @@ -50,7 +52,6 @@ VertexBufferBuilder vtx_builder; { - TRACE_EVENT0("flutter", "Tesselate"); auto tesselation_result = Tessellator{}.Tessellate( entity.GetPath().SubdivideAdaptively(), [&vtx_builder](auto point) { VS::PerVertexData vtx; @@ -77,6 +78,12 @@ if (!pass.RecordCommand(std::move(cmd))) { return RenderResult::kFailure; } + } else if (entity.GetContents()) { + auto result = + entity.GetContents()->Render(*content_renderer_, entity, surface, pass); + if (!result) { + return RenderResult::kFailure; + } } return RenderResult::kSuccess; diff --git a/impeller/entity/gradient_fill.frag b/impeller/entity/gradient_fill.frag new file mode 100644 index 0000000000000..b70cc6b879254 --- /dev/null +++ b/impeller/entity/gradient_fill.frag @@ -0,0 +1,24 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +uniform GradientInfo { + vec2 start_point; + vec2 end_point; + vec4 start_color; + vec4 end_color; +} gradient_info; + +in vec2 interpolated_vertices; + +out vec4 frag_color; + +void main() { + float len = length(gradient_info.end_point - gradient_info.start_point); + float dot = dot( + interpolated_vertices - gradient_info.start_point, + gradient_info.end_point - gradient_info.start_point + ); + float interp = dot / (len * len); + frag_color = mix(gradient_info.start_color, gradient_info.end_color, interp); +} diff --git a/impeller/entity/gradient_fill.vert b/impeller/entity/gradient_fill.vert new file mode 100644 index 0000000000000..f9d0ccdabfc3e --- /dev/null +++ b/impeller/entity/gradient_fill.vert @@ -0,0 +1,16 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +uniform FrameInfo { + mat4 mvp; +} frame_info; + +in vec2 vertices; + +out vec2 interpolated_vertices; + +void main() { + gl_Position = frame_info.mvp * vec4(vertices, 0.0, 1.0); + interpolated_vertices = vertices; +} diff --git a/impeller/entity/path.vert b/impeller/entity/solid_fill.vert similarity index 100% rename from impeller/entity/path.vert rename to impeller/entity/solid_fill.vert From 29f2aa6ee21130b7088d276f96e36260ba8423ab Mon Sep 17 00:00:00 2001 From: Chinmay Garde Date: Mon, 18 Oct 2021 14:52:29 -0700 Subject: [PATCH 173/433] Merge primitives into compositor. --- impeller/BUILD.gn | 2 -- impeller/compositor/BUILD.gn | 2 ++ .../compositor_unittests.mm} | 16 ++++----- impeller/entity/BUILD.gn | 1 - impeller/fixtures/BUILD.gn | 21 ++++++++++- .../{primitives => fixtures}/box_fade.frag | 0 .../{primitives => fixtures}/box_fade.vert | 0 impeller/playground/playground.mm | 2 +- impeller/primitives/BUILD.gn | 35 ------------------ impeller/primitives/primitive.h | 36 ------------------- impeller/primitives/primitive.mm | 18 ---------- 11 files changed, 31 insertions(+), 102 deletions(-) rename impeller/{primitives/primitives_unittests.mm => compositor/compositor_unittests.mm} (96%) rename impeller/{primitives => fixtures}/box_fade.frag (100%) rename impeller/{primitives => fixtures}/box_fade.vert (100%) delete mode 100644 impeller/primitives/BUILD.gn delete mode 100644 impeller/primitives/primitive.h delete mode 100644 impeller/primitives/primitive.mm diff --git a/impeller/BUILD.gn b/impeller/BUILD.gn index 1d77a23fc650c..6d76110214f93 100644 --- a/impeller/BUILD.gn +++ b/impeller/BUILD.gn @@ -15,7 +15,6 @@ group("impeller") { "entity", "geometry", "image", - "primitives", ] } @@ -32,6 +31,5 @@ executable("impeller_unittests") { "geometry:geometry_unittests", "image:image_unittests", "playground", - "primitives:primitives_unittests", ] } diff --git a/impeller/compositor/BUILD.gn b/impeller/compositor/BUILD.gn index c29c5b976c34a..e6d5abcac1023 100644 --- a/impeller/compositor/BUILD.gn +++ b/impeller/compositor/BUILD.gn @@ -87,12 +87,14 @@ source_set("compositor_unittests") { testonly = true sources = [ + "compositor_unittests.mm", "device_buffer_unittests.mm", "host_buffer_unittests.mm", ] deps = [ ":compositor", + "../fixtures", "../playground", "//flutter/testing:testing_lib", ] diff --git a/impeller/primitives/primitives_unittests.mm b/impeller/compositor/compositor_unittests.mm similarity index 96% rename from impeller/primitives/primitives_unittests.mm rename to impeller/compositor/compositor_unittests.mm index d8dfee10c2cf0..da028490b1865 100644 --- a/impeller/primitives/primitives_unittests.mm +++ b/impeller/compositor/compositor_unittests.mm @@ -3,6 +3,8 @@ // found in the LICENSE file. #include "flutter/fml/time/time_point.h" +#include "flutter/impeller/fixtures/box_fade.frag.h" +#include "flutter/impeller/fixtures/box_fade.vert.h" #include "flutter/testing/testing.h" #include "impeller/compositor/command.h" #include "impeller/compositor/command_buffer.h" @@ -17,15 +19,13 @@ #include "impeller/image/compressed_image.h" #include "impeller/image/decompressed_image.h" #include "impeller/playground/playground.h" -#include "impeller/primitives/box_fade.frag.h" -#include "impeller/primitives/box_fade.vert.h" namespace impeller { namespace testing { -using PrimitivesTest = Playground; +using CompositorTest = Playground; -TEST_F(PrimitivesTest, CanCreateBoxPrimitive) { +TEST_F(CompositorTest, CanCreateBoxPrimitive) { using VS = BoxFadeVertexShader; using FS = BoxFadeFragmentShader; auto context = GetContext(); @@ -89,7 +89,7 @@ // OpenPlaygroundHere(callback); } -TEST_F(PrimitivesTest, CanRenderMultiplePrimitives) { +TEST_F(CompositorTest, CanRenderMultiplePrimitives) { using VS = BoxFadeVertexShader; using FS = BoxFadeFragmentShader; auto context = GetContext(); @@ -160,7 +160,7 @@ // OpenPlaygroundHere(callback); } -TEST_F(PrimitivesTest, CanRenderToTexture) { +TEST_F(CompositorTest, CanRenderToTexture) { using VS = BoxFadeVertexShader; using FS = BoxFadeFragmentShader; auto context = GetContext(); @@ -251,7 +251,7 @@ ASSERT_TRUE(r2t_pass->Commit(*context->GetTransientsAllocator())); } -TEST_F(PrimitivesTest, CanRenderPath) { +TEST_F(CompositorTest, CanRenderPath) { auto path = PathBuilder{}.AddCircle({550, 550}, 500).CreatePath(); ASSERT_FALSE(path.GetBoundingBox().IsZero()); @@ -318,7 +318,7 @@ return true; }; - OpenPlaygroundHere(callback); + // OpenPlaygroundHere(callback); } } // namespace testing diff --git a/impeller/entity/BUILD.gn b/impeller/entity/BUILD.gn index ef9cc7aa89ad9..2b76285467fc5 100644 --- a/impeller/entity/BUILD.gn +++ b/impeller/entity/BUILD.gn @@ -34,7 +34,6 @@ impeller_component("entity") { public_deps = [ "../compositor", "../image", - "../primitives", ] } diff --git a/impeller/fixtures/BUILD.gn b/impeller/fixtures/BUILD.gn index c55f7cad7e55a..6e664a4e2f362 100644 --- a/impeller/fixtures/BUILD.gn +++ b/impeller/fixtures/BUILD.gn @@ -5,7 +5,17 @@ import("//flutter/impeller/tools/impeller.gni") import("//flutter/testing/testing.gni") -test_fixtures("fixtures") { +import("//flutter/impeller/tools/impeller.gni") + +impeller_shaders("shader_fixtures") { + name = "shader_fixtures" + shaders = [ + "box_fade.vert", + "box_fade.frag", + ] +} + +test_fixtures("file_fixtures") { fixtures = [ "sample.vert", "types.h", @@ -16,3 +26,12 @@ test_fixtures("fixtures") { "kalimba.jpg", ] } + +group("fixtures") { + testonly = true + + deps = [ + ":file_fixtures", + ":shader_fixtures", + ] +} diff --git a/impeller/primitives/box_fade.frag b/impeller/fixtures/box_fade.frag similarity index 100% rename from impeller/primitives/box_fade.frag rename to impeller/fixtures/box_fade.frag diff --git a/impeller/primitives/box_fade.vert b/impeller/fixtures/box_fade.vert similarity index 100% rename from impeller/primitives/box_fade.vert rename to impeller/fixtures/box_fade.vert diff --git a/impeller/playground/playground.mm b/impeller/playground/playground.mm index 3373594c05945..8104b9bd7f782 100644 --- a/impeller/playground/playground.mm +++ b/impeller/playground/playground.mm @@ -34,7 +34,7 @@ } Playground::Playground() - : renderer_(ShaderLibraryDirectory(), "impeller.metallib") {} + : renderer_(ShaderLibraryDirectory(), "shader_fixtures.metallib") {} Playground::~Playground() = default; diff --git a/impeller/primitives/BUILD.gn b/impeller/primitives/BUILD.gn deleted file mode 100644 index 6d50bbf25e9f7..0000000000000 --- a/impeller/primitives/BUILD.gn +++ /dev/null @@ -1,35 +0,0 @@ -# Copyright 2013 The Flutter Authors. All rights reserved. -# Use of this source code is governed by a BSD-style license that can be -# found in the LICENSE file. - -import("//flutter/impeller/tools/impeller.gni") - -impeller_shaders("impeller_shaders") { - name = "impeller" - shaders = [ - "box_fade.vert", - "box_fade.frag", - ] -} - -impeller_component("primitives") { - sources = [ - "primitive.h", - "primitive.mm", - ] - - public_deps = [ - ":impeller_shaders", - "../compositor", - ] -} - -impeller_component("primitives_unittests") { - testonly = true - sources = [ "primitives_unittests.mm" ] - deps = [ - ":primitives", - "../image", - "../playground", - ] -} diff --git a/impeller/primitives/primitive.h b/impeller/primitives/primitive.h deleted file mode 100644 index 755abde8df49d..0000000000000 --- a/impeller/primitives/primitive.h +++ /dev/null @@ -1,36 +0,0 @@ -// Copyright 2013 The Flutter Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#pragma once - -#include - -#include "flutter/fml/macros.h" -#include "impeller/compositor/context.h" -#include "impeller/compositor/pipeline.h" -#include "impeller/compositor/render_pass.h" - -namespace impeller { - -class Primitive { - public: - Primitive(std::shared_ptr); - - virtual std::shared_ptr GetPipeline() const = 0; - - virtual ~Primitive(); - - virtual bool IsValid() const = 0; - - std::shared_ptr GetContext() const; - - virtual bool Encode(RenderPass& pass) const = 0; - - private: - std::shared_ptr context_; - - FML_DISALLOW_COPY_AND_ASSIGN(Primitive); -}; - -} // namespace impeller diff --git a/impeller/primitives/primitive.mm b/impeller/primitives/primitive.mm deleted file mode 100644 index ef24844db99ef..0000000000000 --- a/impeller/primitives/primitive.mm +++ /dev/null @@ -1,18 +0,0 @@ -// Copyright 2013 The Flutter Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#include "impeller/primitives/primitive.h" - -namespace impeller { - -Primitive::Primitive(std::shared_ptr context) - : context_(std::move(context)) {} - -Primitive::~Primitive() = default; - -std::shared_ptr Primitive::GetContext() const { - return context_; -} - -} // namespace impeller From b0c33fa26398c5136a96605fd511d7285a396da5 Mon Sep 17 00:00:00 2001 From: Chinmay Garde Date: Tue, 19 Oct 2021 13:27:55 -0700 Subject: [PATCH 174/433] Move MatrixDecomposition into its TU. --- impeller/fixtures/BUILD.gn | 2 +- impeller/geometry/BUILD.gn | 2 ++ impeller/geometry/geometry_unittests.cc | 16 +++++------ impeller/geometry/matrix.cc | 14 +++++----- impeller/geometry/matrix.h | 28 +++---------------- impeller/geometry/matrix_decomposition.cc | 11 ++++++++ impeller/geometry/matrix_decomposition.h | 33 +++++++++++++++++++++++ 7 files changed, 66 insertions(+), 40 deletions(-) create mode 100644 impeller/geometry/matrix_decomposition.cc create mode 100644 impeller/geometry/matrix_decomposition.h diff --git a/impeller/fixtures/BUILD.gn b/impeller/fixtures/BUILD.gn index 6e664a4e2f362..3bbffb60917aa 100644 --- a/impeller/fixtures/BUILD.gn +++ b/impeller/fixtures/BUILD.gn @@ -30,7 +30,7 @@ test_fixtures("file_fixtures") { group("fixtures") { testonly = true - deps = [ + public_deps = [ ":file_fixtures", ":shader_fixtures", ] diff --git a/impeller/geometry/BUILD.gn b/impeller/geometry/BUILD.gn index f57a99bfe6ad2..414b5026f0648 100644 --- a/impeller/geometry/BUILD.gn +++ b/impeller/geometry/BUILD.gn @@ -10,6 +10,8 @@ impeller_component("geometry") { "color.h", "matrix.cc", "matrix.h", + "matrix_decomposition.cc", + "matrix_decomposition.h", "path.cc", "path.h", "path_builder.cc", diff --git a/impeller/geometry/geometry_unittests.cc b/impeller/geometry/geometry_unittests.cc index 13b39bd34a1d0..f7f270dfa6420 100644 --- a/impeller/geometry/geometry_unittests.cc +++ b/impeller/geometry/geometry_unittests.cc @@ -64,9 +64,9 @@ TEST(GeometryTest, TestDecomposition) { auto result = rotated.Decompose(); - ASSERT_TRUE(result.first); + ASSERT_TRUE(result.has_value()); - Matrix::Decomposition res = result.second; + MatrixDecomposition res = result.value(); auto quaternion = Quaternion{{0.0, 0.0, 1.0}, M_PI_4}; ASSERT_QUATERNION_NEAR(res.rotation, quaternion); @@ -79,9 +79,9 @@ TEST(GeometryTest, TestDecomposition2) { auto result = (translated * rotated * scaled).Decompose(); - ASSERT_TRUE(result.first); + ASSERT_TRUE(result.has_value()); - Matrix::Decomposition res = result.second; + MatrixDecomposition res = result.value(); auto quaternion = Quaternion{{0.0, 0.0, 1.0}, M_PI_4}; @@ -104,9 +104,9 @@ TEST(GeometryTest, TestRecomposition) { auto result = rotated.Decompose(); - ASSERT_TRUE(result.first); + ASSERT_TRUE(result.has_value()); - Matrix::Decomposition res = result.second; + MatrixDecomposition res = result.value(); auto quaternion = Quaternion{{0.0, 0.0, 1.0}, M_PI_4}; @@ -125,9 +125,9 @@ TEST(GeometryTest, TestRecomposition2) { auto result = matrix.Decompose(); - ASSERT_TRUE(result.first); + ASSERT_TRUE(result.has_value()); - ASSERT_MATRIX_NEAR(matrix, Matrix{result.second}); + ASSERT_MATRIX_NEAR(matrix, Matrix{result.value()}); } TEST(GeometryTest, QuaternionLerp) { diff --git a/impeller/geometry/matrix.cc b/impeller/geometry/matrix.cc index 5f77fcced0ff4..f26411039a097 100644 --- a/impeller/geometry/matrix.cc +++ b/impeller/geometry/matrix.cc @@ -9,7 +9,7 @@ namespace impeller { -Matrix::Matrix(const Decomposition& d) : Matrix() { +Matrix::Matrix(const MatrixDecomposition& d) : Matrix() { /* * Apply perspective. */ @@ -197,14 +197,14 @@ Scalar Matrix::GetDeterminant() const { * Adapted for Impeller from Graphics Gems: * http://www.realtimerendering.com/resources/GraphicsGems/gemsii/unmatrix.c */ -Matrix::DecompositionResult Matrix::Decompose() const { +std::optional Matrix::Decompose() const { /* * Normalize the matrix. */ Matrix self = *this; if (self.e[3][3] == 0) { - return {false, {}}; + return std::nullopt; } for (int i = 0; i < 4; i++) { @@ -225,10 +225,10 @@ Matrix::DecompositionResult Matrix::Decompose() const { perpectiveMatrix.e[3][3] = 1; if (perpectiveMatrix.GetDeterminant() == 0.0) { - return {false, {}}; + return std::nullopt; } - Decomposition result; + MatrixDecomposition result; /* * ========================================================================== @@ -355,10 +355,10 @@ Matrix::DecompositionResult Matrix::Decompose() const { result.rotation.z = -result.rotation.z; } - return DecompositionResult(true, result); + return result; } -uint64_t Matrix::Decomposition::GetComponentsMask() const { +uint64_t MatrixDecomposition::GetComponentsMask() const { uint64_t mask = 0; Quaternion noRotation(0.0, 0.0, 0.0, 1.0); diff --git a/impeller/geometry/matrix.h b/impeller/geometry/matrix.h index f88eafb6e0504..3156a81009a7b 100644 --- a/impeller/geometry/matrix.h +++ b/impeller/geometry/matrix.h @@ -5,8 +5,10 @@ #pragma once #include +#include #include +#include "impeller/geometry/matrix_decomposition.h" #include "impeller/geometry/point.h" #include "impeller/geometry/quaternion.h" #include "impeller/geometry/scalar.h" @@ -35,28 +37,6 @@ struct Matrix { Vector4 vec[4]; }; - struct Decomposition { - Vector3 translation; - Vector3 scale; - Shear shear; - Vector4 perspective; - Quaternion rotation; - - enum class Component { - kTranslation = 1 << 0, - kScale = 1 << 1, - kShear = 1 << 2, - kPerspective = 1 << 3, - kRotation = 1 << 4, - }; - - uint64_t GetComponentsMask() const; - }; - - // TODO(csg): Radar restrictions of C++11 don't exist. Use optionals instead. - using DecompositionResult = - std::pair; - //---------------------------------------------------------------------------- /// Construts a default identity matrix. /// @@ -79,7 +59,7 @@ struct Matrix { Vector4(m12, m13, m14, m15)} {} // clang-format on - Matrix(const Decomposition& decomposition); + Matrix(const MatrixDecomposition& decomposition); static constexpr Matrix MakeTranslation(const Vector3& t) { // clang-format off @@ -243,7 +223,7 @@ struct Matrix { ); } - DecompositionResult Decompose() const; + std::optional Decompose() const; constexpr bool operator==(const Matrix& m) const { // clang-format off diff --git a/impeller/geometry/matrix_decomposition.cc b/impeller/geometry/matrix_decomposition.cc new file mode 100644 index 0000000000000..a945345b30a36 --- /dev/null +++ b/impeller/geometry/matrix_decomposition.cc @@ -0,0 +1,11 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "impeller/geometry/matrix_decomposition.h" + +namespace impeller { + +// + +} // namespace impeller diff --git a/impeller/geometry/matrix_decomposition.h b/impeller/geometry/matrix_decomposition.h new file mode 100644 index 0000000000000..def9029ea7369 --- /dev/null +++ b/impeller/geometry/matrix_decomposition.h @@ -0,0 +1,33 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#pragma once + +#include "flutter/fml/macros.h" +#include "impeller/geometry/quaternion.h" +#include "impeller/geometry/scalar.h" +#include "impeller/geometry/shear.h" +#include "impeller/geometry/vector.h" + +namespace impeller { + +struct MatrixDecomposition { + Vector3 translation; + Vector3 scale; + Shear shear; + Vector4 perspective; + Quaternion rotation; + + enum class Component { + kTranslation = 1 << 0, + kScale = 1 << 1, + kShear = 1 << 2, + kPerspective = 1 << 3, + kRotation = 1 << 4, + }; + + uint64_t GetComponentsMask() const; +}; + +} // namespace impeller From 57e2c66f7e4989f84f1c5b67dea88c7df29bc6e4 Mon Sep 17 00:00:00 2001 From: Chinmay Garde Date: Tue, 19 Oct 2021 13:50:28 -0700 Subject: [PATCH 175/433] Rename the compositor to the entity framework. --- impeller/BUILD.gn | 4 ++-- impeller/compiler/code_gen_template.h | 10 +++++----- impeller/entity/BUILD.gn | 2 +- impeller/entity/content_renderer.h | 2 +- impeller/entity/contents.mm | 8 ++++---- impeller/entity/entity_renderer_impl.h | 10 +++++----- impeller/entity/entity_renderer_impl.mm | 4 ++-- impeller/playground/BUILD.gn | 2 +- impeller/playground/playground.h | 5 +++-- impeller/playground/playground.mm | 12 ++++++------ impeller/{compositor => renderer}/BUILD.gn | 8 ++++---- impeller/{compositor => renderer}/allocator.h | 2 +- impeller/{compositor => renderer}/allocator.mm | 8 ++++---- impeller/{compositor => renderer}/buffer.h | 0 impeller/{compositor => renderer}/buffer.mm | 2 +- .../{compositor => renderer}/buffer_view.h | 4 ++-- .../{compositor => renderer}/buffer_view.mm | 2 +- impeller/{compositor => renderer}/command.h | 14 +++++++------- impeller/{compositor => renderer}/command.mm | 4 ++-- .../{compositor => renderer}/command_buffer.h | 2 +- .../{compositor => renderer}/command_buffer.mm | 2 +- .../{compositor => renderer}/comparable.cc | 2 +- impeller/{compositor => renderer}/comparable.h | 0 impeller/{compositor => renderer}/context.h | 0 impeller/{compositor => renderer}/context.mm | 10 +++++----- .../{compositor => renderer}/device_buffer.h | 10 +++++----- .../{compositor => renderer}/device_buffer.mm | 4 ++-- .../device_buffer_unittests.mm | 2 +- impeller/{compositor => renderer}/formats.cc | 2 +- impeller/{compositor => renderer}/formats.h | 0 .../{compositor => renderer}/formats_metal.h | 4 ++-- .../{compositor => renderer}/formats_metal.mm | 4 ++-- .../{compositor => renderer}/host_buffer.h | 6 +++--- .../{compositor => renderer}/host_buffer.mm | 8 ++++---- .../host_buffer_unittests.mm | 2 +- impeller/{compositor => renderer}/pipeline.h | 4 ++-- impeller/{compositor => renderer}/pipeline.mm | 6 +++--- .../pipeline_builder.h | 8 ++++---- .../pipeline_builder.mm | 2 +- .../pipeline_descriptor.h | 6 +++--- .../pipeline_descriptor.mm | 8 ++++---- .../pipeline_library.h | 4 ++-- .../pipeline_library.mm | 2 +- impeller/{compositor => renderer}/platform.h | 0 impeller/{compositor => renderer}/platform.mm | 2 +- impeller/{compositor => renderer}/range.cc | 2 +- impeller/{compositor => renderer}/range.h | 0 .../{compositor => renderer}/render_pass.h | 10 +++++----- .../{compositor => renderer}/render_pass.mm | 10 +++++----- .../render_pass_descriptor.h | 2 +- .../render_pass_descriptor.mm | 4 ++-- impeller/{compositor => renderer}/renderer.h | 2 +- impeller/{compositor => renderer}/renderer.mm | 6 +++--- .../renderer_unittests.mm} | 18 +++++++++--------- impeller/{compositor => renderer}/sampler.h | 0 impeller/{compositor => renderer}/sampler.mm | 2 +- .../sampler_descriptor.h | 4 ++-- .../sampler_descriptor.mm | 6 +++--- .../{compositor => renderer}/shader_function.h | 4 ++-- .../shader_function.mm | 2 +- .../{compositor => renderer}/shader_library.h | 4 ++-- .../{compositor => renderer}/shader_library.mm | 2 +- .../{compositor => renderer}/shader_types.cc | 2 +- .../{compositor => renderer}/shader_types.h | 0 impeller/{compositor => renderer}/surface.h | 4 ++-- impeller/{compositor => renderer}/surface.mm | 2 +- .../{compositor => renderer}/tessellator.cc | 2 +- .../{compositor => renderer}/tessellator.h | 2 +- impeller/{compositor => renderer}/texture.h | 4 ++-- impeller/{compositor => renderer}/texture.mm | 2 +- .../texture_descriptor.h | 2 +- .../texture_descriptor.mm | 2 +- .../{compositor => renderer}/vertex_buffer.h | 2 +- .../{compositor => renderer}/vertex_buffer.mm | 2 +- .../vertex_buffer_builder.h | 10 +++++----- .../vertex_buffer_builder.mm | 2 +- .../vertex_descriptor.h | 4 ++-- .../vertex_descriptor.mm | 2 +- impeller/tools/impeller.gni | 2 +- 79 files changed, 164 insertions(+), 163 deletions(-) rename impeller/{compositor => renderer}/BUILD.gn (94%) rename impeller/{compositor => renderer}/allocator.h (98%) rename impeller/{compositor => renderer}/allocator.mm (94%) rename impeller/{compositor => renderer}/buffer.h (100%) rename impeller/{compositor => renderer}/buffer.mm (85%) rename impeller/{compositor => renderer}/buffer_view.h (83%) rename impeller/{compositor => renderer}/buffer_view.mm (82%) rename impeller/{compositor => renderer}/command.h (85%) rename impeller/{compositor => renderer}/command.mm (96%) rename impeller/{compositor => renderer}/command_buffer.h (95%) rename impeller/{compositor => renderer}/command_buffer.mm (96%) rename impeller/{compositor => renderer}/comparable.cc (86%) rename impeller/{compositor => renderer}/comparable.h (100%) rename impeller/{compositor => renderer}/context.h (100%) rename impeller/{compositor => renderer}/context.mm (93%) rename impeller/{compositor => renderer}/device_buffer.h (90%) rename impeller/{compositor => renderer}/device_buffer.mm (96%) rename impeller/{compositor => renderer}/device_buffer_unittests.mm (88%) rename impeller/{compositor => renderer}/formats.cc (84%) rename impeller/{compositor => renderer}/formats.h (100%) rename impeller/{compositor => renderer}/formats_metal.h (98%) rename impeller/{compositor => renderer}/formats_metal.mm (97%) rename impeller/{compositor => renderer}/host_buffer.h (93%) rename impeller/{compositor => renderer}/host_buffer.mm (90%) rename impeller/{compositor => renderer}/host_buffer_unittests.mm (97%) rename impeller/{compositor => renderer}/pipeline.h (95%) rename impeller/{compositor => renderer}/pipeline.mm (89%) rename impeller/{compositor => renderer}/pipeline_builder.h (95%) rename impeller/{compositor => renderer}/pipeline_builder.mm (81%) rename impeller/{compositor => renderer}/pipeline_descriptor.h (95%) rename impeller/{compositor => renderer}/pipeline_descriptor.mm (96%) rename impeller/{compositor => renderer}/pipeline_library.h (92%) rename impeller/{compositor => renderer}/pipeline_library.mm (97%) rename impeller/{compositor => renderer}/platform.h (100%) rename impeller/{compositor => renderer}/platform.mm (83%) rename impeller/{compositor => renderer}/range.cc (84%) rename impeller/{compositor => renderer}/range.h (100%) rename impeller/{compositor => renderer}/render_pass.h (88%) rename impeller/{compositor => renderer}/render_pass.mm (98%) rename impeller/{compositor => renderer}/render_pass_descriptor.h (96%) rename impeller/{compositor => renderer}/render_pass_descriptor.mm (93%) rename impeller/{compositor => renderer}/renderer.h (95%) rename impeller/{compositor => renderer}/renderer.mm (93%) rename impeller/{compositor/compositor_unittests.mm => renderer/renderer_unittests.mm} (96%) rename impeller/{compositor => renderer}/sampler.h (100%) rename impeller/{compositor => renderer}/sampler.mm (91%) rename impeller/{compositor => renderer}/sampler_descriptor.h (95%) rename impeller/{compositor => renderer}/sampler_descriptor.mm (90%) rename impeller/{compositor => renderer}/shader_function.h (92%) rename impeller/{compositor => renderer}/shader_function.mm (95%) rename impeller/{compositor => renderer}/shader_library.h (94%) rename impeller/{compositor => renderer}/shader_library.mm (95%) rename impeller/{compositor => renderer}/shader_types.cc (82%) rename impeller/{compositor => renderer}/shader_types.h (100%) rename impeller/{compositor => renderer}/surface.h (88%) rename impeller/{compositor => renderer}/surface.mm (94%) rename impeller/{compositor => renderer}/tessellator.cc (98%) rename impeller/{compositor => renderer}/tessellator.h (95%) rename impeller/{compositor => renderer}/texture.h (89%) rename impeller/{compositor => renderer}/texture.mm (97%) rename impeller/{compositor => renderer}/texture_descriptor.h (96%) rename impeller/{compositor => renderer}/texture_descriptor.mm (80%) rename impeller/{compositor => renderer}/vertex_buffer.h (91%) rename impeller/{compositor => renderer}/vertex_buffer.mm (82%) rename impeller/{compositor => renderer}/vertex_buffer_builder.h (94%) rename impeller/{compositor => renderer}/vertex_buffer_builder.mm (79%) rename impeller/{compositor => renderer}/vertex_descriptor.h (96%) rename impeller/{compositor => renderer}/vertex_descriptor.mm (99%) diff --git a/impeller/BUILD.gn b/impeller/BUILD.gn index 6d76110214f93..e9a840fb1b102 100644 --- a/impeller/BUILD.gn +++ b/impeller/BUILD.gn @@ -11,10 +11,10 @@ group("impeller") { "aiks", "base", "compiler", - "compositor", "entity", "geometry", "image", + "renderer", ] } @@ -25,11 +25,11 @@ executable("impeller_unittests") { "aiks:aiks_unittests", "base:base_unittests", "compiler:compiler_unittests", - "compositor:compositor_unittests", "entity:entity_unittests", "fixtures", "geometry:geometry_unittests", "image:image_unittests", "playground", + "renderer:renderer_unittests", ] } diff --git a/impeller/compiler/code_gen_template.h b/impeller/compiler/code_gen_template.h index a5a01b0165035..8783c5573e5c8 100644 --- a/impeller/compiler/code_gen_template.h +++ b/impeller/compiler/code_gen_template.h @@ -15,11 +15,11 @@ constexpr std::string_view kReflectionHeaderTemplate = // Note: The nogncheck decorations are only to make GN not mad at the template. // There are no GN rule violations in the generated file itself. -#include "impeller/compositor/buffer_view.h" // nogncheck -#include "impeller/compositor/command.h" // nogncheck -#include "impeller/compositor/sampler.h" // nogncheck -#include "impeller/compositor/shader_types.h" // nogncheck -#include "impeller/compositor/texture.h" // nogncheck +#include "impeller/renderer/buffer_view.h" // nogncheck +#include "impeller/renderer/command.h" // nogncheck +#include "impeller/renderer/sampler.h" // nogncheck +#include "impeller/renderer/shader_types.h" // nogncheck +#include "impeller/renderer/texture.h" // nogncheck namespace impeller { diff --git a/impeller/entity/BUILD.gn b/impeller/entity/BUILD.gn index 2b76285467fc5..e67246e55fd1b 100644 --- a/impeller/entity/BUILD.gn +++ b/impeller/entity/BUILD.gn @@ -32,8 +32,8 @@ impeller_component("entity") { deps = [ ":entity_shaders" ] public_deps = [ - "../compositor", "../image", + "../renderer", ] } diff --git a/impeller/entity/content_renderer.h b/impeller/entity/content_renderer.h index 6b113d86c516b..d803ef45f2243 100644 --- a/impeller/entity/content_renderer.h +++ b/impeller/entity/content_renderer.h @@ -7,9 +7,9 @@ #include #include "flutter/fml/macros.h" -#include "impeller/compositor/pipeline.h" #include "impeller/entity/gradient_fill.frag.h" #include "impeller/entity/gradient_fill.vert.h" +#include "impeller/renderer/pipeline.h" namespace impeller { diff --git a/impeller/entity/contents.mm b/impeller/entity/contents.mm index 9f57215f3bb59..de6faaef4c119 100644 --- a/impeller/entity/contents.mm +++ b/impeller/entity/contents.mm @@ -5,12 +5,12 @@ #include "impeller/entity/contents.h" #include "flutter/fml/logging.h" -#include "impeller/compositor/render_pass.h" -#include "impeller/compositor/surface.h" -#include "impeller/compositor/tessellator.h" -#include "impeller/compositor/vertex_buffer_builder.h" #include "impeller/entity/content_renderer.h" #include "impeller/entity/entity.h" +#include "impeller/renderer/render_pass.h" +#include "impeller/renderer/surface.h" +#include "impeller/renderer/tessellator.h" +#include "impeller/renderer/vertex_buffer_builder.h" namespace impeller { diff --git a/impeller/entity/entity_renderer_impl.h b/impeller/entity/entity_renderer_impl.h index 1f40114aac2ac..dcea72aded521 100644 --- a/impeller/entity/entity_renderer_impl.h +++ b/impeller/entity/entity_renderer_impl.h @@ -7,15 +7,15 @@ #include #include "flutter/fml/macros.h" -#include "impeller/compositor/context.h" -#include "impeller/compositor/pipeline.h" -#include "impeller/compositor/pipeline_builder.h" -#include "impeller/compositor/render_pass.h" -#include "impeller/compositor/surface.h" #include "impeller/entity/content_renderer.h" #include "impeller/entity/entity.h" #include "impeller/entity/solid_fill.frag.h" #include "impeller/entity/solid_fill.vert.h" +#include "impeller/renderer/context.h" +#include "impeller/renderer/pipeline.h" +#include "impeller/renderer/pipeline_builder.h" +#include "impeller/renderer/render_pass.h" +#include "impeller/renderer/surface.h" namespace impeller { diff --git a/impeller/entity/entity_renderer_impl.mm b/impeller/entity/entity_renderer_impl.mm index 13caea3acbafb..4a46a5f4d4b10 100644 --- a/impeller/entity/entity_renderer_impl.mm +++ b/impeller/entity/entity_renderer_impl.mm @@ -5,8 +5,8 @@ #include "impeller/entity/entity_renderer_impl.h" #include "flutter/fml/trace_event.h" -#include "impeller/compositor/tessellator.h" -#include "impeller/compositor/vertex_buffer_builder.h" +#include "impeller/renderer/tessellator.h" +#include "impeller/renderer/vertex_buffer_builder.h" namespace impeller { diff --git a/impeller/playground/BUILD.gn b/impeller/playground/BUILD.gn index 5c142a125c8ae..111e40d763ac7 100644 --- a/impeller/playground/BUILD.gn +++ b/impeller/playground/BUILD.gn @@ -14,7 +14,7 @@ impeller_component("playground") { ] public_deps = [ - "../compositor", + "../renderer", "//flutter/testing", "//third_party/glfw", ] diff --git a/impeller/playground/playground.h b/impeller/playground/playground.h index 1d345111d87db..6e11998cda78b 100644 --- a/impeller/playground/playground.h +++ b/impeller/playground/playground.h @@ -5,8 +5,9 @@ #include "flutter/fml/closure.h" #include "flutter/fml/macros.h" #include "gtest/gtest.h" -#include "impeller/compositor/renderer.h" -#include "impeller/compositor/texture.h" +#include "impeller/geometry/point.h" +#include "impeller/renderer/renderer.h" +#include "impeller/renderer/texture.h" namespace impeller { diff --git a/impeller/playground/playground.mm b/impeller/playground/playground.mm index 8104b9bd7f782..95247d2a94c63 100644 --- a/impeller/playground/playground.mm +++ b/impeller/playground/playground.mm @@ -6,14 +6,14 @@ #include "flutter/fml/paths.h" #include "flutter/testing/testing.h" -#include "impeller/compositor/allocator.h" -#include "impeller/compositor/context.h" -#include "impeller/compositor/formats_metal.h" -#include "impeller/compositor/render_pass.h" -#include "impeller/compositor/renderer.h" -#include "impeller/compositor/surface.h" #include "impeller/image/compressed_image.h" #include "impeller/playground/playground.h" +#include "impeller/renderer/allocator.h" +#include "impeller/renderer/context.h" +#include "impeller/renderer/formats_metal.h" +#include "impeller/renderer/render_pass.h" +#include "impeller/renderer/renderer.h" +#include "impeller/renderer/surface.h" #define GLFW_INCLUDE_NONE #import "third_party/glfw/include/GLFW/glfw3.h" diff --git a/impeller/compositor/BUILD.gn b/impeller/renderer/BUILD.gn similarity index 94% rename from impeller/compositor/BUILD.gn rename to impeller/renderer/BUILD.gn index e6d5abcac1023..2992255101ee7 100644 --- a/impeller/compositor/BUILD.gn +++ b/impeller/renderer/BUILD.gn @@ -4,7 +4,7 @@ import("//flutter/impeller/tools/impeller.gni") -impeller_component("compositor") { +impeller_component("renderer") { sources = [ "allocator.h", "allocator.mm", @@ -83,17 +83,17 @@ impeller_component("compositor") { frameworks = [ "Metal.framework" ] } -source_set("compositor_unittests") { +source_set("renderer_unittests") { testonly = true sources = [ - "compositor_unittests.mm", "device_buffer_unittests.mm", "host_buffer_unittests.mm", + "renderer_unittests.mm", ] deps = [ - ":compositor", + ":renderer", "../fixtures", "../playground", "//flutter/testing:testing_lib", diff --git a/impeller/compositor/allocator.h b/impeller/renderer/allocator.h similarity index 98% rename from impeller/compositor/allocator.h rename to impeller/renderer/allocator.h index 4b3222026ebbb..14c37288f1ae3 100644 --- a/impeller/compositor/allocator.h +++ b/impeller/renderer/allocator.h @@ -10,7 +10,7 @@ #include "flutter/fml/macros.h" #include "flutter/fml/mapping.h" -#include "impeller/compositor/texture_descriptor.h" +#include "impeller/renderer/texture_descriptor.h" namespace impeller { diff --git a/impeller/compositor/allocator.mm b/impeller/renderer/allocator.mm similarity index 94% rename from impeller/compositor/allocator.mm rename to impeller/renderer/allocator.mm index 99cb0c1c1e1bc..c409871a371e1 100644 --- a/impeller/compositor/allocator.mm +++ b/impeller/renderer/allocator.mm @@ -2,13 +2,13 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -#include "impeller/compositor/allocator.h" +#include "impeller/renderer/allocator.h" #include "flutter/fml/build_config.h" #include "flutter/fml/logging.h" -#include "impeller/compositor/buffer.h" -#include "impeller/compositor/device_buffer.h" -#include "impeller/compositor/formats_metal.h" +#include "impeller/renderer/buffer.h" +#include "impeller/renderer/device_buffer.h" +#include "impeller/renderer/formats_metal.h" namespace impeller { diff --git a/impeller/compositor/buffer.h b/impeller/renderer/buffer.h similarity index 100% rename from impeller/compositor/buffer.h rename to impeller/renderer/buffer.h diff --git a/impeller/compositor/buffer.mm b/impeller/renderer/buffer.mm similarity index 85% rename from impeller/compositor/buffer.mm rename to impeller/renderer/buffer.mm index fe8f4a860f922..1b0d524463dce 100644 --- a/impeller/compositor/buffer.mm +++ b/impeller/renderer/buffer.mm @@ -2,7 +2,7 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -#include "impeller/compositor/buffer.h" +#include "impeller/renderer/buffer.h" namespace impeller { diff --git a/impeller/compositor/buffer_view.h b/impeller/renderer/buffer_view.h similarity index 83% rename from impeller/compositor/buffer_view.h rename to impeller/renderer/buffer_view.h index 835d8e134ffe7..eb70e1312a6bf 100644 --- a/impeller/compositor/buffer_view.h +++ b/impeller/renderer/buffer_view.h @@ -5,8 +5,8 @@ #pragma once #include "flutter/fml/macros.h" -#include "impeller/compositor/buffer.h" -#include "impeller/compositor/range.h" +#include "impeller/renderer/buffer.h" +#include "impeller/renderer/range.h" namespace impeller { diff --git a/impeller/compositor/buffer_view.mm b/impeller/renderer/buffer_view.mm similarity index 82% rename from impeller/compositor/buffer_view.mm rename to impeller/renderer/buffer_view.mm index c7962504f638b..a52f369c8dc8a 100644 --- a/impeller/compositor/buffer_view.mm +++ b/impeller/renderer/buffer_view.mm @@ -2,7 +2,7 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -#include "impeller/compositor/buffer_view.h" +#include "impeller/renderer/buffer_view.h" namespace impeller { diff --git a/impeller/compositor/command.h b/impeller/renderer/command.h similarity index 85% rename from impeller/compositor/command.h rename to impeller/renderer/command.h index e03fd955f4e54..0b9ed1ffe60ac 100644 --- a/impeller/compositor/command.h +++ b/impeller/renderer/command.h @@ -10,13 +10,13 @@ #include "flutter/fml/logging.h" #include "flutter/fml/macros.h" -#include "impeller/compositor/buffer_view.h" -#include "impeller/compositor/formats.h" -#include "impeller/compositor/pipeline.h" -#include "impeller/compositor/sampler.h" -#include "impeller/compositor/shader_types.h" -#include "impeller/compositor/texture.h" -#include "impeller/compositor/vertex_buffer.h" +#include "impeller/renderer/buffer_view.h" +#include "impeller/renderer/formats.h" +#include "impeller/renderer/pipeline.h" +#include "impeller/renderer/sampler.h" +#include "impeller/renderer/shader_types.h" +#include "impeller/renderer/texture.h" +#include "impeller/renderer/vertex_buffer.h" namespace impeller { diff --git a/impeller/compositor/command.mm b/impeller/renderer/command.mm similarity index 96% rename from impeller/compositor/command.mm rename to impeller/renderer/command.mm index bc7427eed574e..e4a457b0438f1 100644 --- a/impeller/compositor/command.mm +++ b/impeller/renderer/command.mm @@ -2,11 +2,11 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -#include "impeller/compositor/command.h" +#include "impeller/renderer/command.h" #include -#include "impeller/compositor/vertex_descriptor.h" +#include "impeller/renderer/vertex_descriptor.h" namespace impeller { diff --git a/impeller/compositor/command_buffer.h b/impeller/renderer/command_buffer.h similarity index 95% rename from impeller/compositor/command_buffer.h rename to impeller/renderer/command_buffer.h index 3233312ef1343..7c7c58eb27ef7 100644 --- a/impeller/compositor/command_buffer.h +++ b/impeller/renderer/command_buffer.h @@ -10,7 +10,7 @@ #include #include "flutter/fml/macros.h" -#include "impeller/compositor/render_pass.h" +#include "impeller/renderer/render_pass.h" namespace impeller { diff --git a/impeller/compositor/command_buffer.mm b/impeller/renderer/command_buffer.mm similarity index 96% rename from impeller/compositor/command_buffer.mm rename to impeller/renderer/command_buffer.mm index c636bab2ca8b9..43d3f8afe81c9 100644 --- a/impeller/compositor/command_buffer.mm +++ b/impeller/renderer/command_buffer.mm @@ -2,7 +2,7 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -#include "impeller/compositor/command_buffer.h" +#include "impeller/renderer/command_buffer.h" namespace impeller { diff --git a/impeller/compositor/comparable.cc b/impeller/renderer/comparable.cc similarity index 86% rename from impeller/compositor/comparable.cc rename to impeller/renderer/comparable.cc index f58bdf6b8c6b6..aac17598ac657 100644 --- a/impeller/compositor/comparable.cc +++ b/impeller/renderer/comparable.cc @@ -2,7 +2,7 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -#include "impeller/compositor/comparable.h" +#include "impeller/renderer/comparable.h" namespace impeller { diff --git a/impeller/compositor/comparable.h b/impeller/renderer/comparable.h similarity index 100% rename from impeller/compositor/comparable.h rename to impeller/renderer/comparable.h diff --git a/impeller/compositor/context.h b/impeller/renderer/context.h similarity index 100% rename from impeller/compositor/context.h rename to impeller/renderer/context.h diff --git a/impeller/compositor/context.mm b/impeller/renderer/context.mm similarity index 93% rename from impeller/compositor/context.mm rename to impeller/renderer/context.mm index c9d1891e8f83e..b1203398b11e3 100644 --- a/impeller/compositor/context.mm +++ b/impeller/renderer/context.mm @@ -7,11 +7,11 @@ #include "flutter/fml/file.h" #include "flutter/fml/logging.h" #include "flutter/fml/paths.h" -#include "impeller/compositor/allocator.h" -#include "impeller/compositor/command_buffer.h" -#include "impeller/compositor/pipeline_library.h" -#include "impeller/compositor/sampler_descriptor.h" -#include "impeller/compositor/shader_library.h" +#include "impeller/renderer/allocator.h" +#include "impeller/renderer/command_buffer.h" +#include "impeller/renderer/pipeline_library.h" +#include "impeller/renderer/sampler_descriptor.h" +#include "impeller/renderer/shader_library.h" namespace impeller { diff --git a/impeller/compositor/device_buffer.h b/impeller/renderer/device_buffer.h similarity index 90% rename from impeller/compositor/device_buffer.h rename to impeller/renderer/device_buffer.h index 6918d04dbbb99..91e2a385b5875 100644 --- a/impeller/compositor/device_buffer.h +++ b/impeller/renderer/device_buffer.h @@ -10,11 +10,11 @@ #include #include "flutter/fml/macros.h" -#include "impeller/compositor/allocator.h" -#include "impeller/compositor/buffer.h" -#include "impeller/compositor/buffer_view.h" -#include "impeller/compositor/range.h" -#include "impeller/compositor/texture.h" +#include "impeller/renderer/allocator.h" +#include "impeller/renderer/buffer.h" +#include "impeller/renderer/buffer_view.h" +#include "impeller/renderer/range.h" +#include "impeller/renderer/texture.h" namespace impeller { diff --git a/impeller/compositor/device_buffer.mm b/impeller/renderer/device_buffer.mm similarity index 96% rename from impeller/compositor/device_buffer.mm rename to impeller/renderer/device_buffer.mm index ceb8c17f7de65..4bc26264c00e4 100644 --- a/impeller/compositor/device_buffer.mm +++ b/impeller/renderer/device_buffer.mm @@ -2,10 +2,10 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -#include "impeller/compositor/device_buffer.h" +#include "impeller/renderer/device_buffer.h" #include "flutter/fml/logging.h" -#include "impeller/compositor/formats_metal.h" +#include "impeller/renderer/formats_metal.h" namespace impeller { diff --git a/impeller/compositor/device_buffer_unittests.mm b/impeller/renderer/device_buffer_unittests.mm similarity index 88% rename from impeller/compositor/device_buffer_unittests.mm rename to impeller/renderer/device_buffer_unittests.mm index a502b36f56555..49c6e626ed8b0 100644 --- a/impeller/compositor/device_buffer_unittests.mm +++ b/impeller/renderer/device_buffer_unittests.mm @@ -3,8 +3,8 @@ // found in the LICENSE file. #include "flutter/testing/testing.h" -#include "impeller/compositor/device_buffer.h" #include "impeller/playground/playground.h" +#include "impeller/renderer/device_buffer.h" namespace impeller { namespace testing { diff --git a/impeller/compositor/formats.cc b/impeller/renderer/formats.cc similarity index 84% rename from impeller/compositor/formats.cc rename to impeller/renderer/formats.cc index c992bc4abc566..6d3d7cea8afc7 100644 --- a/impeller/compositor/formats.cc +++ b/impeller/renderer/formats.cc @@ -2,7 +2,7 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -#include "impeller/compositor/formats.h" +#include "impeller/renderer/formats.h" namespace impeller { diff --git a/impeller/compositor/formats.h b/impeller/renderer/formats.h similarity index 100% rename from impeller/compositor/formats.h rename to impeller/renderer/formats.h diff --git a/impeller/compositor/formats_metal.h b/impeller/renderer/formats_metal.h similarity index 98% rename from impeller/compositor/formats_metal.h rename to impeller/renderer/formats_metal.h index f82bbbeee3bf0..10e536e31f07b 100644 --- a/impeller/compositor/formats_metal.h +++ b/impeller/renderer/formats_metal.h @@ -9,9 +9,9 @@ #include #include "flutter/fml/macros.h" -#include "impeller/compositor/formats.h" -#include "impeller/compositor/texture_descriptor.h" #include "impeller/geometry/color.h" +#include "impeller/renderer/formats.h" +#include "impeller/renderer/texture_descriptor.h" namespace impeller { diff --git a/impeller/compositor/formats_metal.mm b/impeller/renderer/formats_metal.mm similarity index 97% rename from impeller/compositor/formats_metal.mm rename to impeller/renderer/formats_metal.mm index b3b7b472a181d..ba23493979dbc 100644 --- a/impeller/compositor/formats_metal.mm +++ b/impeller/renderer/formats_metal.mm @@ -2,11 +2,11 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -#include "impeller/compositor/formats_metal.h" +#include "impeller/renderer/formats_metal.h" #include -#include "impeller/compositor/render_pass.h" +#include "impeller/renderer/render_pass.h" namespace impeller { diff --git a/impeller/compositor/host_buffer.h b/impeller/renderer/host_buffer.h similarity index 93% rename from impeller/compositor/host_buffer.h rename to impeller/renderer/host_buffer.h index 0f61453683278..7edb747b7e556 100644 --- a/impeller/compositor/host_buffer.h +++ b/impeller/renderer/host_buffer.h @@ -11,9 +11,9 @@ #include "flutter/fml/macros.h" #include "impeller/base/allocation.h" -#include "impeller/compositor/buffer.h" -#include "impeller/compositor/buffer_view.h" -#include "impeller/compositor/platform.h" +#include "impeller/renderer/buffer.h" +#include "impeller/renderer/buffer_view.h" +#include "impeller/renderer/platform.h" namespace impeller { diff --git a/impeller/compositor/host_buffer.mm b/impeller/renderer/host_buffer.mm similarity index 90% rename from impeller/compositor/host_buffer.mm rename to impeller/renderer/host_buffer.mm index 25e1724e4f2a0..44d162129864e 100644 --- a/impeller/compositor/host_buffer.mm +++ b/impeller/renderer/host_buffer.mm @@ -2,15 +2,15 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -#include "impeller/compositor/host_buffer.h" +#include "impeller/renderer/host_buffer.h" #include #include "flutter/fml/logging.h" -#include "impeller/compositor/allocator.h" -#include "impeller/compositor/buffer_view.h" -#include "impeller/compositor/device_buffer.h" +#include "impeller/renderer/allocator.h" +#include "impeller/renderer/buffer_view.h" +#include "impeller/renderer/device_buffer.h" namespace impeller { diff --git a/impeller/compositor/host_buffer_unittests.mm b/impeller/renderer/host_buffer_unittests.mm similarity index 97% rename from impeller/compositor/host_buffer_unittests.mm rename to impeller/renderer/host_buffer_unittests.mm index 5b678a29bebc3..8de2339ff3738 100644 --- a/impeller/compositor/host_buffer_unittests.mm +++ b/impeller/renderer/host_buffer_unittests.mm @@ -3,7 +3,7 @@ // found in the LICENSE file. #include "flutter/testing/testing.h" -#include "impeller/compositor/host_buffer.h" +#include "impeller/renderer/host_buffer.h" namespace impeller { namespace testing { diff --git a/impeller/compositor/pipeline.h b/impeller/renderer/pipeline.h similarity index 95% rename from impeller/compositor/pipeline.h rename to impeller/renderer/pipeline.h index e0f1ab5b0df3f..1c979e17bc857 100644 --- a/impeller/compositor/pipeline.h +++ b/impeller/renderer/pipeline.h @@ -9,8 +9,8 @@ #include #include "flutter/fml/macros.h" -#include "impeller/compositor/context.h" -#include "impeller/compositor/pipeline_builder.h" +#include "impeller/renderer/context.h" +#include "impeller/renderer/pipeline_builder.h" namespace impeller { diff --git a/impeller/compositor/pipeline.mm b/impeller/renderer/pipeline.mm similarity index 89% rename from impeller/compositor/pipeline.mm rename to impeller/renderer/pipeline.mm index 7a60c55a021d9..a5fc33aa93775 100644 --- a/impeller/compositor/pipeline.mm +++ b/impeller/renderer/pipeline.mm @@ -2,10 +2,10 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -#include "impeller/compositor/pipeline.h" +#include "impeller/renderer/pipeline.h" -#include "impeller/compositor/context.h" -#include "impeller/compositor/pipeline_library.h" +#include "impeller/renderer/context.h" +#include "impeller/renderer/pipeline_library.h" namespace impeller { diff --git a/impeller/compositor/pipeline_builder.h b/impeller/renderer/pipeline_builder.h similarity index 95% rename from impeller/compositor/pipeline_builder.h rename to impeller/renderer/pipeline_builder.h index ec67662805e2e..f67e76017c1a6 100644 --- a/impeller/compositor/pipeline_builder.h +++ b/impeller/renderer/pipeline_builder.h @@ -7,10 +7,10 @@ #include "flutter/fml/logging.h" #include "flutter/fml/macros.h" #include "impeller/base/base.h" -#include "impeller/compositor/context.h" -#include "impeller/compositor/pipeline_descriptor.h" -#include "impeller/compositor/shader_library.h" -#include "impeller/compositor/vertex_descriptor.h" +#include "impeller/renderer/context.h" +#include "impeller/renderer/pipeline_descriptor.h" +#include "impeller/renderer/shader_library.h" +#include "impeller/renderer/vertex_descriptor.h" namespace impeller { diff --git a/impeller/compositor/pipeline_builder.mm b/impeller/renderer/pipeline_builder.mm similarity index 81% rename from impeller/compositor/pipeline_builder.mm rename to impeller/renderer/pipeline_builder.mm index dbea9cc563015..79efa7bf3855b 100644 --- a/impeller/compositor/pipeline_builder.mm +++ b/impeller/renderer/pipeline_builder.mm @@ -2,7 +2,7 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -#include "impeller/compositor/pipeline_builder.h" +#include "impeller/renderer/pipeline_builder.h" namespace impeller { diff --git a/impeller/compositor/pipeline_descriptor.h b/impeller/renderer/pipeline_descriptor.h similarity index 95% rename from impeller/compositor/pipeline_descriptor.h rename to impeller/renderer/pipeline_descriptor.h index 1c297f7c327d3..840a074a5fb47 100644 --- a/impeller/compositor/pipeline_descriptor.h +++ b/impeller/renderer/pipeline_descriptor.h @@ -15,9 +15,9 @@ #include "flutter/fml/hash_combine.h" #include "flutter/fml/macros.h" -#include "impeller/compositor/comparable.h" -#include "impeller/compositor/formats.h" -#include "impeller/compositor/shader_types.h" +#include "impeller/renderer/comparable.h" +#include "impeller/renderer/formats.h" +#include "impeller/renderer/shader_types.h" namespace impeller { diff --git a/impeller/compositor/pipeline_descriptor.mm b/impeller/renderer/pipeline_descriptor.mm similarity index 96% rename from impeller/compositor/pipeline_descriptor.mm rename to impeller/renderer/pipeline_descriptor.mm index 9713ee8540739..fee3a9faa0c81 100644 --- a/impeller/compositor/pipeline_descriptor.mm +++ b/impeller/renderer/pipeline_descriptor.mm @@ -2,11 +2,11 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -#include "impeller/compositor/pipeline_descriptor.h" +#include "impeller/renderer/pipeline_descriptor.h" -#include "impeller/compositor/formats_metal.h" -#include "impeller/compositor/shader_library.h" -#include "impeller/compositor/vertex_descriptor.h" +#include "impeller/renderer/formats_metal.h" +#include "impeller/renderer/shader_library.h" +#include "impeller/renderer/vertex_descriptor.h" namespace impeller { diff --git a/impeller/compositor/pipeline_library.h b/impeller/renderer/pipeline_library.h similarity index 92% rename from impeller/compositor/pipeline_library.h rename to impeller/renderer/pipeline_library.h index 5e72c18b99420..b161b2e8d82f2 100644 --- a/impeller/compositor/pipeline_library.h +++ b/impeller/renderer/pipeline_library.h @@ -10,8 +10,8 @@ #include #include "flutter/fml/macros.h" -#include "impeller/compositor/pipeline.h" -#include "impeller/compositor/pipeline_descriptor.h" +#include "impeller/renderer/pipeline.h" +#include "impeller/renderer/pipeline_descriptor.h" namespace impeller { diff --git a/impeller/compositor/pipeline_library.mm b/impeller/renderer/pipeline_library.mm similarity index 97% rename from impeller/compositor/pipeline_library.mm rename to impeller/renderer/pipeline_library.mm index d799ce7034ceb..931e9428c9398 100644 --- a/impeller/compositor/pipeline_library.mm +++ b/impeller/renderer/pipeline_library.mm @@ -2,7 +2,7 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -#include "impeller/compositor/pipeline_library.h" +#include "impeller/renderer/pipeline_library.h" #include diff --git a/impeller/compositor/platform.h b/impeller/renderer/platform.h similarity index 100% rename from impeller/compositor/platform.h rename to impeller/renderer/platform.h diff --git a/impeller/compositor/platform.mm b/impeller/renderer/platform.mm similarity index 83% rename from impeller/compositor/platform.mm rename to impeller/renderer/platform.mm index 1ce3def0ad608..7f225f3d3dbbc 100644 --- a/impeller/compositor/platform.mm +++ b/impeller/renderer/platform.mm @@ -2,7 +2,7 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -#include "impeller/compositor/platform.h" +#include "impeller/renderer/platform.h" namespace impeller { diff --git a/impeller/compositor/range.cc b/impeller/renderer/range.cc similarity index 84% rename from impeller/compositor/range.cc rename to impeller/renderer/range.cc index a6fe921a10180..14d7c001a10b9 100644 --- a/impeller/compositor/range.cc +++ b/impeller/renderer/range.cc @@ -2,7 +2,7 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -#include "impeller/compositor/range.h" +#include "impeller/renderer/range.h" namespace impeller { diff --git a/impeller/compositor/range.h b/impeller/renderer/range.h similarity index 100% rename from impeller/compositor/range.h rename to impeller/renderer/range.h diff --git a/impeller/compositor/render_pass.h b/impeller/renderer/render_pass.h similarity index 88% rename from impeller/compositor/render_pass.h rename to impeller/renderer/render_pass.h index bf1129d9866fa..289bd182c7d73 100644 --- a/impeller/compositor/render_pass.h +++ b/impeller/renderer/render_pass.h @@ -10,13 +10,13 @@ #include #include "flutter/fml/macros.h" -#include "impeller/compositor/command.h" -#include "impeller/compositor/formats.h" -#include "impeller/compositor/host_buffer.h" -#include "impeller/compositor/render_pass_descriptor.h" -#include "impeller/compositor/texture.h" #include "impeller/geometry/color.h" #include "impeller/geometry/size.h" +#include "impeller/renderer/command.h" +#include "impeller/renderer/formats.h" +#include "impeller/renderer/host_buffer.h" +#include "impeller/renderer/render_pass_descriptor.h" +#include "impeller/renderer/texture.h" namespace impeller { diff --git a/impeller/compositor/render_pass.mm b/impeller/renderer/render_pass.mm similarity index 98% rename from impeller/compositor/render_pass.mm rename to impeller/renderer/render_pass.mm index bcbbb36fbc016..a136864b85e0d 100644 --- a/impeller/compositor/render_pass.mm +++ b/impeller/renderer/render_pass.mm @@ -2,7 +2,7 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -#include "impeller/compositor/render_pass.h" +#include "impeller/renderer/render_pass.h" #include #include @@ -10,10 +10,10 @@ #include "flutter/fml/closure.h" #include "flutter/fml/logging.h" #include "impeller/base/base.h" -#include "impeller/compositor/device_buffer.h" -#include "impeller/compositor/formats_metal.h" -#include "impeller/compositor/sampler.h" -#include "impeller/compositor/shader_types.h" +#include "impeller/renderer/device_buffer.h" +#include "impeller/renderer/formats_metal.h" +#include "impeller/renderer/sampler.h" +#include "impeller/renderer/shader_types.h" namespace impeller { diff --git a/impeller/compositor/render_pass_descriptor.h b/impeller/renderer/render_pass_descriptor.h similarity index 96% rename from impeller/compositor/render_pass_descriptor.h rename to impeller/renderer/render_pass_descriptor.h index a8a3fe93c94b6..1c68781f0ea38 100644 --- a/impeller/compositor/render_pass_descriptor.h +++ b/impeller/renderer/render_pass_descriptor.h @@ -10,8 +10,8 @@ #include #include "flutter/fml/macros.h" -#include "impeller/compositor/formats.h" #include "impeller/geometry/size.h" +#include "impeller/renderer/formats.h" namespace impeller { diff --git a/impeller/compositor/render_pass_descriptor.mm b/impeller/renderer/render_pass_descriptor.mm similarity index 93% rename from impeller/compositor/render_pass_descriptor.mm rename to impeller/renderer/render_pass_descriptor.mm index a39523e2828ce..b267bc40f05f1 100644 --- a/impeller/compositor/render_pass_descriptor.mm +++ b/impeller/renderer/render_pass_descriptor.mm @@ -2,9 +2,9 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -#include "impeller/compositor/render_pass_descriptor.h" +#include "impeller/renderer/render_pass_descriptor.h" -#include "impeller/compositor/texture.h" +#include "impeller/renderer/texture.h" namespace impeller { diff --git a/impeller/compositor/renderer.h b/impeller/renderer/renderer.h similarity index 95% rename from impeller/compositor/renderer.h rename to impeller/renderer/renderer.h index 89b95c108b18f..c3c8b56f3bb85 100644 --- a/impeller/compositor/renderer.h +++ b/impeller/renderer/renderer.h @@ -10,8 +10,8 @@ #include #include "flutter/fml/macros.h" -#include "impeller/compositor/context.h" #include "impeller/geometry/size.h" +#include "impeller/renderer/context.h" namespace impeller { diff --git a/impeller/compositor/renderer.mm b/impeller/renderer/renderer.mm similarity index 93% rename from impeller/compositor/renderer.mm rename to impeller/renderer/renderer.mm index 0bed94141f72c..43a38e8f354d7 100644 --- a/impeller/compositor/renderer.mm +++ b/impeller/renderer/renderer.mm @@ -2,11 +2,11 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -#include "impeller/compositor/renderer.h" +#include "impeller/renderer/renderer.h" #include "flutter/fml/logging.h" -#include "impeller/compositor/command_buffer.h" -#include "impeller/compositor/surface.h" +#include "impeller/renderer/command_buffer.h" +#include "impeller/renderer/surface.h" namespace impeller { diff --git a/impeller/compositor/compositor_unittests.mm b/impeller/renderer/renderer_unittests.mm similarity index 96% rename from impeller/compositor/compositor_unittests.mm rename to impeller/renderer/renderer_unittests.mm index da028490b1865..f037087f3d8e6 100644 --- a/impeller/compositor/compositor_unittests.mm +++ b/impeller/renderer/renderer_unittests.mm @@ -6,19 +6,19 @@ #include "flutter/impeller/fixtures/box_fade.frag.h" #include "flutter/impeller/fixtures/box_fade.vert.h" #include "flutter/testing/testing.h" -#include "impeller/compositor/command.h" -#include "impeller/compositor/command_buffer.h" -#include "impeller/compositor/pipeline_builder.h" -#include "impeller/compositor/pipeline_library.h" -#include "impeller/compositor/renderer.h" -#include "impeller/compositor/sampler_descriptor.h" -#include "impeller/compositor/surface.h" -#include "impeller/compositor/tessellator.h" -#include "impeller/compositor/vertex_buffer_builder.h" #include "impeller/geometry/path_builder.h" #include "impeller/image/compressed_image.h" #include "impeller/image/decompressed_image.h" #include "impeller/playground/playground.h" +#include "impeller/renderer/command.h" +#include "impeller/renderer/command_buffer.h" +#include "impeller/renderer/pipeline_builder.h" +#include "impeller/renderer/pipeline_library.h" +#include "impeller/renderer/renderer.h" +#include "impeller/renderer/sampler_descriptor.h" +#include "impeller/renderer/surface.h" +#include "impeller/renderer/tessellator.h" +#include "impeller/renderer/vertex_buffer_builder.h" namespace impeller { namespace testing { diff --git a/impeller/compositor/sampler.h b/impeller/renderer/sampler.h similarity index 100% rename from impeller/compositor/sampler.h rename to impeller/renderer/sampler.h diff --git a/impeller/compositor/sampler.mm b/impeller/renderer/sampler.mm similarity index 91% rename from impeller/compositor/sampler.mm rename to impeller/renderer/sampler.mm index 26f09d0fbe799..63e4a7404cc17 100644 --- a/impeller/compositor/sampler.mm +++ b/impeller/renderer/sampler.mm @@ -2,7 +2,7 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -#include "impeller/compositor/sampler.h" +#include "impeller/renderer/sampler.h" namespace impeller { diff --git a/impeller/compositor/sampler_descriptor.h b/impeller/renderer/sampler_descriptor.h similarity index 95% rename from impeller/compositor/sampler_descriptor.h rename to impeller/renderer/sampler_descriptor.h index b07bacfe82a49..ab1d169884d97 100644 --- a/impeller/compositor/sampler_descriptor.h +++ b/impeller/renderer/sampler_descriptor.h @@ -9,8 +9,8 @@ #include #include "flutter/fml/macros.h" -#include "impeller/compositor/comparable.h" -#include "impeller/compositor/formats.h" +#include "impeller/renderer/comparable.h" +#include "impeller/renderer/formats.h" namespace impeller { diff --git a/impeller/compositor/sampler_descriptor.mm b/impeller/renderer/sampler_descriptor.mm similarity index 90% rename from impeller/compositor/sampler_descriptor.mm rename to impeller/renderer/sampler_descriptor.mm index b59dfae8908f7..72d7509c2ccaf 100644 --- a/impeller/compositor/sampler_descriptor.mm +++ b/impeller/renderer/sampler_descriptor.mm @@ -2,10 +2,10 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -#include "impeller/compositor/sampler_descriptor.h" +#include "impeller/renderer/sampler_descriptor.h" -#include "impeller/compositor/formats_metal.h" -#include "impeller/compositor/sampler.h" +#include "impeller/renderer/formats_metal.h" +#include "impeller/renderer/sampler.h" namespace impeller { diff --git a/impeller/compositor/shader_function.h b/impeller/renderer/shader_function.h similarity index 92% rename from impeller/compositor/shader_function.h rename to impeller/renderer/shader_function.h index f3230a72bde14..04e4f573b939f 100644 --- a/impeller/compositor/shader_function.h +++ b/impeller/renderer/shader_function.h @@ -8,8 +8,8 @@ #include "flutter/fml/hash_combine.h" #include "flutter/fml/macros.h" -#include "impeller/compositor/comparable.h" -#include "impeller/compositor/shader_types.h" +#include "impeller/renderer/comparable.h" +#include "impeller/renderer/shader_types.h" namespace impeller { diff --git a/impeller/compositor/shader_function.mm b/impeller/renderer/shader_function.mm similarity index 95% rename from impeller/compositor/shader_function.mm rename to impeller/renderer/shader_function.mm index 58c9d65d87622..bed9c729fd212 100644 --- a/impeller/compositor/shader_function.mm +++ b/impeller/renderer/shader_function.mm @@ -2,7 +2,7 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -#include "impeller/compositor/shader_function.h" +#include "impeller/renderer/shader_function.h" namespace impeller { diff --git a/impeller/compositor/shader_library.h b/impeller/renderer/shader_library.h similarity index 94% rename from impeller/compositor/shader_library.h rename to impeller/renderer/shader_library.h index 4a9b76c4e19a9..4d28a690ac7d6 100644 --- a/impeller/compositor/shader_library.h +++ b/impeller/renderer/shader_library.h @@ -12,8 +12,8 @@ #include #include "flutter/fml/macros.h" -#include "impeller/compositor/comparable.h" -#include "impeller/compositor/shader_function.h" +#include "impeller/renderer/comparable.h" +#include "impeller/renderer/shader_function.h" namespace impeller { diff --git a/impeller/compositor/shader_library.mm b/impeller/renderer/shader_library.mm similarity index 95% rename from impeller/compositor/shader_library.mm rename to impeller/renderer/shader_library.mm index 65db43c6ea22a..baeb0fac0a2fe 100644 --- a/impeller/compositor/shader_library.mm +++ b/impeller/renderer/shader_library.mm @@ -2,7 +2,7 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -#include "impeller/compositor/shader_library.h" +#include "impeller/renderer/shader_library.h" namespace impeller { diff --git a/impeller/compositor/shader_types.cc b/impeller/renderer/shader_types.cc similarity index 82% rename from impeller/compositor/shader_types.cc rename to impeller/renderer/shader_types.cc index 7cc75876667b7..f49b27cc45e68 100644 --- a/impeller/compositor/shader_types.cc +++ b/impeller/renderer/shader_types.cc @@ -2,7 +2,7 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -#include "impeller/compositor/shader_types.h" +#include "impeller/renderer/shader_types.h" namespace impeller { diff --git a/impeller/compositor/shader_types.h b/impeller/renderer/shader_types.h similarity index 100% rename from impeller/compositor/shader_types.h rename to impeller/renderer/shader_types.h diff --git a/impeller/compositor/surface.h b/impeller/renderer/surface.h similarity index 88% rename from impeller/compositor/surface.h rename to impeller/renderer/surface.h index b71b209602d85..231d89e424a0e 100644 --- a/impeller/compositor/surface.h +++ b/impeller/renderer/surface.h @@ -10,8 +10,8 @@ #include #include "flutter/fml/macros.h" -#include "impeller/compositor/context.h" -#include "impeller/compositor/render_pass.h" +#include "impeller/renderer/context.h" +#include "impeller/renderer/render_pass.h" namespace impeller { diff --git a/impeller/compositor/surface.mm b/impeller/renderer/surface.mm similarity index 94% rename from impeller/compositor/surface.mm rename to impeller/renderer/surface.mm index 6e16d21cf2552..4c699497956a9 100644 --- a/impeller/compositor/surface.mm +++ b/impeller/renderer/surface.mm @@ -2,7 +2,7 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -#include "impeller/compositor/surface.h" +#include "impeller/renderer/surface.h" #include "flutter/fml/logging.h" diff --git a/impeller/compositor/tessellator.cc b/impeller/renderer/tessellator.cc similarity index 98% rename from impeller/compositor/tessellator.cc rename to impeller/renderer/tessellator.cc index 66bb326f1ade4..3f7960f7f4ddf 100644 --- a/impeller/compositor/tessellator.cc +++ b/impeller/renderer/tessellator.cc @@ -2,7 +2,7 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -#include "impeller/compositor/tessellator.h" +#include "impeller/renderer/tessellator.h" #include "third_party/libtess2/Include/tesselator.h" diff --git a/impeller/compositor/tessellator.h b/impeller/renderer/tessellator.h similarity index 95% rename from impeller/compositor/tessellator.h rename to impeller/renderer/tessellator.h index 118bc1bdd162e..fcc04dff68ce6 100644 --- a/impeller/compositor/tessellator.h +++ b/impeller/renderer/tessellator.h @@ -8,8 +8,8 @@ #include #include "flutter/fml/macros.h" -#include "impeller/compositor/formats.h" #include "impeller/geometry/point.h" +#include "impeller/renderer/formats.h" namespace impeller { diff --git a/impeller/compositor/texture.h b/impeller/renderer/texture.h similarity index 89% rename from impeller/compositor/texture.h rename to impeller/renderer/texture.h index e9b9241909f57..d3067498e61e7 100644 --- a/impeller/compositor/texture.h +++ b/impeller/renderer/texture.h @@ -9,9 +9,9 @@ #include #include "flutter/fml/macros.h" -#include "impeller/compositor/formats.h" -#include "impeller/compositor/texture_descriptor.h" #include "impeller/geometry/size.h" +#include "impeller/renderer/formats.h" +#include "impeller/renderer/texture_descriptor.h" namespace impeller { diff --git a/impeller/compositor/texture.mm b/impeller/renderer/texture.mm similarity index 97% rename from impeller/compositor/texture.mm rename to impeller/renderer/texture.mm index f9f71d581ce08..591ceb1202422 100644 --- a/impeller/compositor/texture.mm +++ b/impeller/renderer/texture.mm @@ -2,7 +2,7 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -#include "impeller/compositor/texture.h" +#include "impeller/renderer/texture.h" namespace impeller { diff --git a/impeller/compositor/texture_descriptor.h b/impeller/renderer/texture_descriptor.h similarity index 96% rename from impeller/compositor/texture_descriptor.h rename to impeller/renderer/texture_descriptor.h index c5ba247664aab..dfaaab36cc7c5 100644 --- a/impeller/compositor/texture_descriptor.h +++ b/impeller/renderer/texture_descriptor.h @@ -6,9 +6,9 @@ #include -#include "impeller/compositor/formats.h" #include "impeller/geometry/size.h" #include "impeller/image/decompressed_image.h" +#include "impeller/renderer/formats.h" namespace impeller { diff --git a/impeller/compositor/texture_descriptor.mm b/impeller/renderer/texture_descriptor.mm similarity index 80% rename from impeller/compositor/texture_descriptor.mm rename to impeller/renderer/texture_descriptor.mm index c72e4dfd022c4..190f4482ef136 100644 --- a/impeller/compositor/texture_descriptor.mm +++ b/impeller/renderer/texture_descriptor.mm @@ -2,7 +2,7 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -#include "impeller/compositor/texture_descriptor.h" +#include "impeller/renderer/texture_descriptor.h" namespace impeller { diff --git a/impeller/compositor/vertex_buffer.h b/impeller/renderer/vertex_buffer.h similarity index 91% rename from impeller/compositor/vertex_buffer.h rename to impeller/renderer/vertex_buffer.h index be29c5f3d194c..596731fa72bc0 100644 --- a/impeller/compositor/vertex_buffer.h +++ b/impeller/renderer/vertex_buffer.h @@ -4,7 +4,7 @@ #pragma once -#include "impeller/compositor/buffer_view.h" +#include "impeller/renderer/buffer_view.h" namespace impeller { diff --git a/impeller/compositor/vertex_buffer.mm b/impeller/renderer/vertex_buffer.mm similarity index 82% rename from impeller/compositor/vertex_buffer.mm rename to impeller/renderer/vertex_buffer.mm index 62568a5e197c4..e73f64e533a82 100644 --- a/impeller/compositor/vertex_buffer.mm +++ b/impeller/renderer/vertex_buffer.mm @@ -2,7 +2,7 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -#include "impeller/compositor/vertex_buffer.h" +#include "impeller/renderer/vertex_buffer.h" namespace impeller { diff --git a/impeller/compositor/vertex_buffer_builder.h b/impeller/renderer/vertex_buffer_builder.h similarity index 94% rename from impeller/compositor/vertex_buffer_builder.h rename to impeller/renderer/vertex_buffer_builder.h index 1d5a2669025b8..d9b0e36d34778 100644 --- a/impeller/compositor/vertex_buffer_builder.h +++ b/impeller/renderer/vertex_buffer_builder.h @@ -10,12 +10,12 @@ #include "flutter/fml/macros.h" #include "impeller/base/base.h" -#include "impeller/compositor/allocator.h" -#include "impeller/compositor/device_buffer.h" -#include "impeller/compositor/formats.h" -#include "impeller/compositor/host_buffer.h" -#include "impeller/compositor/vertex_buffer.h" #include "impeller/geometry/vector.h" +#include "impeller/renderer/allocator.h" +#include "impeller/renderer/device_buffer.h" +#include "impeller/renderer/formats.h" +#include "impeller/renderer/host_buffer.h" +#include "impeller/renderer/vertex_buffer.h" namespace impeller { diff --git a/impeller/compositor/vertex_buffer_builder.mm b/impeller/renderer/vertex_buffer_builder.mm similarity index 79% rename from impeller/compositor/vertex_buffer_builder.mm rename to impeller/renderer/vertex_buffer_builder.mm index 0e548006bcc1c..b29e4032132a0 100644 --- a/impeller/compositor/vertex_buffer_builder.mm +++ b/impeller/renderer/vertex_buffer_builder.mm @@ -2,7 +2,7 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -#include "impeller/compositor/vertex_buffer_builder.h" +#include "impeller/renderer/vertex_buffer_builder.h" namespace impeller { diff --git a/impeller/compositor/vertex_descriptor.h b/impeller/renderer/vertex_descriptor.h similarity index 96% rename from impeller/compositor/vertex_descriptor.h rename to impeller/renderer/vertex_descriptor.h index 62ddbe35dba49..26e63be373bcc 100644 --- a/impeller/compositor/vertex_descriptor.h +++ b/impeller/renderer/vertex_descriptor.h @@ -9,8 +9,8 @@ #include #include "flutter/fml/macros.h" -#include "impeller/compositor/comparable.h" -#include "impeller/compositor/shader_types.h" +#include "impeller/renderer/comparable.h" +#include "impeller/renderer/shader_types.h" namespace impeller { diff --git a/impeller/compositor/vertex_descriptor.mm b/impeller/renderer/vertex_descriptor.mm similarity index 99% rename from impeller/compositor/vertex_descriptor.mm rename to impeller/renderer/vertex_descriptor.mm index 6a729b4df1e35..55121bcc5fec7 100644 --- a/impeller/compositor/vertex_descriptor.mm +++ b/impeller/renderer/vertex_descriptor.mm @@ -2,7 +2,7 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -#include "impeller/compositor/vertex_descriptor.h" +#include "impeller/renderer/vertex_descriptor.h" #include "flutter/fml/logging.h" diff --git a/impeller/tools/impeller.gni b/impeller/tools/impeller.gni index 4af9af2666f8d..f2310fb5e4e7f 100644 --- a/impeller/tools/impeller.gni +++ b/impeller/tools/impeller.gni @@ -169,7 +169,7 @@ template("impeller_shaders") { deps = [ ":$impellerc_target_name", - "//flutter/impeller/compositor", + "//flutter/impeller/renderer", ] } From d1fe7a0e25eebd0e805db665f13145d8c15800cd Mon Sep 17 00:00:00 2001 From: Chinmay Garde Date: Wed, 20 Oct 2021 11:33:08 -0700 Subject: [PATCH 176/433] Add stubs for backend library. --- impeller/renderer/backend/metal/BUILD.gn | 9 + .../renderer/backend/metal/allocator_mtl.h | 22 ++ .../renderer/backend/metal/allocator_mtl.mm | 0 .../backend/metal/command_buffer_mtl.h | 22 ++ .../backend/metal/command_buffer_mtl.mm | 0 impeller/renderer/backend/metal/context_mtl.h | 22 ++ .../renderer/backend/metal/context_mtl.mm | 0 .../backend/metal/device_buffer_mtl.h | 22 ++ .../backend/metal/device_buffer_mtl.mm | 0 .../renderer/backend/metal/formats_metal.h | 263 ++++++++++++++++++ .../renderer/backend/metal/formats_metal.mm | 102 +++++++ .../backend/metal/pipeline_library_mtl.h | 22 ++ .../backend/metal/pipeline_library_mtl.mm | 0 .../renderer/backend/metal/pipeline_mtl.h | 22 ++ .../renderer/backend/metal/pipeline_mtl.mm | 0 .../renderer/backend/metal/render_pass_mtl.h | 22 ++ .../renderer/backend/metal/render_pass_mtl.mm | 0 .../renderer/backend/metal/renderer_mtl.h | 22 ++ .../renderer/backend/metal/renderer_mtl.mm | 0 impeller/renderer/backend/metal/sampler_mtl.h | 22 ++ .../renderer/backend/metal/sampler_mtl.mm | 0 .../backend/metal/shader_function_mtl.h | 22 ++ .../backend/metal/shader_function_mtl.mm | 0 .../backend/metal/shader_library_mtl.h | 22 ++ .../backend/metal/shader_library_mtl.mm | 0 impeller/renderer/backend/metal/texture_mtl.h | 22 ++ .../renderer/backend/metal/texture_mtl.mm | 0 27 files changed, 638 insertions(+) create mode 100644 impeller/renderer/backend/metal/BUILD.gn create mode 100644 impeller/renderer/backend/metal/allocator_mtl.h create mode 100644 impeller/renderer/backend/metal/allocator_mtl.mm create mode 100644 impeller/renderer/backend/metal/command_buffer_mtl.h create mode 100644 impeller/renderer/backend/metal/command_buffer_mtl.mm create mode 100644 impeller/renderer/backend/metal/context_mtl.h create mode 100644 impeller/renderer/backend/metal/context_mtl.mm create mode 100644 impeller/renderer/backend/metal/device_buffer_mtl.h create mode 100644 impeller/renderer/backend/metal/device_buffer_mtl.mm create mode 100644 impeller/renderer/backend/metal/formats_metal.h create mode 100644 impeller/renderer/backend/metal/formats_metal.mm create mode 100644 impeller/renderer/backend/metal/pipeline_library_mtl.h create mode 100644 impeller/renderer/backend/metal/pipeline_library_mtl.mm create mode 100644 impeller/renderer/backend/metal/pipeline_mtl.h create mode 100644 impeller/renderer/backend/metal/pipeline_mtl.mm create mode 100644 impeller/renderer/backend/metal/render_pass_mtl.h create mode 100644 impeller/renderer/backend/metal/render_pass_mtl.mm create mode 100644 impeller/renderer/backend/metal/renderer_mtl.h create mode 100644 impeller/renderer/backend/metal/renderer_mtl.mm create mode 100644 impeller/renderer/backend/metal/sampler_mtl.h create mode 100644 impeller/renderer/backend/metal/sampler_mtl.mm create mode 100644 impeller/renderer/backend/metal/shader_function_mtl.h create mode 100644 impeller/renderer/backend/metal/shader_function_mtl.mm create mode 100644 impeller/renderer/backend/metal/shader_library_mtl.h create mode 100644 impeller/renderer/backend/metal/shader_library_mtl.mm create mode 100644 impeller/renderer/backend/metal/texture_mtl.h create mode 100644 impeller/renderer/backend/metal/texture_mtl.mm diff --git a/impeller/renderer/backend/metal/BUILD.gn b/impeller/renderer/backend/metal/BUILD.gn new file mode 100644 index 0000000000000..aae582859569c --- /dev/null +++ b/impeller/renderer/backend/metal/BUILD.gn @@ -0,0 +1,9 @@ +# Copyright 2013 The Flutter Authors. All rights reserved. +# Use of this source code is governed by a BSD-style license that can be +# found in the LICENSE file. + +import("//flutter/impeller/tools/impeller.gni") + +impeller_component("metal") { + sources = [] +} diff --git a/impeller/renderer/backend/metal/allocator_mtl.h b/impeller/renderer/backend/metal/allocator_mtl.h new file mode 100644 index 0000000000000..494b6f78a3960 --- /dev/null +++ b/impeller/renderer/backend/metal/allocator_mtl.h @@ -0,0 +1,22 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#pragma once + +#include "flutter/fml/macros.h" +#include "impeller/renderer/allocator.h" + +namespace impeller { + +class AllocatorMTL final : public Allocator { + public: + AllocatorMTL(); + + ~AllocatorMTL() override; + + private: + FML_DISALLOW_COPY_AND_ASSIGN(AllocatorMTL); +}; + +} // namespace impeller diff --git a/impeller/renderer/backend/metal/allocator_mtl.mm b/impeller/renderer/backend/metal/allocator_mtl.mm new file mode 100644 index 0000000000000..e69de29bb2d1d diff --git a/impeller/renderer/backend/metal/command_buffer_mtl.h b/impeller/renderer/backend/metal/command_buffer_mtl.h new file mode 100644 index 0000000000000..2f3f35d424bd0 --- /dev/null +++ b/impeller/renderer/backend/metal/command_buffer_mtl.h @@ -0,0 +1,22 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#pragma once + +#include "flutter/fml/macros.h" +#include "impeller/renderer/command_buffer.h" + +namespace impeller { + +class CommabdBufferMTL final : public CommabdBuffer { + public: + CommabdBufferMTL(); + + ~CommabdBufferMTL() override; + + private: + FML_DISALLOW_COPY_AND_ASSIGN(CommabdBufferMTL); +}; + +} // namespace impeller diff --git a/impeller/renderer/backend/metal/command_buffer_mtl.mm b/impeller/renderer/backend/metal/command_buffer_mtl.mm new file mode 100644 index 0000000000000..e69de29bb2d1d diff --git a/impeller/renderer/backend/metal/context_mtl.h b/impeller/renderer/backend/metal/context_mtl.h new file mode 100644 index 0000000000000..aaba0258b634c --- /dev/null +++ b/impeller/renderer/backend/metal/context_mtl.h @@ -0,0 +1,22 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#pragma once + +#include "flutter/fml/macros.h" +#include "impeller/renderer/context.h" + +namespace impeller { + +class ContextMTL final : public Context { + public: + ContextMTL(); + + ~ContextMTL() override; + + private: + FML_DISALLOW_COPY_AND_ASSIGN(ContextMTL); +}; + +} // namespace impeller diff --git a/impeller/renderer/backend/metal/context_mtl.mm b/impeller/renderer/backend/metal/context_mtl.mm new file mode 100644 index 0000000000000..e69de29bb2d1d diff --git a/impeller/renderer/backend/metal/device_buffer_mtl.h b/impeller/renderer/backend/metal/device_buffer_mtl.h new file mode 100644 index 0000000000000..bc7386c39bb70 --- /dev/null +++ b/impeller/renderer/backend/metal/device_buffer_mtl.h @@ -0,0 +1,22 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#pragma once + +#include "flutter/fml/macros.h" +#include "impeller/renderer/device_buffer.h" + +namespace impeller { + +class DeviceBufferMTL final : public DeviceBuffer { + public: + DeviceBufferMTL(); + + ~DeviceBufferMTL() override; + + private: + FML_DISALLOW_COPY_AND_ASSIGN(DeviceBufferMTL); +}; + +} // namespace impeller diff --git a/impeller/renderer/backend/metal/device_buffer_mtl.mm b/impeller/renderer/backend/metal/device_buffer_mtl.mm new file mode 100644 index 0000000000000..e69de29bb2d1d diff --git a/impeller/renderer/backend/metal/formats_metal.h b/impeller/renderer/backend/metal/formats_metal.h new file mode 100644 index 0000000000000..10e536e31f07b --- /dev/null +++ b/impeller/renderer/backend/metal/formats_metal.h @@ -0,0 +1,263 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#pragma once + +#include + +#include + +#include "flutter/fml/macros.h" +#include "impeller/geometry/color.h" +#include "impeller/renderer/formats.h" +#include "impeller/renderer/texture_descriptor.h" + +namespace impeller { + +class RenderPassDescriptor; + +constexpr MTLPixelFormat ToMTLPixelFormat(PixelFormat format) { + switch (format) { + case PixelFormat::kUnknown: + return MTLPixelFormatInvalid; + case PixelFormat::kB8G8R8A8UNormInt: + return MTLPixelFormatBGRA8Unorm; + case PixelFormat::kB8G8R8A8UNormIntSRGB: + return MTLPixelFormatBGRA8Unorm_sRGB; + case PixelFormat::kD32FloatS8UNormInt: + return MTLPixelFormatDepth32Float_Stencil8; + case PixelFormat::kR8G8B8A8UNormInt: + return MTLPixelFormatRGBA8Unorm; + case PixelFormat::kR8G8B8A8UNormIntSRGB: + return MTLPixelFormatRGBA8Unorm_sRGB; + } + return MTLPixelFormatInvalid; +}; + +constexpr MTLBlendFactor ToMTLBlendFactor(BlendFactor type) { + switch (type) { + case BlendFactor::kZero: + return MTLBlendFactorZero; + case BlendFactor::kOne: + return MTLBlendFactorOne; + case BlendFactor::kSourceColor: + return MTLBlendFactorSourceColor; + case BlendFactor::kOneMinusSourceColor: + return MTLBlendFactorOneMinusSourceColor; + case BlendFactor::kSourceAlpha: + return MTLBlendFactorSourceAlpha; + case BlendFactor::kOneMinusSourceAlpha: + return MTLBlendFactorOneMinusSourceAlpha; + case BlendFactor::kDestinationColor: + return MTLBlendFactorDestinationColor; + case BlendFactor::kOneMinusDestinationColor: + return MTLBlendFactorOneMinusDestinationColor; + case BlendFactor::kDestinationAlpha: + return MTLBlendFactorDestinationAlpha; + case BlendFactor::kOneMinusDestinationAlpha: + return MTLBlendFactorOneMinusDestinationAlpha; + case BlendFactor::kSourceAlphaSaturated: + return MTLBlendFactorSourceAlphaSaturated; + case BlendFactor::kBlendColor: + return MTLBlendFactorBlendColor; + case BlendFactor::kOneMinusBlendColor: + return MTLBlendFactorOneMinusBlendColor; + case BlendFactor::kBlendAlpha: + return MTLBlendFactorBlendAlpha; + case BlendFactor::kOneMinusBlendAlpha: + return MTLBlendFactorOneMinusBlendAlpha; + } + return MTLBlendFactorZero; +}; + +constexpr MTLPrimitiveType ToMTLPrimitiveType(PrimitiveType type) { + switch (type) { + case PrimitiveType::kTriangle: + return MTLPrimitiveTypeTriangle; + case PrimitiveType::kTriangleStrip: + return MTLPrimitiveTypeTriangleStrip; + case PrimitiveType::kLine: + return MTLPrimitiveTypeLine; + case PrimitiveType::kLineStrip: + return MTLPrimitiveTypeLineStrip; + case PrimitiveType::kPoint: + return MTLPrimitiveTypePoint; + } + return MTLPrimitiveTypePoint; +} + +constexpr MTLBlendOperation ToMTLBlendOperation(BlendOperation type) { + switch (type) { + case BlendOperation::kAdd: + return MTLBlendOperationAdd; + case BlendOperation::kSubtract: + return MTLBlendOperationSubtract; + case BlendOperation::kReverseSubtract: + return MTLBlendOperationReverseSubtract; + case BlendOperation::kMin: + return MTLBlendOperationMin; + case BlendOperation::kMax: + return MTLBlendOperationMax; + } + return MTLBlendOperationAdd; +}; + +constexpr MTLColorWriteMask ToMTLColorWriteMask( + std::underlying_type_t type) { + using UnderlyingType = decltype(type); + + MTLColorWriteMask mask = MTLColorWriteMaskNone; + + if (type & static_cast(ColorWriteMask::kRed)) { + mask |= MTLColorWriteMaskRed; + } + + if (type & static_cast(ColorWriteMask::kGreen)) { + mask |= MTLColorWriteMaskGreen; + } + + if (type & static_cast(ColorWriteMask::kBlue)) { + mask |= MTLColorWriteMaskBlue; + } + + if (type & static_cast(ColorWriteMask::kAlpha)) { + mask |= MTLColorWriteMaskAlpha; + } + + return mask; +}; + +constexpr MTLCompareFunction ToMTLCompareFunction(CompareFunction func) { + switch (func) { + case CompareFunction::kNever: + return MTLCompareFunctionNever; + case CompareFunction::kLess: + return MTLCompareFunctionLess; + case CompareFunction::kEqual: + return MTLCompareFunctionEqual; + case CompareFunction::kLessEqual: + return MTLCompareFunctionLessEqual; + case CompareFunction::kGreater: + return MTLCompareFunctionGreater; + case CompareFunction::kNotEqual: + return MTLCompareFunctionNotEqual; + case CompareFunction::kGreaterEqual: + return MTLCompareFunctionGreaterEqual; + case CompareFunction::kAlways: + return MTLCompareFunctionAlways; + } + return MTLCompareFunctionAlways; +}; + +constexpr MTLStencilOperation ToMTLStencilOperation(StencilOperation op) { + switch (op) { + case StencilOperation::kKeep: + return MTLStencilOperationKeep; + case StencilOperation::kZero: + return MTLStencilOperationZero; + case StencilOperation::kSetToReferneceValue: + return MTLStencilOperationReplace; + case StencilOperation::kIncrementClamp: + return MTLStencilOperationIncrementClamp; + case StencilOperation::kDecrementClamp: + return MTLStencilOperationDecrementClamp; + case StencilOperation::kInvert: + return MTLStencilOperationInvert; + case StencilOperation::kIncrementWrap: + return MTLStencilOperationIncrementWrap; + case StencilOperation::kDecrementWrap: + return MTLStencilOperationDecrementWrap; + } + return MTLStencilOperationKeep; +}; + +constexpr MTLLoadAction ToMTLLoadAction(LoadAction action) { + switch (action) { + case LoadAction::kDontCare: + return MTLLoadActionDontCare; + case LoadAction::kLoad: + return MTLLoadActionLoad; + case LoadAction::kClear: + return MTLLoadActionClear; + } + + return MTLLoadActionDontCare; +} + +constexpr LoadAction FromMTLLoadAction(MTLLoadAction action) { + switch (action) { + case MTLLoadActionDontCare: + return LoadAction::kDontCare; + case MTLLoadActionLoad: + return LoadAction::kLoad; + case MTLLoadActionClear: + return LoadAction::kClear; + default: + break; + } + + return LoadAction::kDontCare; +} + +constexpr MTLStoreAction ToMTLStoreAction(StoreAction action) { + switch (action) { + case StoreAction::kDontCare: + return MTLStoreActionDontCare; + case StoreAction::kStore: + return MTLStoreActionStore; + } + return MTLStoreActionDontCare; +} + +constexpr StoreAction FromMTLStoreAction(MTLStoreAction action) { + switch (action) { + case MTLStoreActionDontCare: + return StoreAction::kDontCare; + case MTLStoreActionStore: + return StoreAction::kStore; + default: + break; + } + return StoreAction::kDontCare; +} + +constexpr MTLSamplerMinMagFilter ToMTLSamplerMinMagFilter(MinMagFilter filter) { + switch (filter) { + case MinMagFilter::kNearest: + return MTLSamplerMinMagFilterNearest; + case MinMagFilter::kLinear: + return MTLSamplerMinMagFilterLinear; + } + return MTLSamplerMinMagFilterNearest; +} + +constexpr MTLSamplerAddressMode ToMTLSamplerAddressMode( + SamplerAddressMode mode) { + switch (mode) { + case SamplerAddressMode::kClampToEdge: + return MTLSamplerAddressModeClampToEdge; + case SamplerAddressMode::kRepeat: + return MTLSamplerAddressModeRepeat; + case SamplerAddressMode::kMirror: + return MTLSamplerAddressModeMirrorRepeat; + } + return MTLSamplerAddressModeClampToEdge; +} + +constexpr MTLClearColor ToMTLClearColor(const Color& color) { + return MTLClearColorMake(color.red, color.green, color.blue, color.alpha); +} + +MTLRenderPipelineColorAttachmentDescriptor* +ToMTLRenderPipelineColorAttachmentDescriptor( + PipelineColorAttachment descriptor); + +MTLDepthStencilDescriptor* ToMTLDepthStencilDescriptor( + std::optional depth, + std::optional front, + std::optional back); + +MTLTextureDescriptor* ToMTLTextureDescriptor(const TextureDescriptor& desc); + +} // namespace impeller diff --git a/impeller/renderer/backend/metal/formats_metal.mm b/impeller/renderer/backend/metal/formats_metal.mm new file mode 100644 index 0000000000000..ba23493979dbc --- /dev/null +++ b/impeller/renderer/backend/metal/formats_metal.mm @@ -0,0 +1,102 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "impeller/renderer/formats_metal.h" + +#include + +#include "impeller/renderer/render_pass.h" + +namespace impeller { + +MTLRenderPipelineColorAttachmentDescriptor* +ToMTLRenderPipelineColorAttachmentDescriptor( + PipelineColorAttachment descriptor) { + auto des = [[MTLRenderPipelineColorAttachmentDescriptor alloc] init]; + des.pixelFormat = ToMTLPixelFormat(descriptor.format); + + des.blendingEnabled = descriptor.blending_enabled; + + des.sourceRGBBlendFactor = + ToMTLBlendFactor(descriptor.src_color_blend_factor); + des.rgbBlendOperation = ToMTLBlendOperation(descriptor.color_blend_op); + des.destinationRGBBlendFactor = + ToMTLBlendFactor(descriptor.dst_color_blend_factor); + + des.sourceAlphaBlendFactor = + ToMTLBlendFactor(descriptor.src_alpha_blend_factor); + des.alphaBlendOperation = ToMTLBlendOperation(descriptor.alpha_blend_op); + des.destinationAlphaBlendFactor = + ToMTLBlendFactor(descriptor.dst_alpha_blend_factor); + + des.writeMask = ToMTLColorWriteMask(descriptor.write_mask); + return des; +} + +MTLStencilDescriptor* ToMTLStencilDescriptor( + const PipelineStencilAttachment& descriptor) { + auto des = [[MTLStencilDescriptor alloc] init]; + des.stencilCompareFunction = ToMTLCompareFunction(descriptor.stencil_compare); + des.stencilFailureOperation = + ToMTLStencilOperation(descriptor.stencil_failure); + des.depthFailureOperation = ToMTLStencilOperation(descriptor.depth_failure); + des.depthStencilPassOperation = + ToMTLStencilOperation(descriptor.depth_stencil_pass); + + des.readMask = descriptor.read_mask; + des.writeMask = descriptor.write_mask; + + return des; +} + +MTLDepthStencilDescriptor* ToMTLDepthStencilDescriptor( + std::optional depth, + std::optional front, + std::optional back) { + if (!depth) { + depth = PipelineDepthAttachment{ + // Always pass the depth test. + .depth_compare = CompareFunction::kAlways, + .depth_write_enabled = false, + }; + } + + auto des = [[MTLDepthStencilDescriptor alloc] init]; + + des.depthCompareFunction = ToMTLCompareFunction(depth->depth_compare); + des.depthWriteEnabled = depth->depth_write_enabled; + + if (front.has_value()) { + des.frontFaceStencil = ToMTLStencilDescriptor(front.value()); + } + if (back.has_value()) { + des.backFaceStencil = ToMTLStencilDescriptor(back.value()); + } + + return des; +} + +MTLTextureDescriptor* ToMTLTextureDescriptor(const TextureDescriptor& desc) { + auto mtl_desc = [[MTLTextureDescriptor alloc] init]; + mtl_desc.pixelFormat = ToMTLPixelFormat(desc.format); + mtl_desc.width = desc.size.width; + mtl_desc.height = desc.size.height; + mtl_desc.mipmapLevelCount = desc.mip_count; + mtl_desc.usage = MTLTextureUsageUnknown; + if (desc.usage & static_cast(TextureUsage::kUnknown)) { + mtl_desc.usage |= MTLTextureUsageUnknown; + } + if (desc.usage & static_cast(TextureUsage::kShaderRead)) { + mtl_desc.usage |= MTLTextureUsageShaderRead; + } + if (desc.usage & static_cast(TextureUsage::kShaderWrite)) { + mtl_desc.usage |= MTLTextureUsageShaderWrite; + } + if (desc.usage & static_cast(TextureUsage::kRenderTarget)) { + mtl_desc.usage |= MTLTextureUsageRenderTarget; + } + return mtl_desc; +} + +} // namespace impeller diff --git a/impeller/renderer/backend/metal/pipeline_library_mtl.h b/impeller/renderer/backend/metal/pipeline_library_mtl.h new file mode 100644 index 0000000000000..ef5ccbaadaf1a --- /dev/null +++ b/impeller/renderer/backend/metal/pipeline_library_mtl.h @@ -0,0 +1,22 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#pragma once + +#include "flutter/fml/macros.h" +#include "impeller/renderer/pipeline_library.h" + +namespace impeller { + +class PipelineLibraryMTL final : public PipelineLibrary { + public: + PipelineLibraryMTL(); + + ~PipelineLibraryMTL() override; + + private: + FML_DISALLOW_COPY_AND_ASSIGN(PipelineLibraryMTL); +}; + +} // namespace impeller diff --git a/impeller/renderer/backend/metal/pipeline_library_mtl.mm b/impeller/renderer/backend/metal/pipeline_library_mtl.mm new file mode 100644 index 0000000000000..e69de29bb2d1d diff --git a/impeller/renderer/backend/metal/pipeline_mtl.h b/impeller/renderer/backend/metal/pipeline_mtl.h new file mode 100644 index 0000000000000..7ef22a2b0e7c1 --- /dev/null +++ b/impeller/renderer/backend/metal/pipeline_mtl.h @@ -0,0 +1,22 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#pragma once + +#include "flutter/fml/macros.h" +#include "impeller/renderer/pipeline.h" + +namespace impeller { + +class PipelineMTL final : public Pipeline { + public: + PipelineMTL(); + + ~PipelineMTL() override; + + private: + FML_DISALLOW_COPY_AND_ASSIGN(PipelineMTL); +}; + +} // namespace impeller diff --git a/impeller/renderer/backend/metal/pipeline_mtl.mm b/impeller/renderer/backend/metal/pipeline_mtl.mm new file mode 100644 index 0000000000000..e69de29bb2d1d diff --git a/impeller/renderer/backend/metal/render_pass_mtl.h b/impeller/renderer/backend/metal/render_pass_mtl.h new file mode 100644 index 0000000000000..b11658c19dad2 --- /dev/null +++ b/impeller/renderer/backend/metal/render_pass_mtl.h @@ -0,0 +1,22 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#pragma once + +#include "flutter/fml/macros.h" +#include "impeller/renderer/render_pass.h" + +namespace impeller { + +class RenderPassMTL final : public RenderPass { + public: + RenderPassMTL(); + + ~RenderPassMTL() override; + + private: + FML_DISALLOW_COPY_AND_ASSIGN(RenderPassMTL); +}; + +} // namespace impeller diff --git a/impeller/renderer/backend/metal/render_pass_mtl.mm b/impeller/renderer/backend/metal/render_pass_mtl.mm new file mode 100644 index 0000000000000..e69de29bb2d1d diff --git a/impeller/renderer/backend/metal/renderer_mtl.h b/impeller/renderer/backend/metal/renderer_mtl.h new file mode 100644 index 0000000000000..dbb9f2565b40f --- /dev/null +++ b/impeller/renderer/backend/metal/renderer_mtl.h @@ -0,0 +1,22 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#pragma once + +#include "flutter/fml/macros.h" +#include "impeller/renderer/renderer.h" + +namespace impeller { + +class RendererMTL final : public Renderer { + public: + RendererMTL(); + + ~RendererMTL() override; + + private: + FML_DISALLOW_COPY_AND_ASSIGN(RendererMTL); +}; + +} // namespace impeller diff --git a/impeller/renderer/backend/metal/renderer_mtl.mm b/impeller/renderer/backend/metal/renderer_mtl.mm new file mode 100644 index 0000000000000..e69de29bb2d1d diff --git a/impeller/renderer/backend/metal/sampler_mtl.h b/impeller/renderer/backend/metal/sampler_mtl.h new file mode 100644 index 0000000000000..9a80eb8d8ae41 --- /dev/null +++ b/impeller/renderer/backend/metal/sampler_mtl.h @@ -0,0 +1,22 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#pragma once + +#include "flutter/fml/macros.h" +#include "impeller/renderer/sampler.h" + +namespace impeller { + +class SamplerMTL final : public Sampler { + public: + SamplerMTL(); + + ~SamplerMTL() override; + + private: + FML_DISALLOW_COPY_AND_ASSIGN(SamplerMTL); +}; + +} // namespace impeller diff --git a/impeller/renderer/backend/metal/sampler_mtl.mm b/impeller/renderer/backend/metal/sampler_mtl.mm new file mode 100644 index 0000000000000..e69de29bb2d1d diff --git a/impeller/renderer/backend/metal/shader_function_mtl.h b/impeller/renderer/backend/metal/shader_function_mtl.h new file mode 100644 index 0000000000000..f48b64a9f3cf3 --- /dev/null +++ b/impeller/renderer/backend/metal/shader_function_mtl.h @@ -0,0 +1,22 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#pragma once + +#include "flutter/fml/macros.h" +#include "impeller/renderer/shader_function.h" + +namespace impeller { + +class ShaderFunctionMTL final : public ShaderFunction { + public: + ShaderFunctionMTL(); + + ~ShaderFunctionMTL() override; + + private: + FML_DISALLOW_COPY_AND_ASSIGN(ShaderFunctionMTL); +}; + +} // namespace impeller diff --git a/impeller/renderer/backend/metal/shader_function_mtl.mm b/impeller/renderer/backend/metal/shader_function_mtl.mm new file mode 100644 index 0000000000000..e69de29bb2d1d diff --git a/impeller/renderer/backend/metal/shader_library_mtl.h b/impeller/renderer/backend/metal/shader_library_mtl.h new file mode 100644 index 0000000000000..7ea6beeebe87e --- /dev/null +++ b/impeller/renderer/backend/metal/shader_library_mtl.h @@ -0,0 +1,22 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#pragma once + +#include "flutter/fml/macros.h" +#include "impeller/renderer/shader_library.h" + +namespace impeller { + +class ShaderLibraryMTL final : public ShaderLibrary { + public: + ShaderLibraryMTL(); + + ~ShaderLibraryMTL() override; + + private: + FML_DISALLOW_COPY_AND_ASSIGN(ShaderLibraryMTL); +}; + +} // namespace impeller diff --git a/impeller/renderer/backend/metal/shader_library_mtl.mm b/impeller/renderer/backend/metal/shader_library_mtl.mm new file mode 100644 index 0000000000000..e69de29bb2d1d diff --git a/impeller/renderer/backend/metal/texture_mtl.h b/impeller/renderer/backend/metal/texture_mtl.h new file mode 100644 index 0000000000000..33d516d8ae323 --- /dev/null +++ b/impeller/renderer/backend/metal/texture_mtl.h @@ -0,0 +1,22 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#pragma once + +#include "flutter/fml/macros.h" +#include "impeller/renderer/texture.h" + +namespace impeller { + +class TextureMTL final : public Texture { + public: + TextureMTL(); + + ~TextureMTL() override; + + private: + FML_DISALLOW_COPY_AND_ASSIGN(TextureMTL); +}; + +} // namespace impeller diff --git a/impeller/renderer/backend/metal/texture_mtl.mm b/impeller/renderer/backend/metal/texture_mtl.mm new file mode 100644 index 0000000000000..e69de29bb2d1d From ae3b180b955cd899831fdf35ff51116c0ae68343 Mon Sep 17 00:00:00 2001 From: Chinmay Garde Date: Mon, 25 Oct 2021 14:08:02 -0700 Subject: [PATCH 177/433] Start getting rid of ObjC TU's from core library. --- impeller/playground/playground.mm | 6 +- impeller/renderer/BUILD.gn | 166 ++++--- impeller/renderer/allocator.cc | 27 ++ impeller/renderer/allocator.h | 35 +- impeller/renderer/allocator.mm | 134 ------ impeller/renderer/backend/metal/BUILD.gn | 9 - .../renderer/backend/metal/allocator_mtl.h | 34 ++ .../renderer/backend/metal/allocator_mtl.mm | 121 ++++++ .../metal/{renderer_mtl.h => backend_cast.h} | 13 +- .../backend/metal/command_buffer_mtl.h | 28 +- .../backend/metal/command_buffer_mtl.mm | 70 +++ impeller/renderer/backend/metal/context_mtl.h | 49 ++- .../renderer/backend/metal/context_mtl.mm | 135 ++++++ .../backend/metal/device_buffer_mtl.h | 40 +- .../backend/metal/device_buffer_mtl.mm | 105 +++++ .../renderer/backend/metal/formats_metal.h | 263 ----------- .../metal/formats_mtl.h} | 4 +- .../{formats_metal.mm => formats_mtl.mm} | 0 .../backend/metal/pipeline_library_mtl.h | 25 ++ .../backend/metal/pipeline_library_mtl.mm | 103 +++++ .../renderer/backend/metal/pipeline_mtl.h | 26 +- .../renderer/backend/metal/pipeline_mtl.mm | 33 ++ .../renderer/backend/metal/render_pass_mtl.h | 35 +- .../renderer/backend/metal/render_pass_mtl.mm | 411 ++++++++++++++++++ .../renderer/backend/metal/renderer_mtl.mm | 0 .../backend/metal/sampler_library_mtl.h | 46 ++ .../metal/sampler_library_mtl.mm} | 14 +- impeller/renderer/backend/metal/sampler_mtl.h | 20 +- .../renderer/backend/metal/sampler_mtl.mm | 21 + .../backend/metal/shader_function_mtl.h | 21 +- .../backend/metal/shader_function_mtl.mm | 22 + .../backend/metal/shader_library_mtl.h | 47 ++ .../backend/metal/shader_library_mtl.mm | 40 ++ impeller/renderer/backend/metal/texture_mtl.h | 25 +- .../renderer/backend/metal/texture_mtl.mm | 72 +++ impeller/renderer/{buffer.mm => buffer.cc} | 0 .../{buffer_view.mm => buffer_view.cc} | 0 impeller/renderer/{command.mm => command.cc} | 2 - impeller/renderer/command_buffer.cc | 13 + impeller/renderer/command_buffer.h | 26 +- impeller/renderer/command_buffer.mm | 67 --- impeller/renderer/context.cc | 13 + impeller/renderer/context.h | 33 +- impeller/renderer/context.mm | 138 ------ impeller/renderer/device_buffer.cc | 13 + impeller/renderer/device_buffer.h | 39 +- impeller/renderer/device_buffer.mm | 103 ----- ...nittests.mm => device_buffer_unittests.cc} | 0 impeller/renderer/formats_metal.mm | 102 ----- .../{host_buffer.mm => host_buffer.cc} | 0 ..._unittests.mm => host_buffer_unittests.cc} | 0 .../renderer/{pipeline.mm => pipeline.cc} | 22 +- impeller/renderer/pipeline.h | 21 +- ...ipeline_builder.mm => pipeline_builder.cc} | 0 ...e_descriptor.mm => pipeline_descriptor.cc} | 47 +- impeller/renderer/pipeline_descriptor.h | 47 +- impeller/renderer/pipeline_library.cc | 23 + impeller/renderer/pipeline_library.h | 26 +- impeller/renderer/pipeline_library.mm | 65 --- .../renderer/{platform.mm => platform.cc} | 0 impeller/renderer/render_pass.cc | 13 + impeller/renderer/render_pass.h | 44 +- impeller/renderer/render_pass.mm | 408 ----------------- ...escriptor.mm => render_pass_descriptor.cc} | 0 impeller/renderer/render_pass_descriptor.h | 4 - impeller/renderer/renderer.h | 2 +- impeller/renderer/renderer.mm | 5 +- ...rer_unittests.mm => renderer_unittests.cc} | 2 + impeller/renderer/{sampler.mm => sampler.cc} | 10 +- impeller/renderer/sampler.h | 17 +- impeller/renderer/sampler_descriptor.cc | 11 + impeller/renderer/sampler_descriptor.h | 25 -- impeller/renderer/sampler_library.cc | 13 + impeller/renderer/sampler_library.h | 28 ++ ...{shader_function.mm => shader_function.cc} | 6 - impeller/renderer/shader_function.h | 18 +- impeller/renderer/shader_library.cc | 13 + impeller/renderer/shader_library.h | 51 +-- impeller/renderer/shader_library.mm | 37 -- impeller/renderer/{surface.mm => surface.cc} | 0 impeller/renderer/surface.h | 3 +- impeller/renderer/texture.cc | 17 + impeller/renderer/texture.h | 20 +- impeller/renderer/texture.mm | 68 --- ...re_descriptor.mm => texture_descriptor.cc} | 0 .../{vertex_buffer.mm => vertex_buffer.cc} | 0 ...er_builder.mm => vertex_buffer_builder.cc} | 0 ...tex_descriptor.mm => vertex_descriptor.cc} | 0 impeller/renderer/vertex_descriptor.h | 2 - 89 files changed, 1958 insertions(+), 1859 deletions(-) create mode 100644 impeller/renderer/allocator.cc delete mode 100644 impeller/renderer/allocator.mm delete mode 100644 impeller/renderer/backend/metal/BUILD.gn rename impeller/renderer/backend/metal/{renderer_mtl.h => backend_cast.h} (55%) delete mode 100644 impeller/renderer/backend/metal/formats_metal.h rename impeller/renderer/{formats_metal.h => backend/metal/formats_mtl.h} (100%) rename impeller/renderer/backend/metal/{formats_metal.mm => formats_mtl.mm} (100%) delete mode 100644 impeller/renderer/backend/metal/renderer_mtl.mm create mode 100644 impeller/renderer/backend/metal/sampler_library_mtl.h rename impeller/renderer/{sampler_descriptor.mm => backend/metal/sampler_library_mtl.mm} (69%) rename impeller/renderer/{buffer.mm => buffer.cc} (100%) rename impeller/renderer/{buffer_view.mm => buffer_view.cc} (100%) rename impeller/renderer/{command.mm => command.cc} (99%) create mode 100644 impeller/renderer/command_buffer.cc delete mode 100644 impeller/renderer/command_buffer.mm create mode 100644 impeller/renderer/context.cc delete mode 100644 impeller/renderer/context.mm create mode 100644 impeller/renderer/device_buffer.cc delete mode 100644 impeller/renderer/device_buffer.mm rename impeller/renderer/{device_buffer_unittests.mm => device_buffer_unittests.cc} (100%) delete mode 100644 impeller/renderer/formats_metal.mm rename impeller/renderer/{host_buffer.mm => host_buffer.cc} (100%) rename impeller/renderer/{host_buffer_unittests.mm => host_buffer_unittests.cc} (100%) rename impeller/renderer/{pipeline.mm => pipeline.cc} (58%) rename impeller/renderer/{pipeline_builder.mm => pipeline_builder.cc} (100%) rename impeller/renderer/{pipeline_descriptor.mm => pipeline_descriptor.cc} (73%) create mode 100644 impeller/renderer/pipeline_library.cc delete mode 100644 impeller/renderer/pipeline_library.mm rename impeller/renderer/{platform.mm => platform.cc} (100%) create mode 100644 impeller/renderer/render_pass.cc delete mode 100644 impeller/renderer/render_pass.mm rename impeller/renderer/{render_pass_descriptor.mm => render_pass_descriptor.cc} (100%) rename impeller/renderer/{renderer_unittests.mm => renderer_unittests.cc} (99%) rename impeller/renderer/{sampler.mm => sampler.cc} (59%) create mode 100644 impeller/renderer/sampler_descriptor.cc create mode 100644 impeller/renderer/sampler_library.cc create mode 100644 impeller/renderer/sampler_library.h rename impeller/renderer/{shader_function.mm => shader_function.cc} (85%) create mode 100644 impeller/renderer/shader_library.cc delete mode 100644 impeller/renderer/shader_library.mm rename impeller/renderer/{surface.mm => surface.cc} (100%) create mode 100644 impeller/renderer/texture.cc delete mode 100644 impeller/renderer/texture.mm rename impeller/renderer/{texture_descriptor.mm => texture_descriptor.cc} (100%) rename impeller/renderer/{vertex_buffer.mm => vertex_buffer.cc} (100%) rename impeller/renderer/{vertex_buffer_builder.mm => vertex_buffer_builder.cc} (100%) rename impeller/renderer/{vertex_descriptor.mm => vertex_descriptor.cc} (100%) diff --git a/impeller/playground/playground.mm b/impeller/playground/playground.mm index 95247d2a94c63..f9397e1a9fddd 100644 --- a/impeller/playground/playground.mm +++ b/impeller/playground/playground.mm @@ -9,6 +9,7 @@ #include "impeller/image/compressed_image.h" #include "impeller/playground/playground.h" #include "impeller/renderer/allocator.h" +#include "impeller/renderer/backend/metal/context_mtl.h" #include "impeller/renderer/context.h" #include "impeller/renderer/formats_metal.h" #include "impeller/renderer/render_pass.h" @@ -34,7 +35,8 @@ } Playground::Playground() - : renderer_(ShaderLibraryDirectory(), "shader_fixtures.metallib") {} + : renderer_(std::make_shared(ShaderLibraryDirectory(), + "shader_fixtures.metallib")) {} Playground::~Playground() = default; @@ -112,7 +114,7 @@ static void PlaygroundKeyCallback(GLFWwindow* window, NSWindow* cocoa_window = ::glfwGetCocoaWindow(window); CAMetalLayer* layer = [CAMetalLayer layer]; - layer.device = renderer_.GetContext()->GetMTLDevice(); + layer.device = ContextMTL::Cast(*renderer_.GetContext()).GetMTLDevice(); layer.pixelFormat = MTLPixelFormatBGRA8Unorm; cocoa_window.contentView.layer = layer; cocoa_window.contentView.wantsLayer = YES; diff --git a/impeller/renderer/BUILD.gn b/impeller/renderer/BUILD.gn index 2992255101ee7..ef46bf756befd 100644 --- a/impeller/renderer/BUILD.gn +++ b/impeller/renderer/BUILD.gn @@ -5,73 +5,103 @@ import("//flutter/impeller/tools/impeller.gni") impeller_component("renderer") { - sources = [ - "allocator.h", - "allocator.mm", - "buffer.h", - "buffer.mm", - "buffer_view.h", - "buffer_view.mm", - "command.h", - "command.mm", - "command_buffer.h", - "command_buffer.mm", - "comparable.cc", - "comparable.h", - "context.h", - "context.mm", - "device_buffer.h", - "device_buffer.mm", - "formats.cc", - "formats.h", - "formats_metal.h", - "formats_metal.mm", - "host_buffer.h", - "host_buffer.mm", - "pipeline.h", - "pipeline.mm", - "pipeline_builder.h", - "pipeline_builder.mm", - "pipeline_descriptor.h", - "pipeline_descriptor.mm", - "pipeline_library.h", - "pipeline_library.mm", - "platform.h", - "platform.mm", - "range.cc", - "range.h", - "render_pass.h", - "render_pass.mm", - "render_pass_descriptor.h", - "render_pass_descriptor.mm", - "renderer.h", - "renderer.mm", - "sampler.h", - "sampler.mm", - "sampler_descriptor.h", - "sampler_descriptor.mm", - "shader_function.h", - "shader_function.mm", - "shader_library.h", - "shader_library.mm", - "shader_types.cc", - "shader_types.h", - "surface.h", - "surface.mm", - "tessellator.cc", - "tessellator.h", - "texture.h", - "texture.mm", - "texture_descriptor.h", - "texture_descriptor.mm", - "vertex_buffer.h", - "vertex_buffer.mm", - "vertex_buffer_builder.h", - "vertex_buffer_builder.mm", - "vertex_descriptor.h", - "vertex_descriptor.mm", + metal_backend_sources = [ + "backend/metal/allocator_mtl.h", + "backend/metal/allocator_mtl.mm", + "backend/metal/backend_cast.h", + "backend/metal/command_buffer_mtl.h", + "backend/metal/command_buffer_mtl.mm", + "backend/metal/context_mtl.h", + "backend/metal/context_mtl.mm", + "backend/metal/device_buffer_mtl.h", + "backend/metal/device_buffer_mtl.mm", + "backend/metal/formats_mtl.h", + "backend/metal/formats_mtl.mm", + "backend/metal/pipeline_library_mtl.h", + "backend/metal/pipeline_library_mtl.mm", + "backend/metal/pipeline_mtl.h", + "backend/metal/pipeline_mtl.mm", + "backend/metal/render_pass_mtl.h", + "backend/metal/render_pass_mtl.mm", + "backend/metal/sampler_library_mtl.h", + "backend/metal/sampler_library_mtl.mm", + "backend/metal/sampler_mtl.h", + "backend/metal/sampler_mtl.mm", + "backend/metal/shader_function_mtl.h", + "backend/metal/shader_function_mtl.mm", + "backend/metal/shader_library_mtl.h", + "backend/metal/shader_library_mtl.mm", + "backend/metal/texture_mtl.h", + "backend/metal/texture_mtl.mm", ] + sources = [ + "allocator.h", + "allocator.cc", + "buffer.h", + "buffer.cc", + "buffer_view.h", + "buffer_view.cc", + "command.h", + "command.cc", + "command_buffer.h", + "command_buffer.cc", + "comparable.cc", + "comparable.h", + "context.h", + "context.cc", + "device_buffer.h", + "device_buffer.cc", + "formats.cc", + "formats.h", + "host_buffer.h", + "host_buffer.cc", + "pipeline.h", + "pipeline.cc", + "pipeline_builder.h", + "pipeline_builder.cc", + "pipeline_descriptor.h", + "pipeline_descriptor.cc", + "pipeline_library.h", + "pipeline_library.cc", + "platform.h", + "platform.cc", + "range.cc", + "range.h", + "render_pass.h", + "render_pass.cc", + "render_pass_descriptor.h", + "render_pass_descriptor.cc", + "renderer.h", + "renderer.mm", + "sampler.h", + "sampler.cc", + "sampler_descriptor.h", + "sampler_descriptor.cc", + "sampler_library.h", + "sampler_library.cc", + "shader_function.h", + "shader_function.cc", + "shader_library.h", + "shader_library.cc", + "shader_types.cc", + "shader_types.h", + "surface.h", + "surface.cc", + "tessellator.cc", + "tessellator.h", + "texture.h", + "texture.cc", + "texture_descriptor.h", + "texture_descriptor.cc", + "vertex_buffer.h", + "vertex_buffer.cc", + "vertex_buffer_builder.h", + "vertex_buffer_builder.cc", + "vertex_descriptor.h", + "vertex_descriptor.cc", + ] + metal_backend_sources + public_deps = [ "../base", "../geometry", @@ -87,9 +117,9 @@ source_set("renderer_unittests") { testonly = true sources = [ - "device_buffer_unittests.mm", - "host_buffer_unittests.mm", - "renderer_unittests.mm", + "device_buffer_unittests.cc", + "host_buffer_unittests.cc", + "renderer_unittests.cc", ] deps = [ diff --git a/impeller/renderer/allocator.cc b/impeller/renderer/allocator.cc new file mode 100644 index 0000000000000..5969d220c21a7 --- /dev/null +++ b/impeller/renderer/allocator.cc @@ -0,0 +1,27 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "impeller/renderer/allocator.h" + +namespace impeller { + +Allocator::Allocator() = default; + +Allocator::~Allocator() = default; + +bool Allocator::RequiresExplicitHostSynchronization(StorageMode mode) { + if (mode != StorageMode::kHostVisible) { + return false; + } + +#if OS_IOS + // StorageMode::kHostVisible is MTLStorageModeShared already. + return false; +#else // OS_IOS + // StorageMode::kHostVisible is MTLResourceStorageModeManaged. + return true; +#endif // OS_IOS +} + +} // namespace impeller diff --git a/impeller/renderer/allocator.h b/impeller/renderer/allocator.h index 14c37288f1ae3..d8a81a8abebf6 100644 --- a/impeller/renderer/allocator.h +++ b/impeller/renderer/allocator.h @@ -4,8 +4,6 @@ #pragma once -#include - #include #include "flutter/fml/macros.h" @@ -51,35 +49,30 @@ class Texture; class Allocator { public: - ~Allocator(); + virtual ~Allocator(); bool IsValid() const; - std::shared_ptr CreateBuffer(StorageMode mode, size_t length); + virtual std::shared_ptr CreateBuffer(StorageMode mode, + size_t length) = 0; - std::shared_ptr CreateTexture(StorageMode mode, - const TextureDescriptor& desc); + virtual std::shared_ptr CreateTexture( + StorageMode mode, + const TextureDescriptor& desc) = 0; - std::shared_ptr CreateBufferWithCopy(const uint8_t* buffer, - size_t length); + virtual std::shared_ptr CreateBufferWithCopy( + const uint8_t* buffer, + size_t length) = 0; - std::shared_ptr CreateBufferWithCopy( - const fml::Mapping& mapping); + virtual std::shared_ptr CreateBufferWithCopy( + const fml::Mapping& mapping) = 0; static bool RequiresExplicitHostSynchronization(StorageMode mode); - private: - friend class Context; - - // In the prototype, we are going to be allocating resources directly with the - // MTLDevice APIs. But, in the future, this could be backed by named heaps - // with specific limits. - id device_; - std::string allocator_label_; - bool is_valid_ = false; - - Allocator(id device, std::string label); + protected: + Allocator(); + private: FML_DISALLOW_COPY_AND_ASSIGN(Allocator); }; diff --git a/impeller/renderer/allocator.mm b/impeller/renderer/allocator.mm deleted file mode 100644 index c409871a371e1..0000000000000 --- a/impeller/renderer/allocator.mm +++ /dev/null @@ -1,134 +0,0 @@ -// Copyright 2013 The Flutter Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#include "impeller/renderer/allocator.h" - -#include "flutter/fml/build_config.h" -#include "flutter/fml/logging.h" -#include "impeller/renderer/buffer.h" -#include "impeller/renderer/device_buffer.h" -#include "impeller/renderer/formats_metal.h" - -namespace impeller { - -Allocator::Allocator(id device, std::string label) - : device_(device), allocator_label_(std::move(label)) { - if (!device_) { - return; - } - - is_valid_ = true; -} - -Allocator::~Allocator() = default; - -bool Allocator::IsValid() const { - return is_valid_; -} - -static MTLResourceOptions ToMTLResourceOptions(StorageMode type) { - switch (type) { - case StorageMode::kHostVisible: -#if OS_IOS - return MTLResourceStorageModeShared; -#else - return MTLResourceStorageModeManaged; -#endif - case StorageMode::kDevicePrivate: - return MTLResourceStorageModePrivate; - case StorageMode::kDeviceTransient: -#if OS_IOS - return MTLResourceStorageModeMemoryless; -#else - return MTLResourceStorageModePrivate; -#endif - } - - return MTLResourceStorageModePrivate; -} - -static MTLStorageMode ToMTLStorageMode(StorageMode mode) { - switch (mode) { - case StorageMode::kHostVisible: -#if OS_IOS - return MTLStorageModeShared; -#else - return MTLStorageModeManaged; -#endif - case StorageMode::kDevicePrivate: - return MTLStorageModePrivate; - case StorageMode::kDeviceTransient: -#if OS_IOS - return MTLStorageModeMemoryless; -#else - return MTLStorageModePrivate; -#endif - } - return MTLStorageModeShared; -} - -bool Allocator::RequiresExplicitHostSynchronization(StorageMode mode) { - if (mode != StorageMode::kHostVisible) { - return false; - } - -#if OS_IOS - // StorageMode::kHostVisible is MTLStorageModeShared already. - return false; -#else - // StorageMode::kHostVisible is MTLResourceStorageModeManaged. - return true; -#endif -} - -std::shared_ptr Allocator::CreateBuffer(StorageMode mode, - size_t length) { - auto buffer = [device_ newBufferWithLength:length - options:ToMTLResourceOptions(mode)]; - if (!buffer) { - return nullptr; - } - return std::shared_ptr(new DeviceBuffer(buffer, length, mode)); -} - -std::shared_ptr Allocator::CreateBufferWithCopy( - const uint8_t* buffer, - size_t length) { - auto new_buffer = CreateBuffer(StorageMode::kHostVisible, length); - - if (!new_buffer) { - return nullptr; - } - - auto entire_range = Range{0, length}; - - if (!new_buffer->CopyHostBuffer(buffer, entire_range)) { - return nullptr; - } - - return new_buffer; -} - -std::shared_ptr Allocator::CreateBufferWithCopy( - const fml::Mapping& mapping) { - return CreateBufferWithCopy(mapping.GetMapping(), mapping.GetSize()); -} - -std::shared_ptr Allocator::CreateTexture( - StorageMode mode, - const TextureDescriptor& desc) { - if (!IsValid()) { - return nullptr; - } - - auto mtl_texture_desc = ToMTLTextureDescriptor(desc); - mtl_texture_desc.storageMode = ToMTLStorageMode(mode); - auto texture = [device_ newTextureWithDescriptor:mtl_texture_desc]; - if (!texture) { - return nullptr; - } - return std::make_shared(desc, texture); -} - -} // namespace impeller diff --git a/impeller/renderer/backend/metal/BUILD.gn b/impeller/renderer/backend/metal/BUILD.gn deleted file mode 100644 index aae582859569c..0000000000000 --- a/impeller/renderer/backend/metal/BUILD.gn +++ /dev/null @@ -1,9 +0,0 @@ -# Copyright 2013 The Flutter Authors. All rights reserved. -# Use of this source code is governed by a BSD-style license that can be -# found in the LICENSE file. - -import("//flutter/impeller/tools/impeller.gni") - -impeller_component("metal") { - sources = [] -} diff --git a/impeller/renderer/backend/metal/allocator_mtl.h b/impeller/renderer/backend/metal/allocator_mtl.h index 494b6f78a3960..fc5d8c170da2b 100644 --- a/impeller/renderer/backend/metal/allocator_mtl.h +++ b/impeller/renderer/backend/metal/allocator_mtl.h @@ -4,6 +4,8 @@ #pragma once +#include + #include "flutter/fml/macros.h" #include "impeller/renderer/allocator.h" @@ -13,9 +15,41 @@ class AllocatorMTL final : public Allocator { public: AllocatorMTL(); + // |Allocator| ~AllocatorMTL() override; private: + friend class ContextMTL; + + // In the prototype, we are going to be allocating resources directly with the + // MTLDevice APIs. But, in the future, this could be backed by named heaps + // with specific limits. + id device_; + std::string allocator_label_; + bool is_valid_ = false; + + AllocatorMTL(id device, std::string label); + + // |Allocator| + bool IsValid() const; + + // |Allocator| + std::shared_ptr CreateBuffer(StorageMode mode, + size_t length) override; + + // |Allocator| + std::shared_ptr CreateTexture( + StorageMode mode, + const TextureDescriptor& desc) override; + + // |Allocator| + std::shared_ptr CreateBufferWithCopy(const uint8_t* buffer, + size_t length) override; + + // |Allocator| + std::shared_ptr CreateBufferWithCopy( + const fml::Mapping& mapping) override; + FML_DISALLOW_COPY_AND_ASSIGN(AllocatorMTL); }; diff --git a/impeller/renderer/backend/metal/allocator_mtl.mm b/impeller/renderer/backend/metal/allocator_mtl.mm index e69de29bb2d1d..0414bd135d96f 100644 --- a/impeller/renderer/backend/metal/allocator_mtl.mm +++ b/impeller/renderer/backend/metal/allocator_mtl.mm @@ -0,0 +1,121 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "impeller/renderer/backend/metal/allocator_mtl.h" + +#include "flutter/fml/build_config.h" +#include "flutter/fml/logging.h" +#include "impeller/renderer/backend/metal/device_buffer_mtl.h" +#include "impeller/renderer/backend/metal/formats_mtl.h" +#include "impeller/renderer/buffer.h" + +namespace impeller { + +AllocatorMTL::AllocatorMTL(id device, std::string label) + : device_(device), allocator_label_(std::move(label)) { + if (!device_) { + return; + } + + is_valid_ = true; +} + +AllocatorMTL::~AllocatorMTL() = default; + +bool AllocatorMTL::IsValid() const { + return is_valid_; +} + +static MTLResourceOptions ToMTLResourceOptions(StorageMode type) { + switch (type) { + case StorageMode::kHostVisible: +#if OS_IOS + return MTLResourceStorageModeShared; +#else + return MTLResourceStorageModeManaged; +#endif + case StorageMode::kDevicePrivate: + return MTLResourceStorageModePrivate; + case StorageMode::kDeviceTransient: +#if OS_IOS + return MTLResourceStorageModeMemoryless; +#else + return MTLResourceStorageModePrivate; +#endif + } + + return MTLResourceStorageModePrivate; +} + +static MTLStorageMode ToMTLStorageMode(StorageMode mode) { + switch (mode) { + case StorageMode::kHostVisible: +#if OS_IOS + return MTLStorageModeShared; +#else + return MTLStorageModeManaged; +#endif + case StorageMode::kDevicePrivate: + return MTLStorageModePrivate; + case StorageMode::kDeviceTransient: +#if OS_IOS + return MTLStorageModeMemoryless; +#else + return MTLStorageModePrivate; +#endif + } + return MTLStorageModeShared; +} + +std::shared_ptr AllocatorMTL::CreateBuffer(StorageMode mode, + size_t length) { + auto buffer = [device_ newBufferWithLength:length + options:ToMTLResourceOptions(mode)]; + if (!buffer) { + return nullptr; + } + return std::shared_ptr( + new DeviceBufferMTL(buffer, length, mode)); +} + +std::shared_ptr AllocatorMTL::CreateBufferWithCopy( + const uint8_t* buffer, + size_t length) { + auto new_buffer = CreateBuffer(StorageMode::kHostVisible, length); + + if (!new_buffer) { + return nullptr; + } + + auto entire_range = Range{0, length}; + + if (!new_buffer->CopyHostBuffer(buffer, entire_range)) { + return nullptr; + } + + return new_buffer; +} + +std::shared_ptr AllocatorMTL::CreateBufferWithCopy( + const fml::Mapping& mapping) { + return CreateBufferWithCopy(mapping.GetMapping(), mapping.GetSize()); +} + +std::shared_ptr AllocatorMTL::CreateTexture( + StorageMode mode, + const TextureDescriptor& desc) { + if (!IsValid()) { + return nullptr; + } + + auto mtl_texture_desc = ToMTLTextureDescriptor(desc); + mtl_texture_desc.storageMode = ToMTLStorageMode(mode); + auto texture = [device_ newTextureWithDescriptor:mtl_texture_desc]; + if (!texture) { + return nullptr; + } + return std::make_shared(desc, texture); +} + +} // namespace impeller diff --git a/impeller/renderer/backend/metal/renderer_mtl.h b/impeller/renderer/backend/metal/backend_cast.h similarity index 55% rename from impeller/renderer/backend/metal/renderer_mtl.h rename to impeller/renderer/backend/metal/backend_cast.h index dbb9f2565b40f..a6bf9db907375 100644 --- a/impeller/renderer/backend/metal/renderer_mtl.h +++ b/impeller/renderer/backend/metal/backend_cast.h @@ -5,18 +5,17 @@ #pragma once #include "flutter/fml/macros.h" -#include "impeller/renderer/renderer.h" namespace impeller { -class RendererMTL final : public Renderer { +template +class BackendCast { public: - RendererMTL(); + static Sub& Cast(Base& base) { return reinterpret_cast(base); } - ~RendererMTL() override; - - private: - FML_DISALLOW_COPY_AND_ASSIGN(RendererMTL); + static const Sub& Cast(const Base& base) { + return reinterpret_cast(base); + } }; } // namespace impeller diff --git a/impeller/renderer/backend/metal/command_buffer_mtl.h b/impeller/renderer/backend/metal/command_buffer_mtl.h index 2f3f35d424bd0..bd816dd76ed52 100644 --- a/impeller/renderer/backend/metal/command_buffer_mtl.h +++ b/impeller/renderer/backend/metal/command_buffer_mtl.h @@ -4,19 +4,39 @@ #pragma once +#include + #include "flutter/fml/macros.h" #include "impeller/renderer/command_buffer.h" namespace impeller { -class CommabdBufferMTL final : public CommabdBuffer { +class CommandBufferMTL final : public CommandBuffer { public: - CommabdBufferMTL(); + CommandBufferMTL(); - ~CommabdBufferMTL() override; + // |CommandBuffer| + ~CommandBufferMTL() override; private: - FML_DISALLOW_COPY_AND_ASSIGN(CommabdBufferMTL); + friend class ContextMTL; + + id buffer_ = nullptr; + bool is_valid_ = false; + + CommandBufferMTL(id queue); + + // |CommandBuffer| + bool IsValid() const override; + + // |CommandBuffer| + void Commit(CommitCallback callback) override; + + // |CommandBuffer| + std::shared_ptr CreateRenderPass( + const RenderPassDescriptor& desc) const override; + + FML_DISALLOW_COPY_AND_ASSIGN(CommandBufferMTL); }; } // namespace impeller diff --git a/impeller/renderer/backend/metal/command_buffer_mtl.mm b/impeller/renderer/backend/metal/command_buffer_mtl.mm index e69de29bb2d1d..bd9f56e9e8c80 100644 --- a/impeller/renderer/backend/metal/command_buffer_mtl.mm +++ b/impeller/renderer/backend/metal/command_buffer_mtl.mm @@ -0,0 +1,70 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "impeller/renderer/backend/metal/command_buffer_mtl.h" + +#include "impeller/renderer/backend/metal/render_pass_mtl.h" + +namespace impeller { + +CommandBufferMTL::CommandBufferMTL(id queue) + : buffer_([queue commandBuffer]) { + if (!buffer_) { + return; + } + is_valid_ = true; +} + +CommandBufferMTL::~CommandBufferMTL() = default; + +bool CommandBufferMTL::IsValid() const { + return is_valid_; +} + +static CommandBuffer::CommitResult ToCommitResult( + MTLCommandBufferStatus status) { + switch (status) { + case MTLCommandBufferStatusCompleted: + return CommandBufferMTL::CommitResult::kCompleted; + case MTLCommandBufferStatusEnqueued: + return CommandBufferMTL::CommitResult::kPending; + default: + break; + } + return CommandBufferMTL::CommitResult::kError; +} + +void CommandBufferMTL::Commit(CommitCallback callback) { + if (!callback) { + callback = [](auto) {}; + } + + if (!buffer_) { + // Already committed. This is caller error. + callback(CommitResult::kError); + return; + } + + [buffer_ addCompletedHandler:^(id buffer) { + callback(ToCommitResult(buffer.status)); + }]; + [buffer_ commit]; + buffer_ = nil; +} + +std::shared_ptr CommandBufferMTL::CreateRenderPass( + const RenderPassDescriptor& desc) const { + if (!buffer_) { + return nullptr; + } + + auto pass = std::shared_ptr(new RenderPassMTL(buffer_, desc)); + if (!pass->IsValid()) { + return nullptr; + } + + return pass; +} + +} // namespace impeller diff --git a/impeller/renderer/backend/metal/context_mtl.h b/impeller/renderer/backend/metal/context_mtl.h index aaba0258b634c..fd9a0e6c20dfc 100644 --- a/impeller/renderer/backend/metal/context_mtl.h +++ b/impeller/renderer/backend/metal/context_mtl.h @@ -4,18 +4,63 @@ #pragma once +#include + +#include + #include "flutter/fml/macros.h" +#include "impeller/renderer/backend/metal/allocator_mtl.h" +#include "impeller/renderer/backend/metal/backend_cast.h" +#include "impeller/renderer/backend/metal/command_buffer_mtl.h" +#include "impeller/renderer/backend/metal/pipeline_library_mtl.h" +#include "impeller/renderer/backend/metal/shader_library_mtl.h" #include "impeller/renderer/context.h" +#include "impeller/renderer/sampler.h" namespace impeller { -class ContextMTL final : public Context { +class ContextMTL final : public Context, + public BackendCast { public: - ContextMTL(); + ContextMTL(std::string shaders_directory, std::string main_library_file_name); + // |Context| ~ContextMTL() override; + id GetMTLDevice() const; + private: + id device_ = nullptr; + id render_queue_ = nullptr; + id transfer_queue_ = nullptr; + std::shared_ptr shader_library_; + std::shared_ptr pipeline_library_; + std::shared_ptr sampler_library_; + std::shared_ptr permanents_allocator_; + std::shared_ptr transients_allocator_; + bool is_valid_ = false; + + // |Context| + bool IsValid() const override; + + // |Context| + std::shared_ptr GetPermanentsAllocator() const override; + + // |Context| + std::shared_ptr GetTransientsAllocator() const override; + + // |Context| + std::shared_ptr GetShaderLibrary() const override; + + // |Context| + std::shared_ptr GetSamplerLibrary() const override; + + // |Context| + std::shared_ptr GetPipelineLibrary() const override; + + // |Context| + std::shared_ptr CreateRenderCommandBuffer() const override; + FML_DISALLOW_COPY_AND_ASSIGN(ContextMTL); }; diff --git a/impeller/renderer/backend/metal/context_mtl.mm b/impeller/renderer/backend/metal/context_mtl.mm index e69de29bb2d1d..35efa3b560be4 100644 --- a/impeller/renderer/backend/metal/context_mtl.mm +++ b/impeller/renderer/backend/metal/context_mtl.mm @@ -0,0 +1,135 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "impeller/renderer/backend/metal/context_mtl.h" + +#include "flutter/fml/file.h" +#include "flutter/fml/logging.h" +#include "flutter/fml/paths.h" +#include "impeller/renderer/backend/metal/sampler_library_mtl.h" +#include "impeller/renderer/sampler_descriptor.h" + +namespace impeller { + +ContextMTL::ContextMTL(std::string shaders_directory, + std::string main_library_file_name) + : device_(::MTLCreateSystemDefaultDevice()) { + // Setup device. + if (!device_) { + return; + } + + // Setup command queues. + render_queue_ = device_.newCommandQueue; + transfer_queue_ = device_.newCommandQueue; + + if (!render_queue_ || !transfer_queue_) { + return; + } + + render_queue_.label = @"Impeller Render Queue"; + transfer_queue_.label = @"Impeller Transfer Queue"; + + // Setup the shader library. + { + NSError* shader_library_error = nil; + auto shader_library_path = + fml::paths::JoinPaths({shaders_directory, main_library_file_name}); + + auto library_exists = fml::IsFile(shader_library_path); + + if (!library_exists) { + FML_LOG(ERROR) << "Shader library does not exist at path '" + << shader_library_path + << "'. No piplines can be created in this context."; + } + auto library = + library_exists + ? [device_ newLibraryWithFile:@(shader_library_path.c_str()) + error:&shader_library_error] + : [device_ newDefaultLibrary]; + if (!library && shader_library_error) { + FML_LOG(ERROR) << "Could not create shader library: " + << shader_library_error.localizedDescription.UTF8String; + return; + } + + // std::make_shared disallowed because of private friend ctor. + shader_library_ = + std::shared_ptr(new ShaderLibraryMTL(library)); + } + + // Setup the pipeline library. + { // + pipeline_library_ = + std::shared_ptr(new PipelineLibraryMTL(device_)); + } + + // Setup the sampler library. + { // + sampler_library_ = + std::shared_ptr(new SamplerLibraryMTL(device_)); + } + + { + transients_allocator_ = std::shared_ptr( + new AllocatorMTL(device_, "Impeller Transients Allocator")); + if (!transients_allocator_) { + return; + } + + permanents_allocator_ = std::shared_ptr( + new AllocatorMTL(device_, "Impeller Permanents Allocator")); + if (!permanents_allocator_) { + return; + } + } + + is_valid_ = true; +} + +ContextMTL::~ContextMTL() = default; + +bool ContextMTL::IsValid() const { + return is_valid_; +} + +std::shared_ptr ContextMTL::GetShaderLibrary() const { + return shader_library_; +} + +std::shared_ptr ContextMTL::GetPipelineLibrary() const { + return pipeline_library_; +} + +std::shared_ptr ContextMTL::GetSamplerLibrary() const { + return sampler_library_; +} + +std::shared_ptr ContextMTL::CreateRenderCommandBuffer() const { + if (!IsValid()) { + return nullptr; + } + + auto buffer = + std::shared_ptr(new CommandBufferMTL(render_queue_)); + if (!buffer->IsValid()) { + return nullptr; + } + return buffer; +} + +std::shared_ptr ContextMTL::GetPermanentsAllocator() const { + return permanents_allocator_; +} + +std::shared_ptr ContextMTL::GetTransientsAllocator() const { + return transients_allocator_; +} + +id ContextMTL::GetMTLDevice() const { + return device_; +} + +} // namespace impeller diff --git a/impeller/renderer/backend/metal/device_buffer_mtl.h b/impeller/renderer/backend/metal/device_buffer_mtl.h index bc7386c39bb70..7523cc4cacd6c 100644 --- a/impeller/renderer/backend/metal/device_buffer_mtl.h +++ b/impeller/renderer/backend/metal/device_buffer_mtl.h @@ -4,18 +4,56 @@ #pragma once +#include + #include "flutter/fml/macros.h" +#include "impeller/renderer/backend/metal/backend_cast.h" #include "impeller/renderer/device_buffer.h" namespace impeller { -class DeviceBufferMTL final : public DeviceBuffer { +class DeviceBufferMTL final + : public DeviceBuffer, + public BackendCast { public: DeviceBufferMTL(); + // |DeviceBuffer| ~DeviceBufferMTL() override; + id GetMTLBuffer() const; + private: + friend class AllocatorMTL; + + const id buffer_; + const size_t size_; + const StorageMode mode_; + + DeviceBufferMTL(id buffer, size_t size, StorageMode mode); + + // |DeviceBuffer| + bool CopyHostBuffer(const uint8_t* source, + Range source_range, + size_t offset) override; + + // |DeviceBuffer| + std::shared_ptr MakeTexture(TextureDescriptor desc, + size_t offset) const override; + + // |DeviceBuffer| + bool SetLabel(const std::string& label) override; + + // |DeviceBuffer| + bool SetLabel(const std::string& label, Range range) override; + + // |DeviceBuffer| + BufferView AsBufferView() const override; + + // |Buffer| + std::shared_ptr GetDeviceBuffer( + Allocator& allocator) const override; + FML_DISALLOW_COPY_AND_ASSIGN(DeviceBufferMTL); }; diff --git a/impeller/renderer/backend/metal/device_buffer_mtl.mm b/impeller/renderer/backend/metal/device_buffer_mtl.mm index e69de29bb2d1d..4469cc349752c 100644 --- a/impeller/renderer/backend/metal/device_buffer_mtl.mm +++ b/impeller/renderer/backend/metal/device_buffer_mtl.mm @@ -0,0 +1,105 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "impeller/renderer/backend/metal/device_buffer_mtl.h" + +#include "flutter/fml/logging.h" +#include "impeller/renderer/backend/metal/formats_mtl.h" + +namespace impeller { + +DeviceBufferMTL::DeviceBufferMTL(id buffer, + size_t size, + StorageMode mode) + : buffer_(buffer), size_(size), mode_(mode) {} + +DeviceBufferMTL::~DeviceBufferMTL() = default; + +id DeviceBufferMTL::GetMTLBuffer() const { + return buffer_; +} + +std::shared_ptr DeviceBufferMTL::MakeTexture(TextureDescriptor desc, + size_t offset) const { + if (!desc.IsValid() || !buffer_) { + return nullptr; + } + + // Avoid overruns. + if (offset + desc.GetSizeOfBaseMipLevel() > size_) { + FML_DLOG(ERROR) << "Avoiding buffer overrun when creating texture."; + return nullptr; + } + + auto texture = [buffer_ newTextureWithDescriptor:ToMTLTextureDescriptor(desc) + offset:offset + bytesPerRow:desc.GetBytesPerRow()]; + if (!texture) { + return nullptr; + } + + return std::make_shared(desc, texture); +} + +[[nodiscard]] bool DeviceBufferMTL::CopyHostBuffer(const uint8_t* source, + Range source_range, + size_t offset) { + if (mode_ != StorageMode::kHostVisible) { + // One of the storage modes where a transfer queue must be used. + return false; + } + + if (offset + source_range.length > size_) { + // Out of bounds of this buffer. + return false; + } + + auto dest = static_cast(buffer_.contents); + + if (!dest) { + return false; + } + + if (source) { + ::memmove(dest + offset, source + source_range.offset, source_range.length); + } + + if (Allocator::RequiresExplicitHostSynchronization(mode_)) { + [buffer_ didModifyRange:NSMakeRange(offset, source_range.length)]; + } + + return true; +} + +// |Buffer| +std::shared_ptr DeviceBufferMTL::GetDeviceBuffer( + Allocator& allocator) const { + return shared_from_this(); +} + +bool DeviceBufferMTL::SetLabel(const std::string& label) { + if (label.empty()) { + return false; + } + [buffer_ setLabel:@(label.c_str())]; + return true; +} + +bool DeviceBufferMTL::SetLabel(const std::string& label, Range range) { + if (label.empty()) { + return false; + } + [buffer_ addDebugMarker:@(label.c_str()) + range:NSMakeRange(range.offset, range.length)]; + return true; +} + +BufferView DeviceBufferMTL::AsBufferView() const { + BufferView view; + view.buffer = shared_from_this(); + view.range = {0u, size_}; + return view; +} + +} // namespace impeller diff --git a/impeller/renderer/backend/metal/formats_metal.h b/impeller/renderer/backend/metal/formats_metal.h deleted file mode 100644 index 10e536e31f07b..0000000000000 --- a/impeller/renderer/backend/metal/formats_metal.h +++ /dev/null @@ -1,263 +0,0 @@ -// Copyright 2013 The Flutter Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#pragma once - -#include - -#include - -#include "flutter/fml/macros.h" -#include "impeller/geometry/color.h" -#include "impeller/renderer/formats.h" -#include "impeller/renderer/texture_descriptor.h" - -namespace impeller { - -class RenderPassDescriptor; - -constexpr MTLPixelFormat ToMTLPixelFormat(PixelFormat format) { - switch (format) { - case PixelFormat::kUnknown: - return MTLPixelFormatInvalid; - case PixelFormat::kB8G8R8A8UNormInt: - return MTLPixelFormatBGRA8Unorm; - case PixelFormat::kB8G8R8A8UNormIntSRGB: - return MTLPixelFormatBGRA8Unorm_sRGB; - case PixelFormat::kD32FloatS8UNormInt: - return MTLPixelFormatDepth32Float_Stencil8; - case PixelFormat::kR8G8B8A8UNormInt: - return MTLPixelFormatRGBA8Unorm; - case PixelFormat::kR8G8B8A8UNormIntSRGB: - return MTLPixelFormatRGBA8Unorm_sRGB; - } - return MTLPixelFormatInvalid; -}; - -constexpr MTLBlendFactor ToMTLBlendFactor(BlendFactor type) { - switch (type) { - case BlendFactor::kZero: - return MTLBlendFactorZero; - case BlendFactor::kOne: - return MTLBlendFactorOne; - case BlendFactor::kSourceColor: - return MTLBlendFactorSourceColor; - case BlendFactor::kOneMinusSourceColor: - return MTLBlendFactorOneMinusSourceColor; - case BlendFactor::kSourceAlpha: - return MTLBlendFactorSourceAlpha; - case BlendFactor::kOneMinusSourceAlpha: - return MTLBlendFactorOneMinusSourceAlpha; - case BlendFactor::kDestinationColor: - return MTLBlendFactorDestinationColor; - case BlendFactor::kOneMinusDestinationColor: - return MTLBlendFactorOneMinusDestinationColor; - case BlendFactor::kDestinationAlpha: - return MTLBlendFactorDestinationAlpha; - case BlendFactor::kOneMinusDestinationAlpha: - return MTLBlendFactorOneMinusDestinationAlpha; - case BlendFactor::kSourceAlphaSaturated: - return MTLBlendFactorSourceAlphaSaturated; - case BlendFactor::kBlendColor: - return MTLBlendFactorBlendColor; - case BlendFactor::kOneMinusBlendColor: - return MTLBlendFactorOneMinusBlendColor; - case BlendFactor::kBlendAlpha: - return MTLBlendFactorBlendAlpha; - case BlendFactor::kOneMinusBlendAlpha: - return MTLBlendFactorOneMinusBlendAlpha; - } - return MTLBlendFactorZero; -}; - -constexpr MTLPrimitiveType ToMTLPrimitiveType(PrimitiveType type) { - switch (type) { - case PrimitiveType::kTriangle: - return MTLPrimitiveTypeTriangle; - case PrimitiveType::kTriangleStrip: - return MTLPrimitiveTypeTriangleStrip; - case PrimitiveType::kLine: - return MTLPrimitiveTypeLine; - case PrimitiveType::kLineStrip: - return MTLPrimitiveTypeLineStrip; - case PrimitiveType::kPoint: - return MTLPrimitiveTypePoint; - } - return MTLPrimitiveTypePoint; -} - -constexpr MTLBlendOperation ToMTLBlendOperation(BlendOperation type) { - switch (type) { - case BlendOperation::kAdd: - return MTLBlendOperationAdd; - case BlendOperation::kSubtract: - return MTLBlendOperationSubtract; - case BlendOperation::kReverseSubtract: - return MTLBlendOperationReverseSubtract; - case BlendOperation::kMin: - return MTLBlendOperationMin; - case BlendOperation::kMax: - return MTLBlendOperationMax; - } - return MTLBlendOperationAdd; -}; - -constexpr MTLColorWriteMask ToMTLColorWriteMask( - std::underlying_type_t type) { - using UnderlyingType = decltype(type); - - MTLColorWriteMask mask = MTLColorWriteMaskNone; - - if (type & static_cast(ColorWriteMask::kRed)) { - mask |= MTLColorWriteMaskRed; - } - - if (type & static_cast(ColorWriteMask::kGreen)) { - mask |= MTLColorWriteMaskGreen; - } - - if (type & static_cast(ColorWriteMask::kBlue)) { - mask |= MTLColorWriteMaskBlue; - } - - if (type & static_cast(ColorWriteMask::kAlpha)) { - mask |= MTLColorWriteMaskAlpha; - } - - return mask; -}; - -constexpr MTLCompareFunction ToMTLCompareFunction(CompareFunction func) { - switch (func) { - case CompareFunction::kNever: - return MTLCompareFunctionNever; - case CompareFunction::kLess: - return MTLCompareFunctionLess; - case CompareFunction::kEqual: - return MTLCompareFunctionEqual; - case CompareFunction::kLessEqual: - return MTLCompareFunctionLessEqual; - case CompareFunction::kGreater: - return MTLCompareFunctionGreater; - case CompareFunction::kNotEqual: - return MTLCompareFunctionNotEqual; - case CompareFunction::kGreaterEqual: - return MTLCompareFunctionGreaterEqual; - case CompareFunction::kAlways: - return MTLCompareFunctionAlways; - } - return MTLCompareFunctionAlways; -}; - -constexpr MTLStencilOperation ToMTLStencilOperation(StencilOperation op) { - switch (op) { - case StencilOperation::kKeep: - return MTLStencilOperationKeep; - case StencilOperation::kZero: - return MTLStencilOperationZero; - case StencilOperation::kSetToReferneceValue: - return MTLStencilOperationReplace; - case StencilOperation::kIncrementClamp: - return MTLStencilOperationIncrementClamp; - case StencilOperation::kDecrementClamp: - return MTLStencilOperationDecrementClamp; - case StencilOperation::kInvert: - return MTLStencilOperationInvert; - case StencilOperation::kIncrementWrap: - return MTLStencilOperationIncrementWrap; - case StencilOperation::kDecrementWrap: - return MTLStencilOperationDecrementWrap; - } - return MTLStencilOperationKeep; -}; - -constexpr MTLLoadAction ToMTLLoadAction(LoadAction action) { - switch (action) { - case LoadAction::kDontCare: - return MTLLoadActionDontCare; - case LoadAction::kLoad: - return MTLLoadActionLoad; - case LoadAction::kClear: - return MTLLoadActionClear; - } - - return MTLLoadActionDontCare; -} - -constexpr LoadAction FromMTLLoadAction(MTLLoadAction action) { - switch (action) { - case MTLLoadActionDontCare: - return LoadAction::kDontCare; - case MTLLoadActionLoad: - return LoadAction::kLoad; - case MTLLoadActionClear: - return LoadAction::kClear; - default: - break; - } - - return LoadAction::kDontCare; -} - -constexpr MTLStoreAction ToMTLStoreAction(StoreAction action) { - switch (action) { - case StoreAction::kDontCare: - return MTLStoreActionDontCare; - case StoreAction::kStore: - return MTLStoreActionStore; - } - return MTLStoreActionDontCare; -} - -constexpr StoreAction FromMTLStoreAction(MTLStoreAction action) { - switch (action) { - case MTLStoreActionDontCare: - return StoreAction::kDontCare; - case MTLStoreActionStore: - return StoreAction::kStore; - default: - break; - } - return StoreAction::kDontCare; -} - -constexpr MTLSamplerMinMagFilter ToMTLSamplerMinMagFilter(MinMagFilter filter) { - switch (filter) { - case MinMagFilter::kNearest: - return MTLSamplerMinMagFilterNearest; - case MinMagFilter::kLinear: - return MTLSamplerMinMagFilterLinear; - } - return MTLSamplerMinMagFilterNearest; -} - -constexpr MTLSamplerAddressMode ToMTLSamplerAddressMode( - SamplerAddressMode mode) { - switch (mode) { - case SamplerAddressMode::kClampToEdge: - return MTLSamplerAddressModeClampToEdge; - case SamplerAddressMode::kRepeat: - return MTLSamplerAddressModeRepeat; - case SamplerAddressMode::kMirror: - return MTLSamplerAddressModeMirrorRepeat; - } - return MTLSamplerAddressModeClampToEdge; -} - -constexpr MTLClearColor ToMTLClearColor(const Color& color) { - return MTLClearColorMake(color.red, color.green, color.blue, color.alpha); -} - -MTLRenderPipelineColorAttachmentDescriptor* -ToMTLRenderPipelineColorAttachmentDescriptor( - PipelineColorAttachment descriptor); - -MTLDepthStencilDescriptor* ToMTLDepthStencilDescriptor( - std::optional depth, - std::optional front, - std::optional back); - -MTLTextureDescriptor* ToMTLTextureDescriptor(const TextureDescriptor& desc); - -} // namespace impeller diff --git a/impeller/renderer/formats_metal.h b/impeller/renderer/backend/metal/formats_mtl.h similarity index 100% rename from impeller/renderer/formats_metal.h rename to impeller/renderer/backend/metal/formats_mtl.h index 10e536e31f07b..6b3f6e25c3de8 100644 --- a/impeller/renderer/formats_metal.h +++ b/impeller/renderer/backend/metal/formats_mtl.h @@ -4,10 +4,10 @@ #pragma once -#include - #include +#include + #include "flutter/fml/macros.h" #include "impeller/geometry/color.h" #include "impeller/renderer/formats.h" diff --git a/impeller/renderer/backend/metal/formats_metal.mm b/impeller/renderer/backend/metal/formats_mtl.mm similarity index 100% rename from impeller/renderer/backend/metal/formats_metal.mm rename to impeller/renderer/backend/metal/formats_mtl.mm diff --git a/impeller/renderer/backend/metal/pipeline_library_mtl.h b/impeller/renderer/backend/metal/pipeline_library_mtl.h index ef5ccbaadaf1a..612a95a1de611 100644 --- a/impeller/renderer/backend/metal/pipeline_library_mtl.h +++ b/impeller/renderer/backend/metal/pipeline_library_mtl.h @@ -4,18 +4,43 @@ #pragma once +#include + +#include +#include + #include "flutter/fml/macros.h" #include "impeller/renderer/pipeline_library.h" namespace impeller { +class ContextMTL; + class PipelineLibraryMTL final : public PipelineLibrary { public: PipelineLibraryMTL(); + // |PipelineLibrary| ~PipelineLibraryMTL() override; private: + friend ContextMTL; + + using Pipelines = std::unordered_map, + ComparableHash, + ComparableEqual>; + id device_ = nullptr; + Pipelines pipelines_; + + PipelineLibraryMTL(id device); + + void SavePipeline(PipelineDescriptor descriptor, + std::shared_ptr pipeline); + + // |PipelineLibrary| + PipelineFuture GetRenderPipeline(PipelineDescriptor descriptor) override; + FML_DISALLOW_COPY_AND_ASSIGN(PipelineLibraryMTL); }; diff --git a/impeller/renderer/backend/metal/pipeline_library_mtl.mm b/impeller/renderer/backend/metal/pipeline_library_mtl.mm index e69de29bb2d1d..73d0f26e725bc 100644 --- a/impeller/renderer/backend/metal/pipeline_library_mtl.mm +++ b/impeller/renderer/backend/metal/pipeline_library_mtl.mm @@ -0,0 +1,103 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "impeller/renderer/backend/metal/pipeline_library_mtl.h" + +#include "impeller/renderer/backend/metal/formats_mtl.h" +#include "impeller/renderer/backend/metal/pipeline_mtl.h" +#include "impeller/renderer/backend/metal/shader_function_mtl.h" + +namespace impeller { + +PipelineLibraryMTL::PipelineLibraryMTL(id device) + : device_(device) {} + +PipelineLibraryMTL::~PipelineLibraryMTL() = default; + +// TODO(csg): Make PipelineDescriptor a struct and move this to formats_mtl. +static MTLRenderPipelineDescriptor* GetMTLRenderPipelineDescriptor( + const PipelineDescriptor& desc) { + auto descriptor = [[MTLRenderPipelineDescriptor alloc] init]; + descriptor.label = @(desc.GetLabel().c_str()); + descriptor.sampleCount = desc.GetSampleCount(); + + for (const auto& entry : desc.GetStageEntrypoints()) { + if (entry.first == ShaderStage::kVertex) { + descriptor.vertexFunction = + ShaderFunctionMTL::Cast(*entry.second).GetMTLFunction(); + } + if (entry.first == ShaderStage::kFragment) { + descriptor.fragmentFunction = + ShaderFunctionMTL::Cast(*entry.second).GetMTLFunction(); + } + } + + if (const auto& vertex_descriptor = desc.GetVertexDescriptor()) { + descriptor.vertexDescriptor = vertex_descriptor->GetMTLVertexDescriptor(); + } + + for (const auto& item : desc.GetColorAttachmentDescriptors()) { + descriptor.colorAttachments[item.first] = + ToMTLRenderPipelineColorAttachmentDescriptor(item.second); + } + + descriptor.depthAttachmentPixelFormat = + ToMTLPixelFormat(desc.GetDepthPixelFormat()); + descriptor.stencilAttachmentPixelFormat = + ToMTLPixelFormat(desc.GetStencilPixelFormat()); + + return descriptor; +} + +// TODO(csg): Make PipelineDescriptor a struct and move this to formats_mtl. +static id CreateDepthStencilDescriptor( + const PipelineDescriptor& desc, + id device) { + auto descriptor = ToMTLDepthStencilDescriptor( + desc.GetDepthStencilAttachmentDescriptor(), // + desc.GetFrontStencilAttachmentDescriptor(), // + desc.GetBackStencilAttachmentDescriptor() // + ); + return [device newDepthStencilStateWithDescriptor:descriptor]; +} + +std::future> PipelineLibraryMTL::GetRenderPipeline( + PipelineDescriptor descriptor) { + auto promise = std::make_shared>>(); + auto future = promise->get_future(); + if (auto found = pipelines_.find(descriptor); found != pipelines_.end()) { + promise->set_value(nullptr); + return future; + } + + auto thiz = shared_from_this(); + + auto completion_handler = + ^(id _Nullable render_pipeline_state, + NSError* _Nullable error) { + if (error != nil) { + FML_LOG(ERROR) << "Could not create render pipeline: " + << error.localizedDescription.UTF8String; + promise->set_value(nullptr); + } else { + auto new_pipeline = std::shared_ptr(new PipelineMTL( + render_pipeline_state, + CreateDepthStencilDescriptor(descriptor, device_))); + promise->set_value(new_pipeline); + this->SavePipeline(descriptor, new_pipeline); + } + }; + [device_ newRenderPipelineStateWithDescriptor:GetMTLRenderPipelineDescriptor( + descriptor) + completionHandler:completion_handler]; + return future; +} + +void PipelineLibraryMTL::SavePipeline( + PipelineDescriptor descriptor, + std::shared_ptr pipeline) { + pipelines_[descriptor] = std::move(pipeline); +} + +} // namespace impeller diff --git a/impeller/renderer/backend/metal/pipeline_mtl.h b/impeller/renderer/backend/metal/pipeline_mtl.h index 7ef22a2b0e7c1..c115880b0ac20 100644 --- a/impeller/renderer/backend/metal/pipeline_mtl.h +++ b/impeller/renderer/backend/metal/pipeline_mtl.h @@ -4,18 +4,38 @@ #pragma once +#include + #include "flutter/fml/macros.h" +#include "impeller/renderer/backend/metal/backend_cast.h" #include "impeller/renderer/pipeline.h" namespace impeller { -class PipelineMTL final : public Pipeline { +class PipelineMTL final : public Pipeline, + public BackendCast { public: - PipelineMTL(); - + // |PipelineMTL| ~PipelineMTL() override; + id GetMTLRenderPipelineState() const; + + id GetMTLDepthStencilState() const; + private: + friend class PipelineLibraryMTL; + + Type type_ = Type::kUnknown; + id pipeline_state_; + id depth_stencil_state_; + bool is_valid_ = false; + + PipelineMTL(id state, + id depth_stencil_state); + + // |PipelineMTL| + bool IsValid() const override; + FML_DISALLOW_COPY_AND_ASSIGN(PipelineMTL); }; diff --git a/impeller/renderer/backend/metal/pipeline_mtl.mm b/impeller/renderer/backend/metal/pipeline_mtl.mm index e69de29bb2d1d..9ff5627697f7a 100644 --- a/impeller/renderer/backend/metal/pipeline_mtl.mm +++ b/impeller/renderer/backend/metal/pipeline_mtl.mm @@ -0,0 +1,33 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "impeller/renderer/backend/metal/pipeline_mtl.h" + +namespace impeller { + +PipelineMTL::PipelineMTL(id state, + id depth_stencil_state) + : pipeline_state_(state), depth_stencil_state_(depth_stencil_state) { + if (!pipeline_state_) { + return; + } + type_ = Type::kRender; + is_valid_ = true; +} + +PipelineMTL::~PipelineMTL() = default; + +bool PipelineMTL::IsValid() const { + return is_valid_; +} + +id PipelineMTL::GetMTLRenderPipelineState() const { + return pipeline_state_; +} + +id PipelineMTL::GetMTLDepthStencilState() const { + return depth_stencil_state_; +} + +} // namespace impeller diff --git a/impeller/renderer/backend/metal/render_pass_mtl.h b/impeller/renderer/backend/metal/render_pass_mtl.h index b11658c19dad2..0726dca9620ff 100644 --- a/impeller/renderer/backend/metal/render_pass_mtl.h +++ b/impeller/renderer/backend/metal/render_pass_mtl.h @@ -4,18 +4,49 @@ #pragma once +#include + #include "flutter/fml/macros.h" #include "impeller/renderer/render_pass.h" +#include "impeller/renderer/render_pass_descriptor.h" namespace impeller { class RenderPassMTL final : public RenderPass { public: - RenderPassMTL(); - + // |RenderPass| ~RenderPassMTL() override; private: + friend class CommandBufferMTL; + + id buffer_ = nil; + MTLRenderPassDescriptor* desc_ = nil; + std::vector commands_; + std::shared_ptr transients_buffer_; + std::string label_; + bool is_valid_ = false; + + RenderPassMTL(id buffer, const RenderPassDescriptor& desc); + + // |RenderPass| + bool IsValid() const override; + + // |RenderPass| + void SetLabel(std::string label) override; + + // |RenderPass| + HostBuffer& GetTransientsBuffer() override; + + // |RenderPass| + bool RecordCommand(Command command) override; + + // |RenderPass| + bool Commit(Allocator& transients_allocator) const override; + + bool EncodeCommands(Allocator& transients_allocator, + id pass) const; + FML_DISALLOW_COPY_AND_ASSIGN(RenderPassMTL); }; diff --git a/impeller/renderer/backend/metal/render_pass_mtl.mm b/impeller/renderer/backend/metal/render_pass_mtl.mm index e69de29bb2d1d..29d02f81d3da8 100644 --- a/impeller/renderer/backend/metal/render_pass_mtl.mm +++ b/impeller/renderer/backend/metal/render_pass_mtl.mm @@ -0,0 +1,411 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "impeller/renderer/backend/metal/render_pass_mtl.h" + +#include "flutter/fml/closure.h" +#include "flutter/fml/logging.h" +#include "impeller/base/base.h" +#include "impeller/renderer/backend/metal/device_buffer_mtl.h" +#include "impeller/renderer/backend/metal/formats_mtl.h" +#include "impeller/renderer/backend/metal/pipeline_mtl.h" +#include "impeller/renderer/backend/metal/sampler_mtl.h" +#include "impeller/renderer/backend/metal/texture_mtl.h" +#include "impeller/renderer/host_buffer.h" +#include "impeller/renderer/shader_types.h" + +namespace impeller { + +static bool ConfigureAttachment(const RenderPassAttachment& desc, + MTLRenderPassAttachmentDescriptor* attachment) { + if (!desc.texture) { + return false; + } + + attachment.texture = TextureMTL::Cast(*desc.texture).GetMTLTexture(); + attachment.loadAction = ToMTLLoadAction(desc.load_action); + attachment.storeAction = ToMTLStoreAction(desc.store_action); + return true; +} + +static bool ConfigureColorAttachment( + const RenderPassColorAttachment& desc, + MTLRenderPassColorAttachmentDescriptor* attachment) { + if (!ConfigureAttachment(desc, attachment)) { + return false; + } + attachment.clearColor = ToMTLClearColor(desc.clear_color); + return true; +} + +static bool ConfigureDepthAttachment( + const RenderPassDepthAttachment& desc, + MTLRenderPassDepthAttachmentDescriptor* attachment) { + if (!ConfigureAttachment(desc, attachment)) { + return false; + } + attachment.clearDepth = desc.clear_depth; + return true; +} + +static bool ConfigureStencilAttachment( + const RenderPassStencilAttachment& desc, + MTLRenderPassStencilAttachmentDescriptor* attachment) { + if (!ConfigureAttachment(desc, attachment)) { + return false; + } + attachment.clearStencil = desc.clear_stencil; + return true; +} + +MTLRenderPassDescriptor* RenderPassDescriptor::ToMTLRenderPassDescriptor() + const { + auto result = [MTLRenderPassDescriptor renderPassDescriptor]; + + for (const auto& color : colors_) { + if (!ConfigureColorAttachment(color.second, + result.colorAttachments[color.first])) { + FML_LOG(ERROR) << "Could not configure color attachment at index " + << color.first; + return nil; + } + } + + if (depth_.has_value() && + !ConfigureDepthAttachment(depth_.value(), result.depthAttachment)) { + return nil; + } + + if (stencil_.has_value() && + !ConfigureStencilAttachment(stencil_.value(), result.stencilAttachment)) { + return nil; + } + + return result; +} + +RenderPassMTL::RenderPassMTL(id buffer, + const RenderPassDescriptor& desc) + : buffer_(buffer), + desc_(desc.ToMTLRenderPassDescriptor()), + transients_buffer_(HostBuffer::Create()) { + if (!buffer_ || !desc_) { + return; + } + is_valid_ = true; +} + +RenderPassMTL::~RenderPassMTL() = default; + +HostBuffer& RenderPassMTL::GetTransientsBuffer() { + return *transients_buffer_; +} + +bool RenderPassMTL::IsValid() const { + return is_valid_; +} + +void RenderPassMTL::SetLabel(std::string label) { + label_ = std::move(label); + transients_buffer_->SetLabel(SPrintF("%s Transients", label_.c_str())); +} + +bool RenderPassMTL::Commit(Allocator& transients_allocator) const { + if (!IsValid()) { + return false; + } + auto pass = [buffer_ renderCommandEncoderWithDescriptor:desc_]; + + if (!pass) { + return false; + } + + if (!label_.empty()) { + [pass setLabel:@(label_.c_str())]; + } + + // Success or failure, the pass must end. The buffer can only process one pass + // at a time. + fml::ScopedCleanupClosure auto_end([pass]() { [pass endEncoding]; }); + + return EncodeCommands(transients_allocator, pass); +} + +//----------------------------------------------------------------------------- +/// @brief Ensures that bindings on the pass are not redundantly set or +/// updated. Avoids making the driver do additional checks and makes +/// the frame insights during profiling and instrumentation not +/// complain about the same. +/// +/// There should be no change to rendering if this caching was +/// absent. +/// +struct PassBindingsCache { + PassBindingsCache(id pass) : pass_(pass) {} + + PassBindingsCache(const PassBindingsCache&) = delete; + + PassBindingsCache(PassBindingsCache&&) = delete; + + void SetRenderPipelineState(id pipeline) { + if (pipeline == pipeline_) { + return; + } + pipeline_ = pipeline; + [pass_ setRenderPipelineState:pipeline_]; + } + + void SetDepthStencilState(id depth_stencil) { + if (depth_stencil_ == depth_stencil) { + return; + } + depth_stencil_ = depth_stencil; + [pass_ setDepthStencilState:depth_stencil_]; + } + + bool SetBuffer(ShaderStage stage, + uint64_t index, + uint64_t offset, + id buffer) { + auto& buffers_map = buffers_[stage]; + auto found = buffers_map.find(index); + if (found != buffers_map.end() && found->second.buffer == buffer) { + // The right buffer is bound. Check if its offset needs to be updated. + if (found->second.offset == offset) { + // Buffer and its offset is identical. Nothing to do. + return true; + } + + // Only the offset needs to be updated. + found->second.offset = offset; + + switch (stage) { + case ShaderStage::kVertex: + [pass_ setVertexBufferOffset:offset atIndex:index]; + return true; + case ShaderStage::kFragment: + [pass_ setFragmentBufferOffset:offset atIndex:index]; + return true; + default: + FML_DCHECK(false) + << "Cannot update buffer offset of an unknown stage."; + return false; + } + return true; + } + buffers_map[index] = {buffer, offset}; + switch (stage) { + case ShaderStage::kVertex: + [pass_ setVertexBuffer:buffer offset:offset atIndex:index]; + return true; + case ShaderStage::kFragment: + [pass_ setFragmentBuffer:buffer offset:offset atIndex:index]; + return true; + default: + FML_DCHECK(false) << "Cannot bind buffer to unknown shader stage."; + return false; + } + return false; + } + + bool SetTexture(ShaderStage stage, uint64_t index, id texture) { + auto& texture_map = textures_[stage]; + auto found = texture_map.find(index); + if (found != texture_map.end() && found->second == texture) { + // Already bound. + return true; + } + texture_map[index] = texture; + switch (stage) { + case ShaderStage::kVertex: + [pass_ setVertexTexture:texture atIndex:index]; + return true; + case ShaderStage::kFragment: + [pass_ setFragmentTexture:texture atIndex:index]; + return true; + default: + FML_DCHECK(false) << "Cannot bind buffer to unknown shader stage."; + return false; + } + return false; + } + + bool SetSampler(ShaderStage stage, + uint64_t index, + id sampler) { + auto& sampler_map = samplers_[stage]; + auto found = sampler_map.find(index); + if (found != sampler_map.end() && found->second == sampler) { + // Already bound. + return true; + } + sampler_map[index] = sampler; + switch (stage) { + case ShaderStage::kVertex: + [pass_ setVertexSamplerState:sampler atIndex:index]; + return true; + case ShaderStage::kFragment: + [pass_ setFragmentSamplerState:sampler atIndex:index]; + return true; + default: + FML_DCHECK(false) << "Cannot bind buffer to unknown shader stage."; + return false; + } + return false; + } + + private: + struct BufferOffsetPair { + id buffer = nullptr; + size_t offset = 0u; + }; + using BufferMap = std::map; + using TextureMap = std::map>; + using SamplerMap = std::map>; + + const id pass_; + id pipeline_ = nullptr; + id depth_stencil_ = nullptr; + std::map buffers_; + std::map textures_; + std::map samplers_; +}; + +static bool Bind(PassBindingsCache& pass, + Allocator& allocator, + ShaderStage stage, + size_t bind_index, + const BufferView& view) { + if (!view.buffer) { + return false; + } + + auto device_buffer = view.buffer->GetDeviceBuffer(allocator); + if (!device_buffer) { + return false; + } + + auto buffer = DeviceBufferMTL::Cast(*device_buffer).GetMTLBuffer(); + // The Metal call is a void return and we don't want to make it on nil. + if (!buffer) { + return false; + } + + return pass.SetBuffer(stage, bind_index, view.range.offset, buffer); +} + +static bool Bind(PassBindingsCache& pass, + ShaderStage stage, + size_t bind_index, + const Texture& texture) { + if (!texture.IsValid()) { + return false; + } + + return pass.SetTexture(stage, bind_index, + TextureMTL::Cast(texture).GetMTLTexture()); +} + +static bool Bind(PassBindingsCache& pass, + ShaderStage stage, + size_t bind_index, + const Sampler& sampler) { + if (!sampler.IsValid()) { + return false; + } + + return pass.SetSampler(stage, bind_index, + SamplerMTL::Cast(sampler).GetMTLSamplerState()); +} + +bool RenderPassMTL::EncodeCommands(Allocator& allocator, + id pass) const { + PassBindingsCache pass_bindings(pass); + auto bind_stage_resources = [&allocator, &pass_bindings]( + const Bindings& bindings, + ShaderStage stage) -> bool { + for (const auto buffer : bindings.buffers) { + if (!Bind(pass_bindings, allocator, stage, buffer.first, buffer.second)) { + return false; + } + } + for (const auto texture : bindings.textures) { + if (!Bind(pass_bindings, stage, texture.first, *texture.second)) { + return false; + } + } + for (const auto sampler : bindings.samplers) { + if (!Bind(pass_bindings, stage, sampler.first, *sampler.second)) { + return false; + } + } + return true; + }; + + fml::closure pop_debug_marker = [pass]() { [pass popDebugGroup]; }; + for (const auto& command : commands_) { + if (command.index_count == 0u) { + FML_DLOG(ERROR) << "Zero index count in render pass command."; + continue; + } + + fml::ScopedCleanupClosure auto_pop_debug_marker(pop_debug_marker); + if (!command.label.empty()) { + [pass pushDebugGroup:@(command.label.c_str())]; + } else { + auto_pop_debug_marker.Release(); + } + pass_bindings.SetRenderPipelineState( + PipelineMTL::Cast(*command.pipeline).GetMTLRenderPipelineState()); + pass_bindings.SetDepthStencilState( + PipelineMTL::Cast(*command.pipeline).GetMTLDepthStencilState()); + [pass setFrontFacingWinding:command.winding == WindingOrder::kClockwise + ? MTLWindingClockwise + : MTLWindingCounterClockwise]; + [pass setCullMode:MTLCullModeNone]; + if (!bind_stage_resources(command.vertex_bindings, ShaderStage::kVertex)) { + return false; + } + if (!bind_stage_resources(command.fragment_bindings, + ShaderStage::kFragment)) { + return false; + } + auto index_buffer = command.index_buffer.buffer; + if (!index_buffer) { + return false; + } + auto device_buffer = index_buffer->GetDeviceBuffer(allocator); + if (!device_buffer) { + return false; + } + auto mtl_index_buffer = + DeviceBufferMTL::Cast(*device_buffer).GetMTLBuffer(); + if (!mtl_index_buffer) { + return false; + } + FML_DCHECK(command.index_count * sizeof(uint32_t) == + command.index_buffer.range.length); + // Returns void. All error checking must be done by this point. + [pass drawIndexedPrimitives:ToMTLPrimitiveType(command.primitive_type) + indexCount:command.index_count + indexType:MTLIndexTypeUInt32 + indexBuffer:mtl_index_buffer + indexBufferOffset:command.index_buffer.range.offset + instanceCount:1u + baseVertex:0u + baseInstance:0u]; + } + return true; +} + +bool RenderPassMTL::RecordCommand(Command command) { + if (!command) { + return false; + } + + commands_.emplace_back(std::move(command)); + return true; +} + +} // namespace impeller diff --git a/impeller/renderer/backend/metal/renderer_mtl.mm b/impeller/renderer/backend/metal/renderer_mtl.mm deleted file mode 100644 index e69de29bb2d1d..0000000000000 diff --git a/impeller/renderer/backend/metal/sampler_library_mtl.h b/impeller/renderer/backend/metal/sampler_library_mtl.h new file mode 100644 index 0000000000000..71b8acb56312e --- /dev/null +++ b/impeller/renderer/backend/metal/sampler_library_mtl.h @@ -0,0 +1,46 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#pragma once + +#include + +#include +#include + +#include "flutter/fml/macros.h" +#include "impeller/renderer/backend/metal/backend_cast.h" +#include "impeller/renderer/comparable.h" +#include "impeller/renderer/sampler_descriptor.h" +#include "impeller/renderer/sampler_library.h" + +namespace impeller { + +class SamplerLibraryMTL final + : public SamplerLibrary, + public BackendCast { + public: + // |SamplerLibrary| + ~SamplerLibraryMTL() override; + + private: + friend class ContextMTL; + + using CachedSamplers = std::unordered_map, + ComparableHash, + ComparableEqual>; + id device_ = nullptr; + CachedSamplers samplers_; + + SamplerLibraryMTL(id device); + + // |SamplerLibrary| + std::shared_ptr GetSampler( + SamplerDescriptor descriptor) override; + + FML_DISALLOW_COPY_AND_ASSIGN(SamplerLibraryMTL); +}; + +} // namespace impeller diff --git a/impeller/renderer/sampler_descriptor.mm b/impeller/renderer/backend/metal/sampler_library_mtl.mm similarity index 69% rename from impeller/renderer/sampler_descriptor.mm rename to impeller/renderer/backend/metal/sampler_library_mtl.mm index 72d7509c2ccaf..313e670971023 100644 --- a/impeller/renderer/sampler_descriptor.mm +++ b/impeller/renderer/backend/metal/sampler_library_mtl.mm @@ -2,18 +2,18 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -#include "impeller/renderer/sampler_descriptor.h" +#include "impeller/renderer/backend/metal/sampler_library_mtl.h" -#include "impeller/renderer/formats_metal.h" -#include "impeller/renderer/sampler.h" +#include "impeller/renderer/backend/metal/formats_mtl.h" +#include "impeller/renderer/backend/metal/sampler_mtl.h" namespace impeller { -SamplerLibrary::SamplerLibrary(id device) : device_(device) {} +SamplerLibraryMTL::SamplerLibraryMTL(id device) : device_(device) {} -SamplerLibrary::~SamplerLibrary() = default; +SamplerLibraryMTL::~SamplerLibraryMTL() = default; -std::shared_ptr SamplerLibrary::GetSampler( +std::shared_ptr SamplerLibraryMTL::GetSampler( SamplerDescriptor descriptor) { auto found = samplers_.find(descriptor); if (found != samplers_.end()) { @@ -32,7 +32,7 @@ if (!mtl_sampler) { return nullptr; } - auto sampler = std::shared_ptr(new Sampler(mtl_sampler)); + auto sampler = std::shared_ptr(new SamplerMTL(mtl_sampler)); if (!sampler->IsValid()) { return nullptr; } diff --git a/impeller/renderer/backend/metal/sampler_mtl.h b/impeller/renderer/backend/metal/sampler_mtl.h index 9a80eb8d8ae41..de80442942ab4 100644 --- a/impeller/renderer/backend/metal/sampler_mtl.h +++ b/impeller/renderer/backend/metal/sampler_mtl.h @@ -4,18 +4,36 @@ #pragma once +#include + #include "flutter/fml/macros.h" +#include "impeller/renderer/backend/metal/backend_cast.h" #include "impeller/renderer/sampler.h" namespace impeller { -class SamplerMTL final : public Sampler { +class SamplerLibraryMTL; + +class SamplerMTL final : public Sampler, + public BackendCast { public: SamplerMTL(); + // |Sampler| ~SamplerMTL() override; + id GetMTLSamplerState() const; + private: + friend SamplerLibraryMTL; + + id state_ = nullptr; + + SamplerMTL(id state); + + // |Sampler| + bool IsValid() const override; + FML_DISALLOW_COPY_AND_ASSIGN(SamplerMTL); }; diff --git a/impeller/renderer/backend/metal/sampler_mtl.mm b/impeller/renderer/backend/metal/sampler_mtl.mm index e69de29bb2d1d..a66c5473bd3cf 100644 --- a/impeller/renderer/backend/metal/sampler_mtl.mm +++ b/impeller/renderer/backend/metal/sampler_mtl.mm @@ -0,0 +1,21 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "impeller/renderer/backend/metal/sampler_mtl.h" + +namespace impeller { + +SamplerMTL::SamplerMTL(id state) : state_(state) {} + +SamplerMTL::~SamplerMTL() = default; + +bool SamplerMTL::IsValid() const { + return state_; +} + +id SamplerMTL::GetMTLSamplerState() const { + return state_; +} + +} // namespace impeller diff --git a/impeller/renderer/backend/metal/shader_function_mtl.h b/impeller/renderer/backend/metal/shader_function_mtl.h index f48b64a9f3cf3..6dc35b248e867 100644 --- a/impeller/renderer/backend/metal/shader_function_mtl.h +++ b/impeller/renderer/backend/metal/shader_function_mtl.h @@ -4,18 +4,33 @@ #pragma once +#include + #include "flutter/fml/macros.h" +#include "impeller/renderer/backend/metal/backend_cast.h" #include "impeller/renderer/shader_function.h" namespace impeller { -class ShaderFunctionMTL final : public ShaderFunction { +class ShaderFunctionMTL final + : public ShaderFunction, + public BackendCast { public: - ShaderFunctionMTL(); - + // |ShaderFunction| ~ShaderFunctionMTL() override; + id GetMTLFunction() const; + private: + friend class ShaderLibraryMTL; + + id function_ = nullptr; + + ShaderFunctionMTL(UniqueID parent_library_id, + id function, + std::string name, + ShaderStage stage); + FML_DISALLOW_COPY_AND_ASSIGN(ShaderFunctionMTL); }; diff --git a/impeller/renderer/backend/metal/shader_function_mtl.mm b/impeller/renderer/backend/metal/shader_function_mtl.mm index e69de29bb2d1d..55c7e06b19cf3 100644 --- a/impeller/renderer/backend/metal/shader_function_mtl.mm +++ b/impeller/renderer/backend/metal/shader_function_mtl.mm @@ -0,0 +1,22 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "impeller/renderer/backend/metal/shader_function_mtl.h" + +namespace impeller { + +ShaderFunctionMTL::ShaderFunctionMTL(UniqueID parent_library_id, + id function, + std::string name, + ShaderStage stage) + : ShaderFunction(std::move(parent_library_id), std::move(name), stage), + function_(function) {} + +ShaderFunctionMTL::~ShaderFunctionMTL() = default; + +id ShaderFunctionMTL::GetMTLFunction() const { + return function_; +} + +} // namespace impeller diff --git a/impeller/renderer/backend/metal/shader_library_mtl.h b/impeller/renderer/backend/metal/shader_library_mtl.h index 7ea6beeebe87e..fe10dfd657b2f 100644 --- a/impeller/renderer/backend/metal/shader_library_mtl.h +++ b/impeller/renderer/backend/metal/shader_library_mtl.h @@ -4,7 +4,14 @@ #pragma once +#include + +#include +#include +#include + #include "flutter/fml/macros.h" +#include "impeller/renderer/comparable.h" #include "impeller/renderer/shader_library.h" namespace impeller { @@ -13,9 +20,49 @@ class ShaderLibraryMTL final : public ShaderLibrary { public: ShaderLibraryMTL(); + // |ShaderLibrary| ~ShaderLibraryMTL() override; private: + friend class ContextMTL; + + struct ShaderKey { + std::string name; + ShaderStage stage = ShaderStage::kUnknown; + + ShaderKey(const std::string_view& p_name, ShaderStage p_stage) + : name({p_name.data(), p_name.size()}), stage(p_stage) {} + + struct Hash { + size_t operator()(const ShaderKey& key) const { + return fml::HashCombine(key.name, key.stage); + } + }; + + struct Equal { + constexpr bool operator()(const ShaderKey& k1, + const ShaderKey& k2) const { + return k1.stage == k2.stage && k1.name == k2.name; + } + }; + }; + + using Functions = std::unordered_map, + ShaderKey::Hash, + ShaderKey::Equal>; + + UniqueID library_id_; + id library_ = nullptr; + Functions functions_; + + ShaderLibraryMTL(id library); + + // |ShaderLibrary| + std::shared_ptr GetFunction( + const std::string_view& name, + ShaderStage stage) override; + FML_DISALLOW_COPY_AND_ASSIGN(ShaderLibraryMTL); }; diff --git a/impeller/renderer/backend/metal/shader_library_mtl.mm b/impeller/renderer/backend/metal/shader_library_mtl.mm index e69de29bb2d1d..5167e4cb7cd57 100644 --- a/impeller/renderer/backend/metal/shader_library_mtl.mm +++ b/impeller/renderer/backend/metal/shader_library_mtl.mm @@ -0,0 +1,40 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "impeller/renderer/backend/metal/shader_library_mtl.h" + +#include "impeller/renderer/backend/metal/shader_function_mtl.h" + +namespace impeller { + +ShaderLibraryMTL::ShaderLibraryMTL(id library) + : library_(library) {} + +ShaderLibraryMTL::~ShaderLibraryMTL() = default; + +std::shared_ptr ShaderLibraryMTL::GetFunction( + const std::string_view& name, + ShaderStage stage) { + if (!library_) { + return nullptr; + } + + ShaderKey key(name, stage); + + if (auto found = functions_.find(key); found != functions_.end()) { + return found->second; + } + + auto function = [library_ newFunctionWithName:@(name.data())]; + if (!function) { + return nullptr; + } + + auto func = std::shared_ptr(new ShaderFunctionMTL( + library_id_, function, {name.data(), name.size()}, stage)); + functions_[key] = func; + return func; +} + +} // namespace impeller diff --git a/impeller/renderer/backend/metal/texture_mtl.h b/impeller/renderer/backend/metal/texture_mtl.h index 33d516d8ae323..4ffeb9761c19e 100644 --- a/impeller/renderer/backend/metal/texture_mtl.h +++ b/impeller/renderer/backend/metal/texture_mtl.h @@ -4,18 +4,37 @@ #pragma once +#include + #include "flutter/fml/macros.h" +#include "impeller/renderer/backend/metal/backend_cast.h" #include "impeller/renderer/texture.h" namespace impeller { -class TextureMTL final : public Texture { +class TextureMTL final : public Texture, + public BackendCast { public: - TextureMTL(); - + // |Texture| ~TextureMTL() override; + void SetLabel(const std::string_view& label) override; + + [[nodiscard]] bool SetContents(const uint8_t* contents, + size_t length) override; + + bool IsValid() const override; + + ISize GetSize() const override; + + id GetMTLTexture() const; + private: + id texture_ = nullptr; + bool is_valid_ = false; + + TextureMTL(TextureDescriptor desc, id texture); + FML_DISALLOW_COPY_AND_ASSIGN(TextureMTL); }; diff --git a/impeller/renderer/backend/metal/texture_mtl.mm b/impeller/renderer/backend/metal/texture_mtl.mm index e69de29bb2d1d..8d1f42575fa89 100644 --- a/impeller/renderer/backend/metal/texture_mtl.mm +++ b/impeller/renderer/backend/metal/texture_mtl.mm @@ -0,0 +1,72 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "impeller/renderer/backend/metal/texture_mtl.h" + +namespace impeller { + +TextureMTL::TextureMTL(TextureDescriptor p_desc, id texture) + : Texture(std::move(p_desc)), texture_(texture) { + const auto& desc = GetTextureDescriptor(); + + if (!desc.IsValid() || !texture_) { + return; + } + + if (desc.size != GetSize()) { + FML_DLOG(ERROR) + << "The texture and its descriptor disagree about its size."; + return; + } + + is_valid_ = true; +} + +TextureMTL::~TextureMTL() = default; + +void TextureMTL::SetLabel(const std::string_view& label) { + [texture_ setLabel:@(label.data())]; +} + +bool TextureMTL::SetContents(const uint8_t* contents, size_t length) { + if (!IsValid() || !contents) { + return false; + } + + const auto& desc = GetTextureDescriptor(); + + // Out of bounds access. + if (length != desc.GetSizeOfBaseMipLevel()) { + return false; + } + + // TODO(csg): Perhaps the storage mode should be added to the texture + // descriptor so that invalid region replacements on potentially non-host + // visible textures are disallowed. The annoying bit about the API below is + // that there seems to be no error handling guidance. + const auto region = + MTLRegionMake2D(0u, 0u, desc.size.width, desc.size.height); + [texture_ replaceRegion:region // + mipmapLevel:0u // + withBytes:contents // + bytesPerRow:desc.GetBytesPerRow() // + ]; + + return true; +} + +ISize TextureMTL::GetSize() const { + return {static_cast(texture_.width), + static_cast(texture_.height)}; +} + +id TextureMTL::GetMTLTexture() const { + return texture_; +} + +bool TextureMTL::IsValid() const { + return is_valid_; +} + +} // namespace impeller diff --git a/impeller/renderer/buffer.mm b/impeller/renderer/buffer.cc similarity index 100% rename from impeller/renderer/buffer.mm rename to impeller/renderer/buffer.cc diff --git a/impeller/renderer/buffer_view.mm b/impeller/renderer/buffer_view.cc similarity index 100% rename from impeller/renderer/buffer_view.mm rename to impeller/renderer/buffer_view.cc diff --git a/impeller/renderer/command.mm b/impeller/renderer/command.cc similarity index 99% rename from impeller/renderer/command.mm rename to impeller/renderer/command.cc index e4a457b0438f1..bc18314d7c98a 100644 --- a/impeller/renderer/command.mm +++ b/impeller/renderer/command.cc @@ -4,8 +4,6 @@ #include "impeller/renderer/command.h" -#include - #include "impeller/renderer/vertex_descriptor.h" namespace impeller { diff --git a/impeller/renderer/command_buffer.cc b/impeller/renderer/command_buffer.cc new file mode 100644 index 0000000000000..9398bc6822ba0 --- /dev/null +++ b/impeller/renderer/command_buffer.cc @@ -0,0 +1,13 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "impeller/renderer/command_buffer.h" + +namespace impeller { + +CommandBuffer::CommandBuffer() = default; + +CommandBuffer::~CommandBuffer() = default; + +} // namespace impeller diff --git a/impeller/renderer/command_buffer.h b/impeller/renderer/command_buffer.h index 7c7c58eb27ef7..2d7f6be2601b8 100644 --- a/impeller/renderer/command_buffer.h +++ b/impeller/renderer/command_buffer.h @@ -4,24 +4,19 @@ #pragma once -#include - #include #include #include "flutter/fml/macros.h" -#include "impeller/renderer/render_pass.h" namespace impeller { class Context; +class RenderPass; +class RenderPassDescriptor; class CommandBuffer { public: - ~CommandBuffer(); - - bool IsValid() const; - enum class CommitResult { kPending, kError, @@ -29,19 +24,20 @@ class CommandBuffer { }; using CommitCallback = std::function; - void Commit(CommitCallback callback); - std::shared_ptr CreateRenderPass( - const RenderPassDescriptor& desc) const; + virtual ~CommandBuffer(); - private: - friend class Context; + virtual bool IsValid() const = 0; + + virtual void Commit(CommitCallback callback) = 0; - id buffer_ = nullptr; - bool is_valid_ = false; + virtual std::shared_ptr CreateRenderPass( + const RenderPassDescriptor& desc) const = 0; - CommandBuffer(id queue); + protected: + CommandBuffer(); + private: FML_DISALLOW_COPY_AND_ASSIGN(CommandBuffer); }; diff --git a/impeller/renderer/command_buffer.mm b/impeller/renderer/command_buffer.mm deleted file mode 100644 index 43d3f8afe81c9..0000000000000 --- a/impeller/renderer/command_buffer.mm +++ /dev/null @@ -1,67 +0,0 @@ -// Copyright 2013 The Flutter Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#include "impeller/renderer/command_buffer.h" - -namespace impeller { - -CommandBuffer::CommandBuffer(id queue) - : buffer_([queue commandBuffer]) { - if (!buffer_) { - return; - } - is_valid_ = true; -} - -CommandBuffer::~CommandBuffer() = default; - -bool CommandBuffer::IsValid() const { - return is_valid_; -} - -static CommandBuffer::CommitResult ToCommitResult( - MTLCommandBufferStatus status) { - switch (status) { - case MTLCommandBufferStatusCompleted: - return CommandBuffer::CommitResult::kCompleted; - case MTLCommandBufferStatusEnqueued: - return CommandBuffer::CommitResult::kPending; - default: - break; - } - return CommandBuffer::CommitResult::kError; -} - -void CommandBuffer::Commit(CommitCallback callback) { - if (!callback) { - callback = [](auto) {}; - } - - if (!buffer_) { - callback(CommitResult::kError); - return; - } - - [buffer_ addCompletedHandler:^(id buffer) { - callback(ToCommitResult(buffer.status)); - }]; - [buffer_ commit]; - buffer_ = nil; -} - -std::shared_ptr CommandBuffer::CreateRenderPass( - const RenderPassDescriptor& desc) const { - if (!buffer_) { - return nullptr; - } - - auto pass = std::shared_ptr(new RenderPass(buffer_, desc)); - if (!pass->IsValid()) { - return nullptr; - } - - return pass; -} - -} // namespace impeller diff --git a/impeller/renderer/context.cc b/impeller/renderer/context.cc new file mode 100644 index 0000000000000..542d04ee14d78 --- /dev/null +++ b/impeller/renderer/context.cc @@ -0,0 +1,13 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "impeller/renderer/context.h" + +namespace impeller { + +Context::~Context() = default; + +Context::Context() = default; + +} // namespace impeller diff --git a/impeller/renderer/context.h b/impeller/renderer/context.h index 0da2ce6da01ff..bb19ac458102d 100644 --- a/impeller/renderer/context.h +++ b/impeller/renderer/context.h @@ -4,8 +4,6 @@ #pragma once -#include - #include #include @@ -21,45 +19,34 @@ class Allocator; class Context { public: - Context(std::string shaders_directory, std::string main_library_file_name); - - ~Context(); + virtual ~Context(); - bool IsValid() const; + virtual bool IsValid() const = 0; //---------------------------------------------------------------------------- /// @return An allocator suitable for allocations that persist between /// frames. /// - std::shared_ptr GetPermanentsAllocator() const; + virtual std::shared_ptr GetPermanentsAllocator() const = 0; //---------------------------------------------------------------------------- /// @return An allocator suitable for allocations that used only for one /// frame or render pass. /// - std::shared_ptr GetTransientsAllocator() const; + virtual std::shared_ptr GetTransientsAllocator() const = 0; - std::shared_ptr GetShaderLibrary() const; + virtual std::shared_ptr GetShaderLibrary() const = 0; - std::shared_ptr GetSamplerLibrary() const; + virtual std::shared_ptr GetSamplerLibrary() const = 0; - std::shared_ptr GetPipelineLibrary() const; + virtual std::shared_ptr GetPipelineLibrary() const = 0; - std::shared_ptr CreateRenderCommandBuffer() const; + virtual std::shared_ptr CreateRenderCommandBuffer() const = 0; - id GetMTLDevice() const; + protected: + Context(); private: - id device_ = nullptr; - id render_queue_ = nullptr; - id transfer_queue_ = nullptr; - std::shared_ptr shader_library_; - std::shared_ptr pipeline_library_; - std::shared_ptr sampler_library_; - std::shared_ptr permanents_allocator_; - std::shared_ptr transients_allocator_; - bool is_valid_ = false; - FML_DISALLOW_COPY_AND_ASSIGN(Context); }; diff --git a/impeller/renderer/context.mm b/impeller/renderer/context.mm deleted file mode 100644 index b1203398b11e3..0000000000000 --- a/impeller/renderer/context.mm +++ /dev/null @@ -1,138 +0,0 @@ -// Copyright 2013 The Flutter Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#include "context.h" - -#include "flutter/fml/file.h" -#include "flutter/fml/logging.h" -#include "flutter/fml/paths.h" -#include "impeller/renderer/allocator.h" -#include "impeller/renderer/command_buffer.h" -#include "impeller/renderer/pipeline_library.h" -#include "impeller/renderer/sampler_descriptor.h" -#include "impeller/renderer/shader_library.h" - -namespace impeller { - -Context::Context(std::string shaders_directory, - std::string main_library_file_name) - : device_(::MTLCreateSystemDefaultDevice()) { - // Setup device. - if (!device_) { - return; - } - - // Setup command queues. - render_queue_ = device_.newCommandQueue; - transfer_queue_ = device_.newCommandQueue; - - if (!render_queue_ || !transfer_queue_) { - return; - } - - render_queue_.label = @"Impeller Render Queue"; - transfer_queue_.label = @"Impeller Transfer Queue"; - - // Setup the shader library. - { - NSError* shader_library_error = nil; - auto shader_library_path = - fml::paths::JoinPaths({shaders_directory, main_library_file_name}); - - auto library_exists = fml::IsFile(shader_library_path); - - if (!library_exists) { - FML_LOG(ERROR) << "Shader library does not exist at path '" - << shader_library_path - << "'. No piplines can be created in this context."; - } - auto library = - library_exists - ? [device_ newLibraryWithFile:@(shader_library_path.c_str()) - error:&shader_library_error] - : [device_ newDefaultLibrary]; - if (!library && shader_library_error) { - FML_LOG(ERROR) << "Could not create shader library: " - << shader_library_error.localizedDescription.UTF8String; - return; - } - - // std::make_shared disallowed because of private friend ctor. - shader_library_ = - std::shared_ptr(new ShaderLibrary(library)); - } - - // Setup the pipeline library. - { // - pipeline_library_ = - std::shared_ptr(new PipelineLibrary(device_)); - } - - // Setup the sampler library. - { // - sampler_library_ = - std::shared_ptr(new SamplerLibrary(device_)); - } - - { - transients_allocator_ = std::shared_ptr( - new Allocator(device_, "Impeller Transients Allocator")); - if (!transients_allocator_) { - return; - } - - permanents_allocator_ = std::shared_ptr( - new Allocator(device_, "Impeller Permanents Allocator")); - if (!permanents_allocator_) { - return; - } - } - - is_valid_ = true; -} - -Context::~Context() = default; - -bool Context::IsValid() const { - return is_valid_; -} - -std::shared_ptr Context::GetShaderLibrary() const { - return shader_library_; -} - -std::shared_ptr Context::GetPipelineLibrary() const { - return pipeline_library_; -} - -std::shared_ptr Context::GetSamplerLibrary() const { - return sampler_library_; -} - -std::shared_ptr Context::CreateRenderCommandBuffer() const { - if (!IsValid()) { - return nullptr; - } - - auto buffer = - std::shared_ptr(new CommandBuffer(render_queue_)); - if (!buffer->IsValid()) { - return nullptr; - } - return buffer; -} - -std::shared_ptr Context::GetPermanentsAllocator() const { - return permanents_allocator_; -} - -std::shared_ptr Context::GetTransientsAllocator() const { - return transients_allocator_; -} - -id Context::GetMTLDevice() const { - return device_; -} - -} // namespace impeller diff --git a/impeller/renderer/device_buffer.cc b/impeller/renderer/device_buffer.cc new file mode 100644 index 0000000000000..7fd7e74cfc0d3 --- /dev/null +++ b/impeller/renderer/device_buffer.cc @@ -0,0 +1,13 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "impeller/renderer/device_buffer.h" + +namespace impeller { + +DeviceBuffer::DeviceBuffer() = default; + +DeviceBuffer::~DeviceBuffer() = default; + +} // namespace impeller diff --git a/impeller/renderer/device_buffer.h b/impeller/renderer/device_buffer.h index 91e2a385b5875..e9d1130200650 100644 --- a/impeller/renderer/device_buffer.h +++ b/impeller/renderer/device_buffer.h @@ -4,8 +4,6 @@ #pragma once -#include - #include #include @@ -18,14 +16,14 @@ namespace impeller { -class DeviceBuffer final : public Buffer, - public std::enable_shared_from_this { +class DeviceBuffer : public Buffer, + public std::enable_shared_from_this { public: - ~DeviceBuffer(); + virtual ~DeviceBuffer(); - [[nodiscard]] bool CopyHostBuffer(const uint8_t* source, - Range source_range, - size_t offset = 0u); + [[nodiscard]] virtual bool CopyHostBuffer(const uint8_t* source, + Range source_range, + size_t offset = 0u) = 0; //---------------------------------------------------------------------------- /// @brief Create a texture whose contents are the same as that of this @@ -39,30 +37,19 @@ class DeviceBuffer final : public Buffer, /// @return The texture whose contents are backed by (a part of) this /// buffer. /// - std::shared_ptr MakeTexture(TextureDescriptor desc, - size_t offset = 0u) const; + virtual std::shared_ptr MakeTexture(TextureDescriptor desc, + size_t offset = 0u) const = 0; - id GetMTLBuffer() const; + virtual bool SetLabel(const std::string& label) = 0; - bool SetLabel(const std::string& label); + virtual bool SetLabel(const std::string& label, Range range) = 0; - bool SetLabel(const std::string& label, Range range); + virtual BufferView AsBufferView() const = 0; - BufferView AsBufferView() const; + protected: + DeviceBuffer(); private: - friend class Allocator; - - const id buffer_; - const size_t size_; - const StorageMode mode_; - - DeviceBuffer(id buffer, size_t size, StorageMode mode); - - // |Buffer| - std::shared_ptr GetDeviceBuffer( - Allocator& allocator) const override; - FML_DISALLOW_COPY_AND_ASSIGN(DeviceBuffer); }; diff --git a/impeller/renderer/device_buffer.mm b/impeller/renderer/device_buffer.mm deleted file mode 100644 index 4bc26264c00e4..0000000000000 --- a/impeller/renderer/device_buffer.mm +++ /dev/null @@ -1,103 +0,0 @@ -// Copyright 2013 The Flutter Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#include "impeller/renderer/device_buffer.h" - -#include "flutter/fml/logging.h" -#include "impeller/renderer/formats_metal.h" - -namespace impeller { - -DeviceBuffer::DeviceBuffer(id buffer, size_t size, StorageMode mode) - : buffer_(buffer), size_(size), mode_(mode) {} - -DeviceBuffer::~DeviceBuffer() = default; - -id DeviceBuffer::GetMTLBuffer() const { - return buffer_; -} - -std::shared_ptr DeviceBuffer::MakeTexture(TextureDescriptor desc, - size_t offset) const { - if (!desc.IsValid() || !buffer_) { - return nullptr; - } - - // Avoid overruns. - if (offset + desc.GetSizeOfBaseMipLevel() > size_) { - FML_DLOG(ERROR) << "Avoiding buffer overrun when creating texture."; - return nullptr; - } - - auto texture = [buffer_ newTextureWithDescriptor:ToMTLTextureDescriptor(desc) - offset:offset - bytesPerRow:desc.GetBytesPerRow()]; - if (!texture) { - return nullptr; - } - - return std::make_shared(desc, texture); -} - -[[nodiscard]] bool DeviceBuffer::CopyHostBuffer(const uint8_t* source, - Range source_range, - size_t offset) { - if (mode_ != StorageMode::kHostVisible) { - // One of the storage modes where a transfer queue must be used. - return false; - } - - if (offset + source_range.length > size_) { - // Out of bounds of this buffer. - return false; - } - - auto dest = static_cast(buffer_.contents); - - if (!dest) { - return false; - } - - if (source) { - ::memmove(dest + offset, source + source_range.offset, source_range.length); - } - - if (Allocator::RequiresExplicitHostSynchronization(mode_)) { - [buffer_ didModifyRange:NSMakeRange(offset, source_range.length)]; - } - - return true; -} - -// |Buffer| -std::shared_ptr DeviceBuffer::GetDeviceBuffer( - Allocator& allocator) const { - return shared_from_this(); -} - -bool DeviceBuffer::SetLabel(const std::string& label) { - if (label.empty()) { - return false; - } - [buffer_ setLabel:@(label.c_str())]; - return true; -} - -bool DeviceBuffer::SetLabel(const std::string& label, Range range) { - if (label.empty()) { - return false; - } - [buffer_ addDebugMarker:@(label.c_str()) - range:NSMakeRange(range.offset, range.length)]; - return true; -} - -BufferView DeviceBuffer::AsBufferView() const { - BufferView view; - view.buffer = shared_from_this(); - view.range = {0u, size_}; - return view; -} - -} // namespace impeller diff --git a/impeller/renderer/device_buffer_unittests.mm b/impeller/renderer/device_buffer_unittests.cc similarity index 100% rename from impeller/renderer/device_buffer_unittests.mm rename to impeller/renderer/device_buffer_unittests.cc diff --git a/impeller/renderer/formats_metal.mm b/impeller/renderer/formats_metal.mm deleted file mode 100644 index ba23493979dbc..0000000000000 --- a/impeller/renderer/formats_metal.mm +++ /dev/null @@ -1,102 +0,0 @@ -// Copyright 2013 The Flutter Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#include "impeller/renderer/formats_metal.h" - -#include - -#include "impeller/renderer/render_pass.h" - -namespace impeller { - -MTLRenderPipelineColorAttachmentDescriptor* -ToMTLRenderPipelineColorAttachmentDescriptor( - PipelineColorAttachment descriptor) { - auto des = [[MTLRenderPipelineColorAttachmentDescriptor alloc] init]; - des.pixelFormat = ToMTLPixelFormat(descriptor.format); - - des.blendingEnabled = descriptor.blending_enabled; - - des.sourceRGBBlendFactor = - ToMTLBlendFactor(descriptor.src_color_blend_factor); - des.rgbBlendOperation = ToMTLBlendOperation(descriptor.color_blend_op); - des.destinationRGBBlendFactor = - ToMTLBlendFactor(descriptor.dst_color_blend_factor); - - des.sourceAlphaBlendFactor = - ToMTLBlendFactor(descriptor.src_alpha_blend_factor); - des.alphaBlendOperation = ToMTLBlendOperation(descriptor.alpha_blend_op); - des.destinationAlphaBlendFactor = - ToMTLBlendFactor(descriptor.dst_alpha_blend_factor); - - des.writeMask = ToMTLColorWriteMask(descriptor.write_mask); - return des; -} - -MTLStencilDescriptor* ToMTLStencilDescriptor( - const PipelineStencilAttachment& descriptor) { - auto des = [[MTLStencilDescriptor alloc] init]; - des.stencilCompareFunction = ToMTLCompareFunction(descriptor.stencil_compare); - des.stencilFailureOperation = - ToMTLStencilOperation(descriptor.stencil_failure); - des.depthFailureOperation = ToMTLStencilOperation(descriptor.depth_failure); - des.depthStencilPassOperation = - ToMTLStencilOperation(descriptor.depth_stencil_pass); - - des.readMask = descriptor.read_mask; - des.writeMask = descriptor.write_mask; - - return des; -} - -MTLDepthStencilDescriptor* ToMTLDepthStencilDescriptor( - std::optional depth, - std::optional front, - std::optional back) { - if (!depth) { - depth = PipelineDepthAttachment{ - // Always pass the depth test. - .depth_compare = CompareFunction::kAlways, - .depth_write_enabled = false, - }; - } - - auto des = [[MTLDepthStencilDescriptor alloc] init]; - - des.depthCompareFunction = ToMTLCompareFunction(depth->depth_compare); - des.depthWriteEnabled = depth->depth_write_enabled; - - if (front.has_value()) { - des.frontFaceStencil = ToMTLStencilDescriptor(front.value()); - } - if (back.has_value()) { - des.backFaceStencil = ToMTLStencilDescriptor(back.value()); - } - - return des; -} - -MTLTextureDescriptor* ToMTLTextureDescriptor(const TextureDescriptor& desc) { - auto mtl_desc = [[MTLTextureDescriptor alloc] init]; - mtl_desc.pixelFormat = ToMTLPixelFormat(desc.format); - mtl_desc.width = desc.size.width; - mtl_desc.height = desc.size.height; - mtl_desc.mipmapLevelCount = desc.mip_count; - mtl_desc.usage = MTLTextureUsageUnknown; - if (desc.usage & static_cast(TextureUsage::kUnknown)) { - mtl_desc.usage |= MTLTextureUsageUnknown; - } - if (desc.usage & static_cast(TextureUsage::kShaderRead)) { - mtl_desc.usage |= MTLTextureUsageShaderRead; - } - if (desc.usage & static_cast(TextureUsage::kShaderWrite)) { - mtl_desc.usage |= MTLTextureUsageShaderWrite; - } - if (desc.usage & static_cast(TextureUsage::kRenderTarget)) { - mtl_desc.usage |= MTLTextureUsageRenderTarget; - } - return mtl_desc; -} - -} // namespace impeller diff --git a/impeller/renderer/host_buffer.mm b/impeller/renderer/host_buffer.cc similarity index 100% rename from impeller/renderer/host_buffer.mm rename to impeller/renderer/host_buffer.cc diff --git a/impeller/renderer/host_buffer_unittests.mm b/impeller/renderer/host_buffer_unittests.cc similarity index 100% rename from impeller/renderer/host_buffer_unittests.mm rename to impeller/renderer/host_buffer_unittests.cc diff --git a/impeller/renderer/pipeline.mm b/impeller/renderer/pipeline.cc similarity index 58% rename from impeller/renderer/pipeline.mm rename to impeller/renderer/pipeline.cc index a5fc33aa93775..6badd53ab2e08 100644 --- a/impeller/renderer/pipeline.mm +++ b/impeller/renderer/pipeline.cc @@ -9,30 +9,10 @@ namespace impeller { -Pipeline::Pipeline(id state, - id depth_stencil_state) - : pipeline_state_(state), depth_stencil_state_(depth_stencil_state) { - if (!pipeline_state_) { - return; - } - type_ = Type::kRender; - is_valid_ = true; -} +Pipeline::Pipeline() = default; Pipeline::~Pipeline() = default; -bool Pipeline::IsValid() const { - return is_valid_; -} - -id Pipeline::GetMTLRenderPipelineState() const { - return pipeline_state_; -} - -id Pipeline::GetMTLDepthStencilState() const { - return depth_stencil_state_; -} - PipelineFuture CreatePipelineFuture(const Context& context, std::optional desc) { if (!context.IsValid()) { diff --git a/impeller/renderer/pipeline.h b/impeller/renderer/pipeline.h index 1c979e17bc857..bfb0f649e38c2 100644 --- a/impeller/renderer/pipeline.h +++ b/impeller/renderer/pipeline.h @@ -4,8 +4,6 @@ #pragma once -#include - #include #include "flutter/fml/macros.h" @@ -26,25 +24,14 @@ class Pipeline { kRender, }; - ~Pipeline(); - - bool IsValid() const; + virtual ~Pipeline(); - id GetMTLRenderPipelineState() const; + virtual bool IsValid() const = 0; - id GetMTLDepthStencilState() const; + protected: + Pipeline(); private: - friend class PipelineLibrary; - - Type type_ = Type::kUnknown; - id pipeline_state_; - id depth_stencil_state_; - bool is_valid_ = false; - - Pipeline(id state, - id depth_stencil_state); - FML_DISALLOW_COPY_AND_ASSIGN(Pipeline); }; diff --git a/impeller/renderer/pipeline_builder.mm b/impeller/renderer/pipeline_builder.cc similarity index 100% rename from impeller/renderer/pipeline_builder.mm rename to impeller/renderer/pipeline_builder.cc diff --git a/impeller/renderer/pipeline_descriptor.mm b/impeller/renderer/pipeline_descriptor.cc similarity index 73% rename from impeller/renderer/pipeline_descriptor.mm rename to impeller/renderer/pipeline_descriptor.cc index fee3a9faa0c81..25484f06bf6dc 100644 --- a/impeller/renderer/pipeline_descriptor.mm +++ b/impeller/renderer/pipeline_descriptor.cc @@ -5,6 +5,7 @@ #include "impeller/renderer/pipeline_descriptor.h" #include "impeller/renderer/formats_metal.h" +#include "impeller/renderer/shader_function.h" #include "impeller/renderer/shader_library.h" #include "impeller/renderer/vertex_descriptor.h" @@ -55,9 +56,8 @@ other.back_stencil_attachment_descriptor_; } -PipelineDescriptor& PipelineDescriptor::SetLabel( - const std::string_view& label) { - label_ = {label.data(), label.size()}; +PipelineDescriptor& PipelineDescriptor::SetLabel(std::string label) { + label_ = std::move(label); return *this; } @@ -132,45 +132,4 @@ return *this; } -MTLRenderPipelineDescriptor* -PipelineDescriptor::GetMTLRenderPipelineDescriptor() const { - auto descriptor = [[MTLRenderPipelineDescriptor alloc] init]; - descriptor.label = @(label_.c_str()); - descriptor.sampleCount = sample_count_; - - for (const auto& entry : entrypoints_) { - if (entry.first == ShaderStage::kVertex) { - descriptor.vertexFunction = entry.second->GetMTLFunction(); - } - if (entry.first == ShaderStage::kFragment) { - descriptor.fragmentFunction = entry.second->GetMTLFunction(); - } - } - - if (vertex_descriptor_) { - descriptor.vertexDescriptor = vertex_descriptor_->GetMTLVertexDescriptor(); - } - - for (const auto& item : color_attachment_descriptors_) { - descriptor.colorAttachments[item.first] = - ToMTLRenderPipelineColorAttachmentDescriptor(item.second); - } - - descriptor.depthAttachmentPixelFormat = ToMTLPixelFormat(depth_pixel_format_); - descriptor.stencilAttachmentPixelFormat = - ToMTLPixelFormat(stencil_pixel_format_); - - return descriptor; -} - -id PipelineDescriptor::CreateDepthStencilDescriptor( - id device) const { - auto descriptor = - ToMTLDepthStencilDescriptor(depth_attachment_descriptor_, // - front_stencil_attachment_descriptor_, // - back_stencil_attachment_descriptor_ // - ); - return [device newDepthStencilStateWithDescriptor:descriptor]; -} - } // namespace impeller diff --git a/impeller/renderer/pipeline_descriptor.h b/impeller/renderer/pipeline_descriptor.h index 840a074a5fb47..678bbe23c903b 100644 --- a/impeller/renderer/pipeline_descriptor.h +++ b/impeller/renderer/pipeline_descriptor.h @@ -4,8 +4,6 @@ #pragma once -#include - #include #include #include @@ -30,16 +28,29 @@ class PipelineDescriptor final : public Comparable { ~PipelineDescriptor(); - PipelineDescriptor& SetLabel(const std::string_view& label); + PipelineDescriptor& SetLabel(std::string label); + + const std::string& GetLabel() const { return label_; } PipelineDescriptor& SetSampleCount(size_t samples); + size_t GetSampleCount() const { return sample_count_; } + PipelineDescriptor& AddStageEntrypoint( std::shared_ptr function); + const std::map>& + GetStageEntrypoints() const { + return entrypoints_; + } + PipelineDescriptor& SetVertexDescriptor( std::shared_ptr vertex_descriptor); + const std::shared_ptr& GetVertexDescriptor() const { + return vertex_descriptor_; + } + PipelineDescriptor& SetColorAttachmentDescriptor( size_t index, PipelineColorAttachment desc); @@ -47,9 +58,19 @@ class PipelineDescriptor final : public Comparable { const PipelineColorAttachment* GetColorAttachmentDescriptor( size_t index) const; + const std::map + GetColorAttachmentDescriptors() const { + return color_attachment_descriptors_; + } + PipelineDescriptor& SetDepthStencilAttachmentDescriptor( PipelineDepthAttachment desc); + std::optional GetDepthStencilAttachmentDescriptor() + const { + return depth_attachment_descriptor_; + } + PipelineDescriptor& SetStencilAttachmentDescriptors( PipelineStencilAttachment front_and_back); @@ -57,14 +78,23 @@ class PipelineDescriptor final : public Comparable { PipelineStencilAttachment front, PipelineStencilAttachment back); + std::optional GetFrontStencilAttachmentDescriptor() + const { + return front_stencil_attachment_descriptor_; + } + + std::optional GetBackStencilAttachmentDescriptor() + const { + return back_stencil_attachment_descriptor_; + } + PipelineDescriptor& SetDepthPixelFormat(PixelFormat format); - PipelineDescriptor& SetStencilPixelFormat(PixelFormat format); + PixelFormat GetDepthPixelFormat() const { return depth_pixel_format_; } - MTLRenderPipelineDescriptor* GetMTLRenderPipelineDescriptor() const; + PipelineDescriptor& SetStencilPixelFormat(PixelFormat format); - id CreateDepthStencilDescriptor( - id device) const; + PixelFormat GetStencilPixelFormat() const { return stencil_pixel_format_; } // Comparable std::size_t GetHash() const override; @@ -76,7 +106,8 @@ class PipelineDescriptor final : public Comparable { std::string label_; size_t sample_count_ = 1; std::map> entrypoints_; - std::map color_attachment_descriptors_; + std::map + color_attachment_descriptors_; std::shared_ptr vertex_descriptor_; PixelFormat depth_pixel_format_ = PixelFormat::kUnknown; PixelFormat stencil_pixel_format_ = PixelFormat::kUnknown; diff --git a/impeller/renderer/pipeline_library.cc b/impeller/renderer/pipeline_library.cc new file mode 100644 index 0000000000000..10bb194bf4101 --- /dev/null +++ b/impeller/renderer/pipeline_library.cc @@ -0,0 +1,23 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "impeller/renderer/pipeline_library.h" + +namespace impeller { + +PipelineLibrary::PipelineLibrary() = default; + +PipelineLibrary::~PipelineLibrary() = default; + +PipelineFuture PipelineLibrary::GetRenderPipeline( + std::optional descriptor) { + if (descriptor.has_value()) { + return GetRenderPipeline(std::move(descriptor.value())); + } + auto promise = std::make_shared>>(); + promise->set_value(nullptr); + return promise->get_future(); +} + +} // namespace impeller diff --git a/impeller/renderer/pipeline_library.h b/impeller/renderer/pipeline_library.h index b161b2e8d82f2..67fc8efdc6d8d 100644 --- a/impeller/renderer/pipeline_library.h +++ b/impeller/renderer/pipeline_library.h @@ -4,10 +4,7 @@ #pragma once -#include - -#include -#include +#include #include "flutter/fml/macros.h" #include "impeller/renderer/pipeline.h" @@ -19,28 +16,17 @@ class Context; class PipelineLibrary : public std::enable_shared_from_this { public: - ~PipelineLibrary(); + virtual ~PipelineLibrary(); PipelineFuture GetRenderPipeline( std::optional descriptor); - PipelineFuture GetRenderPipeline(PipelineDescriptor descriptor); - - private: - friend Context; - - using Pipelines = std::unordered_map, - ComparableHash, - ComparableEqual>; - id device_ = nullptr; - Pipelines pipelines_; - - PipelineLibrary(id device); + virtual PipelineFuture GetRenderPipeline(PipelineDescriptor descriptor) = 0; - void SavePipeline(PipelineDescriptor descriptor, - std::shared_ptr pipeline); + protected: + PipelineLibrary(); + private: FML_DISALLOW_COPY_AND_ASSIGN(PipelineLibrary); }; diff --git a/impeller/renderer/pipeline_library.mm b/impeller/renderer/pipeline_library.mm deleted file mode 100644 index 931e9428c9398..0000000000000 --- a/impeller/renderer/pipeline_library.mm +++ /dev/null @@ -1,65 +0,0 @@ -// Copyright 2013 The Flutter Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#include "impeller/renderer/pipeline_library.h" - -#include - -#include "flutter/fml/logging.h" - -namespace impeller { - -PipelineLibrary::PipelineLibrary(id device) : device_(device) {} - -PipelineLibrary::~PipelineLibrary() = default; - -std::future> PipelineLibrary::GetRenderPipeline( - std::optional descriptor) { - if (descriptor.has_value()) { - return GetRenderPipeline(std::move(descriptor.value())); - } - auto promise = std::make_shared>>(); - promise->set_value(nullptr); - return promise->get_future(); -} - -std::future> PipelineLibrary::GetRenderPipeline( - PipelineDescriptor descriptor) { - auto promise = std::make_shared>>(); - auto future = promise->get_future(); - if (auto found = pipelines_.find(descriptor); found != pipelines_.end()) { - promise->set_value(nullptr); - return future; - } - - auto thiz = shared_from_this(); - - auto completion_handler = - ^(id _Nullable render_pipeline_state, - NSError* _Nullable error) { - if (error != nil) { - FML_LOG(ERROR) << "Could not create render pipeline: " - << error.localizedDescription.UTF8String; - promise->set_value(nullptr); - } else { - auto new_pipeline = std::shared_ptr( - new Pipeline(render_pipeline_state, - descriptor.CreateDepthStencilDescriptor(device_))); - promise->set_value(new_pipeline); - this->SavePipeline(descriptor, new_pipeline); - } - }; - [device_ - newRenderPipelineStateWithDescriptor:descriptor - .GetMTLRenderPipelineDescriptor() - completionHandler:completion_handler]; - return future; -} - -void PipelineLibrary::SavePipeline(PipelineDescriptor descriptor, - std::shared_ptr pipeline) { - pipelines_[descriptor] = std::move(pipeline); -} - -} // namespace impeller diff --git a/impeller/renderer/platform.mm b/impeller/renderer/platform.cc similarity index 100% rename from impeller/renderer/platform.mm rename to impeller/renderer/platform.cc diff --git a/impeller/renderer/render_pass.cc b/impeller/renderer/render_pass.cc new file mode 100644 index 0000000000000..93c4ead3bf638 --- /dev/null +++ b/impeller/renderer/render_pass.cc @@ -0,0 +1,13 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "impeller/renderer/render_pass.h" + +namespace impeller { + +RenderPass::RenderPass() = default; + +RenderPass::~RenderPass() = default; + +} // namespace impeller diff --git a/impeller/renderer/render_pass.h b/impeller/renderer/render_pass.h index 289bd182c7d73..c61f16cc2ed57 100644 --- a/impeller/renderer/render_pass.h +++ b/impeller/renderer/render_pass.h @@ -4,35 +4,26 @@ #pragma once -#include +#include -#include -#include - -#include "flutter/fml/macros.h" -#include "impeller/geometry/color.h" -#include "impeller/geometry/size.h" #include "impeller/renderer/command.h" -#include "impeller/renderer/formats.h" -#include "impeller/renderer/host_buffer.h" -#include "impeller/renderer/render_pass_descriptor.h" -#include "impeller/renderer/texture.h" namespace impeller { -class CommandBuffer; +class HostBuffer; +class Allocator; class RenderPass { public: - ~RenderPass(); + virtual ~RenderPass(); - bool IsValid() const; + virtual bool IsValid() const = 0; - void SetLabel(std::string label); + virtual void SetLabel(std::string label) = 0; - HostBuffer& GetTransientsBuffer(); + virtual HostBuffer& GetTransientsBuffer() = 0; - [[nodiscard]] bool RecordCommand(Command command); + [[nodiscard]] virtual bool RecordCommand(Command command) = 0; //---------------------------------------------------------------------------- /// @brief Commit the recorded commands to the underlying command buffer. @@ -44,23 +35,12 @@ class RenderPass { /// @return If the commands were committed to the underlying command /// buffer. /// - [[nodiscard]] bool Commit(Allocator& transients_allocator) const; - - private: - friend class CommandBuffer; + [[nodiscard]] virtual bool Commit(Allocator& transients_allocator) const = 0; - id buffer_ = nil; - MTLRenderPassDescriptor* desc_ = nil; - std::vector commands_; - std::shared_ptr transients_buffer_; - std::string label_; - bool is_valid_ = false; - - RenderPass(id buffer, const RenderPassDescriptor& desc); - - bool EncodeCommands(Allocator& transients_allocator, - id pass) const; + protected: + RenderPass(); + private: FML_DISALLOW_COPY_AND_ASSIGN(RenderPass); }; diff --git a/impeller/renderer/render_pass.mm b/impeller/renderer/render_pass.mm deleted file mode 100644 index a136864b85e0d..0000000000000 --- a/impeller/renderer/render_pass.mm +++ /dev/null @@ -1,408 +0,0 @@ -// Copyright 2013 The Flutter Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#include "impeller/renderer/render_pass.h" - -#include -#include - -#include "flutter/fml/closure.h" -#include "flutter/fml/logging.h" -#include "impeller/base/base.h" -#include "impeller/renderer/device_buffer.h" -#include "impeller/renderer/formats_metal.h" -#include "impeller/renderer/sampler.h" -#include "impeller/renderer/shader_types.h" - -namespace impeller { - -static bool ConfigureAttachment(const RenderPassAttachment& desc, - MTLRenderPassAttachmentDescriptor* attachment) { - if (!desc.texture) { - return false; - } - - attachment.texture = desc.texture->GetMTLTexture(); - attachment.loadAction = ToMTLLoadAction(desc.load_action); - attachment.storeAction = ToMTLStoreAction(desc.store_action); - return true; -} - -static bool ConfigureColorAttachment( - const RenderPassColorAttachment& desc, - MTLRenderPassColorAttachmentDescriptor* attachment) { - if (!ConfigureAttachment(desc, attachment)) { - return false; - } - attachment.clearColor = ToMTLClearColor(desc.clear_color); - return true; -} - -static bool ConfigureDepthAttachment( - const RenderPassDepthAttachment& desc, - MTLRenderPassDepthAttachmentDescriptor* attachment) { - if (!ConfigureAttachment(desc, attachment)) { - return false; - } - attachment.clearDepth = desc.clear_depth; - return true; -} - -static bool ConfigureStencilAttachment( - const RenderPassStencilAttachment& desc, - MTLRenderPassStencilAttachmentDescriptor* attachment) { - if (!ConfigureAttachment(desc, attachment)) { - return false; - } - attachment.clearStencil = desc.clear_stencil; - return true; -} - -MTLRenderPassDescriptor* RenderPassDescriptor::ToMTLRenderPassDescriptor() - const { - auto result = [MTLRenderPassDescriptor renderPassDescriptor]; - - for (const auto& color : colors_) { - if (!ConfigureColorAttachment(color.second, - result.colorAttachments[color.first])) { - FML_LOG(ERROR) << "Could not configure color attachment at index " - << color.first; - return nil; - } - } - - if (depth_.has_value() && - !ConfigureDepthAttachment(depth_.value(), result.depthAttachment)) { - return nil; - } - - if (stencil_.has_value() && - !ConfigureStencilAttachment(stencil_.value(), result.stencilAttachment)) { - return nil; - } - - return result; -} - -RenderPass::RenderPass(id buffer, - const RenderPassDescriptor& desc) - : buffer_(buffer), - desc_(desc.ToMTLRenderPassDescriptor()), - transients_buffer_(HostBuffer::Create()) { - if (!buffer_ || !desc_) { - return; - } - is_valid_ = true; -} - -RenderPass::~RenderPass() = default; - -HostBuffer& RenderPass::GetTransientsBuffer() { - return *transients_buffer_; -} - -bool RenderPass::IsValid() const { - return is_valid_; -} - -void RenderPass::SetLabel(std::string label) { - label_ = std::move(label); - transients_buffer_->SetLabel(SPrintF("%s Transients", label_.c_str())); -} - -bool RenderPass::Commit(Allocator& transients_allocator) const { - if (!IsValid()) { - return false; - } - auto pass = [buffer_ renderCommandEncoderWithDescriptor:desc_]; - - if (!pass) { - return false; - } - - if (!label_.empty()) { - [pass setLabel:@(label_.c_str())]; - } - - // Success or failure, the pass must end. The buffer can only process one pass - // at a time. - fml::ScopedCleanupClosure auto_end([pass]() { [pass endEncoding]; }); - - return EncodeCommands(transients_allocator, pass); -} - -//----------------------------------------------------------------------------- -/// @brief Ensures that bindings on the pass are not redundantly set or -/// updated. Avoids making the driver do additional checks and makes -/// the frame insights during profiling and instrumentation not -/// complain about the same. -/// -/// There should be no change to rendering if this caching was -/// absent. -/// -struct PassBindingsCache { - PassBindingsCache(id pass) : pass_(pass) {} - - PassBindingsCache(const PassBindingsCache&) = delete; - - PassBindingsCache(PassBindingsCache&&) = delete; - - void SetRenderPipelineState(id pipeline) { - if (pipeline == pipeline_) { - return; - } - pipeline_ = pipeline; - [pass_ setRenderPipelineState:pipeline_]; - } - - void SetDepthStencilState(id depth_stencil) { - if (depth_stencil_ == depth_stencil) { - return; - } - depth_stencil_ = depth_stencil; - [pass_ setDepthStencilState:depth_stencil_]; - } - - bool SetBuffer(ShaderStage stage, - uint64_t index, - uint64_t offset, - id buffer) { - auto& buffers_map = buffers_[stage]; - auto found = buffers_map.find(index); - if (found != buffers_map.end() && found->second.buffer == buffer) { - // The right buffer is bound. Check if its offset needs to be updated. - if (found->second.offset == offset) { - // Buffer and its offset is identical. Nothing to do. - return true; - } - - // Only the offset needs to be updated. - found->second.offset = offset; - - switch (stage) { - case ShaderStage::kVertex: - [pass_ setVertexBufferOffset:offset atIndex:index]; - return true; - case ShaderStage::kFragment: - [pass_ setFragmentBufferOffset:offset atIndex:index]; - return true; - default: - FML_DCHECK(false) - << "Cannot update buffer offset of an unknown stage."; - return false; - } - return true; - } - buffers_map[index] = {buffer, offset}; - switch (stage) { - case ShaderStage::kVertex: - [pass_ setVertexBuffer:buffer offset:offset atIndex:index]; - return true; - case ShaderStage::kFragment: - [pass_ setFragmentBuffer:buffer offset:offset atIndex:index]; - return true; - default: - FML_DCHECK(false) << "Cannot bind buffer to unknown shader stage."; - return false; - } - return false; - } - - bool SetTexture(ShaderStage stage, uint64_t index, id texture) { - auto& texture_map = textures_[stage]; - auto found = texture_map.find(index); - if (found != texture_map.end() && found->second == texture) { - // Already bound. - return true; - } - texture_map[index] = texture; - switch (stage) { - case ShaderStage::kVertex: - [pass_ setVertexTexture:texture atIndex:index]; - return true; - case ShaderStage::kFragment: - [pass_ setFragmentTexture:texture atIndex:index]; - return true; - default: - FML_DCHECK(false) << "Cannot bind buffer to unknown shader stage."; - return false; - } - return false; - } - - bool SetSampler(ShaderStage stage, - uint64_t index, - id sampler) { - auto& sampler_map = samplers_[stage]; - auto found = sampler_map.find(index); - if (found != sampler_map.end() && found->second == sampler) { - // Already bound. - return true; - } - sampler_map[index] = sampler; - switch (stage) { - case ShaderStage::kVertex: - [pass_ setVertexSamplerState:sampler atIndex:index]; - return true; - case ShaderStage::kFragment: - [pass_ setFragmentSamplerState:sampler atIndex:index]; - return true; - default: - FML_DCHECK(false) << "Cannot bind buffer to unknown shader stage."; - return false; - } - return false; - } - - private: - struct BufferOffsetPair { - id buffer = nullptr; - size_t offset = 0u; - }; - using BufferMap = std::map; - using TextureMap = std::map>; - using SamplerMap = std::map>; - - const id pass_; - id pipeline_ = nullptr; - id depth_stencil_ = nullptr; - std::map buffers_; - std::map textures_; - std::map samplers_; -}; - -static bool Bind(PassBindingsCache& pass, - Allocator& allocator, - ShaderStage stage, - size_t bind_index, - const BufferView& view) { - if (!view.buffer) { - return false; - } - - auto device_buffer = view.buffer->GetDeviceBuffer(allocator); - if (!device_buffer) { - return false; - } - - auto buffer = device_buffer->GetMTLBuffer(); - // The Metal call is a void return and we don't want to make it on nil. - if (!buffer) { - return false; - } - - return pass.SetBuffer(stage, bind_index, view.range.offset, buffer); -} - -static bool Bind(PassBindingsCache& pass, - ShaderStage stage, - size_t bind_index, - const Texture& texture) { - if (!texture.IsValid()) { - return false; - } - - return pass.SetTexture(stage, bind_index, texture.GetMTLTexture()); -} - -static bool Bind(PassBindingsCache& pass, - ShaderStage stage, - size_t bind_index, - const Sampler& sampler) { - if (!sampler.IsValid()) { - return false; - } - - return pass.SetSampler(stage, bind_index, sampler.GetMTLSamplerState()); -} - -bool RenderPass::EncodeCommands(Allocator& allocator, - id pass) const { - PassBindingsCache pass_bindings(pass); - auto bind_stage_resources = [&allocator, &pass_bindings]( - const Bindings& bindings, - ShaderStage stage) -> bool { - for (const auto buffer : bindings.buffers) { - if (!Bind(pass_bindings, allocator, stage, buffer.first, buffer.second)) { - return false; - } - } - for (const auto texture : bindings.textures) { - if (!Bind(pass_bindings, stage, texture.first, *texture.second)) { - return false; - } - } - for (const auto sampler : bindings.samplers) { - if (!Bind(pass_bindings, stage, sampler.first, *sampler.second)) { - return false; - } - } - return true; - }; - - fml::closure pop_debug_marker = [pass]() { [pass popDebugGroup]; }; - for (const auto& command : commands_) { - if (command.index_count == 0u) { - FML_DLOG(ERROR) << "Zero index count in render pass command."; - continue; - } - - fml::ScopedCleanupClosure auto_pop_debug_marker(pop_debug_marker); - if (!command.label.empty()) { - [pass pushDebugGroup:@(command.label.c_str())]; - } else { - auto_pop_debug_marker.Release(); - } - pass_bindings.SetRenderPipelineState( - command.pipeline->GetMTLRenderPipelineState()); - pass_bindings.SetDepthStencilState( - command.pipeline->GetMTLDepthStencilState()); - [pass setFrontFacingWinding:command.winding == WindingOrder::kClockwise - ? MTLWindingClockwise - : MTLWindingCounterClockwise]; - [pass setCullMode:MTLCullModeNone]; - if (!bind_stage_resources(command.vertex_bindings, ShaderStage::kVertex)) { - return false; - } - if (!bind_stage_resources(command.fragment_bindings, - ShaderStage::kFragment)) { - return false; - } - auto index_buffer = command.index_buffer.buffer; - if (!index_buffer) { - return false; - } - auto device_buffer = index_buffer->GetDeviceBuffer(allocator); - if (!device_buffer) { - return false; - } - auto mtl_index_buffer = device_buffer->GetMTLBuffer(); - if (!mtl_index_buffer) { - return false; - } - FML_DCHECK(command.index_count * sizeof(uint32_t) == - command.index_buffer.range.length); - // Returns void. All error checking must be done by this point. - [pass drawIndexedPrimitives:ToMTLPrimitiveType(command.primitive_type) - indexCount:command.index_count - indexType:MTLIndexTypeUInt32 - indexBuffer:mtl_index_buffer - indexBufferOffset:command.index_buffer.range.offset - instanceCount:1u - baseVertex:0u - baseInstance:0u]; - } - return true; -} - -bool RenderPass::RecordCommand(Command command) { - if (!command) { - return false; - } - - commands_.emplace_back(std::move(command)); - return true; -} - -} // namespace impeller diff --git a/impeller/renderer/render_pass_descriptor.mm b/impeller/renderer/render_pass_descriptor.cc similarity index 100% rename from impeller/renderer/render_pass_descriptor.mm rename to impeller/renderer/render_pass_descriptor.cc diff --git a/impeller/renderer/render_pass_descriptor.h b/impeller/renderer/render_pass_descriptor.h index 1c68781f0ea38..df4a2a35f962e 100644 --- a/impeller/renderer/render_pass_descriptor.h +++ b/impeller/renderer/render_pass_descriptor.h @@ -7,8 +7,6 @@ #include #include -#include - #include "flutter/fml/macros.h" #include "impeller/geometry/size.h" #include "impeller/renderer/formats.h" @@ -34,8 +32,6 @@ class RenderPassDescriptor { RenderPassDescriptor& SetStencilAttachment( RenderPassStencilAttachment attachment); - MTLRenderPassDescriptor* ToMTLRenderPassDescriptor() const; - private: std::map colors_; std::optional depth_; diff --git a/impeller/renderer/renderer.h b/impeller/renderer/renderer.h index c3c8b56f3bb85..529ccb3c78d80 100644 --- a/impeller/renderer/renderer.h +++ b/impeller/renderer/renderer.h @@ -23,7 +23,7 @@ class Renderer { using RenderCallback = std::function; - Renderer(std::string shaders_directory, std::string main_library_file); + Renderer(std::shared_ptr context); ~Renderer(); diff --git a/impeller/renderer/renderer.mm b/impeller/renderer/renderer.mm index 43a38e8f354d7..461deb0c4cd90 100644 --- a/impeller/renderer/renderer.mm +++ b/impeller/renderer/renderer.mm @@ -12,10 +12,9 @@ constexpr size_t kMaxFramesInFlight = 3u; -Renderer::Renderer(std::string shaders_directory, std::string main_library_file) +Renderer::Renderer(std::shared_ptr context) : frames_in_flight_sema_(::dispatch_semaphore_create(kMaxFramesInFlight)), - context_(std::make_shared(std::move(shaders_directory), - std::move(main_library_file))) { + context_(std::move(context)) { if (!context_->IsValid()) { return; } diff --git a/impeller/renderer/renderer_unittests.mm b/impeller/renderer/renderer_unittests.cc similarity index 99% rename from impeller/renderer/renderer_unittests.mm rename to impeller/renderer/renderer_unittests.cc index f037087f3d8e6..0d5117f68ca9c 100644 --- a/impeller/renderer/renderer_unittests.mm +++ b/impeller/renderer/renderer_unittests.cc @@ -15,7 +15,9 @@ #include "impeller/renderer/pipeline_builder.h" #include "impeller/renderer/pipeline_library.h" #include "impeller/renderer/renderer.h" +#include "impeller/renderer/sampler.h" #include "impeller/renderer/sampler_descriptor.h" +#include "impeller/renderer/sampler_library.h" #include "impeller/renderer/surface.h" #include "impeller/renderer/tessellator.h" #include "impeller/renderer/vertex_buffer_builder.h" diff --git a/impeller/renderer/sampler.mm b/impeller/renderer/sampler.cc similarity index 59% rename from impeller/renderer/sampler.mm rename to impeller/renderer/sampler.cc index 63e4a7404cc17..67f818689a718 100644 --- a/impeller/renderer/sampler.mm +++ b/impeller/renderer/sampler.cc @@ -6,16 +6,8 @@ namespace impeller { -Sampler::Sampler(id state) : state_(state) {} +Sampler::Sampler() = default; Sampler::~Sampler() = default; -bool Sampler::IsValid() const { - return state_; -} - -id Sampler::GetMTLSamplerState() const { - return state_; -} - } // namespace impeller diff --git a/impeller/renderer/sampler.h b/impeller/renderer/sampler.h index da7fdd37f5238..81609ad16df6f 100644 --- a/impeller/renderer/sampler.h +++ b/impeller/renderer/sampler.h @@ -4,29 +4,20 @@ #pragma once -#include - #include "flutter/fml/macros.h" namespace impeller { -class SamplerLibrary; - class Sampler { public: - bool IsValid() const; + virtual ~Sampler(); - ~Sampler(); + virtual bool IsValid() const = 0; - id GetMTLSamplerState() const; + protected: + Sampler(); private: - friend SamplerLibrary; - - id state_ = nullptr; - - Sampler(id state); - FML_DISALLOW_COPY_AND_ASSIGN(Sampler); }; diff --git a/impeller/renderer/sampler_descriptor.cc b/impeller/renderer/sampler_descriptor.cc new file mode 100644 index 0000000000000..35251848bc742 --- /dev/null +++ b/impeller/renderer/sampler_descriptor.cc @@ -0,0 +1,11 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "impeller/renderer/sampler_descriptor.h" + +namespace impeller { + +// + +} // namespace impeller diff --git a/impeller/renderer/sampler_descriptor.h b/impeller/renderer/sampler_descriptor.h index ab1d169884d97..6b0eb69c58d3c 100644 --- a/impeller/renderer/sampler_descriptor.h +++ b/impeller/renderer/sampler_descriptor.h @@ -4,10 +4,6 @@ #pragma once -#include - -#include - #include "flutter/fml/macros.h" #include "impeller/renderer/comparable.h" #include "impeller/renderer/formats.h" @@ -40,25 +36,4 @@ struct SamplerDescriptor final : public Comparable { } }; -class SamplerLibrary { - public: - ~SamplerLibrary(); - - std::shared_ptr GetSampler(SamplerDescriptor descriptor); - - private: - friend Context; - - using Samplers = std::unordered_map, - ComparableHash, - ComparableEqual>; - id device_ = nullptr; - Samplers samplers_; - - SamplerLibrary(id device); - - FML_DISALLOW_COPY_AND_ASSIGN(SamplerLibrary); -}; - } // namespace impeller diff --git a/impeller/renderer/sampler_library.cc b/impeller/renderer/sampler_library.cc new file mode 100644 index 0000000000000..7aae0f779f851 --- /dev/null +++ b/impeller/renderer/sampler_library.cc @@ -0,0 +1,13 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "impeller/renderer/sampler_library.h" + +namespace impeller { + +SamplerLibrary::SamplerLibrary() = default; + +SamplerLibrary::~SamplerLibrary() = default; + +} // namespace impeller diff --git a/impeller/renderer/sampler_library.h b/impeller/renderer/sampler_library.h new file mode 100644 index 0000000000000..e515b6facab36 --- /dev/null +++ b/impeller/renderer/sampler_library.h @@ -0,0 +1,28 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#pragma once + +#include + +#include "flutter/fml/macros.h" +#include "impeller/renderer/sampler_descriptor.h" + +namespace impeller { + +class SamplerLibrary { + public: + virtual ~SamplerLibrary(); + + virtual std::shared_ptr GetSampler( + SamplerDescriptor descriptor) = 0; + + protected: + SamplerLibrary(); + + private: + FML_DISALLOW_COPY_AND_ASSIGN(SamplerLibrary); +}; + +} // namespace impeller diff --git a/impeller/renderer/shader_function.mm b/impeller/renderer/shader_function.cc similarity index 85% rename from impeller/renderer/shader_function.mm rename to impeller/renderer/shader_function.cc index bed9c729fd212..b51a4888d006f 100644 --- a/impeller/renderer/shader_function.mm +++ b/impeller/renderer/shader_function.cc @@ -7,20 +7,14 @@ namespace impeller { ShaderFunction::ShaderFunction(UniqueID parent_library_id, - id function, std::string name, ShaderStage stage) : parent_library_id_(parent_library_id), - function_(function), name_(std::move(name)), stage_(stage) {} ShaderFunction::~ShaderFunction() = default; -id ShaderFunction::GetMTLFunction() const { - return function_; -} - ShaderStage ShaderFunction::GetStage() const { return stage_; } diff --git a/impeller/renderer/shader_function.h b/impeller/renderer/shader_function.h index 04e4f573b939f..682a67e21ee23 100644 --- a/impeller/renderer/shader_function.h +++ b/impeller/renderer/shader_function.h @@ -4,8 +4,6 @@ #pragma once -#include - #include "flutter/fml/hash_combine.h" #include "flutter/fml/macros.h" #include "impeller/renderer/comparable.h" @@ -15,31 +13,27 @@ namespace impeller { class ShaderFunction : public Comparable { public: + // |Comparable| virtual ~ShaderFunction(); ShaderStage GetStage() const; - id GetMTLFunction() const; - // |Comparable| std::size_t GetHash() const override; // |Comparable| bool IsEqual(const ShaderFunction& other) const override; - private: - friend class ShaderLibrary; + protected: + ShaderFunction(UniqueID parent_library_id, + std::string name, + ShaderStage stage); + private: UniqueID parent_library_id_; - id function_ = nullptr; std::string name_; ShaderStage stage_; - ShaderFunction(UniqueID parent_library_id, - id function, - std::string name, - ShaderStage stage); - FML_DISALLOW_COPY_AND_ASSIGN(ShaderFunction); }; diff --git a/impeller/renderer/shader_library.cc b/impeller/renderer/shader_library.cc new file mode 100644 index 0000000000000..0c7b445501afa --- /dev/null +++ b/impeller/renderer/shader_library.cc @@ -0,0 +1,13 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "impeller/renderer/shader_library.h" + +namespace impeller { + +ShaderLibrary::ShaderLibrary() = default; + +ShaderLibrary::~ShaderLibrary() = default; + +} // namespace impeller diff --git a/impeller/renderer/shader_library.h b/impeller/renderer/shader_library.h index 4d28a690ac7d6..357be584cfc91 100644 --- a/impeller/renderer/shader_library.h +++ b/impeller/renderer/shader_library.h @@ -4,64 +4,29 @@ #pragma once -#include - #include -#include #include -#include #include "flutter/fml/macros.h" -#include "impeller/renderer/comparable.h" -#include "impeller/renderer/shader_function.h" +#include "impeller/renderer/shader_types.h" namespace impeller { class Context; +class ShaderFunction; class ShaderLibrary { public: - ~ShaderLibrary(); + virtual ~ShaderLibrary(); - std::shared_ptr GetFunction( + virtual std::shared_ptr GetFunction( const std::string_view& name, - ShaderStage stage); - - private: - friend class Context; - - struct ShaderKey { - std::string name; - ShaderStage stage = ShaderStage::kUnknown; - - ShaderKey(const std::string_view& p_name, ShaderStage p_stage) - : name({p_name.data(), p_name.size()}), stage(p_stage) {} + ShaderStage stage) = 0; - struct Hash { - size_t operator()(const ShaderKey& key) const { - return fml::HashCombine(key.name, key.stage); - } - }; - - struct Equal { - constexpr bool operator()(const ShaderKey& k1, - const ShaderKey& k2) const { - return k1.stage == k2.stage && k1.name == k2.name; - } - }; - }; - - using Functions = std::unordered_map, - ShaderKey::Hash, - ShaderKey::Equal>; - - UniqueID library_id_; - id library_ = nullptr; - Functions functions_; - - ShaderLibrary(id library); + protected: + ShaderLibrary(); + private: FML_DISALLOW_COPY_AND_ASSIGN(ShaderLibrary); }; diff --git a/impeller/renderer/shader_library.mm b/impeller/renderer/shader_library.mm deleted file mode 100644 index baeb0fac0a2fe..0000000000000 --- a/impeller/renderer/shader_library.mm +++ /dev/null @@ -1,37 +0,0 @@ -// Copyright 2013 The Flutter Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#include "impeller/renderer/shader_library.h" - -namespace impeller { - -ShaderLibrary::ShaderLibrary(id library) : library_(library) {} - -ShaderLibrary::~ShaderLibrary() = default; - -std::shared_ptr ShaderLibrary::GetFunction( - const std::string_view& name, - ShaderStage stage) { - if (!library_) { - return nullptr; - } - - ShaderKey key(name, stage); - - if (auto found = functions_.find(key); found != functions_.end()) { - return found->second; - } - - auto function = [library_ newFunctionWithName:@(name.data())]; - if (!function) { - return nullptr; - } - - auto func = std::shared_ptr(new ShaderFunction( - library_id_, function, {name.data(), name.size()}, stage)); - functions_[key] = func; - return func; -} - -} // namespace impeller diff --git a/impeller/renderer/surface.mm b/impeller/renderer/surface.cc similarity index 100% rename from impeller/renderer/surface.mm rename to impeller/renderer/surface.cc diff --git a/impeller/renderer/surface.h b/impeller/renderer/surface.h index 231d89e424a0e..2fb2d85a821dc 100644 --- a/impeller/renderer/surface.h +++ b/impeller/renderer/surface.h @@ -4,14 +4,13 @@ #pragma once -#include - #include #include #include "flutter/fml/macros.h" #include "impeller/renderer/context.h" #include "impeller/renderer/render_pass.h" +#include "impeller/renderer/render_pass_descriptor.h" namespace impeller { diff --git a/impeller/renderer/texture.cc b/impeller/renderer/texture.cc new file mode 100644 index 0000000000000..1d29828e938ee --- /dev/null +++ b/impeller/renderer/texture.cc @@ -0,0 +1,17 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "impeller/renderer/texture.h" + +namespace impeller { + +Texture::Texture(TextureDescriptor desc) : desc_(std::move(desc)) {} + +Texture::~Texture() = default; + +const TextureDescriptor& Texture::GetTextureDescriptor() const { + return desc_; +} + +} // namespace impeller diff --git a/impeller/renderer/texture.h b/impeller/renderer/texture.h index d3067498e61e7..a698820432baf 100644 --- a/impeller/renderer/texture.h +++ b/impeller/renderer/texture.h @@ -6,8 +6,6 @@ #include -#include - #include "flutter/fml/macros.h" #include "impeller/geometry/size.h" #include "impeller/renderer/formats.h" @@ -17,24 +15,24 @@ namespace impeller { class Texture { public: - Texture(TextureDescriptor desc, id texture); + virtual ~Texture(); - ~Texture(); + virtual void SetLabel(const std::string_view& label) = 0; - void SetLabel(const std::string_view& label); + [[nodiscard]] virtual bool SetContents(const uint8_t* contents, + size_t length) = 0; - [[nodiscard]] bool SetContents(const uint8_t* contents, size_t length); + virtual bool IsValid() const = 0; - bool IsValid() const; + virtual ISize GetSize() const = 0; - ISize GetSize() const; + const TextureDescriptor& GetTextureDescriptor() const; - id GetMTLTexture() const; + protected: + Texture(TextureDescriptor desc); private: const TextureDescriptor desc_; - id texture_ = nullptr; - bool is_valid_ = false; FML_DISALLOW_COPY_AND_ASSIGN(Texture); }; diff --git a/impeller/renderer/texture.mm b/impeller/renderer/texture.mm deleted file mode 100644 index 591ceb1202422..0000000000000 --- a/impeller/renderer/texture.mm +++ /dev/null @@ -1,68 +0,0 @@ -// Copyright 2013 The Flutter Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#include "impeller/renderer/texture.h" - -namespace impeller { - -Texture::Texture(TextureDescriptor desc, id texture) - : desc_(std::move(desc)), texture_(texture) { - if (!desc_.IsValid() || !texture_) { - return; - } - - if (desc_.size != GetSize()) { - FML_DLOG(ERROR) - << "The texture and its descriptor disagree about its size."; - return; - } - - is_valid_ = true; -} - -Texture::~Texture() = default; - -void Texture::SetLabel(const std::string_view& label) { - [texture_ setLabel:@(label.data())]; -} - -bool Texture::SetContents(const uint8_t* contents, size_t length) { - if (!IsValid() || !contents) { - return false; - } - - // Out of bounds access. - if (length != desc_.GetSizeOfBaseMipLevel()) { - return false; - } - - // TODO(csg): Perhaps the storage mode should be added to the texture - // descriptor so that invalid region replacements on potentially non-host - // visible textures are disallowed. The annoying bit about the API below is - // that there seems to be no error handling guidance. - const auto region = - MTLRegionMake2D(0u, 0u, desc_.size.width, desc_.size.height); - [texture_ replaceRegion:region // - mipmapLevel:0u // - withBytes:contents // - bytesPerRow:desc_.GetBytesPerRow() // - ]; - - return true; -} - -ISize Texture::GetSize() const { - return {static_cast(texture_.width), - static_cast(texture_.height)}; -} - -id Texture::GetMTLTexture() const { - return texture_; -} - -bool Texture::IsValid() const { - return is_valid_; -} - -} // namespace impeller diff --git a/impeller/renderer/texture_descriptor.mm b/impeller/renderer/texture_descriptor.cc similarity index 100% rename from impeller/renderer/texture_descriptor.mm rename to impeller/renderer/texture_descriptor.cc diff --git a/impeller/renderer/vertex_buffer.mm b/impeller/renderer/vertex_buffer.cc similarity index 100% rename from impeller/renderer/vertex_buffer.mm rename to impeller/renderer/vertex_buffer.cc diff --git a/impeller/renderer/vertex_buffer_builder.mm b/impeller/renderer/vertex_buffer_builder.cc similarity index 100% rename from impeller/renderer/vertex_buffer_builder.mm rename to impeller/renderer/vertex_buffer_builder.cc diff --git a/impeller/renderer/vertex_descriptor.mm b/impeller/renderer/vertex_descriptor.cc similarity index 100% rename from impeller/renderer/vertex_descriptor.mm rename to impeller/renderer/vertex_descriptor.cc diff --git a/impeller/renderer/vertex_descriptor.h b/impeller/renderer/vertex_descriptor.h index 26e63be373bcc..09f7c20dbcb28 100644 --- a/impeller/renderer/vertex_descriptor.h +++ b/impeller/renderer/vertex_descriptor.h @@ -4,8 +4,6 @@ #pragma once -#include - #include #include "flutter/fml/macros.h" From 6d6d4d44501c3125963279e75287071d5f3feb08 Mon Sep 17 00:00:00 2001 From: Chinmay Garde Date: Mon, 1 Nov 2021 13:50:58 -0700 Subject: [PATCH 178/433] Make Impeller ObjC agnostic. --- impeller/playground/playground.mm | 5 +- impeller/renderer/BUILD.gn | 2 + .../renderer/backend/metal/allocator_mtl.mm | 3 +- .../backend/metal/device_buffer_mtl.mm | 3 +- .../renderer/backend/metal/formats_mtl.mm | 2 +- .../backend/metal/pipeline_library_mtl.mm | 8 +- .../renderer/backend/metal/render_pass_mtl.mm | 23 +- impeller/renderer/backend/metal/texture_mtl.h | 11 +- .../backend/metal/vertex_descriptor_mtl.h | 37 +++ .../backend/metal/vertex_descriptor_mtl.mm | 215 ++++++++++++++++++ impeller/renderer/pipeline_descriptor.cc | 2 +- impeller/renderer/render_pass_descriptor.cc | 15 ++ impeller/renderer/render_pass_descriptor.h | 8 + impeller/renderer/shader_types.h | 18 ++ impeller/renderer/vertex_descriptor.cc | 210 +---------------- impeller/renderer/vertex_descriptor.h | 22 +- 16 files changed, 349 insertions(+), 235 deletions(-) create mode 100644 impeller/renderer/backend/metal/vertex_descriptor_mtl.h create mode 100644 impeller/renderer/backend/metal/vertex_descriptor_mtl.mm diff --git a/impeller/playground/playground.mm b/impeller/playground/playground.mm index f9397e1a9fddd..67fe3807683e9 100644 --- a/impeller/playground/playground.mm +++ b/impeller/playground/playground.mm @@ -10,8 +10,9 @@ #include "impeller/playground/playground.h" #include "impeller/renderer/allocator.h" #include "impeller/renderer/backend/metal/context_mtl.h" +#include "impeller/renderer/backend/metal/texture_mtl.h" #include "impeller/renderer/context.h" -#include "impeller/renderer/formats_metal.h" +#include "impeller/renderer/formats.h" #include "impeller/renderer/render_pass.h" #include "impeller/renderer/renderer.h" #include "impeller/renderer/surface.h" @@ -141,7 +142,7 @@ static void PlaygroundKeyCallback(GLFWwindow* window, RenderPassColorAttachment color0; color0.texture = - std::make_shared(color0_desc, current_drawable.texture); + std::make_shared(color0_desc, current_drawable.texture); color0.clear_color = Color::SkyBlue(); color0.load_action = LoadAction::kClear; color0.store_action = StoreAction::kStore; diff --git a/impeller/renderer/BUILD.gn b/impeller/renderer/BUILD.gn index ef46bf756befd..fb3a6f7ef2f26 100644 --- a/impeller/renderer/BUILD.gn +++ b/impeller/renderer/BUILD.gn @@ -33,6 +33,8 @@ impeller_component("renderer") { "backend/metal/shader_library_mtl.mm", "backend/metal/texture_mtl.h", "backend/metal/texture_mtl.mm", + "backend/metal/vertex_descriptor_mtl.h", + "backend/metal/vertex_descriptor_mtl.mm", ] sources = [ diff --git a/impeller/renderer/backend/metal/allocator_mtl.mm b/impeller/renderer/backend/metal/allocator_mtl.mm index 0414bd135d96f..b331fdcfe3103 100644 --- a/impeller/renderer/backend/metal/allocator_mtl.mm +++ b/impeller/renderer/backend/metal/allocator_mtl.mm @@ -8,6 +8,7 @@ #include "flutter/fml/logging.h" #include "impeller/renderer/backend/metal/device_buffer_mtl.h" #include "impeller/renderer/backend/metal/formats_mtl.h" +#include "impeller/renderer/backend/metal/texture_mtl.h" #include "impeller/renderer/buffer.h" namespace impeller { @@ -115,7 +116,7 @@ static MTLStorageMode ToMTLStorageMode(StorageMode mode) { if (!texture) { return nullptr; } - return std::make_shared(desc, texture); + return std::make_shared(desc, texture); } } // namespace impeller diff --git a/impeller/renderer/backend/metal/device_buffer_mtl.mm b/impeller/renderer/backend/metal/device_buffer_mtl.mm index 4469cc349752c..2aa7e5a2cdeea 100644 --- a/impeller/renderer/backend/metal/device_buffer_mtl.mm +++ b/impeller/renderer/backend/metal/device_buffer_mtl.mm @@ -6,6 +6,7 @@ #include "flutter/fml/logging.h" #include "impeller/renderer/backend/metal/formats_mtl.h" +#include "impeller/renderer/backend/metal/texture_mtl.h" namespace impeller { @@ -39,7 +40,7 @@ return nullptr; } - return std::make_shared(desc, texture); + return std::make_shared(desc, texture); } [[nodiscard]] bool DeviceBufferMTL::CopyHostBuffer(const uint8_t* source, diff --git a/impeller/renderer/backend/metal/formats_mtl.mm b/impeller/renderer/backend/metal/formats_mtl.mm index ba23493979dbc..2030a07cbe687 100644 --- a/impeller/renderer/backend/metal/formats_mtl.mm +++ b/impeller/renderer/backend/metal/formats_mtl.mm @@ -2,7 +2,7 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -#include "impeller/renderer/formats_metal.h" +#include "impeller/renderer/backend/metal/formats_mtl.h" #include diff --git a/impeller/renderer/backend/metal/pipeline_library_mtl.mm b/impeller/renderer/backend/metal/pipeline_library_mtl.mm index 73d0f26e725bc..6d8bfc6e24b59 100644 --- a/impeller/renderer/backend/metal/pipeline_library_mtl.mm +++ b/impeller/renderer/backend/metal/pipeline_library_mtl.mm @@ -7,6 +7,7 @@ #include "impeller/renderer/backend/metal/formats_mtl.h" #include "impeller/renderer/backend/metal/pipeline_mtl.h" #include "impeller/renderer/backend/metal/shader_function_mtl.h" +#include "impeller/renderer/backend/metal/vertex_descriptor_mtl.h" namespace impeller { @@ -34,7 +35,12 @@ } if (const auto& vertex_descriptor = desc.GetVertexDescriptor()) { - descriptor.vertexDescriptor = vertex_descriptor->GetMTLVertexDescriptor(); + VertexDescriptorMTL vertex_descriptor_mtl; + if (vertex_descriptor_mtl.SetStageInputs( + vertex_descriptor->GetStageInputs())) { + descriptor.vertexDescriptor = + vertex_descriptor_mtl.GetMTLVertexDescriptor(); + } } for (const auto& item : desc.GetColorAttachmentDescriptors()) { diff --git a/impeller/renderer/backend/metal/render_pass_mtl.mm b/impeller/renderer/backend/metal/render_pass_mtl.mm index 29d02f81d3da8..f2549a4d0d037 100644 --- a/impeller/renderer/backend/metal/render_pass_mtl.mm +++ b/impeller/renderer/backend/metal/render_pass_mtl.mm @@ -59,11 +59,14 @@ static bool ConfigureStencilAttachment( return true; } -MTLRenderPassDescriptor* RenderPassDescriptor::ToMTLRenderPassDescriptor() - const { +// TODO(csg): Move this to formats_mtl.h +static MTLRenderPassDescriptor* ToMTLRenderPassDescriptor( + const RenderPassDescriptor& desc) { auto result = [MTLRenderPassDescriptor renderPassDescriptor]; - for (const auto& color : colors_) { + const auto& colors = desc.GetColorAttachments(); + + for (const auto& color : colors) { if (!ConfigureColorAttachment(color.second, result.colorAttachments[color.first])) { FML_LOG(ERROR) << "Could not configure color attachment at index " @@ -72,13 +75,17 @@ static bool ConfigureStencilAttachment( } } - if (depth_.has_value() && - !ConfigureDepthAttachment(depth_.value(), result.depthAttachment)) { + const auto& depth = desc.GetDepthAttachment(); + + if (depth.has_value() && + !ConfigureDepthAttachment(depth.value(), result.depthAttachment)) { return nil; } - if (stencil_.has_value() && - !ConfigureStencilAttachment(stencil_.value(), result.stencilAttachment)) { + const auto& stencil = desc.GetStencilAttachment(); + + if (stencil.has_value() && + !ConfigureStencilAttachment(stencil.value(), result.stencilAttachment)) { return nil; } @@ -88,7 +95,7 @@ static bool ConfigureStencilAttachment( RenderPassMTL::RenderPassMTL(id buffer, const RenderPassDescriptor& desc) : buffer_(buffer), - desc_(desc.ToMTLRenderPassDescriptor()), + desc_(ToMTLRenderPassDescriptor(desc)), transients_buffer_(HostBuffer::Create()) { if (!buffer_ || !desc_) { return; diff --git a/impeller/renderer/backend/metal/texture_mtl.h b/impeller/renderer/backend/metal/texture_mtl.h index 4ffeb9761c19e..8f254672e2e23 100644 --- a/impeller/renderer/backend/metal/texture_mtl.h +++ b/impeller/renderer/backend/metal/texture_mtl.h @@ -15,16 +15,21 @@ namespace impeller { class TextureMTL final : public Texture, public BackendCast { public: + TextureMTL(TextureDescriptor desc, id texture); + // |Texture| ~TextureMTL() override; + // |Texture| void SetLabel(const std::string_view& label) override; - [[nodiscard]] bool SetContents(const uint8_t* contents, - size_t length) override; + // |Texture| + bool SetContents(const uint8_t* contents, size_t length) override; + // |Texture| bool IsValid() const override; + // |Texture| ISize GetSize() const override; id GetMTLTexture() const; @@ -33,8 +38,6 @@ class TextureMTL final : public Texture, id texture_ = nullptr; bool is_valid_ = false; - TextureMTL(TextureDescriptor desc, id texture); - FML_DISALLOW_COPY_AND_ASSIGN(TextureMTL); }; diff --git a/impeller/renderer/backend/metal/vertex_descriptor_mtl.h b/impeller/renderer/backend/metal/vertex_descriptor_mtl.h new file mode 100644 index 0000000000000..7f8851f4b47f8 --- /dev/null +++ b/impeller/renderer/backend/metal/vertex_descriptor_mtl.h @@ -0,0 +1,37 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include + +#include "flutter/fml/macros.h" +#include "impeller/renderer/backend/metal/backend_cast.h" +#include "impeller/renderer/vertex_descriptor.h" + +namespace impeller { + +class VertexDescriptorMTL { + public: + VertexDescriptorMTL(); + + ~VertexDescriptorMTL(); + + bool SetStageInputs(const std::vector& inputs); + + MTLVertexDescriptor* GetMTLVertexDescriptor() const; + + private: + struct StageInput { + size_t location; + MTLVertexFormat format; + size_t length; + + StageInput(size_t p_location, MTLVertexFormat p_format, size_t p_length) + : location(p_location), format(p_format), length(p_length) {} + }; + std::vector stage_inputs_; + + FML_DISALLOW_COPY_AND_ASSIGN(VertexDescriptorMTL); +}; + +} // namespace impeller diff --git a/impeller/renderer/backend/metal/vertex_descriptor_mtl.mm b/impeller/renderer/backend/metal/vertex_descriptor_mtl.mm new file mode 100644 index 0000000000000..244bd07b0a756 --- /dev/null +++ b/impeller/renderer/backend/metal/vertex_descriptor_mtl.mm @@ -0,0 +1,215 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "impeller/renderer/backend/metal/vertex_descriptor_mtl.h" + +#include "flutter/fml/logging.h" + +namespace impeller { + +VertexDescriptorMTL::VertexDescriptorMTL() = default; + +VertexDescriptorMTL::~VertexDescriptorMTL() = default; + +static MTLVertexFormat ReadStageInputFormat(const ShaderStageIOSlot& input) { + if (input.columns != 1) { + // All matrix types are unsupported as vertex inputs. + return MTLVertexFormatInvalid; + } + + switch (input.type) { + case ShaderType::kFloat: { + if (input.bit_width == 8 * sizeof(float)) { + switch (input.vec_size) { + case 1: + return MTLVertexFormatFloat; + case 2: + return MTLVertexFormatFloat2; + case 3: + return MTLVertexFormatFloat3; + case 4: + return MTLVertexFormatFloat4; + } + } + return MTLVertexFormatInvalid; + } + case ShaderType::kHalfFloat: { + if (input.bit_width == 8 * sizeof(float) / 2) { + switch (input.vec_size) { + case 1: + return MTLVertexFormatHalf; + case 2: + return MTLVertexFormatHalf2; + case 3: + return MTLVertexFormatHalf3; + case 4: + return MTLVertexFormatHalf4; + } + } + return MTLVertexFormatInvalid; + } + case ShaderType::kDouble: { + // Unsupported. + return MTLVertexFormatInvalid; + } + case ShaderType::kBoolean: { + if (input.bit_width == 8 * sizeof(bool) && input.vec_size == 1) { + return MTLVertexFormatChar; + } + return MTLVertexFormatInvalid; + } + case ShaderType::kSignedByte: { + if (input.bit_width == 8 * sizeof(char)) { + switch (input.vec_size) { + case 1: + return MTLVertexFormatChar; + case 2: + return MTLVertexFormatChar2; + case 3: + return MTLVertexFormatChar3; + case 4: + return MTLVertexFormatChar4; + } + } + return MTLVertexFormatInvalid; + } + case ShaderType::kUnsignedByte: { + if (input.bit_width == 8 * sizeof(char)) { + switch (input.vec_size) { + case 1: + return MTLVertexFormatUChar; + case 2: + return MTLVertexFormatUChar2; + case 3: + return MTLVertexFormatUChar3; + case 4: + return MTLVertexFormatUChar4; + } + } + return MTLVertexFormatInvalid; + } + case ShaderType::kSignedShort: { + if (input.bit_width == 8 * sizeof(short)) { + switch (input.vec_size) { + case 1: + return MTLVertexFormatShort; + case 2: + return MTLVertexFormatShort2; + case 3: + return MTLVertexFormatShort3; + case 4: + return MTLVertexFormatShort4; + } + } + return MTLVertexFormatInvalid; + } + case ShaderType::kUnsignedShort: { + if (input.bit_width == 8 * sizeof(ushort)) { + switch (input.vec_size) { + case 1: + return MTLVertexFormatUShort; + case 2: + return MTLVertexFormatUShort2; + case 3: + return MTLVertexFormatUShort3; + case 4: + return MTLVertexFormatUShort4; + } + } + return MTLVertexFormatInvalid; + } + case ShaderType::kSignedInt: { + if (input.bit_width == 8 * sizeof(int32_t)) { + switch (input.vec_size) { + case 1: + return MTLVertexFormatInt; + case 2: + return MTLVertexFormatInt2; + case 3: + return MTLVertexFormatInt3; + case 4: + return MTLVertexFormatInt4; + } + } + return MTLVertexFormatInvalid; + } + case ShaderType::kUnsignedInt: { + if (input.bit_width == 8 * sizeof(uint32_t)) { + switch (input.vec_size) { + case 1: + return MTLVertexFormatUInt; + case 2: + return MTLVertexFormatUInt2; + case 3: + return MTLVertexFormatUInt3; + case 4: + return MTLVertexFormatUInt4; + } + } + return MTLVertexFormatInvalid; + } + case ShaderType::kSignedInt64: { + // Unsupported. + return MTLVertexFormatInvalid; + } + case ShaderType::kUnsignedInt64: { + // Unsupported. + return MTLVertexFormatInvalid; + } + case ShaderType::kAtomicCounter: + case ShaderType::kStruct: + case ShaderType::kImage: + case ShaderType::kSampledImage: + case ShaderType::kUnknown: + case ShaderType::kVoid: + case ShaderType::kSampler: + return MTLVertexFormatInvalid; + } +} + +bool VertexDescriptorMTL::SetStageInputs( + const std::vector& inputs) { + stage_inputs_.clear(); + + for (size_t i = 0; i < inputs.size(); i++) { + const auto& input = inputs[i]; + auto vertex_format = ReadStageInputFormat(input); + if (vertex_format == MTLVertexFormatInvalid) { + FML_LOG(ERROR) << "Format for input " << input.name << " not supported."; + return false; + } + stage_inputs_.emplace_back(StageInput{ + input.location, vertex_format, (input.bit_width * input.vec_size) / 8}); + } + + return true; +} + +MTLVertexDescriptor* VertexDescriptorMTL::GetMTLVertexDescriptor() const { + auto descriptor = [MTLVertexDescriptor vertexDescriptor]; + + const size_t vertex_buffer_index = + PipelineVertexDescriptor::kReservedVertexBufferIndex; + + size_t stride = 0u; + for (const auto& input : stage_inputs_) { + auto attrib = descriptor.attributes[input.location]; + attrib.format = input.format; + attrib.offset = stride; + // All vertex inputs are interleaved and tightly packed in one buffer at a + // reserved index. + attrib.bufferIndex = vertex_buffer_index; + stride += input.length; + } + + // Since it's all in one buffer, indicate its layout. + auto vertex_layout = descriptor.layouts[vertex_buffer_index]; + vertex_layout.stride = stride; + vertex_layout.stepRate = 1u; + vertex_layout.stepFunction = MTLVertexStepFunctionPerVertex; + + return descriptor; +} + +} // namespace impeller diff --git a/impeller/renderer/pipeline_descriptor.cc b/impeller/renderer/pipeline_descriptor.cc index 25484f06bf6dc..d7ebb742b7c83 100644 --- a/impeller/renderer/pipeline_descriptor.cc +++ b/impeller/renderer/pipeline_descriptor.cc @@ -4,7 +4,7 @@ #include "impeller/renderer/pipeline_descriptor.h" -#include "impeller/renderer/formats_metal.h" +#include "impeller/renderer/formats.h" #include "impeller/renderer/shader_function.h" #include "impeller/renderer/shader_library.h" #include "impeller/renderer/vertex_descriptor.h" diff --git a/impeller/renderer/render_pass_descriptor.cc b/impeller/renderer/render_pass_descriptor.cc index b267bc40f05f1..bbd8f326b7cfc 100644 --- a/impeller/renderer/render_pass_descriptor.cc +++ b/impeller/renderer/render_pass_descriptor.cc @@ -55,4 +55,19 @@ RenderPassDescriptor& RenderPassDescriptor::SetStencilAttachment( return *this; } +const std::map& +RenderPassDescriptor::GetColorAttachments() const { + return colors_; +} + +const std::optional& +RenderPassDescriptor::GetDepthAttachment() const { + return depth_; +} + +const std::optional& +RenderPassDescriptor::GetStencilAttachment() const { + return stencil_; +} + } // namespace impeller diff --git a/impeller/renderer/render_pass_descriptor.h b/impeller/renderer/render_pass_descriptor.h index df4a2a35f962e..d0f17f1c696e2 100644 --- a/impeller/renderer/render_pass_descriptor.h +++ b/impeller/renderer/render_pass_descriptor.h @@ -32,6 +32,14 @@ class RenderPassDescriptor { RenderPassDescriptor& SetStencilAttachment( RenderPassStencilAttachment attachment); + const std::map& GetColorAttachments() + const; + + const std::optional& GetDepthAttachment() const; + + const std::optional& GetStencilAttachment() + const; + private: std::map colors_; std::optional depth_; diff --git a/impeller/renderer/shader_types.h b/impeller/renderer/shader_types.h index 9045829681720..efcc28a2a34a7 100644 --- a/impeller/renderer/shader_types.h +++ b/impeller/renderer/shader_types.h @@ -8,6 +8,7 @@ #include #include +#include "flutter/fml/hash_combine.h" #include "impeller/geometry/matrix.h" namespace impeller { @@ -48,6 +49,7 @@ struct ShaderUniformSlot { }; struct ShaderStageIOSlot { + // Statically allocated const string containing advisory debug description. const char* name; size_t location; size_t set; @@ -56,6 +58,22 @@ struct ShaderStageIOSlot { size_t bit_width; size_t vec_size; size_t columns; + + constexpr size_t GetHash() const { + return fml::HashCombine(name, location, set, binding, type, bit_width, + vec_size, columns); + } + + constexpr bool operator==(const ShaderStageIOSlot& other) const { + return name == other.name && // + location == other.location && // + set == other.set && // + binding == other.binding && // + type == other.type && // + bit_width == other.bit_width && // + vec_size == other.vec_size && // + columns == other.columns; + } }; struct SampledImageSlot { diff --git a/impeller/renderer/vertex_descriptor.cc b/impeller/renderer/vertex_descriptor.cc index 55121bcc5fec7..d919da06a5d48 100644 --- a/impeller/renderer/vertex_descriptor.cc +++ b/impeller/renderer/vertex_descriptor.cc @@ -4,220 +4,27 @@ #include "impeller/renderer/vertex_descriptor.h" -#include "flutter/fml/logging.h" - namespace impeller { PipelineVertexDescriptor::PipelineVertexDescriptor() = default; PipelineVertexDescriptor::~PipelineVertexDescriptor() = default; -static MTLVertexFormat ReadStageInputFormat(const ShaderStageIOSlot& input) { - if (input.columns != 1) { - // All matrix types are unsupported as vertex inputs. - return MTLVertexFormatInvalid; - } - - switch (input.type) { - case ShaderType::kFloat: { - if (input.bit_width == 8 * sizeof(float)) { - switch (input.vec_size) { - case 1: - return MTLVertexFormatFloat; - case 2: - return MTLVertexFormatFloat2; - case 3: - return MTLVertexFormatFloat3; - case 4: - return MTLVertexFormatFloat4; - } - } - return MTLVertexFormatInvalid; - } - case ShaderType::kHalfFloat: { - if (input.bit_width == 8 * sizeof(float) / 2) { - switch (input.vec_size) { - case 1: - return MTLVertexFormatHalf; - case 2: - return MTLVertexFormatHalf2; - case 3: - return MTLVertexFormatHalf3; - case 4: - return MTLVertexFormatHalf4; - } - } - return MTLVertexFormatInvalid; - } - case ShaderType::kDouble: { - // Unsupported. - return MTLVertexFormatInvalid; - } - case ShaderType::kBoolean: { - if (input.bit_width == 8 * sizeof(bool) && input.vec_size == 1) { - return MTLVertexFormatChar; - } - return MTLVertexFormatInvalid; - } - case ShaderType::kSignedByte: { - if (input.bit_width == 8 * sizeof(char)) { - switch (input.vec_size) { - case 1: - return MTLVertexFormatChar; - case 2: - return MTLVertexFormatChar2; - case 3: - return MTLVertexFormatChar3; - case 4: - return MTLVertexFormatChar4; - } - } - return MTLVertexFormatInvalid; - } - case ShaderType::kUnsignedByte: { - if (input.bit_width == 8 * sizeof(char)) { - switch (input.vec_size) { - case 1: - return MTLVertexFormatUChar; - case 2: - return MTLVertexFormatUChar2; - case 3: - return MTLVertexFormatUChar3; - case 4: - return MTLVertexFormatUChar4; - } - } - return MTLVertexFormatInvalid; - } - case ShaderType::kSignedShort: { - if (input.bit_width == 8 * sizeof(short)) { - switch (input.vec_size) { - case 1: - return MTLVertexFormatShort; - case 2: - return MTLVertexFormatShort2; - case 3: - return MTLVertexFormatShort3; - case 4: - return MTLVertexFormatShort4; - } - } - return MTLVertexFormatInvalid; - } - case ShaderType::kUnsignedShort: { - if (input.bit_width == 8 * sizeof(ushort)) { - switch (input.vec_size) { - case 1: - return MTLVertexFormatUShort; - case 2: - return MTLVertexFormatUShort2; - case 3: - return MTLVertexFormatUShort3; - case 4: - return MTLVertexFormatUShort4; - } - } - return MTLVertexFormatInvalid; - } - case ShaderType::kSignedInt: { - if (input.bit_width == 8 * sizeof(int32_t)) { - switch (input.vec_size) { - case 1: - return MTLVertexFormatInt; - case 2: - return MTLVertexFormatInt2; - case 3: - return MTLVertexFormatInt3; - case 4: - return MTLVertexFormatInt4; - } - } - return MTLVertexFormatInvalid; - } - case ShaderType::kUnsignedInt: { - if (input.bit_width == 8 * sizeof(uint32_t)) { - switch (input.vec_size) { - case 1: - return MTLVertexFormatUInt; - case 2: - return MTLVertexFormatUInt2; - case 3: - return MTLVertexFormatUInt3; - case 4: - return MTLVertexFormatUInt4; - } - } - return MTLVertexFormatInvalid; - } - case ShaderType::kSignedInt64: { - // Unsupported. - return MTLVertexFormatInvalid; - } - case ShaderType::kUnsignedInt64: { - // Unsupported. - return MTLVertexFormatInvalid; - } - case ShaderType::kAtomicCounter: - case ShaderType::kStruct: - case ShaderType::kImage: - case ShaderType::kSampledImage: - case ShaderType::kUnknown: - case ShaderType::kVoid: - case ShaderType::kSampler: - return MTLVertexFormatInvalid; - } -} - bool PipelineVertexDescriptor::SetStageInputs( const ShaderStageIOSlot* const stage_inputs[], size_t count) { - stage_inputs_.clear(); - + inputs_.reserve(inputs_.size() + count); for (size_t i = 0; i < count; i++) { - const ShaderStageIOSlot* input = stage_inputs[i]; - auto vertex_format = ReadStageInputFormat(*input); - if (vertex_format == MTLVertexFormatInvalid) { - FML_LOG(ERROR) << "Format for input " << input->name << " not supported."; - return false; - } - stage_inputs_.emplace_back( - StageInput{input->location, vertex_format, - (input->bit_width * input->vec_size) / 8}); + inputs_.emplace_back(*stage_inputs[i]); } - return true; } -MTLVertexDescriptor* PipelineVertexDescriptor::GetMTLVertexDescriptor() const { - auto descriptor = [MTLVertexDescriptor vertexDescriptor]; - - const size_t vertex_buffer_index = kReservedVertexBufferIndex; - - size_t stride = 0u; - for (const auto& input : stage_inputs_) { - auto attrib = descriptor.attributes[input.location]; - attrib.format = input.format; - attrib.offset = stride; - // All vertex inputs are interleaved and tightly packed in one buffer at a - // reserved index. - attrib.bufferIndex = vertex_buffer_index; - stride += input.length; - } - - // Since it's all in one buffer, indicate its layout. - auto vertex_layout = descriptor.layouts[vertex_buffer_index]; - vertex_layout.stride = stride; - vertex_layout.stepRate = 1u; - vertex_layout.stepFunction = MTLVertexStepFunctionPerVertex; - - return descriptor; -} - // |Comparable| -std::size_t PipelineVertexDescriptor::GetHash() const { +size_t PipelineVertexDescriptor::GetHash() const { auto seed = fml::HashCombine(); - for (const auto& input : stage_inputs_) { - fml::HashCombineSeed(seed, input.location, input.format); + for (const auto& input : inputs_) { + fml::HashCombineSeed(seed, input.GetHash()); } return seed; } @@ -225,7 +32,12 @@ std::size_t PipelineVertexDescriptor::GetHash() const { // |Comparable| bool PipelineVertexDescriptor::IsEqual( const PipelineVertexDescriptor& other) const { - return stage_inputs_ == other.stage_inputs_; + return inputs_ == other.inputs_; +} + +const std::vector& PipelineVertexDescriptor::GetStageInputs() + const { + return inputs_; } } // namespace impeller diff --git a/impeller/renderer/vertex_descriptor.h b/impeller/renderer/vertex_descriptor.h index 09f7c20dbcb28..114abcd51c59e 100644 --- a/impeller/renderer/vertex_descriptor.h +++ b/impeller/renderer/vertex_descriptor.h @@ -28,6 +28,7 @@ class PipelineVertexDescriptor final PipelineVertexDescriptor(); + // |Comparable| virtual ~PipelineVertexDescriptor(); template @@ -39,29 +40,16 @@ class PipelineVertexDescriptor final bool SetStageInputs(const ShaderStageIOSlot* const stage_inputs[], size_t count); - //| Comparable| + const std::vector& GetStageInputs() const; + + // |Comparable| std::size_t GetHash() const override; // |Comparable| bool IsEqual(const PipelineVertexDescriptor& other) const override; - MTLVertexDescriptor* GetMTLVertexDescriptor() const; - private: - struct StageInput { - size_t location; - MTLVertexFormat format; - size_t length; - - StageInput(size_t p_location, MTLVertexFormat p_format, size_t p_length) - : location(p_location), format(p_format), length(p_length) {} - - constexpr bool operator==(const StageInput& other) const { - return location == other.location && format == other.format && - length == other.length; - } - }; - std::vector stage_inputs_; + std::vector inputs_; FML_DISALLOW_COPY_AND_ASSIGN(PipelineVertexDescriptor); }; From 8dae3b0dfc1835271701800d5981353ca0c88eac Mon Sep 17 00:00:00 2001 From: Chinmay Garde Date: Mon, 1 Nov 2021 14:03:14 -0700 Subject: [PATCH 179/433] Use platform agnostic semaphores in the renderer. --- impeller/renderer/BUILD.gn | 2 +- impeller/renderer/{renderer.mm => renderer.cc} | 9 ++++++--- impeller/renderer/renderer.h | 3 ++- 3 files changed, 9 insertions(+), 5 deletions(-) rename impeller/renderer/{renderer.mm => renderer.cc} (89%) diff --git a/impeller/renderer/BUILD.gn b/impeller/renderer/BUILD.gn index fb3a6f7ef2f26..be4fe29aa4006 100644 --- a/impeller/renderer/BUILD.gn +++ b/impeller/renderer/BUILD.gn @@ -75,7 +75,7 @@ impeller_component("renderer") { "render_pass_descriptor.h", "render_pass_descriptor.cc", "renderer.h", - "renderer.mm", + "renderer.cc", "sampler.h", "sampler.cc", "sampler_descriptor.h", diff --git a/impeller/renderer/renderer.mm b/impeller/renderer/renderer.cc similarity index 89% rename from impeller/renderer/renderer.mm rename to impeller/renderer/renderer.cc index 461deb0c4cd90..59c9be341e6d5 100644 --- a/impeller/renderer/renderer.mm +++ b/impeller/renderer/renderer.cc @@ -13,7 +13,8 @@ constexpr size_t kMaxFramesInFlight = 3u; Renderer::Renderer(std::shared_ptr context) - : frames_in_flight_sema_(::dispatch_semaphore_create(kMaxFramesInFlight)), + : frames_in_flight_sema_( + std::make_shared(kMaxFramesInFlight)), context_(std::move(context)) { if (!context_->IsValid()) { return; @@ -58,11 +59,13 @@ return false; } - ::dispatch_semaphore_wait(frames_in_flight_sema_, DISPATCH_TIME_FOREVER); + if (!frames_in_flight_sema_->Wait()) { + return false; + } command_buffer->Commit( [sema = frames_in_flight_sema_](CommandBuffer::CommitResult result) { - ::dispatch_semaphore_signal(sema); + sema->Signal(); if (result != CommandBuffer::CommitResult::kCompleted) { FML_LOG(ERROR) << "Could not commit command buffer."; } diff --git a/impeller/renderer/renderer.h b/impeller/renderer/renderer.h index 529ccb3c78d80..ebd3930aec6cc 100644 --- a/impeller/renderer/renderer.h +++ b/impeller/renderer/renderer.h @@ -10,6 +10,7 @@ #include #include "flutter/fml/macros.h" +#include "fml/synchronization/semaphore.h" #include "impeller/geometry/size.h" #include "impeller/renderer/context.h" @@ -34,7 +35,7 @@ class Renderer { std::shared_ptr GetContext() const; private: - dispatch_semaphore_t frames_in_flight_sema_ = nullptr; + std::shared_ptr frames_in_flight_sema_; std::shared_ptr context_; bool is_valid_ = false; From c440cdc3e9c88233f9be00d88cbabce87db998a6 Mon Sep 17 00:00:00 2001 From: Chinmay Garde Date: Mon, 1 Nov 2021 14:07:40 -0700 Subject: [PATCH 180/433] Rename the pipeline vertex descriptor. --- .../backend/metal/vertex_descriptor_mtl.mm | 2 +- impeller/renderer/command.cc | 3 +-- impeller/renderer/pipeline_builder.h | 4 ++-- impeller/renderer/pipeline_descriptor.cc | 2 +- impeller/renderer/pipeline_descriptor.h | 8 ++++---- impeller/renderer/vertex_descriptor.cc | 14 ++++++-------- impeller/renderer/vertex_descriptor.h | 11 +++++------ 7 files changed, 20 insertions(+), 24 deletions(-) diff --git a/impeller/renderer/backend/metal/vertex_descriptor_mtl.mm b/impeller/renderer/backend/metal/vertex_descriptor_mtl.mm index 244bd07b0a756..02b45f9c3d6b1 100644 --- a/impeller/renderer/backend/metal/vertex_descriptor_mtl.mm +++ b/impeller/renderer/backend/metal/vertex_descriptor_mtl.mm @@ -190,7 +190,7 @@ static MTLVertexFormat ReadStageInputFormat(const ShaderStageIOSlot& input) { auto descriptor = [MTLVertexDescriptor vertexDescriptor]; const size_t vertex_buffer_index = - PipelineVertexDescriptor::kReservedVertexBufferIndex; + VertexDescriptor::kReservedVertexBufferIndex; size_t stride = 0u; for (const auto& input : stage_inputs_) { diff --git a/impeller/renderer/command.cc b/impeller/renderer/command.cc index bc18314d7c98a..ff4febc421f78 100644 --- a/impeller/renderer/command.cc +++ b/impeller/renderer/command.cc @@ -9,8 +9,7 @@ namespace impeller { bool Command::BindVertices(const VertexBuffer& buffer) { - vertex_bindings - .buffers[PipelineVertexDescriptor::kReservedVertexBufferIndex] = + vertex_bindings.buffers[VertexDescriptor::kReservedVertexBufferIndex] = buffer.vertex_buffer; index_buffer = buffer.index_buffer; index_count = buffer.index_count; diff --git a/impeller/renderer/pipeline_builder.h b/impeller/renderer/pipeline_builder.h index f67e76017c1a6..2f9f8bc1ecc34 100644 --- a/impeller/renderer/pipeline_builder.h +++ b/impeller/renderer/pipeline_builder.h @@ -32,7 +32,7 @@ struct PipelineBuilder { using FragmentShader = FragmentShader_; static constexpr size_t kVertexBufferIndex = - PipelineVertexDescriptor::kReservedVertexBufferIndex; + VertexDescriptor::kReservedVertexBufferIndex; //---------------------------------------------------------------------------- /// @brief Create a default pipeline descriptor using the combination @@ -70,7 +70,7 @@ struct PipelineBuilder { // Setup the vertex descriptor from reflected information. { - auto vertex_descriptor = std::make_shared(); + auto vertex_descriptor = std::make_shared(); if (!vertex_descriptor->SetStageInputs( VertexShader::kAllShaderStageInputs)) { FML_LOG(ERROR) << "Could not configure vertex descriptor."; diff --git a/impeller/renderer/pipeline_descriptor.cc b/impeller/renderer/pipeline_descriptor.cc index d7ebb742b7c83..828fee19ac64c 100644 --- a/impeller/renderer/pipeline_descriptor.cc +++ b/impeller/renderer/pipeline_descriptor.cc @@ -82,7 +82,7 @@ PipelineDescriptor& PipelineDescriptor::AddStageEntrypoint( } PipelineDescriptor& PipelineDescriptor::SetVertexDescriptor( - std::shared_ptr vertex_descriptor) { + std::shared_ptr vertex_descriptor) { vertex_descriptor_ = std::move(vertex_descriptor); return *this; } diff --git a/impeller/renderer/pipeline_descriptor.h b/impeller/renderer/pipeline_descriptor.h index 678bbe23c903b..17c80ad46bebc 100644 --- a/impeller/renderer/pipeline_descriptor.h +++ b/impeller/renderer/pipeline_descriptor.h @@ -20,7 +20,7 @@ namespace impeller { class ShaderFunction; -class PipelineVertexDescriptor; +class VertexDescriptor; class PipelineDescriptor final : public Comparable { public: @@ -45,9 +45,9 @@ class PipelineDescriptor final : public Comparable { } PipelineDescriptor& SetVertexDescriptor( - std::shared_ptr vertex_descriptor); + std::shared_ptr vertex_descriptor); - const std::shared_ptr& GetVertexDescriptor() const { + const std::shared_ptr& GetVertexDescriptor() const { return vertex_descriptor_; } @@ -108,7 +108,7 @@ class PipelineDescriptor final : public Comparable { std::map> entrypoints_; std::map color_attachment_descriptors_; - std::shared_ptr vertex_descriptor_; + std::shared_ptr vertex_descriptor_; PixelFormat depth_pixel_format_ = PixelFormat::kUnknown; PixelFormat stencil_pixel_format_ = PixelFormat::kUnknown; std::optional depth_attachment_descriptor_; diff --git a/impeller/renderer/vertex_descriptor.cc b/impeller/renderer/vertex_descriptor.cc index d919da06a5d48..a40efb31c5eb3 100644 --- a/impeller/renderer/vertex_descriptor.cc +++ b/impeller/renderer/vertex_descriptor.cc @@ -6,11 +6,11 @@ namespace impeller { -PipelineVertexDescriptor::PipelineVertexDescriptor() = default; +VertexDescriptor::VertexDescriptor() = default; -PipelineVertexDescriptor::~PipelineVertexDescriptor() = default; +VertexDescriptor::~VertexDescriptor() = default; -bool PipelineVertexDescriptor::SetStageInputs( +bool VertexDescriptor::SetStageInputs( const ShaderStageIOSlot* const stage_inputs[], size_t count) { inputs_.reserve(inputs_.size() + count); @@ -21,7 +21,7 @@ bool PipelineVertexDescriptor::SetStageInputs( } // |Comparable| -size_t PipelineVertexDescriptor::GetHash() const { +size_t VertexDescriptor::GetHash() const { auto seed = fml::HashCombine(); for (const auto& input : inputs_) { fml::HashCombineSeed(seed, input.GetHash()); @@ -30,13 +30,11 @@ size_t PipelineVertexDescriptor::GetHash() const { } // |Comparable| -bool PipelineVertexDescriptor::IsEqual( - const PipelineVertexDescriptor& other) const { +bool VertexDescriptor::IsEqual(const VertexDescriptor& other) const { return inputs_ == other.inputs_; } -const std::vector& PipelineVertexDescriptor::GetStageInputs() - const { +const std::vector& VertexDescriptor::GetStageInputs() const { return inputs_; } diff --git a/impeller/renderer/vertex_descriptor.h b/impeller/renderer/vertex_descriptor.h index 114abcd51c59e..f0018c4566b97 100644 --- a/impeller/renderer/vertex_descriptor.h +++ b/impeller/renderer/vertex_descriptor.h @@ -20,16 +20,15 @@ namespace impeller { /// `impellerc`. The usage of this class is indirectly via /// `PipelineBuilder`. /// -class PipelineVertexDescriptor final - : public Comparable { +class VertexDescriptor final : public Comparable { public: static constexpr size_t kReservedVertexBufferIndex = 30u; // The final slot available. Regular buffer indices go up from 0. - PipelineVertexDescriptor(); + VertexDescriptor(); // |Comparable| - virtual ~PipelineVertexDescriptor(); + virtual ~VertexDescriptor(); template bool SetStageInputs( @@ -46,12 +45,12 @@ class PipelineVertexDescriptor final std::size_t GetHash() const override; // |Comparable| - bool IsEqual(const PipelineVertexDescriptor& other) const override; + bool IsEqual(const VertexDescriptor& other) const override; private: std::vector inputs_; - FML_DISALLOW_COPY_AND_ASSIGN(PipelineVertexDescriptor); + FML_DISALLOW_COPY_AND_ASSIGN(VertexDescriptor); }; } // namespace impeller From cd76bc22f8f19310c2de8e818bf019eb6d7b21c3 Mon Sep 17 00:00:00 2001 From: Chinmay Garde Date: Tue, 2 Nov 2021 15:45:04 -0700 Subject: [PATCH 181/433] Minor doc updates. --- impeller/renderer/command_buffer.h | 5 +++++ impeller/renderer/render_pass.h | 11 +++++++++-- impeller/renderer/renderer_unittests.cc | 14 +++++++------- 3 files changed, 21 insertions(+), 9 deletions(-) diff --git a/impeller/renderer/command_buffer.h b/impeller/renderer/command_buffer.h index 2d7f6be2601b8..10b9a6b189447 100644 --- a/impeller/renderer/command_buffer.h +++ b/impeller/renderer/command_buffer.h @@ -15,6 +15,11 @@ class Context; class RenderPass; class RenderPassDescriptor; +//------------------------------------------------------------------------------ +/// @brief A collection of encoded commands to be submitted to the GPU for +/// execution. A command buffer is obtained from a graphics +/// `Context`. +/// class CommandBuffer { public: enum class CommitResult { diff --git a/impeller/renderer/render_pass.h b/impeller/renderer/render_pass.h index c61f16cc2ed57..e64ff8e24947d 100644 --- a/impeller/renderer/render_pass.h +++ b/impeller/renderer/render_pass.h @@ -23,12 +23,19 @@ class RenderPass { virtual HostBuffer& GetTransientsBuffer() = 0; + //---------------------------------------------------------------------------- + /// @brief Record a command for subsequent commit to the underlying + /// command buffer. No work is encoded into the command buffer at + /// this time. + /// + /// @param[in] command The command + /// + /// @return If the command was valid for subsequent commitment. + /// [[nodiscard]] virtual bool RecordCommand(Command command) = 0; //---------------------------------------------------------------------------- /// @brief Commit the recorded commands to the underlying command buffer. - /// Any completion handlers must on the underlying command buffer - /// must have already been added by this point. /// /// @param transients_allocator The transients allocator. /// diff --git a/impeller/renderer/renderer_unittests.cc b/impeller/renderer/renderer_unittests.cc index 0d5117f68ca9c..cdfb75b6f9007 100644 --- a/impeller/renderer/renderer_unittests.cc +++ b/impeller/renderer/renderer_unittests.cc @@ -25,9 +25,9 @@ namespace impeller { namespace testing { -using CompositorTest = Playground; +using RendererTest = Playground; -TEST_F(CompositorTest, CanCreateBoxPrimitive) { +TEST_F(RendererTest, CanCreateBoxPrimitive) { using VS = BoxFadeVertexShader; using FS = BoxFadeFragmentShader; auto context = GetContext(); @@ -91,7 +91,7 @@ TEST_F(CompositorTest, CanCreateBoxPrimitive) { // OpenPlaygroundHere(callback); } -TEST_F(CompositorTest, CanRenderMultiplePrimitives) { +TEST_F(RendererTest, CanRenderMultiplePrimitives) { using VS = BoxFadeVertexShader; using FS = BoxFadeFragmentShader; auto context = GetContext(); @@ -144,8 +144,8 @@ TEST_F(CompositorTest, CanRenderMultiplePrimitives) { cmd.primitive_type = PrimitiveType::kTriangle; - for (size_t i = 0; i < 50; i++) { - for (size_t j = 0; j < 50; j++) { + for (size_t i = 0; i < 1; i++) { + for (size_t j = 0; j < 1; j++) { VS::UniformBuffer uniforms; uniforms.mvp = Matrix::MakeOrthographic(surface.GetSize()) * Matrix::MakeTranslation({i * 50.0f, j * 50.0f, 0.0f}); @@ -162,7 +162,7 @@ TEST_F(CompositorTest, CanRenderMultiplePrimitives) { // OpenPlaygroundHere(callback); } -TEST_F(CompositorTest, CanRenderToTexture) { +TEST_F(RendererTest, CanRenderToTexture) { using VS = BoxFadeVertexShader; using FS = BoxFadeFragmentShader; auto context = GetContext(); @@ -253,7 +253,7 @@ TEST_F(CompositorTest, CanRenderToTexture) { ASSERT_TRUE(r2t_pass->Commit(*context->GetTransientsAllocator())); } -TEST_F(CompositorTest, CanRenderPath) { +TEST_F(RendererTest, CanRenderPath) { auto path = PathBuilder{}.AddCircle({550, 550}, 500).CreatePath(); ASSERT_FALSE(path.GetBoundingBox().IsZero()); From f814243138f343b89affeb19fa15c9ad11cc6971 Mon Sep 17 00:00:00 2001 From: Chinmay Garde Date: Tue, 2 Nov 2021 16:29:46 -0700 Subject: [PATCH 182/433] Add docs for command buffer. --- .../backend/metal/command_buffer_mtl.h | 2 +- .../backend/metal/command_buffer_mtl.mm | 13 ++++----- impeller/renderer/command_buffer.h | 28 +++++++++++++++++-- impeller/renderer/renderer.cc | 4 +-- 4 files changed, 34 insertions(+), 13 deletions(-) diff --git a/impeller/renderer/backend/metal/command_buffer_mtl.h b/impeller/renderer/backend/metal/command_buffer_mtl.h index bd816dd76ed52..946157789c39b 100644 --- a/impeller/renderer/backend/metal/command_buffer_mtl.h +++ b/impeller/renderer/backend/metal/command_buffer_mtl.h @@ -30,7 +30,7 @@ class CommandBufferMTL final : public CommandBuffer { bool IsValid() const override; // |CommandBuffer| - void Commit(CommitCallback callback) override; + void Commit(CompletionCallback callback) override; // |CommandBuffer| std::shared_ptr CreateRenderPass( diff --git a/impeller/renderer/backend/metal/command_buffer_mtl.mm b/impeller/renderer/backend/metal/command_buffer_mtl.mm index bd9f56e9e8c80..8e97418396bee 100644 --- a/impeller/renderer/backend/metal/command_buffer_mtl.mm +++ b/impeller/renderer/backend/metal/command_buffer_mtl.mm @@ -22,27 +22,26 @@ return is_valid_; } -static CommandBuffer::CommitResult ToCommitResult( - MTLCommandBufferStatus status) { +static CommandBuffer::Status ToCommitResult(MTLCommandBufferStatus status) { switch (status) { case MTLCommandBufferStatusCompleted: - return CommandBufferMTL::CommitResult::kCompleted; + return CommandBufferMTL::Status::kCompleted; case MTLCommandBufferStatusEnqueued: - return CommandBufferMTL::CommitResult::kPending; + return CommandBufferMTL::Status::kPending; default: break; } - return CommandBufferMTL::CommitResult::kError; + return CommandBufferMTL::Status::kError; } -void CommandBufferMTL::Commit(CommitCallback callback) { +void CommandBufferMTL::Commit(CompletionCallback callback) { if (!callback) { callback = [](auto) {}; } if (!buffer_) { // Already committed. This is caller error. - callback(CommitResult::kError); + callback(Status::kError); return; } diff --git a/impeller/renderer/command_buffer.h b/impeller/renderer/command_buffer.h index 10b9a6b189447..8830e62ae8874 100644 --- a/impeller/renderer/command_buffer.h +++ b/impeller/renderer/command_buffer.h @@ -20,22 +20,44 @@ class RenderPassDescriptor; /// execution. A command buffer is obtained from a graphics /// `Context`. /// +/// To submit commands to the GPU, acquire a `RenderPass` from the +/// command buffer and record `Command`s into that pass. A +/// `RenderPass` describes the configuration of the various +/// attachments when the command is submitted. +/// +/// A command buffer is only meant to be used on a single thread. +/// class CommandBuffer { public: - enum class CommitResult { + enum class Status { kPending, kError, kCompleted, }; - using CommitCallback = std::function; + using CompletionCallback = std::function; virtual ~CommandBuffer(); virtual bool IsValid() const = 0; - virtual void Commit(CommitCallback callback) = 0; + //---------------------------------------------------------------------------- + /// @brief Schedule the command encoded by render passes within this + /// command buffer on the GPU. + /// + /// A command buffer may only be committed once. + /// + /// @param[in] callback The completion callback. + /// + virtual void Commit(CompletionCallback callback) = 0; + //---------------------------------------------------------------------------- + /// @brief Create a render pass to record render commands into. + /// + /// @param[in] desc The description of the render pass. + /// + /// @return A valid render pass or null. + /// virtual std::shared_ptr CreateRenderPass( const RenderPassDescriptor& desc) const = 0; diff --git a/impeller/renderer/renderer.cc b/impeller/renderer/renderer.cc index 59c9be341e6d5..f8ee49a401175 100644 --- a/impeller/renderer/renderer.cc +++ b/impeller/renderer/renderer.cc @@ -64,9 +64,9 @@ bool Renderer::Render(const Surface& surface, } command_buffer->Commit( - [sema = frames_in_flight_sema_](CommandBuffer::CommitResult result) { + [sema = frames_in_flight_sema_](CommandBuffer::Status result) { sema->Signal(); - if (result != CommandBuffer::CommitResult::kCompleted) { + if (result != CommandBuffer::Status::kCompleted) { FML_LOG(ERROR) << "Could not commit command buffer."; } }); From d5cc1e0917d4a11b56350b4fb662c81ec29b6c36 Mon Sep 17 00:00:00 2001 From: Chinmay Garde Date: Wed, 3 Nov 2021 11:16:58 -0700 Subject: [PATCH 183/433] Make the renderer APIs more verbose. --- impeller/entity/contents.mm | 2 +- impeller/entity/entity_renderer_impl.mm | 2 +- impeller/renderer/backend/metal/command_buffer_mtl.h | 2 +- impeller/renderer/backend/metal/command_buffer_mtl.mm | 2 +- impeller/renderer/backend/metal/render_pass_mtl.h | 4 ++-- impeller/renderer/backend/metal/render_pass_mtl.mm | 4 ++-- impeller/renderer/command_buffer.h | 2 +- impeller/renderer/render_pass.h | 10 +++++----- impeller/renderer/renderer.cc | 4 ++-- impeller/renderer/renderer_unittests.cc | 10 +++++----- 10 files changed, 21 insertions(+), 21 deletions(-) diff --git a/impeller/entity/contents.mm b/impeller/entity/contents.mm index de6faaef4c119..a7879e13862c5 100644 --- a/impeller/entity/contents.mm +++ b/impeller/entity/contents.mm @@ -82,7 +82,7 @@ FS::BindGradientInfo( cmd, pass.GetTransientsBuffer().EmplaceUniform(gradient_info)); VS::BindFrameInfo(cmd, pass.GetTransientsBuffer().EmplaceUniform(frame_info)); - return pass.RecordCommand(std::move(cmd)); + return pass.AddCommand(std::move(cmd)); } } // namespace impeller diff --git a/impeller/entity/entity_renderer_impl.mm b/impeller/entity/entity_renderer_impl.mm index 4a46a5f4d4b10..2f0c1e831ff4a 100644 --- a/impeller/entity/entity_renderer_impl.mm +++ b/impeller/entity/entity_renderer_impl.mm @@ -75,7 +75,7 @@ cmd.primitive_type = PrimitiveType::kTriangle; - if (!pass.RecordCommand(std::move(cmd))) { + if (!pass.AddCommand(std::move(cmd))) { return RenderResult::kFailure; } } else if (entity.GetContents()) { diff --git a/impeller/renderer/backend/metal/command_buffer_mtl.h b/impeller/renderer/backend/metal/command_buffer_mtl.h index 946157789c39b..b5551f09f3fb5 100644 --- a/impeller/renderer/backend/metal/command_buffer_mtl.h +++ b/impeller/renderer/backend/metal/command_buffer_mtl.h @@ -30,7 +30,7 @@ class CommandBufferMTL final : public CommandBuffer { bool IsValid() const override; // |CommandBuffer| - void Commit(CompletionCallback callback) override; + void SubmitCommands(CompletionCallback callback) override; // |CommandBuffer| std::shared_ptr CreateRenderPass( diff --git a/impeller/renderer/backend/metal/command_buffer_mtl.mm b/impeller/renderer/backend/metal/command_buffer_mtl.mm index 8e97418396bee..b6c12464aca38 100644 --- a/impeller/renderer/backend/metal/command_buffer_mtl.mm +++ b/impeller/renderer/backend/metal/command_buffer_mtl.mm @@ -34,7 +34,7 @@ return CommandBufferMTL::Status::kError; } -void CommandBufferMTL::Commit(CompletionCallback callback) { +void CommandBufferMTL::SubmitCommands(CompletionCallback callback) { if (!callback) { callback = [](auto) {}; } diff --git a/impeller/renderer/backend/metal/render_pass_mtl.h b/impeller/renderer/backend/metal/render_pass_mtl.h index 0726dca9620ff..40b92e7553701 100644 --- a/impeller/renderer/backend/metal/render_pass_mtl.h +++ b/impeller/renderer/backend/metal/render_pass_mtl.h @@ -39,10 +39,10 @@ class RenderPassMTL final : public RenderPass { HostBuffer& GetTransientsBuffer() override; // |RenderPass| - bool RecordCommand(Command command) override; + bool AddCommand(Command command) override; // |RenderPass| - bool Commit(Allocator& transients_allocator) const override; + bool EncodeCommands(Allocator& transients_allocator) const override; bool EncodeCommands(Allocator& transients_allocator, id pass) const; diff --git a/impeller/renderer/backend/metal/render_pass_mtl.mm b/impeller/renderer/backend/metal/render_pass_mtl.mm index f2549a4d0d037..fe21cf84e50db 100644 --- a/impeller/renderer/backend/metal/render_pass_mtl.mm +++ b/impeller/renderer/backend/metal/render_pass_mtl.mm @@ -118,7 +118,7 @@ static bool ConfigureStencilAttachment( transients_buffer_->SetLabel(SPrintF("%s Transients", label_.c_str())); } -bool RenderPassMTL::Commit(Allocator& transients_allocator) const { +bool RenderPassMTL::EncodeCommands(Allocator& transients_allocator) const { if (!IsValid()) { return false; } @@ -406,7 +406,7 @@ static bool Bind(PassBindingsCache& pass, return true; } -bool RenderPassMTL::RecordCommand(Command command) { +bool RenderPassMTL::AddCommand(Command command) { if (!command) { return false; } diff --git a/impeller/renderer/command_buffer.h b/impeller/renderer/command_buffer.h index 8830e62ae8874..5108453b35112 100644 --- a/impeller/renderer/command_buffer.h +++ b/impeller/renderer/command_buffer.h @@ -49,7 +49,7 @@ class CommandBuffer { /// /// @param[in] callback The completion callback. /// - virtual void Commit(CompletionCallback callback) = 0; + virtual void SubmitCommands(CompletionCallback callback) = 0; //---------------------------------------------------------------------------- /// @brief Create a render pass to record render commands into. diff --git a/impeller/renderer/render_pass.h b/impeller/renderer/render_pass.h index e64ff8e24947d..42e242d7a035a 100644 --- a/impeller/renderer/render_pass.h +++ b/impeller/renderer/render_pass.h @@ -24,7 +24,7 @@ class RenderPass { virtual HostBuffer& GetTransientsBuffer() = 0; //---------------------------------------------------------------------------- - /// @brief Record a command for subsequent commit to the underlying + /// @brief Record a command for subsequent encoding to the underlying /// command buffer. No work is encoded into the command buffer at /// this time. /// @@ -32,17 +32,17 @@ class RenderPass { /// /// @return If the command was valid for subsequent commitment. /// - [[nodiscard]] virtual bool RecordCommand(Command command) = 0; + virtual bool AddCommand(Command command) = 0; //---------------------------------------------------------------------------- - /// @brief Commit the recorded commands to the underlying command buffer. + /// @brief Encode the recorded commands to the underlying command buffer. /// /// @param transients_allocator The transients allocator. /// - /// @return If the commands were committed to the underlying command + /// @return If the commands were encoded to the underlying command /// buffer. /// - [[nodiscard]] virtual bool Commit(Allocator& transients_allocator) const = 0; + virtual bool EncodeCommands(Allocator& transients_allocator) const = 0; protected: RenderPass(); diff --git a/impeller/renderer/renderer.cc b/impeller/renderer/renderer.cc index f8ee49a401175..bc3e3e4657b15 100644 --- a/impeller/renderer/renderer.cc +++ b/impeller/renderer/renderer.cc @@ -55,7 +55,7 @@ bool Renderer::Render(const Surface& surface, return false; } - if (!render_pass->Commit(*GetContext()->GetTransientsAllocator())) { + if (!render_pass->EncodeCommands(*GetContext()->GetTransientsAllocator())) { return false; } @@ -63,7 +63,7 @@ bool Renderer::Render(const Surface& surface, return false; } - command_buffer->Commit( + command_buffer->SubmitCommands( [sema = frames_in_flight_sema_](CommandBuffer::Status result) { sema->Signal(); if (result != CommandBuffer::Status::kCompleted) { diff --git a/impeller/renderer/renderer_unittests.cc b/impeller/renderer/renderer_unittests.cc index cdfb75b6f9007..148b6f43e4e81 100644 --- a/impeller/renderer/renderer_unittests.cc +++ b/impeller/renderer/renderer_unittests.cc @@ -83,7 +83,7 @@ TEST_F(RendererTest, CanCreateBoxPrimitive) { FS::BindContents2(cmd, bridge, sampler); cmd.primitive_type = PrimitiveType::kTriangle; - if (!pass.RecordCommand(std::move(cmd))) { + if (!pass.AddCommand(std::move(cmd))) { return false; } return true; @@ -151,7 +151,7 @@ TEST_F(RendererTest, CanRenderMultiplePrimitives) { Matrix::MakeTranslation({i * 50.0f, j * 50.0f, 0.0f}); VS::BindUniformBuffer( cmd, pass.GetTransientsBuffer().EmplaceUniform(uniforms)); - if (!pass.RecordCommand(cmd)) { + if (!pass.AddCommand(cmd)) { return false; } } @@ -249,8 +249,8 @@ TEST_F(RendererTest, CanRenderToTexture) { Matrix::MakeTranslation({50.0f, 50.0f, 0.0f}); VS::BindUniformBuffer( cmd, r2t_pass->GetTransientsBuffer().EmplaceUniform(uniforms)); - ASSERT_TRUE(r2t_pass->RecordCommand(std::move(cmd))); - ASSERT_TRUE(r2t_pass->Commit(*context->GetTransientsAllocator())); + ASSERT_TRUE(r2t_pass->AddCommand(std::move(cmd))); + ASSERT_TRUE(r2t_pass->EncodeCommands(*context->GetTransientsAllocator())); } TEST_F(RendererTest, CanRenderPath) { @@ -314,7 +314,7 @@ TEST_F(RendererTest, CanRenderPath) { uniforms.mvp = Matrix::MakeOrthographic(surface.GetSize()); VS::BindUniformBuffer(cmd, pass.GetTransientsBuffer().EmplaceUniform(uniforms)); - if (!pass.RecordCommand(cmd)) { + if (!pass.AddCommand(cmd)) { return false; } From d641bf04bf261d993500e872df51edd776f822e6 Mon Sep 17 00:00:00 2001 From: Chinmay Garde Date: Wed, 3 Nov 2021 13:55:20 -0700 Subject: [PATCH 184/433] More doc updates. --- impeller/entity/contents.mm | 13 +++++------ impeller/entity/entity_renderer_impl.mm | 2 +- impeller/fixtures/BUILD.gn | 2 ++ impeller/geometry/path.cc | 2 +- impeller/geometry/path.h | 2 +- impeller/renderer/command.h | 31 +++++++++++++++++++++++++ impeller/renderer/pipeline.h | 14 +++++++++++ impeller/renderer/renderer_unittests.cc | 2 +- impeller/renderer/tessellator.h | 19 +++++++++++++-- 9 files changed, 74 insertions(+), 13 deletions(-) diff --git a/impeller/entity/contents.mm b/impeller/entity/contents.mm index a7879e13862c5..41f468aa441b8 100644 --- a/impeller/entity/contents.mm +++ b/impeller/entity/contents.mm @@ -51,13 +51,12 @@ auto vertices_builder = VertexBufferBuilder(); { - auto result = - Tessellator{}.Tessellate(entity.GetPath().SubdivideAdaptively(), - [&vertices_builder](Point point) { - VS::PerVertexData vtx; - vtx.vertices = point; - vertices_builder.AppendVertex(vtx); - }); + auto result = Tessellator{}.Tessellate(entity.GetPath().CreatePolyline(), + [&vertices_builder](Point point) { + VS::PerVertexData vtx; + vtx.vertices = point; + vertices_builder.AppendVertex(vtx); + }); if (!result) { return false; } diff --git a/impeller/entity/entity_renderer_impl.mm b/impeller/entity/entity_renderer_impl.mm index 2f0c1e831ff4a..9ff3410a893e0 100644 --- a/impeller/entity/entity_renderer_impl.mm +++ b/impeller/entity/entity_renderer_impl.mm @@ -53,7 +53,7 @@ VertexBufferBuilder vtx_builder; { auto tesselation_result = Tessellator{}.Tessellate( - entity.GetPath().SubdivideAdaptively(), [&vtx_builder](auto point) { + entity.GetPath().CreatePolyline(), [&vtx_builder](auto point) { VS::PerVertexData vtx; vtx.vertices = point; vtx_builder.AppendVertex(vtx); diff --git a/impeller/fixtures/BUILD.gn b/impeller/fixtures/BUILD.gn index 3bbffb60917aa..9f6dba1e4d81c 100644 --- a/impeller/fixtures/BUILD.gn +++ b/impeller/fixtures/BUILD.gn @@ -12,6 +12,8 @@ impeller_shaders("shader_fixtures") { shaders = [ "box_fade.vert", "box_fade.frag", + "sandbox.vert", + "sandbox.frag", ] } diff --git a/impeller/geometry/path.cc b/impeller/geometry/path.cc index 1d444b5fb6fe6..61a8d4c99d999 100644 --- a/impeller/geometry/path.cc +++ b/impeller/geometry/path.cc @@ -149,7 +149,7 @@ static void AddPoints(std::vector& dest, const std::vector& src) { dest.insert(dest.end(), src.begin(), src.end()); } -std::vector Path::SubdivideAdaptively( +std::vector Path::CreatePolyline( const SmoothingApproximation& approximation) const { std::vector points; for (const auto& component : components_) { diff --git a/impeller/geometry/path.h b/impeller/geometry/path.h index 4952ecab16ded..8ae91753ff49a 100644 --- a/impeller/geometry/path.h +++ b/impeller/geometry/path.h @@ -53,7 +53,7 @@ class Path { bool UpdateCubicComponentAtIndex(size_t index, CubicPathComponent& cubic); - std::vector SubdivideAdaptively( + std::vector CreatePolyline( const SmoothingApproximation& approximation = {}) const; Rect GetBoundingBox() const; diff --git a/impeller/renderer/command.h b/impeller/renderer/command.h index 0b9ed1ffe60ac..a54cfc34d82a3 100644 --- a/impeller/renderer/command.h +++ b/impeller/renderer/command.h @@ -26,10 +26,41 @@ struct Bindings { std::map> samplers; }; +//------------------------------------------------------------------------------ +/// @brief An object used to specify work to the GPU along with references +/// to resources the GPU will used when doing said work. +/// +/// To construct a valid command, follow these steps: +/// * Specify a valid pipeline. +/// * Specify vertex information via a call `BindVertices` +/// * Specify any stage bindings. +/// * (Optional) Specify a debug label. +/// +/// Command are very lightweight objects and can be created +/// frequently and on demand. The resources referenced in commands +/// views into buffers managed by other allocators and resource +/// managers. +/// struct Command { + //---------------------------------------------------------------------------- + /// The pipeline to use for this command. + /// std::shared_ptr pipeline; + //---------------------------------------------------------------------------- + /// The buffer, texture, and sampler bindings used by the vertex pipeline + /// stage. + /// Bindings vertex_bindings; + //---------------------------------------------------------------------------- + /// The buffer, texture, and sampler bindings used by the fragment pipeline + /// stage. + /// Bindings fragment_bindings; + //---------------------------------------------------------------------------- + /// The index buffer binding used by the vertex shader stage. Instead of + /// setting this directly, it usually easier to specify the vertex and index + /// buffer bindings directly via a single call to `BindVertices`. + /// BufferView index_buffer; size_t index_count = 0u; std::string label; diff --git a/impeller/renderer/pipeline.h b/impeller/renderer/pipeline.h index bfb0f649e38c2..8a5e17236685d 100644 --- a/impeller/renderer/pipeline.h +++ b/impeller/renderer/pipeline.h @@ -17,6 +17,20 @@ class Pipeline; using PipelineFuture = std::future>; +//------------------------------------------------------------------------------ +/// @brief Describes the fixed function and programmable aspects of +/// rendering and compute operations performed by commands submitted +/// to the GPU via a command buffer. +/// +/// A pipeline handle must be allocated upfront and kept alive for +/// as long as possible. Do not create a pipeline object within a +/// frame workload. +/// +/// This pipeline object is almost never used directly as it is +/// untyped. Use reflected shader information generated by the +/// Impeller offline shader compiler to generate a typed pipeline +/// object. +/// class Pipeline { public: enum class Type { diff --git a/impeller/renderer/renderer_unittests.cc b/impeller/renderer/renderer_unittests.cc index 148b6f43e4e81..496637f822161 100644 --- a/impeller/renderer/renderer_unittests.cc +++ b/impeller/renderer/renderer_unittests.cc @@ -269,7 +269,7 @@ TEST_F(RendererTest, CanRenderPath) { Tessellator tessellator; ASSERT_TRUE(tessellator.Tessellate( - path.SubdivideAdaptively({}), [&vertex_builder](Point point) { + path.CreatePolyline({}), [&vertex_builder](Point point) { VS::PerVertexData vtx; vtx.vertex_position = {point.x, point.y, 0.0}; vtx.texture_coordinates = {0.5, 0.5}; diff --git a/impeller/renderer/tessellator.h b/impeller/renderer/tessellator.h index fcc04dff68ce6..0ca17608b61f5 100644 --- a/impeller/renderer/tessellator.h +++ b/impeller/renderer/tessellator.h @@ -13,6 +13,12 @@ namespace impeller { +//------------------------------------------------------------------------------ +/// @brief A utility that generates triangles of the specified fill type +/// given a polyline. This happens on the CPU. +/// +/// @bug This should just be called a triangulator. +/// class Tessellator { public: enum class FillType { @@ -34,8 +40,17 @@ class Tessellator { WindingOrder GetFrontFaceWinding() const; using VertexCallback = std::function; - [[nodiscard]] bool Tessellate(const std::vector& vertices, - VertexCallback callback) const; + //---------------------------------------------------------------------------- + /// @brief Generates triangles from the polyline. A callback is invoked + /// for each vertex of the triangle. + /// + /// @param[in] polyline The polyline + /// @param[in] callback The callback + /// + /// @return If tessellation was successful. + /// + bool Tessellate(const std::vector& polyline, + VertexCallback callback) const; private: FillType fill_type_ = FillType::kNonZero; From 25f39f79d2c8dd5632646ab206a0dc8c84516284 Mon Sep 17 00:00:00 2001 From: Chinmay Garde Date: Wed, 3 Nov 2021 14:11:49 -0700 Subject: [PATCH 185/433] Minor build fixups. --- impeller/compiler/code_gen_template.h | 5 +++-- impeller/fixtures/BUILD.gn | 2 -- 2 files changed, 3 insertions(+), 4 deletions(-) diff --git a/impeller/compiler/code_gen_template.h b/impeller/compiler/code_gen_template.h index 8783c5573e5c8..7eef094987810 100644 --- a/impeller/compiler/code_gen_template.h +++ b/impeller/compiler/code_gen_template.h @@ -13,8 +13,9 @@ constexpr std::string_view kReflectionHeaderTemplate = #pragma once -// Note: The nogncheck decorations are only to make GN not mad at the template. -// There are no GN rule violations in the generated file itself. +// Note: The nogncheck decorations are only to make GN not mad at the template +// this file is generated from. There are no GN rule violations in the generated +// file itself. #include "impeller/renderer/buffer_view.h" // nogncheck #include "impeller/renderer/command.h" // nogncheck #include "impeller/renderer/sampler.h" // nogncheck diff --git a/impeller/fixtures/BUILD.gn b/impeller/fixtures/BUILD.gn index 9f6dba1e4d81c..3bbffb60917aa 100644 --- a/impeller/fixtures/BUILD.gn +++ b/impeller/fixtures/BUILD.gn @@ -12,8 +12,6 @@ impeller_shaders("shader_fixtures") { shaders = [ "box_fade.vert", "box_fade.frag", - "sandbox.vert", - "sandbox.frag", ] } From a05ee53f46c5599eeed870e32459e842af465a5b Mon Sep 17 00:00:00 2001 From: Chinmay Garde Date: Wed, 3 Nov 2021 15:33:00 -0700 Subject: [PATCH 186/433] Merge entity renderer impl into the entity renderer. --- impeller/aiks/BUILD.gn | 2 +- ...icture_renderer.mm => picture_renderer.cc} | 0 impeller/entity/BUILD.gn | 10 ++-- ...ontent_renderer.mm => content_renderer.cc} | 0 impeller/entity/content_renderer.h | 4 +- impeller/entity/{contents.mm => contents.cc} | 0 ...ty_renderer_impl.mm => entity_renderer.cc} | 27 +++++++-- impeller/entity/entity_renderer.h | 32 ++++++++--- impeller/entity/entity_renderer.mm | 43 -------------- impeller/entity/entity_renderer_impl.h | 56 ------------------- ...ntity_unittests.mm => entity_unittests.cc} | 0 11 files changed, 54 insertions(+), 120 deletions(-) rename impeller/aiks/{picture_renderer.mm => picture_renderer.cc} (100%) rename impeller/entity/{content_renderer.mm => content_renderer.cc} (100%) rename impeller/entity/{contents.mm => contents.cc} (100%) rename impeller/entity/{entity_renderer_impl.mm => entity_renderer.cc} (77%) delete mode 100644 impeller/entity/entity_renderer.mm delete mode 100644 impeller/entity/entity_renderer_impl.h rename impeller/entity/{entity_unittests.mm => entity_unittests.cc} (100%) diff --git a/impeller/aiks/BUILD.gn b/impeller/aiks/BUILD.gn index 5d3d66f7dfab8..39ad62609f5e7 100644 --- a/impeller/aiks/BUILD.gn +++ b/impeller/aiks/BUILD.gn @@ -16,8 +16,8 @@ impeller_component("aiks") { "picture.h", "picture_recorder.cc", "picture_recorder.h", + "picture_renderer.cc", "picture_renderer.h", - "picture_renderer.mm", ] public_deps = [ diff --git a/impeller/aiks/picture_renderer.mm b/impeller/aiks/picture_renderer.cc similarity index 100% rename from impeller/aiks/picture_renderer.mm rename to impeller/aiks/picture_renderer.cc diff --git a/impeller/entity/BUILD.gn b/impeller/entity/BUILD.gn index e67246e55fd1b..1e7b138eb3eb2 100644 --- a/impeller/entity/BUILD.gn +++ b/impeller/entity/BUILD.gn @@ -17,16 +17,14 @@ impeller_shaders("entity_shaders") { impeller_component("entity") { sources = [ + "content_renderer.cc", "content_renderer.h", - "content_renderer.mm", + "contents.cc", "contents.h", - "contents.mm", "entity.cc", "entity.h", + "entity_renderer.cc", "entity_renderer.h", - "entity_renderer.mm", - "entity_renderer_impl.h", - "entity_renderer_impl.mm", ] deps = [ ":entity_shaders" ] @@ -40,7 +38,7 @@ impeller_component("entity") { impeller_component("entity_unittests") { testonly = true - sources = [ "entity_unittests.mm" ] + sources = [ "entity_unittests.cc" ] deps = [ ":entity", diff --git a/impeller/entity/content_renderer.mm b/impeller/entity/content_renderer.cc similarity index 100% rename from impeller/entity/content_renderer.mm rename to impeller/entity/content_renderer.cc diff --git a/impeller/entity/content_renderer.h b/impeller/entity/content_renderer.h index d803ef45f2243..671504a298099 100644 --- a/impeller/entity/content_renderer.h +++ b/impeller/entity/content_renderer.h @@ -7,8 +7,8 @@ #include #include "flutter/fml/macros.h" -#include "impeller/entity/gradient_fill.frag.h" -#include "impeller/entity/gradient_fill.vert.h" +#include "flutter/impeller/entity/gradient_fill.frag.h" +#include "flutter/impeller/entity/gradient_fill.vert.h" #include "impeller/renderer/pipeline.h" namespace impeller { diff --git a/impeller/entity/contents.mm b/impeller/entity/contents.cc similarity index 100% rename from impeller/entity/contents.mm rename to impeller/entity/contents.cc diff --git a/impeller/entity/entity_renderer_impl.mm b/impeller/entity/entity_renderer.cc similarity index 77% rename from impeller/entity/entity_renderer_impl.mm rename to impeller/entity/entity_renderer.cc index 9ff3410a893e0..a15846b7bba00 100644 --- a/impeller/entity/entity_renderer_impl.mm +++ b/impeller/entity/entity_renderer.cc @@ -2,7 +2,7 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -#include "impeller/entity/entity_renderer_impl.h" +#include "flutter/impeller/entity/entity_renderer.h" #include "flutter/fml/trace_event.h" #include "impeller/renderer/tessellator.h" @@ -10,7 +10,7 @@ namespace impeller { -EntityRendererImpl::EntityRendererImpl(std::shared_ptr context) +EntityRenderer::EntityRenderer(std::shared_ptr context) : context_(std::move(context)) { if (!context_ || !context_->IsValid()) { return; @@ -26,13 +26,30 @@ is_valid_ = true; } -EntityRendererImpl::~EntityRendererImpl() = default; +EntityRenderer::~EntityRenderer() = default; -bool EntityRendererImpl::IsValid() const { +bool EntityRenderer::IsValid() const { return is_valid_; } -EntityRendererImpl::RenderResult EntityRendererImpl::RenderEntity( +bool EntityRenderer::RenderEntities(const Surface& surface, + RenderPass& onscreen_pass, + const std::vector& entities) { + if (!IsValid()) { + return false; + } + + for (const auto& entity : entities) { + if (RenderEntity(surface, onscreen_pass, entity) == + EntityRenderer::RenderResult::kFailure) { + return false; + } + } + + return true; +} + +EntityRenderer::RenderResult EntityRenderer::RenderEntity( const Surface& surface, RenderPass& pass, const Entity& entity) { diff --git a/impeller/entity/entity_renderer.h b/impeller/entity/entity_renderer.h index ed6629b5ec19d..caa670dc9c4fd 100644 --- a/impeller/entity/entity_renderer.h +++ b/impeller/entity/entity_renderer.h @@ -7,15 +7,18 @@ #include #include "flutter/fml/macros.h" +#include "flutter/impeller/entity/solid_fill.frag.h" +#include "flutter/impeller/entity/solid_fill.vert.h" +#include "impeller/entity/content_renderer.h" #include "impeller/entity/entity.h" +#include "impeller/renderer/context.h" +#include "impeller/renderer/pipeline.h" +#include "impeller/renderer/pipeline_builder.h" +#include "impeller/renderer/render_pass.h" +#include "impeller/renderer/surface.h" namespace impeller { -class Surface; -class RenderPass; -class Context; -class EntityRendererImpl; - class EntityRenderer { public: EntityRenderer(std::shared_ptr context); @@ -26,10 +29,25 @@ class EntityRenderer { [[nodiscard]] bool RenderEntities(const Surface& surface, RenderPass& onscreen_pass, - const std::vector& entities) const; + const std::vector& entities); + + enum class RenderResult { + kSkipped, + kSuccess, + kFailure, + }; + + [[nodiscard]] RenderResult RenderEntity(const Surface& surface, + RenderPass& onscreen_pass, + const Entity& entities); private: - std::unique_ptr renderer_; + using SolidFillPipeline = + PipelineT; + + std::shared_ptr context_; + std::unique_ptr solid_fill_pipeline_; + std::unique_ptr content_renderer_; bool is_valid_ = false; FML_DISALLOW_COPY_AND_ASSIGN(EntityRenderer); diff --git a/impeller/entity/entity_renderer.mm b/impeller/entity/entity_renderer.mm deleted file mode 100644 index a352ce4f4efb3..0000000000000 --- a/impeller/entity/entity_renderer.mm +++ /dev/null @@ -1,43 +0,0 @@ -// Copyright 2013 The Flutter Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#include "flutter/impeller/entity/entity_renderer.h" - -#include "flutter/fml/trace_event.h" -#include "flutter/impeller/entity/entity_renderer_impl.h" - -namespace impeller { - -EntityRenderer::EntityRenderer(std::shared_ptr context) - : renderer_(std::make_unique(std::move(context))) { - if (!renderer_->IsValid()) { - return; - } - is_valid_ = true; -} - -EntityRenderer::~EntityRenderer() = default; - -bool EntityRenderer::IsValid() const { - return is_valid_; -} - -bool EntityRenderer::RenderEntities(const Surface& surface, - RenderPass& onscreen_pass, - const std::vector& entities) const { - if (!IsValid()) { - return false; - } - - for (const auto& entity : entities) { - if (renderer_->RenderEntity(surface, onscreen_pass, entity) == - EntityRendererImpl::RenderResult::kFailure) { - return false; - } - } - - return true; -} - -} // namespace impeller diff --git a/impeller/entity/entity_renderer_impl.h b/impeller/entity/entity_renderer_impl.h deleted file mode 100644 index dcea72aded521..0000000000000 --- a/impeller/entity/entity_renderer_impl.h +++ /dev/null @@ -1,56 +0,0 @@ -// Copyright 2013 The Flutter Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#pragma once - -#include - -#include "flutter/fml/macros.h" -#include "impeller/entity/content_renderer.h" -#include "impeller/entity/entity.h" -#include "impeller/entity/solid_fill.frag.h" -#include "impeller/entity/solid_fill.vert.h" -#include "impeller/renderer/context.h" -#include "impeller/renderer/pipeline.h" -#include "impeller/renderer/pipeline_builder.h" -#include "impeller/renderer/render_pass.h" -#include "impeller/renderer/surface.h" - -namespace impeller { - -// TODO(csg): Only present to hide Objective-C++ interface in the headers. Once -// the backend is separated into its own TU, this can be merged with -// EntityRenderer. -class EntityRendererImpl { - public: - EntityRendererImpl(std::shared_ptr context); - - ~EntityRendererImpl(); - - bool IsValid() const; - - enum class RenderResult { - kSkipped, - kSuccess, - kFailure, - }; - - [[nodiscard]] RenderResult RenderEntity(const Surface& surface, - RenderPass& onscreen_pass, - const Entity& entities); - - private: - using SolidFillPipeline = - PipelineT; - - std::shared_ptr context_; - std::unique_ptr solid_fill_pipeline_; - std::unique_ptr content_renderer_; - - bool is_valid_ = false; - - FML_DISALLOW_COPY_AND_ASSIGN(EntityRendererImpl); -}; - -} // namespace impeller diff --git a/impeller/entity/entity_unittests.mm b/impeller/entity/entity_unittests.cc similarity index 100% rename from impeller/entity/entity_unittests.mm rename to impeller/entity/entity_unittests.cc From 1bc2d905f3aa260d36db68b2185ad803740e2d1f Mon Sep 17 00:00:00 2001 From: Chinmay Garde Date: Wed, 3 Nov 2021 15:49:54 -0700 Subject: [PATCH 187/433] Move entity shaders to their own directory. --- impeller/entity/BUILD.gn | 8 +++--- .../entity/{ => shaders}/gradient_fill.frag | 0 .../entity/{ => shaders}/gradient_fill.vert | 0 impeller/entity/{ => shaders}/solid_fill.frag | 0 impeller/entity/{ => shaders}/solid_fill.vert | 0 impeller/geometry/path.cc | 25 ++++++++++--------- impeller/geometry/path.h | 12 ++++++++- impeller/geometry/path_component.cc | 8 +++--- impeller/geometry/path_component.h | 6 ++--- 9 files changed, 35 insertions(+), 24 deletions(-) rename impeller/entity/{ => shaders}/gradient_fill.frag (100%) rename impeller/entity/{ => shaders}/gradient_fill.vert (100%) rename impeller/entity/{ => shaders}/solid_fill.frag (100%) rename impeller/entity/{ => shaders}/solid_fill.vert (100%) diff --git a/impeller/entity/BUILD.gn b/impeller/entity/BUILD.gn index 1e7b138eb3eb2..e4d368cc1ab97 100644 --- a/impeller/entity/BUILD.gn +++ b/impeller/entity/BUILD.gn @@ -8,10 +8,10 @@ impeller_shaders("entity_shaders") { name = "entity" shaders = [ - "gradient_fill.frag", - "gradient_fill.vert", - "solid_fill.frag", - "solid_fill.vert", + "shaders/gradient_fill.frag", + "shaders/gradient_fill.vert", + "shaders/solid_fill.frag", + "shaders/solid_fill.vert", ] } diff --git a/impeller/entity/gradient_fill.frag b/impeller/entity/shaders/gradient_fill.frag similarity index 100% rename from impeller/entity/gradient_fill.frag rename to impeller/entity/shaders/gradient_fill.frag diff --git a/impeller/entity/gradient_fill.vert b/impeller/entity/shaders/gradient_fill.vert similarity index 100% rename from impeller/entity/gradient_fill.vert rename to impeller/entity/shaders/gradient_fill.vert diff --git a/impeller/entity/solid_fill.frag b/impeller/entity/shaders/solid_fill.frag similarity index 100% rename from impeller/entity/solid_fill.frag rename to impeller/entity/shaders/solid_fill.frag diff --git a/impeller/entity/solid_fill.vert b/impeller/entity/shaders/solid_fill.vert similarity index 100% rename from impeller/entity/solid_fill.vert rename to impeller/entity/shaders/solid_fill.vert diff --git a/impeller/geometry/path.cc b/impeller/geometry/path.cc index 61a8d4c99d999..2eb79b1a0b656 100644 --- a/impeller/geometry/path.cc +++ b/impeller/geometry/path.cc @@ -32,25 +32,26 @@ Path& Path::AddCubicComponent(Point p1, Point cp1, Point cp2, Point p2) { return *this; } -void Path::EnumerateComponents(Applier linearApplier, - Applier quadApplier, - Applier cubicApplier) const { +void Path::EnumerateComponents( + Applier linear_applier, + Applier quad_applier, + Applier cubic_applier) const { size_t currentIndex = 0; for (const auto& component : components_) { switch (component.type) { case ComponentType::kLinear: - if (linearApplier) { - linearApplier(currentIndex, linears_[component.index]); + if (linear_applier) { + linear_applier(currentIndex, linears_[component.index]); } break; case ComponentType::kQuadratic: - if (quadApplier) { - quadApplier(currentIndex, quads_[component.index]); + if (quad_applier) { + quad_applier(currentIndex, quads_[component.index]); } break; case ComponentType::kCubic: - if (cubicApplier) { - cubicApplier(currentIndex, cubics_[component.index]); + if (cubic_applier) { + cubic_applier(currentIndex, cubics_[component.index]); } break; } @@ -155,15 +156,15 @@ std::vector Path::CreatePolyline( for (const auto& component : components_) { switch (component.type) { case ComponentType::kLinear: - AddPoints(points, linears_[component.index].SubdivideAdaptively()); + AddPoints(points, linears_[component.index].CreatePolyline()); break; case ComponentType::kQuadratic: AddPoints(points, - quads_[component.index].SubdivideAdaptively(approximation)); + quads_[component.index].CreatePolyline(approximation)); break; case ComponentType::kCubic: AddPoints(points, - cubics_[component.index].SubdivideAdaptively(approximation)); + cubics_[component.index].CreatePolyline(approximation)); break; } } diff --git a/impeller/geometry/path.h b/impeller/geometry/path.h index 8ae91753ff49a..1eda541179294 100644 --- a/impeller/geometry/path.h +++ b/impeller/geometry/path.h @@ -7,10 +7,20 @@ #include #include -#include "path_component.h" +#include "impeller/geometry/path_component.h" namespace impeller { +//------------------------------------------------------------------------------ +/// @brief Paths are lightweight objects that describe a collection of +/// linear, quadratic, or cubic segments. +/// +/// All shapes supported by Impeller are paths either directly or +/// via approximation (in the case of circles). +/// +/// Creating paths that describe complex shapes is usually done by a +/// path builder. +/// class Path { public: enum class ComponentType { diff --git a/impeller/geometry/path_component.cc b/impeller/geometry/path_component.cc index 02c72428036ec..a4b4737d88667 100644 --- a/impeller/geometry/path_component.cc +++ b/impeller/geometry/path_component.cc @@ -63,7 +63,7 @@ Point LinearPathComponent::Solve(Scalar time) const { }; } -std::vector LinearPathComponent::SubdivideAdaptively() const { +std::vector LinearPathComponent::CreatePolyline() const { return {p1, p2}; } @@ -85,10 +85,10 @@ Point QuadraticPathComponent::SolveDerivative(Scalar time) const { }; } -std::vector QuadraticPathComponent::SubdivideAdaptively( +std::vector QuadraticPathComponent::CreatePolyline( const SmoothingApproximation& approximation) const { CubicPathComponent elevated(*this); - return elevated.SubdivideAdaptively(approximation); + return elevated.CreatePolyline(approximation); } std::vector QuadraticPathComponent::Extrema() const { @@ -336,7 +336,7 @@ static void CubicPathSmoothenRecursive(const SmoothingApproximation& approx, CubicPathSmoothenRecursive(approx, points, p1234, p234, p34, p4, level + 1); } -std::vector CubicPathComponent::SubdivideAdaptively( +std::vector CubicPathComponent::CreatePolyline( const SmoothingApproximation& approximation) const { std::vector points; points.emplace_back(p1); diff --git a/impeller/geometry/path_component.h b/impeller/geometry/path_component.h index cda5258eaa192..289b72793e115 100644 --- a/impeller/geometry/path_component.h +++ b/impeller/geometry/path_component.h @@ -41,7 +41,7 @@ struct LinearPathComponent { Point Solve(Scalar time) const; - std::vector SubdivideAdaptively() const; + std::vector CreatePolyline() const; std::vector Extrema() const; @@ -64,7 +64,7 @@ struct QuadraticPathComponent { Point SolveDerivative(Scalar time) const; - std::vector SubdivideAdaptively( + std::vector CreatePolyline( const SmoothingApproximation& approximation) const; std::vector Extrema() const; @@ -95,7 +95,7 @@ struct CubicPathComponent { Point SolveDerivative(Scalar time) const; - std::vector SubdivideAdaptively( + std::vector CreatePolyline( const SmoothingApproximation& approximation) const; std::vector Extrema() const; From 6d95783b62058929582a720526ad9719b03a321e Mon Sep 17 00:00:00 2001 From: Chinmay Garde Date: Mon, 8 Nov 2021 14:13:23 -0800 Subject: [PATCH 188/433] Shorten names of the render target attachment descriptors. --- impeller/playground/playground.mm | 4 +-- .../backend/metal/command_buffer_mtl.h | 2 +- .../backend/metal/command_buffer_mtl.mm | 2 +- impeller/renderer/backend/metal/formats_mtl.h | 2 +- .../renderer/backend/metal/render_pass_mtl.h | 2 +- .../renderer/backend/metal/render_pass_mtl.mm | 12 +++---- impeller/renderer/command_buffer.h | 4 +-- impeller/renderer/formats.h | 8 ++--- impeller/renderer/render_pass.h | 7 +++++ impeller/renderer/render_pass_descriptor.cc | 31 ++++++++----------- impeller/renderer/render_pass_descriptor.h | 29 +++++++---------- impeller/renderer/renderer_unittests.cc | 4 +-- impeller/renderer/surface.cc | 5 ++- impeller/renderer/surface.h | 6 ++-- 14 files changed, 57 insertions(+), 61 deletions(-) diff --git a/impeller/playground/playground.mm b/impeller/playground/playground.mm index 67fe3807683e9..a0d9dcbeacf6f 100644 --- a/impeller/playground/playground.mm +++ b/impeller/playground/playground.mm @@ -140,14 +140,14 @@ static void PlaygroundKeyCallback(GLFWwindow* window, static_cast(current_drawable.texture.width), static_cast(current_drawable.texture.height)}; - RenderPassColorAttachment color0; + ColorAttachment color0; color0.texture = std::make_shared(color0_desc, current_drawable.texture); color0.clear_color = Color::SkyBlue(); color0.load_action = LoadAction::kClear; color0.store_action = StoreAction::kStore; - RenderPassDescriptor desc; + RenderTarget desc; desc.SetColorAttachment(color0, 0u); Surface surface(desc); diff --git a/impeller/renderer/backend/metal/command_buffer_mtl.h b/impeller/renderer/backend/metal/command_buffer_mtl.h index b5551f09f3fb5..9b438455f4923 100644 --- a/impeller/renderer/backend/metal/command_buffer_mtl.h +++ b/impeller/renderer/backend/metal/command_buffer_mtl.h @@ -34,7 +34,7 @@ class CommandBufferMTL final : public CommandBuffer { // |CommandBuffer| std::shared_ptr CreateRenderPass( - const RenderPassDescriptor& desc) const override; + const RenderTarget& desc) const override; FML_DISALLOW_COPY_AND_ASSIGN(CommandBufferMTL); }; diff --git a/impeller/renderer/backend/metal/command_buffer_mtl.mm b/impeller/renderer/backend/metal/command_buffer_mtl.mm index b6c12464aca38..5b4f7628cc39d 100644 --- a/impeller/renderer/backend/metal/command_buffer_mtl.mm +++ b/impeller/renderer/backend/metal/command_buffer_mtl.mm @@ -53,7 +53,7 @@ } std::shared_ptr CommandBufferMTL::CreateRenderPass( - const RenderPassDescriptor& desc) const { + const RenderTarget& desc) const { if (!buffer_) { return nullptr; } diff --git a/impeller/renderer/backend/metal/formats_mtl.h b/impeller/renderer/backend/metal/formats_mtl.h index 6b3f6e25c3de8..4d3d7cc6cee89 100644 --- a/impeller/renderer/backend/metal/formats_mtl.h +++ b/impeller/renderer/backend/metal/formats_mtl.h @@ -15,7 +15,7 @@ namespace impeller { -class RenderPassDescriptor; +class RenderTarget; constexpr MTLPixelFormat ToMTLPixelFormat(PixelFormat format) { switch (format) { diff --git a/impeller/renderer/backend/metal/render_pass_mtl.h b/impeller/renderer/backend/metal/render_pass_mtl.h index 40b92e7553701..530a60c2b0a2d 100644 --- a/impeller/renderer/backend/metal/render_pass_mtl.h +++ b/impeller/renderer/backend/metal/render_pass_mtl.h @@ -27,7 +27,7 @@ class RenderPassMTL final : public RenderPass { std::string label_; bool is_valid_ = false; - RenderPassMTL(id buffer, const RenderPassDescriptor& desc); + RenderPassMTL(id buffer, const RenderTarget& desc); // |RenderPass| bool IsValid() const override; diff --git a/impeller/renderer/backend/metal/render_pass_mtl.mm b/impeller/renderer/backend/metal/render_pass_mtl.mm index fe21cf84e50db..2a505888f293a 100644 --- a/impeller/renderer/backend/metal/render_pass_mtl.mm +++ b/impeller/renderer/backend/metal/render_pass_mtl.mm @@ -17,7 +17,7 @@ namespace impeller { -static bool ConfigureAttachment(const RenderPassAttachment& desc, +static bool ConfigureAttachment(const Attachment& desc, MTLRenderPassAttachmentDescriptor* attachment) { if (!desc.texture) { return false; @@ -30,7 +30,7 @@ static bool ConfigureAttachment(const RenderPassAttachment& desc, } static bool ConfigureColorAttachment( - const RenderPassColorAttachment& desc, + const ColorAttachment& desc, MTLRenderPassColorAttachmentDescriptor* attachment) { if (!ConfigureAttachment(desc, attachment)) { return false; @@ -40,7 +40,7 @@ static bool ConfigureColorAttachment( } static bool ConfigureDepthAttachment( - const RenderPassDepthAttachment& desc, + const DepthAttachment& desc, MTLRenderPassDepthAttachmentDescriptor* attachment) { if (!ConfigureAttachment(desc, attachment)) { return false; @@ -50,7 +50,7 @@ static bool ConfigureDepthAttachment( } static bool ConfigureStencilAttachment( - const RenderPassStencilAttachment& desc, + const StencilAttachment& desc, MTLRenderPassStencilAttachmentDescriptor* attachment) { if (!ConfigureAttachment(desc, attachment)) { return false; @@ -61,7 +61,7 @@ static bool ConfigureStencilAttachment( // TODO(csg): Move this to formats_mtl.h static MTLRenderPassDescriptor* ToMTLRenderPassDescriptor( - const RenderPassDescriptor& desc) { + const RenderTarget& desc) { auto result = [MTLRenderPassDescriptor renderPassDescriptor]; const auto& colors = desc.GetColorAttachments(); @@ -93,7 +93,7 @@ static bool ConfigureStencilAttachment( } RenderPassMTL::RenderPassMTL(id buffer, - const RenderPassDescriptor& desc) + const RenderTarget& desc) : buffer_(buffer), desc_(ToMTLRenderPassDescriptor(desc)), transients_buffer_(HostBuffer::Create()) { diff --git a/impeller/renderer/command_buffer.h b/impeller/renderer/command_buffer.h index 5108453b35112..554d595c2412f 100644 --- a/impeller/renderer/command_buffer.h +++ b/impeller/renderer/command_buffer.h @@ -13,7 +13,7 @@ namespace impeller { class Context; class RenderPass; -class RenderPassDescriptor; +class RenderTarget; //------------------------------------------------------------------------------ /// @brief A collection of encoded commands to be submitted to the GPU for @@ -59,7 +59,7 @@ class CommandBuffer { /// @return A valid render pass or null. /// virtual std::shared_ptr CreateRenderPass( - const RenderPassDescriptor& desc) const = 0; + const RenderTarget& desc) const = 0; protected: CommandBuffer(); diff --git a/impeller/renderer/formats.h b/impeller/renderer/formats.h index 59d24535f6f78..c826d0c26397e 100644 --- a/impeller/renderer/formats.h +++ b/impeller/renderer/formats.h @@ -319,7 +319,7 @@ struct PipelineStencilAttachment { } }; -struct RenderPassAttachment { +struct Attachment { std::shared_ptr texture = nullptr; LoadAction load_action = LoadAction::kDontCare; StoreAction store_action = StoreAction::kStore; @@ -327,15 +327,15 @@ struct RenderPassAttachment { constexpr operator bool() const { return static_cast(texture); } }; -struct RenderPassColorAttachment : public RenderPassAttachment { +struct ColorAttachment : public Attachment { Color clear_color = Color::BlackTransparent(); }; -struct RenderPassDepthAttachment : public RenderPassAttachment { +struct DepthAttachment : public Attachment { double clear_depth = 0.0; }; -struct RenderPassStencilAttachment : public RenderPassAttachment { +struct StencilAttachment : public Attachment { uint32_t clear_stencil = 0; }; diff --git a/impeller/renderer/render_pass.h b/impeller/renderer/render_pass.h index 42e242d7a035a..d0b2ee8c921f1 100644 --- a/impeller/renderer/render_pass.h +++ b/impeller/renderer/render_pass.h @@ -13,6 +13,13 @@ namespace impeller { class HostBuffer; class Allocator; +//------------------------------------------------------------------------------ +/// @brief Render passes encode render commands directed as one specific +/// render target into an underlying command buffer. +/// +/// Render passes can be obtained from the command buffer in which +/// the pass is meant to encode commands into. +/// class RenderPass { public: virtual ~RenderPass(); diff --git a/impeller/renderer/render_pass_descriptor.cc b/impeller/renderer/render_pass_descriptor.cc index bbd8f326b7cfc..f8dbb732cdd41 100644 --- a/impeller/renderer/render_pass_descriptor.cc +++ b/impeller/renderer/render_pass_descriptor.cc @@ -8,19 +8,18 @@ namespace impeller { -RenderPassDescriptor::RenderPassDescriptor() = default; +RenderTarget::RenderTarget() = default; -RenderPassDescriptor::~RenderPassDescriptor() = default; +RenderTarget::~RenderTarget() = default; -bool RenderPassDescriptor::HasColorAttachment(size_t index) const { +bool RenderTarget::HasColorAttachment(size_t index) const { if (auto found = colors_.find(index); found != colors_.end()) { return true; } return false; } -std::optional RenderPassDescriptor::GetColorAttachmentSize( - size_t index) const { +std::optional RenderTarget::GetColorAttachmentSize(size_t index) const { auto found = colors_.find(index); if (found == colors_.end()) { @@ -30,43 +29,39 @@ std::optional RenderPassDescriptor::GetColorAttachmentSize( return found->second.texture->GetSize(); } -RenderPassDescriptor& RenderPassDescriptor::SetColorAttachment( - RenderPassColorAttachment attachment, - size_t index) { +RenderTarget& RenderTarget::SetColorAttachment(ColorAttachment attachment, + size_t index) { if (attachment) { colors_[index] = attachment; } return *this; } -RenderPassDescriptor& RenderPassDescriptor::SetDepthAttachment( - RenderPassDepthAttachment attachment) { +RenderTarget& RenderTarget::SetDepthAttachment(DepthAttachment attachment) { if (attachment) { depth_ = std::move(attachment); } return *this; } -RenderPassDescriptor& RenderPassDescriptor::SetStencilAttachment( - RenderPassStencilAttachment attachment) { +RenderTarget& RenderTarget::SetStencilAttachment(StencilAttachment attachment) { if (attachment) { stencil_ = std::move(attachment); } return *this; } -const std::map& -RenderPassDescriptor::GetColorAttachments() const { +const std::map& RenderTarget::GetColorAttachments() + const { return colors_; } -const std::optional& -RenderPassDescriptor::GetDepthAttachment() const { +const std::optional& RenderTarget::GetDepthAttachment() const { return depth_; } -const std::optional& -RenderPassDescriptor::GetStencilAttachment() const { +const std::optional& RenderTarget::GetStencilAttachment() + const { return stencil_; } diff --git a/impeller/renderer/render_pass_descriptor.h b/impeller/renderer/render_pass_descriptor.h index d0f17f1c696e2..5ace1b2aca138 100644 --- a/impeller/renderer/render_pass_descriptor.h +++ b/impeller/renderer/render_pass_descriptor.h @@ -13,37 +13,32 @@ namespace impeller { -class RenderPassDescriptor { +class RenderTarget { public: - RenderPassDescriptor(); + RenderTarget(); - ~RenderPassDescriptor(); + ~RenderTarget(); bool HasColorAttachment(size_t index) const; std::optional GetColorAttachmentSize(size_t index) const; - RenderPassDescriptor& SetColorAttachment(RenderPassColorAttachment attachment, - size_t index); + RenderTarget& SetColorAttachment(ColorAttachment attachment, size_t index); - RenderPassDescriptor& SetDepthAttachment( - RenderPassDepthAttachment attachment); + RenderTarget& SetDepthAttachment(DepthAttachment attachment); - RenderPassDescriptor& SetStencilAttachment( - RenderPassStencilAttachment attachment); + RenderTarget& SetStencilAttachment(StencilAttachment attachment); - const std::map& GetColorAttachments() - const; + const std::map& GetColorAttachments() const; - const std::optional& GetDepthAttachment() const; + const std::optional& GetDepthAttachment() const; - const std::optional& GetStencilAttachment() - const; + const std::optional& GetStencilAttachment() const; private: - std::map colors_; - std::optional depth_; - std::optional stencil_; + std::map colors_; + std::optional depth_; + std::optional stencil_; }; } // namespace impeller diff --git a/impeller/renderer/renderer_unittests.cc b/impeller/renderer/renderer_unittests.cc index 496637f822161..3cf50fc5f6b1b 100644 --- a/impeller/renderer/renderer_unittests.cc +++ b/impeller/renderer/renderer_unittests.cc @@ -198,7 +198,7 @@ TEST_F(RendererTest, CanRenderToTexture) { std::shared_ptr r2t_pass; { - RenderPassColorAttachment color0; + ColorAttachment color0; color0.load_action = LoadAction::kClear; color0.store_action = StoreAction::kStore; @@ -218,7 +218,7 @@ TEST_F(RendererTest, CanRenderToTexture) { color0.texture->SetLabel("r2t_target"); - RenderPassDescriptor r2t_desc; + RenderTarget r2t_desc; r2t_desc.SetColorAttachment(color0, 0u); auto cmd_buffer = context->CreateRenderCommandBuffer(); r2t_pass = cmd_buffer->CreateRenderPass(r2t_desc); diff --git a/impeller/renderer/surface.cc b/impeller/renderer/surface.cc index 4c699497956a9..3882d9bcda74f 100644 --- a/impeller/renderer/surface.cc +++ b/impeller/renderer/surface.cc @@ -8,8 +8,7 @@ namespace impeller { -Surface::Surface(RenderPassDescriptor target_desc) - : desc_(std::move(target_desc)) { +Surface::Surface(RenderTarget target_desc) : desc_(std::move(target_desc)) { if (auto size = desc_.GetColorAttachmentSize(0u); size.has_value()) { size_ = size.value(); } else { @@ -29,7 +28,7 @@ bool Surface::IsValid() const { return is_valid_; } -const RenderPassDescriptor& Surface::GetTargetRenderPassDescriptor() const { +const RenderTarget& Surface::GetTargetRenderPassDescriptor() const { return desc_; } diff --git a/impeller/renderer/surface.h b/impeller/renderer/surface.h index 2fb2d85a821dc..7a7515ffe4029 100644 --- a/impeller/renderer/surface.h +++ b/impeller/renderer/surface.h @@ -16,7 +16,7 @@ namespace impeller { class Surface { public: - Surface(RenderPassDescriptor target_desc); + Surface(RenderTarget target_desc); ~Surface(); @@ -24,10 +24,10 @@ class Surface { bool IsValid() const; - const RenderPassDescriptor& GetTargetRenderPassDescriptor() const; + const RenderTarget& GetTargetRenderPassDescriptor() const; private: - RenderPassDescriptor desc_; + RenderTarget desc_; ISize size_; bool is_valid_ = false; From c43ed6d6d5589575f58da22f8e5711a6be3cdbd5 Mon Sep 17 00:00:00 2001 From: Chinmay Garde Date: Mon, 8 Nov 2021 14:16:09 -0800 Subject: [PATCH 189/433] Fixup names of pipeline descriptors. --- impeller/renderer/backend/metal/formats_mtl.h | 8 ++--- .../renderer/backend/metal/formats_mtl.mm | 12 +++---- impeller/renderer/formats.h | 20 +++++------ impeller/renderer/pipeline_builder.h | 2 +- impeller/renderer/pipeline_descriptor.cc | 14 ++++---- impeller/renderer/pipeline_descriptor.h | 34 ++++++++++--------- 6 files changed, 46 insertions(+), 44 deletions(-) diff --git a/impeller/renderer/backend/metal/formats_mtl.h b/impeller/renderer/backend/metal/formats_mtl.h index 4d3d7cc6cee89..0e76ff0926626 100644 --- a/impeller/renderer/backend/metal/formats_mtl.h +++ b/impeller/renderer/backend/metal/formats_mtl.h @@ -251,12 +251,12 @@ constexpr MTLClearColor ToMTLClearColor(const Color& color) { MTLRenderPipelineColorAttachmentDescriptor* ToMTLRenderPipelineColorAttachmentDescriptor( - PipelineColorAttachment descriptor); + ColorAttachmentDescriptor descriptor); MTLDepthStencilDescriptor* ToMTLDepthStencilDescriptor( - std::optional depth, - std::optional front, - std::optional back); + std::optional depth, + std::optional front, + std::optional back); MTLTextureDescriptor* ToMTLTextureDescriptor(const TextureDescriptor& desc); diff --git a/impeller/renderer/backend/metal/formats_mtl.mm b/impeller/renderer/backend/metal/formats_mtl.mm index 2030a07cbe687..e32121b2c66b2 100644 --- a/impeller/renderer/backend/metal/formats_mtl.mm +++ b/impeller/renderer/backend/metal/formats_mtl.mm @@ -12,7 +12,7 @@ MTLRenderPipelineColorAttachmentDescriptor* ToMTLRenderPipelineColorAttachmentDescriptor( - PipelineColorAttachment descriptor) { + ColorAttachmentDescriptor descriptor) { auto des = [[MTLRenderPipelineColorAttachmentDescriptor alloc] init]; des.pixelFormat = ToMTLPixelFormat(descriptor.format); @@ -35,7 +35,7 @@ } MTLStencilDescriptor* ToMTLStencilDescriptor( - const PipelineStencilAttachment& descriptor) { + const StencilAttachmentDescriptor& descriptor) { auto des = [[MTLStencilDescriptor alloc] init]; des.stencilCompareFunction = ToMTLCompareFunction(descriptor.stencil_compare); des.stencilFailureOperation = @@ -51,11 +51,11 @@ } MTLDepthStencilDescriptor* ToMTLDepthStencilDescriptor( - std::optional depth, - std::optional front, - std::optional back) { + std::optional depth, + std::optional front, + std::optional back) { if (!depth) { - depth = PipelineDepthAttachment{ + depth = DepthAttachmentDescriptor{ // Always pass the depth test. .depth_compare = CompareFunction::kAlways, .depth_write_enabled = false, diff --git a/impeller/renderer/formats.h b/impeller/renderer/formats.h index c826d0c26397e..fabbb76e36ee9 100644 --- a/impeller/renderer/formats.h +++ b/impeller/renderer/formats.h @@ -181,7 +181,7 @@ constexpr size_t BytesPerPixelForPixelFormat(PixelFormat format) { /// ``` /// /// The default blend mode is 1 - source alpha. -struct PipelineColorAttachment { +struct ColorAttachmentDescriptor { PixelFormat format = PixelFormat::kUnknown; bool blending_enabled = false; @@ -196,7 +196,7 @@ struct PipelineColorAttachment { std::underlying_type_t write_mask = static_cast(ColorWriteMask::kAll); - constexpr bool operator==(const PipelineColorAttachment& o) const { + constexpr bool operator==(const ColorAttachmentDescriptor& o) const { return format == o.format && // blending_enabled == o.blending_enabled && // src_color_blend_factor == o.src_color_blend_factor && // @@ -254,7 +254,7 @@ enum class StencilOperation { kDecrementWrap, }; -struct PipelineDepthAttachment { +struct DepthAttachmentDescriptor { //---------------------------------------------------------------------------- /// Indicates how to compare the value with that in the depth buffer. /// @@ -264,7 +264,7 @@ struct PipelineDepthAttachment { /// bool depth_write_enabled = false; - constexpr bool operator==(const PipelineDepthAttachment& o) const { + constexpr bool operator==(const DepthAttachmentDescriptor& o) const { return depth_compare == o.depth_compare && depth_write_enabled == o.depth_write_enabled; } @@ -274,7 +274,7 @@ struct PipelineDepthAttachment { } }; -struct PipelineStencilAttachment { +struct StencilAttachmentDescriptor { //---------------------------------------------------------------------------- /// Indicates the operation to perform between the reference value and the /// value in the stencil buffer. Both values have the read_mask applied to @@ -305,7 +305,7 @@ struct PipelineStencilAttachment { /// uint32_t write_mask = ~0; - constexpr bool operator==(const PipelineStencilAttachment& o) const { + constexpr bool operator==(const StencilAttachmentDescriptor& o) const { return stencil_compare == o.stencil_compare && stencil_failure == o.stencil_failure && depth_failure == o.depth_failure && @@ -344,17 +344,17 @@ struct StencilAttachment : public Attachment { namespace std { template <> -struct hash { +struct hash { constexpr std::size_t operator()( - const impeller::PipelineDepthAttachment& des) const { + const impeller::DepthAttachmentDescriptor& des) const { return des.GetHash(); } }; template <> -struct hash { +struct hash { constexpr std::size_t operator()( - const impeller::PipelineStencilAttachment& des) const { + const impeller::StencilAttachmentDescriptor& des) const { return des.GetHash(); } }; diff --git a/impeller/renderer/pipeline_builder.h b/impeller/renderer/pipeline_builder.h index 2f9f8bc1ecc34..f157b7fcc9c4b 100644 --- a/impeller/renderer/pipeline_builder.h +++ b/impeller/renderer/pipeline_builder.h @@ -84,7 +84,7 @@ struct PipelineBuilder { // Configure the sole color attachments pixel format. // TODO(csg): This can be easily reflected but we are sticking to the // convention that the first stage output is the color output. - PipelineColorAttachment color0; + ColorAttachmentDescriptor color0; color0.format = PixelFormat::kB8G8R8A8UNormInt; color0.blending_enabled = true; desc.SetColorAttachmentDescriptor(0u, std::move(color0)); diff --git a/impeller/renderer/pipeline_descriptor.cc b/impeller/renderer/pipeline_descriptor.cc index 828fee19ac64c..b367d4d009636 100644 --- a/impeller/renderer/pipeline_descriptor.cc +++ b/impeller/renderer/pipeline_descriptor.cc @@ -89,13 +89,13 @@ PipelineDescriptor& PipelineDescriptor::SetVertexDescriptor( PipelineDescriptor& PipelineDescriptor::SetColorAttachmentDescriptor( size_t index, - PipelineColorAttachment desc) { + ColorAttachmentDescriptor desc) { color_attachment_descriptors_[index] = std::move(desc); return *this; } -const PipelineColorAttachment* PipelineDescriptor::GetColorAttachmentDescriptor( - size_t index) const { +const ColorAttachmentDescriptor* +PipelineDescriptor::GetColorAttachmentDescriptor(size_t index) const { auto found = color_attachment_descriptors_.find(index); return found == color_attachment_descriptors_.end() ? nullptr : &found->second; @@ -114,19 +114,19 @@ PipelineDescriptor& PipelineDescriptor::SetStencilPixelFormat( } PipelineDescriptor& PipelineDescriptor::SetDepthStencilAttachmentDescriptor( - PipelineDepthAttachment desc) { + DepthAttachmentDescriptor desc) { depth_attachment_descriptor_ = desc; return *this; } PipelineDescriptor& PipelineDescriptor::SetStencilAttachmentDescriptors( - PipelineStencilAttachment front_and_back) { + StencilAttachmentDescriptor front_and_back) { return SetStencilAttachmentDescriptors(front_and_back, front_and_back); } PipelineDescriptor& PipelineDescriptor::SetStencilAttachmentDescriptors( - PipelineStencilAttachment front, - PipelineStencilAttachment back) { + StencilAttachmentDescriptor front, + StencilAttachmentDescriptor back) { front_stencil_attachment_descriptor_ = front; back_stencil_attachment_descriptor_ = back; return *this; diff --git a/impeller/renderer/pipeline_descriptor.h b/impeller/renderer/pipeline_descriptor.h index 17c80ad46bebc..3413b755fd9a7 100644 --- a/impeller/renderer/pipeline_descriptor.h +++ b/impeller/renderer/pipeline_descriptor.h @@ -53,38 +53,38 @@ class PipelineDescriptor final : public Comparable { PipelineDescriptor& SetColorAttachmentDescriptor( size_t index, - PipelineColorAttachment desc); + ColorAttachmentDescriptor desc); - const PipelineColorAttachment* GetColorAttachmentDescriptor( + const ColorAttachmentDescriptor* GetColorAttachmentDescriptor( size_t index) const; - const std::map + const std::map GetColorAttachmentDescriptors() const { return color_attachment_descriptors_; } PipelineDescriptor& SetDepthStencilAttachmentDescriptor( - PipelineDepthAttachment desc); + DepthAttachmentDescriptor desc); - std::optional GetDepthStencilAttachmentDescriptor() + std::optional GetDepthStencilAttachmentDescriptor() const { return depth_attachment_descriptor_; } PipelineDescriptor& SetStencilAttachmentDescriptors( - PipelineStencilAttachment front_and_back); + StencilAttachmentDescriptor front_and_back); PipelineDescriptor& SetStencilAttachmentDescriptors( - PipelineStencilAttachment front, - PipelineStencilAttachment back); + StencilAttachmentDescriptor front, + StencilAttachmentDescriptor back); - std::optional GetFrontStencilAttachmentDescriptor() - const { + std::optional + GetFrontStencilAttachmentDescriptor() const { return front_stencil_attachment_descriptor_; } - std::optional GetBackStencilAttachmentDescriptor() - const { + std::optional + GetBackStencilAttachmentDescriptor() const { return back_stencil_attachment_descriptor_; } @@ -106,14 +106,16 @@ class PipelineDescriptor final : public Comparable { std::string label_; size_t sample_count_ = 1; std::map> entrypoints_; - std::map + std::map color_attachment_descriptors_; std::shared_ptr vertex_descriptor_; PixelFormat depth_pixel_format_ = PixelFormat::kUnknown; PixelFormat stencil_pixel_format_ = PixelFormat::kUnknown; - std::optional depth_attachment_descriptor_; - std::optional front_stencil_attachment_descriptor_; - std::optional back_stencil_attachment_descriptor_; + std::optional depth_attachment_descriptor_; + std::optional + front_stencil_attachment_descriptor_; + std::optional + back_stencil_attachment_descriptor_; }; } // namespace impeller From 254d400fee531abb2d7cb3c66c74bc2488799cd6 Mon Sep 17 00:00:00 2001 From: Chinmay Garde Date: Tue, 9 Nov 2021 15:55:13 -0800 Subject: [PATCH 190/433] Minor fixups to pipeline creation. --- impeller/renderer/pipeline_builder.h | 32 +++++++--------- impeller/renderer/pipeline_descriptor.cc | 49 ++++++++++++++++++++++++ impeller/renderer/pipeline_descriptor.h | 32 ++++++---------- 3 files changed, 74 insertions(+), 39 deletions(-) diff --git a/impeller/renderer/pipeline_builder.h b/impeller/renderer/pipeline_builder.h index f157b7fcc9c4b..8cc488c512d7d 100644 --- a/impeller/renderer/pipeline_builder.h +++ b/impeller/renderer/pipeline_builder.h @@ -48,7 +48,16 @@ struct PipelineBuilder { static std::optional MakeDefaultPipelineDescriptor( const Context& context) { PipelineDescriptor desc; + if (InitializePipelineDescriptorDefaults(context, desc)) { + return {std::move(desc)}; + } else { + return std::nullopt; + } + } + [[nodiscard]] static bool InitializePipelineDescriptorDefaults( + const Context& context, + PipelineDescriptor& desc) { // Setup debug instrumentation. desc.SetLabel(SPrintF("%s Pipeline", VertexShader::kLabel.data())); @@ -61,7 +70,7 @@ struct PipelineBuilder { if (!vertex_function || !fragment_function) { FML_LOG(ERROR) << "Could not resolve pipeline entrypoint(s)."; - return std::nullopt; + return false; } desc.AddStageEntrypoint(std::move(vertex_function)); @@ -74,35 +83,22 @@ struct PipelineBuilder { if (!vertex_descriptor->SetStageInputs( VertexShader::kAllShaderStageInputs)) { FML_LOG(ERROR) << "Could not configure vertex descriptor."; - return std::nullopt; + return false; } desc.SetVertexDescriptor(std::move(vertex_descriptor)); } // Setup fragment shader output descriptions. { - // Configure the sole color attachments pixel format. - // TODO(csg): This can be easily reflected but we are sticking to the - // convention that the first stage output is the color output. + // Configure the sole color attachments pixel format. This is by + // convention. ColorAttachmentDescriptor color0; color0.format = PixelFormat::kB8G8R8A8UNormInt; color0.blending_enabled = true; desc.SetColorAttachmentDescriptor(0u, std::move(color0)); } - // Setup depth and stencil attachment descriptions. - { - // Configure the stencil attachment. - // TODO(csg): Make this configurable if possible as the D32 component is - // wasted. This can even be moved out of the "default" descriptor - // construction as a case can be made that this is caller responsibility. - // const auto combined_depth_stencil_format = - // PixelFormat::kPixelFormat_D32_Float_S8_UNormInt; - // desc.SetDepthPixelFormat(combined_depth_stencil_format); - // desc.SetStencilPixelFormat(combined_depth_stencil_format); - } - - return desc; + return true; } }; diff --git a/impeller/renderer/pipeline_descriptor.cc b/impeller/renderer/pipeline_descriptor.cc index b367d4d009636..0f7af4a65138f 100644 --- a/impeller/renderer/pipeline_descriptor.cc +++ b/impeller/renderer/pipeline_descriptor.cc @@ -132,4 +132,53 @@ PipelineDescriptor& PipelineDescriptor::SetStencilAttachmentDescriptors( return *this; } +void PipelineDescriptor::ResetAttachments() { + color_attachment_descriptors_.clear(); + depth_attachment_descriptor_.reset(); + front_stencil_attachment_descriptor_.reset(); + back_stencil_attachment_descriptor_.reset(); +} + +PixelFormat PipelineDescriptor::GetStencilPixelFormat() const { + return stencil_pixel_format_; +} + +std::optional +PipelineDescriptor::GetFrontStencilAttachmentDescriptor() const { + return front_stencil_attachment_descriptor_; +} + +std::optional +PipelineDescriptor::GetDepthStencilAttachmentDescriptor() const { + return depth_attachment_descriptor_; +} + +const std::map +PipelineDescriptor::GetColorAttachmentDescriptors() const { + return color_attachment_descriptors_; +} + +const std::shared_ptr& +PipelineDescriptor::GetVertexDescriptor() const { + return vertex_descriptor_; +} + +const std::map>& +PipelineDescriptor::GetStageEntrypoints() const { + return entrypoints_; +} + +const std::string& PipelineDescriptor::GetLabel() const { + return label_; +} + +PixelFormat PipelineDescriptor::GetDepthPixelFormat() const { + return depth_pixel_format_; +} + +std::optional +PipelineDescriptor::GetBackStencilAttachmentDescriptor() const { + return back_stencil_attachment_descriptor_; +} + } // namespace impeller diff --git a/impeller/renderer/pipeline_descriptor.h b/impeller/renderer/pipeline_descriptor.h index 3413b755fd9a7..3e43df2d0b488 100644 --- a/impeller/renderer/pipeline_descriptor.h +++ b/impeller/renderer/pipeline_descriptor.h @@ -30,7 +30,7 @@ class PipelineDescriptor final : public Comparable { PipelineDescriptor& SetLabel(std::string label); - const std::string& GetLabel() const { return label_; } + const std::string& GetLabel() const; PipelineDescriptor& SetSampleCount(size_t samples); @@ -40,16 +40,12 @@ class PipelineDescriptor final : public Comparable { std::shared_ptr function); const std::map>& - GetStageEntrypoints() const { - return entrypoints_; - } + GetStageEntrypoints() const; PipelineDescriptor& SetVertexDescriptor( std::shared_ptr vertex_descriptor); - const std::shared_ptr& GetVertexDescriptor() const { - return vertex_descriptor_; - } + const std::shared_ptr& GetVertexDescriptor() const; PipelineDescriptor& SetColorAttachmentDescriptor( size_t index, @@ -59,17 +55,13 @@ class PipelineDescriptor final : public Comparable { size_t index) const; const std::map - GetColorAttachmentDescriptors() const { - return color_attachment_descriptors_; - } + GetColorAttachmentDescriptors() const; PipelineDescriptor& SetDepthStencilAttachmentDescriptor( DepthAttachmentDescriptor desc); std::optional GetDepthStencilAttachmentDescriptor() - const { - return depth_attachment_descriptor_; - } + const; PipelineDescriptor& SetStencilAttachmentDescriptors( StencilAttachmentDescriptor front_and_back); @@ -79,22 +71,18 @@ class PipelineDescriptor final : public Comparable { StencilAttachmentDescriptor back); std::optional - GetFrontStencilAttachmentDescriptor() const { - return front_stencil_attachment_descriptor_; - } + GetFrontStencilAttachmentDescriptor() const; std::optional - GetBackStencilAttachmentDescriptor() const { - return back_stencil_attachment_descriptor_; - } + GetBackStencilAttachmentDescriptor() const; PipelineDescriptor& SetDepthPixelFormat(PixelFormat format); - PixelFormat GetDepthPixelFormat() const { return depth_pixel_format_; } + PixelFormat GetDepthPixelFormat() const; PipelineDescriptor& SetStencilPixelFormat(PixelFormat format); - PixelFormat GetStencilPixelFormat() const { return stencil_pixel_format_; } + PixelFormat GetStencilPixelFormat() const; // Comparable std::size_t GetHash() const override; @@ -102,6 +90,8 @@ class PipelineDescriptor final : public Comparable { // Comparable bool IsEqual(const PipelineDescriptor& other) const override; + void ResetAttachments(); + private: std::string label_; size_t sample_count_ = 1; From 23fd56fd040894e3e2c4e6e3ba87e0d8669810f0 Mon Sep 17 00:00:00 2001 From: Chinmay Garde Date: Wed, 10 Nov 2021 11:07:23 -0800 Subject: [PATCH 191/433] Make pipelines store pipeline descriptors. --- impeller/renderer/backend/metal/context_mtl.h | 6 ++++++ impeller/renderer/backend/metal/context_mtl.mm | 12 ++++++++++-- .../renderer/backend/metal/pipeline_library_mtl.mm | 11 +++++++++-- impeller/renderer/backend/metal/pipeline_mtl.h | 3 ++- impeller/renderer/backend/metal/pipeline_mtl.mm | 7 +++++-- impeller/renderer/context.h | 3 +++ impeller/renderer/pipeline.cc | 6 +++++- impeller/renderer/pipeline.h | 6 +++++- 8 files changed, 45 insertions(+), 9 deletions(-) diff --git a/impeller/renderer/backend/metal/context_mtl.h b/impeller/renderer/backend/metal/context_mtl.h index fd9a0e6c20dfc..fc07f5f7b7a01 100644 --- a/impeller/renderer/backend/metal/context_mtl.h +++ b/impeller/renderer/backend/metal/context_mtl.h @@ -61,6 +61,12 @@ class ContextMTL final : public Context, // |Context| std::shared_ptr CreateRenderCommandBuffer() const override; + // |Context| + std::shared_ptr CreateTransferCommandBuffer() const override; + + std::shared_ptr CreateCommandBufferInQueue( + id queue) const; + FML_DISALLOW_COPY_AND_ASSIGN(ContextMTL); }; diff --git a/impeller/renderer/backend/metal/context_mtl.mm b/impeller/renderer/backend/metal/context_mtl.mm index 35efa3b560be4..c84c17cc1e9b6 100644 --- a/impeller/renderer/backend/metal/context_mtl.mm +++ b/impeller/renderer/backend/metal/context_mtl.mm @@ -108,12 +108,20 @@ } std::shared_ptr ContextMTL::CreateRenderCommandBuffer() const { + return CreateCommandBufferInQueue(render_queue_); +} + +std::shared_ptr ContextMTL::CreateTransferCommandBuffer() const { + return CreateCommandBufferInQueue(transfer_queue_); +} + +std::shared_ptr ContextMTL::CreateCommandBufferInQueue( + id queue) const { if (!IsValid()) { return nullptr; } - auto buffer = - std::shared_ptr(new CommandBufferMTL(render_queue_)); + auto buffer = std::shared_ptr(new CommandBufferMTL(queue)); if (!buffer->IsValid()) { return nullptr; } diff --git a/impeller/renderer/backend/metal/pipeline_library_mtl.mm b/impeller/renderer/backend/metal/pipeline_library_mtl.mm index 6d8bfc6e24b59..05437b8eb8a0e 100644 --- a/impeller/renderer/backend/metal/pipeline_library_mtl.mm +++ b/impeller/renderer/backend/metal/pipeline_library_mtl.mm @@ -77,6 +77,11 @@ return future; } + // TODO(csg): There is a bug here where multiple calls to GetRenderPipeline + // will result in multiple render pipelines of the same descriptor being + // created till the first instance of the creation invokes its completion + // callback. + auto thiz = shared_from_this(); auto completion_handler = @@ -88,8 +93,10 @@ promise->set_value(nullptr); } else { auto new_pipeline = std::shared_ptr(new PipelineMTL( - render_pipeline_state, - CreateDepthStencilDescriptor(descriptor, device_))); + descriptor, // + render_pipeline_state, // + CreateDepthStencilDescriptor(descriptor, device_) // + )); promise->set_value(new_pipeline); this->SavePipeline(descriptor, new_pipeline); } diff --git a/impeller/renderer/backend/metal/pipeline_mtl.h b/impeller/renderer/backend/metal/pipeline_mtl.h index c115880b0ac20..0cb0ef7bc81f2 100644 --- a/impeller/renderer/backend/metal/pipeline_mtl.h +++ b/impeller/renderer/backend/metal/pipeline_mtl.h @@ -30,7 +30,8 @@ class PipelineMTL final : public Pipeline, id depth_stencil_state_; bool is_valid_ = false; - PipelineMTL(id state, + PipelineMTL(PipelineDescriptor desc, + id state, id depth_stencil_state); // |PipelineMTL| diff --git a/impeller/renderer/backend/metal/pipeline_mtl.mm b/impeller/renderer/backend/metal/pipeline_mtl.mm index 9ff5627697f7a..6af3e7bd2e46b 100644 --- a/impeller/renderer/backend/metal/pipeline_mtl.mm +++ b/impeller/renderer/backend/metal/pipeline_mtl.mm @@ -6,9 +6,12 @@ namespace impeller { -PipelineMTL::PipelineMTL(id state, +PipelineMTL::PipelineMTL(PipelineDescriptor desc, + id state, id depth_stencil_state) - : pipeline_state_(state), depth_stencil_state_(depth_stencil_state) { + : Pipeline(std::move(desc)), + pipeline_state_(state), + depth_stencil_state_(depth_stencil_state) { if (!pipeline_state_) { return; } diff --git a/impeller/renderer/context.h b/impeller/renderer/context.h index bb19ac458102d..ac60ab2b40b4a 100644 --- a/impeller/renderer/context.h +++ b/impeller/renderer/context.h @@ -43,6 +43,9 @@ class Context { virtual std::shared_ptr CreateRenderCommandBuffer() const = 0; + virtual std::shared_ptr CreateTransferCommandBuffer() + const = 0; + protected: Context(); diff --git a/impeller/renderer/pipeline.cc b/impeller/renderer/pipeline.cc index 6badd53ab2e08..9577fc0e6cf30 100644 --- a/impeller/renderer/pipeline.cc +++ b/impeller/renderer/pipeline.cc @@ -9,7 +9,7 @@ namespace impeller { -Pipeline::Pipeline() = default; +Pipeline::Pipeline(PipelineDescriptor desc) : desc_(std::move(desc)) {} Pipeline::~Pipeline() = default; @@ -25,4 +25,8 @@ PipelineFuture CreatePipelineFuture(const Context& context, return context.GetPipelineLibrary()->GetRenderPipeline(std::move(desc)); } +const PipelineDescriptor& Pipeline::GetDescriptor() const { + return desc_; +} + } // namespace impeller diff --git a/impeller/renderer/pipeline.h b/impeller/renderer/pipeline.h index 8a5e17236685d..4a6521e02ff94 100644 --- a/impeller/renderer/pipeline.h +++ b/impeller/renderer/pipeline.h @@ -42,10 +42,14 @@ class Pipeline { virtual bool IsValid() const = 0; + const PipelineDescriptor& GetDescriptor() const; + protected: - Pipeline(); + Pipeline(PipelineDescriptor desc); private: + const PipelineDescriptor desc_; + FML_DISALLOW_COPY_AND_ASSIGN(Pipeline); }; From b2910635305ea131bdd5cd55af797b6357a6b020 Mon Sep 17 00:00:00 2001 From: Chinmay Garde Date: Wed, 10 Nov 2021 15:40:28 -0800 Subject: [PATCH 192/433] Start consolidating content rendering. --- impeller/.clang-format | 7 +-- impeller/entity/content_renderer.cc | 9 ++++ impeller/entity/content_renderer.h | 7 +++ impeller/entity/contents.cc | 77 ++++++++++++++++++++++++++++- impeller/entity/contents.h | 53 +++++++++++++++++++- impeller/entity/entity.cc | 33 ------------- impeller/entity/entity.h | 6 --- impeller/entity/entity_renderer.cc | 66 ++----------------------- impeller/entity/entity_renderer.h | 22 +-------- impeller/entity/entity_unittests.cc | 15 +++++- 10 files changed, 164 insertions(+), 131 deletions(-) diff --git a/impeller/.clang-format b/impeller/.clang-format index 6fdf1dc888cb6..1b8a1bdd7dc31 100644 --- a/impeller/.clang-format +++ b/impeller/.clang-format @@ -1,8 +1,5 @@ # Defines the Chromium style for automatic reformatting. # http://clang.llvm.org/docs/ClangFormatStyleOptions.html BasedOnStyle: Chromium -# This defaults to 'Auto'. Explicitly set it for a while, so that -# 'vector >' in existing files gets formatted to -# 'vector>'. ('Auto' means that clang-format will only use -# 'int>>' if the file already contains at least one such instance.) -Standard: Cpp11 +Standard: c++17 +EmptyLineBeforeAccessModifier: Always diff --git a/impeller/entity/content_renderer.cc b/impeller/entity/content_renderer.cc index 4ea53ec0c4b7b..d798d62f54c62 100644 --- a/impeller/entity/content_renderer.cc +++ b/impeller/entity/content_renderer.cc @@ -13,6 +13,7 @@ ContentRenderer::ContentRenderer(std::shared_ptr context) } gradient_fill_pipeline_ = std::make_unique(*context_); + solid_fill_pipeline_ = std::make_unique(*context_); is_valid_ = true; } @@ -34,4 +35,12 @@ std::shared_ptr ContentRenderer::GetGradientFillPipeline() const { return gradient_fill_pipeline_->WaitAndGet(); } +std::shared_ptr ContentRenderer::GetSolidFillPipeline() const { + if (!IsValid()) { + return nullptr; + } + + return solid_fill_pipeline_->WaitAndGet(); +} + } // namespace impeller diff --git a/impeller/entity/content_renderer.h b/impeller/entity/content_renderer.h index 671504a298099..77869bbcf35dc 100644 --- a/impeller/entity/content_renderer.h +++ b/impeller/entity/content_renderer.h @@ -9,12 +9,16 @@ #include "flutter/fml/macros.h" #include "flutter/impeller/entity/gradient_fill.frag.h" #include "flutter/impeller/entity/gradient_fill.vert.h" +#include "flutter/impeller/entity/solid_fill.frag.h" +#include "flutter/impeller/entity/solid_fill.vert.h" #include "impeller/renderer/pipeline.h" namespace impeller { using GradientFillPipeline = PipelineT; +using SolidFillPipeline = + PipelineT; class ContentRenderer { public: @@ -26,11 +30,14 @@ class ContentRenderer { std::shared_ptr GetGradientFillPipeline() const; + std::shared_ptr GetSolidFillPipeline() const; + std::shared_ptr GetContext() const; private: std::shared_ptr context_; std::unique_ptr gradient_fill_pipeline_; + std::unique_ptr solid_fill_pipeline_; bool is_valid_ = false; FML_DISALLOW_COPY_AND_ASSIGN(ContentRenderer); diff --git a/impeller/entity/contents.cc b/impeller/entity/contents.cc index 41f468aa441b8..dc2db4a47747a 100644 --- a/impeller/entity/contents.cc +++ b/impeller/entity/contents.cc @@ -14,10 +14,18 @@ namespace impeller { +/******************************************************************************* + ******* Contents + ******************************************************************************/ + Contents::Contents() = default; Contents::~Contents() = default; +/******************************************************************************* + ******* Linear Gradient Contents + ******************************************************************************/ + LinearGradientContents::LinearGradientContents() = default; LinearGradientContents::~LinearGradientContents() = default; @@ -41,7 +49,6 @@ const std::vector& LinearGradientContents::GetColors() const { return colors_; } -// |Contents| bool LinearGradientContents::Render(const ContentRenderer& renderer, const Entity& entity, const Surface& surface, @@ -84,4 +91,72 @@ bool LinearGradientContents::Render(const ContentRenderer& renderer, return pass.AddCommand(std::move(cmd)); } +/******************************************************************************* + ******* SolidColorContents + ******************************************************************************/ + +void SolidColorContents::SetColor(Color color) { + color_ = color; +} + +const Color& SolidColorContents::GetColor() const { + return color_; +} + +bool SolidColorContents::Render(const ContentRenderer& renderer, + const Entity& entity, + const Surface& surface, + RenderPass& pass) const { + using VS = SolidFillPipeline::VertexShader; + + Command cmd; + cmd.label = "SolidFill"; + cmd.pipeline = renderer.GetSolidFillPipeline(); + if (cmd.pipeline == nullptr) { + return false; + } + + VertexBufferBuilder vtx_builder; + { + auto tesselation_result = Tessellator{}.Tessellate( + entity.GetPath().CreatePolyline(), [&vtx_builder](auto point) { + VS::PerVertexData vtx; + vtx.vertices = point; + vtx_builder.AppendVertex(vtx); + }); + if (!tesselation_result) { + return false; + } + } + + cmd.BindVertices(vtx_builder.CreateVertexBuffer( + *renderer.GetContext()->GetPermanentsAllocator())); + + VS::FrameInfo frame_info; + frame_info.mvp = + Matrix::MakeOrthographic(surface.GetSize()) * entity.GetTransformation(); + frame_info.color = entity.GetBackgroundColor(); + VS::BindFrameInfo(cmd, pass.GetTransientsBuffer().EmplaceUniform(frame_info)); + + cmd.primitive_type = PrimitiveType::kTriangle; + + if (!pass.AddCommand(std::move(cmd))) { + return false; + } + + return true; +} + +/******************************************************************************* + ******* SolidStrokeContents + ******************************************************************************/ + +void SolidStrokeContents::SetColor(Color color) { + color_ = color; +} + +const Color& SolidStrokeContents::GetColor() const { + return color_; +} + } // namespace impeller diff --git a/impeller/entity/contents.h b/impeller/entity/contents.h index a2a55bf404289..0c04e5474cc91 100644 --- a/impeller/entity/contents.h +++ b/impeller/entity/contents.h @@ -21,7 +21,7 @@ class Contents { public: Contents(); - ~Contents(); + virtual ~Contents(); virtual bool Render(const ContentRenderer& renderer, const Entity& entity, @@ -36,7 +36,7 @@ class LinearGradientContents final : public Contents { public: LinearGradientContents(); - ~LinearGradientContents(); + ~LinearGradientContents() override; // |Contents| bool Render(const ContentRenderer& renderer, @@ -58,4 +58,53 @@ class LinearGradientContents final : public Contents { FML_DISALLOW_COPY_AND_ASSIGN(LinearGradientContents); }; +class SolidColorContents final : public Contents { + public: + SolidColorContents(); + + ~SolidColorContents() override; + + void SetColor(Color color); + + const Color& GetColor() const; + + // |Contents| + bool Render(const ContentRenderer& renderer, + const Entity& entity, + const Surface& surface, + RenderPass& pass) const override; + + private: + Color color_; + + FML_DISALLOW_COPY_AND_ASSIGN(SolidColorContents); +}; + +class SolidStrokeContents final : public Contents { + public: + SolidStrokeContents(); + + ~SolidStrokeContents(); + + void SetColor(Color color); + + const Color& GetColor() const; + + void SetStrokeSize(Scalar size) { stroke_size_ = size; } + + Scalar GetStrokeSize() const { return stroke_size_; } + + // |Contents| + bool Render(const ContentRenderer& renderer, + const Entity& entity, + const Surface& surface, + RenderPass& pass) const override; + + private: + Color color_; + Scalar stroke_size_ = 0.0; + + FML_DISALLOW_COPY_AND_ASSIGN(SolidStrokeContents); +}; + } // namespace impeller diff --git a/impeller/entity/entity.cc b/impeller/entity/entity.cc index 186d8e1aacc24..bc8d81aae49d7 100644 --- a/impeller/entity/entity.cc +++ b/impeller/entity/entity.cc @@ -66,37 +66,4 @@ const std::shared_ptr& Entity::GetContents() const { return contents_; } -bool Entity::HasStroke() const { - return stroke_size_ > 0.0 && !stroke_color_.IsTransparent(); -} - -bool Entity::HasContents() const { - if (contents_) { - return true; - } - return !background_color_.IsTransparent(); -} - -bool Entity::HasRenderableContents() const { - const bool has_empty_path = path_.GetBoundingBox().IsZero(); - - if (has_empty_path) { - return false; - } - - if (IsClip()) { - return true; - } - - if (HasStroke()) { - return true; - } - - if (HasContents()) { - return true; - } - - return false; -} - } // namespace impeller diff --git a/impeller/entity/entity.h b/impeller/entity/entity.h index ed3942fc8aedf..bb4067927012b 100644 --- a/impeller/entity/entity.h +++ b/impeller/entity/entity.h @@ -43,12 +43,6 @@ class Entity { bool IsClip() const; - bool HasStroke() const; - - bool HasContents() const; - - bool HasRenderableContents() const; - void SetContents(std::shared_ptr contents); const std::shared_ptr& GetContents() const; diff --git a/impeller/entity/entity_renderer.cc b/impeller/entity/entity_renderer.cc index a15846b7bba00..77e1369754c66 100644 --- a/impeller/entity/entity_renderer.cc +++ b/impeller/entity/entity_renderer.cc @@ -5,8 +5,7 @@ #include "flutter/impeller/entity/entity_renderer.h" #include "flutter/fml/trace_event.h" -#include "impeller/renderer/tessellator.h" -#include "impeller/renderer/vertex_buffer_builder.h" +#include "impeller/entity/content_renderer.h" namespace impeller { @@ -21,8 +20,6 @@ EntityRenderer::EntityRenderer(std::shared_ptr context) return; } - solid_fill_pipeline_ = std::make_unique(*context_); - is_valid_ = true; } @@ -40,8 +37,8 @@ bool EntityRenderer::RenderEntities(const Surface& surface, } for (const auto& entity : entities) { - if (RenderEntity(surface, onscreen_pass, entity) == - EntityRenderer::RenderResult::kFailure) { + if (auto contents = entity.GetContents(); + !contents->Render(*content_renderer_, entity, surface, onscreen_pass)) { return false; } } @@ -49,61 +46,4 @@ bool EntityRenderer::RenderEntities(const Surface& surface, return true; } -EntityRenderer::RenderResult EntityRenderer::RenderEntity( - const Surface& surface, - RenderPass& pass, - const Entity& entity) { - if (!entity.HasRenderableContents()) { - return RenderResult::kSkipped; - } - - if (entity.HasContents() && !entity.IsClip() && !entity.GetContents()) { - using VS = SolidFillPipeline::VertexShader; - - Command cmd; - cmd.label = "SolidFill"; - cmd.pipeline = solid_fill_pipeline_->WaitAndGet(); - if (cmd.pipeline == nullptr) { - return RenderResult::kFailure; - } - - VertexBufferBuilder vtx_builder; - { - auto tesselation_result = Tessellator{}.Tessellate( - entity.GetPath().CreatePolyline(), [&vtx_builder](auto point) { - VS::PerVertexData vtx; - vtx.vertices = point; - vtx_builder.AppendVertex(vtx); - }); - if (!tesselation_result) { - return RenderResult::kFailure; - } - } - - cmd.BindVertices( - vtx_builder.CreateVertexBuffer(*context_->GetPermanentsAllocator())); - - VS::FrameInfo frame_info; - frame_info.mvp = Matrix::MakeOrthographic(surface.GetSize()) * - entity.GetTransformation(); - frame_info.color = entity.GetBackgroundColor(); - VS::BindFrameInfo(cmd, - pass.GetTransientsBuffer().EmplaceUniform(frame_info)); - - cmd.primitive_type = PrimitiveType::kTriangle; - - if (!pass.AddCommand(std::move(cmd))) { - return RenderResult::kFailure; - } - } else if (entity.GetContents()) { - auto result = - entity.GetContents()->Render(*content_renderer_, entity, surface, pass); - if (!result) { - return RenderResult::kFailure; - } - } - - return RenderResult::kSuccess; -} - } // namespace impeller diff --git a/impeller/entity/entity_renderer.h b/impeller/entity/entity_renderer.h index caa670dc9c4fd..1a0160cc1d798 100644 --- a/impeller/entity/entity_renderer.h +++ b/impeller/entity/entity_renderer.h @@ -7,18 +7,14 @@ #include #include "flutter/fml/macros.h" -#include "flutter/impeller/entity/solid_fill.frag.h" -#include "flutter/impeller/entity/solid_fill.vert.h" -#include "impeller/entity/content_renderer.h" #include "impeller/entity/entity.h" #include "impeller/renderer/context.h" -#include "impeller/renderer/pipeline.h" -#include "impeller/renderer/pipeline_builder.h" -#include "impeller/renderer/render_pass.h" #include "impeller/renderer/surface.h" namespace impeller { +class ContentRenderer; + class EntityRenderer { public: EntityRenderer(std::shared_ptr context); @@ -31,22 +27,8 @@ class EntityRenderer { RenderPass& onscreen_pass, const std::vector& entities); - enum class RenderResult { - kSkipped, - kSuccess, - kFailure, - }; - - [[nodiscard]] RenderResult RenderEntity(const Surface& surface, - RenderPass& onscreen_pass, - const Entity& entities); - private: - using SolidFillPipeline = - PipelineT; - std::shared_ptr context_; - std::unique_ptr solid_fill_pipeline_; std::unique_ptr content_renderer_; bool is_valid_ = false; diff --git a/impeller/entity/entity_unittests.cc b/impeller/entity/entity_unittests.cc index 939d907173bf2..9a82998fc295b 100644 --- a/impeller/entity/entity_unittests.cc +++ b/impeller/entity/entity_unittests.cc @@ -3,11 +3,24 @@ // found in the LICENSE file. #include "flutter/testing/testing.h" +#include "impeller/entity/entity.h" +#include "impeller/geometry/path_builder.h" +#include "impeller/playground/playground.h" namespace impeller { namespace testing { -// +using EntityTest = Playground; + +TEST_F(EntityTest, CanCreateEntity) { + Entity entity; + ASSERT_TRUE(entity.GetTransformation().IsIdentity()); +} + +TEST_F(EntityTest, CanDrawRect) { + Entity entity; + entity.SetPath(PathBuilder{}.AddRect({100, 100, 100, 100}).CreatePath()); +} } // namespace testing } // namespace impeller From 843e97a113b8f67e81057b268687af3799e27121 Mon Sep 17 00:00:00 2001 From: Chinmay Garde Date: Fri, 12 Nov 2021 14:16:21 -0800 Subject: [PATCH 193/433] Separate save layer calls into a canvas pass. --- impeller/aiks/BUILD.gn | 2 ++ impeller/aiks/canvas.cc | 40 ++++++++++++++---------------- impeller/aiks/canvas.h | 6 +++-- impeller/aiks/canvas_pass.cc | 21 ++++++++++++++++ impeller/aiks/canvas_pass.h | 28 +++++++++++++++++++++ impeller/aiks/paint.cc | 10 +++++++- impeller/aiks/paint.h | 3 ++- impeller/aiks/picture.h | 3 ++- impeller/aiks/picture_renderer.cc | 12 ++++++--- impeller/entity/contents.cc | 6 ++++- impeller/entity/contents.h | 2 +- impeller/entity/entity.cc | 8 ------ impeller/entity/entity.h | 6 ----- impeller/entity/entity_renderer.cc | 8 +++--- 14 files changed, 106 insertions(+), 49 deletions(-) create mode 100644 impeller/aiks/canvas_pass.cc create mode 100644 impeller/aiks/canvas_pass.h diff --git a/impeller/aiks/BUILD.gn b/impeller/aiks/BUILD.gn index 39ad62609f5e7..ce098d2b4a791 100644 --- a/impeller/aiks/BUILD.gn +++ b/impeller/aiks/BUILD.gn @@ -8,6 +8,8 @@ impeller_component("aiks") { sources = [ "canvas.cc", "canvas.h", + "canvas_pass.cc", + "canvas_pass.h", "image.cc", "image.h", "paint.cc", diff --git a/impeller/aiks/canvas.cc b/impeller/aiks/canvas.cc index edfda32088e45..b8a4b052a8a89 100644 --- a/impeller/aiks/canvas.cc +++ b/impeller/aiks/canvas.cc @@ -12,10 +12,7 @@ namespace impeller { Canvas::Canvas() { xformation_stack_.push({}); - - Paint default_paint; - default_paint.color = Color::White(); - paint_stack_.push(default_paint); + passes_.emplace_back(CanvasPass{}); } Canvas::~Canvas() = default; @@ -23,7 +20,6 @@ Canvas::~Canvas() = default; void Canvas::Save() { FML_DCHECK(xformation_stack_.size() > 0); xformation_stack_.push(xformation_stack_.top()); - paint_stack_.push(paint_stack_.top()); } bool Canvas::Restore() { @@ -32,7 +28,6 @@ bool Canvas::Restore() { return false; } xformation_stack_.pop(); - paint_stack_.pop(); return true; } @@ -69,21 +64,15 @@ void Canvas::RestoreToCount(size_t count) { } void Canvas::DrawPath(Path path, Paint paint) { - Color color = paint.color; - color.alpha *= paint_stack_.top().color.alpha; - Entity entity; entity.SetTransformation(GetCurrentTransformation()); entity.SetPath(std::move(path)); - entity.SetBackgroundColor(color); - entity.SetContents(std::move(paint.contents)); - - ops_.emplace_back(std::move(entity)); + entity.SetContents(paint.CreateContentsForEntity()); + GetCurrentPass().PushEntity(std::move(entity)); } void Canvas::SaveLayer(const Paint& paint, std::optional bounds) { Save(); - paint_stack_.top() = paint; } void Canvas::ClipPath(Path path) { @@ -91,24 +80,33 @@ void Canvas::ClipPath(Path path) { entity.SetTransformation(GetCurrentTransformation()); entity.SetPath(std::move(path)); entity.SetIsClip(true); - ops_.emplace_back(std::move(entity)); + GetCurrentPass().PushEntity(std::move(entity)); } void Canvas::DrawShadow(Path path, Color color, Scalar elevation) {} void Canvas::DrawPicture(const Picture& picture) { - for (const auto& entity : picture.entities) { - auto new_entity = entity; - new_entity.SetTransformation(GetCurrentTransformation() * - new_entity.GetTransformation()); - ops_.emplace_back(std::move(new_entity)); + for (const auto& pass : picture.passes) { + CanvasPass new_pass; + for (const auto& entity : pass.GetPassEntities()) { + auto new_entity = entity; + new_entity.SetTransformation(GetCurrentTransformation() * + entity.GetTransformation()); + new_pass.PushEntity(std::move(new_entity)); + } + passes_.emplace_back(std::move(new_pass)); } } Picture Canvas::EndRecordingAsPicture() { Picture picture; - picture.entities = std::move(ops_); + picture.passes = std::move(passes_); return picture; } +CanvasPass& Canvas::GetCurrentPass() { + FML_DCHECK(!passes_.empty()); + return passes_.back(); +} + } // namespace impeller diff --git a/impeller/aiks/canvas.h b/impeller/aiks/canvas.h index 6cb9fcec37ba9..4dedd3224160c 100644 --- a/impeller/aiks/canvas.h +++ b/impeller/aiks/canvas.h @@ -10,6 +10,7 @@ #include #include "flutter/fml/macros.h" +#include "impeller/aiks/canvas_pass.h" #include "impeller/aiks/paint.h" #include "impeller/aiks/picture.h" #include "impeller/geometry/matrix.h" @@ -58,8 +59,9 @@ class Canvas { private: std::stack xformation_stack_; - std::stack paint_stack_; - std::vector ops_; + std::vector passes_; + + CanvasPass& GetCurrentPass(); FML_DISALLOW_COPY_AND_ASSIGN(Canvas); }; diff --git a/impeller/aiks/canvas_pass.cc b/impeller/aiks/canvas_pass.cc new file mode 100644 index 0000000000000..7ea207533a151 --- /dev/null +++ b/impeller/aiks/canvas_pass.cc @@ -0,0 +1,21 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "impeller/aiks/canvas_pass.h" + +namespace impeller { + +CanvasPass::CanvasPass() = default; + +CanvasPass::~CanvasPass() = default; + +void CanvasPass::PushEntity(Entity entity) { + ops_.emplace_back(std::move(entity)); +} + +const std::vector& CanvasPass::GetPassEntities() const { + return ops_; +} + +} // namespace impeller diff --git a/impeller/aiks/canvas_pass.h b/impeller/aiks/canvas_pass.h new file mode 100644 index 0000000000000..0183ade6c2796 --- /dev/null +++ b/impeller/aiks/canvas_pass.h @@ -0,0 +1,28 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#pragma once + +#include + +#include "flutter/fml/macros.h" +#include "impeller/entity/entity.h" + +namespace impeller { + +class CanvasPass { + public: + CanvasPass(); + + ~CanvasPass(); + + void PushEntity(Entity entity); + + const std::vector& GetPassEntities() const; + + private: + std::vector ops_; +}; + +} // namespace impeller diff --git a/impeller/aiks/paint.cc b/impeller/aiks/paint.cc index 05279a4ecc5be..e45f9a76458c4 100644 --- a/impeller/aiks/paint.cc +++ b/impeller/aiks/paint.cc @@ -6,6 +6,14 @@ namespace impeller { -// +std::shared_ptr Paint::CreateContentsForEntity() const { + if (!color.IsTransparent()) { + auto solid_color = std::make_shared(); + solid_color->SetColor(color); + return solid_color; + } + + return nullptr; +} } // namespace impeller diff --git a/impeller/aiks/paint.h b/impeller/aiks/paint.h index c3b947b8d8469..7f59475fcee58 100644 --- a/impeller/aiks/paint.h +++ b/impeller/aiks/paint.h @@ -14,7 +14,8 @@ namespace impeller { struct Paint { Color color; Scalar stroke_width = 0.0; - std::shared_ptr contents; + + std::shared_ptr CreateContentsForEntity() const; }; } // namespace impeller diff --git a/impeller/aiks/picture.h b/impeller/aiks/picture.h index d65d272c48907..048a81b98a300 100644 --- a/impeller/aiks/picture.h +++ b/impeller/aiks/picture.h @@ -8,12 +8,13 @@ #include #include "flutter/fml/macros.h" +#include "impeller/aiks/canvas_pass.h" #include "impeller/entity/entity.h" namespace impeller { struct Picture { - std::vector entities; + std::vector passes; }; } // namespace impeller diff --git a/impeller/aiks/picture_renderer.cc b/impeller/aiks/picture_renderer.cc index ad613a0ccd703..9f918a1837bf4 100644 --- a/impeller/aiks/picture_renderer.cc +++ b/impeller/aiks/picture_renderer.cc @@ -29,10 +29,14 @@ bool PictureRenderer::Render(const Surface& surface, return false; } - return entity_renderer_.RenderEntities(surface, // - onscreen_pass, // - picture.entities // - ); + for (const auto& pass : picture.passes) { + if (!entity_renderer_.RenderEntities(surface, onscreen_pass, + pass.GetPassEntities())) { + return false; + } + } + + return true; } } // namespace impeller diff --git a/impeller/entity/contents.cc b/impeller/entity/contents.cc index dc2db4a47747a..7ce063119b669 100644 --- a/impeller/entity/contents.cc +++ b/impeller/entity/contents.cc @@ -95,6 +95,10 @@ bool LinearGradientContents::Render(const ContentRenderer& renderer, ******* SolidColorContents ******************************************************************************/ +SolidColorContents::SolidColorContents() = default; + +SolidColorContents::~SolidColorContents() = default; + void SolidColorContents::SetColor(Color color) { color_ = color; } @@ -135,7 +139,7 @@ bool SolidColorContents::Render(const ContentRenderer& renderer, VS::FrameInfo frame_info; frame_info.mvp = Matrix::MakeOrthographic(surface.GetSize()) * entity.GetTransformation(); - frame_info.color = entity.GetBackgroundColor(); + frame_info.color = color_; VS::BindFrameInfo(cmd, pass.GetTransientsBuffer().EmplaceUniform(frame_info)); cmd.primitive_type = PrimitiveType::kTriangle; diff --git a/impeller/entity/contents.h b/impeller/entity/contents.h index 0c04e5474cc91..2feedcfccf2f9 100644 --- a/impeller/entity/contents.h +++ b/impeller/entity/contents.h @@ -84,7 +84,7 @@ class SolidStrokeContents final : public Contents { public: SolidStrokeContents(); - ~SolidStrokeContents(); + ~SolidStrokeContents() override; void SetColor(Color color); diff --git a/impeller/entity/entity.cc b/impeller/entity/entity.cc index bc8d81aae49d7..77ab4ccd2769c 100644 --- a/impeller/entity/entity.cc +++ b/impeller/entity/entity.cc @@ -18,14 +18,6 @@ void Entity::SetTransformation(const Matrix& transformation) { transformation_ = transformation; } -const Color& Entity::GetBackgroundColor() const { - return background_color_; -} - -void Entity::SetBackgroundColor(const Color& backgroundColor) { - background_color_ = backgroundColor; -} - const Color& Entity::GetStrokeColor() const { return stroke_color_; } diff --git a/impeller/entity/entity.h b/impeller/entity/entity.h index bb4067927012b..4ee9bf2e26e53 100644 --- a/impeller/entity/entity.h +++ b/impeller/entity/entity.h @@ -23,10 +23,6 @@ class Entity { void SetTransformation(const Matrix& transformation); - const Color& GetBackgroundColor() const; - - void SetBackgroundColor(const Color& backgroundColor); - const Color& GetStrokeColor() const; void SetStrokeColor(const Color& strokeColor); @@ -49,9 +45,7 @@ class Entity { private: Matrix transformation_; - Color background_color_; std::shared_ptr contents_; - Path path_; Color stroke_color_; double stroke_size_ = 1.0; diff --git a/impeller/entity/entity_renderer.cc b/impeller/entity/entity_renderer.cc index 77e1369754c66..0b35bb5ca8216 100644 --- a/impeller/entity/entity_renderer.cc +++ b/impeller/entity/entity_renderer.cc @@ -37,9 +37,11 @@ bool EntityRenderer::RenderEntities(const Surface& surface, } for (const auto& entity : entities) { - if (auto contents = entity.GetContents(); - !contents->Render(*content_renderer_, entity, surface, onscreen_pass)) { - return false; + if (auto contents = entity.GetContents()) { + if (!contents->Render(*content_renderer_, entity, surface, + onscreen_pass)) { + return false; + } } } From 62f353a25ef8ee53b6afedeaa01a769d504b4ff4 Mon Sep 17 00:00:00 2001 From: Chinmay Garde Date: Mon, 15 Nov 2021 13:02:21 -0800 Subject: [PATCH 194/433] Start wiring up the entity playground. --- impeller/entity/BUILD.gn | 6 +++++- impeller/entity/contents.cc | 8 ++++++++ impeller/entity/contents.h | 2 ++ impeller/entity/entity_playground.cc | 28 ++++++++++++++++++++++++++++ impeller/entity/entity_playground.h | 28 ++++++++++++++++++++++++++++ impeller/entity/entity_unittests.cc | 5 ++++- impeller/playground/playground.h | 2 ++ impeller/renderer/pipeline_builder.h | 10 ++++++++-- 8 files changed, 85 insertions(+), 4 deletions(-) create mode 100644 impeller/entity/entity_playground.cc create mode 100644 impeller/entity/entity_playground.h diff --git a/impeller/entity/BUILD.gn b/impeller/entity/BUILD.gn index e4d368cc1ab97..0b35d51a7f6d9 100644 --- a/impeller/entity/BUILD.gn +++ b/impeller/entity/BUILD.gn @@ -38,7 +38,11 @@ impeller_component("entity") { impeller_component("entity_unittests") { testonly = true - sources = [ "entity_unittests.cc" ] + sources = [ + "entity_playground.cc", + "entity_playground.h", + "entity_unittests.cc", + ] deps = [ ":entity", diff --git a/impeller/entity/contents.cc b/impeller/entity/contents.cc index 7ce063119b669..3e27ea27bb1dc 100644 --- a/impeller/entity/contents.cc +++ b/impeller/entity/contents.cc @@ -4,6 +4,8 @@ #include "impeller/entity/contents.h" +#include + #include "flutter/fml/logging.h" #include "impeller/entity/content_renderer.h" #include "impeller/entity/entity.h" @@ -151,6 +153,12 @@ bool SolidColorContents::Render(const ContentRenderer& renderer, return true; } +std::unique_ptr SolidColorContents::Make(Color color) { + auto contents = std::make_unique(); + contents->SetColor(color); + return contents; +} + /******************************************************************************* ******* SolidStrokeContents ******************************************************************************/ diff --git a/impeller/entity/contents.h b/impeller/entity/contents.h index 2feedcfccf2f9..c4b8ac9c7540e 100644 --- a/impeller/entity/contents.h +++ b/impeller/entity/contents.h @@ -64,6 +64,8 @@ class SolidColorContents final : public Contents { ~SolidColorContents() override; + static std::unique_ptr Make(Color color); + void SetColor(Color color); const Color& GetColor() const; diff --git a/impeller/entity/entity_playground.cc b/impeller/entity/entity_playground.cc new file mode 100644 index 0000000000000..e643bd81488c7 --- /dev/null +++ b/impeller/entity/entity_playground.cc @@ -0,0 +1,28 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "impeller/entity/entity_playground.h" + +namespace impeller { + +EntityPlayground::EntityPlayground() = default; + +EntityPlayground::~EntityPlayground() = default; + +bool EntityPlayground::OpenPlaygroundHere(Entity entity) { + if (!renderer_) { + renderer_ = std::make_unique(GetContext()); + if (!renderer_) { + return false; + } + } + Renderer::RenderCallback callback = [&](const Surface& surface, + RenderPass& pass) -> bool { + std::vector entities = {entity}; + return renderer_->RenderEntities(surface, pass, entities); + }; + return Playground::OpenPlaygroundHere(callback); +} + +} // namespace impeller diff --git a/impeller/entity/entity_playground.h b/impeller/entity/entity_playground.h new file mode 100644 index 0000000000000..fdbc3c492a6c5 --- /dev/null +++ b/impeller/entity/entity_playground.h @@ -0,0 +1,28 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#pragma once + +#include "flutter/fml/macros.h" +#include "impeller/entity/entity.h" +#include "impeller/entity/entity_renderer.h" +#include "impeller/playground/playground.h" + +namespace impeller { + +class EntityPlayground : public Playground { + public: + EntityPlayground(); + + ~EntityPlayground(); + + bool OpenPlaygroundHere(Entity entity); + + private: + std::unique_ptr renderer_; + + FML_DISALLOW_COPY_AND_ASSIGN(EntityPlayground); +}; + +} // namespace impeller diff --git a/impeller/entity/entity_unittests.cc b/impeller/entity/entity_unittests.cc index 9a82998fc295b..b589d979f726e 100644 --- a/impeller/entity/entity_unittests.cc +++ b/impeller/entity/entity_unittests.cc @@ -4,13 +4,14 @@ #include "flutter/testing/testing.h" #include "impeller/entity/entity.h" +#include "impeller/entity/entity_playground.h" #include "impeller/geometry/path_builder.h" #include "impeller/playground/playground.h" namespace impeller { namespace testing { -using EntityTest = Playground; +using EntityTest = EntityPlayground; TEST_F(EntityTest, CanCreateEntity) { Entity entity; @@ -20,6 +21,8 @@ TEST_F(EntityTest, CanCreateEntity) { TEST_F(EntityTest, CanDrawRect) { Entity entity; entity.SetPath(PathBuilder{}.AddRect({100, 100, 100, 100}).CreatePath()); + entity.SetContents(SolidColorContents::Make(Color::Red())); + ASSERT_TRUE(OpenPlaygroundHere(entity)); } } // namespace testing diff --git a/impeller/playground/playground.h b/impeller/playground/playground.h index 6e11998cda78b..1d35b1c9b07e8 100644 --- a/impeller/playground/playground.h +++ b/impeller/playground/playground.h @@ -2,6 +2,8 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. +#pragma once + #include "flutter/fml/closure.h" #include "flutter/fml/macros.h" #include "gtest/gtest.h" diff --git a/impeller/renderer/pipeline_builder.h b/impeller/renderer/pipeline_builder.h index 8cc488c512d7d..89a7833995ebe 100644 --- a/impeller/renderer/pipeline_builder.h +++ b/impeller/renderer/pipeline_builder.h @@ -69,7 +69,11 @@ struct PipelineBuilder { FragmentShader::kEntrypointName, ShaderStage::kFragment); if (!vertex_function || !fragment_function) { - FML_LOG(ERROR) << "Could not resolve pipeline entrypoint(s)."; + FML_LOG(ERROR) << "Could not resolve pipeline entrypoint(s) '" + << VertexShader::kEntrypointName << "' and '" + << FragmentShader::kEntrypointName + << "' for pipline named '" << VertexShader::kLabel + << "'."; return false; } @@ -82,7 +86,9 @@ struct PipelineBuilder { auto vertex_descriptor = std::make_shared(); if (!vertex_descriptor->SetStageInputs( VertexShader::kAllShaderStageInputs)) { - FML_LOG(ERROR) << "Could not configure vertex descriptor."; + FML_LOG(ERROR) + << "Could not configure vertex descriptor for pipeline named '" + << VertexShader::kLabel << "'."; return false; } desc.SetVertexDescriptor(std::move(vertex_descriptor)); From ff6a2b7da93941fec540e3cadf9b57f08a834b74 Mon Sep 17 00:00:00 2001 From: Chinmay Garde Date: Mon, 15 Nov 2021 13:59:09 -0800 Subject: [PATCH 195/433] Allow shader libraries to look at multiple shader dylibs. --- impeller/playground/playground.mm | 14 ++++- impeller/renderer/backend/metal/context_mtl.h | 2 +- .../renderer/backend/metal/context_mtl.mm | 57 +++++++++++-------- .../backend/metal/shader_library_mtl.h | 9 ++- .../backend/metal/shader_library_mtl.mm | 28 +++++++-- impeller/renderer/shader_library.h | 2 + 6 files changed, 77 insertions(+), 35 deletions(-) diff --git a/impeller/playground/playground.mm b/impeller/playground/playground.mm index a0d9dcbeacf6f..dad34f622e110 100644 --- a/impeller/playground/playground.mm +++ b/impeller/playground/playground.mm @@ -35,9 +35,19 @@ return fml::paths::JoinPaths({path_result.second, "shaders"}); } +static std::vector ShaderLibraryPathsForPlayground() { + std::vector paths; + paths.emplace_back(fml::paths::JoinPaths( + {ShaderLibraryDirectory(), "shader_fixtures.metallib"})); + paths.emplace_back( + fml::paths::JoinPaths({fml::paths::GetExecutableDirectoryPath().second, + "shaders", "entity.metallib"})); + return paths; +} + Playground::Playground() - : renderer_(std::make_shared(ShaderLibraryDirectory(), - "shader_fixtures.metallib")) {} + : renderer_( + std::make_shared(ShaderLibraryPathsForPlayground())) {} Playground::~Playground() = default; diff --git a/impeller/renderer/backend/metal/context_mtl.h b/impeller/renderer/backend/metal/context_mtl.h index fc07f5f7b7a01..4572128c8be0b 100644 --- a/impeller/renderer/backend/metal/context_mtl.h +++ b/impeller/renderer/backend/metal/context_mtl.h @@ -22,7 +22,7 @@ namespace impeller { class ContextMTL final : public Context, public BackendCast { public: - ContextMTL(std::string shaders_directory, std::string main_library_file_name); + ContextMTL(const std::vector& shader_libraries); // |Context| ~ContextMTL() override; diff --git a/impeller/renderer/backend/metal/context_mtl.mm b/impeller/renderer/backend/metal/context_mtl.mm index c84c17cc1e9b6..19d91acb33197 100644 --- a/impeller/renderer/backend/metal/context_mtl.mm +++ b/impeller/renderer/backend/metal/context_mtl.mm @@ -4,6 +4,8 @@ #include "impeller/renderer/backend/metal/context_mtl.h" +#include + #include "flutter/fml/file.h" #include "flutter/fml/logging.h" #include "flutter/fml/paths.h" @@ -12,8 +14,30 @@ namespace impeller { -ContextMTL::ContextMTL(std::string shaders_directory, - std::string main_library_file_name) +static NSArray>* ShaderLibrariesFromFiles( + id device, + const std::vector& libraries_paths) { + NSMutableArray>* found_libraries = [NSMutableArray array]; + for (const auto& library_path : libraries_paths) { + if (!fml::IsFile(library_path)) { + FML_LOG(ERROR) << "Shader library does not exist at path '" + << library_path << "'"; + continue; + } + NSError* shader_library_error = nil; + auto library = [device newLibraryWithFile:@(library_path.c_str()) + error:&shader_library_error]; + if (!library) { + FML_LOG(ERROR) << "Could not create shader library: " + << shader_library_error.localizedDescription.UTF8String; + continue; + } + [found_libraries addObject:library]; + } + return found_libraries; +} + +ContextMTL::ContextMTL(const std::vector& libraries_paths) : device_(::MTLCreateSystemDefaultDevice()) { // Setup device. if (!device_) { @@ -33,31 +57,14 @@ // Setup the shader library. { - NSError* shader_library_error = nil; - auto shader_library_path = - fml::paths::JoinPaths({shaders_directory, main_library_file_name}); - - auto library_exists = fml::IsFile(shader_library_path); - - if (!library_exists) { - FML_LOG(ERROR) << "Shader library does not exist at path '" - << shader_library_path - << "'. No piplines can be created in this context."; - } - auto library = - library_exists - ? [device_ newLibraryWithFile:@(shader_library_path.c_str()) - error:&shader_library_error] - : [device_ newDefaultLibrary]; - if (!library && shader_library_error) { - FML_LOG(ERROR) << "Could not create shader library: " - << shader_library_error.localizedDescription.UTF8String; + // std::make_shared disallowed because of private friend ctor. + auto library = std::shared_ptr(new ShaderLibraryMTL( + ShaderLibrariesFromFiles(device_, libraries_paths))); + if (!library->IsValid()) { + FML_DLOG(ERROR) << "Could not create valid Metal shader library."; return; } - - // std::make_shared disallowed because of private friend ctor. - shader_library_ = - std::shared_ptr(new ShaderLibraryMTL(library)); + shader_library_ = std::move(library); } // Setup the pipeline library. diff --git a/impeller/renderer/backend/metal/shader_library_mtl.h b/impeller/renderer/backend/metal/shader_library_mtl.h index fe10dfd657b2f..b09b4cda33a1b 100644 --- a/impeller/renderer/backend/metal/shader_library_mtl.h +++ b/impeller/renderer/backend/metal/shader_library_mtl.h @@ -4,6 +4,7 @@ #pragma once +#include #include #include @@ -23,6 +24,9 @@ class ShaderLibraryMTL final : public ShaderLibrary { // |ShaderLibrary| ~ShaderLibraryMTL() override; + // |ShaderLibrary| + bool IsValid() const override; + private: friend class ContextMTL; @@ -53,10 +57,11 @@ class ShaderLibraryMTL final : public ShaderLibrary { ShaderKey::Equal>; UniqueID library_id_; - id library_ = nullptr; + NSArray>* libraries_ = nullptr; Functions functions_; + bool is_valid_ = false; - ShaderLibraryMTL(id library); + ShaderLibraryMTL(NSArray>* libraries); // |ShaderLibrary| std::shared_ptr GetFunction( diff --git a/impeller/renderer/backend/metal/shader_library_mtl.mm b/impeller/renderer/backend/metal/shader_library_mtl.mm index 5167e4cb7cd57..6844f4e463d6a 100644 --- a/impeller/renderer/backend/metal/shader_library_mtl.mm +++ b/impeller/renderer/backend/metal/shader_library_mtl.mm @@ -8,15 +8,25 @@ namespace impeller { -ShaderLibraryMTL::ShaderLibraryMTL(id library) - : library_(library) {} +ShaderLibraryMTL::ShaderLibraryMTL(NSArray>* libraries) + : libraries_(libraries) { + if (libraries_ == nil || libraries_.count == 0) { + return; + } + + is_valid_ = true; +} ShaderLibraryMTL::~ShaderLibraryMTL() = default; +bool ShaderLibraryMTL::IsValid() const { + return is_valid_; +} + std::shared_ptr ShaderLibraryMTL::GetFunction( const std::string_view& name, ShaderStage stage) { - if (!library_) { + if (!IsValid()) { return nullptr; } @@ -26,8 +36,16 @@ return found->second; } - auto function = [library_ newFunctionWithName:@(name.data())]; - if (!function) { + id function = nil; + + for (size_t i = 0, count = [libraries_ count]; i < count; i++) { + function = [libraries_[i] newFunctionWithName:@(name.data())]; + if (function) { + break; + } + } + + if (function == nil) { return nullptr; } diff --git a/impeller/renderer/shader_library.h b/impeller/renderer/shader_library.h index 357be584cfc91..256e614254813 100644 --- a/impeller/renderer/shader_library.h +++ b/impeller/renderer/shader_library.h @@ -19,6 +19,8 @@ class ShaderLibrary { public: virtual ~ShaderLibrary(); + virtual bool IsValid() const = 0; + virtual std::shared_ptr GetFunction( const std::string_view& name, ShaderStage stage) = 0; From 4271bad808584f0187f0903639ab6083c3943393 Mon Sep 17 00:00:00 2001 From: Chinmay Garde Date: Tue, 16 Nov 2021 15:21:25 -0800 Subject: [PATCH 196/433] Implement texture mapping. --- impeller/aiks/BUILD.gn | 7 +- impeller/aiks/aiks_playground.cc | 27 +++++++ impeller/aiks/aiks_playground.h | 25 ++++++ impeller/aiks/aiks_unittests.cc | 46 ++++++++++- impeller/aiks/canvas.cc | 40 ++++++++++ impeller/aiks/canvas.h | 9 +++ impeller/aiks/image.cc | 10 ++- impeller/aiks/image.h | 11 ++- impeller/entity/BUILD.gn | 2 + impeller/entity/content_renderer.cc | 9 +++ impeller/entity/content_renderer.h | 7 ++ impeller/entity/contents.cc | 96 +++++++++++++++++++++++ impeller/entity/contents.h | 30 +++++++ impeller/entity/entity_unittests.cc | 2 +- impeller/entity/shaders/texture_fill.frag | 13 +++ impeller/entity/shaders/texture_fill.vert | 17 ++++ impeller/geometry/path.cc | 37 +++++++-- impeller/geometry/point.h | 2 + impeller/geometry/rect.h | 12 ++- impeller/geometry/size.h | 2 +- impeller/renderer/command.cc | 4 - impeller/renderer/host_buffer.h | 43 ++++++++-- impeller/renderer/vertex_buffer_builder.h | 22 ++---- 23 files changed, 434 insertions(+), 39 deletions(-) create mode 100644 impeller/aiks/aiks_playground.cc create mode 100644 impeller/aiks/aiks_playground.h create mode 100644 impeller/entity/shaders/texture_fill.frag create mode 100644 impeller/entity/shaders/texture_fill.vert diff --git a/impeller/aiks/BUILD.gn b/impeller/aiks/BUILD.gn index ce098d2b4a791..09fde1849cdfa 100644 --- a/impeller/aiks/BUILD.gn +++ b/impeller/aiks/BUILD.gn @@ -31,10 +31,15 @@ impeller_component("aiks") { impeller_component("aiks_unittests") { testonly = true - sources = [ "aiks_unittests.cc" ] + sources = [ + "aiks_playground.cc", + "aiks_playground.h", + "aiks_unittests.cc", + ] deps = [ ":aiks", "../geometry:geometry_unittests", + "../playground", "//flutter/testing", ] } diff --git a/impeller/aiks/aiks_playground.cc b/impeller/aiks/aiks_playground.cc new file mode 100644 index 0000000000000..8541b193a6bfd --- /dev/null +++ b/impeller/aiks/aiks_playground.cc @@ -0,0 +1,27 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "impeller/aiks/aiks_playground.h" + +#include "impeller/aiks/picture_renderer.h" + +namespace impeller { + +AiksPlayground::AiksPlayground() = default; + +AiksPlayground::~AiksPlayground() = default; + +bool AiksPlayground::OpenPlaygroundHere(const Picture& picture) { + auto renderer = std::make_shared(GetContext()); + if (!renderer) { + return false; + } + + return Playground::OpenPlaygroundHere( + [renderer, &picture](const Surface& surface, RenderPass& pass) -> bool { + return renderer->Render(surface, pass, picture); + }); +} + +} // namespace impeller diff --git a/impeller/aiks/aiks_playground.h b/impeller/aiks/aiks_playground.h new file mode 100644 index 0000000000000..a77622b012ad9 --- /dev/null +++ b/impeller/aiks/aiks_playground.h @@ -0,0 +1,25 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#pragma once + +#include "flutter/fml/macros.h" +#include "impeller/aiks/picture.h" +#include "impeller/playground/playground.h" + +namespace impeller { + +class AiksPlayground : public Playground { + public: + AiksPlayground(); + + ~AiksPlayground(); + + bool OpenPlaygroundHere(const Picture& picture); + + private: + FML_DISALLOW_COPY_AND_ASSIGN(AiksPlayground); +}; + +} // namespace impeller diff --git a/impeller/aiks/aiks_unittests.cc b/impeller/aiks/aiks_unittests.cc index 5825cbc9ebc6b..d5fbae03dba62 100644 --- a/impeller/aiks/aiks_unittests.cc +++ b/impeller/aiks/aiks_unittests.cc @@ -3,13 +3,18 @@ // found in the LICENSE file. #include "flutter/testing/testing.h" +#include "impeller/aiks/aiks_playground.h" #include "impeller/aiks/canvas.h" +#include "impeller/aiks/image.h" #include "impeller/geometry/geometry_unittests.h" +#include "impeller/geometry/path_builder.h" namespace impeller { namespace testing { -TEST(AiksTest, CanvasCTMCanBeUpdated) { +using AiksTest = AiksPlayground; + +TEST_F(AiksTest, CanvasCTMCanBeUpdated) { Canvas canvas; Matrix identity; ASSERT_MATRIX_NEAR(canvas.GetCurrentTransformation(), identity); @@ -18,7 +23,7 @@ TEST(AiksTest, CanvasCTMCanBeUpdated) { Matrix::MakeTranslation({100.0, 100.0, 0.0})); } -TEST(AiksTest, CanvasCanPushPopCTM) { +TEST_F(AiksTest, CanvasCanPushPopCTM) { Canvas canvas; ASSERT_EQ(canvas.GetSaveCount(), 1u); ASSERT_EQ(canvas.Restore(), false); @@ -34,5 +39,42 @@ TEST(AiksTest, CanvasCanPushPopCTM) { Matrix::MakeTranslation({100.0, 100.0, 0.0})); } +TEST_F(AiksTest, CanRenderColoredRect) { + Canvas canvas; + Paint paint; + paint.color = Color::Red(); + canvas.DrawPath(PathBuilder{} + .AddRect(Rect::MakeXYWH(100.0, 100.0, 100.0, 100.0)) + .CreatePath(), + paint); + // ASSERT_TRUE(OpenPlaygroundHere(canvas.EndRecordingAsPicture())); +} + +TEST_F(AiksTest, CanRenderImage) { + Canvas canvas; + Paint paint; + auto image = std::make_shared(CreateTextureForFixture("kalimba.jpg")); + paint.color = Color::Red(); + canvas.DrawImage(image, Point::MakeXY(100.0, 100.0), paint); + ASSERT_TRUE(OpenPlaygroundHere(canvas.EndRecordingAsPicture())); +} + +TEST_F(AiksTest, CanRenderImageRect) { + Canvas canvas; + Paint paint; + auto image = std::make_shared(CreateTextureForFixture("kalimba.jpg")); + auto source_rect = IRect::MakeSize(image->GetSize()); + + // Render the bottom right quarter of the source image in a stretched rect. + source_rect.size.width /= 2; + source_rect.size.height /= 2; + source_rect.origin.x += source_rect.size.width; + source_rect.origin.y += source_rect.size.height; + + canvas.DrawImageRect(image, source_rect, Rect::MakeXYWH(100, 100, 600, 600), + paint); + // ASSERT_TRUE(OpenPlaygroundHere(canvas.EndRecordingAsPicture())); +} + } // namespace testing } // namespace impeller diff --git a/impeller/aiks/canvas.cc b/impeller/aiks/canvas.cc index b8a4b052a8a89..e52c2b7c43d41 100644 --- a/impeller/aiks/canvas.cc +++ b/impeller/aiks/canvas.cc @@ -7,6 +7,7 @@ #include #include "flutter/fml/logging.h" +#include "impeller/geometry/path_builder.h" namespace impeller { @@ -98,6 +99,45 @@ void Canvas::DrawPicture(const Picture& picture) { } } +void Canvas::DrawImage(std::shared_ptr image, + Point offset, + Paint paint) { + if (!image) { + return; + } + + const auto source = IRect::MakeSize(image->GetSize()); + const auto dest = + Rect::MakeXYWH(offset.x, offset.y, source.size.width, source.size.height); + + DrawImageRect(image, source, dest, std::move(paint)); +} + +void Canvas::DrawImageRect(std::shared_ptr image, + IRect source, + Rect dest, + Paint paint) { + if (!image || source.size.IsEmpty() || dest.size.IsEmpty()) { + return; + } + + auto size = image->GetSize(); + + if (size.IsEmpty()) { + return; + } + + auto contents = std::make_shared(); + contents->SetTexture(image->GetTexture()); + contents->SetSourceRect(source); + + Entity entity; + entity.SetPath(PathBuilder{}.AddRect(dest).CreatePath()); + entity.SetContents(contents); + entity.SetTransformation(GetCurrentTransformation()); + GetCurrentPass().PushEntity(std::move(entity)); +} + Picture Canvas::EndRecordingAsPicture() { Picture picture; picture.passes = std::move(passes_); diff --git a/impeller/aiks/canvas.h b/impeller/aiks/canvas.h index 4dedd3224160c..47a84d2aafd02 100644 --- a/impeller/aiks/canvas.h +++ b/impeller/aiks/canvas.h @@ -11,10 +11,12 @@ #include "flutter/fml/macros.h" #include "impeller/aiks/canvas_pass.h" +#include "impeller/aiks/image.h" #include "impeller/aiks/paint.h" #include "impeller/aiks/picture.h" #include "impeller/geometry/matrix.h" #include "impeller/geometry/path.h" +#include "impeller/geometry/point.h" #include "impeller/geometry/vector.h" namespace impeller { @@ -49,6 +51,13 @@ class Canvas { void DrawPath(Path path, Paint paint); + void DrawImage(std::shared_ptr image, Point offset, Paint paint); + + void DrawImageRect(std::shared_ptr image, + IRect source, + Rect dest, + Paint paint); + void ClipPath(Path path); void DrawShadow(Path path, Color color, Scalar elevation); diff --git a/impeller/aiks/image.cc b/impeller/aiks/image.cc index bb1ea3a81218c..9e0c8fdf8fee6 100644 --- a/impeller/aiks/image.cc +++ b/impeller/aiks/image.cc @@ -6,8 +6,16 @@ namespace impeller { -Image::Image() = default; +Image::Image(std::shared_ptr texture) : texture_(std::move(texture)) {} Image::~Image() = default; +ISize Image::GetSize() const { + return texture_ ? texture_->GetSize() : ISize{}; +} + +std::shared_ptr Image::GetTexture() const { + return texture_; +} + } // namespace impeller diff --git a/impeller/aiks/image.h b/impeller/aiks/image.h index 48a2bc2ed6f21..aa124f2ed7dd1 100644 --- a/impeller/aiks/image.h +++ b/impeller/aiks/image.h @@ -4,17 +4,26 @@ #pragma once +#include + #include "flutter/fml/macros.h" +#include "impeller/renderer/texture.h" namespace impeller { class Image { public: - Image(); + Image(std::shared_ptr texture); ~Image(); + ISize GetSize() const; + + std::shared_ptr GetTexture() const; + private: + const std::shared_ptr texture_; + FML_DISALLOW_COPY_AND_ASSIGN(Image); }; diff --git a/impeller/entity/BUILD.gn b/impeller/entity/BUILD.gn index 0b35d51a7f6d9..b465c0cdd5d31 100644 --- a/impeller/entity/BUILD.gn +++ b/impeller/entity/BUILD.gn @@ -12,6 +12,8 @@ impeller_shaders("entity_shaders") { "shaders/gradient_fill.vert", "shaders/solid_fill.frag", "shaders/solid_fill.vert", + "shaders/texture_fill.frag", + "shaders/texture_fill.vert", ] } diff --git a/impeller/entity/content_renderer.cc b/impeller/entity/content_renderer.cc index d798d62f54c62..6ea7fb76e4248 100644 --- a/impeller/entity/content_renderer.cc +++ b/impeller/entity/content_renderer.cc @@ -14,6 +14,7 @@ ContentRenderer::ContentRenderer(std::shared_ptr context) gradient_fill_pipeline_ = std::make_unique(*context_); solid_fill_pipeline_ = std::make_unique(*context_); + texture_pipeline_ = std::make_unique(*context_); is_valid_ = true; } @@ -43,4 +44,12 @@ std::shared_ptr ContentRenderer::GetSolidFillPipeline() const { return solid_fill_pipeline_->WaitAndGet(); } +std::shared_ptr ContentRenderer::GetTexturePipeline() const { + if (!IsValid()) { + return nullptr; + } + + return texture_pipeline_->WaitAndGet(); +} + } // namespace impeller diff --git a/impeller/entity/content_renderer.h b/impeller/entity/content_renderer.h index 77869bbcf35dc..997655329b5df 100644 --- a/impeller/entity/content_renderer.h +++ b/impeller/entity/content_renderer.h @@ -11,6 +11,8 @@ #include "flutter/impeller/entity/gradient_fill.vert.h" #include "flutter/impeller/entity/solid_fill.frag.h" #include "flutter/impeller/entity/solid_fill.vert.h" +#include "flutter/impeller/entity/texture_fill.frag.h" +#include "flutter/impeller/entity/texture_fill.vert.h" #include "impeller/renderer/pipeline.h" namespace impeller { @@ -19,6 +21,8 @@ using GradientFillPipeline = PipelineT; using SolidFillPipeline = PipelineT; +using TexturePipeline = + PipelineT; class ContentRenderer { public: @@ -32,12 +36,15 @@ class ContentRenderer { std::shared_ptr GetSolidFillPipeline() const; + std::shared_ptr GetTexturePipeline() const; + std::shared_ptr GetContext() const; private: std::shared_ptr context_; std::unique_ptr gradient_fill_pipeline_; std::unique_ptr solid_fill_pipeline_; + std::unique_ptr texture_pipeline_; bool is_valid_ = false; FML_DISALLOW_COPY_AND_ASSIGN(ContentRenderer); diff --git a/impeller/entity/contents.cc b/impeller/entity/contents.cc index 3e27ea27bb1dc..24edc049c8723 100644 --- a/impeller/entity/contents.cc +++ b/impeller/entity/contents.cc @@ -9,7 +9,9 @@ #include "flutter/fml/logging.h" #include "impeller/entity/content_renderer.h" #include "impeller/entity/entity.h" +#include "impeller/geometry/path_builder.h" #include "impeller/renderer/render_pass.h" +#include "impeller/renderer/sampler_library.h" #include "impeller/renderer/surface.h" #include "impeller/renderer/tessellator.h" #include "impeller/renderer/vertex_buffer_builder.h" @@ -135,6 +137,10 @@ bool SolidColorContents::Render(const ContentRenderer& renderer, } } + if (!vtx_builder.HasVertices()) { + return true; + } + cmd.BindVertices(vtx_builder.CreateVertexBuffer( *renderer.GetContext()->GetPermanentsAllocator())); @@ -171,4 +177,94 @@ const Color& SolidStrokeContents::GetColor() const { return color_; } +/******************************************************************************* + ******* TextureContents + ******************************************************************************/ + +TextureContents::TextureContents() = default; + +TextureContents::~TextureContents() = default; + +void TextureContents::SetTexture(std::shared_ptr texture) { + texture_ = std::move(texture); +} + +std::shared_ptr TextureContents::GetTexture() const { + return texture_; +} + +bool TextureContents::Render(const ContentRenderer& renderer, + const Entity& entity, + const Surface& surface, + RenderPass& pass) const { + if (texture_ == nullptr) { + return true; + } + + using VS = TextureFillVertexShader; + using FS = TextureFillFragmentShader; + + const auto coverage_rect = entity.GetPath().GetBoundingBox(); + if (coverage_rect.size.IsEmpty()) { + return true; + } + + const auto texture_size = texture_->GetSize(); + if (texture_size.IsEmpty()) { + return true; + } + + if (source_rect_.IsEmpty()) { + return true; + } + + VertexBufferBuilder vertex_builder; + { + const auto tess_result = Tessellator{}.Tessellate( + entity.GetPath().CreatePolyline(), + [&vertex_builder, &coverage_rect](Point vtx) { + VS::PerVertexData data; + data.vertices = vtx; + data.texture_coords = + ((vtx - coverage_rect.origin) / coverage_rect.size); + vertex_builder.AppendVertex(data); + }); + if (!tess_result) { + return false; + } + } + + if (!vertex_builder.HasVertices()) { + return true; + } + + auto& host_buffer = pass.GetTransientsBuffer(); + + VS::FrameInfo frame_info; + frame_info.mvp = + Matrix::MakeOrthographic(surface.GetSize()) * entity.GetTransformation(); + + auto frame_info_view = host_buffer.EmplaceUniform(frame_info); + + Command cmd; + cmd.label = "TextureFill"; + cmd.pipeline = renderer.GetTexturePipeline(); + cmd.BindVertices(vertex_builder.CreateVertexBuffer(host_buffer)); + VS::BindFrameInfo(cmd, frame_info_view); + FS::BindTextureSampler( + cmd, texture_, + renderer.GetContext()->GetSamplerLibrary()->GetSampler({})); + pass.AddCommand(std::move(cmd)); + + return true; +} + +void TextureContents::SetSourceRect(const IRect& source_rect) { + source_rect_ = source_rect; +} + +const IRect& TextureContents::GetSourceRect() const { + return source_rect_; +} + } // namespace impeller diff --git a/impeller/entity/contents.h b/impeller/entity/contents.h index c4b8ac9c7540e..3e3b9ecc3f8af 100644 --- a/impeller/entity/contents.h +++ b/impeller/entity/contents.h @@ -4,11 +4,14 @@ #pragma once +#include #include #include "flutter/fml/macros.h" #include "impeller/geometry/color.h" #include "impeller/geometry/point.h" +#include "impeller/geometry/rect.h" +#include "impeller/renderer/texture.h" namespace impeller { @@ -82,6 +85,33 @@ class SolidColorContents final : public Contents { FML_DISALLOW_COPY_AND_ASSIGN(SolidColorContents); }; +class TextureContents final : public Contents { + public: + TextureContents(); + + ~TextureContents() override; + + void SetTexture(std::shared_ptr texture); + + std::shared_ptr GetTexture() const; + + void SetSourceRect(const IRect& source_rect); + + const IRect& GetSourceRect() const; + + // |Contents| + bool Render(const ContentRenderer& renderer, + const Entity& entity, + const Surface& surface, + RenderPass& pass) const override; + + public: + std::shared_ptr texture_; + IRect source_rect_; + + FML_DISALLOW_COPY_AND_ASSIGN(TextureContents); +}; + class SolidStrokeContents final : public Contents { public: SolidStrokeContents(); diff --git a/impeller/entity/entity_unittests.cc b/impeller/entity/entity_unittests.cc index b589d979f726e..c76fb2d249fcc 100644 --- a/impeller/entity/entity_unittests.cc +++ b/impeller/entity/entity_unittests.cc @@ -22,7 +22,7 @@ TEST_F(EntityTest, CanDrawRect) { Entity entity; entity.SetPath(PathBuilder{}.AddRect({100, 100, 100, 100}).CreatePath()); entity.SetContents(SolidColorContents::Make(Color::Red())); - ASSERT_TRUE(OpenPlaygroundHere(entity)); + // ASSERT_TRUE(OpenPlaygroundHere(entity)); } } // namespace testing diff --git a/impeller/entity/shaders/texture_fill.frag b/impeller/entity/shaders/texture_fill.frag new file mode 100644 index 0000000000000..8c12a8be15fb7 --- /dev/null +++ b/impeller/entity/shaders/texture_fill.frag @@ -0,0 +1,13 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +uniform sampler2D texture_sampler; + +in vec2 v_texture_coords; + +out vec4 frag_color; + +void main() { + frag_color = texture(texture_sampler, v_texture_coords); +} diff --git a/impeller/entity/shaders/texture_fill.vert b/impeller/entity/shaders/texture_fill.vert new file mode 100644 index 0000000000000..daa30f5650a3f --- /dev/null +++ b/impeller/entity/shaders/texture_fill.vert @@ -0,0 +1,17 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +uniform FrameInfo { + mat4 mvp; +} frame_info; + +in vec2 vertices; +in vec2 texture_coords; + +out vec2 v_texture_coords; + +void main() { + gl_Position = frame_info.mvp * vec4(vertices, 0.0, 1.0); + v_texture_coords = texture_coords; +} diff --git a/impeller/geometry/path.cc b/impeller/geometry/path.cc index 2eb79b1a0b656..facb85173cef7 100644 --- a/impeller/geometry/path.cc +++ b/impeller/geometry/path.cc @@ -2,7 +2,9 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -#include "path.h" +#include "impeller/geometry/path.h" + +#include namespace impeller { @@ -172,21 +174,44 @@ std::vector Path::CreatePolyline( } Rect Path::GetBoundingBox() const { - Rect box; + if (linears_.empty() && quads_.empty() && cubics_.empty()) { + return {}; + } + + std::optional min, max; + + auto clamp = [&min, &max](const std::vector& extrema) { + for (const auto& extremum : extrema) { + if (!min.has_value()) { + min = extremum; + } + + if (!max.has_value()) { + max = extremum; + } + + min->x = std::min(min->x, extremum.x); + min->y = std::min(min->y, extremum.y); + max->x = std::max(max->x, extremum.x); + max->y = std::max(max->y, extremum.y); + } + }; for (const auto& linear : linears_) { - box = box.WithPoints(linear.Extrema()); + clamp(linear.Extrema()); } for (const auto& quad : quads_) { - box = box.WithPoints(quad.Extrema()); + clamp(quad.Extrema()); } for (const auto& cubic : cubics_) { - box = box.WithPoints(cubic.Extrema()); + clamp(cubic.Extrema()); } - return box; + const auto difference = *max - *min; + + return {min->x, min->y, difference.x, difference.y}; } } // namespace impeller diff --git a/impeller/geometry/point.h b/impeller/geometry/point.h index ec3e4b5adc888..6270f1714eb56 100644 --- a/impeller/geometry/point.h +++ b/impeller/geometry/point.h @@ -27,6 +27,8 @@ struct TPoint { constexpr TPoint(Type x, Type y) : x(x), y(y) {} + static constexpr TPoint MakeXY(Type x, Type y) { return {x, y}; } + constexpr bool operator==(const TPoint& p) const { return p.x == x && p.y == y; } diff --git a/impeller/geometry/rect.h b/impeller/geometry/rect.h index deebcfccb853c..4fcd69d15abf4 100644 --- a/impeller/geometry/rect.h +++ b/impeller/geometry/rect.h @@ -19,7 +19,7 @@ struct TRect { TPoint origin; TSize size; - constexpr TRect() : origin({0.0, 0.0}), size({0.0, 0.0}) {} + constexpr TRect() : origin({0, 0}), size({0, 0}) {} constexpr TRect(TSize size) : origin({0.0, 0.0}), size(size) {} @@ -40,6 +40,14 @@ struct TRect { return TRect(left, top, right - left, bottom - top); } + constexpr static TRect MakeXYWH(Type x, Type y, Type width, Type height) { + return TRect(x, y, width, height); + } + + constexpr static TRect MakeSize(const TSize& size) { + return TRect(0.0, 0.0, size.width, size.height); + } + template constexpr explicit TRect(const TRect& other) : origin(static_cast>(other.origin)), @@ -76,6 +84,8 @@ struct TRect { constexpr bool IsZero() const { return size.IsZero(); } + constexpr bool IsEmpty() const { return size.IsEmpty(); } + constexpr TRect WithPoint(const TPoint& p) const { TRect copy = *this; if (p.x < origin.x) { diff --git a/impeller/geometry/size.h b/impeller/geometry/size.h index 2f0957660cd2e..62f4529b02940 100644 --- a/impeller/geometry/size.h +++ b/impeller/geometry/size.h @@ -73,7 +73,7 @@ struct TSize { constexpr bool IsPositive() const { return width * height > 0.0; } - constexpr bool IsEmpty() { return !IsPositive(); } + constexpr bool IsEmpty() const { return !IsPositive(); } constexpr size_t MipCount() const { if (!IsPositive()) { diff --git a/impeller/renderer/command.cc b/impeller/renderer/command.cc index ff4febc421f78..aca7d2b31cecb 100644 --- a/impeller/renderer/command.cc +++ b/impeller/renderer/command.cc @@ -43,8 +43,6 @@ bool Command::BindResource(ShaderStage stage, } if (!slot.HasTexture()) { - FML_DLOG(WARNING) << "Texture named " << slot.name - << " is not actually used in the shader."; return true; } @@ -70,8 +68,6 @@ bool Command::BindResource(ShaderStage stage, } if (!slot.HasSampler()) { - FML_DLOG(WARNING) << "Sampler named " << slot.name - << " is not actually used in the shader."; return true; } diff --git a/impeller/renderer/host_buffer.h b/impeller/renderer/host_buffer.h index 7edb747b7e556..82be86cbcba0f 100644 --- a/impeller/renderer/host_buffer.h +++ b/impeller/renderer/host_buffer.h @@ -28,15 +28,44 @@ class HostBuffer final : public std::enable_shared_from_this, void SetLabel(std::string label); - template >> - [[nodiscard]] BufferView EmplaceUniform(const T& t) { - return Emplace(reinterpret_cast(&t), sizeof(T), - std::max(alignof(T), DefaultUniformAlignment())); + //---------------------------------------------------------------------------- + /// @brief Emplace uniform data onto the host buffer. Ensure that backend + /// specific uniform alignment requirements are respected. + /// + /// @param[in] uniform The uniform struct to emplace onto the buffer. + /// + /// @tparam UniformType The type of the uniform struct. + /// + /// @return The buffer view. + /// + template >> + [[nodiscard]] BufferView EmplaceUniform(const UniformType& uniform) { + const auto alignment = + std::max(alignof(UniformType), DefaultUniformAlignment()); + return Emplace(reinterpret_cast(&uniform), // buffer + sizeof(UniformType), // size + alignment // alignment + ); } - template >> - [[nodiscard]] BufferView Emplace(const T& t) { - return Emplace(reinterpret_cast(&t), sizeof(T), alignof(T)); + //---------------------------------------------------------------------------- + /// @brief Emplace non-uniform data (like contiguous vertices) onto the + /// host buffer. + /// + /// @param[in] buffer The buffer data. + /// + /// @tparam BufferType The type of the buffer data. + /// + /// @return The buffer view. + /// + template >> + [[nodiscard]] BufferView Emplace(const BufferType& buffer) { + return Emplace(reinterpret_cast(&buffer), // buffer + sizeof(BufferType), // size + alignof(BufferType) // alignment + ); } [[nodiscard]] BufferView Emplace(const void* buffer, diff --git a/impeller/renderer/vertex_buffer_builder.h b/impeller/renderer/vertex_buffer_builder.h index d9b0e36d34778..0d92e2debe720 100644 --- a/impeller/renderer/vertex_buffer_builder.h +++ b/impeller/renderer/vertex_buffer_builder.h @@ -33,6 +33,8 @@ class VertexBufferBuilder { void Reserve(size_t count) { return vertices_.reserve(count); } + bool HasVertices() const { return !vertices_.empty(); } + VertexBufferBuilder& AppendVertex(VertexType_ vertex) { vertices_.emplace_back(std::move(vertex)); return *this; @@ -71,13 +73,9 @@ class VertexBufferBuilder { std::string label_; BufferView CreateVertexBufferView(HostBuffer& buffer) const { - auto view = - buffer.Emplace(vertices_.data(), vertices_.size() * sizeof(VertexType), - alignof(VertexType)); - if (!label_.empty()) { - view.SetLabel(SPrintF("%s Vertices"), label_.c_str()); - } - return view; + return buffer.Emplace(vertices_.data(), + vertices_.size() * sizeof(VertexType), + alignof(VertexType)); } BufferView CreateVertexBufferView(Allocator& allocator) const { @@ -105,13 +103,9 @@ class VertexBufferBuilder { BufferView CreateIndexBufferView(HostBuffer& buffer) const { const auto index_buffer = CreateIndexBuffer(); - auto view = buffer.Emplace(index_buffer.data(), - index_buffer.size() * sizeof(IndexType), - alignof(IndexType)); - if (!label_.empty()) { - view.SetLabel(SPrintF("%s Indices"), label_); - } - return view; + return buffer.Emplace(index_buffer.data(), + index_buffer.size() * sizeof(IndexType), + alignof(IndexType)); } BufferView CreateIndexBufferView(Allocator& allocator) const { From ff402844265336ac9542c879898652c085229779 Mon Sep 17 00:00:00 2001 From: Chinmay Garde Date: Tue, 16 Nov 2021 15:58:29 -0800 Subject: [PATCH 197/433] WIP on stroke rendering. --- impeller/aiks/aiks_unittests.cc | 13 +++++++-- impeller/aiks/paint.cc | 16 ++++++++--- impeller/aiks/paint.h | 9 +++++- impeller/entity/contents.cc | 51 +++++++++++++++++++++++++-------- impeller/entity/contents.h | 4 +-- 5 files changed, 72 insertions(+), 21 deletions(-) diff --git a/impeller/aiks/aiks_unittests.cc b/impeller/aiks/aiks_unittests.cc index d5fbae03dba62..d0135aa86833e 100644 --- a/impeller/aiks/aiks_unittests.cc +++ b/impeller/aiks/aiks_unittests.cc @@ -56,10 +56,10 @@ TEST_F(AiksTest, CanRenderImage) { auto image = std::make_shared(CreateTextureForFixture("kalimba.jpg")); paint.color = Color::Red(); canvas.DrawImage(image, Point::MakeXY(100.0, 100.0), paint); - ASSERT_TRUE(OpenPlaygroundHere(canvas.EndRecordingAsPicture())); + // ASSERT_TRUE(OpenPlaygroundHere(canvas.EndRecordingAsPicture())); } -TEST_F(AiksTest, CanRenderImageRect) { +TEST_F(AiksTest, DISABLED_CanRenderImageRect) { Canvas canvas; Paint paint; auto image = std::make_shared(CreateTextureForFixture("kalimba.jpg")); @@ -76,5 +76,14 @@ TEST_F(AiksTest, CanRenderImageRect) { // ASSERT_TRUE(OpenPlaygroundHere(canvas.EndRecordingAsPicture())); } +TEST_F(AiksTest, CanRenderStrokes) { + Canvas canvas; + Paint paint; + paint.color = Color::Red(); + paint.stroke_width = 20.0; + canvas.DrawPath(PathBuilder{}.AddCircle({500, 500}, 250).CreatePath(), paint); + ASSERT_TRUE(OpenPlaygroundHere(canvas.EndRecordingAsPicture())); +} + } // namespace testing } // namespace impeller diff --git a/impeller/aiks/paint.cc b/impeller/aiks/paint.cc index e45f9a76458c4..ca1f6169f212d 100644 --- a/impeller/aiks/paint.cc +++ b/impeller/aiks/paint.cc @@ -7,10 +7,18 @@ namespace impeller { std::shared_ptr Paint::CreateContentsForEntity() const { - if (!color.IsTransparent()) { - auto solid_color = std::make_shared(); - solid_color->SetColor(color); - return solid_color; + switch (style) { + case Style::kFill: { + auto solid_color = std::make_shared(); + solid_color->SetColor(color); + return solid_color; + } + case Style::kStroke: { + auto solid_stroke = std::make_shared(); + solid_stroke->SetColor(color); + solid_stroke->SetStrokeSize(stroke_width); + return solid_stroke; + } } return nullptr; diff --git a/impeller/aiks/paint.h b/impeller/aiks/paint.h index 7f59475fcee58..98cb1ce1cac49 100644 --- a/impeller/aiks/paint.h +++ b/impeller/aiks/paint.h @@ -4,16 +4,23 @@ #pragma once -#include "flutter/fml/macros.h" +#include +#include "flutter/fml/macros.h" #include "impeller/entity/contents.h" #include "impeller/geometry/color.h" namespace impeller { struct Paint { + enum class Style { + kFill, + kStroke, + }; + Color color; Scalar stroke_width = 0.0; + Style style = Style::kFill; std::shared_ptr CreateContentsForEntity() const; }; diff --git a/impeller/entity/contents.cc b/impeller/entity/contents.cc index 24edc049c8723..e99e442c783c1 100644 --- a/impeller/entity/contents.cc +++ b/impeller/entity/contents.cc @@ -115,6 +115,10 @@ bool SolidColorContents::Render(const ContentRenderer& renderer, const Entity& entity, const Surface& surface, RenderPass& pass) const { + if (color_.IsTransparent()) { + return true; + } + using VS = SolidFillPipeline::VertexShader; Command cmd; @@ -165,18 +169,6 @@ std::unique_ptr SolidColorContents::Make(Color color) { return contents; } -/******************************************************************************* - ******* SolidStrokeContents - ******************************************************************************/ - -void SolidStrokeContents::SetColor(Color color) { - color_ = color; -} - -const Color& SolidStrokeContents::GetColor() const { - return color_; -} - /******************************************************************************* ******* TextureContents ******************************************************************************/ @@ -267,4 +259,39 @@ const IRect& TextureContents::GetSourceRect() const { return source_rect_; } +/******************************************************************************* + ******* SolidStrokeContents + ******************************************************************************/ + +SolidStrokeContents::SolidStrokeContents() = default; + +SolidStrokeContents::~SolidStrokeContents() = default; + +void SolidStrokeContents::SetColor(Color color) { + color_ = color; +} + +const Color& SolidStrokeContents::GetColor() const { + return color_; +} + +bool SolidStrokeContents::Render(const ContentRenderer& renderer, + const Entity& entity, + const Surface& surface, + RenderPass& pass) const { + if (color_.IsTransparent() || stroke_size_ <= 0.0) { + return true; + } + + return false; +} + +void SolidStrokeContents::SetStrokeSize(Scalar size) { + stroke_size_ = size; +} + +Scalar SolidStrokeContents::GetStrokeSize() const { + return stroke_size_; +} + } // namespace impeller diff --git a/impeller/entity/contents.h b/impeller/entity/contents.h index 3e3b9ecc3f8af..855ec0adfd881 100644 --- a/impeller/entity/contents.h +++ b/impeller/entity/contents.h @@ -122,9 +122,9 @@ class SolidStrokeContents final : public Contents { const Color& GetColor() const; - void SetStrokeSize(Scalar size) { stroke_size_ = size; } + void SetStrokeSize(Scalar size); - Scalar GetStrokeSize() const { return stroke_size_; } + Scalar GetStrokeSize() const; // |Contents| bool Render(const ContentRenderer& renderer, From 89952c25f13c23bd7b61b3731cade726a1452610 Mon Sep 17 00:00:00 2001 From: Chinmay Garde Date: Wed, 17 Nov 2021 16:02:00 -0800 Subject: [PATCH 198/433] Insert additional padding at the end of the struct if the size of the struct does not satisfy the alignment requirements of all its members. --- impeller/compiler/code_gen_template.h | 8 +++-- impeller/compiler/reflector.cc | 51 ++++++++++++++++++++++++--- impeller/compiler/reflector.h | 14 ++++---- 3 files changed, 59 insertions(+), 14 deletions(-) diff --git a/impeller/compiler/code_gen_template.h b/impeller/compiler/code_gen_template.h index 7eef094987810..20afb9e2714f6 100644 --- a/impeller/compiler/code_gen_template.h +++ b/impeller/compiler/code_gen_template.h @@ -39,12 +39,13 @@ struct {{camel_case(shader_name)}}{{camel_case(shader_stage)}}Shader { struct {{def.name}} { {% for member in def.members %} - {{member.type}} {{member.name}}; + {{member.type}} {{member.name}}; // (offset {{member.offset}}, size {{member.byte_length}}) {% endfor %} - }; // struct {{def.name}} + }; // struct {{def.name}} (size {{def.byte_length}}) {% endfor %} {% endif %} {% if length(uniform_buffers) > 0 %} + // =========================================================================== // Stage Uniforms ============================================================ // =========================================================================== @@ -52,10 +53,11 @@ struct {{camel_case(shader_name)}}{{camel_case(shader_stage)}}Shader { static constexpr auto kResource{{camel_case(uniform.name)}} = ShaderUniformSlot<{{uniform.name}}> { // {{uniform.name}} "{{uniform.name}}", // name - {{uniform.binding}}u, // binding + {{uniform.msl_res_0}}u, // binding }; {% endfor %} {% endif %} + // =========================================================================== // Stage Inputs ============================================================== // =========================================================================== diff --git a/impeller/compiler/reflector.cc b/impeller/compiler/reflector.cc index e97d7f7c39021..b44dbd45fc490 100644 --- a/impeller/compiler/reflector.cc +++ b/impeller/compiler/reflector.cc @@ -372,7 +372,28 @@ static std::optional ReadKnownScalarType( return std::nullopt; } -std::vector Reflector::ReadStructMembers( +//------------------------------------------------------------------------------ +/// @brief Get the reflected struct size. In the vast majority of the +/// cases, this is the same as the declared struct size as given by +/// the compiler. But, additional padding may need to be introduced +/// after the end of the struct to keep in line with the alignment +/// requirement of the individual struct members. This method +/// figures out the actual size of the reflected struct that can be +/// referenced in native code. +/// +/// @param[in] members The members +/// +/// @return The reflected structure size. +/// +static size_t GetReflectedStructSize(const std::vector& members) { + auto struct_size = 0u; + for (const auto& member : members) { + struct_size += member.byte_length; + } + return struct_size; +} + +std::vector Reflector::ReadStructMembers( const spirv_cross::TypeID& type_id) const { const auto& struct_type = compiler_->get_type(type_id); FML_CHECK(struct_type.basetype == spirv_cross::SPIRType::BaseType::Struct); @@ -380,6 +401,7 @@ std::vector Reflector::ReadStructMembers( std::vector result; size_t current_byte_offset = 0; + size_t max_member_alignment = 0; for (size_t i = 0; i < struct_type.member_types.size(); i++) { const auto& member = compiler_->get_type(struct_type.member_types[i]); @@ -390,7 +412,7 @@ std::vector Reflector::ReadStructMembers( const auto alignment_pad = struct_member_offset - current_byte_offset; result.emplace_back(StructMember{ .type = TypeNameWithPaddingOfSize(alignment_pad), - .name = SPrintF("_align_%s", + .name = SPrintF("_PADDING_%s_", GetMemberNameAtIndex(struct_type, i).c_str()), .offset = current_byte_offset, .byte_length = alignment_pad, @@ -398,6 +420,10 @@ std::vector Reflector::ReadStructMembers( current_byte_offset += alignment_pad; } + max_member_alignment = + std::max(max_member_alignment, + (member.width / 8) * member.columns * member.vecsize); + FML_CHECK(current_byte_offset == struct_member_offset); // Tightly packed 4x4 Matrix is special cased as we know how to work with @@ -499,6 +525,20 @@ std::vector Reflector::ReadStructMembers( continue; } } + + const auto struct_length = current_byte_offset; + { + const auto padding = struct_length % max_member_alignment; + if (padding != 0) { + result.emplace_back(StructMember{ + .type = TypeNameWithPaddingOfSize(padding), + .name = "_PADDING_", + .offset = current_byte_offset, + .byte_length = padding, + }); + } + } + return result; } @@ -514,10 +554,13 @@ std::optional Reflector::ReflectStructDefinition( return std::nullopt; } + auto struct_members = ReadStructMembers(type_id); + auto reflected_struct_size = GetReflectedStructSize(struct_members); + StructDefinition struc; struc.name = struct_name; - struc.byte_length = compiler_->get_declared_struct_size(type); - struc.members = ReadStructMembers(type_id); + struc.byte_length = reflected_struct_size; + struc.members = std::move(struct_members); return struc; } diff --git a/impeller/compiler/reflector.h b/impeller/compiler/reflector.h index b71a84aad5f18..302b26456c690 100644 --- a/impeller/compiler/reflector.h +++ b/impeller/compiler/reflector.h @@ -16,6 +16,13 @@ namespace impeller { namespace compiler { +struct StructMember { + std::string type; + std::string name; + size_t offset = 0u; + size_t byte_length = 0u; +}; + class Reflector { public: struct Options { @@ -38,13 +45,6 @@ class Reflector { std::shared_ptr GetReflectionCC() const; private: - struct StructMember { - std::string type; - std::string name; - size_t offset = 0u; - size_t byte_length = 0u; - }; - struct StructDefinition { std::string name; size_t byte_length = 0u; From b0f5b46290c6678f129a049112c776ad6ae2f88a Mon Sep 17 00:00:00 2001 From: Chinmay Garde Date: Thu, 18 Nov 2021 11:09:54 -0800 Subject: [PATCH 199/433] Fix struct padding calculations. --- impeller/compiler/reflector.cc | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/impeller/compiler/reflector.cc b/impeller/compiler/reflector.cc index b44dbd45fc490..956532e940cc0 100644 --- a/impeller/compiler/reflector.cc +++ b/impeller/compiler/reflector.cc @@ -528,8 +528,9 @@ std::vector Reflector::ReadStructMembers( const auto struct_length = current_byte_offset; { - const auto padding = struct_length % max_member_alignment; - if (padding != 0) { + const auto excess = struct_length % max_member_alignment; + if (excess != 0) { + const auto padding = max_member_alignment - excess; result.emplace_back(StructMember{ .type = TypeNameWithPaddingOfSize(padding), .name = "_PADDING_", From 180ecd33beabfc583ecbb58f7ec7fb81dd3d4b76 Mon Sep 17 00:00:00 2001 From: Chinmay Garde Date: Thu, 18 Nov 2021 11:42:00 -0800 Subject: [PATCH 200/433] Rendering of continuous strokes. --- impeller/aiks/aiks_unittests.cc | 12 ++++ impeller/entity/BUILD.gn | 2 + impeller/entity/content_renderer.cc | 9 +++ impeller/entity/content_renderer.h | 7 ++ impeller/entity/contents.cc | 68 +++++++++++++++++-- impeller/entity/shaders/solid_stroke.frag | 11 +++ impeller/entity/shaders/solid_stroke.vert | 23 +++++++ impeller/geometry/path_builder.cc | 5 ++ impeller/geometry/path_builder.h | 2 + impeller/geometry/point.h | 12 ++++ .../backend/metal/vertex_descriptor_mtl.h | 11 ++- .../backend/metal/vertex_descriptor_mtl.mm | 5 +- impeller/renderer/shader_types.h | 2 + impeller/renderer/vertex_buffer_builder.h | 2 + 14 files changed, 164 insertions(+), 7 deletions(-) create mode 100644 impeller/entity/shaders/solid_stroke.frag create mode 100644 impeller/entity/shaders/solid_stroke.vert diff --git a/impeller/aiks/aiks_unittests.cc b/impeller/aiks/aiks_unittests.cc index d0135aa86833e..f713c007949aa 100644 --- a/impeller/aiks/aiks_unittests.cc +++ b/impeller/aiks/aiks_unittests.cc @@ -81,6 +81,18 @@ TEST_F(AiksTest, CanRenderStrokes) { Paint paint; paint.color = Color::Red(); paint.stroke_width = 20.0; + paint.style = Paint::Style::kStroke; + canvas.DrawPath(PathBuilder{}.AddLine({200, 100}, {800, 100}).CreatePath(), + paint); + // ASSERT_TRUE(OpenPlaygroundHere(canvas.EndRecordingAsPicture())); +} + +TEST_F(AiksTest, CanRenderCurvedStrokes) { + Canvas canvas; + Paint paint; + paint.color = Color::Blue(); + paint.stroke_width = 25.0; + paint.style = Paint::Style::kStroke; canvas.DrawPath(PathBuilder{}.AddCircle({500, 500}, 250).CreatePath(), paint); ASSERT_TRUE(OpenPlaygroundHere(canvas.EndRecordingAsPicture())); } diff --git a/impeller/entity/BUILD.gn b/impeller/entity/BUILD.gn index b465c0cdd5d31..e3c1d596ef58a 100644 --- a/impeller/entity/BUILD.gn +++ b/impeller/entity/BUILD.gn @@ -12,6 +12,8 @@ impeller_shaders("entity_shaders") { "shaders/gradient_fill.vert", "shaders/solid_fill.frag", "shaders/solid_fill.vert", + "shaders/solid_stroke.frag", + "shaders/solid_stroke.vert", "shaders/texture_fill.frag", "shaders/texture_fill.vert", ] diff --git a/impeller/entity/content_renderer.cc b/impeller/entity/content_renderer.cc index 6ea7fb76e4248..4209027a842d1 100644 --- a/impeller/entity/content_renderer.cc +++ b/impeller/entity/content_renderer.cc @@ -15,6 +15,7 @@ ContentRenderer::ContentRenderer(std::shared_ptr context) gradient_fill_pipeline_ = std::make_unique(*context_); solid_fill_pipeline_ = std::make_unique(*context_); texture_pipeline_ = std::make_unique(*context_); + solid_stroke_pipeline_ = std::make_unique(*context_); is_valid_ = true; } @@ -52,4 +53,12 @@ std::shared_ptr ContentRenderer::GetTexturePipeline() const { return texture_pipeline_->WaitAndGet(); } +std::shared_ptr ContentRenderer::GetSolidStrokePipeline() const { + if (!IsValid()) { + return nullptr; + } + + return solid_stroke_pipeline_->WaitAndGet(); +} + } // namespace impeller diff --git a/impeller/entity/content_renderer.h b/impeller/entity/content_renderer.h index 997655329b5df..781c6aeb36edc 100644 --- a/impeller/entity/content_renderer.h +++ b/impeller/entity/content_renderer.h @@ -11,6 +11,8 @@ #include "flutter/impeller/entity/gradient_fill.vert.h" #include "flutter/impeller/entity/solid_fill.frag.h" #include "flutter/impeller/entity/solid_fill.vert.h" +#include "flutter/impeller/entity/solid_stroke.frag.h" +#include "flutter/impeller/entity/solid_stroke.vert.h" #include "flutter/impeller/entity/texture_fill.frag.h" #include "flutter/impeller/entity/texture_fill.vert.h" #include "impeller/renderer/pipeline.h" @@ -23,6 +25,8 @@ using SolidFillPipeline = PipelineT; using TexturePipeline = PipelineT; +using SolidStrokePipeline = + PipelineT; class ContentRenderer { public: @@ -38,6 +42,8 @@ class ContentRenderer { std::shared_ptr GetTexturePipeline() const; + std::shared_ptr GetSolidStrokePipeline() const; + std::shared_ptr GetContext() const; private: @@ -45,6 +51,7 @@ class ContentRenderer { std::unique_ptr gradient_fill_pipeline_; std::unique_ptr solid_fill_pipeline_; std::unique_ptr texture_pipeline_; + std::unique_ptr solid_stroke_pipeline_; bool is_valid_ = false; FML_DISALLOW_COPY_AND_ASSIGN(ContentRenderer); diff --git a/impeller/entity/contents.cc b/impeller/entity/contents.cc index e99e442c783c1..a9dd5e306a166 100644 --- a/impeller/entity/contents.cc +++ b/impeller/entity/contents.cc @@ -10,6 +10,7 @@ #include "impeller/entity/content_renderer.h" #include "impeller/entity/entity.h" #include "impeller/geometry/path_builder.h" +#include "impeller/geometry/vector.h" #include "impeller/renderer/render_pass.h" #include "impeller/renderer/sampler_library.h" #include "impeller/renderer/surface.h" @@ -236,13 +237,11 @@ bool TextureContents::Render(const ContentRenderer& renderer, frame_info.mvp = Matrix::MakeOrthographic(surface.GetSize()) * entity.GetTransformation(); - auto frame_info_view = host_buffer.EmplaceUniform(frame_info); - Command cmd; cmd.label = "TextureFill"; cmd.pipeline = renderer.GetTexturePipeline(); cmd.BindVertices(vertex_builder.CreateVertexBuffer(host_buffer)); - VS::BindFrameInfo(cmd, frame_info_view); + VS::BindFrameInfo(cmd, host_buffer.EmplaceUniform(frame_info)); FS::BindTextureSampler( cmd, texture_, renderer.GetContext()->GetSamplerLibrary()->GetSampler({})); @@ -275,6 +274,45 @@ const Color& SolidStrokeContents::GetColor() const { return color_; } +static VertexBuffer CreateSolidStrokeVertices(const Path& path, + HostBuffer& buffer) { + using VS = SolidStrokeVertexShader; + + VertexBufferBuilder vtx_builder; + auto polyline = path.CreatePolyline(); + + for (size_t i = 0, polyline_size = polyline.size(); i < polyline_size; i++) { + const auto is_last_point = i == polyline_size - 1; + + const auto& p1 = polyline[i]; + const auto& p2 = is_last_point ? polyline[i - 1] : polyline[i + 1]; + + const auto diff = p2 - p1; + + const Scalar direction = is_last_point ? -1.0 : 1.0; + + const auto normal = + Point{-diff.y * direction, diff.x * direction}.Normalize(); + + VS::PerVertexData vtx; + vtx.vertex_position = p1; + + if (i == 0) { + vtx.vertex_normal = -normal; + vtx_builder.AppendVertex(vtx); + vtx.vertex_normal = normal; + vtx_builder.AppendVertex(vtx); + } + + vtx.vertex_normal = normal; + vtx_builder.AppendVertex(vtx); + vtx.vertex_normal = -normal; + vtx_builder.AppendVertex(vtx); + } + + return vtx_builder.CreateVertexBuffer(buffer); +} + bool SolidStrokeContents::Render(const ContentRenderer& renderer, const Entity& entity, const Surface& surface, @@ -283,7 +321,29 @@ bool SolidStrokeContents::Render(const ContentRenderer& renderer, return true; } - return false; + using VS = SolidStrokeVertexShader; + + VS::FrameInfo frame_info; + frame_info.mvp = + Matrix::MakeOrthographic(surface.GetSize()) * entity.GetTransformation(); + + VS::StrokeInfo stroke_info; + stroke_info.color = color_; + stroke_info.size = stroke_size_; + + Command cmd; + cmd.primitive_type = PrimitiveType::kTriangleStrip; + cmd.label = "SolidStroke"; + cmd.pipeline = renderer.GetSolidStrokePipeline(); + cmd.BindVertices( + CreateSolidStrokeVertices(entity.GetPath(), pass.GetTransientsBuffer())); + VS::BindFrameInfo(cmd, pass.GetTransientsBuffer().EmplaceUniform(frame_info)); + VS::BindStrokeInfo(cmd, + pass.GetTransientsBuffer().EmplaceUniform(stroke_info)); + + pass.AddCommand(std::move(cmd)); + + return true; } void SolidStrokeContents::SetStrokeSize(Scalar size) { diff --git a/impeller/entity/shaders/solid_stroke.frag b/impeller/entity/shaders/solid_stroke.frag new file mode 100644 index 0000000000000..88b1f7635d9f0 --- /dev/null +++ b/impeller/entity/shaders/solid_stroke.frag @@ -0,0 +1,11 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +in vec4 stroke_color; + +out vec4 frag_color; + +void main() { + frag_color = stroke_color; +} diff --git a/impeller/entity/shaders/solid_stroke.vert b/impeller/entity/shaders/solid_stroke.vert new file mode 100644 index 0000000000000..696c039801303 --- /dev/null +++ b/impeller/entity/shaders/solid_stroke.vert @@ -0,0 +1,23 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +uniform FrameInfo { + mat4 mvp; +} frame_info; + +uniform StrokeInfo { + vec4 color; + float size; +} stroke_info; + +in vec2 vertex_position; +in vec2 vertex_normal; + +out vec4 stroke_color; + +void main() { + vec2 offset = vertex_normal * vec2(stroke_info.size * 0.5); + gl_Position = frame_info.mvp * vec4(vertex_position + offset, 0.0, 1.0); + stroke_color = stroke_info.color; +} diff --git a/impeller/geometry/path_builder.cc b/impeller/geometry/path_builder.cc index 835e15ad349b0..94062420dd3d4 100644 --- a/impeller/geometry/path_builder.cc +++ b/impeller/geometry/path_builder.cc @@ -298,6 +298,11 @@ PathBuilder& PathBuilder::AddOval(const Rect& container) { return *this; } +PathBuilder& PathBuilder::AddLine(const Point& p1, const Point& p2) { + prototype_.AddLinearComponent(p1, p2); + return *this; +} + const Path& PathBuilder::GetCurrentPath() const { return prototype_; } diff --git a/impeller/geometry/path_builder.h b/impeller/geometry/path_builder.h index 36e391fa81f01..84057fa79d33c 100644 --- a/impeller/geometry/path_builder.h +++ b/impeller/geometry/path_builder.h @@ -54,6 +54,8 @@ class PathBuilder { PathBuilder& AddOval(const Rect& rect); + PathBuilder& AddLine(const Point& p1, const Point& p2); + struct RoundingRadii { Scalar topLeft = 0.0; Scalar bottomLeft = 0.0; diff --git a/impeller/geometry/point.h b/impeller/geometry/point.h index 6270f1714eb56..b015a2ee44ba7 100644 --- a/impeller/geometry/point.h +++ b/impeller/geometry/point.h @@ -86,6 +86,18 @@ struct TPoint { constexpr Type GetDistance(const TPoint& p) const { return sqrt(GetDistanceSquared(p)); } + + constexpr Type GetLengthSquared() const { return GetDistanceSquared({}); } + + constexpr Type GetLength() const { return GetDistance({}); } + + constexpr TPoint Normalize() const { + const auto length = GetLength(); + if (length == 0) { + return {}; + } + return {x / length, y / length}; + } }; using Point = TPoint; diff --git a/impeller/renderer/backend/metal/vertex_descriptor_mtl.h b/impeller/renderer/backend/metal/vertex_descriptor_mtl.h index 7f8851f4b47f8..82dcceb4dab00 100644 --- a/impeller/renderer/backend/metal/vertex_descriptor_mtl.h +++ b/impeller/renderer/backend/metal/vertex_descriptor_mtl.h @@ -4,6 +4,8 @@ #include +#include + #include "flutter/fml/macros.h" #include "impeller/renderer/backend/metal/backend_cast.h" #include "impeller/renderer/vertex_descriptor.h" @@ -28,8 +30,15 @@ class VertexDescriptorMTL { StageInput(size_t p_location, MTLVertexFormat p_format, size_t p_length) : location(p_location), format(p_format), length(p_length) {} + + struct Compare { + constexpr bool operator()(const StageInput& lhs, + const StageInput& rhs) const { + return lhs.location < rhs.location; + } + }; }; - std::vector stage_inputs_; + std::set stage_inputs_; FML_DISALLOW_COPY_AND_ASSIGN(VertexDescriptorMTL); }; diff --git a/impeller/renderer/backend/metal/vertex_descriptor_mtl.mm b/impeller/renderer/backend/metal/vertex_descriptor_mtl.mm index 02b45f9c3d6b1..0ae058f6d87cf 100644 --- a/impeller/renderer/backend/metal/vertex_descriptor_mtl.mm +++ b/impeller/renderer/backend/metal/vertex_descriptor_mtl.mm @@ -179,8 +179,9 @@ static MTLVertexFormat ReadStageInputFormat(const ShaderStageIOSlot& input) { FML_LOG(ERROR) << "Format for input " << input.name << " not supported."; return false; } - stage_inputs_.emplace_back(StageInput{ - input.location, vertex_format, (input.bit_width * input.vec_size) / 8}); + + stage_inputs_.insert(StageInput{input.location, vertex_format, + (input.bit_width * input.vec_size) / 8}); } return true; diff --git a/impeller/renderer/shader_types.h b/impeller/renderer/shader_types.h index efcc28a2a34a7..fb6e51b65e6da 100644 --- a/impeller/renderer/shader_types.h +++ b/impeller/renderer/shader_types.h @@ -50,6 +50,8 @@ struct ShaderUniformSlot { struct ShaderStageIOSlot { // Statically allocated const string containing advisory debug description. + // This may be absent in release modes and the runtime may not use this string + // for normal operation. const char* name; size_t location; size_t set; diff --git a/impeller/renderer/vertex_buffer_builder.h b/impeller/renderer/vertex_buffer_builder.h index 0d92e2debe720..c217241e4861c 100644 --- a/impeller/renderer/vertex_buffer_builder.h +++ b/impeller/renderer/vertex_buffer_builder.h @@ -35,6 +35,8 @@ class VertexBufferBuilder { bool HasVertices() const { return !vertices_.empty(); } + size_t GetVertexCount() const { return vertices_.size(); } + VertexBufferBuilder& AppendVertex(VertexType_ vertex) { vertices_.emplace_back(std::move(vertex)); return *this; From a27c98b40a0ec0e57701eb8210a8e2b432206419 Mon Sep 17 00:00:00 2001 From: Chinmay Garde Date: Thu, 18 Nov 2021 12:47:27 -0800 Subject: [PATCH 201/433] Cleanup entity API. --- impeller/aiks/aiks_unittests.cc | 2 +- impeller/aiks/canvas.cc | 1 - impeller/entity/entity.cc | 24 ----------------------- impeller/entity/entity.h | 15 -------------- impeller/entity/shaders/solid_stroke.vert | 1 + 5 files changed, 2 insertions(+), 41 deletions(-) diff --git a/impeller/aiks/aiks_unittests.cc b/impeller/aiks/aiks_unittests.cc index f713c007949aa..74eb557b0d036 100644 --- a/impeller/aiks/aiks_unittests.cc +++ b/impeller/aiks/aiks_unittests.cc @@ -94,7 +94,7 @@ TEST_F(AiksTest, CanRenderCurvedStrokes) { paint.stroke_width = 25.0; paint.style = Paint::Style::kStroke; canvas.DrawPath(PathBuilder{}.AddCircle({500, 500}, 250).CreatePath(), paint); - ASSERT_TRUE(OpenPlaygroundHere(canvas.EndRecordingAsPicture())); + // ASSERT_TRUE(OpenPlaygroundHere(canvas.EndRecordingAsPicture())); } } // namespace testing diff --git a/impeller/aiks/canvas.cc b/impeller/aiks/canvas.cc index e52c2b7c43d41..18654d67d9632 100644 --- a/impeller/aiks/canvas.cc +++ b/impeller/aiks/canvas.cc @@ -80,7 +80,6 @@ void Canvas::ClipPath(Path path) { Entity entity; entity.SetTransformation(GetCurrentTransformation()); entity.SetPath(std::move(path)); - entity.SetIsClip(true); GetCurrentPass().PushEntity(std::move(entity)); } diff --git a/impeller/entity/entity.cc b/impeller/entity/entity.cc index 77ab4ccd2769c..37e9f615c83eb 100644 --- a/impeller/entity/entity.cc +++ b/impeller/entity/entity.cc @@ -18,22 +18,6 @@ void Entity::SetTransformation(const Matrix& transformation) { transformation_ = transformation; } -const Color& Entity::GetStrokeColor() const { - return stroke_color_; -} - -void Entity::SetStrokeColor(const Color& strokeColor) { - stroke_color_ = strokeColor; -} - -double Entity::GetStrokeSize() const { - return stroke_size_; -} - -void Entity::SetStrokeSize(double strokeSize) { - stroke_size_ = std::max(strokeSize, 0.0); -} - const Path& Entity::GetPath() const { return path_; } @@ -42,14 +26,6 @@ void Entity::SetPath(Path path) { path_ = std::move(path); } -void Entity::SetIsClip(bool is_clip) { - is_clip_ = is_clip; -} - -bool Entity::IsClip() const { - return is_clip_; -} - void Entity::SetContents(std::shared_ptr contents) { contents_ = std::move(contents); } diff --git a/impeller/entity/entity.h b/impeller/entity/entity.h index 4ee9bf2e26e53..eeb0dd007f5c7 100644 --- a/impeller/entity/entity.h +++ b/impeller/entity/entity.h @@ -23,22 +23,10 @@ class Entity { void SetTransformation(const Matrix& transformation); - const Color& GetStrokeColor() const; - - void SetStrokeColor(const Color& strokeColor); - - double GetStrokeSize() const; - - void SetStrokeSize(double strokeSize); - const Path& GetPath() const; void SetPath(Path path); - void SetIsClip(bool is_clip); - - bool IsClip() const; - void SetContents(std::shared_ptr contents); const std::shared_ptr& GetContents() const; @@ -47,9 +35,6 @@ class Entity { Matrix transformation_; std::shared_ptr contents_; Path path_; - Color stroke_color_; - double stroke_size_ = 1.0; - bool is_clip_ = false; }; } // namespace impeller diff --git a/impeller/entity/shaders/solid_stroke.vert b/impeller/entity/shaders/solid_stroke.vert index 696c039801303..896fb565991f3 100644 --- a/impeller/entity/shaders/solid_stroke.vert +++ b/impeller/entity/shaders/solid_stroke.vert @@ -17,6 +17,7 @@ in vec2 vertex_normal; out vec4 stroke_color; void main() { + // Push one vertex by the half stroke size along the normal vector. vec2 offset = vertex_normal * vec2(stroke_info.size * 0.5); gl_Position = frame_info.mvp * vec4(vertex_position + offset, 0.0, 1.0); stroke_color = stroke_info.color; From da131d8928f317e4d344935bc3240a52c8084d00 Mon Sep 17 00:00:00 2001 From: Chinmay Garde Date: Thu, 18 Nov 2021 15:26:47 -0800 Subject: [PATCH 202/433] WIP Add test for clip rendering. --- impeller/aiks/aiks_unittests.cc | 10 ++++++++++ impeller/aiks/canvas.cc | 5 +---- impeller/renderer/allocator.h | 3 +++ 3 files changed, 14 insertions(+), 4 deletions(-) diff --git a/impeller/aiks/aiks_unittests.cc b/impeller/aiks/aiks_unittests.cc index 74eb557b0d036..214a46d5af564 100644 --- a/impeller/aiks/aiks_unittests.cc +++ b/impeller/aiks/aiks_unittests.cc @@ -97,5 +97,15 @@ TEST_F(AiksTest, CanRenderCurvedStrokes) { // ASSERT_TRUE(OpenPlaygroundHere(canvas.EndRecordingAsPicture())); } +TEST_F(AiksTest, CanRenderClips) { + Canvas canvas; + Paint paint; + paint.color = Color::Fuchsia(); + canvas.ClipPath( + PathBuilder{}.AddRect(Rect::MakeXYWH(0, 0, 100, 100)).CreatePath()); + canvas.DrawPath(PathBuilder{}.AddCircle({100, 100}, 50).CreatePath(), paint); + ASSERT_TRUE(OpenPlaygroundHere(canvas.EndRecordingAsPicture())); +} + } // namespace testing } // namespace impeller diff --git a/impeller/aiks/canvas.cc b/impeller/aiks/canvas.cc index 18654d67d9632..e4429733f514a 100644 --- a/impeller/aiks/canvas.cc +++ b/impeller/aiks/canvas.cc @@ -77,10 +77,7 @@ void Canvas::SaveLayer(const Paint& paint, std::optional bounds) { } void Canvas::ClipPath(Path path) { - Entity entity; - entity.SetTransformation(GetCurrentTransformation()); - entity.SetPath(std::move(path)); - GetCurrentPass().PushEntity(std::move(entity)); + passes_.push_back({}); } void Canvas::DrawShadow(Path path, Color color, Scalar elevation) {} diff --git a/impeller/renderer/allocator.h b/impeller/renderer/allocator.h index d8a81a8abebf6..56d0483d759ac 100644 --- a/impeller/renderer/allocator.h +++ b/impeller/renderer/allocator.h @@ -47,6 +47,9 @@ class Context; class DeviceBuffer; class Texture; +//------------------------------------------------------------------------------ +/// @brief An object that allocates device memory. +/// class Allocator { public: virtual ~Allocator(); From 75e827b3a9452d3ed962207520fb2b8f17547773 Mon Sep 17 00:00:00 2001 From: Chinmay Garde Date: Fri, 19 Nov 2021 15:03:27 -0800 Subject: [PATCH 203/433] Add support for typed commands. --- impeller/aiks/aiks_unittests.cc | 2 +- impeller/aiks/canvas.cc | 6 +++- impeller/aiks/canvas_pass.cc | 8 +++++ impeller/aiks/canvas_pass.h | 8 ++++- impeller/aiks/picture_renderer.cc | 4 +-- impeller/aiks/picture_renderer.h | 2 +- impeller/entity/contents.cc | 15 ++++++++ impeller/entity/contents.h | 16 +++++++++ impeller/entity/entity_renderer.cc | 5 ++- impeller/entity/entity_renderer.h | 2 +- impeller/fixtures/BUILD.gn | 2 ++ impeller/fixtures/test_texture.frag | 9 +++++ impeller/fixtures/test_texture.vert | 14 ++++++++ impeller/renderer/command.h | 38 ++++++++++++++++++++ impeller/renderer/renderer_unittests.cc | 46 +++++++++++++++++++++++++ 15 files changed, 167 insertions(+), 10 deletions(-) create mode 100644 impeller/fixtures/test_texture.frag create mode 100644 impeller/fixtures/test_texture.vert diff --git a/impeller/aiks/aiks_unittests.cc b/impeller/aiks/aiks_unittests.cc index 214a46d5af564..ceb920d576f0a 100644 --- a/impeller/aiks/aiks_unittests.cc +++ b/impeller/aiks/aiks_unittests.cc @@ -104,7 +104,7 @@ TEST_F(AiksTest, CanRenderClips) { canvas.ClipPath( PathBuilder{}.AddRect(Rect::MakeXYWH(0, 0, 100, 100)).CreatePath()); canvas.DrawPath(PathBuilder{}.AddCircle({100, 100}, 50).CreatePath(), paint); - ASSERT_TRUE(OpenPlaygroundHere(canvas.EndRecordingAsPicture())); + // ASSERT_TRUE(OpenPlaygroundHere(canvas.EndRecordingAsPicture())); } } // namespace testing diff --git a/impeller/aiks/canvas.cc b/impeller/aiks/canvas.cc index e4429733f514a..de67b16cdbd0d 100644 --- a/impeller/aiks/canvas.cc +++ b/impeller/aiks/canvas.cc @@ -77,7 +77,11 @@ void Canvas::SaveLayer(const Paint& paint, std::optional bounds) { } void Canvas::ClipPath(Path path) { - passes_.push_back({}); + Entity entity; + entity.SetTransformation(GetCurrentTransformation()); + entity.SetPath(std::move(path)); + entity.SetContents(std::make_shared()); + GetCurrentPass().PushEntity(std::move(entity)); } void Canvas::DrawShadow(Path path, Color color, Scalar elevation) {} diff --git a/impeller/aiks/canvas_pass.cc b/impeller/aiks/canvas_pass.cc index 7ea207533a151..e861e923e5b78 100644 --- a/impeller/aiks/canvas_pass.cc +++ b/impeller/aiks/canvas_pass.cc @@ -18,4 +18,12 @@ const std::vector& CanvasPass::GetPassEntities() const { return ops_; } +void CanvasPass::SetPostProcessingEntity(Entity entity) { + post_processing_entity_ = std::move(entity); +} + +const Entity& CanvasPass::GetPostProcessingEntity() const { + return post_processing_entity_; +} + } // namespace impeller diff --git a/impeller/aiks/canvas_pass.h b/impeller/aiks/canvas_pass.h index 0183ade6c2796..0074492bd4625 100644 --- a/impeller/aiks/canvas_pass.h +++ b/impeller/aiks/canvas_pass.h @@ -4,9 +4,10 @@ #pragma once -#include +#include #include "flutter/fml/macros.h" +#include "impeller/entity/contents.h" #include "impeller/entity/entity.h" namespace impeller { @@ -21,8 +22,13 @@ class CanvasPass { const std::vector& GetPassEntities() const; + void SetPostProcessingEntity(Entity entity); + + const Entity& GetPostProcessingEntity() const; + private: std::vector ops_; + Entity post_processing_entity_; }; } // namespace impeller diff --git a/impeller/aiks/picture_renderer.cc b/impeller/aiks/picture_renderer.cc index 9f918a1837bf4..a37330bfb3cb3 100644 --- a/impeller/aiks/picture_renderer.cc +++ b/impeller/aiks/picture_renderer.cc @@ -23,14 +23,14 @@ bool PictureRenderer::IsValid() const { } bool PictureRenderer::Render(const Surface& surface, - RenderPass& onscreen_pass, + RenderPass& parent_pass, const Picture& picture) { if (!IsValid()) { return false; } for (const auto& pass : picture.passes) { - if (!entity_renderer_.RenderEntities(surface, onscreen_pass, + if (!entity_renderer_.RenderEntities(surface, parent_pass, pass.GetPassEntities())) { return false; } diff --git a/impeller/aiks/picture_renderer.h b/impeller/aiks/picture_renderer.h index 3e3ab2939ca63..66aa1b5c4826a 100644 --- a/impeller/aiks/picture_renderer.h +++ b/impeller/aiks/picture_renderer.h @@ -23,7 +23,7 @@ class PictureRenderer { bool IsValid() const; [[nodiscard]] bool Render(const Surface& surface, - RenderPass& onscreen_pass, + RenderPass& parent_pass, const Picture& picture); private: diff --git a/impeller/entity/contents.cc b/impeller/entity/contents.cc index a9dd5e306a166..958cabf03527b 100644 --- a/impeller/entity/contents.cc +++ b/impeller/entity/contents.cc @@ -354,4 +354,19 @@ Scalar SolidStrokeContents::GetStrokeSize() const { return stroke_size_; } +/******************************************************************************* + ******* ClipContents + ******************************************************************************/ + +ClipContents::ClipContents() = default; + +ClipContents::~ClipContents() = default; + +bool ClipContents::Render(const ContentRenderer& renderer, + const Entity& entity, + const Surface& surface, + RenderPass& pass) const { + return true; +} + } // namespace impeller diff --git a/impeller/entity/contents.h b/impeller/entity/contents.h index 855ec0adfd881..826bf29040763 100644 --- a/impeller/entity/contents.h +++ b/impeller/entity/contents.h @@ -139,4 +139,20 @@ class SolidStrokeContents final : public Contents { FML_DISALLOW_COPY_AND_ASSIGN(SolidStrokeContents); }; +class ClipContents final : public Contents { + public: + ClipContents(); + + ~ClipContents(); + + // |Contents| + bool Render(const ContentRenderer& renderer, + const Entity& entity, + const Surface& surface, + RenderPass& pass) const override; + + private: + FML_DISALLOW_COPY_AND_ASSIGN(ClipContents); +}; + } // namespace impeller diff --git a/impeller/entity/entity_renderer.cc b/impeller/entity/entity_renderer.cc index 0b35bb5ca8216..2531ff833b3ce 100644 --- a/impeller/entity/entity_renderer.cc +++ b/impeller/entity/entity_renderer.cc @@ -30,7 +30,7 @@ bool EntityRenderer::IsValid() const { } bool EntityRenderer::RenderEntities(const Surface& surface, - RenderPass& onscreen_pass, + RenderPass& parent_pass, const std::vector& entities) { if (!IsValid()) { return false; @@ -38,8 +38,7 @@ bool EntityRenderer::RenderEntities(const Surface& surface, for (const auto& entity : entities) { if (auto contents = entity.GetContents()) { - if (!contents->Render(*content_renderer_, entity, surface, - onscreen_pass)) { + if (!contents->Render(*content_renderer_, entity, surface, parent_pass)) { return false; } } diff --git a/impeller/entity/entity_renderer.h b/impeller/entity/entity_renderer.h index 1a0160cc1d798..b87b64572ef29 100644 --- a/impeller/entity/entity_renderer.h +++ b/impeller/entity/entity_renderer.h @@ -24,7 +24,7 @@ class EntityRenderer { bool IsValid() const; [[nodiscard]] bool RenderEntities(const Surface& surface, - RenderPass& onscreen_pass, + RenderPass& parent_pass, const std::vector& entities); private: diff --git a/impeller/fixtures/BUILD.gn b/impeller/fixtures/BUILD.gn index 3bbffb60917aa..11640f79c3c09 100644 --- a/impeller/fixtures/BUILD.gn +++ b/impeller/fixtures/BUILD.gn @@ -12,6 +12,8 @@ impeller_shaders("shader_fixtures") { shaders = [ "box_fade.vert", "box_fade.frag", + "test_texture.vert", + "test_texture.frag", ] } diff --git a/impeller/fixtures/test_texture.frag b/impeller/fixtures/test_texture.frag new file mode 100644 index 0000000000000..778163ed04f29 --- /dev/null +++ b/impeller/fixtures/test_texture.frag @@ -0,0 +1,9 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +out vec4 frag_color; + +void main() { + frag_color = vec4(1.0); +} diff --git a/impeller/fixtures/test_texture.vert b/impeller/fixtures/test_texture.vert new file mode 100644 index 0000000000000..24b7ae6b13b7f --- /dev/null +++ b/impeller/fixtures/test_texture.vert @@ -0,0 +1,14 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +uniform FrameInfo { + mat4 mvp; +} frame_info; + +in vec2 vtx; + +void main() { + gl_Position = frame_info.mvp * vec4(vtx, 0.0, 1.0); + +} diff --git a/impeller/renderer/command.h b/impeller/renderer/command.h index a54cfc34d82a3..8ec8eca100968 100644 --- a/impeller/renderer/command.h +++ b/impeller/renderer/command.h @@ -17,6 +17,7 @@ #include "impeller/renderer/shader_types.h" #include "impeller/renderer/texture.h" #include "impeller/renderer/vertex_buffer.h" +#include "impeller/renderer/vertex_buffer_builder.h" namespace impeller { @@ -94,4 +95,41 @@ struct Command { constexpr operator bool() const { return pipeline && pipeline->IsValid(); } }; +template +struct CommandT { + using VertexShader = VertexShader_; + using FragmentShader = FragmentShader_; + using VertexBufferBuilder = + VertexBufferBuilder; + using Pipeline = PipelineT; + + CommandT(PipelineT& pipeline) { + command_.label = VertexShader::kLabel; + + // This could be moved to the accessor to delay the wait. + command_.pipeline = pipeline.WaitAndGet(); + } + + static VertexBufferBuilder CreateVertexBuilder() { + VertexBufferBuilder builder; + builder.SetLabel(std::string{VertexShader::kLabel}); + return builder; + } + + Command& Get() { return command_; } + + operator Command&() { return Get(); } + + bool BindVertices(VertexBufferBuilder builder, HostBuffer& buffer) { + return command_.BindVertices(builder.CreateVertexBuffer(buffer)); + } + + bool BindVerticesDynamic(const VertexBuffer& buffer) { + return command_.BindVertices(buffer); + } + + private: + Command command_; +}; + } // namespace impeller diff --git a/impeller/renderer/renderer_unittests.cc b/impeller/renderer/renderer_unittests.cc index 3cf50fc5f6b1b..43b39b926080e 100644 --- a/impeller/renderer/renderer_unittests.cc +++ b/impeller/renderer/renderer_unittests.cc @@ -5,6 +5,8 @@ #include "flutter/fml/time/time_point.h" #include "flutter/impeller/fixtures/box_fade.frag.h" #include "flutter/impeller/fixtures/box_fade.vert.h" +#include "flutter/impeller/fixtures/test_texture.frag.h" +#include "flutter/impeller/fixtures/test_texture.vert.h" #include "flutter/testing/testing.h" #include "impeller/geometry/path_builder.h" #include "impeller/image/compressed_image.h" @@ -323,5 +325,49 @@ TEST_F(RendererTest, CanRenderPath) { // OpenPlaygroundHere(callback); } +TEST_F(RendererTest, CanPerformStencilOperations) { + using VS = TestTextureVertexShader; + using FS = TestTextureFragmentShader; + using TestTextureCommand = CommandT; + + auto pipeline = std::make_unique(*GetContext()); + + auto buffer = HostBuffer::Create(); + + auto circle_vtx_builder = TestTextureCommand::CreateVertexBuilder(); + Tessellator{}.Tessellate( + PathBuilder{}.AddCircle({500, 500}, 250).CreatePath().CreatePolyline(), + [&circle_vtx_builder](Point vtx) { + VS::PerVertexData data; + data.vtx = vtx; + circle_vtx_builder.AppendVertex(data); + }); + auto cicle_vertices = circle_vtx_builder.CreateVertexBuffer(*buffer); + + auto square_vtx_builder = TestTextureCommand::CreateVertexBuilder(); + Tessellator{}.Tessellate( + PathBuilder{}.AddRect({0, 0, 250, 250}).CreatePath().CreatePolyline(), + [&square_vtx_builder](Point vtx) { + VS::PerVertexData data; + data.vtx = vtx; + square_vtx_builder.AppendVertex(data); + }); + auto square_vertices = square_vtx_builder.CreateVertexBuffer(*buffer); + + OpenPlaygroundHere([&](const Surface& surface, RenderPass& pass) -> bool { + TestTextureCommand command(*pipeline.get()); + + command.BindVerticesDynamic(cicle_vertices); + + VS::FrameInfo info; + info.mvp = Matrix::MakeOrthographic(surface.GetSize()); + + VS::BindFrameInfo(command, pass.GetTransientsBuffer().EmplaceUniform(info)); + + pass.AddCommand(command); + return true; + }); +} + } // namespace testing } // namespace impeller From b93f3c197c93543f178c9c9f36262ec6d62a7d53 Mon Sep 17 00:00:00 2001 From: Chinmay Garde Date: Sat, 20 Nov 2021 13:12:35 -0800 Subject: [PATCH 204/433] Give the default render target a stencil buffer. --- impeller/playground/playground.mm | 18 ++++++++++++++++++ impeller/renderer/pipeline.h | 4 ++++ impeller/renderer/pipeline_builder.h | 8 ++++++++ 3 files changed, 30 insertions(+) diff --git a/impeller/playground/playground.mm b/impeller/playground/playground.mm index dad34f622e110..8459efc16376a 100644 --- a/impeller/playground/playground.mm +++ b/impeller/playground/playground.mm @@ -157,8 +157,26 @@ static void PlaygroundKeyCallback(GLFWwindow* window, color0.load_action = LoadAction::kClear; color0.store_action = StoreAction::kStore; + TextureDescriptor stencil_texture_descriptor; + stencil_texture_descriptor.format = PixelFormat::kD32FloatS8UNormInt; + stencil_texture_descriptor.size = color0_desc.size; + stencil_texture_descriptor.usage = + static_cast(TextureUsage::kShaderRead) | + static_cast(TextureUsage::kShaderWrite); + auto stencil_texture = + renderer_.GetContext()->GetPermanentsAllocator()->CreateTexture( + StorageMode::kDeviceTransient, stencil_texture_descriptor); + stencil_texture->SetLabel("PlaygroundMainStencil"); + + StencilAttachment stencil0; + stencil0.texture = stencil_texture; + stencil0.clear_stencil = 0; + stencil0.load_action = LoadAction::kClear; + stencil0.store_action = StoreAction::kStore; + RenderTarget desc; desc.SetColorAttachment(color0, 0u); + desc.SetStencilAttachment(stencil0); Surface surface(desc); diff --git a/impeller/renderer/pipeline.h b/impeller/renderer/pipeline.h index 4a6521e02ff94..c0601cb41f13c 100644 --- a/impeller/renderer/pipeline.h +++ b/impeller/renderer/pipeline.h @@ -68,6 +68,10 @@ class PipelineT { context, Builder::MakeDefaultPipelineDescriptor(context))) {} + explicit PipelineT(const Context& context, + std::optional desc) + : pipeline_future_(CreatePipelineFuture(context, desc)) {} + std::shared_ptr WaitAndGet() { if (did_wait_) { return pipeline_; diff --git a/impeller/renderer/pipeline_builder.h b/impeller/renderer/pipeline_builder.h index 89a7833995ebe..38d721660d6d3 100644 --- a/impeller/renderer/pipeline_builder.h +++ b/impeller/renderer/pipeline_builder.h @@ -104,6 +104,14 @@ struct PipelineBuilder { desc.SetColorAttachmentDescriptor(0u, std::move(color0)); } + // Setup default stencil buffer descriptions. + { + StencilAttachmentDescriptor stencil0; + stencil0.depth_stencil_pass = StencilOperation::kIncrementClamp; + desc.SetStencilAttachmentDescriptors(stencil0); + desc.SetStencilPixelFormat(PixelFormat::kD32FloatS8UNormInt); + } + return true; } }; From 564b3e48e990959bf82e488e2f40c41a0c56a90e Mon Sep 17 00:00:00 2001 From: Chinmay Garde Date: Sat, 20 Nov 2021 14:39:55 -0800 Subject: [PATCH 205/433] Implement stencil-only clips. --- impeller/aiks/aiks_unittests.cc | 9 ++- impeller/aiks/canvas.cc | 17 ++++- impeller/aiks/canvas.h | 11 +++- impeller/compiler/reflector.cc | 24 +++---- impeller/entity/content_renderer.cc | 32 +++++++++ impeller/entity/content_renderer.h | 6 ++ impeller/entity/contents.cc | 65 ++++++++++++------- impeller/entity/entity.cc | 8 +++ impeller/entity/entity.h | 5 ++ impeller/playground/playground.mm | 2 +- impeller/renderer/backend/metal/formats_mtl.h | 2 +- .../renderer/backend/metal/render_pass_mtl.mm | 1 + impeller/renderer/command.h | 1 + impeller/renderer/formats.h | 2 +- impeller/renderer/pipeline_builder.h | 2 +- impeller/renderer/pipeline_descriptor.cc | 8 ++- impeller/renderer/pipeline_descriptor.h | 5 +- impeller/renderer/renderer_unittests.cc | 55 ++++------------ 18 files changed, 163 insertions(+), 92 deletions(-) diff --git a/impeller/aiks/aiks_unittests.cc b/impeller/aiks/aiks_unittests.cc index ceb920d576f0a..05fc556df41df 100644 --- a/impeller/aiks/aiks_unittests.cc +++ b/impeller/aiks/aiks_unittests.cc @@ -70,10 +70,9 @@ TEST_F(AiksTest, DISABLED_CanRenderImageRect) { source_rect.size.height /= 2; source_rect.origin.x += source_rect.size.width; source_rect.origin.y += source_rect.size.height; - canvas.DrawImageRect(image, source_rect, Rect::MakeXYWH(100, 100, 600, 600), paint); - // ASSERT_TRUE(OpenPlaygroundHere(canvas.EndRecordingAsPicture())); + ASSERT_TRUE(OpenPlaygroundHere(canvas.EndRecordingAsPicture())); } TEST_F(AiksTest, CanRenderStrokes) { @@ -102,9 +101,9 @@ TEST_F(AiksTest, CanRenderClips) { Paint paint; paint.color = Color::Fuchsia(); canvas.ClipPath( - PathBuilder{}.AddRect(Rect::MakeXYWH(0, 0, 100, 100)).CreatePath()); - canvas.DrawPath(PathBuilder{}.AddCircle({100, 100}, 50).CreatePath(), paint); - // ASSERT_TRUE(OpenPlaygroundHere(canvas.EndRecordingAsPicture())); + PathBuilder{}.AddRect(Rect::MakeXYWH(0, 0, 500, 500)).CreatePath()); + canvas.DrawPath(PathBuilder{}.AddCircle({500, 500}, 250).CreatePath(), paint); + ASSERT_TRUE(OpenPlaygroundHere(canvas.EndRecordingAsPicture())); } } // namespace testing diff --git a/impeller/aiks/canvas.cc b/impeller/aiks/canvas.cc index de67b16cdbd0d..c5d401b2a2814 100644 --- a/impeller/aiks/canvas.cc +++ b/impeller/aiks/canvas.cc @@ -33,11 +33,12 @@ bool Canvas::Restore() { } void Canvas::Concat(const Matrix& xformation) { - xformation_stack_.top() = xformation * xformation_stack_.top(); + const auto current_xformation = xformation_stack_.top().xformation; + xformation_stack_.top().xformation = xformation * current_xformation; } const Matrix& Canvas::GetCurrentTransformation() const { - return xformation_stack_.top(); + return xformation_stack_.top().xformation; } void Canvas::Translate(const Vector3& offset) { @@ -68,6 +69,7 @@ void Canvas::DrawPath(Path path, Paint paint) { Entity entity; entity.SetTransformation(GetCurrentTransformation()); entity.SetPath(std::move(path)); + entity.SetStencilDepth(GetStencilDepth()); entity.SetContents(paint.CreateContentsForEntity()); GetCurrentPass().PushEntity(std::move(entity)); } @@ -77,10 +79,13 @@ void Canvas::SaveLayer(const Paint& paint, std::optional bounds) { } void Canvas::ClipPath(Path path) { + IncrementStencilDepth(); + Entity entity; entity.SetTransformation(GetCurrentTransformation()); entity.SetPath(std::move(path)); entity.SetContents(std::make_shared()); + entity.SetStencilDepth(GetStencilDepth()); GetCurrentPass().PushEntity(std::move(entity)); } @@ -149,4 +154,12 @@ CanvasPass& Canvas::GetCurrentPass() { return passes_.back(); } +void Canvas::IncrementStencilDepth() { + ++xformation_stack_.top().stencil_depth; +} + +size_t Canvas::GetStencilDepth() const { + return xformation_stack_.top().stencil_depth; +} + } // namespace impeller diff --git a/impeller/aiks/canvas.h b/impeller/aiks/canvas.h index 47a84d2aafd02..162d3428f9ec2 100644 --- a/impeller/aiks/canvas.h +++ b/impeller/aiks/canvas.h @@ -23,6 +23,11 @@ namespace impeller { class Entity; +struct CanvasStackEntry { + Matrix xformation; + size_t stencil_depth = 0u; +}; + class Canvas { public: Canvas(); @@ -67,11 +72,15 @@ class Canvas { Picture EndRecordingAsPicture(); private: - std::stack xformation_stack_; + std::stack xformation_stack_; std::vector passes_; CanvasPass& GetCurrentPass(); + void IncrementStencilDepth(); + + size_t GetStencilDepth() const; + FML_DISALLOW_COPY_AND_ASSIGN(Canvas); }; diff --git a/impeller/compiler/reflector.cc b/impeller/compiler/reflector.cc index 956532e940cc0..6725557579056 100644 --- a/impeller/compiler/reflector.cc +++ b/impeller/compiler/reflector.cc @@ -526,17 +526,19 @@ std::vector Reflector::ReadStructMembers( } } - const auto struct_length = current_byte_offset; - { - const auto excess = struct_length % max_member_alignment; - if (excess != 0) { - const auto padding = max_member_alignment - excess; - result.emplace_back(StructMember{ - .type = TypeNameWithPaddingOfSize(padding), - .name = "_PADDING_", - .offset = current_byte_offset, - .byte_length = padding, - }); + if (max_member_alignment > 0u) { + const auto struct_length = current_byte_offset; + { + const auto excess = struct_length % max_member_alignment; + if (excess != 0) { + const auto padding = max_member_alignment - excess; + result.emplace_back(StructMember{ + .type = TypeNameWithPaddingOfSize(padding), + .name = "_PADDING_", + .offset = current_byte_offset, + .byte_length = padding, + }); + } } } diff --git a/impeller/entity/content_renderer.cc b/impeller/entity/content_renderer.cc index 4209027a842d1..6fba04b83b7a1 100644 --- a/impeller/entity/content_renderer.cc +++ b/impeller/entity/content_renderer.cc @@ -12,11 +12,35 @@ ContentRenderer::ContentRenderer(std::shared_ptr context) return; } + // Pipelines whose default descriptors work fine for the entity framework. gradient_fill_pipeline_ = std::make_unique(*context_); solid_fill_pipeline_ = std::make_unique(*context_); texture_pipeline_ = std::make_unique(*context_); solid_stroke_pipeline_ = std::make_unique(*context_); + // Pipelines that are variants of the base pipelines with custom descriptors. + if (auto solid_fill_pipeline = solid_fill_pipeline_->WaitAndGet()) { + auto clip_pipeline_descriptor = solid_fill_pipeline->GetDescriptor(); + // Write to the stencil buffer. + StencilAttachmentDescriptor stencil0; + stencil0.stencil_compare = CompareFunction::kGreaterEqual; + stencil0.depth_stencil_pass = StencilOperation::kSetToReferenceValue; + clip_pipeline_descriptor.SetStencilAttachmentDescriptors(stencil0); + // Disable write to all color attachments. + auto color_attachments = + clip_pipeline_descriptor.GetColorAttachmentDescriptors(); + for (auto& color_attachment : color_attachments) { + color_attachment.second.write_mask = + static_cast(ColorWriteMask::kNone); + } + clip_pipeline_descriptor.SetColorAttachmentDescriptors( + std::move(color_attachments)); + clip_pipeline_ = std::make_unique( + *context_, std::move(clip_pipeline_descriptor)); + } else { + return; + } + is_valid_ = true; } @@ -61,4 +85,12 @@ std::shared_ptr ContentRenderer::GetSolidStrokePipeline() const { return solid_stroke_pipeline_->WaitAndGet(); } +std::shared_ptr ContentRenderer::GetClipPipeline() const { + if (!IsValid()) { + return nullptr; + } + + return clip_pipeline_->WaitAndGet(); +} + } // namespace impeller diff --git a/impeller/entity/content_renderer.h b/impeller/entity/content_renderer.h index 781c6aeb36edc..1172d17f189cc 100644 --- a/impeller/entity/content_renderer.h +++ b/impeller/entity/content_renderer.h @@ -27,6 +27,9 @@ using TexturePipeline = PipelineT; using SolidStrokePipeline = PipelineT; +// Instead of requiring new shaders for clips, the solid fill stages are used +// to redirect writing to the stencil instead of color attachments. +using ClipPipeline = PipelineT; class ContentRenderer { public: @@ -44,6 +47,8 @@ class ContentRenderer { std::shared_ptr GetSolidStrokePipeline() const; + std::shared_ptr GetClipPipeline() const; + std::shared_ptr GetContext() const; private: @@ -52,6 +57,7 @@ class ContentRenderer { std::unique_ptr solid_fill_pipeline_; std::unique_ptr texture_pipeline_; std::unique_ptr solid_stroke_pipeline_; + std::unique_ptr clip_pipeline_; bool is_valid_ = false; FML_DISALLOW_COPY_AND_ASSIGN(ContentRenderer); diff --git a/impeller/entity/contents.cc b/impeller/entity/contents.cc index 958cabf03527b..571418652a41a 100644 --- a/impeller/entity/contents.cc +++ b/impeller/entity/contents.cc @@ -87,6 +87,7 @@ bool LinearGradientContents::Render(const ContentRenderer& renderer, Command cmd; cmd.label = "LinearGradientFill"; cmd.pipeline = renderer.GetGradientFillPipeline(); + cmd.stencil_reference = entity.GetStencilDepth(); cmd.BindVertices(vertices_builder.CreateVertexBuffer( *renderer.GetContext()->GetPermanentsAllocator())); cmd.primitive_type = PrimitiveType::kTriangle; @@ -112,6 +113,25 @@ const Color& SolidColorContents::GetColor() const { return color_; } +static VertexBuffer CreateSolidFillVertices(const Path& path, + HostBuffer& buffer) { + using VS = SolidFillPipeline::VertexShader; + + VertexBufferBuilder vtx_builder; + + auto tesselation_result = Tessellator{}.Tessellate( + path.CreatePolyline(), [&vtx_builder](auto point) { + VS::PerVertexData vtx; + vtx.vertices = point; + vtx_builder.AppendVertex(vtx); + }); + if (!tesselation_result) { + return {}; + } + + return vtx_builder.CreateVertexBuffer(buffer); +} + bool SolidColorContents::Render(const ContentRenderer& renderer, const Entity& entity, const Surface& surface, @@ -125,29 +145,9 @@ bool SolidColorContents::Render(const ContentRenderer& renderer, Command cmd; cmd.label = "SolidFill"; cmd.pipeline = renderer.GetSolidFillPipeline(); - if (cmd.pipeline == nullptr) { - return false; - } - - VertexBufferBuilder vtx_builder; - { - auto tesselation_result = Tessellator{}.Tessellate( - entity.GetPath().CreatePolyline(), [&vtx_builder](auto point) { - VS::PerVertexData vtx; - vtx.vertices = point; - vtx_builder.AppendVertex(vtx); - }); - if (!tesselation_result) { - return false; - } - } - - if (!vtx_builder.HasVertices()) { - return true; - } - - cmd.BindVertices(vtx_builder.CreateVertexBuffer( - *renderer.GetContext()->GetPermanentsAllocator())); + cmd.stencil_reference = entity.GetStencilDepth(); + cmd.BindVertices( + CreateSolidFillVertices(entity.GetPath(), pass.GetTransientsBuffer())); VS::FrameInfo frame_info; frame_info.mvp = @@ -240,6 +240,7 @@ bool TextureContents::Render(const ContentRenderer& renderer, Command cmd; cmd.label = "TextureFill"; cmd.pipeline = renderer.GetTexturePipeline(); + cmd.stencil_reference = entity.GetStencilDepth(); cmd.BindVertices(vertex_builder.CreateVertexBuffer(host_buffer)); VS::BindFrameInfo(cmd, host_buffer.EmplaceUniform(frame_info)); FS::BindTextureSampler( @@ -335,6 +336,7 @@ bool SolidStrokeContents::Render(const ContentRenderer& renderer, cmd.primitive_type = PrimitiveType::kTriangleStrip; cmd.label = "SolidStroke"; cmd.pipeline = renderer.GetSolidStrokePipeline(); + cmd.stencil_reference = entity.GetStencilDepth(); cmd.BindVertices( CreateSolidStrokeVertices(entity.GetPath(), pass.GetTransientsBuffer())); VS::BindFrameInfo(cmd, pass.GetTransientsBuffer().EmplaceUniform(frame_info)); @@ -366,6 +368,23 @@ bool ClipContents::Render(const ContentRenderer& renderer, const Entity& entity, const Surface& surface, RenderPass& pass) const { + using VS = ClipPipeline::VertexShader; + + Command cmd; + cmd.label = "Clip"; + cmd.pipeline = renderer.GetClipPipeline(); + cmd.stencil_reference = entity.GetStencilDepth() + 1u; + cmd.BindVertices( + CreateSolidFillVertices(entity.GetPath(), pass.GetTransientsBuffer())); + + VS::FrameInfo info; + // The color really doesn't matter. + info.color = Color::SkyBlue(); + info.mvp = Matrix::MakeOrthographic(surface.GetSize()); + + VS::BindFrameInfo(cmd, pass.GetTransientsBuffer().EmplaceUniform(info)); + + pass.AddCommand(std::move(cmd)); return true; } diff --git a/impeller/entity/entity.cc b/impeller/entity/entity.cc index 37e9f615c83eb..6e315f95ebd1c 100644 --- a/impeller/entity/entity.cc +++ b/impeller/entity/entity.cc @@ -34,4 +34,12 @@ const std::shared_ptr& Entity::GetContents() const { return contents_; } +void Entity::SetStencilDepth(uint32_t depth) { + stencil_depth_ = depth; +} + +uint32_t Entity::GetStencilDepth() const { + return stencil_depth_; +} + } // namespace impeller diff --git a/impeller/entity/entity.h b/impeller/entity/entity.h index eeb0dd007f5c7..89b82d4fe5d01 100644 --- a/impeller/entity/entity.h +++ b/impeller/entity/entity.h @@ -31,10 +31,15 @@ class Entity { const std::shared_ptr& GetContents() const; + void SetStencilDepth(uint32_t stencil_depth); + + uint32_t GetStencilDepth() const; + private: Matrix transformation_; std::shared_ptr contents_; Path path_; + uint32_t stencil_depth_ = 0u; }; } // namespace impeller diff --git a/impeller/playground/playground.mm b/impeller/playground/playground.mm index 8459efc16376a..00c7d92c423ff 100644 --- a/impeller/playground/playground.mm +++ b/impeller/playground/playground.mm @@ -172,7 +172,7 @@ static void PlaygroundKeyCallback(GLFWwindow* window, stencil0.texture = stencil_texture; stencil0.clear_stencil = 0; stencil0.load_action = LoadAction::kClear; - stencil0.store_action = StoreAction::kStore; + stencil0.store_action = StoreAction::kDontCare; RenderTarget desc; desc.SetColorAttachment(color0, 0u); diff --git a/impeller/renderer/backend/metal/formats_mtl.h b/impeller/renderer/backend/metal/formats_mtl.h index 0e76ff0926626..5788312eb57d6 100644 --- a/impeller/renderer/backend/metal/formats_mtl.h +++ b/impeller/renderer/backend/metal/formats_mtl.h @@ -156,7 +156,7 @@ constexpr MTLStencilOperation ToMTLStencilOperation(StencilOperation op) { return MTLStencilOperationKeep; case StencilOperation::kZero: return MTLStencilOperationZero; - case StencilOperation::kSetToReferneceValue: + case StencilOperation::kSetToReferenceValue: return MTLStencilOperationReplace; case StencilOperation::kIncrementClamp: return MTLStencilOperationIncrementClamp; diff --git a/impeller/renderer/backend/metal/render_pass_mtl.mm b/impeller/renderer/backend/metal/render_pass_mtl.mm index 2a505888f293a..3737bf25cc8ce 100644 --- a/impeller/renderer/backend/metal/render_pass_mtl.mm +++ b/impeller/renderer/backend/metal/render_pass_mtl.mm @@ -371,6 +371,7 @@ static bool Bind(PassBindingsCache& pass, ? MTLWindingClockwise : MTLWindingCounterClockwise]; [pass setCullMode:MTLCullModeNone]; + [pass setStencilReferenceValue:command.stencil_reference]; if (!bind_stage_resources(command.vertex_bindings, ShaderStage::kVertex)) { return false; } diff --git a/impeller/renderer/command.h b/impeller/renderer/command.h index 8ec8eca100968..e0d18193ad32b 100644 --- a/impeller/renderer/command.h +++ b/impeller/renderer/command.h @@ -67,6 +67,7 @@ struct Command { std::string label; PrimitiveType primitive_type = PrimitiveType::kTriangle; WindingOrder winding = WindingOrder::kClockwise; + uint32_t stencil_reference = 0u; bool BindVertices(const VertexBuffer& buffer); diff --git a/impeller/renderer/formats.h b/impeller/renderer/formats.h index fabbb76e36ee9..2e001f6552797 100644 --- a/impeller/renderer/formats.h +++ b/impeller/renderer/formats.h @@ -241,7 +241,7 @@ enum class StencilOperation { /// Reset the stencil value to zero. kZero, /// Reset the stencil value to the reference value. - kSetToReferneceValue, + kSetToReferenceValue, /// Increment the current stencil value by 1. Clamp it to the maximum. kIncrementClamp, /// Decrement the current stencil value by 1. Clamp it to zero. diff --git a/impeller/renderer/pipeline_builder.h b/impeller/renderer/pipeline_builder.h index 38d721660d6d3..84712c4705eca 100644 --- a/impeller/renderer/pipeline_builder.h +++ b/impeller/renderer/pipeline_builder.h @@ -107,7 +107,7 @@ struct PipelineBuilder { // Setup default stencil buffer descriptions. { StencilAttachmentDescriptor stencil0; - stencil0.depth_stencil_pass = StencilOperation::kIncrementClamp; + stencil0.stencil_compare = CompareFunction::kLessEqual; desc.SetStencilAttachmentDescriptors(stencil0); desc.SetStencilPixelFormat(PixelFormat::kD32FloatS8UNormInt); } diff --git a/impeller/renderer/pipeline_descriptor.cc b/impeller/renderer/pipeline_descriptor.cc index 0f7af4a65138f..eb24bb483ab3a 100644 --- a/impeller/renderer/pipeline_descriptor.cc +++ b/impeller/renderer/pipeline_descriptor.cc @@ -94,6 +94,12 @@ PipelineDescriptor& PipelineDescriptor::SetColorAttachmentDescriptor( return *this; } +PipelineDescriptor& PipelineDescriptor::SetColorAttachmentDescriptors( + std::map descriptors) { + color_attachment_descriptors_ = std::move(descriptors); + return *this; +} + const ColorAttachmentDescriptor* PipelineDescriptor::GetColorAttachmentDescriptor(size_t index) const { auto found = color_attachment_descriptors_.find(index); @@ -153,7 +159,7 @@ PipelineDescriptor::GetDepthStencilAttachmentDescriptor() const { return depth_attachment_descriptor_; } -const std::map +const std::map& PipelineDescriptor::GetColorAttachmentDescriptors() const { return color_attachment_descriptors_; } diff --git a/impeller/renderer/pipeline_descriptor.h b/impeller/renderer/pipeline_descriptor.h index 3e43df2d0b488..225b0faf5f2b8 100644 --- a/impeller/renderer/pipeline_descriptor.h +++ b/impeller/renderer/pipeline_descriptor.h @@ -51,10 +51,13 @@ class PipelineDescriptor final : public Comparable { size_t index, ColorAttachmentDescriptor desc); + PipelineDescriptor& SetColorAttachmentDescriptors( + std::map descriptors); + const ColorAttachmentDescriptor* GetColorAttachmentDescriptor( size_t index) const; - const std::map + const std::map& GetColorAttachmentDescriptors() const; PipelineDescriptor& SetDepthStencilAttachmentDescriptor( diff --git a/impeller/renderer/renderer_unittests.cc b/impeller/renderer/renderer_unittests.cc index 43b39b926080e..d165aa7a3bb7a 100644 --- a/impeller/renderer/renderer_unittests.cc +++ b/impeller/renderer/renderer_unittests.cc @@ -172,6 +172,7 @@ TEST_F(RendererTest, CanRenderToTexture) { using BoxPipelineBuilder = PipelineBuilder; auto pipeline_desc = BoxPipelineBuilder::MakeDefaultPipelineDescriptor(*context); + ASSERT_TRUE(pipeline_desc.has_value()); auto box_pipeline = context->GetPipelineLibrary() ->GetRenderPipeline(std::move(pipeline_desc)) .get(); @@ -220,8 +221,18 @@ TEST_F(RendererTest, CanRenderToTexture) { color0.texture->SetLabel("r2t_target"); + StencilAttachment stencil0; + stencil0.load_action = LoadAction::kClear; + stencil0.store_action = StoreAction::kDontCare; + TextureDescriptor stencil_texture_desc; + stencil_texture_desc.size = texture_descriptor.size; + stencil_texture_desc.format = PixelFormat::kD32FloatS8UNormInt; + stencil0.texture = context->GetPermanentsAllocator()->CreateTexture( + StorageMode::kDeviceTransient, stencil_texture_desc); + RenderTarget r2t_desc; r2t_desc.SetColorAttachment(color0, 0u); + r2t_desc.SetStencilAttachment(stencil0); auto cmd_buffer = context->CreateRenderCommandBuffer(); r2t_pass = cmd_buffer->CreateRenderPass(r2t_desc); ASSERT_TRUE(r2t_pass && r2t_pass->IsValid()); @@ -325,49 +336,5 @@ TEST_F(RendererTest, CanRenderPath) { // OpenPlaygroundHere(callback); } -TEST_F(RendererTest, CanPerformStencilOperations) { - using VS = TestTextureVertexShader; - using FS = TestTextureFragmentShader; - using TestTextureCommand = CommandT; - - auto pipeline = std::make_unique(*GetContext()); - - auto buffer = HostBuffer::Create(); - - auto circle_vtx_builder = TestTextureCommand::CreateVertexBuilder(); - Tessellator{}.Tessellate( - PathBuilder{}.AddCircle({500, 500}, 250).CreatePath().CreatePolyline(), - [&circle_vtx_builder](Point vtx) { - VS::PerVertexData data; - data.vtx = vtx; - circle_vtx_builder.AppendVertex(data); - }); - auto cicle_vertices = circle_vtx_builder.CreateVertexBuffer(*buffer); - - auto square_vtx_builder = TestTextureCommand::CreateVertexBuilder(); - Tessellator{}.Tessellate( - PathBuilder{}.AddRect({0, 0, 250, 250}).CreatePath().CreatePolyline(), - [&square_vtx_builder](Point vtx) { - VS::PerVertexData data; - data.vtx = vtx; - square_vtx_builder.AppendVertex(data); - }); - auto square_vertices = square_vtx_builder.CreateVertexBuffer(*buffer); - - OpenPlaygroundHere([&](const Surface& surface, RenderPass& pass) -> bool { - TestTextureCommand command(*pipeline.get()); - - command.BindVerticesDynamic(cicle_vertices); - - VS::FrameInfo info; - info.mvp = Matrix::MakeOrthographic(surface.GetSize()); - - VS::BindFrameInfo(command, pass.GetTransientsBuffer().EmplaceUniform(info)); - - pass.AddCommand(command); - return true; - }); -} - } // namespace testing } // namespace impeller From 98528fe7c177edbe5a3c3fbb926d4841e8dda639 Mon Sep 17 00:00:00 2001 From: Chinmay Garde Date: Mon, 22 Nov 2021 14:34:24 -0800 Subject: [PATCH 206/433] Rework canvas passes in prep for subpasses. --- impeller/aiks/aiks_unittests.cc | 24 +++++++++++ impeller/aiks/canvas.cc | 70 +++++++++++++++++++++---------- impeller/aiks/canvas.h | 14 +++---- impeller/aiks/canvas_pass.cc | 23 ++++++++++ impeller/aiks/canvas_pass.h | 9 ++++ impeller/aiks/picture.h | 4 +- impeller/aiks/picture_renderer.cc | 10 +++-- impeller/entity/entity.cc | 4 ++ impeller/entity/entity.h | 2 + impeller/geometry/color.h | 4 ++ impeller/geometry/path.cc | 17 ++++++-- impeller/geometry/path.h | 3 ++ impeller/geometry/point.h | 9 ++++ 13 files changed, 154 insertions(+), 39 deletions(-) diff --git a/impeller/aiks/aiks_unittests.cc b/impeller/aiks/aiks_unittests.cc index 05fc556df41df..8182261890aa8 100644 --- a/impeller/aiks/aiks_unittests.cc +++ b/impeller/aiks/aiks_unittests.cc @@ -103,6 +103,30 @@ TEST_F(AiksTest, CanRenderClips) { canvas.ClipPath( PathBuilder{}.AddRect(Rect::MakeXYWH(0, 0, 500, 500)).CreatePath()); canvas.DrawPath(PathBuilder{}.AddCircle({500, 500}, 250).CreatePath(), paint); + // ASSERT_TRUE(OpenPlaygroundHere(canvas.EndRecordingAsPicture())); +} + +TEST_F(AiksTest, CanRenderGroupOpacity) { + Canvas canvas; + + Paint red; + red.color = Color::Red(); //.WithAlpha(0.5); + Paint green; + green.color = Color::Green(); //.WithAlpha(0.5); + Paint blue; + blue.color = Color::Blue(); //.WithAlpha(0.5); + + Paint alpha; + alpha.color = Color::Red().WithAlpha(0.5); + + canvas.SaveLayer(alpha); + + canvas.DrawRect({100, 100, 100, 100}, red); + canvas.DrawRect({120, 120, 100, 100}, green); + canvas.DrawRect({140, 140, 100, 100}, blue); + + canvas.Restore(); + ASSERT_TRUE(OpenPlaygroundHere(canvas.EndRecordingAsPicture())); } diff --git a/impeller/aiks/canvas.cc b/impeller/aiks/canvas.cc index c5d401b2a2814..4d9c3d99a5f64 100644 --- a/impeller/aiks/canvas.cc +++ b/impeller/aiks/canvas.cc @@ -12,15 +12,13 @@ namespace impeller { Canvas::Canvas() { - xformation_stack_.push({}); - passes_.emplace_back(CanvasPass{}); + Save(true); } Canvas::~Canvas() = default; void Canvas::Save() { - FML_DCHECK(xformation_stack_.size() > 0); - xformation_stack_.push(xformation_stack_.top()); + Save(false); } bool Canvas::Restore() { @@ -28,17 +26,16 @@ bool Canvas::Restore() { if (xformation_stack_.size() == 1) { return false; } - xformation_stack_.pop(); + xformation_stack_.pop_back(); return true; } void Canvas::Concat(const Matrix& xformation) { - const auto current_xformation = xformation_stack_.top().xformation; - xformation_stack_.top().xformation = xformation * current_xformation; + xformation_stack_.back().xformation = xformation * GetCurrentTransformation(); } const Matrix& Canvas::GetCurrentTransformation() const { - return xformation_stack_.top().xformation; + return xformation_stack_.back().xformation; } void Canvas::Translate(const Vector3& offset) { @@ -71,6 +68,7 @@ void Canvas::DrawPath(Path path, Paint paint) { entity.SetPath(std::move(path)); entity.SetStencilDepth(GetStencilDepth()); entity.SetContents(paint.CreateContentsForEntity()); + GetCurrentPass().PushEntity(std::move(entity)); } @@ -86,21 +84,23 @@ void Canvas::ClipPath(Path path) { entity.SetPath(std::move(path)); entity.SetContents(std::make_shared()); entity.SetStencilDepth(GetStencilDepth()); + GetCurrentPass().PushEntity(std::move(entity)); } void Canvas::DrawShadow(Path path, Color color, Scalar elevation) {} void Canvas::DrawPicture(const Picture& picture) { - for (const auto& pass : picture.passes) { - CanvasPass new_pass; - for (const auto& entity : pass.GetPassEntities()) { - auto new_entity = entity; - new_entity.SetTransformation(GetCurrentTransformation() * - entity.GetTransformation()); - new_pass.PushEntity(std::move(new_entity)); + for (const auto& stack_entry : picture.entries) { + auto new_stack_entry = stack_entry; + if (auto pass = new_stack_entry.pass) { + for (auto entity : pass->GetPassEntities()) { + entity.IncrementStencilDepth(GetStencilDepth()); + entity.SetTransformation(GetCurrentTransformation() * + entity.GetTransformation()); + } } - passes_.emplace_back(std::move(new_pass)); + xformation_stack_.emplace_back(std::move(new_stack_entry)); } } @@ -145,21 +145,49 @@ void Canvas::DrawImageRect(std::shared_ptr image, Picture Canvas::EndRecordingAsPicture() { Picture picture; - picture.passes = std::move(passes_); + picture.entries = std::move(xformation_stack_); return picture; } CanvasPass& Canvas::GetCurrentPass() { - FML_DCHECK(!passes_.empty()); - return passes_.back(); + for (auto i = xformation_stack_.rbegin(), end = xformation_stack_.rend(); + i < end; i++) { + if (i->pass.has_value()) { + return i->pass.value(); + } + } + FML_UNREACHABLE(); } void Canvas::IncrementStencilDepth() { - ++xformation_stack_.top().stencil_depth; + ++xformation_stack_.back().stencil_depth; } size_t Canvas::GetStencilDepth() const { - return xformation_stack_.top().stencil_depth; + return xformation_stack_.back().stencil_depth; +} + +void Canvas::DrawRect(Rect rect, Paint paint) { + DrawPath(PathBuilder{}.AddRect(rect).CreatePath(), std::move(paint)); +} + +void Canvas::Save(bool create_subpass) { + // Check if called from the ctor. + if (xformation_stack_.empty()) { + FML_DCHECK(create_subpass) << "Base entries must have a pass."; + CanvasStackEntry entry; + entry.pass = CanvasPass{}; + xformation_stack_.emplace_back(std::move(entry)); + } + + auto entry = CanvasStackEntry{}; + + entry.xformation = xformation_stack_.back().xformation; + entry.stencil_depth = xformation_stack_.back().stencil_depth; + if (create_subpass) { + entry.pass = CanvasPass{}; + } + xformation_stack_.emplace_back(std::move(entry)); } } // namespace impeller diff --git a/impeller/aiks/canvas.h b/impeller/aiks/canvas.h index 162d3428f9ec2..b2a5d8c3ff1fa 100644 --- a/impeller/aiks/canvas.h +++ b/impeller/aiks/canvas.h @@ -4,9 +4,9 @@ #pragma once +#include #include #include -#include #include #include "flutter/fml/macros.h" @@ -23,11 +23,6 @@ namespace impeller { class Entity; -struct CanvasStackEntry { - Matrix xformation; - size_t stencil_depth = 0u; -}; - class Canvas { public: Canvas(); @@ -56,6 +51,8 @@ class Canvas { void DrawPath(Path path, Paint paint); + void DrawRect(Rect rect, Paint paint); + void DrawImage(std::shared_ptr image, Point offset, Paint paint); void DrawImageRect(std::shared_ptr image, @@ -72,8 +69,7 @@ class Canvas { Picture EndRecordingAsPicture(); private: - std::stack xformation_stack_; - std::vector passes_; + std::deque xformation_stack_; CanvasPass& GetCurrentPass(); @@ -81,6 +77,8 @@ class Canvas { size_t GetStencilDepth() const; + void Save(bool create_subpass); + FML_DISALLOW_COPY_AND_ASSIGN(Canvas); }; diff --git a/impeller/aiks/canvas_pass.cc b/impeller/aiks/canvas_pass.cc index e861e923e5b78..d7e3773f2a73a 100644 --- a/impeller/aiks/canvas_pass.cc +++ b/impeller/aiks/canvas_pass.cc @@ -26,4 +26,27 @@ const Entity& CanvasPass::GetPostProcessingEntity() const { return post_processing_entity_; } +Rect CanvasPass::GetCoverageRect() const { + std::optional min, max; + for (const auto& entity : ops_) { + auto coverage = entity.GetPath().GetMinMaxCoveragePoints(); + if (!coverage.has_value()) { + continue; + } + if (!min.has_value()) { + min = coverage->first; + } + if (!max.has_value()) { + max = coverage->second; + } + min = min->Min(coverage->first); + max = max->Max(coverage->second); + } + if (!min.has_value() || !max.has_value()) { + return {}; + } + const auto diff = *max - *min; + return {min->x, min->y, diff.x, diff.y}; +} + } // namespace impeller diff --git a/impeller/aiks/canvas_pass.h b/impeller/aiks/canvas_pass.h index 0074492bd4625..5d7494bac133b 100644 --- a/impeller/aiks/canvas_pass.h +++ b/impeller/aiks/canvas_pass.h @@ -5,6 +5,7 @@ #pragma once #include +#include #include "flutter/fml/macros.h" #include "impeller/entity/contents.h" @@ -20,6 +21,8 @@ class CanvasPass { void PushEntity(Entity entity); + Rect GetCoverageRect() const; + const std::vector& GetPassEntities() const; void SetPostProcessingEntity(Entity entity); @@ -31,4 +34,10 @@ class CanvasPass { Entity post_processing_entity_; }; +struct CanvasStackEntry { + Matrix xformation; + size_t stencil_depth = 0u; + std::optional pass; +}; + } // namespace impeller diff --git a/impeller/aiks/picture.h b/impeller/aiks/picture.h index 048a81b98a300..4b5fed22a6e40 100644 --- a/impeller/aiks/picture.h +++ b/impeller/aiks/picture.h @@ -4,8 +4,8 @@ #pragma once +#include #include -#include #include "flutter/fml/macros.h" #include "impeller/aiks/canvas_pass.h" @@ -14,7 +14,7 @@ namespace impeller { struct Picture { - std::vector passes; + std::deque entries; }; } // namespace impeller diff --git a/impeller/aiks/picture_renderer.cc b/impeller/aiks/picture_renderer.cc index a37330bfb3cb3..85f9da018fd47 100644 --- a/impeller/aiks/picture_renderer.cc +++ b/impeller/aiks/picture_renderer.cc @@ -29,10 +29,12 @@ bool PictureRenderer::Render(const Surface& surface, return false; } - for (const auto& pass : picture.passes) { - if (!entity_renderer_.RenderEntities(surface, parent_pass, - pass.GetPassEntities())) { - return false; + for (const auto& entry : picture.entries) { + if (auto pass = entry.pass) { + if (!entity_renderer_.RenderEntities(surface, parent_pass, + pass->GetPassEntities())) { + return false; + } } } diff --git a/impeller/entity/entity.cc b/impeller/entity/entity.cc index 6e315f95ebd1c..9ff908721cb2d 100644 --- a/impeller/entity/entity.cc +++ b/impeller/entity/entity.cc @@ -42,4 +42,8 @@ uint32_t Entity::GetStencilDepth() const { return stencil_depth_; } +void Entity::IncrementStencilDepth(uint32_t increment) { + stencil_depth_ += increment; +} + } // namespace impeller diff --git a/impeller/entity/entity.h b/impeller/entity/entity.h index 89b82d4fe5d01..ef5285c2dbc2e 100644 --- a/impeller/entity/entity.h +++ b/impeller/entity/entity.h @@ -33,6 +33,8 @@ class Entity { void SetStencilDepth(uint32_t stencil_depth); + void IncrementStencilDepth(uint32_t increment); + uint32_t GetStencilDepth() const; private: diff --git a/impeller/geometry/color.h b/impeller/geometry/color.h index dea534b2a3e10..30706da4c678a 100644 --- a/impeller/geometry/color.h +++ b/impeller/geometry/color.h @@ -62,6 +62,10 @@ struct Color { static constexpr Color Blue() { return {0.0, 0.0, 1.0, 1.0}; } + constexpr Color WithAlpha(Scalar new_alpha) const { + return {red, green, blue, new_alpha}; + } + static constexpr Color AliceBlue() { return {240.0 / 255.0, 248.0 / 255.0, 255.0 / 255.0, 1.0}; } diff --git a/impeller/geometry/path.cc b/impeller/geometry/path.cc index facb85173cef7..9959488bdfdf2 100644 --- a/impeller/geometry/path.cc +++ b/impeller/geometry/path.cc @@ -174,9 +174,20 @@ std::vector Path::CreatePolyline( } Rect Path::GetBoundingBox() const { - if (linears_.empty() && quads_.empty() && cubics_.empty()) { + auto min_max = GetMinMaxCoveragePoints(); + if (!min_max.has_value()) { return {}; } + auto min = min_max->first; + auto max = min_max->second; + const auto difference = max - min; + return {min.x, min.y, difference.x, difference.y}; +} + +std::optional> Path::GetMinMaxCoveragePoints() const { + if (linears_.empty() && quads_.empty() && cubics_.empty()) { + return std::nullopt; + } std::optional min, max; @@ -209,9 +220,7 @@ Rect Path::GetBoundingBox() const { clamp(cubic.Extrema()); } - const auto difference = *max - *min; - - return {min->x, min->y, difference.x, difference.y}; + return std::make_pair(min.value(), max.value()); } } // namespace impeller diff --git a/impeller/geometry/path.h b/impeller/geometry/path.h index 1eda541179294..78bee57798deb 100644 --- a/impeller/geometry/path.h +++ b/impeller/geometry/path.h @@ -5,6 +5,7 @@ #pragma once #include +#include #include #include "impeller/geometry/path_component.h" @@ -68,6 +69,8 @@ class Path { Rect GetBoundingBox() const; + std::optional> GetMinMaxCoveragePoints() const; + private: struct ComponentIndexPair { ComponentType type = ComponentType::kLinear; diff --git a/impeller/geometry/point.h b/impeller/geometry/point.h index b015a2ee44ba7..ba12338557c52 100644 --- a/impeller/geometry/point.h +++ b/impeller/geometry/point.h @@ -4,6 +4,7 @@ #pragma once +#include #include #include @@ -83,6 +84,14 @@ struct TPoint { return dx * dx + dy * dy; } + constexpr TPoint Min(const TPoint& p) const { + return {std::min(x, p.x), std::min(y, p.y)}; + } + + constexpr TPoint Max(const TPoint& p) const { + return {std::max(x, p.x), std::max(y, p.y)}; + } + constexpr Type GetDistance(const TPoint& p) const { return sqrt(GetDistanceSquared(p)); } From a56bab4e3afd0a0f18fcf53c0618a24125a3b545 Mon Sep 17 00:00:00 2001 From: Chinmay Garde Date: Mon, 22 Nov 2021 14:56:58 -0800 Subject: [PATCH 207/433] Renderer components no longer need a surface reference to function. --- impeller/aiks/aiks_playground.cc | 4 ++-- impeller/aiks/picture_renderer.cc | 6 ++--- impeller/aiks/picture_renderer.h | 4 +--- impeller/entity/contents.cc | 23 ++++++++----------- impeller/entity/contents.h | 6 ----- impeller/entity/entity_playground.cc | 5 ++-- impeller/entity/entity_renderer.cc | 5 ++-- impeller/entity/entity_renderer.h | 3 +-- impeller/playground/playground.mm | 9 ++++---- impeller/renderer/BUILD.gn | 4 ++-- .../backend/metal/command_buffer_mtl.h | 2 +- .../backend/metal/command_buffer_mtl.mm | 5 ++-- .../renderer/backend/metal/render_pass_mtl.h | 4 ++-- .../renderer/backend/metal/render_pass_mtl.mm | 8 +++---- impeller/renderer/command_buffer.h | 5 ++-- impeller/renderer/render_pass.cc | 11 ++++++++- impeller/renderer/render_pass.h | 11 ++++++++- ...er_pass_descriptor.cc => render_target.cc} | 7 +++++- ...nder_pass_descriptor.h => render_target.h} | 2 ++ impeller/renderer/renderer.cc | 2 +- impeller/renderer/renderer.h | 3 +-- impeller/renderer/renderer_unittests.cc | 15 +++++------- impeller/renderer/surface.h | 2 +- 23 files changed, 75 insertions(+), 71 deletions(-) rename impeller/renderer/{render_pass_descriptor.cc => render_target.cc} (89%) rename impeller/renderer/{render_pass_descriptor.h => render_target.h} (96%) diff --git a/impeller/aiks/aiks_playground.cc b/impeller/aiks/aiks_playground.cc index 8541b193a6bfd..7928ead04cdb4 100644 --- a/impeller/aiks/aiks_playground.cc +++ b/impeller/aiks/aiks_playground.cc @@ -19,8 +19,8 @@ bool AiksPlayground::OpenPlaygroundHere(const Picture& picture) { } return Playground::OpenPlaygroundHere( - [renderer, &picture](const Surface& surface, RenderPass& pass) -> bool { - return renderer->Render(surface, pass, picture); + [renderer, &picture](RenderPass& pass) -> bool { + return renderer->Render(pass, picture); }); } diff --git a/impeller/aiks/picture_renderer.cc b/impeller/aiks/picture_renderer.cc index 85f9da018fd47..33db1008f364c 100644 --- a/impeller/aiks/picture_renderer.cc +++ b/impeller/aiks/picture_renderer.cc @@ -22,16 +22,14 @@ bool PictureRenderer::IsValid() const { return is_valid_; } -bool PictureRenderer::Render(const Surface& surface, - RenderPass& parent_pass, - const Picture& picture) { +bool PictureRenderer::Render(RenderPass& parent_pass, const Picture& picture) { if (!IsValid()) { return false; } for (const auto& entry : picture.entries) { if (auto pass = entry.pass) { - if (!entity_renderer_.RenderEntities(surface, parent_pass, + if (!entity_renderer_.RenderEntities(parent_pass, pass->GetPassEntities())) { return false; } diff --git a/impeller/aiks/picture_renderer.h b/impeller/aiks/picture_renderer.h index 66aa1b5c4826a..269c321b29f70 100644 --- a/impeller/aiks/picture_renderer.h +++ b/impeller/aiks/picture_renderer.h @@ -22,9 +22,7 @@ class PictureRenderer { bool IsValid() const; - [[nodiscard]] bool Render(const Surface& surface, - RenderPass& parent_pass, - const Picture& picture); + [[nodiscard]] bool Render(RenderPass& parent_pass, const Picture& picture); private: EntityRenderer entity_renderer_; diff --git a/impeller/entity/contents.cc b/impeller/entity/contents.cc index 571418652a41a..9d15c959e6cbe 100644 --- a/impeller/entity/contents.cc +++ b/impeller/entity/contents.cc @@ -56,7 +56,6 @@ const std::vector& LinearGradientContents::GetColors() const { bool LinearGradientContents::Render(const ContentRenderer& renderer, const Entity& entity, - const Surface& surface, RenderPass& pass) const { using VS = GradientFillPipeline::VertexShader; using FS = GradientFillPipeline::FragmentShader; @@ -75,8 +74,8 @@ bool LinearGradientContents::Render(const ContentRenderer& renderer, } VS::FrameInfo frame_info; - frame_info.mvp = - Matrix::MakeOrthographic(surface.GetSize()) * entity.GetTransformation(); + frame_info.mvp = Matrix::MakeOrthographic(pass.GetRenderTargetSize()) * + entity.GetTransformation(); FS::GradientInfo gradient_info; gradient_info.start_point = start_point_; @@ -134,7 +133,6 @@ static VertexBuffer CreateSolidFillVertices(const Path& path, bool SolidColorContents::Render(const ContentRenderer& renderer, const Entity& entity, - const Surface& surface, RenderPass& pass) const { if (color_.IsTransparent()) { return true; @@ -150,8 +148,8 @@ bool SolidColorContents::Render(const ContentRenderer& renderer, CreateSolidFillVertices(entity.GetPath(), pass.GetTransientsBuffer())); VS::FrameInfo frame_info; - frame_info.mvp = - Matrix::MakeOrthographic(surface.GetSize()) * entity.GetTransformation(); + frame_info.mvp = Matrix::MakeOrthographic(pass.GetRenderTargetSize()) * + entity.GetTransformation(); frame_info.color = color_; VS::BindFrameInfo(cmd, pass.GetTransientsBuffer().EmplaceUniform(frame_info)); @@ -188,7 +186,6 @@ std::shared_ptr TextureContents::GetTexture() const { bool TextureContents::Render(const ContentRenderer& renderer, const Entity& entity, - const Surface& surface, RenderPass& pass) const { if (texture_ == nullptr) { return true; @@ -234,8 +231,8 @@ bool TextureContents::Render(const ContentRenderer& renderer, auto& host_buffer = pass.GetTransientsBuffer(); VS::FrameInfo frame_info; - frame_info.mvp = - Matrix::MakeOrthographic(surface.GetSize()) * entity.GetTransformation(); + frame_info.mvp = Matrix::MakeOrthographic(pass.GetRenderTargetSize()) * + entity.GetTransformation(); Command cmd; cmd.label = "TextureFill"; @@ -316,7 +313,6 @@ static VertexBuffer CreateSolidStrokeVertices(const Path& path, bool SolidStrokeContents::Render(const ContentRenderer& renderer, const Entity& entity, - const Surface& surface, RenderPass& pass) const { if (color_.IsTransparent() || stroke_size_ <= 0.0) { return true; @@ -325,8 +321,8 @@ bool SolidStrokeContents::Render(const ContentRenderer& renderer, using VS = SolidStrokeVertexShader; VS::FrameInfo frame_info; - frame_info.mvp = - Matrix::MakeOrthographic(surface.GetSize()) * entity.GetTransformation(); + frame_info.mvp = Matrix::MakeOrthographic(pass.GetRenderTargetSize()) * + entity.GetTransformation(); VS::StrokeInfo stroke_info; stroke_info.color = color_; @@ -366,7 +362,6 @@ ClipContents::~ClipContents() = default; bool ClipContents::Render(const ContentRenderer& renderer, const Entity& entity, - const Surface& surface, RenderPass& pass) const { using VS = ClipPipeline::VertexShader; @@ -380,7 +375,7 @@ bool ClipContents::Render(const ContentRenderer& renderer, VS::FrameInfo info; // The color really doesn't matter. info.color = Color::SkyBlue(); - info.mvp = Matrix::MakeOrthographic(surface.GetSize()); + info.mvp = Matrix::MakeOrthographic(pass.GetRenderTargetSize()); VS::BindFrameInfo(cmd, pass.GetTransientsBuffer().EmplaceUniform(info)); diff --git a/impeller/entity/contents.h b/impeller/entity/contents.h index 826bf29040763..381a11f5f011f 100644 --- a/impeller/entity/contents.h +++ b/impeller/entity/contents.h @@ -28,7 +28,6 @@ class Contents { virtual bool Render(const ContentRenderer& renderer, const Entity& entity, - const Surface& surface, RenderPass& pass) const = 0; private: @@ -44,7 +43,6 @@ class LinearGradientContents final : public Contents { // |Contents| bool Render(const ContentRenderer& renderer, const Entity& entity, - const Surface& surface, RenderPass& pass) const override; void SetEndPoints(Point start_point, Point end_point); @@ -76,7 +74,6 @@ class SolidColorContents final : public Contents { // |Contents| bool Render(const ContentRenderer& renderer, const Entity& entity, - const Surface& surface, RenderPass& pass) const override; private: @@ -102,7 +99,6 @@ class TextureContents final : public Contents { // |Contents| bool Render(const ContentRenderer& renderer, const Entity& entity, - const Surface& surface, RenderPass& pass) const override; public: @@ -129,7 +125,6 @@ class SolidStrokeContents final : public Contents { // |Contents| bool Render(const ContentRenderer& renderer, const Entity& entity, - const Surface& surface, RenderPass& pass) const override; private: @@ -148,7 +143,6 @@ class ClipContents final : public Contents { // |Contents| bool Render(const ContentRenderer& renderer, const Entity& entity, - const Surface& surface, RenderPass& pass) const override; private: diff --git a/impeller/entity/entity_playground.cc b/impeller/entity/entity_playground.cc index e643bd81488c7..835cf655fbaa5 100644 --- a/impeller/entity/entity_playground.cc +++ b/impeller/entity/entity_playground.cc @@ -17,10 +17,9 @@ bool EntityPlayground::OpenPlaygroundHere(Entity entity) { return false; } } - Renderer::RenderCallback callback = [&](const Surface& surface, - RenderPass& pass) -> bool { + Renderer::RenderCallback callback = [&](RenderPass& pass) -> bool { std::vector entities = {entity}; - return renderer_->RenderEntities(surface, pass, entities); + return renderer_->RenderEntities(pass, entities); }; return Playground::OpenPlaygroundHere(callback); } diff --git a/impeller/entity/entity_renderer.cc b/impeller/entity/entity_renderer.cc index 2531ff833b3ce..9572080b059f4 100644 --- a/impeller/entity/entity_renderer.cc +++ b/impeller/entity/entity_renderer.cc @@ -29,8 +29,7 @@ bool EntityRenderer::IsValid() const { return is_valid_; } -bool EntityRenderer::RenderEntities(const Surface& surface, - RenderPass& parent_pass, +bool EntityRenderer::RenderEntities(RenderPass& parent_pass, const std::vector& entities) { if (!IsValid()) { return false; @@ -38,7 +37,7 @@ bool EntityRenderer::RenderEntities(const Surface& surface, for (const auto& entity : entities) { if (auto contents = entity.GetContents()) { - if (!contents->Render(*content_renderer_, entity, surface, parent_pass)) { + if (!contents->Render(*content_renderer_, entity, parent_pass)) { return false; } } diff --git a/impeller/entity/entity_renderer.h b/impeller/entity/entity_renderer.h index b87b64572ef29..e453100ce0744 100644 --- a/impeller/entity/entity_renderer.h +++ b/impeller/entity/entity_renderer.h @@ -23,8 +23,7 @@ class EntityRenderer { bool IsValid() const; - [[nodiscard]] bool RenderEntities(const Surface& surface, - RenderPass& parent_pass, + [[nodiscard]] bool RenderEntities(RenderPass& parent_pass, const std::vector& entities); private: diff --git a/impeller/playground/playground.mm b/impeller/playground/playground.mm index 00c7d92c423ff..a325c0be6a12f 100644 --- a/impeller/playground/playground.mm +++ b/impeller/playground/playground.mm @@ -180,11 +180,10 @@ static void PlaygroundKeyCallback(GLFWwindow* window, Surface surface(desc); - Renderer::RenderCallback wrapped_callback = - [render_callback](const auto& surface, auto& pass) { - pass.SetLabel("Playground Main Render Pass"); - return render_callback(surface, pass); - }; + Renderer::RenderCallback wrapped_callback = [render_callback](auto& pass) { + pass.SetLabel("Playground Main Render Pass"); + return render_callback(pass); + }; if (!renderer_.Render(surface, wrapped_callback)) { FML_LOG(ERROR) << "Could not render into the surface."; diff --git a/impeller/renderer/BUILD.gn b/impeller/renderer/BUILD.gn index be4fe29aa4006..cb175b91e3b41 100644 --- a/impeller/renderer/BUILD.gn +++ b/impeller/renderer/BUILD.gn @@ -72,8 +72,8 @@ impeller_component("renderer") { "range.h", "render_pass.h", "render_pass.cc", - "render_pass_descriptor.h", - "render_pass_descriptor.cc", + "render_target.h", + "render_target.cc", "renderer.h", "renderer.cc", "sampler.h", diff --git a/impeller/renderer/backend/metal/command_buffer_mtl.h b/impeller/renderer/backend/metal/command_buffer_mtl.h index 9b438455f4923..46b398efc1db1 100644 --- a/impeller/renderer/backend/metal/command_buffer_mtl.h +++ b/impeller/renderer/backend/metal/command_buffer_mtl.h @@ -34,7 +34,7 @@ class CommandBufferMTL final : public CommandBuffer { // |CommandBuffer| std::shared_ptr CreateRenderPass( - const RenderTarget& desc) const override; + RenderTarget target) const override; FML_DISALLOW_COPY_AND_ASSIGN(CommandBufferMTL); }; diff --git a/impeller/renderer/backend/metal/command_buffer_mtl.mm b/impeller/renderer/backend/metal/command_buffer_mtl.mm index 5b4f7628cc39d..1065008dba461 100644 --- a/impeller/renderer/backend/metal/command_buffer_mtl.mm +++ b/impeller/renderer/backend/metal/command_buffer_mtl.mm @@ -53,12 +53,13 @@ } std::shared_ptr CommandBufferMTL::CreateRenderPass( - const RenderTarget& desc) const { + RenderTarget target) const { if (!buffer_) { return nullptr; } - auto pass = std::shared_ptr(new RenderPassMTL(buffer_, desc)); + auto pass = std::shared_ptr( + new RenderPassMTL(buffer_, std::move(target))); if (!pass->IsValid()) { return nullptr; } diff --git a/impeller/renderer/backend/metal/render_pass_mtl.h b/impeller/renderer/backend/metal/render_pass_mtl.h index 530a60c2b0a2d..d177c688cc613 100644 --- a/impeller/renderer/backend/metal/render_pass_mtl.h +++ b/impeller/renderer/backend/metal/render_pass_mtl.h @@ -8,7 +8,7 @@ #include "flutter/fml/macros.h" #include "impeller/renderer/render_pass.h" -#include "impeller/renderer/render_pass_descriptor.h" +#include "impeller/renderer/render_target.h" namespace impeller { @@ -27,7 +27,7 @@ class RenderPassMTL final : public RenderPass { std::string label_; bool is_valid_ = false; - RenderPassMTL(id buffer, const RenderTarget& desc); + RenderPassMTL(id buffer, RenderTarget target); // |RenderPass| bool IsValid() const override; diff --git a/impeller/renderer/backend/metal/render_pass_mtl.mm b/impeller/renderer/backend/metal/render_pass_mtl.mm index 3737bf25cc8ce..399749b1b0081 100644 --- a/impeller/renderer/backend/metal/render_pass_mtl.mm +++ b/impeller/renderer/backend/metal/render_pass_mtl.mm @@ -92,10 +92,10 @@ static bool ConfigureStencilAttachment( return result; } -RenderPassMTL::RenderPassMTL(id buffer, - const RenderTarget& desc) - : buffer_(buffer), - desc_(ToMTLRenderPassDescriptor(desc)), +RenderPassMTL::RenderPassMTL(id buffer, RenderTarget target) + : RenderPass(std::move(target)), + buffer_(buffer), + desc_(ToMTLRenderPassDescriptor(GetRenderTarget())), transients_buffer_(HostBuffer::Create()) { if (!buffer_ || !desc_) { return; diff --git a/impeller/renderer/command_buffer.h b/impeller/renderer/command_buffer.h index 554d595c2412f..a1240badbfaeb 100644 --- a/impeller/renderer/command_buffer.h +++ b/impeller/renderer/command_buffer.h @@ -54,12 +54,13 @@ class CommandBuffer { //---------------------------------------------------------------------------- /// @brief Create a render pass to record render commands into. /// - /// @param[in] desc The description of the render pass. + /// @param[in] desc The description of the render target this pass will + /// target. /// /// @return A valid render pass or null. /// virtual std::shared_ptr CreateRenderPass( - const RenderTarget& desc) const = 0; + RenderTarget render_target) const = 0; protected: CommandBuffer(); diff --git a/impeller/renderer/render_pass.cc b/impeller/renderer/render_pass.cc index 93c4ead3bf638..411891c2c3f57 100644 --- a/impeller/renderer/render_pass.cc +++ b/impeller/renderer/render_pass.cc @@ -6,8 +6,17 @@ namespace impeller { -RenderPass::RenderPass() = default; +RenderPass::RenderPass(RenderTarget target) + : render_target_(std::move(target)) {} RenderPass::~RenderPass() = default; +const RenderTarget& RenderPass::GetRenderTarget() const { + return render_target_; +} + +ISize RenderPass::GetRenderTargetSize() const { + return render_target_.GetRenderTargetSize(); +} + } // namespace impeller diff --git a/impeller/renderer/render_pass.h b/impeller/renderer/render_pass.h index d0b2ee8c921f1..588250a631269 100644 --- a/impeller/renderer/render_pass.h +++ b/impeller/renderer/render_pass.h @@ -7,6 +7,7 @@ #include #include "impeller/renderer/command.h" +#include "impeller/renderer/render_target.h" namespace impeller { @@ -20,10 +21,16 @@ class Allocator; /// Render passes can be obtained from the command buffer in which /// the pass is meant to encode commands into. /// +/// @see `CommandBuffer` +/// class RenderPass { public: virtual ~RenderPass(); + const RenderTarget& GetRenderTarget() const; + + ISize GetRenderTargetSize() const; + virtual bool IsValid() const = 0; virtual void SetLabel(std::string label) = 0; @@ -52,9 +59,11 @@ class RenderPass { virtual bool EncodeCommands(Allocator& transients_allocator) const = 0; protected: - RenderPass(); + RenderPass(RenderTarget target); private: + const RenderTarget render_target_; + FML_DISALLOW_COPY_AND_ASSIGN(RenderPass); }; diff --git a/impeller/renderer/render_pass_descriptor.cc b/impeller/renderer/render_target.cc similarity index 89% rename from impeller/renderer/render_pass_descriptor.cc rename to impeller/renderer/render_target.cc index f8dbb732cdd41..db533e8674e85 100644 --- a/impeller/renderer/render_pass_descriptor.cc +++ b/impeller/renderer/render_target.cc @@ -2,7 +2,7 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -#include "impeller/renderer/render_pass_descriptor.h" +#include "impeller/renderer/render_target.h" #include "impeller/renderer/texture.h" @@ -29,6 +29,11 @@ std::optional RenderTarget::GetColorAttachmentSize(size_t index) const { return found->second.texture->GetSize(); } +ISize RenderTarget::GetRenderTargetSize() const { + auto size = GetColorAttachmentSize(0u); + return size.has_value() ? size.value() : ISize{}; +} + RenderTarget& RenderTarget::SetColorAttachment(ColorAttachment attachment, size_t index) { if (attachment) { diff --git a/impeller/renderer/render_pass_descriptor.h b/impeller/renderer/render_target.h similarity index 96% rename from impeller/renderer/render_pass_descriptor.h rename to impeller/renderer/render_target.h index 5ace1b2aca138..8a98442439bea 100644 --- a/impeller/renderer/render_pass_descriptor.h +++ b/impeller/renderer/render_target.h @@ -21,6 +21,8 @@ class RenderTarget { bool HasColorAttachment(size_t index) const; + ISize GetRenderTargetSize() const; + std::optional GetColorAttachmentSize(size_t index) const; RenderTarget& SetColorAttachment(ColorAttachment attachment, size_t index); diff --git a/impeller/renderer/renderer.cc b/impeller/renderer/renderer.cc index bc3e3e4657b15..f7e5662f99f50 100644 --- a/impeller/renderer/renderer.cc +++ b/impeller/renderer/renderer.cc @@ -51,7 +51,7 @@ bool Renderer::Render(const Surface& surface, return false; } - if (render_callback && !render_callback(surface, *render_pass)) { + if (render_callback && !render_callback(*render_pass)) { return false; } diff --git a/impeller/renderer/renderer.h b/impeller/renderer/renderer.h index ebd3930aec6cc..74e2d8c5222bf 100644 --- a/impeller/renderer/renderer.h +++ b/impeller/renderer/renderer.h @@ -21,8 +21,7 @@ class RenderPass; class Renderer { public: - using RenderCallback = - std::function; + using RenderCallback = std::function; Renderer(std::shared_ptr context); diff --git a/impeller/renderer/renderer_unittests.cc b/impeller/renderer/renderer_unittests.cc index d165aa7a3bb7a..a388851b76321 100644 --- a/impeller/renderer/renderer_unittests.cc +++ b/impeller/renderer/renderer_unittests.cc @@ -60,8 +60,7 @@ TEST_F(RendererTest, CanCreateBoxPrimitive) { ASSERT_TRUE(bridge && boston); auto sampler = context->GetSamplerLibrary()->GetSampler({}); ASSERT_TRUE(sampler); - Renderer::RenderCallback callback = [&](const Surface& surface, - RenderPass& pass) { + Renderer::RenderCallback callback = [&](RenderPass& pass) { Command cmd; cmd.label = "Box"; cmd.pipeline = box_pipeline; @@ -69,7 +68,7 @@ TEST_F(RendererTest, CanCreateBoxPrimitive) { cmd.BindVertices(vertex_buffer); VS::UniformBuffer uniforms; - uniforms.mvp = Matrix::MakeOrthographic(surface.GetSize()); + uniforms.mvp = Matrix::MakeOrthographic(pass.GetRenderTargetSize()); VS::BindUniformBuffer(cmd, pass.GetTransientsBuffer().EmplaceUniform(uniforms)); @@ -125,8 +124,7 @@ TEST_F(RendererTest, CanRenderMultiplePrimitives) { auto sampler = context->GetSamplerLibrary()->GetSampler({}); ASSERT_TRUE(sampler); - Renderer::RenderCallback callback = [&](const Surface& surface, - RenderPass& pass) { + Renderer::RenderCallback callback = [&](RenderPass& pass) { Command cmd; cmd.label = "Box"; cmd.pipeline = box_pipeline; @@ -149,7 +147,7 @@ TEST_F(RendererTest, CanRenderMultiplePrimitives) { for (size_t i = 0; i < 1; i++) { for (size_t j = 0; j < 1; j++) { VS::UniformBuffer uniforms; - uniforms.mvp = Matrix::MakeOrthographic(surface.GetSize()) * + uniforms.mvp = Matrix::MakeOrthographic(pass.GetRenderTargetSize()) * Matrix::MakeTranslation({i * 50.0f, j * 50.0f, 0.0f}); VS::BindUniformBuffer( cmd, pass.GetTransientsBuffer().EmplaceUniform(uniforms)); @@ -301,8 +299,7 @@ TEST_F(RendererTest, CanRenderPath) { auto sampler = context->GetSamplerLibrary()->GetSampler({}); ASSERT_TRUE(sampler); - Renderer::RenderCallback callback = [&](const Surface& surface, - RenderPass& pass) { + Renderer::RenderCallback callback = [&](RenderPass& pass) { Command cmd; cmd.label = "Box"; cmd.pipeline = box_pipeline.WaitAndGet(); @@ -324,7 +321,7 @@ TEST_F(RendererTest, CanRenderPath) { cmd.winding = tessellator.GetFrontFaceWinding(); VS::UniformBuffer uniforms; - uniforms.mvp = Matrix::MakeOrthographic(surface.GetSize()); + uniforms.mvp = Matrix::MakeOrthographic(pass.GetRenderTargetSize()); VS::BindUniformBuffer(cmd, pass.GetTransientsBuffer().EmplaceUniform(uniforms)); if (!pass.AddCommand(cmd)) { diff --git a/impeller/renderer/surface.h b/impeller/renderer/surface.h index 7a7515ffe4029..db6fc2283a5fc 100644 --- a/impeller/renderer/surface.h +++ b/impeller/renderer/surface.h @@ -10,7 +10,7 @@ #include "flutter/fml/macros.h" #include "impeller/renderer/context.h" #include "impeller/renderer/render_pass.h" -#include "impeller/renderer/render_pass_descriptor.h" +#include "impeller/renderer/render_target.h" namespace impeller { From bf2fc9b5fef9243a82d573651cb547a345242fa0 Mon Sep 17 00:00:00 2001 From: Chinmay Garde Date: Mon, 22 Nov 2021 15:40:39 -0800 Subject: [PATCH 208/433] WIP on creating utils for creating offscreen render targets. --- impeller/playground/playground.mm | 19 +++---- .../renderer/backend/metal/render_pass_mtl.mm | 3 ++ impeller/renderer/render_target.cc | 50 +++++++++++++++++++ impeller/renderer/render_target.h | 6 +++ 4 files changed, 69 insertions(+), 9 deletions(-) diff --git a/impeller/playground/playground.mm b/impeller/playground/playground.mm index a325c0be6a12f..1fcfc4790febf 100644 --- a/impeller/playground/playground.mm +++ b/impeller/playground/playground.mm @@ -144,28 +144,29 @@ static void PlaygroundKeyCallback(GLFWwindow* window, return false; } - TextureDescriptor color0_desc; - color0_desc.format = PixelFormat::kB8G8R8A8UNormInt; - color0_desc.size = { + TextureDescriptor color0_tex; + color0_tex.format = PixelFormat::kB8G8R8A8UNormInt; + color0_tex.size = { static_cast(current_drawable.texture.width), static_cast(current_drawable.texture.height)}; + color0_tex.usage = static_cast(TextureUsage::kRenderTarget); ColorAttachment color0; color0.texture = - std::make_shared(color0_desc, current_drawable.texture); + std::make_shared(color0_tex, current_drawable.texture); color0.clear_color = Color::SkyBlue(); color0.load_action = LoadAction::kClear; color0.store_action = StoreAction::kStore; - TextureDescriptor stencil_texture_descriptor; - stencil_texture_descriptor.format = PixelFormat::kD32FloatS8UNormInt; - stencil_texture_descriptor.size = color0_desc.size; - stencil_texture_descriptor.usage = + TextureDescriptor stencil0_tex; + stencil0_tex.format = PixelFormat::kD32FloatS8UNormInt; + stencil0_tex.size = color0_tex.size; + stencil0_tex.usage = static_cast(TextureUsage::kShaderRead) | static_cast(TextureUsage::kShaderWrite); auto stencil_texture = renderer_.GetContext()->GetPermanentsAllocator()->CreateTexture( - StorageMode::kDeviceTransient, stencil_texture_descriptor); + StorageMode::kDeviceTransient, stencil0_tex); stencil_texture->SetLabel("PlaygroundMainStencil"); StencilAttachment stencil0; diff --git a/impeller/renderer/backend/metal/render_pass_mtl.mm b/impeller/renderer/backend/metal/render_pass_mtl.mm index 399749b1b0081..5ff8392faa476 100644 --- a/impeller/renderer/backend/metal/render_pass_mtl.mm +++ b/impeller/renderer/backend/metal/render_pass_mtl.mm @@ -114,6 +114,9 @@ static bool ConfigureStencilAttachment( } void RenderPassMTL::SetLabel(std::string label) { + if (label.empty()) { + return; + } label_ = std::move(label); transients_buffer_->SetLabel(SPrintF("%s Transients", label_.c_str())); } diff --git a/impeller/renderer/render_target.cc b/impeller/renderer/render_target.cc index db533e8674e85..190091e0bd945 100644 --- a/impeller/renderer/render_target.cc +++ b/impeller/renderer/render_target.cc @@ -4,6 +4,8 @@ #include "impeller/renderer/render_target.h" +#include "impeller/renderer/allocator.h" +#include "impeller/renderer/context.h" #include "impeller/renderer/texture.h" namespace impeller { @@ -70,4 +72,52 @@ const std::optional& RenderTarget::GetStencilAttachment() return stencil_; } +RenderTarget RenderTarget::CreateOffscreen(const Context& context, + ISize size, + std::string label) { + TextureDescriptor color_tex0; + color_tex0.format = PixelFormat::kB8G8R8A8UNormInt; + color_tex0.size = size; + color_tex0.usage = static_cast(TextureUsage::kRenderTarget); + + TextureDescriptor stencil_tex0; + stencil_tex0.format = PixelFormat::kD32FloatS8UNormInt; + stencil_tex0.size = size; + stencil_tex0.usage = + static_cast(TextureUsage::kShaderRead) | + static_cast(TextureUsage::kShaderWrite); + + ColorAttachment color0; + color0.clear_color = Color::BlackTransparent(); + color0.load_action = LoadAction::kClear; + color0.store_action = StoreAction::kStore; + color0.texture = context.GetPermanentsAllocator()->CreateTexture( + StorageMode::kDevicePrivate, color_tex0); + + if (!color0.texture) { + return {}; + } + + color0.texture->SetLabel(label); + + StencilAttachment stencil0; + stencil0.load_action = LoadAction::kClear; + stencil0.store_action = StoreAction::kDontCare; + stencil0.clear_stencil = 0u; + stencil0.texture = context.GetPermanentsAllocator()->CreateTexture( + StorageMode::kDeviceTransient, stencil_tex0); + + if (!stencil0.texture) { + return {}; + } + + stencil0.texture->SetLabel(label); + + RenderTarget target; + target.SetColorAttachment(std::move(color0), 0u); + target.SetStencilAttachment(std::move(stencil0)); + + return target; +} + } // namespace impeller diff --git a/impeller/renderer/render_target.h b/impeller/renderer/render_target.h index 8a98442439bea..66ecf2cc98a7c 100644 --- a/impeller/renderer/render_target.h +++ b/impeller/renderer/render_target.h @@ -13,8 +13,14 @@ namespace impeller { +class Context; + class RenderTarget { public: + static RenderTarget CreateOffscreen(const Context& context, + ISize size, + std::string label = "Offscreen"); + RenderTarget(); ~RenderTarget(); From 46003e3e3e27913f27f53cb4070650f9fcc095e3 Mon Sep 17 00:00:00 2001 From: Chinmay Garde Date: Mon, 22 Nov 2021 15:45:42 -0800 Subject: [PATCH 209/433] Fix bug in texture usage mask. --- impeller/playground/playground.mm | 3 +-- impeller/renderer/formats.h | 8 ++++---- impeller/renderer/render_target.cc | 3 +-- 3 files changed, 6 insertions(+), 8 deletions(-) diff --git a/impeller/playground/playground.mm b/impeller/playground/playground.mm index 1fcfc4790febf..61f76d847a6b3 100644 --- a/impeller/playground/playground.mm +++ b/impeller/playground/playground.mm @@ -162,8 +162,7 @@ static void PlaygroundKeyCallback(GLFWwindow* window, stencil0_tex.format = PixelFormat::kD32FloatS8UNormInt; stencil0_tex.size = color0_tex.size; stencil0_tex.usage = - static_cast(TextureUsage::kShaderRead) | - static_cast(TextureUsage::kShaderWrite); + static_cast(TextureUsage::kRenderTarget); auto stencil_texture = renderer_.GetContext()->GetPermanentsAllocator()->CreateTexture( StorageMode::kDeviceTransient, stencil0_tex); diff --git a/impeller/renderer/formats.h b/impeller/renderer/formats.h index 2e001f6552797..7088e16dcba43 100644 --- a/impeller/renderer/formats.h +++ b/impeller/renderer/formats.h @@ -94,10 +94,10 @@ enum class StoreAction { using TextureUsageMask = uint64_t; enum class TextureUsage : TextureUsageMask { - kUnknown, - kShaderRead, - kShaderWrite, - kRenderTarget, + kUnknown = 0, + kShaderRead = 1 << 0, + kShaderWrite = 1 << 1, + kRenderTarget = 1 << 2, }; enum class WindingOrder { diff --git a/impeller/renderer/render_target.cc b/impeller/renderer/render_target.cc index 190091e0bd945..b2710a1f6c917 100644 --- a/impeller/renderer/render_target.cc +++ b/impeller/renderer/render_target.cc @@ -84,8 +84,7 @@ RenderTarget RenderTarget::CreateOffscreen(const Context& context, stencil_tex0.format = PixelFormat::kD32FloatS8UNormInt; stencil_tex0.size = size; stencil_tex0.usage = - static_cast(TextureUsage::kShaderRead) | - static_cast(TextureUsage::kShaderWrite); + static_cast(TextureUsage::kRenderTarget); ColorAttachment color0; color0.clear_color = Color::BlackTransparent(); From 8c78d91e13b9330f3e9e5bba51f85606d68814e2 Mon Sep 17 00:00:00 2001 From: Chinmay Garde Date: Tue, 23 Nov 2021 12:54:19 -0800 Subject: [PATCH 210/433] Setup aiks for canvas subpasses. --- impeller/aiks/BUILD.gn | 4 +- impeller/aiks/aiks_playground.cc | 11 +++-- impeller/aiks/aiks_renderer.cc | 49 +++++++++++++++++++ .../aiks_renderer.h} | 17 +++---- impeller/aiks/canvas.cc | 2 +- impeller/aiks/canvas_pass.cc | 37 +++++++++----- impeller/aiks/canvas_pass.h | 17 +++++-- impeller/aiks/picture_renderer.cc | 42 ---------------- impeller/aiks/picture_renderer.h | 34 ------------- impeller/entity/BUILD.gn | 2 - impeller/entity/entity.cc | 11 +++++ impeller/entity/entity.h | 5 ++ impeller/entity/entity_playground.cc | 13 +++-- impeller/entity/entity_playground.h | 3 -- impeller/entity/entity_renderer.cc | 49 ------------------- 15 files changed, 125 insertions(+), 171 deletions(-) create mode 100644 impeller/aiks/aiks_renderer.cc rename impeller/{entity/entity_renderer.h => aiks/aiks_renderer.h} (56%) delete mode 100644 impeller/aiks/picture_renderer.cc delete mode 100644 impeller/aiks/picture_renderer.h delete mode 100644 impeller/entity/entity_renderer.cc diff --git a/impeller/aiks/BUILD.gn b/impeller/aiks/BUILD.gn index 09fde1849cdfa..0c6bc01a00578 100644 --- a/impeller/aiks/BUILD.gn +++ b/impeller/aiks/BUILD.gn @@ -6,6 +6,8 @@ import("../tools/impeller.gni") impeller_component("aiks") { sources = [ + "aiks_renderer.cc", + "aiks_renderer.h", "canvas.cc", "canvas.h", "canvas_pass.cc", @@ -18,8 +20,6 @@ impeller_component("aiks") { "picture.h", "picture_recorder.cc", "picture_recorder.h", - "picture_renderer.cc", - "picture_renderer.h", ] public_deps = [ diff --git a/impeller/aiks/aiks_playground.cc b/impeller/aiks/aiks_playground.cc index 7928ead04cdb4..32c301b4ca913 100644 --- a/impeller/aiks/aiks_playground.cc +++ b/impeller/aiks/aiks_playground.cc @@ -4,7 +4,7 @@ #include "impeller/aiks/aiks_playground.h" -#include "impeller/aiks/picture_renderer.h" +#include "impeller/aiks/aiks_renderer.h" namespace impeller { @@ -13,14 +13,15 @@ AiksPlayground::AiksPlayground() = default; AiksPlayground::~AiksPlayground() = default; bool AiksPlayground::OpenPlaygroundHere(const Picture& picture) { - auto renderer = std::make_shared(GetContext()); - if (!renderer) { + AiksRenderer renderer(GetContext()); + + if (!renderer.IsValid()) { return false; } return Playground::OpenPlaygroundHere( - [renderer, &picture](RenderPass& pass) -> bool { - return renderer->Render(pass, picture); + [&renderer, &picture](RenderPass& pass) -> bool { + return renderer.Render(picture, pass); }); } diff --git a/impeller/aiks/aiks_renderer.cc b/impeller/aiks/aiks_renderer.cc new file mode 100644 index 0000000000000..f491e4d083882 --- /dev/null +++ b/impeller/aiks/aiks_renderer.cc @@ -0,0 +1,49 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "impeller/aiks/aiks_renderer.h" + +#include "impeller/aiks/picture.h" + +namespace impeller { + +AiksRenderer::AiksRenderer(std::shared_ptr context) + : context_(std::move(context)) { + if (!context_ || !context_->IsValid()) { + return; + } + + content_renderer_ = std::make_unique(context_); + if (!content_renderer_->IsValid()) { + return; + } + + is_valid_ = true; +} + +AiksRenderer::~AiksRenderer() = default; + +bool AiksRenderer::IsValid() const { + return is_valid_; +} + +bool AiksRenderer::Render(const Picture& picture, RenderPass& parent_pass) { + if (!IsValid()) { + return false; + } + + for (const auto& entry : picture.entries) { + if (!entry.pass.has_value()) { + continue; + ; + } + + if (!entry.pass->Render(*content_renderer_, parent_pass)) { + return false; + } + } + return true; +} + +} // namespace impeller diff --git a/impeller/entity/entity_renderer.h b/impeller/aiks/aiks_renderer.h similarity index 56% rename from impeller/entity/entity_renderer.h rename to impeller/aiks/aiks_renderer.h index e453100ce0744..aa09a7ab4746b 100644 --- a/impeller/entity/entity_renderer.h +++ b/impeller/aiks/aiks_renderer.h @@ -7,31 +7,30 @@ #include #include "flutter/fml/macros.h" -#include "impeller/entity/entity.h" +#include "impeller/entity/content_renderer.h" #include "impeller/renderer/context.h" -#include "impeller/renderer/surface.h" namespace impeller { -class ContentRenderer; +struct Picture; +class RenderPass; -class EntityRenderer { +class AiksRenderer { public: - EntityRenderer(std::shared_ptr context); + AiksRenderer(std::shared_ptr context); - ~EntityRenderer(); + ~AiksRenderer(); bool IsValid() const; - [[nodiscard]] bool RenderEntities(RenderPass& parent_pass, - const std::vector& entities); + bool Render(const Picture& picture, RenderPass& parent_pass); private: std::shared_ptr context_; std::unique_ptr content_renderer_; bool is_valid_ = false; - FML_DISALLOW_COPY_AND_ASSIGN(EntityRenderer); + FML_DISALLOW_COPY_AND_ASSIGN(AiksRenderer); }; } // namespace impeller diff --git a/impeller/aiks/canvas.cc b/impeller/aiks/canvas.cc index 4d9c3d99a5f64..87ec026a6321f 100644 --- a/impeller/aiks/canvas.cc +++ b/impeller/aiks/canvas.cc @@ -94,7 +94,7 @@ void Canvas::DrawPicture(const Picture& picture) { for (const auto& stack_entry : picture.entries) { auto new_stack_entry = stack_entry; if (auto pass = new_stack_entry.pass) { - for (auto entity : pass->GetPassEntities()) { + for (auto entity : pass->GetEntities()) { entity.IncrementStencilDepth(GetStencilDepth()); entity.SetTransformation(GetCurrentTransformation() * entity.GetTransformation()); diff --git a/impeller/aiks/canvas_pass.cc b/impeller/aiks/canvas_pass.cc index d7e3773f2a73a..edada76473835 100644 --- a/impeller/aiks/canvas_pass.cc +++ b/impeller/aiks/canvas_pass.cc @@ -4,6 +4,8 @@ #include "impeller/aiks/canvas_pass.h" +#include "impeller/entity/content_renderer.h" + namespace impeller { CanvasPass::CanvasPass() = default; @@ -11,24 +13,16 @@ CanvasPass::CanvasPass() = default; CanvasPass::~CanvasPass() = default; void CanvasPass::PushEntity(Entity entity) { - ops_.emplace_back(std::move(entity)); -} - -const std::vector& CanvasPass::GetPassEntities() const { - return ops_; + entities_.emplace_back(std::move(entity)); } -void CanvasPass::SetPostProcessingEntity(Entity entity) { - post_processing_entity_ = std::move(entity); -} - -const Entity& CanvasPass::GetPostProcessingEntity() const { - return post_processing_entity_; +const std::vector& CanvasPass::GetEntities() const { + return entities_; } Rect CanvasPass::GetCoverageRect() const { std::optional min, max; - for (const auto& entity : ops_) { + for (const auto& entity : entities_) { auto coverage = entity.GetPath().GetMinMaxCoveragePoints(); if (!coverage.has_value()) { continue; @@ -49,4 +43,23 @@ Rect CanvasPass::GetCoverageRect() const { return {min->x, min->y, diff.x, diff.y}; } +const CanvasPass::Subpasses& CanvasPass::GetSubpasses() const { + return subpasses_; +} + +bool CanvasPass::AddSubpass(CanvasPass pass) { + subpasses_.emplace_back(std::move(pass)); + return true; +} + +bool CanvasPass::Render(ContentRenderer& renderer, + RenderPass& parent_pass) const { + for (const auto& entity : entities_) { + if (!entity.Render(renderer, parent_pass)) { + return false; + } + } + return true; +} + } // namespace impeller diff --git a/impeller/aiks/canvas_pass.h b/impeller/aiks/canvas_pass.h index 5d7494bac133b..3aa7cb825c3eb 100644 --- a/impeller/aiks/canvas_pass.h +++ b/impeller/aiks/canvas_pass.h @@ -6,6 +6,7 @@ #include #include +#include #include "flutter/fml/macros.h" #include "impeller/entity/contents.h" @@ -13,8 +14,12 @@ namespace impeller { +class ContentRenderer; + class CanvasPass { public: + using Subpasses = std::vector; + CanvasPass(); ~CanvasPass(); @@ -23,15 +28,17 @@ class CanvasPass { Rect GetCoverageRect() const; - const std::vector& GetPassEntities() const; + const std::vector& GetEntities() const; + + const Subpasses& GetSubpasses() const; - void SetPostProcessingEntity(Entity entity); + bool AddSubpass(CanvasPass pass); - const Entity& GetPostProcessingEntity() const; + bool Render(ContentRenderer& renderer, RenderPass& parent_pass) const; private: - std::vector ops_; - Entity post_processing_entity_; + std::vector entities_; + Subpasses subpasses_; }; struct CanvasStackEntry { diff --git a/impeller/aiks/picture_renderer.cc b/impeller/aiks/picture_renderer.cc deleted file mode 100644 index 33db1008f364c..0000000000000 --- a/impeller/aiks/picture_renderer.cc +++ /dev/null @@ -1,42 +0,0 @@ -// Copyright 2013 The Flutter Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#include "impeller/aiks/picture_renderer.h" - -#include "impeller/aiks/picture.h" - -namespace impeller { - -PictureRenderer::PictureRenderer(std::shared_ptr context) - : entity_renderer_(std::move(context)) { - if (!entity_renderer_.IsValid()) { - return; - } - is_valid_ = true; -} - -PictureRenderer::~PictureRenderer() = default; - -bool PictureRenderer::IsValid() const { - return is_valid_; -} - -bool PictureRenderer::Render(RenderPass& parent_pass, const Picture& picture) { - if (!IsValid()) { - return false; - } - - for (const auto& entry : picture.entries) { - if (auto pass = entry.pass) { - if (!entity_renderer_.RenderEntities(parent_pass, - pass->GetPassEntities())) { - return false; - } - } - } - - return true; -} - -} // namespace impeller diff --git a/impeller/aiks/picture_renderer.h b/impeller/aiks/picture_renderer.h deleted file mode 100644 index 269c321b29f70..0000000000000 --- a/impeller/aiks/picture_renderer.h +++ /dev/null @@ -1,34 +0,0 @@ -// Copyright 2013 The Flutter Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#pragma once - -#include "flutter/fml/macros.h" -#include "impeller/entity/entity_renderer.h" - -namespace impeller { - -class Surface; -class RenderPass; -class Context; -struct Picture; - -class PictureRenderer { - public: - PictureRenderer(std::shared_ptr context); - - ~PictureRenderer(); - - bool IsValid() const; - - [[nodiscard]] bool Render(RenderPass& parent_pass, const Picture& picture); - - private: - EntityRenderer entity_renderer_; - bool is_valid_ = false; - - FML_DISALLOW_COPY_AND_ASSIGN(PictureRenderer); -}; - -} // namespace impeller diff --git a/impeller/entity/BUILD.gn b/impeller/entity/BUILD.gn index e3c1d596ef58a..6dac3568748c8 100644 --- a/impeller/entity/BUILD.gn +++ b/impeller/entity/BUILD.gn @@ -27,8 +27,6 @@ impeller_component("entity") { "contents.h", "entity.cc", "entity.h", - "entity_renderer.cc", - "entity_renderer.h", ] deps = [ ":entity_shaders" ] diff --git a/impeller/entity/entity.cc b/impeller/entity/entity.cc index 9ff908721cb2d..fec6b9710b1fa 100644 --- a/impeller/entity/entity.cc +++ b/impeller/entity/entity.cc @@ -4,6 +4,9 @@ #include "impeller/entity/entity.h" +#include "impeller/entity/content_renderer.h" +#include "impeller/renderer/render_pass.h" + namespace impeller { Entity::Entity() = default; @@ -46,4 +49,12 @@ void Entity::IncrementStencilDepth(uint32_t increment) { stencil_depth_ += increment; } +bool Entity::Render(ContentRenderer& renderer, RenderPass& parent_pass) const { + if (!contents_) { + return true; + } + + return contents_->Render(renderer, *this, parent_pass); +} + } // namespace impeller diff --git a/impeller/entity/entity.h b/impeller/entity/entity.h index ef5285c2dbc2e..26c354f00dc00 100644 --- a/impeller/entity/entity.h +++ b/impeller/entity/entity.h @@ -13,6 +13,9 @@ namespace impeller { +class Renderer; +class RenderPass; + class Entity { public: Entity(); @@ -37,6 +40,8 @@ class Entity { uint32_t GetStencilDepth() const; + bool Render(ContentRenderer& renderer, RenderPass& parent_pass) const; + private: Matrix transformation_; std::shared_ptr contents_; diff --git a/impeller/entity/entity_playground.cc b/impeller/entity/entity_playground.cc index 835cf655fbaa5..140a0d93d1479 100644 --- a/impeller/entity/entity_playground.cc +++ b/impeller/entity/entity_playground.cc @@ -4,6 +4,8 @@ #include "impeller/entity/entity_playground.h" +#include "impeller/entity/content_renderer.h" + namespace impeller { EntityPlayground::EntityPlayground() = default; @@ -11,15 +13,12 @@ EntityPlayground::EntityPlayground() = default; EntityPlayground::~EntityPlayground() = default; bool EntityPlayground::OpenPlaygroundHere(Entity entity) { - if (!renderer_) { - renderer_ = std::make_unique(GetContext()); - if (!renderer_) { - return false; - } + ContentRenderer renderer(GetContext()); + if (!renderer.IsValid()) { + return false; } Renderer::RenderCallback callback = [&](RenderPass& pass) -> bool { - std::vector entities = {entity}; - return renderer_->RenderEntities(pass, entities); + return entity.Render(renderer, pass); }; return Playground::OpenPlaygroundHere(callback); } diff --git a/impeller/entity/entity_playground.h b/impeller/entity/entity_playground.h index fdbc3c492a6c5..9d16caab30a95 100644 --- a/impeller/entity/entity_playground.h +++ b/impeller/entity/entity_playground.h @@ -6,7 +6,6 @@ #include "flutter/fml/macros.h" #include "impeller/entity/entity.h" -#include "impeller/entity/entity_renderer.h" #include "impeller/playground/playground.h" namespace impeller { @@ -20,8 +19,6 @@ class EntityPlayground : public Playground { bool OpenPlaygroundHere(Entity entity); private: - std::unique_ptr renderer_; - FML_DISALLOW_COPY_AND_ASSIGN(EntityPlayground); }; diff --git a/impeller/entity/entity_renderer.cc b/impeller/entity/entity_renderer.cc deleted file mode 100644 index 9572080b059f4..0000000000000 --- a/impeller/entity/entity_renderer.cc +++ /dev/null @@ -1,49 +0,0 @@ -// Copyright 2013 The Flutter Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#include "flutter/impeller/entity/entity_renderer.h" - -#include "flutter/fml/trace_event.h" -#include "impeller/entity/content_renderer.h" - -namespace impeller { - -EntityRenderer::EntityRenderer(std::shared_ptr context) - : context_(std::move(context)) { - if (!context_ || !context_->IsValid()) { - return; - } - - content_renderer_ = std::make_unique(context_); - if (!content_renderer_->IsValid()) { - return; - } - - is_valid_ = true; -} - -EntityRenderer::~EntityRenderer() = default; - -bool EntityRenderer::IsValid() const { - return is_valid_; -} - -bool EntityRenderer::RenderEntities(RenderPass& parent_pass, - const std::vector& entities) { - if (!IsValid()) { - return false; - } - - for (const auto& entity : entities) { - if (auto contents = entity.GetContents()) { - if (!contents->Render(*content_renderer_, entity, parent_pass)) { - return false; - } - } - } - - return true; -} - -} // namespace impeller From 89e71b678dd7d61f0837f658746dedd009ba1030 Mon Sep 17 00:00:00 2001 From: Chinmay Garde Date: Thu, 25 Nov 2021 14:12:35 -0800 Subject: [PATCH 211/433] Setup canvas pass hierarchies. --- impeller/aiks/aiks_renderer.cc | 12 ++--- impeller/aiks/canvas.cc | 81 ++++++++++++++++++++-------------- impeller/aiks/canvas.h | 8 +++- impeller/aiks/canvas_pass.cc | 58 ++++++++++++++++++++++-- impeller/aiks/canvas_pass.h | 24 +++++++--- impeller/aiks/picture.h | 2 +- 6 files changed, 131 insertions(+), 54 deletions(-) diff --git a/impeller/aiks/aiks_renderer.cc b/impeller/aiks/aiks_renderer.cc index f491e4d083882..dbdc2838ad121 100644 --- a/impeller/aiks/aiks_renderer.cc +++ b/impeller/aiks/aiks_renderer.cc @@ -33,16 +33,10 @@ bool AiksRenderer::Render(const Picture& picture, RenderPass& parent_pass) { return false; } - for (const auto& entry : picture.entries) { - if (!entry.pass.has_value()) { - continue; - ; - } - - if (!entry.pass->Render(*content_renderer_, parent_pass)) { - return false; - } + if (picture.pass) { + return picture.pass->Render(*content_renderer_, parent_pass); } + return true; } diff --git a/impeller/aiks/canvas.cc b/impeller/aiks/canvas.cc index 87ec026a6321f..d5b12962fc272 100644 --- a/impeller/aiks/canvas.cc +++ b/impeller/aiks/canvas.cc @@ -12,11 +12,25 @@ namespace impeller { Canvas::Canvas() { - Save(true); + Initialize(); } Canvas::~Canvas() = default; +void Canvas::Initialize() { + base_pass_ = std::make_unique(); + current_pass_ = base_pass_.get(); + xformation_stack_.emplace_back(CanvasStackEntry{}); + FML_DCHECK(GetSaveCount() == 1u); + FML_DCHECK(base_pass_->GetDepth() == 1u); +} + +void Canvas::Reset() { + base_pass_ = nullptr; + current_pass_ = nullptr; + xformation_stack_ = {}; +} + void Canvas::Save() { Save(false); } @@ -26,6 +40,10 @@ bool Canvas::Restore() { if (xformation_stack_.size() == 1) { return false; } + if (xformation_stack_.back().is_subpass) { + current_pass_ = GetCurrentPass().GetSuperpass(); + FML_DCHECK(current_pass_); + } xformation_stack_.pop_back(); return true; } @@ -69,11 +87,11 @@ void Canvas::DrawPath(Path path, Paint paint) { entity.SetStencilDepth(GetStencilDepth()); entity.SetContents(paint.CreateContentsForEntity()); - GetCurrentPass().PushEntity(std::move(entity)); + GetCurrentPass().AddEntity(std::move(entity)); } void Canvas::SaveLayer(const Paint& paint, std::optional bounds) { - Save(); + Save(true); } void Canvas::ClipPath(Path path) { @@ -85,23 +103,24 @@ void Canvas::ClipPath(Path path) { entity.SetContents(std::make_shared()); entity.SetStencilDepth(GetStencilDepth()); - GetCurrentPass().PushEntity(std::move(entity)); + GetCurrentPass().AddEntity(std::move(entity)); } void Canvas::DrawShadow(Path path, Color color, Scalar elevation) {} -void Canvas::DrawPicture(const Picture& picture) { - for (const auto& stack_entry : picture.entries) { - auto new_stack_entry = stack_entry; - if (auto pass = new_stack_entry.pass) { - for (auto entity : pass->GetEntities()) { - entity.IncrementStencilDepth(GetStencilDepth()); - entity.SetTransformation(GetCurrentTransformation() * - entity.GetTransformation()); - } - } - xformation_stack_.emplace_back(std::move(new_stack_entry)); +void Canvas::DrawPicture(Picture picture) { + if (!picture.pass) { + return; } + // Clone the base pass and account for the CTM updates. + auto pass = picture.pass->Clone(); + pass->IterateAllEntities([&](auto& entity) -> bool { + entity.IncrementStencilDepth(GetStencilDepth()); + entity.SetTransformation(GetCurrentTransformation() * + entity.GetTransformation()); + return true; + }); + return; } void Canvas::DrawImage(std::shared_ptr image, @@ -140,23 +159,23 @@ void Canvas::DrawImageRect(std::shared_ptr image, entity.SetPath(PathBuilder{}.AddRect(dest).CreatePath()); entity.SetContents(contents); entity.SetTransformation(GetCurrentTransformation()); - GetCurrentPass().PushEntity(std::move(entity)); + + GetCurrentPass().AddEntity(std::move(entity)); } Picture Canvas::EndRecordingAsPicture() { Picture picture; - picture.entries = std::move(xformation_stack_); + picture.pass = std::move(base_pass_); + + Reset(); + Initialize(); + return picture; } CanvasPass& Canvas::GetCurrentPass() { - for (auto i = xformation_stack_.rbegin(), end = xformation_stack_.rend(); - i < end; i++) { - if (i->pass.has_value()) { - return i->pass.value(); - } - } - FML_UNREACHABLE(); + FML_DCHECK(current_pass_ != nullptr); + return *current_pass_; } void Canvas::IncrementStencilDepth() { @@ -172,21 +191,15 @@ void Canvas::DrawRect(Rect rect, Paint paint) { } void Canvas::Save(bool create_subpass) { - // Check if called from the ctor. - if (xformation_stack_.empty()) { - FML_DCHECK(create_subpass) << "Base entries must have a pass."; - CanvasStackEntry entry; - entry.pass = CanvasPass{}; - xformation_stack_.emplace_back(std::move(entry)); - } - auto entry = CanvasStackEntry{}; - entry.xformation = xformation_stack_.back().xformation; entry.stencil_depth = xformation_stack_.back().stencil_depth; + entry.is_subpass = create_subpass; + if (create_subpass) { - entry.pass = CanvasPass{}; + current_pass_ = GetCurrentPass().AddSubpass(std::make_unique()); } + xformation_stack_.emplace_back(std::move(entry)); } diff --git a/impeller/aiks/canvas.h b/impeller/aiks/canvas.h index b2a5d8c3ff1fa..06d90e54f334c 100644 --- a/impeller/aiks/canvas.h +++ b/impeller/aiks/canvas.h @@ -64,13 +64,19 @@ class Canvas { void DrawShadow(Path path, Color color, Scalar elevation); - void DrawPicture(const Picture& picture); + void DrawPicture(Picture picture); Picture EndRecordingAsPicture(); private: + std::unique_ptr base_pass_; + CanvasPass* current_pass_ = nullptr; std::deque xformation_stack_; + void Initialize(); + + void Reset(); + CanvasPass& GetCurrentPass(); void IncrementStencilDepth(); diff --git a/impeller/aiks/canvas_pass.cc b/impeller/aiks/canvas_pass.cc index edada76473835..09eea3934e645 100644 --- a/impeller/aiks/canvas_pass.cc +++ b/impeller/aiks/canvas_pass.cc @@ -12,7 +12,7 @@ CanvasPass::CanvasPass() = default; CanvasPass::~CanvasPass() = default; -void CanvasPass::PushEntity(Entity entity) { +void CanvasPass::AddEntity(Entity entity) { entities_.emplace_back(std::move(entity)); } @@ -20,6 +20,18 @@ const std::vector& CanvasPass::GetEntities() const { return entities_; } +void CanvasPass::SetEntities(Entities entities) { + entities_ = std::move(entities); +} + +size_t CanvasPass::GetDepth() const { + size_t max_subpass_depth = 0u; + for (const auto& subpass : subpasses_) { + max_subpass_depth = std::max(max_subpass_depth, subpass->GetDepth()); + } + return max_subpass_depth + 1u; +} + Rect CanvasPass::GetCoverageRect() const { std::optional min, max; for (const auto& entity : entities_) { @@ -43,13 +55,21 @@ Rect CanvasPass::GetCoverageRect() const { return {min->x, min->y, diff.x, diff.y}; } +CanvasPass* CanvasPass::GetSuperpass() const { + return superpass_; +} + const CanvasPass::Subpasses& CanvasPass::GetSubpasses() const { return subpasses_; } -bool CanvasPass::AddSubpass(CanvasPass pass) { - subpasses_.emplace_back(std::move(pass)); - return true; +CanvasPass* CanvasPass::AddSubpass(std::unique_ptr pass) { + if (!pass) { + return nullptr; + } + FML_DCHECK(pass->superpass_ == nullptr); + pass->superpass_ = this; + return subpasses_.emplace_back(std::move(pass)).get(); } bool CanvasPass::Render(ContentRenderer& renderer, @@ -59,7 +79,37 @@ bool CanvasPass::Render(ContentRenderer& renderer, return false; } } + for (const auto& subpass : subpasses_) { + if (!subpass->Render(renderer, parent_pass)) { + return false; + } + } return true; } +void CanvasPass::IterateAllEntities(std::function iterator) { + if (!iterator) { + return; + } + + for (auto& entity : entities_) { + if (!iterator(entity)) { + return; + } + } + + for (auto& subpass : subpasses_) { + subpass->IterateAllEntities(iterator); + } +} + +std::unique_ptr CanvasPass::Clone() const { + auto pass = std::make_unique(); + pass->SetEntities(entities_); + for (const auto& subpass : subpasses_) { + pass->AddSubpass(subpass->Clone()); + } + return pass; +} + } // namespace impeller diff --git a/impeller/aiks/canvas_pass.h b/impeller/aiks/canvas_pass.h index 3aa7cb825c3eb..55ea2ea6e2f2c 100644 --- a/impeller/aiks/canvas_pass.h +++ b/impeller/aiks/canvas_pass.h @@ -18,33 +18,47 @@ class ContentRenderer; class CanvasPass { public: - using Subpasses = std::vector; + using Entities = std::vector; + using Subpasses = std::vector>; CanvasPass(); ~CanvasPass(); - void PushEntity(Entity entity); + size_t GetDepth() const; + + std::unique_ptr Clone() const; Rect GetCoverageRect() const; + void AddEntity(Entity entity); + + void SetEntities(Entities entities); + const std::vector& GetEntities() const; const Subpasses& GetSubpasses() const; - bool AddSubpass(CanvasPass pass); + CanvasPass* AddSubpass(std::unique_ptr pass); + + CanvasPass* GetSuperpass() const; bool Render(ContentRenderer& renderer, RenderPass& parent_pass) const; + void IterateAllEntities(std::function iterator); + private: - std::vector entities_; + Entities entities_; Subpasses subpasses_; + CanvasPass* superpass_ = nullptr; + + FML_DISALLOW_COPY_AND_ASSIGN(CanvasPass); }; struct CanvasStackEntry { Matrix xformation; size_t stencil_depth = 0u; - std::optional pass; + bool is_subpass = false; }; } // namespace impeller diff --git a/impeller/aiks/picture.h b/impeller/aiks/picture.h index 4b5fed22a6e40..cf676f91c886f 100644 --- a/impeller/aiks/picture.h +++ b/impeller/aiks/picture.h @@ -14,7 +14,7 @@ namespace impeller { struct Picture { - std::deque entries; + std::unique_ptr pass; }; } // namespace impeller From c21c5c08dea0659dda89af820dd1a9c363f27d81 Mon Sep 17 00:00:00 2001 From: Chinmay Garde Date: Thu, 25 Nov 2021 15:27:48 -0800 Subject: [PATCH 212/433] Canvas subpasses rendering into their own command buffers. --- impeller/aiks/canvas_pass.cc | 55 ++++++++++++++++++- impeller/geometry/size.h | 6 ++ .../renderer/backend/metal/render_pass_mtl.mm | 6 +- impeller/renderer/command_buffer.cc | 4 ++ impeller/renderer/command_buffer.h | 2 + impeller/renderer/render_target.cc | 15 ++++- impeller/renderer/render_target.h | 2 + 7 files changed, 86 insertions(+), 4 deletions(-) diff --git a/impeller/aiks/canvas_pass.cc b/impeller/aiks/canvas_pass.cc index 09eea3934e645..cd8575f2c60e1 100644 --- a/impeller/aiks/canvas_pass.cc +++ b/impeller/aiks/canvas_pass.cc @@ -5,6 +5,10 @@ #include "impeller/aiks/canvas_pass.h" #include "impeller/entity/content_renderer.h" +#include "impeller/geometry/path_builder.h" +#include "impeller/renderer/command_buffer.h" +#include "impeller/renderer/render_pass.h" +#include "impeller/renderer/render_target.h" namespace impeller { @@ -80,10 +84,59 @@ bool CanvasPass::Render(ContentRenderer& renderer, } } for (const auto& subpass : subpasses_) { - if (!subpass->Render(renderer, parent_pass)) { + const auto subpass_coverage = subpass->GetCoverageRect(); + + if (subpass_coverage.IsEmpty()) { + // It is not an error to have an empty subpass. But subpasses that can't + // create their intermediates must trip errors. + continue; + } + + auto context = renderer.GetContext(); + + auto subpass_target = RenderTarget::CreateOffscreen( + *context, ISize::Ceil(subpass_coverage.size)); + + auto sub_command_buffer = context->CreateRenderCommandBuffer(); + + if (!sub_command_buffer) { + return false; + } + + auto sub_renderpass = sub_command_buffer->CreateRenderPass(subpass_target); + + if (!sub_renderpass) { + return false; + } + + if (!subpass) { + return false; + } + + if (!subpass->Render(renderer, *sub_renderpass)) { + return false; + } + + if (!sub_renderpass->EncodeCommands(*context->GetTransientsAllocator())) { + return false; + } + + sub_command_buffer->SubmitCommands(); + + auto offscreen_texture_contents = std::make_shared(); + offscreen_texture_contents->SetTexture( + subpass_target.GetRenderTargetTexture()); + offscreen_texture_contents->SetSourceRect( + IRect::MakeSize(subpass_target.GetRenderTargetTexture()->GetSize())); + + Entity entity; + entity.SetPath(PathBuilder{}.AddRect(subpass_coverage).CreatePath()); + entity.SetContents(std::move(offscreen_texture_contents)); + if (!entity.Render(renderer, parent_pass)) { return false; } } + return true; } diff --git a/impeller/geometry/size.h b/impeller/geometry/size.h index 62f4529b02940..2c24c05456c9a 100644 --- a/impeller/geometry/size.h +++ b/impeller/geometry/size.h @@ -75,6 +75,12 @@ struct TSize { constexpr bool IsEmpty() const { return !IsPositive(); } + template + static constexpr TSize Ceil(const TSize& other) { + return TSize{static_cast(std::ceil(other.width)), + static_cast(std::ceil(other.height))}; + } + constexpr size_t MipCount() const { if (!IsPositive()) { return 1u; diff --git a/impeller/renderer/backend/metal/render_pass_mtl.mm b/impeller/renderer/backend/metal/render_pass_mtl.mm index 5ff8392faa476..803a4bff26f0c 100644 --- a/impeller/renderer/backend/metal/render_pass_mtl.mm +++ b/impeller/renderer/backend/metal/render_pass_mtl.mm @@ -69,8 +69,8 @@ static bool ConfigureStencilAttachment( for (const auto& color : colors) { if (!ConfigureColorAttachment(color.second, result.colorAttachments[color.first])) { - FML_LOG(ERROR) << "Could not configure color attachment at index " - << color.first; + FML_DLOG(ERROR) << "Could not configure color attachment at index " + << color.first; return nil; } } @@ -79,6 +79,7 @@ static bool ConfigureStencilAttachment( if (depth.has_value() && !ConfigureDepthAttachment(depth.value(), result.depthAttachment)) { + FML_DLOG(ERROR) << "Could not configure depth attachment."; return nil; } @@ -86,6 +87,7 @@ static bool ConfigureStencilAttachment( if (stencil.has_value() && !ConfigureStencilAttachment(stencil.value(), result.stencilAttachment)) { + FML_DLOG(ERROR) << "Could not configure stencil attachment."; return nil; } diff --git a/impeller/renderer/command_buffer.cc b/impeller/renderer/command_buffer.cc index 9398bc6822ba0..73f4b71c70c0b 100644 --- a/impeller/renderer/command_buffer.cc +++ b/impeller/renderer/command_buffer.cc @@ -10,4 +10,8 @@ CommandBuffer::CommandBuffer() = default; CommandBuffer::~CommandBuffer() = default; +void CommandBuffer::SubmitCommands() { + SubmitCommands(nullptr); +} + } // namespace impeller diff --git a/impeller/renderer/command_buffer.h b/impeller/renderer/command_buffer.h index a1240badbfaeb..3c53d917ec5cf 100644 --- a/impeller/renderer/command_buffer.h +++ b/impeller/renderer/command_buffer.h @@ -51,6 +51,8 @@ class CommandBuffer { /// virtual void SubmitCommands(CompletionCallback callback) = 0; + void SubmitCommands(); + //---------------------------------------------------------------------------- /// @brief Create a render pass to record render commands into. /// diff --git a/impeller/renderer/render_target.cc b/impeller/renderer/render_target.cc index b2710a1f6c917..0fabd12ced91c 100644 --- a/impeller/renderer/render_target.cc +++ b/impeller/renderer/render_target.cc @@ -36,6 +36,14 @@ ISize RenderTarget::GetRenderTargetSize() const { return size.has_value() ? size.value() : ISize{}; } +std::shared_ptr RenderTarget::GetRenderTargetTexture() const { + auto found = colors_.find(0u); + if (found == colors_.end()) { + return nullptr; + } + return found->second.texture; +} + RenderTarget& RenderTarget::SetColorAttachment(ColorAttachment attachment, size_t index) { if (attachment) { @@ -75,10 +83,15 @@ const std::optional& RenderTarget::GetStencilAttachment() RenderTarget RenderTarget::CreateOffscreen(const Context& context, ISize size, std::string label) { + if (size.IsEmpty()) { + return {}; + } + TextureDescriptor color_tex0; color_tex0.format = PixelFormat::kB8G8R8A8UNormInt; color_tex0.size = size; - color_tex0.usage = static_cast(TextureUsage::kRenderTarget); + color_tex0.usage = static_cast(TextureUsage::kRenderTarget) | + static_cast(TextureUsage::kShaderRead); TextureDescriptor stencil_tex0; stencil_tex0.format = PixelFormat::kD32FloatS8UNormInt; diff --git a/impeller/renderer/render_target.h b/impeller/renderer/render_target.h index 66ecf2cc98a7c..9b00c82fbd75c 100644 --- a/impeller/renderer/render_target.h +++ b/impeller/renderer/render_target.h @@ -29,6 +29,8 @@ class RenderTarget { ISize GetRenderTargetSize() const; + std::shared_ptr GetRenderTargetTexture() const; + std::optional GetColorAttachmentSize(size_t index) const; RenderTarget& SetColorAttachment(ColorAttachment attachment, size_t index); From c717bb84dfd1c364579ee21fe77e5d4b296538d0 Mon Sep 17 00:00:00 2001 From: Chinmay Garde Date: Fri, 26 Nov 2021 15:19:47 -0800 Subject: [PATCH 213/433] Work towards making canvas subpasses configure postprocessing effects. --- impeller/aiks/aiks_unittests.cc | 6 +- impeller/aiks/canvas.cc | 13 ++-- impeller/aiks/canvas_pass.cc | 23 ++++-- impeller/aiks/canvas_pass.h | 8 ++- impeller/playground/playground.mm | 2 +- .../backend/metal/command_buffer_mtl.h | 3 + .../backend/metal/command_buffer_mtl.mm | 21 +++--- .../renderer/backend/metal/render_pass_mtl.mm | 70 ++++++++++--------- impeller/renderer/command_buffer.h | 2 + 9 files changed, 90 insertions(+), 58 deletions(-) diff --git a/impeller/aiks/aiks_unittests.cc b/impeller/aiks/aiks_unittests.cc index 8182261890aa8..428b6ccbb484f 100644 --- a/impeller/aiks/aiks_unittests.cc +++ b/impeller/aiks/aiks_unittests.cc @@ -121,9 +121,9 @@ TEST_F(AiksTest, CanRenderGroupOpacity) { canvas.SaveLayer(alpha); - canvas.DrawRect({100, 100, 100, 100}, red); - canvas.DrawRect({120, 120, 100, 100}, green); - canvas.DrawRect({140, 140, 100, 100}, blue); + canvas.DrawRect({000, 000, 100, 100}, red); + // canvas.DrawRect({020, 020, 100, 100}, green); + // canvas.DrawRect({040, 040, 100, 100}, blue); canvas.Restore(); diff --git a/impeller/aiks/canvas.cc b/impeller/aiks/canvas.cc index d5b12962fc272..842f6b6373fba 100644 --- a/impeller/aiks/canvas.cc +++ b/impeller/aiks/canvas.cc @@ -22,7 +22,7 @@ void Canvas::Initialize() { current_pass_ = base_pass_.get(); xformation_stack_.emplace_back(CanvasStackEntry{}); FML_DCHECK(GetSaveCount() == 1u); - FML_DCHECK(base_pass_->GetDepth() == 1u); + FML_DCHECK(base_pass_->GetSubpassesDepth() == 1u); } void Canvas::Reset() { @@ -192,14 +192,15 @@ void Canvas::DrawRect(Rect rect, Paint paint) { void Canvas::Save(bool create_subpass) { auto entry = CanvasStackEntry{}; - entry.xformation = xformation_stack_.back().xformation; - entry.stencil_depth = xformation_stack_.back().stencil_depth; - entry.is_subpass = create_subpass; - if (create_subpass) { + entry.is_subpass = true; current_pass_ = GetCurrentPass().AddSubpass(std::make_unique()); + current_pass_->SetTransformation(xformation_stack_.back().xformation); + current_pass_->SetStencilDepth(xformation_stack_.back().stencil_depth); + } else { + entry.xformation = xformation_stack_.back().xformation; + entry.stencil_depth = xformation_stack_.back().stencil_depth; } - xformation_stack_.emplace_back(std::move(entry)); } diff --git a/impeller/aiks/canvas_pass.cc b/impeller/aiks/canvas_pass.cc index cd8575f2c60e1..1c534133a8f6f 100644 --- a/impeller/aiks/canvas_pass.cc +++ b/impeller/aiks/canvas_pass.cc @@ -28,10 +28,11 @@ void CanvasPass::SetEntities(Entities entities) { entities_ = std::move(entities); } -size_t CanvasPass::GetDepth() const { +size_t CanvasPass::GetSubpassesDepth() const { size_t max_subpass_depth = 0u; for (const auto& subpass : subpasses_) { - max_subpass_depth = std::max(max_subpass_depth, subpass->GetDepth()); + max_subpass_depth = + std::max(max_subpass_depth, subpass->GetSubpassesDepth()); } return max_subpass_depth + 1u; } @@ -84,6 +85,10 @@ bool CanvasPass::Render(ContentRenderer& renderer, } } for (const auto& subpass : subpasses_) { + if (!subpass) { + return false; + } + const auto subpass_coverage = subpass->GetCoverageRect(); if (subpass_coverage.IsEmpty()) { @@ -109,9 +114,7 @@ bool CanvasPass::Render(ContentRenderer& renderer, return false; } - if (!subpass) { - return false; - } + sub_renderpass->SetLabel("OffscreenPass"); if (!subpass->Render(renderer, *sub_renderpass)) { return false; @@ -132,6 +135,8 @@ bool CanvasPass::Render(ContentRenderer& renderer, Entity entity; entity.SetPath(PathBuilder{}.AddRect(subpass_coverage).CreatePath()); entity.SetContents(std::move(offscreen_texture_contents)); + entity.SetStencilDepth(stencil_depth_); + entity.SetTransformation(xformation_); if (!entity.Render(renderer, parent_pass)) { return false; } @@ -165,4 +170,12 @@ std::unique_ptr CanvasPass::Clone() const { return pass; } +void CanvasPass::SetTransformation(Matrix xformation) { + xformation_ = std::move(xformation); +} + +void CanvasPass::SetStencilDepth(size_t stencil_depth) { + stencil_depth_ = stencil_depth; +} + } // namespace impeller diff --git a/impeller/aiks/canvas_pass.h b/impeller/aiks/canvas_pass.h index 55ea2ea6e2f2c..4a942010acb53 100644 --- a/impeller/aiks/canvas_pass.h +++ b/impeller/aiks/canvas_pass.h @@ -25,7 +25,7 @@ class CanvasPass { ~CanvasPass(); - size_t GetDepth() const; + size_t GetSubpassesDepth() const; std::unique_ptr Clone() const; @@ -47,10 +47,16 @@ class CanvasPass { void IterateAllEntities(std::function iterator); + void SetTransformation(Matrix xformation); + + void SetStencilDepth(size_t stencil_depth); + private: Entities entities_; Subpasses subpasses_; CanvasPass* superpass_ = nullptr; + Matrix xformation_; + size_t stencil_depth_ = 0u; FML_DISALLOW_COPY_AND_ASSIGN(CanvasPass); }; diff --git a/impeller/playground/playground.mm b/impeller/playground/playground.mm index 61f76d847a6b3..848d28fb47b61 100644 --- a/impeller/playground/playground.mm +++ b/impeller/playground/playground.mm @@ -154,7 +154,7 @@ static void PlaygroundKeyCallback(GLFWwindow* window, ColorAttachment color0; color0.texture = std::make_shared(color0_tex, current_drawable.texture); - color0.clear_color = Color::SkyBlue(); + color0.clear_color = Color::DarkSlateGray(); color0.load_action = LoadAction::kClear; color0.store_action = StoreAction::kStore; diff --git a/impeller/renderer/backend/metal/command_buffer_mtl.h b/impeller/renderer/backend/metal/command_buffer_mtl.h index 46b398efc1db1..f9a27f15d0024 100644 --- a/impeller/renderer/backend/metal/command_buffer_mtl.h +++ b/impeller/renderer/backend/metal/command_buffer_mtl.h @@ -32,6 +32,9 @@ class CommandBufferMTL final : public CommandBuffer { // |CommandBuffer| void SubmitCommands(CompletionCallback callback) override; + // |CommandBuffer| + void ReserveSpotInQueue() override; + // |CommandBuffer| std::shared_ptr CreateRenderPass( RenderTarget target) const override; diff --git a/impeller/renderer/backend/metal/command_buffer_mtl.mm b/impeller/renderer/backend/metal/command_buffer_mtl.mm index 1065008dba461..8c55ae10a0119 100644 --- a/impeller/renderer/backend/metal/command_buffer_mtl.mm +++ b/impeller/renderer/backend/metal/command_buffer_mtl.mm @@ -35,23 +35,28 @@ } void CommandBufferMTL::SubmitCommands(CompletionCallback callback) { - if (!callback) { - callback = [](auto) {}; - } - if (!buffer_) { // Already committed. This is caller error. - callback(Status::kError); + if (callback) { + callback(Status::kError); + } return; } - [buffer_ addCompletedHandler:^(id buffer) { - callback(ToCommitResult(buffer.status)); - }]; + if (callback) { + [buffer_ addCompletedHandler:^(id buffer) { + callback(ToCommitResult(buffer.status)); + }]; + } + [buffer_ commit]; buffer_ = nil; } +void CommandBufferMTL::ReserveSpotInQueue() { + [buffer_ enqueue]; +} + std::shared_ptr CommandBufferMTL::CreateRenderPass( RenderTarget target) const { if (!buffer_) { diff --git a/impeller/renderer/backend/metal/render_pass_mtl.mm b/impeller/renderer/backend/metal/render_pass_mtl.mm index 803a4bff26f0c..12499878396a8 100644 --- a/impeller/renderer/backend/metal/render_pass_mtl.mm +++ b/impeller/renderer/backend/metal/render_pass_mtl.mm @@ -127,21 +127,23 @@ static bool ConfigureStencilAttachment( if (!IsValid()) { return false; } - auto pass = [buffer_ renderCommandEncoderWithDescriptor:desc_]; + auto render_command_encoder = + [buffer_ renderCommandEncoderWithDescriptor:desc_]; - if (!pass) { + if (!render_command_encoder) { return false; } if (!label_.empty()) { - [pass setLabel:@(label_.c_str())]; + [render_command_encoder setLabel:@(label_.c_str())]; } // Success or failure, the pass must end. The buffer can only process one pass // at a time. - fml::ScopedCleanupClosure auto_end([pass]() { [pass endEncoding]; }); + fml::ScopedCleanupClosure auto_end( + [render_command_encoder]() { [render_command_encoder endEncoding]; }); - return EncodeCommands(transients_allocator, pass); + return EncodeCommands(transients_allocator, render_command_encoder); } //----------------------------------------------------------------------------- @@ -154,7 +156,7 @@ static bool ConfigureStencilAttachment( /// absent. /// struct PassBindingsCache { - PassBindingsCache(id pass) : pass_(pass) {} + PassBindingsCache(id encoder) : encoder_(encoder) {} PassBindingsCache(const PassBindingsCache&) = delete; @@ -165,7 +167,7 @@ void SetRenderPipelineState(id pipeline) { return; } pipeline_ = pipeline; - [pass_ setRenderPipelineState:pipeline_]; + [encoder_ setRenderPipelineState:pipeline_]; } void SetDepthStencilState(id depth_stencil) { @@ -173,7 +175,7 @@ void SetDepthStencilState(id depth_stencil) { return; } depth_stencil_ = depth_stencil; - [pass_ setDepthStencilState:depth_stencil_]; + [encoder_ setDepthStencilState:depth_stencil_]; } bool SetBuffer(ShaderStage stage, @@ -194,10 +196,10 @@ bool SetBuffer(ShaderStage stage, switch (stage) { case ShaderStage::kVertex: - [pass_ setVertexBufferOffset:offset atIndex:index]; + [encoder_ setVertexBufferOffset:offset atIndex:index]; return true; case ShaderStage::kFragment: - [pass_ setFragmentBufferOffset:offset atIndex:index]; + [encoder_ setFragmentBufferOffset:offset atIndex:index]; return true; default: FML_DCHECK(false) @@ -209,10 +211,10 @@ bool SetBuffer(ShaderStage stage, buffers_map[index] = {buffer, offset}; switch (stage) { case ShaderStage::kVertex: - [pass_ setVertexBuffer:buffer offset:offset atIndex:index]; + [encoder_ setVertexBuffer:buffer offset:offset atIndex:index]; return true; case ShaderStage::kFragment: - [pass_ setFragmentBuffer:buffer offset:offset atIndex:index]; + [encoder_ setFragmentBuffer:buffer offset:offset atIndex:index]; return true; default: FML_DCHECK(false) << "Cannot bind buffer to unknown shader stage."; @@ -231,10 +233,10 @@ bool SetTexture(ShaderStage stage, uint64_t index, id texture) { texture_map[index] = texture; switch (stage) { case ShaderStage::kVertex: - [pass_ setVertexTexture:texture atIndex:index]; + [encoder_ setVertexTexture:texture atIndex:index]; return true; case ShaderStage::kFragment: - [pass_ setFragmentTexture:texture atIndex:index]; + [encoder_ setFragmentTexture:texture atIndex:index]; return true; default: FML_DCHECK(false) << "Cannot bind buffer to unknown shader stage."; @@ -255,10 +257,10 @@ bool SetSampler(ShaderStage stage, sampler_map[index] = sampler; switch (stage) { case ShaderStage::kVertex: - [pass_ setVertexSamplerState:sampler atIndex:index]; + [encoder_ setVertexSamplerState:sampler atIndex:index]; return true; case ShaderStage::kFragment: - [pass_ setFragmentSamplerState:sampler atIndex:index]; + [encoder_ setFragmentSamplerState:sampler atIndex:index]; return true; default: FML_DCHECK(false) << "Cannot bind buffer to unknown shader stage."; @@ -276,7 +278,7 @@ bool SetSampler(ShaderStage stage, using TextureMap = std::map>; using SamplerMap = std::map>; - const id pass_; + const id encoder_; id pipeline_ = nullptr; id depth_stencil_ = nullptr; std::map buffers_; @@ -332,8 +334,8 @@ static bool Bind(PassBindingsCache& pass, } bool RenderPassMTL::EncodeCommands(Allocator& allocator, - id pass) const { - PassBindingsCache pass_bindings(pass); + id encoder) const { + PassBindingsCache pass_bindings(encoder); auto bind_stage_resources = [&allocator, &pass_bindings]( const Bindings& bindings, ShaderStage stage) -> bool { @@ -355,7 +357,7 @@ static bool Bind(PassBindingsCache& pass, return true; }; - fml::closure pop_debug_marker = [pass]() { [pass popDebugGroup]; }; + fml::closure pop_debug_marker = [encoder]() { [encoder popDebugGroup]; }; for (const auto& command : commands_) { if (command.index_count == 0u) { FML_DLOG(ERROR) << "Zero index count in render pass command."; @@ -364,7 +366,7 @@ static bool Bind(PassBindingsCache& pass, fml::ScopedCleanupClosure auto_pop_debug_marker(pop_debug_marker); if (!command.label.empty()) { - [pass pushDebugGroup:@(command.label.c_str())]; + [encoder pushDebugGroup:@(command.label.c_str())]; } else { auto_pop_debug_marker.Release(); } @@ -372,11 +374,11 @@ static bool Bind(PassBindingsCache& pass, PipelineMTL::Cast(*command.pipeline).GetMTLRenderPipelineState()); pass_bindings.SetDepthStencilState( PipelineMTL::Cast(*command.pipeline).GetMTLDepthStencilState()); - [pass setFrontFacingWinding:command.winding == WindingOrder::kClockwise - ? MTLWindingClockwise - : MTLWindingCounterClockwise]; - [pass setCullMode:MTLCullModeNone]; - [pass setStencilReferenceValue:command.stencil_reference]; + [encoder setFrontFacingWinding:command.winding == WindingOrder::kClockwise + ? MTLWindingClockwise + : MTLWindingCounterClockwise]; + [encoder setCullMode:MTLCullModeNone]; + [encoder setStencilReferenceValue:command.stencil_reference]; if (!bind_stage_resources(command.vertex_bindings, ShaderStage::kVertex)) { return false; } @@ -400,14 +402,14 @@ static bool Bind(PassBindingsCache& pass, FML_DCHECK(command.index_count * sizeof(uint32_t) == command.index_buffer.range.length); // Returns void. All error checking must be done by this point. - [pass drawIndexedPrimitives:ToMTLPrimitiveType(command.primitive_type) - indexCount:command.index_count - indexType:MTLIndexTypeUInt32 - indexBuffer:mtl_index_buffer - indexBufferOffset:command.index_buffer.range.offset - instanceCount:1u - baseVertex:0u - baseInstance:0u]; + [encoder drawIndexedPrimitives:ToMTLPrimitiveType(command.primitive_type) + indexCount:command.index_count + indexType:MTLIndexTypeUInt32 + indexBuffer:mtl_index_buffer + indexBufferOffset:command.index_buffer.range.offset + instanceCount:1u + baseVertex:0u + baseInstance:0u]; } return true; } diff --git a/impeller/renderer/command_buffer.h b/impeller/renderer/command_buffer.h index 3c53d917ec5cf..c377baa3fbd05 100644 --- a/impeller/renderer/command_buffer.h +++ b/impeller/renderer/command_buffer.h @@ -53,6 +53,8 @@ class CommandBuffer { void SubmitCommands(); + virtual void ReserveSpotInQueue() = 0; + //---------------------------------------------------------------------------- /// @brief Create a render pass to record render commands into. /// From 7dd5e24998f1ee71736ce4e372c0a99251346e94 Mon Sep 17 00:00:00 2001 From: Chinmay Garde Date: Sat, 27 Nov 2021 16:01:33 -0800 Subject: [PATCH 214/433] Fix alpha writes while rendering to texture. --- impeller/aiks/aiks_unittests.cc | 6 +++--- impeller/aiks/canvas_pass.cc | 6 +++++- impeller/entity/contents.cc | 4 ++-- impeller/entity/shaders/texture_fill.frag | 3 ++- impeller/geometry/size.h | 7 ++++++- .../renderer/backend/metal/command_buffer_mtl.h | 5 ++++- .../renderer/backend/metal/command_buffer_mtl.mm | 13 +++++++++++-- impeller/renderer/command_buffer.cc | 4 ++-- impeller/renderer/command_buffer.h | 6 ++++-- impeller/renderer/formats.h | 6 +++--- impeller/renderer/render_target.cc | 5 +++-- impeller/renderer/renderer.cc | 6 +++--- 12 files changed, 48 insertions(+), 23 deletions(-) diff --git a/impeller/aiks/aiks_unittests.cc b/impeller/aiks/aiks_unittests.cc index 428b6ccbb484f..07fae56e3a32d 100644 --- a/impeller/aiks/aiks_unittests.cc +++ b/impeller/aiks/aiks_unittests.cc @@ -56,7 +56,7 @@ TEST_F(AiksTest, CanRenderImage) { auto image = std::make_shared(CreateTextureForFixture("kalimba.jpg")); paint.color = Color::Red(); canvas.DrawImage(image, Point::MakeXY(100.0, 100.0), paint); - // ASSERT_TRUE(OpenPlaygroundHere(canvas.EndRecordingAsPicture())); + ASSERT_TRUE(OpenPlaygroundHere(canvas.EndRecordingAsPicture())); } TEST_F(AiksTest, DISABLED_CanRenderImageRect) { @@ -122,8 +122,8 @@ TEST_F(AiksTest, CanRenderGroupOpacity) { canvas.SaveLayer(alpha); canvas.DrawRect({000, 000, 100, 100}, red); - // canvas.DrawRect({020, 020, 100, 100}, green); - // canvas.DrawRect({040, 040, 100, 100}, blue); + canvas.DrawRect({020, 020, 100, 100}, green); + canvas.DrawRect({040, 040, 100, 100}, blue); canvas.Restore(); diff --git a/impeller/aiks/canvas_pass.cc b/impeller/aiks/canvas_pass.cc index 1c534133a8f6f..ad3aa1fb52892 100644 --- a/impeller/aiks/canvas_pass.cc +++ b/impeller/aiks/canvas_pass.cc @@ -104,6 +104,8 @@ bool CanvasPass::Render(ContentRenderer& renderer, auto sub_command_buffer = context->CreateRenderCommandBuffer(); + sub_command_buffer->SetLabel("Offscreen Command Buffer"); + if (!sub_command_buffer) { return false; } @@ -124,7 +126,9 @@ bool CanvasPass::Render(ContentRenderer& renderer, return false; } - sub_command_buffer->SubmitCommands(); + if (!sub_command_buffer->SubmitCommands()) { + return false; + } auto offscreen_texture_contents = std::make_shared(); offscreen_texture_contents->SetTexture( diff --git a/impeller/entity/contents.cc b/impeller/entity/contents.cc index 9d15c959e6cbe..3bf1c0daba8a9 100644 --- a/impeller/entity/contents.cc +++ b/impeller/entity/contents.cc @@ -87,8 +87,8 @@ bool LinearGradientContents::Render(const ContentRenderer& renderer, cmd.label = "LinearGradientFill"; cmd.pipeline = renderer.GetGradientFillPipeline(); cmd.stencil_reference = entity.GetStencilDepth(); - cmd.BindVertices(vertices_builder.CreateVertexBuffer( - *renderer.GetContext()->GetPermanentsAllocator())); + cmd.BindVertices( + vertices_builder.CreateVertexBuffer(pass.GetTransientsBuffer())); cmd.primitive_type = PrimitiveType::kTriangle; FS::BindGradientInfo( cmd, pass.GetTransientsBuffer().EmplaceUniform(gradient_info)); diff --git a/impeller/entity/shaders/texture_fill.frag b/impeller/entity/shaders/texture_fill.frag index 8c12a8be15fb7..9c4dd1ec58b30 100644 --- a/impeller/entity/shaders/texture_fill.frag +++ b/impeller/entity/shaders/texture_fill.frag @@ -9,5 +9,6 @@ in vec2 v_texture_coords; out vec4 frag_color; void main() { - frag_color = texture(texture_sampler, v_texture_coords); + vec4 sampled = texture(texture_sampler, v_texture_coords); + frag_color = sampled; } diff --git a/impeller/geometry/size.h b/impeller/geometry/size.h index 2c24c05456c9a..450f11b19a4de 100644 --- a/impeller/geometry/size.h +++ b/impeller/geometry/size.h @@ -33,10 +33,15 @@ struct TSize { std::numeric_limits::max()}; } - constexpr TSize operator*(Type scale) const { + constexpr TSize operator*(Scalar scale) const { return {width * scale, height * scale}; } + constexpr TSize operator/(Scalar scale) const { + return {static_cast(width) / scale, + static_cast(height) / scale}; + } + constexpr bool operator==(const TSize& s) const { return s.width == width && s.height == height; } diff --git a/impeller/renderer/backend/metal/command_buffer_mtl.h b/impeller/renderer/backend/metal/command_buffer_mtl.h index f9a27f15d0024..d2e5ee31bab51 100644 --- a/impeller/renderer/backend/metal/command_buffer_mtl.h +++ b/impeller/renderer/backend/metal/command_buffer_mtl.h @@ -26,11 +26,14 @@ class CommandBufferMTL final : public CommandBuffer { CommandBufferMTL(id queue); + // |CommandBuffer| + void SetLabel(const std::string& label) const override; + // |CommandBuffer| bool IsValid() const override; // |CommandBuffer| - void SubmitCommands(CompletionCallback callback) override; + bool SubmitCommands(CompletionCallback callback) override; // |CommandBuffer| void ReserveSpotInQueue() override; diff --git a/impeller/renderer/backend/metal/command_buffer_mtl.mm b/impeller/renderer/backend/metal/command_buffer_mtl.mm index 8c55ae10a0119..672edfa0a36d2 100644 --- a/impeller/renderer/backend/metal/command_buffer_mtl.mm +++ b/impeller/renderer/backend/metal/command_buffer_mtl.mm @@ -22,6 +22,14 @@ return is_valid_; } +void CommandBufferMTL::SetLabel(const std::string& label) const { + if (label.empty()) { + return; + } + + [buffer_ setLabel:@(label.data())]; +} + static CommandBuffer::Status ToCommitResult(MTLCommandBufferStatus status) { switch (status) { case MTLCommandBufferStatusCompleted: @@ -34,13 +42,13 @@ return CommandBufferMTL::Status::kError; } -void CommandBufferMTL::SubmitCommands(CompletionCallback callback) { +bool CommandBufferMTL::SubmitCommands(CompletionCallback callback) { if (!buffer_) { // Already committed. This is caller error. if (callback) { callback(Status::kError); } - return; + return false; } if (callback) { @@ -51,6 +59,7 @@ [buffer_ commit]; buffer_ = nil; + return true; } void CommandBufferMTL::ReserveSpotInQueue() { diff --git a/impeller/renderer/command_buffer.cc b/impeller/renderer/command_buffer.cc index 73f4b71c70c0b..51ecf9b9dffe0 100644 --- a/impeller/renderer/command_buffer.cc +++ b/impeller/renderer/command_buffer.cc @@ -10,8 +10,8 @@ CommandBuffer::CommandBuffer() = default; CommandBuffer::~CommandBuffer() = default; -void CommandBuffer::SubmitCommands() { - SubmitCommands(nullptr); +bool CommandBuffer::SubmitCommands() { + return SubmitCommands(nullptr); } } // namespace impeller diff --git a/impeller/renderer/command_buffer.h b/impeller/renderer/command_buffer.h index c377baa3fbd05..10ac6a65a4247 100644 --- a/impeller/renderer/command_buffer.h +++ b/impeller/renderer/command_buffer.h @@ -41,6 +41,8 @@ class CommandBuffer { virtual bool IsValid() const = 0; + virtual void SetLabel(const std::string& label) const = 0; + //---------------------------------------------------------------------------- /// @brief Schedule the command encoded by render passes within this /// command buffer on the GPU. @@ -49,9 +51,9 @@ class CommandBuffer { /// /// @param[in] callback The completion callback. /// - virtual void SubmitCommands(CompletionCallback callback) = 0; + [[nodiscard]] virtual bool SubmitCommands(CompletionCallback callback) = 0; - void SubmitCommands(); + [[nodiscard]] bool SubmitCommands(); virtual void ReserveSpotInQueue() = 0; diff --git a/impeller/renderer/formats.h b/impeller/renderer/formats.h index 7088e16dcba43..10608fa8bea1a 100644 --- a/impeller/renderer/formats.h +++ b/impeller/renderer/formats.h @@ -138,7 +138,7 @@ enum class ColorWriteMask : uint64_t { kGreen = 1 << 1, kBlue = 1 << 2, kAlpha = 1 << 3, - kAll = kRed | kGreen | kBlue, + kAll = kRed | kGreen | kBlue | kAlpha, }; constexpr size_t BytesPerPixelForPixelFormat(PixelFormat format) { @@ -163,7 +163,7 @@ constexpr size_t BytesPerPixelForPixelFormat(PixelFormat format) { /// @brief Describe the color attachment that will be used with this /// pipeline. /// -/// Blending at specific color attachments follows the pseudocode: +/// Blending at specific color attachments follows the pseudo-code: /// ``` /// if (blending_enabled) { /// final_color.rgb = (src_color_blend_factor * new_color.rgb) @@ -248,7 +248,7 @@ enum class StencilOperation { kDecrementClamp, /// Perform a logical bitwise invert on the current stencil value. kInvert, - /// Increment the current stencil value by 1. If at maxium, set to zero. + /// Increment the current stencil value by 1. If at maximum, set to zero. kIncrementWrap, /// Decrement the current stencil value by 1. If at zero, set to maximum. kDecrementWrap, diff --git a/impeller/renderer/render_target.cc b/impeller/renderer/render_target.cc index 0fabd12ced91c..922f81a71ec82 100644 --- a/impeller/renderer/render_target.cc +++ b/impeller/renderer/render_target.cc @@ -4,6 +4,7 @@ #include "impeller/renderer/render_target.h" +#include "impeller/base/strings.h" #include "impeller/renderer/allocator.h" #include "impeller/renderer/context.h" #include "impeller/renderer/texture.h" @@ -110,7 +111,7 @@ RenderTarget RenderTarget::CreateOffscreen(const Context& context, return {}; } - color0.texture->SetLabel(label); + color0.texture->SetLabel(SPrintF("%sColorTexture", label.c_str())); StencilAttachment stencil0; stencil0.load_action = LoadAction::kClear; @@ -123,7 +124,7 @@ RenderTarget RenderTarget::CreateOffscreen(const Context& context, return {}; } - stencil0.texture->SetLabel(label); + stencil0.texture->SetLabel(SPrintF("%sStencilTexture", label.c_str())); RenderTarget target; target.SetColorAttachment(std::move(color0), 0u); diff --git a/impeller/renderer/renderer.cc b/impeller/renderer/renderer.cc index f7e5662f99f50..2a77090a2cb47 100644 --- a/impeller/renderer/renderer.cc +++ b/impeller/renderer/renderer.cc @@ -45,6 +45,8 @@ bool Renderer::Render(const Surface& surface, return false; } + command_buffer->SetLabel("Onscreen Command Buffer"); + auto render_pass = command_buffer->CreateRenderPass(surface.GetTargetRenderPassDescriptor()); if (!render_pass) { @@ -63,15 +65,13 @@ bool Renderer::Render(const Surface& surface, return false; } - command_buffer->SubmitCommands( + return command_buffer->SubmitCommands( [sema = frames_in_flight_sema_](CommandBuffer::Status result) { sema->Signal(); if (result != CommandBuffer::Status::kCompleted) { FML_LOG(ERROR) << "Could not commit command buffer."; } }); - - return true; } std::shared_ptr Renderer::GetContext() const { From 22dcbec2a914c64ca367ac6e0f16d16bb88d22a0 Mon Sep 17 00:00:00 2001 From: Chinmay Garde Date: Sun, 28 Nov 2021 12:27:45 -0800 Subject: [PATCH 215/433] Fixup ortho calculations and add ostream printers for geometry utils. --- impeller/aiks/aiks_unittests.cc | 2 +- impeller/geometry/geometry_unittests.h | 43 -------------------------- impeller/geometry/matrix.h | 21 +++++++++++-- impeller/geometry/point.h | 12 +++++++ impeller/geometry/quaternion.cc | 7 ----- impeller/geometry/quaternion.h | 16 ++++++++-- impeller/geometry/rect.h | 12 +++++++ impeller/geometry/size.h | 12 +++++++ 8 files changed, 69 insertions(+), 56 deletions(-) diff --git a/impeller/aiks/aiks_unittests.cc b/impeller/aiks/aiks_unittests.cc index 07fae56e3a32d..e29d40d8bc748 100644 --- a/impeller/aiks/aiks_unittests.cc +++ b/impeller/aiks/aiks_unittests.cc @@ -119,7 +119,7 @@ TEST_F(AiksTest, CanRenderGroupOpacity) { Paint alpha; alpha.color = Color::Red().WithAlpha(0.5); - canvas.SaveLayer(alpha); + // canvas.SaveLayer(alpha); canvas.DrawRect({000, 000, 100, 100}, red); canvas.DrawRect({020, 020, 100, 100}, green); diff --git a/impeller/geometry/geometry_unittests.h b/impeller/geometry/geometry_unittests.h index e247facd523e3..746df49df6075 100644 --- a/impeller/geometry/geometry_unittests.h +++ b/impeller/geometry/geometry_unittests.h @@ -11,49 +11,6 @@ #include "impeller/geometry/size.h" #include "impeller/geometry/vector.h" -namespace std { - -inline std::ostream& operator<<(std::ostream& out, const impeller::Matrix& m) { - out << "("; - for (size_t i = 0; i < 4u; i++) { - for (size_t j = 0; j < 4u; j++) { - out << m.e[i][j] << ","; - } - out << std::endl; - } - out << ")"; - return out; -} - -inline std::ostream& operator<<(std::ostream& out, - const impeller::Quaternion& q) { - out << "(" << q.x << ", " << q.y << ", " << q.z << ", " << q.w << ")"; - return out; -} - -template -inline std::ostream& operator<<(std::ostream& out, - const impeller::TSize& s) { - out << "(" << s.width << ", " << s.height << ")"; - return out; -} - -template -inline std::ostream& operator<<(std::ostream& out, - const impeller::TPoint& p) { - out << "(" << p.x << ", " << p.y << ")"; - return out; -} - -template -inline std::ostream& operator<<(std::ostream& out, - const impeller::TRect& r) { - out << "(" << r.origin << ", " << r.size << ")"; - return out; -} - -} // namespace std - inline bool NumberNear(double a, double b) { static const double epsilon = 1e-3; return (a > (b - epsilon)) && (a < (b + epsilon)); diff --git a/impeller/geometry/matrix.h b/impeller/geometry/matrix.h index 3156a81009a7b..a52e5eeed0375 100644 --- a/impeller/geometry/matrix.h +++ b/impeller/geometry/matrix.h @@ -6,6 +6,7 @@ #include #include +#include #include #include "impeller/geometry/matrix_decomposition.h" @@ -257,8 +258,8 @@ struct Matrix { static constexpr Matrix MakeOrthographic(TSize size) { // Per assumptions about NDC documented above. const auto scale = - MakeScale({1.0f / static_cast(size.width), - -1.0f / static_cast(size.height), 1.0}); + MakeScale({2.0f / static_cast(size.width), + -2.0f / static_cast(size.height), 1.0}); const auto translate = MakeTranslation({-1.0, 1.0, 0.5}); return translate * scale; } @@ -275,3 +276,19 @@ inline Vector4 operator*(const Vector4& v, const Matrix& m) { } } // namespace impeller + +namespace std { + +inline std::ostream& operator<<(std::ostream& out, const impeller::Matrix& m) { + out << "("; + for (size_t i = 0; i < 4u; i++) { + for (size_t j = 0; j < 4u; j++) { + out << m.e[i][j] << ","; + } + out << std::endl; + } + out << ")"; + return out; +} + +} // namespace std diff --git a/impeller/geometry/point.h b/impeller/geometry/point.h index ba12338557c52..e84338796d5e2 100644 --- a/impeller/geometry/point.h +++ b/impeller/geometry/point.h @@ -6,6 +6,7 @@ #include #include +#include #include #include "impeller/geometry/scalar.h" @@ -113,3 +114,14 @@ using Point = TPoint; using IPoint = TPoint; } // namespace impeller + +namespace std { + +template +inline std::ostream& operator<<(std::ostream& out, + const impeller::TPoint& p) { + out << "(" << p.x << ", " << p.y << ")"; + return out; +} + +} // namespace std diff --git a/impeller/geometry/quaternion.cc b/impeller/geometry/quaternion.cc index b00e350ed9691..2c5a75b286b53 100644 --- a/impeller/geometry/quaternion.cc +++ b/impeller/geometry/quaternion.cc @@ -27,11 +27,4 @@ Quaternion Quaternion::Slerp(const Quaternion& to, double time) const { } } -std::string Quaternion::ToString() const { - std::stringstream stream; - stream << "{" << x << ", " - << ", " << y << ", " << z << ", " << w << "}"; - return stream.str(); -} - } // namespace impeller diff --git a/impeller/geometry/quaternion.h b/impeller/geometry/quaternion.h index 00b9e0c1d904c..4985287000b71 100644 --- a/impeller/geometry/quaternion.h +++ b/impeller/geometry/quaternion.h @@ -4,7 +4,9 @@ #pragma once -#include "vector.h" +#include + +#include "impeller/geometry/vector.h" namespace impeller { @@ -73,8 +75,16 @@ struct Quaternion { bool operator!=(const Quaternion& o) const { return x != o.x || y != o.y || z != o.z || w != o.w; } - - std::string ToString() const; }; } // namespace impeller + +namespace std { + +inline std::ostream& operator<<(std::ostream& out, + const impeller::Quaternion& q) { + out << "(" << q.x << ", " << q.y << ", " << q.z << ", " << q.w << ")"; + return out; +} + +} // namespace std diff --git a/impeller/geometry/rect.h b/impeller/geometry/rect.h index 4fcd69d15abf4..c7477d806c9ee 100644 --- a/impeller/geometry/rect.h +++ b/impeller/geometry/rect.h @@ -4,6 +4,7 @@ #pragma once +#include #include #include "impeller/geometry/point.h" @@ -122,3 +123,14 @@ using Rect = TRect; using IRect = TRect; } // namespace impeller + +namespace std { + +template +inline std::ostream& operator<<(std::ostream& out, + const impeller::TRect& r) { + out << "(" << r.origin << ", " << r.size << ")"; + return out; +} + +} // namespace std diff --git a/impeller/geometry/size.h b/impeller/geometry/size.h index 450f11b19a4de..11301eaa9d9a4 100644 --- a/impeller/geometry/size.h +++ b/impeller/geometry/size.h @@ -6,6 +6,7 @@ #include #include +#include #include #include "impeller/geometry/scalar.h" @@ -100,3 +101,14 @@ using ISize = TSize; static_assert(sizeof(Size) == 2 * sizeof(Scalar)); } // namespace impeller + +namespace std { + +template +inline std::ostream& operator<<(std::ostream& out, + const impeller::TSize& s) { + out << "(" << s.width << ", " << s.height << ")"; + return out; +} + +} // namespace std From dadb471eb4343d26fda1860d6632ad4bc156458c Mon Sep 17 00:00:00 2001 From: Chinmay Garde Date: Sun, 28 Nov 2021 13:33:14 -0800 Subject: [PATCH 216/433] Setup a framework to collapse passes. --- impeller/aiks/BUILD.gn | 2 ++ impeller/aiks/aiks_unittests.cc | 8 ++--- impeller/aiks/canvas_pass.cc | 43 ++++++++++++++++++++------- impeller/aiks/canvas_pass.h | 5 +++- impeller/aiks/canvas_pass_delegate.cc | 35 ++++++++++++++++++++++ impeller/aiks/canvas_pass_delegate.h | 32 ++++++++++++++++++++ 6 files changed, 110 insertions(+), 15 deletions(-) create mode 100644 impeller/aiks/canvas_pass_delegate.cc create mode 100644 impeller/aiks/canvas_pass_delegate.h diff --git a/impeller/aiks/BUILD.gn b/impeller/aiks/BUILD.gn index 0c6bc01a00578..a179fa2458081 100644 --- a/impeller/aiks/BUILD.gn +++ b/impeller/aiks/BUILD.gn @@ -12,6 +12,8 @@ impeller_component("aiks") { "canvas.h", "canvas_pass.cc", "canvas_pass.h", + "canvas_pass_delegate.cc", + "canvas_pass_delegate.h", "image.cc", "image.h", "paint.cc", diff --git a/impeller/aiks/aiks_unittests.cc b/impeller/aiks/aiks_unittests.cc index e29d40d8bc748..c64d894003159 100644 --- a/impeller/aiks/aiks_unittests.cc +++ b/impeller/aiks/aiks_unittests.cc @@ -110,16 +110,16 @@ TEST_F(AiksTest, CanRenderGroupOpacity) { Canvas canvas; Paint red; - red.color = Color::Red(); //.WithAlpha(0.5); + red.color = Color::Red(); Paint green; - green.color = Color::Green(); //.WithAlpha(0.5); + green.color = Color::Green(); Paint blue; - blue.color = Color::Blue(); //.WithAlpha(0.5); + blue.color = Color::Blue(); Paint alpha; alpha.color = Color::Red().WithAlpha(0.5); - // canvas.SaveLayer(alpha); + canvas.SaveLayer(alpha); canvas.DrawRect({000, 000, 100, 100}, red); canvas.DrawRect({020, 020, 100, 100}, green); diff --git a/impeller/aiks/canvas_pass.cc b/impeller/aiks/canvas_pass.cc index ad3aa1fb52892..c177f6c221a8f 100644 --- a/impeller/aiks/canvas_pass.cc +++ b/impeller/aiks/canvas_pass.cc @@ -8,11 +8,15 @@ #include "impeller/geometry/path_builder.h" #include "impeller/renderer/command_buffer.h" #include "impeller/renderer/render_pass.h" -#include "impeller/renderer/render_target.h" namespace impeller { -CanvasPass::CanvasPass() = default; +CanvasPass::CanvasPass(std::unique_ptr delegate) + : delegate_(std::move(delegate)) { + if (!delegate_) { + delegate_ = CanvasPassDelegate::MakeDefault(); + } +} CanvasPass::~CanvasPass() = default; @@ -85,8 +89,12 @@ bool CanvasPass::Render(ContentRenderer& renderer, } } for (const auto& subpass : subpasses_) { - if (!subpass) { - return false; + if (delegate_->CanCollapseIntoParentPass()) { + // Directly render into the parent pass and move on. + if (!subpass->Render(renderer, parent_pass)) { + return false; + } + continue; } const auto subpass_coverage = subpass->GetCoverageRect(); @@ -102,6 +110,27 @@ bool CanvasPass::Render(ContentRenderer& renderer, auto subpass_target = RenderTarget::CreateOffscreen( *context, ISize::Ceil(subpass_coverage.size)); + auto subpass_texture = subpass_target.GetRenderTargetTexture(); + + if (!subpass_texture) { + return false; + } + + auto offscreen_texture_contents = + delegate_->CreateContentsForSubpassTarget(*subpass_texture); + + if (!offscreen_texture_contents) { + // This is an error because the subpass delegate said the pass couldn't be + // collapsed into its parent. Yet, when asked how it want's to postprocess + // the offscreen texture, it couldn't give us an answer. + // + // Theoretically, we could collapse the pass now. But that would be + // wasteful as we already have the offscreen texture and we don't want to + // discard it without ever using it. Just make the delegate do the right + // thing. + return false; + } + auto sub_command_buffer = context->CreateRenderCommandBuffer(); sub_command_buffer->SetLabel("Offscreen Command Buffer"); @@ -130,12 +159,6 @@ bool CanvasPass::Render(ContentRenderer& renderer, return false; } - auto offscreen_texture_contents = std::make_shared(); - offscreen_texture_contents->SetTexture( - subpass_target.GetRenderTargetTexture()); - offscreen_texture_contents->SetSourceRect( - IRect::MakeSize(subpass_target.GetRenderTargetTexture()->GetSize())); - Entity entity; entity.SetPath(PathBuilder{}.AddRect(subpass_coverage).CreatePath()); entity.SetContents(std::move(offscreen_texture_contents)); diff --git a/impeller/aiks/canvas_pass.h b/impeller/aiks/canvas_pass.h index 4a942010acb53..6f846321be88c 100644 --- a/impeller/aiks/canvas_pass.h +++ b/impeller/aiks/canvas_pass.h @@ -9,8 +9,10 @@ #include #include "flutter/fml/macros.h" +#include "impeller/aiks/canvas_pass_delegate.h" #include "impeller/entity/contents.h" #include "impeller/entity/entity.h" +#include "impeller/renderer/render_target.h" namespace impeller { @@ -21,7 +23,7 @@ class CanvasPass { using Entities = std::vector; using Subpasses = std::vector>; - CanvasPass(); + CanvasPass(std::unique_ptr delegate = nullptr); ~CanvasPass(); @@ -57,6 +59,7 @@ class CanvasPass { CanvasPass* superpass_ = nullptr; Matrix xformation_; size_t stencil_depth_ = 0u; + std::unique_ptr delegate_; FML_DISALLOW_COPY_AND_ASSIGN(CanvasPass); }; diff --git a/impeller/aiks/canvas_pass_delegate.cc b/impeller/aiks/canvas_pass_delegate.cc new file mode 100644 index 0000000000000..cd5a07f11a6bc --- /dev/null +++ b/impeller/aiks/canvas_pass_delegate.cc @@ -0,0 +1,35 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "impeller/aiks/canvas_pass_delegate.h" + +namespace impeller { + +CanvasPassDelegate::CanvasPassDelegate() = default; + +CanvasPassDelegate::~CanvasPassDelegate() = default; + +class DefaultCanvasPassDelegate final : public CanvasPassDelegate { + public: + DefaultCanvasPassDelegate() = default; + + ~DefaultCanvasPassDelegate() override = default; + + bool CanCollapseIntoParentPass() override { return true; } + + std::shared_ptr CreateContentsForSubpassTarget( + const Texture& target) override { + // Not possible since this pass always collapses into its parent. + FML_UNREACHABLE(); + } + + private: + FML_DISALLOW_COPY_AND_ASSIGN(DefaultCanvasPassDelegate); +}; + +std::unique_ptr CanvasPassDelegate::MakeDefault() { + return std::make_unique(); +} + +} // namespace impeller diff --git a/impeller/aiks/canvas_pass_delegate.h b/impeller/aiks/canvas_pass_delegate.h new file mode 100644 index 0000000000000..b41feb3c9789e --- /dev/null +++ b/impeller/aiks/canvas_pass_delegate.h @@ -0,0 +1,32 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#pragma once + +#include + +#include "flutter/fml/macros.h" +#include "impeller/entity/contents.h" +#include "impeller/renderer/texture.h" + +namespace impeller { + +class CanvasPassDelegate { + public: + static std::unique_ptr MakeDefault(); + + CanvasPassDelegate(); + + virtual ~CanvasPassDelegate(); + + virtual bool CanCollapseIntoParentPass() = 0; + + virtual std::shared_ptr CreateContentsForSubpassTarget( + const Texture& target) = 0; + + private: + FML_DISALLOW_COPY_AND_ASSIGN(CanvasPassDelegate); +}; + +} // namespace impeller From 8526be0a5b48a6db669744595593b2e36128abd5 Mon Sep 17 00:00:00 2001 From: Chinmay Garde Date: Sun, 28 Nov 2021 13:38:17 -0800 Subject: [PATCH 217/433] Move entity pass management to the entity framework. --- impeller/aiks/BUILD.gn | 4 --- impeller/aiks/canvas.cc | 6 ++-- impeller/aiks/canvas.h | 8 ++--- impeller/aiks/picture.h | 4 +-- impeller/entity/BUILD.gn | 4 +++ .../canvas_pass.cc => entity/entity_pass.cc} | 36 +++++++++---------- .../canvas_pass.h => entity/entity_pass.h} | 24 +++++++------ .../entity_pass_delegate.cc} | 18 +++++----- .../entity_pass_delegate.h} | 10 +++--- 9 files changed, 58 insertions(+), 56 deletions(-) rename impeller/{aiks/canvas_pass.cc => entity/entity_pass.cc} (82%) rename impeller/{aiks/canvas_pass.h => entity/entity_pass.h} (66%) rename impeller/{aiks/canvas_pass_delegate.cc => entity/entity_pass_delegate.cc} (51%) rename impeller/{aiks/canvas_pass_delegate.h => entity/entity_pass_delegate.h} (73%) diff --git a/impeller/aiks/BUILD.gn b/impeller/aiks/BUILD.gn index a179fa2458081..a061204f7806d 100644 --- a/impeller/aiks/BUILD.gn +++ b/impeller/aiks/BUILD.gn @@ -10,10 +10,6 @@ impeller_component("aiks") { "aiks_renderer.h", "canvas.cc", "canvas.h", - "canvas_pass.cc", - "canvas_pass.h", - "canvas_pass_delegate.cc", - "canvas_pass_delegate.h", "image.cc", "image.h", "paint.cc", diff --git a/impeller/aiks/canvas.cc b/impeller/aiks/canvas.cc index 842f6b6373fba..f41aa125451f5 100644 --- a/impeller/aiks/canvas.cc +++ b/impeller/aiks/canvas.cc @@ -18,7 +18,7 @@ Canvas::Canvas() { Canvas::~Canvas() = default; void Canvas::Initialize() { - base_pass_ = std::make_unique(); + base_pass_ = std::make_unique(); current_pass_ = base_pass_.get(); xformation_stack_.emplace_back(CanvasStackEntry{}); FML_DCHECK(GetSaveCount() == 1u); @@ -173,7 +173,7 @@ Picture Canvas::EndRecordingAsPicture() { return picture; } -CanvasPass& Canvas::GetCurrentPass() { +EntityPass& Canvas::GetCurrentPass() { FML_DCHECK(current_pass_ != nullptr); return *current_pass_; } @@ -194,7 +194,7 @@ void Canvas::Save(bool create_subpass) { auto entry = CanvasStackEntry{}; if (create_subpass) { entry.is_subpass = true; - current_pass_ = GetCurrentPass().AddSubpass(std::make_unique()); + current_pass_ = GetCurrentPass().AddSubpass(std::make_unique()); current_pass_->SetTransformation(xformation_stack_.back().xformation); current_pass_->SetStencilDepth(xformation_stack_.back().stencil_depth); } else { diff --git a/impeller/aiks/canvas.h b/impeller/aiks/canvas.h index 06d90e54f334c..470c9b1b6ad6a 100644 --- a/impeller/aiks/canvas.h +++ b/impeller/aiks/canvas.h @@ -10,10 +10,10 @@ #include #include "flutter/fml/macros.h" -#include "impeller/aiks/canvas_pass.h" #include "impeller/aiks/image.h" #include "impeller/aiks/paint.h" #include "impeller/aiks/picture.h" +#include "impeller/entity/entity_pass.h" #include "impeller/geometry/matrix.h" #include "impeller/geometry/path.h" #include "impeller/geometry/point.h" @@ -69,15 +69,15 @@ class Canvas { Picture EndRecordingAsPicture(); private: - std::unique_ptr base_pass_; - CanvasPass* current_pass_ = nullptr; + std::unique_ptr base_pass_; + EntityPass* current_pass_ = nullptr; std::deque xformation_stack_; void Initialize(); void Reset(); - CanvasPass& GetCurrentPass(); + EntityPass& GetCurrentPass(); void IncrementStencilDepth(); diff --git a/impeller/aiks/picture.h b/impeller/aiks/picture.h index cf676f91c886f..e12566b00070a 100644 --- a/impeller/aiks/picture.h +++ b/impeller/aiks/picture.h @@ -8,13 +8,13 @@ #include #include "flutter/fml/macros.h" -#include "impeller/aiks/canvas_pass.h" #include "impeller/entity/entity.h" +#include "impeller/entity/entity_pass.h" namespace impeller { struct Picture { - std::unique_ptr pass; + std::unique_ptr pass; }; } // namespace impeller diff --git a/impeller/entity/BUILD.gn b/impeller/entity/BUILD.gn index 6dac3568748c8..200724b22cdd5 100644 --- a/impeller/entity/BUILD.gn +++ b/impeller/entity/BUILD.gn @@ -27,6 +27,10 @@ impeller_component("entity") { "contents.h", "entity.cc", "entity.h", + "entity_pass.cc", + "entity_pass.h", + "entity_pass_delegate.cc", + "entity_pass_delegate.h", ] deps = [ ":entity_shaders" ] diff --git a/impeller/aiks/canvas_pass.cc b/impeller/entity/entity_pass.cc similarity index 82% rename from impeller/aiks/canvas_pass.cc rename to impeller/entity/entity_pass.cc index c177f6c221a8f..22128553b0e10 100644 --- a/impeller/aiks/canvas_pass.cc +++ b/impeller/entity/entity_pass.cc @@ -2,7 +2,7 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -#include "impeller/aiks/canvas_pass.h" +#include "impeller/entity/entity_pass.h" #include "impeller/entity/content_renderer.h" #include "impeller/geometry/path_builder.h" @@ -11,28 +11,28 @@ namespace impeller { -CanvasPass::CanvasPass(std::unique_ptr delegate) +EntityPass::EntityPass(std::unique_ptr delegate) : delegate_(std::move(delegate)) { if (!delegate_) { - delegate_ = CanvasPassDelegate::MakeDefault(); + delegate_ = EntityPassDelegate::MakeDefault(); } } -CanvasPass::~CanvasPass() = default; +EntityPass::~EntityPass() = default; -void CanvasPass::AddEntity(Entity entity) { +void EntityPass::AddEntity(Entity entity) { entities_.emplace_back(std::move(entity)); } -const std::vector& CanvasPass::GetEntities() const { +const std::vector& EntityPass::GetEntities() const { return entities_; } -void CanvasPass::SetEntities(Entities entities) { +void EntityPass::SetEntities(Entities entities) { entities_ = std::move(entities); } -size_t CanvasPass::GetSubpassesDepth() const { +size_t EntityPass::GetSubpassesDepth() const { size_t max_subpass_depth = 0u; for (const auto& subpass : subpasses_) { max_subpass_depth = @@ -41,7 +41,7 @@ size_t CanvasPass::GetSubpassesDepth() const { return max_subpass_depth + 1u; } -Rect CanvasPass::GetCoverageRect() const { +Rect EntityPass::GetCoverageRect() const { std::optional min, max; for (const auto& entity : entities_) { auto coverage = entity.GetPath().GetMinMaxCoveragePoints(); @@ -64,15 +64,15 @@ Rect CanvasPass::GetCoverageRect() const { return {min->x, min->y, diff.x, diff.y}; } -CanvasPass* CanvasPass::GetSuperpass() const { +EntityPass* EntityPass::GetSuperpass() const { return superpass_; } -const CanvasPass::Subpasses& CanvasPass::GetSubpasses() const { +const EntityPass::Subpasses& EntityPass::GetSubpasses() const { return subpasses_; } -CanvasPass* CanvasPass::AddSubpass(std::unique_ptr pass) { +EntityPass* EntityPass::AddSubpass(std::unique_ptr pass) { if (!pass) { return nullptr; } @@ -81,7 +81,7 @@ CanvasPass* CanvasPass::AddSubpass(std::unique_ptr pass) { return subpasses_.emplace_back(std::move(pass)).get(); } -bool CanvasPass::Render(ContentRenderer& renderer, +bool EntityPass::Render(ContentRenderer& renderer, RenderPass& parent_pass) const { for (const auto& entity : entities_) { if (!entity.Render(renderer, parent_pass)) { @@ -172,7 +172,7 @@ bool CanvasPass::Render(ContentRenderer& renderer, return true; } -void CanvasPass::IterateAllEntities(std::function iterator) { +void EntityPass::IterateAllEntities(std::function iterator) { if (!iterator) { return; } @@ -188,8 +188,8 @@ void CanvasPass::IterateAllEntities(std::function iterator) { } } -std::unique_ptr CanvasPass::Clone() const { - auto pass = std::make_unique(); +std::unique_ptr EntityPass::Clone() const { + auto pass = std::make_unique(); pass->SetEntities(entities_); for (const auto& subpass : subpasses_) { pass->AddSubpass(subpass->Clone()); @@ -197,11 +197,11 @@ std::unique_ptr CanvasPass::Clone() const { return pass; } -void CanvasPass::SetTransformation(Matrix xformation) { +void EntityPass::SetTransformation(Matrix xformation) { xformation_ = std::move(xformation); } -void CanvasPass::SetStencilDepth(size_t stencil_depth) { +void EntityPass::SetStencilDepth(size_t stencil_depth) { stencil_depth_ = stencil_depth; } diff --git a/impeller/aiks/canvas_pass.h b/impeller/entity/entity_pass.h similarity index 66% rename from impeller/aiks/canvas_pass.h rename to impeller/entity/entity_pass.h index 6f846321be88c..5c1b1315e5e79 100644 --- a/impeller/aiks/canvas_pass.h +++ b/impeller/entity/entity_pass.h @@ -9,30 +9,32 @@ #include #include "flutter/fml/macros.h" -#include "impeller/aiks/canvas_pass_delegate.h" #include "impeller/entity/contents.h" #include "impeller/entity/entity.h" +#include "impeller/entity/entity_pass_delegate.h" #include "impeller/renderer/render_target.h" namespace impeller { class ContentRenderer; -class CanvasPass { +class EntityPass { public: using Entities = std::vector; - using Subpasses = std::vector>; + using Subpasses = std::vector>; - CanvasPass(std::unique_ptr delegate = nullptr); + EntityPass(std::unique_ptr delegate = nullptr); - ~CanvasPass(); + ~EntityPass(); size_t GetSubpassesDepth() const; - std::unique_ptr Clone() const; + std::unique_ptr Clone() const; Rect GetCoverageRect() const; + // TODO(csg): This prevents an optimization where the coverage can be + // calculated once in SetEntities an memoized. void AddEntity(Entity entity); void SetEntities(Entities entities); @@ -41,9 +43,9 @@ class CanvasPass { const Subpasses& GetSubpasses() const; - CanvasPass* AddSubpass(std::unique_ptr pass); + EntityPass* AddSubpass(std::unique_ptr pass); - CanvasPass* GetSuperpass() const; + EntityPass* GetSuperpass() const; bool Render(ContentRenderer& renderer, RenderPass& parent_pass) const; @@ -56,12 +58,12 @@ class CanvasPass { private: Entities entities_; Subpasses subpasses_; - CanvasPass* superpass_ = nullptr; + EntityPass* superpass_ = nullptr; Matrix xformation_; size_t stencil_depth_ = 0u; - std::unique_ptr delegate_; + std::unique_ptr delegate_; - FML_DISALLOW_COPY_AND_ASSIGN(CanvasPass); + FML_DISALLOW_COPY_AND_ASSIGN(EntityPass); }; struct CanvasStackEntry { diff --git a/impeller/aiks/canvas_pass_delegate.cc b/impeller/entity/entity_pass_delegate.cc similarity index 51% rename from impeller/aiks/canvas_pass_delegate.cc rename to impeller/entity/entity_pass_delegate.cc index cd5a07f11a6bc..a1567f451d1de 100644 --- a/impeller/aiks/canvas_pass_delegate.cc +++ b/impeller/entity/entity_pass_delegate.cc @@ -2,19 +2,19 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -#include "impeller/aiks/canvas_pass_delegate.h" +#include "impeller/entity/entity_pass_delegate.h" namespace impeller { -CanvasPassDelegate::CanvasPassDelegate() = default; +EntityPassDelegate::EntityPassDelegate() = default; -CanvasPassDelegate::~CanvasPassDelegate() = default; +EntityPassDelegate::~EntityPassDelegate() = default; -class DefaultCanvasPassDelegate final : public CanvasPassDelegate { +class DefaultEntityPassDelegate final : public EntityPassDelegate { public: - DefaultCanvasPassDelegate() = default; + DefaultEntityPassDelegate() = default; - ~DefaultCanvasPassDelegate() override = default; + ~DefaultEntityPassDelegate() override = default; bool CanCollapseIntoParentPass() override { return true; } @@ -25,11 +25,11 @@ class DefaultCanvasPassDelegate final : public CanvasPassDelegate { } private: - FML_DISALLOW_COPY_AND_ASSIGN(DefaultCanvasPassDelegate); + FML_DISALLOW_COPY_AND_ASSIGN(DefaultEntityPassDelegate); }; -std::unique_ptr CanvasPassDelegate::MakeDefault() { - return std::make_unique(); +std::unique_ptr EntityPassDelegate::MakeDefault() { + return std::make_unique(); } } // namespace impeller diff --git a/impeller/aiks/canvas_pass_delegate.h b/impeller/entity/entity_pass_delegate.h similarity index 73% rename from impeller/aiks/canvas_pass_delegate.h rename to impeller/entity/entity_pass_delegate.h index b41feb3c9789e..03a8d05853eba 100644 --- a/impeller/aiks/canvas_pass_delegate.h +++ b/impeller/entity/entity_pass_delegate.h @@ -12,13 +12,13 @@ namespace impeller { -class CanvasPassDelegate { +class EntityPassDelegate { public: - static std::unique_ptr MakeDefault(); + static std::unique_ptr MakeDefault(); - CanvasPassDelegate(); + EntityPassDelegate(); - virtual ~CanvasPassDelegate(); + virtual ~EntityPassDelegate(); virtual bool CanCollapseIntoParentPass() = 0; @@ -26,7 +26,7 @@ class CanvasPassDelegate { const Texture& target) = 0; private: - FML_DISALLOW_COPY_AND_ASSIGN(CanvasPassDelegate); + FML_DISALLOW_COPY_AND_ASSIGN(EntityPassDelegate); }; } // namespace impeller From aef77f70f5539cb01c9ce8f3a0748a00f18190ff Mon Sep 17 00:00:00 2001 From: Chinmay Garde Date: Sun, 28 Nov 2021 14:28:44 -0800 Subject: [PATCH 218/433] Setup a paint pass delgate. --- impeller/aiks/BUILD.gn | 2 ++ impeller/aiks/aiks_unittests.cc | 2 +- impeller/aiks/canvas.cc | 5 +++- impeller/aiks/canvas.h | 2 +- impeller/aiks/paint.h | 2 +- impeller/aiks/paint_pass_delegate.cc | 35 +++++++++++++++++++++++ impeller/aiks/paint_pass_delegate.h | 33 +++++++++++++++++++++ impeller/entity/contents.cc | 5 ++++ impeller/entity/contents.h | 3 ++ impeller/entity/entity_pass.cc | 16 ++++++----- impeller/entity/entity_pass.h | 7 +++-- impeller/entity/entity_pass_delegate.cc | 2 +- impeller/entity/entity_pass_delegate.h | 2 +- impeller/entity/shaders/texture_fill.frag | 2 ++ impeller/entity/shaders/texture_fill.vert | 3 ++ impeller/geometry/color.h | 2 ++ 16 files changed, 108 insertions(+), 15 deletions(-) create mode 100644 impeller/aiks/paint_pass_delegate.cc create mode 100644 impeller/aiks/paint_pass_delegate.h diff --git a/impeller/aiks/BUILD.gn b/impeller/aiks/BUILD.gn index a061204f7806d..a3398f394b54a 100644 --- a/impeller/aiks/BUILD.gn +++ b/impeller/aiks/BUILD.gn @@ -14,6 +14,8 @@ impeller_component("aiks") { "image.h", "paint.cc", "paint.h", + "paint_pass_delegate.cc", + "paint_pass_delegate.h", "picture.cc", "picture.h", "picture_recorder.cc", diff --git a/impeller/aiks/aiks_unittests.cc b/impeller/aiks/aiks_unittests.cc index c64d894003159..ecc3b4fa0ec39 100644 --- a/impeller/aiks/aiks_unittests.cc +++ b/impeller/aiks/aiks_unittests.cc @@ -112,7 +112,7 @@ TEST_F(AiksTest, CanRenderGroupOpacity) { Paint red; red.color = Color::Red(); Paint green; - green.color = Color::Green(); + green.color = Color::Green().WithAlpha(0.5); Paint blue; blue.color = Color::Blue(); diff --git a/impeller/aiks/canvas.cc b/impeller/aiks/canvas.cc index f41aa125451f5..1c11d886ba172 100644 --- a/impeller/aiks/canvas.cc +++ b/impeller/aiks/canvas.cc @@ -7,6 +7,7 @@ #include #include "flutter/fml/logging.h" +#include "impeller/aiks/paint_pass_delegate.h" #include "impeller/geometry/path_builder.h" namespace impeller { @@ -90,7 +91,9 @@ void Canvas::DrawPath(Path path, Paint paint) { GetCurrentPass().AddEntity(std::move(entity)); } -void Canvas::SaveLayer(const Paint& paint, std::optional bounds) { +void Canvas::SaveLayer(Paint paint, std::optional bounds) { + GetCurrentPass().SetDelegate( + std::make_unique(std::move(paint))); Save(true); } diff --git a/impeller/aiks/canvas.h b/impeller/aiks/canvas.h index 470c9b1b6ad6a..c7b16fcdaf6b8 100644 --- a/impeller/aiks/canvas.h +++ b/impeller/aiks/canvas.h @@ -31,7 +31,7 @@ class Canvas { void Save(); - void SaveLayer(const Paint& paint, std::optional bounds = std::nullopt); + void SaveLayer(Paint paint, std::optional bounds = std::nullopt); bool Restore(); diff --git a/impeller/aiks/paint.h b/impeller/aiks/paint.h index 98cb1ce1cac49..6334e42471375 100644 --- a/impeller/aiks/paint.h +++ b/impeller/aiks/paint.h @@ -18,7 +18,7 @@ struct Paint { kStroke, }; - Color color; + Color color = Color::Black(); Scalar stroke_width = 0.0; Style style = Style::kFill; diff --git a/impeller/aiks/paint_pass_delegate.cc b/impeller/aiks/paint_pass_delegate.cc new file mode 100644 index 0000000000000..5a683cfe6655d --- /dev/null +++ b/impeller/aiks/paint_pass_delegate.cc @@ -0,0 +1,35 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "impeller/aiks/paint_pass_delegate.h" + +#include "impeller/entity/contents.h" + +namespace impeller { + +PaintPassDelegate::PaintPassDelegate(Paint paint) : paint_(std::move(paint)) {} + +// |EntityPassDelgate| +PaintPassDelegate::~PaintPassDelegate() = default; + +// |EntityPassDelgate| +bool PaintPassDelegate::CanCollapseIntoParentPass() { + if (paint_.color.IsOpaque()) { + return true; + } + + return false; +} + +// |EntityPassDelgate| +std::shared_ptr PaintPassDelegate::CreateContentsForSubpassTarget( + std::shared_ptr target) { + auto contents = std::make_shared(); + contents->SetTexture(target); + contents->SetSourceRect(IRect::MakeSize(target->GetSize())); + contents->SetOpacity(paint_.color.alpha); + return contents; +} + +} // namespace impeller diff --git a/impeller/aiks/paint_pass_delegate.h b/impeller/aiks/paint_pass_delegate.h new file mode 100644 index 0000000000000..b731503971d7a --- /dev/null +++ b/impeller/aiks/paint_pass_delegate.h @@ -0,0 +1,33 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#pragma once + +#include "flutter/fml/macros.h" +#include "impeller/aiks/paint.h" +#include "impeller/entity/entity_pass_delegate.h" + +namespace impeller { + +class PaintPassDelegate final : public EntityPassDelegate { + public: + PaintPassDelegate(Paint paint); + + // |EntityPassDelgate| + ~PaintPassDelegate() override; + + // |EntityPassDelgate| + bool CanCollapseIntoParentPass() override; + + // |EntityPassDelgate| + std::shared_ptr CreateContentsForSubpassTarget( + std::shared_ptr target) override; + + private: + const Paint paint_; + + FML_DISALLOW_COPY_AND_ASSIGN(PaintPassDelegate); +}; + +} // namespace impeller diff --git a/impeller/entity/contents.cc b/impeller/entity/contents.cc index 3bf1c0daba8a9..48be6459c92bc 100644 --- a/impeller/entity/contents.cc +++ b/impeller/entity/contents.cc @@ -184,6 +184,10 @@ std::shared_ptr TextureContents::GetTexture() const { return texture_; } +void TextureContents::SetOpacity(Scalar opacity) { + opacity_ = opacity; +} + bool TextureContents::Render(const ContentRenderer& renderer, const Entity& entity, RenderPass& pass) const { @@ -233,6 +237,7 @@ bool TextureContents::Render(const ContentRenderer& renderer, VS::FrameInfo frame_info; frame_info.mvp = Matrix::MakeOrthographic(pass.GetRenderTargetSize()) * entity.GetTransformation(); + frame_info.alpha = opacity_; Command cmd; cmd.label = "TextureFill"; diff --git a/impeller/entity/contents.h b/impeller/entity/contents.h index 381a11f5f011f..ffda5f7d432a8 100644 --- a/impeller/entity/contents.h +++ b/impeller/entity/contents.h @@ -94,6 +94,8 @@ class TextureContents final : public Contents { void SetSourceRect(const IRect& source_rect); + void SetOpacity(Scalar opacity); + const IRect& GetSourceRect() const; // |Contents| @@ -104,6 +106,7 @@ class TextureContents final : public Contents { public: std::shared_ptr texture_; IRect source_rect_; + Scalar opacity_ = 1.0f; FML_DISALLOW_COPY_AND_ASSIGN(TextureContents); }; diff --git a/impeller/entity/entity_pass.cc b/impeller/entity/entity_pass.cc index 22128553b0e10..40866649e38bb 100644 --- a/impeller/entity/entity_pass.cc +++ b/impeller/entity/entity_pass.cc @@ -11,15 +11,17 @@ namespace impeller { -EntityPass::EntityPass(std::unique_ptr delegate) - : delegate_(std::move(delegate)) { - if (!delegate_) { - delegate_ = EntityPassDelegate::MakeDefault(); - } -} +EntityPass::EntityPass() = default; EntityPass::~EntityPass() = default; +void EntityPass::SetDelegate(std::unique_ptr delegate) { + if (!delegate) { + return; + } + delegate_ = std::move(delegate); +} + void EntityPass::AddEntity(Entity entity) { entities_.emplace_back(std::move(entity)); } @@ -117,7 +119,7 @@ bool EntityPass::Render(ContentRenderer& renderer, } auto offscreen_texture_contents = - delegate_->CreateContentsForSubpassTarget(*subpass_texture); + delegate_->CreateContentsForSubpassTarget(subpass_texture); if (!offscreen_texture_contents) { // This is an error because the subpass delegate said the pass couldn't be diff --git a/impeller/entity/entity_pass.h b/impeller/entity/entity_pass.h index 5c1b1315e5e79..47cf3b2a0f427 100644 --- a/impeller/entity/entity_pass.h +++ b/impeller/entity/entity_pass.h @@ -23,10 +23,12 @@ class EntityPass { using Entities = std::vector; using Subpasses = std::vector>; - EntityPass(std::unique_ptr delegate = nullptr); + EntityPass(); ~EntityPass(); + void SetDelegate(std::unique_ptr delgate); + size_t GetSubpassesDepth() const; std::unique_ptr Clone() const; @@ -61,7 +63,8 @@ class EntityPass { EntityPass* superpass_ = nullptr; Matrix xformation_; size_t stencil_depth_ = 0u; - std::unique_ptr delegate_; + std::unique_ptr delegate_ = + EntityPassDelegate::MakeDefault(); FML_DISALLOW_COPY_AND_ASSIGN(EntityPass); }; diff --git a/impeller/entity/entity_pass_delegate.cc b/impeller/entity/entity_pass_delegate.cc index a1567f451d1de..daf328328522b 100644 --- a/impeller/entity/entity_pass_delegate.cc +++ b/impeller/entity/entity_pass_delegate.cc @@ -19,7 +19,7 @@ class DefaultEntityPassDelegate final : public EntityPassDelegate { bool CanCollapseIntoParentPass() override { return true; } std::shared_ptr CreateContentsForSubpassTarget( - const Texture& target) override { + std::shared_ptr target) override { // Not possible since this pass always collapses into its parent. FML_UNREACHABLE(); } diff --git a/impeller/entity/entity_pass_delegate.h b/impeller/entity/entity_pass_delegate.h index 03a8d05853eba..456b79dc4ec01 100644 --- a/impeller/entity/entity_pass_delegate.h +++ b/impeller/entity/entity_pass_delegate.h @@ -23,7 +23,7 @@ class EntityPassDelegate { virtual bool CanCollapseIntoParentPass() = 0; virtual std::shared_ptr CreateContentsForSubpassTarget( - const Texture& target) = 0; + std::shared_ptr target) = 0; private: FML_DISALLOW_COPY_AND_ASSIGN(EntityPassDelegate); diff --git a/impeller/entity/shaders/texture_fill.frag b/impeller/entity/shaders/texture_fill.frag index 9c4dd1ec58b30..4c7810480d3ce 100644 --- a/impeller/entity/shaders/texture_fill.frag +++ b/impeller/entity/shaders/texture_fill.frag @@ -5,10 +5,12 @@ uniform sampler2D texture_sampler; in vec2 v_texture_coords; +in float v_alpha; out vec4 frag_color; void main() { vec4 sampled = texture(texture_sampler, v_texture_coords); + sampled.w *= v_alpha; frag_color = sampled; } diff --git a/impeller/entity/shaders/texture_fill.vert b/impeller/entity/shaders/texture_fill.vert index daa30f5650a3f..303936eec685e 100644 --- a/impeller/entity/shaders/texture_fill.vert +++ b/impeller/entity/shaders/texture_fill.vert @@ -4,14 +4,17 @@ uniform FrameInfo { mat4 mvp; + float alpha; } frame_info; in vec2 vertices; in vec2 texture_coords; out vec2 v_texture_coords; +out float v_alpha; void main() { gl_Position = frame_info.mvp * vec4(vertices, 0.0, 1.0); v_texture_coords = texture_coords; + v_alpha = frame_info.alpha; } diff --git a/impeller/geometry/color.h b/impeller/geometry/color.h index 30706da4c678a..86d40f6abb61d 100644 --- a/impeller/geometry/color.h +++ b/impeller/geometry/color.h @@ -644,6 +644,8 @@ struct Color { } constexpr bool IsTransparent() const { return alpha == 0.0; } + + constexpr bool IsOpaque() const { return alpha == 1.0; } }; /** From 5e74c560c16a81c75daefab26a5c3cc9bfc3880f Mon Sep 17 00:00:00 2001 From: Chinmay Garde Date: Sun, 28 Nov 2021 14:33:41 -0800 Subject: [PATCH 219/433] Allow entity passes to be elided based on their contents. --- impeller/aiks/paint_pass_delegate.cc | 11 ++++++----- impeller/aiks/paint_pass_delegate.h | 3 +++ impeller/entity/entity_pass.cc | 4 ++++ impeller/entity/entity_pass_delegate.cc | 2 ++ impeller/entity/entity_pass_delegate.h | 2 ++ 5 files changed, 17 insertions(+), 5 deletions(-) diff --git a/impeller/aiks/paint_pass_delegate.cc b/impeller/aiks/paint_pass_delegate.cc index 5a683cfe6655d..24ca9d676d46d 100644 --- a/impeller/aiks/paint_pass_delegate.cc +++ b/impeller/aiks/paint_pass_delegate.cc @@ -14,12 +14,13 @@ PaintPassDelegate::PaintPassDelegate(Paint paint) : paint_(std::move(paint)) {} PaintPassDelegate::~PaintPassDelegate() = default; // |EntityPassDelgate| -bool PaintPassDelegate::CanCollapseIntoParentPass() { - if (paint_.color.IsOpaque()) { - return true; - } +bool PaintPassDelegate::CanElide() { + return paint_.color.IsTransparent(); +} - return false; +// |EntityPassDelgate| +bool PaintPassDelegate::CanCollapseIntoParentPass() { + return paint_.color.IsOpaque(); } // |EntityPassDelgate| diff --git a/impeller/aiks/paint_pass_delegate.h b/impeller/aiks/paint_pass_delegate.h index b731503971d7a..18261727d83e5 100644 --- a/impeller/aiks/paint_pass_delegate.h +++ b/impeller/aiks/paint_pass_delegate.h @@ -17,6 +17,9 @@ class PaintPassDelegate final : public EntityPassDelegate { // |EntityPassDelgate| ~PaintPassDelegate() override; + // |EntityPassDelgate| + bool CanElide() override; + // |EntityPassDelgate| bool CanCollapseIntoParentPass() override; diff --git a/impeller/entity/entity_pass.cc b/impeller/entity/entity_pass.cc index 40866649e38bb..33726523f1415 100644 --- a/impeller/entity/entity_pass.cc +++ b/impeller/entity/entity_pass.cc @@ -91,6 +91,10 @@ bool EntityPass::Render(ContentRenderer& renderer, } } for (const auto& subpass : subpasses_) { + if (delegate_->CanElide()) { + continue; + } + if (delegate_->CanCollapseIntoParentPass()) { // Directly render into the parent pass and move on. if (!subpass->Render(renderer, parent_pass)) { diff --git a/impeller/entity/entity_pass_delegate.cc b/impeller/entity/entity_pass_delegate.cc index daf328328522b..766a4e210ddd5 100644 --- a/impeller/entity/entity_pass_delegate.cc +++ b/impeller/entity/entity_pass_delegate.cc @@ -16,6 +16,8 @@ class DefaultEntityPassDelegate final : public EntityPassDelegate { ~DefaultEntityPassDelegate() override = default; + bool CanElide() override { return false; } + bool CanCollapseIntoParentPass() override { return true; } std::shared_ptr CreateContentsForSubpassTarget( diff --git a/impeller/entity/entity_pass_delegate.h b/impeller/entity/entity_pass_delegate.h index 456b79dc4ec01..f25de16122112 100644 --- a/impeller/entity/entity_pass_delegate.h +++ b/impeller/entity/entity_pass_delegate.h @@ -20,6 +20,8 @@ class EntityPassDelegate { virtual ~EntityPassDelegate(); + virtual bool CanElide() = 0; + virtual bool CanCollapseIntoParentPass() = 0; virtual std::shared_ptr CreateContentsForSubpassTarget( From ff259c5da44a8b2913f82610573f5ec373e91023 Mon Sep 17 00:00:00 2001 From: Chinmay Garde Date: Tue, 30 Nov 2021 14:23:48 -0800 Subject: [PATCH 220/433] Turn on full-screen 4xMSAA by default and additional render target validations. --- impeller/aiks/aiks_unittests.cc | 11 +++ impeller/aiks/canvas.cc | 13 ++- impeller/aiks/canvas.h | 2 + impeller/base/BUILD.gn | 2 + impeller/base/base.h | 1 + impeller/base/validation.cc | 28 ++++++ impeller/base/validation.h | 31 +++++++ impeller/playground/playground.mm | 37 ++++++-- .../renderer/backend/metal/allocator_mtl.mm | 6 ++ impeller/renderer/backend/metal/formats_mtl.h | 14 +++ .../renderer/backend/metal/formats_mtl.mm | 5 ++ .../backend/metal/pipeline_library_mtl.mm | 2 + .../renderer/backend/metal/render_pass_mtl.mm | 53 ++++++++--- impeller/renderer/formats.h | 24 ++++- impeller/renderer/render_pass.h | 4 +- impeller/renderer/render_target.cc | 88 ++++++++++++++++++- impeller/renderer/render_target.h | 6 ++ impeller/renderer/texture_descriptor.h | 13 ++- 18 files changed, 310 insertions(+), 30 deletions(-) create mode 100644 impeller/base/validation.cc create mode 100644 impeller/base/validation.h diff --git a/impeller/aiks/aiks_unittests.cc b/impeller/aiks/aiks_unittests.cc index ecc3b4fa0ec39..14d9b8d5a7fc1 100644 --- a/impeller/aiks/aiks_unittests.cc +++ b/impeller/aiks/aiks_unittests.cc @@ -127,6 +127,17 @@ TEST_F(AiksTest, CanRenderGroupOpacity) { canvas.Restore(); + // ASSERT_TRUE(OpenPlaygroundHere(canvas.EndRecordingAsPicture())); +} + +TEST_F(AiksTest, CanPerformFullScreenMSAA) { + Canvas canvas; + + Paint red; + red.color = Color::Red(); + + canvas.DrawCircle({250, 250}, 125, red); + ASSERT_TRUE(OpenPlaygroundHere(canvas.EndRecordingAsPicture())); } diff --git a/impeller/aiks/canvas.cc b/impeller/aiks/canvas.cc index 1c11d886ba172..c4fab50f5f979 100644 --- a/impeller/aiks/canvas.cc +++ b/impeller/aiks/canvas.cc @@ -91,6 +91,15 @@ void Canvas::DrawPath(Path path, Paint paint) { GetCurrentPass().AddEntity(std::move(entity)); } +void Canvas::DrawRect(Rect rect, Paint paint) { + DrawPath(PathBuilder{}.AddRect(rect).CreatePath(), std::move(paint)); +} + +void Canvas::DrawCircle(Point center, Scalar radius, Paint paint) { + DrawPath(PathBuilder{}.AddCircle(center, radius).CreatePath(), + std::move(paint)); +} + void Canvas::SaveLayer(Paint paint, std::optional bounds) { GetCurrentPass().SetDelegate( std::make_unique(std::move(paint))); @@ -189,10 +198,6 @@ size_t Canvas::GetStencilDepth() const { return xformation_stack_.back().stencil_depth; } -void Canvas::DrawRect(Rect rect, Paint paint) { - DrawPath(PathBuilder{}.AddRect(rect).CreatePath(), std::move(paint)); -} - void Canvas::Save(bool create_subpass) { auto entry = CanvasStackEntry{}; if (create_subpass) { diff --git a/impeller/aiks/canvas.h b/impeller/aiks/canvas.h index c7b16fcdaf6b8..6fa9de69b1054 100644 --- a/impeller/aiks/canvas.h +++ b/impeller/aiks/canvas.h @@ -53,6 +53,8 @@ class Canvas { void DrawRect(Rect rect, Paint paint); + void DrawCircle(Point center, Scalar radius, Paint paint); + void DrawImage(std::shared_ptr image, Point offset, Paint paint); void DrawImageRect(std::shared_ptr image, diff --git a/impeller/base/BUILD.gn b/impeller/base/BUILD.gn index 3a510cac0d85d..498ce4bef1a89 100644 --- a/impeller/base/BUILD.gn +++ b/impeller/base/BUILD.gn @@ -12,6 +12,8 @@ impeller_component("base") { "config.h", "strings.cc", "strings.h", + "validation.cc", + "validation.h", ] } diff --git a/impeller/base/base.h b/impeller/base/base.h index dc6e687dcfb70..c9a222db8015f 100644 --- a/impeller/base/base.h +++ b/impeller/base/base.h @@ -5,3 +5,4 @@ #pragma once #include "impeller/base/strings.h" +#include "impeller/base/validation.h" diff --git a/impeller/base/validation.cc b/impeller/base/validation.cc new file mode 100644 index 0000000000000..d0b113aee1244 --- /dev/null +++ b/impeller/base/validation.cc @@ -0,0 +1,28 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "flutter/impeller/base/validation.h" + +#include "flutter/fml/logging.h" + +namespace impeller { + +ValidationLog::ValidationLog() = default; + +ValidationLog::~ValidationLog() { + FML_LOG(ERROR) << stream_.str(); + ImpellerValidationBreak(); +} + +std::ostream& ValidationLog::GetStream() { + return stream_; +} + +void ImpellerValidationBreak() { + // Nothing to do. Exists for the debugger. + FML_LOG(ERROR) << "Break on " << __FUNCTION__ + << " to inspect point of failure."; +} + +} // namespace impeller diff --git a/impeller/base/validation.h b/impeller/base/validation.h new file mode 100644 index 0000000000000..f19471e1eea37 --- /dev/null +++ b/impeller/base/validation.h @@ -0,0 +1,31 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#pragma once + +#include + +#include "flutter/fml/macros.h" + +namespace impeller { + +class ValidationLog { + public: + ValidationLog(); + + ~ValidationLog(); + + std::ostream& GetStream(); + + private: + std::ostringstream stream_; + + FML_DISALLOW_COPY_ASSIGN_AND_MOVE(ValidationLog); +}; + +void ImpellerValidationBreak(); + +} // namespace impeller + +#define VALIDATION_LOG ::impeller::ValidationLog{}.GetStream() diff --git a/impeller/playground/playground.mm b/impeller/playground/playground.mm index 848d28fb47b61..afff6bbdb715f 100644 --- a/impeller/playground/playground.mm +++ b/impeller/playground/playground.mm @@ -144,23 +144,44 @@ static void PlaygroundKeyCallback(GLFWwindow* window, return false; } - TextureDescriptor color0_tex; - color0_tex.format = PixelFormat::kB8G8R8A8UNormInt; - color0_tex.size = { + TextureDescriptor msaa_tex_desc; + msaa_tex_desc.type = TextureType::k2DMultisample; + msaa_tex_desc.sample_count = SampleCount::kCount4; + msaa_tex_desc.format = PixelFormat::kB8G8R8A8UNormInt; + msaa_tex_desc.size = { static_cast(current_drawable.texture.width), static_cast(current_drawable.texture.height)}; - color0_tex.usage = static_cast(TextureUsage::kRenderTarget); + msaa_tex_desc.usage = static_cast(TextureUsage::kRenderTarget); + + auto msaa_tex = + renderer_.GetContext()->GetPermanentsAllocator()->CreateTexture( + StorageMode::kDeviceTransient, msaa_tex_desc); + if (!msaa_tex) { + FML_LOG(ERROR) << "Could not allocate MSAA resolve texture."; + return false; + } + + msaa_tex->SetLabel("PlaygroundMainColor4xMSAA"); + + TextureDescriptor onscreen_tex_desc; + onscreen_tex_desc.format = PixelFormat::kB8G8R8A8UNormInt; + onscreen_tex_desc.size = msaa_tex_desc.size; + onscreen_tex_desc.usage = + static_cast(TextureUsage::kRenderTarget); ColorAttachment color0; - color0.texture = - std::make_shared(color0_tex, current_drawable.texture); + color0.texture = msaa_tex; color0.clear_color = Color::DarkSlateGray(); color0.load_action = LoadAction::kClear; - color0.store_action = StoreAction::kStore; + color0.store_action = StoreAction::kMultisampleResolve; + color0.resolve_texture = std::make_shared( + onscreen_tex_desc, current_drawable.texture); TextureDescriptor stencil0_tex; + stencil0_tex.type = TextureType::k2DMultisample; + stencil0_tex.sample_count = SampleCount::kCount4; stencil0_tex.format = PixelFormat::kD32FloatS8UNormInt; - stencil0_tex.size = color0_tex.size; + stencil0_tex.size = msaa_tex_desc.size; stencil0_tex.usage = static_cast(TextureUsage::kRenderTarget); auto stencil_texture = diff --git a/impeller/renderer/backend/metal/allocator_mtl.mm b/impeller/renderer/backend/metal/allocator_mtl.mm index b331fdcfe3103..a9b1c6acebcb0 100644 --- a/impeller/renderer/backend/metal/allocator_mtl.mm +++ b/impeller/renderer/backend/metal/allocator_mtl.mm @@ -111,6 +111,12 @@ static MTLStorageMode ToMTLStorageMode(StorageMode mode) { } auto mtl_texture_desc = ToMTLTextureDescriptor(desc); + + if (!mtl_texture_desc) { + FML_DLOG(ERROR) << "Texture descriptor was invalid."; + return nullptr; + } + mtl_texture_desc.storageMode = ToMTLStorageMode(mode); auto texture = [device_ newTextureWithDescriptor:mtl_texture_desc]; if (!texture) { diff --git a/impeller/renderer/backend/metal/formats_mtl.h b/impeller/renderer/backend/metal/formats_mtl.h index 5788312eb57d6..9ebf190477561 100644 --- a/impeller/renderer/backend/metal/formats_mtl.h +++ b/impeller/renderer/backend/metal/formats_mtl.h @@ -206,6 +206,8 @@ constexpr MTLStoreAction ToMTLStoreAction(StoreAction action) { return MTLStoreActionDontCare; case StoreAction::kStore: return MTLStoreActionStore; + case StoreAction::kMultisampleResolve: + return MTLStoreActionMultisampleResolve; } return MTLStoreActionDontCare; } @@ -216,6 +218,8 @@ constexpr StoreAction FromMTLStoreAction(MTLStoreAction action) { return StoreAction::kDontCare; case MTLStoreActionStore: return StoreAction::kStore; + case MTLStoreActionMultisampleResolve: + return StoreAction::kMultisampleResolve; default: break; } @@ -249,6 +253,16 @@ constexpr MTLClearColor ToMTLClearColor(const Color& color) { return MTLClearColorMake(color.red, color.green, color.blue, color.alpha); } +constexpr MTLTextureType ToMTLTextureType(TextureType type) { + switch (type) { + case TextureType::k2D: + return MTLTextureType2D; + case TextureType::k2DMultisample: + return MTLTextureType2DMultisample; + } + return MTLTextureType2D; +} + MTLRenderPipelineColorAttachmentDescriptor* ToMTLRenderPipelineColorAttachmentDescriptor( ColorAttachmentDescriptor descriptor); diff --git a/impeller/renderer/backend/metal/formats_mtl.mm b/impeller/renderer/backend/metal/formats_mtl.mm index e32121b2c66b2..3d88d59c5251e 100644 --- a/impeller/renderer/backend/metal/formats_mtl.mm +++ b/impeller/renderer/backend/metal/formats_mtl.mm @@ -78,8 +78,13 @@ } MTLTextureDescriptor* ToMTLTextureDescriptor(const TextureDescriptor& desc) { + if (!desc.IsValid()) { + return nil; + } auto mtl_desc = [[MTLTextureDescriptor alloc] init]; + mtl_desc.textureType = ToMTLTextureType(desc.type); mtl_desc.pixelFormat = ToMTLPixelFormat(desc.format); + mtl_desc.sampleCount = static_cast(desc.sample_count); mtl_desc.width = desc.size.width; mtl_desc.height = desc.size.height; mtl_desc.mipmapLevelCount = desc.mip_count; diff --git a/impeller/renderer/backend/metal/pipeline_library_mtl.mm b/impeller/renderer/backend/metal/pipeline_library_mtl.mm index 05437b8eb8a0e..9dcd5c5349447 100644 --- a/impeller/renderer/backend/metal/pipeline_library_mtl.mm +++ b/impeller/renderer/backend/metal/pipeline_library_mtl.mm @@ -53,6 +53,8 @@ descriptor.stencilAttachmentPixelFormat = ToMTLPixelFormat(desc.GetStencilPixelFormat()); + descriptor.sampleCount = 4u; + return descriptor; } diff --git a/impeller/renderer/backend/metal/render_pass_mtl.mm b/impeller/renderer/backend/metal/render_pass_mtl.mm index 12499878396a8..972820c2eb286 100644 --- a/impeller/renderer/backend/metal/render_pass_mtl.mm +++ b/impeller/renderer/backend/metal/render_pass_mtl.mm @@ -17,6 +17,33 @@ namespace impeller { +static bool ConfigureResolveTextureAttachment( + const Attachment& desc, + MTLRenderPassAttachmentDescriptor* attachment) { + if (desc.store_action == StoreAction::kMultisampleResolve && + !desc.resolve_texture) { + VALIDATION_LOG << "Resolve store action specified on attachment but no " + "resolve texture was specified."; + return false; + } + + if (desc.resolve_texture && + desc.store_action != StoreAction::kMultisampleResolve) { + VALIDATION_LOG << "Resolve store action specified but there was no " + "resolve attachment."; + return false; + } + + if (!desc.resolve_texture) { + return true; + } + + attachment.resolveTexture = + TextureMTL::Cast(*desc.resolve_texture).GetMTLTexture(); + + return true; +} + static bool ConfigureAttachment(const Attachment& desc, MTLRenderPassAttachmentDescriptor* attachment) { if (!desc.texture) { @@ -26,6 +53,11 @@ static bool ConfigureAttachment(const Attachment& desc, attachment.texture = TextureMTL::Cast(*desc.texture).GetMTLTexture(); attachment.loadAction = ToMTLLoadAction(desc.load_action); attachment.storeAction = ToMTLStoreAction(desc.store_action); + + if (!ConfigureResolveTextureAttachment(desc, attachment)) { + return false; + } + return true; } @@ -69,8 +101,8 @@ static bool ConfigureStencilAttachment( for (const auto& color : colors) { if (!ConfigureColorAttachment(color.second, result.colorAttachments[color.first])) { - FML_DLOG(ERROR) << "Could not configure color attachment at index " - << color.first; + VALIDATION_LOG << "Could not configure color attachment at index " + << color.first; return nil; } } @@ -79,7 +111,7 @@ static bool ConfigureStencilAttachment( if (depth.has_value() && !ConfigureDepthAttachment(depth.value(), result.depthAttachment)) { - FML_DLOG(ERROR) << "Could not configure depth attachment."; + VALIDATION_LOG << "Could not configure depth attachment."; return nil; } @@ -87,7 +119,7 @@ static bool ConfigureStencilAttachment( if (stencil.has_value() && !ConfigureStencilAttachment(stencil.value(), result.stencilAttachment)) { - FML_DLOG(ERROR) << "Could not configure stencil attachment."; + VALIDATION_LOG << "Could not configure stencil attachment."; return nil; } @@ -99,7 +131,7 @@ static bool ConfigureStencilAttachment( buffer_(buffer), desc_(ToMTLRenderPassDescriptor(GetRenderTarget())), transients_buffer_(HostBuffer::Create()) { - if (!buffer_ || !desc_) { + if (!buffer_ || !desc_ || !render_target_.IsValid()) { return; } is_valid_ = true; @@ -202,8 +234,7 @@ bool SetBuffer(ShaderStage stage, [encoder_ setFragmentBufferOffset:offset atIndex:index]; return true; default: - FML_DCHECK(false) - << "Cannot update buffer offset of an unknown stage."; + VALIDATION_LOG << "Cannot update buffer offset of an unknown stage."; return false; } return true; @@ -217,7 +248,7 @@ bool SetBuffer(ShaderStage stage, [encoder_ setFragmentBuffer:buffer offset:offset atIndex:index]; return true; default: - FML_DCHECK(false) << "Cannot bind buffer to unknown shader stage."; + VALIDATION_LOG << "Cannot bind buffer to unknown shader stage."; return false; } return false; @@ -239,7 +270,7 @@ bool SetTexture(ShaderStage stage, uint64_t index, id texture) { [encoder_ setFragmentTexture:texture atIndex:index]; return true; default: - FML_DCHECK(false) << "Cannot bind buffer to unknown shader stage."; + VALIDATION_LOG << "Cannot bind buffer to unknown shader stage."; return false; } return false; @@ -263,7 +294,7 @@ bool SetSampler(ShaderStage stage, [encoder_ setFragmentSamplerState:sampler atIndex:index]; return true; default: - FML_DCHECK(false) << "Cannot bind buffer to unknown shader stage."; + VALIDATION_LOG << "Cannot bind buffer to unknown shader stage."; return false; } return false; @@ -360,7 +391,7 @@ static bool Bind(PassBindingsCache& pass, fml::closure pop_debug_marker = [encoder]() { [encoder popDebugGroup]; }; for (const auto& command : commands_) { if (command.index_count == 0u) { - FML_DLOG(ERROR) << "Zero index count in render pass command."; + VALIDATION_LOG << "Zero index count in render pass command."; continue; } diff --git a/impeller/renderer/formats.h b/impeller/renderer/formats.h index 10608fa8bea1a..72c49a1a71bf1 100644 --- a/impeller/renderer/formats.h +++ b/impeller/renderer/formats.h @@ -89,6 +89,27 @@ enum class LoadAction { enum class StoreAction { kDontCare, kStore, + kMultisampleResolve, +}; + +enum class TextureType { + k2D, + k2DMultisample, +}; + +constexpr bool IsMultisampleCapable(TextureType type) { + switch (type) { + case TextureType::k2D: + return false; + case TextureType::k2DMultisample: + return true; + } + return false; +} + +enum class SampleCount { + kCount1 = 1, + kCount4 = 4, }; using TextureUsageMask = uint64_t; @@ -320,7 +341,8 @@ struct StencilAttachmentDescriptor { }; struct Attachment { - std::shared_ptr texture = nullptr; + std::shared_ptr texture; + std::shared_ptr resolve_texture; LoadAction load_action = LoadAction::kDontCare; StoreAction store_action = StoreAction::kStore; diff --git a/impeller/renderer/render_pass.h b/impeller/renderer/render_pass.h index 588250a631269..05e3b62eca2cc 100644 --- a/impeller/renderer/render_pass.h +++ b/impeller/renderer/render_pass.h @@ -59,11 +59,11 @@ class RenderPass { virtual bool EncodeCommands(Allocator& transients_allocator) const = 0; protected: + const RenderTarget render_target_; + RenderPass(RenderTarget target); private: - const RenderTarget render_target_; - FML_DISALLOW_COPY_AND_ASSIGN(RenderPass); }; diff --git a/impeller/renderer/render_target.cc b/impeller/renderer/render_target.cc index 922f81a71ec82..666ddece17c03 100644 --- a/impeller/renderer/render_target.cc +++ b/impeller/renderer/render_target.cc @@ -4,7 +4,7 @@ #include "impeller/renderer/render_target.h" -#include "impeller/base/strings.h" +#include "impeller/base/base.h" #include "impeller/renderer/allocator.h" #include "impeller/renderer/context.h" #include "impeller/renderer/texture.h" @@ -15,6 +15,92 @@ RenderTarget::RenderTarget() = default; RenderTarget::~RenderTarget() = default; +bool RenderTarget::IsValid() const { + // Validate that there is a color attachment at zero index. + if (!HasColorAttachment(0u)) { + VALIDATION_LOG + << "Render target does not have color attachment at index 0."; + return false; + } + + // Validate that all attachments are of the same size. + { + std::optional size; + bool sizes_are_same = true; + auto iterator = [&](const Attachment& attachment) -> bool { + if (!size.has_value()) { + size = attachment.texture->GetSize(); + } + if (size != attachment.texture->GetSize()) { + sizes_are_same = false; + return false; + } + return true; + }; + IterateAllAttachments(iterator); + if (!sizes_are_same) { + VALIDATION_LOG + << "Sizes of all render target attachments are not the same."; + return false; + } + } + + // Validate that all attachments are of the same type and sample counts. + { + std::optional texture_type; + std::optional sample_count; + bool passes_type_validation = true; + auto iterator = [&](const Attachment& attachment) -> bool { + if (!texture_type.has_value() || !sample_count.has_value()) { + texture_type = attachment.texture->GetTextureDescriptor().type; + sample_count = attachment.texture->GetTextureDescriptor().sample_count; + } + + if (texture_type != attachment.texture->GetTextureDescriptor().type) { + passes_type_validation = false; + return false; + } + + if (sample_count != + attachment.texture->GetTextureDescriptor().sample_count) { + passes_type_validation = false; + return false; + } + + return true; + }; + IterateAllAttachments(iterator); + if (!passes_type_validation) { + VALIDATION_LOG << "Render target texture types are not of the same type " + "and sample count."; + return false; + } + } + + return true; +} + +void RenderTarget::IterateAllAttachments( + std::function iterator) const { + for (const auto& color : colors_) { + if (!iterator(color.second)) { + return; + } + } + + if (depth_.has_value()) { + if (!iterator(depth_.value())) { + return; + } + } + + if (stencil_.has_value()) { + if (!iterator(stencil_.value())) { + return; + } + } +} + bool RenderTarget::HasColorAttachment(size_t index) const { if (auto found = colors_.find(index); found != colors_.end()) { return true; diff --git a/impeller/renderer/render_target.h b/impeller/renderer/render_target.h index 9b00c82fbd75c..3ce7a5a480e4a 100644 --- a/impeller/renderer/render_target.h +++ b/impeller/renderer/render_target.h @@ -4,6 +4,7 @@ #pragma once +#include #include #include @@ -25,6 +26,8 @@ class RenderTarget { ~RenderTarget(); + bool IsValid() const; + bool HasColorAttachment(size_t index) const; ISize GetRenderTargetSize() const; @@ -49,6 +52,9 @@ class RenderTarget { std::map colors_; std::optional depth_; std::optional stencil_; + + void IterateAllAttachments( + std::function iterator) const; }; } // namespace impeller diff --git a/impeller/renderer/texture_descriptor.h b/impeller/renderer/texture_descriptor.h index dfaaab36cc7c5..d453ae28508bd 100644 --- a/impeller/renderer/texture_descriptor.h +++ b/impeller/renderer/texture_descriptor.h @@ -13,11 +13,13 @@ namespace impeller { struct TextureDescriptor { + TextureType type = TextureType::k2D; PixelFormat format = PixelFormat::kUnknown; ISize size; size_t mip_count = 1u; // Size::MipCount is usually appropriate. TextureUsageMask usage = static_cast(TextureUsage::kShaderRead); + SampleCount sample_count = SampleCount::kCount1; constexpr size_t GetSizeOfBaseMipLevel() const { if (!IsValid()) { @@ -33,11 +35,16 @@ struct TextureDescriptor { return size.width * BytesPerPixelForPixelFormat(format); } - bool IsValid() const { + constexpr bool SamplingOptionsAreValid() const { + const auto count = static_cast(sample_count); + return IsMultisampleCapable(type) ? count > 1 : count == 1; + } + + constexpr bool IsValid() const { return format != PixelFormat::kUnknown && // size.IsPositive() && // - mip_count >= 1u // - ; + mip_count >= 1u && // + SamplingOptionsAreValid(); } }; From e6a67e4013a1d803ecd75358d3cf173fcbf586bc Mon Sep 17 00:00:00 2001 From: Chinmay Garde Date: Wed, 1 Dec 2021 10:21:38 -0800 Subject: [PATCH 221/433] Add render pass and pipeline sample count validation. --- .../renderer/backend/metal/pipeline_library_mtl.mm | 5 +---- impeller/renderer/backend/metal/render_pass_mtl.mm | 11 +++++++++++ impeller/renderer/pipeline_builder.h | 6 +++--- impeller/renderer/pipeline_descriptor.cc | 2 +- impeller/renderer/pipeline_descriptor.h | 6 +++--- impeller/renderer/render_target.cc | 7 +++++++ impeller/renderer/render_target.h | 6 ++++++ 7 files changed, 32 insertions(+), 11 deletions(-) diff --git a/impeller/renderer/backend/metal/pipeline_library_mtl.mm b/impeller/renderer/backend/metal/pipeline_library_mtl.mm index 9dcd5c5349447..ce0fadebbb69b 100644 --- a/impeller/renderer/backend/metal/pipeline_library_mtl.mm +++ b/impeller/renderer/backend/metal/pipeline_library_mtl.mm @@ -16,12 +16,11 @@ PipelineLibraryMTL::~PipelineLibraryMTL() = default; -// TODO(csg): Make PipelineDescriptor a struct and move this to formats_mtl. static MTLRenderPipelineDescriptor* GetMTLRenderPipelineDescriptor( const PipelineDescriptor& desc) { auto descriptor = [[MTLRenderPipelineDescriptor alloc] init]; descriptor.label = @(desc.GetLabel().c_str()); - descriptor.sampleCount = desc.GetSampleCount(); + descriptor.sampleCount = static_cast(desc.GetSampleCount()); for (const auto& entry : desc.GetStageEntrypoints()) { if (entry.first == ShaderStage::kVertex) { @@ -53,8 +52,6 @@ descriptor.stencilAttachmentPixelFormat = ToMTLPixelFormat(desc.GetStencilPixelFormat()); - descriptor.sampleCount = 4u; - return descriptor; } diff --git a/impeller/renderer/backend/metal/render_pass_mtl.mm b/impeller/renderer/backend/metal/render_pass_mtl.mm index 972820c2eb286..cba003d628776 100644 --- a/impeller/renderer/backend/metal/render_pass_mtl.mm +++ b/impeller/renderer/backend/metal/render_pass_mtl.mm @@ -388,6 +388,8 @@ static bool Bind(PassBindingsCache& pass, return true; }; + const auto target_sample_count = render_target_.GetSampleCount(); + fml::closure pop_debug_marker = [encoder]() { [encoder popDebugGroup]; }; for (const auto& command : commands_) { if (command.index_count == 0u) { @@ -401,6 +403,14 @@ static bool Bind(PassBindingsCache& pass, } else { auto_pop_debug_marker.Release(); } + + if (target_sample_count != + command.pipeline->GetDescriptor().GetSampleCount()) { + VALIDATION_LOG << "Pipeline for command and the render target disagree " + "on sample counts."; + return false; + } + pass_bindings.SetRenderPipelineState( PipelineMTL::Cast(*command.pipeline).GetMTLRenderPipelineState()); pass_bindings.SetDepthStencilState( @@ -447,6 +457,7 @@ static bool Bind(PassBindingsCache& pass, bool RenderPassMTL::AddCommand(Command command) { if (!command) { + VALIDATION_LOG << "Attempted to add an invalid command to the render pass."; return false; } diff --git a/impeller/renderer/pipeline_builder.h b/impeller/renderer/pipeline_builder.h index 84712c4705eca..82e6a742d33a2 100644 --- a/impeller/renderer/pipeline_builder.h +++ b/impeller/renderer/pipeline_builder.h @@ -69,10 +69,10 @@ struct PipelineBuilder { FragmentShader::kEntrypointName, ShaderStage::kFragment); if (!vertex_function || !fragment_function) { - FML_LOG(ERROR) << "Could not resolve pipeline entrypoint(s) '" + VALIDATION_LOG << "Could not resolve pipeline entrypoint(s) '" << VertexShader::kEntrypointName << "' and '" << FragmentShader::kEntrypointName - << "' for pipline named '" << VertexShader::kLabel + << "' for pipeline named '" << VertexShader::kLabel << "'."; return false; } @@ -86,7 +86,7 @@ struct PipelineBuilder { auto vertex_descriptor = std::make_shared(); if (!vertex_descriptor->SetStageInputs( VertexShader::kAllShaderStageInputs)) { - FML_LOG(ERROR) + VALIDATION_LOG << "Could not configure vertex descriptor for pipeline named '" << VertexShader::kLabel << "'."; return false; diff --git a/impeller/renderer/pipeline_descriptor.cc b/impeller/renderer/pipeline_descriptor.cc index eb24bb483ab3a..ca0a06ea2e602 100644 --- a/impeller/renderer/pipeline_descriptor.cc +++ b/impeller/renderer/pipeline_descriptor.cc @@ -61,7 +61,7 @@ PipelineDescriptor& PipelineDescriptor::SetLabel(std::string label) { return *this; } -PipelineDescriptor& PipelineDescriptor::SetSampleCount(size_t samples) { +PipelineDescriptor& PipelineDescriptor::SetSampleCount(SampleCount samples) { sample_count_ = samples; return *this; } diff --git a/impeller/renderer/pipeline_descriptor.h b/impeller/renderer/pipeline_descriptor.h index 225b0faf5f2b8..c26579b020a6f 100644 --- a/impeller/renderer/pipeline_descriptor.h +++ b/impeller/renderer/pipeline_descriptor.h @@ -32,9 +32,9 @@ class PipelineDescriptor final : public Comparable { const std::string& GetLabel() const; - PipelineDescriptor& SetSampleCount(size_t samples); + PipelineDescriptor& SetSampleCount(SampleCount samples); - size_t GetSampleCount() const { return sample_count_; } + SampleCount GetSampleCount() const { return sample_count_; } PipelineDescriptor& AddStageEntrypoint( std::shared_ptr function); @@ -97,7 +97,7 @@ class PipelineDescriptor final : public Comparable { private: std::string label_; - size_t sample_count_ = 1; + SampleCount sample_count_ = SampleCount::kCount1; std::map> entrypoints_; std::map color_attachment_descriptors_; diff --git a/impeller/renderer/render_target.cc b/impeller/renderer/render_target.cc index 666ddece17c03..0e6f8bcf20e14 100644 --- a/impeller/renderer/render_target.cc +++ b/impeller/renderer/render_target.cc @@ -101,6 +101,13 @@ void RenderTarget::IterateAllAttachments( } } +SampleCount RenderTarget::GetSampleCount() const { + if (auto found = colors_.find(0u); found != colors_.end()) { + return found->second.texture->GetTextureDescriptor().sample_count; + } + return SampleCount::kCount1; +} + bool RenderTarget::HasColorAttachment(size_t index) const { if (auto found = colors_.find(index); found != colors_.end()) { return true; diff --git a/impeller/renderer/render_target.h b/impeller/renderer/render_target.h index 3ce7a5a480e4a..4fed906696155 100644 --- a/impeller/renderer/render_target.h +++ b/impeller/renderer/render_target.h @@ -22,12 +22,18 @@ class RenderTarget { ISize size, std::string label = "Offscreen"); + static RenderTarget CreateMSAA(const Context& context, + std::shared_ptr resolve_texture, + std::string label = "Offscreen"); + RenderTarget(); ~RenderTarget(); bool IsValid() const; + SampleCount GetSampleCount() const; + bool HasColorAttachment(size_t index) const; ISize GetRenderTargetSize() const; From 6eb72ad22cff597ecfcb851903caa898851a6690 Mon Sep 17 00:00:00 2001 From: Chinmay Garde Date: Wed, 1 Dec 2021 11:01:36 -0800 Subject: [PATCH 222/433] All creation of pipeline variants from a pipeline. --- impeller/base/BUILD.gn | 2 ++ impeller/base/base.h | 1 + impeller/base/promise.cc | 11 +++++++ impeller/base/promise.h | 19 ++++++++++++ .../backend/metal/pipeline_library_mtl.mm | 29 +++++++++++------- .../renderer/backend/metal/pipeline_mtl.h | 3 +- .../renderer/backend/metal/pipeline_mtl.mm | 5 ++-- impeller/renderer/pipeline.cc | 30 +++++++++++++++---- impeller/renderer/pipeline.h | 6 +++- 9 files changed, 87 insertions(+), 19 deletions(-) create mode 100644 impeller/base/promise.cc create mode 100644 impeller/base/promise.h diff --git a/impeller/base/BUILD.gn b/impeller/base/BUILD.gn index 498ce4bef1a89..ab988b1adc31f 100644 --- a/impeller/base/BUILD.gn +++ b/impeller/base/BUILD.gn @@ -10,6 +10,8 @@ impeller_component("base") { "allocation.h", "base.h", "config.h", + "promise.cc", + "promise.h", "strings.cc", "strings.h", "validation.cc", diff --git a/impeller/base/base.h b/impeller/base/base.h index c9a222db8015f..a55a765f092fd 100644 --- a/impeller/base/base.h +++ b/impeller/base/base.h @@ -4,5 +4,6 @@ #pragma once +#include "impeller/base/promise.h" #include "impeller/base/strings.h" #include "impeller/base/validation.h" diff --git a/impeller/base/promise.cc b/impeller/base/promise.cc new file mode 100644 index 0000000000000..9c7b7606636bd --- /dev/null +++ b/impeller/base/promise.cc @@ -0,0 +1,11 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "impeller/base/promise.h" + +namespace impeller { + +// + +} // namespace impeller diff --git a/impeller/base/promise.h b/impeller/base/promise.h new file mode 100644 index 0000000000000..04b0cc962bd60 --- /dev/null +++ b/impeller/base/promise.h @@ -0,0 +1,19 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#pragma once + +#include + +namespace impeller { + +template +std::future RealizedFuture(T t) { + std::promise promise; + auto future = promise.get_future(); + promise.set_value(std::move(t)); + return future; +} + +} // namespace impeller diff --git a/impeller/renderer/backend/metal/pipeline_library_mtl.mm b/impeller/renderer/backend/metal/pipeline_library_mtl.mm index ce0fadebbb69b..2d33f463706e8 100644 --- a/impeller/renderer/backend/metal/pipeline_library_mtl.mm +++ b/impeller/renderer/backend/metal/pipeline_library_mtl.mm @@ -81,24 +81,33 @@ // created till the first instance of the creation invokes its completion // callback. - auto thiz = shared_from_this(); + auto weak_this = weak_from_this(); auto completion_handler = ^(id _Nullable render_pipeline_state, NSError* _Nullable error) { if (error != nil) { - FML_LOG(ERROR) << "Could not create render pipeline: " + VALIDATION_LOG << "Could not create render pipeline: " << error.localizedDescription.UTF8String; promise->set_value(nullptr); - } else { - auto new_pipeline = std::shared_ptr(new PipelineMTL( - descriptor, // - render_pipeline_state, // - CreateDepthStencilDescriptor(descriptor, device_) // - )); - promise->set_value(new_pipeline); - this->SavePipeline(descriptor, new_pipeline); + return; } + + auto strong_this = weak_this.lock(); + if (!strong_this) { + VALIDATION_LOG << "Library was collected before a pending pipeline " + "creation could finish."; + promise->set_value(nullptr); + return; + } + auto new_pipeline = std::shared_ptr(new PipelineMTL( + weak_this, + descriptor, // + render_pipeline_state, // + CreateDepthStencilDescriptor(descriptor, device_) // + )); + promise->set_value(new_pipeline); + this->SavePipeline(descriptor, new_pipeline); }; [device_ newRenderPipelineStateWithDescriptor:GetMTLRenderPipelineDescriptor( descriptor) diff --git a/impeller/renderer/backend/metal/pipeline_mtl.h b/impeller/renderer/backend/metal/pipeline_mtl.h index 0cb0ef7bc81f2..6756747121e89 100644 --- a/impeller/renderer/backend/metal/pipeline_mtl.h +++ b/impeller/renderer/backend/metal/pipeline_mtl.h @@ -30,7 +30,8 @@ class PipelineMTL final : public Pipeline, id depth_stencil_state_; bool is_valid_ = false; - PipelineMTL(PipelineDescriptor desc, + PipelineMTL(std::weak_ptr library, + PipelineDescriptor desc, id state, id depth_stencil_state); diff --git a/impeller/renderer/backend/metal/pipeline_mtl.mm b/impeller/renderer/backend/metal/pipeline_mtl.mm index 6af3e7bd2e46b..5e8717f00edcc 100644 --- a/impeller/renderer/backend/metal/pipeline_mtl.mm +++ b/impeller/renderer/backend/metal/pipeline_mtl.mm @@ -6,10 +6,11 @@ namespace impeller { -PipelineMTL::PipelineMTL(PipelineDescriptor desc, +PipelineMTL::PipelineMTL(std::weak_ptr library, + PipelineDescriptor desc, id state, id depth_stencil_state) - : Pipeline(std::move(desc)), + : Pipeline(std::move(library), std::move(desc)), pipeline_state_(state), depth_stencil_state_(depth_stencil_state) { if (!pipeline_state_) { diff --git a/impeller/renderer/pipeline.cc b/impeller/renderer/pipeline.cc index 9577fc0e6cf30..ee0f574a69e50 100644 --- a/impeller/renderer/pipeline.cc +++ b/impeller/renderer/pipeline.cc @@ -4,22 +4,22 @@ #include "impeller/renderer/pipeline.h" +#include "impeller/base/base.h" #include "impeller/renderer/context.h" #include "impeller/renderer/pipeline_library.h" namespace impeller { -Pipeline::Pipeline(PipelineDescriptor desc) : desc_(std::move(desc)) {} +Pipeline::Pipeline(std::weak_ptr library, + PipelineDescriptor desc) + : library_(std::move(library)), desc_(std::move(desc)) {} Pipeline::~Pipeline() = default; PipelineFuture CreatePipelineFuture(const Context& context, std::optional desc) { if (!context.IsValid()) { - std::promise> promise; - auto future = promise.get_future(); - promise.set_value(nullptr); - return future; + return RealizedFuture>(nullptr); } return context.GetPipelineLibrary()->GetRenderPipeline(std::move(desc)); @@ -29,4 +29,24 @@ const PipelineDescriptor& Pipeline::GetDescriptor() const { return desc_; } +PipelineFuture Pipeline::CreateVariant( + std::function descriptor_callback) const { + if (!descriptor_callback) { + return RealizedFuture>(nullptr); + } + + auto copied_desc = desc_; + + descriptor_callback(copied_desc); + + auto library = library_.lock(); + if (!library) { + VALIDATION_LOG << "The library from which this pipeline was created was " + "already collected."; + return RealizedFuture>(nullptr); + } + + return library->GetRenderPipeline(std::move(copied_desc)); +} + } // namespace impeller diff --git a/impeller/renderer/pipeline.h b/impeller/renderer/pipeline.h index c0601cb41f13c..2d6760cc0d61d 100644 --- a/impeller/renderer/pipeline.h +++ b/impeller/renderer/pipeline.h @@ -44,10 +44,14 @@ class Pipeline { const PipelineDescriptor& GetDescriptor() const; + PipelineFuture CreateVariant( + std::function descriptor_callback) const; + protected: - Pipeline(PipelineDescriptor desc); + Pipeline(std::weak_ptr library, PipelineDescriptor desc); private: + const std::weak_ptr library_; const PipelineDescriptor desc_; FML_DISALLOW_COPY_AND_ASSIGN(Pipeline); From e682751d339735ebcb9821946f6d83af7ed80e6a Mon Sep 17 00:00:00 2001 From: Chinmay Garde Date: Wed, 1 Dec 2021 13:03:43 -0800 Subject: [PATCH 223/433] Allow for the on-demand creation and caching of pipeline variants from the prototype. --- impeller/aiks/aiks_unittests.cc | 4 +- impeller/entity/content_renderer.cc | 53 +++-------------- impeller/entity/content_renderer.h | 90 +++++++++++++++++++++++++---- impeller/entity/contents.cc | 16 +++-- impeller/renderer/pipeline.h | 9 +++ 5 files changed, 110 insertions(+), 62 deletions(-) diff --git a/impeller/aiks/aiks_unittests.cc b/impeller/aiks/aiks_unittests.cc index 14d9b8d5a7fc1..b6ee69e3e52f8 100644 --- a/impeller/aiks/aiks_unittests.cc +++ b/impeller/aiks/aiks_unittests.cc @@ -127,7 +127,7 @@ TEST_F(AiksTest, CanRenderGroupOpacity) { canvas.Restore(); - // ASSERT_TRUE(OpenPlaygroundHere(canvas.EndRecordingAsPicture())); + ASSERT_TRUE(OpenPlaygroundHere(canvas.EndRecordingAsPicture())); } TEST_F(AiksTest, CanPerformFullScreenMSAA) { @@ -138,7 +138,7 @@ TEST_F(AiksTest, CanPerformFullScreenMSAA) { canvas.DrawCircle({250, 250}, 125, red); - ASSERT_TRUE(OpenPlaygroundHere(canvas.EndRecordingAsPicture())); + // ASSERT_TRUE(OpenPlaygroundHere(canvas.EndRecordingAsPicture())); } } // namespace testing diff --git a/impeller/entity/content_renderer.cc b/impeller/entity/content_renderer.cc index 6fba04b83b7a1..d22e81f104ab3 100644 --- a/impeller/entity/content_renderer.cc +++ b/impeller/entity/content_renderer.cc @@ -13,13 +13,15 @@ ContentRenderer::ContentRenderer(std::shared_ptr context) } // Pipelines whose default descriptors work fine for the entity framework. - gradient_fill_pipeline_ = std::make_unique(*context_); - solid_fill_pipeline_ = std::make_unique(*context_); - texture_pipeline_ = std::make_unique(*context_); - solid_stroke_pipeline_ = std::make_unique(*context_); + gradient_fill_pipelines_[{}] = + std::make_unique(*context_); + solid_fill_pipelines_[{}] = std::make_unique(*context_); + texture_pipelines_[{}] = std::make_unique(*context_); + solid_stroke_pipelines_[{}] = + std::make_unique(*context_); // Pipelines that are variants of the base pipelines with custom descriptors. - if (auto solid_fill_pipeline = solid_fill_pipeline_->WaitAndGet()) { + if (auto solid_fill_pipeline = solid_fill_pipelines_[{}]->WaitAndGet()) { auto clip_pipeline_descriptor = solid_fill_pipeline->GetDescriptor(); // Write to the stencil buffer. StencilAttachmentDescriptor stencil0; @@ -35,7 +37,7 @@ ContentRenderer::ContentRenderer(std::shared_ptr context) } clip_pipeline_descriptor.SetColorAttachmentDescriptors( std::move(color_attachments)); - clip_pipeline_ = std::make_unique( + clip_pipelines_[{}] = std::make_unique( *context_, std::move(clip_pipeline_descriptor)); } else { return; @@ -54,43 +56,4 @@ std::shared_ptr ContentRenderer::GetContext() const { return context_; } -std::shared_ptr ContentRenderer::GetGradientFillPipeline() const { - if (!IsValid()) { - return nullptr; - } - return gradient_fill_pipeline_->WaitAndGet(); -} - -std::shared_ptr ContentRenderer::GetSolidFillPipeline() const { - if (!IsValid()) { - return nullptr; - } - - return solid_fill_pipeline_->WaitAndGet(); -} - -std::shared_ptr ContentRenderer::GetTexturePipeline() const { - if (!IsValid()) { - return nullptr; - } - - return texture_pipeline_->WaitAndGet(); -} - -std::shared_ptr ContentRenderer::GetSolidStrokePipeline() const { - if (!IsValid()) { - return nullptr; - } - - return solid_stroke_pipeline_->WaitAndGet(); -} - -std::shared_ptr ContentRenderer::GetClipPipeline() const { - if (!IsValid()) { - return nullptr; - } - - return clip_pipeline_->WaitAndGet(); -} - } // namespace impeller diff --git a/impeller/entity/content_renderer.h b/impeller/entity/content_renderer.h index 1172d17f189cc..19edbb8167fd7 100644 --- a/impeller/entity/content_renderer.h +++ b/impeller/entity/content_renderer.h @@ -5,7 +5,9 @@ #pragma once #include +#include +#include "flutter/fml/hash_combine.h" #include "flutter/fml/macros.h" #include "flutter/impeller/entity/gradient_fill.frag.h" #include "flutter/impeller/entity/gradient_fill.vert.h" @@ -33,31 +35,99 @@ using ClipPipeline = PipelineT; class ContentRenderer { public: + struct Options { + SampleCount sample_count = SampleCount::kCount1; + + Options() {} + + struct Hash { + constexpr std::size_t operator()(const Options& o) const { + return fml::HashCombine(o.sample_count); + } + }; + + struct Equal { + constexpr bool operator()(const Options& lhs, const Options& rhs) const { + return lhs.sample_count == rhs.sample_count; + } + }; + }; + ContentRenderer(std::shared_ptr context); ~ContentRenderer(); bool IsValid() const; - std::shared_ptr GetGradientFillPipeline() const; + std::shared_ptr GetGradientFillPipeline(Options opts) const { + return GetPipeline(gradient_fill_pipelines_, opts); + } - std::shared_ptr GetSolidFillPipeline() const; + std::shared_ptr GetSolidFillPipeline(Options opts) const { + return GetPipeline(solid_fill_pipelines_, opts); + } - std::shared_ptr GetTexturePipeline() const; + std::shared_ptr GetTexturePipeline(Options opts) const { + return GetPipeline(texture_pipelines_, opts); + } - std::shared_ptr GetSolidStrokePipeline() const; + std::shared_ptr GetSolidStrokePipeline(Options opts) const { + return GetPipeline(solid_stroke_pipelines_, opts); + } - std::shared_ptr GetClipPipeline() const; + std::shared_ptr GetClipPipeline(Options opts) const { + return GetPipeline(clip_pipelines_, opts); + } std::shared_ptr GetContext() const; private: std::shared_ptr context_; - std::unique_ptr gradient_fill_pipeline_; - std::unique_ptr solid_fill_pipeline_; - std::unique_ptr texture_pipeline_; - std::unique_ptr solid_stroke_pipeline_; - std::unique_ptr clip_pipeline_; + + template + using Variants = std:: + unordered_map, Options::Hash, Options::Equal>; + + // These are mutable because while the prototypes are created eagerly, any + // variants requested from that are lazily created and cached in the variants + // map. + mutable Variants gradient_fill_pipelines_; + mutable Variants solid_fill_pipelines_; + mutable Variants texture_pipelines_; + mutable Variants solid_stroke_pipelines_; + mutable Variants clip_pipelines_; + + static void ApplyOptionsToDescriptor(PipelineDescriptor& desc, + const Options& options) { + desc.SetSampleCount(options.sample_count); + } + + template + std::shared_ptr GetPipeline(Variants& container, + Options opts) const { + if (!IsValid()) { + return nullptr; + } + + if (auto found = container.find(opts); found != container.end()) { + return found->second->WaitAndGet(); + } + + auto found = container.find({}); + + // The prototype must always be initialized in the constructor. + FML_CHECK(found != container.end()); + + auto variant_future = found->second->WaitAndGet()->CreateVariant( + [&opts](PipelineDescriptor& desc) { + ApplyOptionsToDescriptor(desc, opts); + }); + auto variant = std::make_unique(std::move(variant_future)); + auto variant_pipeline = variant->WaitAndGet(); + container[opts] = std::move(variant); + return variant_pipeline; + } + bool is_valid_ = false; FML_DISALLOW_COPY_AND_ASSIGN(ContentRenderer); diff --git a/impeller/entity/contents.cc b/impeller/entity/contents.cc index 48be6459c92bc..7de2f68d6efb4 100644 --- a/impeller/entity/contents.cc +++ b/impeller/entity/contents.cc @@ -19,6 +19,12 @@ namespace impeller { +static ContentRenderer::Options OptionsFromPass(const RenderPass& pass) { + ContentRenderer::Options opts; + opts.sample_count = pass.GetRenderTarget().GetSampleCount(); + return opts; +} + /******************************************************************************* ******* Contents ******************************************************************************/ @@ -85,7 +91,7 @@ bool LinearGradientContents::Render(const ContentRenderer& renderer, Command cmd; cmd.label = "LinearGradientFill"; - cmd.pipeline = renderer.GetGradientFillPipeline(); + cmd.pipeline = renderer.GetGradientFillPipeline(OptionsFromPass(pass)); cmd.stencil_reference = entity.GetStencilDepth(); cmd.BindVertices( vertices_builder.CreateVertexBuffer(pass.GetTransientsBuffer())); @@ -142,7 +148,7 @@ bool SolidColorContents::Render(const ContentRenderer& renderer, Command cmd; cmd.label = "SolidFill"; - cmd.pipeline = renderer.GetSolidFillPipeline(); + cmd.pipeline = renderer.GetSolidFillPipeline(OptionsFromPass(pass)); cmd.stencil_reference = entity.GetStencilDepth(); cmd.BindVertices( CreateSolidFillVertices(entity.GetPath(), pass.GetTransientsBuffer())); @@ -241,7 +247,7 @@ bool TextureContents::Render(const ContentRenderer& renderer, Command cmd; cmd.label = "TextureFill"; - cmd.pipeline = renderer.GetTexturePipeline(); + cmd.pipeline = renderer.GetTexturePipeline(OptionsFromPass(pass)); cmd.stencil_reference = entity.GetStencilDepth(); cmd.BindVertices(vertex_builder.CreateVertexBuffer(host_buffer)); VS::BindFrameInfo(cmd, host_buffer.EmplaceUniform(frame_info)); @@ -336,7 +342,7 @@ bool SolidStrokeContents::Render(const ContentRenderer& renderer, Command cmd; cmd.primitive_type = PrimitiveType::kTriangleStrip; cmd.label = "SolidStroke"; - cmd.pipeline = renderer.GetSolidStrokePipeline(); + cmd.pipeline = renderer.GetSolidStrokePipeline(OptionsFromPass(pass)); cmd.stencil_reference = entity.GetStencilDepth(); cmd.BindVertices( CreateSolidStrokeVertices(entity.GetPath(), pass.GetTransientsBuffer())); @@ -372,7 +378,7 @@ bool ClipContents::Render(const ContentRenderer& renderer, Command cmd; cmd.label = "Clip"; - cmd.pipeline = renderer.GetClipPipeline(); + cmd.pipeline = renderer.GetClipPipeline(OptionsFromPass(pass)); cmd.stencil_reference = entity.GetStencilDepth() + 1u; cmd.BindVertices( CreateSolidFillVertices(entity.GetPath(), pass.GetTransientsBuffer())); diff --git a/impeller/renderer/pipeline.h b/impeller/renderer/pipeline.h index 2d6760cc0d61d..69182785fb5a1 100644 --- a/impeller/renderer/pipeline.h +++ b/impeller/renderer/pipeline.h @@ -15,6 +15,12 @@ namespace impeller { class PipelineLibrary; class Pipeline; +// TODO(csg): Using a simple future is sub-optimal since callers that want to +// eagerly create and cache pipeline variants will have to await on the future +// to get its pipeline descriptor (unless they have explicitly cached it). This +// would be a concurrency pessimization. +// +// Use a struct that stores the future and the descriptor separately. using PipelineFuture = std::future>; //------------------------------------------------------------------------------ @@ -76,6 +82,9 @@ class PipelineT { std::optional desc) : pipeline_future_(CreatePipelineFuture(context, desc)) {} + explicit PipelineT(PipelineFuture future) + : pipeline_future_(std::move(future)) {} + std::shared_ptr WaitAndGet() { if (did_wait_) { return pipeline_; From 4b653ef2cf51c6e51d427c48414fa943d923dbd1 Mon Sep 17 00:00:00 2001 From: Chinmay Garde Date: Thu, 2 Dec 2021 10:53:35 -0800 Subject: [PATCH 224/433] Add more validation logs instead of FML logs. --- impeller/aiks/aiks_unittests.cc | 8 ++++---- impeller/base/allocation.cc | 3 ++- impeller/compiler/compiler_unittests.cc | 5 +++-- impeller/compiler/reflector.cc | 3 ++- impeller/entity/content_renderer.h | 2 -- impeller/image/compressed_image.cc | 6 ++++-- impeller/playground/playground.mm | 11 ++++++----- impeller/renderer/backend/metal/allocator_mtl.mm | 3 ++- impeller/renderer/backend/metal/context_mtl.mm | 4 ++-- impeller/renderer/backend/metal/device_buffer_mtl.mm | 3 ++- impeller/renderer/backend/metal/texture_mtl.mm | 5 +++-- .../renderer/backend/metal/vertex_descriptor_mtl.mm | 3 ++- impeller/renderer/pipeline.h | 4 ++-- impeller/renderer/renderer.cc | 3 ++- 14 files changed, 36 insertions(+), 27 deletions(-) diff --git a/impeller/aiks/aiks_unittests.cc b/impeller/aiks/aiks_unittests.cc index b6ee69e3e52f8..2c916a8d720db 100644 --- a/impeller/aiks/aiks_unittests.cc +++ b/impeller/aiks/aiks_unittests.cc @@ -89,11 +89,11 @@ TEST_F(AiksTest, CanRenderStrokes) { TEST_F(AiksTest, CanRenderCurvedStrokes) { Canvas canvas; Paint paint; - paint.color = Color::Blue(); + paint.color = Color::Red(); paint.stroke_width = 25.0; paint.style = Paint::Style::kStroke; canvas.DrawPath(PathBuilder{}.AddCircle({500, 500}, 250).CreatePath(), paint); - // ASSERT_TRUE(OpenPlaygroundHere(canvas.EndRecordingAsPicture())); + ASSERT_TRUE(OpenPlaygroundHere(canvas.EndRecordingAsPicture())); } TEST_F(AiksTest, CanRenderClips) { @@ -103,7 +103,7 @@ TEST_F(AiksTest, CanRenderClips) { canvas.ClipPath( PathBuilder{}.AddRect(Rect::MakeXYWH(0, 0, 500, 500)).CreatePath()); canvas.DrawPath(PathBuilder{}.AddCircle({500, 500}, 250).CreatePath(), paint); - // ASSERT_TRUE(OpenPlaygroundHere(canvas.EndRecordingAsPicture())); + ASSERT_TRUE(OpenPlaygroundHere(canvas.EndRecordingAsPicture())); } TEST_F(AiksTest, CanRenderGroupOpacity) { @@ -138,7 +138,7 @@ TEST_F(AiksTest, CanPerformFullScreenMSAA) { canvas.DrawCircle({250, 250}, 125, red); - // ASSERT_TRUE(OpenPlaygroundHere(canvas.EndRecordingAsPicture())); + ASSERT_TRUE(OpenPlaygroundHere(canvas.EndRecordingAsPicture())); } } // namespace testing diff --git a/impeller/base/allocation.cc b/impeller/base/allocation.cc index e41d0f9e6cc5b..d3c8602e104d8 100644 --- a/impeller/base/allocation.cc +++ b/impeller/base/allocation.cc @@ -7,6 +7,7 @@ #include #include "flutter/fml/logging.h" +#include "impeller/base/validation.h" namespace impeller { @@ -69,7 +70,7 @@ bool Allocation::Reserve(size_t reserved) { // If new length is zero, a minimum non-zero sized allocation is returned. // So this check will not trip and this routine will indicate success as // expected. - FML_LOG(ERROR) << "Allocation failed. Out of host memory."; + VALIDATION_LOG << "Allocation failed. Out of host memory."; return false; } diff --git a/impeller/compiler/compiler_unittests.cc b/impeller/compiler/compiler_unittests.cc index 5c97bfc45a67a..77ad7b88ce975 100644 --- a/impeller/compiler/compiler_unittests.cc +++ b/impeller/compiler/compiler_unittests.cc @@ -5,6 +5,7 @@ #include "flutter/impeller/compiler/compiler.h" #include "flutter/testing/testing.h" #include "gtest/gtest.h" +#include "impeller/base/validation.h" namespace impeller { namespace compiler { @@ -25,7 +26,7 @@ class CompilerTest : public ::testing::Test { bool CanCompileFixture(const char* fixture_name) const { auto fixture = flutter::testing::OpenFixtureAsMapping(fixture_name); if (!fixture->GetMapping()) { - FML_LOG(ERROR) << "Could not find shader in fixtures: " << fixture_name; + VALIDATION_LOG << "Could not find shader in fixtures: " << fixture_name; return false; } Compiler::SourceOptions compiler_options(fixture_name); @@ -34,7 +35,7 @@ class CompilerTest : public ::testing::Test { Reflector::Options reflector_options; Compiler compiler(*fixture.get(), compiler_options, reflector_options); if (!compiler.IsValid()) { - FML_LOG(ERROR) << "Compilation failed: " << compiler.GetErrorMessages(); + VALIDATION_LOG << "Compilation failed: " << compiler.GetErrorMessages(); return false; } return true; diff --git a/impeller/compiler/reflector.cc b/impeller/compiler/reflector.cc index 6725557579056..3266a292bf1d1 100644 --- a/impeller/compiler/reflector.cc +++ b/impeller/compiler/reflector.cc @@ -16,6 +16,7 @@ #include "flutter/impeller/geometry/matrix.h" #include "flutter/impeller/geometry/scalar.h" #include "impeller/base/strings.h" +#include "impeller/base/validation.h" namespace impeller { namespace compiler { @@ -150,7 +151,7 @@ std::optional Reflector::GenerateTemplateArguments() const { const auto& entrypoints = compiler_->get_entry_points_and_stages(); if (entrypoints.size() != 1) { - FML_LOG(ERROR) << "Incorrect number of entrypoints in the shader. Found " + VALIDATION_LOG << "Incorrect number of entrypoints in the shader. Found " << entrypoints.size() << " but expected 1."; return std::nullopt; } diff --git a/impeller/entity/content_renderer.h b/impeller/entity/content_renderer.h index 19edbb8167fd7..4e4aab0e366fd 100644 --- a/impeller/entity/content_renderer.h +++ b/impeller/entity/content_renderer.h @@ -38,8 +38,6 @@ class ContentRenderer { struct Options { SampleCount sample_count = SampleCount::kCount1; - Options() {} - struct Hash { constexpr std::size_t operator()(const Options& o) const { return fml::HashCombine(o.sample_count); diff --git a/impeller/image/compressed_image.cc b/impeller/image/compressed_image.cc index c47ed4b2ddf7c..0837be17e24d0 100644 --- a/impeller/image/compressed_image.cc +++ b/impeller/image/compressed_image.cc @@ -6,6 +6,8 @@ #include +#include "impeller/base/validation.h" + namespace impeller { CompressedImage::CompressedImage( @@ -32,7 +34,7 @@ DecompressedImage CompressedImage::Decode() const { STBI_default); if (decoded == nullptr) { - FML_LOG(ERROR) << "Could not decode image from host memory."; + VALIDATION_LOG << "Could not decode image from host memory."; return {}; } @@ -68,7 +70,7 @@ DecompressedImage CompressedImage::Decode() const { } if (components == DecompressedImage::Format::kInvalid) { - FML_LOG(ERROR) << "Could not detect image components when decoding."; + VALIDATION_LOG << "Could not detect image components when decoding."; return {}; } diff --git a/impeller/playground/playground.mm b/impeller/playground/playground.mm index afff6bbdb715f..0007e7fd26c58 100644 --- a/impeller/playground/playground.mm +++ b/impeller/playground/playground.mm @@ -6,6 +6,7 @@ #include "flutter/fml/paths.h" #include "flutter/testing/testing.h" +#include "impeller/base/validation.h" #include "impeller/image/compressed_image.h" #include "impeller/playground/playground.h" #include "impeller/renderer/allocator.h" @@ -140,7 +141,7 @@ static void PlaygroundKeyCallback(GLFWwindow* window, auto current_drawable = [layer nextDrawable]; if (!current_drawable) { - FML_LOG(ERROR) << "Could not acquire current drawable."; + VALIDATION_LOG << "Could not acquire current drawable."; return false; } @@ -207,7 +208,7 @@ static void PlaygroundKeyCallback(GLFWwindow* window, }; if (!renderer_.Render(surface, wrapped_callback)) { - FML_LOG(ERROR) << "Could not render into the surface."; + VALIDATION_LOG << "Could not render into the surface."; return false; } @@ -228,7 +229,7 @@ CompressedImage compressed_image( // simplicity. auto image = compressed_image.Decode().ConvertToRGBA(); if (!image.IsValid()) { - FML_LOG(ERROR) << "Could not find fixture named " << fixture_name; + VALIDATION_LOG << "Could not find fixture named " << fixture_name; return nullptr; } @@ -241,7 +242,7 @@ CompressedImage compressed_image( renderer_.GetContext()->GetPermanentsAllocator()->CreateTexture( StorageMode::kHostVisible, texture_descriptor); if (!texture) { - FML_LOG(ERROR) << "Could not allocate texture for fixture " << fixture_name; + VALIDATION_LOG << "Could not allocate texture for fixture " << fixture_name; return nullptr; } texture->SetLabel(fixture_name); @@ -249,7 +250,7 @@ CompressedImage compressed_image( auto uploaded = texture->SetContents(image.GetAllocation()->GetMapping(), image.GetAllocation()->GetSize()); if (!uploaded) { - FML_LOG(ERROR) << "Could not upload texture to device memory for fixture " + VALIDATION_LOG << "Could not upload texture to device memory for fixture " << fixture_name; return nullptr; } diff --git a/impeller/renderer/backend/metal/allocator_mtl.mm b/impeller/renderer/backend/metal/allocator_mtl.mm index a9b1c6acebcb0..c1f606b61abea 100644 --- a/impeller/renderer/backend/metal/allocator_mtl.mm +++ b/impeller/renderer/backend/metal/allocator_mtl.mm @@ -6,6 +6,7 @@ #include "flutter/fml/build_config.h" #include "flutter/fml/logging.h" +#include "impeller/base/validation.h" #include "impeller/renderer/backend/metal/device_buffer_mtl.h" #include "impeller/renderer/backend/metal/formats_mtl.h" #include "impeller/renderer/backend/metal/texture_mtl.h" @@ -113,7 +114,7 @@ static MTLStorageMode ToMTLStorageMode(StorageMode mode) { auto mtl_texture_desc = ToMTLTextureDescriptor(desc); if (!mtl_texture_desc) { - FML_DLOG(ERROR) << "Texture descriptor was invalid."; + VALIDATION_LOG << "Texture descriptor was invalid."; return nullptr; } diff --git a/impeller/renderer/backend/metal/context_mtl.mm b/impeller/renderer/backend/metal/context_mtl.mm index 19d91acb33197..5c00d3ac85e85 100644 --- a/impeller/renderer/backend/metal/context_mtl.mm +++ b/impeller/renderer/backend/metal/context_mtl.mm @@ -20,7 +20,7 @@ NSMutableArray>* found_libraries = [NSMutableArray array]; for (const auto& library_path : libraries_paths) { if (!fml::IsFile(library_path)) { - FML_LOG(ERROR) << "Shader library does not exist at path '" + VALIDATION_LOG << "Shader library does not exist at path '" << library_path << "'"; continue; } @@ -61,7 +61,7 @@ auto library = std::shared_ptr(new ShaderLibraryMTL( ShaderLibrariesFromFiles(device_, libraries_paths))); if (!library->IsValid()) { - FML_DLOG(ERROR) << "Could not create valid Metal shader library."; + VALIDATION_LOG << "Could not create valid Metal shader library."; return; } shader_library_ = std::move(library); diff --git a/impeller/renderer/backend/metal/device_buffer_mtl.mm b/impeller/renderer/backend/metal/device_buffer_mtl.mm index 2aa7e5a2cdeea..99380195f7cd5 100644 --- a/impeller/renderer/backend/metal/device_buffer_mtl.mm +++ b/impeller/renderer/backend/metal/device_buffer_mtl.mm @@ -5,6 +5,7 @@ #include "impeller/renderer/backend/metal/device_buffer_mtl.h" #include "flutter/fml/logging.h" +#include "impeller/base/validation.h" #include "impeller/renderer/backend/metal/formats_mtl.h" #include "impeller/renderer/backend/metal/texture_mtl.h" @@ -29,7 +30,7 @@ // Avoid overruns. if (offset + desc.GetSizeOfBaseMipLevel() > size_) { - FML_DLOG(ERROR) << "Avoiding buffer overrun when creating texture."; + VALIDATION_LOG << "Avoiding buffer overrun when creating texture."; return nullptr; } diff --git a/impeller/renderer/backend/metal/texture_mtl.mm b/impeller/renderer/backend/metal/texture_mtl.mm index 8d1f42575fa89..ec6040f8ad73a 100644 --- a/impeller/renderer/backend/metal/texture_mtl.mm +++ b/impeller/renderer/backend/metal/texture_mtl.mm @@ -4,6 +4,8 @@ #include "impeller/renderer/backend/metal/texture_mtl.h" +#include "impeller/base/validation.h" + namespace impeller { TextureMTL::TextureMTL(TextureDescriptor p_desc, id texture) @@ -15,8 +17,7 @@ } if (desc.size != GetSize()) { - FML_DLOG(ERROR) - << "The texture and its descriptor disagree about its size."; + VALIDATION_LOG << "The texture and its descriptor disagree about its size."; return; } diff --git a/impeller/renderer/backend/metal/vertex_descriptor_mtl.mm b/impeller/renderer/backend/metal/vertex_descriptor_mtl.mm index 0ae058f6d87cf..942ab11eac57e 100644 --- a/impeller/renderer/backend/metal/vertex_descriptor_mtl.mm +++ b/impeller/renderer/backend/metal/vertex_descriptor_mtl.mm @@ -5,6 +5,7 @@ #include "impeller/renderer/backend/metal/vertex_descriptor_mtl.h" #include "flutter/fml/logging.h" +#include "impeller/base/validation.h" namespace impeller { @@ -176,7 +177,7 @@ static MTLVertexFormat ReadStageInputFormat(const ShaderStageIOSlot& input) { const auto& input = inputs[i]; auto vertex_format = ReadStageInputFormat(input); if (vertex_format == MTLVertexFormatInvalid) { - FML_LOG(ERROR) << "Format for input " << input.name << " not supported."; + VALIDATION_LOG << "Format for input " << input.name << " not supported."; return false; } diff --git a/impeller/renderer/pipeline.h b/impeller/renderer/pipeline.h index 69182785fb5a1..9362cbd0e6fd5 100644 --- a/impeller/renderer/pipeline.h +++ b/impeller/renderer/pipeline.h @@ -74,13 +74,13 @@ class PipelineT { using Builder = PipelineBuilder; explicit PipelineT(const Context& context) - : pipeline_future_(CreatePipelineFuture( + : PipelineT(CreatePipelineFuture( context, Builder::MakeDefaultPipelineDescriptor(context))) {} explicit PipelineT(const Context& context, std::optional desc) - : pipeline_future_(CreatePipelineFuture(context, desc)) {} + : PipelineT(CreatePipelineFuture(context, desc)) {} explicit PipelineT(PipelineFuture future) : pipeline_future_(std::move(future)) {} diff --git a/impeller/renderer/renderer.cc b/impeller/renderer/renderer.cc index 2a77090a2cb47..25ec21a9221c1 100644 --- a/impeller/renderer/renderer.cc +++ b/impeller/renderer/renderer.cc @@ -5,6 +5,7 @@ #include "impeller/renderer/renderer.h" #include "flutter/fml/logging.h" +#include "impeller/base/validation.h" #include "impeller/renderer/command_buffer.h" #include "impeller/renderer/surface.h" @@ -69,7 +70,7 @@ bool Renderer::Render(const Surface& surface, [sema = frames_in_flight_sema_](CommandBuffer::Status result) { sema->Signal(); if (result != CommandBuffer::Status::kCompleted) { - FML_LOG(ERROR) << "Could not commit command buffer."; + VALIDATION_LOG << "Could not commit command buffer."; } }); } From df7b79642d7946ff48a21bcb9f14322a87001516 Mon Sep 17 00:00:00 2001 From: Chinmay Garde Date: Thu, 2 Dec 2021 15:44:53 -0800 Subject: [PATCH 225/433] Implement Canvas::Skew. --- impeller/aiks/aiks_unittests.cc | 12 ++++++++++++ impeller/aiks/canvas.cc | 4 ++++ impeller/aiks/canvas.h | 2 ++ impeller/geometry/matrix.h | 31 +++++++++++++++++++++---------- impeller/geometry/shear.cc | 6 +----- impeller/geometry/shear.h | 2 -- impeller/geometry/size.cc | 1 - 7 files changed, 40 insertions(+), 18 deletions(-) diff --git a/impeller/aiks/aiks_unittests.cc b/impeller/aiks/aiks_unittests.cc index 2c916a8d720db..f10423ba9b6a0 100644 --- a/impeller/aiks/aiks_unittests.cc +++ b/impeller/aiks/aiks_unittests.cc @@ -141,5 +141,17 @@ TEST_F(AiksTest, CanPerformFullScreenMSAA) { ASSERT_TRUE(OpenPlaygroundHere(canvas.EndRecordingAsPicture())); } +TEST_F(AiksTest, CanPerformSkew) { + Canvas canvas; + + Paint red; + red.color = Color::Red(); + + canvas.Skew(10, 125); + canvas.DrawRect(Rect::MakeXYWH(0, 0, 100, 100), red); + + ASSERT_TRUE(OpenPlaygroundHere(canvas.EndRecordingAsPicture())); +} + } // namespace testing } // namespace impeller diff --git a/impeller/aiks/canvas.cc b/impeller/aiks/canvas.cc index c4fab50f5f979..634becd436c62 100644 --- a/impeller/aiks/canvas.cc +++ b/impeller/aiks/canvas.cc @@ -65,6 +65,10 @@ void Canvas::Scale(const Vector3& scale) { Concat(Matrix::MakeScale(scale)); } +void Canvas::Skew(Scalar sx, Scalar sy) { + Concat(Matrix::MakeSkew(sx, sy)); +} + void Canvas::Rotate(Radians radians) { Concat(Matrix::MakeRotationZ(radians)); } diff --git a/impeller/aiks/canvas.h b/impeller/aiks/canvas.h index 6fa9de69b1054..94ebdea3d4e48 100644 --- a/impeller/aiks/canvas.h +++ b/impeller/aiks/canvas.h @@ -47,6 +47,8 @@ class Canvas { void Scale(const Vector3& scale); + void Skew(Scalar sx, Scalar sy); + void Rotate(Radians radians); void DrawPath(Path path, Paint paint); diff --git a/impeller/geometry/matrix.h b/impeller/geometry/matrix.h index a52e5eeed0375..70a58482b863b 100644 --- a/impeller/geometry/matrix.h +++ b/impeller/geometry/matrix.h @@ -80,6 +80,15 @@ struct Matrix { // clang-format on } + static constexpr Matrix MakeSkew(Scalar sx, Scalar sy) { + // clang-format off + return Matrix(1.0, sy , 0.0, 0.0, + sx , 1.0, 0.0, 0.0, + 0.0, 0.0, 1.0, 0.0, + 0.0, 0.0, 0.0, 1.0); + // clang-format on + } + static Matrix MakeRotation(Scalar radians, const Vector4& r) { const Vector4 v = r.Normalize(); @@ -112,8 +121,8 @@ struct Matrix { } static Matrix MakeRotationX(Radians r) { - Scalar cosine = cos(r.radians); - Scalar sine = sin(r.radians); + const Scalar cosine = cos(r.radians); + const Scalar sine = sin(r.radians); // clang-format off return Matrix( 1.0, 0.0, 0.0, 0.0, @@ -125,8 +134,8 @@ struct Matrix { } static Matrix MakeRotationY(Radians r) { - Scalar cosine = cos(r.radians); - Scalar sine = sin(r.radians); + const Scalar cosine = cos(r.radians); + const Scalar sine = sin(r.radians); // clang-format off return Matrix( @@ -139,8 +148,8 @@ struct Matrix { } static Matrix MakeRotationZ(Radians r) { - Scalar cosine = cos(r.radians); - Scalar sine = sin(r.radians); + const Scalar cosine = cos(r.radians); + const Scalar sine = sin(r.radians); // clang-format off return Matrix ( @@ -196,12 +205,14 @@ struct Matrix { } constexpr Matrix Transpose() const { + // clang-format off return { - m[0], m[4], m[8], m[12], // - m[1], m[5], m[9], m[13], // - m[2], m[6], m[10], m[14], // - m[3], m[7], m[11], m[15], // + m[0], m[4], m[8], m[12], + m[1], m[5], m[9], m[13], + m[2], m[6], m[10], m[14], + m[3], m[7], m[11], m[15], }; + // clang-format on } Matrix Invert() const; diff --git a/impeller/geometry/shear.cc b/impeller/geometry/shear.cc index fed0fc9be0eb6..34986a9d787ce 100644 --- a/impeller/geometry/shear.cc +++ b/impeller/geometry/shear.cc @@ -7,10 +7,6 @@ namespace impeller { -std::string Shear::ToString() const { - std::stringstream stream; - stream << "{" << xy << ", " << xz << ", " << yz << "}"; - return stream.str(); -} +// } // namespace impeller diff --git a/impeller/geometry/shear.h b/impeller/geometry/shear.h index 220342a8b6538..155f7ea7fae43 100644 --- a/impeller/geometry/shear.h +++ b/impeller/geometry/shear.h @@ -27,8 +27,6 @@ struct Shear { } bool operator!=(const Shear& o) const { return !(*this == o); } - - std::string ToString() const; }; } // namespace impeller diff --git a/impeller/geometry/size.cc b/impeller/geometry/size.cc index 13bd3aebce283..77460b7ce431f 100644 --- a/impeller/geometry/size.cc +++ b/impeller/geometry/size.cc @@ -3,7 +3,6 @@ // found in the LICENSE file. #include "size.h" -#include namespace impeller { From 703388663a9e59775c52d100cf0acf54c2289619 Mon Sep 17 00:00:00 2001 From: Chinmay Garde Date: Fri, 3 Dec 2021 17:24:07 -0800 Subject: [PATCH 226/433] Implement Canvas::SaveLayer with bounds. --- impeller/aiks/aiks_unittests.cc | 54 +++++++++++++++++++++++++ impeller/aiks/canvas.cc | 12 +++++- impeller/aiks/paint_pass_delegate.cc | 8 +++- impeller/aiks/paint_pass_delegate.h | 8 +++- impeller/entity/entity_pass.cc | 14 ++++++- impeller/entity/entity_pass.h | 2 + impeller/entity/entity_pass_delegate.cc | 7 ++++ impeller/entity/entity_pass_delegate.h | 2 + impeller/geometry/size.h | 4 ++ 9 files changed, 107 insertions(+), 4 deletions(-) diff --git a/impeller/aiks/aiks_unittests.cc b/impeller/aiks/aiks_unittests.cc index f10423ba9b6a0..36f0a3d1cb0a2 100644 --- a/impeller/aiks/aiks_unittests.cc +++ b/impeller/aiks/aiks_unittests.cc @@ -153,5 +153,59 @@ TEST_F(AiksTest, CanPerformSkew) { ASSERT_TRUE(OpenPlaygroundHere(canvas.EndRecordingAsPicture())); } +TEST_F(AiksTest, CanPerformSaveLayerWithBounds) { + Canvas canvas; + + Paint red; + red.color = Color::Red(); + + Paint green; + green.color = Color::Green(); + + Paint blue; + blue.color = Color::Blue(); + + Paint save; + save.color = Color::Black(); + + canvas.SaveLayer(save, Rect{0, 0, 50, 50}); + + canvas.DrawRect({0, 0, 100, 100}, red); + canvas.DrawRect({10, 10, 100, 100}, green); + canvas.DrawRect({20, 20, 100, 100}, blue); + + canvas.Restore(); + + ASSERT_TRUE(OpenPlaygroundHere(canvas.EndRecordingAsPicture())); +} + +TEST_F( + AiksTest, + DISABLED_CanPerformSaveLayerWithBoundsAndLargerIntermediateIsNotAllocated) { + Canvas canvas; + + Paint red; + red.color = Color::Red(); + + Paint green; + green.color = Color::Green(); + + Paint blue; + blue.color = Color::Blue(); + + Paint save; + save.color = Color::Black().WithAlpha(0.5); + + canvas.SaveLayer(save, Rect{0, 0, 100000, 100000}); + + canvas.DrawRect({0, 0, 100, 100}, red); + canvas.DrawRect({10, 10, 100, 100}, green); + canvas.DrawRect({20, 20, 100, 100}, blue); + + canvas.Restore(); + + ASSERT_TRUE(OpenPlaygroundHere(canvas.EndRecordingAsPicture())); +} + } // namespace testing } // namespace impeller diff --git a/impeller/aiks/canvas.cc b/impeller/aiks/canvas.cc index 634becd436c62..99726bffa8d49 100644 --- a/impeller/aiks/canvas.cc +++ b/impeller/aiks/canvas.cc @@ -106,8 +106,18 @@ void Canvas::DrawCircle(Point center, Scalar radius, Paint paint) { void Canvas::SaveLayer(Paint paint, std::optional bounds) { GetCurrentPass().SetDelegate( - std::make_unique(std::move(paint))); + std::make_unique(std::move(paint), bounds)); + Save(true); + + if (bounds.has_value()) { + // Render target switches due to a save layer can be elided. In such cases + // where passes are collapsed into their parent, the clipping effect to + // the size of the render target that would have been allocated will be + // absent. Explicitly add back a clip to reproduce that behavior. Since + // clips never require a render target switch, this is a cheap operation. + ClipPath(PathBuilder{}.AddRect(bounds.value()).CreatePath()); + } } void Canvas::ClipPath(Path path) { diff --git a/impeller/aiks/paint_pass_delegate.cc b/impeller/aiks/paint_pass_delegate.cc index 24ca9d676d46d..f0f09cd5c674a 100644 --- a/impeller/aiks/paint_pass_delegate.cc +++ b/impeller/aiks/paint_pass_delegate.cc @@ -8,11 +8,17 @@ namespace impeller { -PaintPassDelegate::PaintPassDelegate(Paint paint) : paint_(std::move(paint)) {} +PaintPassDelegate::PaintPassDelegate(Paint paint, std::optional coverage) + : paint_(std::move(paint)), coverage_(std::move(coverage)) {} // |EntityPassDelgate| PaintPassDelegate::~PaintPassDelegate() = default; +// |EntityPassDelgate| +std::optional PaintPassDelegate::GetCoverageRect() { + return coverage_; +} + // |EntityPassDelgate| bool PaintPassDelegate::CanElide() { return paint_.color.IsTransparent(); diff --git a/impeller/aiks/paint_pass_delegate.h b/impeller/aiks/paint_pass_delegate.h index 18261727d83e5..dcd94d16d8258 100644 --- a/impeller/aiks/paint_pass_delegate.h +++ b/impeller/aiks/paint_pass_delegate.h @@ -4,6 +4,8 @@ #pragma once +#include + #include "flutter/fml/macros.h" #include "impeller/aiks/paint.h" #include "impeller/entity/entity_pass_delegate.h" @@ -12,11 +14,14 @@ namespace impeller { class PaintPassDelegate final : public EntityPassDelegate { public: - PaintPassDelegate(Paint paint); + PaintPassDelegate(Paint paint, std::optional coverage); // |EntityPassDelgate| ~PaintPassDelegate() override; + // |EntityPassDelegate| + std::optional GetCoverageRect() override; + // |EntityPassDelgate| bool CanElide() override; @@ -29,6 +34,7 @@ class PaintPassDelegate final : public EntityPassDelegate { private: const Paint paint_; + const std::optional coverage_; FML_DISALLOW_COPY_AND_ASSIGN(PaintPassDelegate); }; diff --git a/impeller/entity/entity_pass.cc b/impeller/entity/entity_pass.cc index 33726523f1415..e01e1fdddb96e 100644 --- a/impeller/entity/entity_pass.cc +++ b/impeller/entity/entity_pass.cc @@ -74,6 +74,18 @@ const EntityPass::Subpasses& EntityPass::GetSubpasses() const { return subpasses_; } +Rect EntityPass::GetSubpassCoverage(const EntityPass& subpass) const { + auto subpass_coverage = subpass.GetCoverageRect(); + auto delegate_coverage = + delegate_->GetCoverageRect().value_or(subpass_coverage); + Rect coverage; + coverage.origin = subpass_coverage.origin; + // TODO(csg): This must still be restricted to the max texture size. Or, + // decide if this must be done by the allocator. + coverage.size = subpass_coverage.size.Min(delegate_coverage.size); + return coverage; +} + EntityPass* EntityPass::AddSubpass(std::unique_ptr pass) { if (!pass) { return nullptr; @@ -103,7 +115,7 @@ bool EntityPass::Render(ContentRenderer& renderer, continue; } - const auto subpass_coverage = subpass->GetCoverageRect(); + const auto subpass_coverage = GetSubpassCoverage(*subpass); if (subpass_coverage.IsEmpty()) { // It is not an error to have an empty subpass. But subpasses that can't diff --git a/impeller/entity/entity_pass.h b/impeller/entity/entity_pass.h index 47cf3b2a0f427..eb89894e52f6e 100644 --- a/impeller/entity/entity_pass.h +++ b/impeller/entity/entity_pass.h @@ -66,6 +66,8 @@ class EntityPass { std::unique_ptr delegate_ = EntityPassDelegate::MakeDefault(); + Rect GetSubpassCoverage(const EntityPass& subpass) const; + FML_DISALLOW_COPY_AND_ASSIGN(EntityPass); }; diff --git a/impeller/entity/entity_pass_delegate.cc b/impeller/entity/entity_pass_delegate.cc index 766a4e210ddd5..00963048a6e9e 100644 --- a/impeller/entity/entity_pass_delegate.cc +++ b/impeller/entity/entity_pass_delegate.cc @@ -14,12 +14,19 @@ class DefaultEntityPassDelegate final : public EntityPassDelegate { public: DefaultEntityPassDelegate() = default; + // |EntityPassDelegate| ~DefaultEntityPassDelegate() override = default; + // |EntityPassDelegate| + std::optional GetCoverageRect() override { return std::nullopt; } + + // |EntityPassDelegate| bool CanElide() override { return false; } + // |EntityPassDelegate| bool CanCollapseIntoParentPass() override { return true; } + // |EntityPassDelegate| std::shared_ptr CreateContentsForSubpassTarget( std::shared_ptr target) override { // Not possible since this pass always collapses into its parent. diff --git a/impeller/entity/entity_pass_delegate.h b/impeller/entity/entity_pass_delegate.h index f25de16122112..1325bcd920227 100644 --- a/impeller/entity/entity_pass_delegate.h +++ b/impeller/entity/entity_pass_delegate.h @@ -18,6 +18,8 @@ class EntityPassDelegate { EntityPassDelegate(); + virtual std::optional GetCoverageRect() = 0; + virtual ~EntityPassDelegate(); virtual bool CanElide() = 0; diff --git a/impeller/geometry/size.h b/impeller/geometry/size.h index 11301eaa9d9a4..526866060f27e 100644 --- a/impeller/geometry/size.h +++ b/impeller/geometry/size.h @@ -59,6 +59,10 @@ struct TSize { return {width - s.width, height - s.height}; } + constexpr TSize Min(const TSize& s) const { return Intersection(s); } + + constexpr TSize Max(const TSize& s) const { return Union(s); } + constexpr TSize Union(const TSize& o) const { return { std::max(width, o.width), From c2292c88ee47820dbce16b1527b3a12c4855087f Mon Sep 17 00:00:00 2001 From: Chinmay Garde Date: Fri, 3 Dec 2021 17:48:50 -0800 Subject: [PATCH 227/433] Minor: Add Canvas::Transform. --- impeller/aiks/canvas.cc | 4 ++++ impeller/aiks/canvas.h | 2 ++ 2 files changed, 6 insertions(+) diff --git a/impeller/aiks/canvas.cc b/impeller/aiks/canvas.cc index 99726bffa8d49..6820291de70bc 100644 --- a/impeller/aiks/canvas.cc +++ b/impeller/aiks/canvas.cc @@ -53,6 +53,10 @@ void Canvas::Concat(const Matrix& xformation) { xformation_stack_.back().xformation = xformation * GetCurrentTransformation(); } +void Canvas::Transform(const Matrix& xformation) { + Concat(xformation); +} + const Matrix& Canvas::GetCurrentTransformation() const { return xformation_stack_.back().xformation; } diff --git a/impeller/aiks/canvas.h b/impeller/aiks/canvas.h index 94ebdea3d4e48..2bf6aeeed4816 100644 --- a/impeller/aiks/canvas.h +++ b/impeller/aiks/canvas.h @@ -41,6 +41,8 @@ class Canvas { const Matrix& GetCurrentTransformation() const; + void Transform(const Matrix& xformation); + void Concat(const Matrix& xformation); void Translate(const Vector3& offset); From 255b8d671251b980fb30ce4a34db09fcf813bfe7 Mon Sep 17 00:00:00 2001 From: Chinmay Garde Date: Sat, 4 Dec 2021 13:39:44 -0800 Subject: [PATCH 228/433] Rect union and intersection. --- impeller/aiks/aiks_unittests.cc | 18 +++++ impeller/geometry/geometry_unittests.cc | 103 ++++++++++++++++-------- impeller/geometry/rect.h | 55 +++++++------ impeller/geometry/size.h | 8 +- 4 files changed, 123 insertions(+), 61 deletions(-) diff --git a/impeller/aiks/aiks_unittests.cc b/impeller/aiks/aiks_unittests.cc index 36f0a3d1cb0a2..4d0089f0a54a1 100644 --- a/impeller/aiks/aiks_unittests.cc +++ b/impeller/aiks/aiks_unittests.cc @@ -106,6 +106,24 @@ TEST_F(AiksTest, CanRenderClips) { ASSERT_TRUE(OpenPlaygroundHere(canvas.EndRecordingAsPicture())); } +TEST_F(AiksTest, CanSaveLayerStandalone) { + Canvas canvas; + + Paint red; + red.color = Color::Red(); + + Paint alpha; + alpha.color = Color::Red().WithAlpha(0.5); + + canvas.SaveLayer(alpha); + + canvas.DrawCircle({125, 125}, 125, red); + + canvas.Restore(); + + ASSERT_TRUE(OpenPlaygroundHere(canvas.EndRecordingAsPicture())); +} + TEST_F(AiksTest, CanRenderGroupOpacity) { Canvas canvas; diff --git a/impeller/geometry/geometry_unittests.cc b/impeller/geometry/geometry_unittests.cc index f7f270dfa6420..60163c5b2e6fc 100644 --- a/impeller/geometry/geometry_unittests.cc +++ b/impeller/geometry/geometry_unittests.cc @@ -141,38 +141,6 @@ TEST(GeometryTest, QuaternionLerp) { ASSERT_QUATERNION_NEAR(q3, expected); } -TEST(GeometryTest, RectWithPoint) { - auto rect = Rect{}; - - auto expected = Rect{}; - - ASSERT_RECT_NEAR(rect, expected); - - rect = rect.WithPoint({100, 100}); - expected = Rect{0, 0, 100, 100}; - ASSERT_RECT_NEAR(rect, expected); - - rect = rect.WithPoint({-11, -12}); - expected = Rect{-11, -12, 111, 112}; - ASSERT_RECT_NEAR(rect, expected); - - rect = rect.WithPoint({55, 65}); - expected = Rect{-11, -12, 111, 112}; - ASSERT_RECT_NEAR(rect, expected); - - rect = rect.WithPoint({-25, 0}); - expected = Rect{-25, -12, 125, 112}; - ASSERT_RECT_NEAR(rect, expected); - - rect = rect.WithPoint({0, -25}); - expected = Rect{-25, -25, 125, 125}; - ASSERT_RECT_NEAR(rect, expected); - - rect = rect.WithPoint({125, 135}); - expected = Rect{-25, -25, 150, 160}; - ASSERT_RECT_NEAR(rect, expected); -} - TEST(GeometryTest, SimplePath) { Path path; @@ -216,7 +184,7 @@ TEST(GeometryTest, BoundingBoxCubic) { Path path; path.AddCubicComponent({120, 160}, {25, 200}, {220, 260}, {220, 40}); auto box = path.GetBoundingBox(); - Rect expected(0, 0, 220, 198.862); + Rect expected(93.9101, 40, 126.09, 158.862); ASSERT_RECT_NEAR(box, expected); } @@ -225,7 +193,7 @@ TEST(GeometryTest, BoundingBoxOfCompositePathIsCorrect) { builder.AddRoundedRect({{10, 10}, {300, 300}}, {50, 50, 50, 50}); auto path = builder.CreatePath(); auto actual = path.GetBoundingBox(); - Rect expected(0, 0, 310, 310); + Rect expected(10, 10, 300, 300); ASSERT_RECT_NEAR(actual, expected); } @@ -273,5 +241,72 @@ TEST(GeometryTest, CanConvertBetweenDegressAndRadians) { } } +TEST(GeometryTest, RectUnion) { + { + Rect a(100, 100, 100, 100); + Rect b(0, 0, 0, 0); + auto u = a.Union(b); + auto expected = Rect(0, 0, 200, 200); + ASSERT_RECT_NEAR(u, expected); + } + + { + Rect a(100, 100, 100, 100); + Rect b(10, 10, 0, 0); + auto u = a.Union(b); + auto expected = Rect(10, 10, 190, 190); + ASSERT_RECT_NEAR(u, expected); + } + + { + Rect a(0, 0, 100, 100); + Rect b(10, 10, 100, 100); + auto u = a.Union(b); + auto expected = Rect(0, 0, 110, 110); + ASSERT_RECT_NEAR(u, expected); + } + + { + Rect a(0, 0, 100, 100); + Rect b(100, 100, 100, 100); + auto u = a.Union(b); + auto expected = Rect(0, 0, 200, 200); + ASSERT_RECT_NEAR(u, expected); + } +} + +TEST(GeometryTest, RectIntersection) { + { + Rect a(100, 100, 100, 100); + Rect b(0, 0, 0, 0); + + auto u = a.Intersection(b); + ASSERT_FALSE(u.has_value()); + } + + { + Rect a(100, 100, 100, 100); + Rect b(10, 10, 0, 0); + auto u = a.Intersection(b); + ASSERT_FALSE(u.has_value()); + } + + { + Rect a(0, 0, 100, 100); + Rect b(10, 10, 100, 100); + auto u = a.Intersection(b); + ASSERT_TRUE(u.has_value()); + auto expected = Rect(10, 10, 90, 90); + ASSERT_RECT_NEAR(u.value(), expected); + } + + { + Rect a(0, 0, 100, 100); + Rect b(100, 100, 100, 100); + auto u = a.Intersection(b); + ASSERT_FALSE(u.has_value()); + } +} + } // namespace testing } // namespace impeller diff --git a/impeller/geometry/rect.h b/impeller/geometry/rect.h index c7477d806c9ee..4517eba06e5db 100644 --- a/impeller/geometry/rect.h +++ b/impeller/geometry/rect.h @@ -4,6 +4,7 @@ #pragma once +#include #include #include @@ -87,35 +88,41 @@ struct TRect { constexpr bool IsEmpty() const { return size.IsEmpty(); } - constexpr TRect WithPoint(const TPoint& p) const { - TRect copy = *this; - if (p.x < origin.x) { - copy.origin.x = p.x; - copy.size.width += (origin.x - p.x); - } - - if (p.y < origin.y) { - copy.origin.y = p.y; - copy.size.height += (origin.y - p.y); - } + constexpr std::array GetLTRB() const { + const auto left = std::min(origin.x, origin.x + size.width); + const auto top = std::min(origin.y, origin.y + size.height); + const auto right = std::max(origin.x, origin.x + size.width); + const auto bottom = std::max(origin.y, origin.y + size.height); + return {left, top, right, bottom}; + } - if (p.x > (size.width + origin.x)) { - copy.size.width += p.x - (size.width + origin.x); - } + constexpr TRect Union(const TRect& o) const { + auto this_ltrb = GetLTRB(); + auto other_ltrb = o.GetLTRB(); + return TRect::MakeLTRB(std::min(this_ltrb[0], other_ltrb[0]), // + std::min(this_ltrb[1], other_ltrb[1]), // + std::max(this_ltrb[2], other_ltrb[2]), // + std::max(this_ltrb[3], other_ltrb[3]) // + ); + } - if (p.y > (size.height + origin.y)) { - copy.size.height += p.y - (size.height + origin.y); + constexpr std::optional> Intersection(const TRect& o) const { + auto this_ltrb = GetLTRB(); + auto other_ltrb = o.GetLTRB(); + auto intersection = + TRect::MakeLTRB(std::max(this_ltrb[0], other_ltrb[0]), // + std::max(this_ltrb[1], other_ltrb[1]), // + std::min(this_ltrb[2], other_ltrb[2]), // + std::min(this_ltrb[3], other_ltrb[3]) // + ); + if (intersection.size.IsEmpty()) { + return std::nullopt; } - - return copy; + return intersection; } - constexpr TRect WithPoints(const std::vector>& points) const { - TRect box = *this; - for (const auto& point : points) { - box = box.WithPoint(point); - } - return box; + constexpr bool IntersectsWithRect(const TRect& o) const { + return Interesection(o).has_value(); } }; diff --git a/impeller/geometry/size.h b/impeller/geometry/size.h index 526866060f27e..f56e682cdc7b6 100644 --- a/impeller/geometry/size.h +++ b/impeller/geometry/size.h @@ -79,11 +79,13 @@ struct TSize { constexpr Type Area() const { return width * height; } - constexpr bool IsZero() const { return width * height == 0.0; } + constexpr bool IsPositive() const { return width > 0 && height > 0; } - constexpr bool IsPositive() const { return width * height > 0.0; } + constexpr bool IsNegative() const { return width < 0 || height < 0; } - constexpr bool IsEmpty() const { return !IsPositive(); } + constexpr bool IsZero() const { return width == 0 || height == 0; } + + constexpr bool IsEmpty() const { return IsNegative() || IsZero(); } template static constexpr TSize Ceil(const TSize& other) { From c3711204db31d3d38d239c791391f0b97d729004 Mon Sep 17 00:00:00 2001 From: Chinmay Garde Date: Sat, 4 Dec 2021 14:02:31 -0800 Subject: [PATCH 229/433] Don't let subpass coverage exceed entity coverage. --- impeller/aiks/aiks_unittests.cc | 5 +- impeller/aiks/canvas.cc | 1 + impeller/entity/contents.cc | 9 +++- impeller/entity/entity.cc | 16 ++++++ impeller/entity/entity.h | 7 +++ impeller/entity/entity_pass.cc | 67 ++++++++++++++----------- impeller/entity/entity_pass.h | 8 ++- impeller/geometry/geometry_unittests.cc | 6 ++- impeller/geometry/path.cc | 6 +-- impeller/geometry/path.h | 2 +- impeller/renderer/renderer_unittests.cc | 2 +- 11 files changed, 82 insertions(+), 47 deletions(-) diff --git a/impeller/aiks/aiks_unittests.cc b/impeller/aiks/aiks_unittests.cc index 4d0089f0a54a1..0aacead3f3bd8 100644 --- a/impeller/aiks/aiks_unittests.cc +++ b/impeller/aiks/aiks_unittests.cc @@ -197,9 +197,8 @@ TEST_F(AiksTest, CanPerformSaveLayerWithBounds) { ASSERT_TRUE(OpenPlaygroundHere(canvas.EndRecordingAsPicture())); } -TEST_F( - AiksTest, - DISABLED_CanPerformSaveLayerWithBoundsAndLargerIntermediateIsNotAllocated) { +TEST_F(AiksTest, + CanPerformSaveLayerWithBoundsAndLargerIntermediateIsNotAllocated) { Canvas canvas; Paint red; diff --git a/impeller/aiks/canvas.cc b/impeller/aiks/canvas.cc index 6820291de70bc..7b66187af37fe 100644 --- a/impeller/aiks/canvas.cc +++ b/impeller/aiks/canvas.cc @@ -132,6 +132,7 @@ void Canvas::ClipPath(Path path) { entity.SetPath(std::move(path)); entity.SetContents(std::make_shared()); entity.SetStencilDepth(GetStencilDepth()); + entity.SetAddsToCoverage(false); GetCurrentPass().AddEntity(std::move(entity)); } diff --git a/impeller/entity/contents.cc b/impeller/entity/contents.cc index 7de2f68d6efb4..f2e1d7f18d27f 100644 --- a/impeller/entity/contents.cc +++ b/impeller/entity/contents.cc @@ -205,7 +205,12 @@ bool TextureContents::Render(const ContentRenderer& renderer, using FS = TextureFillFragmentShader; const auto coverage_rect = entity.GetPath().GetBoundingBox(); - if (coverage_rect.size.IsEmpty()) { + + if (!coverage_rect.has_value()) { + return true; + } + + if (coverage_rect->size.IsEmpty()) { return true; } @@ -226,7 +231,7 @@ bool TextureContents::Render(const ContentRenderer& renderer, VS::PerVertexData data; data.vertices = vtx; data.texture_coords = - ((vtx - coverage_rect.origin) / coverage_rect.size); + ((vtx - coverage_rect->origin) / coverage_rect->size); vertex_builder.AppendVertex(data); }); if (!tess_result) { diff --git a/impeller/entity/entity.cc b/impeller/entity/entity.cc index fec6b9710b1fa..150b30fcf603b 100644 --- a/impeller/entity/entity.cc +++ b/impeller/entity/entity.cc @@ -29,6 +29,22 @@ void Entity::SetPath(Path path) { path_ = std::move(path); } +void Entity::SetAddsToCoverage(bool adds) { + adds_to_coverage_ = adds; +} + +bool Entity::AddsToCoverage() const { + return adds_to_coverage_; +} + +std::optional Entity::GetCoverage() const { + if (!adds_to_coverage_) { + return std::nullopt; + } + + return path_.GetBoundingBox(); +} + void Entity::SetContents(std::shared_ptr contents) { contents_ = std::move(contents); } diff --git a/impeller/entity/entity.h b/impeller/entity/entity.h index 26c354f00dc00..8e78391b38ef1 100644 --- a/impeller/entity/entity.h +++ b/impeller/entity/entity.h @@ -30,6 +30,12 @@ class Entity { void SetPath(Path path); + void SetAddsToCoverage(bool adds); + + bool AddsToCoverage() const; + + std::optional GetCoverage() const; + void SetContents(std::shared_ptr contents); const std::shared_ptr& GetContents() const; @@ -47,6 +53,7 @@ class Entity { std::shared_ptr contents_; Path path_; uint32_t stencil_depth_ = 0u; + bool adds_to_coverage_ = true; }; } // namespace impeller diff --git a/impeller/entity/entity_pass.cc b/impeller/entity/entity_pass.cc index e01e1fdddb96e..fc8734a59a91e 100644 --- a/impeller/entity/entity_pass.cc +++ b/impeller/entity/entity_pass.cc @@ -43,27 +43,41 @@ size_t EntityPass::GetSubpassesDepth() const { return max_subpass_depth + 1u; } -Rect EntityPass::GetCoverageRect() const { - std::optional min, max; +std::optional EntityPass::GetEntitiesCoverage() const { + std::optional result; for (const auto& entity : entities_) { - auto coverage = entity.GetPath().GetMinMaxCoveragePoints(); - if (!coverage.has_value()) { + auto coverage = entity.GetCoverage(); + if (!result.has_value() && coverage.has_value()) { + result = coverage; continue; } - if (!min.has_value()) { - min = coverage->first; - } - if (!max.has_value()) { - max = coverage->second; + if (!coverage.has_value()) { + continue; } - min = min->Min(coverage->first); - max = max->Max(coverage->second); + result = result->Union(coverage.value()); + } + return result; +} + +std::optional EntityPass::GetSubpassCoverage( + const EntityPass& subpass) const { + auto entities_coverage = subpass.GetEntitiesCoverage(); + // The entities don't cover anything. There is nothing to do. + if (!entities_coverage.has_value()) { + return std::nullopt; } - if (!min.has_value() || !max.has_value()) { - return {}; + + // The delegates don't have an opinion on what the entity coverage has to be. + // Just use that as-is. + auto delegate_coverage = delegate_->GetCoverageRect(); + if (!delegate_coverage.has_value()) { + return entities_coverage; } - const auto diff = *max - *min; - return {min->x, min->y, diff.x, diff.y}; + + // If the delete tells us the coverage is smaller than it needs to be, then + // great. OTOH, if the delegate is being wasteful, limit coverage to what is + // actually needed. + return entities_coverage->Intersection(delegate_coverage.value()); } EntityPass* EntityPass::GetSuperpass() const { @@ -74,18 +88,6 @@ const EntityPass::Subpasses& EntityPass::GetSubpasses() const { return subpasses_; } -Rect EntityPass::GetSubpassCoverage(const EntityPass& subpass) const { - auto subpass_coverage = subpass.GetCoverageRect(); - auto delegate_coverage = - delegate_->GetCoverageRect().value_or(subpass_coverage); - Rect coverage; - coverage.origin = subpass_coverage.origin; - // TODO(csg): This must still be restricted to the max texture size. Or, - // decide if this must be done by the allocator. - coverage.size = subpass_coverage.size.Min(delegate_coverage.size); - return coverage; -} - EntityPass* EntityPass::AddSubpass(std::unique_ptr pass) { if (!pass) { return nullptr; @@ -117,7 +119,11 @@ bool EntityPass::Render(ContentRenderer& renderer, const auto subpass_coverage = GetSubpassCoverage(*subpass); - if (subpass_coverage.IsEmpty()) { + if (!subpass_coverage.has_value()) { + continue; + } + + if (subpass_coverage->size.IsEmpty()) { // It is not an error to have an empty subpass. But subpasses that can't // create their intermediates must trip errors. continue; @@ -126,7 +132,7 @@ bool EntityPass::Render(ContentRenderer& renderer, auto context = renderer.GetContext(); auto subpass_target = RenderTarget::CreateOffscreen( - *context, ISize::Ceil(subpass_coverage.size)); + *context, ISize::Ceil(subpass_coverage->size)); auto subpass_texture = subpass_target.GetRenderTargetTexture(); @@ -178,7 +184,8 @@ bool EntityPass::Render(ContentRenderer& renderer, } Entity entity; - entity.SetPath(PathBuilder{}.AddRect(subpass_coverage).CreatePath()); + entity.SetPath( + PathBuilder{}.AddRect(subpass_coverage.value()).CreatePath()); entity.SetContents(std::move(offscreen_texture_contents)); entity.SetStencilDepth(stencil_depth_); entity.SetTransformation(xformation_); diff --git a/impeller/entity/entity_pass.h b/impeller/entity/entity_pass.h index eb89894e52f6e..2a9648fe7dd97 100644 --- a/impeller/entity/entity_pass.h +++ b/impeller/entity/entity_pass.h @@ -33,10 +33,6 @@ class EntityPass { std::unique_ptr Clone() const; - Rect GetCoverageRect() const; - - // TODO(csg): This prevents an optimization where the coverage can be - // calculated once in SetEntities an memoized. void AddEntity(Entity entity); void SetEntities(Entities entities); @@ -66,7 +62,9 @@ class EntityPass { std::unique_ptr delegate_ = EntityPassDelegate::MakeDefault(); - Rect GetSubpassCoverage(const EntityPass& subpass) const; + std::optional GetSubpassCoverage(const EntityPass& subpass) const; + + std::optional GetEntitiesCoverage() const; FML_DISALLOW_COPY_AND_ASSIGN(EntityPass); }; diff --git a/impeller/geometry/geometry_unittests.cc b/impeller/geometry/geometry_unittests.cc index 60163c5b2e6fc..5aec38a236ae4 100644 --- a/impeller/geometry/geometry_unittests.cc +++ b/impeller/geometry/geometry_unittests.cc @@ -185,7 +185,8 @@ TEST(GeometryTest, BoundingBoxCubic) { path.AddCubicComponent({120, 160}, {25, 200}, {220, 260}, {220, 40}); auto box = path.GetBoundingBox(); Rect expected(93.9101, 40, 126.09, 158.862); - ASSERT_RECT_NEAR(box, expected); + ASSERT_TRUE(box.has_value()); + ASSERT_RECT_NEAR(box.value(), expected); } TEST(GeometryTest, BoundingBoxOfCompositePathIsCorrect) { @@ -194,7 +195,8 @@ TEST(GeometryTest, BoundingBoxOfCompositePathIsCorrect) { auto path = builder.CreatePath(); auto actual = path.GetBoundingBox(); Rect expected(10, 10, 300, 300); - ASSERT_RECT_NEAR(actual, expected); + ASSERT_TRUE(actual.has_value()); + ASSERT_RECT_NEAR(actual.value(), expected); } TEST(GeometryTest, CanGenerateMipCounts) { diff --git a/impeller/geometry/path.cc b/impeller/geometry/path.cc index 9959488bdfdf2..1c66461bea0c8 100644 --- a/impeller/geometry/path.cc +++ b/impeller/geometry/path.cc @@ -173,15 +173,15 @@ std::vector Path::CreatePolyline( return points; } -Rect Path::GetBoundingBox() const { +std::optional Path::GetBoundingBox() const { auto min_max = GetMinMaxCoveragePoints(); if (!min_max.has_value()) { - return {}; + return std::nullopt; } auto min = min_max->first; auto max = min_max->second; const auto difference = max - min; - return {min.x, min.y, difference.x, difference.y}; + return Rect{min.x, min.y, difference.x, difference.y}; } std::optional> Path::GetMinMaxCoveragePoints() const { diff --git a/impeller/geometry/path.h b/impeller/geometry/path.h index 78bee57798deb..2abd72d08e5d3 100644 --- a/impeller/geometry/path.h +++ b/impeller/geometry/path.h @@ -67,7 +67,7 @@ class Path { std::vector CreatePolyline( const SmoothingApproximation& approximation = {}) const; - Rect GetBoundingBox() const; + std::optional GetBoundingBox() const; std::optional> GetMinMaxCoveragePoints() const; diff --git a/impeller/renderer/renderer_unittests.cc b/impeller/renderer/renderer_unittests.cc index a388851b76321..c7b59436052d6 100644 --- a/impeller/renderer/renderer_unittests.cc +++ b/impeller/renderer/renderer_unittests.cc @@ -266,7 +266,7 @@ TEST_F(RendererTest, CanRenderToTexture) { TEST_F(RendererTest, CanRenderPath) { auto path = PathBuilder{}.AddCircle({550, 550}, 500).CreatePath(); - ASSERT_FALSE(path.GetBoundingBox().IsZero()); + ASSERT_FALSE(path.GetBoundingBox().has_value()); using BoxPipeline = PipelineT; using VS = BoxFadeVertexShader; From 7964f5d989714104ae6c5144cc1b4e3d597a3af1 Mon Sep 17 00:00:00 2001 From: Chinmay Garde Date: Sat, 4 Dec 2021 14:17:20 -0800 Subject: [PATCH 230/433] Remove unused and confusing API in TRect. --- impeller/geometry/size.h | 16 ++++++---------- 1 file changed, 6 insertions(+), 10 deletions(-) diff --git a/impeller/geometry/size.h b/impeller/geometry/size.h index f56e682cdc7b6..2415d314a5560 100644 --- a/impeller/geometry/size.h +++ b/impeller/geometry/size.h @@ -59,21 +59,17 @@ struct TSize { return {width - s.width, height - s.height}; } - constexpr TSize Min(const TSize& s) const { return Intersection(s); } - - constexpr TSize Max(const TSize& s) const { return Union(s); } - - constexpr TSize Union(const TSize& o) const { + constexpr TSize Min(const TSize& o) const { return { - std::max(width, o.width), - std::max(height, o.height), + std::min(width, o.width), + std::min(height, o.height), }; } - constexpr TSize Intersection(const TSize& o) const { + constexpr TSize Max(const TSize& o) const { return { - std::min(width, o.width), - std::min(height, o.height), + std::max(width, o.width), + std::max(height, o.height), }; } From e39504563ff552f18d2351d43656b1d797300e07 Mon Sep 17 00:00:00 2001 From: Chinmay Garde Date: Mon, 6 Dec 2021 13:49:18 -0800 Subject: [PATCH 231/433] Add macOS availability checks. --- .../backend/metal/device_buffer_mtl.mm | 17 +++++---- .../backend/metal/vertex_descriptor_mtl.mm | 36 +++++++++++++++---- 2 files changed, 41 insertions(+), 12 deletions(-) diff --git a/impeller/renderer/backend/metal/device_buffer_mtl.mm b/impeller/renderer/backend/metal/device_buffer_mtl.mm index 99380195f7cd5..5fdb783431bec 100644 --- a/impeller/renderer/backend/metal/device_buffer_mtl.mm +++ b/impeller/renderer/backend/metal/device_buffer_mtl.mm @@ -34,14 +34,19 @@ return nullptr; } - auto texture = [buffer_ newTextureWithDescriptor:ToMTLTextureDescriptor(desc) - offset:offset - bytesPerRow:desc.GetBytesPerRow()]; - if (!texture) { + if (@available(macOS 10.13, *)) { + auto texture = + [buffer_ newTextureWithDescriptor:ToMTLTextureDescriptor(desc) + offset:offset + bytesPerRow:desc.GetBytesPerRow()]; + if (!texture) { + return nullptr; + } + + return std::make_shared(desc, texture); + } else { return nullptr; } - - return std::make_shared(desc, texture); } [[nodiscard]] bool DeviceBufferMTL::CopyHostBuffer(const uint8_t* source, diff --git a/impeller/renderer/backend/metal/vertex_descriptor_mtl.mm b/impeller/renderer/backend/metal/vertex_descriptor_mtl.mm index 942ab11eac57e..e71571c4d837a 100644 --- a/impeller/renderer/backend/metal/vertex_descriptor_mtl.mm +++ b/impeller/renderer/backend/metal/vertex_descriptor_mtl.mm @@ -39,7 +39,11 @@ static MTLVertexFormat ReadStageInputFormat(const ShaderStageIOSlot& input) { if (input.bit_width == 8 * sizeof(float) / 2) { switch (input.vec_size) { case 1: - return MTLVertexFormatHalf; + if (@available(macOS 10.13, *)) { + return MTLVertexFormatHalf; + } else { + return MTLVertexFormatInvalid; + } case 2: return MTLVertexFormatHalf2; case 3: @@ -56,7 +60,11 @@ static MTLVertexFormat ReadStageInputFormat(const ShaderStageIOSlot& input) { } case ShaderType::kBoolean: { if (input.bit_width == 8 * sizeof(bool) && input.vec_size == 1) { - return MTLVertexFormatChar; + if (@available(macOS 10.13, *)) { + return MTLVertexFormatChar; + } else { + return MTLVertexFormatInvalid; + } } return MTLVertexFormatInvalid; } @@ -64,7 +72,11 @@ static MTLVertexFormat ReadStageInputFormat(const ShaderStageIOSlot& input) { if (input.bit_width == 8 * sizeof(char)) { switch (input.vec_size) { case 1: - return MTLVertexFormatChar; + if (@available(macOS 10.13, *)) { + return MTLVertexFormatChar; + } else { + return MTLVertexFormatInvalid; + } case 2: return MTLVertexFormatChar2; case 3: @@ -79,7 +91,11 @@ static MTLVertexFormat ReadStageInputFormat(const ShaderStageIOSlot& input) { if (input.bit_width == 8 * sizeof(char)) { switch (input.vec_size) { case 1: - return MTLVertexFormatUChar; + if (@available(macOS 10.13, *)) { + return MTLVertexFormatUChar; + } else { + return MTLVertexFormatInvalid; + } case 2: return MTLVertexFormatUChar2; case 3: @@ -94,7 +110,11 @@ static MTLVertexFormat ReadStageInputFormat(const ShaderStageIOSlot& input) { if (input.bit_width == 8 * sizeof(short)) { switch (input.vec_size) { case 1: - return MTLVertexFormatShort; + if (@available(macOS 10.13, *)) { + return MTLVertexFormatShort; + } else { + return MTLVertexFormatInvalid; + } case 2: return MTLVertexFormatShort2; case 3: @@ -109,7 +129,11 @@ static MTLVertexFormat ReadStageInputFormat(const ShaderStageIOSlot& input) { if (input.bit_width == 8 * sizeof(ushort)) { switch (input.vec_size) { case 1: - return MTLVertexFormatUShort; + if (@available(macOS 10.13, *)) { + return MTLVertexFormatUShort; + } else { + return MTLVertexFormatInvalid; + } case 2: return MTLVertexFormatUShort2; case 3: From dade71a2c4c282496c7881ffff697b90adda9b05 Mon Sep 17 00:00:00 2001 From: Chinmay Garde Date: Mon, 6 Dec 2021 14:05:29 -0800 Subject: [PATCH 232/433] Add missing availability check for macOS 10.12. --- impeller/renderer/backend/metal/device_buffer_mtl.mm | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/impeller/renderer/backend/metal/device_buffer_mtl.mm b/impeller/renderer/backend/metal/device_buffer_mtl.mm index 5fdb783431bec..1a94831fe35d8 100644 --- a/impeller/renderer/backend/metal/device_buffer_mtl.mm +++ b/impeller/renderer/backend/metal/device_buffer_mtl.mm @@ -97,8 +97,10 @@ if (label.empty()) { return false; } - [buffer_ addDebugMarker:@(label.c_str()) - range:NSMakeRange(range.offset, range.length)]; + if (@available(macOS 10.12, *)) { + [buffer_ addDebugMarker:@(label.c_str()) + range:NSMakeRange(range.offset, range.length)]; + } return true; } From 2d2f40e0208b1bdbe8faff0816a01b4ad63c49e1 Mon Sep 17 00:00:00 2001 From: Chinmay Garde Date: Tue, 7 Dec 2021 12:24:34 -0800 Subject: [PATCH 233/433] Fix broken test. --- impeller/renderer/renderer_unittests.cc | 71 +------------------------ 1 file changed, 2 insertions(+), 69 deletions(-) diff --git a/impeller/renderer/renderer_unittests.cc b/impeller/renderer/renderer_unittests.cc index c7b59436052d6..57933170d1a54 100644 --- a/impeller/renderer/renderer_unittests.cc +++ b/impeller/renderer/renderer_unittests.cc @@ -225,6 +225,8 @@ TEST_F(RendererTest, CanRenderToTexture) { TextureDescriptor stencil_texture_desc; stencil_texture_desc.size = texture_descriptor.size; stencil_texture_desc.format = PixelFormat::kD32FloatS8UNormInt; + stencil_texture_desc.usage = + static_cast(TextureUsage::kRenderTarget); stencil0.texture = context->GetPermanentsAllocator()->CreateTexture( StorageMode::kDeviceTransient, stencil_texture_desc); @@ -264,74 +266,5 @@ TEST_F(RendererTest, CanRenderToTexture) { ASSERT_TRUE(r2t_pass->EncodeCommands(*context->GetTransientsAllocator())); } -TEST_F(RendererTest, CanRenderPath) { - auto path = PathBuilder{}.AddCircle({550, 550}, 500).CreatePath(); - ASSERT_FALSE(path.GetBoundingBox().has_value()); - - using BoxPipeline = PipelineT; - using VS = BoxFadeVertexShader; - using FS = BoxFadeFragmentShader; - - BoxPipeline box_pipeline(*GetContext()); - - // Vertex buffer. - VertexBufferBuilder vertex_builder; - vertex_builder.SetLabel("Box"); - - Tessellator tessellator; - ASSERT_TRUE(tessellator.Tessellate( - path.CreatePolyline({}), [&vertex_builder](Point point) { - VS::PerVertexData vtx; - vtx.vertex_position = {point.x, point.y, 0.0}; - vtx.texture_coordinates = {0.5, 0.5}; - vertex_builder.AppendVertex(vtx); - })); - - auto context = GetContext(); - - auto vertex_buffer = - vertex_builder.CreateVertexBuffer(*context->GetPermanentsAllocator()); - ASSERT_TRUE(vertex_buffer); - - auto bridge = CreateTextureForFixture("bay_bridge.jpg"); - auto boston = CreateTextureForFixture("boston.jpg"); - ASSERT_TRUE(bridge && boston); - auto sampler = context->GetSamplerLibrary()->GetSampler({}); - ASSERT_TRUE(sampler); - - Renderer::RenderCallback callback = [&](RenderPass& pass) { - Command cmd; - cmd.label = "Box"; - cmd.pipeline = box_pipeline.WaitAndGet(); - - cmd.BindVertices(vertex_buffer); - - FS::FrameInfo frame_info; - frame_info.current_time = fml::TimePoint::Now().ToEpochDelta().ToSecondsF(); - frame_info.cursor_position = GetCursorPosition(); - frame_info.window_size.x = GetWindowSize().width; - frame_info.window_size.y = GetWindowSize().height; - - FS::BindFrameInfo(cmd, - pass.GetTransientsBuffer().EmplaceUniform(frame_info)); - FS::BindContents1(cmd, boston, sampler); - FS::BindContents2(cmd, bridge, sampler); - - cmd.primitive_type = PrimitiveType::kTriangle; - cmd.winding = tessellator.GetFrontFaceWinding(); - - VS::UniformBuffer uniforms; - uniforms.mvp = Matrix::MakeOrthographic(pass.GetRenderTargetSize()); - VS::BindUniformBuffer(cmd, - pass.GetTransientsBuffer().EmplaceUniform(uniforms)); - if (!pass.AddCommand(cmd)) { - return false; - } - - return true; - }; - // OpenPlaygroundHere(callback); -} - } // namespace testing } // namespace impeller From 699077236d801b3721f080f792630515437035da Mon Sep 17 00:00:00 2001 From: Chinmay Garde Date: Wed, 8 Dec 2021 14:08:22 -0800 Subject: [PATCH 234/433] Support non-uniform rounding radii. --- impeller/aiks/aiks_unittests.cc | 21 ++++++ impeller/geometry/path_builder.cc | 117 ++++++++++++++++-------------- impeller/geometry/path_builder.h | 41 ++++++----- impeller/geometry/point.h | 6 +- 4 files changed, 110 insertions(+), 75 deletions(-) diff --git a/impeller/aiks/aiks_unittests.cc b/impeller/aiks/aiks_unittests.cc index 0aacead3f3bd8..bac170f9cdc71 100644 --- a/impeller/aiks/aiks_unittests.cc +++ b/impeller/aiks/aiks_unittests.cc @@ -224,5 +224,26 @@ TEST_F(AiksTest, ASSERT_TRUE(OpenPlaygroundHere(canvas.EndRecordingAsPicture())); } +TEST_F(AiksTest, CanRenderRoundedRectWithNonUniformRadii) { + Canvas canvas; + + Paint paint; + paint.color = Color::Red(); + + PathBuilder::RoundingRadii radii; + radii.top_left = {50, 25}; + radii.top_right = {25, 50}; + radii.bottom_right = {50, 25}; + radii.bottom_left = {25, 50}; + + auto path = PathBuilder{} + .AddRoundedRect(Rect{100, 100, 500, 500}, radii) + .CreatePath(); + + canvas.DrawPath(path, paint); + + ASSERT_TRUE(OpenPlaygroundHere(canvas.EndRecordingAsPicture())); +} + } // namespace testing } // namespace impeller diff --git a/impeller/geometry/path_builder.cc b/impeller/geometry/path_builder.cc index 94062420dd3d4..2524c4276291b 100644 --- a/impeller/geometry/path_builder.cc +++ b/impeller/geometry/path_builder.cc @@ -163,92 +163,97 @@ PathBuilder& PathBuilder::AddCircle(const Point& c, Scalar r) { } PathBuilder& PathBuilder::AddRoundedRect(Rect rect, Scalar radius) { - return radius == 0.0 ? AddRect(rect) + return radius <= 0.0 ? AddRect(rect) : AddRoundedRect(rect, {radius, radius, radius, radius}); } PathBuilder& PathBuilder::AddRoundedRect(Rect rect, RoundingRadii radii) { - current_ = rect.origin + Point{radii.topLeft, 0.0}; + if (radii.AreAllZero()) { + return AddRect(rect); + } + + current_ = rect.origin + Point{radii.top_left.x, 0.0}; - const Scalar magic_top_right = kArcApproximationMagic * radii.topRight; - const Scalar magic_bottom_right = kArcApproximationMagic * radii.bottomRight; - const Scalar magic_bottom_left = kArcApproximationMagic * radii.bottomLeft; - const Scalar magic_top_left = kArcApproximationMagic * radii.topLeft; + const auto magic_top_right = radii.top_right * kArcApproximationMagic; + const auto magic_bottom_right = radii.bottom_right * kArcApproximationMagic; + const auto magic_bottom_left = radii.bottom_left * kArcApproximationMagic; + const auto magic_top_left = radii.top_left * kArcApproximationMagic; //---------------------------------------------------------------------------- - /// Top line. - /// + // Top line. + // prototype_.AddLinearComponent( - {rect.origin.x + radii.topLeft, rect.origin.y}, - {rect.origin.x + rect.size.width - radii.topRight, rect.origin.y}); + {rect.origin.x + radii.top_left.x, rect.origin.y}, + {rect.origin.x + rect.size.width - radii.top_right.x, rect.origin.y}); //---------------------------------------------------------------------------- - /// Top right arc. - /// + // Top right arc. + // prototype_.AddCubicComponent( - {rect.origin.x + rect.size.width - radii.topRight, rect.origin.y}, - {rect.origin.x + rect.size.width - radii.topRight + magic_top_right, + {rect.origin.x + rect.size.width - radii.top_right.x, rect.origin.y}, + {rect.origin.x + rect.size.width - radii.top_right.x + magic_top_right.x, rect.origin.y}, {rect.origin.x + rect.size.width, - rect.origin.y + radii.topRight - magic_top_right}, - {rect.origin.x + rect.size.width, rect.origin.y + radii.topRight}); + rect.origin.y + radii.top_right.y - magic_top_right.y}, + {rect.origin.x + rect.size.width, rect.origin.y + radii.top_right.y}); //---------------------------------------------------------------------------- - /// Right line. - /// + // Right line. + // prototype_.AddLinearComponent( - {rect.origin.x + rect.size.width, rect.origin.y + radii.topRight}, + {rect.origin.x + rect.size.width, rect.origin.y + radii.top_right.y}, {rect.origin.x + rect.size.width, - rect.origin.y + rect.size.height - radii.bottomRight}); + rect.origin.y + rect.size.height - radii.bottom_right.y}); //---------------------------------------------------------------------------- - /// Bottom right arc. - /// + // Bottom right arc. + // prototype_.AddCubicComponent( {rect.origin.x + rect.size.width, - rect.origin.y + rect.size.height - radii.bottomRight}, + rect.origin.y + rect.size.height - radii.bottom_right.y}, {rect.origin.x + rect.size.width, rect.origin.y + rect.size.height - - radii.bottomRight + - magic_bottom_right}, - {rect.origin.x + rect.size.width - radii.bottomRight + magic_bottom_right, + radii.bottom_right.y + + magic_bottom_right.y}, + {rect.origin.x + rect.size.width - radii.bottom_right.x + + magic_bottom_right.x, rect.origin.y + rect.size.height}, - {rect.origin.x + rect.size.width - radii.bottomRight, + {rect.origin.x + rect.size.width - radii.bottom_right.x, rect.origin.y + rect.size.height}); //---------------------------------------------------------------------------- - /// Bottom line. - /// + // Bottom line. + // prototype_.AddLinearComponent( - {rect.origin.x + rect.size.width - radii.bottomRight, + {rect.origin.x + rect.size.width - radii.bottom_right.x, rect.origin.y + rect.size.height}, - {rect.origin.x + radii.bottomLeft, rect.origin.y + rect.size.height}); + {rect.origin.x + radii.bottom_left.x, rect.origin.y + rect.size.height}); //---------------------------------------------------------------------------- - /// Bottom left arc. - /// + // Bottom left arc. + // prototype_.AddCubicComponent( - {rect.origin.x + radii.bottomLeft, rect.origin.y + rect.size.height}, - {rect.origin.x + radii.bottomLeft - magic_bottom_left, + {rect.origin.x + radii.bottom_left.x, rect.origin.y + rect.size.height}, + {rect.origin.x + radii.bottom_left.x - magic_bottom_left.x, rect.origin.y + rect.size.height}, - {rect.origin.x, - rect.origin.y + rect.size.height - radii.bottomLeft + magic_bottom_left}, - {rect.origin.x, rect.origin.y + rect.size.height - radii.bottomLeft}); + {rect.origin.x, rect.origin.y + rect.size.height - radii.bottom_left.y + + magic_bottom_left.y}, + {rect.origin.x, rect.origin.y + rect.size.height - radii.bottom_left.y}); //---------------------------------------------------------------------------- - /// Left line. - /// + // Left line. + // prototype_.AddLinearComponent( - {rect.origin.x, rect.origin.y + rect.size.height - radii.bottomLeft}, - {rect.origin.x, rect.origin.y + radii.topLeft}); + {rect.origin.x, rect.origin.y + rect.size.height - radii.bottom_left.y}, + {rect.origin.x, rect.origin.y + radii.top_left.y}); //---------------------------------------------------------------------------- - /// Top left arc. - /// + // Top left arc. + // prototype_.AddCubicComponent( - {rect.origin.x, rect.origin.y + radii.topLeft}, - {rect.origin.x, rect.origin.y + radii.topLeft - magic_top_left}, - {rect.origin.x + radii.topLeft - magic_top_left, rect.origin.y}, - {rect.origin.x + radii.topLeft, rect.origin.y}); + {rect.origin.x, rect.origin.y + radii.top_left.y}, + {rect.origin.x, rect.origin.y + radii.top_left.y - magic_top_left.y}, + {rect.origin.x + radii.top_left.x - magic_top_left.x, rect.origin.y}, + {rect.origin.x + radii.top_left.x, rect.origin.y}); return *this; } @@ -260,8 +265,8 @@ PathBuilder& PathBuilder::AddOval(const Rect& container) { const Point m = {kArcApproximationMagic * r.x, kArcApproximationMagic * r.y}; //---------------------------------------------------------------------------- - /// Top right arc. - /// + // Top right arc. + // prototype_.AddCubicComponent({c.x, c.y - r.y}, // p1 {c.x + m.x, c.y - r.y}, // cp1 {c.x + r.x, c.y - m.y}, // cp2 @@ -269,8 +274,8 @@ PathBuilder& PathBuilder::AddOval(const Rect& container) { ); //---------------------------------------------------------------------------- - /// Bottom right arc. - /// + // Bottom right arc. + // prototype_.AddCubicComponent({c.x + r.x, c.y}, // p1 {c.x + r.x, c.y + m.y}, // cp1 {c.x + m.x, c.y + r.y}, // cp2 @@ -278,8 +283,8 @@ PathBuilder& PathBuilder::AddOval(const Rect& container) { ); //---------------------------------------------------------------------------- - /// Bottom left arc. - /// + // Bottom left arc. + // prototype_.AddCubicComponent({c.x, c.y + r.y}, // p1 {c.x - m.x, c.y + r.y}, // cp1 {c.x - r.x, c.y + m.y}, // cp2 @@ -287,8 +292,8 @@ PathBuilder& PathBuilder::AddOval(const Rect& container) { ); //---------------------------------------------------------------------------- - /// Top left arc. - /// + // Top left arc. + // prototype_.AddCubicComponent({c.x - r.x, c.y}, // p1 {c.x - r.x, c.y - m.y}, // cp1 {c.x - m.x, c.y - r.y}, // cp2 diff --git a/impeller/geometry/path_builder.h b/impeller/geometry/path_builder.h index 84057fa79d33c..13ebf6ab13068 100644 --- a/impeller/geometry/path_builder.h +++ b/impeller/geometry/path_builder.h @@ -48,8 +48,6 @@ class PathBuilder { PathBuilder& AddRect(Rect rect); - PathBuilder& AddRoundedRect(Rect rect, Scalar radius); - PathBuilder& AddCircle(const Point& center, Scalar radius); PathBuilder& AddOval(const Rect& rect); @@ -57,25 +55,34 @@ class PathBuilder { PathBuilder& AddLine(const Point& p1, const Point& p2); struct RoundingRadii { - Scalar topLeft = 0.0; - Scalar bottomLeft = 0.0; - Scalar topRight = 0.0; - Scalar bottomRight = 0.0; - - RoundingRadii() {} - - RoundingRadii(Scalar pTopLeft, - Scalar pBottomLeft, - Scalar pTopRight, - Scalar pBottomRight) - : topLeft(pTopLeft), - bottomLeft(pBottomLeft), - topRight(pTopRight), - bottomRight(pBottomRight) {} + Point top_left; + Point bottom_left; + Point top_right; + Point bottom_right; + + RoundingRadii() = default; + + RoundingRadii(Scalar p_top_left, + Scalar p_bottom_left, + Scalar p_top_right, + Scalar p_bottom_right) + : top_left(p_top_left, p_top_left), + bottom_left(p_bottom_left, p_bottom_left), + top_right(p_top_right, p_top_right), + bottom_right(p_bottom_right, p_bottom_right) {} + + bool AreAllZero() const { + return top_left.IsZero() && // + bottom_left.IsZero() && // + top_right.IsZero() && // + bottom_right.IsZero(); + } }; PathBuilder& AddRoundedRect(Rect rect, RoundingRadii radii); + PathBuilder& AddRoundedRect(Rect rect, Scalar radius); + private: Point subpath_start_; Point current_; diff --git a/impeller/geometry/point.h b/impeller/geometry/point.h index e84338796d5e2..0d497a70f934c 100644 --- a/impeller/geometry/point.h +++ b/impeller/geometry/point.h @@ -57,7 +57,7 @@ struct TPoint { return {x - s.width, y - s.height}; } - constexpr TPoint operator*(Type scale) const { + constexpr TPoint operator*(Scalar scale) const { return {x * scale, y * scale}; } @@ -69,7 +69,7 @@ struct TPoint { return {x * s.width, y * s.height}; } - constexpr TPoint operator/(Type d) const { return {x / d, y / d}; } + constexpr TPoint operator/(Scalar d) const { return {x / d, y / d}; } constexpr TPoint operator/(const TPoint& p) const { return {x / p.x, y / p.y}; @@ -108,6 +108,8 @@ struct TPoint { } return {x / length, y / length}; } + + constexpr bool IsZero() const { return x == 0 && y == 0; } }; using Point = TPoint; From 275893675910a594516adbd52135e76bf113a44d Mon Sep 17 00:00:00 2001 From: Chinmay Garde Date: Wed, 8 Dec 2021 14:22:57 -0800 Subject: [PATCH 235/433] Stub out a display list dispatcher. --- impeller/BUILD.gn | 2 + impeller/compiler/BUILD.gn | 1 - impeller/display_list/BUILD.gn | 30 ++ .../display_list/display_list_impeller.cc | 411 ++++++++++++++++++ impeller/display_list/display_list_impeller.h | 235 ++++++++++ .../display_list/display_list_unittests.cc | 13 + 6 files changed, 691 insertions(+), 1 deletion(-) create mode 100644 impeller/display_list/BUILD.gn create mode 100644 impeller/display_list/display_list_impeller.cc create mode 100644 impeller/display_list/display_list_impeller.h create mode 100644 impeller/display_list/display_list_unittests.cc diff --git a/impeller/BUILD.gn b/impeller/BUILD.gn index e9a840fb1b102..e7db23c224dc2 100644 --- a/impeller/BUILD.gn +++ b/impeller/BUILD.gn @@ -11,6 +11,7 @@ group("impeller") { "aiks", "base", "compiler", + "display_list", "entity", "geometry", "image", @@ -25,6 +26,7 @@ executable("impeller_unittests") { "aiks:aiks_unittests", "base:base_unittests", "compiler:compiler_unittests", + "display_list:display_list_unittests", "entity:entity_unittests", "fixtures", "geometry:geometry_unittests", diff --git a/impeller/compiler/BUILD.gn b/impeller/compiler/BUILD.gn index dcef761963d5e..363377b10d5b7 100644 --- a/impeller/compiler/BUILD.gn +++ b/impeller/compiler/BUILD.gn @@ -25,7 +25,6 @@ impeller_component("compiler_lib") { "//flutter/fml", "//flutter/runtime:libdart", "//third_party/inja", - "//third_party/rapidjson", "//third_party/shaderc_flutter", "//third_party/spirv_cross_flutter", ] diff --git a/impeller/display_list/BUILD.gn b/impeller/display_list/BUILD.gn new file mode 100644 index 0000000000000..6a697baab4968 --- /dev/null +++ b/impeller/display_list/BUILD.gn @@ -0,0 +1,30 @@ +# Copyright 2013 The Flutter Authors. All rights reserved. +# Use of this source code is governed by a BSD-style license that can be +# found in the LICENSE file. + +import("//flutter/impeller/tools/impeller.gni") + +impeller_component("display_list") { + sources = [ + "display_list_impeller.cc", + "display_list_impeller.h", + ] + + deps = [ + "../aiks", + "//flutter/flow", + "//flutter/fml", + "//third_party/skia", + ] +} + +impeller_component("display_list_unittests") { + testonly = true + + sources = [ "display_list_unittests.cc" ] + + deps = [ + ":display_list", + "../playground", + ] +} diff --git a/impeller/display_list/display_list_impeller.cc b/impeller/display_list/display_list_impeller.cc new file mode 100644 index 0000000000000..68213eebba3f8 --- /dev/null +++ b/impeller/display_list/display_list_impeller.cc @@ -0,0 +1,411 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "impeller/display_list/display_list_impeller.h" + +#include "impeller/geometry/path_builder.h" + +namespace impeller { + +#define UNIMPLEMENTED \ + FML_LOG(ERROR) << "Unimplemented detail in " << __FUNCTION__; + +DisplayListImpeller::DisplayListImpeller() = default; + +DisplayListImpeller::~DisplayListImpeller() = default; + +// |flutter::Dispatcher| +void DisplayListImpeller::setAntiAlias(bool aa) { + // Nothing to do because AA is implicit. +} + +// |flutter::Dispatcher| +void DisplayListImpeller::setDither(bool dither) {} + +static Paint::Style ToStyle(SkPaint::Style style) { + switch (style) { + case SkPaint::kFill_Style: + return Paint::Style::kFill; + case SkPaint::kStroke_Style: + return Paint::Style::kStroke; + case SkPaint::kStrokeAndFill_Style: + UNIMPLEMENTED; + break; + } + return Paint::Style::kFill; +} + +// |flutter::Dispatcher| +void DisplayListImpeller::setStyle(SkPaint::Style style) { + paint_.style = ToStyle(style); +} + +// |flutter::Dispatcher| +void DisplayListImpeller::setColor(SkColor color) { + paint_.color = { + SkColorGetR(color) / 255.0f, // red + SkColorGetG(color) / 255.0f, // green + SkColorGetB(color) / 255.0f, // blue + SkColorGetA(color) / 255.0f // alpha + }; +} + +// |flutter::Dispatcher| +void DisplayListImpeller::setStrokeWidth(SkScalar width) { + paint_.stroke_width = width; +} + +// |flutter::Dispatcher| +void DisplayListImpeller::setStrokeMiter(SkScalar limit) { + UNIMPLEMENTED; +} + +// |flutter::Dispatcher| +void DisplayListImpeller::setStrokeCap(SkPaint::Cap cap) { + UNIMPLEMENTED; +} + +// |flutter::Dispatcher| +void DisplayListImpeller::setStrokeJoin(SkPaint::Join join) { + UNIMPLEMENTED; +} + +// |flutter::Dispatcher| +void DisplayListImpeller::setShader(sk_sp shader) { + UNIMPLEMENTED; +} + +// |flutter::Dispatcher| +void DisplayListImpeller::setColorFilter(sk_sp filter) { + UNIMPLEMENTED; +} + +// |flutter::Dispatcher| +void DisplayListImpeller::setInvertColors(bool invert) { + UNIMPLEMENTED; +} + +// |flutter::Dispatcher| +void DisplayListImpeller::setBlendMode(SkBlendMode mode) { + UNIMPLEMENTED; +} + +// |flutter::Dispatcher| +void DisplayListImpeller::setBlender(sk_sp blender) { + UNIMPLEMENTED; +} + +// |flutter::Dispatcher| +void DisplayListImpeller::setPathEffect(sk_sp effect) { + UNIMPLEMENTED; +} + +// |flutter::Dispatcher| +void DisplayListImpeller::setMaskFilter(sk_sp filter) { + UNIMPLEMENTED; +} + +// |flutter::Dispatcher| +void DisplayListImpeller::setMaskBlurFilter(SkBlurStyle style, SkScalar sigma) { + UNIMPLEMENTED; +} + +// |flutter::Dispatcher| +void DisplayListImpeller::setImageFilter(sk_sp filter) { + UNIMPLEMENTED; +} + +// |flutter::Dispatcher| +void DisplayListImpeller::save() { + canvas_.Save(); +} + +static std::optional ToRect(const SkRect* rect) { + if (rect == nullptr) { + return std::nullopt; + } + return Rect::MakeLTRB(rect->fLeft, rect->fTop, rect->fRight, rect->fBottom); +} + +// |flutter::Dispatcher| +void DisplayListImpeller::saveLayer(const SkRect* bounds, + bool restore_with_paint) { + canvas_.SaveLayer(restore_with_paint ? paint_ : Paint{}, ToRect(bounds)); +} + +// |flutter::Dispatcher| +void DisplayListImpeller::restore() { + canvas_.Restore(); +} + +// |flutter::Dispatcher| +void DisplayListImpeller::translate(SkScalar tx, SkScalar ty) { + canvas_.Translate({tx, ty, 0.0}); +} + +// |flutter::Dispatcher| +void DisplayListImpeller::scale(SkScalar sx, SkScalar sy) { + canvas_.Scale({sx, sy, 1.0}); +} + +// |flutter::Dispatcher| +void DisplayListImpeller::rotate(SkScalar degrees) { + canvas_.Rotate(Degrees{degrees}); +} + +// |flutter::Dispatcher| +void DisplayListImpeller::skew(SkScalar sx, SkScalar sy) { + canvas_.Skew(sx, sy); +} + +// |flutter::Dispatcher| +void DisplayListImpeller::transform2DAffine(SkScalar mxx, + SkScalar mxy, + SkScalar mxt, + SkScalar myx, + SkScalar myy, + SkScalar myt) { + // clang-format off + transformFullPerspective( + mxx, mxy, 0, mxt, + myx, myy, 0, myt, + 0 , 0, 1, 0, + 0 , 0, 0, 1 + ); + // clang-format on +} + +// |flutter::Dispatcher| +void DisplayListImpeller::transformFullPerspective(SkScalar mxx, + SkScalar mxy, + SkScalar mxz, + SkScalar mxt, + SkScalar myx, + SkScalar myy, + SkScalar myz, + SkScalar myt, + SkScalar mzx, + SkScalar mzy, + SkScalar mzz, + SkScalar mzt, + SkScalar mwx, + SkScalar mwy, + SkScalar mwz, + SkScalar mwt) { + // The order of arguments is row-major but Impeller matrices are column-major. + // clang-format off + auto xformation = Matrix{ + mxx, myx, mzx, mwx, + mxy, myy, mzy, mwy, + mxz, myz, mzz, mwz, + mxt, myt, mzt, mwt + }; + // clang-format on + canvas_.Transform(xformation); +} + +static Rect ToRect(const SkRect& rect) { + return Rect::MakeLTRB(rect.fLeft, rect.fTop, rect.fRight, rect.fBottom); +} + +// |flutter::Dispatcher| +void DisplayListImpeller::clipRect(const SkRect& rect, + SkClipOp clip_op, + bool is_aa) { + auto path = PathBuilder{}.AddRect(ToRect(rect)).CreatePath(); + canvas_.ClipPath(std::move(path)); +} + +static Point ToPoint(const SkVector& vector) { + return {vector.fX, vector.fY}; +} + +static PathBuilder::RoundingRadii ToRoundingRadii(const SkRRect& rrect) { + using Corner = SkRRect::Corner; + PathBuilder::RoundingRadii radii; + radii.bottom_left = ToPoint(rrect.radii(Corner::kLowerLeft_Corner)); + radii.bottom_right = ToPoint(rrect.radii(Corner::kLowerRight_Corner)); + radii.top_left = ToPoint(rrect.radii(Corner::kUpperLeft_Corner)); + radii.top_right = ToPoint(rrect.radii(Corner::kUpperRight_Corner)); + return radii; +} + +// |flutter::Dispatcher| +void DisplayListImpeller::clipRRect(const SkRRect& rrect, + SkClipOp clip_op, + bool is_aa) { + auto path = + PathBuilder{} + .AddRoundedRect(ToRect(rrect.getBounds()), ToRoundingRadii(rrect)) + .CreatePath(); + canvas_.ClipPath(std::move(path)); +} + +static Path ToPath(const SkPath& path) { + UNIMPLEMENTED; + return Path{}; +} + +// |flutter::Dispatcher| +void DisplayListImpeller::clipPath(const SkPath& path, + SkClipOp clip_op, + bool is_aa) { + canvas_.ClipPath(ToPath(path)); +} + +// |flutter::Dispatcher| +void DisplayListImpeller::drawColor(SkColor color, SkBlendMode mode) { + UNIMPLEMENTED; +} + +// |flutter::Dispatcher| +void DisplayListImpeller::drawPaint() { + UNIMPLEMENTED; +} + +// |flutter::Dispatcher| +void DisplayListImpeller::drawLine(const SkPoint& p0, const SkPoint& p1) { + auto path = PathBuilder{}.AddLine(ToPoint(p0), ToPoint(p1)).CreatePath(); + canvas_.DrawPath(std::move(path), paint_); +} + +// |flutter::Dispatcher| +void DisplayListImpeller::drawRect(const SkRect& rect) { + auto path = PathBuilder{}.AddRect(ToRect(rect)).CreatePath(); + canvas_.DrawPath(std::move(path), paint_); +} + +// |flutter::Dispatcher| +void DisplayListImpeller::drawOval(const SkRect& bounds) { + auto path = PathBuilder{}.AddOval(ToRect(bounds)).CreatePath(); + canvas_.DrawPath(std::move(path), paint_); +} + +// |flutter::Dispatcher| +void DisplayListImpeller::drawCircle(const SkPoint& center, SkScalar radius) { + auto path = PathBuilder{}.AddCircle(ToPoint(center), radius).CreatePath(); + canvas_.DrawPath(std::move(path), paint_); +} + +// |flutter::Dispatcher| +void DisplayListImpeller::drawRRect(const SkRRect& rrect) { + auto path = + PathBuilder{} + .AddRoundedRect(ToRect(rrect.getBounds()), ToRoundingRadii(rrect)) + .CreatePath(); + canvas_.DrawPath(std::move(path), paint_); +} + +// |flutter::Dispatcher| +void DisplayListImpeller::drawDRRect(const SkRRect& outer, + const SkRRect& inner) { + UNIMPLEMENTED; +} + +// |flutter::Dispatcher| +void DisplayListImpeller::drawPath(const SkPath& path) { + canvas_.DrawPath(ToPath(path), paint_); +} + +// |flutter::Dispatcher| +void DisplayListImpeller::drawArc(const SkRect& oval_bounds, + SkScalar start_degrees, + SkScalar sweep_degrees, + bool use_center) { + UNIMPLEMENTED; +} + +// |flutter::Dispatcher| +void DisplayListImpeller::drawPoints(SkCanvas::PointMode mode, + uint32_t count, + const SkPoint points[]) { + UNIMPLEMENTED; +} + +// |flutter::Dispatcher| +void DisplayListImpeller::drawVertices(const sk_sp vertices, + SkBlendMode mode) { + UNIMPLEMENTED; +} + +// |flutter::Dispatcher| +void DisplayListImpeller::drawImage(const sk_sp image, + const SkPoint point, + const SkSamplingOptions& sampling, + bool render_with_attributes) { + UNIMPLEMENTED; +} + +// |flutter::Dispatcher| +void DisplayListImpeller::drawImageRect( + const sk_sp image, + const SkRect& src, + const SkRect& dst, + const SkSamplingOptions& sampling, + bool render_with_attributes, + SkCanvas::SrcRectConstraint constraint) { + UNIMPLEMENTED; +} + +// |flutter::Dispatcher| +void DisplayListImpeller::drawImageNine(const sk_sp image, + const SkIRect& center, + const SkRect& dst, + SkFilterMode filter, + bool render_with_attributes) { + UNIMPLEMENTED; +} + +// |flutter::Dispatcher| +void DisplayListImpeller::drawImageLattice(const sk_sp image, + const SkCanvas::Lattice& lattice, + const SkRect& dst, + SkFilterMode filter, + bool render_with_attributes) { + UNIMPLEMENTED; +} + +// |flutter::Dispatcher| +void DisplayListImpeller::drawAtlas(const sk_sp atlas, + const SkRSXform xform[], + const SkRect tex[], + const SkColor colors[], + int count, + SkBlendMode mode, + const SkSamplingOptions& sampling, + const SkRect* cull_rect, + bool render_with_attributes) { + UNIMPLEMENTED; +} + +// |flutter::Dispatcher| +void DisplayListImpeller::drawPicture(const sk_sp picture, + const SkMatrix* matrix, + bool render_with_attributes) { + UNIMPLEMENTED; +} + +// |flutter::Dispatcher| +void DisplayListImpeller::drawDisplayList( + const sk_sp display_list) { + UNIMPLEMENTED; +} + +// |flutter::Dispatcher| +void DisplayListImpeller::drawTextBlob(const sk_sp blob, + SkScalar x, + SkScalar y) { + UNIMPLEMENTED; +} + +// |flutter::Dispatcher| +void DisplayListImpeller::drawShadow(const SkPath& path, + const SkColor color, + const SkScalar elevation, + bool transparent_occluder, + SkScalar dpr) { + UNIMPLEMENTED; +} + +} // namespace impeller diff --git a/impeller/display_list/display_list_impeller.h b/impeller/display_list/display_list_impeller.h new file mode 100644 index 0000000000000..8bea69f4cf926 --- /dev/null +++ b/impeller/display_list/display_list_impeller.h @@ -0,0 +1,235 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#pragma once + +#include "flutter/flow/display_list.h" +#include "flutter/fml/macros.h" +#include "impeller/aiks/canvas.h" +#include "impeller/aiks/paint.h" + +namespace impeller { + +class DisplayListImpeller final : public flutter::Dispatcher { + public: + DisplayListImpeller(); + + ~DisplayListImpeller(); + + // |flutter::Dispatcher| + void setAntiAlias(bool aa) override; + + // |flutter::Dispatcher| + void setDither(bool dither) override; + + // |flutter::Dispatcher| + void setStyle(SkPaint::Style style) override; + + // |flutter::Dispatcher| + void setColor(SkColor color) override; + + // |flutter::Dispatcher| + void setStrokeWidth(SkScalar width) override; + + // |flutter::Dispatcher| + void setStrokeMiter(SkScalar limit) override; + + // |flutter::Dispatcher| + void setStrokeCap(SkPaint::Cap cap) override; + + // |flutter::Dispatcher| + void setStrokeJoin(SkPaint::Join join) override; + + // |flutter::Dispatcher| + void setShader(sk_sp shader) override; + + // |flutter::Dispatcher| + void setColorFilter(sk_sp filter) override; + + // |flutter::Dispatcher| + void setInvertColors(bool invert) override; + + // |flutter::Dispatcher| + void setBlendMode(SkBlendMode mode) override; + + // |flutter::Dispatcher| + void setBlender(sk_sp blender) override; + + // |flutter::Dispatcher| + void setPathEffect(sk_sp effect) override; + + // |flutter::Dispatcher| + void setMaskFilter(sk_sp filter) override; + + // |flutter::Dispatcher| + void setMaskBlurFilter(SkBlurStyle style, SkScalar sigma) override; + + // |flutter::Dispatcher| + void setImageFilter(sk_sp filter) override; + + // |flutter::Dispatcher| + void save() override; + + // |flutter::Dispatcher| + void saveLayer(const SkRect* bounds, bool restore_with_paint) override; + + // |flutter::Dispatcher| + void restore() override; + + // |flutter::Dispatcher| + void translate(SkScalar tx, SkScalar ty) override; + + // |flutter::Dispatcher| + void scale(SkScalar sx, SkScalar sy) override; + + // |flutter::Dispatcher| + void rotate(SkScalar degrees) override; + + // |flutter::Dispatcher| + void skew(SkScalar sx, SkScalar sy) override; + + // |flutter::Dispatcher| + void transform2DAffine(SkScalar mxx, + SkScalar mxy, + SkScalar mxt, + SkScalar myx, + SkScalar myy, + SkScalar myt) override; + + // |flutter::Dispatcher| + void transformFullPerspective(SkScalar mxx, + SkScalar mxy, + SkScalar mxz, + SkScalar mxt, + SkScalar myx, + SkScalar myy, + SkScalar myz, + SkScalar myt, + SkScalar mzx, + SkScalar mzy, + SkScalar mzz, + SkScalar mzt, + SkScalar mwx, + SkScalar mwy, + SkScalar mwz, + SkScalar mwt) override; + + // |flutter::Dispatcher| + void clipRect(const SkRect& rect, SkClipOp clip_op, bool is_aa) override; + + // |flutter::Dispatcher| + void clipRRect(const SkRRect& rrect, SkClipOp clip_op, bool is_aa) override; + + // |flutter::Dispatcher| + void clipPath(const SkPath& path, SkClipOp clip_op, bool is_aa) override; + + // |flutter::Dispatcher| + void drawColor(SkColor color, SkBlendMode mode) override; + + // |flutter::Dispatcher| + void drawPaint() override; + + // |flutter::Dispatcher| + void drawLine(const SkPoint& p0, const SkPoint& p1) override; + + // |flutter::Dispatcher| + void drawRect(const SkRect& rect) override; + + // |flutter::Dispatcher| + void drawOval(const SkRect& bounds) override; + + // |flutter::Dispatcher| + void drawCircle(const SkPoint& center, SkScalar radius) override; + + // |flutter::Dispatcher| + void drawRRect(const SkRRect& rrect) override; + + // |flutter::Dispatcher| + void drawDRRect(const SkRRect& outer, const SkRRect& inner) override; + + // |flutter::Dispatcher| + void drawPath(const SkPath& path) override; + + // |flutter::Dispatcher| + void drawArc(const SkRect& oval_bounds, + SkScalar start_degrees, + SkScalar sweep_degrees, + bool use_center) override; + + // |flutter::Dispatcher| + void drawPoints(SkCanvas::PointMode mode, + uint32_t count, + const SkPoint points[]) override; + + // |flutter::Dispatcher| + void drawVertices(const sk_sp vertices, + SkBlendMode mode) override; + + // |flutter::Dispatcher| + void drawImage(const sk_sp image, + const SkPoint point, + const SkSamplingOptions& sampling, + bool render_with_attributes) override; + + // |flutter::Dispatcher| + void drawImageRect(const sk_sp image, + const SkRect& src, + const SkRect& dst, + const SkSamplingOptions& sampling, + bool render_with_attributes, + SkCanvas::SrcRectConstraint constraint) override; + + // |flutter::Dispatcher| + void drawImageNine(const sk_sp image, + const SkIRect& center, + const SkRect& dst, + SkFilterMode filter, + bool render_with_attributes) override; + + // |flutter::Dispatcher| + void drawImageLattice(const sk_sp image, + const SkCanvas::Lattice& lattice, + const SkRect& dst, + SkFilterMode filter, + bool render_with_attributes) override; + + // |flutter::Dispatcher| + void drawAtlas(const sk_sp atlas, + const SkRSXform xform[], + const SkRect tex[], + const SkColor colors[], + int count, + SkBlendMode mode, + const SkSamplingOptions& sampling, + const SkRect* cull_rect, + bool render_with_attributes) override; + + // |flutter::Dispatcher| + void drawPicture(const sk_sp picture, + const SkMatrix* matrix, + bool render_with_attributes) override; + + // |flutter::Dispatcher| + void drawDisplayList(const sk_sp display_list) override; + + // |flutter::Dispatcher| + void drawTextBlob(const sk_sp blob, + SkScalar x, + SkScalar y) override; + + // |flutter::Dispatcher| + void drawShadow(const SkPath& path, + const SkColor color, + const SkScalar elevation, + bool transparent_occluder, + SkScalar dpr) override; + + private: + Paint paint_; + Canvas canvas_; + + FML_DISALLOW_COPY_AND_ASSIGN(DisplayListImpeller); +}; + +} // namespace impeller diff --git a/impeller/display_list/display_list_unittests.cc b/impeller/display_list/display_list_unittests.cc new file mode 100644 index 0000000000000..4a1ad3f52f11c --- /dev/null +++ b/impeller/display_list/display_list_unittests.cc @@ -0,0 +1,13 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "flutter/testing/testing.h" + +namespace impeller { +namespace testing { + +TEST(DisplayListTest, CanCreateDisatcher) {} + +} // namespace testing +} // namespace impeller From a37bc061af9383cb96b05075d472f76bd5f02f32 Mon Sep 17 00:00:00 2001 From: Chinmay Garde Date: Wed, 8 Dec 2021 15:19:04 -0800 Subject: [PATCH 236/433] Support path fill types. --- impeller/aiks/aiks_unittests.cc | 41 +++++++++++++++---- impeller/aiks/canvas.cc | 8 ++-- .../display_list/display_list_impeller.cc | 14 +++---- impeller/entity/contents.cc | 33 +++++++-------- impeller/entity/entity_pass.cc | 3 +- impeller/entity/entity_unittests.cc | 2 +- impeller/geometry/geometry_unittests.cc | 2 +- impeller/geometry/path.cc | 8 ++++ impeller/geometry/path.h | 13 ++++++ impeller/geometry/path_builder.cc | 12 +++++- impeller/geometry/path_builder.h | 4 +- impeller/renderer/tessellator.cc | 24 ++++------- impeller/renderer/tessellator.h | 17 ++------ 13 files changed, 109 insertions(+), 72 deletions(-) diff --git a/impeller/aiks/aiks_unittests.cc b/impeller/aiks/aiks_unittests.cc index bac170f9cdc71..62782b05f526c 100644 --- a/impeller/aiks/aiks_unittests.cc +++ b/impeller/aiks/aiks_unittests.cc @@ -45,7 +45,7 @@ TEST_F(AiksTest, CanRenderColoredRect) { paint.color = Color::Red(); canvas.DrawPath(PathBuilder{} .AddRect(Rect::MakeXYWH(100.0, 100.0, 100.0, 100.0)) - .CreatePath(), + .TakePath(), paint); // ASSERT_TRUE(OpenPlaygroundHere(canvas.EndRecordingAsPicture())); } @@ -81,7 +81,7 @@ TEST_F(AiksTest, CanRenderStrokes) { paint.color = Color::Red(); paint.stroke_width = 20.0; paint.style = Paint::Style::kStroke; - canvas.DrawPath(PathBuilder{}.AddLine({200, 100}, {800, 100}).CreatePath(), + canvas.DrawPath(PathBuilder{}.AddLine({200, 100}, {800, 100}).TakePath(), paint); // ASSERT_TRUE(OpenPlaygroundHere(canvas.EndRecordingAsPicture())); } @@ -92,7 +92,7 @@ TEST_F(AiksTest, CanRenderCurvedStrokes) { paint.color = Color::Red(); paint.stroke_width = 25.0; paint.style = Paint::Style::kStroke; - canvas.DrawPath(PathBuilder{}.AddCircle({500, 500}, 250).CreatePath(), paint); + canvas.DrawPath(PathBuilder{}.AddCircle({500, 500}, 250).TakePath(), paint); ASSERT_TRUE(OpenPlaygroundHere(canvas.EndRecordingAsPicture())); } @@ -101,8 +101,8 @@ TEST_F(AiksTest, CanRenderClips) { Paint paint; paint.color = Color::Fuchsia(); canvas.ClipPath( - PathBuilder{}.AddRect(Rect::MakeXYWH(0, 0, 500, 500)).CreatePath()); - canvas.DrawPath(PathBuilder{}.AddCircle({500, 500}, 250).CreatePath(), paint); + PathBuilder{}.AddRect(Rect::MakeXYWH(0, 0, 500, 500)).TakePath()); + canvas.DrawPath(PathBuilder{}.AddCircle({500, 500}, 250).TakePath(), paint); ASSERT_TRUE(OpenPlaygroundHere(canvas.EndRecordingAsPicture())); } @@ -236,14 +236,39 @@ TEST_F(AiksTest, CanRenderRoundedRectWithNonUniformRadii) { radii.bottom_right = {50, 25}; radii.bottom_left = {25, 50}; - auto path = PathBuilder{} - .AddRoundedRect(Rect{100, 100, 500, 500}, radii) - .CreatePath(); + auto path = + PathBuilder{}.AddRoundedRect(Rect{100, 100, 500, 500}, radii).TakePath(); canvas.DrawPath(path, paint); ASSERT_TRUE(OpenPlaygroundHere(canvas.EndRecordingAsPicture())); } +TEST_F(AiksTest, CanRenderDifferencePaths) { + Canvas canvas; + + Paint paint; + paint.color = Color::Red(); + + PathBuilder builder; + + PathBuilder::RoundingRadii radii; + radii.top_left = {50, 25}; + radii.top_right = {25, 50}; + radii.bottom_right = {50, 25}; + radii.bottom_left = {25, 50}; + + builder.AddRoundedRect({100, 100, 200, 200}, radii); + builder.AddCircle({200, 200}, 50); + auto path = builder.TakePath(FillType::kOdd); + + canvas.DrawImage( + std::make_shared(CreateTextureForFixture("boston.jpg")), {10, 10}, + Paint{}); + canvas.DrawPath(std::move(path), paint); + + ASSERT_TRUE(OpenPlaygroundHere(canvas.EndRecordingAsPicture())); +} + } // namespace testing } // namespace impeller diff --git a/impeller/aiks/canvas.cc b/impeller/aiks/canvas.cc index 7b66187af37fe..99d7a47c58368 100644 --- a/impeller/aiks/canvas.cc +++ b/impeller/aiks/canvas.cc @@ -100,11 +100,11 @@ void Canvas::DrawPath(Path path, Paint paint) { } void Canvas::DrawRect(Rect rect, Paint paint) { - DrawPath(PathBuilder{}.AddRect(rect).CreatePath(), std::move(paint)); + DrawPath(PathBuilder{}.AddRect(rect).TakePath(), std::move(paint)); } void Canvas::DrawCircle(Point center, Scalar radius, Paint paint) { - DrawPath(PathBuilder{}.AddCircle(center, radius).CreatePath(), + DrawPath(PathBuilder{}.AddCircle(center, radius).TakePath(), std::move(paint)); } @@ -120,7 +120,7 @@ void Canvas::SaveLayer(Paint paint, std::optional bounds) { // the size of the render target that would have been allocated will be // absent. Explicitly add back a clip to reproduce that behavior. Since // clips never require a render target switch, this is a cheap operation. - ClipPath(PathBuilder{}.AddRect(bounds.value()).CreatePath()); + ClipPath(PathBuilder{}.AddRect(bounds.value()).TakePath()); } } @@ -187,7 +187,7 @@ void Canvas::DrawImageRect(std::shared_ptr image, contents->SetSourceRect(source); Entity entity; - entity.SetPath(PathBuilder{}.AddRect(dest).CreatePath()); + entity.SetPath(PathBuilder{}.AddRect(dest).TakePath()); entity.SetContents(contents); entity.SetTransformation(GetCurrentTransformation()); diff --git a/impeller/display_list/display_list_impeller.cc b/impeller/display_list/display_list_impeller.cc index 68213eebba3f8..1258b7f6f2045 100644 --- a/impeller/display_list/display_list_impeller.cc +++ b/impeller/display_list/display_list_impeller.cc @@ -213,7 +213,7 @@ static Rect ToRect(const SkRect& rect) { void DisplayListImpeller::clipRect(const SkRect& rect, SkClipOp clip_op, bool is_aa) { - auto path = PathBuilder{}.AddRect(ToRect(rect)).CreatePath(); + auto path = PathBuilder{}.AddRect(ToRect(rect)).TakePath(); canvas_.ClipPath(std::move(path)); } @@ -238,7 +238,7 @@ void DisplayListImpeller::clipRRect(const SkRRect& rrect, auto path = PathBuilder{} .AddRoundedRect(ToRect(rrect.getBounds()), ToRoundingRadii(rrect)) - .CreatePath(); + .TakePath(); canvas_.ClipPath(std::move(path)); } @@ -266,25 +266,25 @@ void DisplayListImpeller::drawPaint() { // |flutter::Dispatcher| void DisplayListImpeller::drawLine(const SkPoint& p0, const SkPoint& p1) { - auto path = PathBuilder{}.AddLine(ToPoint(p0), ToPoint(p1)).CreatePath(); + auto path = PathBuilder{}.AddLine(ToPoint(p0), ToPoint(p1)).TakePath(); canvas_.DrawPath(std::move(path), paint_); } // |flutter::Dispatcher| void DisplayListImpeller::drawRect(const SkRect& rect) { - auto path = PathBuilder{}.AddRect(ToRect(rect)).CreatePath(); + auto path = PathBuilder{}.AddRect(ToRect(rect)).TakePath(); canvas_.DrawPath(std::move(path), paint_); } // |flutter::Dispatcher| void DisplayListImpeller::drawOval(const SkRect& bounds) { - auto path = PathBuilder{}.AddOval(ToRect(bounds)).CreatePath(); + auto path = PathBuilder{}.AddOval(ToRect(bounds)).TakePath(); canvas_.DrawPath(std::move(path), paint_); } // |flutter::Dispatcher| void DisplayListImpeller::drawCircle(const SkPoint& center, SkScalar radius) { - auto path = PathBuilder{}.AddCircle(ToPoint(center), radius).CreatePath(); + auto path = PathBuilder{}.AddCircle(ToPoint(center), radius).TakePath(); canvas_.DrawPath(std::move(path), paint_); } @@ -293,7 +293,7 @@ void DisplayListImpeller::drawRRect(const SkRRect& rrect) { auto path = PathBuilder{} .AddRoundedRect(ToRect(rrect.getBounds()), ToRoundingRadii(rrect)) - .CreatePath(); + .TakePath(); canvas_.DrawPath(std::move(path), paint_); } diff --git a/impeller/entity/contents.cc b/impeller/entity/contents.cc index f2e1d7f18d27f..71ae6c2b66e55 100644 --- a/impeller/entity/contents.cc +++ b/impeller/entity/contents.cc @@ -68,12 +68,12 @@ bool LinearGradientContents::Render(const ContentRenderer& renderer, auto vertices_builder = VertexBufferBuilder(); { - auto result = Tessellator{}.Tessellate(entity.GetPath().CreatePolyline(), - [&vertices_builder](Point point) { - VS::PerVertexData vtx; - vtx.vertices = point; - vertices_builder.AppendVertex(vtx); - }); + auto result = Tessellator{entity.GetPath().GetFillType()}.Tessellate( + entity.GetPath().CreatePolyline(), [&vertices_builder](Point point) { + VS::PerVertexData vtx; + vtx.vertices = point; + vertices_builder.AppendVertex(vtx); + }); if (!result) { return false; } @@ -124,7 +124,7 @@ static VertexBuffer CreateSolidFillVertices(const Path& path, VertexBufferBuilder vtx_builder; - auto tesselation_result = Tessellator{}.Tessellate( + auto tesselation_result = Tessellator{path.GetFillType()}.Tessellate( path.CreatePolyline(), [&vtx_builder](auto point) { VS::PerVertexData vtx; vtx.vertices = point; @@ -225,15 +225,16 @@ bool TextureContents::Render(const ContentRenderer& renderer, VertexBufferBuilder vertex_builder; { - const auto tess_result = Tessellator{}.Tessellate( - entity.GetPath().CreatePolyline(), - [&vertex_builder, &coverage_rect](Point vtx) { - VS::PerVertexData data; - data.vertices = vtx; - data.texture_coords = - ((vtx - coverage_rect->origin) / coverage_rect->size); - vertex_builder.AppendVertex(data); - }); + const auto tess_result = + Tessellator{entity.GetPath().GetFillType()}.Tessellate( + entity.GetPath().CreatePolyline(), + [&vertex_builder, &coverage_rect](Point vtx) { + VS::PerVertexData data; + data.vertices = vtx; + data.texture_coords = + ((vtx - coverage_rect->origin) / coverage_rect->size); + vertex_builder.AppendVertex(data); + }); if (!tess_result) { return false; } diff --git a/impeller/entity/entity_pass.cc b/impeller/entity/entity_pass.cc index fc8734a59a91e..e0a7d1f413390 100644 --- a/impeller/entity/entity_pass.cc +++ b/impeller/entity/entity_pass.cc @@ -184,8 +184,7 @@ bool EntityPass::Render(ContentRenderer& renderer, } Entity entity; - entity.SetPath( - PathBuilder{}.AddRect(subpass_coverage.value()).CreatePath()); + entity.SetPath(PathBuilder{}.AddRect(subpass_coverage.value()).TakePath()); entity.SetContents(std::move(offscreen_texture_contents)); entity.SetStencilDepth(stencil_depth_); entity.SetTransformation(xformation_); diff --git a/impeller/entity/entity_unittests.cc b/impeller/entity/entity_unittests.cc index c76fb2d249fcc..cf3cafb47cb70 100644 --- a/impeller/entity/entity_unittests.cc +++ b/impeller/entity/entity_unittests.cc @@ -20,7 +20,7 @@ TEST_F(EntityTest, CanCreateEntity) { TEST_F(EntityTest, CanDrawRect) { Entity entity; - entity.SetPath(PathBuilder{}.AddRect({100, 100, 100, 100}).CreatePath()); + entity.SetPath(PathBuilder{}.AddRect({100, 100, 100, 100}).TakePath()); entity.SetContents(SolidColorContents::Make(Color::Red())); // ASSERT_TRUE(OpenPlaygroundHere(entity)); } diff --git a/impeller/geometry/geometry_unittests.cc b/impeller/geometry/geometry_unittests.cc index 5aec38a236ae4..fdd36133691f5 100644 --- a/impeller/geometry/geometry_unittests.cc +++ b/impeller/geometry/geometry_unittests.cc @@ -192,7 +192,7 @@ TEST(GeometryTest, BoundingBoxCubic) { TEST(GeometryTest, BoundingBoxOfCompositePathIsCorrect) { PathBuilder builder; builder.AddRoundedRect({{10, 10}, {300, 300}}, {50, 50, 50, 50}); - auto path = builder.CreatePath(); + auto path = builder.TakePath(); auto actual = path.GetBoundingBox(); Rect expected(10, 10, 300, 300); ASSERT_TRUE(actual.has_value()); diff --git a/impeller/geometry/path.cc b/impeller/geometry/path.cc index 1c66461bea0c8..5bbf70b184a03 100644 --- a/impeller/geometry/path.cc +++ b/impeller/geometry/path.cc @@ -16,6 +16,14 @@ size_t Path::GetComponentCount() const { return components_.size(); } +void Path::SetFillType(FillType fill) { + fill_ = fill; +} + +FillType Path::GetFillType() const { + return fill_; +} + Path& Path::AddLinearComponent(Point p1, Point p2) { linears_.emplace_back(p1, p2); components_.emplace_back(ComponentType::kLinear, linears_.size() - 1); diff --git a/impeller/geometry/path.h b/impeller/geometry/path.h index 2abd72d08e5d3..e14f057e7f5d5 100644 --- a/impeller/geometry/path.h +++ b/impeller/geometry/path.h @@ -12,6 +12,14 @@ namespace impeller { +enum class FillType { + kNonZero, // The default winding order. + kOdd, + kPositive, + kNegative, + kAbsGeqTwo, +}; + //------------------------------------------------------------------------------ /// @brief Paths are lightweight objects that describe a collection of /// linear, quadratic, or cubic segments. @@ -36,6 +44,10 @@ class Path { size_t GetComponentCount() const; + void SetFillType(FillType fill); + + FillType GetFillType() const; + Path& AddLinearComponent(Point p1, Point p2); Path& AddQuadraticComponent(Point p1, Point cp, Point p2); @@ -82,6 +94,7 @@ class Path { : type(aType), index(aIndex) {} }; + FillType fill_ = FillType::kNonZero; std::vector components_; std::vector linears_; std::vector quads_; diff --git a/impeller/geometry/path_builder.cc b/impeller/geometry/path_builder.cc index 2524c4276291b..8cd4b29048f08 100644 --- a/impeller/geometry/path_builder.cc +++ b/impeller/geometry/path_builder.cc @@ -12,8 +12,16 @@ PathBuilder::PathBuilder() = default; PathBuilder::~PathBuilder() = default; -Path PathBuilder::CreatePath() const { - return prototype_; +Path PathBuilder::CopyPath(FillType fill) const { + auto path = prototype_; + path.SetFillType(fill); + return path; +} + +Path PathBuilder::TakePath(FillType fill) { + auto path = std::move(prototype_); + path.SetFillType(fill); + return path; } PathBuilder& PathBuilder::MoveTo(Point point, bool relative) { diff --git a/impeller/geometry/path_builder.h b/impeller/geometry/path_builder.h index 13ebf6ab13068..7687d889e8f57 100644 --- a/impeller/geometry/path_builder.h +++ b/impeller/geometry/path_builder.h @@ -17,7 +17,9 @@ class PathBuilder { ~PathBuilder(); - Path CreatePath() const; + Path CopyPath(FillType fill = FillType::kNonZero) const; + + Path TakePath(FillType fill = FillType::kNonZero); const Path& GetCurrentPath() const; diff --git a/impeller/renderer/tessellator.cc b/impeller/renderer/tessellator.cc index 3f7960f7f4ddf..9cc5d4ebba0cd 100644 --- a/impeller/renderer/tessellator.cc +++ b/impeller/renderer/tessellator.cc @@ -8,29 +8,21 @@ namespace impeller { -Tessellator::Tessellator() {} +Tessellator::Tessellator(FillType type) : fill_type_(type) {} -Tessellator::~Tessellator() {} +Tessellator::~Tessellator() = default; -void Tessellator::SetFillType(FillType winding) { - fill_type_ = winding; -} - -Tessellator::FillType Tessellator::GetFillType() const { - return fill_type_; -} - -static int ToTessWindingRule(Tessellator::FillType fill_type) { +static int ToTessWindingRule(FillType fill_type) { switch (fill_type) { - case Tessellator::FillType::kOdd: + case FillType::kOdd: return TESS_WINDING_ODD; - case Tessellator::FillType::kNonZero: + case FillType::kNonZero: return TESS_WINDING_NONZERO; - case Tessellator::FillType::kPositive: + case FillType::kPositive: return TESS_WINDING_POSITIVE; - case Tessellator::FillType::kNegative: + case FillType::kNegative: return TESS_WINDING_NEGATIVE; - case Tessellator::FillType::kAbsGeqTwo: + case FillType::kAbsGeqTwo: return TESS_WINDING_ABS_GEQ_TWO; } return TESS_WINDING_ODD; diff --git a/impeller/renderer/tessellator.h b/impeller/renderer/tessellator.h index 0ca17608b61f5..ee27d924748e2 100644 --- a/impeller/renderer/tessellator.h +++ b/impeller/renderer/tessellator.h @@ -8,6 +8,7 @@ #include #include "flutter/fml/macros.h" +#include "impeller/geometry/path.h" #include "impeller/geometry/point.h" #include "impeller/renderer/formats.h" @@ -21,22 +22,10 @@ namespace impeller { /// class Tessellator { public: - enum class FillType { - kNonZero, // The default winding order. - kOdd, - kPositive, - kNegative, - kAbsGeqTwo, - }; - - Tessellator(); + explicit Tessellator(FillType type); ~Tessellator(); - void SetFillType(FillType winding); - - FillType GetFillType() const; - WindingOrder GetFrontFaceWinding() const; using VertexCallback = std::function; @@ -53,7 +42,7 @@ class Tessellator { VertexCallback callback) const; private: - FillType fill_type_ = FillType::kNonZero; + const FillType fill_type_ = FillType::kNonZero; FML_DISALLOW_COPY_AND_ASSIGN(Tessellator); }; From 9e0df789f14a3dee120c063a83730fa7f8117240 Mon Sep 17 00:00:00 2001 From: Chinmay Garde Date: Wed, 8 Dec 2021 16:28:56 -0800 Subject: [PATCH 237/433] Add support for path composition in path builder. --- .../display_list/display_list_impeller.cc | 33 ++++++++++--------- impeller/geometry/path.cc | 17 ++++------ impeller/geometry/path_builder.cc | 14 ++++++++ impeller/geometry/path_builder.h | 2 ++ 4 files changed, 40 insertions(+), 26 deletions(-) diff --git a/impeller/display_list/display_list_impeller.cc b/impeller/display_list/display_list_impeller.cc index 1258b7f6f2045..b83d7e3469f03 100644 --- a/impeller/display_list/display_list_impeller.cc +++ b/impeller/display_list/display_list_impeller.cc @@ -231,20 +231,22 @@ static PathBuilder::RoundingRadii ToRoundingRadii(const SkRRect& rrect) { return radii; } +static Path ToPath(const SkPath& path) { + UNIMPLEMENTED; + return Path{}; +} + +static Path ToPath(const SkRRect& rrect) { + return PathBuilder{} + .AddRoundedRect(ToRect(rrect.getBounds()), ToRoundingRadii(rrect)) + .TakePath(); +} + // |flutter::Dispatcher| void DisplayListImpeller::clipRRect(const SkRRect& rrect, SkClipOp clip_op, bool is_aa) { - auto path = - PathBuilder{} - .AddRoundedRect(ToRect(rrect.getBounds()), ToRoundingRadii(rrect)) - .TakePath(); - canvas_.ClipPath(std::move(path)); -} - -static Path ToPath(const SkPath& path) { - UNIMPLEMENTED; - return Path{}; + canvas_.ClipPath(ToPath(rrect)); } // |flutter::Dispatcher| @@ -290,17 +292,16 @@ void DisplayListImpeller::drawCircle(const SkPoint& center, SkScalar radius) { // |flutter::Dispatcher| void DisplayListImpeller::drawRRect(const SkRRect& rrect) { - auto path = - PathBuilder{} - .AddRoundedRect(ToRect(rrect.getBounds()), ToRoundingRadii(rrect)) - .TakePath(); - canvas_.DrawPath(std::move(path), paint_); + canvas_.DrawPath(ToPath(rrect), paint_); } // |flutter::Dispatcher| void DisplayListImpeller::drawDRRect(const SkRRect& outer, const SkRRect& inner) { - UNIMPLEMENTED; + PathBuilder builder; + builder.AddPath(ToPath(outer)); + builder.AddPath(ToPath(inner)); + canvas_.DrawPath(builder.TakePath(FillType::kOdd), paint_); } // |flutter::Dispatcher| diff --git a/impeller/geometry/path.cc b/impeller/geometry/path.cc index 5bbf70b184a03..a9e09e5b837c8 100644 --- a/impeller/geometry/path.cc +++ b/impeller/geometry/path.cc @@ -155,26 +155,23 @@ bool Path::UpdateCubicComponentAtIndex(size_t index, return true; } -static void AddPoints(std::vector& dest, const std::vector& src) { - dest.reserve(dest.size() + src.size()); - dest.insert(dest.end(), src.begin(), src.end()); -} - std::vector Path::CreatePolyline( const SmoothingApproximation& approximation) const { std::vector points; + auto collect_points = [&points](const std::vector& collection) { + points.reserve(points.size() + collection.size()); + points.insert(points.end(), collection.begin(), collection.end()); + }; for (const auto& component : components_) { switch (component.type) { case ComponentType::kLinear: - AddPoints(points, linears_[component.index].CreatePolyline()); + collect_points(linears_[component.index].CreatePolyline()); break; case ComponentType::kQuadratic: - AddPoints(points, - quads_[component.index].CreatePolyline(approximation)); + collect_points(quads_[component.index].CreatePolyline(approximation)); break; case ComponentType::kCubic: - AddPoints(points, - cubics_[component.index].CreatePolyline(approximation)); + collect_points(cubics_[component.index].CreatePolyline(approximation)); break; } } diff --git a/impeller/geometry/path_builder.cc b/impeller/geometry/path_builder.cc index 8cd4b29048f08..4c7d8a341aa7c 100644 --- a/impeller/geometry/path_builder.cc +++ b/impeller/geometry/path_builder.cc @@ -320,4 +320,18 @@ const Path& PathBuilder::GetCurrentPath() const { return prototype_; } +PathBuilder& PathBuilder::AddPath(const Path& path) { + auto linear = [&](size_t index, const LinearPathComponent& l) { + prototype_.AddLinearComponent(l.p1, l.p2); + }; + auto quadratic = [&](size_t index, const QuadraticPathComponent& q) { + prototype_.AddQuadraticComponent(q.p1, q.cp, q.p2); + }; + auto cubic = [&](size_t index, const CubicPathComponent& c) { + prototype_.AddCubicComponent(c.p1, c.cp1, c.cp2, c.p2); + }; + path.EnumerateComponents(linear, quadratic, cubic); + return *this; +} + } // namespace impeller diff --git a/impeller/geometry/path_builder.h b/impeller/geometry/path_builder.h index 7687d889e8f57..7c55299f1e900 100644 --- a/impeller/geometry/path_builder.h +++ b/impeller/geometry/path_builder.h @@ -85,6 +85,8 @@ class PathBuilder { PathBuilder& AddRoundedRect(Rect rect, Scalar radius); + PathBuilder& AddPath(const Path& path); + private: Point subpath_start_; Point current_; From 9505eea7b525bfe61a4871443afa3145f8a60bf5 Mon Sep 17 00:00:00 2001 From: Chinmay Garde Date: Wed, 8 Dec 2021 17:56:33 -0800 Subject: [PATCH 238/433] Implement conversion SkPath to Impeller paths. --- .../display_list/display_list_impeller.cc | 67 ++++++++++++++++++- impeller/geometry/path_builder.cc | 13 ++++ impeller/geometry/path_builder.h | 4 ++ 3 files changed, 82 insertions(+), 2 deletions(-) diff --git a/impeller/display_list/display_list_impeller.cc b/impeller/display_list/display_list_impeller.cc index b83d7e3469f03..442ade60c1efc 100644 --- a/impeller/display_list/display_list_impeller.cc +++ b/impeller/display_list/display_list_impeller.cc @@ -232,8 +232,71 @@ static PathBuilder::RoundingRadii ToRoundingRadii(const SkRRect& rrect) { } static Path ToPath(const SkPath& path) { - UNIMPLEMENTED; - return Path{}; + auto iterator = SkPath::Iter(path, false); + + struct PathData { + union { + SkPoint points[4]; + }; + }; + + PathBuilder builder; + PathData data; + auto verb = SkPath::Verb::kDone_Verb; + do { + verb = iterator.next(data.points); + switch (verb) { + case SkPath::kMove_Verb: + builder.MoveTo(ToPoint(data.points[0])); + break; + case SkPath::kLine_Verb: + builder.AddLine(ToPoint(data.points[0]), ToPoint(data.points[1])); + break; + case SkPath::kQuad_Verb: + builder.AddQuadraticCurve(ToPoint(data.points[0]), // p1 + ToPoint(data.points[1]), // cp + ToPoint(data.points[2]) // p2 + ); + break; + case SkPath::kConic_Verb: { + constexpr auto kPow2 = 1; // Only works for sweeps up to 90 degrees. + constexpr auto kQuadCount = 1 + (2 * (1 << kPow2)); + SkPoint points[kQuadCount]; + const auto curve_count = + SkPath::ConvertConicToQuads(data.points[0], // + data.points[1], // + data.points[2], // + iterator.conicWeight(), // + points, // + kPow2 // + ); + + for (int curve_index = 0, point_index = 0; // + curve_index < curve_count; // + curve_index++, point_index += 2 // + ) { + builder.AddQuadraticCurve(ToPoint(points[point_index + 0]), // p1 + ToPoint(points[point_index + 1]), // cp + ToPoint(points[point_index + 2]) // p2 + ); + } + } break; + case SkPath::kCubic_Verb: + builder.AddCubicCurve(ToPoint(data.points[0]), // p1 + ToPoint(data.points[1]), // cp1 + ToPoint(data.points[2]), // cp2 + ToPoint(data.points[3]) // p2 + ); + break; + case SkPath::kClose_Verb: + builder.Close(); + break; + case SkPath::kDone_Verb: + break; + } + } while (verb != SkPath::Verb::kDone_Verb); + // TODO: Convert fill types. + return builder.TakePath(); } static Path ToPath(const SkRRect& rrect) { diff --git a/impeller/geometry/path_builder.cc b/impeller/geometry/path_builder.cc index 4c7d8a341aa7c..c0278f58f368c 100644 --- a/impeller/geometry/path_builder.cc +++ b/impeller/geometry/path_builder.cc @@ -150,6 +150,19 @@ PathBuilder& PathBuilder::SmoothCubicCurveTo(Point point, return *this; } +PathBuilder& PathBuilder::AddQuadraticCurve(Point p1, Point cp, Point p2) { + prototype_.AddQuadraticComponent(p1, cp, p2); + return *this; +} + +PathBuilder& PathBuilder::AddCubicCurve(Point p1, + Point cp1, + Point cp2, + Point p2) { + prototype_.AddCubicComponent(p1, cp1, cp2, p2); + return *this; +} + PathBuilder& PathBuilder::AddRect(Rect rect) { current_ = rect.origin; diff --git a/impeller/geometry/path_builder.h b/impeller/geometry/path_builder.h index 7c55299f1e900..0e8fb32712650 100644 --- a/impeller/geometry/path_builder.h +++ b/impeller/geometry/path_builder.h @@ -56,6 +56,10 @@ class PathBuilder { PathBuilder& AddLine(const Point& p1, const Point& p2); + PathBuilder& AddQuadraticCurve(Point p1, Point cp, Point p2); + + PathBuilder& AddCubicCurve(Point p1, Point cp1, Point cp2, Point p2); + struct RoundingRadii { Point top_left; Point bottom_left; From 5e441703431c7b63f3e1be6326f7677119551463 Mon Sep 17 00:00:00 2001 From: Chinmay Garde Date: Fri, 10 Dec 2021 12:24:49 -0800 Subject: [PATCH 239/433] Import the archivist framework. --- impeller/BUILD.gn | 2 + impeller/archivist/BUILD.gn | 39 +++ impeller/archivist/archive.cc | 317 ++++++++++++++++++ impeller/archivist/archive.h | 282 ++++++++++++++++ .../archivist/archive_class_registration.cc | 150 +++++++++ .../archivist/archive_class_registration.h | 49 +++ impeller/archivist/archive_database.cc | 121 +++++++ impeller/archivist/archive_database.h | 50 +++ impeller/archivist/archive_statement.cc | 179 ++++++++++ impeller/archivist/archive_statement.h | 71 ++++ impeller/archivist/archive_transaction.cc | 52 +++ impeller/archivist/archive_transaction.h | 40 +++ impeller/archivist/archive_vector.cc | 58 ++++ impeller/archivist/archive_vector.h | 36 ++ impeller/archivist/archivist_unittests.cc | 158 +++++++++ impeller/base/allocation.h | 2 + 16 files changed, 1606 insertions(+) create mode 100644 impeller/archivist/BUILD.gn create mode 100644 impeller/archivist/archive.cc create mode 100644 impeller/archivist/archive.h create mode 100644 impeller/archivist/archive_class_registration.cc create mode 100644 impeller/archivist/archive_class_registration.h create mode 100644 impeller/archivist/archive_database.cc create mode 100644 impeller/archivist/archive_database.h create mode 100644 impeller/archivist/archive_statement.cc create mode 100644 impeller/archivist/archive_statement.h create mode 100644 impeller/archivist/archive_transaction.cc create mode 100644 impeller/archivist/archive_transaction.h create mode 100644 impeller/archivist/archive_vector.cc create mode 100644 impeller/archivist/archive_vector.h create mode 100644 impeller/archivist/archivist_unittests.cc diff --git a/impeller/BUILD.gn b/impeller/BUILD.gn index e7db23c224dc2..525b78851ccf6 100644 --- a/impeller/BUILD.gn +++ b/impeller/BUILD.gn @@ -9,6 +9,7 @@ config("impeller_public_config") { group("impeller") { public_deps = [ "aiks", + "archivist", "base", "compiler", "display_list", @@ -24,6 +25,7 @@ executable("impeller_unittests") { deps = [ "aiks:aiks_unittests", + "archivist:archivist_unittests", "base:base_unittests", "compiler:compiler_unittests", "display_list:display_list_unittests", diff --git a/impeller/archivist/BUILD.gn b/impeller/archivist/BUILD.gn new file mode 100644 index 0000000000000..4aabe3e7b81ff --- /dev/null +++ b/impeller/archivist/BUILD.gn @@ -0,0 +1,39 @@ +# Copyright 2013 The Flutter Authors. All rights reserved. +# Use of this source code is governed by a BSD-style license that can be +# found in the LICENSE file. + +import("../tools/impeller.gni") + +impeller_component("archivist") { + # Only the umbrella header is public since all other TU's are implementation + # detail that will be compiled away in release modes. + # Because they are implementation details, they may expose dependency headers. + public = [ "archive.h" ] + + sources = [ + "archive.cc", + "archive_class_registration.cc", + "archive_class_registration.h", + "archive_database.cc", + "archive_database.h", + "archive_statement.cc", + "archive_statement.h", + "archive_transaction.cc", + "archive_transaction.h", + "archive_vector.cc", + "archive_vector.h", + ] + + public_deps = [ "../base" ] + + deps = [ "//third_party/sqlite" ] +} + +impeller_component("archivist_unittests") { + testonly = true + sources = [ "archivist_unittests.cc" ] + deps = [ + ":archivist", + "//flutter/testing", + ] +} diff --git a/impeller/archivist/archive.cc b/impeller/archivist/archive.cc new file mode 100644 index 0000000000000..ad8436ed8a1e7 --- /dev/null +++ b/impeller/archivist/archive.cc @@ -0,0 +1,317 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "impeller/archivist/archive.h" + +#include + +#include "flutter/fml/logging.h" +#include "impeller/archivist/archive_class_registration.h" +#include "impeller/archivist/archive_database.h" +#include "impeller/archivist/archive_statement.h" +#include "impeller/archivist/archive_vector.h" + +namespace impeller { + +Archive::Archive(const std::string& path, bool recreate) + : _db(std::make_unique(path, recreate)) {} + +Archive::~Archive() { + FML_DCHECK(_transactionCount == 0) << "There must be no pending transactions"; +} + +bool Archive::isReady() const { + return _db->isReady(); +} + +bool Archive::archiveInstance(const ArchiveDef& definition, + const ArchiveSerializable& archivable, + int64_t& lastInsertIDOut) { + if (!isReady()) { + return false; + } + + auto transaction = _db->acquireTransaction(_transactionCount); + + const auto* registration = _db->registrationForDefinition(definition); + + if (registration == nullptr) { + return false; + } + + auto statement = registration->insertStatement(); + + if (!statement.isReady() || !statement.reset()) { + /* + * Must be able to reset the statement for a new write + */ + return false; + } + + auto itemName = archivable.archiveName(); + + /* + * The lifecycle of the archive item is tied to this scope and there is no + * way for the user to create an instance of an archive item. So its safe + * for its members to be references. It does not manage the lifetimes of + * anything. + */ + ArchiveItem item(*this, statement, *registration, itemName); + + /* + * We need to bind the primary key only if the item does not provide its own + */ + if (!definition.autoAssignName && + !statement.bind(ArchiveClassRegistration::NameIndex, itemName)) { + return false; + } + + if (!archivable.serialize(item)) { + return false; + } + + if (statement.run() != ArchiveStatement::Result::Done) { + return false; + } + + int64_t lastInsert = _db->lastInsertRowID(); + + if (!definition.autoAssignName && + lastInsert != static_cast(itemName)) { + return false; + } + + lastInsertIDOut = lastInsert; + + /* + * If any of the nested calls fail, we would have already checked for the + * failure and returned. + */ + transaction.markWritesSuccessful(); + + return true; +} + +bool Archive::unarchiveInstance(const ArchiveDef& definition, + ArchiveSerializable::ArchiveName name, + ArchiveSerializable& archivable) { + UnarchiveStep stepper = [&archivable](ArchiveItem& item) { + archivable.deserialize(item); + return false /* no-more after single read */; + }; + + return unarchiveInstances(definition, stepper, name) == 1; +} + +size_t Archive::unarchiveInstances(const ArchiveDef& definition, + Archive::UnarchiveStep stepper, + ArchiveSerializable::ArchiveName name) { + if (!isReady()) { + return 0; + } + + const auto* registration = _db->registrationForDefinition(definition); + + if (registration == nullptr) { + return 0; + } + + const bool isQueryingSingle = name != ArchiveNameAuto; + + auto statement = registration->queryStatement(isQueryingSingle); + + if (!statement.isReady() || !statement.reset()) { + return 0; + } + + if (isQueryingSingle) { + /* + * If a single statement is being queried for, bind the name as a statement + * argument. + */ + if (!statement.bind(ArchiveClassRegistration::NameIndex, name)) { + return 0; + } + } + + if (statement.columnCount() != + registration->memberCount() + 1 /* primary key */) { + return 0; + } + + /* + * Acquire a transaction but never mark it successful since we will never + * be committing any writes to the database during unarchiving. + */ + auto transaction = _db->acquireTransaction(_transactionCount); + + size_t itemsRead = 0; + + while (statement.run() == ArchiveStatement::Result::Row) { + itemsRead++; + + /* + * Prepare a fresh archive item for the given statement + */ + ArchiveItem item(*this, statement, *registration, name); + + if (!stepper(item)) { + break; + } + + if (isQueryingSingle) { + break; + } + } + + return itemsRead; +} + +ArchiveItem::ArchiveItem(Archive& context, + ArchiveStatement& statement, + const ArchiveClassRegistration& registration, + ArchiveSerializable::ArchiveName name) + : _context(context), + _statement(statement), + _registration(registration), + _name(name), + _currentClass(registration.className()) {} + +ArchiveSerializable::ArchiveName ArchiveItem::name() const { + return _name; +} + +bool ArchiveItem::encode(ArchiveSerializable::Member member, + const std::string& item) { + auto found = _registration.findColumn(_currentClass, member); + return found.second ? _statement.bind(found.first, item) : false; +} + +bool ArchiveItem::encodeIntegral(ArchiveSerializable::Member member, + int64_t item) { + auto found = _registration.findColumn(_currentClass, member); + return found.second ? _statement.bind(found.first, item) : false; +} + +bool ArchiveItem::encode(ArchiveSerializable::Member member, double item) { + auto found = _registration.findColumn(_currentClass, member); + return found.second ? _statement.bind(found.first, item) : false; +} + +bool ArchiveItem::encode(ArchiveSerializable::Member member, + const Allocation& item) { + auto found = _registration.findColumn(_currentClass, member); + return found.second ? _statement.bind(found.first, item) : false; +} + +bool ArchiveItem::encode(ArchiveSerializable::Member member, + const ArchiveDef& otherDef, + const ArchiveSerializable& other) { + auto found = _registration.findColumn(_currentClass, member); + + if (!found.second) { + return false; + } + + /* + * We need to fully archive the other instance first because it could + * have a name that is auto assigned. In that case, we cannot ask it before + * archival (via `other.archiveName()`). + */ + int64_t lastInsert = 0; + if (!_context.archiveInstance(otherDef, other, lastInsert)) { + return false; + } + + /* + * Bind the name of the serialiable + */ + if (!_statement.bind(found.first, lastInsert)) { + return false; + } + + return true; +} + +std::pair ArchiveItem::encodeVectorKeys( + std::vector&& members) { + ArchiveVector vector(std::move(members)); + int64_t vectorID = 0; + if (!_context.archiveInstance(ArchiveVector::ArchiveDefinition, // + vector, // + vectorID)) { + return {false, 0}; + } + return {true, vectorID}; +} + +bool ArchiveItem::decodeVectorKeys(ArchiveSerializable::ArchiveName name, + std::vector& members) { + ArchiveVector vector; + + if (!_context.unarchiveInstance(ArchiveVector::ArchiveDefinition, name, + vector)) { + return false; + } + + const auto& keys = vector.keys(); + + std::move(keys.begin(), keys.end(), std::back_inserter(members)); + + return true; +} + +bool ArchiveItem::decode(ArchiveSerializable::Member member, + std::string& item) { + auto found = _registration.findColumn(_currentClass, member); + return found.second ? _statement.column(found.first, item) : false; +} + +bool ArchiveItem::decodeIntegral(ArchiveSerializable::Member member, + int64_t& item) { + auto found = _registration.findColumn(_currentClass, member); + return found.second ? _statement.column(found.first, item) : false; +} + +bool ArchiveItem::decode(ArchiveSerializable::Member member, double& item) { + auto found = _registration.findColumn(_currentClass, member); + return found.second ? _statement.column(found.first, item) : false; +} + +bool ArchiveItem::decode(ArchiveSerializable::Member member, Allocation& item) { + auto found = _registration.findColumn(_currentClass, member); + return found.second ? _statement.column(found.first, item) : false; +} + +bool ArchiveItem::decode(ArchiveSerializable::Member member, + const ArchiveDef& otherDef, + ArchiveSerializable& other) { + auto found = _registration.findColumn(_currentClass, member); + + /* + * Make sure a member is present at that column + */ + if (!found.second) { + return false; + } + + /* + * Try to find the foreign key in the current items row + */ + int64_t foreignKey = 0; + if (!_statement.column(found.first, foreignKey)) { + return false; + } + + /* + * Find the other item and unarchive by this foreign key + */ + if (!_context.unarchiveInstance(otherDef, foreignKey, other)) { + return false; + } + + return true; +} + +} // namespace impeller diff --git a/impeller/archivist/archive.h b/impeller/archivist/archive.h new file mode 100644 index 0000000000000..aa3ae54989734 --- /dev/null +++ b/impeller/archivist/archive.h @@ -0,0 +1,282 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#pragma once + +#include +#include +#include +#include + +#include "flutter/fml/macros.h" +#include "impeller/base/allocation.h" + +namespace impeller { + +class ArchiveItem; +class ArchiveClassRegistration; +class ArchiveDatabase; +class ArchiveStatement; + +class ArchiveSerializable { + public: + using Member = uint64_t; + using Members = std::vector; + using ArchiveName = uint64_t; + + virtual ArchiveName archiveName() const = 0; + + virtual bool serialize(ArchiveItem& item) const = 0; + + virtual bool deserialize(ArchiveItem& item) = 0; +}; + +struct ArchiveDef { + const ArchiveDef* superClass; + const std::string className; + const bool autoAssignName; + const ArchiveSerializable::Members members; +}; + +static const ArchiveSerializable::ArchiveName ArchiveNameAuto = 0; + +class Archive { + public: + Archive(const std::string& path, bool recreate); + + ~Archive(); + + bool isReady() const; + + template < + class T, + class = std::enable_if::value>> + bool archive(const T& archivable) { + const ArchiveDef& def = T::ArchiveDefinition; + int64_t unusedLast = 0; + return archiveInstance(def, archivable, unusedLast); + } + + template < + class T, + class = std::enable_if::value>> + bool unarchive(ArchiveSerializable::ArchiveName name, T& archivable) { + const ArchiveDef& def = T::ArchiveDefinition; + return unarchiveInstance(def, name, archivable); + } + + using UnarchiveStep = std::function; + + template < + class T, + class = std::enable_if::value>> + size_t unarchive(UnarchiveStep stepper) { + const ArchiveDef& def = T::ArchiveDefinition; + return unarchiveInstances(def, stepper, ArchiveNameAuto); + } + + private: + std::unique_ptr _db; + int64_t _transactionCount = 0; + + friend class ArchiveItem; + + bool archiveInstance(const ArchiveDef& definition, + const ArchiveSerializable& archivable, + int64_t& lastInsertID); + bool unarchiveInstance(const ArchiveDef& definition, + ArchiveSerializable::ArchiveName name, + ArchiveSerializable& archivable); + size_t unarchiveInstances(const ArchiveDef& definition, + UnarchiveStep stepper, + ArchiveSerializable::ArchiveName optionalName); + + FML_DISALLOW_COPY_AND_ASSIGN(Archive); +}; + +class ArchiveItem { + public: + template ::value>> + bool encode(ArchiveSerializable::Member member, T item) { + return encodeIntegral(member, static_cast(item)); + } + + bool encode(ArchiveSerializable::Member member, double item); + + bool encode(ArchiveSerializable::Member member, const std::string& item); + + bool encode(ArchiveSerializable::Member member, const Allocation& allocation); + + template < + class T, + class = std::enable_if::value>> + bool encodeArchivable(ArchiveSerializable::Member member, const T& other) { + const ArchiveDef& otherDef = T::ArchiveDefinition; + return encode(member, otherDef, other); + } + + template ::value>> + bool encodeEnum(ArchiveSerializable::Member member, const T& item) { + return encodeIntegral(member, static_cast(item)); + } + + template < + class T, + class = std::enable_if::value>> + bool encode(ArchiveSerializable::Member member, const std::vector& items) { + /* + * All items in the vector are individually encoded and their keys noted + */ + std::vector members; + members.reserve(items.size()); + + const ArchiveDef& itemDefinition = T::ArchiveDefinition; + for (const auto& item : items) { + int64_t added = 0; + bool result = _context.archiveInstance(itemDefinition, item, added); + if (!result) { + return false; + } + members.emplace_back(added); + } + + /* + * The keys are flattened into the vectors table. Write to that table + */ + auto vectorInsert = encodeVectorKeys(std::move(members)); + + if (!vectorInsert.first) { + return false; + } + + return encodeIntegral(member, vectorInsert.second); + } + + template ::value && + std::is_base_of::value>> + bool encodeSuper(const Current& thiz) { + std::string oldClass = _currentClass; + _currentClass = Super::ArchiveDefinition.className; + auto success = thiz.Super::serialize(*this); + _currentClass = oldClass; + return success; + } + + template ::value>> + bool decode(ArchiveSerializable::Member member, T& item) { + int64_t decoded = 0; + auto result = decodeIntegral(member, decoded); + item = static_cast(decoded); + return result; + } + + bool decode(ArchiveSerializable::Member member, double& item); + + bool decode(ArchiveSerializable::Member member, std::string& item); + + bool decode(ArchiveSerializable::Member member, Allocation& allocation); + + template < + class T, + class = std::enable_if::value>> + bool decodeArchivable(ArchiveSerializable::Member member, T& other) { + const ArchiveDef& otherDef = T::ArchiveDefinition; + return decode(member, otherDef, other); + } + + template ::value>> + bool decodeEnum(ArchiveSerializable::Member member, T& item) { + int64_t desugared = 0; + if (decodeIntegral(member, desugared)) { + item = static_cast(desugared); + return true; + } + return false; + } + + template < + class T, + class = std::enable_if::value>> + bool decode(ArchiveSerializable::Member member, std::vector& items) { + /* + * From the member, find the foreign key of the vector + */ + int64_t vectorForeignKey = 0; + if (!decodeIntegral(member, vectorForeignKey)) { + return false; + } + + /* + * Get vector keys + */ + std::vector keys; + if (!decodeVectorKeys(vectorForeignKey, keys)) { + return false; + } + + const ArchiveDef& otherDef = T::ArchiveDefinition; + for (const auto& key : keys) { + items.emplace_back(); + + if (!_context.unarchiveInstance(otherDef, key, items.back())) { + return false; + } + } + + return true; + } + + template ::value && + std::is_base_of::value>> + bool decodeSuper(Current& thiz) { + std::string oldClass = _currentClass; + _currentClass = Super::ArchiveDefinition.className; + auto success = thiz.Super::deserialize(*this); + _currentClass = oldClass; + return success; + } + + ArchiveSerializable::ArchiveName name() const; + + private: + Archive& _context; + ArchiveStatement& _statement; + const ArchiveClassRegistration& _registration; + ArchiveSerializable::ArchiveName _name; + std::string _currentClass; + + friend class Archive; + + ArchiveItem(Archive& context, + ArchiveStatement& statement, + const ArchiveClassRegistration& registration, + ArchiveSerializable::ArchiveName name); + + bool encodeIntegral(ArchiveSerializable::Member member, int64_t item); + + bool decodeIntegral(ArchiveSerializable::Member member, int64_t& item); + + std::pair encodeVectorKeys(std::vector&& members); + + bool decodeVectorKeys(ArchiveSerializable::ArchiveName name, + std::vector& members); + + bool encode(ArchiveSerializable::Member member, + const ArchiveDef& otherDef, + const ArchiveSerializable& other); + + bool decode(ArchiveSerializable::Member member, + const ArchiveDef& otherDef, + ArchiveSerializable& other); + + FML_DISALLOW_COPY_AND_ASSIGN(ArchiveItem); +}; + +} // namespace impeller diff --git a/impeller/archivist/archive_class_registration.cc b/impeller/archivist/archive_class_registration.cc new file mode 100644 index 0000000000000..cc797f6202e9a --- /dev/null +++ b/impeller/archivist/archive_class_registration.cc @@ -0,0 +1,150 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "impeller/archivist/archive_class_registration.h" + +#include + +#include "impeller/archivist/archive_database.h" +#include "impeller/archivist/archive_statement.h" + +namespace impeller { + +static const char* const ArchiveColumnPrefix = "item"; +static const char* const ArchivePrimaryKeyColumnName = "name"; +static const char* const ArchiveTablePrefix = "RL_"; + +ArchiveClassRegistration::ArchiveClassRegistration(ArchiveDatabase& database, + ArchiveDef definition) + : _database(database), _className(definition.className), _memberCount(0) { + /* + * Each class in the archive class hierarchy is assigned an entry in the + * class map. + */ + const ArchiveDef* current = &definition; + size_t currentMember = 1; + while (current != nullptr) { + auto membersInCurrent = current->members.size(); + _memberCount += membersInCurrent; + MemberColumnMap map; + for (const auto& member : current->members) { + map[member] = currentMember++; + } + _classMap[current->className] = map; + current = current->superClass; + } + + _isReady = createTable(definition.autoAssignName); +} + +const std::string& ArchiveClassRegistration::className() const { + return _className; +} + +size_t ArchiveClassRegistration::memberCount() const { + return _memberCount; +} + +bool ArchiveClassRegistration::isReady() const { + return _isReady; +} + +ArchiveClassRegistration::ColumnResult ArchiveClassRegistration::findColumn( + const std::string& className, + ArchiveSerializable::Member member) const { + auto found = _classMap.find(className); + + if (found == _classMap.end()) { + return {0, false}; + } + + const auto& memberToColumns = found->second; + + auto foundColumn = memberToColumns.find(member); + + if (foundColumn == memberToColumns.end()) { + return {0, false}; + } + + return {foundColumn->second, true}; +} + +bool ArchiveClassRegistration::createTable(bool autoIncrement) { + if (_className.size() == 0 || _memberCount == 0) { + return false; + } + + std::stringstream stream; + + /* + * Table names cannot participate in parameter substitution, so we prepare + * a statement and check its validity before running. + */ + stream << "CREATE TABLE IF NOT EXISTS " << ArchiveTablePrefix + << _className.c_str() << " (" << ArchivePrimaryKeyColumnName; + + if (autoIncrement) { + stream << " INTEGER PRIMARY KEY AUTOINCREMENT, "; + } else { + stream << " INTEGER PRIMARY KEY, "; + } + for (size_t i = 0, columns = _memberCount; i < columns; i++) { + stream << ArchiveColumnPrefix << std::to_string(i + 1); + if (i != columns - 1) { + stream << ", "; + } + } + stream << ");"; + + auto statement = _database.acquireStatement(stream.str()); + + if (!statement.isReady()) { + return false; + } + + if (!statement.reset()) { + return false; + } + + return statement.run() == ArchiveStatement::Result::Done; +} + +ArchiveStatement ArchiveClassRegistration::queryStatement(bool single) const { + std::stringstream stream; + stream << "SELECT " << ArchivePrimaryKeyColumnName << ", "; + for (size_t i = 0, members = _memberCount; i < members; i++) { + stream << ArchiveColumnPrefix << std::to_string(i + 1); + if (i != members - 1) { + stream << ","; + } + } + stream << " FROM " << ArchiveTablePrefix << _className; + + if (single) { + stream << " WHERE " << ArchivePrimaryKeyColumnName << " = ?"; + } else { + stream << " ORDER BY " << ArchivePrimaryKeyColumnName << " ASC"; + } + + stream << ";"; + + return _database.acquireStatement(stream.str()); +} + +ArchiveStatement ArchiveClassRegistration::insertStatement() const { + std::stringstream stream; + stream << "INSERT OR REPLACE INTO " << ArchiveTablePrefix << _className + << " VALUES ( ?, "; + for (size_t i = 0; i < _memberCount; i++) { + stream << "?"; + if (i != _memberCount - 1) { + stream << ", "; + } + } + stream << ");"; + + return _database.acquireStatement(stream.str()); +} + +} // namespace impeller diff --git a/impeller/archivist/archive_class_registration.h b/impeller/archivist/archive_class_registration.h new file mode 100644 index 0000000000000..22b8231c1e83b --- /dev/null +++ b/impeller/archivist/archive_class_registration.h @@ -0,0 +1,49 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include + +#include "flutter/fml/macros.h" +#include "impeller/archivist/archive.h" + +namespace impeller { + +class ArchiveClassRegistration { + public: + using ColumnResult = std::pair; + ColumnResult findColumn(const std::string& className, + ArchiveSerializable::Member member) const; + + const std::string& className() const; + + size_t memberCount() const; + + bool isReady() const; + + ArchiveStatement insertStatement() const; + + ArchiveStatement queryStatement(bool single) const; + + static const size_t NameIndex = 0; + + private: + using MemberColumnMap = std::map; + using ClassMap = std::map; + + friend class ArchiveDatabase; + + ArchiveClassRegistration(ArchiveDatabase& database, ArchiveDef definition); + + bool createTable(bool autoIncrement); + + ArchiveDatabase& _database; + ClassMap _classMap; + std::string _className; + size_t _memberCount; + bool _isReady; + + FML_DISALLOW_COPY_AND_ASSIGN(ArchiveClassRegistration); +}; + +} // namespace impeller diff --git a/impeller/archivist/archive_database.cc b/impeller/archivist/archive_database.cc new file mode 100644 index 0000000000000..a99707a513bf0 --- /dev/null +++ b/impeller/archivist/archive_database.cc @@ -0,0 +1,121 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "impeller/archivist/archive_database.h" + +#include "third_party/sqlite/sqlite3.h" + +#include +#include + +#include "impeller/archivist/archive.h" +#include "impeller/archivist/archive_class_registration.h" +#include "impeller/archivist/archive_statement.h" +#include "impeller/base/validation.h" + +namespace impeller { + +#define DB_HANDLE reinterpret_cast(_db) + +ArchiveDatabase::ArchiveDatabase(const std::string& filename, bool recreate) { + if (recreate) { + ::remove(filename.c_str()); + } + + if (::sqlite3_initialize() != SQLITE_OK) { + VALIDATION_LOG << "Could not initialize sqlite."; + return; + } + + sqlite3* db = nullptr; + auto res = ::sqlite3_open(filename.c_str(), &db); + _db = db; + + if (res != SQLITE_OK || _db == nullptr) { + return; + } + + _beginTransaction = std::unique_ptr( + new ArchiveStatement(_db, "BEGIN TRANSACTION;")); + + if (!_beginTransaction->isReady()) { + return; + } + + _endTransaction = std::unique_ptr( + new ArchiveStatement(_db, "END TRANSACTION;")); + + if (!_endTransaction->isReady()) { + return; + } + + _rollbackTransaction = std::unique_ptr( + new ArchiveStatement(_db, "ROLLBACK TRANSACTION;")); + + if (!_rollbackTransaction->isReady()) { + return; + } + + _ready = true; +} + +ArchiveDatabase::~ArchiveDatabase() { + ::sqlite3_close(DB_HANDLE); +} + +bool ArchiveDatabase::isReady() const { + return _ready; +} + +int64_t ArchiveDatabase::lastInsertRowID() { + return ::sqlite3_last_insert_rowid(DB_HANDLE); +} + +static inline const ArchiveClassRegistration* RegistrationIfReady( + const ArchiveClassRegistration* registration) { + if (registration == nullptr) { + return nullptr; + } + return registration->isReady() ? registration : nullptr; +} + +const ArchiveClassRegistration* ArchiveDatabase::registrationForDefinition( + const ArchiveDef& definition) { + auto found = _registrations.find(definition.className); + if (found != _registrations.end()) { + /* + * This class has already been registered. + */ + return RegistrationIfReady(found->second.get()); + } + + /* + * Initialize a new class registration for the given class definition. + */ + auto registration = std::unique_ptr( + new ArchiveClassRegistration(*this, definition)); + auto res = + _registrations.emplace(definition.className, std::move(registration)); + + /* + * If the new class registation is ready, return it to the caller. + */ + return res.second ? RegistrationIfReady((*(res.first)).second.get()) + : nullptr; +} + +ArchiveStatement ArchiveDatabase::acquireStatement( + const std::string& statementString) const { + return ArchiveStatement{_db, statementString}; +} + +ArchiveTransaction ArchiveDatabase::acquireTransaction( + int64_t& transactionCount) { + return ArchiveTransaction{transactionCount, // + *_beginTransaction, // + *_endTransaction, // + *_rollbackTransaction}; +} + +} // namespace impeller diff --git a/impeller/archivist/archive_database.h b/impeller/archivist/archive_database.h new file mode 100644 index 0000000000000..aa368b85a90bb --- /dev/null +++ b/impeller/archivist/archive_database.h @@ -0,0 +1,50 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#pragma once + +#include +#include + +#include "flutter/fml/macros.h" +#include "impeller/archivist/archive_transaction.h" + +namespace impeller { + +class ArchiveStatement; +class ArchiveClassRegistration; +struct ArchiveDef; + +class ArchiveDatabase { + public: + ArchiveDatabase(const std::string& filename, bool recreate); + + ~ArchiveDatabase(); + + bool isReady() const; + + int64_t lastInsertRowID(); + + const ArchiveClassRegistration* registrationForDefinition( + const ArchiveDef& definition); + + ArchiveTransaction acquireTransaction(int64_t& transactionCount); + + private: + void* _db = nullptr; + bool _ready = false; + std::map> + _registrations; + std::unique_ptr _beginTransaction; + std::unique_ptr _endTransaction; + std::unique_ptr _rollbackTransaction; + + friend class ArchiveClassRegistration; + + ArchiveStatement acquireStatement(const std::string& statementString) const; + + FML_DISALLOW_COPY_AND_ASSIGN(ArchiveDatabase); +}; + +} // namespace impeller diff --git a/impeller/archivist/archive_statement.cc b/impeller/archivist/archive_statement.cc new file mode 100644 index 0000000000000..69f143580bd81 --- /dev/null +++ b/impeller/archivist/archive_statement.cc @@ -0,0 +1,179 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "impeller/archivist/archive_statement.h" + +#include + +#include "flutter/fml/logging.h" +#include "third_party/sqlite/sqlite3.h" + +namespace impeller { + +#define STATEMENT_HANDLE reinterpret_cast<::sqlite3_stmt*>(_statement) + +ArchiveStatement::ArchiveStatement(void* db, const std::string& statememt) { + ::sqlite3_stmt* statementHandle = nullptr; + auto res = ::sqlite3_prepare_v2(reinterpret_cast(db), // + statememt.c_str(), // + static_cast(statememt.size()), // + &statementHandle, // + nullptr); + _statement = statementHandle; + _ready = res == SQLITE_OK && _statement != nullptr; +} + +ArchiveStatement::ArchiveStatement(ArchiveStatement&& other) + : _statement(other._statement), _ready(other._ready) { + other._statement = nullptr; + other._ready = false; +} + +ArchiveStatement::~ArchiveStatement() { + if (_statement != nullptr) { + auto res = ::sqlite3_finalize(STATEMENT_HANDLE); + FML_CHECK(res == SQLITE_OK) << "Unable to finalize the archive."; + } +} + +bool ArchiveStatement::isReady() const { + return _ready; +} + +bool ArchiveStatement::reset() { + if (::sqlite3_reset(STATEMENT_HANDLE) != SQLITE_OK) { + return false; + } + + if (::sqlite3_clear_bindings(STATEMENT_HANDLE) != SQLITE_OK) { + return false; + } + + return true; +} + +static constexpr int ToParam(size_t index) { + /* + * sqlite parameters begin from 1 + */ + return static_cast(index + 1); +} + +static constexpr int ToColumn(size_t index) { + /* + * sqlite columns begin from 1 + */ + return static_cast(index); +} + +size_t ArchiveStatement::columnCount() { + return ::sqlite3_column_count(STATEMENT_HANDLE); +} + +/* + * Bind Variants + */ +bool ArchiveStatement::bind(size_t index, const std::string& item) { + return ::sqlite3_bind_text(STATEMENT_HANDLE, // + ToParam(index), // + item.data(), // + static_cast(item.size()), // + SQLITE_TRANSIENT) == SQLITE_OK; +} + +bool ArchiveStatement::bindIntegral(size_t index, int64_t item) { + return ::sqlite3_bind_int64(STATEMENT_HANDLE, // + ToParam(index), // + item) == SQLITE_OK; +} + +bool ArchiveStatement::bind(size_t index, double item) { + return ::sqlite3_bind_double(STATEMENT_HANDLE, // + ToParam(index), // + item) == SQLITE_OK; +} + +bool ArchiveStatement::bind(size_t index, const Allocation& item) { + return ::sqlite3_bind_blob(STATEMENT_HANDLE, // + ToParam(index), // + item.GetBuffer(), // + static_cast(item.GetLength()), // + SQLITE_TRANSIENT) == SQLITE_OK; +} + +/* + * Column Variants + */ +bool ArchiveStatement::columnIntegral(size_t index, int64_t& item) { + item = ::sqlite3_column_int64(STATEMENT_HANDLE, ToColumn(index)); + return true; +} + +bool ArchiveStatement::column(size_t index, double& item) { + item = ::sqlite3_column_double(STATEMENT_HANDLE, ToColumn(index)); + return true; +} + +/* + * For cases where byte sizes of column data is necessary, the + * recommendations in https://www.sqlite.org/c3ref/column_blob.html regarding + * type conversions are followed. + * + * TL;DR: Access blobs then bytes. + */ + +bool ArchiveStatement::column(size_t index, std::string& item) { + /* + * Get the character data + */ + auto chars = reinterpret_cast( + ::sqlite3_column_text(STATEMENT_HANDLE, ToColumn(index))); + + /* + * Get the length of the string (in bytes) + */ + size_t textByteSize = + ::sqlite3_column_bytes(STATEMENT_HANDLE, ToColumn(index)); + + std::string text(chars, textByteSize); + item.swap(text); + + return true; +} + +bool ArchiveStatement::column(size_t index, Allocation& item) { + /* + * Get a blob pointer + */ + auto blob = reinterpret_cast( + ::sqlite3_column_blob(STATEMENT_HANDLE, ToColumn(index))); + + /* + * Decode the number of bytes in the blob + */ + size_t byteSize = ::sqlite3_column_bytes(STATEMENT_HANDLE, ToColumn(index)); + + /* + * Reszie the host allocation and move the blob contents into it + */ + if (!item.Truncate(byteSize, false /* npot */)) { + return false; + } + + memmove(item.GetBuffer(), blob, byteSize); + return true; +} + +ArchiveStatement::Result ArchiveStatement::run() { + switch (::sqlite3_step(STATEMENT_HANDLE)) { + case SQLITE_DONE: + return Result::Done; + case SQLITE_ROW: + return Result::Row; + default: + return Result::Failure; + } +} + +} // namespace impeller diff --git a/impeller/archivist/archive_statement.h b/impeller/archivist/archive_statement.h new file mode 100644 index 0000000000000..0110d853b2327 --- /dev/null +++ b/impeller/archivist/archive_statement.h @@ -0,0 +1,71 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#pragma once + +#include + +#include "flutter/fml/macros.h" +#include "impeller/base/allocation.h" + +namespace impeller { + +class ArchiveStatement { + public: + ~ArchiveStatement(); + + ArchiveStatement(ArchiveStatement&& message); + + bool isReady() const; + + bool reset(); + + bool bind(size_t index, const std::string& item); + + template ::value>> + bool bind(size_t index, T item) { + return bindIntegral(index, static_cast(item)); + } + + bool bind(size_t index, double item); + + bool bind(size_t index, const Allocation& item); + + template ::value>> + bool column(size_t index, T& item) { + return columnIntegral(index, item); + } + + bool column(size_t index, double& item); + + bool column(size_t index, std::string& item); + + bool column(size_t index, Allocation& item); + + size_t columnCount(); + + enum class Result { + Done, + Row, + Failure, + }; + + Result run(); + + private: + void* _statement = nullptr; + bool _ready = false; + + friend class ArchiveDatabase; + + ArchiveStatement(void* db, const std::string& statememt); + + bool bindIntegral(size_t index, int64_t item); + + bool columnIntegral(size_t index, int64_t& item); + + FML_DISALLOW_COPY_AND_ASSIGN(ArchiveStatement); +}; + +} // namespace impeller diff --git a/impeller/archivist/archive_transaction.cc b/impeller/archivist/archive_transaction.cc new file mode 100644 index 0000000000000..6fe7f03e2521c --- /dev/null +++ b/impeller/archivist/archive_transaction.cc @@ -0,0 +1,52 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "impeller/archivist/archive_transaction.h" + +#include "flutter/fml/logging.h" +#include "impeller/archivist/archive_statement.h" + +namespace impeller { + +ArchiveTransaction::ArchiveTransaction(int64_t& transactionCount, + ArchiveStatement& beginStatement, + ArchiveStatement& endStatement, + ArchiveStatement& rollbackStatement) + : _endStatement(endStatement), + _rollbackStatement(rollbackStatement), + _transactionCount(transactionCount) { + if (_transactionCount == 0) { + _cleanup = beginStatement.run() == ArchiveStatement::Result::Done; + } + _transactionCount++; +} + +ArchiveTransaction::ArchiveTransaction(ArchiveTransaction&& other) + : _endStatement(other._endStatement), + _rollbackStatement(other._rollbackStatement), + _transactionCount(other._transactionCount), + _cleanup(other._cleanup), + _successful(other._successful) { + other._abandoned = true; +} + +ArchiveTransaction::~ArchiveTransaction() { + if (_abandoned) { + return; + } + + FML_CHECK(_transactionCount != 0); + if (_transactionCount == 1 && _cleanup) { + auto res = _successful ? _endStatement.run() : _rollbackStatement.run(); + FML_CHECK(res == ArchiveStatement::Result::Done) + << "Must be able to commit the nested transaction"; + } + _transactionCount--; +} + +void ArchiveTransaction::markWritesSuccessful() { + _successful = true; +} + +} // namespace impeller diff --git a/impeller/archivist/archive_transaction.h b/impeller/archivist/archive_transaction.h new file mode 100644 index 0000000000000..ed54c2281e7d8 --- /dev/null +++ b/impeller/archivist/archive_transaction.h @@ -0,0 +1,40 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. +#pragma once + +#include + +#include "flutter/fml/macros.h" + +namespace impeller { + +class ArchiveStatement; + +class ArchiveTransaction { + public: + ArchiveTransaction(ArchiveTransaction&& transaction); + + ~ArchiveTransaction(); + + void markWritesSuccessful(); + + private: + ArchiveStatement& _endStatement; + ArchiveStatement& _rollbackStatement; + int64_t& _transactionCount; + bool _cleanup = false; + bool _successful = false; + bool _abandoned = false; + + friend class ArchiveDatabase; + + ArchiveTransaction(int64_t& transactionCount, + ArchiveStatement& beginStatement, + ArchiveStatement& endStatement, + ArchiveStatement& rollbackStatement); + + FML_DISALLOW_COPY_AND_ASSIGN(ArchiveTransaction); +}; + +} // namespace impeller diff --git a/impeller/archivist/archive_vector.cc b/impeller/archivist/archive_vector.cc new file mode 100644 index 0000000000000..6b99cda8c843a --- /dev/null +++ b/impeller/archivist/archive_vector.cc @@ -0,0 +1,58 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "impeller/archivist/archive_vector.h" + +#include + +namespace impeller { + +ArchiveVector::ArchiveVector(std::vector&& keys) + : _keys(std::move(keys)) {} + +ArchiveVector::ArchiveVector() {} + +const ArchiveDef ArchiveVector::ArchiveDefinition = { + /* .superClass = */ nullptr, + /* .className = */ "Meta_Vector", + /* .autoAssignName = */ true, + /* .members = */ {0}, +}; + +ArchiveSerializable::ArchiveName ArchiveVector::archiveName() const { + return ArchiveNameAuto; +} + +const std::vector ArchiveVector::keys() const { + return _keys; +} + +bool ArchiveVector::serialize(ArchiveItem& item) const { + std::stringstream stream; + for (size_t i = 0, count = _keys.size(); i < count; i++) { + stream << _keys[i]; + if (i != count - 1) { + stream << ","; + } + } + return item.encode(0, stream.str()); +} + +bool ArchiveVector::deserialize(ArchiveItem& item) { + std::string flattened; + if (!item.decode(0, flattened)) { + return false; + } + + std::stringstream stream(flattened); + int64_t single = 0; + while (stream >> single) { + _keys.emplace_back(single); + stream.ignore(); + } + + return true; +} + +} // namespace impeller diff --git a/impeller/archivist/archive_vector.h b/impeller/archivist/archive_vector.h new file mode 100644 index 0000000000000..99d23c02f05c1 --- /dev/null +++ b/impeller/archivist/archive_vector.h @@ -0,0 +1,36 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#pragma once + +#include "flutter/fml/macros.h" +#include "impeller/archivist/archive.h" + +namespace impeller { + +class ArchiveVector : public ArchiveSerializable { + public: + static const ArchiveDef ArchiveDefinition; + + ArchiveName archiveName() const override; + + const std::vector keys() const; + + bool serialize(ArchiveItem& item) const override; + + bool deserialize(ArchiveItem& item) override; + + private: + std::vector _keys; + + friend class ArchiveItem; + + ArchiveVector(); + + ArchiveVector(std::vector&& keys); + + FML_DISALLOW_COPY_AND_ASSIGN(ArchiveVector); +}; + +} // namespace impeller diff --git a/impeller/archivist/archivist_unittests.cc b/impeller/archivist/archivist_unittests.cc new file mode 100644 index 0000000000000..e1e44497b967d --- /dev/null +++ b/impeller/archivist/archivist_unittests.cc @@ -0,0 +1,158 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include +#include + +#include "flutter/fml/macros.h" +#include "flutter/testing/testing.h" +#include "impeller/archivist/archive.h" + +namespace impeller { +namespace testing { + +static ArchiveSerializable::ArchiveName LastSample = 0; + +class Sample : public ArchiveSerializable { + public: + Sample(uint64_t count = 42) : _someData(count), _name(++LastSample) {} + + uint64_t someData() const { return _someData; } + + ArchiveName archiveName() const override { return _name; } + + bool serialize(ArchiveItem& item) const override { + return item.encode(999, _someData); + }; + + bool deserialize(ArchiveItem& item) override { + _name = item.name(); + return item.decode(999, _someData); + }; + + static const ArchiveDef ArchiveDefinition; + + private: + uint64_t _someData; + ArchiveName _name; + + FML_DISALLOW_COPY_AND_ASSIGN(Sample); +}; + +const ArchiveDef Sample::ArchiveDefinition = { + .superClass = nullptr, + .className = "Sample", + .autoAssignName = false, + .members = {999}, +}; + +TEST(ArchiveTest, SimpleInitialization) { + auto name = "/tmp/sample.db"; + { + Archive archive(name, true); + ASSERT_TRUE(archive.isReady()); + } + ASSERT_EQ(::remove(name), 0); +} + +TEST(ArchiveTest, AddStorageClass) { + auto name = "/tmp/sample2.db"; + { + Archive archive(name, true); + ASSERT_TRUE(archive.isReady()); + } + ASSERT_EQ(::remove(name), 0); +} + +TEST(ArchiveTest, AddData) { + auto name = "/tmp/sample3.db"; + { + Archive archive(name, true); + ASSERT_TRUE(archive.isReady()); + Sample sample; + ASSERT_TRUE(archive.archive(sample)); + } + ASSERT_EQ(::remove(name), 0); +} + +TEST(ArchiveTest, AddDataMultiple) { + auto name = "/tmp/sample4.db"; + { + Archive archive(name, true); + ASSERT_TRUE(archive.isReady()); + + for (size_t i = 0; i < 100; i++) { + Sample sample(i + 1); + ASSERT_TRUE(archive.archive(sample)); + } + } + ASSERT_EQ(::remove(name), 0); +} + +TEST(ArchiveTest, ReadData) { + auto name = "/tmp/sample5.db"; + { + Archive archive(name, true); + ASSERT_TRUE(archive.isReady()); + + size_t count = 50; + + std::vector keys; + std::vector values; + + keys.reserve(count); + values.reserve(count); + + for (size_t i = 0; i < count; i++) { + Sample sample(i + 1); + keys.push_back(sample.archiveName()); + values.push_back(sample.someData()); + ASSERT_TRUE(archive.archive(sample)); + } + + for (size_t i = 0; i < count; i++) { + Sample sample; + ASSERT_TRUE(archive.unarchive(keys[i], sample)); + ASSERT_EQ(values[i], sample.someData()); + } + } + ASSERT_EQ(::remove(name), 0); +} + +/* + * This shouldn't be slow. Need to cache compiled statements. + */ +TEST(ArchiveTest, ReadDataWithNames) { + auto name = "/tmp/sample6.db"; + { + Archive archive(name, true); + ASSERT_TRUE(archive.isReady()); + + size_t count = 8; + + std::vector keys; + std::vector values; + + keys.reserve(count); + values.reserve(count); + + for (size_t i = 0; i < count; i++) { + Sample sample(i + 1); + keys.push_back(sample.archiveName()); + values.push_back(sample.someData()); + ASSERT_TRUE(archive.archive(sample)); + } + + for (size_t i = 0; i < count; i++) { + Sample sample; + ASSERT_TRUE(archive.unarchive(keys[i], sample)); + ASSERT_EQ(values[i], sample.someData()); + ASSERT_EQ(keys[i], sample.archiveName()); + } + } + ASSERT_EQ(::remove(name), 0); +} + +} // namespace testing +} // namespace impeller diff --git a/impeller/base/allocation.h b/impeller/base/allocation.h index b4bef69baeace..f707e089298d3 100644 --- a/impeller/base/allocation.h +++ b/impeller/base/allocation.h @@ -2,6 +2,8 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. +#pragma once + #include #include From a30d15bad035b53a8c831731b20c7477a9b91b76 Mon Sep 17 00:00:00 2001 From: Chinmay Garde Date: Fri, 10 Dec 2021 13:44:38 -0800 Subject: [PATCH 240/433] Update translation units for style guide. --- impeller/archivist/BUILD.gn | 3 + impeller/archivist/archivable.cc | 11 + impeller/archivist/archivable.h | 28 +++ impeller/archivist/archive.cc | 187 ++++++++------- impeller/archivist/archive.h | 214 ++++++++---------- .../archivist/archive_class_registration.cc | 63 +++--- .../archivist/archive_class_registration.h | 28 +-- impeller/archivist/archive_database.cc | 56 ++--- impeller/archivist/archive_database.h | 22 +- impeller/archivist/archive_statement.cc | 46 ++-- impeller/archivist/archive_statement.h | 42 ++-- impeller/archivist/archive_transaction.cc | 40 ++-- impeller/archivist/archive_transaction.h | 23 +- impeller/archivist/archive_vector.cc | 20 +- impeller/archivist/archive_vector.h | 12 +- impeller/archivist/archivist_unittests.cc | 72 +++--- 16 files changed, 450 insertions(+), 417 deletions(-) create mode 100644 impeller/archivist/archivable.cc create mode 100644 impeller/archivist/archivable.h diff --git a/impeller/archivist/BUILD.gn b/impeller/archivist/BUILD.gn index 4aabe3e7b81ff..67667865232a9 100644 --- a/impeller/archivist/BUILD.gn +++ b/impeller/archivist/BUILD.gn @@ -11,7 +11,10 @@ impeller_component("archivist") { public = [ "archive.h" ] sources = [ + "archivable.cc", + "archivable.h", "archive.cc", + "archive.h", "archive_class_registration.cc", "archive_class_registration.h", "archive_database.cc", diff --git a/impeller/archivist/archivable.cc b/impeller/archivist/archivable.cc new file mode 100644 index 0000000000000..29c8a3c019a47 --- /dev/null +++ b/impeller/archivist/archivable.cc @@ -0,0 +1,11 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "impeller/archivist/archivable.h" + +namespace impeller { + +// + +} // namespace impeller diff --git a/impeller/archivist/archivable.h b/impeller/archivist/archivable.h new file mode 100644 index 0000000000000..88b766fe6466b --- /dev/null +++ b/impeller/archivist/archivable.h @@ -0,0 +1,28 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#pragma once + +#include + +namespace impeller { + +class ArchiveLocation; + +//------------------------------------------------------------------------------ +/// @brief Instances of `Archivable`s can be read from and written to a +/// persistent archive. +/// +class Archivable { + public: + using ArchiveName = uint64_t; + + virtual ArchiveName GetArchiveName() const = 0; + + virtual bool Write(ArchiveLocation& item) const = 0; + + virtual bool Read(ArchiveLocation& item) = 0; +}; + +} // namespace impeller diff --git a/impeller/archivist/archive.cc b/impeller/archivist/archive.cc index ad8436ed8a1e7..0289b648a0c1c 100644 --- a/impeller/archivist/archive.cc +++ b/impeller/archivist/archive.cc @@ -15,41 +15,43 @@ namespace impeller { Archive::Archive(const std::string& path, bool recreate) - : _db(std::make_unique(path, recreate)) {} + : database_(std::make_unique(path, recreate)) {} Archive::~Archive() { - FML_DCHECK(_transactionCount == 0) << "There must be no pending transactions"; + FML_DCHECK(transaction_count_ == 0) + << "There must be no pending transactions"; } -bool Archive::isReady() const { - return _db->isReady(); +bool Archive::IsReady() const { + return database_->IsReady(); } -bool Archive::archiveInstance(const ArchiveDef& definition, - const ArchiveSerializable& archivable, +bool Archive::ArchiveInstance(const ArchiveDef& definition, + const Archivable& archivable, int64_t& lastInsertIDOut) { - if (!isReady()) { + if (!IsReady()) { return false; } - auto transaction = _db->acquireTransaction(_transactionCount); + auto transaction = database_->CreateTransaction(transaction_count_); - const auto* registration = _db->registrationForDefinition(definition); + const auto* registration = + database_->GetRegistrationForDefinition(definition); if (registration == nullptr) { return false; } - auto statement = registration->insertStatement(); + auto statement = registration->GetInsertStatement(); - if (!statement.isReady() || !statement.reset()) { + if (!statement.IsReady() || !statement.Reset()) { /* * Must be able to reset the statement for a new write */ return false; } - auto itemName = archivable.archiveName(); + auto itemName = archivable.GetArchiveName(); /* * The lifecycle of the archive item is tied to this scope and there is no @@ -57,25 +59,25 @@ bool Archive::archiveInstance(const ArchiveDef& definition, * for its members to be references. It does not manage the lifetimes of * anything. */ - ArchiveItem item(*this, statement, *registration, itemName); + ArchiveLocation item(*this, statement, *registration, itemName); /* * We need to bind the primary key only if the item does not provide its own */ if (!definition.autoAssignName && - !statement.bind(ArchiveClassRegistration::NameIndex, itemName)) { + !statement.WriteValue(ArchiveClassRegistration::NameIndex, itemName)) { return false; } - if (!archivable.serialize(item)) { + if (!archivable.Write(item)) { return false; } - if (statement.run() != ArchiveStatement::Result::Done) { + if (statement.Run() != ArchiveStatement::Result::kDone) { return false; } - int64_t lastInsert = _db->lastInsertRowID(); + int64_t lastInsert = database_->GetLastInsertRowID(); if (!definition.autoAssignName && lastInsert != static_cast(itemName)) { @@ -88,30 +90,31 @@ bool Archive::archiveInstance(const ArchiveDef& definition, * If any of the nested calls fail, we would have already checked for the * failure and returned. */ - transaction.markWritesSuccessful(); + transaction.MarkWritesAsReadyForCommit(); return true; } -bool Archive::unarchiveInstance(const ArchiveDef& definition, - ArchiveSerializable::ArchiveName name, - ArchiveSerializable& archivable) { - UnarchiveStep stepper = [&archivable](ArchiveItem& item) { - archivable.deserialize(item); +bool Archive::UnarchiveInstance(const ArchiveDef& definition, + Archivable::ArchiveName name, + Archivable& archivable) { + UnarchiveStep stepper = [&archivable](ArchiveLocation& item) { + archivable.Read(item); return false /* no-more after single read */; }; - return unarchiveInstances(definition, stepper, name) == 1; + return UnarchiveInstances(definition, stepper, name) == 1; } -size_t Archive::unarchiveInstances(const ArchiveDef& definition, +size_t Archive::UnarchiveInstances(const ArchiveDef& definition, Archive::UnarchiveStep stepper, - ArchiveSerializable::ArchiveName name) { - if (!isReady()) { + Archivable::ArchiveName name) { + if (!IsReady()) { return 0; } - const auto* registration = _db->registrationForDefinition(definition); + const auto* registration = + database_->GetRegistrationForDefinition(definition); if (registration == nullptr) { return 0; @@ -119,9 +122,9 @@ size_t Archive::unarchiveInstances(const ArchiveDef& definition, const bool isQueryingSingle = name != ArchiveNameAuto; - auto statement = registration->queryStatement(isQueryingSingle); + auto statement = registration->GetQueryStatement(isQueryingSingle); - if (!statement.isReady() || !statement.reset()) { + if (!statement.IsReady() || !statement.Reset()) { return 0; } @@ -130,13 +133,13 @@ size_t Archive::unarchiveInstances(const ArchiveDef& definition, * If a single statement is being queried for, bind the name as a statement * argument. */ - if (!statement.bind(ArchiveClassRegistration::NameIndex, name)) { + if (!statement.WriteValue(ArchiveClassRegistration::NameIndex, name)) { return 0; } } - if (statement.columnCount() != - registration->memberCount() + 1 /* primary key */) { + if (statement.GetColumnCount() != + registration->GetMemberCount() + 1 /* primary key */) { return 0; } @@ -144,17 +147,17 @@ size_t Archive::unarchiveInstances(const ArchiveDef& definition, * Acquire a transaction but never mark it successful since we will never * be committing any writes to the database during unarchiving. */ - auto transaction = _db->acquireTransaction(_transactionCount); + auto transaction = database_->CreateTransaction(transaction_count_); size_t itemsRead = 0; - while (statement.run() == ArchiveStatement::Result::Row) { + while (statement.Run() == ArchiveStatement::Result::kRow) { itemsRead++; /* * Prepare a fresh archive item for the given statement */ - ArchiveItem item(*this, statement, *registration, name); + ArchiveLocation item(*this, statement, *registration, name); if (!stepper(item)) { break; @@ -168,47 +171,45 @@ size_t Archive::unarchiveInstances(const ArchiveDef& definition, return itemsRead; } -ArchiveItem::ArchiveItem(Archive& context, - ArchiveStatement& statement, - const ArchiveClassRegistration& registration, - ArchiveSerializable::ArchiveName name) - : _context(context), - _statement(statement), - _registration(registration), - _name(name), - _currentClass(registration.className()) {} - -ArchiveSerializable::ArchiveName ArchiveItem::name() const { - return _name; +ArchiveLocation::ArchiveLocation(Archive& context, + ArchiveStatement& statement, + const ArchiveClassRegistration& registration, + Archivable::ArchiveName name) + : context_(context), + statement_(statement), + registration_(registration), + name_(name), + current_class_(registration.GetClassName()) {} + +Archivable::ArchiveName ArchiveLocation::Name() const { + return name_; } -bool ArchiveItem::encode(ArchiveSerializable::Member member, - const std::string& item) { - auto found = _registration.findColumn(_currentClass, member); - return found.second ? _statement.bind(found.first, item) : false; +bool ArchiveLocation::Write(ArchiveDef::Member member, + const std::string& item) { + auto found = registration_.FindColumn(current_class_, member); + return found.second ? statement_.WriteValue(found.first, item) : false; } -bool ArchiveItem::encodeIntegral(ArchiveSerializable::Member member, - int64_t item) { - auto found = _registration.findColumn(_currentClass, member); - return found.second ? _statement.bind(found.first, item) : false; +bool ArchiveLocation::WriteIntegral(ArchiveDef::Member member, int64_t item) { + auto found = registration_.FindColumn(current_class_, member); + return found.second ? statement_.WriteValue(found.first, item) : false; } -bool ArchiveItem::encode(ArchiveSerializable::Member member, double item) { - auto found = _registration.findColumn(_currentClass, member); - return found.second ? _statement.bind(found.first, item) : false; +bool ArchiveLocation::Write(ArchiveDef::Member member, double item) { + auto found = registration_.FindColumn(current_class_, member); + return found.second ? statement_.WriteValue(found.first, item) : false; } -bool ArchiveItem::encode(ArchiveSerializable::Member member, - const Allocation& item) { - auto found = _registration.findColumn(_currentClass, member); - return found.second ? _statement.bind(found.first, item) : false; +bool ArchiveLocation::Write(ArchiveDef::Member member, const Allocation& item) { + auto found = registration_.FindColumn(current_class_, member); + return found.second ? statement_.WriteValue(found.first, item) : false; } -bool ArchiveItem::encode(ArchiveSerializable::Member member, - const ArchiveDef& otherDef, - const ArchiveSerializable& other) { - auto found = _registration.findColumn(_currentClass, member); +bool ArchiveLocation::Write(ArchiveDef::Member member, + const ArchiveDef& otherDef, + const Archivable& other) { + auto found = registration_.FindColumn(current_class_, member); if (!found.second) { return false; @@ -220,25 +221,25 @@ bool ArchiveItem::encode(ArchiveSerializable::Member member, * archival (via `other.archiveName()`). */ int64_t lastInsert = 0; - if (!_context.archiveInstance(otherDef, other, lastInsert)) { + if (!context_.ArchiveInstance(otherDef, other, lastInsert)) { return false; } /* * Bind the name of the serialiable */ - if (!_statement.bind(found.first, lastInsert)) { + if (!statement_.WriteValue(found.first, lastInsert)) { return false; } return true; } -std::pair ArchiveItem::encodeVectorKeys( +std::pair ArchiveLocation::WriteVectorKeys( std::vector&& members) { ArchiveVector vector(std::move(members)); int64_t vectorID = 0; - if (!_context.archiveInstance(ArchiveVector::ArchiveDefinition, // + if (!context_.ArchiveInstance(ArchiveVector::ArchiveDefinition, // vector, // vectorID)) { return {false, 0}; @@ -246,11 +247,11 @@ std::pair ArchiveItem::encodeVectorKeys( return {true, vectorID}; } -bool ArchiveItem::decodeVectorKeys(ArchiveSerializable::ArchiveName name, - std::vector& members) { +bool ArchiveLocation::ReadVectorKeys(Archivable::ArchiveName name, + std::vector& members) { ArchiveVector vector; - if (!_context.unarchiveInstance(ArchiveVector::ArchiveDefinition, name, + if (!context_.UnarchiveInstance(ArchiveVector::ArchiveDefinition, name, vector)) { return false; } @@ -262,32 +263,30 @@ bool ArchiveItem::decodeVectorKeys(ArchiveSerializable::ArchiveName name, return true; } -bool ArchiveItem::decode(ArchiveSerializable::Member member, - std::string& item) { - auto found = _registration.findColumn(_currentClass, member); - return found.second ? _statement.column(found.first, item) : false; +bool ArchiveLocation::Read(ArchiveDef::Member member, std::string& item) { + auto found = registration_.FindColumn(current_class_, member); + return found.second ? statement_.ReadValue(found.first, item) : false; } -bool ArchiveItem::decodeIntegral(ArchiveSerializable::Member member, - int64_t& item) { - auto found = _registration.findColumn(_currentClass, member); - return found.second ? _statement.column(found.first, item) : false; +bool ArchiveLocation::ReadIntegral(ArchiveDef::Member member, int64_t& item) { + auto found = registration_.FindColumn(current_class_, member); + return found.second ? statement_.ReadValue(found.first, item) : false; } -bool ArchiveItem::decode(ArchiveSerializable::Member member, double& item) { - auto found = _registration.findColumn(_currentClass, member); - return found.second ? _statement.column(found.first, item) : false; +bool ArchiveLocation::Read(ArchiveDef::Member member, double& item) { + auto found = registration_.FindColumn(current_class_, member); + return found.second ? statement_.ReadValue(found.first, item) : false; } -bool ArchiveItem::decode(ArchiveSerializable::Member member, Allocation& item) { - auto found = _registration.findColumn(_currentClass, member); - return found.second ? _statement.column(found.first, item) : false; +bool ArchiveLocation::Read(ArchiveDef::Member member, Allocation& item) { + auto found = registration_.FindColumn(current_class_, member); + return found.second ? statement_.ReadValue(found.first, item) : false; } -bool ArchiveItem::decode(ArchiveSerializable::Member member, - const ArchiveDef& otherDef, - ArchiveSerializable& other) { - auto found = _registration.findColumn(_currentClass, member); +bool ArchiveLocation::Read(ArchiveDef::Member member, + const ArchiveDef& otherDef, + Archivable& other) { + auto found = registration_.FindColumn(current_class_, member); /* * Make sure a member is present at that column @@ -300,14 +299,14 @@ bool ArchiveItem::decode(ArchiveSerializable::Member member, * Try to find the foreign key in the current items row */ int64_t foreignKey = 0; - if (!_statement.column(found.first, foreignKey)) { + if (!statement_.ReadValue(found.first, foreignKey)) { return false; } /* * Find the other item and unarchive by this foreign key */ - if (!_context.unarchiveInstance(otherDef, foreignKey, other)) { + if (!context_.UnarchiveInstance(otherDef, foreignKey, other)) { return false; } diff --git a/impeller/archivist/archive.h b/impeller/archivist/archive.h index aa3ae54989734..97ebd6593cb42 100644 --- a/impeller/archivist/archive.h +++ b/impeller/archivist/archive.h @@ -10,36 +10,27 @@ #include #include "flutter/fml/macros.h" +#include "impeller/archivist/archivable.h" #include "impeller/base/allocation.h" namespace impeller { -class ArchiveItem; +class ArchiveLocation; class ArchiveClassRegistration; class ArchiveDatabase; class ArchiveStatement; -class ArchiveSerializable { - public: +struct ArchiveDef { using Member = uint64_t; using Members = std::vector; - using ArchiveName = uint64_t; - - virtual ArchiveName archiveName() const = 0; - - virtual bool serialize(ArchiveItem& item) const = 0; - virtual bool deserialize(ArchiveItem& item) = 0; -}; - -struct ArchiveDef { const ArchiveDef* superClass; const std::string className; const bool autoAssignName; - const ArchiveSerializable::Members members; + const Members members; }; -static const ArchiveSerializable::ArchiveName ArchiveNameAuto = 0; +static const Archivable::ArchiveName ArchiveNameAuto = 0; class Archive { public: @@ -47,84 +38,79 @@ class Archive { ~Archive(); - bool isReady() const; + bool IsReady() const; - template < - class T, - class = std::enable_if::value>> - bool archive(const T& archivable) { + template ::value>> + bool Write(const T& archivable) { const ArchiveDef& def = T::ArchiveDefinition; - int64_t unusedLast = 0; - return archiveInstance(def, archivable, unusedLast); + int64_t unused_last = 0; + return ArchiveInstance(def, archivable, unused_last); } - template < - class T, - class = std::enable_if::value>> - bool unarchive(ArchiveSerializable::ArchiveName name, T& archivable) { + template ::value>> + bool Read(Archivable::ArchiveName name, T& archivable) { const ArchiveDef& def = T::ArchiveDefinition; - return unarchiveInstance(def, name, archivable); + return UnarchiveInstance(def, name, archivable); } - using UnarchiveStep = std::function; + using UnarchiveStep = std::function; - template < - class T, - class = std::enable_if::value>> - size_t unarchive(UnarchiveStep stepper) { + template ::value>> + size_t Read(UnarchiveStep stepper) { const ArchiveDef& def = T::ArchiveDefinition; - return unarchiveInstances(def, stepper, ArchiveNameAuto); + return UnarchiveInstances(def, stepper, ArchiveNameAuto); } private: - std::unique_ptr _db; - int64_t _transactionCount = 0; + std::unique_ptr database_; + int64_t transaction_count_ = 0; - friend class ArchiveItem; + friend class ArchiveLocation; - bool archiveInstance(const ArchiveDef& definition, - const ArchiveSerializable& archivable, + bool ArchiveInstance(const ArchiveDef& definition, + const Archivable& archivable, int64_t& lastInsertID); - bool unarchiveInstance(const ArchiveDef& definition, - ArchiveSerializable::ArchiveName name, - ArchiveSerializable& archivable); - size_t unarchiveInstances(const ArchiveDef& definition, + bool UnarchiveInstance(const ArchiveDef& definition, + Archivable::ArchiveName name, + Archivable& archivable); + size_t UnarchiveInstances(const ArchiveDef& definition, UnarchiveStep stepper, - ArchiveSerializable::ArchiveName optionalName); + Archivable::ArchiveName optionalName); FML_DISALLOW_COPY_AND_ASSIGN(Archive); }; -class ArchiveItem { +class ArchiveLocation { public: template ::value>> - bool encode(ArchiveSerializable::Member member, T item) { - return encodeIntegral(member, static_cast(item)); + bool Write(ArchiveDef::Member member, T item) { + return WriteIntegral(member, static_cast(item)); } - bool encode(ArchiveSerializable::Member member, double item); + bool Write(ArchiveDef::Member member, double item); - bool encode(ArchiveSerializable::Member member, const std::string& item); + bool Write(ArchiveDef::Member member, const std::string& item); - bool encode(ArchiveSerializable::Member member, const Allocation& allocation); + bool Write(ArchiveDef::Member member, const Allocation& allocation); - template < - class T, - class = std::enable_if::value>> - bool encodeArchivable(ArchiveSerializable::Member member, const T& other) { + template ::value>> + bool WriteArchivable(ArchiveDef::Member member, const T& other) { const ArchiveDef& otherDef = T::ArchiveDefinition; - return encode(member, otherDef, other); + return Write(member, otherDef, other); } template ::value>> - bool encodeEnum(ArchiveSerializable::Member member, const T& item) { - return encodeIntegral(member, static_cast(item)); + bool WriteEnum(ArchiveDef::Member member, const T& item) { + return WriteIntegral(member, static_cast(item)); } - template < - class T, - class = std::enable_if::value>> - bool encode(ArchiveSerializable::Member member, const std::vector& items) { + template ::value>> + bool Write(ArchiveDef::Member member, const std::vector& items) { /* * All items in the vector are individually encoded and their keys noted */ @@ -134,7 +120,7 @@ class ArchiveItem { const ArchiveDef& itemDefinition = T::ArchiveDefinition; for (const auto& item : items) { int64_t added = 0; - bool result = _context.archiveInstance(itemDefinition, item, added); + bool result = context_.ArchiveInstance(itemDefinition, item, added); if (!result) { return false; } @@ -144,69 +130,66 @@ class ArchiveItem { /* * The keys are flattened into the vectors table. Write to that table */ - auto vectorInsert = encodeVectorKeys(std::move(members)); + auto vectorInsert = WriteVectorKeys(std::move(members)); if (!vectorInsert.first) { return false; } - return encodeIntegral(member, vectorInsert.second); + return WriteIntegral(member, vectorInsert.second); } template ::value && - std::is_base_of::value>> - bool encodeSuper(const Current& thiz) { - std::string oldClass = _currentClass; - _currentClass = Super::ArchiveDefinition.className; + class = std::enable_if::value && + std::is_base_of::value>> + bool WriteSuper(const Current& thiz) { + std::string oldClass = current_class_; + current_class_ = Super::ArchiveDefinition.className; auto success = thiz.Super::serialize(*this); - _currentClass = oldClass; + current_class_ = oldClass; return success; } template ::value>> - bool decode(ArchiveSerializable::Member member, T& item) { + bool Read(ArchiveDef::Member member, T& item) { int64_t decoded = 0; - auto result = decodeIntegral(member, decoded); + auto result = ReadIntegral(member, decoded); item = static_cast(decoded); return result; } - bool decode(ArchiveSerializable::Member member, double& item); + bool Read(ArchiveDef::Member member, double& item); - bool decode(ArchiveSerializable::Member member, std::string& item); + bool Read(ArchiveDef::Member member, std::string& item); - bool decode(ArchiveSerializable::Member member, Allocation& allocation); + bool Read(ArchiveDef::Member member, Allocation& allocation); - template < - class T, - class = std::enable_if::value>> - bool decodeArchivable(ArchiveSerializable::Member member, T& other) { + template ::value>> + bool ReadArchivable(ArchiveDef::Member member, T& other) { const ArchiveDef& otherDef = T::ArchiveDefinition; return decode(member, otherDef, other); } template ::value>> - bool decodeEnum(ArchiveSerializable::Member member, T& item) { + bool ReadEnum(ArchiveDef::Member member, T& item) { int64_t desugared = 0; - if (decodeIntegral(member, desugared)) { + if (ReadIntegral(member, desugared)) { item = static_cast(desugared); return true; } return false; } - template < - class T, - class = std::enable_if::value>> - bool decode(ArchiveSerializable::Member member, std::vector& items) { + template ::value>> + bool Read(ArchiveDef::Member member, std::vector& items) { /* * From the member, find the foreign key of the vector */ int64_t vectorForeignKey = 0; - if (!decodeIntegral(member, vectorForeignKey)) { + if (!ReadIntegral(member, vectorForeignKey)) { return false; } @@ -214,7 +197,7 @@ class ArchiveItem { * Get vector keys */ std::vector keys; - if (!decodeVectorKeys(vectorForeignKey, keys)) { + if (!ReadVectorKeys(vectorForeignKey, keys)) { return false; } @@ -222,7 +205,7 @@ class ArchiveItem { for (const auto& key : keys) { items.emplace_back(); - if (!_context.unarchiveInstance(otherDef, key, items.back())) { + if (!context_.UnarchiveInstance(otherDef, key, items.back())) { return false; } } @@ -232,51 +215,50 @@ class ArchiveItem { template ::value && - std::is_base_of::value>> - bool decodeSuper(Current& thiz) { - std::string oldClass = _currentClass; - _currentClass = Super::ArchiveDefinition.className; + class = std::enable_if::value && + std::is_base_of::value>> + bool ReadSuper(Current& thiz) { + std::string oldClass = current_class_; + current_class_ = Super::ArchiveDefinition.className; auto success = thiz.Super::deserialize(*this); - _currentClass = oldClass; + current_class_ = oldClass; return success; } - ArchiveSerializable::ArchiveName name() const; + Archivable::ArchiveName Name() const; private: - Archive& _context; - ArchiveStatement& _statement; - const ArchiveClassRegistration& _registration; - ArchiveSerializable::ArchiveName _name; - std::string _currentClass; + Archive& context_; + ArchiveStatement& statement_; + const ArchiveClassRegistration& registration_; + Archivable::ArchiveName name_; + std::string current_class_; friend class Archive; - ArchiveItem(Archive& context, - ArchiveStatement& statement, - const ArchiveClassRegistration& registration, - ArchiveSerializable::ArchiveName name); + ArchiveLocation(Archive& context, + ArchiveStatement& statement, + const ArchiveClassRegistration& registration, + Archivable::ArchiveName name); - bool encodeIntegral(ArchiveSerializable::Member member, int64_t item); + bool WriteIntegral(ArchiveDef::Member member, int64_t item); - bool decodeIntegral(ArchiveSerializable::Member member, int64_t& item); + bool ReadIntegral(ArchiveDef::Member member, int64_t& item); - std::pair encodeVectorKeys(std::vector&& members); + std::pair WriteVectorKeys(std::vector&& members); - bool decodeVectorKeys(ArchiveSerializable::ArchiveName name, - std::vector& members); + bool ReadVectorKeys(Archivable::ArchiveName name, + std::vector& members); - bool encode(ArchiveSerializable::Member member, - const ArchiveDef& otherDef, - const ArchiveSerializable& other); + bool Write(ArchiveDef::Member member, + const ArchiveDef& otherDef, + const Archivable& other); - bool decode(ArchiveSerializable::Member member, - const ArchiveDef& otherDef, - ArchiveSerializable& other); + bool Read(ArchiveDef::Member member, + const ArchiveDef& otherDef, + Archivable& other); - FML_DISALLOW_COPY_AND_ASSIGN(ArchiveItem); + FML_DISALLOW_COPY_AND_ASSIGN(ArchiveLocation); }; } // namespace impeller diff --git a/impeller/archivist/archive_class_registration.cc b/impeller/archivist/archive_class_registration.cc index cc797f6202e9a..e67275904201e 100644 --- a/impeller/archivist/archive_class_registration.cc +++ b/impeller/archivist/archive_class_registration.cc @@ -17,7 +17,7 @@ static const char* const ArchiveTablePrefix = "RL_"; ArchiveClassRegistration::ArchiveClassRegistration(ArchiveDatabase& database, ArchiveDef definition) - : _database(database), _className(definition.className), _memberCount(0) { + : database_(database), class_name_(definition.className) { /* * Each class in the archive class hierarchy is assigned an entry in the * class map. @@ -26,36 +26,36 @@ ArchiveClassRegistration::ArchiveClassRegistration(ArchiveDatabase& database, size_t currentMember = 1; while (current != nullptr) { auto membersInCurrent = current->members.size(); - _memberCount += membersInCurrent; + member_count_ += membersInCurrent; MemberColumnMap map; for (const auto& member : current->members) { map[member] = currentMember++; } - _classMap[current->className] = map; + class_map_[current->className] = map; current = current->superClass; } - _isReady = createTable(definition.autoAssignName); + is_ready_ = CreateTable(definition.autoAssignName); } -const std::string& ArchiveClassRegistration::className() const { - return _className; +const std::string& ArchiveClassRegistration::GetClassName() const { + return class_name_; } -size_t ArchiveClassRegistration::memberCount() const { - return _memberCount; +size_t ArchiveClassRegistration::GetMemberCount() const { + return member_count_; } -bool ArchiveClassRegistration::isReady() const { - return _isReady; +bool ArchiveClassRegistration::IsReady() const { + return is_ready_; } -ArchiveClassRegistration::ColumnResult ArchiveClassRegistration::findColumn( +ArchiveClassRegistration::ColumnResult ArchiveClassRegistration::FindColumn( const std::string& className, - ArchiveSerializable::Member member) const { - auto found = _classMap.find(className); + ArchiveDef::Member member) const { + auto found = class_map_.find(className); - if (found == _classMap.end()) { + if (found == class_map_.end()) { return {0, false}; } @@ -70,8 +70,8 @@ ArchiveClassRegistration::ColumnResult ArchiveClassRegistration::findColumn( return {foundColumn->second, true}; } -bool ArchiveClassRegistration::createTable(bool autoIncrement) { - if (_className.size() == 0 || _memberCount == 0) { +bool ArchiveClassRegistration::CreateTable(bool autoIncrement) { + if (class_name_.size() == 0 || member_count_ == 0) { return false; } @@ -82,14 +82,14 @@ bool ArchiveClassRegistration::createTable(bool autoIncrement) { * a statement and check its validity before running. */ stream << "CREATE TABLE IF NOT EXISTS " << ArchiveTablePrefix - << _className.c_str() << " (" << ArchivePrimaryKeyColumnName; + << class_name_.c_str() << " (" << ArchivePrimaryKeyColumnName; if (autoIncrement) { stream << " INTEGER PRIMARY KEY AUTOINCREMENT, "; } else { stream << " INTEGER PRIMARY KEY, "; } - for (size_t i = 0, columns = _memberCount; i < columns; i++) { + for (size_t i = 0, columns = member_count_; i < columns; i++) { stream << ArchiveColumnPrefix << std::to_string(i + 1); if (i != columns - 1) { stream << ", "; @@ -97,29 +97,30 @@ bool ArchiveClassRegistration::createTable(bool autoIncrement) { } stream << ");"; - auto statement = _database.acquireStatement(stream.str()); + auto statement = database_.CreateStatement(stream.str()); - if (!statement.isReady()) { + if (!statement.IsReady()) { return false; } - if (!statement.reset()) { + if (!statement.Reset()) { return false; } - return statement.run() == ArchiveStatement::Result::Done; + return statement.Run() == ArchiveStatement::Result::kDone; } -ArchiveStatement ArchiveClassRegistration::queryStatement(bool single) const { +ArchiveStatement ArchiveClassRegistration::GetQueryStatement( + bool single) const { std::stringstream stream; stream << "SELECT " << ArchivePrimaryKeyColumnName << ", "; - for (size_t i = 0, members = _memberCount; i < members; i++) { + for (size_t i = 0, members = member_count_; i < members; i++) { stream << ArchiveColumnPrefix << std::to_string(i + 1); if (i != members - 1) { stream << ","; } } - stream << " FROM " << ArchiveTablePrefix << _className; + stream << " FROM " << ArchiveTablePrefix << class_name_; if (single) { stream << " WHERE " << ArchivePrimaryKeyColumnName << " = ?"; @@ -129,22 +130,22 @@ ArchiveStatement ArchiveClassRegistration::queryStatement(bool single) const { stream << ";"; - return _database.acquireStatement(stream.str()); + return database_.CreateStatement(stream.str()); } -ArchiveStatement ArchiveClassRegistration::insertStatement() const { +ArchiveStatement ArchiveClassRegistration::GetInsertStatement() const { std::stringstream stream; - stream << "INSERT OR REPLACE INTO " << ArchiveTablePrefix << _className + stream << "INSERT OR REPLACE INTO " << ArchiveTablePrefix << class_name_ << " VALUES ( ?, "; - for (size_t i = 0; i < _memberCount; i++) { + for (size_t i = 0; i < member_count_; i++) { stream << "?"; - if (i != _memberCount - 1) { + if (i != member_count_ - 1) { stream << ", "; } } stream << ");"; - return _database.acquireStatement(stream.str()); + return database_.CreateStatement(stream.str()); } } // namespace impeller diff --git a/impeller/archivist/archive_class_registration.h b/impeller/archivist/archive_class_registration.h index 22b8231c1e83b..94bfddf8bd7fd 100644 --- a/impeller/archivist/archive_class_registration.h +++ b/impeller/archivist/archive_class_registration.h @@ -12,36 +12,36 @@ namespace impeller { class ArchiveClassRegistration { public: using ColumnResult = std::pair; - ColumnResult findColumn(const std::string& className, - ArchiveSerializable::Member member) const; + ColumnResult FindColumn(const std::string& className, + ArchiveDef::Member member) const; - const std::string& className() const; + const std::string& GetClassName() const; - size_t memberCount() const; + size_t GetMemberCount() const; - bool isReady() const; + bool IsReady() const; - ArchiveStatement insertStatement() const; + ArchiveStatement GetInsertStatement() const; - ArchiveStatement queryStatement(bool single) const; + ArchiveStatement GetQueryStatement(bool single) const; static const size_t NameIndex = 0; private: - using MemberColumnMap = std::map; + using MemberColumnMap = std::map; using ClassMap = std::map; friend class ArchiveDatabase; ArchiveClassRegistration(ArchiveDatabase& database, ArchiveDef definition); - bool createTable(bool autoIncrement); + bool CreateTable(bool autoIncrement); - ArchiveDatabase& _database; - ClassMap _classMap; - std::string _className; - size_t _memberCount; - bool _isReady; + ArchiveDatabase& database_; + ClassMap class_map_; + std::string class_name_; + size_t member_count_ = 0; + bool is_ready_ = false; FML_DISALLOW_COPY_AND_ASSIGN(ArchiveClassRegistration); }; diff --git a/impeller/archivist/archive_database.cc b/impeller/archivist/archive_database.cc index a99707a513bf0..75b13d7e55603 100644 --- a/impeller/archivist/archive_database.cc +++ b/impeller/archivist/archive_database.cc @@ -16,7 +16,7 @@ namespace impeller { -#define DB_HANDLE reinterpret_cast(_db) +#define DB_HANDLE reinterpret_cast(database_) ArchiveDatabase::ArchiveDatabase(const std::string& filename, bool recreate) { if (recreate) { @@ -30,45 +30,45 @@ ArchiveDatabase::ArchiveDatabase(const std::string& filename, bool recreate) { sqlite3* db = nullptr; auto res = ::sqlite3_open(filename.c_str(), &db); - _db = db; + database_ = db; - if (res != SQLITE_OK || _db == nullptr) { + if (res != SQLITE_OK || database_ == nullptr) { return; } - _beginTransaction = std::unique_ptr( - new ArchiveStatement(_db, "BEGIN TRANSACTION;")); + begin_transaction_stmt_ = std::unique_ptr( + new ArchiveStatement(database_, "BEGIN TRANSACTION;")); - if (!_beginTransaction->isReady()) { + if (!begin_transaction_stmt_->IsReady()) { return; } - _endTransaction = std::unique_ptr( - new ArchiveStatement(_db, "END TRANSACTION;")); + end_transaction_stmt_ = std::unique_ptr( + new ArchiveStatement(database_, "END TRANSACTION;")); - if (!_endTransaction->isReady()) { + if (!end_transaction_stmt_->IsReady()) { return; } - _rollbackTransaction = std::unique_ptr( - new ArchiveStatement(_db, "ROLLBACK TRANSACTION;")); + rollback_transaction_stmt_ = std::unique_ptr( + new ArchiveStatement(database_, "ROLLBACK TRANSACTION;")); - if (!_rollbackTransaction->isReady()) { + if (!rollback_transaction_stmt_->IsReady()) { return; } - _ready = true; + ready_ = true; } ArchiveDatabase::~ArchiveDatabase() { ::sqlite3_close(DB_HANDLE); } -bool ArchiveDatabase::isReady() const { - return _ready; +bool ArchiveDatabase::IsReady() const { + return ready_; } -int64_t ArchiveDatabase::lastInsertRowID() { +int64_t ArchiveDatabase::GetLastInsertRowID() { return ::sqlite3_last_insert_rowid(DB_HANDLE); } @@ -77,13 +77,13 @@ static inline const ArchiveClassRegistration* RegistrationIfReady( if (registration == nullptr) { return nullptr; } - return registration->isReady() ? registration : nullptr; + return registration->IsReady() ? registration : nullptr; } -const ArchiveClassRegistration* ArchiveDatabase::registrationForDefinition( +const ArchiveClassRegistration* ArchiveDatabase::GetRegistrationForDefinition( const ArchiveDef& definition) { - auto found = _registrations.find(definition.className); - if (found != _registrations.end()) { + auto found = registrations_.find(definition.className); + if (found != registrations_.end()) { /* * This class has already been registered. */ @@ -96,7 +96,7 @@ const ArchiveClassRegistration* ArchiveDatabase::registrationForDefinition( auto registration = std::unique_ptr( new ArchiveClassRegistration(*this, definition)); auto res = - _registrations.emplace(definition.className, std::move(registration)); + registrations_.emplace(definition.className, std::move(registration)); /* * If the new class registation is ready, return it to the caller. @@ -105,17 +105,17 @@ const ArchiveClassRegistration* ArchiveDatabase::registrationForDefinition( : nullptr; } -ArchiveStatement ArchiveDatabase::acquireStatement( +ArchiveStatement ArchiveDatabase::CreateStatement( const std::string& statementString) const { - return ArchiveStatement{_db, statementString}; + return ArchiveStatement{database_, statementString}; } -ArchiveTransaction ArchiveDatabase::acquireTransaction( +ArchiveTransaction ArchiveDatabase::CreateTransaction( int64_t& transactionCount) { - return ArchiveTransaction{transactionCount, // - *_beginTransaction, // - *_endTransaction, // - *_rollbackTransaction}; + return ArchiveTransaction{transactionCount, // + *begin_transaction_stmt_, // + *end_transaction_stmt_, // + *rollback_transaction_stmt_}; } } // namespace impeller diff --git a/impeller/archivist/archive_database.h b/impeller/archivist/archive_database.h index aa368b85a90bb..6092c3e231e23 100644 --- a/impeller/archivist/archive_database.h +++ b/impeller/archivist/archive_database.h @@ -22,27 +22,27 @@ class ArchiveDatabase { ~ArchiveDatabase(); - bool isReady() const; + bool IsReady() const; - int64_t lastInsertRowID(); + int64_t GetLastInsertRowID(); - const ArchiveClassRegistration* registrationForDefinition( + const ArchiveClassRegistration* GetRegistrationForDefinition( const ArchiveDef& definition); - ArchiveTransaction acquireTransaction(int64_t& transactionCount); + ArchiveTransaction CreateTransaction(int64_t& transactionCount); private: - void* _db = nullptr; - bool _ready = false; + void* database_ = nullptr; + bool ready_ = false; std::map> - _registrations; - std::unique_ptr _beginTransaction; - std::unique_ptr _endTransaction; - std::unique_ptr _rollbackTransaction; + registrations_; + std::unique_ptr begin_transaction_stmt_; + std::unique_ptr end_transaction_stmt_; + std::unique_ptr rollback_transaction_stmt_; friend class ArchiveClassRegistration; - ArchiveStatement acquireStatement(const std::string& statementString) const; + ArchiveStatement CreateStatement(const std::string& statementString) const; FML_DISALLOW_COPY_AND_ASSIGN(ArchiveDatabase); }; diff --git a/impeller/archivist/archive_statement.cc b/impeller/archivist/archive_statement.cc index 69f143580bd81..de4bec087aa5e 100644 --- a/impeller/archivist/archive_statement.cc +++ b/impeller/archivist/archive_statement.cc @@ -11,7 +11,7 @@ namespace impeller { -#define STATEMENT_HANDLE reinterpret_cast<::sqlite3_stmt*>(_statement) +#define STATEMENT_HANDLE reinterpret_cast<::sqlite3_stmt*>(statement_handle_) ArchiveStatement::ArchiveStatement(void* db, const std::string& statememt) { ::sqlite3_stmt* statementHandle = nullptr; @@ -20,28 +20,28 @@ ArchiveStatement::ArchiveStatement(void* db, const std::string& statememt) { static_cast(statememt.size()), // &statementHandle, // nullptr); - _statement = statementHandle; - _ready = res == SQLITE_OK && _statement != nullptr; + statement_handle_ = statementHandle; + ready_ = res == SQLITE_OK && statement_handle_ != nullptr; } ArchiveStatement::ArchiveStatement(ArchiveStatement&& other) - : _statement(other._statement), _ready(other._ready) { - other._statement = nullptr; - other._ready = false; + : statement_handle_(other.statement_handle_), ready_(other.ready_) { + other.statement_handle_ = nullptr; + other.ready_ = false; } ArchiveStatement::~ArchiveStatement() { - if (_statement != nullptr) { + if (statement_handle_ != nullptr) { auto res = ::sqlite3_finalize(STATEMENT_HANDLE); FML_CHECK(res == SQLITE_OK) << "Unable to finalize the archive."; } } -bool ArchiveStatement::isReady() const { - return _ready; +bool ArchiveStatement::IsReady() const { + return ready_; } -bool ArchiveStatement::reset() { +bool ArchiveStatement::Reset() { if (::sqlite3_reset(STATEMENT_HANDLE) != SQLITE_OK) { return false; } @@ -67,14 +67,14 @@ static constexpr int ToColumn(size_t index) { return static_cast(index); } -size_t ArchiveStatement::columnCount() { +size_t ArchiveStatement::GetColumnCount() { return ::sqlite3_column_count(STATEMENT_HANDLE); } /* * Bind Variants */ -bool ArchiveStatement::bind(size_t index, const std::string& item) { +bool ArchiveStatement::WriteValue(size_t index, const std::string& item) { return ::sqlite3_bind_text(STATEMENT_HANDLE, // ToParam(index), // item.data(), // @@ -82,19 +82,19 @@ bool ArchiveStatement::bind(size_t index, const std::string& item) { SQLITE_TRANSIENT) == SQLITE_OK; } -bool ArchiveStatement::bindIntegral(size_t index, int64_t item) { +bool ArchiveStatement::BindIntegral(size_t index, int64_t item) { return ::sqlite3_bind_int64(STATEMENT_HANDLE, // ToParam(index), // item) == SQLITE_OK; } -bool ArchiveStatement::bind(size_t index, double item) { +bool ArchiveStatement::WriteValue(size_t index, double item) { return ::sqlite3_bind_double(STATEMENT_HANDLE, // ToParam(index), // item) == SQLITE_OK; } -bool ArchiveStatement::bind(size_t index, const Allocation& item) { +bool ArchiveStatement::WriteValue(size_t index, const Allocation& item) { return ::sqlite3_bind_blob(STATEMENT_HANDLE, // ToParam(index), // item.GetBuffer(), // @@ -105,12 +105,12 @@ bool ArchiveStatement::bind(size_t index, const Allocation& item) { /* * Column Variants */ -bool ArchiveStatement::columnIntegral(size_t index, int64_t& item) { +bool ArchiveStatement::ColumnIntegral(size_t index, int64_t& item) { item = ::sqlite3_column_int64(STATEMENT_HANDLE, ToColumn(index)); return true; } -bool ArchiveStatement::column(size_t index, double& item) { +bool ArchiveStatement::ReadValue(size_t index, double& item) { item = ::sqlite3_column_double(STATEMENT_HANDLE, ToColumn(index)); return true; } @@ -123,7 +123,7 @@ bool ArchiveStatement::column(size_t index, double& item) { * TL;DR: Access blobs then bytes. */ -bool ArchiveStatement::column(size_t index, std::string& item) { +bool ArchiveStatement::ReadValue(size_t index, std::string& item) { /* * Get the character data */ @@ -142,7 +142,7 @@ bool ArchiveStatement::column(size_t index, std::string& item) { return true; } -bool ArchiveStatement::column(size_t index, Allocation& item) { +bool ArchiveStatement::ReadValue(size_t index, Allocation& item) { /* * Get a blob pointer */ @@ -165,14 +165,14 @@ bool ArchiveStatement::column(size_t index, Allocation& item) { return true; } -ArchiveStatement::Result ArchiveStatement::run() { +ArchiveStatement::Result ArchiveStatement::Run() { switch (::sqlite3_step(STATEMENT_HANDLE)) { case SQLITE_DONE: - return Result::Done; + return Result::kDone; case SQLITE_ROW: - return Result::Row; + return Result::kRow; default: - return Result::Failure; + return Result::kFailure; } } diff --git a/impeller/archivist/archive_statement.h b/impeller/archivist/archive_statement.h index 0110d853b2327..d60be1b4be677 100644 --- a/impeller/archivist/archive_statement.h +++ b/impeller/archivist/archive_statement.h @@ -17,53 +17,53 @@ class ArchiveStatement { ArchiveStatement(ArchiveStatement&& message); - bool isReady() const; + bool IsReady() const; - bool reset(); + bool Reset(); - bool bind(size_t index, const std::string& item); + bool WriteValue(size_t index, const std::string& item); template ::value>> - bool bind(size_t index, T item) { - return bindIntegral(index, static_cast(item)); + bool WriteValue(size_t index, T item) { + return BindIntegral(index, static_cast(item)); } - bool bind(size_t index, double item); + bool WriteValue(size_t index, double item); - bool bind(size_t index, const Allocation& item); + bool WriteValue(size_t index, const Allocation& item); template ::value>> - bool column(size_t index, T& item) { - return columnIntegral(index, item); + bool ReadValue(size_t index, T& item) { + return ColumnIntegral(index, item); } - bool column(size_t index, double& item); + bool ReadValue(size_t index, double& item); - bool column(size_t index, std::string& item); + bool ReadValue(size_t index, std::string& item); - bool column(size_t index, Allocation& item); + bool ReadValue(size_t index, Allocation& item); - size_t columnCount(); + size_t GetColumnCount(); enum class Result { - Done, - Row, - Failure, + kDone, + kRow, + kFailure, }; - Result run(); + [[nodiscard]] Result Run(); private: - void* _statement = nullptr; - bool _ready = false; + void* statement_handle_ = nullptr; + bool ready_ = false; friend class ArchiveDatabase; ArchiveStatement(void* db, const std::string& statememt); - bool bindIntegral(size_t index, int64_t item); + bool BindIntegral(size_t index, int64_t item); - bool columnIntegral(size_t index, int64_t& item); + bool ColumnIntegral(size_t index, int64_t& item); FML_DISALLOW_COPY_AND_ASSIGN(ArchiveStatement); }; diff --git a/impeller/archivist/archive_transaction.cc b/impeller/archivist/archive_transaction.cc index 6fe7f03e2521c..18d00bff96c1a 100644 --- a/impeller/archivist/archive_transaction.cc +++ b/impeller/archivist/archive_transaction.cc @@ -13,40 +13,40 @@ ArchiveTransaction::ArchiveTransaction(int64_t& transactionCount, ArchiveStatement& beginStatement, ArchiveStatement& endStatement, ArchiveStatement& rollbackStatement) - : _endStatement(endStatement), - _rollbackStatement(rollbackStatement), - _transactionCount(transactionCount) { - if (_transactionCount == 0) { - _cleanup = beginStatement.run() == ArchiveStatement::Result::Done; + : end_stmt_(endStatement), + rollback_stmt_(rollbackStatement), + transaction_count_(transactionCount) { + if (transaction_count_ == 0) { + cleanup_ = beginStatement.Run() == ArchiveStatement::Result::kDone; } - _transactionCount++; + transaction_count_++; } ArchiveTransaction::ArchiveTransaction(ArchiveTransaction&& other) - : _endStatement(other._endStatement), - _rollbackStatement(other._rollbackStatement), - _transactionCount(other._transactionCount), - _cleanup(other._cleanup), - _successful(other._successful) { - other._abandoned = true; + : end_stmt_(other.end_stmt_), + rollback_stmt_(other.rollback_stmt_), + transaction_count_(other.transaction_count_), + cleanup_(other.cleanup_), + successful_(other.successful_) { + other.abandoned_ = true; } ArchiveTransaction::~ArchiveTransaction() { - if (_abandoned) { + if (abandoned_) { return; } - FML_CHECK(_transactionCount != 0); - if (_transactionCount == 1 && _cleanup) { - auto res = _successful ? _endStatement.run() : _rollbackStatement.run(); - FML_CHECK(res == ArchiveStatement::Result::Done) + FML_CHECK(transaction_count_ != 0); + if (transaction_count_ == 1 && cleanup_) { + auto res = successful_ ? end_stmt_.Run() : rollback_stmt_.Run(); + FML_CHECK(res == ArchiveStatement::Result::kDone) << "Must be able to commit the nested transaction"; } - _transactionCount--; + transaction_count_--; } -void ArchiveTransaction::markWritesSuccessful() { - _successful = true; +void ArchiveTransaction::MarkWritesAsReadyForCommit() { + successful_ = true; } } // namespace impeller diff --git a/impeller/archivist/archive_transaction.h b/impeller/archivist/archive_transaction.h index ed54c2281e7d8..74fe132d7db66 100644 --- a/impeller/archivist/archive_transaction.h +++ b/impeller/archivist/archive_transaction.h @@ -11,21 +11,30 @@ namespace impeller { class ArchiveStatement; +//------------------------------------------------------------------------------ +/// @brief All writes made to the archive within a transaction that is not +/// marked as ready for commit will be rolled back with the +/// transaction ends. +/// +/// All transactions are obtained from the `ArchiveDatabase`. +/// +/// @see `ArchiveDatabase` +/// class ArchiveTransaction { public: ArchiveTransaction(ArchiveTransaction&& transaction); ~ArchiveTransaction(); - void markWritesSuccessful(); + void MarkWritesAsReadyForCommit(); private: - ArchiveStatement& _endStatement; - ArchiveStatement& _rollbackStatement; - int64_t& _transactionCount; - bool _cleanup = false; - bool _successful = false; - bool _abandoned = false; + ArchiveStatement& end_stmt_; + ArchiveStatement& rollback_stmt_; + int64_t& transaction_count_; + bool cleanup_ = false; + bool successful_ = false; + bool abandoned_ = false; friend class ArchiveDatabase; diff --git a/impeller/archivist/archive_vector.cc b/impeller/archivist/archive_vector.cc index 6b99cda8c843a..e642db06a78f7 100644 --- a/impeller/archivist/archive_vector.cc +++ b/impeller/archivist/archive_vector.cc @@ -9,7 +9,7 @@ namespace impeller { ArchiveVector::ArchiveVector(std::vector&& keys) - : _keys(std::move(keys)) {} + : keys_(std::move(keys)) {} ArchiveVector::ArchiveVector() {} @@ -20,35 +20,35 @@ const ArchiveDef ArchiveVector::ArchiveDefinition = { /* .members = */ {0}, }; -ArchiveSerializable::ArchiveName ArchiveVector::archiveName() const { +Archivable::ArchiveName ArchiveVector::GetArchiveName() const { return ArchiveNameAuto; } const std::vector ArchiveVector::keys() const { - return _keys; + return keys_; } -bool ArchiveVector::serialize(ArchiveItem& item) const { +bool ArchiveVector::Write(ArchiveLocation& item) const { std::stringstream stream; - for (size_t i = 0, count = _keys.size(); i < count; i++) { - stream << _keys[i]; + for (size_t i = 0, count = keys_.size(); i < count; i++) { + stream << keys_[i]; if (i != count - 1) { stream << ","; } } - return item.encode(0, stream.str()); + return item.Write(0, stream.str()); } -bool ArchiveVector::deserialize(ArchiveItem& item) { +bool ArchiveVector::Read(ArchiveLocation& item) { std::string flattened; - if (!item.decode(0, flattened)) { + if (!item.Read(0, flattened)) { return false; } std::stringstream stream(flattened); int64_t single = 0; while (stream >> single) { - _keys.emplace_back(single); + keys_.emplace_back(single); stream.ignore(); } diff --git a/impeller/archivist/archive_vector.h b/impeller/archivist/archive_vector.h index 99d23c02f05c1..e186d6f9d3123 100644 --- a/impeller/archivist/archive_vector.h +++ b/impeller/archivist/archive_vector.h @@ -9,22 +9,22 @@ namespace impeller { -class ArchiveVector : public ArchiveSerializable { +class ArchiveVector : public Archivable { public: static const ArchiveDef ArchiveDefinition; - ArchiveName archiveName() const override; + ArchiveName GetArchiveName() const override; const std::vector keys() const; - bool serialize(ArchiveItem& item) const override; + bool Write(ArchiveLocation& item) const override; - bool deserialize(ArchiveItem& item) override; + bool Read(ArchiveLocation& item) override; private: - std::vector _keys; + std::vector keys_; - friend class ArchiveItem; + friend class ArchiveLocation; ArchiveVector(); diff --git a/impeller/archivist/archivist_unittests.cc b/impeller/archivist/archivist_unittests.cc index e1e44497b967d..96d47d6467119 100644 --- a/impeller/archivist/archivist_unittests.cc +++ b/impeller/archivist/archivist_unittests.cc @@ -12,30 +12,33 @@ namespace impeller { namespace testing { -static ArchiveSerializable::ArchiveName LastSample = 0; +static Archivable::ArchiveName LastSample = 0; -class Sample : public ArchiveSerializable { +class Sample : public Archivable { public: - Sample(uint64_t count = 42) : _someData(count), _name(++LastSample) {} + Sample(uint64_t count = 42) : some_data_(count) {} - uint64_t someData() const { return _someData; } + uint64_t GetSomeData() const { return some_data_; } - ArchiveName archiveName() const override { return _name; } + // |Archivable| + ArchiveName GetArchiveName() const override { return name_; } - bool serialize(ArchiveItem& item) const override { - return item.encode(999, _someData); + // |Archivable| + bool Write(ArchiveLocation& item) const override { + return item.Write(999, some_data_); }; - bool deserialize(ArchiveItem& item) override { - _name = item.name(); - return item.decode(999, _someData); + // |Archivable| + bool Read(ArchiveLocation& item) override { + name_ = item.Name(); + return item.Read(999, some_data_); }; static const ArchiveDef ArchiveDefinition; private: - uint64_t _someData; - ArchiveName _name; + uint64_t some_data_; + ArchiveName name_ = ++LastSample; FML_DISALLOW_COPY_AND_ASSIGN(Sample); }; @@ -51,7 +54,7 @@ TEST(ArchiveTest, SimpleInitialization) { auto name = "/tmp/sample.db"; { Archive archive(name, true); - ASSERT_TRUE(archive.isReady()); + ASSERT_TRUE(archive.IsReady()); } ASSERT_EQ(::remove(name), 0); } @@ -60,7 +63,7 @@ TEST(ArchiveTest, AddStorageClass) { auto name = "/tmp/sample2.db"; { Archive archive(name, true); - ASSERT_TRUE(archive.isReady()); + ASSERT_TRUE(archive.IsReady()); } ASSERT_EQ(::remove(name), 0); } @@ -69,9 +72,9 @@ TEST(ArchiveTest, AddData) { auto name = "/tmp/sample3.db"; { Archive archive(name, true); - ASSERT_TRUE(archive.isReady()); + ASSERT_TRUE(archive.IsReady()); Sample sample; - ASSERT_TRUE(archive.archive(sample)); + ASSERT_TRUE(archive.Write(sample)); } ASSERT_EQ(::remove(name), 0); } @@ -80,11 +83,11 @@ TEST(ArchiveTest, AddDataMultiple) { auto name = "/tmp/sample4.db"; { Archive archive(name, true); - ASSERT_TRUE(archive.isReady()); + ASSERT_TRUE(archive.IsReady()); for (size_t i = 0; i < 100; i++) { Sample sample(i + 1); - ASSERT_TRUE(archive.archive(sample)); + ASSERT_TRUE(archive.Write(sample)); } } ASSERT_EQ(::remove(name), 0); @@ -94,27 +97,24 @@ TEST(ArchiveTest, ReadData) { auto name = "/tmp/sample5.db"; { Archive archive(name, true); - ASSERT_TRUE(archive.isReady()); + ASSERT_TRUE(archive.IsReady()); size_t count = 50; - std::vector keys; + std::vector keys; std::vector values; - keys.reserve(count); - values.reserve(count); - for (size_t i = 0; i < count; i++) { Sample sample(i + 1); - keys.push_back(sample.archiveName()); - values.push_back(sample.someData()); - ASSERT_TRUE(archive.archive(sample)); + keys.push_back(sample.GetArchiveName()); + values.push_back(sample.GetSomeData()); + ASSERT_TRUE(archive.Write(sample)); } for (size_t i = 0; i < count; i++) { Sample sample; - ASSERT_TRUE(archive.unarchive(keys[i], sample)); - ASSERT_EQ(values[i], sample.someData()); + ASSERT_TRUE(archive.Read(keys[i], sample)); + ASSERT_EQ(values[i], sample.GetSomeData()); } } ASSERT_EQ(::remove(name), 0); @@ -127,11 +127,11 @@ TEST(ArchiveTest, ReadDataWithNames) { auto name = "/tmp/sample6.db"; { Archive archive(name, true); - ASSERT_TRUE(archive.isReady()); + ASSERT_TRUE(archive.IsReady()); size_t count = 8; - std::vector keys; + std::vector keys; std::vector values; keys.reserve(count); @@ -139,16 +139,16 @@ TEST(ArchiveTest, ReadDataWithNames) { for (size_t i = 0; i < count; i++) { Sample sample(i + 1); - keys.push_back(sample.archiveName()); - values.push_back(sample.someData()); - ASSERT_TRUE(archive.archive(sample)); + keys.push_back(sample.GetArchiveName()); + values.push_back(sample.GetSomeData()); + ASSERT_TRUE(archive.Write(sample)); } for (size_t i = 0; i < count; i++) { Sample sample; - ASSERT_TRUE(archive.unarchive(keys[i], sample)); - ASSERT_EQ(values[i], sample.someData()); - ASSERT_EQ(keys[i], sample.archiveName()); + ASSERT_TRUE(archive.Read(keys[i], sample)); + ASSERT_EQ(values[i], sample.GetSomeData()); + ASSERT_EQ(keys[i], sample.GetArchiveName()); } } ASSERT_EQ(::remove(name), 0); From 5f1ee5e1df1b2f9f41b5e7e770a2b17980dba66e Mon Sep 17 00:00:00 2001 From: Chinmay Garde Date: Fri, 10 Dec 2021 14:29:48 -0800 Subject: [PATCH 241/433] Cleanup archive structure. --- impeller/archivist/BUILD.gn | 6 +- impeller/archivist/archive.cc | 11 +- impeller/archivist/archive.h | 8 +- .../archivist/archive_class_registration.cc | 22 ++- impeller/archivist/archive_database.cc | 10 +- impeller/archivist/archive_database.h | 2 +- impeller/archivist/archive_vector.cc | 2 +- impeller/archivist/archive_vector.h | 2 +- impeller/archivist/archivist_fixture.cc | 38 +++++ impeller/archivist/archivist_fixture.h | 36 ++++ impeller/archivist/archivist_unittests.cc | 156 ++++++++---------- 11 files changed, 170 insertions(+), 123 deletions(-) create mode 100644 impeller/archivist/archivist_fixture.cc create mode 100644 impeller/archivist/archivist_fixture.h diff --git a/impeller/archivist/BUILD.gn b/impeller/archivist/BUILD.gn index 67667865232a9..3a409fb2e3a96 100644 --- a/impeller/archivist/BUILD.gn +++ b/impeller/archivist/BUILD.gn @@ -34,7 +34,11 @@ impeller_component("archivist") { impeller_component("archivist_unittests") { testonly = true - sources = [ "archivist_unittests.cc" ] + sources = [ + "archivist_fixture.cc", + "archivist_fixture.h", + "archivist_unittests.cc", + ] deps = [ ":archivist", "//flutter/testing", diff --git a/impeller/archivist/archive.cc b/impeller/archivist/archive.cc index 0289b648a0c1c..25e3d1d25e99b 100644 --- a/impeller/archivist/archive.cc +++ b/impeller/archivist/archive.cc @@ -14,8 +14,8 @@ namespace impeller { -Archive::Archive(const std::string& path, bool recreate) - : database_(std::make_unique(path, recreate)) {} +Archive::Archive(const std::string& path) + : database_(std::make_unique(path)) {} Archive::~Archive() { FML_DCHECK(transaction_count_ == 0) @@ -64,7 +64,7 @@ bool Archive::ArchiveInstance(const ArchiveDef& definition, /* * We need to bind the primary key only if the item does not provide its own */ - if (!definition.autoAssignName && + if (!definition.auto_key && !statement.WriteValue(ArchiveClassRegistration::NameIndex, itemName)) { return false; } @@ -79,8 +79,7 @@ bool Archive::ArchiveInstance(const ArchiveDef& definition, int64_t lastInsert = database_->GetLastInsertRowID(); - if (!definition.autoAssignName && - lastInsert != static_cast(itemName)) { + if (!definition.auto_key && lastInsert != static_cast(itemName)) { return false; } @@ -256,7 +255,7 @@ bool ArchiveLocation::ReadVectorKeys(Archivable::ArchiveName name, return false; } - const auto& keys = vector.keys(); + const auto& keys = vector.GetKeys(); std::move(keys.begin(), keys.end(), std::back_inserter(members)); diff --git a/impeller/archivist/archive.h b/impeller/archivist/archive.h index 97ebd6593cb42..930813e03c01c 100644 --- a/impeller/archivist/archive.h +++ b/impeller/archivist/archive.h @@ -24,9 +24,9 @@ struct ArchiveDef { using Member = uint64_t; using Members = std::vector; - const ArchiveDef* superClass; - const std::string className; - const bool autoAssignName; + const ArchiveDef* isa = nullptr; + const std::string table_name; + const bool auto_key = true; const Members members; }; @@ -34,7 +34,7 @@ static const Archivable::ArchiveName ArchiveNameAuto = 0; class Archive { public: - Archive(const std::string& path, bool recreate); + Archive(const std::string& path); ~Archive(); diff --git a/impeller/archivist/archive_class_registration.cc b/impeller/archivist/archive_class_registration.cc index e67275904201e..36f16bfa251f9 100644 --- a/impeller/archivist/archive_class_registration.cc +++ b/impeller/archivist/archive_class_registration.cc @@ -11,13 +11,12 @@ namespace impeller { -static const char* const ArchiveColumnPrefix = "item"; -static const char* const ArchivePrimaryKeyColumnName = "name"; -static const char* const ArchiveTablePrefix = "RL_"; +static const char* const ArchiveColumnPrefix = "col_"; +static const char* const ArchivePrimaryKeyColumnName = "primary_key"; ArchiveClassRegistration::ArchiveClassRegistration(ArchiveDatabase& database, ArchiveDef definition) - : database_(database), class_name_(definition.className) { + : database_(database), class_name_(definition.table_name) { /* * Each class in the archive class hierarchy is assigned an entry in the * class map. @@ -31,11 +30,11 @@ ArchiveClassRegistration::ArchiveClassRegistration(ArchiveDatabase& database, for (const auto& member : current->members) { map[member] = currentMember++; } - class_map_[current->className] = map; - current = current->superClass; + class_map_[current->table_name] = map; + current = current->isa; } - is_ready_ = CreateTable(definition.autoAssignName); + is_ready_ = CreateTable(definition.auto_key); } const std::string& ArchiveClassRegistration::GetClassName() const { @@ -81,8 +80,8 @@ bool ArchiveClassRegistration::CreateTable(bool autoIncrement) { * Table names cannot participate in parameter substitution, so we prepare * a statement and check its validity before running. */ - stream << "CREATE TABLE IF NOT EXISTS " << ArchiveTablePrefix - << class_name_.c_str() << " (" << ArchivePrimaryKeyColumnName; + stream << "CREATE TABLE IF NOT EXISTS " << class_name_.c_str() << " (" + << ArchivePrimaryKeyColumnName; if (autoIncrement) { stream << " INTEGER PRIMARY KEY AUTOINCREMENT, "; @@ -120,7 +119,7 @@ ArchiveStatement ArchiveClassRegistration::GetQueryStatement( stream << ","; } } - stream << " FROM " << ArchiveTablePrefix << class_name_; + stream << " FROM " << class_name_; if (single) { stream << " WHERE " << ArchivePrimaryKeyColumnName << " = ?"; @@ -135,8 +134,7 @@ ArchiveStatement ArchiveClassRegistration::GetQueryStatement( ArchiveStatement ArchiveClassRegistration::GetInsertStatement() const { std::stringstream stream; - stream << "INSERT OR REPLACE INTO " << ArchiveTablePrefix << class_name_ - << " VALUES ( ?, "; + stream << "INSERT OR REPLACE INTO " << class_name_ << " VALUES ( ?, "; for (size_t i = 0; i < member_count_; i++) { stream << "?"; if (i != member_count_ - 1) { diff --git a/impeller/archivist/archive_database.cc b/impeller/archivist/archive_database.cc index 75b13d7e55603..df174a508128d 100644 --- a/impeller/archivist/archive_database.cc +++ b/impeller/archivist/archive_database.cc @@ -18,11 +18,7 @@ namespace impeller { #define DB_HANDLE reinterpret_cast(database_) -ArchiveDatabase::ArchiveDatabase(const std::string& filename, bool recreate) { - if (recreate) { - ::remove(filename.c_str()); - } - +ArchiveDatabase::ArchiveDatabase(const std::string& filename) { if (::sqlite3_initialize() != SQLITE_OK) { VALIDATION_LOG << "Could not initialize sqlite."; return; @@ -82,7 +78,7 @@ static inline const ArchiveClassRegistration* RegistrationIfReady( const ArchiveClassRegistration* ArchiveDatabase::GetRegistrationForDefinition( const ArchiveDef& definition) { - auto found = registrations_.find(definition.className); + auto found = registrations_.find(definition.table_name); if (found != registrations_.end()) { /* * This class has already been registered. @@ -96,7 +92,7 @@ const ArchiveClassRegistration* ArchiveDatabase::GetRegistrationForDefinition( auto registration = std::unique_ptr( new ArchiveClassRegistration(*this, definition)); auto res = - registrations_.emplace(definition.className, std::move(registration)); + registrations_.emplace(definition.table_name, std::move(registration)); /* * If the new class registation is ready, return it to the caller. diff --git a/impeller/archivist/archive_database.h b/impeller/archivist/archive_database.h index 6092c3e231e23..3f66d8414ee32 100644 --- a/impeller/archivist/archive_database.h +++ b/impeller/archivist/archive_database.h @@ -18,7 +18,7 @@ struct ArchiveDef; class ArchiveDatabase { public: - ArchiveDatabase(const std::string& filename, bool recreate); + ArchiveDatabase(const std::string& filename); ~ArchiveDatabase(); diff --git a/impeller/archivist/archive_vector.cc b/impeller/archivist/archive_vector.cc index e642db06a78f7..2589cc3dd73fc 100644 --- a/impeller/archivist/archive_vector.cc +++ b/impeller/archivist/archive_vector.cc @@ -24,7 +24,7 @@ Archivable::ArchiveName ArchiveVector::GetArchiveName() const { return ArchiveNameAuto; } -const std::vector ArchiveVector::keys() const { +const std::vector ArchiveVector::GetKeys() const { return keys_; } diff --git a/impeller/archivist/archive_vector.h b/impeller/archivist/archive_vector.h index e186d6f9d3123..feceafed144d4 100644 --- a/impeller/archivist/archive_vector.h +++ b/impeller/archivist/archive_vector.h @@ -15,7 +15,7 @@ class ArchiveVector : public Archivable { ArchiveName GetArchiveName() const override; - const std::vector keys() const; + const std::vector GetKeys() const; bool Write(ArchiveLocation& item) const override; diff --git a/impeller/archivist/archivist_fixture.cc b/impeller/archivist/archivist_fixture.cc new file mode 100644 index 0000000000000..302bb7470a466 --- /dev/null +++ b/impeller/archivist/archivist_fixture.cc @@ -0,0 +1,38 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "impeller/archivist/archivist_fixture.h" + +#include "flutter/fml/paths.h" + +namespace impeller { +namespace testing { + +ArchivistFixture::ArchivistFixture() { + std::stringstream stream; + stream << flutter::testing::GetCurrentTestName() << ".db"; + archive_file_name_ = fml::paths::JoinPaths( + {flutter::testing::GetFixturesPath(), stream.str()}); +} + +ArchivistFixture::~ArchivistFixture() = default; + +const std::string ArchivistFixture::GetArchiveFileName() const { + return archive_file_name_; +} + +void ArchivistFixture::SetUp() { + DeleteArchiveFile(); +} + +void ArchivistFixture::TearDown() { + // DeleteArchiveFile(); +} + +void ArchivistFixture::DeleteArchiveFile() const { + fml::UnlinkFile(archive_file_name_.c_str()); +} + +} // namespace testing +} // namespace impeller diff --git a/impeller/archivist/archivist_fixture.h b/impeller/archivist/archivist_fixture.h new file mode 100644 index 0000000000000..2f444add6bdd2 --- /dev/null +++ b/impeller/archivist/archivist_fixture.h @@ -0,0 +1,36 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#pragma once + +#include "flutter/fml/macros.h" +#include "flutter/testing/testing.h" + +namespace impeller { +namespace testing { + +class ArchivistFixture : public ::testing::Test { + public: + ArchivistFixture(); + + ~ArchivistFixture(); + + // |::testing::Test| + void SetUp() override; + + // |::testing::Test| + void TearDown() override; + + const std::string GetArchiveFileName() const; + + private: + std::string archive_file_name_; + + void DeleteArchiveFile() const; + + FML_DISALLOW_COPY_AND_ASSIGN(ArchivistFixture); +}; + +} // namespace testing +} // namespace impeller diff --git a/impeller/archivist/archivist_unittests.cc b/impeller/archivist/archivist_unittests.cc index 96d47d6467119..eb247a98a64e8 100644 --- a/impeller/archivist/archivist_unittests.cc +++ b/impeller/archivist/archivist_unittests.cc @@ -8,6 +8,7 @@ #include "flutter/fml/macros.h" #include "flutter/testing/testing.h" #include "impeller/archivist/archive.h" +#include "impeller/archivist/archivist_fixture.h" namespace impeller { namespace testing { @@ -44,114 +45,89 @@ class Sample : public Archivable { }; const ArchiveDef Sample::ArchiveDefinition = { - .superClass = nullptr, - .className = "Sample", - .autoAssignName = false, + .isa = nullptr, + .table_name = "Sample", + .auto_key = false, .members = {999}, }; -TEST(ArchiveTest, SimpleInitialization) { - auto name = "/tmp/sample.db"; - { - Archive archive(name, true); - ASSERT_TRUE(archive.IsReady()); - } - ASSERT_EQ(::remove(name), 0); +using ArchiveTest = ArchivistFixture; + +TEST_F(ArchiveTest, SimpleInitialization) { + Archive archive(GetArchiveFileName().c_str()); + ASSERT_TRUE(archive.IsReady()); } -TEST(ArchiveTest, AddStorageClass) { - auto name = "/tmp/sample2.db"; - { - Archive archive(name, true); - ASSERT_TRUE(archive.IsReady()); - } - ASSERT_EQ(::remove(name), 0); +TEST_F(ArchiveTest, AddStorageClass) { + Archive archive(GetArchiveFileName().c_str()); + ASSERT_TRUE(archive.IsReady()); } -TEST(ArchiveTest, AddData) { - auto name = "/tmp/sample3.db"; - { - Archive archive(name, true); - ASSERT_TRUE(archive.IsReady()); - Sample sample; +TEST_F(ArchiveTest, AddData) { + Archive archive(GetArchiveFileName().c_str()); + ASSERT_TRUE(archive.IsReady()); + Sample sample; + ASSERT_TRUE(archive.Write(sample)); +} + +TEST_F(ArchiveTest, AddDataMultiple) { + Archive archive(GetArchiveFileName().c_str()); + ASSERT_TRUE(archive.IsReady()); + + for (size_t i = 0; i < 100; i++) { + Sample sample(i + 1); ASSERT_TRUE(archive.Write(sample)); } - ASSERT_EQ(::remove(name), 0); } -TEST(ArchiveTest, AddDataMultiple) { - auto name = "/tmp/sample4.db"; - { - Archive archive(name, true); - ASSERT_TRUE(archive.IsReady()); +TEST_F(ArchiveTest, ReadData) { + Archive archive(GetArchiveFileName().c_str()); + ASSERT_TRUE(archive.IsReady()); + + size_t count = 50; + + std::vector keys; + std::vector values; + + for (size_t i = 0; i < count; i++) { + Sample sample(i + 1); + keys.push_back(sample.GetArchiveName()); + values.push_back(sample.GetSomeData()); + ASSERT_TRUE(archive.Write(sample)); + } - for (size_t i = 0; i < 100; i++) { - Sample sample(i + 1); - ASSERT_TRUE(archive.Write(sample)); - } + for (size_t i = 0; i < count; i++) { + Sample sample; + ASSERT_TRUE(archive.Read(keys[i], sample)); + ASSERT_EQ(values[i], sample.GetSomeData()); } - ASSERT_EQ(::remove(name), 0); } -TEST(ArchiveTest, ReadData) { - auto name = "/tmp/sample5.db"; - { - Archive archive(name, true); - ASSERT_TRUE(archive.IsReady()); - - size_t count = 50; - - std::vector keys; - std::vector values; - - for (size_t i = 0; i < count; i++) { - Sample sample(i + 1); - keys.push_back(sample.GetArchiveName()); - values.push_back(sample.GetSomeData()); - ASSERT_TRUE(archive.Write(sample)); - } - - for (size_t i = 0; i < count; i++) { - Sample sample; - ASSERT_TRUE(archive.Read(keys[i], sample)); - ASSERT_EQ(values[i], sample.GetSomeData()); - } +TEST_F(ArchiveTest, ReadDataWithNames) { + Archive archive(GetArchiveFileName().c_str()); + ASSERT_TRUE(archive.IsReady()); + + size_t count = 8; + + std::vector keys; + std::vector values; + + keys.reserve(count); + values.reserve(count); + + for (size_t i = 0; i < count; i++) { + Sample sample(i + 1); + keys.push_back(sample.GetArchiveName()); + values.push_back(sample.GetSomeData()); + ASSERT_TRUE(archive.Write(sample)); } - ASSERT_EQ(::remove(name), 0); -} -/* - * This shouldn't be slow. Need to cache compiled statements. - */ -TEST(ArchiveTest, ReadDataWithNames) { - auto name = "/tmp/sample6.db"; - { - Archive archive(name, true); - ASSERT_TRUE(archive.IsReady()); - - size_t count = 8; - - std::vector keys; - std::vector values; - - keys.reserve(count); - values.reserve(count); - - for (size_t i = 0; i < count; i++) { - Sample sample(i + 1); - keys.push_back(sample.GetArchiveName()); - values.push_back(sample.GetSomeData()); - ASSERT_TRUE(archive.Write(sample)); - } - - for (size_t i = 0; i < count; i++) { - Sample sample; - ASSERT_TRUE(archive.Read(keys[i], sample)); - ASSERT_EQ(values[i], sample.GetSomeData()); - ASSERT_EQ(keys[i], sample.GetArchiveName()); - } + for (size_t i = 0; i < count; i++) { + Sample sample; + ASSERT_TRUE(archive.Read(keys[i], sample)); + ASSERT_EQ(values[i], sample.GetSomeData()); + ASSERT_EQ(keys[i], sample.GetArchiveName()); } - ASSERT_EQ(::remove(name), 0); } } // namespace testing From 7763e90006d2a985375ecc4d0901311dafd04c3e Mon Sep 17 00:00:00 2001 From: Chinmay Garde Date: Sat, 11 Dec 2021 13:17:19 -0800 Subject: [PATCH 242/433] Separate archive_location into its own TU. --- impeller/archivist/BUILD.gn | 2 + impeller/archivist/archivable.h | 12 ++ impeller/archivist/archive.cc | 17 +- impeller/archivist/archive.h | 193 +---------------- .../archivist/archive_class_registration.cc | 6 +- .../archivist/archive_class_registration.h | 3 +- impeller/archivist/archive_database.cc | 12 +- impeller/archivist/archive_database.h | 5 +- impeller/archivist/archive_location.cc | 11 + impeller/archivist/archive_location.h | 198 ++++++++++++++++++ impeller/archivist/archive_statement.cc | 6 +- impeller/archivist/archive_statement.h | 46 +++- impeller/archivist/archive_transaction.cc | 4 +- impeller/archivist/archive_vector.cc | 2 + impeller/archivist/archivist_fixture.cc | 2 + impeller/archivist/archivist_unittests.cc | 13 +- 16 files changed, 300 insertions(+), 232 deletions(-) create mode 100644 impeller/archivist/archive_location.cc create mode 100644 impeller/archivist/archive_location.h diff --git a/impeller/archivist/BUILD.gn b/impeller/archivist/BUILD.gn index 3a409fb2e3a96..48e7324ec5135 100644 --- a/impeller/archivist/BUILD.gn +++ b/impeller/archivist/BUILD.gn @@ -19,6 +19,8 @@ impeller_component("archivist") { "archive_class_registration.h", "archive_database.cc", "archive_database.h", + "archive_location.cc", + "archive_location.h", "archive_statement.cc", "archive_statement.h", "archive_transaction.cc", diff --git a/impeller/archivist/archivable.h b/impeller/archivist/archivable.h index 88b766fe6466b..04cd7bdc74a8f 100644 --- a/impeller/archivist/archivable.h +++ b/impeller/archivist/archivable.h @@ -5,9 +5,21 @@ #pragma once #include +#include +#include namespace impeller { +struct ArchiveDef { + using Member = uint64_t; + using Members = std::vector; + + const ArchiveDef* isa = nullptr; + const std::string table_name; + const bool auto_key = true; + const Members members; +}; + class ArchiveLocation; //------------------------------------------------------------------------------ diff --git a/impeller/archivist/archive.cc b/impeller/archivist/archive.cc index 25e3d1d25e99b..7e0cd83ef56ae 100644 --- a/impeller/archivist/archive.cc +++ b/impeller/archivist/archive.cc @@ -9,6 +9,7 @@ #include "flutter/fml/logging.h" #include "impeller/archivist/archive_class_registration.h" #include "impeller/archivist/archive_database.h" +#include "impeller/archivist/archive_location.h" #include "impeller/archivist/archive_statement.h" #include "impeller/archivist/archive_vector.h" @@ -22,14 +23,14 @@ Archive::~Archive() { << "There must be no pending transactions"; } -bool Archive::IsReady() const { - return database_->IsReady(); +bool Archive::IsValid() const { + return database_->IsValid(); } bool Archive::ArchiveInstance(const ArchiveDef& definition, const Archivable& archivable, int64_t& lastInsertIDOut) { - if (!IsReady()) { + if (!IsValid()) { return false; } @@ -44,7 +45,7 @@ bool Archive::ArchiveInstance(const ArchiveDef& definition, auto statement = registration->GetInsertStatement(); - if (!statement.IsReady() || !statement.Reset()) { + if (!statement.IsValid() || !statement.Reset()) { /* * Must be able to reset the statement for a new write */ @@ -73,7 +74,7 @@ bool Archive::ArchiveInstance(const ArchiveDef& definition, return false; } - if (statement.Run() != ArchiveStatement::Result::kDone) { + if (statement.Execute() != ArchiveStatement::Result::kDone) { return false; } @@ -108,7 +109,7 @@ bool Archive::UnarchiveInstance(const ArchiveDef& definition, size_t Archive::UnarchiveInstances(const ArchiveDef& definition, Archive::UnarchiveStep stepper, Archivable::ArchiveName name) { - if (!IsReady()) { + if (!IsValid()) { return 0; } @@ -123,7 +124,7 @@ size_t Archive::UnarchiveInstances(const ArchiveDef& definition, auto statement = registration->GetQueryStatement(isQueryingSingle); - if (!statement.IsReady() || !statement.Reset()) { + if (!statement.IsValid() || !statement.Reset()) { return 0; } @@ -150,7 +151,7 @@ size_t Archive::UnarchiveInstances(const ArchiveDef& definition, size_t itemsRead = 0; - while (statement.Run() == ArchiveStatement::Result::kRow) { + while (statement.Execute() == ArchiveStatement::Result::kRow) { itemsRead++; /* diff --git a/impeller/archivist/archive.h b/impeller/archivist/archive.h index 930813e03c01c..5fc7aa0add96d 100644 --- a/impeller/archivist/archive.h +++ b/impeller/archivist/archive.h @@ -11,24 +11,11 @@ #include "flutter/fml/macros.h" #include "impeller/archivist/archivable.h" -#include "impeller/base/allocation.h" namespace impeller { class ArchiveLocation; -class ArchiveClassRegistration; class ArchiveDatabase; -class ArchiveStatement; - -struct ArchiveDef { - using Member = uint64_t; - using Members = std::vector; - - const ArchiveDef* isa = nullptr; - const std::string table_name; - const bool auto_key = true; - const Members members; -}; static const Archivable::ArchiveName ArchiveNameAuto = 0; @@ -38,7 +25,7 @@ class Archive { ~Archive(); - bool IsReady() const; + bool IsValid() const; template ::value>> @@ -83,182 +70,4 @@ class Archive { FML_DISALLOW_COPY_AND_ASSIGN(Archive); }; -class ArchiveLocation { - public: - template ::value>> - bool Write(ArchiveDef::Member member, T item) { - return WriteIntegral(member, static_cast(item)); - } - - bool Write(ArchiveDef::Member member, double item); - - bool Write(ArchiveDef::Member member, const std::string& item); - - bool Write(ArchiveDef::Member member, const Allocation& allocation); - - template ::value>> - bool WriteArchivable(ArchiveDef::Member member, const T& other) { - const ArchiveDef& otherDef = T::ArchiveDefinition; - return Write(member, otherDef, other); - } - - template ::value>> - bool WriteEnum(ArchiveDef::Member member, const T& item) { - return WriteIntegral(member, static_cast(item)); - } - - template ::value>> - bool Write(ArchiveDef::Member member, const std::vector& items) { - /* - * All items in the vector are individually encoded and their keys noted - */ - std::vector members; - members.reserve(items.size()); - - const ArchiveDef& itemDefinition = T::ArchiveDefinition; - for (const auto& item : items) { - int64_t added = 0; - bool result = context_.ArchiveInstance(itemDefinition, item, added); - if (!result) { - return false; - } - members.emplace_back(added); - } - - /* - * The keys are flattened into the vectors table. Write to that table - */ - auto vectorInsert = WriteVectorKeys(std::move(members)); - - if (!vectorInsert.first) { - return false; - } - - return WriteIntegral(member, vectorInsert.second); - } - - template ::value && - std::is_base_of::value>> - bool WriteSuper(const Current& thiz) { - std::string oldClass = current_class_; - current_class_ = Super::ArchiveDefinition.className; - auto success = thiz.Super::serialize(*this); - current_class_ = oldClass; - return success; - } - - template ::value>> - bool Read(ArchiveDef::Member member, T& item) { - int64_t decoded = 0; - auto result = ReadIntegral(member, decoded); - item = static_cast(decoded); - return result; - } - - bool Read(ArchiveDef::Member member, double& item); - - bool Read(ArchiveDef::Member member, std::string& item); - - bool Read(ArchiveDef::Member member, Allocation& allocation); - - template ::value>> - bool ReadArchivable(ArchiveDef::Member member, T& other) { - const ArchiveDef& otherDef = T::ArchiveDefinition; - return decode(member, otherDef, other); - } - - template ::value>> - bool ReadEnum(ArchiveDef::Member member, T& item) { - int64_t desugared = 0; - if (ReadIntegral(member, desugared)) { - item = static_cast(desugared); - return true; - } - return false; - } - - template ::value>> - bool Read(ArchiveDef::Member member, std::vector& items) { - /* - * From the member, find the foreign key of the vector - */ - int64_t vectorForeignKey = 0; - if (!ReadIntegral(member, vectorForeignKey)) { - return false; - } - - /* - * Get vector keys - */ - std::vector keys; - if (!ReadVectorKeys(vectorForeignKey, keys)) { - return false; - } - - const ArchiveDef& otherDef = T::ArchiveDefinition; - for (const auto& key : keys) { - items.emplace_back(); - - if (!context_.UnarchiveInstance(otherDef, key, items.back())) { - return false; - } - } - - return true; - } - - template ::value && - std::is_base_of::value>> - bool ReadSuper(Current& thiz) { - std::string oldClass = current_class_; - current_class_ = Super::ArchiveDefinition.className; - auto success = thiz.Super::deserialize(*this); - current_class_ = oldClass; - return success; - } - - Archivable::ArchiveName Name() const; - - private: - Archive& context_; - ArchiveStatement& statement_; - const ArchiveClassRegistration& registration_; - Archivable::ArchiveName name_; - std::string current_class_; - - friend class Archive; - - ArchiveLocation(Archive& context, - ArchiveStatement& statement, - const ArchiveClassRegistration& registration, - Archivable::ArchiveName name); - - bool WriteIntegral(ArchiveDef::Member member, int64_t item); - - bool ReadIntegral(ArchiveDef::Member member, int64_t& item); - - std::pair WriteVectorKeys(std::vector&& members); - - bool ReadVectorKeys(Archivable::ArchiveName name, - std::vector& members); - - bool Write(ArchiveDef::Member member, - const ArchiveDef& otherDef, - const Archivable& other); - - bool Read(ArchiveDef::Member member, - const ArchiveDef& otherDef, - Archivable& other); - - FML_DISALLOW_COPY_AND_ASSIGN(ArchiveLocation); -}; - } // namespace impeller diff --git a/impeller/archivist/archive_class_registration.cc b/impeller/archivist/archive_class_registration.cc index 36f16bfa251f9..288e301db521f 100644 --- a/impeller/archivist/archive_class_registration.cc +++ b/impeller/archivist/archive_class_registration.cc @@ -45,7 +45,7 @@ size_t ArchiveClassRegistration::GetMemberCount() const { return member_count_; } -bool ArchiveClassRegistration::IsReady() const { +bool ArchiveClassRegistration::IsValid() const { return is_ready_; } @@ -98,7 +98,7 @@ bool ArchiveClassRegistration::CreateTable(bool autoIncrement) { auto statement = database_.CreateStatement(stream.str()); - if (!statement.IsReady()) { + if (!statement.IsValid()) { return false; } @@ -106,7 +106,7 @@ bool ArchiveClassRegistration::CreateTable(bool autoIncrement) { return false; } - return statement.Run() == ArchiveStatement::Result::kDone; + return statement.Execute() == ArchiveStatement::Result::kDone; } ArchiveStatement ArchiveClassRegistration::GetQueryStatement( diff --git a/impeller/archivist/archive_class_registration.h b/impeller/archivist/archive_class_registration.h index 94bfddf8bd7fd..f9f34b8d29932 100644 --- a/impeller/archivist/archive_class_registration.h +++ b/impeller/archivist/archive_class_registration.h @@ -6,6 +6,7 @@ #include "flutter/fml/macros.h" #include "impeller/archivist/archive.h" +#include "impeller/archivist/archive_statement.h" namespace impeller { @@ -19,7 +20,7 @@ class ArchiveClassRegistration { size_t GetMemberCount() const; - bool IsReady() const; + bool IsValid() const; ArchiveStatement GetInsertStatement() const; diff --git a/impeller/archivist/archive_database.cc b/impeller/archivist/archive_database.cc index df174a508128d..d58a7acbc7c41 100644 --- a/impeller/archivist/archive_database.cc +++ b/impeller/archivist/archive_database.cc @@ -35,21 +35,21 @@ ArchiveDatabase::ArchiveDatabase(const std::string& filename) { begin_transaction_stmt_ = std::unique_ptr( new ArchiveStatement(database_, "BEGIN TRANSACTION;")); - if (!begin_transaction_stmt_->IsReady()) { + if (!begin_transaction_stmt_->IsValid()) { return; } end_transaction_stmt_ = std::unique_ptr( new ArchiveStatement(database_, "END TRANSACTION;")); - if (!end_transaction_stmt_->IsReady()) { + if (!end_transaction_stmt_->IsValid()) { return; } rollback_transaction_stmt_ = std::unique_ptr( new ArchiveStatement(database_, "ROLLBACK TRANSACTION;")); - if (!rollback_transaction_stmt_->IsReady()) { + if (!rollback_transaction_stmt_->IsValid()) { return; } @@ -60,7 +60,7 @@ ArchiveDatabase::~ArchiveDatabase() { ::sqlite3_close(DB_HANDLE); } -bool ArchiveDatabase::IsReady() const { +bool ArchiveDatabase::IsValid() const { return ready_; } @@ -73,7 +73,7 @@ static inline const ArchiveClassRegistration* RegistrationIfReady( if (registration == nullptr) { return nullptr; } - return registration->IsReady() ? registration : nullptr; + return registration->IsValid() ? registration : nullptr; } const ArchiveClassRegistration* ArchiveDatabase::GetRegistrationForDefinition( @@ -95,7 +95,7 @@ const ArchiveClassRegistration* ArchiveDatabase::GetRegistrationForDefinition( registrations_.emplace(definition.table_name, std::move(registration)); /* - * If the new class registation is ready, return it to the caller. + * If the new class registration is ready, return it to the caller. */ return res.second ? RegistrationIfReady((*(res.first)).second.get()) : nullptr; diff --git a/impeller/archivist/archive_database.h b/impeller/archivist/archive_database.h index 3f66d8414ee32..ed1b4480170f1 100644 --- a/impeller/archivist/archive_database.h +++ b/impeller/archivist/archive_database.h @@ -16,13 +16,16 @@ class ArchiveStatement; class ArchiveClassRegistration; struct ArchiveDef; +//------------------------------------------------------------------------------ +/// @brief A handle to the underlying database connection for an archive. +/// class ArchiveDatabase { public: ArchiveDatabase(const std::string& filename); ~ArchiveDatabase(); - bool IsReady() const; + bool IsValid() const; int64_t GetLastInsertRowID(); diff --git a/impeller/archivist/archive_location.cc b/impeller/archivist/archive_location.cc new file mode 100644 index 0000000000000..308444047ec54 --- /dev/null +++ b/impeller/archivist/archive_location.cc @@ -0,0 +1,11 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "impeller/archivist/archive_location.h" + +namespace impeller { + +// + +} // namespace impeller diff --git a/impeller/archivist/archive_location.h b/impeller/archivist/archive_location.h new file mode 100644 index 0000000000000..adf6fcd80f3ae --- /dev/null +++ b/impeller/archivist/archive_location.h @@ -0,0 +1,198 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#pragma once + +#include + +#include "flutter/fml/macros.h" +#include "impeller/archivist/archivable.h" +#include "impeller/archivist/archive.h" +#include "impeller/base/allocation.h" + +namespace impeller { + +class Archive; +class ArchiveClassRegistration; +class ArchiveStatement; + +class ArchiveLocation { + public: + template ::value>> + bool Write(ArchiveDef::Member member, T item) { + return WriteIntegral(member, static_cast(item)); + } + + bool Write(ArchiveDef::Member member, double item); + + bool Write(ArchiveDef::Member member, const std::string& item); + + bool Write(ArchiveDef::Member member, const Allocation& allocation); + + template ::value>> + bool WriteArchivable(ArchiveDef::Member member, const T& other) { + const ArchiveDef& otherDef = T::ArchiveDefinition; + return Write(member, otherDef, other); + } + + template ::value>> + bool WriteEnum(ArchiveDef::Member member, const T& item) { + return WriteIntegral(member, static_cast(item)); + } + + template ::value>> + bool Write(ArchiveDef::Member member, const std::vector& items) { + /* + * All items in the vector are individually encoded and their keys noted + */ + std::vector members; + members.reserve(items.size()); + + const ArchiveDef& itemDefinition = T::ArchiveDefinition; + for (const auto& item : items) { + int64_t added = 0; + bool result = context_.ArchiveInstance(itemDefinition, item, added); + if (!result) { + return false; + } + members.emplace_back(added); + } + + /* + * The keys are flattened into the vectors table. Write to that table + */ + auto vectorInsert = WriteVectorKeys(std::move(members)); + + if (!vectorInsert.first) { + return false; + } + + return WriteIntegral(member, vectorInsert.second); + } + + template ::value && + std::is_base_of::value>> + bool WriteSuper(const Current& thiz) { + std::string oldClass = current_class_; + current_class_ = Super::ArchiveDefinition.className; + auto success = thiz.Super::serialize(*this); + current_class_ = oldClass; + return success; + } + + template ::value>> + bool Read(ArchiveDef::Member member, T& item) { + int64_t decoded = 0; + auto result = ReadIntegral(member, decoded); + item = static_cast(decoded); + return result; + } + + bool Read(ArchiveDef::Member member, double& item); + + bool Read(ArchiveDef::Member member, std::string& item); + + bool Read(ArchiveDef::Member member, Allocation& allocation); + + template ::value>> + bool ReadArchivable(ArchiveDef::Member member, T& other) { + const ArchiveDef& otherDef = T::ArchiveDefinition; + return decode(member, otherDef, other); + } + + template ::value>> + bool ReadEnum(ArchiveDef::Member member, T& item) { + int64_t desugared = 0; + if (ReadIntegral(member, desugared)) { + item = static_cast(desugared); + return true; + } + return false; + } + + template ::value>> + bool Read(ArchiveDef::Member member, std::vector& items) { + /* + * From the member, find the foreign key of the vector + */ + int64_t vectorForeignKey = 0; + if (!ReadIntegral(member, vectorForeignKey)) { + return false; + } + + /* + * Get vector keys + */ + std::vector keys; + if (!ReadVectorKeys(vectorForeignKey, keys)) { + return false; + } + + const ArchiveDef& otherDef = T::ArchiveDefinition; + for (const auto& key : keys) { + items.emplace_back(); + + if (!context_.UnarchiveInstance(otherDef, key, items.back())) { + return false; + } + } + + return true; + } + + template ::value && + std::is_base_of::value>> + bool ReadSuper(Current& thiz) { + std::string oldClass = current_class_; + current_class_ = Super::ArchiveDefinition.className; + auto success = thiz.Super::deserialize(*this); + current_class_ = oldClass; + return success; + } + + Archivable::ArchiveName Name() const; + + private: + Archive& context_; + ArchiveStatement& statement_; + const ArchiveClassRegistration& registration_; + Archivable::ArchiveName name_; + std::string current_class_; + + friend class Archive; + + ArchiveLocation(Archive& context, + ArchiveStatement& statement, + const ArchiveClassRegistration& registration, + Archivable::ArchiveName name); + + bool WriteIntegral(ArchiveDef::Member member, int64_t item); + + bool ReadIntegral(ArchiveDef::Member member, int64_t& item); + + std::pair WriteVectorKeys(std::vector&& members); + + bool ReadVectorKeys(Archivable::ArchiveName name, + std::vector& members); + + bool Write(ArchiveDef::Member member, + const ArchiveDef& otherDef, + const Archivable& other); + + bool Read(ArchiveDef::Member member, + const ArchiveDef& otherDef, + Archivable& other); + + FML_DISALLOW_COPY_AND_ASSIGN(ArchiveLocation); +}; + +} // namespace impeller diff --git a/impeller/archivist/archive_statement.cc b/impeller/archivist/archive_statement.cc index de4bec087aa5e..976ab47a8e894 100644 --- a/impeller/archivist/archive_statement.cc +++ b/impeller/archivist/archive_statement.cc @@ -37,7 +37,7 @@ ArchiveStatement::~ArchiveStatement() { } } -bool ArchiveStatement::IsReady() const { +bool ArchiveStatement::IsValid() const { return ready_; } @@ -62,7 +62,7 @@ static constexpr int ToParam(size_t index) { static constexpr int ToColumn(size_t index) { /* - * sqlite columns begin from 1 + * sqlite columns begin from 0 */ return static_cast(index); } @@ -165,7 +165,7 @@ bool ArchiveStatement::ReadValue(size_t index, Allocation& item) { return true; } -ArchiveStatement::Result ArchiveStatement::Run() { +ArchiveStatement::Result ArchiveStatement::Execute() { switch (::sqlite3_step(STATEMENT_HANDLE)) { case SQLITE_DONE: return Result::kDone; diff --git a/impeller/archivist/archive_statement.h b/impeller/archivist/archive_statement.h index d60be1b4be677..35abbb1d73a3c 100644 --- a/impeller/archivist/archive_statement.h +++ b/impeller/archivist/archive_statement.h @@ -11,14 +11,48 @@ namespace impeller { +//------------------------------------------------------------------------------ +/// @brief Represents a read/write query to an archive database. Statements +/// are expensive to create and must be cached for as long as +/// possible. +/// class ArchiveStatement { public: ~ArchiveStatement(); ArchiveStatement(ArchiveStatement&& message); - bool IsReady() const; + bool IsValid() const; + enum class Result { + //-------------------------------------------------------------------------- + /// The statement is done executing. + /// + kDone, + //-------------------------------------------------------------------------- + /// The statement found a row of information ready for reading. + /// + kRow, + //-------------------------------------------------------------------------- + /// Statement execution was a failure. + /// + kFailure, + }; + + //---------------------------------------------------------------------------- + /// @brief Execute the given statement with the provided data. + /// + /// @return Is the execution was succeessful. + /// + [[nodiscard]] Result Execute(); + + //---------------------------------------------------------------------------- + /// @brief All writes after the last successfull `Run` call are reset. + /// Since statements are expensive to create, reset them for new + /// writes instead of creating new statements. + /// + /// @return If the statement writes were reset. + /// bool Reset(); bool WriteValue(size_t index, const std::string& item); @@ -45,21 +79,13 @@ class ArchiveStatement { size_t GetColumnCount(); - enum class Result { - kDone, - kRow, - kFailure, - }; - - [[nodiscard]] Result Run(); - private: void* statement_handle_ = nullptr; bool ready_ = false; friend class ArchiveDatabase; - ArchiveStatement(void* db, const std::string& statememt); + ArchiveStatement(void* db, const std::string& statement); bool BindIntegral(size_t index, int64_t item); diff --git a/impeller/archivist/archive_transaction.cc b/impeller/archivist/archive_transaction.cc index 18d00bff96c1a..bc546ff88f266 100644 --- a/impeller/archivist/archive_transaction.cc +++ b/impeller/archivist/archive_transaction.cc @@ -17,7 +17,7 @@ ArchiveTransaction::ArchiveTransaction(int64_t& transactionCount, rollback_stmt_(rollbackStatement), transaction_count_(transactionCount) { if (transaction_count_ == 0) { - cleanup_ = beginStatement.Run() == ArchiveStatement::Result::kDone; + cleanup_ = beginStatement.Execute() == ArchiveStatement::Result::kDone; } transaction_count_++; } @@ -38,7 +38,7 @@ ArchiveTransaction::~ArchiveTransaction() { FML_CHECK(transaction_count_ != 0); if (transaction_count_ == 1 && cleanup_) { - auto res = successful_ ? end_stmt_.Run() : rollback_stmt_.Run(); + auto res = successful_ ? end_stmt_.Execute() : rollback_stmt_.Execute(); FML_CHECK(res == ArchiveStatement::Result::kDone) << "Must be able to commit the nested transaction"; } diff --git a/impeller/archivist/archive_vector.cc b/impeller/archivist/archive_vector.cc index 2589cc3dd73fc..bf561d77004f2 100644 --- a/impeller/archivist/archive_vector.cc +++ b/impeller/archivist/archive_vector.cc @@ -6,6 +6,8 @@ #include +#include "impeller/archivist/archive_location.h" + namespace impeller { ArchiveVector::ArchiveVector(std::vector&& keys) diff --git a/impeller/archivist/archivist_fixture.cc b/impeller/archivist/archivist_fixture.cc index 302bb7470a466..7a80634114315 100644 --- a/impeller/archivist/archivist_fixture.cc +++ b/impeller/archivist/archivist_fixture.cc @@ -27,6 +27,8 @@ void ArchivistFixture::SetUp() { } void ArchivistFixture::TearDown() { + // TODO: Tear this down. For now, I am inspecting the files for readability of + // schema. // DeleteArchiveFile(); } diff --git a/impeller/archivist/archivist_unittests.cc b/impeller/archivist/archivist_unittests.cc index eb247a98a64e8..19fbdcf72cfad 100644 --- a/impeller/archivist/archivist_unittests.cc +++ b/impeller/archivist/archivist_unittests.cc @@ -8,6 +8,7 @@ #include "flutter/fml/macros.h" #include "flutter/testing/testing.h" #include "impeller/archivist/archive.h" +#include "impeller/archivist/archive_location.h" #include "impeller/archivist/archivist_fixture.h" namespace impeller { @@ -55,24 +56,24 @@ using ArchiveTest = ArchivistFixture; TEST_F(ArchiveTest, SimpleInitialization) { Archive archive(GetArchiveFileName().c_str()); - ASSERT_TRUE(archive.IsReady()); + ASSERT_TRUE(archive.IsValid()); } TEST_F(ArchiveTest, AddStorageClass) { Archive archive(GetArchiveFileName().c_str()); - ASSERT_TRUE(archive.IsReady()); + ASSERT_TRUE(archive.IsValid()); } TEST_F(ArchiveTest, AddData) { Archive archive(GetArchiveFileName().c_str()); - ASSERT_TRUE(archive.IsReady()); + ASSERT_TRUE(archive.IsValid()); Sample sample; ASSERT_TRUE(archive.Write(sample)); } TEST_F(ArchiveTest, AddDataMultiple) { Archive archive(GetArchiveFileName().c_str()); - ASSERT_TRUE(archive.IsReady()); + ASSERT_TRUE(archive.IsValid()); for (size_t i = 0; i < 100; i++) { Sample sample(i + 1); @@ -82,7 +83,7 @@ TEST_F(ArchiveTest, AddDataMultiple) { TEST_F(ArchiveTest, ReadData) { Archive archive(GetArchiveFileName().c_str()); - ASSERT_TRUE(archive.IsReady()); + ASSERT_TRUE(archive.IsValid()); size_t count = 50; @@ -105,7 +106,7 @@ TEST_F(ArchiveTest, ReadData) { TEST_F(ArchiveTest, ReadDataWithNames) { Archive archive(GetArchiveFileName().c_str()); - ASSERT_TRUE(archive.IsReady()); + ASSERT_TRUE(archive.IsValid()); size_t count = 8; From 2edcf8820a1cba7c01e086442fe38e7751300c00 Mon Sep 17 00:00:00 2001 From: Chinmay Garde Date: Sat, 11 Dec 2021 13:29:00 -0800 Subject: [PATCH 243/433] Clearer names for primary keys. --- impeller/archivist/archivable.h | 2 +- impeller/archivist/archive.cc | 20 ++++++++++--------- .../archivist/archive_class_registration.cc | 4 ++-- .../archivist/archive_class_registration.h | 12 +++++------ impeller/archivist/archive_location.h | 2 +- impeller/archivist/archive_vector.cc | 2 +- impeller/archivist/archive_vector.h | 2 +- impeller/archivist/archivist_unittests.cc | 10 +++++----- 8 files changed, 28 insertions(+), 26 deletions(-) diff --git a/impeller/archivist/archivable.h b/impeller/archivist/archivable.h index 04cd7bdc74a8f..4a85166e20f44 100644 --- a/impeller/archivist/archivable.h +++ b/impeller/archivist/archivable.h @@ -30,7 +30,7 @@ class Archivable { public: using ArchiveName = uint64_t; - virtual ArchiveName GetArchiveName() const = 0; + virtual ArchiveName GetArchivePrimaryKey() const = 0; virtual bool Write(ArchiveLocation& item) const = 0; diff --git a/impeller/archivist/archive.cc b/impeller/archivist/archive.cc index 7e0cd83ef56ae..4d97ff07d7d4b 100644 --- a/impeller/archivist/archive.cc +++ b/impeller/archivist/archive.cc @@ -43,7 +43,7 @@ bool Archive::ArchiveInstance(const ArchiveDef& definition, return false; } - auto statement = registration->GetInsertStatement(); + auto statement = registration->CreateInsertStatement(); if (!statement.IsValid() || !statement.Reset()) { /* @@ -52,7 +52,7 @@ bool Archive::ArchiveInstance(const ArchiveDef& definition, return false; } - auto itemName = archivable.GetArchiveName(); + auto primary_key = archivable.GetArchivePrimaryKey(); /* * The lifecycle of the archive item is tied to this scope and there is no @@ -60,13 +60,14 @@ bool Archive::ArchiveInstance(const ArchiveDef& definition, * for its members to be references. It does not manage the lifetimes of * anything. */ - ArchiveLocation item(*this, statement, *registration, itemName); + ArchiveLocation item(*this, statement, *registration, primary_key); /* * We need to bind the primary key only if the item does not provide its own */ if (!definition.auto_key && - !statement.WriteValue(ArchiveClassRegistration::NameIndex, itemName)) { + !statement.WriteValue(ArchiveClassRegistration::kPrimaryKeyIndex, + primary_key)) { return false; } @@ -80,7 +81,7 @@ bool Archive::ArchiveInstance(const ArchiveDef& definition, int64_t lastInsert = database_->GetLastInsertRowID(); - if (!definition.auto_key && lastInsert != static_cast(itemName)) { + if (!definition.auto_key && lastInsert != static_cast(primary_key)) { return false; } @@ -122,7 +123,7 @@ size_t Archive::UnarchiveInstances(const ArchiveDef& definition, const bool isQueryingSingle = name != ArchiveNameAuto; - auto statement = registration->GetQueryStatement(isQueryingSingle); + auto statement = registration->CreateQueryStatement(isQueryingSingle); if (!statement.IsValid() || !statement.Reset()) { return 0; @@ -133,7 +134,8 @@ size_t Archive::UnarchiveInstances(const ArchiveDef& definition, * If a single statement is being queried for, bind the name as a statement * argument. */ - if (!statement.WriteValue(ArchiveClassRegistration::NameIndex, name)) { + if (!statement.WriteValue(ArchiveClassRegistration::kPrimaryKeyIndex, + name)) { return 0; } } @@ -181,7 +183,7 @@ ArchiveLocation::ArchiveLocation(Archive& context, name_(name), current_class_(registration.GetClassName()) {} -Archivable::ArchiveName ArchiveLocation::Name() const { +Archivable::ArchiveName ArchiveLocation::GetPrimaryKey() const { return name_; } @@ -226,7 +228,7 @@ bool ArchiveLocation::Write(ArchiveDef::Member member, } /* - * Bind the name of the serialiable + * Bind the name of the serializable */ if (!statement_.WriteValue(found.first, lastInsert)) { return false; diff --git a/impeller/archivist/archive_class_registration.cc b/impeller/archivist/archive_class_registration.cc index 288e301db521f..7f7b49b4678d0 100644 --- a/impeller/archivist/archive_class_registration.cc +++ b/impeller/archivist/archive_class_registration.cc @@ -109,7 +109,7 @@ bool ArchiveClassRegistration::CreateTable(bool autoIncrement) { return statement.Execute() == ArchiveStatement::Result::kDone; } -ArchiveStatement ArchiveClassRegistration::GetQueryStatement( +ArchiveStatement ArchiveClassRegistration::CreateQueryStatement( bool single) const { std::stringstream stream; stream << "SELECT " << ArchivePrimaryKeyColumnName << ", "; @@ -132,7 +132,7 @@ ArchiveStatement ArchiveClassRegistration::GetQueryStatement( return database_.CreateStatement(stream.str()); } -ArchiveStatement ArchiveClassRegistration::GetInsertStatement() const { +ArchiveStatement ArchiveClassRegistration::CreateInsertStatement() const { std::stringstream stream; stream << "INSERT OR REPLACE INTO " << class_name_ << " VALUES ( ?, "; for (size_t i = 0; i < member_count_; i++) { diff --git a/impeller/archivist/archive_class_registration.h b/impeller/archivist/archive_class_registration.h index f9f34b8d29932..d0bbbbd983270 100644 --- a/impeller/archivist/archive_class_registration.h +++ b/impeller/archivist/archive_class_registration.h @@ -12,6 +12,10 @@ namespace impeller { class ArchiveClassRegistration { public: + static constexpr size_t kPrimaryKeyIndex = 0u; + + bool IsValid() const; + using ColumnResult = std::pair; ColumnResult FindColumn(const std::string& className, ArchiveDef::Member member) const; @@ -20,13 +24,9 @@ class ArchiveClassRegistration { size_t GetMemberCount() const; - bool IsValid() const; - - ArchiveStatement GetInsertStatement() const; - - ArchiveStatement GetQueryStatement(bool single) const; + ArchiveStatement CreateInsertStatement() const; - static const size_t NameIndex = 0; + ArchiveStatement CreateQueryStatement(bool single) const; private: using MemberColumnMap = std::map; diff --git a/impeller/archivist/archive_location.h b/impeller/archivist/archive_location.h index adf6fcd80f3ae..b30b37b6fa93a 100644 --- a/impeller/archivist/archive_location.h +++ b/impeller/archivist/archive_location.h @@ -159,7 +159,7 @@ class ArchiveLocation { return success; } - Archivable::ArchiveName Name() const; + Archivable::ArchiveName GetPrimaryKey() const; private: Archive& context_; diff --git a/impeller/archivist/archive_vector.cc b/impeller/archivist/archive_vector.cc index bf561d77004f2..c094838b6f849 100644 --- a/impeller/archivist/archive_vector.cc +++ b/impeller/archivist/archive_vector.cc @@ -22,7 +22,7 @@ const ArchiveDef ArchiveVector::ArchiveDefinition = { /* .members = */ {0}, }; -Archivable::ArchiveName ArchiveVector::GetArchiveName() const { +Archivable::ArchiveName ArchiveVector::GetArchivePrimaryKey() const { return ArchiveNameAuto; } diff --git a/impeller/archivist/archive_vector.h b/impeller/archivist/archive_vector.h index feceafed144d4..303fb1d9fe468 100644 --- a/impeller/archivist/archive_vector.h +++ b/impeller/archivist/archive_vector.h @@ -13,7 +13,7 @@ class ArchiveVector : public Archivable { public: static const ArchiveDef ArchiveDefinition; - ArchiveName GetArchiveName() const override; + ArchiveName GetArchivePrimaryKey() const override; const std::vector GetKeys() const; diff --git a/impeller/archivist/archivist_unittests.cc b/impeller/archivist/archivist_unittests.cc index 19fbdcf72cfad..241c3fb64513c 100644 --- a/impeller/archivist/archivist_unittests.cc +++ b/impeller/archivist/archivist_unittests.cc @@ -23,7 +23,7 @@ class Sample : public Archivable { uint64_t GetSomeData() const { return some_data_; } // |Archivable| - ArchiveName GetArchiveName() const override { return name_; } + ArchiveName GetArchivePrimaryKey() const override { return name_; } // |Archivable| bool Write(ArchiveLocation& item) const override { @@ -32,7 +32,7 @@ class Sample : public Archivable { // |Archivable| bool Read(ArchiveLocation& item) override { - name_ = item.Name(); + name_ = item.GetPrimaryKey(); return item.Read(999, some_data_); }; @@ -92,7 +92,7 @@ TEST_F(ArchiveTest, ReadData) { for (size_t i = 0; i < count; i++) { Sample sample(i + 1); - keys.push_back(sample.GetArchiveName()); + keys.push_back(sample.GetArchivePrimaryKey()); values.push_back(sample.GetSomeData()); ASSERT_TRUE(archive.Write(sample)); } @@ -118,7 +118,7 @@ TEST_F(ArchiveTest, ReadDataWithNames) { for (size_t i = 0; i < count; i++) { Sample sample(i + 1); - keys.push_back(sample.GetArchiveName()); + keys.push_back(sample.GetArchivePrimaryKey()); values.push_back(sample.GetSomeData()); ASSERT_TRUE(archive.Write(sample)); } @@ -127,7 +127,7 @@ TEST_F(ArchiveTest, ReadDataWithNames) { Sample sample; ASSERT_TRUE(archive.Read(keys[i], sample)); ASSERT_EQ(values[i], sample.GetSomeData()); - ASSERT_EQ(keys[i], sample.GetArchiveName()); + ASSERT_EQ(keys[i], sample.GetArchivePrimaryKey()); } } From e42e373d630b752c3e4ac94a1465d0677903908a Mon Sep 17 00:00:00 2001 From: Chinmay Garde Date: Sat, 11 Dec 2021 13:35:27 -0800 Subject: [PATCH 244/433] Add uncommitted patches from a previous change. --- impeller/archivist/archive.cc | 144 ------------------------ impeller/archivist/archive_location.cc | 145 ++++++++++++++++++++++++- 2 files changed, 144 insertions(+), 145 deletions(-) diff --git a/impeller/archivist/archive.cc b/impeller/archivist/archive.cc index 4d97ff07d7d4b..882813e0280ef 100644 --- a/impeller/archivist/archive.cc +++ b/impeller/archivist/archive.cc @@ -10,8 +10,6 @@ #include "impeller/archivist/archive_class_registration.h" #include "impeller/archivist/archive_database.h" #include "impeller/archivist/archive_location.h" -#include "impeller/archivist/archive_statement.h" -#include "impeller/archivist/archive_vector.h" namespace impeller { @@ -173,146 +171,4 @@ size_t Archive::UnarchiveInstances(const ArchiveDef& definition, return itemsRead; } -ArchiveLocation::ArchiveLocation(Archive& context, - ArchiveStatement& statement, - const ArchiveClassRegistration& registration, - Archivable::ArchiveName name) - : context_(context), - statement_(statement), - registration_(registration), - name_(name), - current_class_(registration.GetClassName()) {} - -Archivable::ArchiveName ArchiveLocation::GetPrimaryKey() const { - return name_; -} - -bool ArchiveLocation::Write(ArchiveDef::Member member, - const std::string& item) { - auto found = registration_.FindColumn(current_class_, member); - return found.second ? statement_.WriteValue(found.first, item) : false; -} - -bool ArchiveLocation::WriteIntegral(ArchiveDef::Member member, int64_t item) { - auto found = registration_.FindColumn(current_class_, member); - return found.second ? statement_.WriteValue(found.first, item) : false; -} - -bool ArchiveLocation::Write(ArchiveDef::Member member, double item) { - auto found = registration_.FindColumn(current_class_, member); - return found.second ? statement_.WriteValue(found.first, item) : false; -} - -bool ArchiveLocation::Write(ArchiveDef::Member member, const Allocation& item) { - auto found = registration_.FindColumn(current_class_, member); - return found.second ? statement_.WriteValue(found.first, item) : false; -} - -bool ArchiveLocation::Write(ArchiveDef::Member member, - const ArchiveDef& otherDef, - const Archivable& other) { - auto found = registration_.FindColumn(current_class_, member); - - if (!found.second) { - return false; - } - - /* - * We need to fully archive the other instance first because it could - * have a name that is auto assigned. In that case, we cannot ask it before - * archival (via `other.archiveName()`). - */ - int64_t lastInsert = 0; - if (!context_.ArchiveInstance(otherDef, other, lastInsert)) { - return false; - } - - /* - * Bind the name of the serializable - */ - if (!statement_.WriteValue(found.first, lastInsert)) { - return false; - } - - return true; -} - -std::pair ArchiveLocation::WriteVectorKeys( - std::vector&& members) { - ArchiveVector vector(std::move(members)); - int64_t vectorID = 0; - if (!context_.ArchiveInstance(ArchiveVector::ArchiveDefinition, // - vector, // - vectorID)) { - return {false, 0}; - } - return {true, vectorID}; -} - -bool ArchiveLocation::ReadVectorKeys(Archivable::ArchiveName name, - std::vector& members) { - ArchiveVector vector; - - if (!context_.UnarchiveInstance(ArchiveVector::ArchiveDefinition, name, - vector)) { - return false; - } - - const auto& keys = vector.GetKeys(); - - std::move(keys.begin(), keys.end(), std::back_inserter(members)); - - return true; -} - -bool ArchiveLocation::Read(ArchiveDef::Member member, std::string& item) { - auto found = registration_.FindColumn(current_class_, member); - return found.second ? statement_.ReadValue(found.first, item) : false; -} - -bool ArchiveLocation::ReadIntegral(ArchiveDef::Member member, int64_t& item) { - auto found = registration_.FindColumn(current_class_, member); - return found.second ? statement_.ReadValue(found.first, item) : false; -} - -bool ArchiveLocation::Read(ArchiveDef::Member member, double& item) { - auto found = registration_.FindColumn(current_class_, member); - return found.second ? statement_.ReadValue(found.first, item) : false; -} - -bool ArchiveLocation::Read(ArchiveDef::Member member, Allocation& item) { - auto found = registration_.FindColumn(current_class_, member); - return found.second ? statement_.ReadValue(found.first, item) : false; -} - -bool ArchiveLocation::Read(ArchiveDef::Member member, - const ArchiveDef& otherDef, - Archivable& other) { - auto found = registration_.FindColumn(current_class_, member); - - /* - * Make sure a member is present at that column - */ - if (!found.second) { - return false; - } - - /* - * Try to find the foreign key in the current items row - */ - int64_t foreignKey = 0; - if (!statement_.ReadValue(found.first, foreignKey)) { - return false; - } - - /* - * Find the other item and unarchive by this foreign key - */ - if (!context_.UnarchiveInstance(otherDef, foreignKey, other)) { - return false; - } - - return true; -} - } // namespace impeller diff --git a/impeller/archivist/archive_location.cc b/impeller/archivist/archive_location.cc index 308444047ec54..df0c19e6cd3eb 100644 --- a/impeller/archivist/archive_location.cc +++ b/impeller/archivist/archive_location.cc @@ -4,8 +4,151 @@ #include "impeller/archivist/archive_location.h" +#include "impeller/archivist/archive_class_registration.h" +#include "impeller/archivist/archive_vector.h" + namespace impeller { -// +ArchiveLocation::ArchiveLocation(Archive& context, + ArchiveStatement& statement, + const ArchiveClassRegistration& registration, + Archivable::ArchiveName name) + : context_(context), + statement_(statement), + registration_(registration), + name_(name), + current_class_(registration.GetClassName()) {} + +Archivable::ArchiveName ArchiveLocation::GetPrimaryKey() const { + return name_; +} + +bool ArchiveLocation::Write(ArchiveDef::Member member, + const std::string& item) { + auto found = registration_.FindColumn(current_class_, member); + return found.second ? statement_.WriteValue(found.first, item) : false; +} + +bool ArchiveLocation::WriteIntegral(ArchiveDef::Member member, int64_t item) { + auto found = registration_.FindColumn(current_class_, member); + return found.second ? statement_.WriteValue(found.first, item) : false; +} + +bool ArchiveLocation::Write(ArchiveDef::Member member, double item) { + auto found = registration_.FindColumn(current_class_, member); + return found.second ? statement_.WriteValue(found.first, item) : false; +} + +bool ArchiveLocation::Write(ArchiveDef::Member member, const Allocation& item) { + auto found = registration_.FindColumn(current_class_, member); + return found.second ? statement_.WriteValue(found.first, item) : false; +} + +bool ArchiveLocation::Write(ArchiveDef::Member member, + const ArchiveDef& otherDef, + const Archivable& other) { + auto found = registration_.FindColumn(current_class_, member); + + if (!found.second) { + return false; + } + + /* + * We need to fully archive the other instance first because it could + * have a name that is auto assigned. In that case, we cannot ask it before + * archival (via `other.archiveName()`). + */ + int64_t lastInsert = 0; + if (!context_.ArchiveInstance(otherDef, other, lastInsert)) { + return false; + } + + /* + * Bind the name of the serializable + */ + if (!statement_.WriteValue(found.first, lastInsert)) { + return false; + } + + return true; +} + +std::pair ArchiveLocation::WriteVectorKeys( + std::vector&& members) { + ArchiveVector vector(std::move(members)); + int64_t vectorID = 0; + if (!context_.ArchiveInstance(ArchiveVector::ArchiveDefinition, // + vector, // + vectorID)) { + return {false, 0}; + } + return {true, vectorID}; +} + +bool ArchiveLocation::ReadVectorKeys(Archivable::ArchiveName name, + std::vector& members) { + ArchiveVector vector; + + if (!context_.UnarchiveInstance(ArchiveVector::ArchiveDefinition, name, + vector)) { + return false; + } + + const auto& keys = vector.GetKeys(); + + std::move(keys.begin(), keys.end(), std::back_inserter(members)); + + return true; +} + +bool ArchiveLocation::Read(ArchiveDef::Member member, std::string& item) { + auto found = registration_.FindColumn(current_class_, member); + return found.second ? statement_.ReadValue(found.first, item) : false; +} + +bool ArchiveLocation::ReadIntegral(ArchiveDef::Member member, int64_t& item) { + auto found = registration_.FindColumn(current_class_, member); + return found.second ? statement_.ReadValue(found.first, item) : false; +} + +bool ArchiveLocation::Read(ArchiveDef::Member member, double& item) { + auto found = registration_.FindColumn(current_class_, member); + return found.second ? statement_.ReadValue(found.first, item) : false; +} + +bool ArchiveLocation::Read(ArchiveDef::Member member, Allocation& item) { + auto found = registration_.FindColumn(current_class_, member); + return found.second ? statement_.ReadValue(found.first, item) : false; +} + +bool ArchiveLocation::Read(ArchiveDef::Member member, + const ArchiveDef& otherDef, + Archivable& other) { + auto found = registration_.FindColumn(current_class_, member); + + /* + * Make sure a member is present at that column + */ + if (!found.second) { + return false; + } + + /* + * Try to find the foreign key in the current items row + */ + int64_t foreignKey = 0; + if (!statement_.ReadValue(found.first, foreignKey)) { + return false; + } + + /* + * Find the other item and unarchive by this foreign key + */ + if (!context_.UnarchiveInstance(otherDef, foreignKey, other)) { + return false; + } + + return true; +} } // namespace impeller From bf346dcc4c1232312f0bb66ebcde126db69a0592 Mon Sep 17 00:00:00 2001 From: Chinmay Garde Date: Sat, 11 Dec 2021 14:10:18 -0800 Subject: [PATCH 245/433] More use of optionals in archivist. --- impeller/archivist/archive.cc | 36 +++++----- impeller/archivist/archive.h | 20 +++--- .../archivist/archive_class_registration.cc | 8 +-- .../archivist/archive_class_registration.h | 6 +- impeller/archivist/archive_location.cc | 66 ++++++++----------- impeller/archivist/archive_location.h | 15 +++-- impeller/archivist/archive_vector.cc | 2 +- 7 files changed, 71 insertions(+), 82 deletions(-) diff --git a/impeller/archivist/archive.cc b/impeller/archivist/archive.cc index 882813e0280ef..b6c6a9069eea3 100644 --- a/impeller/archivist/archive.cc +++ b/impeller/archivist/archive.cc @@ -25,11 +25,11 @@ bool Archive::IsValid() const { return database_->IsValid(); } -bool Archive::ArchiveInstance(const ArchiveDef& definition, - const Archivable& archivable, - int64_t& lastInsertIDOut) { +std::optional Archive::ArchiveInstance( + const ArchiveDef& definition, + const Archivable& archivable) { if (!IsValid()) { - return false; + return std::nullopt; } auto transaction = database_->CreateTransaction(transaction_count_); @@ -38,7 +38,7 @@ bool Archive::ArchiveInstance(const ArchiveDef& definition, database_->GetRegistrationForDefinition(definition); if (registration == nullptr) { - return false; + return std::nullopt; } auto statement = registration->CreateInsertStatement(); @@ -47,7 +47,7 @@ bool Archive::ArchiveInstance(const ArchiveDef& definition, /* * Must be able to reset the statement for a new write */ - return false; + return std::nullopt; } auto primary_key = archivable.GetArchivePrimaryKey(); @@ -66,32 +66,30 @@ bool Archive::ArchiveInstance(const ArchiveDef& definition, if (!definition.auto_key && !statement.WriteValue(ArchiveClassRegistration::kPrimaryKeyIndex, primary_key)) { - return false; + return std::nullopt; } if (!archivable.Write(item)) { - return false; + return std::nullopt; } if (statement.Execute() != ArchiveStatement::Result::kDone) { - return false; + return std::nullopt; } int64_t lastInsert = database_->GetLastInsertRowID(); if (!definition.auto_key && lastInsert != static_cast(primary_key)) { - return false; + return std::nullopt; } - lastInsertIDOut = lastInsert; - /* * If any of the nested calls fail, we would have already checked for the * failure and returned. */ transaction.MarkWritesAsReadyForCommit(); - return true; + return lastInsert; } bool Archive::UnarchiveInstance(const ArchiveDef& definition, @@ -107,7 +105,7 @@ bool Archive::UnarchiveInstance(const ArchiveDef& definition, size_t Archive::UnarchiveInstances(const ArchiveDef& definition, Archive::UnarchiveStep stepper, - Archivable::ArchiveName name) { + std::optional primary_key) { if (!IsValid()) { return 0; } @@ -119,7 +117,7 @@ size_t Archive::UnarchiveInstances(const ArchiveDef& definition, return 0; } - const bool isQueryingSingle = name != ArchiveNameAuto; + const bool isQueryingSingle = primary_key.has_value(); auto statement = registration->CreateQueryStatement(isQueryingSingle); @@ -129,11 +127,11 @@ size_t Archive::UnarchiveInstances(const ArchiveDef& definition, if (isQueryingSingle) { /* - * If a single statement is being queried for, bind the name as a statement - * argument. + * If a single statement is being queried for, bind the primary key as a + * statement argument. */ if (!statement.WriteValue(ArchiveClassRegistration::kPrimaryKeyIndex, - name)) { + primary_key.value())) { return 0; } } @@ -157,7 +155,7 @@ size_t Archive::UnarchiveInstances(const ArchiveDef& definition, /* * Prepare a fresh archive item for the given statement */ - ArchiveLocation item(*this, statement, *registration, name); + ArchiveLocation item(*this, statement, *registration, primary_key); if (!stepper(item)) { break; diff --git a/impeller/archivist/archive.h b/impeller/archivist/archive.h index 5fc7aa0add96d..3927159abd059 100644 --- a/impeller/archivist/archive.h +++ b/impeller/archivist/archive.h @@ -5,6 +5,7 @@ #pragma once #include +#include #include #include #include @@ -17,8 +18,6 @@ namespace impeller { class ArchiveLocation; class ArchiveDatabase; -static const Archivable::ArchiveName ArchiveNameAuto = 0; - class Archive { public: Archive(const std::string& path); @@ -31,8 +30,7 @@ class Archive { class = std::enable_if::value>> bool Write(const T& archivable) { const ArchiveDef& def = T::ArchiveDefinition; - int64_t unused_last = 0; - return ArchiveInstance(def, archivable, unused_last); + return ArchiveInstance(def, archivable).has_value(); } template ; + using UnarchiveStep = std::function; template ::value>> size_t Read(UnarchiveStep stepper) { const ArchiveDef& def = T::ArchiveDefinition; - return UnarchiveInstances(def, stepper, ArchiveNameAuto); + return UnarchiveInstances(def, stepper); } private: @@ -57,15 +55,17 @@ class Archive { friend class ArchiveLocation; - bool ArchiveInstance(const ArchiveDef& definition, - const Archivable& archivable, - int64_t& lastInsertID); + std::optional ArchiveInstance( + const ArchiveDef& definition, + const Archivable& archivable); + bool UnarchiveInstance(const ArchiveDef& definition, Archivable::ArchiveName name, Archivable& archivable); + size_t UnarchiveInstances(const ArchiveDef& definition, UnarchiveStep stepper, - Archivable::ArchiveName optionalName); + std::optional primary_key = std::nullopt); FML_DISALLOW_COPY_AND_ASSIGN(Archive); }; diff --git a/impeller/archivist/archive_class_registration.cc b/impeller/archivist/archive_class_registration.cc index 7f7b49b4678d0..10ef0951a8981 100644 --- a/impeller/archivist/archive_class_registration.cc +++ b/impeller/archivist/archive_class_registration.cc @@ -49,13 +49,13 @@ bool ArchiveClassRegistration::IsValid() const { return is_ready_; } -ArchiveClassRegistration::ColumnResult ArchiveClassRegistration::FindColumn( +std::optional ArchiveClassRegistration::FindColumnIndex( const std::string& className, ArchiveDef::Member member) const { auto found = class_map_.find(className); if (found == class_map_.end()) { - return {0, false}; + return std::nullopt; } const auto& memberToColumns = found->second; @@ -63,10 +63,10 @@ ArchiveClassRegistration::ColumnResult ArchiveClassRegistration::FindColumn( auto foundColumn = memberToColumns.find(member); if (foundColumn == memberToColumns.end()) { - return {0, false}; + return std::nullopt; } - return {foundColumn->second, true}; + return foundColumn->second; } bool ArchiveClassRegistration::CreateTable(bool autoIncrement) { diff --git a/impeller/archivist/archive_class_registration.h b/impeller/archivist/archive_class_registration.h index d0bbbbd983270..eec4ecf2ea1ca 100644 --- a/impeller/archivist/archive_class_registration.h +++ b/impeller/archivist/archive_class_registration.h @@ -3,6 +3,7 @@ // found in the LICENSE file. #include +#include #include "flutter/fml/macros.h" #include "impeller/archivist/archive.h" @@ -16,9 +17,8 @@ class ArchiveClassRegistration { bool IsValid() const; - using ColumnResult = std::pair; - ColumnResult FindColumn(const std::string& className, - ArchiveDef::Member member) const; + std::optional FindColumnIndex(const std::string& className, + ArchiveDef::Member member) const; const std::string& GetClassName() const; diff --git a/impeller/archivist/archive_location.cc b/impeller/archivist/archive_location.cc index df0c19e6cd3eb..382863848550e 100644 --- a/impeller/archivist/archive_location.cc +++ b/impeller/archivist/archive_location.cc @@ -12,7 +12,7 @@ namespace impeller { ArchiveLocation::ArchiveLocation(Archive& context, ArchiveStatement& statement, const ArchiveClassRegistration& registration, - Archivable::ArchiveName name) + std::optional name) : context_(context), statement_(statement), registration_(registration), @@ -20,36 +20,36 @@ ArchiveLocation::ArchiveLocation(Archive& context, current_class_(registration.GetClassName()) {} Archivable::ArchiveName ArchiveLocation::GetPrimaryKey() const { - return name_; + return name_.value_or(0u); } bool ArchiveLocation::Write(ArchiveDef::Member member, const std::string& item) { - auto found = registration_.FindColumn(current_class_, member); - return found.second ? statement_.WriteValue(found.first, item) : false; + auto index = registration_.FindColumnIndex(current_class_, member); + return index.has_value() ? statement_.WriteValue(index.value(), item) : false; } bool ArchiveLocation::WriteIntegral(ArchiveDef::Member member, int64_t item) { - auto found = registration_.FindColumn(current_class_, member); - return found.second ? statement_.WriteValue(found.first, item) : false; + auto index = registration_.FindColumnIndex(current_class_, member); + return index.has_value() ? statement_.WriteValue(index.value(), item) : false; } bool ArchiveLocation::Write(ArchiveDef::Member member, double item) { - auto found = registration_.FindColumn(current_class_, member); - return found.second ? statement_.WriteValue(found.first, item) : false; + auto index = registration_.FindColumnIndex(current_class_, member); + return index.has_value() ? statement_.WriteValue(index.value(), item) : false; } bool ArchiveLocation::Write(ArchiveDef::Member member, const Allocation& item) { - auto found = registration_.FindColumn(current_class_, member); - return found.second ? statement_.WriteValue(found.first, item) : false; + auto index = registration_.FindColumnIndex(current_class_, member); + return index.has_value() ? statement_.WriteValue(index.value(), item) : false; } bool ArchiveLocation::Write(ArchiveDef::Member member, const ArchiveDef& otherDef, const Archivable& other) { - auto found = registration_.FindColumn(current_class_, member); + auto index = registration_.FindColumnIndex(current_class_, member); - if (!found.second) { + if (!index.has_value()) { return false; } @@ -58,78 +58,68 @@ bool ArchiveLocation::Write(ArchiveDef::Member member, * have a name that is auto assigned. In that case, we cannot ask it before * archival (via `other.archiveName()`). */ - int64_t lastInsert = 0; - if (!context_.ArchiveInstance(otherDef, other, lastInsert)) { + auto row_id = context_.ArchiveInstance(otherDef, other); + if (!row_id.has_value()) { return false; } /* * Bind the name of the serializable */ - if (!statement_.WriteValue(found.first, lastInsert)) { + if (!statement_.WriteValue(index.value(), row_id.value())) { return false; } return true; } -std::pair ArchiveLocation::WriteVectorKeys( +std::optional ArchiveLocation::WriteVectorKeys( std::vector&& members) { ArchiveVector vector(std::move(members)); - int64_t vectorID = 0; - if (!context_.ArchiveInstance(ArchiveVector::ArchiveDefinition, // - vector, // - vectorID)) { - return {false, 0}; - } - return {true, vectorID}; + return context_.ArchiveInstance(ArchiveVector::ArchiveDefinition, vector); } bool ArchiveLocation::ReadVectorKeys(Archivable::ArchiveName name, std::vector& members) { ArchiveVector vector; - if (!context_.UnarchiveInstance(ArchiveVector::ArchiveDefinition, name, vector)) { return false; } - const auto& keys = vector.GetKeys(); - std::move(keys.begin(), keys.end(), std::back_inserter(members)); - return true; } bool ArchiveLocation::Read(ArchiveDef::Member member, std::string& item) { - auto found = registration_.FindColumn(current_class_, member); - return found.second ? statement_.ReadValue(found.first, item) : false; + auto index = registration_.FindColumnIndex(current_class_, member); + return index.has_value() ? statement_.ReadValue(index.value(), item) : false; } bool ArchiveLocation::ReadIntegral(ArchiveDef::Member member, int64_t& item) { - auto found = registration_.FindColumn(current_class_, member); - return found.second ? statement_.ReadValue(found.first, item) : false; + auto index = registration_.FindColumnIndex(current_class_, member); + return index.has_value() ? statement_.ReadValue(index.value(), item) : false; } bool ArchiveLocation::Read(ArchiveDef::Member member, double& item) { - auto found = registration_.FindColumn(current_class_, member); - return found.second ? statement_.ReadValue(found.first, item) : false; + auto index = registration_.FindColumnIndex(current_class_, member); + return index.has_value() ? statement_.ReadValue(index.value(), item) : false; } bool ArchiveLocation::Read(ArchiveDef::Member member, Allocation& item) { - auto found = registration_.FindColumn(current_class_, member); - return found.second ? statement_.ReadValue(found.first, item) : false; + auto index = registration_.FindColumnIndex(current_class_, member); + return index.has_value() ? statement_.ReadValue(index.value(), item) : false; } bool ArchiveLocation::Read(ArchiveDef::Member member, const ArchiveDef& otherDef, Archivable& other) { - auto found = registration_.FindColumn(current_class_, member); + auto index = registration_.FindColumnIndex(current_class_, member); /* * Make sure a member is present at that column */ - if (!found.second) { + if (!index.has_value()) { return false; } @@ -137,7 +127,7 @@ bool ArchiveLocation::Read(ArchiveDef::Member member, * Try to find the foreign key in the current items row */ int64_t foreignKey = 0; - if (!statement_.ReadValue(found.first, foreignKey)) { + if (!statement_.ReadValue(index.value(), foreignKey)) { return false; } diff --git a/impeller/archivist/archive_location.h b/impeller/archivist/archive_location.h index b30b37b6fa93a..598254b45ca3f 100644 --- a/impeller/archivist/archive_location.h +++ b/impeller/archivist/archive_location.h @@ -4,6 +4,7 @@ #pragma once +#include #include #include "flutter/fml/macros.h" @@ -19,6 +20,8 @@ class ArchiveStatement; class ArchiveLocation { public: + Archivable::ArchiveName GetPrimaryKey() const; + template ::value>> bool Write(ArchiveDef::Member member, T item) { return WriteIntegral(member, static_cast(item)); @@ -66,11 +69,11 @@ class ArchiveLocation { */ auto vectorInsert = WriteVectorKeys(std::move(members)); - if (!vectorInsert.first) { + if (!vectorInsert.has_value()) { return false; } - return WriteIntegral(member, vectorInsert.second); + return WriteIntegral(member, vectorInsert.value()); } template name_; std::string current_class_; friend class Archive; @@ -173,13 +174,13 @@ class ArchiveLocation { ArchiveLocation(Archive& context, ArchiveStatement& statement, const ArchiveClassRegistration& registration, - Archivable::ArchiveName name); + std::optional name); bool WriteIntegral(ArchiveDef::Member member, int64_t item); bool ReadIntegral(ArchiveDef::Member member, int64_t& item); - std::pair WriteVectorKeys(std::vector&& members); + std::optional WriteVectorKeys(std::vector&& members); bool ReadVectorKeys(Archivable::ArchiveName name, std::vector& members); diff --git a/impeller/archivist/archive_vector.cc b/impeller/archivist/archive_vector.cc index c094838b6f849..92117a1bee746 100644 --- a/impeller/archivist/archive_vector.cc +++ b/impeller/archivist/archive_vector.cc @@ -23,7 +23,7 @@ const ArchiveDef ArchiveVector::ArchiveDefinition = { }; Archivable::ArchiveName ArchiveVector::GetArchivePrimaryKey() const { - return ArchiveNameAuto; + return 0; } const std::vector ArchiveVector::GetKeys() const { From 23d34bf7d2231ba87b8babf6c47de8ac9d61862f Mon Sep 17 00:00:00 2001 From: Chinmay Garde Date: Sat, 11 Dec 2021 14:23:59 -0800 Subject: [PATCH 246/433] Make primary keys optional. --- impeller/archivist/archivable.h | 7 ++++--- impeller/archivist/archive.cc | 18 +++++++++++++----- impeller/archivist/archive.h | 6 +++--- impeller/archivist/archive_location.cc | 8 ++++---- impeller/archivist/archive_location.h | 9 ++++----- impeller/archivist/archive_vector.cc | 7 ++++--- impeller/archivist/archive_vector.h | 2 +- impeller/archivist/archivist_unittests.cc | 16 ++++++++-------- 8 files changed, 41 insertions(+), 32 deletions(-) diff --git a/impeller/archivist/archivable.h b/impeller/archivist/archivable.h index 4a85166e20f44..783c138920471 100644 --- a/impeller/archivist/archivable.h +++ b/impeller/archivist/archivable.h @@ -5,6 +5,7 @@ #pragma once #include +#include #include #include @@ -22,15 +23,15 @@ struct ArchiveDef { class ArchiveLocation; +using PrimaryKey = std::optional; + //------------------------------------------------------------------------------ /// @brief Instances of `Archivable`s can be read from and written to a /// persistent archive. /// class Archivable { public: - using ArchiveName = uint64_t; - - virtual ArchiveName GetArchivePrimaryKey() const = 0; + virtual PrimaryKey GetPrimaryKey() const = 0; virtual bool Write(ArchiveLocation& item) const = 0; diff --git a/impeller/archivist/archive.cc b/impeller/archivist/archive.cc index b6c6a9069eea3..c10646e081e3d 100644 --- a/impeller/archivist/archive.cc +++ b/impeller/archivist/archive.cc @@ -10,6 +10,7 @@ #include "impeller/archivist/archive_class_registration.h" #include "impeller/archivist/archive_database.h" #include "impeller/archivist/archive_location.h" +#include "impeller/base/validation.h" namespace impeller { @@ -50,7 +51,13 @@ std::optional Archive::ArchiveInstance( return std::nullopt; } - auto primary_key = archivable.GetArchivePrimaryKey(); + auto primary_key = archivable.GetPrimaryKey(); + + if (!definition.auto_key && !primary_key.has_value()) { + VALIDATION_LOG << "Archive definition specified that primary keys will be " + "explicitly specified but none was provided when asked."; + return std::nullopt; + } /* * The lifecycle of the archive item is tied to this scope and there is no @@ -65,7 +72,7 @@ std::optional Archive::ArchiveInstance( */ if (!definition.auto_key && !statement.WriteValue(ArchiveClassRegistration::kPrimaryKeyIndex, - primary_key)) { + primary_key.value())) { return std::nullopt; } @@ -79,7 +86,8 @@ std::optional Archive::ArchiveInstance( int64_t lastInsert = database_->GetLastInsertRowID(); - if (!definition.auto_key && lastInsert != static_cast(primary_key)) { + if (!definition.auto_key && + lastInsert != static_cast(primary_key.value())) { return std::nullopt; } @@ -93,7 +101,7 @@ std::optional Archive::ArchiveInstance( } bool Archive::UnarchiveInstance(const ArchiveDef& definition, - Archivable::ArchiveName name, + PrimaryKey name, Archivable& archivable) { UnarchiveStep stepper = [&archivable](ArchiveLocation& item) { archivable.Read(item); @@ -105,7 +113,7 @@ bool Archive::UnarchiveInstance(const ArchiveDef& definition, size_t Archive::UnarchiveInstances(const ArchiveDef& definition, Archive::UnarchiveStep stepper, - std::optional primary_key) { + PrimaryKey primary_key) { if (!IsValid()) { return 0; } diff --git a/impeller/archivist/archive.h b/impeller/archivist/archive.h index 3927159abd059..18d7b9ad1cd66 100644 --- a/impeller/archivist/archive.h +++ b/impeller/archivist/archive.h @@ -35,7 +35,7 @@ class Archive { template ::value>> - bool Read(Archivable::ArchiveName name, T& archivable) { + bool Read(PrimaryKey name, T& archivable) { const ArchiveDef& def = T::ArchiveDefinition; return UnarchiveInstance(def, name, archivable); } @@ -60,12 +60,12 @@ class Archive { const Archivable& archivable); bool UnarchiveInstance(const ArchiveDef& definition, - Archivable::ArchiveName name, + PrimaryKey name, Archivable& archivable); size_t UnarchiveInstances(const ArchiveDef& definition, UnarchiveStep stepper, - std::optional primary_key = std::nullopt); + PrimaryKey primary_key = std::nullopt); FML_DISALLOW_COPY_AND_ASSIGN(Archive); }; diff --git a/impeller/archivist/archive_location.cc b/impeller/archivist/archive_location.cc index 382863848550e..85080f9bab458 100644 --- a/impeller/archivist/archive_location.cc +++ b/impeller/archivist/archive_location.cc @@ -12,15 +12,15 @@ namespace impeller { ArchiveLocation::ArchiveLocation(Archive& context, ArchiveStatement& statement, const ArchiveClassRegistration& registration, - std::optional name) + PrimaryKey name) : context_(context), statement_(statement), registration_(registration), name_(name), current_class_(registration.GetClassName()) {} -Archivable::ArchiveName ArchiveLocation::GetPrimaryKey() const { - return name_.value_or(0u); +PrimaryKey ArchiveLocation::GetPrimaryKey() const { + return name_; } bool ArchiveLocation::Write(ArchiveDef::Member member, @@ -79,7 +79,7 @@ std::optional ArchiveLocation::WriteVectorKeys( return context_.ArchiveInstance(ArchiveVector::ArchiveDefinition, vector); } -bool ArchiveLocation::ReadVectorKeys(Archivable::ArchiveName name, +bool ArchiveLocation::ReadVectorKeys(PrimaryKey name, std::vector& members) { ArchiveVector vector; if (!context_.UnarchiveInstance(ArchiveVector::ArchiveDefinition, name, diff --git a/impeller/archivist/archive_location.h b/impeller/archivist/archive_location.h index 598254b45ca3f..2afdb4faa1f25 100644 --- a/impeller/archivist/archive_location.h +++ b/impeller/archivist/archive_location.h @@ -20,7 +20,7 @@ class ArchiveStatement; class ArchiveLocation { public: - Archivable::ArchiveName GetPrimaryKey() const; + PrimaryKey GetPrimaryKey() const; template ::value>> bool Write(ArchiveDef::Member member, T item) { @@ -166,7 +166,7 @@ class ArchiveLocation { Archive& context_; ArchiveStatement& statement_; const ArchiveClassRegistration& registration_; - std::optional name_; + PrimaryKey name_; std::string current_class_; friend class Archive; @@ -174,7 +174,7 @@ class ArchiveLocation { ArchiveLocation(Archive& context, ArchiveStatement& statement, const ArchiveClassRegistration& registration, - std::optional name); + PrimaryKey name); bool WriteIntegral(ArchiveDef::Member member, int64_t item); @@ -182,8 +182,7 @@ class ArchiveLocation { std::optional WriteVectorKeys(std::vector&& members); - bool ReadVectorKeys(Archivable::ArchiveName name, - std::vector& members); + bool ReadVectorKeys(PrimaryKey name, std::vector& members); bool Write(ArchiveDef::Member member, const ArchiveDef& otherDef, diff --git a/impeller/archivist/archive_vector.cc b/impeller/archivist/archive_vector.cc index 92117a1bee746..697d93e32b924 100644 --- a/impeller/archivist/archive_vector.cc +++ b/impeller/archivist/archive_vector.cc @@ -17,13 +17,14 @@ ArchiveVector::ArchiveVector() {} const ArchiveDef ArchiveVector::ArchiveDefinition = { /* .superClass = */ nullptr, - /* .className = */ "Meta_Vector", + /* .className = */ "_IPLR_meta_vector_items_", /* .autoAssignName = */ true, /* .members = */ {0}, }; -Archivable::ArchiveName ArchiveVector::GetArchivePrimaryKey() const { - return 0; +PrimaryKey ArchiveVector::GetPrimaryKey() const { + // Archive definition says the keys will be auto assigned. + return std::nullopt; } const std::vector ArchiveVector::GetKeys() const { diff --git a/impeller/archivist/archive_vector.h b/impeller/archivist/archive_vector.h index 303fb1d9fe468..e7fd59bd156c9 100644 --- a/impeller/archivist/archive_vector.h +++ b/impeller/archivist/archive_vector.h @@ -13,7 +13,7 @@ class ArchiveVector : public Archivable { public: static const ArchiveDef ArchiveDefinition; - ArchiveName GetArchivePrimaryKey() const override; + PrimaryKey GetPrimaryKey() const override; const std::vector GetKeys() const; diff --git a/impeller/archivist/archivist_unittests.cc b/impeller/archivist/archivist_unittests.cc index 241c3fb64513c..5e8d17a5b6106 100644 --- a/impeller/archivist/archivist_unittests.cc +++ b/impeller/archivist/archivist_unittests.cc @@ -14,7 +14,7 @@ namespace impeller { namespace testing { -static Archivable::ArchiveName LastSample = 0; +static int64_t LastSample = 0; class Sample : public Archivable { public: @@ -23,7 +23,7 @@ class Sample : public Archivable { uint64_t GetSomeData() const { return some_data_; } // |Archivable| - ArchiveName GetArchivePrimaryKey() const override { return name_; } + PrimaryKey GetPrimaryKey() const override { return name_; } // |Archivable| bool Write(ArchiveLocation& item) const override { @@ -40,7 +40,7 @@ class Sample : public Archivable { private: uint64_t some_data_; - ArchiveName name_ = ++LastSample; + PrimaryKey name_ = ++LastSample; FML_DISALLOW_COPY_AND_ASSIGN(Sample); }; @@ -87,12 +87,12 @@ TEST_F(ArchiveTest, ReadData) { size_t count = 50; - std::vector keys; + std::vector keys; std::vector values; for (size_t i = 0; i < count; i++) { Sample sample(i + 1); - keys.push_back(sample.GetArchivePrimaryKey()); + keys.push_back(sample.GetPrimaryKey().value()); values.push_back(sample.GetSomeData()); ASSERT_TRUE(archive.Write(sample)); } @@ -110,7 +110,7 @@ TEST_F(ArchiveTest, ReadDataWithNames) { size_t count = 8; - std::vector keys; + std::vector keys; std::vector values; keys.reserve(count); @@ -118,7 +118,7 @@ TEST_F(ArchiveTest, ReadDataWithNames) { for (size_t i = 0; i < count; i++) { Sample sample(i + 1); - keys.push_back(sample.GetArchivePrimaryKey()); + keys.push_back(sample.GetPrimaryKey().value()); values.push_back(sample.GetSomeData()); ASSERT_TRUE(archive.Write(sample)); } @@ -127,7 +127,7 @@ TEST_F(ArchiveTest, ReadDataWithNames) { Sample sample; ASSERT_TRUE(archive.Read(keys[i], sample)); ASSERT_EQ(values[i], sample.GetSomeData()); - ASSERT_EQ(keys[i], sample.GetArchivePrimaryKey()); + ASSERT_EQ(keys[i], sample.GetPrimaryKey()); } } From f84da3e48bce5c44e02ca2e141d169a356cafed7 Mon Sep 17 00:00:00 2001 From: Chinmay Garde Date: Sat, 11 Dec 2021 14:42:23 -0800 Subject: [PATCH 247/433] Minor: Rename primary key variable name. --- impeller/archivist/archive_location.cc | 4 ++-- impeller/archivist/archive_location.h | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/impeller/archivist/archive_location.cc b/impeller/archivist/archive_location.cc index 85080f9bab458..1945d6492c3c9 100644 --- a/impeller/archivist/archive_location.cc +++ b/impeller/archivist/archive_location.cc @@ -16,11 +16,11 @@ ArchiveLocation::ArchiveLocation(Archive& context, : context_(context), statement_(statement), registration_(registration), - name_(name), + primary_key_(name), current_class_(registration.GetClassName()) {} PrimaryKey ArchiveLocation::GetPrimaryKey() const { - return name_; + return primary_key_; } bool ArchiveLocation::Write(ArchiveDef::Member member, diff --git a/impeller/archivist/archive_location.h b/impeller/archivist/archive_location.h index 2afdb4faa1f25..af2188689415c 100644 --- a/impeller/archivist/archive_location.h +++ b/impeller/archivist/archive_location.h @@ -166,7 +166,7 @@ class ArchiveLocation { Archive& context_; ArchiveStatement& statement_; const ArchiveClassRegistration& registration_; - PrimaryKey name_; + PrimaryKey primary_key_; std::string current_class_; friend class Archive; From 9d8f9fdb971e0269c30b160238a1c4c2576128be Mon Sep 17 00:00:00 2001 From: Chinmay Garde Date: Sat, 11 Dec 2021 15:57:30 -0800 Subject: [PATCH 248/433] Support user readable table names. --- impeller/archivist/archivable.h | 8 +- impeller/archivist/archive.h | 18 ++-- .../archivist/archive_class_registration.cc | 87 +++++++----------- .../archivist/archive_class_registration.h | 15 ++-- impeller/archivist/archive_location.cc | 47 +++++----- impeller/archivist/archive_location.h | 88 +++++++------------ impeller/archivist/archive_vector.cc | 16 ++-- impeller/archivist/archive_vector.h | 4 +- impeller/archivist/archivist_unittests.cc | 78 ++++++++++++++-- 9 files changed, 186 insertions(+), 175 deletions(-) diff --git a/impeller/archivist/archivable.h b/impeller/archivist/archivable.h index 783c138920471..5018d5a16e93d 100644 --- a/impeller/archivist/archivable.h +++ b/impeller/archivist/archivable.h @@ -12,13 +12,9 @@ namespace impeller { struct ArchiveDef { - using Member = uint64_t; - using Members = std::vector; - - const ArchiveDef* isa = nullptr; const std::string table_name; const bool auto_key = true; - const Members members; + const std::vector members; }; class ArchiveLocation; @@ -31,6 +27,8 @@ using PrimaryKey = std::optional; /// class Archivable { public: + virtual ~Archivable() = default; + virtual PrimaryKey GetPrimaryKey() const = 0; virtual bool Write(ArchiveLocation& item) const = 0; diff --git a/impeller/archivist/archive.h b/impeller/archivist/archive.h index 18d7b9ad1cd66..c2e2eb7a48184 100644 --- a/impeller/archivist/archive.h +++ b/impeller/archivist/archive.h @@ -27,25 +27,25 @@ class Archive { bool IsValid() const; template ::value>> - bool Write(const T& archivable) { - const ArchiveDef& def = T::ArchiveDefinition; + class = std::enable_if_t::value>> + [[nodiscard]] bool Write(const T& archivable) { + const ArchiveDef& def = T::kArchiveDefinition; return ArchiveInstance(def, archivable).has_value(); } template ::value>> - bool Read(PrimaryKey name, T& archivable) { - const ArchiveDef& def = T::ArchiveDefinition; + class = std::enable_if_t::value>> + [[nodiscard]] bool Read(PrimaryKey name, T& archivable) { + const ArchiveDef& def = T::kArchiveDefinition; return UnarchiveInstance(def, name, archivable); } using UnarchiveStep = std::function; template ::value>> - size_t Read(UnarchiveStep stepper) { - const ArchiveDef& def = T::ArchiveDefinition; + class = std::enable_if_t::value>> + [[nodiscard]] size_t Read(UnarchiveStep stepper) { + const ArchiveDef& def = T::kArchiveDefinition; return UnarchiveInstances(def, stepper); } diff --git a/impeller/archivist/archive_class_registration.cc b/impeller/archivist/archive_class_registration.cc index 10ef0951a8981..0a715eabaf8b4 100644 --- a/impeller/archivist/archive_class_registration.cc +++ b/impeller/archivist/archive_class_registration.cc @@ -11,66 +11,41 @@ namespace impeller { -static const char* const ArchiveColumnPrefix = "col_"; -static const char* const ArchivePrimaryKeyColumnName = "primary_key"; +static constexpr const char* kArchivePrimaryKeyColumnName = "primary_key"; ArchiveClassRegistration::ArchiveClassRegistration(ArchiveDatabase& database, ArchiveDef definition) - : database_(database), class_name_(definition.table_name) { - /* - * Each class in the archive class hierarchy is assigned an entry in the - * class map. - */ - const ArchiveDef* current = &definition; - size_t currentMember = 1; - while (current != nullptr) { - auto membersInCurrent = current->members.size(); - member_count_ += membersInCurrent; - MemberColumnMap map; - for (const auto& member : current->members) { - map[member] = currentMember++; - } - class_map_[current->table_name] = map; - current = current->isa; + : database_(database), definition_(std::move(definition)) { + for (size_t i = 0; i < definition.members.size(); i++) { + // The first index entry is the primary key. So add one to the index. + column_map_[definition.members[i]] = i + 1; } - - is_ready_ = CreateTable(definition.auto_key); + is_valid_ = CreateTable(); } const std::string& ArchiveClassRegistration::GetClassName() const { - return class_name_; + return definition_.table_name; } size_t ArchiveClassRegistration::GetMemberCount() const { - return member_count_; + return column_map_.size(); } bool ArchiveClassRegistration::IsValid() const { - return is_ready_; + return is_valid_; } std::optional ArchiveClassRegistration::FindColumnIndex( - const std::string& className, - ArchiveDef::Member member) const { - auto found = class_map_.find(className); - - if (found == class_map_.end()) { - return std::nullopt; - } - - const auto& memberToColumns = found->second; - - auto foundColumn = memberToColumns.find(member); - - if (foundColumn == memberToColumns.end()) { + const std::string& member) const { + auto found = column_map_.find(member); + if (found == column_map_.end()) { return std::nullopt; } - - return foundColumn->second; + return found->second; } -bool ArchiveClassRegistration::CreateTable(bool autoIncrement) { - if (class_name_.size() == 0 || member_count_ == 0) { +bool ArchiveClassRegistration::CreateTable() { + if (definition_.table_name.empty() || definition_.members.empty()) { return false; } @@ -80,16 +55,17 @@ bool ArchiveClassRegistration::CreateTable(bool autoIncrement) { * Table names cannot participate in parameter substitution, so we prepare * a statement and check its validity before running. */ - stream << "CREATE TABLE IF NOT EXISTS " << class_name_.c_str() << " (" - << ArchivePrimaryKeyColumnName; + stream << "CREATE TABLE IF NOT EXISTS " << definition_.table_name << " (" + << kArchivePrimaryKeyColumnName; - if (autoIncrement) { + if (definition_.auto_key) { stream << " INTEGER PRIMARY KEY AUTOINCREMENT, "; } else { stream << " INTEGER PRIMARY KEY, "; } - for (size_t i = 0, columns = member_count_; i < columns; i++) { - stream << ArchiveColumnPrefix << std::to_string(i + 1); + + for (size_t i = 0, columns = definition_.members.size(); i < columns; i++) { + stream << definition_.members[i]; if (i != columns - 1) { stream << ", "; } @@ -112,19 +88,19 @@ bool ArchiveClassRegistration::CreateTable(bool autoIncrement) { ArchiveStatement ArchiveClassRegistration::CreateQueryStatement( bool single) const { std::stringstream stream; - stream << "SELECT " << ArchivePrimaryKeyColumnName << ", "; - for (size_t i = 0, members = member_count_; i < members; i++) { - stream << ArchiveColumnPrefix << std::to_string(i + 1); - if (i != members - 1) { + stream << "SELECT " << kArchivePrimaryKeyColumnName << ", "; + for (size_t i = 0, columns = definition_.members.size(); i < columns; i++) { + stream << definition_.members[i]; + if (i != columns - 1) { stream << ","; } } - stream << " FROM " << class_name_; + stream << " FROM " << definition_.table_name; if (single) { - stream << " WHERE " << ArchivePrimaryKeyColumnName << " = ?"; + stream << " WHERE " << kArchivePrimaryKeyColumnName << " = ?"; } else { - stream << " ORDER BY " << ArchivePrimaryKeyColumnName << " ASC"; + stream << " ORDER BY " << kArchivePrimaryKeyColumnName << " ASC"; } stream << ";"; @@ -134,10 +110,11 @@ ArchiveStatement ArchiveClassRegistration::CreateQueryStatement( ArchiveStatement ArchiveClassRegistration::CreateInsertStatement() const { std::stringstream stream; - stream << "INSERT OR REPLACE INTO " << class_name_ << " VALUES ( ?, "; - for (size_t i = 0; i < member_count_; i++) { + stream << "INSERT OR REPLACE INTO " << definition_.table_name + << " VALUES ( ?, "; + for (size_t i = 0, columns = definition_.members.size(); i < columns; i++) { stream << "?"; - if (i != member_count_ - 1) { + if (i != columns - 1) { stream << ", "; } } diff --git a/impeller/archivist/archive_class_registration.h b/impeller/archivist/archive_class_registration.h index eec4ecf2ea1ca..d290a46f5bbb2 100644 --- a/impeller/archivist/archive_class_registration.h +++ b/impeller/archivist/archive_class_registration.h @@ -17,8 +17,7 @@ class ArchiveClassRegistration { bool IsValid() const; - std::optional FindColumnIndex(const std::string& className, - ArchiveDef::Member member) const; + std::optional FindColumnIndex(const std::string& member) const; const std::string& GetClassName() const; @@ -29,20 +28,18 @@ class ArchiveClassRegistration { ArchiveStatement CreateQueryStatement(bool single) const; private: - using MemberColumnMap = std::map; - using ClassMap = std::map; + using MemberColumnMap = std::map; friend class ArchiveDatabase; ArchiveClassRegistration(ArchiveDatabase& database, ArchiveDef definition); - bool CreateTable(bool autoIncrement); + bool CreateTable(); ArchiveDatabase& database_; - ClassMap class_map_; - std::string class_name_; - size_t member_count_ = 0; - bool is_ready_ = false; + const ArchiveDef definition_; + MemberColumnMap column_map_; + bool is_valid_ = false; FML_DISALLOW_COPY_AND_ASSIGN(ArchiveClassRegistration); }; diff --git a/impeller/archivist/archive_location.cc b/impeller/archivist/archive_location.cc index 1945d6492c3c9..906174809b712 100644 --- a/impeller/archivist/archive_location.cc +++ b/impeller/archivist/archive_location.cc @@ -16,38 +16,37 @@ ArchiveLocation::ArchiveLocation(Archive& context, : context_(context), statement_(statement), registration_(registration), - primary_key_(name), - current_class_(registration.GetClassName()) {} + primary_key_(name) {} PrimaryKey ArchiveLocation::GetPrimaryKey() const { return primary_key_; } -bool ArchiveLocation::Write(ArchiveDef::Member member, +bool ArchiveLocation::Write(const std::string& member, const std::string& item) { - auto index = registration_.FindColumnIndex(current_class_, member); + auto index = registration_.FindColumnIndex(member); return index.has_value() ? statement_.WriteValue(index.value(), item) : false; } -bool ArchiveLocation::WriteIntegral(ArchiveDef::Member member, int64_t item) { - auto index = registration_.FindColumnIndex(current_class_, member); +bool ArchiveLocation::WriteIntegral(const std::string& member, int64_t item) { + auto index = registration_.FindColumnIndex(member); return index.has_value() ? statement_.WriteValue(index.value(), item) : false; } -bool ArchiveLocation::Write(ArchiveDef::Member member, double item) { - auto index = registration_.FindColumnIndex(current_class_, member); +bool ArchiveLocation::Write(const std::string& member, double item) { + auto index = registration_.FindColumnIndex(member); return index.has_value() ? statement_.WriteValue(index.value(), item) : false; } -bool ArchiveLocation::Write(ArchiveDef::Member member, const Allocation& item) { - auto index = registration_.FindColumnIndex(current_class_, member); +bool ArchiveLocation::Write(const std::string& member, const Allocation& item) { + auto index = registration_.FindColumnIndex(member); return index.has_value() ? statement_.WriteValue(index.value(), item) : false; } -bool ArchiveLocation::Write(ArchiveDef::Member member, +bool ArchiveLocation::Write(const std::string& member, const ArchiveDef& otherDef, const Archivable& other) { - auto index = registration_.FindColumnIndex(current_class_, member); + auto index = registration_.FindColumnIndex(member); if (!index.has_value()) { return false; @@ -76,13 +75,13 @@ bool ArchiveLocation::Write(ArchiveDef::Member member, std::optional ArchiveLocation::WriteVectorKeys( std::vector&& members) { ArchiveVector vector(std::move(members)); - return context_.ArchiveInstance(ArchiveVector::ArchiveDefinition, vector); + return context_.ArchiveInstance(ArchiveVector::kArchiveDefinition, vector); } bool ArchiveLocation::ReadVectorKeys(PrimaryKey name, std::vector& members) { ArchiveVector vector; - if (!context_.UnarchiveInstance(ArchiveVector::ArchiveDefinition, name, + if (!context_.UnarchiveInstance(ArchiveVector::kArchiveDefinition, name, vector)) { return false; } @@ -91,30 +90,30 @@ bool ArchiveLocation::ReadVectorKeys(PrimaryKey name, return true; } -bool ArchiveLocation::Read(ArchiveDef::Member member, std::string& item) { - auto index = registration_.FindColumnIndex(current_class_, member); +bool ArchiveLocation::Read(const std::string& member, std::string& item) { + auto index = registration_.FindColumnIndex(member); return index.has_value() ? statement_.ReadValue(index.value(), item) : false; } -bool ArchiveLocation::ReadIntegral(ArchiveDef::Member member, int64_t& item) { - auto index = registration_.FindColumnIndex(current_class_, member); +bool ArchiveLocation::ReadIntegral(const std::string& member, int64_t& item) { + auto index = registration_.FindColumnIndex(member); return index.has_value() ? statement_.ReadValue(index.value(), item) : false; } -bool ArchiveLocation::Read(ArchiveDef::Member member, double& item) { - auto index = registration_.FindColumnIndex(current_class_, member); +bool ArchiveLocation::Read(const std::string& member, double& item) { + auto index = registration_.FindColumnIndex(member); return index.has_value() ? statement_.ReadValue(index.value(), item) : false; } -bool ArchiveLocation::Read(ArchiveDef::Member member, Allocation& item) { - auto index = registration_.FindColumnIndex(current_class_, member); +bool ArchiveLocation::Read(const std::string& member, Allocation& item) { + auto index = registration_.FindColumnIndex(member); return index.has_value() ? statement_.ReadValue(index.value(), item) : false; } -bool ArchiveLocation::Read(ArchiveDef::Member member, +bool ArchiveLocation::Read(const std::string& member, const ArchiveDef& otherDef, Archivable& other) { - auto index = registration_.FindColumnIndex(current_class_, member); + auto index = registration_.FindColumnIndex(member); /* * Make sure a member is present at that column diff --git a/impeller/archivist/archive_location.h b/impeller/archivist/archive_location.h index af2188689415c..63428dfa94dfe 100644 --- a/impeller/archivist/archive_location.h +++ b/impeller/archivist/archive_location.h @@ -22,46 +22,45 @@ class ArchiveLocation { public: PrimaryKey GetPrimaryKey() const; - template ::value>> - bool Write(ArchiveDef::Member member, T item) { + template ::value>> + bool Write(const std::string& member, T item) { return WriteIntegral(member, static_cast(item)); } - bool Write(ArchiveDef::Member member, double item); + bool Write(const std::string& member, double item); - bool Write(ArchiveDef::Member member, const std::string& item); + bool Write(const std::string& member, const std::string& item); - bool Write(ArchiveDef::Member member, const Allocation& allocation); + bool Write(const std::string& member, const Allocation& allocation); template ::value>> - bool WriteArchivable(ArchiveDef::Member member, const T& other) { + class = std::enable_if_t::value>> + bool WriteArchivable(const std::string& member, const T& other) { const ArchiveDef& otherDef = T::ArchiveDefinition; return Write(member, otherDef, other); } - template ::value>> - bool WriteEnum(ArchiveDef::Member member, const T& item) { + template ::value>> + bool WriteEnum(const std::string& member, const T& item) { return WriteIntegral(member, static_cast(item)); } template ::value>> - bool Write(ArchiveDef::Member member, const std::vector& items) { + class = std::enable_if_t::value>> + bool Write(const std::string& member, const std::vector& items) { /* * All items in the vector are individually encoded and their keys noted */ std::vector members; members.reserve(items.size()); - const ArchiveDef& itemDefinition = T::ArchiveDefinition; + const ArchiveDef& itemDefinition = T::kArchiveDefinition; for (const auto& item : items) { - int64_t added = 0; - bool result = context_.ArchiveInstance(itemDefinition, item, added); - if (!result) { + auto row_id = context_.ArchiveInstance(itemDefinition, item); + if (!row_id.has_value()) { return false; } - members.emplace_back(added); + members.emplace_back(row_id.value()); } /* @@ -76,41 +75,29 @@ class ArchiveLocation { return WriteIntegral(member, vectorInsert.value()); } - template ::value && - std::is_base_of::value>> - bool WriteSuper(const Current& thiz) { - std::string oldClass = current_class_; - current_class_ = Super::ArchiveDefinition.className; - auto success = thiz.Super::serialize(*this); - current_class_ = oldClass; - return success; - } - - template ::value>> - bool Read(ArchiveDef::Member member, T& item) { + template ::value>> + bool Read(const std::string& member, T& item) { int64_t decoded = 0; auto result = ReadIntegral(member, decoded); item = static_cast(decoded); return result; } - bool Read(ArchiveDef::Member member, double& item); + bool Read(const std::string& member, double& item); - bool Read(ArchiveDef::Member member, std::string& item); + bool Read(const std::string& member, std::string& item); - bool Read(ArchiveDef::Member member, Allocation& allocation); + bool Read(const std::string& member, Allocation& allocation); template ::value>> - bool ReadArchivable(ArchiveDef::Member member, T& other) { + class = std::enable_if_t::value>> + bool ReadArchivable(const std::string& member, T& other) { const ArchiveDef& otherDef = T::ArchiveDefinition; return decode(member, otherDef, other); } - template ::value>> - bool ReadEnum(ArchiveDef::Member member, T& item) { + template ::value>> + bool ReadEnum(const std::string& member, T& item) { int64_t desugared = 0; if (ReadIntegral(member, desugared)) { item = static_cast(desugared); @@ -120,8 +107,8 @@ class ArchiveLocation { } template ::value>> - bool Read(ArchiveDef::Member member, std::vector& items) { + class = std::enable_if_t::value>> + bool Read(const std::string& member, std::vector& items) { /* * From the member, find the foreign key of the vector */ @@ -138,7 +125,7 @@ class ArchiveLocation { return false; } - const ArchiveDef& otherDef = T::ArchiveDefinition; + const ArchiveDef& otherDef = T::kArchiveDefinition; for (const auto& key : keys) { items.emplace_back(); @@ -150,24 +137,11 @@ class ArchiveLocation { return true; } - template ::value && - std::is_base_of::value>> - bool ReadSuper(Current& thiz) { - std::string oldClass = current_class_; - current_class_ = Super::ArchiveDefinition.className; - auto success = thiz.Super::deserialize(*this); - current_class_ = oldClass; - return success; - } - private: Archive& context_; ArchiveStatement& statement_; const ArchiveClassRegistration& registration_; PrimaryKey primary_key_; - std::string current_class_; friend class Archive; @@ -176,19 +150,19 @@ class ArchiveLocation { const ArchiveClassRegistration& registration, PrimaryKey name); - bool WriteIntegral(ArchiveDef::Member member, int64_t item); + bool WriteIntegral(const std::string& member, int64_t item); - bool ReadIntegral(ArchiveDef::Member member, int64_t& item); + bool ReadIntegral(const std::string& member, int64_t& item); std::optional WriteVectorKeys(std::vector&& members); bool ReadVectorKeys(PrimaryKey name, std::vector& members); - bool Write(ArchiveDef::Member member, + bool Write(const std::string& member, const ArchiveDef& otherDef, const Archivable& other); - bool Read(ArchiveDef::Member member, + bool Read(const std::string& member, const ArchiveDef& otherDef, Archivable& other); diff --git a/impeller/archivist/archive_vector.cc b/impeller/archivist/archive_vector.cc index 697d93e32b924..57a83724aa154 100644 --- a/impeller/archivist/archive_vector.cc +++ b/impeller/archivist/archive_vector.cc @@ -10,16 +10,16 @@ namespace impeller { -ArchiveVector::ArchiveVector(std::vector&& keys) +ArchiveVector::ArchiveVector(std::vector keys) : keys_(std::move(keys)) {} ArchiveVector::ArchiveVector() {} -const ArchiveDef ArchiveVector::ArchiveDefinition = { - /* .superClass = */ nullptr, - /* .className = */ "_IPLR_meta_vector_items_", - /* .autoAssignName = */ true, - /* .members = */ {0}, +static constexpr const char* kVectorKeys = "keys"; + +ArchiveDef ArchiveVector::kArchiveDefinition = { + .table_name = "IPLR_vectors", + .members = {kVectorKeys}, }; PrimaryKey ArchiveVector::GetPrimaryKey() const { @@ -39,12 +39,12 @@ bool ArchiveVector::Write(ArchiveLocation& item) const { stream << ","; } } - return item.Write(0, stream.str()); + return item.Write(kVectorKeys, stream.str()); } bool ArchiveVector::Read(ArchiveLocation& item) { std::string flattened; - if (!item.Read(0, flattened)) { + if (!item.Read(kVectorKeys, flattened)) { return false; } diff --git a/impeller/archivist/archive_vector.h b/impeller/archivist/archive_vector.h index e7fd59bd156c9..556dd3c31c21a 100644 --- a/impeller/archivist/archive_vector.h +++ b/impeller/archivist/archive_vector.h @@ -11,7 +11,7 @@ namespace impeller { class ArchiveVector : public Archivable { public: - static const ArchiveDef ArchiveDefinition; + static ArchiveDef kArchiveDefinition; PrimaryKey GetPrimaryKey() const override; @@ -28,7 +28,7 @@ class ArchiveVector : public Archivable { ArchiveVector(); - ArchiveVector(std::vector&& keys); + ArchiveVector(std::vector keys); FML_DISALLOW_COPY_AND_ASSIGN(ArchiveVector); }; diff --git a/impeller/archivist/archivist_unittests.cc b/impeller/archivist/archivist_unittests.cc index 5e8d17a5b6106..fdf9608e451a0 100644 --- a/impeller/archivist/archivist_unittests.cc +++ b/impeller/archivist/archivist_unittests.cc @@ -20,6 +20,8 @@ class Sample : public Archivable { public: Sample(uint64_t count = 42) : some_data_(count) {} + Sample(Sample&&) = default; + uint64_t GetSomeData() const { return some_data_; } // |Archivable| @@ -27,16 +29,16 @@ class Sample : public Archivable { // |Archivable| bool Write(ArchiveLocation& item) const override { - return item.Write(999, some_data_); + return item.Write("some_data", some_data_); }; // |Archivable| bool Read(ArchiveLocation& item) override { name_ = item.GetPrimaryKey(); - return item.Read(999, some_data_); + return item.Read("some_data", some_data_); }; - static const ArchiveDef ArchiveDefinition; + static const ArchiveDef kArchiveDefinition; private: uint64_t some_data_; @@ -45,11 +47,58 @@ class Sample : public Archivable { FML_DISALLOW_COPY_AND_ASSIGN(Sample); }; -const ArchiveDef Sample::ArchiveDefinition = { - .isa = nullptr, +const ArchiveDef Sample::kArchiveDefinition = { .table_name = "Sample", .auto_key = false, - .members = {999}, + .members = {"some_data"}, +}; + +class SampleWithVector : public Archivable { + public: + SampleWithVector() = default; + + // |Archivable| + PrimaryKey GetPrimaryKey() const override { return std::nullopt; } + + // |Archivable| + bool Write(ArchiveLocation& item) const override { + std::vector samples; + for (size_t i = 0; i < 50u; i++) { + samples.emplace_back(Sample{1988 + i}); + } + return item.Write("hello", "world") && item.Write("samples", samples); + }; + + // |Archivable| + bool Read(ArchiveLocation& item) override { + std::string str; + auto str_result = item.Read("hello", str); + std::vector samples; + auto vec_result = item.Read("samples", samples); + + if (!str_result || str != "world" || !vec_result || samples.size() != 50) { + return false; + } + + size_t current = 1988; + for (const auto& sample : samples) { + if (sample.GetSomeData() != current++) { + return false; + } + } + return true; + }; + + static const ArchiveDef kArchiveDefinition; + + private: + std::vector samples_; + FML_DISALLOW_COPY_AND_ASSIGN(SampleWithVector); +}; + +const ArchiveDef SampleWithVector::kArchiveDefinition = { + .table_name = "SampleWithVector", + .members = {"hello", "samples"}, }; using ArchiveTest = ArchivistFixture; @@ -131,5 +180,22 @@ TEST_F(ArchiveTest, ReadDataWithNames) { } } +TEST_F(ArchiveTest, CanReadWriteVectorOfArchivables) { + Archive archive(GetArchiveFileName().c_str()); + ASSERT_TRUE(archive.IsValid()); + + SampleWithVector sample_with_vector; + ASSERT_TRUE(archive.Write(sample_with_vector)); + bool read_success = false; + ASSERT_EQ( + archive.Read([&](ArchiveLocation& location) -> bool { + SampleWithVector other_sample_with_vector; + read_success = other_sample_with_vector.Read(location); + return true; // Always keep continuing but assert that we only get one. + }), + 1u); + ASSERT_TRUE(read_success); +} + } // namespace testing } // namespace impeller From 80ee43d0477eb40b99940ef70fe79054131313f7 Mon Sep 17 00:00:00 2001 From: Chinmay Garde Date: Sun, 12 Dec 2021 13:54:46 -0800 Subject: [PATCH 249/433] Add error message for incorrect member lookup. --- impeller/archivist/archive_class_registration.cc | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/impeller/archivist/archive_class_registration.cc b/impeller/archivist/archive_class_registration.cc index 0a715eabaf8b4..335f3acd0e86e 100644 --- a/impeller/archivist/archive_class_registration.cc +++ b/impeller/archivist/archive_class_registration.cc @@ -8,6 +8,7 @@ #include "impeller/archivist/archive_database.h" #include "impeller/archivist/archive_statement.h" +#include "impeller/base/validation.h" namespace impeller { @@ -39,6 +40,9 @@ std::optional ArchiveClassRegistration::FindColumnIndex( const std::string& member) const { auto found = column_map_.find(member); if (found == column_map_.end()) { + VALIDATION_LOG << "No member named '" << member << "' in class '" + << definition_.table_name + << "'. Did you forget to register it?"; return std::nullopt; } return found->second; From cdba383acf831437d2c6136e1635d2f8816c563f Mon Sep 17 00:00:00 2001 From: Chinmay Garde Date: Sun, 12 Dec 2021 14:00:35 -0800 Subject: [PATCH 250/433] Remove superfluous auto_key from ArchiveDef. --- impeller/archivist/archivable.h | 1 - impeller/archivist/archive.cc | 13 ++++--------- impeller/archivist/archive_class_registration.cc | 8 +------- impeller/archivist/archivist_unittests.cc | 1 - 4 files changed, 5 insertions(+), 18 deletions(-) diff --git a/impeller/archivist/archivable.h b/impeller/archivist/archivable.h index 5018d5a16e93d..41c296301d20a 100644 --- a/impeller/archivist/archivable.h +++ b/impeller/archivist/archivable.h @@ -13,7 +13,6 @@ namespace impeller { struct ArchiveDef { const std::string table_name; - const bool auto_key = true; const std::vector members; }; diff --git a/impeller/archivist/archive.cc b/impeller/archivist/archive.cc index c10646e081e3d..8cf81ddb65ec4 100644 --- a/impeller/archivist/archive.cc +++ b/impeller/archivist/archive.cc @@ -53,12 +53,6 @@ std::optional Archive::ArchiveInstance( auto primary_key = archivable.GetPrimaryKey(); - if (!definition.auto_key && !primary_key.has_value()) { - VALIDATION_LOG << "Archive definition specified that primary keys will be " - "explicitly specified but none was provided when asked."; - return std::nullopt; - } - /* * The lifecycle of the archive item is tied to this scope and there is no * way for the user to create an instance of an archive item. So its safe @@ -68,9 +62,10 @@ std::optional Archive::ArchiveInstance( ArchiveLocation item(*this, statement, *registration, primary_key); /* - * We need to bind the primary key only if the item does not provide its own + * If the item provides its own primary key, we need to bind it now. + * Otherwise, one will be automatically assigned to it. */ - if (!definition.auto_key && + if (primary_key.has_value() && !statement.WriteValue(ArchiveClassRegistration::kPrimaryKeyIndex, primary_key.value())) { return std::nullopt; @@ -86,7 +81,7 @@ std::optional Archive::ArchiveInstance( int64_t lastInsert = database_->GetLastInsertRowID(); - if (!definition.auto_key && + if (primary_key.has_value() && lastInsert != static_cast(primary_key.value())) { return std::nullopt; } diff --git a/impeller/archivist/archive_class_registration.cc b/impeller/archivist/archive_class_registration.cc index 335f3acd0e86e..33b3695c61c7e 100644 --- a/impeller/archivist/archive_class_registration.cc +++ b/impeller/archivist/archive_class_registration.cc @@ -60,13 +60,7 @@ bool ArchiveClassRegistration::CreateTable() { * a statement and check its validity before running. */ stream << "CREATE TABLE IF NOT EXISTS " << definition_.table_name << " (" - << kArchivePrimaryKeyColumnName; - - if (definition_.auto_key) { - stream << " INTEGER PRIMARY KEY AUTOINCREMENT, "; - } else { - stream << " INTEGER PRIMARY KEY, "; - } + << kArchivePrimaryKeyColumnName << " INTEGER PRIMARY KEY, "; for (size_t i = 0, columns = definition_.members.size(); i < columns; i++) { stream << definition_.members[i]; diff --git a/impeller/archivist/archivist_unittests.cc b/impeller/archivist/archivist_unittests.cc index fdf9608e451a0..d789034c3fde7 100644 --- a/impeller/archivist/archivist_unittests.cc +++ b/impeller/archivist/archivist_unittests.cc @@ -49,7 +49,6 @@ class Sample : public Archivable { const ArchiveDef Sample::kArchiveDefinition = { .table_name = "Sample", - .auto_key = false, .members = {"some_data"}, }; From 40189f2ca7615c1ed9752dfb2435d3526cd1ef85 Mon Sep 17 00:00:00 2001 From: Chinmay Garde Date: Sun, 12 Dec 2021 14:12:40 -0800 Subject: [PATCH 251/433] Cleanup macros in archive statement. --- impeller/archivist/archive_statement.cc | 90 ++++++++++++++----------- impeller/archivist/archive_statement.h | 7 +- 2 files changed, 55 insertions(+), 42 deletions(-) diff --git a/impeller/archivist/archive_statement.cc b/impeller/archivist/archive_statement.cc index 976ab47a8e894..ddc6c4f4189c2 100644 --- a/impeller/archivist/archive_statement.cc +++ b/impeller/archivist/archive_statement.cc @@ -11,42 +11,55 @@ namespace impeller { -#define STATEMENT_HANDLE reinterpret_cast<::sqlite3_stmt*>(statement_handle_) - -ArchiveStatement::ArchiveStatement(void* db, const std::string& statememt) { - ::sqlite3_stmt* statementHandle = nullptr; - auto res = ::sqlite3_prepare_v2(reinterpret_cast(db), // - statememt.c_str(), // - static_cast(statememt.size()), // - &statementHandle, // - nullptr); - statement_handle_ = statementHandle; - ready_ = res == SQLITE_OK && statement_handle_ != nullptr; -} - -ArchiveStatement::ArchiveStatement(ArchiveStatement&& other) - : statement_handle_(other.statement_handle_), ready_(other.ready_) { - other.statement_handle_ = nullptr; - other.ready_ = false; -} +struct ArchiveStatement::Handle { + Handle(void* db, const std::string& statememt) { + ::sqlite3_stmt* handle = nullptr; + if (::sqlite3_prepare_v2(reinterpret_cast(db), // + statememt.c_str(), // + static_cast(statememt.size()), // + &handle, // + nullptr) == SQLITE_OK) { + handle_ = handle; + } + } -ArchiveStatement::~ArchiveStatement() { - if (statement_handle_ != nullptr) { - auto res = ::sqlite3_finalize(STATEMENT_HANDLE); + ~Handle() { + if (handle_ == nullptr) { + return; + } + auto res = ::sqlite3_finalize(handle_); FML_CHECK(res == SQLITE_OK) << "Unable to finalize the archive."; } + + bool IsValid() { return handle_ != nullptr; } + + ::sqlite3_stmt* Get() const { return handle_; } + + private: + ::sqlite3_stmt* handle_; + + FML_DISALLOW_COPY_AND_ASSIGN(Handle); +}; + +ArchiveStatement::ArchiveStatement(void* db, const std::string& statememt) + : statement_handle_(std::make_unique(db, statememt)) { + if (!statement_handle_->IsValid()) { + statement_handle_.reset(); + } } +ArchiveStatement::~ArchiveStatement() = default; + bool ArchiveStatement::IsValid() const { - return ready_; + return statement_handle_ != nullptr; } bool ArchiveStatement::Reset() { - if (::sqlite3_reset(STATEMENT_HANDLE) != SQLITE_OK) { + if (::sqlite3_reset(statement_handle_->Get()) != SQLITE_OK) { return false; } - if (::sqlite3_clear_bindings(STATEMENT_HANDLE) != SQLITE_OK) { + if (::sqlite3_clear_bindings(statement_handle_->Get()) != SQLITE_OK) { return false; } @@ -68,14 +81,14 @@ static constexpr int ToColumn(size_t index) { } size_t ArchiveStatement::GetColumnCount() { - return ::sqlite3_column_count(STATEMENT_HANDLE); + return ::sqlite3_column_count(statement_handle_->Get()); } /* * Bind Variants */ bool ArchiveStatement::WriteValue(size_t index, const std::string& item) { - return ::sqlite3_bind_text(STATEMENT_HANDLE, // + return ::sqlite3_bind_text(statement_handle_->Get(), // ToParam(index), // item.data(), // static_cast(item.size()), // @@ -83,19 +96,19 @@ bool ArchiveStatement::WriteValue(size_t index, const std::string& item) { } bool ArchiveStatement::BindIntegral(size_t index, int64_t item) { - return ::sqlite3_bind_int64(STATEMENT_HANDLE, // - ToParam(index), // + return ::sqlite3_bind_int64(statement_handle_->Get(), // + ToParam(index), // item) == SQLITE_OK; } bool ArchiveStatement::WriteValue(size_t index, double item) { - return ::sqlite3_bind_double(STATEMENT_HANDLE, // - ToParam(index), // + return ::sqlite3_bind_double(statement_handle_->Get(), // + ToParam(index), // item) == SQLITE_OK; } bool ArchiveStatement::WriteValue(size_t index, const Allocation& item) { - return ::sqlite3_bind_blob(STATEMENT_HANDLE, // + return ::sqlite3_bind_blob(statement_handle_->Get(), // ToParam(index), // item.GetBuffer(), // static_cast(item.GetLength()), // @@ -106,12 +119,12 @@ bool ArchiveStatement::WriteValue(size_t index, const Allocation& item) { * Column Variants */ bool ArchiveStatement::ColumnIntegral(size_t index, int64_t& item) { - item = ::sqlite3_column_int64(STATEMENT_HANDLE, ToColumn(index)); + item = ::sqlite3_column_int64(statement_handle_->Get(), ToColumn(index)); return true; } bool ArchiveStatement::ReadValue(size_t index, double& item) { - item = ::sqlite3_column_double(STATEMENT_HANDLE, ToColumn(index)); + item = ::sqlite3_column_double(statement_handle_->Get(), ToColumn(index)); return true; } @@ -128,13 +141,13 @@ bool ArchiveStatement::ReadValue(size_t index, std::string& item) { * Get the character data */ auto chars = reinterpret_cast( - ::sqlite3_column_text(STATEMENT_HANDLE, ToColumn(index))); + ::sqlite3_column_text(statement_handle_->Get(), ToColumn(index))); /* * Get the length of the string (in bytes) */ size_t textByteSize = - ::sqlite3_column_bytes(STATEMENT_HANDLE, ToColumn(index)); + ::sqlite3_column_bytes(statement_handle_->Get(), ToColumn(index)); std::string text(chars, textByteSize); item.swap(text); @@ -147,12 +160,13 @@ bool ArchiveStatement::ReadValue(size_t index, Allocation& item) { * Get a blob pointer */ auto blob = reinterpret_cast( - ::sqlite3_column_blob(STATEMENT_HANDLE, ToColumn(index))); + ::sqlite3_column_blob(statement_handle_->Get(), ToColumn(index))); /* * Decode the number of bytes in the blob */ - size_t byteSize = ::sqlite3_column_bytes(STATEMENT_HANDLE, ToColumn(index)); + size_t byteSize = + ::sqlite3_column_bytes(statement_handle_->Get(), ToColumn(index)); /* * Reszie the host allocation and move the blob contents into it @@ -166,7 +180,7 @@ bool ArchiveStatement::ReadValue(size_t index, Allocation& item) { } ArchiveStatement::Result ArchiveStatement::Execute() { - switch (::sqlite3_step(STATEMENT_HANDLE)) { + switch (::sqlite3_step(statement_handle_->Get())) { case SQLITE_DONE: return Result::kDone; case SQLITE_ROW: diff --git a/impeller/archivist/archive_statement.h b/impeller/archivist/archive_statement.h index 35abbb1d73a3c..34856c7f24218 100644 --- a/impeller/archivist/archive_statement.h +++ b/impeller/archivist/archive_statement.h @@ -4,6 +4,7 @@ #pragma once +#include #include #include "flutter/fml/macros.h" @@ -20,8 +21,6 @@ class ArchiveStatement { public: ~ArchiveStatement(); - ArchiveStatement(ArchiveStatement&& message); - bool IsValid() const; enum class Result { @@ -80,8 +79,8 @@ class ArchiveStatement { size_t GetColumnCount(); private: - void* statement_handle_ = nullptr; - bool ready_ = false; + struct Handle; + std::unique_ptr statement_handle_; friend class ArchiveDatabase; From b6720c72faa53ba9b7d15f9879c6e9c3479aeb59 Mon Sep 17 00:00:00 2001 From: Chinmay Garde Date: Sun, 12 Dec 2021 14:27:47 -0800 Subject: [PATCH 252/433] Remove remaining C-Style casts. --- impeller/archivist/archive_database.cc | 64 +++++++++++++++++-------- impeller/archivist/archive_database.h | 4 +- impeller/archivist/archive_statement.cc | 40 +++++++++++++++- 3 files changed, 84 insertions(+), 24 deletions(-) diff --git a/impeller/archivist/archive_database.cc b/impeller/archivist/archive_database.cc index d58a7acbc7c41..06f6bf0624928 100644 --- a/impeller/archivist/archive_database.cc +++ b/impeller/archivist/archive_database.cc @@ -16,56 +16,80 @@ namespace impeller { -#define DB_HANDLE reinterpret_cast(database_) +struct ArchiveDatabase::Handle { + Handle(const std::string& filename) { + if (::sqlite3_initialize() != SQLITE_OK) { + VALIDATION_LOG << "Could not initialize sqlite."; + return; + } -ArchiveDatabase::ArchiveDatabase(const std::string& filename) { - if (::sqlite3_initialize() != SQLITE_OK) { - VALIDATION_LOG << "Could not initialize sqlite."; - return; + sqlite3* db = nullptr; + auto res = ::sqlite3_open(filename.c_str(), &db); + + if (res != SQLITE_OK || db == nullptr) { + return; + } + + handle_ = db; } - sqlite3* db = nullptr; - auto res = ::sqlite3_open(filename.c_str(), &db); - database_ = db; + ~Handle() { + if (handle_ == nullptr) { + return; + } + ::sqlite3_close(handle_); + } + + ::sqlite3* Get() const { return handle_; } + + bool IsValid() const { return handle_ != nullptr; } - if (res != SQLITE_OK || database_ == nullptr) { + private: + ::sqlite3* handle_ = nullptr; + + FML_DISALLOW_COPY_AND_ASSIGN(Handle); +}; + +ArchiveDatabase::ArchiveDatabase(const std::string& filename) + : handle_(std::make_unique(filename)) { + if (!handle_->IsValid()) { + handle_.reset(); return; } begin_transaction_stmt_ = std::unique_ptr( - new ArchiveStatement(database_, "BEGIN TRANSACTION;")); + new ArchiveStatement(handle_->Get(), "BEGIN TRANSACTION;")); if (!begin_transaction_stmt_->IsValid()) { return; } end_transaction_stmt_ = std::unique_ptr( - new ArchiveStatement(database_, "END TRANSACTION;")); + new ArchiveStatement(handle_->Get(), "END TRANSACTION;")); if (!end_transaction_stmt_->IsValid()) { return; } rollback_transaction_stmt_ = std::unique_ptr( - new ArchiveStatement(database_, "ROLLBACK TRANSACTION;")); + new ArchiveStatement(handle_->Get(), "ROLLBACK TRANSACTION;")); if (!rollback_transaction_stmt_->IsValid()) { return; } - - ready_ = true; } -ArchiveDatabase::~ArchiveDatabase() { - ::sqlite3_close(DB_HANDLE); -} +ArchiveDatabase::~ArchiveDatabase() = default; bool ArchiveDatabase::IsValid() const { - return ready_; + return handle_ != nullptr; } int64_t ArchiveDatabase::GetLastInsertRowID() { - return ::sqlite3_last_insert_rowid(DB_HANDLE); + if (!IsValid()) { + return 0u; + } + return ::sqlite3_last_insert_rowid(handle_->Get()); } static inline const ArchiveClassRegistration* RegistrationIfReady( @@ -103,7 +127,7 @@ const ArchiveClassRegistration* ArchiveDatabase::GetRegistrationForDefinition( ArchiveStatement ArchiveDatabase::CreateStatement( const std::string& statementString) const { - return ArchiveStatement{database_, statementString}; + return ArchiveStatement{handle_ ? handle_->Get() : nullptr, statementString}; } ArchiveTransaction ArchiveDatabase::CreateTransaction( diff --git a/impeller/archivist/archive_database.h b/impeller/archivist/archive_database.h index ed1b4480170f1..5a99a322bd811 100644 --- a/impeller/archivist/archive_database.h +++ b/impeller/archivist/archive_database.h @@ -35,8 +35,8 @@ class ArchiveDatabase { ArchiveTransaction CreateTransaction(int64_t& transactionCount); private: - void* database_ = nullptr; - bool ready_ = false; + struct Handle; + std::unique_ptr handle_; std::map> registrations_; std::unique_ptr begin_transaction_stmt_; diff --git a/impeller/archivist/archive_statement.cc b/impeller/archivist/archive_statement.cc index ddc6c4f4189c2..d6f6f5722fc5a 100644 --- a/impeller/archivist/archive_statement.cc +++ b/impeller/archivist/archive_statement.cc @@ -13,6 +13,9 @@ namespace impeller { struct ArchiveStatement::Handle { Handle(void* db, const std::string& statememt) { + if (db == nullptr) { + return; + } ::sqlite3_stmt* handle = nullptr; if (::sqlite3_prepare_v2(reinterpret_cast(db), // statememt.c_str(), // @@ -31,12 +34,12 @@ struct ArchiveStatement::Handle { FML_CHECK(res == SQLITE_OK) << "Unable to finalize the archive."; } - bool IsValid() { return handle_ != nullptr; } + bool IsValid() const { return handle_ != nullptr; } ::sqlite3_stmt* Get() const { return handle_; } private: - ::sqlite3_stmt* handle_; + ::sqlite3_stmt* handle_ = nullptr; FML_DISALLOW_COPY_AND_ASSIGN(Handle); }; @@ -55,6 +58,9 @@ bool ArchiveStatement::IsValid() const { } bool ArchiveStatement::Reset() { + if (!IsValid()) { + return false; + } if (::sqlite3_reset(statement_handle_->Get()) != SQLITE_OK) { return false; } @@ -81,6 +87,9 @@ static constexpr int ToColumn(size_t index) { } size_t ArchiveStatement::GetColumnCount() { + if (!IsValid()) { + return 0u; + } return ::sqlite3_column_count(statement_handle_->Get()); } @@ -88,6 +97,9 @@ size_t ArchiveStatement::GetColumnCount() { * Bind Variants */ bool ArchiveStatement::WriteValue(size_t index, const std::string& item) { + if (!IsValid()) { + return false; + } return ::sqlite3_bind_text(statement_handle_->Get(), // ToParam(index), // item.data(), // @@ -96,18 +108,27 @@ bool ArchiveStatement::WriteValue(size_t index, const std::string& item) { } bool ArchiveStatement::BindIntegral(size_t index, int64_t item) { + if (!IsValid()) { + return false; + } return ::sqlite3_bind_int64(statement_handle_->Get(), // ToParam(index), // item) == SQLITE_OK; } bool ArchiveStatement::WriteValue(size_t index, double item) { + if (!IsValid()) { + return false; + } return ::sqlite3_bind_double(statement_handle_->Get(), // ToParam(index), // item) == SQLITE_OK; } bool ArchiveStatement::WriteValue(size_t index, const Allocation& item) { + if (!IsValid()) { + return false; + } return ::sqlite3_bind_blob(statement_handle_->Get(), // ToParam(index), // item.GetBuffer(), // @@ -119,11 +140,17 @@ bool ArchiveStatement::WriteValue(size_t index, const Allocation& item) { * Column Variants */ bool ArchiveStatement::ColumnIntegral(size_t index, int64_t& item) { + if (!IsValid()) { + return false; + } item = ::sqlite3_column_int64(statement_handle_->Get(), ToColumn(index)); return true; } bool ArchiveStatement::ReadValue(size_t index, double& item) { + if (!IsValid()) { + return false; + } item = ::sqlite3_column_double(statement_handle_->Get(), ToColumn(index)); return true; } @@ -137,6 +164,9 @@ bool ArchiveStatement::ReadValue(size_t index, double& item) { */ bool ArchiveStatement::ReadValue(size_t index, std::string& item) { + if (!IsValid()) { + return false; + } /* * Get the character data */ @@ -156,6 +186,9 @@ bool ArchiveStatement::ReadValue(size_t index, std::string& item) { } bool ArchiveStatement::ReadValue(size_t index, Allocation& item) { + if (!IsValid()) { + return false; + } /* * Get a blob pointer */ @@ -180,6 +213,9 @@ bool ArchiveStatement::ReadValue(size_t index, Allocation& item) { } ArchiveStatement::Result ArchiveStatement::Execute() { + if (!IsValid()) { + return Result::kFailure; + } switch (::sqlite3_step(statement_handle_->Get())) { case SQLITE_DONE: return Result::kDone; From 02bc8788da0bedcd24de9999e38f9db53ceb7e5c Mon Sep 17 00:00:00 2001 From: Chinmay Garde Date: Sun, 12 Dec 2021 14:51:52 -0800 Subject: [PATCH 253/433] Fix temporary database file deletion. --- impeller/archivist/archivist_fixture.cc | 17 +++++++++-------- 1 file changed, 9 insertions(+), 8 deletions(-) diff --git a/impeller/archivist/archivist_fixture.cc b/impeller/archivist/archivist_fixture.cc index 7a80634114315..4bb0413a575ee 100644 --- a/impeller/archivist/archivist_fixture.cc +++ b/impeller/archivist/archivist_fixture.cc @@ -11,15 +11,15 @@ namespace testing { ArchivistFixture::ArchivistFixture() { std::stringstream stream; - stream << flutter::testing::GetCurrentTestName() << ".db"; - archive_file_name_ = fml::paths::JoinPaths( - {flutter::testing::GetFixturesPath(), stream.str()}); + stream << "Test" << flutter::testing::GetCurrentTestName() << ".db"; + archive_file_name_ = stream.str(); } ArchivistFixture::~ArchivistFixture() = default; const std::string ArchivistFixture::GetArchiveFileName() const { - return archive_file_name_; + return fml::paths::JoinPaths( + {flutter::testing::GetFixturesPath(), archive_file_name_}); } void ArchivistFixture::SetUp() { @@ -27,13 +27,14 @@ void ArchivistFixture::SetUp() { } void ArchivistFixture::TearDown() { - // TODO: Tear this down. For now, I am inspecting the files for readability of - // schema. - // DeleteArchiveFile(); + DeleteArchiveFile(); } void ArchivistFixture::DeleteArchiveFile() const { - fml::UnlinkFile(archive_file_name_.c_str()); + auto fixtures = flutter::testing::OpenFixturesDirectory(); + if (fml::FileExists(fixtures, archive_file_name_.c_str())) { + fml::UnlinkFile(fixtures, archive_file_name_.c_str()); + } } } // namespace testing From a4a3bff8d0cbeffa1d9a30fbb6bfa2496dcd019f Mon Sep 17 00:00:00 2001 From: Chinmay Garde Date: Sun, 12 Dec 2021 16:38:42 -0800 Subject: [PATCH 254/433] Fix iOS compilation issues. --- impeller/BUILD.gn | 7 ++++++- impeller/archivist/BUILD.gn | 10 +++++----- impeller/entity/BUILD.gn | 1 + impeller/renderer/backend/metal/allocator_mtl.mm | 12 ++++++++++-- impeller/renderer/backend/metal/device_buffer_mtl.mm | 11 ++++++++++- .../renderer/backend/metal/vertex_descriptor_mtl.mm | 12 ++++++------ 6 files changed, 38 insertions(+), 15 deletions(-) diff --git a/impeller/BUILD.gn b/impeller/BUILD.gn index 525b78851ccf6..90fb1942d27f5 100644 --- a/impeller/BUILD.gn +++ b/impeller/BUILD.gn @@ -6,18 +6,23 @@ config("impeller_public_config") { include_dirs = [ ".." ] } +is_host = is_mac || is_linux || is_win + group("impeller") { public_deps = [ "aiks", "archivist", "base", - "compiler", "display_list", "entity", "geometry", "image", "renderer", ] + + if (is_host) { + public_deps += [ "compiler" ] + } } executable("impeller_unittests") { diff --git a/impeller/archivist/BUILD.gn b/impeller/archivist/BUILD.gn index 48e7324ec5135..3d3fce98e2264 100644 --- a/impeller/archivist/BUILD.gn +++ b/impeller/archivist/BUILD.gn @@ -5,10 +5,11 @@ import("../tools/impeller.gni") impeller_component("archivist") { - # Only the umbrella header is public since all other TU's are implementation - # detail that will be compiled away in release modes. - # Because they are implementation details, they may expose dependency headers. - public = [ "archive.h" ] + public = [ + "archivable.h", + "archive.h", + "archive_location.h", + ] sources = [ "archivable.cc", @@ -20,7 +21,6 @@ impeller_component("archivist") { "archive_database.cc", "archive_database.h", "archive_location.cc", - "archive_location.h", "archive_statement.cc", "archive_statement.h", "archive_transaction.cc", diff --git a/impeller/entity/BUILD.gn b/impeller/entity/BUILD.gn index 200724b22cdd5..19fbd42a67c54 100644 --- a/impeller/entity/BUILD.gn +++ b/impeller/entity/BUILD.gn @@ -36,6 +36,7 @@ impeller_component("entity") { deps = [ ":entity_shaders" ] public_deps = [ + "../archivist", "../image", "../renderer", ] diff --git a/impeller/renderer/backend/metal/allocator_mtl.mm b/impeller/renderer/backend/metal/allocator_mtl.mm index c1f606b61abea..4cbe7ac143646 100644 --- a/impeller/renderer/backend/metal/allocator_mtl.mm +++ b/impeller/renderer/backend/metal/allocator_mtl.mm @@ -41,7 +41,11 @@ static MTLResourceOptions ToMTLResourceOptions(StorageMode type) { return MTLResourceStorageModePrivate; case StorageMode::kDeviceTransient: #if OS_IOS - return MTLResourceStorageModeMemoryless; + if (@available(iOS 10.0, *)) { + return MTLResourceStorageModeMemoryless; + } else { + return MTLResourceStorageModePrivate; + } #else return MTLResourceStorageModePrivate; #endif @@ -62,7 +66,11 @@ static MTLStorageMode ToMTLStorageMode(StorageMode mode) { return MTLStorageModePrivate; case StorageMode::kDeviceTransient: #if OS_IOS - return MTLStorageModeMemoryless; + if (@available(iOS 10.0, *)) { + return MTLStorageModeMemoryless; + } else { + return MTLStorageModePrivate; + } #else return MTLStorageModePrivate; #endif diff --git a/impeller/renderer/backend/metal/device_buffer_mtl.mm b/impeller/renderer/backend/metal/device_buffer_mtl.mm index 1a94831fe35d8..cb6415f89b13d 100644 --- a/impeller/renderer/backend/metal/device_buffer_mtl.mm +++ b/impeller/renderer/backend/metal/device_buffer_mtl.mm @@ -72,9 +72,18 @@ ::memmove(dest + offset, source + source_range.offset, source_range.length); } +// |RequiresExplicitHostSynchronization| always returns false on iOS. But the +// compiler is mad that `didModifyRange:` appears in a TU meant for iOS. So, +// just compile it away. +// +// Making this call is never necessary on iOS because there is no +// MTLResourceStorageModeManaged mode. Only the MTLStorageModeShared mode is +// available. +#if !OS_IOS if (Allocator::RequiresExplicitHostSynchronization(mode_)) { [buffer_ didModifyRange:NSMakeRange(offset, source_range.length)]; } +#endif return true; } @@ -97,7 +106,7 @@ if (label.empty()) { return false; } - if (@available(macOS 10.12, *)) { + if (@available(macOS 10.12, iOS 10.0, *)) { [buffer_ addDebugMarker:@(label.c_str()) range:NSMakeRange(range.offset, range.length)]; } diff --git a/impeller/renderer/backend/metal/vertex_descriptor_mtl.mm b/impeller/renderer/backend/metal/vertex_descriptor_mtl.mm index e71571c4d837a..fec416b0041e9 100644 --- a/impeller/renderer/backend/metal/vertex_descriptor_mtl.mm +++ b/impeller/renderer/backend/metal/vertex_descriptor_mtl.mm @@ -39,7 +39,7 @@ static MTLVertexFormat ReadStageInputFormat(const ShaderStageIOSlot& input) { if (input.bit_width == 8 * sizeof(float) / 2) { switch (input.vec_size) { case 1: - if (@available(macOS 10.13, *)) { + if (@available(macOS 10.13, iOS 11.0, *)) { return MTLVertexFormatHalf; } else { return MTLVertexFormatInvalid; @@ -60,7 +60,7 @@ static MTLVertexFormat ReadStageInputFormat(const ShaderStageIOSlot& input) { } case ShaderType::kBoolean: { if (input.bit_width == 8 * sizeof(bool) && input.vec_size == 1) { - if (@available(macOS 10.13, *)) { + if (@available(macOS 10.13, iOS 11.0, *)) { return MTLVertexFormatChar; } else { return MTLVertexFormatInvalid; @@ -72,7 +72,7 @@ static MTLVertexFormat ReadStageInputFormat(const ShaderStageIOSlot& input) { if (input.bit_width == 8 * sizeof(char)) { switch (input.vec_size) { case 1: - if (@available(macOS 10.13, *)) { + if (@available(macOS 10.13, iOS 11.0, *)) { return MTLVertexFormatChar; } else { return MTLVertexFormatInvalid; @@ -91,7 +91,7 @@ static MTLVertexFormat ReadStageInputFormat(const ShaderStageIOSlot& input) { if (input.bit_width == 8 * sizeof(char)) { switch (input.vec_size) { case 1: - if (@available(macOS 10.13, *)) { + if (@available(macOS 10.13, iOS 11.0, *)) { return MTLVertexFormatUChar; } else { return MTLVertexFormatInvalid; @@ -110,7 +110,7 @@ static MTLVertexFormat ReadStageInputFormat(const ShaderStageIOSlot& input) { if (input.bit_width == 8 * sizeof(short)) { switch (input.vec_size) { case 1: - if (@available(macOS 10.13, *)) { + if (@available(macOS 10.13, iOS 11.0, *)) { return MTLVertexFormatShort; } else { return MTLVertexFormatInvalid; @@ -129,7 +129,7 @@ static MTLVertexFormat ReadStageInputFormat(const ShaderStageIOSlot& input) { if (input.bit_width == 8 * sizeof(ushort)) { switch (input.vec_size) { case 1: - if (@available(macOS 10.13, *)) { + if (@available(macOS 10.13, iOS 11.0, *)) { return MTLVertexFormatUShort; } else { return MTLVertexFormatInvalid; From 7265c7a9cb9472a385a2acf6de75cefeb5a77c4f Mon Sep 17 00:00:00 2001 From: Chinmay Garde Date: Wed, 22 Dec 2021 14:51:33 -0800 Subject: [PATCH 255/433] Update GN rules for Flutter updates. --- impeller/display_list/BUILD.gn | 2 +- impeller/display_list/display_list_impeller.h | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/impeller/display_list/BUILD.gn b/impeller/display_list/BUILD.gn index 6a697baab4968..e93e1cb205099 100644 --- a/impeller/display_list/BUILD.gn +++ b/impeller/display_list/BUILD.gn @@ -12,7 +12,7 @@ impeller_component("display_list") { deps = [ "../aiks", - "//flutter/flow", + "//flutter/display_list", "//flutter/fml", "//third_party/skia", ] diff --git a/impeller/display_list/display_list_impeller.h b/impeller/display_list/display_list_impeller.h index 8bea69f4cf926..e9f806c26be4c 100644 --- a/impeller/display_list/display_list_impeller.h +++ b/impeller/display_list/display_list_impeller.h @@ -4,7 +4,7 @@ #pragma once -#include "flutter/flow/display_list.h" +#include "flutter/display_list/display_list.h" #include "flutter/fml/macros.h" #include "impeller/aiks/canvas.h" #include "impeller/aiks/paint.h" From e0d051d6c5df22ac28ab612da3e1d0c62a3883d7 Mon Sep 17 00:00:00 2001 From: Chinmay Garde Date: Wed, 22 Dec 2021 14:54:03 -0800 Subject: [PATCH 256/433] Minor: Rename the Impeller dispatcher to be something sane and prep for sidecar wireup. --- impeller/display_list/BUILD.gn | 4 +- ...impeller.cc => display_list_dispatcher.cc} | 229 +++++++++--------- ...t_impeller.h => display_list_dispatcher.h} | 8 +- 3 files changed, 121 insertions(+), 120 deletions(-) rename impeller/display_list/{display_list_impeller.cc => display_list_dispatcher.cc} (53%) rename impeller/display_list/{display_list_impeller.h => display_list_dispatcher.h} (97%) diff --git a/impeller/display_list/BUILD.gn b/impeller/display_list/BUILD.gn index e93e1cb205099..ca5a54bc253b0 100644 --- a/impeller/display_list/BUILD.gn +++ b/impeller/display_list/BUILD.gn @@ -6,8 +6,8 @@ import("//flutter/impeller/tools/impeller.gni") impeller_component("display_list") { sources = [ - "display_list_impeller.cc", - "display_list_impeller.h", + "display_list_dispatcher.cc", + "display_list_dispatcher.h", ] deps = [ diff --git a/impeller/display_list/display_list_impeller.cc b/impeller/display_list/display_list_dispatcher.cc similarity index 53% rename from impeller/display_list/display_list_impeller.cc rename to impeller/display_list/display_list_dispatcher.cc index 442ade60c1efc..ba6f0e1aa407b 100644 --- a/impeller/display_list/display_list_impeller.cc +++ b/impeller/display_list/display_list_dispatcher.cc @@ -2,7 +2,7 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -#include "impeller/display_list/display_list_impeller.h" +#include "impeller/display_list/display_list_dispatcher.h" #include "impeller/geometry/path_builder.h" @@ -11,17 +11,17 @@ namespace impeller { #define UNIMPLEMENTED \ FML_LOG(ERROR) << "Unimplemented detail in " << __FUNCTION__; -DisplayListImpeller::DisplayListImpeller() = default; +DisplayListDispatcher::DisplayListDispatcher() = default; -DisplayListImpeller::~DisplayListImpeller() = default; +DisplayListDispatcher::~DisplayListDispatcher() = default; // |flutter::Dispatcher| -void DisplayListImpeller::setAntiAlias(bool aa) { +void DisplayListDispatcher::setAntiAlias(bool aa) { // Nothing to do because AA is implicit. } // |flutter::Dispatcher| -void DisplayListImpeller::setDither(bool dither) {} +void DisplayListDispatcher::setDither(bool dither) {} static Paint::Style ToStyle(SkPaint::Style style) { switch (style) { @@ -37,12 +37,12 @@ static Paint::Style ToStyle(SkPaint::Style style) { } // |flutter::Dispatcher| -void DisplayListImpeller::setStyle(SkPaint::Style style) { +void DisplayListDispatcher::setStyle(SkPaint::Style style) { paint_.style = ToStyle(style); } // |flutter::Dispatcher| -void DisplayListImpeller::setColor(SkColor color) { +void DisplayListDispatcher::setColor(SkColor color) { paint_.color = { SkColorGetR(color) / 255.0f, // red SkColorGetG(color) / 255.0f, // green @@ -52,72 +52,73 @@ void DisplayListImpeller::setColor(SkColor color) { } // |flutter::Dispatcher| -void DisplayListImpeller::setStrokeWidth(SkScalar width) { +void DisplayListDispatcher::setStrokeWidth(SkScalar width) { paint_.stroke_width = width; } // |flutter::Dispatcher| -void DisplayListImpeller::setStrokeMiter(SkScalar limit) { +void DisplayListDispatcher::setStrokeMiter(SkScalar limit) { UNIMPLEMENTED; } // |flutter::Dispatcher| -void DisplayListImpeller::setStrokeCap(SkPaint::Cap cap) { +void DisplayListDispatcher::setStrokeCap(SkPaint::Cap cap) { UNIMPLEMENTED; } // |flutter::Dispatcher| -void DisplayListImpeller::setStrokeJoin(SkPaint::Join join) { +void DisplayListDispatcher::setStrokeJoin(SkPaint::Join join) { UNIMPLEMENTED; } // |flutter::Dispatcher| -void DisplayListImpeller::setShader(sk_sp shader) { +void DisplayListDispatcher::setShader(sk_sp shader) { UNIMPLEMENTED; } // |flutter::Dispatcher| -void DisplayListImpeller::setColorFilter(sk_sp filter) { +void DisplayListDispatcher::setColorFilter(sk_sp filter) { UNIMPLEMENTED; } // |flutter::Dispatcher| -void DisplayListImpeller::setInvertColors(bool invert) { +void DisplayListDispatcher::setInvertColors(bool invert) { UNIMPLEMENTED; } // |flutter::Dispatcher| -void DisplayListImpeller::setBlendMode(SkBlendMode mode) { +void DisplayListDispatcher::setBlendMode(SkBlendMode mode) { UNIMPLEMENTED; } // |flutter::Dispatcher| -void DisplayListImpeller::setBlender(sk_sp blender) { +void DisplayListDispatcher::setBlender(sk_sp blender) { UNIMPLEMENTED; } // |flutter::Dispatcher| -void DisplayListImpeller::setPathEffect(sk_sp effect) { +void DisplayListDispatcher::setPathEffect(sk_sp effect) { UNIMPLEMENTED; } // |flutter::Dispatcher| -void DisplayListImpeller::setMaskFilter(sk_sp filter) { +void DisplayListDispatcher::setMaskFilter(sk_sp filter) { UNIMPLEMENTED; } // |flutter::Dispatcher| -void DisplayListImpeller::setMaskBlurFilter(SkBlurStyle style, SkScalar sigma) { +void DisplayListDispatcher::setMaskBlurFilter(SkBlurStyle style, + SkScalar sigma) { UNIMPLEMENTED; } // |flutter::Dispatcher| -void DisplayListImpeller::setImageFilter(sk_sp filter) { +void DisplayListDispatcher::setImageFilter(sk_sp filter) { UNIMPLEMENTED; } // |flutter::Dispatcher| -void DisplayListImpeller::save() { +void DisplayListDispatcher::save() { canvas_.Save(); } @@ -129,43 +130,43 @@ static std::optional ToRect(const SkRect* rect) { } // |flutter::Dispatcher| -void DisplayListImpeller::saveLayer(const SkRect* bounds, - bool restore_with_paint) { +void DisplayListDispatcher::saveLayer(const SkRect* bounds, + bool restore_with_paint) { canvas_.SaveLayer(restore_with_paint ? paint_ : Paint{}, ToRect(bounds)); } // |flutter::Dispatcher| -void DisplayListImpeller::restore() { +void DisplayListDispatcher::restore() { canvas_.Restore(); } // |flutter::Dispatcher| -void DisplayListImpeller::translate(SkScalar tx, SkScalar ty) { +void DisplayListDispatcher::translate(SkScalar tx, SkScalar ty) { canvas_.Translate({tx, ty, 0.0}); } // |flutter::Dispatcher| -void DisplayListImpeller::scale(SkScalar sx, SkScalar sy) { +void DisplayListDispatcher::scale(SkScalar sx, SkScalar sy) { canvas_.Scale({sx, sy, 1.0}); } // |flutter::Dispatcher| -void DisplayListImpeller::rotate(SkScalar degrees) { +void DisplayListDispatcher::rotate(SkScalar degrees) { canvas_.Rotate(Degrees{degrees}); } // |flutter::Dispatcher| -void DisplayListImpeller::skew(SkScalar sx, SkScalar sy) { +void DisplayListDispatcher::skew(SkScalar sx, SkScalar sy) { canvas_.Skew(sx, sy); } // |flutter::Dispatcher| -void DisplayListImpeller::transform2DAffine(SkScalar mxx, - SkScalar mxy, - SkScalar mxt, - SkScalar myx, - SkScalar myy, - SkScalar myt) { +void DisplayListDispatcher::transform2DAffine(SkScalar mxx, + SkScalar mxy, + SkScalar mxt, + SkScalar myx, + SkScalar myy, + SkScalar myt) { // clang-format off transformFullPerspective( mxx, mxy, 0, mxt, @@ -177,22 +178,22 @@ void DisplayListImpeller::transform2DAffine(SkScalar mxx, } // |flutter::Dispatcher| -void DisplayListImpeller::transformFullPerspective(SkScalar mxx, - SkScalar mxy, - SkScalar mxz, - SkScalar mxt, - SkScalar myx, - SkScalar myy, - SkScalar myz, - SkScalar myt, - SkScalar mzx, - SkScalar mzy, - SkScalar mzz, - SkScalar mzt, - SkScalar mwx, - SkScalar mwy, - SkScalar mwz, - SkScalar mwt) { +void DisplayListDispatcher::transformFullPerspective(SkScalar mxx, + SkScalar mxy, + SkScalar mxz, + SkScalar mxt, + SkScalar myx, + SkScalar myy, + SkScalar myz, + SkScalar myt, + SkScalar mzx, + SkScalar mzy, + SkScalar mzz, + SkScalar mzt, + SkScalar mwx, + SkScalar mwy, + SkScalar mwz, + SkScalar mwt) { // The order of arguments is row-major but Impeller matrices are column-major. // clang-format off auto xformation = Matrix{ @@ -210,9 +211,9 @@ static Rect ToRect(const SkRect& rect) { } // |flutter::Dispatcher| -void DisplayListImpeller::clipRect(const SkRect& rect, - SkClipOp clip_op, - bool is_aa) { +void DisplayListDispatcher::clipRect(const SkRect& rect, + SkClipOp clip_op, + bool is_aa) { auto path = PathBuilder{}.AddRect(ToRect(rect)).TakePath(); canvas_.ClipPath(std::move(path)); } @@ -306,61 +307,61 @@ static Path ToPath(const SkRRect& rrect) { } // |flutter::Dispatcher| -void DisplayListImpeller::clipRRect(const SkRRect& rrect, - SkClipOp clip_op, - bool is_aa) { +void DisplayListDispatcher::clipRRect(const SkRRect& rrect, + SkClipOp clip_op, + bool is_aa) { canvas_.ClipPath(ToPath(rrect)); } // |flutter::Dispatcher| -void DisplayListImpeller::clipPath(const SkPath& path, - SkClipOp clip_op, - bool is_aa) { +void DisplayListDispatcher::clipPath(const SkPath& path, + SkClipOp clip_op, + bool is_aa) { canvas_.ClipPath(ToPath(path)); } // |flutter::Dispatcher| -void DisplayListImpeller::drawColor(SkColor color, SkBlendMode mode) { +void DisplayListDispatcher::drawColor(SkColor color, SkBlendMode mode) { UNIMPLEMENTED; } // |flutter::Dispatcher| -void DisplayListImpeller::drawPaint() { +void DisplayListDispatcher::drawPaint() { UNIMPLEMENTED; } // |flutter::Dispatcher| -void DisplayListImpeller::drawLine(const SkPoint& p0, const SkPoint& p1) { +void DisplayListDispatcher::drawLine(const SkPoint& p0, const SkPoint& p1) { auto path = PathBuilder{}.AddLine(ToPoint(p0), ToPoint(p1)).TakePath(); canvas_.DrawPath(std::move(path), paint_); } // |flutter::Dispatcher| -void DisplayListImpeller::drawRect(const SkRect& rect) { +void DisplayListDispatcher::drawRect(const SkRect& rect) { auto path = PathBuilder{}.AddRect(ToRect(rect)).TakePath(); canvas_.DrawPath(std::move(path), paint_); } // |flutter::Dispatcher| -void DisplayListImpeller::drawOval(const SkRect& bounds) { +void DisplayListDispatcher::drawOval(const SkRect& bounds) { auto path = PathBuilder{}.AddOval(ToRect(bounds)).TakePath(); canvas_.DrawPath(std::move(path), paint_); } // |flutter::Dispatcher| -void DisplayListImpeller::drawCircle(const SkPoint& center, SkScalar radius) { +void DisplayListDispatcher::drawCircle(const SkPoint& center, SkScalar radius) { auto path = PathBuilder{}.AddCircle(ToPoint(center), radius).TakePath(); canvas_.DrawPath(std::move(path), paint_); } // |flutter::Dispatcher| -void DisplayListImpeller::drawRRect(const SkRRect& rrect) { +void DisplayListDispatcher::drawRRect(const SkRRect& rrect) { canvas_.DrawPath(ToPath(rrect), paint_); } // |flutter::Dispatcher| -void DisplayListImpeller::drawDRRect(const SkRRect& outer, - const SkRRect& inner) { +void DisplayListDispatcher::drawDRRect(const SkRRect& outer, + const SkRRect& inner) { PathBuilder builder; builder.AddPath(ToPath(outer)); builder.AddPath(ToPath(inner)); @@ -368,41 +369,41 @@ void DisplayListImpeller::drawDRRect(const SkRRect& outer, } // |flutter::Dispatcher| -void DisplayListImpeller::drawPath(const SkPath& path) { +void DisplayListDispatcher::drawPath(const SkPath& path) { canvas_.DrawPath(ToPath(path), paint_); } // |flutter::Dispatcher| -void DisplayListImpeller::drawArc(const SkRect& oval_bounds, - SkScalar start_degrees, - SkScalar sweep_degrees, - bool use_center) { +void DisplayListDispatcher::drawArc(const SkRect& oval_bounds, + SkScalar start_degrees, + SkScalar sweep_degrees, + bool use_center) { UNIMPLEMENTED; } // |flutter::Dispatcher| -void DisplayListImpeller::drawPoints(SkCanvas::PointMode mode, - uint32_t count, - const SkPoint points[]) { +void DisplayListDispatcher::drawPoints(SkCanvas::PointMode mode, + uint32_t count, + const SkPoint points[]) { UNIMPLEMENTED; } // |flutter::Dispatcher| -void DisplayListImpeller::drawVertices(const sk_sp vertices, - SkBlendMode mode) { +void DisplayListDispatcher::drawVertices(const sk_sp vertices, + SkBlendMode mode) { UNIMPLEMENTED; } // |flutter::Dispatcher| -void DisplayListImpeller::drawImage(const sk_sp image, - const SkPoint point, - const SkSamplingOptions& sampling, - bool render_with_attributes) { +void DisplayListDispatcher::drawImage(const sk_sp image, + const SkPoint point, + const SkSamplingOptions& sampling, + bool render_with_attributes) { UNIMPLEMENTED; } // |flutter::Dispatcher| -void DisplayListImpeller::drawImageRect( +void DisplayListDispatcher::drawImageRect( const sk_sp image, const SkRect& src, const SkRect& dst, @@ -413,62 +414,62 @@ void DisplayListImpeller::drawImageRect( } // |flutter::Dispatcher| -void DisplayListImpeller::drawImageNine(const sk_sp image, - const SkIRect& center, - const SkRect& dst, - SkFilterMode filter, - bool render_with_attributes) { +void DisplayListDispatcher::drawImageNine(const sk_sp image, + const SkIRect& center, + const SkRect& dst, + SkFilterMode filter, + bool render_with_attributes) { UNIMPLEMENTED; } // |flutter::Dispatcher| -void DisplayListImpeller::drawImageLattice(const sk_sp image, - const SkCanvas::Lattice& lattice, - const SkRect& dst, - SkFilterMode filter, - bool render_with_attributes) { +void DisplayListDispatcher::drawImageLattice(const sk_sp image, + const SkCanvas::Lattice& lattice, + const SkRect& dst, + SkFilterMode filter, + bool render_with_attributes) { UNIMPLEMENTED; } // |flutter::Dispatcher| -void DisplayListImpeller::drawAtlas(const sk_sp atlas, - const SkRSXform xform[], - const SkRect tex[], - const SkColor colors[], - int count, - SkBlendMode mode, - const SkSamplingOptions& sampling, - const SkRect* cull_rect, - bool render_with_attributes) { +void DisplayListDispatcher::drawAtlas(const sk_sp atlas, + const SkRSXform xform[], + const SkRect tex[], + const SkColor colors[], + int count, + SkBlendMode mode, + const SkSamplingOptions& sampling, + const SkRect* cull_rect, + bool render_with_attributes) { UNIMPLEMENTED; } // |flutter::Dispatcher| -void DisplayListImpeller::drawPicture(const sk_sp picture, - const SkMatrix* matrix, - bool render_with_attributes) { +void DisplayListDispatcher::drawPicture(const sk_sp picture, + const SkMatrix* matrix, + bool render_with_attributes) { UNIMPLEMENTED; } // |flutter::Dispatcher| -void DisplayListImpeller::drawDisplayList( +void DisplayListDispatcher::drawDisplayList( const sk_sp display_list) { UNIMPLEMENTED; } // |flutter::Dispatcher| -void DisplayListImpeller::drawTextBlob(const sk_sp blob, - SkScalar x, - SkScalar y) { +void DisplayListDispatcher::drawTextBlob(const sk_sp blob, + SkScalar x, + SkScalar y) { UNIMPLEMENTED; } // |flutter::Dispatcher| -void DisplayListImpeller::drawShadow(const SkPath& path, - const SkColor color, - const SkScalar elevation, - bool transparent_occluder, - SkScalar dpr) { +void DisplayListDispatcher::drawShadow(const SkPath& path, + const SkColor color, + const SkScalar elevation, + bool transparent_occluder, + SkScalar dpr) { UNIMPLEMENTED; } diff --git a/impeller/display_list/display_list_impeller.h b/impeller/display_list/display_list_dispatcher.h similarity index 97% rename from impeller/display_list/display_list_impeller.h rename to impeller/display_list/display_list_dispatcher.h index e9f806c26be4c..2ee6f790e6204 100644 --- a/impeller/display_list/display_list_impeller.h +++ b/impeller/display_list/display_list_dispatcher.h @@ -11,11 +11,11 @@ namespace impeller { -class DisplayListImpeller final : public flutter::Dispatcher { +class DisplayListDispatcher final : public flutter::Dispatcher { public: - DisplayListImpeller(); + DisplayListDispatcher(); - ~DisplayListImpeller(); + ~DisplayListDispatcher(); // |flutter::Dispatcher| void setAntiAlias(bool aa) override; @@ -229,7 +229,7 @@ class DisplayListImpeller final : public flutter::Dispatcher { Paint paint_; Canvas canvas_; - FML_DISALLOW_COPY_AND_ASSIGN(DisplayListImpeller); + FML_DISALLOW_COPY_AND_ASSIGN(DisplayListDispatcher); }; } // namespace impeller From 78764d1289fbb7b47c051e3caa91ddffd397afaf Mon Sep 17 00:00:00 2001 From: Chinmay Garde Date: Tue, 28 Dec 2021 16:07:11 -0800 Subject: [PATCH 257/433] Rework the confusingly named renderer subsystem instances. --- impeller/BUILD.gn | 5 ++++- impeller/aiks/BUILD.gn | 4 ++-- .../aiks/{aiks_renderer.cc => aiks_context.cc} | 16 ++++++++-------- .../aiks/{aiks_renderer.h => aiks_context.h} | 12 ++++++------ impeller/aiks/aiks_playground.cc | 4 ++-- impeller/display_list/display_list_dispatcher.h | 1 + impeller/entity/BUILD.gn | 4 ++-- .../{content_renderer.cc => content_context.cc} | 10 +++++----- .../{content_renderer.h => content_context.h} | 8 ++++---- impeller/entity/contents.cc | 16 ++++++++-------- impeller/entity/contents.h | 14 +++++++------- impeller/entity/entity.cc | 4 ++-- impeller/entity/entity.h | 2 +- impeller/entity/entity_pass.cc | 4 ++-- impeller/entity/entity_pass.h | 4 ++-- impeller/entity/entity_playground.cc | 8 ++++---- impeller/renderer/backend/metal/context_mtl.h | 1 + impeller/renderer/renderer.cc | 11 ++++++----- impeller/renderer/renderer.h | 7 +++++-- 19 files changed, 72 insertions(+), 63 deletions(-) rename impeller/aiks/{aiks_renderer.cc => aiks_context.cc} (53%) rename impeller/aiks/{aiks_renderer.h => aiks_context.h} (69%) rename impeller/entity/{content_renderer.cc => content_context.cc} (87%) rename impeller/entity/{content_renderer.h => content_context.h} (96%) diff --git a/impeller/BUILD.gn b/impeller/BUILD.gn index 90fb1942d27f5..f46067dd58eca 100644 --- a/impeller/BUILD.gn +++ b/impeller/BUILD.gn @@ -3,7 +3,10 @@ # found in the LICENSE file. config("impeller_public_config") { - include_dirs = [ ".." ] + include_dirs = [ + "..", + ".", + ] } is_host = is_mac || is_linux || is_win diff --git a/impeller/aiks/BUILD.gn b/impeller/aiks/BUILD.gn index a3398f394b54a..8f7df7aa66a46 100644 --- a/impeller/aiks/BUILD.gn +++ b/impeller/aiks/BUILD.gn @@ -6,8 +6,8 @@ import("../tools/impeller.gni") impeller_component("aiks") { sources = [ - "aiks_renderer.cc", - "aiks_renderer.h", + "aiks_context.cc", + "aiks_context.h", "canvas.cc", "canvas.h", "image.cc", diff --git a/impeller/aiks/aiks_renderer.cc b/impeller/aiks/aiks_context.cc similarity index 53% rename from impeller/aiks/aiks_renderer.cc rename to impeller/aiks/aiks_context.cc index dbdc2838ad121..da4ae4c833d1c 100644 --- a/impeller/aiks/aiks_renderer.cc +++ b/impeller/aiks/aiks_context.cc @@ -2,39 +2,39 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -#include "impeller/aiks/aiks_renderer.h" +#include "impeller/aiks/aiks_context.h" #include "impeller/aiks/picture.h" namespace impeller { -AiksRenderer::AiksRenderer(std::shared_ptr context) +AiksContext::AiksContext(std::shared_ptr context) : context_(std::move(context)) { if (!context_ || !context_->IsValid()) { return; } - content_renderer_ = std::make_unique(context_); - if (!content_renderer_->IsValid()) { + content_context_ = std::make_unique(context_); + if (!content_context_->IsValid()) { return; } is_valid_ = true; } -AiksRenderer::~AiksRenderer() = default; +AiksContext::~AiksContext() = default; -bool AiksRenderer::IsValid() const { +bool AiksContext::IsValid() const { return is_valid_; } -bool AiksRenderer::Render(const Picture& picture, RenderPass& parent_pass) { +bool AiksContext::Render(const Picture& picture, RenderPass& parent_pass) { if (!IsValid()) { return false; } if (picture.pass) { - return picture.pass->Render(*content_renderer_, parent_pass); + return picture.pass->Render(*content_context_, parent_pass); } return true; diff --git a/impeller/aiks/aiks_renderer.h b/impeller/aiks/aiks_context.h similarity index 69% rename from impeller/aiks/aiks_renderer.h rename to impeller/aiks/aiks_context.h index aa09a7ab4746b..edbdd604a491a 100644 --- a/impeller/aiks/aiks_renderer.h +++ b/impeller/aiks/aiks_context.h @@ -7,7 +7,7 @@ #include #include "flutter/fml/macros.h" -#include "impeller/entity/content_renderer.h" +#include "impeller/entity/content_context.h" #include "impeller/renderer/context.h" namespace impeller { @@ -15,11 +15,11 @@ namespace impeller { struct Picture; class RenderPass; -class AiksRenderer { +class AiksContext { public: - AiksRenderer(std::shared_ptr context); + AiksContext(std::shared_ptr context); - ~AiksRenderer(); + ~AiksContext(); bool IsValid() const; @@ -27,10 +27,10 @@ class AiksRenderer { private: std::shared_ptr context_; - std::unique_ptr content_renderer_; + std::unique_ptr content_context_; bool is_valid_ = false; - FML_DISALLOW_COPY_AND_ASSIGN(AiksRenderer); + FML_DISALLOW_COPY_AND_ASSIGN(AiksContext); }; } // namespace impeller diff --git a/impeller/aiks/aiks_playground.cc b/impeller/aiks/aiks_playground.cc index 32c301b4ca913..ff2513b31d584 100644 --- a/impeller/aiks/aiks_playground.cc +++ b/impeller/aiks/aiks_playground.cc @@ -4,7 +4,7 @@ #include "impeller/aiks/aiks_playground.h" -#include "impeller/aiks/aiks_renderer.h" +#include "impeller/aiks/aiks_context.h" namespace impeller { @@ -13,7 +13,7 @@ AiksPlayground::AiksPlayground() = default; AiksPlayground::~AiksPlayground() = default; bool AiksPlayground::OpenPlaygroundHere(const Picture& picture) { - AiksRenderer renderer(GetContext()); + AiksContext renderer(GetContext()); if (!renderer.IsValid()) { return false; diff --git a/impeller/display_list/display_list_dispatcher.h b/impeller/display_list/display_list_dispatcher.h index 2ee6f790e6204..d007de6653aa8 100644 --- a/impeller/display_list/display_list_dispatcher.h +++ b/impeller/display_list/display_list_dispatcher.h @@ -5,6 +5,7 @@ #pragma once #include "flutter/display_list/display_list.h" +#include "flutter/display_list/display_list_dispatcher.h" #include "flutter/fml/macros.h" #include "impeller/aiks/canvas.h" #include "impeller/aiks/paint.h" diff --git a/impeller/entity/BUILD.gn b/impeller/entity/BUILD.gn index 19fbd42a67c54..e6c109e06e99e 100644 --- a/impeller/entity/BUILD.gn +++ b/impeller/entity/BUILD.gn @@ -21,8 +21,8 @@ impeller_shaders("entity_shaders") { impeller_component("entity") { sources = [ - "content_renderer.cc", - "content_renderer.h", + "content_context.cc", + "content_context.h", "contents.cc", "contents.h", "entity.cc", diff --git a/impeller/entity/content_renderer.cc b/impeller/entity/content_context.cc similarity index 87% rename from impeller/entity/content_renderer.cc rename to impeller/entity/content_context.cc index d22e81f104ab3..ed0dcc5ddb04a 100644 --- a/impeller/entity/content_renderer.cc +++ b/impeller/entity/content_context.cc @@ -2,11 +2,11 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -#include "impeller/entity/content_renderer.h" +#include "impeller/entity/content_context.h" namespace impeller { -ContentRenderer::ContentRenderer(std::shared_ptr context) +ContentContext::ContentContext(std::shared_ptr context) : context_(std::move(context)) { if (!context_ || !context_->IsValid()) { return; @@ -46,13 +46,13 @@ ContentRenderer::ContentRenderer(std::shared_ptr context) is_valid_ = true; } -ContentRenderer::~ContentRenderer() = default; +ContentContext::~ContentContext() = default; -bool ContentRenderer::IsValid() const { +bool ContentContext::IsValid() const { return is_valid_; } -std::shared_ptr ContentRenderer::GetContext() const { +std::shared_ptr ContentContext::GetContext() const { return context_; } diff --git a/impeller/entity/content_renderer.h b/impeller/entity/content_context.h similarity index 96% rename from impeller/entity/content_renderer.h rename to impeller/entity/content_context.h index 4e4aab0e366fd..0143fb29135b6 100644 --- a/impeller/entity/content_renderer.h +++ b/impeller/entity/content_context.h @@ -33,7 +33,7 @@ using SolidStrokePipeline = // to redirect writing to the stencil instead of color attachments. using ClipPipeline = PipelineT; -class ContentRenderer { +class ContentContext { public: struct Options { SampleCount sample_count = SampleCount::kCount1; @@ -51,9 +51,9 @@ class ContentRenderer { }; }; - ContentRenderer(std::shared_ptr context); + ContentContext(std::shared_ptr context); - ~ContentRenderer(); + ~ContentContext(); bool IsValid() const; @@ -128,7 +128,7 @@ class ContentRenderer { bool is_valid_ = false; - FML_DISALLOW_COPY_AND_ASSIGN(ContentRenderer); + FML_DISALLOW_COPY_AND_ASSIGN(ContentContext); }; } // namespace impeller diff --git a/impeller/entity/contents.cc b/impeller/entity/contents.cc index 71ae6c2b66e55..91130debc04e6 100644 --- a/impeller/entity/contents.cc +++ b/impeller/entity/contents.cc @@ -7,7 +7,7 @@ #include #include "flutter/fml/logging.h" -#include "impeller/entity/content_renderer.h" +#include "impeller/entity/content_context.h" #include "impeller/entity/entity.h" #include "impeller/geometry/path_builder.h" #include "impeller/geometry/vector.h" @@ -19,8 +19,8 @@ namespace impeller { -static ContentRenderer::Options OptionsFromPass(const RenderPass& pass) { - ContentRenderer::Options opts; +static ContentContext::Options OptionsFromPass(const RenderPass& pass) { + ContentContext::Options opts; opts.sample_count = pass.GetRenderTarget().GetSampleCount(); return opts; } @@ -60,7 +60,7 @@ const std::vector& LinearGradientContents::GetColors() const { return colors_; } -bool LinearGradientContents::Render(const ContentRenderer& renderer, +bool LinearGradientContents::Render(const ContentContext& renderer, const Entity& entity, RenderPass& pass) const { using VS = GradientFillPipeline::VertexShader; @@ -137,7 +137,7 @@ static VertexBuffer CreateSolidFillVertices(const Path& path, return vtx_builder.CreateVertexBuffer(buffer); } -bool SolidColorContents::Render(const ContentRenderer& renderer, +bool SolidColorContents::Render(const ContentContext& renderer, const Entity& entity, RenderPass& pass) const { if (color_.IsTransparent()) { @@ -194,7 +194,7 @@ void TextureContents::SetOpacity(Scalar opacity) { opacity_ = opacity; } -bool TextureContents::Render(const ContentRenderer& renderer, +bool TextureContents::Render(const ContentContext& renderer, const Entity& entity, RenderPass& pass) const { if (texture_ == nullptr) { @@ -328,7 +328,7 @@ static VertexBuffer CreateSolidStrokeVertices(const Path& path, return vtx_builder.CreateVertexBuffer(buffer); } -bool SolidStrokeContents::Render(const ContentRenderer& renderer, +bool SolidStrokeContents::Render(const ContentContext& renderer, const Entity& entity, RenderPass& pass) const { if (color_.IsTransparent() || stroke_size_ <= 0.0) { @@ -377,7 +377,7 @@ ClipContents::ClipContents() = default; ClipContents::~ClipContents() = default; -bool ClipContents::Render(const ContentRenderer& renderer, +bool ClipContents::Render(const ContentContext& renderer, const Entity& entity, RenderPass& pass) const { using VS = ClipPipeline::VertexShader; diff --git a/impeller/entity/contents.h b/impeller/entity/contents.h index ffda5f7d432a8..126dd2bf671c2 100644 --- a/impeller/entity/contents.h +++ b/impeller/entity/contents.h @@ -15,7 +15,7 @@ namespace impeller { -class ContentRenderer; +class ContentContext; class Entity; class Surface; class RenderPass; @@ -26,7 +26,7 @@ class Contents { virtual ~Contents(); - virtual bool Render(const ContentRenderer& renderer, + virtual bool Render(const ContentContext& renderer, const Entity& entity, RenderPass& pass) const = 0; @@ -41,7 +41,7 @@ class LinearGradientContents final : public Contents { ~LinearGradientContents() override; // |Contents| - bool Render(const ContentRenderer& renderer, + bool Render(const ContentContext& renderer, const Entity& entity, RenderPass& pass) const override; @@ -72,7 +72,7 @@ class SolidColorContents final : public Contents { const Color& GetColor() const; // |Contents| - bool Render(const ContentRenderer& renderer, + bool Render(const ContentContext& renderer, const Entity& entity, RenderPass& pass) const override; @@ -99,7 +99,7 @@ class TextureContents final : public Contents { const IRect& GetSourceRect() const; // |Contents| - bool Render(const ContentRenderer& renderer, + bool Render(const ContentContext& renderer, const Entity& entity, RenderPass& pass) const override; @@ -126,7 +126,7 @@ class SolidStrokeContents final : public Contents { Scalar GetStrokeSize() const; // |Contents| - bool Render(const ContentRenderer& renderer, + bool Render(const ContentContext& renderer, const Entity& entity, RenderPass& pass) const override; @@ -144,7 +144,7 @@ class ClipContents final : public Contents { ~ClipContents(); // |Contents| - bool Render(const ContentRenderer& renderer, + bool Render(const ContentContext& renderer, const Entity& entity, RenderPass& pass) const override; diff --git a/impeller/entity/entity.cc b/impeller/entity/entity.cc index 150b30fcf603b..701702b6690ec 100644 --- a/impeller/entity/entity.cc +++ b/impeller/entity/entity.cc @@ -4,7 +4,7 @@ #include "impeller/entity/entity.h" -#include "impeller/entity/content_renderer.h" +#include "impeller/entity/content_context.h" #include "impeller/renderer/render_pass.h" namespace impeller { @@ -65,7 +65,7 @@ void Entity::IncrementStencilDepth(uint32_t increment) { stencil_depth_ += increment; } -bool Entity::Render(ContentRenderer& renderer, RenderPass& parent_pass) const { +bool Entity::Render(ContentContext& renderer, RenderPass& parent_pass) const { if (!contents_) { return true; } diff --git a/impeller/entity/entity.h b/impeller/entity/entity.h index 8e78391b38ef1..13cc918ace955 100644 --- a/impeller/entity/entity.h +++ b/impeller/entity/entity.h @@ -46,7 +46,7 @@ class Entity { uint32_t GetStencilDepth() const; - bool Render(ContentRenderer& renderer, RenderPass& parent_pass) const; + bool Render(ContentContext& renderer, RenderPass& parent_pass) const; private: Matrix transformation_; diff --git a/impeller/entity/entity_pass.cc b/impeller/entity/entity_pass.cc index e0a7d1f413390..eec7f0cbfd412 100644 --- a/impeller/entity/entity_pass.cc +++ b/impeller/entity/entity_pass.cc @@ -4,7 +4,7 @@ #include "impeller/entity/entity_pass.h" -#include "impeller/entity/content_renderer.h" +#include "impeller/entity/content_context.h" #include "impeller/geometry/path_builder.h" #include "impeller/renderer/command_buffer.h" #include "impeller/renderer/render_pass.h" @@ -97,7 +97,7 @@ EntityPass* EntityPass::AddSubpass(std::unique_ptr pass) { return subpasses_.emplace_back(std::move(pass)).get(); } -bool EntityPass::Render(ContentRenderer& renderer, +bool EntityPass::Render(ContentContext& renderer, RenderPass& parent_pass) const { for (const auto& entity : entities_) { if (!entity.Render(renderer, parent_pass)) { diff --git a/impeller/entity/entity_pass.h b/impeller/entity/entity_pass.h index 2a9648fe7dd97..1e179915cf3da 100644 --- a/impeller/entity/entity_pass.h +++ b/impeller/entity/entity_pass.h @@ -16,7 +16,7 @@ namespace impeller { -class ContentRenderer; +class ContentContext; class EntityPass { public: @@ -45,7 +45,7 @@ class EntityPass { EntityPass* GetSuperpass() const; - bool Render(ContentRenderer& renderer, RenderPass& parent_pass) const; + bool Render(ContentContext& renderer, RenderPass& parent_pass) const; void IterateAllEntities(std::function iterator); diff --git a/impeller/entity/entity_playground.cc b/impeller/entity/entity_playground.cc index 140a0d93d1479..69e93b74751a4 100644 --- a/impeller/entity/entity_playground.cc +++ b/impeller/entity/entity_playground.cc @@ -4,7 +4,7 @@ #include "impeller/entity/entity_playground.h" -#include "impeller/entity/content_renderer.h" +#include "impeller/entity/content_context.h" namespace impeller { @@ -13,12 +13,12 @@ EntityPlayground::EntityPlayground() = default; EntityPlayground::~EntityPlayground() = default; bool EntityPlayground::OpenPlaygroundHere(Entity entity) { - ContentRenderer renderer(GetContext()); - if (!renderer.IsValid()) { + ContentContext context_context(GetContext()); + if (!context_context.IsValid()) { return false; } Renderer::RenderCallback callback = [&](RenderPass& pass) -> bool { - return entity.Render(renderer, pass); + return entity.Render(context_context, pass); }; return Playground::OpenPlaygroundHere(callback); } diff --git a/impeller/renderer/backend/metal/context_mtl.h b/impeller/renderer/backend/metal/context_mtl.h index 4572128c8be0b..74884899dcf35 100644 --- a/impeller/renderer/backend/metal/context_mtl.h +++ b/impeller/renderer/backend/metal/context_mtl.h @@ -7,6 +7,7 @@ #include #include +#include #include "flutter/fml/macros.h" #include "impeller/renderer/backend/metal/allocator_mtl.h" diff --git a/impeller/renderer/renderer.cc b/impeller/renderer/renderer.cc index 25ec21a9221c1..c00a0ad408b67 100644 --- a/impeller/renderer/renderer.cc +++ b/impeller/renderer/renderer.cc @@ -4,6 +4,8 @@ #include "impeller/renderer/renderer.h" +#include + #include "flutter/fml/logging.h" #include "impeller/base/validation.h" #include "impeller/renderer/command_buffer.h" @@ -11,11 +13,10 @@ namespace impeller { -constexpr size_t kMaxFramesInFlight = 3u; - -Renderer::Renderer(std::shared_ptr context) - : frames_in_flight_sema_( - std::make_shared(kMaxFramesInFlight)), +Renderer::Renderer(std::shared_ptr context, + size_t max_frames_in_flight) + : frames_in_flight_sema_(std::make_shared( + std::max(1u, max_frames_in_flight))), context_(std::move(context)) { if (!context_->IsValid()) { return; diff --git a/impeller/renderer/renderer.h b/impeller/renderer/renderer.h index 74e2d8c5222bf..98f7621c294fc 100644 --- a/impeller/renderer/renderer.h +++ b/impeller/renderer/renderer.h @@ -10,7 +10,7 @@ #include #include "flutter/fml/macros.h" -#include "fml/synchronization/semaphore.h" +#include "flutter/fml/synchronization/semaphore.h" #include "impeller/geometry/size.h" #include "impeller/renderer/context.h" @@ -21,9 +21,12 @@ class RenderPass; class Renderer { public: + static constexpr size_t kDefaultMaxFramesInFlight = 3u; + using RenderCallback = std::function; - Renderer(std::shared_ptr context); + Renderer(std::shared_ptr context, + size_t max_frames_in_flight = kDefaultMaxFramesInFlight); ~Renderer(); From 515bbd0288d0055cae2066947b01b0eb58df023e Mon Sep 17 00:00:00 2001 From: Chinmay Garde Date: Tue, 28 Dec 2021 16:58:35 -0800 Subject: [PATCH 258/433] Make the Metal drawable wrapping routine from the playground a utility. --- impeller/playground/playground.mm | 72 +----------- impeller/renderer/BUILD.gn | 2 + impeller/renderer/backend/metal/surface_mtl.h | 47 ++++++++ .../renderer/backend/metal/surface_mtl.mm | 103 ++++++++++++++++++ impeller/renderer/renderer.cc | 26 +++-- impeller/renderer/renderer.h | 2 +- impeller/renderer/surface.cc | 6 + impeller/renderer/surface.h | 6 +- 8 files changed, 183 insertions(+), 81 deletions(-) create mode 100644 impeller/renderer/backend/metal/surface_mtl.h create mode 100644 impeller/renderer/backend/metal/surface_mtl.mm diff --git a/impeller/playground/playground.mm b/impeller/playground/playground.mm index 0007e7fd26c58..d79740cb8dbdc 100644 --- a/impeller/playground/playground.mm +++ b/impeller/playground/playground.mm @@ -11,12 +11,12 @@ #include "impeller/playground/playground.h" #include "impeller/renderer/allocator.h" #include "impeller/renderer/backend/metal/context_mtl.h" +#include "impeller/renderer/backend/metal/surface_mtl.h" #include "impeller/renderer/backend/metal/texture_mtl.h" #include "impeller/renderer/context.h" #include "impeller/renderer/formats.h" #include "impeller/renderer/render_pass.h" #include "impeller/renderer/renderer.h" -#include "impeller/renderer/surface.h" #define GLFW_INCLUDE_NONE #import "third_party/glfw/include/GLFW/glfw3.h" @@ -138,81 +138,17 @@ static void PlaygroundKeyCallback(GLFWwindow* window, return true; } - auto current_drawable = [layer nextDrawable]; - - if (!current_drawable) { - VALIDATION_LOG << "Could not acquire current drawable."; - return false; - } - - TextureDescriptor msaa_tex_desc; - msaa_tex_desc.type = TextureType::k2DMultisample; - msaa_tex_desc.sample_count = SampleCount::kCount4; - msaa_tex_desc.format = PixelFormat::kB8G8R8A8UNormInt; - msaa_tex_desc.size = { - static_cast(current_drawable.texture.width), - static_cast(current_drawable.texture.height)}; - msaa_tex_desc.usage = static_cast(TextureUsage::kRenderTarget); - - auto msaa_tex = - renderer_.GetContext()->GetPermanentsAllocator()->CreateTexture( - StorageMode::kDeviceTransient, msaa_tex_desc); - if (!msaa_tex) { - FML_LOG(ERROR) << "Could not allocate MSAA resolve texture."; - return false; - } - - msaa_tex->SetLabel("PlaygroundMainColor4xMSAA"); - - TextureDescriptor onscreen_tex_desc; - onscreen_tex_desc.format = PixelFormat::kB8G8R8A8UNormInt; - onscreen_tex_desc.size = msaa_tex_desc.size; - onscreen_tex_desc.usage = - static_cast(TextureUsage::kRenderTarget); - - ColorAttachment color0; - color0.texture = msaa_tex; - color0.clear_color = Color::DarkSlateGray(); - color0.load_action = LoadAction::kClear; - color0.store_action = StoreAction::kMultisampleResolve; - color0.resolve_texture = std::make_shared( - onscreen_tex_desc, current_drawable.texture); - - TextureDescriptor stencil0_tex; - stencil0_tex.type = TextureType::k2DMultisample; - stencil0_tex.sample_count = SampleCount::kCount4; - stencil0_tex.format = PixelFormat::kD32FloatS8UNormInt; - stencil0_tex.size = msaa_tex_desc.size; - stencil0_tex.usage = - static_cast(TextureUsage::kRenderTarget); - auto stencil_texture = - renderer_.GetContext()->GetPermanentsAllocator()->CreateTexture( - StorageMode::kDeviceTransient, stencil0_tex); - stencil_texture->SetLabel("PlaygroundMainStencil"); - - StencilAttachment stencil0; - stencil0.texture = stencil_texture; - stencil0.clear_stencil = 0; - stencil0.load_action = LoadAction::kClear; - stencil0.store_action = StoreAction::kDontCare; - - RenderTarget desc; - desc.SetColorAttachment(color0, 0u); - desc.SetStencilAttachment(stencil0); - - Surface surface(desc); - Renderer::RenderCallback wrapped_callback = [render_callback](auto& pass) { pass.SetLabel("Playground Main Render Pass"); return render_callback(pass); }; - if (!renderer_.Render(surface, wrapped_callback)) { + if (!renderer_.Render(SurfaceMTL::WrapCurrentMetalLayerDrawable( + renderer_.GetContext(), layer), + wrapped_callback)) { VALIDATION_LOG << "Could not render into the surface."; return false; } - - [current_drawable present]; } return true; diff --git a/impeller/renderer/BUILD.gn b/impeller/renderer/BUILD.gn index cb175b91e3b41..5bd501217d404 100644 --- a/impeller/renderer/BUILD.gn +++ b/impeller/renderer/BUILD.gn @@ -31,6 +31,8 @@ impeller_component("renderer") { "backend/metal/shader_function_mtl.mm", "backend/metal/shader_library_mtl.h", "backend/metal/shader_library_mtl.mm", + "backend/metal/surface_mtl.h", + "backend/metal/surface_mtl.mm", "backend/metal/texture_mtl.h", "backend/metal/texture_mtl.mm", "backend/metal/vertex_descriptor_mtl.h", diff --git a/impeller/renderer/backend/metal/surface_mtl.h b/impeller/renderer/backend/metal/surface_mtl.h new file mode 100644 index 0000000000000..44f2c49410991 --- /dev/null +++ b/impeller/renderer/backend/metal/surface_mtl.h @@ -0,0 +1,47 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#pragma once + +#include + +#include "flutter/fml/macros.h" +#include "impeller/renderer/context.h" +#include "impeller/renderer/surface.h" + +namespace impeller { + +class SurfaceMTL final : public Surface { + public: + //---------------------------------------------------------------------------- + /// @brief Wraps the current drawable of the given Metal layer to create + /// a surface Impeller can render to. The surface must be created + /// as late as possible and discarded immediately after rendering + /// to it. + /// + /// @param[in] context The context + /// @param[in] layer The layer whose current drawable to wrap to create a + /// surface. + /// + /// @return A pointer to the wrapped surface or null. + /// + static std::unique_ptr WrapCurrentMetalLayerDrawable( + std::shared_ptr context, + CAMetalLayer* layer); + + // |Surface| + ~SurfaceMTL() override; + + // |Surface| + bool Present() const override; + + private: + id drawable_ = nil; + + SurfaceMTL(RenderTarget target, id drawable); + + FML_DISALLOW_COPY_AND_ASSIGN(SurfaceMTL); +}; + +} // namespace impeller diff --git a/impeller/renderer/backend/metal/surface_mtl.mm b/impeller/renderer/backend/metal/surface_mtl.mm new file mode 100644 index 0000000000000..d952b23b497de --- /dev/null +++ b/impeller/renderer/backend/metal/surface_mtl.mm @@ -0,0 +1,103 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "impeller/renderer/backend/metal/surface_mtl.h" + +#include "flutter/fml/trace_event.h" +#include "impeller/base/validation.h" +#include "impeller/renderer/backend/metal/texture_mtl.h" +#include "impeller/renderer/render_target.h" + +namespace impeller { + +std::unique_ptr SurfaceMTL::WrapCurrentMetalLayerDrawable( + std::shared_ptr context, + CAMetalLayer* layer) { + TRACE_EVENT0("flutter", __FUNCTION__); + + if (context == nullptr || !context->IsValid() || layer == nil) { + return nullptr; + } + + auto current_drawable = [layer nextDrawable]; + + if (!current_drawable) { + VALIDATION_LOG << "Could not acquire current drawable."; + return nullptr; + } + + TextureDescriptor msaa_tex_desc; + msaa_tex_desc.type = TextureType::k2DMultisample; + msaa_tex_desc.sample_count = SampleCount::kCount4; + msaa_tex_desc.format = PixelFormat::kB8G8R8A8UNormInt; + msaa_tex_desc.size = { + static_cast(current_drawable.texture.width), + static_cast(current_drawable.texture.height)}; + msaa_tex_desc.usage = static_cast(TextureUsage::kRenderTarget); + + auto msaa_tex = context->GetPermanentsAllocator()->CreateTexture( + StorageMode::kDeviceTransient, msaa_tex_desc); + if (!msaa_tex) { + FML_LOG(ERROR) << "Could not allocate MSAA resolve texture."; + return nullptr; + } + + msaa_tex->SetLabel("PlaygroundMainColor4xMSAA"); + + TextureDescriptor onscreen_tex_desc; + onscreen_tex_desc.format = PixelFormat::kB8G8R8A8UNormInt; + onscreen_tex_desc.size = msaa_tex_desc.size; + onscreen_tex_desc.usage = static_cast(TextureUsage::kRenderTarget); + + ColorAttachment color0; + color0.texture = msaa_tex; + color0.clear_color = Color::DarkSlateGray(); + color0.load_action = LoadAction::kClear; + color0.store_action = StoreAction::kMultisampleResolve; + color0.resolve_texture = + std::make_shared(onscreen_tex_desc, current_drawable.texture); + + TextureDescriptor stencil0_tex; + stencil0_tex.type = TextureType::k2DMultisample; + stencil0_tex.sample_count = SampleCount::kCount4; + stencil0_tex.format = PixelFormat::kD32FloatS8UNormInt; + stencil0_tex.size = msaa_tex_desc.size; + stencil0_tex.usage = + static_cast(TextureUsage::kRenderTarget); + auto stencil_texture = context->GetPermanentsAllocator()->CreateTexture( + StorageMode::kDeviceTransient, stencil0_tex); + stencil_texture->SetLabel("PlaygroundMainStencil"); + + StencilAttachment stencil0; + stencil0.texture = stencil_texture; + stencil0.clear_stencil = 0; + stencil0.load_action = LoadAction::kClear; + stencil0.store_action = StoreAction::kDontCare; + + RenderTarget desc; + desc.SetColorAttachment(color0, 0u); + desc.SetStencilAttachment(stencil0); + + // The constructor is private. So make_unique may not be used. + return std::unique_ptr( + new SurfaceMTL(std::move(desc), current_drawable)); +} + +SurfaceMTL::SurfaceMTL(RenderTarget target, id drawable) + : Surface(std::move(target)), drawable_(drawable) {} + +// |Surface| +SurfaceMTL::~SurfaceMTL() = default; + +// |Surface| +bool SurfaceMTL::Present() const { + if (drawable_ == nil) { + return false; + } + + [drawable_ present]; + return true; +} + +} // namespace impeller diff --git a/impeller/renderer/renderer.cc b/impeller/renderer/renderer.cc index c00a0ad408b67..ff055ee690da8 100644 --- a/impeller/renderer/renderer.cc +++ b/impeller/renderer/renderer.cc @@ -31,13 +31,13 @@ bool Renderer::IsValid() const { return is_valid_; } -bool Renderer::Render(const Surface& surface, +bool Renderer::Render(std::unique_ptr surface, RenderCallback render_callback) const { if (!IsValid()) { return false; } - if (!surface.IsValid()) { + if (!surface || !surface->IsValid()) { return false; } @@ -49,8 +49,8 @@ bool Renderer::Render(const Surface& surface, command_buffer->SetLabel("Onscreen Command Buffer"); - auto render_pass = - command_buffer->CreateRenderPass(surface.GetTargetRenderPassDescriptor()); + auto render_pass = command_buffer->CreateRenderPass( + surface->GetTargetRenderPassDescriptor()); if (!render_pass) { return false; } @@ -67,13 +67,17 @@ bool Renderer::Render(const Surface& surface, return false; } - return command_buffer->SubmitCommands( - [sema = frames_in_flight_sema_](CommandBuffer::Status result) { - sema->Signal(); - if (result != CommandBuffer::Status::kCompleted) { - VALIDATION_LOG << "Could not commit command buffer."; - } - }); + if (!command_buffer->SubmitCommands( + [sema = frames_in_flight_sema_](CommandBuffer::Status result) { + sema->Signal(); + if (result != CommandBuffer::Status::kCompleted) { + VALIDATION_LOG << "Could not commit command buffer."; + } + })) { + return false; + } + + return surface->Present(); } std::shared_ptr Renderer::GetContext() const { diff --git a/impeller/renderer/renderer.h b/impeller/renderer/renderer.h index 98f7621c294fc..3ba709923b28e 100644 --- a/impeller/renderer/renderer.h +++ b/impeller/renderer/renderer.h @@ -32,7 +32,7 @@ class Renderer { bool IsValid() const; - bool Render(const Surface& surface, RenderCallback callback) const; + bool Render(std::unique_ptr surface, RenderCallback callback) const; std::shared_ptr GetContext() const; diff --git a/impeller/renderer/surface.cc b/impeller/renderer/surface.cc index 3882d9bcda74f..5e21f6169ff91 100644 --- a/impeller/renderer/surface.cc +++ b/impeller/renderer/surface.cc @@ -8,6 +8,8 @@ namespace impeller { +Surface::Surface() : Surface(RenderTarget{}) {} + Surface::Surface(RenderTarget target_desc) : desc_(std::move(target_desc)) { if (auto size = desc_.GetColorAttachmentSize(0u); size.has_value()) { size_ = size.value(); @@ -32,4 +34,8 @@ const RenderTarget& Surface::GetTargetRenderPassDescriptor() const { return desc_; } +bool Surface::Present() const { + return false; +}; + } // namespace impeller diff --git a/impeller/renderer/surface.h b/impeller/renderer/surface.h index db6fc2283a5fc..0ab7e53fc2c90 100644 --- a/impeller/renderer/surface.h +++ b/impeller/renderer/surface.h @@ -16,9 +16,11 @@ namespace impeller { class Surface { public: + Surface(); + Surface(RenderTarget target_desc); - ~Surface(); + virtual ~Surface(); const ISize& GetSize() const; @@ -26,6 +28,8 @@ class Surface { const RenderTarget& GetTargetRenderPassDescriptor() const; + virtual bool Present() const; + private: RenderTarget desc_; ISize size_; From d1b6dbcac3405da75dbb061a8a09a4543bc883f9 Mon Sep 17 00:00:00 2001 From: Chinmay Garde Date: Tue, 28 Dec 2021 17:01:23 -0800 Subject: [PATCH 259/433] Minor: Change the names of the intermediates for the wrapped render target. This used to be from the playground. --- impeller/renderer/backend/metal/surface_mtl.mm | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/impeller/renderer/backend/metal/surface_mtl.mm b/impeller/renderer/backend/metal/surface_mtl.mm index d952b23b497de..52b24d24976f1 100644 --- a/impeller/renderer/backend/metal/surface_mtl.mm +++ b/impeller/renderer/backend/metal/surface_mtl.mm @@ -43,7 +43,7 @@ return nullptr; } - msaa_tex->SetLabel("PlaygroundMainColor4xMSAA"); + msaa_tex->SetLabel("ImpellerOnscreenColor4xMSAA"); TextureDescriptor onscreen_tex_desc; onscreen_tex_desc.format = PixelFormat::kB8G8R8A8UNormInt; @@ -67,7 +67,7 @@ static_cast(TextureUsage::kRenderTarget); auto stencil_texture = context->GetPermanentsAllocator()->CreateTexture( StorageMode::kDeviceTransient, stencil0_tex); - stencil_texture->SetLabel("PlaygroundMainStencil"); + stencil_texture->SetLabel("ImpellerOnscreenStencil"); StencilAttachment stencil0; stencil0.texture = stencil_texture; From 4edb5b0c2d283fbbd36e9951b4efe1a0aaf4ffe1 Mon Sep 17 00:00:00 2001 From: Chinmay Garde Date: Tue, 28 Dec 2021 17:33:08 -0800 Subject: [PATCH 260/433] Allow resizing the playground. --- impeller/playground/playground.h | 3 +++ impeller/playground/playground.mm | 25 ++++++++++++++++++++----- 2 files changed, 23 insertions(+), 5 deletions(-) diff --git a/impeller/playground/playground.h b/impeller/playground/playground.h index 1d35b1c9b07e8..fe83512e85464 100644 --- a/impeller/playground/playground.h +++ b/impeller/playground/playground.h @@ -33,9 +33,12 @@ class Playground : public ::testing::Test { private: Renderer renderer_; Point cursor_position_; + ISize window_size_ = ISize{1024, 768}; void SetCursorPosition(Point pos); + void SetWindowSize(ISize size); + FML_DISALLOW_COPY_AND_ASSIGN(Playground); }; diff --git a/impeller/playground/playground.mm b/impeller/playground/playground.mm index d79740cb8dbdc..68b67eac350a5 100644 --- a/impeller/playground/playground.mm +++ b/impeller/playground/playground.mm @@ -78,7 +78,7 @@ static void PlaygroundKeyCallback(GLFWwindow* window, } ISize Playground::GetWindowSize() const { - return {1024, 768}; + return window_size_; } void Playground::SetCursorPosition(Point pos) { @@ -100,10 +100,6 @@ static void PlaygroundKeyCallback(GLFWwindow* window, fml::ScopedCleanupClosure terminate([]() { ::glfwTerminate(); }); ::glfwWindowHint(GLFW_CLIENT_API, GLFW_NO_API); - // Recreation of the target render buffer is not setup in the playground yet. - // So prevent users from resizing and getting confused that their math is - // wrong. - ::glfwWindowHint(GLFW_RESIZABLE, false); auto window_title = GetWindowTitle(flutter::testing::GetCurrentTestName()); auto window = @@ -114,6 +110,16 @@ static void PlaygroundKeyCallback(GLFWwindow* window, } ::glfwSetWindowUserPointer(window, this); + ::glfwSetWindowSizeCallback( + window, [](GLFWwindow* window, int width, int height) -> void { + auto playground = + reinterpret_cast(::glfwGetWindowUserPointer(window)); + if (!playground) { + return; + } + playground->SetWindowSize( + ISize{std::max(width, 0), std::max(height, 0)}); + }); ::glfwSetKeyCallback(window, &PlaygroundKeyCallback); ::glfwSetCursorPosCallback(window, [](GLFWwindow* window, double x, double y) { @@ -138,6 +144,11 @@ static void PlaygroundKeyCallback(GLFWwindow* window, return true; } + const auto layer_size = layer.bounds.size; + const auto layer_scale = layer.contentsScale; + layer.drawableSize = CGSizeMake(layer_size.width * layer_scale, + layer_size.height * layer_scale); + Renderer::RenderCallback wrapped_callback = [render_callback](auto& pass) { pass.SetLabel("Playground Main Render Pass"); return render_callback(pass); @@ -193,4 +204,8 @@ CompressedImage compressed_image( return texture; } +void Playground::SetWindowSize(ISize size) { + window_size_ = size; +} + } // namespace impeller From e1f20a7434b47b551f0227048cd3102162943a19 Mon Sep 17 00:00:00 2001 From: Chinmay Garde Date: Thu, 30 Dec 2021 14:57:56 -0800 Subject: [PATCH 261/433] Holy size savings Batman! Optimize and compress shaders for size in release modes. --- impeller/compiler/compiler.cc | 4 ++ impeller/tools/build_metal_library.py | 53 ++++++++++++++++++++++++--- impeller/tools/impeller.gni | 12 ++++++ 3 files changed, 64 insertions(+), 5 deletions(-) diff --git a/impeller/compiler/compiler.cc b/impeller/compiler/compiler.cc index 9bd9e076d8e24..27778f43bf929 100644 --- a/impeller/compiler/compiler.cc +++ b/impeller/compiler/compiler.cc @@ -298,6 +298,10 @@ Compiler::Compiler(const fml::Mapping& source_mapping, { spirv_cross::CompilerMSL::Options msl_options; msl_options.platform = spirv_cross::CompilerMSL::Options::Platform::macOS; + // If this version specification changes, the GN rules that process the + // Metal to AIR must be updated as well. + msl_options.msl_version = + spirv_cross::CompilerMSL::Options::make_msl_version(1, 2); msl_compiler->set_msl_options(msl_options); } diff --git a/impeller/tools/build_metal_library.py b/impeller/tools/build_metal_library.py index fa06b8f45d07e..ef4913f72a2f5 100644 --- a/impeller/tools/build_metal_library.py +++ b/impeller/tools/build_metal_library.py @@ -29,6 +29,10 @@ def Main(): parser.add_argument("--source", type=str, action="append", required=True, help="The source file to compile. Can be specified multiple times.") + parser.add_argument("--optimize", action="store_true", default=False, + help="If available optimizations must be applied to the compiled Metal sources.") + parser.add_argument("--platform", required=True, choices=["mac", "ios"], + help="Select the platform.") args = parser.parse_args() @@ -36,11 +40,21 @@ def Main(): command = [ "xcrun", + ] + + if args.platform == "mac": + command += [ + "-sdk", + "macosx", + ] + elif args.platform == "ios": + command += [ + "-sdk", + "iphoneos", + ] + + command += [ "metal", - # TODO: Embeds both sources and driver options in the output. This aids in - # debugging but should be removed from release builds. - "-MO", - "-gline-tables-only", # These warnings are from generated code and would make no sense to the GLSL # author. "-Wno-unused-variable", @@ -49,9 +63,38 @@ def Main(): "-MF", args.depfile, "-o", - args.output + args.output, ] + # The Metal standard must match the specification in impellerc. + if args.platform == "mac": + command += [ + "--std=macos-metal1.2", + ] + elif args.platform == "ios": + command += [ + "--std=ios-metal1.2", + ] + + if args.optimize: + command += [ + # Like -Os (and thus -O2), but reduces code size further. + "-Oz", + # Allow aggressive, lossy floating-point optimizations. + "-ffast-math", + ] + else: + command += [ + # Embeds both sources and driver options in the output. This aids in + # debugging but should be removed from release builds. + "-frecord-sources", + # Assist the sampling profiler. + "-gline-tables-only", + "-g", + # Optimize for debuggability. + "-Og", + ] + command += args.source subprocess.check_call(command) diff --git a/impeller/tools/impeller.gni b/impeller/tools/impeller.gni index f2310fb5e4e7f..84aab888b0d32 100644 --- a/impeller/tools/impeller.gni +++ b/impeller/tools/impeller.gni @@ -75,6 +75,18 @@ template("metal_library") { rebase_path(depfile), ] + if (!is_debug) { + args += [ "--optimize" ] + } + + if (is_ios) { + args += [ "--platform=ios" ] + } else if (is_mac) { + args += [ "--platform=mac" ] + } else { + assert(false, "Unsupported platform for generating Metal shaders.") + } + foreach(source, invoker.sources) { args += [ "--source", From 3a80477adda571f6cc6e5f7c429c8022e91718f8 Mon Sep 17 00:00:00 2001 From: Chinmay Garde Date: Thu, 30 Dec 2021 15:28:01 -0800 Subject: [PATCH 262/433] Forward targets specification to the code generator in impellerc. --- impeller/compiler/compiler.cc | 21 +++++++++++++++- impeller/compiler/compiler.h | 7 ++++++ impeller/compiler/impellerc_main.cc | 1 + impeller/compiler/switches.cc | 37 ++++++++++++++++++++++++++++- impeller/compiler/switches.h | 2 ++ impeller/tools/impeller.gni | 7 ++++++ 6 files changed, 73 insertions(+), 2 deletions(-) diff --git a/impeller/compiler/compiler.cc b/impeller/compiler/compiler.cc index 27778f43bf929..f427aeba7b9c3 100644 --- a/impeller/compiler/compiler.cc +++ b/impeller/compiler/compiler.cc @@ -198,6 +198,19 @@ static spv::ExecutionModel ToExecutionModel(Compiler::SourceType type) { ; } +static spirv_cross::CompilerMSL::Options::Platform +CompilerTargetPlatformToCompilerMSLTargetPlatform( + Compiler::TargetPlatform platform) { + switch (platform) { + case Compiler::TargetPlatform::kIPhoneOS: + return spirv_cross::CompilerMSL::Options::Platform::iOS; + case Compiler::TargetPlatform::kMacOS: + // Unknown should not happen due to prior validation. + case Compiler::TargetPlatform::kUnknown: + return spirv_cross::CompilerMSL::Options::Platform::macOS; + } +} + Compiler::Compiler(const fml::Mapping& source_mapping, SourceOptions source_options, Reflector::Options reflector_options) @@ -208,6 +221,11 @@ Compiler::Compiler(const fml::Mapping& source_mapping, return; } + if (source_options.target_platform == TargetPlatform::kUnknown) { + COMPILER_ERROR << "Target platform not specified."; + return; + } + auto shader_kind = ToShaderCShaderKind(source_options.type); if (shader_kind == shaderc_shader_kind::shaderc_glsl_infer_from_source) { @@ -297,7 +315,8 @@ Compiler::Compiler(const fml::Mapping& source_mapping, { spirv_cross::CompilerMSL::Options msl_options; - msl_options.platform = spirv_cross::CompilerMSL::Options::Platform::macOS; + msl_options.platform = CompilerTargetPlatformToCompilerMSLTargetPlatform( + options_.target_platform); // If this version specification changes, the GN rules that process the // Metal to AIR must be updated as well. msl_options.msl_version = diff --git a/impeller/compiler/compiler.h b/impeller/compiler/compiler.h index 7dec9d39ac303..689ec08183b87 100644 --- a/impeller/compiler/compiler.h +++ b/impeller/compiler/compiler.h @@ -27,6 +27,12 @@ class Compiler { kFragmentShader, }; + enum class TargetPlatform { + kUnknown, + kMacOS, + kIPhoneOS, + }; + static SourceType SourceTypeFromFileName(const std::string& file_name); static std::string EntryPointFromSourceName(const std::string& file_name, @@ -34,6 +40,7 @@ class Compiler { struct SourceOptions { SourceType type = SourceType::kUnknown; + TargetPlatform target_platform = TargetPlatform::kUnknown; std::shared_ptr working_directory; std::vector include_dirs; std::string file_name = "main.glsl"; diff --git a/impeller/compiler/impellerc_main.cc b/impeller/compiler/impellerc_main.cc index 23132fd08277b..de9f762238e1f 100644 --- a/impeller/compiler/impellerc_main.cc +++ b/impeller/compiler/impellerc_main.cc @@ -37,6 +37,7 @@ bool Main(const fml::CommandLine& command_line) { } Compiler::SourceOptions options; + options.target_platform = switches.target_platform; options.type = Compiler::SourceTypeFromFileName(switches.source_file_name); options.working_directory = switches.working_directory; options.file_name = switches.source_file_name; diff --git a/impeller/compiler/switches.cc b/impeller/compiler/switches.cc index 9b01363fd875b..30f20a3006f8e 100644 --- a/impeller/compiler/switches.cc +++ b/impeller/compiler/switches.cc @@ -5,14 +5,25 @@ #include "flutter/impeller/compiler/switches.h" #include +#include #include "flutter/fml/file.h" namespace impeller { namespace compiler { +static const std::map kKnownPlatforms = { + {"macos", Compiler::TargetPlatform::kMacOS}, + {"ios", Compiler::TargetPlatform::kIPhoneOS}, +}; + void Switches::PrintHelp(std::ostream& stream) { stream << std::endl << "Valid Argument are:" << std::endl; + stream << "One of ["; + for (const auto& platform : kKnownPlatforms) { + stream << " --" << platform.first; + } + stream << " ]" << std::endl; stream << "--input=" << std::endl; stream << "--metal=" << std::endl; stream << "--spirv=" << std::endl; @@ -28,8 +39,27 @@ Switches::Switches() = default; Switches::~Switches() = default; +static Compiler::TargetPlatform TargetPlatformFromCommandLine( + const fml::CommandLine& command_line) { + auto target = Compiler::TargetPlatform::kUnknown; + for (const auto& platform : kKnownPlatforms) { + if (command_line.HasOption(platform.first)) { + // If the platform has already been determined, the caller may have + // specified multiple platforms. This is an error and only one must be + // selected. + if (target != Compiler::TargetPlatform::kUnknown) { + return Compiler::TargetPlatform::kUnknown; + } + target = platform.second; + // Keep going to detect duplicates. + } + } + return target; +} + Switches::Switches(const fml::CommandLine& command_line) - : working_directory(std::make_shared( + : target_platform(TargetPlatformFromCommandLine(command_line)), + working_directory(std::make_shared( fml::OpenDirectory(std::filesystem::current_path().native().c_str(), false, // create if necessary, fml::FilePermission::kRead))), @@ -67,6 +97,11 @@ Switches::Switches(const fml::CommandLine& command_line) bool Switches::AreValid(std::ostream& explain) const { bool valid = true; + if (target_platform == Compiler::TargetPlatform::kUnknown) { + explain << "The target platform (only one) was not specified." << std::endl; + valid = false; + } + if (!working_directory || !working_directory->is_valid()) { explain << "Could not figure out working directory." << std::endl; valid = false; diff --git a/impeller/compiler/switches.h b/impeller/compiler/switches.h index 7028d4ab4e95f..2380ca21521e6 100644 --- a/impeller/compiler/switches.h +++ b/impeller/compiler/switches.h @@ -10,12 +10,14 @@ #include "flutter/fml/command_line.h" #include "flutter/fml/macros.h" #include "flutter/fml/unique_fd.h" +#include "flutter/impeller/compiler/compiler.h" #include "flutter/impeller/compiler/include_dir.h" namespace impeller { namespace compiler { struct Switches { + Compiler::TargetPlatform target_platform = Compiler::TargetPlatform::kUnknown; std::shared_ptr working_directory; std::vector include_directories; std::string source_file_name; diff --git a/impeller/tools/impeller.gni b/impeller/tools/impeller.gni index 84aab888b0d32..259107582afb0 100644 --- a/impeller/tools/impeller.gni +++ b/impeller/tools/impeller.gni @@ -142,6 +142,13 @@ template("impeller_shaders") { "--include={{source_dir}}", "--depfile=$depfile_intermediate_path", ] + + if (is_mac) { + args += [ "--macos" ] + } + if (is_ios) { + args += [ "--ios" ] + } } metal_library_target_name = "metal_library_$target_name" From a5265e28ed58c62d1caa0d75cd13ac235d74daff Mon Sep 17 00:00:00 2001 From: Chinmay Garde Date: Thu, 30 Dec 2021 17:53:26 -0800 Subject: [PATCH 263/433] Shader data can now be compiled into the binary. There is no more need to figure out separate packaging of shader data. --- impeller/playground/BUILD.gn | 2 + impeller/playground/playground.mm | 36 ++--- impeller/renderer/backend/metal/context_mtl.h | 8 +- .../renderer/backend/metal/context_mtl.mm | 150 +++++++++++++----- impeller/tools/impeller.gni | 34 +++- impeller/tools/xxd.py | 75 +++++++++ 6 files changed, 244 insertions(+), 61 deletions(-) create mode 100644 impeller/tools/xxd.py diff --git a/impeller/playground/BUILD.gn b/impeller/playground/BUILD.gn index 111e40d763ac7..c69165ec428ba 100644 --- a/impeller/playground/BUILD.gn +++ b/impeller/playground/BUILD.gn @@ -14,6 +14,8 @@ impeller_component("playground") { ] public_deps = [ + "../entity:entity_shaders", + "../fixtures:shader_fixtures", "../renderer", "//flutter/testing", "//third_party/glfw", diff --git a/impeller/playground/playground.mm b/impeller/playground/playground.mm index 68b67eac350a5..59a0c777af8d4 100644 --- a/impeller/playground/playground.mm +++ b/impeller/playground/playground.mm @@ -5,6 +5,8 @@ #include #include "flutter/fml/paths.h" +#include "flutter/impeller/entity/entity_shaders.h" +#include "flutter/impeller/fixtures/shader_fixtures.h" #include "flutter/testing/testing.h" #include "impeller/base/validation.h" #include "impeller/image/compressed_image.h" @@ -28,27 +30,19 @@ namespace impeller { -static std::string ShaderLibraryDirectory() { - auto path_result = fml::paths::GetExecutableDirectoryPath(); - if (!path_result.first) { - return {}; - } - return fml::paths::JoinPaths({path_result.second, "shaders"}); -} +static std::vector> +ShaderLibraryMappingsForPlayground() { + return { + std::make_shared(impeller_entity_shaders_data, + impeller_entity_shaders_length), + std::make_shared(impeller_shader_fixtures_data, + impeller_shader_fixtures_length), -static std::vector ShaderLibraryPathsForPlayground() { - std::vector paths; - paths.emplace_back(fml::paths::JoinPaths( - {ShaderLibraryDirectory(), "shader_fixtures.metallib"})); - paths.emplace_back( - fml::paths::JoinPaths({fml::paths::GetExecutableDirectoryPath().second, - "shaders", "entity.metallib"})); - return paths; + }; } Playground::Playground() - : renderer_( - std::make_shared(ShaderLibraryPathsForPlayground())) {} + : renderer_(ContextMTL::Create(ShaderLibraryMappingsForPlayground())) {} Playground::~Playground() = default; @@ -170,10 +164,10 @@ static void PlaygroundKeyCallback(GLFWwindow* window, CompressedImage compressed_image( flutter::testing::OpenFixtureAsMapping(fixture_name)); // The decoded image is immediately converted into RGBA as that format is - // known to be supported everywhere. For image sources that don't need 32 bit - // pixel strides, this is overkill. Since this is a test fixture we aren't - // necessarily trying to eke out memory savings here and instead favor - // simplicity. + // known to be supported everywhere. For image sources that don't need 32 + // bit pixel strides, this is overkill. Since this is a test fixture we + // aren't necessarily trying to eke out memory savings here and instead + // favor simplicity. auto image = compressed_image.Decode().ConvertToRGBA(); if (!image.IsValid()) { VALIDATION_LOG << "Could not find fixture named " << fixture_name; diff --git a/impeller/renderer/backend/metal/context_mtl.h b/impeller/renderer/backend/metal/context_mtl.h index 74884899dcf35..b7f7080e80c65 100644 --- a/impeller/renderer/backend/metal/context_mtl.h +++ b/impeller/renderer/backend/metal/context_mtl.h @@ -23,7 +23,11 @@ namespace impeller { class ContextMTL final : public Context, public BackendCast { public: - ContextMTL(const std::vector& shader_libraries); + static std::shared_ptr Create( + const std::vector& shader_library_paths); + + static std::shared_ptr Create( + const std::vector>& shader_libraries_data); // |Context| ~ContextMTL() override; @@ -41,6 +45,8 @@ class ContextMTL final : public Context, std::shared_ptr transients_allocator_; bool is_valid_ = false; + ContextMTL(id device, NSArray>* shader_libraries); + // |Context| bool IsValid() const override; diff --git a/impeller/renderer/backend/metal/context_mtl.mm b/impeller/renderer/backend/metal/context_mtl.mm index 5c00d3ac85e85..0b464e685124e 100644 --- a/impeller/renderer/backend/metal/context_mtl.mm +++ b/impeller/renderer/backend/metal/context_mtl.mm @@ -14,36 +14,32 @@ namespace impeller { -static NSArray>* ShaderLibrariesFromFiles( - id device, - const std::vector& libraries_paths) { - NSMutableArray>* found_libraries = [NSMutableArray array]; - for (const auto& library_path : libraries_paths) { - if (!fml::IsFile(library_path)) { - VALIDATION_LOG << "Shader library does not exist at path '" - << library_path << "'"; - continue; - } - NSError* shader_library_error = nil; - auto library = [device newLibraryWithFile:@(library_path.c_str()) - error:&shader_library_error]; - if (!library) { - FML_LOG(ERROR) << "Could not create shader library: " - << shader_library_error.localizedDescription.UTF8String; - continue; - } - [found_libraries addObject:library]; - } - return found_libraries; -} - -ContextMTL::ContextMTL(const std::vector& libraries_paths) - : device_(::MTLCreateSystemDefaultDevice()) { - // Setup device. +ContextMTL::ContextMTL(id device, + NSArray>* shader_libraries) + : device_(device) { + // Validate device. if (!device_) { + VALIDATION_LOG << "Could not setup valid Metal device."; return; } + // Setup the shader library. + { + if (shader_libraries == nil) { + VALIDATION_LOG << "Shader libraries were null."; + return; + } + + // std::make_shared disallowed because of private friend ctor. + auto library = std::shared_ptr( + new ShaderLibraryMTL(shader_libraries)); + if (!library->IsValid()) { + VALIDATION_LOG << "Could not create valid Metal shader library."; + return; + } + shader_library_ = std::move(library); + } + // Setup command queues. render_queue_ = device_.newCommandQueue; transfer_queue_ = device_.newCommandQueue; @@ -55,18 +51,6 @@ render_queue_.label = @"Impeller Render Queue"; transfer_queue_.label = @"Impeller Transfer Queue"; - // Setup the shader library. - { - // std::make_shared disallowed because of private friend ctor. - auto library = std::shared_ptr(new ShaderLibraryMTL( - ShaderLibrariesFromFiles(device_, libraries_paths))); - if (!library->IsValid()) { - VALIDATION_LOG << "Could not create valid Metal shader library."; - return; - } - shader_library_ = std::move(library); - } - // Setup the pipeline library. { // pipeline_library_ = @@ -96,6 +80,96 @@ is_valid_ = true; } +static NSArray>* MTLShaderLibraryFromFilePaths( + id device, + const std::vector& libraries_paths) { + NSMutableArray>* found_libraries = [NSMutableArray array]; + for (const auto& library_path : libraries_paths) { + if (!fml::IsFile(library_path)) { + VALIDATION_LOG << "Shader library does not exist at path '" + << library_path << "'"; + return nil; + } + NSError* shader_library_error = nil; + auto library = [device newLibraryWithFile:@(library_path.c_str()) + error:&shader_library_error]; + if (!library) { + FML_LOG(ERROR) << "Could not create shader library: " + << shader_library_error.localizedDescription.UTF8String; + return nil; + } + [found_libraries addObject:library]; + } + return found_libraries; +} + +static NSArray>* MTLShaderLibraryFromFileData( + id device, + const std::vector>& libraries_data) { + NSMutableArray>* found_libraries = [NSMutableArray array]; + for (const auto& library_data : libraries_data) { + if (library_data == nullptr) { + FML_LOG(ERROR) << "Shader library data was null."; + return nil; + } + + __block auto data = library_data; + + auto dispatch_data = + ::dispatch_data_create(library_data->GetMapping(), // buffer + library_data->GetSize(), // size + dispatch_get_main_queue(), // queue + ^() { + // We just need a reference. + data.reset(); + } // destructor + ); + if (!dispatch_data) { + FML_LOG(ERROR) << "Could not wrap shader data in dispatch data."; + return nil; + } + + NSError* shader_library_error = nil; + auto library = [device newLibraryWithData:dispatch_data + error:&shader_library_error]; + if (!library) { + FML_LOG(ERROR) << "Could not create shader library: " + << shader_library_error.localizedDescription.UTF8String; + return nil; + } + [found_libraries addObject:library]; + } + return found_libraries; +} + +static id CreateMetalDevice() { + return ::MTLCreateSystemDefaultDevice(); +} + +std::shared_ptr ContextMTL::Create( + const std::vector& shader_library_paths) { + auto device = CreateMetalDevice(); + auto context = std::shared_ptr(new ContextMTL( + device, MTLShaderLibraryFromFilePaths(device, shader_library_paths))); + if (!context->IsValid()) { + FML_LOG(ERROR) << "Could not create Metal context."; + return nullptr; + } + return context; +} + +std::shared_ptr ContextMTL::Create( + const std::vector>& shader_libraries_data) { + auto device = CreateMetalDevice(); + auto context = std::shared_ptr(new ContextMTL( + device, MTLShaderLibraryFromFileData(device, shader_libraries_data))); + if (!context->IsValid()) { + FML_LOG(ERROR) << "Could not create Metal context."; + return nullptr; + } + return context; +} + ContextMTL::~ContextMTL() = default; bool ContextMTL::IsValid() const { diff --git a/impeller/tools/impeller.gni b/impeller/tools/impeller.gni index 259107582afb0..74a6e10af2826 100644 --- a/impeller/tools/impeller.gni +++ b/impeller/tools/impeller.gni @@ -100,6 +100,7 @@ template("impeller_shaders") { assert(defined(invoker.shaders), "Impeller shaders must be specified.") assert(defined(invoker.name), "Name of the shader library must be specified.") + base_target_name = target_name impellerc_target_name = "impellerc_$target_name" compiled_action_foreach(impellerc_target_name) { tool = "//flutter/impeller/compiler:impellerc" @@ -192,9 +193,40 @@ template("impeller_shaders") { ] } + generate_embedder_data_sources = "embedded_data_gen_sources_$target_name" + action(generate_embedder_data_sources) { + metal_library_files = get_target_outputs(":$metal_library_target_name") + metal_library_file = metal_library_files[0] + inputs = [ metal_library_file ] + output_header = "$target_gen_dir/$base_target_name.h" + output_source = "$target_gen_dir/$base_target_name.c" + outputs = [ + output_header, + output_source, + ] + args = [ + "--symbol-name", + base_target_name, + "--output-header", + rebase_path(output_header), + "--output-source", + rebase_path(output_source), + "--source", + rebase_path(metal_library_file), + ] + script = "//flutter/impeller/tools/xxd.py" + deps = [ ":$metal_library_target_name" ] + } + + shader_embedded_data_target_name = "embedded_data_$target_name" + source_set(shader_embedded_data_target_name) { + sources = get_target_outputs(":$generate_embedder_data_sources") + deps = [ ":$generate_embedder_data_sources" ] + } + group(target_name) { public_deps = [ - ":$metal_library_target_name", + ":$shader_embedded_data_target_name", ":$shader_glue_target_name", ] } diff --git a/impeller/tools/xxd.py b/impeller/tools/xxd.py new file mode 100644 index 0000000000000..5b0baff0c9721 --- /dev/null +++ b/impeller/tools/xxd.py @@ -0,0 +1,75 @@ +# Copyright 2013 The Flutter Authors. All rights reserved. +# Use of this source code is governed by a BSD-style license that can be +# found in the LICENSE file. + +import sys + +import argparse +import errno +import os +import struct + +def MakeDirectories(path): + try: + os.makedirs(path) + except OSError as exc: + if exc.errno == errno.EEXIST and os.path.isdir(path): + pass + else: + raise + +# Dump the bytes of file into a C translation unit. +# This can be used to embed the file contents into a binary. +def Main(): + parser = argparse.ArgumentParser() + parser.add_argument("--symbol-name", + type=str, required=True, + help="The name of the symbol referencing the data.") + parser.add_argument("--output-header", + type=str, required=True, + help="The header file containing the symbol reference.") + parser.add_argument("--output-source", + type=str, required=True, + help="The source file containing the file bytes.") + parser.add_argument("--source", + type=str, required=True, + help="The source file whose contents to embed in the output source file.") + + args = parser.parse_args() + + assert(os.path.exists(args.source)) + + output_header = os.path.abspath(args.output_header) + output_source = os.path.abspath(args.output_source) + + MakeDirectories(os.path.dirname(output_header)) + MakeDirectories(os.path.dirname(output_source)) + + with open(args.source, "rb") as source, open(output_source, "w") as output: + data_len = 0 + output.write(f"const unsigned char impeller_{args.symbol_name}_data[] =\n") + output.write("{\n") + while True: + byte = source.read(1) + if not byte: + break + data_len += 1 + output.write(f"{ord(byte)},") + output.write("};\n") + output.write(f"const unsigned long impeller_{args.symbol_name}_length = {data_len};\n") + + with open(output_header, "w") as output: + output.write("#pragma once\n") + output.write("#ifdef __cplusplus\n") + output.write("extern \"C\" {\n") + output.write("#endif\n\n") + + output.write(f"extern unsigned char impeller_{args.symbol_name}_data[];\n") + output.write(f"extern unsigned long impeller_{args.symbol_name}_length;\n\n") + + output.write("#ifdef __cplusplus\n") + output.write("}\n") + output.write("#endif\n") + +if __name__ == '__main__': + Main() From 4af74f07edbe7b37f441ae85c312d08183177ac6 Mon Sep 17 00:00:00 2001 From: Chinmay Garde Date: Mon, 3 Jan 2022 17:01:58 -0800 Subject: [PATCH 264/433] Fix pipeline library issue related to not tracking pending futures. --- .../display_list/display_list_dispatcher.cc | 4 ++++ .../display_list/display_list_dispatcher.h | 2 ++ impeller/entity/content_context.cc | 3 +++ impeller/entity/content_context.h | 10 ++++---- .../backend/metal/pipeline_library_mtl.h | 12 ++++------ .../backend/metal/pipeline_library_mtl.mm | 23 ++++++------------- impeller/renderer/pipeline.h | 9 +++++++- 7 files changed, 35 insertions(+), 28 deletions(-) diff --git a/impeller/display_list/display_list_dispatcher.cc b/impeller/display_list/display_list_dispatcher.cc index ba6f0e1aa407b..540c08965fde0 100644 --- a/impeller/display_list/display_list_dispatcher.cc +++ b/impeller/display_list/display_list_dispatcher.cc @@ -473,4 +473,8 @@ void DisplayListDispatcher::drawShadow(const SkPath& path, UNIMPLEMENTED; } +Picture DisplayListDispatcher::EndRecordingAsPicture() { + return canvas_.EndRecordingAsPicture(); +} + } // namespace impeller diff --git a/impeller/display_list/display_list_dispatcher.h b/impeller/display_list/display_list_dispatcher.h index d007de6653aa8..c32f295f6e4dd 100644 --- a/impeller/display_list/display_list_dispatcher.h +++ b/impeller/display_list/display_list_dispatcher.h @@ -18,6 +18,8 @@ class DisplayListDispatcher final : public flutter::Dispatcher { ~DisplayListDispatcher(); + Picture EndRecordingAsPicture(); + // |flutter::Dispatcher| void setAntiAlias(bool aa) override; diff --git a/impeller/entity/content_context.cc b/impeller/entity/content_context.cc index ed0dcc5ddb04a..5a628518df3ab 100644 --- a/impeller/entity/content_context.cc +++ b/impeller/entity/content_context.cc @@ -4,6 +4,8 @@ #include "impeller/entity/content_context.h" +#include + namespace impeller { ContentContext::ContentContext(std::shared_ptr context) @@ -23,6 +25,7 @@ ContentContext::ContentContext(std::shared_ptr context) // Pipelines that are variants of the base pipelines with custom descriptors. if (auto solid_fill_pipeline = solid_fill_pipelines_[{}]->WaitAndGet()) { auto clip_pipeline_descriptor = solid_fill_pipeline->GetDescriptor(); + clip_pipeline_descriptor.SetLabel("Clip Pipeline"); // Write to the stencil buffer. StencilAttachmentDescriptor stencil0; stencil0.stencil_compare = CompareFunction::kGreaterEqual; diff --git a/impeller/entity/content_context.h b/impeller/entity/content_context.h index 0143fb29135b6..09870f775d402 100644 --- a/impeller/entity/content_context.h +++ b/impeller/entity/content_context.h @@ -111,14 +111,16 @@ class ContentContext { return found->second->WaitAndGet(); } - auto found = container.find({}); + auto prototype = container.find({}); // The prototype must always be initialized in the constructor. - FML_CHECK(found != container.end()); + FML_CHECK(prototype != container.end()); - auto variant_future = found->second->WaitAndGet()->CreateVariant( - [&opts](PipelineDescriptor& desc) { + auto variant_future = prototype->second->WaitAndGet()->CreateVariant( + [&opts, variants_count = container.size()](PipelineDescriptor& desc) { ApplyOptionsToDescriptor(desc, opts); + desc.SetLabel( + SPrintF("%s V#%zu", desc.GetLabel().c_str(), variants_count)); }); auto variant = std::make_unique(std::move(variant_future)); auto variant_pipeline = variant->WaitAndGet(); diff --git a/impeller/renderer/backend/metal/pipeline_library_mtl.h b/impeller/renderer/backend/metal/pipeline_library_mtl.h index 612a95a1de611..41c62da6a77c9 100644 --- a/impeller/renderer/backend/metal/pipeline_library_mtl.h +++ b/impeller/renderer/backend/metal/pipeline_library_mtl.h @@ -26,18 +26,16 @@ class PipelineLibraryMTL final : public PipelineLibrary { private: friend ContextMTL; - using Pipelines = std::unordered_map, - ComparableHash, - ComparableEqual>; + using Pipelines = + std::unordered_map>, + ComparableHash, + ComparableEqual>; id device_ = nullptr; Pipelines pipelines_; PipelineLibraryMTL(id device); - void SavePipeline(PipelineDescriptor descriptor, - std::shared_ptr pipeline); - // |PipelineLibrary| PipelineFuture GetRenderPipeline(PipelineDescriptor descriptor) override; diff --git a/impeller/renderer/backend/metal/pipeline_library_mtl.mm b/impeller/renderer/backend/metal/pipeline_library_mtl.mm index 2d33f463706e8..e677c931f44ea 100644 --- a/impeller/renderer/backend/metal/pipeline_library_mtl.mm +++ b/impeller/renderer/backend/metal/pipeline_library_mtl.mm @@ -67,19 +67,16 @@ return [device newDepthStencilStateWithDescriptor:descriptor]; } -std::future> PipelineLibraryMTL::GetRenderPipeline( +PipelineFuture PipelineLibraryMTL::GetRenderPipeline( PipelineDescriptor descriptor) { - auto promise = std::make_shared>>(); - auto future = promise->get_future(); if (auto found = pipelines_.find(descriptor); found != pipelines_.end()) { - promise->set_value(nullptr); - return future; + return found->second; } - // TODO(csg): There is a bug here where multiple calls to GetRenderPipeline - // will result in multiple render pipelines of the same descriptor being - // created till the first instance of the creation invokes its completion - // callback. + auto promise = std::make_shared>>(); + auto future = PipelineFuture{promise->get_future()}; + + pipelines_[descriptor] = future; auto weak_this = weak_from_this(); @@ -100,6 +97,7 @@ promise->set_value(nullptr); return; } + auto new_pipeline = std::shared_ptr(new PipelineMTL( weak_this, descriptor, // @@ -107,7 +105,6 @@ CreateDepthStencilDescriptor(descriptor, device_) // )); promise->set_value(new_pipeline); - this->SavePipeline(descriptor, new_pipeline); }; [device_ newRenderPipelineStateWithDescriptor:GetMTLRenderPipelineDescriptor( descriptor) @@ -115,10 +112,4 @@ return future; } -void PipelineLibraryMTL::SavePipeline( - PipelineDescriptor descriptor, - std::shared_ptr pipeline) { - pipelines_[descriptor] = std::move(pipeline); -} - } // namespace impeller diff --git a/impeller/renderer/pipeline.h b/impeller/renderer/pipeline.h index 9362cbd0e6fd5..47df168d8b1dd 100644 --- a/impeller/renderer/pipeline.h +++ b/impeller/renderer/pipeline.h @@ -21,7 +21,7 @@ class Pipeline; // would be a concurrency pessimization. // // Use a struct that stores the future and the descriptor separately. -using PipelineFuture = std::future>; +using PipelineFuture = std::shared_future>; //------------------------------------------------------------------------------ /// @brief Describes the fixed function and programmable aspects of @@ -48,6 +48,13 @@ class Pipeline { virtual bool IsValid() const = 0; + //---------------------------------------------------------------------------- + /// @brief Get the descriptor that was responsible for creating this + /// pipeline. It may be copied and modified to create a pipeline + /// variant. + /// + /// @return The descriptor. + /// const PipelineDescriptor& GetDescriptor() const; PipelineFuture CreateVariant( From a00220c03895d4535d8cd2d2038a976532abd6aa Mon Sep 17 00:00:00 2001 From: Chinmay Garde Date: Wed, 5 Jan 2022 14:08:36 -0800 Subject: [PATCH 265/433] Don't waste space on an unnecessary depth buffer. --- impeller/aiks/canvas.cc | 4 ++++ impeller/aiks/canvas.h | 2 ++ impeller/display_list/display_list_dispatcher.cc | 5 +++++ impeller/display_list/display_list_dispatcher.h | 3 +++ impeller/renderer/backend/metal/formats_mtl.h | 2 ++ impeller/renderer/backend/metal/surface_mtl.mm | 2 +- impeller/renderer/formats.h | 3 +++ impeller/renderer/pipeline_builder.h | 2 +- impeller/renderer/render_target.cc | 2 +- impeller/renderer/renderer_unittests.cc | 2 +- 10 files changed, 23 insertions(+), 4 deletions(-) diff --git a/impeller/aiks/canvas.cc b/impeller/aiks/canvas.cc index 99d7a47c58368..3af66e13e8918 100644 --- a/impeller/aiks/canvas.cc +++ b/impeller/aiks/canvas.cc @@ -53,6 +53,10 @@ void Canvas::Concat(const Matrix& xformation) { xformation_stack_.back().xformation = xformation * GetCurrentTransformation(); } +void Canvas::ResetTransform() { + xformation_stack_.back().xformation = {}; +} + void Canvas::Transform(const Matrix& xformation) { Concat(xformation); } diff --git a/impeller/aiks/canvas.h b/impeller/aiks/canvas.h index 2bf6aeeed4816..2b00151f585a0 100644 --- a/impeller/aiks/canvas.h +++ b/impeller/aiks/canvas.h @@ -41,6 +41,8 @@ class Canvas { const Matrix& GetCurrentTransformation() const; + void ResetTransform(); + void Transform(const Matrix& xformation); void Concat(const Matrix& xformation); diff --git a/impeller/display_list/display_list_dispatcher.cc b/impeller/display_list/display_list_dispatcher.cc index 540c08965fde0..c349a55e5a5d6 100644 --- a/impeller/display_list/display_list_dispatcher.cc +++ b/impeller/display_list/display_list_dispatcher.cc @@ -206,6 +206,11 @@ void DisplayListDispatcher::transformFullPerspective(SkScalar mxx, canvas_.Transform(xformation); } +// |flutter::Dispatcher| +void DisplayListDispatcher::transformReset() { + canvas_.ResetTransform(); +} + static Rect ToRect(const SkRect& rect) { return Rect::MakeLTRB(rect.fLeft, rect.fTop, rect.fRight, rect.fBottom); } diff --git a/impeller/display_list/display_list_dispatcher.h b/impeller/display_list/display_list_dispatcher.h index c32f295f6e4dd..67a95d94b82d3 100644 --- a/impeller/display_list/display_list_dispatcher.h +++ b/impeller/display_list/display_list_dispatcher.h @@ -118,6 +118,9 @@ class DisplayListDispatcher final : public flutter::Dispatcher { SkScalar mwz, SkScalar mwt) override; + // |flutter::Dispatcher| + void transformReset() override; + // |flutter::Dispatcher| void clipRect(const SkRect& rect, SkClipOp clip_op, bool is_aa) override; diff --git a/impeller/renderer/backend/metal/formats_mtl.h b/impeller/renderer/backend/metal/formats_mtl.h index 9ebf190477561..742435d3938bf 100644 --- a/impeller/renderer/backend/metal/formats_mtl.h +++ b/impeller/renderer/backend/metal/formats_mtl.h @@ -29,6 +29,8 @@ constexpr MTLPixelFormat ToMTLPixelFormat(PixelFormat format) { return MTLPixelFormatDepth32Float_Stencil8; case PixelFormat::kR8G8B8A8UNormInt: return MTLPixelFormatRGBA8Unorm; + case PixelFormat::kS8UInt: + return MTLPixelFormatStencil8; case PixelFormat::kR8G8B8A8UNormIntSRGB: return MTLPixelFormatRGBA8Unorm_sRGB; } diff --git a/impeller/renderer/backend/metal/surface_mtl.mm b/impeller/renderer/backend/metal/surface_mtl.mm index 52b24d24976f1..9c66eadfefe8e 100644 --- a/impeller/renderer/backend/metal/surface_mtl.mm +++ b/impeller/renderer/backend/metal/surface_mtl.mm @@ -61,7 +61,7 @@ TextureDescriptor stencil0_tex; stencil0_tex.type = TextureType::k2DMultisample; stencil0_tex.sample_count = SampleCount::kCount4; - stencil0_tex.format = PixelFormat::kD32FloatS8UNormInt; + stencil0_tex.format = PixelFormat::kS8UInt; stencil0_tex.size = msaa_tex_desc.size; stencil0_tex.usage = static_cast(TextureUsage::kRenderTarget); diff --git a/impeller/renderer/formats.h b/impeller/renderer/formats.h index 72c49a1a71bf1..4b4a4dd143f2f 100644 --- a/impeller/renderer/formats.h +++ b/impeller/renderer/formats.h @@ -50,6 +50,7 @@ enum class PixelFormat { kR8G8B8A8UNormIntSRGB, kB8G8R8A8UNormInt, kB8G8R8A8UNormIntSRGB, + kS8UInt, // Esoteric formats only used as render targets. kD32FloatS8UNormInt, }; @@ -166,6 +167,8 @@ constexpr size_t BytesPerPixelForPixelFormat(PixelFormat format) { switch (format) { case PixelFormat::kUnknown: return 0u; + case PixelFormat::kS8UInt: + return 1u; case PixelFormat::kR8G8B8A8UNormInt: case PixelFormat::kR8G8B8A8UNormIntSRGB: case PixelFormat::kB8G8R8A8UNormInt: diff --git a/impeller/renderer/pipeline_builder.h b/impeller/renderer/pipeline_builder.h index 82e6a742d33a2..90c6c7884f63a 100644 --- a/impeller/renderer/pipeline_builder.h +++ b/impeller/renderer/pipeline_builder.h @@ -109,7 +109,7 @@ struct PipelineBuilder { StencilAttachmentDescriptor stencil0; stencil0.stencil_compare = CompareFunction::kLessEqual; desc.SetStencilAttachmentDescriptors(stencil0); - desc.SetStencilPixelFormat(PixelFormat::kD32FloatS8UNormInt); + desc.SetStencilPixelFormat(PixelFormat::kS8UInt); } return true; diff --git a/impeller/renderer/render_target.cc b/impeller/renderer/render_target.cc index 0e6f8bcf20e14..fb6d8ce58dc57 100644 --- a/impeller/renderer/render_target.cc +++ b/impeller/renderer/render_target.cc @@ -188,7 +188,7 @@ RenderTarget RenderTarget::CreateOffscreen(const Context& context, static_cast(TextureUsage::kShaderRead); TextureDescriptor stencil_tex0; - stencil_tex0.format = PixelFormat::kD32FloatS8UNormInt; + stencil_tex0.format = PixelFormat::kS8UInt; stencil_tex0.size = size; stencil_tex0.usage = static_cast(TextureUsage::kRenderTarget); diff --git a/impeller/renderer/renderer_unittests.cc b/impeller/renderer/renderer_unittests.cc index 57933170d1a54..097cba63cd060 100644 --- a/impeller/renderer/renderer_unittests.cc +++ b/impeller/renderer/renderer_unittests.cc @@ -224,7 +224,7 @@ TEST_F(RendererTest, CanRenderToTexture) { stencil0.store_action = StoreAction::kDontCare; TextureDescriptor stencil_texture_desc; stencil_texture_desc.size = texture_descriptor.size; - stencil_texture_desc.format = PixelFormat::kD32FloatS8UNormInt; + stencil_texture_desc.format = PixelFormat::kS8UInt; stencil_texture_desc.usage = static_cast(TextureUsage::kRenderTarget); stencil0.texture = context->GetPermanentsAllocator()->CreateTexture( From e92b74b0b5b101ae5d2243cfebde9e77b678809c Mon Sep 17 00:00:00 2001 From: Chinmay Garde Date: Thu, 6 Jan 2022 12:16:50 -0800 Subject: [PATCH 266/433] Setup default pixel formats. --- impeller/compiler/compiler_unittests.cc | 1 + impeller/playground/playground.mm | 5 +- impeller/renderer/backend/metal/formats_mtl.h | 22 +++++++++ .../renderer/backend/metal/surface_mtl.mm | 47 ++++++++++++------- impeller/renderer/formats.h | 9 ++++ impeller/renderer/pipeline_builder.h | 4 +- impeller/renderer/render_target.cc | 4 +- 7 files changed, 71 insertions(+), 21 deletions(-) diff --git a/impeller/compiler/compiler_unittests.cc b/impeller/compiler/compiler_unittests.cc index 77ad7b88ce975..f681d34281d33 100644 --- a/impeller/compiler/compiler_unittests.cc +++ b/impeller/compiler/compiler_unittests.cc @@ -30,6 +30,7 @@ class CompilerTest : public ::testing::Test { return false; } Compiler::SourceOptions compiler_options(fixture_name); + compiler_options.target_platform = Compiler::TargetPlatform::kMacOS; compiler_options.working_directory = std::make_shared( flutter::testing::OpenFixturesDirectory()); Reflector::Options reflector_options; diff --git a/impeller/playground/playground.mm b/impeller/playground/playground.mm index 59a0c777af8d4..9918e9fe3faff 100644 --- a/impeller/playground/playground.mm +++ b/impeller/playground/playground.mm @@ -13,6 +13,7 @@ #include "impeller/playground/playground.h" #include "impeller/renderer/allocator.h" #include "impeller/renderer/backend/metal/context_mtl.h" +#include "impeller/renderer/backend/metal/formats_mtl.h" #include "impeller/renderer/backend/metal/surface_mtl.h" #include "impeller/renderer/backend/metal/texture_mtl.h" #include "impeller/renderer/context.h" @@ -127,7 +128,8 @@ static void PlaygroundKeyCallback(GLFWwindow* window, NSWindow* cocoa_window = ::glfwGetCocoaWindow(window); CAMetalLayer* layer = [CAMetalLayer layer]; layer.device = ContextMTL::Cast(*renderer_.GetContext()).GetMTLDevice(); - layer.pixelFormat = MTLPixelFormatBGRA8Unorm; + // This pixel format is one of the documented supported formats. + layer.pixelFormat = ToMTLPixelFormat(PixelFormat::kDefaultColor); cocoa_window.contentView.layer = layer; cocoa_window.contentView.wantsLayer = YES; @@ -175,6 +177,7 @@ CompressedImage compressed_image( } auto texture_descriptor = TextureDescriptor{}; + // We just converted to RGBA above. texture_descriptor.format = PixelFormat::kR8G8B8A8UNormInt; texture_descriptor.size = image.GetSize(); texture_descriptor.mip_count = 1u; diff --git a/impeller/renderer/backend/metal/formats_mtl.h b/impeller/renderer/backend/metal/formats_mtl.h index 742435d3938bf..22fdcedad3eb3 100644 --- a/impeller/renderer/backend/metal/formats_mtl.h +++ b/impeller/renderer/backend/metal/formats_mtl.h @@ -17,6 +17,28 @@ namespace impeller { class RenderTarget; +constexpr PixelFormat FromMTLPixelFormat(MTLPixelFormat format) { + switch (format) { + case MTLPixelFormatInvalid: + return PixelFormat::kUnknown; + case MTLPixelFormatBGRA8Unorm: + return PixelFormat::kB8G8R8A8UNormInt; + case MTLPixelFormatBGRA8Unorm_sRGB: + return PixelFormat::kB8G8R8A8UNormIntSRGB; + case MTLPixelFormatDepth32Float_Stencil8: + return PixelFormat::kD32FloatS8UNormInt; + case MTLPixelFormatRGBA8Unorm: + return PixelFormat::kR8G8B8A8UNormInt; + case MTLPixelFormatStencil8: + return PixelFormat::kS8UInt; + case MTLPixelFormatRGBA8Unorm_sRGB: + return PixelFormat::kR8G8B8A8UNormIntSRGB; + default: + return PixelFormat::kUnknown; + } + return PixelFormat::kUnknown; +} + constexpr MTLPixelFormat ToMTLPixelFormat(PixelFormat format) { switch (format) { case PixelFormat::kUnknown: diff --git a/impeller/renderer/backend/metal/surface_mtl.mm b/impeller/renderer/backend/metal/surface_mtl.mm index 9c66eadfefe8e..9a8ed8185ccf7 100644 --- a/impeller/renderer/backend/metal/surface_mtl.mm +++ b/impeller/renderer/backend/metal/surface_mtl.mm @@ -6,6 +6,7 @@ #include "flutter/fml/trace_event.h" #include "impeller/base/validation.h" +#include "impeller/renderer/backend/metal/formats_mtl.h" #include "impeller/renderer/backend/metal/texture_mtl.h" #include "impeller/renderer/render_target.h" @@ -27,46 +28,60 @@ return nullptr; } - TextureDescriptor msaa_tex_desc; - msaa_tex_desc.type = TextureType::k2DMultisample; - msaa_tex_desc.sample_count = SampleCount::kCount4; - msaa_tex_desc.format = PixelFormat::kB8G8R8A8UNormInt; - msaa_tex_desc.size = { + const auto color_format = + FromMTLPixelFormat(current_drawable.texture.pixelFormat); + + if (color_format == PixelFormat::kUnknown) { + VALIDATION_LOG << "Unknown drawable color format."; + return nullptr; + } + + TextureDescriptor color0_tex_desc; + color0_tex_desc.type = TextureType::k2DMultisample; + color0_tex_desc.sample_count = SampleCount::kCount4; + color0_tex_desc.format = color_format; + color0_tex_desc.size = { static_cast(current_drawable.texture.width), static_cast(current_drawable.texture.height)}; - msaa_tex_desc.usage = static_cast(TextureUsage::kRenderTarget); + color0_tex_desc.usage = static_cast(TextureUsage::kRenderTarget); auto msaa_tex = context->GetPermanentsAllocator()->CreateTexture( - StorageMode::kDeviceTransient, msaa_tex_desc); + StorageMode::kDeviceTransient, color0_tex_desc); if (!msaa_tex) { - FML_LOG(ERROR) << "Could not allocate MSAA resolve texture."; + VALIDATION_LOG << "Could not allocate MSAA resolve texture."; return nullptr; } msaa_tex->SetLabel("ImpellerOnscreenColor4xMSAA"); - TextureDescriptor onscreen_tex_desc; - onscreen_tex_desc.format = PixelFormat::kB8G8R8A8UNormInt; - onscreen_tex_desc.size = msaa_tex_desc.size; - onscreen_tex_desc.usage = static_cast(TextureUsage::kRenderTarget); + TextureDescriptor color0_resolve_tex_desc; + color0_resolve_tex_desc.format = color_format; + color0_resolve_tex_desc.size = color0_tex_desc.size; + color0_resolve_tex_desc.usage = + static_cast(TextureUsage::kRenderTarget); ColorAttachment color0; color0.texture = msaa_tex; color0.clear_color = Color::DarkSlateGray(); color0.load_action = LoadAction::kClear; color0.store_action = StoreAction::kMultisampleResolve; - color0.resolve_texture = - std::make_shared(onscreen_tex_desc, current_drawable.texture); + color0.resolve_texture = std::make_shared( + color0_resolve_tex_desc, current_drawable.texture); TextureDescriptor stencil0_tex; stencil0_tex.type = TextureType::k2DMultisample; stencil0_tex.sample_count = SampleCount::kCount4; - stencil0_tex.format = PixelFormat::kS8UInt; - stencil0_tex.size = msaa_tex_desc.size; + stencil0_tex.format = PixelFormat::kDefaultStencil; + stencil0_tex.size = color0_tex_desc.size; stencil0_tex.usage = static_cast(TextureUsage::kRenderTarget); auto stencil_texture = context->GetPermanentsAllocator()->CreateTexture( StorageMode::kDeviceTransient, stencil0_tex); + + if (!stencil_texture) { + VALIDATION_LOG << "Could not create stencil texture."; + return nullptr; + } stencil_texture->SetLabel("ImpellerOnscreenStencil"); StencilAttachment stencil0; diff --git a/impeller/renderer/formats.h b/impeller/renderer/formats.h index 4b4a4dd143f2f..c6b9440b22848 100644 --- a/impeller/renderer/formats.h +++ b/impeller/renderer/formats.h @@ -51,8 +51,17 @@ enum class PixelFormat { kB8G8R8A8UNormInt, kB8G8R8A8UNormIntSRGB, kS8UInt, + // Esoteric formats only used as render targets. kD32FloatS8UNormInt, + + // Defaults. If you don't know which ones to use, these are usually a safe + // bet. + // + // On Metal, this is a support format for layer drawable and can be used to + // specify the format of the resolve texture if needed. + kDefaultColor = kB8G8R8A8UNormInt, + kDefaultStencil = kS8UInt, }; enum class BlendFactor { diff --git a/impeller/renderer/pipeline_builder.h b/impeller/renderer/pipeline_builder.h index 90c6c7884f63a..9002c803aa5dd 100644 --- a/impeller/renderer/pipeline_builder.h +++ b/impeller/renderer/pipeline_builder.h @@ -99,7 +99,7 @@ struct PipelineBuilder { // Configure the sole color attachments pixel format. This is by // convention. ColorAttachmentDescriptor color0; - color0.format = PixelFormat::kB8G8R8A8UNormInt; + color0.format = PixelFormat::kDefaultColor; color0.blending_enabled = true; desc.SetColorAttachmentDescriptor(0u, std::move(color0)); } @@ -109,7 +109,7 @@ struct PipelineBuilder { StencilAttachmentDescriptor stencil0; stencil0.stencil_compare = CompareFunction::kLessEqual; desc.SetStencilAttachmentDescriptors(stencil0); - desc.SetStencilPixelFormat(PixelFormat::kS8UInt); + desc.SetStencilPixelFormat(PixelFormat::kDefaultStencil); } return true; diff --git a/impeller/renderer/render_target.cc b/impeller/renderer/render_target.cc index fb6d8ce58dc57..d7087f8cd0078 100644 --- a/impeller/renderer/render_target.cc +++ b/impeller/renderer/render_target.cc @@ -182,13 +182,13 @@ RenderTarget RenderTarget::CreateOffscreen(const Context& context, } TextureDescriptor color_tex0; - color_tex0.format = PixelFormat::kB8G8R8A8UNormInt; + color_tex0.format = PixelFormat::kDefaultColor; color_tex0.size = size; color_tex0.usage = static_cast(TextureUsage::kRenderTarget) | static_cast(TextureUsage::kShaderRead); TextureDescriptor stencil_tex0; - stencil_tex0.format = PixelFormat::kS8UInt; + stencil_tex0.format = PixelFormat::kDefaultStencil; stencil_tex0.size = size; stencil_tex0.usage = static_cast(TextureUsage::kRenderTarget); From 59ebc2567d8551299a62eea1c605822a21f8dea3 Mon Sep 17 00:00:00 2001 From: Chinmay Garde Date: Thu, 6 Jan 2022 12:17:59 -0800 Subject: [PATCH 267/433] Remove support for the weird 5 byte pixel format. --- impeller/renderer/backend/metal/formats_mtl.h | 4 ---- impeller/renderer/formats.h | 8 -------- 2 files changed, 12 deletions(-) diff --git a/impeller/renderer/backend/metal/formats_mtl.h b/impeller/renderer/backend/metal/formats_mtl.h index 22fdcedad3eb3..370284bb62f0c 100644 --- a/impeller/renderer/backend/metal/formats_mtl.h +++ b/impeller/renderer/backend/metal/formats_mtl.h @@ -25,8 +25,6 @@ constexpr PixelFormat FromMTLPixelFormat(MTLPixelFormat format) { return PixelFormat::kB8G8R8A8UNormInt; case MTLPixelFormatBGRA8Unorm_sRGB: return PixelFormat::kB8G8R8A8UNormIntSRGB; - case MTLPixelFormatDepth32Float_Stencil8: - return PixelFormat::kD32FloatS8UNormInt; case MTLPixelFormatRGBA8Unorm: return PixelFormat::kR8G8B8A8UNormInt; case MTLPixelFormatStencil8: @@ -47,8 +45,6 @@ constexpr MTLPixelFormat ToMTLPixelFormat(PixelFormat format) { return MTLPixelFormatBGRA8Unorm; case PixelFormat::kB8G8R8A8UNormIntSRGB: return MTLPixelFormatBGRA8Unorm_sRGB; - case PixelFormat::kD32FloatS8UNormInt: - return MTLPixelFormatDepth32Float_Stencil8; case PixelFormat::kR8G8B8A8UNormInt: return MTLPixelFormatRGBA8Unorm; case PixelFormat::kS8UInt: diff --git a/impeller/renderer/formats.h b/impeller/renderer/formats.h index c6b9440b22848..3b4b0c19688b9 100644 --- a/impeller/renderer/formats.h +++ b/impeller/renderer/formats.h @@ -52,9 +52,6 @@ enum class PixelFormat { kB8G8R8A8UNormIntSRGB, kS8UInt, - // Esoteric formats only used as render targets. - kD32FloatS8UNormInt, - // Defaults. If you don't know which ones to use, these are usually a safe // bet. // @@ -183,11 +180,6 @@ constexpr size_t BytesPerPixelForPixelFormat(PixelFormat format) { case PixelFormat::kB8G8R8A8UNormInt: case PixelFormat::kB8G8R8A8UNormIntSRGB: return 4u; - case PixelFormat::kD32FloatS8UNormInt: - // This is an esoteric format and implementations may use 64 bits. - // Impeller doesn't work with these natively and this return is only here - // for completeness. The 40 bits is as documented. - return 5u; } return 0u; } From 217b51beb3eca8280cc3682206a29c2437ee51ed Mon Sep 17 00:00:00 2001 From: Chinmay Garde Date: Mon, 31 Jan 2022 13:05:38 -0800 Subject: [PATCH 268/433] Minor: Account for macro namespacing. --- impeller/entity/BUILD.gn | 3 +-- impeller/renderer/allocator.cc | 2 +- impeller/renderer/backend/metal/allocator_mtl.mm | 8 ++++---- impeller/renderer/backend/metal/formats_mtl.h | 4 ++-- impeller/renderer/backend/metal/surface_mtl.mm | 4 ++-- impeller/renderer/command_buffer.h | 9 ++++++++- impeller/renderer/formats.h | 8 ++++---- impeller/renderer/platform.h | 4 ++-- impeller/renderer/texture_descriptor.h | 2 +- 9 files changed, 25 insertions(+), 19 deletions(-) diff --git a/impeller/entity/BUILD.gn b/impeller/entity/BUILD.gn index e6c109e06e99e..7badc02495d46 100644 --- a/impeller/entity/BUILD.gn +++ b/impeller/entity/BUILD.gn @@ -33,9 +33,8 @@ impeller_component("entity") { "entity_pass_delegate.h", ] - deps = [ ":entity_shaders" ] - public_deps = [ + ":entity_shaders", "../archivist", "../image", "../renderer", diff --git a/impeller/renderer/allocator.cc b/impeller/renderer/allocator.cc index 5969d220c21a7..fa4147141cf9b 100644 --- a/impeller/renderer/allocator.cc +++ b/impeller/renderer/allocator.cc @@ -15,7 +15,7 @@ bool Allocator::RequiresExplicitHostSynchronization(StorageMode mode) { return false; } -#if OS_IOS +#if FML_OS_IOS // StorageMode::kHostVisible is MTLStorageModeShared already. return false; #else // OS_IOS diff --git a/impeller/renderer/backend/metal/allocator_mtl.mm b/impeller/renderer/backend/metal/allocator_mtl.mm index 4cbe7ac143646..20ceb1e2c7910 100644 --- a/impeller/renderer/backend/metal/allocator_mtl.mm +++ b/impeller/renderer/backend/metal/allocator_mtl.mm @@ -32,7 +32,7 @@ static MTLResourceOptions ToMTLResourceOptions(StorageMode type) { switch (type) { case StorageMode::kHostVisible: -#if OS_IOS +#if FML_OS_IOS return MTLResourceStorageModeShared; #else return MTLResourceStorageModeManaged; @@ -40,7 +40,7 @@ static MTLResourceOptions ToMTLResourceOptions(StorageMode type) { case StorageMode::kDevicePrivate: return MTLResourceStorageModePrivate; case StorageMode::kDeviceTransient: -#if OS_IOS +#if FML_OS_IOS if (@available(iOS 10.0, *)) { return MTLResourceStorageModeMemoryless; } else { @@ -57,7 +57,7 @@ static MTLResourceOptions ToMTLResourceOptions(StorageMode type) { static MTLStorageMode ToMTLStorageMode(StorageMode mode) { switch (mode) { case StorageMode::kHostVisible: -#if OS_IOS +#if FML_OS_IOS return MTLStorageModeShared; #else return MTLStorageModeManaged; @@ -65,7 +65,7 @@ static MTLStorageMode ToMTLStorageMode(StorageMode mode) { case StorageMode::kDevicePrivate: return MTLStorageModePrivate; case StorageMode::kDeviceTransient: -#if OS_IOS +#if FML_OS_IOS if (@available(iOS 10.0, *)) { return MTLStorageModeMemoryless; } else { diff --git a/impeller/renderer/backend/metal/formats_mtl.h b/impeller/renderer/backend/metal/formats_mtl.h index 370284bb62f0c..47e17843132a4 100644 --- a/impeller/renderer/backend/metal/formats_mtl.h +++ b/impeller/renderer/backend/metal/formats_mtl.h @@ -275,9 +275,9 @@ constexpr MTLClearColor ToMTLClearColor(const Color& color) { constexpr MTLTextureType ToMTLTextureType(TextureType type) { switch (type) { - case TextureType::k2D: + case TextureType::kTexture2D: return MTLTextureType2D; - case TextureType::k2DMultisample: + case TextureType::kTexture2DMultisample: return MTLTextureType2DMultisample; } return MTLTextureType2D; diff --git a/impeller/renderer/backend/metal/surface_mtl.mm b/impeller/renderer/backend/metal/surface_mtl.mm index 9a8ed8185ccf7..4c57ee21f6579 100644 --- a/impeller/renderer/backend/metal/surface_mtl.mm +++ b/impeller/renderer/backend/metal/surface_mtl.mm @@ -37,7 +37,7 @@ } TextureDescriptor color0_tex_desc; - color0_tex_desc.type = TextureType::k2DMultisample; + color0_tex_desc.type = TextureType::kTexture2DMultisample; color0_tex_desc.sample_count = SampleCount::kCount4; color0_tex_desc.format = color_format; color0_tex_desc.size = { @@ -69,7 +69,7 @@ color0_resolve_tex_desc, current_drawable.texture); TextureDescriptor stencil0_tex; - stencil0_tex.type = TextureType::k2DMultisample; + stencil0_tex.type = TextureType::kTexture2DMultisample; stencil0_tex.sample_count = SampleCount::kCount4; stencil0_tex.format = PixelFormat::kDefaultStencil; stencil0_tex.size = color0_tex_desc.size; diff --git a/impeller/renderer/command_buffer.h b/impeller/renderer/command_buffer.h index 10ac6a65a4247..2056b048cb891 100644 --- a/impeller/renderer/command_buffer.h +++ b/impeller/renderer/command_buffer.h @@ -25,7 +25,14 @@ class RenderTarget; /// `RenderPass` describes the configuration of the various /// attachments when the command is submitted. /// -/// A command buffer is only meant to be used on a single thread. +/// A command buffer is only meant to be used on a single thread. If +/// a frame workload needs to be encoded from multiple threads, +/// setup and record into multiple command buffers. The order of +/// submission of commands encoded in multiple command buffers can +/// be controlled via either the order in which the command buffers +/// were created, or, using the `ReserveSpotInQueue` command which +/// allows for encoding commands for submission in an order that is +/// different from the encoding order. /// class CommandBuffer { public: diff --git a/impeller/renderer/formats.h b/impeller/renderer/formats.h index 3b4b0c19688b9..796c6258387c7 100644 --- a/impeller/renderer/formats.h +++ b/impeller/renderer/formats.h @@ -100,15 +100,15 @@ enum class StoreAction { }; enum class TextureType { - k2D, - k2DMultisample, + kTexture2D, + kTexture2DMultisample, }; constexpr bool IsMultisampleCapable(TextureType type) { switch (type) { - case TextureType::k2D: + case TextureType::kTexture2D: return false; - case TextureType::k2DMultisample: + case TextureType::kTexture2DMultisample: return true; } return false; diff --git a/impeller/renderer/platform.h b/impeller/renderer/platform.h index 9044c8b78bc8b..1ca8133c328c5 100644 --- a/impeller/renderer/platform.h +++ b/impeller/renderer/platform.h @@ -12,9 +12,9 @@ namespace impeller { constexpr size_t DefaultUniformAlignment() { -#if OS_IOS +#if FML_OS_IOS return 16u; -#elif OS_MACOSX +#elif FML_OS_MACOSX return 256u; #else #error "Unsupported platform". diff --git a/impeller/renderer/texture_descriptor.h b/impeller/renderer/texture_descriptor.h index d453ae28508bd..da51479b0e4a5 100644 --- a/impeller/renderer/texture_descriptor.h +++ b/impeller/renderer/texture_descriptor.h @@ -13,7 +13,7 @@ namespace impeller { struct TextureDescriptor { - TextureType type = TextureType::k2D; + TextureType type = TextureType::kTexture2D; PixelFormat format = PixelFormat::kUnknown; ISize size; size_t mip_count = 1u; // Size::MipCount is usually appropriate. From 546fa19e71dec6d932dfa22c416529fa30693c3d Mon Sep 17 00:00:00 2001 From: Chinmay Garde Date: Mon, 31 Jan 2022 13:13:22 -0800 Subject: [PATCH 269/433] IOS macros have not been namespaced yet. --- impeller/renderer/backend/metal/allocator_mtl.mm | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/impeller/renderer/backend/metal/allocator_mtl.mm b/impeller/renderer/backend/metal/allocator_mtl.mm index 20ceb1e2c7910..4cbe7ac143646 100644 --- a/impeller/renderer/backend/metal/allocator_mtl.mm +++ b/impeller/renderer/backend/metal/allocator_mtl.mm @@ -32,7 +32,7 @@ static MTLResourceOptions ToMTLResourceOptions(StorageMode type) { switch (type) { case StorageMode::kHostVisible: -#if FML_OS_IOS +#if OS_IOS return MTLResourceStorageModeShared; #else return MTLResourceStorageModeManaged; @@ -40,7 +40,7 @@ static MTLResourceOptions ToMTLResourceOptions(StorageMode type) { case StorageMode::kDevicePrivate: return MTLResourceStorageModePrivate; case StorageMode::kDeviceTransient: -#if FML_OS_IOS +#if OS_IOS if (@available(iOS 10.0, *)) { return MTLResourceStorageModeMemoryless; } else { @@ -57,7 +57,7 @@ static MTLResourceOptions ToMTLResourceOptions(StorageMode type) { static MTLStorageMode ToMTLStorageMode(StorageMode mode) { switch (mode) { case StorageMode::kHostVisible: -#if FML_OS_IOS +#if OS_IOS return MTLStorageModeShared; #else return MTLStorageModeManaged; @@ -65,7 +65,7 @@ static MTLStorageMode ToMTLStorageMode(StorageMode mode) { case StorageMode::kDevicePrivate: return MTLStorageModePrivate; case StorageMode::kDeviceTransient: -#if FML_OS_IOS +#if OS_IOS if (@available(iOS 10.0, *)) { return MTLStorageModeMemoryless; } else { From d9f3c7041c0d09b00da138b09d00704b87bfb2f2 Mon Sep 17 00:00:00 2001 From: Chinmay Garde Date: Mon, 31 Jan 2022 14:57:21 -0800 Subject: [PATCH 270/433] Gate the minimum iOS deployment versions in the generated shaders. --- impeller/renderer/allocator.cc | 2 +- impeller/renderer/platform.h | 2 +- impeller/tools/build_metal_library.py | 1 + 3 files changed, 3 insertions(+), 2 deletions(-) diff --git a/impeller/renderer/allocator.cc b/impeller/renderer/allocator.cc index fa4147141cf9b..5969d220c21a7 100644 --- a/impeller/renderer/allocator.cc +++ b/impeller/renderer/allocator.cc @@ -15,7 +15,7 @@ bool Allocator::RequiresExplicitHostSynchronization(StorageMode mode) { return false; } -#if FML_OS_IOS +#if OS_IOS // StorageMode::kHostVisible is MTLStorageModeShared already. return false; #else // OS_IOS diff --git a/impeller/renderer/platform.h b/impeller/renderer/platform.h index 1ca8133c328c5..464d47451ac33 100644 --- a/impeller/renderer/platform.h +++ b/impeller/renderer/platform.h @@ -12,7 +12,7 @@ namespace impeller { constexpr size_t DefaultUniformAlignment() { -#if FML_OS_IOS +#if OS_IOS return 16u; #elif FML_OS_MACOSX return 256u; diff --git a/impeller/tools/build_metal_library.py b/impeller/tools/build_metal_library.py index ef4913f72a2f5..4e18d09e33125 100644 --- a/impeller/tools/build_metal_library.py +++ b/impeller/tools/build_metal_library.py @@ -74,6 +74,7 @@ def Main(): elif args.platform == "ios": command += [ "--std=ios-metal1.2", + "-mios-version-min=10.0", ] if args.optimize: From 8ac69960675c539eaddef819062716b95cc9b6d4 Mon Sep 17 00:00:00 2001 From: Chinmay Garde Date: Mon, 31 Jan 2022 19:28:11 -0800 Subject: [PATCH 271/433] Cross reference issues and add additional tracing. --- .../display_list/display_list_dispatcher.cc | 26 ++++++++++++++++++- impeller/entity/entity_pass.cc | 3 +++ .../renderer/backend/metal/render_pass_mtl.mm | 2 ++ .../renderer/backend/metal/surface_mtl.mm | 2 +- impeller/renderer/renderer.cc | 2 ++ impeller/renderer/tessellator.cc | 2 ++ impeller/renderer/texture_descriptor.h | 4 +++ 7 files changed, 39 insertions(+), 2 deletions(-) diff --git a/impeller/display_list/display_list_dispatcher.cc b/impeller/display_list/display_list_dispatcher.cc index c349a55e5a5d6..dccbd1f86a4bf 100644 --- a/impeller/display_list/display_list_dispatcher.cc +++ b/impeller/display_list/display_list_dispatcher.cc @@ -4,6 +4,7 @@ #include "impeller/display_list/display_list_dispatcher.h" +#include "flutter/fml/trace_event.h" #include "impeller/geometry/path_builder.h" namespace impeller { @@ -73,11 +74,13 @@ void DisplayListDispatcher::setStrokeJoin(SkPaint::Join join) { // |flutter::Dispatcher| void DisplayListDispatcher::setShader(sk_sp shader) { + // Needs https://github.com/flutter/flutter/issues/95434 UNIMPLEMENTED; } // |flutter::Dispatcher| void DisplayListDispatcher::setColorFilter(sk_sp filter) { + // Needs https://github.com/flutter/flutter/issues/95434 UNIMPLEMENTED; } @@ -93,16 +96,19 @@ void DisplayListDispatcher::setBlendMode(SkBlendMode mode) { // |flutter::Dispatcher| void DisplayListDispatcher::setBlender(sk_sp blender) { + // Needs https://github.com/flutter/flutter/issues/95434 UNIMPLEMENTED; } // |flutter::Dispatcher| void DisplayListDispatcher::setPathEffect(sk_sp effect) { + // Needs https://github.com/flutter/flutter/issues/95434 UNIMPLEMENTED; } // |flutter::Dispatcher| void DisplayListDispatcher::setMaskFilter(sk_sp filter) { + // Needs https://github.com/flutter/flutter/issues/95434 UNIMPLEMENTED; } @@ -396,6 +402,7 @@ void DisplayListDispatcher::drawPoints(SkCanvas::PointMode mode, // |flutter::Dispatcher| void DisplayListDispatcher::drawVertices(const sk_sp vertices, SkBlendMode mode) { + // Needs https://github.com/flutter/flutter/issues/95434 UNIMPLEMENTED; } @@ -404,6 +411,7 @@ void DisplayListDispatcher::drawImage(const sk_sp image, const SkPoint point, const SkSamplingOptions& sampling, bool render_with_attributes) { + // Needs https://github.com/flutter/flutter/issues/95434 UNIMPLEMENTED; } @@ -415,6 +423,7 @@ void DisplayListDispatcher::drawImageRect( const SkSamplingOptions& sampling, bool render_with_attributes, SkCanvas::SrcRectConstraint constraint) { + // Needs https://github.com/flutter/flutter/issues/95434 UNIMPLEMENTED; } @@ -424,6 +433,7 @@ void DisplayListDispatcher::drawImageNine(const sk_sp image, const SkRect& dst, SkFilterMode filter, bool render_with_attributes) { + // Needs https://github.com/flutter/flutter/issues/95434 UNIMPLEMENTED; } @@ -433,6 +443,7 @@ void DisplayListDispatcher::drawImageLattice(const sk_sp image, const SkRect& dst, SkFilterMode filter, bool render_with_attributes) { + // Needs https://github.com/flutter/flutter/issues/95434 UNIMPLEMENTED; } @@ -446,6 +457,7 @@ void DisplayListDispatcher::drawAtlas(const sk_sp atlas, const SkSamplingOptions& sampling, const SkRect* cull_rect, bool render_with_attributes) { + // Needs https://github.com/flutter/flutter/issues/95434 UNIMPLEMENTED; } @@ -453,6 +465,7 @@ void DisplayListDispatcher::drawAtlas(const sk_sp atlas, void DisplayListDispatcher::drawPicture(const sk_sp picture, const SkMatrix* matrix, bool render_with_attributes) { + // Needs https://github.com/flutter/flutter/issues/95434 UNIMPLEMENTED; } @@ -466,7 +479,17 @@ void DisplayListDispatcher::drawDisplayList( void DisplayListDispatcher::drawTextBlob(const sk_sp blob, SkScalar x, SkScalar y) { - UNIMPLEMENTED; + if (!blob) { + return; + } + + auto bounds = blob->bounds(); + bounds.fLeft += x; + bounds.fTop += y; + + impeller::Paint paint; + paint.color = impeller::Color::Random().WithAlpha(0.2); + canvas_.DrawRect(ToRect(bounds), paint); } // |flutter::Dispatcher| @@ -479,6 +502,7 @@ void DisplayListDispatcher::drawShadow(const SkPath& path, } Picture DisplayListDispatcher::EndRecordingAsPicture() { + TRACE_EVENT0("impeller", "DisplayListDispatcher::EndRecordingAsPicture"); return canvas_.EndRecordingAsPicture(); } diff --git a/impeller/entity/entity_pass.cc b/impeller/entity/entity_pass.cc index eec7f0cbfd412..69c14c888a17b 100644 --- a/impeller/entity/entity_pass.cc +++ b/impeller/entity/entity_pass.cc @@ -4,6 +4,7 @@ #include "impeller/entity/entity_pass.h" +#include "flutter/fml/trace_event.h" #include "impeller/entity/content_context.h" #include "impeller/geometry/path_builder.h" #include "impeller/renderer/command_buffer.h" @@ -99,6 +100,8 @@ EntityPass* EntityPass::AddSubpass(std::unique_ptr pass) { bool EntityPass::Render(ContentContext& renderer, RenderPass& parent_pass) const { + TRACE_EVENT0("impeller", "EntityPass::Render"); + for (const auto& entity : entities_) { if (!entity.Render(renderer, parent_pass)) { return false; diff --git a/impeller/renderer/backend/metal/render_pass_mtl.mm b/impeller/renderer/backend/metal/render_pass_mtl.mm index cba003d628776..aa9d5737e93ab 100644 --- a/impeller/renderer/backend/metal/render_pass_mtl.mm +++ b/impeller/renderer/backend/metal/render_pass_mtl.mm @@ -6,6 +6,7 @@ #include "flutter/fml/closure.h" #include "flutter/fml/logging.h" +#include "flutter/fml/trace_event.h" #include "impeller/base/base.h" #include "impeller/renderer/backend/metal/device_buffer_mtl.h" #include "impeller/renderer/backend/metal/formats_mtl.h" @@ -156,6 +157,7 @@ static bool ConfigureStencilAttachment( } bool RenderPassMTL::EncodeCommands(Allocator& transients_allocator) const { + TRACE_EVENT0("impeller", "RenderPassMTL::EncodeCommands"); if (!IsValid()) { return false; } diff --git a/impeller/renderer/backend/metal/surface_mtl.mm b/impeller/renderer/backend/metal/surface_mtl.mm index 4c57ee21f6579..8b0c6e26ca4cf 100644 --- a/impeller/renderer/backend/metal/surface_mtl.mm +++ b/impeller/renderer/backend/metal/surface_mtl.mm @@ -15,7 +15,7 @@ std::unique_ptr SurfaceMTL::WrapCurrentMetalLayerDrawable( std::shared_ptr context, CAMetalLayer* layer) { - TRACE_EVENT0("flutter", __FUNCTION__); + TRACE_EVENT0("impeller", "SurfaceMTL::WrapCurrentMetalLayerDrawable"); if (context == nullptr || !context->IsValid() || layer == nil) { return nullptr; diff --git a/impeller/renderer/renderer.cc b/impeller/renderer/renderer.cc index ff055ee690da8..5507314fe87ef 100644 --- a/impeller/renderer/renderer.cc +++ b/impeller/renderer/renderer.cc @@ -7,6 +7,7 @@ #include #include "flutter/fml/logging.h" +#include "flutter/fml/trace_event.h" #include "impeller/base/validation.h" #include "impeller/renderer/command_buffer.h" #include "impeller/renderer/surface.h" @@ -33,6 +34,7 @@ bool Renderer::IsValid() const { bool Renderer::Render(std::unique_ptr surface, RenderCallback render_callback) const { + TRACE_EVENT0("impeller", "Renderer::Render"); if (!IsValid()) { return false; } diff --git a/impeller/renderer/tessellator.cc b/impeller/renderer/tessellator.cc index 9cc5d4ebba0cd..38d724b263c51 100644 --- a/impeller/renderer/tessellator.cc +++ b/impeller/renderer/tessellator.cc @@ -4,6 +4,7 @@ #include "impeller/renderer/tessellator.h" +#include "flutter/fml/trace_event.h" #include "third_party/libtess2/Include/tesselator.h" namespace impeller { @@ -36,6 +37,7 @@ static void DestroyTessellator(TESStesselator* tessellator) { bool Tessellator::Tessellate(const std::vector& contours, VertexCallback callback) const { + TRACE_EVENT0("impeller", "Tessellator::Tessellate"); if (!callback) { return false; } diff --git a/impeller/renderer/texture_descriptor.h b/impeller/renderer/texture_descriptor.h index da51479b0e4a5..20b9a367dddc3 100644 --- a/impeller/renderer/texture_descriptor.h +++ b/impeller/renderer/texture_descriptor.h @@ -12,6 +12,10 @@ namespace impeller { +//------------------------------------------------------------------------------ +/// @brief A lightweight object that describes the attributes of a texture +/// that can then used used an allocator to create that texture. +/// struct TextureDescriptor { TextureType type = TextureType::kTexture2D; PixelFormat format = PixelFormat::kUnknown; From bbb71a773efc76253a2c7d4d2cd361c33fbf6509 Mon Sep 17 00:00:00 2001 From: Chinmay Garde Date: Wed, 2 Feb 2022 15:51:17 -0800 Subject: [PATCH 272/433] Add a README. --- impeller/README.md | 50 ++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 50 insertions(+) diff --git a/impeller/README.md b/impeller/README.md index a0a0d521e3e98..d15979ae5f3ba 100644 --- a/impeller/README.md +++ b/impeller/README.md @@ -10,3 +10,53 @@ -------------------------------------------------------------------------------- -------------------------------------------------------------------------------- ``` + +⚠️ Impeller is a Prototype and Work-In-Progress. Proceed with caution. ⚠️ + +Impeller is a rendering runtime for Flutter with the following objectives: + + + +* **Predictable Performance**: All shader compilation and reflection is performed offline at build time. All pipeline state objects are built upfront. Caching is explicit and under the control of the engine. +* **Instrumentable**: All graphics resources (textures, buffers, pipeline state objects, etc..) are tagged and labeled. Animations can be captured and persisted to disk without affecting per-frame rendering performance. +* **Portable**: Not tied to a specific client rendering API. Shaders are authored once and converted as necessary. +* **Uses Modern Graphics APIs Effectively**: Makes heavy use of (but doesn’t depend on) features available in Modern APIs like Metal and Vulkan. +* **Makes Effective Use of Concurrency**: Can distribute single-frame workloads across multiple threads if necessary. + + +## Project Organization + +Impeller is a meta-framework. While a user of Impeller may choose to include the whole enchilada (in `//impeller/:impeller`), the various sub-frameworks have clearly defined responsibilities and adhere to a strict hierarchy. + +Impeller itself may not depend on anything in `//flutter` except `//flutter/fml` and `flutter/display_list`. FML is a base library for C++ projects and Impeller implements the display list dispatcher interface to make it easy for Flutter to swap the renderer with Impeller. Impeller is meant to be used by the Flow (`//flutter/flow`) subsystem. Hence the name. + +An overview of the major sub-frameworks, their responsibilities, and, relative states of completion: + + + +* **`//impeller/compiler`**: The offline shader compiler. Takes GLSL 4.60 shader source code and converts it into a backend specific shader representation (like Metal Shading Language). It also generates C++ bindings that callers can include as a GN `source_set`s so there is no runtime shader reflection either. The target is an executable called `impellerc` which is never shipped into the binary or as an artifact. +* **`//impeller/renderer`**: The very lowest level of the renderer that is still backend agnostic. Allows users to build a renderer from scratch with few restrictions. Has utilities for creating allocators, generating pipeline state objects from bindings generated by `//impeller/compiler`, setting up render passes, managing jumbo uniform-buffers, tessellators, etc.. + * **`//impeller/renderer/backend`**: Contains all the implementation details for a specific client rendering API. The interfaces in these targets are meant to be private for non-WSI user targets. No Impeller sub-frameworks may depend on these targets. +* **`//impeller/archivist**`: Allows persisting objects to disk as performantly as possible (usually on a background thread). The framework is meant to be used for storing frame meta-data and related profiling/instrumentation information. Collection of information should succeed despite process crashes and retrieval of traces must not use inordinate amounts of time or memory (which usually leads to crashes). +* **`//impeller/geometry`**: All (or, most of) the math! This C++ mathematics library is used extensively by Impeller and its clients. The reasonably interesting bit about this library is that all types can be used interchangeably in device and host memory. Various Impeller subsystems understand these types and can take care of packing and alignment concerns w.r.t these types. +* **`//impeller/playground`**: When working with graphics APIs, it is often necessary to visually verify rendering results as a specific feature is being worked upon. Moreover, it is useful to attach frame debuggers or profilers to specific test cases. The playground framework provides Google Test fixtures that open the current state of various rendering related objects in a window in which rendering results can be visualized, or, to which frame debuggers can be attached. Most Impeller sub-frameworks that have a test harness also have a custom playground subclass. This sub-framework is only meant to provide utilities for tests and will not be compiled into any shipping binary. +* **`//impeller/entity`:** Sits one level above `//impeller/renderer` and provides a framework for building 2D renderers. Most of the pipeline state objects generated from shaders authored at build time reside in this framework. The render-pass optimization and pass-rewriting framework also resides there. This allows authoring composable 2D rendering optimizations (like collapsing passes, or, eliding them completely). +* **`//impeller/aiks`**: Aiks wraps `//impeller/entity` into an API that resembles Skia. This makes it easy to mechanically replace Skia calls with their Impeller counterparts even though the `//impeller/entity` framework API is different from Skia. This presence of this sub-framework is probably short-lived as integration of Impeller into Flutter should likely happen via a custom Display List implementation in `//impeller/display_list`. The roadblocks to this today are graphics package agnosticism in the Display List interface. +* **`//impeller/display_list`**: The replacement for `//impeller/aiks` to serve in the integration of Impeller in `//flutter/flow`. This is pending graphics package agnosticism in the Impeller interface. This sub-framework primarily provides a custom implementation of the `flutter::DisplayListDispatcher` that forwards Flutter rendering intent to Impeller. +* **`//impeller/base`**: Contains C++ utilities that are used throughout the Impeller family of frameworks. Ideally, these should go in `//flutter/fml` but their use is probably not widespread enough to at this time. +* **`//impeller/image`**: The Impeller renderer works with textures whose memory is resident in device memory. However, pending the migration of `//flutter/display_list` to graphics package agnosticism and the subsequent migration of the image decoders to work with the package agnostic types, there needs to be a way for tests and such to decode compressed image data. This sub-framework provides that functionality. This sub-framework is slated for removal and must not be used outside of tests. +* **`//fixtures`**: Contains test fixtures used by the various test harnesses. This depends on `//flutter/testing`. +* **`//tools`**: Contains all GN rules and python scripts for working with Impeller. These include GN rules processing GLSL shaders, including reflected shader information as source set targets, and, including compiled shader intermediate representations into the final executable as binary blobs for easier packaging. + + +## The Offline Shader Compilation Pipeline + + + +* Shaders are authored once in GLSL 4.60. This choice of shading language is consistent across all backends. Shader code resides in the Impeller source tree like any other source file. +* At build time, the Impeller Shader Compiler (`impellerc`) converts the GLSL into SPIRV. No optimizations are performed on the generated SPIRV at this stage. This is to preserve all debugging and instrumentation information. +* Using the SPIRV, a backend specific transpiler converts the SPIRV to the appropriate high-level shading language. This is controlled using flags to the `impellerc`. +* All the files generated in the high-level shading language are compiled, optimized, and linked into a single binary blob. +* The binary blob containing the compiled and optimized high-level shading language is included as a hex dump (see `xxd.py`) into a C source file with a generated GN target. Executable targets that want to include the compiled code in their binaries just need to depend on the generated GN target. This eases any shader packaging concerns. +* In parallel, the SPIRV is processed by a reflector. This produces C++ translation units that allow for the easy creation of pipeline state objects at runtime. The headers for these translation units include any structs (with appropriate padding and alignment) such that uniform data as well as vertex information can be specified to the shader without having to deal with bindings, vertex descriptors, etc.. This also makes iterating on shaders easier as changes to the shader interface lead to compile time errors. +* The C++ translation units generated from reflected shader information are made available to callers as a generated GN target that callers may use if necessary. It is possible for callers to perform reflection at runtime but there are no Impeller components that do this currently. From 8bc0573857e6a642ea1ac0527593436855d7de6c Mon Sep 17 00:00:00 2001 From: Chinmay Garde Date: Wed, 2 Feb 2022 15:53:11 -0800 Subject: [PATCH 273/433] Fixup minor Markdown formatting issues in the README. --- impeller/README.md | 8 +------- 1 file changed, 1 insertion(+), 7 deletions(-) diff --git a/impeller/README.md b/impeller/README.md index d15979ae5f3ba..5c7f83671ffd7 100644 --- a/impeller/README.md +++ b/impeller/README.md @@ -23,7 +23,6 @@ Impeller is a rendering runtime for Flutter with the following objectives: * **Uses Modern Graphics APIs Effectively**: Makes heavy use of (but doesn’t depend on) features available in Modern APIs like Metal and Vulkan. * **Makes Effective Use of Concurrency**: Can distribute single-frame workloads across multiple threads if necessary. - ## Project Organization Impeller is a meta-framework. While a user of Impeller may choose to include the whole enchilada (in `//impeller/:impeller`), the various sub-frameworks have clearly defined responsibilities and adhere to a strict hierarchy. @@ -32,12 +31,10 @@ Impeller itself may not depend on anything in `//flutter` except `//flutter/fml` An overview of the major sub-frameworks, their responsibilities, and, relative states of completion: - - * **`//impeller/compiler`**: The offline shader compiler. Takes GLSL 4.60 shader source code and converts it into a backend specific shader representation (like Metal Shading Language). It also generates C++ bindings that callers can include as a GN `source_set`s so there is no runtime shader reflection either. The target is an executable called `impellerc` which is never shipped into the binary or as an artifact. * **`//impeller/renderer`**: The very lowest level of the renderer that is still backend agnostic. Allows users to build a renderer from scratch with few restrictions. Has utilities for creating allocators, generating pipeline state objects from bindings generated by `//impeller/compiler`, setting up render passes, managing jumbo uniform-buffers, tessellators, etc.. * **`//impeller/renderer/backend`**: Contains all the implementation details for a specific client rendering API. The interfaces in these targets are meant to be private for non-WSI user targets. No Impeller sub-frameworks may depend on these targets. -* **`//impeller/archivist**`: Allows persisting objects to disk as performantly as possible (usually on a background thread). The framework is meant to be used for storing frame meta-data and related profiling/instrumentation information. Collection of information should succeed despite process crashes and retrieval of traces must not use inordinate amounts of time or memory (which usually leads to crashes). +* **`//impeller/archivist`**: Allows persisting objects to disk as performantly as possible (usually on a background thread). The framework is meant to be used for storing frame meta-data and related profiling/instrumentation information. Collection of information should succeed despite process crashes and retrieval of traces must not use inordinate amounts of time or memory (which usually leads to crashes). * **`//impeller/geometry`**: All (or, most of) the math! This C++ mathematics library is used extensively by Impeller and its clients. The reasonably interesting bit about this library is that all types can be used interchangeably in device and host memory. Various Impeller subsystems understand these types and can take care of packing and alignment concerns w.r.t these types. * **`//impeller/playground`**: When working with graphics APIs, it is often necessary to visually verify rendering results as a specific feature is being worked upon. Moreover, it is useful to attach frame debuggers or profilers to specific test cases. The playground framework provides Google Test fixtures that open the current state of various rendering related objects in a window in which rendering results can be visualized, or, to which frame debuggers can be attached. Most Impeller sub-frameworks that have a test harness also have a custom playground subclass. This sub-framework is only meant to provide utilities for tests and will not be compiled into any shipping binary. * **`//impeller/entity`:** Sits one level above `//impeller/renderer` and provides a framework for building 2D renderers. Most of the pipeline state objects generated from shaders authored at build time reside in this framework. The render-pass optimization and pass-rewriting framework also resides there. This allows authoring composable 2D rendering optimizations (like collapsing passes, or, eliding them completely). @@ -48,11 +45,8 @@ An overview of the major sub-frameworks, their responsibilities, and, relative s * **`//fixtures`**: Contains test fixtures used by the various test harnesses. This depends on `//flutter/testing`. * **`//tools`**: Contains all GN rules and python scripts for working with Impeller. These include GN rules processing GLSL shaders, including reflected shader information as source set targets, and, including compiled shader intermediate representations into the final executable as binary blobs for easier packaging. - ## The Offline Shader Compilation Pipeline - - * Shaders are authored once in GLSL 4.60. This choice of shading language is consistent across all backends. Shader code resides in the Impeller source tree like any other source file. * At build time, the Impeller Shader Compiler (`impellerc`) converts the GLSL into SPIRV. No optimizations are performed on the generated SPIRV at this stage. This is to preserve all debugging and instrumentation information. * Using the SPIRV, a backend specific transpiler converts the SPIRV to the appropriate high-level shading language. This is controlled using flags to the `impellerc`. From 8b980ed2337718c36b916defa1ac4d668461eca8 Mon Sep 17 00:00:00 2001 From: Chinmay Garde Date: Wed, 2 Feb 2022 15:56:30 -0800 Subject: [PATCH 274/433] Add an image overview of the shader compilation pipeline. --- impeller/docs/shader_pipeline.png | Bin 0 -> 21705 bytes 1 file changed, 0 insertions(+), 0 deletions(-) create mode 100644 impeller/docs/shader_pipeline.png diff --git a/impeller/docs/shader_pipeline.png b/impeller/docs/shader_pipeline.png new file mode 100644 index 0000000000000000000000000000000000000000..dc7320b50d7406f827f3a4f22a2d79a41ba8aa64 GIT binary patch literal 21705 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 LXtN&CNX<_u@lZ=A@3w?P9 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 d|35+-!9ck}Ga_53i)g1r; literal 0 HcmV?d00001 From e5220510bef958804fe385e30b7c4241f8c41ef0 Mon Sep 17 00:00:00 2001 From: Chinmay Garde Date: Wed, 2 Feb 2022 15:59:39 -0800 Subject: [PATCH 275/433] Link to the shader pipeline from the README. --- impeller/README.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/impeller/README.md b/impeller/README.md index 5c7f83671ffd7..a1fb2d48b1f63 100644 --- a/impeller/README.md +++ b/impeller/README.md @@ -54,3 +54,5 @@ An overview of the major sub-frameworks, their responsibilities, and, relative s * The binary blob containing the compiled and optimized high-level shading language is included as a hex dump (see `xxd.py`) into a C source file with a generated GN target. Executable targets that want to include the compiled code in their binaries just need to depend on the generated GN target. This eases any shader packaging concerns. * In parallel, the SPIRV is processed by a reflector. This produces C++ translation units that allow for the easy creation of pipeline state objects at runtime. The headers for these translation units include any structs (with appropriate padding and alignment) such that uniform data as well as vertex information can be specified to the shader without having to deal with bindings, vertex descriptors, etc.. This also makes iterating on shaders easier as changes to the shader interface lead to compile time errors. * The C++ translation units generated from reflected shader information are made available to callers as a generated GN target that callers may use if necessary. It is possible for callers to perform reflection at runtime but there are no Impeller components that do this currently. + +![Shader Compilation Pipeline](docs/shader_pipeline.png) From 7b752f5b9c587b3da4a3732f9479bd3a99b0dbef Mon Sep 17 00:00:00 2001 From: Chinmay Garde Date: Wed, 2 Feb 2022 16:13:12 -0800 Subject: [PATCH 276/433] Enable playgrounds in all tests and fix default sample counts. The sample counts were invalid after the default assumed rendering to an MSAA target. --- impeller/aiks/aiks_unittests.cc | 4 ++-- impeller/entity/entity_unittests.cc | 2 +- impeller/renderer/backend/metal/render_pass_mtl.mm | 7 ++++++- impeller/renderer/renderer_unittests.cc | 6 ++++-- 4 files changed, 13 insertions(+), 6 deletions(-) diff --git a/impeller/aiks/aiks_unittests.cc b/impeller/aiks/aiks_unittests.cc index 62782b05f526c..a756bbf0185ad 100644 --- a/impeller/aiks/aiks_unittests.cc +++ b/impeller/aiks/aiks_unittests.cc @@ -47,7 +47,7 @@ TEST_F(AiksTest, CanRenderColoredRect) { .AddRect(Rect::MakeXYWH(100.0, 100.0, 100.0, 100.0)) .TakePath(), paint); - // ASSERT_TRUE(OpenPlaygroundHere(canvas.EndRecordingAsPicture())); + ASSERT_TRUE(OpenPlaygroundHere(canvas.EndRecordingAsPicture())); } TEST_F(AiksTest, CanRenderImage) { @@ -83,7 +83,7 @@ TEST_F(AiksTest, CanRenderStrokes) { paint.style = Paint::Style::kStroke; canvas.DrawPath(PathBuilder{}.AddLine({200, 100}, {800, 100}).TakePath(), paint); - // ASSERT_TRUE(OpenPlaygroundHere(canvas.EndRecordingAsPicture())); + ASSERT_TRUE(OpenPlaygroundHere(canvas.EndRecordingAsPicture())); } TEST_F(AiksTest, CanRenderCurvedStrokes) { diff --git a/impeller/entity/entity_unittests.cc b/impeller/entity/entity_unittests.cc index cf3cafb47cb70..ed83995dc7031 100644 --- a/impeller/entity/entity_unittests.cc +++ b/impeller/entity/entity_unittests.cc @@ -22,7 +22,7 @@ TEST_F(EntityTest, CanDrawRect) { Entity entity; entity.SetPath(PathBuilder{}.AddRect({100, 100, 100, 100}).TakePath()); entity.SetContents(SolidColorContents::Make(Color::Red())); - // ASSERT_TRUE(OpenPlaygroundHere(entity)); + ASSERT_TRUE(OpenPlaygroundHere(entity)); } } // namespace testing diff --git a/impeller/renderer/backend/metal/render_pass_mtl.mm b/impeller/renderer/backend/metal/render_pass_mtl.mm index aa9d5737e93ab..6a685d975c271 100644 --- a/impeller/renderer/backend/metal/render_pass_mtl.mm +++ b/impeller/renderer/backend/metal/render_pass_mtl.mm @@ -409,7 +409,12 @@ static bool Bind(PassBindingsCache& pass, if (target_sample_count != command.pipeline->GetDescriptor().GetSampleCount()) { VALIDATION_LOG << "Pipeline for command and the render target disagree " - "on sample counts."; + "on sample counts (target was " + << static_cast(target_sample_count) + << " but pipeline wanted " + << static_cast( + command.pipeline->GetDescriptor().GetSampleCount()) + << ")."; return false; } diff --git a/impeller/renderer/renderer_unittests.cc b/impeller/renderer/renderer_unittests.cc index 097cba63cd060..3cabc03a43c06 100644 --- a/impeller/renderer/renderer_unittests.cc +++ b/impeller/renderer/renderer_unittests.cc @@ -36,6 +36,8 @@ TEST_F(RendererTest, CanCreateBoxPrimitive) { ASSERT_TRUE(context); using BoxPipelineBuilder = PipelineBuilder; auto desc = BoxPipelineBuilder::MakeDefaultPipelineDescriptor(*context); + ASSERT_TRUE(desc.has_value()); + desc->SetSampleCount(SampleCount::kCount4); auto box_pipeline = context->GetPipelineLibrary()->GetRenderPipeline(std::move(desc)).get(); ASSERT_TRUE(box_pipeline); @@ -89,7 +91,7 @@ TEST_F(RendererTest, CanCreateBoxPrimitive) { } return true; }; - // OpenPlaygroundHere(callback); + OpenPlaygroundHere(callback); } TEST_F(RendererTest, CanRenderMultiplePrimitives) { @@ -159,7 +161,7 @@ TEST_F(RendererTest, CanRenderMultiplePrimitives) { return true; }; - // OpenPlaygroundHere(callback); + OpenPlaygroundHere(callback); } TEST_F(RendererTest, CanRenderToTexture) { From 7a920fd74f970f361b61ae4845ab5abdd72c323c Mon Sep 17 00:00:00 2001 From: Chinmay Garde Date: Wed, 2 Feb 2022 16:18:16 -0800 Subject: [PATCH 277/433] Update tests to account for sample count defaults. --- impeller/renderer/renderer_unittests.cc | 2 ++ 1 file changed, 2 insertions(+) diff --git a/impeller/renderer/renderer_unittests.cc b/impeller/renderer/renderer_unittests.cc index 3cabc03a43c06..ca08dab43b8cd 100644 --- a/impeller/renderer/renderer_unittests.cc +++ b/impeller/renderer/renderer_unittests.cc @@ -101,6 +101,8 @@ TEST_F(RendererTest, CanRenderMultiplePrimitives) { ASSERT_TRUE(context); using BoxPipelineBuilder = PipelineBuilder; auto desc = BoxPipelineBuilder::MakeDefaultPipelineDescriptor(*context); + ASSERT_TRUE(desc.has_value()); + desc->SetSampleCount(SampleCount::kCount4); auto box_pipeline = context->GetPipelineLibrary()->GetRenderPipeline(std::move(desc)).get(); ASSERT_TRUE(box_pipeline); From 10d5d62e3129f326e7bf35d3ee8d6c2e05cbdd78 Mon Sep 17 00:00:00 2001 From: Chinmay Garde Date: Thu, 3 Feb 2022 11:19:29 -0800 Subject: [PATCH 278/433] Back out changes to the Impeller dispatcher stub that depend on WIP interface changes. (#3) --- impeller/display_list/display_list_dispatcher.cc | 5 ----- impeller/display_list/display_list_dispatcher.h | 3 --- 2 files changed, 8 deletions(-) diff --git a/impeller/display_list/display_list_dispatcher.cc b/impeller/display_list/display_list_dispatcher.cc index dccbd1f86a4bf..f5eebf2326982 100644 --- a/impeller/display_list/display_list_dispatcher.cc +++ b/impeller/display_list/display_list_dispatcher.cc @@ -212,11 +212,6 @@ void DisplayListDispatcher::transformFullPerspective(SkScalar mxx, canvas_.Transform(xformation); } -// |flutter::Dispatcher| -void DisplayListDispatcher::transformReset() { - canvas_.ResetTransform(); -} - static Rect ToRect(const SkRect& rect) { return Rect::MakeLTRB(rect.fLeft, rect.fTop, rect.fRight, rect.fBottom); } diff --git a/impeller/display_list/display_list_dispatcher.h b/impeller/display_list/display_list_dispatcher.h index 67a95d94b82d3..c32f295f6e4dd 100644 --- a/impeller/display_list/display_list_dispatcher.h +++ b/impeller/display_list/display_list_dispatcher.h @@ -118,9 +118,6 @@ class DisplayListDispatcher final : public flutter::Dispatcher { SkScalar mwz, SkScalar mwt) override; - // |flutter::Dispatcher| - void transformReset() override; - // |flutter::Dispatcher| void clipRect(const SkRect& rect, SkClipOp clip_op, bool is_aa) override; From 14cb1226afada15585e56201e92c923ff1c3b59c Mon Sep 17 00:00:00 2001 From: godofredoc Date: Thu, 3 Feb 2022 14:57:09 -0800 Subject: [PATCH 279/433] create scorecards-analysis.yml (#4) Adds scorecards to the impeller repository. --- .../.github/workflows/scorecards-analysis.yml | 55 +++++++++++++++++++ 1 file changed, 55 insertions(+) create mode 100644 impeller/.github/workflows/scorecards-analysis.yml diff --git a/impeller/.github/workflows/scorecards-analysis.yml b/impeller/.github/workflows/scorecards-analysis.yml new file mode 100644 index 0000000000000..8f29792baba4b --- /dev/null +++ b/impeller/.github/workflows/scorecards-analysis.yml @@ -0,0 +1,55 @@ +name: Scorecards supply-chain security +on: + # Only the default branch is supported. + branch_protection_rule: + schedule: + - cron: '37 18 * * 2' + push: + branches: [ main ] + +# Declare default permissions as read only. +permissions: read-all + +jobs: + analysis: + name: Scorecards analysis + runs-on: ubuntu-latest + permissions: + # Needed to upload the results to code-scanning dashboard. + security-events: write + actions: read + contents: read + + steps: + - name: "Checkout code" + uses: actions/checkout@ec3a7ce113134d7a93b817d10a8272cb61118579 # v2.4.0 + with: + persist-credentials: false + + - name: "Run analysis" + uses: ossf/scorecard-action@b614d455ee90608b5e36e3299cd50d457eb37d5f # v1.0.3 + with: + results_file: results.sarif + results_format: sarif + # Read-only PAT token. To create it, + # follow the steps in https://github.com/ossf/scorecard-action#pat-token-creation. + repo_token: ${{ secrets.SCORECARD_READ_TOKEN }} + # Publish the results to enable scorecard badges. For more details, see + # https://github.com/ossf/scorecard-action#publishing-results. + # For private repositories, `publish_results` will automatically be set to `false`, + # regardless of the value entered here. + publish_results: true + + # Upload the results as artifacts (optional). + - name: "Upload artifact" + uses: actions/upload-artifact@82c141cc518b40d92cc801eee768e7aafc9c2fa2 # v2.3.1 + with: + name: SARIF file + path: results.sarif + retention-days: 5 + + # Upload the results to GitHub's code scanning dashboard. + - name: "Upload to code-scanning" + uses: github/codeql-action/upload-sarif@5f532563584d71fdef14ee64d17bafb34f751ce5 # v1.0.26 + with: + sarif_file: results.sarif From 5a2d0b6c23eff09955bcac7c79f349891960ff1d Mon Sep 17 00:00:00 2001 From: Zachary Anderson Date: Fri, 4 Feb 2022 11:11:41 -0800 Subject: [PATCH 280/433] Use -M0 instead of -frecord-sources (#5) --- impeller/tools/build_metal_library.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/impeller/tools/build_metal_library.py b/impeller/tools/build_metal_library.py index 4e18d09e33125..fbfae128e3448 100644 --- a/impeller/tools/build_metal_library.py +++ b/impeller/tools/build_metal_library.py @@ -88,7 +88,9 @@ def Main(): command += [ # Embeds both sources and driver options in the output. This aids in # debugging but should be removed from release builds. - "-frecord-sources", + # TODO(chinmaygarde): Use -frecord-sources when CI upgrades to + # Xcode 13. + "-MO", # Assist the sampling profiler. "-gline-tables-only", "-g", From f4f6c37027b3d13df9a5286b2252aa6599dcb9e4 Mon Sep 17 00:00:00 2001 From: Zachary Anderson Date: Fri, 4 Feb 2022 15:29:58 -0800 Subject: [PATCH 281/433] Compensate for change to saveLayer (#6) --- impeller/display_list/display_list_dispatcher.cc | 6 ++++-- impeller/display_list/display_list_dispatcher.h | 3 ++- 2 files changed, 6 insertions(+), 3 deletions(-) diff --git a/impeller/display_list/display_list_dispatcher.cc b/impeller/display_list/display_list_dispatcher.cc index f5eebf2326982..1724ecf258942 100644 --- a/impeller/display_list/display_list_dispatcher.cc +++ b/impeller/display_list/display_list_dispatcher.cc @@ -137,8 +137,10 @@ static std::optional ToRect(const SkRect* rect) { // |flutter::Dispatcher| void DisplayListDispatcher::saveLayer(const SkRect* bounds, - bool restore_with_paint) { - canvas_.SaveLayer(restore_with_paint ? paint_ : Paint{}, ToRect(bounds)); + const flutter::SaveLayerOptions options) { + canvas_.SaveLayer( + options.renders_with_attributes() ? paint_ : Paint{}, + ToRect(bounds)); } // |flutter::Dispatcher| diff --git a/impeller/display_list/display_list_dispatcher.h b/impeller/display_list/display_list_dispatcher.h index c32f295f6e4dd..139ed5c6858fd 100644 --- a/impeller/display_list/display_list_dispatcher.h +++ b/impeller/display_list/display_list_dispatcher.h @@ -75,7 +75,8 @@ class DisplayListDispatcher final : public flutter::Dispatcher { void save() override; // |flutter::Dispatcher| - void saveLayer(const SkRect* bounds, bool restore_with_paint) override; + void saveLayer(const SkRect* bounds, + const flutter::SaveLayerOptions options) override; // |flutter::Dispatcher| void restore() override; From 51cb05aaa1412ffd0473bb3ee63b080bfad7e195 Mon Sep 17 00:00:00 2001 From: Zachary Anderson Date: Sat, 5 Feb 2022 21:52:17 -0800 Subject: [PATCH 282/433] Adds a GN flag for playgrounds (#7) --- impeller/aiks/aiks_playground.cc | 4 ++++ impeller/entity/entity_playground.cc | 4 ++++ impeller/playground/BUILD.gn | 8 ++++++++ impeller/playground/playground.h | 8 ++++++++ impeller/playground/playground.mm | 4 ++++ impeller/tools/impeller.gni | 5 +++++ 6 files changed, 33 insertions(+) diff --git a/impeller/aiks/aiks_playground.cc b/impeller/aiks/aiks_playground.cc index ff2513b31d584..572b761f83ab5 100644 --- a/impeller/aiks/aiks_playground.cc +++ b/impeller/aiks/aiks_playground.cc @@ -13,6 +13,10 @@ AiksPlayground::AiksPlayground() = default; AiksPlayground::~AiksPlayground() = default; bool AiksPlayground::OpenPlaygroundHere(const Picture& picture) { + if (!Playground::is_enabled()) { + return true; + } + AiksContext renderer(GetContext()); if (!renderer.IsValid()) { diff --git a/impeller/entity/entity_playground.cc b/impeller/entity/entity_playground.cc index 69e93b74751a4..4c76be6088bd4 100644 --- a/impeller/entity/entity_playground.cc +++ b/impeller/entity/entity_playground.cc @@ -13,6 +13,10 @@ EntityPlayground::EntityPlayground() = default; EntityPlayground::~EntityPlayground() = default; bool EntityPlayground::OpenPlaygroundHere(Entity entity) { + if (!Playground::is_enabled()) { + return true; + } + ContentContext context_context(GetContext()); if (!context_context.IsValid()) { return false; diff --git a/impeller/playground/BUILD.gn b/impeller/playground/BUILD.gn index c69165ec428ba..e1b38dfde4caa 100644 --- a/impeller/playground/BUILD.gn +++ b/impeller/playground/BUILD.gn @@ -21,6 +21,8 @@ impeller_component("playground") { "//third_party/glfw", ] + public_configs = [ ":playground_config" ] + if (is_mac) { frameworks = [ "AppKit.framework", @@ -28,3 +30,9 @@ impeller_component("playground") { ] } } + +config("playground_config") { + if (impeller_enable_playground) { + defines = [ "IMPELLER_ENABLE_PLAYGROUND" ] + } +} diff --git a/impeller/playground/playground.h b/impeller/playground/playground.h index fe83512e85464..b1779a624d956 100644 --- a/impeller/playground/playground.h +++ b/impeller/playground/playground.h @@ -19,6 +19,8 @@ class Playground : public ::testing::Test { ~Playground(); + static constexpr bool is_enabled() { return is_enabled_; } + Point GetCursorPosition() const; ISize GetWindowSize() const; @@ -31,6 +33,12 @@ class Playground : public ::testing::Test { const char* fixture_name) const; private: +#if IMPELLER_ENABLE_PLAYGROUND + static const bool is_enabled_ = true; +#else + static const bool is_enabled_ = false; +#endif // IMPELLER_ENABLE_PLAYGROUND + Renderer renderer_; Point cursor_position_; ISize window_size_ = ISize{1024, 768}; diff --git a/impeller/playground/playground.mm b/impeller/playground/playground.mm index 9918e9fe3faff..df4867ecaa423 100644 --- a/impeller/playground/playground.mm +++ b/impeller/playground/playground.mm @@ -81,6 +81,10 @@ static void PlaygroundKeyCallback(GLFWwindow* window, } bool Playground::OpenPlaygroundHere(Renderer::RenderCallback render_callback) { + if (!is_enabled()) { + return true; + } + if (!render_callback) { return true; } diff --git a/impeller/tools/impeller.gni b/impeller/tools/impeller.gni index 74a6e10af2826..2af17e3490405 100644 --- a/impeller/tools/impeller.gni +++ b/impeller/tools/impeller.gni @@ -5,6 +5,11 @@ import("//build/compiled_action.gni") import("//flutter/common/config.gni") +declare_args() { + # Whether playgrounds are enabled for unit tests. + impeller_enable_playground = false +} + template("impeller_component") { source_set(target_name) { forward_variables_from(invoker, "*") From 163b227d7aca52c803cfbfa988b5ae0def47c7eb Mon Sep 17 00:00:00 2001 From: Dan Field Date: Tue, 15 Feb 2022 21:30:08 -0800 Subject: [PATCH 283/433] Wrap all MD files at 80 cols, minor cleanup of headers/indenting (#8) --- impeller/README.md | 151 +++++++++++++++++++++++++++------- impeller/base/README.md | 3 +- impeller/compiler/README.md | 6 +- impeller/fixtures/README.md | 4 +- impeller/geometry/README.md | 7 +- impeller/image/README.md | 5 +- impeller/playground/README.md | 10 ++- 7 files changed, 148 insertions(+), 38 deletions(-) diff --git a/impeller/README.md b/impeller/README.md index a1fb2d48b1f63..6c2980751a146 100644 --- a/impeller/README.md +++ b/impeller/README.md @@ -15,44 +15,135 @@ Impeller is a rendering runtime for Flutter with the following objectives: - - -* **Predictable Performance**: All shader compilation and reflection is performed offline at build time. All pipeline state objects are built upfront. Caching is explicit and under the control of the engine. -* **Instrumentable**: All graphics resources (textures, buffers, pipeline state objects, etc..) are tagged and labeled. Animations can be captured and persisted to disk without affecting per-frame rendering performance. -* **Portable**: Not tied to a specific client rendering API. Shaders are authored once and converted as necessary. -* **Uses Modern Graphics APIs Effectively**: Makes heavy use of (but doesn’t depend on) features available in Modern APIs like Metal and Vulkan. -* **Makes Effective Use of Concurrency**: Can distribute single-frame workloads across multiple threads if necessary. +* **Predictable Performance**: All shader compilation and reflection is + performed offline at build time. All pipeline state objects are built upfront. + Caching is explicit and under the control of the engine. +* **Instrumentable**: All graphics resources (textures, buffers, pipeline state + objects, etc..) are tagged and labeled. Animations can be captured and + persisted to disk without affecting per-frame rendering performance. +* **Portable**: Not tied to a specific client rendering API. Shaders are + authored once and converted as necessary. +* **Uses Modern Graphics APIs Effectively**: Makes heavy use of (but doesn’t + depend on) features available in Modern APIs like Metal and Vulkan. +* **Makes Effective Use of Concurrency**: Can distribute single-frame workloads + across multiple threads if necessary. ## Project Organization -Impeller is a meta-framework. While a user of Impeller may choose to include the whole enchilada (in `//impeller/:impeller`), the various sub-frameworks have clearly defined responsibilities and adhere to a strict hierarchy. +Impeller is a meta-framework. While a user of Impeller may choose to include the +whole enchilada (in `//impeller/:impeller`), the various sub-frameworks have +clearly defined responsibilities and adhere to a strict hierarchy. -Impeller itself may not depend on anything in `//flutter` except `//flutter/fml` and `flutter/display_list`. FML is a base library for C++ projects and Impeller implements the display list dispatcher interface to make it easy for Flutter to swap the renderer with Impeller. Impeller is meant to be used by the Flow (`//flutter/flow`) subsystem. Hence the name. +Impeller itself may not depend on anything in `//flutter` except `//flutter/fml` +and `flutter/display_list`. FML is a base library for C++ projects and Impeller +implements the display list dispatcher interface to make it easy for Flutter to +swap the renderer with Impeller. Impeller is meant to be used by the Flow +(`//flutter/flow`) subsystem. Hence the name. -An overview of the major sub-frameworks, their responsibilities, and, relative states of completion: +An overview of the major sub-frameworks, their responsibilities, and, relative +states of completion: -* **`//impeller/compiler`**: The offline shader compiler. Takes GLSL 4.60 shader source code and converts it into a backend specific shader representation (like Metal Shading Language). It also generates C++ bindings that callers can include as a GN `source_set`s so there is no runtime shader reflection either. The target is an executable called `impellerc` which is never shipped into the binary or as an artifact. -* **`//impeller/renderer`**: The very lowest level of the renderer that is still backend agnostic. Allows users to build a renderer from scratch with few restrictions. Has utilities for creating allocators, generating pipeline state objects from bindings generated by `//impeller/compiler`, setting up render passes, managing jumbo uniform-buffers, tessellators, etc.. - * **`//impeller/renderer/backend`**: Contains all the implementation details for a specific client rendering API. The interfaces in these targets are meant to be private for non-WSI user targets. No Impeller sub-frameworks may depend on these targets. -* **`//impeller/archivist`**: Allows persisting objects to disk as performantly as possible (usually on a background thread). The framework is meant to be used for storing frame meta-data and related profiling/instrumentation information. Collection of information should succeed despite process crashes and retrieval of traces must not use inordinate amounts of time or memory (which usually leads to crashes). -* **`//impeller/geometry`**: All (or, most of) the math! This C++ mathematics library is used extensively by Impeller and its clients. The reasonably interesting bit about this library is that all types can be used interchangeably in device and host memory. Various Impeller subsystems understand these types and can take care of packing and alignment concerns w.r.t these types. -* **`//impeller/playground`**: When working with graphics APIs, it is often necessary to visually verify rendering results as a specific feature is being worked upon. Moreover, it is useful to attach frame debuggers or profilers to specific test cases. The playground framework provides Google Test fixtures that open the current state of various rendering related objects in a window in which rendering results can be visualized, or, to which frame debuggers can be attached. Most Impeller sub-frameworks that have a test harness also have a custom playground subclass. This sub-framework is only meant to provide utilities for tests and will not be compiled into any shipping binary. -* **`//impeller/entity`:** Sits one level above `//impeller/renderer` and provides a framework for building 2D renderers. Most of the pipeline state objects generated from shaders authored at build time reside in this framework. The render-pass optimization and pass-rewriting framework also resides there. This allows authoring composable 2D rendering optimizations (like collapsing passes, or, eliding them completely). -* **`//impeller/aiks`**: Aiks wraps `//impeller/entity` into an API that resembles Skia. This makes it easy to mechanically replace Skia calls with their Impeller counterparts even though the `//impeller/entity` framework API is different from Skia. This presence of this sub-framework is probably short-lived as integration of Impeller into Flutter should likely happen via a custom Display List implementation in `//impeller/display_list`. The roadblocks to this today are graphics package agnosticism in the Display List interface. -* **`//impeller/display_list`**: The replacement for `//impeller/aiks` to serve in the integration of Impeller in `//flutter/flow`. This is pending graphics package agnosticism in the Impeller interface. This sub-framework primarily provides a custom implementation of the `flutter::DisplayListDispatcher` that forwards Flutter rendering intent to Impeller. -* **`//impeller/base`**: Contains C++ utilities that are used throughout the Impeller family of frameworks. Ideally, these should go in `//flutter/fml` but their use is probably not widespread enough to at this time. -* **`//impeller/image`**: The Impeller renderer works with textures whose memory is resident in device memory. However, pending the migration of `//flutter/display_list` to graphics package agnosticism and the subsequent migration of the image decoders to work with the package agnostic types, there needs to be a way for tests and such to decode compressed image data. This sub-framework provides that functionality. This sub-framework is slated for removal and must not be used outside of tests. -* **`//fixtures`**: Contains test fixtures used by the various test harnesses. This depends on `//flutter/testing`. -* **`//tools`**: Contains all GN rules and python scripts for working with Impeller. These include GN rules processing GLSL shaders, including reflected shader information as source set targets, and, including compiled shader intermediate representations into the final executable as binary blobs for easier packaging. +* **`//impeller/compiler`**: The offline shader compiler. Takes GLSL 4.60 shader + source code and converts it into a backend specific shader representation + (like Metal Shading Language). It also generates C++ bindings that callers can + include as a GN `source_set`s so there is no runtime shader reflection either. + The target is an executable called `impellerc` which is never shipped into the + binary or as an artifact. +* **`//impeller/renderer`**: The very lowest level of the renderer that is still + backend agnostic. Allows users to build a renderer from scratch with few + restrictions. Has utilities for creating allocators, generating pipeline state + objects from bindings generated by `//impeller/compiler`, setting up render + passes, managing jumbo uniform-buffers, tessellators, etc.. + * **`//impeller/renderer/backend`**: Contains all the implementation details + for a specific client rendering API. The interfaces in these targets are + meant to be private for non-WSI user targets. No Impeller sub-frameworks + may depend on these targets. +* **`//impeller/archivist`**: Allows persisting objects to disk as performantly + as possible (usually on a background thread). The framework is meant to be + used for storing frame meta-data and related profiling/instrumentation + information. Collection of information should succeed despite process crashes + and retrieval of traces must not use inordinate amounts of time or memory + (which usually leads to crashes). +* **`//impeller/geometry`**: All (or, most of) the math! This C++ mathematics + library is used extensively by Impeller and its clients. The reasonably + interesting bit about this library is that all types can be used + interchangeably in device and host memory. Various Impeller subsystems + understand these types and can take care of packing and alignment concerns + w.r.t these types. +* **`//impeller/playground`**: When working with graphics APIs, it is often + necessary to visually verify rendering results as a specific feature is being + worked upon. Moreover, it is useful to attach frame debuggers or profilers to + specific test cases. The playground framework provides Google Test fixtures + that open the current state of various rendering related objects in a window + in which rendering results can be visualized, or, to which frame debuggers can + be attached. Most Impeller sub-frameworks that have a test harness also have a + custom playground subclass. This sub-framework is only meant to provide + utilities for tests and will not be compiled into any shipping binary. +* **`//impeller/entity`:** Sits one level above `//impeller/renderer` and + provides a framework for building 2D renderers. Most of the pipeline state + objects generated from shaders authored at build time reside in this + framework. The render-pass optimization and pass-rewriting framework also + resides there. This allows authoring composable 2D rendering optimizations + (like collapsing passes, or, eliding them completely). +* **`//impeller/aiks`**: Aiks wraps `//impeller/entity` into an API that + resembles Skia. This makes it easy to mechanically replace Skia calls with + their Impeller counterparts even though the `//impeller/entity` framework API + is different from Skia. This presence of this sub-framework is probably + short-lived as integration of Impeller into Flutter should likely happen via a + custom Display List implementation in `//impeller/display_list`. The + roadblocks to this today are graphics package agnosticism in the Display List + interface. +* **`//impeller/display_list`**: The replacement for `//impeller/aiks` to serve + in the integration of Impeller in `//flutter/flow`. This is pending graphics + package agnosticism in the Impeller interface. This sub-framework primarily + provides a custom implementation of the `flutter::DisplayListDispatcher` that + forwards Flutter rendering intent to Impeller. +* **`//impeller/base`**: Contains C++ utilities that are used throughout the + Impeller family of frameworks. Ideally, these should go in `//flutter/fml` but + their use is probably not widespread enough to at this time. +* **`//impeller/image`**: The Impeller renderer works with textures whose memory + is resident in device memory. However, pending the migration of + `//flutter/display_list` to graphics package agnosticism and the subsequent + migration of the image decoders to work with the package agnostic types, there + needs to be a way for tests and such to decode compressed image data. This + sub-framework provides that functionality. This sub-framework is slated for + removal and must not be used outside of tests. +* **`//fixtures`**: Contains test fixtures used by the various test harnesses. + This depends on `//flutter/testing`. +* **`//tools`**: Contains all GN rules and python scripts for working with + Impeller. These include GN rules processing GLSL shaders, including reflected + shader information as source set targets, and, including compiled shader + intermediate representations into the final executable as binary blobs for + easier packaging. ## The Offline Shader Compilation Pipeline -* Shaders are authored once in GLSL 4.60. This choice of shading language is consistent across all backends. Shader code resides in the Impeller source tree like any other source file. -* At build time, the Impeller Shader Compiler (`impellerc`) converts the GLSL into SPIRV. No optimizations are performed on the generated SPIRV at this stage. This is to preserve all debugging and instrumentation information. -* Using the SPIRV, a backend specific transpiler converts the SPIRV to the appropriate high-level shading language. This is controlled using flags to the `impellerc`. -* All the files generated in the high-level shading language are compiled, optimized, and linked into a single binary blob. -* The binary blob containing the compiled and optimized high-level shading language is included as a hex dump (see `xxd.py`) into a C source file with a generated GN target. Executable targets that want to include the compiled code in their binaries just need to depend on the generated GN target. This eases any shader packaging concerns. -* In parallel, the SPIRV is processed by a reflector. This produces C++ translation units that allow for the easy creation of pipeline state objects at runtime. The headers for these translation units include any structs (with appropriate padding and alignment) such that uniform data as well as vertex information can be specified to the shader without having to deal with bindings, vertex descriptors, etc.. This also makes iterating on shaders easier as changes to the shader interface lead to compile time errors. -* The C++ translation units generated from reflected shader information are made available to callers as a generated GN target that callers may use if necessary. It is possible for callers to perform reflection at runtime but there are no Impeller components that do this currently. +* Shaders are authored once in GLSL 4.60. This choice of shading language is + consistent across all backends. Shader code resides in the Impeller source + tree like any other source file. +* At build time, the Impeller Shader Compiler (`impellerc`) converts the GLSL + into SPIRV. No optimizations are performed on the generated SPIRV at this + stage. This is to preserve all debugging and instrumentation information. +* Using the SPIRV, a backend specific transpiler converts the SPIRV to the + appropriate high-level shading language. This is controlled using flags to the + `impellerc`. +* All the files generated in the high-level shading language are compiled, + optimized, and linked into a single binary blob. +* The binary blob containing the compiled and optimized high-level shading + language is included as a hex dump (see `xxd.py`) into a C source file with a + generated GN target. Executable targets that want to include the compiled code + in their binaries just need to depend on the generated GN target. This eases + any shader packaging concerns. +* In parallel, the SPIRV is processed by a reflector. This produces C++ + translation units that allow for the easy creation of pipeline state objects + at runtime. The headers for these translation units include any structs (with + appropriate padding and alignment) such that uniform data as well as vertex + information can be specified to the shader without having to deal with + bindings, vertex descriptors, etc.. This also makes iterating on shaders + easier as changes to the shader interface lead to compile time errors. +* The C++ translation units generated from reflected shader information are made + available to callers as a generated GN target that callers may use if + necessary. It is possible for callers to perform reflection at runtime but + there are no Impeller components that do this currently. ![Shader Compilation Pipeline](docs/shader_pipeline.png) diff --git a/impeller/base/README.md b/impeller/base/README.md index 563bbb0bd10a4..08814d637966f 100644 --- a/impeller/base/README.md +++ b/impeller/base/README.md @@ -1,3 +1,4 @@ # The Impeller Base Library -Contains a number of utilities that should probably go in the base library in the buildroot but whose use is not extensive enough to warrant a move yet. +Contains a number of utilities that should probably go in the base library in +the buildroot but whose use is not extensive enough to warrant a move yet. diff --git a/impeller/compiler/README.md b/impeller/compiler/README.md index 6430d4cb0dfb8..cf44365babce9 100644 --- a/impeller/compiler/README.md +++ b/impeller/compiler/README.md @@ -1,3 +1,7 @@ # The Impeller Shader Compiler & Reflector -Host side tooling that consumes [GLSL 4.60 (Core Profile)](https://www.khronos.org/registry/OpenGL/specs/gl/GLSLangSpec.4.60.pdf) shaders and generates libraries suitable for consumption by an Impeller backend. Along with said libraries, the reflector generates code and meta-data to construct rendering and compute pipelines at runtime. +Host side tooling that consumes [GLSL 4.60 (Core +Profile)](https://www.khronos.org/registry/OpenGL/specs/gl/GLSLangSpec.4.60.pdf) +shaders and generates libraries suitable for consumption by an Impeller backend. +Along with said libraries, the reflector generates code and meta-data to +construct rendering and compute pipelines at runtime. diff --git a/impeller/fixtures/README.md b/impeller/fixtures/README.md index 362166c02e0ff..5c937aab5e4b3 100644 --- a/impeller/fixtures/README.md +++ b/impeller/fixtures/README.md @@ -1,3 +1,5 @@ # The Impeller Fixtures Set -Unlike other targets in the buildroot, all Impeller unit-tests use the same fixture set and are invoked using a single test harness (`impeller_unittest`). This is for convenience but also to make working with shader libraries easier. +Unlike other targets in the buildroot, all Impeller unit-tests use the same +fixture set and are invoked using a single test harness (`impeller_unittest`). +This is for convenience but also to make working with shader libraries easier. diff --git a/impeller/geometry/README.md b/impeller/geometry/README.md index eb154c30eeb88..c58c4af1e0e22 100644 --- a/impeller/geometry/README.md +++ b/impeller/geometry/README.md @@ -1,3 +1,8 @@ # The Impeller Geometry Library -Set of utilities used by most graphics operations. While the utilities themselves are rendering backend agnostic, the layout and packing of the various POD structs is arranged such that these can be copied into device memory directly. The supported operations also mimic GLSL to some extent. For this reason, the Impeller shader compiler and reflector uses these utilities in generated code. +Set of utilities used by most graphics operations. While the utilities +themselves are rendering backend agnostic, the layout and packing of the various +POD structs is arranged such that these can be copied into device memory +directly. The supported operations also mimic GLSL to some extent. For this +reason, the Impeller shader compiler and reflector uses these utilities in +generated code. diff --git a/impeller/image/README.md b/impeller/image/README.md index b1400c70d8d0a..97840b564b674 100644 --- a/impeller/image/README.md +++ b/impeller/image/README.md @@ -1,3 +1,4 @@ -The Impeller Image Library +# The Impeller Image Library -Set of utilities for working with texture information. The library is indepenent of the rendering subsystem. +Set of utilities for working with texture information. The library is indepenent +of the rendering subsystem. diff --git a/impeller/playground/README.md b/impeller/playground/README.md index d5d11dd30492a..d1964b51fbea1 100644 --- a/impeller/playground/README.md +++ b/impeller/playground/README.md @@ -1,3 +1,9 @@ -The Impeller Playground +# The Impeller Playground -An extension of the testing fixtures set, provides utilities for interactive experimentation with the Impeller rendering subsystem. One the test author is satisfied with the behavior of component as verified in the playground, pixel test assertions can be added to before committing the new test case. Meant to provide a gentle-er on-ramp to testing Impeller components. The WSI in the playground allows for points at which third-party profiling and instrumentation tools can be used to examine isolated test cases. +An extension of the testing fixtures set, provides utilities for interactive +experimentation with the Impeller rendering subsystem. One the test author is +satisfied with the behavior of component as verified in the playground, pixel +test assertions can be added to before committing the new test case. Meant to +provide a gentle-er on-ramp to testing Impeller components. The WSI in the +playground allows for points at which third-party profiling and instrumentation +tools can be used to examine isolated test cases. From de0983ffbf0e2ae7e74a186430a7185f7e01ca6f Mon Sep 17 00:00:00 2001 From: Brandon DeRosier Date: Wed, 16 Feb 2022 11:53:49 -0800 Subject: [PATCH 284/433] Fix Rect::Compare bugs (#10) --- impeller/geometry/geometry_unittests.cc | 25 +++++++++++++++++++++++++ impeller/geometry/rect.h | 4 ++-- 2 files changed, 27 insertions(+), 2 deletions(-) diff --git a/impeller/geometry/geometry_unittests.cc b/impeller/geometry/geometry_unittests.cc index fdd36133691f5..a567475c789e9 100644 --- a/impeller/geometry/geometry_unittests.cc +++ b/impeller/geometry/geometry_unittests.cc @@ -310,5 +310,30 @@ TEST(GeometryTest, RectIntersection) { } } +TEST(GeometryTest, RectContainsPoint) { + { + // Origin is inclusive + Rect r(100, 100, 100, 100); + Point p(100, 100); + ASSERT_TRUE(r.Contains(p)); + } + { + // Size is exclusive + Rect r(100, 100, 100, 100); + Point p(200, 200); + ASSERT_FALSE(r.Contains(p)); + } + { + Rect r(100, 100, 100, 100); + Point p(99, 99); + ASSERT_FALSE(r.Contains(p)); + } + { + Rect r(100, 100, 100, 100); + Point p(199, 199); + ASSERT_TRUE(r.Contains(p)); + } +} + } // namespace testing } // namespace impeller diff --git a/impeller/geometry/rect.h b/impeller/geometry/rect.h index 4517eba06e5db..00a0ae6ddd255 100644 --- a/impeller/geometry/rect.h +++ b/impeller/geometry/rect.h @@ -80,8 +80,8 @@ struct TRect { } constexpr bool Contains(const TPoint& p) const { - return p.x >= origin.x && p.x <= size.width && p.y >= origin.y && - p.y <= size.height; + return p.x >= origin.x && p.x < origin.x + size.width && p.y >= origin.y && + p.y < origin.y + size.height; } constexpr bool IsZero() const { return size.IsZero(); } From 56cfb376552b6a0dbadc004214f118310c66d6c3 Mon Sep 17 00:00:00 2001 From: Jim Graham Date: Wed, 16 Feb 2022 14:09:22 -0800 Subject: [PATCH 285/433] adopt new DlColorFilter objects (#12) --- .../display_list/display_list_dispatcher.cc | 17 +++++++++++++++-- impeller/display_list/display_list_dispatcher.h | 2 +- 2 files changed, 16 insertions(+), 3 deletions(-) diff --git a/impeller/display_list/display_list_dispatcher.cc b/impeller/display_list/display_list_dispatcher.cc index 1724ecf258942..331e002aa5aba 100644 --- a/impeller/display_list/display_list_dispatcher.cc +++ b/impeller/display_list/display_list_dispatcher.cc @@ -79,9 +79,22 @@ void DisplayListDispatcher::setShader(sk_sp shader) { } // |flutter::Dispatcher| -void DisplayListDispatcher::setColorFilter(sk_sp filter) { +void DisplayListDispatcher::setColorFilter( + const flutter::DlColorFilter* filter) { // Needs https://github.com/flutter/flutter/issues/95434 - UNIMPLEMENTED; + if (filter == nullptr) { + // Reset everything + return; + } + switch (filter->type()) { + case flutter::DlColorFilter::kBlend: + case flutter::DlColorFilter::kMatrix: + case flutter::DlColorFilter::kSrgbToLinearGamma: + case flutter::DlColorFilter::kLinearToSrgbGamma: + case flutter::DlColorFilter::kUnknown: + UNIMPLEMENTED; + break; + } } // |flutter::Dispatcher| diff --git a/impeller/display_list/display_list_dispatcher.h b/impeller/display_list/display_list_dispatcher.h index 139ed5c6858fd..79234d418480a 100644 --- a/impeller/display_list/display_list_dispatcher.h +++ b/impeller/display_list/display_list_dispatcher.h @@ -48,7 +48,7 @@ class DisplayListDispatcher final : public flutter::Dispatcher { void setShader(sk_sp shader) override; // |flutter::Dispatcher| - void setColorFilter(sk_sp filter) override; + void setColorFilter(const flutter::DlColorFilter* filter) override; // |flutter::Dispatcher| void setInvertColors(bool invert) override; From a476b9299b8c96b2afee0acb15a30780f26bdc2e Mon Sep 17 00:00:00 2001 From: Brandon DeRosier Date: Wed, 16 Feb 2022 14:35:08 -0800 Subject: [PATCH 286/433] Support 16bit index buffers (#11) --- impeller/renderer/backend/metal/formats_mtl.h | 9 +++++++++ impeller/renderer/backend/metal/render_pass_mtl.mm | 6 +++++- impeller/renderer/command.cc | 6 ++++++ impeller/renderer/command.h | 1 + impeller/renderer/formats.h | 6 ++++++ impeller/renderer/vertex_buffer.h | 2 ++ impeller/renderer/vertex_buffer_builder.h | 12 ++++++++++++ 7 files changed, 41 insertions(+), 1 deletion(-) diff --git a/impeller/renderer/backend/metal/formats_mtl.h b/impeller/renderer/backend/metal/formats_mtl.h index 47e17843132a4..6d1b47e69dae1 100644 --- a/impeller/renderer/backend/metal/formats_mtl.h +++ b/impeller/renderer/backend/metal/formats_mtl.h @@ -107,6 +107,15 @@ constexpr MTLPrimitiveType ToMTLPrimitiveType(PrimitiveType type) { return MTLPrimitiveTypePoint; } +constexpr MTLIndexType ToMTLIndexType(IndexType type) { + switch (type) { + case IndexType::k16bit: + return MTLIndexTypeUInt16; + default: + return MTLIndexTypeUInt32; + } +} + constexpr MTLBlendOperation ToMTLBlendOperation(BlendOperation type) { switch (type) { case BlendOperation::kAdd: diff --git a/impeller/renderer/backend/metal/render_pass_mtl.mm b/impeller/renderer/backend/metal/render_pass_mtl.mm index 6a685d975c271..f50389a2d7d04 100644 --- a/impeller/renderer/backend/metal/render_pass_mtl.mm +++ b/impeller/renderer/backend/metal/render_pass_mtl.mm @@ -13,6 +13,7 @@ #include "impeller/renderer/backend/metal/pipeline_mtl.h" #include "impeller/renderer/backend/metal/sampler_mtl.h" #include "impeller/renderer/backend/metal/texture_mtl.h" +#include "impeller/renderer/formats.h" #include "impeller/renderer/host_buffer.h" #include "impeller/renderer/shader_types.h" @@ -434,6 +435,9 @@ static bool Bind(PassBindingsCache& pass, ShaderStage::kFragment)) { return false; } + if (command.index_type == IndexType::kUnknown) { + return false; + } auto index_buffer = command.index_buffer.buffer; if (!index_buffer) { return false; @@ -452,7 +456,7 @@ static bool Bind(PassBindingsCache& pass, // Returns void. All error checking must be done by this point. [encoder drawIndexedPrimitives:ToMTLPrimitiveType(command.primitive_type) indexCount:command.index_count - indexType:MTLIndexTypeUInt32 + indexType:ToMTLIndexType(command.index_type) indexBuffer:mtl_index_buffer indexBufferOffset:command.index_buffer.range.offset instanceCount:1u diff --git a/impeller/renderer/command.cc b/impeller/renderer/command.cc index aca7d2b31cecb..cbdb529c8467b 100644 --- a/impeller/renderer/command.cc +++ b/impeller/renderer/command.cc @@ -4,15 +4,21 @@ #include "impeller/renderer/command.h" +#include "impeller/renderer/formats.h" #include "impeller/renderer/vertex_descriptor.h" namespace impeller { bool Command::BindVertices(const VertexBuffer& buffer) { + if (index_type == IndexType::kUnknown) { + return false; + } + vertex_bindings.buffers[VertexDescriptor::kReservedVertexBufferIndex] = buffer.vertex_buffer; index_buffer = buffer.index_buffer; index_count = buffer.index_count; + index_type = buffer.index_type; return true; } diff --git a/impeller/renderer/command.h b/impeller/renderer/command.h index e0d18193ad32b..4890d6ffaefe1 100644 --- a/impeller/renderer/command.h +++ b/impeller/renderer/command.h @@ -64,6 +64,7 @@ struct Command { /// BufferView index_buffer; size_t index_count = 0u; + IndexType index_type = IndexType::kUnknown; std::string label; PrimitiveType primitive_type = PrimitiveType::kTriangle; WindingOrder winding = WindingOrder::kClockwise; diff --git a/impeller/renderer/formats.h b/impeller/renderer/formats.h index 796c6258387c7..0d420ee1097da 100644 --- a/impeller/renderer/formats.h +++ b/impeller/renderer/formats.h @@ -133,6 +133,12 @@ enum class WindingOrder { kCounterClockwise, }; +enum class IndexType { + kUnknown, + k16bit, + k32bit, +}; + enum class PrimitiveType { kTriangle, kTriangleStrip, diff --git a/impeller/renderer/vertex_buffer.h b/impeller/renderer/vertex_buffer.h index 596731fa72bc0..35b9743db8621 100644 --- a/impeller/renderer/vertex_buffer.h +++ b/impeller/renderer/vertex_buffer.h @@ -5,6 +5,7 @@ #pragma once #include "impeller/renderer/buffer_view.h" +#include "impeller/renderer/formats.h" namespace impeller { @@ -12,6 +13,7 @@ struct VertexBuffer { BufferView vertex_buffer; BufferView index_buffer; size_t index_count = 0u; + IndexType index_type = IndexType::kUnknown; constexpr operator bool() const { return static_cast(vertex_buffer) && static_cast(index_buffer); diff --git a/impeller/renderer/vertex_buffer_builder.h b/impeller/renderer/vertex_buffer_builder.h index c217241e4861c..1caac9032ae8f 100644 --- a/impeller/renderer/vertex_buffer_builder.h +++ b/impeller/renderer/vertex_buffer_builder.h @@ -29,6 +29,16 @@ class VertexBufferBuilder { ~VertexBufferBuilder() = default; + constexpr impeller::IndexType GetIndexType() const { + if constexpr (sizeof(IndexType) == 2) { + return impeller::IndexType::k16bit; + } else if (sizeof(IndexType) == 4) { + return impeller::IndexType::k32bit; + } else { + return impeller::IndexType::kUnknown; + } + } + void SetLabel(std::string label) { label_ = std::move(label); } void Reserve(size_t count) { return vertices_.reserve(count); } @@ -56,6 +66,7 @@ class VertexBufferBuilder { buffer.vertex_buffer = CreateVertexBufferView(host_buffer); buffer.index_buffer = CreateIndexBufferView(host_buffer); buffer.index_count = GetIndexCount(); + buffer.index_type = GetIndexType(); return buffer; }; @@ -65,6 +76,7 @@ class VertexBufferBuilder { buffer.vertex_buffer = CreateVertexBufferView(device_allocator); buffer.index_buffer = CreateIndexBufferView(device_allocator); buffer.index_count = GetIndexCount(); + buffer.index_type = GetIndexType(); return buffer; }; From 3c95a89ff28e3ccb0d68c5a1ccfe6592a3cde80a Mon Sep 17 00:00:00 2001 From: Brandon DeRosier Date: Wed, 16 Feb 2022 15:46:24 -0800 Subject: [PATCH 287/433] Add viewport and scissor support (#9) --- impeller/geometry/geometry_unittests.cc | 32 +++++++++++++++++++ impeller/geometry/rect.h | 4 +++ .../renderer/backend/metal/render_pass_mtl.mm | 31 ++++++++++++++++++ impeller/renderer/command.h | 15 +++++++++ impeller/renderer/formats.h | 8 +++++ 5 files changed, 90 insertions(+) diff --git a/impeller/geometry/geometry_unittests.cc b/impeller/geometry/geometry_unittests.cc index a567475c789e9..af5cb6c9bbfa7 100644 --- a/impeller/geometry/geometry_unittests.cc +++ b/impeller/geometry/geometry_unittests.cc @@ -335,5 +335,37 @@ TEST(GeometryTest, RectContainsPoint) { } } +TEST(GeometryTest, RectContainsRect) { + { + Rect a(100, 100, 100, 100); + ASSERT_TRUE(a.Contains(a)); + } + { + Rect a(100, 100, 100, 100); + Rect b(0, 0, 0, 0); + ASSERT_FALSE(a.Contains(b)); + } + { + Rect a(100, 100, 100, 100); + Rect b(150, 150, 20, 20); + ASSERT_TRUE(a.Contains(b)); + } + { + Rect a(100, 100, 100, 100); + Rect b(150, 150, 100, 100); + ASSERT_FALSE(a.Contains(b)); + } + { + Rect a(100, 100, 100, 100); + Rect b(50, 50, 100, 100); + ASSERT_FALSE(a.Contains(b)); + } + { + Rect a(100, 100, 100, 100); + Rect b(0, 0, 300, 300); + ASSERT_FALSE(a.Contains(b)); + } +} + } // namespace testing } // namespace impeller diff --git a/impeller/geometry/rect.h b/impeller/geometry/rect.h index 00a0ae6ddd255..96fc41fd8f1f3 100644 --- a/impeller/geometry/rect.h +++ b/impeller/geometry/rect.h @@ -84,6 +84,10 @@ struct TRect { p.y < origin.y + size.height; } + constexpr bool Contains(const TRect& o) const { + return Union(o).size == size; + } + constexpr bool IsZero() const { return size.IsZero(); } constexpr bool IsEmpty() const { return size.IsEmpty(); } diff --git a/impeller/renderer/backend/metal/render_pass_mtl.mm b/impeller/renderer/backend/metal/render_pass_mtl.mm index f50389a2d7d04..9279d16f2671d 100644 --- a/impeller/renderer/backend/metal/render_pass_mtl.mm +++ b/impeller/renderer/backend/metal/render_pass_mtl.mm @@ -428,6 +428,28 @@ static bool Bind(PassBindingsCache& pass, : MTLWindingCounterClockwise]; [encoder setCullMode:MTLCullModeNone]; [encoder setStencilReferenceValue:command.stencil_reference]; + if (command.viewport.has_value()) { + auto v = command.viewport.value(); + MTLViewport viewport = { + .originX = v.rect.origin.x, + .originY = v.rect.origin.y, + .width = v.rect.size.width, + .height = v.rect.size.height, + .znear = v.znear, + .zfar = v.zfar, + }; + [encoder setViewport:viewport]; + } + if (command.scissor.has_value()) { + auto s = command.scissor.value(); + MTLScissorRect scissor = { + .x = static_cast(s.origin.x), + .y = static_cast(s.origin.y), + .width = static_cast(s.size.width), + .height = static_cast(s.size.height), + }; + [encoder setScissorRect:scissor]; + } if (!bind_stage_resources(command.vertex_bindings, ShaderStage::kVertex)) { return false; } @@ -472,6 +494,15 @@ static bool Bind(PassBindingsCache& pass, return false; } + if (command.scissor.has_value()) { + auto target_rect = IRect({}, render_target_.GetRenderTargetSize()); + if (!target_rect.Contains(command.scissor.value())) { + VALIDATION_LOG << "Cannot apply a scissor that lies outside the bounds " + "of the render target."; + return false; + } + } + commands_.emplace_back(std::move(command)); return true; } diff --git a/impeller/renderer/command.h b/impeller/renderer/command.h index 4890d6ffaefe1..30c2d4cfe662c 100644 --- a/impeller/renderer/command.h +++ b/impeller/renderer/command.h @@ -6,10 +6,12 @@ #include #include +#include #include #include "flutter/fml/logging.h" #include "flutter/fml/macros.h" +#include "impeller/geometry/rect.h" #include "impeller/renderer/buffer_view.h" #include "impeller/renderer/formats.h" #include "impeller/renderer/pipeline.h" @@ -69,6 +71,19 @@ struct Command { PrimitiveType primitive_type = PrimitiveType::kTriangle; WindingOrder winding = WindingOrder::kClockwise; uint32_t stencil_reference = 0u; + //---------------------------------------------------------------------------- + /// The viewport coordinates that the rasterizer linearly maps normalized + /// device coordinates to. + /// If unset, the viewport is the size of the render target with a zero + /// origin, znear=0, and zfar=1. + /// + std::optional viewport; + //---------------------------------------------------------------------------- + /// The scissor rect to use for clipping writes to the render target. The + /// scissor rect must lie entirely within the render target. + /// If unset, no scissor is applied. + /// + std::optional scissor; bool BindVertices(const VertexBuffer& buffer); diff --git a/impeller/renderer/formats.h b/impeller/renderer/formats.h index 0d420ee1097da..94e7d94012287 100644 --- a/impeller/renderer/formats.h +++ b/impeller/renderer/formats.h @@ -11,6 +11,8 @@ #include "flutter/fml/hash_combine.h" #include "flutter/fml/macros.h" +#include "impeller/geometry/rect.h" +#include "impeller/geometry/scalar.h" #include "impeller/geometry/color.h" namespace impeller { @@ -149,6 +151,12 @@ enum class PrimitiveType { // checks. Hence, they are not supported here. }; +struct Viewport { + Rect rect; + Scalar znear = 0.0f; + Scalar zfar = 1.0f; +}; + enum class MinMagFilter { /// Select nearest to the sample point. Most widely supported. kNearest, From eefea2d60d47fb4fa4918a0b7d63c67272cbd451 Mon Sep 17 00:00:00 2001 From: Brandon DeRosier Date: Thu, 17 Feb 2022 10:46:55 -0800 Subject: [PATCH 288/433] Add cull mode to command (#13) --- impeller/renderer/backend/metal/formats_mtl.h | 12 ++++++++++++ impeller/renderer/backend/metal/render_pass_mtl.mm | 2 +- impeller/renderer/command.h | 1 + impeller/renderer/formats.h | 8 +++++++- 4 files changed, 21 insertions(+), 2 deletions(-) diff --git a/impeller/renderer/backend/metal/formats_mtl.h b/impeller/renderer/backend/metal/formats_mtl.h index 6d1b47e69dae1..9cdb319f1bfca 100644 --- a/impeller/renderer/backend/metal/formats_mtl.h +++ b/impeller/renderer/backend/metal/formats_mtl.h @@ -116,6 +116,18 @@ constexpr MTLIndexType ToMTLIndexType(IndexType type) { } } +constexpr MTLCullMode ToMTLCullMode(CullMode mode) { + switch (mode) { + case CullMode::kNone: + return MTLCullModeNone; + case CullMode::kBackFace: + return MTLCullModeBack; + case CullMode::kFrontFace: + return MTLCullModeFront; + } + return MTLCullModeNone; +} + constexpr MTLBlendOperation ToMTLBlendOperation(BlendOperation type) { switch (type) { case BlendOperation::kAdd: diff --git a/impeller/renderer/backend/metal/render_pass_mtl.mm b/impeller/renderer/backend/metal/render_pass_mtl.mm index 9279d16f2671d..9d7a9e24ffe50 100644 --- a/impeller/renderer/backend/metal/render_pass_mtl.mm +++ b/impeller/renderer/backend/metal/render_pass_mtl.mm @@ -426,7 +426,7 @@ static bool Bind(PassBindingsCache& pass, [encoder setFrontFacingWinding:command.winding == WindingOrder::kClockwise ? MTLWindingClockwise : MTLWindingCounterClockwise]; - [encoder setCullMode:MTLCullModeNone]; + [encoder setCullMode:ToMTLCullMode(command.cull_mode)]; [encoder setStencilReferenceValue:command.stencil_reference]; if (command.viewport.has_value()) { auto v = command.viewport.value(); diff --git a/impeller/renderer/command.h b/impeller/renderer/command.h index 30c2d4cfe662c..fb73d1b16da63 100644 --- a/impeller/renderer/command.h +++ b/impeller/renderer/command.h @@ -70,6 +70,7 @@ struct Command { std::string label; PrimitiveType primitive_type = PrimitiveType::kTriangle; WindingOrder winding = WindingOrder::kClockwise; + CullMode cull_mode = CullMode::kNone; uint32_t stencil_reference = 0u; //---------------------------------------------------------------------------- /// The viewport coordinates that the rasterizer linearly maps normalized diff --git a/impeller/renderer/formats.h b/impeller/renderer/formats.h index 94e7d94012287..092d86283fc53 100644 --- a/impeller/renderer/formats.h +++ b/impeller/renderer/formats.h @@ -11,9 +11,9 @@ #include "flutter/fml/hash_combine.h" #include "flutter/fml/macros.h" +#include "impeller/geometry/color.h" #include "impeller/geometry/rect.h" #include "impeller/geometry/scalar.h" -#include "impeller/geometry/color.h" namespace impeller { @@ -135,6 +135,12 @@ enum class WindingOrder { kCounterClockwise, }; +enum class CullMode { + kNone, + kFrontFace, + kBackFace, +}; + enum class IndexType { kUnknown, k16bit, From 3cefd909e621774e866579846c24ee50f7a5d843 Mon Sep 17 00:00:00 2001 From: Brandon DeRosier Date: Thu, 17 Feb 2022 11:32:35 -0800 Subject: [PATCH 289/433] Support nested clips & clip state restoration (#14) Clip restoration works with a single draw call: - When clip paths are added, they increase the stencil height only if the stencil matches the current depth. So higher depths are always a subset of lower depths. - When popping the canvas stack, an entity is appended to run a draw call which max bounds the stencil to the previous depth. Fixes flutter/flutter#98631. --- impeller/aiks/aiks_unittests.cc | 13 +++++++ impeller/aiks/canvas.cc | 29 +++++++++++---- impeller/aiks/canvas.h | 4 +-- impeller/entity/content_context.cc | 53 +++++++++++++++++++--------- impeller/entity/content_context.h | 5 +++ impeller/entity/contents.cc | 45 ++++++++++++++++++++++- impeller/entity/contents.h | 15 ++++++++ impeller/entity/entity_pass.h | 1 + impeller/renderer/pipeline_builder.h | 3 +- 9 files changed, 141 insertions(+), 27 deletions(-) diff --git a/impeller/aiks/aiks_unittests.cc b/impeller/aiks/aiks_unittests.cc index a756bbf0185ad..0851fd7e0de82 100644 --- a/impeller/aiks/aiks_unittests.cc +++ b/impeller/aiks/aiks_unittests.cc @@ -106,6 +106,19 @@ TEST_F(AiksTest, CanRenderClips) { ASSERT_TRUE(OpenPlaygroundHere(canvas.EndRecordingAsPicture())); } +TEST_F(AiksTest, CanRenderNestedClips) { + Canvas canvas; + Paint paint; + paint.color = Color::Fuchsia(); + canvas.Save(); + canvas.ClipPath(PathBuilder{}.AddCircle({200, 400}, 300).TakePath()); + canvas.Restore(); + canvas.ClipPath(PathBuilder{}.AddCircle({600, 400}, 300).TakePath()); + canvas.ClipPath(PathBuilder{}.AddCircle({400, 600}, 300).TakePath()); + canvas.DrawRect(Rect::MakeXYWH(200, 200, 400, 400), paint); + ASSERT_TRUE(OpenPlaygroundHere(canvas.EndRecordingAsPicture())); +} + TEST_F(AiksTest, CanSaveLayerStandalone) { Canvas canvas; diff --git a/impeller/aiks/canvas.cc b/impeller/aiks/canvas.cc index 3af66e13e8918..0825d5c2bac4d 100644 --- a/impeller/aiks/canvas.cc +++ b/impeller/aiks/canvas.cc @@ -45,7 +45,14 @@ bool Canvas::Restore() { current_pass_ = GetCurrentPass().GetSuperpass(); FML_DCHECK(current_pass_); } + + bool contains_clips = xformation_stack_.back().contains_clips; xformation_stack_.pop_back(); + + if (contains_clips) { + RestoreClip(); + } + return true; } @@ -129,8 +136,6 @@ void Canvas::SaveLayer(Paint paint, std::optional bounds) { } void Canvas::ClipPath(Path path) { - IncrementStencilDepth(); - Entity entity; entity.SetTransformation(GetCurrentTransformation()); entity.SetPath(std::move(path)); @@ -139,6 +144,22 @@ void Canvas::ClipPath(Path path) { entity.SetAddsToCoverage(false); GetCurrentPass().AddEntity(std::move(entity)); + + ++xformation_stack_.back().stencil_depth; + xformation_stack_.back().contains_clips = true; +} + +void Canvas::RestoreClip() { + Entity entity; + entity.SetTransformation(GetCurrentTransformation()); + // This path is empty because ClipRestoreContents just generates a quad that + // takes up the full render target. + entity.SetPath({}); + entity.SetContents(std::make_shared()); + entity.SetStencilDepth(GetStencilDepth()); + entity.SetAddsToCoverage(false); + + GetCurrentPass().AddEntity(std::move(entity)); } void Canvas::DrawShadow(Path path, Color color, Scalar elevation) {} @@ -213,10 +234,6 @@ EntityPass& Canvas::GetCurrentPass() { return *current_pass_; } -void Canvas::IncrementStencilDepth() { - ++xformation_stack_.back().stencil_depth; -} - size_t Canvas::GetStencilDepth() const { return xformation_stack_.back().stencil_depth; } diff --git a/impeller/aiks/canvas.h b/impeller/aiks/canvas.h index 2b00151f585a0..367669eccbdb8 100644 --- a/impeller/aiks/canvas.h +++ b/impeller/aiks/canvas.h @@ -87,12 +87,12 @@ class Canvas { EntityPass& GetCurrentPass(); - void IncrementStencilDepth(); - size_t GetStencilDepth() const; void Save(bool create_subpass); + void RestoreClip(); + FML_DISALLOW_COPY_AND_ASSIGN(Canvas); }; diff --git a/impeller/entity/content_context.cc b/impeller/entity/content_context.cc index 5a628518df3ab..53fd91745f6ac 100644 --- a/impeller/entity/content_context.cc +++ b/impeller/entity/content_context.cc @@ -23,25 +23,44 @@ ContentContext::ContentContext(std::shared_ptr context) std::make_unique(*context_); // Pipelines that are variants of the base pipelines with custom descriptors. + // TODO(98684): Rework this API to allow fetching the descriptor without + // waiting for the pipeline to build. if (auto solid_fill_pipeline = solid_fill_pipelines_[{}]->WaitAndGet()) { - auto clip_pipeline_descriptor = solid_fill_pipeline->GetDescriptor(); - clip_pipeline_descriptor.SetLabel("Clip Pipeline"); - // Write to the stencil buffer. - StencilAttachmentDescriptor stencil0; - stencil0.stencil_compare = CompareFunction::kGreaterEqual; - stencil0.depth_stencil_pass = StencilOperation::kSetToReferenceValue; - clip_pipeline_descriptor.SetStencilAttachmentDescriptors(stencil0); - // Disable write to all color attachments. - auto color_attachments = - clip_pipeline_descriptor.GetColorAttachmentDescriptors(); - for (auto& color_attachment : color_attachments) { - color_attachment.second.write_mask = - static_cast(ColorWriteMask::kNone); + // Clip pipeline. + { + auto clip_pipeline_descriptor = solid_fill_pipeline->GetDescriptor(); + clip_pipeline_descriptor.SetLabel("Clip Pipeline"); + // Write to the stencil buffer. + StencilAttachmentDescriptor stencil0; + stencil0.stencil_compare = CompareFunction::kEqual; + stencil0.depth_stencil_pass = StencilOperation::kIncrementClamp; + clip_pipeline_descriptor.SetStencilAttachmentDescriptors(stencil0); + // Disable write to all color attachments. + auto color_attachments = + clip_pipeline_descriptor.GetColorAttachmentDescriptors(); + for (auto& color_attachment : color_attachments) { + color_attachment.second.write_mask = + static_cast(ColorWriteMask::kNone); + } + clip_pipeline_descriptor.SetColorAttachmentDescriptors( + std::move(color_attachments)); + clip_pipelines_[{}] = + std::make_unique(*context_, clip_pipeline_descriptor); + } + + // Clip restoration pipeline. + { + auto clip_pipeline_descriptor = + clip_pipelines_[{}]->WaitAndGet()->GetDescriptor(); + clip_pipeline_descriptor.SetLabel("Clip Restoration Pipeline"); + // Write to the stencil buffer. + StencilAttachmentDescriptor stencil0; + stencil0.stencil_compare = CompareFunction::kLess; + stencil0.depth_stencil_pass = StencilOperation::kSetToReferenceValue; + clip_pipeline_descriptor.SetStencilAttachmentDescriptors(stencil0); + clip_restoration_pipelines_[{}] = std::make_unique( + *context_, std::move(clip_pipeline_descriptor)); } - clip_pipeline_descriptor.SetColorAttachmentDescriptors( - std::move(color_attachments)); - clip_pipelines_[{}] = std::make_unique( - *context_, std::move(clip_pipeline_descriptor)); } else { return; } diff --git a/impeller/entity/content_context.h b/impeller/entity/content_context.h index 09870f775d402..78d21e5c6340a 100644 --- a/impeller/entity/content_context.h +++ b/impeller/entity/content_context.h @@ -77,6 +77,10 @@ class ContentContext { return GetPipeline(clip_pipelines_, opts); } + std::shared_ptr GetClipRestorePipeline(Options opts) const { + return GetPipeline(clip_restoration_pipelines_, opts); + } + std::shared_ptr GetContext() const; private: @@ -94,6 +98,7 @@ class ContentContext { mutable Variants texture_pipelines_; mutable Variants solid_stroke_pipelines_; mutable Variants clip_pipelines_; + mutable Variants clip_restoration_pipelines_; static void ApplyOptionsToDescriptor(PipelineDescriptor& desc, const Options& options) { diff --git a/impeller/entity/contents.cc b/impeller/entity/contents.cc index 91130debc04e6..79b943721ff9a 100644 --- a/impeller/entity/contents.cc +++ b/impeller/entity/contents.cc @@ -15,6 +15,7 @@ #include "impeller/renderer/sampler_library.h" #include "impeller/renderer/surface.h" #include "impeller/renderer/tessellator.h" +#include "impeller/renderer/vertex_buffer.h" #include "impeller/renderer/vertex_buffer_builder.h" namespace impeller { @@ -385,7 +386,7 @@ bool ClipContents::Render(const ContentContext& renderer, Command cmd; cmd.label = "Clip"; cmd.pipeline = renderer.GetClipPipeline(OptionsFromPass(pass)); - cmd.stencil_reference = entity.GetStencilDepth() + 1u; + cmd.stencil_reference = entity.GetStencilDepth(); cmd.BindVertices( CreateSolidFillVertices(entity.GetPath(), pass.GetTransientsBuffer())); @@ -400,4 +401,46 @@ bool ClipContents::Render(const ContentContext& renderer, return true; } +/******************************************************************************* + ******* ClipRestoreContents + ******************************************************************************/ + +ClipRestoreContents::ClipRestoreContents() = default; + +ClipRestoreContents::~ClipRestoreContents() = default; + +bool ClipRestoreContents::Render(const ContentContext& renderer, + const Entity& entity, + RenderPass& pass) const { + using VS = ClipPipeline::VertexShader; + + Command cmd; + cmd.label = "Clip Restore"; + cmd.pipeline = renderer.GetClipRestorePipeline(OptionsFromPass(pass)); + cmd.stencil_reference = entity.GetStencilDepth(); + + // Create a rect that covers the whole render target. + auto size = pass.GetRenderTargetSize(); + VertexBufferBuilder vtx_builder; + vtx_builder.AddVertices({ + {Point(0.0, 0.0)}, + {Point(size.width, 0.0)}, + {Point(size.width, size.height)}, + {Point(0.0, 0.0)}, + {Point(size.width, size.height)}, + {Point(0.0, size.height)}, + }); + cmd.BindVertices(vtx_builder.CreateVertexBuffer(pass.GetTransientsBuffer())); + + VS::FrameInfo info; + // The color really doesn't matter. + info.color = Color::SkyBlue(); + info.mvp = Matrix::MakeOrthographic(pass.GetRenderTargetSize()); + + VS::BindFrameInfo(cmd, pass.GetTransientsBuffer().EmplaceUniform(info)); + + pass.AddCommand(std::move(cmd)); + return true; +} + } // namespace impeller diff --git a/impeller/entity/contents.h b/impeller/entity/contents.h index 126dd2bf671c2..f80b2375bf952 100644 --- a/impeller/entity/contents.h +++ b/impeller/entity/contents.h @@ -152,4 +152,19 @@ class ClipContents final : public Contents { FML_DISALLOW_COPY_AND_ASSIGN(ClipContents); }; +class ClipRestoreContents final : public Contents { + public: + ClipRestoreContents(); + + ~ClipRestoreContents(); + + // |Contents| + bool Render(const ContentContext& renderer, + const Entity& entity, + RenderPass& pass) const override; + + private: + FML_DISALLOW_COPY_AND_ASSIGN(ClipRestoreContents); +}; + } // namespace impeller diff --git a/impeller/entity/entity_pass.h b/impeller/entity/entity_pass.h index 1e179915cf3da..cff63df7ac68a 100644 --- a/impeller/entity/entity_pass.h +++ b/impeller/entity/entity_pass.h @@ -73,6 +73,7 @@ struct CanvasStackEntry { Matrix xformation; size_t stencil_depth = 0u; bool is_subpass = false; + bool contains_clips = false; }; } // namespace impeller diff --git a/impeller/renderer/pipeline_builder.h b/impeller/renderer/pipeline_builder.h index 9002c803aa5dd..090cd813b79b3 100644 --- a/impeller/renderer/pipeline_builder.h +++ b/impeller/renderer/pipeline_builder.h @@ -8,6 +8,7 @@ #include "flutter/fml/macros.h" #include "impeller/base/base.h" #include "impeller/renderer/context.h" +#include "impeller/renderer/formats.h" #include "impeller/renderer/pipeline_descriptor.h" #include "impeller/renderer/shader_library.h" #include "impeller/renderer/vertex_descriptor.h" @@ -107,7 +108,7 @@ struct PipelineBuilder { // Setup default stencil buffer descriptions. { StencilAttachmentDescriptor stencil0; - stencil0.stencil_compare = CompareFunction::kLessEqual; + stencil0.stencil_compare = CompareFunction::kEqual; desc.SetStencilAttachmentDescriptors(stencil0); desc.SetStencilPixelFormat(PixelFormat::kDefaultStencil); } From 1d99df1c921b9b3cdc3624f642a8124bb6a7e674 Mon Sep 17 00:00:00 2001 From: Brandon DeRosier Date: Thu, 17 Feb 2022 23:44:14 -0800 Subject: [PATCH 290/433] Fix bug introduced in a476b92 which broke a bunch of tests (#15) --- impeller/renderer/command.cc | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/impeller/renderer/command.cc b/impeller/renderer/command.cc index cbdb529c8467b..13bf4d69b61d1 100644 --- a/impeller/renderer/command.cc +++ b/impeller/renderer/command.cc @@ -4,13 +4,15 @@ #include "impeller/renderer/command.h" +#include "impeller/base/validation.h" #include "impeller/renderer/formats.h" #include "impeller/renderer/vertex_descriptor.h" namespace impeller { bool Command::BindVertices(const VertexBuffer& buffer) { - if (index_type == IndexType::kUnknown) { + if (buffer.index_type == IndexType::kUnknown) { + VALIDATION_LOG << "Cannot bind vertex buffer with an unknown index type."; return false; } From cabe96ece9c24ffa5e5bc82017f432ebb11fe0cd Mon Sep 17 00:00:00 2001 From: Jim Graham Date: Fri, 18 Feb 2022 01:30:25 -0800 Subject: [PATCH 291/433] changes for new DlMaskFilter objects (#17) --- .../display_list/display_list_dispatcher.cc | 19 +++++++++++-------- .../display_list/display_list_dispatcher.h | 5 +---- 2 files changed, 12 insertions(+), 12 deletions(-) diff --git a/impeller/display_list/display_list_dispatcher.cc b/impeller/display_list/display_list_dispatcher.cc index 331e002aa5aba..a8243c337e617 100644 --- a/impeller/display_list/display_list_dispatcher.cc +++ b/impeller/display_list/display_list_dispatcher.cc @@ -120,15 +120,18 @@ void DisplayListDispatcher::setPathEffect(sk_sp effect) { } // |flutter::Dispatcher| -void DisplayListDispatcher::setMaskFilter(sk_sp filter) { +void DisplayListDispatcher::setMaskFilter(const flutter::DlMaskFilter* filter) { // Needs https://github.com/flutter/flutter/issues/95434 - UNIMPLEMENTED; -} - -// |flutter::Dispatcher| -void DisplayListDispatcher::setMaskBlurFilter(SkBlurStyle style, - SkScalar sigma) { - UNIMPLEMENTED; + if (filter == nullptr) { + // Reset everything + return; + } + switch (filter->type()) { + case flutter::DlMaskFilter::kBlur: + case flutter::DlMaskFilter::kUnknown: + UNIMPLEMENTED; + break; + } } // |flutter::Dispatcher| diff --git a/impeller/display_list/display_list_dispatcher.h b/impeller/display_list/display_list_dispatcher.h index 79234d418480a..8995a3d803493 100644 --- a/impeller/display_list/display_list_dispatcher.h +++ b/impeller/display_list/display_list_dispatcher.h @@ -63,10 +63,7 @@ class DisplayListDispatcher final : public flutter::Dispatcher { void setPathEffect(sk_sp effect) override; // |flutter::Dispatcher| - void setMaskFilter(sk_sp filter) override; - - // |flutter::Dispatcher| - void setMaskBlurFilter(SkBlurStyle style, SkScalar sigma) override; + void setMaskFilter(const flutter::DlMaskFilter* filter) override; // |flutter::Dispatcher| void setImageFilter(sk_sp filter) override; From b5f4b77cefa9633be253c7ac0afe9ee29203c5d6 Mon Sep 17 00:00:00 2001 From: Brandon DeRosier Date: Fri, 18 Feb 2022 08:38:27 -0800 Subject: [PATCH 292/433] Add baseVertex (#16) --- impeller/renderer/backend/metal/render_pass_mtl.mm | 2 +- impeller/renderer/command.h | 4 ++++ 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/impeller/renderer/backend/metal/render_pass_mtl.mm b/impeller/renderer/backend/metal/render_pass_mtl.mm index 9d7a9e24ffe50..81007c7d6108a 100644 --- a/impeller/renderer/backend/metal/render_pass_mtl.mm +++ b/impeller/renderer/backend/metal/render_pass_mtl.mm @@ -482,7 +482,7 @@ static bool Bind(PassBindingsCache& pass, indexBuffer:mtl_index_buffer indexBufferOffset:command.index_buffer.range.offset instanceCount:1u - baseVertex:0u + baseVertex:command.base_vertex baseInstance:0u]; } return true; diff --git a/impeller/renderer/command.h b/impeller/renderer/command.h index fb73d1b16da63..0e4b1c99ef5ab 100644 --- a/impeller/renderer/command.h +++ b/impeller/renderer/command.h @@ -73,6 +73,10 @@ struct Command { CullMode cull_mode = CullMode::kNone; uint32_t stencil_reference = 0u; //---------------------------------------------------------------------------- + /// The offset used when indexing into the vertex buffer. + /// + uint64_t base_vertex = 0u; + //---------------------------------------------------------------------------- /// The viewport coordinates that the rasterizer linearly maps normalized /// device coordinates to. /// If unset, the viewport is the size of the render target with a zero From 467ea5c407ada335fc3ca322fb2dfaf038fb362f Mon Sep 17 00:00:00 2001 From: Brandon DeRosier Date: Tue, 22 Feb 2022 11:15:33 -0800 Subject: [PATCH 293/433] Fix index size check to respect 16 bit buffers (#19) --- impeller/renderer/backend/metal/render_pass_mtl.mm | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/impeller/renderer/backend/metal/render_pass_mtl.mm b/impeller/renderer/backend/metal/render_pass_mtl.mm index 81007c7d6108a..8335b362331ea 100644 --- a/impeller/renderer/backend/metal/render_pass_mtl.mm +++ b/impeller/renderer/backend/metal/render_pass_mtl.mm @@ -473,7 +473,8 @@ static bool Bind(PassBindingsCache& pass, if (!mtl_index_buffer) { return false; } - FML_DCHECK(command.index_count * sizeof(uint32_t) == + FML_DCHECK(command.index_count * + (command.index_type == IndexType::k16bit ? 2 : 4) == command.index_buffer.range.length); // Returns void. All error checking must be done by this point. [encoder drawIndexedPrimitives:ToMTLPrimitiveType(command.primitive_type) From 4b729ec7a7c9ff2d85f7256e17880357257e1ffa Mon Sep 17 00:00:00 2001 From: Brandon DeRosier Date: Tue, 22 Feb 2022 11:46:16 -0800 Subject: [PATCH 294/433] Add mixed type specializations on algebraic ops in TPoint (#21) Also adds missing RHS operator overloads for TSize. For any algebraic ops involving TPoint and TSize: 1. `TPoint` takes precedent over `TSize`. 2. Floating point types take precedent over integer types. 3. If there's a tie (for example: `TPoint + TPoint`), the LHS takes precedent. --- impeller/geometry/BUILD.gn | 2 + impeller/geometry/geometry_unittests.cc | 157 ++++++++++++++++++++++++ impeller/geometry/point.h | 90 ++++++++++++-- impeller/geometry/type_traits.cc | 11 ++ impeller/geometry/type_traits.h | 20 +++ 5 files changed, 272 insertions(+), 8 deletions(-) create mode 100644 impeller/geometry/type_traits.cc create mode 100644 impeller/geometry/type_traits.h diff --git a/impeller/geometry/BUILD.gn b/impeller/geometry/BUILD.gn index 414b5026f0648..becb1677f99b3 100644 --- a/impeller/geometry/BUILD.gn +++ b/impeller/geometry/BUILD.gn @@ -29,6 +29,8 @@ impeller_component("geometry") { "shear.h", "size.cc", "size.h", + "type_traits.cc", + "type_traits.h", "vector.cc", "vector.h", ] diff --git a/impeller/geometry/geometry_unittests.cc b/impeller/geometry/geometry_unittests.cc index af5cb6c9bbfa7..3c4330c4b5435 100644 --- a/impeller/geometry/geometry_unittests.cc +++ b/impeller/geometry/geometry_unittests.cc @@ -225,6 +225,13 @@ TEST(GeometryTest, CanConvertTTypesExplicitly) { ASSERT_EQ(s2.height, 2u); } + { + Size s1(1.0, 2.0); + Point p1 = static_cast(s1); + ASSERT_EQ(p1.x, 1u); + ASSERT_EQ(p1.y, 2u); + } + { Rect r1(1.0, 2.0, 3.0, 4.0); IRect r2 = static_cast(r1); @@ -235,6 +242,156 @@ TEST(GeometryTest, CanConvertTTypesExplicitly) { } } +TEST(GeometryTest, CanPerformAlgebraicPointOps) { + { + IPoint p1(1, 2); + IPoint p2 = p1 + IPoint(1, 2); + ASSERT_EQ(p2.x, 2u); + ASSERT_EQ(p2.y, 4u); + } + + { + IPoint p1(3, 6); + IPoint p2 = p1 - IPoint(1, 2); + ASSERT_EQ(p2.x, 2u); + ASSERT_EQ(p2.y, 4u); + } + + { + IPoint p1(1, 2); + IPoint p2 = p1 * IPoint(2, 3); + ASSERT_EQ(p2.x, 2u); + ASSERT_EQ(p2.y, 6u); + } + + { + IPoint p1(2, 6); + IPoint p2 = p1 / IPoint(2, 3); + ASSERT_EQ(p2.x, 1u); + ASSERT_EQ(p2.y, 2u); + } +} + +TEST(GeometryTest, PointIntegerCoercesToFloat) { + // Integer on LHS, float on RHS + { + IPoint p1(1, 2); + Point p2 = p1 + Point(1, 2); + ASSERT_FLOAT_EQ(p2.x, 2u); + ASSERT_FLOAT_EQ(p2.y, 4u); + } + + { + IPoint p1(3, 6); + Point p2 = p1 - Point(1, 2); + ASSERT_FLOAT_EQ(p2.x, 2u); + ASSERT_FLOAT_EQ(p2.y, 4u); + } + + { + IPoint p1(1, 2); + Point p2 = p1 * Point(2, 3); + ASSERT_FLOAT_EQ(p2.x, 2u); + ASSERT_FLOAT_EQ(p2.y, 6u); + } + + { + IPoint p1(2, 6); + Point p2 = p1 / Point(2, 3); + ASSERT_FLOAT_EQ(p2.x, 1u); + ASSERT_FLOAT_EQ(p2.y, 2u); + } + + // Float on LHS, integer on RHS + { + Point p1(1, 2); + Point p2 = p1 + IPoint(1, 2); + ASSERT_FLOAT_EQ(p2.x, 2u); + ASSERT_FLOAT_EQ(p2.y, 4u); + } + + { + Point p1(3, 6); + Point p2 = p1 - IPoint(1, 2); + ASSERT_FLOAT_EQ(p2.x, 2u); + ASSERT_FLOAT_EQ(p2.y, 4u); + } + + { + Point p1(1, 2); + Point p2 = p1 * IPoint(2, 3); + ASSERT_FLOAT_EQ(p2.x, 2u); + ASSERT_FLOAT_EQ(p2.y, 6u); + } + + { + Point p1(2, 6); + Point p2 = p1 / IPoint(2, 3); + ASSERT_FLOAT_EQ(p2.x, 1u); + ASSERT_FLOAT_EQ(p2.y, 2u); + } +} + +TEST(GeometryTest, SizeCoercesToPoint) { + // Point on LHS, Size on RHS + { + IPoint p1(1, 2); + IPoint p2 = p1 + ISize(1, 2); + ASSERT_EQ(p2.x, 2u); + ASSERT_EQ(p2.y, 4u); + } + + { + IPoint p1(3, 6); + IPoint p2 = p1 - ISize(1, 2); + ASSERT_EQ(p2.x, 2u); + ASSERT_EQ(p2.y, 4u); + } + + { + IPoint p1(1, 2); + IPoint p2 = p1 * ISize(2, 3); + ASSERT_EQ(p2.x, 2u); + ASSERT_EQ(p2.y, 6u); + } + + { + IPoint p1(2, 6); + IPoint p2 = p1 / ISize(2, 3); + ASSERT_EQ(p2.x, 1u); + ASSERT_EQ(p2.y, 2u); + } + + // Size on LHS, Point on RHS + { + ISize p1(1, 2); + IPoint p2 = p1 + IPoint(1, 2); + ASSERT_EQ(p2.x, 2u); + ASSERT_EQ(p2.y, 4u); + } + + { + ISize p1(3, 6); + IPoint p2 = p1 - IPoint(1, 2); + ASSERT_EQ(p2.x, 2u); + ASSERT_EQ(p2.y, 4u); + } + + { + ISize p1(1, 2); + IPoint p2 = p1 * IPoint(2, 3); + ASSERT_EQ(p2.x, 2u); + ASSERT_EQ(p2.y, 6u); + } + + { + ISize p1(2, 6); + IPoint p2 = p1 / IPoint(2, 3); + ASSERT_EQ(p2.x, 1u); + ASSERT_EQ(p2.y, 2u); + } +} + TEST(GeometryTest, CanConvertBetweenDegressAndRadians) { { auto deg = Degrees{90.0}; diff --git a/impeller/geometry/point.h b/impeller/geometry/point.h index 0d497a70f934c..8d183b7771e76 100644 --- a/impeller/geometry/point.h +++ b/impeller/geometry/point.h @@ -11,6 +11,7 @@ #include "impeller/geometry/scalar.h" #include "impeller/geometry/size.h" +#include "impeller/geometry/type_traits.h" namespace impeller { @@ -27,6 +28,11 @@ struct TPoint { explicit constexpr TPoint(const TPoint& other) : TPoint(static_cast(other.x), static_cast(other.y)) {} + template + explicit constexpr TPoint(const TSize& other) + : TPoint(static_cast(other.width), + static_cast(other.height)) {} + constexpr TPoint(Type x, Type y) : x(x), y(y) {} static constexpr TPoint MakeXY(Type x, Type y) { return {x, y}; } @@ -45,16 +51,18 @@ struct TPoint { return {x + p.x, y + p.y}; } - constexpr TPoint operator+(const TSize& s) const { - return {x + s.width, y + s.height}; + template + constexpr TPoint operator+(const TSize& s) const { + return {x + static_cast(s.width), y + static_cast(s.height)}; } constexpr TPoint operator-(const TPoint& p) const { return {x - p.x, y - p.y}; } - constexpr TPoint operator-(const TSize& s) const { - return {x - s.width, y - s.height}; + template + constexpr TPoint operator-(const TSize& s) const { + return {x - static_cast(s.width), y - static_cast(s.height)}; } constexpr TPoint operator*(Scalar scale) const { @@ -65,8 +73,9 @@ struct TPoint { return {x * p.x, y * p.y}; } - constexpr TPoint operator*(const TSize& s) const { - return {x * s.width, y * s.height}; + template + constexpr TPoint operator*(const TSize& s) const { + return {x * static_cast(s.width), y * static_cast(s.height)}; } constexpr TPoint operator/(Scalar d) const { return {x / d, y / d}; } @@ -75,8 +84,9 @@ struct TPoint { return {x / p.x, y / p.y}; } - constexpr TPoint operator/(const TSize& s) const { - return {x / s.width, y / s.height}; + template + constexpr TPoint operator/(const TSize& s) const { + return {x / static_cast(s.width), y / static_cast(s.height)}; } constexpr Type GetDistanceSquared(const TPoint& p) const { @@ -112,6 +122,70 @@ struct TPoint { constexpr bool IsZero() const { return x == 0 && y == 0; } }; +// Specializations for mixed (float & integer) algebraic operations. + +template > +constexpr TPoint operator+(const TPoint& p1, const TPoint& p2) { + return {p1.x + static_cast(p2.x), p1.y + static_cast(p2.y)}; +} + +template > +constexpr TPoint operator+(const TPoint& p1, const TPoint& p2) { + return p2 + p1; +} + +template > +constexpr TPoint operator-(const TPoint& p1, const TPoint& p2) { + return {p1.x - static_cast(p2.x), p1.y - static_cast(p2.y)}; +} + +template > +constexpr TPoint operator-(const TPoint& p1, const TPoint& p2) { + return {static_cast(p1.x) - p2.x, static_cast(p1.y) - p2.y}; +} + +template > +constexpr TPoint operator*(const TPoint& p1, const TPoint& p2) { + return {p1.x * static_cast(p2.x), p1.y * static_cast(p2.y)}; +} + +template > +constexpr TPoint operator*(const TPoint& p1, const TPoint& p2) { + return p2 * p1; +} + +template > +constexpr TPoint operator/(const TPoint& p1, const TPoint& p2) { + return {p1.x / static_cast(p2.x), p1.y / static_cast(p2.y)}; +} + +template > +constexpr TPoint operator/(const TPoint& p1, const TPoint& p2) { + return {static_cast(p1.x) / p2.x, static_cast(p1.y) / p2.y}; +} + +// RHS algebraic operations with TSize. + +template +constexpr TPoint operator+(const TSize& s, const TPoint& p) { + return p + s; +} + +template +constexpr TPoint operator-(const TSize& s, const TPoint& p) { + return {static_cast(s.width) - p.x, static_cast(s.height) - p.y}; +} + +template +constexpr TPoint operator*(const TSize& s, const TPoint& p) { + return p * s; +} + +template +constexpr TPoint operator/(const TSize& s, const TPoint& p) { + return {static_cast(s.width) / p.x, static_cast(s.height) / p.y}; +} + using Point = TPoint; using IPoint = TPoint; diff --git a/impeller/geometry/type_traits.cc b/impeller/geometry/type_traits.cc new file mode 100644 index 0000000000000..84aea9a9330b0 --- /dev/null +++ b/impeller/geometry/type_traits.cc @@ -0,0 +1,11 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "type_traits.h" + +namespace impeller { + +// + +} // namespace impeller diff --git a/impeller/geometry/type_traits.h b/impeller/geometry/type_traits.h new file mode 100644 index 0000000000000..4e884d16c1eca --- /dev/null +++ b/impeller/geometry/type_traits.h @@ -0,0 +1,20 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#pragma once + +#include + +namespace impeller { + +template && + std::is_integral_v>> +struct MixedOp_ : public std::true_type {}; + +template +using MixedOp = typename MixedOp_::type; + +} // namespace impeller From dbe06cd348451aa97004263f484e1d61f057e571 Mon Sep 17 00:00:00 2001 From: Brandon DeRosier Date: Tue, 22 Feb 2022 12:41:09 -0800 Subject: [PATCH 295/433] Respect TextureContents/Canvas::DrawImageRect source rect (#22) --- impeller/aiks/aiks_unittests.cc | 2 +- impeller/entity/contents.cc | 8 ++++++-- 2 files changed, 7 insertions(+), 3 deletions(-) diff --git a/impeller/aiks/aiks_unittests.cc b/impeller/aiks/aiks_unittests.cc index 0851fd7e0de82..ca96b494c4a86 100644 --- a/impeller/aiks/aiks_unittests.cc +++ b/impeller/aiks/aiks_unittests.cc @@ -59,7 +59,7 @@ TEST_F(AiksTest, CanRenderImage) { ASSERT_TRUE(OpenPlaygroundHere(canvas.EndRecordingAsPicture())); } -TEST_F(AiksTest, DISABLED_CanRenderImageRect) { +TEST_F(AiksTest, CanRenderImageRect) { Canvas canvas; Paint paint; auto image = std::make_shared(CreateTextureForFixture("kalimba.jpg")); diff --git a/impeller/entity/contents.cc b/impeller/entity/contents.cc index 79b943721ff9a..f25a50d98c9b1 100644 --- a/impeller/entity/contents.cc +++ b/impeller/entity/contents.cc @@ -229,11 +229,15 @@ bool TextureContents::Render(const ContentContext& renderer, const auto tess_result = Tessellator{entity.GetPath().GetFillType()}.Tessellate( entity.GetPath().CreatePolyline(), - [&vertex_builder, &coverage_rect](Point vtx) { + [this, &vertex_builder, &coverage_rect, &texture_size](Point vtx) { VS::PerVertexData data; data.vertices = vtx; + auto coverage_coords = + (vtx - coverage_rect->origin) / coverage_rect->size; data.texture_coords = - ((vtx - coverage_rect->origin) / coverage_rect->size); + (source_rect_.origin + + source_rect_.size * coverage_coords) / + texture_size; vertex_builder.AppendVertex(data); }); if (!tess_result) { From f80bb19fa4df0a9b655f3c32dae50777c0589158 Mon Sep 17 00:00:00 2001 From: Brandon DeRosier Date: Tue, 22 Feb 2022 23:48:49 -0800 Subject: [PATCH 296/433] Add an ImGui backend targetting Impeller to the playground (#20) --- impeller/playground/BUILD.gn | 2 + impeller/playground/imgui/BUILD.gn | 25 ++ .../playground/imgui/imgui_impl_impeller.cc | 225 ++++++++++++++++++ .../playground/imgui/imgui_impl_impeller.h | 19 ++ impeller/playground/imgui/imgui_raster.frag | 10 + impeller/playground/imgui/imgui_raster.vert | 17 ++ impeller/playground/playground.mm | 29 ++- 7 files changed, 326 insertions(+), 1 deletion(-) create mode 100644 impeller/playground/imgui/BUILD.gn create mode 100644 impeller/playground/imgui/imgui_impl_impeller.cc create mode 100644 impeller/playground/imgui/imgui_impl_impeller.h create mode 100644 impeller/playground/imgui/imgui_raster.frag create mode 100644 impeller/playground/imgui/imgui_raster.vert diff --git a/impeller/playground/BUILD.gn b/impeller/playground/BUILD.gn index e1b38dfde4caa..b01803ed86281 100644 --- a/impeller/playground/BUILD.gn +++ b/impeller/playground/BUILD.gn @@ -17,8 +17,10 @@ impeller_component("playground") { "../entity:entity_shaders", "../fixtures:shader_fixtures", "../renderer", + "imgui:imgui_impeller_backend", "//flutter/testing", "//third_party/glfw", + "//third_party/imgui:imgui_glfw", ] public_configs = [ ":playground_config" ] diff --git a/impeller/playground/imgui/BUILD.gn b/impeller/playground/imgui/BUILD.gn new file mode 100644 index 0000000000000..9f1e0d3b89d0f --- /dev/null +++ b/impeller/playground/imgui/BUILD.gn @@ -0,0 +1,25 @@ +import("//flutter/impeller/tools/impeller.gni") + +impeller_shaders("imgui_shaders") { + name = "imgui_shaders" + shaders = [ + "imgui_raster.vert", + "imgui_raster.frag", + ] +} + +source_set("imgui_impeller_backend") { + testonly = true + + public_deps = [ + ":imgui_shaders", + "//third_party/imgui", + ] + + deps = [ "//flutter/impeller/renderer" ] + + sources = [ + "imgui_impl_impeller.cc", + "imgui_impl_impeller.h", + ] +} diff --git a/impeller/playground/imgui/imgui_impl_impeller.cc b/impeller/playground/imgui/imgui_impl_impeller.cc new file mode 100644 index 0000000000000..bda00edc5c22e --- /dev/null +++ b/impeller/playground/imgui/imgui_impl_impeller.cc @@ -0,0 +1,225 @@ +#include "imgui_impl_impeller.h" + +#include +#include +#include +#include + +#include "imgui_raster.frag.h" +#include "imgui_raster.vert.h" +#include "third_party/imgui/imgui.h" + +#include "impeller/geometry/matrix.h" +#include "impeller/geometry/point.h" +#include "impeller/geometry/rect.h" +#include "impeller/geometry/size.h" +#include "impeller/renderer/allocator.h" +#include "impeller/renderer/command.h" +#include "impeller/renderer/context.h" +#include "impeller/renderer/formats.h" +#include "impeller/renderer/pipeline_builder.h" +#include "impeller/renderer/pipeline_library.h" +#include "impeller/renderer/range.h" +#include "impeller/renderer/render_pass.h" +#include "impeller/renderer/sampler.h" +#include "impeller/renderer/sampler_library.h" +#include "impeller/renderer/texture.h" +#include "impeller/renderer/texture_descriptor.h" +#include "impeller/renderer/vertex_buffer.h" + +struct ImGui_ImplImpeller_Data { + std::shared_ptr context; + std::shared_ptr font_texture; + std::shared_ptr pipeline; + std::shared_ptr sampler; +}; + +static ImGui_ImplImpeller_Data* ImGui_ImplImpeller_GetBackendData() { + return ImGui::GetCurrentContext() + ? static_cast( + ImGui::GetIO().BackendRendererUserData) + : nullptr; +} + +bool ImGui_ImplImpeller_Init(std::shared_ptr context) { + ImGuiIO& io = ImGui::GetIO(); + IM_ASSERT(io.BackendRendererUserData == nullptr && + "Already initialized a renderer backend!"); + + // Setup backend capabilities flags + auto* bd = new ImGui_ImplImpeller_Data(); + io.BackendRendererUserData = reinterpret_cast(bd); + io.BackendRendererName = "imgui_impl_impeller"; + io.BackendFlags |= + ImGuiBackendFlags_RendererHasVtxOffset; // We can honor the + // ImDrawCmd::VtxOffset field, + // allowing for large meshes. + + bd->context = context; + + // Generate/upload the font atlas. + { + unsigned char* pixels; + int width, height; + io.Fonts->GetTexDataAsRGBA32(&pixels, &width, &height); + + auto texture_descriptor = impeller::TextureDescriptor{}; + texture_descriptor.format = impeller::PixelFormat::kR8G8B8A8UNormInt; + texture_descriptor.size = {width, height}; + texture_descriptor.mip_count = 1u; + + bd->font_texture = context->GetPermanentsAllocator()->CreateTexture( + impeller::StorageMode::kHostVisible, texture_descriptor); + IM_ASSERT(bd->font_texture != nullptr && + "Could not allocate ImGui font texture."); + + bool uploaded = bd->font_texture->SetContents(pixels, width * height * 4); + IM_ASSERT(uploaded && + "Could not upload ImGui font texture to device memory."); + } + + // Build the raster pipeline. + { + auto desc = impeller::PipelineBuilder:: + MakeDefaultPipelineDescriptor(*context); + desc->SetSampleCount(impeller::SampleCount::kCount4); + bd->pipeline = + context->GetPipelineLibrary()->GetRenderPipeline(std::move(desc)).get(); + IM_ASSERT(bd->pipeline != nullptr && "Could not create ImGui pipeline."); + + bd->sampler = context->GetSamplerLibrary()->GetSampler({}); + IM_ASSERT(bd->pipeline != nullptr && "Could not create ImGui sampler."); + } + + return true; +} + +void ImGui_ImplImpeller_Shutdown() { + auto* bd = ImGui_ImplImpeller_GetBackendData(); + IM_ASSERT(bd != nullptr && + "No renderer backend to shutdown, or already shutdown?"); + delete bd; +} + +void ImGui_ImplImpeller_RenderDrawData(ImDrawData* draw_data, + impeller::RenderPass& render_pass) { + if (draw_data->CmdListsCount == 0) { + return; // Nothing to render. + } + + using VS = impeller::ImguiRasterVertexShader; + using FS = impeller::ImguiRasterFragmentShader; + + auto* bd = ImGui_ImplImpeller_GetBackendData(); + IM_ASSERT(bd != nullptr && "Did you call ImGui_ImplImpeller_Init()?"); + + size_t total_vtx_bytes = draw_data->TotalVtxCount * sizeof(ImDrawVert); + size_t total_idx_bytes = draw_data->TotalIdxCount * sizeof(ImDrawIdx); + if (!total_vtx_bytes || !total_idx_bytes) { + return; // Nothing to render. + } + + // Allocate buffer for vertices + indices. + auto buffer = bd->context->GetTransientsAllocator()->CreateBuffer( + impeller::StorageMode::kHostVisible, total_vtx_bytes + total_idx_bytes); + buffer->SetLabel(impeller::SPrintF("ImGui vertex+index buffer")); + + VS::UniformBuffer uniforms; + uniforms.mvp = impeller::Matrix::MakeOrthographic( + impeller::Size(draw_data->DisplaySize.x, draw_data->DisplaySize.y)); + uniforms.mvp = uniforms.mvp.Translate( + -impeller::Vector3(draw_data->DisplayPos.x, draw_data->DisplayPos.y)); + + size_t vertex_buffer_offset = 0; + size_t index_buffer_offset = total_vtx_bytes; + + for (int draw_list_i = 0; draw_list_i < draw_data->CmdListsCount; + draw_list_i++) { + const ImDrawList* cmd_list = draw_data->CmdLists[draw_list_i]; + + auto draw_list_vtx_bytes = + static_cast(cmd_list->VtxBuffer.size_in_bytes()); + auto draw_list_idx_bytes = + static_cast(cmd_list->IdxBuffer.size_in_bytes()); + + if (!buffer->CopyHostBuffer( + reinterpret_cast(cmd_list->VtxBuffer.Data), + impeller::Range{0, draw_list_vtx_bytes}, vertex_buffer_offset)) { + IM_ASSERT(false && "Could not copy vertices to buffer."); + } + if (!buffer->CopyHostBuffer( + reinterpret_cast(cmd_list->IdxBuffer.Data), + impeller::Range{0, draw_list_idx_bytes}, index_buffer_offset)) { + IM_ASSERT(false && "Could not copy indices to buffer."); + } + + auto viewport = impeller::Viewport{ + .rect = + impeller::Rect(draw_data->DisplayPos.x, draw_data->DisplayPos.y, + draw_data->DisplaySize.x, draw_data->DisplaySize.y)}; + + for (int cmd_i = 0; cmd_i < cmd_list->CmdBuffer.Size; cmd_i++) { + const ImDrawCmd* pcmd = &cmd_list->CmdBuffer[cmd_i]; + + if (pcmd->UserCallback) { + pcmd->UserCallback(cmd_list, pcmd); + } else { + // Project scissor/clipping rectangles into framebuffer space. + impeller::IPoint clip_min(pcmd->ClipRect.x - draw_data->DisplayPos.x, + pcmd->ClipRect.y - draw_data->DisplayPos.y); + impeller::IPoint clip_max(pcmd->ClipRect.z - draw_data->DisplayPos.x, + pcmd->ClipRect.w - draw_data->DisplayPos.y); + // Ensure the scissor never goes out of bounds. + clip_min.x = std::clamp( + clip_min.x, 0ll, + static_cast(draw_data->DisplaySize.x)); + clip_min.y = std::clamp( + clip_min.y, 0ll, + static_cast(draw_data->DisplaySize.y)); + if (clip_max.x <= clip_min.x || clip_max.y <= clip_min.y) { + continue; // Nothing to render. + } + + impeller::Command cmd; + cmd.label = impeller::SPrintF("ImGui draw list %d (command %d)", + draw_list_i, cmd_i); + + cmd.viewport = viewport; + cmd.scissor = impeller::IRect::MakeLTRB( + std::max(0ll, clip_min.x), std::max(0ll, clip_min.y), + std::min(render_pass.GetRenderTargetSize().width, clip_max.x), + std::min(render_pass.GetRenderTargetSize().height, clip_max.y)); + + cmd.winding = impeller::WindingOrder::kClockwise; + cmd.pipeline = bd->pipeline; + VS::BindUniformBuffer( + cmd, render_pass.GetTransientsBuffer().EmplaceUniform(uniforms)); + FS::BindTex(cmd, bd->font_texture, bd->sampler); + + size_t vb_start = + vertex_buffer_offset + pcmd->VtxOffset * sizeof(ImDrawVert); + + impeller::VertexBuffer vertex_buffer; + vertex_buffer.vertex_buffer = { + .buffer = buffer, + .range = impeller::Range(vb_start, draw_list_vtx_bytes - vb_start)}; + vertex_buffer.index_buffer = { + .buffer = buffer, + .range = impeller::Range( + index_buffer_offset + pcmd->IdxOffset * sizeof(ImDrawIdx), + pcmd->ElemCount * sizeof(ImDrawIdx))}; + vertex_buffer.index_count = pcmd->ElemCount; + vertex_buffer.index_type = impeller::IndexType::k16bit; + cmd.BindVertices(vertex_buffer); + cmd.base_vertex = pcmd->VtxOffset; + cmd.primitive_type = impeller::PrimitiveType::kTriangle; + + render_pass.AddCommand(std::move(cmd)); + } + } + + vertex_buffer_offset += draw_list_vtx_bytes; + index_buffer_offset += draw_list_idx_bytes; + } +} diff --git a/impeller/playground/imgui/imgui_impl_impeller.h b/impeller/playground/imgui/imgui_impl_impeller.h new file mode 100644 index 0000000000000..ffdaaa072d5a2 --- /dev/null +++ b/impeller/playground/imgui/imgui_impl_impeller.h @@ -0,0 +1,19 @@ +#pragma once + +#include + +#include "third_party/imgui/imgui.h" + +namespace impeller { +class Context; +class RenderPass; +} // namespace impeller + +IMGUI_IMPL_API bool ImGui_ImplImpeller_Init( + std::shared_ptr context); + +IMGUI_IMPL_API void ImGui_ImplImpeller_Shutdown(); + +IMGUI_IMPL_API void ImGui_ImplImpeller_RenderDrawData( + ImDrawData* draw_data, + impeller::RenderPass& renderpass); diff --git a/impeller/playground/imgui/imgui_raster.frag b/impeller/playground/imgui/imgui_raster.frag new file mode 100644 index 0000000000000..890a784245885 --- /dev/null +++ b/impeller/playground/imgui/imgui_raster.frag @@ -0,0 +1,10 @@ +in vec2 frag_texture_coordinates; +in vec4 frag_vertex_color; + +out vec4 frag_color; + +uniform sampler2D tex; + +void main() { + frag_color = frag_vertex_color * texture(tex, frag_texture_coordinates.st); +} diff --git a/impeller/playground/imgui/imgui_raster.vert b/impeller/playground/imgui/imgui_raster.vert new file mode 100644 index 0000000000000..391c14eb3d005 --- /dev/null +++ b/impeller/playground/imgui/imgui_raster.vert @@ -0,0 +1,17 @@ +uniform UniformBuffer { + mat4 mvp; +} +uniforms; + +in vec2 vertex_position; +in vec2 texture_coordinates; +in uint vertex_color; + +out vec2 frag_texture_coordinates; +out vec4 frag_vertex_color; + +void main() { + gl_Position = uniforms.mvp * vec4(vertex_position.xy, 0.0, 1.0); + frag_texture_coordinates = texture_coordinates; + frag_vertex_color = unpackUnorm4x8(vertex_color); +} diff --git a/impeller/playground/playground.mm b/impeller/playground/playground.mm index df4867ecaa423..183f91aaa48e9 100644 --- a/impeller/playground/playground.mm +++ b/impeller/playground/playground.mm @@ -10,6 +10,8 @@ #include "flutter/testing/testing.h" #include "impeller/base/validation.h" #include "impeller/image/compressed_image.h" +#include "impeller/playground/imgui/imgui_impl_impeller.h" +#include "impeller/playground/imgui/imgui_shaders.h" #include "impeller/playground/playground.h" #include "impeller/renderer/allocator.h" #include "impeller/renderer/backend/metal/context_mtl.h" @@ -20,6 +22,8 @@ #include "impeller/renderer/formats.h" #include "impeller/renderer/render_pass.h" #include "impeller/renderer/renderer.h" +#include "third_party/imgui/backends/imgui_impl_glfw.h" +#include "third_party/imgui/imgui.h" #define GLFW_INCLUDE_NONE #import "third_party/glfw/include/GLFW/glfw3.h" @@ -38,6 +42,8 @@ impeller_entity_shaders_length), std::make_shared(impeller_shader_fixtures_data, impeller_shader_fixtures_length), + std::make_shared(impeller_imgui_shaders_data, + impeller_imgui_shaders_length), }; } @@ -93,6 +99,13 @@ static void PlaygroundKeyCallback(GLFWwindow* window, return false; } + IMGUI_CHECKVERSION(); + ImGui::CreateContext(); + fml::ScopedCleanupClosure destroy_imgui_context( + []() { ImGui::DestroyContext(); }); + ImGui::StyleColorsDark(); + ImGui::GetIO().IniFilename = nullptr; + if (::glfwInit() != GLFW_TRUE) { return false; } @@ -129,6 +142,13 @@ static void PlaygroundKeyCallback(GLFWwindow* window, fml::ScopedCleanupClosure close_window( [window]() { ::glfwDestroyWindow(window); }); + ImGui_ImplGlfw_InitForOther(window, true); + fml::ScopedCleanupClosure shutdown_imgui([]() { ImGui_ImplGlfw_Shutdown(); }); + + ImGui_ImplImpeller_Init(renderer_.GetContext()); + fml::ScopedCleanupClosure shutdown_imgui_impeller( + []() { ImGui_ImplImpeller_Shutdown(); }); + NSWindow* cocoa_window = ::glfwGetCocoaWindow(window); CAMetalLayer* layer = [CAMetalLayer layer]; layer.device = ContextMTL::Cast(*renderer_.GetContext()).GetMTLDevice(); @@ -144,6 +164,8 @@ static void PlaygroundKeyCallback(GLFWwindow* window, return true; } + ImGui_ImplGlfw_NewFrame(); + const auto layer_size = layer.bounds.size; const auto layer_scale = layer.contentsScale; layer.drawableSize = CGSizeMake(layer_size.width * layer_scale, @@ -151,7 +173,12 @@ static void PlaygroundKeyCallback(GLFWwindow* window, Renderer::RenderCallback wrapped_callback = [render_callback](auto& pass) { pass.SetLabel("Playground Main Render Pass"); - return render_callback(pass); + + ImGui::NewFrame(); + bool result = render_callback(pass); + ImGui::Render(); + ImGui_ImplImpeller_RenderDrawData(ImGui::GetDrawData(), pass); + return result; }; if (!renderer_.Render(SurfaceMTL::WrapCurrentMetalLayerDrawable( From 485ee1fafc9b0058bc26a8022f9f2eeea90fa25f Mon Sep 17 00:00:00 2001 From: Brandon DeRosier Date: Wed, 23 Feb 2022 15:26:05 -0800 Subject: [PATCH 297/433] Fix prod build (#23) --- impeller/playground/imgui/imgui_impl_impeller.cc | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/impeller/playground/imgui/imgui_impl_impeller.cc b/impeller/playground/imgui/imgui_impl_impeller.cc index bda00edc5c22e..0d898b151a22b 100644 --- a/impeller/playground/imgui/imgui_impl_impeller.cc +++ b/impeller/playground/imgui/imgui_impl_impeller.cc @@ -73,7 +73,8 @@ bool ImGui_ImplImpeller_Init(std::shared_ptr context) { IM_ASSERT(bd->font_texture != nullptr && "Could not allocate ImGui font texture."); - bool uploaded = bd->font_texture->SetContents(pixels, width * height * 4); + [[maybe_unused]] bool uploaded = bd->font_texture->SetContents( + pixels, texture_descriptor.GetSizeOfBaseMipLevel()); IM_ASSERT(uploaded && "Could not upload ImGui font texture to device memory."); } From 42b620892774c6e7abd66aee3a2239f46d8d56c6 Mon Sep 17 00:00:00 2001 From: Dan Field Date: Wed, 23 Feb 2022 16:19:45 -0800 Subject: [PATCH 298/433] Bad cubic tests (#24) --- impeller/entity/entity_unittests.cc | 251 ++++++++++++++++++++++++++++ 1 file changed, 251 insertions(+) diff --git a/impeller/entity/entity_unittests.cc b/impeller/entity/entity_unittests.cc index ed83995dc7031..8a678e7475d27 100644 --- a/impeller/entity/entity_unittests.cc +++ b/impeller/entity/entity_unittests.cc @@ -25,5 +25,256 @@ TEST_F(EntityTest, CanDrawRect) { ASSERT_TRUE(OpenPlaygroundHere(entity)); } +TEST_F(EntityTest, DISABLED_BadCubicCurveTest) { + // Compare with https://fiddle.skia.org/c/b3625f26122c9de7afe7794fcf25ead3 + Path path = + PathBuilder{} + .MoveTo({237.164, 125.003}) + .CubicCurveTo({236.709, 125.184}, {236.262, 125.358}, + {235.81, 125.538}) + .CubicCurveTo({235.413, 125.68}, {234.994, 125.832}, + {234.592, 125.977}) + .CubicCurveTo({234.592, 125.977}, {234.591, 125.977}, + {234.59, 125.977}) + .CubicCurveTo({222.206, 130.435}, {207.708, 135.753}, + {192.381, 141.429}) + .CubicCurveTo({162.77, 151.336}, {122.17, 156.894}, {84.1123, 160}) + .Close() + .TakePath(); + Entity entity; + entity.SetPath(path); + entity.SetContents(SolidColorContents::Make(Color::Red())); + ASSERT_TRUE(OpenPlaygroundHere(entity)); + +} + +TEST_F(EntityTest, DISABLED_BadCubicCurveAndOverlapTest) { + // Compare with https://fiddle.skia.org/c/7a05a3e186c65a8dfb732f68020aae06 + Path path = + PathBuilder{} + .MoveTo({359.934, 96.6335}) + .CubicCurveTo({358.189, 96.7055}, {356.436, 96.7908}, + {354.673, 96.8895}) + .CubicCurveTo({354.571, 96.8953}, {354.469, 96.9016}, + {354.367, 96.9075}) + .CubicCurveTo({352.672, 97.0038}, {350.969, 97.113}, + {349.259, 97.2355}) + .CubicCurveTo({349.048, 97.2506}, {348.836, 97.2678}, + {348.625, 97.2834}) + .CubicCurveTo({347.019, 97.4014}, {345.407, 97.5299}, + {343.789, 97.6722}) + .CubicCurveTo({343.428, 97.704}, {343.065, 97.7402}, + {342.703, 97.7734}) + .CubicCurveTo({341.221, 97.9086}, {339.736, 98.0505}, + {338.246, 98.207}) + .CubicCurveTo({337.702, 98.2642}, {337.156, 98.3292}, + {336.612, 98.3894}) + .CubicCurveTo({335.284, 98.5356}, {333.956, 98.6837}, + {332.623, 98.8476}) + .CubicCurveTo({332.495, 98.8635}, {332.366, 98.8818}, + {332.237, 98.8982}) + .LineTo({332.237, 102.601}) + .LineTo({321.778, 102.601}) + .LineTo({321.778, 100.382}) + .CubicCurveTo({321.572, 100.413}, {321.367, 100.442}, + {321.161, 100.476}) + .CubicCurveTo({319.22, 100.79}, {317.277, 101.123}, + {315.332, 101.479}) + .CubicCurveTo({315.322, 101.481}, {315.311, 101.482}, + {315.301, 101.484}) + .LineTo({310.017, 105.94}) + .LineTo({309.779, 105.427}) + .LineTo({314.403, 101.651}) + .CubicCurveTo({314.391, 101.653}, {314.379, 101.656}, + {314.368, 101.658}) + .CubicCurveTo({312.528, 102.001}, {310.687, 102.366}, + {308.846, 102.748}) + .CubicCurveTo({307.85, 102.955}, {306.855, 103.182}, {305.859, 103.4}) + .CubicCurveTo({305.048, 103.579}, {304.236, 103.75}, + {303.425, 103.936}) + .LineTo({299.105, 107.578}) + .LineTo({298.867, 107.065}) + .LineTo({302.394, 104.185}) + .LineTo({302.412, 104.171}) + .CubicCurveTo({301.388, 104.409}, {300.366, 104.67}, + {299.344, 104.921}) + .CubicCurveTo({298.618, 105.1}, {297.89, 105.269}, {297.165, 105.455}) + .CubicCurveTo({295.262, 105.94}, {293.36, 106.445}, + {291.462, 106.979}) + .CubicCurveTo({291.132, 107.072}, {290.802, 107.163}, + {290.471, 107.257}) + .CubicCurveTo({289.463, 107.544}, {288.455, 107.839}, + {287.449, 108.139}) + .CubicCurveTo({286.476, 108.431}, {285.506, 108.73}, + {284.536, 109.035}) + .CubicCurveTo({283.674, 109.304}, {282.812, 109.579}, + {281.952, 109.859}) + .CubicCurveTo({281.177, 110.112}, {280.406, 110.377}, + {279.633, 110.638}) + .CubicCurveTo({278.458, 111.037}, {277.256, 111.449}, + {276.803, 111.607}) + .CubicCurveTo({276.76, 111.622}, {276.716, 111.637}, + {276.672, 111.653}) + .CubicCurveTo({275.017, 112.239}, {273.365, 112.836}, + {271.721, 113.463}) + .LineTo({271.717, 113.449}) + .CubicCurveTo({271.496, 113.496}, {271.238, 113.559}, + {270.963, 113.628}) + .CubicCurveTo({270.893, 113.645}, {270.822, 113.663}, + {270.748, 113.682}) + .CubicCurveTo({270.468, 113.755}, {270.169, 113.834}, + {269.839, 113.926}) + .CubicCurveTo({269.789, 113.94}, {269.732, 113.957}, + {269.681, 113.972}) + .CubicCurveTo({269.391, 114.053}, {269.081, 114.143}, + {268.756, 114.239}) + .CubicCurveTo({268.628, 114.276}, {268.5, 114.314}, + {268.367, 114.354}) + .CubicCurveTo({268.172, 114.412}, {267.959, 114.478}, + {267.752, 114.54}) + .CubicCurveTo({263.349, 115.964}, {258.058, 117.695}, + {253.564, 119.252}) + .CubicCurveTo({253.556, 119.255}, {253.547, 119.258}, + {253.538, 119.261}) + .CubicCurveTo({251.844, 119.849}, {250.056, 120.474}, + {248.189, 121.131}) + .CubicCurveTo({248, 121.197}, {247.812, 121.264}, {247.621, 121.331}) + .CubicCurveTo({247.079, 121.522}, {246.531, 121.715}, + {245.975, 121.912}) + .CubicCurveTo({245.554, 122.06}, {245.126, 122.212}, + {244.698, 122.364}) + .CubicCurveTo({244.071, 122.586}, {243.437, 122.811}, + {242.794, 123.04}) + .CubicCurveTo({242.189, 123.255}, {241.58, 123.472}, + {240.961, 123.693}) + .CubicCurveTo({240.659, 123.801}, {240.357, 123.909}, + {240.052, 124.018}) + .CubicCurveTo({239.12, 124.351}, {238.18, 124.687}, {237.22, 125.032}) + .LineTo({237.164, 125.003}) + .CubicCurveTo({236.709, 125.184}, {236.262, 125.358}, + {235.81, 125.538}) + .CubicCurveTo({235.413, 125.68}, {234.994, 125.832}, + {234.592, 125.977}) + .CubicCurveTo({234.592, 125.977}, {234.591, 125.977}, + {234.59, 125.977}) + .CubicCurveTo({222.206, 130.435}, {207.708, 135.753}, + {192.381, 141.429}) + .CubicCurveTo({162.77, 151.336}, {122.17, 156.894}, {84.1123, 160}) + .LineTo({360, 160}) + .LineTo({360, 119.256}) + .LineTo({360, 106.332}) + .LineTo({360, 96.6307}) + .CubicCurveTo({359.978, 96.6317}, {359.956, 96.6326}, + {359.934, 96.6335}) + .Close() + .MoveTo({337.336, 124.143}) + .CubicCurveTo({337.274, 122.359}, {338.903, 121.511}, + {338.903, 121.511}) + .CubicCurveTo({338.903, 121.511}, {338.96, 123.303}, + {337.336, 124.143}) + .Close() + .MoveTo({340.082, 121.849}) + .CubicCurveTo({340.074, 121.917}, {340.062, 121.992}, + {340.046, 122.075}) + .CubicCurveTo({340.039, 122.109}, {340.031, 122.142}, + {340.023, 122.177}) + .CubicCurveTo({340.005, 122.26}, {339.98, 122.346}, + {339.952, 122.437}) + .CubicCurveTo({339.941, 122.473}, {339.931, 122.507}, + {339.918, 122.544}) + .CubicCurveTo({339.873, 122.672}, {339.819, 122.804}, + {339.75, 122.938}) + .CubicCurveTo({339.747, 122.944}, {339.743, 122.949}, + {339.74, 122.955}) + .CubicCurveTo({339.674, 123.08}, {339.593, 123.205}, + {339.501, 123.328}) + .CubicCurveTo({339.473, 123.366}, {339.441, 123.401}, + {339.41, 123.438}) + .CubicCurveTo({339.332, 123.534}, {339.243, 123.625}, + {339.145, 123.714}) + .CubicCurveTo({339.105, 123.75}, {339.068, 123.786}, + {339.025, 123.821}) + .CubicCurveTo({338.881, 123.937}, {338.724, 124.048}, + {338.539, 124.143}) + .CubicCurveTo({338.532, 123.959}, {338.554, 123.79}, + {338.58, 123.626}) + .CubicCurveTo({338.58, 123.625}, {338.58, 123.625}, {338.58, 123.625}) + .CubicCurveTo({338.607, 123.455}, {338.65, 123.299}, + {338.704, 123.151}) + .CubicCurveTo({338.708, 123.14}, {338.71, 123.127}, + {338.714, 123.117}) + .CubicCurveTo({338.769, 122.971}, {338.833, 122.838}, + {338.905, 122.712}) + .CubicCurveTo({338.911, 122.702}, {338.916, 122.69200000000001}, + {338.922, 122.682}) + .CubicCurveTo({338.996, 122.557}, {339.072, 122.444}, + {339.155, 122.34}) + .CubicCurveTo({339.161, 122.333}, {339.166, 122.326}, + {339.172, 122.319}) + .CubicCurveTo({339.256, 122.215}, {339.339, 122.12}, + {339.425, 122.037}) + .CubicCurveTo({339.428, 122.033}, {339.431, 122.03}, + {339.435, 122.027}) + .CubicCurveTo({339.785, 121.687}, {340.106, 121.511}, + {340.106, 121.511}) + .CubicCurveTo({340.106, 121.511}, {340.107, 121.645}, + {340.082, 121.849}) + .Close() + .MoveTo({340.678, 113.245}) + .CubicCurveTo({340.594, 113.488}, {340.356, 113.655}, + {340.135, 113.775}) + .CubicCurveTo({339.817, 113.948}, {339.465, 114.059}, + {339.115, 114.151}) + .CubicCurveTo({338.251, 114.379}, {337.34, 114.516}, + {336.448, 114.516}) + .CubicCurveTo({335.761, 114.516}, {335.072, 114.527}, + {334.384, 114.513}) + .CubicCurveTo({334.125, 114.508}, {333.862, 114.462}, + {333.605, 114.424}) + .CubicCurveTo({332.865, 114.318}, {332.096, 114.184}, + {331.41, 113.883}) + .CubicCurveTo({330.979, 113.695}, {330.442, 113.34}, + {330.672, 112.813}) + .CubicCurveTo({331.135, 111.755}, {333.219, 112.946}, + {334.526, 113.833}) + .CubicCurveTo({334.54, 113.816}, {334.554, 113.8}, {334.569, 113.784}) + .CubicCurveTo({333.38, 112.708}, {331.749, 110.985}, + {332.76, 110.402}) + .CubicCurveTo({333.769, 109.82}, {334.713, 111.93}, + {335.228, 113.395}) + .CubicCurveTo({334.915, 111.889}, {334.59, 109.636}, + {335.661, 109.592}) + .CubicCurveTo({336.733, 109.636}, {336.408, 111.889}, + {336.07, 113.389}) + .CubicCurveTo({336.609, 111.93}, {337.553, 109.82}, + {338.563, 110.402}) + .CubicCurveTo({339.574, 110.984}, {337.942, 112.708}, + {336.753, 113.784}) + .CubicCurveTo({336.768, 113.8}, {336.782, 113.816}, + {336.796, 113.833}) + .CubicCurveTo({338.104, 112.946}, {340.187, 111.755}, + {340.65, 112.813}) + .CubicCurveTo({340.71, 112.95}, {340.728, 113.102}, + {340.678, 113.245}) + .Close() + .MoveTo({346.357, 106.771}) + .CubicCurveTo({346.295, 104.987}, {347.924, 104.139}, + {347.924, 104.139}) + .CubicCurveTo({347.924, 104.139}, {347.982, 105.931}, + {346.357, 106.771}) + .Close() + .MoveTo({347.56, 106.771}) + .CubicCurveTo({347.498, 104.987}, {349.127, 104.139}, + {349.127, 104.139}) + .CubicCurveTo({349.127, 104.139}, {349.185, 105.931}, + {347.56, 106.771}) + .Close() + .TakePath(); + Entity entity; + entity.SetPath(path); + entity.SetContents(SolidColorContents::Make(Color::Red())); + ASSERT_TRUE(OpenPlaygroundHere(entity)); +} + } // namespace testing } // namespace impeller From be7d04daaf96e129c3ff94282618b898e972b1e4 Mon Sep 17 00:00:00 2001 From: Brandon DeRosier Date: Thu, 24 Feb 2022 01:36:37 -0800 Subject: [PATCH 299/433] Match skia's path param ordering (#25) --- impeller/geometry/path_builder.cc | 12 ++++++------ impeller/geometry/path_builder.h | 12 ++++++------ 2 files changed, 12 insertions(+), 12 deletions(-) diff --git a/impeller/geometry/path_builder.cc b/impeller/geometry/path_builder.cc index c0278f58f368c..d65c1dcf1279c 100644 --- a/impeller/geometry/path_builder.cc +++ b/impeller/geometry/path_builder.cc @@ -58,8 +58,8 @@ PathBuilder& PathBuilder::VerticalLineTo(Scalar y, bool relative) { return *this; } -PathBuilder& PathBuilder::QuadraticCurveTo(Point point, - Point controlPoint, +PathBuilder& PathBuilder::QuadraticCurveTo(Point controlPoint, + Point point, bool relative) { point = relative ? current_ + point : point; controlPoint = relative ? current_ + controlPoint : controlPoint; @@ -100,9 +100,9 @@ PathBuilder& PathBuilder::SmoothQuadraticCurveTo(Point point, bool relative) { return *this; } -PathBuilder& PathBuilder::CubicCurveTo(Point point, - Point controlPoint1, +PathBuilder& PathBuilder::CubicCurveTo(Point controlPoint1, Point controlPoint2, + Point point, bool relative) { controlPoint1 = relative ? current_ + controlPoint1 : controlPoint1; controlPoint2 = relative ? current_ + controlPoint2 : controlPoint2; @@ -135,8 +135,8 @@ Point PathBuilder::ReflectedCubicControlPoint1() const { return (current_ * 2.0) - cubic.cp2; } -PathBuilder& PathBuilder::SmoothCubicCurveTo(Point point, - Point controlPoint2, +PathBuilder& PathBuilder::SmoothCubicCurveTo(Point controlPoint2, + Point point, bool relative) { auto controlPoint1 = ReflectedCubicControlPoint1(); controlPoint2 = relative ? current_ + controlPoint2 : controlPoint2; diff --git a/impeller/geometry/path_builder.h b/impeller/geometry/path_builder.h index 0e8fb32712650..09bd3b825665e 100644 --- a/impeller/geometry/path_builder.h +++ b/impeller/geometry/path_builder.h @@ -33,19 +33,19 @@ class PathBuilder { PathBuilder& VerticalLineTo(Scalar y, bool relative = false); - PathBuilder& QuadraticCurveTo(Point point, - Point controlPoint, + PathBuilder& QuadraticCurveTo(Point controlPoint, + Point point, bool relative = false); PathBuilder& SmoothQuadraticCurveTo(Point point, bool relative = false); - PathBuilder& CubicCurveTo(Point point, - Point controlPoint1, + PathBuilder& CubicCurveTo(Point controlPoint1, Point controlPoint2, + Point point, bool relative = false); - PathBuilder& SmoothCubicCurveTo(Point point, - Point controlPoint2, + PathBuilder& SmoothCubicCurveTo(Point controlPoint2, + Point point, bool relative = false); PathBuilder& AddRect(Rect rect); From 2979397379b49e3825d7ba3dd30a4be6e140bff4 Mon Sep 17 00:00:00 2001 From: Dan Field Date: Thu, 24 Feb 2022 10:34:01 -0800 Subject: [PATCH 300/433] Do not include the leading point for cubic polylines (#29) This avoids having duplicated points present in the polyline. --- impeller/geometry/geometry_unittests.cc | 11 +++++++++++ impeller/geometry/path_component.cc | 1 - 2 files changed, 11 insertions(+), 1 deletion(-) diff --git a/impeller/geometry/geometry_unittests.cc b/impeller/geometry/geometry_unittests.cc index 3c4330c4b5435..fe8c7726883fe 100644 --- a/impeller/geometry/geometry_unittests.cc +++ b/impeller/geometry/geometry_unittests.cc @@ -6,6 +6,7 @@ #include "flutter/testing/testing.h" #include "impeller/geometry/path.h" #include "impeller/geometry/path_builder.h" +#include "impeller/geometry/path_component.h" #include "impeller/geometry/point.h" #include "impeller/geometry/rect.h" #include "impeller/geometry/size.h" @@ -524,5 +525,15 @@ TEST(GeometryTest, RectContainsRect) { } } +TEST(GeometryTest, CubicPathComponentPolylineDoesNotIncludePointOne) { + CubicPathComponent component({10, 10}, {20,35}, {35, 20}, {40, 40}); + SmoothingApproximation approximation; + auto polyline = component.CreatePolyline(approximation); + ASSERT_NE(polyline.front().x, 10); + ASSERT_NE(polyline.front().y, 10); + ASSERT_EQ(polyline.back().x, 40); + ASSERT_EQ(polyline.back().y, 40); +} + } // namespace testing } // namespace impeller diff --git a/impeller/geometry/path_component.cc b/impeller/geometry/path_component.cc index a4b4737d88667..919a14c939b6c 100644 --- a/impeller/geometry/path_component.cc +++ b/impeller/geometry/path_component.cc @@ -339,7 +339,6 @@ static void CubicPathSmoothenRecursive(const SmoothingApproximation& approx, std::vector CubicPathComponent::CreatePolyline( const SmoothingApproximation& approximation) const { std::vector points; - points.emplace_back(p1); CubicPathSmoothenRecursive(approximation, points, p1, cp1, cp2, p2, 0); points.emplace_back(p2); return points; From 6415924a1c9e480437b9af1d6c931c2c4956298e Mon Sep 17 00:00:00 2001 From: Brandon DeRosier Date: Thu, 24 Feb 2022 15:01:11 -0800 Subject: [PATCH 301/433] Add assignment operators to point (#26) --- impeller/geometry/geometry_unittests.cc | 60 +++++++++++++++++++++++++ impeller/geometry/point.h | 56 +++++++++++++++++++++++ 2 files changed, 116 insertions(+) diff --git a/impeller/geometry/geometry_unittests.cc b/impeller/geometry/geometry_unittests.cc index fe8c7726883fe..61702c55afe19 100644 --- a/impeller/geometry/geometry_unittests.cc +++ b/impeller/geometry/geometry_unittests.cc @@ -393,6 +393,66 @@ TEST(GeometryTest, SizeCoercesToPoint) { } } +TEST(GeometryTest, CanUsePointAssignmentOperators) { + // Point on RHS + { + IPoint p(1, 2); + p += IPoint(1, 2); + ASSERT_EQ(p.x, 2u); + ASSERT_EQ(p.y, 4u); + } + + { + IPoint p(3, 6); + p -= IPoint(1, 2); + ASSERT_EQ(p.x, 2u); + ASSERT_EQ(p.y, 4u); + } + + { + IPoint p(1, 2); + p *= IPoint(2, 3); + ASSERT_EQ(p.x, 2u); + ASSERT_EQ(p.y, 6u); + } + + { + IPoint p(2, 6); + p /= IPoint(2, 3); + ASSERT_EQ(p.x, 1u); + ASSERT_EQ(p.y, 2u); + } + + // Size on RHS + { + IPoint p(1, 2); + p += ISize(1, 2); + ASSERT_EQ(p.x, 2u); + ASSERT_EQ(p.y, 4u); + } + + { + IPoint p(3, 6); + p -= ISize(1, 2); + ASSERT_EQ(p.x, 2u); + ASSERT_EQ(p.y, 4u); + } + + { + IPoint p(1, 2); + p *= ISize(2, 3); + ASSERT_EQ(p.x, 2u); + ASSERT_EQ(p.y, 6u); + } + + { + IPoint p(2, 6); + p /= ISize(2, 3); + ASSERT_EQ(p.x, 1u); + ASSERT_EQ(p.y, 2u); + } +} + TEST(GeometryTest, CanConvertBetweenDegressAndRadians) { { auto deg = Degrees{90.0}; diff --git a/impeller/geometry/point.h b/impeller/geometry/point.h index 8d183b7771e76..ad30904fdad4a 100644 --- a/impeller/geometry/point.h +++ b/impeller/geometry/point.h @@ -45,6 +45,62 @@ struct TPoint { return p.x != x || p.y != y; } + template + inline TPoint operator+=(const TPoint& p) { + x += static_cast(p.x); + y += static_cast(p.y); + return *this; + } + + template + inline TPoint operator+=(const TSize& s) { + x += static_cast(s.width); + y += static_cast(s.height); + return *this; + } + + template + inline TPoint operator-=(const TPoint& p) { + x -= static_cast(p.x); + y -= static_cast(p.y); + return *this; + } + + template + inline TPoint operator-=(const TSize& s) { + x -= static_cast(s.width); + y -= static_cast(s.height); + return *this; + } + + template + inline TPoint operator*=(const TPoint& p) { + x *= static_cast(p.x); + y *= static_cast(p.y); + return *this; + } + + template + inline TPoint operator*=(const TSize& s) { + x *= static_cast(s.width); + y *= static_cast(s.height); + return *this; + } + + template + inline TPoint operator/=(const TPoint& p) { + x /= static_cast(p.x); + y /= static_cast(p.y); + return *this; + } + + template + inline TPoint operator/=(const TSize& s) { + x /= static_cast(s.width); + y /= static_cast(s.height); + return *this; + } + constexpr TPoint operator-() const { return {-x, -y}; } constexpr TPoint operator+(const TPoint& p) const { From 48ff4a1542216e5bf5d119a397dddbad51da0b22 Mon Sep 17 00:00:00 2001 From: Brandon DeRosier Date: Thu, 24 Feb 2022 15:01:18 -0800 Subject: [PATCH 302/433] Add entity playground callback (#27) --- impeller/entity/entity_playground.cc | 21 ++++++++++++++++++--- impeller/entity/entity_playground.h | 6 ++++++ 2 files changed, 24 insertions(+), 3 deletions(-) diff --git a/impeller/entity/entity_playground.cc b/impeller/entity/entity_playground.cc index 4c76be6088bd4..e65451ac8659d 100644 --- a/impeller/entity/entity_playground.cc +++ b/impeller/entity/entity_playground.cc @@ -17,14 +17,29 @@ bool EntityPlayground::OpenPlaygroundHere(Entity entity) { return true; } - ContentContext context_context(GetContext()); - if (!context_context.IsValid()) { + ContentContext content_context(GetContext()); + if (!content_context.IsValid()) { return false; } Renderer::RenderCallback callback = [&](RenderPass& pass) -> bool { - return entity.Render(context_context, pass); + return entity.Render(content_context, pass); }; return Playground::OpenPlaygroundHere(callback); } +bool EntityPlayground::OpenPlaygroundHere(EntityPlaygroundCallback callback) { + if (!Playground::is_enabled()) { + return true; + } + + ContentContext content_context(GetContext()); + if (!content_context.IsValid()) { + return false; + } + Renderer::RenderCallback render_callback = [&](RenderPass& pass) -> bool { + return callback(content_context, pass); + }; + return Playground::OpenPlaygroundHere(render_callback); +} + } // namespace impeller diff --git a/impeller/entity/entity_playground.h b/impeller/entity/entity_playground.h index 9d16caab30a95..c506b72dc5384 100644 --- a/impeller/entity/entity_playground.h +++ b/impeller/entity/entity_playground.h @@ -5,6 +5,7 @@ #pragma once #include "flutter/fml/macros.h" +#include "impeller/entity/content_context.h" #include "impeller/entity/entity.h" #include "impeller/playground/playground.h" @@ -12,12 +13,17 @@ namespace impeller { class EntityPlayground : public Playground { public: + using EntityPlaygroundCallback = + std::function; + EntityPlayground(); ~EntityPlayground(); bool OpenPlaygroundHere(Entity entity); + bool OpenPlaygroundHere(EntityPlaygroundCallback callback); + private: FML_DISALLOW_COPY_AND_ASSIGN(EntityPlayground); }; From d3180f513539babd68693cf65a7ead1040f946dc Mon Sep 17 00:00:00 2001 From: Jim Graham Date: Thu, 24 Feb 2022 15:17:04 -0800 Subject: [PATCH 303/433] adopt new DlAttributeType enum naming (#30) --- impeller/display_list/display_list_dispatcher.cc | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/impeller/display_list/display_list_dispatcher.cc b/impeller/display_list/display_list_dispatcher.cc index a8243c337e617..9fdf58b4ba87b 100644 --- a/impeller/display_list/display_list_dispatcher.cc +++ b/impeller/display_list/display_list_dispatcher.cc @@ -87,11 +87,11 @@ void DisplayListDispatcher::setColorFilter( return; } switch (filter->type()) { - case flutter::DlColorFilter::kBlend: - case flutter::DlColorFilter::kMatrix: - case flutter::DlColorFilter::kSrgbToLinearGamma: - case flutter::DlColorFilter::kLinearToSrgbGamma: - case flutter::DlColorFilter::kUnknown: + case flutter::DlColorFilterType::kBlend: + case flutter::DlColorFilterType::kMatrix: + case flutter::DlColorFilterType::kSrgbToLinearGamma: + case flutter::DlColorFilterType::kLinearToSrgbGamma: + case flutter::DlColorFilterType::kUnknown: UNIMPLEMENTED; break; } @@ -127,8 +127,8 @@ void DisplayListDispatcher::setMaskFilter(const flutter::DlMaskFilter* filter) { return; } switch (filter->type()) { - case flutter::DlMaskFilter::kBlur: - case flutter::DlMaskFilter::kUnknown: + case flutter::DlMaskFilterType::kBlur: + case flutter::DlMaskFilterType::kUnknown: UNIMPLEMENTED; break; } From 476ecacc05e15f725af15874c018e4ea570abb70 Mon Sep 17 00:00:00 2001 From: Dan Field Date: Thu, 24 Feb 2022 16:51:40 -0800 Subject: [PATCH 304/433] Support subcontours in strokes, lay groundwork for fills (#31) --- impeller/compiler/reflector.cc | 8 +++ impeller/entity/contents.cc | 34 +++++++----- impeller/entity/entity_unittests.cc | 42 ++++++++++++++- impeller/entity/shaders/solid_stroke.frag | 2 + impeller/entity/shaders/solid_stroke.vert | 3 ++ impeller/geometry/geometry_unittests.cc | 5 +- impeller/geometry/path.cc | 63 +++++++++++++++++++---- impeller/geometry/path.h | 28 ++++++++-- impeller/geometry/path_builder.cc | 6 ++- impeller/geometry/path_component.h | 13 +++++ impeller/renderer/tessellator.cc | 13 ++--- impeller/renderer/tessellator.h | 2 +- 12 files changed, 183 insertions(+), 36 deletions(-) diff --git a/impeller/compiler/reflector.cc b/impeller/compiler/reflector.cc index 3266a292bf1d1..b7f3f190e4627 100644 --- a/impeller/compiler/reflector.cc +++ b/impeller/compiler/reflector.cc @@ -611,6 +611,14 @@ static VertexType VertexTypeFromInputResource( type.columns == 1u && type.vecsize == 3u && type.width == sizeof(float) * 8u) { result.type_name = "Vector3"; + } else if (type.basetype == spirv_cross::SPIRType::BaseType::Float && + type.columns == 1u && type.vecsize == 1u && + type.width == sizeof(float) * 8u) { + result.type_name = "Scalar"; + } else if (type.basetype == spirv_cross::SPIRType::BaseType::Int && + type.columns == 1u && type.vecsize == 1u && + type.width == sizeof(int32_t) * 8u) { + result.type_name = "int32_t"; } else { // Catch all unknown padding. result.type_name = TypeNameWithPaddingOfSize(total_size); diff --git a/impeller/entity/contents.cc b/impeller/entity/contents.cc index f25a50d98c9b1..9567d1c11f515 100644 --- a/impeller/entity/contents.cc +++ b/impeller/entity/contents.cc @@ -235,8 +235,7 @@ bool TextureContents::Render(const ContentContext& renderer, auto coverage_coords = (vtx - coverage_rect->origin) / coverage_rect->size; data.texture_coords = - (source_rect_.origin + - source_rect_.size * coverage_coords) / + (source_rect_.origin + source_rect_.size * coverage_coords) / texture_size; vertex_builder.AppendVertex(data); }); @@ -301,11 +300,13 @@ static VertexBuffer CreateSolidStrokeVertices(const Path& path, VertexBufferBuilder vtx_builder; auto polyline = path.CreatePolyline(); - for (size_t i = 0, polyline_size = polyline.size(); i < polyline_size; i++) { + for (size_t i = 0, polyline_size = polyline.points.size(); i < polyline_size; + i++) { const auto is_last_point = i == polyline_size - 1; - const auto& p1 = polyline[i]; - const auto& p2 = is_last_point ? polyline[i - 1] : polyline[i + 1]; + const auto& p1 = polyline.points[i]; + const auto& p2 = + is_last_point ? polyline.points[i - 1] : polyline.points[i + 1]; const auto diff = p2 - p1; @@ -316,18 +317,27 @@ static VertexBuffer CreateSolidStrokeVertices(const Path& path, VS::PerVertexData vtx; vtx.vertex_position = p1; - - if (i == 0) { - vtx.vertex_normal = -normal; - vtx_builder.AppendVertex(vtx); - vtx.vertex_normal = normal; - vtx_builder.AppendVertex(vtx); - } + auto pen_down = + polyline.breaks.find(i) == polyline.breaks.end() ? 1.0 : 0.0; vtx.vertex_normal = normal; + vtx.pen_down = pen_down; vtx_builder.AppendVertex(vtx); + vtx.vertex_normal = -normal; + vtx.pen_down = pen_down; vtx_builder.AppendVertex(vtx); + + // Put the pen down again for the next contour. + if (!pen_down) { + vtx.vertex_normal = normal; + vtx.pen_down = 1.0; + vtx_builder.AppendVertex(vtx); + + vtx.vertex_normal = -normal; + vtx.pen_down = 1.0; + vtx_builder.AppendVertex(vtx); + } } return vtx_builder.CreateVertexBuffer(buffer); diff --git a/impeller/entity/entity_unittests.cc b/impeller/entity/entity_unittests.cc index 8a678e7475d27..c5f9d52168bad 100644 --- a/impeller/entity/entity_unittests.cc +++ b/impeller/entity/entity_unittests.cc @@ -25,6 +25,47 @@ TEST_F(EntityTest, CanDrawRect) { ASSERT_TRUE(OpenPlaygroundHere(entity)); } +TEST_F(EntityTest, ThreeStrokesInOnePath) { + Path path = PathBuilder{} + .MoveTo({100, 100}) + .LineTo({100, 200}) + .MoveTo({100, 300}) + .LineTo({100, 400}) + .MoveTo({100, 500}) + .LineTo({100, 600}) + .TakePath(); + + Entity entity; + entity.SetPath(path); + auto contents = std::make_unique(); + contents->SetColor(Color::Red()); + contents->SetStrokeSize(5.0); + entity.SetContents(std::move(contents)); + ASSERT_TRUE(OpenPlaygroundHere(entity)); +} + +TEST_F(EntityTest, TriangleInsideASquare) { + Path path = PathBuilder{} + .MoveTo({10, 10}) + .LineTo({210, 10}) + .LineTo({210, 210}) + .LineTo({10, 210}) + .Close() + .MoveTo({50, 50}) + .LineTo({100, 50}) + .LineTo({50, 150}) + .Close() + .TakePath(); + + Entity entity; + entity.SetPath(path); + auto contents = std::make_unique(); + contents->SetColor(Color::Red()); + contents->SetStrokeSize(5.0); + entity.SetContents(std::move(contents)); + ASSERT_TRUE(OpenPlaygroundHere(entity)); +} + TEST_F(EntityTest, DISABLED_BadCubicCurveTest) { // Compare with https://fiddle.skia.org/c/b3625f26122c9de7afe7794fcf25ead3 Path path = @@ -45,7 +86,6 @@ TEST_F(EntityTest, DISABLED_BadCubicCurveTest) { entity.SetPath(path); entity.SetContents(SolidColorContents::Make(Color::Red())); ASSERT_TRUE(OpenPlaygroundHere(entity)); - } TEST_F(EntityTest, DISABLED_BadCubicCurveAndOverlapTest) { diff --git a/impeller/entity/shaders/solid_stroke.frag b/impeller/entity/shaders/solid_stroke.frag index 88b1f7635d9f0..3d997a40ee4a6 100644 --- a/impeller/entity/shaders/solid_stroke.frag +++ b/impeller/entity/shaders/solid_stroke.frag @@ -3,9 +3,11 @@ // found in the LICENSE file. in vec4 stroke_color; +in float v_pen_down; out vec4 frag_color; void main() { frag_color = stroke_color; + frag_color.a *= floor(v_pen_down); } diff --git a/impeller/entity/shaders/solid_stroke.vert b/impeller/entity/shaders/solid_stroke.vert index 896fb565991f3..b92e63828cff1 100644 --- a/impeller/entity/shaders/solid_stroke.vert +++ b/impeller/entity/shaders/solid_stroke.vert @@ -13,12 +13,15 @@ uniform StrokeInfo { in vec2 vertex_position; in vec2 vertex_normal; +in float pen_down; out vec4 stroke_color; +out float v_pen_down; void main() { // Push one vertex by the half stroke size along the normal vector. vec2 offset = vertex_normal * vec2(stroke_info.size * 0.5); gl_Position = frame_info.mvp * vec4(vertex_position + offset, 0.0, 1.0); stroke_color = stroke_info.color; + v_pen_down = pen_down; } diff --git a/impeller/geometry/geometry_unittests.cc b/impeller/geometry/geometry_unittests.cc index 61702c55afe19..5791393800bc1 100644 --- a/impeller/geometry/geometry_unittests.cc +++ b/impeller/geometry/geometry_unittests.cc @@ -178,7 +178,8 @@ TEST(GeometryTest, SimplePath) { ASSERT_EQ(cubic.cp1, cp1); ASSERT_EQ(cubic.cp2, cp2); ASSERT_EQ(cubic.p2, p2); - }); + }, + [](size_t index, const MovePathComponent& move) { ASSERT_TRUE(false); }); } TEST(GeometryTest, BoundingBoxCubic) { @@ -586,7 +587,7 @@ TEST(GeometryTest, RectContainsRect) { } TEST(GeometryTest, CubicPathComponentPolylineDoesNotIncludePointOne) { - CubicPathComponent component({10, 10}, {20,35}, {35, 20}, {40, 40}); + CubicPathComponent component({10, 10}, {20, 35}, {35, 20}, {40, 40}); SmoothingApproximation approximation; auto polyline = component.CreatePolyline(approximation); ASSERT_NE(polyline.front().x, 10); diff --git a/impeller/geometry/path.cc b/impeller/geometry/path.cc index a9e09e5b837c8..a5c0268bfc18f 100644 --- a/impeller/geometry/path.cc +++ b/impeller/geometry/path.cc @@ -42,10 +42,16 @@ Path& Path::AddCubicComponent(Point p1, Point cp1, Point cp2, Point p2) { return *this; } -void Path::EnumerateComponents( - Applier linear_applier, - Applier quad_applier, - Applier cubic_applier) const { +Path& Path::AddMoveComponent(Point destination) { + moves_.emplace_back(destination); + components_.emplace_back(ComponentType::kMove, moves_.size() - 1); + return *this; +} + +void Path::EnumerateComponents(Applier linear_applier, + Applier quad_applier, + Applier cubic_applier, + Applier move_applier) const { size_t currentIndex = 0; for (const auto& component : components_) { switch (component.type) { @@ -64,6 +70,11 @@ void Path::EnumerateComponents( cubic_applier(currentIndex, cubics_[component.index]); } break; + case ComponentType::kMove: + if (move_applier) { + move_applier(currentIndex, moves_[component.index]); + } + break; } currentIndex++; } @@ -112,6 +123,20 @@ bool Path::GetCubicComponentAtIndex(size_t index, return true; } +bool Path::GetMoveComponentAtIndex(size_t index, + MovePathComponent& move) const { + if (index >= components_.size()) { + return false; + } + + if (components_[index].type != ComponentType::kMove) { + return false; + } + + move = moves_[components_[index].index]; + return true; +} + bool Path::UpdateLinearComponentAtIndex(size_t index, const LinearPathComponent& linear) { if (index >= components_.size()) { @@ -155,12 +180,27 @@ bool Path::UpdateCubicComponentAtIndex(size_t index, return true; } -std::vector Path::CreatePolyline( +bool Path::UpdateMoveComponentAtIndex(size_t index, + const MovePathComponent& move) { + if (index >= components_.size()) { + return false; + } + + if (components_[index].type != ComponentType::kMove) { + return false; + } + + moves_[components_[index].index] = move; + return true; +} + +Path::Polyline Path::CreatePolyline( const SmoothingApproximation& approximation) const { - std::vector points; - auto collect_points = [&points](const std::vector& collection) { - points.reserve(points.size() + collection.size()); - points.insert(points.end(), collection.begin(), collection.end()); + Polyline polyline; + auto collect_points = [&polyline](const std::vector& collection) { + polyline.points.reserve(polyline.points.size() + collection.size()); + polyline.points.insert(polyline.points.end(), collection.begin(), + collection.end()); }; for (const auto& component : components_) { switch (component.type) { @@ -173,9 +213,12 @@ std::vector Path::CreatePolyline( case ComponentType::kCubic: collect_points(cubics_[component.index].CreatePolyline(approximation)); break; + case ComponentType::kMove: + polyline.breaks.insert(polyline.points.size()); + break; } } - return points; + return polyline; } std::optional Path::GetBoundingBox() const { diff --git a/impeller/geometry/path.h b/impeller/geometry/path.h index e14f057e7f5d5..450000e896f3e 100644 --- a/impeller/geometry/path.h +++ b/impeller/geometry/path.h @@ -6,6 +6,7 @@ #include #include +#include #include #include "impeller/geometry/path_component.h" @@ -22,7 +23,9 @@ enum class FillType { //------------------------------------------------------------------------------ /// @brief Paths are lightweight objects that describe a collection of -/// linear, quadratic, or cubic segments. +/// linear, quadratic, or cubic segments. These segments may be +/// be broken up by move commands, which are effectively linear +/// commands that pick up the pen rather than continuing to draw. /// /// All shapes supported by Impeller are paths either directly or /// via approximation (in the case of circles). @@ -36,6 +39,17 @@ class Path { kLinear, kQuadratic, kCubic, + kMove, + }; + + /// One or more contours represented as a series of points and indices in + /// the point vector representing the start of a new contour. + struct Polyline { + /// Points in the polyline, which may represent multiple contours specified + /// by indices in |breaks|. + std::vector points; + /// Indices of points that end a subcontour. + std::set breaks; }; Path(); @@ -54,11 +68,14 @@ class Path { Path& AddCubicComponent(Point p1, Point cp1, Point cp2, Point p2); + Path& AddMoveComponent(Point destination); + template using Applier = std::function; void EnumerateComponents(Applier linearApplier, Applier quadApplier, - Applier cubicApplier) const; + Applier cubicApplier, + Applier moveApplier) const; bool GetLinearComponentAtIndex(size_t index, LinearPathComponent& linear) const; @@ -68,6 +85,8 @@ class Path { bool GetCubicComponentAtIndex(size_t index, CubicPathComponent& cubic) const; + bool GetMoveComponentAtIndex(size_t index, MovePathComponent& move) const; + bool UpdateLinearComponentAtIndex(size_t index, const LinearPathComponent& linear); @@ -76,7 +95,9 @@ class Path { bool UpdateCubicComponentAtIndex(size_t index, CubicPathComponent& cubic); - std::vector CreatePolyline( + bool UpdateMoveComponentAtIndex(size_t index, const MovePathComponent& move); + + Polyline CreatePolyline( const SmoothingApproximation& approximation = {}) const; std::optional GetBoundingBox() const; @@ -99,6 +120,7 @@ class Path { std::vector linears_; std::vector quads_; std::vector cubics_; + std::vector moves_; }; } // namespace impeller diff --git a/impeller/geometry/path_builder.cc b/impeller/geometry/path_builder.cc index d65c1dcf1279c..aba307b0893bf 100644 --- a/impeller/geometry/path_builder.cc +++ b/impeller/geometry/path_builder.cc @@ -27,6 +27,7 @@ Path PathBuilder::TakePath(FillType fill) { PathBuilder& PathBuilder::MoveTo(Point point, bool relative) { current_ = relative ? current_ + point : point; subpath_start_ = current_; + prototype_.AddMoveComponent(current_); return *this; } @@ -343,7 +344,10 @@ PathBuilder& PathBuilder::AddPath(const Path& path) { auto cubic = [&](size_t index, const CubicPathComponent& c) { prototype_.AddCubicComponent(c.p1, c.cp1, c.cp2, c.p2); }; - path.EnumerateComponents(linear, quadratic, cubic); + auto move = [&](size_t index, const MovePathComponent& m) { + prototype_.AddMoveComponent(m.destination); + }; + path.EnumerateComponents(linear, quadratic, cubic, move); return *this; } diff --git a/impeller/geometry/path_component.h b/impeller/geometry/path_component.h index 289b72793e115..c9f03ddad8db4 100644 --- a/impeller/geometry/path_component.h +++ b/impeller/geometry/path_component.h @@ -6,6 +6,7 @@ #include +#include "impeller/geometry/point.h" #include "impeller/geometry/rect.h" #include "impeller/geometry/scalar.h" @@ -106,4 +107,16 @@ struct CubicPathComponent { } }; +struct MovePathComponent { + Point destination; + + MovePathComponent() {} + + MovePathComponent(Point p) : destination(p) {} + + bool operator==(const MovePathComponent& other) const { + return destination == other.destination; + } +}; + } // namespace impeller diff --git a/impeller/renderer/tessellator.cc b/impeller/renderer/tessellator.cc index 38d724b263c51..3bbcfa30428e7 100644 --- a/impeller/renderer/tessellator.cc +++ b/impeller/renderer/tessellator.cc @@ -4,6 +4,7 @@ #include "impeller/renderer/tessellator.h" +#include "flutter/fml/logging.h" #include "flutter/fml/trace_event.h" #include "third_party/libtess2/Include/tesselator.h" @@ -35,7 +36,7 @@ static void DestroyTessellator(TESStesselator* tessellator) { } } -bool Tessellator::Tessellate(const std::vector& contours, +bool Tessellator::Tessellate(const Path::Polyline& contours, VertexCallback callback) const { TRACE_EVENT0("impeller", "Tessellator::Tessellate"); if (!callback) { @@ -60,11 +61,11 @@ bool Tessellator::Tessellate(const std::vector& contours, /// Feed contour information to the tessellator. /// static_assert(sizeof(Point) == 2 * sizeof(float)); - ::tessAddContour(tessellator.get(), // the C tessellator - kVertexSize, // - contours.data(), // - sizeof(Point), // - contours.size() // + ::tessAddContour(tessellator.get(), // the C tessellator + kVertexSize, // + contours.points.data(), // + sizeof(Point), // + contours.points.size() // ); //---------------------------------------------------------------------------- diff --git a/impeller/renderer/tessellator.h b/impeller/renderer/tessellator.h index ee27d924748e2..db8a1f29100c0 100644 --- a/impeller/renderer/tessellator.h +++ b/impeller/renderer/tessellator.h @@ -38,7 +38,7 @@ class Tessellator { /// /// @return If tessellation was successful. /// - bool Tessellate(const std::vector& polyline, + bool Tessellate(const Path::Polyline& polyline, VertexCallback callback) const; private: From 0cea3cc324e8bc32a097a054bfe38a2709c26867 Mon Sep 17 00:00:00 2001 From: Brandon DeRosier Date: Thu, 24 Feb 2022 17:09:21 -0800 Subject: [PATCH 305/433] Add immediate mode manipulator widget macros for the playground (#28) --- impeller/playground/BUILD.gn | 2 + impeller/playground/widgets.cc | 11 +++++ impeller/playground/widgets.h | 75 ++++++++++++++++++++++++++++++++++ 3 files changed, 88 insertions(+) create mode 100644 impeller/playground/widgets.cc create mode 100644 impeller/playground/widgets.h diff --git a/impeller/playground/BUILD.gn b/impeller/playground/BUILD.gn index b01803ed86281..5f9618e98db35 100644 --- a/impeller/playground/BUILD.gn +++ b/impeller/playground/BUILD.gn @@ -11,6 +11,8 @@ impeller_component("playground") { sources = [ "playground.h", "playground.mm", + "widgets.cc", + "widgets.h", ] public_deps = [ diff --git a/impeller/playground/widgets.cc b/impeller/playground/widgets.cc new file mode 100644 index 0000000000000..ed3f76c380098 --- /dev/null +++ b/impeller/playground/widgets.cc @@ -0,0 +1,11 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "widgets.h" + +namespace impeller { + +// + +} // namespace impeller diff --git a/impeller/playground/widgets.h b/impeller/playground/widgets.h new file mode 100644 index 0000000000000..819d47ff2c64c --- /dev/null +++ b/impeller/playground/widgets.h @@ -0,0 +1,75 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#pragma once + +#include + +#include "impeller/base/strings.h" +#include "impeller/geometry/color.h" +#include "impeller/geometry/point.h" +#include "third_party/imgui/imgui.h" + +#define IMPELLER_PLAYGROUND_POINT(default_position, radius, color) \ + ({ \ + static impeller::Point position = default_position; \ + static impeller::Point reset_position = default_position; \ + float radius_ = radius; \ + impeller::Color color_ = color; \ + \ + static bool dragging = false; \ + impeller::Point mouse_pos(ImGui::GetMousePos().x, ImGui::GetMousePos().y); \ + static impeller::Point prev_mouse_pos = mouse_pos; \ + \ + if (ImGui::IsKeyPressed('R')) { \ + position = reset_position; \ + dragging = false; \ + } \ + \ + bool hovering = position.GetDistance(mouse_pos) < radius_ && \ + position.GetDistance(prev_mouse_pos) < radius_; \ + if (!ImGui::IsMouseDown(0)) { \ + dragging = false; \ + } else if (hovering && ImGui::IsMouseClicked(0)) { \ + dragging = true; \ + } \ + if (dragging) { \ + position += mouse_pos - prev_mouse_pos; \ + } \ + ImGui::GetBackgroundDrawList()->AddCircleFilled( \ + {position.x, position.y}, radius_, \ + ImColor(color_.red, color_.green, color_.blue, \ + (hovering || dragging) ? 0.6f : 0.3f)); \ + if (hovering || dragging) { \ + ImGui::GetBackgroundDrawList()->AddText( \ + {position.x - radius_, position.y + radius_ + 10}, \ + ImColor(color_.red, color.green, color.blue, 1.0f), \ + impeller::SPrintF("x:%0.3f y:%0.3f", position.x, position.y) \ + .c_str()); \ + } \ + prev_mouse_pos = mouse_pos; \ + position; \ + }) + +#define IMPELLER_PLAYGROUND_LINE(default_position_a, default_position_b, \ + radius, color_a, color_b) \ + ({ \ + impeller::Point position_a = default_position_a; \ + impeller::Point position_b = default_position_b; \ + float r_ = radius; \ + impeller::Color color_a_ = color_a; \ + impeller::Color color_b_ = color_b; \ + \ + position_a = IMPELLER_PLAYGROUND_POINT(position_a, r_, color_a_); \ + position_b = IMPELLER_PLAYGROUND_POINT(position_b, r_, color_b_); \ + \ + auto dir = (position_b - position_a).Normalize() * r_; \ + auto line_a = position_a + dir; \ + auto line_b = position_b - dir; \ + ImGui::GetBackgroundDrawList()->AddLine( \ + {line_a.x, line_a.y}, {line_b.x, line_b.y}, \ + ImColor(color_b.red, color_b.green, color_b.blue, 0.3f)); \ + \ + std::make_tuple(position_a, position_b); \ + }) From 01bbb3ffbcb2885a52f7fb45c6c80d4f21def44b Mon Sep 17 00:00:00 2001 From: Dan Field Date: Fri, 25 Feb 2022 10:54:21 -0800 Subject: [PATCH 306/433] Fix MoveTo for fills (#32) --- impeller/entity/entity_unittests.cc | 4 ++-- impeller/renderer/tessellator.cc | 25 +++++++++++++++++++------ 2 files changed, 21 insertions(+), 8 deletions(-) diff --git a/impeller/entity/entity_unittests.cc b/impeller/entity/entity_unittests.cc index c5f9d52168bad..a69a5cab934fe 100644 --- a/impeller/entity/entity_unittests.cc +++ b/impeller/entity/entity_unittests.cc @@ -66,7 +66,7 @@ TEST_F(EntityTest, TriangleInsideASquare) { ASSERT_TRUE(OpenPlaygroundHere(entity)); } -TEST_F(EntityTest, DISABLED_BadCubicCurveTest) { +TEST_F(EntityTest, CubicCurveTest) { // Compare with https://fiddle.skia.org/c/b3625f26122c9de7afe7794fcf25ead3 Path path = PathBuilder{} @@ -88,7 +88,7 @@ TEST_F(EntityTest, DISABLED_BadCubicCurveTest) { ASSERT_TRUE(OpenPlaygroundHere(entity)); } -TEST_F(EntityTest, DISABLED_BadCubicCurveAndOverlapTest) { +TEST_F(EntityTest, CubicCurveAndOverlapTest) { // Compare with https://fiddle.skia.org/c/7a05a3e186c65a8dfb732f68020aae06 Path path = PathBuilder{} diff --git a/impeller/renderer/tessellator.cc b/impeller/renderer/tessellator.cc index 3bbcfa30428e7..d98225d54f6d5 100644 --- a/impeller/renderer/tessellator.cc +++ b/impeller/renderer/tessellator.cc @@ -61,12 +61,25 @@ bool Tessellator::Tessellate(const Path::Polyline& contours, /// Feed contour information to the tessellator. /// static_assert(sizeof(Point) == 2 * sizeof(float)); - ::tessAddContour(tessellator.get(), // the C tessellator - kVertexSize, // - contours.points.data(), // - sizeof(Point), // - contours.points.size() // - ); + size_t start_point_index = 0; + for (size_t end_point_index : contours.breaks) { + end_point_index = std::min(end_point_index, contours.points.size()); + ::tessAddContour(tessellator.get(), // the C tessellator + kVertexSize, // + contours.points.data() + start_point_index, // + sizeof(Point), // + end_point_index - start_point_index // + ); + start_point_index = end_point_index; + } + if (start_point_index < contours.points.size()) { + ::tessAddContour(tessellator.get(), // the C tessellator + kVertexSize, // + contours.points.data() + start_point_index, // + sizeof(Point), // + contours.points.size() - start_point_index // + ); + } //---------------------------------------------------------------------------- /// Let's tessellate. From 4e5ae50ccd5d66b84a8f5e2f9c3de05ab9ac7f32 Mon Sep 17 00:00:00 2001 From: Brandon DeRosier Date: Fri, 25 Feb 2022 12:29:43 -0800 Subject: [PATCH 307/433] Add dot and cross product to Point (#33) --- impeller/geometry/geometry_unittests.cc | 40 +++++++++++++++++++++++++ impeller/geometry/point.h | 6 ++++ 2 files changed, 46 insertions(+) diff --git a/impeller/geometry/geometry_unittests.cc b/impeller/geometry/geometry_unittests.cc index 5791393800bc1..4c0f2e3340463 100644 --- a/impeller/geometry/geometry_unittests.cc +++ b/impeller/geometry/geometry_unittests.cc @@ -454,6 +454,46 @@ TEST(GeometryTest, CanUsePointAssignmentOperators) { } } +TEST(GeometryTest, PointDotProduct) { + { + Point p(1, 0); + Scalar s = p.Dot(Point(-1, 0)); + ASSERT_FLOAT_EQ(s, -1); + } + + { + Point p(0, -1); + Scalar s = p.Dot(Point(-1, 0)); + ASSERT_FLOAT_EQ(s, 0); + } + + { + Point p(1, 2); + Scalar s = p.Dot(Point(3, -4)); + ASSERT_FLOAT_EQ(s, -5); + } +} + +TEST(GeometryTest, PointCrossProduct) { + { + Point p(1, 0); + Scalar s = p.Cross(Point(-1, 0)); + ASSERT_FLOAT_EQ(s, 0); + } + + { + Point p(0, -1); + Scalar s = p.Cross(Point(-1, 0)); + ASSERT_FLOAT_EQ(s, -1); + } + + { + Point p(1, 2); + Scalar s = p.Cross(Point(3, -4)); + ASSERT_FLOAT_EQ(s, -10); + } +} + TEST(GeometryTest, CanConvertBetweenDegressAndRadians) { { auto deg = Degrees{90.0}; diff --git a/impeller/geometry/point.h b/impeller/geometry/point.h index ad30904fdad4a..f41184f0b867e 100644 --- a/impeller/geometry/point.h +++ b/impeller/geometry/point.h @@ -175,6 +175,12 @@ struct TPoint { return {x / length, y / length}; } + constexpr Scalar Cross(const TPoint& p) const { + return (x * p.y) - (y * p.x); + } + + constexpr Scalar Dot(const TPoint& p) const { return (x * p.x) + (y * p.y); } + constexpr bool IsZero() const { return x == 0 && y == 0; } }; From 9c39cd8e12397d2e353b82c80c5c23189a2411bb Mon Sep 17 00:00:00 2001 From: Brandon DeRosier Date: Fri, 25 Feb 2022 13:24:14 -0800 Subject: [PATCH 308/433] Remove duplicate points between connected components when generating polylines (#34) --- impeller/geometry/geometry_unittests.cc | 19 +++++++++++++++++++ impeller/geometry/path.cc | 16 +++++++++++++--- 2 files changed, 32 insertions(+), 3 deletions(-) diff --git a/impeller/geometry/geometry_unittests.cc b/impeller/geometry/geometry_unittests.cc index 4c0f2e3340463..2d551d32ee9f8 100644 --- a/impeller/geometry/geometry_unittests.cc +++ b/impeller/geometry/geometry_unittests.cc @@ -636,5 +636,24 @@ TEST(GeometryTest, CubicPathComponentPolylineDoesNotIncludePointOne) { ASSERT_EQ(polyline.back().y, 40); } +TEST(GeometryTest, PathCreatePolyLineDoesNotDuplicatePoints) { + Path path; + path.AddMoveComponent({10, 10}); + path.AddLinearComponent({10, 10}, {20, 20}); + path.AddLinearComponent({20, 20}, {30, 30}); + path.AddMoveComponent({40, 40}); + path.AddLinearComponent({40, 40}, {50, 50}); + + auto polyline = path.CreatePolyline(); + + ASSERT_EQ(polyline.breaks.size(), 2u); + ASSERT_EQ(polyline.points.size(), 5u); + ASSERT_EQ(polyline.points[0].x, 10); + ASSERT_EQ(polyline.points[1].x, 20); + ASSERT_EQ(polyline.points[2].x, 30); + ASSERT_EQ(polyline.points[3].x, 40); + ASSERT_EQ(polyline.points[4].x, 50); +} + } // namespace testing } // namespace impeller diff --git a/impeller/geometry/path.cc b/impeller/geometry/path.cc index a5c0268bfc18f..4bacb04ea9bb0 100644 --- a/impeller/geometry/path.cc +++ b/impeller/geometry/path.cc @@ -197,9 +197,18 @@ bool Path::UpdateMoveComponentAtIndex(size_t index, Path::Polyline Path::CreatePolyline( const SmoothingApproximation& approximation) const { Polyline polyline; - auto collect_points = [&polyline](const std::vector& collection) { - polyline.points.reserve(polyline.points.size() + collection.size()); - polyline.points.insert(polyline.points.end(), collection.begin(), + // TODO(99177): Refactor this to have component polyline creation always + // exclude the first point, and append the destination point for + // move components. See issue for details. + bool new_contour = true; + auto collect_points = [&polyline, + &new_contour](const std::vector& collection) { + size_t offset = new_contour ? 0 : 1; + new_contour = false; + + polyline.points.reserve(polyline.points.size() + collection.size() - + offset); + polyline.points.insert(polyline.points.end(), collection.begin() + offset, collection.end()); }; for (const auto& component : components_) { @@ -215,6 +224,7 @@ Path::Polyline Path::CreatePolyline( break; case ComponentType::kMove: polyline.breaks.insert(polyline.points.size()); + new_contour = true; break; } } From dbae5261ded86ca3bd5d1e3d2c31f562627f26e4 Mon Sep 17 00:00:00 2001 From: Brandon DeRosier Date: Fri, 25 Feb 2022 13:42:50 -0800 Subject: [PATCH 309/433] Handle all corner cases for stroke geometry, add bevel join & cap/join enums (#35) --- impeller/entity/contents.cc | 156 ++++++++++++++++++++++------ impeller/entity/contents.h | 29 ++++++ impeller/entity/entity_unittests.cc | 48 +++++---- 3 files changed, 184 insertions(+), 49 deletions(-) diff --git a/impeller/entity/contents.cc b/impeller/entity/contents.cc index 9567d1c11f515..8f2ad028785a2 100644 --- a/impeller/entity/contents.cc +++ b/impeller/entity/contents.cc @@ -293,6 +293,30 @@ const Color& SolidStrokeContents::GetColor() const { return color_; } +static void CreateCap( + VertexBufferBuilder& vtx_builder, + const Point& position, + const Point& normal) {} + +static void CreateJoin( + VertexBufferBuilder& vtx_builder, + const Point& position, + const Point& start_normal, + const Point& end_normal) { + SolidStrokeVertexShader::PerVertexData vtx; + vtx.vertex_position = position; + vtx.pen_down = 1.0; + vtx.vertex_normal = {}; + vtx_builder.AppendVertex(vtx); + + // A simple bevel join to start with. + Scalar dir = start_normal.Cross(end_normal) > 0 ? -1 : 1; + vtx.vertex_normal = start_normal * dir; + vtx_builder.AppendVertex(vtx); + vtx.vertex_normal = end_normal * dir; + vtx_builder.AppendVertex(vtx); +} + static VertexBuffer CreateSolidStrokeVertices(const Path& path, HostBuffer& buffer) { using VS = SolidStrokeVertexShader; @@ -300,43 +324,89 @@ static VertexBuffer CreateSolidStrokeVertices(const Path& path, VertexBufferBuilder vtx_builder; auto polyline = path.CreatePolyline(); - for (size_t i = 0, polyline_size = polyline.points.size(); i < polyline_size; - i++) { - const auto is_last_point = i == polyline_size - 1; - - const auto& p1 = polyline.points[i]; - const auto& p2 = - is_last_point ? polyline.points[i - 1] : polyline.points[i + 1]; - - const auto diff = p2 - p1; - - const Scalar direction = is_last_point ? -1.0 : 1.0; + size_t point_i = 0; + if (polyline.points.size() < 2) { + return {}; // Nothing to render. + } - const auto normal = - Point{-diff.y * direction, diff.x * direction}.Normalize(); + VS::PerVertexData vtx; + + // Cursor state. + Point direction; + Point normal; + Point previous_normal; // Used for computing joins. + auto compute_normals = [&](size_t point_i) { + previous_normal = normal; + direction = + (polyline.points[point_i] - polyline.points[point_i - 1]).Normalize(); + normal = {-direction.y, direction.x}; + }; + compute_normals(1); + + // Break state. + auto breaks_it = polyline.breaks.begin(); + size_t break_end = + breaks_it != polyline.breaks.end() ? *breaks_it : polyline.points.size(); + + while (point_i < polyline.points.size()) { + if (point_i > 0) { + compute_normals(point_i); + + // This branch only executes when we've just finished drawing a contour + // and are switching to a new one. + // We're drawing a triangle strip, so we need to "pick up the pen" by + // appending transparent vertices between the end of the previous contour + // and the beginning of the new contour. + vtx.vertex_position = polyline.points[point_i - 1]; + vtx.vertex_normal = {}; + vtx.pen_down = 0.0; + vtx_builder.AppendVertex(vtx); + vtx.vertex_position = polyline.points[point_i]; + vtx_builder.AppendVertex(vtx); + } - VS::PerVertexData vtx; - vtx.vertex_position = p1; - auto pen_down = - polyline.breaks.find(i) == polyline.breaks.end() ? 1.0 : 0.0; + // Generate start cap. + CreateCap(vtx_builder, polyline.points[point_i], -direction); + + // Generate contour geometry. + size_t contour_point_i = 0; + while (point_i < break_end) { + if (contour_point_i > 0) { + if (contour_point_i > 1) { + // Generate join from the previous line to the current line. + CreateJoin(vtx_builder, polyline.points[point_i - 1], previous_normal, + normal); + } else { + compute_normals(point_i); + } + + // Generate line rect. + vtx.vertex_position = polyline.points[point_i - 1]; + vtx.pen_down = 1.0; + vtx.vertex_normal = normal; + vtx_builder.AppendVertex(vtx); + vtx.vertex_normal = -normal; + vtx_builder.AppendVertex(vtx); + vtx.vertex_position = polyline.points[point_i]; + vtx.vertex_normal = normal; + vtx_builder.AppendVertex(vtx); + vtx.vertex_normal = -normal; + vtx_builder.AppendVertex(vtx); - vtx.vertex_normal = normal; - vtx.pen_down = pen_down; - vtx_builder.AppendVertex(vtx); + compute_normals(point_i + 1); + } - vtx.vertex_normal = -normal; - vtx.pen_down = pen_down; - vtx_builder.AppendVertex(vtx); + ++contour_point_i; + ++point_i; + } - // Put the pen down again for the next contour. - if (!pen_down) { - vtx.vertex_normal = normal; - vtx.pen_down = 1.0; - vtx_builder.AppendVertex(vtx); + // Generate end cap. + CreateCap(vtx_builder, polyline.points[point_i - 1], -direction); - vtx.vertex_normal = -normal; - vtx.pen_down = 1.0; - vtx_builder.AppendVertex(vtx); + if (break_end < polyline.points.size()) { + ++breaks_it; + break_end = breaks_it != polyline.breaks.end() ? *breaks_it + : polyline.points.size(); } } @@ -384,6 +454,30 @@ Scalar SolidStrokeContents::GetStrokeSize() const { return stroke_size_; } +void SolidStrokeContents::SetStrokeMiter(Scalar miter) { + miter_ = miter; +} + +Scalar SolidStrokeContents::GetStrokeMiter(Scalar miter) { + return miter_; +} + +void SolidStrokeContents::SetStrokeCap(Cap cap) { + cap_ = cap; +} + +SolidStrokeContents::Cap SolidStrokeContents::GetStrokeCap() { + return cap_; +} + +void SolidStrokeContents::SetStrokeJoin(Join join) { + join_ = join; +} + +SolidStrokeContents::Join SolidStrokeContents::GetStrokeJoin() { + return join_; +} + /******************************************************************************* ******* ClipContents ******************************************************************************/ diff --git a/impeller/entity/contents.h b/impeller/entity/contents.h index f80b2375bf952..dbdffb231d08b 100644 --- a/impeller/entity/contents.h +++ b/impeller/entity/contents.h @@ -113,6 +113,20 @@ class TextureContents final : public Contents { class SolidStrokeContents final : public Contents { public: + enum class Cap { + kButt, + kRound, + kSquare, + kLast, + }; + + enum class Join { + kMiter, + kRound, + kBevel, + kLast, + }; + SolidStrokeContents(); ~SolidStrokeContents() override; @@ -125,6 +139,18 @@ class SolidStrokeContents final : public Contents { Scalar GetStrokeSize() const; + void SetStrokeMiter(Scalar miter); + + Scalar GetStrokeMiter(Scalar miter); + + void SetStrokeCap(Cap cap); + + Cap GetStrokeCap(); + + void SetStrokeJoin(Join join); + + Join GetStrokeJoin(); + // |Contents| bool Render(const ContentContext& renderer, const Entity& entity, @@ -133,6 +159,9 @@ class SolidStrokeContents final : public Contents { private: Color color_; Scalar stroke_size_ = 0.0; + Scalar miter_ = 0.0; + Cap cap_ = Cap::kButt; + Join join_ = Join::kMiter; FML_DISALLOW_COPY_AND_ASSIGN(SolidStrokeContents); }; diff --git a/impeller/entity/entity_unittests.cc b/impeller/entity/entity_unittests.cc index a69a5cab934fe..29b784e1c4f5e 100644 --- a/impeller/entity/entity_unittests.cc +++ b/impeller/entity/entity_unittests.cc @@ -7,6 +7,7 @@ #include "impeller/entity/entity_playground.h" #include "impeller/geometry/path_builder.h" #include "impeller/playground/playground.h" +#include "impeller/playground/widgets.h" namespace impeller { namespace testing { @@ -45,25 +46,36 @@ TEST_F(EntityTest, ThreeStrokesInOnePath) { } TEST_F(EntityTest, TriangleInsideASquare) { - Path path = PathBuilder{} - .MoveTo({10, 10}) - .LineTo({210, 10}) - .LineTo({210, 210}) - .LineTo({10, 210}) - .Close() - .MoveTo({50, 50}) - .LineTo({100, 50}) - .LineTo({50, 150}) - .Close() - .TakePath(); + auto callback = [&](ContentContext& context, RenderPass& pass) { + Point a = IMPELLER_PLAYGROUND_POINT(Point(10, 10), 20, Color::White()); + Point b = IMPELLER_PLAYGROUND_POINT(Point(210, 10), 20, Color::White()); + Point c = IMPELLER_PLAYGROUND_POINT(Point(210, 210), 20, Color::White()); + Point d = IMPELLER_PLAYGROUND_POINT(Point(10, 210), 20, Color::White()); + Point e = IMPELLER_PLAYGROUND_POINT(Point(50, 50), 20, Color::White()); + Point f = IMPELLER_PLAYGROUND_POINT(Point(100, 50), 20, Color::White()); + Point g = IMPELLER_PLAYGROUND_POINT(Point(50, 150), 20, Color::White()); + Path path = PathBuilder{} + .MoveTo(a) + .LineTo(b) + .LineTo(c) + .LineTo(d) + .Close() + .MoveTo(e) + .LineTo(f) + .LineTo(g) + .Close() + .TakePath(); - Entity entity; - entity.SetPath(path); - auto contents = std::make_unique(); - contents->SetColor(Color::Red()); - contents->SetStrokeSize(5.0); - entity.SetContents(std::move(contents)); - ASSERT_TRUE(OpenPlaygroundHere(entity)); + Entity entity; + entity.SetPath(path); + auto contents = std::make_unique(); + contents->SetColor(Color::Red()); + contents->SetStrokeSize(20.0); + entity.SetContents(std::move(contents)); + + return entity.Render(context, pass); + }; + ASSERT_TRUE(OpenPlaygroundHere(callback)); } TEST_F(EntityTest, CubicCurveTest) { From 676628856c6b256fd905d511c69e285ff93d23bf Mon Sep 17 00:00:00 2001 From: Chinmay Garde Date: Fri, 25 Feb 2022 14:50:32 -0800 Subject: [PATCH 310/433] Add `//impeller/typographer`. (#36) Renders shaped text frames. Has the ability to plug into to different text shapers and render glyphs using different techniques. For now, the Aiks layer expects a prepared glyph atlas. But this will be changed so that render pass will be responsible for preparing these and setting these on the content renderer after pass consolidation. --- impeller/BUILD.gn | 2 + impeller/aiks/aiks_unittests.cc | 112 ++++++++ impeller/aiks/canvas.cc | 21 ++ impeller/aiks/canvas.h | 5 + impeller/aiks/paint.cc | 4 + impeller/aiks/paint.h | 1 + impeller/base/BUILD.gn | 1 + impeller/base/allocation.cc | 2 +- impeller/base/allocation.h | 2 + .../backend/metal => base}/backend_cast.h | 0 .../display_list/display_list_dispatcher.cc | 60 ++++- impeller/entity/BUILD.gn | 3 + impeller/entity/content_context.cc | 1 + impeller/entity/content_context.h | 9 + impeller/entity/contents.cc | 117 ++++++++ impeller/entity/contents.h | 26 ++ impeller/entity/shaders/glyph_atlas.frag | 22 ++ impeller/entity/shaders/glyph_atlas.vert | 40 +++ impeller/fixtures/BUILD.gn | 3 + impeller/geometry/color.h | 11 + impeller/geometry/rect.h | 16 ++ impeller/geometry/size.h | 4 + impeller/playground/playground.mm | 3 +- impeller/renderer/BUILD.gn | 1 - impeller/renderer/backend/metal/context_mtl.h | 5 +- .../renderer/backend/metal/context_mtl.mm | 9 +- .../backend/metal/device_buffer_mtl.h | 2 +- impeller/renderer/backend/metal/formats_mtl.h | 4 + .../renderer/backend/metal/pipeline_mtl.h | 2 +- .../renderer/backend/metal/render_pass_mtl.mm | 1 + .../backend/metal/sampler_library_mtl.h | 2 +- .../backend/metal/sampler_library_mtl.mm | 5 + impeller/renderer/backend/metal/sampler_mtl.h | 2 +- .../backend/metal/shader_function_mtl.h | 2 +- .../renderer/backend/metal/surface_mtl.mm | 2 +- impeller/renderer/backend/metal/texture_mtl.h | 2 +- .../backend/metal/vertex_descriptor_mtl.h | 2 +- impeller/renderer/formats.h | 2 + impeller/renderer/sampler_descriptor.h | 2 + impeller/typographer/BUILD.gn | 50 ++++ .../backends/skia/text_frame_skia.cc | 69 +++++ .../backends/skia/text_frame_skia.h | 15 ++ .../backends/skia/text_render_context_skia.cc | 252 ++++++++++++++++++ .../backends/skia/text_render_context_skia.h | 26 ++ .../backends/skia/typeface_skia.cc | 46 ++++ .../typographer/backends/skia/typeface_skia.h | 42 +++ impeller/typographer/font.cc | 48 ++++ impeller/typographer/font.h | 96 +++++++ impeller/typographer/font_glyph_pair.cc | 11 + impeller/typographer/font_glyph_pair.h | 44 +++ impeller/typographer/glyph.cc | 11 + impeller/typographer/glyph.h | 38 +++ impeller/typographer/glyph_atlas.cc | 59 ++++ impeller/typographer/glyph_atlas.h | 99 +++++++ impeller/typographer/text_frame.cc | 29 ++ impeller/typographer/text_frame.h | 51 ++++ impeller/typographer/text_render_context.cc | 27 ++ impeller/typographer/text_render_context.h | 63 +++++ impeller/typographer/text_run.cc | 39 +++ impeller/typographer/text_run.h | 77 ++++++ impeller/typographer/typeface.cc | 13 + impeller/typographer/typeface.h | 40 +++ impeller/typographer/typographer_unittests.cc | 46 ++++ 63 files changed, 1778 insertions(+), 23 deletions(-) rename impeller/{renderer/backend/metal => base}/backend_cast.h (100%) create mode 100644 impeller/entity/shaders/glyph_atlas.frag create mode 100644 impeller/entity/shaders/glyph_atlas.vert create mode 100644 impeller/typographer/BUILD.gn create mode 100644 impeller/typographer/backends/skia/text_frame_skia.cc create mode 100644 impeller/typographer/backends/skia/text_frame_skia.h create mode 100644 impeller/typographer/backends/skia/text_render_context_skia.cc create mode 100644 impeller/typographer/backends/skia/text_render_context_skia.h create mode 100644 impeller/typographer/backends/skia/typeface_skia.cc create mode 100644 impeller/typographer/backends/skia/typeface_skia.h create mode 100644 impeller/typographer/font.cc create mode 100644 impeller/typographer/font.h create mode 100644 impeller/typographer/font_glyph_pair.cc create mode 100644 impeller/typographer/font_glyph_pair.h create mode 100644 impeller/typographer/glyph.cc create mode 100644 impeller/typographer/glyph.h create mode 100644 impeller/typographer/glyph_atlas.cc create mode 100644 impeller/typographer/glyph_atlas.h create mode 100644 impeller/typographer/text_frame.cc create mode 100644 impeller/typographer/text_frame.h create mode 100644 impeller/typographer/text_render_context.cc create mode 100644 impeller/typographer/text_render_context.h create mode 100644 impeller/typographer/text_run.cc create mode 100644 impeller/typographer/text_run.h create mode 100644 impeller/typographer/typeface.cc create mode 100644 impeller/typographer/typeface.h create mode 100644 impeller/typographer/typographer_unittests.cc diff --git a/impeller/BUILD.gn b/impeller/BUILD.gn index f46067dd58eca..d3b6bb6de7192 100644 --- a/impeller/BUILD.gn +++ b/impeller/BUILD.gn @@ -21,6 +21,7 @@ group("impeller") { "geometry", "image", "renderer", + "typographer", ] if (is_host) { @@ -43,5 +44,6 @@ executable("impeller_unittests") { "image:image_unittests", "playground", "renderer:renderer_unittests", + "typographer:typographer_unittests", ] } diff --git a/impeller/aiks/aiks_unittests.cc b/impeller/aiks/aiks_unittests.cc index ca96b494c4a86..5531beee475d1 100644 --- a/impeller/aiks/aiks_unittests.cc +++ b/impeller/aiks/aiks_unittests.cc @@ -8,6 +8,9 @@ #include "impeller/aiks/image.h" #include "impeller/geometry/geometry_unittests.h" #include "impeller/geometry/path_builder.h" +#include "impeller/typographer/backends/skia/text_frame_skia.h" +#include "impeller/typographer/backends/skia/text_render_context_skia.h" +#include "third_party/skia/include/core/SkData.h" namespace impeller { namespace testing { @@ -283,5 +286,114 @@ TEST_F(AiksTest, CanRenderDifferencePaths) { ASSERT_TRUE(OpenPlaygroundHere(canvas.EndRecordingAsPicture())); } +static sk_sp OpenFixtureAsSkData(const char* fixture_name) { + auto mapping = flutter::testing::OpenFixtureAsMapping(fixture_name); + if (!mapping) { + return nullptr; + } + return SkData::MakeWithProc( + mapping->GetMapping(), mapping->GetSize(), + [](const void* ptr, void* context) { + delete reinterpret_cast(context); + }, + mapping.release()); +} + +TEST_F(AiksTest, CanRenderTextFrame) { + Canvas canvas; + + Scalar baseline = 200.0; + Point text_position = {100, baseline}; + + // Draw the baseline. + canvas.DrawRect({50, baseline, 900, 10}, + Paint{.color = Color::Aqua().WithAlpha(0.25)}); + + // Mark the point at which the text is drawn. + canvas.DrawCircle(text_position, 5.0, + Paint{.color = Color::Red().WithAlpha(0.25)}); + + // Construct the text blob. + auto mapping = OpenFixtureAsSkData("Roboto-Regular.ttf"); + ASSERT_TRUE(mapping); + SkFont sk_font(SkTypeface::MakeFromData(mapping), 50.0); + auto blob = SkTextBlob::MakeFromString( + "the quick brown fox jumped over the lazy dog!.?", sk_font); + ASSERT_TRUE(blob); + + // Create the Impeller text frame and draw it at the designated baseline. + auto frame = TextFrameFromTextBlob(blob); + TextRenderContextSkia text_context(GetContext()); + ASSERT_TRUE(text_context.IsValid()); + auto atlas = text_context.CreateGlyphAtlas(frame); + ASSERT_NE(atlas, nullptr); + canvas.DrawTextFrame(std::move(frame), std::move(atlas), text_position); + ASSERT_TRUE(OpenPlaygroundHere(canvas.EndRecordingAsPicture())); +} + +TEST_F(AiksTest, CanRenderItalicizedText) { + Canvas canvas; + + Scalar baseline = 200.0; + Point text_position = {100, baseline}; + + // Draw the baseline. + canvas.DrawRect({50, baseline, 900, 10}, + Paint{.color = Color::Aqua().WithAlpha(0.25)}); + + // Mark the point at which the text is drawn. + canvas.DrawCircle(text_position, 5.0, + Paint{.color = Color::Red().WithAlpha(0.25)}); + + // Construct the text blob. + auto mapping = OpenFixtureAsSkData("HomemadeApple.ttf"); + ASSERT_TRUE(mapping); + SkFont sk_font(SkTypeface::MakeFromData(mapping), 50.0); + auto blob = SkTextBlob::MakeFromString( + "the quick brown fox jumped over the lazy dog!.?", sk_font); + ASSERT_TRUE(blob); + + // Create the Impeller text frame and draw it at the designated baseline. + auto frame = TextFrameFromTextBlob(blob); + TextRenderContextSkia text_context(GetContext()); + ASSERT_TRUE(text_context.IsValid()); + auto atlas = text_context.CreateGlyphAtlas(frame); + ASSERT_NE(atlas, nullptr); + canvas.DrawTextFrame(std::move(frame), std::move(atlas), text_position); + ASSERT_TRUE(OpenPlaygroundHere(canvas.EndRecordingAsPicture())); +} + +TEST_F(AiksTest, CanRenderEmojiTextFrame) { + Canvas canvas; + + Scalar baseline = 200.0; + Point text_position = {100, baseline}; + + // Draw the baseline. + canvas.DrawRect({50, baseline, 900, 10}, + Paint{.color = Color::Aqua().WithAlpha(0.25)}); + + // Mark the point at which the text is drawn. + canvas.DrawCircle(text_position, 5.0, + Paint{.color = Color::Red().WithAlpha(0.25)}); + + // Construct the text blob. + auto mapping = OpenFixtureAsSkData("NotoColorEmoji.ttf"); + ASSERT_TRUE(mapping); + SkFont sk_font(SkTypeface::MakeFromData(mapping), 50.0); + auto blob = SkTextBlob::MakeFromString( + "😀 😃 😄 😁 😆 😅 😂 🤣 🥲 ☺️ 😊", sk_font); + ASSERT_TRUE(blob); + + // Create the Impeller text frame and draw it at the designated baseline. + auto frame = TextFrameFromTextBlob(blob); + TextRenderContextSkia text_context(GetContext()); + ASSERT_TRUE(text_context.IsValid()); + auto atlas = text_context.CreateGlyphAtlas(frame); + ASSERT_NE(atlas, nullptr); + canvas.DrawTextFrame(std::move(frame), std::move(atlas), text_position); + ASSERT_TRUE(OpenPlaygroundHere(canvas.EndRecordingAsPicture())); +} + } // namespace testing } // namespace impeller diff --git a/impeller/aiks/canvas.cc b/impeller/aiks/canvas.cc index 0825d5c2bac4d..0a6e214de9dc9 100644 --- a/impeller/aiks/canvas.cc +++ b/impeller/aiks/canvas.cc @@ -252,4 +252,25 @@ void Canvas::Save(bool create_subpass) { xformation_stack_.emplace_back(std::move(entry)); } +void Canvas::DrawTextFrame(TextFrame text_frame, + std::shared_ptr atlas, + Point position) { + if (!atlas || !atlas->IsValid()) { + return; + } + + auto text_contents = std::make_shared(); + text_contents->SetTextFrame(std::move(text_frame)); + text_contents->SetGlyphAtlas(std::move(atlas)); + + Entity entity; + entity.SetTransformation(GetCurrentTransformation() * + Matrix::MakeTranslation(position)); + entity.SetPath({}); + entity.SetStencilDepth(GetStencilDepth()); + entity.SetContents(std::move(text_contents)); + + GetCurrentPass().AddEntity(std::move(entity)); +} + } // namespace impeller diff --git a/impeller/aiks/canvas.h b/impeller/aiks/canvas.h index 367669eccbdb8..88db60911ed66 100644 --- a/impeller/aiks/canvas.h +++ b/impeller/aiks/canvas.h @@ -18,6 +18,7 @@ #include "impeller/geometry/path.h" #include "impeller/geometry/point.h" #include "impeller/geometry/vector.h" +#include "impeller/typographer/text_frame.h" namespace impeller { @@ -74,6 +75,10 @@ class Canvas { void DrawPicture(Picture picture); + void DrawTextFrame(TextFrame text_frame, + std::shared_ptr atlas, + Point position); + Picture EndRecordingAsPicture(); private: diff --git a/impeller/aiks/paint.cc b/impeller/aiks/paint.cc index ca1f6169f212d..6f2eea50cce7d 100644 --- a/impeller/aiks/paint.cc +++ b/impeller/aiks/paint.cc @@ -7,6 +7,10 @@ namespace impeller { std::shared_ptr Paint::CreateContentsForEntity() const { + if (contents) { + return contents; + } + switch (style) { case Style::kFill: { auto solid_color = std::make_shared(); diff --git a/impeller/aiks/paint.h b/impeller/aiks/paint.h index 6334e42471375..7a941474a7125 100644 --- a/impeller/aiks/paint.h +++ b/impeller/aiks/paint.h @@ -21,6 +21,7 @@ struct Paint { Color color = Color::Black(); Scalar stroke_width = 0.0; Style style = Style::kFill; + std::shared_ptr contents; std::shared_ptr CreateContentsForEntity() const; }; diff --git a/impeller/base/BUILD.gn b/impeller/base/BUILD.gn index ab988b1adc31f..0a4ba5fcec5f4 100644 --- a/impeller/base/BUILD.gn +++ b/impeller/base/BUILD.gn @@ -8,6 +8,7 @@ impeller_component("base") { sources = [ "allocation.cc", "allocation.h", + "backend_cast.h", "base.h", "config.h", "promise.cc", diff --git a/impeller/base/allocation.cc b/impeller/base/allocation.cc index d3c8602e104d8..0a66a24e74de5 100644 --- a/impeller/base/allocation.cc +++ b/impeller/base/allocation.cc @@ -38,7 +38,7 @@ bool Allocation::Truncate(size_t length, bool npot) { return true; } -static uint32_t NextPowerOfTwoSize(uint32_t x) { +uint32_t Allocation::NextPowerOfTwoSize(uint32_t x) { if (x == 0) { return 1; } diff --git a/impeller/base/allocation.h b/impeller/base/allocation.h index f707e089298d3..ffb202562ab18 100644 --- a/impeller/base/allocation.h +++ b/impeller/base/allocation.h @@ -25,6 +25,8 @@ class Allocation { [[nodiscard]] bool Truncate(size_t length, bool npot = true); + static uint32_t NextPowerOfTwoSize(uint32_t x); + private: uint8_t* buffer_ = nullptr; size_t length_ = 0; diff --git a/impeller/renderer/backend/metal/backend_cast.h b/impeller/base/backend_cast.h similarity index 100% rename from impeller/renderer/backend/metal/backend_cast.h rename to impeller/base/backend_cast.h diff --git a/impeller/display_list/display_list_dispatcher.cc b/impeller/display_list/display_list_dispatcher.cc index 9fdf58b4ba87b..ddeabcffa3739 100644 --- a/impeller/display_list/display_list_dispatcher.cc +++ b/impeller/display_list/display_list_dispatcher.cc @@ -6,6 +6,9 @@ #include "flutter/fml/trace_event.h" #include "impeller/geometry/path_builder.h" +#include "impeller/typographer/backends/skia/text_frame_skia.h" +#include "third_party/skia/include/core/SkColor.h" +#include "third_party/skia/include/core/SkShader.h" namespace impeller { @@ -72,8 +75,56 @@ void DisplayListDispatcher::setStrokeJoin(SkPaint::Join join) { UNIMPLEMENTED; } +static Point ToPoint(const SkPoint& point) { + return Point::MakeXY(point.fX, point.fY); +} + +static Color ToColor(const SkColor& color) { + return { + static_cast(SkColorGetR(color) / 255.0), // + static_cast(SkColorGetG(color) / 255.0), // + static_cast(SkColorGetB(color) / 255.0), // + static_cast(SkColorGetA(color) / 255.0) // + }; +} + // |flutter::Dispatcher| void DisplayListDispatcher::setShader(sk_sp shader) { + if (!shader) { + return; + } + + { + SkShader::GradientInfo info = {}; + constexpr auto kColorsArrayCount = 2u; + info.fColorCount = kColorsArrayCount; + SkColor sk_colors[kColorsArrayCount]; + info.fColors = sk_colors; + auto gradient_type = shader->asAGradient(&info); + switch (gradient_type) { + case SkShader::kLinear_GradientType: { + auto contents = std::make_shared(); + contents->SetEndPoints(ToPoint(info.fPoint[0]), + ToPoint(info.fPoint[1])); + std::vector colors; + for (auto i = 0; i < info.fColorCount; i++) { + colors.emplace_back(ToColor(sk_colors[i])); + } + contents->SetColors(std::move(colors)); + paint_.contents = std::move(contents); + return; + } break; + case SkShader::kNone_GradientType: + case SkShader::kColor_GradientType: + case SkShader::kRadial_GradientType: + case SkShader::kSweep_GradientType: + case SkShader::kConical_GradientType: + default: + UNIMPLEMENTED; + break; + } + } + // Needs https://github.com/flutter/flutter/issues/95434 UNIMPLEMENTED; } @@ -154,9 +205,8 @@ static std::optional ToRect(const SkRect* rect) { // |flutter::Dispatcher| void DisplayListDispatcher::saveLayer(const SkRect* bounds, const flutter::SaveLayerOptions options) { - canvas_.SaveLayer( - options.renders_with_attributes() ? paint_ : Paint{}, - ToRect(bounds)); + canvas_.SaveLayer(options.renders_with_attributes() ? paint_ : Paint{}, + ToRect(bounds)); } // |flutter::Dispatcher| @@ -242,10 +292,6 @@ void DisplayListDispatcher::clipRect(const SkRect& rect, canvas_.ClipPath(std::move(path)); } -static Point ToPoint(const SkVector& vector) { - return {vector.fX, vector.fY}; -} - static PathBuilder::RoundingRadii ToRoundingRadii(const SkRRect& rrect) { using Corner = SkRRect::Corner; PathBuilder::RoundingRadii radii; diff --git a/impeller/entity/BUILD.gn b/impeller/entity/BUILD.gn index 7badc02495d46..6a01accfba421 100644 --- a/impeller/entity/BUILD.gn +++ b/impeller/entity/BUILD.gn @@ -16,6 +16,8 @@ impeller_shaders("entity_shaders") { "shaders/solid_stroke.vert", "shaders/texture_fill.frag", "shaders/texture_fill.vert", + "shaders/glyph_atlas.frag", + "shaders/glyph_atlas.vert", ] } @@ -38,6 +40,7 @@ impeller_component("entity") { "../archivist", "../image", "../renderer", + "../typographer", ] } diff --git a/impeller/entity/content_context.cc b/impeller/entity/content_context.cc index 53fd91745f6ac..0d4b23de76da5 100644 --- a/impeller/entity/content_context.cc +++ b/impeller/entity/content_context.cc @@ -21,6 +21,7 @@ ContentContext::ContentContext(std::shared_ptr context) texture_pipelines_[{}] = std::make_unique(*context_); solid_stroke_pipelines_[{}] = std::make_unique(*context_); + glyph_atlas_pipelines_[{}] = std::make_unique(*context_); // Pipelines that are variants of the base pipelines with custom descriptors. // TODO(98684): Rework this API to allow fetching the descriptor without diff --git a/impeller/entity/content_context.h b/impeller/entity/content_context.h index 78d21e5c6340a..a3b0ec270e70c 100644 --- a/impeller/entity/content_context.h +++ b/impeller/entity/content_context.h @@ -9,6 +9,8 @@ #include "flutter/fml/hash_combine.h" #include "flutter/fml/macros.h" +#include "flutter/impeller/entity/glyph_atlas.frag.h" +#include "flutter/impeller/entity/glyph_atlas.vert.h" #include "flutter/impeller/entity/gradient_fill.frag.h" #include "flutter/impeller/entity/gradient_fill.vert.h" #include "flutter/impeller/entity/solid_fill.frag.h" @@ -29,6 +31,8 @@ using TexturePipeline = PipelineT; using SolidStrokePipeline = PipelineT; +using GlyphAtlasPipeline = + PipelineT; // Instead of requiring new shaders for clips, the solid fill stages are used // to redirect writing to the stencil instead of color attachments. using ClipPipeline = PipelineT; @@ -81,6 +85,10 @@ class ContentContext { return GetPipeline(clip_restoration_pipelines_, opts); } + std::shared_ptr GetGlyphAtlasPipeline(Options opts) const { + return GetPipeline(glyph_atlas_pipelines_, opts); + } + std::shared_ptr GetContext() const; private: @@ -99,6 +107,7 @@ class ContentContext { mutable Variants solid_stroke_pipelines_; mutable Variants clip_pipelines_; mutable Variants clip_restoration_pipelines_; + mutable Variants glyph_atlas_pipelines_; static void ApplyOptionsToDescriptor(PipelineDescriptor& desc, const Options& options) { diff --git a/impeller/entity/contents.cc b/impeller/entity/contents.cc index 8f2ad028785a2..6288d3b3c118e 100644 --- a/impeller/entity/contents.cc +++ b/impeller/entity/contents.cc @@ -551,4 +551,121 @@ bool ClipRestoreContents::Render(const ContentContext& renderer, return true; } +/******************************************************************************* + ******* TextContents + ******************************************************************************/ + +TextContents::TextContents() = default; + +TextContents::~TextContents() = default; + +void TextContents::SetTextFrame(TextFrame frame) { + frame_ = std::move(frame); +} + +void TextContents::SetGlyphAtlas(std::shared_ptr atlas) { + atlas_ = std::move(atlas); +} + +bool TextContents::Render(const ContentContext& renderer, + const Entity& entity, + RenderPass& pass) const { + if (!atlas_ || !atlas_->IsValid()) { + VALIDATION_LOG << "Cannot render glyphs without prepared atlas."; + return false; + } + + using VS = GlyphAtlasPipeline::VertexShader; + using FS = GlyphAtlasPipeline::FragmentShader; + + // Information shared by all glyph draw calls. + Command cmd; + cmd.label = "Glyph"; + cmd.primitive_type = PrimitiveType::kTriangle; + cmd.pipeline = renderer.GetGlyphAtlasPipeline(OptionsFromPass(pass)); + cmd.stencil_reference = entity.GetStencilDepth(); + + // Common vertex uniforms for all glyphs. + VS::FrameInfo frame_info; + frame_info.mvp = Matrix::MakeOrthographic(pass.GetRenderTargetSize()) * + entity.GetTransformation(); + frame_info.atlas_size = + Point{static_cast(atlas_->GetTexture()->GetSize().width), + static_cast(atlas_->GetTexture()->GetSize().height)}; + VS::BindFrameInfo(cmd, pass.GetTransientsBuffer().EmplaceUniform(frame_info)); + + // Common fragment uniforms for all glyphs. + FS::BindGlyphAtlasSampler( + cmd, // command + atlas_->GetTexture(), // texture + renderer.GetContext()->GetSamplerLibrary()->GetSampler({}) // sampler + ); + + // Common vertex information for all glyphs. + // Currently, glyphs are being drawn individually. This can be batched later. + // But we don't want to give each glyph unique vertex information. So all + // glyphs are given the same vertex information in the form of a unit-sized + // quad. The size of the glyph is specified in uniform data and the vertex + // shader uses this to size the glyph correctly. The interpolated vertex + // information is also used in the fragment shader to sample from the glyph + // atlas. + { + VertexBufferBuilder vertex_builder; + if (!Tessellator{FillType::kPositive}.Tessellate( + PathBuilder{} + .AddRect(Rect::MakeXYWH(0.0, 0.0, 1.0, 1.0)) + .TakePath() + .CreatePolyline(), + [&vertex_builder](Point point) { + VS::PerVertexData vtx; + vtx.unit_vertex = point; + vertex_builder.AppendVertex(std::move(vtx)); + })) { + return false; + } + auto dummy = vertex_builder.CreateVertexBuffer(pass.GetTransientsBuffer()); + auto vertex_buffer = dummy; + if (!vertex_buffer) { + return false; + } + cmd.BindVertices(std::move(vertex_buffer)); + } + + // Iterate through all the runs in the blob. + for (const auto& run : frame_.GetRuns()) { + auto font = run.GetFont(); + auto glyph_size = font.GetGlyphSize(); + if (!glyph_size.has_value()) { + VALIDATION_LOG << "Glyph has no size."; + return false; + } + // Draw each glyph individually. This should probably be batched. + for (const auto& glyph_position : run.GetGlyphPositions()) { + FontGlyphPair font_glyph_pair{font, glyph_position.glyph}; + auto atlas_glyph_pos = atlas_->FindFontGlyphPosition(font_glyph_pair); + if (!atlas_glyph_pos.has_value()) { + VALIDATION_LOG << "Could not find glyph position in the atlas."; + return false; + } + + VS::GlyphInfo glyph_info; + glyph_info.position = glyph_position.position.Translate( + {0.0, font.GetMetrics().ascent, 0.0}); + glyph_info.glyph_size = {static_cast(glyph_size->width), + static_cast(glyph_size->height)}; + glyph_info.atlas_position = atlas_glyph_pos->origin; + glyph_info.atlas_glyph_size = {atlas_glyph_pos->size.width, + atlas_glyph_pos->size.height}; + VS::BindGlyphInfo(cmd, + pass.GetTransientsBuffer().EmplaceUniform(glyph_info)); + + if (!pass.AddCommand(cmd)) { + return false; + } + } + } + + return true; +} + } // namespace impeller diff --git a/impeller/entity/contents.h b/impeller/entity/contents.h index dbdffb231d08b..5488f6d3a8670 100644 --- a/impeller/entity/contents.h +++ b/impeller/entity/contents.h @@ -12,6 +12,8 @@ #include "impeller/geometry/point.h" #include "impeller/geometry/rect.h" #include "impeller/renderer/texture.h" +#include "impeller/typographer/glyph_atlas.h" +#include "impeller/typographer/text_frame.h" namespace impeller { @@ -187,6 +189,8 @@ class ClipRestoreContents final : public Contents { ~ClipRestoreContents(); + void SetGlyphAtlas(std::shared_ptr atlas); + // |Contents| bool Render(const ContentContext& renderer, const Entity& entity, @@ -196,4 +200,26 @@ class ClipRestoreContents final : public Contents { FML_DISALLOW_COPY_AND_ASSIGN(ClipRestoreContents); }; +class TextContents final : public Contents { + public: + TextContents(); + + ~TextContents(); + + void SetTextFrame(TextFrame frame); + + void SetGlyphAtlas(std::shared_ptr atlas); + + // |Contents| + bool Render(const ContentContext& renderer, + const Entity& entity, + RenderPass& pass) const override; + + private: + TextFrame frame_; + std::shared_ptr atlas_; + + FML_DISALLOW_COPY_AND_ASSIGN(TextContents); +}; + } // namespace impeller diff --git a/impeller/entity/shaders/glyph_atlas.frag b/impeller/entity/shaders/glyph_atlas.frag new file mode 100644 index 0000000000000..0fedba1c140b1 --- /dev/null +++ b/impeller/entity/shaders/glyph_atlas.frag @@ -0,0 +1,22 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +uniform sampler2D glyph_atlas_sampler; + +in vec2 v_unit_vertex; +in vec2 v_atlas_position; +in vec2 v_atlas_glyph_size; +in vec2 v_atlas_size; + +out vec4 frag_color; + +void main() { + vec2 scale_perspective = v_atlas_glyph_size / v_atlas_size; + vec2 offset = v_atlas_position / v_atlas_size; + + frag_color = texture( + glyph_atlas_sampler, + v_unit_vertex * scale_perspective + offset + ); +} diff --git a/impeller/entity/shaders/glyph_atlas.vert b/impeller/entity/shaders/glyph_atlas.vert new file mode 100644 index 0000000000000..7cff4886b72bd --- /dev/null +++ b/impeller/entity/shaders/glyph_atlas.vert @@ -0,0 +1,40 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +uniform FrameInfo { + mat4 mvp; + vec2 atlas_size; +} frame_info; + +uniform GlyphInfo { + mat4 position; + vec2 glyph_size; + vec2 atlas_position; + vec2 atlas_glyph_size; +} glyph_info; + +in vec2 unit_vertex; + +out vec2 v_unit_vertex; +out vec2 v_atlas_position; +out vec2 v_atlas_glyph_size; +out vec2 v_atlas_size; + +void main() { + mat4 scale = mat4( + glyph_info.glyph_size.x, 0.0, 0.0, 0.0, + 0.0, glyph_info.glyph_size.y, 0.0, 0.0, + 0.0, 0.0, 1.0 , 0.0, + 0.0, 0.0, 0.0 , 1.0 + ); + gl_Position = frame_info.mvp + * glyph_info.position + * scale + * vec4(unit_vertex, 0.0, 1.0); + + v_unit_vertex = unit_vertex; + v_atlas_position = glyph_info.atlas_position; + v_atlas_glyph_size = glyph_info.atlas_glyph_size; + v_atlas_size = frame_info.atlas_size; +} diff --git a/impeller/fixtures/BUILD.gn b/impeller/fixtures/BUILD.gn index 11640f79c3c09..db56ad66fb679 100644 --- a/impeller/fixtures/BUILD.gn +++ b/impeller/fixtures/BUILD.gn @@ -26,6 +26,9 @@ test_fixtures("file_fixtures") { "boston.jpg", "embarcadero.jpg", "kalimba.jpg", + "//flutter/third_party/txt/third_party/fonts/Roboto-Regular.ttf", + "//flutter/third_party/txt/third_party/fonts/NotoColorEmoji.ttf", + "//flutter/third_party/txt/third_party/fonts/HomemadeApple.ttf", ] } diff --git a/impeller/geometry/color.h b/impeller/geometry/color.h index 86d40f6abb61d..8f5a83885e1b9 100644 --- a/impeller/geometry/color.h +++ b/impeller/geometry/color.h @@ -5,6 +5,7 @@ #pragma once #include +#include #include "impeller/geometry/scalar.h" @@ -683,3 +684,13 @@ struct ColorHSB { static_assert(sizeof(Color) == 4 * sizeof(Scalar)); } // namespace impeller + +namespace std { + +inline std::ostream& operator<<(std::ostream& out, const impeller::Color& c) { + out << "(" << c.red << ", " << c.green << ", " << c.blue << ", " << c.alpha + << ")"; + return out; +} + +} // namespace std diff --git a/impeller/geometry/rect.h b/impeller/geometry/rect.h index 96fc41fd8f1f3..bb39b4bcd4759 100644 --- a/impeller/geometry/rect.h +++ b/impeller/geometry/rect.h @@ -92,6 +92,22 @@ struct TRect { constexpr bool IsEmpty() const { return size.IsEmpty(); } + constexpr auto GetLeft() const { + return std::min(origin.x, origin.x + size.width); + } + + constexpr auto GetTop() const { + return std::min(origin.y, origin.y + size.height); + } + + constexpr auto GetRight() const { + return std::max(origin.x, origin.x + size.width); + } + + constexpr auto GetBottom() const { + return std::max(origin.y, origin.y + size.height); + } + constexpr std::array GetLTRB() const { const auto left = std::min(origin.x, origin.x + size.width); const auto top = std::min(origin.y, origin.y + size.height); diff --git a/impeller/geometry/size.h b/impeller/geometry/size.h index 2415d314a5560..9a58f32f68b49 100644 --- a/impeller/geometry/size.h +++ b/impeller/geometry/size.h @@ -29,6 +29,10 @@ struct TSize { : TSize(static_cast(other.width), static_cast(other.height)) { } + static constexpr TSize MakeWH(Type width, Type height) { + return TSize{width, height}; + } + static constexpr TSize Infinite() { return TSize{std::numeric_limits::max(), std::numeric_limits::max()}; diff --git a/impeller/playground/playground.mm b/impeller/playground/playground.mm index 183f91aaa48e9..52b2c11599ed6 100644 --- a/impeller/playground/playground.mm +++ b/impeller/playground/playground.mm @@ -49,7 +49,8 @@ } Playground::Playground() - : renderer_(ContextMTL::Create(ShaderLibraryMappingsForPlayground())) {} + : renderer_(ContextMTL::Create(ShaderLibraryMappingsForPlayground(), + "Playground Library")) {} Playground::~Playground() = default; diff --git a/impeller/renderer/BUILD.gn b/impeller/renderer/BUILD.gn index 5bd501217d404..8e208638f22a4 100644 --- a/impeller/renderer/BUILD.gn +++ b/impeller/renderer/BUILD.gn @@ -8,7 +8,6 @@ impeller_component("renderer") { metal_backend_sources = [ "backend/metal/allocator_mtl.h", "backend/metal/allocator_mtl.mm", - "backend/metal/backend_cast.h", "backend/metal/command_buffer_mtl.h", "backend/metal/command_buffer_mtl.mm", "backend/metal/context_mtl.h", diff --git a/impeller/renderer/backend/metal/context_mtl.h b/impeller/renderer/backend/metal/context_mtl.h index b7f7080e80c65..705978601b8f6 100644 --- a/impeller/renderer/backend/metal/context_mtl.h +++ b/impeller/renderer/backend/metal/context_mtl.h @@ -10,8 +10,8 @@ #include #include "flutter/fml/macros.h" +#include "impeller/base/backend_cast.h" #include "impeller/renderer/backend/metal/allocator_mtl.h" -#include "impeller/renderer/backend/metal/backend_cast.h" #include "impeller/renderer/backend/metal/command_buffer_mtl.h" #include "impeller/renderer/backend/metal/pipeline_library_mtl.h" #include "impeller/renderer/backend/metal/shader_library_mtl.h" @@ -27,7 +27,8 @@ class ContextMTL final : public Context, const std::vector& shader_library_paths); static std::shared_ptr Create( - const std::vector>& shader_libraries_data); + const std::vector>& shader_libraries_data, + const std::string& label); // |Context| ~ContextMTL() override; diff --git a/impeller/renderer/backend/metal/context_mtl.mm b/impeller/renderer/backend/metal/context_mtl.mm index 0b464e685124e..af3ec0de7640b 100644 --- a/impeller/renderer/backend/metal/context_mtl.mm +++ b/impeller/renderer/backend/metal/context_mtl.mm @@ -105,7 +105,8 @@ static NSArray>* MTLShaderLibraryFromFileData( id device, - const std::vector>& libraries_data) { + const std::vector>& libraries_data, + const std::string& label) { NSMutableArray>* found_libraries = [NSMutableArray array]; for (const auto& library_data : libraries_data) { if (library_data == nullptr) { @@ -159,10 +160,12 @@ } std::shared_ptr ContextMTL::Create( - const std::vector>& shader_libraries_data) { + const std::vector>& shader_libraries_data, + const std::string& label) { auto device = CreateMetalDevice(); auto context = std::shared_ptr(new ContextMTL( - device, MTLShaderLibraryFromFileData(device, shader_libraries_data))); + device, + MTLShaderLibraryFromFileData(device, shader_libraries_data, label))); if (!context->IsValid()) { FML_LOG(ERROR) << "Could not create Metal context."; return nullptr; diff --git a/impeller/renderer/backend/metal/device_buffer_mtl.h b/impeller/renderer/backend/metal/device_buffer_mtl.h index 7523cc4cacd6c..c930626c243dd 100644 --- a/impeller/renderer/backend/metal/device_buffer_mtl.h +++ b/impeller/renderer/backend/metal/device_buffer_mtl.h @@ -7,7 +7,7 @@ #include #include "flutter/fml/macros.h" -#include "impeller/renderer/backend/metal/backend_cast.h" +#include "impeller/base/backend_cast.h" #include "impeller/renderer/device_buffer.h" namespace impeller { diff --git a/impeller/renderer/backend/metal/formats_mtl.h b/impeller/renderer/backend/metal/formats_mtl.h index 9cdb319f1bfca..dd91136924c1e 100644 --- a/impeller/renderer/backend/metal/formats_mtl.h +++ b/impeller/renderer/backend/metal/formats_mtl.h @@ -21,6 +21,8 @@ constexpr PixelFormat FromMTLPixelFormat(MTLPixelFormat format) { switch (format) { case MTLPixelFormatInvalid: return PixelFormat::kUnknown; + case MTLPixelFormatR8Unorm: + return PixelFormat::kR8UNormInt; case MTLPixelFormatBGRA8Unorm: return PixelFormat::kB8G8R8A8UNormInt; case MTLPixelFormatBGRA8Unorm_sRGB: @@ -41,6 +43,8 @@ constexpr MTLPixelFormat ToMTLPixelFormat(PixelFormat format) { switch (format) { case PixelFormat::kUnknown: return MTLPixelFormatInvalid; + case PixelFormat::kR8UNormInt: + return MTLPixelFormatR8Unorm; case PixelFormat::kB8G8R8A8UNormInt: return MTLPixelFormatBGRA8Unorm; case PixelFormat::kB8G8R8A8UNormIntSRGB: diff --git a/impeller/renderer/backend/metal/pipeline_mtl.h b/impeller/renderer/backend/metal/pipeline_mtl.h index 6756747121e89..238fae7944caf 100644 --- a/impeller/renderer/backend/metal/pipeline_mtl.h +++ b/impeller/renderer/backend/metal/pipeline_mtl.h @@ -7,7 +7,7 @@ #include #include "flutter/fml/macros.h" -#include "impeller/renderer/backend/metal/backend_cast.h" +#include "impeller/base/backend_cast.h" #include "impeller/renderer/pipeline.h" namespace impeller { diff --git a/impeller/renderer/backend/metal/render_pass_mtl.mm b/impeller/renderer/backend/metal/render_pass_mtl.mm index 8335b362331ea..068dd67ba1dc0 100644 --- a/impeller/renderer/backend/metal/render_pass_mtl.mm +++ b/impeller/renderer/backend/metal/render_pass_mtl.mm @@ -136,6 +136,7 @@ static bool ConfigureStencilAttachment( if (!buffer_ || !desc_ || !render_target_.IsValid()) { return; } + SetLabel("RenderPass"); is_valid_ = true; } diff --git a/impeller/renderer/backend/metal/sampler_library_mtl.h b/impeller/renderer/backend/metal/sampler_library_mtl.h index 71b8acb56312e..c8195320e8347 100644 --- a/impeller/renderer/backend/metal/sampler_library_mtl.h +++ b/impeller/renderer/backend/metal/sampler_library_mtl.h @@ -10,7 +10,7 @@ #include #include "flutter/fml/macros.h" -#include "impeller/renderer/backend/metal/backend_cast.h" +#include "impeller/base/backend_cast.h" #include "impeller/renderer/comparable.h" #include "impeller/renderer/sampler_descriptor.h" #include "impeller/renderer/sampler_library.h" diff --git a/impeller/renderer/backend/metal/sampler_library_mtl.mm b/impeller/renderer/backend/metal/sampler_library_mtl.mm index 313e670971023..fd8f6d6ed97c8 100644 --- a/impeller/renderer/backend/metal/sampler_library_mtl.mm +++ b/impeller/renderer/backend/metal/sampler_library_mtl.mm @@ -28,6 +28,11 @@ desc.sAddressMode = MTLSamplerAddressMode(descriptor.width_address_mode); desc.rAddressMode = MTLSamplerAddressMode(descriptor.depth_address_mode); desc.tAddressMode = MTLSamplerAddressMode(descriptor.height_address_mode); + + if (!descriptor.label.empty()) { + desc.label = @(descriptor.label.c_str()); + } + auto mtl_sampler = [device_ newSamplerStateWithDescriptor:desc]; if (!mtl_sampler) { return nullptr; diff --git a/impeller/renderer/backend/metal/sampler_mtl.h b/impeller/renderer/backend/metal/sampler_mtl.h index de80442942ab4..001b943da1a1e 100644 --- a/impeller/renderer/backend/metal/sampler_mtl.h +++ b/impeller/renderer/backend/metal/sampler_mtl.h @@ -7,7 +7,7 @@ #include #include "flutter/fml/macros.h" -#include "impeller/renderer/backend/metal/backend_cast.h" +#include "impeller/base/backend_cast.h" #include "impeller/renderer/sampler.h" namespace impeller { diff --git a/impeller/renderer/backend/metal/shader_function_mtl.h b/impeller/renderer/backend/metal/shader_function_mtl.h index 6dc35b248e867..e7f43c886b1cb 100644 --- a/impeller/renderer/backend/metal/shader_function_mtl.h +++ b/impeller/renderer/backend/metal/shader_function_mtl.h @@ -7,7 +7,7 @@ #include #include "flutter/fml/macros.h" -#include "impeller/renderer/backend/metal/backend_cast.h" +#include "impeller/base/backend_cast.h" #include "impeller/renderer/shader_function.h" namespace impeller { diff --git a/impeller/renderer/backend/metal/surface_mtl.mm b/impeller/renderer/backend/metal/surface_mtl.mm index 8b0c6e26ca4cf..3dbd602ab0ad2 100644 --- a/impeller/renderer/backend/metal/surface_mtl.mm +++ b/impeller/renderer/backend/metal/surface_mtl.mm @@ -52,7 +52,7 @@ return nullptr; } - msaa_tex->SetLabel("ImpellerOnscreenColor4xMSAA"); + msaa_tex->SetLabel("ImpellerOnscreenColorMSAA"); TextureDescriptor color0_resolve_tex_desc; color0_resolve_tex_desc.format = color_format; diff --git a/impeller/renderer/backend/metal/texture_mtl.h b/impeller/renderer/backend/metal/texture_mtl.h index 8f254672e2e23..02e9cbb4d3e01 100644 --- a/impeller/renderer/backend/metal/texture_mtl.h +++ b/impeller/renderer/backend/metal/texture_mtl.h @@ -7,7 +7,7 @@ #include #include "flutter/fml/macros.h" -#include "impeller/renderer/backend/metal/backend_cast.h" +#include "impeller/base/backend_cast.h" #include "impeller/renderer/texture.h" namespace impeller { diff --git a/impeller/renderer/backend/metal/vertex_descriptor_mtl.h b/impeller/renderer/backend/metal/vertex_descriptor_mtl.h index 82dcceb4dab00..0fe392b2142e9 100644 --- a/impeller/renderer/backend/metal/vertex_descriptor_mtl.h +++ b/impeller/renderer/backend/metal/vertex_descriptor_mtl.h @@ -7,7 +7,7 @@ #include #include "flutter/fml/macros.h" -#include "impeller/renderer/backend/metal/backend_cast.h" +#include "impeller/base/backend_cast.h" #include "impeller/renderer/vertex_descriptor.h" namespace impeller { diff --git a/impeller/renderer/formats.h b/impeller/renderer/formats.h index 092d86283fc53..dedc080a80541 100644 --- a/impeller/renderer/formats.h +++ b/impeller/renderer/formats.h @@ -48,6 +48,7 @@ class Texture; /// enum class PixelFormat { kUnknown, + kR8UNormInt, kR8G8B8A8UNormInt, kR8G8B8A8UNormIntSRGB, kB8G8R8A8UNormInt, @@ -193,6 +194,7 @@ constexpr size_t BytesPerPixelForPixelFormat(PixelFormat format) { switch (format) { case PixelFormat::kUnknown: return 0u; + case PixelFormat::kR8UNormInt: case PixelFormat::kS8UInt: return 1u; case PixelFormat::kR8G8B8A8UNormInt: diff --git a/impeller/renderer/sampler_descriptor.h b/impeller/renderer/sampler_descriptor.h index 6b0eb69c58d3c..bb2fffc61f120 100644 --- a/impeller/renderer/sampler_descriptor.h +++ b/impeller/renderer/sampler_descriptor.h @@ -21,6 +21,8 @@ struct SamplerDescriptor final : public Comparable { SamplerAddressMode height_address_mode = SamplerAddressMode::kClampToEdge; SamplerAddressMode depth_address_mode = SamplerAddressMode::kClampToEdge; + std::string label = "NN Clamp Sampler"; + // Comparable std::size_t GetHash() const override { return fml::HashCombine(min_filter, mag_filter, width_address_mode, diff --git a/impeller/typographer/BUILD.gn b/impeller/typographer/BUILD.gn new file mode 100644 index 0000000000000..3e06926491f76 --- /dev/null +++ b/impeller/typographer/BUILD.gn @@ -0,0 +1,50 @@ +# Copyright 2013 The Flutter Authors. All rights reserved. +# Use of this source code is governed by a BSD-style license that can be +# found in the LICENSE file. + +import("//flutter/impeller/tools/impeller.gni") + +impeller_component("typographer") { + sources = [ + "backends/skia/text_frame_skia.cc", + "backends/skia/text_frame_skia.h", + "backends/skia/text_render_context_skia.cc", + "backends/skia/text_render_context_skia.h", + "backends/skia/typeface_skia.cc", + "backends/skia/typeface_skia.h", + "font.cc", + "font.h", + "font_glyph_pair.cc", + "font_glyph_pair.h", + "glyph.cc", + "glyph.h", + "glyph_atlas.cc", + "glyph_atlas.h", + "text_frame.cc", + "text_frame.h", + "text_render_context.cc", + "text_render_context.h", + "text_run.cc", + "text_run.h", + "typeface.cc", + "typeface.h", + ] + + public_deps = [ + "../base", + "../geometry", + "../renderer", + "//third_party/skia", + ] +} + +impeller_component("typographer_unittests") { + testonly = true + + sources = [ "typographer_unittests.cc" ] + + deps = [ + ":typographer", + "../playground", + ] +} diff --git a/impeller/typographer/backends/skia/text_frame_skia.cc b/impeller/typographer/backends/skia/text_frame_skia.cc new file mode 100644 index 0000000000000..979d6fcda767b --- /dev/null +++ b/impeller/typographer/backends/skia/text_frame_skia.cc @@ -0,0 +1,69 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "impeller/typographer/backends/skia/text_frame_skia.h" + +#include "flutter/fml/logging.h" +#include "impeller/typographer/backends/skia/typeface_skia.h" +#include "third_party/skia/include/core/SkFont.h" +#include "third_party/skia/include/core/SkFontMetrics.h" +#include "third_party/skia/src/core/SkTextBlobPriv.h" // nogncheck + +namespace impeller { + +static Font ToFont(const SkFont& font) { + auto typeface = std::make_shared(font.refTypefaceOrDefault()); + + SkFontMetrics sk_metrics; + font.getMetrics(&sk_metrics); + + Font::Metrics metrics; + metrics.point_size = font.getSize(); + metrics.ascent = sk_metrics.fAscent; + metrics.descent = sk_metrics.fDescent; + + return Font{std::move(typeface), std::move(metrics)}; +} + +TextFrame TextFrameFromTextBlob(sk_sp blob) { + if (!blob) { + return {}; + } + + TextFrame frame; + + for (SkTextBlobRunIterator run(blob.get()); !run.done(); run.next()) { + TextRun text_run(ToFont(run.font())); + const auto glyph_count = run.glyphCount(); + const auto* glyphs = run.glyphs(); + switch (run.positioning()) { + case SkTextBlobRunIterator::kDefault_Positioning: + FML_DLOG(ERROR) << "Unimplemented."; + break; + case SkTextBlobRunIterator::kHorizontal_Positioning: + FML_DLOG(ERROR) << "Unimplemented."; + break; + case SkTextBlobRunIterator::kFull_Positioning: + for (auto i = 0u; i < glyph_count; i++) { + // kFull_Positioning has two scalars per glyph. + const SkPoint* glyph_points = run.points(); + const auto* point = glyph_points + i; + text_run.AddGlyph(glyphs[i], + Matrix::MakeTranslation({point->x(), point->y()})); + } + break; + case SkTextBlobRunIterator::kRSXform_Positioning: + FML_DLOG(ERROR) << "Unimplemented."; + break; + default: + FML_DLOG(ERROR) << "Unimplemented."; + continue; + } + frame.AddTextRun(std::move(text_run)); + } + + return frame; +} + +} // namespace impeller diff --git a/impeller/typographer/backends/skia/text_frame_skia.h b/impeller/typographer/backends/skia/text_frame_skia.h new file mode 100644 index 0000000000000..ca15f01881712 --- /dev/null +++ b/impeller/typographer/backends/skia/text_frame_skia.h @@ -0,0 +1,15 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#pragma once + +#include "flutter/fml/macros.h" +#include "impeller/typographer/text_frame.h" +#include "third_party/skia/include/core/SkTextBlob.h" + +namespace impeller { + +TextFrame TextFrameFromTextBlob(sk_sp blob); + +} // namespace impeller diff --git a/impeller/typographer/backends/skia/text_render_context_skia.cc b/impeller/typographer/backends/skia/text_render_context_skia.cc new file mode 100644 index 0000000000000..84beacfca734b --- /dev/null +++ b/impeller/typographer/backends/skia/text_render_context_skia.cc @@ -0,0 +1,252 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "impeller/typographer/backends/skia/text_render_context_skia.h" + +#include "flutter/fml/trace_event.h" +#include "impeller/base/allocation.h" +#include "impeller/renderer/allocator.h" +#include "impeller/typographer/backends/skia/typeface_skia.h" +#include "third_party/skia/include/core/SkBitmap.h" +#include "third_party/skia/include/core/SkCanvas.h" +#include "third_party/skia/include/core/SkFont.h" +#include "third_party/skia/include/core/SkFontMetrics.h" +#include "third_party/skia/include/core/SkRSXform.h" +#include "third_party/skia/include/core/SkSurface.h" +#include "third_party/skia/src/core/SkIPoint16.h" //nogncheck +#include "third_party/skia/src/gpu/GrRectanizer.h" //nogncheck + +namespace impeller { + +TextRenderContextSkia::TextRenderContextSkia(std::shared_ptr context) + : TextRenderContext(std::move(context)) {} + +TextRenderContextSkia::~TextRenderContextSkia() = default; + +static FontGlyphPair::Set CollectUniqueFontGlyphPairsSet( + const std::vector& runs) { + FontGlyphPair::Set set; + for (const auto& run : runs) { + auto font = run.GetFont(); + for (const auto& glyph_position : run.GetGlyphPositions()) { + set.insert({font, glyph_position.glyph}); + } + } + return set; +} + +static FontGlyphPair::Vector CollectUniqueFontGlyphPairs( + const std::vector& runs) { + TRACE_EVENT0("impeller", __FUNCTION__); + FontGlyphPair::Vector vector; + auto set = CollectUniqueFontGlyphPairsSet(runs); + vector.reserve(set.size()); + for (const auto& item : set) { + vector.emplace_back(std::move(item)); + } + return vector; +} + +static bool PairsFitInAtlasOfSize(const FontGlyphPair::Vector& pairs, + size_t atlas_size, + std::vector& glyph_positions) { + if (atlas_size == 0u) { + return false; + } + + auto rect_packer = std::unique_ptr( + GrRectanizer::Factory(atlas_size, atlas_size)); + + glyph_positions.clear(); + glyph_positions.reserve(pairs.size()); + + for (const auto& pair : pairs) { + auto glyph_size = pair.font.GetGlyphSize(); + if (!glyph_size.has_value()) { + continue; + } + SkIPoint16 location_in_atlas; + if (!rect_packer->addRect(glyph_size->width, // + glyph_size->height, // + &location_in_atlas // + )) { + return false; + } + glyph_positions.emplace_back(Rect::MakeXYWH(location_in_atlas.x(), // + location_in_atlas.y(), // + glyph_size->width, // + glyph_size->height // + )); + } + + return true; +} + +static size_t OptimumAtlasSizeForFontGlyphPairs( + const FontGlyphPair::Vector& pairs, + std::vector& glyph_positions) { + static constexpr auto kMinAtlasSize = 8u; + static constexpr auto kMaxAtlasSize = 4096u; + + TRACE_EVENT0("impeller", __FUNCTION__); + + size_t current_size = kMinAtlasSize; + do { + if (PairsFitInAtlasOfSize(pairs, current_size, glyph_positions)) { + return current_size; + } + current_size = Allocation::NextPowerOfTwoSize(current_size + 1); + } while (current_size <= kMaxAtlasSize); + return 0u; +} + +static std::optional CreateAtlasBitmap(const GlyphAtlas& atlas, + size_t atlas_size) { + TRACE_EVENT0("impeller", __FUNCTION__); + SkBitmap bitmap; + auto image_info = SkImageInfo::MakeN32Premul(atlas_size, atlas_size); + if (!bitmap.tryAllocPixels(image_info)) { + return std::nullopt; + } + auto surface = SkSurface::MakeRasterDirect(bitmap.pixmap()); + if (!surface) { + return std::nullopt; + } + auto canvas = surface->getCanvas(); + if (!canvas) { + return std::nullopt; + } + + atlas.IterateGlyphs([canvas](const FontGlyphPair& font_glyph, + const Rect& location) -> bool { + const auto position = SkPoint::Make(location.origin.x, location.origin.y); + SkGlyphID glyph_id = font_glyph.glyph.index; + SkFont font( + TypefaceSkia::Cast(*font_glyph.font.GetTypeface()).GetSkiaTypeface(), + font_glyph.font.GetMetrics().point_size); + + SkFontMetrics metrics; + font.getMetrics(&metrics); + + SkPaint glyph_paint; + glyph_paint.setColor(SK_ColorWHITE); + canvas->drawGlyphs(1u, // count + &glyph_id, // glyphs + &position, // positions + SkPoint::Make(0.0, + -metrics.fAscent), // origin + font, // font + glyph_paint // paint + ); + return true; + }); + + return bitmap; +} + +static std::shared_ptr UploadGlyphTextureAtlas( + std::shared_ptr allocator, + const SkBitmap& bitmap, + size_t atlas_size) { + TRACE_EVENT0("impeller", __FUNCTION__); + if (!allocator) { + return nullptr; + } + + const auto& pixmap = bitmap.pixmap(); + + TextureDescriptor texture_descriptor; + texture_descriptor.format = PixelFormat::kR8G8B8A8UNormInt; + texture_descriptor.size = ISize::MakeWH(atlas_size, atlas_size); + + if (pixmap.rowBytes() * pixmap.height() != + texture_descriptor.GetSizeOfBaseMipLevel()) { + return nullptr; + } + + auto texture = + allocator->CreateTexture(StorageMode::kHostVisible, texture_descriptor); + if (!texture || !texture->IsValid()) { + return nullptr; + } + texture->SetLabel("GlyphAtlas"); + + if (!texture->SetContents(static_cast(pixmap.addr()), + pixmap.rowBytes() * pixmap.height())) { + return nullptr; + } + return texture; +} + +std::shared_ptr TextRenderContextSkia::CreateGlyphAtlas( + const TextFrame& frame) const { + TRACE_EVENT0("impeller", __FUNCTION__); + if (!IsValid()) { + return nullptr; + } + + auto glyph_atlas = std::make_shared(); + + // --------------------------------------------------------------------------- + // Step 1: Collect unique font-glyph pairs in the frame. + // --------------------------------------------------------------------------- + auto font_glyph_pairs = CollectUniqueFontGlyphPairs(frame.GetRuns()); + if (font_glyph_pairs.empty()) { + return glyph_atlas; + } + + // --------------------------------------------------------------------------- + // Step 2: Get the optimum size of the texture atlas. + // --------------------------------------------------------------------------- + std::vector glyph_positions; + const auto atlas_size = + OptimumAtlasSizeForFontGlyphPairs(font_glyph_pairs, glyph_positions); + if (atlas_size == 0u) { + return nullptr; + } + + // --------------------------------------------------------------------------- + // Step 3: Find location of font-glyph pairs in the atlas. We have this from + // the last step. So no need to do create another rect packer. But just do a + // sanity check of counts. This could also be just an assertion as only a + // construction issue would cause such a failure. + // --------------------------------------------------------------------------- + if (glyph_positions.size() != font_glyph_pairs.size()) { + return nullptr; + } + + // --------------------------------------------------------------------------- + // Step 4: Record the positions in the glyph atlas. + // --------------------------------------------------------------------------- + for (size_t i = 0, count = glyph_positions.size(); i < count; i++) { + glyph_atlas->AddTypefaceGlyphPosition(font_glyph_pairs[i], + glyph_positions[i]); + } + + // --------------------------------------------------------------------------- + // Step 5: Draw font-glyph pairs in the correct spot in the atlas. + // --------------------------------------------------------------------------- + auto bitmap = CreateAtlasBitmap(*glyph_atlas, atlas_size); + if (!bitmap.has_value()) { + return nullptr; + } + + // --------------------------------------------------------------------------- + // Step 6: Upload the atlas as a texture. + // --------------------------------------------------------------------------- + auto texture = UploadGlyphTextureAtlas(GetContext()->GetTransientsAllocator(), + bitmap.value(), atlas_size); + if (!texture) { + return nullptr; + } + + // --------------------------------------------------------------------------- + // Step 7: Record the texture in the glyph atlas. + // --------------------------------------------------------------------------- + glyph_atlas->SetTexture(std::move(texture)); + + return glyph_atlas; +} + +} // namespace impeller diff --git a/impeller/typographer/backends/skia/text_render_context_skia.h b/impeller/typographer/backends/skia/text_render_context_skia.h new file mode 100644 index 0000000000000..936b64bfde448 --- /dev/null +++ b/impeller/typographer/backends/skia/text_render_context_skia.h @@ -0,0 +1,26 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#pragma once + +#include "flutter/fml/macros.h" +#include "impeller/typographer/text_render_context.h" + +namespace impeller { + +class TextRenderContextSkia : public TextRenderContext { + public: + TextRenderContextSkia(std::shared_ptr context); + + ~TextRenderContextSkia() override; + + // |TextRenderContext| + std::shared_ptr CreateGlyphAtlas( + const TextFrame& frame) const override; + + private: + FML_DISALLOW_COPY_AND_ASSIGN(TextRenderContextSkia); +}; + +} // namespace impeller diff --git a/impeller/typographer/backends/skia/typeface_skia.cc b/impeller/typographer/backends/skia/typeface_skia.cc new file mode 100644 index 0000000000000..552e159f1f824 --- /dev/null +++ b/impeller/typographer/backends/skia/typeface_skia.cc @@ -0,0 +1,46 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "impeller/typographer/backends/skia/typeface_skia.h" + +namespace impeller { + +TypefaceSkia::TypefaceSkia(sk_sp typeface) + : typeface_(std::move(typeface)) {} + +TypefaceSkia::~TypefaceSkia() = default; + +bool TypefaceSkia::IsValid() const { + return !!typeface_; +} + +std::size_t TypefaceSkia::GetHash() const { + if (!IsValid()) { + return 0u; + } + + return reinterpret_cast(typeface_.get()); +} + +bool TypefaceSkia::IsEqual(const Typeface& other) const { + auto sk_other = reinterpret_cast(&other); + return sk_other->typeface_ == typeface_; +} + +Rect TypefaceSkia::GetBoundingBox() const { + if (!IsValid()) { + return {}; + } + + const auto bounds = typeface_->getBounds(); + + return Rect::MakeLTRB(bounds.left(), bounds.top(), bounds.right(), + bounds.bottom()); +} + +const sk_sp& TypefaceSkia::GetSkiaTypeface() const { + return typeface_; +} + +} // namespace impeller diff --git a/impeller/typographer/backends/skia/typeface_skia.h b/impeller/typographer/backends/skia/typeface_skia.h new file mode 100644 index 0000000000000..6ac52c47761fe --- /dev/null +++ b/impeller/typographer/backends/skia/typeface_skia.h @@ -0,0 +1,42 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#pragma once + +#include "flutter/fml/macros.h" +#include "impeller/base/backend_cast.h" +#include "impeller/typographer/typeface.h" +#include "third_party/skia/include/core/SkRefCnt.h" +#include "third_party/skia/include/core/SkTypeface.h" + +namespace impeller { + +class TypefaceSkia final : public Typeface, + public BackendCast { + public: + TypefaceSkia(sk_sp typeface); + + ~TypefaceSkia() override; + + // |Typeface| + bool IsValid() const override; + + // |Comparable| + std::size_t GetHash() const override; + + // |Comparable| + bool IsEqual(const Typeface& other) const override; + + // |Typeface| + Rect GetBoundingBox() const override; + + const sk_sp& GetSkiaTypeface() const; + + private: + sk_sp typeface_; + + FML_DISALLOW_COPY_AND_ASSIGN(TypefaceSkia); +}; + +} // namespace impeller diff --git a/impeller/typographer/font.cc b/impeller/typographer/font.cc new file mode 100644 index 0000000000000..e675b0431eba7 --- /dev/null +++ b/impeller/typographer/font.cc @@ -0,0 +1,48 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "impeller/typographer/font.h" + +namespace impeller { + +Font::Font(std::shared_ptr typeface, Metrics metrics) + : typeface_(std::move(typeface)), metrics_(std::move(metrics)) { + if (!typeface_) { + return; + } + is_valid_ = true; +} + +Font::~Font() = default; + +bool Font::IsValid() const { + return is_valid_; +} + +const std::shared_ptr& Font::GetTypeface() const { + return typeface_; +} + +std::size_t Font::GetHash() const { + return fml::HashCombine(is_valid_, typeface_ ? typeface_->GetHash() : 0u, + metrics_); +} + +bool Font::IsEqual(const Font& other) const { + return DeepComparePointer(typeface_, other.typeface_) && + is_valid_ == other.is_valid_ && metrics_ == other.metrics_; +} + +std::optional Font::GetGlyphSize() const { + if (!IsValid()) { + return std::nullopt; + } + return ISize::Ceil(typeface_->GetBoundingBox().size * metrics_.point_size); +} + +const Font::Metrics& Font::GetMetrics() const { + return metrics_; +} + +} // namespace impeller diff --git a/impeller/typographer/font.h b/impeller/typographer/font.h new file mode 100644 index 0000000000000..2b4af8f86f880 --- /dev/null +++ b/impeller/typographer/font.h @@ -0,0 +1,96 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#pragma once + +#include +#include + +#include "flutter/fml/macros.h" +#include "impeller/renderer/comparable.h" +#include "impeller/typographer/glyph.h" +#include "impeller/typographer/typeface.h" + +namespace impeller { + +//------------------------------------------------------------------------------ +/// @brief Describes a typeface along with any modifications to its +/// intrinsic properties. +/// +class Font : public Comparable { + public: + //---------------------------------------------------------------------------- + /// @brief Describes the modifications made to the intrinsic properties + /// of a typeface. + /// + /// The coordinate system of a font has its origin at (0, 0) on + /// the baseline with an upper-left-origin coordinate system. + /// + struct Metrics { + //-------------------------------------------------------------------------- + /// The point size of the font. + /// + Scalar point_size = 12.0f; + //-------------------------------------------------------------------------- + /// The font ascent relative to the baseline. This is usually negative as + /// moving upwards (ascending) in an upper-left-origin coordinate system + /// yields smaller numbers. + /// + Scalar ascent = 0.0f; + //-------------------------------------------------------------------------- + /// The font descent relative to the baseline. This is usually positive as + /// moving downwards (descending) in an upper-left-origin coordinate system + /// yields larger numbers. + /// + Scalar descent = 0.0f; + + constexpr bool operator==(const Metrics& o) const { + return point_size == o.point_size && ascent == o.ascent && + descent == o.descent; + } + }; + + Font(std::shared_ptr typeface, Metrics metrics); + + ~Font(); + + bool IsValid() const; + + //---------------------------------------------------------------------------- + /// @brief The typeface whose intrinsic properties this font modifies. + /// + /// @return The typeface. + /// + const std::shared_ptr& GetTypeface() const; + + //---------------------------------------------------------------------------- + /// @brief A conservatively large scaled bounding box of all glyphs in + /// this font. + /// + /// @return The scaled glyph size. + /// + std::optional GetGlyphSize() const; + + const Metrics& GetMetrics() const; + + // |Comparable| + std::size_t GetHash() const override; + + // |Comparable| + bool IsEqual(const Font& other) const override; + + private: + std::shared_ptr typeface_; + Metrics metrics_ = {}; + bool is_valid_ = false; +}; + +} // namespace impeller + +template <> +struct std::hash { + constexpr std::size_t operator()(const impeller::Font::Metrics& m) const { + return fml::HashCombine(m.point_size, m.ascent, m.descent); + } +}; diff --git a/impeller/typographer/font_glyph_pair.cc b/impeller/typographer/font_glyph_pair.cc new file mode 100644 index 0000000000000..667caadb4e827 --- /dev/null +++ b/impeller/typographer/font_glyph_pair.cc @@ -0,0 +1,11 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "impeller/typographer/font_glyph_pair.h" + +namespace impeller { + +// + +} // namespace impeller diff --git a/impeller/typographer/font_glyph_pair.h b/impeller/typographer/font_glyph_pair.h new file mode 100644 index 0000000000000..b8cb7b0af2811 --- /dev/null +++ b/impeller/typographer/font_glyph_pair.h @@ -0,0 +1,44 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#pragma once + +#include +#include +#include + +#include "flutter/fml/macros.h" +#include "impeller/geometry/size.h" +#include "impeller/typographer/font.h" +#include "impeller/typographer/glyph.h" + +namespace impeller { + +//------------------------------------------------------------------------------ +/// @brief A font along with a glyph in that font. Used in glyph atlases as +/// keys. +/// +struct FontGlyphPair { + struct Hash; + struct Equal; + + using Set = std::unordered_set; + using Vector = std::vector; + + Font font; + Glyph glyph; + + struct Hash { + std::size_t operator()(const FontGlyphPair& p) const { + return fml::HashCombine(p.font.GetHash(), p.glyph); + } + }; + struct Equal { + bool operator()(const FontGlyphPair& lhs, const FontGlyphPair& rhs) const { + return lhs.font.IsEqual(rhs.font) && lhs.glyph.index == rhs.glyph.index; + } + }; +}; + +} // namespace impeller diff --git a/impeller/typographer/glyph.cc b/impeller/typographer/glyph.cc new file mode 100644 index 0000000000000..9182d8f1f9082 --- /dev/null +++ b/impeller/typographer/glyph.cc @@ -0,0 +1,11 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "impeller/typographer/glyph.h" + +namespace impeller { + +// + +} // namespace impeller diff --git a/impeller/typographer/glyph.h b/impeller/typographer/glyph.h new file mode 100644 index 0000000000000..000346ba9d17d --- /dev/null +++ b/impeller/typographer/glyph.h @@ -0,0 +1,38 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#pragma once + +#include +#include + +#include "flutter/fml/macros.h" + +namespace impeller { + +//------------------------------------------------------------------------------ +/// @brief The glyph index in the typeface. +/// +struct Glyph { + uint16_t index = 0; + + Glyph(uint16_t p_index) : index(p_index) {} +}; + +} // namespace impeller + +template <> +struct std::hash { + constexpr std::size_t operator()(const impeller::Glyph& g) const { + return g.index; + } +}; + +template <> +struct std::less { + constexpr bool operator()(const impeller::Glyph& lhs, + const impeller::Glyph& rhs) const { + return lhs.index < rhs.index; + } +}; diff --git a/impeller/typographer/glyph_atlas.cc b/impeller/typographer/glyph_atlas.cc new file mode 100644 index 0000000000000..7ed4578bbd923 --- /dev/null +++ b/impeller/typographer/glyph_atlas.cc @@ -0,0 +1,59 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "impeller/typographer/glyph_atlas.h" + +namespace impeller { + +GlyphAtlas::GlyphAtlas() = default; + +GlyphAtlas::~GlyphAtlas() = default; + +bool GlyphAtlas::IsValid() const { + return !!texture_; +} + +const std::shared_ptr& GlyphAtlas::GetTexture() const { + return texture_; +} + +void GlyphAtlas::SetTexture(std::shared_ptr texture) { + texture_ = std::move(texture); +} + +void GlyphAtlas::AddTypefaceGlyphPosition(FontGlyphPair pair, Rect rect) { + positions_[pair] = rect; +} + +std::optional GlyphAtlas::FindFontGlyphPosition( + const FontGlyphPair& pair) const { + auto found = positions_.find(pair); + if (found == positions_.end()) { + return std::nullopt; + } + return found->second; +} + +size_t GlyphAtlas::GetGlyphCount() const { + return positions_.size(); +} + +size_t GlyphAtlas::IterateGlyphs( + std::function iterator) + const { + if (!iterator) { + return 0u; + } + + size_t count = 0u; + for (const auto& position : positions_) { + count++; + if (!iterator(position.first, position.second)) { + return count; + } + } + return count; +} + +} // namespace impeller diff --git a/impeller/typographer/glyph_atlas.h b/impeller/typographer/glyph_atlas.h new file mode 100644 index 0000000000000..e72c3b83e1553 --- /dev/null +++ b/impeller/typographer/glyph_atlas.h @@ -0,0 +1,99 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#pragma once + +#include +#include +#include +#include + +#include "flutter/fml/macros.h" +#include "impeller/geometry/rect.h" +#include "impeller/renderer/texture.h" +#include "impeller/typographer/font_glyph_pair.h" + +namespace impeller { + +//------------------------------------------------------------------------------ +/// @brief A texture containing the bitmap representation of glyphs in +/// different fonts along with the ability to query the location of +/// specific font glyphs within the texture. +/// +class GlyphAtlas { + public: + //---------------------------------------------------------------------------- + /// @brief Create an empty glyph atlas. + /// + GlyphAtlas(); + + ~GlyphAtlas(); + + bool IsValid() const; + + //---------------------------------------------------------------------------- + /// @brief Set the texture for the glyph atlas. + /// + /// @param[in] texture The texture + /// + void SetTexture(std::shared_ptr texture); + + //---------------------------------------------------------------------------- + /// @brief Get the texture for the glyph atlas. + /// + /// @return The texture. + /// + const std::shared_ptr& GetTexture() const; + + //---------------------------------------------------------------------------- + /// @brief Record there location of a specific font-glyph pair within the + /// atlas. + /// + /// @param[in] pair The font-glyph pair + /// @param[in] rect The rectangle + /// + void AddTypefaceGlyphPosition(FontGlyphPair pair, Rect rect); + + //---------------------------------------------------------------------------- + /// @brief Get the number of unique font-glyph pairs in this atlas. + /// + /// @return The glyph count. + /// + size_t GetGlyphCount() const; + + //---------------------------------------------------------------------------- + /// @brief Iterate of all the glyphs along with their locations in the + /// atlas. + /// + /// @param[in] iterator The iterator. Return `false` from the iterator to + /// stop iterating. + /// + /// @return The number of glyphs iterated over. + /// + size_t IterateGlyphs(std::function iterator) const; + + //---------------------------------------------------------------------------- + /// @brief Find the location of a specific font-glyph pair in the atlas. + /// + /// @param[in] pair The font-glyph pair + /// + /// @return The location of the font-glyph pair in the atlas. + /// `std::nullopt` of the pair in not in the atlas. + /// + std::optional FindFontGlyphPosition(const FontGlyphPair& pair) const; + + private: + std::shared_ptr texture_; + + std::unordered_map + positions_; + + FML_DISALLOW_COPY_AND_ASSIGN(GlyphAtlas); +}; + +} // namespace impeller diff --git a/impeller/typographer/text_frame.cc b/impeller/typographer/text_frame.cc new file mode 100644 index 0000000000000..f6681e5e4671c --- /dev/null +++ b/impeller/typographer/text_frame.cc @@ -0,0 +1,29 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "impeller/typographer/text_frame.h" + +namespace impeller { + +TextFrame::TextFrame() = default; + +TextFrame::~TextFrame() = default; + +bool TextFrame::AddTextRun(TextRun run) { + if (!run.IsValid()) { + return false; + } + runs_.emplace_back(std::move(run)); + return true; +} + +size_t TextFrame::GetRunCount() const { + return runs_.size(); +} + +const std::vector& TextFrame::GetRuns() const { + return runs_; +} + +} // namespace impeller diff --git a/impeller/typographer/text_frame.h b/impeller/typographer/text_frame.h new file mode 100644 index 0000000000000..a6d86b01d068b --- /dev/null +++ b/impeller/typographer/text_frame.h @@ -0,0 +1,51 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#pragma once + +#include "flutter/fml/macros.h" +#include "impeller/typographer/text_run.h" + +namespace impeller { + +//------------------------------------------------------------------------------ +/// @brief Represents a collection of shaped text runs. +/// +/// This object is typically the entrypoint in the Impeller type +/// rendering subsystem. +/// +class TextFrame { + public: + TextFrame(); + + ~TextFrame(); + + //---------------------------------------------------------------------------- + /// @brief The number of runs in this text frame. + /// + /// @return The run count. + /// + size_t GetRunCount() const; + + //---------------------------------------------------------------------------- + /// @brief Adds a new text run to the text frame. + /// + /// @param[in] run The run + /// + /// @return If the text run could be added to this frame. + /// + bool AddTextRun(TextRun run); + + //---------------------------------------------------------------------------- + /// @brief Returns a reference to all the text runs in this frame. + /// + /// @return The runs in this frame. + /// + const std::vector& GetRuns() const; + + private: + std::vector runs_; +}; + +} // namespace impeller diff --git a/impeller/typographer/text_render_context.cc b/impeller/typographer/text_render_context.cc new file mode 100644 index 0000000000000..600e21391547e --- /dev/null +++ b/impeller/typographer/text_render_context.cc @@ -0,0 +1,27 @@ +// Copyright 2019 The Fuchsia Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "impeller/typographer/text_render_context.h" + +namespace impeller { + +TextRenderContext::TextRenderContext(std::shared_ptr context) + : context_(std::move(context)) { + if (!context_ || !context_->IsValid()) { + return; + } + is_valid_ = true; +} + +TextRenderContext::~TextRenderContext() = default; + +bool TextRenderContext::IsValid() const { + return is_valid_; +} + +const std::shared_ptr& TextRenderContext::GetContext() const { + return context_; +} + +} // namespace impeller diff --git a/impeller/typographer/text_render_context.h b/impeller/typographer/text_render_context.h new file mode 100644 index 0000000000000..8d5ac6fa133ab --- /dev/null +++ b/impeller/typographer/text_render_context.h @@ -0,0 +1,63 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#pragma once + +#include + +#include "flutter/fml/macros.h" +#include "impeller/renderer/context.h" +#include "impeller/typographer/glyph_atlas.h" +#include "impeller/typographer/text_frame.h" + +namespace impeller { + +//------------------------------------------------------------------------------ +/// @brief The graphics context necessary to render text. +/// +/// This is necessary to create and reference resources related to +/// rendering text on the GPU. +/// +/// It is caller responsibility to create as few of these and keep +/// these around for as long possible. +/// +class TextRenderContext { + public: + //---------------------------------------------------------------------------- + /// @brief Create a new context to render text that talks to an + /// underlying graphics context. + /// + /// @param[in] context The graphics context + /// + TextRenderContext(std::shared_ptr context); + + virtual ~TextRenderContext(); + + virtual bool IsValid() const; + + //---------------------------------------------------------------------------- + /// @brief Get the underlying graphics context. + /// + /// @return The context. + /// + const std::shared_ptr& GetContext() const; + + //---------------------------------------------------------------------------- + /// @brief Create a new glyph atlas for the specified text frame. + /// + /// @param[in] frame The text frame + /// + /// @return A valid glyph atlas or null. + /// + virtual std::shared_ptr CreateGlyphAtlas( + const TextFrame& frame) const = 0; + + private: + std::shared_ptr context_; + bool is_valid_ = false; + + FML_DISALLOW_COPY_AND_ASSIGN(TextRenderContext); +}; + +} // namespace impeller diff --git a/impeller/typographer/text_run.cc b/impeller/typographer/text_run.cc new file mode 100644 index 0000000000000..dc21b3865b0ae --- /dev/null +++ b/impeller/typographer/text_run.cc @@ -0,0 +1,39 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "impeller/typographer/text_run.h" + +namespace impeller { + +TextRun::TextRun(Font font) : font_(std::move(font)) { + if (!font.IsValid()) { + return; + } + is_valid_ = true; +} + +TextRun::~TextRun() = default; + +bool TextRun::AddGlyph(Glyph glyph, Matrix position) { + glyphs_.emplace_back(GlyphPosition{glyph, position}); + return true; +} + +bool TextRun::IsValid() const { + return is_valid_; +} + +const std::vector& TextRun::GetGlyphPositions() const { + return glyphs_; +} + +size_t TextRun::GetGlyphCount() const { + return glyphs_.size(); +} + +const Font& TextRun::GetFont() const { + return font_; +} + +} // namespace impeller diff --git a/impeller/typographer/text_run.h b/impeller/typographer/text_run.h new file mode 100644 index 0000000000000..10c5e3ef71289 --- /dev/null +++ b/impeller/typographer/text_run.h @@ -0,0 +1,77 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#pragma once + +#include + +#include "impeller/geometry/matrix.h" +#include "impeller/typographer/font.h" +#include "impeller/typographer/glyph.h" + +namespace impeller { + +//------------------------------------------------------------------------------ +/// @brief Represents a collection of positioned glyphs from a specific +/// font. +/// +class TextRun { + public: + struct GlyphPosition { + Glyph glyph; + Matrix position; + + GlyphPosition(Glyph p_glyph, Matrix p_position) + : glyph(p_glyph), position(p_position) {} + }; + + //---------------------------------------------------------------------------- + /// @brief Create an empty text run with the specified font. + /// + /// @param[in] font The font + /// + TextRun(Font font); + + ~TextRun(); + + bool IsValid() const; + + //---------------------------------------------------------------------------- + /// @brief Add a glyph at the specified position to the run. + /// + /// @param[in] glyph The glyph + /// @param[in] position The position + /// + /// @return If the glyph could be added to the run. + /// + bool AddGlyph(Glyph glyph, Matrix position); + + //---------------------------------------------------------------------------- + /// @brief Get the number of glyphs in the run. + /// + /// @return The glyph count. + /// + size_t GetGlyphCount() const; + + //---------------------------------------------------------------------------- + /// @brief Get a reference to all the glyph positions within the run. + /// + /// @return The glyph positions. + /// + const std::vector& GetGlyphPositions() const; + + //---------------------------------------------------------------------------- + /// @brief Get the font for this run. + /// + /// @return The font. + /// + const Font& GetFont() const; + + private: + Font font_; + std::vector glyphs_; + bool is_valid_ = false; +}; + +} // namespace impeller diff --git a/impeller/typographer/typeface.cc b/impeller/typographer/typeface.cc new file mode 100644 index 0000000000000..9c5fdd5451ca5 --- /dev/null +++ b/impeller/typographer/typeface.cc @@ -0,0 +1,13 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "impeller/typographer/typeface.h" + +namespace impeller { + +Typeface::Typeface() = default; + +Typeface::~Typeface() = default; + +} // namespace impeller diff --git a/impeller/typographer/typeface.h b/impeller/typographer/typeface.h new file mode 100644 index 0000000000000..5c2a8574ee84a --- /dev/null +++ b/impeller/typographer/typeface.h @@ -0,0 +1,40 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#pragma once + +#include "flutter/fml/macros.h" +#include "impeller/geometry/rect.h" +#include "impeller/renderer/comparable.h" + +namespace impeller { + +//------------------------------------------------------------------------------ +/// @brief A typeface, usually obtained from a font-file, on disk describes +/// the intrinsic properties of the font. Typefaces are rarely used +/// directly. Instead, font refer to typefaces along with any +/// modifications applied to its intrinsic properties. +/// +class Typeface : public Comparable { + public: + Typeface(); + + virtual ~Typeface(); + + virtual bool IsValid() const = 0; + + //---------------------------------------------------------------------------- + /// @brief Get the union of the bounding boxes of all glyphs in the + /// typeface. This box is unit-scaled and conservatively large to + /// cover all glyphs. + /// + /// @return The conservative unit-scaled bounding box. + /// + virtual Rect GetBoundingBox() const = 0; + + private: + FML_DISALLOW_COPY_AND_ASSIGN(Typeface); +}; + +} // namespace impeller diff --git a/impeller/typographer/typographer_unittests.cc b/impeller/typographer/typographer_unittests.cc new file mode 100644 index 0000000000000..1f59d633bc7dd --- /dev/null +++ b/impeller/typographer/typographer_unittests.cc @@ -0,0 +1,46 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "flutter/testing/testing.h" +#include "impeller/playground/playground.h" +#include "impeller/typographer/backends/skia/text_frame_skia.h" +#include "impeller/typographer/backends/skia/text_render_context_skia.h" +#include "third_party/skia/include/core/SkTextBlob.h" + +namespace impeller { +namespace testing { + +using TypographerTest = Playground; + +TEST_F(TypographerTest, CanConvertTextBlob) { + SkFont font; + auto blob = SkTextBlob::MakeFromString( + "the quick brown fox jumped over the lazy dog.", font); + ASSERT_TRUE(blob); + auto frame = TextFrameFromTextBlob(blob); + ASSERT_EQ(frame.GetRunCount(), 1u); + for (const auto& run : frame.GetRuns()) { + ASSERT_TRUE(run.IsValid()); + ASSERT_EQ(run.GetGlyphCount(), 45u); + } +} + +TEST_F(TypographerTest, CanCreateRenderContext) { + auto context = std::make_shared(GetContext()); + ASSERT_TRUE(context->IsValid()); +} + +TEST_F(TypographerTest, CanCreateGlyphAtlas) { + auto context = std::make_shared(GetContext()); + ASSERT_TRUE(context->IsValid()); + SkFont sk_font; + auto blob = SkTextBlob::MakeFromString("hello", sk_font); + ASSERT_TRUE(blob); + auto atlas = context->CreateGlyphAtlas(TextFrameFromTextBlob(blob)); + ASSERT_NE(atlas, nullptr); + OpenPlaygroundHere([](auto&) { return true; }); +} + +} // namespace testing +} // namespace impeller From 2bda26a0ac66f98e742ccc5b499161fa7ad0d53b Mon Sep 17 00:00:00 2001 From: godofredoc Date: Fri, 25 Feb 2022 17:15:18 -0800 Subject: [PATCH 311/433] Remove schedule runs of scorecards. (#38) This will also add dependabot to auto update the workflows dependencies. Bug: https://github.com/flutter/flutter/issues/99185 --- impeller/.github/dependabot.yml | 16 ++++++++++++++++ .../.github/workflows/scorecards-analysis.yml | 10 ++++------ 2 files changed, 20 insertions(+), 6 deletions(-) create mode 100644 impeller/.github/dependabot.yml diff --git a/impeller/.github/dependabot.yml b/impeller/.github/dependabot.yml new file mode 100644 index 0000000000000..7c509aff972c4 --- /dev/null +++ b/impeller/.github/dependabot.yml @@ -0,0 +1,16 @@ +# See Dependabot documentation for all configuration options: +# https://help.github.com/github/administering-a-repository/configuration-options-for-dependency-updates + +version: 2 +updates: + - package-ecosystem: "github-actions" + directory: "/" + schedule: + interval: "daily" + reviewers: + - "hixie" + - "godofredoc" + labels: + - "team" + - "team: infra" + - "waiting for tree to go green" diff --git a/impeller/.github/workflows/scorecards-analysis.yml b/impeller/.github/workflows/scorecards-analysis.yml index 8f29792baba4b..4866e8dead094 100644 --- a/impeller/.github/workflows/scorecards-analysis.yml +++ b/impeller/.github/workflows/scorecards-analysis.yml @@ -2,8 +2,6 @@ name: Scorecards supply-chain security on: # Only the default branch is supported. branch_protection_rule: - schedule: - - cron: '37 18 * * 2' push: branches: [ main ] @@ -22,12 +20,12 @@ jobs: steps: - name: "Checkout code" - uses: actions/checkout@ec3a7ce113134d7a93b817d10a8272cb61118579 # v2.4.0 + uses: actions/checkout@ec3a7ce113134d7a93b817d10a8272cb61118579 with: persist-credentials: false - name: "Run analysis" - uses: ossf/scorecard-action@b614d455ee90608b5e36e3299cd50d457eb37d5f # v1.0.3 + uses: ossf/scorecard-action@b614d455ee90608b5e36e3299cd50d457eb37d5f with: results_file: results.sarif results_format: sarif @@ -42,7 +40,7 @@ jobs: # Upload the results as artifacts (optional). - name: "Upload artifact" - uses: actions/upload-artifact@82c141cc518b40d92cc801eee768e7aafc9c2fa2 # v2.3.1 + uses: actions/upload-artifact@82c141cc518b40d92cc801eee768e7aafc9c2fa2 with: name: SARIF file path: results.sarif @@ -50,6 +48,6 @@ jobs: # Upload the results to GitHub's code scanning dashboard. - name: "Upload to code-scanning" - uses: github/codeql-action/upload-sarif@5f532563584d71fdef14ee64d17bafb34f751ce5 # v1.0.26 + uses: github/codeql-action/upload-sarif@5f532563584d71fdef14ee64d17bafb34f751ce5 with: sarif_file: results.sarif From a6f8fd9bc7e54009c0f60b1fc4b9e3e8095c5dd2 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 25 Feb 2022 17:19:21 -0800 Subject: [PATCH 312/433] Bump ossf/scorecard-action from 1.0.3 to 1.0.4 (#40) Bumps [ossf/scorecard-action](https://github.com/ossf/scorecard-action) from 1.0.3 to 1.0.4. - [Release notes](https://github.com/ossf/scorecard-action/releases) - [Commits](https://github.com/ossf/scorecard-action/compare/b614d455ee90608b5e36e3299cd50d457eb37d5f...c1aec4ac820532bab364f02a81873c555a0ba3a1) --- updated-dependencies: - dependency-name: ossf/scorecard-action dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- impeller/.github/workflows/scorecards-analysis.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/impeller/.github/workflows/scorecards-analysis.yml b/impeller/.github/workflows/scorecards-analysis.yml index 4866e8dead094..38f2b159eeb1e 100644 --- a/impeller/.github/workflows/scorecards-analysis.yml +++ b/impeller/.github/workflows/scorecards-analysis.yml @@ -25,7 +25,7 @@ jobs: persist-credentials: false - name: "Run analysis" - uses: ossf/scorecard-action@b614d455ee90608b5e36e3299cd50d457eb37d5f + uses: ossf/scorecard-action@c1aec4ac820532bab364f02a81873c555a0ba3a1 with: results_file: results.sarif results_format: sarif From edc8791ceb51210a991ff9c2f1e606e169cfd893 Mon Sep 17 00:00:00 2001 From: Brandon DeRosier Date: Fri, 25 Feb 2022 17:29:06 -0800 Subject: [PATCH 313/433] Fix license to make the license script happy (#37) --- impeller/typographer/text_render_context.cc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/impeller/typographer/text_render_context.cc b/impeller/typographer/text_render_context.cc index 600e21391547e..98b74047670f9 100644 --- a/impeller/typographer/text_render_context.cc +++ b/impeller/typographer/text_render_context.cc @@ -1,4 +1,4 @@ -// Copyright 2019 The Fuchsia Authors. All rights reserved. +// Copyright 2013 The Flutter Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. From 357895eee9ba2ca9893263c79f9c3889154c2fde Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 25 Feb 2022 17:55:53 -0800 Subject: [PATCH 314/433] Bump github/codeql-action from 1.0.26 to 1.1.3 (#39) Bumps [github/codeql-action](https://github.com/github/codeql-action) from 1.0.26 to 1.1.3. - [Release notes](https://github.com/github/codeql-action/releases) - [Changelog](https://github.com/github/codeql-action/blob/main/CHANGELOG.md) - [Commits](https://github.com/github/codeql-action/compare/5f532563584d71fdef14ee64d17bafb34f751ce5...75f07e7ab2ee63cba88752d8c696324e4df67466) --- updated-dependencies: - dependency-name: github/codeql-action dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- impeller/.github/workflows/scorecards-analysis.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/impeller/.github/workflows/scorecards-analysis.yml b/impeller/.github/workflows/scorecards-analysis.yml index 38f2b159eeb1e..6c6812bf36441 100644 --- a/impeller/.github/workflows/scorecards-analysis.yml +++ b/impeller/.github/workflows/scorecards-analysis.yml @@ -48,6 +48,6 @@ jobs: # Upload the results to GitHub's code scanning dashboard. - name: "Upload to code-scanning" - uses: github/codeql-action/upload-sarif@5f532563584d71fdef14ee64d17bafb34f751ce5 + uses: github/codeql-action/upload-sarif@75f07e7ab2ee63cba88752d8c696324e4df67466 with: sarif_file: results.sarif From 4545a204c318ba85c9c9f04e983084b9a635f3f6 Mon Sep 17 00:00:00 2001 From: Brandon DeRosier Date: Mon, 28 Feb 2022 11:40:43 -0800 Subject: [PATCH 315/433] Remove break corner cases, simplify strokes, and generate closed path joins (#41) --- impeller/entity/contents.cc | 77 +++++++-------- impeller/geometry/geometry_unittests.cc | 123 ++++++++++++++++++++++-- impeller/geometry/path.cc | 98 ++++++++++++------- impeller/geometry/path.h | 43 ++++++--- impeller/geometry/path_builder.cc | 21 +++- impeller/geometry/path_component.cc | 2 +- impeller/geometry/path_component.h | 12 ++- impeller/renderer/tessellator.cc | 22 ++--- 8 files changed, 281 insertions(+), 117 deletions(-) diff --git a/impeller/entity/contents.cc b/impeller/entity/contents.cc index 6288d3b3c118e..44bea6c025b9c 100644 --- a/impeller/entity/contents.cc +++ b/impeller/entity/contents.cc @@ -5,6 +5,7 @@ #include "impeller/entity/contents.h" #include +#include #include "flutter/fml/logging.h" #include "impeller/entity/content_context.h" @@ -324,62 +325,60 @@ static VertexBuffer CreateSolidStrokeVertices(const Path& path, VertexBufferBuilder vtx_builder; auto polyline = path.CreatePolyline(); - size_t point_i = 0; if (polyline.points.size() < 2) { return {}; // Nothing to render. } VS::PerVertexData vtx; - // Cursor state. - Point direction; + // Normal state. Point normal; Point previous_normal; // Used for computing joins. - auto compute_normals = [&](size_t point_i) { + + auto compute_normal = [&polyline, &normal, &previous_normal](size_t point_i) { previous_normal = normal; - direction = + Point direction = (polyline.points[point_i] - polyline.points[point_i - 1]).Normalize(); normal = {-direction.y, direction.x}; }; - compute_normals(1); - // Break state. - auto breaks_it = polyline.breaks.begin(); - size_t break_end = - breaks_it != polyline.breaks.end() ? *breaks_it : polyline.points.size(); + for (size_t contour_i = 0; contour_i < polyline.contours.size(); + contour_i++) { + size_t contour_start_point_i, contour_end_point_i; + std::tie(contour_start_point_i, contour_end_point_i) = + polyline.GetContourPointBounds(contour_i); + + if (contour_end_point_i - contour_start_point_i < 2) { + continue; // This contour has no renderable content. + } - while (point_i < polyline.points.size()) { - if (point_i > 0) { - compute_normals(point_i); + // The first point's normal is always the same as + compute_normal(contour_start_point_i + 1); + const Point contour_first_normal = normal; + if (contour_i > 0) { // This branch only executes when we've just finished drawing a contour // and are switching to a new one. // We're drawing a triangle strip, so we need to "pick up the pen" by // appending transparent vertices between the end of the previous contour // and the beginning of the new contour. - vtx.vertex_position = polyline.points[point_i - 1]; + vtx.vertex_position = polyline.points[contour_start_point_i - 1]; vtx.vertex_normal = {}; vtx.pen_down = 0.0; vtx_builder.AppendVertex(vtx); - vtx.vertex_position = polyline.points[point_i]; + vtx.vertex_position = polyline.points[contour_start_point_i]; vtx_builder.AppendVertex(vtx); } // Generate start cap. - CreateCap(vtx_builder, polyline.points[point_i], -direction); + if (!polyline.contours[contour_i].is_closed) { + CreateCap(vtx_builder, polyline.points[contour_start_point_i], -normal); + } // Generate contour geometry. - size_t contour_point_i = 0; - while (point_i < break_end) { - if (contour_point_i > 0) { - if (contour_point_i > 1) { - // Generate join from the previous line to the current line. - CreateJoin(vtx_builder, polyline.points[point_i - 1], previous_normal, - normal); - } else { - compute_normals(point_i); - } - + for (size_t point_i = contour_start_point_i; point_i < contour_end_point_i; + point_i++) { + if (point_i > contour_start_point_i) { // Generate line rect. vtx.vertex_position = polyline.points[point_i - 1]; vtx.pen_down = 1.0; @@ -393,20 +392,22 @@ static VertexBuffer CreateSolidStrokeVertices(const Path& path, vtx.vertex_normal = -normal; vtx_builder.AppendVertex(vtx); - compute_normals(point_i + 1); - } + if (point_i < contour_end_point_i - 1) { + compute_normal(point_i + 1); - ++contour_point_i; - ++point_i; + // Generate join from the current line to the next line. + CreateJoin(vtx_builder, polyline.points[point_i], previous_normal, + normal); + } + } } - // Generate end cap. - CreateCap(vtx_builder, polyline.points[point_i - 1], -direction); - - if (break_end < polyline.points.size()) { - ++breaks_it; - break_end = breaks_it != polyline.breaks.end() ? *breaks_it - : polyline.points.size(); + // Generate end cap or join. + if (!polyline.contours[contour_i].is_closed) { + CreateCap(vtx_builder, polyline.points[contour_end_point_i - 1], normal); + } else { + CreateJoin(vtx_builder, polyline.points[contour_start_point_i], normal, + contour_first_normal); } } diff --git a/impeller/geometry/geometry_unittests.cc b/impeller/geometry/geometry_unittests.cc index 2d551d32ee9f8..c663764512f57 100644 --- a/impeller/geometry/geometry_unittests.cc +++ b/impeller/geometry/geometry_unittests.cc @@ -149,13 +149,13 @@ TEST(GeometryTest, SimplePath) { .AddQuadraticComponent({100, 100}, {200, 200}, {300, 300}) .AddCubicComponent({300, 300}, {400, 400}, {500, 500}, {600, 600}); - ASSERT_EQ(path.GetComponentCount(), 3u); + ASSERT_EQ(path.GetComponentCount(), 4u); path.EnumerateComponents( [](size_t index, const LinearPathComponent& linear) { Point p1(0, 0); Point p2(100, 100); - ASSERT_EQ(index, 0u); + ASSERT_EQ(index, 1u); ASSERT_EQ(linear.p1, p1); ASSERT_EQ(linear.p2, p2); }, @@ -163,7 +163,7 @@ TEST(GeometryTest, SimplePath) { Point p1(100, 100); Point cp(200, 200); Point p2(300, 300); - ASSERT_EQ(index, 1u); + ASSERT_EQ(index, 2u); ASSERT_EQ(quad.p1, p1); ASSERT_EQ(quad.cp, cp); ASSERT_EQ(quad.p2, p2); @@ -173,13 +173,18 @@ TEST(GeometryTest, SimplePath) { Point cp1(400, 400); Point cp2(500, 500); Point p2(600, 600); - ASSERT_EQ(index, 2u); + ASSERT_EQ(index, 3u); ASSERT_EQ(cubic.p1, p1); ASSERT_EQ(cubic.cp1, cp1); ASSERT_EQ(cubic.cp2, cp2); ASSERT_EQ(cubic.p2, p2); }, - [](size_t index, const MovePathComponent& move) { ASSERT_TRUE(false); }); + [](size_t index, const ContourComponent& contour) { + Point p1(0, 0); + ASSERT_EQ(index, 0u); + ASSERT_EQ(contour.destination, p1); + ASSERT_FALSE(contour.is_closed); + }); } TEST(GeometryTest, BoundingBoxCubic) { @@ -638,15 +643,15 @@ TEST(GeometryTest, CubicPathComponentPolylineDoesNotIncludePointOne) { TEST(GeometryTest, PathCreatePolyLineDoesNotDuplicatePoints) { Path path; - path.AddMoveComponent({10, 10}); + path.AddContourComponent({10, 10}); path.AddLinearComponent({10, 10}, {20, 20}); path.AddLinearComponent({20, 20}, {30, 30}); - path.AddMoveComponent({40, 40}); + path.AddContourComponent({40, 40}); path.AddLinearComponent({40, 40}, {50, 50}); auto polyline = path.CreatePolyline(); - ASSERT_EQ(polyline.breaks.size(), 2u); + ASSERT_EQ(polyline.contours.size(), 2u); ASSERT_EQ(polyline.points.size(), 5u); ASSERT_EQ(polyline.points[0].x, 10); ASSERT_EQ(polyline.points[1].x, 20); @@ -655,5 +660,107 @@ TEST(GeometryTest, PathCreatePolyLineDoesNotDuplicatePoints) { ASSERT_EQ(polyline.points[4].x, 50); } +TEST(GeometryTest, PathBuilderSetsCorrectContourPropertiesForAddCommands) { + // Closed shapes. + { + Path path = PathBuilder{}.AddCircle({100, 100}, 50).TakePath(); + ContourComponent contour; + path.GetContourComponentAtIndex(0, contour); + ASSERT_POINT_NEAR(contour.destination, Point(100, 50)); + ASSERT_TRUE(contour.is_closed); + } + + { + Path path = PathBuilder{}.AddOval(Rect(100, 100, 100, 100)).TakePath(); + ContourComponent contour; + path.GetContourComponentAtIndex(0, contour); + ASSERT_POINT_NEAR(contour.destination, Point(150, 100)); + ASSERT_TRUE(contour.is_closed); + } + + { + Path path = PathBuilder{}.AddRect(Rect(100, 100, 100, 100)).TakePath(); + ContourComponent contour; + path.GetContourComponentAtIndex(0, contour); + ASSERT_POINT_NEAR(contour.destination, Point(100, 100)); + ASSERT_TRUE(contour.is_closed); + } + + { + Path path = + PathBuilder{}.AddRoundedRect(Rect(100, 100, 100, 100), 10).TakePath(); + ContourComponent contour; + path.GetContourComponentAtIndex(0, contour); + ASSERT_POINT_NEAR(contour.destination, Point(110, 100)); + ASSERT_TRUE(contour.is_closed); + } + + // Open shapes. + { + Point p(100, 100); + Path path = PathBuilder{}.AddLine(p, {200, 100}).TakePath(); + ContourComponent contour; + path.GetContourComponentAtIndex(0, contour); + ASSERT_POINT_NEAR(contour.destination, p); + ASSERT_FALSE(contour.is_closed); + } + + { + Path path = + PathBuilder{} + .AddCubicCurve({100, 100}, {100, 50}, {100, 150}, {200, 100}) + .TakePath(); + ContourComponent contour; + path.GetContourComponentAtIndex(0, contour); + ASSERT_POINT_NEAR(contour.destination, Point(100, 100)); + ASSERT_FALSE(contour.is_closed); + } + + { + Path path = PathBuilder{} + .AddQuadraticCurve({100, 100}, {100, 50}, {200, 100}) + .TakePath(); + ContourComponent contour; + path.GetContourComponentAtIndex(0, contour); + ASSERT_POINT_NEAR(contour.destination, Point(100, 100)); + ASSERT_FALSE(contour.is_closed); + } +} + +TEST(GeometryTest, PathCreatePolylineGeneratesCorrectContourData) { + Path::Polyline polyline = PathBuilder{} + .AddLine({100, 100}, {200, 100}) + .MoveTo({100, 200}) + .LineTo({150, 250}) + .LineTo({200, 200}) + .Close() + .TakePath() + .CreatePolyline(); + ASSERT_EQ(polyline.points.size(), 6u); + ASSERT_EQ(polyline.contours.size(), 2u); + ASSERT_EQ(polyline.contours[0].is_closed, false); + ASSERT_EQ(polyline.contours[0].start_index, 0u); + ASSERT_EQ(polyline.contours[1].is_closed, true); + ASSERT_EQ(polyline.contours[1].start_index, 2u); +} + +TEST(GeometryTest, PolylineGetContourPointBoundsReturnsCorrectRanges) { + Path::Polyline polyline = PathBuilder{} + .AddLine({100, 100}, {200, 100}) + .MoveTo({100, 200}) + .LineTo({150, 250}) + .LineTo({200, 200}) + .Close() + .TakePath() + .CreatePolyline(); + size_t a1, a2, b1, b2; + std::tie(a1, a2) = polyline.GetContourPointBounds(0); + std::tie(b1, b2) = polyline.GetContourPointBounds(1); + ASSERT_EQ(a1, 0u); + ASSERT_EQ(a2, 2u); + ASSERT_EQ(b1, 2u); + ASSERT_EQ(b2, 6u); +} + } // namespace testing } // namespace impeller diff --git a/impeller/geometry/path.cc b/impeller/geometry/path.cc index 4bacb04ea9bb0..3a31ac61b96d7 100644 --- a/impeller/geometry/path.cc +++ b/impeller/geometry/path.cc @@ -6,12 +6,28 @@ #include +#include "flutter/fml/logging.h" +#include "impeller/geometry/path_component.h" + namespace impeller { -Path::Path() = default; +Path::Path() { + AddContourComponent({}); +}; Path::~Path() = default; +std::tuple Path::Polyline::GetContourPointBounds( + size_t contour_index) const { + FML_DCHECK(contour_index < contours.size()); + + const size_t start_index = contours[contour_index].start_index; + const size_t end_index = (contour_index >= contours.size() - 1) + ? points.size() + : contours[contour_index + 1].start_index; + return std::make_tuple(start_index, end_index); +} + size_t Path::GetComponentCount() const { return components_.size(); } @@ -42,16 +58,27 @@ Path& Path::AddCubicComponent(Point p1, Point cp1, Point cp2, Point p2) { return *this; } -Path& Path::AddMoveComponent(Point destination) { - moves_.emplace_back(destination); - components_.emplace_back(ComponentType::kMove, moves_.size() - 1); +Path& Path::AddContourComponent(Point destination, bool is_closed) { + if (components_.size() > 0 && + components_.back().type == ComponentType::kContour) { + // Never insert contiguous contours. + contours_.back() = ContourComponent(destination, is_closed); + } else { + contours_.emplace_back(ContourComponent(destination, is_closed)); + components_.emplace_back(ComponentType::kContour, contours_.size() - 1); + } return *this; } -void Path::EnumerateComponents(Applier linear_applier, - Applier quad_applier, - Applier cubic_applier, - Applier move_applier) const { +void Path::SetContourClosed(bool is_closed) { + contours_.back().is_closed = is_closed; +} + +void Path::EnumerateComponents( + Applier linear_applier, + Applier quad_applier, + Applier cubic_applier, + Applier contour_applier) const { size_t currentIndex = 0; for (const auto& component : components_) { switch (component.type) { @@ -70,9 +97,9 @@ void Path::EnumerateComponents(Applier linear_applier, cubic_applier(currentIndex, cubics_[component.index]); } break; - case ComponentType::kMove: - if (move_applier) { - move_applier(currentIndex, moves_[component.index]); + case ComponentType::kContour: + if (contour_applier) { + contour_applier(currentIndex, contours_[component.index]); } break; } @@ -123,17 +150,17 @@ bool Path::GetCubicComponentAtIndex(size_t index, return true; } -bool Path::GetMoveComponentAtIndex(size_t index, - MovePathComponent& move) const { +bool Path::GetContourComponentAtIndex(size_t index, + ContourComponent& move) const { if (index >= components_.size()) { return false; } - if (components_[index].type != ComponentType::kMove) { + if (components_[index].type != ComponentType::kContour) { return false; } - move = moves_[components_[index].index]; + move = contours_[components_[index].index]; return true; } @@ -180,38 +207,32 @@ bool Path::UpdateCubicComponentAtIndex(size_t index, return true; } -bool Path::UpdateMoveComponentAtIndex(size_t index, - const MovePathComponent& move) { +bool Path::UpdateContourComponentAtIndex(size_t index, + const ContourComponent& move) { if (index >= components_.size()) { return false; } - if (components_[index].type != ComponentType::kMove) { + if (components_[index].type != ComponentType::kContour) { return false; } - moves_[components_[index].index] = move; + contours_[components_[index].index] = move; return true; } Path::Polyline Path::CreatePolyline( const SmoothingApproximation& approximation) const { Polyline polyline; - // TODO(99177): Refactor this to have component polyline creation always - // exclude the first point, and append the destination point for - // move components. See issue for details. - bool new_contour = true; - auto collect_points = [&polyline, - &new_contour](const std::vector& collection) { - size_t offset = new_contour ? 0 : 1; - new_contour = false; - - polyline.points.reserve(polyline.points.size() + collection.size() - - offset); - polyline.points.insert(polyline.points.end(), collection.begin() + offset, + auto collect_points = [&polyline](const std::vector& collection) { + polyline.points.reserve(polyline.points.size() + collection.size()); + polyline.points.insert(polyline.points.end(), collection.begin(), collection.end()); }; - for (const auto& component : components_) { + // for (const auto& component : components_) { + for (size_t component_i = 0; component_i < components_.size(); + component_i++) { + const auto& component = components_[component_i]; switch (component.type) { case ComponentType::kLinear: collect_points(linears_[component.index].CreatePolyline()); @@ -222,9 +243,16 @@ Path::Polyline Path::CreatePolyline( case ComponentType::kCubic: collect_points(cubics_[component.index].CreatePolyline(approximation)); break; - case ComponentType::kMove: - polyline.breaks.insert(polyline.points.size()); - new_contour = true; + case ComponentType::kContour: + if (component_i == components_.size() - 1) { + // If the last component is a contour, that means it's an empty + // contour, so skip it. + continue; + } + const auto& contour = contours_[component.index]; + polyline.contours.push_back({.start_index = polyline.points.size(), + .is_closed = contour.is_closed}); + collect_points({contour.destination}); break; } } diff --git a/impeller/geometry/path.h b/impeller/geometry/path.h index 450000e896f3e..061664742f4a6 100644 --- a/impeller/geometry/path.h +++ b/impeller/geometry/path.h @@ -7,6 +7,7 @@ #include #include #include +#include #include #include "impeller/geometry/path_component.h" @@ -39,7 +40,15 @@ class Path { kLinear, kQuadratic, kCubic, - kMove, + kContour, + }; + + struct PolylineContour { + /// Index that denotes the first point of this contour. + size_t start_index; + /// Denotes whether the last point of this contour is connected to the first + /// point of this contour or not. + bool is_closed; }; /// One or more contours represented as a series of points and indices in @@ -48,8 +57,12 @@ class Path { /// Points in the polyline, which may represent multiple contours specified /// by indices in |breaks|. std::vector points; - /// Indices of points that end a subcontour. - std::set breaks; + std::vector contours; + + /// Convenience method to compute the start (inclusive) and end (exclusive) + /// point of the given contour index. + std::tuple GetContourPointBounds( + size_t contour_index) const; }; Path(); @@ -68,14 +81,16 @@ class Path { Path& AddCubicComponent(Point p1, Point cp1, Point cp2, Point p2); - Path& AddMoveComponent(Point destination); + Path& AddContourComponent(Point destination, bool is_closed = false); + + void SetContourClosed(bool is_closed); template using Applier = std::function; - void EnumerateComponents(Applier linearApplier, - Applier quadApplier, - Applier cubicApplier, - Applier moveApplier) const; + void EnumerateComponents(Applier linear_applier, + Applier quad_applier, + Applier cubic_applier, + Applier contour_applier) const; bool GetLinearComponentAtIndex(size_t index, LinearPathComponent& linear) const; @@ -85,7 +100,8 @@ class Path { bool GetCubicComponentAtIndex(size_t index, CubicPathComponent& cubic) const; - bool GetMoveComponentAtIndex(size_t index, MovePathComponent& move) const; + bool GetContourComponentAtIndex(size_t index, + ContourComponent& contour) const; bool UpdateLinearComponentAtIndex(size_t index, const LinearPathComponent& linear); @@ -95,7 +111,8 @@ class Path { bool UpdateCubicComponentAtIndex(size_t index, CubicPathComponent& cubic); - bool UpdateMoveComponentAtIndex(size_t index, const MovePathComponent& move); + bool UpdateContourComponentAtIndex(size_t index, + const ContourComponent& contour); Polyline CreatePolyline( const SmoothingApproximation& approximation = {}) const; @@ -111,8 +128,8 @@ class Path { ComponentIndexPair() {} - ComponentIndexPair(ComponentType aType, size_t aIndex) - : type(aType), index(aIndex) {} + ComponentIndexPair(ComponentType a_type, size_t a_index) + : type(a_type), index(a_index) {} }; FillType fill_ = FillType::kNonZero; @@ -120,7 +137,7 @@ class Path { std::vector linears_; std::vector quads_; std::vector cubics_; - std::vector moves_; + std::vector contours_; }; } // namespace impeller diff --git a/impeller/geometry/path_builder.cc b/impeller/geometry/path_builder.cc index aba307b0893bf..1e97b52afba6f 100644 --- a/impeller/geometry/path_builder.cc +++ b/impeller/geometry/path_builder.cc @@ -27,12 +27,14 @@ Path PathBuilder::TakePath(FillType fill) { PathBuilder& PathBuilder::MoveTo(Point point, bool relative) { current_ = relative ? current_ + point : point; subpath_start_ = current_; - prototype_.AddMoveComponent(current_); + prototype_.AddContourComponent(current_); return *this; } PathBuilder& PathBuilder::Close() { LineTo(subpath_start_); + prototype_.SetContourClosed(true); + prototype_.AddContourComponent(current_); return *this; } @@ -152,6 +154,7 @@ PathBuilder& PathBuilder::SmoothCubicCurveTo(Point controlPoint2, } PathBuilder& PathBuilder::AddQuadraticCurve(Point p1, Point cp, Point p2) { + MoveTo(p1); prototype_.AddQuadraticComponent(p1, cp, p2); return *this; } @@ -160,6 +163,7 @@ PathBuilder& PathBuilder::AddCubicCurve(Point p1, Point cp1, Point cp2, Point p2) { + MoveTo(p1); prototype_.AddCubicComponent(p1, cp1, cp2, p2); return *this; } @@ -172,10 +176,12 @@ PathBuilder& PathBuilder::AddRect(Rect rect) { auto br = rect.origin + Point{rect.size.width, rect.size.height}; auto tr = rect.origin + Point{rect.size.width, 0.0}; + MoveTo(tl); prototype_.AddLinearComponent(tl, tr) .AddLinearComponent(tr, br) .AddLinearComponent(br, bl) .AddLinearComponent(bl, tl); + Close(); return *this; } @@ -201,6 +207,8 @@ PathBuilder& PathBuilder::AddRoundedRect(Rect rect, RoundingRadii radii) { const auto magic_bottom_left = radii.bottom_left * kArcApproximationMagic; const auto magic_top_left = radii.top_left * kArcApproximationMagic; + MoveTo({rect.origin.x + radii.top_left.x, rect.origin.y}); + //---------------------------------------------------------------------------- // Top line. // @@ -277,6 +285,8 @@ PathBuilder& PathBuilder::AddRoundedRect(Rect rect, RoundingRadii radii) { {rect.origin.x + radii.top_left.x - magic_top_left.x, rect.origin.y}, {rect.origin.x + radii.top_left.x, rect.origin.y}); + Close(); + return *this; } @@ -286,6 +296,8 @@ PathBuilder& PathBuilder::AddOval(const Rect& container) { container.origin.y + (container.size.height * 0.5f)}; const Point m = {kArcApproximationMagic * r.x, kArcApproximationMagic * r.y}; + MoveTo({c.x, c.y - r.y}); + //---------------------------------------------------------------------------- // Top right arc. // @@ -322,10 +334,13 @@ PathBuilder& PathBuilder::AddOval(const Rect& container) { {c.x, c.y - r.y} // p2 ); + Close(); + return *this; } PathBuilder& PathBuilder::AddLine(const Point& p1, const Point& p2) { + MoveTo(p1); prototype_.AddLinearComponent(p1, p2); return *this; } @@ -344,8 +359,8 @@ PathBuilder& PathBuilder::AddPath(const Path& path) { auto cubic = [&](size_t index, const CubicPathComponent& c) { prototype_.AddCubicComponent(c.p1, c.cp1, c.cp2, c.p2); }; - auto move = [&](size_t index, const MovePathComponent& m) { - prototype_.AddMoveComponent(m.destination); + auto move = [&](size_t index, const ContourComponent& m) { + prototype_.AddContourComponent(m.destination); }; path.EnumerateComponents(linear, quadratic, cubic, move); return *this; diff --git a/impeller/geometry/path_component.cc b/impeller/geometry/path_component.cc index 919a14c939b6c..eb842e509e9c1 100644 --- a/impeller/geometry/path_component.cc +++ b/impeller/geometry/path_component.cc @@ -64,7 +64,7 @@ Point LinearPathComponent::Solve(Scalar time) const { } std::vector LinearPathComponent::CreatePolyline() const { - return {p1, p2}; + return {p2}; } std::vector LinearPathComponent::Extrema() const { diff --git a/impeller/geometry/path_component.h b/impeller/geometry/path_component.h index c9f03ddad8db4..60331ef899232 100644 --- a/impeller/geometry/path_component.h +++ b/impeller/geometry/path_component.h @@ -107,15 +107,17 @@ struct CubicPathComponent { } }; -struct MovePathComponent { +struct ContourComponent { Point destination; + bool is_closed; - MovePathComponent() {} + ContourComponent() {} - MovePathComponent(Point p) : destination(p) {} + ContourComponent(Point p, bool is_closed = false) + : destination(p), is_closed(is_closed) {} - bool operator==(const MovePathComponent& other) const { - return destination == other.destination; + bool operator==(const ContourComponent& other) const { + return destination == other.destination && is_closed == other.is_closed; } }; diff --git a/impeller/renderer/tessellator.cc b/impeller/renderer/tessellator.cc index d98225d54f6d5..44f18306e0bb8 100644 --- a/impeller/renderer/tessellator.cc +++ b/impeller/renderer/tessellator.cc @@ -36,7 +36,7 @@ static void DestroyTessellator(TESStesselator* tessellator) { } } -bool Tessellator::Tessellate(const Path::Polyline& contours, +bool Tessellator::Tessellate(const Path::Polyline& polyline, VertexCallback callback) const { TRACE_EVENT0("impeller", "Tessellator::Tessellate"); if (!callback) { @@ -61,24 +61,18 @@ bool Tessellator::Tessellate(const Path::Polyline& contours, /// Feed contour information to the tessellator. /// static_assert(sizeof(Point) == 2 * sizeof(float)); - size_t start_point_index = 0; - for (size_t end_point_index : contours.breaks) { - end_point_index = std::min(end_point_index, contours.points.size()); + for (size_t contour_i = 0; contour_i < polyline.contours.size(); + contour_i++) { + size_t start_point_index, end_point_index; + std::tie(start_point_index, end_point_index) = + polyline.GetContourPointBounds(contour_i); + ::tessAddContour(tessellator.get(), // the C tessellator kVertexSize, // - contours.points.data() + start_point_index, // + polyline.points.data() + start_point_index, // sizeof(Point), // end_point_index - start_point_index // ); - start_point_index = end_point_index; - } - if (start_point_index < contours.points.size()) { - ::tessAddContour(tessellator.get(), // the C tessellator - kVertexSize, // - contours.points.data() + start_point_index, // - sizeof(Point), // - contours.points.size() - start_point_index // - ); } //---------------------------------------------------------------------------- From e382f13abe3f04c4701974e153c10a272fd4f011 Mon Sep 17 00:00:00 2001 From: Chinmay Garde Date: Mon, 28 Feb 2022 12:55:43 -0800 Subject: [PATCH 316/433] Account for glyph extents in the atlas. (#42) --- impeller/entity/contents.cc | 12 ++-- .../backends/skia/text_frame_skia.cc | 2 + .../backends/skia/text_render_context_skia.cc | 56 +++++++++++++------ impeller/typographer/font.cc | 7 --- impeller/typographer/font.h | 34 ++++++++--- 5 files changed, 70 insertions(+), 41 deletions(-) diff --git a/impeller/entity/contents.cc b/impeller/entity/contents.cc index 44bea6c025b9c..07ec6db4eb096 100644 --- a/impeller/entity/contents.cc +++ b/impeller/entity/contents.cc @@ -635,11 +635,7 @@ bool TextContents::Render(const ContentContext& renderer, // Iterate through all the runs in the blob. for (const auto& run : frame_.GetRuns()) { auto font = run.GetFont(); - auto glyph_size = font.GetGlyphSize(); - if (!glyph_size.has_value()) { - VALIDATION_LOG << "Glyph has no size."; - return false; - } + auto glyph_size = ISize::Ceil(font.GetMetrics().GetBoundingBox().size); // Draw each glyph individually. This should probably be batched. for (const auto& glyph_position : run.GetGlyphPositions()) { FontGlyphPair font_glyph_pair{font, glyph_position.glyph}; @@ -651,9 +647,9 @@ bool TextContents::Render(const ContentContext& renderer, VS::GlyphInfo glyph_info; glyph_info.position = glyph_position.position.Translate( - {0.0, font.GetMetrics().ascent, 0.0}); - glyph_info.glyph_size = {static_cast(glyph_size->width), - static_cast(glyph_size->height)}; + {font.GetMetrics().min_extent.x, font.GetMetrics().ascent, 0.0}); + glyph_info.glyph_size = {static_cast(glyph_size.width), + static_cast(glyph_size.height)}; glyph_info.atlas_position = atlas_glyph_pos->origin; glyph_info.atlas_glyph_size = {atlas_glyph_pos->size.width, atlas_glyph_pos->size.height}; diff --git a/impeller/typographer/backends/skia/text_frame_skia.cc b/impeller/typographer/backends/skia/text_frame_skia.cc index 979d6fcda767b..d9db8e36f4757 100644 --- a/impeller/typographer/backends/skia/text_frame_skia.cc +++ b/impeller/typographer/backends/skia/text_frame_skia.cc @@ -22,6 +22,8 @@ static Font ToFont(const SkFont& font) { metrics.point_size = font.getSize(); metrics.ascent = sk_metrics.fAscent; metrics.descent = sk_metrics.fDescent; + metrics.min_extent = {sk_metrics.fXMin, sk_metrics.fTop}; + metrics.max_extent = {sk_metrics.fXMax, sk_metrics.fBottom}; return Font{std::move(typeface), std::move(metrics)}; } diff --git a/impeller/typographer/backends/skia/text_render_context_skia.cc b/impeller/typographer/backends/skia/text_render_context_skia.cc index 84beacfca734b..ca7b93bdad01e 100644 --- a/impeller/typographer/backends/skia/text_render_context_skia.cc +++ b/impeller/typographer/backends/skia/text_render_context_skia.cc @@ -62,21 +62,19 @@ static bool PairsFitInAtlasOfSize(const FontGlyphPair::Vector& pairs, glyph_positions.reserve(pairs.size()); for (const auto& pair : pairs) { - auto glyph_size = pair.font.GetGlyphSize(); - if (!glyph_size.has_value()) { - continue; - } + const auto glyph_size = + ISize::Ceil(pair.font.GetMetrics().GetBoundingBox().size); SkIPoint16 location_in_atlas; - if (!rect_packer->addRect(glyph_size->width, // - glyph_size->height, // - &location_in_atlas // + if (!rect_packer->addRect(glyph_size.width, // + glyph_size.height, // + &location_in_atlas // )) { return false; } glyph_positions.emplace_back(Rect::MakeXYWH(location_in_atlas.x(), // location_in_atlas.y(), // - glyph_size->width, // - glyph_size->height // + glyph_size.width, // + glyph_size.height // )); } @@ -122,22 +120,46 @@ static std::optional CreateAtlasBitmap(const GlyphAtlas& atlas, const Rect& location) -> bool { const auto position = SkPoint::Make(location.origin.x, location.origin.y); SkGlyphID glyph_id = font_glyph.glyph.index; - SkFont font( + + SkFont sk_font( TypefaceSkia::Cast(*font_glyph.font.GetTypeface()).GetSkiaTypeface(), font_glyph.font.GetMetrics().point_size); - SkFontMetrics metrics; - font.getMetrics(&metrics); + const auto& metrics = font_glyph.font.GetMetrics(); + + auto glyph_color = SK_ColorWHITE; + +#if 0 + { + glyph_color = SkColorSetARGB(255, // + std::rand() % 255, // + std::rand() % 255, // + std::rand() % 255 // + ); + SkPaint debug_paint; + debug_paint.setARGB(255 / 4, // + std::rand() % 255, // + std::rand() % 255, // + std::rand() % 255 // + ); + canvas->drawRect(SkRect::MakeXYWH(location.origin.x, // + location.origin.y, // + location.size.width, // + location.size.height // + ), + debug_paint); + } +#endif SkPaint glyph_paint; - glyph_paint.setColor(SK_ColorWHITE); + glyph_paint.setColor(glyph_color); canvas->drawGlyphs(1u, // count &glyph_id, // glyphs &position, // positions - SkPoint::Make(0.0, - -metrics.fAscent), // origin - font, // font - glyph_paint // paint + SkPoint::Make(-metrics.min_extent.x, + -metrics.ascent), // origin + sk_font, // font + glyph_paint // paint ); return true; }); diff --git a/impeller/typographer/font.cc b/impeller/typographer/font.cc index e675b0431eba7..f8287341a2f10 100644 --- a/impeller/typographer/font.cc +++ b/impeller/typographer/font.cc @@ -34,13 +34,6 @@ bool Font::IsEqual(const Font& other) const { is_valid_ == other.is_valid_ && metrics_ == other.metrics_; } -std::optional Font::GetGlyphSize() const { - if (!IsValid()) { - return std::nullopt; - } - return ISize::Ceil(typeface_->GetBoundingBox().size * metrics_.point_size); -} - const Font::Metrics& Font::GetMetrics() const { return metrics_; } diff --git a/impeller/typographer/font.h b/impeller/typographer/font.h index 2b4af8f86f880..691b5d59483f0 100644 --- a/impeller/typographer/font.h +++ b/impeller/typographer/font.h @@ -44,10 +44,34 @@ class Font : public Comparable { /// yields larger numbers. /// Scalar descent = 0.0f; + //-------------------------------------------------------------------------- + /// The minimum glyph extents relative to the origin. Typically negative in + /// an upper-left-origin coordinate system. + /// + Point min_extent; + //-------------------------------------------------------------------------- + /// The maximum glyph extents relative to the origin. Typically positive in + /// an upper-left-origin coordinate system. + /// + Point max_extent; + + //-------------------------------------------------------------------------- + /// @brief The union of the bounding boxes of all the glyphs. + /// + /// @return The bounding box. + /// + constexpr Rect GetBoundingBox() const { + return Rect::MakeLTRB(min_extent.x, // + min_extent.y, // + max_extent.x, // + max_extent.y // + ); + } constexpr bool operator==(const Metrics& o) const { return point_size == o.point_size && ascent == o.ascent && - descent == o.descent; + descent == o.descent && min_extent == o.min_extent && + max_extent == o.max_extent; } }; @@ -64,14 +88,6 @@ class Font : public Comparable { /// const std::shared_ptr& GetTypeface() const; - //---------------------------------------------------------------------------- - /// @brief A conservatively large scaled bounding box of all glyphs in - /// this font. - /// - /// @return The scaled glyph size. - /// - std::optional GetGlyphSize() const; - const Metrics& GetMetrics() const; // |Comparable| From 9d760a56fe16468b8f68e2729099b902e686c1bb Mon Sep 17 00:00:00 2001 From: Chinmay Garde Date: Mon, 28 Feb 2022 13:26:49 -0800 Subject: [PATCH 317/433] Relay text color from Paint. (#43) --- impeller/aiks/aiks_unittests.cc | 106 +++++++++-------------- impeller/aiks/canvas.cc | 4 +- impeller/aiks/canvas.h | 3 +- impeller/entity/contents.cc | 9 ++ impeller/entity/contents.h | 3 + impeller/entity/shaders/glyph_atlas.frag | 3 +- impeller/entity/shaders/glyph_atlas.vert | 3 + impeller/renderer/shader_types.h | 4 + 8 files changed, 66 insertions(+), 69 deletions(-) diff --git a/impeller/aiks/aiks_unittests.cc b/impeller/aiks/aiks_unittests.cc index 5531beee475d1..e8ab4e8b8d085 100644 --- a/impeller/aiks/aiks_unittests.cc +++ b/impeller/aiks/aiks_unittests.cc @@ -299,9 +299,11 @@ static sk_sp OpenFixtureAsSkData(const char* fixture_name) { mapping.release()); } -TEST_F(AiksTest, CanRenderTextFrame) { - Canvas canvas; - +bool RenderTextInCanvas(std::shared_ptr context, + Canvas& canvas, + const std::string& text, + const std::string& font_fixture, + Scalar font_size = 50.0) { Scalar baseline = 200.0; Point text_position = {100, baseline}; @@ -314,84 +316,56 @@ TEST_F(AiksTest, CanRenderTextFrame) { Paint{.color = Color::Red().WithAlpha(0.25)}); // Construct the text blob. - auto mapping = OpenFixtureAsSkData("Roboto-Regular.ttf"); - ASSERT_TRUE(mapping); + auto mapping = OpenFixtureAsSkData(font_fixture.c_str()); + if (!mapping) { + return false; + } SkFont sk_font(SkTypeface::MakeFromData(mapping), 50.0); - auto blob = SkTextBlob::MakeFromString( - "the quick brown fox jumped over the lazy dog!.?", sk_font); - ASSERT_TRUE(blob); + auto blob = SkTextBlob::MakeFromString(text.c_str(), sk_font); + if (!blob) { + return false; + } // Create the Impeller text frame and draw it at the designated baseline. auto frame = TextFrameFromTextBlob(blob); - TextRenderContextSkia text_context(GetContext()); - ASSERT_TRUE(text_context.IsValid()); + TextRenderContextSkia text_context(context); + if (!text_context.IsValid()) { + return false; + } auto atlas = text_context.CreateGlyphAtlas(frame); - ASSERT_NE(atlas, nullptr); - canvas.DrawTextFrame(std::move(frame), std::move(atlas), text_position); + if (!atlas) { + return false; + } + + Paint text_paint; + text_paint.color = Color::Yellow(); + canvas.DrawTextFrame(std::move(frame), std::move(atlas), text_position, + text_paint); + return true; +} + +TEST_F(AiksTest, CanRenderTextFrame) { + Canvas canvas; + ASSERT_TRUE(RenderTextInCanvas( + GetContext(), canvas, "the quick brown fox jumped over the lazy dog!.?", + "Roboto-Regular.ttf")); ASSERT_TRUE(OpenPlaygroundHere(canvas.EndRecordingAsPicture())); } TEST_F(AiksTest, CanRenderItalicizedText) { Canvas canvas; - - Scalar baseline = 200.0; - Point text_position = {100, baseline}; - - // Draw the baseline. - canvas.DrawRect({50, baseline, 900, 10}, - Paint{.color = Color::Aqua().WithAlpha(0.25)}); - - // Mark the point at which the text is drawn. - canvas.DrawCircle(text_position, 5.0, - Paint{.color = Color::Red().WithAlpha(0.25)}); - - // Construct the text blob. - auto mapping = OpenFixtureAsSkData("HomemadeApple.ttf"); - ASSERT_TRUE(mapping); - SkFont sk_font(SkTypeface::MakeFromData(mapping), 50.0); - auto blob = SkTextBlob::MakeFromString( - "the quick brown fox jumped over the lazy dog!.?", sk_font); - ASSERT_TRUE(blob); - - // Create the Impeller text frame and draw it at the designated baseline. - auto frame = TextFrameFromTextBlob(blob); - TextRenderContextSkia text_context(GetContext()); - ASSERT_TRUE(text_context.IsValid()); - auto atlas = text_context.CreateGlyphAtlas(frame); - ASSERT_NE(atlas, nullptr); - canvas.DrawTextFrame(std::move(frame), std::move(atlas), text_position); + ASSERT_TRUE(RenderTextInCanvas( + GetContext(), canvas, "the quick brown fox jumped over the lazy dog!.?", + "HomemadeApple.ttf")); ASSERT_TRUE(OpenPlaygroundHere(canvas.EndRecordingAsPicture())); } TEST_F(AiksTest, CanRenderEmojiTextFrame) { Canvas canvas; - - Scalar baseline = 200.0; - Point text_position = {100, baseline}; - - // Draw the baseline. - canvas.DrawRect({50, baseline, 900, 10}, - Paint{.color = Color::Aqua().WithAlpha(0.25)}); - - // Mark the point at which the text is drawn. - canvas.DrawCircle(text_position, 5.0, - Paint{.color = Color::Red().WithAlpha(0.25)}); - - // Construct the text blob. - auto mapping = OpenFixtureAsSkData("NotoColorEmoji.ttf"); - ASSERT_TRUE(mapping); - SkFont sk_font(SkTypeface::MakeFromData(mapping), 50.0); - auto blob = SkTextBlob::MakeFromString( - "😀 😃 😄 😁 😆 😅 😂 🤣 🥲 ☺️ 😊", sk_font); - ASSERT_TRUE(blob); - - // Create the Impeller text frame and draw it at the designated baseline. - auto frame = TextFrameFromTextBlob(blob); - TextRenderContextSkia text_context(GetContext()); - ASSERT_TRUE(text_context.IsValid()); - auto atlas = text_context.CreateGlyphAtlas(frame); - ASSERT_NE(atlas, nullptr); - canvas.DrawTextFrame(std::move(frame), std::move(atlas), text_position); + ASSERT_TRUE(RenderTextInCanvas( + GetContext(), canvas, + "😀 😃 😄 😁 😆 😅 😂 🤣 🥲 ☺️ 😊", + "NotoColorEmoji.ttf")); ASSERT_TRUE(OpenPlaygroundHere(canvas.EndRecordingAsPicture())); } diff --git a/impeller/aiks/canvas.cc b/impeller/aiks/canvas.cc index 0a6e214de9dc9..f9c19fe3cc2d4 100644 --- a/impeller/aiks/canvas.cc +++ b/impeller/aiks/canvas.cc @@ -254,7 +254,8 @@ void Canvas::Save(bool create_subpass) { void Canvas::DrawTextFrame(TextFrame text_frame, std::shared_ptr atlas, - Point position) { + Point position, + Paint paint) { if (!atlas || !atlas->IsValid()) { return; } @@ -262,6 +263,7 @@ void Canvas::DrawTextFrame(TextFrame text_frame, auto text_contents = std::make_shared(); text_contents->SetTextFrame(std::move(text_frame)); text_contents->SetGlyphAtlas(std::move(atlas)); + text_contents->SetColor(paint.color); Entity entity; entity.SetTransformation(GetCurrentTransformation() * diff --git a/impeller/aiks/canvas.h b/impeller/aiks/canvas.h index 88db60911ed66..56a23aaa9426e 100644 --- a/impeller/aiks/canvas.h +++ b/impeller/aiks/canvas.h @@ -77,7 +77,8 @@ class Canvas { void DrawTextFrame(TextFrame text_frame, std::shared_ptr atlas, - Point position); + Point position, + Paint paint); Picture EndRecordingAsPicture(); diff --git a/impeller/entity/contents.cc b/impeller/entity/contents.cc index 07ec6db4eb096..0cb59466a7672 100644 --- a/impeller/entity/contents.cc +++ b/impeller/entity/contents.cc @@ -568,9 +568,17 @@ void TextContents::SetGlyphAtlas(std::shared_ptr atlas) { atlas_ = std::move(atlas); } +void TextContents::SetColor(Color color) { + color_ = color; +} + bool TextContents::Render(const ContentContext& renderer, const Entity& entity, RenderPass& pass) const { + if (color_.IsTransparent()) { + return true; + } + if (!atlas_ || !atlas_->IsValid()) { VALIDATION_LOG << "Cannot render glyphs without prepared atlas."; return false; @@ -593,6 +601,7 @@ bool TextContents::Render(const ContentContext& renderer, frame_info.atlas_size = Point{static_cast(atlas_->GetTexture()->GetSize().width), static_cast(atlas_->GetTexture()->GetSize().height)}; + frame_info.text_color = ToVector(color_); VS::BindFrameInfo(cmd, pass.GetTransientsBuffer().EmplaceUniform(frame_info)); // Common fragment uniforms for all glyphs. diff --git a/impeller/entity/contents.h b/impeller/entity/contents.h index 5488f6d3a8670..3b1bec7509690 100644 --- a/impeller/entity/contents.h +++ b/impeller/entity/contents.h @@ -210,6 +210,8 @@ class TextContents final : public Contents { void SetGlyphAtlas(std::shared_ptr atlas); + void SetColor(Color color); + // |Contents| bool Render(const ContentContext& renderer, const Entity& entity, @@ -217,6 +219,7 @@ class TextContents final : public Contents { private: TextFrame frame_; + Color color_; std::shared_ptr atlas_; FML_DISALLOW_COPY_AND_ASSIGN(TextContents); diff --git a/impeller/entity/shaders/glyph_atlas.frag b/impeller/entity/shaders/glyph_atlas.frag index 0fedba1c140b1..f07e591f19d10 100644 --- a/impeller/entity/shaders/glyph_atlas.frag +++ b/impeller/entity/shaders/glyph_atlas.frag @@ -8,6 +8,7 @@ in vec2 v_unit_vertex; in vec2 v_atlas_position; in vec2 v_atlas_glyph_size; in vec2 v_atlas_size; +in vec4 v_text_color; out vec4 frag_color; @@ -18,5 +19,5 @@ void main() { frag_color = texture( glyph_atlas_sampler, v_unit_vertex * scale_perspective + offset - ); + ) * v_text_color; } diff --git a/impeller/entity/shaders/glyph_atlas.vert b/impeller/entity/shaders/glyph_atlas.vert index 7cff4886b72bd..aff822fdd4aad 100644 --- a/impeller/entity/shaders/glyph_atlas.vert +++ b/impeller/entity/shaders/glyph_atlas.vert @@ -5,6 +5,7 @@ uniform FrameInfo { mat4 mvp; vec2 atlas_size; + vec4 text_color; } frame_info; uniform GlyphInfo { @@ -20,6 +21,7 @@ out vec2 v_unit_vertex; out vec2 v_atlas_position; out vec2 v_atlas_glyph_size; out vec2 v_atlas_size; +out vec4 v_text_color; void main() { mat4 scale = mat4( @@ -37,4 +39,5 @@ void main() { v_atlas_position = glyph_info.atlas_position; v_atlas_glyph_size = glyph_info.atlas_glyph_size; v_atlas_size = frame_info.atlas_size; + v_text_color = frame_info.text_color; } diff --git a/impeller/renderer/shader_types.h b/impeller/renderer/shader_types.h index fb6e51b65e6da..0f0f9919f02b8 100644 --- a/impeller/renderer/shader_types.h +++ b/impeller/renderer/shader_types.h @@ -94,4 +94,8 @@ struct Padding { uint8_t pad_[Size]; }; +inline constexpr Vector4 ToVector(Color color) { + return {color.red, color.green, color.blue, color.alpha}; +} + } // namespace impeller From 8284c94696da41c6166034a06d04ad282f861a7b Mon Sep 17 00:00:00 2001 From: Chinmay Garde Date: Mon, 28 Feb 2022 13:52:11 -0800 Subject: [PATCH 318/433] Account for updated macro name in //flutter/fml/build_config.h (#44) --- impeller/geometry/matrix.h | 2 +- impeller/renderer/allocator.cc | 6 +++--- impeller/renderer/backend/metal/allocator_mtl.mm | 8 ++++---- impeller/renderer/backend/metal/device_buffer_mtl.mm | 2 +- impeller/renderer/platform.h | 2 +- 5 files changed, 10 insertions(+), 10 deletions(-) diff --git a/impeller/geometry/matrix.h b/impeller/geometry/matrix.h index 70a58482b863b..6b7e4453645e3 100644 --- a/impeller/geometry/matrix.h +++ b/impeller/geometry/matrix.h @@ -39,7 +39,7 @@ struct Matrix { }; //---------------------------------------------------------------------------- - /// Construts a default identity matrix. + /// Constructs a default identity matrix. /// constexpr Matrix() // clang-format off diff --git a/impeller/renderer/allocator.cc b/impeller/renderer/allocator.cc index 5969d220c21a7..74d9e1d4bc812 100644 --- a/impeller/renderer/allocator.cc +++ b/impeller/renderer/allocator.cc @@ -15,13 +15,13 @@ bool Allocator::RequiresExplicitHostSynchronization(StorageMode mode) { return false; } -#if OS_IOS +#if FML_OS_IOS // StorageMode::kHostVisible is MTLStorageModeShared already. return false; -#else // OS_IOS +#else // FML_OS_IOS // StorageMode::kHostVisible is MTLResourceStorageModeManaged. return true; -#endif // OS_IOS +#endif // FML_OS_IOS } } // namespace impeller diff --git a/impeller/renderer/backend/metal/allocator_mtl.mm b/impeller/renderer/backend/metal/allocator_mtl.mm index 4cbe7ac143646..20ceb1e2c7910 100644 --- a/impeller/renderer/backend/metal/allocator_mtl.mm +++ b/impeller/renderer/backend/metal/allocator_mtl.mm @@ -32,7 +32,7 @@ static MTLResourceOptions ToMTLResourceOptions(StorageMode type) { switch (type) { case StorageMode::kHostVisible: -#if OS_IOS +#if FML_OS_IOS return MTLResourceStorageModeShared; #else return MTLResourceStorageModeManaged; @@ -40,7 +40,7 @@ static MTLResourceOptions ToMTLResourceOptions(StorageMode type) { case StorageMode::kDevicePrivate: return MTLResourceStorageModePrivate; case StorageMode::kDeviceTransient: -#if OS_IOS +#if FML_OS_IOS if (@available(iOS 10.0, *)) { return MTLResourceStorageModeMemoryless; } else { @@ -57,7 +57,7 @@ static MTLResourceOptions ToMTLResourceOptions(StorageMode type) { static MTLStorageMode ToMTLStorageMode(StorageMode mode) { switch (mode) { case StorageMode::kHostVisible: -#if OS_IOS +#if FML_OS_IOS return MTLStorageModeShared; #else return MTLStorageModeManaged; @@ -65,7 +65,7 @@ static MTLStorageMode ToMTLStorageMode(StorageMode mode) { case StorageMode::kDevicePrivate: return MTLStorageModePrivate; case StorageMode::kDeviceTransient: -#if OS_IOS +#if FML_OS_IOS if (@available(iOS 10.0, *)) { return MTLStorageModeMemoryless; } else { diff --git a/impeller/renderer/backend/metal/device_buffer_mtl.mm b/impeller/renderer/backend/metal/device_buffer_mtl.mm index cb6415f89b13d..3f47629aa27f4 100644 --- a/impeller/renderer/backend/metal/device_buffer_mtl.mm +++ b/impeller/renderer/backend/metal/device_buffer_mtl.mm @@ -79,7 +79,7 @@ // Making this call is never necessary on iOS because there is no // MTLResourceStorageModeManaged mode. Only the MTLStorageModeShared mode is // available. -#if !OS_IOS +#if !FML_OS_IOS if (Allocator::RequiresExplicitHostSynchronization(mode_)) { [buffer_ didModifyRange:NSMakeRange(offset, source_range.length)]; } diff --git a/impeller/renderer/platform.h b/impeller/renderer/platform.h index 464d47451ac33..1ca8133c328c5 100644 --- a/impeller/renderer/platform.h +++ b/impeller/renderer/platform.h @@ -12,7 +12,7 @@ namespace impeller { constexpr size_t DefaultUniformAlignment() { -#if OS_IOS +#if FML_OS_IOS return 16u; #elif FML_OS_MACOSX return 256u; From 21411fc015f5a8c8daf76238dac9bc986030a29f Mon Sep 17 00:00:00 2001 From: Brandon DeRosier Date: Mon, 28 Feb 2022 14:28:16 -0800 Subject: [PATCH 319/433] Setup join/cap proc (#45) --- impeller/entity/contents.cc | 94 ++++++++++++++++++----------- impeller/entity/contents.h | 22 +++++-- impeller/entity/entity_unittests.cc | 9 +++ 3 files changed, 86 insertions(+), 39 deletions(-) diff --git a/impeller/entity/contents.cc b/impeller/entity/contents.cc index 0cb59466a7672..d5a2610245aba 100644 --- a/impeller/entity/contents.cc +++ b/impeller/entity/contents.cc @@ -282,7 +282,11 @@ const IRect& TextureContents::GetSourceRect() const { ******* SolidStrokeContents ******************************************************************************/ -SolidStrokeContents::SolidStrokeContents() = default; +SolidStrokeContents::SolidStrokeContents() { + SetStrokeCap(Cap::kButt); + // TODO(99089): Change this to kMiter once implemented. + SetStrokeJoin(Join::kBevel); +} SolidStrokeContents::~SolidStrokeContents() = default; @@ -294,32 +298,11 @@ const Color& SolidStrokeContents::GetColor() const { return color_; } -static void CreateCap( - VertexBufferBuilder& vtx_builder, - const Point& position, - const Point& normal) {} - -static void CreateJoin( - VertexBufferBuilder& vtx_builder, - const Point& position, - const Point& start_normal, - const Point& end_normal) { - SolidStrokeVertexShader::PerVertexData vtx; - vtx.vertex_position = position; - vtx.pen_down = 1.0; - vtx.vertex_normal = {}; - vtx_builder.AppendVertex(vtx); - - // A simple bevel join to start with. - Scalar dir = start_normal.Cross(end_normal) > 0 ? -1 : 1; - vtx.vertex_normal = start_normal * dir; - vtx_builder.AppendVertex(vtx); - vtx.vertex_normal = end_normal * dir; - vtx_builder.AppendVertex(vtx); -} - -static VertexBuffer CreateSolidStrokeVertices(const Path& path, - HostBuffer& buffer) { +static VertexBuffer CreateSolidStrokeVertices( + const Path& path, + HostBuffer& buffer, + const SolidStrokeContents::CapProc& cap_proc, + const SolidStrokeContents::JoinProc& join_proc) { using VS = SolidStrokeVertexShader; VertexBufferBuilder vtx_builder; @@ -372,7 +355,7 @@ static VertexBuffer CreateSolidStrokeVertices(const Path& path, // Generate start cap. if (!polyline.contours[contour_i].is_closed) { - CreateCap(vtx_builder, polyline.points[contour_start_point_i], -normal); + cap_proc(vtx_builder, polyline.points[contour_start_point_i], -normal); } // Generate contour geometry. @@ -396,18 +379,18 @@ static VertexBuffer CreateSolidStrokeVertices(const Path& path, compute_normal(point_i + 1); // Generate join from the current line to the next line. - CreateJoin(vtx_builder, polyline.points[point_i], previous_normal, - normal); + join_proc(vtx_builder, polyline.points[point_i], previous_normal, + normal); } } } // Generate end cap or join. if (!polyline.contours[contour_i].is_closed) { - CreateCap(vtx_builder, polyline.points[contour_end_point_i - 1], normal); + cap_proc(vtx_builder, polyline.points[contour_end_point_i - 1], normal); } else { - CreateJoin(vtx_builder, polyline.points[contour_start_point_i], normal, - contour_first_normal); + join_proc(vtx_builder, polyline.points[contour_start_point_i], normal, + contour_first_normal); } } @@ -436,8 +419,8 @@ bool SolidStrokeContents::Render(const ContentContext& renderer, cmd.label = "SolidStroke"; cmd.pipeline = renderer.GetSolidStrokePipeline(OptionsFromPass(pass)); cmd.stencil_reference = entity.GetStencilDepth(); - cmd.BindVertices( - CreateSolidStrokeVertices(entity.GetPath(), pass.GetTransientsBuffer())); + cmd.BindVertices(CreateSolidStrokeVertices( + entity.GetPath(), pass.GetTransientsBuffer(), cap_proc_, join_proc_)); VS::BindFrameInfo(cmd, pass.GetTransientsBuffer().EmplaceUniform(frame_info)); VS::BindStrokeInfo(cmd, pass.GetTransientsBuffer().EmplaceUniform(stroke_info)); @@ -465,6 +448,20 @@ Scalar SolidStrokeContents::GetStrokeMiter(Scalar miter) { void SolidStrokeContents::SetStrokeCap(Cap cap) { cap_ = cap; + + using VS = SolidStrokeVertexShader; + switch (cap) { + case Cap::kButt: + cap_proc_ = [](VertexBufferBuilder& vtx_builder, + const Point& position, const Point& normal) {}; + break; + case Cap::kRound: + FML_DLOG(ERROR) << "Unimplemented."; + break; + case Cap::kSquare: + FML_DLOG(ERROR) << "Unimplemented."; + break; + } } SolidStrokeContents::Cap SolidStrokeContents::GetStrokeCap() { @@ -473,6 +470,33 @@ SolidStrokeContents::Cap SolidStrokeContents::GetStrokeCap() { void SolidStrokeContents::SetStrokeJoin(Join join) { join_ = join; + + using VS = SolidStrokeVertexShader; + switch (join) { + case Join::kBevel: + join_proc_ = [](VertexBufferBuilder& vtx_builder, + const Point& position, const Point& start_normal, + const Point& end_normal) { + SolidStrokeVertexShader::PerVertexData vtx; + vtx.vertex_position = position; + vtx.pen_down = 1.0; + vtx.vertex_normal = {}; + vtx_builder.AppendVertex(vtx); + + Scalar dir = start_normal.Cross(end_normal) > 0 ? -1 : 1; + vtx.vertex_normal = start_normal * dir; + vtx_builder.AppendVertex(vtx); + vtx.vertex_normal = end_normal * dir; + vtx_builder.AppendVertex(vtx); + }; + break; + case Join::kMiter: + FML_DLOG(ERROR) << "Unimplemented."; + break; + case Join::kRound: + FML_DLOG(ERROR) << "Unimplemented."; + break; + } } SolidStrokeContents::Join SolidStrokeContents::GetStrokeJoin() { diff --git a/impeller/entity/contents.h b/impeller/entity/contents.h index 3b1bec7509690..2e03096d78e40 100644 --- a/impeller/entity/contents.h +++ b/impeller/entity/contents.h @@ -4,10 +4,12 @@ #pragma once +#include #include #include #include "flutter/fml/macros.h" +#include "impeller/entity/solid_stroke.vert.h" #include "impeller/geometry/color.h" #include "impeller/geometry/point.h" #include "impeller/geometry/rect.h" @@ -119,16 +121,24 @@ class SolidStrokeContents final : public Contents { kButt, kRound, kSquare, - kLast, }; enum class Join { kMiter, kRound, kBevel, - kLast, }; + using CapProc = std::function& vtx_builder, + const Point& position, + const Point& normal)>; + using JoinProc = std::function& vtx_builder, + const Point& position, + const Point& start_normal, + const Point& end_normal)>; + SolidStrokeContents(); ~SolidStrokeContents() override; @@ -162,8 +172,12 @@ class SolidStrokeContents final : public Contents { Color color_; Scalar stroke_size_ = 0.0; Scalar miter_ = 0.0; - Cap cap_ = Cap::kButt; - Join join_ = Join::kMiter; + + Cap cap_; + CapProc cap_proc_; + + Join join_; + JoinProc join_proc_; FML_DISALLOW_COPY_AND_ASSIGN(SolidStrokeContents); }; diff --git a/impeller/entity/entity_unittests.cc b/impeller/entity/entity_unittests.cc index 29b784e1c4f5e..704e64b4379d1 100644 --- a/impeller/entity/entity_unittests.cc +++ b/impeller/entity/entity_unittests.cc @@ -2,6 +2,7 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. +#include "entity/contents.h" #include "flutter/testing/testing.h" #include "impeller/entity/entity.h" #include "impeller/entity/entity_playground.h" @@ -328,5 +329,13 @@ TEST_F(EntityTest, CubicCurveAndOverlapTest) { ASSERT_TRUE(OpenPlaygroundHere(entity)); } +TEST_F(EntityTest, SolidStrokeContentsSetStrokeDefaults) { + SolidStrokeContents stroke; + ASSERT_EQ(stroke.GetStrokeCap(), SolidStrokeContents::Cap::kButt); + ASSERT_EQ(stroke.GetStrokeJoin(), SolidStrokeContents::Join::kBevel); + // TODO(99089): Test that SetStroke[Cap|Join] works once there are multiple + // caps and joins. +} + } // namespace testing } // namespace impeller From 01db57aa980b98addd3a0d03b4625b0ba0351b0b Mon Sep 17 00:00:00 2001 From: Chinmay Garde Date: Mon, 28 Feb 2022 16:01:18 -0800 Subject: [PATCH 320/433] Avoid depending on STB in //flutter/impeller/image. (#47) --- impeller/image/BUILD.gn | 11 +- .../backends/skia/compressed_image_skia.cc | 73 + .../backends/skia/compressed_image_skia.h | 25 + impeller/image/compressed_image.cc | 81 +- impeller/image/compressed_image.h | 15 +- impeller/playground/playground.mm | 8 +- impeller/third_party/stb/BUILD.gn | 12 - impeller/third_party/stb/STBImplementation.c | 11 - impeller/third_party/stb/stb/README.md | 131 - .../stb/stb/data/atari_8bit_font_revised.png | Bin 1769 -> 0 bytes .../stb/stb/data/easy_font_raw.png | Bin 645 -> 0 bytes .../stb/stb/data/herringbone/license.txt | 4 - .../template_caves_limit_connectivity.png | Bin 10818 -> 0 bytes .../template_caves_tiny_corridors.png | Bin 4904 -> 0 bytes .../herringbone/template_corner_caves.png | Bin 9337 -> 0 bytes .../template_horizontal_corridors_v1.png | Bin 5491 -> 0 bytes .../template_horizontal_corridors_v2.png | Bin 5759 -> 0 bytes .../template_horizontal_corridors_v3.png | Bin 6766 -> 0 bytes .../template_limit_connectivity_fat.png | Bin 6655 -> 0 bytes .../template_limited_connectivity.png | Bin 6728 -> 0 bytes .../data/herringbone/template_maze_2_wide.png | Bin 12776 -> 0 bytes .../herringbone/template_maze_plus_2_wide.png | Bin 13141 -> 0 bytes .../data/herringbone/template_open_areas.png | Bin 7764 -> 0 bytes .../template_ref2_corner_caves.png | Bin 9729 -> 0 bytes .../template_rooms_and_corridors.png | Bin 4736 -> 0 bytes ...oms_and_corridors_2_wide_diagonal_bias.png | Bin 4193 -> 0 bytes .../template_rooms_limit_connectivity.png | Bin 7664 -> 0 bytes ...emplate_round_rooms_diagonal_corridors.png | Bin 8172 -> 0 bytes .../herringbone/template_sean_dungeon.png | Bin 10261 -> 0 bytes .../template_simple_caves_2_wide.png | Bin 15712 -> 0 bytes ...emplate_square_rooms_with_random_rects.png | Bin 4772 -> 0 bytes .../stb/stb/deprecated/stb_image.c | 4678 ----- .../stb/stb/deprecated/stretchy_buffer.txt | 28 - .../third_party/stb/stb/docs/other_libs.md | 181 - .../third_party/stb/stb/docs/stb_howto.txt | 185 - .../stb/docs/stb_voxel_render_interview.md | 173 - .../stb/stb/docs/why_public_domain.md | 116 - impeller/third_party/stb/stb/release_notes.md | 26 - impeller/third_party/stb/stb/stb.h | 14185 ---------------- impeller/third_party/stb/stb/stb_c_lexer.h | 816 - impeller/third_party/stb/stb/stb_divide.h | 379 - impeller/third_party/stb/stb/stb_dxt.h | 630 - impeller/third_party/stb/stb/stb_easy_font.h | 258 - .../stb/stb/stb_herringbone_wang_tile.h | 1220 -- impeller/third_party/stb/stb/stb_image.h | 6755 -------- .../third_party/stb/stb/stb_image_resize.h | 2578 --- .../third_party/stb/stb/stb_image_write.h | 1048 -- impeller/third_party/stb/stb/stb_leakcheck.h | 124 - impeller/third_party/stb/stb/stb_perlin.h | 182 - impeller/third_party/stb/stb/stb_rect_pack.h | 572 - impeller/third_party/stb/stb/stb_textedit.h | 1304 -- .../third_party/stb/stb/stb_tilemap_editor.h | 4131 ----- impeller/third_party/stb/stb/stb_truetype.h | 3267 ---- impeller/third_party/stb/stb/stb_vorbis.c | 5397 ------ .../third_party/stb/stb/stb_voxel_render.h | 3752 ---- .../third_party/stb/stb/stretchy_buffer.h | 216 - .../third_party/stb/stb/tests/c_lexer_test.c | 3 - .../stb/stb/tests/c_lexer_test.dsp | 89 - .../stb/stb/tests/caveview/README.md | 85 - .../stb/stb/tests/caveview/cave_main.c | 598 - .../stb/stb/tests/caveview/cave_mesher.c | 928 - .../stb/stb/tests/caveview/cave_parse.c | 632 - .../stb/stb/tests/caveview/cave_parse.h | 41 - .../stb/stb/tests/caveview/cave_render.c | 951 -- .../stb/stb/tests/caveview/caveview.dsp | 157 - .../stb/stb/tests/caveview/caveview.dsw | 29 - .../stb/stb/tests/caveview/caveview.h | 50 - .../stb/stb/tests/caveview/glext.h | 11124 ------------ .../stb/stb/tests/caveview/glext_list.h | 34 - .../third_party/stb/stb/tests/caveview/main.c | 0 .../stb/stb/tests/caveview/stb_gl.h | 1103 -- .../stb/stb/tests/caveview/stb_glprog.h | 504 - .../tests/caveview/win32/SDL_windows_main.c | 224 - .../third_party/stb/stb/tests/herringbone.dsp | 95 - .../stb/stb/tests/herringbone_generator.c | 87 - .../stb/stb/tests/herringbone_map.c | 83 - .../stb/stb/tests/herringbone_map.dsp | 94 - .../third_party/stb/stb/tests/image_test.c | 166 - .../third_party/stb/stb/tests/image_test.dsp | 97 - .../stb/stb/tests/oversample/README.md | 94 - .../stb/stb/tests/oversample/main.c | 332 - .../stb/stb/tests/oversample/oversample.dsp | 97 - .../stb/stb/tests/oversample/oversample.dsw | 29 - .../stb/stb/tests/oversample/oversample.exe | Bin 54272 -> 0 bytes .../stb/stb/tests/oversample/stb_wingraph.h | 829 - .../stb/stb/tests/pngsuite/16bit/basi0g16.png | Bin 299 -> 0 bytes .../stb/stb/tests/pngsuite/16bit/basi2c16.png | Bin 595 -> 0 bytes .../stb/stb/tests/pngsuite/16bit/basi4a16.png | Bin 2855 -> 0 bytes .../stb/stb/tests/pngsuite/16bit/basi6a16.png | Bin 4180 -> 0 bytes .../stb/stb/tests/pngsuite/16bit/basn0g16.png | Bin 167 -> 0 bytes .../stb/stb/tests/pngsuite/16bit/basn2c16.png | Bin 302 -> 0 bytes .../stb/stb/tests/pngsuite/16bit/basn4a16.png | Bin 2206 -> 0 bytes .../stb/stb/tests/pngsuite/16bit/basn6a16.png | Bin 3435 -> 0 bytes .../stb/stb/tests/pngsuite/16bit/bgai4a16.png | Bin 2855 -> 0 bytes .../stb/stb/tests/pngsuite/16bit/bgan6a16.png | Bin 3435 -> 0 bytes .../stb/stb/tests/pngsuite/16bit/bggn4a16.png | Bin 2220 -> 0 bytes .../stb/stb/tests/pngsuite/16bit/bgyn6a16.png | Bin 3453 -> 0 bytes .../stb/stb/tests/pngsuite/16bit/oi1n0g16.png | Bin 167 -> 0 bytes .../stb/stb/tests/pngsuite/16bit/oi1n2c16.png | Bin 302 -> 0 bytes .../stb/stb/tests/pngsuite/16bit/oi2n0g16.png | Bin 179 -> 0 bytes .../stb/stb/tests/pngsuite/16bit/oi2n2c16.png | Bin 314 -> 0 bytes .../stb/stb/tests/pngsuite/16bit/oi4n0g16.png | Bin 203 -> 0 bytes .../stb/stb/tests/pngsuite/16bit/oi4n2c16.png | Bin 338 -> 0 bytes .../stb/stb/tests/pngsuite/16bit/oi9n0g16.png | Bin 1283 -> 0 bytes .../stb/stb/tests/pngsuite/16bit/oi9n2c16.png | Bin 3038 -> 0 bytes .../stb/stb/tests/pngsuite/16bit/tbbn2c16.png | Bin 2041 -> 0 bytes .../stb/stb/tests/pngsuite/16bit/tbgn2c16.png | Bin 2041 -> 0 bytes .../stb/stb/tests/pngsuite/16bit/tbwn0g16.png | Bin 1313 -> 0 bytes .../stb/stb/tests/pngsuite/PngSuite.LICENSE | 9 - .../stb/tests/pngsuite/corrupt/xc1n0g08.png | Bin 138 -> 0 bytes .../stb/tests/pngsuite/corrupt/xc9n2c08.png | Bin 145 -> 0 bytes .../stb/tests/pngsuite/corrupt/xcrn0g04.png | Bin 145 -> 0 bytes .../stb/tests/pngsuite/corrupt/xcsn0g01.png | Bin 164 -> 0 bytes .../stb/tests/pngsuite/corrupt/xd0n2c08.png | Bin 145 -> 0 bytes .../stb/tests/pngsuite/corrupt/xd3n2c08.png | Bin 145 -> 0 bytes .../stb/tests/pngsuite/corrupt/xd9n2c08.png | Bin 145 -> 0 bytes .../stb/tests/pngsuite/corrupt/xdtn0g01.png | Bin 61 -> 0 bytes .../stb/tests/pngsuite/corrupt/xhdn0g08.png | Bin 138 -> 0 bytes .../stb/tests/pngsuite/corrupt/xlfn0g04.png | Bin 145 -> 0 bytes .../stb/tests/pngsuite/corrupt/xs1n0g01.png | Bin 164 -> 0 bytes .../stb/tests/pngsuite/corrupt/xs2n0g01.png | Bin 164 -> 0 bytes .../stb/tests/pngsuite/corrupt/xs4n0g01.png | Bin 164 -> 0 bytes .../stb/tests/pngsuite/corrupt/xs7n0g01.png | Bin 164 -> 0 bytes .../stb/tests/pngsuite/primary/basi0g01.png | Bin 217 -> 0 bytes .../stb/tests/pngsuite/primary/basi0g02.png | Bin 154 -> 0 bytes .../stb/tests/pngsuite/primary/basi0g04.png | Bin 247 -> 0 bytes .../stb/tests/pngsuite/primary/basi0g08.png | Bin 254 -> 0 bytes .../stb/tests/pngsuite/primary/basi2c08.png | Bin 315 -> 0 bytes .../stb/tests/pngsuite/primary/basi3p01.png | Bin 132 -> 0 bytes .../stb/tests/pngsuite/primary/basi3p02.png | Bin 193 -> 0 bytes .../stb/tests/pngsuite/primary/basi3p04.png | Bin 327 -> 0 bytes .../stb/tests/pngsuite/primary/basi3p08.png | Bin 1527 -> 0 bytes .../stb/tests/pngsuite/primary/basi4a08.png | Bin 214 -> 0 bytes .../stb/tests/pngsuite/primary/basi6a08.png | Bin 361 -> 0 bytes .../stb/tests/pngsuite/primary/basn0g01.png | Bin 164 -> 0 bytes .../stb/tests/pngsuite/primary/basn0g02.png | Bin 104 -> 0 bytes .../stb/tests/pngsuite/primary/basn0g04.png | Bin 145 -> 0 bytes .../stb/tests/pngsuite/primary/basn0g08.png | Bin 138 -> 0 bytes .../stb/tests/pngsuite/primary/basn2c08.png | Bin 145 -> 0 bytes .../stb/tests/pngsuite/primary/basn3p01.png | Bin 112 -> 0 bytes .../stb/tests/pngsuite/primary/basn3p02.png | Bin 146 -> 0 bytes .../stb/tests/pngsuite/primary/basn3p04.png | Bin 216 -> 0 bytes .../stb/tests/pngsuite/primary/basn3p08.png | Bin 1286 -> 0 bytes .../stb/tests/pngsuite/primary/basn4a08.png | Bin 126 -> 0 bytes .../stb/tests/pngsuite/primary/basn6a08.png | Bin 184 -> 0 bytes .../stb/tests/pngsuite/primary/bgai4a08.png | Bin 214 -> 0 bytes .../stb/tests/pngsuite/primary/bgan6a08.png | Bin 184 -> 0 bytes .../stb/tests/pngsuite/primary/bgbn4a08.png | Bin 140 -> 0 bytes .../stb/tests/pngsuite/primary/bgwn6a08.png | Bin 202 -> 0 bytes .../stb/tests/pngsuite/primary/s01i3p01.png | Bin 113 -> 0 bytes .../stb/tests/pngsuite/primary/s01n3p01.png | Bin 113 -> 0 bytes .../stb/tests/pngsuite/primary/s02i3p01.png | Bin 114 -> 0 bytes .../stb/tests/pngsuite/primary/s02n3p01.png | Bin 115 -> 0 bytes .../stb/tests/pngsuite/primary/s03i3p01.png | Bin 118 -> 0 bytes .../stb/tests/pngsuite/primary/s03n3p01.png | Bin 120 -> 0 bytes .../stb/tests/pngsuite/primary/s04i3p01.png | Bin 126 -> 0 bytes .../stb/tests/pngsuite/primary/s04n3p01.png | Bin 121 -> 0 bytes .../stb/tests/pngsuite/primary/s05i3p02.png | Bin 134 -> 0 bytes .../stb/tests/pngsuite/primary/s05n3p02.png | Bin 129 -> 0 bytes .../stb/tests/pngsuite/primary/s06i3p02.png | Bin 143 -> 0 bytes .../stb/tests/pngsuite/primary/s06n3p02.png | Bin 131 -> 0 bytes .../stb/tests/pngsuite/primary/s07i3p02.png | Bin 149 -> 0 bytes .../stb/tests/pngsuite/primary/s07n3p02.png | Bin 138 -> 0 bytes .../stb/tests/pngsuite/primary/s08i3p02.png | Bin 149 -> 0 bytes .../stb/tests/pngsuite/primary/s08n3p02.png | Bin 139 -> 0 bytes .../stb/tests/pngsuite/primary/s09i3p02.png | Bin 147 -> 0 bytes .../stb/tests/pngsuite/primary/s09n3p02.png | Bin 143 -> 0 bytes .../stb/tests/pngsuite/primary/s32i3p04.png | Bin 355 -> 0 bytes .../stb/tests/pngsuite/primary/s32n3p04.png | Bin 263 -> 0 bytes .../stb/tests/pngsuite/primary/s33i3p04.png | Bin 385 -> 0 bytes .../stb/tests/pngsuite/primary/s33n3p04.png | Bin 329 -> 0 bytes .../stb/tests/pngsuite/primary/s34i3p04.png | Bin 349 -> 0 bytes .../stb/tests/pngsuite/primary/s34n3p04.png | Bin 248 -> 0 bytes .../stb/tests/pngsuite/primary/s35i3p04.png | Bin 399 -> 0 bytes .../stb/tests/pngsuite/primary/s35n3p04.png | Bin 338 -> 0 bytes .../stb/tests/pngsuite/primary/s36i3p04.png | Bin 356 -> 0 bytes .../stb/tests/pngsuite/primary/s36n3p04.png | Bin 258 -> 0 bytes .../stb/tests/pngsuite/primary/s37i3p04.png | Bin 393 -> 0 bytes .../stb/tests/pngsuite/primary/s37n3p04.png | Bin 336 -> 0 bytes .../stb/tests/pngsuite/primary/s38i3p04.png | Bin 357 -> 0 bytes .../stb/tests/pngsuite/primary/s38n3p04.png | Bin 245 -> 0 bytes .../stb/tests/pngsuite/primary/s39i3p04.png | Bin 420 -> 0 bytes .../stb/tests/pngsuite/primary/s39n3p04.png | Bin 352 -> 0 bytes .../stb/tests/pngsuite/primary/s40i3p04.png | Bin 357 -> 0 bytes .../stb/tests/pngsuite/primary/s40n3p04.png | Bin 256 -> 0 bytes .../stb/tests/pngsuite/primary/tbbn0g04.png | Bin 429 -> 0 bytes .../stb/tests/pngsuite/primary/tbbn3p08.png | Bin 1499 -> 0 bytes .../stb/tests/pngsuite/primary/tbgn3p08.png | Bin 1499 -> 0 bytes .../stb/tests/pngsuite/primary/tbrn2c08.png | Bin 1633 -> 0 bytes .../stb/tests/pngsuite/primary/tbwn3p08.png | Bin 1496 -> 0 bytes .../stb/tests/pngsuite/primary/tbyn3p08.png | Bin 1499 -> 0 bytes .../stb/tests/pngsuite/primary/tm3n3p02.png | Bin 116 -> 0 bytes .../stb/tests/pngsuite/primary/tp0n0g08.png | Bin 719 -> 0 bytes .../stb/tests/pngsuite/primary/tp0n2c08.png | Bin 1594 -> 0 bytes .../stb/tests/pngsuite/primary/tp0n3p08.png | Bin 1476 -> 0 bytes .../stb/tests/pngsuite/primary/tp1n3p08.png | Bin 1483 -> 0 bytes .../stb/tests/pngsuite/primary/z00n2c08.png | Bin 3172 -> 0 bytes .../stb/tests/pngsuite/primary/z03n2c08.png | Bin 232 -> 0 bytes .../stb/tests/pngsuite/primary/z06n2c08.png | Bin 224 -> 0 bytes .../stb/tests/pngsuite/primary/z09n2c08.png | Bin 224 -> 0 bytes .../tests/pngsuite/primary_check/basi0g01.png | Bin 391 -> 0 bytes .../tests/pngsuite/primary_check/basi0g02.png | Bin 283 -> 0 bytes .../tests/pngsuite/primary_check/basi0g04.png | Bin 252 -> 0 bytes .../tests/pngsuite/primary_check/basi0g08.png | Bin 293 -> 0 bytes .../tests/pngsuite/primary_check/basi2c08.png | Bin 274 -> 0 bytes .../tests/pngsuite/primary_check/basi3p01.png | Bin 273 -> 0 bytes .../tests/pngsuite/primary_check/basi3p02.png | Bin 286 -> 0 bytes .../tests/pngsuite/primary_check/basi3p04.png | Bin 331 -> 0 bytes .../tests/pngsuite/primary_check/basi3p08.png | Bin 387 -> 0 bytes .../tests/pngsuite/primary_check/basi4a08.png | Bin 263 -> 0 bytes .../tests/pngsuite/primary_check/basi6a08.png | Bin 298 -> 0 bytes .../tests/pngsuite/primary_check/basn0g01.png | Bin 391 -> 0 bytes .../tests/pngsuite/primary_check/basn0g02.png | Bin 283 -> 0 bytes .../tests/pngsuite/primary_check/basn0g04.png | Bin 252 -> 0 bytes .../tests/pngsuite/primary_check/basn0g08.png | Bin 293 -> 0 bytes .../tests/pngsuite/primary_check/basn2c08.png | Bin 274 -> 0 bytes .../tests/pngsuite/primary_check/basn3p01.png | Bin 273 -> 0 bytes .../tests/pngsuite/primary_check/basn3p02.png | Bin 286 -> 0 bytes .../tests/pngsuite/primary_check/basn3p04.png | Bin 331 -> 0 bytes .../tests/pngsuite/primary_check/basn3p08.png | Bin 387 -> 0 bytes .../tests/pngsuite/primary_check/basn4a08.png | Bin 263 -> 0 bytes .../tests/pngsuite/primary_check/basn6a08.png | Bin 298 -> 0 bytes .../tests/pngsuite/primary_check/bgai4a08.png | Bin 263 -> 0 bytes .../tests/pngsuite/primary_check/bgan6a08.png | Bin 298 -> 0 bytes .../tests/pngsuite/primary_check/bgbn4a08.png | Bin 263 -> 0 bytes .../tests/pngsuite/primary_check/bgwn6a08.png | Bin 298 -> 0 bytes .../tests/pngsuite/primary_check/s01i3p01.png | Bin 202 -> 0 bytes .../tests/pngsuite/primary_check/s01n3p01.png | Bin 202 -> 0 bytes .../tests/pngsuite/primary_check/s02i3p01.png | Bin 210 -> 0 bytes .../tests/pngsuite/primary_check/s02n3p01.png | Bin 210 -> 0 bytes .../tests/pngsuite/primary_check/s03i3p01.png | Bin 216 -> 0 bytes .../tests/pngsuite/primary_check/s03n3p01.png | Bin 216 -> 0 bytes .../tests/pngsuite/primary_check/s04i3p01.png | Bin 221 -> 0 bytes .../tests/pngsuite/primary_check/s04n3p01.png | Bin 221 -> 0 bytes .../tests/pngsuite/primary_check/s05i3p02.png | Bin 232 -> 0 bytes .../tests/pngsuite/primary_check/s05n3p02.png | Bin 232 -> 0 bytes .../tests/pngsuite/primary_check/s06i3p02.png | Bin 239 -> 0 bytes .../tests/pngsuite/primary_check/s06n3p02.png | Bin 239 -> 0 bytes .../tests/pngsuite/primary_check/s07i3p02.png | Bin 249 -> 0 bytes .../tests/pngsuite/primary_check/s07n3p02.png | Bin 249 -> 0 bytes .../tests/pngsuite/primary_check/s08i3p02.png | Bin 255 -> 0 bytes .../tests/pngsuite/primary_check/s08n3p02.png | Bin 255 -> 0 bytes .../tests/pngsuite/primary_check/s09i3p02.png | Bin 263 -> 0 bytes .../tests/pngsuite/primary_check/s09n3p02.png | Bin 263 -> 0 bytes .../tests/pngsuite/primary_check/s32i3p04.png | Bin 441 -> 0 bytes .../tests/pngsuite/primary_check/s32n3p04.png | Bin 441 -> 0 bytes .../tests/pngsuite/primary_check/s33i3p04.png | Bin 470 -> 0 bytes .../tests/pngsuite/primary_check/s33n3p04.png | Bin 470 -> 0 bytes .../tests/pngsuite/primary_check/s34i3p04.png | Bin 431 -> 0 bytes .../tests/pngsuite/primary_check/s34n3p04.png | Bin 431 -> 0 bytes .../tests/pngsuite/primary_check/s35i3p04.png | Bin 477 -> 0 bytes .../tests/pngsuite/primary_check/s35n3p04.png | Bin 477 -> 0 bytes .../tests/pngsuite/primary_check/s36i3p04.png | Bin 448 -> 0 bytes .../tests/pngsuite/primary_check/s36n3p04.png | Bin 448 -> 0 bytes .../tests/pngsuite/primary_check/s37i3p04.png | Bin 478 -> 0 bytes .../tests/pngsuite/primary_check/s37n3p04.png | Bin 478 -> 0 bytes .../tests/pngsuite/primary_check/s38i3p04.png | Bin 439 -> 0 bytes .../tests/pngsuite/primary_check/s38n3p04.png | Bin 439 -> 0 bytes .../tests/pngsuite/primary_check/s39i3p04.png | Bin 499 -> 0 bytes .../tests/pngsuite/primary_check/s39n3p04.png | Bin 499 -> 0 bytes .../tests/pngsuite/primary_check/s40i3p04.png | Bin 463 -> 0 bytes .../tests/pngsuite/primary_check/s40n3p04.png | Bin 463 -> 0 bytes .../tests/pngsuite/primary_check/tbbn0g04.png | Bin 762 -> 0 bytes .../tests/pngsuite/primary_check/tbbn3p08.png | Bin 1911 -> 0 bytes .../tests/pngsuite/primary_check/tbgn3p08.png | Bin 1911 -> 0 bytes .../tests/pngsuite/primary_check/tbrn2c08.png | Bin 1901 -> 0 bytes .../tests/pngsuite/primary_check/tbwn3p08.png | Bin 1911 -> 0 bytes .../tests/pngsuite/primary_check/tbyn3p08.png | Bin 1911 -> 0 bytes .../tests/pngsuite/primary_check/tm3n3p02.png | Bin 306 -> 0 bytes .../tests/pngsuite/primary_check/tp0n0g08.png | Bin 1802 -> 0 bytes .../tests/pngsuite/primary_check/tp0n2c08.png | Bin 1955 -> 0 bytes .../tests/pngsuite/primary_check/tp0n3p08.png | Bin 1959 -> 0 bytes .../tests/pngsuite/primary_check/tp1n3p08.png | Bin 1911 -> 0 bytes .../tests/pngsuite/primary_check/z00n2c08.png | Bin 422 -> 0 bytes .../tests/pngsuite/primary_check/z03n2c08.png | Bin 422 -> 0 bytes .../tests/pngsuite/primary_check/z06n2c08.png | Bin 422 -> 0 bytes .../tests/pngsuite/primary_check/z09n2c08.png | Bin 422 -> 0 bytes .../stb/tests/pngsuite/unused/ccwn2c08.png | Bin 1514 -> 0 bytes .../stb/tests/pngsuite/unused/ccwn3p08.png | Bin 1554 -> 0 bytes .../stb/tests/pngsuite/unused/cdfn2c08.png | Bin 404 -> 0 bytes .../stb/tests/pngsuite/unused/cdhn2c08.png | Bin 344 -> 0 bytes .../stb/tests/pngsuite/unused/cdsn2c08.png | Bin 232 -> 0 bytes .../stb/tests/pngsuite/unused/cdun2c08.png | Bin 724 -> 0 bytes .../stb/tests/pngsuite/unused/ch1n3p04.png | Bin 258 -> 0 bytes .../stb/tests/pngsuite/unused/ch2n3p08.png | Bin 1810 -> 0 bytes .../stb/tests/pngsuite/unused/cm0n0g04.png | Bin 292 -> 0 bytes .../stb/tests/pngsuite/unused/cm7n0g04.png | Bin 292 -> 0 bytes .../stb/tests/pngsuite/unused/cm9n0g04.png | Bin 292 -> 0 bytes .../stb/tests/pngsuite/unused/cs3n2c16.png | Bin 214 -> 0 bytes .../stb/tests/pngsuite/unused/cs3n3p08.png | Bin 259 -> 0 bytes .../stb/tests/pngsuite/unused/cs5n2c08.png | Bin 186 -> 0 bytes .../stb/tests/pngsuite/unused/cs5n3p08.png | Bin 271 -> 0 bytes .../stb/tests/pngsuite/unused/cs8n2c08.png | Bin 149 -> 0 bytes .../stb/tests/pngsuite/unused/cs8n3p08.png | Bin 256 -> 0 bytes .../stb/tests/pngsuite/unused/ct0n0g04.png | Bin 273 -> 0 bytes .../stb/tests/pngsuite/unused/ct1n0g04.png | Bin 792 -> 0 bytes .../stb/tests/pngsuite/unused/cten0g04.png | Bin 742 -> 0 bytes .../stb/tests/pngsuite/unused/ctfn0g04.png | Bin 716 -> 0 bytes .../stb/tests/pngsuite/unused/ctgn0g04.png | Bin 1182 -> 0 bytes .../stb/tests/pngsuite/unused/cthn0g04.png | Bin 1269 -> 0 bytes .../stb/tests/pngsuite/unused/ctjn0g04.png | Bin 941 -> 0 bytes .../stb/tests/pngsuite/unused/ctzn0g04.png | Bin 753 -> 0 bytes .../stb/tests/pngsuite/unused/f00n0g08.png | Bin 319 -> 0 bytes .../stb/tests/pngsuite/unused/f00n2c08.png | Bin 2475 -> 0 bytes .../stb/tests/pngsuite/unused/f01n0g08.png | Bin 321 -> 0 bytes .../stb/tests/pngsuite/unused/f01n2c08.png | Bin 1180 -> 0 bytes .../stb/tests/pngsuite/unused/f02n0g08.png | Bin 355 -> 0 bytes .../stb/tests/pngsuite/unused/f02n2c08.png | Bin 1729 -> 0 bytes .../stb/tests/pngsuite/unused/f03n0g08.png | Bin 389 -> 0 bytes .../stb/tests/pngsuite/unused/f03n2c08.png | Bin 1291 -> 0 bytes .../stb/tests/pngsuite/unused/f04n0g08.png | Bin 269 -> 0 bytes .../stb/tests/pngsuite/unused/f04n2c08.png | Bin 985 -> 0 bytes .../stb/tests/pngsuite/unused/f99n0g04.png | Bin 426 -> 0 bytes .../stb/tests/pngsuite/unused/g03n0g16.png | Bin 345 -> 0 bytes .../stb/tests/pngsuite/unused/g03n2c08.png | Bin 370 -> 0 bytes .../stb/tests/pngsuite/unused/g03n3p04.png | Bin 214 -> 0 bytes .../stb/tests/pngsuite/unused/g04n0g16.png | Bin 363 -> 0 bytes .../stb/tests/pngsuite/unused/g04n2c08.png | Bin 377 -> 0 bytes .../stb/tests/pngsuite/unused/g04n3p04.png | Bin 219 -> 0 bytes .../stb/tests/pngsuite/unused/g05n0g16.png | Bin 339 -> 0 bytes .../stb/tests/pngsuite/unused/g05n2c08.png | Bin 350 -> 0 bytes .../stb/tests/pngsuite/unused/g05n3p04.png | Bin 206 -> 0 bytes .../stb/tests/pngsuite/unused/g07n0g16.png | Bin 321 -> 0 bytes .../stb/tests/pngsuite/unused/g07n2c08.png | Bin 340 -> 0 bytes .../stb/tests/pngsuite/unused/g07n3p04.png | Bin 207 -> 0 bytes .../stb/tests/pngsuite/unused/g10n0g16.png | Bin 262 -> 0 bytes .../stb/tests/pngsuite/unused/g10n2c08.png | Bin 285 -> 0 bytes .../stb/tests/pngsuite/unused/g10n3p04.png | Bin 214 -> 0 bytes .../stb/tests/pngsuite/unused/g25n0g16.png | Bin 383 -> 0 bytes .../stb/tests/pngsuite/unused/g25n2c08.png | Bin 405 -> 0 bytes .../stb/tests/pngsuite/unused/g25n3p04.png | Bin 215 -> 0 bytes .../stb/tests/pngsuite/unused/pp0n2c16.png | Bin 962 -> 0 bytes .../stb/tests/pngsuite/unused/pp0n6a08.png | Bin 818 -> 0 bytes .../stb/tests/pngsuite/unused/ps1n0g08.png | Bin 1456 -> 0 bytes .../stb/tests/pngsuite/unused/ps1n2c16.png | Bin 1620 -> 0 bytes .../stb/tests/pngsuite/unused/ps2n0g08.png | Bin 2320 -> 0 bytes .../stb/tests/pngsuite/unused/ps2n2c16.png | Bin 2484 -> 0 bytes .../stb/stb/tests/resample_test.cpp | 1116 -- .../stb/stb/tests/resample_test_c.c | 5 - impeller/third_party/stb/stb/tests/resize.dsp | 94 - impeller/third_party/stb/stb/tests/stb.c | 3323 ---- impeller/third_party/stb/stb/tests/stb.dsp | 192 - impeller/third_party/stb/stb/tests/stb.dsw | 161 - .../third_party/stb/stb/tests/stb_cpp.cpp | 82 - .../third_party/stb/stb/tests/stb_cpp.dsp | 98 - .../third_party/stb/stb/tests/stretch_test.c | 28 - .../stb/stb/tests/stretch_test.dsp | 89 - .../stb/stb/tests/stretchy_buffer_test.c | 1 - .../stb/stb/tests/test_c_compilation.c | 30 - .../stb/stb/tests/test_cpp_compilation.cpp | 137 - .../third_party/stb/stb/tests/test_truetype.c | 95 - .../third_party/stb/stb/tests/test_vorbis.c | 18 - .../stb/stb/tests/textedit_sample.c | 84 - .../tilemap_editor_integration_example.c | 193 - .../stb/stb/tests/vorbseek/vorbseek.c | 125 - .../stb/stb/tests/vorbseek/vorbseek.dsp | 96 - .../stb/stb/tools/README.footer.md | 100 - .../stb/stb/tools/README.header.md | 7 - .../third_party/stb/stb/tools/README.list | 18 - .../stb/stb/tools/easy_font_maker.c | 211 - .../third_party/stb/stb/tools/make_readme.c | 61 - .../third_party/stb/stb/tools/make_readme.dsp | 97 - impeller/third_party/stb/stb/tools/mr.bat | 1 - impeller/third_party/stb/stb/tools/unicode.c | 749 - .../stb/stb/tools/unicode/unicode.dsp | 88 - 365 files changed, 133 insertions(+), 78302 deletions(-) create mode 100644 impeller/image/backends/skia/compressed_image_skia.cc create mode 100644 impeller/image/backends/skia/compressed_image_skia.h delete mode 100644 impeller/third_party/stb/BUILD.gn delete mode 100644 impeller/third_party/stb/STBImplementation.c delete mode 100644 impeller/third_party/stb/stb/README.md delete mode 100644 impeller/third_party/stb/stb/data/atari_8bit_font_revised.png delete mode 100644 impeller/third_party/stb/stb/data/easy_font_raw.png delete mode 100644 impeller/third_party/stb/stb/data/herringbone/license.txt delete mode 100644 impeller/third_party/stb/stb/data/herringbone/template_caves_limit_connectivity.png delete mode 100644 impeller/third_party/stb/stb/data/herringbone/template_caves_tiny_corridors.png delete mode 100644 impeller/third_party/stb/stb/data/herringbone/template_corner_caves.png delete mode 100644 impeller/third_party/stb/stb/data/herringbone/template_horizontal_corridors_v1.png delete mode 100644 impeller/third_party/stb/stb/data/herringbone/template_horizontal_corridors_v2.png delete mode 100644 impeller/third_party/stb/stb/data/herringbone/template_horizontal_corridors_v3.png delete mode 100644 impeller/third_party/stb/stb/data/herringbone/template_limit_connectivity_fat.png delete mode 100644 impeller/third_party/stb/stb/data/herringbone/template_limited_connectivity.png delete mode 100644 impeller/third_party/stb/stb/data/herringbone/template_maze_2_wide.png delete mode 100644 impeller/third_party/stb/stb/data/herringbone/template_maze_plus_2_wide.png delete mode 100644 impeller/third_party/stb/stb/data/herringbone/template_open_areas.png delete mode 100644 impeller/third_party/stb/stb/data/herringbone/template_ref2_corner_caves.png delete mode 100644 impeller/third_party/stb/stb/data/herringbone/template_rooms_and_corridors.png delete mode 100644 impeller/third_party/stb/stb/data/herringbone/template_rooms_and_corridors_2_wide_diagonal_bias.png delete mode 100644 impeller/third_party/stb/stb/data/herringbone/template_rooms_limit_connectivity.png delete mode 100644 impeller/third_party/stb/stb/data/herringbone/template_round_rooms_diagonal_corridors.png delete mode 100644 impeller/third_party/stb/stb/data/herringbone/template_sean_dungeon.png delete mode 100644 impeller/third_party/stb/stb/data/herringbone/template_simple_caves_2_wide.png delete mode 100644 impeller/third_party/stb/stb/data/herringbone/template_square_rooms_with_random_rects.png delete mode 100644 impeller/third_party/stb/stb/deprecated/stb_image.c delete mode 100644 impeller/third_party/stb/stb/deprecated/stretchy_buffer.txt delete mode 100644 impeller/third_party/stb/stb/docs/other_libs.md delete mode 100644 impeller/third_party/stb/stb/docs/stb_howto.txt delete mode 100644 impeller/third_party/stb/stb/docs/stb_voxel_render_interview.md delete mode 100644 impeller/third_party/stb/stb/docs/why_public_domain.md delete mode 100644 impeller/third_party/stb/stb/release_notes.md delete mode 100644 impeller/third_party/stb/stb/stb.h delete mode 100644 impeller/third_party/stb/stb/stb_c_lexer.h delete mode 100644 impeller/third_party/stb/stb/stb_divide.h delete mode 100644 impeller/third_party/stb/stb/stb_dxt.h delete mode 100644 impeller/third_party/stb/stb/stb_easy_font.h delete mode 100644 impeller/third_party/stb/stb/stb_herringbone_wang_tile.h delete mode 100644 impeller/third_party/stb/stb/stb_image.h delete mode 100644 impeller/third_party/stb/stb/stb_image_resize.h delete mode 100644 impeller/third_party/stb/stb/stb_image_write.h delete mode 100644 impeller/third_party/stb/stb/stb_leakcheck.h delete mode 100644 impeller/third_party/stb/stb/stb_perlin.h delete mode 100644 impeller/third_party/stb/stb/stb_rect_pack.h delete mode 100644 impeller/third_party/stb/stb/stb_textedit.h delete mode 100644 impeller/third_party/stb/stb/stb_tilemap_editor.h delete mode 100644 impeller/third_party/stb/stb/stb_truetype.h delete mode 100644 impeller/third_party/stb/stb/stb_vorbis.c delete mode 100644 impeller/third_party/stb/stb/stb_voxel_render.h delete mode 100644 impeller/third_party/stb/stb/stretchy_buffer.h delete mode 100644 impeller/third_party/stb/stb/tests/c_lexer_test.c delete mode 100644 impeller/third_party/stb/stb/tests/c_lexer_test.dsp delete mode 100644 impeller/third_party/stb/stb/tests/caveview/README.md delete mode 100644 impeller/third_party/stb/stb/tests/caveview/cave_main.c delete mode 100644 impeller/third_party/stb/stb/tests/caveview/cave_mesher.c delete mode 100644 impeller/third_party/stb/stb/tests/caveview/cave_parse.c delete mode 100644 impeller/third_party/stb/stb/tests/caveview/cave_parse.h delete mode 100644 impeller/third_party/stb/stb/tests/caveview/cave_render.c delete mode 100644 impeller/third_party/stb/stb/tests/caveview/caveview.dsp delete mode 100644 impeller/third_party/stb/stb/tests/caveview/caveview.dsw delete mode 100644 impeller/third_party/stb/stb/tests/caveview/caveview.h delete mode 100644 impeller/third_party/stb/stb/tests/caveview/glext.h delete mode 100644 impeller/third_party/stb/stb/tests/caveview/glext_list.h delete mode 100644 impeller/third_party/stb/stb/tests/caveview/main.c delete mode 100644 impeller/third_party/stb/stb/tests/caveview/stb_gl.h delete mode 100644 impeller/third_party/stb/stb/tests/caveview/stb_glprog.h delete mode 100644 impeller/third_party/stb/stb/tests/caveview/win32/SDL_windows_main.c delete mode 100644 impeller/third_party/stb/stb/tests/herringbone.dsp delete mode 100644 impeller/third_party/stb/stb/tests/herringbone_generator.c delete mode 100644 impeller/third_party/stb/stb/tests/herringbone_map.c delete mode 100644 impeller/third_party/stb/stb/tests/herringbone_map.dsp delete mode 100644 impeller/third_party/stb/stb/tests/image_test.c delete mode 100644 impeller/third_party/stb/stb/tests/image_test.dsp delete mode 100644 impeller/third_party/stb/stb/tests/oversample/README.md delete mode 100644 impeller/third_party/stb/stb/tests/oversample/main.c delete mode 100644 impeller/third_party/stb/stb/tests/oversample/oversample.dsp delete mode 100644 impeller/third_party/stb/stb/tests/oversample/oversample.dsw delete mode 100644 impeller/third_party/stb/stb/tests/oversample/oversample.exe delete mode 100644 impeller/third_party/stb/stb/tests/oversample/stb_wingraph.h delete mode 100644 impeller/third_party/stb/stb/tests/pngsuite/16bit/basi0g16.png delete mode 100644 impeller/third_party/stb/stb/tests/pngsuite/16bit/basi2c16.png delete mode 100644 impeller/third_party/stb/stb/tests/pngsuite/16bit/basi4a16.png delete mode 100644 impeller/third_party/stb/stb/tests/pngsuite/16bit/basi6a16.png delete mode 100644 impeller/third_party/stb/stb/tests/pngsuite/16bit/basn0g16.png delete mode 100644 impeller/third_party/stb/stb/tests/pngsuite/16bit/basn2c16.png delete mode 100644 impeller/third_party/stb/stb/tests/pngsuite/16bit/basn4a16.png delete mode 100644 impeller/third_party/stb/stb/tests/pngsuite/16bit/basn6a16.png delete mode 100644 impeller/third_party/stb/stb/tests/pngsuite/16bit/bgai4a16.png delete mode 100644 impeller/third_party/stb/stb/tests/pngsuite/16bit/bgan6a16.png delete mode 100644 impeller/third_party/stb/stb/tests/pngsuite/16bit/bggn4a16.png delete mode 100644 impeller/third_party/stb/stb/tests/pngsuite/16bit/bgyn6a16.png delete mode 100644 impeller/third_party/stb/stb/tests/pngsuite/16bit/oi1n0g16.png delete mode 100644 impeller/third_party/stb/stb/tests/pngsuite/16bit/oi1n2c16.png delete mode 100644 impeller/third_party/stb/stb/tests/pngsuite/16bit/oi2n0g16.png delete mode 100644 impeller/third_party/stb/stb/tests/pngsuite/16bit/oi2n2c16.png delete mode 100644 impeller/third_party/stb/stb/tests/pngsuite/16bit/oi4n0g16.png delete mode 100644 impeller/third_party/stb/stb/tests/pngsuite/16bit/oi4n2c16.png delete mode 100644 impeller/third_party/stb/stb/tests/pngsuite/16bit/oi9n0g16.png delete mode 100644 impeller/third_party/stb/stb/tests/pngsuite/16bit/oi9n2c16.png delete mode 100644 impeller/third_party/stb/stb/tests/pngsuite/16bit/tbbn2c16.png delete mode 100644 impeller/third_party/stb/stb/tests/pngsuite/16bit/tbgn2c16.png delete mode 100644 impeller/third_party/stb/stb/tests/pngsuite/16bit/tbwn0g16.png delete mode 100644 impeller/third_party/stb/stb/tests/pngsuite/PngSuite.LICENSE delete mode 100644 impeller/third_party/stb/stb/tests/pngsuite/corrupt/xc1n0g08.png delete mode 100644 impeller/third_party/stb/stb/tests/pngsuite/corrupt/xc9n2c08.png delete mode 100644 impeller/third_party/stb/stb/tests/pngsuite/corrupt/xcrn0g04.png delete mode 100644 impeller/third_party/stb/stb/tests/pngsuite/corrupt/xcsn0g01.png delete mode 100644 impeller/third_party/stb/stb/tests/pngsuite/corrupt/xd0n2c08.png delete mode 100644 impeller/third_party/stb/stb/tests/pngsuite/corrupt/xd3n2c08.png delete mode 100644 impeller/third_party/stb/stb/tests/pngsuite/corrupt/xd9n2c08.png delete mode 100644 impeller/third_party/stb/stb/tests/pngsuite/corrupt/xdtn0g01.png delete mode 100644 impeller/third_party/stb/stb/tests/pngsuite/corrupt/xhdn0g08.png delete mode 100644 impeller/third_party/stb/stb/tests/pngsuite/corrupt/xlfn0g04.png delete mode 100644 impeller/third_party/stb/stb/tests/pngsuite/corrupt/xs1n0g01.png delete mode 100644 impeller/third_party/stb/stb/tests/pngsuite/corrupt/xs2n0g01.png delete mode 100644 impeller/third_party/stb/stb/tests/pngsuite/corrupt/xs4n0g01.png delete mode 100644 impeller/third_party/stb/stb/tests/pngsuite/corrupt/xs7n0g01.png delete mode 100644 impeller/third_party/stb/stb/tests/pngsuite/primary/basi0g01.png delete mode 100644 impeller/third_party/stb/stb/tests/pngsuite/primary/basi0g02.png delete mode 100644 impeller/third_party/stb/stb/tests/pngsuite/primary/basi0g04.png delete mode 100644 impeller/third_party/stb/stb/tests/pngsuite/primary/basi0g08.png delete mode 100644 impeller/third_party/stb/stb/tests/pngsuite/primary/basi2c08.png delete mode 100644 impeller/third_party/stb/stb/tests/pngsuite/primary/basi3p01.png delete mode 100644 impeller/third_party/stb/stb/tests/pngsuite/primary/basi3p02.png delete mode 100644 impeller/third_party/stb/stb/tests/pngsuite/primary/basi3p04.png delete mode 100644 impeller/third_party/stb/stb/tests/pngsuite/primary/basi3p08.png delete mode 100644 impeller/third_party/stb/stb/tests/pngsuite/primary/basi4a08.png delete mode 100644 impeller/third_party/stb/stb/tests/pngsuite/primary/basi6a08.png delete mode 100644 impeller/third_party/stb/stb/tests/pngsuite/primary/basn0g01.png delete mode 100644 impeller/third_party/stb/stb/tests/pngsuite/primary/basn0g02.png delete mode 100644 impeller/third_party/stb/stb/tests/pngsuite/primary/basn0g04.png delete mode 100644 impeller/third_party/stb/stb/tests/pngsuite/primary/basn0g08.png delete mode 100644 impeller/third_party/stb/stb/tests/pngsuite/primary/basn2c08.png delete mode 100644 impeller/third_party/stb/stb/tests/pngsuite/primary/basn3p01.png delete mode 100644 impeller/third_party/stb/stb/tests/pngsuite/primary/basn3p02.png delete mode 100644 impeller/third_party/stb/stb/tests/pngsuite/primary/basn3p04.png delete mode 100644 impeller/third_party/stb/stb/tests/pngsuite/primary/basn3p08.png delete mode 100644 impeller/third_party/stb/stb/tests/pngsuite/primary/basn4a08.png delete mode 100644 impeller/third_party/stb/stb/tests/pngsuite/primary/basn6a08.png delete mode 100644 impeller/third_party/stb/stb/tests/pngsuite/primary/bgai4a08.png delete mode 100644 impeller/third_party/stb/stb/tests/pngsuite/primary/bgan6a08.png delete mode 100644 impeller/third_party/stb/stb/tests/pngsuite/primary/bgbn4a08.png delete mode 100644 impeller/third_party/stb/stb/tests/pngsuite/primary/bgwn6a08.png delete mode 100644 impeller/third_party/stb/stb/tests/pngsuite/primary/s01i3p01.png delete mode 100644 impeller/third_party/stb/stb/tests/pngsuite/primary/s01n3p01.png delete mode 100644 impeller/third_party/stb/stb/tests/pngsuite/primary/s02i3p01.png delete mode 100644 impeller/third_party/stb/stb/tests/pngsuite/primary/s02n3p01.png delete mode 100644 impeller/third_party/stb/stb/tests/pngsuite/primary/s03i3p01.png delete mode 100644 impeller/third_party/stb/stb/tests/pngsuite/primary/s03n3p01.png delete mode 100644 impeller/third_party/stb/stb/tests/pngsuite/primary/s04i3p01.png delete mode 100644 impeller/third_party/stb/stb/tests/pngsuite/primary/s04n3p01.png delete mode 100644 impeller/third_party/stb/stb/tests/pngsuite/primary/s05i3p02.png delete mode 100644 impeller/third_party/stb/stb/tests/pngsuite/primary/s05n3p02.png delete mode 100644 impeller/third_party/stb/stb/tests/pngsuite/primary/s06i3p02.png delete mode 100644 impeller/third_party/stb/stb/tests/pngsuite/primary/s06n3p02.png delete mode 100644 impeller/third_party/stb/stb/tests/pngsuite/primary/s07i3p02.png delete mode 100644 impeller/third_party/stb/stb/tests/pngsuite/primary/s07n3p02.png delete mode 100644 impeller/third_party/stb/stb/tests/pngsuite/primary/s08i3p02.png delete mode 100644 impeller/third_party/stb/stb/tests/pngsuite/primary/s08n3p02.png delete mode 100644 impeller/third_party/stb/stb/tests/pngsuite/primary/s09i3p02.png delete mode 100644 impeller/third_party/stb/stb/tests/pngsuite/primary/s09n3p02.png delete mode 100644 impeller/third_party/stb/stb/tests/pngsuite/primary/s32i3p04.png delete mode 100644 impeller/third_party/stb/stb/tests/pngsuite/primary/s32n3p04.png delete mode 100644 impeller/third_party/stb/stb/tests/pngsuite/primary/s33i3p04.png delete mode 100644 impeller/third_party/stb/stb/tests/pngsuite/primary/s33n3p04.png delete mode 100644 impeller/third_party/stb/stb/tests/pngsuite/primary/s34i3p04.png delete mode 100644 impeller/third_party/stb/stb/tests/pngsuite/primary/s34n3p04.png delete mode 100644 impeller/third_party/stb/stb/tests/pngsuite/primary/s35i3p04.png delete mode 100644 impeller/third_party/stb/stb/tests/pngsuite/primary/s35n3p04.png delete mode 100644 impeller/third_party/stb/stb/tests/pngsuite/primary/s36i3p04.png delete mode 100644 impeller/third_party/stb/stb/tests/pngsuite/primary/s36n3p04.png delete mode 100644 impeller/third_party/stb/stb/tests/pngsuite/primary/s37i3p04.png delete mode 100644 impeller/third_party/stb/stb/tests/pngsuite/primary/s37n3p04.png delete mode 100644 impeller/third_party/stb/stb/tests/pngsuite/primary/s38i3p04.png delete mode 100644 impeller/third_party/stb/stb/tests/pngsuite/primary/s38n3p04.png delete mode 100644 impeller/third_party/stb/stb/tests/pngsuite/primary/s39i3p04.png delete mode 100644 impeller/third_party/stb/stb/tests/pngsuite/primary/s39n3p04.png delete mode 100644 impeller/third_party/stb/stb/tests/pngsuite/primary/s40i3p04.png delete mode 100644 impeller/third_party/stb/stb/tests/pngsuite/primary/s40n3p04.png delete mode 100644 impeller/third_party/stb/stb/tests/pngsuite/primary/tbbn0g04.png delete mode 100644 impeller/third_party/stb/stb/tests/pngsuite/primary/tbbn3p08.png delete mode 100644 impeller/third_party/stb/stb/tests/pngsuite/primary/tbgn3p08.png delete mode 100644 impeller/third_party/stb/stb/tests/pngsuite/primary/tbrn2c08.png delete mode 100644 impeller/third_party/stb/stb/tests/pngsuite/primary/tbwn3p08.png delete mode 100644 impeller/third_party/stb/stb/tests/pngsuite/primary/tbyn3p08.png delete mode 100644 impeller/third_party/stb/stb/tests/pngsuite/primary/tm3n3p02.png delete mode 100644 impeller/third_party/stb/stb/tests/pngsuite/primary/tp0n0g08.png delete mode 100644 impeller/third_party/stb/stb/tests/pngsuite/primary/tp0n2c08.png delete mode 100644 impeller/third_party/stb/stb/tests/pngsuite/primary/tp0n3p08.png delete mode 100644 impeller/third_party/stb/stb/tests/pngsuite/primary/tp1n3p08.png delete mode 100644 impeller/third_party/stb/stb/tests/pngsuite/primary/z00n2c08.png delete mode 100644 impeller/third_party/stb/stb/tests/pngsuite/primary/z03n2c08.png delete mode 100644 impeller/third_party/stb/stb/tests/pngsuite/primary/z06n2c08.png delete mode 100644 impeller/third_party/stb/stb/tests/pngsuite/primary/z09n2c08.png delete mode 100644 impeller/third_party/stb/stb/tests/pngsuite/primary_check/basi0g01.png delete mode 100644 impeller/third_party/stb/stb/tests/pngsuite/primary_check/basi0g02.png delete mode 100644 impeller/third_party/stb/stb/tests/pngsuite/primary_check/basi0g04.png delete mode 100644 impeller/third_party/stb/stb/tests/pngsuite/primary_check/basi0g08.png delete mode 100644 impeller/third_party/stb/stb/tests/pngsuite/primary_check/basi2c08.png delete mode 100644 impeller/third_party/stb/stb/tests/pngsuite/primary_check/basi3p01.png delete mode 100644 impeller/third_party/stb/stb/tests/pngsuite/primary_check/basi3p02.png delete mode 100644 impeller/third_party/stb/stb/tests/pngsuite/primary_check/basi3p04.png delete mode 100644 impeller/third_party/stb/stb/tests/pngsuite/primary_check/basi3p08.png delete mode 100644 impeller/third_party/stb/stb/tests/pngsuite/primary_check/basi4a08.png delete mode 100644 impeller/third_party/stb/stb/tests/pngsuite/primary_check/basi6a08.png delete mode 100644 impeller/third_party/stb/stb/tests/pngsuite/primary_check/basn0g01.png delete mode 100644 impeller/third_party/stb/stb/tests/pngsuite/primary_check/basn0g02.png delete mode 100644 impeller/third_party/stb/stb/tests/pngsuite/primary_check/basn0g04.png delete mode 100644 impeller/third_party/stb/stb/tests/pngsuite/primary_check/basn0g08.png delete mode 100644 impeller/third_party/stb/stb/tests/pngsuite/primary_check/basn2c08.png delete mode 100644 impeller/third_party/stb/stb/tests/pngsuite/primary_check/basn3p01.png delete mode 100644 impeller/third_party/stb/stb/tests/pngsuite/primary_check/basn3p02.png delete mode 100644 impeller/third_party/stb/stb/tests/pngsuite/primary_check/basn3p04.png delete mode 100644 impeller/third_party/stb/stb/tests/pngsuite/primary_check/basn3p08.png delete mode 100644 impeller/third_party/stb/stb/tests/pngsuite/primary_check/basn4a08.png delete mode 100644 impeller/third_party/stb/stb/tests/pngsuite/primary_check/basn6a08.png delete mode 100644 impeller/third_party/stb/stb/tests/pngsuite/primary_check/bgai4a08.png delete mode 100644 impeller/third_party/stb/stb/tests/pngsuite/primary_check/bgan6a08.png delete mode 100644 impeller/third_party/stb/stb/tests/pngsuite/primary_check/bgbn4a08.png delete mode 100644 impeller/third_party/stb/stb/tests/pngsuite/primary_check/bgwn6a08.png delete mode 100644 impeller/third_party/stb/stb/tests/pngsuite/primary_check/s01i3p01.png delete mode 100644 impeller/third_party/stb/stb/tests/pngsuite/primary_check/s01n3p01.png delete mode 100644 impeller/third_party/stb/stb/tests/pngsuite/primary_check/s02i3p01.png delete mode 100644 impeller/third_party/stb/stb/tests/pngsuite/primary_check/s02n3p01.png delete mode 100644 impeller/third_party/stb/stb/tests/pngsuite/primary_check/s03i3p01.png delete mode 100644 impeller/third_party/stb/stb/tests/pngsuite/primary_check/s03n3p01.png delete mode 100644 impeller/third_party/stb/stb/tests/pngsuite/primary_check/s04i3p01.png delete mode 100644 impeller/third_party/stb/stb/tests/pngsuite/primary_check/s04n3p01.png delete mode 100644 impeller/third_party/stb/stb/tests/pngsuite/primary_check/s05i3p02.png delete mode 100644 impeller/third_party/stb/stb/tests/pngsuite/primary_check/s05n3p02.png delete mode 100644 impeller/third_party/stb/stb/tests/pngsuite/primary_check/s06i3p02.png delete mode 100644 impeller/third_party/stb/stb/tests/pngsuite/primary_check/s06n3p02.png delete mode 100644 impeller/third_party/stb/stb/tests/pngsuite/primary_check/s07i3p02.png delete mode 100644 impeller/third_party/stb/stb/tests/pngsuite/primary_check/s07n3p02.png delete mode 100644 impeller/third_party/stb/stb/tests/pngsuite/primary_check/s08i3p02.png delete mode 100644 impeller/third_party/stb/stb/tests/pngsuite/primary_check/s08n3p02.png delete mode 100644 impeller/third_party/stb/stb/tests/pngsuite/primary_check/s09i3p02.png delete mode 100644 impeller/third_party/stb/stb/tests/pngsuite/primary_check/s09n3p02.png delete mode 100644 impeller/third_party/stb/stb/tests/pngsuite/primary_check/s32i3p04.png delete mode 100644 impeller/third_party/stb/stb/tests/pngsuite/primary_check/s32n3p04.png delete mode 100644 impeller/third_party/stb/stb/tests/pngsuite/primary_check/s33i3p04.png delete mode 100644 impeller/third_party/stb/stb/tests/pngsuite/primary_check/s33n3p04.png delete mode 100644 impeller/third_party/stb/stb/tests/pngsuite/primary_check/s34i3p04.png delete mode 100644 impeller/third_party/stb/stb/tests/pngsuite/primary_check/s34n3p04.png delete mode 100644 impeller/third_party/stb/stb/tests/pngsuite/primary_check/s35i3p04.png delete mode 100644 impeller/third_party/stb/stb/tests/pngsuite/primary_check/s35n3p04.png delete mode 100644 impeller/third_party/stb/stb/tests/pngsuite/primary_check/s36i3p04.png delete mode 100644 impeller/third_party/stb/stb/tests/pngsuite/primary_check/s36n3p04.png delete mode 100644 impeller/third_party/stb/stb/tests/pngsuite/primary_check/s37i3p04.png delete mode 100644 impeller/third_party/stb/stb/tests/pngsuite/primary_check/s37n3p04.png delete mode 100644 impeller/third_party/stb/stb/tests/pngsuite/primary_check/s38i3p04.png delete mode 100644 impeller/third_party/stb/stb/tests/pngsuite/primary_check/s38n3p04.png delete mode 100644 impeller/third_party/stb/stb/tests/pngsuite/primary_check/s39i3p04.png delete mode 100644 impeller/third_party/stb/stb/tests/pngsuite/primary_check/s39n3p04.png delete mode 100644 impeller/third_party/stb/stb/tests/pngsuite/primary_check/s40i3p04.png delete mode 100644 impeller/third_party/stb/stb/tests/pngsuite/primary_check/s40n3p04.png delete mode 100644 impeller/third_party/stb/stb/tests/pngsuite/primary_check/tbbn0g04.png delete mode 100644 impeller/third_party/stb/stb/tests/pngsuite/primary_check/tbbn3p08.png delete mode 100644 impeller/third_party/stb/stb/tests/pngsuite/primary_check/tbgn3p08.png delete mode 100644 impeller/third_party/stb/stb/tests/pngsuite/primary_check/tbrn2c08.png delete mode 100644 impeller/third_party/stb/stb/tests/pngsuite/primary_check/tbwn3p08.png delete mode 100644 impeller/third_party/stb/stb/tests/pngsuite/primary_check/tbyn3p08.png delete mode 100644 impeller/third_party/stb/stb/tests/pngsuite/primary_check/tm3n3p02.png delete mode 100644 impeller/third_party/stb/stb/tests/pngsuite/primary_check/tp0n0g08.png delete mode 100644 impeller/third_party/stb/stb/tests/pngsuite/primary_check/tp0n2c08.png delete mode 100644 impeller/third_party/stb/stb/tests/pngsuite/primary_check/tp0n3p08.png delete mode 100644 impeller/third_party/stb/stb/tests/pngsuite/primary_check/tp1n3p08.png delete mode 100644 impeller/third_party/stb/stb/tests/pngsuite/primary_check/z00n2c08.png delete mode 100644 impeller/third_party/stb/stb/tests/pngsuite/primary_check/z03n2c08.png delete mode 100644 impeller/third_party/stb/stb/tests/pngsuite/primary_check/z06n2c08.png delete mode 100644 impeller/third_party/stb/stb/tests/pngsuite/primary_check/z09n2c08.png delete mode 100644 impeller/third_party/stb/stb/tests/pngsuite/unused/ccwn2c08.png delete mode 100644 impeller/third_party/stb/stb/tests/pngsuite/unused/ccwn3p08.png delete mode 100644 impeller/third_party/stb/stb/tests/pngsuite/unused/cdfn2c08.png delete mode 100644 impeller/third_party/stb/stb/tests/pngsuite/unused/cdhn2c08.png delete mode 100644 impeller/third_party/stb/stb/tests/pngsuite/unused/cdsn2c08.png delete mode 100644 impeller/third_party/stb/stb/tests/pngsuite/unused/cdun2c08.png delete mode 100644 impeller/third_party/stb/stb/tests/pngsuite/unused/ch1n3p04.png delete mode 100644 impeller/third_party/stb/stb/tests/pngsuite/unused/ch2n3p08.png delete mode 100644 impeller/third_party/stb/stb/tests/pngsuite/unused/cm0n0g04.png delete mode 100644 impeller/third_party/stb/stb/tests/pngsuite/unused/cm7n0g04.png delete mode 100644 impeller/third_party/stb/stb/tests/pngsuite/unused/cm9n0g04.png delete mode 100644 impeller/third_party/stb/stb/tests/pngsuite/unused/cs3n2c16.png delete mode 100644 impeller/third_party/stb/stb/tests/pngsuite/unused/cs3n3p08.png delete mode 100644 impeller/third_party/stb/stb/tests/pngsuite/unused/cs5n2c08.png delete mode 100644 impeller/third_party/stb/stb/tests/pngsuite/unused/cs5n3p08.png delete mode 100644 impeller/third_party/stb/stb/tests/pngsuite/unused/cs8n2c08.png delete mode 100644 impeller/third_party/stb/stb/tests/pngsuite/unused/cs8n3p08.png delete mode 100644 impeller/third_party/stb/stb/tests/pngsuite/unused/ct0n0g04.png delete mode 100644 impeller/third_party/stb/stb/tests/pngsuite/unused/ct1n0g04.png delete mode 100644 impeller/third_party/stb/stb/tests/pngsuite/unused/cten0g04.png delete mode 100644 impeller/third_party/stb/stb/tests/pngsuite/unused/ctfn0g04.png delete mode 100644 impeller/third_party/stb/stb/tests/pngsuite/unused/ctgn0g04.png delete mode 100644 impeller/third_party/stb/stb/tests/pngsuite/unused/cthn0g04.png delete mode 100644 impeller/third_party/stb/stb/tests/pngsuite/unused/ctjn0g04.png delete mode 100644 impeller/third_party/stb/stb/tests/pngsuite/unused/ctzn0g04.png delete mode 100644 impeller/third_party/stb/stb/tests/pngsuite/unused/f00n0g08.png delete mode 100644 impeller/third_party/stb/stb/tests/pngsuite/unused/f00n2c08.png delete mode 100644 impeller/third_party/stb/stb/tests/pngsuite/unused/f01n0g08.png delete mode 100644 impeller/third_party/stb/stb/tests/pngsuite/unused/f01n2c08.png delete mode 100644 impeller/third_party/stb/stb/tests/pngsuite/unused/f02n0g08.png delete mode 100644 impeller/third_party/stb/stb/tests/pngsuite/unused/f02n2c08.png delete mode 100644 impeller/third_party/stb/stb/tests/pngsuite/unused/f03n0g08.png delete mode 100644 impeller/third_party/stb/stb/tests/pngsuite/unused/f03n2c08.png delete mode 100644 impeller/third_party/stb/stb/tests/pngsuite/unused/f04n0g08.png delete mode 100644 impeller/third_party/stb/stb/tests/pngsuite/unused/f04n2c08.png delete mode 100644 impeller/third_party/stb/stb/tests/pngsuite/unused/f99n0g04.png delete mode 100644 impeller/third_party/stb/stb/tests/pngsuite/unused/g03n0g16.png delete mode 100644 impeller/third_party/stb/stb/tests/pngsuite/unused/g03n2c08.png delete mode 100644 impeller/third_party/stb/stb/tests/pngsuite/unused/g03n3p04.png delete mode 100644 impeller/third_party/stb/stb/tests/pngsuite/unused/g04n0g16.png delete mode 100644 impeller/third_party/stb/stb/tests/pngsuite/unused/g04n2c08.png delete mode 100644 impeller/third_party/stb/stb/tests/pngsuite/unused/g04n3p04.png delete mode 100644 impeller/third_party/stb/stb/tests/pngsuite/unused/g05n0g16.png delete mode 100644 impeller/third_party/stb/stb/tests/pngsuite/unused/g05n2c08.png delete mode 100644 impeller/third_party/stb/stb/tests/pngsuite/unused/g05n3p04.png delete mode 100644 impeller/third_party/stb/stb/tests/pngsuite/unused/g07n0g16.png delete mode 100644 impeller/third_party/stb/stb/tests/pngsuite/unused/g07n2c08.png delete mode 100644 impeller/third_party/stb/stb/tests/pngsuite/unused/g07n3p04.png delete mode 100644 impeller/third_party/stb/stb/tests/pngsuite/unused/g10n0g16.png delete mode 100644 impeller/third_party/stb/stb/tests/pngsuite/unused/g10n2c08.png delete mode 100644 impeller/third_party/stb/stb/tests/pngsuite/unused/g10n3p04.png delete mode 100644 impeller/third_party/stb/stb/tests/pngsuite/unused/g25n0g16.png delete mode 100644 impeller/third_party/stb/stb/tests/pngsuite/unused/g25n2c08.png delete mode 100644 impeller/third_party/stb/stb/tests/pngsuite/unused/g25n3p04.png delete mode 100644 impeller/third_party/stb/stb/tests/pngsuite/unused/pp0n2c16.png delete mode 100644 impeller/third_party/stb/stb/tests/pngsuite/unused/pp0n6a08.png delete mode 100644 impeller/third_party/stb/stb/tests/pngsuite/unused/ps1n0g08.png delete mode 100644 impeller/third_party/stb/stb/tests/pngsuite/unused/ps1n2c16.png delete mode 100644 impeller/third_party/stb/stb/tests/pngsuite/unused/ps2n0g08.png delete mode 100644 impeller/third_party/stb/stb/tests/pngsuite/unused/ps2n2c16.png delete mode 100644 impeller/third_party/stb/stb/tests/resample_test.cpp delete mode 100644 impeller/third_party/stb/stb/tests/resample_test_c.c delete mode 100644 impeller/third_party/stb/stb/tests/resize.dsp delete mode 100644 impeller/third_party/stb/stb/tests/stb.c delete mode 100644 impeller/third_party/stb/stb/tests/stb.dsp delete mode 100644 impeller/third_party/stb/stb/tests/stb.dsw delete mode 100644 impeller/third_party/stb/stb/tests/stb_cpp.cpp delete mode 100644 impeller/third_party/stb/stb/tests/stb_cpp.dsp delete mode 100644 impeller/third_party/stb/stb/tests/stretch_test.c delete mode 100644 impeller/third_party/stb/stb/tests/stretch_test.dsp delete mode 100644 impeller/third_party/stb/stb/tests/stretchy_buffer_test.c delete mode 100644 impeller/third_party/stb/stb/tests/test_c_compilation.c delete mode 100644 impeller/third_party/stb/stb/tests/test_cpp_compilation.cpp delete mode 100644 impeller/third_party/stb/stb/tests/test_truetype.c delete mode 100644 impeller/third_party/stb/stb/tests/test_vorbis.c delete mode 100644 impeller/third_party/stb/stb/tests/textedit_sample.c delete mode 100644 impeller/third_party/stb/stb/tests/tilemap_editor_integration_example.c delete mode 100644 impeller/third_party/stb/stb/tests/vorbseek/vorbseek.c delete mode 100644 impeller/third_party/stb/stb/tests/vorbseek/vorbseek.dsp delete mode 100644 impeller/third_party/stb/stb/tools/README.footer.md delete mode 100644 impeller/third_party/stb/stb/tools/README.header.md delete mode 100644 impeller/third_party/stb/stb/tools/README.list delete mode 100644 impeller/third_party/stb/stb/tools/easy_font_maker.c delete mode 100644 impeller/third_party/stb/stb/tools/make_readme.c delete mode 100644 impeller/third_party/stb/stb/tools/make_readme.dsp delete mode 100644 impeller/third_party/stb/stb/tools/mr.bat delete mode 100644 impeller/third_party/stb/stb/tools/unicode.c delete mode 100644 impeller/third_party/stb/stb/tools/unicode/unicode.dsp diff --git a/impeller/image/BUILD.gn b/impeller/image/BUILD.gn index 5475257810b84..0d61d2deeb7ea 100644 --- a/impeller/image/BUILD.gn +++ b/impeller/image/BUILD.gn @@ -5,14 +5,19 @@ import("//flutter/impeller/tools/impeller.gni") impeller_component("image") { + public = [ + "compressed_image.h", + "decompressed_image.h", + ] + sources = [ + "backends/skia/compressed_image_skia.cc", + "backends/skia/compressed_image_skia.h", "compressed_image.cc", - "compressed_image.h", "decompressed_image.cc", - "decompressed_image.h", ] - deps = [ "../third_party/stb" ] + deps = [ "//third_party/skia" ] public_deps = [ "../base", diff --git a/impeller/image/backends/skia/compressed_image_skia.cc b/impeller/image/backends/skia/compressed_image_skia.cc new file mode 100644 index 0000000000000..0b3fc9da19234 --- /dev/null +++ b/impeller/image/backends/skia/compressed_image_skia.cc @@ -0,0 +1,73 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "impeller/image/backends/skia/compressed_image_skia.h" + +#include + +#include "impeller/base/validation.h" +#include "third_party/skia/include/core/SkData.h" +#include "third_party/skia/include/core/SkImageGenerator.h" +#include "third_party/skia/include/core/SkPixmap.h" + +namespace impeller { + +CompressedImageSkia::CompressedImageSkia( + std::shared_ptr allocation) + : CompressedImage(std::move(allocation)) {} + +CompressedImageSkia::~CompressedImageSkia() = default; + +// |CompressedImage| +DecompressedImage CompressedImageSkia::Decode() const { + if (!IsValid()) { + return {}; + } + if (source_->GetSize() == 0u) { + return {}; + } + + auto src = new std::shared_ptr(source_); + auto sk_data = SkData::MakeWithProc( + source_->GetMapping(), source_->GetSize(), + [](const void* ptr, void* context) { + delete reinterpret_cast(context); + }, + src); + + auto generator = SkImageGenerator::MakeFromEncoded(sk_data); + if (!generator) { + return {}; + } + + auto info = SkImageInfo::MakeN32Premul(generator->getInfo().dimensions()); + + auto bitmap = std::make_shared(); + if (!bitmap->tryAllocPixels(info)) { + VALIDATION_LOG << "Could not allocate arena for decompressing image."; + return {}; + } + + if (!generator->getPixels(bitmap->pixmap())) { + VALIDATION_LOG << "Could not decompress image into arena."; + return {}; + } + + auto mapping = std::make_shared( + reinterpret_cast(bitmap->pixmap().addr()), // data + bitmap->pixmap().rowBytes() * bitmap->pixmap().height(), // size + [bitmap](const uint8_t* data, size_t size) mutable { + bitmap.reset(); + } // proc + ); + + return { + {bitmap->pixmap().dimensions().fWidth, + bitmap->pixmap().dimensions().fHeight}, // size + DecompressedImage::Format::kRGBA, // format + mapping // allocation + }; +} + +} // namespace impeller diff --git a/impeller/image/backends/skia/compressed_image_skia.h b/impeller/image/backends/skia/compressed_image_skia.h new file mode 100644 index 0000000000000..856c5304cc385 --- /dev/null +++ b/impeller/image/backends/skia/compressed_image_skia.h @@ -0,0 +1,25 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#pragma once + +#include "flutter/fml/macros.h" +#include "impeller/image/compressed_image.h" + +namespace impeller { + +class CompressedImageSkia final : public CompressedImage { + public: + CompressedImageSkia(std::shared_ptr allocation); + + ~CompressedImageSkia() override; + + // |CompressedImage| + DecompressedImage Decode() const override; + + private: + FML_DISALLOW_COPY_AND_ASSIGN(CompressedImageSkia); +}; + +} // namespace impeller diff --git a/impeller/image/compressed_image.cc b/impeller/image/compressed_image.cc index 0837be17e24d0..15b4e01487b9c 100644 --- a/impeller/image/compressed_image.cc +++ b/impeller/image/compressed_image.cc @@ -4,82 +4,23 @@ #include "impeller/image/compressed_image.h" -#include - -#include "impeller/base/validation.h" +#include "impeller/image/backends/skia/compressed_image_skia.h" namespace impeller { -CompressedImage::CompressedImage( - std::shared_ptr sourceAllocation) - : source_(std::move(sourceAllocation)) {} - -CompressedImage::~CompressedImage() = default; - -DecompressedImage CompressedImage::Decode() const { - if (!source_) { - return {}; - } - - int width = 0; - int height = 0; - int comps = 0; - - stbi_uc* decoded = - ::stbi_load_from_memory(source_->GetMapping(), // Source Data - source_->GetSize(), // Source Data Size - &width, // Out: Width - &height, // Out: Height - &comps, // Out: Components - STBI_default); - - if (decoded == nullptr) { - VALIDATION_LOG << "Could not decode image from host memory."; - return {}; - } - - auto dest_allocation = std::make_shared( - decoded, // bytes - width * height * comps * sizeof(stbi_uc), // byte size - [](const uint8_t* data, size_t size) { - ::stbi_image_free(const_cast(data)); - } // release proc - ); - - /* - * Make sure we got a valid component set. - */ - auto components = DecompressedImage::Format::kInvalid; - - switch (comps) { - case STBI_grey: - components = DecompressedImage::Format::kGrey; - break; - case STBI_grey_alpha: - components = DecompressedImage::Format::kGreyAlpha; - break; - case STBI_rgb: - components = DecompressedImage::Format::kRGB; - break; - case STBI_rgb_alpha: - components = DecompressedImage::Format::kRGBA; - break; - default: - components = DecompressedImage::Format::kInvalid; - break; +std::shared_ptr CompressedImage::Create( + std::shared_ptr allocation) { + // There is only one backend today. + if (!allocation) { + return nullptr; } + return std::make_shared(std::move(allocation)); +} - if (components == DecompressedImage::Format::kInvalid) { - VALIDATION_LOG << "Could not detect image components when decoding."; - return {}; - } +CompressedImage::CompressedImage(std::shared_ptr allocation) + : source_(std::move(allocation)) {} - return DecompressedImage{ - ISize{width, height}, // size - components, // components - std::move(dest_allocation) // allocation - }; -} +CompressedImage::~CompressedImage() = default; bool CompressedImage::IsValid() const { return static_cast(source_); diff --git a/impeller/image/compressed_image.h b/impeller/image/compressed_image.h index 7b79096f3d39e..58c5a5050d66e 100644 --- a/impeller/image/compressed_image.h +++ b/impeller/image/compressed_image.h @@ -4,6 +4,8 @@ #pragma once +#include + #include "flutter/fml/macros.h" #include "flutter/fml/mapping.h" #include "impeller/geometry/size.h" @@ -15,16 +17,19 @@ class ImageSource; class CompressedImage { public: - CompressedImage(std::shared_ptr sourceAllocation); + static std::shared_ptr Create( + std::shared_ptr allocation); - ~CompressedImage(); + virtual ~CompressedImage(); - [[nodiscard]] DecompressedImage Decode() const; + [[nodiscard]] virtual DecompressedImage Decode() const = 0; bool IsValid() const; - private: - std::shared_ptr source_; + protected: + const std::shared_ptr source_; + + CompressedImage(std::shared_ptr allocation); }; } // namespace impeller diff --git a/impeller/playground/playground.mm b/impeller/playground/playground.mm index 52b2c11599ed6..be65b582ad6e5 100644 --- a/impeller/playground/playground.mm +++ b/impeller/playground/playground.mm @@ -195,14 +195,18 @@ static void PlaygroundKeyCallback(GLFWwindow* window, std::shared_ptr Playground::CreateTextureForFixture( const char* fixture_name) const { - CompressedImage compressed_image( + auto compressed_image = CompressedImage::Create( flutter::testing::OpenFixtureAsMapping(fixture_name)); + if (!compressed_image) { + VALIDATION_LOG << "Could not create compressed image."; + return nullptr; + } // The decoded image is immediately converted into RGBA as that format is // known to be supported everywhere. For image sources that don't need 32 // bit pixel strides, this is overkill. Since this is a test fixture we // aren't necessarily trying to eke out memory savings here and instead // favor simplicity. - auto image = compressed_image.Decode().ConvertToRGBA(); + auto image = compressed_image->Decode().ConvertToRGBA(); if (!image.IsValid()) { VALIDATION_LOG << "Could not find fixture named " << fixture_name; return nullptr; diff --git a/impeller/third_party/stb/BUILD.gn b/impeller/third_party/stb/BUILD.gn deleted file mode 100644 index 942bbd0ba1685..0000000000000 --- a/impeller/third_party/stb/BUILD.gn +++ /dev/null @@ -1,12 +0,0 @@ -# Copyright 2013 The Flutter Authors. All rights reserved. -# Use of this source code is governed by a BSD-style license that can be -# found in the LICENSE file. - -config("stb_config") { - include_dirs = [ "stb" ] -} - -source_set("stb") { - public_configs = [ ":stb_config" ] - sources = [ "STBImplementation.c" ] -} diff --git a/impeller/third_party/stb/STBImplementation.c b/impeller/third_party/stb/STBImplementation.c deleted file mode 100644 index 1751a6251425d..0000000000000 --- a/impeller/third_party/stb/STBImplementation.c +++ /dev/null @@ -1,11 +0,0 @@ -// Copyright 2013 The Flutter Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#define STB_IMAGE_IMPLEMENTATION - -#include - -#define STB_IMAGE_WRITE_IMPLEMENTATION - -#include diff --git a/impeller/third_party/stb/stb/README.md b/impeller/third_party/stb/stb/README.md deleted file mode 100644 index f813a5bb360f8..0000000000000 --- a/impeller/third_party/stb/stb/README.md +++ /dev/null @@ -1,131 +0,0 @@ - - -stb -=== - -single-file public domain libraries for C/C++ - -library | lastest version | category | LoC | description ---------------------- | ---- | -------- | --- | -------------------------------- -**stb_vorbis.c** | 1.09 | audio | 5397 | decode ogg vorbis files from file/memory to float/16-bit signed output -**stb_image.h** | 2.12 | graphics | 6755 | image loading/decoding from file/memory: JPG, PNG, TGA, BMP, PSD, GIF, HDR, PIC -**stb_truetype.h** | 1.11 | graphics | 3267 | parse, decode, and rasterize characters from truetype fonts -**stb_image_write.h** | 1.02 | graphics | 1048 | image writing to disk: PNG, TGA, BMP -**stb_image_resize.h** | 0.91 | graphics | 2578 | resize images larger/smaller with good quality -**stb_rect_pack.h** | 0.08 | graphics | 572 | simple 2D rectangle packer with decent quality -**stretchy_buffer.h** | 1.02 | utility | 216 | typesafe dynamic array for C (i.e. approximation to vector<>), doesn't compile as C++ -**stb_textedit.h** | 1.8 | user interface | 1304 | guts of a text editor for games etc implementing them from scratch -**stb_voxel_render.h** | 0.84 | 3D graphics | 3752 | Minecraft-esque voxel rendering "engine" with many more features -**stb_dxt.h** | 1.04 | 3D graphics | 630 | Fabian "ryg" Giesen's real-time DXT compressor -**stb_perlin.h** | 0.2 | 3D graphics | 182 | revised Perlin noise (3D input, 1D output) -**stb_easy_font.h** | 0.7 | 3D graphics | 258 | quick-and-dirty easy-to-deploy bitmap font for printing frame rate, etc -**stb_tilemap_editor.h** | 0.37 | game dev | 4131 | embeddable tilemap editor -**stb_herringbone_wa...** | 0.6 | game dev | 1220 | herringbone Wang tile map generator -**stb_c_lexer.h** | 0.07 | parsing | 816 | simplify writing parsers for C-like languages -**stb_divide.h** | 0.91 | math | 379 | more useful 32-bit modulus e.g. "euclidean divide" -**stb.h** | 2.27 | misc | 14185 | helper functions for C, mostly redundant in C++; basically author's personal stuff -**stb_leakcheck.h** | 0.2 | misc | 124 | quick-and-dirty malloc/free leak-checking - -Total libraries: 18 -Total lines of C code: 46814 - - -FAQ ---- - -#### What's the license? - -These libraries are in the public domain (or the equivalent where that is not -possible). You can do anything you want with them. You have no legal obligation -to do anything else, although I appreciate attribution. - -#### Are there other single-file public-domain/open source libraries with minimal dependencies out there? - -[Yes.](https://github.com/nothings/stb/blob/master/docs/other_libs.md) - -#### If I wrap an stb library in a new library, does the new library have to be public domain? - -No. - -#### Some of these libraries seem redundant to existing open source libraries. Are they better somehow? - -Generally they're only better in that they're easier to integrate, -easier to use, and easier to release (single file; good API; no -attribution requirement). They may be less featureful, slower, -and/or use more memory. If you're already using an equivalent -library, there's probably no good reason to switch. - -###### Can I link directly to the table of stb libraries? - -You can use [this URL](https://github.com/nothings/stb#stb_libs) to link directly to that list. - -#### Why do you list "lines of code"? It's a terrible metric. - -Just to give you some idea of the internal complexity of the library, -to help you manage your expectations, or to let you know what you're -getting into. While not all the libraries are written in the same -style, they're certainly similar styles, and so comparisons between -the libraries are probably still meaningful. - -Note though that the lines do include both the implementation, the -part that corresponds to a header file, and the documentation. - -#### Why single-file headers? - -Windows doesn't have standard directories where libraries -live. That makes deploying libraries in Windows a lot more -painful than open source developers on Unix-derivates generally -realize. (It also makes library dependencies a lot worse in Windows.) - -There's also a common problem in Windows where a library was built -against a different version of the runtime library, which causes -link conflicts and confusion. Shipping the libs as headers means -you normally just compile them straight into your project without -making libraries, thus sidestepping that problem. - -Making them a single file makes it very easy to just -drop them into a project that needs them. (Of course you can -still put them in a proper shared library tree if you want.) - -Why not two files, one a header and one an implementation? -The difference between 10 files and 9 files is not a big deal, -but the difference between 2 files and 1 file is a big deal. -You don't need to zip or tar the files up, you don't have to -remember to attach *two* files, etc. - -#### Why "stb"? Is this something to do with Set-Top Boxes? - -No, they are just the initials for my name, Sean T. Barrett. -This was not chosen out of egomania, but as a moderately sane -way of namespacing the filenames and source function names. - -#### Will you add more image types to stb_image.h? - -If people submit them, I generally add them, but the goal of stb_image -is less for applications like image viewer apps (which need to support -every type of image under the sun) and more for things like games which -can choose what images to use, so I may decline to add them if they're -too rare or if the size of implementation vs. apparent benefit is too low. - -#### Do you have any advice on how to create my own single-file library? - -Yes. https://github.com/nothings/stb/blob/master/docs/stb_howto.txt - -#### Why public domain? - -I prefer it over GPL, LGPL, BSD, zlib, etc. for many reasons. -Some of them are listed here: -https://github.com/nothings/stb/blob/master/docs/why_public_domain.md - -#### Why C? - -Primarily, because I use C, not C++. But it does also make it easier -for other people to use them from other languages. - -#### Why not C99? stdint.h, declare-anywhere, etc. - -I still use MSVC 6 (1998) as my IDE because it has better human factors -for me than later versions of MSVC. - - - diff --git a/impeller/third_party/stb/stb/data/atari_8bit_font_revised.png b/impeller/third_party/stb/stb/data/atari_8bit_font_revised.png deleted file mode 100644 index 91c553c6387fd5eb6c09db3b1aa05ede45d4ba82..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1769 zcmVU8P*7-ZbZ>KLZ*U+lnSp_Ufq@}0xwybFAi#%#fq@|}KQEO56)-X|e7nZL z$iTqBa9P*U#mSX{G{Bl%P*lRez;J+pfx##xwK$o9f#C}S14DXwNkIt%17i#W1A|CX zc0maP17iUL1A|C*NRTrF17iyV0~1e4YDEbH0|SF|enDkXW_m`6f}y3QrGjHhep0GJ zaAk2xYHqQDXI^rCQ9*uDVo7QW0|Nup4h9AW240u^5(W3f%sd4n162kpgNVo|1qcff zJ_s=cNG>fZg9jx8g8+j9g8_pBLjXe}Lp{R+hNBE`7{wV~7)u#fFy3PlV+vxLz;uCG zm^qSpA@ds+OO_6nTdaDlt*rOhEZL^9ePa)2-_4=K(Z%tFGm-NGmm}8}ZcXk5JW@PU zd4+f<@d@)yL(o<5icqT158+-B6_LH7;i6x}CW#w~Uy-Pgl#@Irl`kzV zeL|*8R$ca%T%Wv){2zs_iiJvgN^h0dsuZZ2sQy$tsNSU!s;Q*;LF<6_B%M@UD?LHI zSNcZ`78uqV#TeU~$eS{ozBIdFzSClfs*^S+dw;4dus<{M;#|MXC)T}S9v!D zcV!QCPhBq)ZyO(X-(bH4|NMaZz==UigLj2o41F2S6d@OB6%`R(5i>J(Puzn9wnW{e zu;hl6HK{k#IWjCVGqdJqU(99Cv(K+6*i`tgSi2;vbXD1#3jNBGs$DgVwO(~o>mN4i zHPtkqZIx>)Y(Ls5-Br|mx>vQYvH$Kwn@O`L|D75??eGkZnfg$5<;Xeg_o%+-I&+-3%01W^SH2RkDT>t<8AY({UO#lFTB>(_`g8%^e z{{R4h=>PzAFaQARU;qF*m;eA5Z<1fdMgRZ9Ya zfbQPS&6I9gN+2Pe@ZLVg#M7Yp7n=!*KBhs3V0@#wJYRvu4-EIh0eFD(WQ_!C0MS1yUwmi$ zJOuHM4kldFpro=@Axc~(JETEnh|ZpJz-E{swgX>4b&wnoTOZ>V&NW)JLl@EyagL5* z<%k38M}Yb>cl!Y3=nD{U?_0&j{5?*Mi=jpZqlp3NKM;{arC$))B zMejr(NF3})kH|P*J0!7;eV@V{PN3FAk#h8PVjiYvxZBBribS1Mmc9dOO0dyfO4tnn zK8E_624=rWe)H>v@3q07AGsTI9`c06a zBi|D*8`IJkCmwYDgqD_3QJpK^xBahAKCuD6l`Ca?g-)%{ag9C=&Yhx@1%%J?d6lf4 zQZU{emJ$p<+{ovqV1Jg+TUjyW^UBSQ#9pzkz|;~WyACtFJ3t(Z*0X<@>#=RhmhJALb# zqPNH3e6XUIB`hrGNSf;NlZCE6p9hSX3v7oSaOL0nxvA6QVph1qgEw(*3Jb)2oKG=j zlo+eWDSQqKaae^lrurOPvD>l+CW8l8QnXBWR~D(o-ap}$g0GlL20-|dP~QmIdiX|tuH~m;D>P3r z)#qd&2P>y<+ diff --git a/impeller/third_party/stb/stb/data/easy_font_raw.png b/impeller/third_party/stb/stb/data/easy_font_raw.png deleted file mode 100644 index 2f08148b4614413c1c975ab264ca563fdf667c65..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 645 zcmV;00($+4P)dL_t(|UhS5FYQr!LMDHp4|8E|o2WL&hWbGJiRCUf$ zY{`)?s-q7{A4XlE+q@aq`w+kGn&`JeMBR@DHueUS=)8LYGtsDfr|vv4(Bu9ac}tzY z2j~Pid+-f}c9Mx;I0Ir^(F{}YpnIgUhRPP+y~S(gUW5JS8hGbs@i?eVmxG< zJ&U8a3qQ>wGB?wcgM&3m^G!?mg2nQbyDTC%*XSClo@CL6^-_J4ML!?v%VtfoaY#^W znS)E8gA7`nI0*SZ(J1c-;$Xg7DEkJiEFwCKsm1k0O2VcY07-K1DonO=GB}YFGT?j@ zK58ut#5*G(gM{aV#rn#kAtP2*m88~rsW<17E1PM#de0a^AIOrMG=c2FU{NHj4V5(% zNiEtp4=!zMG3(}PB}4w0ttA4okW~Qg)M9OFQJN9_^K?xXt19&t@?u$bEW!ngXv<=X zrqZ&j9_V*aMQ f1)BOYi~B&Iu%ieWP969(00000NkvXXu0mjfRf{XR diff --git a/impeller/third_party/stb/stb/data/herringbone/license.txt b/impeller/third_party/stb/stb/data/herringbone/license.txt deleted file mode 100644 index 11ffc42e57199..0000000000000 --- a/impeller/third_party/stb/stb/data/herringbone/license.txt +++ /dev/null @@ -1,4 +0,0 @@ -All files in this directory are in the public domain. Where -a public domain declaration is not recognized, you are granted -a license to freely use, modify, and redistribute them in -any way you choose. \ No newline at end of file diff --git a/impeller/third_party/stb/stb/data/herringbone/template_caves_limit_connectivity.png b/impeller/third_party/stb/stb/data/herringbone/template_caves_limit_connectivity.png deleted file mode 100644 index 1c286e7ce44665d478b9bf4c767826793bab28b4..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 10818 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ⅇhl(t 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 M07*qoM6N<$f^o<=cK`qY diff --git a/impeller/third_party/stb/stb/data/herringbone/template_caves_tiny_corridors.png b/impeller/third_party/stb/stb/data/herringbone/template_caves_tiny_corridors.png deleted file mode 100644 index e9b0d444eade835c71554eb09e253d0d4c541fb1..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 4904 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 a@$UfWm`XT>Heh}L0000OtuM9NCZ-^ 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 diff --git a/impeller/third_party/stb/stb/data/herringbone/template_horizontal_corridors_v3.png b/impeller/third_party/stb/stb/data/herringbone/template_horizontal_corridors_v3.png deleted file mode 100644 index f921807255481904cd8800be3adff4cd7138acb1..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 6766 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 Qk diff --git a/impeller/third_party/stb/stb/data/herringbone/template_limit_connectivity_fat.png b/impeller/third_party/stb/stb/data/herringbone/template_limit_connectivity_fat.png deleted file mode 100644 index dda13023c77749a9f5dfe7d36eb9d5c62404e70b..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 6655 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 diff --git a/impeller/third_party/stb/stb/data/herringbone/template_limited_connectivity.png b/impeller/third_party/stb/stb/data/herringbone/template_limited_connectivity.png deleted file mode 100644 index d9f97c9c6463b3ccba096d946e86412eb57e8846..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 6728 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 diff --git a/impeller/third_party/stb/stb/data/herringbone/template_maze_plus_2_wide.png b/impeller/third_party/stb/stb/data/herringbone/template_maze_plus_2_wide.png deleted file mode 100644 index d27f7bd8762461db38869c8397ca335f7619de3d..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 13141 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 }>*>vQfd-W65-M^3{ff6BuI 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%0DA5(W={doe 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 G^8W$t>>vFA diff --git a/impeller/third_party/stb/stb/data/herringbone/template_rooms_and_corridors.png b/impeller/third_party/stb/stb/data/herringbone/template_rooms_and_corridors.png deleted file mode 100644 index c0467f3e92b00086f4d74ca4498ed4f1b6156d64..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 4736 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�Ea(pn}$By;{m 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 Y)$SCh)qml~ifuP#?FV;25@0Cg$+ng9R* diff --git a/impeller/third_party/stb/stb/data/herringbone/template_round_rooms_diagonal_corridors.png b/impeller/third_party/stb/stb/data/herringbone/template_round_rooms_diagonal_corridors.png deleted file mode 100644 index 2073f988906f634b1c0f884470df9f10c8bac369..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 8172 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 bwErIfH*5$lY+ElB00000NkvXXu0mjfWy4L8 diff --git a/impeller/third_party/stb/stb/data/herringbone/template_simple_caves_2_wide.png b/impeller/third_party/stb/stb/data/herringbone/template_simple_caves_2_wide.png deleted file mode 100644 index 321727146615e7644ced4a1c96157cae96cf76be..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 15712 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 UDo4cI6&8SkjH+~%)Ti(N2Vn*JQ~&?~ diff --git a/impeller/third_party/stb/stb/data/herringbone/template_square_rooms_with_random_rects.png b/impeller/third_party/stb/stb/data/herringbone/template_square_rooms_with_random_rects.png deleted file mode 100644 index 0d7e82e560c996d89533869f71aa66aed4e9dbf4..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 4772 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 1400 -#define _CRT_SECURE_NO_WARNINGS // suppress warnings about fopen() -#pragma warning(push) -#pragma warning(disable:4996) // suppress even more warnings about fopen() -#endif -#include -#endif // STBI_NO_STDIO - -#define STBI_VERSION 1 - -enum -{ - STBI_default = 0, // only used for req_comp - - STBI_grey = 1, - STBI_grey_alpha = 2, - STBI_rgb = 3, - STBI_rgb_alpha = 4 -}; - -typedef unsigned char stbi_uc; - -#ifdef __cplusplus -extern "C" { -#endif - -////////////////////////////////////////////////////////////////////////////// -// -// PRIMARY API - works on images of any type -// - -// -// load image by filename, open file, or memory buffer -// - -extern stbi_uc *stbi_load_from_memory(stbi_uc const *buffer, int len, int *x, int *y, int *comp, int req_comp); - -#ifndef STBI_NO_STDIO -extern stbi_uc *stbi_load (char const *filename, int *x, int *y, int *comp, int req_comp); -extern stbi_uc *stbi_load_from_file (FILE *f, int *x, int *y, int *comp, int req_comp); -// for stbi_load_from_file, file pointer is left pointing immediately after image -#endif - -typedef struct -{ - int (*read) (void *user,char *data,int size); // fill 'data' with 'size' bytes. return number of bytes actually read - void (*skip) (void *user,int n); // skip the next 'n' bytes, or 'unget' the last -n bytes if negative - int (*eof) (void *user); // returns nonzero if we are at end of file/data -} stbi_io_callbacks; - -extern stbi_uc *stbi_load_from_callbacks (stbi_io_callbacks const *clbk, void *user, int *x, int *y, int *comp, int req_comp); - -#ifndef STBI_NO_HDR - extern float *stbi_loadf_from_memory(stbi_uc const *buffer, int len, int *x, int *y, int *comp, int req_comp); - - #ifndef STBI_NO_STDIO - extern float *stbi_loadf (char const *filename, int *x, int *y, int *comp, int req_comp); - extern float *stbi_loadf_from_file (FILE *f, int *x, int *y, int *comp, int req_comp); - #endif - - extern float *stbi_loadf_from_callbacks (stbi_io_callbacks const *clbk, void *user, int *x, int *y, int *comp, int req_comp); - - extern void stbi_hdr_to_ldr_gamma(float gamma); - extern void stbi_hdr_to_ldr_scale(float scale); - - extern void stbi_ldr_to_hdr_gamma(float gamma); - extern void stbi_ldr_to_hdr_scale(float scale); -#endif // STBI_NO_HDR - -// stbi_is_hdr is always defined -extern int stbi_is_hdr_from_callbacks(stbi_io_callbacks const *clbk, void *user); -extern int stbi_is_hdr_from_memory(stbi_uc const *buffer, int len); -#ifndef STBI_NO_STDIO -extern int stbi_is_hdr (char const *filename); -extern int stbi_is_hdr_from_file(FILE *f); -#endif // STBI_NO_STDIO - - -// get a VERY brief reason for failure -// NOT THREADSAFE -extern const char *stbi_failure_reason (void); - -// free the loaded image -- this is just free() -extern void stbi_image_free (void *retval_from_stbi_load); - -// get image dimensions & components without fully decoding -extern int stbi_info_from_memory(stbi_uc const *buffer, int len, int *x, int *y, int *comp); -extern int stbi_info_from_callbacks(stbi_io_callbacks const *clbk, void *user, int *x, int *y, int *comp); - -#ifndef STBI_NO_STDIO -extern int stbi_info (char const *filename, int *x, int *y, int *comp); -extern int stbi_info_from_file (FILE *f, int *x, int *y, int *comp); - -#endif - - - -// for image formats that explicitly notate that they have premultiplied alpha, -// we just return the colors as stored in the file. set this flag to force -// unpremultiplication. results are undefined if the unpremultiply overflow. -extern void stbi_set_unpremultiply_on_load(int flag_true_if_should_unpremultiply); - -// indicate whether we should process iphone images back to canonical format, -// or just pass them through "as-is" -extern void stbi_convert_iphone_png_to_rgb(int flag_true_if_should_convert); - - -// ZLIB client - used by PNG, available for other purposes - -extern char *stbi_zlib_decode_malloc_guesssize(const char *buffer, int len, int initial_size, int *outlen); -extern char *stbi_zlib_decode_malloc_guesssize_headerflag(const char *buffer, int len, int initial_size, int *outlen, int parse_header); -extern char *stbi_zlib_decode_malloc(const char *buffer, int len, int *outlen); -extern int stbi_zlib_decode_buffer(char *obuffer, int olen, const char *ibuffer, int ilen); - -extern char *stbi_zlib_decode_noheader_malloc(const char *buffer, int len, int *outlen); -extern int stbi_zlib_decode_noheader_buffer(char *obuffer, int olen, const char *ibuffer, int ilen); - - -// define faster low-level operations (typically SIMD support) -#ifdef STBI_SIMD -typedef void (*stbi_idct_8x8)(stbi_uc *out, int out_stride, short data[64], unsigned short *dequantize); -// compute an integer IDCT on "input" -// input[x] = data[x] * dequantize[x] -// write results to 'out': 64 samples, each run of 8 spaced by 'out_stride' -// CLAMP results to 0..255 -typedef void (*stbi_YCbCr_to_RGB_run)(stbi_uc *output, stbi_uc const *y, stbi_uc const *cb, stbi_uc const *cr, int count, int step); -// compute a conversion from YCbCr to RGB -// 'count' pixels -// write pixels to 'output'; each pixel is 'step' bytes (either 3 or 4; if 4, write '255' as 4th), order R,G,B -// y: Y input channel -// cb: Cb input channel; scale/biased to be 0..255 -// cr: Cr input channel; scale/biased to be 0..255 - -extern void stbi_install_idct(stbi_idct_8x8 func); -extern void stbi_install_YCbCr_to_RGB(stbi_YCbCr_to_RGB_run func); -#endif // STBI_SIMD - - -#ifdef __cplusplus -} -#endif - -// -// -//// end header file ///////////////////////////////////////////////////// -#endif // STBI_INCLUDE_STB_IMAGE_H - -#ifndef STBI_HEADER_FILE_ONLY - -#ifndef STBI_NO_HDR -#include // ldexp -#include // strcmp, strtok -#endif - -#ifndef STBI_NO_STDIO -#include -#endif -#include -#include -#include -#include -#include // ptrdiff_t on osx - -#ifndef _MSC_VER - #ifdef __cplusplus - #define stbi_inline inline - #else - #define stbi_inline - #endif -#else - #define stbi_inline __forceinline -#endif - - -#ifdef _MSC_VER -typedef unsigned char stbi__uint8; -typedef unsigned short stbi__uint16; -typedef signed short stbi__int16; -typedef unsigned int stbi__uint32; -typedef signed int stbi__int32; -#else -#include -typedef uint8_t stbi__uint8; -typedef uint16_t stbi__uint16; -typedef int16_t stbi__int16; -typedef uint32_t stbi__uint32; -typedef int32_t stbi__int32; -#endif - -// should produce compiler error if size is wrong -typedef unsigned char validate_uint32[sizeof(stbi__uint32)==4 ? 1 : -1]; - -#ifdef _MSC_VER -#define STBI_NOTUSED(v) (void)(v) -#else -#define STBI_NOTUSED(v) (void)sizeof(v) -#endif - -#ifdef _MSC_VER -#define STBI_HAS_LROTL -#endif - -#ifdef STBI_HAS_LROTL - #define stbi_lrot(x,y) _lrotl(x,y) -#else - #define stbi_lrot(x,y) (((x) << (y)) | ((x) >> (32 - (y)))) -#endif - -/////////////////////////////////////////////// -// -// stbi struct and start_xxx functions - -// stbi structure is our basic context used by all images, so it -// contains all the IO context, plus some basic image information -typedef struct -{ - stbi__uint32 img_x, img_y; - int img_n, img_out_n; - - stbi_io_callbacks io; - void *io_user_data; - - int read_from_callbacks; - int buflen; - stbi__uint8 buffer_start[128]; - - stbi__uint8 *img_buffer, *img_buffer_end; - stbi__uint8 *img_buffer_original; -} stbi; - - -static void refill_buffer(stbi *s); - -// initialize a memory-decode context -static void start_mem(stbi *s, stbi__uint8 const *buffer, int len) -{ - s->io.read = NULL; - s->read_from_callbacks = 0; - s->img_buffer = s->img_buffer_original = (stbi__uint8 *) buffer; - s->img_buffer_end = (stbi__uint8 *) buffer+len; -} - -// initialize a callback-based context -static void start_callbacks(stbi *s, stbi_io_callbacks *c, void *user) -{ - s->io = *c; - s->io_user_data = user; - s->buflen = sizeof(s->buffer_start); - s->read_from_callbacks = 1; - s->img_buffer_original = s->buffer_start; - refill_buffer(s); -} - -#ifndef STBI_NO_STDIO - -static int stdio_read(void *user, char *data, int size) -{ - return (int) fread(data,1,size,(FILE*) user); -} - -static void stdio_skip(void *user, int n) -{ - fseek((FILE*) user, n, SEEK_CUR); -} - -static int stdio_eof(void *user) -{ - return feof((FILE*) user); -} - -static stbi_io_callbacks stbi_stdio_callbacks = -{ - stdio_read, - stdio_skip, - stdio_eof, -}; - -static void start_file(stbi *s, FILE *f) -{ - start_callbacks(s, &stbi_stdio_callbacks, (void *) f); -} - -//static void stop_file(stbi *s) { } - -#endif // !STBI_NO_STDIO - -static void stbi_rewind(stbi *s) -{ - // conceptually rewind SHOULD rewind to the beginning of the stream, - // but we just rewind to the beginning of the initial buffer, because - // we only use it after doing 'test', which only ever looks at at most 92 bytes - s->img_buffer = s->img_buffer_original; -} - -static int stbi_jpeg_test(stbi *s); -static stbi_uc *stbi_jpeg_load(stbi *s, int *x, int *y, int *comp, int req_comp); -static int stbi_jpeg_info(stbi *s, int *x, int *y, int *comp); -static int stbi_png_test(stbi *s); -static stbi_uc *stbi_png_load(stbi *s, int *x, int *y, int *comp, int req_comp); -static int stbi_png_info(stbi *s, int *x, int *y, int *comp); -static int stbi_bmp_test(stbi *s); -static stbi_uc *stbi_bmp_load(stbi *s, int *x, int *y, int *comp, int req_comp); -static int stbi_tga_test(stbi *s); -static stbi_uc *stbi_tga_load(stbi *s, int *x, int *y, int *comp, int req_comp); -static int stbi_tga_info(stbi *s, int *x, int *y, int *comp); -static int stbi_psd_test(stbi *s); -static stbi_uc *stbi_psd_load(stbi *s, int *x, int *y, int *comp, int req_comp); -#ifndef STBI_NO_HDR -static int stbi_hdr_test(stbi *s); -static float *stbi_hdr_load(stbi *s, int *x, int *y, int *comp, int req_comp); -#endif -static int stbi_pic_test(stbi *s); -static stbi_uc *stbi_pic_load(stbi *s, int *x, int *y, int *comp, int req_comp); -static int stbi_gif_test(stbi *s); -static stbi_uc *stbi_gif_load(stbi *s, int *x, int *y, int *comp, int req_comp); -static int stbi_gif_info(stbi *s, int *x, int *y, int *comp); - - -// this is not threadsafe -static const char *failure_reason; - -const char *stbi_failure_reason(void) -{ - return failure_reason; -} - -static int e(const char *str) -{ - failure_reason = str; - return 0; -} - -// e - error -// epf - error returning pointer to float -// epuc - error returning pointer to unsigned char - -#ifdef STBI_NO_FAILURE_STRINGS - #define e(x,y) 0 -#elif defined(STBI_FAILURE_USERMSG) - #define e(x,y) e(y) -#else - #define e(x,y) e(x) -#endif - -#define epf(x,y) ((float *) (e(x,y)?NULL:NULL)) -#define epuc(x,y) ((unsigned char *) (e(x,y)?NULL:NULL)) - -void stbi_image_free(void *retval_from_stbi_load) -{ - free(retval_from_stbi_load); -} - -#ifndef STBI_NO_HDR -static float *ldr_to_hdr(stbi_uc *data, int x, int y, int comp); -static stbi_uc *hdr_to_ldr(float *data, int x, int y, int comp); -#endif - -static unsigned char *stbi_load_main(stbi *s, int *x, int *y, int *comp, int req_comp) -{ - if (stbi_jpeg_test(s)) return stbi_jpeg_load(s,x,y,comp,req_comp); - if (stbi_png_test(s)) return stbi_png_load(s,x,y,comp,req_comp); - if (stbi_bmp_test(s)) return stbi_bmp_load(s,x,y,comp,req_comp); - if (stbi_gif_test(s)) return stbi_gif_load(s,x,y,comp,req_comp); - if (stbi_psd_test(s)) return stbi_psd_load(s,x,y,comp,req_comp); - if (stbi_pic_test(s)) return stbi_pic_load(s,x,y,comp,req_comp); - - #ifndef STBI_NO_HDR - if (stbi_hdr_test(s)) { - float *hdr = stbi_hdr_load(s, x,y,comp,req_comp); - return hdr_to_ldr(hdr, *x, *y, req_comp ? req_comp : *comp); - } - #endif - - // test tga last because it's a crappy test! - if (stbi_tga_test(s)) - return stbi_tga_load(s,x,y,comp,req_comp); - return epuc("unknown image type", "Image not of any known type, or corrupt"); -} - -#ifndef STBI_NO_STDIO -unsigned char *stbi_load(char const *filename, int *x, int *y, int *comp, int req_comp) -{ - FILE *f = fopen(filename, "rb"); - unsigned char *result; - if (!f) return epuc("can't fopen", "Unable to open file"); - result = stbi_load_from_file(f,x,y,comp,req_comp); - fclose(f); - return result; -} - -unsigned char *stbi_load_from_file(FILE *f, int *x, int *y, int *comp, int req_comp) -{ - unsigned char *result; - stbi s; - start_file(&s,f); - result = stbi_load_main(&s,x,y,comp,req_comp); - if (result) { - // need to 'unget' all the characters in the IO buffer - fseek(f, - (int) (s.img_buffer_end - s.img_buffer), SEEK_CUR); - } - return result; -} -#endif //!STBI_NO_STDIO - -unsigned char *stbi_load_from_memory(stbi_uc const *buffer, int len, int *x, int *y, int *comp, int req_comp) -{ - stbi s; - start_mem(&s,buffer,len); - return stbi_load_main(&s,x,y,comp,req_comp); -} - -unsigned char *stbi_load_from_callbacks(stbi_io_callbacks const *clbk, void *user, int *x, int *y, int *comp, int req_comp) -{ - stbi s; - start_callbacks(&s, (stbi_io_callbacks *) clbk, user); - return stbi_load_main(&s,x,y,comp,req_comp); -} - -#ifndef STBI_NO_HDR - -float *stbi_loadf_main(stbi *s, int *x, int *y, int *comp, int req_comp) -{ - unsigned char *data; - #ifndef STBI_NO_HDR - if (stbi_hdr_test(s)) - return stbi_hdr_load(s,x,y,comp,req_comp); - #endif - data = stbi_load_main(s, x, y, comp, req_comp); - if (data) - return ldr_to_hdr(data, *x, *y, req_comp ? req_comp : *comp); - return epf("unknown image type", "Image not of any known type, or corrupt"); -} - -float *stbi_loadf_from_memory(stbi_uc const *buffer, int len, int *x, int *y, int *comp, int req_comp) -{ - stbi s; - start_mem(&s,buffer,len); - return stbi_loadf_main(&s,x,y,comp,req_comp); -} - -float *stbi_loadf_from_callbacks(stbi_io_callbacks const *clbk, void *user, int *x, int *y, int *comp, int req_comp) -{ - stbi s; - start_callbacks(&s, (stbi_io_callbacks *) clbk, user); - return stbi_loadf_main(&s,x,y,comp,req_comp); -} - -#ifndef STBI_NO_STDIO -float *stbi_loadf(char const *filename, int *x, int *y, int *comp, int req_comp) -{ - FILE *f = fopen(filename, "rb"); - float *result; - if (!f) return epf("can't fopen", "Unable to open file"); - result = stbi_loadf_from_file(f,x,y,comp,req_comp); - fclose(f); - return result; -} - -float *stbi_loadf_from_file(FILE *f, int *x, int *y, int *comp, int req_comp) -{ - stbi s; - start_file(&s,f); - return stbi_loadf_main(&s,x,y,comp,req_comp); -} -#endif // !STBI_NO_STDIO - -#endif // !STBI_NO_HDR - -// these is-hdr-or-not is defined independent of whether STBI_NO_HDR is -// defined, for API simplicity; if STBI_NO_HDR is defined, it always -// reports false! - -int stbi_is_hdr_from_memory(stbi_uc const *buffer, int len) -{ - #ifndef STBI_NO_HDR - stbi s; - start_mem(&s,buffer,len); - return stbi_hdr_test(&s); - #else - STBI_NOTUSED(buffer); - STBI_NOTUSED(len); - return 0; - #endif -} - -#ifndef STBI_NO_STDIO -extern int stbi_is_hdr (char const *filename) -{ - FILE *f = fopen(filename, "rb"); - int result=0; - if (f) { - result = stbi_is_hdr_from_file(f); - fclose(f); - } - return result; -} - -extern int stbi_is_hdr_from_file(FILE *f) -{ - #ifndef STBI_NO_HDR - stbi s; - start_file(&s,f); - return stbi_hdr_test(&s); - #else - return 0; - #endif -} -#endif // !STBI_NO_STDIO - -extern int stbi_is_hdr_from_callbacks(stbi_io_callbacks const *clbk, void *user) -{ - #ifndef STBI_NO_HDR - stbi s; - start_callbacks(&s, (stbi_io_callbacks *) clbk, user); - return stbi_hdr_test(&s); - #else - return 0; - #endif -} - -#ifndef STBI_NO_HDR -static float h2l_gamma_i=1.0f/2.2f, h2l_scale_i=1.0f; -static float l2h_gamma=2.2f, l2h_scale=1.0f; - -void stbi_hdr_to_ldr_gamma(float gamma) { h2l_gamma_i = 1/gamma; } -void stbi_hdr_to_ldr_scale(float scale) { h2l_scale_i = 1/scale; } - -void stbi_ldr_to_hdr_gamma(float gamma) { l2h_gamma = gamma; } -void stbi_ldr_to_hdr_scale(float scale) { l2h_scale = scale; } -#endif - - -////////////////////////////////////////////////////////////////////////////// -// -// Common code used by all image loaders -// - -enum -{ - SCAN_load=0, - SCAN_type, - SCAN_header -}; - -static void refill_buffer(stbi *s) -{ - int n = (s->io.read)(s->io_user_data,(char*)s->buffer_start,s->buflen); - if (n == 0) { - // at end of file, treat same as if from memory, but need to handle case - // where s->img_buffer isn't pointing to safe memory, e.g. 0-byte file - s->read_from_callbacks = 0; - s->img_buffer = s->buffer_start; - s->img_buffer_end = s->buffer_start+1; - *s->img_buffer = 0; - } else { - s->img_buffer = s->buffer_start; - s->img_buffer_end = s->buffer_start + n; - } -} - -stbi_inline static int get8(stbi *s) -{ - if (s->img_buffer < s->img_buffer_end) - return *s->img_buffer++; - if (s->read_from_callbacks) { - refill_buffer(s); - return *s->img_buffer++; - } - return 0; -} - -stbi_inline static int at_eof(stbi *s) -{ - if (s->io.read) { - if (!(s->io.eof)(s->io_user_data)) return 0; - // if feof() is true, check if buffer = end - // special case: we've only got the special 0 character at the end - if (s->read_from_callbacks == 0) return 1; - } - - return s->img_buffer >= s->img_buffer_end; -} - -stbi_inline static stbi__uint8 get8u(stbi *s) -{ - return (stbi__uint8) get8(s); -} - -static void skip(stbi *s, int n) -{ - if (s->io.read) { - int blen = (int) (s->img_buffer_end - s->img_buffer); - if (blen < n) { - s->img_buffer = s->img_buffer_end; - (s->io.skip)(s->io_user_data, n - blen); - return; - } - } - s->img_buffer += n; -} - -static int getn(stbi *s, stbi_uc *buffer, int n) -{ - if (s->io.read) { - int blen = (int) (s->img_buffer_end - s->img_buffer); - if (blen < n) { - int res, count; - - memcpy(buffer, s->img_buffer, blen); - - count = (s->io.read)(s->io_user_data, (char*) buffer + blen, n - blen); - res = (count == (n-blen)); - s->img_buffer = s->img_buffer_end; - return res; - } - } - - if (s->img_buffer+n <= s->img_buffer_end) { - memcpy(buffer, s->img_buffer, n); - s->img_buffer += n; - return 1; - } else - return 0; -} - -static int get16(stbi *s) -{ - int z = get8(s); - return (z << 8) + get8(s); -} - -static stbi__uint32 get32(stbi *s) -{ - stbi__uint32 z = get16(s); - return (z << 16) + get16(s); -} - -static int get16le(stbi *s) -{ - int z = get8(s); - return z + (get8(s) << 8); -} - -static stbi__uint32 get32le(stbi *s) -{ - stbi__uint32 z = get16le(s); - return z + (get16le(s) << 16); -} - -////////////////////////////////////////////////////////////////////////////// -// -// generic converter from built-in img_n to req_comp -// individual types do this automatically as much as possible (e.g. jpeg -// does all cases internally since it needs to colorspace convert anyway, -// and it never has alpha, so very few cases ). png can automatically -// interleave an alpha=255 channel, but falls back to this for other cases -// -// assume data buffer is malloced, so malloc a new one and free that one -// only failure mode is malloc failing - -static stbi__uint8 compute_y(int r, int g, int b) -{ - return (stbi__uint8) (((r*77) + (g*150) + (29*b)) >> 8); -} - -static unsigned char *convert_format(unsigned char *data, int img_n, int req_comp, unsigned int x, unsigned int y) -{ - int i,j; - unsigned char *good; - - if (req_comp == img_n) return data; - assert(req_comp >= 1 && req_comp <= 4); - - good = (unsigned char *) malloc(req_comp * x * y); - if (good == NULL) { - free(data); - return epuc("outofmem", "Out of memory"); - } - - for (j=0; j < (int) y; ++j) { - unsigned char *src = data + j * x * img_n ; - unsigned char *dest = good + j * x * req_comp; - - #define COMBO(a,b) ((a)*8+(b)) - #define CASE(a,b) case COMBO(a,b): for(i=x-1; i >= 0; --i, src += a, dest += b) - // convert source image with img_n components to one with req_comp components; - // avoid switch per pixel, so use switch per scanline and massive macros - switch (COMBO(img_n, req_comp)) { - CASE(1,2) dest[0]=src[0], dest[1]=255; break; - CASE(1,3) dest[0]=dest[1]=dest[2]=src[0]; break; - CASE(1,4) dest[0]=dest[1]=dest[2]=src[0], dest[3]=255; break; - CASE(2,1) dest[0]=src[0]; break; - CASE(2,3) dest[0]=dest[1]=dest[2]=src[0]; break; - CASE(2,4) dest[0]=dest[1]=dest[2]=src[0], dest[3]=src[1]; break; - CASE(3,4) dest[0]=src[0],dest[1]=src[1],dest[2]=src[2],dest[3]=255; break; - CASE(3,1) dest[0]=compute_y(src[0],src[1],src[2]); break; - CASE(3,2) dest[0]=compute_y(src[0],src[1],src[2]), dest[1] = 255; break; - CASE(4,1) dest[0]=compute_y(src[0],src[1],src[2]); break; - CASE(4,2) dest[0]=compute_y(src[0],src[1],src[2]), dest[1] = src[3]; break; - CASE(4,3) dest[0]=src[0],dest[1]=src[1],dest[2]=src[2]; break; - default: assert(0); - } - #undef CASE - } - - free(data); - return good; -} - -#ifndef STBI_NO_HDR -static float *ldr_to_hdr(stbi_uc *data, int x, int y, int comp) -{ - int i,k,n; - float *output = (float *) malloc(x * y * comp * sizeof(float)); - if (output == NULL) { free(data); return epf("outofmem", "Out of memory"); } - // compute number of non-alpha components - if (comp & 1) n = comp; else n = comp-1; - for (i=0; i < x*y; ++i) { - for (k=0; k < n; ++k) { - output[i*comp + k] = (float) pow(data[i*comp+k]/255.0f, l2h_gamma) * l2h_scale; - } - if (k < comp) output[i*comp + k] = data[i*comp+k]/255.0f; - } - free(data); - return output; -} - -#define float2int(x) ((int) (x)) -static stbi_uc *hdr_to_ldr(float *data, int x, int y, int comp) -{ - int i,k,n; - stbi_uc *output = (stbi_uc *) malloc(x * y * comp); - if (output == NULL) { free(data); return epuc("outofmem", "Out of memory"); } - // compute number of non-alpha components - if (comp & 1) n = comp; else n = comp-1; - for (i=0; i < x*y; ++i) { - for (k=0; k < n; ++k) { - float z = (float) pow(data[i*comp+k]*h2l_scale_i, h2l_gamma_i) * 255 + 0.5f; - if (z < 0) z = 0; - if (z > 255) z = 255; - output[i*comp + k] = (stbi__uint8) float2int(z); - } - if (k < comp) { - float z = data[i*comp+k] * 255 + 0.5f; - if (z < 0) z = 0; - if (z > 255) z = 255; - output[i*comp + k] = (stbi__uint8) float2int(z); - } - } - free(data); - return output; -} -#endif - -////////////////////////////////////////////////////////////////////////////// -// -// "baseline" JPEG/JFIF decoder (not actually fully baseline implementation) -// -// simple implementation -// - channel subsampling of at most 2 in each dimension -// - doesn't support delayed output of y-dimension -// - simple interface (only one output format: 8-bit interleaved RGB) -// - doesn't try to recover corrupt jpegs -// - doesn't allow partial loading, loading multiple at once -// - still fast on x86 (copying globals into locals doesn't help x86) -// - allocates lots of intermediate memory (full size of all components) -// - non-interleaved case requires this anyway -// - allows good upsampling (see next) -// high-quality -// - upsampled channels are bilinearly interpolated, even across blocks -// - quality integer IDCT derived from IJG's 'slow' -// performance -// - fast huffman; reasonable integer IDCT -// - uses a lot of intermediate memory, could cache poorly -// - load http://nothings.org/remote/anemones.jpg 3 times on 2.8Ghz P4 -// stb_jpeg: 1.34 seconds (MSVC6, default release build) -// stb_jpeg: 1.06 seconds (MSVC6, processor = Pentium Pro) -// IJL11.dll: 1.08 seconds (compiled by intel) -// IJG 1998: 0.98 seconds (MSVC6, makefile provided by IJG) -// IJG 1998: 0.95 seconds (MSVC6, makefile + proc=PPro) - -// huffman decoding acceleration -#define FAST_BITS 9 // larger handles more cases; smaller stomps less cache - -typedef struct -{ - stbi__uint8 fast[1 << FAST_BITS]; - // weirdly, repacking this into AoS is a 10% speed loss, instead of a win - stbi__uint16 code[256]; - stbi__uint8 values[256]; - stbi__uint8 size[257]; - unsigned int maxcode[18]; - int delta[17]; // old 'firstsymbol' - old 'firstcode' -} huffman; - -typedef struct -{ - #ifdef STBI_SIMD - unsigned short dequant2[4][64]; - #endif - stbi *s; - huffman huff_dc[4]; - huffman huff_ac[4]; - stbi__uint8 dequant[4][64]; - -// sizes for components, interleaved MCUs - int img_h_max, img_v_max; - int img_mcu_x, img_mcu_y; - int img_mcu_w, img_mcu_h; - -// definition of jpeg image component - struct - { - int id; - int h,v; - int tq; - int hd,ha; - int dc_pred; - - int x,y,w2,h2; - stbi__uint8 *data; - void *raw_data; - stbi__uint8 *linebuf; - } img_comp[4]; - - stbi__uint32 code_buffer; // jpeg entropy-coded buffer - int code_bits; // number of valid bits - unsigned char marker; // marker seen while filling entropy buffer - int nomore; // flag if we saw a marker so must stop - - int scan_n, order[4]; - int restart_interval, todo; -} jpeg; - -static int build_huffman(huffman *h, int *count) -{ - int i,j,k=0,code; - // build size list for each symbol (from JPEG spec) - for (i=0; i < 16; ++i) - for (j=0; j < count[i]; ++j) - h->size[k++] = (stbi__uint8) (i+1); - h->size[k] = 0; - - // compute actual symbols (from jpeg spec) - code = 0; - k = 0; - for(j=1; j <= 16; ++j) { - // compute delta to add to code to compute symbol id - h->delta[j] = k - code; - if (h->size[k] == j) { - while (h->size[k] == j) - h->code[k++] = (stbi__uint16) (code++); - if (code-1 >= (1 << j)) return e("bad code lengths","Corrupt JPEG"); - } - // compute largest code + 1 for this size, preshifted as needed later - h->maxcode[j] = code << (16-j); - code <<= 1; - } - h->maxcode[j] = 0xffffffff; - - // build non-spec acceleration table; 255 is flag for not-accelerated - memset(h->fast, 255, 1 << FAST_BITS); - for (i=0; i < k; ++i) { - int s = h->size[i]; - if (s <= FAST_BITS) { - int c = h->code[i] << (FAST_BITS-s); - int m = 1 << (FAST_BITS-s); - for (j=0; j < m; ++j) { - h->fast[c+j] = (stbi__uint8) i; - } - } - } - return 1; -} - -static void grow_buffer_unsafe(jpeg *j) -{ - do { - int b = j->nomore ? 0 : get8(j->s); - if (b == 0xff) { - int c = get8(j->s); - if (c != 0) { - j->marker = (unsigned char) c; - j->nomore = 1; - return; - } - } - j->code_buffer |= b << (24 - j->code_bits); - j->code_bits += 8; - } while (j->code_bits <= 24); -} - -// (1 << n) - 1 -static stbi__uint32 bmask[17]={0,1,3,7,15,31,63,127,255,511,1023,2047,4095,8191,16383,32767,65535}; - -// decode a jpeg huffman value from the bitstream -stbi_inline static int decode(jpeg *j, huffman *h) -{ - unsigned int temp; - int c,k; - - if (j->code_bits < 16) grow_buffer_unsafe(j); - - // look at the top FAST_BITS and determine what symbol ID it is, - // if the code is <= FAST_BITS - c = (j->code_buffer >> (32 - FAST_BITS)) & ((1 << FAST_BITS)-1); - k = h->fast[c]; - if (k < 255) { - int s = h->size[k]; - if (s > j->code_bits) - return -1; - j->code_buffer <<= s; - j->code_bits -= s; - return h->values[k]; - } - - // naive test is to shift the code_buffer down so k bits are - // valid, then test against maxcode. To speed this up, we've - // preshifted maxcode left so that it has (16-k) 0s at the - // end; in other words, regardless of the number of bits, it - // wants to be compared against something shifted to have 16; - // that way we don't need to shift inside the loop. - temp = j->code_buffer >> 16; - for (k=FAST_BITS+1 ; ; ++k) - if (temp < h->maxcode[k]) - break; - if (k == 17) { - // error! code not found - j->code_bits -= 16; - return -1; - } - - if (k > j->code_bits) - return -1; - - // convert the huffman code to the symbol id - c = ((j->code_buffer >> (32 - k)) & bmask[k]) + h->delta[k]; - assert((((j->code_buffer) >> (32 - h->size[c])) & bmask[h->size[c]]) == h->code[c]); - - // convert the id to a symbol - j->code_bits -= k; - j->code_buffer <<= k; - return h->values[c]; -} - -// combined JPEG 'receive' and JPEG 'extend', since baseline -// always extends everything it receives. -stbi_inline static int extend_receive(jpeg *j, int n) -{ - unsigned int m = 1 << (n-1); - unsigned int k; - if (j->code_bits < n) grow_buffer_unsafe(j); - - #if 1 - k = stbi_lrot(j->code_buffer, n); - j->code_buffer = k & ~bmask[n]; - k &= bmask[n]; - j->code_bits -= n; - #else - k = (j->code_buffer >> (32 - n)) & bmask[n]; - j->code_bits -= n; - j->code_buffer <<= n; - #endif - // the following test is probably a random branch that won't - // predict well. I tried to table accelerate it but failed. - // maybe it's compiling as a conditional move? - if (k < m) - return (-1 << n) + k + 1; - else - return k; -} - -// given a value that's at position X in the zigzag stream, -// where does it appear in the 8x8 matrix coded as row-major? -static stbi__uint8 dezigzag[64+15] = -{ - 0, 1, 8, 16, 9, 2, 3, 10, - 17, 24, 32, 25, 18, 11, 4, 5, - 12, 19, 26, 33, 40, 48, 41, 34, - 27, 20, 13, 6, 7, 14, 21, 28, - 35, 42, 49, 56, 57, 50, 43, 36, - 29, 22, 15, 23, 30, 37, 44, 51, - 58, 59, 52, 45, 38, 31, 39, 46, - 53, 60, 61, 54, 47, 55, 62, 63, - // let corrupt input sample past end - 63, 63, 63, 63, 63, 63, 63, 63, - 63, 63, 63, 63, 63, 63, 63 -}; - -// decode one 64-entry block-- -static int decode_block(jpeg *j, short data[64], huffman *hdc, huffman *hac, int b) -{ - int diff,dc,k; - int t = decode(j, hdc); - if (t < 0) return e("bad huffman code","Corrupt JPEG"); - - // 0 all the ac values now so we can do it 32-bits at a time - memset(data,0,64*sizeof(data[0])); - - diff = t ? extend_receive(j, t) : 0; - dc = j->img_comp[b].dc_pred + diff; - j->img_comp[b].dc_pred = dc; - data[0] = (short) dc; - - // decode AC components, see JPEG spec - k = 1; - do { - int r,s; - int rs = decode(j, hac); - if (rs < 0) return e("bad huffman code","Corrupt JPEG"); - s = rs & 15; - r = rs >> 4; - if (s == 0) { - if (rs != 0xf0) break; // end block - k += 16; - } else { - k += r; - // decode into unzigzag'd location - data[dezigzag[k++]] = (short) extend_receive(j,s); - } - } while (k < 64); - return 1; -} - -// take a -128..127 value and clamp it and convert to 0..255 -stbi_inline static stbi__uint8 clamp(int x) -{ - // trick to use a single test to catch both cases - if ((unsigned int) x > 255) { - if (x < 0) return 0; - if (x > 255) return 255; - } - return (stbi__uint8) x; -} - -#define f2f(x) (int) (((x) * 4096 + 0.5)) -#define fsh(x) ((x) << 12) - -// derived from jidctint -- DCT_ISLOW -#define IDCT_1D(s0,s1,s2,s3,s4,s5,s6,s7) \ - int t0,t1,t2,t3,p1,p2,p3,p4,p5,x0,x1,x2,x3; \ - p2 = s2; \ - p3 = s6; \ - p1 = (p2+p3) * f2f(0.5411961f); \ - t2 = p1 + p3*f2f(-1.847759065f); \ - t3 = p1 + p2*f2f( 0.765366865f); \ - p2 = s0; \ - p3 = s4; \ - t0 = fsh(p2+p3); \ - t1 = fsh(p2-p3); \ - x0 = t0+t3; \ - x3 = t0-t3; \ - x1 = t1+t2; \ - x2 = t1-t2; \ - t0 = s7; \ - t1 = s5; \ - t2 = s3; \ - t3 = s1; \ - p3 = t0+t2; \ - p4 = t1+t3; \ - p1 = t0+t3; \ - p2 = t1+t2; \ - p5 = (p3+p4)*f2f( 1.175875602f); \ - t0 = t0*f2f( 0.298631336f); \ - t1 = t1*f2f( 2.053119869f); \ - t2 = t2*f2f( 3.072711026f); \ - t3 = t3*f2f( 1.501321110f); \ - p1 = p5 + p1*f2f(-0.899976223f); \ - p2 = p5 + p2*f2f(-2.562915447f); \ - p3 = p3*f2f(-1.961570560f); \ - p4 = p4*f2f(-0.390180644f); \ - t3 += p1+p4; \ - t2 += p2+p3; \ - t1 += p2+p4; \ - t0 += p1+p3; - -#ifdef STBI_SIMD -typedef unsigned short stbi_dequantize_t; -#else -typedef stbi__uint8 stbi_dequantize_t; -#endif - -// .344 seconds on 3*anemones.jpg -static void idct_block(stbi__uint8 *out, int out_stride, short data[64], stbi_dequantize_t *dequantize) -{ - int i,val[64],*v=val; - stbi_dequantize_t *dq = dequantize; - stbi__uint8 *o; - short *d = data; - - // columns - for (i=0; i < 8; ++i,++d,++dq, ++v) { - // if all zeroes, shortcut -- this avoids dequantizing 0s and IDCTing - if (d[ 8]==0 && d[16]==0 && d[24]==0 && d[32]==0 - && d[40]==0 && d[48]==0 && d[56]==0) { - // no shortcut 0 seconds - // (1|2|3|4|5|6|7)==0 0 seconds - // all separate -0.047 seconds - // 1 && 2|3 && 4|5 && 6|7: -0.047 seconds - int dcterm = d[0] * dq[0] << 2; - v[0] = v[8] = v[16] = v[24] = v[32] = v[40] = v[48] = v[56] = dcterm; - } else { - IDCT_1D(d[ 0]*dq[ 0],d[ 8]*dq[ 8],d[16]*dq[16],d[24]*dq[24], - d[32]*dq[32],d[40]*dq[40],d[48]*dq[48],d[56]*dq[56]) - // constants scaled things up by 1<<12; let's bring them back - // down, but keep 2 extra bits of precision - x0 += 512; x1 += 512; x2 += 512; x3 += 512; - v[ 0] = (x0+t3) >> 10; - v[56] = (x0-t3) >> 10; - v[ 8] = (x1+t2) >> 10; - v[48] = (x1-t2) >> 10; - v[16] = (x2+t1) >> 10; - v[40] = (x2-t1) >> 10; - v[24] = (x3+t0) >> 10; - v[32] = (x3-t0) >> 10; - } - } - - for (i=0, v=val, o=out; i < 8; ++i,v+=8,o+=out_stride) { - // no fast case since the first 1D IDCT spread components out - IDCT_1D(v[0],v[1],v[2],v[3],v[4],v[5],v[6],v[7]) - // constants scaled things up by 1<<12, plus we had 1<<2 from first - // loop, plus horizontal and vertical each scale by sqrt(8) so together - // we've got an extra 1<<3, so 1<<17 total we need to remove. - // so we want to round that, which means adding 0.5 * 1<<17, - // aka 65536. Also, we'll end up with -128 to 127 that we want - // to encode as 0..255 by adding 128, so we'll add that before the shift - x0 += 65536 + (128<<17); - x1 += 65536 + (128<<17); - x2 += 65536 + (128<<17); - x3 += 65536 + (128<<17); - // tried computing the shifts into temps, or'ing the temps to see - // if any were out of range, but that was slower - o[0] = clamp((x0+t3) >> 17); - o[7] = clamp((x0-t3) >> 17); - o[1] = clamp((x1+t2) >> 17); - o[6] = clamp((x1-t2) >> 17); - o[2] = clamp((x2+t1) >> 17); - o[5] = clamp((x2-t1) >> 17); - o[3] = clamp((x3+t0) >> 17); - o[4] = clamp((x3-t0) >> 17); - } -} - -#ifdef STBI_SIMD -static stbi_idct_8x8 stbi_idct_installed = idct_block; - -void stbi_install_idct(stbi_idct_8x8 func) -{ - stbi_idct_installed = func; -} -#endif - -#define MARKER_none 0xff -// if there's a pending marker from the entropy stream, return that -// otherwise, fetch from the stream and get a marker. if there's no -// marker, return 0xff, which is never a valid marker value -static stbi__uint8 get_marker(jpeg *j) -{ - stbi__uint8 x; - if (j->marker != MARKER_none) { x = j->marker; j->marker = MARKER_none; return x; } - x = get8u(j->s); - if (x != 0xff) return MARKER_none; - while (x == 0xff) - x = get8u(j->s); - return x; -} - -// in each scan, we'll have scan_n components, and the order -// of the components is specified by order[] -#define RESTART(x) ((x) >= 0xd0 && (x) <= 0xd7) - -// after a restart interval, reset the entropy decoder and -// the dc prediction -static void reset(jpeg *j) -{ - j->code_bits = 0; - j->code_buffer = 0; - j->nomore = 0; - j->img_comp[0].dc_pred = j->img_comp[1].dc_pred = j->img_comp[2].dc_pred = 0; - j->marker = MARKER_none; - j->todo = j->restart_interval ? j->restart_interval : 0x7fffffff; - // no more than 1<<31 MCUs if no restart_interal? that's plenty safe, - // since we don't even allow 1<<30 pixels -} - -static int parse_entropy_coded_data(jpeg *z) -{ - reset(z); - if (z->scan_n == 1) { - int i,j; - #ifdef STBI_SIMD - __declspec(align(16)) - #endif - short data[64]; - int n = z->order[0]; - // non-interleaved data, we just need to process one block at a time, - // in trivial scanline order - // number of blocks to do just depends on how many actual "pixels" this - // component has, independent of interleaved MCU blocking and such - int w = (z->img_comp[n].x+7) >> 3; - int h = (z->img_comp[n].y+7) >> 3; - for (j=0; j < h; ++j) { - for (i=0; i < w; ++i) { - if (!decode_block(z, data, z->huff_dc+z->img_comp[n].hd, z->huff_ac+z->img_comp[n].ha, n)) return 0; - #ifdef STBI_SIMD - stbi_idct_installed(z->img_comp[n].data+z->img_comp[n].w2*j*8+i*8, z->img_comp[n].w2, data, z->dequant2[z->img_comp[n].tq]); - #else - idct_block(z->img_comp[n].data+z->img_comp[n].w2*j*8+i*8, z->img_comp[n].w2, data, z->dequant[z->img_comp[n].tq]); - #endif - // every data block is an MCU, so countdown the restart interval - if (--z->todo <= 0) { - if (z->code_bits < 24) grow_buffer_unsafe(z); - // if it's NOT a restart, then just bail, so we get corrupt data - // rather than no data - if (!RESTART(z->marker)) return 1; - reset(z); - } - } - } - } else { // interleaved! - int i,j,k,x,y; - short data[64]; - for (j=0; j < z->img_mcu_y; ++j) { - for (i=0; i < z->img_mcu_x; ++i) { - // scan an interleaved mcu... process scan_n components in order - for (k=0; k < z->scan_n; ++k) { - int n = z->order[k]; - // scan out an mcu's worth of this component; that's just determined - // by the basic H and V specified for the component - for (y=0; y < z->img_comp[n].v; ++y) { - for (x=0; x < z->img_comp[n].h; ++x) { - int x2 = (i*z->img_comp[n].h + x)*8; - int y2 = (j*z->img_comp[n].v + y)*8; - if (!decode_block(z, data, z->huff_dc+z->img_comp[n].hd, z->huff_ac+z->img_comp[n].ha, n)) return 0; - #ifdef STBI_SIMD - stbi_idct_installed(z->img_comp[n].data+z->img_comp[n].w2*y2+x2, z->img_comp[n].w2, data, z->dequant2[z->img_comp[n].tq]); - #else - idct_block(z->img_comp[n].data+z->img_comp[n].w2*y2+x2, z->img_comp[n].w2, data, z->dequant[z->img_comp[n].tq]); - #endif - } - } - } - // after all interleaved components, that's an interleaved MCU, - // so now count down the restart interval - if (--z->todo <= 0) { - if (z->code_bits < 24) grow_buffer_unsafe(z); - // if it's NOT a restart, then just bail, so we get corrupt data - // rather than no data - if (!RESTART(z->marker)) return 1; - reset(z); - } - } - } - } - return 1; -} - -static int process_marker(jpeg *z, int m) -{ - int L; - switch (m) { - case MARKER_none: // no marker found - return e("expected marker","Corrupt JPEG"); - - case 0xC2: // SOF - progressive - return e("progressive jpeg","JPEG format not supported (progressive)"); - - case 0xDD: // DRI - specify restart interval - if (get16(z->s) != 4) return e("bad DRI len","Corrupt JPEG"); - z->restart_interval = get16(z->s); - return 1; - - case 0xDB: // DQT - define quantization table - L = get16(z->s)-2; - while (L > 0) { - int q = get8(z->s); - int p = q >> 4; - int t = q & 15,i; - if (p != 0) return e("bad DQT type","Corrupt JPEG"); - if (t > 3) return e("bad DQT table","Corrupt JPEG"); - for (i=0; i < 64; ++i) - z->dequant[t][dezigzag[i]] = get8u(z->s); - #ifdef STBI_SIMD - for (i=0; i < 64; ++i) - z->dequant2[t][i] = z->dequant[t][i]; - #endif - L -= 65; - } - return L==0; - - case 0xC4: // DHT - define huffman table - L = get16(z->s)-2; - while (L > 0) { - stbi__uint8 *v; - int sizes[16],i,n=0; - int q = get8(z->s); - int tc = q >> 4; - int th = q & 15; - if (tc > 1 || th > 3) return e("bad DHT header","Corrupt JPEG"); - for (i=0; i < 16; ++i) { - sizes[i] = get8(z->s); - n += sizes[i]; - } - L -= 17; - if (tc == 0) { - if (!build_huffman(z->huff_dc+th, sizes)) return 0; - v = z->huff_dc[th].values; - } else { - if (!build_huffman(z->huff_ac+th, sizes)) return 0; - v = z->huff_ac[th].values; - } - for (i=0; i < n; ++i) - v[i] = get8u(z->s); - L -= n; - } - return L==0; - } - // check for comment block or APP blocks - if ((m >= 0xE0 && m <= 0xEF) || m == 0xFE) { - skip(z->s, get16(z->s)-2); - return 1; - } - return 0; -} - -// after we see SOS -static int process_scan_header(jpeg *z) -{ - int i; - int Ls = get16(z->s); - z->scan_n = get8(z->s); - if (z->scan_n < 1 || z->scan_n > 4 || z->scan_n > (int) z->s->img_n) return e("bad SOS component count","Corrupt JPEG"); - if (Ls != 6+2*z->scan_n) return e("bad SOS len","Corrupt JPEG"); - for (i=0; i < z->scan_n; ++i) { - int id = get8(z->s), which; - int q = get8(z->s); - for (which = 0; which < z->s->img_n; ++which) - if (z->img_comp[which].id == id) - break; - if (which == z->s->img_n) return 0; - z->img_comp[which].hd = q >> 4; if (z->img_comp[which].hd > 3) return e("bad DC huff","Corrupt JPEG"); - z->img_comp[which].ha = q & 15; if (z->img_comp[which].ha > 3) return e("bad AC huff","Corrupt JPEG"); - z->order[i] = which; - } - if (get8(z->s) != 0) return e("bad SOS","Corrupt JPEG"); - get8(z->s); // should be 63, but might be 0 - if (get8(z->s) != 0) return e("bad SOS","Corrupt JPEG"); - - return 1; -} - -static int process_frame_header(jpeg *z, int scan) -{ - stbi *s = z->s; - int Lf,p,i,q, h_max=1,v_max=1,c; - Lf = get16(s); if (Lf < 11) return e("bad SOF len","Corrupt JPEG"); // JPEG - p = get8(s); if (p != 8) return e("only 8-bit","JPEG format not supported: 8-bit only"); // JPEG baseline - s->img_y = get16(s); if (s->img_y == 0) return e("no header height", "JPEG format not supported: delayed height"); // Legal, but we don't handle it--but neither does IJG - s->img_x = get16(s); if (s->img_x == 0) return e("0 width","Corrupt JPEG"); // JPEG requires - c = get8(s); - if (c != 3 && c != 1) return e("bad component count","Corrupt JPEG"); // JFIF requires - s->img_n = c; - for (i=0; i < c; ++i) { - z->img_comp[i].data = NULL; - z->img_comp[i].linebuf = NULL; - } - - if (Lf != 8+3*s->img_n) return e("bad SOF len","Corrupt JPEG"); - - for (i=0; i < s->img_n; ++i) { - z->img_comp[i].id = get8(s); - if (z->img_comp[i].id != i+1) // JFIF requires - if (z->img_comp[i].id != i) // some version of jpegtran outputs non-JFIF-compliant files! - return e("bad component ID","Corrupt JPEG"); - q = get8(s); - z->img_comp[i].h = (q >> 4); if (!z->img_comp[i].h || z->img_comp[i].h > 4) return e("bad H","Corrupt JPEG"); - z->img_comp[i].v = q & 15; if (!z->img_comp[i].v || z->img_comp[i].v > 4) return e("bad V","Corrupt JPEG"); - z->img_comp[i].tq = get8(s); if (z->img_comp[i].tq > 3) return e("bad TQ","Corrupt JPEG"); - } - - if (scan != SCAN_load) return 1; - - if ((1 << 30) / s->img_x / s->img_n < s->img_y) return e("too large", "Image too large to decode"); - - for (i=0; i < s->img_n; ++i) { - if (z->img_comp[i].h > h_max) h_max = z->img_comp[i].h; - if (z->img_comp[i].v > v_max) v_max = z->img_comp[i].v; - } - - // compute interleaved mcu info - z->img_h_max = h_max; - z->img_v_max = v_max; - z->img_mcu_w = h_max * 8; - z->img_mcu_h = v_max * 8; - z->img_mcu_x = (s->img_x + z->img_mcu_w-1) / z->img_mcu_w; - z->img_mcu_y = (s->img_y + z->img_mcu_h-1) / z->img_mcu_h; - - for (i=0; i < s->img_n; ++i) { - // number of effective pixels (e.g. for non-interleaved MCU) - z->img_comp[i].x = (s->img_x * z->img_comp[i].h + h_max-1) / h_max; - z->img_comp[i].y = (s->img_y * z->img_comp[i].v + v_max-1) / v_max; - // to simplify generation, we'll allocate enough memory to decode - // the bogus oversized data from using interleaved MCUs and their - // big blocks (e.g. a 16x16 iMCU on an image of width 33); we won't - // discard the extra data until colorspace conversion - z->img_comp[i].w2 = z->img_mcu_x * z->img_comp[i].h * 8; - z->img_comp[i].h2 = z->img_mcu_y * z->img_comp[i].v * 8; - z->img_comp[i].raw_data = malloc(z->img_comp[i].w2 * z->img_comp[i].h2+15); - if (z->img_comp[i].raw_data == NULL) { - for(--i; i >= 0; --i) { - free(z->img_comp[i].raw_data); - z->img_comp[i].data = NULL; - } - return e("outofmem", "Out of memory"); - } - // align blocks for installable-idct using mmx/sse - z->img_comp[i].data = (stbi__uint8*) (((size_t) z->img_comp[i].raw_data + 15) & ~15); - z->img_comp[i].linebuf = NULL; - } - - return 1; -} - -// use comparisons since in some cases we handle more than one case (e.g. SOF) -#define DNL(x) ((x) == 0xdc) -#define SOI(x) ((x) == 0xd8) -#define EOI(x) ((x) == 0xd9) -#define SOF(x) ((x) == 0xc0 || (x) == 0xc1) -#define SOS(x) ((x) == 0xda) - -static int decode_jpeg_header(jpeg *z, int scan) -{ - int m; - z->marker = MARKER_none; // initialize cached marker to empty - m = get_marker(z); - if (!SOI(m)) return e("no SOI","Corrupt JPEG"); - if (scan == SCAN_type) return 1; - m = get_marker(z); - while (!SOF(m)) { - if (!process_marker(z,m)) return 0; - m = get_marker(z); - while (m == MARKER_none) { - // some files have extra padding after their blocks, so ok, we'll scan - if (at_eof(z->s)) return e("no SOF", "Corrupt JPEG"); - m = get_marker(z); - } - } - if (!process_frame_header(z, scan)) return 0; - return 1; -} - -static int decode_jpeg_image(jpeg *j) -{ - int m; - j->restart_interval = 0; - if (!decode_jpeg_header(j, SCAN_load)) return 0; - m = get_marker(j); - while (!EOI(m)) { - if (SOS(m)) { - if (!process_scan_header(j)) return 0; - if (!parse_entropy_coded_data(j)) return 0; - if (j->marker == MARKER_none ) { - // handle 0s at the end of image data from IP Kamera 9060 - while (!at_eof(j->s)) { - int x = get8(j->s); - if (x == 255) { - j->marker = get8u(j->s); - break; - } else if (x != 0) { - return 0; - } - } - // if we reach eof without hitting a marker, get_marker() below will fail and we'll eventually return 0 - } - } else { - if (!process_marker(j, m)) return 0; - } - m = get_marker(j); - } - return 1; -} - -// static jfif-centered resampling (across block boundaries) - -typedef stbi__uint8 *(*resample_row_func)(stbi__uint8 *out, stbi__uint8 *in0, stbi__uint8 *in1, - int w, int hs); - -#define div4(x) ((stbi__uint8) ((x) >> 2)) - -static stbi__uint8 *resample_row_1(stbi__uint8 *out, stbi__uint8 *in_near, stbi__uint8 *in_far, int w, int hs) -{ - STBI_NOTUSED(out); - STBI_NOTUSED(in_far); - STBI_NOTUSED(w); - STBI_NOTUSED(hs); - return in_near; -} - -static stbi__uint8* resample_row_v_2(stbi__uint8 *out, stbi__uint8 *in_near, stbi__uint8 *in_far, int w, int hs) -{ - // need to generate two samples vertically for every one in input - int i; - STBI_NOTUSED(hs); - for (i=0; i < w; ++i) - out[i] = div4(3*in_near[i] + in_far[i] + 2); - return out; -} - -static stbi__uint8* resample_row_h_2(stbi__uint8 *out, stbi__uint8 *in_near, stbi__uint8 *in_far, int w, int hs) -{ - // need to generate two samples horizontally for every one in input - int i; - stbi__uint8 *input = in_near; - - if (w == 1) { - // if only one sample, can't do any interpolation - out[0] = out[1] = input[0]; - return out; - } - - out[0] = input[0]; - out[1] = div4(input[0]*3 + input[1] + 2); - for (i=1; i < w-1; ++i) { - int n = 3*input[i]+2; - out[i*2+0] = div4(n+input[i-1]); - out[i*2+1] = div4(n+input[i+1]); - } - out[i*2+0] = div4(input[w-2]*3 + input[w-1] + 2); - out[i*2+1] = input[w-1]; - - STBI_NOTUSED(in_far); - STBI_NOTUSED(hs); - - return out; -} - -#define div16(x) ((stbi__uint8) ((x) >> 4)) - -static stbi__uint8 *resample_row_hv_2(stbi__uint8 *out, stbi__uint8 *in_near, stbi__uint8 *in_far, int w, int hs) -{ - // need to generate 2x2 samples for every one in input - int i,t0,t1; - if (w == 1) { - out[0] = out[1] = div4(3*in_near[0] + in_far[0] + 2); - return out; - } - - t1 = 3*in_near[0] + in_far[0]; - out[0] = div4(t1+2); - for (i=1; i < w; ++i) { - t0 = t1; - t1 = 3*in_near[i]+in_far[i]; - out[i*2-1] = div16(3*t0 + t1 + 8); - out[i*2 ] = div16(3*t1 + t0 + 8); - } - out[w*2-1] = div4(t1+2); - - STBI_NOTUSED(hs); - - return out; -} - -static stbi__uint8 *resample_row_generic(stbi__uint8 *out, stbi__uint8 *in_near, stbi__uint8 *in_far, int w, int hs) -{ - // resample with nearest-neighbor - int i,j; - STBI_NOTUSED(in_far); - for (i=0; i < w; ++i) - for (j=0; j < hs; ++j) - out[i*hs+j] = in_near[i]; - return out; -} - -#define float2fixed(x) ((int) ((x) * 65536 + 0.5)) - -// 0.38 seconds on 3*anemones.jpg (0.25 with processor = Pro) -// VC6 without processor=Pro is generating multiple LEAs per multiply! -static void YCbCr_to_RGB_row(stbi__uint8 *out, const stbi__uint8 *y, const stbi__uint8 *pcb, const stbi__uint8 *pcr, int count, int step) -{ - int i; - for (i=0; i < count; ++i) { - int y_fixed = (y[i] << 16) + 32768; // rounding - int r,g,b; - int cr = pcr[i] - 128; - int cb = pcb[i] - 128; - r = y_fixed + cr*float2fixed(1.40200f); - g = y_fixed - cr*float2fixed(0.71414f) - cb*float2fixed(0.34414f); - b = y_fixed + cb*float2fixed(1.77200f); - r >>= 16; - g >>= 16; - b >>= 16; - if ((unsigned) r > 255) { if (r < 0) r = 0; else r = 255; } - if ((unsigned) g > 255) { if (g < 0) g = 0; else g = 255; } - if ((unsigned) b > 255) { if (b < 0) b = 0; else b = 255; } - out[0] = (stbi__uint8)r; - out[1] = (stbi__uint8)g; - out[2] = (stbi__uint8)b; - out[3] = 255; - out += step; - } -} - -#ifdef STBI_SIMD -static stbi_YCbCr_to_RGB_run stbi_YCbCr_installed = YCbCr_to_RGB_row; - -void stbi_install_YCbCr_to_RGB(stbi_YCbCr_to_RGB_run func) -{ - stbi_YCbCr_installed = func; -} -#endif - - -// clean up the temporary component buffers -static void cleanup_jpeg(jpeg *j) -{ - int i; - for (i=0; i < j->s->img_n; ++i) { - if (j->img_comp[i].data) { - free(j->img_comp[i].raw_data); - j->img_comp[i].data = NULL; - } - if (j->img_comp[i].linebuf) { - free(j->img_comp[i].linebuf); - j->img_comp[i].linebuf = NULL; - } - } -} - -typedef struct -{ - resample_row_func resample; - stbi__uint8 *line0,*line1; - int hs,vs; // expansion factor in each axis - int w_lores; // horizontal pixels pre-expansion - int ystep; // how far through vertical expansion we are - int ypos; // which pre-expansion row we're on -} stbi_resample; - -static stbi__uint8 *load_jpeg_image(jpeg *z, int *out_x, int *out_y, int *comp, int req_comp) -{ - int n, decode_n; - // validate req_comp - if (req_comp < 0 || req_comp > 4) return epuc("bad req_comp", "Internal error"); - z->s->img_n = 0; - - // load a jpeg image from whichever source - if (!decode_jpeg_image(z)) { cleanup_jpeg(z); return NULL; } - - // determine actual number of components to generate - n = req_comp ? req_comp : z->s->img_n; - - if (z->s->img_n == 3 && n < 3) - decode_n = 1; - else - decode_n = z->s->img_n; - - // resample and color-convert - { - int k; - unsigned int i,j; - stbi__uint8 *output; - stbi__uint8 *coutput[4]; - - stbi_resample res_comp[4]; - - for (k=0; k < decode_n; ++k) { - stbi_resample *r = &res_comp[k]; - - // allocate line buffer big enough for upsampling off the edges - // with upsample factor of 4 - z->img_comp[k].linebuf = (stbi__uint8 *) malloc(z->s->img_x + 3); - if (!z->img_comp[k].linebuf) { cleanup_jpeg(z); return epuc("outofmem", "Out of memory"); } - - r->hs = z->img_h_max / z->img_comp[k].h; - r->vs = z->img_v_max / z->img_comp[k].v; - r->ystep = r->vs >> 1; - r->w_lores = (z->s->img_x + r->hs-1) / r->hs; - r->ypos = 0; - r->line0 = r->line1 = z->img_comp[k].data; - - if (r->hs == 1 && r->vs == 1) r->resample = resample_row_1; - else if (r->hs == 1 && r->vs == 2) r->resample = resample_row_v_2; - else if (r->hs == 2 && r->vs == 1) r->resample = resample_row_h_2; - else if (r->hs == 2 && r->vs == 2) r->resample = resample_row_hv_2; - else r->resample = resample_row_generic; - } - - // can't error after this so, this is safe - output = (stbi__uint8 *) malloc(n * z->s->img_x * z->s->img_y + 1); - if (!output) { cleanup_jpeg(z); return epuc("outofmem", "Out of memory"); } - - // now go ahead and resample - for (j=0; j < z->s->img_y; ++j) { - stbi__uint8 *out = output + n * z->s->img_x * j; - for (k=0; k < decode_n; ++k) { - stbi_resample *r = &res_comp[k]; - int y_bot = r->ystep >= (r->vs >> 1); - coutput[k] = r->resample(z->img_comp[k].linebuf, - y_bot ? r->line1 : r->line0, - y_bot ? r->line0 : r->line1, - r->w_lores, r->hs); - if (++r->ystep >= r->vs) { - r->ystep = 0; - r->line0 = r->line1; - if (++r->ypos < z->img_comp[k].y) - r->line1 += z->img_comp[k].w2; - } - } - if (n >= 3) { - stbi__uint8 *y = coutput[0]; - if (z->s->img_n == 3) { - #ifdef STBI_SIMD - stbi_YCbCr_installed(out, y, coutput[1], coutput[2], z->s->img_x, n); - #else - YCbCr_to_RGB_row(out, y, coutput[1], coutput[2], z->s->img_x, n); - #endif - } else - for (i=0; i < z->s->img_x; ++i) { - out[0] = out[1] = out[2] = y[i]; - out[3] = 255; // not used if n==3 - out += n; - } - } else { - stbi__uint8 *y = coutput[0]; - if (n == 1) - for (i=0; i < z->s->img_x; ++i) out[i] = y[i]; - else - for (i=0; i < z->s->img_x; ++i) *out++ = y[i], *out++ = 255; - } - } - cleanup_jpeg(z); - *out_x = z->s->img_x; - *out_y = z->s->img_y; - if (comp) *comp = z->s->img_n; // report original components, not output - return output; - } -} - -static unsigned char *stbi_jpeg_load(stbi *s, int *x, int *y, int *comp, int req_comp) -{ - jpeg j; - j.s = s; - return load_jpeg_image(&j, x,y,comp,req_comp); -} - -static int stbi_jpeg_test(stbi *s) -{ - int r; - jpeg j; - j.s = s; - r = decode_jpeg_header(&j, SCAN_type); - stbi_rewind(s); - return r; -} - -static int stbi_jpeg_info_raw(jpeg *j, int *x, int *y, int *comp) -{ - if (!decode_jpeg_header(j, SCAN_header)) { - stbi_rewind( j->s ); - return 0; - } - if (x) *x = j->s->img_x; - if (y) *y = j->s->img_y; - if (comp) *comp = j->s->img_n; - return 1; -} - -static int stbi_jpeg_info(stbi *s, int *x, int *y, int *comp) -{ - jpeg j; - j.s = s; - return stbi_jpeg_info_raw(&j, x, y, comp); -} - -// public domain zlib decode v0.2 Sean Barrett 2006-11-18 -// simple implementation -// - all input must be provided in an upfront buffer -// - all output is written to a single output buffer (can malloc/realloc) -// performance -// - fast huffman - -// fast-way is faster to check than jpeg huffman, but slow way is slower -#define ZFAST_BITS 9 // accelerate all cases in default tables -#define ZFAST_MASK ((1 << ZFAST_BITS) - 1) - -// zlib-style huffman encoding -// (jpegs packs from left, zlib from right, so can't share code) -typedef struct -{ - stbi__uint16 fast[1 << ZFAST_BITS]; - stbi__uint16 firstcode[16]; - int maxcode[17]; - stbi__uint16 firstsymbol[16]; - stbi__uint8 size[288]; - stbi__uint16 value[288]; -} zhuffman; - -stbi_inline static int bitreverse16(int n) -{ - n = ((n & 0xAAAA) >> 1) | ((n & 0x5555) << 1); - n = ((n & 0xCCCC) >> 2) | ((n & 0x3333) << 2); - n = ((n & 0xF0F0) >> 4) | ((n & 0x0F0F) << 4); - n = ((n & 0xFF00) >> 8) | ((n & 0x00FF) << 8); - return n; -} - -stbi_inline static int bit_reverse(int v, int bits) -{ - assert(bits <= 16); - // to bit reverse n bits, reverse 16 and shift - // e.g. 11 bits, bit reverse and shift away 5 - return bitreverse16(v) >> (16-bits); -} - -static int zbuild_huffman(zhuffman *z, stbi__uint8 *sizelist, int num) -{ - int i,k=0; - int code, next_code[16], sizes[17]; - - // DEFLATE spec for generating codes - memset(sizes, 0, sizeof(sizes)); - memset(z->fast, 255, sizeof(z->fast)); - for (i=0; i < num; ++i) - ++sizes[sizelist[i]]; - sizes[0] = 0; - for (i=1; i < 16; ++i) - assert(sizes[i] <= (1 << i)); - code = 0; - for (i=1; i < 16; ++i) { - next_code[i] = code; - z->firstcode[i] = (stbi__uint16) code; - z->firstsymbol[i] = (stbi__uint16) k; - code = (code + sizes[i]); - if (sizes[i]) - if (code-1 >= (1 << i)) return e("bad codelengths","Corrupt JPEG"); - z->maxcode[i] = code << (16-i); // preshift for inner loop - code <<= 1; - k += sizes[i]; - } - z->maxcode[16] = 0x10000; // sentinel - for (i=0; i < num; ++i) { - int s = sizelist[i]; - if (s) { - int c = next_code[s] - z->firstcode[s] + z->firstsymbol[s]; - z->size[c] = (stbi__uint8)s; - z->value[c] = (stbi__uint16)i; - if (s <= ZFAST_BITS) { - int k = bit_reverse(next_code[s],s); - while (k < (1 << ZFAST_BITS)) { - z->fast[k] = (stbi__uint16) c; - k += (1 << s); - } - } - ++next_code[s]; - } - } - return 1; -} - -// zlib-from-memory implementation for PNG reading -// because PNG allows splitting the zlib stream arbitrarily, -// and it's annoying structurally to have PNG call ZLIB call PNG, -// we require PNG read all the IDATs and combine them into a single -// memory buffer - -typedef struct -{ - stbi__uint8 *zbuffer, *zbuffer_end; - int num_bits; - stbi__uint32 code_buffer; - - char *zout; - char *zout_start; - char *zout_end; - int z_expandable; - - zhuffman z_length, z_distance; -} zbuf; - -stbi_inline static int zget8(zbuf *z) -{ - if (z->zbuffer >= z->zbuffer_end) return 0; - return *z->zbuffer++; -} - -static void fill_bits(zbuf *z) -{ - do { - assert(z->code_buffer < (1U << z->num_bits)); - z->code_buffer |= zget8(z) << z->num_bits; - z->num_bits += 8; - } while (z->num_bits <= 24); -} - -stbi_inline static unsigned int zreceive(zbuf *z, int n) -{ - unsigned int k; - if (z->num_bits < n) fill_bits(z); - k = z->code_buffer & ((1 << n) - 1); - z->code_buffer >>= n; - z->num_bits -= n; - return k; -} - -stbi_inline static int zhuffman_decode(zbuf *a, zhuffman *z) -{ - int b,s,k; - if (a->num_bits < 16) fill_bits(a); - b = z->fast[a->code_buffer & ZFAST_MASK]; - if (b < 0xffff) { - s = z->size[b]; - a->code_buffer >>= s; - a->num_bits -= s; - return z->value[b]; - } - - // not resolved by fast table, so compute it the slow way - // use jpeg approach, which requires MSbits at top - k = bit_reverse(a->code_buffer, 16); - for (s=ZFAST_BITS+1; ; ++s) - if (k < z->maxcode[s]) - break; - if (s == 16) return -1; // invalid code! - // code size is s, so: - b = (k >> (16-s)) - z->firstcode[s] + z->firstsymbol[s]; - assert(z->size[b] == s); - a->code_buffer >>= s; - a->num_bits -= s; - return z->value[b]; -} - -static int expand(zbuf *z, int n) // need to make room for n bytes -{ - char *q; - int cur, limit; - if (!z->z_expandable) return e("output buffer limit","Corrupt PNG"); - cur = (int) (z->zout - z->zout_start); - limit = (int) (z->zout_end - z->zout_start); - while (cur + n > limit) - limit *= 2; - q = (char *) realloc(z->zout_start, limit); - if (q == NULL) return e("outofmem", "Out of memory"); - z->zout_start = q; - z->zout = q + cur; - z->zout_end = q + limit; - return 1; -} - -static int length_base[31] = { - 3,4,5,6,7,8,9,10,11,13, - 15,17,19,23,27,31,35,43,51,59, - 67,83,99,115,131,163,195,227,258,0,0 }; - -static int length_extra[31]= -{ 0,0,0,0,0,0,0,0,1,1,1,1,2,2,2,2,3,3,3,3,4,4,4,4,5,5,5,5,0,0,0 }; - -static int dist_base[32] = { 1,2,3,4,5,7,9,13,17,25,33,49,65,97,129,193, -257,385,513,769,1025,1537,2049,3073,4097,6145,8193,12289,16385,24577,0,0}; - -static int dist_extra[32] = -{ 0,0,0,0,1,1,2,2,3,3,4,4,5,5,6,6,7,7,8,8,9,9,10,10,11,11,12,12,13,13}; - -static int parse_huffman_block(zbuf *a) -{ - for(;;) { - int z = zhuffman_decode(a, &a->z_length); - if (z < 256) { - if (z < 0) return e("bad huffman code","Corrupt PNG"); // error in huffman codes - if (a->zout >= a->zout_end) if (!expand(a, 1)) return 0; - *a->zout++ = (char) z; - } else { - stbi__uint8 *p; - int len,dist; - if (z == 256) return 1; - z -= 257; - len = length_base[z]; - if (length_extra[z]) len += zreceive(a, length_extra[z]); - z = zhuffman_decode(a, &a->z_distance); - if (z < 0) return e("bad huffman code","Corrupt PNG"); - dist = dist_base[z]; - if (dist_extra[z]) dist += zreceive(a, dist_extra[z]); - if (a->zout - a->zout_start < dist) return e("bad dist","Corrupt PNG"); - if (a->zout + len > a->zout_end) if (!expand(a, len)) return 0; - p = (stbi__uint8 *) (a->zout - dist); - while (len--) - *a->zout++ = *p++; - } - } -} - -static int compute_huffman_codes(zbuf *a) -{ - static stbi__uint8 length_dezigzag[19] = { 16,17,18,0,8,7,9,6,10,5,11,4,12,3,13,2,14,1,15 }; - zhuffman z_codelength; - stbi__uint8 lencodes[286+32+137];//padding for maximum single op - stbi__uint8 codelength_sizes[19]; - int i,n; - - int hlit = zreceive(a,5) + 257; - int hdist = zreceive(a,5) + 1; - int hclen = zreceive(a,4) + 4; - - memset(codelength_sizes, 0, sizeof(codelength_sizes)); - for (i=0; i < hclen; ++i) { - int s = zreceive(a,3); - codelength_sizes[length_dezigzag[i]] = (stbi__uint8) s; - } - if (!zbuild_huffman(&z_codelength, codelength_sizes, 19)) return 0; - - n = 0; - while (n < hlit + hdist) { - int c = zhuffman_decode(a, &z_codelength); - assert(c >= 0 && c < 19); - if (c < 16) - lencodes[n++] = (stbi__uint8) c; - else if (c == 16) { - c = zreceive(a,2)+3; - memset(lencodes+n, lencodes[n-1], c); - n += c; - } else if (c == 17) { - c = zreceive(a,3)+3; - memset(lencodes+n, 0, c); - n += c; - } else { - assert(c == 18); - c = zreceive(a,7)+11; - memset(lencodes+n, 0, c); - n += c; - } - } - if (n != hlit+hdist) return e("bad codelengths","Corrupt PNG"); - if (!zbuild_huffman(&a->z_length, lencodes, hlit)) return 0; - if (!zbuild_huffman(&a->z_distance, lencodes+hlit, hdist)) return 0; - return 1; -} - -static int parse_uncompressed_block(zbuf *a) -{ - stbi__uint8 header[4]; - int len,nlen,k; - if (a->num_bits & 7) - zreceive(a, a->num_bits & 7); // discard - // drain the bit-packed data into header - k = 0; - while (a->num_bits > 0) { - header[k++] = (stbi__uint8) (a->code_buffer & 255); // wtf this warns? - a->code_buffer >>= 8; - a->num_bits -= 8; - } - assert(a->num_bits == 0); - // now fill header the normal way - while (k < 4) - header[k++] = (stbi__uint8) zget8(a); - len = header[1] * 256 + header[0]; - nlen = header[3] * 256 + header[2]; - if (nlen != (len ^ 0xffff)) return e("zlib corrupt","Corrupt PNG"); - if (a->zbuffer + len > a->zbuffer_end) return e("read past buffer","Corrupt PNG"); - if (a->zout + len > a->zout_end) - if (!expand(a, len)) return 0; - memcpy(a->zout, a->zbuffer, len); - a->zbuffer += len; - a->zout += len; - return 1; -} - -static int parse_zlib_header(zbuf *a) -{ - int cmf = zget8(a); - int cm = cmf & 15; - /* int cinfo = cmf >> 4; */ - int flg = zget8(a); - if ((cmf*256+flg) % 31 != 0) return e("bad zlib header","Corrupt PNG"); // zlib spec - if (flg & 32) return e("no preset dict","Corrupt PNG"); // preset dictionary not allowed in png - if (cm != 8) return e("bad compression","Corrupt PNG"); // DEFLATE required for png - // window = 1 << (8 + cinfo)... but who cares, we fully buffer output - return 1; -} - -// @TODO: should statically initialize these for optimal thread safety -static stbi__uint8 default_length[288], default_distance[32]; -static void init_defaults(void) -{ - int i; // use <= to match clearly with spec - for (i=0; i <= 143; ++i) default_length[i] = 8; - for ( ; i <= 255; ++i) default_length[i] = 9; - for ( ; i <= 279; ++i) default_length[i] = 7; - for ( ; i <= 287; ++i) default_length[i] = 8; - - for (i=0; i <= 31; ++i) default_distance[i] = 5; -} - -int stbi_png_partial; // a quick hack to only allow decoding some of a PNG... I should implement real streaming support instead -static int parse_zlib(zbuf *a, int parse_header) -{ - int final, type; - if (parse_header) - if (!parse_zlib_header(a)) return 0; - a->num_bits = 0; - a->code_buffer = 0; - do { - final = zreceive(a,1); - type = zreceive(a,2); - if (type == 0) { - if (!parse_uncompressed_block(a)) return 0; - } else if (type == 3) { - return 0; - } else { - if (type == 1) { - // use fixed code lengths - if (!default_distance[31]) init_defaults(); - if (!zbuild_huffman(&a->z_length , default_length , 288)) return 0; - if (!zbuild_huffman(&a->z_distance, default_distance, 32)) return 0; - } else { - if (!compute_huffman_codes(a)) return 0; - } - if (!parse_huffman_block(a)) return 0; - } - if (stbi_png_partial && a->zout - a->zout_start > 65536) - break; - } while (!final); - return 1; -} - -static int do_zlib(zbuf *a, char *obuf, int olen, int exp, int parse_header) -{ - a->zout_start = obuf; - a->zout = obuf; - a->zout_end = obuf + olen; - a->z_expandable = exp; - - return parse_zlib(a, parse_header); -} - -char *stbi_zlib_decode_malloc_guesssize(const char *buffer, int len, int initial_size, int *outlen) -{ - zbuf a; - char *p = (char *) malloc(initial_size); - if (p == NULL) return NULL; - a.zbuffer = (stbi__uint8 *) buffer; - a.zbuffer_end = (stbi__uint8 *) buffer + len; - if (do_zlib(&a, p, initial_size, 1, 1)) { - if (outlen) *outlen = (int) (a.zout - a.zout_start); - return a.zout_start; - } else { - free(a.zout_start); - return NULL; - } -} - -char *stbi_zlib_decode_malloc(char const *buffer, int len, int *outlen) -{ - return stbi_zlib_decode_malloc_guesssize(buffer, len, 16384, outlen); -} - -char *stbi_zlib_decode_malloc_guesssize_headerflag(const char *buffer, int len, int initial_size, int *outlen, int parse_header) -{ - zbuf a; - char *p = (char *) malloc(initial_size); - if (p == NULL) return NULL; - a.zbuffer = (stbi__uint8 *) buffer; - a.zbuffer_end = (stbi__uint8 *) buffer + len; - if (do_zlib(&a, p, initial_size, 1, parse_header)) { - if (outlen) *outlen = (int) (a.zout - a.zout_start); - return a.zout_start; - } else { - free(a.zout_start); - return NULL; - } -} - -int stbi_zlib_decode_buffer(char *obuffer, int olen, char const *ibuffer, int ilen) -{ - zbuf a; - a.zbuffer = (stbi__uint8 *) ibuffer; - a.zbuffer_end = (stbi__uint8 *) ibuffer + ilen; - if (do_zlib(&a, obuffer, olen, 0, 1)) - return (int) (a.zout - a.zout_start); - else - return -1; -} - -char *stbi_zlib_decode_noheader_malloc(char const *buffer, int len, int *outlen) -{ - zbuf a; - char *p = (char *) malloc(16384); - if (p == NULL) return NULL; - a.zbuffer = (stbi__uint8 *) buffer; - a.zbuffer_end = (stbi__uint8 *) buffer+len; - if (do_zlib(&a, p, 16384, 1, 0)) { - if (outlen) *outlen = (int) (a.zout - a.zout_start); - return a.zout_start; - } else { - free(a.zout_start); - return NULL; - } -} - -int stbi_zlib_decode_noheader_buffer(char *obuffer, int olen, const char *ibuffer, int ilen) -{ - zbuf a; - a.zbuffer = (stbi__uint8 *) ibuffer; - a.zbuffer_end = (stbi__uint8 *) ibuffer + ilen; - if (do_zlib(&a, obuffer, olen, 0, 0)) - return (int) (a.zout - a.zout_start); - else - return -1; -} - -// public domain "baseline" PNG decoder v0.10 Sean Barrett 2006-11-18 -// simple implementation -// - only 8-bit samples -// - no CRC checking -// - allocates lots of intermediate memory -// - avoids problem of streaming data between subsystems -// - avoids explicit window management -// performance -// - uses stb_zlib, a PD zlib implementation with fast huffman decoding - - -typedef struct -{ - stbi__uint32 length; - stbi__uint32 type; -} chunk; - -#define PNG_TYPE(a,b,c,d) (((a) << 24) + ((b) << 16) + ((c) << 8) + (d)) - -static chunk get_chunk_header(stbi *s) -{ - chunk c; - c.length = get32(s); - c.type = get32(s); - return c; -} - -static int check_png_header(stbi *s) -{ - static stbi__uint8 png_sig[8] = { 137,80,78,71,13,10,26,10 }; - int i; - for (i=0; i < 8; ++i) - if (get8u(s) != png_sig[i]) return e("bad png sig","Not a PNG"); - return 1; -} - -typedef struct -{ - stbi *s; - stbi__uint8 *idata, *expanded, *out; -} png; - - -enum { - F_none=0, F_sub=1, F_up=2, F_avg=3, F_paeth=4, - F_avg_first, F_paeth_first -}; - -static stbi__uint8 first_row_filter[5] = -{ - F_none, F_sub, F_none, F_avg_first, F_paeth_first -}; - -static int paeth(int a, int b, int c) -{ - int p = a + b - c; - int pa = abs(p-a); - int pb = abs(p-b); - int pc = abs(p-c); - if (pa <= pb && pa <= pc) return a; - if (pb <= pc) return b; - return c; -} - -// create the png data from post-deflated data -static int create_png_image_raw(png *a, stbi__uint8 *raw, stbi__uint32 raw_len, int out_n, stbi__uint32 x, stbi__uint32 y) -{ - stbi *s = a->s; - stbi__uint32 i,j,stride = x*out_n; - int k; - int img_n = s->img_n; // copy it into a local for later - assert(out_n == s->img_n || out_n == s->img_n+1); - if (stbi_png_partial) y = 1; - a->out = (stbi__uint8 *) malloc(x * y * out_n); - if (!a->out) return e("outofmem", "Out of memory"); - if (!stbi_png_partial) { - if (s->img_x == x && s->img_y == y) { - if (raw_len != (img_n * x + 1) * y) return e("not enough pixels","Corrupt PNG"); - } else { // interlaced: - if (raw_len < (img_n * x + 1) * y) return e("not enough pixels","Corrupt PNG"); - } - } - for (j=0; j < y; ++j) { - stbi__uint8 *cur = a->out + stride*j; - stbi__uint8 *prior = cur - stride; - int filter = *raw++; - if (filter > 4) return e("invalid filter","Corrupt PNG"); - // if first row, use special filter that doesn't sample previous row - if (j == 0) filter = first_row_filter[filter]; - // handle first pixel explicitly - for (k=0; k < img_n; ++k) { - switch (filter) { - case F_none : cur[k] = raw[k]; break; - case F_sub : cur[k] = raw[k]; break; - case F_up : cur[k] = raw[k] + prior[k]; break; - case F_avg : cur[k] = raw[k] + (prior[k]>>1); break; - case F_paeth : cur[k] = (stbi__uint8) (raw[k] + paeth(0,prior[k],0)); break; - case F_avg_first : cur[k] = raw[k]; break; - case F_paeth_first: cur[k] = raw[k]; break; - } - } - if (img_n != out_n) cur[img_n] = 255; - raw += img_n; - cur += out_n; - prior += out_n; - // this is a little gross, so that we don't switch per-pixel or per-component - if (img_n == out_n) { - #define CASE(f) \ - case f: \ - for (i=x-1; i >= 1; --i, raw+=img_n,cur+=img_n,prior+=img_n) \ - for (k=0; k < img_n; ++k) - switch (filter) { - CASE(F_none) cur[k] = raw[k]; break; - CASE(F_sub) cur[k] = raw[k] + cur[k-img_n]; break; - CASE(F_up) cur[k] = raw[k] + prior[k]; break; - CASE(F_avg) cur[k] = raw[k] + ((prior[k] + cur[k-img_n])>>1); break; - CASE(F_paeth) cur[k] = (stbi__uint8) (raw[k] + paeth(cur[k-img_n],prior[k],prior[k-img_n])); break; - CASE(F_avg_first) cur[k] = raw[k] + (cur[k-img_n] >> 1); break; - CASE(F_paeth_first) cur[k] = (stbi__uint8) (raw[k] + paeth(cur[k-img_n],0,0)); break; - } - #undef CASE - } else { - assert(img_n+1 == out_n); - #define CASE(f) \ - case f: \ - for (i=x-1; i >= 1; --i, cur[img_n]=255,raw+=img_n,cur+=out_n,prior+=out_n) \ - for (k=0; k < img_n; ++k) - switch (filter) { - CASE(F_none) cur[k] = raw[k]; break; - CASE(F_sub) cur[k] = raw[k] + cur[k-out_n]; break; - CASE(F_up) cur[k] = raw[k] + prior[k]; break; - CASE(F_avg) cur[k] = raw[k] + ((prior[k] + cur[k-out_n])>>1); break; - CASE(F_paeth) cur[k] = (stbi__uint8) (raw[k] + paeth(cur[k-out_n],prior[k],prior[k-out_n])); break; - CASE(F_avg_first) cur[k] = raw[k] + (cur[k-out_n] >> 1); break; - CASE(F_paeth_first) cur[k] = (stbi__uint8) (raw[k] + paeth(cur[k-out_n],0,0)); break; - } - #undef CASE - } - } - return 1; -} - -static int create_png_image(png *a, stbi__uint8 *raw, stbi__uint32 raw_len, int out_n, int interlaced) -{ - stbi__uint8 *final; - int p; - int save; - if (!interlaced) - return create_png_image_raw(a, raw, raw_len, out_n, a->s->img_x, a->s->img_y); - save = stbi_png_partial; - stbi_png_partial = 0; - - // de-interlacing - final = (stbi__uint8 *) malloc(a->s->img_x * a->s->img_y * out_n); - for (p=0; p < 7; ++p) { - int xorig[] = { 0,4,0,2,0,1,0 }; - int yorig[] = { 0,0,4,0,2,0,1 }; - int xspc[] = { 8,8,4,4,2,2,1 }; - int yspc[] = { 8,8,8,4,4,2,2 }; - int i,j,x,y; - // pass1_x[4] = 0, pass1_x[5] = 1, pass1_x[12] = 1 - x = (a->s->img_x - xorig[p] + xspc[p]-1) / xspc[p]; - y = (a->s->img_y - yorig[p] + yspc[p]-1) / yspc[p]; - if (x && y) { - if (!create_png_image_raw(a, raw, raw_len, out_n, x, y)) { - free(final); - return 0; - } - for (j=0; j < y; ++j) - for (i=0; i < x; ++i) - memcpy(final + (j*yspc[p]+yorig[p])*a->s->img_x*out_n + (i*xspc[p]+xorig[p])*out_n, - a->out + (j*x+i)*out_n, out_n); - free(a->out); - raw += (x*out_n+1)*y; - raw_len -= (x*out_n+1)*y; - } - } - a->out = final; - - stbi_png_partial = save; - return 1; -} - -static int compute_transparency(png *z, stbi__uint8 tc[3], int out_n) -{ - stbi *s = z->s; - stbi__uint32 i, pixel_count = s->img_x * s->img_y; - stbi__uint8 *p = z->out; - - // compute color-based transparency, assuming we've - // already got 255 as the alpha value in the output - assert(out_n == 2 || out_n == 4); - - if (out_n == 2) { - for (i=0; i < pixel_count; ++i) { - p[1] = (p[0] == tc[0] ? 0 : 255); - p += 2; - } - } else { - for (i=0; i < pixel_count; ++i) { - if (p[0] == tc[0] && p[1] == tc[1] && p[2] == tc[2]) - p[3] = 0; - p += 4; - } - } - return 1; -} - -static int expand_palette(png *a, stbi__uint8 *palette, int len, int pal_img_n) -{ - stbi__uint32 i, pixel_count = a->s->img_x * a->s->img_y; - stbi__uint8 *p, *temp_out, *orig = a->out; - - p = (stbi__uint8 *) malloc(pixel_count * pal_img_n); - if (p == NULL) return e("outofmem", "Out of memory"); - - // between here and free(out) below, exitting would leak - temp_out = p; - - if (pal_img_n == 3) { - for (i=0; i < pixel_count; ++i) { - int n = orig[i]*4; - p[0] = palette[n ]; - p[1] = palette[n+1]; - p[2] = palette[n+2]; - p += 3; - } - } else { - for (i=0; i < pixel_count; ++i) { - int n = orig[i]*4; - p[0] = palette[n ]; - p[1] = palette[n+1]; - p[2] = palette[n+2]; - p[3] = palette[n+3]; - p += 4; - } - } - free(a->out); - a->out = temp_out; - - STBI_NOTUSED(len); - - return 1; -} - -static int stbi_unpremultiply_on_load = 0; -static int stbi_de_iphone_flag = 0; - -void stbi_set_unpremultiply_on_load(int flag_true_if_should_unpremultiply) -{ - stbi_unpremultiply_on_load = flag_true_if_should_unpremultiply; -} -void stbi_convert_iphone_png_to_rgb(int flag_true_if_should_convert) -{ - stbi_de_iphone_flag = flag_true_if_should_convert; -} - -static void stbi_de_iphone(png *z) -{ - stbi *s = z->s; - stbi__uint32 i, pixel_count = s->img_x * s->img_y; - stbi__uint8 *p = z->out; - - if (s->img_out_n == 3) { // convert bgr to rgb - for (i=0; i < pixel_count; ++i) { - stbi__uint8 t = p[0]; - p[0] = p[2]; - p[2] = t; - p += 3; - } - } else { - assert(s->img_out_n == 4); - if (stbi_unpremultiply_on_load) { - // convert bgr to rgb and unpremultiply - for (i=0; i < pixel_count; ++i) { - stbi__uint8 a = p[3]; - stbi__uint8 t = p[0]; - if (a) { - p[0] = p[2] * 255 / a; - p[1] = p[1] * 255 / a; - p[2] = t * 255 / a; - } else { - p[0] = p[2]; - p[2] = t; - } - p += 4; - } - } else { - // convert bgr to rgb - for (i=0; i < pixel_count; ++i) { - stbi__uint8 t = p[0]; - p[0] = p[2]; - p[2] = t; - p += 4; - } - } - } -} - -static int parse_png_file(png *z, int scan, int req_comp) -{ - stbi__uint8 palette[1024], pal_img_n=0; - stbi__uint8 has_trans=0, tc[3]; - stbi__uint32 ioff=0, idata_limit=0, i, pal_len=0; - int first=1,k,interlace=0, iphone=0; - stbi *s = z->s; - - z->expanded = NULL; - z->idata = NULL; - z->out = NULL; - - if (!check_png_header(s)) return 0; - - if (scan == SCAN_type) return 1; - - for (;;) { - chunk c = get_chunk_header(s); - switch (c.type) { - case PNG_TYPE('C','g','B','I'): - iphone = stbi_de_iphone_flag; - skip(s, c.length); - break; - case PNG_TYPE('I','H','D','R'): { - int depth,color,comp,filter; - if (!first) return e("multiple IHDR","Corrupt PNG"); - first = 0; - if (c.length != 13) return e("bad IHDR len","Corrupt PNG"); - s->img_x = get32(s); if (s->img_x > (1 << 24)) return e("too large","Very large image (corrupt?)"); - s->img_y = get32(s); if (s->img_y > (1 << 24)) return e("too large","Very large image (corrupt?)"); - depth = get8(s); if (depth != 8) return e("8bit only","PNG not supported: 8-bit only"); - color = get8(s); if (color > 6) return e("bad ctype","Corrupt PNG"); - if (color == 3) pal_img_n = 3; else if (color & 1) return e("bad ctype","Corrupt PNG"); - comp = get8(s); if (comp) return e("bad comp method","Corrupt PNG"); - filter= get8(s); if (filter) return e("bad filter method","Corrupt PNG"); - interlace = get8(s); if (interlace>1) return e("bad interlace method","Corrupt PNG"); - if (!s->img_x || !s->img_y) return e("0-pixel image","Corrupt PNG"); - if (!pal_img_n) { - s->img_n = (color & 2 ? 3 : 1) + (color & 4 ? 1 : 0); - if ((1 << 30) / s->img_x / s->img_n < s->img_y) return e("too large", "Image too large to decode"); - if (scan == SCAN_header) return 1; - } else { - // if paletted, then pal_n is our final components, and - // img_n is # components to decompress/filter. - s->img_n = 1; - if ((1 << 30) / s->img_x / 4 < s->img_y) return e("too large","Corrupt PNG"); - // if SCAN_header, have to scan to see if we have a tRNS - } - break; - } - - case PNG_TYPE('P','L','T','E'): { - if (first) return e("first not IHDR", "Corrupt PNG"); - if (c.length > 256*3) return e("invalid PLTE","Corrupt PNG"); - pal_len = c.length / 3; - if (pal_len * 3 != c.length) return e("invalid PLTE","Corrupt PNG"); - for (i=0; i < pal_len; ++i) { - palette[i*4+0] = get8u(s); - palette[i*4+1] = get8u(s); - palette[i*4+2] = get8u(s); - palette[i*4+3] = 255; - } - break; - } - - case PNG_TYPE('t','R','N','S'): { - if (first) return e("first not IHDR", "Corrupt PNG"); - if (z->idata) return e("tRNS after IDAT","Corrupt PNG"); - if (pal_img_n) { - if (scan == SCAN_header) { s->img_n = 4; return 1; } - if (pal_len == 0) return e("tRNS before PLTE","Corrupt PNG"); - if (c.length > pal_len) return e("bad tRNS len","Corrupt PNG"); - pal_img_n = 4; - for (i=0; i < c.length; ++i) - palette[i*4+3] = get8u(s); - } else { - if (!(s->img_n & 1)) return e("tRNS with alpha","Corrupt PNG"); - if (c.length != (stbi__uint32) s->img_n*2) return e("bad tRNS len","Corrupt PNG"); - has_trans = 1; - for (k=0; k < s->img_n; ++k) - tc[k] = (stbi__uint8) get16(s); // non 8-bit images will be larger - } - break; - } - - case PNG_TYPE('I','D','A','T'): { - if (first) return e("first not IHDR", "Corrupt PNG"); - if (pal_img_n && !pal_len) return e("no PLTE","Corrupt PNG"); - if (scan == SCAN_header) { s->img_n = pal_img_n; return 1; } - if (ioff + c.length > idata_limit) { - stbi__uint8 *p; - if (idata_limit == 0) idata_limit = c.length > 4096 ? c.length : 4096; - while (ioff + c.length > idata_limit) - idata_limit *= 2; - p = (stbi__uint8 *) realloc(z->idata, idata_limit); if (p == NULL) return e("outofmem", "Out of memory"); - z->idata = p; - } - if (!getn(s, z->idata+ioff,c.length)) return e("outofdata","Corrupt PNG"); - ioff += c.length; - break; - } - - case PNG_TYPE('I','E','N','D'): { - stbi__uint32 raw_len; - if (first) return e("first not IHDR", "Corrupt PNG"); - if (scan != SCAN_load) return 1; - if (z->idata == NULL) return e("no IDAT","Corrupt PNG"); - z->expanded = (stbi__uint8 *) stbi_zlib_decode_malloc_guesssize_headerflag((char *) z->idata, ioff, 16384, (int *) &raw_len, !iphone); - if (z->expanded == NULL) return 0; // zlib should set error - free(z->idata); z->idata = NULL; - if ((req_comp == s->img_n+1 && req_comp != 3 && !pal_img_n) || has_trans) - s->img_out_n = s->img_n+1; - else - s->img_out_n = s->img_n; - if (!create_png_image(z, z->expanded, raw_len, s->img_out_n, interlace)) return 0; - if (has_trans) - if (!compute_transparency(z, tc, s->img_out_n)) return 0; - if (iphone && s->img_out_n > 2) - stbi_de_iphone(z); - if (pal_img_n) { - // pal_img_n == 3 or 4 - s->img_n = pal_img_n; // record the actual colors we had - s->img_out_n = pal_img_n; - if (req_comp >= 3) s->img_out_n = req_comp; - if (!expand_palette(z, palette, pal_len, s->img_out_n)) - return 0; - } - free(z->expanded); z->expanded = NULL; - return 1; - } - - default: - // if critical, fail - if (first) return e("first not IHDR", "Corrupt PNG"); - if ((c.type & (1 << 29)) == 0) { - #ifndef STBI_NO_FAILURE_STRINGS - // not threadsafe - static char invalid_chunk[] = "XXXX chunk not known"; - invalid_chunk[0] = (stbi__uint8) (c.type >> 24); - invalid_chunk[1] = (stbi__uint8) (c.type >> 16); - invalid_chunk[2] = (stbi__uint8) (c.type >> 8); - invalid_chunk[3] = (stbi__uint8) (c.type >> 0); - #endif - return e(invalid_chunk, "PNG not supported: unknown chunk type"); - } - skip(s, c.length); - break; - } - // end of chunk, read and skip CRC - get32(s); - } -} - -static unsigned char *do_png(png *p, int *x, int *y, int *n, int req_comp) -{ - unsigned char *result=NULL; - if (req_comp < 0 || req_comp > 4) return epuc("bad req_comp", "Internal error"); - if (parse_png_file(p, SCAN_load, req_comp)) { - result = p->out; - p->out = NULL; - if (req_comp && req_comp != p->s->img_out_n) { - result = convert_format(result, p->s->img_out_n, req_comp, p->s->img_x, p->s->img_y); - p->s->img_out_n = req_comp; - if (result == NULL) return result; - } - *x = p->s->img_x; - *y = p->s->img_y; - if (n) *n = p->s->img_n; - } - free(p->out); p->out = NULL; - free(p->expanded); p->expanded = NULL; - free(p->idata); p->idata = NULL; - - return result; -} - -static unsigned char *stbi_png_load(stbi *s, int *x, int *y, int *comp, int req_comp) -{ - png p; - p.s = s; - return do_png(&p, x,y,comp,req_comp); -} - -static int stbi_png_test(stbi *s) -{ - int r; - r = check_png_header(s); - stbi_rewind(s); - return r; -} - -static int stbi_png_info_raw(png *p, int *x, int *y, int *comp) -{ - if (!parse_png_file(p, SCAN_header, 0)) { - stbi_rewind( p->s ); - return 0; - } - if (x) *x = p->s->img_x; - if (y) *y = p->s->img_y; - if (comp) *comp = p->s->img_n; - return 1; -} - -static int stbi_png_info(stbi *s, int *x, int *y, int *comp) -{ - png p; - p.s = s; - return stbi_png_info_raw(&p, x, y, comp); -} - -// Microsoft/Windows BMP image - -static int bmp_test(stbi *s) -{ - int sz; - if (get8(s) != 'B') return 0; - if (get8(s) != 'M') return 0; - get32le(s); // discard filesize - get16le(s); // discard reserved - get16le(s); // discard reserved - get32le(s); // discard data offset - sz = get32le(s); - if (sz == 12 || sz == 40 || sz == 56 || sz == 108) return 1; - return 0; -} - -static int stbi_bmp_test(stbi *s) -{ - int r = bmp_test(s); - stbi_rewind(s); - return r; -} - - -// returns 0..31 for the highest set bit -static int high_bit(unsigned int z) -{ - int n=0; - if (z == 0) return -1; - if (z >= 0x10000) n += 16, z >>= 16; - if (z >= 0x00100) n += 8, z >>= 8; - if (z >= 0x00010) n += 4, z >>= 4; - if (z >= 0x00004) n += 2, z >>= 2; - if (z >= 0x00002) n += 1, z >>= 1; - return n; -} - -static int bitcount(unsigned int a) -{ - a = (a & 0x55555555) + ((a >> 1) & 0x55555555); // max 2 - a = (a & 0x33333333) + ((a >> 2) & 0x33333333); // max 4 - a = (a + (a >> 4)) & 0x0f0f0f0f; // max 8 per 4, now 8 bits - a = (a + (a >> 8)); // max 16 per 8 bits - a = (a + (a >> 16)); // max 32 per 8 bits - return a & 0xff; -} - -static int shiftsigned(int v, int shift, int bits) -{ - int result; - int z=0; - - if (shift < 0) v <<= -shift; - else v >>= shift; - result = v; - - z = bits; - while (z < 8) { - result += v >> z; - z += bits; - } - return result; -} - -static stbi_uc *bmp_load(stbi *s, int *x, int *y, int *comp, int req_comp) -{ - stbi__uint8 *out; - unsigned int mr=0,mg=0,mb=0,ma=0, fake_a=0; - stbi_uc pal[256][4]; - int psize=0,i,j,compress=0,width; - int bpp, flip_vertically, pad, target, offset, hsz; - if (get8(s) != 'B' || get8(s) != 'M') return epuc("not BMP", "Corrupt BMP"); - get32le(s); // discard filesize - get16le(s); // discard reserved - get16le(s); // discard reserved - offset = get32le(s); - hsz = get32le(s); - if (hsz != 12 && hsz != 40 && hsz != 56 && hsz != 108) return epuc("unknown BMP", "BMP type not supported: unknown"); - if (hsz == 12) { - s->img_x = get16le(s); - s->img_y = get16le(s); - } else { - s->img_x = get32le(s); - s->img_y = get32le(s); - } - if (get16le(s) != 1) return epuc("bad BMP", "bad BMP"); - bpp = get16le(s); - if (bpp == 1) return epuc("monochrome", "BMP type not supported: 1-bit"); - flip_vertically = ((int) s->img_y) > 0; - s->img_y = abs((int) s->img_y); - if (hsz == 12) { - if (bpp < 24) - psize = (offset - 14 - 24) / 3; - } else { - compress = get32le(s); - if (compress == 1 || compress == 2) return epuc("BMP RLE", "BMP type not supported: RLE"); - get32le(s); // discard sizeof - get32le(s); // discard hres - get32le(s); // discard vres - get32le(s); // discard colorsused - get32le(s); // discard max important - if (hsz == 40 || hsz == 56) { - if (hsz == 56) { - get32le(s); - get32le(s); - get32le(s); - get32le(s); - } - if (bpp == 16 || bpp == 32) { - mr = mg = mb = 0; - if (compress == 0) { - if (bpp == 32) { - mr = 0xffu << 16; - mg = 0xffu << 8; - mb = 0xffu << 0; - ma = 0xffu << 24; - fake_a = 1; // @TODO: check for cases like alpha value is all 0 and switch it to 255 - STBI_NOTUSED(fake_a); - } else { - mr = 31u << 10; - mg = 31u << 5; - mb = 31u << 0; - } - } else if (compress == 3) { - mr = get32le(s); - mg = get32le(s); - mb = get32le(s); - // not documented, but generated by photoshop and handled by mspaint - if (mr == mg && mg == mb) { - // ?!?!? - return epuc("bad BMP", "bad BMP"); - } - } else - return epuc("bad BMP", "bad BMP"); - } - } else { - assert(hsz == 108); - mr = get32le(s); - mg = get32le(s); - mb = get32le(s); - ma = get32le(s); - get32le(s); // discard color space - for (i=0; i < 12; ++i) - get32le(s); // discard color space parameters - } - if (bpp < 16) - psize = (offset - 14 - hsz) >> 2; - } - s->img_n = ma ? 4 : 3; - if (req_comp && req_comp >= 3) // we can directly decode 3 or 4 - target = req_comp; - else - target = s->img_n; // if they want monochrome, we'll post-convert - out = (stbi_uc *) malloc(target * s->img_x * s->img_y); - if (!out) return epuc("outofmem", "Out of memory"); - if (bpp < 16) { - int z=0; - if (psize == 0 || psize > 256) { free(out); return epuc("invalid", "Corrupt BMP"); } - for (i=0; i < psize; ++i) { - pal[i][2] = get8u(s); - pal[i][1] = get8u(s); - pal[i][0] = get8u(s); - if (hsz != 12) get8(s); - pal[i][3] = 255; - } - skip(s, offset - 14 - hsz - psize * (hsz == 12 ? 3 : 4)); - if (bpp == 4) width = (s->img_x + 1) >> 1; - else if (bpp == 8) width = s->img_x; - else { free(out); return epuc("bad bpp", "Corrupt BMP"); } - pad = (-width)&3; - for (j=0; j < (int) s->img_y; ++j) { - for (i=0; i < (int) s->img_x; i += 2) { - int v=get8(s),v2=0; - if (bpp == 4) { - v2 = v & 15; - v >>= 4; - } - out[z++] = pal[v][0]; - out[z++] = pal[v][1]; - out[z++] = pal[v][2]; - if (target == 4) out[z++] = 255; - if (i+1 == (int) s->img_x) break; - v = (bpp == 8) ? get8(s) : v2; - out[z++] = pal[v][0]; - out[z++] = pal[v][1]; - out[z++] = pal[v][2]; - if (target == 4) out[z++] = 255; - } - skip(s, pad); - } - } else { - int rshift=0,gshift=0,bshift=0,ashift=0,rcount=0,gcount=0,bcount=0,acount=0; - int z = 0; - int easy=0; - skip(s, offset - 14 - hsz); - if (bpp == 24) width = 3 * s->img_x; - else if (bpp == 16) width = 2*s->img_x; - else /* bpp = 32 and pad = 0 */ width=0; - pad = (-width) & 3; - if (bpp == 24) { - easy = 1; - } else if (bpp == 32) { - if (mb == 0xff && mg == 0xff00 && mr == 0x00ff0000 && ma == 0xff000000) - easy = 2; - } - if (!easy) { - if (!mr || !mg || !mb) { free(out); return epuc("bad masks", "Corrupt BMP"); } - // right shift amt to put high bit in position #7 - rshift = high_bit(mr)-7; rcount = bitcount(mr); - gshift = high_bit(mg)-7; gcount = bitcount(mg); - bshift = high_bit(mb)-7; bcount = bitcount(mb); - ashift = high_bit(ma)-7; acount = bitcount(ma); - } - for (j=0; j < (int) s->img_y; ++j) { - if (easy) { - for (i=0; i < (int) s->img_x; ++i) { - int a; - out[z+2] = get8u(s); - out[z+1] = get8u(s); - out[z+0] = get8u(s); - z += 3; - a = (easy == 2 ? get8(s) : 255); - if (target == 4) out[z++] = (stbi__uint8) a; - } - } else { - for (i=0; i < (int) s->img_x; ++i) { - stbi__uint32 v = (stbi__uint32) (bpp == 16 ? get16le(s) : get32le(s)); - int a; - out[z++] = (stbi__uint8) shiftsigned(v & mr, rshift, rcount); - out[z++] = (stbi__uint8) shiftsigned(v & mg, gshift, gcount); - out[z++] = (stbi__uint8) shiftsigned(v & mb, bshift, bcount); - a = (ma ? shiftsigned(v & ma, ashift, acount) : 255); - if (target == 4) out[z++] = (stbi__uint8) a; - } - } - skip(s, pad); - } - } - if (flip_vertically) { - stbi_uc t; - for (j=0; j < (int) s->img_y>>1; ++j) { - stbi_uc *p1 = out + j *s->img_x*target; - stbi_uc *p2 = out + (s->img_y-1-j)*s->img_x*target; - for (i=0; i < (int) s->img_x*target; ++i) { - t = p1[i], p1[i] = p2[i], p2[i] = t; - } - } - } - - if (req_comp && req_comp != target) { - out = convert_format(out, target, req_comp, s->img_x, s->img_y); - if (out == NULL) return out; // convert_format frees input on failure - } - - *x = s->img_x; - *y = s->img_y; - if (comp) *comp = s->img_n; - return out; -} - -static stbi_uc *stbi_bmp_load(stbi *s,int *x, int *y, int *comp, int req_comp) -{ - return bmp_load(s, x,y,comp,req_comp); -} - - -// Targa Truevision - TGA -// by Jonathan Dummer - -static int tga_info(stbi *s, int *x, int *y, int *comp) -{ - int tga_w, tga_h, tga_comp; - int sz; - get8u(s); // discard Offset - sz = get8u(s); // color type - if( sz > 1 ) { - stbi_rewind(s); - return 0; // only RGB or indexed allowed - } - sz = get8u(s); // image type - // only RGB or grey allowed, +/- RLE - if ((sz != 1) && (sz != 2) && (sz != 3) && (sz != 9) && (sz != 10) && (sz != 11)) return 0; - skip(s,9); - tga_w = get16le(s); - if( tga_w < 1 ) { - stbi_rewind(s); - return 0; // test width - } - tga_h = get16le(s); - if( tga_h < 1 ) { - stbi_rewind(s); - return 0; // test height - } - sz = get8(s); // bits per pixel - // only RGB or RGBA or grey allowed - if ((sz != 8) && (sz != 16) && (sz != 24) && (sz != 32)) { - stbi_rewind(s); - return 0; - } - tga_comp = sz; - if (x) *x = tga_w; - if (y) *y = tga_h; - if (comp) *comp = tga_comp / 8; - return 1; // seems to have passed everything -} - -int stbi_tga_info(stbi *s, int *x, int *y, int *comp) -{ - return tga_info(s, x, y, comp); -} - -static int tga_test(stbi *s) -{ - int sz; - get8u(s); // discard Offset - sz = get8u(s); // color type - if ( sz > 1 ) return 0; // only RGB or indexed allowed - sz = get8u(s); // image type - if ( (sz != 1) && (sz != 2) && (sz != 3) && (sz != 9) && (sz != 10) && (sz != 11) ) return 0; // only RGB or grey allowed, +/- RLE - get16(s); // discard palette start - get16(s); // discard palette length - get8(s); // discard bits per palette color entry - get16(s); // discard x origin - get16(s); // discard y origin - if ( get16(s) < 1 ) return 0; // test width - if ( get16(s) < 1 ) return 0; // test height - sz = get8(s); // bits per pixel - if ( (sz != 8) && (sz != 16) && (sz != 24) && (sz != 32) ) return 0; // only RGB or RGBA or grey allowed - return 1; // seems to have passed everything -} - -static int stbi_tga_test(stbi *s) -{ - int res = tga_test(s); - stbi_rewind(s); - return res; -} - -static stbi_uc *tga_load(stbi *s, int *x, int *y, int *comp, int req_comp) -{ - // read in the TGA header stuff - int tga_offset = get8u(s); - int tga_indexed = get8u(s); - int tga_image_type = get8u(s); - int tga_is_RLE = 0; - int tga_palette_start = get16le(s); - int tga_palette_len = get16le(s); - int tga_palette_bits = get8u(s); - int tga_x_origin = get16le(s); - int tga_y_origin = get16le(s); - int tga_width = get16le(s); - int tga_height = get16le(s); - int tga_bits_per_pixel = get8u(s); - int tga_comp = tga_bits_per_pixel / 8; - int tga_inverted = get8u(s); - // image data - unsigned char *tga_data; - unsigned char *tga_palette = NULL; - int i, j; - unsigned char raw_data[4]; - int RLE_count = 0; - int RLE_repeating = 0; - int read_next_pixel = 1; - - // do a tiny bit of precessing - if ( tga_image_type >= 8 ) - { - tga_image_type -= 8; - tga_is_RLE = 1; - } - /* int tga_alpha_bits = tga_inverted & 15; */ - tga_inverted = 1 - ((tga_inverted >> 5) & 1); - - // error check - if ( //(tga_indexed) || - (tga_width < 1) || (tga_height < 1) || - (tga_image_type < 1) || (tga_image_type > 3) || - ((tga_bits_per_pixel != 8) && (tga_bits_per_pixel != 16) && - (tga_bits_per_pixel != 24) && (tga_bits_per_pixel != 32)) - ) - { - return NULL; // we don't report this as a bad TGA because we don't even know if it's TGA - } - - // If I'm paletted, then I'll use the number of bits from the palette - if ( tga_indexed ) - { - tga_comp = tga_palette_bits / 8; - } - - // tga info - *x = tga_width; - *y = tga_height; - if (comp) *comp = tga_comp; - - tga_data = (unsigned char*)malloc( tga_width * tga_height * req_comp ); - if (!tga_data) return epuc("outofmem", "Out of memory"); - - // skip to the data's starting position (offset usually = 0) - skip(s, tga_offset ); - - if ( !tga_indexed && !tga_is_RLE) { - for (i=0; i < tga_height; ++i) { - int y = tga_inverted ? tga_height -i - 1 : i; - stbi__uint8 *tga_row = tga_data + y*tga_width*tga_comp; - getn(s, tga_row, tga_width * tga_comp); - } - } else { - // do I need to load a palette? - if ( tga_indexed) - { - // any data to skip? (offset usually = 0) - skip(s, tga_palette_start ); - // load the palette - tga_palette = (unsigned char*)malloc( tga_palette_len * tga_palette_bits / 8 ); - if (!tga_palette) { - free(tga_data); - return epuc("outofmem", "Out of memory"); - } - if (!getn(s, tga_palette, tga_palette_len * tga_palette_bits / 8 )) { - free(tga_data); - free(tga_palette); - return epuc("bad palette", "Corrupt TGA"); - } - } - // load the data - for (i=0; i < tga_width * tga_height; ++i) - { - // if I'm in RLE mode, do I need to get a RLE chunk? - if ( tga_is_RLE ) - { - if ( RLE_count == 0 ) - { - // yep, get the next byte as a RLE command - int RLE_cmd = get8u(s); - RLE_count = 1 + (RLE_cmd & 127); - RLE_repeating = RLE_cmd >> 7; - read_next_pixel = 1; - } else if ( !RLE_repeating ) - { - read_next_pixel = 1; - } - } else - { - read_next_pixel = 1; - } - // OK, if I need to read a pixel, do it now - if ( read_next_pixel ) - { - // load however much data we did have - if ( tga_indexed ) - { - // read in 1 byte, then perform the lookup - int pal_idx = get8u(s); - if ( pal_idx >= tga_palette_len ) - { - // invalid index - pal_idx = 0; - } - pal_idx *= tga_bits_per_pixel / 8; - for (j = 0; j*8 < tga_bits_per_pixel; ++j) - { - raw_data[j] = tga_palette[pal_idx+j]; - } - } else - { - // read in the data raw - for (j = 0; j*8 < tga_bits_per_pixel; ++j) - { - raw_data[j] = get8u(s); - } - } - // clear the reading flag for the next pixel - read_next_pixel = 0; - } // end of reading a pixel - - // copy data - for (j = 0; j < tga_comp; ++j) - tga_data[i*tga_comp+j] = raw_data[j]; - - // in case we're in RLE mode, keep counting down - --RLE_count; - } - // do I need to invert the image? - if ( tga_inverted ) - { - for (j = 0; j*2 < tga_height; ++j) - { - int index1 = j * tga_width * req_comp; - int index2 = (tga_height - 1 - j) * tga_width * req_comp; - for (i = tga_width * req_comp; i > 0; --i) - { - unsigned char temp = tga_data[index1]; - tga_data[index1] = tga_data[index2]; - tga_data[index2] = temp; - ++index1; - ++index2; - } - } - } - // clear my palette, if I had one - if ( tga_palette != NULL ) - { - free( tga_palette ); - } - } - - // swap RGB - if (tga_comp >= 3) - { - unsigned char* tga_pixel = tga_data; - for (i=0; i < tga_width * tga_height; ++i) - { - unsigned char temp = tga_pixel[0]; - tga_pixel[0] = tga_pixel[2]; - tga_pixel[2] = temp; - tga_pixel += tga_comp; - } - } - - // convert to target component count - if (req_comp && req_comp != tga_comp) - tga_data = convert_format(tga_data, tga_comp, req_comp, tga_width, tga_height); - - // the things I do to get rid of an error message, and yet keep - // Microsoft's C compilers happy... [8^( - tga_palette_start = tga_palette_len = tga_palette_bits = - tga_x_origin = tga_y_origin = 0; - // OK, done - return tga_data; -} - -static stbi_uc *stbi_tga_load(stbi *s, int *x, int *y, int *comp, int req_comp) -{ - return tga_load(s,x,y,comp,req_comp); -} - - -// ************************************************************************************************* -// Photoshop PSD loader -- PD by Thatcher Ulrich, integration by Nicolas Schulz, tweaked by STB - -static int psd_test(stbi *s) -{ - if (get32(s) != 0x38425053) return 0; // "8BPS" - else return 1; -} - -static int stbi_psd_test(stbi *s) -{ - int r = psd_test(s); - stbi_rewind(s); - return r; -} - -static stbi_uc *psd_load(stbi *s, int *x, int *y, int *comp, int req_comp) -{ - int pixelCount; - int channelCount, compression; - int channel, i, count, len; - int w,h; - stbi__uint8 *out; - - // Check identifier - if (get32(s) != 0x38425053) // "8BPS" - return epuc("not PSD", "Corrupt PSD image"); - - // Check file type version. - if (get16(s) != 1) - return epuc("wrong version", "Unsupported version of PSD image"); - - // Skip 6 reserved bytes. - skip(s, 6 ); - - // Read the number of channels (R, G, B, A, etc). - channelCount = get16(s); - if (channelCount < 0 || channelCount > 16) - return epuc("wrong channel count", "Unsupported number of channels in PSD image"); - - // Read the rows and columns of the image. - h = get32(s); - w = get32(s); - - // Make sure the depth is 8 bits. - if (get16(s) != 8) - return epuc("unsupported bit depth", "PSD bit depth is not 8 bit"); - - // Make sure the color mode is RGB. - // Valid options are: - // 0: Bitmap - // 1: Grayscale - // 2: Indexed color - // 3: RGB color - // 4: CMYK color - // 7: Multichannel - // 8: Duotone - // 9: Lab color - if (get16(s) != 3) - return epuc("wrong color format", "PSD is not in RGB color format"); - - // Skip the Mode Data. (It's the palette for indexed color; other info for other modes.) - skip(s,get32(s) ); - - // Skip the image resources. (resolution, pen tool paths, etc) - skip(s, get32(s) ); - - // Skip the reserved data. - skip(s, get32(s) ); - - // Find out if the data is compressed. - // Known values: - // 0: no compression - // 1: RLE compressed - compression = get16(s); - if (compression > 1) - return epuc("bad compression", "PSD has an unknown compression format"); - - // Create the destination image. - out = (stbi_uc *) malloc(4 * w*h); - if (!out) return epuc("outofmem", "Out of memory"); - pixelCount = w*h; - - // Initialize the data to zero. - //memset( out, 0, pixelCount * 4 ); - - // Finally, the image data. - if (compression) { - // RLE as used by .PSD and .TIFF - // Loop until you get the number of unpacked bytes you are expecting: - // Read the next source byte into n. - // If n is between 0 and 127 inclusive, copy the next n+1 bytes literally. - // Else if n is between -127 and -1 inclusive, copy the next byte -n+1 times. - // Else if n is 128, noop. - // Endloop - - // The RLE-compressed data is preceeded by a 2-byte data count for each row in the data, - // which we're going to just skip. - skip(s, h * channelCount * 2 ); - - // Read the RLE data by channel. - for (channel = 0; channel < 4; channel++) { - stbi__uint8 *p; - - p = out+channel; - if (channel >= channelCount) { - // Fill this channel with default data. - for (i = 0; i < pixelCount; i++) *p = (channel == 3 ? 255 : 0), p += 4; - } else { - // Read the RLE data. - count = 0; - while (count < pixelCount) { - len = get8(s); - if (len == 128) { - // No-op. - } else if (len < 128) { - // Copy next len+1 bytes literally. - len++; - count += len; - while (len) { - *p = get8u(s); - p += 4; - len--; - } - } else if (len > 128) { - stbi__uint8 val; - // Next -len+1 bytes in the dest are replicated from next source byte. - // (Interpret len as a negative 8-bit int.) - len ^= 0x0FF; - len += 2; - val = get8u(s); - count += len; - while (len) { - *p = val; - p += 4; - len--; - } - } - } - } - } - - } else { - // We're at the raw image data. It's each channel in order (Red, Green, Blue, Alpha, ...) - // where each channel consists of an 8-bit value for each pixel in the image. - - // Read the data by channel. - for (channel = 0; channel < 4; channel++) { - stbi__uint8 *p; - - p = out + channel; - if (channel > channelCount) { - // Fill this channel with default data. - for (i = 0; i < pixelCount; i++) *p = channel == 3 ? 255 : 0, p += 4; - } else { - // Read the data. - for (i = 0; i < pixelCount; i++) - *p = get8u(s), p += 4; - } - } - } - - if (req_comp && req_comp != 4) { - out = convert_format(out, 4, req_comp, w, h); - if (out == NULL) return out; // convert_format frees input on failure - } - - if (comp) *comp = channelCount; - *y = h; - *x = w; - - return out; -} - -static stbi_uc *stbi_psd_load(stbi *s, int *x, int *y, int *comp, int req_comp) -{ - return psd_load(s,x,y,comp,req_comp); -} - -// ************************************************************************************************* -// Softimage PIC loader -// by Tom Seddon -// -// See http://softimage.wiki.softimage.com/index.php/INFO:_PIC_file_format -// See http://ozviz.wasp.uwa.edu.au/~pbourke/dataformats/softimagepic/ - -static int pic_is4(stbi *s,const char *str) -{ - int i; - for (i=0; i<4; ++i) - if (get8(s) != (stbi_uc)str[i]) - return 0; - - return 1; -} - -static int pic_test(stbi *s) -{ - int i; - - if (!pic_is4(s,"\x53\x80\xF6\x34")) - return 0; - - for(i=0;i<84;++i) - get8(s); - - if (!pic_is4(s,"PICT")) - return 0; - - return 1; -} - -typedef struct -{ - stbi_uc size,type,channel; -} pic_packet_t; - -static stbi_uc *pic_readval(stbi *s, int channel, stbi_uc *dest) -{ - int mask=0x80, i; - - for (i=0; i<4; ++i, mask>>=1) { - if (channel & mask) { - if (at_eof(s)) return epuc("bad file","PIC file too short"); - dest[i]=get8u(s); - } - } - - return dest; -} - -static void pic_copyval(int channel,stbi_uc *dest,const stbi_uc *src) -{ - int mask=0x80,i; - - for (i=0;i<4; ++i, mask>>=1) - if (channel&mask) - dest[i]=src[i]; -} - -static stbi_uc *pic_load2(stbi *s,int width,int height,int *comp, stbi_uc *result) -{ - int act_comp=0,num_packets=0,y,chained; - pic_packet_t packets[10]; - - // this will (should...) cater for even some bizarre stuff like having data - // for the same channel in multiple packets. - do { - pic_packet_t *packet; - - if (num_packets==sizeof(packets)/sizeof(packets[0])) - return epuc("bad format","too many packets"); - - packet = &packets[num_packets++]; - - chained = get8(s); - packet->size = get8u(s); - packet->type = get8u(s); - packet->channel = get8u(s); - - act_comp |= packet->channel; - - if (at_eof(s)) return epuc("bad file","file too short (reading packets)"); - if (packet->size != 8) return epuc("bad format","packet isn't 8bpp"); - } while (chained); - - *comp = (act_comp & 0x10 ? 4 : 3); // has alpha channel? - - for(y=0; ytype) { - default: - return epuc("bad format","packet has bad compression type"); - - case 0: {//uncompressed - int x; - - for(x=0;xchannel,dest)) - return 0; - break; - } - - case 1://Pure RLE - { - int left=width, i; - - while (left>0) { - stbi_uc count,value[4]; - - count=get8u(s); - if (at_eof(s)) return epuc("bad file","file too short (pure read count)"); - - if (count > left) - count = (stbi__uint8) left; - - if (!pic_readval(s,packet->channel,value)) return 0; - - for(i=0; ichannel,dest,value); - left -= count; - } - } - break; - - case 2: {//Mixed RLE - int left=width; - while (left>0) { - int count = get8(s), i; - if (at_eof(s)) return epuc("bad file","file too short (mixed read count)"); - - if (count >= 128) { // Repeated - stbi_uc value[4]; - int i; - - if (count==128) - count = get16(s); - else - count -= 127; - if (count > left) - return epuc("bad file","scanline overrun"); - - if (!pic_readval(s,packet->channel,value)) - return 0; - - for(i=0;ichannel,dest,value); - } else { // Raw - ++count; - if (count>left) return epuc("bad file","scanline overrun"); - - for(i=0;ichannel,dest)) - return 0; - } - left-=count; - } - break; - } - } - } - } - - return result; -} - -static stbi_uc *pic_load(stbi *s,int *px,int *py,int *comp,int req_comp) -{ - stbi_uc *result; - int i, x,y; - - for (i=0; i<92; ++i) - get8(s); - - x = get16(s); - y = get16(s); - if (at_eof(s)) return epuc("bad file","file too short (pic header)"); - if ((1 << 28) / x < y) return epuc("too large", "Image too large to decode"); - - get32(s); //skip `ratio' - get16(s); //skip `fields' - get16(s); //skip `pad' - - // intermediate buffer is RGBA - result = (stbi_uc *) malloc(x*y*4); - memset(result, 0xff, x*y*4); - - if (!pic_load2(s,x,y,comp, result)) { - free(result); - result=0; - } - *px = x; - *py = y; - if (req_comp == 0) req_comp = *comp; - result=convert_format(result,4,req_comp,x,y); - - return result; -} - -static int stbi_pic_test(stbi *s) -{ - int r = pic_test(s); - stbi_rewind(s); - return r; -} - -static stbi_uc *stbi_pic_load(stbi *s, int *x, int *y, int *comp, int req_comp) -{ - return pic_load(s,x,y,comp,req_comp); -} - -// ************************************************************************************************* -// GIF loader -- public domain by Jean-Marc Lienher -- simplified/shrunk by stb -typedef struct stbi_gif_lzw_struct { - stbi__int16 prefix; - stbi__uint8 first; - stbi__uint8 suffix; -} stbi_gif_lzw; - -typedef struct stbi_gif_struct -{ - int w,h; - stbi_uc *out; // output buffer (always 4 components) - int flags, bgindex, ratio, transparent, eflags; - stbi__uint8 pal[256][4]; - stbi__uint8 lpal[256][4]; - stbi_gif_lzw codes[4096]; - stbi__uint8 *color_table; - int parse, step; - int lflags; - int start_x, start_y; - int max_x, max_y; - int cur_x, cur_y; - int line_size; -} stbi_gif; - -static int gif_test(stbi *s) -{ - int sz; - if (get8(s) != 'G' || get8(s) != 'I' || get8(s) != 'F' || get8(s) != '8') return 0; - sz = get8(s); - if (sz != '9' && sz != '7') return 0; - if (get8(s) != 'a') return 0; - return 1; -} - -static int stbi_gif_test(stbi *s) -{ - int r = gif_test(s); - stbi_rewind(s); - return r; -} - -static void stbi_gif_parse_colortable(stbi *s, stbi__uint8 pal[256][4], int num_entries, int transp) -{ - int i; - for (i=0; i < num_entries; ++i) { - pal[i][2] = get8u(s); - pal[i][1] = get8u(s); - pal[i][0] = get8u(s); - pal[i][3] = transp ? 0 : 255; - } -} - -static int stbi_gif_header(stbi *s, stbi_gif *g, int *comp, int is_info) -{ - stbi__uint8 version; - if (get8(s) != 'G' || get8(s) != 'I' || get8(s) != 'F' || get8(s) != '8') - return e("not GIF", "Corrupt GIF"); - - version = get8u(s); - if (version != '7' && version != '9') return e("not GIF", "Corrupt GIF"); - if (get8(s) != 'a') return e("not GIF", "Corrupt GIF"); - - failure_reason = ""; - g->w = get16le(s); - g->h = get16le(s); - g->flags = get8(s); - g->bgindex = get8(s); - g->ratio = get8(s); - g->transparent = -1; - - if (comp != 0) *comp = 4; // can't actually tell whether it's 3 or 4 until we parse the comments - - if (is_info) return 1; - - if (g->flags & 0x80) - stbi_gif_parse_colortable(s,g->pal, 2 << (g->flags & 7), -1); - - return 1; -} - -static int stbi_gif_info_raw(stbi *s, int *x, int *y, int *comp) -{ - stbi_gif g; - if (!stbi_gif_header(s, &g, comp, 1)) { - stbi_rewind( s ); - return 0; - } - if (x) *x = g.w; - if (y) *y = g.h; - return 1; -} - -static void stbi_out_gif_code(stbi_gif *g, stbi__uint16 code) -{ - stbi__uint8 *p, *c; - - // recurse to decode the prefixes, since the linked-list is backwards, - // and working backwards through an interleaved image would be nasty - if (g->codes[code].prefix >= 0) - stbi_out_gif_code(g, g->codes[code].prefix); - - if (g->cur_y >= g->max_y) return; - - p = &g->out[g->cur_x + g->cur_y]; - c = &g->color_table[g->codes[code].suffix * 4]; - - if (c[3] >= 128) { - p[0] = c[2]; - p[1] = c[1]; - p[2] = c[0]; - p[3] = c[3]; - } - g->cur_x += 4; - - if (g->cur_x >= g->max_x) { - g->cur_x = g->start_x; - g->cur_y += g->step; - - while (g->cur_y >= g->max_y && g->parse > 0) { - g->step = (1 << g->parse) * g->line_size; - g->cur_y = g->start_y + (g->step >> 1); - --g->parse; - } - } -} - -static stbi__uint8 *stbi_process_gif_raster(stbi *s, stbi_gif *g) -{ - stbi__uint8 lzw_cs; - stbi__int32 len, code; - stbi__uint32 first; - stbi__int32 codesize, codemask, avail, oldcode, bits, valid_bits, clear; - stbi_gif_lzw *p; - - lzw_cs = get8u(s); - clear = 1 << lzw_cs; - first = 1; - codesize = lzw_cs + 1; - codemask = (1 << codesize) - 1; - bits = 0; - valid_bits = 0; - for (code = 0; code < clear; code++) { - g->codes[code].prefix = -1; - g->codes[code].first = (stbi__uint8) code; - g->codes[code].suffix = (stbi__uint8) code; - } - - // support no starting clear code - avail = clear+2; - oldcode = -1; - - len = 0; - for(;;) { - if (valid_bits < codesize) { - if (len == 0) { - len = get8(s); // start new block - if (len == 0) - return g->out; - } - --len; - bits |= (stbi__int32) get8(s) << valid_bits; - valid_bits += 8; - } else { - stbi__int32 code = bits & codemask; - bits >>= codesize; - valid_bits -= codesize; - // @OPTIMIZE: is there some way we can accelerate the non-clear path? - if (code == clear) { // clear code - codesize = lzw_cs + 1; - codemask = (1 << codesize) - 1; - avail = clear + 2; - oldcode = -1; - first = 0; - } else if (code == clear + 1) { // end of stream code - skip(s, len); - while ((len = get8(s)) > 0) - skip(s,len); - return g->out; - } else if (code <= avail) { - if (first) return epuc("no clear code", "Corrupt GIF"); - - if (oldcode >= 0) { - p = &g->codes[avail++]; - if (avail > 4096) return epuc("too many codes", "Corrupt GIF"); - p->prefix = (stbi__int16) oldcode; - p->first = g->codes[oldcode].first; - p->suffix = (code == avail) ? p->first : g->codes[code].first; - } else if (code == avail) - return epuc("illegal code in raster", "Corrupt GIF"); - - stbi_out_gif_code(g, (stbi__uint16) code); - - if ((avail & codemask) == 0 && avail <= 0x0FFF) { - codesize++; - codemask = (1 << codesize) - 1; - } - - oldcode = code; - } else { - return epuc("illegal code in raster", "Corrupt GIF"); - } - } - } -} - -static void stbi_fill_gif_background(stbi_gif *g) -{ - int i; - stbi__uint8 *c = g->pal[g->bgindex]; - // @OPTIMIZE: write a dword at a time - for (i = 0; i < g->w * g->h * 4; i += 4) { - stbi__uint8 *p = &g->out[i]; - p[0] = c[2]; - p[1] = c[1]; - p[2] = c[0]; - p[3] = c[3]; - } -} - -// this function is designed to support animated gifs, although stb_image doesn't support it -static stbi__uint8 *stbi_gif_load_next(stbi *s, stbi_gif *g, int *comp, int req_comp) -{ - int i; - stbi__uint8 *old_out = 0; - - if (g->out == 0) { - if (!stbi_gif_header(s, g, comp,0)) return 0; // failure_reason set by stbi_gif_header - g->out = (stbi__uint8 *) malloc(4 * g->w * g->h); - if (g->out == 0) return epuc("outofmem", "Out of memory"); - stbi_fill_gif_background(g); - } else { - // animated-gif-only path - if (((g->eflags & 0x1C) >> 2) == 3) { - old_out = g->out; - g->out = (stbi__uint8 *) malloc(4 * g->w * g->h); - if (g->out == 0) return epuc("outofmem", "Out of memory"); - memcpy(g->out, old_out, g->w*g->h*4); - } - } - - for (;;) { - switch (get8(s)) { - case 0x2C: /* Image Descriptor */ - { - stbi__int32 x, y, w, h; - stbi__uint8 *o; - - x = get16le(s); - y = get16le(s); - w = get16le(s); - h = get16le(s); - if (((x + w) > (g->w)) || ((y + h) > (g->h))) - return epuc("bad Image Descriptor", "Corrupt GIF"); - - g->line_size = g->w * 4; - g->start_x = x * 4; - g->start_y = y * g->line_size; - g->max_x = g->start_x + w * 4; - g->max_y = g->start_y + h * g->line_size; - g->cur_x = g->start_x; - g->cur_y = g->start_y; - - g->lflags = get8(s); - - if (g->lflags & 0x40) { - g->step = 8 * g->line_size; // first interlaced spacing - g->parse = 3; - } else { - g->step = g->line_size; - g->parse = 0; - } - - if (g->lflags & 0x80) { - stbi_gif_parse_colortable(s,g->lpal, 2 << (g->lflags & 7), g->eflags & 0x01 ? g->transparent : -1); - g->color_table = (stbi__uint8 *) g->lpal; - } else if (g->flags & 0x80) { - for (i=0; i < 256; ++i) // @OPTIMIZE: reset only the previous transparent - g->pal[i][3] = 255; - if (g->transparent >= 0 && (g->eflags & 0x01)) - g->pal[g->transparent][3] = 0; - g->color_table = (stbi__uint8 *) g->pal; - } else - return epuc("missing color table", "Corrupt GIF"); - - o = stbi_process_gif_raster(s, g); - if (o == NULL) return NULL; - - if (req_comp && req_comp != 4) - o = convert_format(o, 4, req_comp, g->w, g->h); - return o; - } - - case 0x21: // Comment Extension. - { - int len; - if (get8(s) == 0xF9) { // Graphic Control Extension. - len = get8(s); - if (len == 4) { - g->eflags = get8(s); - get16le(s); // delay - g->transparent = get8(s); - } else { - skip(s, len); - break; - } - } - while ((len = get8(s)) != 0) - skip(s, len); - break; - } - - case 0x3B: // gif stream termination code - return (stbi__uint8 *) 1; - - default: - return epuc("unknown code", "Corrupt GIF"); - } - } -} - -static stbi_uc *stbi_gif_load(stbi *s, int *x, int *y, int *comp, int req_comp) -{ - stbi__uint8 *u = 0; - stbi_gif g={0}; - - u = stbi_gif_load_next(s, &g, comp, req_comp); - if (u == (void *) 1) u = 0; // end of animated gif marker - if (u) { - *x = g.w; - *y = g.h; - } - - return u; -} - -static int stbi_gif_info(stbi *s, int *x, int *y, int *comp) -{ - return stbi_gif_info_raw(s,x,y,comp); -} - - -// ************************************************************************************************* -// Radiance RGBE HDR loader -// originally by Nicolas Schulz -#ifndef STBI_NO_HDR -static int hdr_test(stbi *s) -{ - const char *signature = "#?RADIANCE\n"; - int i; - for (i=0; signature[i]; ++i) - if (get8(s) != signature[i]) - return 0; - return 1; -} - -static int stbi_hdr_test(stbi* s) -{ - int r = hdr_test(s); - stbi_rewind(s); - return r; -} - -#define HDR_BUFLEN 1024 -static char *hdr_gettoken(stbi *z, char *buffer) -{ - int len=0; - char c = '\0'; - - c = (char) get8(z); - - while (!at_eof(z) && c != '\n') { - buffer[len++] = c; - if (len == HDR_BUFLEN-1) { - // flush to end of line - while (!at_eof(z) && get8(z) != '\n') - ; - break; - } - c = (char) get8(z); - } - - buffer[len] = 0; - return buffer; -} - -static void hdr_convert(float *output, stbi_uc *input, int req_comp) -{ - if ( input[3] != 0 ) { - float f1; - // Exponent - f1 = (float) ldexp(1.0f, input[3] - (int)(128 + 8)); - if (req_comp <= 2) - output[0] = (input[0] + input[1] + input[2]) * f1 / 3; - else { - output[0] = input[0] * f1; - output[1] = input[1] * f1; - output[2] = input[2] * f1; - } - if (req_comp == 2) output[1] = 1; - if (req_comp == 4) output[3] = 1; - } else { - switch (req_comp) { - case 4: output[3] = 1; /* fallthrough */ - case 3: output[0] = output[1] = output[2] = 0; - break; - case 2: output[1] = 1; /* fallthrough */ - case 1: output[0] = 0; - break; - } - } -} - -static float *hdr_load(stbi *s, int *x, int *y, int *comp, int req_comp) -{ - char buffer[HDR_BUFLEN]; - char *token; - int valid = 0; - int width, height; - stbi_uc *scanline; - float *hdr_data; - int len; - unsigned char count, value; - int i, j, k, c1,c2, z; - - - // Check identifier - if (strcmp(hdr_gettoken(s,buffer), "#?RADIANCE") != 0) - return epf("not HDR", "Corrupt HDR image"); - - // Parse header - for(;;) { - token = hdr_gettoken(s,buffer); - if (token[0] == 0) break; - if (strcmp(token, "FORMAT=32-bit_rle_rgbe") == 0) valid = 1; - } - - if (!valid) return epf("unsupported format", "Unsupported HDR format"); - - // Parse width and height - // can't use sscanf() if we're not using stdio! - token = hdr_gettoken(s,buffer); - if (strncmp(token, "-Y ", 3)) return epf("unsupported data layout", "Unsupported HDR format"); - token += 3; - height = (int) strtol(token, &token, 10); - while (*token == ' ') ++token; - if (strncmp(token, "+X ", 3)) return epf("unsupported data layout", "Unsupported HDR format"); - token += 3; - width = (int) strtol(token, NULL, 10); - - *x = width; - *y = height; - - *comp = 3; - if (req_comp == 0) req_comp = 3; - - // Read data - hdr_data = (float *) malloc(height * width * req_comp * sizeof(float)); - - // Load image data - // image data is stored as some number of sca - if ( width < 8 || width >= 32768) { - // Read flat data - for (j=0; j < height; ++j) { - for (i=0; i < width; ++i) { - stbi_uc rgbe[4]; - main_decode_loop: - getn(s, rgbe, 4); - hdr_convert(hdr_data + j * width * req_comp + i * req_comp, rgbe, req_comp); - } - } - } else { - // Read RLE-encoded data - scanline = NULL; - - for (j = 0; j < height; ++j) { - c1 = get8(s); - c2 = get8(s); - len = get8(s); - if (c1 != 2 || c2 != 2 || (len & 0x80)) { - // not run-length encoded, so we have to actually use THIS data as a decoded - // pixel (note this can't be a valid pixel--one of RGB must be >= 128) - stbi__uint8 rgbe[4]; - rgbe[0] = (stbi__uint8) c1; - rgbe[1] = (stbi__uint8) c2; - rgbe[2] = (stbi__uint8) len; - rgbe[3] = (stbi__uint8) get8u(s); - hdr_convert(hdr_data, rgbe, req_comp); - i = 1; - j = 0; - free(scanline); - goto main_decode_loop; // yes, this makes no sense - } - len <<= 8; - len |= get8(s); - if (len != width) { free(hdr_data); free(scanline); return epf("invalid decoded scanline length", "corrupt HDR"); } - if (scanline == NULL) scanline = (stbi_uc *) malloc(width * 4); - - for (k = 0; k < 4; ++k) { - i = 0; - while (i < width) { - count = get8u(s); - if (count > 128) { - // Run - value = get8u(s); - count -= 128; - for (z = 0; z < count; ++z) - scanline[i++ * 4 + k] = value; - } else { - // Dump - for (z = 0; z < count; ++z) - scanline[i++ * 4 + k] = get8u(s); - } - } - } - for (i=0; i < width; ++i) - hdr_convert(hdr_data+(j*width + i)*req_comp, scanline + i*4, req_comp); - } - free(scanline); - } - - return hdr_data; -} - -static float *stbi_hdr_load(stbi *s, int *x, int *y, int *comp, int req_comp) -{ - return hdr_load(s,x,y,comp,req_comp); -} - -static int stbi_hdr_info(stbi *s, int *x, int *y, int *comp) -{ - char buffer[HDR_BUFLEN]; - char *token; - int valid = 0; - - if (strcmp(hdr_gettoken(s,buffer), "#?RADIANCE") != 0) { - stbi_rewind( s ); - return 0; - } - - for(;;) { - token = hdr_gettoken(s,buffer); - if (token[0] == 0) break; - if (strcmp(token, "FORMAT=32-bit_rle_rgbe") == 0) valid = 1; - } - - if (!valid) { - stbi_rewind( s ); - return 0; - } - token = hdr_gettoken(s,buffer); - if (strncmp(token, "-Y ", 3)) { - stbi_rewind( s ); - return 0; - } - token += 3; - *y = (int) strtol(token, &token, 10); - while (*token == ' ') ++token; - if (strncmp(token, "+X ", 3)) { - stbi_rewind( s ); - return 0; - } - token += 3; - *x = (int) strtol(token, NULL, 10); - *comp = 3; - return 1; -} -#endif // STBI_NO_HDR - -static int stbi_bmp_info(stbi *s, int *x, int *y, int *comp) -{ - int hsz; - if (get8(s) != 'B' || get8(s) != 'M') { - stbi_rewind( s ); - return 0; - } - skip(s,12); - hsz = get32le(s); - if (hsz != 12 && hsz != 40 && hsz != 56 && hsz != 108) { - stbi_rewind( s ); - return 0; - } - if (hsz == 12) { - *x = get16le(s); - *y = get16le(s); - } else { - *x = get32le(s); - *y = get32le(s); - } - if (get16le(s) != 1) { - stbi_rewind( s ); - return 0; - } - *comp = get16le(s) / 8; - return 1; -} - -static int stbi_psd_info(stbi *s, int *x, int *y, int *comp) -{ - int channelCount; - if (get32(s) != 0x38425053) { - stbi_rewind( s ); - return 0; - } - if (get16(s) != 1) { - stbi_rewind( s ); - return 0; - } - skip(s, 6); - channelCount = get16(s); - if (channelCount < 0 || channelCount > 16) { - stbi_rewind( s ); - return 0; - } - *y = get32(s); - *x = get32(s); - if (get16(s) != 8) { - stbi_rewind( s ); - return 0; - } - if (get16(s) != 3) { - stbi_rewind( s ); - return 0; - } - *comp = 4; - return 1; -} - -static int stbi_pic_info(stbi *s, int *x, int *y, int *comp) -{ - int act_comp=0,num_packets=0,chained; - pic_packet_t packets[10]; - - skip(s, 92); - - *x = get16(s); - *y = get16(s); - if (at_eof(s)) return 0; - if ( (*x) != 0 && (1 << 28) / (*x) < (*y)) { - stbi_rewind( s ); - return 0; - } - - skip(s, 8); - - do { - pic_packet_t *packet; - - if (num_packets==sizeof(packets)/sizeof(packets[0])) - return 0; - - packet = &packets[num_packets++]; - chained = get8(s); - packet->size = get8u(s); - packet->type = get8u(s); - packet->channel = get8u(s); - act_comp |= packet->channel; - - if (at_eof(s)) { - stbi_rewind( s ); - return 0; - } - if (packet->size != 8) { - stbi_rewind( s ); - return 0; - } - } while (chained); - - *comp = (act_comp & 0x10 ? 4 : 3); - - return 1; -} - -static int stbi_info_main(stbi *s, int *x, int *y, int *comp) -{ - if (stbi_jpeg_info(s, x, y, comp)) - return 1; - if (stbi_png_info(s, x, y, comp)) - return 1; - if (stbi_gif_info(s, x, y, comp)) - return 1; - if (stbi_bmp_info(s, x, y, comp)) - return 1; - if (stbi_psd_info(s, x, y, comp)) - return 1; - if (stbi_pic_info(s, x, y, comp)) - return 1; - #ifndef STBI_NO_HDR - if (stbi_hdr_info(s, x, y, comp)) - return 1; - #endif - // test tga last because it's a crappy test! - if (stbi_tga_info(s, x, y, comp)) - return 1; - return e("unknown image type", "Image not of any known type, or corrupt"); -} - -#ifndef STBI_NO_STDIO -int stbi_info(char const *filename, int *x, int *y, int *comp) -{ - FILE *f = fopen(filename, "rb"); - int result; - if (!f) return e("can't fopen", "Unable to open file"); - result = stbi_info_from_file(f, x, y, comp); - fclose(f); - return result; -} - -int stbi_info_from_file(FILE *f, int *x, int *y, int *comp) -{ - int r; - stbi s; - long pos = ftell(f); - start_file(&s, f); - r = stbi_info_main(&s,x,y,comp); - fseek(f,pos,SEEK_SET); - return r; -} -#endif // !STBI_NO_STDIO - -int stbi_info_from_memory(stbi_uc const *buffer, int len, int *x, int *y, int *comp) -{ - stbi s; - start_mem(&s,buffer,len); - return stbi_info_main(&s,x,y,comp); -} - -int stbi_info_from_callbacks(stbi_io_callbacks const *c, void *user, int *x, int *y, int *comp) -{ - stbi s; - start_callbacks(&s, (stbi_io_callbacks *) c, user); - return stbi_info_main(&s,x,y,comp); -} - -#endif // STBI_HEADER_FILE_ONLY - -#if !defined(STBI_NO_STDIO) && defined(_MSC_VER) && _MSC_VER >= 1400 -#pragma warning(pop) -#endif - - -/* - revision history: - 1.35 (2014-05-27) - various warnings - fix broken STBI_SIMD path - fix bug where stbi_load_from_file no longer left file pointer in correct place - fix broken non-easy path for 32-bit BMP (possibly never used) - TGA optimization by Arseny Kapoulkine - 1.34 (unknown) - use STBI_NOTUSED in resample_row_generic(), fix one more leak in tga failure case - 1.33 (2011-07-14) - make stbi_is_hdr work in STBI_NO_HDR (as specified), minor compiler-friendly improvements - 1.32 (2011-07-13) - support for "info" function for all supported filetypes (SpartanJ) - 1.31 (2011-06-20) - a few more leak fixes, bug in PNG handling (SpartanJ) - 1.30 (2011-06-11) - added ability to load files via callbacks to accomidate custom input streams (Ben Wenger) - removed deprecated format-specific test/load functions - removed support for installable file formats (stbi_loader) -- would have been broken for IO callbacks anyway - error cases in bmp and tga give messages and don't leak (Raymond Barbiero, grisha) - fix inefficiency in decoding 32-bit BMP (David Woo) - 1.29 (2010-08-16) - various warning fixes from Aurelien Pocheville - 1.28 (2010-08-01) - fix bug in GIF palette transparency (SpartanJ) - 1.27 (2010-08-01) - cast-to-stbi__uint8 to fix warnings - 1.26 (2010-07-24) - fix bug in file buffering for PNG reported by SpartanJ - 1.25 (2010-07-17) - refix trans_data warning (Won Chun) - 1.24 (2010-07-12) - perf improvements reading from files on platforms with lock-heavy fgetc() - minor perf improvements for jpeg - deprecated type-specific functions so we'll get feedback if they're needed - attempt to fix trans_data warning (Won Chun) - 1.23 fixed bug in iPhone support - 1.22 (2010-07-10) - removed image *writing* support - stbi_info support from Jetro Lauha - GIF support from Jean-Marc Lienher - iPhone PNG-extensions from James Brown - warning-fixes from Nicolas Schulz and Janez Zemva (i.e. Janez (U+017D)emva) - 1.21 fix use of 'stbi__uint8' in header (reported by jon blow) - 1.20 added support for Softimage PIC, by Tom Seddon - 1.19 bug in interlaced PNG corruption check (found by ryg) - 1.18 2008-08-02 - fix a threading bug (local mutable static) - 1.17 support interlaced PNG - 1.16 major bugfix - convert_format converted one too many pixels - 1.15 initialize some fields for thread safety - 1.14 fix threadsafe conversion bug - header-file-only version (#define STBI_HEADER_FILE_ONLY before including) - 1.13 threadsafe - 1.12 const qualifiers in the API - 1.11 Support installable IDCT, colorspace conversion routines - 1.10 Fixes for 64-bit (don't use "unsigned long") - optimized upsampling by Fabian "ryg" Giesen - 1.09 Fix format-conversion for PSD code (bad global variables!) - 1.08 Thatcher Ulrich's PSD code integrated by Nicolas Schulz - 1.07 attempt to fix C++ warning/errors again - 1.06 attempt to fix C++ warning/errors again - 1.05 fix TGA loading to return correct *comp and use good luminance calc - 1.04 default float alpha is 1, not 255; use 'void *' for stbi_image_free - 1.03 bugfixes to STBI_NO_STDIO, STBI_NO_HDR - 1.02 support for (subset of) HDR files, float interface for preferred access to them - 1.01 fix bug: possible bug in handling right-side up bmps... not sure - fix bug: the stbi_bmp_load() and stbi_tga_load() functions didn't work at all - 1.00 interface to zlib that skips zlib header - 0.99 correct handling of alpha in palette - 0.98 TGA loader by lonesock; dynamically add loaders (untested) - 0.97 jpeg errors on too large a file; also catch another malloc failure - 0.96 fix detection of invalid v value - particleman@mollyrocket forum - 0.95 during header scan, seek to markers in case of padding - 0.94 STBI_NO_STDIO to disable stdio usage; rename all #defines the same - 0.93 handle jpegtran output; verbose errors - 0.92 read 4,8,16,24,32-bit BMP files of several formats - 0.91 output 24-bit Windows 3.0 BMP files - 0.90 fix a few more warnings; bump version number to approach 1.0 - 0.61 bugfixes due to Marc LeBlanc, Christopher Lloyd - 0.60 fix compiling as c++ - 0.59 fix warnings: merge Dave Moore's -Wall fixes - 0.58 fix bug: zlib uncompressed mode len/nlen was wrong endian - 0.57 fix bug: jpg last huffman symbol before marker was >9 bits but less than 16 available - 0.56 fix bug: zlib uncompressed mode len vs. nlen - 0.55 fix bug: restart_interval not initialized to 0 - 0.54 allow NULL for 'int *comp' - 0.53 fix bug in png 3->4; speedup png decoding - 0.52 png handles req_comp=3,4 directly; minor cleanup; jpeg comments - 0.51 obey req_comp requests, 1-component jpegs return as 1-component, - on 'test' only check type, not whether we support this variant - 0.50 first released version -*/ diff --git a/impeller/third_party/stb/stb/deprecated/stretchy_buffer.txt b/impeller/third_party/stb/stb/deprecated/stretchy_buffer.txt deleted file mode 100644 index dcd747e195b2f..0000000000000 --- a/impeller/third_party/stb/stb/deprecated/stretchy_buffer.txt +++ /dev/null @@ -1,28 +0,0 @@ -// stretchy buffer // init: NULL // free: sbfree() // push_back: sbpush() // size: sbcount() // -#define sbfree(a) ((a) ? free(stb__sbraw(a)),0 : 0) -#define sbpush(a,v) (stb__sbmaybegrow(a,1), (a)[stb__sbn(a)++] = (v)) -#define sbcount(a) ((a) ? stb__sbn(a) : 0) -#define sbadd(a,n) (stb__sbmaybegrow(a,n), stb__sbn(a)+=(n), &(a)[stb__sbn(a)-(n)]) -#define sblast(a) ((a)[stb__sbn(a)-1]) - -#include -#define stb__sbraw(a) ((int *) (a) - 2) -#define stb__sbm(a) stb__sbraw(a)[0] -#define stb__sbn(a) stb__sbraw(a)[1] - -#define stb__sbneedgrow(a,n) ((a)==0 || stb__sbn(a)+n >= stb__sbm(a)) -#define stb__sbmaybegrow(a,n) (stb__sbneedgrow(a,(n)) ? stb__sbgrow(a,n) : 0) -#define stb__sbgrow(a,n) stb__sbgrowf((void **) &(a), (n), sizeof(*(a))) - -static void stb__sbgrowf(void **arr, int increment, int itemsize) -{ - int m = *arr ? 2*stb__sbm(*arr)+increment : increment+1; - void *p = realloc(*arr ? stb__sbraw(*arr) : 0, itemsize * m + sizeof(int)*2); - assert(p); - if (p) { - if (!*arr) ((int *) p)[1] = 0; - *arr = (void *) ((int *) p + 2); - stb__sbm(*arr) = m; - } -} - diff --git a/impeller/third_party/stb/stb/docs/other_libs.md b/impeller/third_party/stb/stb/docs/other_libs.md deleted file mode 100644 index d50fe164c46ff..0000000000000 --- a/impeller/third_party/stb/stb/docs/other_libs.md +++ /dev/null @@ -1,181 +0,0 @@ -# Other single-file public-domain/open source libraries with minimal dependencies - -In addition to all of [my libraries](https://github.com/nothings/stb), there are other, similar libraries. - -The following is a list of small, easy-to-integrate, portable libraries -which are usable from C and/or C++, and should be able to be compiled on both -32-bit and 64-bit platforms. - -### Rules - -- Libraries must be usable from C or C++, ideally both -- Libraries should be usable from more than one platform (ideally, all major desktops and/or all major mobile) -- Libraries should compile and work on both 32-bit and 64-bit platforms -- Libraries should use at most two files - -Exceptions will be allowed for good reasons. - -### New libraries and corrections - -See discussion after the list. - -## Library listing - -**Public domain single-file libraries usable from C and C++ are in bold.** Other -libraries are either non-public domain, or two files, or not usable from both C and C++, or -all three. Libraries of more than two files are mostly forbidden. - -For the API column, "C" means C only, "C++" means C++ only, and "C/C++" means C/C++ usable -from either; some files may require *building* as C or C++ but still qualify as "C/C++" as -long as the header file uses `extern "C"` to make it work. (In some cases, a header-file-only -library may compile as both C or C++, but produce an implementation that can only be called from -one or the other, because of a lack of use of `extern "C"`; in this case the table still qualifies it -as C/C++, as this is not an obstacle to most users.) - - -category | library | license | API |files| description ------------------ | --------------------------------------------------------------------- |:--------------------:|:---:|:---:| ----------- -AI | [micropather](http://www.grinninglizard.com/MicroPather/) | zlib | C++ | 2 | pathfinding with A* -argv | [parg](https://github.com/jibsen/parg) | **public domain** | C | 1 | argv parsing -audio | [aw_ima.h](https://github.com/afterwise/aw-ima/blob/master/aw-ima.h) | MIT |C/C++|**1**| IMA-ADPCM audio decoder -audio |**[dr_flac](https://github.com/mackron/dr_libs)** | **public domain** |C/C++|**1**| FLAC audio decoder -compression |**[miniz.c](https://github.com/richgel999/miniz)** |**public domain**|C/C++|**1**| compression,decompression, zip file, png writing -compression | [lz4](https://github.com/Cyan4973/lz4) | BSD |C/C++| 2 | fast but larger LZ compression -compression | [fastlz](https://code.google.com/archive/p/fastlz/source/default/source) | MIT |C/C++| 2 | fast but larger LZ compression -compression | [pithy](https://github.com/johnezang/pithy) | BSD |C/C++| 2 | fast but larger LZ compression -crypto | [TweetNaCl](http://tweetnacl.cr.yp.to/software.html) | **public domain** | C | 2 | high-quality tiny cryptography library -data structures|[klib](http://attractivechaos.github.io/klib/) | MIT |C/C++| 2 | many 2-file libs: hash, sort, b-tree, etc -data structures | [uthash](https://github.com/troydhanson/uthash) | BSD |C/C++| 2 | several 1-header, 1-license-file libs: generic hash, list, etc -data structures | [PackedArray](https://github.com/gpakosz/PackedArray) | **WTFPLv2** | C | 2 | memory-efficient array of elements with non-pow2 bitcount -data structures | [minilibs](https://github.com/ccxvii/minilibs) | **public domain** | C | 2 | two-file binary tress (also regex, etc) -files & filenames |**[DG_misc.h](https://github.com/DanielGibson/Snippets/)** | **public domain** |C/C++|**1**| Daniel Gibson's stb.h-esque cross-platform helpers: path/file, strings -files & filenames | [whereami](https://github.com/gpakosz/whereami) |**WTFPLv2** |C/C++| 2 | get path/filename of executable or module -files & filenames | [noc_file_dialog.h](https://github.com/guillaumechereau/noc) | MIT |C/C++| 1 | file open/save dialogs (Linux/OSX/Windows) -files & filenames | [dirent](https://github.com/tronkko/dirent) | MIT |C/C++|**1**| dirent for windows: retrieve file & dir info -files & filenames | [TinyDir](https://github.com/cxong/tinydir) | BSD | C |**1**| cross-platform directory reader -geometry file | [tk_objfile](https://github.com/joeld42/tk_objfile) | MIT |C/C++|**1**| OBJ file loader -geometry file | [tinyply](https://github.com/ddiakopoulos/tinyply) | **public domain** | C++ | 2 | PLY mesh file loader -geometry file | [tinyobjloader](https://github.com/syoyo/tinyobjloader) | BSD | C++ |**1**| wavefront OBJ file loader -geometry math |**[nv_voronoi.h](http://www.icculus.org/~mordred/nvlib/)** | **public domain** |C/C++|**1**| find voronoi regions on lattice w/ integer inputs -geometry math |**[sobol.h](https://github.com/Marc-B-Reynolds/Stand-alone-junk/)** | **public domain** |C/C++|**1**| sobol & stratified sampling sequences -geometry math | [sdf.h](https://github.com/memononen/SDF) | MIT |C/C++|**1**| compute signed-distance field from antialiased image -geometry math | [nanoflann](https://github.com/jlblancoc/nanoflann) | BSD | C++ |**1**| build KD trees for point clouds -geometry math | [jc_voronoi](https://github.com/JCash/voronoi) | MIT |C/C++|**1**| find voronoi regions on float/double data -geometry math | [par_msquares](https://github.com/prideout/par) | MIT |C/C++|**1**| convert (binarized) image to triangles -geometry math | [par_shapes](http://github.prideout.net/shapes) | MIT |C/C++|**1**| generate various 3d geometric shapes -geometry math | [Tomas Akenine-Moller snippets](http://tinyurl.com/ht79ndj) | **public domain** |C/C++| 2 | various 3D intersection calculations, not lib-ified -geometry math | [Clipper](http://www.angusj.com/delphi/clipper.php) | Boost | C++ | 2 | line & polygon clipping & offsetting -geometry math | [PolyPartition](https://github.com/ivanfratric/polypartition) | MIT | C++ | 2 | polygon triangulation, partitioning -geometry math | [Voxelizer](https://github.com/karimnaaji/voxelizer) | MIT |C/C++|**1**| convert triangle mesh to voxel triangle mesh -graphics (2d) | [blendish](https://bitbucket.org/duangle/oui-blendish/src) | MIT |C/C++|**1**| blender-style widget rendering -graphics (2d) | [tigr](https://bitbucket.org/rmitton/tigr/src) | **public domain** |C/C++| 2 | quick-n-dirty window text/graphics for Windows -graphics (2d) | [noc_turtle](https://github.com/guillaumechereau/noc) | MIT |C/C++| 2 | procedural graphics generator -graphics (3-D) | [mikktspace](http://tinyurl.com/z6xtucm) | zlib |C/C++| 2 | compute tangent space for normal mapping -graphics (3-D) | [debug-draw](https://github.com/glampert/debug-draw) | **public domain** | C++ |**1**| API-agnostic immediate-mode debug rendering -hardware |**[EasyTab](https://github.com/ApoorvaJ/EasyTab)** | **public domain** |C/C++|**1**| multi-platform tablet input -images | [jo_gif.cpp](http://www.jonolick.com/home/gif-writer) | **public domain** | C++ |**1**| animated GIF writer (CPP file can also be used as H file) -images |**[gif.h](https://github.com/ginsweater/gif-h)** | **public domain** | C |**1**| animated GIF writer (can only include once) -images |**[tiny_jpeg.h](https://github.com/serge-rgb/TinyJPEG/)** | **public domain** |C/C++|**1**| JPEG encoder -images | [miniexr](https://github.com/aras-p/miniexr) | **public domain** | C++ | 2 | OpenEXR writer, needs header file -images | [tinyexr](https://github.com/syoyo/tinyexr) | BSD |C/C++|**1**| EXR image read/write, uses miniz internally -images | [lodepng](http://lodev.org/lodepng/) | zlib |C/C++| 2 | PNG encoder/decoder -images | [nanoSVG](https://github.com/memononen/nanosvg) | zlib |C/C++|**1**| 1-file SVG parser; 1-file SVG rasterizer -images | [picopng.cpp](http://lodev.org/lodepng/picopng.cpp) | zlib | C++ | 2 | tiny PNG loader -images | [jpeg-compressor](https://github.com/richgel999/jpeg-compressor) | **public domain** | C++ | 2 | 2-file jpeg compress, 2-file jpeg decompress -images | [easyexif](https://github.com/mayanklahiri/easyexif) | MIT | C++ | 2 | EXIF metadata extractor for JPEG images -images |**[cro_mipmap.h](https://github.com/thebeast33/cro_lib)** | **public domain** |C/C++|**1**| average, min, max mipmap generators -math | [mm_vec.h](https://github.com/vurtun/mmx) | BSD |C/C++|**1**| SIMD vector math -math | [ShaderFastLibs](https://github.com/michaldrobot/ShaderFastLibs) | MIT | C++ |**1**| (also HLSL) approximate transcendental functions optimized for shaders (esp. GCN) -math | [TinyExpr](https://github.com/codeplea/tinyexpr) | zlib | C | 2 | evaluation of math expressions from strings -math | [linalg.h](https://github.com/sgorsten/linalg) | **unlicense** | C++ |**1**| vector/matrix/quaternion math -math | [PoissonGenerator.h](https://github.com/corporateshark/poisson-disk-generator) | MIT | C++ |**1**| Poisson disk points generator (disk or rect) -multithreading | [mm_sched.h](https://github.com/vurtun/mmx) | zlib |C/C++|**1**| cross-platform multithreaded task scheduler -network |**[zed_net](https://github.com/ZedZull/zed_net)** | **public domain** |C/C++|**1**| cross-platform socket wrapper -network | [mm_web.h](https://github.com/vurtun/mmx) | BSD |C/C++|**1**| lightweight webserver, fork of webby -network | [par_easycurl.h](https://github.com/prideout/par) | MIT |C/C++|**1**| curl wrapper -network | [yocto](https://github.com/tom-seddon/yhs) | **public domain** |C/C++| 2 | non-production-use http server -network | [happyhttp](http://scumways.com/happyhttp/happyhttp.html) | zlib | C++ | 2 | http client requests -network | [mongoose](https://github.com/cesanta/mongoose) |_GPLv2_ |C/C++| 2 | http server -network | [LUrlParser](https://github.com/corporateshark/LUrlParser) | MIT | C++ | 2 | lightweight URL & URI parser RFC 1738, RFC 3986 -parsing | [SLRE](https://github.com/cesanta/slre) |_GPLv2_ |C/C++|**1**| regular expression matcher -parsing | [PicoJSON](https://github.com/kazuho/picojson) | BSD | C++ |**1**| JSON parse/serializer -parsing | [mm_lexer.h](https://github.com/vurtun/mmx) | zlib |C/C++|**1**| C-esque language lexer -parsing | [json.h](https://github.com/sheredom/json.h) | **public domain** |C/C++| 2 | JSON parser -parsing | [jzon.h](https://github.com/Zguy/Jzon) | MIT | C++ | 2 | JSON parser -parsing | [parson](https://github.com/kgabis/parson) | MIT |C/C++| 2 | JSON parser and serializer -parsing | [minilibs](https://github.com/ccxvii/minilibs) | **public domain** | C | 2 | two-file regex (also binary tree, etc) -profiling | [Remotery](https://github.com/Celtoys/Remotery) | Apache 2.0 |C/C++| 2 | CPU/GPU profiler Win/Mac/Linux, using web browser for viewer -profiling | [MicroProfile](https://bitbucket.org/jonasmeyer/microprofile) | **unlicense** | C++ | 2-4 | CPU (and GPU?) profiler, 1-3 header files, uses miniz internally -scripting | [LIL](http://runtimelegend.com/rep/lil/) | zlib |C/C++| 2 | interpreter for a Tcl-like scripting language -scripting | [lualite](https://github.com/janezz55/lualite/) | MIT | C++ |**1**| generate lua bindings in C++ -scripting | [Picol](https://chiselapp.com/user/dbohdan/repository/picol/) | BSD |C/C++|**1**| interpreter for a Tcl-like scripting language -strings |**[DG_misc.h](https://github.com/DanielGibson/Snippets/)** | **public domain** |C/C++|**1**| Daniel Gibson's stb.h-esque cross-platform helpers: path/file, strings -strings |**[utf8](https://github.com/sheredom/utf8.h)** | **public domain** |C/C++|**1**| utf8 string library -strings |**[strpool.h](https://github.com/mattiasgustavsson/libs)** | **public domain** |C/C++|**1**| string interning -strings | [dfa](http://bjoern.hoehrmann.de/utf-8/decoder/dfa/) | MIT |C/C++| 2 | fast utf8 decoder (need a header file) -strings |**[gb_string.h](https://github.com/gingerBill/gb)** | **public domain** |C/C++|**1**| dynamic strings -tests | [utest](https://github.com/evolutional/utest) | MIT |C/C++|**1**| unit testing -tests | [catch](https://github.com/philsquared/Catch) | Boost | C++ |**1**| unit testing -tests | [SPUT](http://www.lingua-systems.com/unit-testing/) | BSD |C/C++|**1**| unit testing -tests | [pempek_assert.cpp](https://github.com/gpakosz/Assert) | **WTFPLv2** | C++ | 2 | flexible assertions -tests | [minctest](https://github.com/codeplea/minctest) | zlib | C |**1**| unit testing -tests | [greatest](https://github.com/silentbicycle/greatest) | iSC | C |**1**| unit testing -tests | [µnit](https://github.com/nemequ/munit) | MIT | C |**1**| unit testing -user interface | [dear imgui](https://github.com/ocornut/imgui) | MIT | C++*| 9 | an immediate-mode GUI formerly named "ImGui"; [3rd-party C wrapper](https://github.com/Extrawurst/cimgui) -_misc_ | [MakeID.h](http://www.humus.name/3D/MakeID.h) | **public domain** | C++ |**1**| allocate/deallocate small integer IDs efficiently -_misc_ | [loguru](https://github.com/emilk/loguru) | **public domain** | C++ |**1**| flexible logging -_misc_ | [tinyformat](https://github.com/c42f/tinyformat) | Boost | C++ |**1**| typesafe printf -_misc_ | [dbgtools](https://github.com/wc-duck/dbgtools) | zlib |C/C++| 2 | cross-platform debug util libraries -_misc_ | [stmr](https://github.com/wooorm/stmr.c) | MIT | C | 2 | extract English word stems -_misc_ | [levenshtein](https://github.com/wooorm/levenshtein.c) | MIT | C | 2 | compute edit distance between two strings - -There are also these XML libraries, but if you're using XML, shame on you: - -- parsing: [tinyxml2](https://github.com/leethomason/tinyxml2): XML -- parsing: [pugixml](http://pugixml.org/): XML (MIT license) - -Also you might be interested in other related, but different lists: - -- [clib](https://github.com/clibs/clib/wiki/Packages): list of (mostly) small single C functions (licenses not listed) - -## New libraries and corrections - -Submissions of new libraries: I accept submissions (as issues or as pull requests). Please -note that every file that must be included in a user's project counts; a header and a source -file is 2 files, but a header file, source file, and LICENSE (if the license isn't in the -source file) is 3 files, and won't be accepted, because it's not 2 files. But actually -'LICENSE' is a problem for just dropping the library in a source tree anyway, since it's -not scoped to just the library, so library authors are encouraged to include the license in the -source file and not require a separate LICENSE. - -Corrections: if information for a library above is wrong, please send a correction as an -issue, pull request, or email. Note that if the list indicates a library works from both -C/C++, but it doesn't, this could be an error in the list or it could be a bug in the -library. If you find a library doesn't work in 32-bit or 64-bit, the library should be -removed from this list, unless it's a bug in the library. - -## *List FAQ* - -### Can I link directly to this list? - -Yes, you can just use this page. If you want a shorter, more readable link, you can use [this URL](https://github.com/nothings/stb#other_libs) to link to the FAQ question that links to this page. - -### Why isn't library XXX which is made of 3 or more files on this list? - -I draw the line arbitrarily at 2 files at most. (Note that some libraries that appear to -be two files require a separate LICENSE file, which made me leave them out). Some of these -libraries are still easy to drop into your project and build, so you might still be ok with them. -But since people come to stb for single-file public domain libraries, I feel that starts -to get too far from what we do here. - -### Why isn't library XXX which is at most two files and has minimal other dependencies on this list? - -Probably because I don't know about it, feel free to submit a pull request, issue, email, or tweet it at -me (it can be your own library or somebody else's). But I might not include it for various -other reasons, including subtleties of what is 'minimal other dependencies' and subtleties -about what is 'lightweight'. - -### Why isn't SQLite's amalgamated build on this list? - -Come on. - diff --git a/impeller/third_party/stb/stb/docs/stb_howto.txt b/impeller/third_party/stb/stb/docs/stb_howto.txt deleted file mode 100644 index a969b540225d2..0000000000000 --- a/impeller/third_party/stb/stb/docs/stb_howto.txt +++ /dev/null @@ -1,185 +0,0 @@ -Lessons learned about how to make a header-file library -V1.0 -September 2013 Sean Barrett - -Things to do in an stb-style header-file library, -and rationales: - - -1. #define LIBRARYNAME_IMPLEMENTATION - -Use a symbol like the above to control creating -the implementation. (I used a far-less-clear name -in my first header-file library; it became -clear that was a mistake once I had multiple -libraries.) - -Include a "header-file" section with header-file -guards and declarations for all the functions, -but only guard the implementation with LIBRARYNAME_IMPLEMENTATION, -not the header-file guard. That way, if client's -header file X includes your header file for -declarations, they can still include header file X -in the source file that creates the implementation; -if you guard the implementation too, then the first -include (before the #define) creates the declarations, -and the second one (after the #define) does nothing. - - -2. AVOID DEPENDENCIES - -Don't rely on anything other than the C standard libraries. - -(If you're creating a library specifically to leverage/wrap -some other library, then obviously you can rely on that -library. But if that library is public domain, you might -be better off directly embedding the source, to reduce -dependencies for your clients. But of course now you have -to update whenever that library updates.) - -If you use stdlib, consider wrapping all stdlib calls in -macros, and then conditionally define those macros to the -stdlib function, allowing the user to replace them. - -For functions with side effects, like memory allocations, -consider letting the user pass in a context and pass -that in to the macros. (The stdlib versions will ignore -the parameter.) Otherwise, users may have to use global -or thread-local variables to achieve the same effect. - - -3. AVOID MALLOC - -You can't always do this, but when you can, embedded developers -will appreciate it. I almost never bother avoiding, as it's -too much work (and in some cases is pretty infeasible; -see http://nothings.org/gamedev/font_rendering_malloc.txt ). -But it's definitely something one of the things I've gotten -the most pushback on from potential users. - - -4. ALLOW STATIC IMPLEMENTATION - -Have a #define which makes function declarations and -function definitions static. This makes the implementation -private to the source file that creates it. This allows -people to use your library multiple times in their project -without collision. (This is only necessary if your library -has configuration macros or global state, or if your -library has multiple versions that are not backwards -compatible. I've run into both of those cases.) - - -5. MAKE ACCESSIBLE FROM C - -Making your code accessible from C instead of C++ (i.e. -either coding in C, or using extern "C") makes it more -straightforward to be used in C and in other languages, -which often only have support for C bindings, not C++. -(One of the earliest results I found in googling for -stb_image was a Haskell wrapper.) Otherwise, people -have to wrap it in another set of function calls, and -the whole point here is to make it convenient for people -to use, isn't it? (See below.) - -I prefer to code entirely in C, so the source file that -instantiates the implementation can be C itself, for -those crazy people out there who are programming in C. -But it's probably not a big hardship for a C programmer -to create a single C++ source file to instantiate your -library. - - -6. NAMESPACE PRIVATE FUNCTIONS - -Try to avoid having names in your source code that -will cause conflicts with identical names in client -code. You can do this either by namespacing in C++, -or prefixing with your library name in C. - -In C, generally, I use the same prefix for API -functions and private symbols, such as "stbtt_" -for stb_truetype; but private functions (and -static globals) use a second underscore as -in "stbtt__" to further minimize the chance of -additional collisions in the unlikely but not -impossible event that users write wrapper -functions that have names of the form "stbtt_". -(Consider the user that has used "stbtt_foo" -*successfully*, and then upgrades to a new -version of your library which has a new private -function named either "stbtt_foo" or "stbtt__foo".) - -Note that the double-underscore is reserved for -use by the compiler, but (1) there is nothing -reserved for "middleware", i.e. libraries -desiring to avoid conflicts with user symbols -have no other good options, and (2) in practice -no compilers use double-underscore in the middle -rather than the beginning/end. (Unfortunately, -there is at least one videogame-console compiler that -will warn about double-underscores by default.) - - -7. EASY-TO-COMPLY LICENSE - -I make my libraries public domain. You don't have to. -But my goal in releasing stb-style libraries is to -reduce friction for potential users as much as -possible. That means: - - a. easy to build (what this file is mostly about) - b. easy to invoke (which requires good API design) - c. easy to deploy (which is about licensing) - -I choose to place all my libraries in the public -domain, abjuring copyright, rather than license -the libraries. This has some benefits and some -drawbacks. - -Any license which is "viral" to modifications -causes worries for lawyers, even if their programmers -aren't modifying it. - -Any license which requires crediting in documentation -adds friction which can add up. Valve used to have -a page with a list of all of these on their web site, -and it was insane, and obviously nobody ever looked -at it so why would you care whether your credit appeared -there? - -Permissive licenses like zlib and BSD license are -perfectly reasonable, but they are very wordy and -have only two benefits over public domain: legally-mandated -attribution and liability-control. I do not believe these -are worth the excessive verbosity and user-unfriendliness -these licenses induce, especially in the single-file -case where those licenses tend to be at the top of -the file, the first thing you see. (To the specific -points, I have had no trouble receiving attribution -for my libraries; liability in the face of no explicit -disclaimer of liability is an open question.) - -However, public domain has frictions of its own, because -public domain declarations aren't necessary recognized -in the USA and some other locations. For that reason, -I recommend a declaration along these lines: - -// This software is dual-licensed to the public domain and under the following -// license: you are granted a perpetual, irrevocable license to copy, modify, -// publish, and distribute this file as you see fit. - -I typically place this declaration at the end of the initial -comment block of the file and just say 'public domain' -at the top. - -I have had people say they couldn't use one of my -libraries because it was only "public domain" and didn't -have the additional fallback clause, who asked if -I could dual-license it under a traditional license. - -My answer: they can create a derivative work by -modifying one character, and then license that however -they like. (Indeed, *adding* the zlib or BSD license -would be such a modification!) Unfortunately, their -lawyers reportedly didn't like that answer. :( diff --git a/impeller/third_party/stb/stb/docs/stb_voxel_render_interview.md b/impeller/third_party/stb/stb/docs/stb_voxel_render_interview.md deleted file mode 100644 index 7071466e26f0c..0000000000000 --- a/impeller/third_party/stb/stb/docs/stb_voxel_render_interview.md +++ /dev/null @@ -1,173 +0,0 @@ -# An interview with STB about stb_voxel_render.h - -**Q:** -I suppose you really like Minecraft? - -**A:** -Not really. I mean, I do own it and play it some, and -I do watch YouTube videos of other people playing it -once in a while, but I'm not saying it's that great. - -But I do love voxels. I've been playing with voxel rendering -since the mid-late 90's when we were still doing software -rendering and thinking maybe polygons weren't the answer. -Once GPUs came along that kind of died off, at least until -Minecraft brought it back to attention. - -**Q:** -Do you expect people will make a lot of Minecraft clones -with this? - -**A:** -I hope not! - -For one thing, it's a terrible idea for the -developer. Remember before Minecraft was on the Xbox 360, -there were a ton of "indie" clones (some maybe making -decent money even), but then the real Minecraft came out -and just crushed them (as far as I know). It's just not -something you really want to compete with. - -The reason I made this library is because I'd like -to see more games with Minecraft's *art style*, not -necessary its *gameplay*. - -I can understand the urge to clone the gameplay. When -you have a world made of voxels/blocks, there are a -few things that become incredibly easy to do that would -otherwise be very hard (at least for an indie) to do in 3D. -One thing is that procedural generation becomes much easier. -Another is that destructible environments are easy. Another -is that you have a world where your average user can build -stuff that they find satisfactory. - -Minecraft is at a sort of local maximum, a sweet spot, where -it leverages all of those easy-to-dos. And so I'm sure it's -hard to look at the space of 'games using voxels' and move -away from that local maximum, to give up some of that. -But I think that's what people should do. - -**Q:** -So what else can people do with stb_voxel_render? - -**A:** -All of those benefits I mentioned above are still valid even -if you stay away from the sweet spot. You can make a 3D roguelike -without player-creation/destruction that uses procedural generation. -You could make a shooter with pre-designed maps but destructible -environments. - -And I'm sure there are other possible benefits to using voxels/blocks. -Hopefully this will make it easier for people to explore the space. - -The library has a pretty wide range of features to allow -people to come up with some distinctive looks. For example, -the art style of Continue?9876543210 was one of the inspirations -for trying to make the multitexturing capabilities flexible. -I'm terrible at art, so this isn't really something I can -come up with myself, but I tried to put in flexible -technology that could be used multiple ways. - -One thing I did intentionally was try to make it possible to -make nicer looking ground terrain, using the half-height -slopes and "weird slopes". There are Minecraft mods with -drivable cars and they just go up these blocky slopes and, -like, what? So I wanted you to be able to make smoother -terrain, either just for the look, or for vehicles etc. -Also, you can spatially cross-fade between two ground textures for -that classic bad dirt/grass transition that has shipped -in plenty of professional games. Of course, you could -just use a separate non-voxel ground renderer for all of -this. But this way, you can seamlessly integrate everything -else with it. E.g. in your authoring tool (or procedural -generation) you can make smooth ground and then cut a -sharp-edged hole in it for a building's basement or whatever. - -Another thing you can do is work at a very different scale. -In Minecraft, a person is just under 2 blocks tall. In -Ace of Spades, a person is just under 3 blocks tall. Why -not 4 or 6? Well, partly because you just need a lot more -voxels; if a meter is 2 voxels in Mineraft and 4 voxels in -your game, and you draw the same number of voxels due to -hardware limits, then your game has half the view distance -of Minecraft. Since stb_voxel_render is designed to keep -the meshes small and render efficiently, you can push the -view distance out further than Minecraft--or use a similar -view distance and a higher voxel resolution. You could also -stop making infinite worlds and work at entirely different -scales; where Minecraft is 1 voxel per meter, you could -have 20 voxels per meter and make a small arena that's -50 meters wide and 5 meters tall. - -Back when the voxel game Voxatron was announced, the weekend -after the trailer came out I wrote my own little GPU-accelerated -version of the engine and thought that was pretty cool. I've -been tempted many times to extract that and release it -as a library, but -I don't want to steal Voxatron's thunder so I've avoided -it. You could use this engine to do the same kind of thing, -although it won't be as efficient as an engine dedicated to -that style of thing would be. - -**Q:** -What one thing would you really like to see somebody do? - -**A:** -Before Unity, 3D has seemed deeply problematic in the indie -space. Software like GameMaker has tried to support 3D but -it seems like little of note has been done with it. - -Minecraft has shown that people can build worlds with the -Minecraft toolset far more easily than we've ever seen from those -other tools. Obviously people have done great things with -Unity, but those people are much closer to professional -developers; typically they still need real 3D modelling -and all of that stuff. - -So what I'd really like to see is someone build some kind -of voxel-game-construction-set. Start with stb_voxel_render, -maybe expose all the flexibility of stb_voxel_render (so -people can do different things). Thrown in lua or something -else for scripting, make some kind of editor that feels -at least as good as Minecraft and Infinifactory, and see -where that gets you. - -**Q:** -Why'd you make this library? - -**A:** -Mainly as a way of releasing this technology I've been working -on since 2011 and seemed unlikely to ever ship myself. In 2011 -I was playing the voxel shooter Ace of Spades. One of the maps -that we played on was a partial port of Broville (which is the -first Minecraft map in stb_voxel_render release trailer). I'd -made a bunch of procedural level generators for the game, and -I started trying to make a city generator inspired by Broville. - -But I realized it would be a lot of work, and of very little -value (most of my maps didn't get much play because people -preferred to play on maps where they could charge straight -at the enemies and shoot them as fast as possible). So I -wrote my own voxel engine and started working on a procedural -city game. But I got bogged down after I finally got the road -generator working and never got anywhere with building -generation or gameplay. - -stb_voxel_render is actually a complete rewrite from scratch, -but it's based a lot on what I learned from that previous work. - -**Q:** -About the release video... how long did that take to edit? - -**A:** -About seven or eight hours. I had the first version done in -maybe six or seven hours, but then I realized I'd left out -one clip, and when I went back to add it I also gussied up -a couple other moments in the video. But there was something -basically identical to it that was done in around six. - -**Q:** -Ok, that's it. Thanks, me. - -**A:** -Thanks *me!* diff --git a/impeller/third_party/stb/stb/docs/why_public_domain.md b/impeller/third_party/stb/stb/docs/why_public_domain.md deleted file mode 100644 index 56cef3927ed1a..0000000000000 --- a/impeller/third_party/stb/stb/docs/why_public_domain.md +++ /dev/null @@ -1,116 +0,0 @@ -My collected rationales for placing these libraries -in the public domain: - -1. Public domain vs. viral licenses - - Why is this library public domain? - Because more people will use it. Because it's not viral, people are - not obligated to give back, so you could argue that it hurts the - development of it, and then because it doesn't develop as well it's - not as good, and then because it's not as good, in the long run - maybe fewer people will use it. I have total respect for that - opinion, but I just don't believe it myself for most software. - -2. Public domain vs. attribution-required licenses - - The primary difference between public domain and, say, a Creative Commons - commercial / non-share-alike / attribution license is solely the - requirement for attribution. (Similarly the BSD license and such.) - While I would *appreciate* acknowledgement and attribution, I believe - that it is foolish to place a legal encumberment (i.e. a license) on - the software *solely* to get attribution. - - In other words, I'm arguing that PD is superior to the BSD license and - the Creative Commons 'Attribution' license. If the license offers - anything besides attribution -- as does, e.g., CC NonCommercial-ShareAlike, - or the GPL -- that's a separate discussion. - -3. Other aspects of BSD-style licenses besides attribution - - Permissive licenses like zlib and BSD license are perfectly reasonable - in their requirements, but they are very wordy and - have only two benefits over public domain: legally-mandated - attribution and liability-control. I do not believe these - are worth the excessive verbosity and user-unfriendliness - these licenses induce, especially in the single-file - case where those licenses tend to be at the top of - the file, the first thing you see. - - To the specific points, I have had no trouble receiving - attribution for my libraries; liability in the face of - no explicit disclaimer of liability is an open question, - but one I have a lot of difficulty imagining there being - any actual doubt about in court. Sometimes I explicitly - note in my libraries that I make no guarantees about them - being fit for purpose, but it's pretty absurd to do this; - as a whole, it comes across as "here is a library to decode - vorbis audio files, but it may not actually work and if - you have problems it's not my fault, but also please - report bugs so I can fix them"--so dumb! - -4. full discussion from stb_howto.txt on what YOU should do for YOUR libs - -``` -EASY-TO-COMPLY LICENSE - -I make my libraries public domain. You don't have to. -But my goal in releasing stb-style libraries is to -reduce friction for potential users as much as -possible. That means: - - a. easy to build (what this file is mostly about) - b. easy to invoke (which requires good API design) - c. easy to deploy (which is about licensing) - -I choose to place all my libraries in the public -domain, abjuring copyright, rather than license -the libraries. This has some benefits and some -drawbacks. - -Any license which is "viral" to modifications -causes worries for lawyers, even if their programmers -aren't modifying it. - -Any license which requires crediting in documentation -adds friction which can add up. Valve used to have -a page with a list of all of these on their web site, -and it was insane, and obviously nobody ever looked -at it so why would you care whether your credit appeared -there? - -Permissive licenses like zlib and BSD license are -perfectly reasonable, but they are very wordy and -have only two benefits over public domain: legally-mandated -attribution and liability-control. I do not believe these -are worth the excessive verbosity and user-unfriendliness -these licenses induce, especially in the single-file -case where those licenses tend to be at the top of -the file, the first thing you see. (To the specific -points, I have had no trouble receiving attribution -for my libraries; liability in the face of no explicit -disclaimer of liability is an open question.) - -However, public domain has frictions of its own, because -public domain declarations aren't necessary recognized -in the USA and some other locations. For that reason, -I recommend a declaration along these lines: - -// This software is dual-licensed to the public domain and under the following -// license: you are granted a perpetual, irrevocable license to copy, modify, -// publish, and distribute this file as you see fit. - -I typically place this declaration at the end of the initial -comment block of the file and just say 'public domain' -at the top. - -I have had people say they couldn't use one of my -libraries because it was only "public domain" and didn't -have the additional fallback clause, who asked if -I could dual-license it under a traditional license. - -My answer: they can create a derivative work by -modifying one character, and then license that however -they like. (Indeed, *adding* the zlib or BSD license -would be such a modification!) Unfortunately, their -lawyers reportedly didn't like that answer. :( -``` diff --git a/impeller/third_party/stb/stb/release_notes.md b/impeller/third_party/stb/stb/release_notes.md deleted file mode 100644 index 6712e9daf3ea4..0000000000000 --- a/impeller/third_party/stb/stb/release_notes.md +++ /dev/null @@ -1,26 +0,0 @@ ----- - -2016-04-02: - -- other_libs: cro_mipmap, greatest, munit, parg, dr_flac -- stb_image_write: allocate large structures on stack for embedded (Thatcher Ulrich) -- stb_image: allocate large structures on stack for embedded (Thatcher Ulrich) -- stb_image: remove white matting for transparent PSD (stb, Oriol Ferrer Mesia) -- stb_image: fix reported channel count in PNG when req_comp is non-zero -- stb_image: re-enable SSE2 in x64 (except in gcc) -- stb_image: fix harmless typo in name (Matthew Gregan) -- stb_image: support JPEG images coded as RGB -- stb_image: bmp could return wrong channel count (snagar@github) -- stb_image: read 16-bit PNGs as 8-bit (socks-the-fox) -- stb_image_resize: fix handling of subpixel regions -- stb_image_resize: avoid warnings on asserts (Wu Shuang) -- stb_truetype: allow fabs() to be supplied by user (Simon Glass) -- stb_truetype: duplicate typedef -- stb_truetype: don't leak memory if fontsize=0 -- stb_vorbis: warnings (Thiago Goulart) -- stb_vorbis: fix multiple memory leaks of setup-memory (manxorist@github) -- stb_vorbis: avoid dropping final frame of audio data -- stb_textedit: better support for keying while holding mouse drag button (ocornut) -- stb_voxel_render: fix type of glModelview matrix (Stephen Olsen) -- stb_leakcheck: typo in comment (Lukas Meller) -- stb.h: fix _WIN32 when defining STB_THREADS diff --git a/impeller/third_party/stb/stb/stb.h b/impeller/third_party/stb/stb/stb.h deleted file mode 100644 index b985b13258fbb..0000000000000 --- a/impeller/third_party/stb/stb/stb.h +++ /dev/null @@ -1,14185 +0,0 @@ -/* stb.h - v2.27 - Sean's Tool Box -- public domain -- http://nothings.org/stb.h - no warranty is offered or implied; use this code at your own risk - - This is a single header file with a bunch of useful utilities - for getting stuff done in C/C++. - - Documentation: http://nothings.org/stb/stb_h.html - Unit tests: http://nothings.org/stb/stb.c - - - ============================================================================ - You MUST - - #define STB_DEFINE - - in EXACTLY _one_ C or C++ file that includes this header, BEFORE the - include, like this: - - #define STB_DEFINE - #include "stb.h" - - All other files should just #include "stb.h" without the #define. - ============================================================================ - - -Version History - - 2.27 test _WIN32 not WIN32 in STB_THREADS - 2.26 various warning & bugfixes - 2.25 various warning & bugfixes - 2.24 various warning & bugfixes - 2.23 fix 2.22 - 2.22 64-bit fixes from '!='; fix stb_sdict_copy() to have preferred name - 2.21 utf-8 decoder rejects "overlong" encodings; attempted 64-bit improvements - 2.20 fix to hash "copy" function--reported by someone with handle "!=" - 2.19 ??? - 2.18 stb_readdir_subdirs_mask - 2.17 stb_cfg_dir - 2.16 fix stb_bgio_, add stb_bgio_stat(); begin a streaming wrapper - 2.15 upgraded hash table template to allow: - - aggregate keys (explicit comparison func for EMPTY and DEL keys) - - "static" implementations (so they can be culled if unused) - 2.14 stb_mprintf - 2.13 reduce identifiable strings in STB_NO_STB_STRINGS - 2.12 fix STB_ONLY -- lots of uint32s, TRUE/FALSE things had crept in - 2.11 fix bug in stb_dirtree_get() which caused "c://path" sorts of stuff - 2.10 STB_F(), STB_I() inline constants (also KI,KU,KF,KD) - 2.09 stb_box_face_vertex_axis_side - 2.08 bugfix stb_trimwhite() - 2.07 colored printing in windows (why are we in 1985?) - 2.06 comparison functions are now functions-that-return-functions and - accept a struct-offset as a parameter (not thread-safe) - 2.05 compile and pass tests under Linux (but no threads); thread cleanup - 2.04 stb_cubic_bezier_1d, smoothstep, avoid dependency on registry - 2.03 ? - 2.02 remove integrated documentation - 2.01 integrate various fixes; stb_force_uniprocessor - 2.00 revised stb_dupe to use multiple hashes - 1.99 stb_charcmp - 1.98 stb_arr_deleten, stb_arr_insertn - 1.97 fix stb_newell_normal() - 1.96 stb_hash_number() - 1.95 hack stb__rec_max; clean up recursion code to use new functions - 1.94 stb_dirtree; rename stb_extra to stb_ptrmap - 1.93 stb_sem_new() API cleanup (no blockflag-starts blocked; use 'extra') - 1.92 stb_threadqueue--multi reader/writer queue, fixed size or resizeable - 1.91 stb_bgio_* for reading disk asynchronously - 1.90 stb_mutex uses CRITICAL_REGION; new stb_sync primitive for thread - joining; workqueue supports stb_sync instead of stb_semaphore - 1.89 support ';' in constant-string wildcards; stb_mutex wrapper (can - implement with EnterCriticalRegion eventually) - 1.88 portable threading API (only for win32 so far); worker thread queue - 1.87 fix wildcard handling in stb_readdir_recursive - 1.86 support ';' in wildcards - 1.85 make stb_regex work with non-constant strings; - beginnings of stb_introspect() - 1.84 (forgot to make notes) - 1.83 whoops, stb_keep_if_different wasn't deleting the temp file - 1.82 bring back stb_compress from stb_file.h for cmirror - 1.81 various bugfixes, STB_FASTMALLOC_INIT inits FASTMALLOC in release - 1.80 stb_readdir returns utf8; write own utf8-utf16 because lib was wrong - 1.79 stb_write - 1.78 calloc() support for malloc wrapper, STB_FASTMALLOC - 1.77 STB_FASTMALLOC - 1.76 STB_STUA - Lua-like language; (stb_image, stb_csample, stb_bilinear) - 1.75 alloc/free array of blocks; stb_hheap bug; a few stb_ps_ funcs; - hash*getkey, hash*copy; stb_bitset; stb_strnicmp; bugfix stb_bst - 1.74 stb_replaceinplace; use stdlib C function to convert utf8 to UTF-16 - 1.73 fix performance bug & leak in stb_ischar (C++ port lost a 'static') - 1.72 remove stb_block, stb_block_manager, stb_decompress (to stb_file.h) - 1.71 stb_trimwhite, stb_tokens_nested, etc. - 1.70 back out 1.69 because it might problemize mixed builds; stb_filec() - 1.69 (stb_file returns 'char *' in C++) - 1.68 add a special 'tree root' data type for stb_bst; stb_arr_end - 1.67 full C++ port. (stb_block_manager) - 1.66 stb_newell_normal - 1.65 stb_lex_item_wild -- allow wildcard items which MUST match entirely - 1.64 stb_data - 1.63 stb_log_name - 1.62 stb_define_sort; C++ cleanup - 1.61 stb_hash_fast -- Paul Hsieh's hash function (beats Bob Jenkins'?) - 1.60 stb_delete_directory_recursive - 1.59 stb_readdir_recursive - 1.58 stb_bst variant with parent pointer for O(1) iteration, not O(log N) - 1.57 replace LCG random with Mersenne Twister (found a public domain one) - 1.56 stb_perfect_hash, stb_ischar, stb_regex - 1.55 new stb_bst API allows multiple BSTs per node (e.g. secondary keys) - 1.54 bugfix: stb_define_hash, stb_wildmatch, regexp - 1.53 stb_define_hash; recoded stb_extra, stb_sdict use it - 1.52 stb_rand_define, stb_bst, stb_reverse - 1.51 fix 'stb_arr_setlen(NULL, 0)' - 1.50 stb_wordwrap - 1.49 minor improvements to enable the scripting language - 1.48 better approach for stb_arr using stb_malloc; more invasive, clearer - 1.47 stb_lex (lexes stb.h at 1.5ML/s on 3Ghz P4; 60/70% of optimal/flex) - 1.46 stb_wrapper_*, STB_MALLOC_WRAPPER - 1.45 lightly tested DFA acceleration of regexp searching - 1.44 wildcard matching & searching; regexp matching & searching - 1.43 stb_temp - 1.42 allow stb_arr to use stb_malloc/realloc; note this is global - 1.41 make it compile in C++; (disable stb_arr in C++) - 1.40 stb_dupe tweak; stb_swap; stb_substr - 1.39 stb_dupe; improve stb_file_max to be less stupid - 1.38 stb_sha1_file: generate sha1 for file, even > 4GB - 1.37 stb_file_max; partial support for utf8 filenames in Windows - 1.36 remove STB__NO_PREFIX - poor interaction with IDE, not worth it - streamline stb_arr to make it separately publishable - 1.35 bugfixes for stb_sdict, stb_malloc(0), stristr - 1.34 (streaming interfaces for stb_compress) - 1.33 stb_alloc; bug in stb_getopt; remove stb_overflow - 1.32 (stb_compress returns, smaller&faster; encode window & 64-bit len) - 1.31 stb_prefix_count - 1.30 (STB__NO_PREFIX - remove stb_ prefixes for personal projects) - 1.29 stb_fput_varlen64, etc. - 1.28 stb_sha1 - 1.27 ? - 1.26 stb_extra - 1.25 ? - 1.24 stb_copyfile - 1.23 stb_readdir - 1.22 ? - 1.21 ? - 1.20 ? - 1.19 ? - 1.18 ? - 1.17 ? - 1.16 ? - 1.15 stb_fixpath, stb_splitpath, stb_strchr2 - 1.14 stb_arr - 1.13 ?stb, stb_log, stb_fatal - 1.12 ?stb_hash2 - 1.11 miniML - 1.10 stb_crc32, stb_adler32 - 1.09 stb_sdict - 1.08 stb_bitreverse, stb_ispow2, stb_big32 - stb_fopen, stb_fput_varlen, stb_fput_ranged - stb_fcmp, stb_feq - 1.07 (stb_encompress) - 1.06 stb_compress - 1.05 stb_tokens, (stb_hheap) - 1.04 stb_rand - 1.03 ?(s-strings) - 1.02 ?stb_filelen, stb_tokens - 1.01 stb_tolower - 1.00 stb_hash, stb_intcmp - stb_file, stb_stringfile, stb_fgets - stb_prefix, stb_strlower, stb_strtok - stb_image - (stb_array), (stb_arena) - -Parenthesized items have since been removed. - -LICENSE - -This software is dual-licensed to the public domain and under the following -license: you are granted a perpetual, irrevocable license to copy, modify, -publish, and distribute this file as you see fit. - -CREDITS - - Written by Sean Barrett. - - Fixes: - Philipp Wiesemann - Robert Nix - r-lyeh - blackpawn - Mojofreem@github - Ryan Whitworth - Vincent Isambart - Mike Sartain - Eugene Opalev - Tim Sjostrand -*/ - -#ifndef STB__INCLUDE_STB_H -#define STB__INCLUDE_STB_H - -#define STB_VERSION 1 - -#ifdef STB_INTROSPECT - #define STB_DEFINE -#endif - -#ifdef STB_DEFINE_THREADS - #ifndef STB_DEFINE - #define STB_DEFINE - #endif - #ifndef STB_THREADS - #define STB_THREADS - #endif -#endif - -#if defined(_WIN32) && !defined(__MINGW32__) - #ifndef _CRT_SECURE_NO_WARNINGS - #define _CRT_SECURE_NO_WARNINGS - #endif - #ifndef _CRT_NONSTDC_NO_DEPRECATE - #define _CRT_NONSTDC_NO_DEPRECATE - #endif - #ifndef _CRT_NON_CONFORMING_SWPRINTFS - #define _CRT_NON_CONFORMING_SWPRINTFS - #endif - #if !defined(_MSC_VER) || _MSC_VER > 1700 - #include // _BitScanReverse - #endif -#endif - -#include // stdlib could have min/max -#include // need FILE -#include // stb_define_hash needs memcpy/memset -#include // stb_dirtree -#ifdef __MINGW32__ - #include // O_RDWR -#endif - -#ifdef STB_PERSONAL - typedef int Bool; - #define False 0 - #define True 1 -#endif - -#ifdef STB_MALLOC_WRAPPER_PAGED - #define STB_MALLOC_WRAPPER_DEBUG -#endif -#ifdef STB_MALLOC_WRAPPER_DEBUG - #define STB_MALLOC_WRAPPER -#endif -#ifdef STB_MALLOC_WRAPPER_FASTMALLOC - #define STB_FASTMALLOC - #define STB_MALLOC_WRAPPER -#endif - -#ifdef STB_FASTMALLOC - #ifndef _WIN32 - #undef STB_FASTMALLOC - #endif -#endif - -#ifdef STB_DEFINE - #include - #include - #include - #include - #include - #ifndef _WIN32 - #include - #else - #include // _mktemp - #include // _rmdir - #endif - #include // stat()/_stat() - #include // stat()/_stat() -#endif - -#define stb_min(a,b) ((a) < (b) ? (a) : (b)) -#define stb_max(a,b) ((a) > (b) ? (a) : (b)) - -#ifndef STB_ONLY - #if !defined(__cplusplus) && !defined(min) && !defined(max) - #define min(x,y) stb_min(x,y) - #define max(x,y) stb_max(x,y) - #endif - - #ifndef M_PI - #define M_PI 3.14159265358979323846f - #endif - - #ifndef TRUE - #define TRUE 1 - #define FALSE 0 - #endif - - #ifndef deg2rad - #define deg2rad(a) ((a)*(M_PI/180)) - #endif - #ifndef rad2deg - #define rad2deg(a) ((a)*(180/M_PI)) - #endif - - #ifndef swap - #ifndef __cplusplus - #define swap(TYPE,a,b) \ - do { TYPE stb__t; stb__t = (a); (a) = (b); (b) = stb__t; } while (0) - #endif - #endif - - typedef unsigned char uint8 ; - typedef signed char int8 ; - typedef unsigned short uint16; - typedef signed short int16; - #if defined(STB_USE_LONG_FOR_32_BIT_INT) || defined(STB_LONG32) - typedef unsigned long uint32; - typedef signed long int32; - #else - typedef unsigned int uint32; - typedef signed int int32; - #endif - - typedef unsigned char uchar ; - typedef unsigned short ushort; - typedef unsigned int uint ; - typedef unsigned long ulong ; - - // produce compile errors if the sizes aren't right - typedef char stb__testsize16[sizeof(int16)==2]; - typedef char stb__testsize32[sizeof(int32)==4]; -#endif - -#ifndef STB_TRUE - #define STB_TRUE 1 - #define STB_FALSE 0 -#endif - -// if we're STB_ONLY, can't rely on uint32 or even uint, so all the -// variables we'll use herein need typenames prefixed with 'stb': -typedef unsigned char stb_uchar; -typedef unsigned char stb_uint8; -typedef unsigned int stb_uint; -typedef unsigned short stb_uint16; -typedef short stb_int16; -typedef signed char stb_int8; -#if defined(STB_USE_LONG_FOR_32_BIT_INT) || defined(STB_LONG32) - typedef unsigned long stb_uint32; - typedef long stb_int32; -#else - typedef unsigned int stb_uint32; - typedef int stb_int32; -#endif -typedef char stb__testsize2_16[sizeof(stb_uint16)==2 ? 1 : -1]; -typedef char stb__testsize2_32[sizeof(stb_uint32)==4 ? 1 : -1]; - -#ifdef _MSC_VER - typedef unsigned __int64 stb_uint64; - typedef __int64 stb_int64; - #define STB_IMM_UINT64(literalui64) (literalui64##ui64) - #define STB_IMM_INT64(literali64) (literali64##i64) -#else - // ?? - typedef unsigned long long stb_uint64; - typedef long long stb_int64; - #define STB_IMM_UINT64(literalui64) (literalui64##ULL) - #define STB_IMM_INT64(literali64) (literali64##LL) -#endif -typedef char stb__testsize2_64[sizeof(stb_uint64)==8 ? 1 : -1]; - -// add platform-specific ways of checking for sizeof(char*) == 8, -// and make those define STB_PTR64 -#if defined(_WIN64) || defined(__x86_64__) || defined(__ia64__) || defined(__LP64__) - #define STB_PTR64 -#endif - -#ifdef STB_PTR64 -typedef char stb__testsize2_ptr[sizeof(char *) == 8]; -typedef stb_uint64 stb_uinta; -typedef stb_int64 stb_inta; -#else -typedef char stb__testsize2_ptr[sizeof(char *) == 4]; -typedef stb_uint32 stb_uinta; -typedef stb_int32 stb_inta; -#endif -typedef char stb__testsize2_uinta[sizeof(stb_uinta)==sizeof(char*) ? 1 : -1]; - -// if so, we should define an int type that is the pointer size. until then, -// we'll have to make do with this (which is not the same at all!) - -typedef union -{ - unsigned int i; - void * p; -} stb_uintptr; - - -#ifdef __cplusplus - #define STB_EXTERN extern "C" -#else - #define STB_EXTERN extern -#endif - -// check for well-known debug defines -#if defined(DEBUG) || defined(_DEBUG) || defined(DBG) - #ifndef NDEBUG - #define STB_DEBUG - #endif -#endif - -#ifdef STB_DEBUG - #include -#endif - - -STB_EXTERN void stb_wrapper_malloc(void *newp, int sz, char *file, int line); -STB_EXTERN void stb_wrapper_free(void *oldp, char *file, int line); -STB_EXTERN void stb_wrapper_realloc(void *oldp, void *newp, int sz, char *file, int line); -STB_EXTERN void stb_wrapper_calloc(size_t num, size_t sz, char *file, int line); -STB_EXTERN void stb_wrapper_listall(void (*func)(void *ptr, int sz, char *file, int line)); -STB_EXTERN void stb_wrapper_dump(char *filename); -STB_EXTERN int stb_wrapper_allocsize(void *oldp); -STB_EXTERN void stb_wrapper_check(void *oldp); - -#ifdef STB_DEFINE -// this is a special function used inside malloc wrapper -// to do allocations that aren't tracked (to avoid -// reentrancy). Of course if someone _else_ wraps realloc, -// this breaks, but if they're doing that AND the malloc -// wrapper they need to explicitly check for reentrancy. -// -// only define realloc_raw() and we do realloc(NULL,sz) -// for malloc() and realloc(p,0) for free(). -static void * stb__realloc_raw(void *p, int sz) -{ - if (p == NULL) return malloc(sz); - if (sz == 0) { free(p); return NULL; } - return realloc(p,sz); -} -#endif - -#ifdef _WIN32 -STB_EXTERN void * stb_smalloc(size_t sz); -STB_EXTERN void stb_sfree(void *p); -STB_EXTERN void * stb_srealloc(void *p, size_t sz); -STB_EXTERN void * stb_scalloc(size_t n, size_t sz); -STB_EXTERN char * stb_sstrdup(char *s); -#endif - -#ifdef STB_FASTMALLOC -#define malloc stb_smalloc -#define free stb_sfree -#define realloc stb_srealloc -#define strdup stb_sstrdup -#define calloc stb_scalloc -#endif - -#ifndef STB_MALLOC_ALLCHECK - #define stb__check(p) 1 -#else - #ifndef STB_MALLOC_WRAPPER - #error STB_MALLOC_ALLCHECK requires STB_MALLOC_WRAPPER - #else - #define stb__check(p) stb_mcheck(p) - #endif -#endif - -#ifdef STB_MALLOC_WRAPPER - STB_EXTERN void * stb__malloc(int, char *, int); - STB_EXTERN void * stb__realloc(void *, int, char *, int); - STB_EXTERN void * stb__calloc(size_t n, size_t s, char *, int); - STB_EXTERN void stb__free(void *, char *file, int); - STB_EXTERN char * stb__strdup(char *s, char *file, int); - STB_EXTERN void stb_malloc_checkall(void); - STB_EXTERN void stb_malloc_check_counter(int init_delay, int rep_delay); - #ifndef STB_MALLOC_WRAPPER_DEBUG - #define stb_mcheck(p) 1 - #else - STB_EXTERN int stb_mcheck(void *); - #endif - - - #ifdef STB_DEFINE - - #ifdef STB_MALLOC_WRAPPER_DEBUG - #define STB__PAD 32 - #define STB__BIAS 16 - #define STB__SIG 0x51b01234 - #define STB__FIXSIZE(sz) (((sz+3) & ~3) + STB__PAD) - #define STB__ptr(x,y) ((char *) (x) + (y)) - #else - #define STB__ptr(x,y) (x) - #define STB__FIXSIZE(sz) (sz) - #endif - - #ifdef STB_MALLOC_WRAPPER_DEBUG - int stb_mcheck(void *p) - { - unsigned int sz; - if (p == NULL) return 1; - p = ((char *) p) - STB__BIAS; - sz = * (unsigned int *) p; - assert(* (unsigned int *) STB__ptr(p,4) == STB__SIG); - assert(* (unsigned int *) STB__ptr(p,8) == STB__SIG); - assert(* (unsigned int *) STB__ptr(p,12) == STB__SIG); - assert(* (unsigned int *) STB__ptr(p,sz-4) == STB__SIG+1); - assert(* (unsigned int *) STB__ptr(p,sz-8) == STB__SIG+1); - assert(* (unsigned int *) STB__ptr(p,sz-12) == STB__SIG+1); - assert(* (unsigned int *) STB__ptr(p,sz-16) == STB__SIG+1); - stb_wrapper_check(STB__ptr(p, STB__BIAS)); - return 1; - } - - static void stb__check2(void *p, int sz, char *file, int line) - { - stb_mcheck(p); - } - - void stb_malloc_checkall(void) - { - stb_wrapper_listall(stb__check2); - } - #else - void stb_malloc_checkall(void) { } - #endif - - static int stb__malloc_wait=(1 << 30), stb__malloc_next_wait = (1 << 30), stb__malloc_iter; - void stb_malloc_check_counter(int init_delay, int rep_delay) - { - stb__malloc_wait = init_delay; - stb__malloc_next_wait = rep_delay; - } - - void stb_mcheck_all(void) - { - #ifdef STB_MALLOC_WRAPPER_DEBUG - ++stb__malloc_iter; - if (--stb__malloc_wait <= 0) { - stb_malloc_checkall(); - stb__malloc_wait = stb__malloc_next_wait; - } - #endif - } - - #ifdef STB_MALLOC_WRAPPER_PAGED - #define STB__WINDOWS_PAGE (1 << 12) - #ifndef _WINDOWS_ - STB_EXTERN __declspec(dllimport) void * __stdcall VirtualAlloc(void *p, unsigned long size, unsigned long type, unsigned long protect); - STB_EXTERN __declspec(dllimport) int __stdcall VirtualFree(void *p, unsigned long size, unsigned long freetype); - #endif - #endif - - static void *stb__malloc_final(int sz) - { - #ifdef STB_MALLOC_WRAPPER_PAGED - int aligned = (sz + STB__WINDOWS_PAGE - 1) & ~(STB__WINDOWS_PAGE-1); - char *p = VirtualAlloc(NULL, aligned + STB__WINDOWS_PAGE, 0x2000, 0x04); // RESERVE, READWRITE - if (p == NULL) return p; - VirtualAlloc(p, aligned, 0x1000, 0x04); // COMMIT, READWRITE - return p; - #else - return malloc(sz); - #endif - } - - static void stb__free_final(void *p) - { - #ifdef STB_MALLOC_WRAPPER_PAGED - VirtualFree(p, 0, 0x8000); // RELEASE - #else - free(p); - #endif - } - - int stb__malloc_failure; - static void *stb__realloc_final(void *p, int sz, int old_sz) - { - #ifdef STB_MALLOC_WRAPPER_PAGED - void *q = stb__malloc_final(sz); - if (q == NULL) - return ++stb__malloc_failure, q; - // @TODO: deal with p being smaller! - memcpy(q, p, sz < old_sz ? sz : old_sz); - stb__free_final(p); - return q; - #else - return realloc(p,sz); - #endif - } - - void stb__free(void *p, char *file, int line) - { - stb_mcheck_all(); - if (!p) return; - #ifdef STB_MALLOC_WRAPPER_DEBUG - stb_mcheck(p); - #endif - stb_wrapper_free(p,file,line); - #ifdef STB_MALLOC_WRAPPER_DEBUG - p = STB__ptr(p,-STB__BIAS); - * (unsigned int *) STB__ptr(p,0) = 0xdeadbeef; - * (unsigned int *) STB__ptr(p,4) = 0xdeadbeef; - * (unsigned int *) STB__ptr(p,8) = 0xdeadbeef; - * (unsigned int *) STB__ptr(p,12) = 0xdeadbeef; - #endif - stb__free_final(p); - } - - void * stb__malloc(int sz, char *file, int line) - { - void *p; - stb_mcheck_all(); - if (sz == 0) return NULL; - p = stb__malloc_final(STB__FIXSIZE(sz)); - if (p == NULL) p = stb__malloc_final(STB__FIXSIZE(sz)); - if (p == NULL) p = stb__malloc_final(STB__FIXSIZE(sz)); - if (p == NULL) { - ++stb__malloc_failure; - #ifdef STB_MALLOC_WRAPPER_DEBUG - stb_malloc_checkall(); - #endif - return p; - } - #ifdef STB_MALLOC_WRAPPER_DEBUG - * (int *) STB__ptr(p,0) = STB__FIXSIZE(sz); - * (unsigned int *) STB__ptr(p,4) = STB__SIG; - * (unsigned int *) STB__ptr(p,8) = STB__SIG; - * (unsigned int *) STB__ptr(p,12) = STB__SIG; - * (unsigned int *) STB__ptr(p,STB__FIXSIZE(sz)-4) = STB__SIG+1; - * (unsigned int *) STB__ptr(p,STB__FIXSIZE(sz)-8) = STB__SIG+1; - * (unsigned int *) STB__ptr(p,STB__FIXSIZE(sz)-12) = STB__SIG+1; - * (unsigned int *) STB__ptr(p,STB__FIXSIZE(sz)-16) = STB__SIG+1; - p = STB__ptr(p, STB__BIAS); - #endif - stb_wrapper_malloc(p,sz,file,line); - return p; - } - - void * stb__realloc(void *p, int sz, char *file, int line) - { - void *q; - - stb_mcheck_all(); - if (p == NULL) return stb__malloc(sz,file,line); - if (sz == 0 ) { stb__free(p,file,line); return NULL; } - - #ifdef STB_MALLOC_WRAPPER_DEBUG - stb_mcheck(p); - p = STB__ptr(p,-STB__BIAS); - #endif - #ifdef STB_MALLOC_WRAPPER_PAGED - { - int n = stb_wrapper_allocsize(STB__ptr(p,STB__BIAS)); - if (!n) - stb_wrapper_check(STB__ptr(p,STB__BIAS)); - q = stb__realloc_final(p, STB__FIXSIZE(sz), STB__FIXSIZE(n)); - } - #else - q = realloc(p, STB__FIXSIZE(sz)); - #endif - if (q == NULL) - return ++stb__malloc_failure, q; - #ifdef STB_MALLOC_WRAPPER_DEBUG - * (int *) STB__ptr(q,0) = STB__FIXSIZE(sz); - * (unsigned int *) STB__ptr(q,4) = STB__SIG; - * (unsigned int *) STB__ptr(q,8) = STB__SIG; - * (unsigned int *) STB__ptr(q,12) = STB__SIG; - * (unsigned int *) STB__ptr(q,STB__FIXSIZE(sz)-4) = STB__SIG+1; - * (unsigned int *) STB__ptr(q,STB__FIXSIZE(sz)-8) = STB__SIG+1; - * (unsigned int *) STB__ptr(q,STB__FIXSIZE(sz)-12) = STB__SIG+1; - * (unsigned int *) STB__ptr(q,STB__FIXSIZE(sz)-16) = STB__SIG+1; - - q = STB__ptr(q, STB__BIAS); - p = STB__ptr(p, STB__BIAS); - #endif - stb_wrapper_realloc(p,q,sz,file,line); - return q; - } - - STB_EXTERN int stb_log2_ceil(unsigned int); - static void *stb__calloc(size_t n, size_t sz, char *file, int line) - { - void *q; - stb_mcheck_all(); - if (n == 0 || sz == 0) return NULL; - if (stb_log2_ceil(n) + stb_log2_ceil(sz) >= 32) return NULL; - q = stb__malloc(n*sz, file, line); - if (q) memset(q, 0, n*sz); - return q; - } - - char * stb__strdup(char *s, char *file, int line) - { - char *p; - stb_mcheck_all(); - p = stb__malloc(strlen(s)+1, file, line); - if (!p) return p; - strcpy(p, s); - return p; - } - #endif // STB_DEFINE - - #ifdef STB_FASTMALLOC - #undef malloc - #undef realloc - #undef free - #undef strdup - #undef calloc - #endif - - // include everything that might define these, BEFORE making macros - #include - #include - #include - - #define malloc(s) stb__malloc ( s, __FILE__, __LINE__) - #define realloc(p,s) stb__realloc(p,s, __FILE__, __LINE__) - #define calloc(n,s) stb__calloc (n,s, __FILE__, __LINE__) - #define free(p) stb__free (p, __FILE__, __LINE__) - #define strdup(p) stb__strdup (p, __FILE__, __LINE__) - -#endif - -////////////////////////////////////////////////////////////////////////////// -// -// Windows pretty display -// - -STB_EXTERN void stbprint(const char *fmt, ...); -STB_EXTERN char *stb_sprintf(const char *fmt, ...); -STB_EXTERN char *stb_mprintf(const char *fmt, ...); -STB_EXTERN int stb_snprintf(char *s, size_t n, const char *fmt, ...); -STB_EXTERN int stb_vsnprintf(char *s, size_t n, const char *fmt, va_list v); - -#ifdef STB_DEFINE -int stb_vsnprintf(char *s, size_t n, const char *fmt, va_list v) -{ - int res; - #ifdef _WIN32 - // Could use "_vsnprintf_s(s, n, _TRUNCATE, fmt, v)" ? - res = _vsnprintf(s,n,fmt,v); - #else - res = vsnprintf(s,n,fmt,v); - #endif - if (n) s[n-1] = 0; - // Unix returns length output would require, Windows returns negative when truncated. - return (res >= (int) n || res < 0) ? -1 : res; -} - -int stb_snprintf(char *s, size_t n, const char *fmt, ...) -{ - int res; - va_list v; - va_start(v,fmt); - res = stb_vsnprintf(s, n, fmt, v); - va_end(v); - return res; -} - -char *stb_sprintf(const char *fmt, ...) -{ - static char buffer[1024]; - va_list v; - va_start(v,fmt); - stb_vsnprintf(buffer,1024,fmt,v); - va_end(v); - return buffer; -} - -char *stb_mprintf(const char *fmt, ...) -{ - static char buffer[1024]; - va_list v; - va_start(v,fmt); - stb_vsnprintf(buffer,1024,fmt,v); - va_end(v); - return strdup(buffer); -} - -#ifdef _WIN32 - -#ifndef _WINDOWS_ -STB_EXTERN __declspec(dllimport) int __stdcall WriteConsoleA(void *, const void *, unsigned int, unsigned int *, void *); -STB_EXTERN __declspec(dllimport) void * __stdcall GetStdHandle(unsigned int); -STB_EXTERN __declspec(dllimport) int __stdcall SetConsoleTextAttribute(void *, unsigned short); -#endif - -static void stb__print_one(void *handle, char *s, int len) -{ - if (len) - if (WriteConsoleA(handle, s, len, NULL,NULL)) - fwrite(s, 1, len, stdout); // if it fails, maybe redirected, so do normal -} - -static void stb__print(char *s) -{ - void *handle = GetStdHandle((unsigned int) -11); // STD_OUTPUT_HANDLE - int pad=0; // number of padding characters to add - - char *t = s; - while (*s) { - int lpad; - while (*s && *s != '{') { - if (pad) { - if (*s == '\r' || *s == '\n') - pad = 0; - else if (s[0] == ' ' && s[1] == ' ') { - stb__print_one(handle, t, s-t); - t = s; - while (pad) { - stb__print_one(handle, t, 1); - --pad; - } - } - } - ++s; - } - if (!*s) break; - stb__print_one(handle, t, s-t); - if (s[1] == '{') { - ++s; - continue; - } - - if (s[1] == '#') { - t = s+3; - if (isxdigit(s[2])) - if (isdigit(s[2])) - SetConsoleTextAttribute(handle, s[2] - '0'); - else - SetConsoleTextAttribute(handle, tolower(s[2]) - 'a' + 10); - else { - SetConsoleTextAttribute(handle, 0x0f); - t=s+2; - } - } else if (s[1] == '!') { - SetConsoleTextAttribute(handle, 0x0c); - t = s+2; - } else if (s[1] == '@') { - SetConsoleTextAttribute(handle, 0x09); - t = s+2; - } else if (s[1] == '$') { - SetConsoleTextAttribute(handle, 0x0a); - t = s+2; - } else { - SetConsoleTextAttribute(handle, 0x08); // 0,7,8,15 => shades of grey - t = s+1; - } - - lpad = (t-s); - s = t; - while (*s && *s != '}') ++s; - if (!*s) break; - stb__print_one(handle, t, s-t); - if (s[1] == '}') { - t = s+2; - } else { - pad += 1+lpad; - t = s+1; - } - s=t; - SetConsoleTextAttribute(handle, 0x07); - } - stb__print_one(handle, t, s-t); - SetConsoleTextAttribute(handle, 0x07); -} - -void stbprint(const char *fmt, ...) -{ - int res; - char buffer[1024]; - char *tbuf = buffer; - va_list v; - - va_start(v,fmt); - res = stb_vsnprintf(buffer, sizeof(buffer), fmt, v); - va_end(v); - - if (res < 0) { - tbuf = (char *) malloc(16384); - va_start(v,fmt); - res = _vsnprintf(tbuf,16384, fmt, v); - va_end(v); - tbuf[16383] = 0; - } - - stb__print(tbuf); - - if (tbuf != buffer) - free(tbuf); -} - -#else // _WIN32 -void stbprint(const char *fmt, ...) -{ - va_list v; - va_start(v,fmt); - vprintf(fmt,v); - va_end(v); -} -#endif // _WIN32 -#endif // STB_DEFINE - - - -////////////////////////////////////////////////////////////////////////////// -// -// Windows UTF8 filename handling -// -// Windows stupidly treats 8-bit filenames as some dopey code page, -// rather than utf-8. If we want to use utf8 filenames, we have to -// convert them to WCHAR explicitly and call WCHAR versions of the -// file functions. So, ok, we do. - - -#ifdef _WIN32 - #define stb__fopen(x,y) _wfopen((const wchar_t *)stb__from_utf8(x), (const wchar_t *)stb__from_utf8_alt(y)) - #define stb__windows(x,y) x -#else - #define stb__fopen(x,y) fopen(x,y) - #define stb__windows(x,y) y -#endif - - -typedef unsigned short stb__wchar; - -STB_EXTERN stb__wchar * stb_from_utf8(stb__wchar *buffer, char *str, int n); -STB_EXTERN char * stb_to_utf8 (char *buffer, stb__wchar *str, int n); - -STB_EXTERN stb__wchar *stb__from_utf8(char *str); -STB_EXTERN stb__wchar *stb__from_utf8_alt(char *str); -STB_EXTERN char *stb__to_utf8(stb__wchar *str); - - -#ifdef STB_DEFINE -stb__wchar * stb_from_utf8(stb__wchar *buffer, char *ostr, int n) -{ - unsigned char *str = (unsigned char *) ostr; - stb_uint32 c; - int i=0; - --n; - while (*str) { - if (i >= n) - return NULL; - if (!(*str & 0x80)) - buffer[i++] = *str++; - else if ((*str & 0xe0) == 0xc0) { - if (*str < 0xc2) return NULL; - c = (*str++ & 0x1f) << 6; - if ((*str & 0xc0) != 0x80) return NULL; - buffer[i++] = c + (*str++ & 0x3f); - } else if ((*str & 0xf0) == 0xe0) { - if (*str == 0xe0 && (str[1] < 0xa0 || str[1] > 0xbf)) return NULL; - if (*str == 0xed && str[1] > 0x9f) return NULL; // str[1] < 0x80 is checked below - c = (*str++ & 0x0f) << 12; - if ((*str & 0xc0) != 0x80) return NULL; - c += (*str++ & 0x3f) << 6; - if ((*str & 0xc0) != 0x80) return NULL; - buffer[i++] = c + (*str++ & 0x3f); - } else if ((*str & 0xf8) == 0xf0) { - if (*str > 0xf4) return NULL; - if (*str == 0xf0 && (str[1] < 0x90 || str[1] > 0xbf)) return NULL; - if (*str == 0xf4 && str[1] > 0x8f) return NULL; // str[1] < 0x80 is checked below - c = (*str++ & 0x07) << 18; - if ((*str & 0xc0) != 0x80) return NULL; - c += (*str++ & 0x3f) << 12; - if ((*str & 0xc0) != 0x80) return NULL; - c += (*str++ & 0x3f) << 6; - if ((*str & 0xc0) != 0x80) return NULL; - c += (*str++ & 0x3f); - // utf-8 encodings of values used in surrogate pairs are invalid - if ((c & 0xFFFFF800) == 0xD800) return NULL; - if (c >= 0x10000) { - c -= 0x10000; - if (i + 2 > n) return NULL; - buffer[i++] = 0xD800 | (0x3ff & (c >> 10)); - buffer[i++] = 0xDC00 | (0x3ff & (c )); - } - } else - return NULL; - } - buffer[i] = 0; - return buffer; -} - -char * stb_to_utf8(char *buffer, stb__wchar *str, int n) -{ - int i=0; - --n; - while (*str) { - if (*str < 0x80) { - if (i+1 > n) return NULL; - buffer[i++] = (char) *str++; - } else if (*str < 0x800) { - if (i+2 > n) return NULL; - buffer[i++] = 0xc0 + (*str >> 6); - buffer[i++] = 0x80 + (*str & 0x3f); - str += 1; - } else if (*str >= 0xd800 && *str < 0xdc00) { - stb_uint32 c; - if (i+4 > n) return NULL; - c = ((str[0] - 0xd800) << 10) + ((str[1]) - 0xdc00) + 0x10000; - buffer[i++] = 0xf0 + (c >> 18); - buffer[i++] = 0x80 + ((c >> 12) & 0x3f); - buffer[i++] = 0x80 + ((c >> 6) & 0x3f); - buffer[i++] = 0x80 + ((c ) & 0x3f); - str += 2; - } else if (*str >= 0xdc00 && *str < 0xe000) { - return NULL; - } else { - if (i+3 > n) return NULL; - buffer[i++] = 0xe0 + (*str >> 12); - buffer[i++] = 0x80 + ((*str >> 6) & 0x3f); - buffer[i++] = 0x80 + ((*str ) & 0x3f); - str += 1; - } - } - buffer[i] = 0; - return buffer; -} - -stb__wchar *stb__from_utf8(char *str) -{ - static stb__wchar buffer[4096]; - return stb_from_utf8(buffer, str, 4096); -} - -stb__wchar *stb__from_utf8_alt(char *str) -{ - static stb__wchar buffer[64]; - return stb_from_utf8(buffer, str, 64); -} - -char *stb__to_utf8(stb__wchar *str) -{ - static char buffer[4096]; - return stb_to_utf8(buffer, str, 4096); -} - -#endif - -////////////////////////////////////////////////////////////////////////////// -// -// Miscellany -// - -STB_EXTERN void stb_fatal(char *fmt, ...); -STB_EXTERN void stb_(char *fmt, ...); -STB_EXTERN void stb_append_to_file(char *file, char *fmt, ...); -STB_EXTERN void stb_log(int active); -STB_EXTERN void stb_log_fileline(int active); -STB_EXTERN void stb_log_name(char *filename); - -STB_EXTERN void stb_swap(void *p, void *q, size_t sz); -STB_EXTERN void *stb_copy(void *p, size_t sz); -STB_EXTERN void stb_pointer_array_free(void *p, int len); -STB_EXTERN void **stb_array_block_alloc(int count, int blocksize); - -#define stb_arrcount(x) (sizeof(x)/sizeof((x)[0])) - - -STB_EXTERN int stb__record_fileline(char *f, int n); - -#ifdef STB_DEFINE - -static char *stb__file; -static int stb__line; - -int stb__record_fileline(char *f, int n) -{ - stb__file = f; - stb__line = n; - return 0; -} - -void stb_fatal(char *s, ...) -{ - va_list a; - if (stb__file) - fprintf(stderr, "[%s:%d] ", stb__file, stb__line); - va_start(a,s); - fputs("Fatal error: ", stderr); - vfprintf(stderr, s, a); - va_end(a); - fputs("\n", stderr); - #ifdef STB_DEBUG - #ifdef _MSC_VER - #ifndef STB_PTR64 - __asm int 3; // trap to debugger! - #else - __debugbreak(); - #endif - #else - __builtin_trap(); - #endif - #endif - exit(1); -} - -static int stb__log_active=1, stb__log_fileline=1; - -void stb_log(int active) -{ - stb__log_active = active; -} - -void stb_log_fileline(int active) -{ - stb__log_fileline = active; -} - -#ifdef STB_NO_STB_STRINGS -char *stb__log_filename = "temp.log"; -#else -char *stb__log_filename = "stb.log"; -#endif - -void stb_log_name(char *s) -{ - stb__log_filename = s; -} - -void stb_(char *s, ...) -{ - if (stb__log_active) { - FILE *f = fopen(stb__log_filename, "a"); - if (f) { - va_list a; - if (stb__log_fileline && stb__file) - fprintf(f, "[%s:%4d] ", stb__file, stb__line); - va_start(a,s); - vfprintf(f, s, a); - va_end(a); - fputs("\n", f); - fclose(f); - } - } -} - -void stb_append_to_file(char *filename, char *s, ...) -{ - FILE *f = fopen(filename, "a"); - if (f) { - va_list a; - va_start(a,s); - vfprintf(f, s, a); - va_end(a); - fputs("\n", f); - fclose(f); - } -} - - -typedef struct { char d[4]; } stb__4; -typedef struct { char d[8]; } stb__8; - -// optimize the small cases, though you shouldn't be calling this for those! -void stb_swap(void *p, void *q, size_t sz) -{ - char buffer[256]; - if (p == q) return; - if (sz == 4) { - stb__4 temp = * ( stb__4 *) p; - * (stb__4 *) p = * ( stb__4 *) q; - * (stb__4 *) q = temp; - return; - } else if (sz == 8) { - stb__8 temp = * ( stb__8 *) p; - * (stb__8 *) p = * ( stb__8 *) q; - * (stb__8 *) q = temp; - return; - } - - while (sz > sizeof(buffer)) { - stb_swap(p, q, sizeof(buffer)); - p = (char *) p + sizeof(buffer); - q = (char *) q + sizeof(buffer); - sz -= sizeof(buffer); - } - - memcpy(buffer, p , sz); - memcpy(p , q , sz); - memcpy(q , buffer, sz); -} - -void *stb_copy(void *p, size_t sz) -{ - void *q = malloc(sz); - memcpy(q, p, sz); - return q; -} - -void stb_pointer_array_free(void *q, int len) -{ - void **p = (void **) q; - int i; - for (i=0; i < len; ++i) - free(p[i]); -} - -void **stb_array_block_alloc(int count, int blocksize) -{ - int i; - char *p = (char *) malloc(sizeof(void *) * count + count * blocksize); - void **q; - if (p == NULL) return NULL; - q = (void **) p; - p += sizeof(void *) * count; - for (i=0; i < count; ++i) - q[i] = p + i * blocksize; - return q; -} -#endif - -#ifdef STB_DEBUG - // tricky hack to allow recording FILE,LINE even in varargs functions - #define STB__RECORD_FILE(x) (stb__record_fileline(__FILE__, __LINE__),(x)) - #define stb_log STB__RECORD_FILE(stb_log) - #define stb_ STB__RECORD_FILE(stb_) - #ifndef STB_FATAL_CLEAN - #define stb_fatal STB__RECORD_FILE(stb_fatal) - #endif - #define STB__DEBUG(x) x -#else - #define STB__DEBUG(x) -#endif - -////////////////////////////////////////////////////////////////////////////// -// -// stb_temp -// - -#define stb_temp(block, sz) stb__temp(block, sizeof(block), (sz)) - -STB_EXTERN void * stb__temp(void *b, int b_sz, int want_sz); -STB_EXTERN void stb_tempfree(void *block, void *ptr); - -#ifdef STB_DEFINE - -void * stb__temp(void *b, int b_sz, int want_sz) -{ - if (b_sz >= want_sz) - return b; - else - return malloc(want_sz); -} - -void stb_tempfree(void *b, void *p) -{ - if (p != b) - free(p); -} -#endif - - -////////////////////////////////////////////////////////////////////////////// -// -// math/sampling operations -// - - -#define stb_lerp(t,a,b) ( (a) + (t) * (float) ((b)-(a)) ) -#define stb_unlerp(t,a,b) ( ((t) - (a)) / (float) ((b) - (a)) ) - -#define stb_clamp(x,xmin,xmax) ((x) < (xmin) ? (xmin) : (x) > (xmax) ? (xmax) : (x)) - -STB_EXTERN void stb_newell_normal(float *normal, int num_vert, float **vert, int normalize); -STB_EXTERN int stb_box_face_vertex_axis_side(int face_number, int vertex_number, int axis); -STB_EXTERN void stb_linear_controller(float *curpos, float target_pos, float acc, float deacc, float dt); - -STB_EXTERN int stb_float_eq(float x, float y, float delta, int max_ulps); -STB_EXTERN int stb_is_prime(unsigned int m); -STB_EXTERN unsigned int stb_power_of_two_nearest_prime(int n); - -STB_EXTERN float stb_smoothstep(float t); -STB_EXTERN float stb_cubic_bezier_1d(float t, float p0, float p1, float p2, float p3); - -STB_EXTERN double stb_linear_remap(double x, double a, double b, - double c, double d); - -#ifdef STB_DEFINE -float stb_smoothstep(float t) -{ - return (3 - 2*t)*(t*t); -} - -float stb_cubic_bezier_1d(float t, float p0, float p1, float p2, float p3) -{ - float it = 1-t; - return it*it*it*p0 + 3*it*it*t*p1 + 3*it*t*t*p2 + t*t*t*p3; -} - -void stb_newell_normal(float *normal, int num_vert, float **vert, int normalize) -{ - int i,j; - float p; - normal[0] = normal[1] = normal[2] = 0; - for (i=num_vert-1,j=0; j < num_vert; i=j++) { - float *u = vert[i]; - float *v = vert[j]; - normal[0] += (u[1] - v[1]) * (u[2] + v[2]); - normal[1] += (u[2] - v[2]) * (u[0] + v[0]); - normal[2] += (u[0] - v[0]) * (u[1] + v[1]); - } - if (normalize) { - p = normal[0]*normal[0] + normal[1]*normal[1] + normal[2]*normal[2]; - p = (float) (1.0 / sqrt(p)); - normal[0] *= p; - normal[1] *= p; - normal[2] *= p; - } -} - -int stb_box_face_vertex_axis_side(int face_number, int vertex_number, int axis) -{ - static int box_vertices[6][4][3] = - { - { { 1,1,1 }, { 1,0,1 }, { 1,0,0 }, { 1,1,0 } }, - { { 0,0,0 }, { 0,0,1 }, { 0,1,1 }, { 0,1,0 } }, - { { 0,0,0 }, { 0,1,0 }, { 1,1,0 }, { 1,0,0 } }, - { { 0,0,0 }, { 1,0,0 }, { 1,0,1 }, { 0,0,1 } }, - { { 1,1,1 }, { 0,1,1 }, { 0,0,1 }, { 1,0,1 } }, - { { 1,1,1 }, { 1,1,0 }, { 0,1,0 }, { 0,1,1 } }, - }; - assert(face_number >= 0 && face_number < 6); - assert(vertex_number >= 0 && vertex_number < 4); - assert(axis >= 0 && axis < 3); - return box_vertices[face_number][vertex_number][axis]; -} - -void stb_linear_controller(float *curpos, float target_pos, float acc, float deacc, float dt) -{ - float sign = 1, p, cp = *curpos; - if (cp == target_pos) return; - if (target_pos < cp) { - target_pos = -target_pos; - cp = -cp; - sign = -1; - } - // first decelerate - if (cp < 0) { - p = cp + deacc * dt; - if (p > 0) { - p = 0; - dt = dt - cp / deacc; - if (dt < 0) dt = 0; - } else { - dt = 0; - } - cp = p; - } - // now accelerate - p = cp + acc*dt; - if (p > target_pos) p = target_pos; - *curpos = p * sign; - // @TODO: testing -} - -float stb_quadratic_controller(float target_pos, float curpos, float maxvel, float maxacc, float dt, float *curvel) -{ - return 0; // @TODO -} - -int stb_float_eq(float x, float y, float delta, int max_ulps) -{ - if (fabs(x-y) <= delta) return 1; - if (abs(*(int *)&x - *(int *)&y) <= max_ulps) return 1; - return 0; -} - -int stb_is_prime(unsigned int m) -{ - unsigned int i,j; - if (m < 2) return 0; - if (m == 2) return 1; - if (!(m & 1)) return 0; - if (m % 3 == 0) return (m == 3); - for (i=5; (j=i*i), j <= m && j > i; i += 6) { - if (m % i == 0) return 0; - if (m % (i+2) == 0) return 0; - } - return 1; -} - -unsigned int stb_power_of_two_nearest_prime(int n) -{ - static signed char tab[32] = { 0,0,0,0,1,0,-1,0,1,-1,-1,3,-1,0,-1,2,1, - 0,2,0,-1,-4,-1,5,-1,18,-2,15,2,-1,2,0 }; - if (!tab[0]) { - int i; - for (i=0; i < 32; ++i) - tab[i] = (1 << i) + 2*tab[i] - 1; - tab[1] = 2; - tab[0] = 1; - } - if (n >= 32) return 0xfffffffb; - return tab[n]; -} - -double stb_linear_remap(double x, double x_min, double x_max, - double out_min, double out_max) -{ - return stb_lerp(stb_unlerp(x,x_min,x_max),out_min,out_max); -} -#endif - -// create a macro so it's faster, but you can get at the function pointer -#define stb_linear_remap(t,a,b,c,d) stb_lerp(stb_unlerp(t,a,b),c,d) - - -////////////////////////////////////////////////////////////////////////////// -// -// bit operations -// - -#define stb_big32(c) (((c)[0]<<24) + (c)[1]*65536 + (c)[2]*256 + (c)[3]) -#define stb_little32(c) (((c)[3]<<24) + (c)[2]*65536 + (c)[1]*256 + (c)[0]) -#define stb_big16(c) ((c)[0]*256 + (c)[1]) -#define stb_little16(c) ((c)[1]*256 + (c)[0]) - -STB_EXTERN int stb_bitcount(unsigned int a); -STB_EXTERN unsigned int stb_bitreverse8(unsigned char n); -STB_EXTERN unsigned int stb_bitreverse(unsigned int n); - -STB_EXTERN int stb_is_pow2(unsigned int n); -STB_EXTERN int stb_log2_ceil(unsigned int n); -STB_EXTERN int stb_log2_floor(unsigned int n); - -STB_EXTERN int stb_lowbit8(unsigned int n); -STB_EXTERN int stb_highbit8(unsigned int n); - -#ifdef STB_DEFINE -int stb_bitcount(unsigned int a) -{ - a = (a & 0x55555555) + ((a >> 1) & 0x55555555); // max 2 - a = (a & 0x33333333) + ((a >> 2) & 0x33333333); // max 4 - a = (a + (a >> 4)) & 0x0f0f0f0f; // max 8 per 4, now 8 bits - a = (a + (a >> 8)); // max 16 per 8 bits - a = (a + (a >> 16)); // max 32 per 8 bits - return a & 0xff; -} - -unsigned int stb_bitreverse8(unsigned char n) -{ - n = ((n & 0xAA) >> 1) + ((n & 0x55) << 1); - n = ((n & 0xCC) >> 2) + ((n & 0x33) << 2); - return (unsigned char) ((n >> 4) + (n << 4)); -} - -unsigned int stb_bitreverse(unsigned int n) -{ - n = ((n & 0xAAAAAAAA) >> 1) | ((n & 0x55555555) << 1); - n = ((n & 0xCCCCCCCC) >> 2) | ((n & 0x33333333) << 2); - n = ((n & 0xF0F0F0F0) >> 4) | ((n & 0x0F0F0F0F) << 4); - n = ((n & 0xFF00FF00) >> 8) | ((n & 0x00FF00FF) << 8); - return (n >> 16) | (n << 16); -} - -int stb_is_pow2(unsigned int n) -{ - return (n & (n-1)) == 0; -} - -// tricky use of 4-bit table to identify 5 bit positions (note the '-1') -// 3-bit table would require another tree level; 5-bit table wouldn't save one -#if defined(_WIN32) && !defined(__MINGW32__) -#pragma warning(push) -#pragma warning(disable: 4035) // disable warning about no return value -int stb_log2_floor(unsigned int n) -{ - #if _MSC_VER > 1700 - unsigned long i; - _BitScanReverse(&i, n); - return i != 0 ? i : -1; - #else - __asm { - bsr eax,n - jnz done - mov eax,-1 - } - done:; - #endif -} -#pragma warning(pop) -#else -int stb_log2_floor(unsigned int n) -{ - static signed char log2_4[16] = { -1,0,1,1,2,2,2,2,3,3,3,3,3,3,3,3 }; - - // 2 compares if n < 16, 3 compares otherwise - if (n < (1U << 14)) - if (n < (1U << 4)) return 0 + log2_4[n ]; - else if (n < (1U << 9)) return 5 + log2_4[n >> 5]; - else return 10 + log2_4[n >> 10]; - else if (n < (1U << 24)) - if (n < (1U << 19)) return 15 + log2_4[n >> 15]; - else return 20 + log2_4[n >> 20]; - else if (n < (1U << 29)) return 25 + log2_4[n >> 25]; - else return 30 + log2_4[n >> 30]; -} -#endif - -// define ceil from floor -int stb_log2_ceil(unsigned int n) -{ - if (stb_is_pow2(n)) return stb_log2_floor(n); - else return 1 + stb_log2_floor(n); -} - -int stb_highbit8(unsigned int n) -{ - return stb_log2_ceil(n&255); -} - -int stb_lowbit8(unsigned int n) -{ - static signed char lowbit4[16] = { -1,0,1,0, 2,0,1,0, 3,0,1,0, 2,0,1,0 }; - int k = lowbit4[n & 15]; - if (k >= 0) return k; - k = lowbit4[(n >> 4) & 15]; - if (k >= 0) return k+4; - return k; -} -#endif - - - -////////////////////////////////////////////////////////////////////////////// -// -// qsort Compare Routines -// - -#ifdef _WIN32 - #define stb_stricmp(a,b) stricmp(a,b) - #define stb_strnicmp(a,b,n) strnicmp(a,b,n) -#else - #define stb_stricmp(a,b) strcasecmp(a,b) - #define stb_strnicmp(a,b,n) strncasecmp(a,b,n) -#endif - - -STB_EXTERN int (*stb_intcmp(int offset))(const void *a, const void *b); -STB_EXTERN int (*stb_qsort_strcmp(int offset))(const void *a, const void *b); -STB_EXTERN int (*stb_qsort_stricmp(int offset))(const void *a, const void *b); -STB_EXTERN int (*stb_floatcmp(int offset))(const void *a, const void *b); -STB_EXTERN int (*stb_doublecmp(int offset))(const void *a, const void *b); -STB_EXTERN int (*stb_charcmp(int offset))(const void *a, const void *b); - -#ifdef STB_DEFINE -static int stb__intcmpoffset, stb__charcmpoffset, stb__strcmpoffset; -static int stb__floatcmpoffset, stb__doublecmpoffset; - -int stb__intcmp(const void *a, const void *b) -{ - const int p = *(const int *) ((const char *) a + stb__intcmpoffset); - const int q = *(const int *) ((const char *) b + stb__intcmpoffset); - return p < q ? -1 : p > q; -} - -int stb__charcmp(const void *a, const void *b) -{ - const int p = *(const unsigned char *) ((const char *) a + stb__charcmpoffset); - const int q = *(const unsigned char *) ((const char *) b + stb__charcmpoffset); - return p < q ? -1 : p > q; -} - -int stb__floatcmp(const void *a, const void *b) -{ - const float p = *(const float *) ((const char *) a + stb__floatcmpoffset); - const float q = *(const float *) ((const char *) b + stb__floatcmpoffset); - return p < q ? -1 : p > q; -} - -int stb__doublecmp(const void *a, const void *b) -{ - const double p = *(const double *) ((const char *) a + stb__doublecmpoffset); - const double q = *(const double *) ((const char *) b + stb__doublecmpoffset); - return p < q ? -1 : p > q; -} - -int stb__qsort_strcmp(const void *a, const void *b) -{ - const char *p = *(const char **) ((const char *) a + stb__strcmpoffset); - const char *q = *(const char **) ((const char *) b + stb__strcmpoffset); - return strcmp(p,q); -} - -int stb__qsort_stricmp(const void *a, const void *b) -{ - const char *p = *(const char **) ((const char *) a + stb__strcmpoffset); - const char *q = *(const char **) ((const char *) b + stb__strcmpoffset); - return stb_stricmp(p,q); -} - -int (*stb_intcmp(int offset))(const void *, const void *) -{ - stb__intcmpoffset = offset; - return &stb__intcmp; -} - -int (*stb_charcmp(int offset))(const void *, const void *) -{ - stb__charcmpoffset = offset; - return &stb__charcmp; -} - -int (*stb_qsort_strcmp(int offset))(const void *, const void *) -{ - stb__strcmpoffset = offset; - return &stb__qsort_strcmp; -} - -int (*stb_qsort_stricmp(int offset))(const void *, const void *) -{ - stb__strcmpoffset = offset; - return &stb__qsort_stricmp; -} - -int (*stb_floatcmp(int offset))(const void *, const void *) -{ - stb__floatcmpoffset = offset; - return &stb__floatcmp; -} - -int (*stb_doublecmp(int offset))(const void *, const void *) -{ - stb__doublecmpoffset = offset; - return &stb__doublecmp; -} - -#endif - -////////////////////////////////////////////////////////////////////////////// -// -// Binary Search Toolkit -// - -typedef struct -{ - int minval, maxval, guess; - int mode, step; -} stb_search; - -STB_EXTERN int stb_search_binary(stb_search *s, int minv, int maxv, int find_smallest); -STB_EXTERN int stb_search_open(stb_search *s, int minv, int find_smallest); -STB_EXTERN int stb_probe(stb_search *s, int compare, int *result); // return 0 when done - -#ifdef STB_DEFINE -enum -{ - STB_probe_binary_smallest, - STB_probe_binary_largest, - STB_probe_open_smallest, - STB_probe_open_largest, -}; - -static int stb_probe_guess(stb_search *s, int *result) -{ - switch(s->mode) { - case STB_probe_binary_largest: - if (s->minval == s->maxval) { - *result = s->minval; - return 0; - } - assert(s->minval < s->maxval); - // if a < b, then a < p <= b - s->guess = s->minval + (((unsigned) s->maxval - s->minval + 1) >> 1); - break; - - case STB_probe_binary_smallest: - if (s->minval == s->maxval) { - *result = s->minval; - return 0; - } - assert(s->minval < s->maxval); - // if a < b, then a <= p < b - s->guess = s->minval + (((unsigned) s->maxval - s->minval) >> 1); - break; - case STB_probe_open_smallest: - case STB_probe_open_largest: - s->guess = s->maxval; // guess the current maxval - break; - } - *result = s->guess; - return 1; -} - -int stb_probe(stb_search *s, int compare, int *result) -{ - switch(s->mode) { - case STB_probe_open_smallest: - case STB_probe_open_largest: { - if (compare <= 0) { - // then it lies within minval & maxval - if (s->mode == STB_probe_open_smallest) - s->mode = STB_probe_binary_smallest; - else - s->mode = STB_probe_binary_largest; - } else { - // otherwise, we need to probe larger - s->minval = s->maxval + 1; - s->maxval = s->minval + s->step; - s->step += s->step; - } - break; - } - case STB_probe_binary_smallest: { - // if compare < 0, then s->minval <= a < p - // if compare = 0, then s->minval <= a <= p - // if compare > 0, then p < a <= s->maxval - if (compare <= 0) - s->maxval = s->guess; - else - s->minval = s->guess+1; - break; - } - case STB_probe_binary_largest: { - // if compare < 0, then s->minval <= a < p - // if compare = 0, then p <= a <= s->maxval - // if compare > 0, then p < a <= s->maxval - if (compare < 0) - s->maxval = s->guess-1; - else - s->minval = s->guess; - break; - } - } - return stb_probe_guess(s, result); -} - -int stb_search_binary(stb_search *s, int minv, int maxv, int find_smallest) -{ - int r; - if (maxv < minv) return minv-1; - s->minval = minv; - s->maxval = maxv; - s->mode = find_smallest ? STB_probe_binary_smallest : STB_probe_binary_largest; - stb_probe_guess(s, &r); - return r; -} - -int stb_search_open(stb_search *s, int minv, int find_smallest) -{ - int r; - s->step = 4; - s->minval = minv; - s->maxval = minv+s->step; - s->mode = find_smallest ? STB_probe_open_smallest : STB_probe_open_largest; - stb_probe_guess(s, &r); - return r; -} -#endif - -////////////////////////////////////////////////////////////////////////////// -// -// String Processing -// - -#define stb_prefixi(s,t) (0==stb_strnicmp((s),(t),strlen(t))) - -enum stb_splitpath_flag -{ - STB_PATH = 1, - STB_FILE = 2, - STB_EXT = 4, - STB_PATH_FILE = STB_PATH + STB_FILE, - STB_FILE_EXT = STB_FILE + STB_EXT, - STB_EXT_NO_PERIOD = 8, -}; - -STB_EXTERN char * stb_skipwhite(char *s); -STB_EXTERN char * stb_trimwhite(char *s); -STB_EXTERN char * stb_skipnewline(char *s); -STB_EXTERN char * stb_strncpy(char *s, char *t, int n); -STB_EXTERN char * stb_substr(char *t, int n); -STB_EXTERN char * stb_duplower(char *s); -STB_EXTERN void stb_tolower (char *s); -STB_EXTERN char * stb_strchr2 (char *s, char p1, char p2); -STB_EXTERN char * stb_strrchr2(char *s, char p1, char p2); -STB_EXTERN char * stb_strtok(char *output, char *src, char *delimit); -STB_EXTERN char * stb_strtok_keep(char *output, char *src, char *delimit); -STB_EXTERN char * stb_strtok_invert(char *output, char *src, char *allowed); -STB_EXTERN char * stb_dupreplace(char *s, char *find, char *replace); -STB_EXTERN void stb_replaceinplace(char *s, char *find, char *replace); -STB_EXTERN char * stb_splitpath(char *output, char *src, int flag); -STB_EXTERN char * stb_splitpathdup(char *src, int flag); -STB_EXTERN char * stb_replacedir(char *output, char *src, char *dir); -STB_EXTERN char * stb_replaceext(char *output, char *src, char *ext); -STB_EXTERN void stb_fixpath(char *path); -STB_EXTERN char * stb_shorten_path_readable(char *path, int max_len); -STB_EXTERN int stb_suffix (char *s, char *t); -STB_EXTERN int stb_suffixi(char *s, char *t); -STB_EXTERN int stb_prefix (char *s, char *t); -STB_EXTERN char * stb_strichr(char *s, char t); -STB_EXTERN char * stb_stristr(char *s, char *t); -STB_EXTERN int stb_prefix_count(char *s, char *t); -STB_EXTERN char * stb_plural(int n); // "s" or "" -STB_EXTERN size_t stb_strscpy(char *d, const char *s, size_t n); - -STB_EXTERN char **stb_tokens(char *src, char *delimit, int *count); -STB_EXTERN char **stb_tokens_nested(char *src, char *delimit, int *count, char *nest_in, char *nest_out); -STB_EXTERN char **stb_tokens_nested_empty(char *src, char *delimit, int *count, char *nest_in, char *nest_out); -STB_EXTERN char **stb_tokens_allowempty(char *src, char *delimit, int *count); -STB_EXTERN char **stb_tokens_stripwhite(char *src, char *delimit, int *count); -STB_EXTERN char **stb_tokens_withdelim(char *src, char *delimit, int *count); -STB_EXTERN char **stb_tokens_quoted(char *src, char *delimit, int *count); -// with 'quoted', allow delimiters to appear inside quotation marks, and don't -// strip whitespace inside them (and we delete the quotation marks unless they -// appear back to back, in which case they're considered escaped) - -#ifdef STB_DEFINE - -size_t stb_strscpy(char *d, const char *s, size_t n) -{ - size_t len = strlen(s); - if (len >= n) { - if (n) d[0] = 0; - return 0; - } - strcpy(d,s); - return len + 1; -} - -char *stb_plural(int n) -{ - return n == 1 ? "" : "s"; -} - -int stb_prefix(char *s, char *t) -{ - while (*t) - if (*s++ != *t++) - return STB_FALSE; - return STB_TRUE; -} - -int stb_prefix_count(char *s, char *t) -{ - int c=0; - while (*t) { - if (*s++ != *t++) - break; - ++c; - } - return c; -} - -int stb_suffix(char *s, char *t) -{ - size_t n = strlen(s); - size_t m = strlen(t); - if (m <= n) - return 0 == strcmp(s+n-m, t); - else - return 0; -} - -int stb_suffixi(char *s, char *t) -{ - size_t n = strlen(s); - size_t m = strlen(t); - if (m <= n) - return 0 == stb_stricmp(s+n-m, t); - else - return 0; -} - -// originally I was using this table so that I could create known sentinel -// values--e.g. change whitetable[0] to be true if I was scanning for whitespace, -// and false if I was scanning for nonwhite. I don't appear to be using that -// functionality anymore (I do for tokentable, though), so just replace it -// with isspace() -char *stb_skipwhite(char *s) -{ - while (isspace((unsigned char) *s)) ++s; - return s; -} - -char *stb_skipnewline(char *s) -{ - if (s[0] == '\r' || s[0] == '\n') { - if (s[0]+s[1] == '\r' + '\n') ++s; - ++s; - } - return s; -} - -char *stb_trimwhite(char *s) -{ - int i,n; - s = stb_skipwhite(s); - n = (int) strlen(s); - for (i=n-1; i >= 0; --i) - if (!isspace(s[i])) - break; - s[i+1] = 0; - return s; -} - -char *stb_strncpy(char *s, char *t, int n) -{ - strncpy(s,t,n); - s[n-1] = 0; - return s; -} - -char *stb_substr(char *t, int n) -{ - char *a; - int z = (int) strlen(t); - if (z < n) n = z; - a = (char *) malloc(n+1); - strncpy(a,t,n); - a[n] = 0; - return a; -} - -char *stb_duplower(char *s) -{ - char *p = strdup(s), *q = p; - while (*q) { - *q = tolower(*q); - ++q; - } - return p; -} - -void stb_tolower(char *s) -{ - while (*s) { - *s = tolower(*s); - ++s; - } -} - -char *stb_strchr2(char *s, char x, char y) -{ - for(; *s; ++s) - if (*s == x || *s == y) - return s; - return NULL; -} - -char *stb_strrchr2(char *s, char x, char y) -{ - char *r = NULL; - for(; *s; ++s) - if (*s == x || *s == y) - r = s; - return r; -} - -char *stb_strichr(char *s, char t) -{ - if (tolower(t) == toupper(t)) - return strchr(s,t); - return stb_strchr2(s, (char) tolower(t), (char) toupper(t)); -} - -char *stb_stristr(char *s, char *t) -{ - size_t n = strlen(t); - char *z; - if (n==0) return s; - while ((z = stb_strichr(s, *t)) != NULL) { - if (0==stb_strnicmp(z, t, n)) - return z; - s = z+1; - } - return NULL; -} - -static char *stb_strtok_raw(char *output, char *src, char *delimit, int keep, int invert) -{ - if (invert) { - while (*src && strchr(delimit, *src) != NULL) { - *output++ = *src++; - } - } else { - while (*src && strchr(delimit, *src) == NULL) { - *output++ = *src++; - } - } - *output = 0; - if (keep) - return src; - else - return *src ? src+1 : src; -} - -char *stb_strtok(char *output, char *src, char *delimit) -{ - return stb_strtok_raw(output, src, delimit, 0, 0); -} - -char *stb_strtok_keep(char *output, char *src, char *delimit) -{ - return stb_strtok_raw(output, src, delimit, 1, 0); -} - -char *stb_strtok_invert(char *output, char *src, char *delimit) -{ - return stb_strtok_raw(output, src, delimit, 1,1); -} - -static char **stb_tokens_raw(char *src_, char *delimit, int *count, - int stripwhite, int allow_empty, char *start, char *end) -{ - int nested = 0; - unsigned char *src = (unsigned char *) src_; - static char stb_tokentable[256]; // rely on static initializion to 0 - static char stable[256],etable[256]; - char *out; - char **result; - int num=0; - unsigned char *s; - - s = (unsigned char *) delimit; while (*s) stb_tokentable[*s++] = 1; - if (start) { - s = (unsigned char *) start; while (*s) stable[*s++] = 1; - s = (unsigned char *) end; if (s) while (*s) stable[*s++] = 1; - s = (unsigned char *) end; if (s) while (*s) etable[*s++] = 1; - } - stable[0] = 1; - - // two passes through: the first time, counting how many - s = (unsigned char *) src; - while (*s) { - // state: just found delimiter - // skip further delimiters - if (!allow_empty) { - stb_tokentable[0] = 0; - while (stb_tokentable[*s]) - ++s; - if (!*s) break; - } - ++num; - // skip further non-delimiters - stb_tokentable[0] = 1; - if (stripwhite == 2) { // quoted strings - while (!stb_tokentable[*s]) { - if (*s != '"') - ++s; - else { - ++s; - if (*s == '"') - ++s; // "" -> ", not start a string - else { - // begin a string - while (*s) { - if (s[0] == '"') { - if (s[1] == '"') s += 2; // "" -> " - else { ++s; break; } // terminating " - } else - ++s; - } - } - } - } - } else - while (nested || !stb_tokentable[*s]) { - if (stable[*s]) { - if (!*s) break; - if (end ? etable[*s] : nested) - --nested; - else - ++nested; - } - ++s; - } - if (allow_empty) { - if (*s) ++s; - } - } - // now num has the actual count... malloc our output structure - // need space for all the strings: strings won't be any longer than - // original input, since for every '\0' there's at least one delimiter - result = (char **) malloc(sizeof(*result) * (num+1) + (s-src+1)); - if (result == NULL) return result; - out = (char *) (result + (num+1)); - // second pass: copy out the data - s = (unsigned char *) src; - num = 0; - nested = 0; - while (*s) { - char *last_nonwhite; - // state: just found delimiter - // skip further delimiters - if (!allow_empty) { - stb_tokentable[0] = 0; - if (stripwhite) - while (stb_tokentable[*s] || isspace(*s)) - ++s; - else - while (stb_tokentable[*s]) - ++s; - } else if (stripwhite) { - while (isspace(*s)) ++s; - } - if (!*s) break; - // we're past any leading delimiters and whitespace - result[num] = out; - ++num; - // copy non-delimiters - stb_tokentable[0] = 1; - last_nonwhite = out-1; - if (stripwhite == 2) { - while (!stb_tokentable[*s]) { - if (*s != '"') { - if (!isspace(*s)) last_nonwhite = out; - *out++ = *s++; - } else { - ++s; - if (*s == '"') { - if (!isspace(*s)) last_nonwhite = out; - *out++ = *s++; // "" -> ", not start string - } else { - // begin a quoted string - while (*s) { - if (s[0] == '"') { - if (s[1] == '"') { *out++ = *s; s += 2; } - else { ++s; break; } // terminating " - } else - *out++ = *s++; - } - last_nonwhite = out-1; // all in quotes counts as non-white - } - } - } - } else { - while (nested || !stb_tokentable[*s]) { - if (!isspace(*s)) last_nonwhite = out; - if (stable[*s]) { - if (!*s) break; - if (end ? etable[*s] : nested) - --nested; - else - ++nested; - } - *out++ = *s++; - } - } - - if (stripwhite) // rewind to last non-whitespace char - out = last_nonwhite+1; - *out++ = '\0'; - - if (*s) ++s; // skip delimiter - } - s = (unsigned char *) delimit; while (*s) stb_tokentable[*s++] = 0; - if (start) { - s = (unsigned char *) start; while (*s) stable[*s++] = 1; - s = (unsigned char *) end; if (s) while (*s) stable[*s++] = 1; - s = (unsigned char *) end; if (s) while (*s) etable[*s++] = 1; - } - if (count != NULL) *count = num; - result[num] = 0; - return result; -} - -char **stb_tokens(char *src, char *delimit, int *count) -{ - return stb_tokens_raw(src,delimit,count,0,0,0,0); -} - -char **stb_tokens_nested(char *src, char *delimit, int *count, char *nest_in, char *nest_out) -{ - return stb_tokens_raw(src,delimit,count,0,0,nest_in,nest_out); -} - -char **stb_tokens_nested_empty(char *src, char *delimit, int *count, char *nest_in, char *nest_out) -{ - return stb_tokens_raw(src,delimit,count,0,1,nest_in,nest_out); -} - -char **stb_tokens_allowempty(char *src, char *delimit, int *count) -{ - return stb_tokens_raw(src,delimit,count,0,1,0,0); -} - -char **stb_tokens_stripwhite(char *src, char *delimit, int *count) -{ - return stb_tokens_raw(src,delimit,count,1,1,0,0); -} - -char **stb_tokens_quoted(char *src, char *delimit, int *count) -{ - return stb_tokens_raw(src,delimit,count,2,1,0,0); -} - -char *stb_dupreplace(char *src, char *find, char *replace) -{ - size_t len_find = strlen(find); - size_t len_replace = strlen(replace); - int count = 0; - - char *s,*p,*q; - - s = strstr(src, find); - if (s == NULL) return strdup(src); - do { - ++count; - s = strstr(s + len_find, find); - } while (s != NULL); - - p = (char *) malloc(strlen(src) + count * (len_replace - len_find) + 1); - if (p == NULL) return p; - q = p; - s = src; - for (;;) { - char *t = strstr(s, find); - if (t == NULL) { - strcpy(q,s); - assert(strlen(p) == strlen(src) + count*(len_replace-len_find)); - return p; - } - memcpy(q, s, t-s); - q += t-s; - memcpy(q, replace, len_replace); - q += len_replace; - s = t + len_find; - } -} - -void stb_replaceinplace(char *src, char *find, char *replace) -{ - size_t len_find = strlen(find); - size_t len_replace = strlen(replace); - int delta; - - char *s,*p,*q; - - delta = len_replace - len_find; - assert(delta <= 0); - if (delta > 0) return; - - p = strstr(src, find); - if (p == NULL) return; - - s = q = p; - while (*s) { - memcpy(q, replace, len_replace); - p += len_find; - q += len_replace; - s = strstr(p, find); - if (s == NULL) s = p + strlen(p); - memmove(q, p, s-p); - q += s-p; - p = s; - } - *q = 0; -} - -void stb_fixpath(char *path) -{ - for(; *path; ++path) - if (*path == '\\') - *path = '/'; -} - -void stb__add_section(char *buffer, char *data, int curlen, int newlen) -{ - if (newlen < curlen) { - int z1 = newlen >> 1, z2 = newlen-z1; - memcpy(buffer, data, z1-1); - buffer[z1-1] = '.'; - buffer[z1-0] = '.'; - memcpy(buffer+z1+1, data+curlen-z2+1, z2-1); - } else - memcpy(buffer, data, curlen); -} - -char * stb_shorten_path_readable(char *path, int len) -{ - static char buffer[1024]; - int n = strlen(path),n1,n2,r1,r2; - char *s; - if (n <= len) return path; - if (len > 1024) return path; - s = stb_strrchr2(path, '/', '\\'); - if (s) { - n1 = s - path + 1; - n2 = n - n1; - ++s; - } else { - n1 = 0; - n2 = n; - s = path; - } - // now we need to reduce r1 and r2 so that they fit in len - if (n1 < len>>1) { - r1 = n1; - r2 = len - r1; - } else if (n2 < len >> 1) { - r2 = n2; - r1 = len - r2; - } else { - r1 = n1 * len / n; - r2 = n2 * len / n; - if (r1 < len>>2) r1 = len>>2, r2 = len-r1; - if (r2 < len>>2) r2 = len>>2, r1 = len-r2; - } - assert(r1 <= n1 && r2 <= n2); - if (n1) - stb__add_section(buffer, path, n1, r1); - stb__add_section(buffer+r1, s, n2, r2); - buffer[len] = 0; - return buffer; -} - -static char *stb__splitpath_raw(char *buffer, char *path, int flag) -{ - int len=0,x,y, n = (int) strlen(path), f1,f2; - char *s = stb_strrchr2(path, '/', '\\'); - char *t = strrchr(path, '.'); - if (s && t && t < s) t = NULL; - if (s) ++s; - - if (flag == STB_EXT_NO_PERIOD) - flag |= STB_EXT; - - if (!(flag & (STB_PATH | STB_FILE | STB_EXT))) return NULL; - - f1 = s == NULL ? 0 : s-path; // start of filename - f2 = t == NULL ? n : t-path; // just past end of filename - - if (flag & STB_PATH) { - x = 0; if (f1 == 0 && flag == STB_PATH) len=2; - } else if (flag & STB_FILE) { - x = f1; - } else { - x = f2; - if (flag & STB_EXT_NO_PERIOD) - if (buffer[x] == '.') - ++x; - } - - if (flag & STB_EXT) - y = n; - else if (flag & STB_FILE) - y = f2; - else - y = f1; - - if (buffer == NULL) { - buffer = (char *) malloc(y-x + len + 1); - if (!buffer) return NULL; - } - - if (len) { strcpy(buffer, "./"); return buffer; } - strncpy(buffer, path+x, y-x); - buffer[y-x] = 0; - return buffer; -} - -char *stb_splitpath(char *output, char *src, int flag) -{ - return stb__splitpath_raw(output, src, flag); -} - -char *stb_splitpathdup(char *src, int flag) -{ - return stb__splitpath_raw(NULL, src, flag); -} - -char *stb_replacedir(char *output, char *src, char *dir) -{ - char buffer[4096]; - stb_splitpath(buffer, src, STB_FILE | STB_EXT); - if (dir) - sprintf(output, "%s/%s", dir, buffer); - else - strcpy(output, buffer); - return output; -} - -char *stb_replaceext(char *output, char *src, char *ext) -{ - char buffer[4096]; - stb_splitpath(buffer, src, STB_PATH | STB_FILE); - if (ext) - sprintf(output, "%s.%s", buffer, ext[0] == '.' ? ext+1 : ext); - else - strcpy(output, buffer); - return output; -} -#endif - -////////////////////////////////////////////////////////////////////////////// -// -// stb_alloc - hierarchical allocator -// -// inspired by http://swapped.cc/halloc -// -// -// When you alloc a given block through stb_alloc, you have these choices: -// -// 1. does it have a parent? -// 2. can it have children? -// 3. can it be freed directly? -// 4. is it transferrable? -// 5. what is its alignment? -// -// Here are interesting combinations of those: -// -// children free transfer alignment -// arena Y Y N n/a -// no-overhead, chunked N N N normal -// string pool alloc N N N 1 -// parent-ptr, chunked Y N N normal -// low-overhead, unchunked N Y Y normal -// general purpose alloc Y Y Y normal -// -// Unchunked allocations will probably return 16-aligned pointers. If -// we 16-align the results, we have room for 4 pointers. For smaller -// allocations that allow finer alignment, we can reduce the pointers. -// -// The strategy is that given a pointer, assuming it has a header (only -// the no-overhead allocations have no header), we can determine the -// type of the header fields, and the number of them, by stepping backwards -// through memory and looking at the tags in the bottom bits. -// -// Implementation strategy: -// chunked allocations come from the middle of chunks, and can't -// be freed. thefore they do not need to be on a sibling chain. -// they may need child pointers if they have children. -// -// chunked, with-children -// void *parent; -// -// unchunked, no-children -- reduced storage -// void *next_sibling; -// void *prev_sibling_nextp; -// -// unchunked, general -// void *first_child; -// void *next_sibling; -// void *prev_sibling_nextp; -// void *chunks; -// -// so, if we code each of these fields with different bit patterns -// (actually same one for next/prev/child), then we can identify which -// each one is from the last field. - -STB_EXTERN void stb_free(void *p); -STB_EXTERN void *stb_malloc_global(size_t size); -STB_EXTERN void *stb_malloc(void *context, size_t size); -STB_EXTERN void *stb_malloc_nofree(void *context, size_t size); -STB_EXTERN void *stb_malloc_leaf(void *context, size_t size); -STB_EXTERN void *stb_malloc_raw(void *context, size_t size); -STB_EXTERN void *stb_realloc(void *ptr, size_t newsize); - -STB_EXTERN void stb_reassign(void *new_context, void *ptr); -STB_EXTERN void stb_malloc_validate(void *p, void *parent); - -extern int stb_alloc_chunk_size ; -extern int stb_alloc_count_free ; -extern int stb_alloc_count_alloc; -extern int stb_alloc_alignment ; - -#ifdef STB_DEFINE - -int stb_alloc_chunk_size = 65536; -int stb_alloc_count_free = 0; -int stb_alloc_count_alloc = 0; -int stb_alloc_alignment = -16; - -typedef struct stb__chunk -{ - struct stb__chunk *next; - int data_left; - int alloc; -} stb__chunk; - -typedef struct -{ - void * next; - void ** prevn; -} stb__nochildren; - -typedef struct -{ - void ** prevn; - void * child; - void * next; - stb__chunk *chunks; -} stb__alloc; - -typedef struct -{ - stb__alloc *parent; -} stb__chunked; - -#define STB__PARENT 1 -#define STB__CHUNKS 2 - -typedef enum -{ - STB__nochildren = 0, - STB__chunked = STB__PARENT, - STB__alloc = STB__CHUNKS, - - STB__chunk_raw = 4, -} stb__alloc_type; - -// these functions set the bottom bits of a pointer efficiently -#define STB__DECODE(x,v) ((void *) ((char *) (x) - (v))) -#define STB__ENCODE(x,v) ((void *) ((char *) (x) + (v))) - -#define stb__parent(z) (stb__alloc *) STB__DECODE((z)->parent, STB__PARENT) -#define stb__chunks(z) (stb__chunk *) STB__DECODE((z)->chunks, STB__CHUNKS) - -#define stb__setparent(z,p) (z)->parent = (stb__alloc *) STB__ENCODE((p), STB__PARENT) -#define stb__setchunks(z,c) (z)->chunks = (stb__chunk *) STB__ENCODE((c), STB__CHUNKS) - -static stb__alloc stb__alloc_global = -{ - NULL, - NULL, - NULL, - (stb__chunk *) STB__ENCODE(NULL, STB__CHUNKS) -}; - -static stb__alloc_type stb__identify(void *p) -{ - void **q = (void **) p; - return (stb__alloc_type) ((stb_uinta) q[-1] & 3); -} - -static void *** stb__prevn(void *p) -{ - if (stb__identify(p) == STB__alloc) { - stb__alloc *s = (stb__alloc *) p - 1; - return &s->prevn; - } else { - stb__nochildren *s = (stb__nochildren *) p - 1; - return &s->prevn; - } -} - -void stb_free(void *p) -{ - if (p == NULL) return; - - // count frees so that unit tests can see what's happening - ++stb_alloc_count_free; - - switch(stb__identify(p)) { - case STB__chunked: - // freeing a chunked-block with children does nothing; - // they only get freed when the parent does - // surely this is wrong, and it should free them immediately? - // otherwise how are they getting put on the right chain? - return; - case STB__nochildren: { - stb__nochildren *s = (stb__nochildren *) p - 1; - // unlink from sibling chain - *(s->prevn) = s->next; - if (s->next) - *stb__prevn(s->next) = s->prevn; - free(s); - return; - } - case STB__alloc: { - stb__alloc *s = (stb__alloc *) p - 1; - stb__chunk *c, *n; - void *q; - - // unlink from sibling chain, if any - *(s->prevn) = s->next; - if (s->next) - *stb__prevn(s->next) = s->prevn; - - // first free chunks - c = (stb__chunk *) stb__chunks(s); - while (c != NULL) { - n = c->next; - stb_alloc_count_free += c->alloc; - free(c); - c = n; - } - - // validating - stb__setchunks(s,NULL); - s->prevn = NULL; - s->next = NULL; - - // now free children - while ((q = s->child) != NULL) { - stb_free(q); - } - - // now free self - free(s); - return; - } - default: - assert(0); /* NOTREACHED */ - } -} - -void stb_malloc_validate(void *p, void *parent) -{ - if (p == NULL) return; - - switch(stb__identify(p)) { - case STB__chunked: - return; - case STB__nochildren: { - stb__nochildren *n = (stb__nochildren *) p - 1; - if (n->prevn) - assert(*n->prevn == p); - if (n->next) { - assert(*stb__prevn(n->next) == &n->next); - stb_malloc_validate(n, parent); - } - return; - } - case STB__alloc: { - stb__alloc *s = (stb__alloc *) p - 1; - - if (s->prevn) - assert(*s->prevn == p); - - if (s->child) { - assert(*stb__prevn(s->child) == &s->child); - stb_malloc_validate(s->child, p); - } - - if (s->next) { - assert(*stb__prevn(s->next) == &s->next); - stb_malloc_validate(s->next, parent); - } - return; - } - default: - assert(0); /* NOTREACHED */ - } -} - -static void * stb__try_chunk(stb__chunk *c, int size, int align, int pre_align) -{ - char *memblock = (char *) (c+1), *q; - stb_inta iq; - int start_offset; - - // we going to allocate at the end of the chunk, not the start. confusing, - // but it means we don't need both a 'limit' and a 'cur', just a 'cur'. - // the block ends at: p + c->data_left - // then we move back by size - start_offset = c->data_left - size; - - // now we need to check the alignment of that - q = memblock + start_offset; - iq = (stb_inta) q; - assert(sizeof(q) == sizeof(iq)); - - // suppose align = 2 - // then we need to retreat iq far enough that (iq & (2-1)) == 0 - // to get (iq & (align-1)) = 0 requires subtracting (iq & (align-1)) - - start_offset -= iq & (align-1); - assert(((stb_uinta) (memblock+start_offset) & (align-1)) == 0); - - // now, if that + pre_align works, go for it! - start_offset -= pre_align; - - if (start_offset >= 0) { - c->data_left = start_offset; - return memblock + start_offset; - } - - return NULL; -} - -static void stb__sort_chunks(stb__alloc *src) -{ - // of the first two chunks, put the chunk with more data left in it first - stb__chunk *c = stb__chunks(src), *d; - if (c == NULL) return; - d = c->next; - if (d == NULL) return; - if (c->data_left > d->data_left) return; - - c->next = d->next; - d->next = c; - stb__setchunks(src, d); -} - -static void * stb__alloc_chunk(stb__alloc *src, int size, int align, int pre_align) -{ - void *p; - stb__chunk *c = stb__chunks(src); - - if (c && size <= stb_alloc_chunk_size) { - - p = stb__try_chunk(c, size, align, pre_align); - if (p) { ++c->alloc; return p; } - - // try a second chunk to reduce wastage - if (c->next) { - p = stb__try_chunk(c->next, size, align, pre_align); - if (p) { ++c->alloc; return p; } - - // put the bigger chunk first, since the second will get buried - // the upshot of this is that, until it gets allocated from, chunk #2 - // is always the largest remaining chunk. (could formalize - // this with a heap!) - stb__sort_chunks(src); - c = stb__chunks(src); - } - } - - // allocate a new chunk - { - stb__chunk *n; - - int chunk_size = stb_alloc_chunk_size; - // we're going to allocate a new chunk to put this in - if (size > chunk_size) - chunk_size = size; - - assert(sizeof(*n) + pre_align <= 16); - - // loop trying to allocate a large enough chunk - // the loop is because the alignment may cause problems if it's big... - // and we don't know what our chunk alignment is going to be - while (1) { - n = (stb__chunk *) malloc(16 + chunk_size); - if (n == NULL) return NULL; - - n->data_left = chunk_size - sizeof(*n); - - p = stb__try_chunk(n, size, align, pre_align); - if (p != NULL) { - n->next = c; - stb__setchunks(src, n); - - // if we just used up the whole block immediately, - // move the following chunk up - n->alloc = 1; - if (size == chunk_size) - stb__sort_chunks(src); - - return p; - } - - free(n); - chunk_size += 16+align; - } - } -} - -static stb__alloc * stb__get_context(void *context) -{ - if (context == NULL) { - return &stb__alloc_global; - } else { - int u = stb__identify(context); - // if context is chunked, grab parent - if (u == STB__chunked) { - stb__chunked *s = (stb__chunked *) context - 1; - return stb__parent(s); - } else { - return (stb__alloc *) context - 1; - } - } -} - -static void stb__insert_alloc(stb__alloc *src, stb__alloc *s) -{ - s->prevn = &src->child; - s->next = src->child; - src->child = s+1; - if (s->next) - *stb__prevn(s->next) = &s->next; -} - -static void stb__insert_nochild(stb__alloc *src, stb__nochildren *s) -{ - s->prevn = &src->child; - s->next = src->child; - src->child = s+1; - if (s->next) - *stb__prevn(s->next) = &s->next; -} - -static void * malloc_base(void *context, size_t size, stb__alloc_type t, int align) -{ - void *p; - - stb__alloc *src = stb__get_context(context); - - if (align <= 0) { - // compute worst-case C packed alignment - // e.g. a 24-byte struct is 8-aligned - int align_proposed = 1 << stb_lowbit8(size); - - if (align_proposed < 0) - align_proposed = 4; - - if (align_proposed == 0) { - if (size == 0) - align_proposed = 1; - else - align_proposed = 256; - } - - // a negative alignment means 'don't align any larger - // than this'; so -16 means we align 1,2,4,8, or 16 - - if (align < 0) { - if (align_proposed > -align) - align_proposed = -align; - } - - align = align_proposed; - } - - assert(stb_is_pow2(align)); - - // don't cause misalignment when allocating nochildren - if (t == STB__nochildren && align > 8) - t = STB__alloc; - - switch (t) { - case STB__alloc: { - stb__alloc *s = (stb__alloc *) malloc(size + sizeof(*s)); - if (s == NULL) return NULL; - p = s+1; - s->child = NULL; - stb__insert_alloc(src, s); - - stb__setchunks(s,NULL); - break; - } - - case STB__nochildren: { - stb__nochildren *s = (stb__nochildren *) malloc(size + sizeof(*s)); - if (s == NULL) return NULL; - p = s+1; - stb__insert_nochild(src, s); - break; - } - - case STB__chunk_raw: { - p = stb__alloc_chunk(src, size, align, 0); - if (p == NULL) return NULL; - break; - } - - case STB__chunked: { - stb__chunked *s; - if (align < sizeof(stb_uintptr)) align = sizeof(stb_uintptr); - s = (stb__chunked *) stb__alloc_chunk(src, size, align, sizeof(*s)); - if (s == NULL) return NULL; - stb__setparent(s, src); - p = s+1; - break; - } - - default: p = NULL; assert(0); /* NOTREACHED */ - } - - ++stb_alloc_count_alloc; - return p; -} - -void *stb_malloc_global(size_t size) -{ - return malloc_base(NULL, size, STB__alloc, stb_alloc_alignment); -} - -void *stb_malloc(void *context, size_t size) -{ - return malloc_base(context, size, STB__alloc, stb_alloc_alignment); -} - -void *stb_malloc_nofree(void *context, size_t size) -{ - return malloc_base(context, size, STB__chunked, stb_alloc_alignment); -} - -void *stb_malloc_leaf(void *context, size_t size) -{ - return malloc_base(context, size, STB__nochildren, stb_alloc_alignment); -} - -void *stb_malloc_raw(void *context, size_t size) -{ - return malloc_base(context, size, STB__chunk_raw, stb_alloc_alignment); -} - -char *stb_malloc_string(void *context, size_t size) -{ - return (char *) malloc_base(context, size, STB__chunk_raw, 1); -} - -void *stb_realloc(void *ptr, size_t newsize) -{ - stb__alloc_type t; - - if (ptr == NULL) return stb_malloc(NULL, newsize); - if (newsize == 0) { stb_free(ptr); return NULL; } - - t = stb__identify(ptr); - assert(t == STB__alloc || t == STB__nochildren); - - if (t == STB__alloc) { - stb__alloc *s = (stb__alloc *) ptr - 1; - - s = (stb__alloc *) realloc(s, newsize + sizeof(*s)); - if (s == NULL) return NULL; - - ptr = s+1; - - // update pointers - (*s->prevn) = ptr; - if (s->next) - *stb__prevn(s->next) = &s->next; - - if (s->child) - *stb__prevn(s->child) = &s->child; - - return ptr; - } else { - stb__nochildren *s = (stb__nochildren *) ptr - 1; - - s = (stb__nochildren *) realloc(ptr, newsize + sizeof(s)); - if (s == NULL) return NULL; - - // update pointers - (*s->prevn) = s+1; - if (s->next) - *stb__prevn(s->next) = &s->next; - - return s+1; - } -} - -void *stb_realloc_c(void *context, void *ptr, size_t newsize) -{ - if (ptr == NULL) return stb_malloc(context, newsize); - if (newsize == 0) { stb_free(ptr); return NULL; } - // @TODO: verify you haven't changed contexts - return stb_realloc(ptr, newsize); -} - -void stb_reassign(void *new_context, void *ptr) -{ - stb__alloc *src = stb__get_context(new_context); - - stb__alloc_type t = stb__identify(ptr); - assert(t == STB__alloc || t == STB__nochildren); - - if (t == STB__alloc) { - stb__alloc *s = (stb__alloc *) ptr - 1; - - // unlink from old - *(s->prevn) = s->next; - if (s->next) - *stb__prevn(s->next) = s->prevn; - - stb__insert_alloc(src, s); - } else { - stb__nochildren *s = (stb__nochildren *) ptr - 1; - - // unlink from old - *(s->prevn) = s->next; - if (s->next) - *stb__prevn(s->next) = s->prevn; - - stb__insert_nochild(src, s); - } -} - -#endif - - -////////////////////////////////////////////////////////////////////////////// -// -// stb_arr -// -// An stb_arr is directly useable as a pointer (use the actual type in your -// definition), but when it resizes, it returns a new pointer and you can't -// use the old one, so you have to be careful to copy-in-out as necessary. -// -// Use a NULL pointer as a 0-length array. -// -// float *my_array = NULL, *temp; -// -// // add elements on the end one at a time -// stb_arr_push(my_array, 0.0f); -// stb_arr_push(my_array, 1.0f); -// stb_arr_push(my_array, 2.0f); -// -// assert(my_array[1] == 2.0f); -// -// // add an uninitialized element at the end, then assign it -// *stb_arr_add(my_array) = 3.0f; -// -// // add three uninitialized elements at the end -// temp = stb_arr_addn(my_array,3); -// temp[0] = 4.0f; -// temp[1] = 5.0f; -// temp[2] = 6.0f; -// -// assert(my_array[5] == 5.0f); -// -// // remove the last one -// stb_arr_pop(my_array); -// -// assert(stb_arr_len(my_array) == 6); - - -#ifdef STB_MALLOC_WRAPPER - #define STB__PARAMS , char *file, int line - #define STB__ARGS , file, line -#else - #define STB__PARAMS - #define STB__ARGS -#endif - -// calling this function allocates an empty stb_arr attached to p -// (whereas NULL isn't attached to anything) -STB_EXTERN void stb_arr_malloc(void **target, void *context); - -// call this function with a non-NULL value to have all successive -// stbs that are created be attached to the associated parent. Note -// that once a given stb_arr is non-empty, it stays attached to its -// current parent, even if you call this function again. -// it turns the previous value, so you can restore it -STB_EXTERN void* stb_arr_malloc_parent(void *p); - -// simple functions written on top of other functions -#define stb_arr_empty(a) ( stb_arr_len(a) == 0 ) -#define stb_arr_add(a) ( stb_arr_addn((a),1) ) -#define stb_arr_push(a,v) ( *stb_arr_add(a)=(v) ) - -typedef struct -{ - int len, limit; - int stb_malloc; - unsigned int signature; -} stb__arr; - -#define stb_arr_signature 0x51bada7b // ends with 0123 in decimal - -// access the header block stored before the data -#define stb_arrhead(a) /*lint --e(826)*/ (((stb__arr *) (a)) - 1) -#define stb_arrhead2(a) /*lint --e(826)*/ (((stb__arr *) (a)) - 1) - -#ifdef STB_DEBUG -#define stb_arr_check(a) assert(!a || stb_arrhead(a)->signature == stb_arr_signature) -#define stb_arr_check2(a) assert(!a || stb_arrhead2(a)->signature == stb_arr_signature) -#else -#define stb_arr_check(a) ((void) 0) -#define stb_arr_check2(a) ((void) 0) -#endif - -// ARRAY LENGTH - -// get the array length; special case if pointer is NULL -#define stb_arr_len(a) (a ? stb_arrhead(a)->len : 0) -#define stb_arr_len2(a) ((stb__arr *) (a) ? stb_arrhead2(a)->len : 0) -#define stb_arr_lastn(a) (stb_arr_len(a)-1) - -// check whether a given index is valid -- tests 0 <= i < stb_arr_len(a) -#define stb_arr_valid(a,i) (a ? (int) (i) < stb_arrhead(a)->len : 0) - -// change the array length so is is exactly N entries long, creating -// uninitialized entries as needed -#define stb_arr_setlen(a,n) \ - (stb__arr_setlen((void **) &(a), sizeof(a[0]), (n))) - -// change the array length so that N is a valid index (that is, so -// it is at least N entries long), creating uninitialized entries as needed -#define stb_arr_makevalid(a,n) \ - (stb_arr_len(a) < (n)+1 ? stb_arr_setlen(a,(n)+1),(a) : (a)) - -// remove the last element of the array, returning it -#define stb_arr_pop(a) ((stb_arr_check(a), (a))[--stb_arrhead(a)->len]) - -// access the last element in the array -#define stb_arr_last(a) ((stb_arr_check(a), (a))[stb_arr_len(a)-1]) - -// is iterator at end of list? -#define stb_arr_end(a,i) ((i) >= &(a)[stb_arr_len(a)]) - -// (internal) change the allocated length of the array -#define stb_arr__grow(a,n) (stb_arr_check(a), stb_arrhead(a)->len += (n)) - -// add N new unitialized elements to the end of the array -#define stb_arr__addn(a,n) /*lint --e(826)*/ \ - ((stb_arr_len(a)+(n) > stb_arrcurmax(a)) \ - ? (stb__arr_addlen((void **) &(a),sizeof(*a),(n)),0) \ - : ((stb_arr__grow(a,n), 0))) - -// add N new unitialized elements to the end of the array, and return -// a pointer to the first new one -#define stb_arr_addn(a,n) (stb_arr__addn((a),n),(a)+stb_arr_len(a)-(n)) - -// add N new uninitialized elements starting at index 'i' -#define stb_arr_insertn(a,i,n) (stb__arr_insertn((void **) &(a), sizeof(*a), i, n)) - -// insert an element at i -#define stb_arr_insert(a,i,v) (stb__arr_insertn((void **) &(a), sizeof(*a), i, 1), ((a)[i] = v)) - -// delete N elements from the middle starting at index 'i' -#define stb_arr_deleten(a,i,n) (stb__arr_deleten((void **) &(a), sizeof(*a), i, n)) - -// delete the i'th element -#define stb_arr_delete(a,i) stb_arr_deleten(a,i,1) - -// delete the i'th element, swapping down from the end -#define stb_arr_fastdelete(a,i) \ - (stb_swap(&a[i], &a[stb_arrhead(a)->len-1], sizeof(*a)), stb_arr_pop(a)) - - -// ARRAY STORAGE - -// get the array maximum storage; special case if NULL -#define stb_arrcurmax(a) (a ? stb_arrhead(a)->limit : 0) -#define stb_arrcurmax2(a) (a ? stb_arrhead2(a)->limit : 0) - -// set the maxlength of the array to n in anticipation of further growth -#define stb_arr_setsize(a,n) (stb_arr_check(a), stb__arr_setsize((void **) &(a),sizeof((a)[0]),n)) - -// make sure maxlength is large enough for at least N new allocations -#define stb_arr_atleast(a,n) (stb_arr_len(a)+(n) > stb_arrcurmax(a) \ - ? stb_arr_setsize((a), (n)) : 0) - -// make a copy of a given array (copies contents via 'memcpy'!) -#define stb_arr_copy(a) stb__arr_copy(a, sizeof((a)[0])) - -// compute the storage needed to store all the elements of the array -#define stb_arr_storage(a) (stb_arr_len(a) * sizeof((a)[0])) - -#define stb_arr_for(v,arr) for((v)=(arr); (v) < (arr)+stb_arr_len(arr); ++(v)) - -// IMPLEMENTATION - -STB_EXTERN void stb_arr_free_(void **p); -STB_EXTERN void *stb__arr_copy_(void *p, int elem_size); -STB_EXTERN void stb__arr_setsize_(void **p, int size, int limit STB__PARAMS); -STB_EXTERN void stb__arr_setlen_(void **p, int size, int newlen STB__PARAMS); -STB_EXTERN void stb__arr_addlen_(void **p, int size, int addlen STB__PARAMS); -STB_EXTERN void stb__arr_deleten_(void **p, int size, int loc, int n STB__PARAMS); -STB_EXTERN void stb__arr_insertn_(void **p, int size, int loc, int n STB__PARAMS); - -#define stb_arr_free(p) stb_arr_free_((void **) &(p)) -#define stb__arr_copy stb__arr_copy_ - -#ifndef STB_MALLOC_WRAPPER - #define stb__arr_setsize stb__arr_setsize_ - #define stb__arr_setlen stb__arr_setlen_ - #define stb__arr_addlen stb__arr_addlen_ - #define stb__arr_deleten stb__arr_deleten_ - #define stb__arr_insertn stb__arr_insertn_ -#else - #define stb__arr_addlen(p,s,n) stb__arr_addlen_(p,s,n,__FILE__,__LINE__) - #define stb__arr_setlen(p,s,n) stb__arr_setlen_(p,s,n,__FILE__,__LINE__) - #define stb__arr_setsize(p,s,n) stb__arr_setsize_(p,s,n,__FILE__,__LINE__) - #define stb__arr_deleten(p,s,i,n) stb__arr_deleten_(p,s,i,n,__FILE__,__LINE__) - #define stb__arr_insertn(p,s,i,n) stb__arr_insertn_(p,s,i,n,__FILE__,__LINE__) -#endif - -#ifdef STB_DEFINE -static void *stb__arr_context; - -void *stb_arr_malloc_parent(void *p) -{ - void *q = stb__arr_context; - stb__arr_context = p; - return q; -} - -void stb_arr_malloc(void **target, void *context) -{ - stb__arr *q = (stb__arr *) stb_malloc(context, sizeof(*q)); - q->len = q->limit = 0; - q->stb_malloc = 1; - q->signature = stb_arr_signature; - *target = (void *) (q+1); -} - -static void * stb__arr_malloc(int size) -{ - if (stb__arr_context) - return stb_malloc(stb__arr_context, size); - return malloc(size); -} - -void * stb__arr_copy_(void *p, int elem_size) -{ - stb__arr *q; - if (p == NULL) return p; - q = (stb__arr *) stb__arr_malloc(sizeof(*q) + elem_size * stb_arrhead2(p)->limit); - stb_arr_check2(p); - memcpy(q, stb_arrhead2(p), sizeof(*q) + elem_size * stb_arrhead2(p)->len); - q->stb_malloc = !!stb__arr_context; - return q+1; -} - -void stb_arr_free_(void **pp) -{ - void *p = *pp; - stb_arr_check2(p); - if (p) { - stb__arr *q = stb_arrhead2(p); - if (q->stb_malloc) - stb_free(q); - else - free(q); - } - *pp = NULL; -} - -static void stb__arrsize_(void **pp, int size, int limit, int len STB__PARAMS) -{ - void *p = *pp; - stb__arr *a; - stb_arr_check2(p); - if (p == NULL) { - if (len == 0 && size == 0) return; - a = (stb__arr *) stb__arr_malloc(sizeof(*a) + size*limit); - a->limit = limit; - a->len = len; - a->stb_malloc = !!stb__arr_context; - a->signature = stb_arr_signature; - } else { - a = stb_arrhead2(p); - a->len = len; - if (a->limit < limit) { - void *p; - if (a->limit >= 4 && limit < a->limit * 2) - limit = a->limit * 2; - if (a->stb_malloc) - p = stb_realloc(a, sizeof(*a) + limit*size); - else - #ifdef STB_MALLOC_WRAPPER - p = stb__realloc(a, sizeof(*a) + limit*size, file, line); - #else - p = realloc(a, sizeof(*a) + limit*size); - #endif - if (p) { - a = (stb__arr *) p; - a->limit = limit; - } else { - // throw an error! - } - } - } - a->len = stb_min(a->len, a->limit); - *pp = a+1; -} - -void stb__arr_setsize_(void **pp, int size, int limit STB__PARAMS) -{ - void *p = *pp; - stb_arr_check2(p); - stb__arrsize_(pp, size, limit, stb_arr_len2(p) STB__ARGS); -} - -void stb__arr_setlen_(void **pp, int size, int newlen STB__PARAMS) -{ - void *p = *pp; - stb_arr_check2(p); - if (stb_arrcurmax2(p) < newlen || p == NULL) { - stb__arrsize_(pp, size, newlen, newlen STB__ARGS); - } else { - stb_arrhead2(p)->len = newlen; - } -} - -void stb__arr_addlen_(void **p, int size, int addlen STB__PARAMS) -{ - stb__arr_setlen_(p, size, stb_arr_len2(*p) + addlen STB__ARGS); -} - -void stb__arr_insertn_(void **pp, int size, int i, int n STB__PARAMS) -{ - void *p = *pp; - if (n) { - int z; - - if (p == NULL) { - stb__arr_addlen_(pp, size, n STB__ARGS); - return; - } - - z = stb_arr_len2(p); - stb__arr_addlen_(&p, size, n STB__ARGS); - memmove((char *) p + (i+n)*size, (char *) p + i*size, size * (z-i)); - } - *pp = p; -} - -void stb__arr_deleten_(void **pp, int size, int i, int n STB__PARAMS) -{ - void *p = *pp; - if (n) { - memmove((char *) p + i*size, (char *) p + (i+n)*size, size * (stb_arr_len2(p)-(i+n))); - stb_arrhead2(p)->len -= n; - } - *pp = p; -} - -#endif - -////////////////////////////////////////////////////////////////////////////// -// -// Hashing -// -// typical use for this is to make a power-of-two hash table. -// -// let N = size of table (2^n) -// let H = stb_hash(str) -// let S = stb_rehash(H) | 1 -// -// then hash probe sequence P(i) for i=0..N-1 -// P(i) = (H + S*i) & (N-1) -// -// the idea is that H has 32 bits of hash information, but the -// table has only, say, 2^20 entries so only uses 20 of the bits. -// then by rehashing the original H we get 2^12 different probe -// sequences for a given initial probe location. (So it's optimal -// for 64K tables and its optimality decreases past that.) -// -// ok, so I've added something that generates _two separate_ -// 32-bit hashes simultaneously which should scale better to -// very large tables. - - -STB_EXTERN unsigned int stb_hash(char *str); -STB_EXTERN unsigned int stb_hashptr(void *p); -STB_EXTERN unsigned int stb_hashlen(char *str, int len); -STB_EXTERN unsigned int stb_rehash_improved(unsigned int v); -STB_EXTERN unsigned int stb_hash_fast(void *p, int len); -STB_EXTERN unsigned int stb_hash2(char *str, unsigned int *hash2_ptr); -STB_EXTERN unsigned int stb_hash_number(unsigned int hash); - -#define stb_rehash(x) ((x) + ((x) >> 6) + ((x) >> 19)) - -#ifdef STB_DEFINE -unsigned int stb_hash(char *str) -{ - unsigned int hash = 0; - while (*str) - hash = (hash << 7) + (hash >> 25) + *str++; - return hash + (hash >> 16); -} - -unsigned int stb_hashlen(char *str, int len) -{ - unsigned int hash = 0; - while (len-- > 0 && *str) - hash = (hash << 7) + (hash >> 25) + *str++; - return hash + (hash >> 16); -} - -unsigned int stb_hashptr(void *p) -{ - unsigned int x = (unsigned int) p; - - // typically lacking in low bits and high bits - x = stb_rehash(x); - x += x << 16; - - // pearson's shuffle - x ^= x << 3; - x += x >> 5; - x ^= x << 2; - x += x >> 15; - x ^= x << 10; - return stb_rehash(x); -} - -unsigned int stb_rehash_improved(unsigned int v) -{ - return stb_hashptr((void *)(size_t) v); -} - -unsigned int stb_hash2(char *str, unsigned int *hash2_ptr) -{ - unsigned int hash1 = 0x3141592c; - unsigned int hash2 = 0x77f044ed; - while (*str) { - hash1 = (hash1 << 7) + (hash1 >> 25) + *str; - hash2 = (hash2 << 11) + (hash2 >> 21) + *str; - ++str; - } - *hash2_ptr = hash2 + (hash1 >> 16); - return hash1 + (hash2 >> 16); -} - -// Paul Hsieh hash -#define stb__get16_slow(p) ((p)[0] + ((p)[1] << 8)) -#if defined(_MSC_VER) - #define stb__get16(p) (*((unsigned short *) (p))) -#else - #define stb__get16(p) stb__get16_slow(p) -#endif - -unsigned int stb_hash_fast(void *p, int len) -{ - unsigned char *q = (unsigned char *) p; - unsigned int hash = len; - - if (len <= 0 || q == NULL) return 0; - - /* Main loop */ - if (((int) q & 1) == 0) { - for (;len > 3; len -= 4) { - unsigned int val; - hash += stb__get16(q); - val = (stb__get16(q+2) << 11); - hash = (hash << 16) ^ hash ^ val; - q += 4; - hash += hash >> 11; - } - } else { - for (;len > 3; len -= 4) { - unsigned int val; - hash += stb__get16_slow(q); - val = (stb__get16_slow(q+2) << 11); - hash = (hash << 16) ^ hash ^ val; - q += 4; - hash += hash >> 11; - } - } - - /* Handle end cases */ - switch (len) { - case 3: hash += stb__get16_slow(q); - hash ^= hash << 16; - hash ^= q[2] << 18; - hash += hash >> 11; - break; - case 2: hash += stb__get16_slow(q); - hash ^= hash << 11; - hash += hash >> 17; - break; - case 1: hash += q[0]; - hash ^= hash << 10; - hash += hash >> 1; - break; - case 0: break; - } - - /* Force "avalanching" of final 127 bits */ - hash ^= hash << 3; - hash += hash >> 5; - hash ^= hash << 4; - hash += hash >> 17; - hash ^= hash << 25; - hash += hash >> 6; - - return hash; -} - -unsigned int stb_hash_number(unsigned int hash) -{ - hash ^= hash << 3; - hash += hash >> 5; - hash ^= hash << 4; - hash += hash >> 17; - hash ^= hash << 25; - hash += hash >> 6; - return hash; -} - -#endif - -////////////////////////////////////////////////////////////////////////////// -// -// Perfect hashing for ints/pointers -// -// This is mainly useful for making faster pointer-indexed tables -// that don't change frequently. E.g. for stb_ischar(). -// - -typedef struct -{ - stb_uint32 addend; - stb_uint multiplicand; - stb_uint b_mask; - stb_uint8 small_bmap[16]; - stb_uint16 *large_bmap; - - stb_uint table_mask; - stb_uint32 *table; -} stb_perfect; - -STB_EXTERN int stb_perfect_create(stb_perfect *,unsigned int*,int n); -STB_EXTERN void stb_perfect_destroy(stb_perfect *); -STB_EXTERN int stb_perfect_hash(stb_perfect *, unsigned int x); -extern int stb_perfect_hash_max_failures; - -#ifdef STB_DEFINE - -int stb_perfect_hash_max_failures; - -int stb_perfect_hash(stb_perfect *p, unsigned int x) -{ - stb_uint m = x * p->multiplicand; - stb_uint y = x >> 16; - stb_uint bv = (m >> 24) + y; - stb_uint av = (m + y) >> 12; - if (p->table == NULL) return -1; // uninitialized table fails - bv &= p->b_mask; - av &= p->table_mask; - if (p->large_bmap) - av ^= p->large_bmap[bv]; - else - av ^= p->small_bmap[bv]; - return p->table[av] == x ? av : -1; -} - -static void stb__perfect_prehash(stb_perfect *p, stb_uint x, stb_uint16 *a, stb_uint16 *b) -{ - stb_uint m = x * p->multiplicand; - stb_uint y = x >> 16; - stb_uint bv = (m >> 24) + y; - stb_uint av = (m + y) >> 12; - bv &= p->b_mask; - av &= p->table_mask; - *b = bv; - *a = av; -} - -static unsigned long stb__perfect_rand(void) -{ - static unsigned long stb__rand; - stb__rand = stb__rand * 2147001325 + 715136305; - return 0x31415926 ^ ((stb__rand >> 16) + (stb__rand << 16)); -} - -typedef struct { - unsigned short count; - unsigned short b; - unsigned short map; - unsigned short *entries; -} stb__slot; - -static int stb__slot_compare(const void *p, const void *q) -{ - stb__slot *a = (stb__slot *) p; - stb__slot *b = (stb__slot *) q; - return a->count > b->count ? -1 : a->count < b->count; // sort large to small -} - -int stb_perfect_create(stb_perfect *p, unsigned int *v, int n) -{ - unsigned int buffer1[64], buffer2[64], buffer3[64], buffer4[64], buffer5[32]; - unsigned short *as = (unsigned short *) stb_temp(buffer1, sizeof(*v)*n); - unsigned short *bs = (unsigned short *) stb_temp(buffer2, sizeof(*v)*n); - unsigned short *entries = (unsigned short *) stb_temp(buffer4, sizeof(*entries) * n); - int size = 1 << stb_log2_ceil(n), bsize=8; - int failure = 0,i,j,k; - - assert(n <= 32768); - p->large_bmap = NULL; - - for(;;) { - stb__slot *bcount = (stb__slot *) stb_temp(buffer3, sizeof(*bcount) * bsize); - unsigned short *bloc = (unsigned short *) stb_temp(buffer5, sizeof(*bloc) * bsize); - unsigned short *e; - int bad=0; - - p->addend = stb__perfect_rand(); - p->multiplicand = stb__perfect_rand() | 1; - p->table_mask = size-1; - p->b_mask = bsize-1; - p->table = (stb_uint32 *) malloc(size * sizeof(*p->table)); - - for (i=0; i < bsize; ++i) { - bcount[i].b = i; - bcount[i].count = 0; - bcount[i].map = 0; - } - for (i=0; i < n; ++i) { - stb__perfect_prehash(p, v[i], as+i, bs+i); - ++bcount[bs[i]].count; - } - qsort(bcount, bsize, sizeof(*bcount), stb__slot_compare); - e = entries; // now setup up their entries index - for (i=0; i < bsize; ++i) { - bcount[i].entries = e; - e += bcount[i].count; - bcount[i].count = 0; - bloc[bcount[i].b] = i; - } - // now fill them out - for (i=0; i < n; ++i) { - int b = bs[i]; - int w = bloc[b]; - bcount[w].entries[bcount[w].count++] = i; - } - stb_tempfree(buffer5,bloc); - // verify - for (i=0; i < bsize; ++i) - for (j=0; j < bcount[i].count; ++j) - assert(bs[bcount[i].entries[j]] == bcount[i].b); - memset(p->table, 0, size*sizeof(*p->table)); - - // check if any b has duplicate a - for (i=0; i < bsize; ++i) { - if (bcount[i].count > 1) { - for (j=0; j < bcount[i].count; ++j) { - if (p->table[as[bcount[i].entries[j]]]) - bad = 1; - p->table[as[bcount[i].entries[j]]] = 1; - } - for (j=0; j < bcount[i].count; ++j) { - p->table[as[bcount[i].entries[j]]] = 0; - } - if (bad) break; - } - } - - if (!bad) { - // go through the bs and populate the table, first fit - for (i=0; i < bsize; ++i) { - if (bcount[i].count) { - // go through the candidate table[b] values - for (j=0; j < size; ++j) { - // go through the a values and see if they fit - for (k=0; k < bcount[i].count; ++k) { - int a = as[bcount[i].entries[k]]; - if (p->table[(a^j)&p->table_mask]) { - break; // fails - } - } - // if succeeded, accept - if (k == bcount[i].count) { - bcount[i].map = j; - for (k=0; k < bcount[i].count; ++k) { - int a = as[bcount[i].entries[k]]; - p->table[(a^j)&p->table_mask] = 1; - } - break; - } - } - if (j == size) - break; // no match for i'th entry, so break out in failure - } - } - if (i == bsize) { - // success... fill out map - if (bsize <= 16 && size <= 256) { - p->large_bmap = NULL; - for (i=0; i < bsize; ++i) - p->small_bmap[bcount[i].b] = (stb_uint8) bcount[i].map; - } else { - p->large_bmap = (unsigned short *) malloc(sizeof(*p->large_bmap) * bsize); - for (i=0; i < bsize; ++i) - p->large_bmap[bcount[i].b] = bcount[i].map; - } - - // initialize table to v[0], so empty slots will fail - for (i=0; i < size; ++i) - p->table[i] = v[0]; - - for (i=0; i < n; ++i) - if (p->large_bmap) - p->table[as[i] ^ p->large_bmap[bs[i]]] = v[i]; - else - p->table[as[i] ^ p->small_bmap[bs[i]]] = v[i]; - - // and now validate that none of them collided - for (i=0; i < n; ++i) - assert(stb_perfect_hash(p, v[i]) >= 0); - - stb_tempfree(buffer3, bcount); - break; - } - } - free(p->table); - p->table = NULL; - stb_tempfree(buffer3, bcount); - - ++failure; - if (failure >= 4 && bsize < size) bsize *= 2; - if (failure >= 8 && (failure & 3) == 0 && size < 4*n) { - size *= 2; - bsize *= 2; - } - if (failure == 6) { - // make sure the input data is unique, so we don't infinite loop - unsigned int *data = (unsigned int *) stb_temp(buffer3, n * sizeof(*data)); - memcpy(data, v, sizeof(*data) * n); - qsort(data, n, sizeof(*data), stb_intcmp(0)); - for (i=1; i < n; ++i) { - if (data[i] == data[i-1]) - size = 0; // size is return value, so 0 it - } - stb_tempfree(buffer3, data); - if (!size) break; - } - } - - if (failure > stb_perfect_hash_max_failures) - stb_perfect_hash_max_failures = failure; - - stb_tempfree(buffer1, as); - stb_tempfree(buffer2, bs); - stb_tempfree(buffer4, entries); - - return size; -} - -void stb_perfect_destroy(stb_perfect *p) -{ - if (p->large_bmap) free(p->large_bmap); - if (p->table ) free(p->table); - p->large_bmap = NULL; - p->table = NULL; - p->b_mask = 0; - p->table_mask = 0; -} -#endif - -////////////////////////////////////////////////////////////////////////////// -// -// Perfect hash clients - -STB_EXTERN int stb_ischar(char s, char *set); - -#ifdef STB_DEFINE - -int stb_ischar(char c, char *set) -{ - static unsigned char bit[8] = { 1,2,4,8,16,32,64,128 }; - static stb_perfect p; - static unsigned char (*tables)[256]; - static char ** sets = NULL; - - int z = stb_perfect_hash(&p, (int) set); - if (z < 0) { - int i,k,n,j,f; - // special code that means free all existing data - if (set == NULL) { - stb_arr_free(sets); - free(tables); - tables = NULL; - stb_perfect_destroy(&p); - return 0; - } - stb_arr_push(sets, set); - stb_perfect_destroy(&p); - n = stb_perfect_create(&p, (unsigned int *) (char **) sets, stb_arr_len(sets)); - assert(n != 0); - k = (n+7) >> 3; - tables = (unsigned char (*)[256]) realloc(tables, sizeof(*tables) * k); - memset(tables, 0, sizeof(*tables) * k); - for (i=0; i < stb_arr_len(sets); ++i) { - k = stb_perfect_hash(&p, (int) sets[i]); - assert(k >= 0); - n = k >> 3; - f = bit[k&7]; - for (j=0; !j || sets[i][j]; ++j) { - tables[n][(unsigned char) sets[i][j]] |= f; - } - } - z = stb_perfect_hash(&p, (int) set); - } - return tables[z >> 3][(unsigned char) c] & bit[z & 7]; -} - -#endif - -////////////////////////////////////////////////////////////////////////////// -// -// Instantiated data structures -// -// This is an attempt to implement a templated data structure. -// -// Hash table: call stb_define_hash(TYPE,N,KEY,K1,K2,HASH,VALUE) -// TYPE -- will define a structure type containing the hash table -// N -- the name, will prefix functions named: -// N create -// N destroy -// N get -// N set, N add, N update, -// N remove -// KEY -- the type of the key. 'x == y' must be valid -// K1,K2 -- keys never used by the app, used as flags in the hashtable -// HASH -- a piece of code ending with 'return' that hashes key 'k' -// VALUE -- the type of the value. 'x = y' must be valid -// -// Note that stb_define_hash_base can be used to define more sophisticated -// hash tables, e.g. those that make copies of the key or use special -// comparisons (e.g. strcmp). - -#define STB_(prefix,name) stb__##prefix##name -#define STB__(prefix,name) prefix##name -#define STB__use(x) x -#define STB__skip(x) - -#define stb_declare_hash(PREFIX,TYPE,N,KEY,VALUE) \ - typedef struct stb__st_##TYPE TYPE;\ - PREFIX int STB__(N, init)(TYPE *h, int count);\ - PREFIX int STB__(N, memory_usage)(TYPE *h);\ - PREFIX TYPE * STB__(N, create)(void);\ - PREFIX TYPE * STB__(N, copy)(TYPE *h);\ - PREFIX void STB__(N, destroy)(TYPE *h);\ - PREFIX int STB__(N,get_flag)(TYPE *a, KEY k, VALUE *v);\ - PREFIX VALUE STB__(N,get)(TYPE *a, KEY k);\ - PREFIX int STB__(N, set)(TYPE *a, KEY k, VALUE v);\ - PREFIX int STB__(N, add)(TYPE *a, KEY k, VALUE v);\ - PREFIX int STB__(N, update)(TYPE*a,KEY k,VALUE v);\ - PREFIX int STB__(N, remove)(TYPE *a, KEY k, VALUE *v); - -#define STB_nocopy(x) (x) -#define STB_nodelete(x) 0 -#define STB_nofields -#define STB_nonullvalue(x) -#define STB_nullvalue(x) x -#define STB_safecompare(x) x -#define STB_nosafe(x) -#define STB_noprefix - -#ifdef __GNUC__ -#define STB__nogcc(x) -#else -#define STB__nogcc(x) x -#endif - -#define stb_define_hash_base(PREFIX,TYPE,FIELDS,N,NC,LOAD_FACTOR, \ - KEY,EMPTY,DEL,COPY,DISPOSE,SAFE, \ - VCOMPARE,CCOMPARE,HASH, \ - VALUE,HASVNULL,VNULL) \ - \ -typedef struct \ -{ \ - KEY k; \ - VALUE v; \ -} STB_(N,_hashpair); \ - \ -STB__nogcc( typedef struct stb__st_##TYPE TYPE; ) \ -struct stb__st_##TYPE { \ - FIELDS \ - STB_(N,_hashpair) *table; \ - unsigned int mask; \ - int count, limit; \ - int deleted; \ - \ - int delete_threshhold; \ - int grow_threshhold; \ - int shrink_threshhold; \ - unsigned char alloced, has_empty, has_del; \ - VALUE ev; VALUE dv; \ -}; \ - \ -static unsigned int STB_(N, hash)(KEY k) \ -{ \ - HASH \ -} \ - \ -PREFIX int STB__(N, init)(TYPE *h, int count) \ -{ \ - int i; \ - if (count < 4) count = 4; \ - h->limit = count; \ - h->count = 0; \ - h->mask = count-1; \ - h->deleted = 0; \ - h->grow_threshhold = (int) (count * LOAD_FACTOR); \ - h->has_empty = h->has_del = 0; \ - h->alloced = 0; \ - if (count <= 64) \ - h->shrink_threshhold = 0; \ - else \ - h->shrink_threshhold = (int) (count * (LOAD_FACTOR/2.25)); \ - h->delete_threshhold = (int) (count * (1-LOAD_FACTOR)/2); \ - h->table = (STB_(N,_hashpair)*) malloc(sizeof(h->table[0]) * count); \ - if (h->table == NULL) return 0; \ - /* ideally this gets turned into a memset32 automatically */ \ - for (i=0; i < count; ++i) \ - h->table[i].k = EMPTY; \ - return 1; \ -} \ - \ -PREFIX int STB__(N, memory_usage)(TYPE *h) \ -{ \ - return sizeof(*h) + h->limit * sizeof(h->table[0]); \ -} \ - \ -PREFIX TYPE * STB__(N, create)(void) \ -{ \ - TYPE *h = (TYPE *) malloc(sizeof(*h)); \ - if (h) { \ - if (STB__(N, init)(h, 16)) \ - h->alloced = 1; \ - else { free(h); h=NULL; } \ - } \ - return h; \ -} \ - \ -PREFIX void STB__(N, destroy)(TYPE *a) \ -{ \ - int i; \ - for (i=0; i < a->limit; ++i) \ - if (!CCOMPARE(a->table[i].k,EMPTY) && !CCOMPARE(a->table[i].k, DEL)) \ - DISPOSE(a->table[i].k); \ - free(a->table); \ - if (a->alloced) \ - free(a); \ -} \ - \ -static void STB_(N, rehash)(TYPE *a, int count); \ - \ -PREFIX int STB__(N,get_flag)(TYPE *a, KEY k, VALUE *v) \ -{ \ - unsigned int h = STB_(N, hash)(k); \ - unsigned int n = h & a->mask, s; \ - if (CCOMPARE(k,EMPTY)){ if (a->has_empty) *v = a->ev; return a->has_empty;}\ - if (CCOMPARE(k,DEL)) { if (a->has_del ) *v = a->dv; return a->has_del; }\ - if (CCOMPARE(a->table[n].k,EMPTY)) return 0; \ - SAFE(if (!CCOMPARE(a->table[n].k,DEL))) \ - if (VCOMPARE(a->table[n].k,k)) { *v = a->table[n].v; return 1; } \ - s = stb_rehash(h) | 1; \ - for(;;) { \ - n = (n + s) & a->mask; \ - if (CCOMPARE(a->table[n].k,EMPTY)) return 0; \ - SAFE(if (CCOMPARE(a->table[n].k,DEL)) continue;) \ - if (VCOMPARE(a->table[n].k,k)) \ - { *v = a->table[n].v; return 1; } \ - } \ -} \ - \ -HASVNULL( \ - PREFIX VALUE STB__(N,get)(TYPE *a, KEY k) \ - { \ - VALUE v; \ - if (STB__(N,get_flag)(a,k,&v)) return v; \ - else return VNULL; \ - } \ -) \ - \ -PREFIX int STB__(N,getkey)(TYPE *a, KEY k, KEY *kout) \ -{ \ - unsigned int h = STB_(N, hash)(k); \ - unsigned int n = h & a->mask, s; \ - if (CCOMPARE(k,EMPTY)||CCOMPARE(k,DEL)) return 0; \ - if (CCOMPARE(a->table[n].k,EMPTY)) return 0; \ - SAFE(if (!CCOMPARE(a->table[n].k,DEL))) \ - if (VCOMPARE(a->table[n].k,k)) { *kout = a->table[n].k; return 1; } \ - s = stb_rehash(h) | 1; \ - for(;;) { \ - n = (n + s) & a->mask; \ - if (CCOMPARE(a->table[n].k,EMPTY)) return 0; \ - SAFE(if (CCOMPARE(a->table[n].k,DEL)) continue;) \ - if (VCOMPARE(a->table[n].k,k)) \ - { *kout = a->table[n].k; return 1; } \ - } \ -} \ - \ -static int STB_(N,addset)(TYPE *a, KEY k, VALUE v, \ - int allow_new, int allow_old, int copy) \ -{ \ - unsigned int h = STB_(N, hash)(k); \ - unsigned int n = h & a->mask; \ - int b = -1; \ - if (CCOMPARE(k,EMPTY)) { \ - if (a->has_empty ? allow_old : allow_new) { \ - n=a->has_empty; a->ev = v; a->has_empty = 1; return !n; \ - } else return 0; \ - } \ - if (CCOMPARE(k,DEL)) { \ - if (a->has_del ? allow_old : allow_new) { \ - n=a->has_del; a->dv = v; a->has_del = 1; return !n; \ - } else return 0; \ - } \ - if (!CCOMPARE(a->table[n].k, EMPTY)) { \ - unsigned int s; \ - if (CCOMPARE(a->table[n].k, DEL)) \ - b = n; \ - else if (VCOMPARE(a->table[n].k,k)) { \ - if (allow_old) \ - a->table[n].v = v; \ - return !allow_new; \ - } \ - s = stb_rehash(h) | 1; \ - for(;;) { \ - n = (n + s) & a->mask; \ - if (CCOMPARE(a->table[n].k, EMPTY)) break; \ - if (CCOMPARE(a->table[n].k, DEL)) { \ - if (b < 0) b = n; \ - } else if (VCOMPARE(a->table[n].k,k)) { \ - if (allow_old) \ - a->table[n].v = v; \ - return !allow_new; \ - } \ - } \ - } \ - if (!allow_new) return 0; \ - if (b < 0) b = n; else --a->deleted; \ - a->table[b].k = copy ? COPY(k) : k; \ - a->table[b].v = v; \ - ++a->count; \ - if (a->count > a->grow_threshhold) \ - STB_(N,rehash)(a, a->limit*2); \ - return 1; \ -} \ - \ -PREFIX int STB__(N, set)(TYPE *a, KEY k, VALUE v){return STB_(N,addset)(a,k,v,1,1,1);}\ -PREFIX int STB__(N, add)(TYPE *a, KEY k, VALUE v){return STB_(N,addset)(a,k,v,1,0,1);}\ -PREFIX int STB__(N, update)(TYPE*a,KEY k,VALUE v){return STB_(N,addset)(a,k,v,0,1,1);}\ - \ -PREFIX int STB__(N, remove)(TYPE *a, KEY k, VALUE *v) \ -{ \ - unsigned int h = STB_(N, hash)(k); \ - unsigned int n = h & a->mask, s; \ - if (CCOMPARE(k,EMPTY)) { if (a->has_empty) { if(v)*v = a->ev; a->has_empty=0; return 1; } return 0; } \ - if (CCOMPARE(k,DEL)) { if (a->has_del ) { if(v)*v = a->dv; a->has_del =0; return 1; } return 0; } \ - if (CCOMPARE(a->table[n].k,EMPTY)) return 0; \ - if (SAFE(CCOMPARE(a->table[n].k,DEL) || ) !VCOMPARE(a->table[n].k,k)) { \ - s = stb_rehash(h) | 1; \ - for(;;) { \ - n = (n + s) & a->mask; \ - if (CCOMPARE(a->table[n].k,EMPTY)) return 0; \ - SAFE(if (CCOMPARE(a->table[n].k, DEL)) continue;) \ - if (VCOMPARE(a->table[n].k,k)) break; \ - } \ - } \ - DISPOSE(a->table[n].k); \ - a->table[n].k = DEL; \ - --a->count; \ - ++a->deleted; \ - if (v != NULL) \ - *v = a->table[n].v; \ - if (a->count < a->shrink_threshhold) \ - STB_(N, rehash)(a, a->limit >> 1); \ - else if (a->deleted > a->delete_threshhold) \ - STB_(N, rehash)(a, a->limit); \ - return 1; \ -} \ - \ -PREFIX TYPE * STB__(NC, copy)(TYPE *a) \ -{ \ - int i; \ - TYPE *h = (TYPE *) malloc(sizeof(*h)); \ - if (!h) return NULL; \ - if (!STB__(N, init)(h, a->limit)) { free(h); return NULL; } \ - h->count = a->count; \ - h->deleted = a->deleted; \ - h->alloced = 1; \ - h->ev = a->ev; h->dv = a->dv; \ - h->has_empty = a->has_empty; h->has_del = a->has_del; \ - memcpy(h->table, a->table, h->limit * sizeof(h->table[0])); \ - for (i=0; i < a->limit; ++i) \ - if (!CCOMPARE(h->table[i].k,EMPTY) && !CCOMPARE(h->table[i].k,DEL)) \ - h->table[i].k = COPY(h->table[i].k); \ - return h; \ -} \ - \ -static void STB_(N, rehash)(TYPE *a, int count) \ -{ \ - int i; \ - TYPE b; \ - STB__(N, init)(&b, count); \ - for (i=0; i < a->limit; ++i) \ - if (!CCOMPARE(a->table[i].k,EMPTY) && !CCOMPARE(a->table[i].k,DEL)) \ - STB_(N,addset)(&b, a->table[i].k, a->table[i].v,1,1,0); \ - free(a->table); \ - a->table = b.table; \ - a->mask = b.mask; \ - a->count = b.count; \ - a->limit = b.limit; \ - a->deleted = b.deleted; \ - a->delete_threshhold = b.delete_threshhold; \ - a->grow_threshhold = b.grow_threshhold; \ - a->shrink_threshhold = b.shrink_threshhold; \ -} - -#define STB_equal(a,b) ((a) == (b)) - -#define stb_define_hash(TYPE,N,KEY,EMPTY,DEL,HASH,VALUE) \ - stb_define_hash_base(STB_noprefix, TYPE,STB_nofields,N,NC,0.85f, \ - KEY,EMPTY,DEL,STB_nocopy,STB_nodelete,STB_nosafe, \ - STB_equal,STB_equal,HASH, \ - VALUE,STB_nonullvalue,0) - -#define stb_define_hash_vnull(TYPE,N,KEY,EMPTY,DEL,HASH,VALUE,VNULL) \ - stb_define_hash_base(STB_noprefix, TYPE,STB_nofields,N,NC,0.85f, \ - KEY,EMPTY,DEL,STB_nocopy,STB_nodelete,STB_nosafe, \ - STB_equal,STB_equal,HASH, \ - VALUE,STB_nullvalue,VNULL) - -////////////////////////////////////////////////////////////////////////////// -// -// stb_ptrmap -// -// An stb_ptrmap data structure is an O(1) hash table between pointers. One -// application is to let you store "extra" data associated with pointers, -// which is why it was originally called stb_extra. - -stb_declare_hash(STB_EXTERN, stb_ptrmap, stb_ptrmap_, void *, void *) -stb_declare_hash(STB_EXTERN, stb_idict, stb_idict_, stb_int32, stb_int32) - -STB_EXTERN void stb_ptrmap_delete(stb_ptrmap *e, void (*free_func)(void *)); -STB_EXTERN stb_ptrmap *stb_ptrmap_new(void); - -STB_EXTERN stb_idict * stb_idict_new_size(int size); -STB_EXTERN void stb_idict_remove_all(stb_idict *e); - -#ifdef STB_DEFINE - -#define STB_EMPTY ((void *) 2) -#define STB_EDEL ((void *) 6) - -stb_define_hash_base(STB_noprefix,stb_ptrmap, STB_nofields, stb_ptrmap_,stb_ptrmap_,0.85f, - void *,STB_EMPTY,STB_EDEL,STB_nocopy,STB_nodelete,STB_nosafe, - STB_equal,STB_equal,return stb_hashptr(k);, - void *,STB_nullvalue,NULL) - -stb_ptrmap *stb_ptrmap_new(void) -{ - return stb_ptrmap_create(); -} - -void stb_ptrmap_delete(stb_ptrmap *e, void (*free_func)(void *)) -{ - int i; - if (free_func) - for (i=0; i < e->limit; ++i) - if (e->table[i].k != STB_EMPTY && e->table[i].k != STB_EDEL) { - if (free_func == free) - free(e->table[i].v); // allow STB_MALLOC_WRAPPER to operate - else - free_func(e->table[i].v); - } - stb_ptrmap_destroy(e); -} - -// extra fields needed for stua_dict -#define STB_IEMPTY ((int) 1) -#define STB_IDEL ((int) 3) -stb_define_hash_base(STB_noprefix, stb_idict, short type; short gc; STB_nofields, stb_idict_,stb_idict_,0.85f, - stb_int32,STB_IEMPTY,STB_IDEL,STB_nocopy,STB_nodelete,STB_nosafe, - STB_equal,STB_equal, - return stb_rehash_improved(k);,stb_int32,STB_nonullvalue,0) - -stb_idict * stb_idict_new_size(int size) -{ - stb_idict *e = (stb_idict *) malloc(sizeof(*e)); - if (e) { - if (!stb_is_pow2(size)) - size = 1 << stb_log2_ceil(size); - stb_idict_init(e, size); - e->alloced = 1; - } - return e; -} - -void stb_idict_remove_all(stb_idict *e) -{ - int n; - for (n=0; n < e->limit; ++n) - e->table[n].k = STB_IEMPTY; - e->has_empty = e->has_del = 0; -} -#endif - -////////////////////////////////////////////////////////////////////////////// -// -// stb_sparse_ptr_matrix -// -// An stb_ptrmap data structure is an O(1) hash table storing an arbitrary -// block of data for a given pair of pointers. -// -// If create=0, returns - -typedef struct stb__st_stb_spmatrix stb_spmatrix; - -STB_EXTERN stb_spmatrix * stb_sparse_ptr_matrix_new(int val_size); -STB_EXTERN void stb_sparse_ptr_matrix_free(stb_spmatrix *z); -STB_EXTERN void * stb_sparse_ptr_matrix_get(stb_spmatrix *z, void *a, void *b, int create); - -#ifdef STB_DEFINE -typedef struct -{ - void *a; - void *b; -} stb__ptrpair; - -static stb__ptrpair stb__ptrpair_empty = { (void *) 1, (void *) 1 }; -static stb__ptrpair stb__ptrpair_del = { (void *) 2, (void *) 2 }; - -#define STB__equal_ptrpair(x,y) ((x).a == (y).a && (x).b == (y).b) - -stb_define_hash_base(static, stb_spmatrix, int val_size; void *arena;, stb__spmatrix_,stb__spmatrix_, 0.85, - stb__ptrpair, stb__ptrpair_empty, stb__ptrpair_del, - STB_nocopy, STB_nodelete, STB_nosafe, - STB__equal_ptrpair, STB__equal_ptrpair, return stb_rehash(stb_hashptr(k.a))+stb_hashptr(k.b);, - void *, STB_nullvalue, 0) - -stb_spmatrix *stb_sparse_ptr_matrix_new(int val_size) -{ - stb_spmatrix *m = stb__spmatrix_create(); - if (m) m->val_size = val_size; - if (m) m->arena = stb_malloc_global(1); - return m; -} - -void stb_sparse_ptr_matrix_free(stb_spmatrix *z) -{ - if (z->arena) stb_free(z->arena); - stb__spmatrix_destroy(z); -} - -void *stb_sparse_ptr_matrix_get(stb_spmatrix *z, void *a, void *b, int create) -{ - stb__ptrpair t = { a,b }; - void *data = stb__spmatrix_get(z, t); - if (!data && create) { - data = stb_malloc_raw(z->arena, z->val_size); - if (!data) return NULL; - memset(data, 0, z->val_size); - stb__spmatrix_add(z, t, data); - } - return data; -} -#endif - - - -////////////////////////////////////////////////////////////////////////////// -// -// SDICT: Hash Table for Strings (symbol table) -// -// if "use_arena=1", then strings will be copied -// into blocks and never freed until the sdict is freed; -// otherwise they're malloc()ed and free()d on the fly. -// (specify use_arena=1 if you never stb_sdict_remove) - -stb_declare_hash(STB_EXTERN, stb_sdict, stb_sdict_, char *, void *) - -STB_EXTERN stb_sdict * stb_sdict_new(int use_arena); -STB_EXTERN stb_sdict * stb_sdict_copy(stb_sdict*); -STB_EXTERN void stb_sdict_delete(stb_sdict *); -STB_EXTERN void * stb_sdict_change(stb_sdict *, char *str, void *p); -STB_EXTERN int stb_sdict_count(stb_sdict *d); - -#define stb_sdict_for(d,i,q,z) \ - for(i=0; i < (d)->limit ? q=(d)->table[i].k,z=(d)->table[i].v,1 : 0; ++i) \ - if (q==NULL||q==(void *) 1);else // reversed makes macro friendly - -#ifdef STB_DEFINE - -#define STB_DEL ((void *) 1) -#define STB_SDEL ((char *) 1) - -#define stb_sdict__copy(x) \ - strcpy(a->arena ? stb_malloc_string(a->arena, strlen(x)+1) \ - : (char *) malloc(strlen(x)+1), x) - -#define stb_sdict__dispose(x) if (!a->arena) free(x) - -stb_define_hash_base(STB_noprefix, stb_sdict, void*arena;, stb_sdict_,stb_sdictinternal_, 0.85f, - char *, NULL, STB_SDEL, stb_sdict__copy, stb_sdict__dispose, - STB_safecompare, !strcmp, STB_equal, return stb_hash(k);, - void *, STB_nullvalue, NULL) - -int stb_sdict_count(stb_sdict *a) -{ - return a->count; -} - -stb_sdict * stb_sdict_new(int use_arena) -{ - stb_sdict *d = stb_sdict_create(); - if (d == NULL) return NULL; - d->arena = use_arena ? stb_malloc_global(1) : NULL; - return d; -} - -stb_sdict* stb_sdict_copy(stb_sdict *old) -{ - stb_sdict *n; - void *old_arena = old->arena; - void *new_arena = old_arena ? stb_malloc_global(1) : NULL; - old->arena = new_arena; - n = stb_sdictinternal_copy(old); - old->arena = old_arena; - if (n) - n->arena = new_arena; - else if (new_arena) - stb_free(new_arena); - return n; -} - - -void stb_sdict_delete(stb_sdict *d) -{ - if (d->arena) - stb_free(d->arena); - stb_sdict_destroy(d); -} - -void * stb_sdict_change(stb_sdict *d, char *str, void *p) -{ - void *q = stb_sdict_get(d, str); - stb_sdict_set(d, str, p); - return q; -} -#endif - -////////////////////////////////////////////////////////////////////////////// -// -// Instantiated data structures -// -// This is an attempt to implement a templated data structure. -// What you do is define a struct foo, and then include several -// pointer fields to struct foo in your struct. Then you call -// the instantiator, which creates the functions that implement -// the data structure. This requires massive undebuggable #defines, -// so we limit the cases where we do this. -// -// AA tree is an encoding of a 2-3 tree whereas RB trees encode a 2-3-4 tree; -// much simpler code due to fewer cases. - -#define stb__bst_parent(x) x -#define stb__bst_noparent(x) - -#define stb_bst_fields(N) \ - *STB_(N,left), *STB_(N,right); \ - unsigned char STB_(N,level) - -#define stb_bst_fields_parent(N) \ - *STB_(N,left), *STB_(N,right), *STB_(N,parent); \ - unsigned char STB_(N,level) - -#define STB__level(N,x) ((x) ? (x)->STB_(N,level) : 0) - -#define stb_bst_base(TYPE, N, TREE, M, compare, PAR) \ - \ -static int STB_(N,_compare)(TYPE *p, TYPE *q) \ -{ \ - compare \ -} \ - \ -static void STB_(N,setleft)(TYPE *q, TYPE *v) \ -{ \ - q->STB_(N,left) = v; \ - PAR(if (v) v->STB_(N,parent) = q;) \ -} \ - \ -static void STB_(N,setright)(TYPE *q, TYPE *v) \ -{ \ - q->STB_(N,right) = v; \ - PAR(if (v) v->STB_(N,parent) = q;) \ -} \ - \ -static TYPE *STB_(N,skew)(TYPE *q) \ -{ \ - if (q == NULL) return q; \ - if (q->STB_(N,left) \ - && q->STB_(N,left)->STB_(N,level) == q->STB_(N,level)) { \ - TYPE *p = q->STB_(N,left); \ - STB_(N,setleft)(q, p->STB_(N,right)); \ - STB_(N,setright)(p, q); \ - return p; \ - } \ - return q; \ -} \ - \ -static TYPE *STB_(N,split)(TYPE *p) \ -{ \ - TYPE *q = p->STB_(N,right); \ - if (q && q->STB_(N,right) \ - && q->STB_(N,right)->STB_(N,level) == p->STB_(N,level)) { \ - STB_(N,setright)(p, q->STB_(N,left)); \ - STB_(N,setleft)(q,p); \ - ++q->STB_(N,level); \ - return q; \ - } \ - return p; \ -} \ - \ -TYPE *STB__(N,insert)(TYPE *tree, TYPE *item) \ -{ \ - int c; \ - if (tree == NULL) { \ - item->STB_(N,left) = NULL; \ - item->STB_(N,right) = NULL; \ - item->STB_(N,level) = 1; \ - PAR(item->STB_(N,parent) = NULL;) \ - return item; \ - } \ - c = STB_(N,_compare)(item,tree); \ - if (c == 0) { \ - if (item != tree) { \ - STB_(N,setleft)(item, tree->STB_(N,left)); \ - STB_(N,setright)(item, tree->STB_(N,right)); \ - item->STB_(N,level) = tree->STB_(N,level); \ - PAR(item->STB_(N,parent) = NULL;) \ - } \ - return item; \ - } \ - if (c < 0) \ - STB_(N,setleft )(tree, STB__(N,insert)(tree->STB_(N,left), item)); \ - else \ - STB_(N,setright)(tree, STB__(N,insert)(tree->STB_(N,right), item)); \ - tree = STB_(N,skew)(tree); \ - tree = STB_(N,split)(tree); \ - PAR(tree->STB_(N,parent) = NULL;) \ - return tree; \ -} \ - \ -TYPE *STB__(N,remove)(TYPE *tree, TYPE *item) \ -{ \ - static TYPE *delnode, *leaf, *restore; \ - if (tree == NULL) return NULL; \ - leaf = tree; \ - if (STB_(N,_compare)(item, tree) < 0) { \ - STB_(N,setleft)(tree, STB__(N,remove)(tree->STB_(N,left), item)); \ - } else { \ - TYPE *r; \ - delnode = tree; \ - r = STB__(N,remove)(tree->STB_(N,right), item); \ - /* maybe move 'leaf' up to this location */ \ - if (restore == tree) { tree = leaf; leaf = restore = NULL; } \ - STB_(N,setright)(tree,r); \ - assert(tree->STB_(N,right) != tree); \ - } \ - if (tree == leaf) { \ - if (delnode == item) { \ - tree = tree->STB_(N,right); \ - assert(leaf->STB_(N,left) == NULL); \ - /* move leaf (the right sibling) up to delnode */ \ - STB_(N,setleft )(leaf, item->STB_(N,left )); \ - STB_(N,setright)(leaf, item->STB_(N,right)); \ - leaf->STB_(N,level) = item->STB_(N,level); \ - if (leaf != item) \ - restore = delnode; \ - } \ - delnode = NULL; \ - } else { \ - if (STB__level(N,tree->STB_(N,left) ) < tree->STB_(N,level)-1 || \ - STB__level(N,tree->STB_(N,right)) < tree->STB_(N,level)-1) { \ - --tree->STB_(N,level); \ - if (STB__level(N,tree->STB_(N,right)) > tree->STB_(N,level)) \ - tree->STB_(N,right)->STB_(N,level) = tree->STB_(N,level); \ - tree = STB_(N,skew)(tree); \ - STB_(N,setright)(tree, STB_(N,skew)(tree->STB_(N,right))); \ - if (tree->STB_(N,right)) \ - STB_(N,setright)(tree->STB_(N,right), \ - STB_(N,skew)(tree->STB_(N,right)->STB_(N,right))); \ - tree = STB_(N,split)(tree); \ - if (tree->STB_(N,right)) \ - STB_(N,setright)(tree, STB_(N,split)(tree->STB_(N,right))); \ - } \ - } \ - PAR(if (tree) tree->STB_(N,parent) = NULL;) \ - return tree; \ -} \ - \ -TYPE *STB__(N,last)(TYPE *tree) \ -{ \ - if (tree) \ - while (tree->STB_(N,right)) tree = tree->STB_(N,right); \ - return tree; \ -} \ - \ -TYPE *STB__(N,first)(TYPE *tree) \ -{ \ - if (tree) \ - while (tree->STB_(N,left)) tree = tree->STB_(N,left); \ - return tree; \ -} \ - \ -TYPE *STB__(N,next)(TYPE *tree, TYPE *item) \ -{ \ - TYPE *next = NULL; \ - if (item->STB_(N,right)) \ - return STB__(N,first)(item->STB_(N,right)); \ - PAR( \ - while(item->STB_(N,parent)) { \ - TYPE *up = item->STB_(N,parent); \ - if (up->STB_(N,left) == item) return up; \ - item = up; \ - } \ - return NULL; \ - ) \ - while (tree != item) { \ - if (STB_(N,_compare)(item, tree) < 0) { \ - next = tree; \ - tree = tree->STB_(N,left); \ - } else { \ - tree = tree->STB_(N,right); \ - } \ - } \ - return next; \ -} \ - \ -TYPE *STB__(N,prev)(TYPE *tree, TYPE *item) \ -{ \ - TYPE *next = NULL; \ - if (item->STB_(N,left)) \ - return STB__(N,last)(item->STB_(N,left)); \ - PAR( \ - while(item->STB_(N,parent)) { \ - TYPE *up = item->STB_(N,parent); \ - if (up->STB_(N,right) == item) return up; \ - item = up; \ - } \ - return NULL; \ - ) \ - while (tree != item) { \ - if (STB_(N,_compare)(item, tree) < 0) { \ - tree = tree->STB_(N,left); \ - } else { \ - next = tree; \ - tree = tree->STB_(N,right); \ - } \ - } \ - return next; \ -} \ - \ -STB__DEBUG( \ - void STB__(N,_validate)(TYPE *tree, int root) \ - { \ - if (tree == NULL) return; \ - PAR(if(root) assert(tree->STB_(N,parent) == NULL);) \ - assert(STB__level(N,tree->STB_(N,left) ) == tree->STB_(N,level)-1); \ - assert(STB__level(N,tree->STB_(N,right)) <= tree->STB_(N,level)); \ - assert(STB__level(N,tree->STB_(N,right)) >= tree->STB_(N,level)-1); \ - if (tree->STB_(N,right)) { \ - assert(STB__level(N,tree->STB_(N,right)->STB_(N,right)) \ - != tree->STB_(N,level)); \ - PAR(assert(tree->STB_(N,right)->STB_(N,parent) == tree);) \ - } \ - PAR(if(tree->STB_(N,left)) assert(tree->STB_(N,left)->STB_(N,parent) == tree);) \ - STB__(N,_validate)(tree->STB_(N,left) ,0); \ - STB__(N,_validate)(tree->STB_(N,right),0); \ - } \ -) \ - \ -typedef struct \ -{ \ - TYPE *root; \ -} TREE; \ - \ -void STB__(M,Insert)(TREE *tree, TYPE *item) \ -{ tree->root = STB__(N,insert)(tree->root, item); } \ -void STB__(M,Remove)(TREE *tree, TYPE *item) \ -{ tree->root = STB__(N,remove)(tree->root, item); } \ -TYPE *STB__(M,Next)(TREE *tree, TYPE *item) \ -{ return STB__(N,next)(tree->root, item); } \ -TYPE *STB__(M,Prev)(TREE *tree, TYPE *item) \ -{ return STB__(N,prev)(tree->root, item); } \ -TYPE *STB__(M,First)(TREE *tree) { return STB__(N,first)(tree->root); } \ -TYPE *STB__(M,Last) (TREE *tree) { return STB__(N,last) (tree->root); } \ -void STB__(M,Init)(TREE *tree) { tree->root = NULL; } - - -#define stb_bst_find(N,tree,fcompare) \ -{ \ - int c; \ - while (tree != NULL) { \ - fcompare \ - if (c == 0) return tree; \ - if (c < 0) tree = tree->STB_(N,left); \ - else tree = tree->STB_(N,right); \ - } \ - return NULL; \ -} - -#define stb_bst_raw(TYPE,N,TREE,M,vfield,VTYPE,compare,PAR) \ - stb_bst_base(TYPE,N,TREE,M, \ - VTYPE a = p->vfield; VTYPE b = q->vfield; return (compare);, PAR ) \ - \ -TYPE *STB__(N,find)(TYPE *tree, VTYPE a) \ - stb_bst_find(N,tree,VTYPE b = tree->vfield; c = (compare);) \ -TYPE *STB__(M,Find)(TREE *tree, VTYPE a) \ -{ return STB__(N,find)(tree->root, a); } - -#define stb_bst(TYPE,N,TREE,M,vfield,VTYPE,compare) \ - stb_bst_raw(TYPE,N,TREE,M,vfield,VTYPE,compare,stb__bst_noparent) -#define stb_bst_parent(TYPE,N,TREE,M,vfield,VTYPE,compare) \ - stb_bst_raw(TYPE,N,TREE,M,vfield,VTYPE,compare,stb__bst_parent) - - - -////////////////////////////////////////////////////////////////////////////// -// -// Pointer Nulling -// -// This lets you automatically NULL dangling pointers to "registered" -// objects. Note that you have to make sure you call the appropriate -// functions when you free or realloc blocks of memory that contain -// pointers or pointer targets. stb.h can automatically do this for -// stb_arr, or for all frees/reallocs if it's wrapping them. -// - -#ifdef STB_NPTR - -STB_EXTERN void stb_nptr_set(void *address_of_pointer, void *value_to_write); -STB_EXTERN void stb_nptr_didset(void *address_of_pointer); - -STB_EXTERN void stb_nptr_didfree(void *address_being_freed, int len); -STB_EXTERN void stb_nptr_free(void *address_being_freed, int len); - -STB_EXTERN void stb_nptr_didrealloc(void *new_address, void *old_address, int len); -STB_EXTERN void stb_nptr_recache(void); // recache all known pointers - // do this after pointer sets outside your control, slow - -#ifdef STB_DEFINE -// for fast updating on free/realloc, we need to be able to find -// all the objects (pointers and targets) within a given block; -// this precludes hashing - -// we use a three-level hierarchy of memory to minimize storage: -// level 1: 65536 pointers to stb__memory_node (always uses 256 KB) -// level 2: each stb__memory_node represents a 64K block of memory -// with 256 stb__memory_leafs (worst case 64MB) -// level 3: each stb__memory_leaf represents 256 bytes of memory -// using a list of target locations and a list of pointers -// (which are hopefully fairly short normally!) - -// this approach won't work in 64-bit, which has a much larger address -// space. need to redesign - -#define STB__NPTR_ROOT_LOG2 16 -#define STB__NPTR_ROOT_NUM (1 << STB__NPTR_ROOT_LOG2) -#define STB__NPTR_ROOT_SHIFT (32 - STB__NPTR_ROOT_LOG2) - -#define STB__NPTR_NODE_LOG2 5 -#define STB__NPTR_NODE_NUM (1 << STB__NPTR_NODE_LOG2) -#define STB__NPTR_NODE_MASK (STB__NPTR_NODE_NUM-1) -#define STB__NPTR_NODE_SHIFT (STB__NPTR_ROOT_SHIFT - STB__NPTR_NODE_LOG2) -#define STB__NPTR_NODE_OFFSET(x) (((x) >> STB__NPTR_NODE_SHIFT) & STB__NPTR_NODE_MASK) - -typedef struct stb__st_nptr -{ - void *ptr; // address of actual pointer - struct stb__st_nptr *next; // next pointer with same target - struct stb__st_nptr **prev; // prev pointer with same target, address of 'next' field (or first) - struct stb__st_nptr *next_in_block; -} stb__nptr; - -typedef struct stb__st_nptr_target -{ - void *ptr; // address of target - stb__nptr *first; // address of first nptr pointing to this - struct stb__st_nptr_target *next_in_block; -} stb__nptr_target; - -typedef struct -{ - stb__nptr *pointers; - stb__nptr_target *targets; -} stb__memory_leaf; - -typedef struct -{ - stb__memory_leaf *children[STB__NPTR_NODE_NUM]; -} stb__memory_node; - -stb__memory_node *stb__memtab_root[STB__NPTR_ROOT_NUM]; - -static stb__memory_leaf *stb__nptr_find_leaf(void *mem) -{ - stb_uint32 address = (stb_uint32) mem; - stb__memory_node *z = stb__memtab_root[address >> STB__NPTR_ROOT_SHIFT]; - if (z) - return z->children[STB__NPTR_NODE_OFFSET(address)]; - else - return NULL; -} - -static void * stb__nptr_alloc(int size) -{ - return stb__realloc_raw(0,size); -} - -static void stb__nptr_free(void *p) -{ - stb__realloc_raw(p,0); -} - -static stb__memory_leaf *stb__nptr_make_leaf(void *mem) -{ - stb_uint32 address = (stb_uint32) mem; - stb__memory_node *z = stb__memtab_root[address >> STB__NPTR_ROOT_SHIFT]; - stb__memory_leaf *f; - if (!z) { - int i; - z = (stb__memory_node *) stb__nptr_alloc(sizeof(*stb__memtab_root[0])); - stb__memtab_root[address >> STB__NPTR_ROOT_SHIFT] = z; - for (i=0; i < 256; ++i) - z->children[i] = 0; - } - f = (stb__memory_leaf *) stb__nptr_alloc(sizeof(*f)); - z->children[STB__NPTR_NODE_OFFSET(address)] = f; - f->pointers = NULL; - f->targets = NULL; - return f; -} - -static stb__nptr_target *stb__nptr_find_target(void *target, int force) -{ - stb__memory_leaf *p = stb__nptr_find_leaf(target); - if (p) { - stb__nptr_target *t = p->targets; - while (t) { - if (t->ptr == target) - return t; - t = t->next_in_block; - } - } - if (force) { - stb__nptr_target *t = (stb__nptr_target*) stb__nptr_alloc(sizeof(*t)); - if (!p) p = stb__nptr_make_leaf(target); - t->ptr = target; - t->first = NULL; - t->next_in_block = p->targets; - p->targets = t; - return t; - } else - return NULL; -} - -static stb__nptr *stb__nptr_find_pointer(void *ptr, int force) -{ - stb__memory_leaf *p = stb__nptr_find_leaf(ptr); - if (p) { - stb__nptr *t = p->pointers; - while (t) { - if (t->ptr == ptr) - return t; - t = t->next_in_block; - } - } - if (force) { - stb__nptr *t = (stb__nptr *) stb__nptr_alloc(sizeof(*t)); - if (!p) p = stb__nptr_make_leaf(ptr); - t->ptr = ptr; - t->next = NULL; - t->prev = NULL; - t->next_in_block = p->pointers; - p->pointers = t; - return t; - } else - return NULL; -} - -void stb_nptr_set(void *address_of_pointer, void *value_to_write) -{ - if (*(void **)address_of_pointer != value_to_write) { - *(void **) address_of_pointer = value_to_write; - stb_nptr_didset(address_of_pointer); - } -} - -void stb_nptr_didset(void *address_of_pointer) -{ - // first unlink from old chain - void *new_address; - stb__nptr *p = stb__nptr_find_pointer(address_of_pointer, 1); // force building if doesn't exist - if (p->prev) { // if p->prev is NULL, we just built it, or it was NULL - *(p->prev) = p->next; - if (p->next) p->next->prev = p->prev; - } - // now add to new chain - new_address = *(void **)address_of_pointer; - if (new_address != NULL) { - stb__nptr_target *t = stb__nptr_find_target(new_address, 1); - p->next = t->first; - if (p->next) p->next->prev = &p->next; - p->prev = &t->first; - t->first = p; - } else { - p->prev = NULL; - p->next = NULL; - } -} - -void stb__nptr_block(void *address, int len, void (*function)(stb__memory_leaf *f, int datum, void *start, void *end), int datum) -{ - void *end_address = (void *) ((char *) address + len - 1); - stb__memory_node *n; - stb_uint32 start = (stb_uint32) address; - stb_uint32 end = start + len - 1; - - int b0 = start >> STB__NPTR_ROOT_SHIFT; - int b1 = end >> STB__NPTR_ROOT_SHIFT; - int b=b0,i,e0,e1; - - e0 = STB__NPTR_NODE_OFFSET(start); - - if (datum <= 0) { - // first block - n = stb__memtab_root[b0]; - if (n) { - if (b0 != b1) - e1 = STB__NPTR_NODE_NUM-1; - else - e1 = STB__NPTR_NODE_OFFSET(end); - for (i=e0; i <= e1; ++i) - if (n->children[i]) - function(n->children[i], datum, address, end_address); - } - if (b1 > b0) { - // blocks other than the first and last block - for (b=b0+1; b < b1; ++b) { - n = stb__memtab_root[b]; - if (n) - for (i=0; i <= STB__NPTR_NODE_NUM-1; ++i) - if (n->children[i]) - function(n->children[i], datum, address, end_address); - } - // last block - n = stb__memtab_root[b1]; - if (n) { - e1 = STB__NPTR_NODE_OFFSET(end); - for (i=0; i <= e1; ++i) - if (n->children[i]) - function(n->children[i], datum, address, end_address); - } - } - } else { - if (b1 > b0) { - // last block - n = stb__memtab_root[b1]; - if (n) { - e1 = STB__NPTR_NODE_OFFSET(end); - for (i=e1; i >= 0; --i) - if (n->children[i]) - function(n->children[i], datum, address, end_address); - } - // blocks other than the first and last block - for (b=b1-1; b > b0; --b) { - n = stb__memtab_root[b]; - if (n) - for (i=STB__NPTR_NODE_NUM-1; i >= 0; --i) - if (n->children[i]) - function(n->children[i], datum, address, end_address); - } - } - // first block - n = stb__memtab_root[b0]; - if (n) { - if (b0 != b1) - e1 = STB__NPTR_NODE_NUM-1; - else - e1 = STB__NPTR_NODE_OFFSET(end); - for (i=e1; i >= e0; --i) - if (n->children[i]) - function(n->children[i], datum, address, end_address); - } - } -} - -static void stb__nptr_delete_pointers(stb__memory_leaf *f, int offset, void *start, void *end) -{ - stb__nptr **p = &f->pointers; - while (*p) { - stb__nptr *n = *p; - if (n->ptr >= start && n->ptr <= end) { - // unlink - if (n->prev) { - *(n->prev) = n->next; - if (n->next) n->next->prev = n->prev; - } - *p = n->next_in_block; - stb__nptr_free(n); - } else - p = &(n->next_in_block); - } -} - -static void stb__nptr_delete_targets(stb__memory_leaf *f, int offset, void *start, void *end) -{ - stb__nptr_target **p = &f->targets; - while (*p) { - stb__nptr_target *n = *p; - if (n->ptr >= start && n->ptr <= end) { - // null pointers - stb__nptr *z = n->first; - while (z) { - stb__nptr *y = z->next; - z->prev = NULL; - z->next = NULL; - *(void **) z->ptr = NULL; - z = y; - } - // unlink this target - *p = n->next_in_block; - stb__nptr_free(n); - } else - p = &(n->next_in_block); - } -} - -void stb_nptr_didfree(void *address_being_freed, int len) -{ - // step one: delete all pointers in this block - stb__nptr_block(address_being_freed, len, stb__nptr_delete_pointers, 0); - // step two: NULL all pointers to this block; do this second to avoid NULLing deleted pointers - stb__nptr_block(address_being_freed, len, stb__nptr_delete_targets, 0); -} - -void stb_nptr_free(void *address_being_freed, int len) -{ - free(address_being_freed); - stb_nptr_didfree(address_being_freed, len); -} - -static void stb__nptr_move_targets(stb__memory_leaf *f, int offset, void *start, void *end) -{ - stb__nptr_target **t = &f->targets; - while (*t) { - stb__nptr_target *n = *t; - if (n->ptr >= start && n->ptr <= end) { - stb__nptr *z; - stb__memory_leaf *f; - // unlink n - *t = n->next_in_block; - // update n to new address - n->ptr = (void *) ((char *) n->ptr + offset); - f = stb__nptr_find_leaf(n->ptr); - if (!f) f = stb__nptr_make_leaf(n->ptr); - n->next_in_block = f->targets; - f->targets = n; - // now go through all pointers and make them point here - z = n->first; - while (z) { - *(void**) z->ptr = n->ptr; - z = z->next; - } - } else - t = &(n->next_in_block); - } -} - -static void stb__nptr_move_pointers(stb__memory_leaf *f, int offset, void *start, void *end) -{ - stb__nptr **p = &f->pointers; - while (*p) { - stb__nptr *n = *p; - if (n->ptr >= start && n->ptr <= end) { - // unlink - *p = n->next_in_block; - n->ptr = (void *) ((int) n->ptr + offset); - // move to new block - f = stb__nptr_find_leaf(n->ptr); - if (!f) f = stb__nptr_make_leaf(n->ptr); - n->next_in_block = f->pointers; - f->pointers = n; - } else - p = &(n->next_in_block); - } -} - -void stb_nptr_realloc(void *new_address, void *old_address, int len) -{ - if (new_address == old_address) return; - - // have to move the pointers first, because moving the targets - // requires writing to the pointers-to-the-targets, and if some of those moved too, - // we need to make sure we don't write to the old memory - - // step one: move all pointers within the block - stb__nptr_block(old_address, len, stb__nptr_move_pointers, (char *) new_address - (char *) old_address); - // step two: move all targets within the block - stb__nptr_block(old_address, len, stb__nptr_move_targets, (char *) new_address - (char *) old_address); -} - -void stb_nptr_move(void *new_address, void *old_address) -{ - stb_nptr_realloc(new_address, old_address, 1); -} - -void stb_nptr_recache(void) -{ - int i,j; - for (i=0; i < STB__NPTR_ROOT_NUM; ++i) - if (stb__memtab_root[i]) - for (j=0; j < STB__NPTR_NODE_NUM; ++j) - if (stb__memtab_root[i]->children[j]) { - stb__nptr *p = stb__memtab_root[i]->children[j]->pointers; - while (p) { - stb_nptr_didset(p->ptr); - p = p->next_in_block; - } - } -} - -#endif // STB_DEFINE -#endif // STB_NPTR - - -////////////////////////////////////////////////////////////////////////////// -// -// File Processing -// - - -#ifdef _MSC_VER - #define stb_rename(x,y) _wrename((const wchar_t *)stb__from_utf8(x), (const wchar_t *)stb__from_utf8_alt(y)) - #define stb_mktemp _mktemp -#else - #define stb_mktemp mktemp - #define stb_rename rename -#endif - -STB_EXTERN void stb_fput_varlen64(FILE *f, stb_uint64 v); -STB_EXTERN stb_uint64 stb_fget_varlen64(FILE *f); -STB_EXTERN int stb_size_varlen64(stb_uint64 v); - - -#define stb_filec (char *) stb_file -#define stb_fileu (unsigned char *) stb_file -STB_EXTERN void * stb_file(char *filename, size_t *length); -STB_EXTERN void * stb_file_max(char *filename, size_t *length); -STB_EXTERN size_t stb_filelen(FILE *f); -STB_EXTERN int stb_filewrite(char *filename, void *data, size_t length); -STB_EXTERN int stb_filewritestr(char *filename, char *data); -STB_EXTERN char ** stb_stringfile(char *filename, int *len); -STB_EXTERN char ** stb_stringfile_trimmed(char *name, int *len, char comm); -STB_EXTERN char * stb_fgets(char *buffer, int buflen, FILE *f); -STB_EXTERN char * stb_fgets_malloc(FILE *f); -STB_EXTERN int stb_fexists(char *filename); -STB_EXTERN int stb_fcmp(char *s1, char *s2); -STB_EXTERN int stb_feq(char *s1, char *s2); -STB_EXTERN time_t stb_ftimestamp(char *filename); - -STB_EXTERN int stb_fullpath(char *abs, int abs_size, char *rel); -STB_EXTERN FILE * stb_fopen(char *filename, char *mode); -STB_EXTERN int stb_fclose(FILE *f, int keep); - -enum -{ - stb_keep_no = 0, - stb_keep_yes = 1, - stb_keep_if_different = 2, -}; - -STB_EXTERN int stb_copyfile(char *src, char *dest); - -STB_EXTERN void stb_fput_varlen64(FILE *f, stb_uint64 v); -STB_EXTERN stb_uint64 stb_fget_varlen64(FILE *f); -STB_EXTERN int stb_size_varlen64(stb_uint64 v); - -STB_EXTERN void stb_fwrite32(FILE *f, stb_uint32 datum); -STB_EXTERN void stb_fput_varlen (FILE *f, int v); -STB_EXTERN void stb_fput_varlenu(FILE *f, unsigned int v); -STB_EXTERN int stb_fget_varlen (FILE *f); -STB_EXTERN stb_uint stb_fget_varlenu(FILE *f); -STB_EXTERN void stb_fput_ranged (FILE *f, int v, int b, stb_uint n); -STB_EXTERN int stb_fget_ranged (FILE *f, int b, stb_uint n); -STB_EXTERN int stb_size_varlen (int v); -STB_EXTERN int stb_size_varlenu(unsigned int v); -STB_EXTERN int stb_size_ranged (int b, stb_uint n); - -STB_EXTERN int stb_fread(void *data, size_t len, size_t count, void *f); -STB_EXTERN int stb_fwrite(void *data, size_t len, size_t count, void *f); - -#if 0 -typedef struct -{ - FILE *base_file; - char *buffer; - int buffer_size; - int buffer_off; - int buffer_left; -} STBF; - -STB_EXTERN STBF *stb_tfopen(char *filename, char *mode); -STB_EXTERN int stb_tfread(void *data, size_t len, size_t count, STBF *f); -STB_EXTERN int stb_tfwrite(void *data, size_t len, size_t count, STBF *f); -#endif - -#ifdef STB_DEFINE - -#if 0 -STBF *stb_tfopen(char *filename, char *mode) -{ - STBF *z; - FILE *f = fopen(filename, mode); - if (!f) return NULL; - z = (STBF *) malloc(sizeof(*z)); - if (!z) { fclose(f); return NULL; } - z->base_file = f; - if (!strcmp(mode, "rb") || !strcmp(mode, "wb")) { - z->buffer_size = 4096; - z->buffer_off = z->buffer_size; - z->buffer_left = 0; - z->buffer = malloc(z->buffer_size); - if (!z->buffer) { free(z); fclose(f); return NULL; } - } else { - z->buffer = 0; - z->buffer_size = 0; - z->buffer_left = 0; - } - return z; -} - -int stb_tfread(void *data, size_t len, size_t count, STBF *f) -{ - int total = len*count, done=0; - if (!total) return 0; - if (total <= z->buffer_left) { - memcpy(data, z->buffer + z->buffer_off, total); - z->buffer_off += total; - z->buffer_left -= total; - return count; - } else { - char *out = (char *) data; - - // consume all buffered data - memcpy(data, z->buffer + z->buffer_off, z->buffer_left); - done = z->buffer_left; - out += z->buffer_left; - z->buffer_left=0; - - if (total-done > (z->buffer_size >> 1)) { - done += fread(out - } - } -} -#endif - -void stb_fwrite32(FILE *f, stb_uint32 x) -{ - fwrite(&x, 4, 1, f); -} - -#if defined(_MSC_VER) || defined(__MINGW32__) - #define stb__stat _stat -#else - #define stb__stat stat -#endif - -int stb_fexists(char *filename) -{ - struct stb__stat buf; - return stb__windows( - _wstat((const wchar_t *)stb__from_utf8(filename), &buf), - stat(filename,&buf) - ) == 0; -} - -time_t stb_ftimestamp(char *filename) -{ - struct stb__stat buf; - if (stb__windows( - _wstat((const wchar_t *)stb__from_utf8(filename), &buf), - stat(filename,&buf) - ) == 0) - { - return buf.st_mtime; - } else { - return 0; - } -} - -size_t stb_filelen(FILE *f) -{ - size_t len, pos; - pos = ftell(f); - fseek(f, 0, SEEK_END); - len = ftell(f); - fseek(f, pos, SEEK_SET); - return len; -} - -void *stb_file(char *filename, size_t *length) -{ - FILE *f = stb__fopen(filename, "rb"); - char *buffer; - size_t len, len2; - if (!f) return NULL; - len = stb_filelen(f); - buffer = (char *) malloc(len+2); // nul + extra - len2 = fread(buffer, 1, len, f); - if (len2 == len) { - if (length) *length = len; - buffer[len] = 0; - } else { - free(buffer); - buffer = NULL; - } - fclose(f); - return buffer; -} - -int stb_filewrite(char *filename, void *data, size_t length) -{ - FILE *f = stb_fopen(filename, "wb"); - if (f) { - fwrite(data, 1, length, f); - stb_fclose(f, stb_keep_if_different); - } - return f != NULL; -} - -int stb_filewritestr(char *filename, char *data) -{ - return stb_filewrite(filename, data, strlen(data)); -} - -void * stb_file_max(char *filename, size_t *length) -{ - FILE *f = stb__fopen(filename, "rb"); - char *buffer; - size_t len, maxlen; - if (!f) return NULL; - maxlen = *length; - buffer = (char *) malloc(maxlen+1); - len = fread(buffer, 1, maxlen, f); - buffer[len] = 0; - fclose(f); - *length = len; - return buffer; -} - -char ** stb_stringfile(char *filename, int *plen) -{ - FILE *f = stb__fopen(filename, "rb"); - char *buffer, **list=NULL, *s; - size_t len, count, i; - - if (!f) return NULL; - len = stb_filelen(f); - buffer = (char *) malloc(len+1); - len = fread(buffer, 1, len, f); - buffer[len] = 0; - fclose(f); - - // two passes through: first time count lines, second time set them - for (i=0; i < 2; ++i) { - s = buffer; - if (i == 1) - list[0] = s; - count = 1; - while (*s) { - if (*s == '\n' || *s == '\r') { - // detect if both cr & lf are together - int crlf = (s[0] + s[1]) == ('\n' + '\r'); - if (i == 1) *s = 0; - if (crlf) ++s; - if (s[1]) { // it's not over yet - if (i == 1) list[count] = s+1; - ++count; - } - } - ++s; - } - if (i == 0) { - list = (char **) malloc(sizeof(*list) * (count+1) + len+1); - if (!list) return NULL; - list[count] = 0; - // recopy the file so there's just a single allocation to free - memcpy(&list[count+1], buffer, len+1); - free(buffer); - buffer = (char *) &list[count+1]; - if (plen) *plen = count; - } - } - return list; -} - -char ** stb_stringfile_trimmed(char *name, int *len, char comment) -{ - int i,n,o=0; - char **s = stb_stringfile(name, &n); - if (s == NULL) return NULL; - for (i=0; i < n; ++i) { - char *p = stb_skipwhite(s[i]); - if (*p && *p != comment) - s[o++] = p; - } - s[o] = NULL; - if (len) *len = o; - return s; -} - -char * stb_fgets(char *buffer, int buflen, FILE *f) -{ - char *p; - buffer[0] = 0; - p = fgets(buffer, buflen, f); - if (p) { - int n = strlen(p)-1; - if (n >= 0) - if (p[n] == '\n') - p[n] = 0; - } - return p; -} - -char * stb_fgets_malloc(FILE *f) -{ - // avoid reallocing for small strings - char quick_buffer[800]; - quick_buffer[sizeof(quick_buffer)-2] = 0; - if (!fgets(quick_buffer, sizeof(quick_buffer), f)) - return NULL; - - if (quick_buffer[sizeof(quick_buffer)-2] == 0) { - int n = strlen(quick_buffer); - if (n > 0 && quick_buffer[n-1] == '\n') - quick_buffer[n-1] = 0; - return strdup(quick_buffer); - } else { - char *p; - char *a = strdup(quick_buffer); - int len = sizeof(quick_buffer)-1; - - while (!feof(f)) { - if (a[len-1] == '\n') break; - a = (char *) realloc(a, len*2); - p = &a[len]; - p[len-2] = 0; - if (!fgets(p, len, f)) - break; - if (p[len-2] == 0) { - len += strlen(p); - break; - } - len = len + (len-1); - } - if (a[len-1] == '\n') - a[len-1] = 0; - return a; - } -} - -int stb_fullpath(char *abs, int abs_size, char *rel) -{ - #ifdef _MSC_VER - return _fullpath(abs, rel, abs_size) != NULL; - #else - if (rel[0] == '/' || rel[0] == '~') { - if ((int) strlen(rel) >= abs_size) - return 0; - strcpy(abs,rel); - return STB_TRUE; - } else { - int n; - getcwd(abs, abs_size); - n = strlen(abs); - if (n+(int) strlen(rel)+2 <= abs_size) { - abs[n] = '/'; - strcpy(abs+n+1, rel); - return STB_TRUE; - } else { - return STB_FALSE; - } - } - #endif -} - -static int stb_fcmp_core(FILE *f, FILE *g) -{ - char buf1[1024],buf2[1024]; - int n1,n2, res=0; - - while (1) { - n1 = fread(buf1, 1, sizeof(buf1), f); - n2 = fread(buf2, 1, sizeof(buf2), g); - res = memcmp(buf1,buf2,stb_min(n1,n2)); - if (res) - break; - if (n1 != n2) { - res = n1 < n2 ? -1 : 1; - break; - } - if (n1 == 0) - break; - } - - fclose(f); - fclose(g); - return res; -} - -int stb_fcmp(char *s1, char *s2) -{ - FILE *f = stb__fopen(s1, "rb"); - FILE *g = stb__fopen(s2, "rb"); - - if (f == NULL || g == NULL) { - if (f) fclose(f); - if (g) { - fclose(g); - return STB_TRUE; - } - return f != NULL; - } - - return stb_fcmp_core(f,g); -} - -int stb_feq(char *s1, char *s2) -{ - FILE *f = stb__fopen(s1, "rb"); - FILE *g = stb__fopen(s2, "rb"); - - if (f == NULL || g == NULL) { - if (f) fclose(f); - if (g) fclose(g); - return f == g; - } - - // feq is faster because it shortcuts if they're different length - if (stb_filelen(f) != stb_filelen(g)) { - fclose(f); - fclose(g); - return 0; - } - - return !stb_fcmp_core(f,g); -} - -static stb_ptrmap *stb__files; - -typedef struct -{ - char *temp_name; - char *name; - int errors; -} stb__file_data; - -FILE * stb_fopen(char *filename, char *mode) -{ - FILE *f; - char name_full[4096]; - char temp_full[sizeof(name_full) + 12]; - int p; -#ifdef _MSC_VER - int j; -#endif - if (mode[0] != 'w' && !strchr(mode, '+')) - return stb__fopen(filename, mode); - - // save away the full path to the file so if the program - // changes the cwd everything still works right! unix has - // better ways to do this, but we have to work in windows - name_full[0] = '\0'; // stb_fullpath reads name_full[0] - if (stb_fullpath(name_full, sizeof(name_full), filename)==0) - return 0; - - // try to generate a temporary file in the same directory - p = strlen(name_full)-1; - while (p > 0 && name_full[p] != '/' && name_full[p] != '\\' - && name_full[p] != ':' && name_full[p] != '~') - --p; - ++p; - - memcpy(temp_full, name_full, p); - - #ifdef _MSC_VER - // try multiple times to make a temp file... just in - // case some other process makes the name first - for (j=0; j < 32; ++j) { - strcpy(temp_full+p, "stmpXXXXXX"); - if (stb_mktemp(temp_full) == NULL) - return 0; - - f = fopen(temp_full, mode); - if (f != NULL) - break; - } - #else - { - strcpy(temp_full+p, "stmpXXXXXX"); - #ifdef __MINGW32__ - int fd = open(mktemp(temp_full), O_RDWR); - #else - int fd = mkstemp(temp_full); - #endif - if (fd == -1) return NULL; - f = fdopen(fd, mode); - if (f == NULL) { - unlink(temp_full); - close(fd); - return NULL; - } - } - #endif - if (f != NULL) { - stb__file_data *d = (stb__file_data *) malloc(sizeof(*d)); - if (!d) { assert(0); /* NOTREACHED */fclose(f); return NULL; } - if (stb__files == NULL) stb__files = stb_ptrmap_create(); - d->temp_name = strdup(temp_full); - d->name = strdup(name_full); - d->errors = 0; - stb_ptrmap_add(stb__files, f, d); - return f; - } - - return NULL; -} - -int stb_fclose(FILE *f, int keep) -{ - stb__file_data *d; - - int ok = STB_FALSE; - if (f == NULL) return 0; - - if (ferror(f)) - keep = stb_keep_no; - - fclose(f); - - if (stb__files && stb_ptrmap_remove(stb__files, f, (void **) &d)) { - if (stb__files->count == 0) { - stb_ptrmap_destroy(stb__files); - stb__files = NULL; - } - } else - return STB_TRUE; // not special - - if (keep == stb_keep_if_different) { - // check if the files are identical - if (stb_feq(d->name, d->temp_name)) { - keep = stb_keep_no; - ok = STB_TRUE; // report success if no change - } - } - - if (keep != stb_keep_no) { - if (stb_fexists(d->name) && remove(d->name)) { - // failed to delete old, so don't keep new - keep = stb_keep_no; - } else { - if (!stb_rename(d->temp_name, d->name)) - ok = STB_TRUE; - else - keep=stb_keep_no; - } - } - - if (keep == stb_keep_no) - remove(d->temp_name); - - free(d->temp_name); - free(d->name); - free(d); - - return ok; -} - -int stb_copyfile(char *src, char *dest) -{ - char raw_buffer[1024]; - char *buffer; - int buf_size = 65536; - - FILE *f, *g; - - // if file already exists at destination, do nothing - if (stb_feq(src, dest)) return STB_TRUE; - - // open file - f = stb__fopen(src, "rb"); - if (f == NULL) return STB_FALSE; - - // open file for writing - g = stb__fopen(dest, "wb"); - if (g == NULL) { - fclose(f); - return STB_FALSE; - } - - buffer = (char *) malloc(buf_size); - if (buffer == NULL) { - buffer = raw_buffer; - buf_size = sizeof(raw_buffer); - } - - while (!feof(f)) { - int n = fread(buffer, 1, buf_size, f); - if (n != 0) - fwrite(buffer, 1, n, g); - } - - fclose(f); - if (buffer != raw_buffer) - free(buffer); - - fclose(g); - return STB_TRUE; -} - -// varlen: -// v' = (v >> 31) + (v < 0 ? ~v : v)<<1; // small abs(v) => small v' -// output v as big endian v'+k for v' <= k: -// 1 byte : v' <= 0x00000080 ( -64 <= v < 64) 7 bits -// 2 bytes: v' <= 0x00004000 (-8192 <= v < 8192) 14 bits -// 3 bytes: v' <= 0x00200000 21 bits -// 4 bytes: v' <= 0x10000000 28 bits -// the number of most significant 1-bits in the first byte -// equals the number of bytes after the first - -#define stb__varlen_xform(v) (v<0 ? (~v << 1)+1 : (v << 1)) - -int stb_size_varlen(int v) { return stb_size_varlenu(stb__varlen_xform(v)); } -int stb_size_varlenu(unsigned int v) -{ - if (v < 0x00000080) return 1; - if (v < 0x00004000) return 2; - if (v < 0x00200000) return 3; - if (v < 0x10000000) return 4; - return 5; -} - -void stb_fput_varlen(FILE *f, int v) { stb_fput_varlenu(f, stb__varlen_xform(v)); } - -void stb_fput_varlenu(FILE *f, unsigned int z) -{ - if (z >= 0x10000000) fputc(0xF0,f); - if (z >= 0x00200000) fputc((z < 0x10000000 ? 0xE0 : 0)+(z>>24),f); - if (z >= 0x00004000) fputc((z < 0x00200000 ? 0xC0 : 0)+(z>>16),f); - if (z >= 0x00000080) fputc((z < 0x00004000 ? 0x80 : 0)+(z>> 8),f); - fputc(z,f); -} - -#define stb_fgetc(f) ((unsigned char) fgetc(f)) - -int stb_fget_varlen(FILE *f) -{ - unsigned int z = stb_fget_varlenu(f); - return (z & 1) ? ~(z>>1) : (z>>1); -} - -unsigned int stb_fget_varlenu(FILE *f) -{ - unsigned int z; - unsigned char d; - d = stb_fgetc(f); - - if (d >= 0x80) { - if (d >= 0xc0) { - if (d >= 0xe0) { - if (d == 0xf0) z = stb_fgetc(f) << 24; - else z = (d - 0xe0) << 24; - z += stb_fgetc(f) << 16; - } - else - z = (d - 0xc0) << 16; - z += stb_fgetc(f) << 8; - } else - z = (d - 0x80) << 8; - z += stb_fgetc(f); - } else - z = d; - return z; -} - -stb_uint64 stb_fget_varlen64(FILE *f) -{ - stb_uint64 z; - unsigned char d; - d = stb_fgetc(f); - - if (d >= 0x80) { - if (d >= 0xc0) { - if (d >= 0xe0) { - if (d >= 0xf0) { - if (d >= 0xf8) { - if (d >= 0xfc) { - if (d >= 0xfe) { - if (d >= 0xff) - z = (stb_uint64) stb_fgetc(f) << 56; - else - z = (stb_uint64) (d - 0xfe) << 56; - z |= (stb_uint64) stb_fgetc(f) << 48; - } else z = (stb_uint64) (d - 0xfc) << 48; - z |= (stb_uint64) stb_fgetc(f) << 40; - } else z = (stb_uint64) (d - 0xf8) << 40; - z |= (stb_uint64) stb_fgetc(f) << 32; - } else z = (stb_uint64) (d - 0xf0) << 32; - z |= (stb_uint) stb_fgetc(f) << 24; - } else z = (stb_uint) (d - 0xe0) << 24; - z |= (stb_uint) stb_fgetc(f) << 16; - } else z = (stb_uint) (d - 0xc0) << 16; - z |= (stb_uint) stb_fgetc(f) << 8; - } else z = (stb_uint) (d - 0x80) << 8; - z |= stb_fgetc(f); - } else - z = d; - - return (z & 1) ? ~(z >> 1) : (z >> 1); -} - -int stb_size_varlen64(stb_uint64 v) -{ - if (v < 0x00000080) return 1; - if (v < 0x00004000) return 2; - if (v < 0x00200000) return 3; - if (v < 0x10000000) return 4; - if (v < STB_IMM_UINT64(0x0000000800000000)) return 5; - if (v < STB_IMM_UINT64(0x0000040000000000)) return 6; - if (v < STB_IMM_UINT64(0x0002000000000000)) return 7; - if (v < STB_IMM_UINT64(0x0100000000000000)) return 8; - return 9; -} - -void stb_fput_varlen64(FILE *f, stb_uint64 v) -{ - stb_uint64 z = stb__varlen_xform(v); - int first=1; - if (z >= STB_IMM_UINT64(0x100000000000000)) { - fputc(0xff,f); - first=0; - } - if (z >= STB_IMM_UINT64(0x02000000000000)) fputc((first ? 0xFE : 0)+(char)(z>>56),f), first=0; - if (z >= STB_IMM_UINT64(0x00040000000000)) fputc((first ? 0xFC : 0)+(char)(z>>48),f), first=0; - if (z >= STB_IMM_UINT64(0x00000800000000)) fputc((first ? 0xF8 : 0)+(char)(z>>40),f), first=0; - if (z >= STB_IMM_UINT64(0x00000010000000)) fputc((first ? 0xF0 : 0)+(char)(z>>32),f), first=0; - if (z >= STB_IMM_UINT64(0x00000000200000)) fputc((first ? 0xE0 : 0)+(char)(z>>24),f), first=0; - if (z >= STB_IMM_UINT64(0x00000000004000)) fputc((first ? 0xC0 : 0)+(char)(z>>16),f), first=0; - if (z >= STB_IMM_UINT64(0x00000000000080)) fputc((first ? 0x80 : 0)+(char)(z>> 8),f), first=0; - fputc((char)z,f); -} - -void stb_fput_ranged(FILE *f, int v, int b, stb_uint n) -{ - v -= b; - if (n <= (1 << 31)) - assert((stb_uint) v < n); - if (n > (1 << 24)) fputc(v >> 24, f); - if (n > (1 << 16)) fputc(v >> 16, f); - if (n > (1 << 8)) fputc(v >> 8, f); - fputc(v,f); -} - -int stb_fget_ranged(FILE *f, int b, stb_uint n) -{ - unsigned int v=0; - if (n > (1 << 24)) v += stb_fgetc(f) << 24; - if (n > (1 << 16)) v += stb_fgetc(f) << 16; - if (n > (1 << 8)) v += stb_fgetc(f) << 8; - v += stb_fgetc(f); - return b+v; -} - -int stb_size_ranged(int b, stb_uint n) -{ - if (n > (1 << 24)) return 4; - if (n > (1 << 16)) return 3; - if (n > (1 << 8)) return 2; - return 1; -} - -void stb_fput_string(FILE *f, char *s) -{ - int len = strlen(s); - stb_fput_varlenu(f, len); - fwrite(s, 1, len, f); -} - -// inverse of the above algorithm -char *stb_fget_string(FILE *f, void *p) -{ - char *s; - int len = stb_fget_varlenu(f); - if (len > 4096) return NULL; - s = p ? stb_malloc_string(p, len+1) : (char *) malloc(len+1); - fread(s, 1, len, f); - s[len] = 0; - return s; -} - -char *stb_strdup(char *str, void *pool) -{ - int len = strlen(str); - char *p = stb_malloc_string(pool, len+1); - strcpy(p, str); - return p; -} - -// strip the trailing '/' or '\\' from a directory so we can refer to it -// as a file for _stat() -char *stb_strip_final_slash(char *t) -{ - if (t[0]) { - char *z = t + strlen(t) - 1; - // *z is the last character - if (*z == '\\' || *z == '/') - if (z != t+2 || t[1] != ':') // but don't strip it if it's e.g. "c:/" - *z = 0; - if (*z == '\\') - *z = '/'; // canonicalize to make sure it matches db - } - return t; -} -#endif - -////////////////////////////////////////////////////////////////////////////// -// -// Options parsing -// - -STB_EXTERN char **stb_getopt_param(int *argc, char **argv, char *param); -STB_EXTERN char **stb_getopt(int *argc, char **argv); -STB_EXTERN void stb_getopt_free(char **opts); - -#ifdef STB_DEFINE - -void stb_getopt_free(char **opts) -{ - int i; - char ** o2 = opts; - for (i=0; i < stb_arr_len(o2); ++i) - free(o2[i]); - stb_arr_free(o2); -} - -char **stb_getopt(int *argc, char **argv) -{ - return stb_getopt_param(argc, argv, ""); -} - -char **stb_getopt_param(int *argc, char **argv, char *param) -{ - char ** opts=NULL; - int i,j=1; - for (i=1; i < *argc; ++i) { - if (argv[i][0] != '-') { - argv[j++] = argv[i]; - } else { - if (argv[i][1] == 0) { // plain - == don't parse further options - ++i; - while (i < *argc) - argv[j++] = argv[i++]; - break; - } else { - int k; - char *q = argv[i]; // traverse options list - for (k=1; q[k]; ++k) { - char *s; - if (strchr(param, q[k])) { // does it take a parameter? - char *t = &q[k+1], z = q[k]; - int len=0; - if (*t == 0) { - if (i == *argc-1) { // takes a parameter, but none found - *argc = 0; - stb_getopt_free(opts); - return NULL; - } - t = argv[++i]; - } else - k += strlen(t); - len = strlen(t); - s = (char *) malloc(len+2); - if (!s) return NULL; - s[0] = z; - strcpy(s+1, t); - } else { - // no parameter - s = (char *) malloc(2); - if (!s) return NULL; - s[0] = q[k]; - s[1] = 0; - } - stb_arr_push(opts, s); - } - } - } - } - stb_arr_push(opts, NULL); - *argc = j; - return opts; -} -#endif - - -////////////////////////////////////////////////////////////////////////////// -// -// Portable directory reading -// - -STB_EXTERN char **stb_readdir_files (char *dir); -STB_EXTERN char **stb_readdir_files_mask(char *dir, char *wild); -STB_EXTERN char **stb_readdir_subdirs(char *dir); -STB_EXTERN char **stb_readdir_subdirs_mask(char *dir, char *wild); -STB_EXTERN void stb_readdir_free (char **files); -STB_EXTERN char **stb_readdir_recursive(char *dir, char *filespec); -STB_EXTERN void stb_delete_directory_recursive(char *dir); - -#ifdef STB_DEFINE - -#ifdef _MSC_VER -#include -#else -#include -#include -#endif - -void stb_readdir_free(char **files) -{ - char **f2 = files; - int i; - for (i=0; i < stb_arr_len(f2); ++i) - free(f2[i]); - stb_arr_free(f2); -} - -static int isdotdirname(char *name) -{ - if (name[0] == '.') - return (name[1] == '.') ? !name[2] : !name[1]; - return 0; -} - -STB_EXTERN int stb_wildmatchi(char *expr, char *candidate); -static char **readdir_raw(char *dir, int return_subdirs, char *mask) -{ - char **results = NULL; - char buffer[4096], with_slash[4096]; - size_t n; - - #ifdef _MSC_VER - stb__wchar *ws; - struct _wfinddata_t data; - #ifdef _WIN64 - const intptr_t none = -1; - intptr_t z; - #else - const long none = -1; - long z; - #endif - #else // !_MSC_VER - const DIR *none = NULL; - DIR *z; - #endif - - n = stb_strscpy(buffer,dir,sizeof(buffer)); - if (!n || n >= sizeof(buffer)) - return NULL; - stb_fixpath(buffer); - n--; - - if (n > 0 && (buffer[n-1] != '/')) { - buffer[n++] = '/'; - } - buffer[n] = 0; - if (!stb_strscpy(with_slash,buffer,sizeof(with_slash))) - return NULL; - - #ifdef _MSC_VER - if (!stb_strscpy(buffer+n,"*.*",sizeof(buffer)-n)) - return NULL; - ws = stb__from_utf8(buffer); - z = _wfindfirst((const wchar_t *)ws, &data); - #else - z = opendir(dir); - #endif - - if (z != none) { - int nonempty = STB_TRUE; - #ifndef _MSC_VER - struct dirent *data = readdir(z); - nonempty = (data != NULL); - #endif - - if (nonempty) { - - do { - int is_subdir; - #ifdef _MSC_VER - char *name = stb__to_utf8((stb__wchar *)data.name); - if (name == NULL) { - fprintf(stderr, "%s to convert '%S' to %s!\n", "Unable", data.name, "utf8"); - continue; - } - is_subdir = !!(data.attrib & _A_SUBDIR); - #else - char *name = data->d_name; - if (!stb_strscpy(buffer+n,name,sizeof(buffer)-n)) - break; - // Could follow DT_LNK, but would need to check for recursive links. - is_subdir = !!(data->d_type & DT_DIR); - #endif - - if (is_subdir == return_subdirs) { - if (!is_subdir || !isdotdirname(name)) { - if (!mask || stb_wildmatchi(mask, name)) { - char buffer[4096],*p=buffer; - if ( stb_snprintf(buffer, sizeof(buffer), "%s%s", with_slash, name) < 0 ) - break; - if (buffer[0] == '.' && buffer[1] == '/') - p = buffer+2; - stb_arr_push(results, strdup(p)); - } - } - } - } - #ifdef _MSC_VER - while (0 == _wfindnext(z, &data)); - #else - while ((data = readdir(z)) != NULL); - #endif - } - #ifdef _MSC_VER - _findclose(z); - #else - closedir(z); - #endif - } - return results; -} - -char **stb_readdir_files (char *dir) { return readdir_raw(dir, 0, NULL); } -char **stb_readdir_subdirs(char *dir) { return readdir_raw(dir, 1, NULL); } -char **stb_readdir_files_mask(char *dir, char *wild) { return readdir_raw(dir, 0, wild); } -char **stb_readdir_subdirs_mask(char *dir, char *wild) { return readdir_raw(dir, 1, wild); } - -int stb__rec_max=0x7fffffff; -static char **stb_readdir_rec(char **sofar, char *dir, char *filespec) -{ - char **files; - char ** dirs; - char **p; - - if (stb_arr_len(sofar) >= stb__rec_max) return sofar; - - files = stb_readdir_files_mask(dir, filespec); - stb_arr_for(p, files) { - stb_arr_push(sofar, strdup(*p)); - if (stb_arr_len(sofar) >= stb__rec_max) break; - } - stb_readdir_free(files); - if (stb_arr_len(sofar) >= stb__rec_max) return sofar; - - dirs = stb_readdir_subdirs(dir); - stb_arr_for(p, dirs) - sofar = stb_readdir_rec(sofar, *p, filespec); - stb_readdir_free(dirs); - return sofar; -} - -char **stb_readdir_recursive(char *dir, char *filespec) -{ - return stb_readdir_rec(NULL, dir, filespec); -} - -void stb_delete_directory_recursive(char *dir) -{ - char **list = stb_readdir_subdirs(dir); - int i; - for (i=0; i < stb_arr_len(list); ++i) - stb_delete_directory_recursive(list[i]); - stb_arr_free(list); - list = stb_readdir_files(dir); - for (i=0; i < stb_arr_len(list); ++i) - if (!remove(list[i])) { - // on windows, try again after making it writeable; don't ALWAYS - // do this first since that would be slow in the normal case - #ifdef _MSC_VER - _chmod(list[i], _S_IWRITE); - remove(list[i]); - #endif - } - stb_arr_free(list); - stb__windows(_rmdir,rmdir)(dir); -} - -#endif - -////////////////////////////////////////////////////////////////////////////// -// -// construct trees from filenames; useful for cmirror summaries - -typedef struct stb_dirtree2 stb_dirtree2; - -struct stb_dirtree2 -{ - stb_dirtree2 **subdirs; - - // make convenient for stb_summarize_tree - int num_subdir; - float weight; - - // actual data - char *fullpath; - char *relpath; - char **files; -}; - -STB_EXTERN stb_dirtree2 *stb_dirtree2_from_files_relative(char *src, char **filelist, int count); -STB_EXTERN stb_dirtree2 *stb_dirtree2_from_files(char **filelist, int count); -STB_EXTERN int stb_dir_is_prefix(char *dir, int dirlen, char *file); - -#ifdef STB_DEFINE - -int stb_dir_is_prefix(char *dir, int dirlen, char *file) -{ - if (dirlen == 0) return STB_TRUE; - if (stb_strnicmp(dir, file, dirlen)) return STB_FALSE; - if (file[dirlen] == '/' || file[dirlen] == '\\') return STB_TRUE; - return STB_FALSE; -} - -stb_dirtree2 *stb_dirtree2_from_files_relative(char *src, char **filelist, int count) -{ - char buffer1[1024]; - int i; - int dlen = strlen(src), elen; - stb_dirtree2 *d; - char ** descendents = NULL; - char ** files = NULL; - char *s; - if (!count) return NULL; - // first find all the ones that belong here... note this is will take O(NM) with N files and M subdirs - for (i=0; i < count; ++i) { - if (stb_dir_is_prefix(src, dlen, filelist[i])) { - stb_arr_push(descendents, filelist[i]); - } - } - if (descendents == NULL) - return NULL; - elen = dlen; - // skip a leading slash - if (elen == 0 && (descendents[0][0] == '/' || descendents[0][0] == '\\')) - ++elen; - else if (elen) - ++elen; - // now extract all the ones that have their root here - for (i=0; i < stb_arr_len(descendents);) { - if (!stb_strchr2(descendents[i]+elen, '/', '\\')) { - stb_arr_push(files, descendents[i]); - descendents[i] = descendents[stb_arr_len(descendents)-1]; - stb_arr_pop(descendents); - } else - ++i; - } - // now create a record - d = (stb_dirtree2 *) malloc(sizeof(*d)); - d->files = files; - d->subdirs = NULL; - d->fullpath = strdup(src); - s = stb_strrchr2(d->fullpath, '/', '\\'); - if (s) - ++s; - else - s = d->fullpath; - d->relpath = s; - // now create the children - qsort(descendents, stb_arr_len(descendents), sizeof(char *), stb_qsort_stricmp(0)); - buffer1[0] = 0; - for (i=0; i < stb_arr_len(descendents); ++i) { - char buffer2[1024]; - char *s = descendents[i] + elen, *t; - t = stb_strchr2(s, '/', '\\'); - assert(t); - stb_strncpy(buffer2, descendents[i], t-descendents[i]+1); - if (stb_stricmp(buffer1, buffer2)) { - stb_dirtree2 *t = stb_dirtree2_from_files_relative(buffer2, descendents, stb_arr_len(descendents)); - assert(t != NULL); - strcpy(buffer1, buffer2); - stb_arr_push(d->subdirs, t); - } - } - d->num_subdir = stb_arr_len(d->subdirs); - d->weight = 0; - return d; -} - -stb_dirtree2 *stb_dirtree2_from_files(char **filelist, int count) -{ - return stb_dirtree2_from_files_relative("", filelist, count); -} -#endif - -////////////////////////////////////////////////////////////////////////////// -// -// Checksums: CRC-32, ADLER32, SHA-1 -// -// CRC-32 and ADLER32 allow streaming blocks -// SHA-1 requires either a complete buffer, max size 2^32 - 73 -// or it can checksum directly from a file, max 2^61 - -#define STB_ADLER32_SEED 1 -#define STB_CRC32_SEED 0 // note that we logical NOT this in the code - -STB_EXTERN stb_uint - stb_adler32(stb_uint adler32, stb_uchar *buffer, stb_uint buflen); -STB_EXTERN stb_uint - stb_crc32_block(stb_uint crc32, stb_uchar *buffer, stb_uint len); -STB_EXTERN stb_uint stb_crc32(unsigned char *buffer, stb_uint len); - -STB_EXTERN void stb_sha1( - unsigned char output[20], unsigned char *buffer, unsigned int len); -STB_EXTERN int stb_sha1_file(unsigned char output[20], char *file); - -STB_EXTERN void stb_sha1_readable(char display[27], unsigned char sha[20]); - -#ifdef STB_DEFINE -stb_uint stb_crc32_block(stb_uint crc, unsigned char *buffer, stb_uint len) -{ - static stb_uint crc_table[256]; - stb_uint i,j,s; - crc = ~crc; - - if (crc_table[1] == 0) - for(i=0; i < 256; i++) { - for (s=i, j=0; j < 8; ++j) - s = (s >> 1) ^ (s & 1 ? 0xedb88320 : 0); - crc_table[i] = s; - } - for (i=0; i < len; ++i) - crc = (crc >> 8) ^ crc_table[buffer[i] ^ (crc & 0xff)]; - return ~crc; -} - -stb_uint stb_crc32(unsigned char *buffer, stb_uint len) -{ - return stb_crc32_block(0, buffer, len); -} - -stb_uint stb_adler32(stb_uint adler32, stb_uchar *buffer, stb_uint buflen) -{ - const unsigned long ADLER_MOD = 65521; - unsigned long s1 = adler32 & 0xffff, s2 = adler32 >> 16; - unsigned long blocklen, i; - - blocklen = buflen % 5552; - while (buflen) { - for (i=0; i + 7 < blocklen; i += 8) { - s1 += buffer[0], s2 += s1; - s1 += buffer[1], s2 += s1; - s1 += buffer[2], s2 += s1; - s1 += buffer[3], s2 += s1; - s1 += buffer[4], s2 += s1; - s1 += buffer[5], s2 += s1; - s1 += buffer[6], s2 += s1; - s1 += buffer[7], s2 += s1; - - buffer += 8; - } - - for (; i < blocklen; ++i) - s1 += *buffer++, s2 += s1; - - s1 %= ADLER_MOD, s2 %= ADLER_MOD; - buflen -= blocklen; - blocklen = 5552; - } - return (s2 << 16) + s1; -} - -static void stb__sha1(stb_uchar *chunk, stb_uint h[5]) -{ - int i; - stb_uint a,b,c,d,e; - stb_uint w[80]; - - for (i=0; i < 16; ++i) - w[i] = stb_big32(&chunk[i*4]); - for (i=16; i < 80; ++i) { - stb_uint t; - t = w[i-3] ^ w[i-8] ^ w[i-14] ^ w[i-16]; - w[i] = (t + t) | (t >> 31); - } - - a = h[0]; - b = h[1]; - c = h[2]; - d = h[3]; - e = h[4]; - - #define STB__SHA1(k,f) \ - { \ - stb_uint temp = (a << 5) + (a >> 27) + (f) + e + (k) + w[i]; \ - e = d; \ - d = c; \ - c = (b << 30) + (b >> 2); \ - b = a; \ - a = temp; \ - } - - i=0; - for (; i < 20; ++i) STB__SHA1(0x5a827999, d ^ (b & (c ^ d)) ); - for (; i < 40; ++i) STB__SHA1(0x6ed9eba1, b ^ c ^ d ); - for (; i < 60; ++i) STB__SHA1(0x8f1bbcdc, (b & c) + (d & (b ^ c)) ); - for (; i < 80; ++i) STB__SHA1(0xca62c1d6, b ^ c ^ d ); - - #undef STB__SHA1 - - h[0] += a; - h[1] += b; - h[2] += c; - h[3] += d; - h[4] += e; -} - -void stb_sha1(stb_uchar output[20], stb_uchar *buffer, stb_uint len) -{ - unsigned char final_block[128]; - stb_uint end_start, final_len, j; - int i; - - stb_uint h[5]; - - h[0] = 0x67452301; - h[1] = 0xefcdab89; - h[2] = 0x98badcfe; - h[3] = 0x10325476; - h[4] = 0xc3d2e1f0; - - // we need to write padding to the last one or two - // blocks, so build those first into 'final_block' - - // we have to write one special byte, plus the 8-byte length - - // compute the block where the data runs out - end_start = len & ~63; - - // compute the earliest we can encode the length - if (((len+9) & ~63) == end_start) { - // it all fits in one block, so fill a second-to-last block - end_start -= 64; - } - - final_len = end_start + 128; - - // now we need to copy the data in - assert(end_start + 128 >= len+9); - assert(end_start < len || len < 64-9); - - j = 0; - if (end_start > len) - j = (stb_uint) - (int) end_start; - - for (; end_start + j < len; ++j) - final_block[j] = buffer[end_start + j]; - final_block[j++] = 0x80; - while (j < 128-5) // 5 byte length, so write 4 extra padding bytes - final_block[j++] = 0; - // big-endian size - final_block[j++] = len >> 29; - final_block[j++] = len >> 21; - final_block[j++] = len >> 13; - final_block[j++] = len >> 5; - final_block[j++] = len << 3; - assert(j == 128 && end_start + j == final_len); - - for (j=0; j < final_len; j += 64) { // 512-bit chunks - if (j+64 >= end_start+64) - stb__sha1(&final_block[j - end_start], h); - else - stb__sha1(&buffer[j], h); - } - - for (i=0; i < 5; ++i) { - output[i*4 + 0] = h[i] >> 24; - output[i*4 + 1] = h[i] >> 16; - output[i*4 + 2] = h[i] >> 8; - output[i*4 + 3] = h[i] >> 0; - } -} - -#ifdef _MSC_VER -int stb_sha1_file(stb_uchar output[20], char *file) -{ - int i; - stb_uint64 length=0; - unsigned char buffer[128]; - - FILE *f = stb__fopen(file, "rb"); - stb_uint h[5]; - - if (f == NULL) return 0; // file not found - - h[0] = 0x67452301; - h[1] = 0xefcdab89; - h[2] = 0x98badcfe; - h[3] = 0x10325476; - h[4] = 0xc3d2e1f0; - - for(;;) { - int n = fread(buffer, 1, 64, f); - if (n == 64) { - stb__sha1(buffer, h); - length += n; - } else { - int block = 64; - - length += n; - - buffer[n++] = 0x80; - - // if there isn't enough room for the length, double the block - if (n + 8 > 64) - block = 128; - - // pad to end - memset(buffer+n, 0, block-8-n); - - i = block - 8; - buffer[i++] = (stb_uchar) (length >> 53); - buffer[i++] = (stb_uchar) (length >> 45); - buffer[i++] = (stb_uchar) (length >> 37); - buffer[i++] = (stb_uchar) (length >> 29); - buffer[i++] = (stb_uchar) (length >> 21); - buffer[i++] = (stb_uchar) (length >> 13); - buffer[i++] = (stb_uchar) (length >> 5); - buffer[i++] = (stb_uchar) (length << 3); - assert(i == block); - stb__sha1(buffer, h); - if (block == 128) - stb__sha1(buffer+64, h); - else - assert(block == 64); - break; - } - } - fclose(f); - - for (i=0; i < 5; ++i) { - output[i*4 + 0] = h[i] >> 24; - output[i*4 + 1] = h[i] >> 16; - output[i*4 + 2] = h[i] >> 8; - output[i*4 + 3] = h[i] >> 0; - } - - return 1; -} -#endif // _MSC_VER - -// client can truncate this wherever they like -void stb_sha1_readable(char display[27], unsigned char sha[20]) -{ - char encoding[65] = "0123456789abcdefghijklmnopqrstuv" - "wxyzABCDEFGHIJKLMNOPQRSTUVWXYZ%$"; - int num_bits = 0, acc=0; - int i=0,o=0; - while (o < 26) { - int v; - // expand the accumulator - if (num_bits < 6) { - assert(i != 20); - acc += sha[i++] << num_bits; - num_bits += 8; - } - v = acc & ((1 << 6) - 1); - display[o++] = encoding[v]; - acc >>= 6; - num_bits -= 6; - } - assert(num_bits == 20*8 - 26*6); - display[o++] = encoding[acc]; -} - -#endif // STB_DEFINE - -/////////////////////////////////////////////////////////// -// -// simplified WINDOWS registry interface... hopefully -// we'll never actually use this? - -#if defined(_WIN32) - -STB_EXTERN void * stb_reg_open(char *mode, char *where); // mode: "rHKLM" or "rHKCU" or "w.." -STB_EXTERN void stb_reg_close(void *reg); -STB_EXTERN int stb_reg_read(void *zreg, char *str, void *data, unsigned long len); -STB_EXTERN int stb_reg_read_string(void *zreg, char *str, char *data, int len); -STB_EXTERN void stb_reg_write(void *zreg, char *str, void *data, unsigned long len); -STB_EXTERN void stb_reg_write_string(void *zreg, char *str, char *data); - -#if defined(STB_DEFINE) && !defined(STB_NO_REGISTRY) - -#define STB_HAS_REGISTRY - -#ifndef _WINDOWS_ - -#define HKEY void * - -STB_EXTERN __declspec(dllimport) long __stdcall RegCloseKey ( HKEY hKey ); -STB_EXTERN __declspec(dllimport) long __stdcall RegCreateKeyExA ( HKEY hKey, const char * lpSubKey, - int Reserved, char * lpClass, int dwOptions, - int samDesired, void *lpSecurityAttributes, HKEY * phkResult, int * lpdwDisposition ); -STB_EXTERN __declspec(dllimport) long __stdcall RegDeleteKeyA ( HKEY hKey, const char * lpSubKey ); -STB_EXTERN __declspec(dllimport) long __stdcall RegQueryValueExA ( HKEY hKey, const char * lpValueName, - int * lpReserved, unsigned long * lpType, unsigned char * lpData, unsigned long * lpcbData ); -STB_EXTERN __declspec(dllimport) long __stdcall RegSetValueExA ( HKEY hKey, const char * lpValueName, - int Reserved, int dwType, const unsigned char* lpData, int cbData ); -STB_EXTERN __declspec(dllimport) long __stdcall RegOpenKeyExA ( HKEY hKey, const char * lpSubKey, - int ulOptions, int samDesired, HKEY * phkResult ); - -#endif // _WINDOWS_ - -#define STB__REG_OPTION_NON_VOLATILE 0 -#define STB__REG_KEY_ALL_ACCESS 0x000f003f -#define STB__REG_KEY_READ 0x00020019 - -void *stb_reg_open(char *mode, char *where) -{ - long res; - HKEY base; - HKEY zreg; - if (!stb_stricmp(mode+1, "cu") || !stb_stricmp(mode+1, "hkcu")) - base = (HKEY) 0x80000001; // HKCU - else if (!stb_stricmp(mode+1, "lm") || !stb_stricmp(mode+1, "hklm")) - base = (HKEY) 0x80000002; // HKLM - else - return NULL; - - if (mode[0] == 'r') - res = RegOpenKeyExA(base, where, 0, STB__REG_KEY_READ, &zreg); - else if (mode[0] == 'w') - res = RegCreateKeyExA(base, where, 0, NULL, STB__REG_OPTION_NON_VOLATILE, STB__REG_KEY_ALL_ACCESS, NULL, &zreg, NULL); - else - return NULL; - - return res ? NULL : zreg; -} - -void stb_reg_close(void *reg) -{ - RegCloseKey((HKEY) reg); -} - -#define STB__REG_SZ 1 -#define STB__REG_BINARY 3 -#define STB__REG_DWORD 4 - -int stb_reg_read(void *zreg, char *str, void *data, unsigned long len) -{ - unsigned long type; - unsigned long alen = len; - if (0 == RegQueryValueExA((HKEY) zreg, str, 0, &type, (unsigned char *) data, &len)) - if (type == STB__REG_BINARY || type == STB__REG_SZ || type == STB__REG_DWORD) { - if (len < alen) - *((char *) data + len) = 0; - return 1; - } - return 0; -} - -void stb_reg_write(void *zreg, char *str, void *data, unsigned long len) -{ - if (zreg) - RegSetValueExA((HKEY) zreg, str, 0, STB__REG_BINARY, (const unsigned char *) data, len); -} - -int stb_reg_read_string(void *zreg, char *str, char *data, int len) -{ - if (!stb_reg_read(zreg, str, data, len)) return 0; - data[len-1] = 0; // force a 0 at the end of the string no matter what - return 1; -} - -void stb_reg_write_string(void *zreg, char *str, char *data) -{ - if (zreg) - RegSetValueExA((HKEY) zreg, str, 0, STB__REG_SZ, (const unsigned char *) data, strlen(data)+1); -} -#endif // STB_DEFINE -#endif // _WIN32 - - -////////////////////////////////////////////////////////////////////////////// -// -// stb_cfg - This is like the registry, but the config info -// is all stored in plain old files where we can -// backup and restore them easily. The LOCATION of -// the config files is gotten from... the registry! - -#ifndef STB_NO_STB_STRINGS -typedef struct stb_cfg_st stb_cfg; - -STB_EXTERN stb_cfg * stb_cfg_open(char *config, char *mode); // mode = "r", "w" -STB_EXTERN void stb_cfg_close(stb_cfg *cfg); -STB_EXTERN int stb_cfg_read(stb_cfg *cfg, char *key, void *value, int len); -STB_EXTERN void stb_cfg_write(stb_cfg *cfg, char *key, void *value, int len); -STB_EXTERN int stb_cfg_read_string(stb_cfg *cfg, char *key, char *value, int len); -STB_EXTERN void stb_cfg_write_string(stb_cfg *cfg, char *key, char *value); -STB_EXTERN int stb_cfg_delete(stb_cfg *cfg, char *key); -STB_EXTERN void stb_cfg_set_directory(char *dir); - -#ifdef STB_DEFINE - -typedef struct -{ - char *key; - void *value; - int value_len; -} stb__cfg_item; - -struct stb_cfg_st -{ - stb__cfg_item *data; - char *loaded_file; // this needs to be freed - FILE *f; // write the data to this file on close -}; - -static char *stb__cfg_sig = "sTbCoNfIg!\0\0"; -static char stb__cfg_dir[512]; -STB_EXTERN void stb_cfg_set_directory(char *dir) -{ - strcpy(stb__cfg_dir, dir); -} - -STB_EXTERN stb_cfg * stb_cfg_open(char *config, char *mode) -{ - size_t len; - stb_cfg *z; - char file[512]; - if (mode[0] != 'r' && mode[0] != 'w') return NULL; - - if (!stb__cfg_dir[0]) { - #ifdef _WIN32 - strcpy(stb__cfg_dir, "c:/stb"); - #else - strcpy(stb__cfg_dir, "~/.stbconfig"); - #endif - - #ifdef STB_HAS_REGISTRY - { - void *reg = stb_reg_open("rHKLM", "Software\\SilverSpaceship\\stb"); - if (reg) { - stb_reg_read_string(reg, "config_dir", stb__cfg_dir, sizeof(stb__cfg_dir)); - stb_reg_close(reg); - } - } - #endif - } - - sprintf(file, "%s/%s.cfg", stb__cfg_dir, config); - - z = (stb_cfg *) stb_malloc(0, sizeof(*z)); - z->data = NULL; - - z->loaded_file = stb_filec(file, &len); - if (z->loaded_file) { - char *s = z->loaded_file; - if (!memcmp(s, stb__cfg_sig, 12)) { - char *s = z->loaded_file + 12; - while (s < z->loaded_file + len) { - stb__cfg_item a; - int n = *(stb_int16 *) s; - a.key = s+2; - s = s+2 + n; - a.value_len = *(int *) s; - s += 4; - a.value = s; - s += a.value_len; - stb_arr_push(z->data, a); - } - assert(s == z->loaded_file + len); - } - } - - if (mode[0] == 'w') - z->f = fopen(file, "wb"); - else - z->f = NULL; - - return z; -} - -void stb_cfg_close(stb_cfg *z) -{ - if (z->f) { - int i; - // write the file out - fwrite(stb__cfg_sig, 12, 1, z->f); - for (i=0; i < stb_arr_len(z->data); ++i) { - stb_int16 n = strlen(z->data[i].key)+1; - fwrite(&n, 2, 1, z->f); - fwrite(z->data[i].key, n, 1, z->f); - fwrite(&z->data[i].value_len, 4, 1, z->f); - fwrite(z->data[i].value, z->data[i].value_len, 1, z->f); - } - fclose(z->f); - } - stb_arr_free(z->data); - stb_free(z); -} - -int stb_cfg_read(stb_cfg *z, char *key, void *value, int len) -{ - int i; - for (i=0; i < stb_arr_len(z->data); ++i) { - if (!stb_stricmp(z->data[i].key, key)) { - int n = stb_min(len, z->data[i].value_len); - memcpy(value, z->data[i].value, n); - if (n < len) - *((char *) value + n) = 0; - return 1; - } - } - return 0; -} - -void stb_cfg_write(stb_cfg *z, char *key, void *value, int len) -{ - int i; - for (i=0; i < stb_arr_len(z->data); ++i) - if (!stb_stricmp(z->data[i].key, key)) - break; - if (i == stb_arr_len(z->data)) { - stb__cfg_item p; - p.key = stb_strdup(key, z); - p.value = NULL; - p.value_len = 0; - stb_arr_push(z->data, p); - } - z->data[i].value = stb_malloc(z, len); - z->data[i].value_len = len; - memcpy(z->data[i].value, value, len); -} - -int stb_cfg_delete(stb_cfg *z, char *key) -{ - int i; - for (i=0; i < stb_arr_len(z->data); ++i) - if (!stb_stricmp(z->data[i].key, key)) { - stb_arr_fastdelete(z->data, i); - return 1; - } - return 0; -} - -int stb_cfg_read_string(stb_cfg *z, char *key, char *value, int len) -{ - if (!stb_cfg_read(z, key, value, len)) return 0; - value[len-1] = 0; - return 1; -} - -void stb_cfg_write_string(stb_cfg *z, char *key, char *value) -{ - stb_cfg_write(z, key, value, strlen(value)+1); -} -#endif - -////////////////////////////////////////////////////////////////////////////// -// -// stb_dirtree - load a description of a directory tree -// uses a cache and stat()s the directories for changes -// MUCH faster on NTFS, _wrong_ on FAT32, so should -// ignore the db on FAT32 - -#ifdef _WIN32 - -typedef struct -{ - char * path; // full path from passed-in root - time_t last_modified; - int num_files; -} stb_dirtree_dir; - -typedef struct -{ - char *name; // name relative to path - int dir; // index into dirs[] array - unsigned long size; // size, max 4GB - time_t last_modified; -} stb_dirtree_file; - -typedef struct -{ - stb_dirtree_dir *dirs; - stb_dirtree_file *files; - - // internal use - void * string_pool; // used to free data en masse -} stb_dirtree; - -extern void stb_dirtree_free ( stb_dirtree *d ); -extern stb_dirtree *stb_dirtree_get ( char *dir); -extern stb_dirtree *stb_dirtree_get_dir ( char *dir, char *cache_dir); -extern stb_dirtree *stb_dirtree_get_with_file ( char *dir, char *cache_file); - -// get a list of all the files recursively underneath 'dir' -// -// cache_file is used to store a copy of the directory tree to speed up -// later calls. It must be unique to 'dir' and the current working -// directory! Otherwise who knows what will happen (a good solution -// is to put it _in_ dir, but this API doesn't force that). -// -// Also, it might be possible to break this if you have two different processes -// do a call to stb_dirtree_get() with the same cache file at about the same -// time, but I _think_ it might just work. - - -#ifdef STB_DEFINE -static void stb__dirtree_add_dir(char *path, time_t last, stb_dirtree *active) -{ - stb_dirtree_dir d; - d.last_modified = last; - d.num_files = 0; - d.path = stb_strdup(path, active->string_pool); - stb_arr_push(active->dirs, d); -} - -static void stb__dirtree_add_file(char *name, int dir, unsigned long size, time_t last, stb_dirtree *active) -{ - stb_dirtree_file f; - f.dir = dir; - f.size = size; - f.last_modified = last; - f.name = stb_strdup(name, active->string_pool); - ++active->dirs[dir].num_files; - stb_arr_push(active->files, f); -} - -static char stb__signature[12] = { 's', 'T', 'b', 'D', 'i', 'R', 't', 'R', 'e', 'E', '0', '1' }; - -static void stb__dirtree_save_db(char *filename, stb_dirtree *data, char *root) -{ - int i, num_dirs_final=0, num_files_final; - int *remap; - FILE *f = fopen(filename, "wb"); - if (!f) return; - - fwrite(stb__signature, sizeof(stb__signature), 1, f); - fwrite(root, strlen(root)+1, 1, f); - // need to be slightly tricky and not write out NULLed directories, nor the root - - // build remapping table of all dirs we'll be writing out - remap = (int *) malloc(sizeof(remap[0]) * stb_arr_len(data->dirs)); - for (i=0; i < stb_arr_len(data->dirs); ++i) { - if (data->dirs[i].path == NULL || 0==stb_stricmp(data->dirs[i].path, root)) { - remap[i] = -1; - } else { - remap[i] = num_dirs_final++; - } - } - - fwrite(&num_dirs_final, 4, 1, f); - for (i=0; i < stb_arr_len(data->dirs); ++i) { - if (remap[i] >= 0) { - fwrite(&data->dirs[i].last_modified, 4, 1, f); - stb_fput_string(f, data->dirs[i].path); - } - } - - num_files_final = 0; - for (i=0; i < stb_arr_len(data->files); ++i) - if (remap[data->files[i].dir] >= 0) - ++num_files_final; - - fwrite(&num_files_final, 4, 1, f); - for (i=0; i < stb_arr_len(data->files); ++i) { - if (remap[data->files[i].dir] >= 0) { - stb_fput_ranged(f, remap[data->files[i].dir], 0, num_dirs_final); - stb_fput_varlenu(f, data->files[i].size); - fwrite(&data->files[i].last_modified, 4, 1, f); - stb_fput_string(f, data->files[i].name); - } - } - - fclose(f); -} - -// note: stomps any existing data, rather than appending -static void stb__dirtree_load_db(char *filename, stb_dirtree *data, char *dir) -{ - char sig[2048]; - int i,n; - FILE *f = fopen(filename, "rb"); - - if (!f) return; - - data->string_pool = stb_malloc(0,1); - - fread(sig, sizeof(stb__signature), 1, f); - if (memcmp(stb__signature, sig, sizeof(stb__signature))) { fclose(f); return; } - if (!fread(sig, strlen(dir)+1, 1, f)) { fclose(f); return; } - if (stb_stricmp(sig,dir)) { fclose(f); return; } - - // we can just read them straight in, because they're guaranteed to be valid - fread(&n, 4, 1, f); - stb_arr_setlen(data->dirs, n); - for(i=0; i < stb_arr_len(data->dirs); ++i) { - fread(&data->dirs[i].last_modified, 4, 1, f); - data->dirs[i].path = stb_fget_string(f, data->string_pool); - if (data->dirs[i].path == NULL) goto bail; - } - fread(&n, 4, 1, f); - stb_arr_setlen(data->files, n); - for (i=0; i < stb_arr_len(data->files); ++i) { - data->files[i].dir = stb_fget_ranged(f, 0, stb_arr_len(data->dirs)); - data->files[i].size = stb_fget_varlenu(f); - fread(&data->files[i].last_modified, 4, 1, f); - data->files[i].name = stb_fget_string(f, data->string_pool); - if (data->files[i].name == NULL) goto bail; - } - - if (0) { - bail: - stb_arr_free(data->dirs); - stb_arr_free(data->files); - } - fclose(f); -} - -static void stb__dirtree_scandir(char *path, time_t last_time, stb_dirtree *active) -{ - // this is dumb depth first; theoretically it might be faster - // to fully traverse each directory before visiting its children, - // but it's complicated and didn't seem like a gain in the test app - - int n; - - struct _wfinddata_t c_file; - #ifdef STB_PTR64 - intptr_t hFile; - #else - long hFile; - #endif - stb__wchar full_path[1024]; - int has_slash; - - has_slash = (path[0] && path[strlen(path)-1] == '/'); - if (has_slash) - swprintf((wchar_t *)full_path, L"%s*", stb__from_utf8(path)); - else - swprintf((wchar_t *)full_path, L"%s/*", stb__from_utf8(path)); - - // it's possible this directory is already present: that means it was in the - // cache, but its parent wasn't... in that case, we're done with it - for (n=0; n < stb_arr_len(active->dirs); ++n) - if (0 == stb_stricmp(active->dirs[n].path, path)) - return; - - // otherwise, we need to add it - stb__dirtree_add_dir(path, last_time, active); - n = stb_arr_lastn(active->dirs); - - if( (hFile = _wfindfirst((const wchar_t *)full_path, &c_file )) != -1L ) { - do { - if (c_file.attrib & _A_SUBDIR) { - // ignore subdirectories starting with '.', e.g. "." and ".." - if (c_file.name[0] != '.') { - char *new_path = (char *) full_path; - char *temp = stb__to_utf8((stb__wchar *)c_file.name); - if (has_slash) - sprintf(new_path, "%s%s", path, temp); - else - sprintf(new_path, "%s/%s", path, temp); - stb__dirtree_scandir(new_path, c_file.time_write, active); - } - } else { - char *temp = stb__to_utf8((stb__wchar *)c_file.name); - stb__dirtree_add_file(temp, n, c_file.size, c_file.time_write, active); - } - } while( _wfindnext( hFile, &c_file ) == 0 ); - - _findclose( hFile ); - } -} - -// scan the database and see if it's all valid -static int stb__dirtree_update_db(stb_dirtree *db, stb_dirtree *active) -{ - int changes_detected = STB_FALSE; - int i; - int *remap; - int *rescan=NULL; - remap = (int *) malloc(sizeof(remap[0]) * stb_arr_len(db->dirs)); - memset(remap, 0, sizeof(remap[0]) * stb_arr_len(db->dirs)); - rescan = NULL; - - for (i=0; i < stb_arr_len(db->dirs); ++i) { - struct _stat info; - if (0 == _stat(db->dirs[i].path, &info)) { - if (info.st_mode & _S_IFDIR) { - // it's still a directory, as expected - if (info.st_mtime > db->dirs[i].last_modified) { - // it's changed! force a rescan - // we don't want to scan it until we've stat()d its - // subdirs, though, so we queue it - stb_arr_push(rescan, i); - // update the last_mod time - db->dirs[i].last_modified = info.st_mtime; - // ignore existing files in this dir - remap[i] = -1; - changes_detected = STB_TRUE; - } else { - // it hasn't changed, just copy it through unchanged - stb__dirtree_add_dir(db->dirs[i].path, db->dirs[i].last_modified, active); - remap[i] = stb_arr_lastn(active->dirs); - } - } else { - // this path used to refer to a directory, but now it's a file! - // assume that the parent directory is going to be forced to rescan anyway - goto delete_entry; - } - } else { - delete_entry: - // directory no longer exists, so don't copy it - // we don't free it because it's in the string pool now - db->dirs[i].path = NULL; - remap[i] = -1; - changes_detected = STB_TRUE; - } - } - - // at this point, we have: - // - // holds a list of directory indices that need to be scanned due to being out of date - // holds the directory index in for each dir in , if it exists; -1 if not - // directories in are not in yet - - // so we can go ahead and remap all the known files right now - for (i=0; i < stb_arr_len(db->files); ++i) { - int dir = db->files[i].dir; - if (remap[dir] >= 0) { - stb__dirtree_add_file(db->files[i].name, remap[dir], db->files[i].size, db->files[i].last_modified, active); - } - } - - // at this point we're done with db->files, and done with remap - free(remap); - - // now scan those directories using the standard scan - for (i=0; i < stb_arr_len(rescan); ++i) { - int z = rescan[i]; - stb__dirtree_scandir(db->dirs[z].path, db->dirs[z].last_modified, active); - } - stb_arr_free(rescan); - - return changes_detected; -} - -static void stb__dirtree_free_raw(stb_dirtree *d) -{ - stb_free(d->string_pool); - stb_arr_free(d->dirs); - stb_arr_free(d->files); -} - -stb_dirtree *stb_dirtree_get_with_file(char *dir, char *cache_file) -{ - stb_dirtree *output = (stb_dirtree *) malloc(sizeof(*output)); - stb_dirtree db,active; - int prev_dir_count, cache_mismatch; - - char *stripped_dir; // store the directory name without a trailing '/' or '\\' - - // load the database of last-known state on disk - db.string_pool = NULL; - db.files = NULL; - db.dirs = NULL; - - stripped_dir = stb_strip_final_slash(strdup(dir)); - - if (cache_file != NULL) - stb__dirtree_load_db(cache_file, &db, stripped_dir); - - active.files = NULL; - active.dirs = NULL; - active.string_pool = stb_malloc(0,1); // @TODO: share string pools between both? - - // check all the directories in the database; make note if - // anything we scanned had changed, and rescan those things - cache_mismatch = stb__dirtree_update_db(&db, &active); - - // check the root tree - prev_dir_count = stb_arr_len(active.dirs); // record how many directories we've seen - - stb__dirtree_scandir(stripped_dir, 0, &active); // no last_modified time available for root - - // done with the DB; write it back out if any changes, i.e. either - // 1. any inconsistency found between cached information and actual disk - // or 2. if scanning the root found any new directories--which we detect because - // more than one directory got added to the active db during that scan - if (cache_mismatch || stb_arr_len(active.dirs) > prev_dir_count+1) - stb__dirtree_save_db(cache_file, &active, stripped_dir); - - free(stripped_dir); - - stb__dirtree_free_raw(&db); - - *output = active; - return output; -} - -stb_dirtree *stb_dirtree_get_dir(char *dir, char *cache_dir) -{ - int i; - stb_uint8 sha[20]; - char dir_lower[1024]; - char cache_file[1024],*s; - if (cache_dir == NULL) - return stb_dirtree_get_with_file(dir, NULL); - strcpy(dir_lower, dir); - stb_tolower(dir_lower); - stb_sha1(sha, (unsigned char *) dir_lower, strlen(dir_lower)); - strcpy(cache_file, cache_dir); - s = cache_file + strlen(cache_file); - if (s[-1] != '/' && s[-1] != '\\') *s++ = '/'; - strcpy(s, "dirtree_"); - s += strlen(s); - for (i=0; i < 8; ++i) { - char *hex = "0123456789abcdef"; - stb_uint z = sha[i]; - *s++ = hex[z >> 4]; - *s++ = hex[z & 15]; - } - strcpy(s, ".bin"); - return stb_dirtree_get_with_file(dir, cache_file); -} - -stb_dirtree *stb_dirtree_get(char *dir) -{ - char cache_dir[256]; - strcpy(cache_dir, "c:/stb"); - #ifdef STB_HAS_REGISTRY - { - void *reg = stb_reg_open("rHKLM", "Software\\SilverSpaceship\\stb"); - if (reg) { - stb_reg_read(reg, "dirtree", cache_dir, sizeof(cache_dir)); - stb_reg_close(reg); - } - } - #endif - return stb_dirtree_get_dir(dir, cache_dir); -} - -void stb_dirtree_free(stb_dirtree *d) -{ - stb__dirtree_free_raw(d); - free(d); -} -#endif // STB_DEFINE - -#endif // _WIN32 -#endif // STB_NO_STB_STRINGS - -////////////////////////////////////////////////////////////////////////////// -// -// STB_MALLOC_WRAPPER -// -// you can use the wrapper functions with your own malloc wrapper, -// or define STB_MALLOC_WRAPPER project-wide to have -// malloc/free/realloc/strdup all get vectored to it - -// this has too many very specific error messages you could google for and find in stb.h, -// so don't use it if they don't want any stb.h-identifiable strings -#if defined(STB_DEFINE) && !defined(STB_NO_STB_STRINGS) - -typedef struct -{ - void *p; - char *file; - int line; - int size; -} stb_malloc_record; - -#ifndef STB_MALLOC_HISTORY_COUNT -#define STB_MALLOC_HISTORY_COUNT 50 // 800 bytes -#endif - -stb_malloc_record *stb__allocations; -static int stb__alloc_size, stb__alloc_limit, stb__alloc_mask; -int stb__alloc_count; - -stb_malloc_record stb__alloc_history[STB_MALLOC_HISTORY_COUNT]; -int stb__history_pos; - -static int stb__hashfind(void *p) -{ - stb_uint32 h = stb_hashptr(p); - int s,n = h & stb__alloc_mask; - if (stb__allocations[n].p == p) - return n; - s = stb_rehash(h)|1; - for(;;) { - if (stb__allocations[n].p == NULL) - return -1; - n = (n+s) & stb__alloc_mask; - if (stb__allocations[n].p == p) - return n; - } -} - -int stb_wrapper_allocsize(void *p) -{ - int n = stb__hashfind(p); - if (n < 0) return 0; - return stb__allocations[n].size; -} - -static int stb__historyfind(void *p) -{ - int n = stb__history_pos; - int i; - for (i=0; i < STB_MALLOC_HISTORY_COUNT; ++i) { - if (--n < 0) n = STB_MALLOC_HISTORY_COUNT-1; - if (stb__alloc_history[n].p == p) - return n; - } - return -1; -} - -static void stb__add_alloc(void *p, int sz, char *file, int line); -static void stb__grow_alloc(void) -{ - int i,old_num = stb__alloc_size; - stb_malloc_record *old = stb__allocations; - if (stb__alloc_size == 0) - stb__alloc_size = 64; - else - stb__alloc_size *= 2; - - stb__allocations = (stb_malloc_record *) stb__realloc_raw(NULL, stb__alloc_size * sizeof(stb__allocations[0])); - if (stb__allocations == NULL) - stb_fatal("Internal error: couldn't grow malloc wrapper table"); - memset(stb__allocations, 0, stb__alloc_size * sizeof(stb__allocations[0])); - stb__alloc_limit = (stb__alloc_size*3)>>2; - stb__alloc_mask = stb__alloc_size-1; - - stb__alloc_count = 0; - - for (i=0; i < old_num; ++i) - if (old[i].p > STB_DEL) { - stb__add_alloc(old[i].p, old[i].size, old[i].file, old[i].line); - assert(stb__hashfind(old[i].p) >= 0); - } - for (i=0; i < old_num; ++i) - if (old[i].p > STB_DEL) - assert(stb__hashfind(old[i].p) >= 0); - stb__realloc_raw(old, 0); -} - -static void stb__add_alloc(void *p, int sz, char *file, int line) -{ - stb_uint32 h; - int n; - if (stb__alloc_count >= stb__alloc_limit) - stb__grow_alloc(); - h = stb_hashptr(p); - n = h & stb__alloc_mask; - if (stb__allocations[n].p > STB_DEL) { - int s = stb_rehash(h)|1; - do { - n = (n+s) & stb__alloc_mask; - } while (stb__allocations[n].p > STB_DEL); - } - assert(stb__allocations[n].p == NULL || stb__allocations[n].p == STB_DEL); - stb__allocations[n].p = p; - stb__allocations[n].size = sz; - stb__allocations[n].line = line; - stb__allocations[n].file = file; - ++stb__alloc_count; -} - -static void stb__remove_alloc(int n, char *file, int line) -{ - stb__alloc_history[stb__history_pos] = stb__allocations[n]; - stb__alloc_history[stb__history_pos].file = file; - stb__alloc_history[stb__history_pos].line = line; - if (++stb__history_pos == STB_MALLOC_HISTORY_COUNT) - stb__history_pos = 0; - stb__allocations[n].p = STB_DEL; - --stb__alloc_count; -} - -void stb_wrapper_malloc(void *p, int sz, char *file, int line) -{ - if (!p) return; - stb__add_alloc(p,sz,file,line); -} - -void stb_wrapper_free(void *p, char *file, int line) -{ - int n; - - if (p == NULL) return; - - n = stb__hashfind(p); - - if (n >= 0) - stb__remove_alloc(n, file, line); - else { - // tried to free something we hadn't allocated! - n = stb__historyfind(p); - assert(0); /* NOTREACHED */ - if (n >= 0) - stb_fatal("Attempted to free %d-byte block %p at %s:%d previously freed/realloced at %s:%d", - stb__alloc_history[n].size, p, - file, line, - stb__alloc_history[n].file, stb__alloc_history[n].line); - else - stb_fatal("Attempted to free unknown block %p at %s:%d", p, file,line); - } -} - -void stb_wrapper_check(void *p) -{ - int n; - - if (p == NULL) return; - - n = stb__hashfind(p); - - if (n >= 0) return; - - for (n=0; n < stb__alloc_size; ++n) - if (stb__allocations[n].p == p) - stb_fatal("Internal error: pointer %p was allocated, but hash search failed", p); - - // tried to free something that wasn't allocated! - n = stb__historyfind(p); - if (n >= 0) - stb_fatal("Checked %d-byte block %p previously freed/realloced at %s:%d", - stb__alloc_history[n].size, p, - stb__alloc_history[n].file, stb__alloc_history[n].line); - stb_fatal("Checked unknown block %p"); -} - -void stb_wrapper_realloc(void *p, void *q, int sz, char *file, int line) -{ - int n; - if (p == NULL) { stb_wrapper_malloc(q, sz, file, line); return; } - if (q == NULL) return; // nothing happened - - n = stb__hashfind(p); - if (n == -1) { - // tried to free something we hadn't allocated! - // this is weird, though, because we got past the realloc! - n = stb__historyfind(p); - assert(0); /* NOTREACHED */ - if (n >= 0) - stb_fatal("Attempted to realloc %d-byte block %p at %s:%d previously freed/realloced at %s:%d", - stb__alloc_history[n].size, p, - file, line, - stb__alloc_history[n].file, stb__alloc_history[n].line); - else - stb_fatal("Attempted to realloc unknown block %p at %s:%d", p, file,line); - } else { - if (q == p) { - stb__allocations[n].size = sz; - stb__allocations[n].file = file; - stb__allocations[n].line = line; - } else { - stb__remove_alloc(n, file, line); - stb__add_alloc(q,sz,file,line); - } - } -} - -void stb_wrapper_listall(void (*func)(void *ptr, int sz, char *file, int line)) -{ - int i; - for (i=0; i < stb__alloc_size; ++i) - if (stb__allocations[i].p > STB_DEL) - func(stb__allocations[i].p , stb__allocations[i].size, - stb__allocations[i].file, stb__allocations[i].line); -} - -void stb_wrapper_dump(char *filename) -{ - int i; - FILE *f = fopen(filename, "w"); - if (!f) return; - for (i=0; i < stb__alloc_size; ++i) - if (stb__allocations[i].p > STB_DEL) - fprintf(f, "%p %7d - %4d %s\n", - stb__allocations[i].p , stb__allocations[i].size, - stb__allocations[i].line, stb__allocations[i].file); -} -#endif // STB_DEFINE - - -////////////////////////////////////////////////////////////////////////////// -// -// stb_pointer_set -// -// -// For data structures that support querying by key, data structure -// classes always hand-wave away the issue of what to do if two entries -// have the same key: basically, store a linked list of all the nodes -// which have the same key (a LISP-style list). -// -// The thing is, it's not that trivial. If you have an O(log n) -// lookup data structure, but then n/4 items have the same value, -// you don't want to spend O(n) time scanning that list when -// deleting an item if you already have a pointer to the item. -// (You have to spend O(n) time enumerating all the items with -// a given key, sure, and you can't accelerate deleting a particular -// item if you only have the key, not a pointer to the item.) -// -// I'm going to call this data structure, whatever it turns out to -// be, a "pointer set", because we don't store any associated data for -// items in this data structure, we just answer the question of -// whether an item is in it or not (it's effectively one bit per pointer). -// Technically they don't have to be pointers; you could cast ints -// to (void *) if you want, but you can't store 0 or 1 because of the -// hash table. -// -// Since the fastest data structure we might want to add support for -// identical-keys to is a hash table with O(1)-ish lookup time, -// that means that the conceptual "linked list of all items with -// the same indexed value" that we build needs to have the same -// performance; that way when we index a table we think is arbitrary -// ints, but in fact half of them are 0, we don't get screwed. -// -// Therefore, it needs to be a hash table, at least when it gets -// large. On the other hand, when the data has totally arbitrary ints -// or floats, there won't be many collisions, and we'll have tons of -// 1-item bitmaps. That will be grossly inefficient as hash tables; -// trade-off; the hash table is reasonably efficient per-item when -// it's large, but not when it's small. So we need to do something -// Judy-like and use different strategies depending on the size. -// -// Like Judy, we'll use the bottom bit to encode the strategy: -// -// bottom bits: -// 00 - direct pointer -// 01 - 4-item bucket (16 bytes, no length, NULLs) -// 10 - N-item array -// 11 - hash table - -typedef struct stb_ps stb_ps; - -STB_EXTERN int stb_ps_find (stb_ps *ps, void *value); -STB_EXTERN stb_ps * stb_ps_add (stb_ps *ps, void *value); -STB_EXTERN stb_ps * stb_ps_remove(stb_ps *ps, void *value); -STB_EXTERN stb_ps * stb_ps_remove_any(stb_ps *ps, void **value); -STB_EXTERN void stb_ps_delete(stb_ps *ps); -STB_EXTERN int stb_ps_count (stb_ps *ps); - -STB_EXTERN stb_ps * stb_ps_copy (stb_ps *ps); -STB_EXTERN int stb_ps_subset(stb_ps *bigger, stb_ps *smaller); -STB_EXTERN int stb_ps_eq (stb_ps *p0, stb_ps *p1); - -STB_EXTERN void ** stb_ps_getlist (stb_ps *ps, int *count); -STB_EXTERN int stb_ps_writelist(stb_ps *ps, void **list, int size ); - -// enum and fastlist don't allocate storage, but you must consume the -// list before there's any chance the data structure gets screwed up; -STB_EXTERN int stb_ps_enum (stb_ps *ps, void *data, - int (*func)(void *value, void*data) ); -STB_EXTERN void ** stb_ps_fastlist(stb_ps *ps, int *count); -// result: -// returns a list, *count is the length of that list, -// but some entries of the list may be invalid; -// test with 'stb_ps_fastlist_valid(x)' - -#define stb_ps_fastlist_valid(x) ((stb_uinta) (x) > 1) - -#ifdef STB_DEFINE - -enum -{ - STB_ps_direct = 0, - STB_ps_bucket = 1, - STB_ps_array = 2, - STB_ps_hash = 3, -}; - -#define STB_BUCKET_SIZE 4 - -typedef struct -{ - void *p[STB_BUCKET_SIZE]; -} stb_ps_bucket; -#define GetBucket(p) ((stb_ps_bucket *) ((char *) (p) - STB_ps_bucket)) -#define EncodeBucket(p) ((stb_ps *) ((char *) (p) + STB_ps_bucket)) - -static void stb_bucket_free(stb_ps_bucket *b) -{ - free(b); -} - -static stb_ps_bucket *stb_bucket_create2(void *v0, void *v1) -{ - stb_ps_bucket *b = (stb_ps_bucket*) malloc(sizeof(*b)); - b->p[0] = v0; - b->p[1] = v1; - b->p[2] = NULL; - b->p[3] = NULL; - return b; -} - -static stb_ps_bucket * stb_bucket_create3(void **v) -{ - stb_ps_bucket *b = (stb_ps_bucket*) malloc(sizeof(*b)); - b->p[0] = v[0]; - b->p[1] = v[1]; - b->p[2] = v[2]; - b->p[3] = NULL; - return b; -} - - -// could use stb_arr, but this will save us memory -typedef struct -{ - int count; - void *p[1]; -} stb_ps_array; -#define GetArray(p) ((stb_ps_array *) ((char *) (p) - STB_ps_array)) -#define EncodeArray(p) ((stb_ps *) ((char *) (p) + STB_ps_array)) - -static int stb_ps_array_max = 13; - -typedef struct -{ - int size, mask; - int count, count_deletes; - int grow_threshhold; - int shrink_threshhold; - int rehash_threshhold; - int any_offset; - void *table[1]; -} stb_ps_hash; -#define GetHash(p) ((stb_ps_hash *) ((char *) (p) - STB_ps_hash)) -#define EncodeHash(p) ((stb_ps *) ((char *) (p) + STB_ps_hash)) - -#define stb_ps_empty(v) (((stb_uint32) v) <= 1) - -static stb_ps_hash *stb_ps_makehash(int size, int old_size, void **old_data) -{ - int i; - stb_ps_hash *h = (stb_ps_hash *) malloc(sizeof(*h) + (size-1) * sizeof(h->table[0])); - assert(stb_is_pow2(size)); - h->size = size; - h->mask = size-1; - h->shrink_threshhold = (int) (0.3f * size); - h-> grow_threshhold = (int) (0.8f * size); - h->rehash_threshhold = (int) (0.9f * size); - h->count = 0; - h->count_deletes = 0; - h->any_offset = 0; - memset(h->table, 0, size * sizeof(h->table[0])); - for (i=0; i < old_size; ++i) - if (!stb_ps_empty(old_data[i])) - stb_ps_add(EncodeHash(h), old_data[i]); - return h; -} - -void stb_ps_delete(stb_ps *ps) -{ - switch (3 & (int) ps) { - case STB_ps_direct: break; - case STB_ps_bucket: stb_bucket_free(GetBucket(ps)); break; - case STB_ps_array : free(GetArray(ps)); break; - case STB_ps_hash : free(GetHash(ps)); break; - } -} - -stb_ps *stb_ps_copy(stb_ps *ps) -{ - int i; - // not a switch: order based on expected performance/power-law distribution - switch (3 & (int) ps) { - case STB_ps_direct: return ps; - case STB_ps_bucket: { - stb_ps_bucket *n = (stb_ps_bucket *) malloc(sizeof(*n)); - *n = *GetBucket(ps); - return EncodeBucket(n); - } - case STB_ps_array: { - stb_ps_array *a = GetArray(ps); - stb_ps_array *n = (stb_ps_array *) malloc(sizeof(*n) + stb_ps_array_max * sizeof(n->p[0])); - n->count = a->count; - for (i=0; i < a->count; ++i) - n->p[i] = a->p[i]; - return EncodeArray(n); - } - case STB_ps_hash: { - stb_ps_hash *h = GetHash(ps); - stb_ps_hash *n = stb_ps_makehash(h->size, h->size, h->table); - return EncodeHash(n); - } - } - assert(0); /* NOTREACHED */ - return NULL; -} - -int stb_ps_find(stb_ps *ps, void *value) -{ - int i, code = 3 & (int) ps; - assert((3 & (int) value) == STB_ps_direct); - assert(stb_ps_fastlist_valid(value)); - // not a switch: order based on expected performance/power-law distribution - if (code == STB_ps_direct) - return value == ps; - if (code == STB_ps_bucket) { - stb_ps_bucket *b = GetBucket(ps); - assert(STB_BUCKET_SIZE == 4); - if (b->p[0] == value || b->p[1] == value || - b->p[2] == value || b->p[3] == value) - return STB_TRUE; - return STB_FALSE; - } - if (code == STB_ps_array) { - stb_ps_array *a = GetArray(ps); - for (i=0; i < a->count; ++i) - if (a->p[i] == value) - return STB_TRUE; - return STB_FALSE; - } else { - stb_ps_hash *h = GetHash(ps); - stb_uint32 hash = stb_hashptr(value); - stb_uint32 s, n = hash & h->mask; - void **t = h->table; - if (t[n] == value) return STB_TRUE; - if (t[n] == NULL) return STB_FALSE; - s = stb_rehash(hash) | 1; - do { - n = (n + s) & h->mask; - if (t[n] == value) return STB_TRUE; - } while (t[n] != NULL); - return STB_FALSE; - } -} - -stb_ps * stb_ps_add (stb_ps *ps, void *value) -{ - #ifdef STB_DEBUG - assert(!stb_ps_find(ps,value)); - #endif - if (value == NULL) return ps; // ignore NULL adds to avoid bad breakage - assert((3 & (int) value) == STB_ps_direct); - assert(stb_ps_fastlist_valid(value)); - assert(value != STB_DEL); // STB_DEL is less likely - - switch (3 & (int) ps) { - case STB_ps_direct: - if (ps == NULL) return (stb_ps *) value; - return EncodeBucket(stb_bucket_create2(ps,value)); - - case STB_ps_bucket: { - stb_ps_bucket *b = GetBucket(ps); - stb_ps_array *a; - assert(STB_BUCKET_SIZE == 4); - if (b->p[0] == NULL) { b->p[0] = value; return ps; } - if (b->p[1] == NULL) { b->p[1] = value; return ps; } - if (b->p[2] == NULL) { b->p[2] = value; return ps; } - if (b->p[3] == NULL) { b->p[3] = value; return ps; } - a = (stb_ps_array *) malloc(sizeof(*a) + 7 * sizeof(a->p[0])); // 8 slots, must be 2^k - memcpy(a->p, b, sizeof(*b)); - a->p[4] = value; - a->count = 5; - stb_bucket_free(b); - return EncodeArray(a); - } - - case STB_ps_array: { - stb_ps_array *a = GetArray(ps); - if (a->count == stb_ps_array_max) { - // promote from array to hash - stb_ps_hash *h = stb_ps_makehash(2 << stb_log2_ceil(a->count), a->count, a->p); - free(a); - return stb_ps_add(EncodeHash(h), value); - } - // do we need to resize the array? the array doubles in size when it - // crosses a power-of-two - if ((a->count & (a->count-1))==0) { - int newsize = a->count*2; - // clamp newsize to max if: - // 1. it's larger than max - // 2. newsize*1.5 is larger than max (to avoid extra resizing) - if (newsize + a->count > stb_ps_array_max) - newsize = stb_ps_array_max; - a = (stb_ps_array *) realloc(a, sizeof(*a) + (newsize-1) * sizeof(a->p[0])); - } - a->p[a->count++] = value; - return EncodeArray(a); - } - case STB_ps_hash: { - stb_ps_hash *h = GetHash(ps); - stb_uint32 hash = stb_hashptr(value); - stb_uint32 n = hash & h->mask; - void **t = h->table; - // find first NULL or STB_DEL entry - if (!stb_ps_empty(t[n])) { - stb_uint32 s = stb_rehash(hash) | 1; - do { - n = (n + s) & h->mask; - } while (!stb_ps_empty(t[n])); - } - if (t[n] == STB_DEL) - -- h->count_deletes; - t[n] = value; - ++ h->count; - if (h->count == h->grow_threshhold) { - stb_ps_hash *h2 = stb_ps_makehash(h->size*2, h->size, t); - free(h); - return EncodeHash(h2); - } - if (h->count + h->count_deletes == h->rehash_threshhold) { - stb_ps_hash *h2 = stb_ps_makehash(h->size, h->size, t); - free(h); - return EncodeHash(h2); - } - return ps; - } - } - return NULL; /* NOTREACHED */ -} - -stb_ps *stb_ps_remove(stb_ps *ps, void *value) -{ - #ifdef STB_DEBUG - assert(stb_ps_find(ps, value)); - #endif - assert((3 & (int) value) == STB_ps_direct); - if (value == NULL) return ps; // ignore NULL removes to avoid bad breakage - switch (3 & (int) ps) { - case STB_ps_direct: - return ps == value ? NULL : ps; - case STB_ps_bucket: { - stb_ps_bucket *b = GetBucket(ps); - int count=0; - assert(STB_BUCKET_SIZE == 4); - if (b->p[0] == value) b->p[0] = NULL; else count += (b->p[0] != NULL); - if (b->p[1] == value) b->p[1] = NULL; else count += (b->p[1] != NULL); - if (b->p[2] == value) b->p[2] = NULL; else count += (b->p[2] != NULL); - if (b->p[3] == value) b->p[3] = NULL; else count += (b->p[3] != NULL); - if (count == 1) { // shrink bucket at size 1 - value = b->p[0]; - if (value == NULL) value = b->p[1]; - if (value == NULL) value = b->p[2]; - if (value == NULL) value = b->p[3]; - assert(value != NULL); - stb_bucket_free(b); - return (stb_ps *) value; // return STB_ps_direct of value - } - return ps; - } - case STB_ps_array: { - stb_ps_array *a = GetArray(ps); - int i; - for (i=0; i < a->count; ++i) { - if (a->p[i] == value) { - a->p[i] = a->p[--a->count]; - if (a->count == 3) { // shrink to bucket! - stb_ps_bucket *b = stb_bucket_create3(a->p); - free(a); - return EncodeBucket(b); - } - return ps; - } - } - return ps; - } - case STB_ps_hash: { - stb_ps_hash *h = GetHash(ps); - stb_uint32 hash = stb_hashptr(value); - stb_uint32 s, n = hash & h->mask; - void **t = h->table; - if (t[n] != value) { - s = stb_rehash(hash) | 1; - do { - n = (n + s) & h->mask; - } while (t[n] != value); - } - t[n] = STB_DEL; - -- h->count; - ++ h->count_deletes; - // should we shrink down to an array? - if (h->count < stb_ps_array_max) { - int n = 1 << stb_log2_floor(stb_ps_array_max); - if (h->count < n) { - stb_ps_array *a = (stb_ps_array *) malloc(sizeof(*a) + (n-1) * sizeof(a->p[0])); - int i,j=0; - for (i=0; i < h->size; ++i) - if (!stb_ps_empty(t[i])) - a->p[j++] = t[i]; - assert(j == h->count); - a->count = j; - free(h); - return EncodeArray(a); - } - } - if (h->count == h->shrink_threshhold) { - stb_ps_hash *h2 = stb_ps_makehash(h->size >> 1, h->size, t); - free(h); - return EncodeHash(h2); - } - return ps; - } - } - return ps; /* NOTREACHED */ -} - -stb_ps *stb_ps_remove_any(stb_ps *ps, void **value) -{ - assert(ps != NULL); - switch (3 & (int) ps) { - case STB_ps_direct: - *value = ps; - return NULL; - case STB_ps_bucket: { - stb_ps_bucket *b = GetBucket(ps); - int count=0, slast=0, last=0; - assert(STB_BUCKET_SIZE == 4); - if (b->p[0]) { ++count; last = 0; } - if (b->p[1]) { ++count; slast = last; last = 1; } - if (b->p[2]) { ++count; slast = last; last = 2; } - if (b->p[3]) { ++count; slast = last; last = 3; } - *value = b->p[last]; - b->p[last] = 0; - if (count == 2) { - void *leftover = b->p[slast]; // second to last - stb_bucket_free(b); - return (stb_ps *) leftover; - } - return ps; - } - case STB_ps_array: { - stb_ps_array *a = GetArray(ps); - *value = a->p[a->count-1]; - if (a->count == 4) - return stb_ps_remove(ps, *value); - --a->count; - return ps; - } - case STB_ps_hash: { - stb_ps_hash *h = GetHash(ps); - void **t = h->table; - stb_uint32 n = h->any_offset; - while (stb_ps_empty(t[n])) - n = (n + 1) & h->mask; - *value = t[n]; - h->any_offset = (n+1) & h->mask; - // check if we need to skip down to the previous type - if (h->count-1 < stb_ps_array_max || h->count-1 == h->shrink_threshhold) - return stb_ps_remove(ps, *value); - t[n] = STB_DEL; - -- h->count; - ++ h->count_deletes; - return ps; - } - } - return ps; /* NOTREACHED */ -} - - -void ** stb_ps_getlist(stb_ps *ps, int *count) -{ - int i,n=0; - void **p = NULL; - switch (3 & (int) ps) { - case STB_ps_direct: - if (ps == NULL) { *count = 0; return NULL; } - p = (void **) malloc(sizeof(*p) * 1); - p[0] = ps; - *count = 1; - return p; - case STB_ps_bucket: { - stb_ps_bucket *b = GetBucket(ps); - p = (void **) malloc(sizeof(*p) * STB_BUCKET_SIZE); - for (i=0; i < STB_BUCKET_SIZE; ++i) - if (b->p[i] != NULL) - p[n++] = b->p[i]; - break; - } - case STB_ps_array: { - stb_ps_array *a = GetArray(ps); - p = (void **) malloc(sizeof(*p) * a->count); - memcpy(p, a->p, sizeof(*p) * a->count); - *count = a->count; - return p; - } - case STB_ps_hash: { - stb_ps_hash *h = GetHash(ps); - p = (void **) malloc(sizeof(*p) * h->count); - for (i=0; i < h->size; ++i) - if (!stb_ps_empty(h->table[i])) - p[n++] = h->table[i]; - break; - } - } - *count = n; - return p; -} - -int stb_ps_writelist(stb_ps *ps, void **list, int size ) -{ - int i,n=0; - switch (3 & (int) ps) { - case STB_ps_direct: - if (ps == NULL || size <= 0) return 0; - list[0] = ps; - return 1; - case STB_ps_bucket: { - stb_ps_bucket *b = GetBucket(ps); - for (i=0; i < STB_BUCKET_SIZE; ++i) - if (b->p[i] != NULL && n < size) - list[n++] = b->p[i]; - return n; - } - case STB_ps_array: { - stb_ps_array *a = GetArray(ps); - n = stb_min(size, a->count); - memcpy(list, a->p, sizeof(*list) * n); - return n; - } - case STB_ps_hash: { - stb_ps_hash *h = GetHash(ps); - if (size <= 0) return 0; - for (i=0; i < h->count; ++i) { - if (!stb_ps_empty(h->table[i])) { - list[n++] = h->table[i]; - if (n == size) break; - } - } - return n; - } - } - return 0; /* NOTREACHED */ -} - -int stb_ps_enum(stb_ps *ps, void *data, int (*func)(void *value, void *data)) -{ - int i; - switch (3 & (int) ps) { - case STB_ps_direct: - if (ps == NULL) return STB_TRUE; - return func(ps, data); - case STB_ps_bucket: { - stb_ps_bucket *b = GetBucket(ps); - for (i=0; i < STB_BUCKET_SIZE; ++i) - if (b->p[i] != NULL) - if (!func(b->p[i], data)) - return STB_FALSE; - return STB_TRUE; - } - case STB_ps_array: { - stb_ps_array *a = GetArray(ps); - for (i=0; i < a->count; ++i) - if (!func(a->p[i], data)) - return STB_FALSE; - return STB_TRUE; - } - case STB_ps_hash: { - stb_ps_hash *h = GetHash(ps); - for (i=0; i < h->count; ++i) - if (!stb_ps_empty(h->table[i])) - if (!func(h->table[i], data)) - return STB_FALSE; - return STB_TRUE; - } - } - return STB_TRUE; /* NOTREACHED */ -} - -int stb_ps_count (stb_ps *ps) -{ - switch (3 & (int) ps) { - case STB_ps_direct: - return ps != NULL; - case STB_ps_bucket: { - stb_ps_bucket *b = GetBucket(ps); - return (b->p[0] != NULL) + (b->p[1] != NULL) + - (b->p[2] != NULL) + (b->p[3] != NULL); - } - case STB_ps_array: { - stb_ps_array *a = GetArray(ps); - return a->count; - } - case STB_ps_hash: { - stb_ps_hash *h = GetHash(ps); - return h->count; - } - } - return 0; -} - -void ** stb_ps_fastlist(stb_ps *ps, int *count) -{ - static void *storage; - - switch (3 & (int) ps) { - case STB_ps_direct: - if (ps == NULL) { *count = 0; return NULL; } - storage = ps; - *count = 1; - return &storage; - case STB_ps_bucket: { - stb_ps_bucket *b = GetBucket(ps); - *count = STB_BUCKET_SIZE; - return b->p; - } - case STB_ps_array: { - stb_ps_array *a = GetArray(ps); - *count = a->count; - return a->p; - } - case STB_ps_hash: { - stb_ps_hash *h = GetHash(ps); - *count = h->size; - return h->table; - } - } - return NULL; /* NOTREACHED */ -} - -int stb_ps_subset(stb_ps *bigger, stb_ps *smaller) -{ - int i, listlen; - void **list = stb_ps_fastlist(smaller, &listlen); - for(i=0; i < listlen; ++i) - if (stb_ps_fastlist_valid(list[i])) - if (!stb_ps_find(bigger, list[i])) - return 0; - return 1; -} - -int stb_ps_eq(stb_ps *p0, stb_ps *p1) -{ - if (stb_ps_count(p0) != stb_ps_count(p1)) - return 0; - return stb_ps_subset(p0, p1); -} - -#undef GetBucket -#undef GetArray -#undef GetHash - -#undef EncodeBucket -#undef EncodeArray -#undef EncodeHash - -#endif - - -////////////////////////////////////////////////////////////////////////////// -// -// Random Numbers via Meresenne Twister or LCG -// - -STB_EXTERN unsigned long stb_srandLCG(unsigned long seed); -STB_EXTERN unsigned long stb_randLCG(void); -STB_EXTERN double stb_frandLCG(void); - -STB_EXTERN void stb_srand(unsigned long seed); -STB_EXTERN unsigned long stb_rand(void); -STB_EXTERN double stb_frand(void); -STB_EXTERN void stb_shuffle(void *p, size_t n, size_t sz, - unsigned long seed); -STB_EXTERN void stb_reverse(void *p, size_t n, size_t sz); - -STB_EXTERN unsigned long stb_randLCG_explicit(unsigned long seed); - -#define stb_rand_define(x,y) \ - \ - unsigned long x(void) \ - { \ - static unsigned long stb__rand = y; \ - stb__rand = stb__rand * 2147001325 + 715136305; /* BCPL */ \ - return 0x31415926 ^ ((stb__rand >> 16) + (stb__rand << 16)); \ - } - -#ifdef STB_DEFINE -unsigned long stb_randLCG_explicit(unsigned long seed) -{ - return seed * 2147001325 + 715136305; -} - -static unsigned long stb__rand_seed=0; - -unsigned long stb_srandLCG(unsigned long seed) -{ - unsigned long previous = stb__rand_seed; - stb__rand_seed = seed; - return previous; -} - -unsigned long stb_randLCG(void) -{ - stb__rand_seed = stb__rand_seed * 2147001325 + 715136305; // BCPL generator - // shuffle non-random bits to the middle, and xor to decorrelate with seed - return 0x31415926 ^ ((stb__rand_seed >> 16) + (stb__rand_seed << 16)); -} - -double stb_frandLCG(void) -{ - return stb_randLCG() / ((double) (1 << 16) * (1 << 16)); -} - -void stb_shuffle(void *p, size_t n, size_t sz, unsigned long seed) -{ - char *a; - unsigned long old_seed; - int i; - if (seed) - old_seed = stb_srandLCG(seed); - a = (char *) p + (n-1) * sz; - - for (i=n; i > 1; --i) { - int j = stb_randLCG() % i; - stb_swap(a, (char *) p + j * sz, sz); - a -= sz; - } - if (seed) - stb_srandLCG(old_seed); -} - -void stb_reverse(void *p, size_t n, size_t sz) -{ - int i,j = n-1; - for (i=0; i < j; ++i,--j) { - stb_swap((char *) p + i * sz, (char *) p + j * sz, sz); - } -} - -// public domain Mersenne Twister by Michael Brundage -#define STB__MT_LEN 624 - -int stb__mt_index = STB__MT_LEN*sizeof(unsigned long)+1; -unsigned long stb__mt_buffer[STB__MT_LEN]; - -void stb_srand(unsigned long seed) -{ - int i; - unsigned long old = stb_srandLCG(seed); - for (i = 0; i < STB__MT_LEN; i++) - stb__mt_buffer[i] = stb_randLCG(); - stb_srandLCG(old); - stb__mt_index = STB__MT_LEN*sizeof(unsigned long); -} - -#define STB__MT_IA 397 -#define STB__MT_IB (STB__MT_LEN - STB__MT_IA) -#define STB__UPPER_MASK 0x80000000 -#define STB__LOWER_MASK 0x7FFFFFFF -#define STB__MATRIX_A 0x9908B0DF -#define STB__TWIST(b,i,j) ((b)[i] & STB__UPPER_MASK) | ((b)[j] & STB__LOWER_MASK) -#define STB__MAGIC(s) (((s)&1)*STB__MATRIX_A) - -unsigned long stb_rand() -{ - unsigned long * b = stb__mt_buffer; - int idx = stb__mt_index; - unsigned long s,r; - int i; - - if (idx >= STB__MT_LEN*sizeof(unsigned long)) { - if (idx > STB__MT_LEN*sizeof(unsigned long)) - stb_srand(0); - idx = 0; - i = 0; - for (; i < STB__MT_IB; i++) { - s = STB__TWIST(b, i, i+1); - b[i] = b[i + STB__MT_IA] ^ (s >> 1) ^ STB__MAGIC(s); - } - for (; i < STB__MT_LEN-1; i++) { - s = STB__TWIST(b, i, i+1); - b[i] = b[i - STB__MT_IB] ^ (s >> 1) ^ STB__MAGIC(s); - } - - s = STB__TWIST(b, STB__MT_LEN-1, 0); - b[STB__MT_LEN-1] = b[STB__MT_IA-1] ^ (s >> 1) ^ STB__MAGIC(s); - } - stb__mt_index = idx + sizeof(unsigned long); - - r = *(unsigned long *)((unsigned char *)b + idx); - - r ^= (r >> 11); - r ^= (r << 7) & 0x9D2C5680; - r ^= (r << 15) & 0xEFC60000; - r ^= (r >> 18); - - return r; -} - -double stb_frand(void) -{ - return stb_rand() / ((double) (1 << 16) * (1 << 16)); -} - -#endif - - -////////////////////////////////////////////////////////////////////////////// -// -// stb_dupe -// -// stb_dupe is a duplicate-finding system for very, very large data -// structures--large enough that sorting is too slow, but not so large -// that we can't keep all the data in memory. using it works as follows: -// -// 1. create an stb_dupe: -// provide a hash function -// provide an equality function -// provide an estimate for the size -// optionally provide a comparison function -// -// 2. traverse your data, 'adding' pointers to the stb_dupe -// -// 3. finish and ask for duplicates -// -// the stb_dupe will discard its intermediate data and build -// a collection of sorted lists of duplicates, with non-duplicate -// entries omitted entirely -// -// -// Implementation strategy: -// -// while collecting the N items, we keep a hash table of approximate -// size sqrt(N). (if you tell use the N up front, the hash table is -// just that size exactly) -// -// each entry in the hash table is just an stb__arr of pointers (no need -// to use stb_ps, because we don't need to delete from these) -// -// for step 3, for each entry in the hash table, we apply stb_dupe to it -// recursively. once the size gets small enough (or doesn't decrease -// significantly), we switch to either using qsort() on the comparison -// function, or else we just do the icky N^2 gather - - -typedef struct stb_dupe stb_dupe; - -typedef int (*stb_compare_func)(void *a, void *b); -typedef int (*stb_hash_func)(void *a, unsigned int seed); - -STB_EXTERN void stb_dupe_free(stb_dupe *sd); -STB_EXTERN stb_dupe *stb_dupe_create(stb_hash_func hash, - stb_compare_func eq, int size, stb_compare_func ineq); -STB_EXTERN void stb_dupe_add(stb_dupe *sd, void *item); -STB_EXTERN void stb_dupe_finish(stb_dupe *sd); -STB_EXTERN int stb_dupe_numsets(stb_dupe *sd); -STB_EXTERN void **stb_dupe_set(stb_dupe *sd, int num); -STB_EXTERN int stb_dupe_set_count(stb_dupe *sd, int num); - -struct stb_dupe -{ - void ***hash_table; - int hash_size; - int size_log2; - int population; - - int hash_shift; - stb_hash_func hash; - - stb_compare_func eq; - stb_compare_func ineq; - - void ***dupes; -}; - -#ifdef STB_DEFINE - -int stb_dupe_numsets(stb_dupe *sd) -{ - assert(sd->hash_table == NULL); - return stb_arr_len(sd->dupes); -} - -void **stb_dupe_set(stb_dupe *sd, int num) -{ - assert(sd->hash_table == NULL); - return sd->dupes[num]; -} - -int stb_dupe_set_count(stb_dupe *sd, int num) -{ - assert(sd->hash_table == NULL); - return stb_arr_len(sd->dupes[num]); -} - -stb_dupe *stb_dupe_create(stb_hash_func hash, stb_compare_func eq, int size, - stb_compare_func ineq) -{ - int i, hsize; - stb_dupe *sd = (stb_dupe *) malloc(sizeof(*sd)); - - sd->size_log2 = 4; - hsize = 1 << sd->size_log2; - while (hsize * hsize < size) { - ++sd->size_log2; - hsize *= 2; - } - - sd->hash = hash; - sd->eq = eq; - sd->ineq = ineq; - sd->hash_shift = 0; - - sd->population = 0; - sd->hash_size = hsize; - sd->hash_table = (void ***) malloc(sizeof(*sd->hash_table) * hsize); - for (i=0; i < hsize; ++i) - sd->hash_table[i] = NULL; - - sd->dupes = NULL; - - return sd; -} - -void stb_dupe_add(stb_dupe *sd, void *item) -{ - stb_uint32 hash = sd->hash(item, sd->hash_shift); - int z = hash & (sd->hash_size-1); - stb_arr_push(sd->hash_table[z], item); - ++sd->population; -} - -void stb_dupe_free(stb_dupe *sd) -{ - int i; - for (i=0; i < stb_arr_len(sd->dupes); ++i) - if (sd->dupes[i]) - stb_arr_free(sd->dupes[i]); - stb_arr_free(sd->dupes); - free(sd); -} - -static stb_compare_func stb__compare; - -static int stb__dupe_compare(const void *a, const void *b) -{ - void *p = *(void **) a; - void *q = *(void **) b; - - return stb__compare(p,q); -} - -void stb_dupe_finish(stb_dupe *sd) -{ - int i,j,k; - assert(sd->dupes == NULL); - for (i=0; i < sd->hash_size; ++i) { - void ** list = sd->hash_table[i]; - if (list != NULL) { - int n = stb_arr_len(list); - // @TODO: measure to find good numbers instead of just making them up! - int thresh = (sd->ineq ? 200 : 20); - // if n is large enough to be worth it, and n is smaller than - // before (so we can guarantee we'll use a smaller hash table); - // and there are enough hash bits left, assuming full 32-bit hash - if (n > thresh && n < (sd->population >> 3) && sd->hash_shift + sd->size_log2*2 < 32) { - - // recursively process this row using stb_dupe, O(N log log N) - - stb_dupe *d = stb_dupe_create(sd->hash, sd->eq, n, sd->ineq); - d->hash_shift = stb_randLCG_explicit(sd->hash_shift); - for (j=0; j < n; ++j) - stb_dupe_add(d, list[j]); - stb_arr_free(sd->hash_table[i]); - stb_dupe_finish(d); - for (j=0; j < stb_arr_len(d->dupes); ++j) { - stb_arr_push(sd->dupes, d->dupes[j]); - d->dupes[j] = NULL; // take over ownership - } - stb_dupe_free(d); - - } else if (sd->ineq) { - - // process this row using qsort(), O(N log N) - stb__compare = sd->ineq; - qsort(list, n, sizeof(list[0]), stb__dupe_compare); - - // find equal subsequences of the list - for (j=0; j < n-1; ) { - // find a subsequence from j..k - for (k=j; k < n; ++k) - // only use ineq so eq can be left undefined - if (sd->ineq(list[j], list[k])) - break; - // k is the first one not in the subsequence - if (k-j > 1) { - void **mylist = NULL; - stb_arr_setlen(mylist, k-j); - memcpy(mylist, list+j, sizeof(list[j]) * (k-j)); - stb_arr_push(sd->dupes, mylist); - } - j = k; - } - stb_arr_free(sd->hash_table[i]); - } else { - - // process this row using eq(), O(N^2) - for (j=0; j < n; ++j) { - if (list[j] != NULL) { - void **output = NULL; - for (k=j+1; k < n; ++k) { - if (sd->eq(list[j], list[k])) { - if (output == NULL) - stb_arr_push(output, list[j]); - stb_arr_push(output, list[k]); - list[k] = NULL; - } - } - list[j] = NULL; - if (output) - stb_arr_push(sd->dupes, output); - } - } - stb_arr_free(sd->hash_table[i]); - } - } - } - free(sd->hash_table); - sd->hash_table = NULL; -} -#endif - -////////////////////////////////////////////////////////////////////////////// -// -// templatized Sort routine -// -// This is an attempt to implement a templated sorting algorithm. -// To use it, you have to explicitly instantiate it as a _function_, -// then you call that function. This allows the comparison to be inlined, -// giving the sort similar performance to C++ sorts. -// -// It implements quicksort with three-way-median partitioning (generally -// well-behaved), with a final insertion sort pass. -// -// When you define the compare expression, you should assume you have -// elements of your array pointed to by 'a' and 'b', and perform the comparison -// on those. OR you can use one or more statements; first say '0;', then -// write whatever code you want, and compute the result into a variable 'c'. - -#define stb_declare_sort(FUNCNAME, TYPE) \ - void FUNCNAME(TYPE *p, int n) -#define stb_define_sort(FUNCNAME,TYPE,COMPARE) \ - stb__define_sort( void, FUNCNAME,TYPE,COMPARE) -#define stb_define_sort_static(FUNCNAME,TYPE,COMPARE) \ - stb__define_sort(static void, FUNCNAME,TYPE,COMPARE) - -#define stb__define_sort(MODE, FUNCNAME, TYPE, COMPARE) \ - \ -static void STB_(FUNCNAME,_ins_sort)(TYPE *p, int n) \ -{ \ - int i,j; \ - for (i=1; i < n; ++i) { \ - TYPE t = p[i], *a = &t; \ - j = i; \ - while (j > 0) { \ - TYPE *b = &p[j-1]; \ - int c = COMPARE; \ - if (!c) break; \ - p[j] = p[j-1]; \ - --j; \ - } \ - if (i != j) \ - p[j] = t; \ - } \ -} \ - \ -static void STB_(FUNCNAME,_quicksort)(TYPE *p, int n) \ -{ \ - /* threshhold for transitioning to insertion sort */ \ - while (n > 12) { \ - TYPE *a,*b,t; \ - int c01,c12,c,m,i,j; \ - \ - /* compute median of three */ \ - m = n >> 1; \ - a = &p[0]; \ - b = &p[m]; \ - c = COMPARE; \ - c01 = c; \ - a = &p[m]; \ - b = &p[n-1]; \ - c = COMPARE; \ - c12 = c; \ - /* if 0 >= mid >= end, or 0 < mid < end, then use mid */ \ - if (c01 != c12) { \ - /* otherwise, we'll need to swap something else to middle */ \ - int z; \ - a = &p[0]; \ - b = &p[n-1]; \ - c = COMPARE; \ - /* 0>mid && midn => n; 0 0 */ \ - /* 0n: 0>n => 0; 0 n */ \ - z = (c == c12) ? 0 : n-1; \ - t = p[z]; \ - p[z] = p[m]; \ - p[m] = t; \ - } \ - /* now p[m] is the median-of-three */ \ - /* swap it to the beginning so it won't move around */ \ - t = p[0]; \ - p[0] = p[m]; \ - p[m] = t; \ - \ - /* partition loop */ \ - i=1; \ - j=n-1; \ - for(;;) { \ - /* handling of equality is crucial here */ \ - /* for sentinels & efficiency with duplicates */ \ - b = &p[0]; \ - for (;;++i) { \ - a=&p[i]; \ - c = COMPARE; \ - if (!c) break; \ - } \ - a = &p[0]; \ - for (;;--j) { \ - b=&p[j]; \ - c = COMPARE; \ - if (!c) break; \ - } \ - /* make sure we haven't crossed */ \ - if (i >= j) break; \ - t = p[i]; \ - p[i] = p[j]; \ - p[j] = t; \ - \ - ++i; \ - --j; \ - } \ - /* recurse on smaller side, iterate on larger */ \ - if (j < (n-i)) { \ - STB_(FUNCNAME,_quicksort)(p,j); \ - p = p+i; \ - n = n-i; \ - } else { \ - STB_(FUNCNAME,_quicksort)(p+i, n-i); \ - n = j; \ - } \ - } \ -} \ - \ -MODE FUNCNAME(TYPE *p, int n) \ -{ \ - STB_(FUNCNAME, _quicksort)(p, n); \ - STB_(FUNCNAME, _ins_sort)(p, n); \ -} \ - - -////////////////////////////////////////////////////////////////////////////// -// -// stb_bitset an array of booleans indexed by integers -// - -typedef stb_uint32 stb_bitset; - -STB_EXTERN stb_bitset *stb_bitset_new(int value, int len); - -#define stb_bitset_clearall(arr,len) (memset(arr, 0, 4 * (len))) -#define stb_bitset_setall(arr,len) (memset(arr, 255, 4 * (len))) - -#define stb_bitset_setbit(arr,n) ((arr)[(n) >> 5] |= (1 << (n & 31))) -#define stb_bitset_clearbit(arr,n) ((arr)[(n) >> 5] &= ~(1 << (n & 31))) -#define stb_bitset_testbit(arr,n) ((arr)[(n) >> 5] & (1 << (n & 31))) - -STB_EXTERN stb_bitset *stb_bitset_union(stb_bitset *p0, stb_bitset *p1, int len); - -STB_EXTERN int *stb_bitset_getlist(stb_bitset *out, int start, int end); - -STB_EXTERN int stb_bitset_eq(stb_bitset *p0, stb_bitset *p1, int len); -STB_EXTERN int stb_bitset_disjoint(stb_bitset *p0, stb_bitset *p1, int len); -STB_EXTERN int stb_bitset_disjoint_0(stb_bitset *p0, stb_bitset *p1, int len); -STB_EXTERN int stb_bitset_subset(stb_bitset *bigger, stb_bitset *smaller, int len); -STB_EXTERN int stb_bitset_unioneq_changed(stb_bitset *p0, stb_bitset *p1, int len); - -#ifdef STB_DEFINE -int stb_bitset_eq(stb_bitset *p0, stb_bitset *p1, int len) -{ - int i; - for (i=0; i < len; ++i) - if (p0[i] != p1[i]) return 0; - return 1; -} - -int stb_bitset_disjoint(stb_bitset *p0, stb_bitset *p1, int len) -{ - int i; - for (i=0; i < len; ++i) - if (p0[i] & p1[i]) return 0; - return 1; -} - -int stb_bitset_disjoint_0(stb_bitset *p0, stb_bitset *p1, int len) -{ - int i; - for (i=0; i < len; ++i) - if ((p0[i] | p1[i]) != 0xffffffff) return 0; - return 1; -} - -int stb_bitset_subset(stb_bitset *bigger, stb_bitset *smaller, int len) -{ - int i; - for (i=0; i < len; ++i) - if ((bigger[i] & smaller[i]) != smaller[i]) return 0; - return 1; -} - -stb_bitset *stb_bitset_union(stb_bitset *p0, stb_bitset *p1, int len) -{ - int i; - stb_bitset *d = (stb_bitset *) malloc(sizeof(*d) * len); - for (i=0; i < len; ++i) d[i] = p0[i] | p1[i]; - return d; -} - -int stb_bitset_unioneq_changed(stb_bitset *p0, stb_bitset *p1, int len) -{ - int i, changed=0; - for (i=0; i < len; ++i) { - stb_bitset d = p0[i] | p1[i]; - if (d != p0[i]) { - p0[i] = d; - changed = 1; - } - } - return changed; -} - -stb_bitset *stb_bitset_new(int value, int len) -{ - int i; - stb_bitset *d = (stb_bitset *) malloc(sizeof(*d) * len); - if (value) value = 0xffffffff; - for (i=0; i < len; ++i) d[i] = value; - return d; -} - -int *stb_bitset_getlist(stb_bitset *out, int start, int end) -{ - int *list = NULL; - int i; - for (i=start; i < end; ++i) - if (stb_bitset_testbit(out, i)) - stb_arr_push(list, i); - return list; -} -#endif - -////////////////////////////////////////////////////////////////////////////// -// -// stb_wordwrap quality word-wrapping for fixed-width fonts -// - -STB_EXTERN int stb_wordwrap(int *pairs, int pair_max, int count, char *str); -STB_EXTERN int *stb_wordwrapalloc(int count, char *str); - -#ifdef STB_DEFINE - -int stb_wordwrap(int *pairs, int pair_max, int count, char *str) -{ - int n=0,i=0, start=0,nonwhite=0; - if (pairs == NULL) pair_max = 0x7ffffff0; - else pair_max *= 2; - // parse - for(;;) { - int s=i; // first whitespace char; last nonwhite+1 - int w; // word start - // accept whitespace - while (isspace(str[i])) { - if (str[i] == '\n' || str[i] == '\r') { - if (str[i] + str[i+1] == '\n' + '\r') ++i; - if (n >= pair_max) return -1; - if (pairs) pairs[n] = start, pairs[n+1] = s-start; - n += 2; - nonwhite=0; - start = i+1; - s = start; - } - ++i; - } - if (i >= start+count) { - // we've gone off the end using whitespace - if (nonwhite) { - if (n >= pair_max) return -1; - if (pairs) pairs[n] = start, pairs[n+1] = s-start; - n += 2; - start = s = i; - nonwhite=0; - } else { - // output all the whitespace - while (i >= start+count) { - if (n >= pair_max) return -1; - if (pairs) pairs[n] = start, pairs[n+1] = count; - n += 2; - start += count; - } - s = start; - } - } - - if (str[i] == 0) break; - // now scan out a word and see if it fits - w = i; - while (str[i] && !isspace(str[i])) { - ++i; - } - // wrapped? - if (i > start + count) { - // huge? - if (i-s <= count) { - if (n >= pair_max) return -1; - if (pairs) pairs[n] = start, pairs[n+1] = s-start; - n += 2; - start = w; - } else { - // This word is longer than one line. If we wrap it onto N lines - // there are leftover chars. do those chars fit on the cur line? - // But if we have leading whitespace, we force it to start here. - if ((w-start) + ((i-w) % count) <= count || !nonwhite) { - // output a full line - if (n >= pair_max) return -1; - if (pairs) pairs[n] = start, pairs[n+1] = count; - n += 2; - start += count; - w = start; - } else { - // output a partial line, trimming trailing whitespace - if (s != start) { - if (n >= pair_max) return -1; - if (pairs) pairs[n] = start, pairs[n+1] = s-start; - n += 2; - start = w; - } - } - // now output full lines as needed - while (start + count <= i) { - if (n >= pair_max) return -1; - if (pairs) pairs[n] = start, pairs[n+1] = count; - n += 2; - start += count; - } - } - } - nonwhite=1; - } - if (start < i) { - if (n >= pair_max) return -1; - if (pairs) pairs[n] = start, pairs[n+1] = i-start; - n += 2; - } - return n>>1; -} - -int *stb_wordwrapalloc(int count, char *str) -{ - int n = stb_wordwrap(NULL,0,count,str); - int *z = NULL; - stb_arr_setlen(z, n*2); - stb_wordwrap(z, n, count, str); - return z; -} -#endif - - -////////////////////////////////////////////////////////////////////////////// -// -// stb_match: wildcards and regexping -// - -STB_EXTERN int stb_wildmatch (char *expr, char *candidate); -STB_EXTERN int stb_wildmatchi(char *expr, char *candidate); -STB_EXTERN int stb_wildfind (char *expr, char *candidate); -STB_EXTERN int stb_wildfindi (char *expr, char *candidate); - -STB_EXTERN int stb_regex(char *regex, char *candidate); - -typedef struct stb_matcher stb_matcher; - -STB_EXTERN stb_matcher *stb_regex_matcher(char *regex); -STB_EXTERN int stb_matcher_match(stb_matcher *m, char *str); -STB_EXTERN int stb_matcher_find(stb_matcher *m, char *str); -STB_EXTERN void stb_matcher_free(stb_matcher *f); - -STB_EXTERN stb_matcher *stb_lex_matcher(void); -STB_EXTERN int stb_lex_item(stb_matcher *m, char *str, int result); -STB_EXTERN int stb_lex_item_wild(stb_matcher *matcher, char *regex, int result); -STB_EXTERN int stb_lex(stb_matcher *m, char *str, int *len); - - - -#ifdef STB_DEFINE - -static int stb__match_qstring(char *candidate, char *qstring, int qlen, int insensitive) -{ - int i; - if (insensitive) { - for (i=0; i < qlen; ++i) - if (qstring[i] == '?') { - if (!candidate[i]) return 0; - } else - if (tolower(qstring[i]) != tolower(candidate[i])) - return 0; - } else { - for (i=0; i < qlen; ++i) - if (qstring[i] == '?') { - if (!candidate[i]) return 0; - } else - if (qstring[i] != candidate[i]) - return 0; - } - return 1; -} - -static int stb__find_qstring(char *candidate, char *qstring, int qlen, int insensitive) -{ - char c; - - int offset=0; - while (*qstring == '?') { - ++qstring; - --qlen; - ++candidate; - if (qlen == 0) return 0; - if (*candidate == 0) return -1; - } - - c = *qstring++; - --qlen; - if (insensitive) c = tolower(c); - - while (candidate[offset]) { - if (c == (insensitive ? tolower(candidate[offset]) : candidate[offset])) - if (stb__match_qstring(candidate+offset+1, qstring, qlen, insensitive)) - return offset; - ++offset; - } - - return -1; -} - -int stb__wildmatch_raw2(char *expr, char *candidate, int search, int insensitive) -{ - int where=0; - int start = -1; - - if (!search) { - // parse to first '*' - if (*expr != '*') - start = 0; - while (*expr != '*') { - if (!*expr) - return *candidate == 0 ? 0 : -1; - if (*expr == '?') { - if (!*candidate) return -1; - } else { - if (insensitive) { - if (tolower(*candidate) != tolower(*expr)) - return -1; - } else - if (*candidate != *expr) - return -1; - } - ++candidate, ++expr, ++where; - } - } else { - // 0-length search string - if (!*expr) - return 0; - } - - assert(search || *expr == '*'); - if (!search) - ++expr; - - // implicit '*' at this point - - while (*expr) { - int o=0; - // combine redundant * characters - while (expr[0] == '*') ++expr; - - // ok, at this point, expr[-1] == '*', - // and expr[0] != '*' - - if (!expr[0]) return start >= 0 ? start : 0; - - // now find next '*' - o = 0; - while (expr[o] != '*') { - if (expr[o] == 0) - break; - ++o; - } - // if no '*', scan to end, then match at end - if (expr[o] == 0 && !search) { - int z; - for (z=0; z < o; ++z) - if (candidate[z] == 0) - return -1; - while (candidate[z]) - ++z; - // ok, now check if they match - if (stb__match_qstring(candidate+z-o, expr, o, insensitive)) - return start >= 0 ? start : 0; - return -1; - } else { - // if yes '*', then do stb__find_qmatch on the intervening chars - int n = stb__find_qstring(candidate, expr, o, insensitive); - if (n < 0) - return -1; - if (start < 0) - start = where + n; - expr += o; - candidate += n+o; - } - - if (*expr == 0) { - assert(search); - return start; - } - - assert(*expr == '*'); - ++expr; - } - - return start >= 0 ? start : 0; -} - -int stb__wildmatch_raw(char *expr, char *candidate, int search, int insensitive) -{ - char buffer[256]; - // handle multiple search strings - char *s = strchr(expr, ';'); - char *last = expr; - while (s) { - int z; - // need to allow for non-writeable strings... assume they're small - if (s - last < 256) { - stb_strncpy(buffer, last, s-last+1); - z = stb__wildmatch_raw2(buffer, candidate, search, insensitive); - } else { - *s = 0; - z = stb__wildmatch_raw2(last, candidate, search, insensitive); - *s = ';'; - } - if (z >= 0) return z; - last = s+1; - s = strchr(last, ';'); - } - return stb__wildmatch_raw2(last, candidate, search, insensitive); -} - -int stb_wildmatch(char *expr, char *candidate) -{ - return stb__wildmatch_raw(expr, candidate, 0,0) >= 0; -} - -int stb_wildmatchi(char *expr, char *candidate) -{ - return stb__wildmatch_raw(expr, candidate, 0,1) >= 0; -} - -int stb_wildfind(char *expr, char *candidate) -{ - return stb__wildmatch_raw(expr, candidate, 1,0); -} - -int stb_wildfindi(char *expr, char *candidate) -{ - return stb__wildmatch_raw(expr, candidate, 1,1); -} - -typedef struct -{ - stb_int16 transition[256]; -} stb_dfa; - -// an NFA node represents a state you're in; it then has -// an arbitrary number of edges dangling off of it -// note this isn't utf8-y -typedef struct -{ - stb_int16 match; // character/set to match - stb_uint16 node; // output node to go to -} stb_nfa_edge; - -typedef struct -{ - stb_int16 goal; // does reaching this win the prize? - stb_uint8 active; // is this in the active list - stb_nfa_edge *out; - stb_uint16 *eps; // list of epsilon closures -} stb_nfa_node; - -#define STB__DFA_UNDEF -1 -#define STB__DFA_GOAL -2 -#define STB__DFA_END -3 -#define STB__DFA_MGOAL -4 -#define STB__DFA_VALID 0 - -#define STB__NFA_STOP_GOAL -1 - -// compiled regexp -struct stb_matcher -{ - stb_uint16 start_node; - stb_int16 dfa_start; - stb_uint32 *charset; - int num_charset; - int match_start; - stb_nfa_node *nodes; - int does_lex; - - // dfa matcher - stb_dfa * dfa; - stb_uint32 * dfa_mapping; - stb_int16 * dfa_result; - int num_words_per_dfa; -}; - -static int stb__add_node(stb_matcher *matcher) -{ - stb_nfa_node z; - z.active = 0; - z.eps = 0; - z.goal = 0; - z.out = 0; - stb_arr_push(matcher->nodes, z); - return stb_arr_len(matcher->nodes)-1; -} - -static void stb__add_epsilon(stb_matcher *matcher, int from, int to) -{ - assert(from != to); - if (matcher->nodes[from].eps == NULL) - stb_arr_malloc((void **) &matcher->nodes[from].eps, matcher); - stb_arr_push(matcher->nodes[from].eps, to); -} - -static void stb__add_edge(stb_matcher *matcher, int from, int to, int type) -{ - stb_nfa_edge z = { type, to }; - if (matcher->nodes[from].out == NULL) - stb_arr_malloc((void **) &matcher->nodes[from].out, matcher); - stb_arr_push(matcher->nodes[from].out, z); -} - -static char *stb__reg_parse_alt(stb_matcher *m, int s, char *r, stb_uint16 *e); -static char *stb__reg_parse(stb_matcher *matcher, int start, char *regex, stb_uint16 *end) -{ - int n; - int last_start = -1; - stb_uint16 last_end = start; - - while (*regex) { - switch (*regex) { - case '(': - last_start = last_end; - regex = stb__reg_parse_alt(matcher, last_end, regex+1, &last_end); - if (regex == NULL || *regex != ')') - return NULL; - ++regex; - break; - - case '|': - case ')': - *end = last_end; - return regex; - - case '?': - if (last_start < 0) return NULL; - stb__add_epsilon(matcher, last_start, last_end); - ++regex; - break; - - case '*': - if (last_start < 0) return NULL; - stb__add_epsilon(matcher, last_start, last_end); - - // fall through - - case '+': - if (last_start < 0) return NULL; - stb__add_epsilon(matcher, last_end, last_start); - // prevent links back to last_end from chaining to last_start - n = stb__add_node(matcher); - stb__add_epsilon(matcher, last_end, n); - last_end = n; - ++regex; - break; - - case '{': // not supported! - // @TODO: given {n,m}, clone last_start to last_end m times, - // and include epsilons from start to first m-n blocks - return NULL; - - case '\\': - ++regex; - if (!*regex) return NULL; - - // fallthrough - default: // match exactly this character - n = stb__add_node(matcher); - stb__add_edge(matcher, last_end, n, *regex); - last_start = last_end; - last_end = n; - ++regex; - break; - - case '$': - n = stb__add_node(matcher); - stb__add_edge(matcher, last_end, n, '\n'); - last_start = last_end; - last_end = n; - ++regex; - break; - - case '.': - n = stb__add_node(matcher); - stb__add_edge(matcher, last_end, n, -1); - last_start = last_end; - last_end = n; - ++regex; - break; - - case '[': { - stb_uint8 flags[256]; - int invert = 0,z; - ++regex; - if (matcher->num_charset == 0) { - matcher->charset = (stb_uint *) stb_malloc(matcher, sizeof(*matcher->charset) * 256); - memset(matcher->charset, 0, sizeof(*matcher->charset) * 256); - } - - memset(flags,0,sizeof(flags)); - - // leading ^ is special - if (*regex == '^') - ++regex, invert = 1; - - // leading ] is special - if (*regex == ']') { - flags[']'] = 1; - ++regex; - } - while (*regex != ']') { - stb_uint a; - if (!*regex) return NULL; - a = *regex++; - if (regex[0] == '-' && regex[1] != ']') { - stb_uint i,b = regex[1]; - regex += 2; - if (b == 0) return NULL; - if (a > b) return NULL; - for (i=a; i <= b; ++i) - flags[i] = 1; - } else - flags[a] = 1; - } - ++regex; - if (invert) { - int i; - for (i=0; i < 256; ++i) - flags[i] = 1-flags[i]; - } - - // now check if any existing charset matches - for (z=0; z < matcher->num_charset; ++z) { - int i, k[2] = { 0, 1 << z}; - for (i=0; i < 256; ++i) { - unsigned int f = k[flags[i]]; - if ((matcher->charset[i] & k[1]) != f) - break; - } - if (i == 256) break; - } - - if (z == matcher->num_charset) { - int i; - ++matcher->num_charset; - if (matcher->num_charset > 32) { - assert(0); /* NOTREACHED */ - return NULL; // too many charsets, oops - } - for (i=0; i < 256; ++i) - if (flags[i]) - matcher->charset[i] |= (1 << z); - } - - n = stb__add_node(matcher); - stb__add_edge(matcher, last_end, n, -2 - z); - last_start = last_end; - last_end = n; - break; - } - } - } - *end = last_end; - return regex; -} - -static char *stb__reg_parse_alt(stb_matcher *matcher, int start, char *regex, stb_uint16 *end) -{ - stb_uint16 last_end = start; - stb_uint16 main_end; - - int head, tail; - - head = stb__add_node(matcher); - stb__add_epsilon(matcher, start, head); - - regex = stb__reg_parse(matcher, head, regex, &last_end); - if (regex == NULL) return NULL; - if (*regex == 0 || *regex == ')') { - *end = last_end; - return regex; - } - - main_end = last_end; - tail = stb__add_node(matcher); - - stb__add_epsilon(matcher, last_end, tail); - - // start alternatives from the same starting node; use epsilon - // transitions to combine their endings - while(*regex && *regex != ')') { - assert(*regex == '|'); - head = stb__add_node(matcher); - stb__add_epsilon(matcher, start, head); - regex = stb__reg_parse(matcher, head, regex+1, &last_end); - if (regex == NULL) - return NULL; - stb__add_epsilon(matcher, last_end, tail); - } - - *end = tail; - return regex; -} - -static char *stb__wild_parse(stb_matcher *matcher, int start, char *str, stb_uint16 *end) -{ - int n; - stb_uint16 last_end; - - last_end = stb__add_node(matcher); - stb__add_epsilon(matcher, start, last_end); - - while (*str) { - switch (*str) { - // fallthrough - default: // match exactly this character - n = stb__add_node(matcher); - if (toupper(*str) == tolower(*str)) { - stb__add_edge(matcher, last_end, n, *str); - } else { - stb__add_edge(matcher, last_end, n, tolower(*str)); - stb__add_edge(matcher, last_end, n, toupper(*str)); - } - last_end = n; - ++str; - break; - - case '?': - n = stb__add_node(matcher); - stb__add_edge(matcher, last_end, n, -1); - last_end = n; - ++str; - break; - - case '*': - n = stb__add_node(matcher); - stb__add_edge(matcher, last_end, n, -1); - stb__add_epsilon(matcher, last_end, n); - stb__add_epsilon(matcher, n, last_end); - last_end = n; - ++str; - break; - } - } - - // now require end of string to match - n = stb__add_node(matcher); - stb__add_edge(matcher, last_end, n, 0); - last_end = n; - - *end = last_end; - return str; -} - -static int stb__opt(stb_matcher *m, int n) -{ - for(;;) { - stb_nfa_node *p = &m->nodes[n]; - if (p->goal) return n; - if (stb_arr_len(p->out)) return n; - if (stb_arr_len(p->eps) != 1) return n; - n = p->eps[0]; - } -} - -static void stb__optimize(stb_matcher *m) -{ - // if the target of any edge is a node with exactly - // one out-epsilon, shorten it - int i,j; - for (i=0; i < stb_arr_len(m->nodes); ++i) { - stb_nfa_node *p = &m->nodes[i]; - for (j=0; j < stb_arr_len(p->out); ++j) - p->out[j].node = stb__opt(m,p->out[j].node); - for (j=0; j < stb_arr_len(p->eps); ++j) - p->eps[j] = stb__opt(m,p->eps[j] ); - } - m->start_node = stb__opt(m,m->start_node); -} - -void stb_matcher_free(stb_matcher *f) -{ - stb_free(f); -} - -static stb_matcher *stb__alloc_matcher(void) -{ - stb_matcher *matcher = (stb_matcher *) stb_malloc(0,sizeof(*matcher)); - - matcher->start_node = 0; - stb_arr_malloc((void **) &matcher->nodes, matcher); - matcher->num_charset = 0; - matcher->match_start = 0; - matcher->does_lex = 0; - - matcher->dfa_start = STB__DFA_UNDEF; - stb_arr_malloc((void **) &matcher->dfa, matcher); - stb_arr_malloc((void **) &matcher->dfa_mapping, matcher); - stb_arr_malloc((void **) &matcher->dfa_result, matcher); - - stb__add_node(matcher); - - return matcher; -} - -static void stb__lex_reset(stb_matcher *matcher) -{ - // flush cached dfa data - stb_arr_setlen(matcher->dfa, 0); - stb_arr_setlen(matcher->dfa_mapping, 0); - stb_arr_setlen(matcher->dfa_result, 0); - matcher->dfa_start = STB__DFA_UNDEF; -} - -stb_matcher *stb_regex_matcher(char *regex) -{ - char *z; - stb_uint16 end; - stb_matcher *matcher = stb__alloc_matcher(); - if (*regex == '^') { - matcher->match_start = 1; - ++regex; - } - - z = stb__reg_parse_alt(matcher, matcher->start_node, regex, &end); - - if (!z || *z) { - stb_free(matcher); - return NULL; - } - - ((matcher->nodes)[(int) end]).goal = STB__NFA_STOP_GOAL; - - return matcher; -} - -stb_matcher *stb_lex_matcher(void) -{ - stb_matcher *matcher = stb__alloc_matcher(); - - matcher->match_start = 1; - matcher->does_lex = 1; - - return matcher; -} - -int stb_lex_item(stb_matcher *matcher, char *regex, int result) -{ - char *z; - stb_uint16 end; - - z = stb__reg_parse_alt(matcher, matcher->start_node, regex, &end); - - if (z == NULL) - return 0; - - stb__lex_reset(matcher); - - matcher->nodes[(int) end].goal = result; - return 1; -} - -int stb_lex_item_wild(stb_matcher *matcher, char *regex, int result) -{ - char *z; - stb_uint16 end; - - z = stb__wild_parse(matcher, matcher->start_node, regex, &end); - - if (z == NULL) - return 0; - - stb__lex_reset(matcher); - - matcher->nodes[(int) end].goal = result; - return 1; -} - -static void stb__clear(stb_matcher *m, stb_uint16 *list) -{ - int i; - for (i=0; i < stb_arr_len(list); ++i) - m->nodes[(int) list[i]].active = 0; -} - -static int stb__clear_goalcheck(stb_matcher *m, stb_uint16 *list) -{ - int i, t=0; - for (i=0; i < stb_arr_len(list); ++i) { - t += m->nodes[(int) list[i]].goal; - m->nodes[(int) list[i]].active = 0; - } - return t; -} - -static stb_uint16 * stb__add_if_inactive(stb_matcher *m, stb_uint16 *list, int n) -{ - if (!m->nodes[n].active) { - stb_arr_push(list, n); - m->nodes[n].active = 1; - } - return list; -} - -static stb_uint16 * stb__eps_closure(stb_matcher *m, stb_uint16 *list) -{ - int i,n = stb_arr_len(list); - - for(i=0; i < n; ++i) { - stb_uint16 *e = m->nodes[(int) list[i]].eps; - if (e) { - int j,k = stb_arr_len(e); - for (j=0; j < k; ++j) - list = stb__add_if_inactive(m, list, e[j]); - n = stb_arr_len(list); - } - } - - return list; -} - -int stb_matcher_match(stb_matcher *m, char *str) -{ - int result = 0; - int i,j,y,z; - stb_uint16 *previous = NULL; - stb_uint16 *current = NULL; - stb_uint16 *temp; - - stb_arr_setsize(previous, 4); - stb_arr_setsize(current, 4); - - previous = stb__add_if_inactive(m, previous, m->start_node); - previous = stb__eps_closure(m,previous); - stb__clear(m, previous); - - while (*str && stb_arr_len(previous)) { - y = stb_arr_len(previous); - for (i=0; i < y; ++i) { - stb_nfa_node *n = &m->nodes[(int) previous[i]]; - z = stb_arr_len(n->out); - for (j=0; j < z; ++j) { - if (n->out[j].match >= 0) { - if (n->out[j].match == *str) - current = stb__add_if_inactive(m, current, n->out[j].node); - } else if (n->out[j].match == -1) { - if (*str != '\n') - current = stb__add_if_inactive(m, current, n->out[j].node); - } else if (n->out[j].match < -1) { - int z = -n->out[j].match - 2; - if (m->charset[(stb_uint8) *str] & (1 << z)) - current = stb__add_if_inactive(m, current, n->out[j].node); - } - } - } - stb_arr_setlen(previous, 0); - - temp = previous; - previous = current; - current = temp; - - previous = stb__eps_closure(m,previous); - stb__clear(m, previous); - - ++str; - } - - // transition to pick up a '$' at the end - y = stb_arr_len(previous); - for (i=0; i < y; ++i) - m->nodes[(int) previous[i]].active = 1; - - for (i=0; i < y; ++i) { - stb_nfa_node *n = &m->nodes[(int) previous[i]]; - z = stb_arr_len(n->out); - for (j=0; j < z; ++j) { - if (n->out[j].match == '\n') - current = stb__add_if_inactive(m, current, n->out[j].node); - } - } - - previous = stb__eps_closure(m,previous); - stb__clear(m, previous); - - y = stb_arr_len(previous); - for (i=0; i < y; ++i) - if (m->nodes[(int) previous[i]].goal) - result = 1; - - stb_arr_free(previous); - stb_arr_free(current); - - return result && *str == 0; -} - -stb_int16 stb__get_dfa_node(stb_matcher *m, stb_uint16 *list) -{ - stb_uint16 node; - stb_uint32 data[8], *state, *newstate; - int i,j,n; - - state = (stb_uint32 *) stb_temp(data, m->num_words_per_dfa * 4); - memset(state, 0, m->num_words_per_dfa*4); - - n = stb_arr_len(list); - for (i=0; i < n; ++i) { - int x = list[i]; - state[x >> 5] |= 1 << (x & 31); - } - - // @TODO use a hash table - n = stb_arr_len(m->dfa_mapping); - i=j=0; - for(; j < n; ++i, j += m->num_words_per_dfa) { - // @TODO special case for <= 32 - if (!memcmp(state, m->dfa_mapping + j, m->num_words_per_dfa*4)) { - node = i; - goto done; - } - } - - assert(stb_arr_len(m->dfa) == i); - node = i; - - newstate = stb_arr_addn(m->dfa_mapping, m->num_words_per_dfa); - memcpy(newstate, state, m->num_words_per_dfa*4); - - // set all transitions to 'unknown' - stb_arr_add(m->dfa); - memset(m->dfa[i].transition, -1, sizeof(m->dfa[i].transition)); - - if (m->does_lex) { - int result = -1; - n = stb_arr_len(list); - for (i=0; i < n; ++i) { - if (m->nodes[(int) list[i]].goal > result) - result = m->nodes[(int) list[i]].goal; - } - - stb_arr_push(m->dfa_result, result); - } - -done: - stb_tempfree(data, state); - return node; -} - -static int stb__matcher_dfa(stb_matcher *m, char *str_c, int *len) -{ - stb_uint8 *str = (stb_uint8 *) str_c; - stb_int16 node,prevnode; - stb_dfa *trans; - int match_length = 0; - stb_int16 match_result=0; - - if (m->dfa_start == STB__DFA_UNDEF) { - stb_uint16 *list; - - m->num_words_per_dfa = (stb_arr_len(m->nodes)+31) >> 5; - stb__optimize(m); - - list = stb__add_if_inactive(m, NULL, m->start_node); - list = stb__eps_closure(m,list); - if (m->does_lex) { - m->dfa_start = stb__get_dfa_node(m,list); - stb__clear(m, list); - // DON'T allow start state to be a goal state! - // this allows people to specify regexes that can match 0 - // characters without them actually matching (also we don't - // check _before_ advancing anyway - if (m->dfa_start <= STB__DFA_MGOAL) - m->dfa_start = -(m->dfa_start - STB__DFA_MGOAL); - } else { - if (stb__clear_goalcheck(m, list)) - m->dfa_start = STB__DFA_GOAL; - else - m->dfa_start = stb__get_dfa_node(m,list); - } - stb_arr_free(list); - } - - prevnode = STB__DFA_UNDEF; - node = m->dfa_start; - trans = m->dfa; - - if (m->dfa_start == STB__DFA_GOAL) - return 1; - - for(;;) { - assert(node >= STB__DFA_VALID); - - // fast inner DFA loop; especially if STB__DFA_VALID is 0 - - do { - prevnode = node; - node = trans[node].transition[*str++]; - } while (node >= STB__DFA_VALID); - - assert(node >= STB__DFA_MGOAL - stb_arr_len(m->dfa)); - assert(node < stb_arr_len(m->dfa)); - - // special case for lex: need _longest_ match, so notice goal - // state without stopping - if (node <= STB__DFA_MGOAL) { - match_length = str - (stb_uint8 *) str_c; - node = -(node - STB__DFA_MGOAL); - match_result = node; - continue; - } - - // slow NFA->DFA conversion - - // or we hit the goal or the end of the string, but those - // can only happen once per search... - - if (node == STB__DFA_UNDEF) { - // build a list -- @TODO special case <= 32 states - // heck, use a more compact data structure for <= 16 and <= 8 ?! - - // @TODO keep states/newstates around instead of reallocating them - stb_uint16 *states = NULL; - stb_uint16 *newstates = NULL; - int i,j,y,z; - stb_uint32 *flags = &m->dfa_mapping[prevnode * m->num_words_per_dfa]; - assert(prevnode != STB__DFA_UNDEF); - stb_arr_setsize(states, 4); - stb_arr_setsize(newstates,4); - for (j=0; j < m->num_words_per_dfa; ++j) { - for (i=0; i < 32; ++i) { - if (*flags & (1 << i)) - stb_arr_push(states, j*32+i); - } - ++flags; - } - // states is now the states we were in in the previous node; - // so now we can compute what node it transitions to on str[-1] - - y = stb_arr_len(states); - for (i=0; i < y; ++i) { - stb_nfa_node *n = &m->nodes[(int) states[i]]; - z = stb_arr_len(n->out); - for (j=0; j < z; ++j) { - if (n->out[j].match >= 0) { - if (n->out[j].match == str[-1] || (str[-1] == 0 && n->out[j].match == '\n')) - newstates = stb__add_if_inactive(m, newstates, n->out[j].node); - } else if (n->out[j].match == -1) { - if (str[-1] != '\n' && str[-1]) - newstates = stb__add_if_inactive(m, newstates, n->out[j].node); - } else if (n->out[j].match < -1) { - int z = -n->out[j].match - 2; - if (m->charset[str[-1]] & (1 << z)) - newstates = stb__add_if_inactive(m, newstates, n->out[j].node); - } - } - } - // AND add in the start state! - if (!m->match_start || (str[-1] == '\n' && !m->does_lex)) - newstates = stb__add_if_inactive(m, newstates, m->start_node); - // AND epsilon close it - newstates = stb__eps_closure(m, newstates); - // if it's a goal state, then that's all there is to it - if (stb__clear_goalcheck(m, newstates)) { - if (m->does_lex) { - match_length = str - (stb_uint8 *) str_c; - node = stb__get_dfa_node(m,newstates); - match_result = node; - node = -node + STB__DFA_MGOAL; - trans = m->dfa; // could have gotten realloc()ed - } else - node = STB__DFA_GOAL; - } else if (str[-1] == 0 || stb_arr_len(newstates) == 0) { - node = STB__DFA_END; - } else { - node = stb__get_dfa_node(m,newstates); - trans = m->dfa; // could have gotten realloc()ed - } - trans[prevnode].transition[str[-1]] = node; - if (node <= STB__DFA_MGOAL) - node = -(node - STB__DFA_MGOAL); - stb_arr_free(newstates); - stb_arr_free(states); - } - - if (node == STB__DFA_GOAL) { - return 1; - } - if (node == STB__DFA_END) { - if (m->does_lex) { - if (match_result) { - if (len) *len = match_length; - return m->dfa_result[(int) match_result]; - } - } - return 0; - } - - assert(node != STB__DFA_UNDEF); - } -} - -int stb_matcher_find(stb_matcher *m, char *str) -{ - assert(m->does_lex == 0); - return stb__matcher_dfa(m, str, NULL); -} - -int stb_lex(stb_matcher *m, char *str, int *len) -{ - assert(m->does_lex); - return stb__matcher_dfa(m, str, len); -} - -int stb_regex(char *regex, char *str) -{ - static stb_perfect p; - static stb_matcher ** matchers; - static char ** regexps; - static char ** regexp_cache; - static unsigned short *mapping; - int z = stb_perfect_hash(&p, (int) regex); - if (z >= 0) { - if (strcmp(regex, regexp_cache[(int) mapping[z]])) { - int i = mapping[z]; - stb_matcher_free(matchers[i]); - free(regexp_cache[i]); - regexps[i] = regex; - regexp_cache[i] = strdup(regex); - matchers[i] = stb_regex_matcher(regex); - } - } else { - int i,n; - if (regex == NULL) { - for (i=0; i < stb_arr_len(matchers); ++i) { - stb_matcher_free(matchers[i]); - free(regexp_cache[i]); - } - stb_arr_free(matchers); - stb_arr_free(regexps); - stb_arr_free(regexp_cache); - stb_perfect_destroy(&p); - free(mapping); mapping = NULL; - return -1; - } - stb_arr_push(regexps, regex); - stb_arr_push(regexp_cache, strdup(regex)); - stb_arr_push(matchers, stb_regex_matcher(regex)); - stb_perfect_destroy(&p); - n = stb_perfect_create(&p, (unsigned int *) (char **) regexps, stb_arr_len(regexps)); - mapping = (unsigned short *) realloc(mapping, n * sizeof(*mapping)); - for (i=0; i < stb_arr_len(regexps); ++i) - mapping[stb_perfect_hash(&p, (int) regexps[i])] = i; - z = stb_perfect_hash(&p, (int) regex); - } - return stb_matcher_find(matchers[(int) mapping[z]], str); -} - -#endif // STB_DEFINE - - -#if 0 -////////////////////////////////////////////////////////////////////////////// -// -// C source-code introspection -// - -// runtime structure -typedef struct -{ - char *name; - char *type; // base type - char *comment; // content of comment field - int size; // size of base type - int offset; // field offset - int arrcount[8]; // array sizes; -1 = pointer indirection; 0 = end of list -} stb_info_field; - -typedef struct -{ - char *structname; - int size; - int num_fields; - stb_info_field *fields; -} stb_info_struct; - -extern stb_info_struct stb_introspect_output[]; - -// - -STB_EXTERN void stb_introspect_precompiled(stb_info_struct *compiled); -STB_EXTERN void stb__introspect(char *path, char *file); - -#define stb_introspect_ship() stb__introspect(NULL, NULL, stb__introspect_output) - -#ifdef STB_SHIP -#define stb_introspect() stb_introspect_ship() -#define stb_introspect_path(p) stb_introspect_ship() -#else -// bootstrapping: define stb_introspect() (or 'path') the first time -#define stb_introspect() stb__introspect(NULL, __FILE__, NULL) -#define stb_introspect_auto() stb__introspect(NULL, __FILE__, stb__introspect_output) - -#define stb_introspect_path(p) stb__introspect(p, __FILE__, NULL) -#define stb_introspect_path(p) stb__introspect(p, __FILE__, NULL) -#endif - -#ifdef STB_DEFINE - -#ifndef STB_INTROSPECT_CPP - #ifdef __cplusplus - #define STB_INTROSPECT_CPP 1 - #else - #define STB_INTROSPECT_CPP 0 - #endif -#endif - -void stb_introspect_precompiled(stb_info_struct *compiled) -{ - -} - - -static void stb__introspect_filename(char *buffer, char *path) -{ - #if STB_INTROSPECT_CPP - sprintf(buffer, "%s/stb_introspect.cpp", path); - #else - sprintf(buffer, "%s/stb_introspect.c", path); - #endif -} - -static void stb__introspect_compute(char *path, char *file) -{ - int i; - char ** include_list = NULL; - char ** introspect_list = NULL; - FILE *f; - f = fopen(file, "w"); - if (!f) return; - - fputs("// if you get compiler errors, change the following 0 to a 1:\n", f); - fputs("#define STB_INTROSPECT_INVALID 0\n\n", f); - fputs("// this will force the code to compile, and force the introspector\n", f); - fputs("// to run and then exit, allowing you to recompile\n\n\n", f); - fputs("#include \"stb.h\"\n\n",f ); - fputs("#if STB_INTROSPECT_INVALID\n", f); - fputs(" stb_info_struct stb__introspect_output[] = { (void *) 1 }\n", f); - fputs("#else\n\n", f); - for (i=0; i < stb_arr_len(include_list); ++i) - fprintf(f, " #include \"%s\"\n", include_list[i]); - - fputs(" stb_info_struct stb__introspect_output[] =\n{\n", f); - for (i=0; i < stb_arr_len(introspect_list); ++i) - fprintf(f, " stb_introspect_%s,\n", introspect_list[i]); - fputs(" };\n", f); - fputs("#endif\n", f); - fclose(f); -} - -static stb_info_struct *stb__introspect_info; - -#ifndef STB_SHIP - -#endif - -void stb__introspect(char *path, char *file, stb_info_struct *compiled) -{ - static int first=1; - if (!first) return; - first=0; - - stb__introspect_info = compiled; - - #ifndef STB_SHIP - if (path || file) { - int bail_flag = compiled && compiled[0].structname == (void *) 1; - int needs_building = bail_flag; - struct stb__stat st; - char buffer[1024], buffer2[1024]; - if (!path) { - stb_splitpath(buffer, file, STB_PATH); - path = buffer; - } - // bail if the source path doesn't exist - if (!stb_fexists(path)) return; - - stb__introspect_filename(buffer2, path); - - // get source/include files timestamps, compare to output-file timestamp; - // if mismatched, regenerate - - if (stb__stat(buffer2, &st)) - needs_building = STB_TRUE; - - { - // find any file that contains an introspection command and is newer - // if needs_building is already true, we don't need to do this test, - // but we still need these arrays, so go ahead and get them - char **all[3]; - all[0] = stb_readdir_files_mask(path, "*.h"); - all[1] = stb_readdir_files_mask(path, "*.c"); - all[2] = stb_readdir_files_mask(path, "*.cpp"); - int i,j; - if (needs_building) { - for (j=0; j < 3; ++j) { - for (i=0; i < stb_arr_len(all[j]); ++i) { - struct stb__stat st2; - if (!stb__stat(all[j][i], &st2)) { - if (st.st_mtime < st2.st_mtime) { - char *z = stb_filec(all[j][i], NULL); - int found=STB_FALSE; - while (y) { - y = strstr(y, "//si"); - if (y && isspace(y[4])) { - found = STB_TRUE; - break; - } - } - needs_building = STB_TRUE; - goto done; - } - } - } - } - done:; - } - char *z = stb_filec(all[i], NULL), *y = z; - int found=STB_FALSE; - while (y) { - y = strstr(y, "//si"); - if (y && isspace(y[4])) { - found = STB_TRUE; - break; - } - } - if (found) - stb_arr_push(introspect_h, strdup(all[i])); - free(z); - } - } - stb_readdir_free(all); - if (!needs_building) { - for (i=0; i < stb_arr_len(introspect_h); ++i) { - struct stb__stat st2; - if (!stb__stat(introspect_h[i], &st2)) - if (st.st_mtime < st2.st_mtime) - needs_building = STB_TRUE; - } - } - - if (needs_building) { - stb__introspect_compute(path, buffer2); - } - } - } - #endif -} -#endif -#endif - -#ifdef STB_INTROSPECT -// compile-time code-generator -#define INTROSPECT(x) int main(int argc, char **argv) { stb__introspect(__FILE__); return 0; } -#define FILE(x) - -void stb__introspect(char *filename) -{ - char *file = stb_file(filename, NULL); - char *s = file, *t, **p; - char *out_name = "stb_introspect.c"; - char *out_path; - STB_ARR(char) filelist = NULL; - int i,n; - if (!file) stb_fatal("Couldn't open %s", filename); - - out_path = stb_splitpathdup(filename, STB_PATH); - - // search for the macros - while (*s) { - char buffer[256]; - while (*s && !isupper(*s)) ++s; - s = stb_strtok_invert(buffer, s, "ABCDEFGHIJKLMNOPQRSTUVWXYZ"); - s = stb_skipwhite(s); - if (*s == '(') { - ++s; - t = strchr(s, ')'); - if (t == NULL) stb_fatal("Error parsing %s", filename); - - } - } -} - - - -#endif - - -////////////////////////////////////////////////////////////////////////////// -// -// STB-C sliding-window dictionary compression -// -// This uses a DEFLATE-style sliding window, but no bitwise entropy. -// Everything is on byte boundaries, so you could then apply a byte-wise -// entropy code, though that's nowhere near as effective. -// -// An STB-C stream begins with a 16-byte header: -// 4 bytes: 0x57 0xBC 0x00 0x00 -// 8 bytes: big-endian size of decompressed data, 64-bits -// 4 bytes: big-endian size of window (how far back decompressor may need) -// -// The following symbols appear in the stream (these were determined ad hoc, -// not by analysis): -// -// [dict] 00000100 yyyyyyyy yyyyyyyy yyyyyyyy xxxxxxxx xxxxxxxx -// [END] 00000101 11111010 cccccccc cccccccc cccccccc cccccccc -// [dict] 00000110 yyyyyyyy yyyyyyyy yyyyyyyy xxxxxxxx -// [literals] 00000111 zzzzzzzz zzzzzzzz -// [literals] 00001zzz zzzzzzzz -// [dict] 00010yyy yyyyyyyy yyyyyyyy xxxxxxxx xxxxxxxx -// [dict] 00011yyy yyyyyyyy yyyyyyyy xxxxxxxx -// [literals] 001zzzzz -// [dict] 01yyyyyy yyyyyyyy xxxxxxxx -// [dict] 1xxxxxxx yyyyyyyy -// -// xxxxxxxx: match length - 1 -// yyyyyyyy: backwards distance - 1 -// zzzzzzzz: num literals - 1 -// cccccccc: adler32 checksum of decompressed data -// (all big-endian) - - -STB_EXTERN stb_uint stb_decompress_length(stb_uchar *input); -STB_EXTERN stb_uint stb_decompress(stb_uchar *out,stb_uchar *in,stb_uint len); -STB_EXTERN stb_uint stb_compress (stb_uchar *out,stb_uchar *in,stb_uint len); -STB_EXTERN void stb_compress_window(int z); -STB_EXTERN void stb_compress_hashsize(unsigned int z); - -STB_EXTERN int stb_compress_tofile(char *filename, char *in, stb_uint len); -STB_EXTERN int stb_compress_intofile(FILE *f, char *input, stb_uint len); -STB_EXTERN char *stb_decompress_fromfile(char *filename, stb_uint *len); - -STB_EXTERN int stb_compress_stream_start(FILE *f); -STB_EXTERN void stb_compress_stream_end(int close); -STB_EXTERN void stb_write(char *data, int data_len); - -#ifdef STB_DEFINE - -stb_uint stb_decompress_length(stb_uchar *input) -{ - return (input[8] << 24) + (input[9] << 16) + (input[10] << 8) + input[11]; -} - -//////////////////// decompressor /////////////////////// - -// simple implementation that just writes whole thing into big block - -static unsigned char *stb__barrier; -static unsigned char *stb__barrier2; -static unsigned char *stb__barrier3; -static unsigned char *stb__barrier4; - -static stb_uchar *stb__dout; -static void stb__match(stb_uchar *data, stb_uint length) -{ - // INVERSE of memmove... write each byte before copying the next... - assert (stb__dout + length <= stb__barrier); - if (stb__dout + length > stb__barrier) { stb__dout += length; return; } - if (data < stb__barrier4) { stb__dout = stb__barrier+1; return; } - while (length--) *stb__dout++ = *data++; -} - -static void stb__lit(stb_uchar *data, stb_uint length) -{ - assert (stb__dout + length <= stb__barrier); - if (stb__dout + length > stb__barrier) { stb__dout += length; return; } - if (data < stb__barrier2) { stb__dout = stb__barrier+1; return; } - memcpy(stb__dout, data, length); - stb__dout += length; -} - -#define stb__in2(x) ((i[x] << 8) + i[(x)+1]) -#define stb__in3(x) ((i[x] << 16) + stb__in2((x)+1)) -#define stb__in4(x) ((i[x] << 24) + stb__in3((x)+1)) - -static stb_uchar *stb_decompress_token(stb_uchar *i) -{ - if (*i >= 0x20) { // use fewer if's for cases that expand small - if (*i >= 0x80) stb__match(stb__dout-i[1]-1, i[0] - 0x80 + 1), i += 2; - else if (*i >= 0x40) stb__match(stb__dout-(stb__in2(0) - 0x4000 + 1), i[2]+1), i += 3; - else /* *i >= 0x20 */ stb__lit(i+1, i[0] - 0x20 + 1), i += 1 + (i[0] - 0x20 + 1); - } else { // more ifs for cases that expand large, since overhead is amortized - if (*i >= 0x18) stb__match(stb__dout-(stb__in3(0) - 0x180000 + 1), i[3]+1), i += 4; - else if (*i >= 0x10) stb__match(stb__dout-(stb__in3(0) - 0x100000 + 1), stb__in2(3)+1), i += 5; - else if (*i >= 0x08) stb__lit(i+2, stb__in2(0) - 0x0800 + 1), i += 2 + (stb__in2(0) - 0x0800 + 1); - else if (*i == 0x07) stb__lit(i+3, stb__in2(1) + 1), i += 3 + (stb__in2(1) + 1); - else if (*i == 0x06) stb__match(stb__dout-(stb__in3(1)+1), i[4]+1), i += 5; - else if (*i == 0x04) stb__match(stb__dout-(stb__in3(1)+1), stb__in2(4)+1), i += 6; - } - return i; -} - -stb_uint stb_decompress(stb_uchar *output, stb_uchar *i, stb_uint length) -{ - stb_uint olen; - if (stb__in4(0) != 0x57bC0000) return 0; - if (stb__in4(4) != 0) return 0; // error! stream is > 4GB - olen = stb_decompress_length(i); - stb__barrier2 = i; - stb__barrier3 = i+length; - stb__barrier = output + olen; - stb__barrier4 = output; - i += 16; - - stb__dout = output; - while (1) { - stb_uchar *old_i = i; - i = stb_decompress_token(i); - if (i == old_i) { - if (*i == 0x05 && i[1] == 0xfa) { - assert(stb__dout == output + olen); - if (stb__dout != output + olen) return 0; - if (stb_adler32(1, output, olen) != (stb_uint) stb__in4(2)) - return 0; - return olen; - } else { - assert(0); /* NOTREACHED */ - return 0; - } - } - assert(stb__dout <= output + olen); - if (stb__dout > output + olen) - return 0; - } -} - -char *stb_decompress_fromfile(char *filename, unsigned int *len) -{ - unsigned int n; - char *q; - unsigned char *p; - FILE *f = fopen(filename, "rb"); if (f == NULL) return NULL; - fseek(f, 0, SEEK_END); - n = ftell(f); - fseek(f, 0, SEEK_SET); - p = (unsigned char * ) malloc(n); if (p == NULL) return NULL; - fread(p, 1, n, f); - fclose(f); - if (p == NULL) return NULL; - if (p[0] != 0x57 || p[1] != 0xBc || p[2] || p[3]) { free(p); return NULL; } - q = (char *) malloc(stb_decompress_length(p)+1); - if (!q) { free(p); return NULL; } - *len = stb_decompress((unsigned char *) q, p, n); - if (*len) q[*len] = 0; - free(p); - return q; -} - -#if 0 -// streaming decompressor - -static struct -{ - stb__uchar *in_buffer; - stb__uchar *match; - - stb__uint pending_literals; - stb__uint pending_match; -} xx; - - - -static void stb__match(stb_uchar *data, stb_uint length) -{ - // INVERSE of memmove... write each byte before copying the next... - assert (stb__dout + length <= stb__barrier); - if (stb__dout + length > stb__barrier) { stb__dout += length; return; } - if (data < stb__barrier2) { stb__dout = stb__barrier+1; return; } - while (length--) *stb__dout++ = *data++; -} - -static void stb__lit(stb_uchar *data, stb_uint length) -{ - assert (stb__dout + length <= stb__barrier); - if (stb__dout + length > stb__barrier) { stb__dout += length; return; } - if (data < stb__barrier2) { stb__dout = stb__barrier+1; return; } - memcpy(stb__dout, data, length); - stb__dout += length; -} - -static void sx_match(stb_uchar *data, stb_uint length) -{ - xx.match = data; - xx.pending_match = length; -} - -static void sx_lit(stb_uchar *data, stb_uint length) -{ - xx.pending_lit = length; -} - -static int stb_decompress_token_state(void) -{ - stb__uchar *i = xx.in_buffer; - - if (*i >= 0x20) { // use fewer if's for cases that expand small - if (*i >= 0x80) sx_match(stb__dout-i[1]-1, i[0] - 0x80 + 1), i += 2; - else if (*i >= 0x40) sx_match(stb__dout-(stb__in2(0) - 0x4000 + 1), i[2]+1), i += 3; - else /* *i >= 0x20 */ sx_lit(i+1, i[0] - 0x20 + 1), i += 1; - } else { // more ifs for cases that expand large, since overhead is amortized - if (*i >= 0x18) sx_match(stb__dout-(stb__in3(0) - 0x180000 + 1), i[3]+1), i += 4; - else if (*i >= 0x10) sx_match(stb__dout-(stb__in3(0) - 0x100000 + 1), stb__in2(3)+1), i += 5; - else if (*i >= 0x08) sx_lit(i+2, stb__in2(0) - 0x0800 + 1), i += 2; - else if (*i == 0x07) sx_lit(i+3, stb__in2(1) + 1), i += 3; - else if (*i == 0x06) sx_match(stb__dout-(stb__in3(1)+1), i[4]+1), i += 5; - else if (*i == 0x04) sx_match(stb__dout-(stb__in3(1)+1), stb__in2(4)+1), i += 6; - else return 0; - } - xx.in_buffer = i; - return 1; -} -#endif - - - -//////////////////// compressor /////////////////////// - -static unsigned int stb_matchlen(stb_uchar *m1, stb_uchar *m2, stb_uint maxlen) -{ - stb_uint i; - for (i=0; i < maxlen; ++i) - if (m1[i] != m2[i]) return i; - return i; -} - -// simple implementation that just takes the source data in a big block - -static stb_uchar *stb__out; -static FILE *stb__outfile; -static stb_uint stb__outbytes; - -static void stb__write(unsigned char v) -{ - fputc(v, stb__outfile); - ++stb__outbytes; -} - -#define stb_out(v) (stb__out ? *stb__out++ = (stb_uchar) (v) : stb__write((stb_uchar) (v))) - -static void stb_out2(stb_uint v) -{ - stb_out(v >> 8); - stb_out(v); -} - -static void stb_out3(stb_uint v) { stb_out(v >> 16); stb_out(v >> 8); stb_out(v); } -static void stb_out4(stb_uint v) { stb_out(v >> 24); stb_out(v >> 16); - stb_out(v >> 8 ); stb_out(v); } - -static void outliterals(stb_uchar *in, int numlit) -{ - while (numlit > 65536) { - outliterals(in,65536); - in += 65536; - numlit -= 65536; - } - - if (numlit == 0) ; - else if (numlit <= 32) stb_out (0x000020 + numlit-1); - else if (numlit <= 2048) stb_out2(0x000800 + numlit-1); - else /* numlit <= 65536) */ stb_out3(0x070000 + numlit-1); - - if (stb__out) { - memcpy(stb__out,in,numlit); - stb__out += numlit; - } else - fwrite(in, 1, numlit, stb__outfile); -} - -static int stb__window = 0x40000; // 256K -void stb_compress_window(int z) -{ - if (z >= 0x1000000) z = 0x1000000; // limit of implementation - if (z < 0x100) z = 0x100; // insanely small - stb__window = z; -} - -static int stb_not_crap(int best, int dist) -{ - return ((best > 2 && dist <= 0x00100) - || (best > 5 && dist <= 0x04000) - || (best > 7 && dist <= 0x80000)); -} - -static stb_uint stb__hashsize = 32768; -void stb_compress_hashsize(unsigned int y) -{ - unsigned int z = 1024; - while (z < y) z <<= 1; - stb__hashsize = z >> 2; // pass in bytes, store #pointers -} - -// note that you can play with the hashing functions all you -// want without needing to change the decompressor -#define stb__hc(q,h,c) (((h) << 7) + ((h) >> 25) + q[c]) -#define stb__hc2(q,h,c,d) (((h) << 14) + ((h) >> 18) + (q[c] << 7) + q[d]) -#define stb__hc3(q,c,d,e) ((q[c] << 14) + (q[d] << 7) + q[e]) - -static stb_uint32 stb__running_adler; - -static int stb_compress_chunk(stb_uchar *history, - stb_uchar *start, - stb_uchar *end, - int length, - int *pending_literals, - stb_uchar **chash, - stb_uint mask) -{ - int window = stb__window; - stb_uint match_max; - stb_uchar *lit_start = start - *pending_literals; - stb_uchar *q = start; - - #define STB__SCRAMBLE(h) (((h) + ((h) >> 16)) & mask) - - // stop short of the end so we don't scan off the end doing - // the hashing; this means we won't compress the last few bytes - // unless they were part of something longer - while (q < start+length && q+12 < end) { - int m; - stb_uint h1,h2,h3,h4, h; - stb_uchar *t; - int best = 2, dist=0; - - if (q+65536 > end) - match_max = end-q; - else - match_max = 65536; - - #define stb__nc(b,d) ((d) <= window && ((b) > 9 || stb_not_crap(b,d))) - - #define STB__TRY(t,p) /* avoid retrying a match we already tried */ \ - if (p ? dist != q-t : 1) \ - if ((m = stb_matchlen(t, q, match_max)) > best) \ - if (stb__nc(m,q-(t))) \ - best = m, dist = q - (t) - - // rather than search for all matches, only try 4 candidate locations, - // chosen based on 4 different hash functions of different lengths. - // this strategy is inspired by LZO; hashing is unrolled here using the - // 'hc' macro - h = stb__hc3(q,0, 1, 2); h1 = STB__SCRAMBLE(h); - t = chash[h1]; if (t) STB__TRY(t,0); - h = stb__hc2(q,h, 3, 4); h2 = STB__SCRAMBLE(h); - h = stb__hc2(q,h, 5, 6); t = chash[h2]; if (t) STB__TRY(t,1); - h = stb__hc2(q,h, 7, 8); h3 = STB__SCRAMBLE(h); - h = stb__hc2(q,h, 9,10); t = chash[h3]; if (t) STB__TRY(t,1); - h = stb__hc2(q,h,11,12); h4 = STB__SCRAMBLE(h); - t = chash[h4]; if (t) STB__TRY(t,1); - - // because we use a shared hash table, can only update it - // _after_ we've probed all of them - chash[h1] = chash[h2] = chash[h3] = chash[h4] = q; - - if (best > 2) - assert(dist > 0); - - // see if our best match qualifies - if (best < 3) { // fast path literals - ++q; - } else if (best > 2 && best <= 0x80 && dist <= 0x100) { - outliterals(lit_start, q-lit_start); lit_start = (q += best); - stb_out(0x80 + best-1); - stb_out(dist-1); - } else if (best > 5 && best <= 0x100 && dist <= 0x4000) { - outliterals(lit_start, q-lit_start); lit_start = (q += best); - stb_out2(0x4000 + dist-1); - stb_out(best-1); - } else if (best > 7 && best <= 0x100 && dist <= 0x80000) { - outliterals(lit_start, q-lit_start); lit_start = (q += best); - stb_out3(0x180000 + dist-1); - stb_out(best-1); - } else if (best > 8 && best <= 0x10000 && dist <= 0x80000) { - outliterals(lit_start, q-lit_start); lit_start = (q += best); - stb_out3(0x100000 + dist-1); - stb_out2(best-1); - } else if (best > 9 && dist <= 0x1000000) { - if (best > 65536) best = 65536; - outliterals(lit_start, q-lit_start); lit_start = (q += best); - if (best <= 0x100) { - stb_out(0x06); - stb_out3(dist-1); - stb_out(best-1); - } else { - stb_out(0x04); - stb_out3(dist-1); - stb_out2(best-1); - } - } else { // fallback literals if no match was a balanced tradeoff - ++q; - } - } - - // if we didn't get all the way, add the rest to literals - if (q-start < length) - q = start+length; - - // the literals are everything from lit_start to q - *pending_literals = (q - lit_start); - - stb__running_adler = stb_adler32(stb__running_adler, start, q - start); - return q - start; -} - -static int stb_compress_inner(stb_uchar *input, stb_uint length) -{ - int literals = 0; - stb_uint len,i; - - stb_uchar **chash; - chash = (stb_uchar**) malloc(stb__hashsize * sizeof(stb_uchar*)); - if (chash == NULL) return 0; // failure - for (i=0; i < stb__hashsize; ++i) - chash[i] = NULL; - - // stream signature - stb_out(0x57); stb_out(0xbc); - stb_out2(0); - - stb_out4(0); // 64-bit length requires 32-bit leading 0 - stb_out4(length); - stb_out4(stb__window); - - stb__running_adler = 1; - - len = stb_compress_chunk(input, input, input+length, length, &literals, chash, stb__hashsize-1); - assert(len == length); - - outliterals(input+length - literals, literals); - - free(chash); - - stb_out2(0x05fa); // end opcode - - stb_out4(stb__running_adler); - - return 1; // success -} - -stb_uint stb_compress(stb_uchar *out, stb_uchar *input, stb_uint length) -{ - stb__out = out; - stb__outfile = NULL; - - stb_compress_inner(input, length); - - return stb__out - out; -} - -int stb_compress_tofile(char *filename, char *input, unsigned int length) -{ - //int maxlen = length + 512 + (length >> 2); // total guess - //char *buffer = (char *) malloc(maxlen); - //int blen = stb_compress((stb_uchar*)buffer, (stb_uchar*)input, length); - - stb__out = NULL; - stb__outfile = fopen(filename, "wb"); - if (!stb__outfile) return 0; - - stb__outbytes = 0; - - if (!stb_compress_inner((stb_uchar*)input, length)) - return 0; - - fclose(stb__outfile); - - return stb__outbytes; -} - -int stb_compress_intofile(FILE *f, char *input, unsigned int length) -{ - //int maxlen = length + 512 + (length >> 2); // total guess - //char *buffer = (char*)malloc(maxlen); - //int blen = stb_compress((stb_uchar*)buffer, (stb_uchar*)input, length); - - stb__out = NULL; - stb__outfile = f; - if (!stb__outfile) return 0; - - stb__outbytes = 0; - - if (!stb_compress_inner((stb_uchar*)input, length)) - return 0; - - return stb__outbytes; -} - -////////////////////// streaming I/O version ///////////////////// - - -static size_t stb_out_backpatch_id(void) -{ - if (stb__out) - return (size_t) stb__out; - else - return ftell(stb__outfile); -} - -static void stb_out_backpatch(size_t id, stb_uint value) -{ - stb_uchar data[4] = { value >> 24, value >> 16, value >> 8, value }; - if (stb__out) { - memcpy((void *) id, data, 4); - } else { - stb_uint where = ftell(stb__outfile); - fseek(stb__outfile, id, SEEK_SET); - fwrite(data, 4, 1, stb__outfile); - fseek(stb__outfile, where, SEEK_SET); - } -} - -// ok, the wraparound buffer was a total failure. let's instead -// use a copying-in-place buffer, which lets us share the code. -// This is way less efficient but it'll do for now. - -static struct -{ - stb_uchar *buffer; - int size; // physical size of buffer in bytes - - int valid; // amount of valid data in bytes - int start; // bytes of data already output - - int window; - int fsize; - - int pending_literals; // bytes not-quite output but counted in start - int length_id; - - stb_uint total_bytes; - - stb_uchar **chash; - stb_uint hashmask; -} xtb; - -static int stb_compress_streaming_start(void) -{ - stb_uint i; - xtb.size = stb__window * 3; - xtb.buffer = (stb_uchar*)malloc(xtb.size); - if (!xtb.buffer) return 0; - - xtb.chash = (stb_uchar**)malloc(sizeof(*xtb.chash) * stb__hashsize); - if (!xtb.chash) { - free(xtb.buffer); - return 0; - } - - for (i=0; i < stb__hashsize; ++i) - xtb.chash[i] = NULL; - - xtb.hashmask = stb__hashsize-1; - - xtb.valid = 0; - xtb.start = 0; - xtb.window = stb__window; - xtb.fsize = stb__window; - xtb.pending_literals = 0; - xtb.total_bytes = 0; - - // stream signature - stb_out(0x57); stb_out(0xbc); stb_out2(0); - - stb_out4(0); // 64-bit length requires 32-bit leading 0 - - xtb.length_id = stb_out_backpatch_id(); - stb_out4(0); // we don't know the output length yet - - stb_out4(stb__window); - - stb__running_adler = 1; - - return 1; -} - -static int stb_compress_streaming_end(void) -{ - // flush out any remaining data - stb_compress_chunk(xtb.buffer, xtb.buffer+xtb.start, xtb.buffer+xtb.valid, - xtb.valid-xtb.start, &xtb.pending_literals, xtb.chash, xtb.hashmask); - - // write out pending literals - outliterals(xtb.buffer + xtb.valid - xtb.pending_literals, xtb.pending_literals); - - stb_out2(0x05fa); // end opcode - stb_out4(stb__running_adler); - - stb_out_backpatch(xtb.length_id, xtb.total_bytes); - - free(xtb.buffer); - free(xtb.chash); - return 1; -} - -void stb_write(char *data, int data_len) -{ - stb_uint i; - - // @TODO: fast path for filling the buffer and doing nothing else - // if (xtb.valid + data_len < xtb.size) - - xtb.total_bytes += data_len; - - while (data_len) { - // fill buffer - if (xtb.valid < xtb.size) { - int amt = xtb.size - xtb.valid; - if (data_len < amt) amt = data_len; - memcpy(xtb.buffer + xtb.valid, data, amt); - data_len -= amt; - data += amt; - xtb.valid += amt; - } - if (xtb.valid < xtb.size) - return; - - // at this point, the buffer is full - - // if we can process some data, go for it; make sure - // we leave an 'fsize's worth of data, though - if (xtb.start + xtb.fsize < xtb.valid) { - int amount = (xtb.valid - xtb.fsize) - xtb.start; - int n; - assert(amount > 0); - n = stb_compress_chunk(xtb.buffer, xtb.buffer + xtb.start, xtb.buffer + xtb.valid, - amount, &xtb.pending_literals, xtb.chash, xtb.hashmask); - xtb.start += n; - } - - assert(xtb.start + xtb.fsize >= xtb.valid); - // at this point, our future size is too small, so we - // need to flush some history. we, in fact, flush exactly - // one window's worth of history - - { - int flush = xtb.window; - assert(xtb.start >= flush); - assert(xtb.valid >= flush); - - // if 'pending literals' extends back into the shift region, - // write them out - if (xtb.start - xtb.pending_literals < flush) { - outliterals(xtb.buffer + xtb.start - xtb.pending_literals, xtb.pending_literals); - xtb.pending_literals = 0; - } - - // now shift the window - memmove(xtb.buffer, xtb.buffer + flush, xtb.valid - flush); - xtb.start -= flush; - xtb.valid -= flush; - - for (i=0; i <= xtb.hashmask; ++i) - if (xtb.chash[i] < xtb.buffer + flush) - xtb.chash[i] = NULL; - else - xtb.chash[i] -= flush; - } - // and now that we've made room for more data, go back to the top - } -} - -int stb_compress_stream_start(FILE *f) -{ - stb__out = NULL; - stb__outfile = f; - - if (f == NULL) - return 0; - - if (!stb_compress_streaming_start()) - return 0; - - return 1; -} - -void stb_compress_stream_end(int close) -{ - stb_compress_streaming_end(); - if (close && stb__outfile) { - fclose(stb__outfile); - } -} - -#endif // STB_DEFINE - -////////////////////////////////////////////////////////////////////////////// -// -// File abstraction... tired of not having this... we can write -// compressors to be layers over these that auto-close their children. - - -typedef struct stbfile -{ - int (*getbyte)(struct stbfile *); // -1 on EOF - unsigned int (*getdata)(struct stbfile *, void *block, unsigned int len); - - int (*putbyte)(struct stbfile *, int byte); - unsigned int (*putdata)(struct stbfile *, void *block, unsigned int len); - - unsigned int (*size)(struct stbfile *); - - unsigned int (*tell)(struct stbfile *); - void (*backpatch)(struct stbfile *, unsigned int tell, void *block, unsigned int len); - - void (*close)(struct stbfile *); - - FILE *f; // file to fread/fwrite - unsigned char *buffer; // input/output buffer - unsigned char *indata, *inend; // input buffer - union { - int various; - void *ptr; - }; -} stbfile; - -STB_EXTERN unsigned int stb_getc(stbfile *f); // read -STB_EXTERN int stb_putc(stbfile *f, int ch); // write -STB_EXTERN unsigned int stb_getdata(stbfile *f, void *buffer, unsigned int len); // read -STB_EXTERN unsigned int stb_putdata(stbfile *f, void *buffer, unsigned int len); // write -STB_EXTERN unsigned int stb_tell(stbfile *f); // read -STB_EXTERN unsigned int stb_size(stbfile *f); // read/write -STB_EXTERN void stb_backpatch(stbfile *f, unsigned int tell, void *buffer, unsigned int len); // write - -#ifdef STB_DEFINE - -unsigned int stb_getc(stbfile *f) { return f->getbyte(f); } -int stb_putc(stbfile *f, int ch) { return f->putbyte(f, ch); } - -unsigned int stb_getdata(stbfile *f, void *buffer, unsigned int len) -{ - return f->getdata(f, buffer, len); -} -unsigned int stb_putdata(stbfile *f, void *buffer, unsigned int len) -{ - return f->putdata(f, buffer, len); -} -void stb_close(stbfile *f) -{ - f->close(f); - free(f); -} -unsigned int stb_tell(stbfile *f) { return f->tell(f); } -unsigned int stb_size(stbfile *f) { return f->size(f); } -void stb_backpatch(stbfile *f, unsigned int tell, void *buffer, unsigned int len) -{ - f->backpatch(f,tell,buffer,len); -} - -// FILE * implementation -static int stb__fgetbyte(stbfile *f) { return fgetc(f->f); } -static int stb__fputbyte(stbfile *f, int ch) { return fputc(ch, f->f)==0; } -static unsigned int stb__fgetdata(stbfile *f, void *buffer, unsigned int len) { return fread(buffer,1,len,f->f); } -static unsigned int stb__fputdata(stbfile *f, void *buffer, unsigned int len) { return fwrite(buffer,1,len,f->f); } -static unsigned int stb__fsize(stbfile *f) { return stb_filelen(f->f); } -static unsigned int stb__ftell(stbfile *f) { return ftell(f->f); } -static void stb__fbackpatch(stbfile *f, unsigned int where, void *buffer, unsigned int len) -{ - fseek(f->f, where, SEEK_SET); - fwrite(buffer, 1, len, f->f); - fseek(f->f, 0, SEEK_END); -} -static void stb__fclose(stbfile *f) { fclose(f->f); } - -stbfile *stb_openf(FILE *f) -{ - stbfile m = { stb__fgetbyte, stb__fgetdata, - stb__fputbyte, stb__fputdata, - stb__fsize, stb__ftell, stb__fbackpatch, stb__fclose, - 0,0,0, }; - stbfile *z = (stbfile *) malloc(sizeof(*z)); - if (z) { - *z = m; - z->f = f; - } - return z; -} - -static int stb__nogetbyte(stbfile *f) { assert(0); return -1; } -static unsigned int stb__nogetdata(stbfile *f, void *buffer, unsigned int len) { assert(0); return 0; } -static int stb__noputbyte(stbfile *f, int ch) { assert(0); return 0; } -static unsigned int stb__noputdata(stbfile *f, void *buffer, unsigned int len) { assert(0); return 0; } -static void stb__nobackpatch(stbfile *f, unsigned int where, void *buffer, unsigned int len) { assert(0); } - -static int stb__bgetbyte(stbfile *s) -{ - if (s->indata < s->inend) - return *s->indata++; - else - return -1; -} - -static unsigned int stb__bgetdata(stbfile *s, void *buffer, unsigned int len) -{ - if (s->indata + len > s->inend) - len = s->inend - s->indata; - memcpy(buffer, s->indata, len); - s->indata += len; - return len; -} -static unsigned int stb__bsize(stbfile *s) { return s->inend - s->buffer; } -static unsigned int stb__btell(stbfile *s) { return s->indata - s->buffer; } - -static void stb__bclose(stbfile *s) -{ - if (s->various) - free(s->buffer); -} - -stbfile *stb_open_inbuffer(void *buffer, unsigned int len) -{ - stbfile m = { stb__bgetbyte, stb__bgetdata, - stb__noputbyte, stb__noputdata, - stb__bsize, stb__btell, stb__nobackpatch, stb__bclose }; - stbfile *z = (stbfile *) malloc(sizeof(*z)); - if (z) { - *z = m; - z->buffer = (unsigned char *) buffer; - z->indata = z->buffer; - z->inend = z->indata + len; - } - return z; -} - -stbfile *stb_open_inbuffer_free(void *buffer, unsigned int len) -{ - stbfile *z = stb_open_inbuffer(buffer, len); - if (z) - z->various = 1; // free - return z; -} - -#ifndef STB_VERSION -// if we've been cut-and-pasted elsewhere, you get a limited -// version of stb_open, without the 'k' flag and utf8 support -static void stb__fclose2(stbfile *f) -{ - fclose(f->f); -} - -stbfile *stb_open(char *filename, char *mode) -{ - FILE *f = fopen(filename, mode); - stbfile *s; - if (f == NULL) return NULL; - s = stb_openf(f); - if (s) - s->close = stb__fclose2; - return s; -} -#else -// the full version depends on some code in stb.h; this -// also includes the memory buffer output format implemented with stb_arr -static void stb__fclose2(stbfile *f) -{ - stb_fclose(f->f, f->various); -} - -stbfile *stb_open(char *filename, char *mode) -{ - FILE *f = stb_fopen(filename, mode[0] == 'k' ? mode+1 : mode); - stbfile *s; - if (f == NULL) return NULL; - s = stb_openf(f); - if (s) { - s->close = stb__fclose2; - s->various = mode[0] == 'k' ? stb_keep_if_different : stb_keep_yes; - } - return s; -} - -static int stb__aputbyte(stbfile *f, int ch) -{ - stb_arr_push(f->buffer, ch); - return 1; -} -static unsigned int stb__aputdata(stbfile *f, void *data, unsigned int len) -{ - memcpy(stb_arr_addn(f->buffer, (int) len), data, len); - return len; -} -static unsigned int stb__asize(stbfile *f) { return stb_arr_len(f->buffer); } -static void stb__abackpatch(stbfile *f, unsigned int where, void *data, unsigned int len) -{ - memcpy(f->buffer+where, data, len); -} -static void stb__aclose(stbfile *f) -{ - *(unsigned char **) f->ptr = f->buffer; -} - -stbfile *stb_open_outbuffer(unsigned char **update_on_close) -{ - stbfile m = { stb__nogetbyte, stb__nogetdata, - stb__aputbyte, stb__aputdata, - stb__asize, stb__asize, stb__abackpatch, stb__aclose }; - stbfile *z = (stbfile *) malloc(sizeof(*z)); - if (z) { - z->ptr = update_on_close; - *z = m; - } - return z; -} -#endif -#endif - - -////////////////////////////////////////////////////////////////////////////// -// -// Arithmetic coder... based on cbloom's notes on the subject, should be -// less code than a huffman code. - -typedef struct -{ - unsigned int range_low; - unsigned int range_high; - unsigned int code, range; // decode - int buffered_u8; - int pending_ffs; - stbfile *output; -} stb_arith; - -STB_EXTERN void stb_arith_init_encode(stb_arith *a, stbfile *out); -STB_EXTERN void stb_arith_init_decode(stb_arith *a, stbfile *in); -STB_EXTERN stbfile *stb_arith_encode_close(stb_arith *a); -STB_EXTERN stbfile *stb_arith_decode_close(stb_arith *a); - -STB_EXTERN void stb_arith_encode(stb_arith *a, unsigned int totalfreq, unsigned int freq, unsigned int cumfreq); -STB_EXTERN void stb_arith_encode_log2(stb_arith *a, unsigned int totalfreq2, unsigned int freq, unsigned int cumfreq); -STB_EXTERN unsigned int stb_arith_decode_value(stb_arith *a, unsigned int totalfreq); -STB_EXTERN void stb_arith_decode_advance(stb_arith *a, unsigned int totalfreq, unsigned int freq, unsigned int cumfreq); -STB_EXTERN unsigned int stb_arith_decode_value_log2(stb_arith *a, unsigned int totalfreq2); -STB_EXTERN void stb_arith_decode_advance_log2(stb_arith *a, unsigned int totalfreq2, unsigned int freq, unsigned int cumfreq); - -STB_EXTERN void stb_arith_encode_byte(stb_arith *a, int byte); -STB_EXTERN int stb_arith_decode_byte(stb_arith *a); - -// this is a memory-inefficient way of doing things, but it's -// fast(?) and simple -typedef struct -{ - unsigned short cumfreq; - unsigned short samples; -} stb_arith_symstate_item; - -typedef struct -{ - int num_sym; - unsigned int pow2; - int countdown; - stb_arith_symstate_item data[1]; -} stb_arith_symstate; - -#ifdef STB_DEFINE -void stb_arith_init_encode(stb_arith *a, stbfile *out) -{ - a->range_low = 0; - a->range_high = 0xffffffff; - a->pending_ffs = -1; // means no buffered character currently, to speed up normal case - a->output = out; -} - -static void stb__arith_carry(stb_arith *a) -{ - int i; - assert(a->pending_ffs != -1); // can't carry with no data - stb_putc(a->output, a->buffered_u8); - for (i=0; i < a->pending_ffs; ++i) - stb_putc(a->output, 0); -} - -static void stb__arith_putbyte(stb_arith *a, int byte) -{ - if (a->pending_ffs) { - if (a->pending_ffs == -1) { // means no buffered data; encoded for fast path efficiency - if (byte == 0xff) - stb_putc(a->output, byte); // just write it immediately - else { - a->buffered_u8 = byte; - a->pending_ffs = 0; - } - } else if (byte == 0xff) { - ++a->pending_ffs; - } else { - int i; - stb_putc(a->output, a->buffered_u8); - for (i=0; i < a->pending_ffs; ++i) - stb_putc(a->output, 0xff); - } - } else if (byte == 0xff) { - ++a->pending_ffs; - } else { - // fast path - stb_putc(a->output, a->buffered_u8); - a->buffered_u8 = byte; - } -} - -static void stb__arith_flush(stb_arith *a) -{ - if (a->pending_ffs >= 0) { - int i; - stb_putc(a->output, a->buffered_u8); - for (i=0; i < a->pending_ffs; ++i) - stb_putc(a->output, 0xff); - } -} - -static void stb__renorm_encoder(stb_arith *a) -{ - stb__arith_putbyte(a, a->range_low >> 24); - a->range_low <<= 8; - a->range_high = (a->range_high << 8) | 0xff; -} - -static void stb__renorm_decoder(stb_arith *a) -{ - int c = stb_getc(a->output); - a->code = (a->code << 8) + (c >= 0 ? c : 0); // if EOF, insert 0 -} - -void stb_arith_encode(stb_arith *a, unsigned int totalfreq, unsigned int freq, unsigned int cumfreq) -{ - unsigned int range = a->range_high - a->range_low; - unsigned int old = a->range_low; - range /= totalfreq; - a->range_low += range * cumfreq; - a->range_high = a->range_low + range*freq; - if (a->range_low < old) - stb__arith_carry(a); - while (a->range_high - a->range_low < 0x1000000) - stb__renorm_encoder(a); -} - -void stb_arith_encode_log2(stb_arith *a, unsigned int totalfreq2, unsigned int freq, unsigned int cumfreq) -{ - unsigned int range = a->range_high - a->range_low; - unsigned int old = a->range_low; - range >>= totalfreq2; - a->range_low += range * cumfreq; - a->range_high = a->range_low + range*freq; - if (a->range_low < old) - stb__arith_carry(a); - while (a->range_high - a->range_low < 0x1000000) - stb__renorm_encoder(a); -} - -unsigned int stb_arith_decode_value(stb_arith *a, unsigned int totalfreq) -{ - unsigned int freqsize = a->range / totalfreq; - unsigned int z = a->code / freqsize; - return z >= totalfreq ? totalfreq-1 : z; -} - -void stb_arith_decode_advance(stb_arith *a, unsigned int totalfreq, unsigned int freq, unsigned int cumfreq) -{ - unsigned int freqsize = a->range / totalfreq; // @OPTIMIZE, share with above divide somehow? - a->code -= freqsize * cumfreq; - a->range = freqsize * freq; - while (a->range < 0x1000000) - stb__renorm_decoder(a); -} - -unsigned int stb_arith_decode_value_log2(stb_arith *a, unsigned int totalfreq2) -{ - unsigned int freqsize = a->range >> totalfreq2; - unsigned int z = a->code / freqsize; - return z >= (1U<range >> totalfreq2; - a->code -= freqsize * cumfreq; - a->range = freqsize * freq; - while (a->range < 0x1000000) - stb__renorm_decoder(a); -} - -stbfile *stb_arith_encode_close(stb_arith *a) -{ - // put exactly as many bytes as we'll read, so we can turn on/off arithmetic coding in a stream - stb__arith_putbyte(a, a->range_low >> 24); - stb__arith_putbyte(a, a->range_low >> 16); - stb__arith_putbyte(a, a->range_low >> 8); - stb__arith_putbyte(a, a->range_low >> 0); - stb__arith_flush(a); - return a->output; -} - -stbfile *stb_arith_decode_close(stb_arith *a) -{ - return a->output; -} - -// this is a simple power-of-two based model -- using -// power of two means we need one divide per decode, -// not two. -#define POW2_LIMIT 12 -stb_arith_symstate *stb_arith_state_create(int num_sym) -{ - stb_arith_symstate *s = (stb_arith_symstate *) malloc(sizeof(*s) + (num_sym-1) * sizeof(s->data[0])); - if (s) { - int i, cf, cf_next, next; - int start_freq, extra; - s->num_sym = num_sym; - s->pow2 = 4; - while (s->pow2 < 15 && (1 << s->pow2) < 3*num_sym) { - ++s->pow2; - } - start_freq = (1 << s->pow2) / num_sym; - assert(start_freq >= 1); - extra = (1 << s->pow2) % num_sym; - // now set up the initial stats - - if (s->pow2 < POW2_LIMIT) - next = 0; - else - next = 1; - - cf = cf_next = 0; - for (i=0; i < extra; ++i) { - s->data[i].cumfreq = cf; - s->data[i].samples = next; - cf += start_freq+1; - cf_next += next; - } - for (; i < num_sym; ++i) { - s->data[i].cumfreq = cf; - s->data[i].samples = next; - cf += start_freq; - cf_next += next; - } - assert(cf == (1 << s->pow2)); - // now, how long should we go until we have 2 << s->pow2 samples? - s->countdown = (2 << s->pow2) - cf - cf_next; - } - return s; -} - -static void stb_arith_state_rescale(stb_arith_symstate *s) -{ - if (s->pow2 < POW2_LIMIT) { - int pcf, cf, cf_next, next, i; - ++s->pow2; - if (s->pow2 < POW2_LIMIT) - next = 0; - else - next = 1; - cf = cf_next = 0; - pcf = 0; - for (i=0; i < s->num_sym; ++i) { - int sample = s->data[i].cumfreq - pcf + s->data[i].samples; - s->data[i].cumfreq = cf; - cf += sample; - s->data[i].samples = next; - cf_next += next; - } - assert(cf == (1 << s->pow2)); - s->countdown = (2 << s->pow2) - cf - cf_next; - } else { - int pcf, cf, cf_next, i; - cf = cf_next = 0; - pcf = 0; - for (i=0; i < s->num_sym; ++i) { - int sample = (s->data[i].cumfreq - pcf + s->data[i].samples) >> 1; - s->data[i].cumfreq = cf; - cf += sample; - s->data[i].samples = 1; - cf_next += 1; - } - assert(cf == (1 << s->pow2)); // this isn't necessarily true, due to rounding down! - s->countdown = (2 << s->pow2) - cf - cf_next; - } -} - -void stb_arith_encode_byte(stb_arith *a, int byte) -{ -} - -int stb_arith_decode_byte(stb_arith *a) -{ - return -1; -} -#endif - -////////////////////////////////////////////////////////////////////////////// -// -// Threads -// - -#ifndef _WIN32 -#ifdef STB_THREADS -#error "threads not implemented except for Windows" -#endif -#endif - -// call this function to free any global variables for memory testing -STB_EXTERN void stb_thread_cleanup(void); - -typedef void * (*stb_thread_func)(void *); - -// do not rely on these types, this is an implementation detail. -// compare against STB_THREAD_NULL and ST_SEMAPHORE_NULL -typedef void *stb_thread; -typedef void *stb_semaphore; -typedef void *stb_mutex; -typedef struct stb__sync *stb_sync; - -#define STB_SEMAPHORE_NULL NULL -#define STB_THREAD_NULL NULL -#define STB_MUTEX_NULL NULL -#define STB_SYNC_NULL NULL - -// get the number of processors (limited to those in the affinity mask for this process). -STB_EXTERN int stb_processor_count(void); -// force to run on a single core -- needed for RDTSC to work, e.g. for iprof -STB_EXTERN void stb_force_uniprocessor(void); - -// stb_work functions: queue up work to be done by some worker threads - -// set number of threads to serve the queue; you can change this on the fly, -// but if you decrease it, it won't decrease until things currently on the -// queue are finished -STB_EXTERN void stb_work_numthreads(int n); -// set maximum number of units in the queue; you can only set this BEFORE running any work functions -STB_EXTERN int stb_work_maxunits(int n); -// enqueue some work to be done (can do this from any thread, or even from a piece of work); -// return value of f is stored in *return_code if non-NULL -STB_EXTERN int stb_work(stb_thread_func f, void *d, volatile void **return_code); -// as above, but stb_sync_reach is called on 'rel' after work is complete -STB_EXTERN int stb_work_reach(stb_thread_func f, void *d, volatile void **return_code, stb_sync rel); - - -// necessary to call this when using volatile to order writes/reads -STB_EXTERN void stb_barrier(void); - -// support for independent queues with their own threads - -typedef struct stb__workqueue stb_workqueue; - -STB_EXTERN stb_workqueue*stb_workq_new(int numthreads, int max_units); -STB_EXTERN stb_workqueue*stb_workq_new_flags(int numthreads, int max_units, int no_add_mutex, int no_remove_mutex); -STB_EXTERN void stb_workq_delete(stb_workqueue *q); -STB_EXTERN void stb_workq_numthreads(stb_workqueue *q, int n); -STB_EXTERN int stb_workq(stb_workqueue *q, stb_thread_func f, void *d, volatile void **return_code); -STB_EXTERN int stb_workq_reach(stb_workqueue *q, stb_thread_func f, void *d, volatile void **return_code, stb_sync rel); -STB_EXTERN int stb_workq_length(stb_workqueue *q); - -STB_EXTERN stb_thread stb_create_thread (stb_thread_func f, void *d); -STB_EXTERN stb_thread stb_create_thread2(stb_thread_func f, void *d, volatile void **return_code, stb_semaphore rel); -STB_EXTERN void stb_destroy_thread(stb_thread t); - -STB_EXTERN stb_semaphore stb_sem_new(int max_val); -STB_EXTERN stb_semaphore stb_sem_new_extra(int max_val, int start_val); -STB_EXTERN void stb_sem_delete (stb_semaphore s); -STB_EXTERN void stb_sem_waitfor(stb_semaphore s); -STB_EXTERN void stb_sem_release(stb_semaphore s); - -STB_EXTERN stb_mutex stb_mutex_new(void); -STB_EXTERN void stb_mutex_delete(stb_mutex m); -STB_EXTERN void stb_mutex_begin(stb_mutex m); -STB_EXTERN void stb_mutex_end(stb_mutex m); - -STB_EXTERN stb_sync stb_sync_new(void); -STB_EXTERN void stb_sync_delete(stb_sync s); -STB_EXTERN int stb_sync_set_target(stb_sync s, int count); -STB_EXTERN void stb_sync_reach_and_wait(stb_sync s); // wait for 'target' reachers -STB_EXTERN int stb_sync_reach(stb_sync s); - -typedef struct stb__threadqueue stb_threadqueue; -#define STB_THREADQ_DYNAMIC 0 -STB_EXTERN stb_threadqueue *stb_threadq_new(int item_size, int num_items, int many_add, int many_remove); -STB_EXTERN void stb_threadq_delete(stb_threadqueue *tq); -STB_EXTERN int stb_threadq_get(stb_threadqueue *tq, void *output); -STB_EXTERN void stb_threadq_get_block(stb_threadqueue *tq, void *output); -STB_EXTERN int stb_threadq_add(stb_threadqueue *tq, void *input); -// can return FALSE if STB_THREADQ_DYNAMIC and attempt to grow fails -STB_EXTERN int stb_threadq_add_block(stb_threadqueue *tq, void *input); - -#ifdef STB_THREADS -#ifdef STB_DEFINE - -typedef struct -{ - stb_thread_func f; - void *d; - volatile void **return_val; - stb_semaphore sem; -} stb__thread; - -// this is initialized along all possible paths to create threads, therefore -// it's always initialized before any other threads are create, therefore -// it's free of races AS LONG AS you only create threads through stb_* -static stb_mutex stb__threadmutex, stb__workmutex; - -static void stb__threadmutex_init(void) -{ - if (stb__threadmutex == STB_SEMAPHORE_NULL) { - stb__threadmutex = stb_mutex_new(); - stb__workmutex = stb_mutex_new(); - } -} - -#ifdef STB_THREAD_TEST -volatile float stb__t1=1, stb__t2; - -static void stb__wait(int n) -{ - float z = 0; - int i; - for (i=0; i < n; ++i) - z += 1 / (stb__t1+i); - stb__t2 = z; -} -#else -#define stb__wait(x) -#endif - -#ifdef _WIN32 - -// avoid including windows.h -- note that our definitions aren't -// exactly the same (we don't define the security descriptor struct) -// so if you want to include windows.h, make sure you do it first. -#include - -#ifndef _WINDOWS_ // check windows.h guard -#define STB__IMPORT STB_EXTERN __declspec(dllimport) -#define STB__DW unsigned long - -STB__IMPORT int __stdcall TerminateThread(void *, STB__DW); -STB__IMPORT void * __stdcall CreateSemaphoreA(void *sec, long,long,char*); -STB__IMPORT int __stdcall CloseHandle(void *); -STB__IMPORT STB__DW __stdcall WaitForSingleObject(void *, STB__DW); -STB__IMPORT int __stdcall ReleaseSemaphore(void *, long, long *); -STB__IMPORT void __stdcall Sleep(STB__DW); -#endif - -// necessary to call this when using volatile to order writes/reads -void stb_barrier(void) -{ - #ifdef MemoryBarrier - MemoryBarrier(); - #else - long temp; - __asm xchg temp,eax; - #endif -} - -static void stb__thread_run(void *t) -{ - void *res; - stb__thread info = * (stb__thread *) t; - free(t); - res = info.f(info.d); - if (info.return_val) - *info.return_val = res; - if (info.sem != STB_SEMAPHORE_NULL) - stb_sem_release(info.sem); -} - -static stb_thread stb_create_thread_raw(stb_thread_func f, void *d, volatile void **return_code, stb_semaphore rel) -{ -#ifdef _MT -#if defined(STB_FASTMALLOC) && !defined(STB_FASTMALLOC_ITS_OKAY_I_ONLY_MALLOC_IN_ONE_THREAD) - stb_fatal("Error! Cannot use STB_FASTMALLOC with threads.\n"); - return STB_THREAD_NULL; -#else - unsigned long id; - stb__thread *data = (stb__thread *) malloc(sizeof(*data)); - if (!data) return NULL; - stb__threadmutex_init(); - data->f = f; - data->d = d; - data->return_val = return_code; - data->sem = rel; - id = _beginthread(stb__thread_run, 0, data); - if (id == -1) return NULL; - return (void *) id; -#endif -#else -#ifdef STB_NO_STB_STRINGS - stb_fatal("Invalid compilation"); -#else - stb_fatal("Must compile mult-threaded to use stb_thread/stb_work."); -#endif - return NULL; -#endif -} - -// trivial win32 wrappers -void stb_destroy_thread(stb_thread t) { TerminateThread(t,0); } -stb_semaphore stb_sem_new(int maxv) {return CreateSemaphoreA(NULL,0,maxv,NULL); } -stb_semaphore stb_sem_new_extra(int maxv,int start){return CreateSemaphoreA(NULL,start,maxv,NULL); } -void stb_sem_delete(stb_semaphore s) { if (s != NULL) CloseHandle(s); } -void stb_sem_waitfor(stb_semaphore s) { WaitForSingleObject(s, 0xffffffff); } // INFINITE -void stb_sem_release(stb_semaphore s) { ReleaseSemaphore(s,1,NULL); } -static void stb__thread_sleep(int ms) { Sleep(ms); } - -#ifndef _WINDOWS_ -STB__IMPORT int __stdcall GetProcessAffinityMask(void *, STB__DW *, STB__DW *); -STB__IMPORT void * __stdcall GetCurrentProcess(void); -STB__IMPORT int __stdcall SetProcessAffinityMask(void *, STB__DW); -#endif - -int stb_processor_count(void) -{ - unsigned long proc,sys; - GetProcessAffinityMask(GetCurrentProcess(), &proc, &sys); - return stb_bitcount(proc); -} - -void stb_force_uniprocessor(void) -{ - unsigned long proc,sys; - GetProcessAffinityMask(GetCurrentProcess(), &proc, &sys); - if (stb_bitcount(proc) > 1) { - int z; - for (z=0; z < 32; ++z) - if (proc & (1 << z)) - break; - if (z < 32) { - proc = 1 << z; - SetProcessAffinityMask(GetCurrentProcess(), proc); - } - } -} - -#ifdef _WINDOWS_ -#define STB_MUTEX_NATIVE -void *stb_mutex_new(void) -{ - CRITICAL_SECTION *p = (CRITICAL_SECTION *) malloc(sizeof(*p)); - if (p) -#if _WIN32_WINNT >= 0x0500 - InitializeCriticalSectionAndSpinCount(p, 500); -#else - InitializeCriticalSection(p); -#endif - return p; -} - -void stb_mutex_delete(void *p) -{ - if (p) { - DeleteCriticalSection((CRITICAL_SECTION *) p); - free(p); - } -} - -void stb_mutex_begin(void *p) -{ - stb__wait(500); - if (p) - EnterCriticalSection((CRITICAL_SECTION *) p); -} - -void stb_mutex_end(void *p) -{ - if (p) - LeaveCriticalSection((CRITICAL_SECTION *) p); - stb__wait(500); -} -#endif // _WINDOWS_ - -#if 0 -// for future reference, -// InterlockedCompareExchange for x86: - int cas64_mp(void * dest, void * xcmp, void * xxchg) { - __asm - { - mov esi, [xxchg] ; exchange - mov ebx, [esi + 0] - mov ecx, [esi + 4] - - mov esi, [xcmp] ; comparand - mov eax, [esi + 0] - mov edx, [esi + 4] - - mov edi, [dest] ; destination - lock cmpxchg8b [edi] - jz yyyy; - - mov [esi + 0], eax; - mov [esi + 4], edx; - -yyyy: - xor eax, eax; - setz al; - }; - -inline unsigned __int64 _InterlockedCompareExchange64(volatile unsigned __int64 *dest - ,unsigned __int64 exchange - ,unsigned __int64 comperand) -{ - //value returned in eax::edx - __asm { - lea esi,comperand; - lea edi,exchange; - - mov eax,[esi]; - mov edx,4[esi]; - mov ebx,[edi]; - mov ecx,4[edi]; - mov esi,dest; - lock CMPXCHG8B [esi]; - } -#endif // #if 0 - -#endif // _WIN32 - -stb_thread stb_create_thread2(stb_thread_func f, void *d, volatile void **return_code, stb_semaphore rel) -{ - return stb_create_thread_raw(f,d,return_code,rel); -} - -stb_thread stb_create_thread(stb_thread_func f, void *d) -{ - return stb_create_thread2(f,d,NULL,STB_SEMAPHORE_NULL); -} - -// mutex implemented by wrapping semaphore -#ifndef STB_MUTEX_NATIVE -stb_mutex stb_mutex_new(void) { return stb_sem_new_extra(1,1); } -void stb_mutex_delete(stb_mutex m) { stb_sem_delete (m); } -void stb_mutex_begin(stb_mutex m) { stb__wait(500); if (m) stb_sem_waitfor(m); } -void stb_mutex_end(stb_mutex m) { if (m) stb_sem_release(m); stb__wait(500); } -#endif - -// thread merge operation -struct stb__sync -{ - int target; // target number of threads to hit it - int sofar; // total threads that hit it - int waiting; // total threads waiting - - stb_mutex start; // mutex to prevent starting again before finishing previous - stb_mutex mutex; // mutex while tweaking state - stb_semaphore release; // semaphore wake up waiting threads - // we have to wake them up one at a time, rather than using a single release - // call, because win32 semaphores don't let you dynamically change the max count! -}; - -stb_sync stb_sync_new(void) -{ - stb_sync s = (stb_sync) malloc(sizeof(*s)); - if (!s) return s; - - s->target = s->sofar = s->waiting = 0; - s->mutex = stb_mutex_new(); - s->start = stb_mutex_new(); - s->release = stb_sem_new(1); - if (s->mutex == STB_MUTEX_NULL || s->release == STB_SEMAPHORE_NULL || s->start == STB_MUTEX_NULL) { - stb_mutex_delete(s->mutex); - stb_mutex_delete(s->mutex); - stb_sem_delete(s->release); - free(s); - return NULL; - } - return s; -} - -void stb_sync_delete(stb_sync s) -{ - if (s->waiting) { - // it's bad to delete while there are threads waiting! - // shall we wait for them to reach, or just bail? just bail - assert(0); - } - stb_mutex_delete(s->mutex); - stb_mutex_delete(s->release); - free(s); -} - -int stb_sync_set_target(stb_sync s, int count) -{ - // don't allow setting a target until the last one is fully released; - // note that this can lead to inefficient pipelining, and maybe we'd - // be better off ping-ponging between two internal syncs? - // I tried seeing how often this happened using TryEnterCriticalSection - // and could _never_ get it to happen in imv(stb), even with more threads - // than processors. So who knows! - stb_mutex_begin(s->start); - - // this mutex is pointless, since it's not valid for threads - // to call reach() before anyone calls set_target() anyway - stb_mutex_begin(s->mutex); - - assert(s->target == 0); // enforced by start mutex - s->target = count; - s->sofar = 0; - s->waiting = 0; - stb_mutex_end(s->mutex); - return STB_TRUE; -} - -void stb__sync_release(stb_sync s) -{ - if (s->waiting) - stb_sem_release(s->release); - else { - s->target = 0; - stb_mutex_end(s->start); - } -} - -int stb_sync_reach(stb_sync s) -{ - int n; - stb_mutex_begin(s->mutex); - assert(s->sofar < s->target); - n = ++s->sofar; // record this value to avoid possible race if we did 'return s->sofar'; - if (s->sofar == s->target) - stb__sync_release(s); - stb_mutex_end(s->mutex); - return n; -} - -void stb_sync_reach_and_wait(stb_sync s) -{ - stb_mutex_begin(s->mutex); - assert(s->sofar < s->target); - ++s->sofar; - if (s->sofar == s->target) { - stb__sync_release(s); - stb_mutex_end(s->mutex); - } else { - ++s->waiting; // we're waiting, so one more waiter - stb_mutex_end(s->mutex); // release the mutex to other threads - - stb_sem_waitfor(s->release); // wait for merge completion - - stb_mutex_begin(s->mutex); // on merge completion, grab the mutex - --s->waiting; // we're done waiting - stb__sync_release(s); // restart the next waiter - stb_mutex_end(s->mutex); // and now we're done - // this ends the same as the first case, but it's a lot - // clearer to understand without sharing the code - } -} - -struct stb__threadqueue -{ - stb_mutex add, remove; - stb_semaphore nonempty, nonfull; - int head_blockers; // number of threads blocking--used to know whether to release(avail) - int tail_blockers; - int head, tail, array_size, growable; - int item_size; - char *data; -}; - -static int stb__tq_wrap(volatile stb_threadqueue *z, int p) -{ - if (p == z->array_size) - return p - z->array_size; - else - return p; -} - -int stb__threadq_get_raw(stb_threadqueue *tq2, void *output, int block) -{ - volatile stb_threadqueue *tq = (volatile stb_threadqueue *) tq2; - if (tq->head == tq->tail && !block) return 0; - - stb_mutex_begin(tq->remove); - - while (tq->head == tq->tail) { - if (!block) { - stb_mutex_end(tq->remove); - return 0; - } - ++tq->head_blockers; - stb_mutex_end(tq->remove); - - stb_sem_waitfor(tq->nonempty); - - stb_mutex_begin(tq->remove); - --tq->head_blockers; - } - - memcpy(output, tq->data + tq->head*tq->item_size, tq->item_size); - stb_barrier(); - tq->head = stb__tq_wrap(tq, tq->head+1); - - stb_sem_release(tq->nonfull); - if (tq->head_blockers) // can't check if actually non-empty due to race? - stb_sem_release(tq->nonempty); // if there are other blockers, wake one - - stb_mutex_end(tq->remove); - return STB_TRUE; -} - -int stb__threadq_grow(volatile stb_threadqueue *tq) -{ - int n; - char *p; - assert(tq->remove != STB_MUTEX_NULL); // must have this to allow growth! - stb_mutex_begin(tq->remove); - - n = tq->array_size * 2; - p = (char *) realloc(tq->data, n * tq->item_size); - if (p == NULL) { - stb_mutex_end(tq->remove); - stb_mutex_end(tq->add); - return STB_FALSE; - } - if (tq->tail < tq->head) { - memcpy(p + tq->array_size * tq->item_size, p, tq->tail * tq->item_size); - tq->tail += tq->array_size; - } - tq->data = p; - tq->array_size = n; - - stb_mutex_end(tq->remove); - return STB_TRUE; -} - -int stb__threadq_add_raw(stb_threadqueue *tq2, void *input, int block) -{ - int tail,pos; - volatile stb_threadqueue *tq = (volatile stb_threadqueue *) tq2; - stb_mutex_begin(tq->add); - for(;;) { - pos = tq->tail; - tail = stb__tq_wrap(tq, pos+1); - if (tail != tq->head) break; - - // full - if (tq->growable) { - if (!stb__threadq_grow(tq)) { - stb_mutex_end(tq->add); - return STB_FALSE; // out of memory - } - } else if (!block) { - stb_mutex_end(tq->add); - return STB_FALSE; - } else { - ++tq->tail_blockers; - stb_mutex_end(tq->add); - - stb_sem_waitfor(tq->nonfull); - - stb_mutex_begin(tq->add); - --tq->tail_blockers; - } - } - memcpy(tq->data + tq->item_size * pos, input, tq->item_size); - stb_barrier(); - tq->tail = tail; - stb_sem_release(tq->nonempty); - if (tq->tail_blockers) // can't check if actually non-full due to race? - stb_sem_release(tq->nonfull); - stb_mutex_end(tq->add); - return STB_TRUE; -} - -int stb_threadq_length(stb_threadqueue *tq2) -{ - int a,b,n; - volatile stb_threadqueue *tq = (volatile stb_threadqueue *) tq2; - stb_mutex_begin(tq->add); - a = tq->head; - b = tq->tail; - n = tq->array_size; - stb_mutex_end(tq->add); - if (a > b) b += n; - return b-a; -} - -int stb_threadq_get(stb_threadqueue *tq, void *output) -{ - return stb__threadq_get_raw(tq, output, STB_FALSE); -} - -void stb_threadq_get_block(stb_threadqueue *tq, void *output) -{ - stb__threadq_get_raw(tq, output, STB_TRUE); -} - -int stb_threadq_add(stb_threadqueue *tq, void *input) -{ - return stb__threadq_add_raw(tq, input, STB_FALSE); -} - -int stb_threadq_add_block(stb_threadqueue *tq, void *input) -{ - return stb__threadq_add_raw(tq, input, STB_TRUE); -} - -void stb_threadq_delete(stb_threadqueue *tq) -{ - if (tq) { - free(tq->data); - stb_mutex_delete(tq->add); - stb_mutex_delete(tq->remove); - stb_sem_delete(tq->nonempty); - stb_sem_delete(tq->nonfull); - free(tq); - } -} - -#define STB_THREADQUEUE_DYNAMIC 0 -stb_threadqueue *stb_threadq_new(int item_size, int num_items, int many_add, int many_remove) -{ - int error=0; - stb_threadqueue *tq = (stb_threadqueue *) malloc(sizeof(*tq)); - if (tq == NULL) return NULL; - - if (num_items == STB_THREADQUEUE_DYNAMIC) { - tq->growable = STB_TRUE; - num_items = 32; - } else - tq->growable = STB_FALSE; - - tq->item_size = item_size; - tq->array_size = num_items+1; - - tq->add = tq->remove = STB_MUTEX_NULL; - tq->nonempty = tq->nonfull = STB_SEMAPHORE_NULL; - tq->data = NULL; - if (many_add) - { tq->add = stb_mutex_new(); if (tq->add == STB_MUTEX_NULL) goto error; } - if (many_remove || tq->growable) - { tq->remove = stb_mutex_new(); if (tq->remove == STB_MUTEX_NULL) goto error; } - tq->nonempty = stb_sem_new(1); if (tq->nonempty == STB_SEMAPHORE_NULL) goto error; - tq->nonfull = stb_sem_new(1); if (tq->nonfull == STB_SEMAPHORE_NULL) goto error; - tq->data = (char *) malloc(tq->item_size * tq->array_size); - if (tq->data == NULL) goto error; - - tq->head = tq->tail = 0; - tq->head_blockers = tq->tail_blockers = 0; - - return tq; - -error: - stb_threadq_delete(tq); - return NULL; -} - -typedef struct -{ - stb_thread_func f; - void *d; - volatile void **retval; - stb_sync sync; -} stb__workinfo; - -//static volatile stb__workinfo *stb__work; - -struct stb__workqueue -{ - int numthreads; - stb_threadqueue *tq; -}; - -static stb_workqueue *stb__work_global; - -static void *stb__thread_workloop(void *p) -{ - volatile stb_workqueue *q = (volatile stb_workqueue *) p; - for(;;) { - void *z; - stb__workinfo w; - stb_threadq_get_block(q->tq, &w); - if (w.f == NULL) // null work is a signal to end the thread - return NULL; - z = w.f(w.d); - if (w.retval) { stb_barrier(); *w.retval = z; } - if (w.sync != STB_SYNC_NULL) stb_sync_reach(w.sync); - } -} - -stb_workqueue *stb_workq_new(int num_threads, int max_units) -{ - return stb_workq_new_flags(num_threads, max_units, 0,0); -} - -stb_workqueue *stb_workq_new_flags(int numthreads, int max_units, int no_add_mutex, int no_remove_mutex) -{ - stb_workqueue *q = (stb_workqueue *) malloc(sizeof(*q)); - if (q == NULL) return NULL; - q->tq = stb_threadq_new(sizeof(stb__workinfo), max_units, !no_add_mutex, !no_remove_mutex); - if (q->tq == NULL) { free(q); return NULL; } - q->numthreads = 0; - stb_workq_numthreads(q, numthreads); - return q; -} - -void stb_workq_delete(stb_workqueue *q) -{ - while (stb_workq_length(q) != 0) - stb__thread_sleep(1); - stb_threadq_delete(q->tq); - free(q); -} - -static int stb__work_maxitems = STB_THREADQUEUE_DYNAMIC; - -static void stb_work_init(int num_threads) -{ - if (stb__work_global == NULL) { - stb__threadmutex_init(); - stb_mutex_begin(stb__workmutex); - stb_barrier(); - if (*(stb_workqueue * volatile *) &stb__work_global == NULL) - stb__work_global = stb_workq_new(num_threads, stb__work_maxitems); - stb_mutex_end(stb__workmutex); - } -} - -static int stb__work_raw(stb_workqueue *q, stb_thread_func f, void *d, volatile void **return_code, stb_sync rel) -{ - stb__workinfo w; - if (q == NULL) { - stb_work_init(1); - q = stb__work_global; - } - w.f = f; - w.d = d; - w.retval = return_code; - w.sync = rel; - return stb_threadq_add(q->tq, &w); -} - -int stb_workq_length(stb_workqueue *q) -{ - return stb_threadq_length(q->tq); -} - -int stb_workq(stb_workqueue *q, stb_thread_func f, void *d, volatile void **return_code) -{ - if (f == NULL) return 0; - return stb_workq_reach(q, f, d, return_code, NULL); -} - -int stb_workq_reach(stb_workqueue *q, stb_thread_func f, void *d, volatile void **return_code, stb_sync rel) -{ - if (f == NULL) return 0; - return stb__work_raw(q, f, d, return_code, rel); -} - -static void stb__workq_numthreads(stb_workqueue *q, int n) -{ - while (q->numthreads < n) { - stb_create_thread(stb__thread_workloop, q); - ++q->numthreads; - } - while (q->numthreads > n) { - stb__work_raw(q, NULL, NULL, NULL, NULL); - --q->numthreads; - } -} - -void stb_workq_numthreads(stb_workqueue *q, int n) -{ - stb_mutex_begin(stb__threadmutex); - stb__workq_numthreads(q,n); - stb_mutex_end(stb__threadmutex); -} - -int stb_work_maxunits(int n) -{ - if (stb__work_global == NULL) { - stb__work_maxitems = n; - stb_work_init(1); - } - return stb__work_maxitems; -} - -int stb_work(stb_thread_func f, void *d, volatile void **return_code) -{ - return stb_workq(stb__work_global, f,d,return_code); -} - -int stb_work_reach(stb_thread_func f, void *d, volatile void **return_code, stb_sync rel) -{ - return stb_workq_reach(stb__work_global, f,d,return_code,rel); -} - -void stb_work_numthreads(int n) -{ - if (stb__work_global == NULL) - stb_work_init(n); - else - stb_workq_numthreads(stb__work_global, n); -} -#endif // STB_DEFINE - - -////////////////////////////////////////////////////////////////////////////// -// -// Background disk I/O -// -// - -#define STB_BGIO_READ_ALL (-1) -STB_EXTERN int stb_bgio_read (char *filename, int offset, int len, stb_uchar **result, int *olen); -STB_EXTERN int stb_bgio_readf (FILE *f , int offset, int len, stb_uchar **result, int *olen); -STB_EXTERN int stb_bgio_read_to (char *filename, int offset, int len, stb_uchar *buffer, int *olen); -STB_EXTERN int stb_bgio_readf_to(FILE *f , int offset, int len, stb_uchar *buffer, int *olen); - -typedef struct -{ - int have_data; - int is_valid; - int is_dir; - time_t filetime; - stb_int64 filesize; -} stb_bgstat; - -STB_EXTERN int stb_bgio_stat (char *filename, stb_bgstat *result); - -#ifdef STB_DEFINE - -static stb_workqueue *stb__diskio; -static stb_mutex stb__diskio_mutex; - -void stb_thread_cleanup(void) -{ - if (stb__work_global) stb_workq_delete(stb__work_global); stb__work_global = NULL; - if (stb__threadmutex) stb_mutex_delete(stb__threadmutex); stb__threadmutex = NULL; - if (stb__workmutex) stb_mutex_delete(stb__workmutex); stb__workmutex = NULL; - if (stb__diskio) stb_workq_delete(stb__diskio); stb__diskio = NULL; - if (stb__diskio_mutex)stb_mutex_delete(stb__diskio_mutex);stb__diskio_mutex= NULL; -} - - -typedef struct -{ - char *filename; - FILE *f; - int offset; - int len; - - stb_bgstat *stat_out; - stb_uchar *output; - stb_uchar **result; - int *len_output; - int *flag; -} stb__disk_command; - -#define STB__MAX_DISK_COMMAND 100 -static stb__disk_command stb__dc_queue[STB__MAX_DISK_COMMAND]; -static int stb__dc_offset; - -void stb__io_init(void) -{ - if (!stb__diskio) { - stb__threadmutex_init(); - stb_mutex_begin(stb__threadmutex); - stb_barrier(); - if (*(stb_thread * volatile *) &stb__diskio == NULL) { - stb__diskio_mutex = stb_mutex_new(); - // use many threads so OS can try to schedule seeks - stb__diskio = stb_workq_new_flags(16,STB__MAX_DISK_COMMAND,STB_FALSE,STB_FALSE); - } - stb_mutex_end(stb__threadmutex); - } -} - -static void * stb__io_error(stb__disk_command *dc) -{ - if (dc->len_output) *dc->len_output = 0; - if (dc->result) *dc->result = NULL; - if (dc->flag) *dc->flag = -1; - return NULL; -} - -static void * stb__io_task(void *p) -{ - stb__disk_command *dc = (stb__disk_command *) p; - int len; - FILE *f; - stb_uchar *buf; - - if (dc->stat_out) { - struct _stati64 s; - if (!_stati64(dc->filename, &s)) { - dc->stat_out->filesize = s.st_size; - dc->stat_out->filetime = s.st_mtime; - dc->stat_out->is_dir = s.st_mode & _S_IFDIR; - dc->stat_out->is_valid = (s.st_mode & _S_IFREG) || dc->stat_out->is_dir; - } else - dc->stat_out->is_valid = 0; - stb_barrier(); - dc->stat_out->have_data = 1; - free(dc->filename); - return 0; - } - if (dc->f) { - #ifdef WIN32 - f = _fdopen(_dup(_fileno(dc->f)), "rb"); - #else - f = fdopen(dup(fileno(dc->f)), "rb"); - #endif - if (!f) - return stb__io_error(dc); - } else { - f = fopen(dc->filename, "rb"); - free(dc->filename); - if (!f) - return stb__io_error(dc); - } - - len = dc->len; - if (len < 0) { - fseek(f, 0, SEEK_END); - len = ftell(f) - dc->offset; - } - - if (fseek(f, dc->offset, SEEK_SET)) { - fclose(f); - return stb__io_error(dc); - } - - if (dc->output) - buf = dc->output; - else { - buf = (stb_uchar *) malloc(len); - if (buf == NULL) { - fclose(f); - return stb__io_error(dc); - } - } - - len = fread(buf, 1, len, f); - fclose(f); - if (dc->len_output) *dc->len_output = len; - if (dc->result) *dc->result = buf; - if (dc->flag) *dc->flag = 1; - - return NULL; -} - -int stb__io_add(char *fname, FILE *f, int off, int len, stb_uchar *out, stb_uchar **result, int *olen, int *flag, stb_bgstat *stat) -{ - int res; - stb__io_init(); - // do memory allocation outside of mutex - if (fname) fname = strdup(fname); - stb_mutex_begin(stb__diskio_mutex); - { - stb__disk_command *dc = &stb__dc_queue[stb__dc_offset]; - dc->filename = fname; - dc->f = f; - dc->offset = off; - dc->len = len; - dc->output = out; - dc->result = result; - dc->len_output = olen; - dc->flag = flag; - dc->stat_out = stat; - res = stb_workq(stb__diskio, stb__io_task, dc, NULL); - if (res) - stb__dc_offset = (stb__dc_offset + 1 == STB__MAX_DISK_COMMAND ? 0 : stb__dc_offset+1); - } - stb_mutex_end(stb__diskio_mutex); - return res; -} - -int stb_bgio_read(char *filename, int offset, int len, stb_uchar **result, int *olen) -{ - return stb__io_add(filename,NULL,offset,len,NULL,result,olen,NULL,NULL); -} - -int stb_bgio_readf(FILE *f, int offset, int len, stb_uchar **result, int *olen) -{ - return stb__io_add(NULL,f,offset,len,NULL,result,olen,NULL,NULL); -} - -int stb_bgio_read_to(char *filename, int offset, int len, stb_uchar *buffer, int *olen) -{ - return stb__io_add(filename,NULL,offset,len,buffer,NULL,olen,NULL,NULL); -} - -int stb_bgio_readf_to(FILE *f, int offset, int len, stb_uchar *buffer, int *olen) -{ - return stb__io_add(NULL,f,offset,len,buffer,NULL,olen,NULL,NULL); -} - -STB_EXTERN int stb_bgio_stat (char *filename, stb_bgstat *result) -{ - result->have_data = 0; - return stb__io_add(filename,NULL,0,0,0,NULL,0,NULL, result); -} -#endif -#endif - - - -////////////////////////////////////////////////////////////////////////////// -// -// Fast malloc implementation -// -// This is a clone of TCMalloc, but without the thread support. -// 1. large objects are allocated directly, page-aligned -// 2. small objects are allocated in homogeonous heaps, 0 overhead -// -// We keep an allocation table for pages a la TCMalloc. This would -// require 4MB for the entire address space, but we only allocate -// the parts that are in use. The overhead from using homogenous heaps -// everywhere is 3MB. (That is, if you allocate 1 object of each size, -// you'll use 3MB.) - -#if defined(STB_DEFINE) && (defined(_WIN32) || defined(STB_FASTMALLOC)) - -#ifdef _WIN32 - #ifndef _WINDOWS_ - #ifndef STB__IMPORT - #define STB__IMPORT STB_EXTERN __declspec(dllimport) - #define STB__DW unsigned long - #endif - STB__IMPORT void * __stdcall VirtualAlloc(void *p, unsigned long size, unsigned long type, unsigned long protect); - STB__IMPORT int __stdcall VirtualFree(void *p, unsigned long size, unsigned long freetype); - #endif - #define stb__alloc_pages_raw(x) (stb_uint32) VirtualAlloc(NULL, (x), 0x3000, 0x04) - #define stb__dealloc_pages_raw(p) VirtualFree((void *) p, 0, 0x8000) -#else - #error "Platform not currently supported" -#endif - -typedef struct stb__span -{ - int start, len; - struct stb__span *next, *prev; - void *first_free; - unsigned short list; // 1..256 free; 257..511 sizeclass; 0=large block - short allocations; // # outstanding allocations for sizeclass -} stb__span; // 24 - -static stb__span **stb__span_for_page; -static int stb__firstpage, stb__lastpage; -static void stb__update_page_range(int first, int last) -{ - stb__span **sfp; - int i, f,l; - if (first >= stb__firstpage && last <= stb__lastpage) return; - if (stb__span_for_page == NULL) { - f = first; - l = f+stb_max(last-f, 16384); - l = stb_min(l, 1<<20); - } else if (last > stb__lastpage) { - f = stb__firstpage; - l = f + (stb__lastpage - f) * 2; - l = stb_clamp(last, l,1<<20); - } else { - l = stb__lastpage; - f = l - (l - stb__firstpage) * 2; - f = stb_clamp(f, 0,first); - } - sfp = (stb__span **) stb__alloc_pages_raw(sizeof(void *) * (l-f)); - for (i=f; i < stb__firstpage; ++i) sfp[i - f] = NULL; - for ( ; i < stb__lastpage ; ++i) sfp[i - f] = stb__span_for_page[i - stb__firstpage]; - for ( ; i < l ; ++i) sfp[i - f] = NULL; - if (stb__span_for_page) stb__dealloc_pages_raw(stb__span_for_page); - stb__firstpage = f; - stb__lastpage = l; - stb__span_for_page = sfp; -} - -static stb__span *stb__span_free=NULL; -static stb__span *stb__span_first, *stb__span_end; -static stb__span *stb__span_alloc(void) -{ - stb__span *s = stb__span_free; - if (s) - stb__span_free = s->next; - else { - if (!stb__span_first) { - stb__span_first = (stb__span *) stb__alloc_pages_raw(65536); - if (stb__span_first == NULL) return NULL; - stb__span_end = stb__span_first + (65536 / sizeof(stb__span)); - } - s = stb__span_first++; - if (stb__span_first == stb__span_end) stb__span_first = NULL; - } - return s; -} - -static stb__span *stb__spanlist[512]; - -static void stb__spanlist_unlink(stb__span *s) -{ - if (s->prev) - s->prev->next = s->next; - else { - int n = s->list; - assert(stb__spanlist[n] == s); - stb__spanlist[n] = s->next; - } - if (s->next) - s->next->prev = s->prev; - s->next = s->prev = NULL; - s->list = 0; -} - -static void stb__spanlist_add(int n, stb__span *s) -{ - s->list = n; - s->next = stb__spanlist[n]; - s->prev = NULL; - stb__spanlist[n] = s; - if (s->next) s->next->prev = s; -} - -#define stb__page_shift 12 -#define stb__page_size (1 << stb__page_shift) -#define stb__page_number(x) ((x) >> stb__page_shift) -#define stb__page_address(x) ((x) << stb__page_shift) - -static void stb__set_span_for_page(stb__span *s) -{ - int i; - for (i=0; i < s->len; ++i) - stb__span_for_page[s->start + i - stb__firstpage] = s; -} - -static stb__span *stb__coalesce(stb__span *a, stb__span *b) -{ - assert(a->start + a->len == b->start); - if (a->list) stb__spanlist_unlink(a); - if (b->list) stb__spanlist_unlink(b); - a->len += b->len; - b->len = 0; - b->next = stb__span_free; - stb__span_free = b; - stb__set_span_for_page(a); - return a; -} - -static void stb__free_span(stb__span *s) -{ - stb__span *n = NULL; - if (s->start > stb__firstpage) { - n = stb__span_for_page[s->start-1 - stb__firstpage]; - if (n && n->allocations == -2 && n->start + n->len == s->start) s = stb__coalesce(n,s); - } - if (s->start + s->len < stb__lastpage) { - n = stb__span_for_page[s->start + s->len - stb__firstpage]; - if (n && n->allocations == -2 && s->start + s->len == n->start) s = stb__coalesce(s,n); - } - s->allocations = -2; - stb__spanlist_add(s->len > 256 ? 256 : s->len, s); -} - -static stb__span *stb__alloc_pages(int num) -{ - stb__span *s = stb__span_alloc(); - int p; - if (!s) return NULL; - p = stb__alloc_pages_raw(num << stb__page_shift); - if (p == 0) { s->next = stb__span_free; stb__span_free = s; return 0; } - assert(stb__page_address(stb__page_number(p)) == p); - p = stb__page_number(p); - stb__update_page_range(p, p+num); - s->start = p; - s->len = num; - s->next = NULL; - s->prev = NULL; - stb__set_span_for_page(s); - return s; -} - -static stb__span *stb__alloc_span(int pagecount) -{ - int i; - stb__span *p = NULL; - for(i=pagecount; i < 256; ++i) - if (stb__spanlist[i]) { - p = stb__spanlist[i]; - break; - } - if (!p) { - p = stb__spanlist[256]; - while (p && p->len < pagecount) - p = p->next; - } - if (!p) { - p = stb__alloc_pages(pagecount < 16 ? 16 : pagecount); - if (p == NULL) return 0; - } else - stb__spanlist_unlink(p); - - if (p->len > pagecount) { - stb__span *q = stb__span_alloc(); - if (q) { - q->start = p->start + pagecount; - q->len = p->len - pagecount; - p->len = pagecount; - for (i=0; i < q->len; ++i) - stb__span_for_page[q->start+i - stb__firstpage] = q; - stb__spanlist_add(q->len > 256 ? 256 : q->len, q); - } - } - return p; -} - -#define STB__MAX_SMALL_SIZE 32768 -#define STB__MAX_SIZE_CLASSES 256 - -static unsigned char stb__class_base[32]; -static unsigned char stb__class_shift[32]; -static unsigned char stb__pages_for_class[STB__MAX_SIZE_CLASSES]; -static int stb__size_for_class[STB__MAX_SIZE_CLASSES]; - -stb__span *stb__get_nonempty_sizeclass(int c) -{ - int s = c + 256, i, size, tsize; // remap to span-list index - char *z; - void *q; - stb__span *p = stb__spanlist[s]; - if (p) { - if (p->first_free) return p; // fast path: it's in the first one in list - for (p=p->next; p; p=p->next) - if (p->first_free) { - // move to front for future queries - stb__spanlist_unlink(p); - stb__spanlist_add(s, p); - return p; - } - } - // no non-empty ones, so allocate a new one - p = stb__alloc_span(stb__pages_for_class[c]); - if (!p) return NULL; - // create the free list up front - size = stb__size_for_class[c]; - tsize = stb__pages_for_class[c] << stb__page_shift; - i = 0; - z = (char *) stb__page_address(p->start); - q = NULL; - while (i + size <= tsize) { - * (void **) z = q; q = z; - z += size; - i += size; - } - p->first_free = q; - p->allocations = 0; - stb__spanlist_add(s,p); - return p; -} - -static int stb__sizeclass(size_t sz) -{ - int z = stb_log2_floor(sz); // -1 below to group e.g. 13,14,15,16 correctly - return stb__class_base[z] + ((sz-1) >> stb__class_shift[z]); -} - -static void stb__init_sizeclass(void) -{ - int i, size, overhead; - int align_shift = 2; // allow 4-byte and 12-byte blocks as well, vs. TCMalloc - int next_class = 1; - int last_log = 0; - - for (i = 0; i < align_shift; i++) { - stb__class_base [i] = next_class; - stb__class_shift[i] = align_shift; - } - - for (size = 1 << align_shift; size <= STB__MAX_SMALL_SIZE; size += 1 << align_shift) { - i = stb_log2_floor(size); - if (i > last_log) { - if (size == 16) ++align_shift; // switch from 4-byte to 8-byte alignment - else if (size >= 128 && align_shift < 8) ++align_shift; - stb__class_base[i] = next_class - ((size-1) >> align_shift); - stb__class_shift[i] = align_shift; - last_log = i; - } - stb__size_for_class[next_class++] = size; - } - - for (i=1; i <= STB__MAX_SMALL_SIZE; ++i) - assert(i <= stb__size_for_class[stb__sizeclass(i)]); - - overhead = 0; - for (i = 1; i < next_class; i++) { - int s = stb__size_for_class[i]; - size = stb__page_size; - while (size % s > size >> 3) - size += stb__page_size; - stb__pages_for_class[i] = (unsigned char) (size >> stb__page_shift); - overhead += size; - } - assert(overhead < (4 << 20)); // make sure it's under 4MB of overhead -} - -#ifdef STB_DEBUG -#define stb__smemset(a,b,c) memset((void *) a, b, c) -#elif defined(STB_FASTMALLOC_INIT) -#define stb__smemset(a,b,c) memset((void *) a, b, c) -#else -#define stb__smemset(a,b,c) -#endif -void *stb_smalloc(size_t sz) -{ - stb__span *s; - if (sz == 0) return NULL; - if (stb__size_for_class[1] == 0) stb__init_sizeclass(); - if (sz > STB__MAX_SMALL_SIZE) { - s = stb__alloc_span((sz + stb__page_size - 1) >> stb__page_shift); - if (s == NULL) return NULL; - s->list = 0; - s->next = s->prev = NULL; - s->allocations = -32767; - stb__smemset(stb__page_address(s->start), 0xcd, (sz+3)&~3); - return (void *) stb__page_address(s->start); - } else { - void *p; - int c = stb__sizeclass(sz); - s = stb__spanlist[256+c]; - if (!s || !s->first_free) - s = stb__get_nonempty_sizeclass(c); - if (s == NULL) return NULL; - p = s->first_free; - s->first_free = * (void **) p; - ++s->allocations; - stb__smemset(p,0xcd, sz); - return p; - } -} - -int stb_ssize(void *p) -{ - stb__span *s; - if (p == NULL) return 0; - s = stb__span_for_page[stb__page_number((stb_uint) p) - stb__firstpage]; - if (s->list >= 256) { - return stb__size_for_class[s->list - 256]; - } else { - assert(s->list == 0); - return s->len << stb__page_shift; - } -} - -void stb_sfree(void *p) -{ - stb__span *s; - if (p == NULL) return; - s = stb__span_for_page[stb__page_number((stb_uint) p) - stb__firstpage]; - if (s->list >= 256) { - stb__smemset(p, 0xfe, stb__size_for_class[s->list-256]); - * (void **) p = s->first_free; - s->first_free = p; - if (--s->allocations == 0) { - stb__spanlist_unlink(s); - stb__free_span(s); - } - } else { - assert(s->list == 0); - stb__smemset(p, 0xfe, stb_ssize(p)); - stb__free_span(s); - } -} - -void *stb_srealloc(void *p, size_t sz) -{ - size_t cur_size; - if (p == NULL) return stb_smalloc(sz); - if (sz == 0) { stb_sfree(p); return NULL; } - cur_size = stb_ssize(p); - if (sz > cur_size || sz <= (cur_size >> 1)) { - void *q; - if (sz > cur_size && sz < (cur_size << 1)) sz = cur_size << 1; - q = stb_smalloc(sz); if (q == NULL) return NULL; - memcpy(q, p, sz < cur_size ? sz : cur_size); - stb_sfree(p); - return q; - } - return p; -} - -void *stb_scalloc(size_t n, size_t sz) -{ - void *p; - if (n == 0 || sz == 0) return NULL; - if (stb_log2_ceil(n) + stb_log2_ceil(n) >= 32) return NULL; - p = stb_smalloc(n*sz); - if (p) memset(p, 0, n*sz); - return p; -} - -char *stb_sstrdup(char *s) -{ - int n = strlen(s); - char *p = (char *) stb_smalloc(n+1); - if (p) strcpy(p,s); - return p; -} -#endif // STB_DEFINE - - - -////////////////////////////////////////////////////////////////////////////// -// -// Source code constants -// -// This is a trivial system to let you specify constants in source code, -// then while running you can change the constants. -// -// Note that you can't wrap the #defines, because we need to know their -// names. So we provide a pre-wrapped version without 'STB_' for convenience; -// to request it, #define STB_CONVENIENT_H, yielding: -// KI -- integer -// KU -- unsigned integer -// KF -- float -// KD -- double -// KS -- string constant -// -// Defaults to functioning in debug build, not in release builds. -// To force on, define STB_ALWAYS_H - -#ifdef STB_CONVENIENT_H -#define KI(x) STB_I(x) -#define KU(x) STB_UI(x) -#define KF(x) STB_F(x) -#define KD(x) STB_D(x) -#define KS(x) STB_S(x) -#endif - -STB_EXTERN void stb_source_path(char *str); -#ifdef STB_DEFINE -char *stb__source_path; -void stb_source_path(char *path) -{ - stb__source_path = path; -} - -char *stb__get_sourcefile_path(char *file) -{ - static char filebuf[512]; - if (stb__source_path) { - sprintf(filebuf, "%s/%s", stb__source_path, file); - if (stb_fexists(filebuf)) return filebuf; - } - - if (stb_fexists(file)) return file; - - sprintf(filebuf, "../%s", file); - if (!stb_fexists(filebuf)) return filebuf; - - return file; -} -#endif - -#define STB_F(x) ((float) STB_H(x)) -#define STB_UI(x) ((unsigned int) STB_I(x)) - -#if !defined(STB_DEBUG) && !defined(STB_ALWAYS_H) -#define STB_D(x) ((double) (x)) -#define STB_I(x) ((int) (x)) -#define STB_S(x) ((char *) (x)) -#else -#define STB_D(x) stb__double_constant(__FILE__, __LINE__-1, (x)) -#define STB_I(x) stb__int_constant(__FILE__, __LINE__-1, (x)) -#define STB_S(x) stb__string_constant(__FILE__, __LINE__-1, (x)) - -STB_EXTERN double stb__double_constant(char *file, int line, double x); -STB_EXTERN int stb__int_constant(char *file, int line, int x); -STB_EXTERN char * stb__string_constant(char *file, int line, char *str); - -#ifdef STB_DEFINE - -enum -{ - STB__CTYPE_int, - STB__CTYPE_uint, - STB__CTYPE_float, - STB__CTYPE_double, - STB__CTYPE_string, -}; - -typedef struct -{ - int line; - int type; - union { - int ival; - double dval; - char *sval; - }; -} stb__Entry; - -typedef struct -{ - stb__Entry *entries; - char *filename; - time_t timestamp; - char **file_data; - int file_len; - unsigned short *line_index; -} stb__FileEntry; - -static void stb__constant_parse(stb__FileEntry *f, int i) -{ - char *s; - int n; - if (!stb_arr_valid(f->entries, i)) return; - n = f->entries[i].line; - if (n >= f->file_len) return; - s = f->file_data[n]; - switch (f->entries[i].type) { - case STB__CTYPE_float: - while (*s) { - if (!strncmp(s, "STB_D(", 6)) { s+=6; goto matched_float; } - if (!strncmp(s, "STB_F(", 6)) { s+=6; goto matched_float; } - if (!strncmp(s, "KD(", 3)) { s+=3; goto matched_float; } - if (!strncmp(s, "KF(", 3)) { s+=3; goto matched_float; } - ++s; - } - break; - matched_float: - f->entries[i].dval = strtod(s, NULL); - break; - case STB__CTYPE_int: - while (*s) { - if (!strncmp(s, "STB_I(", 6)) { s+=6; goto matched_int; } - if (!strncmp(s, "STB_UI(", 7)) { s+=7; goto matched_int; } - if (!strncmp(s, "KI(", 3)) { s+=3; goto matched_int; } - if (!strncmp(s, "KU(", 3)) { s+=3; goto matched_int; } - ++s; - } - break; - matched_int: { - int neg=0; - s = stb_skipwhite(s); - while (*s == '-') { neg = !neg; s = stb_skipwhite(s+1); } // handle '- - 5', pointlessly - if (s[0] == '0' && tolower(s[1]) == 'x') - f->entries[i].ival = strtol(s, NULL, 16); - else if (s[0] == '0') - f->entries[i].ival = strtol(s, NULL, 8); - else - f->entries[i].ival = strtol(s, NULL, 10); - if (neg) f->entries[i].ival = -f->entries[i].ival; - break; - } - case STB__CTYPE_string: - // @TODO - break; - } -} - -static stb_sdict *stb__constant_file_hash; - -stb__Entry *stb__constant_get_entry(char *filename, int line, int type) -{ - int i; - stb__FileEntry *f; - if (stb__constant_file_hash == NULL) - stb__constant_file_hash = stb_sdict_new(STB_TRUE); - f = (stb__FileEntry*) stb_sdict_get(stb__constant_file_hash, filename); - if (f == NULL) { - char *s = stb__get_sourcefile_path(filename); - if (s == NULL || !stb_fexists(s)) return 0; - f = (stb__FileEntry *) malloc(sizeof(*f)); - f->timestamp = stb_ftimestamp(s); - f->file_data = stb_stringfile(s, &f->file_len); - f->filename = strdup(s); // cache the full path - f->entries = NULL; - f->line_index = 0; - stb_arr_setlen(f->line_index, f->file_len); - memset(f->line_index, 0xff, stb_arr_storage(f->line_index)); - } else { - time_t t = stb_ftimestamp(f->filename); - if (f->timestamp != t) { - f->timestamp = t; - free(f->file_data); - f->file_data = stb_stringfile(f->filename, &f->file_len); - stb_arr_setlen(f->line_index, f->file_len); - for (i=0; i < stb_arr_len(f->entries); ++i) - stb__constant_parse(f, i); - } - } - - if (line >= f->file_len) return 0; - - if (f->line_index[line] >= stb_arr_len(f->entries)) { - // need a new entry - int n = stb_arr_len(f->entries); - stb__Entry e; - e.line = line; - if (line < f->file_len) - f->line_index[line] = n; - e.type = type; - stb_arr_push(f->entries, e); - stb__constant_parse(f, n); - } - return f->entries + f->line_index[line]; -} - -double stb__double_constant(char *file, int line, double x) -{ - stb__Entry *e = stb__constant_get_entry(file, line, STB__CTYPE_float); - if (!e) return x; - return e->dval; -} - -int stb__int_constant(char *file, int line, int x) -{ - stb__Entry *e = stb__constant_get_entry(file, line, STB__CTYPE_int); - if (!e) return x; - return e->ival; -} - -char * stb__string_constant(char *file, int line, char *x) -{ - stb__Entry *e = stb__constant_get_entry(file, line, STB__CTYPE_string); - if (!e) return x; - return e->sval; -} - -#endif // STB_DEFINE -#endif // !STB_DEBUG && !STB_ALWAYS_H - - -#ifdef STB_STUA -////////////////////////////////////////////////////////////////////////// -// -// stua: little scripting language -// -// define STB_STUA to compile it -// -// see http://nothings.org/stb/stb_stua.html for documentation -// -// basic parsing model: -// -// lexical analysis -// use stb_lex() to parse tokens; keywords get their own tokens -// -// parsing: -// recursive descent parser. too much of a hassle to make an unambiguous -// LR(1) grammar, and one-pass generation is clumsier (recursive descent -// makes it easier to e.g. compile nested functions). on the other hand, -// dictionary syntax required hackery to get extra lookahead. -// -// codegen: -// output into an evaluation tree, using array indices as 'pointers' -// -// run: -// traverse the tree; support for 'break/continue/return' is tricky -// -// garbage collection: -// stu__mark and sweep; explicit stack with non-stu__compile_global_scope roots - -typedef stb_int32 stua_obj; - -typedef stb_idict stua_dict; - -STB_EXTERN void stua_run_script(char *s); -STB_EXTERN void stua_uninit(void); - -extern stua_obj stua_globals; - -STB_EXTERN double stua_number(stua_obj z); - -STB_EXTERN stua_obj stua_getnil(void); -STB_EXTERN stua_obj stua_getfalse(void); -STB_EXTERN stua_obj stua_gettrue(void); -STB_EXTERN stua_obj stua_string(char *z); -STB_EXTERN stua_obj stua_make_number(double d); -STB_EXTERN stua_obj stua_box(int type, void *data, int size); - -enum -{ - STUA_op_negate=129, - STUA_op_shl, STUA_op_ge, - STUA_op_shr, STUA_op_le, - STUA_op_shru, - STUA_op_last -}; - -#define STUA_NO_VALUE 2 // equivalent to a tagged NULL -STB_EXTERN stua_obj (*stua_overload)(int op, stua_obj a, stua_obj b, stua_obj c); - -STB_EXTERN stua_obj stua_error(char *err, ...); - -STB_EXTERN stua_obj stua_pushroot(stua_obj o); -STB_EXTERN void stua_poproot ( void ); - - -#ifdef STB_DEFINE -// INTERPRETER - -// 31-bit floating point implementation -// force the (1 << 30) bit (2nd highest bit) to be zero by re-biasing the exponent; -// then shift and set the bottom bit - -static stua_obj stu__floatp(float *f) -{ - unsigned int n = *(unsigned int *) f; - unsigned int e = n & (0xff << 23); - - assert(sizeof(int) == 4 && sizeof(float) == 4); - - if (!e) // zero? - n = n; // no change - else if (e < (64 << 23)) // underflow of the packed encoding? - n = (n & 0x80000000); // signed 0 - else if (e > (190 << 23)) // overflow of the encoding? (or INF or NAN) - n = (n & 0x80000000) + (127 << 23); // new INF encoding - else - n -= 0x20000000; - - // now we need to shuffle the bits so that the spare bit is at the bottom - assert((n & 0x40000000) == 0); - return (n & 0x80000000) + (n << 1) + 1; -} - -static unsigned char stu__getfloat_addend[256]; -static float stu__getfloat(stua_obj v) -{ - unsigned int n; - unsigned int e = ((unsigned int) v) >> 24; - - n = (int) v >> 1; // preserve high bit - n += stu__getfloat_addend[e] << 24; - return *(float *) &n; -} - -stua_obj stua_float(float f) -{ - return stu__floatp(&f); -} - -static void stu__float_init(void) -{ - int i; - stu__getfloat_addend[0] = 0; // do nothing to biased exponent of 0 - for (i=1; i < 127; ++i) - stu__getfloat_addend[i] = 32; // undo the -0x20000000 - stu__getfloat_addend[127] = 64; // convert packed INF to INF (0x3f -> 0x7f) - - for (i=0; i < 128; ++i) // for signed floats, remove the bit we just shifted down - stu__getfloat_addend[128+i] = stu__getfloat_addend[i] - 64; -} - -// Tagged data type implementation - - // TAGS: -#define stu__int_tag 0 // of 2 bits // 00 int -#define stu__float_tag 1 // of 1 bit // 01 float -#define stu__ptr_tag 2 // of 2 bits // 10 boxed - // 11 float - -#define stu__tag(x) ((x) & 3) -#define stu__number(x) (stu__tag(x) != stu__ptr_tag) -#define stu__isint(x) (stu__tag(x) == stu__int_tag) - -#define stu__int(x) ((x) >> 2) -#define stu__float(x) (stu__getfloat(x)) - -#define stu__makeint(v) ((v)*4+stu__int_tag) - -// boxed data, and tag support for boxed data - -enum -{ - STU___float = 1, STU___int = 2, - STU___number = 3, STU___string = 4, - STU___function = 5, STU___dict = 6, - STU___boolean = 7, STU___error = 8, -}; - -// boxed data -#define STU__BOX short type, stua_gc -typedef struct stu__box { STU__BOX; } stu__box; - -stu__box stu__nil = { 0, 1 }; -stu__box stu__true = { STU___boolean, 1, }; -stu__box stu__false = { STU___boolean, 1, }; - -#define stu__makeptr(v) ((stua_obj) (v) + stu__ptr_tag) - -#define stua_nil stu__makeptr(&stu__nil) -#define stua_true stu__makeptr(&stu__true) -#define stua_false stu__makeptr(&stu__false) - -stua_obj stua_getnil(void) { return stua_nil; } -stua_obj stua_getfalse(void) { return stua_false; } -stua_obj stua_gettrue(void) { return stua_true; } - -#define stu__ptr(x) ((stu__box *) ((x) - stu__ptr_tag)) - -#define stu__checkt(t,x) ((t) == STU___float ? ((x) & 1) == stu__float_tag : \ - (t) == STU___int ? stu__isint(x) : \ - (t) == STU___number ? stu__number(x) : \ - stu__tag(x) == stu__ptr_tag && stu__ptr(x)->type == (t)) - -typedef struct -{ - STU__BOX; - void *ptr; -} stu__wrapper; - -// implementation of a 'function' or function + closure - -typedef struct stu__func -{ - STU__BOX; - stua_obj closure_source; // 0 - regular function; 4 - C function - // if closure, pointer to source function - union { - stua_obj closure_data; // partial-application data - void *store; // pointer to free that holds 'code' - stua_obj (*func)(stua_dict *context); - } f; - // closure ends here - short *code; - int num_param; - stua_obj *param; // list of parameter strings -} stu__func; - -// apply this to 'short *code' to get at data -#define stu__const(f) ((stua_obj *) (f)) - -static void stu__free_func(stu__func *f) -{ - if (f->closure_source == 0) free(f->f.store); - if ((stb_uint) f->closure_source <= 4) free(f->param); - free(f); -} - -#define stu__pd(x) ((stua_dict *) stu__ptr(x)) -#define stu__pw(x) ((stu__wrapper *) stu__ptr(x)) -#define stu__pf(x) ((stu__func *) stu__ptr(x)) - - -// garbage-collection - - -static stu__box ** stu__gc_ptrlist; -static stua_obj * stu__gc_root_stack; - -stua_obj stua_pushroot(stua_obj o) { stb_arr_push(stu__gc_root_stack, o); return o; } -void stua_poproot ( void ) { stb_arr_pop(stu__gc_root_stack); } - -static stb_sdict *stu__strings; -static void stu__mark(stua_obj z) -{ - int i; - stu__box *p = stu__ptr(z); - if (p->stua_gc == 1) return; // already marked - assert(p->stua_gc == 0); - p->stua_gc = 1; - switch(p->type) { - case STU___function: { - stu__func *f = (stu__func *) p; - if ((stb_uint) f->closure_source <= 4) { - if (f->closure_source == 0) { - for (i=1; i <= f->code[0]; ++i) - if (!stu__number(((stua_obj *) f->code)[-i])) - stu__mark(((stua_obj *) f->code)[-i]); - } - for (i=0; i < f->num_param; ++i) - stu__mark(f->param[i]); - } else { - stu__mark(f->closure_source); - stu__mark(f->f.closure_data); - } - break; - } - case STU___dict: { - stua_dict *e = (stua_dict *) p; - for (i=0; i < e->limit; ++i) - if (e->table[i].k != STB_IEMPTY && e->table[i].k != STB_IDEL) { - if (!stu__number(e->table[i].k)) stu__mark((int) e->table[i].k); - if (!stu__number(e->table[i].v)) stu__mark((int) e->table[i].v); - } - break; - } - } -} - -static int stu__num_allocs, stu__size_allocs; -static stua_obj stu__flow_val = stua_nil; // used for break & return - -static void stua_gc(int force) -{ - int i; - if (!force && stu__num_allocs == 0 && stu__size_allocs == 0) return; - stu__num_allocs = stu__size_allocs = 0; - //printf("[gc]\n"); - - // clear marks - for (i=0; i < stb_arr_len(stu__gc_ptrlist); ++i) - stu__gc_ptrlist[i]->stua_gc = 0; - - // stu__mark everything reachable - stu__nil.stua_gc = stu__true.stua_gc = stu__false.stua_gc = 1; - stu__mark(stua_globals); - if (!stu__number(stu__flow_val)) - stu__mark(stu__flow_val); - for (i=0; i < stb_arr_len(stu__gc_root_stack); ++i) - if (!stu__number(stu__gc_root_stack[i])) - stu__mark(stu__gc_root_stack[i]); - - // sweep unreachables - for (i=0; i < stb_arr_len(stu__gc_ptrlist);) { - stu__box *z = stu__gc_ptrlist[i]; - if (!z->stua_gc) { - switch (z->type) { - case STU___dict: stb_idict_destroy((stua_dict *) z); break; - case STU___error: free(((stu__wrapper *) z)->ptr); break; - case STU___string: stb_sdict_remove(stu__strings, (char*) ((stu__wrapper *) z)->ptr, NULL); free(z); break; - case STU___function: stu__free_func((stu__func *) z); break; - } - // swap in the last item over this, and repeat - z = stb_arr_pop(stu__gc_ptrlist); - stu__gc_ptrlist[i] = z; - } else - ++i; - } -} - -static void stu__consider_gc(stua_obj x) -{ - if (stu__size_allocs < 100000) return; - if (stu__num_allocs < 10 && stu__size_allocs < 1000000) return; - stb_arr_push(stu__gc_root_stack, x); - stua_gc(0); - stb_arr_pop(stu__gc_root_stack); -} - -static stua_obj stu__makeobj(int type, void *data, int size, int safe_to_gc) -{ - stua_obj x = stu__makeptr(data); - ((stu__box *) data)->type = type; - stb_arr_push(stu__gc_ptrlist, (stu__box *) data); - stu__num_allocs += 1; - stu__size_allocs += size; - if (safe_to_gc) stu__consider_gc(x); - return x; -} - -stua_obj stua_box(int type, void *data, int size) -{ - stu__wrapper *p = (stu__wrapper *) malloc(sizeof(*p)); - p->ptr = data; - return stu__makeobj(type, p, size, 0); -} - -// a stu string can be directly compared for equality, because -// they go into a hash table -stua_obj stua_string(char *z) -{ - stu__wrapper *b = (stu__wrapper *) stb_sdict_get(stu__strings, z); - if (b == NULL) { - int o = stua_box(STU___string, NULL, strlen(z) + sizeof(*b)); - b = stu__pw(o); - stb_sdict_add(stu__strings, z, b); - stb_sdict_getkey(stu__strings, z, (char **) &b->ptr); - } - return stu__makeptr(b); -} - -// stb_obj dictionary is just an stb_idict -static void stu__set(stua_dict *d, stua_obj k, stua_obj v) -{ if (stb_idict_set(d, k, v)) stu__size_allocs += 8; } - -static stua_obj stu__get(stua_dict *d, stua_obj k, stua_obj res) -{ - stb_idict_get_flag(d, k, &res); - return res; -} - -static stua_obj make_string(char *z, int len) -{ - stua_obj s; - char temp[256], *q = (char *) stb_temp(temp, len+1), *p = q; - while (len > 0) { - if (*z == '\\') { - if (z[1] == 'n') *p = '\n'; - else if (z[1] == 'r') *p = '\r'; - else if (z[1] == 't') *p = '\t'; - else *p = z[1]; - p += 1; z += 2; len -= 2; - } else { - *p++ = *z++; len -= 1; - } - } - *p = 0; - s = stua_string(q); - stb_tempfree(temp, q); - return s; -} - -enum token_names -{ - T__none=128, - ST_shl = STUA_op_shl, ST_ge = STUA_op_ge, - ST_shr = STUA_op_shr, ST_le = STUA_op_le, - ST_shru = STUA_op_shru, STU__negate = STUA_op_negate, - ST__reset_numbering = STUA_op_last, - ST_white, - ST_id, ST_float, ST_decimal, ST_hex, ST_char,ST_string, ST_number, - // make sure the keywords come _AFTER_ ST_id, so stb_lex prefer them - ST_if, ST_while, ST_for, ST_eq, ST_nil, - ST_then, ST_do, ST_in, ST_ne, ST_true, - ST_else, ST_break, ST_let, ST_and, ST_false, - ST_elseif, ST_continue, ST_into, ST_or, ST_repeat, - ST_end, ST_as, ST_return, ST_var, ST_func, - ST_catch, ST__frame, - ST__max_terminals, - - STU__defaultparm, STU__seq, -}; - -static stua_dict * stu__globaldict; - stua_obj stua_globals; - -static enum -{ - FLOW_normal, FLOW_continue, FLOW_break, FLOW_return, FLOW_error, -} stu__flow; - -stua_obj stua_error(char *z, ...) -{ - stua_obj a; - char temp[4096], *x; - va_list v; va_start(v,z); vsprintf(temp, z, v); va_end(v); - x = strdup(temp); - a = stua_box(STU___error, x, strlen(x)); - stu__flow = FLOW_error; - stu__flow_val = a; - return stua_nil; -} - -double stua_number(stua_obj z) -{ - return stu__tag(z) == stu__int_tag ? stu__int(z) : stu__float(z); -} - -stua_obj stua_make_number(double d) -{ - double e = floor(d); - if (e == d && e < (1 << 29) && e >= -(1 << 29)) - return stu__makeint((int) e); - else - return stua_float((float) d); -} - -stua_obj (*stua_overload)(int op, stua_obj a, stua_obj b, stua_obj c) = NULL; - -static stua_obj stu__op(int op, stua_obj a, stua_obj b, stua_obj c) -{ - stua_obj r = STUA_NO_VALUE; - if (op == '+') { - if (stu__checkt(STU___string, a) && stu__checkt(STU___string, b)) { - ;// @TODO: string concatenation - } else if (stu__checkt(STU___function, a) && stu__checkt(STU___dict, b)) { - stu__func *f = (stu__func *) malloc(12); - assert(offsetof(stu__func, code)==12); - f->closure_source = a; - f->f.closure_data = b; - return stu__makeobj(STU___function, f, 16, 1); - } - } - if (stua_overload) r = stua_overload(op,a,b,c); - if (stu__flow != FLOW_error && r == STUA_NO_VALUE) - stua_error("Typecheck for operator %d", op), r=stua_nil; - return r; -} - -#define STU__EVAL2(a,b) \ - a = stu__eval(stu__f[n+1]); if (stu__flow) break; stua_pushroot(a); \ - b = stu__eval(stu__f[n+2]); stua_poproot(); if (stu__flow) break; - -#define STU__FB(op) \ - STU__EVAL2(a,b) \ - if (stu__tag(a) == stu__int_tag && stu__tag(b) == stu__int_tag) \ - return ((a) op (b)); \ - if (stu__number(a) && stu__number(b)) \ - return stua_make_number(stua_number(a) op stua_number(b)); \ - return stu__op(stu__f[n], a,b, stua_nil) - -#define STU__F(op) \ - STU__EVAL2(a,b) \ - if (stu__number(a) && stu__number(b)) \ - return stua_make_number(stua_number(a) op stua_number(b)); \ - return stu__op(stu__f[n], a,b, stua_nil) - -#define STU__I(op) \ - STU__EVAL2(a,b) \ - if (stu__tag(a) == stu__int_tag && stu__tag(b) == stu__int_tag) \ - return stu__makeint(stu__int(a) op stu__int(b)); \ - return stu__op(stu__f[n], a,b, stua_nil) - -#define STU__C(op) \ - STU__EVAL2(a,b) \ - if (stu__number(a) && stu__number(b)) \ - return (stua_number(a) op stua_number(b)) ? stua_true : stua_false; \ - return stu__op(stu__f[n], a,b, stua_nil) - -#define STU__CE(op) \ - STU__EVAL2(a,b) \ - return (a op b) ? stua_true : stua_false - -static short *stu__f; -static stua_obj stu__f_obj; -static stua_dict *stu__c; -static stua_obj stu__funceval(stua_obj fo, stua_obj co); - -static int stu__cond(stua_obj x) -{ - if (stu__flow) return 0; - if (!stu__checkt(STU___boolean, x)) - x = stu__op('!', x, stua_nil, stua_nil); - if (x == stua_true ) return 1; - if (x == stua_false) return 0; - stu__flow = FLOW_error; - return 0; -} - -// had to manually eliminate tailcall recursion for debugging complex stuff -#define TAILCALL(x) n = (x); goto top; -static stua_obj stu__eval(int n) -{ -top: - if (stu__flow >= FLOW_return) return stua_nil; // is this needed? - if (n < 0) return stu__const(stu__f)[n]; - assert(n != 0 && n != 1); - switch (stu__f[n]) { - stua_obj a,b,c; - case ST_catch: a = stu__eval(stu__f[n+1]); - if (stu__flow == FLOW_error) { a=stu__flow_val; stu__flow = FLOW_normal; } - return a; - case ST_var: b = stu__eval(stu__f[n+2]); if (stu__flow) break; - stu__set(stu__c, stu__const(stu__f)[stu__f[n+1]], b); - return b; - case STU__seq: stu__eval(stu__f[n+1]); if (stu__flow) break; - TAILCALL(stu__f[n+2]); - case ST_if: if (!stu__cond(stu__eval(stu__f[n+1]))) return stua_nil; - TAILCALL(stu__f[n+2]); - case ST_else: a = stu__cond(stu__eval(stu__f[n+1])); - TAILCALL(stu__f[n + 2 + !a]); - #define STU__HANDLE_BREAK \ - if (stu__flow >= FLOW_break) { \ - if (stu__flow == FLOW_break) { \ - a = stu__flow_val; \ - stu__flow = FLOW_normal; \ - stu__flow_val = stua_nil; \ - return a; \ - } \ - return stua_nil; \ - } - case ST_as: stu__eval(stu__f[n+3]); - STU__HANDLE_BREAK - // fallthrough! - case ST_while: a = stua_nil; stua_pushroot(a); - while (stu__cond(stu__eval(stu__f[n+1]))) { - stua_poproot(); - a = stu__eval(stu__f[n+2]); - STU__HANDLE_BREAK - stu__flow = FLOW_normal; // clear 'continue' flag - stua_pushroot(a); - if (stu__f[n+3]) stu__eval(stu__f[n+3]); - STU__HANDLE_BREAK - stu__flow = FLOW_normal; // clear 'continue' flag - } - stua_poproot(); - return a; - case ST_break: stu__flow = FLOW_break; stu__flow_val = stu__eval(stu__f[n+1]); break; - case ST_continue:stu__flow = FLOW_continue; break; - case ST_return: stu__flow = FLOW_return; stu__flow_val = stu__eval(stu__f[n+1]); break; - case ST__frame: return stu__f_obj; - case '[': STU__EVAL2(a,b); - if (stu__checkt(STU___dict, a)) - return stu__get(stu__pd(a), b, stua_nil); - return stu__op(stu__f[n], a, b, stua_nil); - case '=': a = stu__eval(stu__f[n+2]); if (stu__flow) break; - n = stu__f[n+1]; - if (stu__f[n] == ST_id) { - if (!stb_idict_update(stu__c, stu__const(stu__f)[stu__f[n+1]], a)) - if (!stb_idict_update(stu__globaldict, stu__const(stu__f)[stu__f[n+1]], a)) - return stua_error("Assignment to undefined variable"); - } else if (stu__f[n] == '[') { - stua_pushroot(a); - b = stu__eval(stu__f[n+1]); if (stu__flow) { stua_poproot(); break; } - stua_pushroot(b); - c = stu__eval(stu__f[n+2]); stua_poproot(); stua_poproot(); - if (stu__flow) break; - if (!stu__checkt(STU___dict, b)) return stua_nil; - stu__set(stu__pd(b), c, a); - } else { - return stu__op(stu__f[n], stu__eval(n), a, stua_nil); - } - return a; - case STU__defaultparm: - a = stu__eval(stu__f[n+2]); - stu__flow = FLOW_normal; - if (stb_idict_add(stu__c, stu__const(stu__f)[stu__f[n+1]], a)) - stu__size_allocs += 8; - return stua_nil; - case ST_id: a = stu__get(stu__c, stu__const(stu__f)[stu__f[n+1]], STUA_NO_VALUE); // try local variable - return a != STUA_NO_VALUE // else try stu__compile_global_scope variable - ? a : stu__get(stu__globaldict, stu__const(stu__f)[stu__f[n+1]], stua_nil); - case STU__negate:a = stu__eval(stu__f[n+1]); if (stu__flow) break; - return stu__isint(a) ? -a : stu__op(stu__f[n], a, stua_nil, stua_nil); - case '~': a = stu__eval(stu__f[n+1]); if (stu__flow) break; - return stu__isint(a) ? (~a)&~3 : stu__op(stu__f[n], a, stua_nil, stua_nil); - case '!': a = stu__eval(stu__f[n+1]); if (stu__flow) break; - a = stu__cond(a); if (stu__flow) break; - return a ? stua_true : stua_false; - case ST_eq: STU__CE(==); case ST_le: STU__C(<=); case '<': STU__C(<); - case ST_ne: STU__CE(!=); case ST_ge: STU__C(>=); case '>': STU__C(>); - case '+' : STU__FB(+); case '*': STU__F(*); case '&': STU__I(&); case ST_shl: STU__I(<<); - case '-' : STU__FB(-); case '/': STU__F(/); case '|': STU__I(|); case ST_shr: STU__I(>>); - case '%': STU__I(%); case '^': STU__I(^); - case ST_shru: STU__EVAL2(a,b); - if (stu__tag(a) == stu__int_tag && stu__tag(b) == stu__int_tag) - return stu__makeint((unsigned) stu__int(a) >> stu__int(b)); - return stu__op(stu__f[n], a,b, stua_nil); - case ST_and: a = stu__eval(stu__f[n+1]); b = stu__cond(a); if (stu__flow) break; - return a ? stu__eval(stu__f[n+2]) : a; - case ST_or : a = stu__eval(stu__f[n+1]); b = stu__cond(a); if (stu__flow) break; - return a ? b : stu__eval(stu__f[n+2]); - case'(':case':': STU__EVAL2(a,b); - if (!stu__checkt(STU___function, a)) - return stu__op(stu__f[n], a,b, stua_nil); - if (!stu__checkt(STU___dict, b)) - return stua_nil; - if (stu__f[n] == ':') - b = stu__makeobj(STU___dict, stb_idict_copy(stu__pd(b)), stb_idict_memory_usage(stu__pd(b)), 0); - a = stu__funceval(a,b); - return a; - case '{' : { - stua_dict *d; - d = stb_idict_new_size(stu__f[n+1] > 40 ? 64 : 16); - if (d == NULL) - return stua_nil; // breakpoint fodder - c = stu__makeobj(STU___dict, d, 32, 1); - stua_pushroot(c); - a = stu__f[n+1]; - for (b=0; b < a; ++b) { - stua_obj x = stua_pushroot(stu__eval(stu__f[n+2 + b*2 + 0])); - stua_obj y = stu__eval(stu__f[n+2 + b*2 + 1]); - stua_poproot(); - if (stu__flow) { stua_poproot(); return stua_nil; } - stu__set(d, x, y); - } - stua_poproot(); - return c; - } - default: if (stu__f[n] < 0) return stu__const(stu__f)[stu__f[n]]; - assert(0); /* NOTREACHED */ // internal error! - } - return stua_nil; -} - -int stb__stua_nesting; -static stua_obj stu__funceval(stua_obj fo, stua_obj co) -{ - stu__func *f = stu__pf(fo); - stua_dict *context = stu__pd(co); - int i,j; - stua_obj p; - short *tf = stu__f; // save previous function - stua_dict *tc = stu__c; - - if (stu__flow == FLOW_error) return stua_nil; - assert(stu__flow == FLOW_normal); - - stua_pushroot(fo); - stua_pushroot(co); - stu__consider_gc(stua_nil); - - while ((stb_uint) f->closure_source > 4) { - // add data from closure to context - stua_dict *e = (stua_dict *) stu__pd(f->f.closure_data); - for (i=0; i < e->limit; ++i) - if (e->table[i].k != STB_IEMPTY && e->table[i].k != STB_IDEL) - if (stb_idict_add(context, e->table[i].k, e->table[i].v)) - stu__size_allocs += 8; - // use add so if it's already defined, we don't override it; that way - // explicit parameters win over applied ones, and most recent applications - // win over previous ones - f = stu__pf(f->closure_source); - } - - for (j=0, i=0; i < f->num_param; ++i) - // if it doesn't already exist, add it from the numbered parameters - if (stb_idict_add(context, f->param[i], stu__get(context, stu__int(j), stua_nil))) - ++j; - - // @TODO: if (stu__get(context, stu__int(f->num_param+1)) != STUA_NO_VALUE) // error: too many parameters - // @TODO: ditto too few parameters - - if (f->closure_source == 4) - p = f->f.func(context); - else { - stu__f = f->code, stu__c = context; - stu__f_obj = co; - ++stb__stua_nesting; - if (stu__f[1]) - p = stu__eval(stu__f[1]); - else - p = stua_nil; - --stb__stua_nesting; - stu__f = tf, stu__c = tc; // restore previous function - if (stu__flow == FLOW_return) { - stu__flow = FLOW_normal; - p = stu__flow_val; - stu__flow_val = stua_nil; - } - } - - stua_poproot(); - stua_poproot(); - - return p; -} - -// Parser - -static int stu__tok; -static stua_obj stu__tokval; - -static char *stu__curbuf, *stu__bufstart; - -static stb_matcher *stu__lex_matcher; - -static unsigned char stu__prec[ST__max_terminals], stu__end[ST__max_terminals]; - -static void stu__nexttoken(void) -{ - int len; - -retry: - stu__tok = stb_lex(stu__lex_matcher, stu__curbuf, &len); - if (stu__tok == 0) - return; - switch(stu__tok) { - case ST_white : stu__curbuf += len; goto retry; - case T__none : stu__tok = *stu__curbuf; break; - case ST_string: stu__tokval = make_string(stu__curbuf+1, len-2); break; - case ST_id : stu__tokval = make_string(stu__curbuf, len); break; - case ST_hex : stu__tokval = stu__makeint(strtol(stu__curbuf+2,NULL,16)); stu__tok = ST_number; break; - case ST_decimal: stu__tokval = stu__makeint(strtol(stu__curbuf ,NULL,10)); stu__tok = ST_number; break; - case ST_float : stu__tokval = stua_float((float) atof(stu__curbuf)) ; stu__tok = ST_number; break; - case ST_char : stu__tokval = stu__curbuf[2] == '\\' ? stu__curbuf[3] : stu__curbuf[2]; - if (stu__curbuf[3] == 't') stu__tokval = '\t'; - if (stu__curbuf[3] == 'n') stu__tokval = '\n'; - if (stu__curbuf[3] == 'r') stu__tokval = '\r'; - stu__tokval = stu__makeint(stu__tokval); - stu__tok = ST_number; - break; - } - stu__curbuf += len; -} - -static struct { int stu__tok; char *regex; } stu__lexemes[] = -{ - ST_white , "([ \t\n\r]|/\\*(.|\n)*\\*/|//[^\r\n]*([\r\n]|$))+", - ST_id , "[_a-zA-Z][_a-zA-Z0-9]*", - ST_hex , "0x[0-9a-fA-F]+", - ST_decimal, "[0-9]+[0-9]*", - ST_float , "[0-9]+\\.?[0-9]*([eE][-+]?[0-9]+)?", - ST_float , "\\.[0-9]+([eE][-+]?[0-9]+)?", - ST_char , "c'(\\\\.|[^\\'])'", - ST_string , "\"(\\\\.|[^\\\"\n\r])*\"", - ST_string , "\'(\\\\.|[^\\\'\n\r])*\'", - - #define stua_key4(a,b,c,d) ST_##a, #a, ST_##b, #b, ST_##c, #c, ST_##d, #d, - stua_key4(if,then,else,elseif) stua_key4(while,do,for,in) - stua_key4(func,var,let,break) stua_key4(nil,true,false,end) - stua_key4(return,continue,as,repeat) stua_key4(_frame,catch,catch,catch) - - ST_shl, "<<", ST_and, "&&", ST_eq, "==", ST_ge, ">=", - ST_shr, ">>", ST_or , "||", ST_ne, "!=", ST_le, "<=", - ST_shru,">>>", ST_into, "=>", - T__none, ".", -}; - -typedef struct -{ - stua_obj *data; // constants being compiled - short *code; // code being compiled - stua_dict *locals; - short *non_local_refs; -} stu__comp_func; - -static stu__comp_func stu__pfunc; -static stu__comp_func *func_stack = NULL; -static void stu__push_func_comp(void) -{ - stb_arr_push(func_stack, stu__pfunc); - stu__pfunc.data = NULL; - stu__pfunc.code = NULL; - stu__pfunc.locals = stb_idict_new_size(16); - stu__pfunc.non_local_refs = NULL; - stb_arr_push(stu__pfunc.code, 0); // number of data items - stb_arr_push(stu__pfunc.code, 1); // starting execution address -} - -static void stu__pop_func_comp(void) -{ - stb_arr_free(stu__pfunc.code); - stb_arr_free(stu__pfunc.data); - stb_idict_destroy(stu__pfunc.locals); - stb_arr_free(stu__pfunc.non_local_refs); - stu__pfunc = stb_arr_pop(func_stack); -} - -// if an id is a reference to an outer lexical scope, this -// function returns the "name" of it, and updates the stack -// structures to make sure the names are propogated in. -static int stu__nonlocal_id(stua_obj var_obj) -{ - stua_obj dummy, var = var_obj; - int i, n = stb_arr_len(func_stack), j,k; - if (stb_idict_get_flag(stu__pfunc.locals, var, &dummy)) return 0; - for (i=n-1; i > 1; --i) { - if (stb_idict_get_flag(func_stack[i].locals, var, &dummy)) - break; - } - if (i <= 1) return 0; // stu__compile_global_scope - j = i; // need to access variable from j'th frame - for (i=0; i < stb_arr_len(stu__pfunc.non_local_refs); ++i) - if (stu__pfunc.non_local_refs[i] == j) return j-n; - stb_arr_push(stu__pfunc.non_local_refs, j-n); - // now make sure all the parents propogate it down - for (k=n-1; k > 1; --k) { - if (j-k >= 0) return j-n; // comes direct from this parent - for(i=0; i < stb_arr_len(func_stack[k].non_local_refs); ++i) - if (func_stack[k].non_local_refs[i] == j-k) - return j-n; - stb_arr_push(func_stack[k].non_local_refs, j-k); - } - assert (k != 1); - - return j-n; -} - -static int stu__off(void) { return stb_arr_len(stu__pfunc.code); } -static void stu__cc(int a) -{ - assert(a >= -2000 && a < 5000); - stb_arr_push(stu__pfunc.code, a); -} -static int stu__cc1(int a) { stu__cc(a); return stu__off()-1; } -static int stu__cc2(int a, int b) { stu__cc(a); stu__cc(b); return stu__off()-2; } -static int stu__cc3(int a, int b, int c) { - if (a == '=') assert(c != 0); - stu__cc(a); stu__cc(b); stu__cc(c); return stu__off()-3; } -static int stu__cc4(int a, int b, int c, int d) { stu__cc(a); stu__cc(b); stu__cc(c); stu__cc(d); return stu__off()-4; } - -static int stu__cdv(stua_obj p) -{ - int i; - assert(p != STUA_NO_VALUE); - for (i=0; i < stb_arr_len(stu__pfunc.data); ++i) - if (stu__pfunc.data[i] == p) - break; - if (i == stb_arr_len(stu__pfunc.data)) - stb_arr_push(stu__pfunc.data, p); - return ~i; -} - -static int stu__cdt(void) -{ - int z = stu__cdv(stu__tokval); - stu__nexttoken(); - return z; -} - -static int stu__seq(int a, int b) -{ - return !a ? b : !b ? a : stu__cc3(STU__seq, a,b); -} - -static char stu__comp_err_str[1024]; -static int stu__comp_err_line; -static int stu__err(char *str, ...) -{ - va_list v; - char *s = stu__bufstart; - stu__comp_err_line = 1; - while (s < stu__curbuf) { - if (s[0] == '\n' || s[0] == '\r') { - if (s[0]+s[1] == '\n' + '\r') ++s; - ++stu__comp_err_line; - } - ++s; - } - va_start(v, str); - vsprintf(stu__comp_err_str, str, v); - va_end(v); - return 0; -} - -static int stu__accept(int p) -{ - if (stu__tok != p) return 0; - stu__nexttoken(); - return 1; -} - -static int stu__demand(int p) -{ - if (stu__accept(p)) return 1; - return stu__err("Didn't find expected stu__tok"); -} - -static int stu__demandv(int p, stua_obj *val) -{ - if (stu__tok == p || p==0) { - *val = stu__tokval; - stu__nexttoken(); - return 1; - } else - return 0; -} - -static int stu__expr(int p); -int stu__nexpr(int p) { stu__nexttoken(); return stu__expr(p); } -static int stu__statements(int once, int as); - -static int stu__parse_if(void) // parse both ST_if and ST_elseif -{ - int b,c,a; - a = stu__nexpr(1); if (!a) return 0; - if (!stu__demand(ST_then)) return stu__err("expecting THEN"); - b = stu__statements(0,0); if (!b) return 0; - if (b == 1) b = -1; - - if (stu__tok == ST_elseif) { - return stu__parse_if(); - } else if (stu__accept(ST_else)) { - c = stu__statements(0,0); if (!c) return 0; - if (!stu__demand(ST_end)) return stu__err("expecting END after else clause"); - return stu__cc4(ST_else, a, b, c); - } else { - if (!stu__demand(ST_end)) return stu__err("expecting END in if statement"); - return stu__cc3(ST_if, a, b); - } -} - -int stu__varinit(int z, int in_globals) -{ - int a,b; - stu__nexttoken(); - while (stu__demandv(ST_id, &b)) { - if (!stb_idict_add(stu__pfunc.locals, b, 1)) - if (!in_globals) return stu__err("Redefined variable %s.", stu__pw(b)->ptr); - if (stu__accept('=')) { - a = stu__expr(1); if (!a) return 0; - } else - a = stu__cdv(stua_nil); - z = stu__seq(z, stu__cc3(ST_var, stu__cdv(b), a)); - if (!stu__accept(',')) break; - } - return z; -} - -static int stu__compile_unary(int z, int outparm, int require_inparm) -{ - int op = stu__tok, a, b; - stu__nexttoken(); - if (outparm) { - if (require_inparm || (stu__tok && stu__tok != ST_end && stu__tok != ST_else && stu__tok != ST_elseif && stu__tok !=';')) { - a = stu__expr(1); if (!a) return 0; - } else - a = stu__cdv(stua_nil); - b = stu__cc2(op, a); - } else - b = stu__cc1(op); - return stu__seq(z,b); -} - -static int stu__assign(void) -{ - int z; - stu__accept(ST_let); - z = stu__expr(1); if (!z) return 0; - if (stu__accept('=')) { - int y,p = (z >= 0 ? stu__pfunc.code[z] : 0); - if (z < 0 || (p != ST_id && p != '[')) return stu__err("Invalid lvalue in assignment"); - y = stu__assign(); if (!y) return 0; - z = stu__cc3('=', z, y); - } - return z; -} - -static int stu__statements(int once, int stop_while) -{ - int a,b, c, z=0; - for(;;) { - switch (stu__tok) { - case ST_if : a = stu__parse_if(); if (!a) return 0; - z = stu__seq(z, a); - break; - case ST_while : if (stop_while) return (z ? z:1); - a = stu__nexpr(1); if (!a) return 0; - if (stu__accept(ST_as)) c = stu__statements(0,0); else c = 0; - if (!stu__demand(ST_do)) return stu__err("expecting DO"); - b = stu__statements(0,0); if (!b) return 0; - if (!stu__demand(ST_end)) return stu__err("expecting END"); - if (b == 1) b = -1; - z = stu__seq(z, stu__cc4(ST_while, a, b, c)); - break; - case ST_repeat : stu__nexttoken(); - c = stu__statements(0,1); if (!c) return 0; - if (!stu__demand(ST_while)) return stu__err("expecting WHILE"); - a = stu__expr(1); if (!a) return 0; - if (!stu__demand(ST_do)) return stu__err("expecting DO"); - b = stu__statements(0,0); if (!b) return 0; - if (!stu__demand(ST_end)) return stu__err("expecting END"); - if (b == 1) b = -1; - z = stu__seq(z, stu__cc4(ST_as, a, b, c)); - break; - case ST_catch : a = stu__nexpr(1); if (!a) return 0; - z = stu__seq(z, stu__cc2(ST_catch, a)); - break; - case ST_var : z = stu__varinit(z,0); break; - case ST_return : z = stu__compile_unary(z,1,1); break; - case ST_continue:z = stu__compile_unary(z,0,0); break; - case ST_break : z = stu__compile_unary(z,1,0); break; - case ST_into : if (z == 0 && !once) return stu__err("=> cannot be first statement in block"); - a = stu__nexpr(99); - b = (a >= 0? stu__pfunc.code[a] : 0); - if (a < 0 || (b != ST_id && b != '[')) return stu__err("Invalid lvalue on right side of =>"); - z = stu__cc3('=', a, z); - break; - default : if (stu__end[stu__tok]) return once ? 0 : (z ? z:1); - a = stu__assign(); if (!a) return 0; - stu__accept(';'); - if (stu__tok && !stu__end[stu__tok]) { - if (a < 0) - return stu__err("Constant has no effect"); - if (stu__pfunc.code[a] != '(' && stu__pfunc.code[a] != '=') - return stu__err("Expression has no effect"); - } - z = stu__seq(z, a); - break; - } - if (!z) return 0; - stu__accept(';'); - if (once && stu__tok != ST_into) return z; - } -} - -static int stu__postexpr(int z, int p); -static int stu__dictdef(int end, int *count) -{ - int z,n=0,i,flags=0; - short *dict=NULL; - stu__nexttoken(); - while (stu__tok != end) { - if (stu__tok == ST_id) { - stua_obj id = stu__tokval; - stu__nexttoken(); - if (stu__tok == '=') { - flags |= 1; - stb_arr_push(dict, stu__cdv(id)); - z = stu__nexpr(1); if (!z) return 0; - } else { - z = stu__cc2(ST_id, stu__cdv(id)); - z = stu__postexpr(z,1); if (!z) return 0; - flags |= 2; - stb_arr_push(dict, stu__cdv(stu__makeint(n++))); - } - } else { - z = stu__expr(1); if (!z) return 0; - flags |= 2; - stb_arr_push(dict, stu__cdv(stu__makeint(n++))); - } - if (end != ')' && flags == 3) { z=stu__err("can't mix initialized and uninitialized defs"); goto done;} - stb_arr_push(dict, z); - if (!stu__accept(',')) break; - } - if (!stu__demand(end)) - return stu__err(end == ')' ? "Expecting ) at end of function call" - : "Expecting } at end of dictionary definition"); - z = stu__cc2('{', stb_arr_len(dict)/2); - for (i=0; i < stb_arr_len(dict); ++i) - stu__cc(dict[i]); - if (count) *count = n; -done: - stb_arr_free(dict); - return z; -} - -static int stu__comp_id(void) -{ - int z,d; - d = stu__nonlocal_id(stu__tokval); - if (d == 0) - return z = stu__cc2(ST_id, stu__cdt()); - // access a non-local frame by naming it with the appropriate int - assert(d < 0); - z = stu__cdv(d); // relative frame # is the 'variable' in our local frame - z = stu__cc2(ST_id, z); // now access that dictionary - return stu__cc3('[', z, stu__cdt()); // now access the variable from that dir -} - -static stua_obj stu__funcdef(stua_obj *id, stua_obj *func); -static int stu__expr(int p) -{ - int z; - // unary - switch (stu__tok) { - case ST_number: z = stu__cdt(); break; - case ST_string: z = stu__cdt(); break; // @TODO - string concatenation like C - case ST_id : z = stu__comp_id(); break; - case ST__frame: z = stu__cc1(ST__frame); stu__nexttoken(); break; - case ST_func : z = stu__funcdef(NULL,NULL); break; - case ST_if : z = stu__parse_if(); break; - case ST_nil : z = stu__cdv(stua_nil); stu__nexttoken(); break; - case ST_true : z = stu__cdv(stua_true); stu__nexttoken(); break; - case ST_false : z = stu__cdv(stua_false); stu__nexttoken(); break; - case '-' : z = stu__nexpr(99); if (z) z=stu__cc2(STU__negate,z); else return z; break; - case '!' : z = stu__nexpr(99); if (z) z=stu__cc2('!',z); else return z; break; - case '~' : z = stu__nexpr(99); if (z) z=stu__cc2('~',z); else return z; break; - case '{' : z = stu__dictdef('}', NULL); break; - default : return stu__err("Unexpected token"); - case '(' : stu__nexttoken(); z = stu__statements(0,0); if (!stu__demand(')')) return stu__err("Expecting )"); - } - return stu__postexpr(z,p); -} - -static int stu__postexpr(int z, int p) -{ - int q; - // postfix - while (stu__tok == '(' || stu__tok == '[' || stu__tok == '.') { - if (stu__accept('.')) { - // MUST be followed by a plain identifier! use [] for other stuff - if (stu__tok != ST_id) return stu__err("Must follow . with plain name; try [] instead"); - z = stu__cc3('[', z, stu__cdv(stu__tokval)); - stu__nexttoken(); - } else if (stu__accept('[')) { - while (stu__tok != ']') { - int r = stu__expr(1); if (!r) return 0; - z = stu__cc3('[', z, r); - if (!stu__accept(',')) break; - } - if (!stu__demand(']')) return stu__err("Expecting ]"); - } else { - int n, p = stu__dictdef(')', &n); if (!p) return 0; - #if 0 // this is incorrect! - if (z > 0 && stu__pfunc.code[z] == ST_id) { - stua_obj q = stu__get(stu__globaldict, stu__pfunc.data[-stu__pfunc.code[z+1]-1], stua_nil); - if (stu__checkt(STU___function, q)) - if ((stu__pf(q))->num_param != n) - return stu__err("Incorrect number of parameters"); - } - #endif - z = stu__cc3('(', z, p); - } - } - // binop - this implementation taken from lcc - for (q=stu__prec[stu__tok]; q >= p; --q) { - while (stu__prec[stu__tok] == q) { - int o = stu__tok, y = stu__nexpr(p+1); if (!y) return 0; - z = stu__cc3(o,z,y); - } - } - return z; -} - -static stua_obj stu__finish_func(stua_obj *param, int start) -{ - int n, size; - stu__func *f = (stu__func *) malloc(sizeof(*f)); - f->closure_source = 0; - f->num_param = stb_arr_len(param); - f->param = (int *) stb_copy(param, f->num_param * sizeof(*f->param)); - size = stb_arr_storage(stu__pfunc.code) + stb_arr_storage(stu__pfunc.data) + sizeof(*f) + 8; - f->f.store = malloc(stb_arr_storage(stu__pfunc.code) + stb_arr_storage(stu__pfunc.data)); - f->code = (short *) ((char *) f->f.store + stb_arr_storage(stu__pfunc.data)); - memcpy(f->code, stu__pfunc.code, stb_arr_storage(stu__pfunc.code)); - f->code[1] = start; - f->code[0] = stb_arr_len(stu__pfunc.data); - for (n=0; n < f->code[0]; ++n) - ((stua_obj *) f->code)[-1-n] = stu__pfunc.data[n]; - return stu__makeobj(STU___function, f, size, 0); -} - -static int stu__funcdef(stua_obj *id, stua_obj *result) -{ - int n,z=0,i,q; - stua_obj *param = NULL; - short *nonlocal; - stua_obj v,f=stua_nil; - assert(stu__tok == ST_func); - stu__nexttoken(); - if (id) { - if (!stu__demandv(ST_id, id)) return stu__err("Expecting function name"); - } else - stu__accept(ST_id); - if (!stu__demand('(')) return stu__err("Expecting ( for function parameter"); - stu__push_func_comp(); - while (stu__tok != ')') { - if (!stu__demandv(ST_id, &v)) { z=stu__err("Expecting parameter name"); goto done; } - stb_idict_add(stu__pfunc.locals, v, 1); - if (stu__tok == '=') { - n = stu__nexpr(1); if (!n) { z=0; goto done; } - z = stu__seq(z, stu__cc3(STU__defaultparm, stu__cdv(v), n)); - } else - stb_arr_push(param, v); - if (!stu__accept(',')) break; - } - if (!stu__demand(')')) { z=stu__err("Expecting ) at end of parameter list"); goto done; } - n = stu__statements(0,0); if (!n) { z=0; goto done; } - if (!stu__demand(ST_end)) { z=stu__err("Expecting END at end of function"); goto done; } - if (n == 1) n = 0; - n = stu__seq(z,n); - f = stu__finish_func(param, n); - if (result) { *result = f; z=1; stu__pop_func_comp(); } - else { - nonlocal = stu__pfunc.non_local_refs; - stu__pfunc.non_local_refs = NULL; - stu__pop_func_comp(); - z = stu__cdv(f); - if (nonlocal) { // build a closure with references to the needed frames - short *initcode = NULL; - for (i=0; i < stb_arr_len(nonlocal); ++i) { - int k = nonlocal[i], p; - stb_arr_push(initcode, stu__cdv(k)); - if (k == -1) p = stu__cc1(ST__frame); - else { p = stu__cdv(stu__makeint(k+1)); p = stu__cc2(ST_id, p); } - stb_arr_push(initcode, p); - } - q = stu__cc2('{', stb_arr_len(nonlocal)); - for (i=0; i < stb_arr_len(initcode); ++i) - stu__cc(initcode[i]); - z = stu__cc3('+', z, q); - stb_arr_free(initcode); - } - stb_arr_free(nonlocal); - } -done: - stb_arr_free(param); - if (!z) stu__pop_func_comp(); - return z; -} - -static int stu__compile_global_scope(void) -{ - stua_obj o; - int z=0; - - stu__push_func_comp(); - while (stu__tok != 0) { - if (stu__tok == ST_func) { - stua_obj id, f; - if (!stu__funcdef(&id,&f)) - goto error; - stu__set(stu__globaldict, id, f); - } else if (stu__tok == ST_var) { - z = stu__varinit(z,1); if (!z) goto error; - } else { - int y = stu__statements(1,0); if (!y) goto error; - z = stu__seq(z,y); - } - stu__accept(';'); - } - o = stu__finish_func(NULL, z); - stu__pop_func_comp(); - - o = stu__funceval(o, stua_globals); // initialize stu__globaldict - if (stu__flow == FLOW_error) - printf("Error: %s\n", ((stu__wrapper *) stu__ptr(stu__flow_val))->ptr); - return 1; -error: - stu__pop_func_comp(); - return 0; -} - -stua_obj stu__myprint(stua_dict *context) -{ - stua_obj x = stu__get(context, stua_string("x"), stua_nil); - if ((x & 1) == stu__float_tag) printf("%f", stu__getfloat(x)); - else if (stu__tag(x) == stu__int_tag) printf("%d", stu__int(x)); - else { - stu__wrapper *s = stu__pw(x); - if (s->type == STU___string || s->type == STU___error) - printf("%s", s->ptr); - else if (s->type == STU___dict) printf("{{dictionary}}"); - else if (s->type == STU___function) printf("[[function]]"); - else - printf("[[ERROR:%s]]", s->ptr); - } - return x; -} - -void stua_init(void) -{ - if (!stu__globaldict) { - int i; - stua_obj s; - stu__func *f; - - stu__prec[ST_and] = stu__prec[ST_or] = 1; - stu__prec[ST_eq ] = stu__prec[ST_ne] = stu__prec[ST_le] = - stu__prec[ST_ge] = stu__prec['>' ] = stu__prec['<'] = 2; - stu__prec[':'] = 3; - stu__prec['&'] = stu__prec['|'] = stu__prec['^'] = 4; - stu__prec['+'] = stu__prec['-'] = 5; - stu__prec['*'] = stu__prec['/'] = stu__prec['%'] = - stu__prec[ST_shl]= stu__prec[ST_shr]= stu__prec[ST_shru]= 6; - - stu__end[')'] = stu__end[ST_end] = stu__end[ST_else] = 1; - stu__end[ST_do] = stu__end[ST_elseif] = 1; - - stu__float_init(); - stu__lex_matcher = stb_lex_matcher(); - for (i=0; i < sizeof(stu__lexemes)/sizeof(stu__lexemes[0]); ++i) - stb_lex_item(stu__lex_matcher, stu__lexemes[i].regex, stu__lexemes[i].stu__tok); - - stu__globaldict = stb_idict_new_size(64); - stua_globals = stu__makeobj(STU___dict, stu__globaldict, 0,0); - stu__strings = stb_sdict_new(0); - - stu__curbuf = stu__bufstart = "func _print(x) end\n" - "func print()\n var x=0 while _frame[x] != nil as x=x+1 do _print(_frame[x]) end end\n"; - stu__nexttoken(); - if (!stu__compile_global_scope()) - printf("Compile error in line %d: %s\n", stu__comp_err_line, stu__comp_err_str); - - s = stu__get(stu__globaldict, stua_string("_print"), stua_nil); - if (stu__tag(s) == stu__ptr_tag && stu__ptr(s)->type == STU___function) { - f = stu__pf(s); - free(f->f.store); - f->closure_source = 4; - f->f.func = stu__myprint; - f->code = NULL; - } - } -} - -void stua_uninit(void) -{ - if (stu__globaldict) { - stb_idict_remove_all(stu__globaldict); - stb_arr_setlen(stu__gc_root_stack, 0); - stua_gc(1); - stb_idict_destroy(stu__globaldict); - stb_sdict_delete(stu__strings); - stb_matcher_free(stu__lex_matcher); - stb_arr_free(stu__gc_ptrlist); - stb_arr_free(func_stack); - stb_arr_free(stu__gc_root_stack); - stu__globaldict = NULL; - } -} - -void stua_run_script(char *s) -{ - stua_init(); - - stu__curbuf = stu__bufstart = s; - stu__nexttoken(); - - stu__flow = FLOW_normal; - - if (!stu__compile_global_scope()) - printf("Compile error in line %d: %s\n", stu__comp_err_line, stu__comp_err_str); - stua_gc(1); -} -#endif // STB_DEFINE - -#endif // STB_STUA - - -#undef STB_EXTERN -#endif // STB_INCLUDE_STB_H - diff --git a/impeller/third_party/stb/stb/stb_c_lexer.h b/impeller/third_party/stb/stb/stb_c_lexer.h deleted file mode 100644 index e91c6c3c4813f..0000000000000 --- a/impeller/third_party/stb/stb/stb_c_lexer.h +++ /dev/null @@ -1,816 +0,0 @@ -// stb_c_lexer.h - v0.07 - public domain Sean Barrett 2013 -// lexer for making little C-like languages with recursive-descent parsers -// -// This file provides both the interface and the implementation. -// To instantiate the implementation, -// #define STB_C_LEXER_IMPLEMENTATION -// in *ONE* source file, before #including this file. -// -// The default configuration is fairly close to a C lexer, although -// suffixes on integer constants are not handled (you can override this). -// -// History: -// 0.07 fix mishandling of hexadecimal constants parsed by strtol -// 0.06 fix missing next character after ending quote mark (Andreas Fredriksson) -// 0.05 refixed get_location because github version had lost the fix -// 0.04 fix octal parsing bug -// 0.03 added STB_C_LEX_DISCARD_PREPROCESSOR option -// refactor API to simplify (only one struct instead of two) -// change literal enum names to have 'lit' at the end -// 0.02 first public release -// -// Status: -// - haven't tested compiling as C++ -// - haven't tested the float parsing path -// - haven't tested the non-default-config paths (e.g. non-stdlib) -// - only tested default-config paths by eyeballing output of self-parse -// -// - haven't implemented multiline strings -// - haven't implemented octal/hex character constants -// - haven't implemented support for unicode CLEX_char -// - need to expand error reporting so you don't just get "CLEX_parse_error" -// -// LICENSE -// -// This software is dual-licensed to the public domain and under the following -// license: you are granted a perpetual, irrevocable license to copy, modify, -// publish, and distribute this file as you see fit. - -#ifndef STB_C_LEXER_DEFINITIONS -// to change the default parsing rules, copy the following lines -// into your C/C++ file *before* including this, and then replace -// the Y's with N's for the ones you don't want. -// --BEGIN-- - -#define STB_C_LEX_C_DECIMAL_INTS Y // "0|[1-9][0-9]*" CLEX_intlit -#define STB_C_LEX_C_HEX_INTS Y // "0x[0-9a-fA-F]+" CLEX_intlit -#define STB_C_LEX_C_OCTAL_INTS Y // "[0-7]+" CLEX_intlit -#define STB_C_LEX_C_DECIMAL_FLOATS Y // "[0-9]*(.[0-9]*([eE]-?[0-9]+)?) CLEX_floatlit -#define STB_C_LEX_C_IDENTIFIERS Y // "[_a-zA-Z][_a-zA-Z0-9]*" CLEX_id -#define STB_C_LEX_C_DQ_STRINGS Y // double-quote-delimited strings with escapes CLEX_dqstring -#define STB_C_LEX_C_SQ_STRINGS N // single-quote-delimited strings with escapes CLEX_ssstring -#define STB_C_LEX_C_CHARS Y // single-quote-delimited character with escape CLEX_charlits -#define STB_C_LEX_C_COMMENTS Y // "/* comment */" -#define STB_C_LEX_CPP_COMMENTS Y // "// comment to end of line\n" -#define STB_C_LEX_C_COMPARISONS Y // "==" CLEX_eq "!=" CLEX_noteq "<=" CLEX_lesseq ">=" CLEX_greatereq -#define STB_C_LEX_C_LOGICAL Y // "&&" CLEX_andand "||" CLEX_oror -#define STB_C_LEX_C_SHIFTS Y // "<<" CLEX_shl ">>" CLEX_shr -#define STB_C_LEX_C_INCREMENTS Y // "++" CLEX_plusplus "--" CLEX_minusminus -#define STB_C_LEX_C_ARROW Y // "->" CLEX_arrow -#define STB_C_LEX_EQUAL_ARROW N // "=>" CLEX_eqarrow -#define STB_C_LEX_C_BITWISEEQ Y // "&=" CLEX_andeq "|=" CLEX_oreq "^=" CLEX_xoreq -#define STB_C_LEX_C_ARITHEQ Y // "+=" CLEX_pluseq "-=" CLEX_minuseq - // "*=" CLEX_muleq "/=" CLEX_diveq "%=" CLEX_modeq - // if both STB_C_LEX_SHIFTS & STB_C_LEX_ARITHEQ: - // "<<=" CLEX_shleq ">>=" CLEX_shreq - -#define STB_C_LEX_PARSE_SUFFIXES N // letters after numbers are parsed as part of those numbers, and must be in suffix list below -#define STB_C_LEX_DECIMAL_SUFFIXES "" // decimal integer suffixes e.g. "uUlL" -- these are returned as-is in string storage -#define STB_C_LEX_HEX_SUFFIXES "" // e.g. "uUlL" -#define STB_C_LEX_OCTAL_SUFFIXES "" // e.g. "uUlL" -#define STB_C_LEX_FLOAT_SUFFIXES "" // - -#define STB_C_LEX_0_IS_EOF N // if Y, ends parsing at '\0'; if N, returns '\0' as token -#define STB_C_LEX_INTEGERS_AS_DOUBLES N // parses integers as doubles so they can be larger than 'int', but only if STB_C_LEX_STDLIB==N -#define STB_C_LEX_MULTILINE_DSTRINGS N // allow newlines in double-quoted strings -#define STB_C_LEX_MULTILINE_SSTRINGS N // allow newlines in single-quoted strings -#define STB_C_LEX_USE_STDLIB Y // use strtod,strtol for parsing #s; otherwise inaccurate hack -#define STB_C_LEX_DOLLAR_IDENTIFIER Y // allow $ as an identifier character -#define STB_C_LEX_FLOAT_NO_DECIMAL Y // allow floats that have no decimal point if they have an exponent - -#define STB_C_LEX_DEFINE_ALL_TOKEN_NAMES N // if Y, all CLEX_ token names are defined, even if never returned - // leaving it as N should help you catch config bugs - -#define STB_C_LEX_DISCARD_PREPROCESSOR Y // discard C-preprocessor directives (e.g. after prepocess - // still have #line, #pragma, etc) - -//#define STB_C_LEX_ISWHITE(str) ... // return length in bytes of first character if it is whitespace - -#define STB_C_LEXER_DEFINITIONS // This line prevents the header file from replacing your definitions -// --END-- - -#endif - -#ifndef INCLUDE_STB_C_LEXER_H -#define INCLUDE_STB_C_LEXER_H - -typedef struct -{ - // lexer variables - char *input_stream; - char *eof; - char *parse_point; - char *string_storage; - int string_storage_len; - - // lexer parse location for error messages - char *where_firstchar; - char *where_lastchar; - - // lexer token variables - long token; - double real_number; - long int_number; - char *string; - int string_len; -} stb_lexer; - -typedef struct -{ - int line_number; - int line_offset; -} stb_lex_location; - -#ifdef __cplusplus -extern "C" { -#endif - -extern void stb_c_lexer_init(stb_lexer *lexer, const char *input_stream, const char *input_stream_end, char *string_store, int store_length); -// this function initialize the 'lexer' structure -// Input: -// - input_stream points to the file to parse, loaded into memory -// - input_stream_end points to the end of the file, or NULL if you use 0-for-EOF -// - string_store is storage the lexer can use for storing parsed strings and identifiers -// - store_length is the length of that storage - -extern int stb_c_lexer_get_token(stb_lexer *lexer); -// this function returns non-zero if a token is parsed, or 0 if at EOF -// Output: -// - lexer->token is the token ID, which is unicode code point for a single-char token, < 0 for a multichar or eof or error -// - lexer->real_number is a double constant value for CLEX_floatlit, or CLEX_intlit if STB_C_LEX_INTEGERS_AS_DOUBLES -// - lexer->int_number is an integer constant for CLEX_intlit if !STB_C_LEX_INTEGERS_AS_DOUBLES, or character for CLEX_charlit -// - lexer->string is a 0-terminated string for CLEX_dqstring or CLEX_sqstring or CLEX_identifier -// - lexer->string_len is the byte length of lexer->string - -extern void stb_c_lexer_get_location(const stb_lexer *lexer, const char *where, stb_lex_location *loc); -// this inefficient function returns the line number and character offset of a -// given location in the file as returned by stb_lex_token. Because it's inefficient, -// you should only call it for errors, not for every token. -// For error messages of invalid tokens, you typically want the location of the start -// of the token (which caused the token to be invalid). For bugs involving legit -// tokens, you can report the first or the range. -// Output: -// - loc->line_number is the line number in the file, counting from 1, of the location -// - loc->line_offset is the char-offset in the line, counting from 0, of the location - - -#ifdef __cplusplus -} -#endif - -#endif // INCLUDE_STB_C_LEXER_H - -#ifdef STB_C_LEXER_IMPLEMENTATION - - #if defined(Y) || defined(N) - #error "Can only use stb_c_lexer in contexts where the preprocessor symbols 'Y' and 'N' are not defined" - #endif - - -// Hacky definitions so we can easily #if on them -#define Y(x) 1 -#define N(x) 0 - -#if STB_C_LEX_USE_STDLIB(x) -#define STB__CLEX_use_stdlib -#include -#endif - -#if STB_C_LEX_INTEGERS_AS_DOUBLES(x) -typedef double stb__clex_int; -#define intfield real_number -#define STB__clex_int_as_double -#else -typedef long stb__clex_int; -#define intfield int_number -#endif - -// Convert these config options to simple conditional #defines so we can more -// easily test them once we've change the meaning of Y/N - -#if STB_C_LEX_PARSE_SUFFIXES(x) -#define STB__clex_parse_suffixes -#endif - -#if STB_C_LEX_C_DECIMAL_INTS(x) || STB_C_LEX_C_HEX_INTS(x) || STB_C_LEX_DEFINE_ALL_TOKEN_NAMES(x) -#define STB__clex_define_int -#endif - -#if (STB_C_LEX_C_ARITHEQ(x) && STB_C_LEX_C_SHIFTS(x)) || STB_C_LEX_DEFINE_ALL_TOKEN_NAMES(x) -#define STB__clex_define_shifts -#endif - -#if STB_C_LEX_C_HEX_INTS(x) -#define STB__clex_hex_ints -#endif - -#if STB_C_LEX_C_DECIMAL_INTS(x) -#define STB__clex_decimal_ints -#endif - -#if STB_C_LEX_C_OCTAL_INTS(x) -#define STB__clex_octal_ints -#endif - -#if STB_C_LEX_C_DECIMAL_FLOATS(x) -#define STB__clex_decimal_floats -#endif - -#if STB_C_LEX_DISCARD_PREPROCESSOR(x) -#define STB__clex_discard_preprocessor -#endif - -// Now pick a definition of Y/N that's conducive to -// defining the enum of token names. -#if STB_C_LEX_DEFINE_ALL_TOKEN_NAMES(x) || defined(STB_C_LEXER_SELF_TEST) - #undef N - #define N(a) Y(a) -#else - #undef N - #define N(a) -#endif - -#undef Y -#define Y(a) a, - -enum -{ - CLEX_eof = 256, - CLEX_parse_error, - -#ifdef STB__clex_define_int - CLEX_intlit, -#endif - - STB_C_LEX_C_DECIMAL_FLOATS( CLEX_floatlit ) - STB_C_LEX_C_IDENTIFIERS( CLEX_id ) - STB_C_LEX_C_DQ_STRINGS( CLEX_dqstring ) - STB_C_LEX_C_SQ_STRINGS( CLEX_sqstring ) - STB_C_LEX_C_CHARS( CLEX_charlit ) - STB_C_LEX_C_COMPARISONS( CLEX_eq ) - STB_C_LEX_C_COMPARISONS( CLEX_noteq ) - STB_C_LEX_C_COMPARISONS( CLEX_lesseq ) - STB_C_LEX_C_COMPARISONS( CLEX_greatereq ) - STB_C_LEX_C_LOGICAL( CLEX_andand ) - STB_C_LEX_C_LOGICAL( CLEX_oror ) - STB_C_LEX_C_SHIFTS( CLEX_shl ) - STB_C_LEX_C_SHIFTS( CLEX_shr ) - STB_C_LEX_C_INCREMENTS( CLEX_plusplus ) - STB_C_LEX_C_INCREMENTS( CLEX_minusminus ) - STB_C_LEX_C_ARITHEQ( CLEX_pluseq ) - STB_C_LEX_C_ARITHEQ( CLEX_minuseq ) - STB_C_LEX_C_ARITHEQ( CLEX_muleq ) - STB_C_LEX_C_ARITHEQ( CLEX_diveq ) - STB_C_LEX_C_ARITHEQ( CLEX_modeq ) - STB_C_LEX_C_BITWISEEQ( CLEX_andeq ) - STB_C_LEX_C_BITWISEEQ( CLEX_oreq ) - STB_C_LEX_C_BITWISEEQ( CLEX_xoreq ) - STB_C_LEX_C_ARROW( CLEX_arrow ) - STB_C_LEX_EQUAL_ARROW( CLEX_eqarrow ) - -#ifdef STB__clex_define_shifts - CLEX_shleq, CLEX_shreq, -#endif - - CLEX_first_unused_token - -#undef Y -#define Y(a) a -}; - -// Now for the rest of the file we'll use the basic definition where -// where Y expands to its contents and N expands to nothing -#undef N -#define N(a) - -// API function -void stb_c_lexer_init(stb_lexer *lexer, const char *input_stream, const char *input_stream_end, char *string_store, int store_length) -{ - lexer->input_stream = (char *) input_stream; - lexer->eof = (char *) input_stream_end; - lexer->parse_point = (char *) input_stream; - lexer->string_storage = string_store; - lexer->string_storage_len = store_length; -} - -// API function -void stb_c_lexer_get_location(const stb_lexer *lexer, const char *where, stb_lex_location *loc) -{ - char *p = lexer->input_stream; - int line_number = 1; - int char_offset = 0; - while (*p && p < where) { - if (*p == '\n' || *p == '\r') { - p += (p[0]+p[1] == '\r'+'\n' ? 2 : 1); // skip newline - line_number += 1; - char_offset = 0; - } else { - ++p; - ++char_offset; - } - } - loc->line_number = line_number; - loc->line_offset = char_offset; -} - -// main helper function for returning a parsed token -static int stb__clex_token(stb_lexer *lexer, int token, char *start, char *end) -{ - lexer->token = token; - lexer->where_firstchar = start; - lexer->where_lastchar = end; - lexer->parse_point = end+1; - return 1; -} - -// helper function for returning eof -static int stb__clex_eof(stb_lexer *lexer) -{ - lexer->token = CLEX_eof; - return 0; -} - -static int stb__clex_iswhite(int x) -{ - return x == ' ' || x == '\t' || x == '\r' || x == '\n' || x == '\f'; -} - -static const char *stb__strchr(const char *str, int ch) -{ - for (; *str; ++str) - if (*str == ch) - return str; - return 0; -} - -// parse suffixes at the end of a number -static int stb__clex_parse_suffixes(stb_lexer *lexer, long tokenid, char *start, char *cur, const char *suffixes) -{ - #ifdef STB__clex_parse_suffixes - lexer->string = lexer->string_storage; - lexer->string_len = 0; - - while ((*cur >= 'a' && *cur <= 'z') || (*cur >= 'A' && *cur <= 'Z')) { - if (stb__strchr(suffixes, *cur) == 0) - return stb__clex_token(lexer, CLEX_parse_error, start, cur); - if (lexer->string_len+1 >= lexer->string_storage_len) - return stb__clex_token(lexer, CLEX_parse_error, start, cur); - lexer->string[lexer->string_len++] = *cur++; - } - #else - suffixes = suffixes; // attempt to suppress warnings - #endif - return stb__clex_token(lexer, tokenid, start, cur-1); -} - -#ifndef STB__CLEX_use_stdlib -static double stb__clex_parse_float(char *p, char **q) -{ - double value=0; - while (*p >= '0' && *p <= '9') - value = value*10 + (*p++ - '0'); - if (*p == '.') { - double powten=1, addend = 0; - ++p; - while (*p >= '0' && *p <= '9') { - addend = addend + 10*(*p++ - '0'); - powten *= 10; - } - value += addend / powten; - } - if (*p == 'e' || *p == 'E') { - int sign = p[1] == '-'; - int exponent=0; - double pow10=1; - p += 1+sign; - while (*p >= '0' && *p <= '9') - exponent = exponent*10 + (*p++ - '0'); - // can't use pow() from stdlib, so do it slow way - while (exponent-- > 0) - pow10 *= 10; - if (sign) - value /= pow10; - else - value *= pow10; - } - *q = p; - return value; -} -#endif - -static int stb__clex_parse_char(char *p, char **q) -{ - if (*p == '\\') { - *q = p+2; // tentatively guess we'll parse two characters - switch(p[1]) { - case '\\': return '\\'; - case '\'': return '\''; - case '"': return '"'; - case 't': return '\t'; - case 'f': return '\f'; - case 'n': return '\n'; - case 'r': return '\r'; - case '0': return '\0'; // @TODO ocatal constants - case 'x': case 'X': return -1; // @TODO hex constants - case 'u': return -1; // @TODO unicode constants - } - } - *q = p+1; - return (unsigned char) *p; -} - -static int stb__clex_parse_string(stb_lexer *lexer, char *p, int type) -{ - char *start = p; - char delim = *p++; // grab the " or ' for later matching - char *out = lexer->string_storage; - char *outend = lexer->string_storage + lexer->string_storage_len; - while (*p != delim) { - int n; - if (*p == '\\') { - char *q; - n = stb__clex_parse_char(p, &q); - if (n < 0) - return stb__clex_token(lexer, CLEX_parse_error, start, q); - p = q; - } else { - // @OPTIMIZE: could speed this up by looping-while-not-backslash - n = (unsigned char) *p++; - } - if (out+1 > outend) - return stb__clex_token(lexer, CLEX_parse_error, start, p); - // @TODO expand unicode escapes to UTF8 - *out++ = (char) n; - } - *out = 0; - lexer->string = lexer->string_storage; - lexer->string_len = out - lexer->string_storage; - return stb__clex_token(lexer, type, start, p); -} - -int stb_c_lexer_get_token(stb_lexer *lexer) -{ - char *p = lexer->parse_point; - - // skip whitespace and comments - for (;;) { - #ifdef STB_C_LEX_ISWHITE - while (p != lexer->stream_end) { - int n; - n = STB_C_LEX_ISWHITE(p); - if (n == 0) break; - if (lexer->eof && lexer+n > lexer->eof) - return stb__clex_token(tok, CLEX_parse_error, p,lexer->eof-1); - p += n; - } - #else - while (p != lexer->eof && stb__clex_iswhite(*p)) - ++p; - #endif - - STB_C_LEX_CPP_COMMENTS( - if (p != lexer->eof && p[0] == '/' && p[1] == '/') { - while (p != lexer->eof && *p != '\r' && *p != '\n') - ++p; - continue; - } - ) - - STB_C_LEX_C_COMMENTS( - if (p != lexer->eof && p[0] == '/' && p[1] == '*') { - char *start = p; - p += 2; - while (p != lexer->eof && (p[0] != '*' || p[1] != '/')) - ++p; - if (p == lexer->eof) - return stb__clex_token(lexer, CLEX_parse_error, start, p-1); - p += 2; - continue; - } - ) - - #ifdef STB__clex_discard_preprocessor - // @TODO this discards everything after a '#', regardless - // of where in the line the # is, rather than requiring it - // be at the start. (because this parser doesn't otherwise - // check for line breaks!) - if (p != lexer->eof && p[0] == '#') { - while (p != lexer->eof && *p != '\r' && *p != '\n') - ++p; - continue; - } - #endif - - break; - } - - if (p == lexer->eof) - return stb__clex_eof(lexer); - - switch (*p) { - default: - if ( (*p >= 'a' && *p <= 'z') - || (*p >= 'A' && *p <= 'Z') - || *p == '_' || (unsigned char) *p >= 128 // >= 128 is UTF8 char - STB_C_LEX_DOLLAR_IDENTIFIER( || *p == '$' ) ) - { - int n = 0; - lexer->string = lexer->string_storage; - lexer->string_len = n; - do { - if (n+1 >= lexer->string_storage_len) - return stb__clex_token(lexer, CLEX_parse_error, p, p+n); - lexer->string[n] = p[n]; - ++n; - } while ( - (p[n] >= 'a' && p[n] <= 'z') - || (p[n] >= 'A' && p[n] <= 'Z') - || (p[n] >= '0' && p[n] <= '9') // allow digits in middle of identifier - || p[n] == '_' || (unsigned char) p[n] >= 128 - STB_C_LEX_DOLLAR_IDENTIFIER( || p[n] == '$' ) - ); - lexer->string[n] = 0; - return stb__clex_token(lexer, CLEX_id, p, p+n-1); - } - - // check for EOF - STB_C_LEX_0_IS_EOF( - if (*p == 0) - return stb__clex_eof(tok); - ) - - single_char: - // not an identifier, return the character as itself - return stb__clex_token(lexer, *p, p, p); - - case '+': - if (p+1 != lexer->eof) { - STB_C_LEX_C_INCREMENTS(if (p[1] == '+') return stb__clex_token(lexer, CLEX_plusplus, p,p+1);) - STB_C_LEX_C_ARITHEQ( if (p[1] == '=') return stb__clex_token(lexer, CLEX_pluseq , p,p+1);) - } - goto single_char; - case '-': - if (p+1 != lexer->eof) { - STB_C_LEX_C_INCREMENTS(if (p[1] == '-') return stb__clex_token(lexer, CLEX_minusminus, p,p+1);) - STB_C_LEX_C_ARITHEQ( if (p[1] == '=') return stb__clex_token(lexer, CLEX_minuseq , p,p+1);) - STB_C_LEX_C_ARROW( if (p[1] == '>') return stb__clex_token(lexer, CLEX_arrow , p,p+1);) - } - goto single_char; - case '&': - if (p+1 != lexer->eof) { - STB_C_LEX_C_LOGICAL( if (p[1] == '&') return stb__clex_token(lexer, CLEX_andand, p,p+1);) - STB_C_LEX_C_BITWISEEQ(if (p[1] == '=') return stb__clex_token(lexer, CLEX_andeq , p,p+1);) - } - goto single_char; - case '|': - if (p+1 != lexer->eof) { - STB_C_LEX_C_LOGICAL( if (p[1] == '|') return stb__clex_token(lexer, CLEX_oror, p,p+1);) - STB_C_LEX_C_BITWISEEQ(if (p[1] == '=') return stb__clex_token(lexer, CLEX_oreq, p,p+1);) - } - goto single_char; - case '=': - if (p+1 != lexer->eof) { - STB_C_LEX_C_COMPARISONS(if (p[1] == '=') return stb__clex_token(lexer, CLEX_eq, p,p+1);) - STB_C_LEX_EQUAL_ARROW( if (p[1] == '>') return stb__clex_token(lexer, CLEX_eqarrow, p,p+1);) - } - goto single_char; - case '!': - STB_C_LEX_C_COMPARISONS(if (p+1 != lexer->eof && p[1] == '=') return stb__clex_token(lexer, CLEX_noteq, p,p+1);) - goto single_char; - case '^': - STB_C_LEX_C_BITWISEEQ(if (p+1 != lexer->eof && p[1] == '=') return stb__clex_token(lexer, CLEX_xoreq, p,p+1)); - goto single_char; - case '%': - STB_C_LEX_C_ARITHEQ(if (p+1 != lexer->eof && p[1] == '=') return stb__clex_token(lexer, CLEX_modeq, p,p+1)); - goto single_char; - case '*': - STB_C_LEX_C_ARITHEQ(if (p+1 != lexer->eof && p[1] == '=') return stb__clex_token(lexer, CLEX_muleq, p,p+1)); - goto single_char; - case '/': - STB_C_LEX_C_ARITHEQ(if (p+1 != lexer->eof && p[1] == '=') return stb__clex_token(lexer, CLEX_diveq, p,p+1)); - goto single_char; - case '<': - if (p+1 != lexer->eof) { - STB_C_LEX_C_COMPARISONS(if (p[1] == '=') return stb__clex_token(lexer, CLEX_lesseq, p,p+1);) - STB_C_LEX_C_SHIFTS( if (p[1] == '<') { - STB_C_LEX_C_ARITHEQ(if (p+2 != lexer->eof && p[2] == '=') - return stb__clex_token(lexer, CLEX_shleq, p,p+2);) - return stb__clex_token(lexer, CLEX_shl, p,p+1); - } - ) - } - goto single_char; - case '>': - if (p+1 != lexer->eof) { - STB_C_LEX_C_COMPARISONS(if (p[1] == '=') return stb__clex_token(lexer, CLEX_greatereq, p,p+1);) - STB_C_LEX_C_SHIFTS( if (p[1] == '>') { - STB_C_LEX_C_ARITHEQ(if (p+2 != lexer->eof && p[2] == '=') - return stb__clex_token(lexer, CLEX_shreq, p,p+2);) - return stb__clex_token(lexer, CLEX_shr, p,p+1); - } - ) - } - goto single_char; - - case '"': - STB_C_LEX_C_DQ_STRINGS(return stb__clex_parse_string(lexer, p, CLEX_dqstring);) - goto single_char; - case '\'': - STB_C_LEX_C_SQ_STRINGS(return stb__clex_parse_string(lexer, p, CLEX_sqstring);) - STB_C_LEX_C_CHARS( - { - char *start = p; - lexer->int_number = stb__clex_parse_char(p+1, &p); - if (lexer->int_number < 0) - return stb__clex_token(lexer, CLEX_parse_error, start,start); - if (p == lexer->eof || *p != '\'') - return stb__clex_token(lexer, CLEX_parse_error, start,p); - return stb__clex_token(lexer, CLEX_charlit, start, p+1); - }) - goto single_char; - - case '0': - #ifdef STB__clex_hex_ints - if (p+1 != lexer->eof) { - if (p[1] == 'x' || p[1] == 'X') { - char *q = p+2; - #ifdef STB__CLEX_use_stdlib - lexer->int_number = strtol((char *) p, (char **) &q, 16); - #else - stb__clex_int n=0; - while (q != lexer->eof) { - if (*q >= '0' && *q <= '9') - n = n*16 + (*q - '0'); - else if (*q >= 'a' && *q <= 'f') - n = n*16 + (*q - 'a') + 10; - else if (*q >= 'A' && *q <= 'F') - n = n*16 + (*q - 'A') + 10; - else - break; - ++q; - } - lexer->int_field = n; // int_field is macro that expands to real_number/int_number depending on type of n - #endif - if (q == p+2) - return stb__clex_token(lexer, CLEX_parse_error, p-2,p-1); - return stb__clex_parse_suffixes(lexer, CLEX_intlit, p,q, STB_C_LEX_HEX_SUFFIXES); - } - } - #endif // STB__clex_hex_ints - // can't test for octal because we might parse '0.0' as float or as '0' '.' '0', - // so have to do float first - - /* FALL THROUGH */ - case '1': case '2': case '3': case '4': case '5': case '6': case '7': case '8': case '9': - - #ifdef STB__clex_decimal_floats - { - char *q = p; - while (q != lexer->eof && (*q >= '0' && *q <= '9')) - ++q; - if (q != lexer->eof) { - if (*q == '.' STB_C_LEX_FLOAT_NO_DECIMAL(|| *q == 'e' || *q == 'E')) { - #ifdef STB__CLEX_use_stdlib - lexer->real_number = strtod((char *) p, (char**) &q); - #else - lexer->real_number = stb__clex_parse_float(p, &q); - #endif - - return stb__clex_parse_suffixes(lexer, CLEX_floatlit, p,q, STB_C_LEX_FLOAT_SUFFIXES); - - } - } - } - #endif // STB__clex_decimal_floats - - #ifdef STB__clex_octal_ints - if (p[0] == '0') { - char *q = p; - #ifdef STB__CLEX_use_stdlib - lexer->int_number = strtol((char *) p, (char **) &q, 8); - #else - stb__clex_int n=0; - while (q != lexer->eof) { - if (*q >= '0' && *q <= '7') - n = n*8 + (q - '0'); - else - break; - ++q; - } - if (q != lexer->eof && (*q == '8' || *q=='9')) - return stb__clex_token(tok, CLEX_parse_error, p, q); - lexer->int_field = n; - #endif - return stb__clex_parse_suffixes(lexer, CLEX_intlit, p,q, STB_C_LEX_OCTAL_SUFFIXES); - } - #endif // STB__clex_octal_ints - - #ifdef STB__clex_decimal_ints - { - char *q = p; - #ifdef STB__CLEX_use_stdlib - lexer->int_number = strtol((char *) p, (char **) &q, 10); - #else - stb__clex_int n=0; - while (q != lexer->eof) { - if (*q >= '0' && *q <= '9') - n = n*10 + (q - '0'); - else - break; - ++q; - } - lexer->int_field = n; - #endif - return stb__clex_parse_suffixes(lexer, CLEX_intlit, p,q, STB_C_LEX_OCTAL_SUFFIXES); - } - #endif // STB__clex_decimal_ints - goto single_char; - } -} -#endif // STB_C_LEXER_IMPLEMENTATION - -#ifdef STB_C_LEXER_SELF_TEST - -#include - -static void print_token(stb_lexer *lexer) -{ - switch (lexer->token) { - case CLEX_id : printf("_%s", lexer->string); break; - case CLEX_eq : printf("=="); break; - case CLEX_noteq : printf("!="); break; - case CLEX_lesseq : printf("<="); break; - case CLEX_greatereq : printf(">="); break; - case CLEX_andand : printf("&&"); break; - case CLEX_oror : printf("||"); break; - case CLEX_shl : printf("<<"); break; - case CLEX_shr : printf(">>"); break; - case CLEX_plusplus : printf("++"); break; - case CLEX_minusminus: printf("--"); break; - case CLEX_arrow : printf("->"); break; - case CLEX_andeq : printf("&="); break; - case CLEX_oreq : printf("|="); break; - case CLEX_xoreq : printf("^="); break; - case CLEX_pluseq : printf("+="); break; - case CLEX_minuseq : printf("-="); break; - case CLEX_muleq : printf("*="); break; - case CLEX_diveq : printf("/="); break; - case CLEX_modeq : printf("%%="); break; - case CLEX_shleq : printf("<<="); break; - case CLEX_shreq : printf(">>="); break; - case CLEX_eqarrow : printf("=>"); break; - case CLEX_dqstring : printf("\"%s\"", lexer->string); break; - case CLEX_sqstring : printf("'\"%s\"'", lexer->string); break; - case CLEX_charlit : printf("'%s'", lexer->string); break; - #if defined(STB__clex_int_as_double) && !defined(STB__CLEX_use_stdlib) - case CLEX_intlit : printf("#%g", lexer->real_number); break; - #else - case CLEX_intlit : printf("#%ld", lexer->int_number); break; - #endif - case CLEX_floatlit : printf("%g", lexer->real_number); break; - default: - if (lexer->token >= 0 && lexer->token < 256) - printf("%c", (int) lexer->token); - else { - printf("<<>>\n", lexer->token); - } - break; - } -} - -/* Force a test -of parsing -multiline comments */ - -/*/ comment /*/ -/**/ extern /**/ - -void dummy(void) -{ - printf("test",1); // https://github.com/nothings/stb/issues/13 -} - -int main(int argc, char **argv) -{ - FILE *f = fopen("stb_c_lexer.h","rb"); - char *text = (char *) malloc(1 << 20); - int len = f ? fread(text, 1, 1<<20, f) : -1; - stb_lexer lex; - if (len < 0) { - fprintf(stderr, "Error opening file\n"); - return 1; - } - fclose(f); - - stb_c_lexer_init(&lex, text, text+len, (char *) malloc(1<<16), 1<<16); - while (stb_c_lexer_get_token(&lex)) { - if (lex.token == CLEX_parse_error) { - printf("\n<<>>\n"); - break; - } - print_token(&lex); - printf(" "); - } - return 0; -} -#endif diff --git a/impeller/third_party/stb/stb/stb_divide.h b/impeller/third_party/stb/stb/stb_divide.h deleted file mode 100644 index d1702f3b522af..0000000000000 --- a/impeller/third_party/stb/stb/stb_divide.h +++ /dev/null @@ -1,379 +0,0 @@ -// stb_divide.h - v0.91 - public domain - Sean Barrett, Feb 2010 -// Three kinds of divide/modulus of signed integers. -// -// HISTORY -// -// v0.91 2010-02-27 Fix euclidean division by INT_MIN for non-truncating C -// Check result with 64-bit math to catch such cases -// v0.90 2010-02-24 First public release -// -// USAGE -// -// In *ONE* source file, put: -// -// #define STB_DIVIDE_IMPLEMENTATION -// // #define C_INTEGER_DIVISION_TRUNCATES // see Note 1 -// // #define C_INTEGER_DIVISION_FLOORS // see Note 2 -// #include "stb_divide.h" -// -// Other source files should just include stb_divide.h -// -// Note 1: On platforms/compilers that you know signed C division -// truncates, you can #define C_INTEGER_DIVISION_TRUNCATES. -// -// Note 2: On platforms/compilers that you know signed C division -// floors (rounds to negative infinity), you can #define -// C_INTEGER_DIVISION_FLOORS. -// -// You can #define STB_DIVIDE_TEST in which case the implementation -// will generate a main() and compiling the result will create a -// program that tests the implementation. Run it with no arguments -// and any output indicates an error; run it with any argument and -// it will also print the test results. Define STB_DIVIDE_TEST_64 -// to a 64-bit integer type to avoid overflows in the result-checking -// which give false negatives. -// -// ABOUT -// -// This file provides three different consistent divide/mod pairs -// implemented on top of arbitrary C/C++ division, including correct -// handling of overflow of intermediate calculations: -// -// trunc: a/b truncates to 0, a%b has same sign as a -// floor: a/b truncates to -inf, a%b has same sign as b -// eucl: a/b truncates to sign(b)*inf, a%b is non-negative -// -// Not necessarily optimal; I tried to keep it generally efficient, -// but there may be better ways. -// -// Briefly, for those who are not familiar with the problem, we note -// the reason these divides exist and are interesting: -// -// 'trunc' is easy to implement in hardware (strip the signs, -// compute, reapply the signs), thus is commonly defined -// by many languages (including C99) -// -// 'floor' is simple to define and better behaved than trunc; -// for example it divides integers into fixed-size buckets -// without an extra-wide bucket at 0, and for a fixed -// divisor N there are only |N| possible moduli. -// -// 'eucl' guarantees fixed-sized buckets *and* a non-negative -// modulus and defines division to be whatever is needed -// to achieve that result. -// -// See "The Euclidean definition of the functions div and mod" -// by Raymond Boute (1992), or "Division and Modulus for Computer -// Scientists" by Daan Leijen (2001) -// -// We assume of the built-in C division: -// (a) modulus is the remainder for the corresponding division -// (b) a/b truncates if a and b are the same sign -// -// Property (a) requires (a/b)*b + (a%b)==a, and is required by C. -// Property (b) seems to be true of all hardware but is *not* satisfied -// by the euclidean division operator we define, so it's possibly not -// always true. If any such platform turns up, we can add more cases. -// (Possibly only stb_div_trunc currently relies on property (b).) -// -// LICENSE -// -// This software is dual-licensed to the public domain and under the following -// license: you are granted a perpetual, irrevocable license to copy, modify, -// publish, and distribute this file as you see fit. - - -#ifndef INCLUDE_STB_DIVIDE_H -#define INCLUDE_STB_DIVIDE_H - -#ifdef __cplusplus -extern "C" { -#endif - -extern int stb_div_trunc(int value_to_be_divided, int value_to_divide_by); -extern int stb_div_floor(int value_to_be_divided, int value_to_divide_by); -extern int stb_div_eucl (int value_to_be_divided, int value_to_divide_by); -extern int stb_mod_trunc(int value_to_be_divided, int value_to_divide_by); -extern int stb_mod_floor(int value_to_be_divided, int value_to_divide_by); -extern int stb_mod_eucl (int value_to_be_divided, int value_to_divide_by); - -#ifdef __cplusplus -} -#endif - -#ifdef STB_DIVIDE_IMPLEMENTATION - -#if defined(__STDC_VERSION) && __STDC_VERSION__ >= 19901 - #ifndef C_INTEGER_DIVISION_TRUNCATES - #define C_INTEGER_DIVISION_TRUNCATES - #endif -#endif - -#ifndef INT_MIN -#include // if you have no limits.h, #define INT_MIN yourself -#endif - -// the following macros are designed to allow testing -// other platforms by simulating them -#ifndef STB_DIVIDE_TEST_FLOOR - #define stb__div(a,b) ((a)/(b)) - #define stb__mod(a,b) ((a)%(b)) -#else - // implement floor-style divide on trunc platform - #ifndef C_INTEGER_DIVISION_TRUNCATES - #error "floor test requires truncating division" - #endif - #undef C_INTEGER_DIVISION_TRUNCATES - int stb__div(int v1, int v2) - { - int q = v1/v2, r = v1%v2; - if ((r > 0 && v2 < 0) || (r < 0 && v2 > 0)) - return q-1; - else - return q; - } - - int stb__mod(int v1, int v2) - { - int r = v1%v2; - if ((r > 0 && v2 < 0) || (r < 0 && v2 > 0)) - return r+v2; - else - return r; - } -#endif - -int stb_div_trunc(int v1, int v2) -{ - #ifdef C_INTEGER_DIVISION_TRUNCATES - return v1/v2; - #else - if (v1 >= 0 && v2 <= 0) - return -stb__div(-v1,v2); // both negative to avoid overflow - if (v1 <= 0 && v2 >= 0) - if (v1 != INT_MIN) - return -stb__div(v1,-v2); // both negative to avoid overflow - else - return -stb__div(v1+v2,-v2)-1; // push v1 away from wrap point - else - return v1/v2; // same sign, so expect truncation - #endif -} - -int stb_div_floor(int v1, int v2) -{ - #ifdef C_INTEGER_DIVISION_FLOORS - return v1/v2; - #else - if (v1 >= 0 && v2 < 0) - if ((-v1)+v2+1 < 0) // check if increasing v1's magnitude overflows - return -stb__div(-v1+v2+1,v2); // nope, so just compute it - else - return -stb__div(-v1,v2) + ((-v1)%v2 ? -1 : 0); - if (v1 < 0 && v2 >= 0) - if (v1 != INT_MIN) - if (v1-v2+1 < 0) // check if increasing v1's magnitude overflows - return -stb__div(v1-v2+1,-v2); // nope, so just compute it - else - return -stb__div(-v1,v2) + (stb__mod(v1,-v2) ? -1 : 0); - else // it must be possible to compute -(v1+v2) without overflowing - return -stb__div(-(v1+v2),v2) + (stb__mod(-(v1+v2),v2) ? -2 : -1); - else - return v1/v2; // same sign, so expect truncation - #endif -} - -int stb_div_eucl(int v1, int v2) -{ - int q,r; - #ifdef C_INTEGER_DIVISION_TRUNCATES - q = v1/v2; - r = v1%v2; - #else - // handle every quadrant separately, since we can't rely on q and r flor - if (v1 >= 0) - if (v2 >= 0) - return stb__div(v1,v2); - else if (v2 != INT_MIN) - q = -stb__div(v1,-v2), r = stb__mod(v1,-v2); - else - q = 0, r = v1; - else if (v1 != INT_MIN) - if (v2 >= 0) - q = -stb__div(-v1,v2), r = -stb__mod(-v1,v2); - else if (v2 != INT_MIN) - q = stb__div(-v1,-v2), r = -stb__mod(-v1,-v2); - else // if v2 is INT_MIN, then we can't use -v2, but we can't divide by v2 - q = 1, r = v1-q*v2; - else // if v1 is INT_MIN, we have to move away from overflow place - if (v2 >= 0) - q = -stb__div(-(v1+v2),v2)-1, r = -stb__mod(-(v1+v2),v2); - else - q = stb__div(-(v1-v2),-v2)+1, r = -stb__mod(-(v1-v2),-v2); - #endif - if (r >= 0) - return q; - else - return q + (v2 > 0 ? -1 : 1); -} - -int stb_mod_trunc(int v1, int v2) -{ - #ifdef C_INTEGER_DIVISION_TRUNCATES - return v1%v2; - #else - if (v1 >= 0) { // modulus result should always be positive - int r = stb__mod(v1,v2); - if (r >= 0) - return r; - else - return r + (v2 > 0 ? v2 : -v2); - } else { // modulus result should always be negative - int r = stb__mod(v1,v2); - if (r <= 0) - return r; - else - return r - (v2 > 0 ? v2 : -v2); - } - #endif -} - -int stb_mod_floor(int v1, int v2) -{ - #ifdef C_INTEGER_DIVISION_FLOORS - return v1%v2; - #else - if (v2 >= 0) { // result should always be positive - int r = stb__mod(v1,v2); - if (r >= 0) - return r; - else - return r + v2; - } else { // result should always be negative - int r = stb__mod(v1,v2); - if (r <= 0) - return r; - else - return r + v2; - } - #endif -} - -int stb_mod_eucl(int v1, int v2) -{ - int r = stb__mod(v1,v2); - - if (r >= 0) - return r; - else - return r + (v2 > 0 ? v2 : -v2); // abs() -} - -#ifdef STB_DIVIDE_TEST -#include -#include -#include - -int show=0; - -void stbdiv_check(int q, int r, int a, int b, char *type, int dir) -{ - if ((dir > 0 && r < 0) || (dir < 0 && r > 0)) - fprintf(stderr, "FAILED: %s(%d,%d) remainder %d in wrong direction\n", type,a,b,r); - else - if (b != INT_MIN) // can't compute abs(), but if b==INT_MIN all remainders are valid - if (r <= -abs(b) || r >= abs(b)) - fprintf(stderr, "FAILED: %s(%d,%d) remainder %d out of range\n", type,a,b,r); - #ifdef STB_DIVIDE_TEST_64 - { - STB_DIVIDE_TEST_64 q64 = q, r64=r, a64=a, b64=b; - if (q64*b64+r64 != a64) - fprintf(stderr, "FAILED: %s(%d,%d) remainder %d doesn't match quotient %d\n", type,a,b,r,q); - } - #else - if (q*b+r != a) - fprintf(stderr, "FAILED: %s(%d,%d) remainder %d doesn't match quotient %d\n", type,a,b,r,q); - #endif -} - -void test(int a, int b) -{ - int q,r; - if (show) printf("(%+11d,%+d) | ", a,b); - q = stb_div_trunc(a,b), r = stb_mod_trunc(a,b); - if (show) printf("(%+11d,%+2d) ", q,r); stbdiv_check(q,r,a,b, "trunc",a); - q = stb_div_floor(a,b), r = stb_mod_floor(a,b); - if (show) printf("(%+11d,%+2d) ", q,r); stbdiv_check(q,r,a,b, "floor",b); - q = stb_div_eucl (a,b), r = stb_mod_eucl (a,b); - if (show) printf("(%+11d,%+2d)\n", q,r); stbdiv_check(q,r,a,b, "euclidean",1); -} - -void testh(int a, int b) -{ - int q,r; - if (show) printf("(%08x,%08x) |\n", a,b); - q = stb_div_trunc(a,b), r = stb_mod_trunc(a,b); stbdiv_check(q,r,a,b, "trunc",a); - if (show) printf(" (%08x,%08x)", q,r); - q = stb_div_floor(a,b), r = stb_mod_floor(a,b); stbdiv_check(q,r,a,b, "floor",b); - if (show) printf(" (%08x,%08x)", q,r); - q = stb_div_eucl (a,b), r = stb_mod_eucl (a,b); stbdiv_check(q,r,a,b, "euclidean",1); - if (show) printf(" (%08x,%08x)\n ", q,r); -} - -int main(int argc, char **argv) -{ - if (argc > 1) show=1; - - test(8,3); - test(8,-3); - test(-8,3); - test(-8,-3); - test(1,2); - test(1,-2); - test(-1,2); - test(-1,-2); - test(8,4); - test(8,-4); - test(-8,4); - test(-8,-4); - - test(INT_MAX,1); - test(INT_MIN,1); - test(INT_MIN+1,1); - test(INT_MAX,-1); - //test(INT_MIN,-1); // this traps in MSVC, so we leave it untested - test(INT_MIN+1,-1); - test(INT_MIN,-2); - test(INT_MIN+1,2); - test(INT_MIN+1,-2); - test(INT_MAX,2); - test(INT_MAX,-2); - test(INT_MIN+1,2); - test(INT_MIN+1,-2); - test(INT_MIN,2); - test(INT_MIN,-2); - test(INT_MIN,7); - test(INT_MIN,-7); - test(INT_MIN+1,4); - test(INT_MIN+1,-4); - - testh(-7, INT_MIN); - testh(-1, INT_MIN); - testh(1, INT_MIN); - testh(7, INT_MIN); - - testh(INT_MAX-1, INT_MIN); - testh(INT_MAX, INT_MIN); - testh(INT_MIN, INT_MIN); - testh(INT_MIN+1, INT_MIN); - - testh(INT_MAX-1, INT_MAX); - testh(INT_MAX , INT_MAX); - testh(INT_MIN , INT_MAX); - testh(INT_MIN+1, INT_MAX); - - return 0; -} -#endif // STB_DIVIDE_TEST -#endif // STB_DIVIDE_IMPLEMENTATION -#endif // INCLUDE_STB_DIVIDE_H diff --git a/impeller/third_party/stb/stb/stb_dxt.h b/impeller/third_party/stb/stb/stb_dxt.h deleted file mode 100644 index 0a8b34ae2350d..0000000000000 --- a/impeller/third_party/stb/stb/stb_dxt.h +++ /dev/null @@ -1,630 +0,0 @@ -// stb_dxt.h - v1.04 - DXT1/DXT5 compressor - public domain -// original by fabian "ryg" giesen - ported to C by stb -// use '#define STB_DXT_IMPLEMENTATION' before including to create the implementation -// -// USAGE: -// call stb_compress_dxt_block() for every block (you must pad) -// source should be a 4x4 block of RGBA data in row-major order; -// A is ignored if you specify alpha=0; you can turn on dithering -// and "high quality" using mode. -// -// version history: -// v1.04 - (ryg) default to no rounding bias for lerped colors (as per S3TC/DX10 spec); -// single color match fix (allow for inexact color interpolation); -// optimal DXT5 index finder; "high quality" mode that runs multiple refinement steps. -// v1.03 - (stb) endianness support -// v1.02 - (stb) fix alpha encoding bug -// v1.01 - (stb) fix bug converting to RGB that messed up quality, thanks ryg & cbloom -// v1.00 - (stb) first release -// -// LICENSE -// -// This software is dual-licensed to the public domain and under the following -// license: you are granted a perpetual, irrevocable license to copy, modify, -// publish, and distribute this file as you see fit. - -#ifndef STB_INCLUDE_STB_DXT_H -#define STB_INCLUDE_STB_DXT_H - -// compression mode (bitflags) -#define STB_DXT_NORMAL 0 -#define STB_DXT_DITHER 1 // use dithering. dubious win. never use for normal maps and the like! -#define STB_DXT_HIGHQUAL 2 // high quality mode, does two refinement steps instead of 1. ~30-40% slower. - -void stb_compress_dxt_block(unsigned char *dest, const unsigned char *src, int alpha, int mode); -#define STB_COMPRESS_DXT_BLOCK - -#ifdef STB_DXT_IMPLEMENTATION - -// configuration options for DXT encoder. set them in the project/makefile or just define -// them at the top. - -// STB_DXT_USE_ROUNDING_BIAS -// use a rounding bias during color interpolation. this is closer to what "ideal" -// interpolation would do but doesn't match the S3TC/DX10 spec. old versions (pre-1.03) -// implicitly had this turned on. -// -// in case you're targeting a specific type of hardware (e.g. console programmers): -// NVidia and Intel GPUs (as of 2010) as well as DX9 ref use DXT decoders that are closer -// to STB_DXT_USE_ROUNDING_BIAS. AMD/ATI, S3 and DX10 ref are closer to rounding with no bias. -// you also see "(a*5 + b*3) / 8" on some old GPU designs. -// #define STB_DXT_USE_ROUNDING_BIAS - -#include -#include -#include // memset - -static unsigned char stb__Expand5[32]; -static unsigned char stb__Expand6[64]; -static unsigned char stb__OMatch5[256][2]; -static unsigned char stb__OMatch6[256][2]; -static unsigned char stb__QuantRBTab[256+16]; -static unsigned char stb__QuantGTab[256+16]; - -static int stb__Mul8Bit(int a, int b) -{ - int t = a*b + 128; - return (t + (t >> 8)) >> 8; -} - -static void stb__From16Bit(unsigned char *out, unsigned short v) -{ - int rv = (v & 0xf800) >> 11; - int gv = (v & 0x07e0) >> 5; - int bv = (v & 0x001f) >> 0; - - out[0] = stb__Expand5[rv]; - out[1] = stb__Expand6[gv]; - out[2] = stb__Expand5[bv]; - out[3] = 0; -} - -static unsigned short stb__As16Bit(int r, int g, int b) -{ - return (stb__Mul8Bit(r,31) << 11) + (stb__Mul8Bit(g,63) << 5) + stb__Mul8Bit(b,31); -} - -// linear interpolation at 1/3 point between a and b, using desired rounding type -static int stb__Lerp13(int a, int b) -{ -#ifdef STB_DXT_USE_ROUNDING_BIAS - // with rounding bias - return a + stb__Mul8Bit(b-a, 0x55); -#else - // without rounding bias - // replace "/ 3" by "* 0xaaab) >> 17" if your compiler sucks or you really need every ounce of speed. - return (2*a + b) / 3; -#endif -} - -// lerp RGB color -static void stb__Lerp13RGB(unsigned char *out, unsigned char *p1, unsigned char *p2) -{ - out[0] = stb__Lerp13(p1[0], p2[0]); - out[1] = stb__Lerp13(p1[1], p2[1]); - out[2] = stb__Lerp13(p1[2], p2[2]); -} - -/****************************************************************************/ - -// compute table to reproduce constant colors as accurately as possible -static void stb__PrepareOptTable(unsigned char *Table,const unsigned char *expand,int size) -{ - int i,mn,mx; - for (i=0;i<256;i++) { - int bestErr = 256; - for (mn=0;mn> 4)]; - ep1[0] = bp[ 0] - dp[ 0]; - dp[ 4] = quant[bp[ 4] + ((7*ep1[0] + 3*ep2[2] + 5*ep2[1] + ep2[0]) >> 4)]; - ep1[1] = bp[ 4] - dp[ 4]; - dp[ 8] = quant[bp[ 8] + ((7*ep1[1] + 3*ep2[3] + 5*ep2[2] + ep2[1]) >> 4)]; - ep1[2] = bp[ 8] - dp[ 8]; - dp[12] = quant[bp[12] + ((7*ep1[2] + 5*ep2[3] + ep2[2]) >> 4)]; - ep1[3] = bp[12] - dp[12]; - bp += 16; - dp += 16; - et = ep1, ep1 = ep2, ep2 = et; // swap - } - } -} - -// The color matching function -static unsigned int stb__MatchColorsBlock(unsigned char *block, unsigned char *color,int dither) -{ - unsigned int mask = 0; - int dirr = color[0*4+0] - color[1*4+0]; - int dirg = color[0*4+1] - color[1*4+1]; - int dirb = color[0*4+2] - color[1*4+2]; - int dots[16]; - int stops[4]; - int i; - int c0Point, halfPoint, c3Point; - - for(i=0;i<16;i++) - dots[i] = block[i*4+0]*dirr + block[i*4+1]*dirg + block[i*4+2]*dirb; - - for(i=0;i<4;i++) - stops[i] = color[i*4+0]*dirr + color[i*4+1]*dirg + color[i*4+2]*dirb; - - // think of the colors as arranged on a line; project point onto that line, then choose - // next color out of available ones. we compute the crossover points for "best color in top - // half"/"best in bottom half" and then the same inside that subinterval. - // - // relying on this 1d approximation isn't always optimal in terms of euclidean distance, - // but it's very close and a lot faster. - // http://cbloomrants.blogspot.com/2008/12/12-08-08-dxtc-summary.html - - c0Point = (stops[1] + stops[3]) >> 1; - halfPoint = (stops[3] + stops[2]) >> 1; - c3Point = (stops[2] + stops[0]) >> 1; - - if(!dither) { - // the version without dithering is straightforward - for (i=15;i>=0;i--) { - int dot = dots[i]; - mask <<= 2; - - if(dot < halfPoint) - mask |= (dot < c0Point) ? 1 : 3; - else - mask |= (dot < c3Point) ? 2 : 0; - } - } else { - // with floyd-steinberg dithering - int err[8],*ep1 = err,*ep2 = err+4; - int *dp = dots, y; - - c0Point <<= 4; - halfPoint <<= 4; - c3Point <<= 4; - for(i=0;i<8;i++) - err[i] = 0; - - for(y=0;y<4;y++) - { - int dot,lmask,step; - - dot = (dp[0] << 4) + (3*ep2[1] + 5*ep2[0]); - if(dot < halfPoint) - step = (dot < c0Point) ? 1 : 3; - else - step = (dot < c3Point) ? 2 : 0; - ep1[0] = dp[0] - stops[step]; - lmask = step; - - dot = (dp[1] << 4) + (7*ep1[0] + 3*ep2[2] + 5*ep2[1] + ep2[0]); - if(dot < halfPoint) - step = (dot < c0Point) ? 1 : 3; - else - step = (dot < c3Point) ? 2 : 0; - ep1[1] = dp[1] - stops[step]; - lmask |= step<<2; - - dot = (dp[2] << 4) + (7*ep1[1] + 3*ep2[3] + 5*ep2[2] + ep2[1]); - if(dot < halfPoint) - step = (dot < c0Point) ? 1 : 3; - else - step = (dot < c3Point) ? 2 : 0; - ep1[2] = dp[2] - stops[step]; - lmask |= step<<4; - - dot = (dp[3] << 4) + (7*ep1[2] + 5*ep2[3] + ep2[2]); - if(dot < halfPoint) - step = (dot < c0Point) ? 1 : 3; - else - step = (dot < c3Point) ? 2 : 0; - ep1[3] = dp[3] - stops[step]; - lmask |= step<<6; - - dp += 4; - mask |= lmask << (y*8); - { int *et = ep1; ep1 = ep2; ep2 = et; } // swap - } - } - - return mask; -} - -// The color optimization function. (Clever code, part 1) -static void stb__OptimizeColorsBlock(unsigned char *block, unsigned short *pmax16, unsigned short *pmin16) -{ - int mind = 0x7fffffff,maxd = -0x7fffffff; - unsigned char *minp, *maxp; - double magn; - int v_r,v_g,v_b; - static const int nIterPower = 4; - float covf[6],vfr,vfg,vfb; - - // determine color distribution - int cov[6]; - int mu[3],min[3],max[3]; - int ch,i,iter; - - for(ch=0;ch<3;ch++) - { - const unsigned char *bp = ((const unsigned char *) block) + ch; - int muv,minv,maxv; - - muv = minv = maxv = bp[0]; - for(i=4;i<64;i+=4) - { - muv += bp[i]; - if (bp[i] < minv) minv = bp[i]; - else if (bp[i] > maxv) maxv = bp[i]; - } - - mu[ch] = (muv + 8) >> 4; - min[ch] = minv; - max[ch] = maxv; - } - - // determine covariance matrix - for (i=0;i<6;i++) - cov[i] = 0; - - for (i=0;i<16;i++) - { - int r = block[i*4+0] - mu[0]; - int g = block[i*4+1] - mu[1]; - int b = block[i*4+2] - mu[2]; - - cov[0] += r*r; - cov[1] += r*g; - cov[2] += r*b; - cov[3] += g*g; - cov[4] += g*b; - cov[5] += b*b; - } - - // convert covariance matrix to float, find principal axis via power iter - for(i=0;i<6;i++) - covf[i] = cov[i] / 255.0f; - - vfr = (float) (max[0] - min[0]); - vfg = (float) (max[1] - min[1]); - vfb = (float) (max[2] - min[2]); - - for(iter=0;iter magn) magn = fabs(vfg); - if (fabs(vfb) > magn) magn = fabs(vfb); - - if(magn < 4.0f) { // too small, default to luminance - v_r = 299; // JPEG YCbCr luma coefs, scaled by 1000. - v_g = 587; - v_b = 114; - } else { - magn = 512.0 / magn; - v_r = (int) (vfr * magn); - v_g = (int) (vfg * magn); - v_b = (int) (vfb * magn); - } - - // Pick colors at extreme points - for(i=0;i<16;i++) - { - int dot = block[i*4+0]*v_r + block[i*4+1]*v_g + block[i*4+2]*v_b; - - if (dot < mind) { - mind = dot; - minp = block+i*4; - } - - if (dot > maxd) { - maxd = dot; - maxp = block+i*4; - } - } - - *pmax16 = stb__As16Bit(maxp[0],maxp[1],maxp[2]); - *pmin16 = stb__As16Bit(minp[0],minp[1],minp[2]); -} - -static int stb__sclamp(float y, int p0, int p1) -{ - int x = (int) y; - if (x < p0) return p0; - if (x > p1) return p1; - return x; -} - -// The refinement function. (Clever code, part 2) -// Tries to optimize colors to suit block contents better. -// (By solving a least squares system via normal equations+Cramer's rule) -static int stb__RefineBlock(unsigned char *block, unsigned short *pmax16, unsigned short *pmin16, unsigned int mask) -{ - static const int w1Tab[4] = { 3,0,2,1 }; - static const int prods[4] = { 0x090000,0x000900,0x040102,0x010402 }; - // ^some magic to save a lot of multiplies in the accumulating loop... - // (precomputed products of weights for least squares system, accumulated inside one 32-bit register) - - float frb,fg; - unsigned short oldMin, oldMax, min16, max16; - int i, akku = 0, xx,xy,yy; - int At1_r,At1_g,At1_b; - int At2_r,At2_g,At2_b; - unsigned int cm = mask; - - oldMin = *pmin16; - oldMax = *pmax16; - - if((mask ^ (mask<<2)) < 4) // all pixels have the same index? - { - // yes, linear system would be singular; solve using optimal - // single-color match on average color - int r = 8, g = 8, b = 8; - for (i=0;i<16;++i) { - r += block[i*4+0]; - g += block[i*4+1]; - b += block[i*4+2]; - } - - r >>= 4; g >>= 4; b >>= 4; - - max16 = (stb__OMatch5[r][0]<<11) | (stb__OMatch6[g][0]<<5) | stb__OMatch5[b][0]; - min16 = (stb__OMatch5[r][1]<<11) | (stb__OMatch6[g][1]<<5) | stb__OMatch5[b][1]; - } else { - At1_r = At1_g = At1_b = 0; - At2_r = At2_g = At2_b = 0; - for (i=0;i<16;++i,cm>>=2) { - int step = cm&3; - int w1 = w1Tab[step]; - int r = block[i*4+0]; - int g = block[i*4+1]; - int b = block[i*4+2]; - - akku += prods[step]; - At1_r += w1*r; - At1_g += w1*g; - At1_b += w1*b; - At2_r += r; - At2_g += g; - At2_b += b; - } - - At2_r = 3*At2_r - At1_r; - At2_g = 3*At2_g - At1_g; - At2_b = 3*At2_b - At1_b; - - // extract solutions and decide solvability - xx = akku >> 16; - yy = (akku >> 8) & 0xff; - xy = (akku >> 0) & 0xff; - - frb = 3.0f * 31.0f / 255.0f / (xx*yy - xy*xy); - fg = frb * 63.0f / 31.0f; - - // solve. - max16 = stb__sclamp((At1_r*yy - At2_r*xy)*frb+0.5f,0,31) << 11; - max16 |= stb__sclamp((At1_g*yy - At2_g*xy)*fg +0.5f,0,63) << 5; - max16 |= stb__sclamp((At1_b*yy - At2_b*xy)*frb+0.5f,0,31) << 0; - - min16 = stb__sclamp((At2_r*xx - At1_r*xy)*frb+0.5f,0,31) << 11; - min16 |= stb__sclamp((At2_g*xx - At1_g*xy)*fg +0.5f,0,63) << 5; - min16 |= stb__sclamp((At2_b*xx - At1_b*xy)*frb+0.5f,0,31) << 0; - } - - *pmin16 = min16; - *pmax16 = max16; - return oldMin != min16 || oldMax != max16; -} - -// Color block compression -static void stb__CompressColorBlock(unsigned char *dest, unsigned char *block, int mode) -{ - unsigned int mask; - int i; - int dither; - int refinecount; - unsigned short max16, min16; - unsigned char dblock[16*4],color[4*4]; - - dither = mode & STB_DXT_DITHER; - refinecount = (mode & STB_DXT_HIGHQUAL) ? 2 : 1; - - // check if block is constant - for (i=1;i<16;i++) - if (((unsigned int *) block)[i] != ((unsigned int *) block)[0]) - break; - - if(i == 16) { // constant color - int r = block[0], g = block[1], b = block[2]; - mask = 0xaaaaaaaa; - max16 = (stb__OMatch5[r][0]<<11) | (stb__OMatch6[g][0]<<5) | stb__OMatch5[b][0]; - min16 = (stb__OMatch5[r][1]<<11) | (stb__OMatch6[g][1]<<5) | stb__OMatch5[b][1]; - } else { - // first step: compute dithered version for PCA if desired - if(dither) - stb__DitherBlock(dblock,block); - - // second step: pca+map along principal axis - stb__OptimizeColorsBlock(dither ? dblock : block,&max16,&min16); - if (max16 != min16) { - stb__EvalColors(color,max16,min16); - mask = stb__MatchColorsBlock(block,color,dither); - } else - mask = 0; - - // third step: refine (multiple times if requested) - for (i=0;i> 8); - dest[2] = (unsigned char) (min16); - dest[3] = (unsigned char) (min16 >> 8); - dest[4] = (unsigned char) (mask); - dest[5] = (unsigned char) (mask >> 8); - dest[6] = (unsigned char) (mask >> 16); - dest[7] = (unsigned char) (mask >> 24); -} - -// Alpha block compression (this is easy for a change) -static void stb__CompressAlphaBlock(unsigned char *dest,unsigned char *src,int mode) -{ - int i,dist,bias,dist4,dist2,bits,mask; - - // find min/max color - int mn,mx; - mn = mx = src[3]; - - for (i=1;i<16;i++) - { - if (src[i*4+3] < mn) mn = src[i*4+3]; - else if (src[i*4+3] > mx) mx = src[i*4+3]; - } - - // encode them - ((unsigned char *)dest)[0] = mx; - ((unsigned char *)dest)[1] = mn; - dest += 2; - - // determine bias and emit color indices - // given the choice of mx/mn, these indices are optimal: - // http://fgiesen.wordpress.com/2009/12/15/dxt5-alpha-block-index-determination/ - dist = mx-mn; - dist4 = dist*4; - dist2 = dist*2; - bias = (dist < 8) ? (dist - 1) : (dist/2 + 2); - bias -= mn * 7; - bits = 0,mask=0; - - for (i=0;i<16;i++) { - int a = src[i*4+3]*7 + bias; - int ind,t; - - // select index. this is a "linear scale" lerp factor between 0 (val=min) and 7 (val=max). - t = (a >= dist4) ? -1 : 0; ind = t & 4; a -= dist4 & t; - t = (a >= dist2) ? -1 : 0; ind += t & 2; a -= dist2 & t; - ind += (a >= dist); - - // turn linear scale into DXT index (0/1 are extremal pts) - ind = -ind & 7; - ind ^= (2 > ind); - - // write index - mask |= ind << bits; - if((bits += 3) >= 8) { - *dest++ = mask; - mask >>= 8; - bits -= 8; - } - } -} - -static void stb__InitDXT() -{ - int i; - for(i=0;i<32;i++) - stb__Expand5[i] = (i<<3)|(i>>2); - - for(i=0;i<64;i++) - stb__Expand6[i] = (i<<2)|(i>>4); - - for(i=0;i<256+16;i++) - { - int v = i-8 < 0 ? 0 : i-8 > 255 ? 255 : i-8; - stb__QuantRBTab[i] = stb__Expand5[stb__Mul8Bit(v,31)]; - stb__QuantGTab[i] = stb__Expand6[stb__Mul8Bit(v,63)]; - } - - stb__PrepareOptTable(&stb__OMatch5[0][0],stb__Expand5,32); - stb__PrepareOptTable(&stb__OMatch6[0][0],stb__Expand6,64); -} - -void stb_compress_dxt_block(unsigned char *dest, const unsigned char *src, int alpha, int mode) -{ - static int init=1; - if (init) { - stb__InitDXT(); - init=0; - } - - if (alpha) { - stb__CompressAlphaBlock(dest,(unsigned char*) src,mode); - dest += 8; - } - - stb__CompressColorBlock(dest,(unsigned char*) src,mode); -} -#endif // STB_DXT_IMPLEMENTATION - -#endif // STB_INCLUDE_STB_DXT_H diff --git a/impeller/third_party/stb/stb/stb_easy_font.h b/impeller/third_party/stb/stb/stb_easy_font.h deleted file mode 100644 index 45bddc99478fc..0000000000000 --- a/impeller/third_party/stb/stb/stb_easy_font.h +++ /dev/null @@ -1,258 +0,0 @@ -// stb_easy_font.h - v0.7 - bitmap font for 3D rendering - public domain -// Sean Barrett, Feb 2015 -// -// Easy-to-deploy, -// reasonably compact, -// extremely inefficient performance-wise, -// crappy-looking, -// ASCII-only, -// bitmap font for use in 3D APIs. -// -// Intended for when you just want to get some text displaying -// in a 3D app as quickly as possible. -// -// Doesn't use any textures, instead builds characters out of quads. -// -// DOCUMENTATION: -// -// int stb_easy_font_width(char *text) -// int stb_easy_font_height(char *text) -// -// Takes a string and returns the horizontal size and the -// vertical size (which can vary if 'text' has newlines). -// -// int stb_easy_font_print(float x, float y, -// char *text, unsigned char color[4], -// void *vertex_buffer, int vbuf_size) -// -// Takes a string (which can contain '\n') and fills out a -// vertex buffer with renderable data to draw the string. -// Output data assumes increasing x is rightwards, increasing y -// is downwards. -// -// The vertex data is divided into quads, i.e. there are four -// vertices in the vertex buffer for each quad. -// -// The vertices are stored in an interleaved format: -// -// x:float -// y:float -// z:float -// color:uint8[4] -// -// You can ignore z and color if you get them from elsewhere -// This format was chosen in the hopes it would make it -// easier for you to reuse existing vertex-buffer-drawing code. -// -// If you pass in NULL for color, it becomes 255,255,255,255. -// -// Returns the number of quads. -// -// If the buffer isn't large enough, it will truncate. -// Expect it to use an average of ~270 bytes per character. -// -// If your API doesn't draw quads, build a reusable index -// list that allows you to render quads as indexed triangles. -// -// void stb_easy_font_spacing(float spacing) -// -// Use positive values to expand the space between characters, -// and small negative values (no smaller than -1.5) to contract -// the space between characters. -// -// E.g. spacing = 1 adds one "pixel" of spacing between the -// characters. spacing = -1 is reasonable but feels a bit too -// compact to me; -0.5 is a reasonable compromise as long as -// you're scaling the font up. -// -// LICENSE -// -// This software is dual-licensed to the public domain and under the following -// license: you are granted a perpetual, irrevocable license to copy, modify, -// publish, and distribute this file as you see fit. -// -// VERSION HISTORY -// -// (2016-01-22) 0.7 width() supports multiline text; add height() -// (2015-09-13) 0.6 #include ; updated license -// (2015-02-01) 0.5 First release - -#if 0 -// SAMPLE CODE: -// -// Here's sample code for old OpenGL; it's a lot more complicated -// to make work on modern APIs, and that's your problem. -// -void print_string(float x, float y, char *text, float r, float g, float b) -{ - static char buffer[99999]; // ~500 chars - int num_quads; - - num_quads = stb_easy_font_print(x, y, text, NULL, buffer, sizeof(buffer)); - - glColor3f(r,g,b); - glEnableClientState(GL_VERTEX_ARRAY); - glVertexPointer(2, GL_FLOAT, 16, buffer); - glDrawArrays(GL_QUADS, 0, num_quads*4); - glDisableClientState(GL_VERTEX_ARRAY); -} -#endif - -#ifndef INCLUDE_STB_EASY_FONT_H -#define INCLUDE_STB_EASY_FONT_H - -#include -#include - -struct { - unsigned char advance; - unsigned char h_seg; - unsigned char v_seg; -} stb_easy_font_charinfo[96] = { - { 5, 0, 0 }, { 3, 0, 0 }, { 5, 1, 1 }, { 7, 1, 4 }, - { 7, 3, 7 }, { 7, 6, 12 }, { 7, 8, 19 }, { 4, 16, 21 }, - { 4, 17, 22 }, { 4, 19, 23 }, { 23, 21, 24 }, { 23, 22, 31 }, - { 20, 23, 34 }, { 22, 23, 36 }, { 19, 24, 36 }, { 21, 25, 36 }, - { 6, 25, 39 }, { 6, 27, 43 }, { 6, 28, 45 }, { 6, 30, 49 }, - { 6, 33, 53 }, { 6, 34, 57 }, { 6, 40, 58 }, { 6, 46, 59 }, - { 6, 47, 62 }, { 6, 55, 64 }, { 19, 57, 68 }, { 20, 59, 68 }, - { 21, 61, 69 }, { 22, 66, 69 }, { 21, 68, 69 }, { 7, 73, 69 }, - { 9, 75, 74 }, { 6, 78, 81 }, { 6, 80, 85 }, { 6, 83, 90 }, - { 6, 85, 91 }, { 6, 87, 95 }, { 6, 90, 96 }, { 7, 92, 97 }, - { 6, 96,102 }, { 5, 97,106 }, { 6, 99,107 }, { 6,100,110 }, - { 6,100,115 }, { 7,101,116 }, { 6,101,121 }, { 6,101,125 }, - { 6,102,129 }, { 7,103,133 }, { 6,104,140 }, { 6,105,145 }, - { 7,107,149 }, { 6,108,151 }, { 7,109,155 }, { 7,109,160 }, - { 7,109,165 }, { 7,118,167 }, { 6,118,172 }, { 4,120,176 }, - { 6,122,177 }, { 4,122,181 }, { 23,124,182 }, { 22,129,182 }, - { 4,130,182 }, { 22,131,183 }, { 6,133,187 }, { 22,135,191 }, - { 6,137,192 }, { 22,139,196 }, { 5,144,197 }, { 22,147,198 }, - { 6,150,202 }, { 19,151,206 }, { 21,152,207 }, { 6,155,209 }, - { 3,160,210 }, { 23,160,211 }, { 22,164,216 }, { 22,165,220 }, - { 22,167,224 }, { 22,169,228 }, { 21,171,232 }, { 21,173,233 }, - { 5,178,233 }, { 22,179,234 }, { 23,180,238 }, { 23,180,243 }, - { 23,180,248 }, { 22,189,248 }, { 22,191,252 }, { 5,196,252 }, - { 3,203,252 }, { 5,203,253 }, { 22,210,253 }, { 0,214,253 }, -}; - -unsigned char stb_easy_font_hseg[214] = { - 97,37,69,84,28,51,2,18,10,49,98,41,65,25,81,105,33,9,97,1,97,37,37,36, - 81,10,98,107,3,100,3,99,58,51,4,99,58,8,73,81,10,50,98,8,73,81,4,10,50, - 98,8,25,33,65,81,10,50,17,65,97,25,33,25,49,9,65,20,68,1,65,25,49,41, - 11,105,13,101,76,10,50,10,50,98,11,99,10,98,11,50,99,11,50,11,99,8,57, - 58,3,99,99,107,10,10,11,10,99,11,5,100,41,65,57,41,65,9,17,81,97,3,107, - 9,97,1,97,33,25,9,25,41,100,41,26,82,42,98,27,83,42,98,26,51,82,8,41, - 35,8,10,26,82,114,42,1,114,8,9,73,57,81,41,97,18,8,8,25,26,26,82,26,82, - 26,82,41,25,33,82,26,49,73,35,90,17,81,41,65,57,41,65,25,81,90,114,20, - 84,73,57,41,49,25,33,65,81,9,97,1,97,25,33,65,81,57,33,25,41,25, -}; - -unsigned char stb_easy_font_vseg[253] = { - 4,2,8,10,15,8,15,33,8,15,8,73,82,73,57,41,82,10,82,18,66,10,21,29,1,65, - 27,8,27,9,65,8,10,50,97,74,66,42,10,21,57,41,29,25,14,81,73,57,26,8,8, - 26,66,3,8,8,15,19,21,90,58,26,18,66,18,105,89,28,74,17,8,73,57,26,21, - 8,42,41,42,8,28,22,8,8,30,7,8,8,26,66,21,7,8,8,29,7,7,21,8,8,8,59,7,8, - 8,15,29,8,8,14,7,57,43,10,82,7,7,25,42,25,15,7,25,41,15,21,105,105,29, - 7,57,57,26,21,105,73,97,89,28,97,7,57,58,26,82,18,57,57,74,8,30,6,8,8, - 14,3,58,90,58,11,7,74,43,74,15,2,82,2,42,75,42,10,67,57,41,10,7,2,42, - 74,106,15,2,35,8,8,29,7,8,8,59,35,51,8,8,15,35,30,35,8,8,30,7,8,8,60, - 36,8,45,7,7,36,8,43,8,44,21,8,8,44,35,8,8,43,23,8,8,43,35,8,8,31,21,15, - 20,8,8,28,18,58,89,58,26,21,89,73,89,29,20,8,8,30,7, -}; - -typedef struct -{ - unsigned char c[4]; -} stb_easy_font_color; - -static int stb_easy_font_draw_segs(float x, float y, unsigned char *segs, int num_segs, int vertical, stb_easy_font_color c, char *vbuf, int vbuf_size, int offset) -{ - int i,j; - for (i=0; i < num_segs; ++i) { - int len = segs[i] & 7; - x += (float) ((segs[i] >> 3) & 1); - if (len && offset+64 <= vbuf_size) { - float y0 = y + (float) (segs[i]>>4); - for (j=0; j < 4; ++j) { - * (float *) (vbuf+offset+0) = x + (j==1 || j==2 ? (vertical ? 1 : len) : 0); - * (float *) (vbuf+offset+4) = y0 + ( j >= 2 ? (vertical ? len : 1) : 0); - * (float *) (vbuf+offset+8) = 0.f; - * (stb_easy_font_color *) (vbuf+offset+12) = c; - offset += 16; - } - } - } - return offset; -} - -float stb_easy_font_spacing_val = 0; -static void stb_easy_font_spacing(float spacing) -{ - stb_easy_font_spacing_val = spacing; -} - -static int stb_easy_font_print(float x, float y, char *text, unsigned char color[4], void *vertex_buffer, int vbuf_size) -{ - char *vbuf = (char *) vertex_buffer; - float start_x = x; - int offset = 0; - - stb_easy_font_color c = { 255,255,255,255 }; // use structure copying to avoid needing depending on memcpy() - if (color) { c.c[0] = color[0]; c.c[1] = color[1]; c.c[2] = color[2]; c.c[3] = color[3]; } - - while (*text && offset < vbuf_size) { - if (*text == '\n') { - y += 12; - x = start_x; - } else { - unsigned char advance = stb_easy_font_charinfo[*text-32].advance; - float y_ch = advance & 16 ? y+1 : y; - int h_seg, v_seg, num_h, num_v; - h_seg = stb_easy_font_charinfo[*text-32 ].h_seg; - v_seg = stb_easy_font_charinfo[*text-32 ].v_seg; - num_h = stb_easy_font_charinfo[*text-32+1].h_seg - h_seg; - num_v = stb_easy_font_charinfo[*text-32+1].v_seg - v_seg; - offset = stb_easy_font_draw_segs(x, y_ch, &stb_easy_font_hseg[h_seg], num_h, 0, c, vbuf, vbuf_size, offset); - offset = stb_easy_font_draw_segs(x, y_ch, &stb_easy_font_vseg[v_seg], num_v, 1, c, vbuf, vbuf_size, offset); - x += advance & 15; - x += stb_easy_font_spacing_val; - } - ++text; - } - return (unsigned) offset/64; -} - -static int stb_easy_font_width(char *text) -{ - float len = 0; - float max_len = 0; - while (*text) { - if (*text == '\n') { - if (len > max_len) max_len = len; - len = 0; - } else { - len += stb_easy_font_charinfo[*text-32].advance & 15; - len += stb_easy_font_spacing_val; - } - ++text; - } - if (len > max_len) max_len = len; - return (int) ceil(max_len); -} - -static int stb_easy_font_height(char *text) -{ - float y = 0; - int nonempty_line=0; - while (*text) { - if (*text == '\n') { - y += 12; - nonempty_line = 0; - } else { - nonempty_line = 1; - } - ++text; - } - return (int) ceil(y + (nonempty_line ? 12 : 0)); -} -#endif diff --git a/impeller/third_party/stb/stb/stb_herringbone_wang_tile.h b/impeller/third_party/stb/stb/stb_herringbone_wang_tile.h deleted file mode 100644 index ba2cf6027ef6f..0000000000000 --- a/impeller/third_party/stb/stb/stb_herringbone_wang_tile.h +++ /dev/null @@ -1,1220 +0,0 @@ -/* stbhw - v0.6 - http://nothings.org/gamedev/herringbone - Herringbone Wang Tile Generator - Sean Barrett 2014 - public domain - -== LICENSE ============================== - -This software is dual-licensed to the public domain and under the following -license: you are granted a perpetual, irrevocable license to copy, modify, -publish, and distribute this file as you see fit. - -== WHAT IT IS =========================== - - This library is an SDK for Herringbone Wang Tile generation: - - http://nothings.org/gamedev/herringbone - - The core design is that you use this library offline to generate a - "template" of the tiles you'll create. You then edit those tiles, then - load the created tile image file back into this library and use it at - runtime to generate "maps". - - You cannot load arbitrary tile image files with this library; it is - only designed to load image files made from the template it created. - It stores a binary description of the tile sizes & constraints in a - few pixels, and uses those to recover the rules, rather than trying - to parse the tiles themselves. - - You *can* use this library to generate from arbitrary tile sets, but - only by loading the tile set and specifying the constraints explicitly - yourself. - -== COMPILING ============================ - - 1. #define STB_HERRINGBONE_WANG_TILE_IMPLEMENTATION before including this - header file in *one* source file to create the implementation - in that source file. - - 2. optionally #define STB_HBWANG_RAND() to be a random number - generator. if you don't define it, it will use rand(), - and you need to seed srand() yourself. - - 3. optionally #define STB_HBWANG_ASSERT(x), otherwise - it will use assert() - - 4. optionally #define STB_HBWANG_STATIC to force all symbols to be - static instead of public, so they are only accesible - in the source file that creates the implementation - - 5. optionally #define STB_HBWANG_NO_REPITITION_REDUCTION to disable - the code that tries to reduce having the same tile appear - adjacent to itself in wang-corner-tile mode (e.g. imagine - if you were doing something where 90% of things should be - the same grass tile, you need to disable this system) - - 6. optionally define STB_HBWANG_MAX_X and STB_HBWANG_MAX_Y - to be the max dimensions of the generated map in multiples - of the wang tile's short side's length (e.g. if you - have 20x10 wang tiles, so short_side_len=10, and you - have MAX_X is 17, then the largest map you can generate - is 170 pixels wide). The defaults are 100x100. This - is used to define static arrays which affect memory - usage. - -== USING ================================ - - To use the map generator, you need a tileset. You can download - some sample tilesets from http://nothings.org/gamedev/herringbone - - Then see the "sample application" below. - - You can also use this file to generate templates for - tilesets which you then hand-edit to create the data. - - -== MEMORY MANAGEMENT ==================== - - The tileset loader allocates memory with malloc(). The map - generator does no memory allocation, so e.g. you can load - tilesets at startup and never free them and never do any - further allocation. - - -== SAMPLE APPLICATION =================== - -#include -#include -#include - -#define STB_IMAGE_IMPLEMENTATION -#include "stb_image.h" // http://nothings.org/stb_image.c - -#define STB_IMAGE_WRITE_IMPLEMENTATION -#include "stb_image_write.h" // http://nothings.org/stb/stb_image_write.h - -#define STB_HBWANG_IMPLEMENTATION -#include "stb_hbwang.h" - -int main(int argc, char **argv) -{ - unsigned char *data; - int xs,ys, w,h; - stbhw_tileset ts; - - if (argc != 4) { - fprintf(stderr, "Usage: mapgen {tile-file} {xsize} {ysize}\n" - "generates file named 'test_map.png'\n"); - exit(1); - } - data = stbi_load(argv[1], &w, &h, NULL, 3); - xs = atoi(argv[2]); - ys = atoi(argv[3]); - if (data == NULL) { - fprintf(stderr, "Error opening or parsing '%s' as an image file\n", argv[1]); - exit(1); - } - if (xs < 1 || xs > 1000) { - fprintf(stderr, "xsize invalid or out of range\n"); - exit(1); - } - if (ys < 1 || ys > 1000) { - fprintf(stderr, "ysize invalid or out of range\n"); - exit(1); - } - - stbhw_build_tileset_from_image(&ts, data, w*3, w, h); - free(data); - - // allocate a buffer to create the final image to - data = malloc(3 * xs * ys); - - srand(time(NULL)); - stbhw_generate_image(&ts, NULL, data, xs*3, xs, ys); - - stbi_write_png("test_map.png", xs, ys, 3, data, xs*3); - - stbhw_free_tileset(&ts); - free(data); - - return 0; -} - -== VERSION HISTORY =================== - - 0.6 2014-08-17 - fix broken map-maker - 0.5 2014-07-07 - initial release - -*/ - -////////////////////////////////////////////////////////////////////////////// -// // -// HEADER FILE SECTION // -// // - -#ifndef INCLUDE_STB_HWANG_H -#define INCLUDE_STB_HWANG_H - -#ifdef STB_HBWANG_STATIC -#define STBHW_EXTERN static -#else -#ifdef __cplusplus -#define STBHW_EXTERN extern "C" -#else -#define STBHW_EXTERN extern -#endif -#endif - -typedef struct stbhw_tileset stbhw_tileset; - -// returns description of last error produced by any function (not thread-safe) -STBHW_EXTERN char *stbhw_get_last_error(void); - -// build a tileset from an image that conforms to a template created by this -// library. (you allocate storage for stbhw_tileset and function fills it out; -// memory for individual tiles are malloc()ed). -// returns non-zero on success, 0 on error -STBHW_EXTERN int stbhw_build_tileset_from_image(stbhw_tileset *ts, - unsigned char *pixels, int stride_in_bytes, int w, int h); - -// free a tileset built by stbhw_build_tileset_from_image -STBHW_EXTERN void stbhw_free_tileset(stbhw_tileset *ts); - -// generate a map that is w * h pixels (3-bytes each) -// returns non-zero on success, 0 on error -// not thread-safe (uses a global data structure to avoid memory management) -// weighting should be NULL, as non-NULL weighting is currently untested -STBHW_EXTERN int stbhw_generate_image(stbhw_tileset *ts, int **weighting, - unsigned char *pixels, int stride_in_bytes, int w, int h); - -////////////////////////////////////// -// -// TILESET DATA STRUCTURE -// -// if you use the image-to-tileset system from this file, you -// don't need to worry about these data structures. but if you -// want to build/load a tileset yourself, you'll need to fill -// these out. - -typedef struct -{ - // the edge or vertex constraints, according to diagram below - signed char a,b,c,d,e,f; - - // The herringbone wang tile data; it is a bitmap which is either - // w=2*short_sidelen,h=short_sidelen, or w=short_sidelen,h=2*short_sidelen. - // it is always RGB, stored row-major, with no padding between rows. - // (allocate stbhw_tile structure to be large enough for the pixel data) - unsigned char pixels[1]; -} stbhw_tile; - -struct stbhw_tileset -{ - int is_corner; - int num_color[6]; // number of colors for each of 6 edge types or 4 corner types - int short_side_len; - stbhw_tile **h_tiles; - stbhw_tile **v_tiles; - int num_h_tiles, max_h_tiles; - int num_v_tiles, max_v_tiles; -}; - -/////////////// TEMPLATE GENERATOR ////////////////////////// - -// when requesting a template, you fill out this data -typedef struct -{ - int is_corner; // using corner colors or edge colors? - int short_side_len; // rectangles is 2n x n, n = short_side_len - int num_color[6]; // see below diagram for meaning of the index to this; - // 6 values if edge (!is_corner), 4 values if is_corner - // legal numbers: 1..8 if edge, 1..4 if is_corner - int num_vary_x; // additional number of variations along x axis in the template - int num_vary_y; // additional number of variations along y axis in the template - int corner_type_color_template[4][4]; - // if corner_type_color_template[s][t] is non-zero, then any - // corner of type s generated as color t will get a little - // corner sample markup in the template image data - -} stbhw_config; - -// computes the size needed for the template image -STBHW_EXTERN void stbhw_get_template_size(stbhw_config *c, int *w, int *h); - -// generates a template image, assuming data is 3*w*h bytes long, RGB format -STBHW_EXTERN int stbhw_make_template(stbhw_config *c, unsigned char *data, int w, int h, int stride_in_bytes); - -#endif//INCLUDE_STB_HWANG_H - - -// TILE CONSTRAINT TYPES -// -// there are 4 "types" of corners and 6 types of edges. -// you can configure the tileset to have different numbers -// of colors for each type of color or edge. -// -// corner types: -// -// 0---*---1---*---2---*---3 -// | | | -// * * * -// | | | -// 1---*---2---*---3 0---*---1---*---2 -// | | | -// * * * -// | | | -// 0---*---1---*---2---*---3 -// -// -// edge types: -// -// *---2---*---3---* *---0---* -// | | | | -// 1 4 5 1 -// | | | | -// *---0---*---2---* * * -// | | -// 4 5 -// | | -// *---3---* -// -// TILE CONSTRAINTS -// -// each corner/edge has a color; this shows the name -// of the variable containing the color -// -// corner constraints: -// -// a---*---d -// | | -// * * -// | | -// a---*---b---*---c b e -// | | | | -// * * * * -// | | | | -// d---*---e---*---f c---*---f -// -// -// edge constraints: -// -// *---a---*---b---* *---a---* -// | | | | -// c d b c -// | | | | -// *---e---*---f---* * * -// | | -// d e -// | | -// *---f---* -// - - -////////////////////////////////////////////////////////////////////////////// -// // -// IMPLEMENTATION SECTION // -// // - -#ifdef STB_HERRINGBONE_WANG_TILE_IMPLEMENTATION - - -#include // memcpy -#include // malloc - -#ifndef STB_HBWANG_RAND -#include -#define STB_HBWANG_RAND() (rand() >> 4) -#endif - -#ifndef STB_HBWANG_ASSERT -#include -#define STB_HBWANG_ASSERT(x) assert(x) -#endif - -// map size -#ifndef STB_HBWANG_MAX_X -#define STB_HBWANG_MAX_X 100 -#endif - -#ifndef STB_HBWANG_MAX_Y -#define STB_HBWANG_MAX_Y 100 -#endif - -// global variables for color assignments -// @MEMORY change these to just store last two/three rows -// and keep them on the stack -static signed char c_color[STB_HBWANG_MAX_Y+6][STB_HBWANG_MAX_X+6]; -static signed char v_color[STB_HBWANG_MAX_Y+6][STB_HBWANG_MAX_X+5]; -static signed char h_color[STB_HBWANG_MAX_Y+5][STB_HBWANG_MAX_X+6]; - -static char *stbhw_error; -STBHW_EXTERN char *stbhw_get_last_error(void) -{ - char *temp = stbhw_error; - stbhw_error = 0; - return temp; -} - - - - -///////////////////////////////////////////////////////////// -// -// SHARED TEMPLATE-DESCRIPTION CODE -// -// Used by both template generator and tileset parser; by -// using the same code, they are locked in sync and we don't -// need to try to do more sophisticated parsing of edge color -// markup or something. - -typedef void stbhw__process_rect(struct stbhw__process *p, int xpos, int ypos, - int a, int b, int c, int d, int e, int f); - -typedef struct stbhw__process -{ - stbhw_tileset *ts; - stbhw_config *c; - stbhw__process_rect *process_h_rect; - stbhw__process_rect *process_v_rect; - unsigned char *data; - int stride,w,h; -} stbhw__process; - -static void stbhw__process_h_row(stbhw__process *p, - int xpos, int ypos, - int a0, int a1, - int b0, int b1, - int c0, int c1, - int d0, int d1, - int e0, int e1, - int f0, int f1, - int variants) -{ - int a,b,c,d,e,f,v; - - for (v=0; v < variants; ++v) - for (f=f0; f <= f1; ++f) - for (e=e0; e <= e1; ++e) - for (d=d0; d <= d1; ++d) - for (c=c0; c <= c1; ++c) - for (b=b0; b <= b1; ++b) - for (a=a0; a <= a1; ++a) { - p->process_h_rect(p, xpos, ypos, a,b,c,d,e,f); - xpos += 2*p->c->short_side_len + 3; - } -} - -static void stbhw__process_v_row(stbhw__process *p, - int xpos, int ypos, - int a0, int a1, - int b0, int b1, - int c0, int c1, - int d0, int d1, - int e0, int e1, - int f0, int f1, - int variants) -{ - int a,b,c,d,e,f,v; - - for (v=0; v < variants; ++v) - for (f=f0; f <= f1; ++f) - for (e=e0; e <= e1; ++e) - for (d=d0; d <= d1; ++d) - for (c=c0; c <= c1; ++c) - for (b=b0; b <= b1; ++b) - for (a=a0; a <= a1; ++a) { - p->process_v_rect(p, xpos, ypos, a,b,c,d,e,f); - xpos += p->c->short_side_len+3; - } -} - -static void stbhw__get_template_info(stbhw_config *c, int *w, int *h, int *h_count, int *v_count) -{ - int size_x,size_y; - int horz_count,vert_count; - - if (c->is_corner) { - int horz_w = c->num_color[1] * c->num_color[2] * c->num_color[3] * c->num_vary_x; - int horz_h = c->num_color[0] * c->num_color[1] * c->num_color[2] * c->num_vary_y; - - int vert_w = c->num_color[0] * c->num_color[3] * c->num_color[2] * c->num_vary_y; - int vert_h = c->num_color[1] * c->num_color[0] * c->num_color[3] * c->num_vary_x; - - int horz_x = horz_w * (2*c->short_side_len + 3); - int horz_y = horz_h * ( c->short_side_len + 3); - - int vert_x = vert_w * ( c->short_side_len + 3); - int vert_y = vert_h * (2*c->short_side_len + 3); - - horz_count = horz_w * horz_h; - vert_count = vert_w * vert_h; - - size_x = horz_x > vert_x ? horz_x : vert_x; - size_y = 2 + horz_y + 2 + vert_y; - } else { - int horz_w = c->num_color[0] * c->num_color[1] * c->num_color[2] * c->num_vary_x; - int horz_h = c->num_color[3] * c->num_color[4] * c->num_color[2] * c->num_vary_y; - - int vert_w = c->num_color[0] * c->num_color[5] * c->num_color[1] * c->num_vary_y; - int vert_h = c->num_color[3] * c->num_color[4] * c->num_color[5] * c->num_vary_x; - - int horz_x = horz_w * (2*c->short_side_len + 3); - int horz_y = horz_h * ( c->short_side_len + 3); - - int vert_x = vert_w * ( c->short_side_len + 3); - int vert_y = vert_h * (2*c->short_side_len + 3); - - horz_count = horz_w * horz_h; - vert_count = vert_w * vert_h; - - size_x = horz_x > vert_x ? horz_x : vert_x; - size_y = 2 + horz_y + 2 + vert_y; - } - if (w) *w = size_x; - if (h) *h = size_y; - if (h_count) *h_count = horz_count; - if (v_count) *v_count = vert_count; -} - -STBHW_EXTERN void stbhw_get_template_size(stbhw_config *c, int *w, int *h) -{ - stbhw__get_template_info(c, w, h, NULL, NULL); -} - -static int stbhw__process_template(stbhw__process *p) -{ - int i,j,k,q, ypos; - int size_x, size_y; - stbhw_config *c = p->c; - - stbhw__get_template_info(c, &size_x, &size_y, NULL, NULL); - - if (p->w < size_x || p->h < size_y) { - stbhw_error = "image too small for configuration"; - return 0; - } - - if (c->is_corner) { - ypos = 2; - for (k=0; k < c->num_color[2]; ++k) { - for (j=0; j < c->num_color[1]; ++j) { - for (i=0; i < c->num_color[0]; ++i) { - for (q=0; q < c->num_vary_y; ++q) { - stbhw__process_h_row(p, 0,ypos, - 0,c->num_color[1]-1, 0,c->num_color[2]-1, 0,c->num_color[3]-1, - i,i, j,j, k,k, - c->num_vary_x); - ypos += c->short_side_len + 3; - } - } - } - } - ypos += 2; - for (k=0; k < c->num_color[3]; ++k) { - for (j=0; j < c->num_color[0]; ++j) { - for (i=0; i < c->num_color[1]; ++i) { - for (q=0; q < c->num_vary_x; ++q) { - stbhw__process_v_row(p, 0,ypos, - 0,c->num_color[0]-1, 0,c->num_color[3]-1, 0,c->num_color[2]-1, - i,i, j,j, k,k, - c->num_vary_y); - ypos += (c->short_side_len*2) + 3; - } - } - } - } - assert(ypos == size_y); - } else { - ypos = 2; - for (k=0; k < c->num_color[3]; ++k) { - for (j=0; j < c->num_color[4]; ++j) { - for (i=0; i < c->num_color[2]; ++i) { - for (q=0; q < c->num_vary_y; ++q) { - stbhw__process_h_row(p, 0,ypos, - 0,c->num_color[2]-1, k,k, - 0,c->num_color[1]-1, j,j, - 0,c->num_color[0]-1, i,i, - c->num_vary_x); - ypos += c->short_side_len + 3; - } - } - } - } - ypos += 2; - for (k=0; k < c->num_color[3]; ++k) { - for (j=0; j < c->num_color[4]; ++j) { - for (i=0; i < c->num_color[5]; ++i) { - for (q=0; q < c->num_vary_x; ++q) { - stbhw__process_v_row(p, 0,ypos, - 0,c->num_color[0]-1, i,i, - 0,c->num_color[1]-1, j,j, - 0,c->num_color[5]-1, k,k, - c->num_vary_y); - ypos += (c->short_side_len*2) + 3; - } - } - } - } - assert(ypos == size_y); - } - return 1; -} - - -///////////////////////////////////////////////////////////// -// -// MAP GENERATOR -// - -static void stbhw__draw_pixel(unsigned char *output, int stride, int x, int y, unsigned char c[3]) -{ - memcpy(output + y*stride + x*3, c, 3); -} - -static void stbhw__draw_h_tile(unsigned char *output, int stride, int xmax, int ymax, int x, int y, stbhw_tile *h, int sz) -{ - int i,j; - for (j=0; j < sz; ++j) - if (y+j >= 0 && y+j < ymax) - for (i=0; i < sz*2; ++i) - if (x+i >= 0 && x+i < xmax) - stbhw__draw_pixel(output,stride, x+i,y+j, &h->pixels[(j*sz*2 + i)*3]); -} - -static void stbhw__draw_v_tile(unsigned char *output, int stride, int xmax, int ymax, int x, int y, stbhw_tile *h, int sz) -{ - int i,j; - for (j=0; j < sz*2; ++j) - if (y+j >= 0 && y+j < ymax) - for (i=0; i < sz; ++i) - if (x+i >= 0 && x+i < xmax) - stbhw__draw_pixel(output,stride, x+i,y+j, &h->pixels[(j*sz + i)*3]); -} - - -// randomly choose a tile that fits constraints for a given spot, and update the constraints -static stbhw_tile * stbhw__choose_tile(stbhw_tile **list, int numlist, - signed char *a, signed char *b, signed char *c, - signed char *d, signed char *e, signed char *f, - int **weighting) -{ - int i,n,m = 1<<30,pass; - for (pass=0; pass < 2; ++pass) { - n=0; - // pass #1: - // count number of variants that match this partial set of constraints - // pass #2: - // stop on randomly selected match - for (i=0; i < numlist; ++i) { - stbhw_tile *h = list[i]; - if ((*a < 0 || *a == h->a) && - (*b < 0 || *b == h->b) && - (*c < 0 || *c == h->c) && - (*d < 0 || *d == h->d) && - (*e < 0 || *e == h->e) && - (*f < 0 || *f == h->f)) { - if (weighting) - n += weighting[0][i]; - else - n += 1; - if (n > m) { - // use list[i] - // update constraints to reflect what we placed - *a = h->a; - *b = h->b; - *c = h->c; - *d = h->d; - *e = h->e; - *f = h->f; - return h; - } - } - } - if (n == 0) { - stbhw_error = "couldn't find tile matching constraints"; - return NULL; - } - m = STB_HBWANG_RAND() % n; - } - STB_HBWANG_ASSERT(0); - return NULL; -} - -static int stbhw__match(int x, int y) -{ - return c_color[y][x] == c_color[y+1][x+1]; -} - -static int stbhw__weighted(int num_options, int *weights) -{ - int k, total, choice; - total = 0; - for (k=0; k < num_options; ++k) - total += weights[k]; - choice = STB_HBWANG_RAND() % total; - total = 0; - for (k=0; k < num_options; ++k) { - total += weights[k]; - if (choice < total) - break; - } - STB_HBWANG_ASSERT(k < num_options); - return k; -} - -static int stbhw__change_color(int old_color, int num_options, int *weights) -{ - if (weights) { - int k, total, choice; - total = 0; - for (k=0; k < num_options; ++k) - if (k != old_color) - total += weights[k]; - choice = STB_HBWANG_RAND() % total; - total = 0; - for (k=0; k < num_options; ++k) { - if (k != old_color) { - total += weights[k]; - if (choice < total) - break; - } - } - STB_HBWANG_ASSERT(k < num_options); - return k; - } else { - int offset = 1+STB_HBWANG_RAND() % (num_options-1); - return (old_color+offset) % num_options; - } -} - - - -// generate a map that is w * h pixels (3-bytes each) -// returns 1 on success, 0 on error -STBHW_EXTERN int stbhw_generate_image(stbhw_tileset *ts, int **weighting, unsigned char *output, int stride, int w, int h) -{ - int sidelen = ts->short_side_len; - int xmax = (w / sidelen) + 6; - int ymax = (h / sidelen) + 6; - if (xmax > STB_HBWANG_MAX_X+6 || ymax > STB_HBWANG_MAX_Y+6) { - stbhw_error = "increase STB_HBWANG_MAX_X/Y"; - return 0; - } - - if (ts->is_corner) { - int i,j, ypos; - int *cc = ts->num_color; - - for (j=0; j < ymax; ++j) { - for (i=0; i < xmax; ++i) { - int p = (i-j+1)&3; // corner type - if (weighting==NULL || weighting[p]==0 || cc[p] == 1) - c_color[j][i] = STB_HBWANG_RAND() % cc[p]; - else - c_color[j][i] = stbhw__weighted(cc[p], weighting[p]); - } - } - #ifndef STB_HBWANG_NO_REPITITION_REDUCTION - // now go back through and make sure we don't have adjancent 3x2 vertices that are identical, - // to avoid really obvious repetition (which happens easily with extreme weights) - for (j=0; j < ymax-3; ++j) { - for (i=0; i < xmax-3; ++i) { - int p = (i-j+1) & 3; // corner type - STB_HBWANG_ASSERT(i+3 < STB_HBWANG_MAX_X+6); - STB_HBWANG_ASSERT(j+3 < STB_HBWANG_MAX_Y+6); - if (stbhw__match(i,j) && stbhw__match(i,j+1) && stbhw__match(i,j+2) - && stbhw__match(i+1,j) && stbhw__match(i+1,j+1) && stbhw__match(i+1,j+2)) { - int p = ((i+1)-(j+1)+1) & 3; - if (cc[p] > 1) - c_color[j+1][i+1] = stbhw__change_color(c_color[j+1][i+1], cc[p], weighting ? weighting[p] : NULL); - } - if (stbhw__match(i,j) && stbhw__match(i+1,j) && stbhw__match(i+2,j) - && stbhw__match(i,j+1) && stbhw__match(i+1,j+1) && stbhw__match(i+2,j+1)) { - int p = ((i+2)-(j+1)+1) & 3; - if (cc[p] > 1) - c_color[j+1][i+2] = stbhw__change_color(c_color[j+1][i+2], cc[p], weighting ? weighting[p] : NULL); - } - } - } - #endif - - ypos = -1 * sidelen; - for (j = -1; ypos < h; ++j) { - // a general herringbone row consists of: - // horizontal left block, the bottom of a previous vertical, the top of a new vertical - int phase = (j & 3); - // displace horizontally according to pattern - if (phase == 0) { - i = 0; - } else { - i = phase-4; - } - for (i;; i += 4) { - int xpos = i * sidelen; - if (xpos >= w) - break; - // horizontal left-block - if (xpos + sidelen*2 >= 0 && ypos >= 0) { - stbhw_tile *t = stbhw__choose_tile( - ts->h_tiles, ts->num_h_tiles, - &c_color[j+2][i+2], &c_color[j+2][i+3], &c_color[j+2][i+4], - &c_color[j+3][i+2], &c_color[j+3][i+3], &c_color[j+3][i+4], - weighting - ); - if (t == NULL) - return 0; - stbhw__draw_h_tile(output,stride,w,h, xpos, ypos, t, sidelen); - } - xpos += sidelen * 2; - // now we're at the end of a previous vertical one - xpos += sidelen; - // now we're at the start of a new vertical one - if (xpos < w) { - stbhw_tile *t = stbhw__choose_tile( - ts->v_tiles, ts->num_v_tiles, - &c_color[j+2][i+5], &c_color[j+3][i+5], &c_color[j+4][i+5], - &c_color[j+2][i+6], &c_color[j+3][i+6], &c_color[j+4][i+6], - weighting - ); - if (t == NULL) - return 0; - stbhw__draw_v_tile(output,stride,w,h, xpos, ypos, t, sidelen); - } - } - ypos += sidelen; - } - } else { - // @TODO edge-color repetition reduction - int i,j, ypos; - memset(v_color, -1, sizeof(v_color)); - memset(h_color, -1, sizeof(h_color)); - - ypos = -1 * sidelen; - for (j = -1; ypos= w) - break; - // horizontal left-block - if (xpos + sidelen*2 >= 0 && ypos >= 0) { - stbhw_tile *t = stbhw__choose_tile( - ts->h_tiles, ts->num_h_tiles, - &h_color[j+2][i+2], &h_color[j+2][i+3], - &v_color[j+2][i+2], &v_color[j+2][i+4], - &h_color[j+3][i+2], &h_color[j+3][i+3], - weighting - ); - if (t == NULL) return 0; - stbhw__draw_h_tile(output,stride,w,h, xpos, ypos, t, sidelen); - } - xpos += sidelen * 2; - // now we're at the end of a previous vertical one - xpos += sidelen; - // now we're at the start of a new vertical one - if (xpos < w) { - stbhw_tile *t = stbhw__choose_tile( - ts->v_tiles, ts->num_v_tiles, - &h_color[j+2][i+5], - &v_color[j+2][i+5], &v_color[j+2][i+6], - &v_color[j+3][i+5], &v_color[j+3][i+6], - &h_color[j+4][i+5], - weighting - ); - if (t == NULL) return 0; - stbhw__draw_v_tile(output,stride,w,h, xpos, ypos, t, sidelen); - } - } - ypos += sidelen; - } - } - return 1; -} - -static void stbhw__parse_h_rect(stbhw__process *p, int xpos, int ypos, - int a, int b, int c, int d, int e, int f) -{ - int len = p->c->short_side_len; - stbhw_tile *h = (stbhw_tile *) malloc(sizeof(*h)-1 + 3 * (len*2) * len); - int i,j; - ++xpos; - ++ypos; - h->a = a, h->b = b, h->c = c, h->d = d, h->e = e, h->f = f; - for (j=0; j < len; ++j) - for (i=0; i < len*2; ++i) - memcpy(h->pixels + j*(3*len*2) + i*3, p->data+(ypos+j)*p->stride+(xpos+i)*3, 3); - STB_HBWANG_ASSERT(p->ts->num_h_tiles < p->ts->max_h_tiles); - p->ts->h_tiles[p->ts->num_h_tiles++] = h; -} - -static void stbhw__parse_v_rect(stbhw__process *p, int xpos, int ypos, - int a, int b, int c, int d, int e, int f) -{ - int len = p->c->short_side_len; - stbhw_tile *h = (stbhw_tile *) malloc(sizeof(*h)-1 + 3 * (len*2) * len); - int i,j; - ++xpos; - ++ypos; - h->a = a, h->b = b, h->c = c, h->d = d, h->e = e, h->f = f; - for (j=0; j < len*2; ++j) - for (i=0; i < len; ++i) - memcpy(h->pixels + j*(3*len) + i*3, p->data+(ypos+j)*p->stride+(xpos+i)*3, 3); - STB_HBWANG_ASSERT(p->ts->num_v_tiles < p->ts->max_v_tiles); - p->ts->v_tiles[p->ts->num_v_tiles++] = h; -} - -STBHW_EXTERN int stbhw_build_tileset_from_image(stbhw_tileset *ts, unsigned char *data, int stride, int w, int h) -{ - int i, h_count, v_count; - unsigned char header[9]; - stbhw_config c = { 0 }; - stbhw__process p = { 0 }; - - // extract binary header - - // remove encoding that makes it more visually obvious it encodes actual data - for (i=0; i < 9; ++i) - header[i] = data[w*3 - 1 - i] ^ (i*55); - - // extract header info - if (header[7] == 0xc0) { - // corner-type - c.is_corner = 1; - for (i=0; i < 4; ++i) - c.num_color[i] = header[i]; - c.num_vary_x = header[4]; - c.num_vary_y = header[5]; - c.short_side_len = header[6]; - } else { - c.is_corner = 0; - // edge-type - for (i=0; i < 6; ++i) - c.num_color[i] = header[i]; - c.num_vary_x = header[6]; - c.num_vary_y = header[7]; - c.short_side_len = header[8]; - } - - if (c.num_vary_x < 0 || c.num_vary_x > 64 || c.num_vary_y < 0 || c.num_vary_y > 64) - return 0; - if (c.short_side_len == 0) - return 0; - if (c.num_color[0] > 32 || c.num_color[1] > 32 || c.num_color[2] > 32 || c.num_color[3] > 32) - return 0; - - stbhw__get_template_info(&c, NULL, NULL, &h_count, &v_count); - - ts->is_corner = c.is_corner; - ts->short_side_len = c.short_side_len; - memcpy(ts->num_color, c.num_color, sizeof(ts->num_color)); - - ts->max_h_tiles = h_count; - ts->max_v_tiles = v_count; - - ts->num_h_tiles = ts->num_v_tiles = 0; - - ts->h_tiles = (stbhw_tile **) malloc(sizeof(*ts->h_tiles) * h_count); - ts->v_tiles = (stbhw_tile **) malloc(sizeof(*ts->v_tiles) * v_count); - - p.ts = ts; - p.data = data; - p.stride = stride; - p.process_h_rect = stbhw__parse_h_rect; - p.process_v_rect = stbhw__parse_v_rect; - p.w = w; - p.h = h; - p.c = &c; - - // load all the tiles out of the image - return stbhw__process_template(&p); -} - -STBHW_EXTERN void stbhw_free_tileset(stbhw_tileset *ts) -{ - int i; - for (i=0; i < ts->num_h_tiles; ++i) - free(ts->h_tiles[i]); - for (i=0; i < ts->num_v_tiles; ++i) - free(ts->v_tiles[i]); - free(ts->h_tiles); - free(ts->v_tiles); - ts->h_tiles = NULL; - ts->v_tiles = NULL; - ts->num_h_tiles = ts->max_h_tiles = 0; - ts->num_v_tiles = ts->max_v_tiles = 0; -} - -////////////////////////////////////////////////////////////////////////////// -// -// GENERATOR -// -// - - -// shared code - -static void stbhw__set_pixel(unsigned char *data, int stride, int xpos, int ypos, unsigned char color[3]) -{ - memcpy(data + ypos*stride + xpos*3, color, 3); -} - -static void stbhw__stbhw__set_pixel_whiten(unsigned char *data, int stride, int xpos, int ypos, unsigned char color[3]) -{ - unsigned char c2[3]; - int i; - for (i=0; i < 3; ++i) - c2[i] = (color[i]*2 + 255)/3; - memcpy(data + ypos*stride + xpos*3, c2, 3); -} - - -static unsigned char stbhw__black[3] = { 0,0,0 }; - -// each edge set gets its own unique color variants -// used http://phrogz.net/css/distinct-colors.html to generate this set, -// but it's not very good and needs to be revised - -static unsigned char stbhw__color[7][8][3] = -{ - { {255,51,51} , {143,143,29}, {0,199,199}, {159,119,199}, {0,149,199} , {143, 0,143}, {255,128,0}, {64,255,0}, }, - { {235,255,30 }, {255,0,255}, {199,139,119}, {29,143, 57}, {143,0,71} , { 0,143,143}, {0,99,199}, {143,71,0}, }, - { {0,149,199} , {143, 0,143}, {255,128,0}, {64,255,0}, {255,191,0} , {51,255,153}, {0,0,143}, {199,119,159},}, - { {143,0,71} , { 0,143,143}, {0,99,199}, {143,71,0}, {255,190,153}, { 0,255,255}, {128,0,255}, {255,51,102},}, - { {255,191,0} , {51,255,153}, {0,0,143}, {199,119,159}, {255,51,51} , {143,143,29}, {0,199,199}, {159,119,199},}, - { {255,190,153}, { 0,255,255}, {128,0,255}, {255,51,102}, {235,255,30 }, {255,0,255}, {199,139,119}, {29,143, 57}, }, - - { {40,40,40 }, { 90,90,90 }, { 150,150,150 }, { 200,200,200 }, - { 255,90,90 }, { 160,160,80}, { 50,150,150 }, { 200,50,200 } }, -}; - -static void stbhw__draw_hline(unsigned char *data, int stride, int xpos, int ypos, int color, int len, int slot) -{ - int i; - int j = len * 6 / 16; - int k = len * 10 / 16; - for (i=0; i < len; ++i) - stbhw__set_pixel(data, stride, xpos+i, ypos, stbhw__black); - if (k-j < 2) { - j = len/2 - 1; - k = j+2; - if (len & 1) - ++k; - } - for (i=j; i < k; ++i) - stbhw__stbhw__set_pixel_whiten(data, stride, xpos+i, ypos, stbhw__color[slot][color]); -} - -static void stbhw__draw_vline(unsigned char *data, int stride, int xpos, int ypos, int color, int len, int slot) -{ - int i; - int j = len * 6 / 16; - int k = len * 10 / 16; - for (i=0; i < len; ++i) - stbhw__set_pixel(data, stride, xpos, ypos+i, stbhw__black); - if (k-j < 2) { - j = len/2 - 1; - k = j+2; - if (len & 1) - ++k; - } - for (i=j; i < k; ++i) - stbhw__stbhw__set_pixel_whiten(data, stride, xpos, ypos+i, stbhw__color[slot][color]); -} - -// 0--*--1--*--2--*--3 -// | | | -// * * * -// | | | -// 1--*--2--*--3 0--*--1--*--2 -// | | | -// * * * -// | | | -// 0--*--1--*--2--*--3 -// -// variables while enumerating (no correspondence between corners -// of the types is implied by these variables) -// -// a-----b-----c a-----d -// | | | | -// | | | | -// | | | | -// d-----e-----f b e -// | | -// | | -// | | -// c-----f -// - -unsigned char stbhw__corner_colors[4][4][3] = -{ - { { 255,0,0 }, { 200,200,200 }, { 100,100,200 }, { 255,200,150 }, }, - { { 0,0,255 }, { 255,255,0 }, { 100,200,100 }, { 150,255,200 }, }, - { { 255,0,255 }, { 80,80,80 }, { 200,100,100 }, { 200,150,255 }, }, - { { 0,255,255 }, { 0,255,0 }, { 200,120,200 }, { 255,200,200 }, }, -}; - -int stbhw__corner_colors_to_edge_color[4][4] = -{ - // 0 1 2 3 - { 0, 1, 4, 9, }, // 0 - { 2, 3, 5, 10, }, // 1 - { 6, 7, 8, 11, }, // 2 - { 12, 13, 14, 15, }, // 3 -}; - -#define stbhw__c2e stbhw__corner_colors_to_edge_color - -static void stbhw__draw_clipped_corner(unsigned char *data, int stride, int xpos, int ypos, int w, int h, int x, int y) -{ - static unsigned char template_color[3] = { 167,204,204 }; - int i,j; - for (j = -2; j <= 1; ++j) { - for (i = -2; i <= 1; ++i) { - if ((i == -2 || i == 1) && (j == -2 || j == 1)) - continue; - else { - if (x+i < 1 || x+i > w) continue; - if (y+j < 1 || y+j > h) continue; - stbhw__set_pixel(data, stride, xpos+x+i, ypos+y+j, template_color); - - } - } - } -} - -static void stbhw__edge_process_h_rect(stbhw__process *p, int xpos, int ypos, - int a, int b, int c, int d, int e, int f) -{ - int len = p->c->short_side_len; - stbhw__draw_hline(p->data, p->stride, xpos+1 , ypos , a, len, 2); - stbhw__draw_hline(p->data, p->stride, xpos+ len+1 , ypos , b, len, 3); - stbhw__draw_vline(p->data, p->stride, xpos , ypos+1 , c, len, 1); - stbhw__draw_vline(p->data, p->stride, xpos+2*len+1 , ypos+1 , d, len, 4); - stbhw__draw_hline(p->data, p->stride, xpos+1 , ypos + len+1, e, len, 0); - stbhw__draw_hline(p->data, p->stride, xpos + len+1 , ypos + len+1, f, len, 2); -} - -static void stbhw__edge_process_v_rect(stbhw__process *p, int xpos, int ypos, - int a, int b, int c, int d, int e, int f) -{ - int len = p->c->short_side_len; - stbhw__draw_hline(p->data, p->stride, xpos+1 , ypos , a, len, 0); - stbhw__draw_vline(p->data, p->stride, xpos , ypos+1 , b, len, 5); - stbhw__draw_vline(p->data, p->stride, xpos + len+1, ypos+1 , c, len, 1); - stbhw__draw_vline(p->data, p->stride, xpos , ypos + len+1, d, len, 4); - stbhw__draw_vline(p->data, p->stride, xpos + len+1, ypos + len+1, e, len, 5); - stbhw__draw_hline(p->data, p->stride, xpos+1 , ypos + 2*len+1, f, len, 3); -} - -static void stbhw__corner_process_h_rect(stbhw__process *p, int xpos, int ypos, - int a, int b, int c, int d, int e, int f) -{ - int len = p->c->short_side_len; - - stbhw__draw_hline(p->data, p->stride, xpos+1 , ypos , stbhw__c2e[a][b], len, 2); - stbhw__draw_hline(p->data, p->stride, xpos+ len+1 , ypos , stbhw__c2e[b][c], len, 3); - stbhw__draw_vline(p->data, p->stride, xpos , ypos+1 , stbhw__c2e[a][d], len, 1); - stbhw__draw_vline(p->data, p->stride, xpos+2*len+1 , ypos+1 , stbhw__c2e[c][f], len, 4); - stbhw__draw_hline(p->data, p->stride, xpos+1 , ypos + len+1, stbhw__c2e[d][e], len, 0); - stbhw__draw_hline(p->data, p->stride, xpos + len+1 , ypos + len+1, stbhw__c2e[e][f], len, 2); - - if (p->c->corner_type_color_template[1][a]) stbhw__draw_clipped_corner(p->data,p->stride, xpos,ypos, len*2,len, 1,1); - if (p->c->corner_type_color_template[2][b]) stbhw__draw_clipped_corner(p->data,p->stride, xpos,ypos, len*2,len, len+1,1); - if (p->c->corner_type_color_template[3][c]) stbhw__draw_clipped_corner(p->data,p->stride, xpos,ypos, len*2,len, len*2+1,1); - - if (p->c->corner_type_color_template[0][d]) stbhw__draw_clipped_corner(p->data,p->stride, xpos,ypos, len*2,len, 1,len+1); - if (p->c->corner_type_color_template[1][e]) stbhw__draw_clipped_corner(p->data,p->stride, xpos,ypos, len*2,len, len+1,len+1); - if (p->c->corner_type_color_template[2][f]) stbhw__draw_clipped_corner(p->data,p->stride, xpos,ypos, len*2,len, len*2+1,len+1); - - stbhw__set_pixel(p->data, p->stride, xpos , ypos, stbhw__corner_colors[1][a]); - stbhw__set_pixel(p->data, p->stride, xpos+len , ypos, stbhw__corner_colors[2][b]); - stbhw__set_pixel(p->data, p->stride, xpos+2*len+1, ypos, stbhw__corner_colors[3][c]); - stbhw__set_pixel(p->data, p->stride, xpos , ypos+len+1, stbhw__corner_colors[0][d]); - stbhw__set_pixel(p->data, p->stride, xpos+len , ypos+len+1, stbhw__corner_colors[1][e]); - stbhw__set_pixel(p->data, p->stride, xpos+2*len+1, ypos+len+1, stbhw__corner_colors[2][f]); -} - -static void stbhw__corner_process_v_rect(stbhw__process *p, int xpos, int ypos, - int a, int b, int c, int d, int e, int f) -{ - int len = p->c->short_side_len; - - stbhw__draw_hline(p->data, p->stride, xpos+1 , ypos , stbhw__c2e[a][d], len, 0); - stbhw__draw_vline(p->data, p->stride, xpos , ypos+1 , stbhw__c2e[a][b], len, 5); - stbhw__draw_vline(p->data, p->stride, xpos + len+1, ypos+1 , stbhw__c2e[d][e], len, 1); - stbhw__draw_vline(p->data, p->stride, xpos , ypos + len+1, stbhw__c2e[b][c], len, 4); - stbhw__draw_vline(p->data, p->stride, xpos + len+1, ypos + len+1, stbhw__c2e[e][f], len, 5); - stbhw__draw_hline(p->data, p->stride, xpos+1 , ypos + 2*len+1, stbhw__c2e[c][f], len, 3); - - if (p->c->corner_type_color_template[0][a]) stbhw__draw_clipped_corner(p->data,p->stride, xpos,ypos, len,len*2, 1,1); - if (p->c->corner_type_color_template[3][b]) stbhw__draw_clipped_corner(p->data,p->stride, xpos,ypos, len,len*2, 1,len+1); - if (p->c->corner_type_color_template[2][c]) stbhw__draw_clipped_corner(p->data,p->stride, xpos,ypos, len,len*2, 1,len*2+1); - - if (p->c->corner_type_color_template[1][d]) stbhw__draw_clipped_corner(p->data,p->stride, xpos,ypos, len,len*2, len+1,1); - if (p->c->corner_type_color_template[0][e]) stbhw__draw_clipped_corner(p->data,p->stride, xpos,ypos, len,len*2, len+1,len+1); - if (p->c->corner_type_color_template[3][f]) stbhw__draw_clipped_corner(p->data,p->stride, xpos,ypos, len,len*2, len+1,len*2+1); - - stbhw__set_pixel(p->data, p->stride, xpos , ypos , stbhw__corner_colors[0][a]); - stbhw__set_pixel(p->data, p->stride, xpos , ypos+len , stbhw__corner_colors[3][b]); - stbhw__set_pixel(p->data, p->stride, xpos , ypos+2*len+1, stbhw__corner_colors[2][c]); - stbhw__set_pixel(p->data, p->stride, xpos+len+1, ypos , stbhw__corner_colors[1][d]); - stbhw__set_pixel(p->data, p->stride, xpos+len+1, ypos+len , stbhw__corner_colors[0][e]); - stbhw__set_pixel(p->data, p->stride, xpos+len+1, ypos+2*len+1, stbhw__corner_colors[3][f]); -} - -// generates a template image, assuming data is 3*w*h bytes long, RGB format -STBHW_EXTERN int stbhw_make_template(stbhw_config *c, unsigned char *data, int w, int h, int stride_in_bytes) -{ - stbhw__process p; - int i; - - p.data = data; - p.w = w; - p.h = h; - p.stride = stride_in_bytes; - p.ts = 0; - p.c = c; - - if (c->is_corner) { - p.process_h_rect = stbhw__corner_process_h_rect; - p.process_v_rect = stbhw__corner_process_v_rect; - } else { - p.process_h_rect = stbhw__edge_process_h_rect; - p.process_v_rect = stbhw__edge_process_v_rect; - } - - for (i=0; i < p.h; ++i) - memset(p.data + i*p.stride, 255, 3*p.w); - - if (!stbhw__process_template(&p)) - return 0; - - if (c->is_corner) { - // write out binary information in first line of image - for (i=0; i < 4; ++i) - data[w*3-1-i] = c->num_color[i]; - data[w*3-1-i] = c->num_vary_x; - data[w*3-2-i] = c->num_vary_y; - data[w*3-3-i] = c->short_side_len; - data[w*3-4-i] = 0xc0; - } else { - for (i=0; i < 6; ++i) - data[w*3-1-i] = c->num_color[i]; - data[w*3-1-i] = c->num_vary_x; - data[w*3-2-i] = c->num_vary_y; - data[w*3-3-i] = c->short_side_len; - } - - // make it more obvious it encodes actual data - for (i=0; i < 9; ++i) - p.data[p.w*3 - 1 - i] ^= i*55; - - return 1; -} -#endif // STB_HBWANG_IMPLEMENTATION diff --git a/impeller/third_party/stb/stb/stb_image.h b/impeller/third_party/stb/stb/stb_image.h deleted file mode 100644 index a3c1129932fc3..0000000000000 --- a/impeller/third_party/stb/stb/stb_image.h +++ /dev/null @@ -1,6755 +0,0 @@ -/* stb_image - v2.12 - public domain image loader - http://nothings.org/stb_image.h - no warranty implied; use at your own risk - - Do this: - #define STB_IMAGE_IMPLEMENTATION - before you include this file in *one* C or C++ file to create the implementation. - - // i.e. it should look like this: - #include ... - #include ... - #include ... - #define STB_IMAGE_IMPLEMENTATION - #include "stb_image.h" - - You can #define STBI_ASSERT(x) before the #include to avoid using assert.h. - And #define STBI_MALLOC, STBI_REALLOC, and STBI_FREE to avoid using malloc,realloc,free - - - QUICK NOTES: - Primarily of interest to game developers and other people who can - avoid problematic images and only need the trivial interface - - JPEG baseline & progressive (12 bpc/arithmetic not supported, same as stock IJG lib) - PNG 1/2/4/8-bit-per-channel (16 bpc not supported) - - TGA (not sure what subset, if a subset) - BMP non-1bpp, non-RLE - PSD (composited view only, no extra channels, 8/16 bit-per-channel) - - GIF (*comp always reports as 4-channel) - HDR (radiance rgbE format) - PIC (Softimage PIC) - PNM (PPM and PGM binary only) - - Animated GIF still needs a proper API, but here's one way to do it: - http://gist.github.com/urraka/685d9a6340b26b830d49 - - - decode from memory or through FILE (define STBI_NO_STDIO to remove code) - - decode from arbitrary I/O callbacks - - SIMD acceleration on x86/x64 (SSE2) and ARM (NEON) - - Full documentation under "DOCUMENTATION" below. - - - Revision 2.00 release notes: - - - Progressive JPEG is now supported. - - - PPM and PGM binary formats are now supported, thanks to Ken Miller. - - - x86 platforms now make use of SSE2 SIMD instructions for - JPEG decoding, and ARM platforms can use NEON SIMD if requested. - This work was done by Fabian "ryg" Giesen. SSE2 is used by - default, but NEON must be enabled explicitly; see docs. - - With other JPEG optimizations included in this version, we see - 2x speedup on a JPEG on an x86 machine, and a 1.5x speedup - on a JPEG on an ARM machine, relative to previous versions of this - library. The same results will not obtain for all JPGs and for all - x86/ARM machines. (Note that progressive JPEGs are significantly - slower to decode than regular JPEGs.) This doesn't mean that this - is the fastest JPEG decoder in the land; rather, it brings it - closer to parity with standard libraries. If you want the fastest - decode, look elsewhere. (See "Philosophy" section of docs below.) - - See final bullet items below for more info on SIMD. - - - Added STBI_MALLOC, STBI_REALLOC, and STBI_FREE macros for replacing - the memory allocator. Unlike other STBI libraries, these macros don't - support a context parameter, so if you need to pass a context in to - the allocator, you'll have to store it in a global or a thread-local - variable. - - - Split existing STBI_NO_HDR flag into two flags, STBI_NO_HDR and - STBI_NO_LINEAR. - STBI_NO_HDR: suppress implementation of .hdr reader format - STBI_NO_LINEAR: suppress high-dynamic-range light-linear float API - - - You can suppress implementation of any of the decoders to reduce - your code footprint by #defining one or more of the following - symbols before creating the implementation. - - STBI_NO_JPEG - STBI_NO_PNG - STBI_NO_BMP - STBI_NO_PSD - STBI_NO_TGA - STBI_NO_GIF - STBI_NO_HDR - STBI_NO_PIC - STBI_NO_PNM (.ppm and .pgm) - - - You can request *only* certain decoders and suppress all other ones - (this will be more forward-compatible, as addition of new decoders - doesn't require you to disable them explicitly): - - STBI_ONLY_JPEG - STBI_ONLY_PNG - STBI_ONLY_BMP - STBI_ONLY_PSD - STBI_ONLY_TGA - STBI_ONLY_GIF - STBI_ONLY_HDR - STBI_ONLY_PIC - STBI_ONLY_PNM (.ppm and .pgm) - - Note that you can define multiples of these, and you will get all - of them ("only x" and "only y" is interpreted to mean "only x&y"). - - - If you use STBI_NO_PNG (or _ONLY_ without PNG), and you still - want the zlib decoder to be available, #define STBI_SUPPORT_ZLIB - - - Compilation of all SIMD code can be suppressed with - #define STBI_NO_SIMD - It should not be necessary to disable SIMD unless you have issues - compiling (e.g. using an x86 compiler which doesn't support SSE - intrinsics or that doesn't support the method used to detect - SSE2 support at run-time), and even those can be reported as - bugs so I can refine the built-in compile-time checking to be - smarter. - - - The old STBI_SIMD system which allowed installing a user-defined - IDCT etc. has been removed. If you need this, don't upgrade. My - assumption is that almost nobody was doing this, and those who - were will find the built-in SIMD more satisfactory anyway. - - - RGB values computed for JPEG images are slightly different from - previous versions of stb_image. (This is due to using less - integer precision in SIMD.) The C code has been adjusted so - that the same RGB values will be computed regardless of whether - SIMD support is available, so your app should always produce - consistent results. But these results are slightly different from - previous versions. (Specifically, about 3% of available YCbCr values - will compute different RGB results from pre-1.49 versions by +-1; - most of the deviating values are one smaller in the G channel.) - - - If you must produce consistent results with previous versions of - stb_image, #define STBI_JPEG_OLD and you will get the same results - you used to; however, you will not get the SIMD speedups for - the YCbCr-to-RGB conversion step (although you should still see - significant JPEG speedup from the other changes). - - Please note that STBI_JPEG_OLD is a temporary feature; it will be - removed in future versions of the library. It is only intended for - near-term back-compatibility use. - - - Latest revision history: - 2.12 (2016-04-02) fix typo in 2.11 PSD fix that caused crashes - 2.11 (2016-04-02) 16-bit PNGS; enable SSE2 in non-gcc x64 - RGB-format JPEG; remove white matting in PSD; - allocate large structures on the stack; - correct channel count for PNG & BMP - 2.10 (2016-01-22) avoid warning introduced in 2.09 - 2.09 (2016-01-16) 16-bit TGA; comments in PNM files; STBI_REALLOC_SIZED - 2.08 (2015-09-13) fix to 2.07 cleanup, reading RGB PSD as RGBA - 2.07 (2015-09-13) partial animated GIF support - limited 16-bit PSD support - minor bugs, code cleanup, and compiler warnings - 2.06 (2015-04-19) fix bug where PSD returns wrong '*comp' value - 2.05 (2015-04-19) fix bug in progressive JPEG handling, fix warning - 2.04 (2015-04-15) try to re-enable SIMD on MinGW 64-bit - 2.03 (2015-04-12) additional corruption checking - stbi_set_flip_vertically_on_load - fix NEON support; fix mingw support - 2.02 (2015-01-19) fix incorrect assert, fix warning - 2.01 (2015-01-17) fix various warnings - 2.00b (2014-12-25) fix STBI_MALLOC in progressive JPEG - 2.00 (2014-12-25) optimize JPEG, including x86 SSE2 & ARM NEON SIMD - progressive JPEG - PGM/PPM support - STBI_MALLOC,STBI_REALLOC,STBI_FREE - STBI_NO_*, STBI_ONLY_* - GIF bugfix - - See end of file for full revision history. - - - ============================ Contributors ========================= - - Image formats Extensions, features - Sean Barrett (jpeg, png, bmp) Jetro Lauha (stbi_info) - Nicolas Schulz (hdr, psd) Martin "SpartanJ" Golini (stbi_info) - Jonathan Dummer (tga) James "moose2000" Brown (iPhone PNG) - Jean-Marc Lienher (gif) Ben "Disch" Wenger (io callbacks) - Tom Seddon (pic) Omar Cornut (1/2/4-bit PNG) - Thatcher Ulrich (psd) Nicolas Guillemot (vertical flip) - Ken Miller (pgm, ppm) Richard Mitton (16-bit PSD) - urraka@github (animated gif) Junggon Kim (PNM comments) - Daniel Gibson (16-bit TGA) - - Optimizations & bugfixes - Fabian "ryg" Giesen - Arseny Kapoulkine - - Bug & warning fixes - Marc LeBlanc David Woo Guillaume George Martins Mozeiko - Christpher Lloyd Martin Golini Jerry Jansson Joseph Thomson - Dave Moore Roy Eltham Hayaki Saito Phil Jordan - Won Chun Luke Graham Johan Duparc Nathan Reed - the Horde3D community Thomas Ruf Ronny Chevalier Nick Verigakis - Janez Zemva John Bartholomew Michal Cichon svdijk@github - Jonathan Blow Ken Hamada Tero Hanninen Baldur Karlsson - Laurent Gomila Cort Stratton Sergio Gonzalez romigrou@github - Aruelien Pocheville Thibault Reuille Cass Everitt Matthew Gregan - Ryamond Barbiero Paul Du Bois Engin Manap snagar@github - Michaelangel007@github Oriol Ferrer Mesia socks-the-fox - Blazej Dariusz Roszkowski - - -LICENSE - -This software is dual-licensed to the public domain and under the following -license: you are granted a perpetual, irrevocable license to copy, modify, -publish, and distribute this file as you see fit. - -*/ - -#ifndef STBI_INCLUDE_STB_IMAGE_H -#define STBI_INCLUDE_STB_IMAGE_H - -// DOCUMENTATION -// -// Limitations: -// - no 16-bit-per-channel PNG -// - no 12-bit-per-channel JPEG -// - no JPEGs with arithmetic coding -// - no 1-bit BMP -// - GIF always returns *comp=4 -// -// Basic usage (see HDR discussion below for HDR usage): -// int x,y,n; -// unsigned char *data = stbi_load(filename, &x, &y, &n, 0); -// // ... process data if not NULL ... -// // ... x = width, y = height, n = # 8-bit components per pixel ... -// // ... replace '0' with '1'..'4' to force that many components per pixel -// // ... but 'n' will always be the number that it would have been if you said 0 -// stbi_image_free(data) -// -// Standard parameters: -// int *x -- outputs image width in pixels -// int *y -- outputs image height in pixels -// int *comp -- outputs # of image components in image file -// int req_comp -- if non-zero, # of image components requested in result -// -// The return value from an image loader is an 'unsigned char *' which points -// to the pixel data, or NULL on an allocation failure or if the image is -// corrupt or invalid. The pixel data consists of *y scanlines of *x pixels, -// with each pixel consisting of N interleaved 8-bit components; the first -// pixel pointed to is top-left-most in the image. There is no padding between -// image scanlines or between pixels, regardless of format. The number of -// components N is 'req_comp' if req_comp is non-zero, or *comp otherwise. -// If req_comp is non-zero, *comp has the number of components that _would_ -// have been output otherwise. E.g. if you set req_comp to 4, you will always -// get RGBA output, but you can check *comp to see if it's trivially opaque -// because e.g. there were only 3 channels in the source image. -// -// An output image with N components has the following components interleaved -// in this order in each pixel: -// -// N=#comp components -// 1 grey -// 2 grey, alpha -// 3 red, green, blue -// 4 red, green, blue, alpha -// -// If image loading fails for any reason, the return value will be NULL, -// and *x, *y, *comp will be unchanged. The function stbi_failure_reason() -// can be queried for an extremely brief, end-user unfriendly explanation -// of why the load failed. Define STBI_NO_FAILURE_STRINGS to avoid -// compiling these strings at all, and STBI_FAILURE_USERMSG to get slightly -// more user-friendly ones. -// -// Paletted PNG, BMP, GIF, and PIC images are automatically depalettized. -// -// =========================================================================== -// -// Philosophy -// -// stb libraries are designed with the following priorities: -// -// 1. easy to use -// 2. easy to maintain -// 3. good performance -// -// Sometimes I let "good performance" creep up in priority over "easy to maintain", -// and for best performance I may provide less-easy-to-use APIs that give higher -// performance, in addition to the easy to use ones. Nevertheless, it's important -// to keep in mind that from the standpoint of you, a client of this library, -// all you care about is #1 and #3, and stb libraries do not emphasize #3 above all. -// -// Some secondary priorities arise directly from the first two, some of which -// make more explicit reasons why performance can't be emphasized. -// -// - Portable ("ease of use") -// - Small footprint ("easy to maintain") -// - No dependencies ("ease of use") -// -// =========================================================================== -// -// I/O callbacks -// -// I/O callbacks allow you to read from arbitrary sources, like packaged -// files or some other source. Data read from callbacks are processed -// through a small internal buffer (currently 128 bytes) to try to reduce -// overhead. -// -// The three functions you must define are "read" (reads some bytes of data), -// "skip" (skips some bytes of data), "eof" (reports if the stream is at the end). -// -// =========================================================================== -// -// SIMD support -// -// The JPEG decoder will try to automatically use SIMD kernels on x86 when -// supported by the compiler. For ARM Neon support, you must explicitly -// request it. -// -// (The old do-it-yourself SIMD API is no longer supported in the current -// code.) -// -// On x86, SSE2 will automatically be used when available based on a run-time -// test; if not, the generic C versions are used as a fall-back. On ARM targets, -// the typical path is to have separate builds for NEON and non-NEON devices -// (at least this is true for iOS and Android). Therefore, the NEON support is -// toggled by a build flag: define STBI_NEON to get NEON loops. -// -// The output of the JPEG decoder is slightly different from versions where -// SIMD support was introduced (that is, for versions before 1.49). The -// difference is only +-1 in the 8-bit RGB channels, and only on a small -// fraction of pixels. You can force the pre-1.49 behavior by defining -// STBI_JPEG_OLD, but this will disable some of the SIMD decoding path -// and hence cost some performance. -// -// If for some reason you do not want to use any of SIMD code, or if -// you have issues compiling it, you can disable it entirely by -// defining STBI_NO_SIMD. -// -// =========================================================================== -// -// HDR image support (disable by defining STBI_NO_HDR) -// -// stb_image now supports loading HDR images in general, and currently -// the Radiance .HDR file format, although the support is provided -// generically. You can still load any file through the existing interface; -// if you attempt to load an HDR file, it will be automatically remapped to -// LDR, assuming gamma 2.2 and an arbitrary scale factor defaulting to 1; -// both of these constants can be reconfigured through this interface: -// -// stbi_hdr_to_ldr_gamma(2.2f); -// stbi_hdr_to_ldr_scale(1.0f); -// -// (note, do not use _inverse_ constants; stbi_image will invert them -// appropriately). -// -// Additionally, there is a new, parallel interface for loading files as -// (linear) floats to preserve the full dynamic range: -// -// float *data = stbi_loadf(filename, &x, &y, &n, 0); -// -// If you load LDR images through this interface, those images will -// be promoted to floating point values, run through the inverse of -// constants corresponding to the above: -// -// stbi_ldr_to_hdr_scale(1.0f); -// stbi_ldr_to_hdr_gamma(2.2f); -// -// Finally, given a filename (or an open file or memory block--see header -// file for details) containing image data, you can query for the "most -// appropriate" interface to use (that is, whether the image is HDR or -// not), using: -// -// stbi_is_hdr(char *filename); -// -// =========================================================================== -// -// iPhone PNG support: -// -// By default we convert iphone-formatted PNGs back to RGB, even though -// they are internally encoded differently. You can disable this conversion -// by by calling stbi_convert_iphone_png_to_rgb(0), in which case -// you will always just get the native iphone "format" through (which -// is BGR stored in RGB). -// -// Call stbi_set_unpremultiply_on_load(1) as well to force a divide per -// pixel to remove any premultiplied alpha *only* if the image file explicitly -// says there's premultiplied data (currently only happens in iPhone images, -// and only if iPhone convert-to-rgb processing is on). -// - - -#ifndef STBI_NO_STDIO -#include -#endif // STBI_NO_STDIO - -#define STBI_VERSION 1 - -enum -{ - STBI_default = 0, // only used for req_comp - - STBI_grey = 1, - STBI_grey_alpha = 2, - STBI_rgb = 3, - STBI_rgb_alpha = 4 -}; - -typedef unsigned char stbi_uc; - -#ifdef __cplusplus -extern "C" { -#endif - -#ifdef STB_IMAGE_STATIC -#define STBIDEF static -#else -#define STBIDEF extern -#endif - -////////////////////////////////////////////////////////////////////////////// -// -// PRIMARY API - works on images of any type -// - -// -// load image by filename, open file, or memory buffer -// - -typedef struct -{ - int (*read) (void *user,char *data,int size); // fill 'data' with 'size' bytes. return number of bytes actually read - void (*skip) (void *user,int n); // skip the next 'n' bytes, or 'unget' the last -n bytes if negative - int (*eof) (void *user); // returns nonzero if we are at end of file/data -} stbi_io_callbacks; - -STBIDEF stbi_uc *stbi_load (char const *filename, int *x, int *y, int *comp, int req_comp); -STBIDEF stbi_uc *stbi_load_from_memory (stbi_uc const *buffer, int len , int *x, int *y, int *comp, int req_comp); -STBIDEF stbi_uc *stbi_load_from_callbacks(stbi_io_callbacks const *clbk , void *user, int *x, int *y, int *comp, int req_comp); - -#ifndef STBI_NO_STDIO -STBIDEF stbi_uc *stbi_load_from_file (FILE *f, int *x, int *y, int *comp, int req_comp); -// for stbi_load_from_file, file pointer is left pointing immediately after image -#endif - -#ifndef STBI_NO_LINEAR - STBIDEF float *stbi_loadf (char const *filename, int *x, int *y, int *comp, int req_comp); - STBIDEF float *stbi_loadf_from_memory (stbi_uc const *buffer, int len, int *x, int *y, int *comp, int req_comp); - STBIDEF float *stbi_loadf_from_callbacks (stbi_io_callbacks const *clbk, void *user, int *x, int *y, int *comp, int req_comp); - - #ifndef STBI_NO_STDIO - STBIDEF float *stbi_loadf_from_file (FILE *f, int *x, int *y, int *comp, int req_comp); - #endif -#endif - -#ifndef STBI_NO_HDR - STBIDEF void stbi_hdr_to_ldr_gamma(float gamma); - STBIDEF void stbi_hdr_to_ldr_scale(float scale); -#endif // STBI_NO_HDR - -#ifndef STBI_NO_LINEAR - STBIDEF void stbi_ldr_to_hdr_gamma(float gamma); - STBIDEF void stbi_ldr_to_hdr_scale(float scale); -#endif // STBI_NO_LINEAR - -// stbi_is_hdr is always defined, but always returns false if STBI_NO_HDR -STBIDEF int stbi_is_hdr_from_callbacks(stbi_io_callbacks const *clbk, void *user); -STBIDEF int stbi_is_hdr_from_memory(stbi_uc const *buffer, int len); -#ifndef STBI_NO_STDIO -STBIDEF int stbi_is_hdr (char const *filename); -STBIDEF int stbi_is_hdr_from_file(FILE *f); -#endif // STBI_NO_STDIO - - -// get a VERY brief reason for failure -// NOT THREADSAFE -STBIDEF const char *stbi_failure_reason (void); - -// free the loaded image -- this is just free() -STBIDEF void stbi_image_free (void *retval_from_stbi_load); - -// get image dimensions & components without fully decoding -STBIDEF int stbi_info_from_memory(stbi_uc const *buffer, int len, int *x, int *y, int *comp); -STBIDEF int stbi_info_from_callbacks(stbi_io_callbacks const *clbk, void *user, int *x, int *y, int *comp); - -#ifndef STBI_NO_STDIO -STBIDEF int stbi_info (char const *filename, int *x, int *y, int *comp); -STBIDEF int stbi_info_from_file (FILE *f, int *x, int *y, int *comp); - -#endif - - - -// for image formats that explicitly notate that they have premultiplied alpha, -// we just return the colors as stored in the file. set this flag to force -// unpremultiplication. results are undefined if the unpremultiply overflow. -STBIDEF void stbi_set_unpremultiply_on_load(int flag_true_if_should_unpremultiply); - -// indicate whether we should process iphone images back to canonical format, -// or just pass them through "as-is" -STBIDEF void stbi_convert_iphone_png_to_rgb(int flag_true_if_should_convert); - -// flip the image vertically, so the first pixel in the output array is the bottom left -STBIDEF void stbi_set_flip_vertically_on_load(int flag_true_if_should_flip); - -// ZLIB client - used by PNG, available for other purposes - -STBIDEF char *stbi_zlib_decode_malloc_guesssize(const char *buffer, int len, int initial_size, int *outlen); -STBIDEF char *stbi_zlib_decode_malloc_guesssize_headerflag(const char *buffer, int len, int initial_size, int *outlen, int parse_header); -STBIDEF char *stbi_zlib_decode_malloc(const char *buffer, int len, int *outlen); -STBIDEF int stbi_zlib_decode_buffer(char *obuffer, int olen, const char *ibuffer, int ilen); - -STBIDEF char *stbi_zlib_decode_noheader_malloc(const char *buffer, int len, int *outlen); -STBIDEF int stbi_zlib_decode_noheader_buffer(char *obuffer, int olen, const char *ibuffer, int ilen); - - -#ifdef __cplusplus -} -#endif - -// -// -//// end header file ///////////////////////////////////////////////////// -#endif // STBI_INCLUDE_STB_IMAGE_H - -#ifdef STB_IMAGE_IMPLEMENTATION - -#if defined(STBI_ONLY_JPEG) || defined(STBI_ONLY_PNG) || defined(STBI_ONLY_BMP) \ - || defined(STBI_ONLY_TGA) || defined(STBI_ONLY_GIF) || defined(STBI_ONLY_PSD) \ - || defined(STBI_ONLY_HDR) || defined(STBI_ONLY_PIC) || defined(STBI_ONLY_PNM) \ - || defined(STBI_ONLY_ZLIB) - #ifndef STBI_ONLY_JPEG - #define STBI_NO_JPEG - #endif - #ifndef STBI_ONLY_PNG - #define STBI_NO_PNG - #endif - #ifndef STBI_ONLY_BMP - #define STBI_NO_BMP - #endif - #ifndef STBI_ONLY_PSD - #define STBI_NO_PSD - #endif - #ifndef STBI_ONLY_TGA - #define STBI_NO_TGA - #endif - #ifndef STBI_ONLY_GIF - #define STBI_NO_GIF - #endif - #ifndef STBI_ONLY_HDR - #define STBI_NO_HDR - #endif - #ifndef STBI_ONLY_PIC - #define STBI_NO_PIC - #endif - #ifndef STBI_ONLY_PNM - #define STBI_NO_PNM - #endif -#endif - -#if defined(STBI_NO_PNG) && !defined(STBI_SUPPORT_ZLIB) && !defined(STBI_NO_ZLIB) -#define STBI_NO_ZLIB -#endif - - -#include -#include // ptrdiff_t on osx -#include -#include - -#if !defined(STBI_NO_LINEAR) || !defined(STBI_NO_HDR) -#include // ldexp -#endif - -#ifndef STBI_NO_STDIO -#include -#endif - -#ifndef STBI_ASSERT -#include -#define STBI_ASSERT(x) assert(x) -#endif - - -#ifndef _MSC_VER - #ifdef __cplusplus - #define stbi_inline inline - #else - #define stbi_inline - #endif -#else - #define stbi_inline __forceinline -#endif - - -#ifdef _MSC_VER -typedef unsigned short stbi__uint16; -typedef signed short stbi__int16; -typedef unsigned int stbi__uint32; -typedef signed int stbi__int32; -#else -#include -typedef uint16_t stbi__uint16; -typedef int16_t stbi__int16; -typedef uint32_t stbi__uint32; -typedef int32_t stbi__int32; -#endif - -// should produce compiler error if size is wrong -typedef unsigned char validate_uint32[sizeof(stbi__uint32)==4 ? 1 : -1]; - -#ifdef _MSC_VER -#define STBI_NOTUSED(v) (void)(v) -#else -#define STBI_NOTUSED(v) (void)sizeof(v) -#endif - -#ifdef _MSC_VER -#define STBI_HAS_LROTL -#endif - -#ifdef STBI_HAS_LROTL - #define stbi_lrot(x,y) _lrotl(x,y) -#else - #define stbi_lrot(x,y) (((x) << (y)) | ((x) >> (32 - (y)))) -#endif - -#if defined(STBI_MALLOC) && defined(STBI_FREE) && (defined(STBI_REALLOC) || defined(STBI_REALLOC_SIZED)) -// ok -#elif !defined(STBI_MALLOC) && !defined(STBI_FREE) && !defined(STBI_REALLOC) && !defined(STBI_REALLOC_SIZED) -// ok -#else -#error "Must define all or none of STBI_MALLOC, STBI_FREE, and STBI_REALLOC (or STBI_REALLOC_SIZED)." -#endif - -#ifndef STBI_MALLOC -#define STBI_MALLOC(sz) malloc(sz) -#define STBI_REALLOC(p,newsz) realloc(p,newsz) -#define STBI_FREE(p) free(p) -#endif - -#ifndef STBI_REALLOC_SIZED -#define STBI_REALLOC_SIZED(p,oldsz,newsz) STBI_REALLOC(p,newsz) -#endif - -// x86/x64 detection -#if defined(__x86_64__) || defined(_M_X64) -#define STBI__X64_TARGET -#elif defined(__i386) || defined(_M_IX86) -#define STBI__X86_TARGET -#endif - -#if defined(__GNUC__) && (defined(STBI__X86_TARGET) || defined(STBI__X64_TARGET)) && !defined(__SSE2__) && !defined(STBI_NO_SIMD) -// NOTE: not clear do we actually need this for the 64-bit path? -// gcc doesn't support sse2 intrinsics unless you compile with -msse2, -// (but compiling with -msse2 allows the compiler to use SSE2 everywhere; -// this is just broken and gcc are jerks for not fixing it properly -// http://www.virtualdub.org/blog/pivot/entry.php?id=363 ) -#define STBI_NO_SIMD -#endif - -#if defined(__MINGW32__) && defined(STBI__X86_TARGET) && !defined(STBI_MINGW_ENABLE_SSE2) && !defined(STBI_NO_SIMD) -// Note that __MINGW32__ doesn't actually mean 32-bit, so we have to avoid STBI__X64_TARGET -// -// 32-bit MinGW wants ESP to be 16-byte aligned, but this is not in the -// Windows ABI and VC++ as well as Windows DLLs don't maintain that invariant. -// As a result, enabling SSE2 on 32-bit MinGW is dangerous when not -// simultaneously enabling "-mstackrealign". -// -// See https://github.com/nothings/stb/issues/81 for more information. -// -// So default to no SSE2 on 32-bit MinGW. If you've read this far and added -// -mstackrealign to your build settings, feel free to #define STBI_MINGW_ENABLE_SSE2. -#define STBI_NO_SIMD -#endif - -#if !defined(STBI_NO_SIMD) && (defined(STBI__X86_TARGET) || defined(STBI__X64_TARGET)) -#define STBI_SSE2 -#include - -#ifdef _MSC_VER - -#if _MSC_VER >= 1400 // not VC6 -#include // __cpuid -static int stbi__cpuid3(void) -{ - int info[4]; - __cpuid(info,1); - return info[3]; -} -#else -static int stbi__cpuid3(void) -{ - int res; - __asm { - mov eax,1 - cpuid - mov res,edx - } - return res; -} -#endif - -#define STBI_SIMD_ALIGN(type, name) __declspec(align(16)) type name - -static int stbi__sse2_available() -{ - int info3 = stbi__cpuid3(); - return ((info3 >> 26) & 1) != 0; -} -#else // assume GCC-style if not VC++ -#define STBI_SIMD_ALIGN(type, name) type name __attribute__((aligned(16))) - -static int stbi__sse2_available() -{ -#if defined(__GNUC__) && (__GNUC__ * 100 + __GNUC_MINOR__) >= 408 // GCC 4.8 or later - // GCC 4.8+ has a nice way to do this - return __builtin_cpu_supports("sse2"); -#else - // portable way to do this, preferably without using GCC inline ASM? - // just bail for now. - return 0; -#endif -} -#endif -#endif - -// ARM NEON -#if defined(STBI_NO_SIMD) && defined(STBI_NEON) -#undef STBI_NEON -#endif - -#ifdef STBI_NEON -#include -// assume GCC or Clang on ARM targets -#define STBI_SIMD_ALIGN(type, name) type name __attribute__((aligned(16))) -#endif - -#ifndef STBI_SIMD_ALIGN -#define STBI_SIMD_ALIGN(type, name) type name -#endif - -/////////////////////////////////////////////// -// -// stbi__context struct and start_xxx functions - -// stbi__context structure is our basic context used by all images, so it -// contains all the IO context, plus some basic image information -typedef struct -{ - stbi__uint32 img_x, img_y; - int img_n, img_out_n; - - stbi_io_callbacks io; - void *io_user_data; - - int read_from_callbacks; - int buflen; - stbi_uc buffer_start[128]; - - stbi_uc *img_buffer, *img_buffer_end; - stbi_uc *img_buffer_original, *img_buffer_original_end; -} stbi__context; - - -static void stbi__refill_buffer(stbi__context *s); - -// initialize a memory-decode context -static void stbi__start_mem(stbi__context *s, stbi_uc const *buffer, int len) -{ - s->io.read = NULL; - s->read_from_callbacks = 0; - s->img_buffer = s->img_buffer_original = (stbi_uc *) buffer; - s->img_buffer_end = s->img_buffer_original_end = (stbi_uc *) buffer+len; -} - -// initialize a callback-based context -static void stbi__start_callbacks(stbi__context *s, stbi_io_callbacks *c, void *user) -{ - s->io = *c; - s->io_user_data = user; - s->buflen = sizeof(s->buffer_start); - s->read_from_callbacks = 1; - s->img_buffer_original = s->buffer_start; - stbi__refill_buffer(s); - s->img_buffer_original_end = s->img_buffer_end; -} - -#ifndef STBI_NO_STDIO - -static int stbi__stdio_read(void *user, char *data, int size) -{ - return (int) fread(data,1,size,(FILE*) user); -} - -static void stbi__stdio_skip(void *user, int n) -{ - fseek((FILE*) user, n, SEEK_CUR); -} - -static int stbi__stdio_eof(void *user) -{ - return feof((FILE*) user); -} - -static stbi_io_callbacks stbi__stdio_callbacks = -{ - stbi__stdio_read, - stbi__stdio_skip, - stbi__stdio_eof, -}; - -static void stbi__start_file(stbi__context *s, FILE *f) -{ - stbi__start_callbacks(s, &stbi__stdio_callbacks, (void *) f); -} - -//static void stop_file(stbi__context *s) { } - -#endif // !STBI_NO_STDIO - -static void stbi__rewind(stbi__context *s) -{ - // conceptually rewind SHOULD rewind to the beginning of the stream, - // but we just rewind to the beginning of the initial buffer, because - // we only use it after doing 'test', which only ever looks at at most 92 bytes - s->img_buffer = s->img_buffer_original; - s->img_buffer_end = s->img_buffer_original_end; -} - -#ifndef STBI_NO_JPEG -static int stbi__jpeg_test(stbi__context *s); -static stbi_uc *stbi__jpeg_load(stbi__context *s, int *x, int *y, int *comp, int req_comp); -static int stbi__jpeg_info(stbi__context *s, int *x, int *y, int *comp); -#endif - -#ifndef STBI_NO_PNG -static int stbi__png_test(stbi__context *s); -static stbi_uc *stbi__png_load(stbi__context *s, int *x, int *y, int *comp, int req_comp); -static int stbi__png_info(stbi__context *s, int *x, int *y, int *comp); -#endif - -#ifndef STBI_NO_BMP -static int stbi__bmp_test(stbi__context *s); -static stbi_uc *stbi__bmp_load(stbi__context *s, int *x, int *y, int *comp, int req_comp); -static int stbi__bmp_info(stbi__context *s, int *x, int *y, int *comp); -#endif - -#ifndef STBI_NO_TGA -static int stbi__tga_test(stbi__context *s); -static stbi_uc *stbi__tga_load(stbi__context *s, int *x, int *y, int *comp, int req_comp); -static int stbi__tga_info(stbi__context *s, int *x, int *y, int *comp); -#endif - -#ifndef STBI_NO_PSD -static int stbi__psd_test(stbi__context *s); -static stbi_uc *stbi__psd_load(stbi__context *s, int *x, int *y, int *comp, int req_comp); -static int stbi__psd_info(stbi__context *s, int *x, int *y, int *comp); -#endif - -#ifndef STBI_NO_HDR -static int stbi__hdr_test(stbi__context *s); -static float *stbi__hdr_load(stbi__context *s, int *x, int *y, int *comp, int req_comp); -static int stbi__hdr_info(stbi__context *s, int *x, int *y, int *comp); -#endif - -#ifndef STBI_NO_PIC -static int stbi__pic_test(stbi__context *s); -static stbi_uc *stbi__pic_load(stbi__context *s, int *x, int *y, int *comp, int req_comp); -static int stbi__pic_info(stbi__context *s, int *x, int *y, int *comp); -#endif - -#ifndef STBI_NO_GIF -static int stbi__gif_test(stbi__context *s); -static stbi_uc *stbi__gif_load(stbi__context *s, int *x, int *y, int *comp, int req_comp); -static int stbi__gif_info(stbi__context *s, int *x, int *y, int *comp); -#endif - -#ifndef STBI_NO_PNM -static int stbi__pnm_test(stbi__context *s); -static stbi_uc *stbi__pnm_load(stbi__context *s, int *x, int *y, int *comp, int req_comp); -static int stbi__pnm_info(stbi__context *s, int *x, int *y, int *comp); -#endif - -// this is not threadsafe -static const char *stbi__g_failure_reason; - -STBIDEF const char *stbi_failure_reason(void) -{ - return stbi__g_failure_reason; -} - -static int stbi__err(const char *str) -{ - stbi__g_failure_reason = str; - return 0; -} - -static void *stbi__malloc(size_t size) -{ - return STBI_MALLOC(size); -} - -// stbi__err - error -// stbi__errpf - error returning pointer to float -// stbi__errpuc - error returning pointer to unsigned char - -#ifdef STBI_NO_FAILURE_STRINGS - #define stbi__err(x,y) 0 -#elif defined(STBI_FAILURE_USERMSG) - #define stbi__err(x,y) stbi__err(y) -#else - #define stbi__err(x,y) stbi__err(x) -#endif - -#define stbi__errpf(x,y) ((float *)(size_t) (stbi__err(x,y)?NULL:NULL)) -#define stbi__errpuc(x,y) ((unsigned char *)(size_t) (stbi__err(x,y)?NULL:NULL)) - -STBIDEF void stbi_image_free(void *retval_from_stbi_load) -{ - STBI_FREE(retval_from_stbi_load); -} - -#ifndef STBI_NO_LINEAR -static float *stbi__ldr_to_hdr(stbi_uc *data, int x, int y, int comp); -#endif - -#ifndef STBI_NO_HDR -static stbi_uc *stbi__hdr_to_ldr(float *data, int x, int y, int comp); -#endif - -static int stbi__vertically_flip_on_load = 0; - -STBIDEF void stbi_set_flip_vertically_on_load(int flag_true_if_should_flip) -{ - stbi__vertically_flip_on_load = flag_true_if_should_flip; -} - -static unsigned char *stbi__load_main(stbi__context *s, int *x, int *y, int *comp, int req_comp) -{ - #ifndef STBI_NO_JPEG - if (stbi__jpeg_test(s)) return stbi__jpeg_load(s,x,y,comp,req_comp); - #endif - #ifndef STBI_NO_PNG - if (stbi__png_test(s)) return stbi__png_load(s,x,y,comp,req_comp); - #endif - #ifndef STBI_NO_BMP - if (stbi__bmp_test(s)) return stbi__bmp_load(s,x,y,comp,req_comp); - #endif - #ifndef STBI_NO_GIF - if (stbi__gif_test(s)) return stbi__gif_load(s,x,y,comp,req_comp); - #endif - #ifndef STBI_NO_PSD - if (stbi__psd_test(s)) return stbi__psd_load(s,x,y,comp,req_comp); - #endif - #ifndef STBI_NO_PIC - if (stbi__pic_test(s)) return stbi__pic_load(s,x,y,comp,req_comp); - #endif - #ifndef STBI_NO_PNM - if (stbi__pnm_test(s)) return stbi__pnm_load(s,x,y,comp,req_comp); - #endif - - #ifndef STBI_NO_HDR - if (stbi__hdr_test(s)) { - float *hdr = stbi__hdr_load(s, x,y,comp,req_comp); - return stbi__hdr_to_ldr(hdr, *x, *y, req_comp ? req_comp : *comp); - } - #endif - - #ifndef STBI_NO_TGA - // test tga last because it's a crappy test! - if (stbi__tga_test(s)) - return stbi__tga_load(s,x,y,comp,req_comp); - #endif - - return stbi__errpuc("unknown image type", "Image not of any known type, or corrupt"); -} - -static unsigned char *stbi__load_flip(stbi__context *s, int *x, int *y, int *comp, int req_comp) -{ - unsigned char *result = stbi__load_main(s, x, y, comp, req_comp); - - if (stbi__vertically_flip_on_load && result != NULL) { - int w = *x, h = *y; - int depth = req_comp ? req_comp : *comp; - int row,col,z; - stbi_uc temp; - - // @OPTIMIZE: use a bigger temp buffer and memcpy multiple pixels at once - for (row = 0; row < (h>>1); row++) { - for (col = 0; col < w; col++) { - for (z = 0; z < depth; z++) { - temp = result[(row * w + col) * depth + z]; - result[(row * w + col) * depth + z] = result[((h - row - 1) * w + col) * depth + z]; - result[((h - row - 1) * w + col) * depth + z] = temp; - } - } - } - } - - return result; -} - -#ifndef STBI_NO_HDR -static void stbi__float_postprocess(float *result, int *x, int *y, int *comp, int req_comp) -{ - if (stbi__vertically_flip_on_load && result != NULL) { - int w = *x, h = *y; - int depth = req_comp ? req_comp : *comp; - int row,col,z; - float temp; - - // @OPTIMIZE: use a bigger temp buffer and memcpy multiple pixels at once - for (row = 0; row < (h>>1); row++) { - for (col = 0; col < w; col++) { - for (z = 0; z < depth; z++) { - temp = result[(row * w + col) * depth + z]; - result[(row * w + col) * depth + z] = result[((h - row - 1) * w + col) * depth + z]; - result[((h - row - 1) * w + col) * depth + z] = temp; - } - } - } - } -} -#endif - -#ifndef STBI_NO_STDIO - -static FILE *stbi__fopen(char const *filename, char const *mode) -{ - FILE *f; -#if defined(_MSC_VER) && _MSC_VER >= 1400 - if (0 != fopen_s(&f, filename, mode)) - f=0; -#else - f = fopen(filename, mode); -#endif - return f; -} - - -STBIDEF stbi_uc *stbi_load(char const *filename, int *x, int *y, int *comp, int req_comp) -{ - FILE *f = stbi__fopen(filename, "rb"); - unsigned char *result; - if (!f) return stbi__errpuc("can't fopen", "Unable to open file"); - result = stbi_load_from_file(f,x,y,comp,req_comp); - fclose(f); - return result; -} - -STBIDEF stbi_uc *stbi_load_from_file(FILE *f, int *x, int *y, int *comp, int req_comp) -{ - unsigned char *result; - stbi__context s; - stbi__start_file(&s,f); - result = stbi__load_flip(&s,x,y,comp,req_comp); - if (result) { - // need to 'unget' all the characters in the IO buffer - fseek(f, - (int) (s.img_buffer_end - s.img_buffer), SEEK_CUR); - } - return result; -} -#endif //!STBI_NO_STDIO - -STBIDEF stbi_uc *stbi_load_from_memory(stbi_uc const *buffer, int len, int *x, int *y, int *comp, int req_comp) -{ - stbi__context s; - stbi__start_mem(&s,buffer,len); - return stbi__load_flip(&s,x,y,comp,req_comp); -} - -STBIDEF stbi_uc *stbi_load_from_callbacks(stbi_io_callbacks const *clbk, void *user, int *x, int *y, int *comp, int req_comp) -{ - stbi__context s; - stbi__start_callbacks(&s, (stbi_io_callbacks *) clbk, user); - return stbi__load_flip(&s,x,y,comp,req_comp); -} - -#ifndef STBI_NO_LINEAR -static float *stbi__loadf_main(stbi__context *s, int *x, int *y, int *comp, int req_comp) -{ - unsigned char *data; - #ifndef STBI_NO_HDR - if (stbi__hdr_test(s)) { - float *hdr_data = stbi__hdr_load(s,x,y,comp,req_comp); - if (hdr_data) - stbi__float_postprocess(hdr_data,x,y,comp,req_comp); - return hdr_data; - } - #endif - data = stbi__load_flip(s, x, y, comp, req_comp); - if (data) - return stbi__ldr_to_hdr(data, *x, *y, req_comp ? req_comp : *comp); - return stbi__errpf("unknown image type", "Image not of any known type, or corrupt"); -} - -STBIDEF float *stbi_loadf_from_memory(stbi_uc const *buffer, int len, int *x, int *y, int *comp, int req_comp) -{ - stbi__context s; - stbi__start_mem(&s,buffer,len); - return stbi__loadf_main(&s,x,y,comp,req_comp); -} - -STBIDEF float *stbi_loadf_from_callbacks(stbi_io_callbacks const *clbk, void *user, int *x, int *y, int *comp, int req_comp) -{ - stbi__context s; - stbi__start_callbacks(&s, (stbi_io_callbacks *) clbk, user); - return stbi__loadf_main(&s,x,y,comp,req_comp); -} - -#ifndef STBI_NO_STDIO -STBIDEF float *stbi_loadf(char const *filename, int *x, int *y, int *comp, int req_comp) -{ - float *result; - FILE *f = stbi__fopen(filename, "rb"); - if (!f) return stbi__errpf("can't fopen", "Unable to open file"); - result = stbi_loadf_from_file(f,x,y,comp,req_comp); - fclose(f); - return result; -} - -STBIDEF float *stbi_loadf_from_file(FILE *f, int *x, int *y, int *comp, int req_comp) -{ - stbi__context s; - stbi__start_file(&s,f); - return stbi__loadf_main(&s,x,y,comp,req_comp); -} -#endif // !STBI_NO_STDIO - -#endif // !STBI_NO_LINEAR - -// these is-hdr-or-not is defined independent of whether STBI_NO_LINEAR is -// defined, for API simplicity; if STBI_NO_LINEAR is defined, it always -// reports false! - -STBIDEF int stbi_is_hdr_from_memory(stbi_uc const *buffer, int len) -{ - #ifndef STBI_NO_HDR - stbi__context s; - stbi__start_mem(&s,buffer,len); - return stbi__hdr_test(&s); - #else - STBI_NOTUSED(buffer); - STBI_NOTUSED(len); - return 0; - #endif -} - -#ifndef STBI_NO_STDIO -STBIDEF int stbi_is_hdr (char const *filename) -{ - FILE *f = stbi__fopen(filename, "rb"); - int result=0; - if (f) { - result = stbi_is_hdr_from_file(f); - fclose(f); - } - return result; -} - -STBIDEF int stbi_is_hdr_from_file(FILE *f) -{ - #ifndef STBI_NO_HDR - stbi__context s; - stbi__start_file(&s,f); - return stbi__hdr_test(&s); - #else - STBI_NOTUSED(f); - return 0; - #endif -} -#endif // !STBI_NO_STDIO - -STBIDEF int stbi_is_hdr_from_callbacks(stbi_io_callbacks const *clbk, void *user) -{ - #ifndef STBI_NO_HDR - stbi__context s; - stbi__start_callbacks(&s, (stbi_io_callbacks *) clbk, user); - return stbi__hdr_test(&s); - #else - STBI_NOTUSED(clbk); - STBI_NOTUSED(user); - return 0; - #endif -} - -#ifndef STBI_NO_LINEAR -static float stbi__l2h_gamma=2.2f, stbi__l2h_scale=1.0f; - -STBIDEF void stbi_ldr_to_hdr_gamma(float gamma) { stbi__l2h_gamma = gamma; } -STBIDEF void stbi_ldr_to_hdr_scale(float scale) { stbi__l2h_scale = scale; } -#endif - -static float stbi__h2l_gamma_i=1.0f/2.2f, stbi__h2l_scale_i=1.0f; - -STBIDEF void stbi_hdr_to_ldr_gamma(float gamma) { stbi__h2l_gamma_i = 1/gamma; } -STBIDEF void stbi_hdr_to_ldr_scale(float scale) { stbi__h2l_scale_i = 1/scale; } - - -////////////////////////////////////////////////////////////////////////////// -// -// Common code used by all image loaders -// - -enum -{ - STBI__SCAN_load=0, - STBI__SCAN_type, - STBI__SCAN_header -}; - -static void stbi__refill_buffer(stbi__context *s) -{ - int n = (s->io.read)(s->io_user_data,(char*)s->buffer_start,s->buflen); - if (n == 0) { - // at end of file, treat same as if from memory, but need to handle case - // where s->img_buffer isn't pointing to safe memory, e.g. 0-byte file - s->read_from_callbacks = 0; - s->img_buffer = s->buffer_start; - s->img_buffer_end = s->buffer_start+1; - *s->img_buffer = 0; - } else { - s->img_buffer = s->buffer_start; - s->img_buffer_end = s->buffer_start + n; - } -} - -stbi_inline static stbi_uc stbi__get8(stbi__context *s) -{ - if (s->img_buffer < s->img_buffer_end) - return *s->img_buffer++; - if (s->read_from_callbacks) { - stbi__refill_buffer(s); - return *s->img_buffer++; - } - return 0; -} - -stbi_inline static int stbi__at_eof(stbi__context *s) -{ - if (s->io.read) { - if (!(s->io.eof)(s->io_user_data)) return 0; - // if feof() is true, check if buffer = end - // special case: we've only got the special 0 character at the end - if (s->read_from_callbacks == 0) return 1; - } - - return s->img_buffer >= s->img_buffer_end; -} - -static void stbi__skip(stbi__context *s, int n) -{ - if (n < 0) { - s->img_buffer = s->img_buffer_end; - return; - } - if (s->io.read) { - int blen = (int) (s->img_buffer_end - s->img_buffer); - if (blen < n) { - s->img_buffer = s->img_buffer_end; - (s->io.skip)(s->io_user_data, n - blen); - return; - } - } - s->img_buffer += n; -} - -static int stbi__getn(stbi__context *s, stbi_uc *buffer, int n) -{ - if (s->io.read) { - int blen = (int) (s->img_buffer_end - s->img_buffer); - if (blen < n) { - int res, count; - - memcpy(buffer, s->img_buffer, blen); - - count = (s->io.read)(s->io_user_data, (char*) buffer + blen, n - blen); - res = (count == (n-blen)); - s->img_buffer = s->img_buffer_end; - return res; - } - } - - if (s->img_buffer+n <= s->img_buffer_end) { - memcpy(buffer, s->img_buffer, n); - s->img_buffer += n; - return 1; - } else - return 0; -} - -static int stbi__get16be(stbi__context *s) -{ - int z = stbi__get8(s); - return (z << 8) + stbi__get8(s); -} - -static stbi__uint32 stbi__get32be(stbi__context *s) -{ - stbi__uint32 z = stbi__get16be(s); - return (z << 16) + stbi__get16be(s); -} - -#if defined(STBI_NO_BMP) && defined(STBI_NO_TGA) && defined(STBI_NO_GIF) -// nothing -#else -static int stbi__get16le(stbi__context *s) -{ - int z = stbi__get8(s); - return z + (stbi__get8(s) << 8); -} -#endif - -#ifndef STBI_NO_BMP -static stbi__uint32 stbi__get32le(stbi__context *s) -{ - stbi__uint32 z = stbi__get16le(s); - return z + (stbi__get16le(s) << 16); -} -#endif - -#define STBI__BYTECAST(x) ((stbi_uc) ((x) & 255)) // truncate int to byte without warnings - - -////////////////////////////////////////////////////////////////////////////// -// -// generic converter from built-in img_n to req_comp -// individual types do this automatically as much as possible (e.g. jpeg -// does all cases internally since it needs to colorspace convert anyway, -// and it never has alpha, so very few cases ). png can automatically -// interleave an alpha=255 channel, but falls back to this for other cases -// -// assume data buffer is malloced, so malloc a new one and free that one -// only failure mode is malloc failing - -static stbi_uc stbi__compute_y(int r, int g, int b) -{ - return (stbi_uc) (((r*77) + (g*150) + (29*b)) >> 8); -} - -static unsigned char *stbi__convert_format(unsigned char *data, int img_n, int req_comp, unsigned int x, unsigned int y) -{ - int i,j; - unsigned char *good; - - if (req_comp == img_n) return data; - STBI_ASSERT(req_comp >= 1 && req_comp <= 4); - - good = (unsigned char *) stbi__malloc(req_comp * x * y); - if (good == NULL) { - STBI_FREE(data); - return stbi__errpuc("outofmem", "Out of memory"); - } - - for (j=0; j < (int) y; ++j) { - unsigned char *src = data + j * x * img_n ; - unsigned char *dest = good + j * x * req_comp; - - #define COMBO(a,b) ((a)*8+(b)) - #define CASE(a,b) case COMBO(a,b): for(i=x-1; i >= 0; --i, src += a, dest += b) - // convert source image with img_n components to one with req_comp components; - // avoid switch per pixel, so use switch per scanline and massive macros - switch (COMBO(img_n, req_comp)) { - CASE(1,2) dest[0]=src[0], dest[1]=255; break; - CASE(1,3) dest[0]=dest[1]=dest[2]=src[0]; break; - CASE(1,4) dest[0]=dest[1]=dest[2]=src[0], dest[3]=255; break; - CASE(2,1) dest[0]=src[0]; break; - CASE(2,3) dest[0]=dest[1]=dest[2]=src[0]; break; - CASE(2,4) dest[0]=dest[1]=dest[2]=src[0], dest[3]=src[1]; break; - CASE(3,4) dest[0]=src[0],dest[1]=src[1],dest[2]=src[2],dest[3]=255; break; - CASE(3,1) dest[0]=stbi__compute_y(src[0],src[1],src[2]); break; - CASE(3,2) dest[0]=stbi__compute_y(src[0],src[1],src[2]), dest[1] = 255; break; - CASE(4,1) dest[0]=stbi__compute_y(src[0],src[1],src[2]); break; - CASE(4,2) dest[0]=stbi__compute_y(src[0],src[1],src[2]), dest[1] = src[3]; break; - CASE(4,3) dest[0]=src[0],dest[1]=src[1],dest[2]=src[2]; break; - default: STBI_ASSERT(0); - } - #undef CASE - } - - STBI_FREE(data); - return good; -} - -#ifndef STBI_NO_LINEAR -static float *stbi__ldr_to_hdr(stbi_uc *data, int x, int y, int comp) -{ - int i,k,n; - float *output = (float *) stbi__malloc(x * y * comp * sizeof(float)); - if (output == NULL) { STBI_FREE(data); return stbi__errpf("outofmem", "Out of memory"); } - // compute number of non-alpha components - if (comp & 1) n = comp; else n = comp-1; - for (i=0; i < x*y; ++i) { - for (k=0; k < n; ++k) { - output[i*comp + k] = (float) (pow(data[i*comp+k]/255.0f, stbi__l2h_gamma) * stbi__l2h_scale); - } - if (k < comp) output[i*comp + k] = data[i*comp+k]/255.0f; - } - STBI_FREE(data); - return output; -} -#endif - -#ifndef STBI_NO_HDR -#define stbi__float2int(x) ((int) (x)) -static stbi_uc *stbi__hdr_to_ldr(float *data, int x, int y, int comp) -{ - int i,k,n; - stbi_uc *output = (stbi_uc *) stbi__malloc(x * y * comp); - if (output == NULL) { STBI_FREE(data); return stbi__errpuc("outofmem", "Out of memory"); } - // compute number of non-alpha components - if (comp & 1) n = comp; else n = comp-1; - for (i=0; i < x*y; ++i) { - for (k=0; k < n; ++k) { - float z = (float) pow(data[i*comp+k]*stbi__h2l_scale_i, stbi__h2l_gamma_i) * 255 + 0.5f; - if (z < 0) z = 0; - if (z > 255) z = 255; - output[i*comp + k] = (stbi_uc) stbi__float2int(z); - } - if (k < comp) { - float z = data[i*comp+k] * 255 + 0.5f; - if (z < 0) z = 0; - if (z > 255) z = 255; - output[i*comp + k] = (stbi_uc) stbi__float2int(z); - } - } - STBI_FREE(data); - return output; -} -#endif - -////////////////////////////////////////////////////////////////////////////// -// -// "baseline" JPEG/JFIF decoder -// -// simple implementation -// - doesn't support delayed output of y-dimension -// - simple interface (only one output format: 8-bit interleaved RGB) -// - doesn't try to recover corrupt jpegs -// - doesn't allow partial loading, loading multiple at once -// - still fast on x86 (copying globals into locals doesn't help x86) -// - allocates lots of intermediate memory (full size of all components) -// - non-interleaved case requires this anyway -// - allows good upsampling (see next) -// high-quality -// - upsampled channels are bilinearly interpolated, even across blocks -// - quality integer IDCT derived from IJG's 'slow' -// performance -// - fast huffman; reasonable integer IDCT -// - some SIMD kernels for common paths on targets with SSE2/NEON -// - uses a lot of intermediate memory, could cache poorly - -#ifndef STBI_NO_JPEG - -// huffman decoding acceleration -#define FAST_BITS 9 // larger handles more cases; smaller stomps less cache - -typedef struct -{ - stbi_uc fast[1 << FAST_BITS]; - // weirdly, repacking this into AoS is a 10% speed loss, instead of a win - stbi__uint16 code[256]; - stbi_uc values[256]; - stbi_uc size[257]; - unsigned int maxcode[18]; - int delta[17]; // old 'firstsymbol' - old 'firstcode' -} stbi__huffman; - -typedef struct -{ - stbi__context *s; - stbi__huffman huff_dc[4]; - stbi__huffman huff_ac[4]; - stbi_uc dequant[4][64]; - stbi__int16 fast_ac[4][1 << FAST_BITS]; - -// sizes for components, interleaved MCUs - int img_h_max, img_v_max; - int img_mcu_x, img_mcu_y; - int img_mcu_w, img_mcu_h; - -// definition of jpeg image component - struct - { - int id; - int h,v; - int tq; - int hd,ha; - int dc_pred; - - int x,y,w2,h2; - stbi_uc *data; - void *raw_data, *raw_coeff; - stbi_uc *linebuf; - short *coeff; // progressive only - int coeff_w, coeff_h; // number of 8x8 coefficient blocks - } img_comp[4]; - - stbi__uint32 code_buffer; // jpeg entropy-coded buffer - int code_bits; // number of valid bits - unsigned char marker; // marker seen while filling entropy buffer - int nomore; // flag if we saw a marker so must stop - - int progressive; - int spec_start; - int spec_end; - int succ_high; - int succ_low; - int eob_run; - int rgb; - - int scan_n, order[4]; - int restart_interval, todo; - -// kernels - void (*idct_block_kernel)(stbi_uc *out, int out_stride, short data[64]); - void (*YCbCr_to_RGB_kernel)(stbi_uc *out, const stbi_uc *y, const stbi_uc *pcb, const stbi_uc *pcr, int count, int step); - stbi_uc *(*resample_row_hv_2_kernel)(stbi_uc *out, stbi_uc *in_near, stbi_uc *in_far, int w, int hs); -} stbi__jpeg; - -static int stbi__build_huffman(stbi__huffman *h, int *count) -{ - int i,j,k=0,code; - // build size list for each symbol (from JPEG spec) - for (i=0; i < 16; ++i) - for (j=0; j < count[i]; ++j) - h->size[k++] = (stbi_uc) (i+1); - h->size[k] = 0; - - // compute actual symbols (from jpeg spec) - code = 0; - k = 0; - for(j=1; j <= 16; ++j) { - // compute delta to add to code to compute symbol id - h->delta[j] = k - code; - if (h->size[k] == j) { - while (h->size[k] == j) - h->code[k++] = (stbi__uint16) (code++); - if (code-1 >= (1 << j)) return stbi__err("bad code lengths","Corrupt JPEG"); - } - // compute largest code + 1 for this size, preshifted as needed later - h->maxcode[j] = code << (16-j); - code <<= 1; - } - h->maxcode[j] = 0xffffffff; - - // build non-spec acceleration table; 255 is flag for not-accelerated - memset(h->fast, 255, 1 << FAST_BITS); - for (i=0; i < k; ++i) { - int s = h->size[i]; - if (s <= FAST_BITS) { - int c = h->code[i] << (FAST_BITS-s); - int m = 1 << (FAST_BITS-s); - for (j=0; j < m; ++j) { - h->fast[c+j] = (stbi_uc) i; - } - } - } - return 1; -} - -// build a table that decodes both magnitude and value of small ACs in -// one go. -static void stbi__build_fast_ac(stbi__int16 *fast_ac, stbi__huffman *h) -{ - int i; - for (i=0; i < (1 << FAST_BITS); ++i) { - stbi_uc fast = h->fast[i]; - fast_ac[i] = 0; - if (fast < 255) { - int rs = h->values[fast]; - int run = (rs >> 4) & 15; - int magbits = rs & 15; - int len = h->size[fast]; - - if (magbits && len + magbits <= FAST_BITS) { - // magnitude code followed by receive_extend code - int k = ((i << len) & ((1 << FAST_BITS) - 1)) >> (FAST_BITS - magbits); - int m = 1 << (magbits - 1); - if (k < m) k += (-1 << magbits) + 1; - // if the result is small enough, we can fit it in fast_ac table - if (k >= -128 && k <= 127) - fast_ac[i] = (stbi__int16) ((k << 8) + (run << 4) + (len + magbits)); - } - } - } -} - -static void stbi__grow_buffer_unsafe(stbi__jpeg *j) -{ - do { - int b = j->nomore ? 0 : stbi__get8(j->s); - if (b == 0xff) { - int c = stbi__get8(j->s); - if (c != 0) { - j->marker = (unsigned char) c; - j->nomore = 1; - return; - } - } - j->code_buffer |= b << (24 - j->code_bits); - j->code_bits += 8; - } while (j->code_bits <= 24); -} - -// (1 << n) - 1 -static stbi__uint32 stbi__bmask[17]={0,1,3,7,15,31,63,127,255,511,1023,2047,4095,8191,16383,32767,65535}; - -// decode a jpeg huffman value from the bitstream -stbi_inline static int stbi__jpeg_huff_decode(stbi__jpeg *j, stbi__huffman *h) -{ - unsigned int temp; - int c,k; - - if (j->code_bits < 16) stbi__grow_buffer_unsafe(j); - - // look at the top FAST_BITS and determine what symbol ID it is, - // if the code is <= FAST_BITS - c = (j->code_buffer >> (32 - FAST_BITS)) & ((1 << FAST_BITS)-1); - k = h->fast[c]; - if (k < 255) { - int s = h->size[k]; - if (s > j->code_bits) - return -1; - j->code_buffer <<= s; - j->code_bits -= s; - return h->values[k]; - } - - // naive test is to shift the code_buffer down so k bits are - // valid, then test against maxcode. To speed this up, we've - // preshifted maxcode left so that it has (16-k) 0s at the - // end; in other words, regardless of the number of bits, it - // wants to be compared against something shifted to have 16; - // that way we don't need to shift inside the loop. - temp = j->code_buffer >> 16; - for (k=FAST_BITS+1 ; ; ++k) - if (temp < h->maxcode[k]) - break; - if (k == 17) { - // error! code not found - j->code_bits -= 16; - return -1; - } - - if (k > j->code_bits) - return -1; - - // convert the huffman code to the symbol id - c = ((j->code_buffer >> (32 - k)) & stbi__bmask[k]) + h->delta[k]; - STBI_ASSERT((((j->code_buffer) >> (32 - h->size[c])) & stbi__bmask[h->size[c]]) == h->code[c]); - - // convert the id to a symbol - j->code_bits -= k; - j->code_buffer <<= k; - return h->values[c]; -} - -// bias[n] = (-1<code_bits < n) stbi__grow_buffer_unsafe(j); - - sgn = (stbi__int32)j->code_buffer >> 31; // sign bit is always in MSB - k = stbi_lrot(j->code_buffer, n); - STBI_ASSERT(n >= 0 && n < (int) (sizeof(stbi__bmask)/sizeof(*stbi__bmask))); - j->code_buffer = k & ~stbi__bmask[n]; - k &= stbi__bmask[n]; - j->code_bits -= n; - return k + (stbi__jbias[n] & ~sgn); -} - -// get some unsigned bits -stbi_inline static int stbi__jpeg_get_bits(stbi__jpeg *j, int n) -{ - unsigned int k; - if (j->code_bits < n) stbi__grow_buffer_unsafe(j); - k = stbi_lrot(j->code_buffer, n); - j->code_buffer = k & ~stbi__bmask[n]; - k &= stbi__bmask[n]; - j->code_bits -= n; - return k; -} - -stbi_inline static int stbi__jpeg_get_bit(stbi__jpeg *j) -{ - unsigned int k; - if (j->code_bits < 1) stbi__grow_buffer_unsafe(j); - k = j->code_buffer; - j->code_buffer <<= 1; - --j->code_bits; - return k & 0x80000000; -} - -// given a value that's at position X in the zigzag stream, -// where does it appear in the 8x8 matrix coded as row-major? -static stbi_uc stbi__jpeg_dezigzag[64+15] = -{ - 0, 1, 8, 16, 9, 2, 3, 10, - 17, 24, 32, 25, 18, 11, 4, 5, - 12, 19, 26, 33, 40, 48, 41, 34, - 27, 20, 13, 6, 7, 14, 21, 28, - 35, 42, 49, 56, 57, 50, 43, 36, - 29, 22, 15, 23, 30, 37, 44, 51, - 58, 59, 52, 45, 38, 31, 39, 46, - 53, 60, 61, 54, 47, 55, 62, 63, - // let corrupt input sample past end - 63, 63, 63, 63, 63, 63, 63, 63, - 63, 63, 63, 63, 63, 63, 63 -}; - -// decode one 64-entry block-- -static int stbi__jpeg_decode_block(stbi__jpeg *j, short data[64], stbi__huffman *hdc, stbi__huffman *hac, stbi__int16 *fac, int b, stbi_uc *dequant) -{ - int diff,dc,k; - int t; - - if (j->code_bits < 16) stbi__grow_buffer_unsafe(j); - t = stbi__jpeg_huff_decode(j, hdc); - if (t < 0) return stbi__err("bad huffman code","Corrupt JPEG"); - - // 0 all the ac values now so we can do it 32-bits at a time - memset(data,0,64*sizeof(data[0])); - - diff = t ? stbi__extend_receive(j, t) : 0; - dc = j->img_comp[b].dc_pred + diff; - j->img_comp[b].dc_pred = dc; - data[0] = (short) (dc * dequant[0]); - - // decode AC components, see JPEG spec - k = 1; - do { - unsigned int zig; - int c,r,s; - if (j->code_bits < 16) stbi__grow_buffer_unsafe(j); - c = (j->code_buffer >> (32 - FAST_BITS)) & ((1 << FAST_BITS)-1); - r = fac[c]; - if (r) { // fast-AC path - k += (r >> 4) & 15; // run - s = r & 15; // combined length - j->code_buffer <<= s; - j->code_bits -= s; - // decode into unzigzag'd location - zig = stbi__jpeg_dezigzag[k++]; - data[zig] = (short) ((r >> 8) * dequant[zig]); - } else { - int rs = stbi__jpeg_huff_decode(j, hac); - if (rs < 0) return stbi__err("bad huffman code","Corrupt JPEG"); - s = rs & 15; - r = rs >> 4; - if (s == 0) { - if (rs != 0xf0) break; // end block - k += 16; - } else { - k += r; - // decode into unzigzag'd location - zig = stbi__jpeg_dezigzag[k++]; - data[zig] = (short) (stbi__extend_receive(j,s) * dequant[zig]); - } - } - } while (k < 64); - return 1; -} - -static int stbi__jpeg_decode_block_prog_dc(stbi__jpeg *j, short data[64], stbi__huffman *hdc, int b) -{ - int diff,dc; - int t; - if (j->spec_end != 0) return stbi__err("can't merge dc and ac", "Corrupt JPEG"); - - if (j->code_bits < 16) stbi__grow_buffer_unsafe(j); - - if (j->succ_high == 0) { - // first scan for DC coefficient, must be first - memset(data,0,64*sizeof(data[0])); // 0 all the ac values now - t = stbi__jpeg_huff_decode(j, hdc); - diff = t ? stbi__extend_receive(j, t) : 0; - - dc = j->img_comp[b].dc_pred + diff; - j->img_comp[b].dc_pred = dc; - data[0] = (short) (dc << j->succ_low); - } else { - // refinement scan for DC coefficient - if (stbi__jpeg_get_bit(j)) - data[0] += (short) (1 << j->succ_low); - } - return 1; -} - -// @OPTIMIZE: store non-zigzagged during the decode passes, -// and only de-zigzag when dequantizing -static int stbi__jpeg_decode_block_prog_ac(stbi__jpeg *j, short data[64], stbi__huffman *hac, stbi__int16 *fac) -{ - int k; - if (j->spec_start == 0) return stbi__err("can't merge dc and ac", "Corrupt JPEG"); - - if (j->succ_high == 0) { - int shift = j->succ_low; - - if (j->eob_run) { - --j->eob_run; - return 1; - } - - k = j->spec_start; - do { - unsigned int zig; - int c,r,s; - if (j->code_bits < 16) stbi__grow_buffer_unsafe(j); - c = (j->code_buffer >> (32 - FAST_BITS)) & ((1 << FAST_BITS)-1); - r = fac[c]; - if (r) { // fast-AC path - k += (r >> 4) & 15; // run - s = r & 15; // combined length - j->code_buffer <<= s; - j->code_bits -= s; - zig = stbi__jpeg_dezigzag[k++]; - data[zig] = (short) ((r >> 8) << shift); - } else { - int rs = stbi__jpeg_huff_decode(j, hac); - if (rs < 0) return stbi__err("bad huffman code","Corrupt JPEG"); - s = rs & 15; - r = rs >> 4; - if (s == 0) { - if (r < 15) { - j->eob_run = (1 << r); - if (r) - j->eob_run += stbi__jpeg_get_bits(j, r); - --j->eob_run; - break; - } - k += 16; - } else { - k += r; - zig = stbi__jpeg_dezigzag[k++]; - data[zig] = (short) (stbi__extend_receive(j,s) << shift); - } - } - } while (k <= j->spec_end); - } else { - // refinement scan for these AC coefficients - - short bit = (short) (1 << j->succ_low); - - if (j->eob_run) { - --j->eob_run; - for (k = j->spec_start; k <= j->spec_end; ++k) { - short *p = &data[stbi__jpeg_dezigzag[k]]; - if (*p != 0) - if (stbi__jpeg_get_bit(j)) - if ((*p & bit)==0) { - if (*p > 0) - *p += bit; - else - *p -= bit; - } - } - } else { - k = j->spec_start; - do { - int r,s; - int rs = stbi__jpeg_huff_decode(j, hac); // @OPTIMIZE see if we can use the fast path here, advance-by-r is so slow, eh - if (rs < 0) return stbi__err("bad huffman code","Corrupt JPEG"); - s = rs & 15; - r = rs >> 4; - if (s == 0) { - if (r < 15) { - j->eob_run = (1 << r) - 1; - if (r) - j->eob_run += stbi__jpeg_get_bits(j, r); - r = 64; // force end of block - } else { - // r=15 s=0 should write 16 0s, so we just do - // a run of 15 0s and then write s (which is 0), - // so we don't have to do anything special here - } - } else { - if (s != 1) return stbi__err("bad huffman code", "Corrupt JPEG"); - // sign bit - if (stbi__jpeg_get_bit(j)) - s = bit; - else - s = -bit; - } - - // advance by r - while (k <= j->spec_end) { - short *p = &data[stbi__jpeg_dezigzag[k++]]; - if (*p != 0) { - if (stbi__jpeg_get_bit(j)) - if ((*p & bit)==0) { - if (*p > 0) - *p += bit; - else - *p -= bit; - } - } else { - if (r == 0) { - *p = (short) s; - break; - } - --r; - } - } - } while (k <= j->spec_end); - } - } - return 1; -} - -// take a -128..127 value and stbi__clamp it and convert to 0..255 -stbi_inline static stbi_uc stbi__clamp(int x) -{ - // trick to use a single test to catch both cases - if ((unsigned int) x > 255) { - if (x < 0) return 0; - if (x > 255) return 255; - } - return (stbi_uc) x; -} - -#define stbi__f2f(x) ((int) (((x) * 4096 + 0.5))) -#define stbi__fsh(x) ((x) << 12) - -// derived from jidctint -- DCT_ISLOW -#define STBI__IDCT_1D(s0,s1,s2,s3,s4,s5,s6,s7) \ - int t0,t1,t2,t3,p1,p2,p3,p4,p5,x0,x1,x2,x3; \ - p2 = s2; \ - p3 = s6; \ - p1 = (p2+p3) * stbi__f2f(0.5411961f); \ - t2 = p1 + p3*stbi__f2f(-1.847759065f); \ - t3 = p1 + p2*stbi__f2f( 0.765366865f); \ - p2 = s0; \ - p3 = s4; \ - t0 = stbi__fsh(p2+p3); \ - t1 = stbi__fsh(p2-p3); \ - x0 = t0+t3; \ - x3 = t0-t3; \ - x1 = t1+t2; \ - x2 = t1-t2; \ - t0 = s7; \ - t1 = s5; \ - t2 = s3; \ - t3 = s1; \ - p3 = t0+t2; \ - p4 = t1+t3; \ - p1 = t0+t3; \ - p2 = t1+t2; \ - p5 = (p3+p4)*stbi__f2f( 1.175875602f); \ - t0 = t0*stbi__f2f( 0.298631336f); \ - t1 = t1*stbi__f2f( 2.053119869f); \ - t2 = t2*stbi__f2f( 3.072711026f); \ - t3 = t3*stbi__f2f( 1.501321110f); \ - p1 = p5 + p1*stbi__f2f(-0.899976223f); \ - p2 = p5 + p2*stbi__f2f(-2.562915447f); \ - p3 = p3*stbi__f2f(-1.961570560f); \ - p4 = p4*stbi__f2f(-0.390180644f); \ - t3 += p1+p4; \ - t2 += p2+p3; \ - t1 += p2+p4; \ - t0 += p1+p3; - -static void stbi__idct_block(stbi_uc *out, int out_stride, short data[64]) -{ - int i,val[64],*v=val; - stbi_uc *o; - short *d = data; - - // columns - for (i=0; i < 8; ++i,++d, ++v) { - // if all zeroes, shortcut -- this avoids dequantizing 0s and IDCTing - if (d[ 8]==0 && d[16]==0 && d[24]==0 && d[32]==0 - && d[40]==0 && d[48]==0 && d[56]==0) { - // no shortcut 0 seconds - // (1|2|3|4|5|6|7)==0 0 seconds - // all separate -0.047 seconds - // 1 && 2|3 && 4|5 && 6|7: -0.047 seconds - int dcterm = d[0] << 2; - v[0] = v[8] = v[16] = v[24] = v[32] = v[40] = v[48] = v[56] = dcterm; - } else { - STBI__IDCT_1D(d[ 0],d[ 8],d[16],d[24],d[32],d[40],d[48],d[56]) - // constants scaled things up by 1<<12; let's bring them back - // down, but keep 2 extra bits of precision - x0 += 512; x1 += 512; x2 += 512; x3 += 512; - v[ 0] = (x0+t3) >> 10; - v[56] = (x0-t3) >> 10; - v[ 8] = (x1+t2) >> 10; - v[48] = (x1-t2) >> 10; - v[16] = (x2+t1) >> 10; - v[40] = (x2-t1) >> 10; - v[24] = (x3+t0) >> 10; - v[32] = (x3-t0) >> 10; - } - } - - for (i=0, v=val, o=out; i < 8; ++i,v+=8,o+=out_stride) { - // no fast case since the first 1D IDCT spread components out - STBI__IDCT_1D(v[0],v[1],v[2],v[3],v[4],v[5],v[6],v[7]) - // constants scaled things up by 1<<12, plus we had 1<<2 from first - // loop, plus horizontal and vertical each scale by sqrt(8) so together - // we've got an extra 1<<3, so 1<<17 total we need to remove. - // so we want to round that, which means adding 0.5 * 1<<17, - // aka 65536. Also, we'll end up with -128 to 127 that we want - // to encode as 0..255 by adding 128, so we'll add that before the shift - x0 += 65536 + (128<<17); - x1 += 65536 + (128<<17); - x2 += 65536 + (128<<17); - x3 += 65536 + (128<<17); - // tried computing the shifts into temps, or'ing the temps to see - // if any were out of range, but that was slower - o[0] = stbi__clamp((x0+t3) >> 17); - o[7] = stbi__clamp((x0-t3) >> 17); - o[1] = stbi__clamp((x1+t2) >> 17); - o[6] = stbi__clamp((x1-t2) >> 17); - o[2] = stbi__clamp((x2+t1) >> 17); - o[5] = stbi__clamp((x2-t1) >> 17); - o[3] = stbi__clamp((x3+t0) >> 17); - o[4] = stbi__clamp((x3-t0) >> 17); - } -} - -#ifdef STBI_SSE2 -// sse2 integer IDCT. not the fastest possible implementation but it -// produces bit-identical results to the generic C version so it's -// fully "transparent". -static void stbi__idct_simd(stbi_uc *out, int out_stride, short data[64]) -{ - // This is constructed to match our regular (generic) integer IDCT exactly. - __m128i row0, row1, row2, row3, row4, row5, row6, row7; - __m128i tmp; - - // dot product constant: even elems=x, odd elems=y - #define dct_const(x,y) _mm_setr_epi16((x),(y),(x),(y),(x),(y),(x),(y)) - - // out(0) = c0[even]*x + c0[odd]*y (c0, x, y 16-bit, out 32-bit) - // out(1) = c1[even]*x + c1[odd]*y - #define dct_rot(out0,out1, x,y,c0,c1) \ - __m128i c0##lo = _mm_unpacklo_epi16((x),(y)); \ - __m128i c0##hi = _mm_unpackhi_epi16((x),(y)); \ - __m128i out0##_l = _mm_madd_epi16(c0##lo, c0); \ - __m128i out0##_h = _mm_madd_epi16(c0##hi, c0); \ - __m128i out1##_l = _mm_madd_epi16(c0##lo, c1); \ - __m128i out1##_h = _mm_madd_epi16(c0##hi, c1) - - // out = in << 12 (in 16-bit, out 32-bit) - #define dct_widen(out, in) \ - __m128i out##_l = _mm_srai_epi32(_mm_unpacklo_epi16(_mm_setzero_si128(), (in)), 4); \ - __m128i out##_h = _mm_srai_epi32(_mm_unpackhi_epi16(_mm_setzero_si128(), (in)), 4) - - // wide add - #define dct_wadd(out, a, b) \ - __m128i out##_l = _mm_add_epi32(a##_l, b##_l); \ - __m128i out##_h = _mm_add_epi32(a##_h, b##_h) - - // wide sub - #define dct_wsub(out, a, b) \ - __m128i out##_l = _mm_sub_epi32(a##_l, b##_l); \ - __m128i out##_h = _mm_sub_epi32(a##_h, b##_h) - - // butterfly a/b, add bias, then shift by "s" and pack - #define dct_bfly32o(out0, out1, a,b,bias,s) \ - { \ - __m128i abiased_l = _mm_add_epi32(a##_l, bias); \ - __m128i abiased_h = _mm_add_epi32(a##_h, bias); \ - dct_wadd(sum, abiased, b); \ - dct_wsub(dif, abiased, b); \ - out0 = _mm_packs_epi32(_mm_srai_epi32(sum_l, s), _mm_srai_epi32(sum_h, s)); \ - out1 = _mm_packs_epi32(_mm_srai_epi32(dif_l, s), _mm_srai_epi32(dif_h, s)); \ - } - - // 8-bit interleave step (for transposes) - #define dct_interleave8(a, b) \ - tmp = a; \ - a = _mm_unpacklo_epi8(a, b); \ - b = _mm_unpackhi_epi8(tmp, b) - - // 16-bit interleave step (for transposes) - #define dct_interleave16(a, b) \ - tmp = a; \ - a = _mm_unpacklo_epi16(a, b); \ - b = _mm_unpackhi_epi16(tmp, b) - - #define dct_pass(bias,shift) \ - { \ - /* even part */ \ - dct_rot(t2e,t3e, row2,row6, rot0_0,rot0_1); \ - __m128i sum04 = _mm_add_epi16(row0, row4); \ - __m128i dif04 = _mm_sub_epi16(row0, row4); \ - dct_widen(t0e, sum04); \ - dct_widen(t1e, dif04); \ - dct_wadd(x0, t0e, t3e); \ - dct_wsub(x3, t0e, t3e); \ - dct_wadd(x1, t1e, t2e); \ - dct_wsub(x2, t1e, t2e); \ - /* odd part */ \ - dct_rot(y0o,y2o, row7,row3, rot2_0,rot2_1); \ - dct_rot(y1o,y3o, row5,row1, rot3_0,rot3_1); \ - __m128i sum17 = _mm_add_epi16(row1, row7); \ - __m128i sum35 = _mm_add_epi16(row3, row5); \ - dct_rot(y4o,y5o, sum17,sum35, rot1_0,rot1_1); \ - dct_wadd(x4, y0o, y4o); \ - dct_wadd(x5, y1o, y5o); \ - dct_wadd(x6, y2o, y5o); \ - dct_wadd(x7, y3o, y4o); \ - dct_bfly32o(row0,row7, x0,x7,bias,shift); \ - dct_bfly32o(row1,row6, x1,x6,bias,shift); \ - dct_bfly32o(row2,row5, x2,x5,bias,shift); \ - dct_bfly32o(row3,row4, x3,x4,bias,shift); \ - } - - __m128i rot0_0 = dct_const(stbi__f2f(0.5411961f), stbi__f2f(0.5411961f) + stbi__f2f(-1.847759065f)); - __m128i rot0_1 = dct_const(stbi__f2f(0.5411961f) + stbi__f2f( 0.765366865f), stbi__f2f(0.5411961f)); - __m128i rot1_0 = dct_const(stbi__f2f(1.175875602f) + stbi__f2f(-0.899976223f), stbi__f2f(1.175875602f)); - __m128i rot1_1 = dct_const(stbi__f2f(1.175875602f), stbi__f2f(1.175875602f) + stbi__f2f(-2.562915447f)); - __m128i rot2_0 = dct_const(stbi__f2f(-1.961570560f) + stbi__f2f( 0.298631336f), stbi__f2f(-1.961570560f)); - __m128i rot2_1 = dct_const(stbi__f2f(-1.961570560f), stbi__f2f(-1.961570560f) + stbi__f2f( 3.072711026f)); - __m128i rot3_0 = dct_const(stbi__f2f(-0.390180644f) + stbi__f2f( 2.053119869f), stbi__f2f(-0.390180644f)); - __m128i rot3_1 = dct_const(stbi__f2f(-0.390180644f), stbi__f2f(-0.390180644f) + stbi__f2f( 1.501321110f)); - - // rounding biases in column/row passes, see stbi__idct_block for explanation. - __m128i bias_0 = _mm_set1_epi32(512); - __m128i bias_1 = _mm_set1_epi32(65536 + (128<<17)); - - // load - row0 = _mm_load_si128((const __m128i *) (data + 0*8)); - row1 = _mm_load_si128((const __m128i *) (data + 1*8)); - row2 = _mm_load_si128((const __m128i *) (data + 2*8)); - row3 = _mm_load_si128((const __m128i *) (data + 3*8)); - row4 = _mm_load_si128((const __m128i *) (data + 4*8)); - row5 = _mm_load_si128((const __m128i *) (data + 5*8)); - row6 = _mm_load_si128((const __m128i *) (data + 6*8)); - row7 = _mm_load_si128((const __m128i *) (data + 7*8)); - - // column pass - dct_pass(bias_0, 10); - - { - // 16bit 8x8 transpose pass 1 - dct_interleave16(row0, row4); - dct_interleave16(row1, row5); - dct_interleave16(row2, row6); - dct_interleave16(row3, row7); - - // transpose pass 2 - dct_interleave16(row0, row2); - dct_interleave16(row1, row3); - dct_interleave16(row4, row6); - dct_interleave16(row5, row7); - - // transpose pass 3 - dct_interleave16(row0, row1); - dct_interleave16(row2, row3); - dct_interleave16(row4, row5); - dct_interleave16(row6, row7); - } - - // row pass - dct_pass(bias_1, 17); - - { - // pack - __m128i p0 = _mm_packus_epi16(row0, row1); // a0a1a2a3...a7b0b1b2b3...b7 - __m128i p1 = _mm_packus_epi16(row2, row3); - __m128i p2 = _mm_packus_epi16(row4, row5); - __m128i p3 = _mm_packus_epi16(row6, row7); - - // 8bit 8x8 transpose pass 1 - dct_interleave8(p0, p2); // a0e0a1e1... - dct_interleave8(p1, p3); // c0g0c1g1... - - // transpose pass 2 - dct_interleave8(p0, p1); // a0c0e0g0... - dct_interleave8(p2, p3); // b0d0f0h0... - - // transpose pass 3 - dct_interleave8(p0, p2); // a0b0c0d0... - dct_interleave8(p1, p3); // a4b4c4d4... - - // store - _mm_storel_epi64((__m128i *) out, p0); out += out_stride; - _mm_storel_epi64((__m128i *) out, _mm_shuffle_epi32(p0, 0x4e)); out += out_stride; - _mm_storel_epi64((__m128i *) out, p2); out += out_stride; - _mm_storel_epi64((__m128i *) out, _mm_shuffle_epi32(p2, 0x4e)); out += out_stride; - _mm_storel_epi64((__m128i *) out, p1); out += out_stride; - _mm_storel_epi64((__m128i *) out, _mm_shuffle_epi32(p1, 0x4e)); out += out_stride; - _mm_storel_epi64((__m128i *) out, p3); out += out_stride; - _mm_storel_epi64((__m128i *) out, _mm_shuffle_epi32(p3, 0x4e)); - } - -#undef dct_const -#undef dct_rot -#undef dct_widen -#undef dct_wadd -#undef dct_wsub -#undef dct_bfly32o -#undef dct_interleave8 -#undef dct_interleave16 -#undef dct_pass -} - -#endif // STBI_SSE2 - -#ifdef STBI_NEON - -// NEON integer IDCT. should produce bit-identical -// results to the generic C version. -static void stbi__idct_simd(stbi_uc *out, int out_stride, short data[64]) -{ - int16x8_t row0, row1, row2, row3, row4, row5, row6, row7; - - int16x4_t rot0_0 = vdup_n_s16(stbi__f2f(0.5411961f)); - int16x4_t rot0_1 = vdup_n_s16(stbi__f2f(-1.847759065f)); - int16x4_t rot0_2 = vdup_n_s16(stbi__f2f( 0.765366865f)); - int16x4_t rot1_0 = vdup_n_s16(stbi__f2f( 1.175875602f)); - int16x4_t rot1_1 = vdup_n_s16(stbi__f2f(-0.899976223f)); - int16x4_t rot1_2 = vdup_n_s16(stbi__f2f(-2.562915447f)); - int16x4_t rot2_0 = vdup_n_s16(stbi__f2f(-1.961570560f)); - int16x4_t rot2_1 = vdup_n_s16(stbi__f2f(-0.390180644f)); - int16x4_t rot3_0 = vdup_n_s16(stbi__f2f( 0.298631336f)); - int16x4_t rot3_1 = vdup_n_s16(stbi__f2f( 2.053119869f)); - int16x4_t rot3_2 = vdup_n_s16(stbi__f2f( 3.072711026f)); - int16x4_t rot3_3 = vdup_n_s16(stbi__f2f( 1.501321110f)); - -#define dct_long_mul(out, inq, coeff) \ - int32x4_t out##_l = vmull_s16(vget_low_s16(inq), coeff); \ - int32x4_t out##_h = vmull_s16(vget_high_s16(inq), coeff) - -#define dct_long_mac(out, acc, inq, coeff) \ - int32x4_t out##_l = vmlal_s16(acc##_l, vget_low_s16(inq), coeff); \ - int32x4_t out##_h = vmlal_s16(acc##_h, vget_high_s16(inq), coeff) - -#define dct_widen(out, inq) \ - int32x4_t out##_l = vshll_n_s16(vget_low_s16(inq), 12); \ - int32x4_t out##_h = vshll_n_s16(vget_high_s16(inq), 12) - -// wide add -#define dct_wadd(out, a, b) \ - int32x4_t out##_l = vaddq_s32(a##_l, b##_l); \ - int32x4_t out##_h = vaddq_s32(a##_h, b##_h) - -// wide sub -#define dct_wsub(out, a, b) \ - int32x4_t out##_l = vsubq_s32(a##_l, b##_l); \ - int32x4_t out##_h = vsubq_s32(a##_h, b##_h) - -// butterfly a/b, then shift using "shiftop" by "s" and pack -#define dct_bfly32o(out0,out1, a,b,shiftop,s) \ - { \ - dct_wadd(sum, a, b); \ - dct_wsub(dif, a, b); \ - out0 = vcombine_s16(shiftop(sum_l, s), shiftop(sum_h, s)); \ - out1 = vcombine_s16(shiftop(dif_l, s), shiftop(dif_h, s)); \ - } - -#define dct_pass(shiftop, shift) \ - { \ - /* even part */ \ - int16x8_t sum26 = vaddq_s16(row2, row6); \ - dct_long_mul(p1e, sum26, rot0_0); \ - dct_long_mac(t2e, p1e, row6, rot0_1); \ - dct_long_mac(t3e, p1e, row2, rot0_2); \ - int16x8_t sum04 = vaddq_s16(row0, row4); \ - int16x8_t dif04 = vsubq_s16(row0, row4); \ - dct_widen(t0e, sum04); \ - dct_widen(t1e, dif04); \ - dct_wadd(x0, t0e, t3e); \ - dct_wsub(x3, t0e, t3e); \ - dct_wadd(x1, t1e, t2e); \ - dct_wsub(x2, t1e, t2e); \ - /* odd part */ \ - int16x8_t sum15 = vaddq_s16(row1, row5); \ - int16x8_t sum17 = vaddq_s16(row1, row7); \ - int16x8_t sum35 = vaddq_s16(row3, row5); \ - int16x8_t sum37 = vaddq_s16(row3, row7); \ - int16x8_t sumodd = vaddq_s16(sum17, sum35); \ - dct_long_mul(p5o, sumodd, rot1_0); \ - dct_long_mac(p1o, p5o, sum17, rot1_1); \ - dct_long_mac(p2o, p5o, sum35, rot1_2); \ - dct_long_mul(p3o, sum37, rot2_0); \ - dct_long_mul(p4o, sum15, rot2_1); \ - dct_wadd(sump13o, p1o, p3o); \ - dct_wadd(sump24o, p2o, p4o); \ - dct_wadd(sump23o, p2o, p3o); \ - dct_wadd(sump14o, p1o, p4o); \ - dct_long_mac(x4, sump13o, row7, rot3_0); \ - dct_long_mac(x5, sump24o, row5, rot3_1); \ - dct_long_mac(x6, sump23o, row3, rot3_2); \ - dct_long_mac(x7, sump14o, row1, rot3_3); \ - dct_bfly32o(row0,row7, x0,x7,shiftop,shift); \ - dct_bfly32o(row1,row6, x1,x6,shiftop,shift); \ - dct_bfly32o(row2,row5, x2,x5,shiftop,shift); \ - dct_bfly32o(row3,row4, x3,x4,shiftop,shift); \ - } - - // load - row0 = vld1q_s16(data + 0*8); - row1 = vld1q_s16(data + 1*8); - row2 = vld1q_s16(data + 2*8); - row3 = vld1q_s16(data + 3*8); - row4 = vld1q_s16(data + 4*8); - row5 = vld1q_s16(data + 5*8); - row6 = vld1q_s16(data + 6*8); - row7 = vld1q_s16(data + 7*8); - - // add DC bias - row0 = vaddq_s16(row0, vsetq_lane_s16(1024, vdupq_n_s16(0), 0)); - - // column pass - dct_pass(vrshrn_n_s32, 10); - - // 16bit 8x8 transpose - { -// these three map to a single VTRN.16, VTRN.32, and VSWP, respectively. -// whether compilers actually get this is another story, sadly. -#define dct_trn16(x, y) { int16x8x2_t t = vtrnq_s16(x, y); x = t.val[0]; y = t.val[1]; } -#define dct_trn32(x, y) { int32x4x2_t t = vtrnq_s32(vreinterpretq_s32_s16(x), vreinterpretq_s32_s16(y)); x = vreinterpretq_s16_s32(t.val[0]); y = vreinterpretq_s16_s32(t.val[1]); } -#define dct_trn64(x, y) { int16x8_t x0 = x; int16x8_t y0 = y; x = vcombine_s16(vget_low_s16(x0), vget_low_s16(y0)); y = vcombine_s16(vget_high_s16(x0), vget_high_s16(y0)); } - - // pass 1 - dct_trn16(row0, row1); // a0b0a2b2a4b4a6b6 - dct_trn16(row2, row3); - dct_trn16(row4, row5); - dct_trn16(row6, row7); - - // pass 2 - dct_trn32(row0, row2); // a0b0c0d0a4b4c4d4 - dct_trn32(row1, row3); - dct_trn32(row4, row6); - dct_trn32(row5, row7); - - // pass 3 - dct_trn64(row0, row4); // a0b0c0d0e0f0g0h0 - dct_trn64(row1, row5); - dct_trn64(row2, row6); - dct_trn64(row3, row7); - -#undef dct_trn16 -#undef dct_trn32 -#undef dct_trn64 - } - - // row pass - // vrshrn_n_s32 only supports shifts up to 16, we need - // 17. so do a non-rounding shift of 16 first then follow - // up with a rounding shift by 1. - dct_pass(vshrn_n_s32, 16); - - { - // pack and round - uint8x8_t p0 = vqrshrun_n_s16(row0, 1); - uint8x8_t p1 = vqrshrun_n_s16(row1, 1); - uint8x8_t p2 = vqrshrun_n_s16(row2, 1); - uint8x8_t p3 = vqrshrun_n_s16(row3, 1); - uint8x8_t p4 = vqrshrun_n_s16(row4, 1); - uint8x8_t p5 = vqrshrun_n_s16(row5, 1); - uint8x8_t p6 = vqrshrun_n_s16(row6, 1); - uint8x8_t p7 = vqrshrun_n_s16(row7, 1); - - // again, these can translate into one instruction, but often don't. -#define dct_trn8_8(x, y) { uint8x8x2_t t = vtrn_u8(x, y); x = t.val[0]; y = t.val[1]; } -#define dct_trn8_16(x, y) { uint16x4x2_t t = vtrn_u16(vreinterpret_u16_u8(x), vreinterpret_u16_u8(y)); x = vreinterpret_u8_u16(t.val[0]); y = vreinterpret_u8_u16(t.val[1]); } -#define dct_trn8_32(x, y) { uint32x2x2_t t = vtrn_u32(vreinterpret_u32_u8(x), vreinterpret_u32_u8(y)); x = vreinterpret_u8_u32(t.val[0]); y = vreinterpret_u8_u32(t.val[1]); } - - // sadly can't use interleaved stores here since we only write - // 8 bytes to each scan line! - - // 8x8 8-bit transpose pass 1 - dct_trn8_8(p0, p1); - dct_trn8_8(p2, p3); - dct_trn8_8(p4, p5); - dct_trn8_8(p6, p7); - - // pass 2 - dct_trn8_16(p0, p2); - dct_trn8_16(p1, p3); - dct_trn8_16(p4, p6); - dct_trn8_16(p5, p7); - - // pass 3 - dct_trn8_32(p0, p4); - dct_trn8_32(p1, p5); - dct_trn8_32(p2, p6); - dct_trn8_32(p3, p7); - - // store - vst1_u8(out, p0); out += out_stride; - vst1_u8(out, p1); out += out_stride; - vst1_u8(out, p2); out += out_stride; - vst1_u8(out, p3); out += out_stride; - vst1_u8(out, p4); out += out_stride; - vst1_u8(out, p5); out += out_stride; - vst1_u8(out, p6); out += out_stride; - vst1_u8(out, p7); - -#undef dct_trn8_8 -#undef dct_trn8_16 -#undef dct_trn8_32 - } - -#undef dct_long_mul -#undef dct_long_mac -#undef dct_widen -#undef dct_wadd -#undef dct_wsub -#undef dct_bfly32o -#undef dct_pass -} - -#endif // STBI_NEON - -#define STBI__MARKER_none 0xff -// if there's a pending marker from the entropy stream, return that -// otherwise, fetch from the stream and get a marker. if there's no -// marker, return 0xff, which is never a valid marker value -static stbi_uc stbi__get_marker(stbi__jpeg *j) -{ - stbi_uc x; - if (j->marker != STBI__MARKER_none) { x = j->marker; j->marker = STBI__MARKER_none; return x; } - x = stbi__get8(j->s); - if (x != 0xff) return STBI__MARKER_none; - while (x == 0xff) - x = stbi__get8(j->s); - return x; -} - -// in each scan, we'll have scan_n components, and the order -// of the components is specified by order[] -#define STBI__RESTART(x) ((x) >= 0xd0 && (x) <= 0xd7) - -// after a restart interval, stbi__jpeg_reset the entropy decoder and -// the dc prediction -static void stbi__jpeg_reset(stbi__jpeg *j) -{ - j->code_bits = 0; - j->code_buffer = 0; - j->nomore = 0; - j->img_comp[0].dc_pred = j->img_comp[1].dc_pred = j->img_comp[2].dc_pred = 0; - j->marker = STBI__MARKER_none; - j->todo = j->restart_interval ? j->restart_interval : 0x7fffffff; - j->eob_run = 0; - // no more than 1<<31 MCUs if no restart_interal? that's plenty safe, - // since we don't even allow 1<<30 pixels -} - -static int stbi__parse_entropy_coded_data(stbi__jpeg *z) -{ - stbi__jpeg_reset(z); - if (!z->progressive) { - if (z->scan_n == 1) { - int i,j; - STBI_SIMD_ALIGN(short, data[64]); - int n = z->order[0]; - // non-interleaved data, we just need to process one block at a time, - // in trivial scanline order - // number of blocks to do just depends on how many actual "pixels" this - // component has, independent of interleaved MCU blocking and such - int w = (z->img_comp[n].x+7) >> 3; - int h = (z->img_comp[n].y+7) >> 3; - for (j=0; j < h; ++j) { - for (i=0; i < w; ++i) { - int ha = z->img_comp[n].ha; - if (!stbi__jpeg_decode_block(z, data, z->huff_dc+z->img_comp[n].hd, z->huff_ac+ha, z->fast_ac[ha], n, z->dequant[z->img_comp[n].tq])) return 0; - z->idct_block_kernel(z->img_comp[n].data+z->img_comp[n].w2*j*8+i*8, z->img_comp[n].w2, data); - // every data block is an MCU, so countdown the restart interval - if (--z->todo <= 0) { - if (z->code_bits < 24) stbi__grow_buffer_unsafe(z); - // if it's NOT a restart, then just bail, so we get corrupt data - // rather than no data - if (!STBI__RESTART(z->marker)) return 1; - stbi__jpeg_reset(z); - } - } - } - return 1; - } else { // interleaved - int i,j,k,x,y; - STBI_SIMD_ALIGN(short, data[64]); - for (j=0; j < z->img_mcu_y; ++j) { - for (i=0; i < z->img_mcu_x; ++i) { - // scan an interleaved mcu... process scan_n components in order - for (k=0; k < z->scan_n; ++k) { - int n = z->order[k]; - // scan out an mcu's worth of this component; that's just determined - // by the basic H and V specified for the component - for (y=0; y < z->img_comp[n].v; ++y) { - for (x=0; x < z->img_comp[n].h; ++x) { - int x2 = (i*z->img_comp[n].h + x)*8; - int y2 = (j*z->img_comp[n].v + y)*8; - int ha = z->img_comp[n].ha; - if (!stbi__jpeg_decode_block(z, data, z->huff_dc+z->img_comp[n].hd, z->huff_ac+ha, z->fast_ac[ha], n, z->dequant[z->img_comp[n].tq])) return 0; - z->idct_block_kernel(z->img_comp[n].data+z->img_comp[n].w2*y2+x2, z->img_comp[n].w2, data); - } - } - } - // after all interleaved components, that's an interleaved MCU, - // so now count down the restart interval - if (--z->todo <= 0) { - if (z->code_bits < 24) stbi__grow_buffer_unsafe(z); - if (!STBI__RESTART(z->marker)) return 1; - stbi__jpeg_reset(z); - } - } - } - return 1; - } - } else { - if (z->scan_n == 1) { - int i,j; - int n = z->order[0]; - // non-interleaved data, we just need to process one block at a time, - // in trivial scanline order - // number of blocks to do just depends on how many actual "pixels" this - // component has, independent of interleaved MCU blocking and such - int w = (z->img_comp[n].x+7) >> 3; - int h = (z->img_comp[n].y+7) >> 3; - for (j=0; j < h; ++j) { - for (i=0; i < w; ++i) { - short *data = z->img_comp[n].coeff + 64 * (i + j * z->img_comp[n].coeff_w); - if (z->spec_start == 0) { - if (!stbi__jpeg_decode_block_prog_dc(z, data, &z->huff_dc[z->img_comp[n].hd], n)) - return 0; - } else { - int ha = z->img_comp[n].ha; - if (!stbi__jpeg_decode_block_prog_ac(z, data, &z->huff_ac[ha], z->fast_ac[ha])) - return 0; - } - // every data block is an MCU, so countdown the restart interval - if (--z->todo <= 0) { - if (z->code_bits < 24) stbi__grow_buffer_unsafe(z); - if (!STBI__RESTART(z->marker)) return 1; - stbi__jpeg_reset(z); - } - } - } - return 1; - } else { // interleaved - int i,j,k,x,y; - for (j=0; j < z->img_mcu_y; ++j) { - for (i=0; i < z->img_mcu_x; ++i) { - // scan an interleaved mcu... process scan_n components in order - for (k=0; k < z->scan_n; ++k) { - int n = z->order[k]; - // scan out an mcu's worth of this component; that's just determined - // by the basic H and V specified for the component - for (y=0; y < z->img_comp[n].v; ++y) { - for (x=0; x < z->img_comp[n].h; ++x) { - int x2 = (i*z->img_comp[n].h + x); - int y2 = (j*z->img_comp[n].v + y); - short *data = z->img_comp[n].coeff + 64 * (x2 + y2 * z->img_comp[n].coeff_w); - if (!stbi__jpeg_decode_block_prog_dc(z, data, &z->huff_dc[z->img_comp[n].hd], n)) - return 0; - } - } - } - // after all interleaved components, that's an interleaved MCU, - // so now count down the restart interval - if (--z->todo <= 0) { - if (z->code_bits < 24) stbi__grow_buffer_unsafe(z); - if (!STBI__RESTART(z->marker)) return 1; - stbi__jpeg_reset(z); - } - } - } - return 1; - } - } -} - -static void stbi__jpeg_dequantize(short *data, stbi_uc *dequant) -{ - int i; - for (i=0; i < 64; ++i) - data[i] *= dequant[i]; -} - -static void stbi__jpeg_finish(stbi__jpeg *z) -{ - if (z->progressive) { - // dequantize and idct the data - int i,j,n; - for (n=0; n < z->s->img_n; ++n) { - int w = (z->img_comp[n].x+7) >> 3; - int h = (z->img_comp[n].y+7) >> 3; - for (j=0; j < h; ++j) { - for (i=0; i < w; ++i) { - short *data = z->img_comp[n].coeff + 64 * (i + j * z->img_comp[n].coeff_w); - stbi__jpeg_dequantize(data, z->dequant[z->img_comp[n].tq]); - z->idct_block_kernel(z->img_comp[n].data+z->img_comp[n].w2*j*8+i*8, z->img_comp[n].w2, data); - } - } - } - } -} - -static int stbi__process_marker(stbi__jpeg *z, int m) -{ - int L; - switch (m) { - case STBI__MARKER_none: // no marker found - return stbi__err("expected marker","Corrupt JPEG"); - - case 0xDD: // DRI - specify restart interval - if (stbi__get16be(z->s) != 4) return stbi__err("bad DRI len","Corrupt JPEG"); - z->restart_interval = stbi__get16be(z->s); - return 1; - - case 0xDB: // DQT - define quantization table - L = stbi__get16be(z->s)-2; - while (L > 0) { - int q = stbi__get8(z->s); - int p = q >> 4; - int t = q & 15,i; - if (p != 0) return stbi__err("bad DQT type","Corrupt JPEG"); - if (t > 3) return stbi__err("bad DQT table","Corrupt JPEG"); - for (i=0; i < 64; ++i) - z->dequant[t][stbi__jpeg_dezigzag[i]] = stbi__get8(z->s); - L -= 65; - } - return L==0; - - case 0xC4: // DHT - define huffman table - L = stbi__get16be(z->s)-2; - while (L > 0) { - stbi_uc *v; - int sizes[16],i,n=0; - int q = stbi__get8(z->s); - int tc = q >> 4; - int th = q & 15; - if (tc > 1 || th > 3) return stbi__err("bad DHT header","Corrupt JPEG"); - for (i=0; i < 16; ++i) { - sizes[i] = stbi__get8(z->s); - n += sizes[i]; - } - L -= 17; - if (tc == 0) { - if (!stbi__build_huffman(z->huff_dc+th, sizes)) return 0; - v = z->huff_dc[th].values; - } else { - if (!stbi__build_huffman(z->huff_ac+th, sizes)) return 0; - v = z->huff_ac[th].values; - } - for (i=0; i < n; ++i) - v[i] = stbi__get8(z->s); - if (tc != 0) - stbi__build_fast_ac(z->fast_ac[th], z->huff_ac + th); - L -= n; - } - return L==0; - } - // check for comment block or APP blocks - if ((m >= 0xE0 && m <= 0xEF) || m == 0xFE) { - stbi__skip(z->s, stbi__get16be(z->s)-2); - return 1; - } - return 0; -} - -// after we see SOS -static int stbi__process_scan_header(stbi__jpeg *z) -{ - int i; - int Ls = stbi__get16be(z->s); - z->scan_n = stbi__get8(z->s); - if (z->scan_n < 1 || z->scan_n > 4 || z->scan_n > (int) z->s->img_n) return stbi__err("bad SOS component count","Corrupt JPEG"); - if (Ls != 6+2*z->scan_n) return stbi__err("bad SOS len","Corrupt JPEG"); - for (i=0; i < z->scan_n; ++i) { - int id = stbi__get8(z->s), which; - int q = stbi__get8(z->s); - for (which = 0; which < z->s->img_n; ++which) - if (z->img_comp[which].id == id) - break; - if (which == z->s->img_n) return 0; // no match - z->img_comp[which].hd = q >> 4; if (z->img_comp[which].hd > 3) return stbi__err("bad DC huff","Corrupt JPEG"); - z->img_comp[which].ha = q & 15; if (z->img_comp[which].ha > 3) return stbi__err("bad AC huff","Corrupt JPEG"); - z->order[i] = which; - } - - { - int aa; - z->spec_start = stbi__get8(z->s); - z->spec_end = stbi__get8(z->s); // should be 63, but might be 0 - aa = stbi__get8(z->s); - z->succ_high = (aa >> 4); - z->succ_low = (aa & 15); - if (z->progressive) { - if (z->spec_start > 63 || z->spec_end > 63 || z->spec_start > z->spec_end || z->succ_high > 13 || z->succ_low > 13) - return stbi__err("bad SOS", "Corrupt JPEG"); - } else { - if (z->spec_start != 0) return stbi__err("bad SOS","Corrupt JPEG"); - if (z->succ_high != 0 || z->succ_low != 0) return stbi__err("bad SOS","Corrupt JPEG"); - z->spec_end = 63; - } - } - - return 1; -} - -static int stbi__process_frame_header(stbi__jpeg *z, int scan) -{ - stbi__context *s = z->s; - int Lf,p,i,q, h_max=1,v_max=1,c; - Lf = stbi__get16be(s); if (Lf < 11) return stbi__err("bad SOF len","Corrupt JPEG"); // JPEG - p = stbi__get8(s); if (p != 8) return stbi__err("only 8-bit","JPEG format not supported: 8-bit only"); // JPEG baseline - s->img_y = stbi__get16be(s); if (s->img_y == 0) return stbi__err("no header height", "JPEG format not supported: delayed height"); // Legal, but we don't handle it--but neither does IJG - s->img_x = stbi__get16be(s); if (s->img_x == 0) return stbi__err("0 width","Corrupt JPEG"); // JPEG requires - c = stbi__get8(s); - if (c != 3 && c != 1) return stbi__err("bad component count","Corrupt JPEG"); // JFIF requires - s->img_n = c; - for (i=0; i < c; ++i) { - z->img_comp[i].data = NULL; - z->img_comp[i].linebuf = NULL; - } - - if (Lf != 8+3*s->img_n) return stbi__err("bad SOF len","Corrupt JPEG"); - - z->rgb = 0; - for (i=0; i < s->img_n; ++i) { - static unsigned char rgb[3] = { 'R', 'G', 'B' }; - z->img_comp[i].id = stbi__get8(s); - if (z->img_comp[i].id != i+1) // JFIF requires - if (z->img_comp[i].id != i) { // some version of jpegtran outputs non-JFIF-compliant files! - // somethings output this (see http://fileformats.archiveteam.org/wiki/JPEG#Color_format) - if (z->img_comp[i].id != rgb[i]) - return stbi__err("bad component ID","Corrupt JPEG"); - ++z->rgb; - } - q = stbi__get8(s); - z->img_comp[i].h = (q >> 4); if (!z->img_comp[i].h || z->img_comp[i].h > 4) return stbi__err("bad H","Corrupt JPEG"); - z->img_comp[i].v = q & 15; if (!z->img_comp[i].v || z->img_comp[i].v > 4) return stbi__err("bad V","Corrupt JPEG"); - z->img_comp[i].tq = stbi__get8(s); if (z->img_comp[i].tq > 3) return stbi__err("bad TQ","Corrupt JPEG"); - } - - if (scan != STBI__SCAN_load) return 1; - - if ((1 << 30) / s->img_x / s->img_n < s->img_y) return stbi__err("too large", "Image too large to decode"); - - for (i=0; i < s->img_n; ++i) { - if (z->img_comp[i].h > h_max) h_max = z->img_comp[i].h; - if (z->img_comp[i].v > v_max) v_max = z->img_comp[i].v; - } - - // compute interleaved mcu info - z->img_h_max = h_max; - z->img_v_max = v_max; - z->img_mcu_w = h_max * 8; - z->img_mcu_h = v_max * 8; - z->img_mcu_x = (s->img_x + z->img_mcu_w-1) / z->img_mcu_w; - z->img_mcu_y = (s->img_y + z->img_mcu_h-1) / z->img_mcu_h; - - for (i=0; i < s->img_n; ++i) { - // number of effective pixels (e.g. for non-interleaved MCU) - z->img_comp[i].x = (s->img_x * z->img_comp[i].h + h_max-1) / h_max; - z->img_comp[i].y = (s->img_y * z->img_comp[i].v + v_max-1) / v_max; - // to simplify generation, we'll allocate enough memory to decode - // the bogus oversized data from using interleaved MCUs and their - // big blocks (e.g. a 16x16 iMCU on an image of width 33); we won't - // discard the extra data until colorspace conversion - z->img_comp[i].w2 = z->img_mcu_x * z->img_comp[i].h * 8; - z->img_comp[i].h2 = z->img_mcu_y * z->img_comp[i].v * 8; - z->img_comp[i].raw_data = stbi__malloc(z->img_comp[i].w2 * z->img_comp[i].h2+15); - - if (z->img_comp[i].raw_data == NULL) { - for(--i; i >= 0; --i) { - STBI_FREE(z->img_comp[i].raw_data); - z->img_comp[i].raw_data = NULL; - } - return stbi__err("outofmem", "Out of memory"); - } - // align blocks for idct using mmx/sse - z->img_comp[i].data = (stbi_uc*) (((size_t) z->img_comp[i].raw_data + 15) & ~15); - z->img_comp[i].linebuf = NULL; - if (z->progressive) { - z->img_comp[i].coeff_w = (z->img_comp[i].w2 + 7) >> 3; - z->img_comp[i].coeff_h = (z->img_comp[i].h2 + 7) >> 3; - z->img_comp[i].raw_coeff = STBI_MALLOC(z->img_comp[i].coeff_w * z->img_comp[i].coeff_h * 64 * sizeof(short) + 15); - z->img_comp[i].coeff = (short*) (((size_t) z->img_comp[i].raw_coeff + 15) & ~15); - } else { - z->img_comp[i].coeff = 0; - z->img_comp[i].raw_coeff = 0; - } - } - - return 1; -} - -// use comparisons since in some cases we handle more than one case (e.g. SOF) -#define stbi__DNL(x) ((x) == 0xdc) -#define stbi__SOI(x) ((x) == 0xd8) -#define stbi__EOI(x) ((x) == 0xd9) -#define stbi__SOF(x) ((x) == 0xc0 || (x) == 0xc1 || (x) == 0xc2) -#define stbi__SOS(x) ((x) == 0xda) - -#define stbi__SOF_progressive(x) ((x) == 0xc2) - -static int stbi__decode_jpeg_header(stbi__jpeg *z, int scan) -{ - int m; - z->marker = STBI__MARKER_none; // initialize cached marker to empty - m = stbi__get_marker(z); - if (!stbi__SOI(m)) return stbi__err("no SOI","Corrupt JPEG"); - if (scan == STBI__SCAN_type) return 1; - m = stbi__get_marker(z); - while (!stbi__SOF(m)) { - if (!stbi__process_marker(z,m)) return 0; - m = stbi__get_marker(z); - while (m == STBI__MARKER_none) { - // some files have extra padding after their blocks, so ok, we'll scan - if (stbi__at_eof(z->s)) return stbi__err("no SOF", "Corrupt JPEG"); - m = stbi__get_marker(z); - } - } - z->progressive = stbi__SOF_progressive(m); - if (!stbi__process_frame_header(z, scan)) return 0; - return 1; -} - -// decode image to YCbCr format -static int stbi__decode_jpeg_image(stbi__jpeg *j) -{ - int m; - for (m = 0; m < 4; m++) { - j->img_comp[m].raw_data = NULL; - j->img_comp[m].raw_coeff = NULL; - } - j->restart_interval = 0; - if (!stbi__decode_jpeg_header(j, STBI__SCAN_load)) return 0; - m = stbi__get_marker(j); - while (!stbi__EOI(m)) { - if (stbi__SOS(m)) { - if (!stbi__process_scan_header(j)) return 0; - if (!stbi__parse_entropy_coded_data(j)) return 0; - if (j->marker == STBI__MARKER_none ) { - // handle 0s at the end of image data from IP Kamera 9060 - while (!stbi__at_eof(j->s)) { - int x = stbi__get8(j->s); - if (x == 255) { - j->marker = stbi__get8(j->s); - break; - } else if (x != 0) { - return stbi__err("junk before marker", "Corrupt JPEG"); - } - } - // if we reach eof without hitting a marker, stbi__get_marker() below will fail and we'll eventually return 0 - } - } else { - if (!stbi__process_marker(j, m)) return 0; - } - m = stbi__get_marker(j); - } - if (j->progressive) - stbi__jpeg_finish(j); - return 1; -} - -// static jfif-centered resampling (across block boundaries) - -typedef stbi_uc *(*resample_row_func)(stbi_uc *out, stbi_uc *in0, stbi_uc *in1, - int w, int hs); - -#define stbi__div4(x) ((stbi_uc) ((x) >> 2)) - -static stbi_uc *resample_row_1(stbi_uc *out, stbi_uc *in_near, stbi_uc *in_far, int w, int hs) -{ - STBI_NOTUSED(out); - STBI_NOTUSED(in_far); - STBI_NOTUSED(w); - STBI_NOTUSED(hs); - return in_near; -} - -static stbi_uc* stbi__resample_row_v_2(stbi_uc *out, stbi_uc *in_near, stbi_uc *in_far, int w, int hs) -{ - // need to generate two samples vertically for every one in input - int i; - STBI_NOTUSED(hs); - for (i=0; i < w; ++i) - out[i] = stbi__div4(3*in_near[i] + in_far[i] + 2); - return out; -} - -static stbi_uc* stbi__resample_row_h_2(stbi_uc *out, stbi_uc *in_near, stbi_uc *in_far, int w, int hs) -{ - // need to generate two samples horizontally for every one in input - int i; - stbi_uc *input = in_near; - - if (w == 1) { - // if only one sample, can't do any interpolation - out[0] = out[1] = input[0]; - return out; - } - - out[0] = input[0]; - out[1] = stbi__div4(input[0]*3 + input[1] + 2); - for (i=1; i < w-1; ++i) { - int n = 3*input[i]+2; - out[i*2+0] = stbi__div4(n+input[i-1]); - out[i*2+1] = stbi__div4(n+input[i+1]); - } - out[i*2+0] = stbi__div4(input[w-2]*3 + input[w-1] + 2); - out[i*2+1] = input[w-1]; - - STBI_NOTUSED(in_far); - STBI_NOTUSED(hs); - - return out; -} - -#define stbi__div16(x) ((stbi_uc) ((x) >> 4)) - -static stbi_uc *stbi__resample_row_hv_2(stbi_uc *out, stbi_uc *in_near, stbi_uc *in_far, int w, int hs) -{ - // need to generate 2x2 samples for every one in input - int i,t0,t1; - if (w == 1) { - out[0] = out[1] = stbi__div4(3*in_near[0] + in_far[0] + 2); - return out; - } - - t1 = 3*in_near[0] + in_far[0]; - out[0] = stbi__div4(t1+2); - for (i=1; i < w; ++i) { - t0 = t1; - t1 = 3*in_near[i]+in_far[i]; - out[i*2-1] = stbi__div16(3*t0 + t1 + 8); - out[i*2 ] = stbi__div16(3*t1 + t0 + 8); - } - out[w*2-1] = stbi__div4(t1+2); - - STBI_NOTUSED(hs); - - return out; -} - -#if defined(STBI_SSE2) || defined(STBI_NEON) -static stbi_uc *stbi__resample_row_hv_2_simd(stbi_uc *out, stbi_uc *in_near, stbi_uc *in_far, int w, int hs) -{ - // need to generate 2x2 samples for every one in input - int i=0,t0,t1; - - if (w == 1) { - out[0] = out[1] = stbi__div4(3*in_near[0] + in_far[0] + 2); - return out; - } - - t1 = 3*in_near[0] + in_far[0]; - // process groups of 8 pixels for as long as we can. - // note we can't handle the last pixel in a row in this loop - // because we need to handle the filter boundary conditions. - for (; i < ((w-1) & ~7); i += 8) { -#if defined(STBI_SSE2) - // load and perform the vertical filtering pass - // this uses 3*x + y = 4*x + (y - x) - __m128i zero = _mm_setzero_si128(); - __m128i farb = _mm_loadl_epi64((__m128i *) (in_far + i)); - __m128i nearb = _mm_loadl_epi64((__m128i *) (in_near + i)); - __m128i farw = _mm_unpacklo_epi8(farb, zero); - __m128i nearw = _mm_unpacklo_epi8(nearb, zero); - __m128i diff = _mm_sub_epi16(farw, nearw); - __m128i nears = _mm_slli_epi16(nearw, 2); - __m128i curr = _mm_add_epi16(nears, diff); // current row - - // horizontal filter works the same based on shifted vers of current - // row. "prev" is current row shifted right by 1 pixel; we need to - // insert the previous pixel value (from t1). - // "next" is current row shifted left by 1 pixel, with first pixel - // of next block of 8 pixels added in. - __m128i prv0 = _mm_slli_si128(curr, 2); - __m128i nxt0 = _mm_srli_si128(curr, 2); - __m128i prev = _mm_insert_epi16(prv0, t1, 0); - __m128i next = _mm_insert_epi16(nxt0, 3*in_near[i+8] + in_far[i+8], 7); - - // horizontal filter, polyphase implementation since it's convenient: - // even pixels = 3*cur + prev = cur*4 + (prev - cur) - // odd pixels = 3*cur + next = cur*4 + (next - cur) - // note the shared term. - __m128i bias = _mm_set1_epi16(8); - __m128i curs = _mm_slli_epi16(curr, 2); - __m128i prvd = _mm_sub_epi16(prev, curr); - __m128i nxtd = _mm_sub_epi16(next, curr); - __m128i curb = _mm_add_epi16(curs, bias); - __m128i even = _mm_add_epi16(prvd, curb); - __m128i odd = _mm_add_epi16(nxtd, curb); - - // interleave even and odd pixels, then undo scaling. - __m128i int0 = _mm_unpacklo_epi16(even, odd); - __m128i int1 = _mm_unpackhi_epi16(even, odd); - __m128i de0 = _mm_srli_epi16(int0, 4); - __m128i de1 = _mm_srli_epi16(int1, 4); - - // pack and write output - __m128i outv = _mm_packus_epi16(de0, de1); - _mm_storeu_si128((__m128i *) (out + i*2), outv); -#elif defined(STBI_NEON) - // load and perform the vertical filtering pass - // this uses 3*x + y = 4*x + (y - x) - uint8x8_t farb = vld1_u8(in_far + i); - uint8x8_t nearb = vld1_u8(in_near + i); - int16x8_t diff = vreinterpretq_s16_u16(vsubl_u8(farb, nearb)); - int16x8_t nears = vreinterpretq_s16_u16(vshll_n_u8(nearb, 2)); - int16x8_t curr = vaddq_s16(nears, diff); // current row - - // horizontal filter works the same based on shifted vers of current - // row. "prev" is current row shifted right by 1 pixel; we need to - // insert the previous pixel value (from t1). - // "next" is current row shifted left by 1 pixel, with first pixel - // of next block of 8 pixels added in. - int16x8_t prv0 = vextq_s16(curr, curr, 7); - int16x8_t nxt0 = vextq_s16(curr, curr, 1); - int16x8_t prev = vsetq_lane_s16(t1, prv0, 0); - int16x8_t next = vsetq_lane_s16(3*in_near[i+8] + in_far[i+8], nxt0, 7); - - // horizontal filter, polyphase implementation since it's convenient: - // even pixels = 3*cur + prev = cur*4 + (prev - cur) - // odd pixels = 3*cur + next = cur*4 + (next - cur) - // note the shared term. - int16x8_t curs = vshlq_n_s16(curr, 2); - int16x8_t prvd = vsubq_s16(prev, curr); - int16x8_t nxtd = vsubq_s16(next, curr); - int16x8_t even = vaddq_s16(curs, prvd); - int16x8_t odd = vaddq_s16(curs, nxtd); - - // undo scaling and round, then store with even/odd phases interleaved - uint8x8x2_t o; - o.val[0] = vqrshrun_n_s16(even, 4); - o.val[1] = vqrshrun_n_s16(odd, 4); - vst2_u8(out + i*2, o); -#endif - - // "previous" value for next iter - t1 = 3*in_near[i+7] + in_far[i+7]; - } - - t0 = t1; - t1 = 3*in_near[i] + in_far[i]; - out[i*2] = stbi__div16(3*t1 + t0 + 8); - - for (++i; i < w; ++i) { - t0 = t1; - t1 = 3*in_near[i]+in_far[i]; - out[i*2-1] = stbi__div16(3*t0 + t1 + 8); - out[i*2 ] = stbi__div16(3*t1 + t0 + 8); - } - out[w*2-1] = stbi__div4(t1+2); - - STBI_NOTUSED(hs); - - return out; -} -#endif - -static stbi_uc *stbi__resample_row_generic(stbi_uc *out, stbi_uc *in_near, stbi_uc *in_far, int w, int hs) -{ - // resample with nearest-neighbor - int i,j; - STBI_NOTUSED(in_far); - for (i=0; i < w; ++i) - for (j=0; j < hs; ++j) - out[i*hs+j] = in_near[i]; - return out; -} - -#ifdef STBI_JPEG_OLD -// this is the same YCbCr-to-RGB calculation that stb_image has used -// historically before the algorithm changes in 1.49 -#define float2fixed(x) ((int) ((x) * 65536 + 0.5)) -static void stbi__YCbCr_to_RGB_row(stbi_uc *out, const stbi_uc *y, const stbi_uc *pcb, const stbi_uc *pcr, int count, int step) -{ - int i; - for (i=0; i < count; ++i) { - int y_fixed = (y[i] << 16) + 32768; // rounding - int r,g,b; - int cr = pcr[i] - 128; - int cb = pcb[i] - 128; - r = y_fixed + cr*float2fixed(1.40200f); - g = y_fixed - cr*float2fixed(0.71414f) - cb*float2fixed(0.34414f); - b = y_fixed + cb*float2fixed(1.77200f); - r >>= 16; - g >>= 16; - b >>= 16; - if ((unsigned) r > 255) { if (r < 0) r = 0; else r = 255; } - if ((unsigned) g > 255) { if (g < 0) g = 0; else g = 255; } - if ((unsigned) b > 255) { if (b < 0) b = 0; else b = 255; } - out[0] = (stbi_uc)r; - out[1] = (stbi_uc)g; - out[2] = (stbi_uc)b; - out[3] = 255; - out += step; - } -} -#else -// this is a reduced-precision calculation of YCbCr-to-RGB introduced -// to make sure the code produces the same results in both SIMD and scalar -#define float2fixed(x) (((int) ((x) * 4096.0f + 0.5f)) << 8) -static void stbi__YCbCr_to_RGB_row(stbi_uc *out, const stbi_uc *y, const stbi_uc *pcb, const stbi_uc *pcr, int count, int step) -{ - int i; - for (i=0; i < count; ++i) { - int y_fixed = (y[i] << 20) + (1<<19); // rounding - int r,g,b; - int cr = pcr[i] - 128; - int cb = pcb[i] - 128; - r = y_fixed + cr* float2fixed(1.40200f); - g = y_fixed + (cr*-float2fixed(0.71414f)) + ((cb*-float2fixed(0.34414f)) & 0xffff0000); - b = y_fixed + cb* float2fixed(1.77200f); - r >>= 20; - g >>= 20; - b >>= 20; - if ((unsigned) r > 255) { if (r < 0) r = 0; else r = 255; } - if ((unsigned) g > 255) { if (g < 0) g = 0; else g = 255; } - if ((unsigned) b > 255) { if (b < 0) b = 0; else b = 255; } - out[0] = (stbi_uc)r; - out[1] = (stbi_uc)g; - out[2] = (stbi_uc)b; - out[3] = 255; - out += step; - } -} -#endif - -#if defined(STBI_SSE2) || defined(STBI_NEON) -static void stbi__YCbCr_to_RGB_simd(stbi_uc *out, stbi_uc const *y, stbi_uc const *pcb, stbi_uc const *pcr, int count, int step) -{ - int i = 0; - -#ifdef STBI_SSE2 - // step == 3 is pretty ugly on the final interleave, and i'm not convinced - // it's useful in practice (you wouldn't use it for textures, for example). - // so just accelerate step == 4 case. - if (step == 4) { - // this is a fairly straightforward implementation and not super-optimized. - __m128i signflip = _mm_set1_epi8(-0x80); - __m128i cr_const0 = _mm_set1_epi16( (short) ( 1.40200f*4096.0f+0.5f)); - __m128i cr_const1 = _mm_set1_epi16( - (short) ( 0.71414f*4096.0f+0.5f)); - __m128i cb_const0 = _mm_set1_epi16( - (short) ( 0.34414f*4096.0f+0.5f)); - __m128i cb_const1 = _mm_set1_epi16( (short) ( 1.77200f*4096.0f+0.5f)); - __m128i y_bias = _mm_set1_epi8((char) (unsigned char) 128); - __m128i xw = _mm_set1_epi16(255); // alpha channel - - for (; i+7 < count; i += 8) { - // load - __m128i y_bytes = _mm_loadl_epi64((__m128i *) (y+i)); - __m128i cr_bytes = _mm_loadl_epi64((__m128i *) (pcr+i)); - __m128i cb_bytes = _mm_loadl_epi64((__m128i *) (pcb+i)); - __m128i cr_biased = _mm_xor_si128(cr_bytes, signflip); // -128 - __m128i cb_biased = _mm_xor_si128(cb_bytes, signflip); // -128 - - // unpack to short (and left-shift cr, cb by 8) - __m128i yw = _mm_unpacklo_epi8(y_bias, y_bytes); - __m128i crw = _mm_unpacklo_epi8(_mm_setzero_si128(), cr_biased); - __m128i cbw = _mm_unpacklo_epi8(_mm_setzero_si128(), cb_biased); - - // color transform - __m128i yws = _mm_srli_epi16(yw, 4); - __m128i cr0 = _mm_mulhi_epi16(cr_const0, crw); - __m128i cb0 = _mm_mulhi_epi16(cb_const0, cbw); - __m128i cb1 = _mm_mulhi_epi16(cbw, cb_const1); - __m128i cr1 = _mm_mulhi_epi16(crw, cr_const1); - __m128i rws = _mm_add_epi16(cr0, yws); - __m128i gwt = _mm_add_epi16(cb0, yws); - __m128i bws = _mm_add_epi16(yws, cb1); - __m128i gws = _mm_add_epi16(gwt, cr1); - - // descale - __m128i rw = _mm_srai_epi16(rws, 4); - __m128i bw = _mm_srai_epi16(bws, 4); - __m128i gw = _mm_srai_epi16(gws, 4); - - // back to byte, set up for transpose - __m128i brb = _mm_packus_epi16(rw, bw); - __m128i gxb = _mm_packus_epi16(gw, xw); - - // transpose to interleave channels - __m128i t0 = _mm_unpacklo_epi8(brb, gxb); - __m128i t1 = _mm_unpackhi_epi8(brb, gxb); - __m128i o0 = _mm_unpacklo_epi16(t0, t1); - __m128i o1 = _mm_unpackhi_epi16(t0, t1); - - // store - _mm_storeu_si128((__m128i *) (out + 0), o0); - _mm_storeu_si128((__m128i *) (out + 16), o1); - out += 32; - } - } -#endif - -#ifdef STBI_NEON - // in this version, step=3 support would be easy to add. but is there demand? - if (step == 4) { - // this is a fairly straightforward implementation and not super-optimized. - uint8x8_t signflip = vdup_n_u8(0x80); - int16x8_t cr_const0 = vdupq_n_s16( (short) ( 1.40200f*4096.0f+0.5f)); - int16x8_t cr_const1 = vdupq_n_s16( - (short) ( 0.71414f*4096.0f+0.5f)); - int16x8_t cb_const0 = vdupq_n_s16( - (short) ( 0.34414f*4096.0f+0.5f)); - int16x8_t cb_const1 = vdupq_n_s16( (short) ( 1.77200f*4096.0f+0.5f)); - - for (; i+7 < count; i += 8) { - // load - uint8x8_t y_bytes = vld1_u8(y + i); - uint8x8_t cr_bytes = vld1_u8(pcr + i); - uint8x8_t cb_bytes = vld1_u8(pcb + i); - int8x8_t cr_biased = vreinterpret_s8_u8(vsub_u8(cr_bytes, signflip)); - int8x8_t cb_biased = vreinterpret_s8_u8(vsub_u8(cb_bytes, signflip)); - - // expand to s16 - int16x8_t yws = vreinterpretq_s16_u16(vshll_n_u8(y_bytes, 4)); - int16x8_t crw = vshll_n_s8(cr_biased, 7); - int16x8_t cbw = vshll_n_s8(cb_biased, 7); - - // color transform - int16x8_t cr0 = vqdmulhq_s16(crw, cr_const0); - int16x8_t cb0 = vqdmulhq_s16(cbw, cb_const0); - int16x8_t cr1 = vqdmulhq_s16(crw, cr_const1); - int16x8_t cb1 = vqdmulhq_s16(cbw, cb_const1); - int16x8_t rws = vaddq_s16(yws, cr0); - int16x8_t gws = vaddq_s16(vaddq_s16(yws, cb0), cr1); - int16x8_t bws = vaddq_s16(yws, cb1); - - // undo scaling, round, convert to byte - uint8x8x4_t o; - o.val[0] = vqrshrun_n_s16(rws, 4); - o.val[1] = vqrshrun_n_s16(gws, 4); - o.val[2] = vqrshrun_n_s16(bws, 4); - o.val[3] = vdup_n_u8(255); - - // store, interleaving r/g/b/a - vst4_u8(out, o); - out += 8*4; - } - } -#endif - - for (; i < count; ++i) { - int y_fixed = (y[i] << 20) + (1<<19); // rounding - int r,g,b; - int cr = pcr[i] - 128; - int cb = pcb[i] - 128; - r = y_fixed + cr* float2fixed(1.40200f); - g = y_fixed + cr*-float2fixed(0.71414f) + ((cb*-float2fixed(0.34414f)) & 0xffff0000); - b = y_fixed + cb* float2fixed(1.77200f); - r >>= 20; - g >>= 20; - b >>= 20; - if ((unsigned) r > 255) { if (r < 0) r = 0; else r = 255; } - if ((unsigned) g > 255) { if (g < 0) g = 0; else g = 255; } - if ((unsigned) b > 255) { if (b < 0) b = 0; else b = 255; } - out[0] = (stbi_uc)r; - out[1] = (stbi_uc)g; - out[2] = (stbi_uc)b; - out[3] = 255; - out += step; - } -} -#endif - -// set up the kernels -static void stbi__setup_jpeg(stbi__jpeg *j) -{ - j->idct_block_kernel = stbi__idct_block; - j->YCbCr_to_RGB_kernel = stbi__YCbCr_to_RGB_row; - j->resample_row_hv_2_kernel = stbi__resample_row_hv_2; - -#ifdef STBI_SSE2 - if (stbi__sse2_available()) { - j->idct_block_kernel = stbi__idct_simd; - #ifndef STBI_JPEG_OLD - j->YCbCr_to_RGB_kernel = stbi__YCbCr_to_RGB_simd; - #endif - j->resample_row_hv_2_kernel = stbi__resample_row_hv_2_simd; - } -#endif - -#ifdef STBI_NEON - j->idct_block_kernel = stbi__idct_simd; - #ifndef STBI_JPEG_OLD - j->YCbCr_to_RGB_kernel = stbi__YCbCr_to_RGB_simd; - #endif - j->resample_row_hv_2_kernel = stbi__resample_row_hv_2_simd; -#endif -} - -// clean up the temporary component buffers -static void stbi__cleanup_jpeg(stbi__jpeg *j) -{ - int i; - for (i=0; i < j->s->img_n; ++i) { - if (j->img_comp[i].raw_data) { - STBI_FREE(j->img_comp[i].raw_data); - j->img_comp[i].raw_data = NULL; - j->img_comp[i].data = NULL; - } - if (j->img_comp[i].raw_coeff) { - STBI_FREE(j->img_comp[i].raw_coeff); - j->img_comp[i].raw_coeff = 0; - j->img_comp[i].coeff = 0; - } - if (j->img_comp[i].linebuf) { - STBI_FREE(j->img_comp[i].linebuf); - j->img_comp[i].linebuf = NULL; - } - } -} - -typedef struct -{ - resample_row_func resample; - stbi_uc *line0,*line1; - int hs,vs; // expansion factor in each axis - int w_lores; // horizontal pixels pre-expansion - int ystep; // how far through vertical expansion we are - int ypos; // which pre-expansion row we're on -} stbi__resample; - -static stbi_uc *load_jpeg_image(stbi__jpeg *z, int *out_x, int *out_y, int *comp, int req_comp) -{ - int n, decode_n; - z->s->img_n = 0; // make stbi__cleanup_jpeg safe - - // validate req_comp - if (req_comp < 0 || req_comp > 4) return stbi__errpuc("bad req_comp", "Internal error"); - - // load a jpeg image from whichever source, but leave in YCbCr format - if (!stbi__decode_jpeg_image(z)) { stbi__cleanup_jpeg(z); return NULL; } - - // determine actual number of components to generate - n = req_comp ? req_comp : z->s->img_n; - - if (z->s->img_n == 3 && n < 3) - decode_n = 1; - else - decode_n = z->s->img_n; - - // resample and color-convert - { - int k; - unsigned int i,j; - stbi_uc *output; - stbi_uc *coutput[4]; - - stbi__resample res_comp[4]; - - for (k=0; k < decode_n; ++k) { - stbi__resample *r = &res_comp[k]; - - // allocate line buffer big enough for upsampling off the edges - // with upsample factor of 4 - z->img_comp[k].linebuf = (stbi_uc *) stbi__malloc(z->s->img_x + 3); - if (!z->img_comp[k].linebuf) { stbi__cleanup_jpeg(z); return stbi__errpuc("outofmem", "Out of memory"); } - - r->hs = z->img_h_max / z->img_comp[k].h; - r->vs = z->img_v_max / z->img_comp[k].v; - r->ystep = r->vs >> 1; - r->w_lores = (z->s->img_x + r->hs-1) / r->hs; - r->ypos = 0; - r->line0 = r->line1 = z->img_comp[k].data; - - if (r->hs == 1 && r->vs == 1) r->resample = resample_row_1; - else if (r->hs == 1 && r->vs == 2) r->resample = stbi__resample_row_v_2; - else if (r->hs == 2 && r->vs == 1) r->resample = stbi__resample_row_h_2; - else if (r->hs == 2 && r->vs == 2) r->resample = z->resample_row_hv_2_kernel; - else r->resample = stbi__resample_row_generic; - } - - // can't error after this so, this is safe - output = (stbi_uc *) stbi__malloc(n * z->s->img_x * z->s->img_y + 1); - if (!output) { stbi__cleanup_jpeg(z); return stbi__errpuc("outofmem", "Out of memory"); } - - // now go ahead and resample - for (j=0; j < z->s->img_y; ++j) { - stbi_uc *out = output + n * z->s->img_x * j; - for (k=0; k < decode_n; ++k) { - stbi__resample *r = &res_comp[k]; - int y_bot = r->ystep >= (r->vs >> 1); - coutput[k] = r->resample(z->img_comp[k].linebuf, - y_bot ? r->line1 : r->line0, - y_bot ? r->line0 : r->line1, - r->w_lores, r->hs); - if (++r->ystep >= r->vs) { - r->ystep = 0; - r->line0 = r->line1; - if (++r->ypos < z->img_comp[k].y) - r->line1 += z->img_comp[k].w2; - } - } - if (n >= 3) { - stbi_uc *y = coutput[0]; - if (z->s->img_n == 3) { - if (z->rgb == 3) { - for (i=0; i < z->s->img_x; ++i) { - out[0] = y[i]; - out[1] = coutput[1][i]; - out[2] = coutput[2][i]; - out[3] = 255; - out += n; - } - } else { - z->YCbCr_to_RGB_kernel(out, y, coutput[1], coutput[2], z->s->img_x, n); - } - } else - for (i=0; i < z->s->img_x; ++i) { - out[0] = out[1] = out[2] = y[i]; - out[3] = 255; // not used if n==3 - out += n; - } - } else { - stbi_uc *y = coutput[0]; - if (n == 1) - for (i=0; i < z->s->img_x; ++i) out[i] = y[i]; - else - for (i=0; i < z->s->img_x; ++i) *out++ = y[i], *out++ = 255; - } - } - stbi__cleanup_jpeg(z); - *out_x = z->s->img_x; - *out_y = z->s->img_y; - if (comp) *comp = z->s->img_n; // report original components, not output - return output; - } -} - -static unsigned char *stbi__jpeg_load(stbi__context *s, int *x, int *y, int *comp, int req_comp) -{ - unsigned char* result; - stbi__jpeg* j = (stbi__jpeg*) stbi__malloc(sizeof(stbi__jpeg)); - j->s = s; - stbi__setup_jpeg(j); - result = load_jpeg_image(j, x,y,comp,req_comp); - STBI_FREE(j); - return result; -} - -static int stbi__jpeg_test(stbi__context *s) -{ - int r; - stbi__jpeg j; - j.s = s; - stbi__setup_jpeg(&j); - r = stbi__decode_jpeg_header(&j, STBI__SCAN_type); - stbi__rewind(s); - return r; -} - -static int stbi__jpeg_info_raw(stbi__jpeg *j, int *x, int *y, int *comp) -{ - if (!stbi__decode_jpeg_header(j, STBI__SCAN_header)) { - stbi__rewind( j->s ); - return 0; - } - if (x) *x = j->s->img_x; - if (y) *y = j->s->img_y; - if (comp) *comp = j->s->img_n; - return 1; -} - -static int stbi__jpeg_info(stbi__context *s, int *x, int *y, int *comp) -{ - int result; - stbi__jpeg* j = (stbi__jpeg*) (stbi__malloc(sizeof(stbi__jpeg))); - j->s = s; - result = stbi__jpeg_info_raw(j, x, y, comp); - STBI_FREE(j); - return result; -} -#endif - -// public domain zlib decode v0.2 Sean Barrett 2006-11-18 -// simple implementation -// - all input must be provided in an upfront buffer -// - all output is written to a single output buffer (can malloc/realloc) -// performance -// - fast huffman - -#ifndef STBI_NO_ZLIB - -// fast-way is faster to check than jpeg huffman, but slow way is slower -#define STBI__ZFAST_BITS 9 // accelerate all cases in default tables -#define STBI__ZFAST_MASK ((1 << STBI__ZFAST_BITS) - 1) - -// zlib-style huffman encoding -// (jpegs packs from left, zlib from right, so can't share code) -typedef struct -{ - stbi__uint16 fast[1 << STBI__ZFAST_BITS]; - stbi__uint16 firstcode[16]; - int maxcode[17]; - stbi__uint16 firstsymbol[16]; - stbi_uc size[288]; - stbi__uint16 value[288]; -} stbi__zhuffman; - -stbi_inline static int stbi__bitreverse16(int n) -{ - n = ((n & 0xAAAA) >> 1) | ((n & 0x5555) << 1); - n = ((n & 0xCCCC) >> 2) | ((n & 0x3333) << 2); - n = ((n & 0xF0F0) >> 4) | ((n & 0x0F0F) << 4); - n = ((n & 0xFF00) >> 8) | ((n & 0x00FF) << 8); - return n; -} - -stbi_inline static int stbi__bit_reverse(int v, int bits) -{ - STBI_ASSERT(bits <= 16); - // to bit reverse n bits, reverse 16 and shift - // e.g. 11 bits, bit reverse and shift away 5 - return stbi__bitreverse16(v) >> (16-bits); -} - -static int stbi__zbuild_huffman(stbi__zhuffman *z, stbi_uc *sizelist, int num) -{ - int i,k=0; - int code, next_code[16], sizes[17]; - - // DEFLATE spec for generating codes - memset(sizes, 0, sizeof(sizes)); - memset(z->fast, 0, sizeof(z->fast)); - for (i=0; i < num; ++i) - ++sizes[sizelist[i]]; - sizes[0] = 0; - for (i=1; i < 16; ++i) - if (sizes[i] > (1 << i)) - return stbi__err("bad sizes", "Corrupt PNG"); - code = 0; - for (i=1; i < 16; ++i) { - next_code[i] = code; - z->firstcode[i] = (stbi__uint16) code; - z->firstsymbol[i] = (stbi__uint16) k; - code = (code + sizes[i]); - if (sizes[i]) - if (code-1 >= (1 << i)) return stbi__err("bad codelengths","Corrupt PNG"); - z->maxcode[i] = code << (16-i); // preshift for inner loop - code <<= 1; - k += sizes[i]; - } - z->maxcode[16] = 0x10000; // sentinel - for (i=0; i < num; ++i) { - int s = sizelist[i]; - if (s) { - int c = next_code[s] - z->firstcode[s] + z->firstsymbol[s]; - stbi__uint16 fastv = (stbi__uint16) ((s << 9) | i); - z->size [c] = (stbi_uc ) s; - z->value[c] = (stbi__uint16) i; - if (s <= STBI__ZFAST_BITS) { - int j = stbi__bit_reverse(next_code[s],s); - while (j < (1 << STBI__ZFAST_BITS)) { - z->fast[j] = fastv; - j += (1 << s); - } - } - ++next_code[s]; - } - } - return 1; -} - -// zlib-from-memory implementation for PNG reading -// because PNG allows splitting the zlib stream arbitrarily, -// and it's annoying structurally to have PNG call ZLIB call PNG, -// we require PNG read all the IDATs and combine them into a single -// memory buffer - -typedef struct -{ - stbi_uc *zbuffer, *zbuffer_end; - int num_bits; - stbi__uint32 code_buffer; - - char *zout; - char *zout_start; - char *zout_end; - int z_expandable; - - stbi__zhuffman z_length, z_distance; -} stbi__zbuf; - -stbi_inline static stbi_uc stbi__zget8(stbi__zbuf *z) -{ - if (z->zbuffer >= z->zbuffer_end) return 0; - return *z->zbuffer++; -} - -static void stbi__fill_bits(stbi__zbuf *z) -{ - do { - STBI_ASSERT(z->code_buffer < (1U << z->num_bits)); - z->code_buffer |= (unsigned int) stbi__zget8(z) << z->num_bits; - z->num_bits += 8; - } while (z->num_bits <= 24); -} - -stbi_inline static unsigned int stbi__zreceive(stbi__zbuf *z, int n) -{ - unsigned int k; - if (z->num_bits < n) stbi__fill_bits(z); - k = z->code_buffer & ((1 << n) - 1); - z->code_buffer >>= n; - z->num_bits -= n; - return k; -} - -static int stbi__zhuffman_decode_slowpath(stbi__zbuf *a, stbi__zhuffman *z) -{ - int b,s,k; - // not resolved by fast table, so compute it the slow way - // use jpeg approach, which requires MSbits at top - k = stbi__bit_reverse(a->code_buffer, 16); - for (s=STBI__ZFAST_BITS+1; ; ++s) - if (k < z->maxcode[s]) - break; - if (s == 16) return -1; // invalid code! - // code size is s, so: - b = (k >> (16-s)) - z->firstcode[s] + z->firstsymbol[s]; - STBI_ASSERT(z->size[b] == s); - a->code_buffer >>= s; - a->num_bits -= s; - return z->value[b]; -} - -stbi_inline static int stbi__zhuffman_decode(stbi__zbuf *a, stbi__zhuffman *z) -{ - int b,s; - if (a->num_bits < 16) stbi__fill_bits(a); - b = z->fast[a->code_buffer & STBI__ZFAST_MASK]; - if (b) { - s = b >> 9; - a->code_buffer >>= s; - a->num_bits -= s; - return b & 511; - } - return stbi__zhuffman_decode_slowpath(a, z); -} - -static int stbi__zexpand(stbi__zbuf *z, char *zout, int n) // need to make room for n bytes -{ - char *q; - int cur, limit, old_limit; - z->zout = zout; - if (!z->z_expandable) return stbi__err("output buffer limit","Corrupt PNG"); - cur = (int) (z->zout - z->zout_start); - limit = old_limit = (int) (z->zout_end - z->zout_start); - while (cur + n > limit) - limit *= 2; - q = (char *) STBI_REALLOC_SIZED(z->zout_start, old_limit, limit); - STBI_NOTUSED(old_limit); - if (q == NULL) return stbi__err("outofmem", "Out of memory"); - z->zout_start = q; - z->zout = q + cur; - z->zout_end = q + limit; - return 1; -} - -static int stbi__zlength_base[31] = { - 3,4,5,6,7,8,9,10,11,13, - 15,17,19,23,27,31,35,43,51,59, - 67,83,99,115,131,163,195,227,258,0,0 }; - -static int stbi__zlength_extra[31]= -{ 0,0,0,0,0,0,0,0,1,1,1,1,2,2,2,2,3,3,3,3,4,4,4,4,5,5,5,5,0,0,0 }; - -static int stbi__zdist_base[32] = { 1,2,3,4,5,7,9,13,17,25,33,49,65,97,129,193, -257,385,513,769,1025,1537,2049,3073,4097,6145,8193,12289,16385,24577,0,0}; - -static int stbi__zdist_extra[32] = -{ 0,0,0,0,1,1,2,2,3,3,4,4,5,5,6,6,7,7,8,8,9,9,10,10,11,11,12,12,13,13}; - -static int stbi__parse_huffman_block(stbi__zbuf *a) -{ - char *zout = a->zout; - for(;;) { - int z = stbi__zhuffman_decode(a, &a->z_length); - if (z < 256) { - if (z < 0) return stbi__err("bad huffman code","Corrupt PNG"); // error in huffman codes - if (zout >= a->zout_end) { - if (!stbi__zexpand(a, zout, 1)) return 0; - zout = a->zout; - } - *zout++ = (char) z; - } else { - stbi_uc *p; - int len,dist; - if (z == 256) { - a->zout = zout; - return 1; - } - z -= 257; - len = stbi__zlength_base[z]; - if (stbi__zlength_extra[z]) len += stbi__zreceive(a, stbi__zlength_extra[z]); - z = stbi__zhuffman_decode(a, &a->z_distance); - if (z < 0) return stbi__err("bad huffman code","Corrupt PNG"); - dist = stbi__zdist_base[z]; - if (stbi__zdist_extra[z]) dist += stbi__zreceive(a, stbi__zdist_extra[z]); - if (zout - a->zout_start < dist) return stbi__err("bad dist","Corrupt PNG"); - if (zout + len > a->zout_end) { - if (!stbi__zexpand(a, zout, len)) return 0; - zout = a->zout; - } - p = (stbi_uc *) (zout - dist); - if (dist == 1) { // run of one byte; common in images. - stbi_uc v = *p; - if (len) { do *zout++ = v; while (--len); } - } else { - if (len) { do *zout++ = *p++; while (--len); } - } - } - } -} - -static int stbi__compute_huffman_codes(stbi__zbuf *a) -{ - static stbi_uc length_dezigzag[19] = { 16,17,18,0,8,7,9,6,10,5,11,4,12,3,13,2,14,1,15 }; - stbi__zhuffman z_codelength; - stbi_uc lencodes[286+32+137];//padding for maximum single op - stbi_uc codelength_sizes[19]; - int i,n; - - int hlit = stbi__zreceive(a,5) + 257; - int hdist = stbi__zreceive(a,5) + 1; - int hclen = stbi__zreceive(a,4) + 4; - - memset(codelength_sizes, 0, sizeof(codelength_sizes)); - for (i=0; i < hclen; ++i) { - int s = stbi__zreceive(a,3); - codelength_sizes[length_dezigzag[i]] = (stbi_uc) s; - } - if (!stbi__zbuild_huffman(&z_codelength, codelength_sizes, 19)) return 0; - - n = 0; - while (n < hlit + hdist) { - int c = stbi__zhuffman_decode(a, &z_codelength); - if (c < 0 || c >= 19) return stbi__err("bad codelengths", "Corrupt PNG"); - if (c < 16) - lencodes[n++] = (stbi_uc) c; - else if (c == 16) { - c = stbi__zreceive(a,2)+3; - memset(lencodes+n, lencodes[n-1], c); - n += c; - } else if (c == 17) { - c = stbi__zreceive(a,3)+3; - memset(lencodes+n, 0, c); - n += c; - } else { - STBI_ASSERT(c == 18); - c = stbi__zreceive(a,7)+11; - memset(lencodes+n, 0, c); - n += c; - } - } - if (n != hlit+hdist) return stbi__err("bad codelengths","Corrupt PNG"); - if (!stbi__zbuild_huffman(&a->z_length, lencodes, hlit)) return 0; - if (!stbi__zbuild_huffman(&a->z_distance, lencodes+hlit, hdist)) return 0; - return 1; -} - -static int stbi__parse_uncompressed_block(stbi__zbuf *a) -{ - stbi_uc header[4]; - int len,nlen,k; - if (a->num_bits & 7) - stbi__zreceive(a, a->num_bits & 7); // discard - // drain the bit-packed data into header - k = 0; - while (a->num_bits > 0) { - header[k++] = (stbi_uc) (a->code_buffer & 255); // suppress MSVC run-time check - a->code_buffer >>= 8; - a->num_bits -= 8; - } - STBI_ASSERT(a->num_bits == 0); - // now fill header the normal way - while (k < 4) - header[k++] = stbi__zget8(a); - len = header[1] * 256 + header[0]; - nlen = header[3] * 256 + header[2]; - if (nlen != (len ^ 0xffff)) return stbi__err("zlib corrupt","Corrupt PNG"); - if (a->zbuffer + len > a->zbuffer_end) return stbi__err("read past buffer","Corrupt PNG"); - if (a->zout + len > a->zout_end) - if (!stbi__zexpand(a, a->zout, len)) return 0; - memcpy(a->zout, a->zbuffer, len); - a->zbuffer += len; - a->zout += len; - return 1; -} - -static int stbi__parse_zlib_header(stbi__zbuf *a) -{ - int cmf = stbi__zget8(a); - int cm = cmf & 15; - /* int cinfo = cmf >> 4; */ - int flg = stbi__zget8(a); - if ((cmf*256+flg) % 31 != 0) return stbi__err("bad zlib header","Corrupt PNG"); // zlib spec - if (flg & 32) return stbi__err("no preset dict","Corrupt PNG"); // preset dictionary not allowed in png - if (cm != 8) return stbi__err("bad compression","Corrupt PNG"); // DEFLATE required for png - // window = 1 << (8 + cinfo)... but who cares, we fully buffer output - return 1; -} - -// @TODO: should statically initialize these for optimal thread safety -static stbi_uc stbi__zdefault_length[288], stbi__zdefault_distance[32]; -static void stbi__init_zdefaults(void) -{ - int i; // use <= to match clearly with spec - for (i=0; i <= 143; ++i) stbi__zdefault_length[i] = 8; - for ( ; i <= 255; ++i) stbi__zdefault_length[i] = 9; - for ( ; i <= 279; ++i) stbi__zdefault_length[i] = 7; - for ( ; i <= 287; ++i) stbi__zdefault_length[i] = 8; - - for (i=0; i <= 31; ++i) stbi__zdefault_distance[i] = 5; -} - -static int stbi__parse_zlib(stbi__zbuf *a, int parse_header) -{ - int final, type; - if (parse_header) - if (!stbi__parse_zlib_header(a)) return 0; - a->num_bits = 0; - a->code_buffer = 0; - do { - final = stbi__zreceive(a,1); - type = stbi__zreceive(a,2); - if (type == 0) { - if (!stbi__parse_uncompressed_block(a)) return 0; - } else if (type == 3) { - return 0; - } else { - if (type == 1) { - // use fixed code lengths - if (!stbi__zdefault_distance[31]) stbi__init_zdefaults(); - if (!stbi__zbuild_huffman(&a->z_length , stbi__zdefault_length , 288)) return 0; - if (!stbi__zbuild_huffman(&a->z_distance, stbi__zdefault_distance, 32)) return 0; - } else { - if (!stbi__compute_huffman_codes(a)) return 0; - } - if (!stbi__parse_huffman_block(a)) return 0; - } - } while (!final); - return 1; -} - -static int stbi__do_zlib(stbi__zbuf *a, char *obuf, int olen, int exp, int parse_header) -{ - a->zout_start = obuf; - a->zout = obuf; - a->zout_end = obuf + olen; - a->z_expandable = exp; - - return stbi__parse_zlib(a, parse_header); -} - -STBIDEF char *stbi_zlib_decode_malloc_guesssize(const char *buffer, int len, int initial_size, int *outlen) -{ - stbi__zbuf a; - char *p = (char *) stbi__malloc(initial_size); - if (p == NULL) return NULL; - a.zbuffer = (stbi_uc *) buffer; - a.zbuffer_end = (stbi_uc *) buffer + len; - if (stbi__do_zlib(&a, p, initial_size, 1, 1)) { - if (outlen) *outlen = (int) (a.zout - a.zout_start); - return a.zout_start; - } else { - STBI_FREE(a.zout_start); - return NULL; - } -} - -STBIDEF char *stbi_zlib_decode_malloc(char const *buffer, int len, int *outlen) -{ - return stbi_zlib_decode_malloc_guesssize(buffer, len, 16384, outlen); -} - -STBIDEF char *stbi_zlib_decode_malloc_guesssize_headerflag(const char *buffer, int len, int initial_size, int *outlen, int parse_header) -{ - stbi__zbuf a; - char *p = (char *) stbi__malloc(initial_size); - if (p == NULL) return NULL; - a.zbuffer = (stbi_uc *) buffer; - a.zbuffer_end = (stbi_uc *) buffer + len; - if (stbi__do_zlib(&a, p, initial_size, 1, parse_header)) { - if (outlen) *outlen = (int) (a.zout - a.zout_start); - return a.zout_start; - } else { - STBI_FREE(a.zout_start); - return NULL; - } -} - -STBIDEF int stbi_zlib_decode_buffer(char *obuffer, int olen, char const *ibuffer, int ilen) -{ - stbi__zbuf a; - a.zbuffer = (stbi_uc *) ibuffer; - a.zbuffer_end = (stbi_uc *) ibuffer + ilen; - if (stbi__do_zlib(&a, obuffer, olen, 0, 1)) - return (int) (a.zout - a.zout_start); - else - return -1; -} - -STBIDEF char *stbi_zlib_decode_noheader_malloc(char const *buffer, int len, int *outlen) -{ - stbi__zbuf a; - char *p = (char *) stbi__malloc(16384); - if (p == NULL) return NULL; - a.zbuffer = (stbi_uc *) buffer; - a.zbuffer_end = (stbi_uc *) buffer+len; - if (stbi__do_zlib(&a, p, 16384, 1, 0)) { - if (outlen) *outlen = (int) (a.zout - a.zout_start); - return a.zout_start; - } else { - STBI_FREE(a.zout_start); - return NULL; - } -} - -STBIDEF int stbi_zlib_decode_noheader_buffer(char *obuffer, int olen, const char *ibuffer, int ilen) -{ - stbi__zbuf a; - a.zbuffer = (stbi_uc *) ibuffer; - a.zbuffer_end = (stbi_uc *) ibuffer + ilen; - if (stbi__do_zlib(&a, obuffer, olen, 0, 0)) - return (int) (a.zout - a.zout_start); - else - return -1; -} -#endif - -// public domain "baseline" PNG decoder v0.10 Sean Barrett 2006-11-18 -// simple implementation -// - only 8-bit samples -// - no CRC checking -// - allocates lots of intermediate memory -// - avoids problem of streaming data between subsystems -// - avoids explicit window management -// performance -// - uses stb_zlib, a PD zlib implementation with fast huffman decoding - -#ifndef STBI_NO_PNG -typedef struct -{ - stbi__uint32 length; - stbi__uint32 type; -} stbi__pngchunk; - -static stbi__pngchunk stbi__get_chunk_header(stbi__context *s) -{ - stbi__pngchunk c; - c.length = stbi__get32be(s); - c.type = stbi__get32be(s); - return c; -} - -static int stbi__check_png_header(stbi__context *s) -{ - static stbi_uc png_sig[8] = { 137,80,78,71,13,10,26,10 }; - int i; - for (i=0; i < 8; ++i) - if (stbi__get8(s) != png_sig[i]) return stbi__err("bad png sig","Not a PNG"); - return 1; -} - -typedef struct -{ - stbi__context *s; - stbi_uc *idata, *expanded, *out; - int depth; -} stbi__png; - - -enum { - STBI__F_none=0, - STBI__F_sub=1, - STBI__F_up=2, - STBI__F_avg=3, - STBI__F_paeth=4, - // synthetic filters used for first scanline to avoid needing a dummy row of 0s - STBI__F_avg_first, - STBI__F_paeth_first -}; - -static stbi_uc first_row_filter[5] = -{ - STBI__F_none, - STBI__F_sub, - STBI__F_none, - STBI__F_avg_first, - STBI__F_paeth_first -}; - -static int stbi__paeth(int a, int b, int c) -{ - int p = a + b - c; - int pa = abs(p-a); - int pb = abs(p-b); - int pc = abs(p-c); - if (pa <= pb && pa <= pc) return a; - if (pb <= pc) return b; - return c; -} - -static stbi_uc stbi__depth_scale_table[9] = { 0, 0xff, 0x55, 0, 0x11, 0,0,0, 0x01 }; - -// create the png data from post-deflated data -static int stbi__create_png_image_raw(stbi__png *a, stbi_uc *raw, stbi__uint32 raw_len, int out_n, stbi__uint32 x, stbi__uint32 y, int depth, int color) -{ - int bytes = (depth == 16? 2 : 1); - stbi__context *s = a->s; - stbi__uint32 i,j,stride = x*out_n*bytes; - stbi__uint32 img_len, img_width_bytes; - int k; - int img_n = s->img_n; // copy it into a local for later - - int output_bytes = out_n*bytes; - int filter_bytes = img_n*bytes; - int width = x; - - STBI_ASSERT(out_n == s->img_n || out_n == s->img_n+1); - a->out = (stbi_uc *) stbi__malloc(x * y * output_bytes); // extra bytes to write off the end into - if (!a->out) return stbi__err("outofmem", "Out of memory"); - - img_width_bytes = (((img_n * x * depth) + 7) >> 3); - img_len = (img_width_bytes + 1) * y; - if (s->img_x == x && s->img_y == y) { - if (raw_len != img_len) return stbi__err("not enough pixels","Corrupt PNG"); - } else { // interlaced: - if (raw_len < img_len) return stbi__err("not enough pixels","Corrupt PNG"); - } - - for (j=0; j < y; ++j) { - stbi_uc *cur = a->out + stride*j; - stbi_uc *prior = cur - stride; - int filter = *raw++; - - if (filter > 4) - return stbi__err("invalid filter","Corrupt PNG"); - - if (depth < 8) { - STBI_ASSERT(img_width_bytes <= x); - cur += x*out_n - img_width_bytes; // store output to the rightmost img_len bytes, so we can decode in place - filter_bytes = 1; - width = img_width_bytes; - } - - // if first row, use special filter that doesn't sample previous row - if (j == 0) filter = first_row_filter[filter]; - - // handle first byte explicitly - for (k=0; k < filter_bytes; ++k) { - switch (filter) { - case STBI__F_none : cur[k] = raw[k]; break; - case STBI__F_sub : cur[k] = raw[k]; break; - case STBI__F_up : cur[k] = STBI__BYTECAST(raw[k] + prior[k]); break; - case STBI__F_avg : cur[k] = STBI__BYTECAST(raw[k] + (prior[k]>>1)); break; - case STBI__F_paeth : cur[k] = STBI__BYTECAST(raw[k] + stbi__paeth(0,prior[k],0)); break; - case STBI__F_avg_first : cur[k] = raw[k]; break; - case STBI__F_paeth_first: cur[k] = raw[k]; break; - } - } - - if (depth == 8) { - if (img_n != out_n) - cur[img_n] = 255; // first pixel - raw += img_n; - cur += out_n; - prior += out_n; - } else if (depth == 16) { - if (img_n != out_n) { - cur[filter_bytes] = 255; // first pixel top byte - cur[filter_bytes+1] = 255; // first pixel bottom byte - } - raw += filter_bytes; - cur += output_bytes; - prior += output_bytes; - } else { - raw += 1; - cur += 1; - prior += 1; - } - - // this is a little gross, so that we don't switch per-pixel or per-component - if (depth < 8 || img_n == out_n) { - int nk = (width - 1)*filter_bytes; - #define CASE(f) \ - case f: \ - for (k=0; k < nk; ++k) - switch (filter) { - // "none" filter turns into a memcpy here; make that explicit. - case STBI__F_none: memcpy(cur, raw, nk); break; - CASE(STBI__F_sub) cur[k] = STBI__BYTECAST(raw[k] + cur[k-filter_bytes]); break; - CASE(STBI__F_up) cur[k] = STBI__BYTECAST(raw[k] + prior[k]); break; - CASE(STBI__F_avg) cur[k] = STBI__BYTECAST(raw[k] + ((prior[k] + cur[k-filter_bytes])>>1)); break; - CASE(STBI__F_paeth) cur[k] = STBI__BYTECAST(raw[k] + stbi__paeth(cur[k-filter_bytes],prior[k],prior[k-filter_bytes])); break; - CASE(STBI__F_avg_first) cur[k] = STBI__BYTECAST(raw[k] + (cur[k-filter_bytes] >> 1)); break; - CASE(STBI__F_paeth_first) cur[k] = STBI__BYTECAST(raw[k] + stbi__paeth(cur[k-filter_bytes],0,0)); break; - } - #undef CASE - raw += nk; - } else { - STBI_ASSERT(img_n+1 == out_n); - #define CASE(f) \ - case f: \ - for (i=x-1; i >= 1; --i, cur[filter_bytes]=255,raw+=filter_bytes,cur+=output_bytes,prior+=output_bytes) \ - for (k=0; k < filter_bytes; ++k) - switch (filter) { - CASE(STBI__F_none) cur[k] = raw[k]; break; - CASE(STBI__F_sub) cur[k] = STBI__BYTECAST(raw[k] + cur[k- output_bytes]); break; - CASE(STBI__F_up) cur[k] = STBI__BYTECAST(raw[k] + prior[k]); break; - CASE(STBI__F_avg) cur[k] = STBI__BYTECAST(raw[k] + ((prior[k] + cur[k- output_bytes])>>1)); break; - CASE(STBI__F_paeth) cur[k] = STBI__BYTECAST(raw[k] + stbi__paeth(cur[k- output_bytes],prior[k],prior[k- output_bytes])); break; - CASE(STBI__F_avg_first) cur[k] = STBI__BYTECAST(raw[k] + (cur[k- output_bytes] >> 1)); break; - CASE(STBI__F_paeth_first) cur[k] = STBI__BYTECAST(raw[k] + stbi__paeth(cur[k- output_bytes],0,0)); break; - } - #undef CASE - - // the loop above sets the high byte of the pixels' alpha, but for - // 16 bit png files we also need the low byte set. we'll do that here. - if (depth == 16) { - cur = a->out + stride*j; // start at the beginning of the row again - for (i=0; i < x; ++i,cur+=output_bytes) { - cur[filter_bytes+1] = 255; - } - } - } - } - - // we make a separate pass to expand bits to pixels; for performance, - // this could run two scanlines behind the above code, so it won't - // intefere with filtering but will still be in the cache. - if (depth < 8) { - for (j=0; j < y; ++j) { - stbi_uc *cur = a->out + stride*j; - stbi_uc *in = a->out + stride*j + x*out_n - img_width_bytes; - // unpack 1/2/4-bit into a 8-bit buffer. allows us to keep the common 8-bit path optimal at minimal cost for 1/2/4-bit - // png guarante byte alignment, if width is not multiple of 8/4/2 we'll decode dummy trailing data that will be skipped in the later loop - stbi_uc scale = (color == 0) ? stbi__depth_scale_table[depth] : 1; // scale grayscale values to 0..255 range - - // note that the final byte might overshoot and write more data than desired. - // we can allocate enough data that this never writes out of memory, but it - // could also overwrite the next scanline. can it overwrite non-empty data - // on the next scanline? yes, consider 1-pixel-wide scanlines with 1-bit-per-pixel. - // so we need to explicitly clamp the final ones - - if (depth == 4) { - for (k=x*img_n; k >= 2; k-=2, ++in) { - *cur++ = scale * ((*in >> 4) ); - *cur++ = scale * ((*in ) & 0x0f); - } - if (k > 0) *cur++ = scale * ((*in >> 4) ); - } else if (depth == 2) { - for (k=x*img_n; k >= 4; k-=4, ++in) { - *cur++ = scale * ((*in >> 6) ); - *cur++ = scale * ((*in >> 4) & 0x03); - *cur++ = scale * ((*in >> 2) & 0x03); - *cur++ = scale * ((*in ) & 0x03); - } - if (k > 0) *cur++ = scale * ((*in >> 6) ); - if (k > 1) *cur++ = scale * ((*in >> 4) & 0x03); - if (k > 2) *cur++ = scale * ((*in >> 2) & 0x03); - } else if (depth == 1) { - for (k=x*img_n; k >= 8; k-=8, ++in) { - *cur++ = scale * ((*in >> 7) ); - *cur++ = scale * ((*in >> 6) & 0x01); - *cur++ = scale * ((*in >> 5) & 0x01); - *cur++ = scale * ((*in >> 4) & 0x01); - *cur++ = scale * ((*in >> 3) & 0x01); - *cur++ = scale * ((*in >> 2) & 0x01); - *cur++ = scale * ((*in >> 1) & 0x01); - *cur++ = scale * ((*in ) & 0x01); - } - if (k > 0) *cur++ = scale * ((*in >> 7) ); - if (k > 1) *cur++ = scale * ((*in >> 6) & 0x01); - if (k > 2) *cur++ = scale * ((*in >> 5) & 0x01); - if (k > 3) *cur++ = scale * ((*in >> 4) & 0x01); - if (k > 4) *cur++ = scale * ((*in >> 3) & 0x01); - if (k > 5) *cur++ = scale * ((*in >> 2) & 0x01); - if (k > 6) *cur++ = scale * ((*in >> 1) & 0x01); - } - if (img_n != out_n) { - int q; - // insert alpha = 255 - cur = a->out + stride*j; - if (img_n == 1) { - for (q=x-1; q >= 0; --q) { - cur[q*2+1] = 255; - cur[q*2+0] = cur[q]; - } - } else { - STBI_ASSERT(img_n == 3); - for (q=x-1; q >= 0; --q) { - cur[q*4+3] = 255; - cur[q*4+2] = cur[q*3+2]; - cur[q*4+1] = cur[q*3+1]; - cur[q*4+0] = cur[q*3+0]; - } - } - } - } - } else if (depth == 16) { - // force the image data from big-endian to platform-native. - // this is done in a separate pass due to the decoding relying - // on the data being untouched, but could probably be done - // per-line during decode if care is taken. - stbi_uc *cur = a->out; - stbi__uint16 *cur16 = (stbi__uint16*)cur; - - for(i=0; i < x*y*out_n; ++i,cur16++,cur+=2) { - *cur16 = (cur[0] << 8) | cur[1]; - } - } - - return 1; -} - -static int stbi__create_png_image(stbi__png *a, stbi_uc *image_data, stbi__uint32 image_data_len, int out_n, int depth, int color, int interlaced) -{ - stbi_uc *final; - int p; - if (!interlaced) - return stbi__create_png_image_raw(a, image_data, image_data_len, out_n, a->s->img_x, a->s->img_y, depth, color); - - // de-interlacing - final = (stbi_uc *) stbi__malloc(a->s->img_x * a->s->img_y * out_n); - for (p=0; p < 7; ++p) { - int xorig[] = { 0,4,0,2,0,1,0 }; - int yorig[] = { 0,0,4,0,2,0,1 }; - int xspc[] = { 8,8,4,4,2,2,1 }; - int yspc[] = { 8,8,8,4,4,2,2 }; - int i,j,x,y; - // pass1_x[4] = 0, pass1_x[5] = 1, pass1_x[12] = 1 - x = (a->s->img_x - xorig[p] + xspc[p]-1) / xspc[p]; - y = (a->s->img_y - yorig[p] + yspc[p]-1) / yspc[p]; - if (x && y) { - stbi__uint32 img_len = ((((a->s->img_n * x * depth) + 7) >> 3) + 1) * y; - if (!stbi__create_png_image_raw(a, image_data, image_data_len, out_n, x, y, depth, color)) { - STBI_FREE(final); - return 0; - } - for (j=0; j < y; ++j) { - for (i=0; i < x; ++i) { - int out_y = j*yspc[p]+yorig[p]; - int out_x = i*xspc[p]+xorig[p]; - memcpy(final + out_y*a->s->img_x*out_n + out_x*out_n, - a->out + (j*x+i)*out_n, out_n); - } - } - STBI_FREE(a->out); - image_data += img_len; - image_data_len -= img_len; - } - } - a->out = final; - - return 1; -} - -static int stbi__compute_transparency(stbi__png *z, stbi_uc tc[3], int out_n) -{ - stbi__context *s = z->s; - stbi__uint32 i, pixel_count = s->img_x * s->img_y; - stbi_uc *p = z->out; - - // compute color-based transparency, assuming we've - // already got 255 as the alpha value in the output - STBI_ASSERT(out_n == 2 || out_n == 4); - - if (out_n == 2) { - for (i=0; i < pixel_count; ++i) { - p[1] = (p[0] == tc[0] ? 0 : 255); - p += 2; - } - } else { - for (i=0; i < pixel_count; ++i) { - if (p[0] == tc[0] && p[1] == tc[1] && p[2] == tc[2]) - p[3] = 0; - p += 4; - } - } - return 1; -} - -static int stbi__compute_transparency16(stbi__png *z, stbi__uint16 tc[3], int out_n) -{ - stbi__context *s = z->s; - stbi__uint32 i, pixel_count = s->img_x * s->img_y; - stbi__uint16 *p = (stbi__uint16*) z->out; - - // compute color-based transparency, assuming we've - // already got 65535 as the alpha value in the output - STBI_ASSERT(out_n == 2 || out_n == 4); - - if (out_n == 2) { - for (i = 0; i < pixel_count; ++i) { - p[1] = (p[0] == tc[0] ? 0 : 65535); - p += 2; - } - } else { - for (i = 0; i < pixel_count; ++i) { - if (p[0] == tc[0] && p[1] == tc[1] && p[2] == tc[2]) - p[3] = 0; - p += 4; - } - } - return 1; -} - -static int stbi__expand_png_palette(stbi__png *a, stbi_uc *palette, int len, int pal_img_n) -{ - stbi__uint32 i, pixel_count = a->s->img_x * a->s->img_y; - stbi_uc *p, *temp_out, *orig = a->out; - - p = (stbi_uc *) stbi__malloc(pixel_count * pal_img_n); - if (p == NULL) return stbi__err("outofmem", "Out of memory"); - - // between here and free(out) below, exitting would leak - temp_out = p; - - if (pal_img_n == 3) { - for (i=0; i < pixel_count; ++i) { - int n = orig[i]*4; - p[0] = palette[n ]; - p[1] = palette[n+1]; - p[2] = palette[n+2]; - p += 3; - } - } else { - for (i=0; i < pixel_count; ++i) { - int n = orig[i]*4; - p[0] = palette[n ]; - p[1] = palette[n+1]; - p[2] = palette[n+2]; - p[3] = palette[n+3]; - p += 4; - } - } - STBI_FREE(a->out); - a->out = temp_out; - - STBI_NOTUSED(len); - - return 1; -} - -static int stbi__reduce_png(stbi__png *p) -{ - int i; - int img_len = p->s->img_x * p->s->img_y * p->s->img_out_n; - stbi_uc *reduced; - stbi__uint16 *orig = (stbi__uint16*)p->out; - - if (p->depth != 16) return 1; // don't need to do anything if not 16-bit data - - reduced = (stbi_uc *)stbi__malloc(img_len); - if (p == NULL) return stbi__err("outofmem", "Out of memory"); - - for (i = 0; i < img_len; ++i) reduced[i] = (stbi_uc)((orig[i] >> 8) & 0xFF); // top half of each byte is a decent approx of 16->8 bit scaling - - p->out = reduced; - STBI_FREE(orig); - - return 1; -} - -static int stbi__unpremultiply_on_load = 0; -static int stbi__de_iphone_flag = 0; - -STBIDEF void stbi_set_unpremultiply_on_load(int flag_true_if_should_unpremultiply) -{ - stbi__unpremultiply_on_load = flag_true_if_should_unpremultiply; -} - -STBIDEF void stbi_convert_iphone_png_to_rgb(int flag_true_if_should_convert) -{ - stbi__de_iphone_flag = flag_true_if_should_convert; -} - -static void stbi__de_iphone(stbi__png *z) -{ - stbi__context *s = z->s; - stbi__uint32 i, pixel_count = s->img_x * s->img_y; - stbi_uc *p = z->out; - - if (s->img_out_n == 3) { // convert bgr to rgb - for (i=0; i < pixel_count; ++i) { - stbi_uc t = p[0]; - p[0] = p[2]; - p[2] = t; - p += 3; - } - } else { - STBI_ASSERT(s->img_out_n == 4); - if (stbi__unpremultiply_on_load) { - // convert bgr to rgb and unpremultiply - for (i=0; i < pixel_count; ++i) { - stbi_uc a = p[3]; - stbi_uc t = p[0]; - if (a) { - p[0] = p[2] * 255 / a; - p[1] = p[1] * 255 / a; - p[2] = t * 255 / a; - } else { - p[0] = p[2]; - p[2] = t; - } - p += 4; - } - } else { - // convert bgr to rgb - for (i=0; i < pixel_count; ++i) { - stbi_uc t = p[0]; - p[0] = p[2]; - p[2] = t; - p += 4; - } - } - } -} - -#define STBI__PNG_TYPE(a,b,c,d) (((a) << 24) + ((b) << 16) + ((c) << 8) + (d)) - -static int stbi__parse_png_file(stbi__png *z, int scan, int req_comp) -{ - stbi_uc palette[1024], pal_img_n=0; - stbi_uc has_trans=0, tc[3]; - stbi__uint16 tc16[3]; - stbi__uint32 ioff=0, idata_limit=0, i, pal_len=0; - int first=1,k,interlace=0, color=0, is_iphone=0; - stbi__context *s = z->s; - - z->expanded = NULL; - z->idata = NULL; - z->out = NULL; - - if (!stbi__check_png_header(s)) return 0; - - if (scan == STBI__SCAN_type) return 1; - - for (;;) { - stbi__pngchunk c = stbi__get_chunk_header(s); - switch (c.type) { - case STBI__PNG_TYPE('C','g','B','I'): - is_iphone = 1; - stbi__skip(s, c.length); - break; - case STBI__PNG_TYPE('I','H','D','R'): { - int comp,filter; - if (!first) return stbi__err("multiple IHDR","Corrupt PNG"); - first = 0; - if (c.length != 13) return stbi__err("bad IHDR len","Corrupt PNG"); - s->img_x = stbi__get32be(s); if (s->img_x > (1 << 24)) return stbi__err("too large","Very large image (corrupt?)"); - s->img_y = stbi__get32be(s); if (s->img_y > (1 << 24)) return stbi__err("too large","Very large image (corrupt?)"); - z->depth = stbi__get8(s); if (z->depth != 1 && z->depth != 2 && z->depth != 4 && z->depth != 8 && z->depth != 16) return stbi__err("1/2/4/8/16-bit only","PNG not supported: 1/2/4/8/16-bit only"); - color = stbi__get8(s); if (color > 6) return stbi__err("bad ctype","Corrupt PNG"); - if (color == 3 && z->depth == 16) return stbi__err("bad ctype","Corrupt PNG"); - if (color == 3) pal_img_n = 3; else if (color & 1) return stbi__err("bad ctype","Corrupt PNG"); - comp = stbi__get8(s); if (comp) return stbi__err("bad comp method","Corrupt PNG"); - filter= stbi__get8(s); if (filter) return stbi__err("bad filter method","Corrupt PNG"); - interlace = stbi__get8(s); if (interlace>1) return stbi__err("bad interlace method","Corrupt PNG"); - if (!s->img_x || !s->img_y) return stbi__err("0-pixel image","Corrupt PNG"); - if (!pal_img_n) { - s->img_n = (color & 2 ? 3 : 1) + (color & 4 ? 1 : 0); - if ((1 << 30) / s->img_x / s->img_n < s->img_y) return stbi__err("too large", "Image too large to decode"); - if (scan == STBI__SCAN_header) return 1; - } else { - // if paletted, then pal_n is our final components, and - // img_n is # components to decompress/filter. - s->img_n = 1; - if ((1 << 30) / s->img_x / 4 < s->img_y) return stbi__err("too large","Corrupt PNG"); - // if SCAN_header, have to scan to see if we have a tRNS - } - break; - } - - case STBI__PNG_TYPE('P','L','T','E'): { - if (first) return stbi__err("first not IHDR", "Corrupt PNG"); - if (c.length > 256*3) return stbi__err("invalid PLTE","Corrupt PNG"); - pal_len = c.length / 3; - if (pal_len * 3 != c.length) return stbi__err("invalid PLTE","Corrupt PNG"); - for (i=0; i < pal_len; ++i) { - palette[i*4+0] = stbi__get8(s); - palette[i*4+1] = stbi__get8(s); - palette[i*4+2] = stbi__get8(s); - palette[i*4+3] = 255; - } - break; - } - - case STBI__PNG_TYPE('t','R','N','S'): { - if (first) return stbi__err("first not IHDR", "Corrupt PNG"); - if (z->idata) return stbi__err("tRNS after IDAT","Corrupt PNG"); - if (pal_img_n) { - if (scan == STBI__SCAN_header) { s->img_n = 4; return 1; } - if (pal_len == 0) return stbi__err("tRNS before PLTE","Corrupt PNG"); - if (c.length > pal_len) return stbi__err("bad tRNS len","Corrupt PNG"); - pal_img_n = 4; - for (i=0; i < c.length; ++i) - palette[i*4+3] = stbi__get8(s); - } else { - if (!(s->img_n & 1)) return stbi__err("tRNS with alpha","Corrupt PNG"); - if (c.length != (stbi__uint32) s->img_n*2) return stbi__err("bad tRNS len","Corrupt PNG"); - has_trans = 1; - if (z->depth == 16) { - for (k = 0; k < s->img_n; ++k) tc16[k] = stbi__get16be(s); // copy the values as-is - } else { - for (k = 0; k < s->img_n; ++k) tc[k] = (stbi_uc)(stbi__get16be(s) & 255) * stbi__depth_scale_table[z->depth]; // non 8-bit images will be larger - } - } - break; - } - - case STBI__PNG_TYPE('I','D','A','T'): { - if (first) return stbi__err("first not IHDR", "Corrupt PNG"); - if (pal_img_n && !pal_len) return stbi__err("no PLTE","Corrupt PNG"); - if (scan == STBI__SCAN_header) { s->img_n = pal_img_n; return 1; } - if ((int)(ioff + c.length) < (int)ioff) return 0; - if (ioff + c.length > idata_limit) { - stbi__uint32 idata_limit_old = idata_limit; - stbi_uc *p; - if (idata_limit == 0) idata_limit = c.length > 4096 ? c.length : 4096; - while (ioff + c.length > idata_limit) - idata_limit *= 2; - STBI_NOTUSED(idata_limit_old); - p = (stbi_uc *) STBI_REALLOC_SIZED(z->idata, idata_limit_old, idata_limit); if (p == NULL) return stbi__err("outofmem", "Out of memory"); - z->idata = p; - } - if (!stbi__getn(s, z->idata+ioff,c.length)) return stbi__err("outofdata","Corrupt PNG"); - ioff += c.length; - break; - } - - case STBI__PNG_TYPE('I','E','N','D'): { - stbi__uint32 raw_len, bpl; - if (first) return stbi__err("first not IHDR", "Corrupt PNG"); - if (scan != STBI__SCAN_load) return 1; - if (z->idata == NULL) return stbi__err("no IDAT","Corrupt PNG"); - // initial guess for decoded data size to avoid unnecessary reallocs - bpl = (s->img_x * z->depth + 7) / 8; // bytes per line, per component - raw_len = bpl * s->img_y * s->img_n /* pixels */ + s->img_y /* filter mode per row */; - z->expanded = (stbi_uc *) stbi_zlib_decode_malloc_guesssize_headerflag((char *) z->idata, ioff, raw_len, (int *) &raw_len, !is_iphone); - if (z->expanded == NULL) return 0; // zlib should set error - STBI_FREE(z->idata); z->idata = NULL; - if ((req_comp == s->img_n+1 && req_comp != 3 && !pal_img_n) || has_trans) - s->img_out_n = s->img_n+1; - else - s->img_out_n = s->img_n; - if (!stbi__create_png_image(z, z->expanded, raw_len, s->img_out_n, z->depth, color, interlace)) return 0; - if (has_trans) { - if (z->depth == 16) { - if (!stbi__compute_transparency16(z, tc16, s->img_out_n)) return 0; - } else { - if (!stbi__compute_transparency(z, tc, s->img_out_n)) return 0; - } - } - if (is_iphone && stbi__de_iphone_flag && s->img_out_n > 2) - stbi__de_iphone(z); - if (pal_img_n) { - // pal_img_n == 3 or 4 - s->img_n = pal_img_n; // record the actual colors we had - s->img_out_n = pal_img_n; - if (req_comp >= 3) s->img_out_n = req_comp; - if (!stbi__expand_png_palette(z, palette, pal_len, s->img_out_n)) - return 0; - } - STBI_FREE(z->expanded); z->expanded = NULL; - return 1; - } - - default: - // if critical, fail - if (first) return stbi__err("first not IHDR", "Corrupt PNG"); - if ((c.type & (1 << 29)) == 0) { - #ifndef STBI_NO_FAILURE_STRINGS - // not threadsafe - static char invalid_chunk[] = "XXXX PNG chunk not known"; - invalid_chunk[0] = STBI__BYTECAST(c.type >> 24); - invalid_chunk[1] = STBI__BYTECAST(c.type >> 16); - invalid_chunk[2] = STBI__BYTECAST(c.type >> 8); - invalid_chunk[3] = STBI__BYTECAST(c.type >> 0); - #endif - return stbi__err(invalid_chunk, "PNG not supported: unknown PNG chunk type"); - } - stbi__skip(s, c.length); - break; - } - // end of PNG chunk, read and skip CRC - stbi__get32be(s); - } -} - -static unsigned char *stbi__do_png(stbi__png *p, int *x, int *y, int *n, int req_comp) -{ - unsigned char *result=NULL; - if (req_comp < 0 || req_comp > 4) return stbi__errpuc("bad req_comp", "Internal error"); - if (stbi__parse_png_file(p, STBI__SCAN_load, req_comp)) { - if (p->depth == 16) { - if (!stbi__reduce_png(p)) { - return result; - } - } - result = p->out; - p->out = NULL; - if (req_comp && req_comp != p->s->img_out_n) { - result = stbi__convert_format(result, p->s->img_out_n, req_comp, p->s->img_x, p->s->img_y); - p->s->img_out_n = req_comp; - if (result == NULL) return result; - } - *x = p->s->img_x; - *y = p->s->img_y; - if (n) *n = p->s->img_n; - } - STBI_FREE(p->out); p->out = NULL; - STBI_FREE(p->expanded); p->expanded = NULL; - STBI_FREE(p->idata); p->idata = NULL; - - return result; -} - -static unsigned char *stbi__png_load(stbi__context *s, int *x, int *y, int *comp, int req_comp) -{ - stbi__png p; - p.s = s; - return stbi__do_png(&p, x,y,comp,req_comp); -} - -static int stbi__png_test(stbi__context *s) -{ - int r; - r = stbi__check_png_header(s); - stbi__rewind(s); - return r; -} - -static int stbi__png_info_raw(stbi__png *p, int *x, int *y, int *comp) -{ - if (!stbi__parse_png_file(p, STBI__SCAN_header, 0)) { - stbi__rewind( p->s ); - return 0; - } - if (x) *x = p->s->img_x; - if (y) *y = p->s->img_y; - if (comp) *comp = p->s->img_n; - return 1; -} - -static int stbi__png_info(stbi__context *s, int *x, int *y, int *comp) -{ - stbi__png p; - p.s = s; - return stbi__png_info_raw(&p, x, y, comp); -} -#endif - -// Microsoft/Windows BMP image - -#ifndef STBI_NO_BMP -static int stbi__bmp_test_raw(stbi__context *s) -{ - int r; - int sz; - if (stbi__get8(s) != 'B') return 0; - if (stbi__get8(s) != 'M') return 0; - stbi__get32le(s); // discard filesize - stbi__get16le(s); // discard reserved - stbi__get16le(s); // discard reserved - stbi__get32le(s); // discard data offset - sz = stbi__get32le(s); - r = (sz == 12 || sz == 40 || sz == 56 || sz == 108 || sz == 124); - return r; -} - -static int stbi__bmp_test(stbi__context *s) -{ - int r = stbi__bmp_test_raw(s); - stbi__rewind(s); - return r; -} - - -// returns 0..31 for the highest set bit -static int stbi__high_bit(unsigned int z) -{ - int n=0; - if (z == 0) return -1; - if (z >= 0x10000) n += 16, z >>= 16; - if (z >= 0x00100) n += 8, z >>= 8; - if (z >= 0x00010) n += 4, z >>= 4; - if (z >= 0x00004) n += 2, z >>= 2; - if (z >= 0x00002) n += 1, z >>= 1; - return n; -} - -static int stbi__bitcount(unsigned int a) -{ - a = (a & 0x55555555) + ((a >> 1) & 0x55555555); // max 2 - a = (a & 0x33333333) + ((a >> 2) & 0x33333333); // max 4 - a = (a + (a >> 4)) & 0x0f0f0f0f; // max 8 per 4, now 8 bits - a = (a + (a >> 8)); // max 16 per 8 bits - a = (a + (a >> 16)); // max 32 per 8 bits - return a & 0xff; -} - -static int stbi__shiftsigned(int v, int shift, int bits) -{ - int result; - int z=0; - - if (shift < 0) v <<= -shift; - else v >>= shift; - result = v; - - z = bits; - while (z < 8) { - result += v >> z; - z += bits; - } - return result; -} - -typedef struct -{ - int bpp, offset, hsz; - unsigned int mr,mg,mb,ma, all_a; -} stbi__bmp_data; - -static void *stbi__bmp_parse_header(stbi__context *s, stbi__bmp_data *info) -{ - int hsz; - if (stbi__get8(s) != 'B' || stbi__get8(s) != 'M') return stbi__errpuc("not BMP", "Corrupt BMP"); - stbi__get32le(s); // discard filesize - stbi__get16le(s); // discard reserved - stbi__get16le(s); // discard reserved - info->offset = stbi__get32le(s); - info->hsz = hsz = stbi__get32le(s); - info->mr = info->mg = info->mb = info->ma = 0; - - if (hsz != 12 && hsz != 40 && hsz != 56 && hsz != 108 && hsz != 124) return stbi__errpuc("unknown BMP", "BMP type not supported: unknown"); - if (hsz == 12) { - s->img_x = stbi__get16le(s); - s->img_y = stbi__get16le(s); - } else { - s->img_x = stbi__get32le(s); - s->img_y = stbi__get32le(s); - } - if (stbi__get16le(s) != 1) return stbi__errpuc("bad BMP", "bad BMP"); - info->bpp = stbi__get16le(s); - if (info->bpp == 1) return stbi__errpuc("monochrome", "BMP type not supported: 1-bit"); - if (hsz != 12) { - int compress = stbi__get32le(s); - if (compress == 1 || compress == 2) return stbi__errpuc("BMP RLE", "BMP type not supported: RLE"); - stbi__get32le(s); // discard sizeof - stbi__get32le(s); // discard hres - stbi__get32le(s); // discard vres - stbi__get32le(s); // discard colorsused - stbi__get32le(s); // discard max important - if (hsz == 40 || hsz == 56) { - if (hsz == 56) { - stbi__get32le(s); - stbi__get32le(s); - stbi__get32le(s); - stbi__get32le(s); - } - if (info->bpp == 16 || info->bpp == 32) { - if (compress == 0) { - if (info->bpp == 32) { - info->mr = 0xffu << 16; - info->mg = 0xffu << 8; - info->mb = 0xffu << 0; - info->ma = 0xffu << 24; - info->all_a = 0; // if all_a is 0 at end, then we loaded alpha channel but it was all 0 - } else { - info->mr = 31u << 10; - info->mg = 31u << 5; - info->mb = 31u << 0; - } - } else if (compress == 3) { - info->mr = stbi__get32le(s); - info->mg = stbi__get32le(s); - info->mb = stbi__get32le(s); - // not documented, but generated by photoshop and handled by mspaint - if (info->mr == info->mg && info->mg == info->mb) { - // ?!?!? - return stbi__errpuc("bad BMP", "bad BMP"); - } - } else - return stbi__errpuc("bad BMP", "bad BMP"); - } - } else { - int i; - if (hsz != 108 && hsz != 124) - return stbi__errpuc("bad BMP", "bad BMP"); - info->mr = stbi__get32le(s); - info->mg = stbi__get32le(s); - info->mb = stbi__get32le(s); - info->ma = stbi__get32le(s); - stbi__get32le(s); // discard color space - for (i=0; i < 12; ++i) - stbi__get32le(s); // discard color space parameters - if (hsz == 124) { - stbi__get32le(s); // discard rendering intent - stbi__get32le(s); // discard offset of profile data - stbi__get32le(s); // discard size of profile data - stbi__get32le(s); // discard reserved - } - } - } - return (void *) 1; -} - - -static stbi_uc *stbi__bmp_load(stbi__context *s, int *x, int *y, int *comp, int req_comp) -{ - stbi_uc *out; - unsigned int mr=0,mg=0,mb=0,ma=0, all_a; - stbi_uc pal[256][4]; - int psize=0,i,j,width; - int flip_vertically, pad, target; - stbi__bmp_data info; - - info.all_a = 255; - if (stbi__bmp_parse_header(s, &info) == NULL) - return NULL; // error code already set - - flip_vertically = ((int) s->img_y) > 0; - s->img_y = abs((int) s->img_y); - - mr = info.mr; - mg = info.mg; - mb = info.mb; - ma = info.ma; - all_a = info.all_a; - - if (info.hsz == 12) { - if (info.bpp < 24) - psize = (info.offset - 14 - 24) / 3; - } else { - if (info.bpp < 16) - psize = (info.offset - 14 - info.hsz) >> 2; - } - - s->img_n = ma ? 4 : 3; - if (req_comp && req_comp >= 3) // we can directly decode 3 or 4 - target = req_comp; - else - target = s->img_n; // if they want monochrome, we'll post-convert - - out = (stbi_uc *) stbi__malloc(target * s->img_x * s->img_y); - if (!out) return stbi__errpuc("outofmem", "Out of memory"); - if (info.bpp < 16) { - int z=0; - if (psize == 0 || psize > 256) { STBI_FREE(out); return stbi__errpuc("invalid", "Corrupt BMP"); } - for (i=0; i < psize; ++i) { - pal[i][2] = stbi__get8(s); - pal[i][1] = stbi__get8(s); - pal[i][0] = stbi__get8(s); - if (info.hsz != 12) stbi__get8(s); - pal[i][3] = 255; - } - stbi__skip(s, info.offset - 14 - info.hsz - psize * (info.hsz == 12 ? 3 : 4)); - if (info.bpp == 4) width = (s->img_x + 1) >> 1; - else if (info.bpp == 8) width = s->img_x; - else { STBI_FREE(out); return stbi__errpuc("bad bpp", "Corrupt BMP"); } - pad = (-width)&3; - for (j=0; j < (int) s->img_y; ++j) { - for (i=0; i < (int) s->img_x; i += 2) { - int v=stbi__get8(s),v2=0; - if (info.bpp == 4) { - v2 = v & 15; - v >>= 4; - } - out[z++] = pal[v][0]; - out[z++] = pal[v][1]; - out[z++] = pal[v][2]; - if (target == 4) out[z++] = 255; - if (i+1 == (int) s->img_x) break; - v = (info.bpp == 8) ? stbi__get8(s) : v2; - out[z++] = pal[v][0]; - out[z++] = pal[v][1]; - out[z++] = pal[v][2]; - if (target == 4) out[z++] = 255; - } - stbi__skip(s, pad); - } - } else { - int rshift=0,gshift=0,bshift=0,ashift=0,rcount=0,gcount=0,bcount=0,acount=0; - int z = 0; - int easy=0; - stbi__skip(s, info.offset - 14 - info.hsz); - if (info.bpp == 24) width = 3 * s->img_x; - else if (info.bpp == 16) width = 2*s->img_x; - else /* bpp = 32 and pad = 0 */ width=0; - pad = (-width) & 3; - if (info.bpp == 24) { - easy = 1; - } else if (info.bpp == 32) { - if (mb == 0xff && mg == 0xff00 && mr == 0x00ff0000 && ma == 0xff000000) - easy = 2; - } - if (!easy) { - if (!mr || !mg || !mb) { STBI_FREE(out); return stbi__errpuc("bad masks", "Corrupt BMP"); } - // right shift amt to put high bit in position #7 - rshift = stbi__high_bit(mr)-7; rcount = stbi__bitcount(mr); - gshift = stbi__high_bit(mg)-7; gcount = stbi__bitcount(mg); - bshift = stbi__high_bit(mb)-7; bcount = stbi__bitcount(mb); - ashift = stbi__high_bit(ma)-7; acount = stbi__bitcount(ma); - } - for (j=0; j < (int) s->img_y; ++j) { - if (easy) { - for (i=0; i < (int) s->img_x; ++i) { - unsigned char a; - out[z+2] = stbi__get8(s); - out[z+1] = stbi__get8(s); - out[z+0] = stbi__get8(s); - z += 3; - a = (easy == 2 ? stbi__get8(s) : 255); - all_a |= a; - if (target == 4) out[z++] = a; - } - } else { - int bpp = info.bpp; - for (i=0; i < (int) s->img_x; ++i) { - stbi__uint32 v = (bpp == 16 ? (stbi__uint32) stbi__get16le(s) : stbi__get32le(s)); - int a; - out[z++] = STBI__BYTECAST(stbi__shiftsigned(v & mr, rshift, rcount)); - out[z++] = STBI__BYTECAST(stbi__shiftsigned(v & mg, gshift, gcount)); - out[z++] = STBI__BYTECAST(stbi__shiftsigned(v & mb, bshift, bcount)); - a = (ma ? stbi__shiftsigned(v & ma, ashift, acount) : 255); - all_a |= a; - if (target == 4) out[z++] = STBI__BYTECAST(a); - } - } - stbi__skip(s, pad); - } - } - - // if alpha channel is all 0s, replace with all 255s - if (target == 4 && all_a == 0) - for (i=4*s->img_x*s->img_y-1; i >= 0; i -= 4) - out[i] = 255; - - if (flip_vertically) { - stbi_uc t; - for (j=0; j < (int) s->img_y>>1; ++j) { - stbi_uc *p1 = out + j *s->img_x*target; - stbi_uc *p2 = out + (s->img_y-1-j)*s->img_x*target; - for (i=0; i < (int) s->img_x*target; ++i) { - t = p1[i], p1[i] = p2[i], p2[i] = t; - } - } - } - - if (req_comp && req_comp != target) { - out = stbi__convert_format(out, target, req_comp, s->img_x, s->img_y); - if (out == NULL) return out; // stbi__convert_format frees input on failure - } - - *x = s->img_x; - *y = s->img_y; - if (comp) *comp = s->img_n; - return out; -} -#endif - -// Targa Truevision - TGA -// by Jonathan Dummer -#ifndef STBI_NO_TGA -// returns STBI_rgb or whatever, 0 on error -static int stbi__tga_get_comp(int bits_per_pixel, int is_grey, int* is_rgb16) -{ - // only RGB or RGBA (incl. 16bit) or grey allowed - if(is_rgb16) *is_rgb16 = 0; - switch(bits_per_pixel) { - case 8: return STBI_grey; - case 16: if(is_grey) return STBI_grey_alpha; - // else: fall-through - case 15: if(is_rgb16) *is_rgb16 = 1; - return STBI_rgb; - case 24: // fall-through - case 32: return bits_per_pixel/8; - default: return 0; - } -} - -static int stbi__tga_info(stbi__context *s, int *x, int *y, int *comp) -{ - int tga_w, tga_h, tga_comp, tga_image_type, tga_bits_per_pixel, tga_colormap_bpp; - int sz, tga_colormap_type; - stbi__get8(s); // discard Offset - tga_colormap_type = stbi__get8(s); // colormap type - if( tga_colormap_type > 1 ) { - stbi__rewind(s); - return 0; // only RGB or indexed allowed - } - tga_image_type = stbi__get8(s); // image type - if ( tga_colormap_type == 1 ) { // colormapped (paletted) image - if (tga_image_type != 1 && tga_image_type != 9) { - stbi__rewind(s); - return 0; - } - stbi__skip(s,4); // skip index of first colormap entry and number of entries - sz = stbi__get8(s); // check bits per palette color entry - if ( (sz != 8) && (sz != 15) && (sz != 16) && (sz != 24) && (sz != 32) ) { - stbi__rewind(s); - return 0; - } - stbi__skip(s,4); // skip image x and y origin - tga_colormap_bpp = sz; - } else { // "normal" image w/o colormap - only RGB or grey allowed, +/- RLE - if ( (tga_image_type != 2) && (tga_image_type != 3) && (tga_image_type != 10) && (tga_image_type != 11) ) { - stbi__rewind(s); - return 0; // only RGB or grey allowed, +/- RLE - } - stbi__skip(s,9); // skip colormap specification and image x/y origin - tga_colormap_bpp = 0; - } - tga_w = stbi__get16le(s); - if( tga_w < 1 ) { - stbi__rewind(s); - return 0; // test width - } - tga_h = stbi__get16le(s); - if( tga_h < 1 ) { - stbi__rewind(s); - return 0; // test height - } - tga_bits_per_pixel = stbi__get8(s); // bits per pixel - stbi__get8(s); // ignore alpha bits - if (tga_colormap_bpp != 0) { - if((tga_bits_per_pixel != 8) && (tga_bits_per_pixel != 16)) { - // when using a colormap, tga_bits_per_pixel is the size of the indexes - // I don't think anything but 8 or 16bit indexes makes sense - stbi__rewind(s); - return 0; - } - tga_comp = stbi__tga_get_comp(tga_colormap_bpp, 0, NULL); - } else { - tga_comp = stbi__tga_get_comp(tga_bits_per_pixel, (tga_image_type == 3) || (tga_image_type == 11), NULL); - } - if(!tga_comp) { - stbi__rewind(s); - return 0; - } - if (x) *x = tga_w; - if (y) *y = tga_h; - if (comp) *comp = tga_comp; - return 1; // seems to have passed everything -} - -static int stbi__tga_test(stbi__context *s) -{ - int res = 0; - int sz, tga_color_type; - stbi__get8(s); // discard Offset - tga_color_type = stbi__get8(s); // color type - if ( tga_color_type > 1 ) goto errorEnd; // only RGB or indexed allowed - sz = stbi__get8(s); // image type - if ( tga_color_type == 1 ) { // colormapped (paletted) image - if (sz != 1 && sz != 9) goto errorEnd; // colortype 1 demands image type 1 or 9 - stbi__skip(s,4); // skip index of first colormap entry and number of entries - sz = stbi__get8(s); // check bits per palette color entry - if ( (sz != 8) && (sz != 15) && (sz != 16) && (sz != 24) && (sz != 32) ) goto errorEnd; - stbi__skip(s,4); // skip image x and y origin - } else { // "normal" image w/o colormap - if ( (sz != 2) && (sz != 3) && (sz != 10) && (sz != 11) ) goto errorEnd; // only RGB or grey allowed, +/- RLE - stbi__skip(s,9); // skip colormap specification and image x/y origin - } - if ( stbi__get16le(s) < 1 ) goto errorEnd; // test width - if ( stbi__get16le(s) < 1 ) goto errorEnd; // test height - sz = stbi__get8(s); // bits per pixel - if ( (tga_color_type == 1) && (sz != 8) && (sz != 16) ) goto errorEnd; // for colormapped images, bpp is size of an index - if ( (sz != 8) && (sz != 15) && (sz != 16) && (sz != 24) && (sz != 32) ) goto errorEnd; - - res = 1; // if we got this far, everything's good and we can return 1 instead of 0 - -errorEnd: - stbi__rewind(s); - return res; -} - -// read 16bit value and convert to 24bit RGB -void stbi__tga_read_rgb16(stbi__context *s, stbi_uc* out) -{ - stbi__uint16 px = stbi__get16le(s); - stbi__uint16 fiveBitMask = 31; - // we have 3 channels with 5bits each - int r = (px >> 10) & fiveBitMask; - int g = (px >> 5) & fiveBitMask; - int b = px & fiveBitMask; - // Note that this saves the data in RGB(A) order, so it doesn't need to be swapped later - out[0] = (r * 255)/31; - out[1] = (g * 255)/31; - out[2] = (b * 255)/31; - - // some people claim that the most significant bit might be used for alpha - // (possibly if an alpha-bit is set in the "image descriptor byte") - // but that only made 16bit test images completely translucent.. - // so let's treat all 15 and 16bit TGAs as RGB with no alpha. -} - -static stbi_uc *stbi__tga_load(stbi__context *s, int *x, int *y, int *comp, int req_comp) -{ - // read in the TGA header stuff - int tga_offset = stbi__get8(s); - int tga_indexed = stbi__get8(s); - int tga_image_type = stbi__get8(s); - int tga_is_RLE = 0; - int tga_palette_start = stbi__get16le(s); - int tga_palette_len = stbi__get16le(s); - int tga_palette_bits = stbi__get8(s); - int tga_x_origin = stbi__get16le(s); - int tga_y_origin = stbi__get16le(s); - int tga_width = stbi__get16le(s); - int tga_height = stbi__get16le(s); - int tga_bits_per_pixel = stbi__get8(s); - int tga_comp, tga_rgb16=0; - int tga_inverted = stbi__get8(s); - // int tga_alpha_bits = tga_inverted & 15; // the 4 lowest bits - unused (useless?) - // image data - unsigned char *tga_data; - unsigned char *tga_palette = NULL; - int i, j; - unsigned char raw_data[4]; - int RLE_count = 0; - int RLE_repeating = 0; - int read_next_pixel = 1; - - // do a tiny bit of precessing - if ( tga_image_type >= 8 ) - { - tga_image_type -= 8; - tga_is_RLE = 1; - } - tga_inverted = 1 - ((tga_inverted >> 5) & 1); - - // If I'm paletted, then I'll use the number of bits from the palette - if ( tga_indexed ) tga_comp = stbi__tga_get_comp(tga_palette_bits, 0, &tga_rgb16); - else tga_comp = stbi__tga_get_comp(tga_bits_per_pixel, (tga_image_type == 3), &tga_rgb16); - - if(!tga_comp) // shouldn't really happen, stbi__tga_test() should have ensured basic consistency - return stbi__errpuc("bad format", "Can't find out TGA pixelformat"); - - // tga info - *x = tga_width; - *y = tga_height; - if (comp) *comp = tga_comp; - - tga_data = (unsigned char*)stbi__malloc( (size_t)tga_width * tga_height * tga_comp ); - if (!tga_data) return stbi__errpuc("outofmem", "Out of memory"); - - // skip to the data's starting position (offset usually = 0) - stbi__skip(s, tga_offset ); - - if ( !tga_indexed && !tga_is_RLE && !tga_rgb16 ) { - for (i=0; i < tga_height; ++i) { - int row = tga_inverted ? tga_height -i - 1 : i; - stbi_uc *tga_row = tga_data + row*tga_width*tga_comp; - stbi__getn(s, tga_row, tga_width * tga_comp); - } - } else { - // do I need to load a palette? - if ( tga_indexed) - { - // any data to skip? (offset usually = 0) - stbi__skip(s, tga_palette_start ); - // load the palette - tga_palette = (unsigned char*)stbi__malloc( tga_palette_len * tga_comp ); - if (!tga_palette) { - STBI_FREE(tga_data); - return stbi__errpuc("outofmem", "Out of memory"); - } - if (tga_rgb16) { - stbi_uc *pal_entry = tga_palette; - STBI_ASSERT(tga_comp == STBI_rgb); - for (i=0; i < tga_palette_len; ++i) { - stbi__tga_read_rgb16(s, pal_entry); - pal_entry += tga_comp; - } - } else if (!stbi__getn(s, tga_palette, tga_palette_len * tga_comp)) { - STBI_FREE(tga_data); - STBI_FREE(tga_palette); - return stbi__errpuc("bad palette", "Corrupt TGA"); - } - } - // load the data - for (i=0; i < tga_width * tga_height; ++i) - { - // if I'm in RLE mode, do I need to get a RLE stbi__pngchunk? - if ( tga_is_RLE ) - { - if ( RLE_count == 0 ) - { - // yep, get the next byte as a RLE command - int RLE_cmd = stbi__get8(s); - RLE_count = 1 + (RLE_cmd & 127); - RLE_repeating = RLE_cmd >> 7; - read_next_pixel = 1; - } else if ( !RLE_repeating ) - { - read_next_pixel = 1; - } - } else - { - read_next_pixel = 1; - } - // OK, if I need to read a pixel, do it now - if ( read_next_pixel ) - { - // load however much data we did have - if ( tga_indexed ) - { - // read in index, then perform the lookup - int pal_idx = (tga_bits_per_pixel == 8) ? stbi__get8(s) : stbi__get16le(s); - if ( pal_idx >= tga_palette_len ) { - // invalid index - pal_idx = 0; - } - pal_idx *= tga_comp; - for (j = 0; j < tga_comp; ++j) { - raw_data[j] = tga_palette[pal_idx+j]; - } - } else if(tga_rgb16) { - STBI_ASSERT(tga_comp == STBI_rgb); - stbi__tga_read_rgb16(s, raw_data); - } else { - // read in the data raw - for (j = 0; j < tga_comp; ++j) { - raw_data[j] = stbi__get8(s); - } - } - // clear the reading flag for the next pixel - read_next_pixel = 0; - } // end of reading a pixel - - // copy data - for (j = 0; j < tga_comp; ++j) - tga_data[i*tga_comp+j] = raw_data[j]; - - // in case we're in RLE mode, keep counting down - --RLE_count; - } - // do I need to invert the image? - if ( tga_inverted ) - { - for (j = 0; j*2 < tga_height; ++j) - { - int index1 = j * tga_width * tga_comp; - int index2 = (tga_height - 1 - j) * tga_width * tga_comp; - for (i = tga_width * tga_comp; i > 0; --i) - { - unsigned char temp = tga_data[index1]; - tga_data[index1] = tga_data[index2]; - tga_data[index2] = temp; - ++index1; - ++index2; - } - } - } - // clear my palette, if I had one - if ( tga_palette != NULL ) - { - STBI_FREE( tga_palette ); - } - } - - // swap RGB - if the source data was RGB16, it already is in the right order - if (tga_comp >= 3 && !tga_rgb16) - { - unsigned char* tga_pixel = tga_data; - for (i=0; i < tga_width * tga_height; ++i) - { - unsigned char temp = tga_pixel[0]; - tga_pixel[0] = tga_pixel[2]; - tga_pixel[2] = temp; - tga_pixel += tga_comp; - } - } - - // convert to target component count - if (req_comp && req_comp != tga_comp) - tga_data = stbi__convert_format(tga_data, tga_comp, req_comp, tga_width, tga_height); - - // the things I do to get rid of an error message, and yet keep - // Microsoft's C compilers happy... [8^( - tga_palette_start = tga_palette_len = tga_palette_bits = - tga_x_origin = tga_y_origin = 0; - // OK, done - return tga_data; -} -#endif - -// ************************************************************************************************* -// Photoshop PSD loader -- PD by Thatcher Ulrich, integration by Nicolas Schulz, tweaked by STB - -#ifndef STBI_NO_PSD -static int stbi__psd_test(stbi__context *s) -{ - int r = (stbi__get32be(s) == 0x38425053); - stbi__rewind(s); - return r; -} - -static stbi_uc *stbi__psd_load(stbi__context *s, int *x, int *y, int *comp, int req_comp) -{ - int pixelCount; - int channelCount, compression; - int channel, i, count, len; - int bitdepth; - int w,h; - stbi_uc *out; - - // Check identifier - if (stbi__get32be(s) != 0x38425053) // "8BPS" - return stbi__errpuc("not PSD", "Corrupt PSD image"); - - // Check file type version. - if (stbi__get16be(s) != 1) - return stbi__errpuc("wrong version", "Unsupported version of PSD image"); - - // Skip 6 reserved bytes. - stbi__skip(s, 6 ); - - // Read the number of channels (R, G, B, A, etc). - channelCount = stbi__get16be(s); - if (channelCount < 0 || channelCount > 16) - return stbi__errpuc("wrong channel count", "Unsupported number of channels in PSD image"); - - // Read the rows and columns of the image. - h = stbi__get32be(s); - w = stbi__get32be(s); - - // Make sure the depth is 8 bits. - bitdepth = stbi__get16be(s); - if (bitdepth != 8 && bitdepth != 16) - return stbi__errpuc("unsupported bit depth", "PSD bit depth is not 8 or 16 bit"); - - // Make sure the color mode is RGB. - // Valid options are: - // 0: Bitmap - // 1: Grayscale - // 2: Indexed color - // 3: RGB color - // 4: CMYK color - // 7: Multichannel - // 8: Duotone - // 9: Lab color - if (stbi__get16be(s) != 3) - return stbi__errpuc("wrong color format", "PSD is not in RGB color format"); - - // Skip the Mode Data. (It's the palette for indexed color; other info for other modes.) - stbi__skip(s,stbi__get32be(s) ); - - // Skip the image resources. (resolution, pen tool paths, etc) - stbi__skip(s, stbi__get32be(s) ); - - // Skip the reserved data. - stbi__skip(s, stbi__get32be(s) ); - - // Find out if the data is compressed. - // Known values: - // 0: no compression - // 1: RLE compressed - compression = stbi__get16be(s); - if (compression > 1) - return stbi__errpuc("bad compression", "PSD has an unknown compression format"); - - // Create the destination image. - out = (stbi_uc *) stbi__malloc(4 * w*h); - if (!out) return stbi__errpuc("outofmem", "Out of memory"); - pixelCount = w*h; - - // Initialize the data to zero. - //memset( out, 0, pixelCount * 4 ); - - // Finally, the image data. - if (compression) { - // RLE as used by .PSD and .TIFF - // Loop until you get the number of unpacked bytes you are expecting: - // Read the next source byte into n. - // If n is between 0 and 127 inclusive, copy the next n+1 bytes literally. - // Else if n is between -127 and -1 inclusive, copy the next byte -n+1 times. - // Else if n is 128, noop. - // Endloop - - // The RLE-compressed data is preceeded by a 2-byte data count for each row in the data, - // which we're going to just skip. - stbi__skip(s, h * channelCount * 2 ); - - // Read the RLE data by channel. - for (channel = 0; channel < 4; channel++) { - stbi_uc *p; - - p = out+channel; - if (channel >= channelCount) { - // Fill this channel with default data. - for (i = 0; i < pixelCount; i++, p += 4) - *p = (channel == 3 ? 255 : 0); - } else { - // Read the RLE data. - count = 0; - while (count < pixelCount) { - len = stbi__get8(s); - if (len == 128) { - // No-op. - } else if (len < 128) { - // Copy next len+1 bytes literally. - len++; - count += len; - while (len) { - *p = stbi__get8(s); - p += 4; - len--; - } - } else if (len > 128) { - stbi_uc val; - // Next -len+1 bytes in the dest are replicated from next source byte. - // (Interpret len as a negative 8-bit int.) - len ^= 0x0FF; - len += 2; - val = stbi__get8(s); - count += len; - while (len) { - *p = val; - p += 4; - len--; - } - } - } - } - } - - } else { - // We're at the raw image data. It's each channel in order (Red, Green, Blue, Alpha, ...) - // where each channel consists of an 8-bit value for each pixel in the image. - - // Read the data by channel. - for (channel = 0; channel < 4; channel++) { - stbi_uc *p; - - p = out + channel; - if (channel >= channelCount) { - // Fill this channel with default data. - stbi_uc val = channel == 3 ? 255 : 0; - for (i = 0; i < pixelCount; i++, p += 4) - *p = val; - } else { - // Read the data. - if (bitdepth == 16) { - for (i = 0; i < pixelCount; i++, p += 4) - *p = (stbi_uc) (stbi__get16be(s) >> 8); - } else { - for (i = 0; i < pixelCount; i++, p += 4) - *p = stbi__get8(s); - } - } - } - } - - if (channelCount >= 4) { - for (i=0; i < w*h; ++i) { - unsigned char *pixel = out + 4*i; - if (pixel[3] != 0 && pixel[3] != 255) { - // remove weird white matte from PSD - float a = pixel[3] / 255.0f; - float ra = 1.0f / a; - float inv_a = 255.0f * (1 - ra); - pixel[0] = (unsigned char) (pixel[0]*ra + inv_a); - pixel[1] = (unsigned char) (pixel[1]*ra + inv_a); - pixel[2] = (unsigned char) (pixel[2]*ra + inv_a); - } - } - } - - if (req_comp && req_comp != 4) { - out = stbi__convert_format(out, 4, req_comp, w, h); - if (out == NULL) return out; // stbi__convert_format frees input on failure - } - - if (comp) *comp = 4; - *y = h; - *x = w; - - return out; -} -#endif - -// ************************************************************************************************* -// Softimage PIC loader -// by Tom Seddon -// -// See http://softimage.wiki.softimage.com/index.php/INFO:_PIC_file_format -// See http://ozviz.wasp.uwa.edu.au/~pbourke/dataformats/softimagepic/ - -#ifndef STBI_NO_PIC -static int stbi__pic_is4(stbi__context *s,const char *str) -{ - int i; - for (i=0; i<4; ++i) - if (stbi__get8(s) != (stbi_uc)str[i]) - return 0; - - return 1; -} - -static int stbi__pic_test_core(stbi__context *s) -{ - int i; - - if (!stbi__pic_is4(s,"\x53\x80\xF6\x34")) - return 0; - - for(i=0;i<84;++i) - stbi__get8(s); - - if (!stbi__pic_is4(s,"PICT")) - return 0; - - return 1; -} - -typedef struct -{ - stbi_uc size,type,channel; -} stbi__pic_packet; - -static stbi_uc *stbi__readval(stbi__context *s, int channel, stbi_uc *dest) -{ - int mask=0x80, i; - - for (i=0; i<4; ++i, mask>>=1) { - if (channel & mask) { - if (stbi__at_eof(s)) return stbi__errpuc("bad file","PIC file too short"); - dest[i]=stbi__get8(s); - } - } - - return dest; -} - -static void stbi__copyval(int channel,stbi_uc *dest,const stbi_uc *src) -{ - int mask=0x80,i; - - for (i=0;i<4; ++i, mask>>=1) - if (channel&mask) - dest[i]=src[i]; -} - -static stbi_uc *stbi__pic_load_core(stbi__context *s,int width,int height,int *comp, stbi_uc *result) -{ - int act_comp=0,num_packets=0,y,chained; - stbi__pic_packet packets[10]; - - // this will (should...) cater for even some bizarre stuff like having data - // for the same channel in multiple packets. - do { - stbi__pic_packet *packet; - - if (num_packets==sizeof(packets)/sizeof(packets[0])) - return stbi__errpuc("bad format","too many packets"); - - packet = &packets[num_packets++]; - - chained = stbi__get8(s); - packet->size = stbi__get8(s); - packet->type = stbi__get8(s); - packet->channel = stbi__get8(s); - - act_comp |= packet->channel; - - if (stbi__at_eof(s)) return stbi__errpuc("bad file","file too short (reading packets)"); - if (packet->size != 8) return stbi__errpuc("bad format","packet isn't 8bpp"); - } while (chained); - - *comp = (act_comp & 0x10 ? 4 : 3); // has alpha channel? - - for(y=0; ytype) { - default: - return stbi__errpuc("bad format","packet has bad compression type"); - - case 0: {//uncompressed - int x; - - for(x=0;xchannel,dest)) - return 0; - break; - } - - case 1://Pure RLE - { - int left=width, i; - - while (left>0) { - stbi_uc count,value[4]; - - count=stbi__get8(s); - if (stbi__at_eof(s)) return stbi__errpuc("bad file","file too short (pure read count)"); - - if (count > left) - count = (stbi_uc) left; - - if (!stbi__readval(s,packet->channel,value)) return 0; - - for(i=0; ichannel,dest,value); - left -= count; - } - } - break; - - case 2: {//Mixed RLE - int left=width; - while (left>0) { - int count = stbi__get8(s), i; - if (stbi__at_eof(s)) return stbi__errpuc("bad file","file too short (mixed read count)"); - - if (count >= 128) { // Repeated - stbi_uc value[4]; - - if (count==128) - count = stbi__get16be(s); - else - count -= 127; - if (count > left) - return stbi__errpuc("bad file","scanline overrun"); - - if (!stbi__readval(s,packet->channel,value)) - return 0; - - for(i=0;ichannel,dest,value); - } else { // Raw - ++count; - if (count>left) return stbi__errpuc("bad file","scanline overrun"); - - for(i=0;ichannel,dest)) - return 0; - } - left-=count; - } - break; - } - } - } - } - - return result; -} - -static stbi_uc *stbi__pic_load(stbi__context *s,int *px,int *py,int *comp,int req_comp) -{ - stbi_uc *result; - int i, x,y; - - for (i=0; i<92; ++i) - stbi__get8(s); - - x = stbi__get16be(s); - y = stbi__get16be(s); - if (stbi__at_eof(s)) return stbi__errpuc("bad file","file too short (pic header)"); - if ((1 << 28) / x < y) return stbi__errpuc("too large", "Image too large to decode"); - - stbi__get32be(s); //skip `ratio' - stbi__get16be(s); //skip `fields' - stbi__get16be(s); //skip `pad' - - // intermediate buffer is RGBA - result = (stbi_uc *) stbi__malloc(x*y*4); - memset(result, 0xff, x*y*4); - - if (!stbi__pic_load_core(s,x,y,comp, result)) { - STBI_FREE(result); - result=0; - } - *px = x; - *py = y; - if (req_comp == 0) req_comp = *comp; - result=stbi__convert_format(result,4,req_comp,x,y); - - return result; -} - -static int stbi__pic_test(stbi__context *s) -{ - int r = stbi__pic_test_core(s); - stbi__rewind(s); - return r; -} -#endif - -// ************************************************************************************************* -// GIF loader -- public domain by Jean-Marc Lienher -- simplified/shrunk by stb - -#ifndef STBI_NO_GIF -typedef struct -{ - stbi__int16 prefix; - stbi_uc first; - stbi_uc suffix; -} stbi__gif_lzw; - -typedef struct -{ - int w,h; - stbi_uc *out, *old_out; // output buffer (always 4 components) - int flags, bgindex, ratio, transparent, eflags, delay; - stbi_uc pal[256][4]; - stbi_uc lpal[256][4]; - stbi__gif_lzw codes[4096]; - stbi_uc *color_table; - int parse, step; - int lflags; - int start_x, start_y; - int max_x, max_y; - int cur_x, cur_y; - int line_size; -} stbi__gif; - -static int stbi__gif_test_raw(stbi__context *s) -{ - int sz; - if (stbi__get8(s) != 'G' || stbi__get8(s) != 'I' || stbi__get8(s) != 'F' || stbi__get8(s) != '8') return 0; - sz = stbi__get8(s); - if (sz != '9' && sz != '7') return 0; - if (stbi__get8(s) != 'a') return 0; - return 1; -} - -static int stbi__gif_test(stbi__context *s) -{ - int r = stbi__gif_test_raw(s); - stbi__rewind(s); - return r; -} - -static void stbi__gif_parse_colortable(stbi__context *s, stbi_uc pal[256][4], int num_entries, int transp) -{ - int i; - for (i=0; i < num_entries; ++i) { - pal[i][2] = stbi__get8(s); - pal[i][1] = stbi__get8(s); - pal[i][0] = stbi__get8(s); - pal[i][3] = transp == i ? 0 : 255; - } -} - -static int stbi__gif_header(stbi__context *s, stbi__gif *g, int *comp, int is_info) -{ - stbi_uc version; - if (stbi__get8(s) != 'G' || stbi__get8(s) != 'I' || stbi__get8(s) != 'F' || stbi__get8(s) != '8') - return stbi__err("not GIF", "Corrupt GIF"); - - version = stbi__get8(s); - if (version != '7' && version != '9') return stbi__err("not GIF", "Corrupt GIF"); - if (stbi__get8(s) != 'a') return stbi__err("not GIF", "Corrupt GIF"); - - stbi__g_failure_reason = ""; - g->w = stbi__get16le(s); - g->h = stbi__get16le(s); - g->flags = stbi__get8(s); - g->bgindex = stbi__get8(s); - g->ratio = stbi__get8(s); - g->transparent = -1; - - if (comp != 0) *comp = 4; // can't actually tell whether it's 3 or 4 until we parse the comments - - if (is_info) return 1; - - if (g->flags & 0x80) - stbi__gif_parse_colortable(s,g->pal, 2 << (g->flags & 7), -1); - - return 1; -} - -static int stbi__gif_info_raw(stbi__context *s, int *x, int *y, int *comp) -{ - stbi__gif* g = (stbi__gif*) stbi__malloc(sizeof(stbi__gif)); - if (!stbi__gif_header(s, g, comp, 1)) { - STBI_FREE(g); - stbi__rewind( s ); - return 0; - } - if (x) *x = g->w; - if (y) *y = g->h; - STBI_FREE(g); - return 1; -} - -static void stbi__out_gif_code(stbi__gif *g, stbi__uint16 code) -{ - stbi_uc *p, *c; - - // recurse to decode the prefixes, since the linked-list is backwards, - // and working backwards through an interleaved image would be nasty - if (g->codes[code].prefix >= 0) - stbi__out_gif_code(g, g->codes[code].prefix); - - if (g->cur_y >= g->max_y) return; - - p = &g->out[g->cur_x + g->cur_y]; - c = &g->color_table[g->codes[code].suffix * 4]; - - if (c[3] >= 128) { - p[0] = c[2]; - p[1] = c[1]; - p[2] = c[0]; - p[3] = c[3]; - } - g->cur_x += 4; - - if (g->cur_x >= g->max_x) { - g->cur_x = g->start_x; - g->cur_y += g->step; - - while (g->cur_y >= g->max_y && g->parse > 0) { - g->step = (1 << g->parse) * g->line_size; - g->cur_y = g->start_y + (g->step >> 1); - --g->parse; - } - } -} - -static stbi_uc *stbi__process_gif_raster(stbi__context *s, stbi__gif *g) -{ - stbi_uc lzw_cs; - stbi__int32 len, init_code; - stbi__uint32 first; - stbi__int32 codesize, codemask, avail, oldcode, bits, valid_bits, clear; - stbi__gif_lzw *p; - - lzw_cs = stbi__get8(s); - if (lzw_cs > 12) return NULL; - clear = 1 << lzw_cs; - first = 1; - codesize = lzw_cs + 1; - codemask = (1 << codesize) - 1; - bits = 0; - valid_bits = 0; - for (init_code = 0; init_code < clear; init_code++) { - g->codes[init_code].prefix = -1; - g->codes[init_code].first = (stbi_uc) init_code; - g->codes[init_code].suffix = (stbi_uc) init_code; - } - - // support no starting clear code - avail = clear+2; - oldcode = -1; - - len = 0; - for(;;) { - if (valid_bits < codesize) { - if (len == 0) { - len = stbi__get8(s); // start new block - if (len == 0) - return g->out; - } - --len; - bits |= (stbi__int32) stbi__get8(s) << valid_bits; - valid_bits += 8; - } else { - stbi__int32 code = bits & codemask; - bits >>= codesize; - valid_bits -= codesize; - // @OPTIMIZE: is there some way we can accelerate the non-clear path? - if (code == clear) { // clear code - codesize = lzw_cs + 1; - codemask = (1 << codesize) - 1; - avail = clear + 2; - oldcode = -1; - first = 0; - } else if (code == clear + 1) { // end of stream code - stbi__skip(s, len); - while ((len = stbi__get8(s)) > 0) - stbi__skip(s,len); - return g->out; - } else if (code <= avail) { - if (first) return stbi__errpuc("no clear code", "Corrupt GIF"); - - if (oldcode >= 0) { - p = &g->codes[avail++]; - if (avail > 4096) return stbi__errpuc("too many codes", "Corrupt GIF"); - p->prefix = (stbi__int16) oldcode; - p->first = g->codes[oldcode].first; - p->suffix = (code == avail) ? p->first : g->codes[code].first; - } else if (code == avail) - return stbi__errpuc("illegal code in raster", "Corrupt GIF"); - - stbi__out_gif_code(g, (stbi__uint16) code); - - if ((avail & codemask) == 0 && avail <= 0x0FFF) { - codesize++; - codemask = (1 << codesize) - 1; - } - - oldcode = code; - } else { - return stbi__errpuc("illegal code in raster", "Corrupt GIF"); - } - } - } -} - -static void stbi__fill_gif_background(stbi__gif *g, int x0, int y0, int x1, int y1) -{ - int x, y; - stbi_uc *c = g->pal[g->bgindex]; - for (y = y0; y < y1; y += 4 * g->w) { - for (x = x0; x < x1; x += 4) { - stbi_uc *p = &g->out[y + x]; - p[0] = c[2]; - p[1] = c[1]; - p[2] = c[0]; - p[3] = 0; - } - } -} - -// this function is designed to support animated gifs, although stb_image doesn't support it -static stbi_uc *stbi__gif_load_next(stbi__context *s, stbi__gif *g, int *comp, int req_comp) -{ - int i; - stbi_uc *prev_out = 0; - - if (g->out == 0 && !stbi__gif_header(s, g, comp,0)) - return 0; // stbi__g_failure_reason set by stbi__gif_header - - prev_out = g->out; - g->out = (stbi_uc *) stbi__malloc(4 * g->w * g->h); - if (g->out == 0) return stbi__errpuc("outofmem", "Out of memory"); - - switch ((g->eflags & 0x1C) >> 2) { - case 0: // unspecified (also always used on 1st frame) - stbi__fill_gif_background(g, 0, 0, 4 * g->w, 4 * g->w * g->h); - break; - case 1: // do not dispose - if (prev_out) memcpy(g->out, prev_out, 4 * g->w * g->h); - g->old_out = prev_out; - break; - case 2: // dispose to background - if (prev_out) memcpy(g->out, prev_out, 4 * g->w * g->h); - stbi__fill_gif_background(g, g->start_x, g->start_y, g->max_x, g->max_y); - break; - case 3: // dispose to previous - if (g->old_out) { - for (i = g->start_y; i < g->max_y; i += 4 * g->w) - memcpy(&g->out[i + g->start_x], &g->old_out[i + g->start_x], g->max_x - g->start_x); - } - break; - } - - for (;;) { - switch (stbi__get8(s)) { - case 0x2C: /* Image Descriptor */ - { - int prev_trans = -1; - stbi__int32 x, y, w, h; - stbi_uc *o; - - x = stbi__get16le(s); - y = stbi__get16le(s); - w = stbi__get16le(s); - h = stbi__get16le(s); - if (((x + w) > (g->w)) || ((y + h) > (g->h))) - return stbi__errpuc("bad Image Descriptor", "Corrupt GIF"); - - g->line_size = g->w * 4; - g->start_x = x * 4; - g->start_y = y * g->line_size; - g->max_x = g->start_x + w * 4; - g->max_y = g->start_y + h * g->line_size; - g->cur_x = g->start_x; - g->cur_y = g->start_y; - - g->lflags = stbi__get8(s); - - if (g->lflags & 0x40) { - g->step = 8 * g->line_size; // first interlaced spacing - g->parse = 3; - } else { - g->step = g->line_size; - g->parse = 0; - } - - if (g->lflags & 0x80) { - stbi__gif_parse_colortable(s,g->lpal, 2 << (g->lflags & 7), g->eflags & 0x01 ? g->transparent : -1); - g->color_table = (stbi_uc *) g->lpal; - } else if (g->flags & 0x80) { - if (g->transparent >= 0 && (g->eflags & 0x01)) { - prev_trans = g->pal[g->transparent][3]; - g->pal[g->transparent][3] = 0; - } - g->color_table = (stbi_uc *) g->pal; - } else - return stbi__errpuc("missing color table", "Corrupt GIF"); - - o = stbi__process_gif_raster(s, g); - if (o == NULL) return NULL; - - if (prev_trans != -1) - g->pal[g->transparent][3] = (stbi_uc) prev_trans; - - return o; - } - - case 0x21: // Comment Extension. - { - int len; - if (stbi__get8(s) == 0xF9) { // Graphic Control Extension. - len = stbi__get8(s); - if (len == 4) { - g->eflags = stbi__get8(s); - g->delay = stbi__get16le(s); - g->transparent = stbi__get8(s); - } else { - stbi__skip(s, len); - break; - } - } - while ((len = stbi__get8(s)) != 0) - stbi__skip(s, len); - break; - } - - case 0x3B: // gif stream termination code - return (stbi_uc *) s; // using '1' causes warning on some compilers - - default: - return stbi__errpuc("unknown code", "Corrupt GIF"); - } - } - - STBI_NOTUSED(req_comp); -} - -static stbi_uc *stbi__gif_load(stbi__context *s, int *x, int *y, int *comp, int req_comp) -{ - stbi_uc *u = 0; - stbi__gif* g = (stbi__gif*) stbi__malloc(sizeof(stbi__gif)); - memset(g, 0, sizeof(*g)); - - u = stbi__gif_load_next(s, g, comp, req_comp); - if (u == (stbi_uc *) s) u = 0; // end of animated gif marker - if (u) { - *x = g->w; - *y = g->h; - if (req_comp && req_comp != 4) - u = stbi__convert_format(u, 4, req_comp, g->w, g->h); - } - else if (g->out) - STBI_FREE(g->out); - STBI_FREE(g); - return u; -} - -static int stbi__gif_info(stbi__context *s, int *x, int *y, int *comp) -{ - return stbi__gif_info_raw(s,x,y,comp); -} -#endif - -// ************************************************************************************************* -// Radiance RGBE HDR loader -// originally by Nicolas Schulz -#ifndef STBI_NO_HDR -static int stbi__hdr_test_core(stbi__context *s) -{ - const char *signature = "#?RADIANCE\n"; - int i; - for (i=0; signature[i]; ++i) - if (stbi__get8(s) != signature[i]) - return 0; - return 1; -} - -static int stbi__hdr_test(stbi__context* s) -{ - int r = stbi__hdr_test_core(s); - stbi__rewind(s); - return r; -} - -#define STBI__HDR_BUFLEN 1024 -static char *stbi__hdr_gettoken(stbi__context *z, char *buffer) -{ - int len=0; - char c = '\0'; - - c = (char) stbi__get8(z); - - while (!stbi__at_eof(z) && c != '\n') { - buffer[len++] = c; - if (len == STBI__HDR_BUFLEN-1) { - // flush to end of line - while (!stbi__at_eof(z) && stbi__get8(z) != '\n') - ; - break; - } - c = (char) stbi__get8(z); - } - - buffer[len] = 0; - return buffer; -} - -static void stbi__hdr_convert(float *output, stbi_uc *input, int req_comp) -{ - if ( input[3] != 0 ) { - float f1; - // Exponent - f1 = (float) ldexp(1.0f, input[3] - (int)(128 + 8)); - if (req_comp <= 2) - output[0] = (input[0] + input[1] + input[2]) * f1 / 3; - else { - output[0] = input[0] * f1; - output[1] = input[1] * f1; - output[2] = input[2] * f1; - } - if (req_comp == 2) output[1] = 1; - if (req_comp == 4) output[3] = 1; - } else { - switch (req_comp) { - case 4: output[3] = 1; /* fallthrough */ - case 3: output[0] = output[1] = output[2] = 0; - break; - case 2: output[1] = 1; /* fallthrough */ - case 1: output[0] = 0; - break; - } - } -} - -static float *stbi__hdr_load(stbi__context *s, int *x, int *y, int *comp, int req_comp) -{ - char buffer[STBI__HDR_BUFLEN]; - char *token; - int valid = 0; - int width, height; - stbi_uc *scanline; - float *hdr_data; - int len; - unsigned char count, value; - int i, j, k, c1,c2, z; - - - // Check identifier - if (strcmp(stbi__hdr_gettoken(s,buffer), "#?RADIANCE") != 0) - return stbi__errpf("not HDR", "Corrupt HDR image"); - - // Parse header - for(;;) { - token = stbi__hdr_gettoken(s,buffer); - if (token[0] == 0) break; - if (strcmp(token, "FORMAT=32-bit_rle_rgbe") == 0) valid = 1; - } - - if (!valid) return stbi__errpf("unsupported format", "Unsupported HDR format"); - - // Parse width and height - // can't use sscanf() if we're not using stdio! - token = stbi__hdr_gettoken(s,buffer); - if (strncmp(token, "-Y ", 3)) return stbi__errpf("unsupported data layout", "Unsupported HDR format"); - token += 3; - height = (int) strtol(token, &token, 10); - while (*token == ' ') ++token; - if (strncmp(token, "+X ", 3)) return stbi__errpf("unsupported data layout", "Unsupported HDR format"); - token += 3; - width = (int) strtol(token, NULL, 10); - - *x = width; - *y = height; - - if (comp) *comp = 3; - if (req_comp == 0) req_comp = 3; - - // Read data - hdr_data = (float *) stbi__malloc(height * width * req_comp * sizeof(float)); - - // Load image data - // image data is stored as some number of sca - if ( width < 8 || width >= 32768) { - // Read flat data - for (j=0; j < height; ++j) { - for (i=0; i < width; ++i) { - stbi_uc rgbe[4]; - main_decode_loop: - stbi__getn(s, rgbe, 4); - stbi__hdr_convert(hdr_data + j * width * req_comp + i * req_comp, rgbe, req_comp); - } - } - } else { - // Read RLE-encoded data - scanline = NULL; - - for (j = 0; j < height; ++j) { - c1 = stbi__get8(s); - c2 = stbi__get8(s); - len = stbi__get8(s); - if (c1 != 2 || c2 != 2 || (len & 0x80)) { - // not run-length encoded, so we have to actually use THIS data as a decoded - // pixel (note this can't be a valid pixel--one of RGB must be >= 128) - stbi_uc rgbe[4]; - rgbe[0] = (stbi_uc) c1; - rgbe[1] = (stbi_uc) c2; - rgbe[2] = (stbi_uc) len; - rgbe[3] = (stbi_uc) stbi__get8(s); - stbi__hdr_convert(hdr_data, rgbe, req_comp); - i = 1; - j = 0; - STBI_FREE(scanline); - goto main_decode_loop; // yes, this makes no sense - } - len <<= 8; - len |= stbi__get8(s); - if (len != width) { STBI_FREE(hdr_data); STBI_FREE(scanline); return stbi__errpf("invalid decoded scanline length", "corrupt HDR"); } - if (scanline == NULL) scanline = (stbi_uc *) stbi__malloc(width * 4); - - for (k = 0; k < 4; ++k) { - i = 0; - while (i < width) { - count = stbi__get8(s); - if (count > 128) { - // Run - value = stbi__get8(s); - count -= 128; - for (z = 0; z < count; ++z) - scanline[i++ * 4 + k] = value; - } else { - // Dump - for (z = 0; z < count; ++z) - scanline[i++ * 4 + k] = stbi__get8(s); - } - } - } - for (i=0; i < width; ++i) - stbi__hdr_convert(hdr_data+(j*width + i)*req_comp, scanline + i*4, req_comp); - } - STBI_FREE(scanline); - } - - return hdr_data; -} - -static int stbi__hdr_info(stbi__context *s, int *x, int *y, int *comp) -{ - char buffer[STBI__HDR_BUFLEN]; - char *token; - int valid = 0; - - if (stbi__hdr_test(s) == 0) { - stbi__rewind( s ); - return 0; - } - - for(;;) { - token = stbi__hdr_gettoken(s,buffer); - if (token[0] == 0) break; - if (strcmp(token, "FORMAT=32-bit_rle_rgbe") == 0) valid = 1; - } - - if (!valid) { - stbi__rewind( s ); - return 0; - } - token = stbi__hdr_gettoken(s,buffer); - if (strncmp(token, "-Y ", 3)) { - stbi__rewind( s ); - return 0; - } - token += 3; - *y = (int) strtol(token, &token, 10); - while (*token == ' ') ++token; - if (strncmp(token, "+X ", 3)) { - stbi__rewind( s ); - return 0; - } - token += 3; - *x = (int) strtol(token, NULL, 10); - *comp = 3; - return 1; -} -#endif // STBI_NO_HDR - -#ifndef STBI_NO_BMP -static int stbi__bmp_info(stbi__context *s, int *x, int *y, int *comp) -{ - void *p; - stbi__bmp_data info; - - info.all_a = 255; - p = stbi__bmp_parse_header(s, &info); - stbi__rewind( s ); - if (p == NULL) - return 0; - *x = s->img_x; - *y = s->img_y; - *comp = info.ma ? 4 : 3; - return 1; -} -#endif - -#ifndef STBI_NO_PSD -static int stbi__psd_info(stbi__context *s, int *x, int *y, int *comp) -{ - int channelCount; - if (stbi__get32be(s) != 0x38425053) { - stbi__rewind( s ); - return 0; - } - if (stbi__get16be(s) != 1) { - stbi__rewind( s ); - return 0; - } - stbi__skip(s, 6); - channelCount = stbi__get16be(s); - if (channelCount < 0 || channelCount > 16) { - stbi__rewind( s ); - return 0; - } - *y = stbi__get32be(s); - *x = stbi__get32be(s); - if (stbi__get16be(s) != 8) { - stbi__rewind( s ); - return 0; - } - if (stbi__get16be(s) != 3) { - stbi__rewind( s ); - return 0; - } - *comp = 4; - return 1; -} -#endif - -#ifndef STBI_NO_PIC -static int stbi__pic_info(stbi__context *s, int *x, int *y, int *comp) -{ - int act_comp=0,num_packets=0,chained; - stbi__pic_packet packets[10]; - - if (!stbi__pic_is4(s,"\x53\x80\xF6\x34")) { - stbi__rewind(s); - return 0; - } - - stbi__skip(s, 88); - - *x = stbi__get16be(s); - *y = stbi__get16be(s); - if (stbi__at_eof(s)) { - stbi__rewind( s); - return 0; - } - if ( (*x) != 0 && (1 << 28) / (*x) < (*y)) { - stbi__rewind( s ); - return 0; - } - - stbi__skip(s, 8); - - do { - stbi__pic_packet *packet; - - if (num_packets==sizeof(packets)/sizeof(packets[0])) - return 0; - - packet = &packets[num_packets++]; - chained = stbi__get8(s); - packet->size = stbi__get8(s); - packet->type = stbi__get8(s); - packet->channel = stbi__get8(s); - act_comp |= packet->channel; - - if (stbi__at_eof(s)) { - stbi__rewind( s ); - return 0; - } - if (packet->size != 8) { - stbi__rewind( s ); - return 0; - } - } while (chained); - - *comp = (act_comp & 0x10 ? 4 : 3); - - return 1; -} -#endif - -// ************************************************************************************************* -// Portable Gray Map and Portable Pixel Map loader -// by Ken Miller -// -// PGM: http://netpbm.sourceforge.net/doc/pgm.html -// PPM: http://netpbm.sourceforge.net/doc/ppm.html -// -// Known limitations: -// Does not support comments in the header section -// Does not support ASCII image data (formats P2 and P3) -// Does not support 16-bit-per-channel - -#ifndef STBI_NO_PNM - -static int stbi__pnm_test(stbi__context *s) -{ - char p, t; - p = (char) stbi__get8(s); - t = (char) stbi__get8(s); - if (p != 'P' || (t != '5' && t != '6')) { - stbi__rewind( s ); - return 0; - } - return 1; -} - -static stbi_uc *stbi__pnm_load(stbi__context *s, int *x, int *y, int *comp, int req_comp) -{ - stbi_uc *out; - if (!stbi__pnm_info(s, (int *)&s->img_x, (int *)&s->img_y, (int *)&s->img_n)) - return 0; - *x = s->img_x; - *y = s->img_y; - *comp = s->img_n; - - out = (stbi_uc *) stbi__malloc(s->img_n * s->img_x * s->img_y); - if (!out) return stbi__errpuc("outofmem", "Out of memory"); - stbi__getn(s, out, s->img_n * s->img_x * s->img_y); - - if (req_comp && req_comp != s->img_n) { - out = stbi__convert_format(out, s->img_n, req_comp, s->img_x, s->img_y); - if (out == NULL) return out; // stbi__convert_format frees input on failure - } - return out; -} - -static int stbi__pnm_isspace(char c) -{ - return c == ' ' || c == '\t' || c == '\n' || c == '\v' || c == '\f' || c == '\r'; -} - -static void stbi__pnm_skip_whitespace(stbi__context *s, char *c) -{ - for (;;) { - while (!stbi__at_eof(s) && stbi__pnm_isspace(*c)) - *c = (char) stbi__get8(s); - - if (stbi__at_eof(s) || *c != '#') - break; - - while (!stbi__at_eof(s) && *c != '\n' && *c != '\r' ) - *c = (char) stbi__get8(s); - } -} - -static int stbi__pnm_isdigit(char c) -{ - return c >= '0' && c <= '9'; -} - -static int stbi__pnm_getinteger(stbi__context *s, char *c) -{ - int value = 0; - - while (!stbi__at_eof(s) && stbi__pnm_isdigit(*c)) { - value = value*10 + (*c - '0'); - *c = (char) stbi__get8(s); - } - - return value; -} - -static int stbi__pnm_info(stbi__context *s, int *x, int *y, int *comp) -{ - int maxv; - char c, p, t; - - stbi__rewind( s ); - - // Get identifier - p = (char) stbi__get8(s); - t = (char) stbi__get8(s); - if (p != 'P' || (t != '5' && t != '6')) { - stbi__rewind( s ); - return 0; - } - - *comp = (t == '6') ? 3 : 1; // '5' is 1-component .pgm; '6' is 3-component .ppm - - c = (char) stbi__get8(s); - stbi__pnm_skip_whitespace(s, &c); - - *x = stbi__pnm_getinteger(s, &c); // read width - stbi__pnm_skip_whitespace(s, &c); - - *y = stbi__pnm_getinteger(s, &c); // read height - stbi__pnm_skip_whitespace(s, &c); - - maxv = stbi__pnm_getinteger(s, &c); // read max value - - if (maxv > 255) - return stbi__err("max value > 255", "PPM image not 8-bit"); - else - return 1; -} -#endif - -static int stbi__info_main(stbi__context *s, int *x, int *y, int *comp) -{ - #ifndef STBI_NO_JPEG - if (stbi__jpeg_info(s, x, y, comp)) return 1; - #endif - - #ifndef STBI_NO_PNG - if (stbi__png_info(s, x, y, comp)) return 1; - #endif - - #ifndef STBI_NO_GIF - if (stbi__gif_info(s, x, y, comp)) return 1; - #endif - - #ifndef STBI_NO_BMP - if (stbi__bmp_info(s, x, y, comp)) return 1; - #endif - - #ifndef STBI_NO_PSD - if (stbi__psd_info(s, x, y, comp)) return 1; - #endif - - #ifndef STBI_NO_PIC - if (stbi__pic_info(s, x, y, comp)) return 1; - #endif - - #ifndef STBI_NO_PNM - if (stbi__pnm_info(s, x, y, comp)) return 1; - #endif - - #ifndef STBI_NO_HDR - if (stbi__hdr_info(s, x, y, comp)) return 1; - #endif - - // test tga last because it's a crappy test! - #ifndef STBI_NO_TGA - if (stbi__tga_info(s, x, y, comp)) - return 1; - #endif - return stbi__err("unknown image type", "Image not of any known type, or corrupt"); -} - -#ifndef STBI_NO_STDIO -STBIDEF int stbi_info(char const *filename, int *x, int *y, int *comp) -{ - FILE *f = stbi__fopen(filename, "rb"); - int result; - if (!f) return stbi__err("can't fopen", "Unable to open file"); - result = stbi_info_from_file(f, x, y, comp); - fclose(f); - return result; -} - -STBIDEF int stbi_info_from_file(FILE *f, int *x, int *y, int *comp) -{ - int r; - stbi__context s; - long pos = ftell(f); - stbi__start_file(&s, f); - r = stbi__info_main(&s,x,y,comp); - fseek(f,pos,SEEK_SET); - return r; -} -#endif // !STBI_NO_STDIO - -STBIDEF int stbi_info_from_memory(stbi_uc const *buffer, int len, int *x, int *y, int *comp) -{ - stbi__context s; - stbi__start_mem(&s,buffer,len); - return stbi__info_main(&s,x,y,comp); -} - -STBIDEF int stbi_info_from_callbacks(stbi_io_callbacks const *c, void *user, int *x, int *y, int *comp) -{ - stbi__context s; - stbi__start_callbacks(&s, (stbi_io_callbacks *) c, user); - return stbi__info_main(&s,x,y,comp); -} - -#endif // STB_IMAGE_IMPLEMENTATION - -/* - revision history: - 2.12 (2016-04-02) fix typo in 2.11 PSD fix that caused crashes - 2.11 (2016-04-02) allocate large structures on the stack - remove white matting for transparent PSD - fix reported channel count for PNG & BMP - re-enable SSE2 in non-gcc 64-bit - support RGB-formatted JPEG - read 16-bit PNGs (only as 8-bit) - 2.10 (2016-01-22) avoid warning introduced in 2.09 by STBI_REALLOC_SIZED - 2.09 (2016-01-16) allow comments in PNM files - 16-bit-per-pixel TGA (not bit-per-component) - info() for TGA could break due to .hdr handling - info() for BMP to shares code instead of sloppy parse - can use STBI_REALLOC_SIZED if allocator doesn't support realloc - code cleanup - 2.08 (2015-09-13) fix to 2.07 cleanup, reading RGB PSD as RGBA - 2.07 (2015-09-13) fix compiler warnings - partial animated GIF support - limited 16-bpc PSD support - #ifdef unused functions - bug with < 92 byte PIC,PNM,HDR,TGA - 2.06 (2015-04-19) fix bug where PSD returns wrong '*comp' value - 2.05 (2015-04-19) fix bug in progressive JPEG handling, fix warning - 2.04 (2015-04-15) try to re-enable SIMD on MinGW 64-bit - 2.03 (2015-04-12) extra corruption checking (mmozeiko) - stbi_set_flip_vertically_on_load (nguillemot) - fix NEON support; fix mingw support - 2.02 (2015-01-19) fix incorrect assert, fix warning - 2.01 (2015-01-17) fix various warnings; suppress SIMD on gcc 32-bit without -msse2 - 2.00b (2014-12-25) fix STBI_MALLOC in progressive JPEG - 2.00 (2014-12-25) optimize JPG, including x86 SSE2 & NEON SIMD (ryg) - progressive JPEG (stb) - PGM/PPM support (Ken Miller) - STBI_MALLOC,STBI_REALLOC,STBI_FREE - GIF bugfix -- seemingly never worked - STBI_NO_*, STBI_ONLY_* - 1.48 (2014-12-14) fix incorrectly-named assert() - 1.47 (2014-12-14) 1/2/4-bit PNG support, both direct and paletted (Omar Cornut & stb) - optimize PNG (ryg) - fix bug in interlaced PNG with user-specified channel count (stb) - 1.46 (2014-08-26) - fix broken tRNS chunk (colorkey-style transparency) in non-paletted PNG - 1.45 (2014-08-16) - fix MSVC-ARM internal compiler error by wrapping malloc - 1.44 (2014-08-07) - various warning fixes from Ronny Chevalier - 1.43 (2014-07-15) - fix MSVC-only compiler problem in code changed in 1.42 - 1.42 (2014-07-09) - don't define _CRT_SECURE_NO_WARNINGS (affects user code) - fixes to stbi__cleanup_jpeg path - added STBI_ASSERT to avoid requiring assert.h - 1.41 (2014-06-25) - fix search&replace from 1.36 that messed up comments/error messages - 1.40 (2014-06-22) - fix gcc struct-initialization warning - 1.39 (2014-06-15) - fix to TGA optimization when req_comp != number of components in TGA; - fix to GIF loading because BMP wasn't rewinding (whoops, no GIFs in my test suite) - add support for BMP version 5 (more ignored fields) - 1.38 (2014-06-06) - suppress MSVC warnings on integer casts truncating values - fix accidental rename of 'skip' field of I/O - 1.37 (2014-06-04) - remove duplicate typedef - 1.36 (2014-06-03) - convert to header file single-file library - if de-iphone isn't set, load iphone images color-swapped instead of returning NULL - 1.35 (2014-05-27) - various warnings - fix broken STBI_SIMD path - fix bug where stbi_load_from_file no longer left file pointer in correct place - fix broken non-easy path for 32-bit BMP (possibly never used) - TGA optimization by Arseny Kapoulkine - 1.34 (unknown) - use STBI_NOTUSED in stbi__resample_row_generic(), fix one more leak in tga failure case - 1.33 (2011-07-14) - make stbi_is_hdr work in STBI_NO_HDR (as specified), minor compiler-friendly improvements - 1.32 (2011-07-13) - support for "info" function for all supported filetypes (SpartanJ) - 1.31 (2011-06-20) - a few more leak fixes, bug in PNG handling (SpartanJ) - 1.30 (2011-06-11) - added ability to load files via callbacks to accomidate custom input streams (Ben Wenger) - removed deprecated format-specific test/load functions - removed support for installable file formats (stbi_loader) -- would have been broken for IO callbacks anyway - error cases in bmp and tga give messages and don't leak (Raymond Barbiero, grisha) - fix inefficiency in decoding 32-bit BMP (David Woo) - 1.29 (2010-08-16) - various warning fixes from Aurelien Pocheville - 1.28 (2010-08-01) - fix bug in GIF palette transparency (SpartanJ) - 1.27 (2010-08-01) - cast-to-stbi_uc to fix warnings - 1.26 (2010-07-24) - fix bug in file buffering for PNG reported by SpartanJ - 1.25 (2010-07-17) - refix trans_data warning (Won Chun) - 1.24 (2010-07-12) - perf improvements reading from files on platforms with lock-heavy fgetc() - minor perf improvements for jpeg - deprecated type-specific functions so we'll get feedback if they're needed - attempt to fix trans_data warning (Won Chun) - 1.23 fixed bug in iPhone support - 1.22 (2010-07-10) - removed image *writing* support - stbi_info support from Jetro Lauha - GIF support from Jean-Marc Lienher - iPhone PNG-extensions from James Brown - warning-fixes from Nicolas Schulz and Janez Zemva (i.stbi__err. Janez (U+017D)emva) - 1.21 fix use of 'stbi_uc' in header (reported by jon blow) - 1.20 added support for Softimage PIC, by Tom Seddon - 1.19 bug in interlaced PNG corruption check (found by ryg) - 1.18 (2008-08-02) - fix a threading bug (local mutable static) - 1.17 support interlaced PNG - 1.16 major bugfix - stbi__convert_format converted one too many pixels - 1.15 initialize some fields for thread safety - 1.14 fix threadsafe conversion bug - header-file-only version (#define STBI_HEADER_FILE_ONLY before including) - 1.13 threadsafe - 1.12 const qualifiers in the API - 1.11 Support installable IDCT, colorspace conversion routines - 1.10 Fixes for 64-bit (don't use "unsigned long") - optimized upsampling by Fabian "ryg" Giesen - 1.09 Fix format-conversion for PSD code (bad global variables!) - 1.08 Thatcher Ulrich's PSD code integrated by Nicolas Schulz - 1.07 attempt to fix C++ warning/errors again - 1.06 attempt to fix C++ warning/errors again - 1.05 fix TGA loading to return correct *comp and use good luminance calc - 1.04 default float alpha is 1, not 255; use 'void *' for stbi_image_free - 1.03 bugfixes to STBI_NO_STDIO, STBI_NO_HDR - 1.02 support for (subset of) HDR files, float interface for preferred access to them - 1.01 fix bug: possible bug in handling right-side up bmps... not sure - fix bug: the stbi__bmp_load() and stbi__tga_load() functions didn't work at all - 1.00 interface to zlib that skips zlib header - 0.99 correct handling of alpha in palette - 0.98 TGA loader by lonesock; dynamically add loaders (untested) - 0.97 jpeg errors on too large a file; also catch another malloc failure - 0.96 fix detection of invalid v value - particleman@mollyrocket forum - 0.95 during header scan, seek to markers in case of padding - 0.94 STBI_NO_STDIO to disable stdio usage; rename all #defines the same - 0.93 handle jpegtran output; verbose errors - 0.92 read 4,8,16,24,32-bit BMP files of several formats - 0.91 output 24-bit Windows 3.0 BMP files - 0.90 fix a few more warnings; bump version number to approach 1.0 - 0.61 bugfixes due to Marc LeBlanc, Christopher Lloyd - 0.60 fix compiling as c++ - 0.59 fix warnings: merge Dave Moore's -Wall fixes - 0.58 fix bug: zlib uncompressed mode len/nlen was wrong endian - 0.57 fix bug: jpg last huffman symbol before marker was >9 bits but less than 16 available - 0.56 fix bug: zlib uncompressed mode len vs. nlen - 0.55 fix bug: restart_interval not initialized to 0 - 0.54 allow NULL for 'int *comp' - 0.53 fix bug in png 3->4; speedup png decoding - 0.52 png handles req_comp=3,4 directly; minor cleanup; jpeg comments - 0.51 obey req_comp requests, 1-component jpegs return as 1-component, - on 'test' only check type, not whether we support this variant - 0.50 (2006-11-19) - first released version -*/ diff --git a/impeller/third_party/stb/stb/stb_image_resize.h b/impeller/third_party/stb/stb/stb_image_resize.h deleted file mode 100644 index 4cabe54089dda..0000000000000 --- a/impeller/third_party/stb/stb/stb_image_resize.h +++ /dev/null @@ -1,2578 +0,0 @@ -/* stb_image_resize - v0.91 - public domain image resizing - by Jorge L Rodriguez (@VinoBS) - 2014 - http://github.com/nothings/stb - - Written with emphasis on usability, portability, and efficiency. (No - SIMD or threads, so it be easily outperformed by libs that use those.) - Only scaling and translation is supported, no rotations or shears. - Easy API downsamples w/Mitchell filter, upsamples w/cubic interpolation. - - COMPILING & LINKING - In one C/C++ file that #includes this file, do this: - #define STB_IMAGE_RESIZE_IMPLEMENTATION - before the #include. That will create the implementation in that file. - - QUICKSTART - stbir_resize_uint8( input_pixels , in_w , in_h , 0, - output_pixels, out_w, out_h, 0, num_channels) - stbir_resize_float(...) - stbir_resize_uint8_srgb( input_pixels , in_w , in_h , 0, - output_pixels, out_w, out_h, 0, - num_channels , alpha_chan , 0) - stbir_resize_uint8_srgb_edgemode( - input_pixels , in_w , in_h , 0, - output_pixels, out_w, out_h, 0, - num_channels , alpha_chan , 0, STBIR_EDGE_CLAMP) - // WRAP/REFLECT/ZERO - - FULL API - See the "header file" section of the source for API documentation. - - ADDITIONAL DOCUMENTATION - - SRGB & FLOATING POINT REPRESENTATION - The sRGB functions presume IEEE floating point. If you do not have - IEEE floating point, define STBIR_NON_IEEE_FLOAT. This will use - a slower implementation. - - MEMORY ALLOCATION - The resize functions here perform a single memory allocation using - malloc. To control the memory allocation, before the #include that - triggers the implementation, do: - - #define STBIR_MALLOC(size,context) ... - #define STBIR_FREE(ptr,context) ... - - Each resize function makes exactly one call to malloc/free, so to use - temp memory, store the temp memory in the context and return that. - - ASSERT - Define STBIR_ASSERT(boolval) to override assert() and not use assert.h - - OPTIMIZATION - Define STBIR_SATURATE_INT to compute clamp values in-range using - integer operations instead of float operations. This may be faster - on some platforms. - - DEFAULT FILTERS - For functions which don't provide explicit control over what filters - to use, you can change the compile-time defaults with - - #define STBIR_DEFAULT_FILTER_UPSAMPLE STBIR_FILTER_something - #define STBIR_DEFAULT_FILTER_DOWNSAMPLE STBIR_FILTER_something - - See stbir_filter in the header-file section for the list of filters. - - NEW FILTERS - A number of 1D filter kernels are used. For a list of - supported filters see the stbir_filter enum. To add a new filter, - write a filter function and add it to stbir__filter_info_table. - - PROGRESS - For interactive use with slow resize operations, you can install - a progress-report callback: - - #define STBIR_PROGRESS_REPORT(val) some_func(val) - - The parameter val is a float which goes from 0 to 1 as progress is made. - - For example: - - static void my_progress_report(float progress); - #define STBIR_PROGRESS_REPORT(val) my_progress_report(val) - - #define STB_IMAGE_RESIZE_IMPLEMENTATION - #include "stb_image_resize.h" - - static void my_progress_report(float progress) - { - printf("Progress: %f%%\n", progress*100); - } - - MAX CHANNELS - If your image has more than 64 channels, define STBIR_MAX_CHANNELS - to the max you'll have. - - ALPHA CHANNEL - Most of the resizing functions provide the ability to control how - the alpha channel of an image is processed. The important things - to know about this: - - 1. The best mathematically-behaved version of alpha to use is - called "premultiplied alpha", in which the other color channels - have had the alpha value multiplied in. If you use premultiplied - alpha, linear filtering (such as image resampling done by this - library, or performed in texture units on GPUs) does the "right - thing". While premultiplied alpha is standard in the movie CGI - industry, it is still uncommon in the videogame/real-time world. - - If you linearly filter non-premultiplied alpha, strange effects - occur. (For example, the average of 1% opaque bright green - and 99% opaque black produces 50% transparent dark green when - non-premultiplied, whereas premultiplied it produces 50% - transparent near-black. The former introduces green energy - that doesn't exist in the source image.) - - 2. Artists should not edit premultiplied-alpha images; artists - want non-premultiplied alpha images. Thus, art tools generally output - non-premultiplied alpha images. - - 3. You will get best results in most cases by converting images - to premultiplied alpha before processing them mathematically. - - 4. If you pass the flag STBIR_FLAG_ALPHA_PREMULTIPLIED, the - resizer does not do anything special for the alpha channel; - it is resampled identically to other channels. This produces - the correct results for premultiplied-alpha images, but produces - less-than-ideal results for non-premultiplied-alpha images. - - 5. If you do not pass the flag STBIR_FLAG_ALPHA_PREMULTIPLIED, - then the resizer weights the contribution of input pixels - based on their alpha values, or, equivalently, it multiplies - the alpha value into the color channels, resamples, then divides - by the resultant alpha value. Input pixels which have alpha=0 do - not contribute at all to output pixels unless _all_ of the input - pixels affecting that output pixel have alpha=0, in which case - the result for that pixel is the same as it would be without - STBIR_FLAG_ALPHA_PREMULTIPLIED. However, this is only true for - input images in integer formats. For input images in float format, - input pixels with alpha=0 have no effect, and output pixels - which have alpha=0 will be 0 in all channels. (For float images, - you can manually achieve the same result by adding a tiny epsilon - value to the alpha channel of every image, and then subtracting - or clamping it at the end.) - - 6. You can suppress the behavior described in #5 and make - all-0-alpha pixels have 0 in all channels by #defining - STBIR_NO_ALPHA_EPSILON. - - 7. You can separately control whether the alpha channel is - interpreted as linear or affected by the colorspace. By default - it is linear; you almost never want to apply the colorspace. - (For example, graphics hardware does not apply sRGB conversion - to the alpha channel.) - - ADDITIONAL CONTRIBUTORS - Sean Barrett: API design, optimizations - - REVISIONS - 0.91 (2016-04-02) fix warnings; fix handling of subpixel regions - 0.90 (2014-09-17) first released version - - LICENSE - - This software is dual-licensed to the public domain and under the following - license: you are granted a perpetual, irrevocable license to copy, modify, - publish, and distribute this file as you see fit. - - TODO - Don't decode all of the image data when only processing a partial tile - Don't use full-width decode buffers when only processing a partial tile - When processing wide images, break processing into tiles so data fits in L1 cache - Installable filters? - Resize that respects alpha test coverage - (Reference code: FloatImage::alphaTestCoverage and FloatImage::scaleAlphaToCoverage: - https://code.google.com/p/nvidia-texture-tools/source/browse/trunk/src/nvimage/FloatImage.cpp ) -*/ - -#ifndef STBIR_INCLUDE_STB_IMAGE_RESIZE_H -#define STBIR_INCLUDE_STB_IMAGE_RESIZE_H - -#ifdef _MSC_VER -typedef unsigned char stbir_uint8; -typedef unsigned short stbir_uint16; -typedef unsigned int stbir_uint32; -#else -#include -typedef uint8_t stbir_uint8; -typedef uint16_t stbir_uint16; -typedef uint32_t stbir_uint32; -#endif - -#ifdef STB_IMAGE_RESIZE_STATIC -#define STBIRDEF static -#else -#ifdef __cplusplus -#define STBIRDEF extern "C" -#else -#define STBIRDEF extern -#endif -#endif - - -////////////////////////////////////////////////////////////////////////////// -// -// Easy-to-use API: -// -// * "input pixels" points to an array of image data with 'num_channels' channels (e.g. RGB=3, RGBA=4) -// * input_w is input image width (x-axis), input_h is input image height (y-axis) -// * stride is the offset between successive rows of image data in memory, in bytes. you can -// specify 0 to mean packed continuously in memory -// * alpha channel is treated identically to other channels. -// * colorspace is linear or sRGB as specified by function name -// * returned result is 1 for success or 0 in case of an error. -// #define STBIR_ASSERT() to trigger an assert on parameter validation errors. -// * Memory required grows approximately linearly with input and output size, but with -// discontinuities at input_w == output_w and input_h == output_h. -// * These functions use a "default" resampling filter defined at compile time. To change the filter, -// you can change the compile-time defaults by #defining STBIR_DEFAULT_FILTER_UPSAMPLE -// and STBIR_DEFAULT_FILTER_DOWNSAMPLE, or you can use the medium-complexity API. - -STBIRDEF int stbir_resize_uint8( const unsigned char *input_pixels , int input_w , int input_h , int input_stride_in_bytes, - unsigned char *output_pixels, int output_w, int output_h, int output_stride_in_bytes, - int num_channels); - -STBIRDEF int stbir_resize_float( const float *input_pixels , int input_w , int input_h , int input_stride_in_bytes, - float *output_pixels, int output_w, int output_h, int output_stride_in_bytes, - int num_channels); - - -// The following functions interpret image data as gamma-corrected sRGB. -// Specify STBIR_ALPHA_CHANNEL_NONE if you have no alpha channel, -// or otherwise provide the index of the alpha channel. Flags value -// of 0 will probably do the right thing if you're not sure what -// the flags mean. - -#define STBIR_ALPHA_CHANNEL_NONE -1 - -// Set this flag if your texture has premultiplied alpha. Otherwise, stbir will -// use alpha-weighted resampling (effectively premultiplying, resampling, -// then unpremultiplying). -#define STBIR_FLAG_ALPHA_PREMULTIPLIED (1 << 0) -// The specified alpha channel should be handled as gamma-corrected value even -// when doing sRGB operations. -#define STBIR_FLAG_ALPHA_USES_COLORSPACE (1 << 1) - -STBIRDEF int stbir_resize_uint8_srgb(const unsigned char *input_pixels , int input_w , int input_h , int input_stride_in_bytes, - unsigned char *output_pixels, int output_w, int output_h, int output_stride_in_bytes, - int num_channels, int alpha_channel, int flags); - - -typedef enum -{ - STBIR_EDGE_CLAMP = 1, - STBIR_EDGE_REFLECT = 2, - STBIR_EDGE_WRAP = 3, - STBIR_EDGE_ZERO = 4, -} stbir_edge; - -// This function adds the ability to specify how requests to sample off the edge of the image are handled. -STBIRDEF int stbir_resize_uint8_srgb_edgemode(const unsigned char *input_pixels , int input_w , int input_h , int input_stride_in_bytes, - unsigned char *output_pixels, int output_w, int output_h, int output_stride_in_bytes, - int num_channels, int alpha_channel, int flags, - stbir_edge edge_wrap_mode); - -////////////////////////////////////////////////////////////////////////////// -// -// Medium-complexity API -// -// This extends the easy-to-use API as follows: -// -// * Alpha-channel can be processed separately -// * If alpha_channel is not STBIR_ALPHA_CHANNEL_NONE -// * Alpha channel will not be gamma corrected (unless flags&STBIR_FLAG_GAMMA_CORRECT) -// * Filters will be weighted by alpha channel (unless flags&STBIR_FLAG_ALPHA_PREMULTIPLIED) -// * Filter can be selected explicitly -// * uint16 image type -// * sRGB colorspace available for all types -// * context parameter for passing to STBIR_MALLOC - -typedef enum -{ - STBIR_FILTER_DEFAULT = 0, // use same filter type that easy-to-use API chooses - STBIR_FILTER_BOX = 1, // A trapezoid w/1-pixel wide ramps, same result as box for integer scale ratios - STBIR_FILTER_TRIANGLE = 2, // On upsampling, produces same results as bilinear texture filtering - STBIR_FILTER_CUBICBSPLINE = 3, // The cubic b-spline (aka Mitchell-Netrevalli with B=1,C=0), gaussian-esque - STBIR_FILTER_CATMULLROM = 4, // An interpolating cubic spline - STBIR_FILTER_MITCHELL = 5, // Mitchell-Netrevalli filter with B=1/3, C=1/3 -} stbir_filter; - -typedef enum -{ - STBIR_COLORSPACE_LINEAR, - STBIR_COLORSPACE_SRGB, - - STBIR_MAX_COLORSPACES, -} stbir_colorspace; - -// The following functions are all identical except for the type of the image data - -STBIRDEF int stbir_resize_uint8_generic( const unsigned char *input_pixels , int input_w , int input_h , int input_stride_in_bytes, - unsigned char *output_pixels, int output_w, int output_h, int output_stride_in_bytes, - int num_channels, int alpha_channel, int flags, - stbir_edge edge_wrap_mode, stbir_filter filter, stbir_colorspace space, - void *alloc_context); - -STBIRDEF int stbir_resize_uint16_generic(const stbir_uint16 *input_pixels , int input_w , int input_h , int input_stride_in_bytes, - stbir_uint16 *output_pixels , int output_w, int output_h, int output_stride_in_bytes, - int num_channels, int alpha_channel, int flags, - stbir_edge edge_wrap_mode, stbir_filter filter, stbir_colorspace space, - void *alloc_context); - -STBIRDEF int stbir_resize_float_generic( const float *input_pixels , int input_w , int input_h , int input_stride_in_bytes, - float *output_pixels , int output_w, int output_h, int output_stride_in_bytes, - int num_channels, int alpha_channel, int flags, - stbir_edge edge_wrap_mode, stbir_filter filter, stbir_colorspace space, - void *alloc_context); - - - -////////////////////////////////////////////////////////////////////////////// -// -// Full-complexity API -// -// This extends the medium API as follows: -// -// * uint32 image type -// * not typesafe -// * separate filter types for each axis -// * separate edge modes for each axis -// * can specify scale explicitly for subpixel correctness -// * can specify image source tile using texture coordinates - -typedef enum -{ - STBIR_TYPE_UINT8 , - STBIR_TYPE_UINT16, - STBIR_TYPE_UINT32, - STBIR_TYPE_FLOAT , - - STBIR_MAX_TYPES -} stbir_datatype; - -STBIRDEF int stbir_resize( const void *input_pixels , int input_w , int input_h , int input_stride_in_bytes, - void *output_pixels, int output_w, int output_h, int output_stride_in_bytes, - stbir_datatype datatype, - int num_channels, int alpha_channel, int flags, - stbir_edge edge_mode_horizontal, stbir_edge edge_mode_vertical, - stbir_filter filter_horizontal, stbir_filter filter_vertical, - stbir_colorspace space, void *alloc_context); - -STBIRDEF int stbir_resize_subpixel(const void *input_pixels , int input_w , int input_h , int input_stride_in_bytes, - void *output_pixels, int output_w, int output_h, int output_stride_in_bytes, - stbir_datatype datatype, - int num_channels, int alpha_channel, int flags, - stbir_edge edge_mode_horizontal, stbir_edge edge_mode_vertical, - stbir_filter filter_horizontal, stbir_filter filter_vertical, - stbir_colorspace space, void *alloc_context, - float x_scale, float y_scale, - float x_offset, float y_offset); - -STBIRDEF int stbir_resize_region( const void *input_pixels , int input_w , int input_h , int input_stride_in_bytes, - void *output_pixels, int output_w, int output_h, int output_stride_in_bytes, - stbir_datatype datatype, - int num_channels, int alpha_channel, int flags, - stbir_edge edge_mode_horizontal, stbir_edge edge_mode_vertical, - stbir_filter filter_horizontal, stbir_filter filter_vertical, - stbir_colorspace space, void *alloc_context, - float s0, float t0, float s1, float t1); -// (s0, t0) & (s1, t1) are the top-left and bottom right corner (uv addressing style: [0, 1]x[0, 1]) of a region of the input image to use. - -// -// -//// end header file ///////////////////////////////////////////////////// -#endif // STBIR_INCLUDE_STB_IMAGE_RESIZE_H - - - - - -#ifdef STB_IMAGE_RESIZE_IMPLEMENTATION - -#ifndef STBIR_ASSERT -#include -#define STBIR_ASSERT(x) assert(x) -#endif - -// For memset -#include - -#include - -#ifndef STBIR_MALLOC -#include -#define STBIR_MALLOC(size,c) malloc(size) -#define STBIR_FREE(ptr,c) free(ptr) -#endif - -#ifndef _MSC_VER -#ifdef __cplusplus -#define stbir__inline inline -#else -#define stbir__inline -#endif -#else -#define stbir__inline __forceinline -#endif - - -// should produce compiler error if size is wrong -typedef unsigned char stbir__validate_uint32[sizeof(stbir_uint32) == 4 ? 1 : -1]; - -#ifdef _MSC_VER -#define STBIR__NOTUSED(v) (void)(v) -#else -#define STBIR__NOTUSED(v) (void)sizeof(v) -#endif - -#define STBIR__ARRAY_SIZE(a) (sizeof((a))/sizeof((a)[0])) - -#ifndef STBIR_DEFAULT_FILTER_UPSAMPLE -#define STBIR_DEFAULT_FILTER_UPSAMPLE STBIR_FILTER_CATMULLROM -#endif - -#ifndef STBIR_DEFAULT_FILTER_DOWNSAMPLE -#define STBIR_DEFAULT_FILTER_DOWNSAMPLE STBIR_FILTER_MITCHELL -#endif - -#ifndef STBIR_PROGRESS_REPORT -#define STBIR_PROGRESS_REPORT(float_0_to_1) -#endif - -#ifndef STBIR_MAX_CHANNELS -#define STBIR_MAX_CHANNELS 64 -#endif - -#if STBIR_MAX_CHANNELS > 65536 -#error "Too many channels; STBIR_MAX_CHANNELS must be no more than 65536." -// because we store the indices in 16-bit variables -#endif - -// This value is added to alpha just before premultiplication to avoid -// zeroing out color values. It is equivalent to 2^-80. If you don't want -// that behavior (it may interfere if you have floating point images with -// very small alpha values) then you can define STBIR_NO_ALPHA_EPSILON to -// disable it. -#ifndef STBIR_ALPHA_EPSILON -#define STBIR_ALPHA_EPSILON ((float)1 / (1 << 20) / (1 << 20) / (1 << 20) / (1 << 20)) -#endif - - - -#ifdef _MSC_VER -#define STBIR__UNUSED_PARAM(v) (void)(v) -#else -#define STBIR__UNUSED_PARAM(v) (void)sizeof(v) -#endif - -// must match stbir_datatype -static unsigned char stbir__type_size[] = { - 1, // STBIR_TYPE_UINT8 - 2, // STBIR_TYPE_UINT16 - 4, // STBIR_TYPE_UINT32 - 4, // STBIR_TYPE_FLOAT -}; - -// Kernel function centered at 0 -typedef float (stbir__kernel_fn)(float x, float scale); -typedef float (stbir__support_fn)(float scale); - -typedef struct -{ - stbir__kernel_fn* kernel; - stbir__support_fn* support; -} stbir__filter_info; - -// When upsampling, the contributors are which source pixels contribute. -// When downsampling, the contributors are which destination pixels are contributed to. -typedef struct -{ - int n0; // First contributing pixel - int n1; // Last contributing pixel -} stbir__contributors; - -typedef struct -{ - const void* input_data; - int input_w; - int input_h; - int input_stride_bytes; - - void* output_data; - int output_w; - int output_h; - int output_stride_bytes; - - float s0, t0, s1, t1; - - float horizontal_shift; // Units: output pixels - float vertical_shift; // Units: output pixels - float horizontal_scale; - float vertical_scale; - - int channels; - int alpha_channel; - stbir_uint32 flags; - stbir_datatype type; - stbir_filter horizontal_filter; - stbir_filter vertical_filter; - stbir_edge edge_horizontal; - stbir_edge edge_vertical; - stbir_colorspace colorspace; - - stbir__contributors* horizontal_contributors; - float* horizontal_coefficients; - - stbir__contributors* vertical_contributors; - float* vertical_coefficients; - - int decode_buffer_pixels; - float* decode_buffer; - - float* horizontal_buffer; - - // cache these because ceil/floor are inexplicably showing up in profile - int horizontal_coefficient_width; - int vertical_coefficient_width; - int horizontal_filter_pixel_width; - int vertical_filter_pixel_width; - int horizontal_filter_pixel_margin; - int vertical_filter_pixel_margin; - int horizontal_num_contributors; - int vertical_num_contributors; - - int ring_buffer_length_bytes; // The length of an individual entry in the ring buffer. The total number of ring buffers is stbir__get_filter_pixel_width(filter) - int ring_buffer_first_scanline; - int ring_buffer_last_scanline; - int ring_buffer_begin_index; - float* ring_buffer; - - float* encode_buffer; // A temporary buffer to store floats so we don't lose precision while we do multiply-adds. - - int horizontal_contributors_size; - int horizontal_coefficients_size; - int vertical_contributors_size; - int vertical_coefficients_size; - int decode_buffer_size; - int horizontal_buffer_size; - int ring_buffer_size; - int encode_buffer_size; -} stbir__info; - -static stbir__inline int stbir__min(int a, int b) -{ - return a < b ? a : b; -} - -static stbir__inline int stbir__max(int a, int b) -{ - return a > b ? a : b; -} - -static stbir__inline float stbir__saturate(float x) -{ - if (x < 0) - return 0; - - if (x > 1) - return 1; - - return x; -} - -#ifdef STBIR_SATURATE_INT -static stbir__inline stbir_uint8 stbir__saturate8(int x) -{ - if ((unsigned int) x <= 255) - return x; - - if (x < 0) - return 0; - - return 255; -} - -static stbir__inline stbir_uint16 stbir__saturate16(int x) -{ - if ((unsigned int) x <= 65535) - return x; - - if (x < 0) - return 0; - - return 65535; -} -#endif - -static float stbir__srgb_uchar_to_linear_float[256] = { - 0.000000f, 0.000304f, 0.000607f, 0.000911f, 0.001214f, 0.001518f, 0.001821f, 0.002125f, 0.002428f, 0.002732f, 0.003035f, - 0.003347f, 0.003677f, 0.004025f, 0.004391f, 0.004777f, 0.005182f, 0.005605f, 0.006049f, 0.006512f, 0.006995f, 0.007499f, - 0.008023f, 0.008568f, 0.009134f, 0.009721f, 0.010330f, 0.010960f, 0.011612f, 0.012286f, 0.012983f, 0.013702f, 0.014444f, - 0.015209f, 0.015996f, 0.016807f, 0.017642f, 0.018500f, 0.019382f, 0.020289f, 0.021219f, 0.022174f, 0.023153f, 0.024158f, - 0.025187f, 0.026241f, 0.027321f, 0.028426f, 0.029557f, 0.030713f, 0.031896f, 0.033105f, 0.034340f, 0.035601f, 0.036889f, - 0.038204f, 0.039546f, 0.040915f, 0.042311f, 0.043735f, 0.045186f, 0.046665f, 0.048172f, 0.049707f, 0.051269f, 0.052861f, - 0.054480f, 0.056128f, 0.057805f, 0.059511f, 0.061246f, 0.063010f, 0.064803f, 0.066626f, 0.068478f, 0.070360f, 0.072272f, - 0.074214f, 0.076185f, 0.078187f, 0.080220f, 0.082283f, 0.084376f, 0.086500f, 0.088656f, 0.090842f, 0.093059f, 0.095307f, - 0.097587f, 0.099899f, 0.102242f, 0.104616f, 0.107023f, 0.109462f, 0.111932f, 0.114435f, 0.116971f, 0.119538f, 0.122139f, - 0.124772f, 0.127438f, 0.130136f, 0.132868f, 0.135633f, 0.138432f, 0.141263f, 0.144128f, 0.147027f, 0.149960f, 0.152926f, - 0.155926f, 0.158961f, 0.162029f, 0.165132f, 0.168269f, 0.171441f, 0.174647f, 0.177888f, 0.181164f, 0.184475f, 0.187821f, - 0.191202f, 0.194618f, 0.198069f, 0.201556f, 0.205079f, 0.208637f, 0.212231f, 0.215861f, 0.219526f, 0.223228f, 0.226966f, - 0.230740f, 0.234551f, 0.238398f, 0.242281f, 0.246201f, 0.250158f, 0.254152f, 0.258183f, 0.262251f, 0.266356f, 0.270498f, - 0.274677f, 0.278894f, 0.283149f, 0.287441f, 0.291771f, 0.296138f, 0.300544f, 0.304987f, 0.309469f, 0.313989f, 0.318547f, - 0.323143f, 0.327778f, 0.332452f, 0.337164f, 0.341914f, 0.346704f, 0.351533f, 0.356400f, 0.361307f, 0.366253f, 0.371238f, - 0.376262f, 0.381326f, 0.386430f, 0.391573f, 0.396755f, 0.401978f, 0.407240f, 0.412543f, 0.417885f, 0.423268f, 0.428691f, - 0.434154f, 0.439657f, 0.445201f, 0.450786f, 0.456411f, 0.462077f, 0.467784f, 0.473532f, 0.479320f, 0.485150f, 0.491021f, - 0.496933f, 0.502887f, 0.508881f, 0.514918f, 0.520996f, 0.527115f, 0.533276f, 0.539480f, 0.545725f, 0.552011f, 0.558340f, - 0.564712f, 0.571125f, 0.577581f, 0.584078f, 0.590619f, 0.597202f, 0.603827f, 0.610496f, 0.617207f, 0.623960f, 0.630757f, - 0.637597f, 0.644480f, 0.651406f, 0.658375f, 0.665387f, 0.672443f, 0.679543f, 0.686685f, 0.693872f, 0.701102f, 0.708376f, - 0.715694f, 0.723055f, 0.730461f, 0.737911f, 0.745404f, 0.752942f, 0.760525f, 0.768151f, 0.775822f, 0.783538f, 0.791298f, - 0.799103f, 0.806952f, 0.814847f, 0.822786f, 0.830770f, 0.838799f, 0.846873f, 0.854993f, 0.863157f, 0.871367f, 0.879622f, - 0.887923f, 0.896269f, 0.904661f, 0.913099f, 0.921582f, 0.930111f, 0.938686f, 0.947307f, 0.955974f, 0.964686f, 0.973445f, - 0.982251f, 0.991102f, 1.0f -}; - -static float stbir__srgb_to_linear(float f) -{ - if (f <= 0.04045f) - return f / 12.92f; - else - return (float)pow((f + 0.055f) / 1.055f, 2.4f); -} - -static float stbir__linear_to_srgb(float f) -{ - if (f <= 0.0031308f) - return f * 12.92f; - else - return 1.055f * (float)pow(f, 1 / 2.4f) - 0.055f; -} - -#ifndef STBIR_NON_IEEE_FLOAT -// From https://gist.github.com/rygorous/2203834 - -typedef union -{ - stbir_uint32 u; - float f; -} stbir__FP32; - -static const stbir_uint32 fp32_to_srgb8_tab4[104] = { - 0x0073000d, 0x007a000d, 0x0080000d, 0x0087000d, 0x008d000d, 0x0094000d, 0x009a000d, 0x00a1000d, - 0x00a7001a, 0x00b4001a, 0x00c1001a, 0x00ce001a, 0x00da001a, 0x00e7001a, 0x00f4001a, 0x0101001a, - 0x010e0033, 0x01280033, 0x01410033, 0x015b0033, 0x01750033, 0x018f0033, 0x01a80033, 0x01c20033, - 0x01dc0067, 0x020f0067, 0x02430067, 0x02760067, 0x02aa0067, 0x02dd0067, 0x03110067, 0x03440067, - 0x037800ce, 0x03df00ce, 0x044600ce, 0x04ad00ce, 0x051400ce, 0x057b00c5, 0x05dd00bc, 0x063b00b5, - 0x06970158, 0x07420142, 0x07e30130, 0x087b0120, 0x090b0112, 0x09940106, 0x0a1700fc, 0x0a9500f2, - 0x0b0f01cb, 0x0bf401ae, 0x0ccb0195, 0x0d950180, 0x0e56016e, 0x0f0d015e, 0x0fbc0150, 0x10630143, - 0x11070264, 0x1238023e, 0x1357021d, 0x14660201, 0x156601e9, 0x165a01d3, 0x174401c0, 0x182401af, - 0x18fe0331, 0x1a9602fe, 0x1c1502d2, 0x1d7e02ad, 0x1ed4028d, 0x201a0270, 0x21520256, 0x227d0240, - 0x239f0443, 0x25c003fe, 0x27bf03c4, 0x29a10392, 0x2b6a0367, 0x2d1d0341, 0x2ebe031f, 0x304d0300, - 0x31d105b0, 0x34a80555, 0x37520507, 0x39d504c5, 0x3c37048b, 0x3e7c0458, 0x40a8042a, 0x42bd0401, - 0x44c20798, 0x488e071e, 0x4c1c06b6, 0x4f76065d, 0x52a50610, 0x55ac05cc, 0x5892058f, 0x5b590559, - 0x5e0c0a23, 0x631c0980, 0x67db08f6, 0x6c55087f, 0x70940818, 0x74a007bd, 0x787d076c, 0x7c330723, -}; - -static stbir_uint8 stbir__linear_to_srgb_uchar(float in) -{ - static const stbir__FP32 almostone = { 0x3f7fffff }; // 1-eps - static const stbir__FP32 minval = { (127-13) << 23 }; - stbir_uint32 tab,bias,scale,t; - stbir__FP32 f; - - // Clamp to [2^(-13), 1-eps]; these two values map to 0 and 1, respectively. - // The tests are carefully written so that NaNs map to 0, same as in the reference - // implementation. - if (!(in > minval.f)) // written this way to catch NaNs - in = minval.f; - if (in > almostone.f) - in = almostone.f; - - // Do the table lookup and unpack bias, scale - f.f = in; - tab = fp32_to_srgb8_tab4[(f.u - minval.u) >> 20]; - bias = (tab >> 16) << 9; - scale = tab & 0xffff; - - // Grab next-highest mantissa bits and perform linear interpolation - t = (f.u >> 12) & 0xff; - return (unsigned char) ((bias + scale*t) >> 16); -} - -#else -// sRGB transition values, scaled by 1<<28 -static int stbir__srgb_offset_to_linear_scaled[256] = -{ - 0, 40738, 122216, 203693, 285170, 366648, 448125, 529603, - 611080, 692557, 774035, 855852, 942009, 1033024, 1128971, 1229926, - 1335959, 1447142, 1563542, 1685229, 1812268, 1944725, 2082664, 2226148, - 2375238, 2529996, 2690481, 2856753, 3028870, 3206888, 3390865, 3580856, - 3776916, 3979100, 4187460, 4402049, 4622919, 4850123, 5083710, 5323731, - 5570236, 5823273, 6082892, 6349140, 6622065, 6901714, 7188133, 7481369, - 7781466, 8088471, 8402427, 8723380, 9051372, 9386448, 9728650, 10078021, - 10434603, 10798439, 11169569, 11548036, 11933879, 12327139, 12727857, 13136073, - 13551826, 13975156, 14406100, 14844697, 15290987, 15745007, 16206795, 16676389, - 17153826, 17639142, 18132374, 18633560, 19142734, 19659934, 20185196, 20718552, - 21260042, 21809696, 22367554, 22933648, 23508010, 24090680, 24681686, 25281066, - 25888850, 26505076, 27129772, 27762974, 28404716, 29055026, 29713942, 30381490, - 31057708, 31742624, 32436272, 33138682, 33849884, 34569912, 35298800, 36036568, - 36783260, 37538896, 38303512, 39077136, 39859796, 40651528, 41452360, 42262316, - 43081432, 43909732, 44747252, 45594016, 46450052, 47315392, 48190064, 49074096, - 49967516, 50870356, 51782636, 52704392, 53635648, 54576432, 55526772, 56486700, - 57456236, 58435408, 59424248, 60422780, 61431036, 62449032, 63476804, 64514376, - 65561776, 66619028, 67686160, 68763192, 69850160, 70947088, 72053992, 73170912, - 74297864, 75434880, 76581976, 77739184, 78906536, 80084040, 81271736, 82469648, - 83677792, 84896192, 86124888, 87363888, 88613232, 89872928, 91143016, 92423512, - 93714432, 95015816, 96327688, 97650056, 98982952, 100326408, 101680440, 103045072, - 104420320, 105806224, 107202800, 108610064, 110028048, 111456776, 112896264, 114346544, - 115807632, 117279552, 118762328, 120255976, 121760536, 123276016, 124802440, 126339832, - 127888216, 129447616, 131018048, 132599544, 134192112, 135795792, 137410592, 139036528, - 140673648, 142321952, 143981456, 145652208, 147334208, 149027488, 150732064, 152447968, - 154175200, 155913792, 157663776, 159425168, 161197984, 162982240, 164777968, 166585184, - 168403904, 170234160, 172075968, 173929344, 175794320, 177670896, 179559120, 181458992, - 183370528, 185293776, 187228736, 189175424, 191133888, 193104112, 195086128, 197079968, - 199085648, 201103184, 203132592, 205173888, 207227120, 209292272, 211369392, 213458480, - 215559568, 217672656, 219797792, 221934976, 224084240, 226245600, 228419056, 230604656, - 232802400, 235012320, 237234432, 239468736, 241715280, 243974080, 246245120, 248528464, - 250824112, 253132064, 255452368, 257785040, 260130080, 262487520, 264857376, 267239664, -}; - -static stbir_uint8 stbir__linear_to_srgb_uchar(float f) -{ - int x = (int) (f * (1 << 28)); // has headroom so you don't need to clamp - int v = 0; - int i; - - // Refine the guess with a short binary search. - i = v + 128; if (x >= stbir__srgb_offset_to_linear_scaled[i]) v = i; - i = v + 64; if (x >= stbir__srgb_offset_to_linear_scaled[i]) v = i; - i = v + 32; if (x >= stbir__srgb_offset_to_linear_scaled[i]) v = i; - i = v + 16; if (x >= stbir__srgb_offset_to_linear_scaled[i]) v = i; - i = v + 8; if (x >= stbir__srgb_offset_to_linear_scaled[i]) v = i; - i = v + 4; if (x >= stbir__srgb_offset_to_linear_scaled[i]) v = i; - i = v + 2; if (x >= stbir__srgb_offset_to_linear_scaled[i]) v = i; - i = v + 1; if (x >= stbir__srgb_offset_to_linear_scaled[i]) v = i; - - return (stbir_uint8) v; -} -#endif - -static float stbir__filter_trapezoid(float x, float scale) -{ - float halfscale = scale / 2; - float t = 0.5f + halfscale; - STBIR_ASSERT(scale <= 1); - - x = (float)fabs(x); - - if (x >= t) - return 0; - else - { - float r = 0.5f - halfscale; - if (x <= r) - return 1; - else - return (t - x) / scale; - } -} - -static float stbir__support_trapezoid(float scale) -{ - STBIR_ASSERT(scale <= 1); - return 0.5f + scale / 2; -} - -static float stbir__filter_triangle(float x, float s) -{ - STBIR__UNUSED_PARAM(s); - - x = (float)fabs(x); - - if (x <= 1.0f) - return 1 - x; - else - return 0; -} - -static float stbir__filter_cubic(float x, float s) -{ - STBIR__UNUSED_PARAM(s); - - x = (float)fabs(x); - - if (x < 1.0f) - return (4 + x*x*(3*x - 6))/6; - else if (x < 2.0f) - return (8 + x*(-12 + x*(6 - x)))/6; - - return (0.0f); -} - -static float stbir__filter_catmullrom(float x, float s) -{ - STBIR__UNUSED_PARAM(s); - - x = (float)fabs(x); - - if (x < 1.0f) - return 1 - x*x*(2.5f - 1.5f*x); - else if (x < 2.0f) - return 2 - x*(4 + x*(0.5f*x - 2.5f)); - - return (0.0f); -} - -static float stbir__filter_mitchell(float x, float s) -{ - STBIR__UNUSED_PARAM(s); - - x = (float)fabs(x); - - if (x < 1.0f) - return (16 + x*x*(21 * x - 36))/18; - else if (x < 2.0f) - return (32 + x*(-60 + x*(36 - 7*x)))/18; - - return (0.0f); -} - -static float stbir__support_zero(float s) -{ - STBIR__UNUSED_PARAM(s); - return 0; -} - -static float stbir__support_one(float s) -{ - STBIR__UNUSED_PARAM(s); - return 1; -} - -static float stbir__support_two(float s) -{ - STBIR__UNUSED_PARAM(s); - return 2; -} - -static stbir__filter_info stbir__filter_info_table[] = { - { NULL, stbir__support_zero }, - { stbir__filter_trapezoid, stbir__support_trapezoid }, - { stbir__filter_triangle, stbir__support_one }, - { stbir__filter_cubic, stbir__support_two }, - { stbir__filter_catmullrom, stbir__support_two }, - { stbir__filter_mitchell, stbir__support_two }, -}; - -stbir__inline static int stbir__use_upsampling(float ratio) -{ - return ratio > 1; -} - -stbir__inline static int stbir__use_width_upsampling(stbir__info* stbir_info) -{ - return stbir__use_upsampling(stbir_info->horizontal_scale); -} - -stbir__inline static int stbir__use_height_upsampling(stbir__info* stbir_info) -{ - return stbir__use_upsampling(stbir_info->vertical_scale); -} - -// This is the maximum number of input samples that can affect an output sample -// with the given filter -static int stbir__get_filter_pixel_width(stbir_filter filter, float scale) -{ - STBIR_ASSERT(filter != 0); - STBIR_ASSERT(filter < STBIR__ARRAY_SIZE(stbir__filter_info_table)); - - if (stbir__use_upsampling(scale)) - return (int)ceil(stbir__filter_info_table[filter].support(1/scale) * 2); - else - return (int)ceil(stbir__filter_info_table[filter].support(scale) * 2 / scale); -} - -// This is how much to expand buffers to account for filters seeking outside -// the image boundaries. -static int stbir__get_filter_pixel_margin(stbir_filter filter, float scale) -{ - return stbir__get_filter_pixel_width(filter, scale) / 2; -} - -static int stbir__get_coefficient_width(stbir_filter filter, float scale) -{ - if (stbir__use_upsampling(scale)) - return (int)ceil(stbir__filter_info_table[filter].support(1 / scale) * 2); - else - return (int)ceil(stbir__filter_info_table[filter].support(scale) * 2); -} - -static int stbir__get_contributors(float scale, stbir_filter filter, int input_size, int output_size) -{ - if (stbir__use_upsampling(scale)) - return output_size; - else - return (input_size + stbir__get_filter_pixel_margin(filter, scale) * 2); -} - -static int stbir__get_total_horizontal_coefficients(stbir__info* info) -{ - return info->horizontal_num_contributors - * stbir__get_coefficient_width (info->horizontal_filter, info->horizontal_scale); -} - -static int stbir__get_total_vertical_coefficients(stbir__info* info) -{ - return info->vertical_num_contributors - * stbir__get_coefficient_width (info->vertical_filter, info->vertical_scale); -} - -static stbir__contributors* stbir__get_contributor(stbir__contributors* contributors, int n) -{ - return &contributors[n]; -} - -// For perf reasons this code is duplicated in stbir__resample_horizontal_upsample/downsample, -// if you change it here change it there too. -static float* stbir__get_coefficient(float* coefficients, stbir_filter filter, float scale, int n, int c) -{ - int width = stbir__get_coefficient_width(filter, scale); - return &coefficients[width*n + c]; -} - -static int stbir__edge_wrap_slow(stbir_edge edge, int n, int max) -{ - switch (edge) - { - case STBIR_EDGE_ZERO: - return 0; // we'll decode the wrong pixel here, and then overwrite with 0s later - - case STBIR_EDGE_CLAMP: - if (n < 0) - return 0; - - if (n >= max) - return max - 1; - - return n; // NOTREACHED - - case STBIR_EDGE_REFLECT: - { - if (n < 0) - { - if (n < max) - return -n; - else - return max - 1; - } - - if (n >= max) - { - int max2 = max * 2; - if (n >= max2) - return 0; - else - return max2 - n - 1; - } - - return n; // NOTREACHED - } - - case STBIR_EDGE_WRAP: - if (n >= 0) - return (n % max); - else - { - int m = (-n) % max; - - if (m != 0) - m = max - m; - - return (m); - } - return n; // NOTREACHED - - default: - STBIR_ASSERT(!"Unimplemented edge type"); - return 0; - } -} - -stbir__inline static int stbir__edge_wrap(stbir_edge edge, int n, int max) -{ - // avoid per-pixel switch - if (n >= 0 && n < max) - return n; - return stbir__edge_wrap_slow(edge, n, max); -} - -// What input pixels contribute to this output pixel? -static void stbir__calculate_sample_range_upsample(int n, float out_filter_radius, float scale_ratio, float out_shift, int* in_first_pixel, int* in_last_pixel, float* in_center_of_out) -{ - float out_pixel_center = (float)n + 0.5f; - float out_pixel_influence_lowerbound = out_pixel_center - out_filter_radius; - float out_pixel_influence_upperbound = out_pixel_center + out_filter_radius; - - float in_pixel_influence_lowerbound = (out_pixel_influence_lowerbound + out_shift) / scale_ratio; - float in_pixel_influence_upperbound = (out_pixel_influence_upperbound + out_shift) / scale_ratio; - - *in_center_of_out = (out_pixel_center + out_shift) / scale_ratio; - *in_first_pixel = (int)(floor(in_pixel_influence_lowerbound + 0.5)); - *in_last_pixel = (int)(floor(in_pixel_influence_upperbound - 0.5)); -} - -// What output pixels does this input pixel contribute to? -static void stbir__calculate_sample_range_downsample(int n, float in_pixels_radius, float scale_ratio, float out_shift, int* out_first_pixel, int* out_last_pixel, float* out_center_of_in) -{ - float in_pixel_center = (float)n + 0.5f; - float in_pixel_influence_lowerbound = in_pixel_center - in_pixels_radius; - float in_pixel_influence_upperbound = in_pixel_center + in_pixels_radius; - - float out_pixel_influence_lowerbound = in_pixel_influence_lowerbound * scale_ratio - out_shift; - float out_pixel_influence_upperbound = in_pixel_influence_upperbound * scale_ratio - out_shift; - - *out_center_of_in = in_pixel_center * scale_ratio - out_shift; - *out_first_pixel = (int)(floor(out_pixel_influence_lowerbound + 0.5)); - *out_last_pixel = (int)(floor(out_pixel_influence_upperbound - 0.5)); -} - -static void stbir__calculate_coefficients_upsample(stbir__info* stbir_info, stbir_filter filter, float scale, int in_first_pixel, int in_last_pixel, float in_center_of_out, stbir__contributors* contributor, float* coefficient_group) -{ - int i; - float total_filter = 0; - float filter_scale; - - STBIR_ASSERT(in_last_pixel - in_first_pixel <= (int)ceil(stbir__filter_info_table[filter].support(1/scale) * 2)); // Taken directly from stbir__get_coefficient_width() which we can't call because we don't know if we're horizontal or vertical. - - contributor->n0 = in_first_pixel; - contributor->n1 = in_last_pixel; - - STBIR_ASSERT(contributor->n1 >= contributor->n0); - - for (i = 0; i <= in_last_pixel - in_first_pixel; i++) - { - float in_pixel_center = (float)(i + in_first_pixel) + 0.5f; - coefficient_group[i] = stbir__filter_info_table[filter].kernel(in_center_of_out - in_pixel_center, 1 / scale); - - // If the coefficient is zero, skip it. (Don't do the <0 check here, we want the influence of those outside pixels.) - if (i == 0 && !coefficient_group[i]) - { - contributor->n0 = ++in_first_pixel; - i--; - continue; - } - - total_filter += coefficient_group[i]; - } - - STBIR_ASSERT(stbir__filter_info_table[filter].kernel((float)(in_last_pixel + 1) + 0.5f - in_center_of_out, 1/scale) == 0); - - STBIR_ASSERT(total_filter > 0.9); - STBIR_ASSERT(total_filter < 1.1f); // Make sure it's not way off. - - // Make sure the sum of all coefficients is 1. - filter_scale = 1 / total_filter; - - for (i = 0; i <= in_last_pixel - in_first_pixel; i++) - coefficient_group[i] *= filter_scale; - - for (i = in_last_pixel - in_first_pixel; i >= 0; i--) - { - if (coefficient_group[i]) - break; - - // This line has no weight. We can skip it. - contributor->n1 = contributor->n0 + i - 1; - } -} - -static void stbir__calculate_coefficients_downsample(stbir__info* stbir_info, stbir_filter filter, float scale_ratio, int out_first_pixel, int out_last_pixel, float out_center_of_in, stbir__contributors* contributor, float* coefficient_group) -{ - int i; - - STBIR_ASSERT(out_last_pixel - out_first_pixel <= (int)ceil(stbir__filter_info_table[filter].support(scale_ratio) * 2)); // Taken directly from stbir__get_coefficient_width() which we can't call because we don't know if we're horizontal or vertical. - - contributor->n0 = out_first_pixel; - contributor->n1 = out_last_pixel; - - STBIR_ASSERT(contributor->n1 >= contributor->n0); - - for (i = 0; i <= out_last_pixel - out_first_pixel; i++) - { - float out_pixel_center = (float)(i + out_first_pixel) + 0.5f; - float x = out_pixel_center - out_center_of_in; - coefficient_group[i] = stbir__filter_info_table[filter].kernel(x, scale_ratio) * scale_ratio; - } - - STBIR_ASSERT(stbir__filter_info_table[filter].kernel((float)(out_last_pixel + 1) + 0.5f - out_center_of_in, scale_ratio) == 0); - - for (i = out_last_pixel - out_first_pixel; i >= 0; i--) - { - if (coefficient_group[i]) - break; - - // This line has no weight. We can skip it. - contributor->n1 = contributor->n0 + i - 1; - } -} - -static void stbir__normalize_downsample_coefficients(stbir__info* stbir_info, stbir__contributors* contributors, float* coefficients, stbir_filter filter, float scale_ratio, float shift, int input_size, int output_size) -{ - int num_contributors = stbir__get_contributors(scale_ratio, filter, input_size, output_size); - int num_coefficients = stbir__get_coefficient_width(filter, scale_ratio); - int i, j; - int skip; - - for (i = 0; i < output_size; i++) - { - float scale; - float total = 0; - - for (j = 0; j < num_contributors; j++) - { - if (i >= contributors[j].n0 && i <= contributors[j].n1) - { - float coefficient = *stbir__get_coefficient(coefficients, filter, scale_ratio, j, i - contributors[j].n0); - total += coefficient; - } - else if (i < contributors[j].n0) - break; - } - - STBIR_ASSERT(total > 0.9f); - STBIR_ASSERT(total < 1.1f); - - scale = 1 / total; - - for (j = 0; j < num_contributors; j++) - { - if (i >= contributors[j].n0 && i <= contributors[j].n1) - *stbir__get_coefficient(coefficients, filter, scale_ratio, j, i - contributors[j].n0) *= scale; - else if (i < contributors[j].n0) - break; - } - } - - // Optimize: Skip zero coefficients and contributions outside of image bounds. - // Do this after normalizing because normalization depends on the n0/n1 values. - for (j = 0; j < num_contributors; j++) - { - int range, max, width; - - skip = 0; - while (*stbir__get_coefficient(coefficients, filter, scale_ratio, j, skip) == 0) - skip++; - - contributors[j].n0 += skip; - - while (contributors[j].n0 < 0) - { - contributors[j].n0++; - skip++; - } - - range = contributors[j].n1 - contributors[j].n0 + 1; - max = stbir__min(num_coefficients, range); - - width = stbir__get_coefficient_width(filter, scale_ratio); - for (i = 0; i < max; i++) - { - if (i + skip >= width) - break; - - *stbir__get_coefficient(coefficients, filter, scale_ratio, j, i) = *stbir__get_coefficient(coefficients, filter, scale_ratio, j, i + skip); - } - - continue; - } - - // Using min to avoid writing into invalid pixels. - for (i = 0; i < num_contributors; i++) - contributors[i].n1 = stbir__min(contributors[i].n1, output_size - 1); -} - -// Each scan line uses the same kernel values so we should calculate the kernel -// values once and then we can use them for every scan line. -static void stbir__calculate_filters(stbir__info* stbir_info, stbir__contributors* contributors, float* coefficients, stbir_filter filter, float scale_ratio, float shift, int input_size, int output_size) -{ - int n; - int total_contributors = stbir__get_contributors(scale_ratio, filter, input_size, output_size); - - if (stbir__use_upsampling(scale_ratio)) - { - float out_pixels_radius = stbir__filter_info_table[filter].support(1 / scale_ratio) * scale_ratio; - - // Looping through out pixels - for (n = 0; n < total_contributors; n++) - { - float in_center_of_out; // Center of the current out pixel in the in pixel space - int in_first_pixel, in_last_pixel; - - stbir__calculate_sample_range_upsample(n, out_pixels_radius, scale_ratio, shift, &in_first_pixel, &in_last_pixel, &in_center_of_out); - - stbir__calculate_coefficients_upsample(stbir_info, filter, scale_ratio, in_first_pixel, in_last_pixel, in_center_of_out, stbir__get_contributor(contributors, n), stbir__get_coefficient(coefficients, filter, scale_ratio, n, 0)); - } - } - else - { - float in_pixels_radius = stbir__filter_info_table[filter].support(scale_ratio) / scale_ratio; - - // Looping through in pixels - for (n = 0; n < total_contributors; n++) - { - float out_center_of_in; // Center of the current out pixel in the in pixel space - int out_first_pixel, out_last_pixel; - int n_adjusted = n - stbir__get_filter_pixel_margin(filter, scale_ratio); - - stbir__calculate_sample_range_downsample(n_adjusted, in_pixels_radius, scale_ratio, shift, &out_first_pixel, &out_last_pixel, &out_center_of_in); - - stbir__calculate_coefficients_downsample(stbir_info, filter, scale_ratio, out_first_pixel, out_last_pixel, out_center_of_in, stbir__get_contributor(contributors, n), stbir__get_coefficient(coefficients, filter, scale_ratio, n, 0)); - } - - stbir__normalize_downsample_coefficients(stbir_info, contributors, coefficients, filter, scale_ratio, shift, input_size, output_size); - } -} - -static float* stbir__get_decode_buffer(stbir__info* stbir_info) -{ - // The 0 index of the decode buffer starts after the margin. This makes - // it okay to use negative indexes on the decode buffer. - return &stbir_info->decode_buffer[stbir_info->horizontal_filter_pixel_margin * stbir_info->channels]; -} - -#define STBIR__DECODE(type, colorspace) ((type) * (STBIR_MAX_COLORSPACES) + (colorspace)) - -static void stbir__decode_scanline(stbir__info* stbir_info, int n) -{ - int c; - int channels = stbir_info->channels; - int alpha_channel = stbir_info->alpha_channel; - int type = stbir_info->type; - int colorspace = stbir_info->colorspace; - int input_w = stbir_info->input_w; - int input_stride_bytes = stbir_info->input_stride_bytes; - float* decode_buffer = stbir__get_decode_buffer(stbir_info); - stbir_edge edge_horizontal = stbir_info->edge_horizontal; - stbir_edge edge_vertical = stbir_info->edge_vertical; - int in_buffer_row_offset = stbir__edge_wrap(edge_vertical, n, stbir_info->input_h) * input_stride_bytes; - const void* input_data = (char *) stbir_info->input_data + in_buffer_row_offset; - int max_x = input_w + stbir_info->horizontal_filter_pixel_margin; - int decode = STBIR__DECODE(type, colorspace); - - int x = -stbir_info->horizontal_filter_pixel_margin; - - // special handling for STBIR_EDGE_ZERO because it needs to return an item that doesn't appear in the input, - // and we want to avoid paying overhead on every pixel if not STBIR_EDGE_ZERO - if (edge_vertical == STBIR_EDGE_ZERO && (n < 0 || n >= stbir_info->input_h)) - { - for (; x < max_x; x++) - for (c = 0; c < channels; c++) - decode_buffer[x*channels + c] = 0; - return; - } - - switch (decode) - { - case STBIR__DECODE(STBIR_TYPE_UINT8, STBIR_COLORSPACE_LINEAR): - for (; x < max_x; x++) - { - int decode_pixel_index = x * channels; - int input_pixel_index = stbir__edge_wrap(edge_horizontal, x, input_w) * channels; - for (c = 0; c < channels; c++) - decode_buffer[decode_pixel_index + c] = ((float)((const unsigned char*)input_data)[input_pixel_index + c]) / 255; - } - break; - - case STBIR__DECODE(STBIR_TYPE_UINT8, STBIR_COLORSPACE_SRGB): - for (; x < max_x; x++) - { - int decode_pixel_index = x * channels; - int input_pixel_index = stbir__edge_wrap(edge_horizontal, x, input_w) * channels; - for (c = 0; c < channels; c++) - decode_buffer[decode_pixel_index + c] = stbir__srgb_uchar_to_linear_float[((const unsigned char*)input_data)[input_pixel_index + c]]; - - if (!(stbir_info->flags&STBIR_FLAG_ALPHA_USES_COLORSPACE)) - decode_buffer[decode_pixel_index + alpha_channel] = ((float)((const unsigned char*)input_data)[input_pixel_index + alpha_channel]) / 255; - } - break; - - case STBIR__DECODE(STBIR_TYPE_UINT16, STBIR_COLORSPACE_LINEAR): - for (; x < max_x; x++) - { - int decode_pixel_index = x * channels; - int input_pixel_index = stbir__edge_wrap(edge_horizontal, x, input_w) * channels; - for (c = 0; c < channels; c++) - decode_buffer[decode_pixel_index + c] = ((float)((const unsigned short*)input_data)[input_pixel_index + c]) / 65535; - } - break; - - case STBIR__DECODE(STBIR_TYPE_UINT16, STBIR_COLORSPACE_SRGB): - for (; x < max_x; x++) - { - int decode_pixel_index = x * channels; - int input_pixel_index = stbir__edge_wrap(edge_horizontal, x, input_w) * channels; - for (c = 0; c < channels; c++) - decode_buffer[decode_pixel_index + c] = stbir__srgb_to_linear(((float)((const unsigned short*)input_data)[input_pixel_index + c]) / 65535); - - if (!(stbir_info->flags&STBIR_FLAG_ALPHA_USES_COLORSPACE)) - decode_buffer[decode_pixel_index + alpha_channel] = ((float)((const unsigned short*)input_data)[input_pixel_index + alpha_channel]) / 65535; - } - break; - - case STBIR__DECODE(STBIR_TYPE_UINT32, STBIR_COLORSPACE_LINEAR): - for (; x < max_x; x++) - { - int decode_pixel_index = x * channels; - int input_pixel_index = stbir__edge_wrap(edge_horizontal, x, input_w) * channels; - for (c = 0; c < channels; c++) - decode_buffer[decode_pixel_index + c] = (float)(((double)((const unsigned int*)input_data)[input_pixel_index + c]) / 4294967295); - } - break; - - case STBIR__DECODE(STBIR_TYPE_UINT32, STBIR_COLORSPACE_SRGB): - for (; x < max_x; x++) - { - int decode_pixel_index = x * channels; - int input_pixel_index = stbir__edge_wrap(edge_horizontal, x, input_w) * channels; - for (c = 0; c < channels; c++) - decode_buffer[decode_pixel_index + c] = stbir__srgb_to_linear((float)(((double)((const unsigned int*)input_data)[input_pixel_index + c]) / 4294967295)); - - if (!(stbir_info->flags&STBIR_FLAG_ALPHA_USES_COLORSPACE)) - decode_buffer[decode_pixel_index + alpha_channel] = (float)(((double)((const unsigned int*)input_data)[input_pixel_index + alpha_channel]) / 4294967295); - } - break; - - case STBIR__DECODE(STBIR_TYPE_FLOAT, STBIR_COLORSPACE_LINEAR): - for (; x < max_x; x++) - { - int decode_pixel_index = x * channels; - int input_pixel_index = stbir__edge_wrap(edge_horizontal, x, input_w) * channels; - for (c = 0; c < channels; c++) - decode_buffer[decode_pixel_index + c] = ((const float*)input_data)[input_pixel_index + c]; - } - break; - - case STBIR__DECODE(STBIR_TYPE_FLOAT, STBIR_COLORSPACE_SRGB): - for (; x < max_x; x++) - { - int decode_pixel_index = x * channels; - int input_pixel_index = stbir__edge_wrap(edge_horizontal, x, input_w) * channels; - for (c = 0; c < channels; c++) - decode_buffer[decode_pixel_index + c] = stbir__srgb_to_linear(((const float*)input_data)[input_pixel_index + c]); - - if (!(stbir_info->flags&STBIR_FLAG_ALPHA_USES_COLORSPACE)) - decode_buffer[decode_pixel_index + alpha_channel] = ((const float*)input_data)[input_pixel_index + alpha_channel]; - } - - break; - - default: - STBIR_ASSERT(!"Unknown type/colorspace/channels combination."); - break; - } - - if (!(stbir_info->flags & STBIR_FLAG_ALPHA_PREMULTIPLIED)) - { - for (x = -stbir_info->horizontal_filter_pixel_margin; x < max_x; x++) - { - int decode_pixel_index = x * channels; - - // If the alpha value is 0 it will clobber the color values. Make sure it's not. - float alpha = decode_buffer[decode_pixel_index + alpha_channel]; -#ifndef STBIR_NO_ALPHA_EPSILON - if (stbir_info->type != STBIR_TYPE_FLOAT) { - alpha += STBIR_ALPHA_EPSILON; - decode_buffer[decode_pixel_index + alpha_channel] = alpha; - } -#endif - for (c = 0; c < channels; c++) - { - if (c == alpha_channel) - continue; - - decode_buffer[decode_pixel_index + c] *= alpha; - } - } - } - - if (edge_horizontal == STBIR_EDGE_ZERO) - { - for (x = -stbir_info->horizontal_filter_pixel_margin; x < 0; x++) - { - for (c = 0; c < channels; c++) - decode_buffer[x*channels + c] = 0; - } - for (x = input_w; x < max_x; x++) - { - for (c = 0; c < channels; c++) - decode_buffer[x*channels + c] = 0; - } - } -} - -static float* stbir__get_ring_buffer_entry(float* ring_buffer, int index, int ring_buffer_length) -{ - return &ring_buffer[index * ring_buffer_length]; -} - -static float* stbir__add_empty_ring_buffer_entry(stbir__info* stbir_info, int n) -{ - int ring_buffer_index; - float* ring_buffer; - - if (stbir_info->ring_buffer_begin_index < 0) - { - ring_buffer_index = stbir_info->ring_buffer_begin_index = 0; - stbir_info->ring_buffer_first_scanline = n; - } - else - { - ring_buffer_index = (stbir_info->ring_buffer_begin_index + (stbir_info->ring_buffer_last_scanline - stbir_info->ring_buffer_first_scanline) + 1) % stbir_info->vertical_filter_pixel_width; - STBIR_ASSERT(ring_buffer_index != stbir_info->ring_buffer_begin_index); - } - - ring_buffer = stbir__get_ring_buffer_entry(stbir_info->ring_buffer, ring_buffer_index, stbir_info->ring_buffer_length_bytes / sizeof(float)); - memset(ring_buffer, 0, stbir_info->ring_buffer_length_bytes); - - stbir_info->ring_buffer_last_scanline = n; - - return ring_buffer; -} - - -static void stbir__resample_horizontal_upsample(stbir__info* stbir_info, int n, float* output_buffer) -{ - int x, k; - int output_w = stbir_info->output_w; - int kernel_pixel_width = stbir_info->horizontal_filter_pixel_width; - int channels = stbir_info->channels; - float* decode_buffer = stbir__get_decode_buffer(stbir_info); - stbir__contributors* horizontal_contributors = stbir_info->horizontal_contributors; - float* horizontal_coefficients = stbir_info->horizontal_coefficients; - int coefficient_width = stbir_info->horizontal_coefficient_width; - - for (x = 0; x < output_w; x++) - { - int n0 = horizontal_contributors[x].n0; - int n1 = horizontal_contributors[x].n1; - - int out_pixel_index = x * channels; - int coefficient_group = coefficient_width * x; - int coefficient_counter = 0; - - STBIR_ASSERT(n1 >= n0); - STBIR_ASSERT(n0 >= -stbir_info->horizontal_filter_pixel_margin); - STBIR_ASSERT(n1 >= -stbir_info->horizontal_filter_pixel_margin); - STBIR_ASSERT(n0 < stbir_info->input_w + stbir_info->horizontal_filter_pixel_margin); - STBIR_ASSERT(n1 < stbir_info->input_w + stbir_info->horizontal_filter_pixel_margin); - - switch (channels) { - case 1: - for (k = n0; k <= n1; k++) - { - int in_pixel_index = k * 1; - float coefficient = horizontal_coefficients[coefficient_group + coefficient_counter++]; - STBIR_ASSERT(coefficient != 0); - output_buffer[out_pixel_index + 0] += decode_buffer[in_pixel_index + 0] * coefficient; - } - break; - case 2: - for (k = n0; k <= n1; k++) - { - int in_pixel_index = k * 2; - float coefficient = horizontal_coefficients[coefficient_group + coefficient_counter++]; - STBIR_ASSERT(coefficient != 0); - output_buffer[out_pixel_index + 0] += decode_buffer[in_pixel_index + 0] * coefficient; - output_buffer[out_pixel_index + 1] += decode_buffer[in_pixel_index + 1] * coefficient; - } - break; - case 3: - for (k = n0; k <= n1; k++) - { - int in_pixel_index = k * 3; - float coefficient = horizontal_coefficients[coefficient_group + coefficient_counter++]; - STBIR_ASSERT(coefficient != 0); - output_buffer[out_pixel_index + 0] += decode_buffer[in_pixel_index + 0] * coefficient; - output_buffer[out_pixel_index + 1] += decode_buffer[in_pixel_index + 1] * coefficient; - output_buffer[out_pixel_index + 2] += decode_buffer[in_pixel_index + 2] * coefficient; - } - break; - case 4: - for (k = n0; k <= n1; k++) - { - int in_pixel_index = k * 4; - float coefficient = horizontal_coefficients[coefficient_group + coefficient_counter++]; - STBIR_ASSERT(coefficient != 0); - output_buffer[out_pixel_index + 0] += decode_buffer[in_pixel_index + 0] * coefficient; - output_buffer[out_pixel_index + 1] += decode_buffer[in_pixel_index + 1] * coefficient; - output_buffer[out_pixel_index + 2] += decode_buffer[in_pixel_index + 2] * coefficient; - output_buffer[out_pixel_index + 3] += decode_buffer[in_pixel_index + 3] * coefficient; - } - break; - default: - for (k = n0; k <= n1; k++) - { - int in_pixel_index = k * channels; - float coefficient = horizontal_coefficients[coefficient_group + coefficient_counter++]; - int c; - STBIR_ASSERT(coefficient != 0); - for (c = 0; c < channels; c++) - output_buffer[out_pixel_index + c] += decode_buffer[in_pixel_index + c] * coefficient; - } - break; - } - } -} - -static void stbir__resample_horizontal_downsample(stbir__info* stbir_info, int n, float* output_buffer) -{ - int x, k; - int input_w = stbir_info->input_w; - int output_w = stbir_info->output_w; - int kernel_pixel_width = stbir_info->horizontal_filter_pixel_width; - int channels = stbir_info->channels; - float* decode_buffer = stbir__get_decode_buffer(stbir_info); - stbir__contributors* horizontal_contributors = stbir_info->horizontal_contributors; - float* horizontal_coefficients = stbir_info->horizontal_coefficients; - int coefficient_width = stbir_info->horizontal_coefficient_width; - int filter_pixel_margin = stbir_info->horizontal_filter_pixel_margin; - int max_x = input_w + filter_pixel_margin * 2; - - STBIR_ASSERT(!stbir__use_width_upsampling(stbir_info)); - - switch (channels) { - case 1: - for (x = 0; x < max_x; x++) - { - int n0 = horizontal_contributors[x].n0; - int n1 = horizontal_contributors[x].n1; - - int in_x = x - filter_pixel_margin; - int in_pixel_index = in_x * 1; - int max_n = n1; - int coefficient_group = coefficient_width * x; - - for (k = n0; k <= max_n; k++) - { - int out_pixel_index = k * 1; - float coefficient = horizontal_coefficients[coefficient_group + k - n0]; - STBIR_ASSERT(coefficient != 0); - output_buffer[out_pixel_index + 0] += decode_buffer[in_pixel_index + 0] * coefficient; - } - } - break; - - case 2: - for (x = 0; x < max_x; x++) - { - int n0 = horizontal_contributors[x].n0; - int n1 = horizontal_contributors[x].n1; - - int in_x = x - filter_pixel_margin; - int in_pixel_index = in_x * 2; - int max_n = n1; - int coefficient_group = coefficient_width * x; - - for (k = n0; k <= max_n; k++) - { - int out_pixel_index = k * 2; - float coefficient = horizontal_coefficients[coefficient_group + k - n0]; - STBIR_ASSERT(coefficient != 0); - output_buffer[out_pixel_index + 0] += decode_buffer[in_pixel_index + 0] * coefficient; - output_buffer[out_pixel_index + 1] += decode_buffer[in_pixel_index + 1] * coefficient; - } - } - break; - - case 3: - for (x = 0; x < max_x; x++) - { - int n0 = horizontal_contributors[x].n0; - int n1 = horizontal_contributors[x].n1; - - int in_x = x - filter_pixel_margin; - int in_pixel_index = in_x * 3; - int max_n = n1; - int coefficient_group = coefficient_width * x; - - for (k = n0; k <= max_n; k++) - { - int out_pixel_index = k * 3; - float coefficient = horizontal_coefficients[coefficient_group + k - n0]; - STBIR_ASSERT(coefficient != 0); - output_buffer[out_pixel_index + 0] += decode_buffer[in_pixel_index + 0] * coefficient; - output_buffer[out_pixel_index + 1] += decode_buffer[in_pixel_index + 1] * coefficient; - output_buffer[out_pixel_index + 2] += decode_buffer[in_pixel_index + 2] * coefficient; - } - } - break; - - case 4: - for (x = 0; x < max_x; x++) - { - int n0 = horizontal_contributors[x].n0; - int n1 = horizontal_contributors[x].n1; - - int in_x = x - filter_pixel_margin; - int in_pixel_index = in_x * 4; - int max_n = n1; - int coefficient_group = coefficient_width * x; - - for (k = n0; k <= max_n; k++) - { - int out_pixel_index = k * 4; - float coefficient = horizontal_coefficients[coefficient_group + k - n0]; - STBIR_ASSERT(coefficient != 0); - output_buffer[out_pixel_index + 0] += decode_buffer[in_pixel_index + 0] * coefficient; - output_buffer[out_pixel_index + 1] += decode_buffer[in_pixel_index + 1] * coefficient; - output_buffer[out_pixel_index + 2] += decode_buffer[in_pixel_index + 2] * coefficient; - output_buffer[out_pixel_index + 3] += decode_buffer[in_pixel_index + 3] * coefficient; - } - } - break; - - default: - for (x = 0; x < max_x; x++) - { - int n0 = horizontal_contributors[x].n0; - int n1 = horizontal_contributors[x].n1; - - int in_x = x - filter_pixel_margin; - int in_pixel_index = in_x * channels; - int max_n = n1; - int coefficient_group = coefficient_width * x; - - for (k = n0; k <= max_n; k++) - { - int c; - int out_pixel_index = k * channels; - float coefficient = horizontal_coefficients[coefficient_group + k - n0]; - STBIR_ASSERT(coefficient != 0); - for (c = 0; c < channels; c++) - output_buffer[out_pixel_index + c] += decode_buffer[in_pixel_index + c] * coefficient; - } - } - break; - } -} - -static void stbir__decode_and_resample_upsample(stbir__info* stbir_info, int n) -{ - // Decode the nth scanline from the source image into the decode buffer. - stbir__decode_scanline(stbir_info, n); - - // Now resample it into the ring buffer. - if (stbir__use_width_upsampling(stbir_info)) - stbir__resample_horizontal_upsample(stbir_info, n, stbir__add_empty_ring_buffer_entry(stbir_info, n)); - else - stbir__resample_horizontal_downsample(stbir_info, n, stbir__add_empty_ring_buffer_entry(stbir_info, n)); - - // Now it's sitting in the ring buffer ready to be used as source for the vertical sampling. -} - -static void stbir__decode_and_resample_downsample(stbir__info* stbir_info, int n) -{ - // Decode the nth scanline from the source image into the decode buffer. - stbir__decode_scanline(stbir_info, n); - - memset(stbir_info->horizontal_buffer, 0, stbir_info->output_w * stbir_info->channels * sizeof(float)); - - // Now resample it into the horizontal buffer. - if (stbir__use_width_upsampling(stbir_info)) - stbir__resample_horizontal_upsample(stbir_info, n, stbir_info->horizontal_buffer); - else - stbir__resample_horizontal_downsample(stbir_info, n, stbir_info->horizontal_buffer); - - // Now it's sitting in the horizontal buffer ready to be distributed into the ring buffers. -} - -// Get the specified scan line from the ring buffer. -static float* stbir__get_ring_buffer_scanline(int get_scanline, float* ring_buffer, int begin_index, int first_scanline, int ring_buffer_size, int ring_buffer_length) -{ - int ring_buffer_index = (begin_index + (get_scanline - first_scanline)) % ring_buffer_size; - return stbir__get_ring_buffer_entry(ring_buffer, ring_buffer_index, ring_buffer_length); -} - - -static void stbir__encode_scanline(stbir__info* stbir_info, int num_pixels, void *output_buffer, float *encode_buffer, int channels, int alpha_channel, int decode) -{ - int x; - int n; - int num_nonalpha; - stbir_uint16 nonalpha[STBIR_MAX_CHANNELS]; - - if (!(stbir_info->flags&STBIR_FLAG_ALPHA_PREMULTIPLIED)) - { - for (x=0; x < num_pixels; ++x) - { - int pixel_index = x*channels; - - float alpha = encode_buffer[pixel_index + alpha_channel]; - float reciprocal_alpha = alpha ? 1.0f / alpha : 0; - - // unrolling this produced a 1% slowdown upscaling a large RGBA linear-space image on my machine - stb - for (n = 0; n < channels; n++) - if (n != alpha_channel) - encode_buffer[pixel_index + n] *= reciprocal_alpha; - - // We added in a small epsilon to prevent the color channel from being deleted with zero alpha. - // Because we only add it for integer types, it will automatically be discarded on integer - // conversion, so we don't need to subtract it back out (which would be problematic for - // numeric precision reasons). - } - } - - // build a table of all channels that need colorspace correction, so - // we don't perform colorspace correction on channels that don't need it. - for (x=0, num_nonalpha=0; x < channels; ++x) - if (x != alpha_channel || (stbir_info->flags & STBIR_FLAG_ALPHA_USES_COLORSPACE)) - nonalpha[num_nonalpha++] = x; - - #define STBIR__ROUND_INT(f) ((int) ((f)+0.5)) - #define STBIR__ROUND_UINT(f) ((stbir_uint32) ((f)+0.5)) - - #ifdef STBIR__SATURATE_INT - #define STBIR__ENCODE_LINEAR8(f) stbir__saturate8 (STBIR__ROUND_INT((f) * 255 )) - #define STBIR__ENCODE_LINEAR16(f) stbir__saturate16(STBIR__ROUND_INT((f) * 65535)) - #else - #define STBIR__ENCODE_LINEAR8(f) (unsigned char ) STBIR__ROUND_INT(stbir__saturate(f) * 255 ) - #define STBIR__ENCODE_LINEAR16(f) (unsigned short) STBIR__ROUND_INT(stbir__saturate(f) * 65535) - #endif - - switch (decode) - { - case STBIR__DECODE(STBIR_TYPE_UINT8, STBIR_COLORSPACE_LINEAR): - for (x=0; x < num_pixels; ++x) - { - int pixel_index = x*channels; - - for (n = 0; n < channels; n++) - { - int index = pixel_index + n; - ((unsigned char*)output_buffer)[index] = STBIR__ENCODE_LINEAR8(encode_buffer[index]); - } - } - break; - - case STBIR__DECODE(STBIR_TYPE_UINT8, STBIR_COLORSPACE_SRGB): - for (x=0; x < num_pixels; ++x) - { - int pixel_index = x*channels; - - for (n = 0; n < num_nonalpha; n++) - { - int index = pixel_index + nonalpha[n]; - ((unsigned char*)output_buffer)[index] = stbir__linear_to_srgb_uchar(encode_buffer[index]); - } - - if (!(stbir_info->flags & STBIR_FLAG_ALPHA_USES_COLORSPACE)) - ((unsigned char *)output_buffer)[pixel_index + alpha_channel] = STBIR__ENCODE_LINEAR8(encode_buffer[pixel_index+alpha_channel]); - } - break; - - case STBIR__DECODE(STBIR_TYPE_UINT16, STBIR_COLORSPACE_LINEAR): - for (x=0; x < num_pixels; ++x) - { - int pixel_index = x*channels; - - for (n = 0; n < channels; n++) - { - int index = pixel_index + n; - ((unsigned short*)output_buffer)[index] = STBIR__ENCODE_LINEAR16(encode_buffer[index]); - } - } - break; - - case STBIR__DECODE(STBIR_TYPE_UINT16, STBIR_COLORSPACE_SRGB): - for (x=0; x < num_pixels; ++x) - { - int pixel_index = x*channels; - - for (n = 0; n < num_nonalpha; n++) - { - int index = pixel_index + nonalpha[n]; - ((unsigned short*)output_buffer)[index] = (unsigned short)STBIR__ROUND_INT(stbir__linear_to_srgb(stbir__saturate(encode_buffer[index])) * 65535); - } - - if (!(stbir_info->flags&STBIR_FLAG_ALPHA_USES_COLORSPACE)) - ((unsigned short*)output_buffer)[pixel_index + alpha_channel] = STBIR__ENCODE_LINEAR16(encode_buffer[pixel_index + alpha_channel]); - } - - break; - - case STBIR__DECODE(STBIR_TYPE_UINT32, STBIR_COLORSPACE_LINEAR): - for (x=0; x < num_pixels; ++x) - { - int pixel_index = x*channels; - - for (n = 0; n < channels; n++) - { - int index = pixel_index + n; - ((unsigned int*)output_buffer)[index] = (unsigned int)STBIR__ROUND_UINT(((double)stbir__saturate(encode_buffer[index])) * 4294967295); - } - } - break; - - case STBIR__DECODE(STBIR_TYPE_UINT32, STBIR_COLORSPACE_SRGB): - for (x=0; x < num_pixels; ++x) - { - int pixel_index = x*channels; - - for (n = 0; n < num_nonalpha; n++) - { - int index = pixel_index + nonalpha[n]; - ((unsigned int*)output_buffer)[index] = (unsigned int)STBIR__ROUND_UINT(((double)stbir__linear_to_srgb(stbir__saturate(encode_buffer[index]))) * 4294967295); - } - - if (!(stbir_info->flags&STBIR_FLAG_ALPHA_USES_COLORSPACE)) - ((unsigned int*)output_buffer)[pixel_index + alpha_channel] = (unsigned int)STBIR__ROUND_INT(((double)stbir__saturate(encode_buffer[pixel_index + alpha_channel])) * 4294967295); - } - break; - - case STBIR__DECODE(STBIR_TYPE_FLOAT, STBIR_COLORSPACE_LINEAR): - for (x=0; x < num_pixels; ++x) - { - int pixel_index = x*channels; - - for (n = 0; n < channels; n++) - { - int index = pixel_index + n; - ((float*)output_buffer)[index] = encode_buffer[index]; - } - } - break; - - case STBIR__DECODE(STBIR_TYPE_FLOAT, STBIR_COLORSPACE_SRGB): - for (x=0; x < num_pixels; ++x) - { - int pixel_index = x*channels; - - for (n = 0; n < num_nonalpha; n++) - { - int index = pixel_index + nonalpha[n]; - ((float*)output_buffer)[index] = stbir__linear_to_srgb(encode_buffer[index]); - } - - if (!(stbir_info->flags&STBIR_FLAG_ALPHA_USES_COLORSPACE)) - ((float*)output_buffer)[pixel_index + alpha_channel] = encode_buffer[pixel_index + alpha_channel]; - } - break; - - default: - STBIR_ASSERT(!"Unknown type/colorspace/channels combination."); - break; - } -} - -static void stbir__resample_vertical_upsample(stbir__info* stbir_info, int n, int in_first_scanline, int in_last_scanline, float in_center_of_out) -{ - int x, k; - int output_w = stbir_info->output_w; - stbir__contributors* vertical_contributors = stbir_info->vertical_contributors; - float* vertical_coefficients = stbir_info->vertical_coefficients; - int channels = stbir_info->channels; - int alpha_channel = stbir_info->alpha_channel; - int type = stbir_info->type; - int colorspace = stbir_info->colorspace; - int kernel_pixel_width = stbir_info->vertical_filter_pixel_width; - void* output_data = stbir_info->output_data; - float* encode_buffer = stbir_info->encode_buffer; - int decode = STBIR__DECODE(type, colorspace); - int coefficient_width = stbir_info->vertical_coefficient_width; - int coefficient_counter; - int contributor = n; - - float* ring_buffer = stbir_info->ring_buffer; - int ring_buffer_begin_index = stbir_info->ring_buffer_begin_index; - int ring_buffer_first_scanline = stbir_info->ring_buffer_first_scanline; - int ring_buffer_last_scanline = stbir_info->ring_buffer_last_scanline; - int ring_buffer_length = stbir_info->ring_buffer_length_bytes/sizeof(float); - - int n0,n1, output_row_start; - int coefficient_group = coefficient_width * contributor; - - n0 = vertical_contributors[contributor].n0; - n1 = vertical_contributors[contributor].n1; - - output_row_start = n * stbir_info->output_stride_bytes; - - STBIR_ASSERT(stbir__use_height_upsampling(stbir_info)); - - memset(encode_buffer, 0, output_w * sizeof(float) * channels); - - // I tried reblocking this for better cache usage of encode_buffer - // (using x_outer, k, x_inner), but it lost speed. -- stb - - coefficient_counter = 0; - switch (channels) { - case 1: - for (k = n0; k <= n1; k++) - { - int coefficient_index = coefficient_counter++; - float* ring_buffer_entry = stbir__get_ring_buffer_scanline(k, ring_buffer, ring_buffer_begin_index, ring_buffer_first_scanline, kernel_pixel_width, ring_buffer_length); - float coefficient = vertical_coefficients[coefficient_group + coefficient_index]; - for (x = 0; x < output_w; ++x) - { - int in_pixel_index = x * 1; - encode_buffer[in_pixel_index + 0] += ring_buffer_entry[in_pixel_index + 0] * coefficient; - } - } - break; - case 2: - for (k = n0; k <= n1; k++) - { - int coefficient_index = coefficient_counter++; - float* ring_buffer_entry = stbir__get_ring_buffer_scanline(k, ring_buffer, ring_buffer_begin_index, ring_buffer_first_scanline, kernel_pixel_width, ring_buffer_length); - float coefficient = vertical_coefficients[coefficient_group + coefficient_index]; - for (x = 0; x < output_w; ++x) - { - int in_pixel_index = x * 2; - encode_buffer[in_pixel_index + 0] += ring_buffer_entry[in_pixel_index + 0] * coefficient; - encode_buffer[in_pixel_index + 1] += ring_buffer_entry[in_pixel_index + 1] * coefficient; - } - } - break; - case 3: - for (k = n0; k <= n1; k++) - { - int coefficient_index = coefficient_counter++; - float* ring_buffer_entry = stbir__get_ring_buffer_scanline(k, ring_buffer, ring_buffer_begin_index, ring_buffer_first_scanline, kernel_pixel_width, ring_buffer_length); - float coefficient = vertical_coefficients[coefficient_group + coefficient_index]; - for (x = 0; x < output_w; ++x) - { - int in_pixel_index = x * 3; - encode_buffer[in_pixel_index + 0] += ring_buffer_entry[in_pixel_index + 0] * coefficient; - encode_buffer[in_pixel_index + 1] += ring_buffer_entry[in_pixel_index + 1] * coefficient; - encode_buffer[in_pixel_index + 2] += ring_buffer_entry[in_pixel_index + 2] * coefficient; - } - } - break; - case 4: - for (k = n0; k <= n1; k++) - { - int coefficient_index = coefficient_counter++; - float* ring_buffer_entry = stbir__get_ring_buffer_scanline(k, ring_buffer, ring_buffer_begin_index, ring_buffer_first_scanline, kernel_pixel_width, ring_buffer_length); - float coefficient = vertical_coefficients[coefficient_group + coefficient_index]; - for (x = 0; x < output_w; ++x) - { - int in_pixel_index = x * 4; - encode_buffer[in_pixel_index + 0] += ring_buffer_entry[in_pixel_index + 0] * coefficient; - encode_buffer[in_pixel_index + 1] += ring_buffer_entry[in_pixel_index + 1] * coefficient; - encode_buffer[in_pixel_index + 2] += ring_buffer_entry[in_pixel_index + 2] * coefficient; - encode_buffer[in_pixel_index + 3] += ring_buffer_entry[in_pixel_index + 3] * coefficient; - } - } - break; - default: - for (k = n0; k <= n1; k++) - { - int coefficient_index = coefficient_counter++; - float* ring_buffer_entry = stbir__get_ring_buffer_scanline(k, ring_buffer, ring_buffer_begin_index, ring_buffer_first_scanline, kernel_pixel_width, ring_buffer_length); - float coefficient = vertical_coefficients[coefficient_group + coefficient_index]; - for (x = 0; x < output_w; ++x) - { - int in_pixel_index = x * channels; - int c; - for (c = 0; c < channels; c++) - encode_buffer[in_pixel_index + c] += ring_buffer_entry[in_pixel_index + c] * coefficient; - } - } - break; - } - stbir__encode_scanline(stbir_info, output_w, (char *) output_data + output_row_start, encode_buffer, channels, alpha_channel, decode); -} - -static void stbir__resample_vertical_downsample(stbir__info* stbir_info, int n, int in_first_scanline, int in_last_scanline, float in_center_of_out) -{ - int x, k; - int output_w = stbir_info->output_w; - int output_h = stbir_info->output_h; - stbir__contributors* vertical_contributors = stbir_info->vertical_contributors; - float* vertical_coefficients = stbir_info->vertical_coefficients; - int channels = stbir_info->channels; - int kernel_pixel_width = stbir_info->vertical_filter_pixel_width; - void* output_data = stbir_info->output_data; - float* horizontal_buffer = stbir_info->horizontal_buffer; - int coefficient_width = stbir_info->vertical_coefficient_width; - int contributor = n + stbir_info->vertical_filter_pixel_margin; - - float* ring_buffer = stbir_info->ring_buffer; - int ring_buffer_begin_index = stbir_info->ring_buffer_begin_index; - int ring_buffer_first_scanline = stbir_info->ring_buffer_first_scanline; - int ring_buffer_last_scanline = stbir_info->ring_buffer_last_scanline; - int ring_buffer_length = stbir_info->ring_buffer_length_bytes/sizeof(float); - int n0,n1; - - n0 = vertical_contributors[contributor].n0; - n1 = vertical_contributors[contributor].n1; - - STBIR_ASSERT(!stbir__use_height_upsampling(stbir_info)); - - for (k = n0; k <= n1; k++) - { - int coefficient_index = k - n0; - int coefficient_group = coefficient_width * contributor; - float coefficient = vertical_coefficients[coefficient_group + coefficient_index]; - - float* ring_buffer_entry = stbir__get_ring_buffer_scanline(k, ring_buffer, ring_buffer_begin_index, ring_buffer_first_scanline, kernel_pixel_width, ring_buffer_length); - - switch (channels) { - case 1: - for (x = 0; x < output_w; x++) - { - int in_pixel_index = x * 1; - ring_buffer_entry[in_pixel_index + 0] += horizontal_buffer[in_pixel_index + 0] * coefficient; - } - break; - case 2: - for (x = 0; x < output_w; x++) - { - int in_pixel_index = x * 2; - ring_buffer_entry[in_pixel_index + 0] += horizontal_buffer[in_pixel_index + 0] * coefficient; - ring_buffer_entry[in_pixel_index + 1] += horizontal_buffer[in_pixel_index + 1] * coefficient; - } - break; - case 3: - for (x = 0; x < output_w; x++) - { - int in_pixel_index = x * 3; - ring_buffer_entry[in_pixel_index + 0] += horizontal_buffer[in_pixel_index + 0] * coefficient; - ring_buffer_entry[in_pixel_index + 1] += horizontal_buffer[in_pixel_index + 1] * coefficient; - ring_buffer_entry[in_pixel_index + 2] += horizontal_buffer[in_pixel_index + 2] * coefficient; - } - break; - case 4: - for (x = 0; x < output_w; x++) - { - int in_pixel_index = x * 4; - ring_buffer_entry[in_pixel_index + 0] += horizontal_buffer[in_pixel_index + 0] * coefficient; - ring_buffer_entry[in_pixel_index + 1] += horizontal_buffer[in_pixel_index + 1] * coefficient; - ring_buffer_entry[in_pixel_index + 2] += horizontal_buffer[in_pixel_index + 2] * coefficient; - ring_buffer_entry[in_pixel_index + 3] += horizontal_buffer[in_pixel_index + 3] * coefficient; - } - break; - default: - for (x = 0; x < output_w; x++) - { - int in_pixel_index = x * channels; - - int c; - for (c = 0; c < channels; c++) - ring_buffer_entry[in_pixel_index + c] += horizontal_buffer[in_pixel_index + c] * coefficient; - } - break; - } - } -} - -static void stbir__buffer_loop_upsample(stbir__info* stbir_info) -{ - int y; - float scale_ratio = stbir_info->vertical_scale; - float out_scanlines_radius = stbir__filter_info_table[stbir_info->vertical_filter].support(1/scale_ratio) * scale_ratio; - - STBIR_ASSERT(stbir__use_height_upsampling(stbir_info)); - - for (y = 0; y < stbir_info->output_h; y++) - { - float in_center_of_out = 0; // Center of the current out scanline in the in scanline space - int in_first_scanline = 0, in_last_scanline = 0; - - stbir__calculate_sample_range_upsample(y, out_scanlines_radius, scale_ratio, stbir_info->vertical_shift, &in_first_scanline, &in_last_scanline, &in_center_of_out); - - STBIR_ASSERT(in_last_scanline - in_first_scanline <= stbir_info->vertical_filter_pixel_width); - - if (stbir_info->ring_buffer_begin_index >= 0) - { - // Get rid of whatever we don't need anymore. - while (in_first_scanline > stbir_info->ring_buffer_first_scanline) - { - if (stbir_info->ring_buffer_first_scanline == stbir_info->ring_buffer_last_scanline) - { - // We just popped the last scanline off the ring buffer. - // Reset it to the empty state. - stbir_info->ring_buffer_begin_index = -1; - stbir_info->ring_buffer_first_scanline = 0; - stbir_info->ring_buffer_last_scanline = 0; - break; - } - else - { - stbir_info->ring_buffer_first_scanline++; - stbir_info->ring_buffer_begin_index = (stbir_info->ring_buffer_begin_index + 1) % stbir_info->vertical_filter_pixel_width; - } - } - } - - // Load in new ones. - if (stbir_info->ring_buffer_begin_index < 0) - stbir__decode_and_resample_upsample(stbir_info, in_first_scanline); - - while (in_last_scanline > stbir_info->ring_buffer_last_scanline) - stbir__decode_and_resample_upsample(stbir_info, stbir_info->ring_buffer_last_scanline + 1); - - // Now all buffers should be ready to write a row of vertical sampling. - stbir__resample_vertical_upsample(stbir_info, y, in_first_scanline, in_last_scanline, in_center_of_out); - - STBIR_PROGRESS_REPORT((float)y / stbir_info->output_h); - } -} - -static void stbir__empty_ring_buffer(stbir__info* stbir_info, int first_necessary_scanline) -{ - int output_stride_bytes = stbir_info->output_stride_bytes; - int channels = stbir_info->channels; - int alpha_channel = stbir_info->alpha_channel; - int type = stbir_info->type; - int colorspace = stbir_info->colorspace; - int output_w = stbir_info->output_w; - void* output_data = stbir_info->output_data; - int decode = STBIR__DECODE(type, colorspace); - - float* ring_buffer = stbir_info->ring_buffer; - int ring_buffer_length = stbir_info->ring_buffer_length_bytes/sizeof(float); - - if (stbir_info->ring_buffer_begin_index >= 0) - { - // Get rid of whatever we don't need anymore. - while (first_necessary_scanline > stbir_info->ring_buffer_first_scanline) - { - if (stbir_info->ring_buffer_first_scanline >= 0 && stbir_info->ring_buffer_first_scanline < stbir_info->output_h) - { - int output_row_start = stbir_info->ring_buffer_first_scanline * output_stride_bytes; - float* ring_buffer_entry = stbir__get_ring_buffer_entry(ring_buffer, stbir_info->ring_buffer_begin_index, ring_buffer_length); - stbir__encode_scanline(stbir_info, output_w, (char *) output_data + output_row_start, ring_buffer_entry, channels, alpha_channel, decode); - STBIR_PROGRESS_REPORT((float)stbir_info->ring_buffer_first_scanline / stbir_info->output_h); - } - - if (stbir_info->ring_buffer_first_scanline == stbir_info->ring_buffer_last_scanline) - { - // We just popped the last scanline off the ring buffer. - // Reset it to the empty state. - stbir_info->ring_buffer_begin_index = -1; - stbir_info->ring_buffer_first_scanline = 0; - stbir_info->ring_buffer_last_scanline = 0; - break; - } - else - { - stbir_info->ring_buffer_first_scanline++; - stbir_info->ring_buffer_begin_index = (stbir_info->ring_buffer_begin_index + 1) % stbir_info->vertical_filter_pixel_width; - } - } - } -} - -static void stbir__buffer_loop_downsample(stbir__info* stbir_info) -{ - int y; - float scale_ratio = stbir_info->vertical_scale; - int output_h = stbir_info->output_h; - float in_pixels_radius = stbir__filter_info_table[stbir_info->vertical_filter].support(scale_ratio) / scale_ratio; - int pixel_margin = stbir_info->vertical_filter_pixel_margin; - int max_y = stbir_info->input_h + pixel_margin; - - STBIR_ASSERT(!stbir__use_height_upsampling(stbir_info)); - - for (y = -pixel_margin; y < max_y; y++) - { - float out_center_of_in; // Center of the current out scanline in the in scanline space - int out_first_scanline, out_last_scanline; - - stbir__calculate_sample_range_downsample(y, in_pixels_radius, scale_ratio, stbir_info->vertical_shift, &out_first_scanline, &out_last_scanline, &out_center_of_in); - - STBIR_ASSERT(out_last_scanline - out_first_scanline <= stbir_info->vertical_filter_pixel_width); - - if (out_last_scanline < 0 || out_first_scanline >= output_h) - continue; - - stbir__empty_ring_buffer(stbir_info, out_first_scanline); - - stbir__decode_and_resample_downsample(stbir_info, y); - - // Load in new ones. - if (stbir_info->ring_buffer_begin_index < 0) - stbir__add_empty_ring_buffer_entry(stbir_info, out_first_scanline); - - while (out_last_scanline > stbir_info->ring_buffer_last_scanline) - stbir__add_empty_ring_buffer_entry(stbir_info, stbir_info->ring_buffer_last_scanline + 1); - - // Now the horizontal buffer is ready to write to all ring buffer rows. - stbir__resample_vertical_downsample(stbir_info, y, out_first_scanline, out_last_scanline, out_center_of_in); - } - - stbir__empty_ring_buffer(stbir_info, stbir_info->output_h); -} - -static void stbir__setup(stbir__info *info, int input_w, int input_h, int output_w, int output_h, int channels) -{ - info->input_w = input_w; - info->input_h = input_h; - info->output_w = output_w; - info->output_h = output_h; - info->channels = channels; -} - -static void stbir__calculate_transform(stbir__info *info, float s0, float t0, float s1, float t1, float *transform) -{ - info->s0 = s0; - info->t0 = t0; - info->s1 = s1; - info->t1 = t1; - - if (transform) - { - info->horizontal_scale = transform[0]; - info->vertical_scale = transform[1]; - info->horizontal_shift = transform[2]; - info->vertical_shift = transform[3]; - } - else - { - info->horizontal_scale = ((float)info->output_w / info->input_w) / (s1 - s0); - info->vertical_scale = ((float)info->output_h / info->input_h) / (t1 - t0); - - info->horizontal_shift = s0 * info->output_w / (s1 - s0); - info->vertical_shift = t0 * info->output_h / (t1 - t0); - } -} - -static void stbir__choose_filter(stbir__info *info, stbir_filter h_filter, stbir_filter v_filter) -{ - if (h_filter == 0) - h_filter = stbir__use_upsampling(info->horizontal_scale) ? STBIR_DEFAULT_FILTER_UPSAMPLE : STBIR_DEFAULT_FILTER_DOWNSAMPLE; - if (v_filter == 0) - v_filter = stbir__use_upsampling(info->vertical_scale) ? STBIR_DEFAULT_FILTER_UPSAMPLE : STBIR_DEFAULT_FILTER_DOWNSAMPLE; - info->horizontal_filter = h_filter; - info->vertical_filter = v_filter; -} - -static stbir_uint32 stbir__calculate_memory(stbir__info *info) -{ - int pixel_margin = stbir__get_filter_pixel_margin(info->horizontal_filter, info->horizontal_scale); - int filter_height = stbir__get_filter_pixel_width(info->vertical_filter, info->vertical_scale); - - info->horizontal_num_contributors = stbir__get_contributors(info->horizontal_scale, info->horizontal_filter, info->input_w, info->output_w); - info->vertical_num_contributors = stbir__get_contributors(info->vertical_scale , info->vertical_filter , info->input_h, info->output_h); - - info->horizontal_contributors_size = info->horizontal_num_contributors * sizeof(stbir__contributors); - info->horizontal_coefficients_size = stbir__get_total_horizontal_coefficients(info) * sizeof(float); - info->vertical_contributors_size = info->vertical_num_contributors * sizeof(stbir__contributors); - info->vertical_coefficients_size = stbir__get_total_vertical_coefficients(info) * sizeof(float); - info->decode_buffer_size = (info->input_w + pixel_margin * 2) * info->channels * sizeof(float); - info->horizontal_buffer_size = info->output_w * info->channels * sizeof(float); - info->ring_buffer_size = info->output_w * info->channels * filter_height * sizeof(float); - info->encode_buffer_size = info->output_w * info->channels * sizeof(float); - - STBIR_ASSERT(info->horizontal_filter != 0); - STBIR_ASSERT(info->horizontal_filter < STBIR__ARRAY_SIZE(stbir__filter_info_table)); // this now happens too late - STBIR_ASSERT(info->vertical_filter != 0); - STBIR_ASSERT(info->vertical_filter < STBIR__ARRAY_SIZE(stbir__filter_info_table)); // this now happens too late - - if (stbir__use_height_upsampling(info)) - // The horizontal buffer is for when we're downsampling the height and we - // can't output the result of sampling the decode buffer directly into the - // ring buffers. - info->horizontal_buffer_size = 0; - else - // The encode buffer is to retain precision in the height upsampling method - // and isn't used when height downsampling. - info->encode_buffer_size = 0; - - return info->horizontal_contributors_size + info->horizontal_coefficients_size - + info->vertical_contributors_size + info->vertical_coefficients_size - + info->decode_buffer_size + info->horizontal_buffer_size - + info->ring_buffer_size + info->encode_buffer_size; -} - -static int stbir__resize_allocated(stbir__info *info, - const void* input_data, int input_stride_in_bytes, - void* output_data, int output_stride_in_bytes, - int alpha_channel, stbir_uint32 flags, stbir_datatype type, - stbir_edge edge_horizontal, stbir_edge edge_vertical, stbir_colorspace colorspace, - void* tempmem, size_t tempmem_size_in_bytes) -{ - size_t memory_required = stbir__calculate_memory(info); - - int width_stride_input = input_stride_in_bytes ? input_stride_in_bytes : info->channels * info->input_w * stbir__type_size[type]; - int width_stride_output = output_stride_in_bytes ? output_stride_in_bytes : info->channels * info->output_w * stbir__type_size[type]; - -#ifdef STBIR_DEBUG_OVERWRITE_TEST -#define OVERWRITE_ARRAY_SIZE 8 - unsigned char overwrite_output_before_pre[OVERWRITE_ARRAY_SIZE]; - unsigned char overwrite_tempmem_before_pre[OVERWRITE_ARRAY_SIZE]; - unsigned char overwrite_output_after_pre[OVERWRITE_ARRAY_SIZE]; - unsigned char overwrite_tempmem_after_pre[OVERWRITE_ARRAY_SIZE]; - - size_t begin_forbidden = width_stride_output * (info->output_h - 1) + info->output_w * info->channels * stbir__type_size[type]; - memcpy(overwrite_output_before_pre, &((unsigned char*)output_data)[-OVERWRITE_ARRAY_SIZE], OVERWRITE_ARRAY_SIZE); - memcpy(overwrite_output_after_pre, &((unsigned char*)output_data)[begin_forbidden], OVERWRITE_ARRAY_SIZE); - memcpy(overwrite_tempmem_before_pre, &((unsigned char*)tempmem)[-OVERWRITE_ARRAY_SIZE], OVERWRITE_ARRAY_SIZE); - memcpy(overwrite_tempmem_after_pre, &((unsigned char*)tempmem)[tempmem_size_in_bytes], OVERWRITE_ARRAY_SIZE); -#endif - - STBIR_ASSERT(info->channels >= 0); - STBIR_ASSERT(info->channels <= STBIR_MAX_CHANNELS); - - if (info->channels < 0 || info->channels > STBIR_MAX_CHANNELS) - return 0; - - STBIR_ASSERT(info->horizontal_filter < STBIR__ARRAY_SIZE(stbir__filter_info_table)); - STBIR_ASSERT(info->vertical_filter < STBIR__ARRAY_SIZE(stbir__filter_info_table)); - - if (info->horizontal_filter >= STBIR__ARRAY_SIZE(stbir__filter_info_table)) - return 0; - if (info->vertical_filter >= STBIR__ARRAY_SIZE(stbir__filter_info_table)) - return 0; - - if (alpha_channel < 0) - flags |= STBIR_FLAG_ALPHA_USES_COLORSPACE | STBIR_FLAG_ALPHA_PREMULTIPLIED; - - if (!(flags&STBIR_FLAG_ALPHA_USES_COLORSPACE) || !(flags&STBIR_FLAG_ALPHA_PREMULTIPLIED)) - STBIR_ASSERT(alpha_channel >= 0 && alpha_channel < info->channels); - - if (alpha_channel >= info->channels) - return 0; - - STBIR_ASSERT(tempmem); - - if (!tempmem) - return 0; - - STBIR_ASSERT(tempmem_size_in_bytes >= memory_required); - - if (tempmem_size_in_bytes < memory_required) - return 0; - - memset(tempmem, 0, tempmem_size_in_bytes); - - info->input_data = input_data; - info->input_stride_bytes = width_stride_input; - - info->output_data = output_data; - info->output_stride_bytes = width_stride_output; - - info->alpha_channel = alpha_channel; - info->flags = flags; - info->type = type; - info->edge_horizontal = edge_horizontal; - info->edge_vertical = edge_vertical; - info->colorspace = colorspace; - - info->horizontal_coefficient_width = stbir__get_coefficient_width (info->horizontal_filter, info->horizontal_scale); - info->vertical_coefficient_width = stbir__get_coefficient_width (info->vertical_filter , info->vertical_scale ); - info->horizontal_filter_pixel_width = stbir__get_filter_pixel_width (info->horizontal_filter, info->horizontal_scale); - info->vertical_filter_pixel_width = stbir__get_filter_pixel_width (info->vertical_filter , info->vertical_scale ); - info->horizontal_filter_pixel_margin = stbir__get_filter_pixel_margin(info->horizontal_filter, info->horizontal_scale); - info->vertical_filter_pixel_margin = stbir__get_filter_pixel_margin(info->vertical_filter , info->vertical_scale ); - - info->ring_buffer_length_bytes = info->output_w * info->channels * sizeof(float); - info->decode_buffer_pixels = info->input_w + info->horizontal_filter_pixel_margin * 2; - -#define STBIR__NEXT_MEMPTR(current, newtype) (newtype*)(((unsigned char*)current) + current##_size) - - info->horizontal_contributors = (stbir__contributors *) tempmem; - info->horizontal_coefficients = STBIR__NEXT_MEMPTR(info->horizontal_contributors, float); - info->vertical_contributors = STBIR__NEXT_MEMPTR(info->horizontal_coefficients, stbir__contributors); - info->vertical_coefficients = STBIR__NEXT_MEMPTR(info->vertical_contributors, float); - info->decode_buffer = STBIR__NEXT_MEMPTR(info->vertical_coefficients, float); - - if (stbir__use_height_upsampling(info)) - { - info->horizontal_buffer = NULL; - info->ring_buffer = STBIR__NEXT_MEMPTR(info->decode_buffer, float); - info->encode_buffer = STBIR__NEXT_MEMPTR(info->ring_buffer, float); - - STBIR_ASSERT((size_t)STBIR__NEXT_MEMPTR(info->encode_buffer, unsigned char) == (size_t)tempmem + tempmem_size_in_bytes); - } - else - { - info->horizontal_buffer = STBIR__NEXT_MEMPTR(info->decode_buffer, float); - info->ring_buffer = STBIR__NEXT_MEMPTR(info->horizontal_buffer, float); - info->encode_buffer = NULL; - - STBIR_ASSERT((size_t)STBIR__NEXT_MEMPTR(info->ring_buffer, unsigned char) == (size_t)tempmem + tempmem_size_in_bytes); - } - -#undef STBIR__NEXT_MEMPTR - - // This signals that the ring buffer is empty - info->ring_buffer_begin_index = -1; - - stbir__calculate_filters(info, info->horizontal_contributors, info->horizontal_coefficients, info->horizontal_filter, info->horizontal_scale, info->horizontal_shift, info->input_w, info->output_w); - stbir__calculate_filters(info, info->vertical_contributors, info->vertical_coefficients, info->vertical_filter, info->vertical_scale, info->vertical_shift, info->input_h, info->output_h); - - STBIR_PROGRESS_REPORT(0); - - if (stbir__use_height_upsampling(info)) - stbir__buffer_loop_upsample(info); - else - stbir__buffer_loop_downsample(info); - - STBIR_PROGRESS_REPORT(1); - -#ifdef STBIR_DEBUG_OVERWRITE_TEST - STBIR_ASSERT(memcmp(overwrite_output_before_pre, &((unsigned char*)output_data)[-OVERWRITE_ARRAY_SIZE], OVERWRITE_ARRAY_SIZE) == 0); - STBIR_ASSERT(memcmp(overwrite_output_after_pre, &((unsigned char*)output_data)[begin_forbidden], OVERWRITE_ARRAY_SIZE) == 0); - STBIR_ASSERT(memcmp(overwrite_tempmem_before_pre, &((unsigned char*)tempmem)[-OVERWRITE_ARRAY_SIZE], OVERWRITE_ARRAY_SIZE) == 0); - STBIR_ASSERT(memcmp(overwrite_tempmem_after_pre, &((unsigned char*)tempmem)[tempmem_size_in_bytes], OVERWRITE_ARRAY_SIZE) == 0); -#endif - - return 1; -} - - -static int stbir__resize_arbitrary( - void *alloc_context, - const void* input_data, int input_w, int input_h, int input_stride_in_bytes, - void* output_data, int output_w, int output_h, int output_stride_in_bytes, - float s0, float t0, float s1, float t1, float *transform, - int channels, int alpha_channel, stbir_uint32 flags, stbir_datatype type, - stbir_filter h_filter, stbir_filter v_filter, - stbir_edge edge_horizontal, stbir_edge edge_vertical, stbir_colorspace colorspace) -{ - stbir__info info; - int result; - size_t memory_required; - void* extra_memory; - - stbir__setup(&info, input_w, input_h, output_w, output_h, channels); - stbir__calculate_transform(&info, s0,t0,s1,t1,transform); - stbir__choose_filter(&info, h_filter, v_filter); - memory_required = stbir__calculate_memory(&info); - extra_memory = STBIR_MALLOC(memory_required, alloc_context); - - if (!extra_memory) - return 0; - - result = stbir__resize_allocated(&info, input_data, input_stride_in_bytes, - output_data, output_stride_in_bytes, - alpha_channel, flags, type, - edge_horizontal, edge_vertical, - colorspace, extra_memory, memory_required); - - STBIR_FREE(extra_memory, alloc_context); - - return result; -} - -STBIRDEF int stbir_resize_uint8( const unsigned char *input_pixels , int input_w , int input_h , int input_stride_in_bytes, - unsigned char *output_pixels, int output_w, int output_h, int output_stride_in_bytes, - int num_channels) -{ - return stbir__resize_arbitrary(NULL, input_pixels, input_w, input_h, input_stride_in_bytes, - output_pixels, output_w, output_h, output_stride_in_bytes, - 0,0,1,1,NULL,num_channels,-1,0, STBIR_TYPE_UINT8, STBIR_FILTER_DEFAULT, STBIR_FILTER_DEFAULT, - STBIR_EDGE_CLAMP, STBIR_EDGE_CLAMP, STBIR_COLORSPACE_LINEAR); -} - -STBIRDEF int stbir_resize_float( const float *input_pixels , int input_w , int input_h , int input_stride_in_bytes, - float *output_pixels, int output_w, int output_h, int output_stride_in_bytes, - int num_channels) -{ - return stbir__resize_arbitrary(NULL, input_pixels, input_w, input_h, input_stride_in_bytes, - output_pixels, output_w, output_h, output_stride_in_bytes, - 0,0,1,1,NULL,num_channels,-1,0, STBIR_TYPE_FLOAT, STBIR_FILTER_DEFAULT, STBIR_FILTER_DEFAULT, - STBIR_EDGE_CLAMP, STBIR_EDGE_CLAMP, STBIR_COLORSPACE_LINEAR); -} - -STBIRDEF int stbir_resize_uint8_srgb(const unsigned char *input_pixels , int input_w , int input_h , int input_stride_in_bytes, - unsigned char *output_pixels, int output_w, int output_h, int output_stride_in_bytes, - int num_channels, int alpha_channel, int flags) -{ - return stbir__resize_arbitrary(NULL, input_pixels, input_w, input_h, input_stride_in_bytes, - output_pixels, output_w, output_h, output_stride_in_bytes, - 0,0,1,1,NULL,num_channels,alpha_channel,flags, STBIR_TYPE_UINT8, STBIR_FILTER_DEFAULT, STBIR_FILTER_DEFAULT, - STBIR_EDGE_CLAMP, STBIR_EDGE_CLAMP, STBIR_COLORSPACE_SRGB); -} - -STBIRDEF int stbir_resize_uint8_srgb_edgemode(const unsigned char *input_pixels , int input_w , int input_h , int input_stride_in_bytes, - unsigned char *output_pixels, int output_w, int output_h, int output_stride_in_bytes, - int num_channels, int alpha_channel, int flags, - stbir_edge edge_wrap_mode) -{ - return stbir__resize_arbitrary(NULL, input_pixels, input_w, input_h, input_stride_in_bytes, - output_pixels, output_w, output_h, output_stride_in_bytes, - 0,0,1,1,NULL,num_channels,alpha_channel,flags, STBIR_TYPE_UINT8, STBIR_FILTER_DEFAULT, STBIR_FILTER_DEFAULT, - edge_wrap_mode, edge_wrap_mode, STBIR_COLORSPACE_SRGB); -} - -STBIRDEF int stbir_resize_uint8_generic( const unsigned char *input_pixels , int input_w , int input_h , int input_stride_in_bytes, - unsigned char *output_pixels, int output_w, int output_h, int output_stride_in_bytes, - int num_channels, int alpha_channel, int flags, - stbir_edge edge_wrap_mode, stbir_filter filter, stbir_colorspace space, - void *alloc_context) -{ - return stbir__resize_arbitrary(alloc_context, input_pixels, input_w, input_h, input_stride_in_bytes, - output_pixels, output_w, output_h, output_stride_in_bytes, - 0,0,1,1,NULL,num_channels,alpha_channel,flags, STBIR_TYPE_UINT8, filter, filter, - edge_wrap_mode, edge_wrap_mode, space); -} - -STBIRDEF int stbir_resize_uint16_generic(const stbir_uint16 *input_pixels , int input_w , int input_h , int input_stride_in_bytes, - stbir_uint16 *output_pixels , int output_w, int output_h, int output_stride_in_bytes, - int num_channels, int alpha_channel, int flags, - stbir_edge edge_wrap_mode, stbir_filter filter, stbir_colorspace space, - void *alloc_context) -{ - return stbir__resize_arbitrary(alloc_context, input_pixels, input_w, input_h, input_stride_in_bytes, - output_pixels, output_w, output_h, output_stride_in_bytes, - 0,0,1,1,NULL,num_channels,alpha_channel,flags, STBIR_TYPE_UINT16, filter, filter, - edge_wrap_mode, edge_wrap_mode, space); -} - - -STBIRDEF int stbir_resize_float_generic( const float *input_pixels , int input_w , int input_h , int input_stride_in_bytes, - float *output_pixels , int output_w, int output_h, int output_stride_in_bytes, - int num_channels, int alpha_channel, int flags, - stbir_edge edge_wrap_mode, stbir_filter filter, stbir_colorspace space, - void *alloc_context) -{ - return stbir__resize_arbitrary(alloc_context, input_pixels, input_w, input_h, input_stride_in_bytes, - output_pixels, output_w, output_h, output_stride_in_bytes, - 0,0,1,1,NULL,num_channels,alpha_channel,flags, STBIR_TYPE_FLOAT, filter, filter, - edge_wrap_mode, edge_wrap_mode, space); -} - - -STBIRDEF int stbir_resize( const void *input_pixels , int input_w , int input_h , int input_stride_in_bytes, - void *output_pixels, int output_w, int output_h, int output_stride_in_bytes, - stbir_datatype datatype, - int num_channels, int alpha_channel, int flags, - stbir_edge edge_mode_horizontal, stbir_edge edge_mode_vertical, - stbir_filter filter_horizontal, stbir_filter filter_vertical, - stbir_colorspace space, void *alloc_context) -{ - return stbir__resize_arbitrary(alloc_context, input_pixels, input_w, input_h, input_stride_in_bytes, - output_pixels, output_w, output_h, output_stride_in_bytes, - 0,0,1,1,NULL,num_channels,alpha_channel,flags, datatype, filter_horizontal, filter_vertical, - edge_mode_horizontal, edge_mode_vertical, space); -} - - -STBIRDEF int stbir_resize_subpixel(const void *input_pixels , int input_w , int input_h , int input_stride_in_bytes, - void *output_pixels, int output_w, int output_h, int output_stride_in_bytes, - stbir_datatype datatype, - int num_channels, int alpha_channel, int flags, - stbir_edge edge_mode_horizontal, stbir_edge edge_mode_vertical, - stbir_filter filter_horizontal, stbir_filter filter_vertical, - stbir_colorspace space, void *alloc_context, - float x_scale, float y_scale, - float x_offset, float y_offset) -{ - float transform[4]; - transform[0] = x_scale; - transform[1] = y_scale; - transform[2] = x_offset; - transform[3] = y_offset; - return stbir__resize_arbitrary(alloc_context, input_pixels, input_w, input_h, input_stride_in_bytes, - output_pixels, output_w, output_h, output_stride_in_bytes, - 0,0,1,1,transform,num_channels,alpha_channel,flags, datatype, filter_horizontal, filter_vertical, - edge_mode_horizontal, edge_mode_vertical, space); -} - -STBIRDEF int stbir_resize_region( const void *input_pixels , int input_w , int input_h , int input_stride_in_bytes, - void *output_pixels, int output_w, int output_h, int output_stride_in_bytes, - stbir_datatype datatype, - int num_channels, int alpha_channel, int flags, - stbir_edge edge_mode_horizontal, stbir_edge edge_mode_vertical, - stbir_filter filter_horizontal, stbir_filter filter_vertical, - stbir_colorspace space, void *alloc_context, - float s0, float t0, float s1, float t1) -{ - return stbir__resize_arbitrary(alloc_context, input_pixels, input_w, input_h, input_stride_in_bytes, - output_pixels, output_w, output_h, output_stride_in_bytes, - s0,t0,s1,t1,NULL,num_channels,alpha_channel,flags, datatype, filter_horizontal, filter_vertical, - edge_mode_horizontal, edge_mode_vertical, space); -} - -#endif // STB_IMAGE_RESIZE_IMPLEMENTATION diff --git a/impeller/third_party/stb/stb/stb_image_write.h b/impeller/third_party/stb/stb/stb_image_write.h deleted file mode 100644 index 4319c0de1d93d..0000000000000 --- a/impeller/third_party/stb/stb/stb_image_write.h +++ /dev/null @@ -1,1048 +0,0 @@ -/* stb_image_write - v1.02 - public domain - http://nothings.org/stb/stb_image_write.h - writes out PNG/BMP/TGA images to C stdio - Sean Barrett 2010-2015 - no warranty implied; use at your own risk - - Before #including, - - #define STB_IMAGE_WRITE_IMPLEMENTATION - - in the file that you want to have the implementation. - - Will probably not work correctly with strict-aliasing optimizations. - -ABOUT: - - This header file is a library for writing images to C stdio. It could be - adapted to write to memory or a general streaming interface; let me know. - - The PNG output is not optimal; it is 20-50% larger than the file - written by a decent optimizing implementation. This library is designed - for source code compactness and simplicity, not optimal image file size - or run-time performance. - -BUILDING: - - You can #define STBIW_ASSERT(x) before the #include to avoid using assert.h. - You can #define STBIW_MALLOC(), STBIW_REALLOC(), and STBIW_FREE() to replace - malloc,realloc,free. - You can define STBIW_MEMMOVE() to replace memmove() - -USAGE: - - There are four functions, one for each image file format: - - int stbi_write_png(char const *filename, int w, int h, int comp, const void *data, int stride_in_bytes); - int stbi_write_bmp(char const *filename, int w, int h, int comp, const void *data); - int stbi_write_tga(char const *filename, int w, int h, int comp, const void *data); - int stbi_write_hdr(char const *filename, int w, int h, int comp, const float *data); - - There are also four equivalent functions that use an arbitrary write function. You are - expected to open/close your file-equivalent before and after calling these: - - int stbi_write_png_to_func(stbi_write_func *func, void *context, int w, int h, int comp, const void *data, int stride_in_bytes); - int stbi_write_bmp_to_func(stbi_write_func *func, void *context, int w, int h, int comp, const void *data); - int stbi_write_tga_to_func(stbi_write_func *func, void *context, int w, int h, int comp, const void *data); - int stbi_write_hdr_to_func(stbi_write_func *func, void *context, int w, int h, int comp, const float *data); - - where the callback is: - void stbi_write_func(void *context, void *data, int size); - - You can define STBI_WRITE_NO_STDIO to disable the file variant of these - functions, so the library will not use stdio.h at all. However, this will - also disable HDR writing, because it requires stdio for formatted output. - - Each function returns 0 on failure and non-0 on success. - - The functions create an image file defined by the parameters. The image - is a rectangle of pixels stored from left-to-right, top-to-bottom. - Each pixel contains 'comp' channels of data stored interleaved with 8-bits - per channel, in the following order: 1=Y, 2=YA, 3=RGB, 4=RGBA. (Y is - monochrome color.) The rectangle is 'w' pixels wide and 'h' pixels tall. - The *data pointer points to the first byte of the top-left-most pixel. - For PNG, "stride_in_bytes" is the distance in bytes from the first byte of - a row of pixels to the first byte of the next row of pixels. - - PNG creates output files with the same number of components as the input. - The BMP format expands Y to RGB in the file format and does not - output alpha. - - PNG supports writing rectangles of data even when the bytes storing rows of - data are not consecutive in memory (e.g. sub-rectangles of a larger image), - by supplying the stride between the beginning of adjacent rows. The other - formats do not. (Thus you cannot write a native-format BMP through the BMP - writer, both because it is in BGR order and because it may have padding - at the end of the line.) - - HDR expects linear float data. Since the format is always 32-bit rgb(e) - data, alpha (if provided) is discarded, and for monochrome data it is - replicated across all three channels. - - TGA supports RLE or non-RLE compressed data. To use non-RLE-compressed - data, set the global variable 'stbi_write_tga_with_rle' to 0. - -CREDITS: - - PNG/BMP/TGA - Sean Barrett - HDR - Baldur Karlsson - TGA monochrome: - Jean-Sebastien Guay - misc enhancements: - Tim Kelsey - TGA RLE - Alan Hickman - initial file IO callback implementation - Emmanuel Julien - bugfixes: - github:Chribba - Guillaume Chereau - github:jry2 - github:romigrou - Sergio Gonzalez - Jonas Karlsson - Filip Wasil - Thatcher Ulrich - -LICENSE - -This software is dual-licensed to the public domain and under the following -license: you are granted a perpetual, irrevocable license to copy, modify, -publish, and distribute this file as you see fit. - -*/ - -#ifndef INCLUDE_STB_IMAGE_WRITE_H -#define INCLUDE_STB_IMAGE_WRITE_H - -#ifdef __cplusplus -extern "C" { -#endif - -#ifdef STB_IMAGE_WRITE_STATIC -#define STBIWDEF static -#else -#define STBIWDEF extern -extern int stbi_write_tga_with_rle; -#endif - -#ifndef STBI_WRITE_NO_STDIO -STBIWDEF int stbi_write_png(char const *filename, int w, int h, int comp, const void *data, int stride_in_bytes); -STBIWDEF int stbi_write_bmp(char const *filename, int w, int h, int comp, const void *data); -STBIWDEF int stbi_write_tga(char const *filename, int w, int h, int comp, const void *data); -STBIWDEF int stbi_write_hdr(char const *filename, int w, int h, int comp, const float *data); -#endif - -typedef void stbi_write_func(void *context, void *data, int size); - -STBIWDEF int stbi_write_png_to_func(stbi_write_func *func, void *context, int w, int h, int comp, const void *data, int stride_in_bytes); -STBIWDEF int stbi_write_bmp_to_func(stbi_write_func *func, void *context, int w, int h, int comp, const void *data); -STBIWDEF int stbi_write_tga_to_func(stbi_write_func *func, void *context, int w, int h, int comp, const void *data); -STBIWDEF int stbi_write_hdr_to_func(stbi_write_func *func, void *context, int w, int h, int comp, const float *data); - -#ifdef __cplusplus -} -#endif - -#endif//INCLUDE_STB_IMAGE_WRITE_H - -#ifdef STB_IMAGE_WRITE_IMPLEMENTATION - -#ifdef _WIN32 - #ifndef _CRT_SECURE_NO_WARNINGS - #define _CRT_SECURE_NO_WARNINGS - #endif - #ifndef _CRT_NONSTDC_NO_DEPRECATE - #define _CRT_NONSTDC_NO_DEPRECATE - #endif -#endif - -#ifndef STBI_WRITE_NO_STDIO -#include -#endif // STBI_WRITE_NO_STDIO - -#include -#include -#include -#include - -#if defined(STBIW_MALLOC) && defined(STBIW_FREE) && (defined(STBIW_REALLOC) || defined(STBIW_REALLOC_SIZED)) -// ok -#elif !defined(STBIW_MALLOC) && !defined(STBIW_FREE) && !defined(STBIW_REALLOC) && !defined(STBIW_REALLOC_SIZED) -// ok -#else -#error "Must define all or none of STBIW_MALLOC, STBIW_FREE, and STBIW_REALLOC (or STBIW_REALLOC_SIZED)." -#endif - -#ifndef STBIW_MALLOC -#define STBIW_MALLOC(sz) malloc(sz) -#define STBIW_REALLOC(p,newsz) realloc(p,newsz) -#define STBIW_FREE(p) free(p) -#endif - -#ifndef STBIW_REALLOC_SIZED -#define STBIW_REALLOC_SIZED(p,oldsz,newsz) STBIW_REALLOC(p,newsz) -#endif - - -#ifndef STBIW_MEMMOVE -#define STBIW_MEMMOVE(a,b,sz) memmove(a,b,sz) -#endif - - -#ifndef STBIW_ASSERT -#include -#define STBIW_ASSERT(x) assert(x) -#endif - -#define STBIW_UCHAR(x) (unsigned char) ((x) & 0xff) - -typedef struct -{ - stbi_write_func *func; - void *context; -} stbi__write_context; - -// initialize a callback-based context -static void stbi__start_write_callbacks(stbi__write_context *s, stbi_write_func *c, void *context) -{ - s->func = c; - s->context = context; -} - -#ifndef STBI_WRITE_NO_STDIO - -static void stbi__stdio_write(void *context, void *data, int size) -{ - fwrite(data,1,size,(FILE*) context); -} - -static int stbi__start_write_file(stbi__write_context *s, const char *filename) -{ - FILE *f = fopen(filename, "wb"); - stbi__start_write_callbacks(s, stbi__stdio_write, (void *) f); - return f != NULL; -} - -static void stbi__end_write_file(stbi__write_context *s) -{ - fclose((FILE *)s->context); -} - -#endif // !STBI_WRITE_NO_STDIO - -typedef unsigned int stbiw_uint32; -typedef int stb_image_write_test[sizeof(stbiw_uint32)==4 ? 1 : -1]; - -#ifdef STB_IMAGE_WRITE_STATIC -static int stbi_write_tga_with_rle = 1; -#else -int stbi_write_tga_with_rle = 1; -#endif - -static void stbiw__writefv(stbi__write_context *s, const char *fmt, va_list v) -{ - while (*fmt) { - switch (*fmt++) { - case ' ': break; - case '1': { unsigned char x = STBIW_UCHAR(va_arg(v, int)); - s->func(s->context,&x,1); - break; } - case '2': { int x = va_arg(v,int); - unsigned char b[2]; - b[0] = STBIW_UCHAR(x); - b[1] = STBIW_UCHAR(x>>8); - s->func(s->context,b,2); - break; } - case '4': { stbiw_uint32 x = va_arg(v,int); - unsigned char b[4]; - b[0]=STBIW_UCHAR(x); - b[1]=STBIW_UCHAR(x>>8); - b[2]=STBIW_UCHAR(x>>16); - b[3]=STBIW_UCHAR(x>>24); - s->func(s->context,b,4); - break; } - default: - STBIW_ASSERT(0); - return; - } - } -} - -static void stbiw__writef(stbi__write_context *s, const char *fmt, ...) -{ - va_list v; - va_start(v, fmt); - stbiw__writefv(s, fmt, v); - va_end(v); -} - -static void stbiw__write3(stbi__write_context *s, unsigned char a, unsigned char b, unsigned char c) -{ - unsigned char arr[3]; - arr[0] = a, arr[1] = b, arr[2] = c; - s->func(s->context, arr, 3); -} - -static void stbiw__write_pixel(stbi__write_context *s, int rgb_dir, int comp, int write_alpha, int expand_mono, unsigned char *d) -{ - unsigned char bg[3] = { 255, 0, 255}, px[3]; - int k; - - if (write_alpha < 0) - s->func(s->context, &d[comp - 1], 1); - - switch (comp) { - case 1: - s->func(s->context,d,1); - break; - case 2: - if (expand_mono) - stbiw__write3(s, d[0], d[0], d[0]); // monochrome bmp - else - s->func(s->context, d, 1); // monochrome TGA - break; - case 4: - if (!write_alpha) { - // composite against pink background - for (k = 0; k < 3; ++k) - px[k] = bg[k] + ((d[k] - bg[k]) * d[3]) / 255; - stbiw__write3(s, px[1 - rgb_dir], px[1], px[1 + rgb_dir]); - break; - } - /* FALLTHROUGH */ - case 3: - stbiw__write3(s, d[1 - rgb_dir], d[1], d[1 + rgb_dir]); - break; - } - if (write_alpha > 0) - s->func(s->context, &d[comp - 1], 1); -} - -static void stbiw__write_pixels(stbi__write_context *s, int rgb_dir, int vdir, int x, int y, int comp, void *data, int write_alpha, int scanline_pad, int expand_mono) -{ - stbiw_uint32 zero = 0; - int i,j, j_end; - - if (y <= 0) - return; - - if (vdir < 0) - j_end = -1, j = y-1; - else - j_end = y, j = 0; - - for (; j != j_end; j += vdir) { - for (i=0; i < x; ++i) { - unsigned char *d = (unsigned char *) data + (j*x+i)*comp; - stbiw__write_pixel(s, rgb_dir, comp, write_alpha, expand_mono, d); - } - s->func(s->context, &zero, scanline_pad); - } -} - -static int stbiw__outfile(stbi__write_context *s, int rgb_dir, int vdir, int x, int y, int comp, int expand_mono, void *data, int alpha, int pad, const char *fmt, ...) -{ - if (y < 0 || x < 0) { - return 0; - } else { - va_list v; - va_start(v, fmt); - stbiw__writefv(s, fmt, v); - va_end(v); - stbiw__write_pixels(s,rgb_dir,vdir,x,y,comp,data,alpha,pad, expand_mono); - return 1; - } -} - -static int stbi_write_bmp_core(stbi__write_context *s, int x, int y, int comp, const void *data) -{ - int pad = (-x*3) & 3; - return stbiw__outfile(s,-1,-1,x,y,comp,1,(void *) data,0,pad, - "11 4 22 4" "4 44 22 444444", - 'B', 'M', 14+40+(x*3+pad)*y, 0,0, 14+40, // file header - 40, x,y, 1,24, 0,0,0,0,0,0); // bitmap header -} - -STBIWDEF int stbi_write_bmp_to_func(stbi_write_func *func, void *context, int x, int y, int comp, const void *data) -{ - stbi__write_context s; - stbi__start_write_callbacks(&s, func, context); - return stbi_write_bmp_core(&s, x, y, comp, data); -} - -#ifndef STBI_WRITE_NO_STDIO -STBIWDEF int stbi_write_bmp(char const *filename, int x, int y, int comp, const void *data) -{ - stbi__write_context s; - if (stbi__start_write_file(&s,filename)) { - int r = stbi_write_bmp_core(&s, x, y, comp, data); - stbi__end_write_file(&s); - return r; - } else - return 0; -} -#endif //!STBI_WRITE_NO_STDIO - -static int stbi_write_tga_core(stbi__write_context *s, int x, int y, int comp, void *data) -{ - int has_alpha = (comp == 2 || comp == 4); - int colorbytes = has_alpha ? comp-1 : comp; - int format = colorbytes < 2 ? 3 : 2; // 3 color channels (RGB/RGBA) = 2, 1 color channel (Y/YA) = 3 - - if (y < 0 || x < 0) - return 0; - - if (!stbi_write_tga_with_rle) { - return stbiw__outfile(s, -1, -1, x, y, comp, 0, (void *) data, has_alpha, 0, - "111 221 2222 11", 0, 0, format, 0, 0, 0, 0, 0, x, y, (colorbytes + has_alpha) * 8, has_alpha * 8); - } else { - int i,j,k; - - stbiw__writef(s, "111 221 2222 11", 0,0,format+8, 0,0,0, 0,0,x,y, (colorbytes + has_alpha) * 8, has_alpha * 8); - - for (j = y - 1; j >= 0; --j) { - unsigned char *row = (unsigned char *) data + j * x * comp; - int len; - - for (i = 0; i < x; i += len) { - unsigned char *begin = row + i * comp; - int diff = 1; - len = 1; - - if (i < x - 1) { - ++len; - diff = memcmp(begin, row + (i + 1) * comp, comp); - if (diff) { - const unsigned char *prev = begin; - for (k = i + 2; k < x && len < 128; ++k) { - if (memcmp(prev, row + k * comp, comp)) { - prev += comp; - ++len; - } else { - --len; - break; - } - } - } else { - for (k = i + 2; k < x && len < 128; ++k) { - if (!memcmp(begin, row + k * comp, comp)) { - ++len; - } else { - break; - } - } - } - } - - if (diff) { - unsigned char header = STBIW_UCHAR(len - 1); - s->func(s->context, &header, 1); - for (k = 0; k < len; ++k) { - stbiw__write_pixel(s, -1, comp, has_alpha, 0, begin + k * comp); - } - } else { - unsigned char header = STBIW_UCHAR(len - 129); - s->func(s->context, &header, 1); - stbiw__write_pixel(s, -1, comp, has_alpha, 0, begin); - } - } - } - } - return 1; -} - -int stbi_write_tga_to_func(stbi_write_func *func, void *context, int x, int y, int comp, const void *data) -{ - stbi__write_context s; - stbi__start_write_callbacks(&s, func, context); - return stbi_write_tga_core(&s, x, y, comp, (void *) data); -} - -#ifndef STBI_WRITE_NO_STDIO -int stbi_write_tga(char const *filename, int x, int y, int comp, const void *data) -{ - stbi__write_context s; - if (stbi__start_write_file(&s,filename)) { - int r = stbi_write_tga_core(&s, x, y, comp, (void *) data); - stbi__end_write_file(&s); - return r; - } else - return 0; -} -#endif - -// ************************************************************************************************* -// Radiance RGBE HDR writer -// by Baldur Karlsson -#ifndef STBI_WRITE_NO_STDIO - -#define stbiw__max(a, b) ((a) > (b) ? (a) : (b)) - -void stbiw__linear_to_rgbe(unsigned char *rgbe, float *linear) -{ - int exponent; - float maxcomp = stbiw__max(linear[0], stbiw__max(linear[1], linear[2])); - - if (maxcomp < 1e-32f) { - rgbe[0] = rgbe[1] = rgbe[2] = rgbe[3] = 0; - } else { - float normalize = (float) frexp(maxcomp, &exponent) * 256.0f/maxcomp; - - rgbe[0] = (unsigned char)(linear[0] * normalize); - rgbe[1] = (unsigned char)(linear[1] * normalize); - rgbe[2] = (unsigned char)(linear[2] * normalize); - rgbe[3] = (unsigned char)(exponent + 128); - } -} - -void stbiw__write_run_data(stbi__write_context *s, int length, unsigned char databyte) -{ - unsigned char lengthbyte = STBIW_UCHAR(length+128); - STBIW_ASSERT(length+128 <= 255); - s->func(s->context, &lengthbyte, 1); - s->func(s->context, &databyte, 1); -} - -void stbiw__write_dump_data(stbi__write_context *s, int length, unsigned char *data) -{ - unsigned char lengthbyte = STBIW_UCHAR(length); - STBIW_ASSERT(length <= 128); // inconsistent with spec but consistent with official code - s->func(s->context, &lengthbyte, 1); - s->func(s->context, data, length); -} - -void stbiw__write_hdr_scanline(stbi__write_context *s, int width, int ncomp, unsigned char *scratch, float *scanline) -{ - unsigned char scanlineheader[4] = { 2, 2, 0, 0 }; - unsigned char rgbe[4]; - float linear[3]; - int x; - - scanlineheader[2] = (width&0xff00)>>8; - scanlineheader[3] = (width&0x00ff); - - /* skip RLE for images too small or large */ - if (width < 8 || width >= 32768) { - for (x=0; x < width; x++) { - switch (ncomp) { - case 4: /* fallthrough */ - case 3: linear[2] = scanline[x*ncomp + 2]; - linear[1] = scanline[x*ncomp + 1]; - linear[0] = scanline[x*ncomp + 0]; - break; - default: - linear[0] = linear[1] = linear[2] = scanline[x*ncomp + 0]; - break; - } - stbiw__linear_to_rgbe(rgbe, linear); - s->func(s->context, rgbe, 4); - } - } else { - int c,r; - /* encode into scratch buffer */ - for (x=0; x < width; x++) { - switch(ncomp) { - case 4: /* fallthrough */ - case 3: linear[2] = scanline[x*ncomp + 2]; - linear[1] = scanline[x*ncomp + 1]; - linear[0] = scanline[x*ncomp + 0]; - break; - default: - linear[0] = linear[1] = linear[2] = scanline[x*ncomp + 0]; - break; - } - stbiw__linear_to_rgbe(rgbe, linear); - scratch[x + width*0] = rgbe[0]; - scratch[x + width*1] = rgbe[1]; - scratch[x + width*2] = rgbe[2]; - scratch[x + width*3] = rgbe[3]; - } - - s->func(s->context, scanlineheader, 4); - - /* RLE each component separately */ - for (c=0; c < 4; c++) { - unsigned char *comp = &scratch[width*c]; - - x = 0; - while (x < width) { - // find first run - r = x; - while (r+2 < width) { - if (comp[r] == comp[r+1] && comp[r] == comp[r+2]) - break; - ++r; - } - if (r+2 >= width) - r = width; - // dump up to first run - while (x < r) { - int len = r-x; - if (len > 128) len = 128; - stbiw__write_dump_data(s, len, &comp[x]); - x += len; - } - // if there's a run, output it - if (r+2 < width) { // same test as what we break out of in search loop, so only true if we break'd - // find next byte after run - while (r < width && comp[r] == comp[x]) - ++r; - // output run up to r - while (x < r) { - int len = r-x; - if (len > 127) len = 127; - stbiw__write_run_data(s, len, comp[x]); - x += len; - } - } - } - } - } -} - -static int stbi_write_hdr_core(stbi__write_context *s, int x, int y, int comp, float *data) -{ - if (y <= 0 || x <= 0 || data == NULL) - return 0; - else { - // Each component is stored separately. Allocate scratch space for full output scanline. - unsigned char *scratch = (unsigned char *) STBIW_MALLOC(x*4); - int i, len; - char buffer[128]; - char header[] = "#?RADIANCE\n# Written by stb_image_write.h\nFORMAT=32-bit_rle_rgbe\n"; - s->func(s->context, header, sizeof(header)-1); - - len = sprintf(buffer, "EXPOSURE= 1.0000000000000\n\n-Y %d +X %d\n", y, x); - s->func(s->context, buffer, len); - - for(i=0; i < y; i++) - stbiw__write_hdr_scanline(s, x, comp, scratch, data + comp*i*x); - STBIW_FREE(scratch); - return 1; - } -} - -int stbi_write_hdr_to_func(stbi_write_func *func, void *context, int x, int y, int comp, const float *data) -{ - stbi__write_context s; - stbi__start_write_callbacks(&s, func, context); - return stbi_write_hdr_core(&s, x, y, comp, (float *) data); -} - -int stbi_write_hdr(char const *filename, int x, int y, int comp, const float *data) -{ - stbi__write_context s; - if (stbi__start_write_file(&s,filename)) { - int r = stbi_write_hdr_core(&s, x, y, comp, (float *) data); - stbi__end_write_file(&s); - return r; - } else - return 0; -} -#endif // STBI_WRITE_NO_STDIO - - -////////////////////////////////////////////////////////////////////////////// -// -// PNG writer -// - -// stretchy buffer; stbiw__sbpush() == vector<>::push_back() -- stbiw__sbcount() == vector<>::size() -#define stbiw__sbraw(a) ((int *) (a) - 2) -#define stbiw__sbm(a) stbiw__sbraw(a)[0] -#define stbiw__sbn(a) stbiw__sbraw(a)[1] - -#define stbiw__sbneedgrow(a,n) ((a)==0 || stbiw__sbn(a)+n >= stbiw__sbm(a)) -#define stbiw__sbmaybegrow(a,n) (stbiw__sbneedgrow(a,(n)) ? stbiw__sbgrow(a,n) : 0) -#define stbiw__sbgrow(a,n) stbiw__sbgrowf((void **) &(a), (n), sizeof(*(a))) - -#define stbiw__sbpush(a, v) (stbiw__sbmaybegrow(a,1), (a)[stbiw__sbn(a)++] = (v)) -#define stbiw__sbcount(a) ((a) ? stbiw__sbn(a) : 0) -#define stbiw__sbfree(a) ((a) ? STBIW_FREE(stbiw__sbraw(a)),0 : 0) - -static void *stbiw__sbgrowf(void **arr, int increment, int itemsize) -{ - int m = *arr ? 2*stbiw__sbm(*arr)+increment : increment+1; - void *p = STBIW_REALLOC_SIZED(*arr ? stbiw__sbraw(*arr) : 0, *arr ? (stbiw__sbm(*arr)*itemsize + sizeof(int)*2) : 0, itemsize * m + sizeof(int)*2); - STBIW_ASSERT(p); - if (p) { - if (!*arr) ((int *) p)[1] = 0; - *arr = (void *) ((int *) p + 2); - stbiw__sbm(*arr) = m; - } - return *arr; -} - -static unsigned char *stbiw__zlib_flushf(unsigned char *data, unsigned int *bitbuffer, int *bitcount) -{ - while (*bitcount >= 8) { - stbiw__sbpush(data, STBIW_UCHAR(*bitbuffer)); - *bitbuffer >>= 8; - *bitcount -= 8; - } - return data; -} - -static int stbiw__zlib_bitrev(int code, int codebits) -{ - int res=0; - while (codebits--) { - res = (res << 1) | (code & 1); - code >>= 1; - } - return res; -} - -static unsigned int stbiw__zlib_countm(unsigned char *a, unsigned char *b, int limit) -{ - int i; - for (i=0; i < limit && i < 258; ++i) - if (a[i] != b[i]) break; - return i; -} - -static unsigned int stbiw__zhash(unsigned char *data) -{ - stbiw_uint32 hash = data[0] + (data[1] << 8) + (data[2] << 16); - hash ^= hash << 3; - hash += hash >> 5; - hash ^= hash << 4; - hash += hash >> 17; - hash ^= hash << 25; - hash += hash >> 6; - return hash; -} - -#define stbiw__zlib_flush() (out = stbiw__zlib_flushf(out, &bitbuf, &bitcount)) -#define stbiw__zlib_add(code,codebits) \ - (bitbuf |= (code) << bitcount, bitcount += (codebits), stbiw__zlib_flush()) -#define stbiw__zlib_huffa(b,c) stbiw__zlib_add(stbiw__zlib_bitrev(b,c),c) -// default huffman tables -#define stbiw__zlib_huff1(n) stbiw__zlib_huffa(0x30 + (n), 8) -#define stbiw__zlib_huff2(n) stbiw__zlib_huffa(0x190 + (n)-144, 9) -#define stbiw__zlib_huff3(n) stbiw__zlib_huffa(0 + (n)-256,7) -#define stbiw__zlib_huff4(n) stbiw__zlib_huffa(0xc0 + (n)-280,8) -#define stbiw__zlib_huff(n) ((n) <= 143 ? stbiw__zlib_huff1(n) : (n) <= 255 ? stbiw__zlib_huff2(n) : (n) <= 279 ? stbiw__zlib_huff3(n) : stbiw__zlib_huff4(n)) -#define stbiw__zlib_huffb(n) ((n) <= 143 ? stbiw__zlib_huff1(n) : stbiw__zlib_huff2(n)) - -#define stbiw__ZHASH 16384 - -unsigned char * stbi_zlib_compress(unsigned char *data, int data_len, int *out_len, int quality) -{ - static unsigned short lengthc[] = { 3,4,5,6,7,8,9,10,11,13,15,17,19,23,27,31,35,43,51,59,67,83,99,115,131,163,195,227,258, 259 }; - static unsigned char lengtheb[]= { 0,0,0,0,0,0,0, 0, 1, 1, 1, 1, 2, 2, 2, 2, 3, 3, 3, 3, 4, 4, 4, 4, 5, 5, 5, 5, 0 }; - static unsigned short distc[] = { 1,2,3,4,5,7,9,13,17,25,33,49,65,97,129,193,257,385,513,769,1025,1537,2049,3073,4097,6145,8193,12289,16385,24577, 32768 }; - static unsigned char disteb[] = { 0,0,0,0,1,1,2,2,3,3,4,4,5,5,6,6,7,7,8,8,9,9,10,10,11,11,12,12,13,13 }; - unsigned int bitbuf=0; - int i,j, bitcount=0; - unsigned char *out = NULL; - unsigned char ***hash_table = (unsigned char***) STBIW_MALLOC(stbiw__ZHASH * sizeof(char**)); - if (quality < 5) quality = 5; - - stbiw__sbpush(out, 0x78); // DEFLATE 32K window - stbiw__sbpush(out, 0x5e); // FLEVEL = 1 - stbiw__zlib_add(1,1); // BFINAL = 1 - stbiw__zlib_add(1,2); // BTYPE = 1 -- fixed huffman - - for (i=0; i < stbiw__ZHASH; ++i) - hash_table[i] = NULL; - - i=0; - while (i < data_len-3) { - // hash next 3 bytes of data to be compressed - int h = stbiw__zhash(data+i)&(stbiw__ZHASH-1), best=3; - unsigned char *bestloc = 0; - unsigned char **hlist = hash_table[h]; - int n = stbiw__sbcount(hlist); - for (j=0; j < n; ++j) { - if (hlist[j]-data > i-32768) { // if entry lies within window - int d = stbiw__zlib_countm(hlist[j], data+i, data_len-i); - if (d >= best) best=d,bestloc=hlist[j]; - } - } - // when hash table entry is too long, delete half the entries - if (hash_table[h] && stbiw__sbn(hash_table[h]) == 2*quality) { - STBIW_MEMMOVE(hash_table[h], hash_table[h]+quality, sizeof(hash_table[h][0])*quality); - stbiw__sbn(hash_table[h]) = quality; - } - stbiw__sbpush(hash_table[h],data+i); - - if (bestloc) { - // "lazy matching" - check match at *next* byte, and if it's better, do cur byte as literal - h = stbiw__zhash(data+i+1)&(stbiw__ZHASH-1); - hlist = hash_table[h]; - n = stbiw__sbcount(hlist); - for (j=0; j < n; ++j) { - if (hlist[j]-data > i-32767) { - int e = stbiw__zlib_countm(hlist[j], data+i+1, data_len-i-1); - if (e > best) { // if next match is better, bail on current match - bestloc = NULL; - break; - } - } - } - } - - if (bestloc) { - int d = (int) (data+i - bestloc); // distance back - STBIW_ASSERT(d <= 32767 && best <= 258); - for (j=0; best > lengthc[j+1]-1; ++j); - stbiw__zlib_huff(j+257); - if (lengtheb[j]) stbiw__zlib_add(best - lengthc[j], lengtheb[j]); - for (j=0; d > distc[j+1]-1; ++j); - stbiw__zlib_add(stbiw__zlib_bitrev(j,5),5); - if (disteb[j]) stbiw__zlib_add(d - distc[j], disteb[j]); - i += best; - } else { - stbiw__zlib_huffb(data[i]); - ++i; - } - } - // write out final bytes - for (;i < data_len; ++i) - stbiw__zlib_huffb(data[i]); - stbiw__zlib_huff(256); // end of block - // pad with 0 bits to byte boundary - while (bitcount) - stbiw__zlib_add(0,1); - - for (i=0; i < stbiw__ZHASH; ++i) - (void) stbiw__sbfree(hash_table[i]); - STBIW_FREE(hash_table); - - { - // compute adler32 on input - unsigned int s1=1, s2=0; - int blocklen = (int) (data_len % 5552); - j=0; - while (j < data_len) { - for (i=0; i < blocklen; ++i) s1 += data[j+i], s2 += s1; - s1 %= 65521, s2 %= 65521; - j += blocklen; - blocklen = 5552; - } - stbiw__sbpush(out, STBIW_UCHAR(s2 >> 8)); - stbiw__sbpush(out, STBIW_UCHAR(s2)); - stbiw__sbpush(out, STBIW_UCHAR(s1 >> 8)); - stbiw__sbpush(out, STBIW_UCHAR(s1)); - } - *out_len = stbiw__sbn(out); - // make returned pointer freeable - STBIW_MEMMOVE(stbiw__sbraw(out), out, *out_len); - return (unsigned char *) stbiw__sbraw(out); -} - -static unsigned int stbiw__crc32(unsigned char *buffer, int len) -{ - static unsigned int crc_table[256] = - { - 0x00000000, 0x77073096, 0xEE0E612C, 0x990951BA, 0x076DC419, 0x706AF48F, 0xE963A535, 0x9E6495A3, - 0x0eDB8832, 0x79DCB8A4, 0xE0D5E91E, 0x97D2D988, 0x09B64C2B, 0x7EB17CBD, 0xE7B82D07, 0x90BF1D91, - 0x1DB71064, 0x6AB020F2, 0xF3B97148, 0x84BE41DE, 0x1ADAD47D, 0x6DDDE4EB, 0xF4D4B551, 0x83D385C7, - 0x136C9856, 0x646BA8C0, 0xFD62F97A, 0x8A65C9EC, 0x14015C4F, 0x63066CD9, 0xFA0F3D63, 0x8D080DF5, - 0x3B6E20C8, 0x4C69105E, 0xD56041E4, 0xA2677172, 0x3C03E4D1, 0x4B04D447, 0xD20D85FD, 0xA50AB56B, - 0x35B5A8FA, 0x42B2986C, 0xDBBBC9D6, 0xACBCF940, 0x32D86CE3, 0x45DF5C75, 0xDCD60DCF, 0xABD13D59, - 0x26D930AC, 0x51DE003A, 0xC8D75180, 0xBFD06116, 0x21B4F4B5, 0x56B3C423, 0xCFBA9599, 0xB8BDA50F, - 0x2802B89E, 0x5F058808, 0xC60CD9B2, 0xB10BE924, 0x2F6F7C87, 0x58684C11, 0xC1611DAB, 0xB6662D3D, - 0x76DC4190, 0x01DB7106, 0x98D220BC, 0xEFD5102A, 0x71B18589, 0x06B6B51F, 0x9FBFE4A5, 0xE8B8D433, - 0x7807C9A2, 0x0F00F934, 0x9609A88E, 0xE10E9818, 0x7F6A0DBB, 0x086D3D2D, 0x91646C97, 0xE6635C01, - 0x6B6B51F4, 0x1C6C6162, 0x856530D8, 0xF262004E, 0x6C0695ED, 0x1B01A57B, 0x8208F4C1, 0xF50FC457, - 0x65B0D9C6, 0x12B7E950, 0x8BBEB8EA, 0xFCB9887C, 0x62DD1DDF, 0x15DA2D49, 0x8CD37CF3, 0xFBD44C65, - 0x4DB26158, 0x3AB551CE, 0xA3BC0074, 0xD4BB30E2, 0x4ADFA541, 0x3DD895D7, 0xA4D1C46D, 0xD3D6F4FB, - 0x4369E96A, 0x346ED9FC, 0xAD678846, 0xDA60B8D0, 0x44042D73, 0x33031DE5, 0xAA0A4C5F, 0xDD0D7CC9, - 0x5005713C, 0x270241AA, 0xBE0B1010, 0xC90C2086, 0x5768B525, 0x206F85B3, 0xB966D409, 0xCE61E49F, - 0x5EDEF90E, 0x29D9C998, 0xB0D09822, 0xC7D7A8B4, 0x59B33D17, 0x2EB40D81, 0xB7BD5C3B, 0xC0BA6CAD, - 0xEDB88320, 0x9ABFB3B6, 0x03B6E20C, 0x74B1D29A, 0xEAD54739, 0x9DD277AF, 0x04DB2615, 0x73DC1683, - 0xE3630B12, 0x94643B84, 0x0D6D6A3E, 0x7A6A5AA8, 0xE40ECF0B, 0x9309FF9D, 0x0A00AE27, 0x7D079EB1, - 0xF00F9344, 0x8708A3D2, 0x1E01F268, 0x6906C2FE, 0xF762575D, 0x806567CB, 0x196C3671, 0x6E6B06E7, - 0xFED41B76, 0x89D32BE0, 0x10DA7A5A, 0x67DD4ACC, 0xF9B9DF6F, 0x8EBEEFF9, 0x17B7BE43, 0x60B08ED5, - 0xD6D6A3E8, 0xA1D1937E, 0x38D8C2C4, 0x4FDFF252, 0xD1BB67F1, 0xA6BC5767, 0x3FB506DD, 0x48B2364B, - 0xD80D2BDA, 0xAF0A1B4C, 0x36034AF6, 0x41047A60, 0xDF60EFC3, 0xA867DF55, 0x316E8EEF, 0x4669BE79, - 0xCB61B38C, 0xBC66831A, 0x256FD2A0, 0x5268E236, 0xCC0C7795, 0xBB0B4703, 0x220216B9, 0x5505262F, - 0xC5BA3BBE, 0xB2BD0B28, 0x2BB45A92, 0x5CB36A04, 0xC2D7FFA7, 0xB5D0CF31, 0x2CD99E8B, 0x5BDEAE1D, - 0x9B64C2B0, 0xEC63F226, 0x756AA39C, 0x026D930A, 0x9C0906A9, 0xEB0E363F, 0x72076785, 0x05005713, - 0x95BF4A82, 0xE2B87A14, 0x7BB12BAE, 0x0CB61B38, 0x92D28E9B, 0xE5D5BE0D, 0x7CDCEFB7, 0x0BDBDF21, - 0x86D3D2D4, 0xF1D4E242, 0x68DDB3F8, 0x1FDA836E, 0x81BE16CD, 0xF6B9265B, 0x6FB077E1, 0x18B74777, - 0x88085AE6, 0xFF0F6A70, 0x66063BCA, 0x11010B5C, 0x8F659EFF, 0xF862AE69, 0x616BFFD3, 0x166CCF45, - 0xA00AE278, 0xD70DD2EE, 0x4E048354, 0x3903B3C2, 0xA7672661, 0xD06016F7, 0x4969474D, 0x3E6E77DB, - 0xAED16A4A, 0xD9D65ADC, 0x40DF0B66, 0x37D83BF0, 0xA9BCAE53, 0xDEBB9EC5, 0x47B2CF7F, 0x30B5FFE9, - 0xBDBDF21C, 0xCABAC28A, 0x53B39330, 0x24B4A3A6, 0xBAD03605, 0xCDD70693, 0x54DE5729, 0x23D967BF, - 0xB3667A2E, 0xC4614AB8, 0x5D681B02, 0x2A6F2B94, 0xB40BBE37, 0xC30C8EA1, 0x5A05DF1B, 0x2D02EF8D - }; - - unsigned int crc = ~0u; - int i; - for (i=0; i < len; ++i) - crc = (crc >> 8) ^ crc_table[buffer[i] ^ (crc & 0xff)]; - return ~crc; -} - -#define stbiw__wpng4(o,a,b,c,d) ((o)[0]=STBIW_UCHAR(a),(o)[1]=STBIW_UCHAR(b),(o)[2]=STBIW_UCHAR(c),(o)[3]=STBIW_UCHAR(d),(o)+=4) -#define stbiw__wp32(data,v) stbiw__wpng4(data, (v)>>24,(v)>>16,(v)>>8,(v)); -#define stbiw__wptag(data,s) stbiw__wpng4(data, s[0],s[1],s[2],s[3]) - -static void stbiw__wpcrc(unsigned char **data, int len) -{ - unsigned int crc = stbiw__crc32(*data - len - 4, len+4); - stbiw__wp32(*data, crc); -} - -static unsigned char stbiw__paeth(int a, int b, int c) -{ - int p = a + b - c, pa = abs(p-a), pb = abs(p-b), pc = abs(p-c); - if (pa <= pb && pa <= pc) return STBIW_UCHAR(a); - if (pb <= pc) return STBIW_UCHAR(b); - return STBIW_UCHAR(c); -} - -unsigned char *stbi_write_png_to_mem(unsigned char *pixels, int stride_bytes, int x, int y, int n, int *out_len) -{ - int ctype[5] = { -1, 0, 4, 2, 6 }; - unsigned char sig[8] = { 137,80,78,71,13,10,26,10 }; - unsigned char *out,*o, *filt, *zlib; - signed char *line_buffer; - int i,j,k,p,zlen; - - if (stride_bytes == 0) - stride_bytes = x * n; - - filt = (unsigned char *) STBIW_MALLOC((x*n+1) * y); if (!filt) return 0; - line_buffer = (signed char *) STBIW_MALLOC(x * n); if (!line_buffer) { STBIW_FREE(filt); return 0; } - for (j=0; j < y; ++j) { - static int mapping[] = { 0,1,2,3,4 }; - static int firstmap[] = { 0,1,0,5,6 }; - int *mymap = j ? mapping : firstmap; - int best = 0, bestval = 0x7fffffff; - for (p=0; p < 2; ++p) { - for (k= p?best:0; k < 5; ++k) { - int type = mymap[k],est=0; - unsigned char *z = pixels + stride_bytes*j; - for (i=0; i < n; ++i) - switch (type) { - case 0: line_buffer[i] = z[i]; break; - case 1: line_buffer[i] = z[i]; break; - case 2: line_buffer[i] = z[i] - z[i-stride_bytes]; break; - case 3: line_buffer[i] = z[i] - (z[i-stride_bytes]>>1); break; - case 4: line_buffer[i] = (signed char) (z[i] - stbiw__paeth(0,z[i-stride_bytes],0)); break; - case 5: line_buffer[i] = z[i]; break; - case 6: line_buffer[i] = z[i]; break; - } - for (i=n; i < x*n; ++i) { - switch (type) { - case 0: line_buffer[i] = z[i]; break; - case 1: line_buffer[i] = z[i] - z[i-n]; break; - case 2: line_buffer[i] = z[i] - z[i-stride_bytes]; break; - case 3: line_buffer[i] = z[i] - ((z[i-n] + z[i-stride_bytes])>>1); break; - case 4: line_buffer[i] = z[i] - stbiw__paeth(z[i-n], z[i-stride_bytes], z[i-stride_bytes-n]); break; - case 5: line_buffer[i] = z[i] - (z[i-n]>>1); break; - case 6: line_buffer[i] = z[i] - stbiw__paeth(z[i-n], 0,0); break; - } - } - if (p) break; - for (i=0; i < x*n; ++i) - est += abs((signed char) line_buffer[i]); - if (est < bestval) { bestval = est; best = k; } - } - } - // when we get here, best contains the filter type, and line_buffer contains the data - filt[j*(x*n+1)] = (unsigned char) best; - STBIW_MEMMOVE(filt+j*(x*n+1)+1, line_buffer, x*n); - } - STBIW_FREE(line_buffer); - zlib = stbi_zlib_compress(filt, y*( x*n+1), &zlen, 8); // increase 8 to get smaller but use more memory - STBIW_FREE(filt); - if (!zlib) return 0; - - // each tag requires 12 bytes of overhead - out = (unsigned char *) STBIW_MALLOC(8 + 12+13 + 12+zlen + 12); - if (!out) return 0; - *out_len = 8 + 12+13 + 12+zlen + 12; - - o=out; - STBIW_MEMMOVE(o,sig,8); o+= 8; - stbiw__wp32(o, 13); // header length - stbiw__wptag(o, "IHDR"); - stbiw__wp32(o, x); - stbiw__wp32(o, y); - *o++ = 8; - *o++ = STBIW_UCHAR(ctype[n]); - *o++ = 0; - *o++ = 0; - *o++ = 0; - stbiw__wpcrc(&o,13); - - stbiw__wp32(o, zlen); - stbiw__wptag(o, "IDAT"); - STBIW_MEMMOVE(o, zlib, zlen); - o += zlen; - STBIW_FREE(zlib); - stbiw__wpcrc(&o, zlen); - - stbiw__wp32(o,0); - stbiw__wptag(o, "IEND"); - stbiw__wpcrc(&o,0); - - STBIW_ASSERT(o == out + *out_len); - - return out; -} - -#ifndef STBI_WRITE_NO_STDIO -STBIWDEF int stbi_write_png(char const *filename, int x, int y, int comp, const void *data, int stride_bytes) -{ - FILE *f; - int len; - unsigned char *png = stbi_write_png_to_mem((unsigned char *) data, stride_bytes, x, y, comp, &len); - if (png == NULL) return 0; - f = fopen(filename, "wb"); - if (!f) { STBIW_FREE(png); return 0; } - fwrite(png, 1, len, f); - fclose(f); - STBIW_FREE(png); - return 1; -} -#endif - -STBIWDEF int stbi_write_png_to_func(stbi_write_func *func, void *context, int x, int y, int comp, const void *data, int stride_bytes) -{ - int len; - unsigned char *png = stbi_write_png_to_mem((unsigned char *) data, stride_bytes, x, y, comp, &len); - if (png == NULL) return 0; - func(context, png, len); - STBIW_FREE(png); - return 1; -} - -#endif // STB_IMAGE_WRITE_IMPLEMENTATION - -/* Revision history - 1.02 (2016-04-02) - avoid allocating large structures on the stack - 1.01 (2016-01-16) - STBIW_REALLOC_SIZED: support allocators with no realloc support - avoid race-condition in crc initialization - minor compile issues - 1.00 (2015-09-14) - installable file IO function - 0.99 (2015-09-13) - warning fixes; TGA rle support - 0.98 (2015-04-08) - added STBIW_MALLOC, STBIW_ASSERT etc - 0.97 (2015-01-18) - fixed HDR asserts, rewrote HDR rle logic - 0.96 (2015-01-17) - add HDR output - fix monochrome BMP - 0.95 (2014-08-17) - add monochrome TGA output - 0.94 (2014-05-31) - rename private functions to avoid conflicts with stb_image.h - 0.93 (2014-05-27) - warning fixes - 0.92 (2010-08-01) - casts to unsigned char to fix warnings - 0.91 (2010-07-17) - first public release - 0.90 first internal release -*/ diff --git a/impeller/third_party/stb/stb/stb_leakcheck.h b/impeller/third_party/stb/stb/stb_leakcheck.h deleted file mode 100644 index 6e50b224af42d..0000000000000 --- a/impeller/third_party/stb/stb/stb_leakcheck.h +++ /dev/null @@ -1,124 +0,0 @@ -// stb_leakcheck.h - v0.2 - quick & dirty malloc leak-checking - public domain -// LICENSE -// -// This software is dual-licensed to the public domain and under the following -// license: you are granted a perpetual, irrevocable license to copy, modify, -// publish, and distribute this file as you see fit. - -#ifdef STB_LEAKCHECK_IMPLEMENTATION -#undef STB_LEAKCHECK_IMPLEMENTATION // don't implement more than once - -// if we've already included leakcheck before, undefine the macros -#ifdef malloc -#undef malloc -#undef free -#undef realloc -#endif - -#include -#include -#include -#include -#include -typedef struct malloc_info stb_leakcheck_malloc_info; - -struct malloc_info -{ - char *file; - int line; - size_t size; - stb_leakcheck_malloc_info *next,*prev; -}; - -static stb_leakcheck_malloc_info *mi_head; - -void *stb_leakcheck_malloc(size_t sz, char *file, int line) -{ - stb_leakcheck_malloc_info *mi = (stb_leakcheck_malloc_info *) malloc(sz + sizeof(*mi)); - if (mi == NULL) return mi; - mi->file = file; - mi->line = line; - mi->next = mi_head; - if (mi_head) - mi->next->prev = mi; - mi->prev = NULL; - mi->size = (int) sz; - mi_head = mi; - return mi+1; -} - -void stb_leakcheck_free(void *ptr) -{ - if (ptr != NULL) { - stb_leakcheck_malloc_info *mi = (stb_leakcheck_malloc_info *) ptr - 1; - mi->size = ~mi->size; - #ifndef STB_LEAKCHECK_SHOWALL - if (mi->prev == NULL) { - assert(mi_head == mi); - mi_head = mi->next; - } else - mi->prev->next = mi->next; - if (mi->next) - mi->next->prev = mi->prev; - #endif - } -} - -void *stb_leakcheck_realloc(void *ptr, size_t sz, char *file, int line) -{ - if (ptr == NULL) { - return stb_leakcheck_malloc(sz, file, line); - } else if (sz == 0) { - stb_leakcheck_free(ptr); - return NULL; - } else { - stb_leakcheck_malloc_info *mi = (stb_leakcheck_malloc_info *) ptr - 1; - if (sz <= mi->size) - return ptr; - else { - #ifdef STB_LEAKCHECK_REALLOC_PRESERVE_MALLOC_FILELINE - void *q = stb_leakcheck_malloc(sz, mi->file, mi->line); - #else - void *q = stb_leakcheck_malloc(sz, file, line); - #endif - if (q) { - memcpy(q, ptr, mi->size); - stb_leakcheck_free(ptr); - } - return q; - } - } -} - -void stb_leakcheck_dumpmem(void) -{ - stb_leakcheck_malloc_info *mi = mi_head; - while (mi) { - if ((ptrdiff_t) mi->size >= 0) - printf("LEAKED: %s (%4d): %8z bytes at %p\n", mi->file, mi->line, mi->size, mi+1); - mi = mi->next; - } - #ifdef STB_LEAKCHECK_SHOWALL - mi = mi_head; - while (mi) { - if ((ptrdiff_t) mi->size < 0) - printf("FREED : %s (%4d): %8z bytes at %p\n", mi->file, mi->line, ~mi->size, mi+1); - mi = mi->next; - } - #endif -} -#endif // STB_LEAKCHECK_IMPLEMENTATION - -#ifndef INCLUDE_STB_LEAKCHECK_H -#define INCLUDE_STB_LEAKCHECK_H - -#define malloc(sz) stb_leakcheck_malloc(sz, __FILE__, __LINE__) -#define free(p) stb_leakcheck_free(p) -#define realloc(p,sz) stb_leakcheck_realloc(p,sz, __FILE__, __LINE__) - -extern void * stb_leakcheck_malloc(size_t sz, char *file, int line); -extern void * stb_leakcheck_realloc(void *ptr, size_t sz, char *file, int line); -extern void stb_leakcheck_free(void *ptr); -extern void stb_leakcheck_dumpmem(void); - -#endif // INCLUDE_STB_LEAKCHECK_H diff --git a/impeller/third_party/stb/stb/stb_perlin.h b/impeller/third_party/stb/stb/stb_perlin.h deleted file mode 100644 index 0dac7d9f9bb43..0000000000000 --- a/impeller/third_party/stb/stb/stb_perlin.h +++ /dev/null @@ -1,182 +0,0 @@ -// stb_perlin.h - v0.2 - perlin noise -// public domain single-file C implementation by Sean Barrett -// -// LICENSE -// -// This software is dual-licensed to the public domain and under the following -// license: you are granted a perpetual, irrevocable license to copy, modify, -// publish, and distribute this file as you see fit. -// -// -// to create the implementation, -// #define STB_PERLIN_IMPLEMENTATION -// in *one* C/CPP file that includes this file. - - -// Documentation: -// -// float stb_perlin_noise3( float x, -// float y, -// float z, -// int x_wrap=0, -// int y_wrap=0, -// int z_wrap=0) -// -// This function computes a random value at the coordinate (x,y,z). -// Adjacent random values are continuous but the noise fluctuates -// its randomness with period 1, i.e. takes on wholly unrelated values -// at integer points. Specifically, this implements Ken Perlin's -// revised noise function from 2002. -// -// The "wrap" parameters can be used to create wraparound noise that -// wraps at powers of two. The numbers MUST be powers of two. Specify -// 0 to mean "don't care". (The noise always wraps every 256 due -// details of the implementation, even if you ask for larger or no -// wrapping.) - - -#ifdef __cplusplus -extern "C" float stb_perlin_noise3(float x, float y, float z, int x_wrap=0, int y_wrap=0, int z_wrap=0); -#else -extern float stb_perlin_noise3(float x, float y, float z, int x_wrap, int y_wrap, int z_wrap); -#endif - -#ifdef STB_PERLIN_IMPLEMENTATION - -#include // floor() - -// not same permutation table as Perlin's reference to avoid copyright issues; -// Perlin's table can be found at http://mrl.nyu.edu/~perlin/noise/ -// @OPTIMIZE: should this be unsigned char instead of int for cache? -static int stb__perlin_randtab[512] = -{ - 23, 125, 161, 52, 103, 117, 70, 37, 247, 101, 203, 169, 124, 126, 44, 123, - 152, 238, 145, 45, 171, 114, 253, 10, 192, 136, 4, 157, 249, 30, 35, 72, - 175, 63, 77, 90, 181, 16, 96, 111, 133, 104, 75, 162, 93, 56, 66, 240, - 8, 50, 84, 229, 49, 210, 173, 239, 141, 1, 87, 18, 2, 198, 143, 57, - 225, 160, 58, 217, 168, 206, 245, 204, 199, 6, 73, 60, 20, 230, 211, 233, - 94, 200, 88, 9, 74, 155, 33, 15, 219, 130, 226, 202, 83, 236, 42, 172, - 165, 218, 55, 222, 46, 107, 98, 154, 109, 67, 196, 178, 127, 158, 13, 243, - 65, 79, 166, 248, 25, 224, 115, 80, 68, 51, 184, 128, 232, 208, 151, 122, - 26, 212, 105, 43, 179, 213, 235, 148, 146, 89, 14, 195, 28, 78, 112, 76, - 250, 47, 24, 251, 140, 108, 186, 190, 228, 170, 183, 139, 39, 188, 244, 246, - 132, 48, 119, 144, 180, 138, 134, 193, 82, 182, 120, 121, 86, 220, 209, 3, - 91, 241, 149, 85, 205, 150, 113, 216, 31, 100, 41, 164, 177, 214, 153, 231, - 38, 71, 185, 174, 97, 201, 29, 95, 7, 92, 54, 254, 191, 118, 34, 221, - 131, 11, 163, 99, 234, 81, 227, 147, 156, 176, 17, 142, 69, 12, 110, 62, - 27, 255, 0, 194, 59, 116, 242, 252, 19, 21, 187, 53, 207, 129, 64, 135, - 61, 40, 167, 237, 102, 223, 106, 159, 197, 189, 215, 137, 36, 32, 22, 5, - - // and a second copy so we don't need an extra mask or static initializer - 23, 125, 161, 52, 103, 117, 70, 37, 247, 101, 203, 169, 124, 126, 44, 123, - 152, 238, 145, 45, 171, 114, 253, 10, 192, 136, 4, 157, 249, 30, 35, 72, - 175, 63, 77, 90, 181, 16, 96, 111, 133, 104, 75, 162, 93, 56, 66, 240, - 8, 50, 84, 229, 49, 210, 173, 239, 141, 1, 87, 18, 2, 198, 143, 57, - 225, 160, 58, 217, 168, 206, 245, 204, 199, 6, 73, 60, 20, 230, 211, 233, - 94, 200, 88, 9, 74, 155, 33, 15, 219, 130, 226, 202, 83, 236, 42, 172, - 165, 218, 55, 222, 46, 107, 98, 154, 109, 67, 196, 178, 127, 158, 13, 243, - 65, 79, 166, 248, 25, 224, 115, 80, 68, 51, 184, 128, 232, 208, 151, 122, - 26, 212, 105, 43, 179, 213, 235, 148, 146, 89, 14, 195, 28, 78, 112, 76, - 250, 47, 24, 251, 140, 108, 186, 190, 228, 170, 183, 139, 39, 188, 244, 246, - 132, 48, 119, 144, 180, 138, 134, 193, 82, 182, 120, 121, 86, 220, 209, 3, - 91, 241, 149, 85, 205, 150, 113, 216, 31, 100, 41, 164, 177, 214, 153, 231, - 38, 71, 185, 174, 97, 201, 29, 95, 7, 92, 54, 254, 191, 118, 34, 221, - 131, 11, 163, 99, 234, 81, 227, 147, 156, 176, 17, 142, 69, 12, 110, 62, - 27, 255, 0, 194, 59, 116, 242, 252, 19, 21, 187, 53, 207, 129, 64, 135, - 61, 40, 167, 237, 102, 223, 106, 159, 197, 189, 215, 137, 36, 32, 22, 5, -}; - -static float stb__perlin_lerp(float a, float b, float t) -{ - return a + (b-a) * t; -} - -// different grad function from Perlin's, but easy to modify to match reference -static float stb__perlin_grad(int hash, float x, float y, float z) -{ - static float basis[12][4] = - { - { 1, 1, 0 }, - { -1, 1, 0 }, - { 1,-1, 0 }, - { -1,-1, 0 }, - { 1, 0, 1 }, - { -1, 0, 1 }, - { 1, 0,-1 }, - { -1, 0,-1 }, - { 0, 1, 1 }, - { 0,-1, 1 }, - { 0, 1,-1 }, - { 0,-1,-1 }, - }; - - // perlin's gradient has 12 cases so some get used 1/16th of the time - // and some 2/16ths. We reduce bias by changing those fractions - // to 5/16ths and 6/16ths, and the same 4 cases get the extra weight. - static unsigned char indices[64] = - { - 0,1,2,3,4,5,6,7,8,9,10,11, - 0,9,1,11, - 0,1,2,3,4,5,6,7,8,9,10,11, - 0,1,2,3,4,5,6,7,8,9,10,11, - 0,1,2,3,4,5,6,7,8,9,10,11, - 0,1,2,3,4,5,6,7,8,9,10,11, - }; - - // if you use reference permutation table, change 63 below to 15 to match reference - float *grad = basis[indices[hash & 63]]; - return grad[0]*x + grad[1]*y + grad[2]*z; -} - -float stb_perlin_noise3(float x, float y, float z, int x_wrap, int y_wrap, int z_wrap) -{ - float u,v,w; - float n000,n001,n010,n011,n100,n101,n110,n111; - float n00,n01,n10,n11; - float n0,n1; - - unsigned int x_mask = (x_wrap-1) & 255; - unsigned int y_mask = (y_wrap-1) & 255; - unsigned int z_mask = (z_wrap-1) & 255; - int px = (int) floor(x); - int py = (int) floor(y); - int pz = (int) floor(z); - int x0 = px & x_mask, x1 = (px+1) & x_mask; - int y0 = py & y_mask, y1 = (py+1) & y_mask; - int z0 = pz & z_mask, z1 = (pz+1) & z_mask; - int r0,r1, r00,r01,r10,r11; - - #define stb__perlin_ease(a) (((a*6-15)*a + 10) * a * a * a) - - x -= px; u = stb__perlin_ease(x); - y -= py; v = stb__perlin_ease(y); - z -= pz; w = stb__perlin_ease(z); - - r0 = stb__perlin_randtab[x0]; - r1 = stb__perlin_randtab[x1]; - - r00 = stb__perlin_randtab[r0+y0]; - r01 = stb__perlin_randtab[r0+y1]; - r10 = stb__perlin_randtab[r1+y0]; - r11 = stb__perlin_randtab[r1+y1]; - - n000 = stb__perlin_grad(stb__perlin_randtab[r00+z0], x , y , z ); - n001 = stb__perlin_grad(stb__perlin_randtab[r00+z1], x , y , z-1 ); - n010 = stb__perlin_grad(stb__perlin_randtab[r01+z0], x , y-1, z ); - n011 = stb__perlin_grad(stb__perlin_randtab[r01+z1], x , y-1, z-1 ); - n100 = stb__perlin_grad(stb__perlin_randtab[r10+z0], x-1, y , z ); - n101 = stb__perlin_grad(stb__perlin_randtab[r10+z1], x-1, y , z-1 ); - n110 = stb__perlin_grad(stb__perlin_randtab[r11+z0], x-1, y-1, z ); - n111 = stb__perlin_grad(stb__perlin_randtab[r11+z1], x-1, y-1, z-1 ); - - n00 = stb__perlin_lerp(n000,n001,w); - n01 = stb__perlin_lerp(n010,n011,w); - n10 = stb__perlin_lerp(n100,n101,w); - n11 = stb__perlin_lerp(n110,n111,w); - - n0 = stb__perlin_lerp(n00,n01,v); - n1 = stb__perlin_lerp(n10,n11,v); - - return stb__perlin_lerp(n0,n1,u); -} -#endif // STB_PERLIN_IMPLEMENTATION diff --git a/impeller/third_party/stb/stb/stb_rect_pack.h b/impeller/third_party/stb/stb/stb_rect_pack.h deleted file mode 100644 index bd1cfc60b2575..0000000000000 --- a/impeller/third_party/stb/stb/stb_rect_pack.h +++ /dev/null @@ -1,572 +0,0 @@ -// stb_rect_pack.h - v0.08 - public domain - rectangle packing -// Sean Barrett 2014 -// -// Useful for e.g. packing rectangular textures into an atlas. -// Does not do rotation. -// -// Not necessarily the awesomest packing method, but better than -// the totally naive one in stb_truetype (which is primarily what -// this is meant to replace). -// -// Has only had a few tests run, may have issues. -// -// More docs to come. -// -// No memory allocations; uses qsort() and assert() from stdlib. -// Can override those by defining STBRP_SORT and STBRP_ASSERT. -// -// This library currently uses the Skyline Bottom-Left algorithm. -// -// Please note: better rectangle packers are welcome! Please -// implement them to the same API, but with a different init -// function. -// -// Credits -// -// Library -// Sean Barrett -// Minor features -// Martins Mozeiko -// Bugfixes / warning fixes -// Jeremy Jaussaud -// -// Version history: -// -// 0.08 (2015-09-13) really fix bug with empty rects (w=0 or h=0) -// 0.07 (2015-09-13) fix bug with empty rects (w=0 or h=0) -// 0.06 (2015-04-15) added STBRP_SORT to allow replacing qsort -// 0.05: added STBRP_ASSERT to allow replacing assert -// 0.04: fixed minor bug in STBRP_LARGE_RECTS support -// 0.01: initial release -// -// LICENSE -// -// This software is dual-licensed to the public domain and under the following -// license: you are granted a perpetual, irrevocable license to copy, modify, -// publish, and distribute this file as you see fit. - -////////////////////////////////////////////////////////////////////////////// -// -// INCLUDE SECTION -// - -#ifndef STB_INCLUDE_STB_RECT_PACK_H -#define STB_INCLUDE_STB_RECT_PACK_H - -#define STB_RECT_PACK_VERSION 1 - -#ifdef STBRP_STATIC -#define STBRP_DEF static -#else -#define STBRP_DEF extern -#endif - -#ifdef __cplusplus -extern "C" { -#endif - -typedef struct stbrp_context stbrp_context; -typedef struct stbrp_node stbrp_node; -typedef struct stbrp_rect stbrp_rect; - -#ifdef STBRP_LARGE_RECTS -typedef int stbrp_coord; -#else -typedef unsigned short stbrp_coord; -#endif - -STBRP_DEF void stbrp_pack_rects (stbrp_context *context, stbrp_rect *rects, int num_rects); -// Assign packed locations to rectangles. The rectangles are of type -// 'stbrp_rect' defined below, stored in the array 'rects', and there -// are 'num_rects' many of them. -// -// Rectangles which are successfully packed have the 'was_packed' flag -// set to a non-zero value and 'x' and 'y' store the minimum location -// on each axis (i.e. bottom-left in cartesian coordinates, top-left -// if you imagine y increasing downwards). Rectangles which do not fit -// have the 'was_packed' flag set to 0. -// -// You should not try to access the 'rects' array from another thread -// while this function is running, as the function temporarily reorders -// the array while it executes. -// -// To pack into another rectangle, you need to call stbrp_init_target -// again. To continue packing into the same rectangle, you can call -// this function again. Calling this multiple times with multiple rect -// arrays will probably produce worse packing results than calling it -// a single time with the full rectangle array, but the option is -// available. - -struct stbrp_rect -{ - // reserved for your use: - int id; - - // input: - stbrp_coord w, h; - - // output: - stbrp_coord x, y; - int was_packed; // non-zero if valid packing - -}; // 16 bytes, nominally - - -STBRP_DEF void stbrp_init_target (stbrp_context *context, int width, int height, stbrp_node *nodes, int num_nodes); -// Initialize a rectangle packer to: -// pack a rectangle that is 'width' by 'height' in dimensions -// using temporary storage provided by the array 'nodes', which is 'num_nodes' long -// -// You must call this function every time you start packing into a new target. -// -// There is no "shutdown" function. The 'nodes' memory must stay valid for -// the following stbrp_pack_rects() call (or calls), but can be freed after -// the call (or calls) finish. -// -// Note: to guarantee best results, either: -// 1. make sure 'num_nodes' >= 'width' -// or 2. call stbrp_allow_out_of_mem() defined below with 'allow_out_of_mem = 1' -// -// If you don't do either of the above things, widths will be quantized to multiples -// of small integers to guarantee the algorithm doesn't run out of temporary storage. -// -// If you do #2, then the non-quantized algorithm will be used, but the algorithm -// may run out of temporary storage and be unable to pack some rectangles. - -STBRP_DEF void stbrp_setup_allow_out_of_mem (stbrp_context *context, int allow_out_of_mem); -// Optionally call this function after init but before doing any packing to -// change the handling of the out-of-temp-memory scenario, described above. -// If you call init again, this will be reset to the default (false). - - -STBRP_DEF void stbrp_setup_heuristic (stbrp_context *context, int heuristic); -// Optionally select which packing heuristic the library should use. Different -// heuristics will produce better/worse results for different data sets. -// If you call init again, this will be reset to the default. - -enum -{ - STBRP_HEURISTIC_Skyline_default=0, - STBRP_HEURISTIC_Skyline_BL_sortHeight = STBRP_HEURISTIC_Skyline_default, - STBRP_HEURISTIC_Skyline_BF_sortHeight, -}; - - -////////////////////////////////////////////////////////////////////////////// -// -// the details of the following structures don't matter to you, but they must -// be visible so you can handle the memory allocations for them - -struct stbrp_node -{ - stbrp_coord x,y; - stbrp_node *next; -}; - -struct stbrp_context -{ - int width; - int height; - int align; - int init_mode; - int heuristic; - int num_nodes; - stbrp_node *active_head; - stbrp_node *free_head; - stbrp_node extra[2]; // we allocate two extra nodes so optimal user-node-count is 'width' not 'width+2' -}; - -#ifdef __cplusplus -} -#endif - -#endif - -////////////////////////////////////////////////////////////////////////////// -// -// IMPLEMENTATION SECTION -// - -#ifdef STB_RECT_PACK_IMPLEMENTATION -#ifndef STBRP_SORT -#include -#define STBRP_SORT qsort -#endif - -#ifndef STBRP_ASSERT -#include -#define STBRP_ASSERT assert -#endif - -enum -{ - STBRP__INIT_skyline = 1, -}; - -STBRP_DEF void stbrp_setup_heuristic(stbrp_context *context, int heuristic) -{ - switch (context->init_mode) { - case STBRP__INIT_skyline: - STBRP_ASSERT(heuristic == STBRP_HEURISTIC_Skyline_BL_sortHeight || heuristic == STBRP_HEURISTIC_Skyline_BF_sortHeight); - context->heuristic = heuristic; - break; - default: - STBRP_ASSERT(0); - } -} - -STBRP_DEF void stbrp_setup_allow_out_of_mem(stbrp_context *context, int allow_out_of_mem) -{ - if (allow_out_of_mem) - // if it's ok to run out of memory, then don't bother aligning them; - // this gives better packing, but may fail due to OOM (even though - // the rectangles easily fit). @TODO a smarter approach would be to only - // quantize once we've hit OOM, then we could get rid of this parameter. - context->align = 1; - else { - // if it's not ok to run out of memory, then quantize the widths - // so that num_nodes is always enough nodes. - // - // I.e. num_nodes * align >= width - // align >= width / num_nodes - // align = ceil(width/num_nodes) - - context->align = (context->width + context->num_nodes-1) / context->num_nodes; - } -} - -STBRP_DEF void stbrp_init_target(stbrp_context *context, int width, int height, stbrp_node *nodes, int num_nodes) -{ - int i; -#ifndef STBRP_LARGE_RECTS - STBRP_ASSERT(width <= 0xffff && height <= 0xffff); -#endif - - for (i=0; i < num_nodes-1; ++i) - nodes[i].next = &nodes[i+1]; - nodes[i].next = NULL; - context->init_mode = STBRP__INIT_skyline; - context->heuristic = STBRP_HEURISTIC_Skyline_default; - context->free_head = &nodes[0]; - context->active_head = &context->extra[0]; - context->width = width; - context->height = height; - context->num_nodes = num_nodes; - stbrp_setup_allow_out_of_mem(context, 0); - - // node 0 is the full width, node 1 is the sentinel (lets us not store width explicitly) - context->extra[0].x = 0; - context->extra[0].y = 0; - context->extra[0].next = &context->extra[1]; - context->extra[1].x = (stbrp_coord) width; -#ifdef STBRP_LARGE_RECTS - context->extra[1].y = (1<<30); -#else - context->extra[1].y = 65535; -#endif - context->extra[1].next = NULL; -} - -// find minimum y position if it starts at x1 -static int stbrp__skyline_find_min_y(stbrp_context *c, stbrp_node *first, int x0, int width, int *pwaste) -{ - stbrp_node *node = first; - int x1 = x0 + width; - int min_y, visited_width, waste_area; - STBRP_ASSERT(first->x <= x0); - - #if 0 - // skip in case we're past the node - while (node->next->x <= x0) - ++node; - #else - STBRP_ASSERT(node->next->x > x0); // we ended up handling this in the caller for efficiency - #endif - - STBRP_ASSERT(node->x <= x0); - - min_y = 0; - waste_area = 0; - visited_width = 0; - while (node->x < x1) { - if (node->y > min_y) { - // raise min_y higher. - // we've accounted for all waste up to min_y, - // but we'll now add more waste for everything we've visted - waste_area += visited_width * (node->y - min_y); - min_y = node->y; - // the first time through, visited_width might be reduced - if (node->x < x0) - visited_width += node->next->x - x0; - else - visited_width += node->next->x - node->x; - } else { - // add waste area - int under_width = node->next->x - node->x; - if (under_width + visited_width > width) - under_width = width - visited_width; - waste_area += under_width * (min_y - node->y); - visited_width += under_width; - } - node = node->next; - } - - *pwaste = waste_area; - return min_y; -} - -typedef struct -{ - int x,y; - stbrp_node **prev_link; -} stbrp__findresult; - -static stbrp__findresult stbrp__skyline_find_best_pos(stbrp_context *c, int width, int height) -{ - int best_waste = (1<<30), best_x, best_y = (1 << 30); - stbrp__findresult fr; - stbrp_node **prev, *node, *tail, **best = NULL; - - // align to multiple of c->align - width = (width + c->align - 1); - width -= width % c->align; - STBRP_ASSERT(width % c->align == 0); - - node = c->active_head; - prev = &c->active_head; - while (node->x + width <= c->width) { - int y,waste; - y = stbrp__skyline_find_min_y(c, node, node->x, width, &waste); - if (c->heuristic == STBRP_HEURISTIC_Skyline_BL_sortHeight) { // actually just want to test BL - // bottom left - if (y < best_y) { - best_y = y; - best = prev; - } - } else { - // best-fit - if (y + height <= c->height) { - // can only use it if it first vertically - if (y < best_y || (y == best_y && waste < best_waste)) { - best_y = y; - best_waste = waste; - best = prev; - } - } - } - prev = &node->next; - node = node->next; - } - - best_x = (best == NULL) ? 0 : (*best)->x; - - // if doing best-fit (BF), we also have to try aligning right edge to each node position - // - // e.g, if fitting - // - // ____________________ - // |____________________| - // - // into - // - // | | - // | ____________| - // |____________| - // - // then right-aligned reduces waste, but bottom-left BL is always chooses left-aligned - // - // This makes BF take about 2x the time - - if (c->heuristic == STBRP_HEURISTIC_Skyline_BF_sortHeight) { - tail = c->active_head; - node = c->active_head; - prev = &c->active_head; - // find first node that's admissible - while (tail->x < width) - tail = tail->next; - while (tail) { - int xpos = tail->x - width; - int y,waste; - STBRP_ASSERT(xpos >= 0); - // find the left position that matches this - while (node->next->x <= xpos) { - prev = &node->next; - node = node->next; - } - STBRP_ASSERT(node->next->x > xpos && node->x <= xpos); - y = stbrp__skyline_find_min_y(c, node, xpos, width, &waste); - if (y + height < c->height) { - if (y <= best_y) { - if (y < best_y || waste < best_waste || (waste==best_waste && xpos < best_x)) { - best_x = xpos; - STBRP_ASSERT(y <= best_y); - best_y = y; - best_waste = waste; - best = prev; - } - } - } - tail = tail->next; - } - } - - fr.prev_link = best; - fr.x = best_x; - fr.y = best_y; - return fr; -} - -static stbrp__findresult stbrp__skyline_pack_rectangle(stbrp_context *context, int width, int height) -{ - // find best position according to heuristic - stbrp__findresult res = stbrp__skyline_find_best_pos(context, width, height); - stbrp_node *node, *cur; - - // bail if: - // 1. it failed - // 2. the best node doesn't fit (we don't always check this) - // 3. we're out of memory - if (res.prev_link == NULL || res.y + height > context->height || context->free_head == NULL) { - res.prev_link = NULL; - return res; - } - - // on success, create new node - node = context->free_head; - node->x = (stbrp_coord) res.x; - node->y = (stbrp_coord) (res.y + height); - - context->free_head = node->next; - - // insert the new node into the right starting point, and - // let 'cur' point to the remaining nodes needing to be - // stiched back in - - cur = *res.prev_link; - if (cur->x < res.x) { - // preserve the existing one, so start testing with the next one - stbrp_node *next = cur->next; - cur->next = node; - cur = next; - } else { - *res.prev_link = node; - } - - // from here, traverse cur and free the nodes, until we get to one - // that shouldn't be freed - while (cur->next && cur->next->x <= res.x + width) { - stbrp_node *next = cur->next; - // move the current node to the free list - cur->next = context->free_head; - context->free_head = cur; - cur = next; - } - - // stitch the list back in - node->next = cur; - - if (cur->x < res.x + width) - cur->x = (stbrp_coord) (res.x + width); - -#ifdef _DEBUG - cur = context->active_head; - while (cur->x < context->width) { - STBRP_ASSERT(cur->x < cur->next->x); - cur = cur->next; - } - STBRP_ASSERT(cur->next == NULL); - - { - stbrp_node *L1 = NULL, *L2 = NULL; - int count=0; - cur = context->active_head; - while (cur) { - L1 = cur; - cur = cur->next; - ++count; - } - cur = context->free_head; - while (cur) { - L2 = cur; - cur = cur->next; - ++count; - } - STBRP_ASSERT(count == context->num_nodes+2); - } -#endif - - return res; -} - -static int rect_height_compare(const void *a, const void *b) -{ - stbrp_rect *p = (stbrp_rect *) a; - stbrp_rect *q = (stbrp_rect *) b; - if (p->h > q->h) - return -1; - if (p->h < q->h) - return 1; - return (p->w > q->w) ? -1 : (p->w < q->w); -} - -static int rect_width_compare(const void *a, const void *b) -{ - stbrp_rect *p = (stbrp_rect *) a; - stbrp_rect *q = (stbrp_rect *) b; - if (p->w > q->w) - return -1; - if (p->w < q->w) - return 1; - return (p->h > q->h) ? -1 : (p->h < q->h); -} - -static int rect_original_order(const void *a, const void *b) -{ - stbrp_rect *p = (stbrp_rect *) a; - stbrp_rect *q = (stbrp_rect *) b; - return (p->was_packed < q->was_packed) ? -1 : (p->was_packed > q->was_packed); -} - -#ifdef STBRP_LARGE_RECTS -#define STBRP__MAXVAL 0xffffffff -#else -#define STBRP__MAXVAL 0xffff -#endif - -STBRP_DEF void stbrp_pack_rects(stbrp_context *context, stbrp_rect *rects, int num_rects) -{ - int i; - - // we use the 'was_packed' field internally to allow sorting/unsorting - for (i=0; i < num_rects; ++i) { - rects[i].was_packed = i; - #ifndef STBRP_LARGE_RECTS - STBRP_ASSERT(rects[i].w <= 0xffff && rects[i].h <= 0xffff); - #endif - } - - // sort according to heuristic - STBRP_SORT(rects, num_rects, sizeof(rects[0]), rect_height_compare); - - for (i=0; i < num_rects; ++i) { - if (rects[i].w == 0 || rects[i].h == 0) { - rects[i].x = rects[i].y = 0; // empty rect needs no space - } else { - stbrp__findresult fr = stbrp__skyline_pack_rectangle(context, rects[i].w, rects[i].h); - if (fr.prev_link) { - rects[i].x = (stbrp_coord) fr.x; - rects[i].y = (stbrp_coord) fr.y; - } else { - rects[i].x = rects[i].y = STBRP__MAXVAL; - } - } - } - - // unsort - STBRP_SORT(rects, num_rects, sizeof(rects[0]), rect_original_order); - - // set was_packed flags - for (i=0; i < num_rects; ++i) - rects[i].was_packed = !(rects[i].x == STBRP__MAXVAL && rects[i].y == STBRP__MAXVAL); -} -#endif diff --git a/impeller/third_party/stb/stb/stb_textedit.h b/impeller/third_party/stb/stb/stb_textedit.h deleted file mode 100644 index 29af484b1c6da..0000000000000 --- a/impeller/third_party/stb/stb/stb_textedit.h +++ /dev/null @@ -1,1304 +0,0 @@ -// stb_textedit.h - v1.8 - public domain - Sean Barrett -// Development of this library was sponsored by RAD Game Tools -// -// This C header file implements the guts of a multi-line text-editing -// widget; you implement display, word-wrapping, and low-level string -// insertion/deletion, and stb_textedit will map user inputs into -// insertions & deletions, plus updates to the cursor position, -// selection state, and undo state. -// -// It is intended for use in games and other systems that need to build -// their own custom widgets and which do not have heavy text-editing -// requirements (this library is not recommended for use for editing large -// texts, as its performance does not scale and it has limited undo). -// -// Non-trivial behaviors are modelled after Windows text controls. -// -// -// LICENSE -// -// This software is dual-licensed to the public domain and under the following -// license: you are granted a perpetual, irrevocable license to copy, modify, -// publish, and distribute this file as you see fit. -// -// -// DEPENDENCIES -// -// Uses the C runtime function 'memmove', which you can override -// by defining STB_TEXTEDIT_memmove before the implementation. -// Uses no other functions. Performs no runtime allocations. -// -// -// VERSION HISTORY -// -// 1.8 (2016-04-02) better keyboard handling when mouse button is down -// 1.7 (2015-09-13) change y range handling in case baseline is non-0 -// 1.6 (2015-04-15) allow STB_TEXTEDIT_memmove -// 1.5 (2014-09-10) add support for secondary keys for OS X -// 1.4 (2014-08-17) fix signed/unsigned warnings -// 1.3 (2014-06-19) fix mouse clicking to round to nearest char boundary -// 1.2 (2014-05-27) fix some RAD types that had crept into the new code -// 1.1 (2013-12-15) move-by-word (requires STB_TEXTEDIT_IS_SPACE ) -// 1.0 (2012-07-26) improve documentation, initial public release -// 0.3 (2012-02-24) bugfixes, single-line mode; insert mode -// 0.2 (2011-11-28) fixes to undo/redo -// 0.1 (2010-07-08) initial version -// -// ADDITIONAL CONTRIBUTORS -// -// Ulf Winklemann: move-by-word in 1.1 -// Fabian Giesen: secondary key inputs in 1.5 -// Martins Mozeiko: STB_TEXTEDIT_memmove -// -// Bugfixes: -// Scott Graham -// Daniel Keller -// Omar Cornut -// -// USAGE -// -// This file behaves differently depending on what symbols you define -// before including it. -// -// -// Header-file mode: -// -// If you do not define STB_TEXTEDIT_IMPLEMENTATION before including this, -// it will operate in "header file" mode. In this mode, it declares a -// single public symbol, STB_TexteditState, which encapsulates the current -// state of a text widget (except for the string, which you will store -// separately). -// -// To compile in this mode, you must define STB_TEXTEDIT_CHARTYPE to a -// primitive type that defines a single character (e.g. char, wchar_t, etc). -// -// To save space or increase undo-ability, you can optionally define the -// following things that are used by the undo system: -// -// STB_TEXTEDIT_POSITIONTYPE small int type encoding a valid cursor position -// STB_TEXTEDIT_UNDOSTATECOUNT the number of undo states to allow -// STB_TEXTEDIT_UNDOCHARCOUNT the number of characters to store in the undo buffer -// -// If you don't define these, they are set to permissive types and -// moderate sizes. The undo system does no memory allocations, so -// it grows STB_TexteditState by the worst-case storage which is (in bytes): -// -// [4 + sizeof(STB_TEXTEDIT_POSITIONTYPE)] * STB_TEXTEDIT_UNDOSTATE_COUNT -// + sizeof(STB_TEXTEDIT_CHARTYPE) * STB_TEXTEDIT_UNDOCHAR_COUNT -// -// -// Implementation mode: -// -// If you define STB_TEXTEDIT_IMPLEMENTATION before including this, it -// will compile the implementation of the text edit widget, depending -// on a large number of symbols which must be defined before the include. -// -// The implementation is defined only as static functions. You will then -// need to provide your own APIs in the same file which will access the -// static functions. -// -// The basic concept is that you provide a "string" object which -// behaves like an array of characters. stb_textedit uses indices to -// refer to positions in the string, implicitly representing positions -// in the displayed textedit. This is true for both plain text and -// rich text; even with rich text stb_truetype interacts with your -// code as if there was an array of all the displayed characters. -// -// Symbols that must be the same in header-file and implementation mode: -// -// STB_TEXTEDIT_CHARTYPE the character type -// STB_TEXTEDIT_POSITIONTYPE small type that a valid cursor position -// STB_TEXTEDIT_UNDOSTATECOUNT the number of undo states to allow -// STB_TEXTEDIT_UNDOCHARCOUNT the number of characters to store in the undo buffer -// -// Symbols you must define for implementation mode: -// -// STB_TEXTEDIT_STRING the type of object representing a string being edited, -// typically this is a wrapper object with other data you need -// -// STB_TEXTEDIT_STRINGLEN(obj) the length of the string (ideally O(1)) -// STB_TEXTEDIT_LAYOUTROW(&r,obj,n) returns the results of laying out a line of characters -// starting from character #n (see discussion below) -// STB_TEXTEDIT_GETWIDTH(obj,n,i) returns the pixel delta from the xpos of the i'th character -// to the xpos of the i+1'th char for a line of characters -// starting at character #n (i.e. accounts for kerning -// with previous char) -// STB_TEXTEDIT_KEYTOTEXT(k) maps a keyboard input to an insertable character -// (return type is int, -1 means not valid to insert) -// STB_TEXTEDIT_GETCHAR(obj,i) returns the i'th character of obj, 0-based -// STB_TEXTEDIT_NEWLINE the character returned by _GETCHAR() we recognize -// as manually wordwrapping for end-of-line positioning -// -// STB_TEXTEDIT_DELETECHARS(obj,i,n) delete n characters starting at i -// STB_TEXTEDIT_INSERTCHARS(obj,i,c*,n) insert n characters at i (pointed to by STB_TEXTEDIT_CHARTYPE*) -// -// STB_TEXTEDIT_K_SHIFT a power of two that is or'd in to a keyboard input to represent the shift key -// -// STB_TEXTEDIT_K_LEFT keyboard input to move cursor left -// STB_TEXTEDIT_K_RIGHT keyboard input to move cursor right -// STB_TEXTEDIT_K_UP keyboard input to move cursor up -// STB_TEXTEDIT_K_DOWN keyboard input to move cursor down -// STB_TEXTEDIT_K_LINESTART keyboard input to move cursor to start of line // e.g. HOME -// STB_TEXTEDIT_K_LINEEND keyboard input to move cursor to end of line // e.g. END -// STB_TEXTEDIT_K_TEXTSTART keyboard input to move cursor to start of text // e.g. ctrl-HOME -// STB_TEXTEDIT_K_TEXTEND keyboard input to move cursor to end of text // e.g. ctrl-END -// STB_TEXTEDIT_K_DELETE keyboard input to delete selection or character under cursor -// STB_TEXTEDIT_K_BACKSPACE keyboard input to delete selection or character left of cursor -// STB_TEXTEDIT_K_UNDO keyboard input to perform undo -// STB_TEXTEDIT_K_REDO keyboard input to perform redo -// -// Optional: -// STB_TEXTEDIT_K_INSERT keyboard input to toggle insert mode -// STB_TEXTEDIT_IS_SPACE(ch) true if character is whitespace (e.g. 'isspace'), -// required for WORDLEFT/WORDRIGHT -// STB_TEXTEDIT_K_WORDLEFT keyboard input to move cursor left one word // e.g. ctrl-LEFT -// STB_TEXTEDIT_K_WORDRIGHT keyboard input to move cursor right one word // e.g. ctrl-RIGHT -// STB_TEXTEDIT_K_LINESTART2 secondary keyboard input to move cursor to start of line -// STB_TEXTEDIT_K_LINEEND2 secondary keyboard input to move cursor to end of line -// STB_TEXTEDIT_K_TEXTSTART2 secondary keyboard input to move cursor to start of text -// STB_TEXTEDIT_K_TEXTEND2 secondary keyboard input to move cursor to end of text -// -// Todo: -// STB_TEXTEDIT_K_PGUP keyboard input to move cursor up a page -// STB_TEXTEDIT_K_PGDOWN keyboard input to move cursor down a page -// -// Keyboard input must be encoded as a single integer value; e.g. a character code -// and some bitflags that represent shift states. to simplify the interface, SHIFT must -// be a bitflag, so we can test the shifted state of cursor movements to allow selection, -// i.e. (STB_TEXTED_K_RIGHT|STB_TEXTEDIT_K_SHIFT) should be shifted right-arrow. -// -// You can encode other things, such as CONTROL or ALT, in additional bits, and -// then test for their presence in e.g. STB_TEXTEDIT_K_WORDLEFT. For example, -// my Windows implementations add an additional CONTROL bit, and an additional KEYDOWN -// bit. Then all of the STB_TEXTEDIT_K_ values bitwise-or in the KEYDOWN bit, -// and I pass both WM_KEYDOWN and WM_CHAR events to the "key" function in the -// API below. The control keys will only match WM_KEYDOWN events because of the -// keydown bit I add, and STB_TEXTEDIT_KEYTOTEXT only tests for the KEYDOWN -// bit so it only decodes WM_CHAR events. -// -// STB_TEXTEDIT_LAYOUTROW returns information about the shape of one displayed -// row of characters assuming they start on the i'th character--the width and -// the height and the number of characters consumed. This allows this library -// to traverse the entire layout incrementally. You need to compute word-wrapping -// here. -// -// Each textfield keeps its own insert mode state, which is not how normal -// applications work. To keep an app-wide insert mode, update/copy the -// "insert_mode" field of STB_TexteditState before/after calling API functions. -// -// API -// -// void stb_textedit_initialize_state(STB_TexteditState *state, int is_single_line) -// -// void stb_textedit_click(STB_TEXTEDIT_STRING *str, STB_TexteditState *state, float x, float y) -// void stb_textedit_drag(STB_TEXTEDIT_STRING *str, STB_TexteditState *state, float x, float y) -// int stb_textedit_cut(STB_TEXTEDIT_STRING *str, STB_TexteditState *state) -// int stb_textedit_paste(STB_TEXTEDIT_STRING *str, STB_TexteditState *state, STB_TEXTEDIT_CHARTYPE *text, int len) -// void stb_textedit_key(STB_TEXTEDIT_STRING *str, STB_TexteditState *state, int key) -// -// Each of these functions potentially updates the string and updates the -// state. -// -// initialize_state: -// set the textedit state to a known good default state when initially -// constructing the textedit. -// -// click: -// call this with the mouse x,y on a mouse down; it will update the cursor -// and reset the selection start/end to the cursor point. the x,y must -// be relative to the text widget, with (0,0) being the top left. -// -// drag: -// call this with the mouse x,y on a mouse drag/up; it will update the -// cursor and the selection end point -// -// cut: -// call this to delete the current selection; returns true if there was -// one. you should FIRST copy the current selection to the system paste buffer. -// (To copy, just copy the current selection out of the string yourself.) -// -// paste: -// call this to paste text at the current cursor point or over the current -// selection if there is one. -// -// key: -// call this for keyboard inputs sent to the textfield. you can use it -// for "key down" events or for "translated" key events. if you need to -// do both (as in Win32), or distinguish Unicode characters from control -// inputs, set a high bit to distinguish the two; then you can define the -// various definitions like STB_TEXTEDIT_K_LEFT have the is-key-event bit -// set, and make STB_TEXTEDIT_KEYTOCHAR check that the is-key-event bit is -// clear. -// -// When rendering, you can read the cursor position and selection state from -// the STB_TexteditState. -// -// -// Notes: -// -// This is designed to be usable in IMGUI, so it allows for the possibility of -// running in an IMGUI that has NOT cached the multi-line layout. For this -// reason, it provides an interface that is compatible with computing the -// layout incrementally--we try to make sure we make as few passes through -// as possible. (For example, to locate the mouse pointer in the text, we -// could define functions that return the X and Y positions of characters -// and binary search Y and then X, but if we're doing dynamic layout this -// will run the layout algorithm many times, so instead we manually search -// forward in one pass. Similar logic applies to e.g. up-arrow and -// down-arrow movement.) -// -// If it's run in a widget that *has* cached the layout, then this is less -// efficient, but it's not horrible on modern computers. But you wouldn't -// want to edit million-line files with it. - - -//////////////////////////////////////////////////////////////////////////// -//////////////////////////////////////////////////////////////////////////// -//// -//// Header-file mode -//// -//// - -#ifndef INCLUDE_STB_TEXTEDIT_H -#define INCLUDE_STB_TEXTEDIT_H - -//////////////////////////////////////////////////////////////////////// -// -// STB_TexteditState -// -// Definition of STB_TexteditState which you should store -// per-textfield; it includes cursor position, selection state, -// and undo state. -// - -#ifndef STB_TEXTEDIT_UNDOSTATECOUNT -#define STB_TEXTEDIT_UNDOSTATECOUNT 99 -#endif -#ifndef STB_TEXTEDIT_UNDOCHARCOUNT -#define STB_TEXTEDIT_UNDOCHARCOUNT 999 -#endif -#ifndef STB_TEXTEDIT_CHARTYPE -#define STB_TEXTEDIT_CHARTYPE int -#endif -#ifndef STB_TEXTEDIT_POSITIONTYPE -#define STB_TEXTEDIT_POSITIONTYPE int -#endif - -typedef struct -{ - // private data - STB_TEXTEDIT_POSITIONTYPE where; - short insert_length; - short delete_length; - short char_storage; -} StbUndoRecord; - -typedef struct -{ - // private data - StbUndoRecord undo_rec [STB_TEXTEDIT_UNDOSTATECOUNT]; - STB_TEXTEDIT_CHARTYPE undo_char[STB_TEXTEDIT_UNDOCHARCOUNT]; - short undo_point, redo_point; - short undo_char_point, redo_char_point; -} StbUndoState; - -typedef struct -{ - ///////////////////// - // - // public data - // - - int cursor; - // position of the text cursor within the string - - int select_start; // selection start point - int select_end; - // selection start and end point in characters; if equal, no selection. - // note that start may be less than or greater than end (e.g. when - // dragging the mouse, start is where the initial click was, and you - // can drag in either direction) - - unsigned char insert_mode; - // each textfield keeps its own insert mode state. to keep an app-wide - // insert mode, copy this value in/out of the app state - - ///////////////////// - // - // private data - // - unsigned char cursor_at_end_of_line; // not implemented yet - unsigned char initialized; - unsigned char has_preferred_x; - unsigned char single_line; - unsigned char padding1, padding2, padding3; - float preferred_x; // this determines where the cursor up/down tries to seek to along x - StbUndoState undostate; -} STB_TexteditState; - - -//////////////////////////////////////////////////////////////////////// -// -// StbTexteditRow -// -// Result of layout query, used by stb_textedit to determine where -// the text in each row is. - -// result of layout query -typedef struct -{ - float x0,x1; // starting x location, end x location (allows for align=right, etc) - float baseline_y_delta; // position of baseline relative to previous row's baseline - float ymin,ymax; // height of row above and below baseline - int num_chars; -} StbTexteditRow; -#endif //INCLUDE_STB_TEXTEDIT_H - - -//////////////////////////////////////////////////////////////////////////// -//////////////////////////////////////////////////////////////////////////// -//// -//// Implementation mode -//// -//// - - -// implementation isn't include-guarded, since it might have indirectly -// included just the "header" portion -#ifdef STB_TEXTEDIT_IMPLEMENTATION - -#ifndef STB_TEXTEDIT_memmove -#include -#define STB_TEXTEDIT_memmove memmove -#endif - - -///////////////////////////////////////////////////////////////////////////// -// -// Mouse input handling -// - -// traverse the layout to locate the nearest character to a display position -static int stb_text_locate_coord(STB_TEXTEDIT_STRING *str, float x, float y) -{ - StbTexteditRow r; - int n = STB_TEXTEDIT_STRINGLEN(str); - float base_y = 0, prev_x; - int i=0, k; - - r.x0 = r.x1 = 0; - r.ymin = r.ymax = 0; - r.num_chars = 0; - - // search rows to find one that straddles 'y' - while (i < n) { - STB_TEXTEDIT_LAYOUTROW(&r, str, i); - if (r.num_chars <= 0) - return n; - - if (i==0 && y < base_y + r.ymin) - return 0; - - if (y < base_y + r.ymax) - break; - - i += r.num_chars; - base_y += r.baseline_y_delta; - } - - // below all text, return 'after' last character - if (i >= n) - return n; - - // check if it's before the beginning of the line - if (x < r.x0) - return i; - - // check if it's before the end of the line - if (x < r.x1) { - // search characters in row for one that straddles 'x' - k = i; - prev_x = r.x0; - for (i=0; i < r.num_chars; ++i) { - float w = STB_TEXTEDIT_GETWIDTH(str, k, i); - if (x < prev_x+w) { - if (x < prev_x+w/2) - return k+i; - else - return k+i+1; - } - prev_x += w; - } - // shouldn't happen, but if it does, fall through to end-of-line case - } - - // if the last character is a newline, return that. otherwise return 'after' the last character - if (STB_TEXTEDIT_GETCHAR(str, i+r.num_chars-1) == STB_TEXTEDIT_NEWLINE) - return i+r.num_chars-1; - else - return i+r.num_chars; -} - -// API click: on mouse down, move the cursor to the clicked location, and reset the selection -static void stb_textedit_click(STB_TEXTEDIT_STRING *str, STB_TexteditState *state, float x, float y) -{ - state->cursor = stb_text_locate_coord(str, x, y); - state->select_start = state->cursor; - state->select_end = state->cursor; - state->has_preferred_x = 0; -} - -// API drag: on mouse drag, move the cursor and selection endpoint to the clicked location -static void stb_textedit_drag(STB_TEXTEDIT_STRING *str, STB_TexteditState *state, float x, float y) -{ - int p = stb_text_locate_coord(str, x, y); - if (state->select_start == state->select_end) - state->select_start = state->cursor; - state->cursor = state->select_end = p; -} - -///////////////////////////////////////////////////////////////////////////// -// -// Keyboard input handling -// - -// forward declarations -static void stb_text_undo(STB_TEXTEDIT_STRING *str, STB_TexteditState *state); -static void stb_text_redo(STB_TEXTEDIT_STRING *str, STB_TexteditState *state); -static void stb_text_makeundo_delete(STB_TEXTEDIT_STRING *str, STB_TexteditState *state, int where, int length); -static void stb_text_makeundo_insert(STB_TexteditState *state, int where, int length); -static void stb_text_makeundo_replace(STB_TEXTEDIT_STRING *str, STB_TexteditState *state, int where, int old_length, int new_length); - -typedef struct -{ - float x,y; // position of n'th character - float height; // height of line - int first_char, length; // first char of row, and length - int prev_first; // first char of previous row -} StbFindState; - -// find the x/y location of a character, and remember info about the previous row in -// case we get a move-up event (for page up, we'll have to rescan) -static void stb_textedit_find_charpos(StbFindState *find, STB_TEXTEDIT_STRING *str, int n, int single_line) -{ - StbTexteditRow r; - int prev_start = 0; - int z = STB_TEXTEDIT_STRINGLEN(str); - int i=0, first; - - if (n == z) { - // if it's at the end, then find the last line -- simpler than trying to - // explicitly handle this case in the regular code - if (single_line) { - STB_TEXTEDIT_LAYOUTROW(&r, str, 0); - find->y = 0; - find->first_char = 0; - find->length = z; - find->height = r.ymax - r.ymin; - find->x = r.x1; - } else { - find->y = 0; - find->x = 0; - find->height = 1; - while (i < z) { - STB_TEXTEDIT_LAYOUTROW(&r, str, i); - prev_start = i; - i += r.num_chars; - } - find->first_char = i; - find->length = 0; - find->prev_first = prev_start; - } - return; - } - - // search rows to find the one that straddles character n - find->y = 0; - - for(;;) { - STB_TEXTEDIT_LAYOUTROW(&r, str, i); - if (n < i + r.num_chars) - break; - prev_start = i; - i += r.num_chars; - find->y += r.baseline_y_delta; - } - - find->first_char = first = i; - find->length = r.num_chars; - find->height = r.ymax - r.ymin; - find->prev_first = prev_start; - - // now scan to find xpos - find->x = r.x0; - i = 0; - for (i=0; first+i < n; ++i) - find->x += STB_TEXTEDIT_GETWIDTH(str, first, i); -} - -#define STB_TEXT_HAS_SELECTION(s) ((s)->select_start != (s)->select_end) - -// make the selection/cursor state valid if client altered the string -static void stb_textedit_clamp(STB_TEXTEDIT_STRING *str, STB_TexteditState *state) -{ - int n = STB_TEXTEDIT_STRINGLEN(str); - if (STB_TEXT_HAS_SELECTION(state)) { - if (state->select_start > n) state->select_start = n; - if (state->select_end > n) state->select_end = n; - // if clamping forced them to be equal, move the cursor to match - if (state->select_start == state->select_end) - state->cursor = state->select_start; - } - if (state->cursor > n) state->cursor = n; -} - -// delete characters while updating undo -static void stb_textedit_delete(STB_TEXTEDIT_STRING *str, STB_TexteditState *state, int where, int len) -{ - stb_text_makeundo_delete(str, state, where, len); - STB_TEXTEDIT_DELETECHARS(str, where, len); - state->has_preferred_x = 0; -} - -// delete the section -static void stb_textedit_delete_selection(STB_TEXTEDIT_STRING *str, STB_TexteditState *state) -{ - stb_textedit_clamp(str, state); - if (STB_TEXT_HAS_SELECTION(state)) { - if (state->select_start < state->select_end) { - stb_textedit_delete(str, state, state->select_start, state->select_end - state->select_start); - state->select_end = state->cursor = state->select_start; - } else { - stb_textedit_delete(str, state, state->select_end, state->select_start - state->select_end); - state->select_start = state->cursor = state->select_end; - } - state->has_preferred_x = 0; - } -} - -// canoncialize the selection so start <= end -static void stb_textedit_sortselection(STB_TexteditState *state) -{ - if (state->select_end < state->select_start) { - int temp = state->select_end; - state->select_end = state->select_start; - state->select_start = temp; - } -} - -// move cursor to first character of selection -static void stb_textedit_move_to_first(STB_TexteditState *state) -{ - if (STB_TEXT_HAS_SELECTION(state)) { - stb_textedit_sortselection(state); - state->cursor = state->select_start; - state->select_end = state->select_start; - state->has_preferred_x = 0; - } -} - -// move cursor to last character of selection -static void stb_textedit_move_to_last(STB_TEXTEDIT_STRING *str, STB_TexteditState *state) -{ - if (STB_TEXT_HAS_SELECTION(state)) { - stb_textedit_sortselection(state); - stb_textedit_clamp(str, state); - state->cursor = state->select_end; - state->select_start = state->select_end; - state->has_preferred_x = 0; - } -} - -#ifdef STB_TEXTEDIT_IS_SPACE -static int is_word_boundary( STB_TEXTEDIT_STRING *_str, int _idx ) -{ - return _idx > 0 ? (STB_TEXTEDIT_IS_SPACE( STB_TEXTEDIT_GETCHAR(_str,_idx-1) ) && !STB_TEXTEDIT_IS_SPACE( STB_TEXTEDIT_GETCHAR(_str, _idx) ) ) : 1; -} - -static int stb_textedit_move_to_word_previous( STB_TEXTEDIT_STRING *_str, STB_TexteditState *_state ) -{ - int c = _state->cursor - 1; - while( c >= 0 && !is_word_boundary( _str, c ) ) - --c; - - if( c < 0 ) - c = 0; - - return c; -} - -static int stb_textedit_move_to_word_next( STB_TEXTEDIT_STRING *_str, STB_TexteditState *_state ) -{ - const int len = STB_TEXTEDIT_STRINGLEN(_str); - int c = _state->cursor+1; - while( c < len && !is_word_boundary( _str, c ) ) - ++c; - - if( c > len ) - c = len; - - return c; -} -#endif - -// update selection and cursor to match each other -static void stb_textedit_prep_selection_at_cursor(STB_TexteditState *state) -{ - if (!STB_TEXT_HAS_SELECTION(state)) - state->select_start = state->select_end = state->cursor; - else - state->cursor = state->select_end; -} - -// API cut: delete selection -static int stb_textedit_cut(STB_TEXTEDIT_STRING *str, STB_TexteditState *state) -{ - if (STB_TEXT_HAS_SELECTION(state)) { - stb_textedit_delete_selection(str,state); // implicity clamps - state->has_preferred_x = 0; - return 1; - } - return 0; -} - -// API paste: replace existing selection with passed-in text -static int stb_textedit_paste(STB_TEXTEDIT_STRING *str, STB_TexteditState *state, STB_TEXTEDIT_CHARTYPE const *ctext, int len) -{ - STB_TEXTEDIT_CHARTYPE *text = (STB_TEXTEDIT_CHARTYPE *) ctext; - // if there's a selection, the paste should delete it - stb_textedit_clamp(str, state); - stb_textedit_delete_selection(str,state); - // try to insert the characters - if (STB_TEXTEDIT_INSERTCHARS(str, state->cursor, text, len)) { - stb_text_makeundo_insert(state, state->cursor, len); - state->cursor += len; - state->has_preferred_x = 0; - return 1; - } - // remove the undo since we didn't actually insert the characters - if (state->undostate.undo_point) - --state->undostate.undo_point; - return 0; -} - -// API key: process a keyboard input -static void stb_textedit_key(STB_TEXTEDIT_STRING *str, STB_TexteditState *state, int key) -{ -retry: - switch (key) { - default: { - int c = STB_TEXTEDIT_KEYTOTEXT(key); - if (c > 0) { - STB_TEXTEDIT_CHARTYPE ch = (STB_TEXTEDIT_CHARTYPE) c; - - // can't add newline in single-line mode - if (c == '\n' && state->single_line) - break; - - if (state->insert_mode && !STB_TEXT_HAS_SELECTION(state) && state->cursor < STB_TEXTEDIT_STRINGLEN(str)) { - stb_text_makeundo_replace(str, state, state->cursor, 1, 1); - STB_TEXTEDIT_DELETECHARS(str, state->cursor, 1); - if (STB_TEXTEDIT_INSERTCHARS(str, state->cursor, &ch, 1)) { - ++state->cursor; - state->has_preferred_x = 0; - } - } else { - stb_textedit_delete_selection(str,state); // implicity clamps - if (STB_TEXTEDIT_INSERTCHARS(str, state->cursor, &ch, 1)) { - stb_text_makeundo_insert(state, state->cursor, 1); - ++state->cursor; - state->has_preferred_x = 0; - } - } - } - break; - } - -#ifdef STB_TEXTEDIT_K_INSERT - case STB_TEXTEDIT_K_INSERT: - state->insert_mode = !state->insert_mode; - break; -#endif - - case STB_TEXTEDIT_K_UNDO: - stb_text_undo(str, state); - state->has_preferred_x = 0; - break; - - case STB_TEXTEDIT_K_REDO: - stb_text_redo(str, state); - state->has_preferred_x = 0; - break; - - case STB_TEXTEDIT_K_LEFT: - // if currently there's a selection, move cursor to start of selection - if (STB_TEXT_HAS_SELECTION(state)) - stb_textedit_move_to_first(state); - else - if (state->cursor > 0) - --state->cursor; - state->has_preferred_x = 0; - break; - - case STB_TEXTEDIT_K_RIGHT: - // if currently there's a selection, move cursor to end of selection - if (STB_TEXT_HAS_SELECTION(state)) - stb_textedit_move_to_last(str, state); - else - ++state->cursor; - stb_textedit_clamp(str, state); - state->has_preferred_x = 0; - break; - - case STB_TEXTEDIT_K_LEFT | STB_TEXTEDIT_K_SHIFT: - stb_textedit_clamp(str, state); - stb_textedit_prep_selection_at_cursor(state); - // move selection left - if (state->select_end > 0) - --state->select_end; - state->cursor = state->select_end; - state->has_preferred_x = 0; - break; - -#ifdef STB_TEXTEDIT_IS_SPACE - case STB_TEXTEDIT_K_WORDLEFT: - if (STB_TEXT_HAS_SELECTION(state)) - stb_textedit_move_to_first(state); - else { - state->cursor = stb_textedit_move_to_word_previous(str, state); - stb_textedit_clamp( str, state ); - } - break; - - case STB_TEXTEDIT_K_WORDRIGHT: - if (STB_TEXT_HAS_SELECTION(state)) - stb_textedit_move_to_last(str, state); - else { - state->cursor = stb_textedit_move_to_word_next(str, state); - stb_textedit_clamp( str, state ); - } - break; - - case STB_TEXTEDIT_K_WORDLEFT | STB_TEXTEDIT_K_SHIFT: - if( !STB_TEXT_HAS_SELECTION( state ) ) - stb_textedit_prep_selection_at_cursor(state); - - state->cursor = stb_textedit_move_to_word_previous(str, state); - state->select_end = state->cursor; - - stb_textedit_clamp( str, state ); - break; - - case STB_TEXTEDIT_K_WORDRIGHT | STB_TEXTEDIT_K_SHIFT: - if( !STB_TEXT_HAS_SELECTION( state ) ) - stb_textedit_prep_selection_at_cursor(state); - - state->cursor = stb_textedit_move_to_word_next(str, state); - state->select_end = state->cursor; - - stb_textedit_clamp( str, state ); - break; -#endif - - case STB_TEXTEDIT_K_RIGHT | STB_TEXTEDIT_K_SHIFT: - stb_textedit_prep_selection_at_cursor(state); - // move selection right - ++state->select_end; - stb_textedit_clamp(str, state); - state->cursor = state->select_end; - state->has_preferred_x = 0; - break; - - case STB_TEXTEDIT_K_DOWN: - case STB_TEXTEDIT_K_DOWN | STB_TEXTEDIT_K_SHIFT: { - StbFindState find; - StbTexteditRow row; - int i, sel = (key & STB_TEXTEDIT_K_SHIFT) != 0; - - if (state->single_line) { - // on windows, up&down in single-line behave like left&right - key = STB_TEXTEDIT_K_RIGHT | (key & STB_TEXTEDIT_K_SHIFT); - goto retry; - } - - if (sel) - stb_textedit_prep_selection_at_cursor(state); - else if (STB_TEXT_HAS_SELECTION(state)) - stb_textedit_move_to_last(str,state); - - // compute current position of cursor point - stb_textedit_clamp(str, state); - stb_textedit_find_charpos(&find, str, state->cursor, state->single_line); - - // now find character position down a row - if (find.length) { - float goal_x = state->has_preferred_x ? state->preferred_x : find.x; - float x; - int start = find.first_char + find.length; - state->cursor = start; - STB_TEXTEDIT_LAYOUTROW(&row, str, state->cursor); - x = row.x0; - for (i=0; i < row.num_chars; ++i) { - float dx = STB_TEXTEDIT_GETWIDTH(str, start, i); - #ifdef STB_TEXTEDIT_GETWIDTH_NEWLINE - if (dx == STB_TEXTEDIT_GETWIDTH_NEWLINE) - break; - #endif - x += dx; - if (x > goal_x) - break; - ++state->cursor; - } - stb_textedit_clamp(str, state); - - state->has_preferred_x = 1; - state->preferred_x = goal_x; - - if (sel) - state->select_end = state->cursor; - } - break; - } - - case STB_TEXTEDIT_K_UP: - case STB_TEXTEDIT_K_UP | STB_TEXTEDIT_K_SHIFT: { - StbFindState find; - StbTexteditRow row; - int i, sel = (key & STB_TEXTEDIT_K_SHIFT) != 0; - - if (state->single_line) { - // on windows, up&down become left&right - key = STB_TEXTEDIT_K_LEFT | (key & STB_TEXTEDIT_K_SHIFT); - goto retry; - } - - if (sel) - stb_textedit_prep_selection_at_cursor(state); - else if (STB_TEXT_HAS_SELECTION(state)) - stb_textedit_move_to_first(state); - - // compute current position of cursor point - stb_textedit_clamp(str, state); - stb_textedit_find_charpos(&find, str, state->cursor, state->single_line); - - // can only go up if there's a previous row - if (find.prev_first != find.first_char) { - // now find character position up a row - float goal_x = state->has_preferred_x ? state->preferred_x : find.x; - float x; - state->cursor = find.prev_first; - STB_TEXTEDIT_LAYOUTROW(&row, str, state->cursor); - x = row.x0; - for (i=0; i < row.num_chars; ++i) { - float dx = STB_TEXTEDIT_GETWIDTH(str, find.prev_first, i); - #ifdef STB_TEXTEDIT_GETWIDTH_NEWLINE - if (dx == STB_TEXTEDIT_GETWIDTH_NEWLINE) - break; - #endif - x += dx; - if (x > goal_x) - break; - ++state->cursor; - } - stb_textedit_clamp(str, state); - - state->has_preferred_x = 1; - state->preferred_x = goal_x; - - if (sel) - state->select_end = state->cursor; - } - break; - } - - case STB_TEXTEDIT_K_DELETE: - case STB_TEXTEDIT_K_DELETE | STB_TEXTEDIT_K_SHIFT: - if (STB_TEXT_HAS_SELECTION(state)) - stb_textedit_delete_selection(str, state); - else { - int n = STB_TEXTEDIT_STRINGLEN(str); - if (state->cursor < n) - stb_textedit_delete(str, state, state->cursor, 1); - } - state->has_preferred_x = 0; - break; - - case STB_TEXTEDIT_K_BACKSPACE: - case STB_TEXTEDIT_K_BACKSPACE | STB_TEXTEDIT_K_SHIFT: - if (STB_TEXT_HAS_SELECTION(state)) - stb_textedit_delete_selection(str, state); - else { - stb_textedit_clamp(str, state); - if (state->cursor > 0) { - stb_textedit_delete(str, state, state->cursor-1, 1); - --state->cursor; - } - } - state->has_preferred_x = 0; - break; - -#ifdef STB_TEXTEDIT_K_TEXTSTART2 - case STB_TEXTEDIT_K_TEXTSTART2: -#endif - case STB_TEXTEDIT_K_TEXTSTART: - state->cursor = state->select_start = state->select_end = 0; - state->has_preferred_x = 0; - break; - -#ifdef STB_TEXTEDIT_K_TEXTEND2 - case STB_TEXTEDIT_K_TEXTEND2: -#endif - case STB_TEXTEDIT_K_TEXTEND: - state->cursor = STB_TEXTEDIT_STRINGLEN(str); - state->select_start = state->select_end = 0; - state->has_preferred_x = 0; - break; - -#ifdef STB_TEXTEDIT_K_TEXTSTART2 - case STB_TEXTEDIT_K_TEXTSTART2 | STB_TEXTEDIT_K_SHIFT: -#endif - case STB_TEXTEDIT_K_TEXTSTART | STB_TEXTEDIT_K_SHIFT: - stb_textedit_prep_selection_at_cursor(state); - state->cursor = state->select_end = 0; - state->has_preferred_x = 0; - break; - -#ifdef STB_TEXTEDIT_K_TEXTEND2 - case STB_TEXTEDIT_K_TEXTEND2 | STB_TEXTEDIT_K_SHIFT: -#endif - case STB_TEXTEDIT_K_TEXTEND | STB_TEXTEDIT_K_SHIFT: - stb_textedit_prep_selection_at_cursor(state); - state->cursor = state->select_end = STB_TEXTEDIT_STRINGLEN(str); - state->has_preferred_x = 0; - break; - - -#ifdef STB_TEXTEDIT_K_LINESTART2 - case STB_TEXTEDIT_K_LINESTART2: -#endif - case STB_TEXTEDIT_K_LINESTART: { - StbFindState find; - stb_textedit_clamp(str, state); - stb_textedit_move_to_first(state); - stb_textedit_find_charpos(&find, str, state->cursor, state->single_line); - state->cursor = find.first_char; - state->has_preferred_x = 0; - break; - } - -#ifdef STB_TEXTEDIT_K_LINEEND2 - case STB_TEXTEDIT_K_LINEEND2: -#endif - case STB_TEXTEDIT_K_LINEEND: { - StbFindState find; - stb_textedit_clamp(str, state); - stb_textedit_move_to_first(state); - stb_textedit_find_charpos(&find, str, state->cursor, state->single_line); - - state->has_preferred_x = 0; - state->cursor = find.first_char + find.length; - if (find.length > 0 && STB_TEXTEDIT_GETCHAR(str, state->cursor-1) == STB_TEXTEDIT_NEWLINE) - --state->cursor; - break; - } - -#ifdef STB_TEXTEDIT_K_LINESTART2 - case STB_TEXTEDIT_K_LINESTART2 | STB_TEXTEDIT_K_SHIFT: -#endif - case STB_TEXTEDIT_K_LINESTART | STB_TEXTEDIT_K_SHIFT: { - StbFindState find; - stb_textedit_clamp(str, state); - stb_textedit_prep_selection_at_cursor(state); - stb_textedit_find_charpos(&find, str, state->cursor, state->single_line); - state->cursor = state->select_end = find.first_char; - state->has_preferred_x = 0; - break; - } - -#ifdef STB_TEXTEDIT_K_LINEEND2 - case STB_TEXTEDIT_K_LINEEND2 | STB_TEXTEDIT_K_SHIFT: -#endif - case STB_TEXTEDIT_K_LINEEND | STB_TEXTEDIT_K_SHIFT: { - StbFindState find; - stb_textedit_clamp(str, state); - stb_textedit_prep_selection_at_cursor(state); - stb_textedit_find_charpos(&find, str, state->cursor, state->single_line); - state->has_preferred_x = 0; - state->cursor = find.first_char + find.length; - if (find.length > 0 && STB_TEXTEDIT_GETCHAR(str, state->cursor-1) == STB_TEXTEDIT_NEWLINE) - --state->cursor; - state->select_end = state->cursor; - break; - } - -// @TODO: -// STB_TEXTEDIT_K_PGUP - move cursor up a page -// STB_TEXTEDIT_K_PGDOWN - move cursor down a page - } -} - -///////////////////////////////////////////////////////////////////////////// -// -// Undo processing -// -// @OPTIMIZE: the undo/redo buffer should be circular - -static void stb_textedit_flush_redo(StbUndoState *state) -{ - state->redo_point = STB_TEXTEDIT_UNDOSTATECOUNT; - state->redo_char_point = STB_TEXTEDIT_UNDOCHARCOUNT; -} - -// discard the oldest entry in the undo list -static void stb_textedit_discard_undo(StbUndoState *state) -{ - if (state->undo_point > 0) { - // if the 0th undo state has characters, clean those up - if (state->undo_rec[0].char_storage >= 0) { - int n = state->undo_rec[0].insert_length, i; - // delete n characters from all other records - state->undo_char_point = state->undo_char_point - (short) n; // vsnet05 - STB_TEXTEDIT_memmove(state->undo_char, state->undo_char + n, (size_t) (state->undo_char_point*sizeof(STB_TEXTEDIT_CHARTYPE))); - for (i=0; i < state->undo_point; ++i) - if (state->undo_rec[i].char_storage >= 0) - state->undo_rec[i].char_storage = state->undo_rec[i].char_storage - (short) n; // vsnet05 // @OPTIMIZE: get rid of char_storage and infer it - } - --state->undo_point; - STB_TEXTEDIT_memmove(state->undo_rec, state->undo_rec+1, (size_t) (state->undo_point*sizeof(state->undo_rec[0]))); - } -} - -// discard the oldest entry in the redo list--it's bad if this -// ever happens, but because undo & redo have to store the actual -// characters in different cases, the redo character buffer can -// fill up even though the undo buffer didn't -static void stb_textedit_discard_redo(StbUndoState *state) -{ - int k = STB_TEXTEDIT_UNDOSTATECOUNT-1; - - if (state->redo_point <= k) { - // if the k'th undo state has characters, clean those up - if (state->undo_rec[k].char_storage >= 0) { - int n = state->undo_rec[k].insert_length, i; - // delete n characters from all other records - state->redo_char_point = state->redo_char_point + (short) n; // vsnet05 - STB_TEXTEDIT_memmove(state->undo_char + state->redo_char_point, state->undo_char + state->redo_char_point-n, (size_t) ((STB_TEXTEDIT_UNDOSTATECOUNT - state->redo_char_point)*sizeof(STB_TEXTEDIT_CHARTYPE))); - for (i=state->redo_point; i < k; ++i) - if (state->undo_rec[i].char_storage >= 0) - state->undo_rec[i].char_storage = state->undo_rec[i].char_storage + (short) n; // vsnet05 - } - ++state->redo_point; - STB_TEXTEDIT_memmove(state->undo_rec + state->redo_point-1, state->undo_rec + state->redo_point, (size_t) ((STB_TEXTEDIT_UNDOSTATECOUNT - state->redo_point)*sizeof(state->undo_rec[0]))); - } -} - -static StbUndoRecord *stb_text_create_undo_record(StbUndoState *state, int numchars) -{ - // any time we create a new undo record, we discard redo - stb_textedit_flush_redo(state); - - // if we have no free records, we have to make room, by sliding the - // existing records down - if (state->undo_point == STB_TEXTEDIT_UNDOSTATECOUNT) - stb_textedit_discard_undo(state); - - // if the characters to store won't possibly fit in the buffer, we can't undo - if (numchars > STB_TEXTEDIT_UNDOCHARCOUNT) { - state->undo_point = 0; - state->undo_char_point = 0; - return NULL; - } - - // if we don't have enough free characters in the buffer, we have to make room - while (state->undo_char_point + numchars > STB_TEXTEDIT_UNDOCHARCOUNT) - stb_textedit_discard_undo(state); - - return &state->undo_rec[state->undo_point++]; -} - -static STB_TEXTEDIT_CHARTYPE *stb_text_createundo(StbUndoState *state, int pos, int insert_len, int delete_len) -{ - StbUndoRecord *r = stb_text_create_undo_record(state, insert_len); - if (r == NULL) - return NULL; - - r->where = pos; - r->insert_length = (short) insert_len; - r->delete_length = (short) delete_len; - - if (insert_len == 0) { - r->char_storage = -1; - return NULL; - } else { - r->char_storage = state->undo_char_point; - state->undo_char_point = state->undo_char_point + (short) insert_len; - return &state->undo_char[r->char_storage]; - } -} - -static void stb_text_undo(STB_TEXTEDIT_STRING *str, STB_TexteditState *state) -{ - StbUndoState *s = &state->undostate; - StbUndoRecord u, *r; - if (s->undo_point == 0) - return; - - // we need to do two things: apply the undo record, and create a redo record - u = s->undo_rec[s->undo_point-1]; - r = &s->undo_rec[s->redo_point-1]; - r->char_storage = -1; - - r->insert_length = u.delete_length; - r->delete_length = u.insert_length; - r->where = u.where; - - if (u.delete_length) { - // if the undo record says to delete characters, then the redo record will - // need to re-insert the characters that get deleted, so we need to store - // them. - - // there are three cases: - // there's enough room to store the characters - // characters stored for *redoing* don't leave room for redo - // characters stored for *undoing* don't leave room for redo - // if the last is true, we have to bail - - if (s->undo_char_point + u.delete_length >= STB_TEXTEDIT_UNDOCHARCOUNT) { - // the undo records take up too much character space; there's no space to store the redo characters - r->insert_length = 0; - } else { - int i; - - // there's definitely room to store the characters eventually - while (s->undo_char_point + u.delete_length > s->redo_char_point) { - // there's currently not enough room, so discard a redo record - stb_textedit_discard_redo(s); - // should never happen: - if (s->redo_point == STB_TEXTEDIT_UNDOSTATECOUNT) - return; - } - r = &s->undo_rec[s->redo_point-1]; - - r->char_storage = s->redo_char_point - u.delete_length; - s->redo_char_point = s->redo_char_point - (short) u.delete_length; - - // now save the characters - for (i=0; i < u.delete_length; ++i) - s->undo_char[r->char_storage + i] = STB_TEXTEDIT_GETCHAR(str, u.where + i); - } - - // now we can carry out the deletion - STB_TEXTEDIT_DELETECHARS(str, u.where, u.delete_length); - } - - // check type of recorded action: - if (u.insert_length) { - // easy case: was a deletion, so we need to insert n characters - STB_TEXTEDIT_INSERTCHARS(str, u.where, &s->undo_char[u.char_storage], u.insert_length); - s->undo_char_point -= u.insert_length; - } - - state->cursor = u.where + u.insert_length; - - s->undo_point--; - s->redo_point--; -} - -static void stb_text_redo(STB_TEXTEDIT_STRING *str, STB_TexteditState *state) -{ - StbUndoState *s = &state->undostate; - StbUndoRecord *u, r; - if (s->redo_point == STB_TEXTEDIT_UNDOSTATECOUNT) - return; - - // we need to do two things: apply the redo record, and create an undo record - u = &s->undo_rec[s->undo_point]; - r = s->undo_rec[s->redo_point]; - - // we KNOW there must be room for the undo record, because the redo record - // was derived from an undo record - - u->delete_length = r.insert_length; - u->insert_length = r.delete_length; - u->where = r.where; - u->char_storage = -1; - - if (r.delete_length) { - // the redo record requires us to delete characters, so the undo record - // needs to store the characters - - if (s->undo_char_point + u->insert_length > s->redo_char_point) { - u->insert_length = 0; - u->delete_length = 0; - } else { - int i; - u->char_storage = s->undo_char_point; - s->undo_char_point = s->undo_char_point + u->insert_length; - - // now save the characters - for (i=0; i < u->insert_length; ++i) - s->undo_char[u->char_storage + i] = STB_TEXTEDIT_GETCHAR(str, u->where + i); - } - - STB_TEXTEDIT_DELETECHARS(str, r.where, r.delete_length); - } - - if (r.insert_length) { - // easy case: need to insert n characters - STB_TEXTEDIT_INSERTCHARS(str, r.where, &s->undo_char[r.char_storage], r.insert_length); - } - - state->cursor = r.where + r.insert_length; - - s->undo_point++; - s->redo_point++; -} - -static void stb_text_makeundo_insert(STB_TexteditState *state, int where, int length) -{ - stb_text_createundo(&state->undostate, where, 0, length); -} - -static void stb_text_makeundo_delete(STB_TEXTEDIT_STRING *str, STB_TexteditState *state, int where, int length) -{ - int i; - STB_TEXTEDIT_CHARTYPE *p = stb_text_createundo(&state->undostate, where, length, 0); - if (p) { - for (i=0; i < length; ++i) - p[i] = STB_TEXTEDIT_GETCHAR(str, where+i); - } -} - -static void stb_text_makeundo_replace(STB_TEXTEDIT_STRING *str, STB_TexteditState *state, int where, int old_length, int new_length) -{ - int i; - STB_TEXTEDIT_CHARTYPE *p = stb_text_createundo(&state->undostate, where, old_length, new_length); - if (p) { - for (i=0; i < old_length; ++i) - p[i] = STB_TEXTEDIT_GETCHAR(str, where+i); - } -} - -// reset the state to default -static void stb_textedit_clear_state(STB_TexteditState *state, int is_single_line) -{ - state->undostate.undo_point = 0; - state->undostate.undo_char_point = 0; - state->undostate.redo_point = STB_TEXTEDIT_UNDOSTATECOUNT; - state->undostate.redo_char_point = STB_TEXTEDIT_UNDOCHARCOUNT; - state->select_end = state->select_start = 0; - state->cursor = 0; - state->has_preferred_x = 0; - state->preferred_x = 0; - state->cursor_at_end_of_line = 0; - state->initialized = 1; - state->single_line = (unsigned char) is_single_line; - state->insert_mode = 0; -} - -// API initialize -static void stb_textedit_initialize_state(STB_TexteditState *state, int is_single_line) -{ - stb_textedit_clear_state(state, is_single_line); -} -#endif//STB_TEXTEDIT_IMPLEMENTATION diff --git a/impeller/third_party/stb/stb/stb_tilemap_editor.h b/impeller/third_party/stb/stb/stb_tilemap_editor.h deleted file mode 100644 index cc66dd58f9623..0000000000000 --- a/impeller/third_party/stb/stb/stb_tilemap_editor.h +++ /dev/null @@ -1,4131 +0,0 @@ -// stb_tilemap_editor.h - v0.37 - Sean Barrett - http://nothings.org/stb -// placed in the public domain - not copyrighted - first released 2014-09 -// -// Embeddable tilemap editor for C/C++ -// -// -// TABLE OF CONTENTS -// FAQ -// How to compile/use the library -// Additional configuration macros -// API documentation -// Info on editing multiple levels -// Revision history -// Todo -// Credits -// License -// -// -// FAQ -// -// Q: What counts as a tilemap for this library? -// -// A: An array of rectangles, where each rectangle contains a small -// stack of images. -// -// Q: What are the limitations? -// -// A: Maps are limited to 4096x4096 in dimension. -// Each map square can only contain a stack of at most 32 images. -// A map can only use up to 32768 distinct image tiles. -// -// Q: How do I compile this? -// -// A: You need to #define several symbols before #including it, but only -// in one file. This will cause all the function definitions to be -// generated in that file. See the "HOW TO COMPILE" section. -// -// Q: What advantages does this have over a standalone editor? -// -// A: For one, you can integrate the editor into your game so you can -// flip between editing and testing without even switching windows. -// For another, you don't need an XML parser to get at the map data. -// -// Q: Can I live-edit my game maps? -// -// A: Not really, the editor keeps its own map representation. -// -// Q: How do I save and load maps? -// -// A: You have to do this yourself. The editor provides serialization -// functions (get & set) for reading and writing the map it holds. -// You can choose whatever format you want to store the map to on -// disk; you just need to provide functions to convert. (For example, -// I actually store the editor's map representation to disk basically -// as-is; then I have a single function that converts from the editor -// map representation to the game representation, which is used both -// to go from editor-to-game and from loaded-map-to-game.) -// -// Q: I want to have tiles change appearance based on what's -// adjacent, or other tile-display/substitution trickiness. -// -// A: You can do this when you convert from the editor's map -// representation to the game representation, but there's -// no way to show this live in the editor. -// -// Q: The editor appears to be put map location (0,0) at the top left? -// I want to use a different coordinate system in my game (e.g. y -// increasing upwards, or origin at the center). -// -// A: You can do this when you convert from the editor's map -// representation to the game representation. (Don't forget to -// translate link coordinates as well!) -// -// Q: The editor appears to put pixel (0,0) at the top left? I want -// to use a different coordinate system in my game. -// -// A: The editor defines an "editor pixel coordinate system" with -// (0,0) at the top left and requires you to display things in -// that coordinate system. You can freely remap those coordinates -// to anything you want on screen. -// -// Q: How do I scale the user interface? -// -// A: Since you do all the rendering, you can scale up all the rendering -// calls that the library makes to you. If you do, (a) you need -// to also scale up the mouse coordinates, and (b) you may want -// to scale the map display back down so that you're only scaling -// the UI and not everything. See the next question. -// -// Q: How do I scale the map display? -// -// A: Use stbte_set_spacing() to change the size that the map is displayed -// at. Note that the "callbacks" to draw tiles are used for both drawing -// the map and drawing the tile palette, so that callback may need to -// draw at two different scales. You should choose the scales to match -// You can tell them apart because the -// tile palette gets NULL for the property pointer. -// -// Q: How does object editing work? -// -// A: One way to think of this is that in the editor, you're placing -// spawners, not objects. Each spawner must be tile-aligned, because -// it's only a tile editor. Each tile (stack of layers) gets -// an associated set of properties, and it's up to you to -// determine what properties should appear for a given tile, -// based on e.g. the spawners that are in it. -// -// Q: How are properties themselves handled? -// -// A: All properties, regardless of UI behavior, are internally floats. -// Each tile has an array of floats associated with it, which is -// passed back to you when drawing the tiles so you can draw -// objects appropriately modified by the properties. -// -// Q: What if I want to have two different objects/spawners in -// one tile, both of which have their own properties? -// -// A: Make sure STBTE_MAX_PROPERTIES is large enough for the sum of -// properties in both objects, and then you have to explicitly -// map the property slot #s to the appropriate objects. They'll -// still all appear in a single property panel; there's no way -// to get multiple panels. -// -// Q: Can I do one-to-many linking? -// -// A: The library only supports one link per tile. However, you -// can have multiple tiles all link to a single tile. So, you -// can fake one-to-many linking by linking in the reverse -// direction. -// -// Q: What if I have two objects in the same tile, and they each -// need an independent link? Or I have two kinds of link associated -// with a single object? -// -// A: There is no way to do this. (Unless you can reverse one link.) -// -// Q: How does cut & paste interact with object properties & links? -// -// A: Currently the library has no idea which properties or links -// are associated with which layers of a tile. So currently, the -// library will only copy properties & links if the layer panel -// is set to allow all layers to be copied, OR if you set the -// "props" in the layer panel to "always". Similarly, you can -// set "props" to "none" so it will never copy. -// -// Q: What happens if the library gets a memory allocation failure -// while I'm editing? Will I lose my work? -// -// A: The library allocates all editor memory when you create -// the tilemap. It allocates a maximally-sized map and a -// fixed-size undo buffer (and the fixed-size copy buffer -// is static), and never allocates memory while it's running. -// So it can't fail due to running out of memory. -// -// Q: What happens if the library crashes while I'm editing? Will -// I lose my work? -// -// A: Yes. Save often. -// -// -// HOW TO COMPILE -// -// This header file contains both the header file and the -// implementation file in one. To create the implementation, -// in one source file define a few symbols first and then -// include this header: -// -// #define STB_TILEMAP_EDITOR_IMPLEMENTATION -// // this triggers the implementation -// -// void STBTE_DRAW_RECT(int x0, int y0, int x1, int y1, uint color); -// // this must draw a filled rectangle (exclusive on right/bottom) -// // color = (r<<16)|(g<<8)|(b) -// -// void STBTE_DRAW_TILE(int x0, int y0, -// unsigned short id, int highlight, float *data); -// // this draws the tile image identified by 'id' in one of several -// // highlight modes (see STBTE_drawmode_* in the header section); -// // if 'data' is NULL, it's drawing the tile in the palette; if 'data' -// // is not NULL, it's drawing a tile on the map, and that is the data -// // associated with that map tile -// -// #include "stb_tilemap_editor.h" -// -// Optionally you can define the following functions before the include; -// note these must be macros (but they can just call a function) so -// this library can #ifdef to detect if you've defined them: -// -// #define STBTE_PROP_TYPE(int n, short *tiledata, float *params) ... -// // Returns the type of the n'th property of a given tile, which -// // controls how it is edited. Legal types are: -// // 0 /* no editable property in this slot */ -// // STBTE_PROP_int /* uses a slider to adjust value */ -// // STBTE_PROP_float /* uses a weird multi-axis control */ -// // STBTE_PROP_bool /* uses a checkbox to change value */ -// // And you can bitwise-OR in the following flags: -// // STBTE_PROP_disabled -// // Note that all of these are stored as floats in the param array. -// // The integer slider is limited in precision based on the space -// // available on screen, so for wide-ranged integers you may want -// // to use floats instead. -// // -// // Since the tiledata is passed to you, you can choose which property -// // is bound to that slot based on that data. -// // -// // Changing the type of a parameter does not cause the underlying -// // value to be clamped to the type min/max except when the tile is -// // explicitly selected. -// -// #define STBTE_PROP_NAME(int n, short *tiledata, float *params) ... -// // these return a string with the name for slot #n in the float -// // property list for the tile. -// -// #define STBTE_PROP_MIN(int n, short *tiledata) ...your code here... -// #define STBTE_PROP_MAX(int n, short *tiledata) ...your code here... -// // These return the allowable range for the property values for -// // the specified slot. It is never called for boolean types. -// -// #define STBTE_PROP_FLOAT_SCALE(int n, short *tiledata, float *params) -// // This rescales the float control for a given property; by default -// // left mouse drags add integers, right mouse drags adds fractions, -// // but you can rescale this per-property. -// -// #define STBTE_FLOAT_CONTROL_GRANULARITY ... value ... -// // This returns the number of pixels of mouse motion necessary -// // to advance the object float control. Default is 4 -// -// #define STBTE_ALLOW_LINK(short *src, float *src_data, \ -// short *dest, float *dest_data) ...your code... -// // this returns true or false depending on whether you allow a link -// // to be drawn from a tile 'src' to a tile 'dest'. if you don't -// // define this, linking will not be supported -// -// #define STBTE_LINK_COLOR(short *src, float *src_data, \ -// short *dest, float *dest_data) ...your code... -// // return a color encoded as a 24-bit unsigned integer in the -// // form 0xRRGGBB. If you don't define this, default colors will -// // be used. -// -// -// [[ support for those below is not implemented yet ]] -// -// #define STBTE_HITTEST_TILE(x0,y0,id,mx,my) ...your code here... -// // this returns true or false depending on whether the mouse -// // pointer at mx,my is over (touching) a tile of type 'id' -// // displayed at x0,y0. Normally stb_tilemap_editor just does -// // this hittest based on the tile geometry, but if you have -// // tiles whose images extend out of the tile, you'll need this. -// -// ADDITIONAL CONFIGURATION -// -// The following symbols set static limits which determine how much -// memory will be allocated for the editor. You can override them -// by making similiar definitions, but memory usage will increase. -// -// #define STBTE_MAX_TILEMAP_X 200 // max 4096 -// #define STBTE_MAX_TILEMAP_Y 200 // max 4096 -// #define STBTE_MAX_LAYERS 8 // max 32 -// #define STBTE_MAX_CATEGORIES 100 -// #define STBTE_UNDO_BUFFER_BYTES (1 << 24) // 16 MB -// #define STBTE_MAX_COPY 90000 // e.g. 300x300 -// #define STBTE_MAX_PROPERTIES 10 // max properties per tile -// -// API -// -// Further documentation appears in the header-file section below. -// -// EDITING MULTIPLE LEVELS -// -// You can only have one active editor instance. To switch between multiple -// levels, you can either store the levels in your own format and copy them -// in and out of the editor format, or you can create multiple stbte_tilemap -// objects and switch between them. The latter has the advantage that each -// stbte_tilemap keeps its own undo state. (The clipboard is global, so -// either approach allows cut&pasting between levels.) -// -// REVISION HISTORY -// 0.37 fix warning -// 0.36 minor compiler support -// 0.35 layername button changes -// - layername buttons grow with the layer panel -// - fix stbte_create_map being declared as stbte_create -// - fix declaration of stbte_create_map -// 0.30 properties release -// - properties panel for editing user-defined "object" properties -// - can link each tile to one other tile -// - keyboard interface -// - fix eraser tool bug (worked in complex cases, failed in simple) -// - undo/redo tools have visible disabled state -// - tiles on higher layers draw on top of adjacent lower-layer tiles -// 0.20 erasable release -// - eraser tool -// - fix bug when pasting into protected layer -// - better color scheme -// - internal-use color picker -// 0.10 initial release -// -// TODO -// -// Separate scroll state for each category -// Implement paint bucket -// Support STBTE_HITTEST_TILE above -// ?Cancel drags by clicking other button? - may be fixed -// Finish support for toolbar at side -// -// CREDITS -// -// -// Main editor & features -// Sean Barrett -// Additional features: -// Josh Huelsman -// Bugfixes: -// Ryan Whitworth -// Eugene Opalev -// -// LICENSE -// -// This software is dual-licensed to the public domain and under the following -// license: you are granted a perpetual, irrevocable license to copy, modify, -// publish, and distribute this file as you see fit. - - - -/////////////////////////////////////////////////////////////////////// -// -// HEADER SECTION - -#ifndef STB_TILEMAP_INCLUDE_STB_TILEMAP_EDITOR_H -#define STB_TILEMAP_INCLUDE_STB_TILEMAP_EDITOR_H - -#ifdef _WIN32 - #ifndef _CRT_SECURE_NO_WARNINGS - #define _CRT_SECURE_NO_WARNINGS - #endif - #include - #include -#endif - -typedef struct stbte_tilemap stbte_tilemap; - -// these are the drawmodes used in STBTE_DRAW_TILE -enum -{ - STBTE_drawmode_deemphasize = -1, - STBTE_drawmode_normal = 0, - STBTE_drawmode_emphasize = 1, -}; - -// these are the property types -#define STBTE_PROP_none 0 -#define STBTE_PROP_int 1 -#define STBTE_PROP_float 2 -#define STBTE_PROP_bool 3 -#define STBTE_PROP_disabled 4 - -//////// -// -// creation -// - -extern stbte_tilemap *stbte_create_map(int map_x, int map_y, int map_layers, int spacing_x, int spacing_y, int max_tiles); -// create an editable tilemap -// map_x : dimensions of map horizontally (user can change this in editor), <= STBTE_MAX_TILEMAP_X -// map_y : dimensions of map vertically (user can change this in editor) <= STBTE_MAX_TILEMAP_Y -// map_layers : number of layers to use (fixed), <= STBTE_MAX_LAYERS -// spacing_x : initial horizontal distance between left edges of map tiles in stb_tilemap_editor pixels -// spacing_y : initial vertical distance between top edges of map tiles in stb_tilemap_editor pixels -// max_tiles : maximum number of tiles that can defined -// -// If insufficient memory, returns NULL - -extern void stbte_define_tile(stbte_tilemap *tm, unsigned short id, unsigned int layermask, const char * category); -// call this repeatedly for each tile to install the tile definitions into the editable tilemap -// tm : tilemap created by stbte_create_map -// id : unique identifier for each tile, 0 <= id < 32768 -// layermask : bitmask of which layers tile is allowed on: 1 = layer 0, 255 = layers 0..7 -// (note that onscreen, the editor numbers the layers from 1 not 0) -// layer 0 is the furthest back, layer 1 is just in front of layer 0, etc -// category : which category this tile is grouped in - -extern void stbte_set_display(int x0, int y0, int x1, int y1); -// call this once to set the size; if you resize, call it again - - -///////// -// -// every frame -// - -extern void stbte_draw(stbte_tilemap *tm); - -extern void stbte_tick(stbte_tilemap *tm, float time_in_seconds_since_last_frame); - -//////////// -// -// user input -// - -// if you're using SDL, call the next function for SDL_MOUSEMOVE, SDL_MOUSEBUTTON, SDL_MOUSEWHEEL; -// the transformation lets you scale from SDL mouse coords to stb_tilemap_editor coords -extern void stbte_mouse_sdl(stbte_tilemap *tm, const void *sdl_event, float xscale, float yscale, int xoffset, int yoffset); - -// otherwise, hook these up explicitly: -extern void stbte_mouse_move(stbte_tilemap *tm, int x, int y, int shifted, int scrollkey); -extern void stbte_mouse_button(stbte_tilemap *tm, int x, int y, int right, int down, int shifted, int scrollkey); -extern void stbte_mouse_wheel(stbte_tilemap *tm, int x, int y, int vscroll); - -// for keyboard, define your own mapping from keys to the following actions. -// this is totally optional, as all features are accessible with the mouse -enum stbte_action -{ - STBTE_tool_select, - STBTE_tool_brush, - STBTE_tool_erase, - STBTE_tool_rectangle, - STBTE_tool_eyedropper, - STBTE_tool_link, - STBTE_act_toggle_grid, - STBTE_act_toggle_links, - STBTE_act_undo, - STBTE_act_redo, - STBTE_act_cut, - STBTE_act_copy, - STBTE_act_paste, - STBTE_scroll_left, - STBTE_scroll_right, - STBTE_scroll_up, - STBTE_scroll_down, -}; -extern void stbte_action(stbte_tilemap *tm, enum stbte_action act); - -//////////////// -// -// save/load -// -// There is no editor file format. You have to save and load the data yourself -// through the following functions. You can also use these functions to get the -// data to generate game-formatted levels directly. (But make sure you save -// first! You may also want to autosave to a temp file periodically, etc etc.) - -#define STBTE_EMPTY -1 - -extern void stbte_get_dimensions(stbte_tilemap *tm, int *max_x, int *max_y); -// get the dimensions of the level, since the user can change them - -extern short* stbte_get_tile(stbte_tilemap *tm, int x, int y); -// returns an array of shorts that is 'map_layers' in length. each short is -// either one of the tile_id values from define_tile, or STBTE_EMPTY. - -extern float *stbte_get_properties(stbte_tilemap *tm, int x, int y); -// get the property array associated with the tile at x,y. this is an -// array of floats that is STBTE_MAX_PROPERTIES in length; you have to -// interpret the slots according to the semantics you've chosen - -extern void stbte_get_link(stbte_tilemap *tm, int x, int y, int *destx, int *desty); -// gets the link associated with the tile at x,y. - -extern void stbte_set_dimensions(stbte_tilemap *tm, int max_x, int max_y); -// set the dimensions of the level, overrides previous stbte_create_map() -// values or anything the user has changed - -extern void stbte_clear_map(stbte_tilemap *tm); -// clears the map, including the region outside the defined region, so if the -// user expands the map, they won't see garbage there - -extern void stbte_set_tile(stbte_tilemap *tm, int x, int y, int layer, signed short tile); -// tile is your tile_id from define_tile, or STBTE_EMPTY - -extern void stbte_set_property(stbte_tilemap *tm, int x, int y, int n, float val); -// set the value of the n'th slot of the tile at x,y - -extern void stbte_set_link(stbte_tilemap *tm, int x, int y, int destx, int desty); -// set a link going from x,y to destx,desty. to force no link, -// use destx=desty=-1 - -//////// -// -// optional -// - -extern void stbte_set_background_tile(stbte_tilemap *tm, short id); -// selects the tile to fill the bottom layer with and used to clear bottom tiles to; -// should be same ID as - -extern void stbte_set_sidewidths(int left, int right); -// call this once to set the left & right side widths. don't call -// it again since the user can change it - -extern void stbte_set_spacing(stbte_tilemap *tm, int spacing_x, int spacing_y, int palette_spacing_x, int palette_spacing_y); -// call this to set the spacing of map tiles and the spacing of palette tiles. -// if you rescale your display, call it again (e.g. you can implement map zooming yourself) - -extern void stbte_set_layername(stbte_tilemap *tm, int layer, const char *layername); -// sets a string name for your layer that shows in the layer selector. note that this -// makes the layer selector wider. 'layer' is from 0..(map_layers-1) - -#endif - -#ifdef STB_TILEMAP_EDITOR_IMPLEMENTATION - -#ifndef STBTE_ASSERT -#define STBTE_ASSERT assert -#include -#endif - -#ifdef _MSC_VER -#define STBTE__NOTUSED(v) (void)(v) -#else -#define STBTE__NOTUSED(v) (void)sizeof(v) -#endif - -#ifndef STBTE_MAX_TILEMAP_X -#define STBTE_MAX_TILEMAP_X 200 -#endif - -#ifndef STBTE_MAX_TILEMAP_Y -#define STBTE_MAX_TILEMAP_Y 200 -#endif - -#ifndef STBTE_MAX_LAYERS -#define STBTE_MAX_LAYERS 8 -#endif - -#ifndef STBTE_MAX_CATEGORIES -#define STBTE_MAX_CATEGORIES 100 -#endif - -#ifndef STBTE_MAX_COPY -#define STBTE_MAX_COPY 65536 -#endif - -#ifndef STBTE_UNDO_BUFFER_BYTES -#define STBTE_UNDO_BUFFER_BYTES (1 << 24) // 16 MB -#endif - -#ifndef STBTE_PROP_TYPE -#define STBTE__NO_PROPS -#define STBTE_PROP_TYPE(n,td,tp) 0 -#endif - -#ifndef STBTE_PROP_NAME -#define STBTE_PROP_NAME(n,td,tp) "" -#endif - -#ifndef STBTE_MAX_PROPERTIES -#define STBTE_MAX_PROPERTIES 10 -#endif - -#ifndef STBTE_PROP_MIN -#define STBTE_PROP_MIN(n,td,tp) 0 -#endif - -#ifndef STBTE_PROP_MAX -#define STBTE_PROP_MAX(n,td,tp) 100.0 -#endif - -#ifndef STBTE_PROP_FLOAT_SCALE -#define STBTE_PROP_FLOAT_SCALE(n,td,tp) 1 // default scale size -#endif - -#ifndef STBTE_FLOAT_CONTROL_GRANULARITY -#define STBTE_FLOAT_CONTROL_GRANULARITY 4 -#endif - - -#define STBTE__UNDO_BUFFER_COUNT (STBTE_UNDO_BUFFER_BYTES>>1) - -#if STBTE_MAX_TILEMAP_X > 4096 || STBTE_MAX_TILEMAP_Y > 4096 -#error "Maximum editable map size is 4096 x 4096" -#endif -#if STBTE_MAX_LAYERS > 32 -#error "Maximum layers allowed is 32" -#endif -#if STBTE_UNDO_BUFFER_COUNT & (STBTE_UNDO_BUFFER_COUNT-1) -#error "Undo buffer size must be a power of 2" -#endif - -#if STBTE_MAX_PROPERTIES == 0 -#define STBTE__NO_PROPS -#endif - -#ifdef STBTE__NO_PROPS -#undef STBTE_MAX_PROPERTIES -#define STBTE_MAX_PROPERTIES 1 // so we can declare arrays -#endif - -typedef struct -{ - short x,y; -} stbte__link; - -enum -{ - STBTE__base, - STBTE__outline, - STBTE__text, - - STBTE__num_color_aspects, -}; - -enum -{ - STBTE__idle, - STBTE__over, - STBTE__down, - STBTE__over_down, - STBTE__selected, - STBTE__selected_over, - STBTE__disabled, - STBTE__num_color_states, -}; - -enum -{ - STBTE__cexpander, - STBTE__ctoolbar, - STBTE__ctoolbar_button, - STBTE__cpanel, - STBTE__cpanel_sider, - STBTE__cpanel_sizer, - STBTE__cscrollbar, - STBTE__cmapsize, - STBTE__clayer_button, - STBTE__clayer_hide, - STBTE__clayer_lock, - STBTE__clayer_solo, - STBTE__ccategory_button, - - STBTE__num_color_modes, -}; - -#ifdef STBTE__COLORPICKER -static char *stbte__color_names[] = -{ - "expander", "toolbar", "tool button", "panel", - "panel c1", "panel c2", "scollbar", "map button", - "layer", "hide", "lock", "solo", - "category", -}; -#endif // STBTE__COLORPICKER - - // idle, over, down, over&down, selected, sel&over, disabled -static int stbte__color_table[STBTE__num_color_modes][STBTE__num_color_aspects][STBTE__num_color_states] = -{ - { - { 0x000000, 0x84987c, 0xdcdca8, 0xdcdca8, 0x40c040, 0x60d060, 0x505050, }, - { 0xa4b090, 0xe0ec80, 0xffffc0, 0xffffc0, 0x80ff80, 0x80ff80, 0x606060, }, - { 0xffffff, 0xffffff, 0xffffff, 0xffffff, 0xffffff, 0xffffff, 0x909090, }, - }, { - { 0x808890, 0x606060, 0x606060, 0x606060, 0x606060, 0x606060, 0x606060, }, - { 0x605860, 0x606060, 0x606060, 0x606060, 0x606060, 0x606060, 0x606060, }, - { 0x000000, 0x000000, 0x000000, 0x000000, 0x000000, 0x000000, 0x000000, }, - }, { - { 0x3c5068, 0x7088a8, 0x647488, 0x94b4dc, 0x8890c4, 0x9caccc, 0x404040, }, - { 0x889cb8, 0x889cb8, 0x889cb8, 0x889cb8, 0x84c4e8, 0xacc8ff, 0x0c0c08, }, - { 0xbcc4cc, 0xffffff, 0xffffff, 0xffffff, 0xffffff, 0xffffff, 0x707074, }, - }, { - { 0x403848, 0x403010, 0x403010, 0x403010, 0x403010, 0x403010, 0x303024, }, - { 0x68546c, 0xc08040, 0xc08040, 0xc08040, 0xc08040, 0xc08040, 0x605030, }, - { 0xf4e4ff, 0xffffff, 0xffffff, 0xffffff, 0xffffff, 0xffffff, 0x909090, }, - }, { - { 0xb4b04c, 0xacac60, 0xc0ffc0, 0xc0ffc0, 0x40c040, 0x60d060, 0x505050, }, - { 0xa0a04c, 0xd0d04c, 0xffff80, 0xffff80, 0x80ff80, 0x80ff80, 0x606060, }, - { 0xffffff, 0xffffff, 0xffffff, 0xffffff, 0xffffff, 0xffffff, 0x909090, }, - }, { - { 0x40c440, 0x60d060, 0xc0ffc0, 0xc0ffc0, 0x40c040, 0x60d060, 0x505050, }, - { 0x40c040, 0x80ff80, 0x80ff80, 0x80ff80, 0x80ff80, 0x80ff80, 0x606060, }, - { 0xffffff, 0xffffff, 0xffffff, 0xffffff, 0xffffff, 0xffffff, 0x909090, }, - }, { - { 0x9090ac, 0xa0a0b8, 0xbcb8cc, 0xbcb8cc, 0x909040, 0x909040, 0x909040, }, - { 0xa0a0b8, 0xb0b4d0, 0xa0a0b8, 0xa0a0b8, 0xa0a050, 0xa0a050, 0xa0a050, }, - { 0x808088, 0x808030, 0x808030, 0x808030, 0x808030, 0x808030, 0x808030, }, - }, { - { 0x704c70, 0x885c8c, 0x9c68a4, 0xb870bc, 0xb490bc, 0xb490bc, 0x302828, }, - { 0x646064, 0xcca8d4, 0xc060c0, 0xa07898, 0xe0b8e0, 0xe0b8e0, 0x403838, }, - { 0xdccce4, 0xffffff, 0xffffff, 0xffffff, 0xffffff, 0xffffff, 0x909090, }, - }, { - { 0x704c70, 0x885c8c, 0x9c68a4, 0xb870bc, 0xb490bc, 0xb490bc, 0x302828, }, - { 0xb09cb4, 0xcca8d4, 0xc060c0, 0xa07898, 0xe0b8e0, 0xe0b8e0, 0x403838, }, - { 0xdccce4, 0xffffff, 0xffffff, 0xffffff, 0xffffff, 0xffffff, 0x909090, }, - }, { - { 0x646494, 0x888cb8, 0xb0b0b0, 0xb0b0cc, 0x9c9cf4, 0x8888b0, 0x50506c, }, - { 0x9090a4, 0xb0b4d4, 0xb0b0dc, 0xb0b0cc, 0xd0d0fc, 0xd0d4f0, 0x606060, }, - { 0xb4b4d4, 0xe4e4ff, 0xffffff, 0xffffff, 0xe0e4ff, 0xececff, 0x909090, }, - }, { - { 0x646444, 0x888c64, 0xb0b0b0, 0xb0b088, 0xaca858, 0x88886c, 0x505050, }, - { 0x88886c, 0xb0b490, 0xb0b0b0, 0xb0b088, 0xd8d898, 0xd0d4b0, 0x606060, }, - { 0xb4b49c, 0xffffd8, 0xffffff, 0xffffd4, 0xffffdc, 0xffffcc, 0x909090, }, - }, { - { 0x906464, 0xb48c8c, 0xd4b0b0, 0xdcb0b0, 0xff9c9c, 0xc88888, 0x505050, }, - { 0xb47c80, 0xd4b4b8, 0xc4a8a8, 0xdcb0b0, 0xffc0c0, 0xfce8ec, 0x606060, }, - { 0xe0b4b4, 0xffdcd8, 0xffd8d4, 0xffe0e4, 0xffece8, 0xffffff, 0x909090, }, - }, { - { 0x403848, 0x403848, 0x403848, 0x886894, 0x7c80c8, 0x7c80c8, 0x302828, }, - { 0x403848, 0x403848, 0x403848, 0x403848, 0x7c80c8, 0x7c80c8, 0x403838, }, - { 0xc8c4c8, 0xffffff, 0xffffff, 0xffffff, 0xe8e8ec, 0xffffff, 0x909090, }, - }, -}; - -#define STBTE_COLOR_TILEMAP_BACKGROUND 0x000000 -#define STBTE_COLOR_TILEMAP_BORDER 0x203060 -#define STBTE_COLOR_TILEMAP_HIGHLIGHT 0xffffff -#define STBTE_COLOR_GRID 0x404040 -#define STBTE_COLOR_SELECTION_OUTLINE1 0xdfdfdf -#define STBTE_COLOR_SELECTION_OUTLINE2 0x303030 -#define STBTE_COLOR_TILEPALETTE_OUTLINE 0xffffff -#define STBTE_COLOR_TILEPALETTE_BACKGROUND 0x000000 - -#ifndef STBTE_LINK_COLOR -#define STBTE_LINK_COLOR(src,sp,dest,dp) 0x5030ff -#endif - -#ifndef STBTE_LINK_COLOR_DRAWING -#define STBTE_LINK_COLOR_DRAWING 0xff40ff -#endif - -#ifndef STBTE_LINK_COLOR_DISALLOWED -#define STBTE_LINK_COLOR_DISALLOWED 0x602060 -#endif - - -// disabled, selected, down, over -static unsigned char stbte__state_to_index[2][2][2][2] = -{ - { - { { STBTE__idle , STBTE__over }, { STBTE__down , STBTE__over_down }, }, - { { STBTE__selected, STBTE__selected_over }, { STBTE__down , STBTE__over_down }, }, - },{ - { { STBTE__disabled, STBTE__disabled }, { STBTE__disabled, STBTE__disabled }, }, - { { STBTE__selected, STBTE__selected_over }, { STBTE__disabled, STBTE__disabled }, }, - } -}; -#define STBTE__INDEX_FOR_STATE(disable,select,down,over) stbte__state_to_index[disable][select][down][over] -#define STBTE__INDEX_FOR_ID(id,disable,select) STBTE__INDEX_FOR_STATE(disable,select,STBTE__IS_ACTIVE(id),STBTE__IS_HOT(id)) - -#define STBTE__FONT_HEIGHT 9 -static short stbte__font_offset[95+16]; -static short stbte__fontdata[769] = -{ - 4,9,6,9,9,9,9,8,9,8,4,9,7,7,7,7,4,2,6,8,6,6,7,3,4,4,8,6,3,6,2,6,6,6,6,6,6, - 6,6,6,6,6,2,3,5,4,5,6,6,6,6,6,6,6,6,6,6,6,6,7,6,7,7,7,6,7,6,6,6,6,7,7,6,6, - 6,4,6,4,7,7,3,6,6,5,6,6,5,6,6,4,5,6,4,7,6,6,6,6,6,6,6,6,6,7,6,6,6,5,2,5,8, - 0,0,0,0,2,253,130,456,156,8,72,184,64,2,125,66,64,160,64,146,511,146,146, - 511,146,146,511,146,511,257,341,297,341,297,341,257,511,16,56,124,16,16,16, - 124,56,16,96,144,270,261,262,136,80,48,224,192,160,80,40,22,14,15,3,448,496, - 496,240,232,20,10,5,2,112,232,452,450,225,113,58,28,63,30,60,200,455,257, - 257,0,0,0,257,257,455,120,204,132,132,159,14,4,4,14,159,132,132,204,120,8, - 24,56,120,56,24,8,32,48,56,60,56,48,32,0,0,0,0,111,111,7,7,0,0,7,7,34,127, - 127,34,34,127,127,34,36,46,107,107,58,18,99,51,24,12,102,99,48,122,79,93, - 55,114,80,4,7,3,62,127,99,65,65,99,127,62,8,42,62,28,28,62,42,8,8,8,62,62, - 8,8,128,224,96,8,8,8,8,8,8,96,96,96,48,24,12,6,3,62,127,89,77,127,62,64,66, - 127,127,64,64,98,115,89,77,71,66,33,97,73,93,119,35,24,28,22,127,127,16,39, - 103,69,69,125,57,62,127,73,73,121,48,1,1,113,121,15,7,54,127,73,73,127,54, - 6,79,73,105,63,30,54,54,128,246,118,8,28,54,99,65,20,20,20,20,65,99,54,28, - 8,2,3,105,109,7,2,30,63,33,45,47,46,124,126,19,19,126,124,127,127,73,73,127, - 54,62,127,65,65,99,34,127,127,65,99,62,28,127,127,73,73,73,65,127,127,9,9, - 9,1,62,127,65,73,121,121,127,127,8,8,127,127,65,65,127,127,65,65,32,96,64, - 64,127,63,127,127,8,28,54,99,65,127,127,64,64,64,64,127,127,6,12,6,127,127, - 127,127,6,12,24,127,127,62,127,65,65,65,127,62,127,127,9,9,15,6,62,127,65, - 81,49,127,94,127,127,9,25,127,102,70,79,73,73,121,49,1,1,127,127,1,1,63,127, - 64,64,127,63,15,31,48,96,48,31,15,127,127,48,24,48,127,127,99,119,28,28,119, - 99,7,15,120,120,15,7,97,113,89,77,71,67,127,127,65,65,3,6,12,24,48,96,65, - 65,127,127,8,12,6,3,6,12,8,64,64,64,64,64,64,64,3,7,4,32,116,84,84,124,120, - 127,127,68,68,124,56,56,124,68,68,68,56,124,68,68,127,127,56,124,84,84,92, - 24,8,124,126,10,10,56,380,324,324,508,252,127,127,4,4,124,120,72,122,122, - 64,256,256,256,506,250,126,126,16,56,104,64,66,126,126,64,124,124,24,56,28, - 124,120,124,124,4,4,124,120,56,124,68,68,124,56,508,508,68,68,124,56,56,124, - 68,68,508,508,124,124,4,4,12,8,72,92,84,84,116,36,4,4,62,126,68,68,60,124, - 64,64,124,124,28,60,96,96,60,28,28,124,112,56,112,124,28,68,108,56,56,108, - 68,284,316,352,320,508,252,68,100,116,92,76,68,8,62,119,65,65,127,127,65, - 65,119,62,8,16,24,12,12,24,24,12,4, -}; - -typedef struct -{ - short id; - unsigned short category_id; - char *category; - unsigned int layermask; -} stbte__tileinfo; - -#define MAX_LAYERMASK (1 << (8*sizeof(unsigned int))) - -typedef short stbte__tiledata; - -#define STBTE__NO_TILE -1 - -enum -{ - STBTE__panel_toolbar, - STBTE__panel_colorpick, - STBTE__panel_info, - STBTE__panel_layers, - STBTE__panel_props, - STBTE__panel_categories, - STBTE__panel_tiles, - - STBTE__num_panel, -}; - -enum -{ - STBTE__side_left, - STBTE__side_right, - STBTE__side_top, - STBTE__side_bottom, -}; - -enum -{ - STBTE__tool_select, - STBTE__tool_brush, - STBTE__tool_erase, - STBTE__tool_rect, - STBTE__tool_eyedrop, - STBTE__tool_fill, - STBTE__tool_link, - - STBTE__tool_showgrid, - STBTE__tool_showlinks, - - STBTE__tool_undo, - STBTE__tool_redo, - // copy/cut/paste aren't included here because they're displayed differently - - STBTE__num_tool, -}; - -// icons are stored in the 0-31 range of ASCII in the font -static int toolchar[] = { 26,24,25,20,23,22,18, 19,17, 29,28, }; - -enum -{ - STBTE__propmode_default, - STBTE__propmode_always, - STBTE__propmode_never, -}; - -enum -{ - STBTE__paint, - - // from here down does hittesting - STBTE__tick, - STBTE__mousemove, - STBTE__mousewheel, - STBTE__leftdown, - STBTE__leftup, - STBTE__rightdown, - STBTE__rightup, -}; - -typedef struct -{ - int expanded, mode; - int delta_height; // number of rows they've requested for this - int side; - int width,height; - int x0,y0; -} stbte__panel; - -typedef struct -{ - int x0,y0,x1,y1,color; -} stbte__colorrect; - -#define STBTE__MAX_DELAYRECT 256 - -typedef struct -{ - int tool, active_event; - int active_id, hot_id, next_hot_id; - int event; - int mx,my, dx,dy; - int ms_time; - int shift, scrollkey; - int initted; - int side_extended[2]; - stbte__colorrect delayrect[STBTE__MAX_DELAYRECT]; - int delaycount; - int show_grid, show_links; - int brush_state; // used to decide which kind of erasing - int eyedrop_x, eyedrop_y, eyedrop_last_layer; - int pasting, paste_x, paste_y; - int scrolling, start_x, start_y; - int last_mouse_x, last_mouse_y; - int accum_x, accum_y; - int linking; - int dragging; - int drag_x, drag_y, drag_w, drag_h; - int drag_offx, drag_offy, drag_dest_x, drag_dest_y; - int undoing; - int has_selection, select_x0, select_y0, select_x1, select_y1; - int sx,sy; - int x0,y0,x1,y1, left_width, right_width; // configurable widths - float alert_timer; - const char *alert_msg; - float dt; - stbte__panel panel[STBTE__num_panel]; - short copybuffer[STBTE_MAX_COPY][STBTE_MAX_LAYERS]; - float copyprops[STBTE_MAX_COPY][STBTE_MAX_PROPERTIES]; -#ifdef STBTE_ALLOW_LINK - stbte__link copylinks[STBTE_MAX_COPY]; -#endif - int copy_src_x, copy_src_y; - stbte_tilemap *copy_src; - int copy_width,copy_height,has_copy,copy_has_props; -} stbte__ui_t; - -// there's only one UI system at a time, so we can globalize this -static stbte__ui_t stbte__ui = { STBTE__tool_brush, 0 }; - -#define STBTE__INACTIVE() (stbte__ui.active_id == 0) -#define STBTE__IS_ACTIVE(id) (stbte__ui.active_id == (id)) -#define STBTE__IS_HOT(id) (stbte__ui.hot_id == (id)) - -#define STBTE__BUTTON_HEIGHT (STBTE__FONT_HEIGHT + 2 * STBTE__BUTTON_INTERNAL_SPACING) -#define STBTE__BUTTON_INTERNAL_SPACING (2 + (STBTE__FONT_HEIGHT>>4)) - -typedef struct -{ - const char *name; - int locked; - int hidden; -} stbte__layer; - -enum -{ - STBTE__unlocked, - STBTE__protected, - STBTE__locked, -}; - -struct stbte_tilemap -{ - stbte__tiledata data[STBTE_MAX_TILEMAP_Y][STBTE_MAX_TILEMAP_X][STBTE_MAX_LAYERS]; - float props[STBTE_MAX_TILEMAP_Y][STBTE_MAX_TILEMAP_X][STBTE_MAX_PROPERTIES]; - #ifdef STBTE_ALLOW_LINK - stbte__link link[STBTE_MAX_TILEMAP_Y][STBTE_MAX_TILEMAP_X]; - int linkcount[STBTE_MAX_TILEMAP_Y][STBTE_MAX_TILEMAP_X]; - #endif - int max_x, max_y, num_layers; - int spacing_x, spacing_y; - int palette_spacing_x, palette_spacing_y; - int scroll_x,scroll_y; - int cur_category, cur_tile, cur_layer; - char *categories[STBTE_MAX_CATEGORIES]; - int num_categories, category_scroll; - stbte__tileinfo *tiles; - int num_tiles, max_tiles, digits; - unsigned char undo_available_valid; - unsigned char undo_available; - unsigned char redo_available; - unsigned char padding; - int cur_palette_count; - int palette_scroll; - int tileinfo_dirty; - stbte__layer layerinfo[STBTE_MAX_LAYERS]; - int has_layer_names; - int layername_width; - int layer_scroll; - int propmode; - int solo_layer; - int undo_pos, undo_len, redo_len; - short background_tile; - unsigned char id_in_use[32768>>3]; - short *undo_buffer; -}; - -static char *default_category = "[unassigned]"; - -static void stbte__init_gui(void) -{ - int i,n; - stbte__ui.initted = 1; - // init UI state - stbte__ui.show_links = 1; - for (i=0; i < STBTE__num_panel; ++i) { - stbte__ui.panel[i].expanded = 1; // visible if not autohidden - stbte__ui.panel[i].delta_height = 0; - stbte__ui.panel[i].side = STBTE__side_left; - } - stbte__ui.panel[STBTE__panel_toolbar ].side = STBTE__side_top; - stbte__ui.panel[STBTE__panel_colorpick].side = STBTE__side_right; - - if (stbte__ui.left_width == 0) - stbte__ui.left_width = 80; - if (stbte__ui.right_width == 0) - stbte__ui.right_width = 80; - - // init font - n=95+16; - for (i=0; i < 95+16; ++i) { - stbte__font_offset[i] = n; - n += stbte__fontdata[i]; - } -} - -stbte_tilemap *stbte_create_map(int map_x, int map_y, int map_layers, int spacing_x, int spacing_y, int max_tiles) -{ - int i; - stbte_tilemap *tm; - STBTE_ASSERT(map_layers >= 0 && map_layers <= STBTE_MAX_LAYERS); - STBTE_ASSERT(map_x >= 0 && map_x <= STBTE_MAX_TILEMAP_X); - STBTE_ASSERT(map_y >= 0 && map_y <= STBTE_MAX_TILEMAP_Y); - if (map_x < 0 || map_y < 0 || map_layers < 0 || - map_x > STBTE_MAX_TILEMAP_X || map_y > STBTE_MAX_TILEMAP_Y || map_layers > STBTE_MAX_LAYERS) - return NULL; - - if (!stbte__ui.initted) - stbte__init_gui(); - - tm = (stbte_tilemap *) malloc(sizeof(*tm) + sizeof(*tm->tiles) * max_tiles + STBTE_UNDO_BUFFER_BYTES); - if (tm == NULL) - return NULL; - - tm->tiles = (stbte__tileinfo *) (tm+1); - tm->undo_buffer = (short *) (tm->tiles + max_tiles); - tm->num_layers = map_layers; - tm->max_x = map_x; - tm->max_y = map_y; - tm->spacing_x = spacing_x; - tm->spacing_y = spacing_y; - tm->scroll_x = 0; - tm->scroll_y = 0; - tm->palette_scroll = 0; - tm->palette_spacing_x = spacing_x+1; - tm->palette_spacing_y = spacing_y+1; - tm->cur_category = -1; - tm->cur_tile = 0; - tm->solo_layer = -1; - tm->undo_len = 0; - tm->redo_len = 0; - tm->undo_pos = 0; - tm->category_scroll = 0; - tm->layer_scroll = 0; - tm->propmode = 0; - tm->has_layer_names = 0; - tm->layername_width = 0; - tm->undo_available_valid = 0; - - for (i=0; i < tm->num_layers; ++i) { - tm->layerinfo[i].hidden = 0; - tm->layerinfo[i].locked = STBTE__unlocked; - tm->layerinfo[i].name = 0; - } - - tm->background_tile = STBTE__NO_TILE; - stbte_clear_map(tm); - - tm->max_tiles = max_tiles; - tm->num_tiles = 0; - for (i=0; i < 32768/8; ++i) - tm->id_in_use[i] = 0; - tm->tileinfo_dirty = 1; - return tm; -} - -void stbte_set_background_tile(stbte_tilemap *tm, short id) -{ - int i; - STBTE_ASSERT(id >= -1 && id < 32768); - if (id >= 32768 || id < -1) - return; - for (i=0; i < STBTE_MAX_TILEMAP_X * STBTE_MAX_TILEMAP_Y; ++i) - if (tm->data[0][i][0] == -1) - tm->data[0][i][0] = id; - tm->background_tile = id; -} - -void stbte_set_spacing(stbte_tilemap *tm, int spacing_x, int spacing_y, int palette_spacing_x, int palette_spacing_y) -{ - tm->spacing_x = spacing_x; - tm->spacing_y = spacing_y; - tm->palette_spacing_x = palette_spacing_x; - tm->palette_spacing_y = palette_spacing_y; -} - -void stbte_set_sidewidths(int left, int right) -{ - stbte__ui.left_width = left; - stbte__ui.right_width = right; -} - -void stbte_set_display(int x0, int y0, int x1, int y1) -{ - stbte__ui.x0 = x0; - stbte__ui.y0 = y0; - stbte__ui.x1 = x1; - stbte__ui.y1 = y1; -} - -void stbte_define_tile(stbte_tilemap *tm, unsigned short id, unsigned int layermask, const char * category_c) -{ - char *category = (char *) category_c; - STBTE_ASSERT(id < 32768); - STBTE_ASSERT(tm->num_tiles < tm->max_tiles); - STBTE_ASSERT((tm->id_in_use[id>>3]&(1<<(id&7))) == 0); - if (id >= 32768 || tm->num_tiles >= tm->max_tiles || (tm->id_in_use[id>>3]&(1<<(id&7)))) - return; - - if (category == NULL) - category = (char*) default_category; - tm->id_in_use[id>>3] |= 1 << (id&7); - tm->tiles[tm->num_tiles].category = category; - tm->tiles[tm->num_tiles].id = id; - tm->tiles[tm->num_tiles].layermask = layermask; - ++tm->num_tiles; - tm->tileinfo_dirty = 1; -} - -static int stbte__text_width(const char *str); - -void stbte_set_layername(stbte_tilemap *tm, int layer, const char *layername) -{ - STBTE_ASSERT(layer >= 0 && layer < tm->num_layers); - if (layer >= 0 && layer < tm->num_layers) { - int width; - tm->layerinfo[layer].name = layername; - tm->has_layer_names = 1; - width = stbte__text_width(layername); - tm->layername_width = (width > tm->layername_width ? width : tm->layername_width); - } -} - -void stbte_get_dimensions(stbte_tilemap *tm, int *max_x, int *max_y) -{ - *max_x = tm->max_x; - *max_y = tm->max_y; -} - -short* stbte_get_tile(stbte_tilemap *tm, int x, int y) -{ - STBTE_ASSERT(x >= 0 && x < tm->max_x && y >= 0 && y < tm->max_y); - if (x < 0 || x >= STBTE_MAX_TILEMAP_X || y < 0 || y >= STBTE_MAX_TILEMAP_Y) - return NULL; - return tm->data[y][x]; -} - -float *stbte_get_properties(stbte_tilemap *tm, int x, int y) -{ - STBTE_ASSERT(x >= 0 && x < tm->max_x && y >= 0 && y < tm->max_y); - if (x < 0 || x >= STBTE_MAX_TILEMAP_X || y < 0 || y >= STBTE_MAX_TILEMAP_Y) - return NULL; - return tm->props[y][x]; -} - -void stbte_get_link(stbte_tilemap *tm, int x, int y, int *destx, int *desty) -{ - int gx=-1,gy=-1; - STBTE_ASSERT(x >= 0 && x < tm->max_x && y >= 0 && y < tm->max_y); -#ifdef STBTE_ALLOW_LINK - if (x >= 0 && x < STBTE_MAX_TILEMAP_X && y >= 0 && y < STBTE_MAX_TILEMAP_Y) { - gx = tm->link[y][x].x; - gy = tm->link[y][x].y; - if (gx >= 0) - if (!STBTE_ALLOW_LINK(tm->data[y][x], tm->props[y][x], tm->data[gy][gx], tm->props[gy][gx])) - gx = gy = -1; - } -#endif - *destx = gx; - *desty = gy; -} - -void stbte_set_property(stbte_tilemap *tm, int x, int y, int n, float val) -{ - tm->props[y][x][n] = val; -} - -static void stbte__set_link(stbte_tilemap *tm, int src_x, int src_y, int dest_x, int dest_y, int undo_mode); - -enum -{ - STBTE__undo_none, - STBTE__undo_record, - STBTE__undo_block, -}; - -void stbte_set_link(stbte_tilemap *tm, int x, int y, int destx, int desty) -{ -#ifdef STBTE_ALLOW_LINK - stbte__set_link(tm, x, y, destx, desty, STBTE__undo_none); -#else - STBTE_ASSERT(0); -#endif -} - - -// returns an array of map_layers shorts. each short is either -// one of the tile_id values from define_tile, or STBTE_EMPTY - -void stbte_set_dimensions(stbte_tilemap *tm, int map_x, int map_y) -{ - STBTE_ASSERT(map_x >= 0 && map_x <= STBTE_MAX_TILEMAP_X); - STBTE_ASSERT(map_y >= 0 && map_y <= STBTE_MAX_TILEMAP_Y); - if (map_x < 0 || map_y < 0 || map_x > STBTE_MAX_TILEMAP_X || map_y > STBTE_MAX_TILEMAP_Y) - return; - tm->max_x = map_x; - tm->max_y = map_y; -} - -void stbte_clear_map(stbte_tilemap *tm) -{ - int i,j; - for (i=0; i < STBTE_MAX_TILEMAP_X * STBTE_MAX_TILEMAP_Y; ++i) { - tm->data[0][i][0] = tm->background_tile; - for (j=1; j < tm->num_layers; ++j) - tm->data[0][i][j] = STBTE__NO_TILE; - for (j=0; j < STBTE_MAX_PROPERTIES; ++j) - tm->props[0][i][j] = 0; - #ifdef STBTE_ALLOW_LINK - tm->link[0][i].x = -1; - tm->link[0][i].y = -1; - tm->linkcount[0][i] = 0; - #endif - } -} - -void stbte_set_tile(stbte_tilemap *tm, int x, int y, int layer, signed short tile) -{ - STBTE_ASSERT(x >= 0 && x < tm->max_x && y >= 0 && y < tm->max_y); - STBTE_ASSERT(layer >= 0 && layer < tm->num_layers); - STBTE_ASSERT(tile >= -1 && tile < 32768); - if (x < 0 || x >= STBTE_MAX_TILEMAP_X || y < 0 || y >= STBTE_MAX_TILEMAP_Y) - return; - if (layer < 0 || layer >= tm->num_layers || tile < -1) - return; - tm->data[y][x][layer] = tile; -} - -static void stbte__choose_category(stbte_tilemap *tm, int category) -{ - int i,n=0; - tm->cur_category = category; - for (i=0; i < tm->num_tiles; ++i) - if (tm->tiles[i].category_id == category || category == -1) - ++n; - tm->cur_palette_count = n; - tm->palette_scroll = 0; -} - -static int stbte__strequal(char *p, char *q) -{ - while (*p) - if (*p++ != *q++) return 0; - return *q == 0; -} - -static void stbte__compute_tileinfo(stbte_tilemap *tm) -{ - int i,j,n=0; - - tm->num_categories=0; - - for (i=0; i < tm->num_tiles; ++i) { - stbte__tileinfo *t = &tm->tiles[i]; - // find category - for (j=0; j < tm->num_categories; ++j) - if (stbte__strequal(t->category, tm->categories[j])) - goto found; - tm->categories[j] = t->category; - ++tm->num_categories; - found: - t->category_id = (unsigned short) j; - } - - // currently number of categories can never decrease because you - // can't remove tile definitions, but let's get it right anyway - if (tm->cur_category > tm->num_categories) { - tm->cur_category = -1; - } - - stbte__choose_category(tm, tm->cur_category); - - tm->tileinfo_dirty = 0; -} - -static void stbte__prepare_tileinfo(stbte_tilemap *tm) -{ - if (tm->tileinfo_dirty) - stbte__compute_tileinfo(tm); -} - - -/////////////////////// undo system //////////////////////// - -// the undo system works by storing "commands" into a buffer, and -// then playing back those commands. undo and redo have to store -// the commands in different order. -// -// the commands are: -// -// 1) end_of_undo_record -// -1:short -// -// 2) end_of_redo_record -// -2:short -// -// 3) tile update -// tile_id:short (-1..32767) -// x_coord:short -// y_coord:short -// layer:short (0..31) -// -// 4) property update (also used for links) -// value_hi:short -// value_lo:short -// y_coord:short -// x_coord:short -// property:short (256+prop#) -// -// Since we use a circular buffer, we might overwrite the undo storage. -// To detect this, before playing back commands we scan back and see -// if we see an end_of_undo_record before hitting the relevant boundary, -// it's wholly contained. -// -// When we read back through, we see them in reverse order, so -// we'll see the layer number or property number first -// -// To be clearer about the circular buffer, there are two cases: -// 1. a single record is larger than the whole buffer. -// this is caught because the end_of_undo_record will -// get overwritten. -// 2. multiple records written are larger than the whole -// buffer, so some of them have been overwritten by -// the later ones. this is handled by explicitly tracking -// the undo length; we never try to parse the data that -// got overwritten - -// given two points, compute the length between them -#define stbte__wrap(pos) ((pos) & (STBTE__UNDO_BUFFER_COUNT-1)) - -#define STBTE__undo_record -2 -#define STBTE__redo_record -3 -#define STBTE__undo_junk -4 // this is written underneath the undo pointer, never used - -static void stbte__write_undo(stbte_tilemap *tm, short value) -{ - int pos = tm->undo_pos; - tm->undo_buffer[pos] = value; - tm->undo_pos = stbte__wrap(pos+1); - tm->undo_len += (tm->undo_len < STBTE__UNDO_BUFFER_COUNT-2); - tm->redo_len -= (tm->redo_len > 0); - tm->undo_available_valid = 0; -} - -static void stbte__write_redo(stbte_tilemap *tm, short value) -{ - int pos = tm->undo_pos; - tm->undo_buffer[pos] = value; - tm->undo_pos = stbte__wrap(pos-1); - tm->redo_len += (tm->redo_len < STBTE__UNDO_BUFFER_COUNT-2); - tm->undo_len -= (tm->undo_len > 0); - tm->undo_available_valid = 0; -} - -static void stbte__begin_undo(stbte_tilemap *tm) -{ - tm->redo_len = 0; - stbte__write_undo(tm, STBTE__undo_record); - stbte__ui.undoing = 1; - stbte__ui.alert_msg = 0; // clear alert if they start doing something -} - -static void stbte__end_undo(stbte_tilemap *tm) -{ - if (stbte__ui.undoing) { - // check if anything got written - int pos = stbte__wrap(tm->undo_pos-1); - if (tm->undo_buffer[pos] == STBTE__undo_record) { - // empty undo record, move back - tm->undo_pos = pos; - STBTE_ASSERT(tm->undo_len > 0); - tm->undo_len -= 1; - } - tm->undo_buffer[tm->undo_pos] = STBTE__undo_junk; - // otherwise do nothing - - stbte__ui.undoing = 0; - } -} - -static void stbte__undo_record(stbte_tilemap *tm, int x, int y, int i, int v) -{ - STBTE_ASSERT(stbte__ui.undoing); - if (stbte__ui.undoing) { - stbte__write_undo(tm, v); - stbte__write_undo(tm, x); - stbte__write_undo(tm, y); - stbte__write_undo(tm, i); - } -} - -static void stbte__redo_record(stbte_tilemap *tm, int x, int y, int i, int v) -{ - stbte__write_redo(tm, v); - stbte__write_redo(tm, x); - stbte__write_redo(tm, y); - stbte__write_redo(tm, i); -} - -static float stbte__extract_float(short s0, short s1) -{ - union { float f; short s[2]; } converter; - converter.s[0] = s0; - converter.s[1] = s1; - return converter.f; -} - -static short stbte__extract_short(float f, int slot) -{ - union { float f; short s[2]; } converter; - converter.f = f; - return converter.s[slot]; -} - -static void stbte__undo_record_prop(stbte_tilemap *tm, int x, int y, int i, short s0, short s1) -{ - STBTE_ASSERT(stbte__ui.undoing); - if (stbte__ui.undoing) { - stbte__write_undo(tm, s1); - stbte__write_undo(tm, s0); - stbte__write_undo(tm, x); - stbte__write_undo(tm, y); - stbte__write_undo(tm, 256+i); - } -} - -static void stbte__undo_record_prop_float(stbte_tilemap *tm, int x, int y, int i, float f) -{ - stbte__undo_record_prop(tm, x,y,i, stbte__extract_short(f,0), stbte__extract_short(f,1)); -} - -static void stbte__redo_record_prop(stbte_tilemap *tm, int x, int y, int i, short s0, short s1) -{ - stbte__write_redo(tm, s1); - stbte__write_redo(tm, s0); - stbte__write_redo(tm, x); - stbte__write_redo(tm, y); - stbte__write_redo(tm, 256+i); -} - - -static int stbte__undo_find_end(stbte_tilemap *tm) -{ - // first scan through for the end record - int i, pos = stbte__wrap(tm->undo_pos-1); - for (i=0; i < tm->undo_len;) { - STBTE_ASSERT(tm->undo_buffer[pos] != STBTE__undo_junk); - if (tm->undo_buffer[pos] == STBTE__undo_record) - break; - if (tm->undo_buffer[pos] >= 255) - pos = stbte__wrap(pos-5), i += 5; - else - pos = stbte__wrap(pos-4), i += 4; - } - if (i >= tm->undo_len) - return -1; - return pos; -} - -static void stbte__undo(stbte_tilemap *tm) -{ - int i, pos, endpos; - endpos = stbte__undo_find_end(tm); - if (endpos < 0) - return; - - // we found a complete undo record - pos = stbte__wrap(tm->undo_pos-1); - - // start a redo record - stbte__write_redo(tm, STBTE__redo_record); - - // so now go back through undo and apply in reverse - // order, and copy it to redo - for (i=0; endpos != pos; i += 4) { - int x,y,n,v; - // get the undo entry - n = tm->undo_buffer[pos]; - y = tm->undo_buffer[stbte__wrap(pos-1)]; - x = tm->undo_buffer[stbte__wrap(pos-2)]; - v = tm->undo_buffer[stbte__wrap(pos-3)]; - if (n >= 255) { - short s0=0,s1=0; - int v2 = tm->undo_buffer[stbte__wrap(pos-4)]; - pos = stbte__wrap(pos-5); - if (n > 255) { - float vf = stbte__extract_float(v, v2); - s0 = stbte__extract_short(tm->props[y][x][n-256], 0); - s1 = stbte__extract_short(tm->props[y][x][n-256], 1); - tm->props[y][x][n-256] = vf; - } else { -#ifdef STBTE_ALLOW_LINK - s0 = tm->link[y][x].x; - s1 = tm->link[y][x].y; - stbte__set_link(tm, x,y, v, v2, STBTE__undo_none); -#endif - } - // write the redo entry - stbte__redo_record_prop(tm, x, y, n-256, s0,s1); - // apply the undo entry - } else { - pos = stbte__wrap(pos-4); - // write the redo entry - stbte__redo_record(tm, x, y, n, tm->data[y][x][n]); - // apply the undo entry - tm->data[y][x][n] = (short) v; - } - } - // overwrite undo record with junk - tm->undo_buffer[tm->undo_pos] = STBTE__undo_junk; -} - -static int stbte__redo_find_end(stbte_tilemap *tm) -{ - // first scan through for the end record - int i, pos = stbte__wrap(tm->undo_pos+1); - for (i=0; i < tm->redo_len;) { - STBTE_ASSERT(tm->undo_buffer[pos] != STBTE__undo_junk); - if (tm->undo_buffer[pos] == STBTE__redo_record) - break; - if (tm->undo_buffer[pos] >= 255) - pos = stbte__wrap(pos+5), i += 5; - else - pos = stbte__wrap(pos+4), i += 4; - } - if (i >= tm->redo_len) - return -1; // this should only ever happen if redo buffer is empty - return pos; -} - -static void stbte__redo(stbte_tilemap *tm) -{ - // first scan through for the end record - int i, pos, endpos; - endpos = stbte__redo_find_end(tm); - if (endpos < 0) - return; - - // we found a complete redo record - pos = stbte__wrap(tm->undo_pos+1); - - // start an undo record - stbte__write_undo(tm, STBTE__undo_record); - - for (i=0; pos != endpos; i += 4) { - int x,y,n,v; - n = tm->undo_buffer[pos]; - y = tm->undo_buffer[stbte__wrap(pos+1)]; - x = tm->undo_buffer[stbte__wrap(pos+2)]; - v = tm->undo_buffer[stbte__wrap(pos+3)]; - if (n >= 255) { - int v2 = tm->undo_buffer[stbte__wrap(pos+4)]; - short s0=0,s1=0; - pos = stbte__wrap(pos+5); - if (n > 255) { - float vf = stbte__extract_float(v, v2); - s0 = stbte__extract_short(tm->props[y][x][n-256],0); - s1 = stbte__extract_short(tm->props[y][x][n-256],1); - tm->props[y][x][n-256] = vf; - } else { -#ifdef STBTE_ALLOW_LINK - s0 = tm->link[y][x].x; - s1 = tm->link[y][x].y; - stbte__set_link(tm, x,y,v,v2, STBTE__undo_none); -#endif - } - // don't use stbte__undo_record_prop because it's guarded - stbte__write_undo(tm, s1); - stbte__write_undo(tm, s0); - stbte__write_undo(tm, x); - stbte__write_undo(tm, y); - stbte__write_undo(tm, n); - } else { - pos = stbte__wrap(pos+4); - // don't use stbte__undo_record because it's guarded - stbte__write_undo(tm, tm->data[y][x][n]); - stbte__write_undo(tm, x); - stbte__write_undo(tm, y); - stbte__write_undo(tm, n); - tm->data[y][x][n] = (short) v; - } - } - tm->undo_buffer[tm->undo_pos] = STBTE__undo_junk; -} - -// because detecting that undo is available -static void stbte__recompute_undo_available(stbte_tilemap *tm) -{ - tm->undo_available = (stbte__undo_find_end(tm) >= 0); - tm->redo_available = (stbte__redo_find_end(tm) >= 0); -} - -static int stbte__undo_available(stbte_tilemap *tm) -{ - if (!tm->undo_available_valid) - stbte__recompute_undo_available(tm); - return tm->undo_available; -} - -static int stbte__redo_available(stbte_tilemap *tm) -{ - if (!tm->undo_available_valid) - stbte__recompute_undo_available(tm); - return tm->redo_available; -} - -/////////////////////////////////////////////////////////////////////////////////////////////////// - -#ifdef STBTE_ALLOW_LINK -static void stbte__set_link(stbte_tilemap *tm, int src_x, int src_y, int dest_x, int dest_y, int undo_mode) -{ - stbte__link *a; - STBTE_ASSERT(src_x >= 0 && src_x < STBTE_MAX_TILEMAP_X && src_y >= 0 && src_y < STBTE_MAX_TILEMAP_Y); - a = &tm->link[src_y][src_x]; - // check if it's a do nothing - if (a->x == dest_x && a->y == dest_y) - return; - if (undo_mode != STBTE__undo_none ) { - if (undo_mode == STBTE__undo_block) stbte__begin_undo(tm); - stbte__undo_record_prop(tm, src_x, src_y, -1, a->x, a->y); - if (undo_mode == STBTE__undo_block) stbte__end_undo(tm); - } - // check if there's an existing link - if (a->x >= 0) { - // decrement existing link refcount - STBTE_ASSERT(tm->linkcount[a->y][a->x] > 0); - --tm->linkcount[a->y][a->x]; - } - // increment new dest - if (dest_x >= 0) { - ++tm->linkcount[dest_y][dest_x]; - } - a->x = dest_x; - a->y = dest_y; -} -#endif - - -static void stbte__draw_rect(int x0, int y0, int x1, int y1, unsigned int color) -{ - STBTE_DRAW_RECT(x0,y0,x1,y1, color); -} - -static void stbte__draw_line(int x0, int y0, int x1, int y1, unsigned int color) -{ - int temp; - if (x1 < x0) temp=x0,x0=x1,x1=temp; - if (y1 < y0) temp=y0,y0=y1,y1=temp; - stbte__draw_rect(x0,y0,x1+1,y1+1,color); -} - -static void stbte__draw_link(int x0, int y0, int x1, int y1, unsigned int color) -{ - stbte__draw_line(x0,y0,x0,y1, color); - stbte__draw_line(x0,y1,x1,y1, color); -} - -static void stbte__draw_frame(int x0, int y0, int x1, int y1, unsigned int color) -{ - stbte__draw_rect(x0,y0,x1-1,y0+1,color); - stbte__draw_rect(x1-1,y0,x1,y1-1,color); - stbte__draw_rect(x0+1,y1-1,x1,y1,color); - stbte__draw_rect(x0,y0+1,x0+1,y1,color); -} - -static void stbte__draw_halfframe(int x0, int y0, int x1, int y1, unsigned int color) -{ - stbte__draw_rect(x0,y0,x1,y0+1,color); - stbte__draw_rect(x0,y0+1,x0+1,y1,color); -} - -static int stbte__get_char_width(int ch) -{ - return stbte__fontdata[ch-16]; -} - -static short *stbte__get_char_bitmap(int ch) -{ - return stbte__fontdata + stbte__font_offset[ch-16]; -} - -static void stbte__draw_bitmask_as_columns(int x, int y, short bitmask, int color) -{ - int start_i = -1, i=0; - while (bitmask) { - if (bitmask & (1<= 0) { - stbte__draw_rect(x, y+start_i, x+1, y+i, color); - start_i = -1; - bitmask &= ~((1< x_end) - break; - stbte__draw_bitmap(x, y, cw, stbte__get_char_bitmap(c), color); - if (digitspace && c == ' ') - cw = stbte__get_char_width('0'); - x += cw+1; - } -} - -static void stbte__draw_text(int x, int y, const char *str, int w, int color) -{ - stbte__draw_text_core(x,y,str,w,color,0); -} - -static int stbte__text_width(const char *str) -{ - int x = 0; - while (*str) { - int c = *str++; - int cw = stbte__get_char_width(c); - x += cw+1; - } - return x; -} - -static void stbte__draw_frame_delayed(int x0, int y0, int x1, int y1, int color) -{ - if (stbte__ui.delaycount < STBTE__MAX_DELAYRECT) { - stbte__colorrect r = { x0,y0,x1,y1,color }; - stbte__ui.delayrect[stbte__ui.delaycount++] = r; - } -} - -static void stbte__flush_delay(void) -{ - stbte__colorrect *r; - int i; - r = stbte__ui.delayrect; - for (i=0; i < stbte__ui.delaycount; ++i,++r) - stbte__draw_frame(r->x0,r->y0,r->x1,r->y1,r->color); - stbte__ui.delaycount = 0; -} - -static void stbte__activate(int id) -{ - stbte__ui.active_id = id; - stbte__ui.active_event = stbte__ui.event; - stbte__ui.accum_x = 0; - stbte__ui.accum_y = 0; -} - -static int stbte__hittest(int x0, int y0, int x1, int y1, int id) -{ - int over = stbte__ui.mx >= x0 && stbte__ui.my >= y0 - && stbte__ui.mx < x1 && stbte__ui.my < y1; - - if (over && stbte__ui.event >= STBTE__tick) - stbte__ui.next_hot_id = id; - - return over; -} - -static int stbte__button_core(int id) -{ - switch (stbte__ui.event) { - case STBTE__leftdown: - if (stbte__ui.hot_id == id && STBTE__INACTIVE()) - stbte__activate(id); - break; - case STBTE__leftup: - if (stbte__ui.active_id == id && STBTE__IS_HOT(id)) { - stbte__activate(0); - return 1; - } - break; - case STBTE__rightdown: - if (stbte__ui.hot_id == id && STBTE__INACTIVE()) - stbte__activate(id); - break; - case STBTE__rightup: - if (stbte__ui.active_id == id && STBTE__IS_HOT(id)) { - stbte__activate(0); - return -1; - } - break; - } - return 0; -} - -static void stbte__draw_box(int x0, int y0, int x1, int y1, int colormode, int colorindex) -{ - stbte__draw_rect (x0,y0,x1,y1, stbte__color_table[colormode][STBTE__base ][colorindex]); - stbte__draw_frame(x0,y0,x1,y1, stbte__color_table[colormode][STBTE__outline][colorindex]); -} - -static void stbte__draw_textbox(int x0, int y0, int x1, int y1, char *text, int xoff, int yoff, int colormode, int colorindex) -{ - stbte__draw_box(x0,y0,x1,y1,colormode,colorindex); - stbte__draw_text(x0+xoff,y0+yoff, text, x1-x0-xoff-1, stbte__color_table[colormode][STBTE__text][colorindex]); -} - -static int stbte__button(int colormode, char *label, int x, int y, int textoff, int width, int id, int toggled, int disabled) -{ - int x0=x,y0=y, x1=x+width,y1=y+STBTE__BUTTON_HEIGHT; - int s = STBTE__BUTTON_INTERNAL_SPACING; - - int over = !disabled && stbte__hittest(x0,y0,x1,y1,id); - - if (stbte__ui.event == STBTE__paint) - stbte__draw_textbox(x0,y0,x1,y1, label,s+textoff,s, colormode, STBTE__INDEX_FOR_ID(id,disabled,toggled)); - if (disabled) - return 0; - return (stbte__button_core(id) == 1); -} - -static int stbte__button_icon(int colormode, char ch, int x, int y, int width, int id, int toggled, int disabled) -{ - int x0=x,y0=y, x1=x+width,y1=y+STBTE__BUTTON_HEIGHT; - int s = STBTE__BUTTON_INTERNAL_SPACING; - - int over = stbte__hittest(x0,y0,x1,y1,id); - - if (stbte__ui.event == STBTE__paint) { - char label[2] = { ch, 0 }; - int pad = (9 - stbte__get_char_width(ch))/2; - stbte__draw_textbox(x0,y0,x1,y1, label,s+pad,s, colormode, STBTE__INDEX_FOR_ID(id,disabled,toggled)); - } - if (disabled) - return 0; - return (stbte__button_core(id) == 1); -} - -static int stbte__minibutton(int colormode, int x, int y, int ch, int id) -{ - int x0 = x, y0 = y, x1 = x+8, y1 = y+7; - int over = stbte__hittest(x0,y0,x1,y1,id); - if (stbte__ui.event == STBTE__paint) { - char str[2] = { ch,0 }; - stbte__draw_textbox(x0,y0,x1,y1, str,1,0,colormode, STBTE__INDEX_FOR_ID(id,0,0)); - } - return stbte__button_core(id); -} - -static int stbte__layerbutton(int x, int y, int ch, int id, int toggled, int disabled, int colormode) -{ - int x0 = x, y0 = y, x1 = x+10, y1 = y+11; - int over = !disabled && stbte__hittest(x0,y0,x1,y1,id); - if (stbte__ui.event == STBTE__paint) { - char str[2] = { ch,0 }; - int off = (9-stbte__get_char_width(ch))/2; - stbte__draw_textbox(x0,y0,x1,y1, str, off+1,2, colormode, STBTE__INDEX_FOR_ID(id,disabled,toggled)); - } - if (disabled) - return 0; - return stbte__button_core(id); -} - -static int stbte__microbutton(int x, int y, int size, int id, int colormode) -{ - int x0 = x, y0 = y, x1 = x+size, y1 = y+size; - int over = stbte__hittest(x0,y0,x1,y1,id); - if (stbte__ui.event == STBTE__paint) { - stbte__draw_box(x0,y0,x1,y1, colormode, STBTE__INDEX_FOR_ID(id,0,0)); - } - return stbte__button_core(id); -} - -static int stbte__microbutton_dragger(int x, int y, int size, int id, int *pos) -{ - int x0 = x, y0 = y, x1 = x+size, y1 = y+size; - int over = stbte__hittest(x0,y0,x1,y1,id); - switch (stbte__ui.event) { - case STBTE__paint: - stbte__draw_box(x0,y0,x1,y1, STBTE__cexpander, STBTE__INDEX_FOR_ID(id,0,0)); - break; - case STBTE__leftdown: - if (STBTE__IS_HOT(id) && STBTE__INACTIVE()) { - stbte__activate(id); - stbte__ui.sx = stbte__ui.mx - *pos; - } - break; - case STBTE__mousemove: - if (STBTE__IS_ACTIVE(id) && stbte__ui.active_event == STBTE__leftdown) { - *pos = stbte__ui.mx - stbte__ui.sx; - } - break; - case STBTE__leftup: - if (STBTE__IS_ACTIVE(id)) - stbte__activate(0); - break; - default: - return stbte__button_core(id); - } - return 0; -} - -static int stbte__category_button(char *label, int x, int y, int width, int id, int toggled) -{ - int x0=x,y0=y, x1=x+width,y1=y+STBTE__BUTTON_HEIGHT; - int s = STBTE__BUTTON_INTERNAL_SPACING; - - int over = stbte__hittest(x0,y0,x1,y1,id); - - if (stbte__ui.event == STBTE__paint) - stbte__draw_textbox(x0,y0,x1,y1, label, s,s, STBTE__ccategory_button, STBTE__INDEX_FOR_ID(id,0,toggled)); - - return (stbte__button_core(id) == 1); -} - -enum -{ - STBTE__none, - STBTE__begin, - STBTE__end, - STBTE__change, -}; - -// returns -1 if value changes, 1 at end of drag -static int stbte__slider(int x0, int w, int y, int range, int *value, int id) -{ - int x1 = x0+w; - int pos = *value * w / (range+1); - int over = stbte__hittest(x0,y-2,x1,y+3,id); - int event_mouse_move = STBTE__change; - switch (stbte__ui.event) { - case STBTE__paint: - stbte__draw_rect(x0,y,x1,y+1, 0x808080); - stbte__draw_rect(x0+pos-1,y-1,x0+pos+2,y+2, 0xffffff); - break; - case STBTE__leftdown: - if (STBTE__IS_HOT(id) && STBTE__INACTIVE()) { - stbte__activate(id); - event_mouse_move = STBTE__begin; - } - // fall through - case STBTE__mousemove: - if (STBTE__IS_ACTIVE(id)) { - int v = (stbte__ui.mx-x0)*(range+1)/w; - if (v < 0) v = 0; else if (v > range) v = range; - *value = v; - return event_mouse_move; - } - break; - case STBTE__leftup: - if (STBTE__IS_ACTIVE(id)) { - stbte__activate(0); - return STBTE__end; - } - break; - } - return STBTE__none; -} - -static int stbte__float_control(int x0, int y0, int w, float minv, float maxv, float scale, char *fmt, float *value, int colormode, int id) -{ - int x1 = x0+w; - int y1 = y0+11; - int over = stbte__hittest(x0,y0,x1,y1,id); - switch (stbte__ui.event) { - case STBTE__paint: { - char text[32]; - sprintf(text, fmt ? fmt : "%6.2f", *value); - stbte__draw_textbox(x0,y0,x1,y1, text, 1,2, colormode, STBTE__INDEX_FOR_ID(id,0,0)); - break; - } - case STBTE__leftdown: - case STBTE__rightdown: - if (STBTE__IS_HOT(id) && STBTE__INACTIVE()) - stbte__activate(id); - return STBTE__begin; - break; - case STBTE__leftup: - case STBTE__rightup: - if (STBTE__IS_ACTIVE(id)) { - stbte__activate(0); - return STBTE__end; - } - break; - case STBTE__mousemove: - if (STBTE__IS_ACTIVE(id)) { - float v = *value, delta; - int ax = stbte__ui.accum_x/STBTE_FLOAT_CONTROL_GRANULARITY; - int ay = stbte__ui.accum_y/STBTE_FLOAT_CONTROL_GRANULARITY; - stbte__ui.accum_x -= ax*STBTE_FLOAT_CONTROL_GRANULARITY; - stbte__ui.accum_y -= ay*STBTE_FLOAT_CONTROL_GRANULARITY; - if (stbte__ui.shift) { - if (stbte__ui.active_event == STBTE__leftdown) - delta = ax * 16.0f + ay; - else - delta = ax / 16.0f + ay / 256.0f; - } else { - if (stbte__ui.active_event == STBTE__leftdown) - delta = ax*10.0f + ay; - else - delta = ax * 0.1f + ay * 0.01f; - } - v += delta * scale; - if (v < minv) v = minv; - if (v > maxv) v = maxv; - *value = v; - return STBTE__change; - } - break; - } - return STBTE__none; -} - -static void stbte__scrollbar(int x, int y0, int y1, int *val, int v0, int v1, int num_vis, int id) -{ - int over; - int thumbpos; - if (v1 - v0 <= num_vis) - return; - - // generate thumbpos from numvis - thumbpos = y0+2 + (y1-y0-4) * *val / (v1 - v0 - num_vis); - if (thumbpos < y0) thumbpos = y0; - if (thumbpos >= y1) thumbpos = y1; - over = stbte__hittest(x-1,y0,x+2,y1,id); - switch (stbte__ui.event) { - case STBTE__paint: - stbte__draw_rect(x,y0,x+1,y1, stbte__color_table[STBTE__cscrollbar][STBTE__text][STBTE__idle]); - stbte__draw_box(x-1,thumbpos-3,x+2,thumbpos+4, STBTE__cscrollbar, STBTE__INDEX_FOR_ID(id,0,0)); - break; - case STBTE__leftdown: - if (STBTE__IS_HOT(id) && STBTE__INACTIVE()) { - // check if it's over the thumb - stbte__activate(id); - *val = ((stbte__ui.my-y0) * (v1 - v0 - num_vis) + (y1-y0)/2)/ (y1-y0); - } - break; - case STBTE__mousemove: - if (STBTE__IS_ACTIVE(id) && stbte__ui.mx >= x-15 && stbte__ui.mx <= x+15) - *val = ((stbte__ui.my-y0) * (v1 - v0 - num_vis) + (y1-y0)/2)/ (y1-y0); - break; - case STBTE__leftup: - if (STBTE__IS_ACTIVE(id)) - stbte__activate(0); - break; - - } - - if (*val >= v1-num_vis) - *val = v1-num_vis; - if (*val <= v0) - *val = v0; -} - - -static void stbte__compute_digits(stbte_tilemap *tm) -{ - if (tm->max_x >= 1000 || tm->max_y >= 1000) - tm->digits = 4; - else if (tm->max_x >= 100 || tm->max_y >= 100) - tm->digits = 3; - else - tm->digits = 2; -} - -static int stbte__is_single_selection(void) -{ - return stbte__ui.has_selection - && stbte__ui.select_x0 == stbte__ui.select_x1 - && stbte__ui.select_y0 == stbte__ui.select_y1; -} - -typedef struct -{ - int width, height; - int x,y; - int active; - float retracted; -} stbte__region_t; - -static stbte__region_t stbte__region[4]; - -#define STBTE__TOOLBAR_ICON_SIZE (9+2*2) -#define STBTE__TOOLBAR_PASTE_SIZE (34+2*2) - -// This routine computes where every panel goes onscreen: computes -// a minimum width for each side based on which panels are on that -// side, and accounts for width-dependent layout of certain panels. -static void stbte__compute_panel_locations(stbte_tilemap *tm) -{ - int i, limit, w, k; - int window_width = stbte__ui.x1 - stbte__ui.x0; - int window_height = stbte__ui.y1 - stbte__ui.y0; - int min_width[STBTE__num_panel]={0,0,0,0,0,0,0}; - int height[STBTE__num_panel]={0,0,0,0,0,0,0}; - int panel_active[STBTE__num_panel]={1,0,1,1,1,1,1}; - int vpos[4] = { 0,0,0,0 }; - stbte__panel *p = stbte__ui.panel; - stbte__panel *pt = &p[STBTE__panel_toolbar]; -#ifdef STBTE__NO_PROPS - int props = 0; -#else - int props = 1; -#endif - - for (i=0; i < 4; ++i) { - stbte__region[i].active = 0; - stbte__region[i].width = 0; - stbte__region[i].height = 0; - } - - // compute number of digits needs for info panel - stbte__compute_digits(tm); - - // determine which panels are active - panel_active[STBTE__panel_categories] = tm->num_categories != 0; - panel_active[STBTE__panel_layers ] = tm->num_layers > 1; -#ifdef STBTE__COLORPICKER - panel_active[STBTE__panel_colorpick ] = 1; -#endif - - panel_active[STBTE__panel_props ] = props && stbte__is_single_selection(); - - // compute minimum widths for each panel (assuming they're on sides not top) - min_width[STBTE__panel_info ] = 8 + 11 + 7*tm->digits+17+7; // estimate min width of "w:0000" - min_width[STBTE__panel_colorpick ] = 120; - min_width[STBTE__panel_tiles ] = 4 + tm->palette_spacing_x + 5; // 5 for scrollbar - min_width[STBTE__panel_categories] = 4 + 42 + 5; // 42 is enough to show ~7 chars; 5 for scrollbar - min_width[STBTE__panel_layers ] = 4 + 54 + 30*tm->has_layer_names; // 2 digits plus 3 buttons plus scrollbar - min_width[STBTE__panel_toolbar ] = 4 + STBTE__TOOLBAR_PASTE_SIZE; // wide enough for 'Paste' button - min_width[STBTE__panel_props ] = 80; // narrowest info panel - - // compute minimum widths for left & right panels based on the above - stbte__region[0].width = stbte__ui.left_width; - stbte__region[1].width = stbte__ui.right_width; - - for (i=0; i < STBTE__num_panel; ++i) { - if (panel_active[i]) { - int side = stbte__ui.panel[i].side; - if (min_width[i] > stbte__region[side].width) - stbte__region[side].width = min_width[i]; - stbte__region[side].active = 1; - } - } - - // now compute the heights of each panel - - // if toolbar at top, compute its size & push the left and right start points down - if (stbte__region[STBTE__side_top].active) { - int height = STBTE__TOOLBAR_ICON_SIZE+2; - pt->x0 = stbte__ui.x0; - pt->y0 = stbte__ui.y0; - pt->width = window_width; - pt->height = height; - vpos[STBTE__side_left] = vpos[STBTE__side_right] = height; - } else { - int num_rows = STBTE__num_tool * ((stbte__region[pt->side].width-4)/STBTE__TOOLBAR_ICON_SIZE); - height[STBTE__panel_toolbar] = num_rows*13 + 3*15 + 4; // 3*15 for cut/copy/paste, which are stacked vertically - } - - for (i=0; i < 4; ++i) - stbte__region[i].y = stbte__ui.y0 + vpos[i]; - - for (i=0; i < 2; ++i) { - int anim = (int) (stbte__region[i].width * stbte__region[i].retracted); - stbte__region[i].x = (i == STBTE__side_left) ? stbte__ui.x0 - anim : stbte__ui.x1 - stbte__region[i].width + anim; - } - - // color picker - height[STBTE__panel_colorpick] = 300; - - // info panel - w = stbte__region[p[STBTE__panel_info].side].width; - p[STBTE__panel_info].mode = (w >= 8 + (11+7*tm->digits+17)*2 + 4); - if (p[STBTE__panel_info].mode) - height[STBTE__panel_info] = 5 + 11*2 + 2 + tm->palette_spacing_y; - else - height[STBTE__panel_info] = 5 + 11*4 + 2 + tm->palette_spacing_y; - - // layers - limit = 6 + stbte__ui.panel[STBTE__panel_layers].delta_height; - height[STBTE__panel_layers] = (tm->num_layers > limit ? limit : tm->num_layers)*15 + 7 + (tm->has_layer_names ? 0 : 11) + props*13; - - // categories - limit = 6 + stbte__ui.panel[STBTE__panel_categories].delta_height; - height[STBTE__panel_categories] = (tm->num_categories+1 > limit ? limit : tm->num_categories+1)*11 + 14; - if (stbte__ui.panel[STBTE__panel_categories].side == stbte__ui.panel[STBTE__panel_categories].side) - height[STBTE__panel_categories] -= 4; - - // palette - k = (stbte__region[p[STBTE__panel_tiles].side].width - 8) / tm->palette_spacing_x; - if (k == 0) k = 1; - height[STBTE__panel_tiles] = ((tm->num_tiles+k-1)/k) * tm->palette_spacing_y + 8; - - // properties panel - height[STBTE__panel_props] = 9 + STBTE_MAX_PROPERTIES*14; - - // now compute the locations of all the panels - for (i=0; i < STBTE__num_panel; ++i) { - if (panel_active[i]) { - int side = p[i].side; - if (side == STBTE__side_left || side == STBTE__side_right) { - p[i].width = stbte__region[side].width; - p[i].x0 = stbte__region[side].x; - p[i].y0 = stbte__ui.y0 + vpos[side]; - p[i].height = height[i]; - vpos[side] += height[i]; - if (vpos[side] > window_height) { - vpos[side] = window_height; - p[i].height = stbte__ui.y1 - p[i].y0; - } - } else { - ; // it's at top, it's already been explicitly set up earlier - } - } else { - // inactive panel - p[i].height = 0; - p[i].width = 0; - p[i].x0 = stbte__ui.x1; - p[i].y0 = stbte__ui.y1; - } - } -} - -// unique identifiers for imgui -enum -{ - STBTE__map=1, - STBTE__region, - STBTE__panel, // panel background to hide map, and misc controls - STBTE__info, // info data - STBTE__toolbarA, STBTE__toolbarB, // toolbar buttons: param is tool number - STBTE__palette, // palette selectors: param is tile index - STBTE__categories, // category selectors: param is category index - STBTE__layer, // - STBTE__solo, STBTE__hide, STBTE__lock, // layer controls: param is layer - STBTE__scrollbar, // param is panel ID - STBTE__panel_mover, // p1 is panel ID, p2 is destination side - STBTE__panel_sizer, // param panel ID - STBTE__scrollbar_id, - STBTE__colorpick_id, - STBTE__prop_flag, - STBTE__prop_float, - STBTE__prop_int, -}; - -// id is: [ 24-bit data : 7-bit identifer ] -// map id is: [ 12-bit y : 12 bit x : 7-bit identifier ] - -#define STBTE__ID(n,p) ((n) + ((p)<<7)) -#define STBTE__ID2(n,p,q) STBTE__ID(n, ((p)<<12)+(q) ) -#define STBTE__IDMAP(x,y) STBTE__ID2(STBTE__map, x,y) - -static void stbte__activate_map(int x, int y) -{ - stbte__ui.active_id = STBTE__IDMAP(x,y); - stbte__ui.active_event = stbte__ui.event; - stbte__ui.sx = x; - stbte__ui.sy = y; -} - -static void stbte__alert(const char *msg) -{ - stbte__ui.alert_msg = msg; - stbte__ui.alert_timer = 3; -} - -#define STBTE__BG(tm,layer) ((layer) == 0 ? (tm)->background_tile : STBTE__NO_TILE) - - - -static void stbte__brush_predict(stbte_tilemap *tm, short result[]) -{ - int layer_to_paint = tm->cur_layer; - stbte__tileinfo *ti; - int i; - - if (tm->cur_tile < 0) return; - - ti = &tm->tiles[tm->cur_tile]; - - // find lowest legit layer to paint it on, and put it there - for (i=0; i < tm->num_layers; ++i) { - // check if object is allowed on layer - if (!(ti->layermask & (1 << i))) - continue; - - if (i != tm->solo_layer) { - // if there's a selected layer, can only paint on that - if (tm->cur_layer >= 0 && i != tm->cur_layer) - continue; - - // if the layer is hidden, we can't see it - if (tm->layerinfo[i].hidden) - continue; - - // if the layer is locked, we can't write to it - if (tm->layerinfo[i].locked == STBTE__locked) - continue; - - // if the layer is non-empty and protected, can't write to it - if (tm->layerinfo[i].locked == STBTE__protected && result[i] != STBTE__BG(tm,i)) - continue; - } - - result[i] = ti->id; - return; - } -} - -static void stbte__brush(stbte_tilemap *tm, int x, int y) -{ - int layer_to_paint = tm->cur_layer; - stbte__tileinfo *ti; - - // find lowest legit layer to paint it on, and put it there - int i; - - if (tm->cur_tile < 0) return; - - ti = &tm->tiles[tm->cur_tile]; - - for (i=0; i < tm->num_layers; ++i) { - // check if object is allowed on layer - if (!(ti->layermask & (1 << i))) - continue; - - if (i != tm->solo_layer) { - // if there's a selected layer, can only paint on that - if (tm->cur_layer >= 0 && i != tm->cur_layer) - continue; - - // if the layer is hidden, we can't see it - if (tm->layerinfo[i].hidden) - continue; - - // if the layer is locked, we can't write to it - if (tm->layerinfo[i].locked == STBTE__locked) - continue; - - // if the layer is non-empty and protected, can't write to it - if (tm->layerinfo[i].locked == STBTE__protected && tm->data[y][x][i] != STBTE__BG(tm,i)) - continue; - } - - stbte__undo_record(tm,x,y,i,tm->data[y][x][i]); - tm->data[y][x][i] = ti->id; - return; - } - - //stbte__alert("Selected tile not valid on active layer(s)"); -} - -enum -{ - STBTE__erase_none = -1, - STBTE__erase_brushonly = 0, - STBTE__erase_any = 1, - STBTE__erase_all = 2, -}; - -static int stbte__erase_predict(stbte_tilemap *tm, short result[], int allow_any) -{ - stbte__tileinfo *ti = tm->cur_tile >= 0 ? &tm->tiles[tm->cur_tile] : NULL; - int i; - - if (allow_any == STBTE__erase_none) - return allow_any; - - // first check if only one layer is legit - i = tm->cur_layer; - if (tm->solo_layer >= 0) - i = tm->solo_layer; - - // if only one layer is legit, directly process that one for clarity - if (i >= 0) { - short bg = (i == 0 ? tm->background_tile : -1); - if (tm->solo_layer < 0) { - // check that we're allowed to write to it - if (tm->layerinfo[i].hidden) return STBTE__erase_none; - if (tm->layerinfo[i].locked) return STBTE__erase_none; - } - if (result[i] == bg) - return STBTE__erase_none; // didn't erase anything - if (ti && result[i] == ti->id && (i != 0 || ti->id != tm->background_tile)) { - result[i] = bg; - return STBTE__erase_brushonly; - } - if (allow_any == STBTE__erase_any) { - result[i] = bg; - return STBTE__erase_any; - } - return STBTE__erase_none; - } - - // if multiple layers are legit, first scan all for brush data - - if (ti && allow_any != STBTE__erase_all) { - for (i=tm->num_layers-1; i >= 0; --i) { - if (result[i] != ti->id) - continue; - if (tm->layerinfo[i].locked || tm->layerinfo[i].hidden) - continue; - if (i == 0 && result[i] == tm->background_tile) - return STBTE__erase_none; - result[i] = STBTE__BG(tm,i); - return STBTE__erase_brushonly; - } - } - - if (allow_any != STBTE__erase_any && allow_any != STBTE__erase_all) - return STBTE__erase_none; - - // apply layer filters, erase from top - for (i=tm->num_layers-1; i >= 0; --i) { - if (result[i] < 0) - continue; - if (tm->layerinfo[i].locked || tm->layerinfo[i].hidden) - continue; - if (i == 0 && result[i] == tm->background_tile) - return STBTE__erase_none; - result[i] = STBTE__BG(tm,i); - if (allow_any != STBTE__erase_all) - return STBTE__erase_any; - } - - if (allow_any == STBTE__erase_all) - return allow_any; - return STBTE__erase_none; -} - -static int stbte__erase(stbte_tilemap *tm, int x, int y, int allow_any) -{ - stbte__tileinfo *ti = tm->cur_tile >= 0 ? &tm->tiles[tm->cur_tile] : NULL; - int i; - - if (allow_any == STBTE__erase_none) - return allow_any; - - // first check if only one layer is legit - i = tm->cur_layer; - if (tm->solo_layer >= 0) - i = tm->solo_layer; - - // if only one layer is legit, directly process that one for clarity - if (i >= 0) { - short bg = (i == 0 ? tm->background_tile : -1); - if (tm->solo_layer < 0) { - // check that we're allowed to write to it - if (tm->layerinfo[i].hidden) return STBTE__erase_none; - if (tm->layerinfo[i].locked) return STBTE__erase_none; - } - if (tm->data[y][x][i] == bg) - return -1; // didn't erase anything - if (ti && tm->data[y][x][i] == ti->id && (i != 0 || ti->id != tm->background_tile)) { - stbte__undo_record(tm,x,y,i,tm->data[y][x][i]); - tm->data[y][x][i] = bg; - return STBTE__erase_brushonly; - } - if (allow_any == STBTE__erase_any) { - stbte__undo_record(tm,x,y,i,tm->data[y][x][i]); - tm->data[y][x][i] = bg; - return STBTE__erase_any; - } - return STBTE__erase_none; - } - - // if multiple layers are legit, first scan all for brush data - - if (ti && allow_any != STBTE__erase_all) { - for (i=tm->num_layers-1; i >= 0; --i) { - if (tm->data[y][x][i] != ti->id) - continue; - if (tm->layerinfo[i].locked || tm->layerinfo[i].hidden) - continue; - if (i == 0 && tm->data[y][x][i] == tm->background_tile) - return STBTE__erase_none; - stbte__undo_record(tm,x,y,i,tm->data[y][x][i]); - tm->data[y][x][i] = STBTE__BG(tm,i); - return STBTE__erase_brushonly; - } - } - - if (allow_any != STBTE__erase_any && allow_any != STBTE__erase_all) - return STBTE__erase_none; - - // apply layer filters, erase from top - for (i=tm->num_layers-1; i >= 0; --i) { - if (tm->data[y][x][i] < 0) - continue; - if (tm->layerinfo[i].locked || tm->layerinfo[i].hidden) - continue; - if (i == 0 && tm->data[y][x][i] == tm->background_tile) - return STBTE__erase_none; - stbte__undo_record(tm,x,y,i,tm->data[y][x][i]); - tm->data[y][x][i] = STBTE__BG(tm,i); - if (allow_any != STBTE__erase_all) - return STBTE__erase_any; - } - if (allow_any == STBTE__erase_all) - return allow_any; - return STBTE__erase_none; -} - -static int stbte__find_tile(stbte_tilemap *tm, int tile_id) -{ - int i; - for (i=0; i < tm->num_tiles; ++i) - if (tm->tiles[i].id == tile_id) - return i; - stbte__alert("Eyedropped tile that isn't in tileset"); - return -1; -} - -static void stbte__eyedrop(stbte_tilemap *tm, int x, int y) -{ - int i,j; - - // flush eyedropper state - if (stbte__ui.eyedrop_x != x || stbte__ui.eyedrop_y != y) { - stbte__ui.eyedrop_x = x; - stbte__ui.eyedrop_y = y; - stbte__ui.eyedrop_last_layer = tm->num_layers; - } - - // if only one layer is active, query that - i = tm->cur_layer; - if (tm->solo_layer >= 0) - i = tm->solo_layer; - if (i >= 0) { - if (tm->data[y][x][i] == STBTE__NO_TILE) - return; - tm->cur_tile = stbte__find_tile(tm, tm->data[y][x][i]); - return; - } - - // if multiple layers, continue from previous - i = stbte__ui.eyedrop_last_layer; - for (j=0; j < tm->num_layers; ++j) { - if (--i < 0) - i = tm->num_layers-1; - if (tm->layerinfo[i].hidden) - continue; - if (tm->data[y][x][i] == STBTE__NO_TILE) - continue; - stbte__ui.eyedrop_last_layer = i; - tm->cur_tile = stbte__find_tile(tm, tm->data[y][x][i]); - return; - } -} - -static int stbte__should_copy_properties(stbte_tilemap *tm) -{ - int i; - if (tm->propmode == STBTE__propmode_always) - return 1; - if (tm->propmode == STBTE__propmode_never) - return 0; - if (tm->solo_layer >= 0 || tm->cur_layer >= 0) - return 0; - for (i=0; i < tm->num_layers; ++i) - if (tm->layerinfo[i].hidden || tm->layerinfo[i].locked) - return 0; - return 1; -} - -// compute the result of pasting into a tile non-destructively so we can preview it -static void stbte__paste_stack(stbte_tilemap *tm, short result[], short dest[], short src[], int dragging) -{ - int i; - - // special case single-layer - i = tm->cur_layer; - if (tm->solo_layer >= 0) - i = tm->solo_layer; - if (i >= 0) { - if (tm->solo_layer < 0) { - // check that we're allowed to write to it - if (tm->layerinfo[i].hidden) return; - if (tm->layerinfo[i].locked == STBTE__locked) return; - // if protected, dest has to be empty - if (tm->layerinfo[i].locked == STBTE__protected && dest[i] != STBTE__BG(tm,i)) return; - // if dragging w/o copy, we will try to erase stuff, which protection disallows - if (dragging && tm->layerinfo[i].locked == STBTE__protected) - return; - } - result[i] = dest[i]; - if (src[i] != STBTE__BG(tm,i)) - result[i] = src[i]; - return; - } - - for (i=0; i < tm->num_layers; ++i) { - result[i] = dest[i]; - if (src[i] != STBTE__NO_TILE) - if (!tm->layerinfo[i].hidden && tm->layerinfo[i].locked != STBTE__locked) - if (tm->layerinfo[i].locked == STBTE__unlocked || (!dragging && dest[i] == STBTE__BG(tm,i))) - result[i] = src[i]; - } -} - -// compute the result of dragging away from a tile -static void stbte__clear_stack(stbte_tilemap *tm, short result[]) -{ - int i; - // special case single-layer - i = tm->cur_layer; - if (tm->solo_layer >= 0) - i = tm->solo_layer; - if (i >= 0) - result[i] = STBTE__BG(tm,i); - else - for (i=0; i < tm->num_layers; ++i) - if (!tm->layerinfo[i].hidden && tm->layerinfo[i].locked == STBTE__unlocked) - result[i] = STBTE__BG(tm,i); -} - -// check if some map square is active -#define STBTE__IS_MAP_ACTIVE() ((stbte__ui.active_id & 127) == STBTE__map) -#define STBTE__IS_MAP_HOT() ((stbte__ui.hot_id & 127) == STBTE__map) - -static void stbte__fillrect(stbte_tilemap *tm, int x0, int y0, int x1, int y1, int fill) -{ - int i,j; - int x=x0,y=y0; - - stbte__begin_undo(tm); - if (x0 > x1) i=x0,x0=x1,x1=i; - if (y0 > y1) j=y0,y0=y1,y1=j; - for (j=y0; j <= y1; ++j) - for (i=x0; i <= x1; ++i) - if (fill) - stbte__brush(tm, i,j); - else - stbte__erase(tm, i,j,STBTE__erase_any); - stbte__end_undo(tm); - // suppress warning from brush - stbte__ui.alert_msg = 0; -} - -static void stbte__select_rect(stbte_tilemap *tm, int x0, int y0, int x1, int y1) -{ - stbte__ui.has_selection = 1; - stbte__ui.select_x0 = (x0 < x1 ? x0 : x1); - stbte__ui.select_x1 = (x0 < x1 ? x1 : x0); - stbte__ui.select_y0 = (y0 < y1 ? y0 : y1); - stbte__ui.select_y1 = (y0 < y1 ? y1 : y0); -} - -static void stbte__copy_properties(float *dest, float *src) -{ - int i; - for (i=0; i < STBTE_MAX_PROPERTIES; ++i) - dest[i] = src[i]; -} - -static void stbte__copy_cut(stbte_tilemap *tm, int cut) -{ - int i,j,n,w,h,p=0; - int copy_props = stbte__should_copy_properties(tm); - if (!stbte__ui.has_selection) - return; - w = stbte__ui.select_x1 - stbte__ui.select_x0 + 1; - h = stbte__ui.select_y1 - stbte__ui.select_y0 + 1; - if (STBTE_MAX_COPY / w < h) { - stbte__alert("Selection too large for copy buffer, increase STBTE_MAX_COPY"); - return; - } - - for (i=0; i < w*h; ++i) - for (n=0; n < tm->num_layers; ++n) - stbte__ui.copybuffer[i][n] = STBTE__NO_TILE; - - if (cut) - stbte__begin_undo(tm); - for (j=stbte__ui.select_y0; j <= stbte__ui.select_y1; ++j) { - for (i=stbte__ui.select_x0; i <= stbte__ui.select_x1; ++i) { - for (n=0; n < tm->num_layers; ++n) { - if (tm->solo_layer >= 0) { - if (tm->solo_layer != n) - continue; - } else { - if (tm->cur_layer >= 0) - if (tm->cur_layer != n) - continue; - if (tm->layerinfo[n].hidden) - continue; - if (cut && tm->layerinfo[n].locked) - continue; - } - stbte__ui.copybuffer[p][n] = tm->data[j][i][n]; - if (cut) { - stbte__undo_record(tm,i,j,n, tm->data[j][i][n]); - tm->data[j][i][n] = (n==0 ? tm->background_tile : -1); - } - } - if (copy_props) { - stbte__copy_properties(stbte__ui.copyprops[p], tm->props[j][i]); -#ifdef STBTE_ALLOW_LINK - stbte__ui.copylinks[p] = tm->link[j][i]; - if (cut) - stbte__set_link(tm, i,j,-1,-1, STBTE__undo_record); -#endif - } - ++p; - } - } - if (cut) - stbte__end_undo(tm); - stbte__ui.copy_width = w; - stbte__ui.copy_height = h; - stbte__ui.has_copy = 1; - //stbte__ui.has_selection = 0; - stbte__ui.copy_has_props = copy_props; - stbte__ui.copy_src = tm; // used to give better semantics when copying links - stbte__ui.copy_src_x = stbte__ui.select_x0; - stbte__ui.copy_src_y = stbte__ui.select_y0; -} - -static int stbte__in_rect(int x, int y, int x0, int y0, int w, int h) -{ - return x >= x0 && x < x0+w && y >= y0 && y < y0+h; -} - -static int stbte__in_src_rect(int x, int y) -{ - return stbte__in_rect(x,y, stbte__ui.copy_src_x, stbte__ui.copy_src_y, stbte__ui.copy_width, stbte__ui.copy_height); -} - -static int stbte__in_dest_rect(int x, int y, int destx, int desty) -{ - return stbte__in_rect(x,y, destx, desty, stbte__ui.copy_width, stbte__ui.copy_height); -} - -static void stbte__paste(stbte_tilemap *tm, int mapx, int mapy) -{ - int w = stbte__ui.copy_width; - int h = stbte__ui.copy_height; - int i,j,k,p; - int x = mapx - (w>>1); - int y = mapy - (h>>1); - int copy_props = stbte__should_copy_properties(tm) && stbte__ui.copy_has_props; - if (stbte__ui.has_copy == 0) - return; - stbte__begin_undo(tm); - p = 0; - for (j=0; j < h; ++j) { - for (i=0; i < w; ++i) { - if (y+j >= 0 && y+j < tm->max_y && x+i >= 0 && x+i < tm->max_x) { - // compute the new stack - short tilestack[STBTE_MAX_LAYERS]; - for (k=0; k < tm->num_layers; ++k) - tilestack[k] = tm->data[y+j][x+i][k]; - stbte__paste_stack(tm, tilestack, tilestack, stbte__ui.copybuffer[p], 0); - // update anything that changed - for (k=0; k < tm->num_layers; ++k) { - if (tilestack[k] != tm->data[y+j][x+i][k]) { - stbte__undo_record(tm, x+i,y+j,k, tm->data[y+j][x+i][k]); - tm->data[y+j][x+i][k] = tilestack[k]; - } - } - } - if (copy_props) { -#ifdef STBTE_ALLOW_LINK - // need to decide how to paste a link, so there's a few cases - int destx = -1, desty = -1; - stbte__link *link = &stbte__ui.copylinks[p]; - - // check if link is within-rect - if (stbte__in_src_rect(link->x, link->y)) { - // new link should point to copy (but only if copy is within map) - destx = x + (link->x - stbte__ui.copy_src_x); - desty = y + (link->y - stbte__ui.copy_src_y); - } else if (tm == stbte__ui.copy_src) { - // if same map, then preserve link unless target is overwritten - if (!stbte__in_dest_rect(link->x,link->y,x,y)) { - destx = link->x; - desty = link->y; - } - } - // this is necessary for offset-copy, but also in case max_x/max_y has changed - if (destx < 0 || destx >= tm->max_x || desty < 0 || desty >= tm->max_y) - destx = -1, desty = -1; - stbte__set_link(tm, x+i, y+j, destx, desty, STBTE__undo_record); -#endif - for (k=0; k < STBTE_MAX_PROPERTIES; ++k) { - if (tm->props[y+j][x+i][k] != stbte__ui.copyprops[p][k]) - stbte__undo_record_prop_float(tm, x+i, y+j, k, tm->props[y+j][x+i][k]); - } - stbte__copy_properties(tm->props[y+j][x+i], stbte__ui.copyprops[p]); - } - ++p; - } - } - stbte__end_undo(tm); -} - -static void stbte__drag_update(stbte_tilemap *tm, int mapx, int mapy, int copy_props) -{ - int w = stbte__ui.drag_w, h = stbte__ui.drag_h; - int ox,oy,i,deleted=0,written=0; - short temp[STBTE_MAX_LAYERS]; - short *data = NULL; - if (!stbte__ui.shift) { - ox = mapx - stbte__ui.drag_x; - oy = mapy - stbte__ui.drag_y; - if (ox >= 0 && ox < w && oy >= 0 && oy < h) { - deleted=1; - for (i=0; i < tm->num_layers; ++i) - temp[i] = tm->data[mapy][mapx][i]; - data = temp; - stbte__clear_stack(tm, data); - } - } - ox = mapx - stbte__ui.drag_dest_x; - oy = mapy - stbte__ui.drag_dest_y; - // if this map square is in the target drag region - if (ox >= 0 && ox < w && oy >= 0 && oy < h) { - // and the src map square is on the map - if (stbte__in_rect(stbte__ui.drag_x+ox, stbte__ui.drag_y+oy, 0, 0, tm->max_x, tm->max_y)) { - written = 1; - if (data == NULL) { - for (i=0; i < tm->num_layers; ++i) - temp[i] = tm->data[mapy][mapx][i]; - data = temp; - } - stbte__paste_stack(tm, data, data, tm->data[stbte__ui.drag_y+oy][stbte__ui.drag_x+ox], !stbte__ui.shift); - if (copy_props) { - for (i=0; i < STBTE_MAX_PROPERTIES; ++i) { - if (tm->props[mapy][mapx][i] != tm->props[stbte__ui.drag_y+oy][stbte__ui.drag_x+ox][i]) { - stbte__undo_record_prop_float(tm, mapx, mapy, i, tm->props[mapy][mapx][i]); - tm->props[mapy][mapx][i] = tm->props[stbte__ui.drag_y+oy][stbte__ui.drag_x+ox][i]; - } - } - } - } - } - if (data) { - for (i=0; i < tm->num_layers; ++i) { - if (tm->data[mapy][mapx][i] != data[i]) { - stbte__undo_record(tm, mapx, mapy, i, tm->data[mapy][mapx][i]); - tm->data[mapy][mapx][i] = data[i]; - } - } - } - #ifdef STBTE_ALLOW_LINK - if (copy_props) { - int overwritten=0, moved=0, copied=0; - // since this function is called on EVERY tile, we can fix up even tiles not - // involved in the move - - stbte__link *k; - // first, determine what src link ends up here - k = &tm->link[mapy][mapx]; // by default, it's the one currently here - if (deleted) // if dragged away, it's erased - k = NULL; - if (written) // if dragged into, it gets that link - k = &tm->link[stbte__ui.drag_y+oy][stbte__ui.drag_x+ox]; - - // now check whether the *target* gets moved or overwritten - if (k && k->x >= 0) { - overwritten = stbte__in_rect(k->x, k->y, stbte__ui.drag_dest_x, stbte__ui.drag_dest_y, w, h); - if (!stbte__ui.shift) - moved = stbte__in_rect(k->x, k->y, stbte__ui.drag_x , stbte__ui.drag_y , w, h); - else - copied = stbte__in_rect(k->x, k->y, stbte__ui.drag_x , stbte__ui.drag_y , w, h); - } - - if (deleted || written || overwritten || moved || copied) { - // choose the final link value based on the above - if (k == NULL || k->x < 0) - stbte__set_link(tm, mapx, mapy, -1, -1, STBTE__undo_record); - else if (moved || (copied && written)) { - // if we move the target, we update to point to the new target; - // or, if we copy the target and the source is part ofthe copy, then update to new target - int x = k->x + (stbte__ui.drag_dest_x - stbte__ui.drag_x); - int y = k->y + (stbte__ui.drag_dest_y - stbte__ui.drag_y); - if (!(x >= 0 && y >= 0 && x < tm->max_x && y < tm->max_y)) - x = -1, y = -1; - stbte__set_link(tm, mapx, mapy, x, y, STBTE__undo_record); - } else if (overwritten) { - stbte__set_link(tm, mapx, mapy, -1, -1, STBTE__undo_record); - } else - stbte__set_link(tm, mapx, mapy, k->x, k->y, STBTE__undo_record); - } - } - #endif -} - -static void stbte__drag_place(stbte_tilemap *tm, int mapx, int mapy) -{ - int i,j; - int copy_props = stbte__should_copy_properties(tm); - int move_x = (stbte__ui.drag_dest_x - stbte__ui.drag_x); - int move_y = (stbte__ui.drag_dest_y - stbte__ui.drag_y); - if (move_x == 0 && move_y == 0) - return; - - stbte__begin_undo(tm); - // we now need a 2D memmove-style mover that doesn't - // overwrite any data as it goes. this requires being - // direction sensitive in the same way as memmove - if (move_y > 0 || (move_y == 0 && move_x > 0)) { - for (j=tm->max_y-1; j >= 0; --j) - for (i=tm->max_x-1; i >= 0; --i) - stbte__drag_update(tm,i,j,copy_props); - } else { - for (j=0; j < tm->max_y; ++j) - for (i=0; i < tm->max_x; ++i) - stbte__drag_update(tm,i,j,copy_props); - } - stbte__end_undo(tm); - - stbte__ui.has_selection = 1; - stbte__ui.select_x0 = stbte__ui.drag_dest_x; - stbte__ui.select_y0 = stbte__ui.drag_dest_y; - stbte__ui.select_x1 = stbte__ui.select_x0 + stbte__ui.drag_w - 1; - stbte__ui.select_y1 = stbte__ui.select_y0 + stbte__ui.drag_h - 1; -} - -static void stbte__tile_paint(stbte_tilemap *tm, int sx, int sy, int mapx, int mapy, int layer) -{ - int i; - int id = STBTE__IDMAP(mapx,mapy); - int x0=sx, y0=sy; - int x1=sx+tm->spacing_x, y1=sy+tm->spacing_y; - int over = stbte__hittest(x0,y0,x1,y1, id); - short *data = tm->data[mapy][mapx]; - short temp[STBTE_MAX_LAYERS]; - - if (STBTE__IS_MAP_HOT()) { - if (stbte__ui.pasting) { - int ox = mapx - stbte__ui.paste_x; - int oy = mapy - stbte__ui.paste_y; - if (ox >= 0 && ox < stbte__ui.copy_width && oy >= 0 && oy < stbte__ui.copy_height) { - stbte__paste_stack(tm, temp, tm->data[mapy][mapx], stbte__ui.copybuffer[oy*stbte__ui.copy_width+ox], 0); - data = temp; - } - } else if (stbte__ui.dragging) { - int ox,oy; - for (i=0; i < tm->num_layers; ++i) - temp[i] = tm->data[mapy][mapx][i]; - data = temp; - - // if it's in the source area, remove things unless shift-dragging - ox = mapx - stbte__ui.drag_x; - oy = mapy - stbte__ui.drag_y; - if (!stbte__ui.shift && ox >= 0 && ox < stbte__ui.drag_w && oy >= 0 && oy < stbte__ui.drag_h) { - stbte__clear_stack(tm, temp); - } - - ox = mapx - stbte__ui.drag_dest_x; - oy = mapy - stbte__ui.drag_dest_y; - if (ox >= 0 && ox < stbte__ui.drag_w && oy >= 0 && oy < stbte__ui.drag_h) { - stbte__paste_stack(tm, temp, temp, tm->data[stbte__ui.drag_y+oy][stbte__ui.drag_x+ox], !stbte__ui.shift); - } - } else if (STBTE__IS_MAP_ACTIVE()) { - if (stbte__ui.tool == STBTE__tool_rect) { - if ((stbte__ui.ms_time & 511) < 380) { - int ex = ((stbte__ui.hot_id >> 19) & 4095); - int ey = ((stbte__ui.hot_id >> 7) & 4095); - int sx = stbte__ui.sx; - int sy = stbte__ui.sy; - - if ( ((mapx >= sx && mapx < ex+1) || (mapx >= ex && mapx < sx+1)) - && ((mapy >= sy && mapy < ey+1) || (mapy >= ey && mapy < sy+1))) { - int i; - for (i=0; i < tm->num_layers; ++i) - temp[i] = tm->data[mapy][mapx][i]; - data = temp; - if (stbte__ui.active_event == STBTE__leftdown) - stbte__brush_predict(tm, temp); - else - stbte__erase_predict(tm, temp, STBTE__erase_any); - } - } - } - } - } - - if (STBTE__IS_HOT(id) && STBTE__INACTIVE() && !stbte__ui.pasting) { - if (stbte__ui.tool == STBTE__tool_brush) { - if ((stbte__ui.ms_time & 511) < 300) { - data = temp; - for (i=0; i < tm->num_layers; ++i) - temp[i] = tm->data[mapy][mapx][i]; - stbte__brush_predict(tm, temp); - } - } - } - - { - i = layer; - if (i == tm->solo_layer || (!tm->layerinfo[i].hidden && tm->solo_layer < 0)) - if (data[i] >= 0) - STBTE_DRAW_TILE(x0,y0, (unsigned short) data[i], 0, tm->props[mapy][mapx]); - } -} - -static void stbte__tile(stbte_tilemap *tm, int sx, int sy, int mapx, int mapy) -{ - int tool = stbte__ui.tool; - int x0=sx, y0=sy; - int x1=sx+tm->spacing_x, y1=sy+tm->spacing_y; - int id = STBTE__IDMAP(mapx,mapy); - int over = stbte__hittest(x0,y0,x1,y1, id); - switch (stbte__ui.event) { - case STBTE__paint: { - if (stbte__ui.pasting || stbte__ui.dragging || stbte__ui.scrolling) - break; - if (stbte__ui.scrollkey && !STBTE__IS_MAP_ACTIVE()) - break; - if (STBTE__IS_HOT(id) && STBTE__IS_MAP_ACTIVE() && (tool == STBTE__tool_rect || tool == STBTE__tool_select)) { - int rx0,ry0,rx1,ry1,t; - // compute the center of each rect - rx0 = x0 + tm->spacing_x/2; - ry0 = y0 + tm->spacing_y/2; - rx1 = rx0 + (stbte__ui.sx - mapx) * tm->spacing_x; - ry1 = ry0 + (stbte__ui.sy - mapy) * tm->spacing_y; - if (rx0 > rx1) t=rx0,rx0=rx1,rx1=t; - if (ry0 > ry1) t=ry0,ry0=ry1,ry1=t; - rx0 -= tm->spacing_x/2; - ry0 -= tm->spacing_y/2; - rx1 += tm->spacing_x/2; - ry1 += tm->spacing_y/2; - stbte__draw_frame(rx0-1,ry0-1,rx1+1,ry1+1, STBTE_COLOR_TILEMAP_HIGHLIGHT); - break; - } - if (STBTE__IS_HOT(id) && STBTE__INACTIVE()) { - stbte__draw_frame(x0-1,y0-1,x1+1,y1+1, STBTE_COLOR_TILEMAP_HIGHLIGHT); - } -#ifdef STBTE_ALLOW_LINK - if (stbte__ui.show_links && tm->link[mapy][mapx].x >= 0) { - int tx = tm->link[mapy][mapx].x; - int ty = tm->link[mapy][mapx].y; - int lx0,ly0,lx1,ly1; - if (STBTE_ALLOW_LINK(tm->data[mapy][mapx], tm->props[mapy][mapx], - tm->data[ty ][tx ], tm->props[ty ][tx ])) - { - lx0 = x0 + (tm->spacing_x >> 1) - 1; - ly0 = y0 + (tm->spacing_y >> 1) - 1; - lx1 = lx0 + (tx - mapx) * tm->spacing_x + 2; - ly1 = ly0 + (ty - mapy) * tm->spacing_y + 2; - stbte__draw_link(lx0,ly0,lx1,ly1, - STBTE_LINK_COLOR(tm->data[mapy][mapx], tm->props[mapy][mapx], - tm->data[ty ][tx ], tm->props[ty ][tx])); - } - } -#endif - break; - } - } - - if (stbte__ui.pasting) { - switch (stbte__ui.event) { - case STBTE__leftdown: - if (STBTE__IS_HOT(id)) { - stbte__ui.pasting = 0; - stbte__paste(tm, mapx, mapy); - stbte__activate(0); - } - break; - case STBTE__leftup: - // just clear it no matter what, since they might click away to clear it - stbte__activate(0); - break; - case STBTE__rightdown: - if (STBTE__IS_HOT(id)) { - stbte__activate(0); - stbte__ui.pasting = 0; - } - break; - } - return; - } - - if (stbte__ui.scrolling) { - if (stbte__ui.event == STBTE__leftup) { - stbte__activate(0); - stbte__ui.scrolling = 0; - } - if (stbte__ui.event == STBTE__mousemove) { - tm->scroll_x += (stbte__ui.start_x - stbte__ui.mx); - tm->scroll_y += (stbte__ui.start_y - stbte__ui.my); - stbte__ui.start_x = stbte__ui.mx; - stbte__ui.start_y = stbte__ui.my; - } - return; - } - - // regardless of tool, leftdown is a scrolldrag - if (STBTE__IS_HOT(id) && stbte__ui.scrollkey && stbte__ui.event == STBTE__leftdown) { - stbte__ui.scrolling = 1; - stbte__ui.start_x = stbte__ui.mx; - stbte__ui.start_y = stbte__ui.my; - return; - } - - switch (tool) { - case STBTE__tool_brush: - switch (stbte__ui.event) { - case STBTE__mousemove: - if (STBTE__IS_MAP_ACTIVE() && over) { - // don't brush/erase same tile multiple times unless they move away and back @TODO should just be only once, but that needs another data structure - if (!STBTE__IS_ACTIVE(id)) { - if (stbte__ui.active_event == STBTE__leftdown) - stbte__brush(tm, mapx, mapy); - else - stbte__erase(tm, mapx, mapy, stbte__ui.brush_state); - stbte__ui.active_id = id; // switch to this map square so we don't rebrush IT multiple times - } - } - break; - case STBTE__leftdown: - if (STBTE__IS_HOT(id) && STBTE__INACTIVE()) { - stbte__activate(id); - stbte__begin_undo(tm); - stbte__brush(tm, mapx, mapy); - } - break; - case STBTE__rightdown: - if (STBTE__IS_HOT(id) && STBTE__INACTIVE()) { - stbte__activate(id); - stbte__begin_undo(tm); - if (stbte__erase(tm, mapx, mapy, STBTE__erase_any) == STBTE__erase_brushonly) - stbte__ui.brush_state = STBTE__erase_brushonly; - else - stbte__ui.brush_state = STBTE__erase_any; - } - break; - case STBTE__leftup: - case STBTE__rightup: - if (STBTE__IS_MAP_ACTIVE()) { - stbte__end_undo(tm); - stbte__activate(0); - } - break; - } - break; - -#ifdef STBTE_ALLOW_LINK - case STBTE__tool_link: - switch (stbte__ui.event) { - case STBTE__leftdown: - if (STBTE__IS_HOT(id) && STBTE__INACTIVE()) { - stbte__activate(id); - stbte__ui.linking = 1; - stbte__ui.sx = mapx; - stbte__ui.sy = mapy; - // @TODO: undo - } - break; - case STBTE__leftup: - if (STBTE__IS_HOT(id) && STBTE__IS_MAP_ACTIVE()) { - if ((mapx != stbte__ui.sx || mapy != stbte__ui.sy) && - STBTE_ALLOW_LINK(tm->data[stbte__ui.sy][stbte__ui.sx], tm->props[stbte__ui.sy][stbte__ui.sx], - tm->data[mapy][mapx], tm->props[mapy][mapx])) - stbte__set_link(tm, stbte__ui.sx, stbte__ui.sy, mapx, mapy, STBTE__undo_block); - else - stbte__set_link(tm, stbte__ui.sx, stbte__ui.sy, -1,-1, STBTE__undo_block); - stbte__ui.linking = 0; - stbte__activate(0); - } - break; - - case STBTE__rightdown: - if (STBTE__IS_ACTIVE(id)) { - stbte__activate(0); - stbte__ui.linking = 0; - } - break; - } - break; -#endif - - case STBTE__tool_erase: - switch (stbte__ui.event) { - case STBTE__mousemove: - if (STBTE__IS_MAP_ACTIVE() && over) - stbte__erase(tm, mapx, mapy, STBTE__erase_all); - break; - case STBTE__leftdown: - if (STBTE__IS_HOT(id) && STBTE__INACTIVE()) { - stbte__activate(id); - stbte__begin_undo(tm); - stbte__erase(tm, mapx, mapy, STBTE__erase_all); - } - break; - case STBTE__leftup: - if (STBTE__IS_MAP_ACTIVE()) { - stbte__end_undo(tm); - stbte__activate(0); - } - break; - } - break; - - case STBTE__tool_select: - if (STBTE__IS_HOT(id)) { - switch (stbte__ui.event) { - case STBTE__leftdown: - if (STBTE__INACTIVE()) { - // if we're clicking in an existing selection... - if (stbte__ui.has_selection) { - if ( mapx >= stbte__ui.select_x0 && mapx <= stbte__ui.select_x1 - && mapy >= stbte__ui.select_y0 && mapy <= stbte__ui.select_y1) - { - stbte__ui.dragging = 1; - stbte__ui.drag_x = stbte__ui.select_x0; - stbte__ui.drag_y = stbte__ui.select_y0; - stbte__ui.drag_w = stbte__ui.select_x1 - stbte__ui.select_x0 + 1; - stbte__ui.drag_h = stbte__ui.select_y1 - stbte__ui.select_y0 + 1; - stbte__ui.drag_offx = mapx - stbte__ui.select_x0; - stbte__ui.drag_offy = mapy - stbte__ui.select_y0; - } - } - stbte__ui.has_selection = 0; // no selection until it completes - stbte__activate_map(mapx,mapy); - } - break; - case STBTE__leftup: - if (STBTE__IS_MAP_ACTIVE()) { - if (stbte__ui.dragging) { - stbte__drag_place(tm, mapx,mapy); - stbte__ui.dragging = 0; - stbte__activate(0); - } else { - stbte__select_rect(tm, stbte__ui.sx, stbte__ui.sy, mapx, mapy); - stbte__activate(0); - } - } - break; - case STBTE__rightdown: - stbte__ui.has_selection = 0; - break; - } - } - break; - - case STBTE__tool_rect: - if (STBTE__IS_HOT(id)) { - switch (stbte__ui.event) { - case STBTE__leftdown: - if (STBTE__INACTIVE()) - stbte__activate_map(mapx,mapy); - break; - case STBTE__leftup: - if (STBTE__IS_MAP_ACTIVE()) { - stbte__fillrect(tm, stbte__ui.sx, stbte__ui.sy, mapx, mapy, 1); - stbte__activate(0); - } - break; - case STBTE__rightdown: - if (STBTE__INACTIVE()) - stbte__activate_map(mapx,mapy); - break; - case STBTE__rightup: - if (STBTE__IS_MAP_ACTIVE()) { - stbte__fillrect(tm, stbte__ui.sx, stbte__ui.sy, mapx, mapy, 0); - stbte__activate(0); - } - break; - } - } - break; - - - case STBTE__tool_eyedrop: - switch (stbte__ui.event) { - case STBTE__leftdown: - if (STBTE__IS_HOT(id) && STBTE__INACTIVE()) - stbte__eyedrop(tm,mapx,mapy); - break; - } - break; - } -} - -static void stbte__start_paste(stbte_tilemap *tm) -{ - if (stbte__ui.has_copy) { - stbte__ui.pasting = 1; - stbte__activate(STBTE__ID(STBTE__toolbarB,3)); - } -} - -static void stbte__toolbar(stbte_tilemap *tm, int x0, int y0, int w, int h) -{ - int i; - int estimated_width = 13 * STBTE__num_tool + 8+8+ 120+4 - 30; - int x = x0 + w/2 - estimated_width/2; - int y = y0+1; - - for (i=0; i < STBTE__num_tool; ++i) { - int highlight=0, disable=0; - highlight = (stbte__ui.tool == i); - if (i == STBTE__tool_undo || i == STBTE__tool_showgrid) - x += 8; - if (i == STBTE__tool_showgrid && stbte__ui.show_grid) - highlight = 1; - if (i == STBTE__tool_showlinks && stbte__ui.show_links) - highlight = 1; - if (i == STBTE__tool_fill) - continue; - #ifndef STBTE_ALLOW_LINK - if (i == STBTE__tool_link || i == STBTE__tool_showlinks) - disable = 1; - #endif - if (i == STBTE__tool_undo && !stbte__undo_available(tm)) - disable = 1; - if (i == STBTE__tool_redo && !stbte__redo_available(tm)) - disable = 1; - if (stbte__button_icon(STBTE__ctoolbar_button, toolchar[i], x, y, 13, STBTE__ID(STBTE__toolbarA, i), highlight, disable)) { - switch (i) { - case STBTE__tool_eyedrop: - stbte__ui.eyedrop_last_layer = tm->num_layers; // flush eyedropper state - // fallthrough - default: - stbte__ui.tool = i; - stbte__ui.has_selection = 0; - break; - case STBTE__tool_showlinks: - stbte__ui.show_links = !stbte__ui.show_links; - break; - case STBTE__tool_showgrid: - stbte__ui.show_grid = (stbte__ui.show_grid+1)%3; - break; - case STBTE__tool_undo: - stbte__undo(tm); - break; - case STBTE__tool_redo: - stbte__redo(tm); - break; - } - } - x += 13; - } - - x += 8; - if (stbte__button(STBTE__ctoolbar_button, "cut" , x, y,10, 40, STBTE__ID(STBTE__toolbarB,0), 0, !stbte__ui.has_selection)) - stbte__copy_cut(tm, 1); - x += 42; - if (stbte__button(STBTE__ctoolbar_button, "copy" , x, y, 5, 40, STBTE__ID(STBTE__toolbarB,1), 0, !stbte__ui.has_selection)) - stbte__copy_cut(tm, 0); - x += 42; - if (stbte__button(STBTE__ctoolbar_button, "paste", x, y, 0, 40, STBTE__ID(STBTE__toolbarB,2), stbte__ui.pasting, !stbte__ui.has_copy)) - stbte__start_paste(tm); -} - -#define STBTE__TEXTCOLOR(n) stbte__color_table[n][STBTE__text][STBTE__idle] - -static int stbte__info_value(char *label, int x, int y, int val, int digits, int id) -{ - if (stbte__ui.event == STBTE__paint) { - int off = 9-stbte__get_char_width(label[0]); - char text[16]; - sprintf(text, label, digits, val); - stbte__draw_text_core(x+off,y, text, 999, STBTE__TEXTCOLOR(STBTE__cpanel),1); - } - if (id) { - x += 9+7*digits+4; - if (stbte__minibutton(STBTE__cmapsize, x,y, '+', STBTE__ID2(id,1,0))) - val += (stbte__ui.shift ? 10 : 1); - x += 9; - if (stbte__minibutton(STBTE__cmapsize, x,y, '-', STBTE__ID2(id,2,0))) - val -= (stbte__ui.shift ? 10 : 1); - if (val < 1) val = 1; else if (val > 4096) val = 4096; - } - return val; -} - -static void stbte__info(stbte_tilemap *tm, int x0, int y0, int w, int h) -{ - int mode = stbte__ui.panel[STBTE__panel_info].mode; - int s = 11+7*tm->digits+4+15; - int x,y; - int in_region; - - x = x0+2; - y = y0+2; - tm->max_x = stbte__info_value("w:%*d",x,y, tm->max_x, tm->digits, STBTE__ID(STBTE__info,0)); - if (mode) - x += s; - else - y += 11; - tm->max_y = stbte__info_value("h:%*d",x,y, tm->max_y, tm->digits, STBTE__ID(STBTE__info,1)); - x = x0+2; - y += 11; - in_region = (stbte__ui.hot_id & 127) == STBTE__map; - stbte__info_value(in_region ? "x:%*d" : "x:",x,y, (stbte__ui.hot_id>>19)&4095, tm->digits, 0); - if (mode) - x += s; - else - y += 11; - stbte__info_value(in_region ? "y:%*d" : "y:",x,y, (stbte__ui.hot_id>> 7)&4095, tm->digits, 0); - y += 15; - x = x0+2; - stbte__draw_text(x,y,"brush:",40,STBTE__TEXTCOLOR(STBTE__cpanel)); - if (tm->cur_tile >= 0) - STBTE_DRAW_TILE(x+43,y-3,tm->tiles[tm->cur_tile].id,1,0); -} - -static void stbte__layers(stbte_tilemap *tm, int x0, int y0, int w, int h) -{ - static char *propmodes[3] = { - "default", "always", "never" - }; - int num_rows; - int i, y, n; - int x1 = x0+w; - int y1 = y0+h; - int xoff = 20; - - if (tm->has_layer_names) { - int side = stbte__ui.panel[STBTE__panel_layers].side; - xoff = stbte__region[side].width - 42; - xoff = (xoff < tm->layername_width + 10 ? xoff : tm->layername_width + 10); - } - - x0 += 2; - y0 += 5; - if (!tm->has_layer_names) { - if (stbte__ui.event == STBTE__paint) { - stbte__draw_text(x0,y0, "Layers", w-4, STBTE__TEXTCOLOR(STBTE__cpanel)); - } - y0 += 11; - } - num_rows = (y1-y0)/15; -#ifndef STBTE_NO_PROPS - --num_rows; -#endif - y = y0; - for (i=0; i < tm->num_layers; ++i) { - char text[3], *str = (char *) tm->layerinfo[i].name; - static char lockedchar[3] = { 'U', 'P', 'L' }; - int locked = tm->layerinfo[i].locked; - int disabled = (tm->solo_layer >= 0 && tm->solo_layer != i); - if (i-tm->layer_scroll >= 0 && i-tm->layer_scroll < num_rows) { - if (str == NULL) - sprintf(str=text, "%2d", i+1); - if (stbte__button(STBTE__clayer_button, str, x0,y,(i+1<10)*2,xoff-2, STBTE__ID(STBTE__layer,i), tm->cur_layer==i,0)) - tm->cur_layer = (tm->cur_layer == i ? -1 : i); - if (stbte__layerbutton(x0+xoff + 0,y+1,'H',STBTE__ID(STBTE__hide,i), tm->layerinfo[i].hidden,disabled,STBTE__clayer_hide)) - tm->layerinfo[i].hidden = !tm->layerinfo[i].hidden; - if (stbte__layerbutton(x0+xoff + 12,y+1,lockedchar[locked],STBTE__ID(STBTE__lock,i), locked!=0,disabled,STBTE__clayer_lock)) - tm->layerinfo[i].locked = (locked+1)%3; - if (stbte__layerbutton(x0+xoff + 24,y+1,'S',STBTE__ID(STBTE__solo,i), tm->solo_layer==i,0,STBTE__clayer_solo)) - tm->solo_layer = (tm->solo_layer == i ? -1 : i); - y += 15; - } - } - stbte__scrollbar(x1-4, y0,y-2, &tm->layer_scroll, 0, tm->num_layers, num_rows, STBTE__ID(STBTE__scrollbar_id, STBTE__layer)); -#ifndef STBTE_NO_PROPS - n = stbte__text_width("prop:")+2; - stbte__draw_text(x0,y+2, "prop:", w, STBTE__TEXTCOLOR(STBTE__cpanel)); - i = w - n - 4; - if (i > 50) i = 50; - if (stbte__button(STBTE__clayer_button, propmodes[tm->propmode], x0+n,y,0,i, STBTE__ID(STBTE__layer,256), 0,0)) - tm->propmode = (tm->propmode+1)%3; -#endif -} - -static void stbte__categories(stbte_tilemap *tm, int x0, int y0, int w, int h) -{ - int s=11, x,y, i; - int num_rows = h / s; - - w -= 4; - x = x0+2; - y = y0+4; - if (tm->category_scroll == 0) { - if (stbte__category_button("*ALL*", x,y, w, STBTE__ID(STBTE__categories, 65535), tm->cur_category == -1)) { - stbte__choose_category(tm, -1); - } - y += s; - } - - for (i=0; i < tm->num_categories; ++i) { - if (i+1 - tm->category_scroll >= 0 && i+1 - tm->category_scroll < num_rows) { - if (y + 10 > y0+h) - return; - if (stbte__category_button(tm->categories[i], x,y,w, STBTE__ID(STBTE__categories,i), tm->cur_category == i)) - stbte__choose_category(tm, i); - y += s; - } - } - stbte__scrollbar(x0+w, y0+4, y0+h-4, &tm->category_scroll, 0, tm->num_categories+1, num_rows, STBTE__ID(STBTE__scrollbar_id, STBTE__categories)); -} - -static void stbte__tile_in_palette(stbte_tilemap *tm, int x, int y, int slot) -{ - stbte__tileinfo *t = &tm->tiles[slot]; - int x0=x, y0=y, x1 = x+tm->palette_spacing_x - 1, y1 = y+tm->palette_spacing_y; - int id = STBTE__ID(STBTE__palette, slot); - int over = stbte__hittest(x0,y0,x1,y1, id); - switch (stbte__ui.event) { - case STBTE__paint: - stbte__draw_rect(x,y,x+tm->palette_spacing_x-1,y+tm->palette_spacing_x-1, STBTE_COLOR_TILEPALETTE_BACKGROUND); - STBTE_DRAW_TILE(x,y,t->id, slot == tm->cur_tile,0); - if (slot == tm->cur_tile) - stbte__draw_frame_delayed(x-1,y-1,x+tm->palette_spacing_x,y+tm->palette_spacing_y, STBTE_COLOR_TILEPALETTE_OUTLINE); - break; - default: - if (stbte__button_core(id)) - tm->cur_tile = slot; - break; - } -} - -static void stbte__palette_of_tiles(stbte_tilemap *tm, int x0, int y0, int w, int h) -{ - int i,x,y; - int num_vis_rows = (h-6) / tm->palette_spacing_y; - int num_columns = (w-2-6) / tm->palette_spacing_x; - int num_total_rows; - int column,row; - int x1 = x0+w, y1=y0+h; - x = x0+2; - y = y0+6; - - if (num_columns == 0) - return; - - num_total_rows = (tm->cur_palette_count + num_columns-1) / num_columns; // ceil() - - column = 0; - row = -tm->palette_scroll; - for (i=0; i < tm->num_tiles; ++i) { - stbte__tileinfo *t = &tm->tiles[i]; - - // filter based on category - if (tm->cur_category >= 0 && t->category_id != tm->cur_category) - continue; - - // display it - if (row >= 0 && row < num_vis_rows) { - x = x0 + 2 + tm->palette_spacing_x * column; - y = y0 + 6 + tm->palette_spacing_y * row; - stbte__tile_in_palette(tm,x,y,i); - } - - ++column; - if (column == num_columns) { - column = 0; - ++row; - } - } - stbte__flush_delay(); - stbte__scrollbar(x1-4, y0+6, y1-2, &tm->palette_scroll, 0, num_total_rows, num_vis_rows, STBTE__ID(STBTE__scrollbar_id, STBTE__palette)); -} - -static float stbte__linear_remap(float n, float x0, float x1, float y0, float y1) -{ - return (n-x0)/(x1-x0)*(y1-y0) + y0; -} - -static float stbte__saved; -static void stbte__props_panel(stbte_tilemap *tm, int x0, int y0, int w, int h) -{ - int x1 = x0+w, y1 = y0+h; - int i; - int y = y0 + 5, x = x0+2; - int slider_width = 60; - int mx,my; - float *p; - short *data; - if (!stbte__is_single_selection()) - return; - mx = stbte__ui.select_x0; - my = stbte__ui.select_y0; - p = tm->props[my][mx]; - data = tm->data[my][mx]; - for (i=0; i < STBTE_MAX_PROPERTIES; ++i) { - unsigned int n = STBTE_PROP_TYPE(i, data, p); - if (n) { - char *s = STBTE_PROP_NAME(i, data, p); - if (s == NULL) s = ""; - switch (n & 3) { - case STBTE_PROP_bool: { - int flag = (int) p[i]; - if (stbte__layerbutton(x,y, flag ? 'x' : ' ', STBTE__ID(STBTE__prop_flag,i), flag, 0, 2)) { - stbte__begin_undo(tm); - stbte__undo_record_prop_float(tm,mx,my,i,(float) flag); - p[i] = (float) !flag; - stbte__end_undo(tm); - } - stbte__draw_text(x+13,y+1,s,x1-(x+13)-2,STBTE__TEXTCOLOR(STBTE__cpanel)); - y += 13; - break; - } - case STBTE_PROP_int: { - int a = (int) STBTE_PROP_MIN(i,data,p); - int b = (int) STBTE_PROP_MAX(i,data,p); - int v = (int) p[i] - a; - if (a+v != p[i] || v < 0 || v > b-a) { - if (v < 0) v = 0; - if (v > b-a) v = b-a; - p[i] = (float) (a+v); // @TODO undo - } - switch (stbte__slider(x, slider_width, y+7, b-a, &v, STBTE__ID(STBTE__prop_int,i))) - { - case STBTE__begin: - stbte__saved = p[i]; - // fallthrough - case STBTE__change: - p[i] = (float) (a+v); // @TODO undo - break; - case STBTE__end: - if (p[i] != stbte__saved) { - stbte__begin_undo(tm); - stbte__undo_record_prop_float(tm,mx,my,i,stbte__saved); - stbte__end_undo(tm); - } - break; - } - stbte__draw_text(x+slider_width+2,y+2, s, x1-1-(x+slider_width+2), STBTE__TEXTCOLOR(STBTE__cpanel)); - y += 12; - break; - } - case STBTE_PROP_float: { - float a = (float) STBTE_PROP_MIN(i, data,p); - float b = (float) STBTE_PROP_MAX(i, data,p); - float c = STBTE_PROP_FLOAT_SCALE(i, data, p); - float old; - if (p[i] < a || p[i] > b) { - // @TODO undo - if (p[i] < a) p[i] = a; - if (p[i] > b) p[i] = b; - } - old = p[i]; - switch (stbte__float_control(x, y, 50, a, b, c, "%8.4f", &p[i], STBTE__layer,STBTE__ID(STBTE__prop_float,i))) { - case STBTE__begin: - stbte__saved = old; - break; - case STBTE__end: - if (stbte__saved != p[i]) { - stbte__begin_undo(tm); - stbte__undo_record_prop_float(tm,mx,my,i, stbte__saved); - stbte__end_undo(tm); - } - break; - } - stbte__draw_text(x+53,y+1, s, x1-1-(x+53), STBTE__TEXTCOLOR(STBTE__cpanel)); - y += 12; - break; - } - } - } - } -} - -static int stbte__cp_mode, stbte__cp_aspect, stbte__cp_state, stbte__cp_index, stbte__save, stbte__cp_altered, stbte__color_copy; -#ifdef STBTE__COLORPICKER -static void stbte__dump_colorstate(void) -{ - int i,j,k; - printf("static int stbte__color_table[STBTE__num_color_modes][STBTE__num_color_aspects][STBTE__num_color_states] =\n"); - printf("{\n"); - printf(" {\n"); - for (k=0; k < STBTE__num_color_modes; ++k) { - for (j=0; j < STBTE__num_color_aspects; ++j) { - printf(" { "); - for (i=0; i < STBTE__num_color_states; ++i) { - printf("0x%06x, ", stbte__color_table[k][j][i]); - } - printf("},\n"); - } - if (k+1 < STBTE__num_color_modes) - printf(" }, {\n"); - else - printf(" },\n"); - } - printf("};\n"); -} - -static void stbte__colorpicker(int x0, int y0, int w, int h) -{ - int x1 = x0+w, y1 = y0+h, x,y, i; - - x = x0+2; y = y0+6; - - y += 5; - x += 8; - - - { - int color = stbte__color_table[stbte__cp_mode][stbte__cp_aspect][stbte__cp_index]; - int rgb[3]; - if (stbte__cp_altered && stbte__cp_index == STBTE__idle) - color = stbte__save; - - if (stbte__minibutton(STBTE__cmapsize, x1-20,y+ 5, 'C', STBTE__ID2(STBTE__colorpick_id,4,0))) - stbte__color_copy = color; - if (stbte__minibutton(STBTE__cmapsize, x1-20,y+15, 'P', STBTE__ID2(STBTE__colorpick_id,4,1))) - color = stbte__color_copy; - - rgb[0] = color >> 16; rgb[1] = (color>>8)&255; rgb[2] = color & 255; - for (i=0; i < 3; ++i) { - if (stbte__slider(x+8,64, y, 255, rgb+i, STBTE__ID2(STBTE__colorpick_id,3,i)) > 0) - stbte__dump_colorstate(); - y += 15; - } - if (stbte__ui.event != STBTE__paint && stbte__ui.event != STBTE__tick) - stbte__color_table[stbte__cp_mode][stbte__cp_aspect][stbte__cp_index] = (rgb[0]<<16)|(rgb[1]<<8)|(rgb[2]); - } - - y += 5; - - // states - x = x0+2+35; - if (stbte__ui.event == STBTE__paint) { - static char *states[] = { "idle", "over", "down", "down&over", "selected", "selected&over", "disabled" }; - stbte__draw_text(x, y+1, states[stbte__cp_index], x1-x-1, 0xffffff); - } - - x = x0+24; y += 12; - - for (i=3; i >= 0; --i) { - int state = 0 != (stbte__cp_state & (1 << i)); - if (stbte__layerbutton(x,y, "OASD"[i], STBTE__ID2(STBTE__colorpick_id, 0,i), state,0, STBTE__clayer_button)) { - stbte__cp_state ^= (1 << i); - stbte__cp_index = stbte__state_to_index[0][0][0][stbte__cp_state]; - } - x += 16; - } - x = x0+2; y += 18; - - for (i=0; i < 3; ++i) { - static char *labels[] = { "Base", "Edge", "Text" }; - if (stbte__button(STBTE__ctoolbar_button, labels[i], x,y,0,36, STBTE__ID2(STBTE__colorpick_id,1,i), stbte__cp_aspect==i,0)) - stbte__cp_aspect = i; - x += 40; - } - - y += 18; - x = x0+2; - - for (i=0; i < STBTE__num_color_modes; ++i) { - if (stbte__button(STBTE__ctoolbar_button, stbte__color_names[i], x, y, 0,80, STBTE__ID2(STBTE__colorpick_id,2,i), stbte__cp_mode == i,0)) - stbte__cp_mode = i; - y += 12; - } - - // make the currently selected aspect flash, unless we're actively dragging color slider etc - if (stbte__ui.event == STBTE__tick) { - stbte__save = stbte__color_table[stbte__cp_mode][stbte__cp_aspect][STBTE__idle]; - if ((stbte__ui.active_id & 127) != STBTE__colorpick_id) { - if ((stbte__ui.ms_time & 2047) < 200) { - stbte__color_table[stbte__cp_mode][stbte__cp_aspect][STBTE__idle] ^= 0x1f1f1f; - stbte__cp_altered = 1; - } - } - } -} -#endif - -static void stbte__editor_traverse(stbte_tilemap *tm) -{ - int i,j,i0,j0,i1,j1,n; - - if (tm == NULL) - return; - if (stbte__ui.x0 == stbte__ui.x1 || stbte__ui.y0 == stbte__ui.y1) - return; - - stbte__prepare_tileinfo(tm); - - stbte__compute_panel_locations(tm); // @OPTIMIZE: we don't need to recompute this every time - - if (stbte__ui.event == STBTE__paint) { - // fill screen with border - stbte__draw_rect(stbte__ui.x0, stbte__ui.y0, stbte__ui.x1, stbte__ui.y1, STBTE_COLOR_TILEMAP_BORDER); - // fill tilemap with tilemap background - stbte__draw_rect(stbte__ui.x0 - tm->scroll_x, stbte__ui.y0 - tm->scroll_y, - stbte__ui.x0 - tm->scroll_x + tm->spacing_x * tm->max_x, - stbte__ui.y0 - tm->scroll_y + tm->spacing_y * tm->max_y, STBTE_COLOR_TILEMAP_BACKGROUND); - } - - // step 1: traverse all the tilemap data... - - i0 = (tm->scroll_x - tm->spacing_x) / tm->spacing_x; - j0 = (tm->scroll_y - tm->spacing_y) / tm->spacing_y; - i1 = (tm->scroll_x + stbte__ui.x1 - stbte__ui.x0) / tm->spacing_x + 1; - j1 = (tm->scroll_y + stbte__ui.y1 - stbte__ui.y0) / tm->spacing_y + 1; - - if (i0 < 0) i0 = 0; - if (j0 < 0) j0 = 0; - if (i1 > tm->max_x) i1 = tm->max_x; - if (j1 > tm->max_y) j1 = tm->max_y; - - if (stbte__ui.event == STBTE__paint) { - // draw all of layer 0, then all of layer 1, etc, instead of old - // way which drew entire stack of each tile at once - for (n=0; n < tm->num_layers; ++n) { - for (j=j0; j < j1; ++j) { - for (i=i0; i < i1; ++i) { - int x = stbte__ui.x0 + i * tm->spacing_x - tm->scroll_x; - int y = stbte__ui.y0 + j * tm->spacing_y - tm->scroll_y; - stbte__tile_paint(tm, x, y, i, j, n); - } - } - if (n == 0 && stbte__ui.show_grid == 1) { - int x = stbte__ui.x0 + i0 * tm->spacing_x - tm->scroll_x; - int y = stbte__ui.y0 + j0 * tm->spacing_y - tm->scroll_y; - for (i=0; x < stbte__ui.x1 && i <= i1; ++i, x += tm->spacing_x) - stbte__draw_rect(x, stbte__ui.y0, x+1, stbte__ui.y1, STBTE_COLOR_GRID); - for (j=0; y < stbte__ui.y1 && j <= j1; ++j, y += tm->spacing_y) - stbte__draw_rect(stbte__ui.x0, y, stbte__ui.x1, y+1, STBTE_COLOR_GRID); - } - } - } - - if (stbte__ui.event == STBTE__paint) { - // draw grid on top of everything except UI - if (stbte__ui.show_grid == 2) { - int x = stbte__ui.x0 + i0 * tm->spacing_x - tm->scroll_x; - int y = stbte__ui.y0 + j0 * tm->spacing_y - tm->scroll_y; - for (i=0; x < stbte__ui.x1 && i <= i1; ++i, x += tm->spacing_x) - stbte__draw_rect(x, stbte__ui.y0, x+1, stbte__ui.y1, STBTE_COLOR_GRID); - for (j=0; y < stbte__ui.y1 && j <= j1; ++j, y += tm->spacing_y) - stbte__draw_rect(stbte__ui.x0, y, stbte__ui.x1, y+1, STBTE_COLOR_GRID); - } - } - - for (j=j0; j < j1; ++j) { - for (i=i0; i < i1; ++i) { - int x = stbte__ui.x0 + i * tm->spacing_x - tm->scroll_x; - int y = stbte__ui.y0 + j * tm->spacing_y - tm->scroll_y; - stbte__tile(tm, x, y, i, j); - } - } - - if (stbte__ui.event == STBTE__paint) { - // draw the selection border - if (stbte__ui.has_selection) { - int x0,y0,x1,y1; - x0 = stbte__ui.x0 + (stbte__ui.select_x0 ) * tm->spacing_x - tm->scroll_x; - y0 = stbte__ui.y0 + (stbte__ui.select_y0 ) * tm->spacing_y - tm->scroll_y; - x1 = stbte__ui.x0 + (stbte__ui.select_x1 + 1) * tm->spacing_x - tm->scroll_x + 1; - y1 = stbte__ui.y0 + (stbte__ui.select_y1 + 1) * tm->spacing_y - tm->scroll_y + 1; - stbte__draw_frame(x0,y0,x1,y1, (stbte__ui.ms_time & 256 ? STBTE_COLOR_SELECTION_OUTLINE1 : STBTE_COLOR_SELECTION_OUTLINE2)); - } - - stbte__flush_delay(); // draw a dynamic link on top of the queued links - - #ifdef STBTE_ALLOW_LINK - if (stbte__ui.linking && STBTE__IS_MAP_HOT()) { - int x0,y0,x1,y1; - int color; - int ex = ((stbte__ui.hot_id >> 19) & 4095); - int ey = ((stbte__ui.hot_id >> 7) & 4095); - x0 = stbte__ui.x0 + (stbte__ui.sx ) * tm->spacing_x - tm->scroll_x + (tm->spacing_x>>1)+1; - y0 = stbte__ui.y0 + (stbte__ui.sy ) * tm->spacing_y - tm->scroll_y + (tm->spacing_y>>1)+1; - x1 = stbte__ui.x0 + (ex ) * tm->spacing_x - tm->scroll_x + (tm->spacing_x>>1)-1; - y1 = stbte__ui.y0 + (ey ) * tm->spacing_y - tm->scroll_y + (tm->spacing_y>>1)-1; - if (STBTE_ALLOW_LINK(tm->data[stbte__ui.sy][stbte__ui.sx], tm->props[stbte__ui.sy][stbte__ui.sx], tm->data[ey][ex], tm->props[ey][ex])) - color = STBTE_LINK_COLOR_DRAWING; - else - color = STBTE_LINK_COLOR_DISALLOWED; - stbte__draw_link(x0,y0,x1,y1, color); - } - #endif - } - stbte__flush_delay(); - - // step 2: traverse the panels - for (i=0; i < STBTE__num_panel; ++i) { - stbte__panel *p = &stbte__ui.panel[i]; - if (stbte__ui.event == STBTE__paint) { - stbte__draw_box(p->x0,p->y0,p->x0+p->width,p->y0+p->height, STBTE__cpanel, STBTE__idle); - } - // obscure tilemap data underneath panel - stbte__hittest(p->x0,p->y0,p->x0+p->width,p->y0+p->height, STBTE__ID2(STBTE__panel, i, 0)); - switch (i) { - case STBTE__panel_toolbar: - if (stbte__ui.event == STBTE__paint) - stbte__draw_rect(p->x0,p->y0,p->x0+p->width,p->y0+p->height, stbte__color_table[STBTE__ctoolbar][STBTE__base][STBTE__idle]); - stbte__toolbar(tm,p->x0,p->y0,p->width,p->height); - break; - case STBTE__panel_info: - stbte__info(tm,p->x0,p->y0,p->width,p->height); - break; - case STBTE__panel_layers: - stbte__layers(tm,p->x0,p->y0,p->width,p->height); - break; - case STBTE__panel_categories: - stbte__categories(tm,p->x0,p->y0,p->width,p->height); - break; - case STBTE__panel_colorpick: -#ifdef STBTE__COLORPICKER - stbte__colorpicker(p->x0,p->y0,p->width,p->height); -#endif - break; - case STBTE__panel_tiles: - // erase boundary between categories and tiles if they're on same side - if (stbte__ui.event == STBTE__paint && p->side == stbte__ui.panel[STBTE__panel_categories].side) - stbte__draw_rect(p->x0+1,p->y0-1,p->x0+p->width-1,p->y0+1, stbte__color_table[STBTE__cpanel][STBTE__base][STBTE__idle]); - stbte__palette_of_tiles(tm,p->x0,p->y0,p->width,p->height); - break; - case STBTE__panel_props: - stbte__props_panel(tm,p->x0,p->y0,p->width,p->height); - break; - } - // draw the panel side selectors - for (j=0; j < 2; ++j) { - int result; - if (i == STBTE__panel_toolbar) continue; - result = stbte__microbutton(p->x0+p->width - 1 - 2*4 + 4*j,p->y0+2,3, STBTE__ID2(STBTE__panel, i, j+1), STBTE__cpanel_sider+j); - if (result) { - switch (j) { - case 0: p->side = result > 0 ? STBTE__side_left : STBTE__side_right; break; - case 1: p->delta_height += result; break; - } - } - } - } - - if (stbte__ui.panel[STBTE__panel_categories].delta_height < -5) stbte__ui.panel[STBTE__panel_categories].delta_height = -5; - if (stbte__ui.panel[STBTE__panel_layers ].delta_height < -5) stbte__ui.panel[STBTE__panel_layers ].delta_height = -5; - - - // step 3: traverse the regions to place expander controls on them - for (i=0; i < 2; ++i) { - if (stbte__region[i].active) { - int x = stbte__region[i].x; - int width; - if (i == STBTE__side_left) - width = stbte__ui.left_width , x += stbte__region[i].width + 1; - else - width = -stbte__ui.right_width, x -= 6; - if (stbte__microbutton_dragger(x, stbte__region[i].y+2, 5, STBTE__ID(STBTE__region,i), &width)) { - // if non-0, it is expanding, so retract it - if (stbte__region[i].retracted == 0.0) - stbte__region[i].retracted = 0.01f; - else - stbte__region[i].retracted = 0.0; - } - if (i == STBTE__side_left) - stbte__ui.left_width = width; - else - stbte__ui.right_width = -width; - if (stbte__ui.event == STBTE__tick) { - if (stbte__region[i].retracted && stbte__region[i].retracted < 1.0f) { - stbte__region[i].retracted += stbte__ui.dt*4; - if (stbte__region[i].retracted > 1) - stbte__region[i].retracted = 1; - } - } - } - } - - if (stbte__ui.event == STBTE__paint && stbte__ui.alert_msg) { - int w = stbte__text_width(stbte__ui.alert_msg); - int x = (stbte__ui.x0+stbte__ui.x1)/2; - int y = (stbte__ui.y0+stbte__ui.y1)*5/6; - stbte__draw_rect (x-w/2-4,y-8, x+w/2+4,y+8, 0x604020); - stbte__draw_frame(x-w/2-4,y-8, x+w/2+4,y+8, 0x906030); - stbte__draw_text (x-w/2,y-4, stbte__ui.alert_msg, w+1, 0xff8040); - } - -#ifdef STBTE_SHOW_CURSOR - if (stbte__ui.event == STBTE__paint) - stbte__draw_bitmap(stbte__ui.mx, stbte__ui.my, stbte__get_char_width(26), stbte__get_char_bitmap(26), 0xe0e0e0); -#endif - - if (stbte__ui.event == STBTE__tick && stbte__ui.alert_msg) { - stbte__ui.alert_timer -= stbte__ui.dt; - if (stbte__ui.alert_timer < 0) { - stbte__ui.alert_timer = 0; - stbte__ui.alert_msg = 0; - } - } - - if (stbte__ui.event == STBTE__paint) { - stbte__color_table[stbte__cp_mode][stbte__cp_aspect][STBTE__idle] = stbte__save; - stbte__cp_altered = 0; - } -} - -static void stbte__do_event(stbte_tilemap *tm) -{ - stbte__ui.next_hot_id = 0; - stbte__editor_traverse(tm); - stbte__ui.hot_id = stbte__ui.next_hot_id; - - // automatically cancel on mouse-up in case the object that triggered it - // doesn't exist anymore - if (stbte__ui.active_id) { - if (stbte__ui.event == STBTE__leftup || stbte__ui.event == STBTE__rightup) { - if (!stbte__ui.pasting) { - stbte__activate(0); - if (stbte__ui.undoing) - stbte__end_undo(tm); - stbte__ui.scrolling = 0; - stbte__ui.dragging = 0; - stbte__ui.linking = 0; - } - } - } - - // we could do this stuff in the widgets directly, but it would keep recomputing - // the same thing on every tile, which seems dumb. - - if (stbte__ui.pasting) { - if (STBTE__IS_MAP_HOT()) { - // compute pasting location based on last hot - stbte__ui.paste_x = ((stbte__ui.hot_id >> 19) & 4095) - (stbte__ui.copy_width >> 1); - stbte__ui.paste_y = ((stbte__ui.hot_id >> 7) & 4095) - (stbte__ui.copy_height >> 1); - } - } - if (stbte__ui.dragging) { - if (STBTE__IS_MAP_HOT()) { - stbte__ui.drag_dest_x = ((stbte__ui.hot_id >> 19) & 4095) - stbte__ui.drag_offx; - stbte__ui.drag_dest_y = ((stbte__ui.hot_id >> 7) & 4095) - stbte__ui.drag_offy; - } - } -} - -static void stbte__set_event(int event, int x, int y) -{ - stbte__ui.event = event; - stbte__ui.mx = x; - stbte__ui.my = y; - stbte__ui.dx = x - stbte__ui.last_mouse_x; - stbte__ui.dy = y - stbte__ui.last_mouse_y; - stbte__ui.last_mouse_x = x; - stbte__ui.last_mouse_y = y; - stbte__ui.accum_x += stbte__ui.dx; - stbte__ui.accum_y += stbte__ui.dy; -} - -void stbte_draw(stbte_tilemap *tm) -{ - stbte__ui.event = STBTE__paint; - stbte__editor_traverse(tm); -} - -void stbte_mouse_move(stbte_tilemap *tm, int x, int y, int shifted, int scrollkey) -{ - stbte__set_event(STBTE__mousemove, x,y); - stbte__ui.shift = shifted; - stbte__ui.scrollkey = scrollkey; - stbte__do_event(tm); -} - -void stbte_mouse_button(stbte_tilemap *tm, int x, int y, int right, int down, int shifted, int scrollkey) -{ - static int events[2][2] = { { STBTE__leftup , STBTE__leftdown }, - { STBTE__rightup, STBTE__rightdown } }; - stbte__set_event(events[right][down], x,y); - stbte__ui.shift = shifted; - stbte__ui.scrollkey = scrollkey; - - stbte__do_event(tm); -} - -void stbte_mouse_wheel(stbte_tilemap *tm, int x, int y, int vscroll) -{ - // not implemented yet -- need different way of hittesting -} - -void stbte_action(stbte_tilemap *tm, enum stbte_action act) -{ - switch (act) { - case STBTE_tool_select: stbte__ui.tool = STBTE__tool_select; break; - case STBTE_tool_brush: stbte__ui.tool = STBTE__tool_brush; break; - case STBTE_tool_erase: stbte__ui.tool = STBTE__tool_erase; break; - case STBTE_tool_rectangle: stbte__ui.tool = STBTE__tool_rect; break; - case STBTE_tool_eyedropper: stbte__ui.tool = STBTE__tool_eyedrop; break; - case STBTE_tool_link: stbte__ui.tool = STBTE__tool_link; break; - case STBTE_act_toggle_grid: stbte__ui.show_grid = (stbte__ui.show_grid+1) % 3; break; - case STBTE_act_toggle_links: stbte__ui.show_links ^= 1; break; - case STBTE_act_undo: stbte__undo(tm); break; - case STBTE_act_redo: stbte__redo(tm); break; - case STBTE_act_cut: stbte__copy_cut(tm, 1); break; - case STBTE_act_copy: stbte__copy_cut(tm, 0); break; - case STBTE_act_paste: stbte__start_paste(tm); break; - case STBTE_scroll_left: tm->scroll_x -= tm->spacing_x; break; - case STBTE_scroll_right: tm->scroll_x += tm->spacing_x; break; - case STBTE_scroll_up: tm->scroll_y -= tm->spacing_y; break; - case STBTE_scroll_down: tm->scroll_y += tm->spacing_y; break; - } -} - -void stbte_tick(stbte_tilemap *tm, float dt) -{ - stbte__ui.event = STBTE__tick; - stbte__ui.dt = dt; - stbte__do_event(tm); - stbte__ui.ms_time += (int) (dt * 1024) + 1; // make sure if time is superfast it always updates a little -} - -void stbte_mouse_sdl(stbte_tilemap *tm, const void *sdl_event, float xs, float ys, int xo, int yo) -{ -#ifdef _SDL_H - SDL_Event *event = (SDL_Event *) sdl_event; - SDL_Keymod km = SDL_GetModState(); - int shift = (km & KMOD_LCTRL) || (km & KMOD_RCTRL); - int scrollkey = 0 != SDL_GetKeyboardState(NULL)[SDL_SCANCODE_SPACE]; - switch (event->type) { - case SDL_MOUSEMOTION: - stbte_mouse_move(tm, (int) (xs*event->motion.x+xo), (int) (ys*event->motion.y+yo), shift, scrollkey); - break; - case SDL_MOUSEBUTTONUP: - stbte_mouse_button(tm, (int) (xs*event->button.x+xo), (int) (ys*event->button.y+yo), event->button.button != SDL_BUTTON_LEFT, 0, shift, scrollkey); - break; - case SDL_MOUSEBUTTONDOWN: - stbte_mouse_button(tm, (int) (xs*event->button.x+xo), (int) (ys*event->button.y+yo), event->button.button != SDL_BUTTON_LEFT, 1, shift, scrollkey); - break; - case SDL_MOUSEWHEEL: - stbte_mouse_wheel(tm, stbte__ui.mx, stbte__ui.my, event->wheel.y); - break; - } -#else - STBTE__NOTUSED(tm); - STBTE__NOTUSED(sdl_event); - STBTE__NOTUSED(xs); - STBTE__NOTUSED(ys); - STBTE__NOTUSED(xo); - STBTE__NOTUSED(yo); -#endif -} - -#endif // STB_TILEMAP_EDITOR_IMPLEMENTATION diff --git a/impeller/third_party/stb/stb/stb_truetype.h b/impeller/third_party/stb/stb/stb_truetype.h deleted file mode 100644 index d360d60920b30..0000000000000 --- a/impeller/third_party/stb/stb/stb_truetype.h +++ /dev/null @@ -1,3267 +0,0 @@ -// stb_truetype.h - v1.11 - public domain -// authored from 2009-2015 by Sean Barrett / RAD Game Tools -// -// This library processes TrueType files: -// parse files -// extract glyph metrics -// extract glyph shapes -// render glyphs to one-channel bitmaps with antialiasing (box filter) -// -// Todo: -// non-MS cmaps -// crashproof on bad data -// hinting? (no longer patented) -// cleartype-style AA? -// optimize: use simple memory allocator for intermediates -// optimize: build edge-list directly from curves -// optimize: rasterize directly from curves? -// -// ADDITIONAL CONTRIBUTORS -// -// Mikko Mononen: compound shape support, more cmap formats -// Tor Andersson: kerning, subpixel rendering -// -// Misc other: -// Ryan Gordon -// Simon Glass -// -// Bug/warning reports/fixes: -// "Zer" on mollyrocket (with fix) -// Cass Everitt -// stoiko (Haemimont Games) -// Brian Hook -// Walter van Niftrik -// David Gow -// David Given -// Ivan-Assen Ivanov -// Anthony Pesch -// Johan Duparc -// Hou Qiming -// Fabian "ryg" Giesen -// Martins Mozeiko -// Cap Petschulat -// Omar Cornut -// github:aloucks -// Peter LaValle -// Sergey Popov -// Giumo X. Clanjor -// Higor Euripedes -// Thomas Fields -// Derek Vinyard -// -// VERSION HISTORY -// -// 1.11 (2016-04-02) fix unused-variable warning -// 1.10 (2016-04-02) user-defined fabs(); rare memory leak; remove duplicate typedef -// 1.09 (2016-01-16) warning fix; avoid crash on outofmem; use allocation userdata properly -// 1.08 (2015-09-13) document stbtt_Rasterize(); fixes for vertical & horizontal edges -// 1.07 (2015-08-01) allow PackFontRanges to accept arrays of sparse codepoints; -// variant PackFontRanges to pack and render in separate phases; -// fix stbtt_GetFontOFfsetForIndex (never worked for non-0 input?); -// fixed an assert() bug in the new rasterizer -// replace assert() with STBTT_assert() in new rasterizer -// 1.06 (2015-07-14) performance improvements (~35% faster on x86 and x64 on test machine) -// also more precise AA rasterizer, except if shapes overlap -// remove need for STBTT_sort -// 1.05 (2015-04-15) fix misplaced definitions for STBTT_STATIC -// 1.04 (2015-04-15) typo in example -// 1.03 (2015-04-12) STBTT_STATIC, fix memory leak in new packing, various fixes -// -// Full history can be found at the end of this file. -// -// LICENSE -// -// This software is dual-licensed to the public domain and under the following -// license: you are granted a perpetual, irrevocable license to copy, modify, -// publish, and distribute this file as you see fit. -// -// USAGE -// -// Include this file in whatever places neeed to refer to it. In ONE C/C++ -// file, write: -// #define STB_TRUETYPE_IMPLEMENTATION -// before the #include of this file. This expands out the actual -// implementation into that C/C++ file. -// -// To make the implementation private to the file that generates the implementation, -// #define STBTT_STATIC -// -// Simple 3D API (don't ship this, but it's fine for tools and quick start) -// stbtt_BakeFontBitmap() -- bake a font to a bitmap for use as texture -// stbtt_GetBakedQuad() -- compute quad to draw for a given char -// -// Improved 3D API (more shippable): -// #include "stb_rect_pack.h" -- optional, but you really want it -// stbtt_PackBegin() -// stbtt_PackSetOversample() -- for improved quality on small fonts -// stbtt_PackFontRanges() -- pack and renders -// stbtt_PackEnd() -// stbtt_GetPackedQuad() -// -// "Load" a font file from a memory buffer (you have to keep the buffer loaded) -// stbtt_InitFont() -// stbtt_GetFontOffsetForIndex() -- use for TTC font collections -// -// Render a unicode codepoint to a bitmap -// stbtt_GetCodepointBitmap() -- allocates and returns a bitmap -// stbtt_MakeCodepointBitmap() -- renders into bitmap you provide -// stbtt_GetCodepointBitmapBox() -- how big the bitmap must be -// -// Character advance/positioning -// stbtt_GetCodepointHMetrics() -// stbtt_GetFontVMetrics() -// stbtt_GetCodepointKernAdvance() -// -// Starting with version 1.06, the rasterizer was replaced with a new, -// faster and generally-more-precise rasterizer. The new rasterizer more -// accurately measures pixel coverage for anti-aliasing, except in the case -// where multiple shapes overlap, in which case it overestimates the AA pixel -// coverage. Thus, anti-aliasing of intersecting shapes may look wrong. If -// this turns out to be a problem, you can re-enable the old rasterizer with -// #define STBTT_RASTERIZER_VERSION 1 -// which will incur about a 15% speed hit. -// -// ADDITIONAL DOCUMENTATION -// -// Immediately after this block comment are a series of sample programs. -// -// After the sample programs is the "header file" section. This section -// includes documentation for each API function. -// -// Some important concepts to understand to use this library: -// -// Codepoint -// Characters are defined by unicode codepoints, e.g. 65 is -// uppercase A, 231 is lowercase c with a cedilla, 0x7e30 is -// the hiragana for "ma". -// -// Glyph -// A visual character shape (every codepoint is rendered as -// some glyph) -// -// Glyph index -// A font-specific integer ID representing a glyph -// -// Baseline -// Glyph shapes are defined relative to a baseline, which is the -// bottom of uppercase characters. Characters extend both above -// and below the baseline. -// -// Current Point -// As you draw text to the screen, you keep track of a "current point" -// which is the origin of each character. The current point's vertical -// position is the baseline. Even "baked fonts" use this model. -// -// Vertical Font Metrics -// The vertical qualities of the font, used to vertically position -// and space the characters. See docs for stbtt_GetFontVMetrics. -// -// Font Size in Pixels or Points -// The preferred interface for specifying font sizes in stb_truetype -// is to specify how tall the font's vertical extent should be in pixels. -// If that sounds good enough, skip the next paragraph. -// -// Most font APIs instead use "points", which are a common typographic -// measurement for describing font size, defined as 72 points per inch. -// stb_truetype provides a point API for compatibility. However, true -// "per inch" conventions don't make much sense on computer displays -// since they different monitors have different number of pixels per -// inch. For example, Windows traditionally uses a convention that -// there are 96 pixels per inch, thus making 'inch' measurements have -// nothing to do with inches, and thus effectively defining a point to -// be 1.333 pixels. Additionally, the TrueType font data provides -// an explicit scale factor to scale a given font's glyphs to points, -// but the author has observed that this scale factor is often wrong -// for non-commercial fonts, thus making fonts scaled in points -// according to the TrueType spec incoherently sized in practice. -// -// ADVANCED USAGE -// -// Quality: -// -// - Use the functions with Subpixel at the end to allow your characters -// to have subpixel positioning. Since the font is anti-aliased, not -// hinted, this is very import for quality. (This is not possible with -// baked fonts.) -// -// - Kerning is now supported, and if you're supporting subpixel rendering -// then kerning is worth using to give your text a polished look. -// -// Performance: -// -// - Convert Unicode codepoints to glyph indexes and operate on the glyphs; -// if you don't do this, stb_truetype is forced to do the conversion on -// every call. -// -// - There are a lot of memory allocations. We should modify it to take -// a temp buffer and allocate from the temp buffer (without freeing), -// should help performance a lot. -// -// NOTES -// -// The system uses the raw data found in the .ttf file without changing it -// and without building auxiliary data structures. This is a bit inefficient -// on little-endian systems (the data is big-endian), but assuming you're -// caching the bitmaps or glyph shapes this shouldn't be a big deal. -// -// It appears to be very hard to programmatically determine what font a -// given file is in a general way. I provide an API for this, but I don't -// recommend it. -// -// -// SOURCE STATISTICS (based on v0.6c, 2050 LOC) -// -// Documentation & header file 520 LOC \___ 660 LOC documentation -// Sample code 140 LOC / -// Truetype parsing 620 LOC ---- 620 LOC TrueType -// Software rasterization 240 LOC \ . -// Curve tesselation 120 LOC \__ 550 LOC Bitmap creation -// Bitmap management 100 LOC / -// Baked bitmap interface 70 LOC / -// Font name matching & access 150 LOC ---- 150 -// C runtime library abstraction 60 LOC ---- 60 -// -// -// PERFORMANCE MEASUREMENTS FOR 1.06: -// -// 32-bit 64-bit -// Previous release: 8.83 s 7.68 s -// Pool allocations: 7.72 s 6.34 s -// Inline sort : 6.54 s 5.65 s -// New rasterizer : 5.63 s 5.00 s - -////////////////////////////////////////////////////////////////////////////// -////////////////////////////////////////////////////////////////////////////// -//// -//// SAMPLE PROGRAMS -//// -// -// Incomplete text-in-3d-api example, which draws quads properly aligned to be lossless -// -#if 0 -#define STB_TRUETYPE_IMPLEMENTATION // force following include to generate implementation -#include "stb_truetype.h" - -unsigned char ttf_buffer[1<<20]; -unsigned char temp_bitmap[512*512]; - -stbtt_bakedchar cdata[96]; // ASCII 32..126 is 95 glyphs -GLuint ftex; - -void my_stbtt_initfont(void) -{ - fread(ttf_buffer, 1, 1<<20, fopen("c:/windows/fonts/times.ttf", "rb")); - stbtt_BakeFontBitmap(ttf_buffer,0, 32.0, temp_bitmap,512,512, 32,96, cdata); // no guarantee this fits! - // can free ttf_buffer at this point - glGenTextures(1, &ftex); - glBindTexture(GL_TEXTURE_2D, ftex); - glTexImage2D(GL_TEXTURE_2D, 0, GL_ALPHA, 512,512, 0, GL_ALPHA, GL_UNSIGNED_BYTE, temp_bitmap); - // can free temp_bitmap at this point - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); -} - -void my_stbtt_print(float x, float y, char *text) -{ - // assume orthographic projection with units = screen pixels, origin at top left - glEnable(GL_TEXTURE_2D); - glBindTexture(GL_TEXTURE_2D, ftex); - glBegin(GL_QUADS); - while (*text) { - if (*text >= 32 && *text < 128) { - stbtt_aligned_quad q; - stbtt_GetBakedQuad(cdata, 512,512, *text-32, &x,&y,&q,1);//1=opengl & d3d10+,0=d3d9 - glTexCoord2f(q.s0,q.t1); glVertex2f(q.x0,q.y0); - glTexCoord2f(q.s1,q.t1); glVertex2f(q.x1,q.y0); - glTexCoord2f(q.s1,q.t0); glVertex2f(q.x1,q.y1); - glTexCoord2f(q.s0,q.t0); glVertex2f(q.x0,q.y1); - } - ++text; - } - glEnd(); -} -#endif -// -// -////////////////////////////////////////////////////////////////////////////// -// -// Complete program (this compiles): get a single bitmap, print as ASCII art -// -#if 0 -#include -#define STB_TRUETYPE_IMPLEMENTATION // force following include to generate implementation -#include "stb_truetype.h" - -char ttf_buffer[1<<25]; - -int main(int argc, char **argv) -{ - stbtt_fontinfo font; - unsigned char *bitmap; - int w,h,i,j,c = (argc > 1 ? atoi(argv[1]) : 'a'), s = (argc > 2 ? atoi(argv[2]) : 20); - - fread(ttf_buffer, 1, 1<<25, fopen(argc > 3 ? argv[3] : "c:/windows/fonts/arialbd.ttf", "rb")); - - stbtt_InitFont(&font, ttf_buffer, stbtt_GetFontOffsetForIndex(ttf_buffer,0)); - bitmap = stbtt_GetCodepointBitmap(&font, 0,stbtt_ScaleForPixelHeight(&font, s), c, &w, &h, 0,0); - - for (j=0; j < h; ++j) { - for (i=0; i < w; ++i) - putchar(" .:ioVM@"[bitmap[j*w+i]>>5]); - putchar('\n'); - } - return 0; -} -#endif -// -// Output: -// -// .ii. -// @@@@@@. -// V@Mio@@o -// :i. V@V -// :oM@@M -// :@@@MM@M -// @@o o@M -// :@@. M@M -// @@@o@@@@ -// :M@@V:@@. -// -////////////////////////////////////////////////////////////////////////////// -// -// Complete program: print "Hello World!" banner, with bugs -// -#if 0 -char buffer[24<<20]; -unsigned char screen[20][79]; - -int main(int arg, char **argv) -{ - stbtt_fontinfo font; - int i,j,ascent,baseline,ch=0; - float scale, xpos=2; // leave a little padding in case the character extends left - char *text = "Heljo World!"; // intentionally misspelled to show 'lj' brokenness - - fread(buffer, 1, 1000000, fopen("c:/windows/fonts/arialbd.ttf", "rb")); - stbtt_InitFont(&font, buffer, 0); - - scale = stbtt_ScaleForPixelHeight(&font, 15); - stbtt_GetFontVMetrics(&font, &ascent,0,0); - baseline = (int) (ascent*scale); - - while (text[ch]) { - int advance,lsb,x0,y0,x1,y1; - float x_shift = xpos - (float) floor(xpos); - stbtt_GetCodepointHMetrics(&font, text[ch], &advance, &lsb); - stbtt_GetCodepointBitmapBoxSubpixel(&font, text[ch], scale,scale,x_shift,0, &x0,&y0,&x1,&y1); - stbtt_MakeCodepointBitmapSubpixel(&font, &screen[baseline + y0][(int) xpos + x0], x1-x0,y1-y0, 79, scale,scale,x_shift,0, text[ch]); - // note that this stomps the old data, so where character boxes overlap (e.g. 'lj') it's wrong - // because this API is really for baking character bitmaps into textures. if you want to render - // a sequence of characters, you really need to render each bitmap to a temp buffer, then - // "alpha blend" that into the working buffer - xpos += (advance * scale); - if (text[ch+1]) - xpos += scale*stbtt_GetCodepointKernAdvance(&font, text[ch],text[ch+1]); - ++ch; - } - - for (j=0; j < 20; ++j) { - for (i=0; i < 78; ++i) - putchar(" .:ioVM@"[screen[j][i]>>5]); - putchar('\n'); - } - - return 0; -} -#endif - - -////////////////////////////////////////////////////////////////////////////// -////////////////////////////////////////////////////////////////////////////// -//// -//// INTEGRATION WITH YOUR CODEBASE -//// -//// The following sections allow you to supply alternate definitions -//// of C library functions used by stb_truetype. - -#ifdef STB_TRUETYPE_IMPLEMENTATION - // #define your own (u)stbtt_int8/16/32 before including to override this - #ifndef stbtt_uint8 - typedef unsigned char stbtt_uint8; - typedef signed char stbtt_int8; - typedef unsigned short stbtt_uint16; - typedef signed short stbtt_int16; - typedef unsigned int stbtt_uint32; - typedef signed int stbtt_int32; - #endif - - typedef char stbtt__check_size32[sizeof(stbtt_int32)==4 ? 1 : -1]; - typedef char stbtt__check_size16[sizeof(stbtt_int16)==2 ? 1 : -1]; - - // #define your own STBTT_ifloor/STBTT_iceil() to avoid math.h - #ifndef STBTT_ifloor - #include - #define STBTT_ifloor(x) ((int) floor(x)) - #define STBTT_iceil(x) ((int) ceil(x)) - #endif - - #ifndef STBTT_sqrt - #include - #define STBTT_sqrt(x) sqrt(x) - #endif - - #ifndef STBTT_fabs - #include - #define STBTT_fabs(x) fabs(x) - #endif - - // #define your own functions "STBTT_malloc" / "STBTT_free" to avoid malloc.h - #ifndef STBTT_malloc - #include - #define STBTT_malloc(x,u) ((void)(u),malloc(x)) - #define STBTT_free(x,u) ((void)(u),free(x)) - #endif - - #ifndef STBTT_assert - #include - #define STBTT_assert(x) assert(x) - #endif - - #ifndef STBTT_strlen - #include - #define STBTT_strlen(x) strlen(x) - #endif - - #ifndef STBTT_memcpy - #include - #define STBTT_memcpy memcpy - #define STBTT_memset memset - #endif -#endif - -/////////////////////////////////////////////////////////////////////////////// -/////////////////////////////////////////////////////////////////////////////// -//// -//// INTERFACE -//// -//// - -#ifndef __STB_INCLUDE_STB_TRUETYPE_H__ -#define __STB_INCLUDE_STB_TRUETYPE_H__ - -#ifdef STBTT_STATIC -#define STBTT_DEF static -#else -#define STBTT_DEF extern -#endif - -#ifdef __cplusplus -extern "C" { -#endif - -////////////////////////////////////////////////////////////////////////////// -// -// TEXTURE BAKING API -// -// If you use this API, you only have to call two functions ever. -// - -typedef struct -{ - unsigned short x0,y0,x1,y1; // coordinates of bbox in bitmap - float xoff,yoff,xadvance; -} stbtt_bakedchar; - -STBTT_DEF int stbtt_BakeFontBitmap(const unsigned char *data, int offset, // font location (use offset=0 for plain .ttf) - float pixel_height, // height of font in pixels - unsigned char *pixels, int pw, int ph, // bitmap to be filled in - int first_char, int num_chars, // characters to bake - stbtt_bakedchar *chardata); // you allocate this, it's num_chars long -// if return is positive, the first unused row of the bitmap -// if return is negative, returns the negative of the number of characters that fit -// if return is 0, no characters fit and no rows were used -// This uses a very crappy packing. - -typedef struct -{ - float x0,y0,s0,t0; // top-left - float x1,y1,s1,t1; // bottom-right -} stbtt_aligned_quad; - -STBTT_DEF void stbtt_GetBakedQuad(stbtt_bakedchar *chardata, int pw, int ph, // same data as above - int char_index, // character to display - float *xpos, float *ypos, // pointers to current position in screen pixel space - stbtt_aligned_quad *q, // output: quad to draw - int opengl_fillrule); // true if opengl fill rule; false if DX9 or earlier -// Call GetBakedQuad with char_index = 'character - first_char', and it -// creates the quad you need to draw and advances the current position. -// -// The coordinate system used assumes y increases downwards. -// -// Characters will extend both above and below the current position; -// see discussion of "BASELINE" above. -// -// It's inefficient; you might want to c&p it and optimize it. - - - -////////////////////////////////////////////////////////////////////////////// -// -// NEW TEXTURE BAKING API -// -// This provides options for packing multiple fonts into one atlas, not -// perfectly but better than nothing. - -typedef struct -{ - unsigned short x0,y0,x1,y1; // coordinates of bbox in bitmap - float xoff,yoff,xadvance; - float xoff2,yoff2; -} stbtt_packedchar; - -typedef struct stbtt_pack_context stbtt_pack_context; -typedef struct stbtt_fontinfo stbtt_fontinfo; -#ifndef STB_RECT_PACK_VERSION -typedef struct stbrp_rect stbrp_rect; -#endif - -STBTT_DEF int stbtt_PackBegin(stbtt_pack_context *spc, unsigned char *pixels, int width, int height, int stride_in_bytes, int padding, void *alloc_context); -// Initializes a packing context stored in the passed-in stbtt_pack_context. -// Future calls using this context will pack characters into the bitmap passed -// in here: a 1-channel bitmap that is weight x height. stride_in_bytes is -// the distance from one row to the next (or 0 to mean they are packed tightly -// together). "padding" is the amount of padding to leave between each -// character (normally you want '1' for bitmaps you'll use as textures with -// bilinear filtering). -// -// Returns 0 on failure, 1 on success. - -STBTT_DEF void stbtt_PackEnd (stbtt_pack_context *spc); -// Cleans up the packing context and frees all memory. - -#define STBTT_POINT_SIZE(x) (-(x)) - -STBTT_DEF int stbtt_PackFontRange(stbtt_pack_context *spc, unsigned char *fontdata, int font_index, float font_size, - int first_unicode_char_in_range, int num_chars_in_range, stbtt_packedchar *chardata_for_range); -// Creates character bitmaps from the font_index'th font found in fontdata (use -// font_index=0 if you don't know what that is). It creates num_chars_in_range -// bitmaps for characters with unicode values starting at first_unicode_char_in_range -// and increasing. Data for how to render them is stored in chardata_for_range; -// pass these to stbtt_GetPackedQuad to get back renderable quads. -// -// font_size is the full height of the character from ascender to descender, -// as computed by stbtt_ScaleForPixelHeight. To use a point size as computed -// by stbtt_ScaleForMappingEmToPixels, wrap the point size in STBTT_POINT_SIZE() -// and pass that result as 'font_size': -// ..., 20 , ... // font max minus min y is 20 pixels tall -// ..., STBTT_POINT_SIZE(20), ... // 'M' is 20 pixels tall - -typedef struct -{ - float font_size; - int first_unicode_codepoint_in_range; // if non-zero, then the chars are continuous, and this is the first codepoint - int *array_of_unicode_codepoints; // if non-zero, then this is an array of unicode codepoints - int num_chars; - stbtt_packedchar *chardata_for_range; // output - unsigned char h_oversample, v_oversample; // don't set these, they're used internally -} stbtt_pack_range; - -STBTT_DEF int stbtt_PackFontRanges(stbtt_pack_context *spc, unsigned char *fontdata, int font_index, stbtt_pack_range *ranges, int num_ranges); -// Creates character bitmaps from multiple ranges of characters stored in -// ranges. This will usually create a better-packed bitmap than multiple -// calls to stbtt_PackFontRange. Note that you can call this multiple -// times within a single PackBegin/PackEnd. - -STBTT_DEF void stbtt_PackSetOversampling(stbtt_pack_context *spc, unsigned int h_oversample, unsigned int v_oversample); -// Oversampling a font increases the quality by allowing higher-quality subpixel -// positioning, and is especially valuable at smaller text sizes. -// -// This function sets the amount of oversampling for all following calls to -// stbtt_PackFontRange(s) or stbtt_PackFontRangesGatherRects for a given -// pack context. The default (no oversampling) is achieved by h_oversample=1 -// and v_oversample=1. The total number of pixels required is -// h_oversample*v_oversample larger than the default; for example, 2x2 -// oversampling requires 4x the storage of 1x1. For best results, render -// oversampled textures with bilinear filtering. Look at the readme in -// stb/tests/oversample for information about oversampled fonts -// -// To use with PackFontRangesGather etc., you must set it before calls -// call to PackFontRangesGatherRects. - -STBTT_DEF void stbtt_GetPackedQuad(stbtt_packedchar *chardata, int pw, int ph, // same data as above - int char_index, // character to display - float *xpos, float *ypos, // pointers to current position in screen pixel space - stbtt_aligned_quad *q, // output: quad to draw - int align_to_integer); - -STBTT_DEF int stbtt_PackFontRangesGatherRects(stbtt_pack_context *spc, stbtt_fontinfo *info, stbtt_pack_range *ranges, int num_ranges, stbrp_rect *rects); -STBTT_DEF void stbtt_PackFontRangesPackRects(stbtt_pack_context *spc, stbrp_rect *rects, int num_rects); -STBTT_DEF int stbtt_PackFontRangesRenderIntoRects(stbtt_pack_context *spc, stbtt_fontinfo *info, stbtt_pack_range *ranges, int num_ranges, stbrp_rect *rects); -// Calling these functions in sequence is roughly equivalent to calling -// stbtt_PackFontRanges(). If you more control over the packing of multiple -// fonts, or if you want to pack custom data into a font texture, take a look -// at the source to of stbtt_PackFontRanges() and create a custom version -// using these functions, e.g. call GatherRects multiple times, -// building up a single array of rects, then call PackRects once, -// then call RenderIntoRects repeatedly. This may result in a -// better packing than calling PackFontRanges multiple times -// (or it may not). - -// this is an opaque structure that you shouldn't mess with which holds -// all the context needed from PackBegin to PackEnd. -struct stbtt_pack_context { - void *user_allocator_context; - void *pack_info; - int width; - int height; - int stride_in_bytes; - int padding; - unsigned int h_oversample, v_oversample; - unsigned char *pixels; - void *nodes; -}; - -////////////////////////////////////////////////////////////////////////////// -// -// FONT LOADING -// -// - -STBTT_DEF int stbtt_GetFontOffsetForIndex(const unsigned char *data, int index); -// Each .ttf/.ttc file may have more than one font. Each font has a sequential -// index number starting from 0. Call this function to get the font offset for -// a given index; it returns -1 if the index is out of range. A regular .ttf -// file will only define one font and it always be at offset 0, so it will -// return '0' for index 0, and -1 for all other indices. You can just skip -// this step if you know it's that kind of font. - - -// The following structure is defined publically so you can declare one on -// the stack or as a global or etc, but you should treat it as opaque. -struct stbtt_fontinfo -{ - void * userdata; - unsigned char * data; // pointer to .ttf file - int fontstart; // offset of start of font - - int numGlyphs; // number of glyphs, needed for range checking - - int loca,head,glyf,hhea,hmtx,kern; // table locations as offset from start of .ttf - int index_map; // a cmap mapping for our chosen character encoding - int indexToLocFormat; // format needed to map from glyph index to glyph -}; - -STBTT_DEF int stbtt_InitFont(stbtt_fontinfo *info, const unsigned char *data, int offset); -// Given an offset into the file that defines a font, this function builds -// the necessary cached info for the rest of the system. You must allocate -// the stbtt_fontinfo yourself, and stbtt_InitFont will fill it out. You don't -// need to do anything special to free it, because the contents are pure -// value data with no additional data structures. Returns 0 on failure. - - -////////////////////////////////////////////////////////////////////////////// -// -// CHARACTER TO GLYPH-INDEX CONVERSIOn - -STBTT_DEF int stbtt_FindGlyphIndex(const stbtt_fontinfo *info, int unicode_codepoint); -// If you're going to perform multiple operations on the same character -// and you want a speed-up, call this function with the character you're -// going to process, then use glyph-based functions instead of the -// codepoint-based functions. - - -////////////////////////////////////////////////////////////////////////////// -// -// CHARACTER PROPERTIES -// - -STBTT_DEF float stbtt_ScaleForPixelHeight(const stbtt_fontinfo *info, float pixels); -// computes a scale factor to produce a font whose "height" is 'pixels' tall. -// Height is measured as the distance from the highest ascender to the lowest -// descender; in other words, it's equivalent to calling stbtt_GetFontVMetrics -// and computing: -// scale = pixels / (ascent - descent) -// so if you prefer to measure height by the ascent only, use a similar calculation. - -STBTT_DEF float stbtt_ScaleForMappingEmToPixels(const stbtt_fontinfo *info, float pixels); -// computes a scale factor to produce a font whose EM size is mapped to -// 'pixels' tall. This is probably what traditional APIs compute, but -// I'm not positive. - -STBTT_DEF void stbtt_GetFontVMetrics(const stbtt_fontinfo *info, int *ascent, int *descent, int *lineGap); -// ascent is the coordinate above the baseline the font extends; descent -// is the coordinate below the baseline the font extends (i.e. it is typically negative) -// lineGap is the spacing between one row's descent and the next row's ascent... -// so you should advance the vertical position by "*ascent - *descent + *lineGap" -// these are expressed in unscaled coordinates, so you must multiply by -// the scale factor for a given size - -STBTT_DEF void stbtt_GetFontBoundingBox(const stbtt_fontinfo *info, int *x0, int *y0, int *x1, int *y1); -// the bounding box around all possible characters - -STBTT_DEF void stbtt_GetCodepointHMetrics(const stbtt_fontinfo *info, int codepoint, int *advanceWidth, int *leftSideBearing); -// leftSideBearing is the offset from the current horizontal position to the left edge of the character -// advanceWidth is the offset from the current horizontal position to the next horizontal position -// these are expressed in unscaled coordinates - -STBTT_DEF int stbtt_GetCodepointKernAdvance(const stbtt_fontinfo *info, int ch1, int ch2); -// an additional amount to add to the 'advance' value between ch1 and ch2 - -STBTT_DEF int stbtt_GetCodepointBox(const stbtt_fontinfo *info, int codepoint, int *x0, int *y0, int *x1, int *y1); -// Gets the bounding box of the visible part of the glyph, in unscaled coordinates - -STBTT_DEF void stbtt_GetGlyphHMetrics(const stbtt_fontinfo *info, int glyph_index, int *advanceWidth, int *leftSideBearing); -STBTT_DEF int stbtt_GetGlyphKernAdvance(const stbtt_fontinfo *info, int glyph1, int glyph2); -STBTT_DEF int stbtt_GetGlyphBox(const stbtt_fontinfo *info, int glyph_index, int *x0, int *y0, int *x1, int *y1); -// as above, but takes one or more glyph indices for greater efficiency - - -////////////////////////////////////////////////////////////////////////////// -// -// GLYPH SHAPES (you probably don't need these, but they have to go before -// the bitmaps for C declaration-order reasons) -// - -#ifndef STBTT_vmove // you can predefine these to use different values (but why?) - enum { - STBTT_vmove=1, - STBTT_vline, - STBTT_vcurve - }; -#endif - -#ifndef stbtt_vertex // you can predefine this to use different values - // (we share this with other code at RAD) - #define stbtt_vertex_type short // can't use stbtt_int16 because that's not visible in the header file - typedef struct - { - stbtt_vertex_type x,y,cx,cy; - unsigned char type,padding; - } stbtt_vertex; -#endif - -STBTT_DEF int stbtt_IsGlyphEmpty(const stbtt_fontinfo *info, int glyph_index); -// returns non-zero if nothing is drawn for this glyph - -STBTT_DEF int stbtt_GetCodepointShape(const stbtt_fontinfo *info, int unicode_codepoint, stbtt_vertex **vertices); -STBTT_DEF int stbtt_GetGlyphShape(const stbtt_fontinfo *info, int glyph_index, stbtt_vertex **vertices); -// returns # of vertices and fills *vertices with the pointer to them -// these are expressed in "unscaled" coordinates -// -// The shape is a series of countours. Each one starts with -// a STBTT_moveto, then consists of a series of mixed -// STBTT_lineto and STBTT_curveto segments. A lineto -// draws a line from previous endpoint to its x,y; a curveto -// draws a quadratic bezier from previous endpoint to -// its x,y, using cx,cy as the bezier control point. - -STBTT_DEF void stbtt_FreeShape(const stbtt_fontinfo *info, stbtt_vertex *vertices); -// frees the data allocated above - -////////////////////////////////////////////////////////////////////////////// -// -// BITMAP RENDERING -// - -STBTT_DEF void stbtt_FreeBitmap(unsigned char *bitmap, void *userdata); -// frees the bitmap allocated below - -STBTT_DEF unsigned char *stbtt_GetCodepointBitmap(const stbtt_fontinfo *info, float scale_x, float scale_y, int codepoint, int *width, int *height, int *xoff, int *yoff); -// allocates a large-enough single-channel 8bpp bitmap and renders the -// specified character/glyph at the specified scale into it, with -// antialiasing. 0 is no coverage (transparent), 255 is fully covered (opaque). -// *width & *height are filled out with the width & height of the bitmap, -// which is stored left-to-right, top-to-bottom. -// -// xoff/yoff are the offset it pixel space from the glyph origin to the top-left of the bitmap - -STBTT_DEF unsigned char *stbtt_GetCodepointBitmapSubpixel(const stbtt_fontinfo *info, float scale_x, float scale_y, float shift_x, float shift_y, int codepoint, int *width, int *height, int *xoff, int *yoff); -// the same as stbtt_GetCodepoitnBitmap, but you can specify a subpixel -// shift for the character - -STBTT_DEF void stbtt_MakeCodepointBitmap(const stbtt_fontinfo *info, unsigned char *output, int out_w, int out_h, int out_stride, float scale_x, float scale_y, int codepoint); -// the same as stbtt_GetCodepointBitmap, but you pass in storage for the bitmap -// in the form of 'output', with row spacing of 'out_stride' bytes. the bitmap -// is clipped to out_w/out_h bytes. Call stbtt_GetCodepointBitmapBox to get the -// width and height and positioning info for it first. - -STBTT_DEF void stbtt_MakeCodepointBitmapSubpixel(const stbtt_fontinfo *info, unsigned char *output, int out_w, int out_h, int out_stride, float scale_x, float scale_y, float shift_x, float shift_y, int codepoint); -// same as stbtt_MakeCodepointBitmap, but you can specify a subpixel -// shift for the character - -STBTT_DEF void stbtt_GetCodepointBitmapBox(const stbtt_fontinfo *font, int codepoint, float scale_x, float scale_y, int *ix0, int *iy0, int *ix1, int *iy1); -// get the bbox of the bitmap centered around the glyph origin; so the -// bitmap width is ix1-ix0, height is iy1-iy0, and location to place -// the bitmap top left is (leftSideBearing*scale,iy0). -// (Note that the bitmap uses y-increases-down, but the shape uses -// y-increases-up, so CodepointBitmapBox and CodepointBox are inverted.) - -STBTT_DEF void stbtt_GetCodepointBitmapBoxSubpixel(const stbtt_fontinfo *font, int codepoint, float scale_x, float scale_y, float shift_x, float shift_y, int *ix0, int *iy0, int *ix1, int *iy1); -// same as stbtt_GetCodepointBitmapBox, but you can specify a subpixel -// shift for the character - -// the following functions are equivalent to the above functions, but operate -// on glyph indices instead of Unicode codepoints (for efficiency) -STBTT_DEF unsigned char *stbtt_GetGlyphBitmap(const stbtt_fontinfo *info, float scale_x, float scale_y, int glyph, int *width, int *height, int *xoff, int *yoff); -STBTT_DEF unsigned char *stbtt_GetGlyphBitmapSubpixel(const stbtt_fontinfo *info, float scale_x, float scale_y, float shift_x, float shift_y, int glyph, int *width, int *height, int *xoff, int *yoff); -STBTT_DEF void stbtt_MakeGlyphBitmap(const stbtt_fontinfo *info, unsigned char *output, int out_w, int out_h, int out_stride, float scale_x, float scale_y, int glyph); -STBTT_DEF void stbtt_MakeGlyphBitmapSubpixel(const stbtt_fontinfo *info, unsigned char *output, int out_w, int out_h, int out_stride, float scale_x, float scale_y, float shift_x, float shift_y, int glyph); -STBTT_DEF void stbtt_GetGlyphBitmapBox(const stbtt_fontinfo *font, int glyph, float scale_x, float scale_y, int *ix0, int *iy0, int *ix1, int *iy1); -STBTT_DEF void stbtt_GetGlyphBitmapBoxSubpixel(const stbtt_fontinfo *font, int glyph, float scale_x, float scale_y,float shift_x, float shift_y, int *ix0, int *iy0, int *ix1, int *iy1); - - -// @TODO: don't expose this structure -typedef struct -{ - int w,h,stride; - unsigned char *pixels; -} stbtt__bitmap; - -// rasterize a shape with quadratic beziers into a bitmap -STBTT_DEF void stbtt_Rasterize(stbtt__bitmap *result, // 1-channel bitmap to draw into - float flatness_in_pixels, // allowable error of curve in pixels - stbtt_vertex *vertices, // array of vertices defining shape - int num_verts, // number of vertices in above array - float scale_x, float scale_y, // scale applied to input vertices - float shift_x, float shift_y, // translation applied to input vertices - int x_off, int y_off, // another translation applied to input - int invert, // if non-zero, vertically flip shape - void *userdata); // context for to STBTT_MALLOC - -////////////////////////////////////////////////////////////////////////////// -// -// Finding the right font... -// -// You should really just solve this offline, keep your own tables -// of what font is what, and don't try to get it out of the .ttf file. -// That's because getting it out of the .ttf file is really hard, because -// the names in the file can appear in many possible encodings, in many -// possible languages, and e.g. if you need a case-insensitive comparison, -// the details of that depend on the encoding & language in a complex way -// (actually underspecified in truetype, but also gigantic). -// -// But you can use the provided functions in two possible ways: -// stbtt_FindMatchingFont() will use *case-sensitive* comparisons on -// unicode-encoded names to try to find the font you want; -// you can run this before calling stbtt_InitFont() -// -// stbtt_GetFontNameString() lets you get any of the various strings -// from the file yourself and do your own comparisons on them. -// You have to have called stbtt_InitFont() first. - - -STBTT_DEF int stbtt_FindMatchingFont(const unsigned char *fontdata, const char *name, int flags); -// returns the offset (not index) of the font that matches, or -1 if none -// if you use STBTT_MACSTYLE_DONTCARE, use a font name like "Arial Bold". -// if you use any other flag, use a font name like "Arial"; this checks -// the 'macStyle' header field; i don't know if fonts set this consistently -#define STBTT_MACSTYLE_DONTCARE 0 -#define STBTT_MACSTYLE_BOLD 1 -#define STBTT_MACSTYLE_ITALIC 2 -#define STBTT_MACSTYLE_UNDERSCORE 4 -#define STBTT_MACSTYLE_NONE 8 // <= not same as 0, this makes us check the bitfield is 0 - -STBTT_DEF int stbtt_CompareUTF8toUTF16_bigendian(const char *s1, int len1, const char *s2, int len2); -// returns 1/0 whether the first string interpreted as utf8 is identical to -// the second string interpreted as big-endian utf16... useful for strings from next func - -STBTT_DEF const char *stbtt_GetFontNameString(const stbtt_fontinfo *font, int *length, int platformID, int encodingID, int languageID, int nameID); -// returns the string (which may be big-endian double byte, e.g. for unicode) -// and puts the length in bytes in *length. -// -// some of the values for the IDs are below; for more see the truetype spec: -// http://developer.apple.com/textfonts/TTRefMan/RM06/Chap6name.html -// http://www.microsoft.com/typography/otspec/name.htm - -enum { // platformID - STBTT_PLATFORM_ID_UNICODE =0, - STBTT_PLATFORM_ID_MAC =1, - STBTT_PLATFORM_ID_ISO =2, - STBTT_PLATFORM_ID_MICROSOFT =3 -}; - -enum { // encodingID for STBTT_PLATFORM_ID_UNICODE - STBTT_UNICODE_EID_UNICODE_1_0 =0, - STBTT_UNICODE_EID_UNICODE_1_1 =1, - STBTT_UNICODE_EID_ISO_10646 =2, - STBTT_UNICODE_EID_UNICODE_2_0_BMP=3, - STBTT_UNICODE_EID_UNICODE_2_0_FULL=4 -}; - -enum { // encodingID for STBTT_PLATFORM_ID_MICROSOFT - STBTT_MS_EID_SYMBOL =0, - STBTT_MS_EID_UNICODE_BMP =1, - STBTT_MS_EID_SHIFTJIS =2, - STBTT_MS_EID_UNICODE_FULL =10 -}; - -enum { // encodingID for STBTT_PLATFORM_ID_MAC; same as Script Manager codes - STBTT_MAC_EID_ROMAN =0, STBTT_MAC_EID_ARABIC =4, - STBTT_MAC_EID_JAPANESE =1, STBTT_MAC_EID_HEBREW =5, - STBTT_MAC_EID_CHINESE_TRAD =2, STBTT_MAC_EID_GREEK =6, - STBTT_MAC_EID_KOREAN =3, STBTT_MAC_EID_RUSSIAN =7 -}; - -enum { // languageID for STBTT_PLATFORM_ID_MICROSOFT; same as LCID... - // problematic because there are e.g. 16 english LCIDs and 16 arabic LCIDs - STBTT_MS_LANG_ENGLISH =0x0409, STBTT_MS_LANG_ITALIAN =0x0410, - STBTT_MS_LANG_CHINESE =0x0804, STBTT_MS_LANG_JAPANESE =0x0411, - STBTT_MS_LANG_DUTCH =0x0413, STBTT_MS_LANG_KOREAN =0x0412, - STBTT_MS_LANG_FRENCH =0x040c, STBTT_MS_LANG_RUSSIAN =0x0419, - STBTT_MS_LANG_GERMAN =0x0407, STBTT_MS_LANG_SPANISH =0x0409, - STBTT_MS_LANG_HEBREW =0x040d, STBTT_MS_LANG_SWEDISH =0x041D -}; - -enum { // languageID for STBTT_PLATFORM_ID_MAC - STBTT_MAC_LANG_ENGLISH =0 , STBTT_MAC_LANG_JAPANESE =11, - STBTT_MAC_LANG_ARABIC =12, STBTT_MAC_LANG_KOREAN =23, - STBTT_MAC_LANG_DUTCH =4 , STBTT_MAC_LANG_RUSSIAN =32, - STBTT_MAC_LANG_FRENCH =1 , STBTT_MAC_LANG_SPANISH =6 , - STBTT_MAC_LANG_GERMAN =2 , STBTT_MAC_LANG_SWEDISH =5 , - STBTT_MAC_LANG_HEBREW =10, STBTT_MAC_LANG_CHINESE_SIMPLIFIED =33, - STBTT_MAC_LANG_ITALIAN =3 , STBTT_MAC_LANG_CHINESE_TRAD =19 -}; - -#ifdef __cplusplus -} -#endif - -#endif // __STB_INCLUDE_STB_TRUETYPE_H__ - -/////////////////////////////////////////////////////////////////////////////// -/////////////////////////////////////////////////////////////////////////////// -//// -//// IMPLEMENTATION -//// -//// - -#ifdef STB_TRUETYPE_IMPLEMENTATION - -#ifndef STBTT_MAX_OVERSAMPLE -#define STBTT_MAX_OVERSAMPLE 8 -#endif - -#if STBTT_MAX_OVERSAMPLE > 255 -#error "STBTT_MAX_OVERSAMPLE cannot be > 255" -#endif - -typedef int stbtt__test_oversample_pow2[(STBTT_MAX_OVERSAMPLE & (STBTT_MAX_OVERSAMPLE-1)) == 0 ? 1 : -1]; - -#ifndef STBTT_RASTERIZER_VERSION -#define STBTT_RASTERIZER_VERSION 2 -#endif - -#ifdef _MSC_VER -#define STBTT__NOTUSED(v) (void)(v) -#else -#define STBTT__NOTUSED(v) (void)sizeof(v) -#endif - -////////////////////////////////////////////////////////////////////////// -// -// accessors to parse data from file -// - -// on platforms that don't allow misaligned reads, if we want to allow -// truetype fonts that aren't padded to alignment, define ALLOW_UNALIGNED_TRUETYPE - -#define ttBYTE(p) (* (stbtt_uint8 *) (p)) -#define ttCHAR(p) (* (stbtt_int8 *) (p)) -#define ttFixed(p) ttLONG(p) - -#if defined(STB_TRUETYPE_BIGENDIAN) && !defined(ALLOW_UNALIGNED_TRUETYPE) - - #define ttUSHORT(p) (* (stbtt_uint16 *) (p)) - #define ttSHORT(p) (* (stbtt_int16 *) (p)) - #define ttULONG(p) (* (stbtt_uint32 *) (p)) - #define ttLONG(p) (* (stbtt_int32 *) (p)) - -#else - - static stbtt_uint16 ttUSHORT(const stbtt_uint8 *p) { return p[0]*256 + p[1]; } - static stbtt_int16 ttSHORT(const stbtt_uint8 *p) { return p[0]*256 + p[1]; } - static stbtt_uint32 ttULONG(const stbtt_uint8 *p) { return (p[0]<<24) + (p[1]<<16) + (p[2]<<8) + p[3]; } - static stbtt_int32 ttLONG(const stbtt_uint8 *p) { return (p[0]<<24) + (p[1]<<16) + (p[2]<<8) + p[3]; } - -#endif - -#define stbtt_tag4(p,c0,c1,c2,c3) ((p)[0] == (c0) && (p)[1] == (c1) && (p)[2] == (c2) && (p)[3] == (c3)) -#define stbtt_tag(p,str) stbtt_tag4(p,str[0],str[1],str[2],str[3]) - -static int stbtt__isfont(const stbtt_uint8 *font) -{ - // check the version number - if (stbtt_tag4(font, '1',0,0,0)) return 1; // TrueType 1 - if (stbtt_tag(font, "typ1")) return 1; // TrueType with type 1 font -- we don't support this! - if (stbtt_tag(font, "OTTO")) return 1; // OpenType with CFF - if (stbtt_tag4(font, 0,1,0,0)) return 1; // OpenType 1.0 - return 0; -} - -// @OPTIMIZE: binary search -static stbtt_uint32 stbtt__find_table(stbtt_uint8 *data, stbtt_uint32 fontstart, const char *tag) -{ - stbtt_int32 num_tables = ttUSHORT(data+fontstart+4); - stbtt_uint32 tabledir = fontstart + 12; - stbtt_int32 i; - for (i=0; i < num_tables; ++i) { - stbtt_uint32 loc = tabledir + 16*i; - if (stbtt_tag(data+loc+0, tag)) - return ttULONG(data+loc+8); - } - return 0; -} - -STBTT_DEF int stbtt_GetFontOffsetForIndex(const unsigned char *font_collection, int index) -{ - // if it's just a font, there's only one valid index - if (stbtt__isfont(font_collection)) - return index == 0 ? 0 : -1; - - // check if it's a TTC - if (stbtt_tag(font_collection, "ttcf")) { - // version 1? - if (ttULONG(font_collection+4) == 0x00010000 || ttULONG(font_collection+4) == 0x00020000) { - stbtt_int32 n = ttLONG(font_collection+8); - if (index >= n) - return -1; - return ttULONG(font_collection+12+index*4); - } - } - return -1; -} - -STBTT_DEF int stbtt_InitFont(stbtt_fontinfo *info, const unsigned char *data2, int fontstart) -{ - stbtt_uint8 *data = (stbtt_uint8 *) data2; - stbtt_uint32 cmap, t; - stbtt_int32 i,numTables; - - info->data = data; - info->fontstart = fontstart; - - cmap = stbtt__find_table(data, fontstart, "cmap"); // required - info->loca = stbtt__find_table(data, fontstart, "loca"); // required - info->head = stbtt__find_table(data, fontstart, "head"); // required - info->glyf = stbtt__find_table(data, fontstart, "glyf"); // required - info->hhea = stbtt__find_table(data, fontstart, "hhea"); // required - info->hmtx = stbtt__find_table(data, fontstart, "hmtx"); // required - info->kern = stbtt__find_table(data, fontstart, "kern"); // not required - if (!cmap || !info->loca || !info->head || !info->glyf || !info->hhea || !info->hmtx) - return 0; - - t = stbtt__find_table(data, fontstart, "maxp"); - if (t) - info->numGlyphs = ttUSHORT(data+t+4); - else - info->numGlyphs = 0xffff; - - // find a cmap encoding table we understand *now* to avoid searching - // later. (todo: could make this installable) - // the same regardless of glyph. - numTables = ttUSHORT(data + cmap + 2); - info->index_map = 0; - for (i=0; i < numTables; ++i) { - stbtt_uint32 encoding_record = cmap + 4 + 8 * i; - // find an encoding we understand: - switch(ttUSHORT(data+encoding_record)) { - case STBTT_PLATFORM_ID_MICROSOFT: - switch (ttUSHORT(data+encoding_record+2)) { - case STBTT_MS_EID_UNICODE_BMP: - case STBTT_MS_EID_UNICODE_FULL: - // MS/Unicode - info->index_map = cmap + ttULONG(data+encoding_record+4); - break; - } - break; - case STBTT_PLATFORM_ID_UNICODE: - // Mac/iOS has these - // all the encodingIDs are unicode, so we don't bother to check it - info->index_map = cmap + ttULONG(data+encoding_record+4); - break; - } - } - if (info->index_map == 0) - return 0; - - info->indexToLocFormat = ttUSHORT(data+info->head + 50); - return 1; -} - -STBTT_DEF int stbtt_FindGlyphIndex(const stbtt_fontinfo *info, int unicode_codepoint) -{ - stbtt_uint8 *data = info->data; - stbtt_uint32 index_map = info->index_map; - - stbtt_uint16 format = ttUSHORT(data + index_map + 0); - if (format == 0) { // apple byte encoding - stbtt_int32 bytes = ttUSHORT(data + index_map + 2); - if (unicode_codepoint < bytes-6) - return ttBYTE(data + index_map + 6 + unicode_codepoint); - return 0; - } else if (format == 6) { - stbtt_uint32 first = ttUSHORT(data + index_map + 6); - stbtt_uint32 count = ttUSHORT(data + index_map + 8); - if ((stbtt_uint32) unicode_codepoint >= first && (stbtt_uint32) unicode_codepoint < first+count) - return ttUSHORT(data + index_map + 10 + (unicode_codepoint - first)*2); - return 0; - } else if (format == 2) { - STBTT_assert(0); // @TODO: high-byte mapping for japanese/chinese/korean - return 0; - } else if (format == 4) { // standard mapping for windows fonts: binary search collection of ranges - stbtt_uint16 segcount = ttUSHORT(data+index_map+6) >> 1; - stbtt_uint16 searchRange = ttUSHORT(data+index_map+8) >> 1; - stbtt_uint16 entrySelector = ttUSHORT(data+index_map+10); - stbtt_uint16 rangeShift = ttUSHORT(data+index_map+12) >> 1; - - // do a binary search of the segments - stbtt_uint32 endCount = index_map + 14; - stbtt_uint32 search = endCount; - - if (unicode_codepoint > 0xffff) - return 0; - - // they lie from endCount .. endCount + segCount - // but searchRange is the nearest power of two, so... - if (unicode_codepoint >= ttUSHORT(data + search + rangeShift*2)) - search += rangeShift*2; - - // now decrement to bias correctly to find smallest - search -= 2; - while (entrySelector) { - stbtt_uint16 end; - searchRange >>= 1; - end = ttUSHORT(data + search + searchRange*2); - if (unicode_codepoint > end) - search += searchRange*2; - --entrySelector; - } - search += 2; - - { - stbtt_uint16 offset, start; - stbtt_uint16 item = (stbtt_uint16) ((search - endCount) >> 1); - - STBTT_assert(unicode_codepoint <= ttUSHORT(data + endCount + 2*item)); - start = ttUSHORT(data + index_map + 14 + segcount*2 + 2 + 2*item); - if (unicode_codepoint < start) - return 0; - - offset = ttUSHORT(data + index_map + 14 + segcount*6 + 2 + 2*item); - if (offset == 0) - return (stbtt_uint16) (unicode_codepoint + ttSHORT(data + index_map + 14 + segcount*4 + 2 + 2*item)); - - return ttUSHORT(data + offset + (unicode_codepoint-start)*2 + index_map + 14 + segcount*6 + 2 + 2*item); - } - } else if (format == 12 || format == 13) { - stbtt_uint32 ngroups = ttULONG(data+index_map+12); - stbtt_int32 low,high; - low = 0; high = (stbtt_int32)ngroups; - // Binary search the right group. - while (low < high) { - stbtt_int32 mid = low + ((high-low) >> 1); // rounds down, so low <= mid < high - stbtt_uint32 start_char = ttULONG(data+index_map+16+mid*12); - stbtt_uint32 end_char = ttULONG(data+index_map+16+mid*12+4); - if ((stbtt_uint32) unicode_codepoint < start_char) - high = mid; - else if ((stbtt_uint32) unicode_codepoint > end_char) - low = mid+1; - else { - stbtt_uint32 start_glyph = ttULONG(data+index_map+16+mid*12+8); - if (format == 12) - return start_glyph + unicode_codepoint-start_char; - else // format == 13 - return start_glyph; - } - } - return 0; // not found - } - // @TODO - STBTT_assert(0); - return 0; -} - -STBTT_DEF int stbtt_GetCodepointShape(const stbtt_fontinfo *info, int unicode_codepoint, stbtt_vertex **vertices) -{ - return stbtt_GetGlyphShape(info, stbtt_FindGlyphIndex(info, unicode_codepoint), vertices); -} - -static void stbtt_setvertex(stbtt_vertex *v, stbtt_uint8 type, stbtt_int32 x, stbtt_int32 y, stbtt_int32 cx, stbtt_int32 cy) -{ - v->type = type; - v->x = (stbtt_int16) x; - v->y = (stbtt_int16) y; - v->cx = (stbtt_int16) cx; - v->cy = (stbtt_int16) cy; -} - -static int stbtt__GetGlyfOffset(const stbtt_fontinfo *info, int glyph_index) -{ - int g1,g2; - - if (glyph_index >= info->numGlyphs) return -1; // glyph index out of range - if (info->indexToLocFormat >= 2) return -1; // unknown index->glyph map format - - if (info->indexToLocFormat == 0) { - g1 = info->glyf + ttUSHORT(info->data + info->loca + glyph_index * 2) * 2; - g2 = info->glyf + ttUSHORT(info->data + info->loca + glyph_index * 2 + 2) * 2; - } else { - g1 = info->glyf + ttULONG (info->data + info->loca + glyph_index * 4); - g2 = info->glyf + ttULONG (info->data + info->loca + glyph_index * 4 + 4); - } - - return g1==g2 ? -1 : g1; // if length is 0, return -1 -} - -STBTT_DEF int stbtt_GetGlyphBox(const stbtt_fontinfo *info, int glyph_index, int *x0, int *y0, int *x1, int *y1) -{ - int g = stbtt__GetGlyfOffset(info, glyph_index); - if (g < 0) return 0; - - if (x0) *x0 = ttSHORT(info->data + g + 2); - if (y0) *y0 = ttSHORT(info->data + g + 4); - if (x1) *x1 = ttSHORT(info->data + g + 6); - if (y1) *y1 = ttSHORT(info->data + g + 8); - return 1; -} - -STBTT_DEF int stbtt_GetCodepointBox(const stbtt_fontinfo *info, int codepoint, int *x0, int *y0, int *x1, int *y1) -{ - return stbtt_GetGlyphBox(info, stbtt_FindGlyphIndex(info,codepoint), x0,y0,x1,y1); -} - -STBTT_DEF int stbtt_IsGlyphEmpty(const stbtt_fontinfo *info, int glyph_index) -{ - stbtt_int16 numberOfContours; - int g = stbtt__GetGlyfOffset(info, glyph_index); - if (g < 0) return 1; - numberOfContours = ttSHORT(info->data + g); - return numberOfContours == 0; -} - -static int stbtt__close_shape(stbtt_vertex *vertices, int num_vertices, int was_off, int start_off, - stbtt_int32 sx, stbtt_int32 sy, stbtt_int32 scx, stbtt_int32 scy, stbtt_int32 cx, stbtt_int32 cy) -{ - if (start_off) { - if (was_off) - stbtt_setvertex(&vertices[num_vertices++], STBTT_vcurve, (cx+scx)>>1, (cy+scy)>>1, cx,cy); - stbtt_setvertex(&vertices[num_vertices++], STBTT_vcurve, sx,sy,scx,scy); - } else { - if (was_off) - stbtt_setvertex(&vertices[num_vertices++], STBTT_vcurve,sx,sy,cx,cy); - else - stbtt_setvertex(&vertices[num_vertices++], STBTT_vline,sx,sy,0,0); - } - return num_vertices; -} - -STBTT_DEF int stbtt_GetGlyphShape(const stbtt_fontinfo *info, int glyph_index, stbtt_vertex **pvertices) -{ - stbtt_int16 numberOfContours; - stbtt_uint8 *endPtsOfContours; - stbtt_uint8 *data = info->data; - stbtt_vertex *vertices=0; - int num_vertices=0; - int g = stbtt__GetGlyfOffset(info, glyph_index); - - *pvertices = NULL; - - if (g < 0) return 0; - - numberOfContours = ttSHORT(data + g); - - if (numberOfContours > 0) { - stbtt_uint8 flags=0,flagcount; - stbtt_int32 ins, i,j=0,m,n, next_move, was_off=0, off, start_off=0; - stbtt_int32 x,y,cx,cy,sx,sy, scx,scy; - stbtt_uint8 *points; - endPtsOfContours = (data + g + 10); - ins = ttUSHORT(data + g + 10 + numberOfContours * 2); - points = data + g + 10 + numberOfContours * 2 + 2 + ins; - - n = 1+ttUSHORT(endPtsOfContours + numberOfContours*2-2); - - m = n + 2*numberOfContours; // a loose bound on how many vertices we might need - vertices = (stbtt_vertex *) STBTT_malloc(m * sizeof(vertices[0]), info->userdata); - if (vertices == 0) - return 0; - - next_move = 0; - flagcount=0; - - // in first pass, we load uninterpreted data into the allocated array - // above, shifted to the end of the array so we won't overwrite it when - // we create our final data starting from the front - - off = m - n; // starting offset for uninterpreted data, regardless of how m ends up being calculated - - // first load flags - - for (i=0; i < n; ++i) { - if (flagcount == 0) { - flags = *points++; - if (flags & 8) - flagcount = *points++; - } else - --flagcount; - vertices[off+i].type = flags; - } - - // now load x coordinates - x=0; - for (i=0; i < n; ++i) { - flags = vertices[off+i].type; - if (flags & 2) { - stbtt_int16 dx = *points++; - x += (flags & 16) ? dx : -dx; // ??? - } else { - if (!(flags & 16)) { - x = x + (stbtt_int16) (points[0]*256 + points[1]); - points += 2; - } - } - vertices[off+i].x = (stbtt_int16) x; - } - - // now load y coordinates - y=0; - for (i=0; i < n; ++i) { - flags = vertices[off+i].type; - if (flags & 4) { - stbtt_int16 dy = *points++; - y += (flags & 32) ? dy : -dy; // ??? - } else { - if (!(flags & 32)) { - y = y + (stbtt_int16) (points[0]*256 + points[1]); - points += 2; - } - } - vertices[off+i].y = (stbtt_int16) y; - } - - // now convert them to our format - num_vertices=0; - sx = sy = cx = cy = scx = scy = 0; - for (i=0; i < n; ++i) { - flags = vertices[off+i].type; - x = (stbtt_int16) vertices[off+i].x; - y = (stbtt_int16) vertices[off+i].y; - - if (next_move == i) { - if (i != 0) - num_vertices = stbtt__close_shape(vertices, num_vertices, was_off, start_off, sx,sy,scx,scy,cx,cy); - - // now start the new one - start_off = !(flags & 1); - if (start_off) { - // if we start off with an off-curve point, then when we need to find a point on the curve - // where we can start, and we need to save some state for when we wraparound. - scx = x; - scy = y; - if (!(vertices[off+i+1].type & 1)) { - // next point is also a curve point, so interpolate an on-point curve - sx = (x + (stbtt_int32) vertices[off+i+1].x) >> 1; - sy = (y + (stbtt_int32) vertices[off+i+1].y) >> 1; - } else { - // otherwise just use the next point as our start point - sx = (stbtt_int32) vertices[off+i+1].x; - sy = (stbtt_int32) vertices[off+i+1].y; - ++i; // we're using point i+1 as the starting point, so skip it - } - } else { - sx = x; - sy = y; - } - stbtt_setvertex(&vertices[num_vertices++], STBTT_vmove,sx,sy,0,0); - was_off = 0; - next_move = 1 + ttUSHORT(endPtsOfContours+j*2); - ++j; - } else { - if (!(flags & 1)) { // if it's a curve - if (was_off) // two off-curve control points in a row means interpolate an on-curve midpoint - stbtt_setvertex(&vertices[num_vertices++], STBTT_vcurve, (cx+x)>>1, (cy+y)>>1, cx, cy); - cx = x; - cy = y; - was_off = 1; - } else { - if (was_off) - stbtt_setvertex(&vertices[num_vertices++], STBTT_vcurve, x,y, cx, cy); - else - stbtt_setvertex(&vertices[num_vertices++], STBTT_vline, x,y,0,0); - was_off = 0; - } - } - } - num_vertices = stbtt__close_shape(vertices, num_vertices, was_off, start_off, sx,sy,scx,scy,cx,cy); - } else if (numberOfContours == -1) { - // Compound shapes. - int more = 1; - stbtt_uint8 *comp = data + g + 10; - num_vertices = 0; - vertices = 0; - while (more) { - stbtt_uint16 flags, gidx; - int comp_num_verts = 0, i; - stbtt_vertex *comp_verts = 0, *tmp = 0; - float mtx[6] = {1,0,0,1,0,0}, m, n; - - flags = ttSHORT(comp); comp+=2; - gidx = ttSHORT(comp); comp+=2; - - if (flags & 2) { // XY values - if (flags & 1) { // shorts - mtx[4] = ttSHORT(comp); comp+=2; - mtx[5] = ttSHORT(comp); comp+=2; - } else { - mtx[4] = ttCHAR(comp); comp+=1; - mtx[5] = ttCHAR(comp); comp+=1; - } - } - else { - // @TODO handle matching point - STBTT_assert(0); - } - if (flags & (1<<3)) { // WE_HAVE_A_SCALE - mtx[0] = mtx[3] = ttSHORT(comp)/16384.0f; comp+=2; - mtx[1] = mtx[2] = 0; - } else if (flags & (1<<6)) { // WE_HAVE_AN_X_AND_YSCALE - mtx[0] = ttSHORT(comp)/16384.0f; comp+=2; - mtx[1] = mtx[2] = 0; - mtx[3] = ttSHORT(comp)/16384.0f; comp+=2; - } else if (flags & (1<<7)) { // WE_HAVE_A_TWO_BY_TWO - mtx[0] = ttSHORT(comp)/16384.0f; comp+=2; - mtx[1] = ttSHORT(comp)/16384.0f; comp+=2; - mtx[2] = ttSHORT(comp)/16384.0f; comp+=2; - mtx[3] = ttSHORT(comp)/16384.0f; comp+=2; - } - - // Find transformation scales. - m = (float) STBTT_sqrt(mtx[0]*mtx[0] + mtx[1]*mtx[1]); - n = (float) STBTT_sqrt(mtx[2]*mtx[2] + mtx[3]*mtx[3]); - - // Get indexed glyph. - comp_num_verts = stbtt_GetGlyphShape(info, gidx, &comp_verts); - if (comp_num_verts > 0) { - // Transform vertices. - for (i = 0; i < comp_num_verts; ++i) { - stbtt_vertex* v = &comp_verts[i]; - stbtt_vertex_type x,y; - x=v->x; y=v->y; - v->x = (stbtt_vertex_type)(m * (mtx[0]*x + mtx[2]*y + mtx[4])); - v->y = (stbtt_vertex_type)(n * (mtx[1]*x + mtx[3]*y + mtx[5])); - x=v->cx; y=v->cy; - v->cx = (stbtt_vertex_type)(m * (mtx[0]*x + mtx[2]*y + mtx[4])); - v->cy = (stbtt_vertex_type)(n * (mtx[1]*x + mtx[3]*y + mtx[5])); - } - // Append vertices. - tmp = (stbtt_vertex*)STBTT_malloc((num_vertices+comp_num_verts)*sizeof(stbtt_vertex), info->userdata); - if (!tmp) { - if (vertices) STBTT_free(vertices, info->userdata); - if (comp_verts) STBTT_free(comp_verts, info->userdata); - return 0; - } - if (num_vertices > 0) STBTT_memcpy(tmp, vertices, num_vertices*sizeof(stbtt_vertex)); - STBTT_memcpy(tmp+num_vertices, comp_verts, comp_num_verts*sizeof(stbtt_vertex)); - if (vertices) STBTT_free(vertices, info->userdata); - vertices = tmp; - STBTT_free(comp_verts, info->userdata); - num_vertices += comp_num_verts; - } - // More components ? - more = flags & (1<<5); - } - } else if (numberOfContours < 0) { - // @TODO other compound variations? - STBTT_assert(0); - } else { - // numberOfCounters == 0, do nothing - } - - *pvertices = vertices; - return num_vertices; -} - -STBTT_DEF void stbtt_GetGlyphHMetrics(const stbtt_fontinfo *info, int glyph_index, int *advanceWidth, int *leftSideBearing) -{ - stbtt_uint16 numOfLongHorMetrics = ttUSHORT(info->data+info->hhea + 34); - if (glyph_index < numOfLongHorMetrics) { - if (advanceWidth) *advanceWidth = ttSHORT(info->data + info->hmtx + 4*glyph_index); - if (leftSideBearing) *leftSideBearing = ttSHORT(info->data + info->hmtx + 4*glyph_index + 2); - } else { - if (advanceWidth) *advanceWidth = ttSHORT(info->data + info->hmtx + 4*(numOfLongHorMetrics-1)); - if (leftSideBearing) *leftSideBearing = ttSHORT(info->data + info->hmtx + 4*numOfLongHorMetrics + 2*(glyph_index - numOfLongHorMetrics)); - } -} - -STBTT_DEF int stbtt_GetGlyphKernAdvance(const stbtt_fontinfo *info, int glyph1, int glyph2) -{ - stbtt_uint8 *data = info->data + info->kern; - stbtt_uint32 needle, straw; - int l, r, m; - - // we only look at the first table. it must be 'horizontal' and format 0. - if (!info->kern) - return 0; - if (ttUSHORT(data+2) < 1) // number of tables, need at least 1 - return 0; - if (ttUSHORT(data+8) != 1) // horizontal flag must be set in format - return 0; - - l = 0; - r = ttUSHORT(data+10) - 1; - needle = glyph1 << 16 | glyph2; - while (l <= r) { - m = (l + r) >> 1; - straw = ttULONG(data+18+(m*6)); // note: unaligned read - if (needle < straw) - r = m - 1; - else if (needle > straw) - l = m + 1; - else - return ttSHORT(data+22+(m*6)); - } - return 0; -} - -STBTT_DEF int stbtt_GetCodepointKernAdvance(const stbtt_fontinfo *info, int ch1, int ch2) -{ - if (!info->kern) // if no kerning table, don't waste time looking up both codepoint->glyphs - return 0; - return stbtt_GetGlyphKernAdvance(info, stbtt_FindGlyphIndex(info,ch1), stbtt_FindGlyphIndex(info,ch2)); -} - -STBTT_DEF void stbtt_GetCodepointHMetrics(const stbtt_fontinfo *info, int codepoint, int *advanceWidth, int *leftSideBearing) -{ - stbtt_GetGlyphHMetrics(info, stbtt_FindGlyphIndex(info,codepoint), advanceWidth, leftSideBearing); -} - -STBTT_DEF void stbtt_GetFontVMetrics(const stbtt_fontinfo *info, int *ascent, int *descent, int *lineGap) -{ - if (ascent ) *ascent = ttSHORT(info->data+info->hhea + 4); - if (descent) *descent = ttSHORT(info->data+info->hhea + 6); - if (lineGap) *lineGap = ttSHORT(info->data+info->hhea + 8); -} - -STBTT_DEF void stbtt_GetFontBoundingBox(const stbtt_fontinfo *info, int *x0, int *y0, int *x1, int *y1) -{ - *x0 = ttSHORT(info->data + info->head + 36); - *y0 = ttSHORT(info->data + info->head + 38); - *x1 = ttSHORT(info->data + info->head + 40); - *y1 = ttSHORT(info->data + info->head + 42); -} - -STBTT_DEF float stbtt_ScaleForPixelHeight(const stbtt_fontinfo *info, float height) -{ - int fheight = ttSHORT(info->data + info->hhea + 4) - ttSHORT(info->data + info->hhea + 6); - return (float) height / fheight; -} - -STBTT_DEF float stbtt_ScaleForMappingEmToPixels(const stbtt_fontinfo *info, float pixels) -{ - int unitsPerEm = ttUSHORT(info->data + info->head + 18); - return pixels / unitsPerEm; -} - -STBTT_DEF void stbtt_FreeShape(const stbtt_fontinfo *info, stbtt_vertex *v) -{ - STBTT_free(v, info->userdata); -} - -////////////////////////////////////////////////////////////////////////////// -// -// antialiasing software rasterizer -// - -STBTT_DEF void stbtt_GetGlyphBitmapBoxSubpixel(const stbtt_fontinfo *font, int glyph, float scale_x, float scale_y,float shift_x, float shift_y, int *ix0, int *iy0, int *ix1, int *iy1) -{ - int x0=0,y0=0,x1,y1; // =0 suppresses compiler warning - if (!stbtt_GetGlyphBox(font, glyph, &x0,&y0,&x1,&y1)) { - // e.g. space character - if (ix0) *ix0 = 0; - if (iy0) *iy0 = 0; - if (ix1) *ix1 = 0; - if (iy1) *iy1 = 0; - } else { - // move to integral bboxes (treating pixels as little squares, what pixels get touched)? - if (ix0) *ix0 = STBTT_ifloor( x0 * scale_x + shift_x); - if (iy0) *iy0 = STBTT_ifloor(-y1 * scale_y + shift_y); - if (ix1) *ix1 = STBTT_iceil ( x1 * scale_x + shift_x); - if (iy1) *iy1 = STBTT_iceil (-y0 * scale_y + shift_y); - } -} - -STBTT_DEF void stbtt_GetGlyphBitmapBox(const stbtt_fontinfo *font, int glyph, float scale_x, float scale_y, int *ix0, int *iy0, int *ix1, int *iy1) -{ - stbtt_GetGlyphBitmapBoxSubpixel(font, glyph, scale_x, scale_y,0.0f,0.0f, ix0, iy0, ix1, iy1); -} - -STBTT_DEF void stbtt_GetCodepointBitmapBoxSubpixel(const stbtt_fontinfo *font, int codepoint, float scale_x, float scale_y, float shift_x, float shift_y, int *ix0, int *iy0, int *ix1, int *iy1) -{ - stbtt_GetGlyphBitmapBoxSubpixel(font, stbtt_FindGlyphIndex(font,codepoint), scale_x, scale_y,shift_x,shift_y, ix0,iy0,ix1,iy1); -} - -STBTT_DEF void stbtt_GetCodepointBitmapBox(const stbtt_fontinfo *font, int codepoint, float scale_x, float scale_y, int *ix0, int *iy0, int *ix1, int *iy1) -{ - stbtt_GetCodepointBitmapBoxSubpixel(font, codepoint, scale_x, scale_y,0.0f,0.0f, ix0,iy0,ix1,iy1); -} - -////////////////////////////////////////////////////////////////////////////// -// -// Rasterizer - -typedef struct stbtt__hheap_chunk -{ - struct stbtt__hheap_chunk *next; -} stbtt__hheap_chunk; - -typedef struct stbtt__hheap -{ - struct stbtt__hheap_chunk *head; - void *first_free; - int num_remaining_in_head_chunk; -} stbtt__hheap; - -static void *stbtt__hheap_alloc(stbtt__hheap *hh, size_t size, void *userdata) -{ - if (hh->first_free) { - void *p = hh->first_free; - hh->first_free = * (void **) p; - return p; - } else { - if (hh->num_remaining_in_head_chunk == 0) { - int count = (size < 32 ? 2000 : size < 128 ? 800 : 100); - stbtt__hheap_chunk *c = (stbtt__hheap_chunk *) STBTT_malloc(sizeof(stbtt__hheap_chunk) + size * count, userdata); - if (c == NULL) - return NULL; - c->next = hh->head; - hh->head = c; - hh->num_remaining_in_head_chunk = count; - } - --hh->num_remaining_in_head_chunk; - return (char *) (hh->head) + size * hh->num_remaining_in_head_chunk; - } -} - -static void stbtt__hheap_free(stbtt__hheap *hh, void *p) -{ - *(void **) p = hh->first_free; - hh->first_free = p; -} - -static void stbtt__hheap_cleanup(stbtt__hheap *hh, void *userdata) -{ - stbtt__hheap_chunk *c = hh->head; - while (c) { - stbtt__hheap_chunk *n = c->next; - STBTT_free(c, userdata); - c = n; - } -} - -typedef struct stbtt__edge { - float x0,y0, x1,y1; - int invert; -} stbtt__edge; - - -typedef struct stbtt__active_edge -{ - struct stbtt__active_edge *next; - #if STBTT_RASTERIZER_VERSION==1 - int x,dx; - float ey; - int direction; - #elif STBTT_RASTERIZER_VERSION==2 - float fx,fdx,fdy; - float direction; - float sy; - float ey; - #else - #error "Unrecognized value of STBTT_RASTERIZER_VERSION" - #endif -} stbtt__active_edge; - -#if STBTT_RASTERIZER_VERSION == 1 -#define STBTT_FIXSHIFT 10 -#define STBTT_FIX (1 << STBTT_FIXSHIFT) -#define STBTT_FIXMASK (STBTT_FIX-1) - -static stbtt__active_edge *stbtt__new_active(stbtt__hheap *hh, stbtt__edge *e, int off_x, float start_point, void *userdata) -{ - stbtt__active_edge *z = (stbtt__active_edge *) stbtt__hheap_alloc(hh, sizeof(*z), userdata); - float dxdy = (e->x1 - e->x0) / (e->y1 - e->y0); - STBTT_assert(z != NULL); - if (!z) return z; - - // round dx down to avoid overshooting - if (dxdy < 0) - z->dx = -STBTT_ifloor(STBTT_FIX * -dxdy); - else - z->dx = STBTT_ifloor(STBTT_FIX * dxdy); - - z->x = STBTT_ifloor(STBTT_FIX * e->x0 + z->dx * (start_point - e->y0)); // use z->dx so when we offset later it's by the same amount - z->x -= off_x * STBTT_FIX; - - z->ey = e->y1; - z->next = 0; - z->direction = e->invert ? 1 : -1; - return z; -} -#elif STBTT_RASTERIZER_VERSION == 2 -static stbtt__active_edge *stbtt__new_active(stbtt__hheap *hh, stbtt__edge *e, int off_x, float start_point, void *userdata) -{ - stbtt__active_edge *z = (stbtt__active_edge *) stbtt__hheap_alloc(hh, sizeof(*z), userdata); - float dxdy = (e->x1 - e->x0) / (e->y1 - e->y0); - STBTT_assert(z != NULL); - //STBTT_assert(e->y0 <= start_point); - if (!z) return z; - z->fdx = dxdy; - z->fdy = dxdy != 0.0f ? (1.0f/dxdy) : 0.0f; - z->fx = e->x0 + dxdy * (start_point - e->y0); - z->fx -= off_x; - z->direction = e->invert ? 1.0f : -1.0f; - z->sy = e->y0; - z->ey = e->y1; - z->next = 0; - return z; -} -#else -#error "Unrecognized value of STBTT_RASTERIZER_VERSION" -#endif - -#if STBTT_RASTERIZER_VERSION == 1 -// note: this routine clips fills that extend off the edges... ideally this -// wouldn't happen, but it could happen if the truetype glyph bounding boxes -// are wrong, or if the user supplies a too-small bitmap -static void stbtt__fill_active_edges(unsigned char *scanline, int len, stbtt__active_edge *e, int max_weight) -{ - // non-zero winding fill - int x0=0, w=0; - - while (e) { - if (w == 0) { - // if we're currently at zero, we need to record the edge start point - x0 = e->x; w += e->direction; - } else { - int x1 = e->x; w += e->direction; - // if we went to zero, we need to draw - if (w == 0) { - int i = x0 >> STBTT_FIXSHIFT; - int j = x1 >> STBTT_FIXSHIFT; - - if (i < len && j >= 0) { - if (i == j) { - // x0,x1 are the same pixel, so compute combined coverage - scanline[i] = scanline[i] + (stbtt_uint8) ((x1 - x0) * max_weight >> STBTT_FIXSHIFT); - } else { - if (i >= 0) // add antialiasing for x0 - scanline[i] = scanline[i] + (stbtt_uint8) (((STBTT_FIX - (x0 & STBTT_FIXMASK)) * max_weight) >> STBTT_FIXSHIFT); - else - i = -1; // clip - - if (j < len) // add antialiasing for x1 - scanline[j] = scanline[j] + (stbtt_uint8) (((x1 & STBTT_FIXMASK) * max_weight) >> STBTT_FIXSHIFT); - else - j = len; // clip - - for (++i; i < j; ++i) // fill pixels between x0 and x1 - scanline[i] = scanline[i] + (stbtt_uint8) max_weight; - } - } - } - } - - e = e->next; - } -} - -static void stbtt__rasterize_sorted_edges(stbtt__bitmap *result, stbtt__edge *e, int n, int vsubsample, int off_x, int off_y, void *userdata) -{ - stbtt__hheap hh = { 0, 0, 0 }; - stbtt__active_edge *active = NULL; - int y,j=0; - int max_weight = (255 / vsubsample); // weight per vertical scanline - int s; // vertical subsample index - unsigned char scanline_data[512], *scanline; - - if (result->w > 512) - scanline = (unsigned char *) STBTT_malloc(result->w, userdata); - else - scanline = scanline_data; - - y = off_y * vsubsample; - e[n].y0 = (off_y + result->h) * (float) vsubsample + 1; - - while (j < result->h) { - STBTT_memset(scanline, 0, result->w); - for (s=0; s < vsubsample; ++s) { - // find center of pixel for this scanline - float scan_y = y + 0.5f; - stbtt__active_edge **step = &active; - - // update all active edges; - // remove all active edges that terminate before the center of this scanline - while (*step) { - stbtt__active_edge * z = *step; - if (z->ey <= scan_y) { - *step = z->next; // delete from list - STBTT_assert(z->direction); - z->direction = 0; - stbtt__hheap_free(&hh, z); - } else { - z->x += z->dx; // advance to position for current scanline - step = &((*step)->next); // advance through list - } - } - - // resort the list if needed - for(;;) { - int changed=0; - step = &active; - while (*step && (*step)->next) { - if ((*step)->x > (*step)->next->x) { - stbtt__active_edge *t = *step; - stbtt__active_edge *q = t->next; - - t->next = q->next; - q->next = t; - *step = q; - changed = 1; - } - step = &(*step)->next; - } - if (!changed) break; - } - - // insert all edges that start before the center of this scanline -- omit ones that also end on this scanline - while (e->y0 <= scan_y) { - if (e->y1 > scan_y) { - stbtt__active_edge *z = stbtt__new_active(&hh, e, off_x, scan_y, userdata); - if (z != NULL) { - // find insertion point - if (active == NULL) - active = z; - else if (z->x < active->x) { - // insert at front - z->next = active; - active = z; - } else { - // find thing to insert AFTER - stbtt__active_edge *p = active; - while (p->next && p->next->x < z->x) - p = p->next; - // at this point, p->next->x is NOT < z->x - z->next = p->next; - p->next = z; - } - } - } - ++e; - } - - // now process all active edges in XOR fashion - if (active) - stbtt__fill_active_edges(scanline, result->w, active, max_weight); - - ++y; - } - STBTT_memcpy(result->pixels + j * result->stride, scanline, result->w); - ++j; - } - - stbtt__hheap_cleanup(&hh, userdata); - - if (scanline != scanline_data) - STBTT_free(scanline, userdata); -} - -#elif STBTT_RASTERIZER_VERSION == 2 - -// the edge passed in here does not cross the vertical line at x or the vertical line at x+1 -// (i.e. it has already been clipped to those) -static void stbtt__handle_clipped_edge(float *scanline, int x, stbtt__active_edge *e, float x0, float y0, float x1, float y1) -{ - if (y0 == y1) return; - STBTT_assert(y0 < y1); - STBTT_assert(e->sy <= e->ey); - if (y0 > e->ey) return; - if (y1 < e->sy) return; - if (y0 < e->sy) { - x0 += (x1-x0) * (e->sy - y0) / (y1-y0); - y0 = e->sy; - } - if (y1 > e->ey) { - x1 += (x1-x0) * (e->ey - y1) / (y1-y0); - y1 = e->ey; - } - - if (x0 == x) - STBTT_assert(x1 <= x+1); - else if (x0 == x+1) - STBTT_assert(x1 >= x); - else if (x0 <= x) - STBTT_assert(x1 <= x); - else if (x0 >= x+1) - STBTT_assert(x1 >= x+1); - else - STBTT_assert(x1 >= x && x1 <= x+1); - - if (x0 <= x && x1 <= x) - scanline[x] += e->direction * (y1-y0); - else if (x0 >= x+1 && x1 >= x+1) - ; - else { - STBTT_assert(x0 >= x && x0 <= x+1 && x1 >= x && x1 <= x+1); - scanline[x] += e->direction * (y1-y0) * (1-((x0-x)+(x1-x))/2); // coverage = 1 - average x position - } -} - -static void stbtt__fill_active_edges_new(float *scanline, float *scanline_fill, int len, stbtt__active_edge *e, float y_top) -{ - float y_bottom = y_top+1; - - while (e) { - // brute force every pixel - - // compute intersection points with top & bottom - STBTT_assert(e->ey >= y_top); - - if (e->fdx == 0) { - float x0 = e->fx; - if (x0 < len) { - if (x0 >= 0) { - stbtt__handle_clipped_edge(scanline,(int) x0,e, x0,y_top, x0,y_bottom); - stbtt__handle_clipped_edge(scanline_fill-1,(int) x0+1,e, x0,y_top, x0,y_bottom); - } else { - stbtt__handle_clipped_edge(scanline_fill-1,0,e, x0,y_top, x0,y_bottom); - } - } - } else { - float x0 = e->fx; - float dx = e->fdx; - float xb = x0 + dx; - float x_top, x_bottom; - float sy0,sy1; - float dy = e->fdy; - STBTT_assert(e->sy <= y_bottom && e->ey >= y_top); - - // compute endpoints of line segment clipped to this scanline (if the - // line segment starts on this scanline. x0 is the intersection of the - // line with y_top, but that may be off the line segment. - if (e->sy > y_top) { - x_top = x0 + dx * (e->sy - y_top); - sy0 = e->sy; - } else { - x_top = x0; - sy0 = y_top; - } - if (e->ey < y_bottom) { - x_bottom = x0 + dx * (e->ey - y_top); - sy1 = e->ey; - } else { - x_bottom = xb; - sy1 = y_bottom; - } - - if (x_top >= 0 && x_bottom >= 0 && x_top < len && x_bottom < len) { - // from here on, we don't have to range check x values - - if ((int) x_top == (int) x_bottom) { - float height; - // simple case, only spans one pixel - int x = (int) x_top; - height = sy1 - sy0; - STBTT_assert(x >= 0 && x < len); - scanline[x] += e->direction * (1-((x_top - x) + (x_bottom-x))/2) * height; - scanline_fill[x] += e->direction * height; // everything right of this pixel is filled - } else { - int x,x1,x2; - float y_crossing, step, sign, area; - // covers 2+ pixels - if (x_top > x_bottom) { - // flip scanline vertically; signed area is the same - float t; - sy0 = y_bottom - (sy0 - y_top); - sy1 = y_bottom - (sy1 - y_top); - t = sy0, sy0 = sy1, sy1 = t; - t = x_bottom, x_bottom = x_top, x_top = t; - dx = -dx; - dy = -dy; - t = x0, x0 = xb, xb = t; - } - - x1 = (int) x_top; - x2 = (int) x_bottom; - // compute intersection with y axis at x1+1 - y_crossing = (x1+1 - x0) * dy + y_top; - - sign = e->direction; - // area of the rectangle covered from y0..y_crossing - area = sign * (y_crossing-sy0); - // area of the triangle (x_top,y0), (x+1,y0), (x+1,y_crossing) - scanline[x1] += area * (1-((x_top - x1)+(x1+1-x1))/2); - - step = sign * dy; - for (x = x1+1; x < x2; ++x) { - scanline[x] += area + step/2; - area += step; - } - y_crossing += dy * (x2 - (x1+1)); - - STBTT_assert(STBTT_fabs(area) <= 1.01f); - - scanline[x2] += area + sign * (1-((x2-x2)+(x_bottom-x2))/2) * (sy1-y_crossing); - - scanline_fill[x2] += sign * (sy1-sy0); - } - } else { - // if edge goes outside of box we're drawing, we require - // clipping logic. since this does not match the intended use - // of this library, we use a different, very slow brute - // force implementation - int x; - for (x=0; x < len; ++x) { - // cases: - // - // there can be up to two intersections with the pixel. any intersection - // with left or right edges can be handled by splitting into two (or three) - // regions. intersections with top & bottom do not necessitate case-wise logic. - // - // the old way of doing this found the intersections with the left & right edges, - // then used some simple logic to produce up to three segments in sorted order - // from top-to-bottom. however, this had a problem: if an x edge was epsilon - // across the x border, then the corresponding y position might not be distinct - // from the other y segment, and it might ignored as an empty segment. to avoid - // that, we need to explicitly produce segments based on x positions. - - // rename variables to clear pairs - float y0 = y_top; - float x1 = (float) (x); - float x2 = (float) (x+1); - float x3 = xb; - float y3 = y_bottom; - float y1,y2; - - // x = e->x + e->dx * (y-y_top) - // (y-y_top) = (x - e->x) / e->dx - // y = (x - e->x) / e->dx + y_top - y1 = (x - x0) / dx + y_top; - y2 = (x+1 - x0) / dx + y_top; - - if (x0 < x1 && x3 > x2) { // three segments descending down-right - stbtt__handle_clipped_edge(scanline,x,e, x0,y0, x1,y1); - stbtt__handle_clipped_edge(scanline,x,e, x1,y1, x2,y2); - stbtt__handle_clipped_edge(scanline,x,e, x2,y2, x3,y3); - } else if (x3 < x1 && x0 > x2) { // three segments descending down-left - stbtt__handle_clipped_edge(scanline,x,e, x0,y0, x2,y2); - stbtt__handle_clipped_edge(scanline,x,e, x2,y2, x1,y1); - stbtt__handle_clipped_edge(scanline,x,e, x1,y1, x3,y3); - } else if (x0 < x1 && x3 > x1) { // two segments across x, down-right - stbtt__handle_clipped_edge(scanline,x,e, x0,y0, x1,y1); - stbtt__handle_clipped_edge(scanline,x,e, x1,y1, x3,y3); - } else if (x3 < x1 && x0 > x1) { // two segments across x, down-left - stbtt__handle_clipped_edge(scanline,x,e, x0,y0, x1,y1); - stbtt__handle_clipped_edge(scanline,x,e, x1,y1, x3,y3); - } else if (x0 < x2 && x3 > x2) { // two segments across x+1, down-right - stbtt__handle_clipped_edge(scanline,x,e, x0,y0, x2,y2); - stbtt__handle_clipped_edge(scanline,x,e, x2,y2, x3,y3); - } else if (x3 < x2 && x0 > x2) { // two segments across x+1, down-left - stbtt__handle_clipped_edge(scanline,x,e, x0,y0, x2,y2); - stbtt__handle_clipped_edge(scanline,x,e, x2,y2, x3,y3); - } else { // one segment - stbtt__handle_clipped_edge(scanline,x,e, x0,y0, x3,y3); - } - } - } - } - e = e->next; - } -} - -// directly AA rasterize edges w/o supersampling -static void stbtt__rasterize_sorted_edges(stbtt__bitmap *result, stbtt__edge *e, int n, int vsubsample, int off_x, int off_y, void *userdata) -{ - stbtt__hheap hh = { 0, 0, 0 }; - stbtt__active_edge *active = NULL; - int y,j=0, i; - float scanline_data[129], *scanline, *scanline2; - - STBTT__NOTUSED(vsubsample); - - if (result->w > 64) - scanline = (float *) STBTT_malloc((result->w*2+1) * sizeof(float), userdata); - else - scanline = scanline_data; - - scanline2 = scanline + result->w; - - y = off_y; - e[n].y0 = (float) (off_y + result->h) + 1; - - while (j < result->h) { - // find center of pixel for this scanline - float scan_y_top = y + 0.0f; - float scan_y_bottom = y + 1.0f; - stbtt__active_edge **step = &active; - - STBTT_memset(scanline , 0, result->w*sizeof(scanline[0])); - STBTT_memset(scanline2, 0, (result->w+1)*sizeof(scanline[0])); - - // update all active edges; - // remove all active edges that terminate before the top of this scanline - while (*step) { - stbtt__active_edge * z = *step; - if (z->ey <= scan_y_top) { - *step = z->next; // delete from list - STBTT_assert(z->direction); - z->direction = 0; - stbtt__hheap_free(&hh, z); - } else { - step = &((*step)->next); // advance through list - } - } - - // insert all edges that start before the bottom of this scanline - while (e->y0 <= scan_y_bottom) { - if (e->y0 != e->y1) { - stbtt__active_edge *z = stbtt__new_active(&hh, e, off_x, scan_y_top, userdata); - if (z != NULL) { - STBTT_assert(z->ey >= scan_y_top); - // insert at front - z->next = active; - active = z; - } - } - ++e; - } - - // now process all active edges - if (active) - stbtt__fill_active_edges_new(scanline, scanline2+1, result->w, active, scan_y_top); - - { - float sum = 0; - for (i=0; i < result->w; ++i) { - float k; - int m; - sum += scanline2[i]; - k = scanline[i] + sum; - k = (float) STBTT_fabs(k)*255 + 0.5f; - m = (int) k; - if (m > 255) m = 255; - result->pixels[j*result->stride + i] = (unsigned char) m; - } - } - // advance all the edges - step = &active; - while (*step) { - stbtt__active_edge *z = *step; - z->fx += z->fdx; // advance to position for current scanline - step = &((*step)->next); // advance through list - } - - ++y; - ++j; - } - - stbtt__hheap_cleanup(&hh, userdata); - - if (scanline != scanline_data) - STBTT_free(scanline, userdata); -} -#else -#error "Unrecognized value of STBTT_RASTERIZER_VERSION" -#endif - -#define STBTT__COMPARE(a,b) ((a)->y0 < (b)->y0) - -static void stbtt__sort_edges_ins_sort(stbtt__edge *p, int n) -{ - int i,j; - for (i=1; i < n; ++i) { - stbtt__edge t = p[i], *a = &t; - j = i; - while (j > 0) { - stbtt__edge *b = &p[j-1]; - int c = STBTT__COMPARE(a,b); - if (!c) break; - p[j] = p[j-1]; - --j; - } - if (i != j) - p[j] = t; - } -} - -static void stbtt__sort_edges_quicksort(stbtt__edge *p, int n) -{ - /* threshhold for transitioning to insertion sort */ - while (n > 12) { - stbtt__edge t; - int c01,c12,c,m,i,j; - - /* compute median of three */ - m = n >> 1; - c01 = STBTT__COMPARE(&p[0],&p[m]); - c12 = STBTT__COMPARE(&p[m],&p[n-1]); - /* if 0 >= mid >= end, or 0 < mid < end, then use mid */ - if (c01 != c12) { - /* otherwise, we'll need to swap something else to middle */ - int z; - c = STBTT__COMPARE(&p[0],&p[n-1]); - /* 0>mid && midn => n; 0 0 */ - /* 0n: 0>n => 0; 0 n */ - z = (c == c12) ? 0 : n-1; - t = p[z]; - p[z] = p[m]; - p[m] = t; - } - /* now p[m] is the median-of-three */ - /* swap it to the beginning so it won't move around */ - t = p[0]; - p[0] = p[m]; - p[m] = t; - - /* partition loop */ - i=1; - j=n-1; - for(;;) { - /* handling of equality is crucial here */ - /* for sentinels & efficiency with duplicates */ - for (;;++i) { - if (!STBTT__COMPARE(&p[i], &p[0])) break; - } - for (;;--j) { - if (!STBTT__COMPARE(&p[0], &p[j])) break; - } - /* make sure we haven't crossed */ - if (i >= j) break; - t = p[i]; - p[i] = p[j]; - p[j] = t; - - ++i; - --j; - } - /* recurse on smaller side, iterate on larger */ - if (j < (n-i)) { - stbtt__sort_edges_quicksort(p,j); - p = p+i; - n = n-i; - } else { - stbtt__sort_edges_quicksort(p+i, n-i); - n = j; - } - } -} - -static void stbtt__sort_edges(stbtt__edge *p, int n) -{ - stbtt__sort_edges_quicksort(p, n); - stbtt__sort_edges_ins_sort(p, n); -} - -typedef struct -{ - float x,y; -} stbtt__point; - -static void stbtt__rasterize(stbtt__bitmap *result, stbtt__point *pts, int *wcount, int windings, float scale_x, float scale_y, float shift_x, float shift_y, int off_x, int off_y, int invert, void *userdata) -{ - float y_scale_inv = invert ? -scale_y : scale_y; - stbtt__edge *e; - int n,i,j,k,m; -#if STBTT_RASTERIZER_VERSION == 1 - int vsubsample = result->h < 8 ? 15 : 5; -#elif STBTT_RASTERIZER_VERSION == 2 - int vsubsample = 1; -#else - #error "Unrecognized value of STBTT_RASTERIZER_VERSION" -#endif - // vsubsample should divide 255 evenly; otherwise we won't reach full opacity - - // now we have to blow out the windings into explicit edge lists - n = 0; - for (i=0; i < windings; ++i) - n += wcount[i]; - - e = (stbtt__edge *) STBTT_malloc(sizeof(*e) * (n+1), userdata); // add an extra one as a sentinel - if (e == 0) return; - n = 0; - - m=0; - for (i=0; i < windings; ++i) { - stbtt__point *p = pts + m; - m += wcount[i]; - j = wcount[i]-1; - for (k=0; k < wcount[i]; j=k++) { - int a=k,b=j; - // skip the edge if horizontal - if (p[j].y == p[k].y) - continue; - // add edge from j to k to the list - e[n].invert = 0; - if (invert ? p[j].y > p[k].y : p[j].y < p[k].y) { - e[n].invert = 1; - a=j,b=k; - } - e[n].x0 = p[a].x * scale_x + shift_x; - e[n].y0 = (p[a].y * y_scale_inv + shift_y) * vsubsample; - e[n].x1 = p[b].x * scale_x + shift_x; - e[n].y1 = (p[b].y * y_scale_inv + shift_y) * vsubsample; - ++n; - } - } - - // now sort the edges by their highest point (should snap to integer, and then by x) - //STBTT_sort(e, n, sizeof(e[0]), stbtt__edge_compare); - stbtt__sort_edges(e, n); - - // now, traverse the scanlines and find the intersections on each scanline, use xor winding rule - stbtt__rasterize_sorted_edges(result, e, n, vsubsample, off_x, off_y, userdata); - - STBTT_free(e, userdata); -} - -static void stbtt__add_point(stbtt__point *points, int n, float x, float y) -{ - if (!points) return; // during first pass, it's unallocated - points[n].x = x; - points[n].y = y; -} - -// tesselate until threshhold p is happy... @TODO warped to compensate for non-linear stretching -static int stbtt__tesselate_curve(stbtt__point *points, int *num_points, float x0, float y0, float x1, float y1, float x2, float y2, float objspace_flatness_squared, int n) -{ - // midpoint - float mx = (x0 + 2*x1 + x2)/4; - float my = (y0 + 2*y1 + y2)/4; - // versus directly drawn line - float dx = (x0+x2)/2 - mx; - float dy = (y0+y2)/2 - my; - if (n > 16) // 65536 segments on one curve better be enough! - return 1; - if (dx*dx+dy*dy > objspace_flatness_squared) { // half-pixel error allowed... need to be smaller if AA - stbtt__tesselate_curve(points, num_points, x0,y0, (x0+x1)/2.0f,(y0+y1)/2.0f, mx,my, objspace_flatness_squared,n+1); - stbtt__tesselate_curve(points, num_points, mx,my, (x1+x2)/2.0f,(y1+y2)/2.0f, x2,y2, objspace_flatness_squared,n+1); - } else { - stbtt__add_point(points, *num_points,x2,y2); - *num_points = *num_points+1; - } - return 1; -} - -// returns number of contours -static stbtt__point *stbtt_FlattenCurves(stbtt_vertex *vertices, int num_verts, float objspace_flatness, int **contour_lengths, int *num_contours, void *userdata) -{ - stbtt__point *points=0; - int num_points=0; - - float objspace_flatness_squared = objspace_flatness * objspace_flatness; - int i,n=0,start=0, pass; - - // count how many "moves" there are to get the contour count - for (i=0; i < num_verts; ++i) - if (vertices[i].type == STBTT_vmove) - ++n; - - *num_contours = n; - if (n == 0) return 0; - - *contour_lengths = (int *) STBTT_malloc(sizeof(**contour_lengths) * n, userdata); - - if (*contour_lengths == 0) { - *num_contours = 0; - return 0; - } - - // make two passes through the points so we don't need to realloc - for (pass=0; pass < 2; ++pass) { - float x=0,y=0; - if (pass == 1) { - points = (stbtt__point *) STBTT_malloc(num_points * sizeof(points[0]), userdata); - if (points == NULL) goto error; - } - num_points = 0; - n= -1; - for (i=0; i < num_verts; ++i) { - switch (vertices[i].type) { - case STBTT_vmove: - // start the next contour - if (n >= 0) - (*contour_lengths)[n] = num_points - start; - ++n; - start = num_points; - - x = vertices[i].x, y = vertices[i].y; - stbtt__add_point(points, num_points++, x,y); - break; - case STBTT_vline: - x = vertices[i].x, y = vertices[i].y; - stbtt__add_point(points, num_points++, x, y); - break; - case STBTT_vcurve: - stbtt__tesselate_curve(points, &num_points, x,y, - vertices[i].cx, vertices[i].cy, - vertices[i].x, vertices[i].y, - objspace_flatness_squared, 0); - x = vertices[i].x, y = vertices[i].y; - break; - } - } - (*contour_lengths)[n] = num_points - start; - } - - return points; -error: - STBTT_free(points, userdata); - STBTT_free(*contour_lengths, userdata); - *contour_lengths = 0; - *num_contours = 0; - return NULL; -} - -STBTT_DEF void stbtt_Rasterize(stbtt__bitmap *result, float flatness_in_pixels, stbtt_vertex *vertices, int num_verts, float scale_x, float scale_y, float shift_x, float shift_y, int x_off, int y_off, int invert, void *userdata) -{ - float scale = scale_x > scale_y ? scale_y : scale_x; - int winding_count, *winding_lengths; - stbtt__point *windings = stbtt_FlattenCurves(vertices, num_verts, flatness_in_pixels / scale, &winding_lengths, &winding_count, userdata); - if (windings) { - stbtt__rasterize(result, windings, winding_lengths, winding_count, scale_x, scale_y, shift_x, shift_y, x_off, y_off, invert, userdata); - STBTT_free(winding_lengths, userdata); - STBTT_free(windings, userdata); - } -} - -STBTT_DEF void stbtt_FreeBitmap(unsigned char *bitmap, void *userdata) -{ - STBTT_free(bitmap, userdata); -} - -STBTT_DEF unsigned char *stbtt_GetGlyphBitmapSubpixel(const stbtt_fontinfo *info, float scale_x, float scale_y, float shift_x, float shift_y, int glyph, int *width, int *height, int *xoff, int *yoff) -{ - int ix0,iy0,ix1,iy1; - stbtt__bitmap gbm; - stbtt_vertex *vertices; - int num_verts = stbtt_GetGlyphShape(info, glyph, &vertices); - - if (scale_x == 0) scale_x = scale_y; - if (scale_y == 0) { - if (scale_x == 0) { - STBTT_free(vertices, info->userdata); - return NULL; - } - scale_y = scale_x; - } - - stbtt_GetGlyphBitmapBoxSubpixel(info, glyph, scale_x, scale_y, shift_x, shift_y, &ix0,&iy0,&ix1,&iy1); - - // now we get the size - gbm.w = (ix1 - ix0); - gbm.h = (iy1 - iy0); - gbm.pixels = NULL; // in case we error - - if (width ) *width = gbm.w; - if (height) *height = gbm.h; - if (xoff ) *xoff = ix0; - if (yoff ) *yoff = iy0; - - if (gbm.w && gbm.h) { - gbm.pixels = (unsigned char *) STBTT_malloc(gbm.w * gbm.h, info->userdata); - if (gbm.pixels) { - gbm.stride = gbm.w; - - stbtt_Rasterize(&gbm, 0.35f, vertices, num_verts, scale_x, scale_y, shift_x, shift_y, ix0, iy0, 1, info->userdata); - } - } - STBTT_free(vertices, info->userdata); - return gbm.pixels; -} - -STBTT_DEF unsigned char *stbtt_GetGlyphBitmap(const stbtt_fontinfo *info, float scale_x, float scale_y, int glyph, int *width, int *height, int *xoff, int *yoff) -{ - return stbtt_GetGlyphBitmapSubpixel(info, scale_x, scale_y, 0.0f, 0.0f, glyph, width, height, xoff, yoff); -} - -STBTT_DEF void stbtt_MakeGlyphBitmapSubpixel(const stbtt_fontinfo *info, unsigned char *output, int out_w, int out_h, int out_stride, float scale_x, float scale_y, float shift_x, float shift_y, int glyph) -{ - int ix0,iy0; - stbtt_vertex *vertices; - int num_verts = stbtt_GetGlyphShape(info, glyph, &vertices); - stbtt__bitmap gbm; - - stbtt_GetGlyphBitmapBoxSubpixel(info, glyph, scale_x, scale_y, shift_x, shift_y, &ix0,&iy0,0,0); - gbm.pixels = output; - gbm.w = out_w; - gbm.h = out_h; - gbm.stride = out_stride; - - if (gbm.w && gbm.h) - stbtt_Rasterize(&gbm, 0.35f, vertices, num_verts, scale_x, scale_y, shift_x, shift_y, ix0,iy0, 1, info->userdata); - - STBTT_free(vertices, info->userdata); -} - -STBTT_DEF void stbtt_MakeGlyphBitmap(const stbtt_fontinfo *info, unsigned char *output, int out_w, int out_h, int out_stride, float scale_x, float scale_y, int glyph) -{ - stbtt_MakeGlyphBitmapSubpixel(info, output, out_w, out_h, out_stride, scale_x, scale_y, 0.0f,0.0f, glyph); -} - -STBTT_DEF unsigned char *stbtt_GetCodepointBitmapSubpixel(const stbtt_fontinfo *info, float scale_x, float scale_y, float shift_x, float shift_y, int codepoint, int *width, int *height, int *xoff, int *yoff) -{ - return stbtt_GetGlyphBitmapSubpixel(info, scale_x, scale_y,shift_x,shift_y, stbtt_FindGlyphIndex(info,codepoint), width,height,xoff,yoff); -} - -STBTT_DEF void stbtt_MakeCodepointBitmapSubpixel(const stbtt_fontinfo *info, unsigned char *output, int out_w, int out_h, int out_stride, float scale_x, float scale_y, float shift_x, float shift_y, int codepoint) -{ - stbtt_MakeGlyphBitmapSubpixel(info, output, out_w, out_h, out_stride, scale_x, scale_y, shift_x, shift_y, stbtt_FindGlyphIndex(info,codepoint)); -} - -STBTT_DEF unsigned char *stbtt_GetCodepointBitmap(const stbtt_fontinfo *info, float scale_x, float scale_y, int codepoint, int *width, int *height, int *xoff, int *yoff) -{ - return stbtt_GetCodepointBitmapSubpixel(info, scale_x, scale_y, 0.0f,0.0f, codepoint, width,height,xoff,yoff); -} - -STBTT_DEF void stbtt_MakeCodepointBitmap(const stbtt_fontinfo *info, unsigned char *output, int out_w, int out_h, int out_stride, float scale_x, float scale_y, int codepoint) -{ - stbtt_MakeCodepointBitmapSubpixel(info, output, out_w, out_h, out_stride, scale_x, scale_y, 0.0f,0.0f, codepoint); -} - -////////////////////////////////////////////////////////////////////////////// -// -// bitmap baking -// -// This is SUPER-CRAPPY packing to keep source code small - -STBTT_DEF int stbtt_BakeFontBitmap(const unsigned char *data, int offset, // font location (use offset=0 for plain .ttf) - float pixel_height, // height of font in pixels - unsigned char *pixels, int pw, int ph, // bitmap to be filled in - int first_char, int num_chars, // characters to bake - stbtt_bakedchar *chardata) -{ - float scale; - int x,y,bottom_y, i; - stbtt_fontinfo f; - f.userdata = NULL; - if (!stbtt_InitFont(&f, data, offset)) - return -1; - STBTT_memset(pixels, 0, pw*ph); // background of 0 around pixels - x=y=1; - bottom_y = 1; - - scale = stbtt_ScaleForPixelHeight(&f, pixel_height); - - for (i=0; i < num_chars; ++i) { - int advance, lsb, x0,y0,x1,y1,gw,gh; - int g = stbtt_FindGlyphIndex(&f, first_char + i); - stbtt_GetGlyphHMetrics(&f, g, &advance, &lsb); - stbtt_GetGlyphBitmapBox(&f, g, scale,scale, &x0,&y0,&x1,&y1); - gw = x1-x0; - gh = y1-y0; - if (x + gw + 1 >= pw) - y = bottom_y, x = 1; // advance to next row - if (y + gh + 1 >= ph) // check if it fits vertically AFTER potentially moving to next row - return -i; - STBTT_assert(x+gw < pw); - STBTT_assert(y+gh < ph); - stbtt_MakeGlyphBitmap(&f, pixels+x+y*pw, gw,gh,pw, scale,scale, g); - chardata[i].x0 = (stbtt_int16) x; - chardata[i].y0 = (stbtt_int16) y; - chardata[i].x1 = (stbtt_int16) (x + gw); - chardata[i].y1 = (stbtt_int16) (y + gh); - chardata[i].xadvance = scale * advance; - chardata[i].xoff = (float) x0; - chardata[i].yoff = (float) y0; - x = x + gw + 1; - if (y+gh+1 > bottom_y) - bottom_y = y+gh+1; - } - return bottom_y; -} - -STBTT_DEF void stbtt_GetBakedQuad(stbtt_bakedchar *chardata, int pw, int ph, int char_index, float *xpos, float *ypos, stbtt_aligned_quad *q, int opengl_fillrule) -{ - float d3d_bias = opengl_fillrule ? 0 : -0.5f; - float ipw = 1.0f / pw, iph = 1.0f / ph; - stbtt_bakedchar *b = chardata + char_index; - int round_x = STBTT_ifloor((*xpos + b->xoff) + 0.5f); - int round_y = STBTT_ifloor((*ypos + b->yoff) + 0.5f); - - q->x0 = round_x + d3d_bias; - q->y0 = round_y + d3d_bias; - q->x1 = round_x + b->x1 - b->x0 + d3d_bias; - q->y1 = round_y + b->y1 - b->y0 + d3d_bias; - - q->s0 = b->x0 * ipw; - q->t0 = b->y0 * iph; - q->s1 = b->x1 * ipw; - q->t1 = b->y1 * iph; - - *xpos += b->xadvance; -} - -////////////////////////////////////////////////////////////////////////////// -// -// rectangle packing replacement routines if you don't have stb_rect_pack.h -// - -#ifndef STB_RECT_PACK_VERSION - -typedef int stbrp_coord; - -//////////////////////////////////////////////////////////////////////////////////// -// // -// // -// COMPILER WARNING ?!?!? // -// // -// // -// if you get a compile warning due to these symbols being defined more than // -// once, move #include "stb_rect_pack.h" before #include "stb_truetype.h" // -// // -//////////////////////////////////////////////////////////////////////////////////// - -typedef struct -{ - int width,height; - int x,y,bottom_y; -} stbrp_context; - -typedef struct -{ - unsigned char x; -} stbrp_node; - -struct stbrp_rect -{ - stbrp_coord x,y; - int id,w,h,was_packed; -}; - -static void stbrp_init_target(stbrp_context *con, int pw, int ph, stbrp_node *nodes, int num_nodes) -{ - con->width = pw; - con->height = ph; - con->x = 0; - con->y = 0; - con->bottom_y = 0; - STBTT__NOTUSED(nodes); - STBTT__NOTUSED(num_nodes); -} - -static void stbrp_pack_rects(stbrp_context *con, stbrp_rect *rects, int num_rects) -{ - int i; - for (i=0; i < num_rects; ++i) { - if (con->x + rects[i].w > con->width) { - con->x = 0; - con->y = con->bottom_y; - } - if (con->y + rects[i].h > con->height) - break; - rects[i].x = con->x; - rects[i].y = con->y; - rects[i].was_packed = 1; - con->x += rects[i].w; - if (con->y + rects[i].h > con->bottom_y) - con->bottom_y = con->y + rects[i].h; - } - for ( ; i < num_rects; ++i) - rects[i].was_packed = 0; -} -#endif - -////////////////////////////////////////////////////////////////////////////// -// -// bitmap baking -// -// This is SUPER-AWESOME (tm Ryan Gordon) packing using stb_rect_pack.h. If -// stb_rect_pack.h isn't available, it uses the BakeFontBitmap strategy. - -STBTT_DEF int stbtt_PackBegin(stbtt_pack_context *spc, unsigned char *pixels, int pw, int ph, int stride_in_bytes, int padding, void *alloc_context) -{ - stbrp_context *context = (stbrp_context *) STBTT_malloc(sizeof(*context) ,alloc_context); - int num_nodes = pw - padding; - stbrp_node *nodes = (stbrp_node *) STBTT_malloc(sizeof(*nodes ) * num_nodes,alloc_context); - - if (context == NULL || nodes == NULL) { - if (context != NULL) STBTT_free(context, alloc_context); - if (nodes != NULL) STBTT_free(nodes , alloc_context); - return 0; - } - - spc->user_allocator_context = alloc_context; - spc->width = pw; - spc->height = ph; - spc->pixels = pixels; - spc->pack_info = context; - spc->nodes = nodes; - spc->padding = padding; - spc->stride_in_bytes = stride_in_bytes != 0 ? stride_in_bytes : pw; - spc->h_oversample = 1; - spc->v_oversample = 1; - - stbrp_init_target(context, pw-padding, ph-padding, nodes, num_nodes); - - if (pixels) - STBTT_memset(pixels, 0, pw*ph); // background of 0 around pixels - - return 1; -} - -STBTT_DEF void stbtt_PackEnd (stbtt_pack_context *spc) -{ - STBTT_free(spc->nodes , spc->user_allocator_context); - STBTT_free(spc->pack_info, spc->user_allocator_context); -} - -STBTT_DEF void stbtt_PackSetOversampling(stbtt_pack_context *spc, unsigned int h_oversample, unsigned int v_oversample) -{ - STBTT_assert(h_oversample <= STBTT_MAX_OVERSAMPLE); - STBTT_assert(v_oversample <= STBTT_MAX_OVERSAMPLE); - if (h_oversample <= STBTT_MAX_OVERSAMPLE) - spc->h_oversample = h_oversample; - if (v_oversample <= STBTT_MAX_OVERSAMPLE) - spc->v_oversample = v_oversample; -} - -#define STBTT__OVER_MASK (STBTT_MAX_OVERSAMPLE-1) - -static void stbtt__h_prefilter(unsigned char *pixels, int w, int h, int stride_in_bytes, unsigned int kernel_width) -{ - unsigned char buffer[STBTT_MAX_OVERSAMPLE]; - int safe_w = w - kernel_width; - int j; - STBTT_memset(buffer, 0, STBTT_MAX_OVERSAMPLE); // suppress bogus warning from VS2013 -analyze - for (j=0; j < h; ++j) { - int i; - unsigned int total; - STBTT_memset(buffer, 0, kernel_width); - - total = 0; - - // make kernel_width a constant in common cases so compiler can optimize out the divide - switch (kernel_width) { - case 2: - for (i=0; i <= safe_w; ++i) { - total += pixels[i] - buffer[i & STBTT__OVER_MASK]; - buffer[(i+kernel_width) & STBTT__OVER_MASK] = pixels[i]; - pixels[i] = (unsigned char) (total / 2); - } - break; - case 3: - for (i=0; i <= safe_w; ++i) { - total += pixels[i] - buffer[i & STBTT__OVER_MASK]; - buffer[(i+kernel_width) & STBTT__OVER_MASK] = pixels[i]; - pixels[i] = (unsigned char) (total / 3); - } - break; - case 4: - for (i=0; i <= safe_w; ++i) { - total += pixels[i] - buffer[i & STBTT__OVER_MASK]; - buffer[(i+kernel_width) & STBTT__OVER_MASK] = pixels[i]; - pixels[i] = (unsigned char) (total / 4); - } - break; - case 5: - for (i=0; i <= safe_w; ++i) { - total += pixels[i] - buffer[i & STBTT__OVER_MASK]; - buffer[(i+kernel_width) & STBTT__OVER_MASK] = pixels[i]; - pixels[i] = (unsigned char) (total / 5); - } - break; - default: - for (i=0; i <= safe_w; ++i) { - total += pixels[i] - buffer[i & STBTT__OVER_MASK]; - buffer[(i+kernel_width) & STBTT__OVER_MASK] = pixels[i]; - pixels[i] = (unsigned char) (total / kernel_width); - } - break; - } - - for (; i < w; ++i) { - STBTT_assert(pixels[i] == 0); - total -= buffer[i & STBTT__OVER_MASK]; - pixels[i] = (unsigned char) (total / kernel_width); - } - - pixels += stride_in_bytes; - } -} - -static void stbtt__v_prefilter(unsigned char *pixels, int w, int h, int stride_in_bytes, unsigned int kernel_width) -{ - unsigned char buffer[STBTT_MAX_OVERSAMPLE]; - int safe_h = h - kernel_width; - int j; - STBTT_memset(buffer, 0, STBTT_MAX_OVERSAMPLE); // suppress bogus warning from VS2013 -analyze - for (j=0; j < w; ++j) { - int i; - unsigned int total; - STBTT_memset(buffer, 0, kernel_width); - - total = 0; - - // make kernel_width a constant in common cases so compiler can optimize out the divide - switch (kernel_width) { - case 2: - for (i=0; i <= safe_h; ++i) { - total += pixels[i*stride_in_bytes] - buffer[i & STBTT__OVER_MASK]; - buffer[(i+kernel_width) & STBTT__OVER_MASK] = pixels[i*stride_in_bytes]; - pixels[i*stride_in_bytes] = (unsigned char) (total / 2); - } - break; - case 3: - for (i=0; i <= safe_h; ++i) { - total += pixels[i*stride_in_bytes] - buffer[i & STBTT__OVER_MASK]; - buffer[(i+kernel_width) & STBTT__OVER_MASK] = pixels[i*stride_in_bytes]; - pixels[i*stride_in_bytes] = (unsigned char) (total / 3); - } - break; - case 4: - for (i=0; i <= safe_h; ++i) { - total += pixels[i*stride_in_bytes] - buffer[i & STBTT__OVER_MASK]; - buffer[(i+kernel_width) & STBTT__OVER_MASK] = pixels[i*stride_in_bytes]; - pixels[i*stride_in_bytes] = (unsigned char) (total / 4); - } - break; - case 5: - for (i=0; i <= safe_h; ++i) { - total += pixels[i*stride_in_bytes] - buffer[i & STBTT__OVER_MASK]; - buffer[(i+kernel_width) & STBTT__OVER_MASK] = pixels[i*stride_in_bytes]; - pixels[i*stride_in_bytes] = (unsigned char) (total / 5); - } - break; - default: - for (i=0; i <= safe_h; ++i) { - total += pixels[i*stride_in_bytes] - buffer[i & STBTT__OVER_MASK]; - buffer[(i+kernel_width) & STBTT__OVER_MASK] = pixels[i*stride_in_bytes]; - pixels[i*stride_in_bytes] = (unsigned char) (total / kernel_width); - } - break; - } - - for (; i < h; ++i) { - STBTT_assert(pixels[i*stride_in_bytes] == 0); - total -= buffer[i & STBTT__OVER_MASK]; - pixels[i*stride_in_bytes] = (unsigned char) (total / kernel_width); - } - - pixels += 1; - } -} - -static float stbtt__oversample_shift(int oversample) -{ - if (!oversample) - return 0.0f; - - // The prefilter is a box filter of width "oversample", - // which shifts phase by (oversample - 1)/2 pixels in - // oversampled space. We want to shift in the opposite - // direction to counter this. - return (float)-(oversample - 1) / (2.0f * (float)oversample); -} - -// rects array must be big enough to accommodate all characters in the given ranges -STBTT_DEF int stbtt_PackFontRangesGatherRects(stbtt_pack_context *spc, stbtt_fontinfo *info, stbtt_pack_range *ranges, int num_ranges, stbrp_rect *rects) -{ - int i,j,k; - - k=0; - for (i=0; i < num_ranges; ++i) { - float fh = ranges[i].font_size; - float scale = fh > 0 ? stbtt_ScaleForPixelHeight(info, fh) : stbtt_ScaleForMappingEmToPixels(info, -fh); - ranges[i].h_oversample = (unsigned char) spc->h_oversample; - ranges[i].v_oversample = (unsigned char) spc->v_oversample; - for (j=0; j < ranges[i].num_chars; ++j) { - int x0,y0,x1,y1; - int codepoint = ranges[i].array_of_unicode_codepoints == NULL ? ranges[i].first_unicode_codepoint_in_range + j : ranges[i].array_of_unicode_codepoints[j]; - int glyph = stbtt_FindGlyphIndex(info, codepoint); - stbtt_GetGlyphBitmapBoxSubpixel(info,glyph, - scale * spc->h_oversample, - scale * spc->v_oversample, - 0,0, - &x0,&y0,&x1,&y1); - rects[k].w = (stbrp_coord) (x1-x0 + spc->padding + spc->h_oversample-1); - rects[k].h = (stbrp_coord) (y1-y0 + spc->padding + spc->v_oversample-1); - ++k; - } - } - - return k; -} - -// rects array must be big enough to accommodate all characters in the given ranges -STBTT_DEF int stbtt_PackFontRangesRenderIntoRects(stbtt_pack_context *spc, stbtt_fontinfo *info, stbtt_pack_range *ranges, int num_ranges, stbrp_rect *rects) -{ - int i,j,k, return_value = 1; - - // save current values - int old_h_over = spc->h_oversample; - int old_v_over = spc->v_oversample; - - k = 0; - for (i=0; i < num_ranges; ++i) { - float fh = ranges[i].font_size; - float scale = fh > 0 ? stbtt_ScaleForPixelHeight(info, fh) : stbtt_ScaleForMappingEmToPixels(info, -fh); - float recip_h,recip_v,sub_x,sub_y; - spc->h_oversample = ranges[i].h_oversample; - spc->v_oversample = ranges[i].v_oversample; - recip_h = 1.0f / spc->h_oversample; - recip_v = 1.0f / spc->v_oversample; - sub_x = stbtt__oversample_shift(spc->h_oversample); - sub_y = stbtt__oversample_shift(spc->v_oversample); - for (j=0; j < ranges[i].num_chars; ++j) { - stbrp_rect *r = &rects[k]; - if (r->was_packed) { - stbtt_packedchar *bc = &ranges[i].chardata_for_range[j]; - int advance, lsb, x0,y0,x1,y1; - int codepoint = ranges[i].array_of_unicode_codepoints == NULL ? ranges[i].first_unicode_codepoint_in_range + j : ranges[i].array_of_unicode_codepoints[j]; - int glyph = stbtt_FindGlyphIndex(info, codepoint); - stbrp_coord pad = (stbrp_coord) spc->padding; - - // pad on left and top - r->x += pad; - r->y += pad; - r->w -= pad; - r->h -= pad; - stbtt_GetGlyphHMetrics(info, glyph, &advance, &lsb); - stbtt_GetGlyphBitmapBox(info, glyph, - scale * spc->h_oversample, - scale * spc->v_oversample, - &x0,&y0,&x1,&y1); - stbtt_MakeGlyphBitmapSubpixel(info, - spc->pixels + r->x + r->y*spc->stride_in_bytes, - r->w - spc->h_oversample+1, - r->h - spc->v_oversample+1, - spc->stride_in_bytes, - scale * spc->h_oversample, - scale * spc->v_oversample, - 0,0, - glyph); - - if (spc->h_oversample > 1) - stbtt__h_prefilter(spc->pixels + r->x + r->y*spc->stride_in_bytes, - r->w, r->h, spc->stride_in_bytes, - spc->h_oversample); - - if (spc->v_oversample > 1) - stbtt__v_prefilter(spc->pixels + r->x + r->y*spc->stride_in_bytes, - r->w, r->h, spc->stride_in_bytes, - spc->v_oversample); - - bc->x0 = (stbtt_int16) r->x; - bc->y0 = (stbtt_int16) r->y; - bc->x1 = (stbtt_int16) (r->x + r->w); - bc->y1 = (stbtt_int16) (r->y + r->h); - bc->xadvance = scale * advance; - bc->xoff = (float) x0 * recip_h + sub_x; - bc->yoff = (float) y0 * recip_v + sub_y; - bc->xoff2 = (x0 + r->w) * recip_h + sub_x; - bc->yoff2 = (y0 + r->h) * recip_v + sub_y; - } else { - return_value = 0; // if any fail, report failure - } - - ++k; - } - } - - // restore original values - spc->h_oversample = old_h_over; - spc->v_oversample = old_v_over; - - return return_value; -} - -STBTT_DEF void stbtt_PackFontRangesPackRects(stbtt_pack_context *spc, stbrp_rect *rects, int num_rects) -{ - stbrp_pack_rects((stbrp_context *) spc->pack_info, rects, num_rects); -} - -STBTT_DEF int stbtt_PackFontRanges(stbtt_pack_context *spc, unsigned char *fontdata, int font_index, stbtt_pack_range *ranges, int num_ranges) -{ - stbtt_fontinfo info; - int i,j,n, return_value = 1; - //stbrp_context *context = (stbrp_context *) spc->pack_info; - stbrp_rect *rects; - - // flag all characters as NOT packed - for (i=0; i < num_ranges; ++i) - for (j=0; j < ranges[i].num_chars; ++j) - ranges[i].chardata_for_range[j].x0 = - ranges[i].chardata_for_range[j].y0 = - ranges[i].chardata_for_range[j].x1 = - ranges[i].chardata_for_range[j].y1 = 0; - - n = 0; - for (i=0; i < num_ranges; ++i) - n += ranges[i].num_chars; - - rects = (stbrp_rect *) STBTT_malloc(sizeof(*rects) * n, spc->user_allocator_context); - if (rects == NULL) - return 0; - - info.userdata = spc->user_allocator_context; - stbtt_InitFont(&info, fontdata, stbtt_GetFontOffsetForIndex(fontdata,font_index)); - - n = stbtt_PackFontRangesGatherRects(spc, &info, ranges, num_ranges, rects); - - stbtt_PackFontRangesPackRects(spc, rects, n); - - return_value = stbtt_PackFontRangesRenderIntoRects(spc, &info, ranges, num_ranges, rects); - - STBTT_free(rects, spc->user_allocator_context); - return return_value; -} - -STBTT_DEF int stbtt_PackFontRange(stbtt_pack_context *spc, unsigned char *fontdata, int font_index, float font_size, - int first_unicode_codepoint_in_range, int num_chars_in_range, stbtt_packedchar *chardata_for_range) -{ - stbtt_pack_range range; - range.first_unicode_codepoint_in_range = first_unicode_codepoint_in_range; - range.array_of_unicode_codepoints = NULL; - range.num_chars = num_chars_in_range; - range.chardata_for_range = chardata_for_range; - range.font_size = font_size; - return stbtt_PackFontRanges(spc, fontdata, font_index, &range, 1); -} - -STBTT_DEF void stbtt_GetPackedQuad(stbtt_packedchar *chardata, int pw, int ph, int char_index, float *xpos, float *ypos, stbtt_aligned_quad *q, int align_to_integer) -{ - float ipw = 1.0f / pw, iph = 1.0f / ph; - stbtt_packedchar *b = chardata + char_index; - - if (align_to_integer) { - float x = (float) STBTT_ifloor((*xpos + b->xoff) + 0.5f); - float y = (float) STBTT_ifloor((*ypos + b->yoff) + 0.5f); - q->x0 = x; - q->y0 = y; - q->x1 = x + b->xoff2 - b->xoff; - q->y1 = y + b->yoff2 - b->yoff; - } else { - q->x0 = *xpos + b->xoff; - q->y0 = *ypos + b->yoff; - q->x1 = *xpos + b->xoff2; - q->y1 = *ypos + b->yoff2; - } - - q->s0 = b->x0 * ipw; - q->t0 = b->y0 * iph; - q->s1 = b->x1 * ipw; - q->t1 = b->y1 * iph; - - *xpos += b->xadvance; -} - - -////////////////////////////////////////////////////////////////////////////// -// -// font name matching -- recommended not to use this -// - -// check if a utf8 string contains a prefix which is the utf16 string; if so return length of matching utf8 string -static stbtt_int32 stbtt__CompareUTF8toUTF16_bigendian_prefix(const stbtt_uint8 *s1, stbtt_int32 len1, const stbtt_uint8 *s2, stbtt_int32 len2) -{ - stbtt_int32 i=0; - - // convert utf16 to utf8 and compare the results while converting - while (len2) { - stbtt_uint16 ch = s2[0]*256 + s2[1]; - if (ch < 0x80) { - if (i >= len1) return -1; - if (s1[i++] != ch) return -1; - } else if (ch < 0x800) { - if (i+1 >= len1) return -1; - if (s1[i++] != 0xc0 + (ch >> 6)) return -1; - if (s1[i++] != 0x80 + (ch & 0x3f)) return -1; - } else if (ch >= 0xd800 && ch < 0xdc00) { - stbtt_uint32 c; - stbtt_uint16 ch2 = s2[2]*256 + s2[3]; - if (i+3 >= len1) return -1; - c = ((ch - 0xd800) << 10) + (ch2 - 0xdc00) + 0x10000; - if (s1[i++] != 0xf0 + (c >> 18)) return -1; - if (s1[i++] != 0x80 + ((c >> 12) & 0x3f)) return -1; - if (s1[i++] != 0x80 + ((c >> 6) & 0x3f)) return -1; - if (s1[i++] != 0x80 + ((c ) & 0x3f)) return -1; - s2 += 2; // plus another 2 below - len2 -= 2; - } else if (ch >= 0xdc00 && ch < 0xe000) { - return -1; - } else { - if (i+2 >= len1) return -1; - if (s1[i++] != 0xe0 + (ch >> 12)) return -1; - if (s1[i++] != 0x80 + ((ch >> 6) & 0x3f)) return -1; - if (s1[i++] != 0x80 + ((ch ) & 0x3f)) return -1; - } - s2 += 2; - len2 -= 2; - } - return i; -} - -STBTT_DEF int stbtt_CompareUTF8toUTF16_bigendian(const char *s1, int len1, const char *s2, int len2) -{ - return len1 == stbtt__CompareUTF8toUTF16_bigendian_prefix((const stbtt_uint8*) s1, len1, (const stbtt_uint8*) s2, len2); -} - -// returns results in whatever encoding you request... but note that 2-byte encodings -// will be BIG-ENDIAN... use stbtt_CompareUTF8toUTF16_bigendian() to compare -STBTT_DEF const char *stbtt_GetFontNameString(const stbtt_fontinfo *font, int *length, int platformID, int encodingID, int languageID, int nameID) -{ - stbtt_int32 i,count,stringOffset; - stbtt_uint8 *fc = font->data; - stbtt_uint32 offset = font->fontstart; - stbtt_uint32 nm = stbtt__find_table(fc, offset, "name"); - if (!nm) return NULL; - - count = ttUSHORT(fc+nm+2); - stringOffset = nm + ttUSHORT(fc+nm+4); - for (i=0; i < count; ++i) { - stbtt_uint32 loc = nm + 6 + 12 * i; - if (platformID == ttUSHORT(fc+loc+0) && encodingID == ttUSHORT(fc+loc+2) - && languageID == ttUSHORT(fc+loc+4) && nameID == ttUSHORT(fc+loc+6)) { - *length = ttUSHORT(fc+loc+8); - return (const char *) (fc+stringOffset+ttUSHORT(fc+loc+10)); - } - } - return NULL; -} - -static int stbtt__matchpair(stbtt_uint8 *fc, stbtt_uint32 nm, stbtt_uint8 *name, stbtt_int32 nlen, stbtt_int32 target_id, stbtt_int32 next_id) -{ - stbtt_int32 i; - stbtt_int32 count = ttUSHORT(fc+nm+2); - stbtt_int32 stringOffset = nm + ttUSHORT(fc+nm+4); - - for (i=0; i < count; ++i) { - stbtt_uint32 loc = nm + 6 + 12 * i; - stbtt_int32 id = ttUSHORT(fc+loc+6); - if (id == target_id) { - // find the encoding - stbtt_int32 platform = ttUSHORT(fc+loc+0), encoding = ttUSHORT(fc+loc+2), language = ttUSHORT(fc+loc+4); - - // is this a Unicode encoding? - if (platform == 0 || (platform == 3 && encoding == 1) || (platform == 3 && encoding == 10)) { - stbtt_int32 slen = ttUSHORT(fc+loc+8); - stbtt_int32 off = ttUSHORT(fc+loc+10); - - // check if there's a prefix match - stbtt_int32 matchlen = stbtt__CompareUTF8toUTF16_bigendian_prefix(name, nlen, fc+stringOffset+off,slen); - if (matchlen >= 0) { - // check for target_id+1 immediately following, with same encoding & language - if (i+1 < count && ttUSHORT(fc+loc+12+6) == next_id && ttUSHORT(fc+loc+12) == platform && ttUSHORT(fc+loc+12+2) == encoding && ttUSHORT(fc+loc+12+4) == language) { - slen = ttUSHORT(fc+loc+12+8); - off = ttUSHORT(fc+loc+12+10); - if (slen == 0) { - if (matchlen == nlen) - return 1; - } else if (matchlen < nlen && name[matchlen] == ' ') { - ++matchlen; - if (stbtt_CompareUTF8toUTF16_bigendian((char*) (name+matchlen), nlen-matchlen, (char*)(fc+stringOffset+off),slen)) - return 1; - } - } else { - // if nothing immediately following - if (matchlen == nlen) - return 1; - } - } - } - - // @TODO handle other encodings - } - } - return 0; -} - -static int stbtt__matches(stbtt_uint8 *fc, stbtt_uint32 offset, stbtt_uint8 *name, stbtt_int32 flags) -{ - stbtt_int32 nlen = (stbtt_int32) STBTT_strlen((char *) name); - stbtt_uint32 nm,hd; - if (!stbtt__isfont(fc+offset)) return 0; - - // check italics/bold/underline flags in macStyle... - if (flags) { - hd = stbtt__find_table(fc, offset, "head"); - if ((ttUSHORT(fc+hd+44) & 7) != (flags & 7)) return 0; - } - - nm = stbtt__find_table(fc, offset, "name"); - if (!nm) return 0; - - if (flags) { - // if we checked the macStyle flags, then just check the family and ignore the subfamily - if (stbtt__matchpair(fc, nm, name, nlen, 16, -1)) return 1; - if (stbtt__matchpair(fc, nm, name, nlen, 1, -1)) return 1; - if (stbtt__matchpair(fc, nm, name, nlen, 3, -1)) return 1; - } else { - if (stbtt__matchpair(fc, nm, name, nlen, 16, 17)) return 1; - if (stbtt__matchpair(fc, nm, name, nlen, 1, 2)) return 1; - if (stbtt__matchpair(fc, nm, name, nlen, 3, -1)) return 1; - } - - return 0; -} - -STBTT_DEF int stbtt_FindMatchingFont(const unsigned char *font_collection, const char *name_utf8, stbtt_int32 flags) -{ - stbtt_int32 i; - for (i=0;;++i) { - stbtt_int32 off = stbtt_GetFontOffsetForIndex(font_collection, i); - if (off < 0) return off; - if (stbtt__matches((stbtt_uint8 *) font_collection, off, (stbtt_uint8*) name_utf8, flags)) - return off; - } -} - -#endif // STB_TRUETYPE_IMPLEMENTATION - - -// FULL VERSION HISTORY -// -// 1.11 (2016-04-02) fix unused-variable warning -// 1.10 (2016-04-02) allow user-defined fabs() replacement -// fix memory leak if fontsize=0.0 -// fix warning from duplicate typedef -// 1.09 (2016-01-16) warning fix; avoid crash on outofmem; use alloc userdata for PackFontRanges -// 1.08 (2015-09-13) document stbtt_Rasterize(); fixes for vertical & horizontal edges -// 1.07 (2015-08-01) allow PackFontRanges to accept arrays of sparse codepoints; -// allow PackFontRanges to pack and render in separate phases; -// fix stbtt_GetFontOFfsetForIndex (never worked for non-0 input?); -// fixed an assert() bug in the new rasterizer -// replace assert() with STBTT_assert() in new rasterizer -// 1.06 (2015-07-14) performance improvements (~35% faster on x86 and x64 on test machine) -// also more precise AA rasterizer, except if shapes overlap -// remove need for STBTT_sort -// 1.05 (2015-04-15) fix misplaced definitions for STBTT_STATIC -// 1.04 (2015-04-15) typo in example -// 1.03 (2015-04-12) STBTT_STATIC, fix memory leak in new packing, various fixes -// 1.02 (2014-12-10) fix various warnings & compile issues w/ stb_rect_pack, C++ -// 1.01 (2014-12-08) fix subpixel position when oversampling to exactly match -// non-oversampled; STBTT_POINT_SIZE for packed case only -// 1.00 (2014-12-06) add new PackBegin etc. API, w/ support for oversampling -// 0.99 (2014-09-18) fix multiple bugs with subpixel rendering (ryg) -// 0.9 (2014-08-07) support certain mac/iOS fonts without an MS platformID -// 0.8b (2014-07-07) fix a warning -// 0.8 (2014-05-25) fix a few more warnings -// 0.7 (2013-09-25) bugfix: subpixel glyph bug fixed in 0.5 had come back -// 0.6c (2012-07-24) improve documentation -// 0.6b (2012-07-20) fix a few more warnings -// 0.6 (2012-07-17) fix warnings; added stbtt_ScaleForMappingEmToPixels, -// stbtt_GetFontBoundingBox, stbtt_IsGlyphEmpty -// 0.5 (2011-12-09) bugfixes: -// subpixel glyph renderer computed wrong bounding box -// first vertex of shape can be off-curve (FreeSans) -// 0.4b (2011-12-03) fixed an error in the font baking example -// 0.4 (2011-12-01) kerning, subpixel rendering (tor) -// bugfixes for: -// codepoint-to-glyph conversion using table fmt=12 -// codepoint-to-glyph conversion using table fmt=4 -// stbtt_GetBakedQuad with non-square texture (Zer) -// updated Hello World! sample to use kerning and subpixel -// fixed some warnings -// 0.3 (2009-06-24) cmap fmt=12, compound shapes (MM) -// userdata, malloc-from-userdata, non-zero fill (stb) -// 0.2 (2009-03-11) Fix unsigned/signed char warnings -// 0.1 (2009-03-09) First public release -// diff --git a/impeller/third_party/stb/stb/stb_vorbis.c b/impeller/third_party/stb/stb/stb_vorbis.c deleted file mode 100644 index 19459eb1a354e..0000000000000 --- a/impeller/third_party/stb/stb/stb_vorbis.c +++ /dev/null @@ -1,5397 +0,0 @@ -// Ogg Vorbis audio decoder - v1.09 - public domain -// http://nothings.org/stb_vorbis/ -// -// Original version written by Sean Barrett in 2007. -// -// Originally sponsored by RAD Game Tools. Seeking sponsored -// by Phillip Bennefall, Marc Andersen, Aaron Baker, Elias Software, -// Aras Pranckevicius, and Sean Barrett. -// -// LICENSE -// -// This software is dual-licensed to the public domain and under the following -// license: you are granted a perpetual, irrevocable license to copy, modify, -// publish, and distribute this file as you see fit. -// -// No warranty for any purpose is expressed or implied by the author (nor -// by RAD Game Tools). Report bugs and send enhancements to the author. -// -// Limitations: -// -// - floor 0 not supported (used in old ogg vorbis files pre-2004) -// - lossless sample-truncation at beginning ignored -// - cannot concatenate multiple vorbis streams -// - sample positions are 32-bit, limiting seekable 192Khz -// files to around 6 hours (Ogg supports 64-bit) -// -// Feature contributors: -// Dougall Johnson (sample-exact seeking) -// -// Bugfix/warning contributors: -// Terje Mathisen Niklas Frykholm Andy Hill -// Casey Muratori John Bolton Gargaj -// Laurent Gomila Marc LeBlanc Ronny Chevalier -// Bernhard Wodo Evan Balster alxprd@github -// Tom Beaumont Ingo Leitgeb Nicolas Guillemot -// Phillip Bennefall Rohit Thiago Goulart -// manxorist@github saga musix -// -// Partial history: -// 1.09 - 2016/04/04 - back out 'truncation of last frame' fix from previous version -// 1.08 - 2016/04/02 - warnings; setup memory leaks; truncation of last frame -// 1.07 - 2015/01/16 - fixes for crashes on invalid files; warning fixes; const -// 1.06 - 2015/08/31 - full, correct support for seeking API (Dougall Johnson) -// some crash fixes when out of memory or with corrupt files -// fix some inappropriately signed shifts -// 1.05 - 2015/04/19 - don't define __forceinline if it's redundant -// 1.04 - 2014/08/27 - fix missing const-correct case in API -// 1.03 - 2014/08/07 - warning fixes -// 1.02 - 2014/07/09 - declare qsort comparison as explicitly _cdecl in Windows -// 1.01 - 2014/06/18 - fix stb_vorbis_get_samples_float (interleaved was correct) -// 1.0 - 2014/05/26 - fix memory leaks; fix warnings; fix bugs in >2-channel; -// (API change) report sample rate for decode-full-file funcs -// -// See end of file for full version history. - - -////////////////////////////////////////////////////////////////////////////// -// -// HEADER BEGINS HERE -// - -#ifndef STB_VORBIS_INCLUDE_STB_VORBIS_H -#define STB_VORBIS_INCLUDE_STB_VORBIS_H - -#if defined(STB_VORBIS_NO_CRT) && !defined(STB_VORBIS_NO_STDIO) -#define STB_VORBIS_NO_STDIO 1 -#endif - -#ifndef STB_VORBIS_NO_STDIO -#include -#endif - -#ifdef __cplusplus -extern "C" { -#endif - -/////////// THREAD SAFETY - -// Individual stb_vorbis* handles are not thread-safe; you cannot decode from -// them from multiple threads at the same time. However, you can have multiple -// stb_vorbis* handles and decode from them independently in multiple thrads. - - -/////////// MEMORY ALLOCATION - -// normally stb_vorbis uses malloc() to allocate memory at startup, -// and alloca() to allocate temporary memory during a frame on the -// stack. (Memory consumption will depend on the amount of setup -// data in the file and how you set the compile flags for speed -// vs. size. In my test files the maximal-size usage is ~150KB.) -// -// You can modify the wrapper functions in the source (setup_malloc, -// setup_temp_malloc, temp_malloc) to change this behavior, or you -// can use a simpler allocation model: you pass in a buffer from -// which stb_vorbis will allocate _all_ its memory (including the -// temp memory). "open" may fail with a VORBIS_outofmem if you -// do not pass in enough data; there is no way to determine how -// much you do need except to succeed (at which point you can -// query get_info to find the exact amount required. yes I know -// this is lame). -// -// If you pass in a non-NULL buffer of the type below, allocation -// will occur from it as described above. Otherwise just pass NULL -// to use malloc()/alloca() - -typedef struct -{ - char *alloc_buffer; - int alloc_buffer_length_in_bytes; -} stb_vorbis_alloc; - - -/////////// FUNCTIONS USEABLE WITH ALL INPUT MODES - -typedef struct stb_vorbis stb_vorbis; - -typedef struct -{ - unsigned int sample_rate; - int channels; - - unsigned int setup_memory_required; - unsigned int setup_temp_memory_required; - unsigned int temp_memory_required; - - int max_frame_size; -} stb_vorbis_info; - -// get general information about the file -extern stb_vorbis_info stb_vorbis_get_info(stb_vorbis *f); - -// get the last error detected (clears it, too) -extern int stb_vorbis_get_error(stb_vorbis *f); - -// close an ogg vorbis file and free all memory in use -extern void stb_vorbis_close(stb_vorbis *f); - -// this function returns the offset (in samples) from the beginning of the -// file that will be returned by the next decode, if it is known, or -1 -// otherwise. after a flush_pushdata() call, this may take a while before -// it becomes valid again. -// NOT WORKING YET after a seek with PULLDATA API -extern int stb_vorbis_get_sample_offset(stb_vorbis *f); - -// returns the current seek point within the file, or offset from the beginning -// of the memory buffer. In pushdata mode it returns 0. -extern unsigned int stb_vorbis_get_file_offset(stb_vorbis *f); - -/////////// PUSHDATA API - -#ifndef STB_VORBIS_NO_PUSHDATA_API - -// this API allows you to get blocks of data from any source and hand -// them to stb_vorbis. you have to buffer them; stb_vorbis will tell -// you how much it used, and you have to give it the rest next time; -// and stb_vorbis may not have enough data to work with and you will -// need to give it the same data again PLUS more. Note that the Vorbis -// specification does not bound the size of an individual frame. - -extern stb_vorbis *stb_vorbis_open_pushdata( - const unsigned char * datablock, int datablock_length_in_bytes, - int *datablock_memory_consumed_in_bytes, - int *error, - const stb_vorbis_alloc *alloc_buffer); -// create a vorbis decoder by passing in the initial data block containing -// the ogg&vorbis headers (you don't need to do parse them, just provide -// the first N bytes of the file--you're told if it's not enough, see below) -// on success, returns an stb_vorbis *, does not set error, returns the amount of -// data parsed/consumed on this call in *datablock_memory_consumed_in_bytes; -// on failure, returns NULL on error and sets *error, does not change *datablock_memory_consumed -// if returns NULL and *error is VORBIS_need_more_data, then the input block was -// incomplete and you need to pass in a larger block from the start of the file - -extern int stb_vorbis_decode_frame_pushdata( - stb_vorbis *f, - const unsigned char *datablock, int datablock_length_in_bytes, - int *channels, // place to write number of float * buffers - float ***output, // place to write float ** array of float * buffers - int *samples // place to write number of output samples - ); -// decode a frame of audio sample data if possible from the passed-in data block -// -// return value: number of bytes we used from datablock -// -// possible cases: -// 0 bytes used, 0 samples output (need more data) -// N bytes used, 0 samples output (resynching the stream, keep going) -// N bytes used, M samples output (one frame of data) -// note that after opening a file, you will ALWAYS get one N-bytes,0-sample -// frame, because Vorbis always "discards" the first frame. -// -// Note that on resynch, stb_vorbis will rarely consume all of the buffer, -// instead only datablock_length_in_bytes-3 or less. This is because it wants -// to avoid missing parts of a page header if they cross a datablock boundary, -// without writing state-machiney code to record a partial detection. -// -// The number of channels returned are stored in *channels (which can be -// NULL--it is always the same as the number of channels reported by -// get_info). *output will contain an array of float* buffers, one per -// channel. In other words, (*output)[0][0] contains the first sample from -// the first channel, and (*output)[1][0] contains the first sample from -// the second channel. - -extern void stb_vorbis_flush_pushdata(stb_vorbis *f); -// inform stb_vorbis that your next datablock will not be contiguous with -// previous ones (e.g. you've seeked in the data); future attempts to decode -// frames will cause stb_vorbis to resynchronize (as noted above), and -// once it sees a valid Ogg page (typically 4-8KB, as large as 64KB), it -// will begin decoding the _next_ frame. -// -// if you want to seek using pushdata, you need to seek in your file, then -// call stb_vorbis_flush_pushdata(), then start calling decoding, then once -// decoding is returning you data, call stb_vorbis_get_sample_offset, and -// if you don't like the result, seek your file again and repeat. -#endif - - -////////// PULLING INPUT API - -#ifndef STB_VORBIS_NO_PULLDATA_API -// This API assumes stb_vorbis is allowed to pull data from a source-- -// either a block of memory containing the _entire_ vorbis stream, or a -// FILE * that you or it create, or possibly some other reading mechanism -// if you go modify the source to replace the FILE * case with some kind -// of callback to your code. (But if you don't support seeking, you may -// just want to go ahead and use pushdata.) - -#if !defined(STB_VORBIS_NO_STDIO) && !defined(STB_VORBIS_NO_INTEGER_CONVERSION) -extern int stb_vorbis_decode_filename(const char *filename, int *channels, int *sample_rate, short **output); -#endif -#if !defined(STB_VORBIS_NO_INTEGER_CONVERSION) -extern int stb_vorbis_decode_memory(const unsigned char *mem, int len, int *channels, int *sample_rate, short **output); -#endif -// decode an entire file and output the data interleaved into a malloc()ed -// buffer stored in *output. The return value is the number of samples -// decoded, or -1 if the file could not be opened or was not an ogg vorbis file. -// When you're done with it, just free() the pointer returned in *output. - -extern stb_vorbis * stb_vorbis_open_memory(const unsigned char *data, int len, - int *error, const stb_vorbis_alloc *alloc_buffer); -// create an ogg vorbis decoder from an ogg vorbis stream in memory (note -// this must be the entire stream!). on failure, returns NULL and sets *error - -#ifndef STB_VORBIS_NO_STDIO -extern stb_vorbis * stb_vorbis_open_filename(const char *filename, - int *error, const stb_vorbis_alloc *alloc_buffer); -// create an ogg vorbis decoder from a filename via fopen(). on failure, -// returns NULL and sets *error (possibly to VORBIS_file_open_failure). - -extern stb_vorbis * stb_vorbis_open_file(FILE *f, int close_handle_on_close, - int *error, const stb_vorbis_alloc *alloc_buffer); -// create an ogg vorbis decoder from an open FILE *, looking for a stream at -// the _current_ seek point (ftell). on failure, returns NULL and sets *error. -// note that stb_vorbis must "own" this stream; if you seek it in between -// calls to stb_vorbis, it will become confused. Morever, if you attempt to -// perform stb_vorbis_seek_*() operations on this file, it will assume it -// owns the _entire_ rest of the file after the start point. Use the next -// function, stb_vorbis_open_file_section(), to limit it. - -extern stb_vorbis * stb_vorbis_open_file_section(FILE *f, int close_handle_on_close, - int *error, const stb_vorbis_alloc *alloc_buffer, unsigned int len); -// create an ogg vorbis decoder from an open FILE *, looking for a stream at -// the _current_ seek point (ftell); the stream will be of length 'len' bytes. -// on failure, returns NULL and sets *error. note that stb_vorbis must "own" -// this stream; if you seek it in between calls to stb_vorbis, it will become -// confused. -#endif - -extern int stb_vorbis_seek_frame(stb_vorbis *f, unsigned int sample_number); -extern int stb_vorbis_seek(stb_vorbis *f, unsigned int sample_number); -// these functions seek in the Vorbis file to (approximately) 'sample_number'. -// after calling seek_frame(), the next call to get_frame_*() will include -// the specified sample. after calling stb_vorbis_seek(), the next call to -// stb_vorbis_get_samples_* will start with the specified sample. If you -// do not need to seek to EXACTLY the target sample when using get_samples_*, -// you can also use seek_frame(). - -extern void stb_vorbis_seek_start(stb_vorbis *f); -// this function is equivalent to stb_vorbis_seek(f,0) - -extern unsigned int stb_vorbis_stream_length_in_samples(stb_vorbis *f); -extern float stb_vorbis_stream_length_in_seconds(stb_vorbis *f); -// these functions return the total length of the vorbis stream - -extern int stb_vorbis_get_frame_float(stb_vorbis *f, int *channels, float ***output); -// decode the next frame and return the number of samples. the number of -// channels returned are stored in *channels (which can be NULL--it is always -// the same as the number of channels reported by get_info). *output will -// contain an array of float* buffers, one per channel. These outputs will -// be overwritten on the next call to stb_vorbis_get_frame_*. -// -// You generally should not intermix calls to stb_vorbis_get_frame_*() -// and stb_vorbis_get_samples_*(), since the latter calls the former. - -#ifndef STB_VORBIS_NO_INTEGER_CONVERSION -extern int stb_vorbis_get_frame_short_interleaved(stb_vorbis *f, int num_c, short *buffer, int num_shorts); -extern int stb_vorbis_get_frame_short (stb_vorbis *f, int num_c, short **buffer, int num_samples); -#endif -// decode the next frame and return the number of *samples* per channel. -// Note that for interleaved data, you pass in the number of shorts (the -// size of your array), but the return value is the number of samples per -// channel, not the total number of samples. -// -// The data is coerced to the number of channels you request according to the -// channel coercion rules (see below). You must pass in the size of your -// buffer(s) so that stb_vorbis will not overwrite the end of the buffer. -// The maximum buffer size needed can be gotten from get_info(); however, -// the Vorbis I specification implies an absolute maximum of 4096 samples -// per channel. - -// Channel coercion rules: -// Let M be the number of channels requested, and N the number of channels present, -// and Cn be the nth channel; let stereo L be the sum of all L and center channels, -// and stereo R be the sum of all R and center channels (channel assignment from the -// vorbis spec). -// M N output -// 1 k sum(Ck) for all k -// 2 * stereo L, stereo R -// k l k > l, the first l channels, then 0s -// k l k <= l, the first k channels -// Note that this is not _good_ surround etc. mixing at all! It's just so -// you get something useful. - -extern int stb_vorbis_get_samples_float_interleaved(stb_vorbis *f, int channels, float *buffer, int num_floats); -extern int stb_vorbis_get_samples_float(stb_vorbis *f, int channels, float **buffer, int num_samples); -// gets num_samples samples, not necessarily on a frame boundary--this requires -// buffering so you have to supply the buffers. DOES NOT APPLY THE COERCION RULES. -// Returns the number of samples stored per channel; it may be less than requested -// at the end of the file. If there are no more samples in the file, returns 0. - -#ifndef STB_VORBIS_NO_INTEGER_CONVERSION -extern int stb_vorbis_get_samples_short_interleaved(stb_vorbis *f, int channels, short *buffer, int num_shorts); -extern int stb_vorbis_get_samples_short(stb_vorbis *f, int channels, short **buffer, int num_samples); -#endif -// gets num_samples samples, not necessarily on a frame boundary--this requires -// buffering so you have to supply the buffers. Applies the coercion rules above -// to produce 'channels' channels. Returns the number of samples stored per channel; -// it may be less than requested at the end of the file. If there are no more -// samples in the file, returns 0. - -#endif - -//////// ERROR CODES - -enum STBVorbisError -{ - VORBIS__no_error, - - VORBIS_need_more_data=1, // not a real error - - VORBIS_invalid_api_mixing, // can't mix API modes - VORBIS_outofmem, // not enough memory - VORBIS_feature_not_supported, // uses floor 0 - VORBIS_too_many_channels, // STB_VORBIS_MAX_CHANNELS is too small - VORBIS_file_open_failure, // fopen() failed - VORBIS_seek_without_length, // can't seek in unknown-length file - - VORBIS_unexpected_eof=10, // file is truncated? - VORBIS_seek_invalid, // seek past EOF - - // decoding errors (corrupt/invalid stream) -- you probably - // don't care about the exact details of these - - // vorbis errors: - VORBIS_invalid_setup=20, - VORBIS_invalid_stream, - - // ogg errors: - VORBIS_missing_capture_pattern=30, - VORBIS_invalid_stream_structure_version, - VORBIS_continued_packet_flag_invalid, - VORBIS_incorrect_stream_serial_number, - VORBIS_invalid_first_page, - VORBIS_bad_packet_type, - VORBIS_cant_find_last_page, - VORBIS_seek_failed -}; - - -#ifdef __cplusplus -} -#endif - -#endif // STB_VORBIS_INCLUDE_STB_VORBIS_H -// -// HEADER ENDS HERE -// -////////////////////////////////////////////////////////////////////////////// - -#ifndef STB_VORBIS_HEADER_ONLY - -// global configuration settings (e.g. set these in the project/makefile), -// or just set them in this file at the top (although ideally the first few -// should be visible when the header file is compiled too, although it's not -// crucial) - -// STB_VORBIS_NO_PUSHDATA_API -// does not compile the code for the various stb_vorbis_*_pushdata() -// functions -// #define STB_VORBIS_NO_PUSHDATA_API - -// STB_VORBIS_NO_PULLDATA_API -// does not compile the code for the non-pushdata APIs -// #define STB_VORBIS_NO_PULLDATA_API - -// STB_VORBIS_NO_STDIO -// does not compile the code for the APIs that use FILE *s internally -// or externally (implied by STB_VORBIS_NO_PULLDATA_API) -// #define STB_VORBIS_NO_STDIO - -// STB_VORBIS_NO_INTEGER_CONVERSION -// does not compile the code for converting audio sample data from -// float to integer (implied by STB_VORBIS_NO_PULLDATA_API) -// #define STB_VORBIS_NO_INTEGER_CONVERSION - -// STB_VORBIS_NO_FAST_SCALED_FLOAT -// does not use a fast float-to-int trick to accelerate float-to-int on -// most platforms which requires endianness be defined correctly. -//#define STB_VORBIS_NO_FAST_SCALED_FLOAT - - -// STB_VORBIS_MAX_CHANNELS [number] -// globally define this to the maximum number of channels you need. -// The spec does not put a restriction on channels except that -// the count is stored in a byte, so 255 is the hard limit. -// Reducing this saves about 16 bytes per value, so using 16 saves -// (255-16)*16 or around 4KB. Plus anything other memory usage -// I forgot to account for. Can probably go as low as 8 (7.1 audio), -// 6 (5.1 audio), or 2 (stereo only). -#ifndef STB_VORBIS_MAX_CHANNELS -#define STB_VORBIS_MAX_CHANNELS 16 // enough for anyone? -#endif - -// STB_VORBIS_PUSHDATA_CRC_COUNT [number] -// after a flush_pushdata(), stb_vorbis begins scanning for the -// next valid page, without backtracking. when it finds something -// that looks like a page, it streams through it and verifies its -// CRC32. Should that validation fail, it keeps scanning. But it's -// possible that _while_ streaming through to check the CRC32 of -// one candidate page, it sees another candidate page. This #define -// determines how many "overlapping" candidate pages it can search -// at once. Note that "real" pages are typically ~4KB to ~8KB, whereas -// garbage pages could be as big as 64KB, but probably average ~16KB. -// So don't hose ourselves by scanning an apparent 64KB page and -// missing a ton of real ones in the interim; so minimum of 2 -#ifndef STB_VORBIS_PUSHDATA_CRC_COUNT -#define STB_VORBIS_PUSHDATA_CRC_COUNT 4 -#endif - -// STB_VORBIS_FAST_HUFFMAN_LENGTH [number] -// sets the log size of the huffman-acceleration table. Maximum -// supported value is 24. with larger numbers, more decodings are O(1), -// but the table size is larger so worse cache missing, so you'll have -// to probe (and try multiple ogg vorbis files) to find the sweet spot. -#ifndef STB_VORBIS_FAST_HUFFMAN_LENGTH -#define STB_VORBIS_FAST_HUFFMAN_LENGTH 10 -#endif - -// STB_VORBIS_FAST_BINARY_LENGTH [number] -// sets the log size of the binary-search acceleration table. this -// is used in similar fashion to the fast-huffman size to set initial -// parameters for the binary search - -// STB_VORBIS_FAST_HUFFMAN_INT -// The fast huffman tables are much more efficient if they can be -// stored as 16-bit results instead of 32-bit results. This restricts -// the codebooks to having only 65535 possible outcomes, though. -// (At least, accelerated by the huffman table.) -#ifndef STB_VORBIS_FAST_HUFFMAN_INT -#define STB_VORBIS_FAST_HUFFMAN_SHORT -#endif - -// STB_VORBIS_NO_HUFFMAN_BINARY_SEARCH -// If the 'fast huffman' search doesn't succeed, then stb_vorbis falls -// back on binary searching for the correct one. This requires storing -// extra tables with the huffman codes in sorted order. Defining this -// symbol trades off space for speed by forcing a linear search in the -// non-fast case, except for "sparse" codebooks. -// #define STB_VORBIS_NO_HUFFMAN_BINARY_SEARCH - -// STB_VORBIS_DIVIDES_IN_RESIDUE -// stb_vorbis precomputes the result of the scalar residue decoding -// that would otherwise require a divide per chunk. you can trade off -// space for time by defining this symbol. -// #define STB_VORBIS_DIVIDES_IN_RESIDUE - -// STB_VORBIS_DIVIDES_IN_CODEBOOK -// vorbis VQ codebooks can be encoded two ways: with every case explicitly -// stored, or with all elements being chosen from a small range of values, -// and all values possible in all elements. By default, stb_vorbis expands -// this latter kind out to look like the former kind for ease of decoding, -// because otherwise an integer divide-per-vector-element is required to -// unpack the index. If you define STB_VORBIS_DIVIDES_IN_CODEBOOK, you can -// trade off storage for speed. -//#define STB_VORBIS_DIVIDES_IN_CODEBOOK - -#ifdef STB_VORBIS_CODEBOOK_SHORTS -#error "STB_VORBIS_CODEBOOK_SHORTS is no longer supported as it produced incorrect results for some input formats" -#endif - -// STB_VORBIS_DIVIDE_TABLE -// this replaces small integer divides in the floor decode loop with -// table lookups. made less than 1% difference, so disabled by default. - -// STB_VORBIS_NO_INLINE_DECODE -// disables the inlining of the scalar codebook fast-huffman decode. -// might save a little codespace; useful for debugging -// #define STB_VORBIS_NO_INLINE_DECODE - -// STB_VORBIS_NO_DEFER_FLOOR -// Normally we only decode the floor without synthesizing the actual -// full curve. We can instead synthesize the curve immediately. This -// requires more memory and is very likely slower, so I don't think -// you'd ever want to do it except for debugging. -// #define STB_VORBIS_NO_DEFER_FLOOR - - - - -////////////////////////////////////////////////////////////////////////////// - -#ifdef STB_VORBIS_NO_PULLDATA_API - #define STB_VORBIS_NO_INTEGER_CONVERSION - #define STB_VORBIS_NO_STDIO -#endif - -#if defined(STB_VORBIS_NO_CRT) && !defined(STB_VORBIS_NO_STDIO) - #define STB_VORBIS_NO_STDIO 1 -#endif - -#ifndef STB_VORBIS_NO_INTEGER_CONVERSION -#ifndef STB_VORBIS_NO_FAST_SCALED_FLOAT - - // only need endianness for fast-float-to-int, which we don't - // use for pushdata - - #ifndef STB_VORBIS_BIG_ENDIAN - #define STB_VORBIS_ENDIAN 0 - #else - #define STB_VORBIS_ENDIAN 1 - #endif - -#endif -#endif - - -#ifndef STB_VORBIS_NO_STDIO -#include -#endif - -#ifndef STB_VORBIS_NO_CRT -#include -#include -#include -#include -#if !(defined(__APPLE__) || defined(MACOSX) || defined(macintosh) || defined(Macintosh)) -#include -#if defined(__linux__) || defined(__linux) || defined(__EMSCRIPTEN__) -#include -#endif -#endif -#else // STB_VORBIS_NO_CRT -#define NULL 0 -#define malloc(s) 0 -#define free(s) ((void) 0) -#define realloc(s) 0 -#endif // STB_VORBIS_NO_CRT - -#include - -#ifdef __MINGW32__ - // eff you mingw: - // "fixed": - // http://sourceforge.net/p/mingw-w64/mailman/message/32882927/ - // "no that broke the build, reverted, who cares about C": - // http://sourceforge.net/p/mingw-w64/mailman/message/32890381/ - #ifdef __forceinline - #undef __forceinline - #endif - #define __forceinline -#elif !defined(_MSC_VER) - #if __GNUC__ - #define __forceinline inline - #else - #define __forceinline - #endif -#endif - -#if STB_VORBIS_MAX_CHANNELS > 256 -#error "Value of STB_VORBIS_MAX_CHANNELS outside of allowed range" -#endif - -#if STB_VORBIS_FAST_HUFFMAN_LENGTH > 24 -#error "Value of STB_VORBIS_FAST_HUFFMAN_LENGTH outside of allowed range" -#endif - - -#if 0 -#include -#define CHECK(f) _CrtIsValidHeapPointer(f->channel_buffers[1]) -#else -#define CHECK(f) ((void) 0) -#endif - -#define MAX_BLOCKSIZE_LOG 13 // from specification -#define MAX_BLOCKSIZE (1 << MAX_BLOCKSIZE_LOG) - - -typedef unsigned char uint8; -typedef signed char int8; -typedef unsigned short uint16; -typedef signed short int16; -typedef unsigned int uint32; -typedef signed int int32; - -#ifndef TRUE -#define TRUE 1 -#define FALSE 0 -#endif - -typedef float codetype; - -// @NOTE -// -// Some arrays below are tagged "//varies", which means it's actually -// a variable-sized piece of data, but rather than malloc I assume it's -// small enough it's better to just allocate it all together with the -// main thing -// -// Most of the variables are specified with the smallest size I could pack -// them into. It might give better performance to make them all full-sized -// integers. It should be safe to freely rearrange the structures or change -// the sizes larger--nothing relies on silently truncating etc., nor the -// order of variables. - -#define FAST_HUFFMAN_TABLE_SIZE (1 << STB_VORBIS_FAST_HUFFMAN_LENGTH) -#define FAST_HUFFMAN_TABLE_MASK (FAST_HUFFMAN_TABLE_SIZE - 1) - -typedef struct -{ - int dimensions, entries; - uint8 *codeword_lengths; - float minimum_value; - float delta_value; - uint8 value_bits; - uint8 lookup_type; - uint8 sequence_p; - uint8 sparse; - uint32 lookup_values; - codetype *multiplicands; - uint32 *codewords; - #ifdef STB_VORBIS_FAST_HUFFMAN_SHORT - int16 fast_huffman[FAST_HUFFMAN_TABLE_SIZE]; - #else - int32 fast_huffman[FAST_HUFFMAN_TABLE_SIZE]; - #endif - uint32 *sorted_codewords; - int *sorted_values; - int sorted_entries; -} Codebook; - -typedef struct -{ - uint8 order; - uint16 rate; - uint16 bark_map_size; - uint8 amplitude_bits; - uint8 amplitude_offset; - uint8 number_of_books; - uint8 book_list[16]; // varies -} Floor0; - -typedef struct -{ - uint8 partitions; - uint8 partition_class_list[32]; // varies - uint8 class_dimensions[16]; // varies - uint8 class_subclasses[16]; // varies - uint8 class_masterbooks[16]; // varies - int16 subclass_books[16][8]; // varies - uint16 Xlist[31*8+2]; // varies - uint8 sorted_order[31*8+2]; - uint8 neighbors[31*8+2][2]; - uint8 floor1_multiplier; - uint8 rangebits; - int values; -} Floor1; - -typedef union -{ - Floor0 floor0; - Floor1 floor1; -} Floor; - -typedef struct -{ - uint32 begin, end; - uint32 part_size; - uint8 classifications; - uint8 classbook; - uint8 **classdata; - int16 (*residue_books)[8]; -} Residue; - -typedef struct -{ - uint8 magnitude; - uint8 angle; - uint8 mux; -} MappingChannel; - -typedef struct -{ - uint16 coupling_steps; - MappingChannel *chan; - uint8 submaps; - uint8 submap_floor[15]; // varies - uint8 submap_residue[15]; // varies -} Mapping; - -typedef struct -{ - uint8 blockflag; - uint8 mapping; - uint16 windowtype; - uint16 transformtype; -} Mode; - -typedef struct -{ - uint32 goal_crc; // expected crc if match - int bytes_left; // bytes left in packet - uint32 crc_so_far; // running crc - int bytes_done; // bytes processed in _current_ chunk - uint32 sample_loc; // granule pos encoded in page -} CRCscan; - -typedef struct -{ - uint32 page_start, page_end; - uint32 last_decoded_sample; -} ProbedPage; - -struct stb_vorbis -{ - // user-accessible info - unsigned int sample_rate; - int channels; - - unsigned int setup_memory_required; - unsigned int temp_memory_required; - unsigned int setup_temp_memory_required; - - // input config -#ifndef STB_VORBIS_NO_STDIO - FILE *f; - uint32 f_start; - int close_on_free; -#endif - - uint8 *stream; - uint8 *stream_start; - uint8 *stream_end; - - uint32 stream_len; - - uint8 push_mode; - - uint32 first_audio_page_offset; - - ProbedPage p_first, p_last; - - // memory management - stb_vorbis_alloc alloc; - int setup_offset; - int temp_offset; - - // run-time results - int eof; - enum STBVorbisError error; - - // user-useful data - - // header info - int blocksize[2]; - int blocksize_0, blocksize_1; - int codebook_count; - Codebook *codebooks; - int floor_count; - uint16 floor_types[64]; // varies - Floor *floor_config; - int residue_count; - uint16 residue_types[64]; // varies - Residue *residue_config; - int mapping_count; - Mapping *mapping; - int mode_count; - Mode mode_config[64]; // varies - - uint32 total_samples; - - // decode buffer - float *channel_buffers[STB_VORBIS_MAX_CHANNELS]; - float *outputs [STB_VORBIS_MAX_CHANNELS]; - - float *previous_window[STB_VORBIS_MAX_CHANNELS]; - int previous_length; - - #ifndef STB_VORBIS_NO_DEFER_FLOOR - int16 *finalY[STB_VORBIS_MAX_CHANNELS]; - #else - float *floor_buffers[STB_VORBIS_MAX_CHANNELS]; - #endif - - uint32 current_loc; // sample location of next frame to decode - int current_loc_valid; - - // per-blocksize precomputed data - - // twiddle factors - float *A[2],*B[2],*C[2]; - float *window[2]; - uint16 *bit_reverse[2]; - - // current page/packet/segment streaming info - uint32 serial; // stream serial number for verification - int last_page; - int segment_count; - uint8 segments[255]; - uint8 page_flag; - uint8 bytes_in_seg; - uint8 first_decode; - int next_seg; - int last_seg; // flag that we're on the last segment - int last_seg_which; // what was the segment number of the last seg? - uint32 acc; - int valid_bits; - int packet_bytes; - int end_seg_with_known_loc; - uint32 known_loc_for_packet; - int discard_samples_deferred; - uint32 samples_output; - - // push mode scanning - int page_crc_tests; // only in push_mode: number of tests active; -1 if not searching -#ifndef STB_VORBIS_NO_PUSHDATA_API - CRCscan scan[STB_VORBIS_PUSHDATA_CRC_COUNT]; -#endif - - // sample-access - int channel_buffer_start; - int channel_buffer_end; -}; - -#if defined(STB_VORBIS_NO_PUSHDATA_API) - #define IS_PUSH_MODE(f) FALSE -#elif defined(STB_VORBIS_NO_PULLDATA_API) - #define IS_PUSH_MODE(f) TRUE -#else - #define IS_PUSH_MODE(f) ((f)->push_mode) -#endif - -typedef struct stb_vorbis vorb; - -static int error(vorb *f, enum STBVorbisError e) -{ - f->error = e; - if (!f->eof && e != VORBIS_need_more_data) { - f->error=e; // breakpoint for debugging - } - return 0; -} - - -// these functions are used for allocating temporary memory -// while decoding. if you can afford the stack space, use -// alloca(); otherwise, provide a temp buffer and it will -// allocate out of those. - -#define array_size_required(count,size) (count*(sizeof(void *)+(size))) - -#define temp_alloc(f,size) (f->alloc.alloc_buffer ? setup_temp_malloc(f,size) : alloca(size)) -#ifdef dealloca -#define temp_free(f,p) (f->alloc.alloc_buffer ? 0 : dealloca(size)) -#else -#define temp_free(f,p) 0 -#endif -#define temp_alloc_save(f) ((f)->temp_offset) -#define temp_alloc_restore(f,p) ((f)->temp_offset = (p)) - -#define temp_block_array(f,count,size) make_block_array(temp_alloc(f,array_size_required(count,size)), count, size) - -// given a sufficiently large block of memory, make an array of pointers to subblocks of it -static void *make_block_array(void *mem, int count, int size) -{ - int i; - void ** p = (void **) mem; - char *q = (char *) (p + count); - for (i=0; i < count; ++i) { - p[i] = q; - q += size; - } - return p; -} - -static void *setup_malloc(vorb *f, int sz) -{ - sz = (sz+3) & ~3; - f->setup_memory_required += sz; - if (f->alloc.alloc_buffer) { - void *p = (char *) f->alloc.alloc_buffer + f->setup_offset; - if (f->setup_offset + sz > f->temp_offset) return NULL; - f->setup_offset += sz; - return p; - } - return sz ? malloc(sz) : NULL; -} - -static void setup_free(vorb *f, void *p) -{ - if (f->alloc.alloc_buffer) return; // do nothing; setup mem is a stack - free(p); -} - -static void *setup_temp_malloc(vorb *f, int sz) -{ - sz = (sz+3) & ~3; - if (f->alloc.alloc_buffer) { - if (f->temp_offset - sz < f->setup_offset) return NULL; - f->temp_offset -= sz; - return (char *) f->alloc.alloc_buffer + f->temp_offset; - } - return malloc(sz); -} - -static void setup_temp_free(vorb *f, void *p, int sz) -{ - if (f->alloc.alloc_buffer) { - f->temp_offset += (sz+3)&~3; - return; - } - free(p); -} - -#define CRC32_POLY 0x04c11db7 // from spec - -static uint32 crc_table[256]; -static void crc32_init(void) -{ - int i,j; - uint32 s; - for(i=0; i < 256; i++) { - for (s=(uint32) i << 24, j=0; j < 8; ++j) - s = (s << 1) ^ (s >= (1U<<31) ? CRC32_POLY : 0); - crc_table[i] = s; - } -} - -static __forceinline uint32 crc32_update(uint32 crc, uint8 byte) -{ - return (crc << 8) ^ crc_table[byte ^ (crc >> 24)]; -} - - -// used in setup, and for huffman that doesn't go fast path -static unsigned int bit_reverse(unsigned int n) -{ - n = ((n & 0xAAAAAAAA) >> 1) | ((n & 0x55555555) << 1); - n = ((n & 0xCCCCCCCC) >> 2) | ((n & 0x33333333) << 2); - n = ((n & 0xF0F0F0F0) >> 4) | ((n & 0x0F0F0F0F) << 4); - n = ((n & 0xFF00FF00) >> 8) | ((n & 0x00FF00FF) << 8); - return (n >> 16) | (n << 16); -} - -static float square(float x) -{ - return x*x; -} - -// this is a weird definition of log2() for which log2(1) = 1, log2(2) = 2, log2(4) = 3 -// as required by the specification. fast(?) implementation from stb.h -// @OPTIMIZE: called multiple times per-packet with "constants"; move to setup -static int ilog(int32 n) -{ - static signed char log2_4[16] = { 0,1,2,2,3,3,3,3,4,4,4,4,4,4,4,4 }; - - // 2 compares if n < 16, 3 compares otherwise (4 if signed or n > 1<<29) - if (n < (1 << 14)) - if (n < (1 << 4)) return 0 + log2_4[n ]; - else if (n < (1 << 9)) return 5 + log2_4[n >> 5]; - else return 10 + log2_4[n >> 10]; - else if (n < (1 << 24)) - if (n < (1 << 19)) return 15 + log2_4[n >> 15]; - else return 20 + log2_4[n >> 20]; - else if (n < (1 << 29)) return 25 + log2_4[n >> 25]; - else if (n < (1 << 31)) return 30 + log2_4[n >> 30]; - else return 0; // signed n returns 0 -} - -#ifndef M_PI - #define M_PI 3.14159265358979323846264f // from CRC -#endif - -// code length assigned to a value with no huffman encoding -#define NO_CODE 255 - -/////////////////////// LEAF SETUP FUNCTIONS ////////////////////////// -// -// these functions are only called at setup, and only a few times -// per file - -static float float32_unpack(uint32 x) -{ - // from the specification - uint32 mantissa = x & 0x1fffff; - uint32 sign = x & 0x80000000; - uint32 exp = (x & 0x7fe00000) >> 21; - double res = sign ? -(double)mantissa : (double)mantissa; - return (float) ldexp((float)res, exp-788); -} - - -// zlib & jpeg huffman tables assume that the output symbols -// can either be arbitrarily arranged, or have monotonically -// increasing frequencies--they rely on the lengths being sorted; -// this makes for a very simple generation algorithm. -// vorbis allows a huffman table with non-sorted lengths. This -// requires a more sophisticated construction, since symbols in -// order do not map to huffman codes "in order". -static void add_entry(Codebook *c, uint32 huff_code, int symbol, int count, int len, uint32 *values) -{ - if (!c->sparse) { - c->codewords [symbol] = huff_code; - } else { - c->codewords [count] = huff_code; - c->codeword_lengths[count] = len; - values [count] = symbol; - } -} - -static int compute_codewords(Codebook *c, uint8 *len, int n, uint32 *values) -{ - int i,k,m=0; - uint32 available[32]; - - memset(available, 0, sizeof(available)); - // find the first entry - for (k=0; k < n; ++k) if (len[k] < NO_CODE) break; - if (k == n) { assert(c->sorted_entries == 0); return TRUE; } - // add to the list - add_entry(c, 0, k, m++, len[k], values); - // add all available leaves - for (i=1; i <= len[k]; ++i) - available[i] = 1U << (32-i); - // note that the above code treats the first case specially, - // but it's really the same as the following code, so they - // could probably be combined (except the initial code is 0, - // and I use 0 in available[] to mean 'empty') - for (i=k+1; i < n; ++i) { - uint32 res; - int z = len[i], y; - if (z == NO_CODE) continue; - // find lowest available leaf (should always be earliest, - // which is what the specification calls for) - // note that this property, and the fact we can never have - // more than one free leaf at a given level, isn't totally - // trivial to prove, but it seems true and the assert never - // fires, so! - while (z > 0 && !available[z]) --z; - if (z == 0) { return FALSE; } - res = available[z]; - assert(z >= 0 && z < 32); - available[z] = 0; - add_entry(c, bit_reverse(res), i, m++, len[i], values); - // propogate availability up the tree - if (z != len[i]) { - assert(len[i] >= 0 && len[i] < 32); - for (y=len[i]; y > z; --y) { - assert(available[y] == 0); - available[y] = res + (1 << (32-y)); - } - } - } - return TRUE; -} - -// accelerated huffman table allows fast O(1) match of all symbols -// of length <= STB_VORBIS_FAST_HUFFMAN_LENGTH -static void compute_accelerated_huffman(Codebook *c) -{ - int i, len; - for (i=0; i < FAST_HUFFMAN_TABLE_SIZE; ++i) - c->fast_huffman[i] = -1; - - len = c->sparse ? c->sorted_entries : c->entries; - #ifdef STB_VORBIS_FAST_HUFFMAN_SHORT - if (len > 32767) len = 32767; // largest possible value we can encode! - #endif - for (i=0; i < len; ++i) { - if (c->codeword_lengths[i] <= STB_VORBIS_FAST_HUFFMAN_LENGTH) { - uint32 z = c->sparse ? bit_reverse(c->sorted_codewords[i]) : c->codewords[i]; - // set table entries for all bit combinations in the higher bits - while (z < FAST_HUFFMAN_TABLE_SIZE) { - c->fast_huffman[z] = i; - z += 1 << c->codeword_lengths[i]; - } - } - } -} - -#ifdef _MSC_VER -#define STBV_CDECL __cdecl -#else -#define STBV_CDECL -#endif - -static int STBV_CDECL uint32_compare(const void *p, const void *q) -{ - uint32 x = * (uint32 *) p; - uint32 y = * (uint32 *) q; - return x < y ? -1 : x > y; -} - -static int include_in_sort(Codebook *c, uint8 len) -{ - if (c->sparse) { assert(len != NO_CODE); return TRUE; } - if (len == NO_CODE) return FALSE; - if (len > STB_VORBIS_FAST_HUFFMAN_LENGTH) return TRUE; - return FALSE; -} - -// if the fast table above doesn't work, we want to binary -// search them... need to reverse the bits -static void compute_sorted_huffman(Codebook *c, uint8 *lengths, uint32 *values) -{ - int i, len; - // build a list of all the entries - // OPTIMIZATION: don't include the short ones, since they'll be caught by FAST_HUFFMAN. - // this is kind of a frivolous optimization--I don't see any performance improvement, - // but it's like 4 extra lines of code, so. - if (!c->sparse) { - int k = 0; - for (i=0; i < c->entries; ++i) - if (include_in_sort(c, lengths[i])) - c->sorted_codewords[k++] = bit_reverse(c->codewords[i]); - assert(k == c->sorted_entries); - } else { - for (i=0; i < c->sorted_entries; ++i) - c->sorted_codewords[i] = bit_reverse(c->codewords[i]); - } - - qsort(c->sorted_codewords, c->sorted_entries, sizeof(c->sorted_codewords[0]), uint32_compare); - c->sorted_codewords[c->sorted_entries] = 0xffffffff; - - len = c->sparse ? c->sorted_entries : c->entries; - // now we need to indicate how they correspond; we could either - // #1: sort a different data structure that says who they correspond to - // #2: for each sorted entry, search the original list to find who corresponds - // #3: for each original entry, find the sorted entry - // #1 requires extra storage, #2 is slow, #3 can use binary search! - for (i=0; i < len; ++i) { - int huff_len = c->sparse ? lengths[values[i]] : lengths[i]; - if (include_in_sort(c,huff_len)) { - uint32 code = bit_reverse(c->codewords[i]); - int x=0, n=c->sorted_entries; - while (n > 1) { - // invariant: sc[x] <= code < sc[x+n] - int m = x + (n >> 1); - if (c->sorted_codewords[m] <= code) { - x = m; - n -= (n>>1); - } else { - n >>= 1; - } - } - assert(c->sorted_codewords[x] == code); - if (c->sparse) { - c->sorted_values[x] = values[i]; - c->codeword_lengths[x] = huff_len; - } else { - c->sorted_values[x] = i; - } - } - } -} - -// only run while parsing the header (3 times) -static int vorbis_validate(uint8 *data) -{ - static uint8 vorbis[6] = { 'v', 'o', 'r', 'b', 'i', 's' }; - return memcmp(data, vorbis, 6) == 0; -} - -// called from setup only, once per code book -// (formula implied by specification) -static int lookup1_values(int entries, int dim) -{ - int r = (int) floor(exp((float) log((float) entries) / dim)); - if ((int) floor(pow((float) r+1, dim)) <= entries) // (int) cast for MinGW warning; - ++r; // floor() to avoid _ftol() when non-CRT - assert(pow((float) r+1, dim) > entries); - assert((int) floor(pow((float) r, dim)) <= entries); // (int),floor() as above - return r; -} - -// called twice per file -static void compute_twiddle_factors(int n, float *A, float *B, float *C) -{ - int n4 = n >> 2, n8 = n >> 3; - int k,k2; - - for (k=k2=0; k < n4; ++k,k2+=2) { - A[k2 ] = (float) cos(4*k*M_PI/n); - A[k2+1] = (float) -sin(4*k*M_PI/n); - B[k2 ] = (float) cos((k2+1)*M_PI/n/2) * 0.5f; - B[k2+1] = (float) sin((k2+1)*M_PI/n/2) * 0.5f; - } - for (k=k2=0; k < n8; ++k,k2+=2) { - C[k2 ] = (float) cos(2*(k2+1)*M_PI/n); - C[k2+1] = (float) -sin(2*(k2+1)*M_PI/n); - } -} - -static void compute_window(int n, float *window) -{ - int n2 = n >> 1, i; - for (i=0; i < n2; ++i) - window[i] = (float) sin(0.5 * M_PI * square((float) sin((i - 0 + 0.5) / n2 * 0.5 * M_PI))); -} - -static void compute_bitreverse(int n, uint16 *rev) -{ - int ld = ilog(n) - 1; // ilog is off-by-one from normal definitions - int i, n8 = n >> 3; - for (i=0; i < n8; ++i) - rev[i] = (bit_reverse(i) >> (32-ld+3)) << 2; -} - -static int init_blocksize(vorb *f, int b, int n) -{ - int n2 = n >> 1, n4 = n >> 2, n8 = n >> 3; - f->A[b] = (float *) setup_malloc(f, sizeof(float) * n2); - f->B[b] = (float *) setup_malloc(f, sizeof(float) * n2); - f->C[b] = (float *) setup_malloc(f, sizeof(float) * n4); - if (!f->A[b] || !f->B[b] || !f->C[b]) return error(f, VORBIS_outofmem); - compute_twiddle_factors(n, f->A[b], f->B[b], f->C[b]); - f->window[b] = (float *) setup_malloc(f, sizeof(float) * n2); - if (!f->window[b]) return error(f, VORBIS_outofmem); - compute_window(n, f->window[b]); - f->bit_reverse[b] = (uint16 *) setup_malloc(f, sizeof(uint16) * n8); - if (!f->bit_reverse[b]) return error(f, VORBIS_outofmem); - compute_bitreverse(n, f->bit_reverse[b]); - return TRUE; -} - -static void neighbors(uint16 *x, int n, int *plow, int *phigh) -{ - int low = -1; - int high = 65536; - int i; - for (i=0; i < n; ++i) { - if (x[i] > low && x[i] < x[n]) { *plow = i; low = x[i]; } - if (x[i] < high && x[i] > x[n]) { *phigh = i; high = x[i]; } - } -} - -// this has been repurposed so y is now the original index instead of y -typedef struct -{ - uint16 x,y; -} Point; - -static int STBV_CDECL point_compare(const void *p, const void *q) -{ - Point *a = (Point *) p; - Point *b = (Point *) q; - return a->x < b->x ? -1 : a->x > b->x; -} - -// -/////////////////////// END LEAF SETUP FUNCTIONS ////////////////////////// - - -#if defined(STB_VORBIS_NO_STDIO) - #define USE_MEMORY(z) TRUE -#else - #define USE_MEMORY(z) ((z)->stream) -#endif - -static uint8 get8(vorb *z) -{ - if (USE_MEMORY(z)) { - if (z->stream >= z->stream_end) { z->eof = TRUE; return 0; } - return *z->stream++; - } - - #ifndef STB_VORBIS_NO_STDIO - { - int c = fgetc(z->f); - if (c == EOF) { z->eof = TRUE; return 0; } - return c; - } - #endif -} - -static uint32 get32(vorb *f) -{ - uint32 x; - x = get8(f); - x += get8(f) << 8; - x += get8(f) << 16; - x += (uint32) get8(f) << 24; - return x; -} - -static int getn(vorb *z, uint8 *data, int n) -{ - if (USE_MEMORY(z)) { - if (z->stream+n > z->stream_end) { z->eof = 1; return 0; } - memcpy(data, z->stream, n); - z->stream += n; - return 1; - } - - #ifndef STB_VORBIS_NO_STDIO - if (fread(data, n, 1, z->f) == 1) - return 1; - else { - z->eof = 1; - return 0; - } - #endif -} - -static void skip(vorb *z, int n) -{ - if (USE_MEMORY(z)) { - z->stream += n; - if (z->stream >= z->stream_end) z->eof = 1; - return; - } - #ifndef STB_VORBIS_NO_STDIO - { - long x = ftell(z->f); - fseek(z->f, x+n, SEEK_SET); - } - #endif -} - -static int set_file_offset(stb_vorbis *f, unsigned int loc) -{ - #ifndef STB_VORBIS_NO_PUSHDATA_API - if (f->push_mode) return 0; - #endif - f->eof = 0; - if (USE_MEMORY(f)) { - if (f->stream_start + loc >= f->stream_end || f->stream_start + loc < f->stream_start) { - f->stream = f->stream_end; - f->eof = 1; - return 0; - } else { - f->stream = f->stream_start + loc; - return 1; - } - } - #ifndef STB_VORBIS_NO_STDIO - if (loc + f->f_start < loc || loc >= 0x80000000) { - loc = 0x7fffffff; - f->eof = 1; - } else { - loc += f->f_start; - } - if (!fseek(f->f, loc, SEEK_SET)) - return 1; - f->eof = 1; - fseek(f->f, f->f_start, SEEK_END); - return 0; - #endif -} - - -static uint8 ogg_page_header[4] = { 0x4f, 0x67, 0x67, 0x53 }; - -static int capture_pattern(vorb *f) -{ - if (0x4f != get8(f)) return FALSE; - if (0x67 != get8(f)) return FALSE; - if (0x67 != get8(f)) return FALSE; - if (0x53 != get8(f)) return FALSE; - return TRUE; -} - -#define PAGEFLAG_continued_packet 1 -#define PAGEFLAG_first_page 2 -#define PAGEFLAG_last_page 4 - -static int start_page_no_capturepattern(vorb *f) -{ - uint32 loc0,loc1,n; - // stream structure version - if (0 != get8(f)) return error(f, VORBIS_invalid_stream_structure_version); - // header flag - f->page_flag = get8(f); - // absolute granule position - loc0 = get32(f); - loc1 = get32(f); - // @TODO: validate loc0,loc1 as valid positions? - // stream serial number -- vorbis doesn't interleave, so discard - get32(f); - //if (f->serial != get32(f)) return error(f, VORBIS_incorrect_stream_serial_number); - // page sequence number - n = get32(f); - f->last_page = n; - // CRC32 - get32(f); - // page_segments - f->segment_count = get8(f); - if (!getn(f, f->segments, f->segment_count)) - return error(f, VORBIS_unexpected_eof); - // assume we _don't_ know any the sample position of any segments - f->end_seg_with_known_loc = -2; - if (loc0 != ~0U || loc1 != ~0U) { - int i; - // determine which packet is the last one that will complete - for (i=f->segment_count-1; i >= 0; --i) - if (f->segments[i] < 255) - break; - // 'i' is now the index of the _last_ segment of a packet that ends - if (i >= 0) { - f->end_seg_with_known_loc = i; - f->known_loc_for_packet = loc0; - } - } - if (f->first_decode) { - int i,len; - ProbedPage p; - len = 0; - for (i=0; i < f->segment_count; ++i) - len += f->segments[i]; - len += 27 + f->segment_count; - p.page_start = f->first_audio_page_offset; - p.page_end = p.page_start + len; - p.last_decoded_sample = loc0; - f->p_first = p; - } - f->next_seg = 0; - return TRUE; -} - -static int start_page(vorb *f) -{ - if (!capture_pattern(f)) return error(f, VORBIS_missing_capture_pattern); - return start_page_no_capturepattern(f); -} - -static int start_packet(vorb *f) -{ - while (f->next_seg == -1) { - if (!start_page(f)) return FALSE; - if (f->page_flag & PAGEFLAG_continued_packet) - return error(f, VORBIS_continued_packet_flag_invalid); - } - f->last_seg = FALSE; - f->valid_bits = 0; - f->packet_bytes = 0; - f->bytes_in_seg = 0; - // f->next_seg is now valid - return TRUE; -} - -static int maybe_start_packet(vorb *f) -{ - if (f->next_seg == -1) { - int x = get8(f); - if (f->eof) return FALSE; // EOF at page boundary is not an error! - if (0x4f != x ) return error(f, VORBIS_missing_capture_pattern); - if (0x67 != get8(f)) return error(f, VORBIS_missing_capture_pattern); - if (0x67 != get8(f)) return error(f, VORBIS_missing_capture_pattern); - if (0x53 != get8(f)) return error(f, VORBIS_missing_capture_pattern); - if (!start_page_no_capturepattern(f)) return FALSE; - if (f->page_flag & PAGEFLAG_continued_packet) { - // set up enough state that we can read this packet if we want, - // e.g. during recovery - f->last_seg = FALSE; - f->bytes_in_seg = 0; - return error(f, VORBIS_continued_packet_flag_invalid); - } - } - return start_packet(f); -} - -static int next_segment(vorb *f) -{ - int len; - if (f->last_seg) return 0; - if (f->next_seg == -1) { - f->last_seg_which = f->segment_count-1; // in case start_page fails - if (!start_page(f)) { f->last_seg = 1; return 0; } - if (!(f->page_flag & PAGEFLAG_continued_packet)) return error(f, VORBIS_continued_packet_flag_invalid); - } - len = f->segments[f->next_seg++]; - if (len < 255) { - f->last_seg = TRUE; - f->last_seg_which = f->next_seg-1; - } - if (f->next_seg >= f->segment_count) - f->next_seg = -1; - assert(f->bytes_in_seg == 0); - f->bytes_in_seg = len; - return len; -} - -#define EOP (-1) -#define INVALID_BITS (-1) - -static int get8_packet_raw(vorb *f) -{ - if (!f->bytes_in_seg) { // CLANG! - if (f->last_seg) return EOP; - else if (!next_segment(f)) return EOP; - } - assert(f->bytes_in_seg > 0); - --f->bytes_in_seg; - ++f->packet_bytes; - return get8(f); -} - -static int get8_packet(vorb *f) -{ - int x = get8_packet_raw(f); - f->valid_bits = 0; - return x; -} - -static void flush_packet(vorb *f) -{ - while (get8_packet_raw(f) != EOP); -} - -// @OPTIMIZE: this is the secondary bit decoder, so it's probably not as important -// as the huffman decoder? -static uint32 get_bits(vorb *f, int n) -{ - uint32 z; - - if (f->valid_bits < 0) return 0; - if (f->valid_bits < n) { - if (n > 24) { - // the accumulator technique below would not work correctly in this case - z = get_bits(f, 24); - z += get_bits(f, n-24) << 24; - return z; - } - if (f->valid_bits == 0) f->acc = 0; - while (f->valid_bits < n) { - int z = get8_packet_raw(f); - if (z == EOP) { - f->valid_bits = INVALID_BITS; - return 0; - } - f->acc += z << f->valid_bits; - f->valid_bits += 8; - } - } - if (f->valid_bits < 0) return 0; - z = f->acc & ((1 << n)-1); - f->acc >>= n; - f->valid_bits -= n; - return z; -} - -// @OPTIMIZE: primary accumulator for huffman -// expand the buffer to as many bits as possible without reading off end of packet -// it might be nice to allow f->valid_bits and f->acc to be stored in registers, -// e.g. cache them locally and decode locally -static __forceinline void prep_huffman(vorb *f) -{ - if (f->valid_bits <= 24) { - if (f->valid_bits == 0) f->acc = 0; - do { - int z; - if (f->last_seg && !f->bytes_in_seg) return; - z = get8_packet_raw(f); - if (z == EOP) return; - f->acc += (unsigned) z << f->valid_bits; - f->valid_bits += 8; - } while (f->valid_bits <= 24); - } -} - -enum -{ - VORBIS_packet_id = 1, - VORBIS_packet_comment = 3, - VORBIS_packet_setup = 5 -}; - -static int codebook_decode_scalar_raw(vorb *f, Codebook *c) -{ - int i; - prep_huffman(f); - - if (c->codewords == NULL && c->sorted_codewords == NULL) - return -1; - - // cases to use binary search: sorted_codewords && !c->codewords - // sorted_codewords && c->entries > 8 - if (c->entries > 8 ? c->sorted_codewords!=NULL : !c->codewords) { - // binary search - uint32 code = bit_reverse(f->acc); - int x=0, n=c->sorted_entries, len; - - while (n > 1) { - // invariant: sc[x] <= code < sc[x+n] - int m = x + (n >> 1); - if (c->sorted_codewords[m] <= code) { - x = m; - n -= (n>>1); - } else { - n >>= 1; - } - } - // x is now the sorted index - if (!c->sparse) x = c->sorted_values[x]; - // x is now sorted index if sparse, or symbol otherwise - len = c->codeword_lengths[x]; - if (f->valid_bits >= len) { - f->acc >>= len; - f->valid_bits -= len; - return x; - } - - f->valid_bits = 0; - return -1; - } - - // if small, linear search - assert(!c->sparse); - for (i=0; i < c->entries; ++i) { - if (c->codeword_lengths[i] == NO_CODE) continue; - if (c->codewords[i] == (f->acc & ((1 << c->codeword_lengths[i])-1))) { - if (f->valid_bits >= c->codeword_lengths[i]) { - f->acc >>= c->codeword_lengths[i]; - f->valid_bits -= c->codeword_lengths[i]; - return i; - } - f->valid_bits = 0; - return -1; - } - } - - error(f, VORBIS_invalid_stream); - f->valid_bits = 0; - return -1; -} - -#ifndef STB_VORBIS_NO_INLINE_DECODE - -#define DECODE_RAW(var, f,c) \ - if (f->valid_bits < STB_VORBIS_FAST_HUFFMAN_LENGTH) \ - prep_huffman(f); \ - var = f->acc & FAST_HUFFMAN_TABLE_MASK; \ - var = c->fast_huffman[var]; \ - if (var >= 0) { \ - int n = c->codeword_lengths[var]; \ - f->acc >>= n; \ - f->valid_bits -= n; \ - if (f->valid_bits < 0) { f->valid_bits = 0; var = -1; } \ - } else { \ - var = codebook_decode_scalar_raw(f,c); \ - } - -#else - -static int codebook_decode_scalar(vorb *f, Codebook *c) -{ - int i; - if (f->valid_bits < STB_VORBIS_FAST_HUFFMAN_LENGTH) - prep_huffman(f); - // fast huffman table lookup - i = f->acc & FAST_HUFFMAN_TABLE_MASK; - i = c->fast_huffman[i]; - if (i >= 0) { - f->acc >>= c->codeword_lengths[i]; - f->valid_bits -= c->codeword_lengths[i]; - if (f->valid_bits < 0) { f->valid_bits = 0; return -1; } - return i; - } - return codebook_decode_scalar_raw(f,c); -} - -#define DECODE_RAW(var,f,c) var = codebook_decode_scalar(f,c); - -#endif - -#define DECODE(var,f,c) \ - DECODE_RAW(var,f,c) \ - if (c->sparse) var = c->sorted_values[var]; - -#ifndef STB_VORBIS_DIVIDES_IN_CODEBOOK - #define DECODE_VQ(var,f,c) DECODE_RAW(var,f,c) -#else - #define DECODE_VQ(var,f,c) DECODE(var,f,c) -#endif - - - - - - -// CODEBOOK_ELEMENT_FAST is an optimization for the CODEBOOK_FLOATS case -// where we avoid one addition -#define CODEBOOK_ELEMENT(c,off) (c->multiplicands[off]) -#define CODEBOOK_ELEMENT_FAST(c,off) (c->multiplicands[off]) -#define CODEBOOK_ELEMENT_BASE(c) (0) - -static int codebook_decode_start(vorb *f, Codebook *c) -{ - int z = -1; - - // type 0 is only legal in a scalar context - if (c->lookup_type == 0) - error(f, VORBIS_invalid_stream); - else { - DECODE_VQ(z,f,c); - if (c->sparse) assert(z < c->sorted_entries); - if (z < 0) { // check for EOP - if (!f->bytes_in_seg) - if (f->last_seg) - return z; - error(f, VORBIS_invalid_stream); - } - } - return z; -} - -static int codebook_decode(vorb *f, Codebook *c, float *output, int len) -{ - int i,z = codebook_decode_start(f,c); - if (z < 0) return FALSE; - if (len > c->dimensions) len = c->dimensions; - -#ifdef STB_VORBIS_DIVIDES_IN_CODEBOOK - if (c->lookup_type == 1) { - float last = CODEBOOK_ELEMENT_BASE(c); - int div = 1; - for (i=0; i < len; ++i) { - int off = (z / div) % c->lookup_values; - float val = CODEBOOK_ELEMENT_FAST(c,off) + last; - output[i] += val; - if (c->sequence_p) last = val + c->minimum_value; - div *= c->lookup_values; - } - return TRUE; - } -#endif - - z *= c->dimensions; - if (c->sequence_p) { - float last = CODEBOOK_ELEMENT_BASE(c); - for (i=0; i < len; ++i) { - float val = CODEBOOK_ELEMENT_FAST(c,z+i) + last; - output[i] += val; - last = val + c->minimum_value; - } - } else { - float last = CODEBOOK_ELEMENT_BASE(c); - for (i=0; i < len; ++i) { - output[i] += CODEBOOK_ELEMENT_FAST(c,z+i) + last; - } - } - - return TRUE; -} - -static int codebook_decode_step(vorb *f, Codebook *c, float *output, int len, int step) -{ - int i,z = codebook_decode_start(f,c); - float last = CODEBOOK_ELEMENT_BASE(c); - if (z < 0) return FALSE; - if (len > c->dimensions) len = c->dimensions; - -#ifdef STB_VORBIS_DIVIDES_IN_CODEBOOK - if (c->lookup_type == 1) { - int div = 1; - for (i=0; i < len; ++i) { - int off = (z / div) % c->lookup_values; - float val = CODEBOOK_ELEMENT_FAST(c,off) + last; - output[i*step] += val; - if (c->sequence_p) last = val; - div *= c->lookup_values; - } - return TRUE; - } -#endif - - z *= c->dimensions; - for (i=0; i < len; ++i) { - float val = CODEBOOK_ELEMENT_FAST(c,z+i) + last; - output[i*step] += val; - if (c->sequence_p) last = val; - } - - return TRUE; -} - -static int codebook_decode_deinterleave_repeat(vorb *f, Codebook *c, float **outputs, int ch, int *c_inter_p, int *p_inter_p, int len, int total_decode) -{ - int c_inter = *c_inter_p; - int p_inter = *p_inter_p; - int i,z, effective = c->dimensions; - - // type 0 is only legal in a scalar context - if (c->lookup_type == 0) return error(f, VORBIS_invalid_stream); - - while (total_decode > 0) { - float last = CODEBOOK_ELEMENT_BASE(c); - DECODE_VQ(z,f,c); - #ifndef STB_VORBIS_DIVIDES_IN_CODEBOOK - assert(!c->sparse || z < c->sorted_entries); - #endif - if (z < 0) { - if (!f->bytes_in_seg) - if (f->last_seg) return FALSE; - return error(f, VORBIS_invalid_stream); - } - - // if this will take us off the end of the buffers, stop short! - // we check by computing the length of the virtual interleaved - // buffer (len*ch), our current offset within it (p_inter*ch)+(c_inter), - // and the length we'll be using (effective) - if (c_inter + p_inter*ch + effective > len * ch) { - effective = len*ch - (p_inter*ch - c_inter); - } - - #ifdef STB_VORBIS_DIVIDES_IN_CODEBOOK - if (c->lookup_type == 1) { - int div = 1; - for (i=0; i < effective; ++i) { - int off = (z / div) % c->lookup_values; - float val = CODEBOOK_ELEMENT_FAST(c,off) + last; - if (outputs[c_inter]) - outputs[c_inter][p_inter] += val; - if (++c_inter == ch) { c_inter = 0; ++p_inter; } - if (c->sequence_p) last = val; - div *= c->lookup_values; - } - } else - #endif - { - z *= c->dimensions; - if (c->sequence_p) { - for (i=0; i < effective; ++i) { - float val = CODEBOOK_ELEMENT_FAST(c,z+i) + last; - if (outputs[c_inter]) - outputs[c_inter][p_inter] += val; - if (++c_inter == ch) { c_inter = 0; ++p_inter; } - last = val; - } - } else { - for (i=0; i < effective; ++i) { - float val = CODEBOOK_ELEMENT_FAST(c,z+i) + last; - if (outputs[c_inter]) - outputs[c_inter][p_inter] += val; - if (++c_inter == ch) { c_inter = 0; ++p_inter; } - } - } - } - - total_decode -= effective; - } - *c_inter_p = c_inter; - *p_inter_p = p_inter; - return TRUE; -} - -static int predict_point(int x, int x0, int x1, int y0, int y1) -{ - int dy = y1 - y0; - int adx = x1 - x0; - // @OPTIMIZE: force int division to round in the right direction... is this necessary on x86? - int err = abs(dy) * (x - x0); - int off = err / adx; - return dy < 0 ? y0 - off : y0 + off; -} - -// the following table is block-copied from the specification -static float inverse_db_table[256] = -{ - 1.0649863e-07f, 1.1341951e-07f, 1.2079015e-07f, 1.2863978e-07f, - 1.3699951e-07f, 1.4590251e-07f, 1.5538408e-07f, 1.6548181e-07f, - 1.7623575e-07f, 1.8768855e-07f, 1.9988561e-07f, 2.1287530e-07f, - 2.2670913e-07f, 2.4144197e-07f, 2.5713223e-07f, 2.7384213e-07f, - 2.9163793e-07f, 3.1059021e-07f, 3.3077411e-07f, 3.5226968e-07f, - 3.7516214e-07f, 3.9954229e-07f, 4.2550680e-07f, 4.5315863e-07f, - 4.8260743e-07f, 5.1396998e-07f, 5.4737065e-07f, 5.8294187e-07f, - 6.2082472e-07f, 6.6116941e-07f, 7.0413592e-07f, 7.4989464e-07f, - 7.9862701e-07f, 8.5052630e-07f, 9.0579828e-07f, 9.6466216e-07f, - 1.0273513e-06f, 1.0941144e-06f, 1.1652161e-06f, 1.2409384e-06f, - 1.3215816e-06f, 1.4074654e-06f, 1.4989305e-06f, 1.5963394e-06f, - 1.7000785e-06f, 1.8105592e-06f, 1.9282195e-06f, 2.0535261e-06f, - 2.1869758e-06f, 2.3290978e-06f, 2.4804557e-06f, 2.6416497e-06f, - 2.8133190e-06f, 2.9961443e-06f, 3.1908506e-06f, 3.3982101e-06f, - 3.6190449e-06f, 3.8542308e-06f, 4.1047004e-06f, 4.3714470e-06f, - 4.6555282e-06f, 4.9580707e-06f, 5.2802740e-06f, 5.6234160e-06f, - 5.9888572e-06f, 6.3780469e-06f, 6.7925283e-06f, 7.2339451e-06f, - 7.7040476e-06f, 8.2047000e-06f, 8.7378876e-06f, 9.3057248e-06f, - 9.9104632e-06f, 1.0554501e-05f, 1.1240392e-05f, 1.1970856e-05f, - 1.2748789e-05f, 1.3577278e-05f, 1.4459606e-05f, 1.5399272e-05f, - 1.6400004e-05f, 1.7465768e-05f, 1.8600792e-05f, 1.9809576e-05f, - 2.1096914e-05f, 2.2467911e-05f, 2.3928002e-05f, 2.5482978e-05f, - 2.7139006e-05f, 2.8902651e-05f, 3.0780908e-05f, 3.2781225e-05f, - 3.4911534e-05f, 3.7180282e-05f, 3.9596466e-05f, 4.2169667e-05f, - 4.4910090e-05f, 4.7828601e-05f, 5.0936773e-05f, 5.4246931e-05f, - 5.7772202e-05f, 6.1526565e-05f, 6.5524908e-05f, 6.9783085e-05f, - 7.4317983e-05f, 7.9147585e-05f, 8.4291040e-05f, 8.9768747e-05f, - 9.5602426e-05f, 0.00010181521f, 0.00010843174f, 0.00011547824f, - 0.00012298267f, 0.00013097477f, 0.00013948625f, 0.00014855085f, - 0.00015820453f, 0.00016848555f, 0.00017943469f, 0.00019109536f, - 0.00020351382f, 0.00021673929f, 0.00023082423f, 0.00024582449f, - 0.00026179955f, 0.00027881276f, 0.00029693158f, 0.00031622787f, - 0.00033677814f, 0.00035866388f, 0.00038197188f, 0.00040679456f, - 0.00043323036f, 0.00046138411f, 0.00049136745f, 0.00052329927f, - 0.00055730621f, 0.00059352311f, 0.00063209358f, 0.00067317058f, - 0.00071691700f, 0.00076350630f, 0.00081312324f, 0.00086596457f, - 0.00092223983f, 0.00098217216f, 0.0010459992f, 0.0011139742f, - 0.0011863665f, 0.0012634633f, 0.0013455702f, 0.0014330129f, - 0.0015261382f, 0.0016253153f, 0.0017309374f, 0.0018434235f, - 0.0019632195f, 0.0020908006f, 0.0022266726f, 0.0023713743f, - 0.0025254795f, 0.0026895994f, 0.0028643847f, 0.0030505286f, - 0.0032487691f, 0.0034598925f, 0.0036847358f, 0.0039241906f, - 0.0041792066f, 0.0044507950f, 0.0047400328f, 0.0050480668f, - 0.0053761186f, 0.0057254891f, 0.0060975636f, 0.0064938176f, - 0.0069158225f, 0.0073652516f, 0.0078438871f, 0.0083536271f, - 0.0088964928f, 0.009474637f, 0.010090352f, 0.010746080f, - 0.011444421f, 0.012188144f, 0.012980198f, 0.013823725f, - 0.014722068f, 0.015678791f, 0.016697687f, 0.017782797f, - 0.018938423f, 0.020169149f, 0.021479854f, 0.022875735f, - 0.024362330f, 0.025945531f, 0.027631618f, 0.029427276f, - 0.031339626f, 0.033376252f, 0.035545228f, 0.037855157f, - 0.040315199f, 0.042935108f, 0.045725273f, 0.048696758f, - 0.051861348f, 0.055231591f, 0.058820850f, 0.062643361f, - 0.066714279f, 0.071049749f, 0.075666962f, 0.080584227f, - 0.085821044f, 0.091398179f, 0.097337747f, 0.10366330f, - 0.11039993f, 0.11757434f, 0.12521498f, 0.13335215f, - 0.14201813f, 0.15124727f, 0.16107617f, 0.17154380f, - 0.18269168f, 0.19456402f, 0.20720788f, 0.22067342f, - 0.23501402f, 0.25028656f, 0.26655159f, 0.28387361f, - 0.30232132f, 0.32196786f, 0.34289114f, 0.36517414f, - 0.38890521f, 0.41417847f, 0.44109412f, 0.46975890f, - 0.50028648f, 0.53279791f, 0.56742212f, 0.60429640f, - 0.64356699f, 0.68538959f, 0.72993007f, 0.77736504f, - 0.82788260f, 0.88168307f, 0.9389798f, 1.0f -}; - - -// @OPTIMIZE: if you want to replace this bresenham line-drawing routine, -// note that you must produce bit-identical output to decode correctly; -// this specific sequence of operations is specified in the spec (it's -// drawing integer-quantized frequency-space lines that the encoder -// expects to be exactly the same) -// ... also, isn't the whole point of Bresenham's algorithm to NOT -// have to divide in the setup? sigh. -#ifndef STB_VORBIS_NO_DEFER_FLOOR -#define LINE_OP(a,b) a *= b -#else -#define LINE_OP(a,b) a = b -#endif - -#ifdef STB_VORBIS_DIVIDE_TABLE -#define DIVTAB_NUMER 32 -#define DIVTAB_DENOM 64 -int8 integer_divide_table[DIVTAB_NUMER][DIVTAB_DENOM]; // 2KB -#endif - -static __forceinline void draw_line(float *output, int x0, int y0, int x1, int y1, int n) -{ - int dy = y1 - y0; - int adx = x1 - x0; - int ady = abs(dy); - int base; - int x=x0,y=y0; - int err = 0; - int sy; - -#ifdef STB_VORBIS_DIVIDE_TABLE - if (adx < DIVTAB_DENOM && ady < DIVTAB_NUMER) { - if (dy < 0) { - base = -integer_divide_table[ady][adx]; - sy = base-1; - } else { - base = integer_divide_table[ady][adx]; - sy = base+1; - } - } else { - base = dy / adx; - if (dy < 0) - sy = base - 1; - else - sy = base+1; - } -#else - base = dy / adx; - if (dy < 0) - sy = base - 1; - else - sy = base+1; -#endif - ady -= abs(base) * adx; - if (x1 > n) x1 = n; - if (x < x1) { - LINE_OP(output[x], inverse_db_table[y]); - for (++x; x < x1; ++x) { - err += ady; - if (err >= adx) { - err -= adx; - y += sy; - } else - y += base; - LINE_OP(output[x], inverse_db_table[y]); - } - } -} - -static int residue_decode(vorb *f, Codebook *book, float *target, int offset, int n, int rtype) -{ - int k; - if (rtype == 0) { - int step = n / book->dimensions; - for (k=0; k < step; ++k) - if (!codebook_decode_step(f, book, target+offset+k, n-offset-k, step)) - return FALSE; - } else { - for (k=0; k < n; ) { - if (!codebook_decode(f, book, target+offset, n-k)) - return FALSE; - k += book->dimensions; - offset += book->dimensions; - } - } - return TRUE; -} - -static void decode_residue(vorb *f, float *residue_buffers[], int ch, int n, int rn, uint8 *do_not_decode) -{ - int i,j,pass; - Residue *r = f->residue_config + rn; - int rtype = f->residue_types[rn]; - int c = r->classbook; - int classwords = f->codebooks[c].dimensions; - int n_read = r->end - r->begin; - int part_read = n_read / r->part_size; - int temp_alloc_point = temp_alloc_save(f); - #ifndef STB_VORBIS_DIVIDES_IN_RESIDUE - uint8 ***part_classdata = (uint8 ***) temp_block_array(f,f->channels, part_read * sizeof(**part_classdata)); - #else - int **classifications = (int **) temp_block_array(f,f->channels, part_read * sizeof(**classifications)); - #endif - - CHECK(f); - - for (i=0; i < ch; ++i) - if (!do_not_decode[i]) - memset(residue_buffers[i], 0, sizeof(float) * n); - - if (rtype == 2 && ch != 1) { - for (j=0; j < ch; ++j) - if (!do_not_decode[j]) - break; - if (j == ch) - goto done; - - for (pass=0; pass < 8; ++pass) { - int pcount = 0, class_set = 0; - if (ch == 2) { - while (pcount < part_read) { - int z = r->begin + pcount*r->part_size; - int c_inter = (z & 1), p_inter = z>>1; - if (pass == 0) { - Codebook *c = f->codebooks+r->classbook; - int q; - DECODE(q,f,c); - if (q == EOP) goto done; - #ifndef STB_VORBIS_DIVIDES_IN_RESIDUE - part_classdata[0][class_set] = r->classdata[q]; - #else - for (i=classwords-1; i >= 0; --i) { - classifications[0][i+pcount] = q % r->classifications; - q /= r->classifications; - } - #endif - } - for (i=0; i < classwords && pcount < part_read; ++i, ++pcount) { - int z = r->begin + pcount*r->part_size; - #ifndef STB_VORBIS_DIVIDES_IN_RESIDUE - int c = part_classdata[0][class_set][i]; - #else - int c = classifications[0][pcount]; - #endif - int b = r->residue_books[c][pass]; - if (b >= 0) { - Codebook *book = f->codebooks + b; - #ifdef STB_VORBIS_DIVIDES_IN_CODEBOOK - if (!codebook_decode_deinterleave_repeat(f, book, residue_buffers, ch, &c_inter, &p_inter, n, r->part_size)) - goto done; - #else - // saves 1% - if (!codebook_decode_deinterleave_repeat(f, book, residue_buffers, ch, &c_inter, &p_inter, n, r->part_size)) - goto done; - #endif - } else { - z += r->part_size; - c_inter = z & 1; - p_inter = z >> 1; - } - } - #ifndef STB_VORBIS_DIVIDES_IN_RESIDUE - ++class_set; - #endif - } - } else if (ch == 1) { - while (pcount < part_read) { - int z = r->begin + pcount*r->part_size; - int c_inter = 0, p_inter = z; - if (pass == 0) { - Codebook *c = f->codebooks+r->classbook; - int q; - DECODE(q,f,c); - if (q == EOP) goto done; - #ifndef STB_VORBIS_DIVIDES_IN_RESIDUE - part_classdata[0][class_set] = r->classdata[q]; - #else - for (i=classwords-1; i >= 0; --i) { - classifications[0][i+pcount] = q % r->classifications; - q /= r->classifications; - } - #endif - } - for (i=0; i < classwords && pcount < part_read; ++i, ++pcount) { - int z = r->begin + pcount*r->part_size; - #ifndef STB_VORBIS_DIVIDES_IN_RESIDUE - int c = part_classdata[0][class_set][i]; - #else - int c = classifications[0][pcount]; - #endif - int b = r->residue_books[c][pass]; - if (b >= 0) { - Codebook *book = f->codebooks + b; - if (!codebook_decode_deinterleave_repeat(f, book, residue_buffers, ch, &c_inter, &p_inter, n, r->part_size)) - goto done; - } else { - z += r->part_size; - c_inter = 0; - p_inter = z; - } - } - #ifndef STB_VORBIS_DIVIDES_IN_RESIDUE - ++class_set; - #endif - } - } else { - while (pcount < part_read) { - int z = r->begin + pcount*r->part_size; - int c_inter = z % ch, p_inter = z/ch; - if (pass == 0) { - Codebook *c = f->codebooks+r->classbook; - int q; - DECODE(q,f,c); - if (q == EOP) goto done; - #ifndef STB_VORBIS_DIVIDES_IN_RESIDUE - part_classdata[0][class_set] = r->classdata[q]; - #else - for (i=classwords-1; i >= 0; --i) { - classifications[0][i+pcount] = q % r->classifications; - q /= r->classifications; - } - #endif - } - for (i=0; i < classwords && pcount < part_read; ++i, ++pcount) { - int z = r->begin + pcount*r->part_size; - #ifndef STB_VORBIS_DIVIDES_IN_RESIDUE - int c = part_classdata[0][class_set][i]; - #else - int c = classifications[0][pcount]; - #endif - int b = r->residue_books[c][pass]; - if (b >= 0) { - Codebook *book = f->codebooks + b; - if (!codebook_decode_deinterleave_repeat(f, book, residue_buffers, ch, &c_inter, &p_inter, n, r->part_size)) - goto done; - } else { - z += r->part_size; - c_inter = z % ch; - p_inter = z / ch; - } - } - #ifndef STB_VORBIS_DIVIDES_IN_RESIDUE - ++class_set; - #endif - } - } - } - goto done; - } - CHECK(f); - - for (pass=0; pass < 8; ++pass) { - int pcount = 0, class_set=0; - while (pcount < part_read) { - if (pass == 0) { - for (j=0; j < ch; ++j) { - if (!do_not_decode[j]) { - Codebook *c = f->codebooks+r->classbook; - int temp; - DECODE(temp,f,c); - if (temp == EOP) goto done; - #ifndef STB_VORBIS_DIVIDES_IN_RESIDUE - part_classdata[j][class_set] = r->classdata[temp]; - #else - for (i=classwords-1; i >= 0; --i) { - classifications[j][i+pcount] = temp % r->classifications; - temp /= r->classifications; - } - #endif - } - } - } - for (i=0; i < classwords && pcount < part_read; ++i, ++pcount) { - for (j=0; j < ch; ++j) { - if (!do_not_decode[j]) { - #ifndef STB_VORBIS_DIVIDES_IN_RESIDUE - int c = part_classdata[j][class_set][i]; - #else - int c = classifications[j][pcount]; - #endif - int b = r->residue_books[c][pass]; - if (b >= 0) { - float *target = residue_buffers[j]; - int offset = r->begin + pcount * r->part_size; - int n = r->part_size; - Codebook *book = f->codebooks + b; - if (!residue_decode(f, book, target, offset, n, rtype)) - goto done; - } - } - } - } - #ifndef STB_VORBIS_DIVIDES_IN_RESIDUE - ++class_set; - #endif - } - } - done: - CHECK(f); - #ifndef STB_VORBIS_DIVIDES_IN_RESIDUE - temp_free(f,part_classdata); - #else - temp_free(f,classifications); - #endif - temp_alloc_restore(f,temp_alloc_point); -} - - -#if 0 -// slow way for debugging -void inverse_mdct_slow(float *buffer, int n) -{ - int i,j; - int n2 = n >> 1; - float *x = (float *) malloc(sizeof(*x) * n2); - memcpy(x, buffer, sizeof(*x) * n2); - for (i=0; i < n; ++i) { - float acc = 0; - for (j=0; j < n2; ++j) - // formula from paper: - //acc += n/4.0f * x[j] * (float) cos(M_PI / 2 / n * (2 * i + 1 + n/2.0)*(2*j+1)); - // formula from wikipedia - //acc += 2.0f / n2 * x[j] * (float) cos(M_PI/n2 * (i + 0.5 + n2/2)*(j + 0.5)); - // these are equivalent, except the formula from the paper inverts the multiplier! - // however, what actually works is NO MULTIPLIER!?! - //acc += 64 * 2.0f / n2 * x[j] * (float) cos(M_PI/n2 * (i + 0.5 + n2/2)*(j + 0.5)); - acc += x[j] * (float) cos(M_PI / 2 / n * (2 * i + 1 + n/2.0)*(2*j+1)); - buffer[i] = acc; - } - free(x); -} -#elif 0 -// same as above, but just barely able to run in real time on modern machines -void inverse_mdct_slow(float *buffer, int n, vorb *f, int blocktype) -{ - float mcos[16384]; - int i,j; - int n2 = n >> 1, nmask = (n << 2) -1; - float *x = (float *) malloc(sizeof(*x) * n2); - memcpy(x, buffer, sizeof(*x) * n2); - for (i=0; i < 4*n; ++i) - mcos[i] = (float) cos(M_PI / 2 * i / n); - - for (i=0; i < n; ++i) { - float acc = 0; - for (j=0; j < n2; ++j) - acc += x[j] * mcos[(2 * i + 1 + n2)*(2*j+1) & nmask]; - buffer[i] = acc; - } - free(x); -} -#elif 0 -// transform to use a slow dct-iv; this is STILL basically trivial, -// but only requires half as many ops -void dct_iv_slow(float *buffer, int n) -{ - float mcos[16384]; - float x[2048]; - int i,j; - int n2 = n >> 1, nmask = (n << 3) - 1; - memcpy(x, buffer, sizeof(*x) * n); - for (i=0; i < 8*n; ++i) - mcos[i] = (float) cos(M_PI / 4 * i / n); - for (i=0; i < n; ++i) { - float acc = 0; - for (j=0; j < n; ++j) - acc += x[j] * mcos[((2 * i + 1)*(2*j+1)) & nmask]; - buffer[i] = acc; - } -} - -void inverse_mdct_slow(float *buffer, int n, vorb *f, int blocktype) -{ - int i, n4 = n >> 2, n2 = n >> 1, n3_4 = n - n4; - float temp[4096]; - - memcpy(temp, buffer, n2 * sizeof(float)); - dct_iv_slow(temp, n2); // returns -c'-d, a-b' - - for (i=0; i < n4 ; ++i) buffer[i] = temp[i+n4]; // a-b' - for ( ; i < n3_4; ++i) buffer[i] = -temp[n3_4 - i - 1]; // b-a', c+d' - for ( ; i < n ; ++i) buffer[i] = -temp[i - n3_4]; // c'+d -} -#endif - -#ifndef LIBVORBIS_MDCT -#define LIBVORBIS_MDCT 0 -#endif - -#if LIBVORBIS_MDCT -// directly call the vorbis MDCT using an interface documented -// by Jeff Roberts... useful for performance comparison -typedef struct -{ - int n; - int log2n; - - float *trig; - int *bitrev; - - float scale; -} mdct_lookup; - -extern void mdct_init(mdct_lookup *lookup, int n); -extern void mdct_clear(mdct_lookup *l); -extern void mdct_backward(mdct_lookup *init, float *in, float *out); - -mdct_lookup M1,M2; - -void inverse_mdct(float *buffer, int n, vorb *f, int blocktype) -{ - mdct_lookup *M; - if (M1.n == n) M = &M1; - else if (M2.n == n) M = &M2; - else if (M1.n == 0) { mdct_init(&M1, n); M = &M1; } - else { - if (M2.n) __asm int 3; - mdct_init(&M2, n); - M = &M2; - } - - mdct_backward(M, buffer, buffer); -} -#endif - - -// the following were split out into separate functions while optimizing; -// they could be pushed back up but eh. __forceinline showed no change; -// they're probably already being inlined. -static void imdct_step3_iter0_loop(int n, float *e, int i_off, int k_off, float *A) -{ - float *ee0 = e + i_off; - float *ee2 = ee0 + k_off; - int i; - - assert((n & 3) == 0); - for (i=(n>>2); i > 0; --i) { - float k00_20, k01_21; - k00_20 = ee0[ 0] - ee2[ 0]; - k01_21 = ee0[-1] - ee2[-1]; - ee0[ 0] += ee2[ 0];//ee0[ 0] = ee0[ 0] + ee2[ 0]; - ee0[-1] += ee2[-1];//ee0[-1] = ee0[-1] + ee2[-1]; - ee2[ 0] = k00_20 * A[0] - k01_21 * A[1]; - ee2[-1] = k01_21 * A[0] + k00_20 * A[1]; - A += 8; - - k00_20 = ee0[-2] - ee2[-2]; - k01_21 = ee0[-3] - ee2[-3]; - ee0[-2] += ee2[-2];//ee0[-2] = ee0[-2] + ee2[-2]; - ee0[-3] += ee2[-3];//ee0[-3] = ee0[-3] + ee2[-3]; - ee2[-2] = k00_20 * A[0] - k01_21 * A[1]; - ee2[-3] = k01_21 * A[0] + k00_20 * A[1]; - A += 8; - - k00_20 = ee0[-4] - ee2[-4]; - k01_21 = ee0[-5] - ee2[-5]; - ee0[-4] += ee2[-4];//ee0[-4] = ee0[-4] + ee2[-4]; - ee0[-5] += ee2[-5];//ee0[-5] = ee0[-5] + ee2[-5]; - ee2[-4] = k00_20 * A[0] - k01_21 * A[1]; - ee2[-5] = k01_21 * A[0] + k00_20 * A[1]; - A += 8; - - k00_20 = ee0[-6] - ee2[-6]; - k01_21 = ee0[-7] - ee2[-7]; - ee0[-6] += ee2[-6];//ee0[-6] = ee0[-6] + ee2[-6]; - ee0[-7] += ee2[-7];//ee0[-7] = ee0[-7] + ee2[-7]; - ee2[-6] = k00_20 * A[0] - k01_21 * A[1]; - ee2[-7] = k01_21 * A[0] + k00_20 * A[1]; - A += 8; - ee0 -= 8; - ee2 -= 8; - } -} - -static void imdct_step3_inner_r_loop(int lim, float *e, int d0, int k_off, float *A, int k1) -{ - int i; - float k00_20, k01_21; - - float *e0 = e + d0; - float *e2 = e0 + k_off; - - for (i=lim >> 2; i > 0; --i) { - k00_20 = e0[-0] - e2[-0]; - k01_21 = e0[-1] - e2[-1]; - e0[-0] += e2[-0];//e0[-0] = e0[-0] + e2[-0]; - e0[-1] += e2[-1];//e0[-1] = e0[-1] + e2[-1]; - e2[-0] = (k00_20)*A[0] - (k01_21) * A[1]; - e2[-1] = (k01_21)*A[0] + (k00_20) * A[1]; - - A += k1; - - k00_20 = e0[-2] - e2[-2]; - k01_21 = e0[-3] - e2[-3]; - e0[-2] += e2[-2];//e0[-2] = e0[-2] + e2[-2]; - e0[-3] += e2[-3];//e0[-3] = e0[-3] + e2[-3]; - e2[-2] = (k00_20)*A[0] - (k01_21) * A[1]; - e2[-3] = (k01_21)*A[0] + (k00_20) * A[1]; - - A += k1; - - k00_20 = e0[-4] - e2[-4]; - k01_21 = e0[-5] - e2[-5]; - e0[-4] += e2[-4];//e0[-4] = e0[-4] + e2[-4]; - e0[-5] += e2[-5];//e0[-5] = e0[-5] + e2[-5]; - e2[-4] = (k00_20)*A[0] - (k01_21) * A[1]; - e2[-5] = (k01_21)*A[0] + (k00_20) * A[1]; - - A += k1; - - k00_20 = e0[-6] - e2[-6]; - k01_21 = e0[-7] - e2[-7]; - e0[-6] += e2[-6];//e0[-6] = e0[-6] + e2[-6]; - e0[-7] += e2[-7];//e0[-7] = e0[-7] + e2[-7]; - e2[-6] = (k00_20)*A[0] - (k01_21) * A[1]; - e2[-7] = (k01_21)*A[0] + (k00_20) * A[1]; - - e0 -= 8; - e2 -= 8; - - A += k1; - } -} - -static void imdct_step3_inner_s_loop(int n, float *e, int i_off, int k_off, float *A, int a_off, int k0) -{ - int i; - float A0 = A[0]; - float A1 = A[0+1]; - float A2 = A[0+a_off]; - float A3 = A[0+a_off+1]; - float A4 = A[0+a_off*2+0]; - float A5 = A[0+a_off*2+1]; - float A6 = A[0+a_off*3+0]; - float A7 = A[0+a_off*3+1]; - - float k00,k11; - - float *ee0 = e +i_off; - float *ee2 = ee0+k_off; - - for (i=n; i > 0; --i) { - k00 = ee0[ 0] - ee2[ 0]; - k11 = ee0[-1] - ee2[-1]; - ee0[ 0] = ee0[ 0] + ee2[ 0]; - ee0[-1] = ee0[-1] + ee2[-1]; - ee2[ 0] = (k00) * A0 - (k11) * A1; - ee2[-1] = (k11) * A0 + (k00) * A1; - - k00 = ee0[-2] - ee2[-2]; - k11 = ee0[-3] - ee2[-3]; - ee0[-2] = ee0[-2] + ee2[-2]; - ee0[-3] = ee0[-3] + ee2[-3]; - ee2[-2] = (k00) * A2 - (k11) * A3; - ee2[-3] = (k11) * A2 + (k00) * A3; - - k00 = ee0[-4] - ee2[-4]; - k11 = ee0[-5] - ee2[-5]; - ee0[-4] = ee0[-4] + ee2[-4]; - ee0[-5] = ee0[-5] + ee2[-5]; - ee2[-4] = (k00) * A4 - (k11) * A5; - ee2[-5] = (k11) * A4 + (k00) * A5; - - k00 = ee0[-6] - ee2[-6]; - k11 = ee0[-7] - ee2[-7]; - ee0[-6] = ee0[-6] + ee2[-6]; - ee0[-7] = ee0[-7] + ee2[-7]; - ee2[-6] = (k00) * A6 - (k11) * A7; - ee2[-7] = (k11) * A6 + (k00) * A7; - - ee0 -= k0; - ee2 -= k0; - } -} - -static __forceinline void iter_54(float *z) -{ - float k00,k11,k22,k33; - float y0,y1,y2,y3; - - k00 = z[ 0] - z[-4]; - y0 = z[ 0] + z[-4]; - y2 = z[-2] + z[-6]; - k22 = z[-2] - z[-6]; - - z[-0] = y0 + y2; // z0 + z4 + z2 + z6 - z[-2] = y0 - y2; // z0 + z4 - z2 - z6 - - // done with y0,y2 - - k33 = z[-3] - z[-7]; - - z[-4] = k00 + k33; // z0 - z4 + z3 - z7 - z[-6] = k00 - k33; // z0 - z4 - z3 + z7 - - // done with k33 - - k11 = z[-1] - z[-5]; - y1 = z[-1] + z[-5]; - y3 = z[-3] + z[-7]; - - z[-1] = y1 + y3; // z1 + z5 + z3 + z7 - z[-3] = y1 - y3; // z1 + z5 - z3 - z7 - z[-5] = k11 - k22; // z1 - z5 + z2 - z6 - z[-7] = k11 + k22; // z1 - z5 - z2 + z6 -} - -static void imdct_step3_inner_s_loop_ld654(int n, float *e, int i_off, float *A, int base_n) -{ - int a_off = base_n >> 3; - float A2 = A[0+a_off]; - float *z = e + i_off; - float *base = z - 16 * n; - - while (z > base) { - float k00,k11; - - k00 = z[-0] - z[-8]; - k11 = z[-1] - z[-9]; - z[-0] = z[-0] + z[-8]; - z[-1] = z[-1] + z[-9]; - z[-8] = k00; - z[-9] = k11 ; - - k00 = z[ -2] - z[-10]; - k11 = z[ -3] - z[-11]; - z[ -2] = z[ -2] + z[-10]; - z[ -3] = z[ -3] + z[-11]; - z[-10] = (k00+k11) * A2; - z[-11] = (k11-k00) * A2; - - k00 = z[-12] - z[ -4]; // reverse to avoid a unary negation - k11 = z[ -5] - z[-13]; - z[ -4] = z[ -4] + z[-12]; - z[ -5] = z[ -5] + z[-13]; - z[-12] = k11; - z[-13] = k00; - - k00 = z[-14] - z[ -6]; // reverse to avoid a unary negation - k11 = z[ -7] - z[-15]; - z[ -6] = z[ -6] + z[-14]; - z[ -7] = z[ -7] + z[-15]; - z[-14] = (k00+k11) * A2; - z[-15] = (k00-k11) * A2; - - iter_54(z); - iter_54(z-8); - z -= 16; - } -} - -static void inverse_mdct(float *buffer, int n, vorb *f, int blocktype) -{ - int n2 = n >> 1, n4 = n >> 2, n8 = n >> 3, l; - int ld; - // @OPTIMIZE: reduce register pressure by using fewer variables? - int save_point = temp_alloc_save(f); - float *buf2 = (float *) temp_alloc(f, n2 * sizeof(*buf2)); - float *u=NULL,*v=NULL; - // twiddle factors - float *A = f->A[blocktype]; - - // IMDCT algorithm from "The use of multirate filter banks for coding of high quality digital audio" - // See notes about bugs in that paper in less-optimal implementation 'inverse_mdct_old' after this function. - - // kernel from paper - - - // merged: - // copy and reflect spectral data - // step 0 - - // note that it turns out that the items added together during - // this step are, in fact, being added to themselves (as reflected - // by step 0). inexplicable inefficiency! this became obvious - // once I combined the passes. - - // so there's a missing 'times 2' here (for adding X to itself). - // this propogates through linearly to the end, where the numbers - // are 1/2 too small, and need to be compensated for. - - { - float *d,*e, *AA, *e_stop; - d = &buf2[n2-2]; - AA = A; - e = &buffer[0]; - e_stop = &buffer[n2]; - while (e != e_stop) { - d[1] = (e[0] * AA[0] - e[2]*AA[1]); - d[0] = (e[0] * AA[1] + e[2]*AA[0]); - d -= 2; - AA += 2; - e += 4; - } - - e = &buffer[n2-3]; - while (d >= buf2) { - d[1] = (-e[2] * AA[0] - -e[0]*AA[1]); - d[0] = (-e[2] * AA[1] + -e[0]*AA[0]); - d -= 2; - AA += 2; - e -= 4; - } - } - - // now we use symbolic names for these, so that we can - // possibly swap their meaning as we change which operations - // are in place - - u = buffer; - v = buf2; - - // step 2 (paper output is w, now u) - // this could be in place, but the data ends up in the wrong - // place... _somebody_'s got to swap it, so this is nominated - { - float *AA = &A[n2-8]; - float *d0,*d1, *e0, *e1; - - e0 = &v[n4]; - e1 = &v[0]; - - d0 = &u[n4]; - d1 = &u[0]; - - while (AA >= A) { - float v40_20, v41_21; - - v41_21 = e0[1] - e1[1]; - v40_20 = e0[0] - e1[0]; - d0[1] = e0[1] + e1[1]; - d0[0] = e0[0] + e1[0]; - d1[1] = v41_21*AA[4] - v40_20*AA[5]; - d1[0] = v40_20*AA[4] + v41_21*AA[5]; - - v41_21 = e0[3] - e1[3]; - v40_20 = e0[2] - e1[2]; - d0[3] = e0[3] + e1[3]; - d0[2] = e0[2] + e1[2]; - d1[3] = v41_21*AA[0] - v40_20*AA[1]; - d1[2] = v40_20*AA[0] + v41_21*AA[1]; - - AA -= 8; - - d0 += 4; - d1 += 4; - e0 += 4; - e1 += 4; - } - } - - // step 3 - ld = ilog(n) - 1; // ilog is off-by-one from normal definitions - - // optimized step 3: - - // the original step3 loop can be nested r inside s or s inside r; - // it's written originally as s inside r, but this is dumb when r - // iterates many times, and s few. So I have two copies of it and - // switch between them halfway. - - // this is iteration 0 of step 3 - imdct_step3_iter0_loop(n >> 4, u, n2-1-n4*0, -(n >> 3), A); - imdct_step3_iter0_loop(n >> 4, u, n2-1-n4*1, -(n >> 3), A); - - // this is iteration 1 of step 3 - imdct_step3_inner_r_loop(n >> 5, u, n2-1 - n8*0, -(n >> 4), A, 16); - imdct_step3_inner_r_loop(n >> 5, u, n2-1 - n8*1, -(n >> 4), A, 16); - imdct_step3_inner_r_loop(n >> 5, u, n2-1 - n8*2, -(n >> 4), A, 16); - imdct_step3_inner_r_loop(n >> 5, u, n2-1 - n8*3, -(n >> 4), A, 16); - - l=2; - for (; l < (ld-3)>>1; ++l) { - int k0 = n >> (l+2), k0_2 = k0>>1; - int lim = 1 << (l+1); - int i; - for (i=0; i < lim; ++i) - imdct_step3_inner_r_loop(n >> (l+4), u, n2-1 - k0*i, -k0_2, A, 1 << (l+3)); - } - - for (; l < ld-6; ++l) { - int k0 = n >> (l+2), k1 = 1 << (l+3), k0_2 = k0>>1; - int rlim = n >> (l+6), r; - int lim = 1 << (l+1); - int i_off; - float *A0 = A; - i_off = n2-1; - for (r=rlim; r > 0; --r) { - imdct_step3_inner_s_loop(lim, u, i_off, -k0_2, A0, k1, k0); - A0 += k1*4; - i_off -= 8; - } - } - - // iterations with count: - // ld-6,-5,-4 all interleaved together - // the big win comes from getting rid of needless flops - // due to the constants on pass 5 & 4 being all 1 and 0; - // combining them to be simultaneous to improve cache made little difference - imdct_step3_inner_s_loop_ld654(n >> 5, u, n2-1, A, n); - - // output is u - - // step 4, 5, and 6 - // cannot be in-place because of step 5 - { - uint16 *bitrev = f->bit_reverse[blocktype]; - // weirdly, I'd have thought reading sequentially and writing - // erratically would have been better than vice-versa, but in - // fact that's not what my testing showed. (That is, with - // j = bitreverse(i), do you read i and write j, or read j and write i.) - - float *d0 = &v[n4-4]; - float *d1 = &v[n2-4]; - while (d0 >= v) { - int k4; - - k4 = bitrev[0]; - d1[3] = u[k4+0]; - d1[2] = u[k4+1]; - d0[3] = u[k4+2]; - d0[2] = u[k4+3]; - - k4 = bitrev[1]; - d1[1] = u[k4+0]; - d1[0] = u[k4+1]; - d0[1] = u[k4+2]; - d0[0] = u[k4+3]; - - d0 -= 4; - d1 -= 4; - bitrev += 2; - } - } - // (paper output is u, now v) - - - // data must be in buf2 - assert(v == buf2); - - // step 7 (paper output is v, now v) - // this is now in place - { - float *C = f->C[blocktype]; - float *d, *e; - - d = v; - e = v + n2 - 4; - - while (d < e) { - float a02,a11,b0,b1,b2,b3; - - a02 = d[0] - e[2]; - a11 = d[1] + e[3]; - - b0 = C[1]*a02 + C[0]*a11; - b1 = C[1]*a11 - C[0]*a02; - - b2 = d[0] + e[ 2]; - b3 = d[1] - e[ 3]; - - d[0] = b2 + b0; - d[1] = b3 + b1; - e[2] = b2 - b0; - e[3] = b1 - b3; - - a02 = d[2] - e[0]; - a11 = d[3] + e[1]; - - b0 = C[3]*a02 + C[2]*a11; - b1 = C[3]*a11 - C[2]*a02; - - b2 = d[2] + e[ 0]; - b3 = d[3] - e[ 1]; - - d[2] = b2 + b0; - d[3] = b3 + b1; - e[0] = b2 - b0; - e[1] = b1 - b3; - - C += 4; - d += 4; - e -= 4; - } - } - - // data must be in buf2 - - - // step 8+decode (paper output is X, now buffer) - // this generates pairs of data a la 8 and pushes them directly through - // the decode kernel (pushing rather than pulling) to avoid having - // to make another pass later - - // this cannot POSSIBLY be in place, so we refer to the buffers directly - - { - float *d0,*d1,*d2,*d3; - - float *B = f->B[blocktype] + n2 - 8; - float *e = buf2 + n2 - 8; - d0 = &buffer[0]; - d1 = &buffer[n2-4]; - d2 = &buffer[n2]; - d3 = &buffer[n-4]; - while (e >= v) { - float p0,p1,p2,p3; - - p3 = e[6]*B[7] - e[7]*B[6]; - p2 = -e[6]*B[6] - e[7]*B[7]; - - d0[0] = p3; - d1[3] = - p3; - d2[0] = p2; - d3[3] = p2; - - p1 = e[4]*B[5] - e[5]*B[4]; - p0 = -e[4]*B[4] - e[5]*B[5]; - - d0[1] = p1; - d1[2] = - p1; - d2[1] = p0; - d3[2] = p0; - - p3 = e[2]*B[3] - e[3]*B[2]; - p2 = -e[2]*B[2] - e[3]*B[3]; - - d0[2] = p3; - d1[1] = - p3; - d2[2] = p2; - d3[1] = p2; - - p1 = e[0]*B[1] - e[1]*B[0]; - p0 = -e[0]*B[0] - e[1]*B[1]; - - d0[3] = p1; - d1[0] = - p1; - d2[3] = p0; - d3[0] = p0; - - B -= 8; - e -= 8; - d0 += 4; - d2 += 4; - d1 -= 4; - d3 -= 4; - } - } - - temp_free(f,buf2); - temp_alloc_restore(f,save_point); -} - -#if 0 -// this is the original version of the above code, if you want to optimize it from scratch -void inverse_mdct_naive(float *buffer, int n) -{ - float s; - float A[1 << 12], B[1 << 12], C[1 << 11]; - int i,k,k2,k4, n2 = n >> 1, n4 = n >> 2, n8 = n >> 3, l; - int n3_4 = n - n4, ld; - // how can they claim this only uses N words?! - // oh, because they're only used sparsely, whoops - float u[1 << 13], X[1 << 13], v[1 << 13], w[1 << 13]; - // set up twiddle factors - - for (k=k2=0; k < n4; ++k,k2+=2) { - A[k2 ] = (float) cos(4*k*M_PI/n); - A[k2+1] = (float) -sin(4*k*M_PI/n); - B[k2 ] = (float) cos((k2+1)*M_PI/n/2); - B[k2+1] = (float) sin((k2+1)*M_PI/n/2); - } - for (k=k2=0; k < n8; ++k,k2+=2) { - C[k2 ] = (float) cos(2*(k2+1)*M_PI/n); - C[k2+1] = (float) -sin(2*(k2+1)*M_PI/n); - } - - // IMDCT algorithm from "The use of multirate filter banks for coding of high quality digital audio" - // Note there are bugs in that pseudocode, presumably due to them attempting - // to rename the arrays nicely rather than representing the way their actual - // implementation bounces buffers back and forth. As a result, even in the - // "some formulars corrected" version, a direct implementation fails. These - // are noted below as "paper bug". - - // copy and reflect spectral data - for (k=0; k < n2; ++k) u[k] = buffer[k]; - for ( ; k < n ; ++k) u[k] = -buffer[n - k - 1]; - // kernel from paper - // step 1 - for (k=k2=k4=0; k < n4; k+=1, k2+=2, k4+=4) { - v[n-k4-1] = (u[k4] - u[n-k4-1]) * A[k2] - (u[k4+2] - u[n-k4-3])*A[k2+1]; - v[n-k4-3] = (u[k4] - u[n-k4-1]) * A[k2+1] + (u[k4+2] - u[n-k4-3])*A[k2]; - } - // step 2 - for (k=k4=0; k < n8; k+=1, k4+=4) { - w[n2+3+k4] = v[n2+3+k4] + v[k4+3]; - w[n2+1+k4] = v[n2+1+k4] + v[k4+1]; - w[k4+3] = (v[n2+3+k4] - v[k4+3])*A[n2-4-k4] - (v[n2+1+k4]-v[k4+1])*A[n2-3-k4]; - w[k4+1] = (v[n2+1+k4] - v[k4+1])*A[n2-4-k4] + (v[n2+3+k4]-v[k4+3])*A[n2-3-k4]; - } - // step 3 - ld = ilog(n) - 1; // ilog is off-by-one from normal definitions - for (l=0; l < ld-3; ++l) { - int k0 = n >> (l+2), k1 = 1 << (l+3); - int rlim = n >> (l+4), r4, r; - int s2lim = 1 << (l+2), s2; - for (r=r4=0; r < rlim; r4+=4,++r) { - for (s2=0; s2 < s2lim; s2+=2) { - u[n-1-k0*s2-r4] = w[n-1-k0*s2-r4] + w[n-1-k0*(s2+1)-r4]; - u[n-3-k0*s2-r4] = w[n-3-k0*s2-r4] + w[n-3-k0*(s2+1)-r4]; - u[n-1-k0*(s2+1)-r4] = (w[n-1-k0*s2-r4] - w[n-1-k0*(s2+1)-r4]) * A[r*k1] - - (w[n-3-k0*s2-r4] - w[n-3-k0*(s2+1)-r4]) * A[r*k1+1]; - u[n-3-k0*(s2+1)-r4] = (w[n-3-k0*s2-r4] - w[n-3-k0*(s2+1)-r4]) * A[r*k1] - + (w[n-1-k0*s2-r4] - w[n-1-k0*(s2+1)-r4]) * A[r*k1+1]; - } - } - if (l+1 < ld-3) { - // paper bug: ping-ponging of u&w here is omitted - memcpy(w, u, sizeof(u)); - } - } - - // step 4 - for (i=0; i < n8; ++i) { - int j = bit_reverse(i) >> (32-ld+3); - assert(j < n8); - if (i == j) { - // paper bug: original code probably swapped in place; if copying, - // need to directly copy in this case - int i8 = i << 3; - v[i8+1] = u[i8+1]; - v[i8+3] = u[i8+3]; - v[i8+5] = u[i8+5]; - v[i8+7] = u[i8+7]; - } else if (i < j) { - int i8 = i << 3, j8 = j << 3; - v[j8+1] = u[i8+1], v[i8+1] = u[j8 + 1]; - v[j8+3] = u[i8+3], v[i8+3] = u[j8 + 3]; - v[j8+5] = u[i8+5], v[i8+5] = u[j8 + 5]; - v[j8+7] = u[i8+7], v[i8+7] = u[j8 + 7]; - } - } - // step 5 - for (k=0; k < n2; ++k) { - w[k] = v[k*2+1]; - } - // step 6 - for (k=k2=k4=0; k < n8; ++k, k2 += 2, k4 += 4) { - u[n-1-k2] = w[k4]; - u[n-2-k2] = w[k4+1]; - u[n3_4 - 1 - k2] = w[k4+2]; - u[n3_4 - 2 - k2] = w[k4+3]; - } - // step 7 - for (k=k2=0; k < n8; ++k, k2 += 2) { - v[n2 + k2 ] = ( u[n2 + k2] + u[n-2-k2] + C[k2+1]*(u[n2+k2]-u[n-2-k2]) + C[k2]*(u[n2+k2+1]+u[n-2-k2+1]))/2; - v[n-2 - k2] = ( u[n2 + k2] + u[n-2-k2] - C[k2+1]*(u[n2+k2]-u[n-2-k2]) - C[k2]*(u[n2+k2+1]+u[n-2-k2+1]))/2; - v[n2+1+ k2] = ( u[n2+1+k2] - u[n-1-k2] + C[k2+1]*(u[n2+1+k2]+u[n-1-k2]) - C[k2]*(u[n2+k2]-u[n-2-k2]))/2; - v[n-1 - k2] = (-u[n2+1+k2] + u[n-1-k2] + C[k2+1]*(u[n2+1+k2]+u[n-1-k2]) - C[k2]*(u[n2+k2]-u[n-2-k2]))/2; - } - // step 8 - for (k=k2=0; k < n4; ++k,k2 += 2) { - X[k] = v[k2+n2]*B[k2 ] + v[k2+1+n2]*B[k2+1]; - X[n2-1-k] = v[k2+n2]*B[k2+1] - v[k2+1+n2]*B[k2 ]; - } - - // decode kernel to output - // determined the following value experimentally - // (by first figuring out what made inverse_mdct_slow work); then matching that here - // (probably vorbis encoder premultiplies by n or n/2, to save it on the decoder?) - s = 0.5; // theoretically would be n4 - - // [[[ note! the s value of 0.5 is compensated for by the B[] in the current code, - // so it needs to use the "old" B values to behave correctly, or else - // set s to 1.0 ]]] - for (i=0; i < n4 ; ++i) buffer[i] = s * X[i+n4]; - for ( ; i < n3_4; ++i) buffer[i] = -s * X[n3_4 - i - 1]; - for ( ; i < n ; ++i) buffer[i] = -s * X[i - n3_4]; -} -#endif - -static float *get_window(vorb *f, int len) -{ - len <<= 1; - if (len == f->blocksize_0) return f->window[0]; - if (len == f->blocksize_1) return f->window[1]; - assert(0); - return NULL; -} - -#ifndef STB_VORBIS_NO_DEFER_FLOOR -typedef int16 YTYPE; -#else -typedef int YTYPE; -#endif -static int do_floor(vorb *f, Mapping *map, int i, int n, float *target, YTYPE *finalY, uint8 *step2_flag) -{ - int n2 = n >> 1; - int s = map->chan[i].mux, floor; - floor = map->submap_floor[s]; - if (f->floor_types[floor] == 0) { - return error(f, VORBIS_invalid_stream); - } else { - Floor1 *g = &f->floor_config[floor].floor1; - int j,q; - int lx = 0, ly = finalY[0] * g->floor1_multiplier; - for (q=1; q < g->values; ++q) { - j = g->sorted_order[q]; - #ifndef STB_VORBIS_NO_DEFER_FLOOR - if (finalY[j] >= 0) - #else - if (step2_flag[j]) - #endif - { - int hy = finalY[j] * g->floor1_multiplier; - int hx = g->Xlist[j]; - if (lx != hx) - draw_line(target, lx,ly, hx,hy, n2); - CHECK(f); - lx = hx, ly = hy; - } - } - if (lx < n2) { - // optimization of: draw_line(target, lx,ly, n,ly, n2); - for (j=lx; j < n2; ++j) - LINE_OP(target[j], inverse_db_table[ly]); - CHECK(f); - } - } - return TRUE; -} - -// The meaning of "left" and "right" -// -// For a given frame: -// we compute samples from 0..n -// window_center is n/2 -// we'll window and mix the samples from left_start to left_end with data from the previous frame -// all of the samples from left_end to right_start can be output without mixing; however, -// this interval is 0-length except when transitioning between short and long frames -// all of the samples from right_start to right_end need to be mixed with the next frame, -// which we don't have, so those get saved in a buffer -// frame N's right_end-right_start, the number of samples to mix with the next frame, -// has to be the same as frame N+1's left_end-left_start (which they are by -// construction) - -static int vorbis_decode_initial(vorb *f, int *p_left_start, int *p_left_end, int *p_right_start, int *p_right_end, int *mode) -{ - Mode *m; - int i, n, prev, next, window_center; - f->channel_buffer_start = f->channel_buffer_end = 0; - - retry: - if (f->eof) return FALSE; - if (!maybe_start_packet(f)) - return FALSE; - // check packet type - if (get_bits(f,1) != 0) { - if (IS_PUSH_MODE(f)) - return error(f,VORBIS_bad_packet_type); - while (EOP != get8_packet(f)); - goto retry; - } - - if (f->alloc.alloc_buffer) - assert(f->alloc.alloc_buffer_length_in_bytes == f->temp_offset); - - i = get_bits(f, ilog(f->mode_count-1)); - if (i == EOP) return FALSE; - if (i >= f->mode_count) return FALSE; - *mode = i; - m = f->mode_config + i; - if (m->blockflag) { - n = f->blocksize_1; - prev = get_bits(f,1); - next = get_bits(f,1); - } else { - prev = next = 0; - n = f->blocksize_0; - } - -// WINDOWING - - window_center = n >> 1; - if (m->blockflag && !prev) { - *p_left_start = (n - f->blocksize_0) >> 2; - *p_left_end = (n + f->blocksize_0) >> 2; - } else { - *p_left_start = 0; - *p_left_end = window_center; - } - if (m->blockflag && !next) { - *p_right_start = (n*3 - f->blocksize_0) >> 2; - *p_right_end = (n*3 + f->blocksize_0) >> 2; - } else { - *p_right_start = window_center; - *p_right_end = n; - } - - return TRUE; -} - -static int vorbis_decode_packet_rest(vorb *f, int *len, Mode *m, int left_start, int left_end, int right_start, int right_end, int *p_left) -{ - Mapping *map; - int i,j,k,n,n2; - int zero_channel[256]; - int really_zero_channel[256]; - -// WINDOWING - - n = f->blocksize[m->blockflag]; - map = &f->mapping[m->mapping]; - -// FLOORS - n2 = n >> 1; - - CHECK(f); - - for (i=0; i < f->channels; ++i) { - int s = map->chan[i].mux, floor; - zero_channel[i] = FALSE; - floor = map->submap_floor[s]; - if (f->floor_types[floor] == 0) { - return error(f, VORBIS_invalid_stream); - } else { - Floor1 *g = &f->floor_config[floor].floor1; - if (get_bits(f, 1)) { - short *finalY; - uint8 step2_flag[256]; - static int range_list[4] = { 256, 128, 86, 64 }; - int range = range_list[g->floor1_multiplier-1]; - int offset = 2; - finalY = f->finalY[i]; - finalY[0] = get_bits(f, ilog(range)-1); - finalY[1] = get_bits(f, ilog(range)-1); - for (j=0; j < g->partitions; ++j) { - int pclass = g->partition_class_list[j]; - int cdim = g->class_dimensions[pclass]; - int cbits = g->class_subclasses[pclass]; - int csub = (1 << cbits)-1; - int cval = 0; - if (cbits) { - Codebook *c = f->codebooks + g->class_masterbooks[pclass]; - DECODE(cval,f,c); - } - for (k=0; k < cdim; ++k) { - int book = g->subclass_books[pclass][cval & csub]; - cval = cval >> cbits; - if (book >= 0) { - int temp; - Codebook *c = f->codebooks + book; - DECODE(temp,f,c); - finalY[offset++] = temp; - } else - finalY[offset++] = 0; - } - } - if (f->valid_bits == INVALID_BITS) goto error; // behavior according to spec - step2_flag[0] = step2_flag[1] = 1; - for (j=2; j < g->values; ++j) { - int low, high, pred, highroom, lowroom, room, val; - low = g->neighbors[j][0]; - high = g->neighbors[j][1]; - //neighbors(g->Xlist, j, &low, &high); - pred = predict_point(g->Xlist[j], g->Xlist[low], g->Xlist[high], finalY[low], finalY[high]); - val = finalY[j]; - highroom = range - pred; - lowroom = pred; - if (highroom < lowroom) - room = highroom * 2; - else - room = lowroom * 2; - if (val) { - step2_flag[low] = step2_flag[high] = 1; - step2_flag[j] = 1; - if (val >= room) - if (highroom > lowroom) - finalY[j] = val - lowroom + pred; - else - finalY[j] = pred - val + highroom - 1; - else - if (val & 1) - finalY[j] = pred - ((val+1)>>1); - else - finalY[j] = pred + (val>>1); - } else { - step2_flag[j] = 0; - finalY[j] = pred; - } - } - -#ifdef STB_VORBIS_NO_DEFER_FLOOR - do_floor(f, map, i, n, f->floor_buffers[i], finalY, step2_flag); -#else - // defer final floor computation until _after_ residue - for (j=0; j < g->values; ++j) { - if (!step2_flag[j]) - finalY[j] = -1; - } -#endif - } else { - error: - zero_channel[i] = TRUE; - } - // So we just defer everything else to later - - // at this point we've decoded the floor into buffer - } - } - CHECK(f); - // at this point we've decoded all floors - - if (f->alloc.alloc_buffer) - assert(f->alloc.alloc_buffer_length_in_bytes == f->temp_offset); - - // re-enable coupled channels if necessary - memcpy(really_zero_channel, zero_channel, sizeof(really_zero_channel[0]) * f->channels); - for (i=0; i < map->coupling_steps; ++i) - if (!zero_channel[map->chan[i].magnitude] || !zero_channel[map->chan[i].angle]) { - zero_channel[map->chan[i].magnitude] = zero_channel[map->chan[i].angle] = FALSE; - } - - CHECK(f); -// RESIDUE DECODE - for (i=0; i < map->submaps; ++i) { - float *residue_buffers[STB_VORBIS_MAX_CHANNELS]; - int r; - uint8 do_not_decode[256]; - int ch = 0; - for (j=0; j < f->channels; ++j) { - if (map->chan[j].mux == i) { - if (zero_channel[j]) { - do_not_decode[ch] = TRUE; - residue_buffers[ch] = NULL; - } else { - do_not_decode[ch] = FALSE; - residue_buffers[ch] = f->channel_buffers[j]; - } - ++ch; - } - } - r = map->submap_residue[i]; - decode_residue(f, residue_buffers, ch, n2, r, do_not_decode); - } - - if (f->alloc.alloc_buffer) - assert(f->alloc.alloc_buffer_length_in_bytes == f->temp_offset); - CHECK(f); - -// INVERSE COUPLING - for (i = map->coupling_steps-1; i >= 0; --i) { - int n2 = n >> 1; - float *m = f->channel_buffers[map->chan[i].magnitude]; - float *a = f->channel_buffers[map->chan[i].angle ]; - for (j=0; j < n2; ++j) { - float a2,m2; - if (m[j] > 0) - if (a[j] > 0) - m2 = m[j], a2 = m[j] - a[j]; - else - a2 = m[j], m2 = m[j] + a[j]; - else - if (a[j] > 0) - m2 = m[j], a2 = m[j] + a[j]; - else - a2 = m[j], m2 = m[j] - a[j]; - m[j] = m2; - a[j] = a2; - } - } - CHECK(f); - - // finish decoding the floors -#ifndef STB_VORBIS_NO_DEFER_FLOOR - for (i=0; i < f->channels; ++i) { - if (really_zero_channel[i]) { - memset(f->channel_buffers[i], 0, sizeof(*f->channel_buffers[i]) * n2); - } else { - do_floor(f, map, i, n, f->channel_buffers[i], f->finalY[i], NULL); - } - } -#else - for (i=0; i < f->channels; ++i) { - if (really_zero_channel[i]) { - memset(f->channel_buffers[i], 0, sizeof(*f->channel_buffers[i]) * n2); - } else { - for (j=0; j < n2; ++j) - f->channel_buffers[i][j] *= f->floor_buffers[i][j]; - } - } -#endif - -// INVERSE MDCT - CHECK(f); - for (i=0; i < f->channels; ++i) - inverse_mdct(f->channel_buffers[i], n, f, m->blockflag); - CHECK(f); - - // this shouldn't be necessary, unless we exited on an error - // and want to flush to get to the next packet - flush_packet(f); - - if (f->first_decode) { - // assume we start so first non-discarded sample is sample 0 - // this isn't to spec, but spec would require us to read ahead - // and decode the size of all current frames--could be done, - // but presumably it's not a commonly used feature - f->current_loc = -n2; // start of first frame is positioned for discard - // we might have to discard samples "from" the next frame too, - // if we're lapping a large block then a small at the start? - f->discard_samples_deferred = n - right_end; - f->current_loc_valid = TRUE; - f->first_decode = FALSE; - } else if (f->discard_samples_deferred) { - if (f->discard_samples_deferred >= right_start - left_start) { - f->discard_samples_deferred -= (right_start - left_start); - left_start = right_start; - *p_left = left_start; - } else { - left_start += f->discard_samples_deferred; - *p_left = left_start; - f->discard_samples_deferred = 0; - } - } else if (f->previous_length == 0 && f->current_loc_valid) { - // we're recovering from a seek... that means we're going to discard - // the samples from this packet even though we know our position from - // the last page header, so we need to update the position based on - // the discarded samples here - // but wait, the code below is going to add this in itself even - // on a discard, so we don't need to do it here... - } - - // check if we have ogg information about the sample # for this packet - if (f->last_seg_which == f->end_seg_with_known_loc) { - // if we have a valid current loc, and this is final: - if (f->current_loc_valid && (f->page_flag & PAGEFLAG_last_page)) { - uint32 current_end = f->known_loc_for_packet - (n-right_end); - // then let's infer the size of the (probably) short final frame - if (current_end < f->current_loc + (right_end-left_start)) { - if (current_end < f->current_loc) { - // negative truncation, that's impossible! - *len = 0; - } else { - *len = current_end - f->current_loc; - } - *len += left_start; - if (*len > right_end) *len = right_end; // this should never happen - f->current_loc += *len; - return TRUE; - } - } - // otherwise, just set our sample loc - // guess that the ogg granule pos refers to the _middle_ of the - // last frame? - // set f->current_loc to the position of left_start - f->current_loc = f->known_loc_for_packet - (n2-left_start); - f->current_loc_valid = TRUE; - } - if (f->current_loc_valid) - f->current_loc += (right_start - left_start); - - if (f->alloc.alloc_buffer) - assert(f->alloc.alloc_buffer_length_in_bytes == f->temp_offset); - *len = right_end; // ignore samples after the window goes to 0 - CHECK(f); - - return TRUE; -} - -static int vorbis_decode_packet(vorb *f, int *len, int *p_left, int *p_right) -{ - int mode, left_end, right_end; - if (!vorbis_decode_initial(f, p_left, &left_end, p_right, &right_end, &mode)) return 0; - return vorbis_decode_packet_rest(f, len, f->mode_config + mode, *p_left, left_end, *p_right, right_end, p_left); -} - -static int vorbis_finish_frame(stb_vorbis *f, int len, int left, int right) -{ - int prev,i,j; - // we use right&left (the start of the right- and left-window sin()-regions) - // to determine how much to return, rather than inferring from the rules - // (same result, clearer code); 'left' indicates where our sin() window - // starts, therefore where the previous window's right edge starts, and - // therefore where to start mixing from the previous buffer. 'right' - // indicates where our sin() ending-window starts, therefore that's where - // we start saving, and where our returned-data ends. - - // mixin from previous window - if (f->previous_length) { - int i,j, n = f->previous_length; - float *w = get_window(f, n); - for (i=0; i < f->channels; ++i) { - for (j=0; j < n; ++j) - f->channel_buffers[i][left+j] = - f->channel_buffers[i][left+j]*w[ j] + - f->previous_window[i][ j]*w[n-1-j]; - } - } - - prev = f->previous_length; - - // last half of this data becomes previous window - f->previous_length = len - right; - - // @OPTIMIZE: could avoid this copy by double-buffering the - // output (flipping previous_window with channel_buffers), but - // then previous_window would have to be 2x as large, and - // channel_buffers couldn't be temp mem (although they're NOT - // currently temp mem, they could be (unless we want to level - // performance by spreading out the computation)) - for (i=0; i < f->channels; ++i) - for (j=0; right+j < len; ++j) - f->previous_window[i][j] = f->channel_buffers[i][right+j]; - - if (!prev) - // there was no previous packet, so this data isn't valid... - // this isn't entirely true, only the would-have-overlapped data - // isn't valid, but this seems to be what the spec requires - return 0; - - // truncate a short frame - if (len < right) right = len; - - f->samples_output += right-left; - - return right - left; -} - -static void vorbis_pump_first_frame(stb_vorbis *f) -{ - int len, right, left; - if (vorbis_decode_packet(f, &len, &left, &right)) - vorbis_finish_frame(f, len, left, right); -} - -#ifndef STB_VORBIS_NO_PUSHDATA_API -static int is_whole_packet_present(stb_vorbis *f, int end_page) -{ - // make sure that we have the packet available before continuing... - // this requires a full ogg parse, but we know we can fetch from f->stream - - // instead of coding this out explicitly, we could save the current read state, - // read the next packet with get8() until end-of-packet, check f->eof, then - // reset the state? but that would be slower, esp. since we'd have over 256 bytes - // of state to restore (primarily the page segment table) - - int s = f->next_seg, first = TRUE; - uint8 *p = f->stream; - - if (s != -1) { // if we're not starting the packet with a 'continue on next page' flag - for (; s < f->segment_count; ++s) { - p += f->segments[s]; - if (f->segments[s] < 255) // stop at first short segment - break; - } - // either this continues, or it ends it... - if (end_page) - if (s < f->segment_count-1) return error(f, VORBIS_invalid_stream); - if (s == f->segment_count) - s = -1; // set 'crosses page' flag - if (p > f->stream_end) return error(f, VORBIS_need_more_data); - first = FALSE; - } - for (; s == -1;) { - uint8 *q; - int n; - - // check that we have the page header ready - if (p + 26 >= f->stream_end) return error(f, VORBIS_need_more_data); - // validate the page - if (memcmp(p, ogg_page_header, 4)) return error(f, VORBIS_invalid_stream); - if (p[4] != 0) return error(f, VORBIS_invalid_stream); - if (first) { // the first segment must NOT have 'continued_packet', later ones MUST - if (f->previous_length) - if ((p[5] & PAGEFLAG_continued_packet)) return error(f, VORBIS_invalid_stream); - // if no previous length, we're resynching, so we can come in on a continued-packet, - // which we'll just drop - } else { - if (!(p[5] & PAGEFLAG_continued_packet)) return error(f, VORBIS_invalid_stream); - } - n = p[26]; // segment counts - q = p+27; // q points to segment table - p = q + n; // advance past header - // make sure we've read the segment table - if (p > f->stream_end) return error(f, VORBIS_need_more_data); - for (s=0; s < n; ++s) { - p += q[s]; - if (q[s] < 255) - break; - } - if (end_page) - if (s < n-1) return error(f, VORBIS_invalid_stream); - if (s == n) - s = -1; // set 'crosses page' flag - if (p > f->stream_end) return error(f, VORBIS_need_more_data); - first = FALSE; - } - return TRUE; -} -#endif // !STB_VORBIS_NO_PUSHDATA_API - -static int start_decoder(vorb *f) -{ - uint8 header[6], x,y; - int len,i,j,k, max_submaps = 0; - int longest_floorlist=0; - - // first page, first packet - - if (!start_page(f)) return FALSE; - // validate page flag - if (!(f->page_flag & PAGEFLAG_first_page)) return error(f, VORBIS_invalid_first_page); - if (f->page_flag & PAGEFLAG_last_page) return error(f, VORBIS_invalid_first_page); - if (f->page_flag & PAGEFLAG_continued_packet) return error(f, VORBIS_invalid_first_page); - // check for expected packet length - if (f->segment_count != 1) return error(f, VORBIS_invalid_first_page); - if (f->segments[0] != 30) return error(f, VORBIS_invalid_first_page); - // read packet - // check packet header - if (get8(f) != VORBIS_packet_id) return error(f, VORBIS_invalid_first_page); - if (!getn(f, header, 6)) return error(f, VORBIS_unexpected_eof); - if (!vorbis_validate(header)) return error(f, VORBIS_invalid_first_page); - // vorbis_version - if (get32(f) != 0) return error(f, VORBIS_invalid_first_page); - f->channels = get8(f); if (!f->channels) return error(f, VORBIS_invalid_first_page); - if (f->channels > STB_VORBIS_MAX_CHANNELS) return error(f, VORBIS_too_many_channels); - f->sample_rate = get32(f); if (!f->sample_rate) return error(f, VORBIS_invalid_first_page); - get32(f); // bitrate_maximum - get32(f); // bitrate_nominal - get32(f); // bitrate_minimum - x = get8(f); - { - int log0,log1; - log0 = x & 15; - log1 = x >> 4; - f->blocksize_0 = 1 << log0; - f->blocksize_1 = 1 << log1; - if (log0 < 6 || log0 > 13) return error(f, VORBIS_invalid_setup); - if (log1 < 6 || log1 > 13) return error(f, VORBIS_invalid_setup); - if (log0 > log1) return error(f, VORBIS_invalid_setup); - } - - // framing_flag - x = get8(f); - if (!(x & 1)) return error(f, VORBIS_invalid_first_page); - - // second packet! - if (!start_page(f)) return FALSE; - - if (!start_packet(f)) return FALSE; - do { - len = next_segment(f); - skip(f, len); - f->bytes_in_seg = 0; - } while (len); - - // third packet! - if (!start_packet(f)) return FALSE; - - #ifndef STB_VORBIS_NO_PUSHDATA_API - if (IS_PUSH_MODE(f)) { - if (!is_whole_packet_present(f, TRUE)) { - // convert error in ogg header to write type - if (f->error == VORBIS_invalid_stream) - f->error = VORBIS_invalid_setup; - return FALSE; - } - } - #endif - - crc32_init(); // always init it, to avoid multithread race conditions - - if (get8_packet(f) != VORBIS_packet_setup) return error(f, VORBIS_invalid_setup); - for (i=0; i < 6; ++i) header[i] = get8_packet(f); - if (!vorbis_validate(header)) return error(f, VORBIS_invalid_setup); - - // codebooks - - f->codebook_count = get_bits(f,8) + 1; - f->codebooks = (Codebook *) setup_malloc(f, sizeof(*f->codebooks) * f->codebook_count); - if (f->codebooks == NULL) return error(f, VORBIS_outofmem); - memset(f->codebooks, 0, sizeof(*f->codebooks) * f->codebook_count); - for (i=0; i < f->codebook_count; ++i) { - uint32 *values; - int ordered, sorted_count; - int total=0; - uint8 *lengths; - Codebook *c = f->codebooks+i; - CHECK(f); - x = get_bits(f, 8); if (x != 0x42) return error(f, VORBIS_invalid_setup); - x = get_bits(f, 8); if (x != 0x43) return error(f, VORBIS_invalid_setup); - x = get_bits(f, 8); if (x != 0x56) return error(f, VORBIS_invalid_setup); - x = get_bits(f, 8); - c->dimensions = (get_bits(f, 8)<<8) + x; - x = get_bits(f, 8); - y = get_bits(f, 8); - c->entries = (get_bits(f, 8)<<16) + (y<<8) + x; - ordered = get_bits(f,1); - c->sparse = ordered ? 0 : get_bits(f,1); - - if (c->dimensions == 0 && c->entries != 0) return error(f, VORBIS_invalid_setup); - - if (c->sparse) - lengths = (uint8 *) setup_temp_malloc(f, c->entries); - else - lengths = c->codeword_lengths = (uint8 *) setup_malloc(f, c->entries); - - if (!lengths) return error(f, VORBIS_outofmem); - - if (ordered) { - int current_entry = 0; - int current_length = get_bits(f,5) + 1; - while (current_entry < c->entries) { - int limit = c->entries - current_entry; - int n = get_bits(f, ilog(limit)); - if (current_entry + n > (int) c->entries) { return error(f, VORBIS_invalid_setup); } - memset(lengths + current_entry, current_length, n); - current_entry += n; - ++current_length; - } - } else { - for (j=0; j < c->entries; ++j) { - int present = c->sparse ? get_bits(f,1) : 1; - if (present) { - lengths[j] = get_bits(f, 5) + 1; - ++total; - if (lengths[j] == 32) - return error(f, VORBIS_invalid_setup); - } else { - lengths[j] = NO_CODE; - } - } - } - - if (c->sparse && total >= c->entries >> 2) { - // convert sparse items to non-sparse! - if (c->entries > (int) f->setup_temp_memory_required) - f->setup_temp_memory_required = c->entries; - - c->codeword_lengths = (uint8 *) setup_malloc(f, c->entries); - if (c->codeword_lengths == NULL) return error(f, VORBIS_outofmem); - memcpy(c->codeword_lengths, lengths, c->entries); - setup_temp_free(f, lengths, c->entries); // note this is only safe if there have been no intervening temp mallocs! - lengths = c->codeword_lengths; - c->sparse = 0; - } - - // compute the size of the sorted tables - if (c->sparse) { - sorted_count = total; - } else { - sorted_count = 0; - #ifndef STB_VORBIS_NO_HUFFMAN_BINARY_SEARCH - for (j=0; j < c->entries; ++j) - if (lengths[j] > STB_VORBIS_FAST_HUFFMAN_LENGTH && lengths[j] != NO_CODE) - ++sorted_count; - #endif - } - - c->sorted_entries = sorted_count; - values = NULL; - - CHECK(f); - if (!c->sparse) { - c->codewords = (uint32 *) setup_malloc(f, sizeof(c->codewords[0]) * c->entries); - if (!c->codewords) return error(f, VORBIS_outofmem); - } else { - unsigned int size; - if (c->sorted_entries) { - c->codeword_lengths = (uint8 *) setup_malloc(f, c->sorted_entries); - if (!c->codeword_lengths) return error(f, VORBIS_outofmem); - c->codewords = (uint32 *) setup_temp_malloc(f, sizeof(*c->codewords) * c->sorted_entries); - if (!c->codewords) return error(f, VORBIS_outofmem); - values = (uint32 *) setup_temp_malloc(f, sizeof(*values) * c->sorted_entries); - if (!values) return error(f, VORBIS_outofmem); - } - size = c->entries + (sizeof(*c->codewords) + sizeof(*values)) * c->sorted_entries; - if (size > f->setup_temp_memory_required) - f->setup_temp_memory_required = size; - } - - if (!compute_codewords(c, lengths, c->entries, values)) { - if (c->sparse) setup_temp_free(f, values, 0); - return error(f, VORBIS_invalid_setup); - } - - if (c->sorted_entries) { - // allocate an extra slot for sentinels - c->sorted_codewords = (uint32 *) setup_malloc(f, sizeof(*c->sorted_codewords) * (c->sorted_entries+1)); - if (c->sorted_codewords == NULL) return error(f, VORBIS_outofmem); - // allocate an extra slot at the front so that c->sorted_values[-1] is defined - // so that we can catch that case without an extra if - c->sorted_values = ( int *) setup_malloc(f, sizeof(*c->sorted_values ) * (c->sorted_entries+1)); - if (c->sorted_values == NULL) return error(f, VORBIS_outofmem); - ++c->sorted_values; - c->sorted_values[-1] = -1; - compute_sorted_huffman(c, lengths, values); - } - - if (c->sparse) { - setup_temp_free(f, values, sizeof(*values)*c->sorted_entries); - setup_temp_free(f, c->codewords, sizeof(*c->codewords)*c->sorted_entries); - setup_temp_free(f, lengths, c->entries); - c->codewords = NULL; - } - - compute_accelerated_huffman(c); - - CHECK(f); - c->lookup_type = get_bits(f, 4); - if (c->lookup_type > 2) return error(f, VORBIS_invalid_setup); - if (c->lookup_type > 0) { - uint16 *mults; - c->minimum_value = float32_unpack(get_bits(f, 32)); - c->delta_value = float32_unpack(get_bits(f, 32)); - c->value_bits = get_bits(f, 4)+1; - c->sequence_p = get_bits(f,1); - if (c->lookup_type == 1) { - c->lookup_values = lookup1_values(c->entries, c->dimensions); - } else { - c->lookup_values = c->entries * c->dimensions; - } - if (c->lookup_values == 0) return error(f, VORBIS_invalid_setup); - mults = (uint16 *) setup_temp_malloc(f, sizeof(mults[0]) * c->lookup_values); - if (mults == NULL) return error(f, VORBIS_outofmem); - for (j=0; j < (int) c->lookup_values; ++j) { - int q = get_bits(f, c->value_bits); - if (q == EOP) { setup_temp_free(f,mults,sizeof(mults[0])*c->lookup_values); return error(f, VORBIS_invalid_setup); } - mults[j] = q; - } - -#ifndef STB_VORBIS_DIVIDES_IN_CODEBOOK - if (c->lookup_type == 1) { - int len, sparse = c->sparse; - float last=0; - // pre-expand the lookup1-style multiplicands, to avoid a divide in the inner loop - if (sparse) { - if (c->sorted_entries == 0) goto skip; - c->multiplicands = (codetype *) setup_malloc(f, sizeof(c->multiplicands[0]) * c->sorted_entries * c->dimensions); - } else - c->multiplicands = (codetype *) setup_malloc(f, sizeof(c->multiplicands[0]) * c->entries * c->dimensions); - if (c->multiplicands == NULL) { setup_temp_free(f,mults,sizeof(mults[0])*c->lookup_values); return error(f, VORBIS_outofmem); } - len = sparse ? c->sorted_entries : c->entries; - for (j=0; j < len; ++j) { - unsigned int z = sparse ? c->sorted_values[j] : j; - unsigned int div=1; - for (k=0; k < c->dimensions; ++k) { - int off = (z / div) % c->lookup_values; - float val = mults[off]; - val = mults[off]*c->delta_value + c->minimum_value + last; - c->multiplicands[j*c->dimensions + k] = val; - if (c->sequence_p) - last = val; - if (k+1 < c->dimensions) { - if (div > UINT_MAX / (unsigned int) c->lookup_values) { - setup_temp_free(f, mults,sizeof(mults[0])*c->lookup_values); - return error(f, VORBIS_invalid_setup); - } - div *= c->lookup_values; - } - } - } - c->lookup_type = 2; - } - else -#endif - { - float last=0; - CHECK(f); - c->multiplicands = (codetype *) setup_malloc(f, sizeof(c->multiplicands[0]) * c->lookup_values); - if (c->multiplicands == NULL) { setup_temp_free(f, mults,sizeof(mults[0])*c->lookup_values); return error(f, VORBIS_outofmem); } - for (j=0; j < (int) c->lookup_values; ++j) { - float val = mults[j] * c->delta_value + c->minimum_value + last; - c->multiplicands[j] = val; - if (c->sequence_p) - last = val; - } - } -#ifndef STB_VORBIS_DIVIDES_IN_CODEBOOK - skip:; -#endif - setup_temp_free(f, mults, sizeof(mults[0])*c->lookup_values); - - CHECK(f); - } - CHECK(f); - } - - // time domain transfers (notused) - - x = get_bits(f, 6) + 1; - for (i=0; i < x; ++i) { - uint32 z = get_bits(f, 16); - if (z != 0) return error(f, VORBIS_invalid_setup); - } - - // Floors - f->floor_count = get_bits(f, 6)+1; - f->floor_config = (Floor *) setup_malloc(f, f->floor_count * sizeof(*f->floor_config)); - if (f->floor_config == NULL) return error(f, VORBIS_outofmem); - for (i=0; i < f->floor_count; ++i) { - f->floor_types[i] = get_bits(f, 16); - if (f->floor_types[i] > 1) return error(f, VORBIS_invalid_setup); - if (f->floor_types[i] == 0) { - Floor0 *g = &f->floor_config[i].floor0; - g->order = get_bits(f,8); - g->rate = get_bits(f,16); - g->bark_map_size = get_bits(f,16); - g->amplitude_bits = get_bits(f,6); - g->amplitude_offset = get_bits(f,8); - g->number_of_books = get_bits(f,4) + 1; - for (j=0; j < g->number_of_books; ++j) - g->book_list[j] = get_bits(f,8); - return error(f, VORBIS_feature_not_supported); - } else { - Point p[31*8+2]; - Floor1 *g = &f->floor_config[i].floor1; - int max_class = -1; - g->partitions = get_bits(f, 5); - for (j=0; j < g->partitions; ++j) { - g->partition_class_list[j] = get_bits(f, 4); - if (g->partition_class_list[j] > max_class) - max_class = g->partition_class_list[j]; - } - for (j=0; j <= max_class; ++j) { - g->class_dimensions[j] = get_bits(f, 3)+1; - g->class_subclasses[j] = get_bits(f, 2); - if (g->class_subclasses[j]) { - g->class_masterbooks[j] = get_bits(f, 8); - if (g->class_masterbooks[j] >= f->codebook_count) return error(f, VORBIS_invalid_setup); - } - for (k=0; k < 1 << g->class_subclasses[j]; ++k) { - g->subclass_books[j][k] = get_bits(f,8)-1; - if (g->subclass_books[j][k] >= f->codebook_count) return error(f, VORBIS_invalid_setup); - } - } - g->floor1_multiplier = get_bits(f,2)+1; - g->rangebits = get_bits(f,4); - g->Xlist[0] = 0; - g->Xlist[1] = 1 << g->rangebits; - g->values = 2; - for (j=0; j < g->partitions; ++j) { - int c = g->partition_class_list[j]; - for (k=0; k < g->class_dimensions[c]; ++k) { - g->Xlist[g->values] = get_bits(f, g->rangebits); - ++g->values; - } - } - // precompute the sorting - for (j=0; j < g->values; ++j) { - p[j].x = g->Xlist[j]; - p[j].y = j; - } - qsort(p, g->values, sizeof(p[0]), point_compare); - for (j=0; j < g->values; ++j) - g->sorted_order[j] = (uint8) p[j].y; - // precompute the neighbors - for (j=2; j < g->values; ++j) { - int low,hi; - neighbors(g->Xlist, j, &low,&hi); - g->neighbors[j][0] = low; - g->neighbors[j][1] = hi; - } - - if (g->values > longest_floorlist) - longest_floorlist = g->values; - } - } - - // Residue - f->residue_count = get_bits(f, 6)+1; - f->residue_config = (Residue *) setup_malloc(f, f->residue_count * sizeof(f->residue_config[0])); - if (f->residue_config == NULL) return error(f, VORBIS_outofmem); - memset(f->residue_config, 0, f->residue_count * sizeof(f->residue_config[0])); - for (i=0; i < f->residue_count; ++i) { - uint8 residue_cascade[64]; - Residue *r = f->residue_config+i; - f->residue_types[i] = get_bits(f, 16); - if (f->residue_types[i] > 2) return error(f, VORBIS_invalid_setup); - r->begin = get_bits(f, 24); - r->end = get_bits(f, 24); - if (r->end < r->begin) return error(f, VORBIS_invalid_setup); - r->part_size = get_bits(f,24)+1; - r->classifications = get_bits(f,6)+1; - r->classbook = get_bits(f,8); - if (r->classbook >= f->codebook_count) return error(f, VORBIS_invalid_setup); - for (j=0; j < r->classifications; ++j) { - uint8 high_bits=0; - uint8 low_bits=get_bits(f,3); - if (get_bits(f,1)) - high_bits = get_bits(f,5); - residue_cascade[j] = high_bits*8 + low_bits; - } - r->residue_books = (short (*)[8]) setup_malloc(f, sizeof(r->residue_books[0]) * r->classifications); - if (r->residue_books == NULL) return error(f, VORBIS_outofmem); - for (j=0; j < r->classifications; ++j) { - for (k=0; k < 8; ++k) { - if (residue_cascade[j] & (1 << k)) { - r->residue_books[j][k] = get_bits(f, 8); - if (r->residue_books[j][k] >= f->codebook_count) return error(f, VORBIS_invalid_setup); - } else { - r->residue_books[j][k] = -1; - } - } - } - // precompute the classifications[] array to avoid inner-loop mod/divide - // call it 'classdata' since we already have r->classifications - r->classdata = (uint8 **) setup_malloc(f, sizeof(*r->classdata) * f->codebooks[r->classbook].entries); - if (!r->classdata) return error(f, VORBIS_outofmem); - memset(r->classdata, 0, sizeof(*r->classdata) * f->codebooks[r->classbook].entries); - for (j=0; j < f->codebooks[r->classbook].entries; ++j) { - int classwords = f->codebooks[r->classbook].dimensions; - int temp = j; - r->classdata[j] = (uint8 *) setup_malloc(f, sizeof(r->classdata[j][0]) * classwords); - if (r->classdata[j] == NULL) return error(f, VORBIS_outofmem); - for (k=classwords-1; k >= 0; --k) { - r->classdata[j][k] = temp % r->classifications; - temp /= r->classifications; - } - } - } - - f->mapping_count = get_bits(f,6)+1; - f->mapping = (Mapping *) setup_malloc(f, f->mapping_count * sizeof(*f->mapping)); - if (f->mapping == NULL) return error(f, VORBIS_outofmem); - memset(f->mapping, 0, f->mapping_count * sizeof(*f->mapping)); - for (i=0; i < f->mapping_count; ++i) { - Mapping *m = f->mapping + i; - int mapping_type = get_bits(f,16); - if (mapping_type != 0) return error(f, VORBIS_invalid_setup); - m->chan = (MappingChannel *) setup_malloc(f, f->channels * sizeof(*m->chan)); - if (m->chan == NULL) return error(f, VORBIS_outofmem); - if (get_bits(f,1)) - m->submaps = get_bits(f,4)+1; - else - m->submaps = 1; - if (m->submaps > max_submaps) - max_submaps = m->submaps; - if (get_bits(f,1)) { - m->coupling_steps = get_bits(f,8)+1; - for (k=0; k < m->coupling_steps; ++k) { - m->chan[k].magnitude = get_bits(f, ilog(f->channels-1)); - m->chan[k].angle = get_bits(f, ilog(f->channels-1)); - if (m->chan[k].magnitude >= f->channels) return error(f, VORBIS_invalid_setup); - if (m->chan[k].angle >= f->channels) return error(f, VORBIS_invalid_setup); - if (m->chan[k].magnitude == m->chan[k].angle) return error(f, VORBIS_invalid_setup); - } - } else - m->coupling_steps = 0; - - // reserved field - if (get_bits(f,2)) return error(f, VORBIS_invalid_setup); - if (m->submaps > 1) { - for (j=0; j < f->channels; ++j) { - m->chan[j].mux = get_bits(f, 4); - if (m->chan[j].mux >= m->submaps) return error(f, VORBIS_invalid_setup); - } - } else - // @SPECIFICATION: this case is missing from the spec - for (j=0; j < f->channels; ++j) - m->chan[j].mux = 0; - - for (j=0; j < m->submaps; ++j) { - get_bits(f,8); // discard - m->submap_floor[j] = get_bits(f,8); - m->submap_residue[j] = get_bits(f,8); - if (m->submap_floor[j] >= f->floor_count) return error(f, VORBIS_invalid_setup); - if (m->submap_residue[j] >= f->residue_count) return error(f, VORBIS_invalid_setup); - } - } - - // Modes - f->mode_count = get_bits(f, 6)+1; - for (i=0; i < f->mode_count; ++i) { - Mode *m = f->mode_config+i; - m->blockflag = get_bits(f,1); - m->windowtype = get_bits(f,16); - m->transformtype = get_bits(f,16); - m->mapping = get_bits(f,8); - if (m->windowtype != 0) return error(f, VORBIS_invalid_setup); - if (m->transformtype != 0) return error(f, VORBIS_invalid_setup); - if (m->mapping >= f->mapping_count) return error(f, VORBIS_invalid_setup); - } - - flush_packet(f); - - f->previous_length = 0; - - for (i=0; i < f->channels; ++i) { - f->channel_buffers[i] = (float *) setup_malloc(f, sizeof(float) * f->blocksize_1); - f->previous_window[i] = (float *) setup_malloc(f, sizeof(float) * f->blocksize_1/2); - f->finalY[i] = (int16 *) setup_malloc(f, sizeof(int16) * longest_floorlist); - if (f->channel_buffers[i] == NULL || f->previous_window[i] == NULL || f->finalY[i] == NULL) return error(f, VORBIS_outofmem); - #ifdef STB_VORBIS_NO_DEFER_FLOOR - f->floor_buffers[i] = (float *) setup_malloc(f, sizeof(float) * f->blocksize_1/2); - if (f->floor_buffers[i] == NULL) return error(f, VORBIS_outofmem); - #endif - } - - if (!init_blocksize(f, 0, f->blocksize_0)) return FALSE; - if (!init_blocksize(f, 1, f->blocksize_1)) return FALSE; - f->blocksize[0] = f->blocksize_0; - f->blocksize[1] = f->blocksize_1; - -#ifdef STB_VORBIS_DIVIDE_TABLE - if (integer_divide_table[1][1]==0) - for (i=0; i < DIVTAB_NUMER; ++i) - for (j=1; j < DIVTAB_DENOM; ++j) - integer_divide_table[i][j] = i / j; -#endif - - // compute how much temporary memory is needed - - // 1. - { - uint32 imdct_mem = (f->blocksize_1 * sizeof(float) >> 1); - uint32 classify_mem; - int i,max_part_read=0; - for (i=0; i < f->residue_count; ++i) { - Residue *r = f->residue_config + i; - int n_read = r->end - r->begin; - int part_read = n_read / r->part_size; - if (part_read > max_part_read) - max_part_read = part_read; - } - #ifndef STB_VORBIS_DIVIDES_IN_RESIDUE - classify_mem = f->channels * (sizeof(void*) + max_part_read * sizeof(uint8 *)); - #else - classify_mem = f->channels * (sizeof(void*) + max_part_read * sizeof(int *)); - #endif - - f->temp_memory_required = classify_mem; - if (imdct_mem > f->temp_memory_required) - f->temp_memory_required = imdct_mem; - } - - f->first_decode = TRUE; - - if (f->alloc.alloc_buffer) { - assert(f->temp_offset == f->alloc.alloc_buffer_length_in_bytes); - // check if there's enough temp memory so we don't error later - if (f->setup_offset + sizeof(*f) + f->temp_memory_required > (unsigned) f->temp_offset) - return error(f, VORBIS_outofmem); - } - - f->first_audio_page_offset = stb_vorbis_get_file_offset(f); - - return TRUE; -} - -static void vorbis_deinit(stb_vorbis *p) -{ - int i,j; - if (p->residue_config) { - for (i=0; i < p->residue_count; ++i) { - Residue *r = p->residue_config+i; - if (r->classdata) { - for (j=0; j < p->codebooks[r->classbook].entries; ++j) - setup_free(p, r->classdata[j]); - setup_free(p, r->classdata); - } - setup_free(p, r->residue_books); - } - } - - if (p->codebooks) { - CHECK(p); - for (i=0; i < p->codebook_count; ++i) { - Codebook *c = p->codebooks + i; - setup_free(p, c->codeword_lengths); - setup_free(p, c->multiplicands); - setup_free(p, c->codewords); - setup_free(p, c->sorted_codewords); - // c->sorted_values[-1] is the first entry in the array - setup_free(p, c->sorted_values ? c->sorted_values-1 : NULL); - } - setup_free(p, p->codebooks); - } - setup_free(p, p->floor_config); - setup_free(p, p->residue_config); - if (p->mapping) { - for (i=0; i < p->mapping_count; ++i) - setup_free(p, p->mapping[i].chan); - setup_free(p, p->mapping); - } - CHECK(p); - for (i=0; i < p->channels && i < STB_VORBIS_MAX_CHANNELS; ++i) { - setup_free(p, p->channel_buffers[i]); - setup_free(p, p->previous_window[i]); - #ifdef STB_VORBIS_NO_DEFER_FLOOR - setup_free(p, p->floor_buffers[i]); - #endif - setup_free(p, p->finalY[i]); - } - for (i=0; i < 2; ++i) { - setup_free(p, p->A[i]); - setup_free(p, p->B[i]); - setup_free(p, p->C[i]); - setup_free(p, p->window[i]); - setup_free(p, p->bit_reverse[i]); - } - #ifndef STB_VORBIS_NO_STDIO - if (p->close_on_free) fclose(p->f); - #endif -} - -void stb_vorbis_close(stb_vorbis *p) -{ - if (p == NULL) return; - vorbis_deinit(p); - setup_free(p,p); -} - -static void vorbis_init(stb_vorbis *p, const stb_vorbis_alloc *z) -{ - memset(p, 0, sizeof(*p)); // NULL out all malloc'd pointers to start - if (z) { - p->alloc = *z; - p->alloc.alloc_buffer_length_in_bytes = (p->alloc.alloc_buffer_length_in_bytes+3) & ~3; - p->temp_offset = p->alloc.alloc_buffer_length_in_bytes; - } - p->eof = 0; - p->error = VORBIS__no_error; - p->stream = NULL; - p->codebooks = NULL; - p->page_crc_tests = -1; - #ifndef STB_VORBIS_NO_STDIO - p->close_on_free = FALSE; - p->f = NULL; - #endif -} - -int stb_vorbis_get_sample_offset(stb_vorbis *f) -{ - if (f->current_loc_valid) - return f->current_loc; - else - return -1; -} - -stb_vorbis_info stb_vorbis_get_info(stb_vorbis *f) -{ - stb_vorbis_info d; - d.channels = f->channels; - d.sample_rate = f->sample_rate; - d.setup_memory_required = f->setup_memory_required; - d.setup_temp_memory_required = f->setup_temp_memory_required; - d.temp_memory_required = f->temp_memory_required; - d.max_frame_size = f->blocksize_1 >> 1; - return d; -} - -int stb_vorbis_get_error(stb_vorbis *f) -{ - int e = f->error; - f->error = VORBIS__no_error; - return e; -} - -static stb_vorbis * vorbis_alloc(stb_vorbis *f) -{ - stb_vorbis *p = (stb_vorbis *) setup_malloc(f, sizeof(*p)); - return p; -} - -#ifndef STB_VORBIS_NO_PUSHDATA_API - -void stb_vorbis_flush_pushdata(stb_vorbis *f) -{ - f->previous_length = 0; - f->page_crc_tests = 0; - f->discard_samples_deferred = 0; - f->current_loc_valid = FALSE; - f->first_decode = FALSE; - f->samples_output = 0; - f->channel_buffer_start = 0; - f->channel_buffer_end = 0; -} - -static int vorbis_search_for_page_pushdata(vorb *f, uint8 *data, int data_len) -{ - int i,n; - for (i=0; i < f->page_crc_tests; ++i) - f->scan[i].bytes_done = 0; - - // if we have room for more scans, search for them first, because - // they may cause us to stop early if their header is incomplete - if (f->page_crc_tests < STB_VORBIS_PUSHDATA_CRC_COUNT) { - if (data_len < 4) return 0; - data_len -= 3; // need to look for 4-byte sequence, so don't miss - // one that straddles a boundary - for (i=0; i < data_len; ++i) { - if (data[i] == 0x4f) { - if (0==memcmp(data+i, ogg_page_header, 4)) { - int j,len; - uint32 crc; - // make sure we have the whole page header - if (i+26 >= data_len || i+27+data[i+26] >= data_len) { - // only read up to this page start, so hopefully we'll - // have the whole page header start next time - data_len = i; - break; - } - // ok, we have it all; compute the length of the page - len = 27 + data[i+26]; - for (j=0; j < data[i+26]; ++j) - len += data[i+27+j]; - // scan everything up to the embedded crc (which we must 0) - crc = 0; - for (j=0; j < 22; ++j) - crc = crc32_update(crc, data[i+j]); - // now process 4 0-bytes - for ( ; j < 26; ++j) - crc = crc32_update(crc, 0); - // len is the total number of bytes we need to scan - n = f->page_crc_tests++; - f->scan[n].bytes_left = len-j; - f->scan[n].crc_so_far = crc; - f->scan[n].goal_crc = data[i+22] + (data[i+23] << 8) + (data[i+24]<<16) + (data[i+25]<<24); - // if the last frame on a page is continued to the next, then - // we can't recover the sample_loc immediately - if (data[i+27+data[i+26]-1] == 255) - f->scan[n].sample_loc = ~0; - else - f->scan[n].sample_loc = data[i+6] + (data[i+7] << 8) + (data[i+ 8]<<16) + (data[i+ 9]<<24); - f->scan[n].bytes_done = i+j; - if (f->page_crc_tests == STB_VORBIS_PUSHDATA_CRC_COUNT) - break; - // keep going if we still have room for more - } - } - } - } - - for (i=0; i < f->page_crc_tests;) { - uint32 crc; - int j; - int n = f->scan[i].bytes_done; - int m = f->scan[i].bytes_left; - if (m > data_len - n) m = data_len - n; - // m is the bytes to scan in the current chunk - crc = f->scan[i].crc_so_far; - for (j=0; j < m; ++j) - crc = crc32_update(crc, data[n+j]); - f->scan[i].bytes_left -= m; - f->scan[i].crc_so_far = crc; - if (f->scan[i].bytes_left == 0) { - // does it match? - if (f->scan[i].crc_so_far == f->scan[i].goal_crc) { - // Houston, we have page - data_len = n+m; // consumption amount is wherever that scan ended - f->page_crc_tests = -1; // drop out of page scan mode - f->previous_length = 0; // decode-but-don't-output one frame - f->next_seg = -1; // start a new page - f->current_loc = f->scan[i].sample_loc; // set the current sample location - // to the amount we'd have decoded had we decoded this page - f->current_loc_valid = f->current_loc != ~0U; - return data_len; - } - // delete entry - f->scan[i] = f->scan[--f->page_crc_tests]; - } else { - ++i; - } - } - - return data_len; -} - -// return value: number of bytes we used -int stb_vorbis_decode_frame_pushdata( - stb_vorbis *f, // the file we're decoding - const uint8 *data, int data_len, // the memory available for decoding - int *channels, // place to write number of float * buffers - float ***output, // place to write float ** array of float * buffers - int *samples // place to write number of output samples - ) -{ - int i; - int len,right,left; - - if (!IS_PUSH_MODE(f)) return error(f, VORBIS_invalid_api_mixing); - - if (f->page_crc_tests >= 0) { - *samples = 0; - return vorbis_search_for_page_pushdata(f, (uint8 *) data, data_len); - } - - f->stream = (uint8 *) data; - f->stream_end = (uint8 *) data + data_len; - f->error = VORBIS__no_error; - - // check that we have the entire packet in memory - if (!is_whole_packet_present(f, FALSE)) { - *samples = 0; - return 0; - } - - if (!vorbis_decode_packet(f, &len, &left, &right)) { - // save the actual error we encountered - enum STBVorbisError error = f->error; - if (error == VORBIS_bad_packet_type) { - // flush and resynch - f->error = VORBIS__no_error; - while (get8_packet(f) != EOP) - if (f->eof) break; - *samples = 0; - return (int) (f->stream - data); - } - if (error == VORBIS_continued_packet_flag_invalid) { - if (f->previous_length == 0) { - // we may be resynching, in which case it's ok to hit one - // of these; just discard the packet - f->error = VORBIS__no_error; - while (get8_packet(f) != EOP) - if (f->eof) break; - *samples = 0; - return (int) (f->stream - data); - } - } - // if we get an error while parsing, what to do? - // well, it DEFINITELY won't work to continue from where we are! - stb_vorbis_flush_pushdata(f); - // restore the error that actually made us bail - f->error = error; - *samples = 0; - return 1; - } - - // success! - len = vorbis_finish_frame(f, len, left, right); - for (i=0; i < f->channels; ++i) - f->outputs[i] = f->channel_buffers[i] + left; - - if (channels) *channels = f->channels; - *samples = len; - *output = f->outputs; - return (int) (f->stream - data); -} - -stb_vorbis *stb_vorbis_open_pushdata( - const unsigned char *data, int data_len, // the memory available for decoding - int *data_used, // only defined if result is not NULL - int *error, const stb_vorbis_alloc *alloc) -{ - stb_vorbis *f, p; - vorbis_init(&p, alloc); - p.stream = (uint8 *) data; - p.stream_end = (uint8 *) data + data_len; - p.push_mode = TRUE; - if (!start_decoder(&p)) { - if (p.eof) - *error = VORBIS_need_more_data; - else - *error = p.error; - return NULL; - } - f = vorbis_alloc(&p); - if (f) { - *f = p; - *data_used = (int) (f->stream - data); - *error = 0; - return f; - } else { - vorbis_deinit(&p); - return NULL; - } -} -#endif // STB_VORBIS_NO_PUSHDATA_API - -unsigned int stb_vorbis_get_file_offset(stb_vorbis *f) -{ - #ifndef STB_VORBIS_NO_PUSHDATA_API - if (f->push_mode) return 0; - #endif - if (USE_MEMORY(f)) return (unsigned int) (f->stream - f->stream_start); - #ifndef STB_VORBIS_NO_STDIO - return (unsigned int) (ftell(f->f) - f->f_start); - #endif -} - -#ifndef STB_VORBIS_NO_PULLDATA_API -// -// DATA-PULLING API -// - -static uint32 vorbis_find_page(stb_vorbis *f, uint32 *end, uint32 *last) -{ - for(;;) { - int n; - if (f->eof) return 0; - n = get8(f); - if (n == 0x4f) { // page header candidate - unsigned int retry_loc = stb_vorbis_get_file_offset(f); - int i; - // check if we're off the end of a file_section stream - if (retry_loc - 25 > f->stream_len) - return 0; - // check the rest of the header - for (i=1; i < 4; ++i) - if (get8(f) != ogg_page_header[i]) - break; - if (f->eof) return 0; - if (i == 4) { - uint8 header[27]; - uint32 i, crc, goal, len; - for (i=0; i < 4; ++i) - header[i] = ogg_page_header[i]; - for (; i < 27; ++i) - header[i] = get8(f); - if (f->eof) return 0; - if (header[4] != 0) goto invalid; - goal = header[22] + (header[23] << 8) + (header[24]<<16) + (header[25]<<24); - for (i=22; i < 26; ++i) - header[i] = 0; - crc = 0; - for (i=0; i < 27; ++i) - crc = crc32_update(crc, header[i]); - len = 0; - for (i=0; i < header[26]; ++i) { - int s = get8(f); - crc = crc32_update(crc, s); - len += s; - } - if (len && f->eof) return 0; - for (i=0; i < len; ++i) - crc = crc32_update(crc, get8(f)); - // finished parsing probable page - if (crc == goal) { - // we could now check that it's either got the last - // page flag set, OR it's followed by the capture - // pattern, but I guess TECHNICALLY you could have - // a file with garbage between each ogg page and recover - // from it automatically? So even though that paranoia - // might decrease the chance of an invalid decode by - // another 2^32, not worth it since it would hose those - // invalid-but-useful files? - if (end) - *end = stb_vorbis_get_file_offset(f); - if (last) { - if (header[5] & 0x04) - *last = 1; - else - *last = 0; - } - set_file_offset(f, retry_loc-1); - return 1; - } - } - invalid: - // not a valid page, so rewind and look for next one - set_file_offset(f, retry_loc); - } - } -} - - -#define SAMPLE_unknown 0xffffffff - -// seeking is implemented with a binary search, which narrows down the range to -// 64K, before using a linear search (because finding the synchronization -// pattern can be expensive, and the chance we'd find the end page again is -// relatively high for small ranges) -// -// two initial interpolation-style probes are used at the start of the search -// to try to bound either side of the binary search sensibly, while still -// working in O(log n) time if they fail. - -static int get_seek_page_info(stb_vorbis *f, ProbedPage *z) -{ - uint8 header[27], lacing[255]; - int i,len; - - // record where the page starts - z->page_start = stb_vorbis_get_file_offset(f); - - // parse the header - getn(f, header, 27); - if (header[0] != 'O' || header[1] != 'g' || header[2] != 'g' || header[3] != 'S') - return 0; - getn(f, lacing, header[26]); - - // determine the length of the payload - len = 0; - for (i=0; i < header[26]; ++i) - len += lacing[i]; - - // this implies where the page ends - z->page_end = z->page_start + 27 + header[26] + len; - - // read the last-decoded sample out of the data - z->last_decoded_sample = header[6] + (header[7] << 8) + (header[8] << 16) + (header[9] << 24); - - // restore file state to where we were - set_file_offset(f, z->page_start); - return 1; -} - -// rarely used function to seek back to the preceeding page while finding the -// start of a packet -static int go_to_page_before(stb_vorbis *f, unsigned int limit_offset) -{ - unsigned int previous_safe, end; - - // now we want to seek back 64K from the limit - if (limit_offset >= 65536 && limit_offset-65536 >= f->first_audio_page_offset) - previous_safe = limit_offset - 65536; - else - previous_safe = f->first_audio_page_offset; - - set_file_offset(f, previous_safe); - - while (vorbis_find_page(f, &end, NULL)) { - if (end >= limit_offset && stb_vorbis_get_file_offset(f) < limit_offset) - return 1; - set_file_offset(f, end); - } - - return 0; -} - -// implements the search logic for finding a page and starting decoding. if -// the function succeeds, current_loc_valid will be true and current_loc will -// be less than or equal to the provided sample number (the closer the -// better). -static int seek_to_sample_coarse(stb_vorbis *f, uint32 sample_number) -{ - ProbedPage left, right, mid; - int i, start_seg_with_known_loc, end_pos, page_start; - uint32 delta, stream_length, padding; - double offset, bytes_per_sample; - int probe = 0; - - // find the last page and validate the target sample - stream_length = stb_vorbis_stream_length_in_samples(f); - if (stream_length == 0) return error(f, VORBIS_seek_without_length); - if (sample_number > stream_length) return error(f, VORBIS_seek_invalid); - - // this is the maximum difference between the window-center (which is the - // actual granule position value), and the right-start (which the spec - // indicates should be the granule position (give or take one)). - padding = ((f->blocksize_1 - f->blocksize_0) >> 2); - if (sample_number < padding) - sample_number = 0; - else - sample_number -= padding; - - left = f->p_first; - while (left.last_decoded_sample == ~0U) { - // (untested) the first page does not have a 'last_decoded_sample' - set_file_offset(f, left.page_end); - if (!get_seek_page_info(f, &left)) goto error; - } - - right = f->p_last; - assert(right.last_decoded_sample != ~0U); - - // starting from the start is handled differently - if (sample_number <= left.last_decoded_sample) { - stb_vorbis_seek_start(f); - return 1; - } - - while (left.page_end != right.page_start) { - assert(left.page_end < right.page_start); - // search range in bytes - delta = right.page_start - left.page_end; - if (delta <= 65536) { - // there's only 64K left to search - handle it linearly - set_file_offset(f, left.page_end); - } else { - if (probe < 2) { - if (probe == 0) { - // first probe (interpolate) - double data_bytes = right.page_end - left.page_start; - bytes_per_sample = data_bytes / right.last_decoded_sample; - offset = left.page_start + bytes_per_sample * (sample_number - left.last_decoded_sample); - } else { - // second probe (try to bound the other side) - double error = ((double) sample_number - mid.last_decoded_sample) * bytes_per_sample; - if (error >= 0 && error < 8000) error = 8000; - if (error < 0 && error > -8000) error = -8000; - offset += error * 2; - } - - // ensure the offset is valid - if (offset < left.page_end) - offset = left.page_end; - if (offset > right.page_start - 65536) - offset = right.page_start - 65536; - - set_file_offset(f, (unsigned int) offset); - } else { - // binary search for large ranges (offset by 32K to ensure - // we don't hit the right page) - set_file_offset(f, left.page_end + (delta / 2) - 32768); - } - - if (!vorbis_find_page(f, NULL, NULL)) goto error; - } - - for (;;) { - if (!get_seek_page_info(f, &mid)) goto error; - if (mid.last_decoded_sample != ~0U) break; - // (untested) no frames end on this page - set_file_offset(f, mid.page_end); - assert(mid.page_start < right.page_start); - } - - // if we've just found the last page again then we're in a tricky file, - // and we're close enough. - if (mid.page_start == right.page_start) - break; - - if (sample_number < mid.last_decoded_sample) - right = mid; - else - left = mid; - - ++probe; - } - - // seek back to start of the last packet - page_start = left.page_start; - set_file_offset(f, page_start); - if (!start_page(f)) return error(f, VORBIS_seek_failed); - end_pos = f->end_seg_with_known_loc; - assert(end_pos >= 0); - - for (;;) { - for (i = end_pos; i > 0; --i) - if (f->segments[i-1] != 255) - break; - - start_seg_with_known_loc = i; - - if (start_seg_with_known_loc > 0 || !(f->page_flag & PAGEFLAG_continued_packet)) - break; - - // (untested) the final packet begins on an earlier page - if (!go_to_page_before(f, page_start)) - goto error; - - page_start = stb_vorbis_get_file_offset(f); - if (!start_page(f)) goto error; - end_pos = f->segment_count - 1; - } - - // prepare to start decoding - f->current_loc_valid = FALSE; - f->last_seg = FALSE; - f->valid_bits = 0; - f->packet_bytes = 0; - f->bytes_in_seg = 0; - f->previous_length = 0; - f->next_seg = start_seg_with_known_loc; - - for (i = 0; i < start_seg_with_known_loc; i++) - skip(f, f->segments[i]); - - // start decoding (optimizable - this frame is generally discarded) - vorbis_pump_first_frame(f); - return 1; - -error: - // try to restore the file to a valid state - stb_vorbis_seek_start(f); - return error(f, VORBIS_seek_failed); -} - -// the same as vorbis_decode_initial, but without advancing -static int peek_decode_initial(vorb *f, int *p_left_start, int *p_left_end, int *p_right_start, int *p_right_end, int *mode) -{ - int bits_read, bytes_read; - - if (!vorbis_decode_initial(f, p_left_start, p_left_end, p_right_start, p_right_end, mode)) - return 0; - - // either 1 or 2 bytes were read, figure out which so we can rewind - bits_read = 1 + ilog(f->mode_count-1); - if (f->mode_config[*mode].blockflag) - bits_read += 2; - bytes_read = (bits_read + 7) / 8; - - f->bytes_in_seg += bytes_read; - f->packet_bytes -= bytes_read; - skip(f, -bytes_read); - if (f->next_seg == -1) - f->next_seg = f->segment_count - 1; - else - f->next_seg--; - f->valid_bits = 0; - - return 1; -} - -int stb_vorbis_seek_frame(stb_vorbis *f, unsigned int sample_number) -{ - uint32 max_frame_samples; - - if (IS_PUSH_MODE(f)) return error(f, VORBIS_invalid_api_mixing); - - // fast page-level search - if (!seek_to_sample_coarse(f, sample_number)) - return 0; - - assert(f->current_loc_valid); - assert(f->current_loc <= sample_number); - - // linear search for the relevant packet - max_frame_samples = (f->blocksize_1*3 - f->blocksize_0) >> 2; - while (f->current_loc < sample_number) { - int left_start, left_end, right_start, right_end, mode, frame_samples; - if (!peek_decode_initial(f, &left_start, &left_end, &right_start, &right_end, &mode)) - return error(f, VORBIS_seek_failed); - // calculate the number of samples returned by the next frame - frame_samples = right_start - left_start; - if (f->current_loc + frame_samples > sample_number) { - return 1; // the next frame will contain the sample - } else if (f->current_loc + frame_samples + max_frame_samples > sample_number) { - // there's a chance the frame after this could contain the sample - vorbis_pump_first_frame(f); - } else { - // this frame is too early to be relevant - f->current_loc += frame_samples; - f->previous_length = 0; - maybe_start_packet(f); - flush_packet(f); - } - } - // the next frame will start with the sample - assert(f->current_loc == sample_number); - return 1; -} - -int stb_vorbis_seek(stb_vorbis *f, unsigned int sample_number) -{ - if (!stb_vorbis_seek_frame(f, sample_number)) - return 0; - - if (sample_number != f->current_loc) { - int n; - uint32 frame_start = f->current_loc; - stb_vorbis_get_frame_float(f, &n, NULL); - assert(sample_number > frame_start); - assert(f->channel_buffer_start + (int) (sample_number-frame_start) <= f->channel_buffer_end); - f->channel_buffer_start += (sample_number - frame_start); - } - - return 1; -} - -void stb_vorbis_seek_start(stb_vorbis *f) -{ - if (IS_PUSH_MODE(f)) { error(f, VORBIS_invalid_api_mixing); return; } - set_file_offset(f, f->first_audio_page_offset); - f->previous_length = 0; - f->first_decode = TRUE; - f->next_seg = -1; - vorbis_pump_first_frame(f); -} - -unsigned int stb_vorbis_stream_length_in_samples(stb_vorbis *f) -{ - unsigned int restore_offset, previous_safe; - unsigned int end, last_page_loc; - - if (IS_PUSH_MODE(f)) return error(f, VORBIS_invalid_api_mixing); - if (!f->total_samples) { - unsigned int last; - uint32 lo,hi; - char header[6]; - - // first, store the current decode position so we can restore it - restore_offset = stb_vorbis_get_file_offset(f); - - // now we want to seek back 64K from the end (the last page must - // be at most a little less than 64K, but let's allow a little slop) - if (f->stream_len >= 65536 && f->stream_len-65536 >= f->first_audio_page_offset) - previous_safe = f->stream_len - 65536; - else - previous_safe = f->first_audio_page_offset; - - set_file_offset(f, previous_safe); - // previous_safe is now our candidate 'earliest known place that seeking - // to will lead to the final page' - - if (!vorbis_find_page(f, &end, &last)) { - // if we can't find a page, we're hosed! - f->error = VORBIS_cant_find_last_page; - f->total_samples = 0xffffffff; - goto done; - } - - // check if there are more pages - last_page_loc = stb_vorbis_get_file_offset(f); - - // stop when the last_page flag is set, not when we reach eof; - // this allows us to stop short of a 'file_section' end without - // explicitly checking the length of the section - while (!last) { - set_file_offset(f, end); - if (!vorbis_find_page(f, &end, &last)) { - // the last page we found didn't have the 'last page' flag - // set. whoops! - break; - } - previous_safe = last_page_loc+1; - last_page_loc = stb_vorbis_get_file_offset(f); - } - - set_file_offset(f, last_page_loc); - - // parse the header - getn(f, (unsigned char *)header, 6); - // extract the absolute granule position - lo = get32(f); - hi = get32(f); - if (lo == 0xffffffff && hi == 0xffffffff) { - f->error = VORBIS_cant_find_last_page; - f->total_samples = SAMPLE_unknown; - goto done; - } - if (hi) - lo = 0xfffffffe; // saturate - f->total_samples = lo; - - f->p_last.page_start = last_page_loc; - f->p_last.page_end = end; - f->p_last.last_decoded_sample = lo; - - done: - set_file_offset(f, restore_offset); - } - return f->total_samples == SAMPLE_unknown ? 0 : f->total_samples; -} - -float stb_vorbis_stream_length_in_seconds(stb_vorbis *f) -{ - return stb_vorbis_stream_length_in_samples(f) / (float) f->sample_rate; -} - - - -int stb_vorbis_get_frame_float(stb_vorbis *f, int *channels, float ***output) -{ - int len, right,left,i; - if (IS_PUSH_MODE(f)) return error(f, VORBIS_invalid_api_mixing); - - if (!vorbis_decode_packet(f, &len, &left, &right)) { - f->channel_buffer_start = f->channel_buffer_end = 0; - return 0; - } - - len = vorbis_finish_frame(f, len, left, right); - for (i=0; i < f->channels; ++i) - f->outputs[i] = f->channel_buffers[i] + left; - - f->channel_buffer_start = left; - f->channel_buffer_end = left+len; - - if (channels) *channels = f->channels; - if (output) *output = f->outputs; - return len; -} - -#ifndef STB_VORBIS_NO_STDIO - -stb_vorbis * stb_vorbis_open_file_section(FILE *file, int close_on_free, int *error, const stb_vorbis_alloc *alloc, unsigned int length) -{ - stb_vorbis *f, p; - vorbis_init(&p, alloc); - p.f = file; - p.f_start = (uint32) ftell(file); - p.stream_len = length; - p.close_on_free = close_on_free; - if (start_decoder(&p)) { - f = vorbis_alloc(&p); - if (f) { - *f = p; - vorbis_pump_first_frame(f); - return f; - } - } - if (error) *error = p.error; - vorbis_deinit(&p); - return NULL; -} - -stb_vorbis * stb_vorbis_open_file(FILE *file, int close_on_free, int *error, const stb_vorbis_alloc *alloc) -{ - unsigned int len, start; - start = (unsigned int) ftell(file); - fseek(file, 0, SEEK_END); - len = (unsigned int) (ftell(file) - start); - fseek(file, start, SEEK_SET); - return stb_vorbis_open_file_section(file, close_on_free, error, alloc, len); -} - -stb_vorbis * stb_vorbis_open_filename(const char *filename, int *error, const stb_vorbis_alloc *alloc) -{ - FILE *f = fopen(filename, "rb"); - if (f) - return stb_vorbis_open_file(f, TRUE, error, alloc); - if (error) *error = VORBIS_file_open_failure; - return NULL; -} -#endif // STB_VORBIS_NO_STDIO - -stb_vorbis * stb_vorbis_open_memory(const unsigned char *data, int len, int *error, const stb_vorbis_alloc *alloc) -{ - stb_vorbis *f, p; - if (data == NULL) return NULL; - vorbis_init(&p, alloc); - p.stream = (uint8 *) data; - p.stream_end = (uint8 *) data + len; - p.stream_start = (uint8 *) p.stream; - p.stream_len = len; - p.push_mode = FALSE; - if (start_decoder(&p)) { - f = vorbis_alloc(&p); - if (f) { - *f = p; - vorbis_pump_first_frame(f); - return f; - } - } - if (error) *error = p.error; - vorbis_deinit(&p); - return NULL; -} - -#ifndef STB_VORBIS_NO_INTEGER_CONVERSION -#define PLAYBACK_MONO 1 -#define PLAYBACK_LEFT 2 -#define PLAYBACK_RIGHT 4 - -#define L (PLAYBACK_LEFT | PLAYBACK_MONO) -#define C (PLAYBACK_LEFT | PLAYBACK_RIGHT | PLAYBACK_MONO) -#define R (PLAYBACK_RIGHT | PLAYBACK_MONO) - -static int8 channel_position[7][6] = -{ - { 0 }, - { C }, - { L, R }, - { L, C, R }, - { L, R, L, R }, - { L, C, R, L, R }, - { L, C, R, L, R, C }, -}; - - -#ifndef STB_VORBIS_NO_FAST_SCALED_FLOAT - typedef union { - float f; - int i; - } float_conv; - typedef char stb_vorbis_float_size_test[sizeof(float)==4 && sizeof(int) == 4]; - #define FASTDEF(x) float_conv x - // add (1<<23) to convert to int, then divide by 2^SHIFT, then add 0.5/2^SHIFT to round - #define MAGIC(SHIFT) (1.5f * (1 << (23-SHIFT)) + 0.5f/(1 << SHIFT)) - #define ADDEND(SHIFT) (((150-SHIFT) << 23) + (1 << 22)) - #define FAST_SCALED_FLOAT_TO_INT(temp,x,s) (temp.f = (x) + MAGIC(s), temp.i - ADDEND(s)) - #define check_endianness() -#else - #define FAST_SCALED_FLOAT_TO_INT(temp,x,s) ((int) ((x) * (1 << (s)))) - #define check_endianness() - #define FASTDEF(x) -#endif - -static void copy_samples(short *dest, float *src, int len) -{ - int i; - check_endianness(); - for (i=0; i < len; ++i) { - FASTDEF(temp); - int v = FAST_SCALED_FLOAT_TO_INT(temp, src[i],15); - if ((unsigned int) (v + 32768) > 65535) - v = v < 0 ? -32768 : 32767; - dest[i] = v; - } -} - -static void compute_samples(int mask, short *output, int num_c, float **data, int d_offset, int len) -{ - #define BUFFER_SIZE 32 - float buffer[BUFFER_SIZE]; - int i,j,o,n = BUFFER_SIZE; - check_endianness(); - for (o = 0; o < len; o += BUFFER_SIZE) { - memset(buffer, 0, sizeof(buffer)); - if (o + n > len) n = len - o; - for (j=0; j < num_c; ++j) { - if (channel_position[num_c][j] & mask) { - for (i=0; i < n; ++i) - buffer[i] += data[j][d_offset+o+i]; - } - } - for (i=0; i < n; ++i) { - FASTDEF(temp); - int v = FAST_SCALED_FLOAT_TO_INT(temp,buffer[i],15); - if ((unsigned int) (v + 32768) > 65535) - v = v < 0 ? -32768 : 32767; - output[o+i] = v; - } - } -} - -static void compute_stereo_samples(short *output, int num_c, float **data, int d_offset, int len) -{ - #define BUFFER_SIZE 32 - float buffer[BUFFER_SIZE]; - int i,j,o,n = BUFFER_SIZE >> 1; - // o is the offset in the source data - check_endianness(); - for (o = 0; o < len; o += BUFFER_SIZE >> 1) { - // o2 is the offset in the output data - int o2 = o << 1; - memset(buffer, 0, sizeof(buffer)); - if (o + n > len) n = len - o; - for (j=0; j < num_c; ++j) { - int m = channel_position[num_c][j] & (PLAYBACK_LEFT | PLAYBACK_RIGHT); - if (m == (PLAYBACK_LEFT | PLAYBACK_RIGHT)) { - for (i=0; i < n; ++i) { - buffer[i*2+0] += data[j][d_offset+o+i]; - buffer[i*2+1] += data[j][d_offset+o+i]; - } - } else if (m == PLAYBACK_LEFT) { - for (i=0; i < n; ++i) { - buffer[i*2+0] += data[j][d_offset+o+i]; - } - } else if (m == PLAYBACK_RIGHT) { - for (i=0; i < n; ++i) { - buffer[i*2+1] += data[j][d_offset+o+i]; - } - } - } - for (i=0; i < (n<<1); ++i) { - FASTDEF(temp); - int v = FAST_SCALED_FLOAT_TO_INT(temp,buffer[i],15); - if ((unsigned int) (v + 32768) > 65535) - v = v < 0 ? -32768 : 32767; - output[o2+i] = v; - } - } -} - -static void convert_samples_short(int buf_c, short **buffer, int b_offset, int data_c, float **data, int d_offset, int samples) -{ - int i; - if (buf_c != data_c && buf_c <= 2 && data_c <= 6) { - static int channel_selector[3][2] = { {0}, {PLAYBACK_MONO}, {PLAYBACK_LEFT, PLAYBACK_RIGHT} }; - for (i=0; i < buf_c; ++i) - compute_samples(channel_selector[buf_c][i], buffer[i]+b_offset, data_c, data, d_offset, samples); - } else { - int limit = buf_c < data_c ? buf_c : data_c; - for (i=0; i < limit; ++i) - copy_samples(buffer[i]+b_offset, data[i]+d_offset, samples); - for ( ; i < buf_c; ++i) - memset(buffer[i]+b_offset, 0, sizeof(short) * samples); - } -} - -int stb_vorbis_get_frame_short(stb_vorbis *f, int num_c, short **buffer, int num_samples) -{ - float **output; - int len = stb_vorbis_get_frame_float(f, NULL, &output); - if (len > num_samples) len = num_samples; - if (len) - convert_samples_short(num_c, buffer, 0, f->channels, output, 0, len); - return len; -} - -static void convert_channels_short_interleaved(int buf_c, short *buffer, int data_c, float **data, int d_offset, int len) -{ - int i; - check_endianness(); - if (buf_c != data_c && buf_c <= 2 && data_c <= 6) { - assert(buf_c == 2); - for (i=0; i < buf_c; ++i) - compute_stereo_samples(buffer, data_c, data, d_offset, len); - } else { - int limit = buf_c < data_c ? buf_c : data_c; - int j; - for (j=0; j < len; ++j) { - for (i=0; i < limit; ++i) { - FASTDEF(temp); - float f = data[i][d_offset+j]; - int v = FAST_SCALED_FLOAT_TO_INT(temp, f,15);//data[i][d_offset+j],15); - if ((unsigned int) (v + 32768) > 65535) - v = v < 0 ? -32768 : 32767; - *buffer++ = v; - } - for ( ; i < buf_c; ++i) - *buffer++ = 0; - } - } -} - -int stb_vorbis_get_frame_short_interleaved(stb_vorbis *f, int num_c, short *buffer, int num_shorts) -{ - float **output; - int len; - if (num_c == 1) return stb_vorbis_get_frame_short(f,num_c,&buffer, num_shorts); - len = stb_vorbis_get_frame_float(f, NULL, &output); - if (len) { - if (len*num_c > num_shorts) len = num_shorts / num_c; - convert_channels_short_interleaved(num_c, buffer, f->channels, output, 0, len); - } - return len; -} - -int stb_vorbis_get_samples_short_interleaved(stb_vorbis *f, int channels, short *buffer, int num_shorts) -{ - float **outputs; - int len = num_shorts / channels; - int n=0; - int z = f->channels; - if (z > channels) z = channels; - while (n < len) { - int k = f->channel_buffer_end - f->channel_buffer_start; - if (n+k >= len) k = len - n; - if (k) - convert_channels_short_interleaved(channels, buffer, f->channels, f->channel_buffers, f->channel_buffer_start, k); - buffer += k*channels; - n += k; - f->channel_buffer_start += k; - if (n == len) break; - if (!stb_vorbis_get_frame_float(f, NULL, &outputs)) break; - } - return n; -} - -int stb_vorbis_get_samples_short(stb_vorbis *f, int channels, short **buffer, int len) -{ - float **outputs; - int n=0; - int z = f->channels; - if (z > channels) z = channels; - while (n < len) { - int k = f->channel_buffer_end - f->channel_buffer_start; - if (n+k >= len) k = len - n; - if (k) - convert_samples_short(channels, buffer, n, f->channels, f->channel_buffers, f->channel_buffer_start, k); - n += k; - f->channel_buffer_start += k; - if (n == len) break; - if (!stb_vorbis_get_frame_float(f, NULL, &outputs)) break; - } - return n; -} - -#ifndef STB_VORBIS_NO_STDIO -int stb_vorbis_decode_filename(const char *filename, int *channels, int *sample_rate, short **output) -{ - int data_len, offset, total, limit, error; - short *data; - stb_vorbis *v = stb_vorbis_open_filename(filename, &error, NULL); - if (v == NULL) return -1; - limit = v->channels * 4096; - *channels = v->channels; - if (sample_rate) - *sample_rate = v->sample_rate; - offset = data_len = 0; - total = limit; - data = (short *) malloc(total * sizeof(*data)); - if (data == NULL) { - stb_vorbis_close(v); - return -2; - } - for (;;) { - int n = stb_vorbis_get_frame_short_interleaved(v, v->channels, data+offset, total-offset); - if (n == 0) break; - data_len += n; - offset += n * v->channels; - if (offset + limit > total) { - short *data2; - total *= 2; - data2 = (short *) realloc(data, total * sizeof(*data)); - if (data2 == NULL) { - free(data); - stb_vorbis_close(v); - return -2; - } - data = data2; - } - } - *output = data; - stb_vorbis_close(v); - return data_len; -} -#endif // NO_STDIO - -int stb_vorbis_decode_memory(const uint8 *mem, int len, int *channels, int *sample_rate, short **output) -{ - int data_len, offset, total, limit, error; - short *data; - stb_vorbis *v = stb_vorbis_open_memory(mem, len, &error, NULL); - if (v == NULL) return -1; - limit = v->channels * 4096; - *channels = v->channels; - if (sample_rate) - *sample_rate = v->sample_rate; - offset = data_len = 0; - total = limit; - data = (short *) malloc(total * sizeof(*data)); - if (data == NULL) { - stb_vorbis_close(v); - return -2; - } - for (;;) { - int n = stb_vorbis_get_frame_short_interleaved(v, v->channels, data+offset, total-offset); - if (n == 0) break; - data_len += n; - offset += n * v->channels; - if (offset + limit > total) { - short *data2; - total *= 2; - data2 = (short *) realloc(data, total * sizeof(*data)); - if (data2 == NULL) { - free(data); - stb_vorbis_close(v); - return -2; - } - data = data2; - } - } - *output = data; - stb_vorbis_close(v); - return data_len; -} -#endif // STB_VORBIS_NO_INTEGER_CONVERSION - -int stb_vorbis_get_samples_float_interleaved(stb_vorbis *f, int channels, float *buffer, int num_floats) -{ - float **outputs; - int len = num_floats / channels; - int n=0; - int z = f->channels; - if (z > channels) z = channels; - while (n < len) { - int i,j; - int k = f->channel_buffer_end - f->channel_buffer_start; - if (n+k >= len) k = len - n; - for (j=0; j < k; ++j) { - for (i=0; i < z; ++i) - *buffer++ = f->channel_buffers[i][f->channel_buffer_start+j]; - for ( ; i < channels; ++i) - *buffer++ = 0; - } - n += k; - f->channel_buffer_start += k; - if (n == len) - break; - if (!stb_vorbis_get_frame_float(f, NULL, &outputs)) - break; - } - return n; -} - -int stb_vorbis_get_samples_float(stb_vorbis *f, int channels, float **buffer, int num_samples) -{ - float **outputs; - int n=0; - int z = f->channels; - if (z > channels) z = channels; - while (n < num_samples) { - int i; - int k = f->channel_buffer_end - f->channel_buffer_start; - if (n+k >= num_samples) k = num_samples - n; - if (k) { - for (i=0; i < z; ++i) - memcpy(buffer[i]+n, f->channel_buffers[i]+f->channel_buffer_start, sizeof(float)*k); - for ( ; i < channels; ++i) - memset(buffer[i]+n, 0, sizeof(float) * k); - } - n += k; - f->channel_buffer_start += k; - if (n == num_samples) - break; - if (!stb_vorbis_get_frame_float(f, NULL, &outputs)) - break; - } - return n; -} -#endif // STB_VORBIS_NO_PULLDATA_API - -/* Version history - 1.09 - 2016/04/04 - back out 'avoid discarding last frame' fix from previous version - 1.08 - 2016/04/02 - fixed multiple warnings; fix setup memory leaks; - avoid discarding last frame of audio data - 1.07 - 2015/01/16 - fixed some warnings, fix mingw, const-correct API - some more crash fixes when out of memory or with corrupt files - 1.06 - 2015/08/31 - full, correct support for seeking API (Dougall Johnson) - some crash fixes when out of memory or with corrupt files - 1.05 - 2015/04/19 - don't define __forceinline if it's redundant - 1.04 - 2014/08/27 - fix missing const-correct case in API - 1.03 - 2014/08/07 - Warning fixes - 1.02 - 2014/07/09 - Declare qsort compare function _cdecl on windows - 1.01 - 2014/06/18 - fix stb_vorbis_get_samples_float - 1.0 - 2014/05/26 - fix memory leaks; fix warnings; fix bugs in multichannel - (API change) report sample rate for decode-full-file funcs - 0.99996 - bracket #include for macintosh compilation by Laurent Gomila - 0.99995 - use union instead of pointer-cast for fast-float-to-int to avoid alias-optimization problem - 0.99994 - change fast-float-to-int to work in single-precision FPU mode, remove endian-dependence - 0.99993 - remove assert that fired on legal files with empty tables - 0.99992 - rewind-to-start - 0.99991 - bugfix to stb_vorbis_get_samples_short by Bernhard Wodo - 0.9999 - (should have been 0.99990) fix no-CRT support, compiling as C++ - 0.9998 - add a full-decode function with a memory source - 0.9997 - fix a bug in the read-from-FILE case in 0.9996 addition - 0.9996 - query length of vorbis stream in samples/seconds - 0.9995 - bugfix to another optimization that only happened in certain files - 0.9994 - bugfix to one of the optimizations that caused significant (but inaudible?) errors - 0.9993 - performance improvements; runs in 99% to 104% of time of reference implementation - 0.9992 - performance improvement of IMDCT; now performs close to reference implementation - 0.9991 - performance improvement of IMDCT - 0.999 - (should have been 0.9990) performance improvement of IMDCT - 0.998 - no-CRT support from Casey Muratori - 0.997 - bugfixes for bugs found by Terje Mathisen - 0.996 - bugfix: fast-huffman decode initialized incorrectly for sparse codebooks; fixing gives 10% speedup - found by Terje Mathisen - 0.995 - bugfix: fix to 'effective' overrun detection - found by Terje Mathisen - 0.994 - bugfix: garbage decode on final VQ symbol of a non-multiple - found by Terje Mathisen - 0.993 - bugfix: pushdata API required 1 extra byte for empty page (failed to consume final page if empty) - found by Terje Mathisen - 0.992 - fixes for MinGW warning - 0.991 - turn fast-float-conversion on by default - 0.990 - fix push-mode seek recovery if you seek into the headers - 0.98b - fix to bad release of 0.98 - 0.98 - fix push-mode seek recovery; robustify float-to-int and support non-fast mode - 0.97 - builds under c++ (typecasting, don't use 'class' keyword) - 0.96 - somehow MY 0.95 was right, but the web one was wrong, so here's my 0.95 rereleased as 0.96, fixes a typo in the clamping code - 0.95 - clamping code for 16-bit functions - 0.94 - not publically released - 0.93 - fixed all-zero-floor case (was decoding garbage) - 0.92 - fixed a memory leak - 0.91 - conditional compiles to omit parts of the API and the infrastructure to support them: STB_VORBIS_NO_PULLDATA_API, STB_VORBIS_NO_PUSHDATA_API, STB_VORBIS_NO_STDIO, STB_VORBIS_NO_INTEGER_CONVERSION - 0.90 - first public release -*/ - -#endif // STB_VORBIS_HEADER_ONLY diff --git a/impeller/third_party/stb/stb/stb_voxel_render.h b/impeller/third_party/stb/stb/stb_voxel_render.h deleted file mode 100644 index c49aa400aa12e..0000000000000 --- a/impeller/third_party/stb/stb/stb_voxel_render.h +++ /dev/null @@ -1,3752 +0,0 @@ -// stb_voxel_render.h - v0.84 - Sean Barrett, 2015 - public domain -// -// This library helps render large-scale "voxel" worlds for games, -// in this case, one with blocks that can have textures and that -// can also be a few shapes other than cubes. -// -// Video introduction: -// http://www.youtube.com/watch?v=2vnTtiLrV1w -// -// Minecraft-viewer sample app (not very simple though): -// http://github.com/nothings/stb/tree/master/tests/caveview -// -// It works by creating triangle meshes. The library includes -// -// - converter from dense 3D arrays of block info to vertex mesh -// - shader for the vertex mesh -// - assistance in setting up shader state -// -// For portability, none of the library code actually accesses -// the 3D graphics API. (At the moment, it's not actually portable -// since the shaders are GLSL only, but patches are welcome.) -// -// You have to do all the caching and tracking of vertex buffers -// yourself. However, you could also try making a game with -// a small enough world that it's fully loaded rather than -// streaming. Currently the preferred vertex format is 20 bytes -// per quad. There are plans to allow much more compact formats -// with a slight reduction in shader features. -// -// -// USAGE -// -// #define the symbol STB_VOXEL_RENDER_IMPLEMENTATION in *one* -// C/C++ file before the #include of this file; the implementation -// will be generated in that file. -// -// If you define the symbols STB_VOXEL_RENDER_STATIC, then the -// implementation will be private to that file. -// -// -// FEATURES -// -// - you can choose textured blocks with the features below, -// or colored voxels with 2^24 colors and no textures. -// -// - voxels are mostly just cubes, but there's support for -// half-height cubes and diagonal slopes, half-height -// diagonals, and even odder shapes especially for doing -// more-continuous "ground". -// -// - texture coordinates are projections along one of the major -// axes, with the per-texture scaling. -// -// - a number of aspects of the shader and the vertex format -// are configurable; the library generally takes care of -// coordinating the vertex format with the mesh for you. -// -// -// FEATURES (SHADER PERSPECTIVE) -// -// - vertices aligned on integer lattice, z on multiples of 0.5 -// - per-vertex "lighting" or "ambient occlusion" value (6 bits) -// - per-vertex texture crossfade (3 bits) -// -// - per-face texture #1 id (8-bit index into array texture) -// - per-face texture #2 id (8-bit index into second array texture) -// - per-face color (6-bit palette index, 2 bits of per-texture boolean enable) -// - per-face 5-bit normal for lighting calculations & texture coord computation -// - per-face 2-bit texture matrix rotation to rotate faces -// -// - indexed-by-texture-id scale factor (separate for texture #1 and texture #2) -// - indexed-by-texture-#2-id blend mode (alpha composite or modulate/multiply); -// the first is good for decals, the second for detail textures, "light maps", -// etc; both modes are controlled by texture #2's alpha, scaled by the -// per-vertex texture crossfade and the per-face color (if enabled on texture #2); -// modulate/multiply multiplies by an extra factor of 2.0 so that if you -// make detail maps whose average brightness is 0.5 everything works nicely. -// -// - ambient lighting: half-lambert directional plus constant, all scaled by vertex ao -// - face can be fullbright (emissive), controlled by per-face color -// - installable lighting, with default single-point-light -// - installable fog, with default hacked smoothstep -// -// Note that all the variations of lighting selection and texture -// blending are run-time conditions in the shader, so they can be -// intermixed in a single mesh. -// -// -// INTEGRATION ARC -// -// The way to get this library to work from scratch is to do the following: -// -// Step 1. define STBVOX_CONFIG_MODE to 0 -// -// This mode uses only vertex attributes and uniforms, and is easiest -// to get working. It requires 32 bytes per quad and limits the -// size of some tables to avoid hitting uniform limits. -// -// Step 2. define STBVOX_CONFIG_MODE to 1 -// -// This requires using a texture buffer to store the quad data, -// reducing the size to 20 bytes per quad. -// -// Step 3: define STBVOX_CONFIG_PREFER_TEXBUFFER -// -// This causes some uniforms to be stored as texture buffers -// instead. This increases the size of some of those tables, -// and avoids a potential slow path (gathering non-uniform -// data from uniforms) on some hardware. -// -// In the future I hope to add additional modes that have significantly -// smaller meshes but reduce features, down as small as 6 bytes per quad. -// See elsewhere in this file for a table of candidate modes. Switching -// to a mode will require changing some of your mesh creation code, but -// everything else should be seamless. (And I'd like to change the API -// so that mesh creation is data-driven the way the uniforms are, and -// then you wouldn't even have to change anything but the mode number.) -// -// -// IMPROVEMENTS FOR SHIP-WORTHY PROGRAMS USING THIS LIBRARY -// -// I currently tolerate a certain level of "bugginess" in this library. -// -// I'm referring to things which look a little wrong (as long as they -// don't cause holes or cracks in the output meshes), or things which -// do not produce as optimal a mesh as possible. Notable examples: -// -// - incorrect lighting on slopes -// - inefficient meshes for vheight blocks -// -// I am willing to do the work to improve these things if someone is -// going to ship a substantial program that would be improved by them. -// (It need not be commercial, nor need it be a game.) I just didn't -// want to do the work up front if it might never be leveraged. So just -// submit a bug report as usual (github is preferred), but add a note -// that this is for a thing that is really going to ship. (That means -// you need to be far enough into the project that it's clear you're -// committed to it; not during early exploratory development.) -// -// -// VOXEL MESH API -// -// Context -// -// To understand the API, make sure you first understand the feature set -// listed above. -// -// Because the vertices are compact, they have very limited spatial -// precision. Thus a single mesh can only contain the data for a limited -// area. To make very large voxel maps, you'll need to build multiple -// vertex buffers. (But you want this anyway for frustum culling.) -// -// Each generated mesh has three components: -// - vertex data (vertex buffer) -// - face data (optional, stored in texture buffer) -// - mesh transform (uniforms) -// -// Once you've generated the mesh with this library, it's up to you -// to upload it to the GPU, to keep track of the state, and to render -// it. -// -// Concept -// -// The basic design is that you pass in one or more 3D arrays; each array -// is (typically) one-byte-per-voxel and contains information about one -// or more properties of some particular voxel property. -// -// Because there is so much per-vertex and per-face data possible -// in the output, and each voxel can have 6 faces and 8 vertices, it -// would require an very large data structure to describe all -// of the possibilities, and this would cause the mesh-creation -// process to be slow. Instead, the API provides multiple ways -// to express each property, some more compact, others less so; -// each such way has some limitations on what it can express. -// -// Note that there are so many paths and combinations, not all of them -// have been tested. Just report bugs and I'll fix 'em. -// -// Details -// -// See the API documentation in the header-file section. -// -// -// CONTRIBUTORS -// -// Features Porting Bugfixes & Warnings -// Sean Barrett github:r-leyh Jesus Fernandez -// Miguel Lechon github:Arbeiterunfallversicherungsgesetz -// Thomas Frase James Hofmann -// Stephen Olsen -// -// VERSION HISTORY -// -// 0.84 (2016-04-02) fix GLSL syntax error on glModelView path -// 0.83 (2015-09-13) remove non-constant struct initializers to support more compilers -// 0.82 (2015-08-01) added input.packed_compact to store rot, vheight & texlerp efficiently -// fix broken tex_overlay2 -// 0.81 (2015-05-28) fix broken STBVOX_CONFIG_OPTIMIZED_VHEIGHT -// 0.80 (2015-04-11) fix broken STBVOX_CONFIG_ROTATION_IN_LIGHTING refactoring -// change STBVOX_MAKE_LIGHTING to STBVOX_MAKE_LIGHTING_EXT so -// that header defs don't need to see config vars -// add STBVOX_CONFIG_VHEIGHT_IN_LIGHTING and other vheight fixes -// added documentation for vheight ("weird slopes") -// 0.79 (2015-04-01) fix the missing types from 0.78; fix string constants being const -// 0.78 (2015-04-02) bad "#else", compile as C++ -// 0.77 (2015-04-01) documentation tweaks, rename config var to STB_VOXEL_RENDER_STATIC -// 0.76 (2015-04-01) typos, signed/unsigned shader issue, more documentation -// 0.75 (2015-04-01) initial release -// -// -// HISTORICAL FOUNDATION -// -// stb_voxel_render 20-byte quads 2015/01 -// zmc engine 32-byte quads 2013/12 -// zmc engine 96-byte quads 2011/10 -// -// -// LICENSE -// -// This software is dual-licensed to the public domain and under the following -// license: you are granted a perpetual, irrevocable license to copy, modify, -// publish, and distribute this file as you see fit. - -#ifndef INCLUDE_STB_VOXEL_RENDER_H -#define INCLUDE_STB_VOXEL_RENDER_H - -#include - -typedef struct stbvox_mesh_maker stbvox_mesh_maker; -typedef struct stbvox_input_description stbvox_input_description; - -#ifdef STB_VOXEL_RENDER_STATIC -#define STBVXDEC static -#else -#define STBVXDEC extern -#endif - -#ifdef __cplusplus -extern "C" { -#endif - -////////////////////////////////////////////////////////////////////////////// -// -// CONFIGURATION MACROS -// -// #define STBVOX_CONFIG_MODE // REQUIRED -// Configures the overall behavior of stb_voxel_render. This -// can affect the shaders, the uniform info, and other things. -// (If you need more than one mode in the same app, you can -// use STB_VOXEL_RENDER_STATIC to create multiple versions -// in separate files, and then wrap them.) -// -// Mode value Meaning -// 0 Textured blocks, 32-byte quads -// 1 Textured blocks, 20-byte quads -// 20 Untextured blocks, 32-byte quads -// 21 Untextured blocks, 20-byte quads -// -// -// #define STBVOX_CONFIG_PRECISION_Z // OPTIONAL -// Defines the number of bits of fractional position for Z. -// Only 0 or 1 are valid. 1 is the default. If 0, then a -// single mesh has twice the legal Z range; e.g. in -// modes 0,1,20,21, Z in the mesh can extend to 511 instead -// of 255. However, half-height blocks cannot be used. -// -// All of the following just #ifdef tested so need no values, and are optional. -// -// STBVOX_CONFIG_BLOCKTYPE_SHORT -// use unsigned 16-bit values for 'blocktype' in the input instead of 8-bit values -// -// STBVOX_CONFIG_OPENGL_MODELVIEW -// use the gl_ModelView matrix rather than the explicit uniform -// -// STBVOX_CONFIG_HLSL -// NOT IMPLEMENTED! Define HLSL shaders instead of GLSL shaders -// -// STBVOX_CONFIG_PREFER_TEXBUFFER -// Stores many of the uniform arrays in texture buffers intead, -// so they can be larger and may be more efficient on some hardware. -// -// STBVOX_CONFIG_LIGHTING_SIMPLE -// Creates a simple lighting engine with a single point light source -// in addition to the default half-lambert ambient light. -// -// STBVOX_CONFIG_LIGHTING -// Declares a lighting function hook; you must append a lighting function -// to the shader before compiling it: -// vec3 compute_lighting(vec3 pos, vec3 norm, vec3 albedo, vec3 ambient); -// 'ambient' is the half-lambert ambient light with vertex ambient-occlusion applied -// -// STBVOX_CONFIG_FOG_SMOOTHSTEP -// Defines a simple unrealistic fog system designed to maximize -// unobscured view distance while not looking too weird when things -// emerge from the fog. Configured using an extra array element -// in the STBVOX_UNIFORM_ambient uniform. -// -// STBVOX_CONFIG_FOG -// Defines a fog function hook; you must append a fog function to -// the shader before compiling it: -// vec3 compute_fog(vec3 color, vec3 relative_pos, float fragment_alpha); -// "color" is the incoming pre-fogged color, fragment_alpha is the alpha value, -// and relative_pos is the vector from the point to the camera in worldspace -// -// STBVOX_CONFIG_DISABLE_TEX2 -// This disables all processing of texture 2 in the shader in case -// you don't use it. Eventually this will be replaced with a mode -// that omits the unused data entirely. -// -// STBVOX_CONFIG_TEX1_EDGE_CLAMP -// STBVOX_CONFIG_TEX2_EDGE_CLAMP -// If you want to edge clamp the textures, instead of letting them wrap, -// set this flag. By default stb_voxel_render relies on texture wrapping -// to simplify texture coordinate generation. This flag forces it to do -// it correctly, although there can still be minor artifacts. -// -// STBVOX_CONFIG_ROTATION_IN_LIGHTING -// Changes the meaning of the 'lighting' mesher input variable to also -// store the rotation; see later discussion. -// -// STBVOX_CONFIG_VHEIGHT_IN_LIGHTING -// Changes the meaning of the 'lighting' mesher input variable to also -// store the vheight; see later discussion. Cannot use both this and -// the previous variable. -// -// STBVOX_CONFIG_PREMULTIPLIED_ALPHA -// Adjusts the shader calculations on the assumption that tex1.rgba, -// tex2.rgba, and color.rgba all use premultiplied values, and that -// the output of the fragment shader should be premultiplied. -// -// STBVOX_CONFIG_UNPREMULTIPLY -// Only meaningful if STBVOX_CONFIG_PREMULTIPLIED_ALPHA is defined. -// Changes the behavior described above so that the inputs are -// still premultiplied alpha, but the output of the fragment -// shader is not premultiplied alpha. This is needed when allowing -// non-unit alpha values but not doing alpha-blending (for example -// when alpha testing). -// - -////////////////////////////////////////////////////////////////////////////// -// -// MESHING -// -// A mesh represents a (typically) small chunk of a larger world. -// Meshes encode coordinates using small integers, so those -// coordinates must be relative to some base location. -// All of the coordinates in the functions below use -// these relative coordinates unless explicitly stated -// otherwise. -// -// Input to the meshing step is documented further down - -STBVXDEC void stbvox_init_mesh_maker(stbvox_mesh_maker *mm); -// Call this function to initialize a mesh-maker context structure -// used to build meshes. You should have one context per thread -// that's building meshes. - -STBVXDEC void stbvox_set_buffer(stbvox_mesh_maker *mm, int mesh, int slot, void *buffer, size_t len); -// Call this to set the buffer into which stbvox will write the mesh -// it creates. It can build more than one mesh in parallel (distinguished -// by the 'mesh' parameter), and each mesh can be made up of more than -// one buffer (distinguished by the 'slot' parameter). -// -// Multiple meshes are under your control; use the 'selector' input -// variable to choose which mesh each voxel's vertices are written to. -// For example, you can use this to generate separate meshes for opaque -// and transparent data. -// -// You can query the number of slots by calling stbvox_get_buffer_count -// described below. The meaning of the buffer for each slot depends -// on STBVOX_CONFIG_MODE. -// -// In mode 0 & mode 20, there is only one slot. The mesh data for that -// slot is two interleaved vertex attributes: attr_vertex, a single -// 32-bit uint, and attr_face, a single 32-bit uint. -// -// In mode 1 & mode 21, there are two slots. The first buffer should -// be four times as large as the second buffer. The first buffer -// contains a single vertex attribute: 'attr_vertex', a single 32-bit uint. -// The second buffer contains texture buffer data (an array of 32-bit uints) -// that will be accessed through the sampler identified by STBVOX_UNIFORM_face_data. - -STBVXDEC int stbvox_get_buffer_count(stbvox_mesh_maker *mm); -// Returns the number of buffers needed per mesh as described above. - -STBVXDEC int stbvox_get_buffer_size_per_quad(stbvox_mesh_maker *mm, int slot); -// Returns how much of a given buffer will get used per quad. This -// allows you to choose correct relative sizes for each buffer, although -// the values are fixed based on the configuration you've selected at -// compile time, and the details are described in stbvox_set_buffer. - -STBVXDEC void stbvox_set_default_mesh(stbvox_mesh_maker *mm, int mesh); -// Selects which mesh the mesher will output to (see previous function) -// if the input doesn't specify a per-voxel selector. (I doubt this is -// useful, but it's here just in case.) - -STBVXDEC stbvox_input_description *stbvox_get_input_description(stbvox_mesh_maker *mm); -// This function call returns a pointer to the stbvox_input_description part -// of stbvox_mesh_maker (which you should otherwise treat as opaque). You -// zero this structure, then fill out the relevant pointers to the data -// describing your voxel object/world. -// -// See further documentation at the description of stbvox_input_description below. - -STBVXDEC void stbvox_set_input_stride(stbvox_mesh_maker *mm, int x_stride_in_elements, int y_stride_in_elements); -// This sets the stride between successive elements of the 3D arrays -// in the stbvox_input_description. Z values are always stored consecutively. -// (The preferred coordinate system for stbvox is X right, Y forwards, Z up.) - -STBVXDEC void stbvox_set_input_range(stbvox_mesh_maker *mm, int x0, int y0, int z0, int x1, int y1, int z1); -// This sets the range of values in the 3D array for the voxels that -// the mesh generator will convert. The lower values are inclusive, -// the higher values are exclusive, so (0,0,0) to (16,16,16) generates -// mesh data associated with voxels up to (15,15,15) but no higher. -// -// The mesh generate generates faces at the boundary between open space -// and solid space but associates them with the solid space, so if (15,0,0) -// is open and (16,0,0) is solid, then the mesh will contain the boundary -// between them if x0 <= 16 and x1 > 16. -// -// Note that the mesh generator will access array elements 1 beyond the -// limits set in these parameters. For example, if you set the limits -// to be (0,0,0) and (16,16,16), then the generator will access all of -// the voxels between (-1,-1,-1) and (16,16,16), including (16,16,16). -// You may have to do pointer arithmetic to make it work. -// -// For example, caveview processes mesh chunks that are 32x32x16, but it -// does this using input buffers that are 34x34x18. -// -// The lower limits are x0 >= 0, y0 >= 0, and z0 >= 0. -// -// The upper limits are mode dependent, but all the current methods are -// limited to x1 < 127, y1 < 127, z1 < 255. Note that these are not -// powers of two; if you want to use power-of-two chunks (to make -// it efficient to decide which chunk a coordinate falls in), you're -// limited to at most x1=64, y1=64, z1=128. For classic Minecraft-style -// worlds with limited vertical extent, I recommend using a single -// chunk for the entire height, which limits the height to 255 blocks -// (one less than Minecraft), and only chunk the map in X & Y. - -STBVXDEC int stbvox_make_mesh(stbvox_mesh_maker *mm); -// Call this function to create mesh data for the currently configured -// set of input data. This appends to the currently configured mesh output -// buffer. Returns 1 on success. If there is not enough room in the buffer, -// it outputs as much as it can, and returns 0; you need to switch output -// buffers (either by calling stbvox_set_buffer to set new buffers, or -// by copying the data out and calling stbvox_reset_buffers), and then -// call this function again without changing any of the input parameters. -// -// Note that this function appends; you can call it multiple times to -// build a single mesh. For example, caveview uses chunks that are -// 32x32x255, but builds the mesh for it by processing 32x32x16 at atime -// (this is faster as it is reuses the same 34x34x18 input buffers rather -// than needing 34x34x257 input buffers). - -// Once you're done creating a mesh into a given buffer, -// consider the following functions: - -STBVXDEC int stbvox_get_quad_count(stbvox_mesh_maker *mm, int mesh); -// Returns the number of quads in the mesh currently generated by mm. -// This is the sum of all consecutive stbvox_make_mesh runs appending -// to the same buffer. 'mesh' distinguishes between the multiple user -// meshes available via 'selector' or stbvox_set_default_mesh. -// -// Typically you use this function when you're done building the mesh -// and want to record how to draw it. -// -// Note that there are no index buffers; the data stored in the buffers -// should be drawn as quads (e.g. with GL_QUAD); if your API does not -// support quads, you can create a single index buffer large enough to -// draw your largest vertex buffer, and reuse it for every rendering. -// (Note that if you use 32-bit indices, you'll use 24 bytes of bandwidth -// per quad, more than the 20 bytes for the vertex/face mesh data.) - -STBVXDEC void stbvox_set_mesh_coordinates(stbvox_mesh_maker *mm, int x, int y, int z); -// Sets the global coordinates for this chunk, such that (0,0,0) relative -// coordinates will be at (x,y,z) in global coordinates. - -STBVXDEC void stbvox_get_bounds(stbvox_mesh_maker *mm, float bounds[2][3]); -// Returns the bounds for the mesh in global coordinates. Use this -// for e.g. frustum culling the mesh. @BUG: this just uses the -// values from stbvox_set_input_range(), so if you build by -// appending multiple values, this will be wrong, and you need to -// set stbvox_set_input_range() to the full size. Someday this -// will switch to tracking the actual bounds of the *mesh*, though. - -STBVXDEC void stbvox_get_transform(stbvox_mesh_maker *mm, float transform[3][3]); -// Returns the 'transform' data for the shader uniforms. It is your -// job to set this to the shader before drawing the mesh. It is the -// only uniform that needs to change per-mesh. Note that it is not -// a 3x3 matrix, but rather a scale to decode fixed point numbers as -// floats, a translate from relative to global space, and a special -// translation for texture coordinate generation that avoids -// floating-point precision issues. @TODO: currently we add the -// global translation to the vertex, than multiply by modelview, -// but this means if camera location and vertex are far from the -// origin, we lose precision. Need to make a special modelview with -// the translation (or some of it) factored out to avoid this. - -STBVXDEC void stbvox_reset_buffers(stbvox_mesh_maker *mm); -// Call this function if you're done with the current output buffer -// but want to reuse it (e.g. you're done appending with -// stbvox_make_mesh and you've copied the data out to your graphics API -// so can reuse the buffer). - -////////////////////////////////////////////////////////////////////////////// -// -// RENDERING -// - -STBVXDEC char *stbvox_get_vertex_shader(void); -// Returns the (currently GLSL-only) vertex shader. - -STBVXDEC char *stbvox_get_fragment_shader(void); -// Returns the (currently GLSL-only) fragment shader. -// You can override the lighting and fogging calculations -// by appending data to the end of these; see the #define -// documentation for more information. - -STBVXDEC char *stbvox_get_fragment_shader_alpha_only(void); -// Returns a slightly cheaper fragment shader that computes -// alpha but not color. This is useful for e.g. a depth-only -// pass when using alpha test. - -typedef struct stbvox_uniform_info stbvox_uniform_info; - -STBVXDEC int stbvox_get_uniform_info(stbvox_uniform_info *info, int uniform); -// Gets the information about a uniform necessary for you to -// set up each uniform with a minimal amount of explicit code. -// See the sample code after the structure definition for stbvox_uniform_info, -// further down in this header section. -// -// "uniform" is from the list immediately following. For many -// of these, default values are provided which you can set. -// Most values are shared for most draw calls; e.g. for stateful -// APIs you can set most of the state only once. Only -// STBVOX_UNIFORM_transform needs to change per draw call. -// -// STBVOX_UNIFORM_texscale -// 64- or 128-long vec4 array. (128 only if STBVOX_CONFIG_PREFER_TEXBUFFER) -// x: scale factor to apply to texture #1. must be a power of two. 1.0 means 'face-sized' -// y: scale factor to apply to texture #2. must be a power of two. 1.0 means 'face-sized' -// z: blend mode indexed by texture #2. 0.0 is alpha compositing; 1.0 is multiplication. -// w: unused currently. @TODO use to support texture animation? -// -// Texscale is indexed by the bottom 6 or 7 bits of the texture id; thus for -// example the texture at index 0 in the array and the texture in index 128 of -// the array must be scaled the same. This means that if you only have 64 or 128 -// unique textures, they all get distinct values anyway; otherwise you have -// to group them in pairs or sets of four. -// -// STBVOX_UNIFORM_ambient -// 4-long vec4 array: -// ambient[0].xyz - negative of direction of a directional light for half-lambert -// ambient[1].rgb - color of light scaled by NdotL (can be negative) -// ambient[2].rgb - constant light added to above calculation; -// effectively light ranges from ambient[2]-ambient[1] to ambient[2]+ambient[1] -// ambient[3].rgb - fog color for STBVOX_CONFIG_FOG_SMOOTHSTEP -// ambient[3].a - reciprocal of squared distance of farthest fog point (viewing distance) - - - // +----- has a default value - // | +-- you should always use the default value -enum // V V -{ // ------------------------------------------------ - STBVOX_UNIFORM_face_data, // n the sampler with the face texture buffer - STBVOX_UNIFORM_transform, // n the transform data from stbvox_get_transform - STBVOX_UNIFORM_tex_array, // n an array of two texture samplers containing the two texture arrays - STBVOX_UNIFORM_texscale, // Y a table of texture properties, see above - STBVOX_UNIFORM_color_table, // Y 64 vec4 RGBA values; a default palette is provided; if A > 1.0, fullbright - STBVOX_UNIFORM_normals, // Y Y table of normals, internal-only - STBVOX_UNIFORM_texgen, // Y Y table of texgen vectors, internal-only - STBVOX_UNIFORM_ambient, // n lighting & fog info, see above - STBVOX_UNIFORM_camera_pos, // Y camera position in global voxel space (for lighting & fog) - - STBVOX_UNIFORM_count, -}; - -enum -{ - STBVOX_UNIFORM_TYPE_none, - STBVOX_UNIFORM_TYPE_sampler, - STBVOX_UNIFORM_TYPE_vec2, - STBVOX_UNIFORM_TYPE_vec3, - STBVOX_UNIFORM_TYPE_vec4, -}; - -struct stbvox_uniform_info -{ - int type; // which type of uniform - int bytes_per_element; // the size of each uniform array element (e.g. vec3 = 12 bytes) - int array_length; // length of the uniform array - char *name; // name in the shader @TODO use numeric binding - float *default_value; // if not NULL, you can use this as the uniform pointer - int use_tex_buffer; // if true, then the uniform is a sampler but the data can come from default_value -}; - -////////////////////////////////////////////////////////////////////////////// -// -// Uniform sample code -// - -#if 0 -// Run this once per frame before drawing all the meshes. -// You still need to separately set the 'transform' uniform for every mesh. -void setup_uniforms(GLuint shader, float camera_pos[4], GLuint tex1, GLuint tex2) -{ - int i; - glUseProgram(shader); // so uniform binding works - for (i=0; i < STBVOX_UNIFORM_count; ++i) { - stbvox_uniform_info sui; - if (stbvox_get_uniform_info(&sui, i)) { - GLint loc = glGetUniformLocation(shader, sui.name); - if (loc != 0) { - switch (i) { - case STBVOX_UNIFORM_camera_pos: // only needed for fog - glUniform4fv(loc, sui.array_length, camera_pos); - break; - - case STBVOX_UNIFORM_tex_array: { - GLuint tex_unit[2] = { 0, 1 }; // your choice of samplers - glUniform1iv(loc, 2, tex_unit); - - glActiveTexture(GL_TEXTURE0 + tex_unit[0]); glBindTexture(GL_TEXTURE_2D_ARRAY, tex1); - glActiveTexture(GL_TEXTURE0 + tex_unit[1]); glBindTexture(GL_TEXTURE_2D_ARRAY, tex2); - glActiveTexture(GL_TEXTURE0); // reset to default - break; - } - - case STBVOX_UNIFORM_face_data: - glUniform1i(loc, SAMPLER_YOU_WILL_BIND_PER_MESH_FACE_DATA_TO); - break; - - case STBVOX_UNIFORM_ambient: // you definitely want to override this - case STBVOX_UNIFORM_color_table: // you might want to override this - case STBVOX_UNIFORM_texscale: // you may want to override this - glUniform4fv(loc, sui.array_length, sui.default_value); - break; - - case STBVOX_UNIFORM_normals: // you never want to override this - case STBVOX_UNIFORM_texgen: // you never want to override this - glUniform3fv(loc, sui.array_length, sui.default_value); - break; - } - } - } - } -} -#endif - -#ifdef __cplusplus -} -#endif - -////////////////////////////////////////////////////////////////////////////// -// -// INPUT TO MESHING -// - -// Shapes of blocks that aren't always cubes -enum -{ - STBVOX_GEOM_empty, - STBVOX_GEOM_knockout, // creates a hole in the mesh - STBVOX_GEOM_solid, - STBVOX_GEOM_transp, // solid geometry, but transparent contents so neighbors generate normally, unless same blocktype - - // following 4 can be represented by vheight as well - STBVOX_GEOM_slab_upper, - STBVOX_GEOM_slab_lower, - STBVOX_GEOM_floor_slope_north_is_top, - STBVOX_GEOM_ceil_slope_north_is_bottom, - - STBVOX_GEOM_floor_slope_north_is_top_as_wall_UNIMPLEMENTED, // same as floor_slope above, but uses wall's texture & texture projection - STBVOX_GEOM_ceil_slope_north_is_bottom_as_wall_UNIMPLEMENTED, - STBVOX_GEOM_crossed_pair, // corner-to-corner pairs, with normal vector bumped upwards - STBVOX_GEOM_force, // like GEOM_transp, but faces visible even if neighbor is same type, e.g. minecraft fancy leaves - - // these access vheight input - STBVOX_GEOM_floor_vheight_03 = 12, // diagonal is SW-NE - STBVOX_GEOM_floor_vheight_12, // diagonal is SE-NW - STBVOX_GEOM_ceil_vheight_03, - STBVOX_GEOM_ceil_vheight_12, - - STBVOX_GEOM_count, // number of geom cases -}; - -enum -{ - STBVOX_FACE_east, - STBVOX_FACE_north, - STBVOX_FACE_west, - STBVOX_FACE_south, - STBVOX_FACE_up, - STBVOX_FACE_down, - - STBVOX_FACE_count, -}; - -#ifdef STBVOX_CONFIG_BLOCKTYPE_SHORT -typedef unsigned short stbvox_block_type; -#else -typedef unsigned char stbvox_block_type; -#endif - -// 24-bit color -typedef struct -{ - unsigned char r,g,b; -} stbvox_rgb; - -#define STBVOX_COLOR_TEX1_ENABLE 64 -#define STBVOX_COLOR_TEX2_ENABLE 128 - -// This is the data structure you fill out. Most of the arrays can be -// NULL, except when one is required to get the value to index another. -// -// The compass system used in the following descriptions is: -// east means increasing x -// north means increasing y -// up means increasing z -struct stbvox_input_description -{ - unsigned char lighting_at_vertices; - // The default is lighting values (i.e. ambient occlusion) are at block - // center, and the vertex light is gathered from those adjacent block - // centers that the vertex is facing. This makes smooth lighting - // consistent across adjacent faces with the same orientation. - // - // Setting this flag to non-zero gives you explicit control - // of light at each vertex, but now the lighting/ao will be - // shared by all vertices at the same point, even if they - // have different normals. - - // these are mostly 3D maps you use to define your voxel world, using x_stride and y_stride - // note that for cache efficiency, you want to use the block_foo palettes as much as possible instead - - stbvox_rgb *rgb; - // Indexed by 3D coordinate. - // 24-bit voxel color for STBVOX_CONFIG_MODE = 20 or 21 only - - unsigned char *lighting; - // Indexed by 3D coordinate. The lighting value / ambient occlusion - // value that is used to define the vertex lighting values. - // The raw lighting values are defined at the center of blocks - // (or at vertex if 'lighting_at_vertices' is true). - // - // If the macro STBVOX_CONFIG_ROTATION_IN_LIGHTING is defined, - // then an additional 2-bit block rotation value is stored - // in this field as well. - // - // Encode with STBVOX_MAKE_LIGHTING_EXT(lighting,rot)--here - // 'lighting' should still be 8 bits, as the macro will - // discard the bottom bits automatically. Similarly, if - // using STBVOX_CONFIG_VHEIGHT_IN_LIGHTING, encode with - // STBVOX_MAKE_LIGHTING_EXT(lighting,vheight). - // - // (Rationale: rotation needs to be independent of blocktype, - // but is only 2 bits so doesn't want to be its own array. - // Lighting is the one thing that was likely to already be - // in use and that I could easily steal 2 bits from.) - - stbvox_block_type *blocktype; - // Indexed by 3D coordinate. This is a core "block type" value, which is used - // to index into other arrays; essentially a "palette". This is much more - // memory-efficient and performance-friendly than storing the values explicitly, - // but only makes sense if the values are always synchronized. - // - // If a voxel's blocktype is 0, it is assumed to be empty (STBVOX_GEOM_empty), - // and no other blocktypes should be STBVOX_GEOM_empty. (Only if you do not - // have blocktypes should STBVOX_GEOM_empty ever used.) - // - // Normally it is an unsigned byte, but you can override it to be - // a short if you have too many blocktypes. - - unsigned char *geometry; - // Indexed by 3D coordinate. Contains the geometry type for the block. - // Also contains a 2-bit rotation for how the whole block is rotated. - // Also includes a 2-bit vheight value when using shared vheight values. - // See the separate vheight documentation. - // Encode with STBVOX_MAKE_GEOMETRY(geom, rot, vheight) - - unsigned char *block_geometry; - // Array indexed by blocktype containing the geometry for this block, plus - // a 2-bit "simple rotation". Note rotation has limited use since it's not - // independent of blocktype. - // - // Encode with STBVOX_MAKE_GEOMETRY(geom,simple_rot,0) - - unsigned char *block_tex1; - // Array indexed by blocktype containing the texture id for texture #1. - - unsigned char (*block_tex1_face)[6]; - // Array indexed by blocktype and face containing the texture id for texture #1. - // The N/E/S/W face choices can be rotated by one of the rotation selectors; - // The top & bottom face textures will rotate to match. - // Note that it only makes sense to use one of block_tex1 or block_tex1_face; - // this pattern repeats throughout and this notice is not repeated. - - unsigned char *tex2; - // Indexed by 3D coordinate. Contains the texture id for texture #2 - // to use on all faces of the block. - - unsigned char *block_tex2; - // Array indexed by blocktype containing the texture id for texture #2. - - unsigned char (*block_tex2_face)[6]; - // Array indexed by blocktype and face containing the texture id for texture #2. - // The N/E/S/W face choices can be rotated by one of the rotation selectors; - // The top & bottom face textures will rotate to match. - - unsigned char *color; - // Indexed by 3D coordinate. Contains the color for all faces of the block. - // The core color value is 0..63. - // Encode with STBVOX_MAKE_COLOR(color_number, tex1_enable, tex2_enable) - - unsigned char *block_color; - // Array indexed by blocktype containing the color value to apply to the faces. - // The core color value is 0..63. - // Encode with STBVOX_MAKE_COLOR(color_number, tex1_enable, tex2_enable) - - unsigned char (*block_color_face)[6]; - // Array indexed by blocktype and face containing the color value to apply to that face. - // The core color value is 0..63. - // Encode with STBVOX_MAKE_COLOR(color_number, tex1_enable, tex2_enable) - - unsigned char *block_texlerp; - // Array indexed by blocktype containing 3-bit scalar for texture #2 alpha - // (known throughout as 'texlerp'). This is constant over every face even - // though the property is potentially per-vertex. - - unsigned char (*block_texlerp_face)[6]; - // Array indexed by blocktype and face containing 3-bit scalar for texture #2 alpha. - // This is constant over the face even though the property is potentially per-vertex. - - unsigned char *block_vheight; - // Array indexed by blocktype containing the vheight values for the - // top or bottom face of this block. These will rotate properly if the - // block is rotated. See discussion of vheight. - // Encode with STBVOX_MAKE_VHEIGHT(sw_height, se_height, nw_height, ne_height) - - unsigned char *selector; - // Array indexed by 3D coordinates indicating which output mesh to select. - - unsigned char *block_selector; - // Array indexed by blocktype indicating which output mesh to select. - - unsigned char *side_texrot; - // Array indexed by 3D coordinates encoding 2-bit texture rotations for the - // faces on the E/N/W/S sides of the block. - // Encode with STBVOX_MAKE_SIDE_TEXROT(rot_e, rot_n, rot_w, rot_s) - - unsigned char *block_side_texrot; - // Array indexed by blocktype encoding 2-bit texture rotations for the faces - // on the E/N/W/S sides of the block. - // Encode with STBVOX_MAKE_SIDE_TEXROT(rot_e, rot_n, rot_w, rot_s) - - unsigned char *overlay; // index into palettes listed below - // Indexed by 3D coordinate. If 0, there is no overlay. If non-zero, - // it indexes into to the below arrays and overrides the values - // defined by the blocktype. - - unsigned char (*overlay_tex1)[6]; - // Array indexed by overlay value and face, containing an override value - // for the texture id for texture #1. If 0, the value defined by blocktype - // is used. - - unsigned char (*overlay_tex2)[6]; - // Array indexed by overlay value and face, containing an override value - // for the texture id for texture #2. If 0, the value defined by blocktype - // is used. - - unsigned char (*overlay_color)[6]; - // Array indexed by overlay value and face, containing an override value - // for the face color. If 0, the value defined by blocktype is used. - - unsigned char *overlay_side_texrot; - // Array indexed by overlay value, encoding 2-bit texture rotations for the faces - // on the E/N/W/S sides of the block. - // Encode with STBVOX_MAKE_SIDE_TEXROT(rot_e, rot_n, rot_w, rot_s) - - unsigned char *rotate; - // Indexed by 3D coordinate. Allows independent rotation of several - // parts of the voxel, where by rotation I mean swapping textures - // and colors between E/N/S/W faces. - // Block: rotates anything indexed by blocktype - // Overlay: rotates anything indexed by overlay - // EColor: rotates faces defined in ecolor_facemask - // Encode with STBVOX_MAKE_MATROT(block,overlay,ecolor) - - unsigned char *tex2_for_tex1; - // Array indexed by tex1 containing the texture id for texture #2. - // You can use this if the two are always/almost-always strictly - // correlated (e.g. if tex2 is a detail texture for tex1), as it - // will be more efficient (touching fewer cache lines) than using - // e.g. block_tex2_face. - - unsigned char *tex2_replace; - // Indexed by 3D coordinate. Specifies the texture id for texture #2 - // to use on a single face of the voxel, which must be E/N/W/S (not U/D). - // The texture id is limited to 6 bits unless tex2_facemask is also - // defined (see below). - // Encode with STBVOX_MAKE_TEX2_REPLACE(tex2, face) - - unsigned char *tex2_facemask; - // Indexed by 3D coordinate. Specifies which of the six faces should - // have their tex2 replaced by the value of tex2_replace. In this - // case, all 8 bits of tex2_replace are used as the texture id. - // Encode with STBVOX_MAKE_FACE_MASK(east,north,west,south,up,down) - - unsigned char *extended_color; - // Indexed by 3D coordinate. Specifies a value that indexes into - // the ecolor arrays below (both of which must be defined). - - unsigned char *ecolor_color; - // Indexed by extended_color value, specifies an optional override - // for the color value on some faces. - // Encode with STBVOX_MAKE_COLOR(color_number, tex1_enable, tex2_enable) - - unsigned char *ecolor_facemask; - // Indexed by extended_color value, this specifies which faces the - // color in ecolor_color should be applied to. The faces can be - // independently rotated by the ecolor value of 'rotate', if it exists. - // Encode with STBVOX_MAKE_FACE_MASK(e,n,w,s,u,d) - - unsigned char *color2; - // Indexed by 3D coordinates, specifies an alternative color to apply - // to some of the faces of the block. - // Encode with STBVOX_MAKE_COLOR(color_number, tex1_enable, tex2_enable) - - unsigned char *color2_facemask; - // Indexed by 3D coordinates, specifies which faces should use the - // color defined in color2. No rotation value is applied. - // Encode with STBVOX_MAKE_FACE_MASK(e,n,w,s,u,d) - - unsigned char *color3; - // Indexed by 3D coordinates, specifies an alternative color to apply - // to some of the faces of the block. - // Encode with STBVOX_MAKE_COLOR(color_number, tex1_enable, tex2_enable) - - unsigned char *color3_facemask; - // Indexed by 3D coordinates, specifies which faces should use the - // color defined in color3. No rotation value is applied. - // Encode with STBVOX_MAKE_FACE_MASK(e,n,w,s,u,d) - - unsigned char *texlerp_simple; - // Indexed by 3D coordinates, this is the smallest texlerp encoding - // that can do useful work. It consits of three values: baselerp, - // vertlerp, and face_vertlerp. Baselerp defines the value - // to use on all of the faces but one, from the STBVOX_TEXLERP_BASE - // values. face_vertlerp is one of the 6 face values (or STBVOX_FACE_NONE) - // which specifies the face should use the vertlerp values. - // Vertlerp defines a lerp value at every vertex of the mesh. - // Thus, one face can have per-vertex texlerp values, and those - // values are encoded in the space so that they will be shared - // by adjacent faces that also use vertlerp, allowing continuity - // (this is used for the "texture crossfade" bit of the release video). - // Encode with STBVOX_MAKE_TEXLERP_SIMPLE(baselerp, vertlerp, face_vertlerp) - - // The following texlerp encodings are experimental and maybe not - // that useful. - - unsigned char *texlerp; - // Indexed by 3D coordinates, this defines four values: - // vertlerp is a lerp value at every vertex of the mesh (using STBVOX_TEXLERP_BASE values). - // ud is the value to use on up and down faces, from STBVOX_TEXLERP_FACE values - // ew is the value to use on east and west faces, from STBVOX_TEXLERP_FACE values - // ns is the value to use on north and south faces, from STBVOX_TEXLERP_FACE values - // If any of ud, ew, or ns is STBVOX_TEXLERP_FACE_use_vert, then the - // vertlerp values for the vertices are gathered and used for those faces. - // Encode with STBVOX_MAKE_TEXLERP(vertlerp,ud,ew,sw) - - unsigned short *texlerp_vert3; - // Indexed by 3D coordinates, this works with texlerp and - // provides a unique texlerp value for every direction at - // every vertex. The same rules of whether faces share values - // applies. The STBVOX_TEXLERP_FACE vertlerp value defined in - // texlerp is only used for the down direction. The values at - // each vertex in other directions are defined in this array, - // and each uses the STBVOX_TEXLERP3 values (i.e. full precision - // 3-bit texlerp values). - // Encode with STBVOX_MAKE_VERT3(vertlerp_e,vertlerp_n,vertlerp_w,vertlerp_s,vertlerp_u) - - unsigned short *texlerp_face3; // e:3,n:3,w:3,s:3,u:2,d:2 - // Indexed by 3D coordinates, this provides a compact way to - // fully specify the texlerp value indepenendly for every face, - // but doesn't allow per-vertex variation. E/N/W/S values are - // encoded using STBVOX_TEXLERP3 values, whereas up and down - // use STBVOX_TEXLERP_SIMPLE values. - // Encode with STBVOX_MAKE_FACE3(face_e,face_n,face_w,face_s,face_u,face_d) - - unsigned char *vheight; // STBVOX_MAKE_VHEIGHT -- sw:2, se:2, nw:2, ne:2, doesn't rotate - // Indexed by 3D coordinates, this defines the four - // vheight values to use if the geometry is STBVOX_GEOM_vheight*. - // See the vheight discussion. - - unsigned char *packed_compact; - // Stores block rotation, vheight, and texlerp values: - // block rotation: 2 bits - // vertex vheight: 2 bits - // use_texlerp : 1 bit - // vertex texlerp: 3 bits - // If STBVOX_CONFIG_UP_TEXLERP_PACKED is defined, then 'vertex texlerp' is - // used for up faces if use_texlerp is 1. If STBVOX_CONFIG_DOWN_TEXLERP_PACKED - // is defined, then 'vertex texlerp' is used for down faces if use_texlerp is 1. - // Note if those symbols are defined but packed_compact is NULL, the normal - // texlerp default will be used. - // Encode with STBVOX_MAKE_PACKED_COMPACT(rot, vheight, texlerp, use_texlerp) -}; -// @OPTIMIZE allow specializing; build a single struct with all of the -// 3D-indexed arrays combined so it's AoS instead of SoA for better -// cache efficiency - - -////////////////////////////////////////////////////////////////////////////// -// -// VHEIGHT DOCUMENTATION -// -// "vheight" is the internal name for the special block types -// with sloped tops or bottoms. "vheight" stands for "vertex height". -// -// Note that these blocks are very flexible (there are 256 of them, -// although at least 17 of them should never be used), but they -// also have a disadvantage that they generate extra invisible -// faces; the generator does not currently detect whether adjacent -// vheight blocks hide each others sides, so those side faces are -// always generated. For a continuous ground terrain, this means -// that you may generate 5x as many quads as needed. See notes -// on "improvements for shipping products" in the introduction. - -enum -{ - STBVOX_VERTEX_HEIGHT_0, - STBVOX_VERTEX_HEIGHT_half, - STBVOX_VERTEX_HEIGHT_1, - STBVOX_VERTEX_HEIGHT_one_and_a_half, -}; -// These are the "vheight" values. Vheight stands for "vertex height". -// The idea is that for a "floor vheight" block, you take a cube and -// reposition the top-most vertices at various heights as specified by -// the vheight values. Similarly, a "ceiling vheight" block takes a -// cube and repositions the bottom-most vertices. -// -// A floor block only adjusts the top four vertices; the bottom four vertices -// remain at the bottom of the block. The height values are 2 bits, -// measured in halves of a block; so you can specify heights of 0/2, -// 1/2, 2/2, or 3/2. 0 is the bottom of the block, 1 is halfway -// up the block, 2 is the top of the block, and 3 is halfway up the -// next block (and actually outside of the block). The value 3 is -// actually legal for floor vheight (but not ceiling), and allows you to: -// -// (A) have smoother terrain by having slopes that cross blocks, -// e.g. (1,1,3,3) is a regular-seeming slope halfway between blocks -// (B) make slopes steeper than 45-degrees, e.g. (0,0,3,3) -// -// (Because only z coordinates have half-block precision, and x&y are -// limited to block corner precision, it's not possible to make these -// things "properly" out of blocks, e.g. a half-slope block on its side -// or a sloped block halfway between blocks that's made out of two blocks.) -// -// If you define STBVOX_CONFIG_OPTIMIZED_VHEIGHT, then the top face -// (or bottom face for a ceiling vheight block) will be drawn as a -// single quad even if the four vertex heights aren't planar, and a -// single normal will be used over the entire quad. If you -// don't define it, then if the top face is non-planar, it will be -// split into two triangles, each with their own normal/lighting. -// (Note that since all output from stb_voxel_render is quad meshes, -// triangles are actually rendered as degenerate quads.) In this case, -// the distinction betwen STBVOX_GEOM_floor_vheight_03 and -// STBVOX_GEOM_floor_vheight_12 comes into play; the former introduces -// an edge from the SW to NE corner (i.e. from <0,0,?> to <1,1,?>), -// while the latter introduces an edge from the NW to SE corner -// (i.e. from <0,1,?> to <1,0,?>.) For a "lazy mesh" look, use -// exclusively _03 or _12. For a "classic mesh" look, alternate -// _03 and _12 in a checkerboard pattern. For a "smoothest surface" -// look, choose the edge based on actual vertex heights. -// -// The four vertex heights can come from several places. The simplest -// encoding is to just use the 'vheight' parameter which stores four -// explicit vertex heights for every block. This allows total independence, -// but at the cost of the largest memory usage, 1 byte per 3D block. -// Encode this with STBVOX_MAKE_VHEIGHT(vh_sw, vh_se, vh_nw, vh_ne). -// These coordinates are absolute, not affected by block rotations. -// -// An alternative if you just want to encode some very specific block -// types, not all the possibilities--say you just want half-height slopes, -// so you want (0,0,1,1) and (1,1,2,2)--then you can use block_vheight -// to specify them. The geometry rotation will cause block_vheight values -// to be rotated (because it's as if you're just defining a type of -// block). This value is also encoded with STBVOX_MAKE_VHEIGHT. -// -// If you want to save memory and you're creating a "continuous ground" -// sort of effect, you can make each vertex of the lattice share the -// vheight value; that is, two adjacent blocks that share a vertex will -// always get the same vheight value for that vertex. Then you need to -// store two bits of vheight for every block, which you do by storing it -// as part another data structure. Store the south-west vertex's vheight -// with the block. You can either use the "geometry" mesh variable (it's -// a parameter to STBVOX_MAKE_GEOMETRY) or you can store it in the -// "lighting" mesh variable if you defined STBVOX_CONFIG_VHEIGHT_IN_LIGHTING, -// using STBVOX_MAKE_LIGHTING_EXT(lighting,vheight). -// -// Note that if you start with a 2D height map and generate vheight data from -// it, you don't necessarily store only one value per (x,y) coordinate, -// as the same value may need to be set up at multiple z heights. For -// example, if height(8,8) = 13.5, then you want the block at (8,8,13) -// to store STBVOX_VERTEX_HEIGHT_half, and this will be used by blocks -// at (7,7,13), (8,7,13), (7,8,13), and (8,8,13). However, if you're -// allowing steep slopes, it might be the case that you have a block -// at (7,7,12) which is supposed to stick up to 13.5; that means -// you also need to store STBVOX_VERTEX_HEIGHT_one_and_a_half at (8,8,12). - -enum -{ - STBVOX_TEXLERP_FACE_0, - STBVOX_TEXLERP_FACE_half, - STBVOX_TEXLERP_FACE_1, - STBVOX_TEXLERP_FACE_use_vert, -}; - -enum -{ - STBVOX_TEXLERP_BASE_0, // 0.0 - STBVOX_TEXLERP_BASE_2_7, // 2/7 - STBVOX_TEXLERP_BASE_5_7, // 4/7 - STBVOX_TEXLERP_BASE_1 // 1.0 -}; - -enum -{ - STBVOX_TEXLERP3_0_8, - STBVOX_TEXLERP3_1_8, - STBVOX_TEXLERP3_2_8, - STBVOX_TEXLERP3_3_8, - STBVOX_TEXLERP3_4_8, - STBVOX_TEXLERP3_5_8, - STBVOX_TEXLERP3_6_8, - STBVOX_TEXLERP3_7_8, -}; - -#define STBVOX_FACE_NONE 7 - -#define STBVOX_BLOCKTYPE_EMPTY 0 - -#ifdef STBVOX_BLOCKTYPE_SHORT -#define STBVOX_BLOCKTYPE_HOLE 65535 -#else -#define STBVOX_BLOCKTYPE_HOLE 255 -#endif - -#define STBVOX_MAKE_GEOMETRY(geom, rotate, vheight) ((geom) + (rotate)*16 + (vheight)*64) -#define STBVOX_MAKE_VHEIGHT(v_sw, v_se, v_nw, v_ne) ((v_sw) + (v_se)*4 + (v_nw)*16 + (v_ne)*64) -#define STBVOX_MAKE_MATROT(block, overlay, color) ((block) + (overlay)*4 + (color)*64) -#define STBVOX_MAKE_TEX2_REPLACE(tex2, tex2_replace_face) ((tex2) + ((tex2_replace_face) & 3)*64) -#define STBVOX_MAKE_TEXLERP(ns2, ew2, ud2, vert) ((ew2) + (ns2)*4 + (ud2)*16 + (vert)*64) -#define STBVOX_MAKE_TEXLERP_SIMPLE(baselerp,vert,face) ((vert)*32 + (face)*4 + (baselerp)) -#define STBVOX_MAKE_TEXLERP1(vert,e2,n2,w2,s2,u4,d2) STBVOX_MAKE_TEXLERP(s2, w2, d2, vert) -#define STBVOX_MAKE_TEXLERP2(vert,e2,n2,w2,s2,u4,d2) ((u2)*16 + (n2)*4 + (s2)) -#define STBVOX_MAKE_FACE_MASK(e,n,w,s,u,d) ((e)+(n)*2+(w)*4+(s)*8+(u)*16+(d)*32) -#define STBVOX_MAKE_SIDE_TEXROT(e,n,w,s) ((e)+(n)*4+(w)*16+(s)*64) -#define STBVOX_MAKE_COLOR(color,t1,t2) ((color)+(t1)*64+(t2)*128) -#define STBVOX_MAKE_TEXLERP_VERT3(e,n,w,s,u) ((e)+(n)*8+(w)*64+(s)*512+(u)*4096) -#define STBVOX_MAKE_TEXLERP_FACE3(e,n,w,s,u,d) ((e)+(n)*8+(w)*64+(s)*512+(u)*4096+(d)*16384) -#define STBVOX_MAKE_PACKED_COMPACT(rot, vheight, texlerp, def) ((rot)+4*(vheight)+16*(use)+32*(texlerp)) - -#define STBVOX_MAKE_LIGHTING_EXT(lighting, rot) (((lighting)&~3)+(rot)) -#define STBVOX_MAKE_LIGHTING(lighting) (lighting) - -#ifndef STBVOX_MAX_MESHES -#define STBVOX_MAX_MESHES 2 // opaque & transparent -#endif - -#define STBVOX_MAX_MESH_SLOTS 3 // one vertex & two faces, or two vertex and one face - - -// don't mess with this directly, it's just here so you can -// declare stbvox_mesh_maker on the stack or as a global -struct stbvox_mesh_maker -{ - stbvox_input_description input; - int cur_x, cur_y, cur_z; // last unprocessed voxel if it splits into multiple buffers - int x0,y0,z0,x1,y1,z1; - int x_stride_in_bytes; - int y_stride_in_bytes; - int config_dirty; - int default_mesh; - unsigned int tags; - - int cube_vertex_offset[6][4]; // this allows access per-vertex data stored block-centered (like texlerp, ambient) - int vertex_gather_offset[6][4]; - - int pos_x,pos_y,pos_z; - int full; - - // computed from user input - char *output_cur [STBVOX_MAX_MESHES][STBVOX_MAX_MESH_SLOTS]; - char *output_end [STBVOX_MAX_MESHES][STBVOX_MAX_MESH_SLOTS]; - char *output_buffer[STBVOX_MAX_MESHES][STBVOX_MAX_MESH_SLOTS]; - int output_len [STBVOX_MAX_MESHES][STBVOX_MAX_MESH_SLOTS]; - - // computed from config - int output_size [STBVOX_MAX_MESHES][STBVOX_MAX_MESH_SLOTS]; // per quad - int output_step [STBVOX_MAX_MESHES][STBVOX_MAX_MESH_SLOTS]; // per vertex or per face, depending - int num_mesh_slots; - - float default_tex_scale[128][2]; -}; - -#endif // INCLUDE_STB_VOXEL_RENDER_H - - -#ifdef STB_VOXEL_RENDER_IMPLEMENTATION - -#include -#include -#include // memset - -// have to use our own names to avoid the _MSC_VER path having conflicting type names -#ifndef _MSC_VER - #include - typedef uint16_t stbvox_uint16; - typedef uint32_t stbvox_uint32; -#else - typedef unsigned short stbvox_uint16; - typedef unsigned int stbvox_uint32; -#endif - -#ifdef _MSC_VER - #define STBVOX_NOTUSED(v) (void)(v) -#else - #define STBVOX_NOTUSED(v) (void)sizeof(v) -#endif - - - -#ifndef STBVOX_CONFIG_MODE -#error "Must defined STBVOX_CONFIG_MODE to select the mode" -#endif - -#if defined(STBVOX_CONFIG_ROTATION_IN_LIGHTING) && defined(STBVOX_CONFIG_VHEIGHT_IN_LIGHTING) -#error "Can't store both rotation and vheight in lighting" -#endif - - -// The following are candidate voxel modes. Only modes 0, 1, and 20, and 21 are -// currently implemented. Reducing the storage-per-quad further -// shouldn't improve performance, although obviously it allow you -// to create larger worlds without streaming. -// -// -// ----------- Two textures ----------- -- One texture -- ---- Color only ---- -// Mode: 0 1 2 3 4 5 6 10 11 12 20 21 22 23 24 -// ============================================================================================================ -// uses Tex Buffer n Y Y Y Y Y Y Y Y Y n Y Y Y Y -// bytes per quad 32 20 14 12 10 6 6 8 8 4 32 20 10 6 4 -// non-blocks all all some some some slabs stairs some some none all all slabs slabs none -// tex1 256 256 256 256 256 256 256 256 256 256 n n n n n -// tex2 256 256 256 256 256 256 128 n n n n n n n n -// colors 64 64 64 64 64 64 64 8 n n 2^24 2^24 2^24 2^24 256 -// vertex ao Y Y Y Y Y n n Y Y n Y Y Y n n -// vertex texlerp Y Y Y n n n n - - - - - - - - -// x&y extents 127 127 128 64 64 128 64 64 128 128 127 127 128 128 128 -// z extents 255 255 128 64? 64? 64 64 32 64 128 255 255 128 64 128 - -// not sure why I only wrote down the above "result data" and didn't preserve -// the vertex formats, but here I've tried to reconstruct the designs... -// mode # 3 is wrong, one byte too large, but they may have been an error originally - -// Mode: 0 1 2 3 4 5 6 10 11 12 20 21 22 23 24 -// ============================================================================================================= -// bytes per quad 32 20 14 12 10 6 6 8 8 4 20 10 6 4 -// -// vertex x bits 7 7 0 6 0 0 0 0 0 0 7 0 0 0 -// vertex y bits 7 7 0 0 0 0 0 0 0 0 7 0 0 0 -// vertex z bits 9 9 7 4 2 0 0 2 2 0 9 2 0 0 -// vertex ao bits 6 6 6 6 6 0 0 6 6 0 6 6 0 0 -// vertex txl bits 3 3 3 0 0 0 0 0 0 0 (3) 0 0 0 -// -// face tex1 bits (8) 8 8 8 8 8 8 8 8 8 -// face tex2 bits (8) 8 8 8 8 8 7 - - - -// face color bits (8) 8 8 8 8 8 8 3 0 0 24 24 24 8 -// face normal bits (8) 8 8 8 6 4 7 4 4 3 8 3 4 3 -// face x bits 7 0 6 7 6 6 7 7 0 7 7 7 -// face y bits 7 6 6 7 6 6 7 7 0 7 7 7 -// face z bits 2 2 6 6 6 5 6 7 0 7 6 7 - - -#if STBVOX_CONFIG_MODE==0 || STBVOX_CONFIG_MODE==1 - - #define STBVOX_ICONFIG_VERTEX_32 - #define STBVOX_ICONFIG_FACE1_1 - -#elif STBVOX_CONFIG_MODE==20 || STBVOX_CONFIG_MODE==21 - - #define STBVOX_ICONFIG_VERTEX_32 - #define STBVOX_ICONFIG_FACE1_1 - #define STBVOX_ICONFIG_UNTEXTURED - -#else -#error "Selected value of STBVOX_CONFIG_MODE is not supported" -#endif - -#if STBVOX_CONFIG_MODE==0 || STBVOX_CONFIG_MODE==20 -#define STBVOX_ICONFIG_FACE_ATTRIBUTE -#endif - -#ifndef STBVOX_CONFIG_HLSL -// the fallback if all others are exhausted is GLSL -#define STBVOX_ICONFIG_GLSL -#endif - -#ifdef STBVOX_CONFIG_OPENGL_MODELVIEW -#define STBVOX_ICONFIG_OPENGL_3_1_COMPATIBILITY -#endif - -#if defined(STBVOX_ICONFIG_VERTEX_32) - typedef stbvox_uint32 stbvox_mesh_vertex; - #define stbvox_vertex_encode(x,y,z,ao,texlerp) \ - ((stbvox_uint32) ((x)+((y)<<7)+((z)<<14)+((ao)<<23)+((texlerp)<<29))) -#elif defined(STBVOX_ICONFIG_VERTEX_16_1) // mode=2 - typedef stbvox_uint16 stbvox_mesh_vertex; - #define stbvox_vertex_encode(x,y,z,ao,texlerp) \ - ((stbvox_uint16) ((z)+((ao)<<7)+((texlerp)<<13) -#elif defined(STBVOX_ICONFIG_VERTEX_16_2) // mode=3 - typedef stbvox_uint16 stbvox_mesh_vertex; - #define stbvox_vertex_encode(x,y,z,ao,texlerp) \ - ((stbvox_uint16) ((x)+((z)<<6))+((ao)<<10)) -#elif defined(STBVOX_ICONFIG_VERTEX_8) - typedef stbvox_uint8 stbvox_mesh_vertex; - #define stbvox_vertex_encode(x,y,z,ao,texlerp) \ - ((stbvox_uint8) ((z)+((ao)<<6)) -#else - #error "internal error, no vertex type" -#endif - -#ifdef STBVOX_ICONFIG_FACE1_1 - typedef struct - { - unsigned char tex1,tex2,color,face_info; - } stbvox_mesh_face; -#else - #error "internal error, no face type" -#endif - - -// 20-byte quad format: -// -// per vertex: -// -// x:7 -// y:7 -// z:9 -// ao:6 -// tex_lerp:3 -// -// per face: -// -// tex1:8 -// tex2:8 -// face:8 -// color:8 - - -// Faces: -// -// Faces use the bottom 3 bits to choose the texgen -// mode, and all the bits to choose the normal. -// Thus the bottom 3 bits have to be: -// e, n, w, s, u, d, u, d -// -// These use compact names so tables are readable - -enum -{ - STBVF_e, - STBVF_n, - STBVF_w, - STBVF_s, - STBVF_u, - STBVF_d, - STBVF_eu, - STBVF_ed, - - STBVF_eu_wall, - STBVF_nu_wall, - STBVF_wu_wall, - STBVF_su_wall, - STBVF_ne_u, - STBVF_ne_d, - STBVF_nu, - STBVF_nd, - - STBVF_ed_wall, - STBVF_nd_wall, - STBVF_wd_wall, - STBVF_sd_wall, - STBVF_nw_u, - STBVF_nw_d, - STBVF_wu, - STBVF_wd, - - STBVF_ne_u_cross, - STBVF_nw_u_cross, - STBVF_sw_u_cross, - STBVF_se_u_cross, - STBVF_sw_u, - STBVF_sw_d, - STBVF_su, - STBVF_sd, - - // @TODO we need more than 5 bits to encode the normal to fit the following - // so for now we use the right projection but the wrong normal - STBVF_se_u = STBVF_su, - STBVF_se_d = STBVF_sd, - - STBVF_count, -}; - -///////////////////////////////////////////////////////////////////////////// -// -// tables -- i'd prefer if these were at the end of the file, but: C++ -// - -static float stbvox_default_texgen[2][32][3] = -{ - { { 0, 1,0 }, { 0, 0, 1 }, { 0,-1,0 }, { 0, 0,-1 }, - { -1, 0,0 }, { 0, 0, 1 }, { 1, 0,0 }, { 0, 0,-1 }, - { 0,-1,0 }, { 0, 0, 1 }, { 0, 1,0 }, { 0, 0,-1 }, - { 1, 0,0 }, { 0, 0, 1 }, { -1, 0,0 }, { 0, 0,-1 }, - - { 1, 0,0 }, { 0, 1, 0 }, { -1, 0,0 }, { 0,-1, 0 }, - { -1, 0,0 }, { 0,-1, 0 }, { 1, 0,0 }, { 0, 1, 0 }, - { 1, 0,0 }, { 0, 1, 0 }, { -1, 0,0 }, { 0,-1, 0 }, - { -1, 0,0 }, { 0,-1, 0 }, { 1, 0,0 }, { 0, 1, 0 }, - }, - { { 0, 0,-1 }, { 0, 1,0 }, { 0, 0, 1 }, { 0,-1,0 }, - { 0, 0,-1 }, { -1, 0,0 }, { 0, 0, 1 }, { 1, 0,0 }, - { 0, 0,-1 }, { 0,-1,0 }, { 0, 0, 1 }, { 0, 1,0 }, - { 0, 0,-1 }, { 1, 0,0 }, { 0, 0, 1 }, { -1, 0,0 }, - - { 0,-1, 0 }, { 1, 0,0 }, { 0, 1, 0 }, { -1, 0,0 }, - { 0, 1, 0 }, { -1, 0,0 }, { 0,-1, 0 }, { 1, 0,0 }, - { 0,-1, 0 }, { 1, 0,0 }, { 0, 1, 0 }, { -1, 0,0 }, - { 0, 1, 0 }, { -1, 0,0 }, { 0,-1, 0 }, { 1, 0,0 }, - }, -}; - -#define STBVOX_RSQRT2 0.7071067811865f -#define STBVOX_RSQRT3 0.5773502691896f - -static float stbvox_default_normals[32][3] = -{ - { 1,0,0 }, // east - { 0,1,0 }, // north - { -1,0,0 }, // west - { 0,-1,0 }, // south - { 0,0,1 }, // up - { 0,0,-1 }, // down - { STBVOX_RSQRT2,0, STBVOX_RSQRT2 }, // east & up - { STBVOX_RSQRT2,0, -STBVOX_RSQRT2 }, // east & down - - { STBVOX_RSQRT2,0, STBVOX_RSQRT2 }, // east & up - { 0, STBVOX_RSQRT2, STBVOX_RSQRT2 }, // north & up - { -STBVOX_RSQRT2,0, STBVOX_RSQRT2 }, // west & up - { 0,-STBVOX_RSQRT2, STBVOX_RSQRT2 }, // south & up - { STBVOX_RSQRT3, STBVOX_RSQRT3, STBVOX_RSQRT3 }, // ne & up - { STBVOX_RSQRT3, STBVOX_RSQRT3,-STBVOX_RSQRT3 }, // ne & down - { 0, STBVOX_RSQRT2, STBVOX_RSQRT2 }, // north & up - { 0, STBVOX_RSQRT2, -STBVOX_RSQRT2 }, // north & down - - { STBVOX_RSQRT2,0, -STBVOX_RSQRT2 }, // east & down - { 0, STBVOX_RSQRT2, -STBVOX_RSQRT2 }, // north & down - { -STBVOX_RSQRT2,0, -STBVOX_RSQRT2 }, // west & down - { 0,-STBVOX_RSQRT2, -STBVOX_RSQRT2 }, // south & down - { -STBVOX_RSQRT3, STBVOX_RSQRT3, STBVOX_RSQRT3 }, // NW & up - { -STBVOX_RSQRT3, STBVOX_RSQRT3,-STBVOX_RSQRT3 }, // NW & down - { -STBVOX_RSQRT2,0, STBVOX_RSQRT2 }, // west & up - { -STBVOX_RSQRT2,0, -STBVOX_RSQRT2 }, // west & down - - { STBVOX_RSQRT3, STBVOX_RSQRT3,STBVOX_RSQRT3 }, // NE & up crossed - { -STBVOX_RSQRT3, STBVOX_RSQRT3,STBVOX_RSQRT3 }, // NW & up crossed - { -STBVOX_RSQRT3,-STBVOX_RSQRT3,STBVOX_RSQRT3 }, // SW & up crossed - { STBVOX_RSQRT3,-STBVOX_RSQRT3,STBVOX_RSQRT3 }, // SE & up crossed - { -STBVOX_RSQRT3,-STBVOX_RSQRT3, STBVOX_RSQRT3 }, // SW & up - { -STBVOX_RSQRT3,-STBVOX_RSQRT3,-STBVOX_RSQRT3 }, // SW & up - { 0,-STBVOX_RSQRT2, STBVOX_RSQRT2 }, // south & up - { 0,-STBVOX_RSQRT2, -STBVOX_RSQRT2 }, // south & down -}; - -static float stbvox_default_texscale[128][4] = -{ - {1,1,0,0},{1,1,0,0},{1,1,0,0},{1,1,0,0},{1,1,0,0},{1,1,0,0},{1,1,0,0},{1,1,0,0}, - {1,1,0,0},{1,1,0,0},{1,1,0,0},{1,1,0,0},{1,1,0,0},{1,1,0,0},{1,1,0,0},{1,1,0,0}, - {1,1,0,0},{1,1,0,0},{1,1,0,0},{1,1,0,0},{1,1,0,0},{1,1,0,0},{1,1,0,0},{1,1,0,0}, - {1,1,0,0},{1,1,0,0},{1,1,0,0},{1,1,0,0},{1,1,0,0},{1,1,0,0},{1,1,0,0},{1,1,0,0}, - {1,1,0,0},{1,1,0,0},{1,1,0,0},{1,1,0,0},{1,1,0,0},{1,1,0,0},{1,1,0,0},{1,1,0,0}, - {1,1,0,0},{1,1,0,0},{1,1,0,0},{1,1,0,0},{1,1,0,0},{1,1,0,0},{1,1,0,0},{1,1,0,0}, - {1,1,0,0},{1,1,0,0},{1,1,0,0},{1,1,0,0},{1,1,0,0},{1,1,0,0},{1,1,0,0},{1,1,0,0}, - {1,1,0,0},{1,1,0,0},{1,1,0,0},{1,1,0,0},{1,1,0,0},{1,1,0,0},{1,1,0,0},{1,1,0,0}, - {1,1,0,0},{1,1,0,0},{1,1,0,0},{1,1,0,0},{1,1,0,0},{1,1,0,0},{1,1,0,0},{1,1,0,0}, - {1,1,0,0},{1,1,0,0},{1,1,0,0},{1,1,0,0},{1,1,0,0},{1,1,0,0},{1,1,0,0},{1,1,0,0}, - {1,1,0,0},{1,1,0,0},{1,1,0,0},{1,1,0,0},{1,1,0,0},{1,1,0,0},{1,1,0,0},{1,1,0,0}, - {1,1,0,0},{1,1,0,0},{1,1,0,0},{1,1,0,0},{1,1,0,0},{1,1,0,0},{1,1,0,0},{1,1,0,0}, - {1,1,0,0},{1,1,0,0},{1,1,0,0},{1,1,0,0},{1,1,0,0},{1,1,0,0},{1,1,0,0},{1,1,0,0}, - {1,1,0,0},{1,1,0,0},{1,1,0,0},{1,1,0,0},{1,1,0,0},{1,1,0,0},{1,1,0,0},{1,1,0,0}, - {1,1,0,0},{1,1,0,0},{1,1,0,0},{1,1,0,0},{1,1,0,0},{1,1,0,0},{1,1,0,0},{1,1,0,0}, - {1,1,0,0},{1,1,0,0},{1,1,0,0},{1,1,0,0},{1,1,0,0},{1,1,0,0},{1,1,0,0},{1,1,0,0}, -}; - -static unsigned char stbvox_default_palette_compact[64][3] = -{ - { 255,255,255 }, { 238,238,238 }, { 221,221,221 }, { 204,204,204 }, - { 187,187,187 }, { 170,170,170 }, { 153,153,153 }, { 136,136,136 }, - { 119,119,119 }, { 102,102,102 }, { 85, 85, 85 }, { 68, 68, 68 }, - { 51, 51, 51 }, { 34, 34, 34 }, { 17, 17, 17 }, { 0, 0, 0 }, - { 255,240,240 }, { 255,220,220 }, { 255,160,160 }, { 255, 32, 32 }, - { 200,120,160 }, { 200, 60,150 }, { 220,100,130 }, { 255, 0,128 }, - { 240,240,255 }, { 220,220,255 }, { 160,160,255 }, { 32, 32,255 }, - { 120,160,200 }, { 60,150,200 }, { 100,130,220 }, { 0,128,255 }, - { 240,255,240 }, { 220,255,220 }, { 160,255,160 }, { 32,255, 32 }, - { 160,200,120 }, { 150,200, 60 }, { 130,220,100 }, { 128,255, 0 }, - { 255,255,240 }, { 255,255,220 }, { 220,220,180 }, { 255,255, 32 }, - { 200,160,120 }, { 200,150, 60 }, { 220,130,100 }, { 255,128, 0 }, - { 255,240,255 }, { 255,220,255 }, { 220,180,220 }, { 255, 32,255 }, - { 160,120,200 }, { 150, 60,200 }, { 130,100,220 }, { 128, 0,255 }, - { 240,255,255 }, { 220,255,255 }, { 180,220,220 }, { 32,255,255 }, - { 120,200,160 }, { 60,200,150 }, { 100,220,130 }, { 0,255,128 }, -}; - -static float stbvox_default_ambient[4][4] = -{ - { 0,0,1 ,0 }, // reversed lighting direction - { 0.5,0.5,0.5,0 }, // directional color - { 0.5,0.5,0.5,0 }, // constant color - { 0.5,0.5,0.5,1.0f/1000.0f/1000.0f }, // fog data for simple_fog -}; - -static float stbvox_default_palette[64][4]; - -static void stbvox_build_default_palette(void) -{ - int i; - for (i=0; i < 64; ++i) { - stbvox_default_palette[i][0] = stbvox_default_palette_compact[i][0] / 255.0f; - stbvox_default_palette[i][1] = stbvox_default_palette_compact[i][1] / 255.0f; - stbvox_default_palette[i][2] = stbvox_default_palette_compact[i][2] / 255.0f; - stbvox_default_palette[i][3] = 1.0f; - } -} - -////////////////////////////////////////////////////////////////////////////// -// -// Shaders -// - -#if defined(STBVOX_ICONFIG_OPENGL_3_1_COMPATIBILITY) - #define STBVOX_SHADER_VERSION "#version 150 compatibility\n" -#elif defined(STBVOX_ICONFIG_OPENGL_3_0) - #define STBVOX_SHADER_VERSION "#version 130\n" -#elif defined(STBVOX_ICONFIG_GLSL) - #define STBVOX_SHADER_VERSION "#version 150\n" -#else - #define STBVOX_SHADER_VERSION "" -#endif - -static const char *stbvox_vertex_program = -{ - STBVOX_SHADER_VERSION - - #ifdef STBVOX_ICONFIG_FACE_ATTRIBUTE // NOT TAG_face_sampled - "in uvec4 attr_face;\n" - #else - "uniform usamplerBuffer facearray;\n" - #endif - - #ifdef STBVOX_ICONFIG_FACE_ARRAY_2 - "uniform usamplerBuffer facearray2;\n" - #endif - - // vertex input data - "in uint attr_vertex;\n" - - // per-buffer data - "uniform vec3 transform[3];\n" - - // per-frame data - "uniform vec4 camera_pos;\n" // 4th value is used for arbitrary hacking - - // to simplify things, we avoid using more than 256 uniform vectors - // in fragment shader to avoid possible 1024 component limit, so - // we access this table in the fragment shader. - "uniform vec3 normal_table[32];\n" - - #ifndef STBVOX_CONFIG_OPENGL_MODELVIEW - "uniform mat4x4 model_view;\n" - #endif - - // fragment output data - "flat out uvec4 facedata;\n" - " out vec3 voxelspace_pos;\n" - " out vec3 vnormal;\n" - " out float texlerp;\n" - " out float amb_occ;\n" - - // @TODO handle the HLSL way to do this - "void main()\n" - "{\n" - #ifdef STBVOX_ICONFIG_FACE_ATTRIBUTE - " facedata = attr_face;\n" - #else - " int faceID = gl_VertexID >> 2;\n" - " facedata = texelFetch(facearray, faceID);\n" - #endif - - // extract data for vertex - " vec3 offset;\n" - " offset.x = float( (attr_vertex ) & 127u );\n" // a[0..6] - " offset.y = float( (attr_vertex >> 7u) & 127u );\n" // a[7..13] - " offset.z = float( (attr_vertex >> 14u) & 511u );\n" // a[14..22] - " amb_occ = float( (attr_vertex >> 23u) & 63u ) / 63.0;\n" // a[23..28] - " texlerp = float( (attr_vertex >> 29u) ) / 7.0;\n" // a[29..31] - - " vnormal = normal_table[(facedata.w>>2u) & 31u];\n" - " voxelspace_pos = offset * transform[0];\n" // mesh-to-object scale - " vec3 position = voxelspace_pos + transform[1];\n" // mesh-to-object translate - - #ifdef STBVOX_DEBUG_TEST_NORMALS - " if ((facedata.w & 28u) == 16u || (facedata.w & 28u) == 24u)\n" - " position += vnormal.xyz * camera_pos.w;\n" - #endif - - #ifndef STBVOX_CONFIG_OPENGL_MODELVIEW - " gl_Position = model_view * vec4(position,1.0);\n" - #else - " gl_Position = gl_ModelViewProjectionMatrix * vec4(position,1.0);\n" - #endif - - "}\n" -}; - - -static const char *stbvox_fragment_program = -{ - STBVOX_SHADER_VERSION - - // rlerp is lerp but with t on the left, like god intended - #if defined(STBVOX_ICONFIG_GLSL) - "#define rlerp(t,x,y) mix(x,y,t)\n" - #elif defined(STBVOX_CONFIG_HLSL) - "#define rlerp(t,x,y) lerp(x,y,t)\n" - #else - #error "need definition of rlerp()" - #endif - - - // vertex-shader output data - "flat in uvec4 facedata;\n" - " in vec3 voxelspace_pos;\n" - " in vec3 vnormal;\n" - " in float texlerp;\n" - " in float amb_occ;\n" - - // per-buffer data - "uniform vec3 transform[3];\n" - - // per-frame data - "uniform vec4 camera_pos;\n" // 4th value is used for arbitrary hacking - - // probably constant data - "uniform vec4 ambient[4];\n" - - #ifndef STBVOX_ICONFIG_UNTEXTURED - // generally constant data - "uniform sampler2DArray tex_array[2];\n" - - #ifdef STBVOX_CONFIG_PREFER_TEXBUFFER - "uniform samplerBuffer color_table;\n" - "uniform samplerBuffer texscale;\n" - "uniform samplerBuffer texgen;\n" - #else - "uniform vec4 color_table[64];\n" - "uniform vec4 texscale[64];\n" // instead of 128, to avoid running out of uniforms - "uniform vec3 texgen[64];\n" - #endif - #endif - - "out vec4 outcolor;\n" - - #if defined(STBVOX_CONFIG_LIGHTING) || defined(STBVOX_CONFIG_LIGHTING_SIMPLE) - "vec3 compute_lighting(vec3 pos, vec3 norm, vec3 albedo, vec3 ambient);\n" - #endif - #if defined(STBVOX_CONFIG_FOG) || defined(STBVOX_CONFIG_FOG_SMOOTHSTEP) - "vec3 compute_fog(vec3 color, vec3 relative_pos, float fragment_alpha);\n" - #endif - - "void main()\n" - "{\n" - " vec3 albedo;\n" - " float fragment_alpha;\n" - - #ifndef STBVOX_ICONFIG_UNTEXTURED - // unpack the values - " uint tex1_id = facedata.x;\n" - " uint tex2_id = facedata.y;\n" - " uint texprojid = facedata.w & 31u;\n" - " uint color_id = facedata.z;\n" - - #ifndef STBVOX_CONFIG_PREFER_TEXBUFFER - // load from uniforms / texture buffers - " vec3 texgen_s = texgen[texprojid];\n" - " vec3 texgen_t = texgen[texprojid+32u];\n" - " float tex1_scale = texscale[tex1_id & 63u].x;\n" - " vec4 color = color_table[color_id & 63u];\n" - #ifndef STBVOX_CONFIG_DISABLE_TEX2 - " vec4 tex2_props = texscale[tex2_id & 63u];\n" - #endif - #else - " vec3 texgen_s = texelFetch(texgen, int(texprojid)).xyz;\n" - " vec3 texgen_t = texelFetch(texgen, int(texprojid+32u)).xyz;\n" - " float tex1_scale = texelFetch(texscale, int(tex1_id & 127u)).x;\n" - " vec4 color = texelFetch(color_table, int(color_id & 63u));\n" - #ifndef STBVOX_CONFIG_DISABLE_TEX2 - " vec4 tex2_props = texelFetch(texscale, int(tex1_id & 127u));\n" - #endif - #endif - - #ifndef STBVOX_CONFIG_DISABLE_TEX2 - " float tex2_scale = tex2_props.y;\n" - " bool texblend_mode = tex2_props.z != 0.0;\n" - #endif - " vec2 texcoord;\n" - " vec3 texturespace_pos = voxelspace_pos + transform[2].xyz;\n" - " texcoord.s = dot(texturespace_pos, texgen_s);\n" - " texcoord.t = dot(texturespace_pos, texgen_t);\n" - - " vec2 texcoord_1 = tex1_scale * texcoord;\n" - #ifndef STBVOX_CONFIG_DISABLE_TEX2 - " vec2 texcoord_2 = tex2_scale * texcoord;\n" - #endif - - #ifdef STBVOX_CONFIG_TEX1_EDGE_CLAMP - " texcoord_1 = texcoord_1 - floor(texcoord_1);\n" - " vec4 tex1 = textureGrad(tex_array[0], vec3(texcoord_1, float(tex1_id)), dFdx(tex1_scale*texcoord), dFdy(tex1_scale*texcoord));\n" - #else - " vec4 tex1 = texture(tex_array[0], vec3(texcoord_1, float(tex1_id)));\n" - #endif - - #ifndef STBVOX_CONFIG_DISABLE_TEX2 - #ifdef STBVOX_CONFIG_TEX2_EDGE_CLAMP - " texcoord_2 = texcoord_2 - floor(texcoord_2);\n" - " vec4 tex2 = textureGrad(tex_array[0], vec3(texcoord_2, float(tex2_id)), dFdx(tex2_scale*texcoord), dFdy(tex2_scale*texcoord));\n" - #else - " vec4 tex2 = texture(tex_array[1], vec3(texcoord_2, float(tex2_id)));\n" - #endif - #endif - - " bool emissive = (color.a > 1.0);\n" - " color.a = min(color.a, 1.0);\n" - - // recolor textures - " if ((color_id & 64u) != 0u) tex1.rgba *= color.rgba;\n" - " fragment_alpha = tex1.a;\n" - #ifndef STBVOX_CONFIG_DISABLE_TEX2 - " if ((color_id & 128u) != 0u) tex2.rgba *= color.rgba;\n" - - #ifdef STBVOX_CONFIG_PREMULTIPLIED_ALPHA - " tex2.rgba *= texlerp;\n" - #else - " tex2.a *= texlerp;\n" - #endif - - " if (texblend_mode)\n" - " albedo = tex1.xyz * rlerp(tex2.a, vec3(1.0,1.0,1.0), 2.0*tex2.xyz);\n" - " else {\n" - #ifdef STBVOX_CONFIG_PREMULTIPLIED_ALPHA - " albedo = (1.0-tex2.a)*tex1.xyz + tex2.xyz;\n" - #else - " albedo = rlerp(tex2.a, tex1.xyz, tex2.xyz);\n" - #endif - " fragment_alpha = tex1.a*(1-tex2.a)+tex2.a;\n" - " }\n" - #else - " albedo = tex1.xyz;\n" - #endif - - #else // UNTEXTURED - " vec4 color;" - " color.xyz = vec3(facedata.xyz) / 255.0;\n" - " bool emissive = false;\n" - " albedo = color.xyz;\n" - " fragment_alpha = 1.0;\n" - #endif - - #ifdef STBVOX_ICONFIG_VARYING_VERTEX_NORMALS - // currently, there are no modes that trigger this path; idea is that there - // could be a couple of bits per vertex to perturb the normal to e.g. get curved look - " vec3 normal = normalize(vnormal);\n" - #else - " vec3 normal = vnormal;\n" - #endif - - " vec3 ambient_color = dot(normal, ambient[0].xyz) * ambient[1].xyz + ambient[2].xyz;\n" - - " ambient_color = clamp(ambient_color, 0.0, 1.0);" - " ambient_color *= amb_occ;\n" - - " vec3 lit_color;\n" - " if (!emissive)\n" - #if defined(STBVOX_ICONFIG_LIGHTING) || defined(STBVOX_CONFIG_LIGHTING_SIMPLE) - " lit_color = compute_lighting(voxelspace_pos + transform[1], normal, albedo, ambient_color);\n" - #else - " lit_color = albedo * ambient_color ;\n" - #endif - " else\n" - " lit_color = albedo;\n" - - #if defined(STBVOX_ICONFIG_FOG) || defined(STBVOX_CONFIG_FOG_SMOOTHSTEP) - " vec3 dist = voxelspace_pos + (transform[1] - camera_pos.xyz);\n" - " lit_color = compute_fog(lit_color, dist, fragment_alpha);\n" - #endif - - #ifdef STBVOX_CONFIG_UNPREMULTIPLY - " vec4 final_color = vec4(lit_color/fragment_alpha, fragment_alpha);\n" - #else - " vec4 final_color = vec4(lit_color, fragment_alpha);\n" - #endif - " outcolor = final_color;\n" - "}\n" - - #ifdef STBVOX_CONFIG_LIGHTING_SIMPLE - "\n" - "uniform vec3 light_source[2];\n" - "vec3 compute_lighting(vec3 pos, vec3 norm, vec3 albedo, vec3 ambient)\n" - "{\n" - " vec3 light_dir = light_source[0] - pos;\n" - " float lambert = dot(light_dir, norm) / dot(light_dir, light_dir);\n" - " vec3 diffuse = clamp(light_source[1] * clamp(lambert, 0.0, 1.0), 0.0, 1.0);\n" - " return (diffuse + ambient) * albedo;\n" - "}\n" - #endif - - #ifdef STBVOX_CONFIG_FOG_SMOOTHSTEP - "\n" - "vec3 compute_fog(vec3 color, vec3 relative_pos, float fragment_alpha)\n" - "{\n" - " float f = dot(relative_pos,relative_pos)*ambient[3].w;\n" - //" f = rlerp(f, -2,1);\n" - " f = clamp(f, 0.0, 1.0);\n" - " f = 3.0*f*f - 2.0*f*f*f;\n" // smoothstep - //" f = f*f;\n" // fade in more smoothly - #ifdef STBVOX_CONFIG_PREMULTIPLIED_ALPHA - " return rlerp(f, color.xyz, ambient[3].xyz*fragment_alpha);\n" - #else - " return rlerp(f, color.xyz, ambient[3].xyz);\n" - #endif - "}\n" - #endif -}; - - -// still requires full alpha lookups, including tex2 if texblend is enabled -static const char *stbvox_fragment_program_alpha_only = -{ - STBVOX_SHADER_VERSION - - // vertex-shader output data - "flat in uvec4 facedata;\n" - " in vec3 voxelspace_pos;\n" - " in float texlerp;\n" - - // per-buffer data - "uniform vec3 transform[3];\n" - - #ifndef STBVOX_ICONFIG_UNTEXTURED - // generally constant data - "uniform sampler2DArray tex_array[2];\n" - - #ifdef STBVOX_CONFIG_PREFER_TEXBUFFER - "uniform samplerBuffer texscale;\n" - "uniform samplerBuffer texgen;\n" - #else - "uniform vec4 texscale[64];\n" // instead of 128, to avoid running out of uniforms - "uniform vec3 texgen[64];\n" - #endif - #endif - - "out vec4 outcolor;\n" - - "void main()\n" - "{\n" - " vec3 albedo;\n" - " float fragment_alpha;\n" - - #ifndef STBVOX_ICONFIG_UNTEXTURED - // unpack the values - " uint tex1_id = facedata.x;\n" - " uint tex2_id = facedata.y;\n" - " uint texprojid = facedata.w & 31u;\n" - " uint color_id = facedata.z;\n" - - #ifndef STBVOX_CONFIG_PREFER_TEXBUFFER - // load from uniforms / texture buffers - " vec3 texgen_s = texgen[texprojid];\n" - " vec3 texgen_t = texgen[texprojid+32u];\n" - " float tex1_scale = texscale[tex1_id & 63u].x;\n" - " vec4 color = color_table[color_id & 63u];\n" - " vec4 tex2_props = texscale[tex2_id & 63u];\n" - #else - " vec3 texgen_s = texelFetch(texgen, int(texprojid)).xyz;\n" - " vec3 texgen_t = texelFetch(texgen, int(texprojid+32u)).xyz;\n" - " float tex1_scale = texelFetch(texscale, int(tex1_id & 127u)).x;\n" - " vec4 color = texelFetch(color_table, int(color_id & 63u));\n" - " vec4 tex2_props = texelFetch(texscale, int(tex2_id & 127u));\n" - #endif - - #ifndef STBVOX_CONFIG_DISABLE_TEX2 - " float tex2_scale = tex2_props.y;\n" - " bool texblend_mode = tex2_props.z &((facedata.w & 128u) != 0u);\n" - #endif - - " color.a = min(color.a, 1.0);\n" - - " vec2 texcoord;\n" - " vec3 texturespace_pos = voxelspace_pos + transform[2].xyz;\n" - " texcoord.s = dot(texturespace_pos, texgen_s);\n" - " texcoord.t = dot(texturespace_pos, texgen_t);\n" - - " vec2 texcoord_1 = tex1_scale * texcoord;\n" - " vec2 texcoord_2 = tex2_scale * texcoord;\n" - - #ifdef STBVOX_CONFIG_TEX1_EDGE_CLAMP - " texcoord_1 = texcoord_1 - floor(texcoord_1);\n" - " vec4 tex1 = textureGrad(tex_array[0], vec3(texcoord_1, float(tex1_id)), dFdx(tex1_scale*texcoord), dFdy(tex1_scale*texcoord));\n" - #else - " vec4 tex1 = texture(tex_array[0], vec3(texcoord_1, float(tex1_id)));\n" - #endif - - " if ((color_id & 64u) != 0u) tex1.a *= color.a;\n" - " fragment_alpha = tex1.a;\n" - - #ifndef STBVOX_CONFIG_DISABLE_TEX2 - " if (!texblend_mode) {\n" - #ifdef STBVOX_CONFIG_TEX2_EDGE_CLAMP - " texcoord_2 = texcoord_2 - floor(texcoord_2);\n" - " vec4 tex2 = textureGrad(tex_array[0], vec3(texcoord_2, float(tex2_id)), dFdx(tex2_scale*texcoord), dFdy(tex2_scale*texcoord));\n" - #else - " vec4 tex2 = texture(tex_array[1], vec3(texcoord_2, float(tex2_id)));\n" - #endif - - " tex2.a *= texlerp;\n" - " if ((color_id & 128u) != 0u) tex2.rgba *= color.a;\n" - " fragment_alpha = tex1.a*(1-tex2.a)+tex2.a;\n" - "}\n" - "\n" - #endif - - #else // UNTEXTURED - " fragment_alpha = 1.0;\n" - #endif - - " outcolor = vec4(0.0, 0.0, 0.0, fragment_alpha);\n" - "}\n" -}; - - -STBVXDEC char *stbvox_get_vertex_shader(void) -{ - return (char *) stbvox_vertex_program; -} - -STBVXDEC char *stbvox_get_fragment_shader(void) -{ - return (char *) stbvox_fragment_program; -} - -STBVXDEC char *stbvox_get_fragment_shader_alpha_only(void) -{ - return (char *) stbvox_fragment_program_alpha_only; -} - -static float stbvox_dummy_transform[3][3]; - -#ifdef STBVOX_CONFIG_PREFER_TEXBUFFER -#define STBVOX_TEXBUF 1 -#else -#define STBVOX_TEXBUF 0 -#endif - -static stbvox_uniform_info stbvox_uniforms[] = -{ - { STBVOX_UNIFORM_TYPE_sampler , 4, 1, (char*) "facearray" , 0 }, - { STBVOX_UNIFORM_TYPE_vec3 , 12, 3, (char*) "transform" , stbvox_dummy_transform[0] }, - { STBVOX_UNIFORM_TYPE_sampler , 4, 2, (char*) "tex_array" , 0 }, - { STBVOX_UNIFORM_TYPE_vec4 , 16, 128, (char*) "texscale" , stbvox_default_texscale[0] , STBVOX_TEXBUF }, - { STBVOX_UNIFORM_TYPE_vec4 , 16, 64, (char*) "color_table" , stbvox_default_palette[0] , STBVOX_TEXBUF }, - { STBVOX_UNIFORM_TYPE_vec3 , 12, 32, (char*) "normal_table" , stbvox_default_normals[0] }, - { STBVOX_UNIFORM_TYPE_vec3 , 12, 64, (char*) "texgen" , stbvox_default_texgen[0][0], STBVOX_TEXBUF }, - { STBVOX_UNIFORM_TYPE_vec4 , 16, 4, (char*) "ambient" , stbvox_default_ambient[0] }, - { STBVOX_UNIFORM_TYPE_vec4 , 16, 1, (char*) "camera_pos" , stbvox_dummy_transform[0] }, -}; - -STBVXDEC int stbvox_get_uniform_info(stbvox_uniform_info *info, int uniform) -{ - if (uniform < 0 || uniform >= STBVOX_UNIFORM_count) - return 0; - - *info = stbvox_uniforms[uniform]; - return 1; -} - -#define STBVOX_GET_GEO(geom_data) ((geom_data) & 15) - -typedef struct -{ - unsigned char block:2; - unsigned char overlay:2; - unsigned char facerot:2; - unsigned char ecolor:2; -} stbvox_rotate; - -typedef struct -{ - unsigned char x,y,z; -} stbvox_pos; - -static unsigned char stbvox_rotate_face[6][4] = -{ - { 0,1,2,3 }, - { 1,2,3,0 }, - { 2,3,0,1 }, - { 3,0,1,2 }, - { 4,4,4,4 }, - { 5,5,5,5 }, -}; - -#define STBVOX_ROTATE(x,r) stbvox_rotate_face[x][r] // (((x)+(r))&3) - -stbvox_mesh_face stbvox_compute_mesh_face_value(stbvox_mesh_maker *mm, stbvox_rotate rot, int face, int v_off, int normal) -{ - stbvox_mesh_face face_data = { 0 }; - stbvox_block_type bt = mm->input.blocktype[v_off]; - unsigned char bt_face = STBVOX_ROTATE(face, rot.block); - int facerot = rot.facerot; - - #ifdef STBVOX_ICONFIG_UNTEXTURED - if (mm->input.rgb) { - face_data.tex1 = mm->input.rgb[v_off].r; - face_data.tex2 = mm->input.rgb[v_off].g; - face_data.color = mm->input.rgb[v_off].b; - face_data.face_info = (normal<<2); - return face_data; - } - #else - unsigned char color_face; - - if (mm->input.color) - face_data.color = mm->input.color[v_off]; - - if (mm->input.block_tex1) - face_data.tex1 = mm->input.block_tex1[bt]; - else if (mm->input.block_tex1_face) - face_data.tex1 = mm->input.block_tex1_face[bt][bt_face]; - else - face_data.tex1 = bt; - - if (mm->input.block_tex2) - face_data.tex2 = mm->input.block_tex2[bt]; - else if (mm->input.block_tex2_face) - face_data.tex2 = mm->input.block_tex2_face[bt][bt_face]; - - if (mm->input.block_color) { - unsigned char mcol = mm->input.block_color[bt]; - if (mcol) - face_data.color = mcol; - } else if (mm->input.block_color_face) { - unsigned char mcol = mm->input.block_color_face[bt][bt_face]; - if (mcol) - face_data.color = mcol; - } - - if (face <= STBVOX_FACE_south) { - if (mm->input.side_texrot) - facerot = mm->input.side_texrot[v_off] >> (2 * face); - else if (mm->input.block_side_texrot) - facerot = mm->input.block_side_texrot[v_off] >> (2 * bt_face); - } - - if (mm->input.overlay) { - int over_face = STBVOX_ROTATE(face, rot.overlay); - unsigned char over = mm->input.overlay[v_off]; - if (over) { - if (mm->input.overlay_tex1) { - unsigned char rep1 = mm->input.overlay_tex1[over][over_face]; - if (rep1) - face_data.tex1 = rep1; - } - if (mm->input.overlay_tex2) { - unsigned char rep2 = mm->input.overlay_tex2[over][over_face]; - if (rep2) - face_data.tex2 = rep2; - } - if (mm->input.overlay_color) { - unsigned char rep3 = mm->input.overlay_color[over][over_face]; - if (rep3) - face_data.color = rep3; - } - - if (mm->input.overlay_side_texrot && face <= STBVOX_FACE_south) - facerot = mm->input.overlay_side_texrot[over] >> (2*over_face); - } - } - - if (mm->input.tex2_for_tex1) - face_data.tex2 = mm->input.tex2_for_tex1[face_data.tex1]; - if (mm->input.tex2) - face_data.tex2 = mm->input.tex2[v_off]; - if (mm->input.tex2_replace) { - if (mm->input.tex2_facemask[v_off] & (1 << face)) - face_data.tex2 = mm->input.tex2_replace[v_off]; - } - - color_face = STBVOX_ROTATE(face, rot.ecolor); - if (mm->input.extended_color) { - unsigned char ec = mm->input.extended_color[v_off]; - if (mm->input.ecolor_facemask[ec] & (1 << color_face)) - face_data.color = mm->input.ecolor_color[ec]; - } - - if (mm->input.color2) { - if (mm->input.color2_facemask[v_off] & (1 << color_face)) - face_data.color = mm->input.color2[v_off]; - if (mm->input.color3 && (mm->input.color3_facemask[v_off] & (1 << color_face))) - face_data.color = mm->input.color3[v_off]; - } - #endif - - face_data.face_info = (normal<<2) + facerot; - return face_data; -} - -// these are the types of faces each block can have -enum -{ - STBVOX_FT_none , - STBVOX_FT_upper , - STBVOX_FT_lower , - STBVOX_FT_solid , - STBVOX_FT_diag_012, - STBVOX_FT_diag_023, - STBVOX_FT_diag_013, - STBVOX_FT_diag_123, - STBVOX_FT_force , // can't be covered up, used for internal faces, also hides nothing - STBVOX_FT_partial , // only covered by solid, never covers anything else - - STBVOX_FT_count -}; - -static unsigned char stbvox_face_lerp[6] = { 0,2,0,2,4,4 }; -static unsigned char stbvox_vert3_lerp[5] = { 0,3,6,9,12 }; -static unsigned char stbvox_vert_lerp_for_face_lerp[4] = { 0, 4, 7, 7 }; -static unsigned char stbvox_face3_lerp[6] = { 0,3,6,9,12,14 }; -static unsigned char stbvox_vert_lerp_for_simple[4] = { 0,2,5,7 }; -static unsigned char stbvox_face3_updown[8] = { 0,2,5,7,0,2,5,7 }; // ignore top bit - -// vertex offsets for face vertices -static unsigned char stbvox_vertex_vector[6][4][3] = -{ - { { 1,0,1 }, { 1,1,1 }, { 1,1,0 }, { 1,0,0 } }, // east - { { 1,1,1 }, { 0,1,1 }, { 0,1,0 }, { 1,1,0 } }, // north - { { 0,1,1 }, { 0,0,1 }, { 0,0,0 }, { 0,1,0 } }, // west - { { 0,0,1 }, { 1,0,1 }, { 1,0,0 }, { 0,0,0 } }, // south - { { 0,1,1 }, { 1,1,1 }, { 1,0,1 }, { 0,0,1 } }, // up - { { 0,0,0 }, { 1,0,0 }, { 1,1,0 }, { 0,1,0 } }, // down -}; - -// stbvox_vertex_vector, but read coordinates as binary numbers, zyx -static unsigned char stbvox_vertex_selector[6][4] = -{ - { 5,7,3,1 }, - { 7,6,2,3 }, - { 6,4,0,2 }, - { 4,5,1,0 }, - { 6,7,5,4 }, - { 0,1,3,2 }, -}; - -static stbvox_mesh_vertex stbvox_vmesh_delta_normal[6][4] = -{ - { stbvox_vertex_encode(1,0,1,0,0) , - stbvox_vertex_encode(1,1,1,0,0) , - stbvox_vertex_encode(1,1,0,0,0) , - stbvox_vertex_encode(1,0,0,0,0) }, - { stbvox_vertex_encode(1,1,1,0,0) , - stbvox_vertex_encode(0,1,1,0,0) , - stbvox_vertex_encode(0,1,0,0,0) , - stbvox_vertex_encode(1,1,0,0,0) }, - { stbvox_vertex_encode(0,1,1,0,0) , - stbvox_vertex_encode(0,0,1,0,0) , - stbvox_vertex_encode(0,0,0,0,0) , - stbvox_vertex_encode(0,1,0,0,0) }, - { stbvox_vertex_encode(0,0,1,0,0) , - stbvox_vertex_encode(1,0,1,0,0) , - stbvox_vertex_encode(1,0,0,0,0) , - stbvox_vertex_encode(0,0,0,0,0) }, - { stbvox_vertex_encode(0,1,1,0,0) , - stbvox_vertex_encode(1,1,1,0,0) , - stbvox_vertex_encode(1,0,1,0,0) , - stbvox_vertex_encode(0,0,1,0,0) }, - { stbvox_vertex_encode(0,0,0,0,0) , - stbvox_vertex_encode(1,0,0,0,0) , - stbvox_vertex_encode(1,1,0,0,0) , - stbvox_vertex_encode(0,1,0,0,0) } -}; - -static stbvox_mesh_vertex stbvox_vmesh_pre_vheight[6][4] = -{ - { stbvox_vertex_encode(1,0,0,0,0) , - stbvox_vertex_encode(1,1,0,0,0) , - stbvox_vertex_encode(1,1,0,0,0) , - stbvox_vertex_encode(1,0,0,0,0) }, - { stbvox_vertex_encode(1,1,0,0,0) , - stbvox_vertex_encode(0,1,0,0,0) , - stbvox_vertex_encode(0,1,0,0,0) , - stbvox_vertex_encode(1,1,0,0,0) }, - { stbvox_vertex_encode(0,1,0,0,0) , - stbvox_vertex_encode(0,0,0,0,0) , - stbvox_vertex_encode(0,0,0,0,0) , - stbvox_vertex_encode(0,1,0,0,0) }, - { stbvox_vertex_encode(0,0,0,0,0) , - stbvox_vertex_encode(1,0,0,0,0) , - stbvox_vertex_encode(1,0,0,0,0) , - stbvox_vertex_encode(0,0,0,0,0) }, - { stbvox_vertex_encode(0,1,0,0,0) , - stbvox_vertex_encode(1,1,0,0,0) , - stbvox_vertex_encode(1,0,0,0,0) , - stbvox_vertex_encode(0,0,0,0,0) }, - { stbvox_vertex_encode(0,0,0,0,0) , - stbvox_vertex_encode(1,0,0,0,0) , - stbvox_vertex_encode(1,1,0,0,0) , - stbvox_vertex_encode(0,1,0,0,0) } -}; - -static stbvox_mesh_vertex stbvox_vmesh_delta_half_z[6][4] = -{ - { stbvox_vertex_encode(1,0,2,0,0) , - stbvox_vertex_encode(1,1,2,0,0) , - stbvox_vertex_encode(1,1,0,0,0) , - stbvox_vertex_encode(1,0,0,0,0) }, - { stbvox_vertex_encode(1,1,2,0,0) , - stbvox_vertex_encode(0,1,2,0,0) , - stbvox_vertex_encode(0,1,0,0,0) , - stbvox_vertex_encode(1,1,0,0,0) }, - { stbvox_vertex_encode(0,1,2,0,0) , - stbvox_vertex_encode(0,0,2,0,0) , - stbvox_vertex_encode(0,0,0,0,0) , - stbvox_vertex_encode(0,1,0,0,0) }, - { stbvox_vertex_encode(0,0,2,0,0) , - stbvox_vertex_encode(1,0,2,0,0) , - stbvox_vertex_encode(1,0,0,0,0) , - stbvox_vertex_encode(0,0,0,0,0) }, - { stbvox_vertex_encode(0,1,2,0,0) , - stbvox_vertex_encode(1,1,2,0,0) , - stbvox_vertex_encode(1,0,2,0,0) , - stbvox_vertex_encode(0,0,2,0,0) }, - { stbvox_vertex_encode(0,0,0,0,0) , - stbvox_vertex_encode(1,0,0,0,0) , - stbvox_vertex_encode(1,1,0,0,0) , - stbvox_vertex_encode(0,1,0,0,0) } -}; - -static stbvox_mesh_vertex stbvox_vmesh_crossed_pair[6][4] = -{ - { stbvox_vertex_encode(1,0,2,0,0) , - stbvox_vertex_encode(0,1,2,0,0) , - stbvox_vertex_encode(0,1,0,0,0) , - stbvox_vertex_encode(1,0,0,0,0) }, - { stbvox_vertex_encode(1,1,2,0,0) , - stbvox_vertex_encode(0,0,2,0,0) , - stbvox_vertex_encode(0,0,0,0,0) , - stbvox_vertex_encode(1,1,0,0,0) }, - { stbvox_vertex_encode(0,1,2,0,0) , - stbvox_vertex_encode(1,0,2,0,0) , - stbvox_vertex_encode(1,0,0,0,0) , - stbvox_vertex_encode(0,1,0,0,0) }, - { stbvox_vertex_encode(0,0,2,0,0) , - stbvox_vertex_encode(1,1,2,0,0) , - stbvox_vertex_encode(1,1,0,0,0) , - stbvox_vertex_encode(0,0,0,0,0) }, - // not used, so we leave it non-degenerate to make sure it doesn't get gen'd accidentally - { stbvox_vertex_encode(0,1,2,0,0) , - stbvox_vertex_encode(1,1,2,0,0) , - stbvox_vertex_encode(1,0,2,0,0) , - stbvox_vertex_encode(0,0,2,0,0) }, - { stbvox_vertex_encode(0,0,0,0,0) , - stbvox_vertex_encode(1,0,0,0,0) , - stbvox_vertex_encode(1,1,0,0,0) , - stbvox_vertex_encode(0,1,0,0,0) } -}; - -#define STBVOX_MAX_GEOM 16 -#define STBVOX_NUM_ROTATION 4 - -// this is used to determine if a face is ever generated at all -static unsigned char stbvox_hasface[STBVOX_MAX_GEOM][STBVOX_NUM_ROTATION] = -{ - { 0,0,0,0 }, // empty - { 0,0,0,0 }, // knockout - { 63,63,63,63 }, // solid - { 63,63,63,63 }, // transp - { 63,63,63,63 }, // slab - { 63,63,63,63 }, // slab - { 1|2|4|48, 8|1|2|48, 4|8|1|48, 2|4|8|48, }, // floor slopes - { 1|2|4|48, 8|1|2|48, 4|8|1|48, 2|4|8|48, }, // ceil slopes - { 47,47,47,47 }, // wall-projected diagonal with down face - { 31,31,31,31 }, // wall-projected diagonal with up face - { 63,63,63,63 }, // crossed-pair has special handling, but avoid early-out - { 63,63,63,63 }, // force - { 63,63,63,63 }, // vheight - { 63,63,63,63 }, // vheight - { 63,63,63,63 }, // vheight - { 63,63,63,63 }, // vheight -}; - -// this determines which face type above is visible on each side of the geometry -static unsigned char stbvox_facetype[STBVOX_GEOM_count][6] = -{ - { 0, }, // STBVOX_GEOM_empty - { STBVOX_FT_solid, STBVOX_FT_solid, STBVOX_FT_solid, STBVOX_FT_solid, STBVOX_FT_solid, STBVOX_FT_solid }, // knockout - { STBVOX_FT_solid, STBVOX_FT_solid, STBVOX_FT_solid, STBVOX_FT_solid, STBVOX_FT_solid, STBVOX_FT_solid }, // solid - { STBVOX_FT_force, STBVOX_FT_force, STBVOX_FT_force, STBVOX_FT_force, STBVOX_FT_force, STBVOX_FT_force }, // transp - - { STBVOX_FT_upper, STBVOX_FT_upper, STBVOX_FT_upper, STBVOX_FT_upper, STBVOX_FT_solid, STBVOX_FT_force }, - { STBVOX_FT_lower, STBVOX_FT_lower, STBVOX_FT_lower, STBVOX_FT_lower, STBVOX_FT_force, STBVOX_FT_solid }, - { STBVOX_FT_diag_123, STBVOX_FT_solid, STBVOX_FT_diag_023, STBVOX_FT_none, STBVOX_FT_force, STBVOX_FT_solid }, - { STBVOX_FT_diag_012, STBVOX_FT_solid, STBVOX_FT_diag_013, STBVOX_FT_none, STBVOX_FT_solid, STBVOX_FT_force }, - - { STBVOX_FT_diag_123, STBVOX_FT_solid, STBVOX_FT_diag_023, STBVOX_FT_force, STBVOX_FT_none, STBVOX_FT_solid }, - { STBVOX_FT_diag_012, STBVOX_FT_solid, STBVOX_FT_diag_013, STBVOX_FT_force, STBVOX_FT_solid, STBVOX_FT_none }, - { STBVOX_FT_force, STBVOX_FT_force, STBVOX_FT_force, STBVOX_FT_force, 0,0 }, // crossed pair - { STBVOX_FT_force, STBVOX_FT_force, STBVOX_FT_force, STBVOX_FT_force, STBVOX_FT_force, STBVOX_FT_force }, // GEOM_force - - { STBVOX_FT_partial,STBVOX_FT_partial,STBVOX_FT_partial,STBVOX_FT_partial, STBVOX_FT_force, STBVOX_FT_solid }, // floor vheight, all neighbors forced - { STBVOX_FT_partial,STBVOX_FT_partial,STBVOX_FT_partial,STBVOX_FT_partial, STBVOX_FT_force, STBVOX_FT_solid }, // floor vheight, all neighbors forced - { STBVOX_FT_partial,STBVOX_FT_partial,STBVOX_FT_partial,STBVOX_FT_partial, STBVOX_FT_solid, STBVOX_FT_force }, // ceil vheight, all neighbors forced - { STBVOX_FT_partial,STBVOX_FT_partial,STBVOX_FT_partial,STBVOX_FT_partial, STBVOX_FT_solid, STBVOX_FT_force }, // ceil vheight, all neighbors forced -}; - -// This table indicates what normal to use for the "up" face of a sloped geom -// @TODO this could be done with math given the current arrangement of the enum, but let's not require it -static unsigned char stbvox_floor_slope_for_rot[4] = -{ - STBVF_su, - STBVF_wu, // @TODO: why is this reversed from what it should be? this is a north-is-up face, so slope should be south&up - STBVF_nu, - STBVF_eu, -}; - -static unsigned char stbvox_ceil_slope_for_rot[4] = -{ - STBVF_sd, - STBVF_ed, - STBVF_nd, - STBVF_wd, -}; - -// this table indicates whether, for each pair of types above, a face is visible. -// each value indicates whether a given type is visible for all neighbor types -static unsigned short stbvox_face_visible[STBVOX_FT_count] = -{ - // we encode the table by listing which cases cause *obscuration*, and bitwise inverting that - // table is pre-shifted by 5 to save a shift when it's accessed - (unsigned short) ((~0x07ff )<<5), // none is completely obscured by everything - (unsigned short) ((~((1<output_cur[mesh][0]; - int step = mm->output_step[mesh][0]; - - // allocate a new quad from the mesh - vertices[0] = (stbvox_mesh_vertex *) p; p += step; - vertices[1] = (stbvox_mesh_vertex *) p; p += step; - vertices[2] = (stbvox_mesh_vertex *) p; p += step; - vertices[3] = (stbvox_mesh_vertex *) p; p += step; - mm->output_cur[mesh][0] = p; - - // output the face - #ifdef STBVOX_ICONFIG_FACE_ATTRIBUTE - // write face as interleaved vertex data - *(stbvox_mesh_face *) (vertices[0]+1) = face; - *(stbvox_mesh_face *) (vertices[1]+1) = face; - *(stbvox_mesh_face *) (vertices[2]+1) = face; - *(stbvox_mesh_face *) (vertices[3]+1) = face; - #else - *(stbvox_mesh_face *) mm->output_cur[mesh][1] = face; - mm->output_cur[mesh][1] += 4; - #endif -} - -void stbvox_make_mesh_for_face(stbvox_mesh_maker *mm, stbvox_rotate rot, int face, int v_off, stbvox_pos pos, stbvox_mesh_vertex vertbase, stbvox_mesh_vertex *face_coord, unsigned char mesh, int normal) -{ - stbvox_mesh_face face_data = stbvox_compute_mesh_face_value(mm,rot,face,v_off, normal); - - // still need to compute ao & texlerp for each vertex - - // first compute texlerp into p1 - stbvox_mesh_vertex p1[4] = { 0 }; - - #if defined(STBVOX_CONFIG_DOWN_TEXLERP_PACKED) && defined(STBVOX_CONFIG_UP_TEXLERP_PACKED) - #define STBVOX_USE_PACKED(f) ((f) == STBVOX_FACE_up || (f) == STBVOX_FACE_down) - #elif defined(STBVOX_CONFIG_UP_TEXLERP_PACKED) - #define STBVOX_USE_PACKED(f) ((f) == STBVOX_FACE_up ) - #elif defined(STBVOX_CONFIG_DOWN_TEXLERP_PACKED) - #define STBVOX_USE_PACKED(f) ( (f) == STBVOX_FACE_down) - #endif - - #if defined(STBVOX_CONFIG_DOWN_TEXLERP_PACKED) || defined(STBVOX_CONFIG_UP_TEXLERP_PACKED) - if (STBVOX_USE_PACKED(face)) { - if (!mm->input.packed_compact || 0==(mm->input.packed_compact[v_off]&16)) - goto set_default; - p1[0] = (mm->input.packed_compact[v_off + mm->cube_vertex_offset[face][0]] >> 5); - p1[1] = (mm->input.packed_compact[v_off + mm->cube_vertex_offset[face][1]] >> 5); - p1[2] = (mm->input.packed_compact[v_off + mm->cube_vertex_offset[face][2]] >> 5); - p1[3] = (mm->input.packed_compact[v_off + mm->cube_vertex_offset[face][3]] >> 5); - p1[0] = stbvox_vertex_encode(0,0,0,0,p1[0]); - p1[1] = stbvox_vertex_encode(0,0,0,0,p1[1]); - p1[2] = stbvox_vertex_encode(0,0,0,0,p1[2]); - p1[3] = stbvox_vertex_encode(0,0,0,0,p1[3]); - goto skip; - } - #endif - - if (mm->input.block_texlerp) { - stbvox_block_type bt = mm->input.blocktype[v_off]; - unsigned char val = mm->input.block_texlerp[bt]; - p1[0] = p1[1] = p1[2] = p1[3] = stbvox_vertex_encode(0,0,0,0,val); - } else if (mm->input.block_texlerp_face) { - stbvox_block_type bt = mm->input.blocktype[v_off]; - unsigned char bt_face = STBVOX_ROTATE(face, rot.block); - unsigned char val = mm->input.block_texlerp_face[bt][bt_face]; - p1[0] = p1[1] = p1[2] = p1[3] = stbvox_vertex_encode(0,0,0,0,val); - } else if (mm->input.texlerp_face3) { - unsigned char val = (mm->input.texlerp_face3[v_off] >> stbvox_face3_lerp[face]) & 7; - if (face >= STBVOX_FACE_up) - val = stbvox_face3_updown[val]; - p1[0] = p1[1] = p1[2] = p1[3] = stbvox_vertex_encode(0,0,0,0,val); - } else if (mm->input.texlerp_simple) { - unsigned char val = mm->input.texlerp_simple[v_off]; - unsigned char lerp_face = (val >> 2) & 7; - if (lerp_face == face) { - p1[0] = (mm->input.texlerp_simple[v_off + mm->cube_vertex_offset[face][0]] >> 5) & 7; - p1[1] = (mm->input.texlerp_simple[v_off + mm->cube_vertex_offset[face][1]] >> 5) & 7; - p1[2] = (mm->input.texlerp_simple[v_off + mm->cube_vertex_offset[face][2]] >> 5) & 7; - p1[3] = (mm->input.texlerp_simple[v_off + mm->cube_vertex_offset[face][3]] >> 5) & 7; - p1[0] = stbvox_vertex_encode(0,0,0,0,p1[0]); - p1[1] = stbvox_vertex_encode(0,0,0,0,p1[1]); - p1[2] = stbvox_vertex_encode(0,0,0,0,p1[2]); - p1[3] = stbvox_vertex_encode(0,0,0,0,p1[3]); - } else { - unsigned char base = stbvox_vert_lerp_for_simple[val&3]; - p1[0] = p1[1] = p1[2] = p1[3] = stbvox_vertex_encode(0,0,0,0,base); - } - } else if (mm->input.texlerp) { - unsigned char facelerp = (mm->input.texlerp[v_off] >> stbvox_face_lerp[face]) & 3; - if (facelerp == STBVOX_TEXLERP_FACE_use_vert) { - if (mm->input.texlerp_vert3 && face != STBVOX_FACE_down) { - unsigned char shift = stbvox_vert3_lerp[face]; - p1[0] = (mm->input.texlerp_vert3[mm->cube_vertex_offset[face][0]] >> shift) & 7; - p1[1] = (mm->input.texlerp_vert3[mm->cube_vertex_offset[face][1]] >> shift) & 7; - p1[2] = (mm->input.texlerp_vert3[mm->cube_vertex_offset[face][2]] >> shift) & 7; - p1[3] = (mm->input.texlerp_vert3[mm->cube_vertex_offset[face][3]] >> shift) & 7; - } else { - p1[0] = stbvox_vert_lerp_for_simple[mm->input.texlerp[mm->cube_vertex_offset[face][0]]>>6]; - p1[1] = stbvox_vert_lerp_for_simple[mm->input.texlerp[mm->cube_vertex_offset[face][1]]>>6]; - p1[2] = stbvox_vert_lerp_for_simple[mm->input.texlerp[mm->cube_vertex_offset[face][2]]>>6]; - p1[3] = stbvox_vert_lerp_for_simple[mm->input.texlerp[mm->cube_vertex_offset[face][3]]>>6]; - } - p1[0] = stbvox_vertex_encode(0,0,0,0,p1[0]); - p1[1] = stbvox_vertex_encode(0,0,0,0,p1[1]); - p1[2] = stbvox_vertex_encode(0,0,0,0,p1[2]); - p1[3] = stbvox_vertex_encode(0,0,0,0,p1[3]); - } else { - p1[0] = p1[1] = p1[2] = p1[3] = stbvox_vertex_encode(0,0,0,0,stbvox_vert_lerp_for_face_lerp[facelerp]); - } - } else { - #if defined(STBVOX_CONFIG_UP_TEXLERP_PACKED) || defined(STBVOX_CONFIG_DOWN_TEXLERP_PACKED) - set_default: - #endif - p1[0] = p1[1] = p1[2] = p1[3] = stbvox_vertex_encode(0,0,0,0,7); // @TODO make this configurable - } - - #if defined(STBVOX_CONFIG_UP_TEXLERP_PACKED) || defined(STBVOX_CONFIG_DOWN_TEXLERP_PACKED) - skip: - #endif - - // now compute lighting and store to vertices - { - stbvox_mesh_vertex *mv[4]; - stbvox_get_quad_vertex_pointer(mm, mesh, mv, face_data); - - if (mm->input.lighting) { - // @TODO: lighting at block centers, but not gathered, instead constant-per-face - if (mm->input.lighting_at_vertices) { - int i; - for (i=0; i < 4; ++i) { - *mv[i] = vertbase + face_coord[i] - + stbvox_vertex_encode(0,0,0,mm->input.lighting[v_off + mm->cube_vertex_offset[face][i]] & 63,0) - + p1[i]; - } - } else { - unsigned char *amb = &mm->input.lighting[v_off]; - int i,j; - #if defined(STBVOX_CONFIG_ROTATION_IN_LIGHTING) || defined(STBVOX_CONFIG_VHEIGHT_IN_LIGHTING) - #define STBVOX_GET_LIGHTING(light) ((light) & ~3) - #define STBVOX_LIGHTING_ROUNDOFF 8 - #else - #define STBVOX_GET_LIGHTING(light) (light) - #define STBVOX_LIGHTING_ROUNDOFF 2 - #endif - - for (i=0; i < 4; ++i) { - // for each vertex, gather from the four neighbor blocks it's facing - unsigned char *vamb = &amb[mm->cube_vertex_offset[face][i]]; - int total=0; - for (j=0; j < 4; ++j) - total += STBVOX_GET_LIGHTING(vamb[mm->vertex_gather_offset[face][j]]); - *mv[i] = vertbase + face_coord[i] - + stbvox_vertex_encode(0,0,0,(total+STBVOX_LIGHTING_ROUNDOFF)>>4,0) - + p1[i]; - // >> 4 is because: - // >> 2 to divide by 4 to get average over 4 samples - // >> 2 because input is 8 bits, output is 6 bits - } - - // @TODO: note that gathering baked *lighting* - // is different from gathering baked ao; baked ao can count - // solid blocks as 0 ao, but baked lighting wants average - // of non-blocked--not take average & treat blocked as 0. And - // we can't bake the right value into the solid blocks - // because they can have different lighting values on - // different sides. So we need to actually gather and - // then divide by 0..4 (which we can do with a table-driven - // multiply, or have an 'if' for the 3 case) - - } - } else { - vertbase += stbvox_vertex_encode(0,0,0,63,0); - *mv[0] = vertbase + face_coord[0] + p1[0]; - *mv[1] = vertbase + face_coord[1] + p1[1]; - *mv[2] = vertbase + face_coord[2] + p1[2]; - *mv[3] = vertbase + face_coord[3] + p1[3]; - } - } -} - -// get opposite-facing normal & texgen for opposite face, used to map up-facing vheight data to down-facing data -static unsigned char stbvox_reverse_face[STBVF_count] = -{ - STBVF_w, STBVF_s, STBVF_e, STBVF_n, STBVF_d , STBVF_u , STBVF_wd, STBVF_wu, - 0, 0, 0, 0, STBVF_sw_d, STBVF_sw_u, STBVF_sd, STBVF_su, - 0, 0, 0, 0, STBVF_se_d, STBVF_se_u, STBVF_ed, STBVF_eu, - 0, 0, 0, 0, STBVF_ne_d, STBVF_ne_d, STBVF_nd, STBVF_nu -}; - -#ifndef STBVOX_CONFIG_OPTIMIZED_VHEIGHT -// render non-planar quads by splitting into two triangles, rendering each as a degenerate quad -static void stbvox_make_12_split_mesh_for_face(stbvox_mesh_maker *mm, stbvox_rotate rot, int face, int v_off, stbvox_pos pos, stbvox_mesh_vertex vertbase, stbvox_mesh_vertex *face_coord, unsigned char mesh, unsigned char *ht) -{ - stbvox_mesh_vertex v[4]; - - unsigned char normal1 = stbvox_face_up_normal_012[ht[2]][ht[1]][ht[0]]; - unsigned char normal2 = stbvox_face_up_normal_123[ht[3]][ht[2]][ht[1]]; - - if (face == STBVOX_FACE_down) { - normal1 = stbvox_reverse_face[normal1]; - normal2 = stbvox_reverse_face[normal2]; - } - - // the floor side face_coord is stored in order NW,NE,SE,SW, but ht[] is stored SW,SE,NW,NE - v[0] = face_coord[2]; - v[1] = face_coord[3]; - v[2] = face_coord[0]; - v[3] = face_coord[2]; - stbvox_make_mesh_for_face(mm, rot, face, v_off, pos, vertbase, v, mesh, normal1); - v[1] = face_coord[0]; - v[2] = face_coord[1]; - stbvox_make_mesh_for_face(mm, rot, face, v_off, pos, vertbase, v, mesh, normal2); -} - -static void stbvox_make_03_split_mesh_for_face(stbvox_mesh_maker *mm, stbvox_rotate rot, int face, int v_off, stbvox_pos pos, stbvox_mesh_vertex vertbase, stbvox_mesh_vertex *face_coord, unsigned char mesh, unsigned char *ht) -{ - stbvox_mesh_vertex v[4]; - - unsigned char normal1 = stbvox_face_up_normal_013[ht[3]][ht[1]][ht[0]]; - unsigned char normal2 = stbvox_face_up_normal_023[ht[3]][ht[2]][ht[0]]; - - if (face == STBVOX_FACE_down) { - normal1 = stbvox_reverse_face[normal1]; - normal2 = stbvox_reverse_face[normal2]; - } - - v[0] = face_coord[1]; - v[1] = face_coord[2]; - v[2] = face_coord[3]; - v[3] = face_coord[1]; - stbvox_make_mesh_for_face(mm, rot, face, v_off, pos, vertbase, v, mesh, normal1); - v[1] = face_coord[3]; - v[2] = face_coord[0]; - stbvox_make_mesh_for_face(mm, rot, face, v_off, pos, vertbase, v, mesh, normal2); // this one is correct! -} -#endif - -#ifndef STBVOX_CONFIG_PRECISION_Z -#define STBVOX_CONFIG_PRECISION_Z 1 -#endif - -// simple case for mesh generation: we have only solid and empty blocks -static void stbvox_make_mesh_for_block(stbvox_mesh_maker *mm, stbvox_pos pos, int v_off, stbvox_mesh_vertex *vmesh) -{ - int ns_off = mm->y_stride_in_bytes; - int ew_off = mm->x_stride_in_bytes; - - unsigned char *blockptr = &mm->input.blocktype[v_off]; - stbvox_mesh_vertex basevert = stbvox_vertex_encode(pos.x, pos.y, pos.z << STBVOX_CONFIG_PRECISION_Z , 0,0); - - stbvox_rotate rot = { 0,0,0,0 }; - unsigned char simple_rot = 0; - - unsigned char mesh = mm->default_mesh; - - if (mm->input.selector) - mesh = mm->input.selector[v_off]; - - // check if we're going off the end - if (mm->output_cur[mesh][0] + mm->output_size[mesh][0]*6 > mm->output_end[mesh][0]) { - mm->full = 1; - return; - } - - #ifdef STBVOX_CONFIG_ROTATION_IN_LIGHTING - simple_rot = mm->input.lighting[v_off] & 3; - #endif - - if (mm->input.packed_compact) - simple_rot = mm->input.packed_compact[v_off] & 3; - - if (blockptr[ 1]==0) { - rot.facerot = simple_rot; - stbvox_make_mesh_for_face(mm, rot, STBVOX_FACE_up , v_off, pos, basevert, vmesh+4*STBVOX_FACE_up, mesh, STBVOX_FACE_up); - } - if (blockptr[-1]==0) { - rot.facerot = (-simple_rot) & 3; - stbvox_make_mesh_for_face(mm, rot, STBVOX_FACE_down, v_off, pos, basevert, vmesh+4*STBVOX_FACE_down, mesh, STBVOX_FACE_down); - } - - if (mm->input.rotate) { - unsigned char val = mm->input.rotate[v_off]; - rot.block = (val >> 0) & 3; - rot.overlay = (val >> 2) & 3; - //rot.tex2 = (val >> 4) & 3; - rot.ecolor = (val >> 6) & 3; - } else { - rot.block = rot.overlay = rot.ecolor = simple_rot; - } - rot.facerot = 0; - - if (blockptr[ ns_off]==0) - stbvox_make_mesh_for_face(mm, rot, STBVOX_FACE_north, v_off, pos, basevert, vmesh+4*STBVOX_FACE_north, mesh, STBVOX_FACE_north); - if (blockptr[-ns_off]==0) - stbvox_make_mesh_for_face(mm, rot, STBVOX_FACE_south, v_off, pos, basevert, vmesh+4*STBVOX_FACE_south, mesh, STBVOX_FACE_south); - if (blockptr[ ew_off]==0) - stbvox_make_mesh_for_face(mm, rot, STBVOX_FACE_east , v_off, pos, basevert, vmesh+4*STBVOX_FACE_east, mesh, STBVOX_FACE_east); - if (blockptr[-ew_off]==0) - stbvox_make_mesh_for_face(mm, rot, STBVOX_FACE_west , v_off, pos, basevert, vmesh+4*STBVOX_FACE_west, mesh, STBVOX_FACE_west); -} - -// complex case for mesh generation: we have lots of different -// block types, and we don't want to generate faces of blocks -// if they're hidden by neighbors. -// -// we use lots of tables to determine this: we have a table -// which tells us what face type is generated for each type of -// geometry, and then a table that tells us whether that type -// is hidden by a neighbor. -static void stbvox_make_mesh_for_block_with_geo(stbvox_mesh_maker *mm, stbvox_pos pos, int v_off) -{ - int ns_off = mm->y_stride_in_bytes; - int ew_off = mm->x_stride_in_bytes; - int visible_faces, visible_base; - unsigned char mesh; - - // first gather the geometry info for this block and all neighbors - - unsigned char bt, nbt[6]; - unsigned char geo, ngeo[6]; - unsigned char rot, nrot[6]; - - bt = mm->input.blocktype[v_off]; - nbt[0] = mm->input.blocktype[v_off + ew_off]; - nbt[1] = mm->input.blocktype[v_off + ns_off]; - nbt[2] = mm->input.blocktype[v_off - ew_off]; - nbt[3] = mm->input.blocktype[v_off - ns_off]; - nbt[4] = mm->input.blocktype[v_off + 1]; - nbt[5] = mm->input.blocktype[v_off - 1]; - if (mm->input.geometry) { - int i; - geo = mm->input.geometry[v_off]; - ngeo[0] = mm->input.geometry[v_off + ew_off]; - ngeo[1] = mm->input.geometry[v_off + ns_off]; - ngeo[2] = mm->input.geometry[v_off - ew_off]; - ngeo[3] = mm->input.geometry[v_off - ns_off]; - ngeo[4] = mm->input.geometry[v_off + 1]; - ngeo[5] = mm->input.geometry[v_off - 1]; - - rot = (geo >> 4) & 3; - geo &= 15; - for (i=0; i < 6; ++i) { - nrot[i] = (ngeo[i] >> 4) & 3; - ngeo[i] &= 15; - } - } else { - int i; - assert(mm->input.block_geometry); - geo = mm->input.block_geometry[bt]; - for (i=0; i < 6; ++i) - ngeo[i] = mm->input.block_geometry[nbt[i]]; - if (mm->input.selector) { - #ifndef STBVOX_CONFIG_ROTATION_IN_LIGHTING - if (mm->input.packed_compact == NULL) { - rot = (mm->input.selector[v_off ] >> 4) & 3; - nrot[0] = (mm->input.selector[v_off + ew_off] >> 4) & 3; - nrot[1] = (mm->input.selector[v_off + ns_off] >> 4) & 3; - nrot[2] = (mm->input.selector[v_off - ew_off] >> 4) & 3; - nrot[3] = (mm->input.selector[v_off - ns_off] >> 4) & 3; - nrot[4] = (mm->input.selector[v_off + 1] >> 4) & 3; - nrot[5] = (mm->input.selector[v_off - 1] >> 4) & 3; - } - #endif - } else { - #ifndef STBVOX_CONFIG_ROTATION_IN_LIGHTING - if (mm->input.packed_compact == NULL) { - rot = (geo>>4)&3; - geo &= 15; - for (i=0; i < 6; ++i) { - nrot[i] = (ngeo[i]>>4)&3; - ngeo[i] &= 15; - } - } - #endif - } - } - - #ifndef STBVOX_CONFIG_ROTATION_IN_LIGHTING - if (mm->input.packed_compact) { - rot = mm->input.packed_compact[rot] & 3; - nrot[0] = mm->input.packed_compact[v_off + ew_off] & 3; - nrot[1] = mm->input.packed_compact[v_off + ns_off] & 3; - nrot[2] = mm->input.packed_compact[v_off - ew_off] & 3; - nrot[3] = mm->input.packed_compact[v_off - ns_off] & 3; - nrot[4] = mm->input.packed_compact[v_off + 1] & 3; - nrot[5] = mm->input.packed_compact[v_off - 1] & 3; - } - #else - rot = mm->input.lighting[v_off] & 3; - nrot[0] = (mm->input.lighting[v_off + ew_off]) & 3; - nrot[1] = (mm->input.lighting[v_off + ns_off]) & 3; - nrot[2] = (mm->input.lighting[v_off - ew_off]) & 3; - nrot[3] = (mm->input.lighting[v_off - ns_off]) & 3; - nrot[4] = (mm->input.lighting[v_off + 1]) & 3; - nrot[5] = (mm->input.lighting[v_off - 1]) & 3; - #endif - - if (geo == STBVOX_GEOM_transp) { - // transparency has a special rule: if the blocktype is the same, - // and the faces are compatible, then can hide them; otherwise, - // force them on - // Note that this means we don't support any transparentshapes other - // than solid blocks, since detecting them is too complicated. If - // you wanted to do something like minecraft water, you probably - // should just do that with a separate renderer anyway. (We don't - // support transparency sorting so you need to use alpha test - // anyway) - int i; - for (i=0; i < 6; ++i) - if (nbt[i] != bt) { - nbt[i] = 0; - ngeo[i] = STBVOX_GEOM_empty; - } else - ngeo[i] = STBVOX_GEOM_solid; - geo = STBVOX_GEOM_solid; - } - - // now compute the face visibility - visible_base = stbvox_hasface[geo][rot]; - // @TODO: assert(visible_base != 0); // we should have early-outted earlier in this case - visible_faces = 0; - - // now, for every face that might be visible, check if neighbor hides it - if (visible_base & (1 << STBVOX_FACE_east)) { - int type = stbvox_facetype[ geo ][(STBVOX_FACE_east+ rot )&3]; - int ntype = stbvox_facetype[ngeo[0]][(STBVOX_FACE_west+nrot[0])&3]; - visible_faces |= ((stbvox_face_visible[type]) >> (ntype + 5 - STBVOX_FACE_east)) & (1 << STBVOX_FACE_east); - } - if (visible_base & (1 << STBVOX_FACE_north)) { - int type = stbvox_facetype[ geo ][(STBVOX_FACE_north+ rot )&3]; - int ntype = stbvox_facetype[ngeo[1]][(STBVOX_FACE_south+nrot[1])&3]; - visible_faces |= ((stbvox_face_visible[type]) >> (ntype + 5 - STBVOX_FACE_north)) & (1 << STBVOX_FACE_north); - } - if (visible_base & (1 << STBVOX_FACE_west)) { - int type = stbvox_facetype[ geo ][(STBVOX_FACE_west+ rot )&3]; - int ntype = stbvox_facetype[ngeo[2]][(STBVOX_FACE_east+nrot[2])&3]; - visible_faces |= ((stbvox_face_visible[type]) >> (ntype + 5 - STBVOX_FACE_west)) & (1 << STBVOX_FACE_west); - } - if (visible_base & (1 << STBVOX_FACE_south)) { - int type = stbvox_facetype[ geo ][(STBVOX_FACE_south+ rot )&3]; - int ntype = stbvox_facetype[ngeo[3]][(STBVOX_FACE_north+nrot[3])&3]; - visible_faces |= ((stbvox_face_visible[type]) >> (ntype + 5 - STBVOX_FACE_south)) & (1 << STBVOX_FACE_south); - } - if (visible_base & (1 << STBVOX_FACE_up)) { - int type = stbvox_facetype[ geo ][STBVOX_FACE_up]; - int ntype = stbvox_facetype[ngeo[4]][STBVOX_FACE_down]; - visible_faces |= ((stbvox_face_visible[type]) >> (ntype + 5 - STBVOX_FACE_up)) & (1 << STBVOX_FACE_up); - } - if (visible_base & (1 << STBVOX_FACE_down)) { - int type = stbvox_facetype[ geo ][STBVOX_FACE_down]; - int ntype = stbvox_facetype[ngeo[5]][STBVOX_FACE_up]; - visible_faces |= ((stbvox_face_visible[type]) >> (ntype + 5 - STBVOX_FACE_down)) & (1 << STBVOX_FACE_down); - } - - if (geo == STBVOX_GEOM_force) - geo = STBVOX_GEOM_solid; - - assert((geo == STBVOX_GEOM_crossed_pair) ? (visible_faces == 15) : 1); - - // now we finally know for sure which faces are getting generated - if (visible_faces == 0) - return; - - mesh = mm->default_mesh; - if (mm->input.selector) - mesh = mm->input.selector[v_off]; - - if (geo <= STBVOX_GEOM_ceil_slope_north_is_bottom) { - // this is the simple case, we can just use regular block gen with special vmesh calculated with vheight - stbvox_mesh_vertex basevert; - stbvox_mesh_vertex vmesh[6][4]; - stbvox_rotate rotate = { 0,0,0,0 }; - unsigned char simple_rot = rot; - int i; - // we only need to do this for the displayed faces, but it's easier - // to just do it up front; @OPTIMIZE check if it's faster to do it - // for visible faces only - for (i=0; i < 6*4; ++i) { - int vert = stbvox_vertex_selector[0][i]; - vert = stbvox_rotate_vertex[vert][rot]; - vmesh[0][i] = stbvox_vmesh_pre_vheight[0][i] - + stbvox_geometry_vheight[geo][vert]; - } - - basevert = stbvox_vertex_encode(pos.x, pos.y, pos.z << STBVOX_CONFIG_PRECISION_Z, 0,0); - if (mm->input.selector) { - mesh = mm->input.selector[v_off]; - } - - // check if we're going off the end - if (mm->output_cur[mesh][0] + mm->output_size[mesh][0]*6 > mm->output_end[mesh][0]) { - mm->full = 1; - return; - } - - if (geo >= STBVOX_GEOM_floor_slope_north_is_top) { - if (visible_faces & (1 << STBVOX_FACE_up)) { - int normal = geo == STBVOX_GEOM_floor_slope_north_is_top ? stbvox_floor_slope_for_rot[simple_rot] : STBVOX_FACE_up; - rotate.facerot = simple_rot; - stbvox_make_mesh_for_face(mm, rotate, STBVOX_FACE_up , v_off, pos, basevert, vmesh[STBVOX_FACE_up], mesh, normal); - } - if (visible_faces & (1 << STBVOX_FACE_down)) { - int normal = geo == STBVOX_GEOM_ceil_slope_north_is_bottom ? stbvox_ceil_slope_for_rot[simple_rot] : STBVOX_FACE_down; - rotate.facerot = (-rotate.facerot) & 3; - stbvox_make_mesh_for_face(mm, rotate, STBVOX_FACE_down, v_off, pos, basevert, vmesh[STBVOX_FACE_down], mesh, normal); - } - } else { - if (visible_faces & (1 << STBVOX_FACE_up)) { - rotate.facerot = simple_rot; - stbvox_make_mesh_for_face(mm, rotate, STBVOX_FACE_up , v_off, pos, basevert, vmesh[STBVOX_FACE_up], mesh, STBVOX_FACE_up); - } - if (visible_faces & (1 << STBVOX_FACE_down)) { - rotate.facerot = (-rotate.facerot) & 3; - stbvox_make_mesh_for_face(mm, rotate, STBVOX_FACE_down, v_off, pos, basevert, vmesh[STBVOX_FACE_down], mesh, STBVOX_FACE_down); - } - } - - if (mm->input.rotate) { - unsigned char val = mm->input.rotate[v_off]; - rotate.block = (val >> 0) & 3; - rotate.overlay = (val >> 2) & 3; - //rotate.tex2 = (val >> 4) & 3; - rotate.ecolor = (val >> 6) & 3; - } else { - rotate.block = rotate.overlay = rotate.ecolor = simple_rot; - } - - rotate.facerot = 0; - - if (visible_faces & (1 << STBVOX_FACE_north)) - stbvox_make_mesh_for_face(mm, rotate, STBVOX_FACE_north, v_off, pos, basevert, vmesh[STBVOX_FACE_north], mesh, STBVOX_FACE_north); - if (visible_faces & (1 << STBVOX_FACE_south)) - stbvox_make_mesh_for_face(mm, rotate, STBVOX_FACE_south, v_off, pos, basevert, vmesh[STBVOX_FACE_south], mesh, STBVOX_FACE_south); - if (visible_faces & (1 << STBVOX_FACE_east)) - stbvox_make_mesh_for_face(mm, rotate, STBVOX_FACE_east , v_off, pos, basevert, vmesh[STBVOX_FACE_east ], mesh, STBVOX_FACE_east); - if (visible_faces & (1 << STBVOX_FACE_west)) - stbvox_make_mesh_for_face(mm, rotate, STBVOX_FACE_west , v_off, pos, basevert, vmesh[STBVOX_FACE_west ], mesh, STBVOX_FACE_west); - } - if (geo >= STBVOX_GEOM_floor_vheight_03) { - // this case can also be generated with regular block gen with special vmesh, - // except: - // if we want to generate middle diagonal for 'weird' blocks - // it's more complicated to detect neighbor matchups - stbvox_mesh_vertex vmesh[6][4]; - stbvox_mesh_vertex cube[8]; - stbvox_mesh_vertex basevert; - stbvox_rotate rotate = { 0,0,0,0 }; - unsigned char simple_rot = rot; - unsigned char ht[4]; - int extreme; - - // extract the heights - #ifdef STBVOX_CONFIG_VHEIGHT_IN_LIGHTING - ht[0] = mm->input.lighting[v_off ] & 3; - ht[1] = mm->input.lighting[v_off+ew_off ] & 3; - ht[2] = mm->input.lighting[v_off +ns_off] & 3; - ht[3] = mm->input.lighting[v_off+ew_off+ns_off] & 3; - #else - if (mm->input.vheight) { - unsigned char v = mm->input.vheight[v_off]; - ht[0] = (v >> 0) & 3; - ht[1] = (v >> 2) & 3; - ht[2] = (v >> 4) & 3; - ht[3] = (v >> 6) & 3; - } else if (mm->input.block_vheight) { - unsigned char v = mm->input.block_vheight[bt]; - unsigned char raw[4]; - int i; - - raw[0] = (v >> 0) & 3; - raw[1] = (v >> 2) & 3; - raw[2] = (v >> 4) & 3; - raw[3] = (v >> 6) & 3; - - for (i=0; i < 4; ++i) - ht[i] = raw[stbvox_rotate_vertex[i][rot]]; - } else if (mm->input.packed_compact) { - ht[0] = (mm->input.packed_compact[v_off ] >> 2) & 3; - ht[1] = (mm->input.packed_compact[v_off+ew_off ] >> 2) & 3; - ht[2] = (mm->input.packed_compact[v_off +ns_off] >> 2) & 3; - ht[3] = (mm->input.packed_compact[v_off+ew_off+ns_off] >> 2) & 3; - } else if (mm->input.geometry) { - ht[0] = mm->input.geometry[v_off ] >> 6; - ht[1] = mm->input.geometry[v_off+ew_off ] >> 6; - ht[2] = mm->input.geometry[v_off +ns_off] >> 6; - ht[3] = mm->input.geometry[v_off+ew_off+ns_off] >> 6; - } else { - assert(0); - } - #endif - - // flag whether any sides go off the top of the block, which means - // our visible_faces test was wrong - extreme = (ht[0] == 3 || ht[1] == 3 || ht[2] == 3 || ht[3] == 3); - - if (geo >= STBVOX_GEOM_ceil_vheight_03) { - cube[0] = stbvox_vertex_encode(0,0,ht[0],0,0); - cube[1] = stbvox_vertex_encode(0,0,ht[1],0,0); - cube[2] = stbvox_vertex_encode(0,0,ht[2],0,0); - cube[3] = stbvox_vertex_encode(0,0,ht[3],0,0); - cube[4] = stbvox_vertex_encode(0,0,2,0,0); - cube[5] = stbvox_vertex_encode(0,0,2,0,0); - cube[6] = stbvox_vertex_encode(0,0,2,0,0); - cube[7] = stbvox_vertex_encode(0,0,2,0,0); - } else { - cube[0] = stbvox_vertex_encode(0,0,0,0,0); - cube[1] = stbvox_vertex_encode(0,0,0,0,0); - cube[2] = stbvox_vertex_encode(0,0,0,0,0); - cube[3] = stbvox_vertex_encode(0,0,0,0,0); - cube[4] = stbvox_vertex_encode(0,0,ht[0],0,0); - cube[5] = stbvox_vertex_encode(0,0,ht[1],0,0); - cube[6] = stbvox_vertex_encode(0,0,ht[2],0,0); - cube[7] = stbvox_vertex_encode(0,0,ht[3],0,0); - } - if (!mm->input.vheight && mm->input.block_vheight) { - // @TODO: support block vheight here, I've forgotten what needs to be done specially - } - - // build vertex mesh - { - int i; - for (i=0; i < 6*4; ++i) { - int vert = stbvox_vertex_selector[0][i]; - vmesh[0][i] = stbvox_vmesh_pre_vheight[0][i] - + cube[vert]; - } - } - - basevert = stbvox_vertex_encode(pos.x, pos.y, pos.z << STBVOX_CONFIG_PRECISION_Z, 0,0); - // check if we're going off the end - if (mm->output_cur[mesh][0] + mm->output_size[mesh][0]*6 > mm->output_end[mesh][0]) { - mm->full = 1; - return; - } - - // @TODO generate split faces - if (visible_faces & (1 << STBVOX_FACE_up)) { - if (geo >= STBVOX_GEOM_ceil_vheight_03) - // flat - stbvox_make_mesh_for_face(mm, rotate, STBVOX_FACE_up , v_off, pos, basevert, vmesh[STBVOX_FACE_up], mesh, STBVOX_FACE_up); - else { - #ifndef STBVOX_CONFIG_OPTIMIZED_VHEIGHT - // check if it's non-planar - if (cube[5] + cube[6] != cube[4] + cube[7]) { - // not planar, split along diagonal and make degenerate quads - if (geo == STBVOX_GEOM_floor_vheight_03) - stbvox_make_03_split_mesh_for_face(mm, rotate, STBVOX_FACE_up, v_off, pos, basevert, vmesh[STBVOX_FACE_up], mesh, ht); - else - stbvox_make_12_split_mesh_for_face(mm, rotate, STBVOX_FACE_up, v_off, pos, basevert, vmesh[STBVOX_FACE_up], mesh, ht); - } else - stbvox_make_mesh_for_face(mm, rotate, STBVOX_FACE_up , v_off, pos, basevert, vmesh[STBVOX_FACE_up], mesh, stbvox_planar_face_up_normal[ht[2]][ht[1]][ht[0]]); - #else - stbvox_make_mesh_for_face(mm, rotate, STBVOX_FACE_up , v_off, pos, basevert, vmesh[STBVOX_FACE_up], mesh, stbvox_optimized_face_up_normal[ht[3]][ht[2]][ht[1]][ht[0]]); - #endif - } - } - if (visible_faces & (1 << STBVOX_FACE_down)) { - if (geo < STBVOX_GEOM_ceil_vheight_03) - // flat - stbvox_make_mesh_for_face(mm, rotate, STBVOX_FACE_down, v_off, pos, basevert, vmesh[STBVOX_FACE_down], mesh, STBVOX_FACE_down); - else { - #ifndef STBVOX_CONFIG_OPTIMIZED_VHEIGHT - // check if it's non-planar - if (cube[1] + cube[2] != cube[0] + cube[3]) { - // not planar, split along diagonal and make degenerate quads - if (geo == STBVOX_GEOM_ceil_vheight_03) - stbvox_make_03_split_mesh_for_face(mm, rotate, STBVOX_FACE_down, v_off, pos, basevert, vmesh[STBVOX_FACE_down], mesh, ht); - else - stbvox_make_12_split_mesh_for_face(mm, rotate, STBVOX_FACE_down, v_off, pos, basevert, vmesh[STBVOX_FACE_down], mesh, ht); - } else - stbvox_make_mesh_for_face(mm, rotate, STBVOX_FACE_down, v_off, pos, basevert, vmesh[STBVOX_FACE_down], mesh, stbvox_reverse_face[stbvox_planar_face_up_normal[ht[2]][ht[1]][ht[0]]]); - #else - stbvox_make_mesh_for_face(mm, rotate, STBVOX_FACE_down, v_off, pos, basevert, vmesh[STBVOX_FACE_down], mesh, stbvox_reverse_face[stbvox_optimized_face_up_normal[ht[3]][ht[2]][ht[1]][ht[0]]]); - #endif - } - } - - if (mm->input.rotate) { - unsigned char val = mm->input.rotate[v_off]; - rotate.block = (val >> 0) & 3; - rotate.overlay = (val >> 2) & 3; - //rotate.tex2 = (val >> 4) & 3; - rotate.ecolor = (val >> 6) & 3; - } else if (mm->input.selector) { - rotate.block = rotate.overlay = rotate.ecolor = simple_rot; - } - - if ((visible_faces & (1 << STBVOX_FACE_north)) || (extreme && (ht[2] == 3 || ht[3] == 3))) - stbvox_make_mesh_for_face(mm, rotate, STBVOX_FACE_north, v_off, pos, basevert, vmesh[STBVOX_FACE_north], mesh, STBVOX_FACE_north); - if ((visible_faces & (1 << STBVOX_FACE_south)) || (extreme && (ht[0] == 3 || ht[1] == 3))) - stbvox_make_mesh_for_face(mm, rotate, STBVOX_FACE_south, v_off, pos, basevert, vmesh[STBVOX_FACE_south], mesh, STBVOX_FACE_south); - if ((visible_faces & (1 << STBVOX_FACE_east)) || (extreme && (ht[1] == 3 || ht[3] == 3))) - stbvox_make_mesh_for_face(mm, rotate, STBVOX_FACE_east , v_off, pos, basevert, vmesh[STBVOX_FACE_east ], mesh, STBVOX_FACE_east); - if ((visible_faces & (1 << STBVOX_FACE_west)) || (extreme && (ht[0] == 3 || ht[2] == 3))) - stbvox_make_mesh_for_face(mm, rotate, STBVOX_FACE_west , v_off, pos, basevert, vmesh[STBVOX_FACE_west ], mesh, STBVOX_FACE_west); - } - - if (geo == STBVOX_GEOM_crossed_pair) { - // this can be generated with a special vmesh - stbvox_mesh_vertex basevert = stbvox_vertex_encode(pos.x, pos.y, pos.z << STBVOX_CONFIG_PRECISION_Z , 0,0); - unsigned char simple_rot=0; - stbvox_rotate rot = { 0,0,0,0 }; - unsigned char mesh = mm->default_mesh; - if (mm->input.selector) { - mesh = mm->input.selector[v_off]; - simple_rot = mesh >> 4; - mesh &= 15; - } - - // check if we're going off the end - if (mm->output_cur[mesh][0] + mm->output_size[mesh][0]*4 > mm->output_end[mesh][0]) { - mm->full = 1; - return; - } - - if (mm->input.rotate) { - unsigned char val = mm->input.rotate[v_off]; - rot.block = (val >> 0) & 3; - rot.overlay = (val >> 2) & 3; - //rot.tex2 = (val >> 4) & 3; - rot.ecolor = (val >> 6) & 3; - } else if (mm->input.selector) { - rot.block = rot.overlay = rot.ecolor = simple_rot; - } - rot.facerot = 0; - - stbvox_make_mesh_for_face(mm, rot, STBVOX_FACE_north, v_off, pos, basevert, stbvox_vmesh_crossed_pair[STBVOX_FACE_north], mesh, STBVF_ne_u_cross); - stbvox_make_mesh_for_face(mm, rot, STBVOX_FACE_south, v_off, pos, basevert, stbvox_vmesh_crossed_pair[STBVOX_FACE_south], mesh, STBVF_sw_u_cross); - stbvox_make_mesh_for_face(mm, rot, STBVOX_FACE_east , v_off, pos, basevert, stbvox_vmesh_crossed_pair[STBVOX_FACE_east ], mesh, STBVF_se_u_cross); - stbvox_make_mesh_for_face(mm, rot, STBVOX_FACE_west , v_off, pos, basevert, stbvox_vmesh_crossed_pair[STBVOX_FACE_west ], mesh, STBVF_nw_u_cross); - } - - - // @TODO - // STBVOX_GEOM_floor_slope_north_is_top_as_wall, - // STBVOX_GEOM_ceil_slope_north_is_bottom_as_wall, -} - -static void stbvox_make_mesh_for_column(stbvox_mesh_maker *mm, int x, int y, int z0) -{ - stbvox_pos pos; - int v_off = x * mm->x_stride_in_bytes + y * mm->y_stride_in_bytes; - int ns_off = mm->y_stride_in_bytes; - int ew_off = mm->x_stride_in_bytes; - pos.x = x; - pos.y = y; - pos.z = 0; - if (mm->input.geometry) { - unsigned char *bt = mm->input.blocktype + v_off; - unsigned char *geo = mm->input.geometry + v_off; - int z; - for (z=z0; z < mm->z1; ++z) { - if (bt[z] && ( !bt[z+ns_off] || !STBVOX_GET_GEO(geo[z+ns_off]) || !bt[z-ns_off] || !STBVOX_GET_GEO(geo[z-ns_off]) - || !bt[z+ew_off] || !STBVOX_GET_GEO(geo[z+ew_off]) || !bt[z-ew_off] || !STBVOX_GET_GEO(geo[z-ew_off]) - || !bt[z-1] || !STBVOX_GET_GEO(geo[z-1]) || !bt[z+1] || !STBVOX_GET_GEO(geo[z+1]))) - { // TODO check up and down - pos.z = z; - stbvox_make_mesh_for_block_with_geo(mm, pos, v_off+z); - if (mm->full) { - mm->cur_z = z; - return; - } - } - } - } else if (mm->input.block_geometry) { - int z; - unsigned char *bt = mm->input.blocktype + v_off; - unsigned char *geo = mm->input.block_geometry; - for (z=z0; z < mm->z1; ++z) { - if (bt[z] && ( geo[bt[z+ns_off]] != STBVOX_GEOM_solid - || geo[bt[z-ns_off]] != STBVOX_GEOM_solid - || geo[bt[z+ew_off]] != STBVOX_GEOM_solid - || geo[bt[z-ew_off]] != STBVOX_GEOM_solid - || geo[bt[z-1]] != STBVOX_GEOM_solid - || geo[bt[z+1]] != STBVOX_GEOM_solid)) - { - pos.z = z; - stbvox_make_mesh_for_block_with_geo(mm, pos, v_off+z); - if (mm->full) { - mm->cur_z = z; - return; - } - } - } - } else { - unsigned char *bt = mm->input.blocktype + v_off; - int z; - #if STBVOX_CONFIG_PRECISION_Z == 1 - stbvox_mesh_vertex *vmesh = stbvox_vmesh_delta_half_z[0]; - #else - stbvox_mesh_vertex *vmesh = stbvox_vmesh_delta_normal[0]; - #endif - for (z=z0; z < mm->z1; ++z) { - // if it's solid and at least one neighbor isn't solid - if (bt[z] && (!bt[z+ns_off] || !bt[z-ns_off] || !bt[z+ew_off] || !bt[z-ew_off] || !bt[z-1] || !bt[z+1])) { - pos.z = z; - stbvox_make_mesh_for_block(mm, pos, v_off+z, vmesh); - if (mm->full) { - mm->cur_z = z; - return; - } - } - } - } -} - -static void stbvox_bring_up_to_date(stbvox_mesh_maker *mm) -{ - if (mm->config_dirty) { - int i; - #ifdef STBVOX_ICONFIG_FACE_ATTRIBUTE - mm->num_mesh_slots = 1; - for (i=0; i < STBVOX_MAX_MESHES; ++i) { - mm->output_size[i][0] = 32; - mm->output_step[i][0] = 8; - } - #else - mm->num_mesh_slots = 2; - for (i=0; i < STBVOX_MAX_MESHES; ++i) { - mm->output_size[i][0] = 16; - mm->output_step[i][0] = 4; - mm->output_size[i][1] = 4; - mm->output_step[i][1] = 4; - } - #endif - - mm->config_dirty = 0; - } -} - -int stbvox_make_mesh(stbvox_mesh_maker *mm) -{ - int x,y; - stbvox_bring_up_to_date(mm); - mm->full = 0; - if (mm->cur_x > mm->x0 || mm->cur_y > mm->y0 || mm->cur_z > mm->z0) { - stbvox_make_mesh_for_column(mm, mm->cur_x, mm->cur_y, mm->cur_z); - if (mm->full) - return 0; - ++mm->cur_y; - while (mm->cur_y < mm->y1 && !mm->full) { - stbvox_make_mesh_for_column(mm, mm->cur_x, mm->cur_y, mm->z0); - if (mm->full) - return 0; - ++mm->cur_y; - } - ++mm->cur_x; - } - for (x=mm->cur_x; x < mm->x1; ++x) { - for (y=mm->y0; y < mm->y1; ++y) { - stbvox_make_mesh_for_column(mm, x, y, mm->z0); - if (mm->full) { - mm->cur_x = x; - mm->cur_y = y; - return 0; - } - } - } - return 1; -} - -void stbvox_init_mesh_maker(stbvox_mesh_maker *mm) -{ - memset(mm, 0, sizeof(*mm)); - stbvox_build_default_palette(); - - mm->config_dirty = 1; - mm->default_mesh = 0; -} - -int stbvox_get_buffer_count(stbvox_mesh_maker *mm) -{ - stbvox_bring_up_to_date(mm); - return mm->num_mesh_slots; -} - -int stbvox_get_buffer_size_per_quad(stbvox_mesh_maker *mm, int n) -{ - return mm->output_size[0][n]; -} - -void stbvox_reset_buffers(stbvox_mesh_maker *mm) -{ - int i; - for (i=0; i < STBVOX_MAX_MESHES*STBVOX_MAX_MESH_SLOTS; ++i) { - mm->output_cur[0][i] = 0; - mm->output_buffer[0][i] = 0; - } -} - -void stbvox_set_buffer(stbvox_mesh_maker *mm, int mesh, int slot, void *buffer, size_t len) -{ - int i; - stbvox_bring_up_to_date(mm); - mm->output_buffer[mesh][slot] = (char *) buffer; - mm->output_cur [mesh][slot] = (char *) buffer; - mm->output_len [mesh][slot] = len; - mm->output_end [mesh][slot] = (char *) buffer + len; - for (i=0; i < STBVOX_MAX_MESH_SLOTS; ++i) { - if (mm->output_buffer[mesh][i]) { - assert(mm->output_len[mesh][i] / mm->output_size[mesh][i] == mm->output_len[mesh][slot] / mm->output_size[mesh][slot]); - } - } -} - -void stbvox_set_default_mesh(stbvox_mesh_maker *mm, int mesh) -{ - mm->default_mesh = mesh; -} - -int stbvox_get_quad_count(stbvox_mesh_maker *mm, int mesh) -{ - return (mm->output_cur[mesh][0] - mm->output_buffer[mesh][0]) / mm->output_size[mesh][0]; -} - -stbvox_input_description *stbvox_get_input_description(stbvox_mesh_maker *mm) -{ - return &mm->input; -} - -void stbvox_set_input_range(stbvox_mesh_maker *mm, int x0, int y0, int z0, int x1, int y1, int z1) -{ - mm->x0 = x0; - mm->y0 = y0; - mm->z0 = z0; - - mm->x1 = x1; - mm->y1 = y1; - mm->z1 = z1; - - mm->cur_x = x0; - mm->cur_y = y0; - mm->cur_z = z0; - - // @TODO validate that this range is representable in this mode -} - -void stbvox_get_transform(stbvox_mesh_maker *mm, float transform[3][3]) -{ - // scale - transform[0][0] = 1.0; - transform[0][1] = 1.0; - #if STBVOX_CONFIG_PRECISION_Z==1 - transform[0][2] = 0.5f; - #else - transform[0][2] = 1.0f; - #endif - // translation - transform[1][0] = (float) (mm->pos_x); - transform[1][1] = (float) (mm->pos_y); - transform[1][2] = (float) (mm->pos_z); - // texture coordinate projection translation - transform[2][0] = (float) (mm->pos_x & 255); // @TODO depends on max texture scale - transform[2][1] = (float) (mm->pos_y & 255); - transform[2][2] = (float) (mm->pos_z & 255); -} - -void stbvox_get_bounds(stbvox_mesh_maker *mm, float bounds[2][3]) -{ - bounds[0][0] = (float) (mm->pos_x + mm->x0); - bounds[0][1] = (float) (mm->pos_y + mm->y0); - bounds[0][2] = (float) (mm->pos_z + mm->z0); - bounds[1][0] = (float) (mm->pos_x + mm->x1); - bounds[1][1] = (float) (mm->pos_y + mm->y1); - bounds[1][2] = (float) (mm->pos_z + mm->z1); -} - -void stbvox_set_mesh_coordinates(stbvox_mesh_maker *mm, int x, int y, int z) -{ - mm->pos_x = x; - mm->pos_y = y; - mm->pos_z = z; -} - -void stbvox_set_input_stride(stbvox_mesh_maker *mm, int x_stride_in_bytes, int y_stride_in_bytes) -{ - int f,v; - mm->x_stride_in_bytes = x_stride_in_bytes; - mm->y_stride_in_bytes = y_stride_in_bytes; - for (f=0; f < 6; ++f) { - for (v=0; v < 4; ++v) { - mm->cube_vertex_offset[f][v] = stbvox_vertex_vector[f][v][0] * mm->x_stride_in_bytes - + stbvox_vertex_vector[f][v][1] * mm->y_stride_in_bytes - + stbvox_vertex_vector[f][v][2] ; - mm->vertex_gather_offset[f][v] = (stbvox_vertex_vector[f][v][0]-1) * mm->x_stride_in_bytes - + (stbvox_vertex_vector[f][v][1]-1) * mm->y_stride_in_bytes - + (stbvox_vertex_vector[f][v][2]-1) ; - } - } -} - -///////////////////////////////////////////////////////////////////////////// -// -// offline computation of tables -// - -#if 0 -// compute optimized vheight table -static char *normal_names[32] = -{ - 0,0,0,0,"u ",0, "eu ",0, - 0,0,0,0,"ne_u",0, "nu ",0, - 0,0,0,0,"nw_u",0, "wu ",0, - 0,0,0,0,"sw_u",0, "su ",0, -}; - -static char *find_best_normal(float x, float y, float z) -{ - int best_slot = 4; - float best_dot = 0; - int i; - for (i=0; i < 32; ++i) { - if (normal_names[i]) { - float dot = x * stbvox_default_normals[i][0] + y * stbvox_default_normals[i][1] + z * stbvox_default_normals[i][2]; - if (dot > best_dot) { - best_dot = dot; - best_slot = i; - } - } - } - return normal_names[best_slot]; -} - -int main(int argc, char **argv) -{ - int sw,se,nw,ne; - for (ne=0; ne < 4; ++ne) { - for (nw=0; nw < 4; ++nw) { - for (se=0; se < 4; ++se) { - printf(" { "); - for (sw=0; sw < 4; ++sw) { - float x = (float) (nw + sw - ne - se); - float y = (float) (sw + se - nw - ne); - float z = 2; - printf("STBVF_%s, ", find_best_normal(x,y,z)); - } - printf("},\n"); - } - } - } - return 0; -} -#endif - -// @TODO -// -// - test API for texture rotation on side faces -// - API for texture rotation on top & bottom -// - better culling of vheight faces with vheight neighbors -// - better culling of non-vheight faces with vheight neighbors -// - gather vertex lighting from slopes correctly -// - better support texture edge_clamp: currently if you fall -// exactly on 1.0 you get wrapped incorrectly; this is rare, but -// can avoid: compute texcoords in vertex shader, offset towards -// center before modding, need 2 bits per vertex to know offset direction) -// - other mesh modes (10,6,4-byte quads) -// -// -// With TexBuffer for the fixed vertex data, we can actually do -// minecrafty non-blocks like stairs -- we still probably only -// want 256 or so, so we can't do the equivalent of all the vheight -// combos, but that's ok. The 256 includes baked rotations, but only -// some of them need it, and lots of block types share some faces. -// -// mode 5 (6 bytes): mode 6 (6 bytes) -// x:7 x:6 -// y:7 y:6 -// z:6 z:6 -// tex1:8 tex1:8 -// tex2:8 tex2:7 -// color:8 color:8 -// face:4 face:7 -// -// -// side faces (all x4) top&bottom faces (2x) internal faces (1x) -// 1 regular 1 regular -// 2 slabs 2 -// 8 stairs 4 stairs 16 -// 4 diag side 8 -// 4 upper diag side 8 -// 4 lower diag side 8 -// 4 crossed pairs -// -// 23*4 + 5*4 + 46 -// == 92 + 20 + 46 = 158 -// -// Must drop 30 of them to fit in 7 bits: -// ceiling half diagonals: 16+8 = 24 -// Need to get rid of 6 more. -// ceiling diagonals: 8+4 = 12 -// This brings it to 122, so can add a crossed-pair variant. -// (diagonal and non-diagonal, or randomly offset) -// Or carpet, which would be 5 more. -// -// -// Mode 4 (10 bytes): -// v: z:2,light:6 -// f: x:6,y:6,z:7, t1:8,t2:8,c:8,f:5 -// -// Mode ? (10 bytes) -// v: xyz:5 (27 values), light:3 -// f: x:7,y:7,z:6, t1:8,t2:8,c:8,f:4 -// (v: x:2,y:2,z:2,light:2) - -#endif // STB_VOXEL_RENDER_IMPLEMENTATION diff --git a/impeller/third_party/stb/stb/stretchy_buffer.h b/impeller/third_party/stb/stb/stretchy_buffer.h deleted file mode 100644 index 9b47a670824be..0000000000000 --- a/impeller/third_party/stb/stb/stretchy_buffer.h +++ /dev/null @@ -1,216 +0,0 @@ -// stretchy_buffer.h - v1.02 - public domain - nothings.org/stb -// a vector<>-like dynamic array for C -// -// version history: -// 1.02 - tweaks to syntax for no good reason -// 1.01 - added a "common uses" documentation section -// 1.0 - fixed bug in the version I posted prematurely -// 0.9 - rewrite to try to avoid strict-aliasing optimization -// issues, but won't compile as C++ -// -// Will probably not work correctly with strict-aliasing optimizations. -// -// The idea: -// -// This implements an approximation to C++ vector<> for C, in that it -// provides a generic definition for dynamic arrays which you can -// still access in a typesafe way using arr[i] or *(arr+i). However, -// it is simply a convenience wrapper around the common idiom of -// of keeping a set of variables (in a struct or globals) which store -// - pointer to array -// - the length of the "in-use" part of the array -// - the current size of the allocated array -// -// I find it to be single most useful non-built-in-structure when -// programming in C (hash tables a close second), but to be clear -// it lacks many of the capabilities of C++ vector<>: there is no -// range checking, the object address isn't stable (see next section -// for details), the set of methods available is small (although -// the file stb.h has another implementation of stretchy buffers -// called 'stb_arr' which provides more methods, e.g. for insertion -// and deletion). -// -// How to use: -// -// Unlike other stb header file libraries, there is no need to -// define an _IMPLEMENTATION symbol. Every #include creates as -// much implementation is needed. -// -// stretchy_buffer.h does not define any types, so you do not -// need to #include it to before defining data types that are -// stretchy buffers, only in files that *manipulate* stretchy -// buffers. -// -// If you want a stretchy buffer aka dynamic array containing -// objects of TYPE, declare such an array as: -// -// TYPE *myarray = NULL; -// -// (There is no typesafe way to distinguish between stretchy -// buffers and regular arrays/pointers; this is necessary to -// make ordinary array indexing work on these objects.) -// -// Unlike C++ vector<>, the stretchy_buffer has the same -// semantics as an object that you manually malloc and realloc. -// The pointer may relocate every time you add a new object -// to it, so you: -// -// 1. can't take long-term pointers to elements of the array -// 2. have to return the pointer from functions which might expand it -// (either as a return value or by passing it back) -// -// Now you can do the following things with this array: -// -// sb_free(TYPE *a) free the array -// sb_count(TYPE *a) the number of elements in the array -// sb_push(TYPE *a, TYPE v) adds v on the end of the array, a la push_back -// sb_add(TYPE *a, int n) adds n uninitialized elements at end of array & returns pointer to first added -// sb_last(TYPE *a) returns an lvalue of the last item in the array -// a[n] access the nth (counting from 0) element of the array -// -// #define STRETCHY_BUFFER_NO_SHORT_NAMES to only export -// names of the form 'stb_sb_' if you have a name that would -// otherwise collide. -// -// Note that these are all macros and many of them evaluate -// their arguments more than once, so the arguments should -// be side-effect-free. -// -// Note that 'TYPE *a' in sb_push and sb_add must be lvalues -// so that the library can overwrite the existing pointer if -// the object has to be reallocated. -// -// In an out-of-memory condition, the code will try to -// set up a null-pointer or otherwise-invalid-pointer -// exception to happen later. It's possible optimizing -// compilers could detect this write-to-null statically -// and optimize away some of the code, but it should only -// be along the failure path. Nevertheless, for more security -// in the face of such compilers, #define STRETCHY_BUFFER_OUT_OF_MEMORY -// to a statement such as assert(0) or exit(1) or something -// to force a failure when out-of-memory occurs. -// -// Common use: -// -// The main application for this is when building a list of -// things with an unknown quantity, either due to loading from -// a file or through a process which produces an unpredictable -// number. -// -// My most common idiom is something like: -// -// SomeStruct *arr = NULL; -// while (something) -// { -// SomeStruct new_one; -// new_one.whatever = whatever; -// new_one.whatup = whatup; -// new_one.foobar = barfoo; -// sb_push(arr, new_one); -// } -// -// and various closely-related factorings of that. For example, -// you might have several functions to create/init new SomeStructs, -// and if you use the above idiom, you might prefer to make them -// return structs rather than take non-const-pointers-to-structs, -// so you can do things like: -// -// SomeStruct *arr = NULL; -// while (something) -// { -// if (case_A) { -// sb_push(arr, some_func1()); -// } else if (case_B) { -// sb_push(arr, some_func2()); -// } else { -// sb_push(arr, some_func3()); -// } -// } -// -// Note that the above relies on the fact that sb_push doesn't -// evaluate its second argument more than once. The macros do -// evaluate the *array* argument multiple times, and numeric -// arguments may be evaluated multiple times, but you can rely -// on the second argument of sb_push being evaluated only once. -// -// Of course, you don't have to store bare objects in the array; -// if you need the objects to have stable pointers, store an array -// of pointers instead: -// -// SomeStruct **arr = NULL; -// while (something) -// { -// SomeStruct *new_one = malloc(sizeof(*new_one)); -// new_one->whatever = whatever; -// new_one->whatup = whatup; -// new_one->foobar = barfoo; -// sb_push(arr, new_one); -// } -// -// How it works: -// -// A long-standing tradition in things like malloc implementations -// is to store extra data before the beginning of the block returned -// to the user. The stretchy buffer implementation here uses the -// same trick; the current-count and current-allocation-size are -// stored before the beginning of the array returned to the user. -// (This means you can't directly free() the pointer, because the -// allocated pointer is different from the type-safe pointer provided -// to the user.) -// -// The details are trivial and implementation is straightforward; -// the main trick is in realizing in the first place that it's -// possible to do this in a generic, type-safe way in C. -// -// LICENSE -// -// This software is dual-licensed to the public domain and under the following -// license: you are granted a perpetual, irrevocable license to copy, modify, -// publish, and distribute this file as you see fit. - -#ifndef STB_STRETCHY_BUFFER_H_INCLUDED -#define STB_STRETCHY_BUFFER_H_INCLUDED - -#ifndef NO_STRETCHY_BUFFER_SHORT_NAMES -#define sb_free stb_sb_free -#define sb_push stb_sb_push -#define sb_count stb_sb_count -#define sb_add stb_sb_add -#define sb_last stb_sb_last -#endif - -#define stb_sb_free(a) ((a) ? free(stb__sbraw(a)),0 : 0) -#define stb_sb_push(a,v) (stb__sbmaybegrow(a,1), (a)[stb__sbn(a)++] = (v)) -#define stb_sb_count(a) ((a) ? stb__sbn(a) : 0) -#define stb_sb_add(a,n) (stb__sbmaybegrow(a,n), stb__sbn(a)+=(n), &(a)[stb__sbn(a)-(n)]) -#define stb_sb_last(a) ((a)[stb__sbn(a)-1]) - -#define stb__sbraw(a) ((int *) (a) - 2) -#define stb__sbm(a) stb__sbraw(a)[0] -#define stb__sbn(a) stb__sbraw(a)[1] - -#define stb__sbneedgrow(a,n) ((a)==0 || stb__sbn(a)+(n) >= stb__sbm(a)) -#define stb__sbmaybegrow(a,n) (stb__sbneedgrow(a,(n)) ? stb__sbgrow(a,n) : 0) -#define stb__sbgrow(a,n) ((a) = stb__sbgrowf((a), (n), sizeof(*(a)))) - -#include - -static void * stb__sbgrowf(void *arr, int increment, int itemsize) -{ - int dbl_cur = arr ? 2*stb__sbm(arr) : 0; - int min_needed = stb_sb_count(arr) + increment; - int m = dbl_cur > min_needed ? dbl_cur : min_needed; - int *p = (int *) realloc(arr ? stb__sbraw(arr) : 0, itemsize * m + sizeof(int)*2); - if (p) { - if (!arr) - p[1] = 0; - p[0] = m; - return p+2; - } else { - #ifdef STRETCHY_BUFFER_OUT_OF_MEMORY - STRETCHY_BUFFER_OUT_OF_MEMORY ; - #endif - return (void *) (2*sizeof(int)); // try to force a NULL pointer exception later - } -} -#endif // STB_STRETCHY_BUFFER_H_INCLUDED diff --git a/impeller/third_party/stb/stb/tests/c_lexer_test.c b/impeller/third_party/stb/stb/tests/c_lexer_test.c deleted file mode 100644 index fda7d7579c2e9..0000000000000 --- a/impeller/third_party/stb/stb/tests/c_lexer_test.c +++ /dev/null @@ -1,3 +0,0 @@ -#define STB_C_LEXER_IMPLEMENTATION -#define STB_C_LEXER_SELF_TEST -#include "../stb_c_lexer.h" diff --git a/impeller/third_party/stb/stb/tests/c_lexer_test.dsp b/impeller/third_party/stb/stb/tests/c_lexer_test.dsp deleted file mode 100644 index 13f87588b7b69..0000000000000 --- a/impeller/third_party/stb/stb/tests/c_lexer_test.dsp +++ /dev/null @@ -1,89 +0,0 @@ -# Microsoft Developer Studio Project File - Name="c_lexer_test" - Package Owner=<4> -# Microsoft Developer Studio Generated Build File, Format Version 6.00 -# ** DO NOT EDIT ** - -# TARGTYPE "Win32 (x86) Console Application" 0x0103 - -CFG=c_lexer_test - Win32 Debug -!MESSAGE This is not a valid makefile. To build this project using NMAKE, -!MESSAGE use the Export Makefile command and run -!MESSAGE -!MESSAGE NMAKE /f "c_lexer_test.mak". -!MESSAGE -!MESSAGE You can specify a configuration when running NMAKE -!MESSAGE by defining the macro CFG on the command line. For example: -!MESSAGE -!MESSAGE NMAKE /f "c_lexer_test.mak" CFG="c_lexer_test - Win32 Debug" -!MESSAGE -!MESSAGE Possible choices for configuration are: -!MESSAGE -!MESSAGE "c_lexer_test - Win32 Release" (based on "Win32 (x86) Console Application") -!MESSAGE "c_lexer_test - Win32 Debug" (based on "Win32 (x86) Console Application") -!MESSAGE - -# Begin Project -# PROP AllowPerConfigDependencies 0 -# PROP Scc_ProjName "" -# PROP Scc_LocalPath "" -CPP=cl.exe -RSC=rc.exe - -!IF "$(CFG)" == "c_lexer_test - Win32 Release" - -# PROP BASE Use_MFC 0 -# PROP BASE Use_Debug_Libraries 0 -# PROP BASE Output_Dir "Release" -# PROP BASE Intermediate_Dir "Release" -# PROP BASE Target_Dir "" -# PROP Use_MFC 0 -# PROP Use_Debug_Libraries 0 -# PROP Output_Dir "Release" -# PROP Intermediate_Dir "Release" -# PROP Target_Dir "" -# ADD BASE CPP /nologo /W3 /GX /O2 /D "WIN32" /D "NDEBUG" /D "_CONSOLE" /D "_MBCS" /YX /FD /c -# ADD CPP /nologo /W3 /GX /O2 /D "WIN32" /D "NDEBUG" /D "_CONSOLE" /D "_MBCS" /YX /FD /c -# ADD BASE RSC /l 0x409 /d "NDEBUG" -# ADD RSC /l 0x409 /d "NDEBUG" -BSC32=bscmake.exe -# ADD BASE BSC32 /nologo -# ADD BSC32 /nologo -LINK32=link.exe -# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:console /machine:I386 -# ADD LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:console /machine:I386 - -!ELSEIF "$(CFG)" == "c_lexer_test - Win32 Debug" - -# PROP BASE Use_MFC 0 -# PROP BASE Use_Debug_Libraries 1 -# PROP BASE Output_Dir "Debug" -# PROP BASE Intermediate_Dir "Debug" -# PROP BASE Target_Dir "" -# PROP Use_MFC 0 -# PROP Use_Debug_Libraries 1 -# PROP Output_Dir "Debug" -# PROP Intermediate_Dir "Debug\c_lexer_test" -# PROP Target_Dir "" -# ADD BASE CPP /nologo /W3 /Gm /GX /ZI /Od /D "WIN32" /D "_DEBUG" /D "_CONSOLE" /D "_MBCS" /YX /FD /GZ /c -# ADD CPP /nologo /W3 /Gm /GX /ZI /Od /D "WIN32" /D "_DEBUG" /D "_CONSOLE" /D "_MBCS" /FD /GZ /c -# SUBTRACT CPP /YX -# ADD BASE RSC /l 0x409 /d "_DEBUG" -# ADD RSC /l 0x409 /d "_DEBUG" -BSC32=bscmake.exe -# ADD BASE BSC32 /nologo -# ADD BSC32 /nologo -LINK32=link.exe -# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:console /debug /machine:I386 /pdbtype:sept -# ADD LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:console /debug /machine:I386 /pdbtype:sept - -!ENDIF - -# Begin Target - -# Name "c_lexer_test - Win32 Release" -# Name "c_lexer_test - Win32 Debug" -# Begin Source File - -SOURCE=.\c_lexer_test.c -# End Source File -# End Target -# End Project diff --git a/impeller/third_party/stb/stb/tests/caveview/README.md b/impeller/third_party/stb/stb/tests/caveview/README.md deleted file mode 100644 index 10da83818e503..0000000000000 --- a/impeller/third_party/stb/stb/tests/caveview/README.md +++ /dev/null @@ -1,85 +0,0 @@ -# FAQ - -### How to run it? - -There's no GUI. Find a directory with Minecraft Anvil files (.mca). -Copy a Minecraft "terrain.png" into that directory (do a google -image search). Run from that directory. - -### How accurate is this as a Minecraft viewer? - -Not very. Many Minecraft blocks are not handled correctly: - -* No redstone, rails, or other "flat" blocks -* No signs, doors, fences, carpets, or other complicated geometry -* Stairs are turned into ramps -* Upper slabs turn into lower slabs -* Wood types only for blocks, not stairs, slabs, etc -* Colored glass becomes regular glass -* Glass panes become glass blocks -* Water is opaque -* Water level is incorrect -* No biome coloration -* Cactus is not shrunk, shows holes -* Chests are not shrunk -* Double-chests draw as two chests -* Pumpkins etc. are not rotated properly -* Torches are drawn hackily, do not attach to walls -* Incorrect textures for blocks that postdate terrain.png -* Transparent textures have black fringes due to non-premultiplied-alpha -* Skylight and block light are combined in a single value -* Only blocks at y=1..255 are shown (not y=0) -* If a 32x32x256 "quad-chunk" needs more than 800K quads, isn't handled (very unlikely) - -Some of these are due to engine limitations, and some of -these are because I didn't make the effort since my -goal was to make a demo for stb_voxel_render.h, not -to make a proper Minecraft viewer. - - -### Could this be turned into a proper Minecraft viewer? - -Yes and no. Yes, you could do it, but no, it wouldn't -really resemble this code that much anymore. - -You could certainly use this engine to -render the parts of Minecraft it works for, but many -of the things it doesn't handle it can't handle at all -(stairs, water, fences, carpets, etc) because it uses -low-precision coordinates to store voxel data. - -You would have to render all of the stuff it doesn't -handle through another rendering path. In a game (not -a viewer) you would need such a path for movable entities -like doors and carts anyway, so possibly handling other -things that way wouldn't be so bad. - -Rails, ladders, and redstone lines could be implemented by -using tex2 to overlay those effects, but you can't rotate -tex1 and tex2 independently, so there may be cases where -the underlying texture needs a different rotation from the -overlaid texture, which would require separate rendering. -Handling redstone's brightness being different from underlying -block's brightness would require separate rendering. - -You can use the face-color effect to do biome coloration, -but the change won't be smooth the way it is in Minecraft. - - -### Why isn't building the mesh data faster? - -Partly because converting from minecraft data is expensive. - -Here is the approximate breakdown of an older version -of this executable and lib that did the building single-threaded. - -* 25% loading & parsing minecraft files (4/5ths of this is my crappy zlib) -* 18% converting from minecraft blockids & lighting to stb blockids & lighting -* 10% reordering from data[z][y]\[x] (minecraft-style) to data[y][x]\[z] (stb-style) -* 40% building mesh data -* 7% uploading mesh data to OpenGL - -I did do significant optimizations after the above, so the -final breakdown is different, but it should give you some -sense of the costs. - diff --git a/impeller/third_party/stb/stb/tests/caveview/cave_main.c b/impeller/third_party/stb/stb/tests/caveview/cave_main.c deleted file mode 100644 index d345cf1e35b97..0000000000000 --- a/impeller/third_party/stb/stb/tests/caveview/cave_main.c +++ /dev/null @@ -1,598 +0,0 @@ -#define _WIN32_WINNT 0x400 - -#include -#include - -// stb.h -#define STB_DEFINE -#include "stb.h" - -// stb_gl.h -#define STB_GL_IMPLEMENTATION -#define STB_GLEXT_DEFINE "glext_list.h" -#include "stb_gl.h" - -// SDL -#include "sdl.h" -#include "SDL_opengl.h" - -// stb_glprog.h -#define STB_GLPROG_IMPLEMENTATION -#define STB_GLPROG_ARB_DEFINE_EXTENSIONS -#include "stb_glprog.h" - -// stb_image.h -#define STB_IMAGE_IMPLEMENTATION -#include "stb_image.h" - -// stb_easy_font.h -#include "stb_easy_font.h" // doesn't require an IMPLEMENTATION - -#include "caveview.h" - -char *game_name = "caveview"; - - -#define REVERSE_DEPTH - - - -static void print_string(float x, float y, char *text, float r, float g, float b) -{ - static char buffer[99999]; - int num_quads; - - num_quads = stb_easy_font_print(x, y, text, NULL, buffer, sizeof(buffer)); - - glColor3f(r,g,b); - glEnableClientState(GL_VERTEX_ARRAY); - glVertexPointer(2, GL_FLOAT, 16, buffer); - glDrawArrays(GL_QUADS, 0, num_quads*4); - glDisableClientState(GL_VERTEX_ARRAY); -} - -static float text_color[3]; -static float pos_x = 10; -static float pos_y = 10; - -static void print(char *text, ...) -{ - char buffer[999]; - va_list va; - va_start(va, text); - vsprintf(buffer, text, va); - va_end(va); - print_string(pos_x, pos_y, buffer, text_color[0], text_color[1], text_color[2]); - pos_y += 10; -} - -float camang[3], camloc[3] = { 60,22,77 }; -float player_zoom = 1.0; -float rotate_view = 0.0; - - -void camera_to_worldspace(float world[3], float cam_x, float cam_y, float cam_z) -{ - float vec[3] = { cam_x, cam_y, cam_z }; - float t[3]; - float s,c; - s = (float) sin(camang[0]*3.141592/180); - c = (float) cos(camang[0]*3.141592/180); - - t[0] = vec[0]; - t[1] = c*vec[1] - s*vec[2]; - t[2] = s*vec[1] + c*vec[2]; - - s = (float) sin(camang[2]*3.141592/180); - c = (float) cos(camang[2]*3.141592/180); - world[0] = c*t[0] - s*t[1]; - world[1] = s*t[0] + c*t[1]; - world[2] = t[2]; -} - -// camera worldspace velocity -float cam_vel[3]; - -int controls; - -#define MAX_VEL 150.0f // blocks per second -#define ACCEL 6.0f -#define DECEL 3.0f - -#define STATIC_FRICTION DECEL -#define EFFECTIVE_ACCEL (ACCEL+DECEL) - -// dynamic friction: -// -// if going at MAX_VEL, ACCEL and friction must cancel -// EFFECTIVE_ACCEL = DECEL + DYNAMIC_FRIC*MAX_VEL -#define DYNAMIC_FRICTION (ACCEL/(float)MAX_VEL) - -float view_x_vel = 0; -float view_z_vel = 0; -float pending_view_x; -float pending_view_z; -float pending_view_x; -float pending_view_z; - -void process_tick_raw(float dt) -{ - int i; - float thrust[3] = { 0,0,0 }; - float world_thrust[3]; - - // choose direction to apply thrust - - thrust[0] = (controls & 3)== 1 ? EFFECTIVE_ACCEL : (controls & 3)== 2 ? -EFFECTIVE_ACCEL : 0; - thrust[1] = (controls & 12)== 4 ? EFFECTIVE_ACCEL : (controls & 12)== 8 ? -EFFECTIVE_ACCEL : 0; - thrust[2] = (controls & 48)==16 ? EFFECTIVE_ACCEL : (controls & 48)==32 ? -EFFECTIVE_ACCEL : 0; - - // @TODO clamp thrust[0] & thrust[1] vector length to EFFECTIVE_ACCEL - - camera_to_worldspace(world_thrust, thrust[0], thrust[1], 0); - world_thrust[2] += thrust[2]; - - for (i=0; i < 3; ++i) { - float acc = world_thrust[i]; - cam_vel[i] += acc*dt; - } - - if (cam_vel[0] || cam_vel[1] || cam_vel[2]) - { - float vel = (float) sqrt(cam_vel[0]*cam_vel[0] + cam_vel[1]*cam_vel[1] + cam_vel[2]*cam_vel[2]); - float newvel = vel; - float dec = STATIC_FRICTION + DYNAMIC_FRICTION*vel; - newvel = vel - dec*dt; - if (newvel < 0) - newvel = 0; - cam_vel[0] *= newvel/vel; - cam_vel[1] *= newvel/vel; - cam_vel[2] *= newvel/vel; - } - - camloc[0] += cam_vel[0] * dt; - camloc[1] += cam_vel[1] * dt; - camloc[2] += cam_vel[2] * dt; - - view_x_vel *= (float) pow(0.75, dt); - view_z_vel *= (float) pow(0.75, dt); - - view_x_vel += (pending_view_x - view_x_vel)*dt*60; - view_z_vel += (pending_view_z - view_z_vel)*dt*60; - - pending_view_x -= view_x_vel * dt; - pending_view_z -= view_z_vel * dt; - camang[0] += view_x_vel * dt; - camang[2] += view_z_vel * dt; - camang[0] = stb_clamp(camang[0], -90, 90); - camang[2] = (float) fmod(camang[2], 360); -} - -void process_tick(float dt) -{ - while (dt > 1.0f/60) { - process_tick_raw(1.0f/60); - dt -= 1.0f/60; - } - process_tick_raw(dt); -} - -void update_view(float dx, float dy) -{ - // hard-coded mouse sensitivity, not resolution independent? - pending_view_z -= dx*300; - pending_view_x -= dy*700; -} - -extern int screen_x, screen_y; -extern int is_synchronous_debug; -float render_time; - -extern int chunk_locations, chunks_considered, chunks_in_frustum; -extern int quads_considered, quads_rendered; -extern int chunk_storage_rendered, chunk_storage_considered, chunk_storage_total; -extern int view_dist_in_chunks; -extern int num_threads_active, num_meshes_started, num_meshes_uploaded; -extern float chunk_server_activity; - -static Uint64 start_time, end_time; // render time - -float chunk_server_status[32]; -int chunk_server_pos; - -void draw_stats(void) -{ - int i; - - static Uint64 last_frame_time; - Uint64 cur_time = SDL_GetPerformanceCounter(); - float chunk_server=0; - float frame_time = (cur_time - last_frame_time) / (float) SDL_GetPerformanceFrequency(); - last_frame_time = cur_time; - - chunk_server_status[chunk_server_pos] = chunk_server_activity; - chunk_server_pos = (chunk_server_pos+1) %32; - - for (i=0; i < 32; ++i) - chunk_server += chunk_server_status[i] / 32.0f; - - stb_easy_font_spacing(-0.75); - pos_y = 10; - text_color[0] = text_color[1] = text_color[2] = 1.0f; - print("Frame time: %6.2fms, CPU frame render time: %5.2fms", frame_time*1000, render_time*1000); - print("Tris: %4.1fM drawn of %4.1fM in range", 2*quads_rendered/1000000.0f, 2*quads_considered/1000000.0f); - print("Vbuf storage: %dMB in frustum of %dMB in range of %dMB in cache", chunk_storage_rendered>>20, chunk_storage_considered>>20, chunk_storage_total>>20); - print("Num mesh builds started this frame: %d; num uploaded this frame: %d\n", num_meshes_started, num_meshes_uploaded); - print("QChunks: %3d in frustum of %3d valid of %3d in range", chunks_in_frustum, chunks_considered, chunk_locations); - print("Mesh worker threads active: %d", num_threads_active); - print("View distance: %d blocks", view_dist_in_chunks*16); - print("%s", glGetString(GL_RENDERER)); - - if (is_synchronous_debug) { - text_color[0] = 1.0; - text_color[1] = 0.5; - text_color[2] = 0.5; - print("SLOWNESS: Synchronous debug output is enabled!"); - } -} - -void draw_main(void) -{ - glEnable(GL_CULL_FACE); - glDisable(GL_TEXTURE_2D); - glDisable(GL_LIGHTING); - glEnable(GL_DEPTH_TEST); - #ifdef REVERSE_DEPTH - glDepthFunc(GL_GREATER); - glClearDepth(0); - #else - glDepthFunc(GL_LESS); - glClearDepth(1); - #endif - glDepthMask(GL_TRUE); - glDisable(GL_SCISSOR_TEST); - glClearColor(0.6f,0.7f,0.9f,0.0f); - glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); - - glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); - glColor3f(1,1,1); - glFrontFace(GL_CW); - glEnable(GL_TEXTURE_2D); - glDisable(GL_BLEND); - - - glMatrixMode(GL_PROJECTION); - glLoadIdentity(); - - #ifdef REVERSE_DEPTH - stbgl_Perspective(player_zoom, 90, 70, 3000, 1.0/16); - #else - stbgl_Perspective(player_zoom, 90, 70, 1.0/16, 3000); - #endif - - // now compute where the camera should be - glMatrixMode(GL_MODELVIEW); - glLoadIdentity(); - stbgl_initCamera_zup_facing_y(); - - glRotatef(-camang[0],1,0,0); - glRotatef(-camang[2],0,0,1); - glTranslatef(-camloc[0], -camloc[1], -camloc[2]); - - start_time = SDL_GetPerformanceCounter(); - render_caves(camloc); - end_time = SDL_GetPerformanceCounter(); - - render_time = (end_time - start_time) / (float) SDL_GetPerformanceFrequency(); - - glMatrixMode(GL_PROJECTION); - glLoadIdentity(); - gluOrtho2D(0,screen_x/2,screen_y/2,0); - glMatrixMode(GL_MODELVIEW); - glLoadIdentity(); - glDisable(GL_TEXTURE_2D); - glDisable(GL_BLEND); - glDisable(GL_CULL_FACE); - draw_stats(); -} - - - -#pragma warning(disable:4244; disable:4305; disable:4018) - -#define SCALE 2 - -void error(char *s) -{ - SDL_ShowSimpleMessageBox(SDL_MESSAGEBOX_ERROR, "Error", s, NULL); - exit(0); -} - -void ods(char *fmt, ...) -{ - char buffer[1000]; - va_list va; - va_start(va, fmt); - vsprintf(buffer, fmt, va); - va_end(va); - SDL_Log("%s", buffer); -} - -#define TICKS_PER_SECOND 60 - -static SDL_Window *window; - -extern void draw_main(void); -extern void process_tick(float dt); -extern void editor_init(void); - -void draw(void) -{ - draw_main(); - SDL_GL_SwapWindow(window); -} - - -static int initialized=0; -static float last_dt; - -int screen_x,screen_y; - -float carried_dt = 0; -#define TICKRATE 60 - -float tex2_alpha = 1.0; - -int raw_level_time; - -float global_timer; -int global_hack; - -int loopmode(float dt, int real, int in_client) -{ - if (!initialized) return 0; - - if (!real) - return 0; - - // don't allow more than 6 frames to update at a time - if (dt > 0.075) dt = 0.075; - - global_timer += dt; - - carried_dt += dt; - while (carried_dt > 1.0/TICKRATE) { - if (global_hack) { - tex2_alpha += global_hack / 60.0f; - if (tex2_alpha < 0) tex2_alpha = 0; - if (tex2_alpha > 1) tex2_alpha = 1; - } - //update_input(); - // if the player is dead, stop the sim - carried_dt -= 1.0/TICKRATE; - } - - process_tick(dt); - draw(); - - return 0; -} - -static int quit; - -extern int controls; - -void active_control_set(int key) -{ - controls |= 1 << key; -} - -void active_control_clear(int key) -{ - controls &= ~(1 << key); -} - -extern void update_view(float dx, float dy); - -void process_sdl_mouse(SDL_Event *e) -{ - update_view((float) e->motion.xrel / screen_x, (float) e->motion.yrel / screen_y); -} - -void process_event(SDL_Event *e) -{ - switch (e->type) { - case SDL_MOUSEMOTION: - process_sdl_mouse(e); - break; - case SDL_MOUSEBUTTONDOWN: - case SDL_MOUSEBUTTONUP: - break; - - case SDL_QUIT: - quit = 1; - break; - - case SDL_WINDOWEVENT: - switch (e->window.event) { - case SDL_WINDOWEVENT_SIZE_CHANGED: - screen_x = e->window.data1; - screen_y = e->window.data2; - loopmode(0,1,0); - break; - } - break; - - case SDL_KEYDOWN: { - int k = e->key.keysym.sym; - int s = e->key.keysym.scancode; - SDL_Keymod mod; - mod = SDL_GetModState(); - if (k == SDLK_ESCAPE) - quit = 1; - - if (s == SDL_SCANCODE_D) active_control_set(0); - if (s == SDL_SCANCODE_A) active_control_set(1); - if (s == SDL_SCANCODE_W) active_control_set(2); - if (s == SDL_SCANCODE_S) active_control_set(3); - if (k == SDLK_SPACE) active_control_set(4); - if (s == SDL_SCANCODE_LCTRL) active_control_set(5); - if (s == SDL_SCANCODE_S) active_control_set(6); - if (s == SDL_SCANCODE_D) active_control_set(7); - if (k == '1') global_hack = !global_hack; - if (k == '2') global_hack = -1; - - #if 0 - if (game_mode == GAME_editor) { - switch (k) { - case SDLK_RIGHT: editor_key(STBTE_scroll_right); break; - case SDLK_LEFT : editor_key(STBTE_scroll_left ); break; - case SDLK_UP : editor_key(STBTE_scroll_up ); break; - case SDLK_DOWN : editor_key(STBTE_scroll_down ); break; - } - switch (s) { - case SDL_SCANCODE_S: editor_key(STBTE_tool_select); break; - case SDL_SCANCODE_B: editor_key(STBTE_tool_brush ); break; - case SDL_SCANCODE_E: editor_key(STBTE_tool_erase ); break; - case SDL_SCANCODE_R: editor_key(STBTE_tool_rectangle ); break; - case SDL_SCANCODE_I: editor_key(STBTE_tool_eyedropper); break; - case SDL_SCANCODE_L: editor_key(STBTE_tool_link); break; - case SDL_SCANCODE_G: editor_key(STBTE_act_toggle_grid); break; - } - if ((e->key.keysym.mod & KMOD_CTRL) && !(e->key.keysym.mod & ~KMOD_CTRL)) { - switch (s) { - case SDL_SCANCODE_X: editor_key(STBTE_act_cut ); break; - case SDL_SCANCODE_C: editor_key(STBTE_act_copy ); break; - case SDL_SCANCODE_V: editor_key(STBTE_act_paste); break; - case SDL_SCANCODE_Z: editor_key(STBTE_act_undo ); break; - case SDL_SCANCODE_Y: editor_key(STBTE_act_redo ); break; - } - } - } - #endif - break; - } - case SDL_KEYUP: { - int k = e->key.keysym.sym; - int s = e->key.keysym.scancode; - if (s == SDL_SCANCODE_D) active_control_clear(0); - if (s == SDL_SCANCODE_A) active_control_clear(1); - if (s == SDL_SCANCODE_W) active_control_clear(2); - if (s == SDL_SCANCODE_S) active_control_clear(3); - if (k == SDLK_SPACE) active_control_clear(4); - if (s == SDL_SCANCODE_LCTRL) active_control_clear(5); - if (s == SDL_SCANCODE_S) active_control_clear(6); - if (s == SDL_SCANCODE_D) active_control_clear(7); - break; - } - } -} - -static SDL_GLContext *context; - -static float getTimestep(float minimum_time) -{ - float elapsedTime; - double thisTime; - static double lastTime = -1; - - if (lastTime == -1) - lastTime = SDL_GetTicks() / 1000.0 - minimum_time; - - for(;;) { - thisTime = SDL_GetTicks() / 1000.0; - elapsedTime = (float) (thisTime - lastTime); - if (elapsedTime >= minimum_time) { - lastTime = thisTime; - return elapsedTime; - } - // @TODO: compute correct delay - SDL_Delay(1); - } -} - -void APIENTRY gl_debug(GLenum source, GLenum type, GLuint id, GLenum severity, GLsizei length, const GLchar *message, const void *param) -{ - ods("%s\n", message); -} - -int is_synchronous_debug; -void enable_synchronous(void) -{ - glEnable(GL_DEBUG_OUTPUT_SYNCHRONOUS_ARB); - is_synchronous_debug = 1; -} - -void prepare_threads(void); - -//void stbwingraph_main(void) -int SDL_main(int argc, char **argv) -{ - SDL_Init(SDL_INIT_VIDEO); - - prepare_threads(); - - SDL_GL_SetAttribute(SDL_GL_RED_SIZE , 8); - SDL_GL_SetAttribute(SDL_GL_GREEN_SIZE, 8); - SDL_GL_SetAttribute(SDL_GL_BLUE_SIZE , 8); - SDL_GL_SetAttribute(SDL_GL_DEPTH_SIZE, 24); - - SDL_GL_SetAttribute(SDL_GL_CONTEXT_PROFILE_MASK, SDL_GL_CONTEXT_PROFILE_COMPATIBILITY); - SDL_GL_SetAttribute(SDL_GL_CONTEXT_MAJOR_VERSION, 3); - SDL_GL_SetAttribute(SDL_GL_CONTEXT_MINOR_VERSION, 1); - - #ifdef GL_DEBUG - SDL_GL_SetAttribute(SDL_GL_CONTEXT_FLAGS, SDL_GL_CONTEXT_DEBUG_FLAG); - #endif - - SDL_GL_SetAttribute(SDL_GL_MULTISAMPLESAMPLES, 4); - - screen_x = 1920; - screen_y = 1080; - - window = SDL_CreateWindow("caveview", SDL_WINDOWPOS_UNDEFINED, SDL_WINDOWPOS_UNDEFINED, - screen_x, screen_y, - SDL_WINDOW_OPENGL | SDL_WINDOW_RESIZABLE - ); - if (!window) error("Couldn't create window"); - - context = SDL_GL_CreateContext(window); - if (!context) error("Couldn't create context"); - - SDL_GL_MakeCurrent(window, context); // is this true by default? - - SDL_SetRelativeMouseMode(SDL_TRUE); - #if defined(_MSC_VER) && _MSC_VER < 1300 - // work around broken behavior in VC6 debugging - if (IsDebuggerPresent()) - SDL_SetHint(SDL_HINT_MOUSE_RELATIVE_MODE_WARP, "1"); - #endif - - stbgl_initExtensions(); - - #ifdef GL_DEBUG - if (glDebugMessageCallbackARB) { - glDebugMessageCallbackARB(gl_debug, NULL); - - enable_synchronous(); - } - #endif - - SDL_GL_SetSwapInterval(1); - - render_init(); - mesh_init(); - world_init(); - - initialized = 1; - - while (!quit) { - SDL_Event e; - while (SDL_PollEvent(&e)) - process_event(&e); - - loopmode(getTimestep(0.0166f/8), 1, 1); - } - - return 0; -} diff --git a/impeller/third_party/stb/stb/tests/caveview/cave_mesher.c b/impeller/third_party/stb/stb/tests/caveview/cave_mesher.c deleted file mode 100644 index 1fac2972fbd53..0000000000000 --- a/impeller/third_party/stb/stb/tests/caveview/cave_mesher.c +++ /dev/null @@ -1,928 +0,0 @@ -// This file takes minecraft chunks (decoded by cave_parse) and -// uses stb_voxel_render to turn them into vertex buffers. - -#define STB_GLEXT_DECLARE "glext_list.h" -#include "stb_gl.h" -#include "stb_image.h" -#include "stb_glprog.h" - -#include "caveview.h" -#include "cave_parse.h" -#include "stb.h" -#include "sdl.h" -#include "sdl_thread.h" -#include - -//#define VHEIGHT_TEST -//#define STBVOX_OPTIMIZED_VHEIGHT - -#define STBVOX_CONFIG_MODE 1 -#define STBVOX_CONFIG_OPENGL_MODELVIEW -#define STBVOX_CONFIG_PREFER_TEXBUFFER -//#define STBVOX_CONFIG_LIGHTING_SIMPLE -#define STBVOX_CONFIG_FOG_SMOOTHSTEP -//#define STBVOX_CONFIG_PREMULTIPLIED_ALPHA // this doesn't work properly alpha test without next #define -//#define STBVOX_CONFIG_UNPREMULTIPLY // slower, fixes alpha test makes windows & fancy leaves look better -//#define STBVOX_CONFIG_TEX1_EDGE_CLAMP -#define STBVOX_CONFIG_DISABLE_TEX2 -//#define STBVOX_CONFIG_DOWN_TEXLERP_PACKED -#define STBVOX_CONFIG_ROTATION_IN_LIGHTING - -#define STB_VOXEL_RENDER_IMPLEMENTATION -#include "stb_voxel_render.h" - -extern void ods(char *fmt, ...); - -//#define FANCY_LEAVES // nearly 2x the triangles when enabled (if underground is filled) -#define FAST_CHUNK -#define IN_PLACE - -#define SKIP_TERRAIN 48 // use to avoid building underground stuff - // allows you to see what perf would be like if underground was efficiently culled, - // or if you were making a game without underground - -enum -{ - C_empty, - C_solid, - C_trans, - C_cross, - C_water, - C_slab, - C_stair, - C_force, -}; - -unsigned char geom_map[] = -{ - STBVOX_GEOM_empty, - STBVOX_GEOM_solid, - STBVOX_GEOM_transp, - STBVOX_GEOM_crossed_pair, - STBVOX_GEOM_solid, - STBVOX_GEOM_slab_lower, - STBVOX_GEOM_floor_slope_north_is_top, - STBVOX_GEOM_force, -}; - -unsigned char minecraft_info[256][7] = -{ - { C_empty, 0,0,0,0,0,0 }, - { C_solid, 1,1,1,1,1,1 }, - { C_solid, 3,3,3,3,40,2 }, - { C_solid, 2,2,2,2,2,2 }, - { C_solid, 16,16,16,16,16,16 }, - { C_solid, 4,4,4,4,4,4 }, - { C_cross, 15,15,15,15 }, - { C_solid, 17,17,17,17,17,17 }, - - // 8 - { C_water, 223,223,223,223,223,223 }, - { C_water, 223,223,223,223,223,223 }, - { C_solid, 255,255,255,255,255,255 }, - { C_solid, 255,255,255,255,255,255 }, - { C_solid, 18,18,18,18,18,18 }, - { C_solid, 19,19,19,19,19,19 }, - { C_solid, 32,32,32,32,32,32 }, - { C_solid, 33,33,33,33,33,33 }, - - // 16 - { C_solid, 34,34,34,34,34,34 }, - { C_solid, 20,20,20,20,21,21 }, -#ifdef FANCY_LEAVES - { C_force, 52,52,52,52,52,52 }, // leaves -#else - { C_solid, 53,53,53,53,53,53 }, // leaves -#endif - { C_solid, 24,24,24,24,24,24 }, - { C_trans, 49,49,49,49,49,49 }, // glass - { C_solid, 160,160,160,160,160,160 }, - { C_solid, 144,144,144,144,144,144 }, - { C_solid, 46,45,45,45,62,62 }, - - // 24 - { C_solid, 192,192,192,192, 176,176 }, - { C_solid, 74,74,74,74,74,74 }, - { C_empty }, // bed - { C_empty }, // powered rail - { C_empty }, // detector rail - { C_solid, 106,108,109,108,108,108 }, - { C_empty }, // cobweb=11 - { C_cross, 39,39,39,39 }, - - // 32 - { C_cross, 55,55,55,55,0,0 }, - { C_solid, 107,108,109,108,108,108 }, - { C_empty }, // piston head - { C_solid, 64,64,64,64,64,64 }, // various colors - { C_empty }, // unused - { C_cross, 13,13,13,13,0,0 }, - { C_cross, 12,12,12,12,0,0 }, - { C_cross, 29,29,29,29,0,0 }, - - // 40 - { C_cross, 28,28,28,28,0,0 }, - { C_solid, 23,23,23,23,23,23 }, - { C_solid, 22,22,22,22,22,22 }, - { C_solid, 5,5,5,5,6,6, }, - { C_slab , 5,5,5,5,6,6, }, - { C_solid, 7,7,7,7,7,7, }, - { C_solid, 8,8,8,8,9,10 }, - { C_solid, 35,35,35,35,4,4, }, - - // 48 - { C_solid, 36,36,36,36,36,36 }, - { C_solid, 37,37,37,37,37,37 }, - { C_cross, 80,80,80,80,80,80 }, // torch - { C_empty }, // fire - { C_trans, 65,65,65,65,65,65 }, - { C_stair, 4,4,4,4,4,4 }, - { C_solid, 26,26,26,27,25,25 }, - { C_empty }, // redstone - - // 56 - { C_solid, 50,50,50,50,50,50 }, - { C_solid, 26,26,26,26,26,26 }, - { C_solid, 60,59,59,59,43,43 }, - { C_cross, 95,95,95,95 }, - { C_solid, 2,2,2,2,86,2 }, - { C_solid, 44,45,45,45,62,62 }, - { C_solid, 61,45,45,45,62,62 }, - { C_empty }, // sign - - // 64 - { C_empty }, // door - { C_empty }, // ladder - { C_empty }, // rail - { C_stair, 16,16,16,16,16,16 }, // cobblestone stairs - { C_empty }, // sign - { C_empty }, // lever - { C_empty }, // stone pressure plate - { C_empty }, // iron door - - // 72 - { C_empty }, // wooden pressure - { C_solid, 51,51,51,51,51,51 }, - { C_solid, 51,51,51,51,51,51 }, - { C_empty }, - { C_empty }, - { C_empty }, - { C_empty }, // snow on block below, do as half slab? - { C_solid, 67,67,67,67,67,67 }, - - // 80 - { C_solid, 66,66,66,66,66,66 }, - { C_solid, 70,70,70,70,69,71 }, - { C_solid, 72,72,72,72,72,72 }, - { C_cross, 73,73,73,73,73,73 }, - { C_solid, 74,74,74,74,75,74 }, - { C_empty }, // fence - { C_solid,119,118,118,118,102,102 }, - { C_solid,103,103,103,103,103,103 }, - - // 88 - { C_solid, 104,104,104,104,104,104 }, - { C_solid, 105,105,105,105,105,105 }, - { C_solid, 167,167,167,167,167,167 }, - { C_solid, 120,118,118,118,102,102 }, - { C_empty }, // cake - { C_empty }, // repeater - { C_empty }, // repeater - { C_solid, 49,49,49,49,49,49 }, // colored glass - - // 96 - { C_empty }, - { C_empty }, - { C_solid, 54,54,54,54,54,54 }, - { C_solid, 125,125,125,125,125,125 }, - { C_solid, 126,126,126,126,126,126 }, - { C_empty }, // bars - { C_trans, 49,49,49,49,49,49 }, // glass pane - { C_solid, 136,136,136,136,137,137 }, // melon - - // 104 - { C_empty }, // pumpkin stem - { C_empty }, // melon stem - { C_empty }, // vines - { C_empty }, // gate - { C_stair, 7,7,7,7,7,7, }, // brick stairs - { C_stair, 54,54,54,54,54,54 }, // stone brick stairs - { C_empty }, // mycelium - { C_empty }, // lily pad - - // 112 - { C_solid, 224,224,224,224,224,224 }, - { C_empty }, // nether brick fence - { C_stair, 224,224,224,224,224,224 }, // nether brick stairs - { C_empty }, // nether wart - { C_solid, 182,182,182,182,166,183 }, - { C_empty }, // brewing stand - { C_empty }, // cauldron - { C_empty }, // end portal - - // 120 - { C_solid, 159,159,159,159,158,158 }, - { C_solid, 175,175,175,175,175,175 }, - { C_empty }, // dragon egg - { C_solid, 211,211,211,211,211,211 }, - { C_solid, 212,212,212,212,212,212 }, - { C_solid, 4,4,4,4,4,4, }, // wood double-slab - { C_slab , 4,4,4,4,4,4, }, // wood slab - { C_empty }, // cocoa - - // 128 - { C_solid, 192,192,192,192,176,176 }, // sandstone stairs - { C_solid, 32,32,32,32,32,32 }, // emerald ore - { C_solid, 26,26,26,27,25,25 }, // ender chest - { C_empty }, - { C_empty }, - { C_solid, 23,23,23,23,23,23 }, // emerald block - { C_solid, 198,198,198,198,198,198 }, // spruce stairs - { C_solid, 214,214,214,214,214,214 }, // birch stairs - - // 136 - { C_stair, 199,199,199,199,199,199 }, // jungle stairs - { C_empty }, // command block - { C_empty }, // beacon - { C_slab, 16,16,16,16,16,16 }, // cobblestone wall - { C_empty }, // flower pot - { C_empty }, // carrot - { C_empty }, // potatoes - { C_empty }, // wooden button - - // 144 - { C_empty }, // mob head - { C_empty }, // anvil - { C_solid, 26,26,26,27,25,25 }, // trapped chest - { C_empty }, // weighted pressure plate light - { C_empty }, // weighted pressure plat eheavy - { C_empty }, // comparator inactive - { C_empty }, // comparator active - { C_empty }, // daylight sensor - - // 152 - { C_solid, 135,135,135,135,135,135 }, // redstone block - { C_solid, 0,0,0,0,0,0, }, // nether quartz ore - { C_empty }, // hopper - { C_solid, 22,22,22,22,22,22 }, // quartz block - { C_stair, 22,22,22,22,22,22 }, // quartz stairs - { C_empty }, // activator rail - { C_solid, 46,45,45,45,62,62 }, // dropper - { C_solid, 72,72,72,72,72,72 }, // stained clay - - // 160 - { C_trans, 49,49,49,49,49,49 }, // stained glass pane - #ifdef FANCY_LEAVES - { C_force, 52,52,52,52,52,52 }, // leaves - #else - { C_solid, 53,53,53,53,53,53 }, // acacia leaves - #endif - { C_solid, 20,20,20,20,21,21 }, // acacia tree - { C_solid, 199,199,199,199,199,199 }, // acacia wood stairs - { C_solid, 198,198,198,198,198,198 }, // dark oak stairs - { C_solid, 146,146,146,146,146,146 }, // slime block - - { C_solid, 176,176,176,176,176,176 }, // red sandstone - { C_solid, 176,176,176,176,176,176 }, // red sandstone - - // 168 - { C_empty }, - { C_empty }, - { C_empty }, - { C_empty }, - { C_solid, 72,72,72,72,72,72 }, // hardened clay - { C_empty }, - { C_empty }, - { C_empty }, - - // 176 - { C_empty }, - { C_empty }, - { C_solid, 176,176,176,176,176,176 }, // red sandstone -}; - -unsigned char minecraft_tex1_for_blocktype[256][6]; -unsigned char effective_blocktype[256]; -unsigned char minecraft_color_for_blocktype[256][6]; -unsigned char minecraft_geom_for_blocktype[256]; - -uint8 build_buffer[BUILD_BUFFER_SIZE]; -uint8 face_buffer[FACE_BUFFER_SIZE]; - -//GLuint vbuf, fbuf, fbuf_tex; - -//unsigned char tex1_for_blocktype[256][6]; - -//unsigned char blocktype[34][34][257]; -//unsigned char lighting[34][34][257]; - -// a superchunk is 64x64x256, with the border blocks computed as well, -// which means we need 4x4 chunks plus 16 border chunks plus 4 corner chunks - -#define SUPERCHUNK_X 4 -#define SUPERCHUNK_Y 4 - -unsigned char remap_data[16][16]; -unsigned char remap[256]; -unsigned char rotate_data[4] = { 1,3,2,0 }; - -void convert_fastchunk_inplace(fast_chunk *fc) -{ - int i; - int num_blocks=0, step=0; - unsigned char rot[4096]; - #ifndef IN_PLACE - unsigned char *storage; - #endif - - memset(rot, 0, 4096); - - for (i=0; i < 16; ++i) - num_blocks += fc->blockdata[i] != NULL; - - #ifndef IN_PLACE - storage = malloc(16*16*16*2 * num_blocks); - #endif - - for (i=0; i < 16; ++i) { - if (fc->blockdata[i]) { - int o=0; - unsigned char *bd,*dd,*lt,*sky; - unsigned char *out, *outb; - - // this ordering allows us to determine which data we can safely overwrite for in-place processing - bd = fc->blockdata[i]; - dd = fc->data[i]; - lt = fc->light[i]; - sky = fc->skylight[i]; - - #ifdef IN_PLACE - out = bd; - #else - out = storage + 16*16*16*2*step; - #endif - - // bd is written in place, but also reads from dd - for (o=0; o < 16*16*16/2; o += 1) { - unsigned char v1,v2; - unsigned char d = dd[o]; - v1 = bd[o*2+0]; - v2 = bd[o*2+1]; - - if (remap[v1]) - { - //unsigned char d = bd[o] & 15; - v1 = remap_data[remap[v1]][d&15]; - rot[o*2+0] = rotate_data[d&3]; - } else - v1 = effective_blocktype[v1]; - - if (remap[v2]) - { - //unsigned char d = bd[o] >> 4; - v2 = remap_data[remap[v2]][d>>4]; - rot[o*2+1] = rotate_data[(d>>4)&3]; - } else - v2 = effective_blocktype[v2]; - - out[o*2+0] = v1; - out[o*2+1] = v2; - } - - // this reads from lt & sky - #ifndef IN_PLACE - outb = out + 16*16*16; - ++step; - #endif - - // MC used to write in this order and it makes it possible to compute in-place - if (dd < sky && sky < lt) { - // @TODO go this path always if !IN_PLACE - #ifdef IN_PLACE - outb = dd; - #endif - - for (o=0; o < 16*16*16/2; ++o) { - int bright; - bright = (lt[o]&15)*12 + 15 + (sky[o]&15)*16; - if (bright > 255) bright = 255; - if (bright < 32) bright = 32; - outb[o*2+0] = STBVOX_MAKE_LIGHTING_EXT((unsigned char) bright, (rot[o*2+0]&3)); - - bright = (lt[o]>>4)*12 + 15 + (sky[o]>>4)*16; - if (bright > 255) bright = 255; - if (bright < 32) bright = 32; - outb[o*2+1] = STBVOX_MAKE_LIGHTING_EXT((unsigned char) bright, (rot[o*2+1]&3)); - } - } else { - // @TODO: if blocktype is in between others, this breaks; need to find which side has two pointers, and use that - // overwrite rot[] array, then copy out - #ifdef IN_PLACE - outb = (dd < sky) ? dd : sky; - if (lt < outb) lt = outb; - #endif - - for (o=0; o < 16*16*16/2; ++o) { - int bright; - bright = (lt[o]&15)*12 + 15 + (sky[o]&15)*16; - if (bright > 255) bright = 255; - if (bright < 32) bright = 32; - rot[o*2+0] = STBVOX_MAKE_LIGHTING_EXT((unsigned char) bright, (rot[o*2+0]&3)); - - bright = (lt[o]>>4)*12 + 15 + (sky[o]>>4)*16; - if (bright > 255) bright = 255; - if (bright < 32) bright = 32; - rot[o*2+1] = STBVOX_MAKE_LIGHTING_EXT((unsigned char) bright, (rot[o*2+1]&3)); - } - - memcpy(outb, rot, 4096); - fc->data[i] = outb; - } - - #ifndef IN_PLACE - fc->blockdata[i] = out; - fc->data[i] = outb; - #endif - } - } - - #ifndef IN_PLACE - free(fc->pointer_to_free); - fc->pointer_to_free = storage; - #endif -} - -void make_converted_fastchunk(fast_chunk *fc, int x, int y, int segment, uint8 *sv_blocktype, uint8 *sv_lighting) -{ - int z; - assert(fc == NULL || (fc->refcount > 0 && fc->refcount < 64)); - if (fc == NULL || fc->blockdata[segment] == NULL) { - for (z=0; z < 16; ++z) { - sv_blocktype[z] = C_empty; - sv_lighting[z] = 255; - } - } else { - unsigned char *block = fc->blockdata[segment]; - unsigned char *data = fc->data[segment]; - y = 15-y; - for (z=0; z < 16; ++z) { - sv_blocktype[z] = block[z*256 + y*16 + x]; - sv_lighting [z] = data [z*256 + y*16 + x]; - } - } -} - - -#define CHUNK_CACHE 64 -typedef struct -{ - int valid; - int chunk_x, chunk_y; - fast_chunk *fc; -} cached_converted_chunk; - -cached_converted_chunk chunk_cache[CHUNK_CACHE][CHUNK_CACHE]; -int cache_size = CHUNK_CACHE; - -void reset_cache_size(int size) -{ - int i,j; - for (j=size; j < cache_size; ++j) { - for (i=size; i < cache_size; ++i) { - cached_converted_chunk *ccc = &chunk_cache[j][i]; - if (ccc->valid) { - if (ccc->fc) { - free(ccc->fc->pointer_to_free); - free(ccc->fc); - ccc->fc = NULL; - } - ccc->valid = 0; - } - } - } - cache_size = size; -} - -// this must be called inside mutex -void deref_fastchunk(fast_chunk *fc) -{ - if (fc) { - assert(fc->refcount > 0); - --fc->refcount; - if (fc->refcount == 0) { - free(fc->pointer_to_free); - free(fc); - } - } -} - -SDL_mutex * chunk_cache_mutex; -SDL_mutex * chunk_get_mutex; - -void lock_chunk_get_mutex(void) -{ - SDL_LockMutex(chunk_get_mutex); -} -void unlock_chunk_get_mutex(void) -{ - SDL_UnlockMutex(chunk_get_mutex); -} - -fast_chunk *get_converted_fastchunk(int chunk_x, int chunk_y) -{ - int slot_x = (chunk_x & (cache_size-1)); - int slot_y = (chunk_y & (cache_size-1)); - fast_chunk *fc; - cached_converted_chunk *ccc; - SDL_LockMutex(chunk_cache_mutex); - ccc = &chunk_cache[slot_y][slot_x]; - if (ccc->valid) { - if (ccc->chunk_x == chunk_x && ccc->chunk_y == chunk_y) { - fast_chunk *fc = ccc->fc; - if (fc) - ++fc->refcount; - SDL_UnlockMutex(chunk_cache_mutex); - return fc; - } - if (ccc->fc) { - deref_fastchunk(ccc->fc); - ccc->fc = NULL; - ccc->valid = 0; - } - } - SDL_UnlockMutex(chunk_cache_mutex); - - fc = get_decoded_fastchunk_uncached(chunk_x, -chunk_y); - if (fc) - convert_fastchunk_inplace(fc); - - SDL_LockMutex(chunk_cache_mutex); - // another thread might have updated it, so before we overwrite it... - if (ccc->fc) { - deref_fastchunk(ccc->fc); - ccc->fc = NULL; - } - - if (fc) - fc->refcount = 1; // 1 in the cache - - ccc->chunk_x = chunk_x; - ccc->chunk_y = chunk_y; - ccc->valid = 1; - if (fc) - ++fc->refcount; - ccc->fc = fc; - SDL_UnlockMutex(chunk_cache_mutex); - return fc; -} - -void make_map_segment_for_superchunk_preconvert(int chunk_x, int chunk_y, int segment, fast_chunk *fc_table[4][4], uint8 sv_blocktype[34][34][18], uint8 sv_lighting[34][34][18]) -{ - int a,b; - assert((chunk_x & 1) == 0); - assert((chunk_y & 1) == 0); - for (b=-1; b < 3; ++b) { - for (a=-1; a < 3; ++a) { - int xo = a*16+1; - int yo = b*16+1; - int x,y; - fast_chunk *fc = fc_table[b+1][a+1]; - for (y=0; y < 16; ++y) - for (x=0; x < 16; ++x) - if (xo+x >= 0 && xo+x < 34 && yo+y >= 0 && yo+y < 34) - make_converted_fastchunk(fc,x,y, segment, sv_blocktype[xo+x][yo+y], sv_lighting[xo+x][yo+y]); - } - } -} - -// build 1 mesh covering 2x2 chunks -void build_chunk(int chunk_x, int chunk_y, fast_chunk *fc_table[4][4], raw_mesh *rm) -{ - int a,b,z; - stbvox_input_description *map; - - #ifdef VHEIGHT_TEST - unsigned char vheight[34][34][18]; - #endif - - #ifndef STBVOX_CONFIG_DISABLE_TEX2 - unsigned char tex2_choice[34][34][18]; - #endif - - assert((chunk_x & 1) == 0); - assert((chunk_y & 1) == 0); - - rm->cx = chunk_x; - rm->cy = chunk_y; - - stbvox_set_input_stride(&rm->mm, 34*18, 18); - - assert(rm->mm.input.geometry == NULL); - - map = stbvox_get_input_description(&rm->mm); - map->block_tex1_face = minecraft_tex1_for_blocktype; - map->block_color_face = minecraft_color_for_blocktype; - map->block_geometry = minecraft_geom_for_blocktype; - - stbvox_reset_buffers(&rm->mm); - stbvox_set_buffer(&rm->mm, 0, 0, rm->build_buffer, BUILD_BUFFER_SIZE); - stbvox_set_buffer(&rm->mm, 0, 1, rm->face_buffer , FACE_BUFFER_SIZE); - - map->blocktype = &rm->sv_blocktype[1][1][1]; // this is (0,0,0), but we need to be able to query off the edges - map->lighting = &rm->sv_lighting[1][1][1]; - - // fill in the top two rows of the buffer - for (a=0; a < 34; ++a) { - for (b=0; b < 34; ++b) { - rm->sv_blocktype[a][b][16] = 0; - rm->sv_lighting [a][b][16] = 255; - rm->sv_blocktype[a][b][17] = 0; - rm->sv_lighting [a][b][17] = 255; - } - } - - #ifndef STBVOX_CONFIG_DISABLE_TEX2 - for (a=0; a < 34; ++a) { - for (b=0; b < 34; ++b) { - int px = chunk_x*16 + a - 1; - int py = chunk_y*16 + b - 1; - float dist = (float) sqrt(px*px + py*py); - float s1 = (float) sin(dist / 16), s2, s3; - dist = (float) sqrt((px-80)*(px-80) + (py-50)*(py-50)); - s2 = (float) sin(dist / 11); - for (z=0; z < 18; ++z) { - s3 = (float) sin(z * 3.141592 / 8); - - s3 = s1*s2*s3; - tex2_choice[a][b][z] = 63 & (int) stb_linear_remap(s3,-1,1, -20,83); - } - } - } - #endif - - for (z=256-16; z >= SKIP_TERRAIN; z -= 16) - { - int z0 = z; - int z1 = z+16; - if (z1 == 256) z1 = 255; - - make_map_segment_for_superchunk_preconvert(chunk_x, chunk_y, z >> 4, fc_table, rm->sv_blocktype, rm->sv_lighting); - - map->blocktype = &rm->sv_blocktype[1][1][1-z]; // specify location of 0,0,0 so that accessing z0..z1 gets right data - map->lighting = &rm->sv_lighting[1][1][1-z]; - #ifndef STBVOX_CONFIG_DISABLE_TEX2 - map->tex2 = &tex2_choice[1][1][1-z]; - #endif - - #ifdef VHEIGHT_TEST - // hacky test of vheight - for (a=0; a < 34; ++a) { - for (b=0; b < 34; ++b) { - int c; - for (c=0; c < 17; ++c) { - if (rm->sv_blocktype[a][b][c] != 0 && rm->sv_blocktype[a][b][c+1] == 0) { - // topmost block - vheight[a][b][c] = rand() & 255; - rm->sv_blocktype[a][b][c] = 168; - } else if (c > 0 && rm->sv_blocktype[a][b][c] != 0 && rm->sv_blocktype[a][b][c-1] == 0) { - // bottommost block - vheight[a][b][c] = ((rand() % 3) << 6) + ((rand() % 3) << 4) + ((rand() % 3) << 2) + (rand() % 3); - rm->sv_blocktype[a][b][c] = 169; - } - } - vheight[a][b][c] = STBVOX_MAKE_VHEIGHT(2,2,2,2); // flat top - } - } - map->vheight = &vheight[1][1][1-z]; - #endif - - { - stbvox_set_input_range(&rm->mm, 0,0,z0, 32,32,z1); - stbvox_set_default_mesh(&rm->mm, 0); - stbvox_make_mesh(&rm->mm); - } - - // copy the bottom two rows of data up to the top - for (a=0; a < 34; ++a) { - for (b=0; b < 34; ++b) { - rm->sv_blocktype[a][b][16] = rm->sv_blocktype[a][b][0]; - rm->sv_blocktype[a][b][17] = rm->sv_blocktype[a][b][1]; - rm->sv_lighting [a][b][16] = rm->sv_lighting [a][b][0]; - rm->sv_lighting [a][b][17] = rm->sv_lighting [a][b][1]; - } - } - } - - stbvox_set_mesh_coordinates(&rm->mm, chunk_x*16, chunk_y*16, 0); - stbvox_get_transform(&rm->mm, rm->transform); - - stbvox_set_input_range(&rm->mm, 0,0,0, 32,32,255); - stbvox_get_bounds(&rm->mm, rm->bounds); - - rm->num_quads = stbvox_get_quad_count(&rm->mm, 0); -} - -int next_blocktype = 255; - -unsigned char mc_rot[4] = { 1,3,2,0 }; - -// create blocktypes with rotation baked into type... -// @TODO we no longer need this now that we store rotations -// in lighting -void build_stair_rotations(int blocktype, unsigned char *map) -{ - int i; - - // use the existing block type for floor stairs; allocate a new type for ceil stairs - for (i=0; i < 6; ++i) { - minecraft_color_for_blocktype[next_blocktype][i] = minecraft_color_for_blocktype[blocktype][i]; - minecraft_tex1_for_blocktype [next_blocktype][i] = minecraft_tex1_for_blocktype [blocktype][i]; - } - minecraft_geom_for_blocktype[next_blocktype] = (unsigned char) STBVOX_MAKE_GEOMETRY(STBVOX_GEOM_ceil_slope_north_is_bottom, 0, 0); - minecraft_geom_for_blocktype[ blocktype] = (unsigned char) STBVOX_MAKE_GEOMETRY(STBVOX_GEOM_floor_slope_north_is_top, 0, 0); - - for (i=0; i < 4; ++i) { - map[0+i+8] = map[0+i] = blocktype; - map[4+i+8] = map[4+i] = next_blocktype; - } - --next_blocktype; -} - -void build_wool_variations(int bt, unsigned char *map) -{ - int i,k; - unsigned char tex[16] = { 64, 210, 194, 178, 162, 146, 130, 114, 225, 209, 193, 177, 161, 145, 129, 113 }; - for (i=0; i < 16; ++i) { - if (i == 0) - map[i] = bt; - else { - map[i] = next_blocktype; - for (k=0; k < 6; ++k) { - minecraft_tex1_for_blocktype[next_blocktype][k] = tex[i]; - } - minecraft_geom_for_blocktype[next_blocktype] = minecraft_geom_for_blocktype[bt]; - --next_blocktype; - } - } -} - -void build_wood_variations(int bt, unsigned char *map) -{ - int i,k; - unsigned char tex[4] = { 5, 198, 214, 199 }; - for (i=0; i < 4; ++i) { - if (i == 0) - map[i] = bt; - else { - map[i] = next_blocktype; - for (k=0; k < 6; ++k) { - minecraft_tex1_for_blocktype[next_blocktype][k] = tex[i]; - } - minecraft_geom_for_blocktype[next_blocktype] = minecraft_geom_for_blocktype[bt]; - --next_blocktype; - } - } - map[i] = map[i-1]; - ++i; - for (; i < 16; ++i) - map[i] = bt; -} - -void remap_in_place(int bt, int rm) -{ - int i; - remap[bt] = rm; - for (i=0; i < 16; ++i) - remap_data[rm][i] = bt; -} - - -void mesh_init(void) -{ - int i; - - chunk_cache_mutex = SDL_CreateMutex(); - chunk_get_mutex = SDL_CreateMutex(); - - for (i=0; i < 256; ++i) { - memcpy(minecraft_tex1_for_blocktype[i], minecraft_info[i]+1, 6); - effective_blocktype[i] = (minecraft_info[i][0] == C_empty ? 0 : i); - minecraft_geom_for_blocktype[i] = geom_map[minecraft_info[i][0]]; - } - //effective_blocktype[50] = 0; // delete torches - - for (i=0; i < 6*256; ++i) { - if (minecraft_tex1_for_blocktype[0][i] == 40) - minecraft_color_for_blocktype[0][i] = 38 | 64; // apply to tex1 - if (minecraft_tex1_for_blocktype[0][i] == 39) - minecraft_color_for_blocktype[0][i] = 39 | 64; // apply to tex1 - if (minecraft_tex1_for_blocktype[0][i] == 105) - minecraft_color_for_blocktype[0][i] = 63; // emissive - if (minecraft_tex1_for_blocktype[0][i] == 212) - minecraft_color_for_blocktype[0][i] = 63; // emissive - if (minecraft_tex1_for_blocktype[0][i] == 80) - minecraft_color_for_blocktype[0][i] = 63; // emissive - } - - for (i=0; i < 6; ++i) { - minecraft_color_for_blocktype[172][i] = 47 | 64; // apply to tex1 - minecraft_color_for_blocktype[178][i] = 47 | 64; // apply to tex1 - minecraft_color_for_blocktype[18][i] = 39 | 64; // green - minecraft_color_for_blocktype[161][i] = 37 | 64; // green - minecraft_color_for_blocktype[10][i] = 63; // emissive lava - minecraft_color_for_blocktype[11][i] = 63; // emissive - } - - #ifdef VHEIGHT_TEST - effective_blocktype[168] = 168; - minecraft_tex1_for_blocktype[168][0] = 1; - minecraft_tex1_for_blocktype[168][1] = 1; - minecraft_tex1_for_blocktype[168][2] = 1; - minecraft_tex1_for_blocktype[168][3] = 1; - minecraft_tex1_for_blocktype[168][4] = 1; - minecraft_tex1_for_blocktype[168][5] = 1; - minecraft_geom_for_blocktype[168] = STBVOX_GEOM_floor_vheight_12; - effective_blocktype[169] = 169; - minecraft_tex1_for_blocktype[169][0] = 1; - minecraft_tex1_for_blocktype[169][1] = 1; - minecraft_tex1_for_blocktype[169][2] = 1; - minecraft_tex1_for_blocktype[169][3] = 1; - minecraft_tex1_for_blocktype[169][4] = 1; - minecraft_tex1_for_blocktype[169][5] = 1; - minecraft_geom_for_blocktype[169] = STBVOX_GEOM_ceil_vheight_03; - #endif - - remap[53] = 1; - remap[67] = 2; - remap[108] = 3; - remap[109] = 4; - remap[114] = 5; - remap[136] = 6; - remap[156] = 7; - for (i=0; i < 256; ++i) - if (remap[i]) - build_stair_rotations(i, remap_data[remap[i]]); - remap[35] = 8; - build_wool_variations(35, remap_data[remap[35]]); - remap[5] = 11; - build_wood_variations(5, remap_data[remap[5]]); - - // set the remap flags for these so they write the rotation values - remap_in_place(54, 9); - remap_in_place(146, 10); -} - -// Timing stats while optimizing the single-threaded builder - -// 32..-32, 32..-32, SKIP_TERRAIN=0, !FANCY_LEAVES on 'mcrealm' data set - -// 6.27s - reblocked to do 16 z at a time instead of 256 (still using 66x66x258), 4 meshes in parallel -// 5.96s - reblocked to use FAST_CHUNK (no intermediate data structure) -// 5.45s - unknown change, or previous measurement was wrong - -// 6.12s - use preconverted data, not in-place -// 5.91s - use preconverted, in-place -// 5.34s - preconvert, in-place, avoid dependency chain (suggested by ryg) -// 5.34s - preconvert, in-place, avoid dependency chain, use bit-table instead of byte-table -// 5.50s - preconvert, in-place, branchless - -// 6.42s - non-preconvert, avoid dependency chain (not an error) -// 5.40s - non-preconvert, w/dependency chain (same as earlier) - -// 5.50s - non-FAST_CHUNK, reblocked outer loop for better cache reuse -// 4.73s - FAST_CHUNK non-preconvert, reblocked outer loop -// 4.25s - preconvert, in-place, reblocked outer loop -// 4.18s - preconvert, in-place, unrolled again -// 4.10s - 34x34 1 mesh instead of 66x66 and 4 meshes (will make it easier to do multiple threads) - -// 4.83s - building bitmasks but not using them (2 bits per block, one if empty, one if solid) - -// 5.16s - using empty bitmasks to early out -// 5.01s - using solid & empty bitmasks to early out - "foo" -// 4.64s - empty bitmask only, test 8 at a time, then test geom -// 4.72s - empty bitmask only, 8 at a time, then test bits -// 4.46s - split bitmask building into three loops (each byte is separate) -// 4.42s - further optimize computing bitmask - -// 4.58s - using solid & empty bitmasks to early out, same as "foo" but faster bitmask building -// 4.12s - using solid & empty bitmasks to efficiently test neighbors -// 4.04s - using 16-bit fetches (not endian-independent) -// - note this is first place that beats previous best '4.10s - 34x34 1 mesh' - -// 4.30s - current time with bitmasks disabled again (note was 4.10s earlier) -// 3.95s - bitmasks enabled again, no other changes -// 4.00s - current time with bitmasks disabled again, no other changes -- wide variation that is time dependent? -// (note that most of the numbers listed here are median of 3 values already) -// 3.98s - bitmasks enabled - -// Bitmasks removed from the code as not worth the complexity increase - - - -// Raw data for Q&A: -// -// 26% parsing & loading minecraft files (4/5ths of which is zlib decode) -// 39% building mesh from stb input format -// 18% converting from minecraft blocks to stb blocks -// 9% reordering from minecraft axis order to stb axis order -// 7% uploading vertex buffer to OpenGL diff --git a/impeller/third_party/stb/stb/tests/caveview/cave_parse.c b/impeller/third_party/stb/stb/tests/caveview/cave_parse.c deleted file mode 100644 index e8ae02b7dbbf3..0000000000000 --- a/impeller/third_party/stb/stb/tests/caveview/cave_parse.c +++ /dev/null @@ -1,632 +0,0 @@ -#include -#include -#include -#include - -#define FAST_CHUNK // disabling this enables the old, slower path that deblocks into a regular form - -#include "cave_parse.h" - -#include "stb_image.h" -#include "stb.h" - -#define NUM_CHUNKS_PER_REGION 32 // only on one axis -#define NUM_CHUNKS_PER_REGION_LOG2 5 - -#define NUM_COLUMNS_PER_CHUNK 16 -#define NUM_COLUMNS_PER_CHUNK_LOG2 4 - -uint32 read_uint32_be(FILE *f) -{ - unsigned char data[4]; - fread(data, 1, 4, f); - return (data[0]<<24) + (data[1]<<16) + (data[2]<<8) + data[3]; -} - -typedef struct -{ - uint8 *data; - size_t len; - int x,z; // chunk index - int refcount; // for multi-threading -} compressed_chunk; - -typedef struct -{ - int x,z; - uint32 sector_data[NUM_CHUNKS_PER_REGION][NUM_CHUNKS_PER_REGION]; -} region; - -size_t cached_compressed=0; - -FILE *last_region; -int last_region_x; -int last_region_z; -int opened=0; - -static void open_file(int reg_x, int reg_z) -{ - if (!opened || last_region_x != reg_x || last_region_z != reg_z) { - char filename[256]; - if (last_region != NULL) - fclose(last_region); - sprintf(filename, "r.%d.%d.mca", reg_x, reg_z); - last_region = fopen(filename, "rb"); - last_region_x = reg_x; - last_region_z = reg_z; - opened = 1; - } -} - -static region *load_region(int reg_x, int reg_z) -{ - region *r; - int x,z; - - open_file(reg_x, reg_z); - - r = malloc(sizeof(*r)); - - if (last_region == NULL) { - memset(r, 0, sizeof(*r)); - } else { - fseek(last_region, 0, SEEK_SET); - for (z=0; z < NUM_CHUNKS_PER_REGION; ++z) - for (x=0; x < NUM_CHUNKS_PER_REGION; ++x) - r->sector_data[z][x] = read_uint32_be(last_region); - } - r->x = reg_x; - r->z = reg_z; - - return r; -} - -void free_region(region *r) -{ - free(r); -} - -#define MAX_MAP_REGIONS 64 // in one axis: 64 regions * 32 chunk/region * 16 columns/chunk = 16384 columns -region *regions[MAX_MAP_REGIONS][MAX_MAP_REGIONS]; - -static region *get_region(int reg_x, int reg_z) -{ - int slot_x = reg_x & (MAX_MAP_REGIONS-1); - int slot_z = reg_z & (MAX_MAP_REGIONS-1); - region *r; - - r = regions[slot_z][slot_x]; - - if (r) { - if (r->x == reg_x && r->z == reg_z) - return r; - free_region(r); - } - - r = load_region(reg_x, reg_z); - regions[slot_z][slot_x] = r; - - return r; -} - -// about one region, so size should be ok -#define NUM_CACHED_X 64 -#define NUM_CACHED_Z 64 - -// @TODO: is it really worth caching these? we probably can just -// pull them from the disk cache nearly as efficiently. -// Can test that by setting to 1x1? -compressed_chunk *cached_chunk[NUM_CACHED_Z][NUM_CACHED_X]; - -static void deref_compressed_chunk(compressed_chunk *cc) -{ - assert(cc->refcount > 0); - --cc->refcount; - if (cc->refcount == 0) { - if (cc->data) - free(cc->data); - free(cc); - } -} - -static compressed_chunk *get_compressed_chunk(int chunk_x, int chunk_z) -{ - int slot_x = chunk_x & (NUM_CACHED_X-1); - int slot_z = chunk_z & (NUM_CACHED_Z-1); - compressed_chunk *cc = cached_chunk[slot_z][slot_x]; - - if (cc && cc->x == chunk_x && cc->z == chunk_z) - return cc; - else { - int reg_x = chunk_x >> NUM_CHUNKS_PER_REGION_LOG2; - int reg_z = chunk_z >> NUM_CHUNKS_PER_REGION_LOG2; - region *r = get_region(reg_x, reg_z); - if (cc) { - deref_compressed_chunk(cc); - cached_chunk[slot_z][slot_x] = NULL; - } - cc = malloc(sizeof(*cc)); - cc->x = chunk_x; - cc->z = chunk_z; - { - int subchunk_x = chunk_x & (NUM_CHUNKS_PER_REGION-1); - int subchunk_z = chunk_z & (NUM_CHUNKS_PER_REGION-1); - uint32 code = r->sector_data[subchunk_z][subchunk_x]; - - if (code & 255) { - open_file(reg_x, reg_z); - fseek(last_region, (code>>8)*4096, SEEK_SET); - cc->len = (code&255)*4096; - cc->data = malloc(cc->len); - fread(cc->data, 1, cc->len, last_region); - } else { - cc->len = 0; - cc->data = 0; - } - } - cc->refcount = 1; - cached_chunk[slot_z][slot_x] = cc; - return cc; - } -} - - -// NBT parser -- can automatically parse stuff we don't -// have definitions for, but want to explicitly parse -// stuff we do have definitions for. -// -// option 1: auto-parse everything into data structures, -// then read those -// -// option 2: have a "parse next object" which -// doesn't resolve whether it expands its children -// yet, and then the user either says "expand" or -// "skip" after looking at the name. Anything with -// "children" without names can't go through this -// interface. -// -// Let's try option 2. - - -typedef struct -{ - unsigned char *buffer_start; - unsigned char *buffer_end; - unsigned char *cur; - int nesting; - char temp_buffer[256]; -} nbt; - -enum { TAG_End=0, TAG_Byte=1, TAG_Short=2, TAG_Int=3, TAG_Long=4, - TAG_Float=5, TAG_Double=6, TAG_Byte_Array=7, TAG_String=8, - TAG_List=9, TAG_Compound=10, TAG_Int_Array=11 }; - -static void nbt_get_string_data(unsigned char *data, char *buffer, size_t bufsize) -{ - int len = data[0]*256 + data[1]; - int i; - for (i=0; i < len && i+1 < (int) bufsize; ++i) - buffer[i] = (char) data[i+2]; - buffer[i] = 0; -} - -static char *nbt_peek(nbt *n) -{ - unsigned char type = *n->cur; - if (type == TAG_End) - return NULL; - nbt_get_string_data(n->cur+1, n->temp_buffer, sizeof(n->temp_buffer)); - return n->temp_buffer; -} - -static uint32 nbt_parse_uint32(unsigned char *buffer) -{ - return (buffer[0] << 24) + (buffer[1]<<16) + (buffer[2]<<8) + buffer[3]; -} - -static void nbt_skip(nbt *n); - -// skip an item that doesn't have an id or name prefix (usable in lists) -static void nbt_skip_raw(nbt *n, unsigned char type) -{ - switch (type) { - case TAG_Byte : n->cur += 1; break; - case TAG_Short : n->cur += 2; break; - case TAG_Int : n->cur += 4; break; - case TAG_Long : n->cur += 8; break; - case TAG_Float : n->cur += 4; break; - case TAG_Double: n->cur += 8; break; - case TAG_Byte_Array: n->cur += 4 + 1*nbt_parse_uint32(n->cur); break; - case TAG_Int_Array : n->cur += 4 + 4*nbt_parse_uint32(n->cur); break; - case TAG_String : n->cur += 2 + (n->cur[0]*256 + n->cur[1]); break; - case TAG_List : { - unsigned char list_type = *n->cur++; - unsigned int list_len = nbt_parse_uint32(n->cur); - unsigned int i; - n->cur += 4; // list_len - for (i=0; i < list_len; ++i) - nbt_skip_raw(n, list_type); - break; - } - case TAG_Compound : { - while (*n->cur != TAG_End) - nbt_skip(n); - nbt_skip(n); // skip the TAG_end - break; - } - } - assert(n->cur <= n->buffer_end); -} - -static void nbt_skip(nbt *n) -{ - unsigned char type = *n->cur++; - if (type == TAG_End) - return; - // skip name - n->cur += (n->cur[0]*256 + n->cur[1]) + 2; - nbt_skip_raw(n, type); -} - -// byteswap -static void nbt_swap(unsigned char *ptr, int len) -{ - int i; - for (i=0; i < (len>>1); ++i) { - unsigned char t = ptr[i]; - ptr[i] = ptr[len-1-i]; - ptr[len-1-i] = t; - } -} - -// pass in the expected type, fail if doesn't match -// returns a pointer to the data, byteswapped if appropriate -static void *nbt_get_fromlist(nbt *n, unsigned char type, int *len) -{ - unsigned char *ptr; - assert(type != TAG_Compound); - assert(type != TAG_List); // we could support getting lists of primitives as if they were arrays, but eh - if (len) *len = 1; - ptr = n->cur; - switch (type) { - case TAG_Byte : break; - - case TAG_Short : nbt_swap(ptr, 2); break; - case TAG_Int : nbt_swap(ptr, 4); break; - case TAG_Long : nbt_swap(ptr, 8); break; - case TAG_Float : nbt_swap(ptr, 4); break; - case TAG_Double: nbt_swap(ptr, 8); break; - - case TAG_Byte_Array: - *len = nbt_parse_uint32(ptr); - ptr += 4; - break; - case TAG_Int_Array: { - int i; - *len = nbt_parse_uint32(ptr); - ptr += 4; - for (i=0; i < *len; ++i) - nbt_swap(ptr + 4*i, 4); - break; - } - - default: assert(0); // unhandled case - } - nbt_skip_raw(n, type); - return ptr; -} - -static void *nbt_get(nbt *n, unsigned char type, int *len) -{ - assert(n->cur[0] == type); - n->cur += 3 + (n->cur[1]*256+n->cur[2]); - return nbt_get_fromlist(n, type, len); -} - -static void nbt_begin_compound(nbt *n) // start a compound -{ - assert(*n->cur == TAG_Compound); - // skip header - n->cur += 3 + (n->cur[1]*256 + n->cur[2]); - ++n->nesting; -} - -static void nbt_begin_compound_in_list(nbt *n) // start a compound -{ - ++n->nesting; -} - -static void nbt_end_compound(nbt *n) // end a compound -{ - assert(*n->cur == TAG_End); - assert(n->nesting != 0); - ++n->cur; - --n->nesting; -} - -// @TODO no interface to get lists from lists -static int nbt_begin_list(nbt *n, unsigned char type) -{ - uint32 len; - unsigned char *ptr; - - ptr = n->cur + 3 + (n->cur[1]*256 + n->cur[2]); - if (ptr[0] != type) - return -1; - n->cur = ptr; - len = nbt_parse_uint32(n->cur+1); - assert(n->cur[0] == type); - // @TODO keep a stack with the count to make sure they do it right - ++n->nesting; - n->cur += 5; - return (int) len; -} - -static void nbt_end_list(nbt *n) -{ - --n->nesting; -} - -// raw_block chunk is 16x256x16x4 = 2^(4+8+4+2) = 256KB -// -// if we want to process 64x64x256 at a time, that will be: -// 4*4*256KB => 4MB per area in raw_block -// -// (plus we maybe need to decode adjacent regions) - - -#ifdef FAST_CHUNK -typedef fast_chunk parse_chunk; -#else -typedef chunk parse_chunk; -#endif - -static parse_chunk *minecraft_chunk_parse(unsigned char *data, size_t len) -{ - char *s; - parse_chunk *c = NULL; - - nbt n_store, *n = &n_store; - n->buffer_start = data; - n->buffer_end = data + len; - n->cur = n->buffer_start; - n->nesting = 0; - - nbt_begin_compound(n); - while ((s = nbt_peek(n)) != NULL) { - if (!strcmp(s, "Level")) { - int *height; - c = malloc(sizeof(*c)); - #ifdef FAST_CHUNK - memset(c, 0, sizeof(*c)); - c->pointer_to_free = data; - #else - c->rb[15][15][255].block = 0; - #endif - c->max_y = 0; - - nbt_begin_compound(n); - while ((s = nbt_peek(n)) != NULL) { - if (!strcmp(s, "xPos")) - c->xpos = *(int *) nbt_get(n, TAG_Int, 0); - else if (!strcmp(s, "zPos")) - c->zpos = *(int *) nbt_get(n, TAG_Int, 0); - else if (!strcmp(s, "Sections")) { - int count = nbt_begin_list(n, TAG_Compound), i; - if (count == -1) { - // this not-a-list case happens in The End and I'm not sure - // what it means... possibly one of those silly encodings - // where it's not encoded as a list if there's only one? - // not worth figuring out - nbt_skip(n); - count = -1; - } - for (i=0; i < count; ++i) { - int yi, len; - uint8 *light = NULL, *blocks = NULL, *data = NULL, *skylight = NULL; - nbt_begin_compound_in_list(n); - while ((s = nbt_peek(n)) != NULL) { - if (!strcmp(s, "Y")) - yi = * (uint8 *) nbt_get(n, TAG_Byte, 0); - else if (!strcmp(s, "BlockLight")) { - light = nbt_get(n, TAG_Byte_Array, &len); - assert(len == 2048); - } else if (!strcmp(s, "Blocks")) { - blocks = nbt_get(n, TAG_Byte_Array, &len); - assert(len == 4096); - } else if (!strcmp(s, "Data")) { - data = nbt_get(n, TAG_Byte_Array, &len); - assert(len == 2048); - } else if (!strcmp(s, "SkyLight")) { - skylight = nbt_get(n, TAG_Byte_Array, &len); - assert(len == 2048); - } - } - nbt_end_compound(n); - - assert(yi < 16); - - #ifndef FAST_CHUNK - - // clear data below current max_y - { - int x,z; - while (c->max_y < yi*16) { - for (x=0; x < 16; ++x) - for (z=0; z < 16; ++z) - c->rb[z][x][c->max_y].block = 0; - ++c->max_y; - } - } - - // now assemble the data - { - int x,y,z, o2=0,o4=0; - for (y=0; y < 16; ++y) { - for (z=0; z < 16; ++z) { - for (x=0; x < 16; x += 2) { - raw_block *rb = &c->rb[15-z][x][y + yi*16]; // 15-z because switching to z-up will require flipping an axis - rb[0].block = blocks[o4]; - rb[0].light = light[o2] & 15; - rb[0].data = data[o2] & 15; - rb[0].skylight = skylight[o2] & 15; - - rb[256].block = blocks[o4+1]; - rb[256].light = light[o2] >> 4; - rb[256].data = data[o2] >> 4; - rb[256].skylight = skylight[o2] >> 4; - - o2 += 1; - o4 += 2; - } - } - } - c->max_y += 16; - } - #else - c->blockdata[yi] = blocks; - c->data [yi] = data; - c->light [yi] = light; - c->skylight [yi] = skylight; - #endif - } - //nbt_end_list(n); - } else if (!strcmp(s, "HeightMap")) { - height = nbt_get(n, TAG_Int_Array, &len); - assert(len == 256); - } else - nbt_skip(n); - } - nbt_end_compound(n); - - } else - nbt_skip(n); - } - nbt_end_compound(n); - assert(n->cur == n->buffer_end); - return c; -} - -#define MAX_DECODED_CHUNK_X 64 -#define MAX_DECODED_CHUNK_Z 64 - -typedef struct -{ - int cx,cz; - fast_chunk *fc; - int valid; -} decoded_buffer; - -static decoded_buffer decoded_buffers[MAX_DECODED_CHUNK_Z][MAX_DECODED_CHUNK_X]; -void lock_chunk_get_mutex(void); -void unlock_chunk_get_mutex(void); - -#ifdef FAST_CHUNK -fast_chunk *get_decoded_fastchunk_uncached(int chunk_x, int chunk_z) -{ - unsigned char *decoded; - compressed_chunk *cc; - int inlen; - int len; - fast_chunk *fc; - - lock_chunk_get_mutex(); - cc = get_compressed_chunk(chunk_x, chunk_z); - if (cc->len != 0) - ++cc->refcount; - unlock_chunk_get_mutex(); - - if (cc->len == 0) - return NULL; - - assert(cc != NULL); - - assert(cc->data[4] == 2); - - inlen = nbt_parse_uint32(cc->data); - decoded = stbi_zlib_decode_malloc_guesssize(cc->data+5, inlen, inlen*3, &len); - assert(decoded != NULL); - assert(len != 0); - - lock_chunk_get_mutex(); - deref_compressed_chunk(cc); - unlock_chunk_get_mutex(); - - #ifdef FAST_CHUNK - fc = minecraft_chunk_parse(decoded, len); - #else - fc = NULL; - #endif - if (fc == NULL) - free(decoded); - return fc; -} - - -decoded_buffer *get_decoded_buffer(int chunk_x, int chunk_z) -{ - decoded_buffer *db = &decoded_buffers[chunk_z&(MAX_DECODED_CHUNK_Z-1)][chunk_x&(MAX_DECODED_CHUNK_X-1)]; - if (db->valid) { - if (db->cx == chunk_x && db->cz == chunk_z) - return db; - if (db->fc) { - free(db->fc->pointer_to_free); - free(db->fc); - } - } - - db->cx = chunk_x; - db->cz = chunk_z; - db->valid = 1; - db->fc = 0; - - { - db->fc = get_decoded_fastchunk_uncached(chunk_x, chunk_z); - return db; - } -} - -fast_chunk *get_decoded_fastchunk(int chunk_x, int chunk_z) -{ - decoded_buffer *db = get_decoded_buffer(chunk_x, chunk_z); - return db->fc; -} -#endif - -#ifndef FAST_CHUNK -chunk *get_decoded_chunk_raw(int chunk_x, int chunk_z) -{ - unsigned char *decoded; - compressed_chunk *cc = get_compressed_chunk(chunk_x, chunk_z); - assert(cc != NULL); - if (cc->len == 0) - return NULL; - else { - chunk *ch; - int inlen = nbt_parse_uint32(cc->data); - int len; - assert(cc->data[4] == 2); - decoded = stbi_zlib_decode_malloc_guesssize(cc->data+5, inlen, inlen*3, &len); - assert(decoded != NULL); - #ifdef FAST_CHUNK - ch = NULL; - #else - ch = minecraft_chunk_parse(decoded, len); - #endif - free(decoded); - return ch; - } -} - -static chunk *decoded_chunks[MAX_DECODED_CHUNK_Z][MAX_DECODED_CHUNK_X]; -chunk *get_decoded_chunk(int chunk_x, int chunk_z) -{ - chunk *c = decoded_chunks[chunk_z&(MAX_DECODED_CHUNK_Z-1)][chunk_x&(MAX_DECODED_CHUNK_X-1)]; - if (c && c->xpos == chunk_x && c->zpos == chunk_z) - return c; - if (c) free(c); - c = get_decoded_chunk_raw(chunk_x, chunk_z); - decoded_chunks[chunk_z&(MAX_DECODED_CHUNK_Z-1)][chunk_x&(MAX_DECODED_CHUNK_X-1)] = c; - return c; -} -#endif diff --git a/impeller/third_party/stb/stb/tests/caveview/cave_parse.h b/impeller/third_party/stb/stb/tests/caveview/cave_parse.h deleted file mode 100644 index 4cdfe2ad701ed..0000000000000 --- a/impeller/third_party/stb/stb/tests/caveview/cave_parse.h +++ /dev/null @@ -1,41 +0,0 @@ -#ifndef INCLUDE_CAVE_PARSE_H -#define INCLUDE_CAVE_PARSE_H - -typedef struct -{ - unsigned char block; - unsigned char data; - unsigned char light:4; - unsigned char skylight:4; -} raw_block; - -// this is the old fully-decoded chunk -typedef struct -{ - int xpos, zpos, max_y; - int height[16][16]; - raw_block rb[16][16][256]; // [z][x][y] which becomes [y][x][z] in stb -} chunk; - -chunk *get_decoded_chunk(int chunk_x, int chunk_z); - -#define NUM_SEGMENTS 16 -typedef struct -{ - int max_y, xpos, zpos; - - unsigned char *blockdata[NUM_SEGMENTS]; - unsigned char *data[NUM_SEGMENTS]; - unsigned char *skylight[NUM_SEGMENTS]; - unsigned char *light[NUM_SEGMENTS]; - - void *pointer_to_free; - - int refcount; // this allows multi-threaded building without wrapping in ANOTHER struct -} fast_chunk; - -fast_chunk *get_decoded_fastchunk(int chunk_x, int chunk_z); // cache, never call free() - -fast_chunk *get_decoded_fastchunk_uncached(int chunk_x, int chunk_z); - -#endif diff --git a/impeller/third_party/stb/stb/tests/caveview/cave_render.c b/impeller/third_party/stb/stb/tests/caveview/cave_render.c deleted file mode 100644 index 7ac96ec513a88..0000000000000 --- a/impeller/third_party/stb/stb/tests/caveview/cave_render.c +++ /dev/null @@ -1,951 +0,0 @@ -// This file renders vertex buffers, converts raw meshes -// to GL meshes, and manages threads that do the raw-mesh -// building (found in cave_mesher.c) - - -#include "stb_voxel_render.h" - -#define STB_GLEXT_DECLARE "glext_list.h" -#include "stb_gl.h" -#include "stb_image.h" -#include "stb_glprog.h" - -#include "caveview.h" -#include "cave_parse.h" -#include "stb.h" -#include "sdl.h" -#include "sdl_thread.h" -#include -#include - -//#define STBVOX_CONFIG_TEX1_EDGE_CLAMP - - -// currently no dynamic way to set mesh cache size or view distance -//#define SHORTVIEW - - -stbvox_mesh_maker g_mesh_maker; - -GLuint main_prog; -GLint uniform_locations[64]; - -//#define MAX_QUADS_PER_DRAW (65536 / 4) // assuming 16-bit indices, 4 verts per quad -//#define FIXED_INDEX_BUFFER_SIZE (MAX_QUADS_PER_DRAW * 6 * 2) // 16*1024 * 12 == ~192KB - -// while uploading texture data, this holds our each texture -#define TEX_SIZE 64 -uint32 texture[TEX_SIZE][TEX_SIZE]; - -GLuint voxel_tex[2]; - -// chunk state -enum -{ - STATE_invalid, - STATE_needed, - STATE_requested, - STATE_abandoned, - STATE_valid, -}; - -// mesh is 32x32x255 ... this is hardcoded in that -// a mesh covers 2x2 minecraft chunks, no #defines for it -typedef struct -{ - int state; - int chunk_x, chunk_y; - int num_quads; - float priority; - int vbuf_size, fbuf_size; - - float transform[3][3]; - float bounds[2][3]; - - GLuint vbuf;// vbuf_tex; - GLuint fbuf, fbuf_tex; - -} chunk_mesh; - -void scale_texture(unsigned char *src, int x, int y, int w, int h) -{ - int i,j,k; - assert(w == 256 && h == 256); - for (j=0; j < TEX_SIZE; ++j) { - for (i=0; i < TEX_SIZE; ++i) { - uint32 val=0; - for (k=0; k < 4; ++k) { - val >>= 8; - val += src[ 4*(x+(i>>2)) + 4*w*(y+(j>>2)) + k]<<24; - } - texture[j][i] = val; - } - } -} - -void build_base_texture(int n) -{ - int x,y; - uint32 color = stb_rand() | 0x808080; - for (y=0; ystate == STATE_valid) { - glDeleteTextures(1, &cm->fbuf_tex); - glDeleteBuffersARB(1, &cm->vbuf); - glDeleteBuffersARB(1, &cm->fbuf); - cached_chunk_mesh[slot_y][slot_x].state = STATE_invalid; - } -} - -void upload_mesh(chunk_mesh *cm, uint8 *build_buffer, uint8 *face_buffer) -{ - glGenBuffersARB(1, &cm->vbuf); - glBindBufferARB(GL_ARRAY_BUFFER_ARB, cm->vbuf); - glBufferDataARB(GL_ARRAY_BUFFER_ARB, cm->num_quads*4*sizeof(uint32), build_buffer, GL_STATIC_DRAW_ARB); - glBindBufferARB(GL_ARRAY_BUFFER_ARB, 0); - - glGenBuffersARB(1, &cm->fbuf); - glBindBufferARB(GL_TEXTURE_BUFFER_ARB, cm->fbuf); - glBufferDataARB(GL_TEXTURE_BUFFER_ARB, cm->num_quads*sizeof(uint32), face_buffer , GL_STATIC_DRAW_ARB); - glBindBufferARB(GL_TEXTURE_BUFFER_ARB, 0); - - glGenTextures(1, &cm->fbuf_tex); - glBindTexture(GL_TEXTURE_BUFFER_ARB, cm->fbuf_tex); - glTexBufferARB(GL_TEXTURE_BUFFER_ARB, GL_RGBA8UI, cm->fbuf); - glBindTexture(GL_TEXTURE_BUFFER_ARB, 0); -} - -static void upload_mesh_data(raw_mesh *rm) -{ - int cx = rm->cx; - int cy = rm->cy; - int slot_x = (cx >> 1) & (CACHED_MESH_NUM_X-1); - int slot_y = (cy >> 1) & (CACHED_MESH_NUM_Y-1); - chunk_mesh *cm; - - free_chunk(slot_x, slot_y); - - cm = &cached_chunk_mesh[slot_y][slot_x]; - cm->num_quads = rm->num_quads; - - upload_mesh(cm, rm->build_buffer, rm->face_buffer); - cm->vbuf_size = rm->num_quads*4*sizeof(uint32); - cm->fbuf_size = rm->num_quads*sizeof(uint32); - cm->priority = 100000; - cm->chunk_x = cx; - cm->chunk_y = cy; - - memcpy(cm->bounds, rm->bounds, sizeof(cm->bounds)); - memcpy(cm->transform, rm->transform, sizeof(cm->transform)); - - // write barrier here - cm->state = STATE_valid; -} - -GLint uniform_loc[16]; -float table3[128][3]; -float table4[64][4]; -GLint tablei[2]; - -float step=0; - -#ifdef SHORTVIEW -int view_dist_in_chunks = 50; -#else -int view_dist_in_chunks = 80; -#endif - -void setup_uniforms(float pos[3]) -{ - int i,j; - step += 1.0f/60.0f; - for (i=0; i < STBVOX_UNIFORM_count; ++i) { - stbvox_uniform_info raw, *ui=&raw; - stbvox_get_uniform_info(&raw, i); - uniform_loc[i] = -1; - - if (i == STBVOX_UNIFORM_texscale || i == STBVOX_UNIFORM_texgen || i == STBVOX_UNIFORM_color_table) - continue; - - if (ui) { - void *data = ui->default_value; - uniform_loc[i] = stbgl_find_uniform(main_prog, ui->name); - switch (i) { - case STBVOX_UNIFORM_face_data: - tablei[0] = 2; - data = tablei; - break; - - case STBVOX_UNIFORM_tex_array: - glActiveTextureARB(GL_TEXTURE0_ARB); - glBindTexture(GL_TEXTURE_2D_ARRAY_EXT, voxel_tex[0]); - glActiveTextureARB(GL_TEXTURE1_ARB); - glBindTexture(GL_TEXTURE_2D_ARRAY_EXT, voxel_tex[1]); - glActiveTextureARB(GL_TEXTURE0_ARB); - tablei[0] = 0; - tablei[1] = 1; - data = tablei; - break; - - case STBVOX_UNIFORM_color_table: - data = ui->default_value; - ((float *)data)[63*4+3] = 2.0f; // emissive - break; - - case STBVOX_UNIFORM_camera_pos: - data = table3[0]; - table3[0][0] = pos[0]; - table3[0][1] = pos[1]; - table3[0][2] = pos[2]; - table3[0][3] = stb_max(0,(float)sin(step*2)*0.125f); - break; - - case STBVOX_UNIFORM_ambient: { - float bright = 1.0; - //float bright = 0.75; - float amb[3][3]; - - // ambient direction is sky-colored upwards - // "ambient" lighting is from above - table4[0][0] = 0.3f; - table4[0][1] = -0.5f; - table4[0][2] = 0.9f; - - amb[1][0] = 0.3f; amb[1][1] = 0.3f; amb[1][2] = 0.3f; // dark-grey - amb[2][0] = 1.0; amb[2][1] = 1.0; amb[2][2] = 1.0; // white - - // convert so (table[1]*dot+table[2]) gives - // above interpolation - // lerp((dot+1)/2, amb[1], amb[2]) - // amb[1] + (amb[2] - amb[1]) * (dot+1)/2 - // amb[1] + (amb[2] - amb[1]) * dot/2 + (amb[2]-amb[1])/2 - - for (j=0; j < 3; ++j) { - table4[1][j] = (amb[2][j] - amb[1][j])/2 * bright; - table4[2][j] = (amb[1][j] + amb[2][j])/2 * bright; - } - - // fog color - table4[3][0] = 0.6f, table4[3][1] = 0.7f, table4[3][2] = 0.9f; - table4[3][3] = 1.0f / (view_dist_in_chunks * 16); - table4[3][3] *= table4[3][3]; - - data = table4; - break; - } - } - - switch (ui->type) { - case STBVOX_UNIFORM_TYPE_sampler: stbglUniform1iv(uniform_loc[i], ui->array_length, data); break; - case STBVOX_UNIFORM_TYPE_vec2: stbglUniform2fv(uniform_loc[i], ui->array_length, data); break; - case STBVOX_UNIFORM_TYPE_vec3: stbglUniform3fv(uniform_loc[i], ui->array_length, data); break; - case STBVOX_UNIFORM_TYPE_vec4: stbglUniform4fv(uniform_loc[i], ui->array_length, data); break; - } - } - } -} - -GLuint unitex[64], unibuf[64]; -void make_texture_buffer_for_uniform(int uniform, int slot) -{ - GLenum type; - stbvox_uniform_info raw, *ui=&raw; - GLint uloc; - - stbvox_get_uniform_info(ui, uniform); - uloc = stbgl_find_uniform(main_prog, ui->name); - - if (uniform == STBVOX_UNIFORM_color_table) - ((float *)ui->default_value)[63*4+3] = 2.0f; // emissive - - glGenBuffersARB(1, &unibuf[uniform]); - glBindBufferARB(GL_ARRAY_BUFFER_ARB, unibuf[uniform]); - glBufferDataARB(GL_ARRAY_BUFFER_ARB, ui->array_length * ui->bytes_per_element, ui->default_value, GL_STATIC_DRAW_ARB); - glBindBufferARB(GL_ARRAY_BUFFER_ARB, 0); - - glGenTextures(1, &unitex[uniform]); - glBindTexture(GL_TEXTURE_BUFFER_ARB, unitex[uniform]); - switch (ui->type) { - case STBVOX_UNIFORM_TYPE_vec2: type = GL_RG32F; break; - case STBVOX_UNIFORM_TYPE_vec3: type = GL_RGB32F; break; - case STBVOX_UNIFORM_TYPE_vec4: type = GL_RGBA32F; break; - default: assert(0); - } - glTexBufferARB(GL_TEXTURE_BUFFER_ARB, type, unibuf[uniform]); - glBindTexture(GL_TEXTURE_BUFFER_ARB, 0); - - glActiveTextureARB(GL_TEXTURE0 + slot); - glBindTexture(GL_TEXTURE_BUFFER_ARB, unitex[uniform]); - glActiveTextureARB(GL_TEXTURE0); - - stbglUseProgram(main_prog); - stbglUniform1i(uloc, slot); -} - -#define MAX_MESH_WORKERS 8 -#define MAX_CHUNK_LOAD_WORKERS 2 - -int num_mesh_workers; -int num_chunk_load_workers; - -typedef struct -{ - int state; - int request_cx; - int request_cy; - int padding[13]; - - SDL_sem * request_received; - - SDL_sem * chunk_server_done_processing; - int chunk_action; - int chunk_request_x; - int chunk_request_y; - fast_chunk *chunks[4][4]; - - int padding2[16]; - raw_mesh rm; - int padding3[16]; - - uint8 *build_buffer; - uint8 *face_buffer ; -} mesh_worker; - -enum -{ - WSTATE_idle, - WSTATE_requested, - WSTATE_running, - WSTATE_mesh_ready, -}; - -mesh_worker mesh_data[MAX_MESH_WORKERS]; -int num_meshes_started; // stats - -int request_chunk(int chunk_x, int chunk_y); -void update_meshes_from_render_thread(void); - -unsigned char tex2_data[64][4]; - -void init_tex2_gradient(void) -{ - int i; - for (i=0; i < 16; ++i) { - tex2_data[i+ 0][0] = 64 + 12*i; - tex2_data[i+ 0][1] = 32; - tex2_data[i+ 0][2] = 64; - - tex2_data[i+16][0] = 255; - tex2_data[i+16][1] = 32 + 8*i; - tex2_data[i+16][2] = 64; - - tex2_data[i+32][0] = 255; - tex2_data[i+32][1] = 160; - tex2_data[i+32][2] = 64 + 12*i; - - tex2_data[i+48][0] = 255; - tex2_data[i+48][1] = 160 + 6*i; - tex2_data[i+48][2] = 255; - } -} - -void set_tex2_alpha(float fa) -{ - int i; - int a = (int) stb_lerp(fa, 0, 255); - if (a < 0) a = 0; else if (a > 255) a = 255; - glBindTexture(GL_TEXTURE_2D_ARRAY_EXT, voxel_tex[1]); - for (i=0; i < 64; ++i) { - tex2_data[i][3] = a; - glTexSubImage3DEXT(GL_TEXTURE_2D_ARRAY_EXT, 0, 0,0,i, 1,1,1, GL_RGBA, GL_UNSIGNED_BYTE, tex2_data[i]); - } -} - -void render_init(void) -{ - int i; - char *binds[] = { "attr_vertex", "attr_face", NULL }; - char *vertex; - char *fragment; - int w=0,h=0; - - unsigned char *texdata = stbi_load("terrain.png", &w, &h, NULL, 4); - - stbvox_init_mesh_maker(&g_mesh_maker); - for (i=0; i < num_mesh_workers; ++i) { - stbvox_init_mesh_maker(&mesh_data[i].rm.mm); - } - - vertex = stbvox_get_vertex_shader(); - fragment = stbvox_get_fragment_shader(); - - { - char error_buffer[1024]; - char *main_vertex[] = { vertex, NULL }; - char *main_fragment[] = { fragment, NULL }; - main_prog = stbgl_create_program(main_vertex, main_fragment, binds, error_buffer, sizeof(error_buffer)); - if (main_prog == 0) { - ods("Compile error for main shader: %s\n", error_buffer); - assert(0); - exit(1); - } - } - //init_index_buffer(); - - make_texture_buffer_for_uniform(STBVOX_UNIFORM_texscale , 3); - make_texture_buffer_for_uniform(STBVOX_UNIFORM_texgen , 4); - make_texture_buffer_for_uniform(STBVOX_UNIFORM_color_table , 5); - - glGenTextures(2, voxel_tex); - - glBindTexture(GL_TEXTURE_2D_ARRAY_EXT, voxel_tex[0]); - glTexImage3DEXT(GL_TEXTURE_2D_ARRAY_EXT, 0, GL_RGBA, - TEX_SIZE,TEX_SIZE,256, - 0,GL_RGBA,GL_UNSIGNED_BYTE,NULL); - for (i=0; i < 256; ++i) { - if (texdata) - scale_texture(texdata, (i&15)*w/16, (h/16)*(i>>4), w,h); - else - build_base_texture(i); - glTexSubImage3DEXT(GL_TEXTURE_2D_ARRAY_EXT, 0, 0,0,i, TEX_SIZE,TEX_SIZE,1, GL_RGBA, GL_UNSIGNED_BYTE, texture[0]); - } - glTexParameteri(GL_TEXTURE_2D_ARRAY_EXT, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR); - glTexParameteri(GL_TEXTURE_2D_ARRAY_EXT, GL_TEXTURE_MAG_FILTER, GL_LINEAR); - glTexParameteri(GL_TEXTURE_2D_ARRAY_EXT, GL_TEXTURE_MAX_ANISOTROPY_EXT, 16); - #ifdef STBVOX_CONFIG_TEX1_EDGE_CLAMP - glTexParameteri(GL_TEXTURE_2D_ARRAY_EXT, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); - glTexParameteri(GL_TEXTURE_2D_ARRAY_EXT, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); - #endif - - glGenerateMipmapEXT(GL_TEXTURE_2D_ARRAY_EXT); - - glBindTexture(GL_TEXTURE_2D_ARRAY_EXT, voxel_tex[1]); - glTexImage3DEXT(GL_TEXTURE_2D_ARRAY_EXT, 0, GL_RGBA, - 1,1,64, - 0,GL_RGBA,GL_UNSIGNED_BYTE,NULL); - init_tex2_gradient(); - set_tex2_alpha(0.0); - #if 0 - for (i=0; i < 128; ++i) { - //build_overlay_texture(i); - glTexSubImage3DEXT(GL_TEXTURE_2D_ARRAY_EXT, 0, 0,0,i, TEX_SIZE,TEX_SIZE,1, GL_RGBA, GL_UNSIGNED_BYTE, texture[0]); - } - #endif - glTexParameteri(GL_TEXTURE_2D_ARRAY_EXT, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR); - glTexParameteri(GL_TEXTURE_2D_ARRAY_EXT, GL_TEXTURE_MAG_FILTER, GL_LINEAR); - glGenerateMipmapEXT(GL_TEXTURE_2D_ARRAY_EXT); -} - -void world_init(void) -{ - int a,b,x,y; - - Uint64 start_time, end_time; - #ifdef NDEBUG - int range = 32; - #else - int range = 12; - #endif - - start_time = SDL_GetPerformanceCounter(); - - // iterate in 8x8 clusters of qchunks at a time to get better converted-chunk-cache reuse - // than a purely row-by-row ordering is (single-threaded this is a bigger win than - // any of the above optimizations were, since it halves zlib/mc-conversion costs) - for (x=-range; x <= range; x += 16) - for (y=-range; y <= range; y += 16) - for (b=y; b < y+16 && b <= range; b += 2) - for (a=x; a < x+16 && a <= range; a += 2) - while (!request_chunk(a, b)) { // if request fails, all threads are busy - update_meshes_from_render_thread(); - SDL_Delay(1); - } - - // wait until all the workers are done, - // (this is only needed if we want to time - // when the build finishes, or when we want to reset the - // cache size; otherwise we could just go ahead and - // start rendering whatever we've got) - for(;;) { - int i; - update_meshes_from_render_thread(); - for (i=0; i < num_mesh_workers; ++i) - if (mesh_data[i].state != WSTATE_idle) - break; - if (i == num_mesh_workers) - break; - SDL_Delay(3); - } - - end_time = SDL_GetPerformanceCounter(); - ods("Build time: %7.2fs\n", (end_time - start_time) / (float) SDL_GetPerformanceFrequency()); - - // don't waste lots of storage on chunk caches once it's finished starting-up; - // this was only needed to be this large because we worked in large blocks - // to maximize sharing - reset_cache_size(32); -} - -extern SDL_mutex * chunk_cache_mutex; - -int mesh_worker_handler(void *data) -{ - mesh_worker *mw = data; - mw->face_buffer = malloc(FACE_BUFFER_SIZE); - mw->build_buffer = malloc(BUILD_BUFFER_SIZE); - - // this loop only works because the compiler can't - // tell that the SDL_calls don't access mw->state; - // really we should barrier that stuff - for(;;) { - int i,j; - int cx,cy; - - // wait for a chunk request - SDL_SemWait(mw->request_received); - - // analyze the chunk request - assert(mw->state == WSTATE_requested); - cx = mw->request_cx; - cy = mw->request_cy; - - // this is inaccurate as it can block while another thread has the cache locked - mw->state = WSTATE_running; - - // get the chunks we need (this takes a lock and caches them) - for (j=0; j < 4; ++j) - for (i=0; i < 4; ++i) - mw->chunks[j][i] = get_converted_fastchunk(cx-1 + i, cy-1 + j); - - // build the mesh based on the chunks - mw->rm.build_buffer = mw->build_buffer; - mw->rm.face_buffer = mw->face_buffer; - build_chunk(cx, cy, mw->chunks, &mw->rm); - mw->state = WSTATE_mesh_ready; - // don't need to notify of this, because it gets polled - - // when done, free the chunks - - // for efficiency we just take the mutex once around the whole thing, - // though this spreads the mutex logic over two files - SDL_LockMutex(chunk_cache_mutex); - for (j=0; j < 4; ++j) - for (i=0; i < 4; ++i) { - deref_fastchunk(mw->chunks[j][i]); - mw->chunks[j][i] = NULL; - } - SDL_UnlockMutex(chunk_cache_mutex); - } - return 0; -} - -int request_chunk(int chunk_x, int chunk_y) -{ - int i; - for (i=0; i < num_mesh_workers; ++i) { - mesh_worker *mw = &mesh_data[i]; - if (mw->state == WSTATE_idle) { - mw->request_cx = chunk_x; - mw->request_cy = chunk_y; - mw->state = WSTATE_requested; - SDL_SemPost(mw->request_received); - ++num_meshes_started; - return 1; - } - } - return 0; -} - -void prepare_threads(void) -{ - int i; - int num_proc = SDL_GetCPUCount(); - - if (num_proc > 6) - num_mesh_workers = num_proc/2; - else if (num_proc > 4) - num_mesh_workers = 4; - else - num_mesh_workers = num_proc-1; - -// @TODO -// Thread usage is probably pretty terrible; need to make a -// separate queue of needed chunks, instead of just generating -// one request per thread per frame, and a separate queue of -// results. (E.g. If it takes 1.5 frames to build mesh, thread -// is idle for 0.5 frames.) To fake this for now, I've just -// doubled the number of threads to let those serve as a 'queue', -// but that's dumb. - - num_mesh_workers *= 2; // try to get better thread usage - - if (num_mesh_workers > MAX_MESH_WORKERS) - num_mesh_workers = MAX_MESH_WORKERS; - - for (i=0; i < num_mesh_workers; ++i) { - mesh_worker *data = &mesh_data[i]; - data->request_received = SDL_CreateSemaphore(0); - data->chunk_server_done_processing = SDL_CreateSemaphore(0); - SDL_CreateThread(mesh_worker_handler, "mesh worker", data); - } -} - - -// "better" buffer uploading -#if 0 - if (glBufferStorage) { - glDeleteBuffersARB(1, &vb->vbuf); - glGenBuffersARB(1, &vb->vbuf); - - glBindBufferARB(GL_ARRAY_BUFFER_ARB, vb->vbuf); - glBufferStorage(GL_ARRAY_BUFFER_ARB, sizeof(build_buffer), build_buffer, 0); - glBindBufferARB(GL_ARRAY_BUFFER_ARB, 0); - } else { - glBindBufferARB(GL_ARRAY_BUFFER_ARB, vb->vbuf); - glBufferDataARB(GL_ARRAY_BUFFER_ARB, sizeof(build_buffer), build_buffer, GL_STATIC_DRAW_ARB); - glBindBufferARB(GL_ARRAY_BUFFER_ARB, 0); - } -#endif - - -typedef struct -{ - float x,y,z,w; -} plane; - -static plane frustum[6]; - -static void matd_mul(double out[4][4], double src1[4][4], double src2[4][4]) -{ - int i,j,k; - for (j=0; j < 4; ++j) { - for (i=0; i < 4; ++i) { - double t=0; - for (k=0; k < 4; ++k) - t += src1[k][i] * src2[j][k]; - out[i][j] = t; - } - } -} - -// https://fgiesen.wordpress.com/2012/08/31/frustum-planes-from-the-projection-matrix/ -static void compute_frustum(void) -{ - int i; - GLdouble mv[4][4],proj[4][4], mvproj[4][4]; - glGetDoublev(GL_MODELVIEW_MATRIX , mv[0]); - glGetDoublev(GL_PROJECTION_MATRIX, proj[0]); - matd_mul(mvproj, proj, mv); - for (i=0; i < 4; ++i) { - (&frustum[0].x)[i] = (float) (mvproj[3][i] + mvproj[0][i]); - (&frustum[1].x)[i] = (float) (mvproj[3][i] - mvproj[0][i]); - (&frustum[2].x)[i] = (float) (mvproj[3][i] + mvproj[1][i]); - (&frustum[3].x)[i] = (float) (mvproj[3][i] - mvproj[1][i]); - (&frustum[4].x)[i] = (float) (mvproj[3][i] + mvproj[2][i]); - (&frustum[5].x)[i] = (float) (mvproj[3][i] - mvproj[2][i]); - } -} - -static int test_plane(plane *p, float x0, float y0, float z0, float x1, float y1, float z1) -{ - // return false if the box is entirely behind the plane - float d=0; - assert(x0 <= x1 && y0 <= y1 && z0 <= z1); - if (p->x > 0) d += x1*p->x; else d += x0*p->x; - if (p->y > 0) d += y1*p->y; else d += y0*p->y; - if (p->z > 0) d += z1*p->z; else d += z0*p->z; - return d + p->w >= 0; -} - -static int is_box_in_frustum(float *bmin, float *bmax) -{ - int i; - for (i=0; i < 5; ++i) - if (!test_plane(&frustum[i], bmin[0], bmin[1], bmin[2], bmax[0], bmax[1], bmax[2])) - return 0; - return 1; -} - -float compute_priority(int cx, int cy, float x, float y) -{ - float distx, disty, dist2; - distx = (cx*16+8) - x; - disty = (cy*16+8) - y; - dist2 = distx*distx + disty*disty; - return view_dist_in_chunks*view_dist_in_chunks * 16 * 16 - dist2; -} - -int chunk_locations, chunks_considered, chunks_in_frustum; -int quads_considered, quads_rendered; -int chunk_storage_rendered, chunk_storage_considered, chunk_storage_total; -int update_frustum = 1; - -#ifdef SHORTVIEW -int max_chunk_storage = 450 << 20; -int min_chunk_storage = 350 << 20; -#else -int max_chunk_storage = 900 << 20; -int min_chunk_storage = 800 << 20; -#endif - -float min_priority = -500; // this really wants to be in unit space, not squared space - -int num_meshes_uploaded; - -void update_meshes_from_render_thread(void) -{ - int i; - for (i=0; i < num_mesh_workers; ++i) { - mesh_worker *mw = &mesh_data[i]; - if (mw->state == WSTATE_mesh_ready) { - upload_mesh_data(&mw->rm); - ++num_meshes_uploaded; - mw->state = WSTATE_idle; - } - } -} - -extern float tex2_alpha; -extern int global_hack; -int num_threads_active; -float chunk_server_activity; - -void render_caves(float campos[3]) -{ - float x = campos[0], y = campos[1]; - int qchunk_x, qchunk_y; - int cam_x, cam_y; - int i,j, rad; - - compute_frustum(); - - chunk_locations = chunks_considered = chunks_in_frustum = 0; - quads_considered = quads_rendered = 0; - chunk_storage_total = chunk_storage_considered = chunk_storage_rendered = 0; - - cam_x = (int) floor(x+0.5); - cam_y = (int) floor(y+0.5); - - qchunk_x = (((int) floor(x)+16) >> 5) << 1; - qchunk_y = (((int) floor(y)+16) >> 5) << 1; - - glEnable(GL_ALPHA_TEST); - glAlphaFunc(GL_GREATER, 0.5); - - stbglUseProgram(main_prog); - setup_uniforms(campos); // set uniforms to default values inefficiently - glActiveTextureARB(GL_TEXTURE2_ARB); - stbglEnableVertexAttribArray(0); - - { - float lighting[2][3] = { { campos[0],campos[1],campos[2] }, { 0.75,0.75,0.65f } }; - float bright = 8; - lighting[1][0] *= bright; - lighting[1][1] *= bright; - lighting[1][2] *= bright; - stbglUniform3fv(stbgl_find_uniform(main_prog, "light_source"), 2, lighting[0]); - } - - if (global_hack) - set_tex2_alpha(tex2_alpha); - - num_meshes_uploaded = 0; - update_meshes_from_render_thread(); - - // traverse all in-range chunks and analyze them - for (j=-view_dist_in_chunks; j <= view_dist_in_chunks; j += 2) { - for (i=-view_dist_in_chunks; i <= view_dist_in_chunks; i += 2) { - float priority; - int cx = qchunk_x + i; - int cy = qchunk_y + j; - - priority = compute_priority(cx, cy, x, y); - if (priority >= min_priority) { - int slot_x = (cx>>1) & (CACHED_MESH_NUM_X-1); - int slot_y = (cy>>1) & (CACHED_MESH_NUM_Y-1); - chunk_mesh *cm = &cached_chunk_mesh[slot_y][slot_x]; - ++chunk_locations; - if (cm->state == STATE_valid && priority >= 0) { - // check if chunk pos actually matches - if (cm->chunk_x != cx || cm->chunk_y != cy) { - // we have a stale chunk we need to recreate - free_chunk(slot_x, slot_y); // it probably will have already gotten freed, but just in case - } - } - if (cm->state == STATE_invalid) { - cm->chunk_x = cx; - cm->chunk_y = cy; - cm->state = STATE_needed; - } - cm->priority = priority; - } - } - } - - // draw front-to-back - for (rad = 0; rad <= view_dist_in_chunks; rad += 2) { - for (j=-rad; j <= rad; j += 2) { - // if j is +- rad, then iterate i through all values - // if j isn't +-rad, then i should be only -rad & rad - int step = 2; - if (abs(j) != rad) - step = 2*rad; - for (i=-rad; i <= rad; i += step) { - int cx = qchunk_x + i; - int cy = qchunk_y + j; - int slot_x = (cx>>1) & (CACHED_MESH_NUM_X-1); - int slot_y = (cy>>1) & (CACHED_MESH_NUM_Y-1); - chunk_mesh *cm = &cached_chunk_mesh[slot_y][slot_x]; - if (cm->state == STATE_valid && cm->priority >= 0) { - ++chunks_considered; - quads_considered += cm->num_quads; - if (is_box_in_frustum(cm->bounds[0], cm->bounds[1])) { - ++chunks_in_frustum; - - // @TODO if in range - stbglUniform3fv(uniform_loc[STBVOX_UNIFORM_transform], 3, cm->transform[0]); - glBindBufferARB(GL_ARRAY_BUFFER_ARB, cm->vbuf); - glVertexAttribIPointer(0, 1, GL_UNSIGNED_INT, 4, (void*) 0); - glBindTexture(GL_TEXTURE_BUFFER_ARB, cm->fbuf_tex); - glDrawArrays(GL_QUADS, 0, cm->num_quads*4); - quads_rendered += cm->num_quads; - - chunk_storage_rendered += cm->vbuf_size + cm->fbuf_size; - } - chunk_storage_considered += cm->vbuf_size + cm->fbuf_size; - } - } - } - } - - stbglDisableVertexAttribArray(0); - glBindBufferARB(GL_ARRAY_BUFFER_ARB, 0); - glActiveTextureARB(GL_TEXTURE0_ARB); - - stbglUseProgram(0); - num_meshes_started = 0; - - { - #define MAX_QUEUE 8 - float highest_priority[MAX_QUEUE]; - int highest_i[MAX_QUEUE], highest_j[MAX_QUEUE]; - float lowest_priority = view_dist_in_chunks * view_dist_in_chunks * 16 * 16.0f; - int lowest_i = -1, lowest_j = -1; - - for (i=0; i < MAX_QUEUE; ++i) { - highest_priority[i] = min_priority; - highest_i[i] = -1; - highest_j[i] = -1; - } - - for (j=0; j < CACHED_MESH_NUM_Y; ++j) { - for (i=0; i < CACHED_MESH_NUM_X; ++i) { - chunk_mesh *cm = &cached_chunk_mesh[j][i]; - if (cm->state == STATE_valid) { - cm->priority = compute_priority(cm->chunk_x, cm->chunk_y, x, y); - chunk_storage_total += cm->vbuf_size + cm->fbuf_size; - if (cm->priority < lowest_priority) { - lowest_priority = cm->priority; - lowest_i = i; - lowest_j = j; - } - } - if (cm->state == STATE_needed) { - cm->priority = compute_priority(cm->chunk_x, cm->chunk_y, x, y); - if (cm->priority < min_priority) - cm->state = STATE_invalid; - else if (cm->priority > highest_priority[0]) { - int k; - highest_priority[0] = cm->priority; - highest_i[0] = i; - highest_j[0] = j; - // bubble this up to right place - for (k=0; k < MAX_QUEUE-1; ++k) { - if (highest_priority[k] > highest_priority[k+1]) { - highest_priority[k] = highest_priority[k+1]; - highest_priority[k+1] = cm->priority; - highest_i[k] = highest_i[k+1]; - highest_i[k+1] = i; - highest_j[k] = highest_j[k+1]; - highest_j[k+1] = j; - } else { - break; - } - } - } - } - } - } - - - // I couldn't find any straightforward logic that avoids - // the hysteresis problem of continually creating & freeing - // a block on the margin, so I just don't free a block until - // it's out of range, but this doesn't actually correctly - // handle when the cache is too small for the given range - if (chunk_storage_total >= min_chunk_storage && lowest_i >= 0) { - if (cached_chunk_mesh[lowest_j][lowest_i].priority < -1200) // -1000? 0? - free_chunk(lowest_i, lowest_j); - } - - if (chunk_storage_total < max_chunk_storage && highest_i[0] >= 0) { - for (j=MAX_QUEUE-1; j >= 0; --j) { - if (highest_j[0] >= 0) { - chunk_mesh *cm = &cached_chunk_mesh[highest_j[j]][highest_i[j]]; - if (request_chunk(cm->chunk_x, cm->chunk_y)) { - cm->state = STATE_requested; - } else { - // if we couldn't queue this one, skip the remainder - break; - } - } - } - } - } - - update_meshes_from_render_thread(); - - num_threads_active = 0; - for (i=0; i < num_mesh_workers; ++i) { - num_threads_active += (mesh_data[i].state == WSTATE_running); - } -} diff --git a/impeller/third_party/stb/stb/tests/caveview/caveview.dsp b/impeller/third_party/stb/stb/tests/caveview/caveview.dsp deleted file mode 100644 index 2a462d3f877fb..0000000000000 --- a/impeller/third_party/stb/stb/tests/caveview/caveview.dsp +++ /dev/null @@ -1,157 +0,0 @@ -# Microsoft Developer Studio Project File - Name="caveview" - Package Owner=<4> -# Microsoft Developer Studio Generated Build File, Format Version 6.00 -# ** DO NOT EDIT ** - -# TARGTYPE "Win32 (x86) Application" 0x0101 - -CFG=caveview - Win32 Debug -!MESSAGE This is not a valid makefile. To build this project using NMAKE, -!MESSAGE use the Export Makefile command and run -!MESSAGE -!MESSAGE NMAKE /f "caveview.mak". -!MESSAGE -!MESSAGE You can specify a configuration when running NMAKE -!MESSAGE by defining the macro CFG on the command line. For example: -!MESSAGE -!MESSAGE NMAKE /f "caveview.mak" CFG="caveview - Win32 Debug" -!MESSAGE -!MESSAGE Possible choices for configuration are: -!MESSAGE -!MESSAGE "caveview - Win32 Release" (based on "Win32 (x86) Application") -!MESSAGE "caveview - Win32 Debug" (based on "Win32 (x86) Application") -!MESSAGE - -# Begin Project -# PROP AllowPerConfigDependencies 0 -# PROP Scc_ProjName "" -# PROP Scc_LocalPath "" -CPP=cl.exe -MTL=midl.exe -RSC=rc.exe - -!IF "$(CFG)" == "caveview - Win32 Release" - -# PROP BASE Use_MFC 0 -# PROP BASE Use_Debug_Libraries 0 -# PROP BASE Output_Dir "Release" -# PROP BASE Intermediate_Dir "Release" -# PROP BASE Target_Dir "" -# PROP Use_MFC 0 -# PROP Use_Debug_Libraries 0 -# PROP Output_Dir "Release" -# PROP Intermediate_Dir "Release" -# PROP Ignore_Export_Lib 0 -# PROP Target_Dir "" -# ADD BASE CPP /nologo /W3 /GX /O2 /D "WIN32" /D "NDEBUG" /D "_WINDOWS" /D "_MBCS" /YX /FD /c -# ADD CPP /nologo /MD /W3 /WX /GX /Zd /O2 /I "../.." /D "WIN32" /D "NDEBUG" /D "_WINDOWS" /D "_MBCS" /FD /c -# SUBTRACT CPP /YX -# ADD BASE MTL /nologo /D "NDEBUG" /mktyplib203 /win32 -# ADD MTL /nologo /D "NDEBUG" /mktyplib203 /win32 -# ADD BASE RSC /l 0x409 /d "NDEBUG" -# ADD RSC /l 0x409 /d "NDEBUG" -BSC32=bscmake.exe -# ADD BASE BSC32 /nologo -# ADD BSC32 /nologo -LINK32=link.exe -# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:windows /machine:I386 -# ADD LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib sdl2.lib opengl32.lib glu32.lib winmm.lib sdl2_mixer.lib advapi32.lib /nologo /subsystem:windows /debug /machine:I386 -# SUBTRACT LINK32 /map - -!ELSEIF "$(CFG)" == "caveview - Win32 Debug" - -# PROP BASE Use_MFC 0 -# PROP BASE Use_Debug_Libraries 1 -# PROP BASE Output_Dir "Debug" -# PROP BASE Intermediate_Dir "Debug" -# PROP BASE Target_Dir "" -# PROP Use_MFC 0 -# PROP Use_Debug_Libraries 1 -# PROP Output_Dir "Debug" -# PROP Intermediate_Dir "Debug" -# PROP Ignore_Export_Lib 0 -# PROP Target_Dir "" -# ADD BASE CPP /nologo /W3 /Gm /GX /ZI /Od /D "WIN32" /D "_DEBUG" /D "_WINDOWS" /D "_MBCS" /YX /FD /GZ /c -# ADD CPP /nologo /MDd /W3 /WX /Gm /GX /Zi /Od /I "../.." /D "WIN32" /D "_DEBUG" /D "_WINDOWS" /D "_MBCS" /FD /GZ /c -# ADD BASE MTL /nologo /D "_DEBUG" /mktyplib203 /win32 -# ADD MTL /nologo /D "_DEBUG" /mktyplib203 /win32 -# ADD BASE RSC /l 0x409 /d "_DEBUG" -# ADD RSC /l 0x409 /d "_DEBUG" -BSC32=bscmake.exe -# ADD BASE BSC32 /nologo -# ADD BSC32 /nologo -LINK32=link.exe -# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:windows /debug /machine:I386 /pdbtype:sept -# ADD LINK32 kernel32.lib user32.lib gdi32.lib advapi32.lib winspool.lib comdlg32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib sdl2.lib opengl32.lib glu32.lib winmm.lib sdl2_mixer.lib /nologo /subsystem:windows /incremental:no /debug /machine:I386 /pdbtype:sept - -!ENDIF - -# Begin Target - -# Name "caveview - Win32 Release" -# Name "caveview - Win32 Debug" -# Begin Source File - -SOURCE=.\cave_main.c -# End Source File -# Begin Source File - -SOURCE=.\cave_mesher.c -# End Source File -# Begin Source File - -SOURCE=.\cave_parse.c -# End Source File -# Begin Source File - -SOURCE=.\cave_parse.h -# End Source File -# Begin Source File - -SOURCE=.\cave_render.c -# End Source File -# Begin Source File - -SOURCE=.\caveview.h -# End Source File -# Begin Source File - -SOURCE=.\glext.h -# End Source File -# Begin Source File - -SOURCE=.\glext_list.h -# End Source File -# Begin Source File - -SOURCE=.\README.md -# End Source File -# Begin Source File - -SOURCE=.\win32\SDL_windows_main.c -# End Source File -# Begin Source File - -SOURCE=..\..\stb.h -# End Source File -# Begin Source File - -SOURCE=..\..\stb_easy_font.h -# End Source File -# Begin Source File - -SOURCE=.\stb_gl.h -# End Source File -# Begin Source File - -SOURCE=.\stb_glprog.h -# End Source File -# Begin Source File - -SOURCE=..\..\stb_image.h -# End Source File -# Begin Source File - -SOURCE=..\..\stb_voxel_render.h -# End Source File -# End Target -# End Project diff --git a/impeller/third_party/stb/stb/tests/caveview/caveview.dsw b/impeller/third_party/stb/stb/tests/caveview/caveview.dsw deleted file mode 100644 index ddc9387e05702..0000000000000 --- a/impeller/third_party/stb/stb/tests/caveview/caveview.dsw +++ /dev/null @@ -1,29 +0,0 @@ -Microsoft Developer Studio Workspace File, Format Version 6.00 -# WARNING: DO NOT EDIT OR DELETE THIS WORKSPACE FILE! - -############################################################################### - -Project: "caveview"=.\caveview.dsp - Package Owner=<4> - -Package=<5> -{{{ -}}} - -Package=<4> -{{{ -}}} - -############################################################################### - -Global: - -Package=<5> -{{{ -}}} - -Package=<3> -{{{ -}}} - -############################################################################### - diff --git a/impeller/third_party/stb/stb/tests/caveview/caveview.h b/impeller/third_party/stb/stb/tests/caveview/caveview.h deleted file mode 100644 index 73a71da0d3b29..0000000000000 --- a/impeller/third_party/stb/stb/tests/caveview/caveview.h +++ /dev/null @@ -1,50 +0,0 @@ -#ifndef INCLUDE_CAVEVIEW_H -#define INCLUDE_CAVEVIEW_H - -#include "stb.h" - -#include "stb_voxel_render.h" - -typedef struct -{ - int cx,cy; - - stbvox_mesh_maker mm; - - uint8 *build_buffer; - uint8 *face_buffer; - - int num_quads; - float transform[3][3]; - float bounds[2][3]; - - uint8 sv_blocktype[34][34][18]; - uint8 sv_lighting [34][34][18]; -} raw_mesh; - -// a 3D checkerboard of empty,solid would be: 32x32x255x6/2 ~= 800000 -// an all-leaf qchunk would be: 32 x 32 x 255 x 6 ~= 1,600,000 - -#define BUILD_QUAD_MAX 400000 -#define BUILD_BUFFER_SIZE (4*4*BUILD_QUAD_MAX) // 4 bytes per vertex, 4 vertices per quad -#define FACE_BUFFER_SIZE ( 4*BUILD_QUAD_MAX) // 4 bytes per quad - - -extern void mesh_init(void); -extern void render_init(void); -extern void world_init(void); -extern void ods(char *fmt, ...); // output debug string -extern void reset_cache_size(int size); - - -extern void render_caves(float pos[3]); - - -#include "cave_parse.h" // fast_chunk - -extern fast_chunk *get_converted_fastchunk(int chunk_x, int chunk_y); -extern void build_chunk(int chunk_x, int chunk_y, fast_chunk *fc_table[4][4], raw_mesh *rm); -extern void reset_cache_size(int size); -extern void deref_fastchunk(fast_chunk *fc); - -#endif \ No newline at end of file diff --git a/impeller/third_party/stb/stb/tests/caveview/glext.h b/impeller/third_party/stb/stb/tests/caveview/glext.h deleted file mode 100644 index c6a233ad17f93..0000000000000 --- a/impeller/third_party/stb/stb/tests/caveview/glext.h +++ /dev/null @@ -1,11124 +0,0 @@ -#ifndef __glext_h_ -#define __glext_h_ 1 - -#ifdef __cplusplus -extern "C" { -#endif - -/* -** Copyright (c) 2013 The Khronos Group Inc. -** -** Permission is hereby granted, free of charge, to any person obtaining a -** copy of this software and/or associated documentation files (the -** "Materials"), to deal in the Materials without restriction, including -** without limitation the rights to use, copy, modify, merge, publish, -** distribute, sublicense, and/or sell copies of the Materials, and to -** permit persons to whom the Materials are furnished to do so, subject to -** the following conditions: -** -** The above copyright notice and this permission notice shall be included -** in all copies or substantial portions of the Materials. -** -** THE MATERIALS ARE PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, -** EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF -** MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. -** IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY -** CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, -** TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE -** MATERIALS OR THE USE OR OTHER DEALINGS IN THE MATERIALS. -*/ -/* -** This header is generated from the Khronos OpenGL / OpenGL ES XML -** API Registry. The current version of the Registry, generator scripts -** used to make the header, and the header can be found at -** http://www.opengl.org/registry/ -** -** Khronos $Revision: 24756 $ on $Date: 2014-01-14 03:42:29 -0800 (Tue, 14 Jan 2014) $ -*/ - -#if defined(_WIN32) && !defined(APIENTRY) && !defined(__CYGWIN__) && !defined(__SCITECH_SNAP__) -#ifndef WIN32_LEAN_AND_MEAN -#define WIN32_LEAN_AND_MEAN 1 -#endif -#include -#endif - -#ifndef APIENTRY -#define APIENTRY -#endif -#ifndef APIENTRYP -#define APIENTRYP APIENTRY * -#endif -#ifndef GLAPI -#define GLAPI extern -#endif - -#define GL_GLEXT_VERSION 20140114 - -/* Generated C header for: - * API: gl - * Profile: compatibility - * Versions considered: .* - * Versions emitted: 1\.[2-9]|[234]\.[0-9] - * Default extensions included: gl - * Additional extensions included: _nomatch_^ - * Extensions removed: _nomatch_^ - */ - -#ifndef GL_VERSION_1_2 -#define GL_VERSION_1_2 1 -#define GL_UNSIGNED_BYTE_3_3_2 0x8032 -#define GL_UNSIGNED_SHORT_4_4_4_4 0x8033 -#define GL_UNSIGNED_SHORT_5_5_5_1 0x8034 -#define GL_UNSIGNED_INT_8_8_8_8 0x8035 -#define GL_UNSIGNED_INT_10_10_10_2 0x8036 -#define GL_TEXTURE_BINDING_3D 0x806A -#define GL_PACK_SKIP_IMAGES 0x806B -#define GL_PACK_IMAGE_HEIGHT 0x806C -#define GL_UNPACK_SKIP_IMAGES 0x806D -#define GL_UNPACK_IMAGE_HEIGHT 0x806E -#define GL_TEXTURE_3D 0x806F -#define GL_PROXY_TEXTURE_3D 0x8070 -#define GL_TEXTURE_DEPTH 0x8071 -#define GL_TEXTURE_WRAP_R 0x8072 -#define GL_MAX_3D_TEXTURE_SIZE 0x8073 -#define GL_UNSIGNED_BYTE_2_3_3_REV 0x8362 -#define GL_UNSIGNED_SHORT_5_6_5 0x8363 -#define GL_UNSIGNED_SHORT_5_6_5_REV 0x8364 -#define GL_UNSIGNED_SHORT_4_4_4_4_REV 0x8365 -#define GL_UNSIGNED_SHORT_1_5_5_5_REV 0x8366 -#define GL_UNSIGNED_INT_8_8_8_8_REV 0x8367 -#define GL_UNSIGNED_INT_2_10_10_10_REV 0x8368 -#define GL_BGR 0x80E0 -#define GL_BGRA 0x80E1 -#define GL_MAX_ELEMENTS_VERTICES 0x80E8 -#define GL_MAX_ELEMENTS_INDICES 0x80E9 -#define GL_CLAMP_TO_EDGE 0x812F -#define GL_TEXTURE_MIN_LOD 0x813A -#define GL_TEXTURE_MAX_LOD 0x813B -#define GL_TEXTURE_BASE_LEVEL 0x813C -#define GL_TEXTURE_MAX_LEVEL 0x813D -#define GL_SMOOTH_POINT_SIZE_RANGE 0x0B12 -#define GL_SMOOTH_POINT_SIZE_GRANULARITY 0x0B13 -#define GL_SMOOTH_LINE_WIDTH_RANGE 0x0B22 -#define GL_SMOOTH_LINE_WIDTH_GRANULARITY 0x0B23 -#define GL_ALIASED_LINE_WIDTH_RANGE 0x846E -#define GL_RESCALE_NORMAL 0x803A -#define GL_LIGHT_MODEL_COLOR_CONTROL 0x81F8 -#define GL_SINGLE_COLOR 0x81F9 -#define GL_SEPARATE_SPECULAR_COLOR 0x81FA -#define GL_ALIASED_POINT_SIZE_RANGE 0x846D -typedef void (APIENTRYP PFNGLDRAWRANGEELEMENTSPROC) (GLenum mode, GLuint start, GLuint end, GLsizei count, GLenum type, const void *indices); -typedef void (APIENTRYP PFNGLTEXIMAGE3DPROC) (GLenum target, GLint level, GLint internalformat, GLsizei width, GLsizei height, GLsizei depth, GLint border, GLenum format, GLenum type, const void *pixels); -typedef void (APIENTRYP PFNGLTEXSUBIMAGE3DPROC) (GLenum target, GLint level, GLint xoffset, GLint yoffset, GLint zoffset, GLsizei width, GLsizei height, GLsizei depth, GLenum format, GLenum type, const void *pixels); -typedef void (APIENTRYP PFNGLCOPYTEXSUBIMAGE3DPROC) (GLenum target, GLint level, GLint xoffset, GLint yoffset, GLint zoffset, GLint x, GLint y, GLsizei width, GLsizei height); -#ifdef GL_GLEXT_PROTOTYPES -GLAPI void APIENTRY glDrawRangeElements (GLenum mode, GLuint start, GLuint end, GLsizei count, GLenum type, const void *indices); -GLAPI void APIENTRY glTexImage3D (GLenum target, GLint level, GLint internalformat, GLsizei width, GLsizei height, GLsizei depth, GLint border, GLenum format, GLenum type, const void *pixels); -GLAPI void APIENTRY glTexSubImage3D (GLenum target, GLint level, GLint xoffset, GLint yoffset, GLint zoffset, GLsizei width, GLsizei height, GLsizei depth, GLenum format, GLenum type, const void *pixels); -GLAPI void APIENTRY glCopyTexSubImage3D (GLenum target, GLint level, GLint xoffset, GLint yoffset, GLint zoffset, GLint x, GLint y, GLsizei width, GLsizei height); -#endif -#endif /* GL_VERSION_1_2 */ - -#ifndef GL_VERSION_1_3 -#define GL_VERSION_1_3 1 -#define GL_TEXTURE0 0x84C0 -#define GL_TEXTURE1 0x84C1 -#define GL_TEXTURE2 0x84C2 -#define GL_TEXTURE3 0x84C3 -#define GL_TEXTURE4 0x84C4 -#define GL_TEXTURE5 0x84C5 -#define GL_TEXTURE6 0x84C6 -#define GL_TEXTURE7 0x84C7 -#define GL_TEXTURE8 0x84C8 -#define GL_TEXTURE9 0x84C9 -#define GL_TEXTURE10 0x84CA -#define GL_TEXTURE11 0x84CB -#define GL_TEXTURE12 0x84CC -#define GL_TEXTURE13 0x84CD -#define GL_TEXTURE14 0x84CE -#define GL_TEXTURE15 0x84CF -#define GL_TEXTURE16 0x84D0 -#define GL_TEXTURE17 0x84D1 -#define GL_TEXTURE18 0x84D2 -#define GL_TEXTURE19 0x84D3 -#define GL_TEXTURE20 0x84D4 -#define GL_TEXTURE21 0x84D5 -#define GL_TEXTURE22 0x84D6 -#define GL_TEXTURE23 0x84D7 -#define GL_TEXTURE24 0x84D8 -#define GL_TEXTURE25 0x84D9 -#define GL_TEXTURE26 0x84DA -#define GL_TEXTURE27 0x84DB -#define GL_TEXTURE28 0x84DC -#define GL_TEXTURE29 0x84DD -#define GL_TEXTURE30 0x84DE -#define GL_TEXTURE31 0x84DF -#define GL_ACTIVE_TEXTURE 0x84E0 -#define GL_MULTISAMPLE 0x809D -#define GL_SAMPLE_ALPHA_TO_COVERAGE 0x809E -#define GL_SAMPLE_ALPHA_TO_ONE 0x809F -#define GL_SAMPLE_COVERAGE 0x80A0 -#define GL_SAMPLE_BUFFERS 0x80A8 -#define GL_SAMPLES 0x80A9 -#define GL_SAMPLE_COVERAGE_VALUE 0x80AA -#define GL_SAMPLE_COVERAGE_INVERT 0x80AB -#define GL_TEXTURE_CUBE_MAP 0x8513 -#define GL_TEXTURE_BINDING_CUBE_MAP 0x8514 -#define GL_TEXTURE_CUBE_MAP_POSITIVE_X 0x8515 -#define GL_TEXTURE_CUBE_MAP_NEGATIVE_X 0x8516 -#define GL_TEXTURE_CUBE_MAP_POSITIVE_Y 0x8517 -#define GL_TEXTURE_CUBE_MAP_NEGATIVE_Y 0x8518 -#define GL_TEXTURE_CUBE_MAP_POSITIVE_Z 0x8519 -#define GL_TEXTURE_CUBE_MAP_NEGATIVE_Z 0x851A -#define GL_PROXY_TEXTURE_CUBE_MAP 0x851B -#define GL_MAX_CUBE_MAP_TEXTURE_SIZE 0x851C -#define GL_COMPRESSED_RGB 0x84ED -#define GL_COMPRESSED_RGBA 0x84EE -#define GL_TEXTURE_COMPRESSION_HINT 0x84EF -#define GL_TEXTURE_COMPRESSED_IMAGE_SIZE 0x86A0 -#define GL_TEXTURE_COMPRESSED 0x86A1 -#define GL_NUM_COMPRESSED_TEXTURE_FORMATS 0x86A2 -#define GL_COMPRESSED_TEXTURE_FORMATS 0x86A3 -#define GL_CLAMP_TO_BORDER 0x812D -#define GL_CLIENT_ACTIVE_TEXTURE 0x84E1 -#define GL_MAX_TEXTURE_UNITS 0x84E2 -#define GL_TRANSPOSE_MODELVIEW_MATRIX 0x84E3 -#define GL_TRANSPOSE_PROJECTION_MATRIX 0x84E4 -#define GL_TRANSPOSE_TEXTURE_MATRIX 0x84E5 -#define GL_TRANSPOSE_COLOR_MATRIX 0x84E6 -#define GL_MULTISAMPLE_BIT 0x20000000 -#define GL_NORMAL_MAP 0x8511 -#define GL_REFLECTION_MAP 0x8512 -#define GL_COMPRESSED_ALPHA 0x84E9 -#define GL_COMPRESSED_LUMINANCE 0x84EA -#define GL_COMPRESSED_LUMINANCE_ALPHA 0x84EB -#define GL_COMPRESSED_INTENSITY 0x84EC -#define GL_COMBINE 0x8570 -#define GL_COMBINE_RGB 0x8571 -#define GL_COMBINE_ALPHA 0x8572 -#define GL_SOURCE0_RGB 0x8580 -#define GL_SOURCE1_RGB 0x8581 -#define GL_SOURCE2_RGB 0x8582 -#define GL_SOURCE0_ALPHA 0x8588 -#define GL_SOURCE1_ALPHA 0x8589 -#define GL_SOURCE2_ALPHA 0x858A -#define GL_OPERAND0_RGB 0x8590 -#define GL_OPERAND1_RGB 0x8591 -#define GL_OPERAND2_RGB 0x8592 -#define GL_OPERAND0_ALPHA 0x8598 -#define GL_OPERAND1_ALPHA 0x8599 -#define GL_OPERAND2_ALPHA 0x859A -#define GL_RGB_SCALE 0x8573 -#define GL_ADD_SIGNED 0x8574 -#define GL_INTERPOLATE 0x8575 -#define GL_SUBTRACT 0x84E7 -#define GL_CONSTANT 0x8576 -#define GL_PRIMARY_COLOR 0x8577 -#define GL_PREVIOUS 0x8578 -#define GL_DOT3_RGB 0x86AE -#define GL_DOT3_RGBA 0x86AF -typedef void (APIENTRYP PFNGLACTIVETEXTUREPROC) (GLenum texture); -typedef void (APIENTRYP PFNGLSAMPLECOVERAGEPROC) (GLfloat value, GLboolean invert); -typedef void (APIENTRYP PFNGLCOMPRESSEDTEXIMAGE3DPROC) (GLenum target, GLint level, GLenum internalformat, GLsizei width, GLsizei height, GLsizei depth, GLint border, GLsizei imageSize, const void *data); -typedef void (APIENTRYP PFNGLCOMPRESSEDTEXIMAGE2DPROC) (GLenum target, GLint level, GLenum internalformat, GLsizei width, GLsizei height, GLint border, GLsizei imageSize, const void *data); -typedef void (APIENTRYP PFNGLCOMPRESSEDTEXIMAGE1DPROC) (GLenum target, GLint level, GLenum internalformat, GLsizei width, GLint border, GLsizei imageSize, const void *data); -typedef void (APIENTRYP PFNGLCOMPRESSEDTEXSUBIMAGE3DPROC) (GLenum target, GLint level, GLint xoffset, GLint yoffset, GLint zoffset, GLsizei width, GLsizei height, GLsizei depth, GLenum format, GLsizei imageSize, const void *data); -typedef void (APIENTRYP PFNGLCOMPRESSEDTEXSUBIMAGE2DPROC) (GLenum target, GLint level, GLint xoffset, GLint yoffset, GLsizei width, GLsizei height, GLenum format, GLsizei imageSize, const void *data); -typedef void (APIENTRYP PFNGLCOMPRESSEDTEXSUBIMAGE1DPROC) (GLenum target, GLint level, GLint xoffset, GLsizei width, GLenum format, GLsizei imageSize, const void *data); -typedef void (APIENTRYP PFNGLGETCOMPRESSEDTEXIMAGEPROC) (GLenum target, GLint level, void *img); -typedef void (APIENTRYP PFNGLCLIENTACTIVETEXTUREPROC) (GLenum texture); -typedef void (APIENTRYP PFNGLMULTITEXCOORD1DPROC) (GLenum target, GLdouble s); -typedef void (APIENTRYP PFNGLMULTITEXCOORD1DVPROC) (GLenum target, const GLdouble *v); -typedef void (APIENTRYP PFNGLMULTITEXCOORD1FPROC) (GLenum target, GLfloat s); -typedef void (APIENTRYP PFNGLMULTITEXCOORD1FVPROC) (GLenum target, const GLfloat *v); -typedef void (APIENTRYP PFNGLMULTITEXCOORD1IPROC) (GLenum target, GLint s); -typedef void (APIENTRYP PFNGLMULTITEXCOORD1IVPROC) (GLenum target, const GLint *v); -typedef void (APIENTRYP PFNGLMULTITEXCOORD1SPROC) (GLenum target, GLshort s); -typedef void (APIENTRYP PFNGLMULTITEXCOORD1SVPROC) (GLenum target, const GLshort *v); -typedef void (APIENTRYP PFNGLMULTITEXCOORD2DPROC) (GLenum target, GLdouble s, GLdouble t); -typedef void (APIENTRYP PFNGLMULTITEXCOORD2DVPROC) (GLenum target, const GLdouble *v); -typedef void (APIENTRYP PFNGLMULTITEXCOORD2FPROC) (GLenum target, GLfloat s, GLfloat t); -typedef void (APIENTRYP PFNGLMULTITEXCOORD2FVPROC) (GLenum target, const GLfloat *v); -typedef void (APIENTRYP PFNGLMULTITEXCOORD2IPROC) (GLenum target, GLint s, GLint t); -typedef void (APIENTRYP PFNGLMULTITEXCOORD2IVPROC) (GLenum target, const GLint *v); -typedef void (APIENTRYP PFNGLMULTITEXCOORD2SPROC) (GLenum target, GLshort s, GLshort t); -typedef void (APIENTRYP PFNGLMULTITEXCOORD2SVPROC) (GLenum target, const GLshort *v); -typedef void (APIENTRYP PFNGLMULTITEXCOORD3DPROC) (GLenum target, GLdouble s, GLdouble t, GLdouble r); -typedef void (APIENTRYP PFNGLMULTITEXCOORD3DVPROC) (GLenum target, const GLdouble *v); -typedef void (APIENTRYP PFNGLMULTITEXCOORD3FPROC) (GLenum target, GLfloat s, GLfloat t, GLfloat r); -typedef void (APIENTRYP PFNGLMULTITEXCOORD3FVPROC) (GLenum target, const GLfloat *v); -typedef void (APIENTRYP PFNGLMULTITEXCOORD3IPROC) (GLenum target, GLint s, GLint t, GLint r); -typedef void (APIENTRYP PFNGLMULTITEXCOORD3IVPROC) (GLenum target, const GLint *v); -typedef void (APIENTRYP PFNGLMULTITEXCOORD3SPROC) (GLenum target, GLshort s, GLshort t, GLshort r); -typedef void (APIENTRYP PFNGLMULTITEXCOORD3SVPROC) (GLenum target, const GLshort *v); -typedef void (APIENTRYP PFNGLMULTITEXCOORD4DPROC) (GLenum target, GLdouble s, GLdouble t, GLdouble r, GLdouble q); -typedef void (APIENTRYP PFNGLMULTITEXCOORD4DVPROC) (GLenum target, const GLdouble *v); -typedef void (APIENTRYP PFNGLMULTITEXCOORD4FPROC) (GLenum target, GLfloat s, GLfloat t, GLfloat r, GLfloat q); -typedef void (APIENTRYP PFNGLMULTITEXCOORD4FVPROC) (GLenum target, const GLfloat *v); -typedef void (APIENTRYP PFNGLMULTITEXCOORD4IPROC) (GLenum target, GLint s, GLint t, GLint r, GLint q); -typedef void (APIENTRYP PFNGLMULTITEXCOORD4IVPROC) (GLenum target, const GLint *v); -typedef void (APIENTRYP PFNGLMULTITEXCOORD4SPROC) (GLenum target, GLshort s, GLshort t, GLshort r, GLshort q); -typedef void (APIENTRYP PFNGLMULTITEXCOORD4SVPROC) (GLenum target, const GLshort *v); -typedef void (APIENTRYP PFNGLLOADTRANSPOSEMATRIXFPROC) (const GLfloat *m); -typedef void (APIENTRYP PFNGLLOADTRANSPOSEMATRIXDPROC) (const GLdouble *m); -typedef void (APIENTRYP PFNGLMULTTRANSPOSEMATRIXFPROC) (const GLfloat *m); -typedef void (APIENTRYP PFNGLMULTTRANSPOSEMATRIXDPROC) (const GLdouble *m); -#ifdef GL_GLEXT_PROTOTYPES -GLAPI void APIENTRY glActiveTexture (GLenum texture); -GLAPI void APIENTRY glSampleCoverage (GLfloat value, GLboolean invert); -GLAPI void APIENTRY glCompressedTexImage3D (GLenum target, GLint level, GLenum internalformat, GLsizei width, GLsizei height, GLsizei depth, GLint border, GLsizei imageSize, const void *data); -GLAPI void APIENTRY glCompressedTexImage2D (GLenum target, GLint level, GLenum internalformat, GLsizei width, GLsizei height, GLint border, GLsizei imageSize, const void *data); -GLAPI void APIENTRY glCompressedTexImage1D (GLenum target, GLint level, GLenum internalformat, GLsizei width, GLint border, GLsizei imageSize, const void *data); -GLAPI void APIENTRY glCompressedTexSubImage3D (GLenum target, GLint level, GLint xoffset, GLint yoffset, GLint zoffset, GLsizei width, GLsizei height, GLsizei depth, GLenum format, GLsizei imageSize, const void *data); -GLAPI void APIENTRY glCompressedTexSubImage2D (GLenum target, GLint level, GLint xoffset, GLint yoffset, GLsizei width, GLsizei height, GLenum format, GLsizei imageSize, const void *data); -GLAPI void APIENTRY glCompressedTexSubImage1D (GLenum target, GLint level, GLint xoffset, GLsizei width, GLenum format, GLsizei imageSize, const void *data); -GLAPI void APIENTRY glGetCompressedTexImage (GLenum target, GLint level, void *img); -GLAPI void APIENTRY glClientActiveTexture (GLenum texture); -GLAPI void APIENTRY glMultiTexCoord1d (GLenum target, GLdouble s); -GLAPI void APIENTRY glMultiTexCoord1dv (GLenum target, const GLdouble *v); -GLAPI void APIENTRY glMultiTexCoord1f (GLenum target, GLfloat s); -GLAPI void APIENTRY glMultiTexCoord1fv (GLenum target, const GLfloat *v); -GLAPI void APIENTRY glMultiTexCoord1i (GLenum target, GLint s); -GLAPI void APIENTRY glMultiTexCoord1iv (GLenum target, const GLint *v); -GLAPI void APIENTRY glMultiTexCoord1s (GLenum target, GLshort s); -GLAPI void APIENTRY glMultiTexCoord1sv (GLenum target, const GLshort *v); -GLAPI void APIENTRY glMultiTexCoord2d (GLenum target, GLdouble s, GLdouble t); -GLAPI void APIENTRY glMultiTexCoord2dv (GLenum target, const GLdouble *v); -GLAPI void APIENTRY glMultiTexCoord2f (GLenum target, GLfloat s, GLfloat t); -GLAPI void APIENTRY glMultiTexCoord2fv (GLenum target, const GLfloat *v); -GLAPI void APIENTRY glMultiTexCoord2i (GLenum target, GLint s, GLint t); -GLAPI void APIENTRY glMultiTexCoord2iv (GLenum target, const GLint *v); -GLAPI void APIENTRY glMultiTexCoord2s (GLenum target, GLshort s, GLshort t); -GLAPI void APIENTRY glMultiTexCoord2sv (GLenum target, const GLshort *v); -GLAPI void APIENTRY glMultiTexCoord3d (GLenum target, GLdouble s, GLdouble t, GLdouble r); -GLAPI void APIENTRY glMultiTexCoord3dv (GLenum target, const GLdouble *v); -GLAPI void APIENTRY glMultiTexCoord3f (GLenum target, GLfloat s, GLfloat t, GLfloat r); -GLAPI void APIENTRY glMultiTexCoord3fv (GLenum target, const GLfloat *v); -GLAPI void APIENTRY glMultiTexCoord3i (GLenum target, GLint s, GLint t, GLint r); -GLAPI void APIENTRY glMultiTexCoord3iv (GLenum target, const GLint *v); -GLAPI void APIENTRY glMultiTexCoord3s (GLenum target, GLshort s, GLshort t, GLshort r); -GLAPI void APIENTRY glMultiTexCoord3sv (GLenum target, const GLshort *v); -GLAPI void APIENTRY glMultiTexCoord4d (GLenum target, GLdouble s, GLdouble t, GLdouble r, GLdouble q); -GLAPI void APIENTRY glMultiTexCoord4dv (GLenum target, const GLdouble *v); -GLAPI void APIENTRY glMultiTexCoord4f (GLenum target, GLfloat s, GLfloat t, GLfloat r, GLfloat q); -GLAPI void APIENTRY glMultiTexCoord4fv (GLenum target, const GLfloat *v); -GLAPI void APIENTRY glMultiTexCoord4i (GLenum target, GLint s, GLint t, GLint r, GLint q); -GLAPI void APIENTRY glMultiTexCoord4iv (GLenum target, const GLint *v); -GLAPI void APIENTRY glMultiTexCoord4s (GLenum target, GLshort s, GLshort t, GLshort r, GLshort q); -GLAPI void APIENTRY glMultiTexCoord4sv (GLenum target, const GLshort *v); -GLAPI void APIENTRY glLoadTransposeMatrixf (const GLfloat *m); -GLAPI void APIENTRY glLoadTransposeMatrixd (const GLdouble *m); -GLAPI void APIENTRY glMultTransposeMatrixf (const GLfloat *m); -GLAPI void APIENTRY glMultTransposeMatrixd (const GLdouble *m); -#endif -#endif /* GL_VERSION_1_3 */ - -#ifndef GL_VERSION_1_4 -#define GL_VERSION_1_4 1 -#define GL_BLEND_DST_RGB 0x80C8 -#define GL_BLEND_SRC_RGB 0x80C9 -#define GL_BLEND_DST_ALPHA 0x80CA -#define GL_BLEND_SRC_ALPHA 0x80CB -#define GL_POINT_FADE_THRESHOLD_SIZE 0x8128 -#define GL_DEPTH_COMPONENT16 0x81A5 -#define GL_DEPTH_COMPONENT24 0x81A6 -#define GL_DEPTH_COMPONENT32 0x81A7 -#define GL_MIRRORED_REPEAT 0x8370 -#define GL_MAX_TEXTURE_LOD_BIAS 0x84FD -#define GL_TEXTURE_LOD_BIAS 0x8501 -#define GL_INCR_WRAP 0x8507 -#define GL_DECR_WRAP 0x8508 -#define GL_TEXTURE_DEPTH_SIZE 0x884A -#define GL_TEXTURE_COMPARE_MODE 0x884C -#define GL_TEXTURE_COMPARE_FUNC 0x884D -#define GL_POINT_SIZE_MIN 0x8126 -#define GL_POINT_SIZE_MAX 0x8127 -#define GL_POINT_DISTANCE_ATTENUATION 0x8129 -#define GL_GENERATE_MIPMAP 0x8191 -#define GL_GENERATE_MIPMAP_HINT 0x8192 -#define GL_FOG_COORDINATE_SOURCE 0x8450 -#define GL_FOG_COORDINATE 0x8451 -#define GL_FRAGMENT_DEPTH 0x8452 -#define GL_CURRENT_FOG_COORDINATE 0x8453 -#define GL_FOG_COORDINATE_ARRAY_TYPE 0x8454 -#define GL_FOG_COORDINATE_ARRAY_STRIDE 0x8455 -#define GL_FOG_COORDINATE_ARRAY_POINTER 0x8456 -#define GL_FOG_COORDINATE_ARRAY 0x8457 -#define GL_COLOR_SUM 0x8458 -#define GL_CURRENT_SECONDARY_COLOR 0x8459 -#define GL_SECONDARY_COLOR_ARRAY_SIZE 0x845A -#define GL_SECONDARY_COLOR_ARRAY_TYPE 0x845B -#define GL_SECONDARY_COLOR_ARRAY_STRIDE 0x845C -#define GL_SECONDARY_COLOR_ARRAY_POINTER 0x845D -#define GL_SECONDARY_COLOR_ARRAY 0x845E -#define GL_TEXTURE_FILTER_CONTROL 0x8500 -#define GL_DEPTH_TEXTURE_MODE 0x884B -#define GL_COMPARE_R_TO_TEXTURE 0x884E -#define GL_FUNC_ADD 0x8006 -#define GL_FUNC_SUBTRACT 0x800A -#define GL_FUNC_REVERSE_SUBTRACT 0x800B -#define GL_MIN 0x8007 -#define GL_MAX 0x8008 -#define GL_CONSTANT_COLOR 0x8001 -#define GL_ONE_MINUS_CONSTANT_COLOR 0x8002 -#define GL_CONSTANT_ALPHA 0x8003 -#define GL_ONE_MINUS_CONSTANT_ALPHA 0x8004 -typedef void (APIENTRYP PFNGLBLENDFUNCSEPARATEPROC) (GLenum sfactorRGB, GLenum dfactorRGB, GLenum sfactorAlpha, GLenum dfactorAlpha); -typedef void (APIENTRYP PFNGLMULTIDRAWARRAYSPROC) (GLenum mode, const GLint *first, const GLsizei *count, GLsizei drawcount); -typedef void (APIENTRYP PFNGLMULTIDRAWELEMENTSPROC) (GLenum mode, const GLsizei *count, GLenum type, const void *const*indices, GLsizei drawcount); -typedef void (APIENTRYP PFNGLPOINTPARAMETERFPROC) (GLenum pname, GLfloat param); -typedef void (APIENTRYP PFNGLPOINTPARAMETERFVPROC) (GLenum pname, const GLfloat *params); -typedef void (APIENTRYP PFNGLPOINTPARAMETERIPROC) (GLenum pname, GLint param); -typedef void (APIENTRYP PFNGLPOINTPARAMETERIVPROC) (GLenum pname, const GLint *params); -typedef void (APIENTRYP PFNGLFOGCOORDFPROC) (GLfloat coord); -typedef void (APIENTRYP PFNGLFOGCOORDFVPROC) (const GLfloat *coord); -typedef void (APIENTRYP PFNGLFOGCOORDDPROC) (GLdouble coord); -typedef void (APIENTRYP PFNGLFOGCOORDDVPROC) (const GLdouble *coord); -typedef void (APIENTRYP PFNGLFOGCOORDPOINTERPROC) (GLenum type, GLsizei stride, const void *pointer); -typedef void (APIENTRYP PFNGLSECONDARYCOLOR3BPROC) (GLbyte red, GLbyte green, GLbyte blue); -typedef void (APIENTRYP PFNGLSECONDARYCOLOR3BVPROC) (const GLbyte *v); -typedef void (APIENTRYP PFNGLSECONDARYCOLOR3DPROC) (GLdouble red, GLdouble green, GLdouble blue); -typedef void (APIENTRYP PFNGLSECONDARYCOLOR3DVPROC) (const GLdouble *v); -typedef void (APIENTRYP PFNGLSECONDARYCOLOR3FPROC) (GLfloat red, GLfloat green, GLfloat blue); -typedef void (APIENTRYP PFNGLSECONDARYCOLOR3FVPROC) (const GLfloat *v); -typedef void (APIENTRYP PFNGLSECONDARYCOLOR3IPROC) (GLint red, GLint green, GLint blue); -typedef void (APIENTRYP PFNGLSECONDARYCOLOR3IVPROC) (const GLint *v); -typedef void (APIENTRYP PFNGLSECONDARYCOLOR3SPROC) (GLshort red, GLshort green, GLshort blue); -typedef void (APIENTRYP PFNGLSECONDARYCOLOR3SVPROC) (const GLshort *v); -typedef void (APIENTRYP PFNGLSECONDARYCOLOR3UBPROC) (GLubyte red, GLubyte green, GLubyte blue); -typedef void (APIENTRYP PFNGLSECONDARYCOLOR3UBVPROC) (const GLubyte *v); -typedef void (APIENTRYP PFNGLSECONDARYCOLOR3UIPROC) (GLuint red, GLuint green, GLuint blue); -typedef void (APIENTRYP PFNGLSECONDARYCOLOR3UIVPROC) (const GLuint *v); -typedef void (APIENTRYP PFNGLSECONDARYCOLOR3USPROC) (GLushort red, GLushort green, GLushort blue); -typedef void (APIENTRYP PFNGLSECONDARYCOLOR3USVPROC) (const GLushort *v); -typedef void (APIENTRYP PFNGLSECONDARYCOLORPOINTERPROC) (GLint size, GLenum type, GLsizei stride, const void *pointer); -typedef void (APIENTRYP PFNGLWINDOWPOS2DPROC) (GLdouble x, GLdouble y); -typedef void (APIENTRYP PFNGLWINDOWPOS2DVPROC) (const GLdouble *v); -typedef void (APIENTRYP PFNGLWINDOWPOS2FPROC) (GLfloat x, GLfloat y); -typedef void (APIENTRYP PFNGLWINDOWPOS2FVPROC) (const GLfloat *v); -typedef void (APIENTRYP PFNGLWINDOWPOS2IPROC) (GLint x, GLint y); -typedef void (APIENTRYP PFNGLWINDOWPOS2IVPROC) (const GLint *v); -typedef void (APIENTRYP PFNGLWINDOWPOS2SPROC) (GLshort x, GLshort y); -typedef void (APIENTRYP PFNGLWINDOWPOS2SVPROC) (const GLshort *v); -typedef void (APIENTRYP PFNGLWINDOWPOS3DPROC) (GLdouble x, GLdouble y, GLdouble z); -typedef void (APIENTRYP PFNGLWINDOWPOS3DVPROC) (const GLdouble *v); -typedef void (APIENTRYP PFNGLWINDOWPOS3FPROC) (GLfloat x, GLfloat y, GLfloat z); -typedef void (APIENTRYP PFNGLWINDOWPOS3FVPROC) (const GLfloat *v); -typedef void (APIENTRYP PFNGLWINDOWPOS3IPROC) (GLint x, GLint y, GLint z); -typedef void (APIENTRYP PFNGLWINDOWPOS3IVPROC) (const GLint *v); -typedef void (APIENTRYP PFNGLWINDOWPOS3SPROC) (GLshort x, GLshort y, GLshort z); -typedef void (APIENTRYP PFNGLWINDOWPOS3SVPROC) (const GLshort *v); -typedef void (APIENTRYP PFNGLBLENDCOLORPROC) (GLfloat red, GLfloat green, GLfloat blue, GLfloat alpha); -typedef void (APIENTRYP PFNGLBLENDEQUATIONPROC) (GLenum mode); -#ifdef GL_GLEXT_PROTOTYPES -GLAPI void APIENTRY glBlendFuncSeparate (GLenum sfactorRGB, GLenum dfactorRGB, GLenum sfactorAlpha, GLenum dfactorAlpha); -GLAPI void APIENTRY glMultiDrawArrays (GLenum mode, const GLint *first, const GLsizei *count, GLsizei drawcount); -GLAPI void APIENTRY glMultiDrawElements (GLenum mode, const GLsizei *count, GLenum type, const void *const*indices, GLsizei drawcount); -GLAPI void APIENTRY glPointParameterf (GLenum pname, GLfloat param); -GLAPI void APIENTRY glPointParameterfv (GLenum pname, const GLfloat *params); -GLAPI void APIENTRY glPointParameteri (GLenum pname, GLint param); -GLAPI void APIENTRY glPointParameteriv (GLenum pname, const GLint *params); -GLAPI void APIENTRY glFogCoordf (GLfloat coord); -GLAPI void APIENTRY glFogCoordfv (const GLfloat *coord); -GLAPI void APIENTRY glFogCoordd (GLdouble coord); -GLAPI void APIENTRY glFogCoorddv (const GLdouble *coord); -GLAPI void APIENTRY glFogCoordPointer (GLenum type, GLsizei stride, const void *pointer); -GLAPI void APIENTRY glSecondaryColor3b (GLbyte red, GLbyte green, GLbyte blue); -GLAPI void APIENTRY glSecondaryColor3bv (const GLbyte *v); -GLAPI void APIENTRY glSecondaryColor3d (GLdouble red, GLdouble green, GLdouble blue); -GLAPI void APIENTRY glSecondaryColor3dv (const GLdouble *v); -GLAPI void APIENTRY glSecondaryColor3f (GLfloat red, GLfloat green, GLfloat blue); -GLAPI void APIENTRY glSecondaryColor3fv (const GLfloat *v); -GLAPI void APIENTRY glSecondaryColor3i (GLint red, GLint green, GLint blue); -GLAPI void APIENTRY glSecondaryColor3iv (const GLint *v); -GLAPI void APIENTRY glSecondaryColor3s (GLshort red, GLshort green, GLshort blue); -GLAPI void APIENTRY glSecondaryColor3sv (const GLshort *v); -GLAPI void APIENTRY glSecondaryColor3ub (GLubyte red, GLubyte green, GLubyte blue); -GLAPI void APIENTRY glSecondaryColor3ubv (const GLubyte *v); -GLAPI void APIENTRY glSecondaryColor3ui (GLuint red, GLuint green, GLuint blue); -GLAPI void APIENTRY glSecondaryColor3uiv (const GLuint *v); -GLAPI void APIENTRY glSecondaryColor3us (GLushort red, GLushort green, GLushort blue); -GLAPI void APIENTRY glSecondaryColor3usv (const GLushort *v); -GLAPI void APIENTRY glSecondaryColorPointer (GLint size, GLenum type, GLsizei stride, const void *pointer); -GLAPI void APIENTRY glWindowPos2d (GLdouble x, GLdouble y); -GLAPI void APIENTRY glWindowPos2dv (const GLdouble *v); -GLAPI void APIENTRY glWindowPos2f (GLfloat x, GLfloat y); -GLAPI void APIENTRY glWindowPos2fv (const GLfloat *v); -GLAPI void APIENTRY glWindowPos2i (GLint x, GLint y); -GLAPI void APIENTRY glWindowPos2iv (const GLint *v); -GLAPI void APIENTRY glWindowPos2s (GLshort x, GLshort y); -GLAPI void APIENTRY glWindowPos2sv (const GLshort *v); -GLAPI void APIENTRY glWindowPos3d (GLdouble x, GLdouble y, GLdouble z); -GLAPI void APIENTRY glWindowPos3dv (const GLdouble *v); -GLAPI void APIENTRY glWindowPos3f (GLfloat x, GLfloat y, GLfloat z); -GLAPI void APIENTRY glWindowPos3fv (const GLfloat *v); -GLAPI void APIENTRY glWindowPos3i (GLint x, GLint y, GLint z); -GLAPI void APIENTRY glWindowPos3iv (const GLint *v); -GLAPI void APIENTRY glWindowPos3s (GLshort x, GLshort y, GLshort z); -GLAPI void APIENTRY glWindowPos3sv (const GLshort *v); -GLAPI void APIENTRY glBlendColor (GLfloat red, GLfloat green, GLfloat blue, GLfloat alpha); -GLAPI void APIENTRY glBlendEquation (GLenum mode); -#endif -#endif /* GL_VERSION_1_4 */ - -#ifndef GL_VERSION_1_5 -#define GL_VERSION_1_5 1 -#include -typedef ptrdiff_t GLsizeiptr; -typedef ptrdiff_t GLintptr; -#define GL_BUFFER_SIZE 0x8764 -#define GL_BUFFER_USAGE 0x8765 -#define GL_QUERY_COUNTER_BITS 0x8864 -#define GL_CURRENT_QUERY 0x8865 -#define GL_QUERY_RESULT 0x8866 -#define GL_QUERY_RESULT_AVAILABLE 0x8867 -#define GL_ARRAY_BUFFER 0x8892 -#define GL_ELEMENT_ARRAY_BUFFER 0x8893 -#define GL_ARRAY_BUFFER_BINDING 0x8894 -#define GL_ELEMENT_ARRAY_BUFFER_BINDING 0x8895 -#define GL_VERTEX_ATTRIB_ARRAY_BUFFER_BINDING 0x889F -#define GL_READ_ONLY 0x88B8 -#define GL_WRITE_ONLY 0x88B9 -#define GL_READ_WRITE 0x88BA -#define GL_BUFFER_ACCESS 0x88BB -#define GL_BUFFER_MAPPED 0x88BC -#define GL_BUFFER_MAP_POINTER 0x88BD -#define GL_STREAM_DRAW 0x88E0 -#define GL_STREAM_READ 0x88E1 -#define GL_STREAM_COPY 0x88E2 -#define GL_STATIC_DRAW 0x88E4 -#define GL_STATIC_READ 0x88E5 -#define GL_STATIC_COPY 0x88E6 -#define GL_DYNAMIC_DRAW 0x88E8 -#define GL_DYNAMIC_READ 0x88E9 -#define GL_DYNAMIC_COPY 0x88EA -#define GL_SAMPLES_PASSED 0x8914 -#define GL_SRC1_ALPHA 0x8589 -#define GL_VERTEX_ARRAY_BUFFER_BINDING 0x8896 -#define GL_NORMAL_ARRAY_BUFFER_BINDING 0x8897 -#define GL_COLOR_ARRAY_BUFFER_BINDING 0x8898 -#define GL_INDEX_ARRAY_BUFFER_BINDING 0x8899 -#define GL_TEXTURE_COORD_ARRAY_BUFFER_BINDING 0x889A -#define GL_EDGE_FLAG_ARRAY_BUFFER_BINDING 0x889B -#define GL_SECONDARY_COLOR_ARRAY_BUFFER_BINDING 0x889C -#define GL_FOG_COORDINATE_ARRAY_BUFFER_BINDING 0x889D -#define GL_WEIGHT_ARRAY_BUFFER_BINDING 0x889E -#define GL_FOG_COORD_SRC 0x8450 -#define GL_FOG_COORD 0x8451 -#define GL_CURRENT_FOG_COORD 0x8453 -#define GL_FOG_COORD_ARRAY_TYPE 0x8454 -#define GL_FOG_COORD_ARRAY_STRIDE 0x8455 -#define GL_FOG_COORD_ARRAY_POINTER 0x8456 -#define GL_FOG_COORD_ARRAY 0x8457 -#define GL_FOG_COORD_ARRAY_BUFFER_BINDING 0x889D -#define GL_SRC0_RGB 0x8580 -#define GL_SRC1_RGB 0x8581 -#define GL_SRC2_RGB 0x8582 -#define GL_SRC0_ALPHA 0x8588 -#define GL_SRC2_ALPHA 0x858A -typedef void (APIENTRYP PFNGLGENQUERIESPROC) (GLsizei n, GLuint *ids); -typedef void (APIENTRYP PFNGLDELETEQUERIESPROC) (GLsizei n, const GLuint *ids); -typedef GLboolean (APIENTRYP PFNGLISQUERYPROC) (GLuint id); -typedef void (APIENTRYP PFNGLBEGINQUERYPROC) (GLenum target, GLuint id); -typedef void (APIENTRYP PFNGLENDQUERYPROC) (GLenum target); -typedef void (APIENTRYP PFNGLGETQUERYIVPROC) (GLenum target, GLenum pname, GLint *params); -typedef void (APIENTRYP PFNGLGETQUERYOBJECTIVPROC) (GLuint id, GLenum pname, GLint *params); -typedef void (APIENTRYP PFNGLGETQUERYOBJECTUIVPROC) (GLuint id, GLenum pname, GLuint *params); -typedef void (APIENTRYP PFNGLBINDBUFFERPROC) (GLenum target, GLuint buffer); -typedef void (APIENTRYP PFNGLDELETEBUFFERSPROC) (GLsizei n, const GLuint *buffers); -typedef void (APIENTRYP PFNGLGENBUFFERSPROC) (GLsizei n, GLuint *buffers); -typedef GLboolean (APIENTRYP PFNGLISBUFFERPROC) (GLuint buffer); -typedef void (APIENTRYP PFNGLBUFFERDATAPROC) (GLenum target, GLsizeiptr size, const void *data, GLenum usage); -typedef void (APIENTRYP PFNGLBUFFERSUBDATAPROC) (GLenum target, GLintptr offset, GLsizeiptr size, const void *data); -typedef void (APIENTRYP PFNGLGETBUFFERSUBDATAPROC) (GLenum target, GLintptr offset, GLsizeiptr size, void *data); -typedef void *(APIENTRYP PFNGLMAPBUFFERPROC) (GLenum target, GLenum access); -typedef GLboolean (APIENTRYP PFNGLUNMAPBUFFERPROC) (GLenum target); -typedef void (APIENTRYP PFNGLGETBUFFERPARAMETERIVPROC) (GLenum target, GLenum pname, GLint *params); -typedef void (APIENTRYP PFNGLGETBUFFERPOINTERVPROC) (GLenum target, GLenum pname, void **params); -#ifdef GL_GLEXT_PROTOTYPES -GLAPI void APIENTRY glGenQueries (GLsizei n, GLuint *ids); -GLAPI void APIENTRY glDeleteQueries (GLsizei n, const GLuint *ids); -GLAPI GLboolean APIENTRY glIsQuery (GLuint id); -GLAPI void APIENTRY glBeginQuery (GLenum target, GLuint id); -GLAPI void APIENTRY glEndQuery (GLenum target); -GLAPI void APIENTRY glGetQueryiv (GLenum target, GLenum pname, GLint *params); -GLAPI void APIENTRY glGetQueryObjectiv (GLuint id, GLenum pname, GLint *params); -GLAPI void APIENTRY glGetQueryObjectuiv (GLuint id, GLenum pname, GLuint *params); -GLAPI void APIENTRY glBindBuffer (GLenum target, GLuint buffer); -GLAPI void APIENTRY glDeleteBuffers (GLsizei n, const GLuint *buffers); -GLAPI void APIENTRY glGenBuffers (GLsizei n, GLuint *buffers); -GLAPI GLboolean APIENTRY glIsBuffer (GLuint buffer); -GLAPI void APIENTRY glBufferData (GLenum target, GLsizeiptr size, const void *data, GLenum usage); -GLAPI void APIENTRY glBufferSubData (GLenum target, GLintptr offset, GLsizeiptr size, const void *data); -GLAPI void APIENTRY glGetBufferSubData (GLenum target, GLintptr offset, GLsizeiptr size, void *data); -GLAPI void *APIENTRY glMapBuffer (GLenum target, GLenum access); -GLAPI GLboolean APIENTRY glUnmapBuffer (GLenum target); -GLAPI void APIENTRY glGetBufferParameteriv (GLenum target, GLenum pname, GLint *params); -GLAPI void APIENTRY glGetBufferPointerv (GLenum target, GLenum pname, void **params); -#endif -#endif /* GL_VERSION_1_5 */ - -#ifndef GL_VERSION_2_0 -#define GL_VERSION_2_0 1 -typedef char GLchar; -#define GL_BLEND_EQUATION_RGB 0x8009 -#define GL_VERTEX_ATTRIB_ARRAY_ENABLED 0x8622 -#define GL_VERTEX_ATTRIB_ARRAY_SIZE 0x8623 -#define GL_VERTEX_ATTRIB_ARRAY_STRIDE 0x8624 -#define GL_VERTEX_ATTRIB_ARRAY_TYPE 0x8625 -#define GL_CURRENT_VERTEX_ATTRIB 0x8626 -#define GL_VERTEX_PROGRAM_POINT_SIZE 0x8642 -#define GL_VERTEX_ATTRIB_ARRAY_POINTER 0x8645 -#define GL_STENCIL_BACK_FUNC 0x8800 -#define GL_STENCIL_BACK_FAIL 0x8801 -#define GL_STENCIL_BACK_PASS_DEPTH_FAIL 0x8802 -#define GL_STENCIL_BACK_PASS_DEPTH_PASS 0x8803 -#define GL_MAX_DRAW_BUFFERS 0x8824 -#define GL_DRAW_BUFFER0 0x8825 -#define GL_DRAW_BUFFER1 0x8826 -#define GL_DRAW_BUFFER2 0x8827 -#define GL_DRAW_BUFFER3 0x8828 -#define GL_DRAW_BUFFER4 0x8829 -#define GL_DRAW_BUFFER5 0x882A -#define GL_DRAW_BUFFER6 0x882B -#define GL_DRAW_BUFFER7 0x882C -#define GL_DRAW_BUFFER8 0x882D -#define GL_DRAW_BUFFER9 0x882E -#define GL_DRAW_BUFFER10 0x882F -#define GL_DRAW_BUFFER11 0x8830 -#define GL_DRAW_BUFFER12 0x8831 -#define GL_DRAW_BUFFER13 0x8832 -#define GL_DRAW_BUFFER14 0x8833 -#define GL_DRAW_BUFFER15 0x8834 -#define GL_BLEND_EQUATION_ALPHA 0x883D -#define GL_MAX_VERTEX_ATTRIBS 0x8869 -#define GL_VERTEX_ATTRIB_ARRAY_NORMALIZED 0x886A -#define GL_MAX_TEXTURE_IMAGE_UNITS 0x8872 -#define GL_FRAGMENT_SHADER 0x8B30 -#define GL_VERTEX_SHADER 0x8B31 -#define GL_MAX_FRAGMENT_UNIFORM_COMPONENTS 0x8B49 -#define GL_MAX_VERTEX_UNIFORM_COMPONENTS 0x8B4A -#define GL_MAX_VARYING_FLOATS 0x8B4B -#define GL_MAX_VERTEX_TEXTURE_IMAGE_UNITS 0x8B4C -#define GL_MAX_COMBINED_TEXTURE_IMAGE_UNITS 0x8B4D -#define GL_SHADER_TYPE 0x8B4F -#define GL_FLOAT_VEC2 0x8B50 -#define GL_FLOAT_VEC3 0x8B51 -#define GL_FLOAT_VEC4 0x8B52 -#define GL_INT_VEC2 0x8B53 -#define GL_INT_VEC3 0x8B54 -#define GL_INT_VEC4 0x8B55 -#define GL_BOOL 0x8B56 -#define GL_BOOL_VEC2 0x8B57 -#define GL_BOOL_VEC3 0x8B58 -#define GL_BOOL_VEC4 0x8B59 -#define GL_FLOAT_MAT2 0x8B5A -#define GL_FLOAT_MAT3 0x8B5B -#define GL_FLOAT_MAT4 0x8B5C -#define GL_SAMPLER_1D 0x8B5D -#define GL_SAMPLER_2D 0x8B5E -#define GL_SAMPLER_3D 0x8B5F -#define GL_SAMPLER_CUBE 0x8B60 -#define GL_SAMPLER_1D_SHADOW 0x8B61 -#define GL_SAMPLER_2D_SHADOW 0x8B62 -#define GL_DELETE_STATUS 0x8B80 -#define GL_COMPILE_STATUS 0x8B81 -#define GL_LINK_STATUS 0x8B82 -#define GL_VALIDATE_STATUS 0x8B83 -#define GL_INFO_LOG_LENGTH 0x8B84 -#define GL_ATTACHED_SHADERS 0x8B85 -#define GL_ACTIVE_UNIFORMS 0x8B86 -#define GL_ACTIVE_UNIFORM_MAX_LENGTH 0x8B87 -#define GL_SHADER_SOURCE_LENGTH 0x8B88 -#define GL_ACTIVE_ATTRIBUTES 0x8B89 -#define GL_ACTIVE_ATTRIBUTE_MAX_LENGTH 0x8B8A -#define GL_FRAGMENT_SHADER_DERIVATIVE_HINT 0x8B8B -#define GL_SHADING_LANGUAGE_VERSION 0x8B8C -#define GL_CURRENT_PROGRAM 0x8B8D -#define GL_POINT_SPRITE_COORD_ORIGIN 0x8CA0 -#define GL_LOWER_LEFT 0x8CA1 -#define GL_UPPER_LEFT 0x8CA2 -#define GL_STENCIL_BACK_REF 0x8CA3 -#define GL_STENCIL_BACK_VALUE_MASK 0x8CA4 -#define GL_STENCIL_BACK_WRITEMASK 0x8CA5 -#define GL_VERTEX_PROGRAM_TWO_SIDE 0x8643 -#define GL_POINT_SPRITE 0x8861 -#define GL_COORD_REPLACE 0x8862 -#define GL_MAX_TEXTURE_COORDS 0x8871 -typedef void (APIENTRYP PFNGLBLENDEQUATIONSEPARATEPROC) (GLenum modeRGB, GLenum modeAlpha); -typedef void (APIENTRYP PFNGLDRAWBUFFERSPROC) (GLsizei n, const GLenum *bufs); -typedef void (APIENTRYP PFNGLSTENCILOPSEPARATEPROC) (GLenum face, GLenum sfail, GLenum dpfail, GLenum dppass); -typedef void (APIENTRYP PFNGLSTENCILFUNCSEPARATEPROC) (GLenum face, GLenum func, GLint ref, GLuint mask); -typedef void (APIENTRYP PFNGLSTENCILMASKSEPARATEPROC) (GLenum face, GLuint mask); -typedef void (APIENTRYP PFNGLATTACHSHADERPROC) (GLuint program, GLuint shader); -typedef void (APIENTRYP PFNGLBINDATTRIBLOCATIONPROC) (GLuint program, GLuint index, const GLchar *name); -typedef void (APIENTRYP PFNGLCOMPILESHADERPROC) (GLuint shader); -typedef GLuint (APIENTRYP PFNGLCREATEPROGRAMPROC) (void); -typedef GLuint (APIENTRYP PFNGLCREATESHADERPROC) (GLenum type); -typedef void (APIENTRYP PFNGLDELETEPROGRAMPROC) (GLuint program); -typedef void (APIENTRYP PFNGLDELETESHADERPROC) (GLuint shader); -typedef void (APIENTRYP PFNGLDETACHSHADERPROC) (GLuint program, GLuint shader); -typedef void (APIENTRYP PFNGLDISABLEVERTEXATTRIBARRAYPROC) (GLuint index); -typedef void (APIENTRYP PFNGLENABLEVERTEXATTRIBARRAYPROC) (GLuint index); -typedef void (APIENTRYP PFNGLGETACTIVEATTRIBPROC) (GLuint program, GLuint index, GLsizei bufSize, GLsizei *length, GLint *size, GLenum *type, GLchar *name); -typedef void (APIENTRYP PFNGLGETACTIVEUNIFORMPROC) (GLuint program, GLuint index, GLsizei bufSize, GLsizei *length, GLint *size, GLenum *type, GLchar *name); -typedef void (APIENTRYP PFNGLGETATTACHEDSHADERSPROC) (GLuint program, GLsizei maxCount, GLsizei *count, GLuint *shaders); -typedef GLint (APIENTRYP PFNGLGETATTRIBLOCATIONPROC) (GLuint program, const GLchar *name); -typedef void (APIENTRYP PFNGLGETPROGRAMIVPROC) (GLuint program, GLenum pname, GLint *params); -typedef void (APIENTRYP PFNGLGETPROGRAMINFOLOGPROC) (GLuint program, GLsizei bufSize, GLsizei *length, GLchar *infoLog); -typedef void (APIENTRYP PFNGLGETSHADERIVPROC) (GLuint shader, GLenum pname, GLint *params); -typedef void (APIENTRYP PFNGLGETSHADERINFOLOGPROC) (GLuint shader, GLsizei bufSize, GLsizei *length, GLchar *infoLog); -typedef void (APIENTRYP PFNGLGETSHADERSOURCEPROC) (GLuint shader, GLsizei bufSize, GLsizei *length, GLchar *source); -typedef GLint (APIENTRYP PFNGLGETUNIFORMLOCATIONPROC) (GLuint program, const GLchar *name); -typedef void (APIENTRYP PFNGLGETUNIFORMFVPROC) (GLuint program, GLint location, GLfloat *params); -typedef void (APIENTRYP PFNGLGETUNIFORMIVPROC) (GLuint program, GLint location, GLint *params); -typedef void (APIENTRYP PFNGLGETVERTEXATTRIBDVPROC) (GLuint index, GLenum pname, GLdouble *params); -typedef void (APIENTRYP PFNGLGETVERTEXATTRIBFVPROC) (GLuint index, GLenum pname, GLfloat *params); -typedef void (APIENTRYP PFNGLGETVERTEXATTRIBIVPROC) (GLuint index, GLenum pname, GLint *params); -typedef void (APIENTRYP PFNGLGETVERTEXATTRIBPOINTERVPROC) (GLuint index, GLenum pname, void **pointer); -typedef GLboolean (APIENTRYP PFNGLISPROGRAMPROC) (GLuint program); -typedef GLboolean (APIENTRYP PFNGLISSHADERPROC) (GLuint shader); -typedef void (APIENTRYP PFNGLLINKPROGRAMPROC) (GLuint program); -typedef void (APIENTRYP PFNGLSHADERSOURCEPROC) (GLuint shader, GLsizei count, const GLchar *const*string, const GLint *length); -typedef void (APIENTRYP PFNGLUSEPROGRAMPROC) (GLuint program); -typedef void (APIENTRYP PFNGLUNIFORM1FPROC) (GLint location, GLfloat v0); -typedef void (APIENTRYP PFNGLUNIFORM2FPROC) (GLint location, GLfloat v0, GLfloat v1); -typedef void (APIENTRYP PFNGLUNIFORM3FPROC) (GLint location, GLfloat v0, GLfloat v1, GLfloat v2); -typedef void (APIENTRYP PFNGLUNIFORM4FPROC) (GLint location, GLfloat v0, GLfloat v1, GLfloat v2, GLfloat v3); -typedef void (APIENTRYP PFNGLUNIFORM1IPROC) (GLint location, GLint v0); -typedef void (APIENTRYP PFNGLUNIFORM2IPROC) (GLint location, GLint v0, GLint v1); -typedef void (APIENTRYP PFNGLUNIFORM3IPROC) (GLint location, GLint v0, GLint v1, GLint v2); -typedef void (APIENTRYP PFNGLUNIFORM4IPROC) (GLint location, GLint v0, GLint v1, GLint v2, GLint v3); -typedef void (APIENTRYP PFNGLUNIFORM1FVPROC) (GLint location, GLsizei count, const GLfloat *value); -typedef void (APIENTRYP PFNGLUNIFORM2FVPROC) (GLint location, GLsizei count, const GLfloat *value); -typedef void (APIENTRYP PFNGLUNIFORM3FVPROC) (GLint location, GLsizei count, const GLfloat *value); -typedef void (APIENTRYP PFNGLUNIFORM4FVPROC) (GLint location, GLsizei count, const GLfloat *value); -typedef void (APIENTRYP PFNGLUNIFORM1IVPROC) (GLint location, GLsizei count, const GLint *value); -typedef void (APIENTRYP PFNGLUNIFORM2IVPROC) (GLint location, GLsizei count, const GLint *value); -typedef void (APIENTRYP PFNGLUNIFORM3IVPROC) (GLint location, GLsizei count, const GLint *value); -typedef void (APIENTRYP PFNGLUNIFORM4IVPROC) (GLint location, GLsizei count, const GLint *value); -typedef void (APIENTRYP PFNGLUNIFORMMATRIX2FVPROC) (GLint location, GLsizei count, GLboolean transpose, const GLfloat *value); -typedef void (APIENTRYP PFNGLUNIFORMMATRIX3FVPROC) (GLint location, GLsizei count, GLboolean transpose, const GLfloat *value); -typedef void (APIENTRYP PFNGLUNIFORMMATRIX4FVPROC) (GLint location, GLsizei count, GLboolean transpose, const GLfloat *value); -typedef void (APIENTRYP PFNGLVALIDATEPROGRAMPROC) (GLuint program); -typedef void (APIENTRYP PFNGLVERTEXATTRIB1DPROC) (GLuint index, GLdouble x); -typedef void (APIENTRYP PFNGLVERTEXATTRIB1DVPROC) (GLuint index, const GLdouble *v); -typedef void (APIENTRYP PFNGLVERTEXATTRIB1FPROC) (GLuint index, GLfloat x); -typedef void (APIENTRYP PFNGLVERTEXATTRIB1FVPROC) (GLuint index, const GLfloat *v); -typedef void (APIENTRYP PFNGLVERTEXATTRIB1SPROC) (GLuint index, GLshort x); -typedef void (APIENTRYP PFNGLVERTEXATTRIB1SVPROC) (GLuint index, const GLshort *v); -typedef void (APIENTRYP PFNGLVERTEXATTRIB2DPROC) (GLuint index, GLdouble x, GLdouble y); -typedef void (APIENTRYP PFNGLVERTEXATTRIB2DVPROC) (GLuint index, const GLdouble *v); -typedef void (APIENTRYP PFNGLVERTEXATTRIB2FPROC) (GLuint index, GLfloat x, GLfloat y); -typedef void (APIENTRYP PFNGLVERTEXATTRIB2FVPROC) (GLuint index, const GLfloat *v); -typedef void (APIENTRYP PFNGLVERTEXATTRIB2SPROC) (GLuint index, GLshort x, GLshort y); -typedef void (APIENTRYP PFNGLVERTEXATTRIB2SVPROC) (GLuint index, const GLshort *v); -typedef void (APIENTRYP PFNGLVERTEXATTRIB3DPROC) (GLuint index, GLdouble x, GLdouble y, GLdouble z); -typedef void (APIENTRYP PFNGLVERTEXATTRIB3DVPROC) (GLuint index, const GLdouble *v); -typedef void (APIENTRYP PFNGLVERTEXATTRIB3FPROC) (GLuint index, GLfloat x, GLfloat y, GLfloat z); -typedef void (APIENTRYP PFNGLVERTEXATTRIB3FVPROC) (GLuint index, const GLfloat *v); -typedef void (APIENTRYP PFNGLVERTEXATTRIB3SPROC) (GLuint index, GLshort x, GLshort y, GLshort z); -typedef void (APIENTRYP PFNGLVERTEXATTRIB3SVPROC) (GLuint index, const GLshort *v); -typedef void (APIENTRYP PFNGLVERTEXATTRIB4NBVPROC) (GLuint index, const GLbyte *v); -typedef void (APIENTRYP PFNGLVERTEXATTRIB4NIVPROC) (GLuint index, const GLint *v); -typedef void (APIENTRYP PFNGLVERTEXATTRIB4NSVPROC) (GLuint index, const GLshort *v); -typedef void (APIENTRYP PFNGLVERTEXATTRIB4NUBPROC) (GLuint index, GLubyte x, GLubyte y, GLubyte z, GLubyte w); -typedef void (APIENTRYP PFNGLVERTEXATTRIB4NUBVPROC) (GLuint index, const GLubyte *v); -typedef void (APIENTRYP PFNGLVERTEXATTRIB4NUIVPROC) (GLuint index, const GLuint *v); -typedef void (APIENTRYP PFNGLVERTEXATTRIB4NUSVPROC) (GLuint index, const GLushort *v); -typedef void (APIENTRYP PFNGLVERTEXATTRIB4BVPROC) (GLuint index, const GLbyte *v); -typedef void (APIENTRYP PFNGLVERTEXATTRIB4DPROC) (GLuint index, GLdouble x, GLdouble y, GLdouble z, GLdouble w); -typedef void (APIENTRYP PFNGLVERTEXATTRIB4DVPROC) (GLuint index, const GLdouble *v); -typedef void (APIENTRYP PFNGLVERTEXATTRIB4FPROC) (GLuint index, GLfloat x, GLfloat y, GLfloat z, GLfloat w); -typedef void (APIENTRYP PFNGLVERTEXATTRIB4FVPROC) (GLuint index, const GLfloat *v); -typedef void (APIENTRYP PFNGLVERTEXATTRIB4IVPROC) (GLuint index, const GLint *v); -typedef void (APIENTRYP PFNGLVERTEXATTRIB4SPROC) (GLuint index, GLshort x, GLshort y, GLshort z, GLshort w); -typedef void (APIENTRYP PFNGLVERTEXATTRIB4SVPROC) (GLuint index, const GLshort *v); -typedef void (APIENTRYP PFNGLVERTEXATTRIB4UBVPROC) (GLuint index, const GLubyte *v); -typedef void (APIENTRYP PFNGLVERTEXATTRIB4UIVPROC) (GLuint index, const GLuint *v); -typedef void (APIENTRYP PFNGLVERTEXATTRIB4USVPROC) (GLuint index, const GLushort *v); -typedef void (APIENTRYP PFNGLVERTEXATTRIBPOINTERPROC) (GLuint index, GLint size, GLenum type, GLboolean normalized, GLsizei stride, const void *pointer); -#ifdef GL_GLEXT_PROTOTYPES -GLAPI void APIENTRY glBlendEquationSeparate (GLenum modeRGB, GLenum modeAlpha); -GLAPI void APIENTRY glDrawBuffers (GLsizei n, const GLenum *bufs); -GLAPI void APIENTRY glStencilOpSeparate (GLenum face, GLenum sfail, GLenum dpfail, GLenum dppass); -GLAPI void APIENTRY glStencilFuncSeparate (GLenum face, GLenum func, GLint ref, GLuint mask); -GLAPI void APIENTRY glStencilMaskSeparate (GLenum face, GLuint mask); -GLAPI void APIENTRY glAttachShader (GLuint program, GLuint shader); -GLAPI void APIENTRY glBindAttribLocation (GLuint program, GLuint index, const GLchar *name); -GLAPI void APIENTRY glCompileShader (GLuint shader); -GLAPI GLuint APIENTRY glCreateProgram (void); -GLAPI GLuint APIENTRY glCreateShader (GLenum type); -GLAPI void APIENTRY glDeleteProgram (GLuint program); -GLAPI void APIENTRY glDeleteShader (GLuint shader); -GLAPI void APIENTRY glDetachShader (GLuint program, GLuint shader); -GLAPI void APIENTRY glDisableVertexAttribArray (GLuint index); -GLAPI void APIENTRY glEnableVertexAttribArray (GLuint index); -GLAPI void APIENTRY glGetActiveAttrib (GLuint program, GLuint index, GLsizei bufSize, GLsizei *length, GLint *size, GLenum *type, GLchar *name); -GLAPI void APIENTRY glGetActiveUniform (GLuint program, GLuint index, GLsizei bufSize, GLsizei *length, GLint *size, GLenum *type, GLchar *name); -GLAPI void APIENTRY glGetAttachedShaders (GLuint program, GLsizei maxCount, GLsizei *count, GLuint *shaders); -GLAPI GLint APIENTRY glGetAttribLocation (GLuint program, const GLchar *name); -GLAPI void APIENTRY glGetProgramiv (GLuint program, GLenum pname, GLint *params); -GLAPI void APIENTRY glGetProgramInfoLog (GLuint program, GLsizei bufSize, GLsizei *length, GLchar *infoLog); -GLAPI void APIENTRY glGetShaderiv (GLuint shader, GLenum pname, GLint *params); -GLAPI void APIENTRY glGetShaderInfoLog (GLuint shader, GLsizei bufSize, GLsizei *length, GLchar *infoLog); -GLAPI void APIENTRY glGetShaderSource (GLuint shader, GLsizei bufSize, GLsizei *length, GLchar *source); -GLAPI GLint APIENTRY glGetUniformLocation (GLuint program, const GLchar *name); -GLAPI void APIENTRY glGetUniformfv (GLuint program, GLint location, GLfloat *params); -GLAPI void APIENTRY glGetUniformiv (GLuint program, GLint location, GLint *params); -GLAPI void APIENTRY glGetVertexAttribdv (GLuint index, GLenum pname, GLdouble *params); -GLAPI void APIENTRY glGetVertexAttribfv (GLuint index, GLenum pname, GLfloat *params); -GLAPI void APIENTRY glGetVertexAttribiv (GLuint index, GLenum pname, GLint *params); -GLAPI void APIENTRY glGetVertexAttribPointerv (GLuint index, GLenum pname, void **pointer); -GLAPI GLboolean APIENTRY glIsProgram (GLuint program); -GLAPI GLboolean APIENTRY glIsShader (GLuint shader); -GLAPI void APIENTRY glLinkProgram (GLuint program); -GLAPI void APIENTRY glShaderSource (GLuint shader, GLsizei count, const GLchar *const*string, const GLint *length); -GLAPI void APIENTRY glUseProgram (GLuint program); -GLAPI void APIENTRY glUniform1f (GLint location, GLfloat v0); -GLAPI void APIENTRY glUniform2f (GLint location, GLfloat v0, GLfloat v1); -GLAPI void APIENTRY glUniform3f (GLint location, GLfloat v0, GLfloat v1, GLfloat v2); -GLAPI void APIENTRY glUniform4f (GLint location, GLfloat v0, GLfloat v1, GLfloat v2, GLfloat v3); -GLAPI void APIENTRY glUniform1i (GLint location, GLint v0); -GLAPI void APIENTRY glUniform2i (GLint location, GLint v0, GLint v1); -GLAPI void APIENTRY glUniform3i (GLint location, GLint v0, GLint v1, GLint v2); -GLAPI void APIENTRY glUniform4i (GLint location, GLint v0, GLint v1, GLint v2, GLint v3); -GLAPI void APIENTRY glUniform1fv (GLint location, GLsizei count, const GLfloat *value); -GLAPI void APIENTRY glUniform2fv (GLint location, GLsizei count, const GLfloat *value); -GLAPI void APIENTRY glUniform3fv (GLint location, GLsizei count, const GLfloat *value); -GLAPI void APIENTRY glUniform4fv (GLint location, GLsizei count, const GLfloat *value); -GLAPI void APIENTRY glUniform1iv (GLint location, GLsizei count, const GLint *value); -GLAPI void APIENTRY glUniform2iv (GLint location, GLsizei count, const GLint *value); -GLAPI void APIENTRY glUniform3iv (GLint location, GLsizei count, const GLint *value); -GLAPI void APIENTRY glUniform4iv (GLint location, GLsizei count, const GLint *value); -GLAPI void APIENTRY glUniformMatrix2fv (GLint location, GLsizei count, GLboolean transpose, const GLfloat *value); -GLAPI void APIENTRY glUniformMatrix3fv (GLint location, GLsizei count, GLboolean transpose, const GLfloat *value); -GLAPI void APIENTRY glUniformMatrix4fv (GLint location, GLsizei count, GLboolean transpose, const GLfloat *value); -GLAPI void APIENTRY glValidateProgram (GLuint program); -GLAPI void APIENTRY glVertexAttrib1d (GLuint index, GLdouble x); -GLAPI void APIENTRY glVertexAttrib1dv (GLuint index, const GLdouble *v); -GLAPI void APIENTRY glVertexAttrib1f (GLuint index, GLfloat x); -GLAPI void APIENTRY glVertexAttrib1fv (GLuint index, const GLfloat *v); -GLAPI void APIENTRY glVertexAttrib1s (GLuint index, GLshort x); -GLAPI void APIENTRY glVertexAttrib1sv (GLuint index, const GLshort *v); -GLAPI void APIENTRY glVertexAttrib2d (GLuint index, GLdouble x, GLdouble y); -GLAPI void APIENTRY glVertexAttrib2dv (GLuint index, const GLdouble *v); -GLAPI void APIENTRY glVertexAttrib2f (GLuint index, GLfloat x, GLfloat y); -GLAPI void APIENTRY glVertexAttrib2fv (GLuint index, const GLfloat *v); -GLAPI void APIENTRY glVertexAttrib2s (GLuint index, GLshort x, GLshort y); -GLAPI void APIENTRY glVertexAttrib2sv (GLuint index, const GLshort *v); -GLAPI void APIENTRY glVertexAttrib3d (GLuint index, GLdouble x, GLdouble y, GLdouble z); -GLAPI void APIENTRY glVertexAttrib3dv (GLuint index, const GLdouble *v); -GLAPI void APIENTRY glVertexAttrib3f (GLuint index, GLfloat x, GLfloat y, GLfloat z); -GLAPI void APIENTRY glVertexAttrib3fv (GLuint index, const GLfloat *v); -GLAPI void APIENTRY glVertexAttrib3s (GLuint index, GLshort x, GLshort y, GLshort z); -GLAPI void APIENTRY glVertexAttrib3sv (GLuint index, const GLshort *v); -GLAPI void APIENTRY glVertexAttrib4Nbv (GLuint index, const GLbyte *v); -GLAPI void APIENTRY glVertexAttrib4Niv (GLuint index, const GLint *v); -GLAPI void APIENTRY glVertexAttrib4Nsv (GLuint index, const GLshort *v); -GLAPI void APIENTRY glVertexAttrib4Nub (GLuint index, GLubyte x, GLubyte y, GLubyte z, GLubyte w); -GLAPI void APIENTRY glVertexAttrib4Nubv (GLuint index, const GLubyte *v); -GLAPI void APIENTRY glVertexAttrib4Nuiv (GLuint index, const GLuint *v); -GLAPI void APIENTRY glVertexAttrib4Nusv (GLuint index, const GLushort *v); -GLAPI void APIENTRY glVertexAttrib4bv (GLuint index, const GLbyte *v); -GLAPI void APIENTRY glVertexAttrib4d (GLuint index, GLdouble x, GLdouble y, GLdouble z, GLdouble w); -GLAPI void APIENTRY glVertexAttrib4dv (GLuint index, const GLdouble *v); -GLAPI void APIENTRY glVertexAttrib4f (GLuint index, GLfloat x, GLfloat y, GLfloat z, GLfloat w); -GLAPI void APIENTRY glVertexAttrib4fv (GLuint index, const GLfloat *v); -GLAPI void APIENTRY glVertexAttrib4iv (GLuint index, const GLint *v); -GLAPI void APIENTRY glVertexAttrib4s (GLuint index, GLshort x, GLshort y, GLshort z, GLshort w); -GLAPI void APIENTRY glVertexAttrib4sv (GLuint index, const GLshort *v); -GLAPI void APIENTRY glVertexAttrib4ubv (GLuint index, const GLubyte *v); -GLAPI void APIENTRY glVertexAttrib4uiv (GLuint index, const GLuint *v); -GLAPI void APIENTRY glVertexAttrib4usv (GLuint index, const GLushort *v); -GLAPI void APIENTRY glVertexAttribPointer (GLuint index, GLint size, GLenum type, GLboolean normalized, GLsizei stride, const void *pointer); -#endif -#endif /* GL_VERSION_2_0 */ - -#ifndef GL_VERSION_2_1 -#define GL_VERSION_2_1 1 -#define GL_PIXEL_PACK_BUFFER 0x88EB -#define GL_PIXEL_UNPACK_BUFFER 0x88EC -#define GL_PIXEL_PACK_BUFFER_BINDING 0x88ED -#define GL_PIXEL_UNPACK_BUFFER_BINDING 0x88EF -#define GL_FLOAT_MAT2x3 0x8B65 -#define GL_FLOAT_MAT2x4 0x8B66 -#define GL_FLOAT_MAT3x2 0x8B67 -#define GL_FLOAT_MAT3x4 0x8B68 -#define GL_FLOAT_MAT4x2 0x8B69 -#define GL_FLOAT_MAT4x3 0x8B6A -#define GL_SRGB 0x8C40 -#define GL_SRGB8 0x8C41 -#define GL_SRGB_ALPHA 0x8C42 -#define GL_SRGB8_ALPHA8 0x8C43 -#define GL_COMPRESSED_SRGB 0x8C48 -#define GL_COMPRESSED_SRGB_ALPHA 0x8C49 -#define GL_CURRENT_RASTER_SECONDARY_COLOR 0x845F -#define GL_SLUMINANCE_ALPHA 0x8C44 -#define GL_SLUMINANCE8_ALPHA8 0x8C45 -#define GL_SLUMINANCE 0x8C46 -#define GL_SLUMINANCE8 0x8C47 -#define GL_COMPRESSED_SLUMINANCE 0x8C4A -#define GL_COMPRESSED_SLUMINANCE_ALPHA 0x8C4B -typedef void (APIENTRYP PFNGLUNIFORMMATRIX2X3FVPROC) (GLint location, GLsizei count, GLboolean transpose, const GLfloat *value); -typedef void (APIENTRYP PFNGLUNIFORMMATRIX3X2FVPROC) (GLint location, GLsizei count, GLboolean transpose, const GLfloat *value); -typedef void (APIENTRYP PFNGLUNIFORMMATRIX2X4FVPROC) (GLint location, GLsizei count, GLboolean transpose, const GLfloat *value); -typedef void (APIENTRYP PFNGLUNIFORMMATRIX4X2FVPROC) (GLint location, GLsizei count, GLboolean transpose, const GLfloat *value); -typedef void (APIENTRYP PFNGLUNIFORMMATRIX3X4FVPROC) (GLint location, GLsizei count, GLboolean transpose, const GLfloat *value); -typedef void (APIENTRYP PFNGLUNIFORMMATRIX4X3FVPROC) (GLint location, GLsizei count, GLboolean transpose, const GLfloat *value); -#ifdef GL_GLEXT_PROTOTYPES -GLAPI void APIENTRY glUniformMatrix2x3fv (GLint location, GLsizei count, GLboolean transpose, const GLfloat *value); -GLAPI void APIENTRY glUniformMatrix3x2fv (GLint location, GLsizei count, GLboolean transpose, const GLfloat *value); -GLAPI void APIENTRY glUniformMatrix2x4fv (GLint location, GLsizei count, GLboolean transpose, const GLfloat *value); -GLAPI void APIENTRY glUniformMatrix4x2fv (GLint location, GLsizei count, GLboolean transpose, const GLfloat *value); -GLAPI void APIENTRY glUniformMatrix3x4fv (GLint location, GLsizei count, GLboolean transpose, const GLfloat *value); -GLAPI void APIENTRY glUniformMatrix4x3fv (GLint location, GLsizei count, GLboolean transpose, const GLfloat *value); -#endif -#endif /* GL_VERSION_2_1 */ - -#ifndef GL_VERSION_3_0 -#define GL_VERSION_3_0 1 -typedef unsigned short GLhalf; -#define GL_COMPARE_REF_TO_TEXTURE 0x884E -#define GL_CLIP_DISTANCE0 0x3000 -#define GL_CLIP_DISTANCE1 0x3001 -#define GL_CLIP_DISTANCE2 0x3002 -#define GL_CLIP_DISTANCE3 0x3003 -#define GL_CLIP_DISTANCE4 0x3004 -#define GL_CLIP_DISTANCE5 0x3005 -#define GL_CLIP_DISTANCE6 0x3006 -#define GL_CLIP_DISTANCE7 0x3007 -#define GL_MAX_CLIP_DISTANCES 0x0D32 -#define GL_MAJOR_VERSION 0x821B -#define GL_MINOR_VERSION 0x821C -#define GL_NUM_EXTENSIONS 0x821D -#define GL_CONTEXT_FLAGS 0x821E -#define GL_COMPRESSED_RED 0x8225 -#define GL_COMPRESSED_RG 0x8226 -#define GL_CONTEXT_FLAG_FORWARD_COMPATIBLE_BIT 0x00000001 -#define GL_RGBA32F 0x8814 -#define GL_RGB32F 0x8815 -#define GL_RGBA16F 0x881A -#define GL_RGB16F 0x881B -#define GL_VERTEX_ATTRIB_ARRAY_INTEGER 0x88FD -#define GL_MAX_ARRAY_TEXTURE_LAYERS 0x88FF -#define GL_MIN_PROGRAM_TEXEL_OFFSET 0x8904 -#define GL_MAX_PROGRAM_TEXEL_OFFSET 0x8905 -#define GL_CLAMP_READ_COLOR 0x891C -#define GL_FIXED_ONLY 0x891D -#define GL_MAX_VARYING_COMPONENTS 0x8B4B -#define GL_TEXTURE_1D_ARRAY 0x8C18 -#define GL_PROXY_TEXTURE_1D_ARRAY 0x8C19 -#define GL_TEXTURE_2D_ARRAY 0x8C1A -#define GL_PROXY_TEXTURE_2D_ARRAY 0x8C1B -#define GL_TEXTURE_BINDING_1D_ARRAY 0x8C1C -#define GL_TEXTURE_BINDING_2D_ARRAY 0x8C1D -#define GL_R11F_G11F_B10F 0x8C3A -#define GL_UNSIGNED_INT_10F_11F_11F_REV 0x8C3B -#define GL_RGB9_E5 0x8C3D -#define GL_UNSIGNED_INT_5_9_9_9_REV 0x8C3E -#define GL_TEXTURE_SHARED_SIZE 0x8C3F -#define GL_TRANSFORM_FEEDBACK_VARYING_MAX_LENGTH 0x8C76 -#define GL_TRANSFORM_FEEDBACK_BUFFER_MODE 0x8C7F -#define GL_MAX_TRANSFORM_FEEDBACK_SEPARATE_COMPONENTS 0x8C80 -#define GL_TRANSFORM_FEEDBACK_VARYINGS 0x8C83 -#define GL_TRANSFORM_FEEDBACK_BUFFER_START 0x8C84 -#define GL_TRANSFORM_FEEDBACK_BUFFER_SIZE 0x8C85 -#define GL_PRIMITIVES_GENERATED 0x8C87 -#define GL_TRANSFORM_FEEDBACK_PRIMITIVES_WRITTEN 0x8C88 -#define GL_RASTERIZER_DISCARD 0x8C89 -#define GL_MAX_TRANSFORM_FEEDBACK_INTERLEAVED_COMPONENTS 0x8C8A -#define GL_MAX_TRANSFORM_FEEDBACK_SEPARATE_ATTRIBS 0x8C8B -#define GL_INTERLEAVED_ATTRIBS 0x8C8C -#define GL_SEPARATE_ATTRIBS 0x8C8D -#define GL_TRANSFORM_FEEDBACK_BUFFER 0x8C8E -#define GL_TRANSFORM_FEEDBACK_BUFFER_BINDING 0x8C8F -#define GL_RGBA32UI 0x8D70 -#define GL_RGB32UI 0x8D71 -#define GL_RGBA16UI 0x8D76 -#define GL_RGB16UI 0x8D77 -#define GL_RGBA8UI 0x8D7C -#define GL_RGB8UI 0x8D7D -#define GL_RGBA32I 0x8D82 -#define GL_RGB32I 0x8D83 -#define GL_RGBA16I 0x8D88 -#define GL_RGB16I 0x8D89 -#define GL_RGBA8I 0x8D8E -#define GL_RGB8I 0x8D8F -#define GL_RED_INTEGER 0x8D94 -#define GL_GREEN_INTEGER 0x8D95 -#define GL_BLUE_INTEGER 0x8D96 -#define GL_RGB_INTEGER 0x8D98 -#define GL_RGBA_INTEGER 0x8D99 -#define GL_BGR_INTEGER 0x8D9A -#define GL_BGRA_INTEGER 0x8D9B -#define GL_SAMPLER_1D_ARRAY 0x8DC0 -#define GL_SAMPLER_2D_ARRAY 0x8DC1 -#define GL_SAMPLER_1D_ARRAY_SHADOW 0x8DC3 -#define GL_SAMPLER_2D_ARRAY_SHADOW 0x8DC4 -#define GL_SAMPLER_CUBE_SHADOW 0x8DC5 -#define GL_UNSIGNED_INT_VEC2 0x8DC6 -#define GL_UNSIGNED_INT_VEC3 0x8DC7 -#define GL_UNSIGNED_INT_VEC4 0x8DC8 -#define GL_INT_SAMPLER_1D 0x8DC9 -#define GL_INT_SAMPLER_2D 0x8DCA -#define GL_INT_SAMPLER_3D 0x8DCB -#define GL_INT_SAMPLER_CUBE 0x8DCC -#define GL_INT_SAMPLER_1D_ARRAY 0x8DCE -#define GL_INT_SAMPLER_2D_ARRAY 0x8DCF -#define GL_UNSIGNED_INT_SAMPLER_1D 0x8DD1 -#define GL_UNSIGNED_INT_SAMPLER_2D 0x8DD2 -#define GL_UNSIGNED_INT_SAMPLER_3D 0x8DD3 -#define GL_UNSIGNED_INT_SAMPLER_CUBE 0x8DD4 -#define GL_UNSIGNED_INT_SAMPLER_1D_ARRAY 0x8DD6 -#define GL_UNSIGNED_INT_SAMPLER_2D_ARRAY 0x8DD7 -#define GL_QUERY_WAIT 0x8E13 -#define GL_QUERY_NO_WAIT 0x8E14 -#define GL_QUERY_BY_REGION_WAIT 0x8E15 -#define GL_QUERY_BY_REGION_NO_WAIT 0x8E16 -#define GL_BUFFER_ACCESS_FLAGS 0x911F -#define GL_BUFFER_MAP_LENGTH 0x9120 -#define GL_BUFFER_MAP_OFFSET 0x9121 -#define GL_DEPTH_COMPONENT32F 0x8CAC -#define GL_DEPTH32F_STENCIL8 0x8CAD -#define GL_FLOAT_32_UNSIGNED_INT_24_8_REV 0x8DAD -#define GL_INVALID_FRAMEBUFFER_OPERATION 0x0506 -#define GL_FRAMEBUFFER_ATTACHMENT_COLOR_ENCODING 0x8210 -#define GL_FRAMEBUFFER_ATTACHMENT_COMPONENT_TYPE 0x8211 -#define GL_FRAMEBUFFER_ATTACHMENT_RED_SIZE 0x8212 -#define GL_FRAMEBUFFER_ATTACHMENT_GREEN_SIZE 0x8213 -#define GL_FRAMEBUFFER_ATTACHMENT_BLUE_SIZE 0x8214 -#define GL_FRAMEBUFFER_ATTACHMENT_ALPHA_SIZE 0x8215 -#define GL_FRAMEBUFFER_ATTACHMENT_DEPTH_SIZE 0x8216 -#define GL_FRAMEBUFFER_ATTACHMENT_STENCIL_SIZE 0x8217 -#define GL_FRAMEBUFFER_DEFAULT 0x8218 -#define GL_FRAMEBUFFER_UNDEFINED 0x8219 -#define GL_DEPTH_STENCIL_ATTACHMENT 0x821A -#define GL_MAX_RENDERBUFFER_SIZE 0x84E8 -#define GL_DEPTH_STENCIL 0x84F9 -#define GL_UNSIGNED_INT_24_8 0x84FA -#define GL_DEPTH24_STENCIL8 0x88F0 -#define GL_TEXTURE_STENCIL_SIZE 0x88F1 -#define GL_TEXTURE_RED_TYPE 0x8C10 -#define GL_TEXTURE_GREEN_TYPE 0x8C11 -#define GL_TEXTURE_BLUE_TYPE 0x8C12 -#define GL_TEXTURE_ALPHA_TYPE 0x8C13 -#define GL_TEXTURE_DEPTH_TYPE 0x8C16 -#define GL_UNSIGNED_NORMALIZED 0x8C17 -#define GL_FRAMEBUFFER_BINDING 0x8CA6 -#define GL_DRAW_FRAMEBUFFER_BINDING 0x8CA6 -#define GL_RENDERBUFFER_BINDING 0x8CA7 -#define GL_READ_FRAMEBUFFER 0x8CA8 -#define GL_DRAW_FRAMEBUFFER 0x8CA9 -#define GL_READ_FRAMEBUFFER_BINDING 0x8CAA -#define GL_RENDERBUFFER_SAMPLES 0x8CAB -#define GL_FRAMEBUFFER_ATTACHMENT_OBJECT_TYPE 0x8CD0 -#define GL_FRAMEBUFFER_ATTACHMENT_OBJECT_NAME 0x8CD1 -#define GL_FRAMEBUFFER_ATTACHMENT_TEXTURE_LEVEL 0x8CD2 -#define GL_FRAMEBUFFER_ATTACHMENT_TEXTURE_CUBE_MAP_FACE 0x8CD3 -#define GL_FRAMEBUFFER_ATTACHMENT_TEXTURE_LAYER 0x8CD4 -#define GL_FRAMEBUFFER_COMPLETE 0x8CD5 -#define GL_FRAMEBUFFER_INCOMPLETE_ATTACHMENT 0x8CD6 -#define GL_FRAMEBUFFER_INCOMPLETE_MISSING_ATTACHMENT 0x8CD7 -#define GL_FRAMEBUFFER_INCOMPLETE_DRAW_BUFFER 0x8CDB -#define GL_FRAMEBUFFER_INCOMPLETE_READ_BUFFER 0x8CDC -#define GL_FRAMEBUFFER_UNSUPPORTED 0x8CDD -#define GL_MAX_COLOR_ATTACHMENTS 0x8CDF -#define GL_COLOR_ATTACHMENT0 0x8CE0 -#define GL_COLOR_ATTACHMENT1 0x8CE1 -#define GL_COLOR_ATTACHMENT2 0x8CE2 -#define GL_COLOR_ATTACHMENT3 0x8CE3 -#define GL_COLOR_ATTACHMENT4 0x8CE4 -#define GL_COLOR_ATTACHMENT5 0x8CE5 -#define GL_COLOR_ATTACHMENT6 0x8CE6 -#define GL_COLOR_ATTACHMENT7 0x8CE7 -#define GL_COLOR_ATTACHMENT8 0x8CE8 -#define GL_COLOR_ATTACHMENT9 0x8CE9 -#define GL_COLOR_ATTACHMENT10 0x8CEA -#define GL_COLOR_ATTACHMENT11 0x8CEB -#define GL_COLOR_ATTACHMENT12 0x8CEC -#define GL_COLOR_ATTACHMENT13 0x8CED -#define GL_COLOR_ATTACHMENT14 0x8CEE -#define GL_COLOR_ATTACHMENT15 0x8CEF -#define GL_DEPTH_ATTACHMENT 0x8D00 -#define GL_STENCIL_ATTACHMENT 0x8D20 -#define GL_FRAMEBUFFER 0x8D40 -#define GL_RENDERBUFFER 0x8D41 -#define GL_RENDERBUFFER_WIDTH 0x8D42 -#define GL_RENDERBUFFER_HEIGHT 0x8D43 -#define GL_RENDERBUFFER_INTERNAL_FORMAT 0x8D44 -#define GL_STENCIL_INDEX1 0x8D46 -#define GL_STENCIL_INDEX4 0x8D47 -#define GL_STENCIL_INDEX8 0x8D48 -#define GL_STENCIL_INDEX16 0x8D49 -#define GL_RENDERBUFFER_RED_SIZE 0x8D50 -#define GL_RENDERBUFFER_GREEN_SIZE 0x8D51 -#define GL_RENDERBUFFER_BLUE_SIZE 0x8D52 -#define GL_RENDERBUFFER_ALPHA_SIZE 0x8D53 -#define GL_RENDERBUFFER_DEPTH_SIZE 0x8D54 -#define GL_RENDERBUFFER_STENCIL_SIZE 0x8D55 -#define GL_FRAMEBUFFER_INCOMPLETE_MULTISAMPLE 0x8D56 -#define GL_MAX_SAMPLES 0x8D57 -#define GL_INDEX 0x8222 -#define GL_TEXTURE_LUMINANCE_TYPE 0x8C14 -#define GL_TEXTURE_INTENSITY_TYPE 0x8C15 -#define GL_FRAMEBUFFER_SRGB 0x8DB9 -#define GL_HALF_FLOAT 0x140B -#define GL_MAP_READ_BIT 0x0001 -#define GL_MAP_WRITE_BIT 0x0002 -#define GL_MAP_INVALIDATE_RANGE_BIT 0x0004 -#define GL_MAP_INVALIDATE_BUFFER_BIT 0x0008 -#define GL_MAP_FLUSH_EXPLICIT_BIT 0x0010 -#define GL_MAP_UNSYNCHRONIZED_BIT 0x0020 -#define GL_COMPRESSED_RED_RGTC1 0x8DBB -#define GL_COMPRESSED_SIGNED_RED_RGTC1 0x8DBC -#define GL_COMPRESSED_RG_RGTC2 0x8DBD -#define GL_COMPRESSED_SIGNED_RG_RGTC2 0x8DBE -#define GL_RG 0x8227 -#define GL_RG_INTEGER 0x8228 -#define GL_R8 0x8229 -#define GL_R16 0x822A -#define GL_RG8 0x822B -#define GL_RG16 0x822C -#define GL_R16F 0x822D -#define GL_R32F 0x822E -#define GL_RG16F 0x822F -#define GL_RG32F 0x8230 -#define GL_R8I 0x8231 -#define GL_R8UI 0x8232 -#define GL_R16I 0x8233 -#define GL_R16UI 0x8234 -#define GL_R32I 0x8235 -#define GL_R32UI 0x8236 -#define GL_RG8I 0x8237 -#define GL_RG8UI 0x8238 -#define GL_RG16I 0x8239 -#define GL_RG16UI 0x823A -#define GL_RG32I 0x823B -#define GL_RG32UI 0x823C -#define GL_VERTEX_ARRAY_BINDING 0x85B5 -#define GL_CLAMP_VERTEX_COLOR 0x891A -#define GL_CLAMP_FRAGMENT_COLOR 0x891B -#define GL_ALPHA_INTEGER 0x8D97 -typedef void (APIENTRYP PFNGLCOLORMASKIPROC) (GLuint index, GLboolean r, GLboolean g, GLboolean b, GLboolean a); -typedef void (APIENTRYP PFNGLGETBOOLEANI_VPROC) (GLenum target, GLuint index, GLboolean *data); -typedef void (APIENTRYP PFNGLGETINTEGERI_VPROC) (GLenum target, GLuint index, GLint *data); -typedef void (APIENTRYP PFNGLENABLEIPROC) (GLenum target, GLuint index); -typedef void (APIENTRYP PFNGLDISABLEIPROC) (GLenum target, GLuint index); -typedef GLboolean (APIENTRYP PFNGLISENABLEDIPROC) (GLenum target, GLuint index); -typedef void (APIENTRYP PFNGLBEGINTRANSFORMFEEDBACKPROC) (GLenum primitiveMode); -typedef void (APIENTRYP PFNGLENDTRANSFORMFEEDBACKPROC) (void); -typedef void (APIENTRYP PFNGLBINDBUFFERRANGEPROC) (GLenum target, GLuint index, GLuint buffer, GLintptr offset, GLsizeiptr size); -typedef void (APIENTRYP PFNGLBINDBUFFERBASEPROC) (GLenum target, GLuint index, GLuint buffer); -typedef void (APIENTRYP PFNGLTRANSFORMFEEDBACKVARYINGSPROC) (GLuint program, GLsizei count, const GLchar *const*varyings, GLenum bufferMode); -typedef void (APIENTRYP PFNGLGETTRANSFORMFEEDBACKVARYINGPROC) (GLuint program, GLuint index, GLsizei bufSize, GLsizei *length, GLsizei *size, GLenum *type, GLchar *name); -typedef void (APIENTRYP PFNGLCLAMPCOLORPROC) (GLenum target, GLenum clamp); -typedef void (APIENTRYP PFNGLBEGINCONDITIONALRENDERPROC) (GLuint id, GLenum mode); -typedef void (APIENTRYP PFNGLENDCONDITIONALRENDERPROC) (void); -typedef void (APIENTRYP PFNGLVERTEXATTRIBIPOINTERPROC) (GLuint index, GLint size, GLenum type, GLsizei stride, const void *pointer); -typedef void (APIENTRYP PFNGLGETVERTEXATTRIBIIVPROC) (GLuint index, GLenum pname, GLint *params); -typedef void (APIENTRYP PFNGLGETVERTEXATTRIBIUIVPROC) (GLuint index, GLenum pname, GLuint *params); -typedef void (APIENTRYP PFNGLVERTEXATTRIBI1IPROC) (GLuint index, GLint x); -typedef void (APIENTRYP PFNGLVERTEXATTRIBI2IPROC) (GLuint index, GLint x, GLint y); -typedef void (APIENTRYP PFNGLVERTEXATTRIBI3IPROC) (GLuint index, GLint x, GLint y, GLint z); -typedef void (APIENTRYP PFNGLVERTEXATTRIBI4IPROC) (GLuint index, GLint x, GLint y, GLint z, GLint w); -typedef void (APIENTRYP PFNGLVERTEXATTRIBI1UIPROC) (GLuint index, GLuint x); -typedef void (APIENTRYP PFNGLVERTEXATTRIBI2UIPROC) (GLuint index, GLuint x, GLuint y); -typedef void (APIENTRYP PFNGLVERTEXATTRIBI3UIPROC) (GLuint index, GLuint x, GLuint y, GLuint z); -typedef void (APIENTRYP PFNGLVERTEXATTRIBI4UIPROC) (GLuint index, GLuint x, GLuint y, GLuint z, GLuint w); -typedef void (APIENTRYP PFNGLVERTEXATTRIBI1IVPROC) (GLuint index, const GLint *v); -typedef void (APIENTRYP PFNGLVERTEXATTRIBI2IVPROC) (GLuint index, const GLint *v); -typedef void (APIENTRYP PFNGLVERTEXATTRIBI3IVPROC) (GLuint index, const GLint *v); -typedef void (APIENTRYP PFNGLVERTEXATTRIBI4IVPROC) (GLuint index, const GLint *v); -typedef void (APIENTRYP PFNGLVERTEXATTRIBI1UIVPROC) (GLuint index, const GLuint *v); -typedef void (APIENTRYP PFNGLVERTEXATTRIBI2UIVPROC) (GLuint index, const GLuint *v); -typedef void (APIENTRYP PFNGLVERTEXATTRIBI3UIVPROC) (GLuint index, const GLuint *v); -typedef void (APIENTRYP PFNGLVERTEXATTRIBI4UIVPROC) (GLuint index, const GLuint *v); -typedef void (APIENTRYP PFNGLVERTEXATTRIBI4BVPROC) (GLuint index, const GLbyte *v); -typedef void (APIENTRYP PFNGLVERTEXATTRIBI4SVPROC) (GLuint index, const GLshort *v); -typedef void (APIENTRYP PFNGLVERTEXATTRIBI4UBVPROC) (GLuint index, const GLubyte *v); -typedef void (APIENTRYP PFNGLVERTEXATTRIBI4USVPROC) (GLuint index, const GLushort *v); -typedef void (APIENTRYP PFNGLGETUNIFORMUIVPROC) (GLuint program, GLint location, GLuint *params); -typedef void (APIENTRYP PFNGLBINDFRAGDATALOCATIONPROC) (GLuint program, GLuint color, const GLchar *name); -typedef GLint (APIENTRYP PFNGLGETFRAGDATALOCATIONPROC) (GLuint program, const GLchar *name); -typedef void (APIENTRYP PFNGLUNIFORM1UIPROC) (GLint location, GLuint v0); -typedef void (APIENTRYP PFNGLUNIFORM2UIPROC) (GLint location, GLuint v0, GLuint v1); -typedef void (APIENTRYP PFNGLUNIFORM3UIPROC) (GLint location, GLuint v0, GLuint v1, GLuint v2); -typedef void (APIENTRYP PFNGLUNIFORM4UIPROC) (GLint location, GLuint v0, GLuint v1, GLuint v2, GLuint v3); -typedef void (APIENTRYP PFNGLUNIFORM1UIVPROC) (GLint location, GLsizei count, const GLuint *value); -typedef void (APIENTRYP PFNGLUNIFORM2UIVPROC) (GLint location, GLsizei count, const GLuint *value); -typedef void (APIENTRYP PFNGLUNIFORM3UIVPROC) (GLint location, GLsizei count, const GLuint *value); -typedef void (APIENTRYP PFNGLUNIFORM4UIVPROC) (GLint location, GLsizei count, const GLuint *value); -typedef void (APIENTRYP PFNGLTEXPARAMETERIIVPROC) (GLenum target, GLenum pname, const GLint *params); -typedef void (APIENTRYP PFNGLTEXPARAMETERIUIVPROC) (GLenum target, GLenum pname, const GLuint *params); -typedef void (APIENTRYP PFNGLGETTEXPARAMETERIIVPROC) (GLenum target, GLenum pname, GLint *params); -typedef void (APIENTRYP PFNGLGETTEXPARAMETERIUIVPROC) (GLenum target, GLenum pname, GLuint *params); -typedef void (APIENTRYP PFNGLCLEARBUFFERIVPROC) (GLenum buffer, GLint drawbuffer, const GLint *value); -typedef void (APIENTRYP PFNGLCLEARBUFFERUIVPROC) (GLenum buffer, GLint drawbuffer, const GLuint *value); -typedef void (APIENTRYP PFNGLCLEARBUFFERFVPROC) (GLenum buffer, GLint drawbuffer, const GLfloat *value); -typedef void (APIENTRYP PFNGLCLEARBUFFERFIPROC) (GLenum buffer, GLint drawbuffer, GLfloat depth, GLint stencil); -typedef const GLubyte *(APIENTRYP PFNGLGETSTRINGIPROC) (GLenum name, GLuint index); -typedef GLboolean (APIENTRYP PFNGLISRENDERBUFFERPROC) (GLuint renderbuffer); -typedef void (APIENTRYP PFNGLBINDRENDERBUFFERPROC) (GLenum target, GLuint renderbuffer); -typedef void (APIENTRYP PFNGLDELETERENDERBUFFERSPROC) (GLsizei n, const GLuint *renderbuffers); -typedef void (APIENTRYP PFNGLGENRENDERBUFFERSPROC) (GLsizei n, GLuint *renderbuffers); -typedef void (APIENTRYP PFNGLRENDERBUFFERSTORAGEPROC) (GLenum target, GLenum internalformat, GLsizei width, GLsizei height); -typedef void (APIENTRYP PFNGLGETRENDERBUFFERPARAMETERIVPROC) (GLenum target, GLenum pname, GLint *params); -typedef GLboolean (APIENTRYP PFNGLISFRAMEBUFFERPROC) (GLuint framebuffer); -typedef void (APIENTRYP PFNGLBINDFRAMEBUFFERPROC) (GLenum target, GLuint framebuffer); -typedef void (APIENTRYP PFNGLDELETEFRAMEBUFFERSPROC) (GLsizei n, const GLuint *framebuffers); -typedef void (APIENTRYP PFNGLGENFRAMEBUFFERSPROC) (GLsizei n, GLuint *framebuffers); -typedef GLenum (APIENTRYP PFNGLCHECKFRAMEBUFFERSTATUSPROC) (GLenum target); -typedef void (APIENTRYP PFNGLFRAMEBUFFERTEXTURE1DPROC) (GLenum target, GLenum attachment, GLenum textarget, GLuint texture, GLint level); -typedef void (APIENTRYP PFNGLFRAMEBUFFERTEXTURE2DPROC) (GLenum target, GLenum attachment, GLenum textarget, GLuint texture, GLint level); -typedef void (APIENTRYP PFNGLFRAMEBUFFERTEXTURE3DPROC) (GLenum target, GLenum attachment, GLenum textarget, GLuint texture, GLint level, GLint zoffset); -typedef void (APIENTRYP PFNGLFRAMEBUFFERRENDERBUFFERPROC) (GLenum target, GLenum attachment, GLenum renderbuffertarget, GLuint renderbuffer); -typedef void (APIENTRYP PFNGLGETFRAMEBUFFERATTACHMENTPARAMETERIVPROC) (GLenum target, GLenum attachment, GLenum pname, GLint *params); -typedef void (APIENTRYP PFNGLGENERATEMIPMAPPROC) (GLenum target); -typedef void (APIENTRYP PFNGLBLITFRAMEBUFFERPROC) (GLint srcX0, GLint srcY0, GLint srcX1, GLint srcY1, GLint dstX0, GLint dstY0, GLint dstX1, GLint dstY1, GLbitfield mask, GLenum filter); -typedef void (APIENTRYP PFNGLRENDERBUFFERSTORAGEMULTISAMPLEPROC) (GLenum target, GLsizei samples, GLenum internalformat, GLsizei width, GLsizei height); -typedef void (APIENTRYP PFNGLFRAMEBUFFERTEXTURELAYERPROC) (GLenum target, GLenum attachment, GLuint texture, GLint level, GLint layer); -typedef void *(APIENTRYP PFNGLMAPBUFFERRANGEPROC) (GLenum target, GLintptr offset, GLsizeiptr length, GLbitfield access); -typedef void (APIENTRYP PFNGLFLUSHMAPPEDBUFFERRANGEPROC) (GLenum target, GLintptr offset, GLsizeiptr length); -typedef void (APIENTRYP PFNGLBINDVERTEXARRAYPROC) (GLuint array); -typedef void (APIENTRYP PFNGLDELETEVERTEXARRAYSPROC) (GLsizei n, const GLuint *arrays); -typedef void (APIENTRYP PFNGLGENVERTEXARRAYSPROC) (GLsizei n, GLuint *arrays); -typedef GLboolean (APIENTRYP PFNGLISVERTEXARRAYPROC) (GLuint array); -#ifdef GL_GLEXT_PROTOTYPES -GLAPI void APIENTRY glColorMaski (GLuint index, GLboolean r, GLboolean g, GLboolean b, GLboolean a); -GLAPI void APIENTRY glGetBooleani_v (GLenum target, GLuint index, GLboolean *data); -GLAPI void APIENTRY glGetIntegeri_v (GLenum target, GLuint index, GLint *data); -GLAPI void APIENTRY glEnablei (GLenum target, GLuint index); -GLAPI void APIENTRY glDisablei (GLenum target, GLuint index); -GLAPI GLboolean APIENTRY glIsEnabledi (GLenum target, GLuint index); -GLAPI void APIENTRY glBeginTransformFeedback (GLenum primitiveMode); -GLAPI void APIENTRY glEndTransformFeedback (void); -GLAPI void APIENTRY glBindBufferRange (GLenum target, GLuint index, GLuint buffer, GLintptr offset, GLsizeiptr size); -GLAPI void APIENTRY glBindBufferBase (GLenum target, GLuint index, GLuint buffer); -GLAPI void APIENTRY glTransformFeedbackVaryings (GLuint program, GLsizei count, const GLchar *const*varyings, GLenum bufferMode); -GLAPI void APIENTRY glGetTransformFeedbackVarying (GLuint program, GLuint index, GLsizei bufSize, GLsizei *length, GLsizei *size, GLenum *type, GLchar *name); -GLAPI void APIENTRY glClampColor (GLenum target, GLenum clamp); -GLAPI void APIENTRY glBeginConditionalRender (GLuint id, GLenum mode); -GLAPI void APIENTRY glEndConditionalRender (void); -GLAPI void APIENTRY glVertexAttribIPointer (GLuint index, GLint size, GLenum type, GLsizei stride, const void *pointer); -GLAPI void APIENTRY glGetVertexAttribIiv (GLuint index, GLenum pname, GLint *params); -GLAPI void APIENTRY glGetVertexAttribIuiv (GLuint index, GLenum pname, GLuint *params); -GLAPI void APIENTRY glVertexAttribI1i (GLuint index, GLint x); -GLAPI void APIENTRY glVertexAttribI2i (GLuint index, GLint x, GLint y); -GLAPI void APIENTRY glVertexAttribI3i (GLuint index, GLint x, GLint y, GLint z); -GLAPI void APIENTRY glVertexAttribI4i (GLuint index, GLint x, GLint y, GLint z, GLint w); -GLAPI void APIENTRY glVertexAttribI1ui (GLuint index, GLuint x); -GLAPI void APIENTRY glVertexAttribI2ui (GLuint index, GLuint x, GLuint y); -GLAPI void APIENTRY glVertexAttribI3ui (GLuint index, GLuint x, GLuint y, GLuint z); -GLAPI void APIENTRY glVertexAttribI4ui (GLuint index, GLuint x, GLuint y, GLuint z, GLuint w); -GLAPI void APIENTRY glVertexAttribI1iv (GLuint index, const GLint *v); -GLAPI void APIENTRY glVertexAttribI2iv (GLuint index, const GLint *v); -GLAPI void APIENTRY glVertexAttribI3iv (GLuint index, const GLint *v); -GLAPI void APIENTRY glVertexAttribI4iv (GLuint index, const GLint *v); -GLAPI void APIENTRY glVertexAttribI1uiv (GLuint index, const GLuint *v); -GLAPI void APIENTRY glVertexAttribI2uiv (GLuint index, const GLuint *v); -GLAPI void APIENTRY glVertexAttribI3uiv (GLuint index, const GLuint *v); -GLAPI void APIENTRY glVertexAttribI4uiv (GLuint index, const GLuint *v); -GLAPI void APIENTRY glVertexAttribI4bv (GLuint index, const GLbyte *v); -GLAPI void APIENTRY glVertexAttribI4sv (GLuint index, const GLshort *v); -GLAPI void APIENTRY glVertexAttribI4ubv (GLuint index, const GLubyte *v); -GLAPI void APIENTRY glVertexAttribI4usv (GLuint index, const GLushort *v); -GLAPI void APIENTRY glGetUniformuiv (GLuint program, GLint location, GLuint *params); -GLAPI void APIENTRY glBindFragDataLocation (GLuint program, GLuint color, const GLchar *name); -GLAPI GLint APIENTRY glGetFragDataLocation (GLuint program, const GLchar *name); -GLAPI void APIENTRY glUniform1ui (GLint location, GLuint v0); -GLAPI void APIENTRY glUniform2ui (GLint location, GLuint v0, GLuint v1); -GLAPI void APIENTRY glUniform3ui (GLint location, GLuint v0, GLuint v1, GLuint v2); -GLAPI void APIENTRY glUniform4ui (GLint location, GLuint v0, GLuint v1, GLuint v2, GLuint v3); -GLAPI void APIENTRY glUniform1uiv (GLint location, GLsizei count, const GLuint *value); -GLAPI void APIENTRY glUniform2uiv (GLint location, GLsizei count, const GLuint *value); -GLAPI void APIENTRY glUniform3uiv (GLint location, GLsizei count, const GLuint *value); -GLAPI void APIENTRY glUniform4uiv (GLint location, GLsizei count, const GLuint *value); -GLAPI void APIENTRY glTexParameterIiv (GLenum target, GLenum pname, const GLint *params); -GLAPI void APIENTRY glTexParameterIuiv (GLenum target, GLenum pname, const GLuint *params); -GLAPI void APIENTRY glGetTexParameterIiv (GLenum target, GLenum pname, GLint *params); -GLAPI void APIENTRY glGetTexParameterIuiv (GLenum target, GLenum pname, GLuint *params); -GLAPI void APIENTRY glClearBufferiv (GLenum buffer, GLint drawbuffer, const GLint *value); -GLAPI void APIENTRY glClearBufferuiv (GLenum buffer, GLint drawbuffer, const GLuint *value); -GLAPI void APIENTRY glClearBufferfv (GLenum buffer, GLint drawbuffer, const GLfloat *value); -GLAPI void APIENTRY glClearBufferfi (GLenum buffer, GLint drawbuffer, GLfloat depth, GLint stencil); -GLAPI const GLubyte *APIENTRY glGetStringi (GLenum name, GLuint index); -GLAPI GLboolean APIENTRY glIsRenderbuffer (GLuint renderbuffer); -GLAPI void APIENTRY glBindRenderbuffer (GLenum target, GLuint renderbuffer); -GLAPI void APIENTRY glDeleteRenderbuffers (GLsizei n, const GLuint *renderbuffers); -GLAPI void APIENTRY glGenRenderbuffers (GLsizei n, GLuint *renderbuffers); -GLAPI void APIENTRY glRenderbufferStorage (GLenum target, GLenum internalformat, GLsizei width, GLsizei height); -GLAPI void APIENTRY glGetRenderbufferParameteriv (GLenum target, GLenum pname, GLint *params); -GLAPI GLboolean APIENTRY glIsFramebuffer (GLuint framebuffer); -GLAPI void APIENTRY glBindFramebuffer (GLenum target, GLuint framebuffer); -GLAPI void APIENTRY glDeleteFramebuffers (GLsizei n, const GLuint *framebuffers); -GLAPI void APIENTRY glGenFramebuffers (GLsizei n, GLuint *framebuffers); -GLAPI GLenum APIENTRY glCheckFramebufferStatus (GLenum target); -GLAPI void APIENTRY glFramebufferTexture1D (GLenum target, GLenum attachment, GLenum textarget, GLuint texture, GLint level); -GLAPI void APIENTRY glFramebufferTexture2D (GLenum target, GLenum attachment, GLenum textarget, GLuint texture, GLint level); -GLAPI void APIENTRY glFramebufferTexture3D (GLenum target, GLenum attachment, GLenum textarget, GLuint texture, GLint level, GLint zoffset); -GLAPI void APIENTRY glFramebufferRenderbuffer (GLenum target, GLenum attachment, GLenum renderbuffertarget, GLuint renderbuffer); -GLAPI void APIENTRY glGetFramebufferAttachmentParameteriv (GLenum target, GLenum attachment, GLenum pname, GLint *params); -GLAPI void APIENTRY glGenerateMipmap (GLenum target); -GLAPI void APIENTRY glBlitFramebuffer (GLint srcX0, GLint srcY0, GLint srcX1, GLint srcY1, GLint dstX0, GLint dstY0, GLint dstX1, GLint dstY1, GLbitfield mask, GLenum filter); -GLAPI void APIENTRY glRenderbufferStorageMultisample (GLenum target, GLsizei samples, GLenum internalformat, GLsizei width, GLsizei height); -GLAPI void APIENTRY glFramebufferTextureLayer (GLenum target, GLenum attachment, GLuint texture, GLint level, GLint layer); -GLAPI void *APIENTRY glMapBufferRange (GLenum target, GLintptr offset, GLsizeiptr length, GLbitfield access); -GLAPI void APIENTRY glFlushMappedBufferRange (GLenum target, GLintptr offset, GLsizeiptr length); -GLAPI void APIENTRY glBindVertexArray (GLuint array); -GLAPI void APIENTRY glDeleteVertexArrays (GLsizei n, const GLuint *arrays); -GLAPI void APIENTRY glGenVertexArrays (GLsizei n, GLuint *arrays); -GLAPI GLboolean APIENTRY glIsVertexArray (GLuint array); -#endif -#endif /* GL_VERSION_3_0 */ - -#ifndef GL_VERSION_3_1 -#define GL_VERSION_3_1 1 -#define GL_SAMPLER_2D_RECT 0x8B63 -#define GL_SAMPLER_2D_RECT_SHADOW 0x8B64 -#define GL_SAMPLER_BUFFER 0x8DC2 -#define GL_INT_SAMPLER_2D_RECT 0x8DCD -#define GL_INT_SAMPLER_BUFFER 0x8DD0 -#define GL_UNSIGNED_INT_SAMPLER_2D_RECT 0x8DD5 -#define GL_UNSIGNED_INT_SAMPLER_BUFFER 0x8DD8 -#define GL_TEXTURE_BUFFER 0x8C2A -#define GL_MAX_TEXTURE_BUFFER_SIZE 0x8C2B -#define GL_TEXTURE_BINDING_BUFFER 0x8C2C -#define GL_TEXTURE_BUFFER_DATA_STORE_BINDING 0x8C2D -#define GL_TEXTURE_RECTANGLE 0x84F5 -#define GL_TEXTURE_BINDING_RECTANGLE 0x84F6 -#define GL_PROXY_TEXTURE_RECTANGLE 0x84F7 -#define GL_MAX_RECTANGLE_TEXTURE_SIZE 0x84F8 -#define GL_R8_SNORM 0x8F94 -#define GL_RG8_SNORM 0x8F95 -#define GL_RGB8_SNORM 0x8F96 -#define GL_RGBA8_SNORM 0x8F97 -#define GL_R16_SNORM 0x8F98 -#define GL_RG16_SNORM 0x8F99 -#define GL_RGB16_SNORM 0x8F9A -#define GL_RGBA16_SNORM 0x8F9B -#define GL_SIGNED_NORMALIZED 0x8F9C -#define GL_PRIMITIVE_RESTART 0x8F9D -#define GL_PRIMITIVE_RESTART_INDEX 0x8F9E -#define GL_COPY_READ_BUFFER 0x8F36 -#define GL_COPY_WRITE_BUFFER 0x8F37 -#define GL_UNIFORM_BUFFER 0x8A11 -#define GL_UNIFORM_BUFFER_BINDING 0x8A28 -#define GL_UNIFORM_BUFFER_START 0x8A29 -#define GL_UNIFORM_BUFFER_SIZE 0x8A2A -#define GL_MAX_VERTEX_UNIFORM_BLOCKS 0x8A2B -#define GL_MAX_FRAGMENT_UNIFORM_BLOCKS 0x8A2D -#define GL_MAX_COMBINED_UNIFORM_BLOCKS 0x8A2E -#define GL_MAX_UNIFORM_BUFFER_BINDINGS 0x8A2F -#define GL_MAX_UNIFORM_BLOCK_SIZE 0x8A30 -#define GL_MAX_COMBINED_VERTEX_UNIFORM_COMPONENTS 0x8A31 -#define GL_MAX_COMBINED_FRAGMENT_UNIFORM_COMPONENTS 0x8A33 -#define GL_UNIFORM_BUFFER_OFFSET_ALIGNMENT 0x8A34 -#define GL_ACTIVE_UNIFORM_BLOCK_MAX_NAME_LENGTH 0x8A35 -#define GL_ACTIVE_UNIFORM_BLOCKS 0x8A36 -#define GL_UNIFORM_TYPE 0x8A37 -#define GL_UNIFORM_SIZE 0x8A38 -#define GL_UNIFORM_NAME_LENGTH 0x8A39 -#define GL_UNIFORM_BLOCK_INDEX 0x8A3A -#define GL_UNIFORM_OFFSET 0x8A3B -#define GL_UNIFORM_ARRAY_STRIDE 0x8A3C -#define GL_UNIFORM_MATRIX_STRIDE 0x8A3D -#define GL_UNIFORM_IS_ROW_MAJOR 0x8A3E -#define GL_UNIFORM_BLOCK_BINDING 0x8A3F -#define GL_UNIFORM_BLOCK_DATA_SIZE 0x8A40 -#define GL_UNIFORM_BLOCK_NAME_LENGTH 0x8A41 -#define GL_UNIFORM_BLOCK_ACTIVE_UNIFORMS 0x8A42 -#define GL_UNIFORM_BLOCK_ACTIVE_UNIFORM_INDICES 0x8A43 -#define GL_UNIFORM_BLOCK_REFERENCED_BY_VERTEX_SHADER 0x8A44 -#define GL_UNIFORM_BLOCK_REFERENCED_BY_FRAGMENT_SHADER 0x8A46 -#define GL_INVALID_INDEX 0xFFFFFFFFu -typedef void (APIENTRYP PFNGLDRAWARRAYSINSTANCEDPROC) (GLenum mode, GLint first, GLsizei count, GLsizei instancecount); -typedef void (APIENTRYP PFNGLDRAWELEMENTSINSTANCEDPROC) (GLenum mode, GLsizei count, GLenum type, const void *indices, GLsizei instancecount); -typedef void (APIENTRYP PFNGLTEXBUFFERPROC) (GLenum target, GLenum internalformat, GLuint buffer); -typedef void (APIENTRYP PFNGLPRIMITIVERESTARTINDEXPROC) (GLuint index); -typedef void (APIENTRYP PFNGLCOPYBUFFERSUBDATAPROC) (GLenum readTarget, GLenum writeTarget, GLintptr readOffset, GLintptr writeOffset, GLsizeiptr size); -typedef void (APIENTRYP PFNGLGETUNIFORMINDICESPROC) (GLuint program, GLsizei uniformCount, const GLchar *const*uniformNames, GLuint *uniformIndices); -typedef void (APIENTRYP PFNGLGETACTIVEUNIFORMSIVPROC) (GLuint program, GLsizei uniformCount, const GLuint *uniformIndices, GLenum pname, GLint *params); -typedef void (APIENTRYP PFNGLGETACTIVEUNIFORMNAMEPROC) (GLuint program, GLuint uniformIndex, GLsizei bufSize, GLsizei *length, GLchar *uniformName); -typedef GLuint (APIENTRYP PFNGLGETUNIFORMBLOCKINDEXPROC) (GLuint program, const GLchar *uniformBlockName); -typedef void (APIENTRYP PFNGLGETACTIVEUNIFORMBLOCKIVPROC) (GLuint program, GLuint uniformBlockIndex, GLenum pname, GLint *params); -typedef void (APIENTRYP PFNGLGETACTIVEUNIFORMBLOCKNAMEPROC) (GLuint program, GLuint uniformBlockIndex, GLsizei bufSize, GLsizei *length, GLchar *uniformBlockName); -typedef void (APIENTRYP PFNGLUNIFORMBLOCKBINDINGPROC) (GLuint program, GLuint uniformBlockIndex, GLuint uniformBlockBinding); -#ifdef GL_GLEXT_PROTOTYPES -GLAPI void APIENTRY glDrawArraysInstanced (GLenum mode, GLint first, GLsizei count, GLsizei instancecount); -GLAPI void APIENTRY glDrawElementsInstanced (GLenum mode, GLsizei count, GLenum type, const void *indices, GLsizei instancecount); -GLAPI void APIENTRY glTexBuffer (GLenum target, GLenum internalformat, GLuint buffer); -GLAPI void APIENTRY glPrimitiveRestartIndex (GLuint index); -GLAPI void APIENTRY glCopyBufferSubData (GLenum readTarget, GLenum writeTarget, GLintptr readOffset, GLintptr writeOffset, GLsizeiptr size); -GLAPI void APIENTRY glGetUniformIndices (GLuint program, GLsizei uniformCount, const GLchar *const*uniformNames, GLuint *uniformIndices); -GLAPI void APIENTRY glGetActiveUniformsiv (GLuint program, GLsizei uniformCount, const GLuint *uniformIndices, GLenum pname, GLint *params); -GLAPI void APIENTRY glGetActiveUniformName (GLuint program, GLuint uniformIndex, GLsizei bufSize, GLsizei *length, GLchar *uniformName); -GLAPI GLuint APIENTRY glGetUniformBlockIndex (GLuint program, const GLchar *uniformBlockName); -GLAPI void APIENTRY glGetActiveUniformBlockiv (GLuint program, GLuint uniformBlockIndex, GLenum pname, GLint *params); -GLAPI void APIENTRY glGetActiveUniformBlockName (GLuint program, GLuint uniformBlockIndex, GLsizei bufSize, GLsizei *length, GLchar *uniformBlockName); -GLAPI void APIENTRY glUniformBlockBinding (GLuint program, GLuint uniformBlockIndex, GLuint uniformBlockBinding); -#endif -#endif /* GL_VERSION_3_1 */ - -#ifndef GL_VERSION_3_2 -#define GL_VERSION_3_2 1 -typedef struct __GLsync *GLsync; -#ifndef GLEXT_64_TYPES_DEFINED -/* This code block is duplicated in glxext.h, so must be protected */ -#define GLEXT_64_TYPES_DEFINED -/* Define int32_t, int64_t, and uint64_t types for UST/MSC */ -/* (as used in the GL_EXT_timer_query extension). */ -#if defined(__STDC_VERSION__) && __STDC_VERSION__ >= 199901L -#include -#elif defined(__sun__) || defined(__digital__) -#include -#if defined(__STDC__) -#if defined(__arch64__) || defined(_LP64) -typedef long int int64_t; -typedef unsigned long int uint64_t; -#else -typedef long long int int64_t; -typedef unsigned long long int uint64_t; -#endif /* __arch64__ */ -#endif /* __STDC__ */ -#elif defined( __VMS ) || defined(__sgi) -#include -#elif defined(__SCO__) || defined(__USLC__) -#include -#elif defined(__UNIXOS2__) || defined(__SOL64__) -typedef long int int32_t; -typedef long long int int64_t; -typedef unsigned long long int uint64_t; -#elif defined(_WIN32) && defined(__GNUC__) -#include -#elif defined(_WIN32) -typedef __int32 int32_t; -typedef __int64 int64_t; -typedef unsigned __int64 uint64_t; -#else -/* Fallback if nothing above works */ -#include -#endif -#endif -typedef uint64_t GLuint64; -typedef int64_t GLint64; -#define GL_CONTEXT_CORE_PROFILE_BIT 0x00000001 -#define GL_CONTEXT_COMPATIBILITY_PROFILE_BIT 0x00000002 -#define GL_LINES_ADJACENCY 0x000A -#define GL_LINE_STRIP_ADJACENCY 0x000B -#define GL_TRIANGLES_ADJACENCY 0x000C -#define GL_TRIANGLE_STRIP_ADJACENCY 0x000D -#define GL_PROGRAM_POINT_SIZE 0x8642 -#define GL_MAX_GEOMETRY_TEXTURE_IMAGE_UNITS 0x8C29 -#define GL_FRAMEBUFFER_ATTACHMENT_LAYERED 0x8DA7 -#define GL_FRAMEBUFFER_INCOMPLETE_LAYER_TARGETS 0x8DA8 -#define GL_GEOMETRY_SHADER 0x8DD9 -#define GL_GEOMETRY_VERTICES_OUT 0x8916 -#define GL_GEOMETRY_INPUT_TYPE 0x8917 -#define GL_GEOMETRY_OUTPUT_TYPE 0x8918 -#define GL_MAX_GEOMETRY_UNIFORM_COMPONENTS 0x8DDF -#define GL_MAX_GEOMETRY_OUTPUT_VERTICES 0x8DE0 -#define GL_MAX_GEOMETRY_TOTAL_OUTPUT_COMPONENTS 0x8DE1 -#define GL_MAX_VERTEX_OUTPUT_COMPONENTS 0x9122 -#define GL_MAX_GEOMETRY_INPUT_COMPONENTS 0x9123 -#define GL_MAX_GEOMETRY_OUTPUT_COMPONENTS 0x9124 -#define GL_MAX_FRAGMENT_INPUT_COMPONENTS 0x9125 -#define GL_CONTEXT_PROFILE_MASK 0x9126 -#define GL_DEPTH_CLAMP 0x864F -#define GL_QUADS_FOLLOW_PROVOKING_VERTEX_CONVENTION 0x8E4C -#define GL_FIRST_VERTEX_CONVENTION 0x8E4D -#define GL_LAST_VERTEX_CONVENTION 0x8E4E -#define GL_PROVOKING_VERTEX 0x8E4F -#define GL_TEXTURE_CUBE_MAP_SEAMLESS 0x884F -#define GL_MAX_SERVER_WAIT_TIMEOUT 0x9111 -#define GL_OBJECT_TYPE 0x9112 -#define GL_SYNC_CONDITION 0x9113 -#define GL_SYNC_STATUS 0x9114 -#define GL_SYNC_FLAGS 0x9115 -#define GL_SYNC_FENCE 0x9116 -#define GL_SYNC_GPU_COMMANDS_COMPLETE 0x9117 -#define GL_UNSIGNALED 0x9118 -#define GL_SIGNALED 0x9119 -#define GL_ALREADY_SIGNALED 0x911A -#define GL_TIMEOUT_EXPIRED 0x911B -#define GL_CONDITION_SATISFIED 0x911C -#define GL_WAIT_FAILED 0x911D -#define GL_TIMEOUT_IGNORED 0xFFFFFFFFFFFFFFFFull -#define GL_SYNC_FLUSH_COMMANDS_BIT 0x00000001 -#define GL_SAMPLE_POSITION 0x8E50 -#define GL_SAMPLE_MASK 0x8E51 -#define GL_SAMPLE_MASK_VALUE 0x8E52 -#define GL_MAX_SAMPLE_MASK_WORDS 0x8E59 -#define GL_TEXTURE_2D_MULTISAMPLE 0x9100 -#define GL_PROXY_TEXTURE_2D_MULTISAMPLE 0x9101 -#define GL_TEXTURE_2D_MULTISAMPLE_ARRAY 0x9102 -#define GL_PROXY_TEXTURE_2D_MULTISAMPLE_ARRAY 0x9103 -#define GL_TEXTURE_BINDING_2D_MULTISAMPLE 0x9104 -#define GL_TEXTURE_BINDING_2D_MULTISAMPLE_ARRAY 0x9105 -#define GL_TEXTURE_SAMPLES 0x9106 -#define GL_TEXTURE_FIXED_SAMPLE_LOCATIONS 0x9107 -#define GL_SAMPLER_2D_MULTISAMPLE 0x9108 -#define GL_INT_SAMPLER_2D_MULTISAMPLE 0x9109 -#define GL_UNSIGNED_INT_SAMPLER_2D_MULTISAMPLE 0x910A -#define GL_SAMPLER_2D_MULTISAMPLE_ARRAY 0x910B -#define GL_INT_SAMPLER_2D_MULTISAMPLE_ARRAY 0x910C -#define GL_UNSIGNED_INT_SAMPLER_2D_MULTISAMPLE_ARRAY 0x910D -#define GL_MAX_COLOR_TEXTURE_SAMPLES 0x910E -#define GL_MAX_DEPTH_TEXTURE_SAMPLES 0x910F -#define GL_MAX_INTEGER_SAMPLES 0x9110 -typedef void (APIENTRYP PFNGLDRAWELEMENTSBASEVERTEXPROC) (GLenum mode, GLsizei count, GLenum type, const void *indices, GLint basevertex); -typedef void (APIENTRYP PFNGLDRAWRANGEELEMENTSBASEVERTEXPROC) (GLenum mode, GLuint start, GLuint end, GLsizei count, GLenum type, const void *indices, GLint basevertex); -typedef void (APIENTRYP PFNGLDRAWELEMENTSINSTANCEDBASEVERTEXPROC) (GLenum mode, GLsizei count, GLenum type, const void *indices, GLsizei instancecount, GLint basevertex); -typedef void (APIENTRYP PFNGLMULTIDRAWELEMENTSBASEVERTEXPROC) (GLenum mode, const GLsizei *count, GLenum type, const void *const*indices, GLsizei drawcount, const GLint *basevertex); -typedef void (APIENTRYP PFNGLPROVOKINGVERTEXPROC) (GLenum mode); -typedef GLsync (APIENTRYP PFNGLFENCESYNCPROC) (GLenum condition, GLbitfield flags); -typedef GLboolean (APIENTRYP PFNGLISSYNCPROC) (GLsync sync); -typedef void (APIENTRYP PFNGLDELETESYNCPROC) (GLsync sync); -typedef GLenum (APIENTRYP PFNGLCLIENTWAITSYNCPROC) (GLsync sync, GLbitfield flags, GLuint64 timeout); -typedef void (APIENTRYP PFNGLWAITSYNCPROC) (GLsync sync, GLbitfield flags, GLuint64 timeout); -typedef void (APIENTRYP PFNGLGETINTEGER64VPROC) (GLenum pname, GLint64 *data); -typedef void (APIENTRYP PFNGLGETSYNCIVPROC) (GLsync sync, GLenum pname, GLsizei bufSize, GLsizei *length, GLint *values); -typedef void (APIENTRYP PFNGLGETINTEGER64I_VPROC) (GLenum target, GLuint index, GLint64 *data); -typedef void (APIENTRYP PFNGLGETBUFFERPARAMETERI64VPROC) (GLenum target, GLenum pname, GLint64 *params); -typedef void (APIENTRYP PFNGLFRAMEBUFFERTEXTUREPROC) (GLenum target, GLenum attachment, GLuint texture, GLint level); -typedef void (APIENTRYP PFNGLTEXIMAGE2DMULTISAMPLEPROC) (GLenum target, GLsizei samples, GLenum internalformat, GLsizei width, GLsizei height, GLboolean fixedsamplelocations); -typedef void (APIENTRYP PFNGLTEXIMAGE3DMULTISAMPLEPROC) (GLenum target, GLsizei samples, GLenum internalformat, GLsizei width, GLsizei height, GLsizei depth, GLboolean fixedsamplelocations); -typedef void (APIENTRYP PFNGLGETMULTISAMPLEFVPROC) (GLenum pname, GLuint index, GLfloat *val); -typedef void (APIENTRYP PFNGLSAMPLEMASKIPROC) (GLuint maskNumber, GLbitfield mask); -#ifdef GL_GLEXT_PROTOTYPES -GLAPI void APIENTRY glDrawElementsBaseVertex (GLenum mode, GLsizei count, GLenum type, const void *indices, GLint basevertex); -GLAPI void APIENTRY glDrawRangeElementsBaseVertex (GLenum mode, GLuint start, GLuint end, GLsizei count, GLenum type, const void *indices, GLint basevertex); -GLAPI void APIENTRY glDrawElementsInstancedBaseVertex (GLenum mode, GLsizei count, GLenum type, const void *indices, GLsizei instancecount, GLint basevertex); -GLAPI void APIENTRY glMultiDrawElementsBaseVertex (GLenum mode, const GLsizei *count, GLenum type, const void *const*indices, GLsizei drawcount, const GLint *basevertex); -GLAPI void APIENTRY glProvokingVertex (GLenum mode); -GLAPI GLsync APIENTRY glFenceSync (GLenum condition, GLbitfield flags); -GLAPI GLboolean APIENTRY glIsSync (GLsync sync); -GLAPI void APIENTRY glDeleteSync (GLsync sync); -GLAPI GLenum APIENTRY glClientWaitSync (GLsync sync, GLbitfield flags, GLuint64 timeout); -GLAPI void APIENTRY glWaitSync (GLsync sync, GLbitfield flags, GLuint64 timeout); -GLAPI void APIENTRY glGetInteger64v (GLenum pname, GLint64 *data); -GLAPI void APIENTRY glGetSynciv (GLsync sync, GLenum pname, GLsizei bufSize, GLsizei *length, GLint *values); -GLAPI void APIENTRY glGetInteger64i_v (GLenum target, GLuint index, GLint64 *data); -GLAPI void APIENTRY glGetBufferParameteri64v (GLenum target, GLenum pname, GLint64 *params); -GLAPI void APIENTRY glFramebufferTexture (GLenum target, GLenum attachment, GLuint texture, GLint level); -GLAPI void APIENTRY glTexImage2DMultisample (GLenum target, GLsizei samples, GLenum internalformat, GLsizei width, GLsizei height, GLboolean fixedsamplelocations); -GLAPI void APIENTRY glTexImage3DMultisample (GLenum target, GLsizei samples, GLenum internalformat, GLsizei width, GLsizei height, GLsizei depth, GLboolean fixedsamplelocations); -GLAPI void APIENTRY glGetMultisamplefv (GLenum pname, GLuint index, GLfloat *val); -GLAPI void APIENTRY glSampleMaski (GLuint maskNumber, GLbitfield mask); -#endif -#endif /* GL_VERSION_3_2 */ - -#ifndef GL_VERSION_3_3 -#define GL_VERSION_3_3 1 -#define GL_VERTEX_ATTRIB_ARRAY_DIVISOR 0x88FE -#define GL_SRC1_COLOR 0x88F9 -#define GL_ONE_MINUS_SRC1_COLOR 0x88FA -#define GL_ONE_MINUS_SRC1_ALPHA 0x88FB -#define GL_MAX_DUAL_SOURCE_DRAW_BUFFERS 0x88FC -#define GL_ANY_SAMPLES_PASSED 0x8C2F -#define GL_SAMPLER_BINDING 0x8919 -#define GL_RGB10_A2UI 0x906F -#define GL_TEXTURE_SWIZZLE_R 0x8E42 -#define GL_TEXTURE_SWIZZLE_G 0x8E43 -#define GL_TEXTURE_SWIZZLE_B 0x8E44 -#define GL_TEXTURE_SWIZZLE_A 0x8E45 -#define GL_TEXTURE_SWIZZLE_RGBA 0x8E46 -#define GL_TIME_ELAPSED 0x88BF -#define GL_TIMESTAMP 0x8E28 -#define GL_INT_2_10_10_10_REV 0x8D9F -typedef void (APIENTRYP PFNGLBINDFRAGDATALOCATIONINDEXEDPROC) (GLuint program, GLuint colorNumber, GLuint index, const GLchar *name); -typedef GLint (APIENTRYP PFNGLGETFRAGDATAINDEXPROC) (GLuint program, const GLchar *name); -typedef void (APIENTRYP PFNGLGENSAMPLERSPROC) (GLsizei count, GLuint *samplers); -typedef void (APIENTRYP PFNGLDELETESAMPLERSPROC) (GLsizei count, const GLuint *samplers); -typedef GLboolean (APIENTRYP PFNGLISSAMPLERPROC) (GLuint sampler); -typedef void (APIENTRYP PFNGLBINDSAMPLERPROC) (GLuint unit, GLuint sampler); -typedef void (APIENTRYP PFNGLSAMPLERPARAMETERIPROC) (GLuint sampler, GLenum pname, GLint param); -typedef void (APIENTRYP PFNGLSAMPLERPARAMETERIVPROC) (GLuint sampler, GLenum pname, const GLint *param); -typedef void (APIENTRYP PFNGLSAMPLERPARAMETERFPROC) (GLuint sampler, GLenum pname, GLfloat param); -typedef void (APIENTRYP PFNGLSAMPLERPARAMETERFVPROC) (GLuint sampler, GLenum pname, const GLfloat *param); -typedef void (APIENTRYP PFNGLSAMPLERPARAMETERIIVPROC) (GLuint sampler, GLenum pname, const GLint *param); -typedef void (APIENTRYP PFNGLSAMPLERPARAMETERIUIVPROC) (GLuint sampler, GLenum pname, const GLuint *param); -typedef void (APIENTRYP PFNGLGETSAMPLERPARAMETERIVPROC) (GLuint sampler, GLenum pname, GLint *params); -typedef void (APIENTRYP PFNGLGETSAMPLERPARAMETERIIVPROC) (GLuint sampler, GLenum pname, GLint *params); -typedef void (APIENTRYP PFNGLGETSAMPLERPARAMETERFVPROC) (GLuint sampler, GLenum pname, GLfloat *params); -typedef void (APIENTRYP PFNGLGETSAMPLERPARAMETERIUIVPROC) (GLuint sampler, GLenum pname, GLuint *params); -typedef void (APIENTRYP PFNGLQUERYCOUNTERPROC) (GLuint id, GLenum target); -typedef void (APIENTRYP PFNGLGETQUERYOBJECTI64VPROC) (GLuint id, GLenum pname, GLint64 *params); -typedef void (APIENTRYP PFNGLGETQUERYOBJECTUI64VPROC) (GLuint id, GLenum pname, GLuint64 *params); -typedef void (APIENTRYP PFNGLVERTEXATTRIBDIVISORPROC) (GLuint index, GLuint divisor); -typedef void (APIENTRYP PFNGLVERTEXATTRIBP1UIPROC) (GLuint index, GLenum type, GLboolean normalized, GLuint value); -typedef void (APIENTRYP PFNGLVERTEXATTRIBP1UIVPROC) (GLuint index, GLenum type, GLboolean normalized, const GLuint *value); -typedef void (APIENTRYP PFNGLVERTEXATTRIBP2UIPROC) (GLuint index, GLenum type, GLboolean normalized, GLuint value); -typedef void (APIENTRYP PFNGLVERTEXATTRIBP2UIVPROC) (GLuint index, GLenum type, GLboolean normalized, const GLuint *value); -typedef void (APIENTRYP PFNGLVERTEXATTRIBP3UIPROC) (GLuint index, GLenum type, GLboolean normalized, GLuint value); -typedef void (APIENTRYP PFNGLVERTEXATTRIBP3UIVPROC) (GLuint index, GLenum type, GLboolean normalized, const GLuint *value); -typedef void (APIENTRYP PFNGLVERTEXATTRIBP4UIPROC) (GLuint index, GLenum type, GLboolean normalized, GLuint value); -typedef void (APIENTRYP PFNGLVERTEXATTRIBP4UIVPROC) (GLuint index, GLenum type, GLboolean normalized, const GLuint *value); -typedef void (APIENTRYP PFNGLVERTEXP2UIPROC) (GLenum type, GLuint value); -typedef void (APIENTRYP PFNGLVERTEXP2UIVPROC) (GLenum type, const GLuint *value); -typedef void (APIENTRYP PFNGLVERTEXP3UIPROC) (GLenum type, GLuint value); -typedef void (APIENTRYP PFNGLVERTEXP3UIVPROC) (GLenum type, const GLuint *value); -typedef void (APIENTRYP PFNGLVERTEXP4UIPROC) (GLenum type, GLuint value); -typedef void (APIENTRYP PFNGLVERTEXP4UIVPROC) (GLenum type, const GLuint *value); -typedef void (APIENTRYP PFNGLTEXCOORDP1UIPROC) (GLenum type, GLuint coords); -typedef void (APIENTRYP PFNGLTEXCOORDP1UIVPROC) (GLenum type, const GLuint *coords); -typedef void (APIENTRYP PFNGLTEXCOORDP2UIPROC) (GLenum type, GLuint coords); -typedef void (APIENTRYP PFNGLTEXCOORDP2UIVPROC) (GLenum type, const GLuint *coords); -typedef void (APIENTRYP PFNGLTEXCOORDP3UIPROC) (GLenum type, GLuint coords); -typedef void (APIENTRYP PFNGLTEXCOORDP3UIVPROC) (GLenum type, const GLuint *coords); -typedef void (APIENTRYP PFNGLTEXCOORDP4UIPROC) (GLenum type, GLuint coords); -typedef void (APIENTRYP PFNGLTEXCOORDP4UIVPROC) (GLenum type, const GLuint *coords); -typedef void (APIENTRYP PFNGLMULTITEXCOORDP1UIPROC) (GLenum texture, GLenum type, GLuint coords); -typedef void (APIENTRYP PFNGLMULTITEXCOORDP1UIVPROC) (GLenum texture, GLenum type, const GLuint *coords); -typedef void (APIENTRYP PFNGLMULTITEXCOORDP2UIPROC) (GLenum texture, GLenum type, GLuint coords); -typedef void (APIENTRYP PFNGLMULTITEXCOORDP2UIVPROC) (GLenum texture, GLenum type, const GLuint *coords); -typedef void (APIENTRYP PFNGLMULTITEXCOORDP3UIPROC) (GLenum texture, GLenum type, GLuint coords); -typedef void (APIENTRYP PFNGLMULTITEXCOORDP3UIVPROC) (GLenum texture, GLenum type, const GLuint *coords); -typedef void (APIENTRYP PFNGLMULTITEXCOORDP4UIPROC) (GLenum texture, GLenum type, GLuint coords); -typedef void (APIENTRYP PFNGLMULTITEXCOORDP4UIVPROC) (GLenum texture, GLenum type, const GLuint *coords); -typedef void (APIENTRYP PFNGLNORMALP3UIPROC) (GLenum type, GLuint coords); -typedef void (APIENTRYP PFNGLNORMALP3UIVPROC) (GLenum type, const GLuint *coords); -typedef void (APIENTRYP PFNGLCOLORP3UIPROC) (GLenum type, GLuint color); -typedef void (APIENTRYP PFNGLCOLORP3UIVPROC) (GLenum type, const GLuint *color); -typedef void (APIENTRYP PFNGLCOLORP4UIPROC) (GLenum type, GLuint color); -typedef void (APIENTRYP PFNGLCOLORP4UIVPROC) (GLenum type, const GLuint *color); -typedef void (APIENTRYP PFNGLSECONDARYCOLORP3UIPROC) (GLenum type, GLuint color); -typedef void (APIENTRYP PFNGLSECONDARYCOLORP3UIVPROC) (GLenum type, const GLuint *color); -#ifdef GL_GLEXT_PROTOTYPES -GLAPI void APIENTRY glBindFragDataLocationIndexed (GLuint program, GLuint colorNumber, GLuint index, const GLchar *name); -GLAPI GLint APIENTRY glGetFragDataIndex (GLuint program, const GLchar *name); -GLAPI void APIENTRY glGenSamplers (GLsizei count, GLuint *samplers); -GLAPI void APIENTRY glDeleteSamplers (GLsizei count, const GLuint *samplers); -GLAPI GLboolean APIENTRY glIsSampler (GLuint sampler); -GLAPI void APIENTRY glBindSampler (GLuint unit, GLuint sampler); -GLAPI void APIENTRY glSamplerParameteri (GLuint sampler, GLenum pname, GLint param); -GLAPI void APIENTRY glSamplerParameteriv (GLuint sampler, GLenum pname, const GLint *param); -GLAPI void APIENTRY glSamplerParameterf (GLuint sampler, GLenum pname, GLfloat param); -GLAPI void APIENTRY glSamplerParameterfv (GLuint sampler, GLenum pname, const GLfloat *param); -GLAPI void APIENTRY glSamplerParameterIiv (GLuint sampler, GLenum pname, const GLint *param); -GLAPI void APIENTRY glSamplerParameterIuiv (GLuint sampler, GLenum pname, const GLuint *param); -GLAPI void APIENTRY glGetSamplerParameteriv (GLuint sampler, GLenum pname, GLint *params); -GLAPI void APIENTRY glGetSamplerParameterIiv (GLuint sampler, GLenum pname, GLint *params); -GLAPI void APIENTRY glGetSamplerParameterfv (GLuint sampler, GLenum pname, GLfloat *params); -GLAPI void APIENTRY glGetSamplerParameterIuiv (GLuint sampler, GLenum pname, GLuint *params); -GLAPI void APIENTRY glQueryCounter (GLuint id, GLenum target); -GLAPI void APIENTRY glGetQueryObjecti64v (GLuint id, GLenum pname, GLint64 *params); -GLAPI void APIENTRY glGetQueryObjectui64v (GLuint id, GLenum pname, GLuint64 *params); -GLAPI void APIENTRY glVertexAttribDivisor (GLuint index, GLuint divisor); -GLAPI void APIENTRY glVertexAttribP1ui (GLuint index, GLenum type, GLboolean normalized, GLuint value); -GLAPI void APIENTRY glVertexAttribP1uiv (GLuint index, GLenum type, GLboolean normalized, const GLuint *value); -GLAPI void APIENTRY glVertexAttribP2ui (GLuint index, GLenum type, GLboolean normalized, GLuint value); -GLAPI void APIENTRY glVertexAttribP2uiv (GLuint index, GLenum type, GLboolean normalized, const GLuint *value); -GLAPI void APIENTRY glVertexAttribP3ui (GLuint index, GLenum type, GLboolean normalized, GLuint value); -GLAPI void APIENTRY glVertexAttribP3uiv (GLuint index, GLenum type, GLboolean normalized, const GLuint *value); -GLAPI void APIENTRY glVertexAttribP4ui (GLuint index, GLenum type, GLboolean normalized, GLuint value); -GLAPI void APIENTRY glVertexAttribP4uiv (GLuint index, GLenum type, GLboolean normalized, const GLuint *value); -GLAPI void APIENTRY glVertexP2ui (GLenum type, GLuint value); -GLAPI void APIENTRY glVertexP2uiv (GLenum type, const GLuint *value); -GLAPI void APIENTRY glVertexP3ui (GLenum type, GLuint value); -GLAPI void APIENTRY glVertexP3uiv (GLenum type, const GLuint *value); -GLAPI void APIENTRY glVertexP4ui (GLenum type, GLuint value); -GLAPI void APIENTRY glVertexP4uiv (GLenum type, const GLuint *value); -GLAPI void APIENTRY glTexCoordP1ui (GLenum type, GLuint coords); -GLAPI void APIENTRY glTexCoordP1uiv (GLenum type, const GLuint *coords); -GLAPI void APIENTRY glTexCoordP2ui (GLenum type, GLuint coords); -GLAPI void APIENTRY glTexCoordP2uiv (GLenum type, const GLuint *coords); -GLAPI void APIENTRY glTexCoordP3ui (GLenum type, GLuint coords); -GLAPI void APIENTRY glTexCoordP3uiv (GLenum type, const GLuint *coords); -GLAPI void APIENTRY glTexCoordP4ui (GLenum type, GLuint coords); -GLAPI void APIENTRY glTexCoordP4uiv (GLenum type, const GLuint *coords); -GLAPI void APIENTRY glMultiTexCoordP1ui (GLenum texture, GLenum type, GLuint coords); -GLAPI void APIENTRY glMultiTexCoordP1uiv (GLenum texture, GLenum type, const GLuint *coords); -GLAPI void APIENTRY glMultiTexCoordP2ui (GLenum texture, GLenum type, GLuint coords); -GLAPI void APIENTRY glMultiTexCoordP2uiv (GLenum texture, GLenum type, const GLuint *coords); -GLAPI void APIENTRY glMultiTexCoordP3ui (GLenum texture, GLenum type, GLuint coords); -GLAPI void APIENTRY glMultiTexCoordP3uiv (GLenum texture, GLenum type, const GLuint *coords); -GLAPI void APIENTRY glMultiTexCoordP4ui (GLenum texture, GLenum type, GLuint coords); -GLAPI void APIENTRY glMultiTexCoordP4uiv (GLenum texture, GLenum type, const GLuint *coords); -GLAPI void APIENTRY glNormalP3ui (GLenum type, GLuint coords); -GLAPI void APIENTRY glNormalP3uiv (GLenum type, const GLuint *coords); -GLAPI void APIENTRY glColorP3ui (GLenum type, GLuint color); -GLAPI void APIENTRY glColorP3uiv (GLenum type, const GLuint *color); -GLAPI void APIENTRY glColorP4ui (GLenum type, GLuint color); -GLAPI void APIENTRY glColorP4uiv (GLenum type, const GLuint *color); -GLAPI void APIENTRY glSecondaryColorP3ui (GLenum type, GLuint color); -GLAPI void APIENTRY glSecondaryColorP3uiv (GLenum type, const GLuint *color); -#endif -#endif /* GL_VERSION_3_3 */ - -#ifndef GL_VERSION_4_0 -#define GL_VERSION_4_0 1 -#define GL_SAMPLE_SHADING 0x8C36 -#define GL_MIN_SAMPLE_SHADING_VALUE 0x8C37 -#define GL_MIN_PROGRAM_TEXTURE_GATHER_OFFSET 0x8E5E -#define GL_MAX_PROGRAM_TEXTURE_GATHER_OFFSET 0x8E5F -#define GL_TEXTURE_CUBE_MAP_ARRAY 0x9009 -#define GL_TEXTURE_BINDING_CUBE_MAP_ARRAY 0x900A -#define GL_PROXY_TEXTURE_CUBE_MAP_ARRAY 0x900B -#define GL_SAMPLER_CUBE_MAP_ARRAY 0x900C -#define GL_SAMPLER_CUBE_MAP_ARRAY_SHADOW 0x900D -#define GL_INT_SAMPLER_CUBE_MAP_ARRAY 0x900E -#define GL_UNSIGNED_INT_SAMPLER_CUBE_MAP_ARRAY 0x900F -#define GL_DRAW_INDIRECT_BUFFER 0x8F3F -#define GL_DRAW_INDIRECT_BUFFER_BINDING 0x8F43 -#define GL_GEOMETRY_SHADER_INVOCATIONS 0x887F -#define GL_MAX_GEOMETRY_SHADER_INVOCATIONS 0x8E5A -#define GL_MIN_FRAGMENT_INTERPOLATION_OFFSET 0x8E5B -#define GL_MAX_FRAGMENT_INTERPOLATION_OFFSET 0x8E5C -#define GL_FRAGMENT_INTERPOLATION_OFFSET_BITS 0x8E5D -#define GL_MAX_VERTEX_STREAMS 0x8E71 -#define GL_DOUBLE_VEC2 0x8FFC -#define GL_DOUBLE_VEC3 0x8FFD -#define GL_DOUBLE_VEC4 0x8FFE -#define GL_DOUBLE_MAT2 0x8F46 -#define GL_DOUBLE_MAT3 0x8F47 -#define GL_DOUBLE_MAT4 0x8F48 -#define GL_DOUBLE_MAT2x3 0x8F49 -#define GL_DOUBLE_MAT2x4 0x8F4A -#define GL_DOUBLE_MAT3x2 0x8F4B -#define GL_DOUBLE_MAT3x4 0x8F4C -#define GL_DOUBLE_MAT4x2 0x8F4D -#define GL_DOUBLE_MAT4x3 0x8F4E -#define GL_ACTIVE_SUBROUTINES 0x8DE5 -#define GL_ACTIVE_SUBROUTINE_UNIFORMS 0x8DE6 -#define GL_ACTIVE_SUBROUTINE_UNIFORM_LOCATIONS 0x8E47 -#define GL_ACTIVE_SUBROUTINE_MAX_LENGTH 0x8E48 -#define GL_ACTIVE_SUBROUTINE_UNIFORM_MAX_LENGTH 0x8E49 -#define GL_MAX_SUBROUTINES 0x8DE7 -#define GL_MAX_SUBROUTINE_UNIFORM_LOCATIONS 0x8DE8 -#define GL_NUM_COMPATIBLE_SUBROUTINES 0x8E4A -#define GL_COMPATIBLE_SUBROUTINES 0x8E4B -#define GL_PATCHES 0x000E -#define GL_PATCH_VERTICES 0x8E72 -#define GL_PATCH_DEFAULT_INNER_LEVEL 0x8E73 -#define GL_PATCH_DEFAULT_OUTER_LEVEL 0x8E74 -#define GL_TESS_CONTROL_OUTPUT_VERTICES 0x8E75 -#define GL_TESS_GEN_MODE 0x8E76 -#define GL_TESS_GEN_SPACING 0x8E77 -#define GL_TESS_GEN_VERTEX_ORDER 0x8E78 -#define GL_TESS_GEN_POINT_MODE 0x8E79 -#define GL_ISOLINES 0x8E7A -#define GL_FRACTIONAL_ODD 0x8E7B -#define GL_FRACTIONAL_EVEN 0x8E7C -#define GL_MAX_PATCH_VERTICES 0x8E7D -#define GL_MAX_TESS_GEN_LEVEL 0x8E7E -#define GL_MAX_TESS_CONTROL_UNIFORM_COMPONENTS 0x8E7F -#define GL_MAX_TESS_EVALUATION_UNIFORM_COMPONENTS 0x8E80 -#define GL_MAX_TESS_CONTROL_TEXTURE_IMAGE_UNITS 0x8E81 -#define GL_MAX_TESS_EVALUATION_TEXTURE_IMAGE_UNITS 0x8E82 -#define GL_MAX_TESS_CONTROL_OUTPUT_COMPONENTS 0x8E83 -#define GL_MAX_TESS_PATCH_COMPONENTS 0x8E84 -#define GL_MAX_TESS_CONTROL_TOTAL_OUTPUT_COMPONENTS 0x8E85 -#define GL_MAX_TESS_EVALUATION_OUTPUT_COMPONENTS 0x8E86 -#define GL_MAX_TESS_CONTROL_UNIFORM_BLOCKS 0x8E89 -#define GL_MAX_TESS_EVALUATION_UNIFORM_BLOCKS 0x8E8A -#define GL_MAX_TESS_CONTROL_INPUT_COMPONENTS 0x886C -#define GL_MAX_TESS_EVALUATION_INPUT_COMPONENTS 0x886D -#define GL_MAX_COMBINED_TESS_CONTROL_UNIFORM_COMPONENTS 0x8E1E -#define GL_MAX_COMBINED_TESS_EVALUATION_UNIFORM_COMPONENTS 0x8E1F -#define GL_UNIFORM_BLOCK_REFERENCED_BY_TESS_CONTROL_SHADER 0x84F0 -#define GL_UNIFORM_BLOCK_REFERENCED_BY_TESS_EVALUATION_SHADER 0x84F1 -#define GL_TESS_EVALUATION_SHADER 0x8E87 -#define GL_TESS_CONTROL_SHADER 0x8E88 -#define GL_TRANSFORM_FEEDBACK 0x8E22 -#define GL_TRANSFORM_FEEDBACK_BUFFER_PAUSED 0x8E23 -#define GL_TRANSFORM_FEEDBACK_BUFFER_ACTIVE 0x8E24 -#define GL_TRANSFORM_FEEDBACK_BINDING 0x8E25 -#define GL_MAX_TRANSFORM_FEEDBACK_BUFFERS 0x8E70 -typedef void (APIENTRYP PFNGLMINSAMPLESHADINGPROC) (GLfloat value); -typedef void (APIENTRYP PFNGLBLENDEQUATIONIPROC) (GLuint buf, GLenum mode); -typedef void (APIENTRYP PFNGLBLENDEQUATIONSEPARATEIPROC) (GLuint buf, GLenum modeRGB, GLenum modeAlpha); -typedef void (APIENTRYP PFNGLBLENDFUNCIPROC) (GLuint buf, GLenum src, GLenum dst); -typedef void (APIENTRYP PFNGLBLENDFUNCSEPARATEIPROC) (GLuint buf, GLenum srcRGB, GLenum dstRGB, GLenum srcAlpha, GLenum dstAlpha); -typedef void (APIENTRYP PFNGLDRAWARRAYSINDIRECTPROC) (GLenum mode, const void *indirect); -typedef void (APIENTRYP PFNGLDRAWELEMENTSINDIRECTPROC) (GLenum mode, GLenum type, const void *indirect); -typedef void (APIENTRYP PFNGLUNIFORM1DPROC) (GLint location, GLdouble x); -typedef void (APIENTRYP PFNGLUNIFORM2DPROC) (GLint location, GLdouble x, GLdouble y); -typedef void (APIENTRYP PFNGLUNIFORM3DPROC) (GLint location, GLdouble x, GLdouble y, GLdouble z); -typedef void (APIENTRYP PFNGLUNIFORM4DPROC) (GLint location, GLdouble x, GLdouble y, GLdouble z, GLdouble w); -typedef void (APIENTRYP PFNGLUNIFORM1DVPROC) (GLint location, GLsizei count, const GLdouble *value); -typedef void (APIENTRYP PFNGLUNIFORM2DVPROC) (GLint location, GLsizei count, const GLdouble *value); -typedef void (APIENTRYP PFNGLUNIFORM3DVPROC) (GLint location, GLsizei count, const GLdouble *value); -typedef void (APIENTRYP PFNGLUNIFORM4DVPROC) (GLint location, GLsizei count, const GLdouble *value); -typedef void (APIENTRYP PFNGLUNIFORMMATRIX2DVPROC) (GLint location, GLsizei count, GLboolean transpose, const GLdouble *value); -typedef void (APIENTRYP PFNGLUNIFORMMATRIX3DVPROC) (GLint location, GLsizei count, GLboolean transpose, const GLdouble *value); -typedef void (APIENTRYP PFNGLUNIFORMMATRIX4DVPROC) (GLint location, GLsizei count, GLboolean transpose, const GLdouble *value); -typedef void (APIENTRYP PFNGLUNIFORMMATRIX2X3DVPROC) (GLint location, GLsizei count, GLboolean transpose, const GLdouble *value); -typedef void (APIENTRYP PFNGLUNIFORMMATRIX2X4DVPROC) (GLint location, GLsizei count, GLboolean transpose, const GLdouble *value); -typedef void (APIENTRYP PFNGLUNIFORMMATRIX3X2DVPROC) (GLint location, GLsizei count, GLboolean transpose, const GLdouble *value); -typedef void (APIENTRYP PFNGLUNIFORMMATRIX3X4DVPROC) (GLint location, GLsizei count, GLboolean transpose, const GLdouble *value); -typedef void (APIENTRYP PFNGLUNIFORMMATRIX4X2DVPROC) (GLint location, GLsizei count, GLboolean transpose, const GLdouble *value); -typedef void (APIENTRYP PFNGLUNIFORMMATRIX4X3DVPROC) (GLint location, GLsizei count, GLboolean transpose, const GLdouble *value); -typedef void (APIENTRYP PFNGLGETUNIFORMDVPROC) (GLuint program, GLint location, GLdouble *params); -typedef GLint (APIENTRYP PFNGLGETSUBROUTINEUNIFORMLOCATIONPROC) (GLuint program, GLenum shadertype, const GLchar *name); -typedef GLuint (APIENTRYP PFNGLGETSUBROUTINEINDEXPROC) (GLuint program, GLenum shadertype, const GLchar *name); -typedef void (APIENTRYP PFNGLGETACTIVESUBROUTINEUNIFORMIVPROC) (GLuint program, GLenum shadertype, GLuint index, GLenum pname, GLint *values); -typedef void (APIENTRYP PFNGLGETACTIVESUBROUTINEUNIFORMNAMEPROC) (GLuint program, GLenum shadertype, GLuint index, GLsizei bufsize, GLsizei *length, GLchar *name); -typedef void (APIENTRYP PFNGLGETACTIVESUBROUTINENAMEPROC) (GLuint program, GLenum shadertype, GLuint index, GLsizei bufsize, GLsizei *length, GLchar *name); -typedef void (APIENTRYP PFNGLUNIFORMSUBROUTINESUIVPROC) (GLenum shadertype, GLsizei count, const GLuint *indices); -typedef void (APIENTRYP PFNGLGETUNIFORMSUBROUTINEUIVPROC) (GLenum shadertype, GLint location, GLuint *params); -typedef void (APIENTRYP PFNGLGETPROGRAMSTAGEIVPROC) (GLuint program, GLenum shadertype, GLenum pname, GLint *values); -typedef void (APIENTRYP PFNGLPATCHPARAMETERIPROC) (GLenum pname, GLint value); -typedef void (APIENTRYP PFNGLPATCHPARAMETERFVPROC) (GLenum pname, const GLfloat *values); -typedef void (APIENTRYP PFNGLBINDTRANSFORMFEEDBACKPROC) (GLenum target, GLuint id); -typedef void (APIENTRYP PFNGLDELETETRANSFORMFEEDBACKSPROC) (GLsizei n, const GLuint *ids); -typedef void (APIENTRYP PFNGLGENTRANSFORMFEEDBACKSPROC) (GLsizei n, GLuint *ids); -typedef GLboolean (APIENTRYP PFNGLISTRANSFORMFEEDBACKPROC) (GLuint id); -typedef void (APIENTRYP PFNGLPAUSETRANSFORMFEEDBACKPROC) (void); -typedef void (APIENTRYP PFNGLRESUMETRANSFORMFEEDBACKPROC) (void); -typedef void (APIENTRYP PFNGLDRAWTRANSFORMFEEDBACKPROC) (GLenum mode, GLuint id); -typedef void (APIENTRYP PFNGLDRAWTRANSFORMFEEDBACKSTREAMPROC) (GLenum mode, GLuint id, GLuint stream); -typedef void (APIENTRYP PFNGLBEGINQUERYINDEXEDPROC) (GLenum target, GLuint index, GLuint id); -typedef void (APIENTRYP PFNGLENDQUERYINDEXEDPROC) (GLenum target, GLuint index); -typedef void (APIENTRYP PFNGLGETQUERYINDEXEDIVPROC) (GLenum target, GLuint index, GLenum pname, GLint *params); -#ifdef GL_GLEXT_PROTOTYPES -GLAPI void APIENTRY glMinSampleShading (GLfloat value); -GLAPI void APIENTRY glBlendEquationi (GLuint buf, GLenum mode); -GLAPI void APIENTRY glBlendEquationSeparatei (GLuint buf, GLenum modeRGB, GLenum modeAlpha); -GLAPI void APIENTRY glBlendFunci (GLuint buf, GLenum src, GLenum dst); -GLAPI void APIENTRY glBlendFuncSeparatei (GLuint buf, GLenum srcRGB, GLenum dstRGB, GLenum srcAlpha, GLenum dstAlpha); -GLAPI void APIENTRY glDrawArraysIndirect (GLenum mode, const void *indirect); -GLAPI void APIENTRY glDrawElementsIndirect (GLenum mode, GLenum type, const void *indirect); -GLAPI void APIENTRY glUniform1d (GLint location, GLdouble x); -GLAPI void APIENTRY glUniform2d (GLint location, GLdouble x, GLdouble y); -GLAPI void APIENTRY glUniform3d (GLint location, GLdouble x, GLdouble y, GLdouble z); -GLAPI void APIENTRY glUniform4d (GLint location, GLdouble x, GLdouble y, GLdouble z, GLdouble w); -GLAPI void APIENTRY glUniform1dv (GLint location, GLsizei count, const GLdouble *value); -GLAPI void APIENTRY glUniform2dv (GLint location, GLsizei count, const GLdouble *value); -GLAPI void APIENTRY glUniform3dv (GLint location, GLsizei count, const GLdouble *value); -GLAPI void APIENTRY glUniform4dv (GLint location, GLsizei count, const GLdouble *value); -GLAPI void APIENTRY glUniformMatrix2dv (GLint location, GLsizei count, GLboolean transpose, const GLdouble *value); -GLAPI void APIENTRY glUniformMatrix3dv (GLint location, GLsizei count, GLboolean transpose, const GLdouble *value); -GLAPI void APIENTRY glUniformMatrix4dv (GLint location, GLsizei count, GLboolean transpose, const GLdouble *value); -GLAPI void APIENTRY glUniformMatrix2x3dv (GLint location, GLsizei count, GLboolean transpose, const GLdouble *value); -GLAPI void APIENTRY glUniformMatrix2x4dv (GLint location, GLsizei count, GLboolean transpose, const GLdouble *value); -GLAPI void APIENTRY glUniformMatrix3x2dv (GLint location, GLsizei count, GLboolean transpose, const GLdouble *value); -GLAPI void APIENTRY glUniformMatrix3x4dv (GLint location, GLsizei count, GLboolean transpose, const GLdouble *value); -GLAPI void APIENTRY glUniformMatrix4x2dv (GLint location, GLsizei count, GLboolean transpose, const GLdouble *value); -GLAPI void APIENTRY glUniformMatrix4x3dv (GLint location, GLsizei count, GLboolean transpose, const GLdouble *value); -GLAPI void APIENTRY glGetUniformdv (GLuint program, GLint location, GLdouble *params); -GLAPI GLint APIENTRY glGetSubroutineUniformLocation (GLuint program, GLenum shadertype, const GLchar *name); -GLAPI GLuint APIENTRY glGetSubroutineIndex (GLuint program, GLenum shadertype, const GLchar *name); -GLAPI void APIENTRY glGetActiveSubroutineUniformiv (GLuint program, GLenum shadertype, GLuint index, GLenum pname, GLint *values); -GLAPI void APIENTRY glGetActiveSubroutineUniformName (GLuint program, GLenum shadertype, GLuint index, GLsizei bufsize, GLsizei *length, GLchar *name); -GLAPI void APIENTRY glGetActiveSubroutineName (GLuint program, GLenum shadertype, GLuint index, GLsizei bufsize, GLsizei *length, GLchar *name); -GLAPI void APIENTRY glUniformSubroutinesuiv (GLenum shadertype, GLsizei count, const GLuint *indices); -GLAPI void APIENTRY glGetUniformSubroutineuiv (GLenum shadertype, GLint location, GLuint *params); -GLAPI void APIENTRY glGetProgramStageiv (GLuint program, GLenum shadertype, GLenum pname, GLint *values); -GLAPI void APIENTRY glPatchParameteri (GLenum pname, GLint value); -GLAPI void APIENTRY glPatchParameterfv (GLenum pname, const GLfloat *values); -GLAPI void APIENTRY glBindTransformFeedback (GLenum target, GLuint id); -GLAPI void APIENTRY glDeleteTransformFeedbacks (GLsizei n, const GLuint *ids); -GLAPI void APIENTRY glGenTransformFeedbacks (GLsizei n, GLuint *ids); -GLAPI GLboolean APIENTRY glIsTransformFeedback (GLuint id); -GLAPI void APIENTRY glPauseTransformFeedback (void); -GLAPI void APIENTRY glResumeTransformFeedback (void); -GLAPI void APIENTRY glDrawTransformFeedback (GLenum mode, GLuint id); -GLAPI void APIENTRY glDrawTransformFeedbackStream (GLenum mode, GLuint id, GLuint stream); -GLAPI void APIENTRY glBeginQueryIndexed (GLenum target, GLuint index, GLuint id); -GLAPI void APIENTRY glEndQueryIndexed (GLenum target, GLuint index); -GLAPI void APIENTRY glGetQueryIndexediv (GLenum target, GLuint index, GLenum pname, GLint *params); -#endif -#endif /* GL_VERSION_4_0 */ - -#ifndef GL_VERSION_4_1 -#define GL_VERSION_4_1 1 -#define GL_FIXED 0x140C -#define GL_IMPLEMENTATION_COLOR_READ_TYPE 0x8B9A -#define GL_IMPLEMENTATION_COLOR_READ_FORMAT 0x8B9B -#define GL_LOW_FLOAT 0x8DF0 -#define GL_MEDIUM_FLOAT 0x8DF1 -#define GL_HIGH_FLOAT 0x8DF2 -#define GL_LOW_INT 0x8DF3 -#define GL_MEDIUM_INT 0x8DF4 -#define GL_HIGH_INT 0x8DF5 -#define GL_SHADER_COMPILER 0x8DFA -#define GL_SHADER_BINARY_FORMATS 0x8DF8 -#define GL_NUM_SHADER_BINARY_FORMATS 0x8DF9 -#define GL_MAX_VERTEX_UNIFORM_VECTORS 0x8DFB -#define GL_MAX_VARYING_VECTORS 0x8DFC -#define GL_MAX_FRAGMENT_UNIFORM_VECTORS 0x8DFD -#define GL_RGB565 0x8D62 -#define GL_PROGRAM_BINARY_RETRIEVABLE_HINT 0x8257 -#define GL_PROGRAM_BINARY_LENGTH 0x8741 -#define GL_NUM_PROGRAM_BINARY_FORMATS 0x87FE -#define GL_PROGRAM_BINARY_FORMATS 0x87FF -#define GL_VERTEX_SHADER_BIT 0x00000001 -#define GL_FRAGMENT_SHADER_BIT 0x00000002 -#define GL_GEOMETRY_SHADER_BIT 0x00000004 -#define GL_TESS_CONTROL_SHADER_BIT 0x00000008 -#define GL_TESS_EVALUATION_SHADER_BIT 0x00000010 -#define GL_ALL_SHADER_BITS 0xFFFFFFFF -#define GL_PROGRAM_SEPARABLE 0x8258 -#define GL_ACTIVE_PROGRAM 0x8259 -#define GL_PROGRAM_PIPELINE_BINDING 0x825A -#define GL_MAX_VIEWPORTS 0x825B -#define GL_VIEWPORT_SUBPIXEL_BITS 0x825C -#define GL_VIEWPORT_BOUNDS_RANGE 0x825D -#define GL_LAYER_PROVOKING_VERTEX 0x825E -#define GL_VIEWPORT_INDEX_PROVOKING_VERTEX 0x825F -#define GL_UNDEFINED_VERTEX 0x8260 -typedef void (APIENTRYP PFNGLRELEASESHADERCOMPILERPROC) (void); -typedef void (APIENTRYP PFNGLSHADERBINARYPROC) (GLsizei count, const GLuint *shaders, GLenum binaryformat, const void *binary, GLsizei length); -typedef void (APIENTRYP PFNGLGETSHADERPRECISIONFORMATPROC) (GLenum shadertype, GLenum precisiontype, GLint *range, GLint *precision); -typedef void (APIENTRYP PFNGLDEPTHRANGEFPROC) (GLfloat n, GLfloat f); -typedef void (APIENTRYP PFNGLCLEARDEPTHFPROC) (GLfloat d); -typedef void (APIENTRYP PFNGLGETPROGRAMBINARYPROC) (GLuint program, GLsizei bufSize, GLsizei *length, GLenum *binaryFormat, void *binary); -typedef void (APIENTRYP PFNGLPROGRAMBINARYPROC) (GLuint program, GLenum binaryFormat, const void *binary, GLsizei length); -typedef void (APIENTRYP PFNGLPROGRAMPARAMETERIPROC) (GLuint program, GLenum pname, GLint value); -typedef void (APIENTRYP PFNGLUSEPROGRAMSTAGESPROC) (GLuint pipeline, GLbitfield stages, GLuint program); -typedef void (APIENTRYP PFNGLACTIVESHADERPROGRAMPROC) (GLuint pipeline, GLuint program); -typedef GLuint (APIENTRYP PFNGLCREATESHADERPROGRAMVPROC) (GLenum type, GLsizei count, const GLchar *const*strings); -typedef void (APIENTRYP PFNGLBINDPROGRAMPIPELINEPROC) (GLuint pipeline); -typedef void (APIENTRYP PFNGLDELETEPROGRAMPIPELINESPROC) (GLsizei n, const GLuint *pipelines); -typedef void (APIENTRYP PFNGLGENPROGRAMPIPELINESPROC) (GLsizei n, GLuint *pipelines); -typedef GLboolean (APIENTRYP PFNGLISPROGRAMPIPELINEPROC) (GLuint pipeline); -typedef void (APIENTRYP PFNGLGETPROGRAMPIPELINEIVPROC) (GLuint pipeline, GLenum pname, GLint *params); -typedef void (APIENTRYP PFNGLPROGRAMUNIFORM1IPROC) (GLuint program, GLint location, GLint v0); -typedef void (APIENTRYP PFNGLPROGRAMUNIFORM1IVPROC) (GLuint program, GLint location, GLsizei count, const GLint *value); -typedef void (APIENTRYP PFNGLPROGRAMUNIFORM1FPROC) (GLuint program, GLint location, GLfloat v0); -typedef void (APIENTRYP PFNGLPROGRAMUNIFORM1FVPROC) (GLuint program, GLint location, GLsizei count, const GLfloat *value); -typedef void (APIENTRYP PFNGLPROGRAMUNIFORM1DPROC) (GLuint program, GLint location, GLdouble v0); -typedef void (APIENTRYP PFNGLPROGRAMUNIFORM1DVPROC) (GLuint program, GLint location, GLsizei count, const GLdouble *value); -typedef void (APIENTRYP PFNGLPROGRAMUNIFORM1UIPROC) (GLuint program, GLint location, GLuint v0); -typedef void (APIENTRYP PFNGLPROGRAMUNIFORM1UIVPROC) (GLuint program, GLint location, GLsizei count, const GLuint *value); -typedef void (APIENTRYP PFNGLPROGRAMUNIFORM2IPROC) (GLuint program, GLint location, GLint v0, GLint v1); -typedef void (APIENTRYP PFNGLPROGRAMUNIFORM2IVPROC) (GLuint program, GLint location, GLsizei count, const GLint *value); -typedef void (APIENTRYP PFNGLPROGRAMUNIFORM2FPROC) (GLuint program, GLint location, GLfloat v0, GLfloat v1); -typedef void (APIENTRYP PFNGLPROGRAMUNIFORM2FVPROC) (GLuint program, GLint location, GLsizei count, const GLfloat *value); -typedef void (APIENTRYP PFNGLPROGRAMUNIFORM2DPROC) (GLuint program, GLint location, GLdouble v0, GLdouble v1); -typedef void (APIENTRYP PFNGLPROGRAMUNIFORM2DVPROC) (GLuint program, GLint location, GLsizei count, const GLdouble *value); -typedef void (APIENTRYP PFNGLPROGRAMUNIFORM2UIPROC) (GLuint program, GLint location, GLuint v0, GLuint v1); -typedef void (APIENTRYP PFNGLPROGRAMUNIFORM2UIVPROC) (GLuint program, GLint location, GLsizei count, const GLuint *value); -typedef void (APIENTRYP PFNGLPROGRAMUNIFORM3IPROC) (GLuint program, GLint location, GLint v0, GLint v1, GLint v2); -typedef void (APIENTRYP PFNGLPROGRAMUNIFORM3IVPROC) (GLuint program, GLint location, GLsizei count, const GLint *value); -typedef void (APIENTRYP PFNGLPROGRAMUNIFORM3FPROC) (GLuint program, GLint location, GLfloat v0, GLfloat v1, GLfloat v2); -typedef void (APIENTRYP PFNGLPROGRAMUNIFORM3FVPROC) (GLuint program, GLint location, GLsizei count, const GLfloat *value); -typedef void (APIENTRYP PFNGLPROGRAMUNIFORM3DPROC) (GLuint program, GLint location, GLdouble v0, GLdouble v1, GLdouble v2); -typedef void (APIENTRYP PFNGLPROGRAMUNIFORM3DVPROC) (GLuint program, GLint location, GLsizei count, const GLdouble *value); -typedef void (APIENTRYP PFNGLPROGRAMUNIFORM3UIPROC) (GLuint program, GLint location, GLuint v0, GLuint v1, GLuint v2); -typedef void (APIENTRYP PFNGLPROGRAMUNIFORM3UIVPROC) (GLuint program, GLint location, GLsizei count, const GLuint *value); -typedef void (APIENTRYP PFNGLPROGRAMUNIFORM4IPROC) (GLuint program, GLint location, GLint v0, GLint v1, GLint v2, GLint v3); -typedef void (APIENTRYP PFNGLPROGRAMUNIFORM4IVPROC) (GLuint program, GLint location, GLsizei count, const GLint *value); -typedef void (APIENTRYP PFNGLPROGRAMUNIFORM4FPROC) (GLuint program, GLint location, GLfloat v0, GLfloat v1, GLfloat v2, GLfloat v3); -typedef void (APIENTRYP PFNGLPROGRAMUNIFORM4FVPROC) (GLuint program, GLint location, GLsizei count, const GLfloat *value); -typedef void (APIENTRYP PFNGLPROGRAMUNIFORM4DPROC) (GLuint program, GLint location, GLdouble v0, GLdouble v1, GLdouble v2, GLdouble v3); -typedef void (APIENTRYP PFNGLPROGRAMUNIFORM4DVPROC) (GLuint program, GLint location, GLsizei count, const GLdouble *value); -typedef void (APIENTRYP PFNGLPROGRAMUNIFORM4UIPROC) (GLuint program, GLint location, GLuint v0, GLuint v1, GLuint v2, GLuint v3); -typedef void (APIENTRYP PFNGLPROGRAMUNIFORM4UIVPROC) (GLuint program, GLint location, GLsizei count, const GLuint *value); -typedef void (APIENTRYP PFNGLPROGRAMUNIFORMMATRIX2FVPROC) (GLuint program, GLint location, GLsizei count, GLboolean transpose, const GLfloat *value); -typedef void (APIENTRYP PFNGLPROGRAMUNIFORMMATRIX3FVPROC) (GLuint program, GLint location, GLsizei count, GLboolean transpose, const GLfloat *value); -typedef void (APIENTRYP PFNGLPROGRAMUNIFORMMATRIX4FVPROC) (GLuint program, GLint location, GLsizei count, GLboolean transpose, const GLfloat *value); -typedef void (APIENTRYP PFNGLPROGRAMUNIFORMMATRIX2DVPROC) (GLuint program, GLint location, GLsizei count, GLboolean transpose, const GLdouble *value); -typedef void (APIENTRYP PFNGLPROGRAMUNIFORMMATRIX3DVPROC) (GLuint program, GLint location, GLsizei count, GLboolean transpose, const GLdouble *value); -typedef void (APIENTRYP PFNGLPROGRAMUNIFORMMATRIX4DVPROC) (GLuint program, GLint location, GLsizei count, GLboolean transpose, const GLdouble *value); -typedef void (APIENTRYP PFNGLPROGRAMUNIFORMMATRIX2X3FVPROC) (GLuint program, GLint location, GLsizei count, GLboolean transpose, const GLfloat *value); -typedef void (APIENTRYP PFNGLPROGRAMUNIFORMMATRIX3X2FVPROC) (GLuint program, GLint location, GLsizei count, GLboolean transpose, const GLfloat *value); -typedef void (APIENTRYP PFNGLPROGRAMUNIFORMMATRIX2X4FVPROC) (GLuint program, GLint location, GLsizei count, GLboolean transpose, const GLfloat *value); -typedef void (APIENTRYP PFNGLPROGRAMUNIFORMMATRIX4X2FVPROC) (GLuint program, GLint location, GLsizei count, GLboolean transpose, const GLfloat *value); -typedef void (APIENTRYP PFNGLPROGRAMUNIFORMMATRIX3X4FVPROC) (GLuint program, GLint location, GLsizei count, GLboolean transpose, const GLfloat *value); -typedef void (APIENTRYP PFNGLPROGRAMUNIFORMMATRIX4X3FVPROC) (GLuint program, GLint location, GLsizei count, GLboolean transpose, const GLfloat *value); -typedef void (APIENTRYP PFNGLPROGRAMUNIFORMMATRIX2X3DVPROC) (GLuint program, GLint location, GLsizei count, GLboolean transpose, const GLdouble *value); -typedef void (APIENTRYP PFNGLPROGRAMUNIFORMMATRIX3X2DVPROC) (GLuint program, GLint location, GLsizei count, GLboolean transpose, const GLdouble *value); -typedef void (APIENTRYP PFNGLPROGRAMUNIFORMMATRIX2X4DVPROC) (GLuint program, GLint location, GLsizei count, GLboolean transpose, const GLdouble *value); -typedef void (APIENTRYP PFNGLPROGRAMUNIFORMMATRIX4X2DVPROC) (GLuint program, GLint location, GLsizei count, GLboolean transpose, const GLdouble *value); -typedef void (APIENTRYP PFNGLPROGRAMUNIFORMMATRIX3X4DVPROC) (GLuint program, GLint location, GLsizei count, GLboolean transpose, const GLdouble *value); -typedef void (APIENTRYP PFNGLPROGRAMUNIFORMMATRIX4X3DVPROC) (GLuint program, GLint location, GLsizei count, GLboolean transpose, const GLdouble *value); -typedef void (APIENTRYP PFNGLVALIDATEPROGRAMPIPELINEPROC) (GLuint pipeline); -typedef void (APIENTRYP PFNGLGETPROGRAMPIPELINEINFOLOGPROC) (GLuint pipeline, GLsizei bufSize, GLsizei *length, GLchar *infoLog); -typedef void (APIENTRYP PFNGLVERTEXATTRIBL1DPROC) (GLuint index, GLdouble x); -typedef void (APIENTRYP PFNGLVERTEXATTRIBL2DPROC) (GLuint index, GLdouble x, GLdouble y); -typedef void (APIENTRYP PFNGLVERTEXATTRIBL3DPROC) (GLuint index, GLdouble x, GLdouble y, GLdouble z); -typedef void (APIENTRYP PFNGLVERTEXATTRIBL4DPROC) (GLuint index, GLdouble x, GLdouble y, GLdouble z, GLdouble w); -typedef void (APIENTRYP PFNGLVERTEXATTRIBL1DVPROC) (GLuint index, const GLdouble *v); -typedef void (APIENTRYP PFNGLVERTEXATTRIBL2DVPROC) (GLuint index, const GLdouble *v); -typedef void (APIENTRYP PFNGLVERTEXATTRIBL3DVPROC) (GLuint index, const GLdouble *v); -typedef void (APIENTRYP PFNGLVERTEXATTRIBL4DVPROC) (GLuint index, const GLdouble *v); -typedef void (APIENTRYP PFNGLVERTEXATTRIBLPOINTERPROC) (GLuint index, GLint size, GLenum type, GLsizei stride, const void *pointer); -typedef void (APIENTRYP PFNGLGETVERTEXATTRIBLDVPROC) (GLuint index, GLenum pname, GLdouble *params); -typedef void (APIENTRYP PFNGLVIEWPORTARRAYVPROC) (GLuint first, GLsizei count, const GLfloat *v); -typedef void (APIENTRYP PFNGLVIEWPORTINDEXEDFPROC) (GLuint index, GLfloat x, GLfloat y, GLfloat w, GLfloat h); -typedef void (APIENTRYP PFNGLVIEWPORTINDEXEDFVPROC) (GLuint index, const GLfloat *v); -typedef void (APIENTRYP PFNGLSCISSORARRAYVPROC) (GLuint first, GLsizei count, const GLint *v); -typedef void (APIENTRYP PFNGLSCISSORINDEXEDPROC) (GLuint index, GLint left, GLint bottom, GLsizei width, GLsizei height); -typedef void (APIENTRYP PFNGLSCISSORINDEXEDVPROC) (GLuint index, const GLint *v); -typedef void (APIENTRYP PFNGLDEPTHRANGEARRAYVPROC) (GLuint first, GLsizei count, const GLdouble *v); -typedef void (APIENTRYP PFNGLDEPTHRANGEINDEXEDPROC) (GLuint index, GLdouble n, GLdouble f); -typedef void (APIENTRYP PFNGLGETFLOATI_VPROC) (GLenum target, GLuint index, GLfloat *data); -typedef void (APIENTRYP PFNGLGETDOUBLEI_VPROC) (GLenum target, GLuint index, GLdouble *data); -#ifdef GL_GLEXT_PROTOTYPES -GLAPI void APIENTRY glReleaseShaderCompiler (void); -GLAPI void APIENTRY glShaderBinary (GLsizei count, const GLuint *shaders, GLenum binaryformat, const void *binary, GLsizei length); -GLAPI void APIENTRY glGetShaderPrecisionFormat (GLenum shadertype, GLenum precisiontype, GLint *range, GLint *precision); -GLAPI void APIENTRY glDepthRangef (GLfloat n, GLfloat f); -GLAPI void APIENTRY glClearDepthf (GLfloat d); -GLAPI void APIENTRY glGetProgramBinary (GLuint program, GLsizei bufSize, GLsizei *length, GLenum *binaryFormat, void *binary); -GLAPI void APIENTRY glProgramBinary (GLuint program, GLenum binaryFormat, const void *binary, GLsizei length); -GLAPI void APIENTRY glProgramParameteri (GLuint program, GLenum pname, GLint value); -GLAPI void APIENTRY glUseProgramStages (GLuint pipeline, GLbitfield stages, GLuint program); -GLAPI void APIENTRY glActiveShaderProgram (GLuint pipeline, GLuint program); -GLAPI GLuint APIENTRY glCreateShaderProgramv (GLenum type, GLsizei count, const GLchar *const*strings); -GLAPI void APIENTRY glBindProgramPipeline (GLuint pipeline); -GLAPI void APIENTRY glDeleteProgramPipelines (GLsizei n, const GLuint *pipelines); -GLAPI void APIENTRY glGenProgramPipelines (GLsizei n, GLuint *pipelines); -GLAPI GLboolean APIENTRY glIsProgramPipeline (GLuint pipeline); -GLAPI void APIENTRY glGetProgramPipelineiv (GLuint pipeline, GLenum pname, GLint *params); -GLAPI void APIENTRY glProgramUniform1i (GLuint program, GLint location, GLint v0); -GLAPI void APIENTRY glProgramUniform1iv (GLuint program, GLint location, GLsizei count, const GLint *value); -GLAPI void APIENTRY glProgramUniform1f (GLuint program, GLint location, GLfloat v0); -GLAPI void APIENTRY glProgramUniform1fv (GLuint program, GLint location, GLsizei count, const GLfloat *value); -GLAPI void APIENTRY glProgramUniform1d (GLuint program, GLint location, GLdouble v0); -GLAPI void APIENTRY glProgramUniform1dv (GLuint program, GLint location, GLsizei count, const GLdouble *value); -GLAPI void APIENTRY glProgramUniform1ui (GLuint program, GLint location, GLuint v0); -GLAPI void APIENTRY glProgramUniform1uiv (GLuint program, GLint location, GLsizei count, const GLuint *value); -GLAPI void APIENTRY glProgramUniform2i (GLuint program, GLint location, GLint v0, GLint v1); -GLAPI void APIENTRY glProgramUniform2iv (GLuint program, GLint location, GLsizei count, const GLint *value); -GLAPI void APIENTRY glProgramUniform2f (GLuint program, GLint location, GLfloat v0, GLfloat v1); -GLAPI void APIENTRY glProgramUniform2fv (GLuint program, GLint location, GLsizei count, const GLfloat *value); -GLAPI void APIENTRY glProgramUniform2d (GLuint program, GLint location, GLdouble v0, GLdouble v1); -GLAPI void APIENTRY glProgramUniform2dv (GLuint program, GLint location, GLsizei count, const GLdouble *value); -GLAPI void APIENTRY glProgramUniform2ui (GLuint program, GLint location, GLuint v0, GLuint v1); -GLAPI void APIENTRY glProgramUniform2uiv (GLuint program, GLint location, GLsizei count, const GLuint *value); -GLAPI void APIENTRY glProgramUniform3i (GLuint program, GLint location, GLint v0, GLint v1, GLint v2); -GLAPI void APIENTRY glProgramUniform3iv (GLuint program, GLint location, GLsizei count, const GLint *value); -GLAPI void APIENTRY glProgramUniform3f (GLuint program, GLint location, GLfloat v0, GLfloat v1, GLfloat v2); -GLAPI void APIENTRY glProgramUniform3fv (GLuint program, GLint location, GLsizei count, const GLfloat *value); -GLAPI void APIENTRY glProgramUniform3d (GLuint program, GLint location, GLdouble v0, GLdouble v1, GLdouble v2); -GLAPI void APIENTRY glProgramUniform3dv (GLuint program, GLint location, GLsizei count, const GLdouble *value); -GLAPI void APIENTRY glProgramUniform3ui (GLuint program, GLint location, GLuint v0, GLuint v1, GLuint v2); -GLAPI void APIENTRY glProgramUniform3uiv (GLuint program, GLint location, GLsizei count, const GLuint *value); -GLAPI void APIENTRY glProgramUniform4i (GLuint program, GLint location, GLint v0, GLint v1, GLint v2, GLint v3); -GLAPI void APIENTRY glProgramUniform4iv (GLuint program, GLint location, GLsizei count, const GLint *value); -GLAPI void APIENTRY glProgramUniform4f (GLuint program, GLint location, GLfloat v0, GLfloat v1, GLfloat v2, GLfloat v3); -GLAPI void APIENTRY glProgramUniform4fv (GLuint program, GLint location, GLsizei count, const GLfloat *value); -GLAPI void APIENTRY glProgramUniform4d (GLuint program, GLint location, GLdouble v0, GLdouble v1, GLdouble v2, GLdouble v3); -GLAPI void APIENTRY glProgramUniform4dv (GLuint program, GLint location, GLsizei count, const GLdouble *value); -GLAPI void APIENTRY glProgramUniform4ui (GLuint program, GLint location, GLuint v0, GLuint v1, GLuint v2, GLuint v3); -GLAPI void APIENTRY glProgramUniform4uiv (GLuint program, GLint location, GLsizei count, const GLuint *value); -GLAPI void APIENTRY glProgramUniformMatrix2fv (GLuint program, GLint location, GLsizei count, GLboolean transpose, const GLfloat *value); -GLAPI void APIENTRY glProgramUniformMatrix3fv (GLuint program, GLint location, GLsizei count, GLboolean transpose, const GLfloat *value); -GLAPI void APIENTRY glProgramUniformMatrix4fv (GLuint program, GLint location, GLsizei count, GLboolean transpose, const GLfloat *value); -GLAPI void APIENTRY glProgramUniformMatrix2dv (GLuint program, GLint location, GLsizei count, GLboolean transpose, const GLdouble *value); -GLAPI void APIENTRY glProgramUniformMatrix3dv (GLuint program, GLint location, GLsizei count, GLboolean transpose, const GLdouble *value); -GLAPI void APIENTRY glProgramUniformMatrix4dv (GLuint program, GLint location, GLsizei count, GLboolean transpose, const GLdouble *value); -GLAPI void APIENTRY glProgramUniformMatrix2x3fv (GLuint program, GLint location, GLsizei count, GLboolean transpose, const GLfloat *value); -GLAPI void APIENTRY glProgramUniformMatrix3x2fv (GLuint program, GLint location, GLsizei count, GLboolean transpose, const GLfloat *value); -GLAPI void APIENTRY glProgramUniformMatrix2x4fv (GLuint program, GLint location, GLsizei count, GLboolean transpose, const GLfloat *value); -GLAPI void APIENTRY glProgramUniformMatrix4x2fv (GLuint program, GLint location, GLsizei count, GLboolean transpose, const GLfloat *value); -GLAPI void APIENTRY glProgramUniformMatrix3x4fv (GLuint program, GLint location, GLsizei count, GLboolean transpose, const GLfloat *value); -GLAPI void APIENTRY glProgramUniformMatrix4x3fv (GLuint program, GLint location, GLsizei count, GLboolean transpose, const GLfloat *value); -GLAPI void APIENTRY glProgramUniformMatrix2x3dv (GLuint program, GLint location, GLsizei count, GLboolean transpose, const GLdouble *value); -GLAPI void APIENTRY glProgramUniformMatrix3x2dv (GLuint program, GLint location, GLsizei count, GLboolean transpose, const GLdouble *value); -GLAPI void APIENTRY glProgramUniformMatrix2x4dv (GLuint program, GLint location, GLsizei count, GLboolean transpose, const GLdouble *value); -GLAPI void APIENTRY glProgramUniformMatrix4x2dv (GLuint program, GLint location, GLsizei count, GLboolean transpose, const GLdouble *value); -GLAPI void APIENTRY glProgramUniformMatrix3x4dv (GLuint program, GLint location, GLsizei count, GLboolean transpose, const GLdouble *value); -GLAPI void APIENTRY glProgramUniformMatrix4x3dv (GLuint program, GLint location, GLsizei count, GLboolean transpose, const GLdouble *value); -GLAPI void APIENTRY glValidateProgramPipeline (GLuint pipeline); -GLAPI void APIENTRY glGetProgramPipelineInfoLog (GLuint pipeline, GLsizei bufSize, GLsizei *length, GLchar *infoLog); -GLAPI void APIENTRY glVertexAttribL1d (GLuint index, GLdouble x); -GLAPI void APIENTRY glVertexAttribL2d (GLuint index, GLdouble x, GLdouble y); -GLAPI void APIENTRY glVertexAttribL3d (GLuint index, GLdouble x, GLdouble y, GLdouble z); -GLAPI void APIENTRY glVertexAttribL4d (GLuint index, GLdouble x, GLdouble y, GLdouble z, GLdouble w); -GLAPI void APIENTRY glVertexAttribL1dv (GLuint index, const GLdouble *v); -GLAPI void APIENTRY glVertexAttribL2dv (GLuint index, const GLdouble *v); -GLAPI void APIENTRY glVertexAttribL3dv (GLuint index, const GLdouble *v); -GLAPI void APIENTRY glVertexAttribL4dv (GLuint index, const GLdouble *v); -GLAPI void APIENTRY glVertexAttribLPointer (GLuint index, GLint size, GLenum type, GLsizei stride, const void *pointer); -GLAPI void APIENTRY glGetVertexAttribLdv (GLuint index, GLenum pname, GLdouble *params); -GLAPI void APIENTRY glViewportArrayv (GLuint first, GLsizei count, const GLfloat *v); -GLAPI void APIENTRY glViewportIndexedf (GLuint index, GLfloat x, GLfloat y, GLfloat w, GLfloat h); -GLAPI void APIENTRY glViewportIndexedfv (GLuint index, const GLfloat *v); -GLAPI void APIENTRY glScissorArrayv (GLuint first, GLsizei count, const GLint *v); -GLAPI void APIENTRY glScissorIndexed (GLuint index, GLint left, GLint bottom, GLsizei width, GLsizei height); -GLAPI void APIENTRY glScissorIndexedv (GLuint index, const GLint *v); -GLAPI void APIENTRY glDepthRangeArrayv (GLuint first, GLsizei count, const GLdouble *v); -GLAPI void APIENTRY glDepthRangeIndexed (GLuint index, GLdouble n, GLdouble f); -GLAPI void APIENTRY glGetFloati_v (GLenum target, GLuint index, GLfloat *data); -GLAPI void APIENTRY glGetDoublei_v (GLenum target, GLuint index, GLdouble *data); -#endif -#endif /* GL_VERSION_4_1 */ - -#ifndef GL_VERSION_4_2 -#define GL_VERSION_4_2 1 -#define GL_UNPACK_COMPRESSED_BLOCK_WIDTH 0x9127 -#define GL_UNPACK_COMPRESSED_BLOCK_HEIGHT 0x9128 -#define GL_UNPACK_COMPRESSED_BLOCK_DEPTH 0x9129 -#define GL_UNPACK_COMPRESSED_BLOCK_SIZE 0x912A -#define GL_PACK_COMPRESSED_BLOCK_WIDTH 0x912B -#define GL_PACK_COMPRESSED_BLOCK_HEIGHT 0x912C -#define GL_PACK_COMPRESSED_BLOCK_DEPTH 0x912D -#define GL_PACK_COMPRESSED_BLOCK_SIZE 0x912E -#define GL_NUM_SAMPLE_COUNTS 0x9380 -#define GL_MIN_MAP_BUFFER_ALIGNMENT 0x90BC -#define GL_ATOMIC_COUNTER_BUFFER 0x92C0 -#define GL_ATOMIC_COUNTER_BUFFER_BINDING 0x92C1 -#define GL_ATOMIC_COUNTER_BUFFER_START 0x92C2 -#define GL_ATOMIC_COUNTER_BUFFER_SIZE 0x92C3 -#define GL_ATOMIC_COUNTER_BUFFER_DATA_SIZE 0x92C4 -#define GL_ATOMIC_COUNTER_BUFFER_ACTIVE_ATOMIC_COUNTERS 0x92C5 -#define GL_ATOMIC_COUNTER_BUFFER_ACTIVE_ATOMIC_COUNTER_INDICES 0x92C6 -#define GL_ATOMIC_COUNTER_BUFFER_REFERENCED_BY_VERTEX_SHADER 0x92C7 -#define GL_ATOMIC_COUNTER_BUFFER_REFERENCED_BY_TESS_CONTROL_SHADER 0x92C8 -#define GL_ATOMIC_COUNTER_BUFFER_REFERENCED_BY_TESS_EVALUATION_SHADER 0x92C9 -#define GL_ATOMIC_COUNTER_BUFFER_REFERENCED_BY_GEOMETRY_SHADER 0x92CA -#define GL_ATOMIC_COUNTER_BUFFER_REFERENCED_BY_FRAGMENT_SHADER 0x92CB -#define GL_MAX_VERTEX_ATOMIC_COUNTER_BUFFERS 0x92CC -#define GL_MAX_TESS_CONTROL_ATOMIC_COUNTER_BUFFERS 0x92CD -#define GL_MAX_TESS_EVALUATION_ATOMIC_COUNTER_BUFFERS 0x92CE -#define GL_MAX_GEOMETRY_ATOMIC_COUNTER_BUFFERS 0x92CF -#define GL_MAX_FRAGMENT_ATOMIC_COUNTER_BUFFERS 0x92D0 -#define GL_MAX_COMBINED_ATOMIC_COUNTER_BUFFERS 0x92D1 -#define GL_MAX_VERTEX_ATOMIC_COUNTERS 0x92D2 -#define GL_MAX_TESS_CONTROL_ATOMIC_COUNTERS 0x92D3 -#define GL_MAX_TESS_EVALUATION_ATOMIC_COUNTERS 0x92D4 -#define GL_MAX_GEOMETRY_ATOMIC_COUNTERS 0x92D5 -#define GL_MAX_FRAGMENT_ATOMIC_COUNTERS 0x92D6 -#define GL_MAX_COMBINED_ATOMIC_COUNTERS 0x92D7 -#define GL_MAX_ATOMIC_COUNTER_BUFFER_SIZE 0x92D8 -#define GL_MAX_ATOMIC_COUNTER_BUFFER_BINDINGS 0x92DC -#define GL_ACTIVE_ATOMIC_COUNTER_BUFFERS 0x92D9 -#define GL_UNIFORM_ATOMIC_COUNTER_BUFFER_INDEX 0x92DA -#define GL_UNSIGNED_INT_ATOMIC_COUNTER 0x92DB -#define GL_VERTEX_ATTRIB_ARRAY_BARRIER_BIT 0x00000001 -#define GL_ELEMENT_ARRAY_BARRIER_BIT 0x00000002 -#define GL_UNIFORM_BARRIER_BIT 0x00000004 -#define GL_TEXTURE_FETCH_BARRIER_BIT 0x00000008 -#define GL_SHADER_IMAGE_ACCESS_BARRIER_BIT 0x00000020 -#define GL_COMMAND_BARRIER_BIT 0x00000040 -#define GL_PIXEL_BUFFER_BARRIER_BIT 0x00000080 -#define GL_TEXTURE_UPDATE_BARRIER_BIT 0x00000100 -#define GL_BUFFER_UPDATE_BARRIER_BIT 0x00000200 -#define GL_FRAMEBUFFER_BARRIER_BIT 0x00000400 -#define GL_TRANSFORM_FEEDBACK_BARRIER_BIT 0x00000800 -#define GL_ATOMIC_COUNTER_BARRIER_BIT 0x00001000 -#define GL_ALL_BARRIER_BITS 0xFFFFFFFF -#define GL_MAX_IMAGE_UNITS 0x8F38 -#define GL_MAX_COMBINED_IMAGE_UNITS_AND_FRAGMENT_OUTPUTS 0x8F39 -#define GL_IMAGE_BINDING_NAME 0x8F3A -#define GL_IMAGE_BINDING_LEVEL 0x8F3B -#define GL_IMAGE_BINDING_LAYERED 0x8F3C -#define GL_IMAGE_BINDING_LAYER 0x8F3D -#define GL_IMAGE_BINDING_ACCESS 0x8F3E -#define GL_IMAGE_1D 0x904C -#define GL_IMAGE_2D 0x904D -#define GL_IMAGE_3D 0x904E -#define GL_IMAGE_2D_RECT 0x904F -#define GL_IMAGE_CUBE 0x9050 -#define GL_IMAGE_BUFFER 0x9051 -#define GL_IMAGE_1D_ARRAY 0x9052 -#define GL_IMAGE_2D_ARRAY 0x9053 -#define GL_IMAGE_CUBE_MAP_ARRAY 0x9054 -#define GL_IMAGE_2D_MULTISAMPLE 0x9055 -#define GL_IMAGE_2D_MULTISAMPLE_ARRAY 0x9056 -#define GL_INT_IMAGE_1D 0x9057 -#define GL_INT_IMAGE_2D 0x9058 -#define GL_INT_IMAGE_3D 0x9059 -#define GL_INT_IMAGE_2D_RECT 0x905A -#define GL_INT_IMAGE_CUBE 0x905B -#define GL_INT_IMAGE_BUFFER 0x905C -#define GL_INT_IMAGE_1D_ARRAY 0x905D -#define GL_INT_IMAGE_2D_ARRAY 0x905E -#define GL_INT_IMAGE_CUBE_MAP_ARRAY 0x905F -#define GL_INT_IMAGE_2D_MULTISAMPLE 0x9060 -#define GL_INT_IMAGE_2D_MULTISAMPLE_ARRAY 0x9061 -#define GL_UNSIGNED_INT_IMAGE_1D 0x9062 -#define GL_UNSIGNED_INT_IMAGE_2D 0x9063 -#define GL_UNSIGNED_INT_IMAGE_3D 0x9064 -#define GL_UNSIGNED_INT_IMAGE_2D_RECT 0x9065 -#define GL_UNSIGNED_INT_IMAGE_CUBE 0x9066 -#define GL_UNSIGNED_INT_IMAGE_BUFFER 0x9067 -#define GL_UNSIGNED_INT_IMAGE_1D_ARRAY 0x9068 -#define GL_UNSIGNED_INT_IMAGE_2D_ARRAY 0x9069 -#define GL_UNSIGNED_INT_IMAGE_CUBE_MAP_ARRAY 0x906A -#define GL_UNSIGNED_INT_IMAGE_2D_MULTISAMPLE 0x906B -#define GL_UNSIGNED_INT_IMAGE_2D_MULTISAMPLE_ARRAY 0x906C -#define GL_MAX_IMAGE_SAMPLES 0x906D -#define GL_IMAGE_BINDING_FORMAT 0x906E -#define GL_IMAGE_FORMAT_COMPATIBILITY_TYPE 0x90C7 -#define GL_IMAGE_FORMAT_COMPATIBILITY_BY_SIZE 0x90C8 -#define GL_IMAGE_FORMAT_COMPATIBILITY_BY_CLASS 0x90C9 -#define GL_MAX_VERTEX_IMAGE_UNIFORMS 0x90CA -#define GL_MAX_TESS_CONTROL_IMAGE_UNIFORMS 0x90CB -#define GL_MAX_TESS_EVALUATION_IMAGE_UNIFORMS 0x90CC -#define GL_MAX_GEOMETRY_IMAGE_UNIFORMS 0x90CD -#define GL_MAX_FRAGMENT_IMAGE_UNIFORMS 0x90CE -#define GL_MAX_COMBINED_IMAGE_UNIFORMS 0x90CF -#define GL_COMPRESSED_RGBA_BPTC_UNORM 0x8E8C -#define GL_COMPRESSED_SRGB_ALPHA_BPTC_UNORM 0x8E8D -#define GL_COMPRESSED_RGB_BPTC_SIGNED_FLOAT 0x8E8E -#define GL_COMPRESSED_RGB_BPTC_UNSIGNED_FLOAT 0x8E8F -#define GL_TEXTURE_IMMUTABLE_FORMAT 0x912F -typedef void (APIENTRYP PFNGLDRAWARRAYSINSTANCEDBASEINSTANCEPROC) (GLenum mode, GLint first, GLsizei count, GLsizei instancecount, GLuint baseinstance); -typedef void (APIENTRYP PFNGLDRAWELEMENTSINSTANCEDBASEINSTANCEPROC) (GLenum mode, GLsizei count, GLenum type, const void *indices, GLsizei instancecount, GLuint baseinstance); -typedef void (APIENTRYP PFNGLDRAWELEMENTSINSTANCEDBASEVERTEXBASEINSTANCEPROC) (GLenum mode, GLsizei count, GLenum type, const void *indices, GLsizei instancecount, GLint basevertex, GLuint baseinstance); -typedef void (APIENTRYP PFNGLGETINTERNALFORMATIVPROC) (GLenum target, GLenum internalformat, GLenum pname, GLsizei bufSize, GLint *params); -typedef void (APIENTRYP PFNGLGETACTIVEATOMICCOUNTERBUFFERIVPROC) (GLuint program, GLuint bufferIndex, GLenum pname, GLint *params); -typedef void (APIENTRYP PFNGLBINDIMAGETEXTUREPROC) (GLuint unit, GLuint texture, GLint level, GLboolean layered, GLint layer, GLenum access, GLenum format); -typedef void (APIENTRYP PFNGLMEMORYBARRIERPROC) (GLbitfield barriers); -typedef void (APIENTRYP PFNGLTEXSTORAGE1DPROC) (GLenum target, GLsizei levels, GLenum internalformat, GLsizei width); -typedef void (APIENTRYP PFNGLTEXSTORAGE2DPROC) (GLenum target, GLsizei levels, GLenum internalformat, GLsizei width, GLsizei height); -typedef void (APIENTRYP PFNGLTEXSTORAGE3DPROC) (GLenum target, GLsizei levels, GLenum internalformat, GLsizei width, GLsizei height, GLsizei depth); -typedef void (APIENTRYP PFNGLDRAWTRANSFORMFEEDBACKINSTANCEDPROC) (GLenum mode, GLuint id, GLsizei instancecount); -typedef void (APIENTRYP PFNGLDRAWTRANSFORMFEEDBACKSTREAMINSTANCEDPROC) (GLenum mode, GLuint id, GLuint stream, GLsizei instancecount); -#ifdef GL_GLEXT_PROTOTYPES -GLAPI void APIENTRY glDrawArraysInstancedBaseInstance (GLenum mode, GLint first, GLsizei count, GLsizei instancecount, GLuint baseinstance); -GLAPI void APIENTRY glDrawElementsInstancedBaseInstance (GLenum mode, GLsizei count, GLenum type, const void *indices, GLsizei instancecount, GLuint baseinstance); -GLAPI void APIENTRY glDrawElementsInstancedBaseVertexBaseInstance (GLenum mode, GLsizei count, GLenum type, const void *indices, GLsizei instancecount, GLint basevertex, GLuint baseinstance); -GLAPI void APIENTRY glGetInternalformativ (GLenum target, GLenum internalformat, GLenum pname, GLsizei bufSize, GLint *params); -GLAPI void APIENTRY glGetActiveAtomicCounterBufferiv (GLuint program, GLuint bufferIndex, GLenum pname, GLint *params); -GLAPI void APIENTRY glBindImageTexture (GLuint unit, GLuint texture, GLint level, GLboolean layered, GLint layer, GLenum access, GLenum format); -GLAPI void APIENTRY glMemoryBarrier (GLbitfield barriers); -GLAPI void APIENTRY glTexStorage1D (GLenum target, GLsizei levels, GLenum internalformat, GLsizei width); -GLAPI void APIENTRY glTexStorage2D (GLenum target, GLsizei levels, GLenum internalformat, GLsizei width, GLsizei height); -GLAPI void APIENTRY glTexStorage3D (GLenum target, GLsizei levels, GLenum internalformat, GLsizei width, GLsizei height, GLsizei depth); -GLAPI void APIENTRY glDrawTransformFeedbackInstanced (GLenum mode, GLuint id, GLsizei instancecount); -GLAPI void APIENTRY glDrawTransformFeedbackStreamInstanced (GLenum mode, GLuint id, GLuint stream, GLsizei instancecount); -#endif -#endif /* GL_VERSION_4_2 */ - -#ifndef GL_VERSION_4_3 -#define GL_VERSION_4_3 1 -typedef void (APIENTRY *GLDEBUGPROC)(GLenum source,GLenum type,GLuint id,GLenum severity,GLsizei length,const GLchar *message,const void *userParam); -#define GL_NUM_SHADING_LANGUAGE_VERSIONS 0x82E9 -#define GL_VERTEX_ATTRIB_ARRAY_LONG 0x874E -#define GL_COMPRESSED_RGB8_ETC2 0x9274 -#define GL_COMPRESSED_SRGB8_ETC2 0x9275 -#define GL_COMPRESSED_RGB8_PUNCHTHROUGH_ALPHA1_ETC2 0x9276 -#define GL_COMPRESSED_SRGB8_PUNCHTHROUGH_ALPHA1_ETC2 0x9277 -#define GL_COMPRESSED_RGBA8_ETC2_EAC 0x9278 -#define GL_COMPRESSED_SRGB8_ALPHA8_ETC2_EAC 0x9279 -#define GL_COMPRESSED_R11_EAC 0x9270 -#define GL_COMPRESSED_SIGNED_R11_EAC 0x9271 -#define GL_COMPRESSED_RG11_EAC 0x9272 -#define GL_COMPRESSED_SIGNED_RG11_EAC 0x9273 -#define GL_PRIMITIVE_RESTART_FIXED_INDEX 0x8D69 -#define GL_ANY_SAMPLES_PASSED_CONSERVATIVE 0x8D6A -#define GL_MAX_ELEMENT_INDEX 0x8D6B -#define GL_COMPUTE_SHADER 0x91B9 -#define GL_MAX_COMPUTE_UNIFORM_BLOCKS 0x91BB -#define GL_MAX_COMPUTE_TEXTURE_IMAGE_UNITS 0x91BC -#define GL_MAX_COMPUTE_IMAGE_UNIFORMS 0x91BD -#define GL_MAX_COMPUTE_SHARED_MEMORY_SIZE 0x8262 -#define GL_MAX_COMPUTE_UNIFORM_COMPONENTS 0x8263 -#define GL_MAX_COMPUTE_ATOMIC_COUNTER_BUFFERS 0x8264 -#define GL_MAX_COMPUTE_ATOMIC_COUNTERS 0x8265 -#define GL_MAX_COMBINED_COMPUTE_UNIFORM_COMPONENTS 0x8266 -#define GL_MAX_COMPUTE_WORK_GROUP_INVOCATIONS 0x90EB -#define GL_MAX_COMPUTE_WORK_GROUP_COUNT 0x91BE -#define GL_MAX_COMPUTE_WORK_GROUP_SIZE 0x91BF -#define GL_COMPUTE_WORK_GROUP_SIZE 0x8267 -#define GL_UNIFORM_BLOCK_REFERENCED_BY_COMPUTE_SHADER 0x90EC -#define GL_ATOMIC_COUNTER_BUFFER_REFERENCED_BY_COMPUTE_SHADER 0x90ED -#define GL_DISPATCH_INDIRECT_BUFFER 0x90EE -#define GL_DISPATCH_INDIRECT_BUFFER_BINDING 0x90EF -#define GL_DEBUG_OUTPUT_SYNCHRONOUS 0x8242 -#define GL_DEBUG_NEXT_LOGGED_MESSAGE_LENGTH 0x8243 -#define GL_DEBUG_CALLBACK_FUNCTION 0x8244 -#define GL_DEBUG_CALLBACK_USER_PARAM 0x8245 -#define GL_DEBUG_SOURCE_API 0x8246 -#define GL_DEBUG_SOURCE_WINDOW_SYSTEM 0x8247 -#define GL_DEBUG_SOURCE_SHADER_COMPILER 0x8248 -#define GL_DEBUG_SOURCE_THIRD_PARTY 0x8249 -#define GL_DEBUG_SOURCE_APPLICATION 0x824A -#define GL_DEBUG_SOURCE_OTHER 0x824B -#define GL_DEBUG_TYPE_ERROR 0x824C -#define GL_DEBUG_TYPE_DEPRECATED_BEHAVIOR 0x824D -#define GL_DEBUG_TYPE_UNDEFINED_BEHAVIOR 0x824E -#define GL_DEBUG_TYPE_PORTABILITY 0x824F -#define GL_DEBUG_TYPE_PERFORMANCE 0x8250 -#define GL_DEBUG_TYPE_OTHER 0x8251 -#define GL_MAX_DEBUG_MESSAGE_LENGTH 0x9143 -#define GL_MAX_DEBUG_LOGGED_MESSAGES 0x9144 -#define GL_DEBUG_LOGGED_MESSAGES 0x9145 -#define GL_DEBUG_SEVERITY_HIGH 0x9146 -#define GL_DEBUG_SEVERITY_MEDIUM 0x9147 -#define GL_DEBUG_SEVERITY_LOW 0x9148 -#define GL_DEBUG_TYPE_MARKER 0x8268 -#define GL_DEBUG_TYPE_PUSH_GROUP 0x8269 -#define GL_DEBUG_TYPE_POP_GROUP 0x826A -#define GL_DEBUG_SEVERITY_NOTIFICATION 0x826B -#define GL_MAX_DEBUG_GROUP_STACK_DEPTH 0x826C -#define GL_DEBUG_GROUP_STACK_DEPTH 0x826D -#define GL_BUFFER 0x82E0 -#define GL_SHADER 0x82E1 -#define GL_PROGRAM 0x82E2 -#define GL_QUERY 0x82E3 -#define GL_PROGRAM_PIPELINE 0x82E4 -#define GL_SAMPLER 0x82E6 -#define GL_MAX_LABEL_LENGTH 0x82E8 -#define GL_DEBUG_OUTPUT 0x92E0 -#define GL_CONTEXT_FLAG_DEBUG_BIT 0x00000002 -#define GL_MAX_UNIFORM_LOCATIONS 0x826E -#define GL_FRAMEBUFFER_DEFAULT_WIDTH 0x9310 -#define GL_FRAMEBUFFER_DEFAULT_HEIGHT 0x9311 -#define GL_FRAMEBUFFER_DEFAULT_LAYERS 0x9312 -#define GL_FRAMEBUFFER_DEFAULT_SAMPLES 0x9313 -#define GL_FRAMEBUFFER_DEFAULT_FIXED_SAMPLE_LOCATIONS 0x9314 -#define GL_MAX_FRAMEBUFFER_WIDTH 0x9315 -#define GL_MAX_FRAMEBUFFER_HEIGHT 0x9316 -#define GL_MAX_FRAMEBUFFER_LAYERS 0x9317 -#define GL_MAX_FRAMEBUFFER_SAMPLES 0x9318 -#define GL_INTERNALFORMAT_SUPPORTED 0x826F -#define GL_INTERNALFORMAT_PREFERRED 0x8270 -#define GL_INTERNALFORMAT_RED_SIZE 0x8271 -#define GL_INTERNALFORMAT_GREEN_SIZE 0x8272 -#define GL_INTERNALFORMAT_BLUE_SIZE 0x8273 -#define GL_INTERNALFORMAT_ALPHA_SIZE 0x8274 -#define GL_INTERNALFORMAT_DEPTH_SIZE 0x8275 -#define GL_INTERNALFORMAT_STENCIL_SIZE 0x8276 -#define GL_INTERNALFORMAT_SHARED_SIZE 0x8277 -#define GL_INTERNALFORMAT_RED_TYPE 0x8278 -#define GL_INTERNALFORMAT_GREEN_TYPE 0x8279 -#define GL_INTERNALFORMAT_BLUE_TYPE 0x827A -#define GL_INTERNALFORMAT_ALPHA_TYPE 0x827B -#define GL_INTERNALFORMAT_DEPTH_TYPE 0x827C -#define GL_INTERNALFORMAT_STENCIL_TYPE 0x827D -#define GL_MAX_WIDTH 0x827E -#define GL_MAX_HEIGHT 0x827F -#define GL_MAX_DEPTH 0x8280 -#define GL_MAX_LAYERS 0x8281 -#define GL_MAX_COMBINED_DIMENSIONS 0x8282 -#define GL_COLOR_COMPONENTS 0x8283 -#define GL_DEPTH_COMPONENTS 0x8284 -#define GL_STENCIL_COMPONENTS 0x8285 -#define GL_COLOR_RENDERABLE 0x8286 -#define GL_DEPTH_RENDERABLE 0x8287 -#define GL_STENCIL_RENDERABLE 0x8288 -#define GL_FRAMEBUFFER_RENDERABLE 0x8289 -#define GL_FRAMEBUFFER_RENDERABLE_LAYERED 0x828A -#define GL_FRAMEBUFFER_BLEND 0x828B -#define GL_READ_PIXELS 0x828C -#define GL_READ_PIXELS_FORMAT 0x828D -#define GL_READ_PIXELS_TYPE 0x828E -#define GL_TEXTURE_IMAGE_FORMAT 0x828F -#define GL_TEXTURE_IMAGE_TYPE 0x8290 -#define GL_GET_TEXTURE_IMAGE_FORMAT 0x8291 -#define GL_GET_TEXTURE_IMAGE_TYPE 0x8292 -#define GL_MIPMAP 0x8293 -#define GL_MANUAL_GENERATE_MIPMAP 0x8294 -#define GL_AUTO_GENERATE_MIPMAP 0x8295 -#define GL_COLOR_ENCODING 0x8296 -#define GL_SRGB_READ 0x8297 -#define GL_SRGB_WRITE 0x8298 -#define GL_FILTER 0x829A -#define GL_VERTEX_TEXTURE 0x829B -#define GL_TESS_CONTROL_TEXTURE 0x829C -#define GL_TESS_EVALUATION_TEXTURE 0x829D -#define GL_GEOMETRY_TEXTURE 0x829E -#define GL_FRAGMENT_TEXTURE 0x829F -#define GL_COMPUTE_TEXTURE 0x82A0 -#define GL_TEXTURE_SHADOW 0x82A1 -#define GL_TEXTURE_GATHER 0x82A2 -#define GL_TEXTURE_GATHER_SHADOW 0x82A3 -#define GL_SHADER_IMAGE_LOAD 0x82A4 -#define GL_SHADER_IMAGE_STORE 0x82A5 -#define GL_SHADER_IMAGE_ATOMIC 0x82A6 -#define GL_IMAGE_TEXEL_SIZE 0x82A7 -#define GL_IMAGE_COMPATIBILITY_CLASS 0x82A8 -#define GL_IMAGE_PIXEL_FORMAT 0x82A9 -#define GL_IMAGE_PIXEL_TYPE 0x82AA -#define GL_SIMULTANEOUS_TEXTURE_AND_DEPTH_TEST 0x82AC -#define GL_SIMULTANEOUS_TEXTURE_AND_STENCIL_TEST 0x82AD -#define GL_SIMULTANEOUS_TEXTURE_AND_DEPTH_WRITE 0x82AE -#define GL_SIMULTANEOUS_TEXTURE_AND_STENCIL_WRITE 0x82AF -#define GL_TEXTURE_COMPRESSED_BLOCK_WIDTH 0x82B1 -#define GL_TEXTURE_COMPRESSED_BLOCK_HEIGHT 0x82B2 -#define GL_TEXTURE_COMPRESSED_BLOCK_SIZE 0x82B3 -#define GL_CLEAR_BUFFER 0x82B4 -#define GL_TEXTURE_VIEW 0x82B5 -#define GL_VIEW_COMPATIBILITY_CLASS 0x82B6 -#define GL_FULL_SUPPORT 0x82B7 -#define GL_CAVEAT_SUPPORT 0x82B8 -#define GL_IMAGE_CLASS_4_X_32 0x82B9 -#define GL_IMAGE_CLASS_2_X_32 0x82BA -#define GL_IMAGE_CLASS_1_X_32 0x82BB -#define GL_IMAGE_CLASS_4_X_16 0x82BC -#define GL_IMAGE_CLASS_2_X_16 0x82BD -#define GL_IMAGE_CLASS_1_X_16 0x82BE -#define GL_IMAGE_CLASS_4_X_8 0x82BF -#define GL_IMAGE_CLASS_2_X_8 0x82C0 -#define GL_IMAGE_CLASS_1_X_8 0x82C1 -#define GL_IMAGE_CLASS_11_11_10 0x82C2 -#define GL_IMAGE_CLASS_10_10_10_2 0x82C3 -#define GL_VIEW_CLASS_128_BITS 0x82C4 -#define GL_VIEW_CLASS_96_BITS 0x82C5 -#define GL_VIEW_CLASS_64_BITS 0x82C6 -#define GL_VIEW_CLASS_48_BITS 0x82C7 -#define GL_VIEW_CLASS_32_BITS 0x82C8 -#define GL_VIEW_CLASS_24_BITS 0x82C9 -#define GL_VIEW_CLASS_16_BITS 0x82CA -#define GL_VIEW_CLASS_8_BITS 0x82CB -#define GL_VIEW_CLASS_S3TC_DXT1_RGB 0x82CC -#define GL_VIEW_CLASS_S3TC_DXT1_RGBA 0x82CD -#define GL_VIEW_CLASS_S3TC_DXT3_RGBA 0x82CE -#define GL_VIEW_CLASS_S3TC_DXT5_RGBA 0x82CF -#define GL_VIEW_CLASS_RGTC1_RED 0x82D0 -#define GL_VIEW_CLASS_RGTC2_RG 0x82D1 -#define GL_VIEW_CLASS_BPTC_UNORM 0x82D2 -#define GL_VIEW_CLASS_BPTC_FLOAT 0x82D3 -#define GL_UNIFORM 0x92E1 -#define GL_UNIFORM_BLOCK 0x92E2 -#define GL_PROGRAM_INPUT 0x92E3 -#define GL_PROGRAM_OUTPUT 0x92E4 -#define GL_BUFFER_VARIABLE 0x92E5 -#define GL_SHADER_STORAGE_BLOCK 0x92E6 -#define GL_VERTEX_SUBROUTINE 0x92E8 -#define GL_TESS_CONTROL_SUBROUTINE 0x92E9 -#define GL_TESS_EVALUATION_SUBROUTINE 0x92EA -#define GL_GEOMETRY_SUBROUTINE 0x92EB -#define GL_FRAGMENT_SUBROUTINE 0x92EC -#define GL_COMPUTE_SUBROUTINE 0x92ED -#define GL_VERTEX_SUBROUTINE_UNIFORM 0x92EE -#define GL_TESS_CONTROL_SUBROUTINE_UNIFORM 0x92EF -#define GL_TESS_EVALUATION_SUBROUTINE_UNIFORM 0x92F0 -#define GL_GEOMETRY_SUBROUTINE_UNIFORM 0x92F1 -#define GL_FRAGMENT_SUBROUTINE_UNIFORM 0x92F2 -#define GL_COMPUTE_SUBROUTINE_UNIFORM 0x92F3 -#define GL_TRANSFORM_FEEDBACK_VARYING 0x92F4 -#define GL_ACTIVE_RESOURCES 0x92F5 -#define GL_MAX_NAME_LENGTH 0x92F6 -#define GL_MAX_NUM_ACTIVE_VARIABLES 0x92F7 -#define GL_MAX_NUM_COMPATIBLE_SUBROUTINES 0x92F8 -#define GL_NAME_LENGTH 0x92F9 -#define GL_TYPE 0x92FA -#define GL_ARRAY_SIZE 0x92FB -#define GL_OFFSET 0x92FC -#define GL_BLOCK_INDEX 0x92FD -#define GL_ARRAY_STRIDE 0x92FE -#define GL_MATRIX_STRIDE 0x92FF -#define GL_IS_ROW_MAJOR 0x9300 -#define GL_ATOMIC_COUNTER_BUFFER_INDEX 0x9301 -#define GL_BUFFER_BINDING 0x9302 -#define GL_BUFFER_DATA_SIZE 0x9303 -#define GL_NUM_ACTIVE_VARIABLES 0x9304 -#define GL_ACTIVE_VARIABLES 0x9305 -#define GL_REFERENCED_BY_VERTEX_SHADER 0x9306 -#define GL_REFERENCED_BY_TESS_CONTROL_SHADER 0x9307 -#define GL_REFERENCED_BY_TESS_EVALUATION_SHADER 0x9308 -#define GL_REFERENCED_BY_GEOMETRY_SHADER 0x9309 -#define GL_REFERENCED_BY_FRAGMENT_SHADER 0x930A -#define GL_REFERENCED_BY_COMPUTE_SHADER 0x930B -#define GL_TOP_LEVEL_ARRAY_SIZE 0x930C -#define GL_TOP_LEVEL_ARRAY_STRIDE 0x930D -#define GL_LOCATION 0x930E -#define GL_LOCATION_INDEX 0x930F -#define GL_IS_PER_PATCH 0x92E7 -#define GL_SHADER_STORAGE_BUFFER 0x90D2 -#define GL_SHADER_STORAGE_BUFFER_BINDING 0x90D3 -#define GL_SHADER_STORAGE_BUFFER_START 0x90D4 -#define GL_SHADER_STORAGE_BUFFER_SIZE 0x90D5 -#define GL_MAX_VERTEX_SHADER_STORAGE_BLOCKS 0x90D6 -#define GL_MAX_GEOMETRY_SHADER_STORAGE_BLOCKS 0x90D7 -#define GL_MAX_TESS_CONTROL_SHADER_STORAGE_BLOCKS 0x90D8 -#define GL_MAX_TESS_EVALUATION_SHADER_STORAGE_BLOCKS 0x90D9 -#define GL_MAX_FRAGMENT_SHADER_STORAGE_BLOCKS 0x90DA -#define GL_MAX_COMPUTE_SHADER_STORAGE_BLOCKS 0x90DB -#define GL_MAX_COMBINED_SHADER_STORAGE_BLOCKS 0x90DC -#define GL_MAX_SHADER_STORAGE_BUFFER_BINDINGS 0x90DD -#define GL_MAX_SHADER_STORAGE_BLOCK_SIZE 0x90DE -#define GL_SHADER_STORAGE_BUFFER_OFFSET_ALIGNMENT 0x90DF -#define GL_SHADER_STORAGE_BARRIER_BIT 0x00002000 -#define GL_MAX_COMBINED_SHADER_OUTPUT_RESOURCES 0x8F39 -#define GL_DEPTH_STENCIL_TEXTURE_MODE 0x90EA -#define GL_TEXTURE_BUFFER_OFFSET 0x919D -#define GL_TEXTURE_BUFFER_SIZE 0x919E -#define GL_TEXTURE_BUFFER_OFFSET_ALIGNMENT 0x919F -#define GL_TEXTURE_VIEW_MIN_LEVEL 0x82DB -#define GL_TEXTURE_VIEW_NUM_LEVELS 0x82DC -#define GL_TEXTURE_VIEW_MIN_LAYER 0x82DD -#define GL_TEXTURE_VIEW_NUM_LAYERS 0x82DE -#define GL_TEXTURE_IMMUTABLE_LEVELS 0x82DF -#define GL_VERTEX_ATTRIB_BINDING 0x82D4 -#define GL_VERTEX_ATTRIB_RELATIVE_OFFSET 0x82D5 -#define GL_VERTEX_BINDING_DIVISOR 0x82D6 -#define GL_VERTEX_BINDING_OFFSET 0x82D7 -#define GL_VERTEX_BINDING_STRIDE 0x82D8 -#define GL_MAX_VERTEX_ATTRIB_RELATIVE_OFFSET 0x82D9 -#define GL_MAX_VERTEX_ATTRIB_BINDINGS 0x82DA -#define GL_DISPLAY_LIST 0x82E7 -typedef void (APIENTRYP PFNGLCLEARBUFFERDATAPROC) (GLenum target, GLenum internalformat, GLenum format, GLenum type, const void *data); -typedef void (APIENTRYP PFNGLCLEARBUFFERSUBDATAPROC) (GLenum target, GLenum internalformat, GLintptr offset, GLsizeiptr size, GLenum format, GLenum type, const void *data); -typedef void (APIENTRYP PFNGLDISPATCHCOMPUTEPROC) (GLuint num_groups_x, GLuint num_groups_y, GLuint num_groups_z); -typedef void (APIENTRYP PFNGLDISPATCHCOMPUTEINDIRECTPROC) (GLintptr indirect); -typedef void (APIENTRYP PFNGLCOPYIMAGESUBDATAPROC) (GLuint srcName, GLenum srcTarget, GLint srcLevel, GLint srcX, GLint srcY, GLint srcZ, GLuint dstName, GLenum dstTarget, GLint dstLevel, GLint dstX, GLint dstY, GLint dstZ, GLsizei srcWidth, GLsizei srcHeight, GLsizei srcDepth); -typedef void (APIENTRYP PFNGLFRAMEBUFFERPARAMETERIPROC) (GLenum target, GLenum pname, GLint param); -typedef void (APIENTRYP PFNGLGETFRAMEBUFFERPARAMETERIVPROC) (GLenum target, GLenum pname, GLint *params); -typedef void (APIENTRYP PFNGLGETINTERNALFORMATI64VPROC) (GLenum target, GLenum internalformat, GLenum pname, GLsizei bufSize, GLint64 *params); -typedef void (APIENTRYP PFNGLINVALIDATETEXSUBIMAGEPROC) (GLuint texture, GLint level, GLint xoffset, GLint yoffset, GLint zoffset, GLsizei width, GLsizei height, GLsizei depth); -typedef void (APIENTRYP PFNGLINVALIDATETEXIMAGEPROC) (GLuint texture, GLint level); -typedef void (APIENTRYP PFNGLINVALIDATEBUFFERSUBDATAPROC) (GLuint buffer, GLintptr offset, GLsizeiptr length); -typedef void (APIENTRYP PFNGLINVALIDATEBUFFERDATAPROC) (GLuint buffer); -typedef void (APIENTRYP PFNGLINVALIDATEFRAMEBUFFERPROC) (GLenum target, GLsizei numAttachments, const GLenum *attachments); -typedef void (APIENTRYP PFNGLINVALIDATESUBFRAMEBUFFERPROC) (GLenum target, GLsizei numAttachments, const GLenum *attachments, GLint x, GLint y, GLsizei width, GLsizei height); -typedef void (APIENTRYP PFNGLMULTIDRAWARRAYSINDIRECTPROC) (GLenum mode, const void *indirect, GLsizei drawcount, GLsizei stride); -typedef void (APIENTRYP PFNGLMULTIDRAWELEMENTSINDIRECTPROC) (GLenum mode, GLenum type, const void *indirect, GLsizei drawcount, GLsizei stride); -typedef void (APIENTRYP PFNGLGETPROGRAMINTERFACEIVPROC) (GLuint program, GLenum programInterface, GLenum pname, GLint *params); -typedef GLuint (APIENTRYP PFNGLGETPROGRAMRESOURCEINDEXPROC) (GLuint program, GLenum programInterface, const GLchar *name); -typedef void (APIENTRYP PFNGLGETPROGRAMRESOURCENAMEPROC) (GLuint program, GLenum programInterface, GLuint index, GLsizei bufSize, GLsizei *length, GLchar *name); -typedef void (APIENTRYP PFNGLGETPROGRAMRESOURCEIVPROC) (GLuint program, GLenum programInterface, GLuint index, GLsizei propCount, const GLenum *props, GLsizei bufSize, GLsizei *length, GLint *params); -typedef GLint (APIENTRYP PFNGLGETPROGRAMRESOURCELOCATIONPROC) (GLuint program, GLenum programInterface, const GLchar *name); -typedef GLint (APIENTRYP PFNGLGETPROGRAMRESOURCELOCATIONINDEXPROC) (GLuint program, GLenum programInterface, const GLchar *name); -typedef void (APIENTRYP PFNGLSHADERSTORAGEBLOCKBINDINGPROC) (GLuint program, GLuint storageBlockIndex, GLuint storageBlockBinding); -typedef void (APIENTRYP PFNGLTEXBUFFERRANGEPROC) (GLenum target, GLenum internalformat, GLuint buffer, GLintptr offset, GLsizeiptr size); -typedef void (APIENTRYP PFNGLTEXSTORAGE2DMULTISAMPLEPROC) (GLenum target, GLsizei samples, GLenum internalformat, GLsizei width, GLsizei height, GLboolean fixedsamplelocations); -typedef void (APIENTRYP PFNGLTEXSTORAGE3DMULTISAMPLEPROC) (GLenum target, GLsizei samples, GLenum internalformat, GLsizei width, GLsizei height, GLsizei depth, GLboolean fixedsamplelocations); -typedef void (APIENTRYP PFNGLTEXTUREVIEWPROC) (GLuint texture, GLenum target, GLuint origtexture, GLenum internalformat, GLuint minlevel, GLuint numlevels, GLuint minlayer, GLuint numlayers); -typedef void (APIENTRYP PFNGLBINDVERTEXBUFFERPROC) (GLuint bindingindex, GLuint buffer, GLintptr offset, GLsizei stride); -typedef void (APIENTRYP PFNGLVERTEXATTRIBFORMATPROC) (GLuint attribindex, GLint size, GLenum type, GLboolean normalized, GLuint relativeoffset); -typedef void (APIENTRYP PFNGLVERTEXATTRIBIFORMATPROC) (GLuint attribindex, GLint size, GLenum type, GLuint relativeoffset); -typedef void (APIENTRYP PFNGLVERTEXATTRIBLFORMATPROC) (GLuint attribindex, GLint size, GLenum type, GLuint relativeoffset); -typedef void (APIENTRYP PFNGLVERTEXATTRIBBINDINGPROC) (GLuint attribindex, GLuint bindingindex); -typedef void (APIENTRYP PFNGLVERTEXBINDINGDIVISORPROC) (GLuint bindingindex, GLuint divisor); -typedef void (APIENTRYP PFNGLDEBUGMESSAGECONTROLPROC) (GLenum source, GLenum type, GLenum severity, GLsizei count, const GLuint *ids, GLboolean enabled); -typedef void (APIENTRYP PFNGLDEBUGMESSAGEINSERTPROC) (GLenum source, GLenum type, GLuint id, GLenum severity, GLsizei length, const GLchar *buf); -typedef void (APIENTRYP PFNGLDEBUGMESSAGECALLBACKPROC) (GLDEBUGPROC callback, const void *userParam); -typedef GLuint (APIENTRYP PFNGLGETDEBUGMESSAGELOGPROC) (GLuint count, GLsizei bufSize, GLenum *sources, GLenum *types, GLuint *ids, GLenum *severities, GLsizei *lengths, GLchar *messageLog); -typedef void (APIENTRYP PFNGLPUSHDEBUGGROUPPROC) (GLenum source, GLuint id, GLsizei length, const GLchar *message); -typedef void (APIENTRYP PFNGLPOPDEBUGGROUPPROC) (void); -typedef void (APIENTRYP PFNGLOBJECTLABELPROC) (GLenum identifier, GLuint name, GLsizei length, const GLchar *label); -typedef void (APIENTRYP PFNGLGETOBJECTLABELPROC) (GLenum identifier, GLuint name, GLsizei bufSize, GLsizei *length, GLchar *label); -typedef void (APIENTRYP PFNGLOBJECTPTRLABELPROC) (const void *ptr, GLsizei length, const GLchar *label); -typedef void (APIENTRYP PFNGLGETOBJECTPTRLABELPROC) (const void *ptr, GLsizei bufSize, GLsizei *length, GLchar *label); -#ifdef GL_GLEXT_PROTOTYPES -GLAPI void APIENTRY glClearBufferData (GLenum target, GLenum internalformat, GLenum format, GLenum type, const void *data); -GLAPI void APIENTRY glClearBufferSubData (GLenum target, GLenum internalformat, GLintptr offset, GLsizeiptr size, GLenum format, GLenum type, const void *data); -GLAPI void APIENTRY glDispatchCompute (GLuint num_groups_x, GLuint num_groups_y, GLuint num_groups_z); -GLAPI void APIENTRY glDispatchComputeIndirect (GLintptr indirect); -GLAPI void APIENTRY glCopyImageSubData (GLuint srcName, GLenum srcTarget, GLint srcLevel, GLint srcX, GLint srcY, GLint srcZ, GLuint dstName, GLenum dstTarget, GLint dstLevel, GLint dstX, GLint dstY, GLint dstZ, GLsizei srcWidth, GLsizei srcHeight, GLsizei srcDepth); -GLAPI void APIENTRY glFramebufferParameteri (GLenum target, GLenum pname, GLint param); -GLAPI void APIENTRY glGetFramebufferParameteriv (GLenum target, GLenum pname, GLint *params); -GLAPI void APIENTRY glGetInternalformati64v (GLenum target, GLenum internalformat, GLenum pname, GLsizei bufSize, GLint64 *params); -GLAPI void APIENTRY glInvalidateTexSubImage (GLuint texture, GLint level, GLint xoffset, GLint yoffset, GLint zoffset, GLsizei width, GLsizei height, GLsizei depth); -GLAPI void APIENTRY glInvalidateTexImage (GLuint texture, GLint level); -GLAPI void APIENTRY glInvalidateBufferSubData (GLuint buffer, GLintptr offset, GLsizeiptr length); -GLAPI void APIENTRY glInvalidateBufferData (GLuint buffer); -GLAPI void APIENTRY glInvalidateFramebuffer (GLenum target, GLsizei numAttachments, const GLenum *attachments); -GLAPI void APIENTRY glInvalidateSubFramebuffer (GLenum target, GLsizei numAttachments, const GLenum *attachments, GLint x, GLint y, GLsizei width, GLsizei height); -GLAPI void APIENTRY glMultiDrawArraysIndirect (GLenum mode, const void *indirect, GLsizei drawcount, GLsizei stride); -GLAPI void APIENTRY glMultiDrawElementsIndirect (GLenum mode, GLenum type, const void *indirect, GLsizei drawcount, GLsizei stride); -GLAPI void APIENTRY glGetProgramInterfaceiv (GLuint program, GLenum programInterface, GLenum pname, GLint *params); -GLAPI GLuint APIENTRY glGetProgramResourceIndex (GLuint program, GLenum programInterface, const GLchar *name); -GLAPI void APIENTRY glGetProgramResourceName (GLuint program, GLenum programInterface, GLuint index, GLsizei bufSize, GLsizei *length, GLchar *name); -GLAPI void APIENTRY glGetProgramResourceiv (GLuint program, GLenum programInterface, GLuint index, GLsizei propCount, const GLenum *props, GLsizei bufSize, GLsizei *length, GLint *params); -GLAPI GLint APIENTRY glGetProgramResourceLocation (GLuint program, GLenum programInterface, const GLchar *name); -GLAPI GLint APIENTRY glGetProgramResourceLocationIndex (GLuint program, GLenum programInterface, const GLchar *name); -GLAPI void APIENTRY glShaderStorageBlockBinding (GLuint program, GLuint storageBlockIndex, GLuint storageBlockBinding); -GLAPI void APIENTRY glTexBufferRange (GLenum target, GLenum internalformat, GLuint buffer, GLintptr offset, GLsizeiptr size); -GLAPI void APIENTRY glTexStorage2DMultisample (GLenum target, GLsizei samples, GLenum internalformat, GLsizei width, GLsizei height, GLboolean fixedsamplelocations); -GLAPI void APIENTRY glTexStorage3DMultisample (GLenum target, GLsizei samples, GLenum internalformat, GLsizei width, GLsizei height, GLsizei depth, GLboolean fixedsamplelocations); -GLAPI void APIENTRY glTextureView (GLuint texture, GLenum target, GLuint origtexture, GLenum internalformat, GLuint minlevel, GLuint numlevels, GLuint minlayer, GLuint numlayers); -GLAPI void APIENTRY glBindVertexBuffer (GLuint bindingindex, GLuint buffer, GLintptr offset, GLsizei stride); -GLAPI void APIENTRY glVertexAttribFormat (GLuint attribindex, GLint size, GLenum type, GLboolean normalized, GLuint relativeoffset); -GLAPI void APIENTRY glVertexAttribIFormat (GLuint attribindex, GLint size, GLenum type, GLuint relativeoffset); -GLAPI void APIENTRY glVertexAttribLFormat (GLuint attribindex, GLint size, GLenum type, GLuint relativeoffset); -GLAPI void APIENTRY glVertexAttribBinding (GLuint attribindex, GLuint bindingindex); -GLAPI void APIENTRY glVertexBindingDivisor (GLuint bindingindex, GLuint divisor); -GLAPI void APIENTRY glDebugMessageControl (GLenum source, GLenum type, GLenum severity, GLsizei count, const GLuint *ids, GLboolean enabled); -GLAPI void APIENTRY glDebugMessageInsert (GLenum source, GLenum type, GLuint id, GLenum severity, GLsizei length, const GLchar *buf); -GLAPI void APIENTRY glDebugMessageCallback (GLDEBUGPROC callback, const void *userParam); -GLAPI GLuint APIENTRY glGetDebugMessageLog (GLuint count, GLsizei bufSize, GLenum *sources, GLenum *types, GLuint *ids, GLenum *severities, GLsizei *lengths, GLchar *messageLog); -GLAPI void APIENTRY glPushDebugGroup (GLenum source, GLuint id, GLsizei length, const GLchar *message); -GLAPI void APIENTRY glPopDebugGroup (void); -GLAPI void APIENTRY glObjectLabel (GLenum identifier, GLuint name, GLsizei length, const GLchar *label); -GLAPI void APIENTRY glGetObjectLabel (GLenum identifier, GLuint name, GLsizei bufSize, GLsizei *length, GLchar *label); -GLAPI void APIENTRY glObjectPtrLabel (const void *ptr, GLsizei length, const GLchar *label); -GLAPI void APIENTRY glGetObjectPtrLabel (const void *ptr, GLsizei bufSize, GLsizei *length, GLchar *label); -#endif -#endif /* GL_VERSION_4_3 */ - -#ifndef GL_VERSION_4_4 -#define GL_VERSION_4_4 1 -#define GL_MAX_VERTEX_ATTRIB_STRIDE 0x82E5 -#define GL_PRIMITIVE_RESTART_FOR_PATCHES_SUPPORTED 0x8221 -#define GL_TEXTURE_BUFFER_BINDING 0x8C2A -#define GL_MAP_PERSISTENT_BIT 0x0040 -#define GL_MAP_COHERENT_BIT 0x0080 -#define GL_DYNAMIC_STORAGE_BIT 0x0100 -#define GL_CLIENT_STORAGE_BIT 0x0200 -#define GL_CLIENT_MAPPED_BUFFER_BARRIER_BIT 0x00004000 -#define GL_BUFFER_IMMUTABLE_STORAGE 0x821F -#define GL_BUFFER_STORAGE_FLAGS 0x8220 -#define GL_CLEAR_TEXTURE 0x9365 -#define GL_LOCATION_COMPONENT 0x934A -#define GL_TRANSFORM_FEEDBACK_BUFFER_INDEX 0x934B -#define GL_TRANSFORM_FEEDBACK_BUFFER_STRIDE 0x934C -#define GL_QUERY_BUFFER 0x9192 -#define GL_QUERY_BUFFER_BARRIER_BIT 0x00008000 -#define GL_QUERY_BUFFER_BINDING 0x9193 -#define GL_QUERY_RESULT_NO_WAIT 0x9194 -#define GL_MIRROR_CLAMP_TO_EDGE 0x8743 -typedef void (APIENTRYP PFNGLBUFFERSTORAGEPROC) (GLenum target, GLsizeiptr size, const void *data, GLbitfield flags); -typedef void (APIENTRYP PFNGLCLEARTEXIMAGEPROC) (GLuint texture, GLint level, GLenum format, GLenum type, const void *data); -typedef void (APIENTRYP PFNGLCLEARTEXSUBIMAGEPROC) (GLuint texture, GLint level, GLint xoffset, GLint yoffset, GLint zoffset, GLsizei width, GLsizei height, GLsizei depth, GLenum format, GLenum type, const void *data); -typedef void (APIENTRYP PFNGLBINDBUFFERSBASEPROC) (GLenum target, GLuint first, GLsizei count, const GLuint *buffers); -typedef void (APIENTRYP PFNGLBINDBUFFERSRANGEPROC) (GLenum target, GLuint first, GLsizei count, const GLuint *buffers, const GLintptr *offsets, const GLsizeiptr *sizes); -typedef void (APIENTRYP PFNGLBINDTEXTURESPROC) (GLuint first, GLsizei count, const GLuint *textures); -typedef void (APIENTRYP PFNGLBINDSAMPLERSPROC) (GLuint first, GLsizei count, const GLuint *samplers); -typedef void (APIENTRYP PFNGLBINDIMAGETEXTURESPROC) (GLuint first, GLsizei count, const GLuint *textures); -typedef void (APIENTRYP PFNGLBINDVERTEXBUFFERSPROC) (GLuint first, GLsizei count, const GLuint *buffers, const GLintptr *offsets, const GLsizei *strides); -#ifdef GL_GLEXT_PROTOTYPES -GLAPI void APIENTRY glBufferStorage (GLenum target, GLsizeiptr size, const void *data, GLbitfield flags); -GLAPI void APIENTRY glClearTexImage (GLuint texture, GLint level, GLenum format, GLenum type, const void *data); -GLAPI void APIENTRY glClearTexSubImage (GLuint texture, GLint level, GLint xoffset, GLint yoffset, GLint zoffset, GLsizei width, GLsizei height, GLsizei depth, GLenum format, GLenum type, const void *data); -GLAPI void APIENTRY glBindBuffersBase (GLenum target, GLuint first, GLsizei count, const GLuint *buffers); -GLAPI void APIENTRY glBindBuffersRange (GLenum target, GLuint first, GLsizei count, const GLuint *buffers, const GLintptr *offsets, const GLsizeiptr *sizes); -GLAPI void APIENTRY glBindTextures (GLuint first, GLsizei count, const GLuint *textures); -GLAPI void APIENTRY glBindSamplers (GLuint first, GLsizei count, const GLuint *samplers); -GLAPI void APIENTRY glBindImageTextures (GLuint first, GLsizei count, const GLuint *textures); -GLAPI void APIENTRY glBindVertexBuffers (GLuint first, GLsizei count, const GLuint *buffers, const GLintptr *offsets, const GLsizei *strides); -#endif -#endif /* GL_VERSION_4_4 */ - -#ifndef GL_ARB_ES2_compatibility -#define GL_ARB_ES2_compatibility 1 -#endif /* GL_ARB_ES2_compatibility */ - -#ifndef GL_ARB_ES3_compatibility -#define GL_ARB_ES3_compatibility 1 -#endif /* GL_ARB_ES3_compatibility */ - -#ifndef GL_ARB_arrays_of_arrays -#define GL_ARB_arrays_of_arrays 1 -#endif /* GL_ARB_arrays_of_arrays */ - -#ifndef GL_ARB_base_instance -#define GL_ARB_base_instance 1 -#endif /* GL_ARB_base_instance */ - -#ifndef GL_ARB_bindless_texture -#define GL_ARB_bindless_texture 1 -typedef uint64_t GLuint64EXT; -#define GL_UNSIGNED_INT64_ARB 0x140F -typedef GLuint64 (APIENTRYP PFNGLGETTEXTUREHANDLEARBPROC) (GLuint texture); -typedef GLuint64 (APIENTRYP PFNGLGETTEXTURESAMPLERHANDLEARBPROC) (GLuint texture, GLuint sampler); -typedef void (APIENTRYP PFNGLMAKETEXTUREHANDLERESIDENTARBPROC) (GLuint64 handle); -typedef void (APIENTRYP PFNGLMAKETEXTUREHANDLENONRESIDENTARBPROC) (GLuint64 handle); -typedef GLuint64 (APIENTRYP PFNGLGETIMAGEHANDLEARBPROC) (GLuint texture, GLint level, GLboolean layered, GLint layer, GLenum format); -typedef void (APIENTRYP PFNGLMAKEIMAGEHANDLERESIDENTARBPROC) (GLuint64 handle, GLenum access); -typedef void (APIENTRYP PFNGLMAKEIMAGEHANDLENONRESIDENTARBPROC) (GLuint64 handle); -typedef void (APIENTRYP PFNGLUNIFORMHANDLEUI64ARBPROC) (GLint location, GLuint64 value); -typedef void (APIENTRYP PFNGLUNIFORMHANDLEUI64VARBPROC) (GLint location, GLsizei count, const GLuint64 *value); -typedef void (APIENTRYP PFNGLPROGRAMUNIFORMHANDLEUI64ARBPROC) (GLuint program, GLint location, GLuint64 value); -typedef void (APIENTRYP PFNGLPROGRAMUNIFORMHANDLEUI64VARBPROC) (GLuint program, GLint location, GLsizei count, const GLuint64 *values); -typedef GLboolean (APIENTRYP PFNGLISTEXTUREHANDLERESIDENTARBPROC) (GLuint64 handle); -typedef GLboolean (APIENTRYP PFNGLISIMAGEHANDLERESIDENTARBPROC) (GLuint64 handle); -typedef void (APIENTRYP PFNGLVERTEXATTRIBL1UI64ARBPROC) (GLuint index, GLuint64EXT x); -typedef void (APIENTRYP PFNGLVERTEXATTRIBL1UI64VARBPROC) (GLuint index, const GLuint64EXT *v); -typedef void (APIENTRYP PFNGLGETVERTEXATTRIBLUI64VARBPROC) (GLuint index, GLenum pname, GLuint64EXT *params); -#ifdef GL_GLEXT_PROTOTYPES -GLAPI GLuint64 APIENTRY glGetTextureHandleARB (GLuint texture); -GLAPI GLuint64 APIENTRY glGetTextureSamplerHandleARB (GLuint texture, GLuint sampler); -GLAPI void APIENTRY glMakeTextureHandleResidentARB (GLuint64 handle); -GLAPI void APIENTRY glMakeTextureHandleNonResidentARB (GLuint64 handle); -GLAPI GLuint64 APIENTRY glGetImageHandleARB (GLuint texture, GLint level, GLboolean layered, GLint layer, GLenum format); -GLAPI void APIENTRY glMakeImageHandleResidentARB (GLuint64 handle, GLenum access); -GLAPI void APIENTRY glMakeImageHandleNonResidentARB (GLuint64 handle); -GLAPI void APIENTRY glUniformHandleui64ARB (GLint location, GLuint64 value); -GLAPI void APIENTRY glUniformHandleui64vARB (GLint location, GLsizei count, const GLuint64 *value); -GLAPI void APIENTRY glProgramUniformHandleui64ARB (GLuint program, GLint location, GLuint64 value); -GLAPI void APIENTRY glProgramUniformHandleui64vARB (GLuint program, GLint location, GLsizei count, const GLuint64 *values); -GLAPI GLboolean APIENTRY glIsTextureHandleResidentARB (GLuint64 handle); -GLAPI GLboolean APIENTRY glIsImageHandleResidentARB (GLuint64 handle); -GLAPI void APIENTRY glVertexAttribL1ui64ARB (GLuint index, GLuint64EXT x); -GLAPI void APIENTRY glVertexAttribL1ui64vARB (GLuint index, const GLuint64EXT *v); -GLAPI void APIENTRY glGetVertexAttribLui64vARB (GLuint index, GLenum pname, GLuint64EXT *params); -#endif -#endif /* GL_ARB_bindless_texture */ - -#ifndef GL_ARB_blend_func_extended -#define GL_ARB_blend_func_extended 1 -#endif /* GL_ARB_blend_func_extended */ - -#ifndef GL_ARB_buffer_storage -#define GL_ARB_buffer_storage 1 -#endif /* GL_ARB_buffer_storage */ - -#ifndef GL_ARB_cl_event -#define GL_ARB_cl_event 1 -struct _cl_context; -struct _cl_event; -#define GL_SYNC_CL_EVENT_ARB 0x8240 -#define GL_SYNC_CL_EVENT_COMPLETE_ARB 0x8241 -typedef GLsync (APIENTRYP PFNGLCREATESYNCFROMCLEVENTARBPROC) (struct _cl_context *context, struct _cl_event *event, GLbitfield flags); -#ifdef GL_GLEXT_PROTOTYPES -GLAPI GLsync APIENTRY glCreateSyncFromCLeventARB (struct _cl_context *context, struct _cl_event *event, GLbitfield flags); -#endif -#endif /* GL_ARB_cl_event */ - -#ifndef GL_ARB_clear_buffer_object -#define GL_ARB_clear_buffer_object 1 -#endif /* GL_ARB_clear_buffer_object */ - -#ifndef GL_ARB_clear_texture -#define GL_ARB_clear_texture 1 -#endif /* GL_ARB_clear_texture */ - -#ifndef GL_ARB_color_buffer_float -#define GL_ARB_color_buffer_float 1 -#define GL_RGBA_FLOAT_MODE_ARB 0x8820 -#define GL_CLAMP_VERTEX_COLOR_ARB 0x891A -#define GL_CLAMP_FRAGMENT_COLOR_ARB 0x891B -#define GL_CLAMP_READ_COLOR_ARB 0x891C -#define GL_FIXED_ONLY_ARB 0x891D -typedef void (APIENTRYP PFNGLCLAMPCOLORARBPROC) (GLenum target, GLenum clamp); -#ifdef GL_GLEXT_PROTOTYPES -GLAPI void APIENTRY glClampColorARB (GLenum target, GLenum clamp); -#endif -#endif /* GL_ARB_color_buffer_float */ - -#ifndef GL_ARB_compatibility -#define GL_ARB_compatibility 1 -#endif /* GL_ARB_compatibility */ - -#ifndef GL_ARB_compressed_texture_pixel_storage -#define GL_ARB_compressed_texture_pixel_storage 1 -#endif /* GL_ARB_compressed_texture_pixel_storage */ - -#ifndef GL_ARB_compute_shader -#define GL_ARB_compute_shader 1 -#define GL_COMPUTE_SHADER_BIT 0x00000020 -#endif /* GL_ARB_compute_shader */ - -#ifndef GL_ARB_compute_variable_group_size -#define GL_ARB_compute_variable_group_size 1 -#define GL_MAX_COMPUTE_VARIABLE_GROUP_INVOCATIONS_ARB 0x9344 -#define GL_MAX_COMPUTE_FIXED_GROUP_INVOCATIONS_ARB 0x90EB -#define GL_MAX_COMPUTE_VARIABLE_GROUP_SIZE_ARB 0x9345 -#define GL_MAX_COMPUTE_FIXED_GROUP_SIZE_ARB 0x91BF -typedef void (APIENTRYP PFNGLDISPATCHCOMPUTEGROUPSIZEARBPROC) (GLuint num_groups_x, GLuint num_groups_y, GLuint num_groups_z, GLuint group_size_x, GLuint group_size_y, GLuint group_size_z); -#ifdef GL_GLEXT_PROTOTYPES -GLAPI void APIENTRY glDispatchComputeGroupSizeARB (GLuint num_groups_x, GLuint num_groups_y, GLuint num_groups_z, GLuint group_size_x, GLuint group_size_y, GLuint group_size_z); -#endif -#endif /* GL_ARB_compute_variable_group_size */ - -#ifndef GL_ARB_conservative_depth -#define GL_ARB_conservative_depth 1 -#endif /* GL_ARB_conservative_depth */ - -#ifndef GL_ARB_copy_buffer -#define GL_ARB_copy_buffer 1 -#define GL_COPY_READ_BUFFER_BINDING 0x8F36 -#define GL_COPY_WRITE_BUFFER_BINDING 0x8F37 -#endif /* GL_ARB_copy_buffer */ - -#ifndef GL_ARB_copy_image -#define GL_ARB_copy_image 1 -#endif /* GL_ARB_copy_image */ - -#ifndef GL_ARB_debug_output -#define GL_ARB_debug_output 1 -typedef void (APIENTRY *GLDEBUGPROCARB)(GLenum source,GLenum type,GLuint id,GLenum severity,GLsizei length,const GLchar *message,const void *userParam); -#define GL_DEBUG_OUTPUT_SYNCHRONOUS_ARB 0x8242 -#define GL_DEBUG_NEXT_LOGGED_MESSAGE_LENGTH_ARB 0x8243 -#define GL_DEBUG_CALLBACK_FUNCTION_ARB 0x8244 -#define GL_DEBUG_CALLBACK_USER_PARAM_ARB 0x8245 -#define GL_DEBUG_SOURCE_API_ARB 0x8246 -#define GL_DEBUG_SOURCE_WINDOW_SYSTEM_ARB 0x8247 -#define GL_DEBUG_SOURCE_SHADER_COMPILER_ARB 0x8248 -#define GL_DEBUG_SOURCE_THIRD_PARTY_ARB 0x8249 -#define GL_DEBUG_SOURCE_APPLICATION_ARB 0x824A -#define GL_DEBUG_SOURCE_OTHER_ARB 0x824B -#define GL_DEBUG_TYPE_ERROR_ARB 0x824C -#define GL_DEBUG_TYPE_DEPRECATED_BEHAVIOR_ARB 0x824D -#define GL_DEBUG_TYPE_UNDEFINED_BEHAVIOR_ARB 0x824E -#define GL_DEBUG_TYPE_PORTABILITY_ARB 0x824F -#define GL_DEBUG_TYPE_PERFORMANCE_ARB 0x8250 -#define GL_DEBUG_TYPE_OTHER_ARB 0x8251 -#define GL_MAX_DEBUG_MESSAGE_LENGTH_ARB 0x9143 -#define GL_MAX_DEBUG_LOGGED_MESSAGES_ARB 0x9144 -#define GL_DEBUG_LOGGED_MESSAGES_ARB 0x9145 -#define GL_DEBUG_SEVERITY_HIGH_ARB 0x9146 -#define GL_DEBUG_SEVERITY_MEDIUM_ARB 0x9147 -#define GL_DEBUG_SEVERITY_LOW_ARB 0x9148 -typedef void (APIENTRYP PFNGLDEBUGMESSAGECONTROLARBPROC) (GLenum source, GLenum type, GLenum severity, GLsizei count, const GLuint *ids, GLboolean enabled); -typedef void (APIENTRYP PFNGLDEBUGMESSAGEINSERTARBPROC) (GLenum source, GLenum type, GLuint id, GLenum severity, GLsizei length, const GLchar *buf); -typedef void (APIENTRYP PFNGLDEBUGMESSAGECALLBACKARBPROC) (GLDEBUGPROCARB callback, const void *userParam); -typedef GLuint (APIENTRYP PFNGLGETDEBUGMESSAGELOGARBPROC) (GLuint count, GLsizei bufSize, GLenum *sources, GLenum *types, GLuint *ids, GLenum *severities, GLsizei *lengths, GLchar *messageLog); -#ifdef GL_GLEXT_PROTOTYPES -GLAPI void APIENTRY glDebugMessageControlARB (GLenum source, GLenum type, GLenum severity, GLsizei count, const GLuint *ids, GLboolean enabled); -GLAPI void APIENTRY glDebugMessageInsertARB (GLenum source, GLenum type, GLuint id, GLenum severity, GLsizei length, const GLchar *buf); -GLAPI void APIENTRY glDebugMessageCallbackARB (GLDEBUGPROCARB callback, const void *userParam); -GLAPI GLuint APIENTRY glGetDebugMessageLogARB (GLuint count, GLsizei bufSize, GLenum *sources, GLenum *types, GLuint *ids, GLenum *severities, GLsizei *lengths, GLchar *messageLog); -#endif -#endif /* GL_ARB_debug_output */ - -#ifndef GL_ARB_depth_buffer_float -#define GL_ARB_depth_buffer_float 1 -#endif /* GL_ARB_depth_buffer_float */ - -#ifndef GL_ARB_depth_clamp -#define GL_ARB_depth_clamp 1 -#endif /* GL_ARB_depth_clamp */ - -#ifndef GL_ARB_depth_texture -#define GL_ARB_depth_texture 1 -#define GL_DEPTH_COMPONENT16_ARB 0x81A5 -#define GL_DEPTH_COMPONENT24_ARB 0x81A6 -#define GL_DEPTH_COMPONENT32_ARB 0x81A7 -#define GL_TEXTURE_DEPTH_SIZE_ARB 0x884A -#define GL_DEPTH_TEXTURE_MODE_ARB 0x884B -#endif /* GL_ARB_depth_texture */ - -#ifndef GL_ARB_draw_buffers -#define GL_ARB_draw_buffers 1 -#define GL_MAX_DRAW_BUFFERS_ARB 0x8824 -#define GL_DRAW_BUFFER0_ARB 0x8825 -#define GL_DRAW_BUFFER1_ARB 0x8826 -#define GL_DRAW_BUFFER2_ARB 0x8827 -#define GL_DRAW_BUFFER3_ARB 0x8828 -#define GL_DRAW_BUFFER4_ARB 0x8829 -#define GL_DRAW_BUFFER5_ARB 0x882A -#define GL_DRAW_BUFFER6_ARB 0x882B -#define GL_DRAW_BUFFER7_ARB 0x882C -#define GL_DRAW_BUFFER8_ARB 0x882D -#define GL_DRAW_BUFFER9_ARB 0x882E -#define GL_DRAW_BUFFER10_ARB 0x882F -#define GL_DRAW_BUFFER11_ARB 0x8830 -#define GL_DRAW_BUFFER12_ARB 0x8831 -#define GL_DRAW_BUFFER13_ARB 0x8832 -#define GL_DRAW_BUFFER14_ARB 0x8833 -#define GL_DRAW_BUFFER15_ARB 0x8834 -typedef void (APIENTRYP PFNGLDRAWBUFFERSARBPROC) (GLsizei n, const GLenum *bufs); -#ifdef GL_GLEXT_PROTOTYPES -GLAPI void APIENTRY glDrawBuffersARB (GLsizei n, const GLenum *bufs); -#endif -#endif /* GL_ARB_draw_buffers */ - -#ifndef GL_ARB_draw_buffers_blend -#define GL_ARB_draw_buffers_blend 1 -typedef void (APIENTRYP PFNGLBLENDEQUATIONIARBPROC) (GLuint buf, GLenum mode); -typedef void (APIENTRYP PFNGLBLENDEQUATIONSEPARATEIARBPROC) (GLuint buf, GLenum modeRGB, GLenum modeAlpha); -typedef void (APIENTRYP PFNGLBLENDFUNCIARBPROC) (GLuint buf, GLenum src, GLenum dst); -typedef void (APIENTRYP PFNGLBLENDFUNCSEPARATEIARBPROC) (GLuint buf, GLenum srcRGB, GLenum dstRGB, GLenum srcAlpha, GLenum dstAlpha); -#ifdef GL_GLEXT_PROTOTYPES -GLAPI void APIENTRY glBlendEquationiARB (GLuint buf, GLenum mode); -GLAPI void APIENTRY glBlendEquationSeparateiARB (GLuint buf, GLenum modeRGB, GLenum modeAlpha); -GLAPI void APIENTRY glBlendFunciARB (GLuint buf, GLenum src, GLenum dst); -GLAPI void APIENTRY glBlendFuncSeparateiARB (GLuint buf, GLenum srcRGB, GLenum dstRGB, GLenum srcAlpha, GLenum dstAlpha); -#endif -#endif /* GL_ARB_draw_buffers_blend */ - -#ifndef GL_ARB_draw_elements_base_vertex -#define GL_ARB_draw_elements_base_vertex 1 -#endif /* GL_ARB_draw_elements_base_vertex */ - -#ifndef GL_ARB_draw_indirect -#define GL_ARB_draw_indirect 1 -#endif /* GL_ARB_draw_indirect */ - -#ifndef GL_ARB_draw_instanced -#define GL_ARB_draw_instanced 1 -typedef void (APIENTRYP PFNGLDRAWARRAYSINSTANCEDARBPROC) (GLenum mode, GLint first, GLsizei count, GLsizei primcount); -typedef void (APIENTRYP PFNGLDRAWELEMENTSINSTANCEDARBPROC) (GLenum mode, GLsizei count, GLenum type, const void *indices, GLsizei primcount); -#ifdef GL_GLEXT_PROTOTYPES -GLAPI void APIENTRY glDrawArraysInstancedARB (GLenum mode, GLint first, GLsizei count, GLsizei primcount); -GLAPI void APIENTRY glDrawElementsInstancedARB (GLenum mode, GLsizei count, GLenum type, const void *indices, GLsizei primcount); -#endif -#endif /* GL_ARB_draw_instanced */ - -#ifndef GL_ARB_enhanced_layouts -#define GL_ARB_enhanced_layouts 1 -#endif /* GL_ARB_enhanced_layouts */ - -#ifndef GL_ARB_explicit_attrib_location -#define GL_ARB_explicit_attrib_location 1 -#endif /* GL_ARB_explicit_attrib_location */ - -#ifndef GL_ARB_explicit_uniform_location -#define GL_ARB_explicit_uniform_location 1 -#endif /* GL_ARB_explicit_uniform_location */ - -#ifndef GL_ARB_fragment_coord_conventions -#define GL_ARB_fragment_coord_conventions 1 -#endif /* GL_ARB_fragment_coord_conventions */ - -#ifndef GL_ARB_fragment_layer_viewport -#define GL_ARB_fragment_layer_viewport 1 -#endif /* GL_ARB_fragment_layer_viewport */ - -#ifndef GL_ARB_fragment_program -#define GL_ARB_fragment_program 1 -#define GL_FRAGMENT_PROGRAM_ARB 0x8804 -#define GL_PROGRAM_FORMAT_ASCII_ARB 0x8875 -#define GL_PROGRAM_LENGTH_ARB 0x8627 -#define GL_PROGRAM_FORMAT_ARB 0x8876 -#define GL_PROGRAM_BINDING_ARB 0x8677 -#define GL_PROGRAM_INSTRUCTIONS_ARB 0x88A0 -#define GL_MAX_PROGRAM_INSTRUCTIONS_ARB 0x88A1 -#define GL_PROGRAM_NATIVE_INSTRUCTIONS_ARB 0x88A2 -#define GL_MAX_PROGRAM_NATIVE_INSTRUCTIONS_ARB 0x88A3 -#define GL_PROGRAM_TEMPORARIES_ARB 0x88A4 -#define GL_MAX_PROGRAM_TEMPORARIES_ARB 0x88A5 -#define GL_PROGRAM_NATIVE_TEMPORARIES_ARB 0x88A6 -#define GL_MAX_PROGRAM_NATIVE_TEMPORARIES_ARB 0x88A7 -#define GL_PROGRAM_PARAMETERS_ARB 0x88A8 -#define GL_MAX_PROGRAM_PARAMETERS_ARB 0x88A9 -#define GL_PROGRAM_NATIVE_PARAMETERS_ARB 0x88AA -#define GL_MAX_PROGRAM_NATIVE_PARAMETERS_ARB 0x88AB -#define GL_PROGRAM_ATTRIBS_ARB 0x88AC -#define GL_MAX_PROGRAM_ATTRIBS_ARB 0x88AD -#define GL_PROGRAM_NATIVE_ATTRIBS_ARB 0x88AE -#define GL_MAX_PROGRAM_NATIVE_ATTRIBS_ARB 0x88AF -#define GL_MAX_PROGRAM_LOCAL_PARAMETERS_ARB 0x88B4 -#define GL_MAX_PROGRAM_ENV_PARAMETERS_ARB 0x88B5 -#define GL_PROGRAM_UNDER_NATIVE_LIMITS_ARB 0x88B6 -#define GL_PROGRAM_ALU_INSTRUCTIONS_ARB 0x8805 -#define GL_PROGRAM_TEX_INSTRUCTIONS_ARB 0x8806 -#define GL_PROGRAM_TEX_INDIRECTIONS_ARB 0x8807 -#define GL_PROGRAM_NATIVE_ALU_INSTRUCTIONS_ARB 0x8808 -#define GL_PROGRAM_NATIVE_TEX_INSTRUCTIONS_ARB 0x8809 -#define GL_PROGRAM_NATIVE_TEX_INDIRECTIONS_ARB 0x880A -#define GL_MAX_PROGRAM_ALU_INSTRUCTIONS_ARB 0x880B -#define GL_MAX_PROGRAM_TEX_INSTRUCTIONS_ARB 0x880C -#define GL_MAX_PROGRAM_TEX_INDIRECTIONS_ARB 0x880D -#define GL_MAX_PROGRAM_NATIVE_ALU_INSTRUCTIONS_ARB 0x880E -#define GL_MAX_PROGRAM_NATIVE_TEX_INSTRUCTIONS_ARB 0x880F -#define GL_MAX_PROGRAM_NATIVE_TEX_INDIRECTIONS_ARB 0x8810 -#define GL_PROGRAM_STRING_ARB 0x8628 -#define GL_PROGRAM_ERROR_POSITION_ARB 0x864B -#define GL_CURRENT_MATRIX_ARB 0x8641 -#define GL_TRANSPOSE_CURRENT_MATRIX_ARB 0x88B7 -#define GL_CURRENT_MATRIX_STACK_DEPTH_ARB 0x8640 -#define GL_MAX_PROGRAM_MATRICES_ARB 0x862F -#define GL_MAX_PROGRAM_MATRIX_STACK_DEPTH_ARB 0x862E -#define GL_MAX_TEXTURE_COORDS_ARB 0x8871 -#define GL_MAX_TEXTURE_IMAGE_UNITS_ARB 0x8872 -#define GL_PROGRAM_ERROR_STRING_ARB 0x8874 -#define GL_MATRIX0_ARB 0x88C0 -#define GL_MATRIX1_ARB 0x88C1 -#define GL_MATRIX2_ARB 0x88C2 -#define GL_MATRIX3_ARB 0x88C3 -#define GL_MATRIX4_ARB 0x88C4 -#define GL_MATRIX5_ARB 0x88C5 -#define GL_MATRIX6_ARB 0x88C6 -#define GL_MATRIX7_ARB 0x88C7 -#define GL_MATRIX8_ARB 0x88C8 -#define GL_MATRIX9_ARB 0x88C9 -#define GL_MATRIX10_ARB 0x88CA -#define GL_MATRIX11_ARB 0x88CB -#define GL_MATRIX12_ARB 0x88CC -#define GL_MATRIX13_ARB 0x88CD -#define GL_MATRIX14_ARB 0x88CE -#define GL_MATRIX15_ARB 0x88CF -#define GL_MATRIX16_ARB 0x88D0 -#define GL_MATRIX17_ARB 0x88D1 -#define GL_MATRIX18_ARB 0x88D2 -#define GL_MATRIX19_ARB 0x88D3 -#define GL_MATRIX20_ARB 0x88D4 -#define GL_MATRIX21_ARB 0x88D5 -#define GL_MATRIX22_ARB 0x88D6 -#define GL_MATRIX23_ARB 0x88D7 -#define GL_MATRIX24_ARB 0x88D8 -#define GL_MATRIX25_ARB 0x88D9 -#define GL_MATRIX26_ARB 0x88DA -#define GL_MATRIX27_ARB 0x88DB -#define GL_MATRIX28_ARB 0x88DC -#define GL_MATRIX29_ARB 0x88DD -#define GL_MATRIX30_ARB 0x88DE -#define GL_MATRIX31_ARB 0x88DF -typedef void (APIENTRYP PFNGLPROGRAMSTRINGARBPROC) (GLenum target, GLenum format, GLsizei len, const void *string); -typedef void (APIENTRYP PFNGLBINDPROGRAMARBPROC) (GLenum target, GLuint program); -typedef void (APIENTRYP PFNGLDELETEPROGRAMSARBPROC) (GLsizei n, const GLuint *programs); -typedef void (APIENTRYP PFNGLGENPROGRAMSARBPROC) (GLsizei n, GLuint *programs); -typedef void (APIENTRYP PFNGLPROGRAMENVPARAMETER4DARBPROC) (GLenum target, GLuint index, GLdouble x, GLdouble y, GLdouble z, GLdouble w); -typedef void (APIENTRYP PFNGLPROGRAMENVPARAMETER4DVARBPROC) (GLenum target, GLuint index, const GLdouble *params); -typedef void (APIENTRYP PFNGLPROGRAMENVPARAMETER4FARBPROC) (GLenum target, GLuint index, GLfloat x, GLfloat y, GLfloat z, GLfloat w); -typedef void (APIENTRYP PFNGLPROGRAMENVPARAMETER4FVARBPROC) (GLenum target, GLuint index, const GLfloat *params); -typedef void (APIENTRYP PFNGLPROGRAMLOCALPARAMETER4DARBPROC) (GLenum target, GLuint index, GLdouble x, GLdouble y, GLdouble z, GLdouble w); -typedef void (APIENTRYP PFNGLPROGRAMLOCALPARAMETER4DVARBPROC) (GLenum target, GLuint index, const GLdouble *params); -typedef void (APIENTRYP PFNGLPROGRAMLOCALPARAMETER4FARBPROC) (GLenum target, GLuint index, GLfloat x, GLfloat y, GLfloat z, GLfloat w); -typedef void (APIENTRYP PFNGLPROGRAMLOCALPARAMETER4FVARBPROC) (GLenum target, GLuint index, const GLfloat *params); -typedef void (APIENTRYP PFNGLGETPROGRAMENVPARAMETERDVARBPROC) (GLenum target, GLuint index, GLdouble *params); -typedef void (APIENTRYP PFNGLGETPROGRAMENVPARAMETERFVARBPROC) (GLenum target, GLuint index, GLfloat *params); -typedef void (APIENTRYP PFNGLGETPROGRAMLOCALPARAMETERDVARBPROC) (GLenum target, GLuint index, GLdouble *params); -typedef void (APIENTRYP PFNGLGETPROGRAMLOCALPARAMETERFVARBPROC) (GLenum target, GLuint index, GLfloat *params); -typedef void (APIENTRYP PFNGLGETPROGRAMIVARBPROC) (GLenum target, GLenum pname, GLint *params); -typedef void (APIENTRYP PFNGLGETPROGRAMSTRINGARBPROC) (GLenum target, GLenum pname, void *string); -typedef GLboolean (APIENTRYP PFNGLISPROGRAMARBPROC) (GLuint program); -#ifdef GL_GLEXT_PROTOTYPES -GLAPI void APIENTRY glProgramStringARB (GLenum target, GLenum format, GLsizei len, const void *string); -GLAPI void APIENTRY glBindProgramARB (GLenum target, GLuint program); -GLAPI void APIENTRY glDeleteProgramsARB (GLsizei n, const GLuint *programs); -GLAPI void APIENTRY glGenProgramsARB (GLsizei n, GLuint *programs); -GLAPI void APIENTRY glProgramEnvParameter4dARB (GLenum target, GLuint index, GLdouble x, GLdouble y, GLdouble z, GLdouble w); -GLAPI void APIENTRY glProgramEnvParameter4dvARB (GLenum target, GLuint index, const GLdouble *params); -GLAPI void APIENTRY glProgramEnvParameter4fARB (GLenum target, GLuint index, GLfloat x, GLfloat y, GLfloat z, GLfloat w); -GLAPI void APIENTRY glProgramEnvParameter4fvARB (GLenum target, GLuint index, const GLfloat *params); -GLAPI void APIENTRY glProgramLocalParameter4dARB (GLenum target, GLuint index, GLdouble x, GLdouble y, GLdouble z, GLdouble w); -GLAPI void APIENTRY glProgramLocalParameter4dvARB (GLenum target, GLuint index, const GLdouble *params); -GLAPI void APIENTRY glProgramLocalParameter4fARB (GLenum target, GLuint index, GLfloat x, GLfloat y, GLfloat z, GLfloat w); -GLAPI void APIENTRY glProgramLocalParameter4fvARB (GLenum target, GLuint index, const GLfloat *params); -GLAPI void APIENTRY glGetProgramEnvParameterdvARB (GLenum target, GLuint index, GLdouble *params); -GLAPI void APIENTRY glGetProgramEnvParameterfvARB (GLenum target, GLuint index, GLfloat *params); -GLAPI void APIENTRY glGetProgramLocalParameterdvARB (GLenum target, GLuint index, GLdouble *params); -GLAPI void APIENTRY glGetProgramLocalParameterfvARB (GLenum target, GLuint index, GLfloat *params); -GLAPI void APIENTRY glGetProgramivARB (GLenum target, GLenum pname, GLint *params); -GLAPI void APIENTRY glGetProgramStringARB (GLenum target, GLenum pname, void *string); -GLAPI GLboolean APIENTRY glIsProgramARB (GLuint program); -#endif -#endif /* GL_ARB_fragment_program */ - -#ifndef GL_ARB_fragment_program_shadow -#define GL_ARB_fragment_program_shadow 1 -#endif /* GL_ARB_fragment_program_shadow */ - -#ifndef GL_ARB_fragment_shader -#define GL_ARB_fragment_shader 1 -#define GL_FRAGMENT_SHADER_ARB 0x8B30 -#define GL_MAX_FRAGMENT_UNIFORM_COMPONENTS_ARB 0x8B49 -#define GL_FRAGMENT_SHADER_DERIVATIVE_HINT_ARB 0x8B8B -#endif /* GL_ARB_fragment_shader */ - -#ifndef GL_ARB_framebuffer_no_attachments -#define GL_ARB_framebuffer_no_attachments 1 -#endif /* GL_ARB_framebuffer_no_attachments */ - -#ifndef GL_ARB_framebuffer_object -#define GL_ARB_framebuffer_object 1 -#endif /* GL_ARB_framebuffer_object */ - -#ifndef GL_ARB_framebuffer_sRGB -#define GL_ARB_framebuffer_sRGB 1 -#endif /* GL_ARB_framebuffer_sRGB */ - -#ifndef GL_ARB_geometry_shader4 -#define GL_ARB_geometry_shader4 1 -#define GL_LINES_ADJACENCY_ARB 0x000A -#define GL_LINE_STRIP_ADJACENCY_ARB 0x000B -#define GL_TRIANGLES_ADJACENCY_ARB 0x000C -#define GL_TRIANGLE_STRIP_ADJACENCY_ARB 0x000D -#define GL_PROGRAM_POINT_SIZE_ARB 0x8642 -#define GL_MAX_GEOMETRY_TEXTURE_IMAGE_UNITS_ARB 0x8C29 -#define GL_FRAMEBUFFER_ATTACHMENT_LAYERED_ARB 0x8DA7 -#define GL_FRAMEBUFFER_INCOMPLETE_LAYER_TARGETS_ARB 0x8DA8 -#define GL_FRAMEBUFFER_INCOMPLETE_LAYER_COUNT_ARB 0x8DA9 -#define GL_GEOMETRY_SHADER_ARB 0x8DD9 -#define GL_GEOMETRY_VERTICES_OUT_ARB 0x8DDA -#define GL_GEOMETRY_INPUT_TYPE_ARB 0x8DDB -#define GL_GEOMETRY_OUTPUT_TYPE_ARB 0x8DDC -#define GL_MAX_GEOMETRY_VARYING_COMPONENTS_ARB 0x8DDD -#define GL_MAX_VERTEX_VARYING_COMPONENTS_ARB 0x8DDE -#define GL_MAX_GEOMETRY_UNIFORM_COMPONENTS_ARB 0x8DDF -#define GL_MAX_GEOMETRY_OUTPUT_VERTICES_ARB 0x8DE0 -#define GL_MAX_GEOMETRY_TOTAL_OUTPUT_COMPONENTS_ARB 0x8DE1 -typedef void (APIENTRYP PFNGLPROGRAMPARAMETERIARBPROC) (GLuint program, GLenum pname, GLint value); -typedef void (APIENTRYP PFNGLFRAMEBUFFERTEXTUREARBPROC) (GLenum target, GLenum attachment, GLuint texture, GLint level); -typedef void (APIENTRYP PFNGLFRAMEBUFFERTEXTURELAYERARBPROC) (GLenum target, GLenum attachment, GLuint texture, GLint level, GLint layer); -typedef void (APIENTRYP PFNGLFRAMEBUFFERTEXTUREFACEARBPROC) (GLenum target, GLenum attachment, GLuint texture, GLint level, GLenum face); -#ifdef GL_GLEXT_PROTOTYPES -GLAPI void APIENTRY glProgramParameteriARB (GLuint program, GLenum pname, GLint value); -GLAPI void APIENTRY glFramebufferTextureARB (GLenum target, GLenum attachment, GLuint texture, GLint level); -GLAPI void APIENTRY glFramebufferTextureLayerARB (GLenum target, GLenum attachment, GLuint texture, GLint level, GLint layer); -GLAPI void APIENTRY glFramebufferTextureFaceARB (GLenum target, GLenum attachment, GLuint texture, GLint level, GLenum face); -#endif -#endif /* GL_ARB_geometry_shader4 */ - -#ifndef GL_ARB_get_program_binary -#define GL_ARB_get_program_binary 1 -#endif /* GL_ARB_get_program_binary */ - -#ifndef GL_ARB_gpu_shader5 -#define GL_ARB_gpu_shader5 1 -#endif /* GL_ARB_gpu_shader5 */ - -#ifndef GL_ARB_gpu_shader_fp64 -#define GL_ARB_gpu_shader_fp64 1 -#endif /* GL_ARB_gpu_shader_fp64 */ - -#ifndef GL_ARB_half_float_pixel -#define GL_ARB_half_float_pixel 1 -typedef unsigned short GLhalfARB; -#define GL_HALF_FLOAT_ARB 0x140B -#endif /* GL_ARB_half_float_pixel */ - -#ifndef GL_ARB_half_float_vertex -#define GL_ARB_half_float_vertex 1 -#endif /* GL_ARB_half_float_vertex */ - -#ifndef GL_ARB_imaging -#define GL_ARB_imaging 1 -#define GL_BLEND_COLOR 0x8005 -#define GL_BLEND_EQUATION 0x8009 -#define GL_CONVOLUTION_1D 0x8010 -#define GL_CONVOLUTION_2D 0x8011 -#define GL_SEPARABLE_2D 0x8012 -#define GL_CONVOLUTION_BORDER_MODE 0x8013 -#define GL_CONVOLUTION_FILTER_SCALE 0x8014 -#define GL_CONVOLUTION_FILTER_BIAS 0x8015 -#define GL_REDUCE 0x8016 -#define GL_CONVOLUTION_FORMAT 0x8017 -#define GL_CONVOLUTION_WIDTH 0x8018 -#define GL_CONVOLUTION_HEIGHT 0x8019 -#define GL_MAX_CONVOLUTION_WIDTH 0x801A -#define GL_MAX_CONVOLUTION_HEIGHT 0x801B -#define GL_POST_CONVOLUTION_RED_SCALE 0x801C -#define GL_POST_CONVOLUTION_GREEN_SCALE 0x801D -#define GL_POST_CONVOLUTION_BLUE_SCALE 0x801E -#define GL_POST_CONVOLUTION_ALPHA_SCALE 0x801F -#define GL_POST_CONVOLUTION_RED_BIAS 0x8020 -#define GL_POST_CONVOLUTION_GREEN_BIAS 0x8021 -#define GL_POST_CONVOLUTION_BLUE_BIAS 0x8022 -#define GL_POST_CONVOLUTION_ALPHA_BIAS 0x8023 -#define GL_HISTOGRAM 0x8024 -#define GL_PROXY_HISTOGRAM 0x8025 -#define GL_HISTOGRAM_WIDTH 0x8026 -#define GL_HISTOGRAM_FORMAT 0x8027 -#define GL_HISTOGRAM_RED_SIZE 0x8028 -#define GL_HISTOGRAM_GREEN_SIZE 0x8029 -#define GL_HISTOGRAM_BLUE_SIZE 0x802A -#define GL_HISTOGRAM_ALPHA_SIZE 0x802B -#define GL_HISTOGRAM_LUMINANCE_SIZE 0x802C -#define GL_HISTOGRAM_SINK 0x802D -#define GL_MINMAX 0x802E -#define GL_MINMAX_FORMAT 0x802F -#define GL_MINMAX_SINK 0x8030 -#define GL_TABLE_TOO_LARGE 0x8031 -#define GL_COLOR_MATRIX 0x80B1 -#define GL_COLOR_MATRIX_STACK_DEPTH 0x80B2 -#define GL_MAX_COLOR_MATRIX_STACK_DEPTH 0x80B3 -#define GL_POST_COLOR_MATRIX_RED_SCALE 0x80B4 -#define GL_POST_COLOR_MATRIX_GREEN_SCALE 0x80B5 -#define GL_POST_COLOR_MATRIX_BLUE_SCALE 0x80B6 -#define GL_POST_COLOR_MATRIX_ALPHA_SCALE 0x80B7 -#define GL_POST_COLOR_MATRIX_RED_BIAS 0x80B8 -#define GL_POST_COLOR_MATRIX_GREEN_BIAS 0x80B9 -#define GL_POST_COLOR_MATRIX_BLUE_BIAS 0x80BA -#define GL_POST_COLOR_MATRIX_ALPHA_BIAS 0x80BB -#define GL_COLOR_TABLE 0x80D0 -#define GL_POST_CONVOLUTION_COLOR_TABLE 0x80D1 -#define GL_POST_COLOR_MATRIX_COLOR_TABLE 0x80D2 -#define GL_PROXY_COLOR_TABLE 0x80D3 -#define GL_PROXY_POST_CONVOLUTION_COLOR_TABLE 0x80D4 -#define GL_PROXY_POST_COLOR_MATRIX_COLOR_TABLE 0x80D5 -#define GL_COLOR_TABLE_SCALE 0x80D6 -#define GL_COLOR_TABLE_BIAS 0x80D7 -#define GL_COLOR_TABLE_FORMAT 0x80D8 -#define GL_COLOR_TABLE_WIDTH 0x80D9 -#define GL_COLOR_TABLE_RED_SIZE 0x80DA -#define GL_COLOR_TABLE_GREEN_SIZE 0x80DB -#define GL_COLOR_TABLE_BLUE_SIZE 0x80DC -#define GL_COLOR_TABLE_ALPHA_SIZE 0x80DD -#define GL_COLOR_TABLE_LUMINANCE_SIZE 0x80DE -#define GL_COLOR_TABLE_INTENSITY_SIZE 0x80DF -#define GL_CONSTANT_BORDER 0x8151 -#define GL_REPLICATE_BORDER 0x8153 -#define GL_CONVOLUTION_BORDER_COLOR 0x8154 -typedef void (APIENTRYP PFNGLCOLORTABLEPROC) (GLenum target, GLenum internalformat, GLsizei width, GLenum format, GLenum type, const void *table); -typedef void (APIENTRYP PFNGLCOLORTABLEPARAMETERFVPROC) (GLenum target, GLenum pname, const GLfloat *params); -typedef void (APIENTRYP PFNGLCOLORTABLEPARAMETERIVPROC) (GLenum target, GLenum pname, const GLint *params); -typedef void (APIENTRYP PFNGLCOPYCOLORTABLEPROC) (GLenum target, GLenum internalformat, GLint x, GLint y, GLsizei width); -typedef void (APIENTRYP PFNGLGETCOLORTABLEPROC) (GLenum target, GLenum format, GLenum type, void *table); -typedef void (APIENTRYP PFNGLGETCOLORTABLEPARAMETERFVPROC) (GLenum target, GLenum pname, GLfloat *params); -typedef void (APIENTRYP PFNGLGETCOLORTABLEPARAMETERIVPROC) (GLenum target, GLenum pname, GLint *params); -typedef void (APIENTRYP PFNGLCOLORSUBTABLEPROC) (GLenum target, GLsizei start, GLsizei count, GLenum format, GLenum type, const void *data); -typedef void (APIENTRYP PFNGLCOPYCOLORSUBTABLEPROC) (GLenum target, GLsizei start, GLint x, GLint y, GLsizei width); -typedef void (APIENTRYP PFNGLCONVOLUTIONFILTER1DPROC) (GLenum target, GLenum internalformat, GLsizei width, GLenum format, GLenum type, const void *image); -typedef void (APIENTRYP PFNGLCONVOLUTIONFILTER2DPROC) (GLenum target, GLenum internalformat, GLsizei width, GLsizei height, GLenum format, GLenum type, const void *image); -typedef void (APIENTRYP PFNGLCONVOLUTIONPARAMETERFPROC) (GLenum target, GLenum pname, GLfloat params); -typedef void (APIENTRYP PFNGLCONVOLUTIONPARAMETERFVPROC) (GLenum target, GLenum pname, const GLfloat *params); -typedef void (APIENTRYP PFNGLCONVOLUTIONPARAMETERIPROC) (GLenum target, GLenum pname, GLint params); -typedef void (APIENTRYP PFNGLCONVOLUTIONPARAMETERIVPROC) (GLenum target, GLenum pname, const GLint *params); -typedef void (APIENTRYP PFNGLCOPYCONVOLUTIONFILTER1DPROC) (GLenum target, GLenum internalformat, GLint x, GLint y, GLsizei width); -typedef void (APIENTRYP PFNGLCOPYCONVOLUTIONFILTER2DPROC) (GLenum target, GLenum internalformat, GLint x, GLint y, GLsizei width, GLsizei height); -typedef void (APIENTRYP PFNGLGETCONVOLUTIONFILTERPROC) (GLenum target, GLenum format, GLenum type, void *image); -typedef void (APIENTRYP PFNGLGETCONVOLUTIONPARAMETERFVPROC) (GLenum target, GLenum pname, GLfloat *params); -typedef void (APIENTRYP PFNGLGETCONVOLUTIONPARAMETERIVPROC) (GLenum target, GLenum pname, GLint *params); -typedef void (APIENTRYP PFNGLGETSEPARABLEFILTERPROC) (GLenum target, GLenum format, GLenum type, void *row, void *column, void *span); -typedef void (APIENTRYP PFNGLSEPARABLEFILTER2DPROC) (GLenum target, GLenum internalformat, GLsizei width, GLsizei height, GLenum format, GLenum type, const void *row, const void *column); -typedef void (APIENTRYP PFNGLGETHISTOGRAMPROC) (GLenum target, GLboolean reset, GLenum format, GLenum type, void *values); -typedef void (APIENTRYP PFNGLGETHISTOGRAMPARAMETERFVPROC) (GLenum target, GLenum pname, GLfloat *params); -typedef void (APIENTRYP PFNGLGETHISTOGRAMPARAMETERIVPROC) (GLenum target, GLenum pname, GLint *params); -typedef void (APIENTRYP PFNGLGETMINMAXPROC) (GLenum target, GLboolean reset, GLenum format, GLenum type, void *values); -typedef void (APIENTRYP PFNGLGETMINMAXPARAMETERFVPROC) (GLenum target, GLenum pname, GLfloat *params); -typedef void (APIENTRYP PFNGLGETMINMAXPARAMETERIVPROC) (GLenum target, GLenum pname, GLint *params); -typedef void (APIENTRYP PFNGLHISTOGRAMPROC) (GLenum target, GLsizei width, GLenum internalformat, GLboolean sink); -typedef void (APIENTRYP PFNGLMINMAXPROC) (GLenum target, GLenum internalformat, GLboolean sink); -typedef void (APIENTRYP PFNGLRESETHISTOGRAMPROC) (GLenum target); -typedef void (APIENTRYP PFNGLRESETMINMAXPROC) (GLenum target); -#ifdef GL_GLEXT_PROTOTYPES -GLAPI void APIENTRY glColorTable (GLenum target, GLenum internalformat, GLsizei width, GLenum format, GLenum type, const void *table); -GLAPI void APIENTRY glColorTableParameterfv (GLenum target, GLenum pname, const GLfloat *params); -GLAPI void APIENTRY glColorTableParameteriv (GLenum target, GLenum pname, const GLint *params); -GLAPI void APIENTRY glCopyColorTable (GLenum target, GLenum internalformat, GLint x, GLint y, GLsizei width); -GLAPI void APIENTRY glGetColorTable (GLenum target, GLenum format, GLenum type, void *table); -GLAPI void APIENTRY glGetColorTableParameterfv (GLenum target, GLenum pname, GLfloat *params); -GLAPI void APIENTRY glGetColorTableParameteriv (GLenum target, GLenum pname, GLint *params); -GLAPI void APIENTRY glColorSubTable (GLenum target, GLsizei start, GLsizei count, GLenum format, GLenum type, const void *data); -GLAPI void APIENTRY glCopyColorSubTable (GLenum target, GLsizei start, GLint x, GLint y, GLsizei width); -GLAPI void APIENTRY glConvolutionFilter1D (GLenum target, GLenum internalformat, GLsizei width, GLenum format, GLenum type, const void *image); -GLAPI void APIENTRY glConvolutionFilter2D (GLenum target, GLenum internalformat, GLsizei width, GLsizei height, GLenum format, GLenum type, const void *image); -GLAPI void APIENTRY glConvolutionParameterf (GLenum target, GLenum pname, GLfloat params); -GLAPI void APIENTRY glConvolutionParameterfv (GLenum target, GLenum pname, const GLfloat *params); -GLAPI void APIENTRY glConvolutionParameteri (GLenum target, GLenum pname, GLint params); -GLAPI void APIENTRY glConvolutionParameteriv (GLenum target, GLenum pname, const GLint *params); -GLAPI void APIENTRY glCopyConvolutionFilter1D (GLenum target, GLenum internalformat, GLint x, GLint y, GLsizei width); -GLAPI void APIENTRY glCopyConvolutionFilter2D (GLenum target, GLenum internalformat, GLint x, GLint y, GLsizei width, GLsizei height); -GLAPI void APIENTRY glGetConvolutionFilter (GLenum target, GLenum format, GLenum type, void *image); -GLAPI void APIENTRY glGetConvolutionParameterfv (GLenum target, GLenum pname, GLfloat *params); -GLAPI void APIENTRY glGetConvolutionParameteriv (GLenum target, GLenum pname, GLint *params); -GLAPI void APIENTRY glGetSeparableFilter (GLenum target, GLenum format, GLenum type, void *row, void *column, void *span); -GLAPI void APIENTRY glSeparableFilter2D (GLenum target, GLenum internalformat, GLsizei width, GLsizei height, GLenum format, GLenum type, const void *row, const void *column); -GLAPI void APIENTRY glGetHistogram (GLenum target, GLboolean reset, GLenum format, GLenum type, void *values); -GLAPI void APIENTRY glGetHistogramParameterfv (GLenum target, GLenum pname, GLfloat *params); -GLAPI void APIENTRY glGetHistogramParameteriv (GLenum target, GLenum pname, GLint *params); -GLAPI void APIENTRY glGetMinmax (GLenum target, GLboolean reset, GLenum format, GLenum type, void *values); -GLAPI void APIENTRY glGetMinmaxParameterfv (GLenum target, GLenum pname, GLfloat *params); -GLAPI void APIENTRY glGetMinmaxParameteriv (GLenum target, GLenum pname, GLint *params); -GLAPI void APIENTRY glHistogram (GLenum target, GLsizei width, GLenum internalformat, GLboolean sink); -GLAPI void APIENTRY glMinmax (GLenum target, GLenum internalformat, GLboolean sink); -GLAPI void APIENTRY glResetHistogram (GLenum target); -GLAPI void APIENTRY glResetMinmax (GLenum target); -#endif -#endif /* GL_ARB_imaging */ - -#ifndef GL_ARB_indirect_parameters -#define GL_ARB_indirect_parameters 1 -#define GL_PARAMETER_BUFFER_ARB 0x80EE -#define GL_PARAMETER_BUFFER_BINDING_ARB 0x80EF -typedef void (APIENTRYP PFNGLMULTIDRAWARRAYSINDIRECTCOUNTARBPROC) (GLenum mode, GLintptr indirect, GLintptr drawcount, GLsizei maxdrawcount, GLsizei stride); -typedef void (APIENTRYP PFNGLMULTIDRAWELEMENTSINDIRECTCOUNTARBPROC) (GLenum mode, GLenum type, GLintptr indirect, GLintptr drawcount, GLsizei maxdrawcount, GLsizei stride); -#ifdef GL_GLEXT_PROTOTYPES -GLAPI void APIENTRY glMultiDrawArraysIndirectCountARB (GLenum mode, GLintptr indirect, GLintptr drawcount, GLsizei maxdrawcount, GLsizei stride); -GLAPI void APIENTRY glMultiDrawElementsIndirectCountARB (GLenum mode, GLenum type, GLintptr indirect, GLintptr drawcount, GLsizei maxdrawcount, GLsizei stride); -#endif -#endif /* GL_ARB_indirect_parameters */ - -#ifndef GL_ARB_instanced_arrays -#define GL_ARB_instanced_arrays 1 -#define GL_VERTEX_ATTRIB_ARRAY_DIVISOR_ARB 0x88FE -typedef void (APIENTRYP PFNGLVERTEXATTRIBDIVISORARBPROC) (GLuint index, GLuint divisor); -#ifdef GL_GLEXT_PROTOTYPES -GLAPI void APIENTRY glVertexAttribDivisorARB (GLuint index, GLuint divisor); -#endif -#endif /* GL_ARB_instanced_arrays */ - -#ifndef GL_ARB_internalformat_query -#define GL_ARB_internalformat_query 1 -#endif /* GL_ARB_internalformat_query */ - -#ifndef GL_ARB_internalformat_query2 -#define GL_ARB_internalformat_query2 1 -#define GL_SRGB_DECODE_ARB 0x8299 -#endif /* GL_ARB_internalformat_query2 */ - -#ifndef GL_ARB_invalidate_subdata -#define GL_ARB_invalidate_subdata 1 -#endif /* GL_ARB_invalidate_subdata */ - -#ifndef GL_ARB_map_buffer_alignment -#define GL_ARB_map_buffer_alignment 1 -#endif /* GL_ARB_map_buffer_alignment */ - -#ifndef GL_ARB_map_buffer_range -#define GL_ARB_map_buffer_range 1 -#endif /* GL_ARB_map_buffer_range */ - -#ifndef GL_ARB_matrix_palette -#define GL_ARB_matrix_palette 1 -#define GL_MATRIX_PALETTE_ARB 0x8840 -#define GL_MAX_MATRIX_PALETTE_STACK_DEPTH_ARB 0x8841 -#define GL_MAX_PALETTE_MATRICES_ARB 0x8842 -#define GL_CURRENT_PALETTE_MATRIX_ARB 0x8843 -#define GL_MATRIX_INDEX_ARRAY_ARB 0x8844 -#define GL_CURRENT_MATRIX_INDEX_ARB 0x8845 -#define GL_MATRIX_INDEX_ARRAY_SIZE_ARB 0x8846 -#define GL_MATRIX_INDEX_ARRAY_TYPE_ARB 0x8847 -#define GL_MATRIX_INDEX_ARRAY_STRIDE_ARB 0x8848 -#define GL_MATRIX_INDEX_ARRAY_POINTER_ARB 0x8849 -typedef void (APIENTRYP PFNGLCURRENTPALETTEMATRIXARBPROC) (GLint index); -typedef void (APIENTRYP PFNGLMATRIXINDEXUBVARBPROC) (GLint size, const GLubyte *indices); -typedef void (APIENTRYP PFNGLMATRIXINDEXUSVARBPROC) (GLint size, const GLushort *indices); -typedef void (APIENTRYP PFNGLMATRIXINDEXUIVARBPROC) (GLint size, const GLuint *indices); -typedef void (APIENTRYP PFNGLMATRIXINDEXPOINTERARBPROC) (GLint size, GLenum type, GLsizei stride, const void *pointer); -#ifdef GL_GLEXT_PROTOTYPES -GLAPI void APIENTRY glCurrentPaletteMatrixARB (GLint index); -GLAPI void APIENTRY glMatrixIndexubvARB (GLint size, const GLubyte *indices); -GLAPI void APIENTRY glMatrixIndexusvARB (GLint size, const GLushort *indices); -GLAPI void APIENTRY glMatrixIndexuivARB (GLint size, const GLuint *indices); -GLAPI void APIENTRY glMatrixIndexPointerARB (GLint size, GLenum type, GLsizei stride, const void *pointer); -#endif -#endif /* GL_ARB_matrix_palette */ - -#ifndef GL_ARB_multi_bind -#define GL_ARB_multi_bind 1 -#endif /* GL_ARB_multi_bind */ - -#ifndef GL_ARB_multi_draw_indirect -#define GL_ARB_multi_draw_indirect 1 -#endif /* GL_ARB_multi_draw_indirect */ - -#ifndef GL_ARB_multisample -#define GL_ARB_multisample 1 -#define GL_MULTISAMPLE_ARB 0x809D -#define GL_SAMPLE_ALPHA_TO_COVERAGE_ARB 0x809E -#define GL_SAMPLE_ALPHA_TO_ONE_ARB 0x809F -#define GL_SAMPLE_COVERAGE_ARB 0x80A0 -#define GL_SAMPLE_BUFFERS_ARB 0x80A8 -#define GL_SAMPLES_ARB 0x80A9 -#define GL_SAMPLE_COVERAGE_VALUE_ARB 0x80AA -#define GL_SAMPLE_COVERAGE_INVERT_ARB 0x80AB -#define GL_MULTISAMPLE_BIT_ARB 0x20000000 -typedef void (APIENTRYP PFNGLSAMPLECOVERAGEARBPROC) (GLfloat value, GLboolean invert); -#ifdef GL_GLEXT_PROTOTYPES -GLAPI void APIENTRY glSampleCoverageARB (GLfloat value, GLboolean invert); -#endif -#endif /* GL_ARB_multisample */ - -#ifndef GL_ARB_multitexture -#define GL_ARB_multitexture 1 -#define GL_TEXTURE0_ARB 0x84C0 -#define GL_TEXTURE1_ARB 0x84C1 -#define GL_TEXTURE2_ARB 0x84C2 -#define GL_TEXTURE3_ARB 0x84C3 -#define GL_TEXTURE4_ARB 0x84C4 -#define GL_TEXTURE5_ARB 0x84C5 -#define GL_TEXTURE6_ARB 0x84C6 -#define GL_TEXTURE7_ARB 0x84C7 -#define GL_TEXTURE8_ARB 0x84C8 -#define GL_TEXTURE9_ARB 0x84C9 -#define GL_TEXTURE10_ARB 0x84CA -#define GL_TEXTURE11_ARB 0x84CB -#define GL_TEXTURE12_ARB 0x84CC -#define GL_TEXTURE13_ARB 0x84CD -#define GL_TEXTURE14_ARB 0x84CE -#define GL_TEXTURE15_ARB 0x84CF -#define GL_TEXTURE16_ARB 0x84D0 -#define GL_TEXTURE17_ARB 0x84D1 -#define GL_TEXTURE18_ARB 0x84D2 -#define GL_TEXTURE19_ARB 0x84D3 -#define GL_TEXTURE20_ARB 0x84D4 -#define GL_TEXTURE21_ARB 0x84D5 -#define GL_TEXTURE22_ARB 0x84D6 -#define GL_TEXTURE23_ARB 0x84D7 -#define GL_TEXTURE24_ARB 0x84D8 -#define GL_TEXTURE25_ARB 0x84D9 -#define GL_TEXTURE26_ARB 0x84DA -#define GL_TEXTURE27_ARB 0x84DB -#define GL_TEXTURE28_ARB 0x84DC -#define GL_TEXTURE29_ARB 0x84DD -#define GL_TEXTURE30_ARB 0x84DE -#define GL_TEXTURE31_ARB 0x84DF -#define GL_ACTIVE_TEXTURE_ARB 0x84E0 -#define GL_CLIENT_ACTIVE_TEXTURE_ARB 0x84E1 -#define GL_MAX_TEXTURE_UNITS_ARB 0x84E2 -typedef void (APIENTRYP PFNGLACTIVETEXTUREARBPROC) (GLenum texture); -typedef void (APIENTRYP PFNGLCLIENTACTIVETEXTUREARBPROC) (GLenum texture); -typedef void (APIENTRYP PFNGLMULTITEXCOORD1DARBPROC) (GLenum target, GLdouble s); -typedef void (APIENTRYP PFNGLMULTITEXCOORD1DVARBPROC) (GLenum target, const GLdouble *v); -typedef void (APIENTRYP PFNGLMULTITEXCOORD1FARBPROC) (GLenum target, GLfloat s); -typedef void (APIENTRYP PFNGLMULTITEXCOORD1FVARBPROC) (GLenum target, const GLfloat *v); -typedef void (APIENTRYP PFNGLMULTITEXCOORD1IARBPROC) (GLenum target, GLint s); -typedef void (APIENTRYP PFNGLMULTITEXCOORD1IVARBPROC) (GLenum target, const GLint *v); -typedef void (APIENTRYP PFNGLMULTITEXCOORD1SARBPROC) (GLenum target, GLshort s); -typedef void (APIENTRYP PFNGLMULTITEXCOORD1SVARBPROC) (GLenum target, const GLshort *v); -typedef void (APIENTRYP PFNGLMULTITEXCOORD2DARBPROC) (GLenum target, GLdouble s, GLdouble t); -typedef void (APIENTRYP PFNGLMULTITEXCOORD2DVARBPROC) (GLenum target, const GLdouble *v); -typedef void (APIENTRYP PFNGLMULTITEXCOORD2FARBPROC) (GLenum target, GLfloat s, GLfloat t); -typedef void (APIENTRYP PFNGLMULTITEXCOORD2FVARBPROC) (GLenum target, const GLfloat *v); -typedef void (APIENTRYP PFNGLMULTITEXCOORD2IARBPROC) (GLenum target, GLint s, GLint t); -typedef void (APIENTRYP PFNGLMULTITEXCOORD2IVARBPROC) (GLenum target, const GLint *v); -typedef void (APIENTRYP PFNGLMULTITEXCOORD2SARBPROC) (GLenum target, GLshort s, GLshort t); -typedef void (APIENTRYP PFNGLMULTITEXCOORD2SVARBPROC) (GLenum target, const GLshort *v); -typedef void (APIENTRYP PFNGLMULTITEXCOORD3DARBPROC) (GLenum target, GLdouble s, GLdouble t, GLdouble r); -typedef void (APIENTRYP PFNGLMULTITEXCOORD3DVARBPROC) (GLenum target, const GLdouble *v); -typedef void (APIENTRYP PFNGLMULTITEXCOORD3FARBPROC) (GLenum target, GLfloat s, GLfloat t, GLfloat r); -typedef void (APIENTRYP PFNGLMULTITEXCOORD3FVARBPROC) (GLenum target, const GLfloat *v); -typedef void (APIENTRYP PFNGLMULTITEXCOORD3IARBPROC) (GLenum target, GLint s, GLint t, GLint r); -typedef void (APIENTRYP PFNGLMULTITEXCOORD3IVARBPROC) (GLenum target, const GLint *v); -typedef void (APIENTRYP PFNGLMULTITEXCOORD3SARBPROC) (GLenum target, GLshort s, GLshort t, GLshort r); -typedef void (APIENTRYP PFNGLMULTITEXCOORD3SVARBPROC) (GLenum target, const GLshort *v); -typedef void (APIENTRYP PFNGLMULTITEXCOORD4DARBPROC) (GLenum target, GLdouble s, GLdouble t, GLdouble r, GLdouble q); -typedef void (APIENTRYP PFNGLMULTITEXCOORD4DVARBPROC) (GLenum target, const GLdouble *v); -typedef void (APIENTRYP PFNGLMULTITEXCOORD4FARBPROC) (GLenum target, GLfloat s, GLfloat t, GLfloat r, GLfloat q); -typedef void (APIENTRYP PFNGLMULTITEXCOORD4FVARBPROC) (GLenum target, const GLfloat *v); -typedef void (APIENTRYP PFNGLMULTITEXCOORD4IARBPROC) (GLenum target, GLint s, GLint t, GLint r, GLint q); -typedef void (APIENTRYP PFNGLMULTITEXCOORD4IVARBPROC) (GLenum target, const GLint *v); -typedef void (APIENTRYP PFNGLMULTITEXCOORD4SARBPROC) (GLenum target, GLshort s, GLshort t, GLshort r, GLshort q); -typedef void (APIENTRYP PFNGLMULTITEXCOORD4SVARBPROC) (GLenum target, const GLshort *v); -#ifdef GL_GLEXT_PROTOTYPES -GLAPI void APIENTRY glActiveTextureARB (GLenum texture); -GLAPI void APIENTRY glClientActiveTextureARB (GLenum texture); -GLAPI void APIENTRY glMultiTexCoord1dARB (GLenum target, GLdouble s); -GLAPI void APIENTRY glMultiTexCoord1dvARB (GLenum target, const GLdouble *v); -GLAPI void APIENTRY glMultiTexCoord1fARB (GLenum target, GLfloat s); -GLAPI void APIENTRY glMultiTexCoord1fvARB (GLenum target, const GLfloat *v); -GLAPI void APIENTRY glMultiTexCoord1iARB (GLenum target, GLint s); -GLAPI void APIENTRY glMultiTexCoord1ivARB (GLenum target, const GLint *v); -GLAPI void APIENTRY glMultiTexCoord1sARB (GLenum target, GLshort s); -GLAPI void APIENTRY glMultiTexCoord1svARB (GLenum target, const GLshort *v); -GLAPI void APIENTRY glMultiTexCoord2dARB (GLenum target, GLdouble s, GLdouble t); -GLAPI void APIENTRY glMultiTexCoord2dvARB (GLenum target, const GLdouble *v); -GLAPI void APIENTRY glMultiTexCoord2fARB (GLenum target, GLfloat s, GLfloat t); -GLAPI void APIENTRY glMultiTexCoord2fvARB (GLenum target, const GLfloat *v); -GLAPI void APIENTRY glMultiTexCoord2iARB (GLenum target, GLint s, GLint t); -GLAPI void APIENTRY glMultiTexCoord2ivARB (GLenum target, const GLint *v); -GLAPI void APIENTRY glMultiTexCoord2sARB (GLenum target, GLshort s, GLshort t); -GLAPI void APIENTRY glMultiTexCoord2svARB (GLenum target, const GLshort *v); -GLAPI void APIENTRY glMultiTexCoord3dARB (GLenum target, GLdouble s, GLdouble t, GLdouble r); -GLAPI void APIENTRY glMultiTexCoord3dvARB (GLenum target, const GLdouble *v); -GLAPI void APIENTRY glMultiTexCoord3fARB (GLenum target, GLfloat s, GLfloat t, GLfloat r); -GLAPI void APIENTRY glMultiTexCoord3fvARB (GLenum target, const GLfloat *v); -GLAPI void APIENTRY glMultiTexCoord3iARB (GLenum target, GLint s, GLint t, GLint r); -GLAPI void APIENTRY glMultiTexCoord3ivARB (GLenum target, const GLint *v); -GLAPI void APIENTRY glMultiTexCoord3sARB (GLenum target, GLshort s, GLshort t, GLshort r); -GLAPI void APIENTRY glMultiTexCoord3svARB (GLenum target, const GLshort *v); -GLAPI void APIENTRY glMultiTexCoord4dARB (GLenum target, GLdouble s, GLdouble t, GLdouble r, GLdouble q); -GLAPI void APIENTRY glMultiTexCoord4dvARB (GLenum target, const GLdouble *v); -GLAPI void APIENTRY glMultiTexCoord4fARB (GLenum target, GLfloat s, GLfloat t, GLfloat r, GLfloat q); -GLAPI void APIENTRY glMultiTexCoord4fvARB (GLenum target, const GLfloat *v); -GLAPI void APIENTRY glMultiTexCoord4iARB (GLenum target, GLint s, GLint t, GLint r, GLint q); -GLAPI void APIENTRY glMultiTexCoord4ivARB (GLenum target, const GLint *v); -GLAPI void APIENTRY glMultiTexCoord4sARB (GLenum target, GLshort s, GLshort t, GLshort r, GLshort q); -GLAPI void APIENTRY glMultiTexCoord4svARB (GLenum target, const GLshort *v); -#endif -#endif /* GL_ARB_multitexture */ - -#ifndef GL_ARB_occlusion_query -#define GL_ARB_occlusion_query 1 -#define GL_QUERY_COUNTER_BITS_ARB 0x8864 -#define GL_CURRENT_QUERY_ARB 0x8865 -#define GL_QUERY_RESULT_ARB 0x8866 -#define GL_QUERY_RESULT_AVAILABLE_ARB 0x8867 -#define GL_SAMPLES_PASSED_ARB 0x8914 -typedef void (APIENTRYP PFNGLGENQUERIESARBPROC) (GLsizei n, GLuint *ids); -typedef void (APIENTRYP PFNGLDELETEQUERIESARBPROC) (GLsizei n, const GLuint *ids); -typedef GLboolean (APIENTRYP PFNGLISQUERYARBPROC) (GLuint id); -typedef void (APIENTRYP PFNGLBEGINQUERYARBPROC) (GLenum target, GLuint id); -typedef void (APIENTRYP PFNGLENDQUERYARBPROC) (GLenum target); -typedef void (APIENTRYP PFNGLGETQUERYIVARBPROC) (GLenum target, GLenum pname, GLint *params); -typedef void (APIENTRYP PFNGLGETQUERYOBJECTIVARBPROC) (GLuint id, GLenum pname, GLint *params); -typedef void (APIENTRYP PFNGLGETQUERYOBJECTUIVARBPROC) (GLuint id, GLenum pname, GLuint *params); -#ifdef GL_GLEXT_PROTOTYPES -GLAPI void APIENTRY glGenQueriesARB (GLsizei n, GLuint *ids); -GLAPI void APIENTRY glDeleteQueriesARB (GLsizei n, const GLuint *ids); -GLAPI GLboolean APIENTRY glIsQueryARB (GLuint id); -GLAPI void APIENTRY glBeginQueryARB (GLenum target, GLuint id); -GLAPI void APIENTRY glEndQueryARB (GLenum target); -GLAPI void APIENTRY glGetQueryivARB (GLenum target, GLenum pname, GLint *params); -GLAPI void APIENTRY glGetQueryObjectivARB (GLuint id, GLenum pname, GLint *params); -GLAPI void APIENTRY glGetQueryObjectuivARB (GLuint id, GLenum pname, GLuint *params); -#endif -#endif /* GL_ARB_occlusion_query */ - -#ifndef GL_ARB_occlusion_query2 -#define GL_ARB_occlusion_query2 1 -#endif /* GL_ARB_occlusion_query2 */ - -#ifndef GL_ARB_pixel_buffer_object -#define GL_ARB_pixel_buffer_object 1 -#define GL_PIXEL_PACK_BUFFER_ARB 0x88EB -#define GL_PIXEL_UNPACK_BUFFER_ARB 0x88EC -#define GL_PIXEL_PACK_BUFFER_BINDING_ARB 0x88ED -#define GL_PIXEL_UNPACK_BUFFER_BINDING_ARB 0x88EF -#endif /* GL_ARB_pixel_buffer_object */ - -#ifndef GL_ARB_point_parameters -#define GL_ARB_point_parameters 1 -#define GL_POINT_SIZE_MIN_ARB 0x8126 -#define GL_POINT_SIZE_MAX_ARB 0x8127 -#define GL_POINT_FADE_THRESHOLD_SIZE_ARB 0x8128 -#define GL_POINT_DISTANCE_ATTENUATION_ARB 0x8129 -typedef void (APIENTRYP PFNGLPOINTPARAMETERFARBPROC) (GLenum pname, GLfloat param); -typedef void (APIENTRYP PFNGLPOINTPARAMETERFVARBPROC) (GLenum pname, const GLfloat *params); -#ifdef GL_GLEXT_PROTOTYPES -GLAPI void APIENTRY glPointParameterfARB (GLenum pname, GLfloat param); -GLAPI void APIENTRY glPointParameterfvARB (GLenum pname, const GLfloat *params); -#endif -#endif /* GL_ARB_point_parameters */ - -#ifndef GL_ARB_point_sprite -#define GL_ARB_point_sprite 1 -#define GL_POINT_SPRITE_ARB 0x8861 -#define GL_COORD_REPLACE_ARB 0x8862 -#endif /* GL_ARB_point_sprite */ - -#ifndef GL_ARB_program_interface_query -#define GL_ARB_program_interface_query 1 -#endif /* GL_ARB_program_interface_query */ - -#ifndef GL_ARB_provoking_vertex -#define GL_ARB_provoking_vertex 1 -#endif /* GL_ARB_provoking_vertex */ - -#ifndef GL_ARB_query_buffer_object -#define GL_ARB_query_buffer_object 1 -#endif /* GL_ARB_query_buffer_object */ - -#ifndef GL_ARB_robust_buffer_access_behavior -#define GL_ARB_robust_buffer_access_behavior 1 -#endif /* GL_ARB_robust_buffer_access_behavior */ - -#ifndef GL_ARB_robustness -#define GL_ARB_robustness 1 -#define GL_CONTEXT_FLAG_ROBUST_ACCESS_BIT_ARB 0x00000004 -#define GL_LOSE_CONTEXT_ON_RESET_ARB 0x8252 -#define GL_GUILTY_CONTEXT_RESET_ARB 0x8253 -#define GL_INNOCENT_CONTEXT_RESET_ARB 0x8254 -#define GL_UNKNOWN_CONTEXT_RESET_ARB 0x8255 -#define GL_RESET_NOTIFICATION_STRATEGY_ARB 0x8256 -#define GL_NO_RESET_NOTIFICATION_ARB 0x8261 -typedef GLenum (APIENTRYP PFNGLGETGRAPHICSRESETSTATUSARBPROC) (void); -typedef void (APIENTRYP PFNGLGETNTEXIMAGEARBPROC) (GLenum target, GLint level, GLenum format, GLenum type, GLsizei bufSize, void *img); -typedef void (APIENTRYP PFNGLREADNPIXELSARBPROC) (GLint x, GLint y, GLsizei width, GLsizei height, GLenum format, GLenum type, GLsizei bufSize, void *data); -typedef void (APIENTRYP PFNGLGETNCOMPRESSEDTEXIMAGEARBPROC) (GLenum target, GLint lod, GLsizei bufSize, void *img); -typedef void (APIENTRYP PFNGLGETNUNIFORMFVARBPROC) (GLuint program, GLint location, GLsizei bufSize, GLfloat *params); -typedef void (APIENTRYP PFNGLGETNUNIFORMIVARBPROC) (GLuint program, GLint location, GLsizei bufSize, GLint *params); -typedef void (APIENTRYP PFNGLGETNUNIFORMUIVARBPROC) (GLuint program, GLint location, GLsizei bufSize, GLuint *params); -typedef void (APIENTRYP PFNGLGETNUNIFORMDVARBPROC) (GLuint program, GLint location, GLsizei bufSize, GLdouble *params); -typedef void (APIENTRYP PFNGLGETNMAPDVARBPROC) (GLenum target, GLenum query, GLsizei bufSize, GLdouble *v); -typedef void (APIENTRYP PFNGLGETNMAPFVARBPROC) (GLenum target, GLenum query, GLsizei bufSize, GLfloat *v); -typedef void (APIENTRYP PFNGLGETNMAPIVARBPROC) (GLenum target, GLenum query, GLsizei bufSize, GLint *v); -typedef void (APIENTRYP PFNGLGETNPIXELMAPFVARBPROC) (GLenum map, GLsizei bufSize, GLfloat *values); -typedef void (APIENTRYP PFNGLGETNPIXELMAPUIVARBPROC) (GLenum map, GLsizei bufSize, GLuint *values); -typedef void (APIENTRYP PFNGLGETNPIXELMAPUSVARBPROC) (GLenum map, GLsizei bufSize, GLushort *values); -typedef void (APIENTRYP PFNGLGETNPOLYGONSTIPPLEARBPROC) (GLsizei bufSize, GLubyte *pattern); -typedef void (APIENTRYP PFNGLGETNCOLORTABLEARBPROC) (GLenum target, GLenum format, GLenum type, GLsizei bufSize, void *table); -typedef void (APIENTRYP PFNGLGETNCONVOLUTIONFILTERARBPROC) (GLenum target, GLenum format, GLenum type, GLsizei bufSize, void *image); -typedef void (APIENTRYP PFNGLGETNSEPARABLEFILTERARBPROC) (GLenum target, GLenum format, GLenum type, GLsizei rowBufSize, void *row, GLsizei columnBufSize, void *column, void *span); -typedef void (APIENTRYP PFNGLGETNHISTOGRAMARBPROC) (GLenum target, GLboolean reset, GLenum format, GLenum type, GLsizei bufSize, void *values); -typedef void (APIENTRYP PFNGLGETNMINMAXARBPROC) (GLenum target, GLboolean reset, GLenum format, GLenum type, GLsizei bufSize, void *values); -#ifdef GL_GLEXT_PROTOTYPES -GLAPI GLenum APIENTRY glGetGraphicsResetStatusARB (void); -GLAPI void APIENTRY glGetnTexImageARB (GLenum target, GLint level, GLenum format, GLenum type, GLsizei bufSize, void *img); -GLAPI void APIENTRY glReadnPixelsARB (GLint x, GLint y, GLsizei width, GLsizei height, GLenum format, GLenum type, GLsizei bufSize, void *data); -GLAPI void APIENTRY glGetnCompressedTexImageARB (GLenum target, GLint lod, GLsizei bufSize, void *img); -GLAPI void APIENTRY glGetnUniformfvARB (GLuint program, GLint location, GLsizei bufSize, GLfloat *params); -GLAPI void APIENTRY glGetnUniformivARB (GLuint program, GLint location, GLsizei bufSize, GLint *params); -GLAPI void APIENTRY glGetnUniformuivARB (GLuint program, GLint location, GLsizei bufSize, GLuint *params); -GLAPI void APIENTRY glGetnUniformdvARB (GLuint program, GLint location, GLsizei bufSize, GLdouble *params); -GLAPI void APIENTRY glGetnMapdvARB (GLenum target, GLenum query, GLsizei bufSize, GLdouble *v); -GLAPI void APIENTRY glGetnMapfvARB (GLenum target, GLenum query, GLsizei bufSize, GLfloat *v); -GLAPI void APIENTRY glGetnMapivARB (GLenum target, GLenum query, GLsizei bufSize, GLint *v); -GLAPI void APIENTRY glGetnPixelMapfvARB (GLenum map, GLsizei bufSize, GLfloat *values); -GLAPI void APIENTRY glGetnPixelMapuivARB (GLenum map, GLsizei bufSize, GLuint *values); -GLAPI void APIENTRY glGetnPixelMapusvARB (GLenum map, GLsizei bufSize, GLushort *values); -GLAPI void APIENTRY glGetnPolygonStippleARB (GLsizei bufSize, GLubyte *pattern); -GLAPI void APIENTRY glGetnColorTableARB (GLenum target, GLenum format, GLenum type, GLsizei bufSize, void *table); -GLAPI void APIENTRY glGetnConvolutionFilterARB (GLenum target, GLenum format, GLenum type, GLsizei bufSize, void *image); -GLAPI void APIENTRY glGetnSeparableFilterARB (GLenum target, GLenum format, GLenum type, GLsizei rowBufSize, void *row, GLsizei columnBufSize, void *column, void *span); -GLAPI void APIENTRY glGetnHistogramARB (GLenum target, GLboolean reset, GLenum format, GLenum type, GLsizei bufSize, void *values); -GLAPI void APIENTRY glGetnMinmaxARB (GLenum target, GLboolean reset, GLenum format, GLenum type, GLsizei bufSize, void *values); -#endif -#endif /* GL_ARB_robustness */ - -#ifndef GL_ARB_robustness_isolation -#define GL_ARB_robustness_isolation 1 -#endif /* GL_ARB_robustness_isolation */ - -#ifndef GL_ARB_sample_shading -#define GL_ARB_sample_shading 1 -#define GL_SAMPLE_SHADING_ARB 0x8C36 -#define GL_MIN_SAMPLE_SHADING_VALUE_ARB 0x8C37 -typedef void (APIENTRYP PFNGLMINSAMPLESHADINGARBPROC) (GLfloat value); -#ifdef GL_GLEXT_PROTOTYPES -GLAPI void APIENTRY glMinSampleShadingARB (GLfloat value); -#endif -#endif /* GL_ARB_sample_shading */ - -#ifndef GL_ARB_sampler_objects -#define GL_ARB_sampler_objects 1 -#endif /* GL_ARB_sampler_objects */ - -#ifndef GL_ARB_seamless_cube_map -#define GL_ARB_seamless_cube_map 1 -#endif /* GL_ARB_seamless_cube_map */ - -#ifndef GL_ARB_seamless_cubemap_per_texture -#define GL_ARB_seamless_cubemap_per_texture 1 -#endif /* GL_ARB_seamless_cubemap_per_texture */ - -#ifndef GL_ARB_separate_shader_objects -#define GL_ARB_separate_shader_objects 1 -#endif /* GL_ARB_separate_shader_objects */ - -#ifndef GL_ARB_shader_atomic_counters -#define GL_ARB_shader_atomic_counters 1 -#endif /* GL_ARB_shader_atomic_counters */ - -#ifndef GL_ARB_shader_bit_encoding -#define GL_ARB_shader_bit_encoding 1 -#endif /* GL_ARB_shader_bit_encoding */ - -#ifndef GL_ARB_shader_draw_parameters -#define GL_ARB_shader_draw_parameters 1 -#endif /* GL_ARB_shader_draw_parameters */ - -#ifndef GL_ARB_shader_group_vote -#define GL_ARB_shader_group_vote 1 -#endif /* GL_ARB_shader_group_vote */ - -#ifndef GL_ARB_shader_image_load_store -#define GL_ARB_shader_image_load_store 1 -#endif /* GL_ARB_shader_image_load_store */ - -#ifndef GL_ARB_shader_image_size -#define GL_ARB_shader_image_size 1 -#endif /* GL_ARB_shader_image_size */ - -#ifndef GL_ARB_shader_objects -#define GL_ARB_shader_objects 1 -#ifdef __APPLE__ -typedef void *GLhandleARB; -#else -typedef unsigned int GLhandleARB; -#endif -typedef char GLcharARB; -#define GL_PROGRAM_OBJECT_ARB 0x8B40 -#define GL_SHADER_OBJECT_ARB 0x8B48 -#define GL_OBJECT_TYPE_ARB 0x8B4E -#define GL_OBJECT_SUBTYPE_ARB 0x8B4F -#define GL_FLOAT_VEC2_ARB 0x8B50 -#define GL_FLOAT_VEC3_ARB 0x8B51 -#define GL_FLOAT_VEC4_ARB 0x8B52 -#define GL_INT_VEC2_ARB 0x8B53 -#define GL_INT_VEC3_ARB 0x8B54 -#define GL_INT_VEC4_ARB 0x8B55 -#define GL_BOOL_ARB 0x8B56 -#define GL_BOOL_VEC2_ARB 0x8B57 -#define GL_BOOL_VEC3_ARB 0x8B58 -#define GL_BOOL_VEC4_ARB 0x8B59 -#define GL_FLOAT_MAT2_ARB 0x8B5A -#define GL_FLOAT_MAT3_ARB 0x8B5B -#define GL_FLOAT_MAT4_ARB 0x8B5C -#define GL_SAMPLER_1D_ARB 0x8B5D -#define GL_SAMPLER_2D_ARB 0x8B5E -#define GL_SAMPLER_3D_ARB 0x8B5F -#define GL_SAMPLER_CUBE_ARB 0x8B60 -#define GL_SAMPLER_1D_SHADOW_ARB 0x8B61 -#define GL_SAMPLER_2D_SHADOW_ARB 0x8B62 -#define GL_SAMPLER_2D_RECT_ARB 0x8B63 -#define GL_SAMPLER_2D_RECT_SHADOW_ARB 0x8B64 -#define GL_OBJECT_DELETE_STATUS_ARB 0x8B80 -#define GL_OBJECT_COMPILE_STATUS_ARB 0x8B81 -#define GL_OBJECT_LINK_STATUS_ARB 0x8B82 -#define GL_OBJECT_VALIDATE_STATUS_ARB 0x8B83 -#define GL_OBJECT_INFO_LOG_LENGTH_ARB 0x8B84 -#define GL_OBJECT_ATTACHED_OBJECTS_ARB 0x8B85 -#define GL_OBJECT_ACTIVE_UNIFORMS_ARB 0x8B86 -#define GL_OBJECT_ACTIVE_UNIFORM_MAX_LENGTH_ARB 0x8B87 -#define GL_OBJECT_SHADER_SOURCE_LENGTH_ARB 0x8B88 -typedef void (APIENTRYP PFNGLDELETEOBJECTARBPROC) (GLhandleARB obj); -typedef GLhandleARB (APIENTRYP PFNGLGETHANDLEARBPROC) (GLenum pname); -typedef void (APIENTRYP PFNGLDETACHOBJECTARBPROC) (GLhandleARB containerObj, GLhandleARB attachedObj); -typedef GLhandleARB (APIENTRYP PFNGLCREATESHADEROBJECTARBPROC) (GLenum shaderType); -typedef void (APIENTRYP PFNGLSHADERSOURCEARBPROC) (GLhandleARB shaderObj, GLsizei count, const GLcharARB **string, const GLint *length); -typedef void (APIENTRYP PFNGLCOMPILESHADERARBPROC) (GLhandleARB shaderObj); -typedef GLhandleARB (APIENTRYP PFNGLCREATEPROGRAMOBJECTARBPROC) (void); -typedef void (APIENTRYP PFNGLATTACHOBJECTARBPROC) (GLhandleARB containerObj, GLhandleARB obj); -typedef void (APIENTRYP PFNGLLINKPROGRAMARBPROC) (GLhandleARB programObj); -typedef void (APIENTRYP PFNGLUSEPROGRAMOBJECTARBPROC) (GLhandleARB programObj); -typedef void (APIENTRYP PFNGLVALIDATEPROGRAMARBPROC) (GLhandleARB programObj); -typedef void (APIENTRYP PFNGLUNIFORM1FARBPROC) (GLint location, GLfloat v0); -typedef void (APIENTRYP PFNGLUNIFORM2FARBPROC) (GLint location, GLfloat v0, GLfloat v1); -typedef void (APIENTRYP PFNGLUNIFORM3FARBPROC) (GLint location, GLfloat v0, GLfloat v1, GLfloat v2); -typedef void (APIENTRYP PFNGLUNIFORM4FARBPROC) (GLint location, GLfloat v0, GLfloat v1, GLfloat v2, GLfloat v3); -typedef void (APIENTRYP PFNGLUNIFORM1IARBPROC) (GLint location, GLint v0); -typedef void (APIENTRYP PFNGLUNIFORM2IARBPROC) (GLint location, GLint v0, GLint v1); -typedef void (APIENTRYP PFNGLUNIFORM3IARBPROC) (GLint location, GLint v0, GLint v1, GLint v2); -typedef void (APIENTRYP PFNGLUNIFORM4IARBPROC) (GLint location, GLint v0, GLint v1, GLint v2, GLint v3); -typedef void (APIENTRYP PFNGLUNIFORM1FVARBPROC) (GLint location, GLsizei count, const GLfloat *value); -typedef void (APIENTRYP PFNGLUNIFORM2FVARBPROC) (GLint location, GLsizei count, const GLfloat *value); -typedef void (APIENTRYP PFNGLUNIFORM3FVARBPROC) (GLint location, GLsizei count, const GLfloat *value); -typedef void (APIENTRYP PFNGLUNIFORM4FVARBPROC) (GLint location, GLsizei count, const GLfloat *value); -typedef void (APIENTRYP PFNGLUNIFORM1IVARBPROC) (GLint location, GLsizei count, const GLint *value); -typedef void (APIENTRYP PFNGLUNIFORM2IVARBPROC) (GLint location, GLsizei count, const GLint *value); -typedef void (APIENTRYP PFNGLUNIFORM3IVARBPROC) (GLint location, GLsizei count, const GLint *value); -typedef void (APIENTRYP PFNGLUNIFORM4IVARBPROC) (GLint location, GLsizei count, const GLint *value); -typedef void (APIENTRYP PFNGLUNIFORMMATRIX2FVARBPROC) (GLint location, GLsizei count, GLboolean transpose, const GLfloat *value); -typedef void (APIENTRYP PFNGLUNIFORMMATRIX3FVARBPROC) (GLint location, GLsizei count, GLboolean transpose, const GLfloat *value); -typedef void (APIENTRYP PFNGLUNIFORMMATRIX4FVARBPROC) (GLint location, GLsizei count, GLboolean transpose, const GLfloat *value); -typedef void (APIENTRYP PFNGLGETOBJECTPARAMETERFVARBPROC) (GLhandleARB obj, GLenum pname, GLfloat *params); -typedef void (APIENTRYP PFNGLGETOBJECTPARAMETERIVARBPROC) (GLhandleARB obj, GLenum pname, GLint *params); -typedef void (APIENTRYP PFNGLGETINFOLOGARBPROC) (GLhandleARB obj, GLsizei maxLength, GLsizei *length, GLcharARB *infoLog); -typedef void (APIENTRYP PFNGLGETATTACHEDOBJECTSARBPROC) (GLhandleARB containerObj, GLsizei maxCount, GLsizei *count, GLhandleARB *obj); -typedef GLint (APIENTRYP PFNGLGETUNIFORMLOCATIONARBPROC) (GLhandleARB programObj, const GLcharARB *name); -typedef void (APIENTRYP PFNGLGETACTIVEUNIFORMARBPROC) (GLhandleARB programObj, GLuint index, GLsizei maxLength, GLsizei *length, GLint *size, GLenum *type, GLcharARB *name); -typedef void (APIENTRYP PFNGLGETUNIFORMFVARBPROC) (GLhandleARB programObj, GLint location, GLfloat *params); -typedef void (APIENTRYP PFNGLGETUNIFORMIVARBPROC) (GLhandleARB programObj, GLint location, GLint *params); -typedef void (APIENTRYP PFNGLGETSHADERSOURCEARBPROC) (GLhandleARB obj, GLsizei maxLength, GLsizei *length, GLcharARB *source); -#ifdef GL_GLEXT_PROTOTYPES -GLAPI void APIENTRY glDeleteObjectARB (GLhandleARB obj); -GLAPI GLhandleARB APIENTRY glGetHandleARB (GLenum pname); -GLAPI void APIENTRY glDetachObjectARB (GLhandleARB containerObj, GLhandleARB attachedObj); -GLAPI GLhandleARB APIENTRY glCreateShaderObjectARB (GLenum shaderType); -GLAPI void APIENTRY glShaderSourceARB (GLhandleARB shaderObj, GLsizei count, const GLcharARB **string, const GLint *length); -GLAPI void APIENTRY glCompileShaderARB (GLhandleARB shaderObj); -GLAPI GLhandleARB APIENTRY glCreateProgramObjectARB (void); -GLAPI void APIENTRY glAttachObjectARB (GLhandleARB containerObj, GLhandleARB obj); -GLAPI void APIENTRY glLinkProgramARB (GLhandleARB programObj); -GLAPI void APIENTRY glUseProgramObjectARB (GLhandleARB programObj); -GLAPI void APIENTRY glValidateProgramARB (GLhandleARB programObj); -GLAPI void APIENTRY glUniform1fARB (GLint location, GLfloat v0); -GLAPI void APIENTRY glUniform2fARB (GLint location, GLfloat v0, GLfloat v1); -GLAPI void APIENTRY glUniform3fARB (GLint location, GLfloat v0, GLfloat v1, GLfloat v2); -GLAPI void APIENTRY glUniform4fARB (GLint location, GLfloat v0, GLfloat v1, GLfloat v2, GLfloat v3); -GLAPI void APIENTRY glUniform1iARB (GLint location, GLint v0); -GLAPI void APIENTRY glUniform2iARB (GLint location, GLint v0, GLint v1); -GLAPI void APIENTRY glUniform3iARB (GLint location, GLint v0, GLint v1, GLint v2); -GLAPI void APIENTRY glUniform4iARB (GLint location, GLint v0, GLint v1, GLint v2, GLint v3); -GLAPI void APIENTRY glUniform1fvARB (GLint location, GLsizei count, const GLfloat *value); -GLAPI void APIENTRY glUniform2fvARB (GLint location, GLsizei count, const GLfloat *value); -GLAPI void APIENTRY glUniform3fvARB (GLint location, GLsizei count, const GLfloat *value); -GLAPI void APIENTRY glUniform4fvARB (GLint location, GLsizei count, const GLfloat *value); -GLAPI void APIENTRY glUniform1ivARB (GLint location, GLsizei count, const GLint *value); -GLAPI void APIENTRY glUniform2ivARB (GLint location, GLsizei count, const GLint *value); -GLAPI void APIENTRY glUniform3ivARB (GLint location, GLsizei count, const GLint *value); -GLAPI void APIENTRY glUniform4ivARB (GLint location, GLsizei count, const GLint *value); -GLAPI void APIENTRY glUniformMatrix2fvARB (GLint location, GLsizei count, GLboolean transpose, const GLfloat *value); -GLAPI void APIENTRY glUniformMatrix3fvARB (GLint location, GLsizei count, GLboolean transpose, const GLfloat *value); -GLAPI void APIENTRY glUniformMatrix4fvARB (GLint location, GLsizei count, GLboolean transpose, const GLfloat *value); -GLAPI void APIENTRY glGetObjectParameterfvARB (GLhandleARB obj, GLenum pname, GLfloat *params); -GLAPI void APIENTRY glGetObjectParameterivARB (GLhandleARB obj, GLenum pname, GLint *params); -GLAPI void APIENTRY glGetInfoLogARB (GLhandleARB obj, GLsizei maxLength, GLsizei *length, GLcharARB *infoLog); -GLAPI void APIENTRY glGetAttachedObjectsARB (GLhandleARB containerObj, GLsizei maxCount, GLsizei *count, GLhandleARB *obj); -GLAPI GLint APIENTRY glGetUniformLocationARB (GLhandleARB programObj, const GLcharARB *name); -GLAPI void APIENTRY glGetActiveUniformARB (GLhandleARB programObj, GLuint index, GLsizei maxLength, GLsizei *length, GLint *size, GLenum *type, GLcharARB *name); -GLAPI void APIENTRY glGetUniformfvARB (GLhandleARB programObj, GLint location, GLfloat *params); -GLAPI void APIENTRY glGetUniformivARB (GLhandleARB programObj, GLint location, GLint *params); -GLAPI void APIENTRY glGetShaderSourceARB (GLhandleARB obj, GLsizei maxLength, GLsizei *length, GLcharARB *source); -#endif -#endif /* GL_ARB_shader_objects */ - -#ifndef GL_ARB_shader_precision -#define GL_ARB_shader_precision 1 -#endif /* GL_ARB_shader_precision */ - -#ifndef GL_ARB_shader_stencil_export -#define GL_ARB_shader_stencil_export 1 -#endif /* GL_ARB_shader_stencil_export */ - -#ifndef GL_ARB_shader_storage_buffer_object -#define GL_ARB_shader_storage_buffer_object 1 -#endif /* GL_ARB_shader_storage_buffer_object */ - -#ifndef GL_ARB_shader_subroutine -#define GL_ARB_shader_subroutine 1 -#endif /* GL_ARB_shader_subroutine */ - -#ifndef GL_ARB_shader_texture_lod -#define GL_ARB_shader_texture_lod 1 -#endif /* GL_ARB_shader_texture_lod */ - -#ifndef GL_ARB_shading_language_100 -#define GL_ARB_shading_language_100 1 -#define GL_SHADING_LANGUAGE_VERSION_ARB 0x8B8C -#endif /* GL_ARB_shading_language_100 */ - -#ifndef GL_ARB_shading_language_420pack -#define GL_ARB_shading_language_420pack 1 -#endif /* GL_ARB_shading_language_420pack */ - -#ifndef GL_ARB_shading_language_include -#define GL_ARB_shading_language_include 1 -#define GL_SHADER_INCLUDE_ARB 0x8DAE -#define GL_NAMED_STRING_LENGTH_ARB 0x8DE9 -#define GL_NAMED_STRING_TYPE_ARB 0x8DEA -typedef void (APIENTRYP PFNGLNAMEDSTRINGARBPROC) (GLenum type, GLint namelen, const GLchar *name, GLint stringlen, const GLchar *string); -typedef void (APIENTRYP PFNGLDELETENAMEDSTRINGARBPROC) (GLint namelen, const GLchar *name); -typedef void (APIENTRYP PFNGLCOMPILESHADERINCLUDEARBPROC) (GLuint shader, GLsizei count, const GLchar *const*path, const GLint *length); -typedef GLboolean (APIENTRYP PFNGLISNAMEDSTRINGARBPROC) (GLint namelen, const GLchar *name); -typedef void (APIENTRYP PFNGLGETNAMEDSTRINGARBPROC) (GLint namelen, const GLchar *name, GLsizei bufSize, GLint *stringlen, GLchar *string); -typedef void (APIENTRYP PFNGLGETNAMEDSTRINGIVARBPROC) (GLint namelen, const GLchar *name, GLenum pname, GLint *params); -#ifdef GL_GLEXT_PROTOTYPES -GLAPI void APIENTRY glNamedStringARB (GLenum type, GLint namelen, const GLchar *name, GLint stringlen, const GLchar *string); -GLAPI void APIENTRY glDeleteNamedStringARB (GLint namelen, const GLchar *name); -GLAPI void APIENTRY glCompileShaderIncludeARB (GLuint shader, GLsizei count, const GLchar *const*path, const GLint *length); -GLAPI GLboolean APIENTRY glIsNamedStringARB (GLint namelen, const GLchar *name); -GLAPI void APIENTRY glGetNamedStringARB (GLint namelen, const GLchar *name, GLsizei bufSize, GLint *stringlen, GLchar *string); -GLAPI void APIENTRY glGetNamedStringivARB (GLint namelen, const GLchar *name, GLenum pname, GLint *params); -#endif -#endif /* GL_ARB_shading_language_include */ - -#ifndef GL_ARB_shading_language_packing -#define GL_ARB_shading_language_packing 1 -#endif /* GL_ARB_shading_language_packing */ - -#ifndef GL_ARB_shadow -#define GL_ARB_shadow 1 -#define GL_TEXTURE_COMPARE_MODE_ARB 0x884C -#define GL_TEXTURE_COMPARE_FUNC_ARB 0x884D -#define GL_COMPARE_R_TO_TEXTURE_ARB 0x884E -#endif /* GL_ARB_shadow */ - -#ifndef GL_ARB_shadow_ambient -#define GL_ARB_shadow_ambient 1 -#define GL_TEXTURE_COMPARE_FAIL_VALUE_ARB 0x80BF -#endif /* GL_ARB_shadow_ambient */ - -#ifndef GL_ARB_sparse_texture -#define GL_ARB_sparse_texture 1 -#define GL_TEXTURE_SPARSE_ARB 0x91A6 -#define GL_VIRTUAL_PAGE_SIZE_INDEX_ARB 0x91A7 -#define GL_MIN_SPARSE_LEVEL_ARB 0x919B -#define GL_NUM_VIRTUAL_PAGE_SIZES_ARB 0x91A8 -#define GL_VIRTUAL_PAGE_SIZE_X_ARB 0x9195 -#define GL_VIRTUAL_PAGE_SIZE_Y_ARB 0x9196 -#define GL_VIRTUAL_PAGE_SIZE_Z_ARB 0x9197 -#define GL_MAX_SPARSE_TEXTURE_SIZE_ARB 0x9198 -#define GL_MAX_SPARSE_3D_TEXTURE_SIZE_ARB 0x9199 -#define GL_MAX_SPARSE_ARRAY_TEXTURE_LAYERS_ARB 0x919A -#define GL_SPARSE_TEXTURE_FULL_ARRAY_CUBE_MIPMAPS_ARB 0x91A9 -typedef void (APIENTRYP PFNGLTEXPAGECOMMITMENTARBPROC) (GLenum target, GLint level, GLint xoffset, GLint yoffset, GLint zoffset, GLsizei width, GLsizei height, GLsizei depth, GLboolean resident); -#ifdef GL_GLEXT_PROTOTYPES -GLAPI void APIENTRY glTexPageCommitmentARB (GLenum target, GLint level, GLint xoffset, GLint yoffset, GLint zoffset, GLsizei width, GLsizei height, GLsizei depth, GLboolean resident); -#endif -#endif /* GL_ARB_sparse_texture */ - -#ifndef GL_ARB_stencil_texturing -#define GL_ARB_stencil_texturing 1 -#endif /* GL_ARB_stencil_texturing */ - -#ifndef GL_ARB_sync -#define GL_ARB_sync 1 -#endif /* GL_ARB_sync */ - -#ifndef GL_ARB_tessellation_shader -#define GL_ARB_tessellation_shader 1 -#endif /* GL_ARB_tessellation_shader */ - -#ifndef GL_ARB_texture_border_clamp -#define GL_ARB_texture_border_clamp 1 -#define GL_CLAMP_TO_BORDER_ARB 0x812D -#endif /* GL_ARB_texture_border_clamp */ - -#ifndef GL_ARB_texture_buffer_object -#define GL_ARB_texture_buffer_object 1 -#define GL_TEXTURE_BUFFER_ARB 0x8C2A -#define GL_MAX_TEXTURE_BUFFER_SIZE_ARB 0x8C2B -#define GL_TEXTURE_BINDING_BUFFER_ARB 0x8C2C -#define GL_TEXTURE_BUFFER_DATA_STORE_BINDING_ARB 0x8C2D -#define GL_TEXTURE_BUFFER_FORMAT_ARB 0x8C2E -typedef void (APIENTRYP PFNGLTEXBUFFERARBPROC) (GLenum target, GLenum internalformat, GLuint buffer); -#ifdef GL_GLEXT_PROTOTYPES -GLAPI void APIENTRY glTexBufferARB (GLenum target, GLenum internalformat, GLuint buffer); -#endif -#endif /* GL_ARB_texture_buffer_object */ - -#ifndef GL_ARB_texture_buffer_object_rgb32 -#define GL_ARB_texture_buffer_object_rgb32 1 -#endif /* GL_ARB_texture_buffer_object_rgb32 */ - -#ifndef GL_ARB_texture_buffer_range -#define GL_ARB_texture_buffer_range 1 -#endif /* GL_ARB_texture_buffer_range */ - -#ifndef GL_ARB_texture_compression -#define GL_ARB_texture_compression 1 -#define GL_COMPRESSED_ALPHA_ARB 0x84E9 -#define GL_COMPRESSED_LUMINANCE_ARB 0x84EA -#define GL_COMPRESSED_LUMINANCE_ALPHA_ARB 0x84EB -#define GL_COMPRESSED_INTENSITY_ARB 0x84EC -#define GL_COMPRESSED_RGB_ARB 0x84ED -#define GL_COMPRESSED_RGBA_ARB 0x84EE -#define GL_TEXTURE_COMPRESSION_HINT_ARB 0x84EF -#define GL_TEXTURE_COMPRESSED_IMAGE_SIZE_ARB 0x86A0 -#define GL_TEXTURE_COMPRESSED_ARB 0x86A1 -#define GL_NUM_COMPRESSED_TEXTURE_FORMATS_ARB 0x86A2 -#define GL_COMPRESSED_TEXTURE_FORMATS_ARB 0x86A3 -typedef void (APIENTRYP PFNGLCOMPRESSEDTEXIMAGE3DARBPROC) (GLenum target, GLint level, GLenum internalformat, GLsizei width, GLsizei height, GLsizei depth, GLint border, GLsizei imageSize, const void *data); -typedef void (APIENTRYP PFNGLCOMPRESSEDTEXIMAGE2DARBPROC) (GLenum target, GLint level, GLenum internalformat, GLsizei width, GLsizei height, GLint border, GLsizei imageSize, const void *data); -typedef void (APIENTRYP PFNGLCOMPRESSEDTEXIMAGE1DARBPROC) (GLenum target, GLint level, GLenum internalformat, GLsizei width, GLint border, GLsizei imageSize, const void *data); -typedef void (APIENTRYP PFNGLCOMPRESSEDTEXSUBIMAGE3DARBPROC) (GLenum target, GLint level, GLint xoffset, GLint yoffset, GLint zoffset, GLsizei width, GLsizei height, GLsizei depth, GLenum format, GLsizei imageSize, const void *data); -typedef void (APIENTRYP PFNGLCOMPRESSEDTEXSUBIMAGE2DARBPROC) (GLenum target, GLint level, GLint xoffset, GLint yoffset, GLsizei width, GLsizei height, GLenum format, GLsizei imageSize, const void *data); -typedef void (APIENTRYP PFNGLCOMPRESSEDTEXSUBIMAGE1DARBPROC) (GLenum target, GLint level, GLint xoffset, GLsizei width, GLenum format, GLsizei imageSize, const void *data); -typedef void (APIENTRYP PFNGLGETCOMPRESSEDTEXIMAGEARBPROC) (GLenum target, GLint level, void *img); -#ifdef GL_GLEXT_PROTOTYPES -GLAPI void APIENTRY glCompressedTexImage3DARB (GLenum target, GLint level, GLenum internalformat, GLsizei width, GLsizei height, GLsizei depth, GLint border, GLsizei imageSize, const void *data); -GLAPI void APIENTRY glCompressedTexImage2DARB (GLenum target, GLint level, GLenum internalformat, GLsizei width, GLsizei height, GLint border, GLsizei imageSize, const void *data); -GLAPI void APIENTRY glCompressedTexImage1DARB (GLenum target, GLint level, GLenum internalformat, GLsizei width, GLint border, GLsizei imageSize, const void *data); -GLAPI void APIENTRY glCompressedTexSubImage3DARB (GLenum target, GLint level, GLint xoffset, GLint yoffset, GLint zoffset, GLsizei width, GLsizei height, GLsizei depth, GLenum format, GLsizei imageSize, const void *data); -GLAPI void APIENTRY glCompressedTexSubImage2DARB (GLenum target, GLint level, GLint xoffset, GLint yoffset, GLsizei width, GLsizei height, GLenum format, GLsizei imageSize, const void *data); -GLAPI void APIENTRY glCompressedTexSubImage1DARB (GLenum target, GLint level, GLint xoffset, GLsizei width, GLenum format, GLsizei imageSize, const void *data); -GLAPI void APIENTRY glGetCompressedTexImageARB (GLenum target, GLint level, void *img); -#endif -#endif /* GL_ARB_texture_compression */ - -#ifndef GL_ARB_texture_compression_bptc -#define GL_ARB_texture_compression_bptc 1 -#define GL_COMPRESSED_RGBA_BPTC_UNORM_ARB 0x8E8C -#define GL_COMPRESSED_SRGB_ALPHA_BPTC_UNORM_ARB 0x8E8D -#define GL_COMPRESSED_RGB_BPTC_SIGNED_FLOAT_ARB 0x8E8E -#define GL_COMPRESSED_RGB_BPTC_UNSIGNED_FLOAT_ARB 0x8E8F -#endif /* GL_ARB_texture_compression_bptc */ - -#ifndef GL_ARB_texture_compression_rgtc -#define GL_ARB_texture_compression_rgtc 1 -#endif /* GL_ARB_texture_compression_rgtc */ - -#ifndef GL_ARB_texture_cube_map -#define GL_ARB_texture_cube_map 1 -#define GL_NORMAL_MAP_ARB 0x8511 -#define GL_REFLECTION_MAP_ARB 0x8512 -#define GL_TEXTURE_CUBE_MAP_ARB 0x8513 -#define GL_TEXTURE_BINDING_CUBE_MAP_ARB 0x8514 -#define GL_TEXTURE_CUBE_MAP_POSITIVE_X_ARB 0x8515 -#define GL_TEXTURE_CUBE_MAP_NEGATIVE_X_ARB 0x8516 -#define GL_TEXTURE_CUBE_MAP_POSITIVE_Y_ARB 0x8517 -#define GL_TEXTURE_CUBE_MAP_NEGATIVE_Y_ARB 0x8518 -#define GL_TEXTURE_CUBE_MAP_POSITIVE_Z_ARB 0x8519 -#define GL_TEXTURE_CUBE_MAP_NEGATIVE_Z_ARB 0x851A -#define GL_PROXY_TEXTURE_CUBE_MAP_ARB 0x851B -#define GL_MAX_CUBE_MAP_TEXTURE_SIZE_ARB 0x851C -#endif /* GL_ARB_texture_cube_map */ - -#ifndef GL_ARB_texture_cube_map_array -#define GL_ARB_texture_cube_map_array 1 -#define GL_TEXTURE_CUBE_MAP_ARRAY_ARB 0x9009 -#define GL_TEXTURE_BINDING_CUBE_MAP_ARRAY_ARB 0x900A -#define GL_PROXY_TEXTURE_CUBE_MAP_ARRAY_ARB 0x900B -#define GL_SAMPLER_CUBE_MAP_ARRAY_ARB 0x900C -#define GL_SAMPLER_CUBE_MAP_ARRAY_SHADOW_ARB 0x900D -#define GL_INT_SAMPLER_CUBE_MAP_ARRAY_ARB 0x900E -#define GL_UNSIGNED_INT_SAMPLER_CUBE_MAP_ARRAY_ARB 0x900F -#endif /* GL_ARB_texture_cube_map_array */ - -#ifndef GL_ARB_texture_env_add -#define GL_ARB_texture_env_add 1 -#endif /* GL_ARB_texture_env_add */ - -#ifndef GL_ARB_texture_env_combine -#define GL_ARB_texture_env_combine 1 -#define GL_COMBINE_ARB 0x8570 -#define GL_COMBINE_RGB_ARB 0x8571 -#define GL_COMBINE_ALPHA_ARB 0x8572 -#define GL_SOURCE0_RGB_ARB 0x8580 -#define GL_SOURCE1_RGB_ARB 0x8581 -#define GL_SOURCE2_RGB_ARB 0x8582 -#define GL_SOURCE0_ALPHA_ARB 0x8588 -#define GL_SOURCE1_ALPHA_ARB 0x8589 -#define GL_SOURCE2_ALPHA_ARB 0x858A -#define GL_OPERAND0_RGB_ARB 0x8590 -#define GL_OPERAND1_RGB_ARB 0x8591 -#define GL_OPERAND2_RGB_ARB 0x8592 -#define GL_OPERAND0_ALPHA_ARB 0x8598 -#define GL_OPERAND1_ALPHA_ARB 0x8599 -#define GL_OPERAND2_ALPHA_ARB 0x859A -#define GL_RGB_SCALE_ARB 0x8573 -#define GL_ADD_SIGNED_ARB 0x8574 -#define GL_INTERPOLATE_ARB 0x8575 -#define GL_SUBTRACT_ARB 0x84E7 -#define GL_CONSTANT_ARB 0x8576 -#define GL_PRIMARY_COLOR_ARB 0x8577 -#define GL_PREVIOUS_ARB 0x8578 -#endif /* GL_ARB_texture_env_combine */ - -#ifndef GL_ARB_texture_env_crossbar -#define GL_ARB_texture_env_crossbar 1 -#endif /* GL_ARB_texture_env_crossbar */ - -#ifndef GL_ARB_texture_env_dot3 -#define GL_ARB_texture_env_dot3 1 -#define GL_DOT3_RGB_ARB 0x86AE -#define GL_DOT3_RGBA_ARB 0x86AF -#endif /* GL_ARB_texture_env_dot3 */ - -#ifndef GL_ARB_texture_float -#define GL_ARB_texture_float 1 -#define GL_TEXTURE_RED_TYPE_ARB 0x8C10 -#define GL_TEXTURE_GREEN_TYPE_ARB 0x8C11 -#define GL_TEXTURE_BLUE_TYPE_ARB 0x8C12 -#define GL_TEXTURE_ALPHA_TYPE_ARB 0x8C13 -#define GL_TEXTURE_LUMINANCE_TYPE_ARB 0x8C14 -#define GL_TEXTURE_INTENSITY_TYPE_ARB 0x8C15 -#define GL_TEXTURE_DEPTH_TYPE_ARB 0x8C16 -#define GL_UNSIGNED_NORMALIZED_ARB 0x8C17 -#define GL_RGBA32F_ARB 0x8814 -#define GL_RGB32F_ARB 0x8815 -#define GL_ALPHA32F_ARB 0x8816 -#define GL_INTENSITY32F_ARB 0x8817 -#define GL_LUMINANCE32F_ARB 0x8818 -#define GL_LUMINANCE_ALPHA32F_ARB 0x8819 -#define GL_RGBA16F_ARB 0x881A -#define GL_RGB16F_ARB 0x881B -#define GL_ALPHA16F_ARB 0x881C -#define GL_INTENSITY16F_ARB 0x881D -#define GL_LUMINANCE16F_ARB 0x881E -#define GL_LUMINANCE_ALPHA16F_ARB 0x881F -#endif /* GL_ARB_texture_float */ - -#ifndef GL_ARB_texture_gather -#define GL_ARB_texture_gather 1 -#define GL_MIN_PROGRAM_TEXTURE_GATHER_OFFSET_ARB 0x8E5E -#define GL_MAX_PROGRAM_TEXTURE_GATHER_OFFSET_ARB 0x8E5F -#define GL_MAX_PROGRAM_TEXTURE_GATHER_COMPONENTS_ARB 0x8F9F -#endif /* GL_ARB_texture_gather */ - -#ifndef GL_ARB_texture_mirror_clamp_to_edge -#define GL_ARB_texture_mirror_clamp_to_edge 1 -#endif /* GL_ARB_texture_mirror_clamp_to_edge */ - -#ifndef GL_ARB_texture_mirrored_repeat -#define GL_ARB_texture_mirrored_repeat 1 -#define GL_MIRRORED_REPEAT_ARB 0x8370 -#endif /* GL_ARB_texture_mirrored_repeat */ - -#ifndef GL_ARB_texture_multisample -#define GL_ARB_texture_multisample 1 -#endif /* GL_ARB_texture_multisample */ - -#ifndef GL_ARB_texture_non_power_of_two -#define GL_ARB_texture_non_power_of_two 1 -#endif /* GL_ARB_texture_non_power_of_two */ - -#ifndef GL_ARB_texture_query_levels -#define GL_ARB_texture_query_levels 1 -#endif /* GL_ARB_texture_query_levels */ - -#ifndef GL_ARB_texture_query_lod -#define GL_ARB_texture_query_lod 1 -#endif /* GL_ARB_texture_query_lod */ - -#ifndef GL_ARB_texture_rectangle -#define GL_ARB_texture_rectangle 1 -#define GL_TEXTURE_RECTANGLE_ARB 0x84F5 -#define GL_TEXTURE_BINDING_RECTANGLE_ARB 0x84F6 -#define GL_PROXY_TEXTURE_RECTANGLE_ARB 0x84F7 -#define GL_MAX_RECTANGLE_TEXTURE_SIZE_ARB 0x84F8 -#endif /* GL_ARB_texture_rectangle */ - -#ifndef GL_ARB_texture_rg -#define GL_ARB_texture_rg 1 -#endif /* GL_ARB_texture_rg */ - -#ifndef GL_ARB_texture_rgb10_a2ui -#define GL_ARB_texture_rgb10_a2ui 1 -#endif /* GL_ARB_texture_rgb10_a2ui */ - -#ifndef GL_ARB_texture_stencil8 -#define GL_ARB_texture_stencil8 1 -#endif /* GL_ARB_texture_stencil8 */ - -#ifndef GL_ARB_texture_storage -#define GL_ARB_texture_storage 1 -#endif /* GL_ARB_texture_storage */ - -#ifndef GL_ARB_texture_storage_multisample -#define GL_ARB_texture_storage_multisample 1 -#endif /* GL_ARB_texture_storage_multisample */ - -#ifndef GL_ARB_texture_swizzle -#define GL_ARB_texture_swizzle 1 -#endif /* GL_ARB_texture_swizzle */ - -#ifndef GL_ARB_texture_view -#define GL_ARB_texture_view 1 -#endif /* GL_ARB_texture_view */ - -#ifndef GL_ARB_timer_query -#define GL_ARB_timer_query 1 -#endif /* GL_ARB_timer_query */ - -#ifndef GL_ARB_transform_feedback2 -#define GL_ARB_transform_feedback2 1 -#define GL_TRANSFORM_FEEDBACK_PAUSED 0x8E23 -#define GL_TRANSFORM_FEEDBACK_ACTIVE 0x8E24 -#endif /* GL_ARB_transform_feedback2 */ - -#ifndef GL_ARB_transform_feedback3 -#define GL_ARB_transform_feedback3 1 -#endif /* GL_ARB_transform_feedback3 */ - -#ifndef GL_ARB_transform_feedback_instanced -#define GL_ARB_transform_feedback_instanced 1 -#endif /* GL_ARB_transform_feedback_instanced */ - -#ifndef GL_ARB_transpose_matrix -#define GL_ARB_transpose_matrix 1 -#define GL_TRANSPOSE_MODELVIEW_MATRIX_ARB 0x84E3 -#define GL_TRANSPOSE_PROJECTION_MATRIX_ARB 0x84E4 -#define GL_TRANSPOSE_TEXTURE_MATRIX_ARB 0x84E5 -#define GL_TRANSPOSE_COLOR_MATRIX_ARB 0x84E6 -typedef void (APIENTRYP PFNGLLOADTRANSPOSEMATRIXFARBPROC) (const GLfloat *m); -typedef void (APIENTRYP PFNGLLOADTRANSPOSEMATRIXDARBPROC) (const GLdouble *m); -typedef void (APIENTRYP PFNGLMULTTRANSPOSEMATRIXFARBPROC) (const GLfloat *m); -typedef void (APIENTRYP PFNGLMULTTRANSPOSEMATRIXDARBPROC) (const GLdouble *m); -#ifdef GL_GLEXT_PROTOTYPES -GLAPI void APIENTRY glLoadTransposeMatrixfARB (const GLfloat *m); -GLAPI void APIENTRY glLoadTransposeMatrixdARB (const GLdouble *m); -GLAPI void APIENTRY glMultTransposeMatrixfARB (const GLfloat *m); -GLAPI void APIENTRY glMultTransposeMatrixdARB (const GLdouble *m); -#endif -#endif /* GL_ARB_transpose_matrix */ - -#ifndef GL_ARB_uniform_buffer_object -#define GL_ARB_uniform_buffer_object 1 -#define GL_MAX_GEOMETRY_UNIFORM_BLOCKS 0x8A2C -#define GL_MAX_COMBINED_GEOMETRY_UNIFORM_COMPONENTS 0x8A32 -#define GL_UNIFORM_BLOCK_REFERENCED_BY_GEOMETRY_SHADER 0x8A45 -#endif /* GL_ARB_uniform_buffer_object */ - -#ifndef GL_ARB_vertex_array_bgra -#define GL_ARB_vertex_array_bgra 1 -#endif /* GL_ARB_vertex_array_bgra */ - -#ifndef GL_ARB_vertex_array_object -#define GL_ARB_vertex_array_object 1 -#endif /* GL_ARB_vertex_array_object */ - -#ifndef GL_ARB_vertex_attrib_64bit -#define GL_ARB_vertex_attrib_64bit 1 -#endif /* GL_ARB_vertex_attrib_64bit */ - -#ifndef GL_ARB_vertex_attrib_binding -#define GL_ARB_vertex_attrib_binding 1 -#endif /* GL_ARB_vertex_attrib_binding */ - -#ifndef GL_ARB_vertex_blend -#define GL_ARB_vertex_blend 1 -#define GL_MAX_VERTEX_UNITS_ARB 0x86A4 -#define GL_ACTIVE_VERTEX_UNITS_ARB 0x86A5 -#define GL_WEIGHT_SUM_UNITY_ARB 0x86A6 -#define GL_VERTEX_BLEND_ARB 0x86A7 -#define GL_CURRENT_WEIGHT_ARB 0x86A8 -#define GL_WEIGHT_ARRAY_TYPE_ARB 0x86A9 -#define GL_WEIGHT_ARRAY_STRIDE_ARB 0x86AA -#define GL_WEIGHT_ARRAY_SIZE_ARB 0x86AB -#define GL_WEIGHT_ARRAY_POINTER_ARB 0x86AC -#define GL_WEIGHT_ARRAY_ARB 0x86AD -#define GL_MODELVIEW0_ARB 0x1700 -#define GL_MODELVIEW1_ARB 0x850A -#define GL_MODELVIEW2_ARB 0x8722 -#define GL_MODELVIEW3_ARB 0x8723 -#define GL_MODELVIEW4_ARB 0x8724 -#define GL_MODELVIEW5_ARB 0x8725 -#define GL_MODELVIEW6_ARB 0x8726 -#define GL_MODELVIEW7_ARB 0x8727 -#define GL_MODELVIEW8_ARB 0x8728 -#define GL_MODELVIEW9_ARB 0x8729 -#define GL_MODELVIEW10_ARB 0x872A -#define GL_MODELVIEW11_ARB 0x872B -#define GL_MODELVIEW12_ARB 0x872C -#define GL_MODELVIEW13_ARB 0x872D -#define GL_MODELVIEW14_ARB 0x872E -#define GL_MODELVIEW15_ARB 0x872F -#define GL_MODELVIEW16_ARB 0x8730 -#define GL_MODELVIEW17_ARB 0x8731 -#define GL_MODELVIEW18_ARB 0x8732 -#define GL_MODELVIEW19_ARB 0x8733 -#define GL_MODELVIEW20_ARB 0x8734 -#define GL_MODELVIEW21_ARB 0x8735 -#define GL_MODELVIEW22_ARB 0x8736 -#define GL_MODELVIEW23_ARB 0x8737 -#define GL_MODELVIEW24_ARB 0x8738 -#define GL_MODELVIEW25_ARB 0x8739 -#define GL_MODELVIEW26_ARB 0x873A -#define GL_MODELVIEW27_ARB 0x873B -#define GL_MODELVIEW28_ARB 0x873C -#define GL_MODELVIEW29_ARB 0x873D -#define GL_MODELVIEW30_ARB 0x873E -#define GL_MODELVIEW31_ARB 0x873F -typedef void (APIENTRYP PFNGLWEIGHTBVARBPROC) (GLint size, const GLbyte *weights); -typedef void (APIENTRYP PFNGLWEIGHTSVARBPROC) (GLint size, const GLshort *weights); -typedef void (APIENTRYP PFNGLWEIGHTIVARBPROC) (GLint size, const GLint *weights); -typedef void (APIENTRYP PFNGLWEIGHTFVARBPROC) (GLint size, const GLfloat *weights); -typedef void (APIENTRYP PFNGLWEIGHTDVARBPROC) (GLint size, const GLdouble *weights); -typedef void (APIENTRYP PFNGLWEIGHTUBVARBPROC) (GLint size, const GLubyte *weights); -typedef void (APIENTRYP PFNGLWEIGHTUSVARBPROC) (GLint size, const GLushort *weights); -typedef void (APIENTRYP PFNGLWEIGHTUIVARBPROC) (GLint size, const GLuint *weights); -typedef void (APIENTRYP PFNGLWEIGHTPOINTERARBPROC) (GLint size, GLenum type, GLsizei stride, const void *pointer); -typedef void (APIENTRYP PFNGLVERTEXBLENDARBPROC) (GLint count); -#ifdef GL_GLEXT_PROTOTYPES -GLAPI void APIENTRY glWeightbvARB (GLint size, const GLbyte *weights); -GLAPI void APIENTRY glWeightsvARB (GLint size, const GLshort *weights); -GLAPI void APIENTRY glWeightivARB (GLint size, const GLint *weights); -GLAPI void APIENTRY glWeightfvARB (GLint size, const GLfloat *weights); -GLAPI void APIENTRY glWeightdvARB (GLint size, const GLdouble *weights); -GLAPI void APIENTRY glWeightubvARB (GLint size, const GLubyte *weights); -GLAPI void APIENTRY glWeightusvARB (GLint size, const GLushort *weights); -GLAPI void APIENTRY glWeightuivARB (GLint size, const GLuint *weights); -GLAPI void APIENTRY glWeightPointerARB (GLint size, GLenum type, GLsizei stride, const void *pointer); -GLAPI void APIENTRY glVertexBlendARB (GLint count); -#endif -#endif /* GL_ARB_vertex_blend */ - -#ifndef GL_ARB_vertex_buffer_object -#define GL_ARB_vertex_buffer_object 1 -typedef ptrdiff_t GLsizeiptrARB; -typedef ptrdiff_t GLintptrARB; -#define GL_BUFFER_SIZE_ARB 0x8764 -#define GL_BUFFER_USAGE_ARB 0x8765 -#define GL_ARRAY_BUFFER_ARB 0x8892 -#define GL_ELEMENT_ARRAY_BUFFER_ARB 0x8893 -#define GL_ARRAY_BUFFER_BINDING_ARB 0x8894 -#define GL_ELEMENT_ARRAY_BUFFER_BINDING_ARB 0x8895 -#define GL_VERTEX_ARRAY_BUFFER_BINDING_ARB 0x8896 -#define GL_NORMAL_ARRAY_BUFFER_BINDING_ARB 0x8897 -#define GL_COLOR_ARRAY_BUFFER_BINDING_ARB 0x8898 -#define GL_INDEX_ARRAY_BUFFER_BINDING_ARB 0x8899 -#define GL_TEXTURE_COORD_ARRAY_BUFFER_BINDING_ARB 0x889A -#define GL_EDGE_FLAG_ARRAY_BUFFER_BINDING_ARB 0x889B -#define GL_SECONDARY_COLOR_ARRAY_BUFFER_BINDING_ARB 0x889C -#define GL_FOG_COORDINATE_ARRAY_BUFFER_BINDING_ARB 0x889D -#define GL_WEIGHT_ARRAY_BUFFER_BINDING_ARB 0x889E -#define GL_VERTEX_ATTRIB_ARRAY_BUFFER_BINDING_ARB 0x889F -#define GL_READ_ONLY_ARB 0x88B8 -#define GL_WRITE_ONLY_ARB 0x88B9 -#define GL_READ_WRITE_ARB 0x88BA -#define GL_BUFFER_ACCESS_ARB 0x88BB -#define GL_BUFFER_MAPPED_ARB 0x88BC -#define GL_BUFFER_MAP_POINTER_ARB 0x88BD -#define GL_STREAM_DRAW_ARB 0x88E0 -#define GL_STREAM_READ_ARB 0x88E1 -#define GL_STREAM_COPY_ARB 0x88E2 -#define GL_STATIC_DRAW_ARB 0x88E4 -#define GL_STATIC_READ_ARB 0x88E5 -#define GL_STATIC_COPY_ARB 0x88E6 -#define GL_DYNAMIC_DRAW_ARB 0x88E8 -#define GL_DYNAMIC_READ_ARB 0x88E9 -#define GL_DYNAMIC_COPY_ARB 0x88EA -typedef void (APIENTRYP PFNGLBINDBUFFERARBPROC) (GLenum target, GLuint buffer); -typedef void (APIENTRYP PFNGLDELETEBUFFERSARBPROC) (GLsizei n, const GLuint *buffers); -typedef void (APIENTRYP PFNGLGENBUFFERSARBPROC) (GLsizei n, GLuint *buffers); -typedef GLboolean (APIENTRYP PFNGLISBUFFERARBPROC) (GLuint buffer); -typedef void (APIENTRYP PFNGLBUFFERDATAARBPROC) (GLenum target, GLsizeiptrARB size, const void *data, GLenum usage); -typedef void (APIENTRYP PFNGLBUFFERSUBDATAARBPROC) (GLenum target, GLintptrARB offset, GLsizeiptrARB size, const void *data); -typedef void (APIENTRYP PFNGLGETBUFFERSUBDATAARBPROC) (GLenum target, GLintptrARB offset, GLsizeiptrARB size, void *data); -typedef void *(APIENTRYP PFNGLMAPBUFFERARBPROC) (GLenum target, GLenum access); -typedef GLboolean (APIENTRYP PFNGLUNMAPBUFFERARBPROC) (GLenum target); -typedef void (APIENTRYP PFNGLGETBUFFERPARAMETERIVARBPROC) (GLenum target, GLenum pname, GLint *params); -typedef void (APIENTRYP PFNGLGETBUFFERPOINTERVARBPROC) (GLenum target, GLenum pname, void **params); -#ifdef GL_GLEXT_PROTOTYPES -GLAPI void APIENTRY glBindBufferARB (GLenum target, GLuint buffer); -GLAPI void APIENTRY glDeleteBuffersARB (GLsizei n, const GLuint *buffers); -GLAPI void APIENTRY glGenBuffersARB (GLsizei n, GLuint *buffers); -GLAPI GLboolean APIENTRY glIsBufferARB (GLuint buffer); -GLAPI void APIENTRY glBufferDataARB (GLenum target, GLsizeiptrARB size, const void *data, GLenum usage); -GLAPI void APIENTRY glBufferSubDataARB (GLenum target, GLintptrARB offset, GLsizeiptrARB size, const void *data); -GLAPI void APIENTRY glGetBufferSubDataARB (GLenum target, GLintptrARB offset, GLsizeiptrARB size, void *data); -GLAPI void *APIENTRY glMapBufferARB (GLenum target, GLenum access); -GLAPI GLboolean APIENTRY glUnmapBufferARB (GLenum target); -GLAPI void APIENTRY glGetBufferParameterivARB (GLenum target, GLenum pname, GLint *params); -GLAPI void APIENTRY glGetBufferPointervARB (GLenum target, GLenum pname, void **params); -#endif -#endif /* GL_ARB_vertex_buffer_object */ - -#ifndef GL_ARB_vertex_program -#define GL_ARB_vertex_program 1 -#define GL_COLOR_SUM_ARB 0x8458 -#define GL_VERTEX_PROGRAM_ARB 0x8620 -#define GL_VERTEX_ATTRIB_ARRAY_ENABLED_ARB 0x8622 -#define GL_VERTEX_ATTRIB_ARRAY_SIZE_ARB 0x8623 -#define GL_VERTEX_ATTRIB_ARRAY_STRIDE_ARB 0x8624 -#define GL_VERTEX_ATTRIB_ARRAY_TYPE_ARB 0x8625 -#define GL_CURRENT_VERTEX_ATTRIB_ARB 0x8626 -#define GL_VERTEX_PROGRAM_POINT_SIZE_ARB 0x8642 -#define GL_VERTEX_PROGRAM_TWO_SIDE_ARB 0x8643 -#define GL_VERTEX_ATTRIB_ARRAY_POINTER_ARB 0x8645 -#define GL_MAX_VERTEX_ATTRIBS_ARB 0x8869 -#define GL_VERTEX_ATTRIB_ARRAY_NORMALIZED_ARB 0x886A -#define GL_PROGRAM_ADDRESS_REGISTERS_ARB 0x88B0 -#define GL_MAX_PROGRAM_ADDRESS_REGISTERS_ARB 0x88B1 -#define GL_PROGRAM_NATIVE_ADDRESS_REGISTERS_ARB 0x88B2 -#define GL_MAX_PROGRAM_NATIVE_ADDRESS_REGISTERS_ARB 0x88B3 -typedef void (APIENTRYP PFNGLVERTEXATTRIB1DARBPROC) (GLuint index, GLdouble x); -typedef void (APIENTRYP PFNGLVERTEXATTRIB1DVARBPROC) (GLuint index, const GLdouble *v); -typedef void (APIENTRYP PFNGLVERTEXATTRIB1FARBPROC) (GLuint index, GLfloat x); -typedef void (APIENTRYP PFNGLVERTEXATTRIB1FVARBPROC) (GLuint index, const GLfloat *v); -typedef void (APIENTRYP PFNGLVERTEXATTRIB1SARBPROC) (GLuint index, GLshort x); -typedef void (APIENTRYP PFNGLVERTEXATTRIB1SVARBPROC) (GLuint index, const GLshort *v); -typedef void (APIENTRYP PFNGLVERTEXATTRIB2DARBPROC) (GLuint index, GLdouble x, GLdouble y); -typedef void (APIENTRYP PFNGLVERTEXATTRIB2DVARBPROC) (GLuint index, const GLdouble *v); -typedef void (APIENTRYP PFNGLVERTEXATTRIB2FARBPROC) (GLuint index, GLfloat x, GLfloat y); -typedef void (APIENTRYP PFNGLVERTEXATTRIB2FVARBPROC) (GLuint index, const GLfloat *v); -typedef void (APIENTRYP PFNGLVERTEXATTRIB2SARBPROC) (GLuint index, GLshort x, GLshort y); -typedef void (APIENTRYP PFNGLVERTEXATTRIB2SVARBPROC) (GLuint index, const GLshort *v); -typedef void (APIENTRYP PFNGLVERTEXATTRIB3DARBPROC) (GLuint index, GLdouble x, GLdouble y, GLdouble z); -typedef void (APIENTRYP PFNGLVERTEXATTRIB3DVARBPROC) (GLuint index, const GLdouble *v); -typedef void (APIENTRYP PFNGLVERTEXATTRIB3FARBPROC) (GLuint index, GLfloat x, GLfloat y, GLfloat z); -typedef void (APIENTRYP PFNGLVERTEXATTRIB3FVARBPROC) (GLuint index, const GLfloat *v); -typedef void (APIENTRYP PFNGLVERTEXATTRIB3SARBPROC) (GLuint index, GLshort x, GLshort y, GLshort z); -typedef void (APIENTRYP PFNGLVERTEXATTRIB3SVARBPROC) (GLuint index, const GLshort *v); -typedef void (APIENTRYP PFNGLVERTEXATTRIB4NBVARBPROC) (GLuint index, const GLbyte *v); -typedef void (APIENTRYP PFNGLVERTEXATTRIB4NIVARBPROC) (GLuint index, const GLint *v); -typedef void (APIENTRYP PFNGLVERTEXATTRIB4NSVARBPROC) (GLuint index, const GLshort *v); -typedef void (APIENTRYP PFNGLVERTEXATTRIB4NUBARBPROC) (GLuint index, GLubyte x, GLubyte y, GLubyte z, GLubyte w); -typedef void (APIENTRYP PFNGLVERTEXATTRIB4NUBVARBPROC) (GLuint index, const GLubyte *v); -typedef void (APIENTRYP PFNGLVERTEXATTRIB4NUIVARBPROC) (GLuint index, const GLuint *v); -typedef void (APIENTRYP PFNGLVERTEXATTRIB4NUSVARBPROC) (GLuint index, const GLushort *v); -typedef void (APIENTRYP PFNGLVERTEXATTRIB4BVARBPROC) (GLuint index, const GLbyte *v); -typedef void (APIENTRYP PFNGLVERTEXATTRIB4DARBPROC) (GLuint index, GLdouble x, GLdouble y, GLdouble z, GLdouble w); -typedef void (APIENTRYP PFNGLVERTEXATTRIB4DVARBPROC) (GLuint index, const GLdouble *v); -typedef void (APIENTRYP PFNGLVERTEXATTRIB4FARBPROC) (GLuint index, GLfloat x, GLfloat y, GLfloat z, GLfloat w); -typedef void (APIENTRYP PFNGLVERTEXATTRIB4FVARBPROC) (GLuint index, const GLfloat *v); -typedef void (APIENTRYP PFNGLVERTEXATTRIB4IVARBPROC) (GLuint index, const GLint *v); -typedef void (APIENTRYP PFNGLVERTEXATTRIB4SARBPROC) (GLuint index, GLshort x, GLshort y, GLshort z, GLshort w); -typedef void (APIENTRYP PFNGLVERTEXATTRIB4SVARBPROC) (GLuint index, const GLshort *v); -typedef void (APIENTRYP PFNGLVERTEXATTRIB4UBVARBPROC) (GLuint index, const GLubyte *v); -typedef void (APIENTRYP PFNGLVERTEXATTRIB4UIVARBPROC) (GLuint index, const GLuint *v); -typedef void (APIENTRYP PFNGLVERTEXATTRIB4USVARBPROC) (GLuint index, const GLushort *v); -typedef void (APIENTRYP PFNGLVERTEXATTRIBPOINTERARBPROC) (GLuint index, GLint size, GLenum type, GLboolean normalized, GLsizei stride, const void *pointer); -typedef void (APIENTRYP PFNGLENABLEVERTEXATTRIBARRAYARBPROC) (GLuint index); -typedef void (APIENTRYP PFNGLDISABLEVERTEXATTRIBARRAYARBPROC) (GLuint index); -typedef void (APIENTRYP PFNGLGETVERTEXATTRIBDVARBPROC) (GLuint index, GLenum pname, GLdouble *params); -typedef void (APIENTRYP PFNGLGETVERTEXATTRIBFVARBPROC) (GLuint index, GLenum pname, GLfloat *params); -typedef void (APIENTRYP PFNGLGETVERTEXATTRIBIVARBPROC) (GLuint index, GLenum pname, GLint *params); -typedef void (APIENTRYP PFNGLGETVERTEXATTRIBPOINTERVARBPROC) (GLuint index, GLenum pname, void **pointer); -#ifdef GL_GLEXT_PROTOTYPES -GLAPI void APIENTRY glVertexAttrib1dARB (GLuint index, GLdouble x); -GLAPI void APIENTRY glVertexAttrib1dvARB (GLuint index, const GLdouble *v); -GLAPI void APIENTRY glVertexAttrib1fARB (GLuint index, GLfloat x); -GLAPI void APIENTRY glVertexAttrib1fvARB (GLuint index, const GLfloat *v); -GLAPI void APIENTRY glVertexAttrib1sARB (GLuint index, GLshort x); -GLAPI void APIENTRY glVertexAttrib1svARB (GLuint index, const GLshort *v); -GLAPI void APIENTRY glVertexAttrib2dARB (GLuint index, GLdouble x, GLdouble y); -GLAPI void APIENTRY glVertexAttrib2dvARB (GLuint index, const GLdouble *v); -GLAPI void APIENTRY glVertexAttrib2fARB (GLuint index, GLfloat x, GLfloat y); -GLAPI void APIENTRY glVertexAttrib2fvARB (GLuint index, const GLfloat *v); -GLAPI void APIENTRY glVertexAttrib2sARB (GLuint index, GLshort x, GLshort y); -GLAPI void APIENTRY glVertexAttrib2svARB (GLuint index, const GLshort *v); -GLAPI void APIENTRY glVertexAttrib3dARB (GLuint index, GLdouble x, GLdouble y, GLdouble z); -GLAPI void APIENTRY glVertexAttrib3dvARB (GLuint index, const GLdouble *v); -GLAPI void APIENTRY glVertexAttrib3fARB (GLuint index, GLfloat x, GLfloat y, GLfloat z); -GLAPI void APIENTRY glVertexAttrib3fvARB (GLuint index, const GLfloat *v); -GLAPI void APIENTRY glVertexAttrib3sARB (GLuint index, GLshort x, GLshort y, GLshort z); -GLAPI void APIENTRY glVertexAttrib3svARB (GLuint index, const GLshort *v); -GLAPI void APIENTRY glVertexAttrib4NbvARB (GLuint index, const GLbyte *v); -GLAPI void APIENTRY glVertexAttrib4NivARB (GLuint index, const GLint *v); -GLAPI void APIENTRY glVertexAttrib4NsvARB (GLuint index, const GLshort *v); -GLAPI void APIENTRY glVertexAttrib4NubARB (GLuint index, GLubyte x, GLubyte y, GLubyte z, GLubyte w); -GLAPI void APIENTRY glVertexAttrib4NubvARB (GLuint index, const GLubyte *v); -GLAPI void APIENTRY glVertexAttrib4NuivARB (GLuint index, const GLuint *v); -GLAPI void APIENTRY glVertexAttrib4NusvARB (GLuint index, const GLushort *v); -GLAPI void APIENTRY glVertexAttrib4bvARB (GLuint index, const GLbyte *v); -GLAPI void APIENTRY glVertexAttrib4dARB (GLuint index, GLdouble x, GLdouble y, GLdouble z, GLdouble w); -GLAPI void APIENTRY glVertexAttrib4dvARB (GLuint index, const GLdouble *v); -GLAPI void APIENTRY glVertexAttrib4fARB (GLuint index, GLfloat x, GLfloat y, GLfloat z, GLfloat w); -GLAPI void APIENTRY glVertexAttrib4fvARB (GLuint index, const GLfloat *v); -GLAPI void APIENTRY glVertexAttrib4ivARB (GLuint index, const GLint *v); -GLAPI void APIENTRY glVertexAttrib4sARB (GLuint index, GLshort x, GLshort y, GLshort z, GLshort w); -GLAPI void APIENTRY glVertexAttrib4svARB (GLuint index, const GLshort *v); -GLAPI void APIENTRY glVertexAttrib4ubvARB (GLuint index, const GLubyte *v); -GLAPI void APIENTRY glVertexAttrib4uivARB (GLuint index, const GLuint *v); -GLAPI void APIENTRY glVertexAttrib4usvARB (GLuint index, const GLushort *v); -GLAPI void APIENTRY glVertexAttribPointerARB (GLuint index, GLint size, GLenum type, GLboolean normalized, GLsizei stride, const void *pointer); -GLAPI void APIENTRY glEnableVertexAttribArrayARB (GLuint index); -GLAPI void APIENTRY glDisableVertexAttribArrayARB (GLuint index); -GLAPI void APIENTRY glGetVertexAttribdvARB (GLuint index, GLenum pname, GLdouble *params); -GLAPI void APIENTRY glGetVertexAttribfvARB (GLuint index, GLenum pname, GLfloat *params); -GLAPI void APIENTRY glGetVertexAttribivARB (GLuint index, GLenum pname, GLint *params); -GLAPI void APIENTRY glGetVertexAttribPointervARB (GLuint index, GLenum pname, void **pointer); -#endif -#endif /* GL_ARB_vertex_program */ - -#ifndef GL_ARB_vertex_shader -#define GL_ARB_vertex_shader 1 -#define GL_VERTEX_SHADER_ARB 0x8B31 -#define GL_MAX_VERTEX_UNIFORM_COMPONENTS_ARB 0x8B4A -#define GL_MAX_VARYING_FLOATS_ARB 0x8B4B -#define GL_MAX_VERTEX_TEXTURE_IMAGE_UNITS_ARB 0x8B4C -#define GL_MAX_COMBINED_TEXTURE_IMAGE_UNITS_ARB 0x8B4D -#define GL_OBJECT_ACTIVE_ATTRIBUTES_ARB 0x8B89 -#define GL_OBJECT_ACTIVE_ATTRIBUTE_MAX_LENGTH_ARB 0x8B8A -typedef void (APIENTRYP PFNGLBINDATTRIBLOCATIONARBPROC) (GLhandleARB programObj, GLuint index, const GLcharARB *name); -typedef void (APIENTRYP PFNGLGETACTIVEATTRIBARBPROC) (GLhandleARB programObj, GLuint index, GLsizei maxLength, GLsizei *length, GLint *size, GLenum *type, GLcharARB *name); -typedef GLint (APIENTRYP PFNGLGETATTRIBLOCATIONARBPROC) (GLhandleARB programObj, const GLcharARB *name); -#ifdef GL_GLEXT_PROTOTYPES -GLAPI void APIENTRY glBindAttribLocationARB (GLhandleARB programObj, GLuint index, const GLcharARB *name); -GLAPI void APIENTRY glGetActiveAttribARB (GLhandleARB programObj, GLuint index, GLsizei maxLength, GLsizei *length, GLint *size, GLenum *type, GLcharARB *name); -GLAPI GLint APIENTRY glGetAttribLocationARB (GLhandleARB programObj, const GLcharARB *name); -#endif -#endif /* GL_ARB_vertex_shader */ - -#ifndef GL_ARB_vertex_type_10f_11f_11f_rev -#define GL_ARB_vertex_type_10f_11f_11f_rev 1 -#endif /* GL_ARB_vertex_type_10f_11f_11f_rev */ - -#ifndef GL_ARB_vertex_type_2_10_10_10_rev -#define GL_ARB_vertex_type_2_10_10_10_rev 1 -#endif /* GL_ARB_vertex_type_2_10_10_10_rev */ - -#ifndef GL_ARB_viewport_array -#define GL_ARB_viewport_array 1 -#endif /* GL_ARB_viewport_array */ - -#ifndef GL_ARB_window_pos -#define GL_ARB_window_pos 1 -typedef void (APIENTRYP PFNGLWINDOWPOS2DARBPROC) (GLdouble x, GLdouble y); -typedef void (APIENTRYP PFNGLWINDOWPOS2DVARBPROC) (const GLdouble *v); -typedef void (APIENTRYP PFNGLWINDOWPOS2FARBPROC) (GLfloat x, GLfloat y); -typedef void (APIENTRYP PFNGLWINDOWPOS2FVARBPROC) (const GLfloat *v); -typedef void (APIENTRYP PFNGLWINDOWPOS2IARBPROC) (GLint x, GLint y); -typedef void (APIENTRYP PFNGLWINDOWPOS2IVARBPROC) (const GLint *v); -typedef void (APIENTRYP PFNGLWINDOWPOS2SARBPROC) (GLshort x, GLshort y); -typedef void (APIENTRYP PFNGLWINDOWPOS2SVARBPROC) (const GLshort *v); -typedef void (APIENTRYP PFNGLWINDOWPOS3DARBPROC) (GLdouble x, GLdouble y, GLdouble z); -typedef void (APIENTRYP PFNGLWINDOWPOS3DVARBPROC) (const GLdouble *v); -typedef void (APIENTRYP PFNGLWINDOWPOS3FARBPROC) (GLfloat x, GLfloat y, GLfloat z); -typedef void (APIENTRYP PFNGLWINDOWPOS3FVARBPROC) (const GLfloat *v); -typedef void (APIENTRYP PFNGLWINDOWPOS3IARBPROC) (GLint x, GLint y, GLint z); -typedef void (APIENTRYP PFNGLWINDOWPOS3IVARBPROC) (const GLint *v); -typedef void (APIENTRYP PFNGLWINDOWPOS3SARBPROC) (GLshort x, GLshort y, GLshort z); -typedef void (APIENTRYP PFNGLWINDOWPOS3SVARBPROC) (const GLshort *v); -#ifdef GL_GLEXT_PROTOTYPES -GLAPI void APIENTRY glWindowPos2dARB (GLdouble x, GLdouble y); -GLAPI void APIENTRY glWindowPos2dvARB (const GLdouble *v); -GLAPI void APIENTRY glWindowPos2fARB (GLfloat x, GLfloat y); -GLAPI void APIENTRY glWindowPos2fvARB (const GLfloat *v); -GLAPI void APIENTRY glWindowPos2iARB (GLint x, GLint y); -GLAPI void APIENTRY glWindowPos2ivARB (const GLint *v); -GLAPI void APIENTRY glWindowPos2sARB (GLshort x, GLshort y); -GLAPI void APIENTRY glWindowPos2svARB (const GLshort *v); -GLAPI void APIENTRY glWindowPos3dARB (GLdouble x, GLdouble y, GLdouble z); -GLAPI void APIENTRY glWindowPos3dvARB (const GLdouble *v); -GLAPI void APIENTRY glWindowPos3fARB (GLfloat x, GLfloat y, GLfloat z); -GLAPI void APIENTRY glWindowPos3fvARB (const GLfloat *v); -GLAPI void APIENTRY glWindowPos3iARB (GLint x, GLint y, GLint z); -GLAPI void APIENTRY glWindowPos3ivARB (const GLint *v); -GLAPI void APIENTRY glWindowPos3sARB (GLshort x, GLshort y, GLshort z); -GLAPI void APIENTRY glWindowPos3svARB (const GLshort *v); -#endif -#endif /* GL_ARB_window_pos */ - -#ifndef GL_KHR_debug -#define GL_KHR_debug 1 -#endif /* GL_KHR_debug */ - -#ifndef GL_KHR_texture_compression_astc_hdr -#define GL_KHR_texture_compression_astc_hdr 1 -#define GL_COMPRESSED_RGBA_ASTC_4x4_KHR 0x93B0 -#define GL_COMPRESSED_RGBA_ASTC_5x4_KHR 0x93B1 -#define GL_COMPRESSED_RGBA_ASTC_5x5_KHR 0x93B2 -#define GL_COMPRESSED_RGBA_ASTC_6x5_KHR 0x93B3 -#define GL_COMPRESSED_RGBA_ASTC_6x6_KHR 0x93B4 -#define GL_COMPRESSED_RGBA_ASTC_8x5_KHR 0x93B5 -#define GL_COMPRESSED_RGBA_ASTC_8x6_KHR 0x93B6 -#define GL_COMPRESSED_RGBA_ASTC_8x8_KHR 0x93B7 -#define GL_COMPRESSED_RGBA_ASTC_10x5_KHR 0x93B8 -#define GL_COMPRESSED_RGBA_ASTC_10x6_KHR 0x93B9 -#define GL_COMPRESSED_RGBA_ASTC_10x8_KHR 0x93BA -#define GL_COMPRESSED_RGBA_ASTC_10x10_KHR 0x93BB -#define GL_COMPRESSED_RGBA_ASTC_12x10_KHR 0x93BC -#define GL_COMPRESSED_RGBA_ASTC_12x12_KHR 0x93BD -#define GL_COMPRESSED_SRGB8_ALPHA8_ASTC_4x4_KHR 0x93D0 -#define GL_COMPRESSED_SRGB8_ALPHA8_ASTC_5x4_KHR 0x93D1 -#define GL_COMPRESSED_SRGB8_ALPHA8_ASTC_5x5_KHR 0x93D2 -#define GL_COMPRESSED_SRGB8_ALPHA8_ASTC_6x5_KHR 0x93D3 -#define GL_COMPRESSED_SRGB8_ALPHA8_ASTC_6x6_KHR 0x93D4 -#define GL_COMPRESSED_SRGB8_ALPHA8_ASTC_8x5_KHR 0x93D5 -#define GL_COMPRESSED_SRGB8_ALPHA8_ASTC_8x6_KHR 0x93D6 -#define GL_COMPRESSED_SRGB8_ALPHA8_ASTC_8x8_KHR 0x93D7 -#define GL_COMPRESSED_SRGB8_ALPHA8_ASTC_10x5_KHR 0x93D8 -#define GL_COMPRESSED_SRGB8_ALPHA8_ASTC_10x6_KHR 0x93D9 -#define GL_COMPRESSED_SRGB8_ALPHA8_ASTC_10x8_KHR 0x93DA -#define GL_COMPRESSED_SRGB8_ALPHA8_ASTC_10x10_KHR 0x93DB -#define GL_COMPRESSED_SRGB8_ALPHA8_ASTC_12x10_KHR 0x93DC -#define GL_COMPRESSED_SRGB8_ALPHA8_ASTC_12x12_KHR 0x93DD -#endif /* GL_KHR_texture_compression_astc_hdr */ - -#ifndef GL_KHR_texture_compression_astc_ldr -#define GL_KHR_texture_compression_astc_ldr 1 -#endif /* GL_KHR_texture_compression_astc_ldr */ - -#ifndef GL_OES_byte_coordinates -#define GL_OES_byte_coordinates 1 -typedef void (APIENTRYP PFNGLMULTITEXCOORD1BOESPROC) (GLenum texture, GLbyte s); -typedef void (APIENTRYP PFNGLMULTITEXCOORD1BVOESPROC) (GLenum texture, const GLbyte *coords); -typedef void (APIENTRYP PFNGLMULTITEXCOORD2BOESPROC) (GLenum texture, GLbyte s, GLbyte t); -typedef void (APIENTRYP PFNGLMULTITEXCOORD2BVOESPROC) (GLenum texture, const GLbyte *coords); -typedef void (APIENTRYP PFNGLMULTITEXCOORD3BOESPROC) (GLenum texture, GLbyte s, GLbyte t, GLbyte r); -typedef void (APIENTRYP PFNGLMULTITEXCOORD3BVOESPROC) (GLenum texture, const GLbyte *coords); -typedef void (APIENTRYP PFNGLMULTITEXCOORD4BOESPROC) (GLenum texture, GLbyte s, GLbyte t, GLbyte r, GLbyte q); -typedef void (APIENTRYP PFNGLMULTITEXCOORD4BVOESPROC) (GLenum texture, const GLbyte *coords); -typedef void (APIENTRYP PFNGLTEXCOORD1BOESPROC) (GLbyte s); -typedef void (APIENTRYP PFNGLTEXCOORD1BVOESPROC) (const GLbyte *coords); -typedef void (APIENTRYP PFNGLTEXCOORD2BOESPROC) (GLbyte s, GLbyte t); -typedef void (APIENTRYP PFNGLTEXCOORD2BVOESPROC) (const GLbyte *coords); -typedef void (APIENTRYP PFNGLTEXCOORD3BOESPROC) (GLbyte s, GLbyte t, GLbyte r); -typedef void (APIENTRYP PFNGLTEXCOORD3BVOESPROC) (const GLbyte *coords); -typedef void (APIENTRYP PFNGLTEXCOORD4BOESPROC) (GLbyte s, GLbyte t, GLbyte r, GLbyte q); -typedef void (APIENTRYP PFNGLTEXCOORD4BVOESPROC) (const GLbyte *coords); -typedef void (APIENTRYP PFNGLVERTEX2BOESPROC) (GLbyte x); -typedef void (APIENTRYP PFNGLVERTEX2BVOESPROC) (const GLbyte *coords); -typedef void (APIENTRYP PFNGLVERTEX3BOESPROC) (GLbyte x, GLbyte y); -typedef void (APIENTRYP PFNGLVERTEX3BVOESPROC) (const GLbyte *coords); -typedef void (APIENTRYP PFNGLVERTEX4BOESPROC) (GLbyte x, GLbyte y, GLbyte z); -typedef void (APIENTRYP PFNGLVERTEX4BVOESPROC) (const GLbyte *coords); -#ifdef GL_GLEXT_PROTOTYPES -GLAPI void APIENTRY glMultiTexCoord1bOES (GLenum texture, GLbyte s); -GLAPI void APIENTRY glMultiTexCoord1bvOES (GLenum texture, const GLbyte *coords); -GLAPI void APIENTRY glMultiTexCoord2bOES (GLenum texture, GLbyte s, GLbyte t); -GLAPI void APIENTRY glMultiTexCoord2bvOES (GLenum texture, const GLbyte *coords); -GLAPI void APIENTRY glMultiTexCoord3bOES (GLenum texture, GLbyte s, GLbyte t, GLbyte r); -GLAPI void APIENTRY glMultiTexCoord3bvOES (GLenum texture, const GLbyte *coords); -GLAPI void APIENTRY glMultiTexCoord4bOES (GLenum texture, GLbyte s, GLbyte t, GLbyte r, GLbyte q); -GLAPI void APIENTRY glMultiTexCoord4bvOES (GLenum texture, const GLbyte *coords); -GLAPI void APIENTRY glTexCoord1bOES (GLbyte s); -GLAPI void APIENTRY glTexCoord1bvOES (const GLbyte *coords); -GLAPI void APIENTRY glTexCoord2bOES (GLbyte s, GLbyte t); -GLAPI void APIENTRY glTexCoord2bvOES (const GLbyte *coords); -GLAPI void APIENTRY glTexCoord3bOES (GLbyte s, GLbyte t, GLbyte r); -GLAPI void APIENTRY glTexCoord3bvOES (const GLbyte *coords); -GLAPI void APIENTRY glTexCoord4bOES (GLbyte s, GLbyte t, GLbyte r, GLbyte q); -GLAPI void APIENTRY glTexCoord4bvOES (const GLbyte *coords); -GLAPI void APIENTRY glVertex2bOES (GLbyte x); -GLAPI void APIENTRY glVertex2bvOES (const GLbyte *coords); -GLAPI void APIENTRY glVertex3bOES (GLbyte x, GLbyte y); -GLAPI void APIENTRY glVertex3bvOES (const GLbyte *coords); -GLAPI void APIENTRY glVertex4bOES (GLbyte x, GLbyte y, GLbyte z); -GLAPI void APIENTRY glVertex4bvOES (const GLbyte *coords); -#endif -#endif /* GL_OES_byte_coordinates */ - -#ifndef GL_OES_compressed_paletted_texture -#define GL_OES_compressed_paletted_texture 1 -#define GL_PALETTE4_RGB8_OES 0x8B90 -#define GL_PALETTE4_RGBA8_OES 0x8B91 -#define GL_PALETTE4_R5_G6_B5_OES 0x8B92 -#define GL_PALETTE4_RGBA4_OES 0x8B93 -#define GL_PALETTE4_RGB5_A1_OES 0x8B94 -#define GL_PALETTE8_RGB8_OES 0x8B95 -#define GL_PALETTE8_RGBA8_OES 0x8B96 -#define GL_PALETTE8_R5_G6_B5_OES 0x8B97 -#define GL_PALETTE8_RGBA4_OES 0x8B98 -#define GL_PALETTE8_RGB5_A1_OES 0x8B99 -#endif /* GL_OES_compressed_paletted_texture */ - -#ifndef GL_OES_fixed_point -#define GL_OES_fixed_point 1 -typedef GLint GLfixed; -#define GL_FIXED_OES 0x140C -typedef void (APIENTRYP PFNGLALPHAFUNCXOESPROC) (GLenum func, GLfixed ref); -typedef void (APIENTRYP PFNGLCLEARCOLORXOESPROC) (GLfixed red, GLfixed green, GLfixed blue, GLfixed alpha); -typedef void (APIENTRYP PFNGLCLEARDEPTHXOESPROC) (GLfixed depth); -typedef void (APIENTRYP PFNGLCLIPPLANEXOESPROC) (GLenum plane, const GLfixed *equation); -typedef void (APIENTRYP PFNGLCOLOR4XOESPROC) (GLfixed red, GLfixed green, GLfixed blue, GLfixed alpha); -typedef void (APIENTRYP PFNGLDEPTHRANGEXOESPROC) (GLfixed n, GLfixed f); -typedef void (APIENTRYP PFNGLFOGXOESPROC) (GLenum pname, GLfixed param); -typedef void (APIENTRYP PFNGLFOGXVOESPROC) (GLenum pname, const GLfixed *param); -typedef void (APIENTRYP PFNGLFRUSTUMXOESPROC) (GLfixed l, GLfixed r, GLfixed b, GLfixed t, GLfixed n, GLfixed f); -typedef void (APIENTRYP PFNGLGETCLIPPLANEXOESPROC) (GLenum plane, GLfixed *equation); -typedef void (APIENTRYP PFNGLGETFIXEDVOESPROC) (GLenum pname, GLfixed *params); -typedef void (APIENTRYP PFNGLGETTEXENVXVOESPROC) (GLenum target, GLenum pname, GLfixed *params); -typedef void (APIENTRYP PFNGLGETTEXPARAMETERXVOESPROC) (GLenum target, GLenum pname, GLfixed *params); -typedef void (APIENTRYP PFNGLLIGHTMODELXOESPROC) (GLenum pname, GLfixed param); -typedef void (APIENTRYP PFNGLLIGHTMODELXVOESPROC) (GLenum pname, const GLfixed *param); -typedef void (APIENTRYP PFNGLLIGHTXOESPROC) (GLenum light, GLenum pname, GLfixed param); -typedef void (APIENTRYP PFNGLLIGHTXVOESPROC) (GLenum light, GLenum pname, const GLfixed *params); -typedef void (APIENTRYP PFNGLLINEWIDTHXOESPROC) (GLfixed width); -typedef void (APIENTRYP PFNGLLOADMATRIXXOESPROC) (const GLfixed *m); -typedef void (APIENTRYP PFNGLMATERIALXOESPROC) (GLenum face, GLenum pname, GLfixed param); -typedef void (APIENTRYP PFNGLMATERIALXVOESPROC) (GLenum face, GLenum pname, const GLfixed *param); -typedef void (APIENTRYP PFNGLMULTMATRIXXOESPROC) (const GLfixed *m); -typedef void (APIENTRYP PFNGLMULTITEXCOORD4XOESPROC) (GLenum texture, GLfixed s, GLfixed t, GLfixed r, GLfixed q); -typedef void (APIENTRYP PFNGLNORMAL3XOESPROC) (GLfixed nx, GLfixed ny, GLfixed nz); -typedef void (APIENTRYP PFNGLORTHOXOESPROC) (GLfixed l, GLfixed r, GLfixed b, GLfixed t, GLfixed n, GLfixed f); -typedef void (APIENTRYP PFNGLPOINTPARAMETERXVOESPROC) (GLenum pname, const GLfixed *params); -typedef void (APIENTRYP PFNGLPOINTSIZEXOESPROC) (GLfixed size); -typedef void (APIENTRYP PFNGLPOLYGONOFFSETXOESPROC) (GLfixed factor, GLfixed units); -typedef void (APIENTRYP PFNGLROTATEXOESPROC) (GLfixed angle, GLfixed x, GLfixed y, GLfixed z); -typedef void (APIENTRYP PFNGLSAMPLECOVERAGEOESPROC) (GLfixed value, GLboolean invert); -typedef void (APIENTRYP PFNGLSCALEXOESPROC) (GLfixed x, GLfixed y, GLfixed z); -typedef void (APIENTRYP PFNGLTEXENVXOESPROC) (GLenum target, GLenum pname, GLfixed param); -typedef void (APIENTRYP PFNGLTEXENVXVOESPROC) (GLenum target, GLenum pname, const GLfixed *params); -typedef void (APIENTRYP PFNGLTEXPARAMETERXOESPROC) (GLenum target, GLenum pname, GLfixed param); -typedef void (APIENTRYP PFNGLTEXPARAMETERXVOESPROC) (GLenum target, GLenum pname, const GLfixed *params); -typedef void (APIENTRYP PFNGLTRANSLATEXOESPROC) (GLfixed x, GLfixed y, GLfixed z); -typedef void (APIENTRYP PFNGLACCUMXOESPROC) (GLenum op, GLfixed value); -typedef void (APIENTRYP PFNGLBITMAPXOESPROC) (GLsizei width, GLsizei height, GLfixed xorig, GLfixed yorig, GLfixed xmove, GLfixed ymove, const GLubyte *bitmap); -typedef void (APIENTRYP PFNGLBLENDCOLORXOESPROC) (GLfixed red, GLfixed green, GLfixed blue, GLfixed alpha); -typedef void (APIENTRYP PFNGLCLEARACCUMXOESPROC) (GLfixed red, GLfixed green, GLfixed blue, GLfixed alpha); -typedef void (APIENTRYP PFNGLCOLOR3XOESPROC) (GLfixed red, GLfixed green, GLfixed blue); -typedef void (APIENTRYP PFNGLCOLOR3XVOESPROC) (const GLfixed *components); -typedef void (APIENTRYP PFNGLCOLOR4XVOESPROC) (const GLfixed *components); -typedef void (APIENTRYP PFNGLCONVOLUTIONPARAMETERXOESPROC) (GLenum target, GLenum pname, GLfixed param); -typedef void (APIENTRYP PFNGLCONVOLUTIONPARAMETERXVOESPROC) (GLenum target, GLenum pname, const GLfixed *params); -typedef void (APIENTRYP PFNGLEVALCOORD1XOESPROC) (GLfixed u); -typedef void (APIENTRYP PFNGLEVALCOORD1XVOESPROC) (const GLfixed *coords); -typedef void (APIENTRYP PFNGLEVALCOORD2XOESPROC) (GLfixed u, GLfixed v); -typedef void (APIENTRYP PFNGLEVALCOORD2XVOESPROC) (const GLfixed *coords); -typedef void (APIENTRYP PFNGLFEEDBACKBUFFERXOESPROC) (GLsizei n, GLenum type, const GLfixed *buffer); -typedef void (APIENTRYP PFNGLGETCONVOLUTIONPARAMETERXVOESPROC) (GLenum target, GLenum pname, GLfixed *params); -typedef void (APIENTRYP PFNGLGETHISTOGRAMPARAMETERXVOESPROC) (GLenum target, GLenum pname, GLfixed *params); -typedef void (APIENTRYP PFNGLGETLIGHTXOESPROC) (GLenum light, GLenum pname, GLfixed *params); -typedef void (APIENTRYP PFNGLGETMAPXVOESPROC) (GLenum target, GLenum query, GLfixed *v); -typedef void (APIENTRYP PFNGLGETMATERIALXOESPROC) (GLenum face, GLenum pname, GLfixed param); -typedef void (APIENTRYP PFNGLGETPIXELMAPXVPROC) (GLenum map, GLint size, GLfixed *values); -typedef void (APIENTRYP PFNGLGETTEXGENXVOESPROC) (GLenum coord, GLenum pname, GLfixed *params); -typedef void (APIENTRYP PFNGLGETTEXLEVELPARAMETERXVOESPROC) (GLenum target, GLint level, GLenum pname, GLfixed *params); -typedef void (APIENTRYP PFNGLINDEXXOESPROC) (GLfixed component); -typedef void (APIENTRYP PFNGLINDEXXVOESPROC) (const GLfixed *component); -typedef void (APIENTRYP PFNGLLOADTRANSPOSEMATRIXXOESPROC) (const GLfixed *m); -typedef void (APIENTRYP PFNGLMAP1XOESPROC) (GLenum target, GLfixed u1, GLfixed u2, GLint stride, GLint order, GLfixed points); -typedef void (APIENTRYP PFNGLMAP2XOESPROC) (GLenum target, GLfixed u1, GLfixed u2, GLint ustride, GLint uorder, GLfixed v1, GLfixed v2, GLint vstride, GLint vorder, GLfixed points); -typedef void (APIENTRYP PFNGLMAPGRID1XOESPROC) (GLint n, GLfixed u1, GLfixed u2); -typedef void (APIENTRYP PFNGLMAPGRID2XOESPROC) (GLint n, GLfixed u1, GLfixed u2, GLfixed v1, GLfixed v2); -typedef void (APIENTRYP PFNGLMULTTRANSPOSEMATRIXXOESPROC) (const GLfixed *m); -typedef void (APIENTRYP PFNGLMULTITEXCOORD1XOESPROC) (GLenum texture, GLfixed s); -typedef void (APIENTRYP PFNGLMULTITEXCOORD1XVOESPROC) (GLenum texture, const GLfixed *coords); -typedef void (APIENTRYP PFNGLMULTITEXCOORD2XOESPROC) (GLenum texture, GLfixed s, GLfixed t); -typedef void (APIENTRYP PFNGLMULTITEXCOORD2XVOESPROC) (GLenum texture, const GLfixed *coords); -typedef void (APIENTRYP PFNGLMULTITEXCOORD3XOESPROC) (GLenum texture, GLfixed s, GLfixed t, GLfixed r); -typedef void (APIENTRYP PFNGLMULTITEXCOORD3XVOESPROC) (GLenum texture, const GLfixed *coords); -typedef void (APIENTRYP PFNGLMULTITEXCOORD4XVOESPROC) (GLenum texture, const GLfixed *coords); -typedef void (APIENTRYP PFNGLNORMAL3XVOESPROC) (const GLfixed *coords); -typedef void (APIENTRYP PFNGLPASSTHROUGHXOESPROC) (GLfixed token); -typedef void (APIENTRYP PFNGLPIXELMAPXPROC) (GLenum map, GLint size, const GLfixed *values); -typedef void (APIENTRYP PFNGLPIXELSTOREXPROC) (GLenum pname, GLfixed param); -typedef void (APIENTRYP PFNGLPIXELTRANSFERXOESPROC) (GLenum pname, GLfixed param); -typedef void (APIENTRYP PFNGLPIXELZOOMXOESPROC) (GLfixed xfactor, GLfixed yfactor); -typedef void (APIENTRYP PFNGLPRIORITIZETEXTURESXOESPROC) (GLsizei n, const GLuint *textures, const GLfixed *priorities); -typedef void (APIENTRYP PFNGLRASTERPOS2XOESPROC) (GLfixed x, GLfixed y); -typedef void (APIENTRYP PFNGLRASTERPOS2XVOESPROC) (const GLfixed *coords); -typedef void (APIENTRYP PFNGLRASTERPOS3XOESPROC) (GLfixed x, GLfixed y, GLfixed z); -typedef void (APIENTRYP PFNGLRASTERPOS3XVOESPROC) (const GLfixed *coords); -typedef void (APIENTRYP PFNGLRASTERPOS4XOESPROC) (GLfixed x, GLfixed y, GLfixed z, GLfixed w); -typedef void (APIENTRYP PFNGLRASTERPOS4XVOESPROC) (const GLfixed *coords); -typedef void (APIENTRYP PFNGLRECTXOESPROC) (GLfixed x1, GLfixed y1, GLfixed x2, GLfixed y2); -typedef void (APIENTRYP PFNGLRECTXVOESPROC) (const GLfixed *v1, const GLfixed *v2); -typedef void (APIENTRYP PFNGLTEXCOORD1XOESPROC) (GLfixed s); -typedef void (APIENTRYP PFNGLTEXCOORD1XVOESPROC) (const GLfixed *coords); -typedef void (APIENTRYP PFNGLTEXCOORD2XOESPROC) (GLfixed s, GLfixed t); -typedef void (APIENTRYP PFNGLTEXCOORD2XVOESPROC) (const GLfixed *coords); -typedef void (APIENTRYP PFNGLTEXCOORD3XOESPROC) (GLfixed s, GLfixed t, GLfixed r); -typedef void (APIENTRYP PFNGLTEXCOORD3XVOESPROC) (const GLfixed *coords); -typedef void (APIENTRYP PFNGLTEXCOORD4XOESPROC) (GLfixed s, GLfixed t, GLfixed r, GLfixed q); -typedef void (APIENTRYP PFNGLTEXCOORD4XVOESPROC) (const GLfixed *coords); -typedef void (APIENTRYP PFNGLTEXGENXOESPROC) (GLenum coord, GLenum pname, GLfixed param); -typedef void (APIENTRYP PFNGLTEXGENXVOESPROC) (GLenum coord, GLenum pname, const GLfixed *params); -typedef void (APIENTRYP PFNGLVERTEX2XOESPROC) (GLfixed x); -typedef void (APIENTRYP PFNGLVERTEX2XVOESPROC) (const GLfixed *coords); -typedef void (APIENTRYP PFNGLVERTEX3XOESPROC) (GLfixed x, GLfixed y); -typedef void (APIENTRYP PFNGLVERTEX3XVOESPROC) (const GLfixed *coords); -typedef void (APIENTRYP PFNGLVERTEX4XOESPROC) (GLfixed x, GLfixed y, GLfixed z); -typedef void (APIENTRYP PFNGLVERTEX4XVOESPROC) (const GLfixed *coords); -#ifdef GL_GLEXT_PROTOTYPES -GLAPI void APIENTRY glAlphaFuncxOES (GLenum func, GLfixed ref); -GLAPI void APIENTRY glClearColorxOES (GLfixed red, GLfixed green, GLfixed blue, GLfixed alpha); -GLAPI void APIENTRY glClearDepthxOES (GLfixed depth); -GLAPI void APIENTRY glClipPlanexOES (GLenum plane, const GLfixed *equation); -GLAPI void APIENTRY glColor4xOES (GLfixed red, GLfixed green, GLfixed blue, GLfixed alpha); -GLAPI void APIENTRY glDepthRangexOES (GLfixed n, GLfixed f); -GLAPI void APIENTRY glFogxOES (GLenum pname, GLfixed param); -GLAPI void APIENTRY glFogxvOES (GLenum pname, const GLfixed *param); -GLAPI void APIENTRY glFrustumxOES (GLfixed l, GLfixed r, GLfixed b, GLfixed t, GLfixed n, GLfixed f); -GLAPI void APIENTRY glGetClipPlanexOES (GLenum plane, GLfixed *equation); -GLAPI void APIENTRY glGetFixedvOES (GLenum pname, GLfixed *params); -GLAPI void APIENTRY glGetTexEnvxvOES (GLenum target, GLenum pname, GLfixed *params); -GLAPI void APIENTRY glGetTexParameterxvOES (GLenum target, GLenum pname, GLfixed *params); -GLAPI void APIENTRY glLightModelxOES (GLenum pname, GLfixed param); -GLAPI void APIENTRY glLightModelxvOES (GLenum pname, const GLfixed *param); -GLAPI void APIENTRY glLightxOES (GLenum light, GLenum pname, GLfixed param); -GLAPI void APIENTRY glLightxvOES (GLenum light, GLenum pname, const GLfixed *params); -GLAPI void APIENTRY glLineWidthxOES (GLfixed width); -GLAPI void APIENTRY glLoadMatrixxOES (const GLfixed *m); -GLAPI void APIENTRY glMaterialxOES (GLenum face, GLenum pname, GLfixed param); -GLAPI void APIENTRY glMaterialxvOES (GLenum face, GLenum pname, const GLfixed *param); -GLAPI void APIENTRY glMultMatrixxOES (const GLfixed *m); -GLAPI void APIENTRY glMultiTexCoord4xOES (GLenum texture, GLfixed s, GLfixed t, GLfixed r, GLfixed q); -GLAPI void APIENTRY glNormal3xOES (GLfixed nx, GLfixed ny, GLfixed nz); -GLAPI void APIENTRY glOrthoxOES (GLfixed l, GLfixed r, GLfixed b, GLfixed t, GLfixed n, GLfixed f); -GLAPI void APIENTRY glPointParameterxvOES (GLenum pname, const GLfixed *params); -GLAPI void APIENTRY glPointSizexOES (GLfixed size); -GLAPI void APIENTRY glPolygonOffsetxOES (GLfixed factor, GLfixed units); -GLAPI void APIENTRY glRotatexOES (GLfixed angle, GLfixed x, GLfixed y, GLfixed z); -GLAPI void APIENTRY glSampleCoverageOES (GLfixed value, GLboolean invert); -GLAPI void APIENTRY glScalexOES (GLfixed x, GLfixed y, GLfixed z); -GLAPI void APIENTRY glTexEnvxOES (GLenum target, GLenum pname, GLfixed param); -GLAPI void APIENTRY glTexEnvxvOES (GLenum target, GLenum pname, const GLfixed *params); -GLAPI void APIENTRY glTexParameterxOES (GLenum target, GLenum pname, GLfixed param); -GLAPI void APIENTRY glTexParameterxvOES (GLenum target, GLenum pname, const GLfixed *params); -GLAPI void APIENTRY glTranslatexOES (GLfixed x, GLfixed y, GLfixed z); -GLAPI void APIENTRY glAccumxOES (GLenum op, GLfixed value); -GLAPI void APIENTRY glBitmapxOES (GLsizei width, GLsizei height, GLfixed xorig, GLfixed yorig, GLfixed xmove, GLfixed ymove, const GLubyte *bitmap); -GLAPI void APIENTRY glBlendColorxOES (GLfixed red, GLfixed green, GLfixed blue, GLfixed alpha); -GLAPI void APIENTRY glClearAccumxOES (GLfixed red, GLfixed green, GLfixed blue, GLfixed alpha); -GLAPI void APIENTRY glColor3xOES (GLfixed red, GLfixed green, GLfixed blue); -GLAPI void APIENTRY glColor3xvOES (const GLfixed *components); -GLAPI void APIENTRY glColor4xvOES (const GLfixed *components); -GLAPI void APIENTRY glConvolutionParameterxOES (GLenum target, GLenum pname, GLfixed param); -GLAPI void APIENTRY glConvolutionParameterxvOES (GLenum target, GLenum pname, const GLfixed *params); -GLAPI void APIENTRY glEvalCoord1xOES (GLfixed u); -GLAPI void APIENTRY glEvalCoord1xvOES (const GLfixed *coords); -GLAPI void APIENTRY glEvalCoord2xOES (GLfixed u, GLfixed v); -GLAPI void APIENTRY glEvalCoord2xvOES (const GLfixed *coords); -GLAPI void APIENTRY glFeedbackBufferxOES (GLsizei n, GLenum type, const GLfixed *buffer); -GLAPI void APIENTRY glGetConvolutionParameterxvOES (GLenum target, GLenum pname, GLfixed *params); -GLAPI void APIENTRY glGetHistogramParameterxvOES (GLenum target, GLenum pname, GLfixed *params); -GLAPI void APIENTRY glGetLightxOES (GLenum light, GLenum pname, GLfixed *params); -GLAPI void APIENTRY glGetMapxvOES (GLenum target, GLenum query, GLfixed *v); -GLAPI void APIENTRY glGetMaterialxOES (GLenum face, GLenum pname, GLfixed param); -GLAPI void APIENTRY glGetPixelMapxv (GLenum map, GLint size, GLfixed *values); -GLAPI void APIENTRY glGetTexGenxvOES (GLenum coord, GLenum pname, GLfixed *params); -GLAPI void APIENTRY glGetTexLevelParameterxvOES (GLenum target, GLint level, GLenum pname, GLfixed *params); -GLAPI void APIENTRY glIndexxOES (GLfixed component); -GLAPI void APIENTRY glIndexxvOES (const GLfixed *component); -GLAPI void APIENTRY glLoadTransposeMatrixxOES (const GLfixed *m); -GLAPI void APIENTRY glMap1xOES (GLenum target, GLfixed u1, GLfixed u2, GLint stride, GLint order, GLfixed points); -GLAPI void APIENTRY glMap2xOES (GLenum target, GLfixed u1, GLfixed u2, GLint ustride, GLint uorder, GLfixed v1, GLfixed v2, GLint vstride, GLint vorder, GLfixed points); -GLAPI void APIENTRY glMapGrid1xOES (GLint n, GLfixed u1, GLfixed u2); -GLAPI void APIENTRY glMapGrid2xOES (GLint n, GLfixed u1, GLfixed u2, GLfixed v1, GLfixed v2); -GLAPI void APIENTRY glMultTransposeMatrixxOES (const GLfixed *m); -GLAPI void APIENTRY glMultiTexCoord1xOES (GLenum texture, GLfixed s); -GLAPI void APIENTRY glMultiTexCoord1xvOES (GLenum texture, const GLfixed *coords); -GLAPI void APIENTRY glMultiTexCoord2xOES (GLenum texture, GLfixed s, GLfixed t); -GLAPI void APIENTRY glMultiTexCoord2xvOES (GLenum texture, const GLfixed *coords); -GLAPI void APIENTRY glMultiTexCoord3xOES (GLenum texture, GLfixed s, GLfixed t, GLfixed r); -GLAPI void APIENTRY glMultiTexCoord3xvOES (GLenum texture, const GLfixed *coords); -GLAPI void APIENTRY glMultiTexCoord4xvOES (GLenum texture, const GLfixed *coords); -GLAPI void APIENTRY glNormal3xvOES (const GLfixed *coords); -GLAPI void APIENTRY glPassThroughxOES (GLfixed token); -GLAPI void APIENTRY glPixelMapx (GLenum map, GLint size, const GLfixed *values); -GLAPI void APIENTRY glPixelStorex (GLenum pname, GLfixed param); -GLAPI void APIENTRY glPixelTransferxOES (GLenum pname, GLfixed param); -GLAPI void APIENTRY glPixelZoomxOES (GLfixed xfactor, GLfixed yfactor); -GLAPI void APIENTRY glPrioritizeTexturesxOES (GLsizei n, const GLuint *textures, const GLfixed *priorities); -GLAPI void APIENTRY glRasterPos2xOES (GLfixed x, GLfixed y); -GLAPI void APIENTRY glRasterPos2xvOES (const GLfixed *coords); -GLAPI void APIENTRY glRasterPos3xOES (GLfixed x, GLfixed y, GLfixed z); -GLAPI void APIENTRY glRasterPos3xvOES (const GLfixed *coords); -GLAPI void APIENTRY glRasterPos4xOES (GLfixed x, GLfixed y, GLfixed z, GLfixed w); -GLAPI void APIENTRY glRasterPos4xvOES (const GLfixed *coords); -GLAPI void APIENTRY glRectxOES (GLfixed x1, GLfixed y1, GLfixed x2, GLfixed y2); -GLAPI void APIENTRY glRectxvOES (const GLfixed *v1, const GLfixed *v2); -GLAPI void APIENTRY glTexCoord1xOES (GLfixed s); -GLAPI void APIENTRY glTexCoord1xvOES (const GLfixed *coords); -GLAPI void APIENTRY glTexCoord2xOES (GLfixed s, GLfixed t); -GLAPI void APIENTRY glTexCoord2xvOES (const GLfixed *coords); -GLAPI void APIENTRY glTexCoord3xOES (GLfixed s, GLfixed t, GLfixed r); -GLAPI void APIENTRY glTexCoord3xvOES (const GLfixed *coords); -GLAPI void APIENTRY glTexCoord4xOES (GLfixed s, GLfixed t, GLfixed r, GLfixed q); -GLAPI void APIENTRY glTexCoord4xvOES (const GLfixed *coords); -GLAPI void APIENTRY glTexGenxOES (GLenum coord, GLenum pname, GLfixed param); -GLAPI void APIENTRY glTexGenxvOES (GLenum coord, GLenum pname, const GLfixed *params); -GLAPI void APIENTRY glVertex2xOES (GLfixed x); -GLAPI void APIENTRY glVertex2xvOES (const GLfixed *coords); -GLAPI void APIENTRY glVertex3xOES (GLfixed x, GLfixed y); -GLAPI void APIENTRY glVertex3xvOES (const GLfixed *coords); -GLAPI void APIENTRY glVertex4xOES (GLfixed x, GLfixed y, GLfixed z); -GLAPI void APIENTRY glVertex4xvOES (const GLfixed *coords); -#endif -#endif /* GL_OES_fixed_point */ - -#ifndef GL_OES_query_matrix -#define GL_OES_query_matrix 1 -typedef GLbitfield (APIENTRYP PFNGLQUERYMATRIXXOESPROC) (GLfixed *mantissa, GLint *exponent); -#ifdef GL_GLEXT_PROTOTYPES -GLAPI GLbitfield APIENTRY glQueryMatrixxOES (GLfixed *mantissa, GLint *exponent); -#endif -#endif /* GL_OES_query_matrix */ - -#ifndef GL_OES_read_format -#define GL_OES_read_format 1 -#define GL_IMPLEMENTATION_COLOR_READ_TYPE_OES 0x8B9A -#define GL_IMPLEMENTATION_COLOR_READ_FORMAT_OES 0x8B9B -#endif /* GL_OES_read_format */ - -#ifndef GL_OES_single_precision -#define GL_OES_single_precision 1 -typedef void (APIENTRYP PFNGLCLEARDEPTHFOESPROC) (GLclampf depth); -typedef void (APIENTRYP PFNGLCLIPPLANEFOESPROC) (GLenum plane, const GLfloat *equation); -typedef void (APIENTRYP PFNGLDEPTHRANGEFOESPROC) (GLclampf n, GLclampf f); -typedef void (APIENTRYP PFNGLFRUSTUMFOESPROC) (GLfloat l, GLfloat r, GLfloat b, GLfloat t, GLfloat n, GLfloat f); -typedef void (APIENTRYP PFNGLGETCLIPPLANEFOESPROC) (GLenum plane, GLfloat *equation); -typedef void (APIENTRYP PFNGLORTHOFOESPROC) (GLfloat l, GLfloat r, GLfloat b, GLfloat t, GLfloat n, GLfloat f); -#ifdef GL_GLEXT_PROTOTYPES -GLAPI void APIENTRY glClearDepthfOES (GLclampf depth); -GLAPI void APIENTRY glClipPlanefOES (GLenum plane, const GLfloat *equation); -GLAPI void APIENTRY glDepthRangefOES (GLclampf n, GLclampf f); -GLAPI void APIENTRY glFrustumfOES (GLfloat l, GLfloat r, GLfloat b, GLfloat t, GLfloat n, GLfloat f); -GLAPI void APIENTRY glGetClipPlanefOES (GLenum plane, GLfloat *equation); -GLAPI void APIENTRY glOrthofOES (GLfloat l, GLfloat r, GLfloat b, GLfloat t, GLfloat n, GLfloat f); -#endif -#endif /* GL_OES_single_precision */ - -#ifndef GL_3DFX_multisample -#define GL_3DFX_multisample 1 -#define GL_MULTISAMPLE_3DFX 0x86B2 -#define GL_SAMPLE_BUFFERS_3DFX 0x86B3 -#define GL_SAMPLES_3DFX 0x86B4 -#define GL_MULTISAMPLE_BIT_3DFX 0x20000000 -#endif /* GL_3DFX_multisample */ - -#ifndef GL_3DFX_tbuffer -#define GL_3DFX_tbuffer 1 -typedef void (APIENTRYP PFNGLTBUFFERMASK3DFXPROC) (GLuint mask); -#ifdef GL_GLEXT_PROTOTYPES -GLAPI void APIENTRY glTbufferMask3DFX (GLuint mask); -#endif -#endif /* GL_3DFX_tbuffer */ - -#ifndef GL_3DFX_texture_compression_FXT1 -#define GL_3DFX_texture_compression_FXT1 1 -#define GL_COMPRESSED_RGB_FXT1_3DFX 0x86B0 -#define GL_COMPRESSED_RGBA_FXT1_3DFX 0x86B1 -#endif /* GL_3DFX_texture_compression_FXT1 */ - -#ifndef GL_AMD_blend_minmax_factor -#define GL_AMD_blend_minmax_factor 1 -#define GL_FACTOR_MIN_AMD 0x901C -#define GL_FACTOR_MAX_AMD 0x901D -#endif /* GL_AMD_blend_minmax_factor */ - -#ifndef GL_AMD_conservative_depth -#define GL_AMD_conservative_depth 1 -#endif /* GL_AMD_conservative_depth */ - -#ifndef GL_AMD_debug_output -#define GL_AMD_debug_output 1 -typedef void (APIENTRY *GLDEBUGPROCAMD)(GLuint id,GLenum category,GLenum severity,GLsizei length,const GLchar *message,void *userParam); -#define GL_MAX_DEBUG_MESSAGE_LENGTH_AMD 0x9143 -#define GL_MAX_DEBUG_LOGGED_MESSAGES_AMD 0x9144 -#define GL_DEBUG_LOGGED_MESSAGES_AMD 0x9145 -#define GL_DEBUG_SEVERITY_HIGH_AMD 0x9146 -#define GL_DEBUG_SEVERITY_MEDIUM_AMD 0x9147 -#define GL_DEBUG_SEVERITY_LOW_AMD 0x9148 -#define GL_DEBUG_CATEGORY_API_ERROR_AMD 0x9149 -#define GL_DEBUG_CATEGORY_WINDOW_SYSTEM_AMD 0x914A -#define GL_DEBUG_CATEGORY_DEPRECATION_AMD 0x914B -#define GL_DEBUG_CATEGORY_UNDEFINED_BEHAVIOR_AMD 0x914C -#define GL_DEBUG_CATEGORY_PERFORMANCE_AMD 0x914D -#define GL_DEBUG_CATEGORY_SHADER_COMPILER_AMD 0x914E -#define GL_DEBUG_CATEGORY_APPLICATION_AMD 0x914F -#define GL_DEBUG_CATEGORY_OTHER_AMD 0x9150 -typedef void (APIENTRYP PFNGLDEBUGMESSAGEENABLEAMDPROC) (GLenum category, GLenum severity, GLsizei count, const GLuint *ids, GLboolean enabled); -typedef void (APIENTRYP PFNGLDEBUGMESSAGEINSERTAMDPROC) (GLenum category, GLenum severity, GLuint id, GLsizei length, const GLchar *buf); -typedef void (APIENTRYP PFNGLDEBUGMESSAGECALLBACKAMDPROC) (GLDEBUGPROCAMD callback, void *userParam); -typedef GLuint (APIENTRYP PFNGLGETDEBUGMESSAGELOGAMDPROC) (GLuint count, GLsizei bufsize, GLenum *categories, GLuint *severities, GLuint *ids, GLsizei *lengths, GLchar *message); -#ifdef GL_GLEXT_PROTOTYPES -GLAPI void APIENTRY glDebugMessageEnableAMD (GLenum category, GLenum severity, GLsizei count, const GLuint *ids, GLboolean enabled); -GLAPI void APIENTRY glDebugMessageInsertAMD (GLenum category, GLenum severity, GLuint id, GLsizei length, const GLchar *buf); -GLAPI void APIENTRY glDebugMessageCallbackAMD (GLDEBUGPROCAMD callback, void *userParam); -GLAPI GLuint APIENTRY glGetDebugMessageLogAMD (GLuint count, GLsizei bufsize, GLenum *categories, GLuint *severities, GLuint *ids, GLsizei *lengths, GLchar *message); -#endif -#endif /* GL_AMD_debug_output */ - -#ifndef GL_AMD_depth_clamp_separate -#define GL_AMD_depth_clamp_separate 1 -#define GL_DEPTH_CLAMP_NEAR_AMD 0x901E -#define GL_DEPTH_CLAMP_FAR_AMD 0x901F -#endif /* GL_AMD_depth_clamp_separate */ - -#ifndef GL_AMD_draw_buffers_blend -#define GL_AMD_draw_buffers_blend 1 -typedef void (APIENTRYP PFNGLBLENDFUNCINDEXEDAMDPROC) (GLuint buf, GLenum src, GLenum dst); -typedef void (APIENTRYP PFNGLBLENDFUNCSEPARATEINDEXEDAMDPROC) (GLuint buf, GLenum srcRGB, GLenum dstRGB, GLenum srcAlpha, GLenum dstAlpha); -typedef void (APIENTRYP PFNGLBLENDEQUATIONINDEXEDAMDPROC) (GLuint buf, GLenum mode); -typedef void (APIENTRYP PFNGLBLENDEQUATIONSEPARATEINDEXEDAMDPROC) (GLuint buf, GLenum modeRGB, GLenum modeAlpha); -#ifdef GL_GLEXT_PROTOTYPES -GLAPI void APIENTRY glBlendFuncIndexedAMD (GLuint buf, GLenum src, GLenum dst); -GLAPI void APIENTRY glBlendFuncSeparateIndexedAMD (GLuint buf, GLenum srcRGB, GLenum dstRGB, GLenum srcAlpha, GLenum dstAlpha); -GLAPI void APIENTRY glBlendEquationIndexedAMD (GLuint buf, GLenum mode); -GLAPI void APIENTRY glBlendEquationSeparateIndexedAMD (GLuint buf, GLenum modeRGB, GLenum modeAlpha); -#endif -#endif /* GL_AMD_draw_buffers_blend */ - -#ifndef GL_AMD_interleaved_elements -#define GL_AMD_interleaved_elements 1 -#define GL_VERTEX_ELEMENT_SWIZZLE_AMD 0x91A4 -#define GL_VERTEX_ID_SWIZZLE_AMD 0x91A5 -typedef void (APIENTRYP PFNGLVERTEXATTRIBPARAMETERIAMDPROC) (GLuint index, GLenum pname, GLint param); -#ifdef GL_GLEXT_PROTOTYPES -GLAPI void APIENTRY glVertexAttribParameteriAMD (GLuint index, GLenum pname, GLint param); -#endif -#endif /* GL_AMD_interleaved_elements */ - -#ifndef GL_AMD_multi_draw_indirect -#define GL_AMD_multi_draw_indirect 1 -typedef void (APIENTRYP PFNGLMULTIDRAWARRAYSINDIRECTAMDPROC) (GLenum mode, const void *indirect, GLsizei primcount, GLsizei stride); -typedef void (APIENTRYP PFNGLMULTIDRAWELEMENTSINDIRECTAMDPROC) (GLenum mode, GLenum type, const void *indirect, GLsizei primcount, GLsizei stride); -#ifdef GL_GLEXT_PROTOTYPES -GLAPI void APIENTRY glMultiDrawArraysIndirectAMD (GLenum mode, const void *indirect, GLsizei primcount, GLsizei stride); -GLAPI void APIENTRY glMultiDrawElementsIndirectAMD (GLenum mode, GLenum type, const void *indirect, GLsizei primcount, GLsizei stride); -#endif -#endif /* GL_AMD_multi_draw_indirect */ - -#ifndef GL_AMD_name_gen_delete -#define GL_AMD_name_gen_delete 1 -#define GL_DATA_BUFFER_AMD 0x9151 -#define GL_PERFORMANCE_MONITOR_AMD 0x9152 -#define GL_QUERY_OBJECT_AMD 0x9153 -#define GL_VERTEX_ARRAY_OBJECT_AMD 0x9154 -#define GL_SAMPLER_OBJECT_AMD 0x9155 -typedef void (APIENTRYP PFNGLGENNAMESAMDPROC) (GLenum identifier, GLuint num, GLuint *names); -typedef void (APIENTRYP PFNGLDELETENAMESAMDPROC) (GLenum identifier, GLuint num, const GLuint *names); -typedef GLboolean (APIENTRYP PFNGLISNAMEAMDPROC) (GLenum identifier, GLuint name); -#ifdef GL_GLEXT_PROTOTYPES -GLAPI void APIENTRY glGenNamesAMD (GLenum identifier, GLuint num, GLuint *names); -GLAPI void APIENTRY glDeleteNamesAMD (GLenum identifier, GLuint num, const GLuint *names); -GLAPI GLboolean APIENTRY glIsNameAMD (GLenum identifier, GLuint name); -#endif -#endif /* GL_AMD_name_gen_delete */ - -#ifndef GL_AMD_occlusion_query_event -#define GL_AMD_occlusion_query_event 1 -#define GL_OCCLUSION_QUERY_EVENT_MASK_AMD 0x874F -#define GL_QUERY_DEPTH_PASS_EVENT_BIT_AMD 0x00000001 -#define GL_QUERY_DEPTH_FAIL_EVENT_BIT_AMD 0x00000002 -#define GL_QUERY_STENCIL_FAIL_EVENT_BIT_AMD 0x00000004 -#define GL_QUERY_DEPTH_BOUNDS_FAIL_EVENT_BIT_AMD 0x00000008 -#define GL_QUERY_ALL_EVENT_BITS_AMD 0xFFFFFFFF -typedef void (APIENTRYP PFNGLQUERYOBJECTPARAMETERUIAMDPROC) (GLenum target, GLuint id, GLenum pname, GLuint param); -#ifdef GL_GLEXT_PROTOTYPES -GLAPI void APIENTRY glQueryObjectParameteruiAMD (GLenum target, GLuint id, GLenum pname, GLuint param); -#endif -#endif /* GL_AMD_occlusion_query_event */ - -#ifndef GL_AMD_performance_monitor -#define GL_AMD_performance_monitor 1 -#define GL_COUNTER_TYPE_AMD 0x8BC0 -#define GL_COUNTER_RANGE_AMD 0x8BC1 -#define GL_UNSIGNED_INT64_AMD 0x8BC2 -#define GL_PERCENTAGE_AMD 0x8BC3 -#define GL_PERFMON_RESULT_AVAILABLE_AMD 0x8BC4 -#define GL_PERFMON_RESULT_SIZE_AMD 0x8BC5 -#define GL_PERFMON_RESULT_AMD 0x8BC6 -typedef void (APIENTRYP PFNGLGETPERFMONITORGROUPSAMDPROC) (GLint *numGroups, GLsizei groupsSize, GLuint *groups); -typedef void (APIENTRYP PFNGLGETPERFMONITORCOUNTERSAMDPROC) (GLuint group, GLint *numCounters, GLint *maxActiveCounters, GLsizei counterSize, GLuint *counters); -typedef void (APIENTRYP PFNGLGETPERFMONITORGROUPSTRINGAMDPROC) (GLuint group, GLsizei bufSize, GLsizei *length, GLchar *groupString); -typedef void (APIENTRYP PFNGLGETPERFMONITORCOUNTERSTRINGAMDPROC) (GLuint group, GLuint counter, GLsizei bufSize, GLsizei *length, GLchar *counterString); -typedef void (APIENTRYP PFNGLGETPERFMONITORCOUNTERINFOAMDPROC) (GLuint group, GLuint counter, GLenum pname, void *data); -typedef void (APIENTRYP PFNGLGENPERFMONITORSAMDPROC) (GLsizei n, GLuint *monitors); -typedef void (APIENTRYP PFNGLDELETEPERFMONITORSAMDPROC) (GLsizei n, GLuint *monitors); -typedef void (APIENTRYP PFNGLSELECTPERFMONITORCOUNTERSAMDPROC) (GLuint monitor, GLboolean enable, GLuint group, GLint numCounters, GLuint *counterList); -typedef void (APIENTRYP PFNGLBEGINPERFMONITORAMDPROC) (GLuint monitor); -typedef void (APIENTRYP PFNGLENDPERFMONITORAMDPROC) (GLuint monitor); -typedef void (APIENTRYP PFNGLGETPERFMONITORCOUNTERDATAAMDPROC) (GLuint monitor, GLenum pname, GLsizei dataSize, GLuint *data, GLint *bytesWritten); -#ifdef GL_GLEXT_PROTOTYPES -GLAPI void APIENTRY glGetPerfMonitorGroupsAMD (GLint *numGroups, GLsizei groupsSize, GLuint *groups); -GLAPI void APIENTRY glGetPerfMonitorCountersAMD (GLuint group, GLint *numCounters, GLint *maxActiveCounters, GLsizei counterSize, GLuint *counters); -GLAPI void APIENTRY glGetPerfMonitorGroupStringAMD (GLuint group, GLsizei bufSize, GLsizei *length, GLchar *groupString); -GLAPI void APIENTRY glGetPerfMonitorCounterStringAMD (GLuint group, GLuint counter, GLsizei bufSize, GLsizei *length, GLchar *counterString); -GLAPI void APIENTRY glGetPerfMonitorCounterInfoAMD (GLuint group, GLuint counter, GLenum pname, void *data); -GLAPI void APIENTRY glGenPerfMonitorsAMD (GLsizei n, GLuint *monitors); -GLAPI void APIENTRY glDeletePerfMonitorsAMD (GLsizei n, GLuint *monitors); -GLAPI void APIENTRY glSelectPerfMonitorCountersAMD (GLuint monitor, GLboolean enable, GLuint group, GLint numCounters, GLuint *counterList); -GLAPI void APIENTRY glBeginPerfMonitorAMD (GLuint monitor); -GLAPI void APIENTRY glEndPerfMonitorAMD (GLuint monitor); -GLAPI void APIENTRY glGetPerfMonitorCounterDataAMD (GLuint monitor, GLenum pname, GLsizei dataSize, GLuint *data, GLint *bytesWritten); -#endif -#endif /* GL_AMD_performance_monitor */ - -#ifndef GL_AMD_pinned_memory -#define GL_AMD_pinned_memory 1 -#define GL_EXTERNAL_VIRTUAL_MEMORY_BUFFER_AMD 0x9160 -#endif /* GL_AMD_pinned_memory */ - -#ifndef GL_AMD_query_buffer_object -#define GL_AMD_query_buffer_object 1 -#define GL_QUERY_BUFFER_AMD 0x9192 -#define GL_QUERY_BUFFER_BINDING_AMD 0x9193 -#define GL_QUERY_RESULT_NO_WAIT_AMD 0x9194 -#endif /* GL_AMD_query_buffer_object */ - -#ifndef GL_AMD_sample_positions -#define GL_AMD_sample_positions 1 -#define GL_SUBSAMPLE_DISTANCE_AMD 0x883F -typedef void (APIENTRYP PFNGLSETMULTISAMPLEFVAMDPROC) (GLenum pname, GLuint index, const GLfloat *val); -#ifdef GL_GLEXT_PROTOTYPES -GLAPI void APIENTRY glSetMultisamplefvAMD (GLenum pname, GLuint index, const GLfloat *val); -#endif -#endif /* GL_AMD_sample_positions */ - -#ifndef GL_AMD_seamless_cubemap_per_texture -#define GL_AMD_seamless_cubemap_per_texture 1 -#endif /* GL_AMD_seamless_cubemap_per_texture */ - -#ifndef GL_AMD_shader_atomic_counter_ops -#define GL_AMD_shader_atomic_counter_ops 1 -#endif /* GL_AMD_shader_atomic_counter_ops */ - -#ifndef GL_AMD_shader_stencil_export -#define GL_AMD_shader_stencil_export 1 -#endif /* GL_AMD_shader_stencil_export */ - -#ifndef GL_AMD_shader_trinary_minmax -#define GL_AMD_shader_trinary_minmax 1 -#endif /* GL_AMD_shader_trinary_minmax */ - -#ifndef GL_AMD_sparse_texture -#define GL_AMD_sparse_texture 1 -#define GL_VIRTUAL_PAGE_SIZE_X_AMD 0x9195 -#define GL_VIRTUAL_PAGE_SIZE_Y_AMD 0x9196 -#define GL_VIRTUAL_PAGE_SIZE_Z_AMD 0x9197 -#define GL_MAX_SPARSE_TEXTURE_SIZE_AMD 0x9198 -#define GL_MAX_SPARSE_3D_TEXTURE_SIZE_AMD 0x9199 -#define GL_MAX_SPARSE_ARRAY_TEXTURE_LAYERS 0x919A -#define GL_MIN_SPARSE_LEVEL_AMD 0x919B -#define GL_MIN_LOD_WARNING_AMD 0x919C -#define GL_TEXTURE_STORAGE_SPARSE_BIT_AMD 0x00000001 -typedef void (APIENTRYP PFNGLTEXSTORAGESPARSEAMDPROC) (GLenum target, GLenum internalFormat, GLsizei width, GLsizei height, GLsizei depth, GLsizei layers, GLbitfield flags); -typedef void (APIENTRYP PFNGLTEXTURESTORAGESPARSEAMDPROC) (GLuint texture, GLenum target, GLenum internalFormat, GLsizei width, GLsizei height, GLsizei depth, GLsizei layers, GLbitfield flags); -#ifdef GL_GLEXT_PROTOTYPES -GLAPI void APIENTRY glTexStorageSparseAMD (GLenum target, GLenum internalFormat, GLsizei width, GLsizei height, GLsizei depth, GLsizei layers, GLbitfield flags); -GLAPI void APIENTRY glTextureStorageSparseAMD (GLuint texture, GLenum target, GLenum internalFormat, GLsizei width, GLsizei height, GLsizei depth, GLsizei layers, GLbitfield flags); -#endif -#endif /* GL_AMD_sparse_texture */ - -#ifndef GL_AMD_stencil_operation_extended -#define GL_AMD_stencil_operation_extended 1 -#define GL_SET_AMD 0x874A -#define GL_REPLACE_VALUE_AMD 0x874B -#define GL_STENCIL_OP_VALUE_AMD 0x874C -#define GL_STENCIL_BACK_OP_VALUE_AMD 0x874D -typedef void (APIENTRYP PFNGLSTENCILOPVALUEAMDPROC) (GLenum face, GLuint value); -#ifdef GL_GLEXT_PROTOTYPES -GLAPI void APIENTRY glStencilOpValueAMD (GLenum face, GLuint value); -#endif -#endif /* GL_AMD_stencil_operation_extended */ - -#ifndef GL_AMD_texture_texture4 -#define GL_AMD_texture_texture4 1 -#endif /* GL_AMD_texture_texture4 */ - -#ifndef GL_AMD_transform_feedback3_lines_triangles -#define GL_AMD_transform_feedback3_lines_triangles 1 -#endif /* GL_AMD_transform_feedback3_lines_triangles */ - -#ifndef GL_AMD_vertex_shader_layer -#define GL_AMD_vertex_shader_layer 1 -#endif /* GL_AMD_vertex_shader_layer */ - -#ifndef GL_AMD_vertex_shader_tessellator -#define GL_AMD_vertex_shader_tessellator 1 -#define GL_SAMPLER_BUFFER_AMD 0x9001 -#define GL_INT_SAMPLER_BUFFER_AMD 0x9002 -#define GL_UNSIGNED_INT_SAMPLER_BUFFER_AMD 0x9003 -#define GL_TESSELLATION_MODE_AMD 0x9004 -#define GL_TESSELLATION_FACTOR_AMD 0x9005 -#define GL_DISCRETE_AMD 0x9006 -#define GL_CONTINUOUS_AMD 0x9007 -typedef void (APIENTRYP PFNGLTESSELLATIONFACTORAMDPROC) (GLfloat factor); -typedef void (APIENTRYP PFNGLTESSELLATIONMODEAMDPROC) (GLenum mode); -#ifdef GL_GLEXT_PROTOTYPES -GLAPI void APIENTRY glTessellationFactorAMD (GLfloat factor); -GLAPI void APIENTRY glTessellationModeAMD (GLenum mode); -#endif -#endif /* GL_AMD_vertex_shader_tessellator */ - -#ifndef GL_AMD_vertex_shader_viewport_index -#define GL_AMD_vertex_shader_viewport_index 1 -#endif /* GL_AMD_vertex_shader_viewport_index */ - -#ifndef GL_APPLE_aux_depth_stencil -#define GL_APPLE_aux_depth_stencil 1 -#define GL_AUX_DEPTH_STENCIL_APPLE 0x8A14 -#endif /* GL_APPLE_aux_depth_stencil */ - -#ifndef GL_APPLE_client_storage -#define GL_APPLE_client_storage 1 -#define GL_UNPACK_CLIENT_STORAGE_APPLE 0x85B2 -#endif /* GL_APPLE_client_storage */ - -#ifndef GL_APPLE_element_array -#define GL_APPLE_element_array 1 -#define GL_ELEMENT_ARRAY_APPLE 0x8A0C -#define GL_ELEMENT_ARRAY_TYPE_APPLE 0x8A0D -#define GL_ELEMENT_ARRAY_POINTER_APPLE 0x8A0E -typedef void (APIENTRYP PFNGLELEMENTPOINTERAPPLEPROC) (GLenum type, const void *pointer); -typedef void (APIENTRYP PFNGLDRAWELEMENTARRAYAPPLEPROC) (GLenum mode, GLint first, GLsizei count); -typedef void (APIENTRYP PFNGLDRAWRANGEELEMENTARRAYAPPLEPROC) (GLenum mode, GLuint start, GLuint end, GLint first, GLsizei count); -typedef void (APIENTRYP PFNGLMULTIDRAWELEMENTARRAYAPPLEPROC) (GLenum mode, const GLint *first, const GLsizei *count, GLsizei primcount); -typedef void (APIENTRYP PFNGLMULTIDRAWRANGEELEMENTARRAYAPPLEPROC) (GLenum mode, GLuint start, GLuint end, const GLint *first, const GLsizei *count, GLsizei primcount); -#ifdef GL_GLEXT_PROTOTYPES -GLAPI void APIENTRY glElementPointerAPPLE (GLenum type, const void *pointer); -GLAPI void APIENTRY glDrawElementArrayAPPLE (GLenum mode, GLint first, GLsizei count); -GLAPI void APIENTRY glDrawRangeElementArrayAPPLE (GLenum mode, GLuint start, GLuint end, GLint first, GLsizei count); -GLAPI void APIENTRY glMultiDrawElementArrayAPPLE (GLenum mode, const GLint *first, const GLsizei *count, GLsizei primcount); -GLAPI void APIENTRY glMultiDrawRangeElementArrayAPPLE (GLenum mode, GLuint start, GLuint end, const GLint *first, const GLsizei *count, GLsizei primcount); -#endif -#endif /* GL_APPLE_element_array */ - -#ifndef GL_APPLE_fence -#define GL_APPLE_fence 1 -#define GL_DRAW_PIXELS_APPLE 0x8A0A -#define GL_FENCE_APPLE 0x8A0B -typedef void (APIENTRYP PFNGLGENFENCESAPPLEPROC) (GLsizei n, GLuint *fences); -typedef void (APIENTRYP PFNGLDELETEFENCESAPPLEPROC) (GLsizei n, const GLuint *fences); -typedef void (APIENTRYP PFNGLSETFENCEAPPLEPROC) (GLuint fence); -typedef GLboolean (APIENTRYP PFNGLISFENCEAPPLEPROC) (GLuint fence); -typedef GLboolean (APIENTRYP PFNGLTESTFENCEAPPLEPROC) (GLuint fence); -typedef void (APIENTRYP PFNGLFINISHFENCEAPPLEPROC) (GLuint fence); -typedef GLboolean (APIENTRYP PFNGLTESTOBJECTAPPLEPROC) (GLenum object, GLuint name); -typedef void (APIENTRYP PFNGLFINISHOBJECTAPPLEPROC) (GLenum object, GLint name); -#ifdef GL_GLEXT_PROTOTYPES -GLAPI void APIENTRY glGenFencesAPPLE (GLsizei n, GLuint *fences); -GLAPI void APIENTRY glDeleteFencesAPPLE (GLsizei n, const GLuint *fences); -GLAPI void APIENTRY glSetFenceAPPLE (GLuint fence); -GLAPI GLboolean APIENTRY glIsFenceAPPLE (GLuint fence); -GLAPI GLboolean APIENTRY glTestFenceAPPLE (GLuint fence); -GLAPI void APIENTRY glFinishFenceAPPLE (GLuint fence); -GLAPI GLboolean APIENTRY glTestObjectAPPLE (GLenum object, GLuint name); -GLAPI void APIENTRY glFinishObjectAPPLE (GLenum object, GLint name); -#endif -#endif /* GL_APPLE_fence */ - -#ifndef GL_APPLE_float_pixels -#define GL_APPLE_float_pixels 1 -#define GL_HALF_APPLE 0x140B -#define GL_RGBA_FLOAT32_APPLE 0x8814 -#define GL_RGB_FLOAT32_APPLE 0x8815 -#define GL_ALPHA_FLOAT32_APPLE 0x8816 -#define GL_INTENSITY_FLOAT32_APPLE 0x8817 -#define GL_LUMINANCE_FLOAT32_APPLE 0x8818 -#define GL_LUMINANCE_ALPHA_FLOAT32_APPLE 0x8819 -#define GL_RGBA_FLOAT16_APPLE 0x881A -#define GL_RGB_FLOAT16_APPLE 0x881B -#define GL_ALPHA_FLOAT16_APPLE 0x881C -#define GL_INTENSITY_FLOAT16_APPLE 0x881D -#define GL_LUMINANCE_FLOAT16_APPLE 0x881E -#define GL_LUMINANCE_ALPHA_FLOAT16_APPLE 0x881F -#define GL_COLOR_FLOAT_APPLE 0x8A0F -#endif /* GL_APPLE_float_pixels */ - -#ifndef GL_APPLE_flush_buffer_range -#define GL_APPLE_flush_buffer_range 1 -#define GL_BUFFER_SERIALIZED_MODIFY_APPLE 0x8A12 -#define GL_BUFFER_FLUSHING_UNMAP_APPLE 0x8A13 -typedef void (APIENTRYP PFNGLBUFFERPARAMETERIAPPLEPROC) (GLenum target, GLenum pname, GLint param); -typedef void (APIENTRYP PFNGLFLUSHMAPPEDBUFFERRANGEAPPLEPROC) (GLenum target, GLintptr offset, GLsizeiptr size); -#ifdef GL_GLEXT_PROTOTYPES -GLAPI void APIENTRY glBufferParameteriAPPLE (GLenum target, GLenum pname, GLint param); -GLAPI void APIENTRY glFlushMappedBufferRangeAPPLE (GLenum target, GLintptr offset, GLsizeiptr size); -#endif -#endif /* GL_APPLE_flush_buffer_range */ - -#ifndef GL_APPLE_object_purgeable -#define GL_APPLE_object_purgeable 1 -#define GL_BUFFER_OBJECT_APPLE 0x85B3 -#define GL_RELEASED_APPLE 0x8A19 -#define GL_VOLATILE_APPLE 0x8A1A -#define GL_RETAINED_APPLE 0x8A1B -#define GL_UNDEFINED_APPLE 0x8A1C -#define GL_PURGEABLE_APPLE 0x8A1D -typedef GLenum (APIENTRYP PFNGLOBJECTPURGEABLEAPPLEPROC) (GLenum objectType, GLuint name, GLenum option); -typedef GLenum (APIENTRYP PFNGLOBJECTUNPURGEABLEAPPLEPROC) (GLenum objectType, GLuint name, GLenum option); -typedef void (APIENTRYP PFNGLGETOBJECTPARAMETERIVAPPLEPROC) (GLenum objectType, GLuint name, GLenum pname, GLint *params); -#ifdef GL_GLEXT_PROTOTYPES -GLAPI GLenum APIENTRY glObjectPurgeableAPPLE (GLenum objectType, GLuint name, GLenum option); -GLAPI GLenum APIENTRY glObjectUnpurgeableAPPLE (GLenum objectType, GLuint name, GLenum option); -GLAPI void APIENTRY glGetObjectParameterivAPPLE (GLenum objectType, GLuint name, GLenum pname, GLint *params); -#endif -#endif /* GL_APPLE_object_purgeable */ - -#ifndef GL_APPLE_rgb_422 -#define GL_APPLE_rgb_422 1 -#define GL_RGB_422_APPLE 0x8A1F -#define GL_UNSIGNED_SHORT_8_8_APPLE 0x85BA -#define GL_UNSIGNED_SHORT_8_8_REV_APPLE 0x85BB -#define GL_RGB_RAW_422_APPLE 0x8A51 -#endif /* GL_APPLE_rgb_422 */ - -#ifndef GL_APPLE_row_bytes -#define GL_APPLE_row_bytes 1 -#define GL_PACK_ROW_BYTES_APPLE 0x8A15 -#define GL_UNPACK_ROW_BYTES_APPLE 0x8A16 -#endif /* GL_APPLE_row_bytes */ - -#ifndef GL_APPLE_specular_vector -#define GL_APPLE_specular_vector 1 -#define GL_LIGHT_MODEL_SPECULAR_VECTOR_APPLE 0x85B0 -#endif /* GL_APPLE_specular_vector */ - -#ifndef GL_APPLE_texture_range -#define GL_APPLE_texture_range 1 -#define GL_TEXTURE_RANGE_LENGTH_APPLE 0x85B7 -#define GL_TEXTURE_RANGE_POINTER_APPLE 0x85B8 -#define GL_TEXTURE_STORAGE_HINT_APPLE 0x85BC -#define GL_STORAGE_PRIVATE_APPLE 0x85BD -#define GL_STORAGE_CACHED_APPLE 0x85BE -#define GL_STORAGE_SHARED_APPLE 0x85BF -typedef void (APIENTRYP PFNGLTEXTURERANGEAPPLEPROC) (GLenum target, GLsizei length, const void *pointer); -typedef void (APIENTRYP PFNGLGETTEXPARAMETERPOINTERVAPPLEPROC) (GLenum target, GLenum pname, void **params); -#ifdef GL_GLEXT_PROTOTYPES -GLAPI void APIENTRY glTextureRangeAPPLE (GLenum target, GLsizei length, const void *pointer); -GLAPI void APIENTRY glGetTexParameterPointervAPPLE (GLenum target, GLenum pname, void **params); -#endif -#endif /* GL_APPLE_texture_range */ - -#ifndef GL_APPLE_transform_hint -#define GL_APPLE_transform_hint 1 -#define GL_TRANSFORM_HINT_APPLE 0x85B1 -#endif /* GL_APPLE_transform_hint */ - -#ifndef GL_APPLE_vertex_array_object -#define GL_APPLE_vertex_array_object 1 -#define GL_VERTEX_ARRAY_BINDING_APPLE 0x85B5 -typedef void (APIENTRYP PFNGLBINDVERTEXARRAYAPPLEPROC) (GLuint array); -typedef void (APIENTRYP PFNGLDELETEVERTEXARRAYSAPPLEPROC) (GLsizei n, const GLuint *arrays); -typedef void (APIENTRYP PFNGLGENVERTEXARRAYSAPPLEPROC) (GLsizei n, GLuint *arrays); -typedef GLboolean (APIENTRYP PFNGLISVERTEXARRAYAPPLEPROC) (GLuint array); -#ifdef GL_GLEXT_PROTOTYPES -GLAPI void APIENTRY glBindVertexArrayAPPLE (GLuint array); -GLAPI void APIENTRY glDeleteVertexArraysAPPLE (GLsizei n, const GLuint *arrays); -GLAPI void APIENTRY glGenVertexArraysAPPLE (GLsizei n, GLuint *arrays); -GLAPI GLboolean APIENTRY glIsVertexArrayAPPLE (GLuint array); -#endif -#endif /* GL_APPLE_vertex_array_object */ - -#ifndef GL_APPLE_vertex_array_range -#define GL_APPLE_vertex_array_range 1 -#define GL_VERTEX_ARRAY_RANGE_APPLE 0x851D -#define GL_VERTEX_ARRAY_RANGE_LENGTH_APPLE 0x851E -#define GL_VERTEX_ARRAY_STORAGE_HINT_APPLE 0x851F -#define GL_VERTEX_ARRAY_RANGE_POINTER_APPLE 0x8521 -#define GL_STORAGE_CLIENT_APPLE 0x85B4 -typedef void (APIENTRYP PFNGLVERTEXARRAYRANGEAPPLEPROC) (GLsizei length, void *pointer); -typedef void (APIENTRYP PFNGLFLUSHVERTEXARRAYRANGEAPPLEPROC) (GLsizei length, void *pointer); -typedef void (APIENTRYP PFNGLVERTEXARRAYPARAMETERIAPPLEPROC) (GLenum pname, GLint param); -#ifdef GL_GLEXT_PROTOTYPES -GLAPI void APIENTRY glVertexArrayRangeAPPLE (GLsizei length, void *pointer); -GLAPI void APIENTRY glFlushVertexArrayRangeAPPLE (GLsizei length, void *pointer); -GLAPI void APIENTRY glVertexArrayParameteriAPPLE (GLenum pname, GLint param); -#endif -#endif /* GL_APPLE_vertex_array_range */ - -#ifndef GL_APPLE_vertex_program_evaluators -#define GL_APPLE_vertex_program_evaluators 1 -#define GL_VERTEX_ATTRIB_MAP1_APPLE 0x8A00 -#define GL_VERTEX_ATTRIB_MAP2_APPLE 0x8A01 -#define GL_VERTEX_ATTRIB_MAP1_SIZE_APPLE 0x8A02 -#define GL_VERTEX_ATTRIB_MAP1_COEFF_APPLE 0x8A03 -#define GL_VERTEX_ATTRIB_MAP1_ORDER_APPLE 0x8A04 -#define GL_VERTEX_ATTRIB_MAP1_DOMAIN_APPLE 0x8A05 -#define GL_VERTEX_ATTRIB_MAP2_SIZE_APPLE 0x8A06 -#define GL_VERTEX_ATTRIB_MAP2_COEFF_APPLE 0x8A07 -#define GL_VERTEX_ATTRIB_MAP2_ORDER_APPLE 0x8A08 -#define GL_VERTEX_ATTRIB_MAP2_DOMAIN_APPLE 0x8A09 -typedef void (APIENTRYP PFNGLENABLEVERTEXATTRIBAPPLEPROC) (GLuint index, GLenum pname); -typedef void (APIENTRYP PFNGLDISABLEVERTEXATTRIBAPPLEPROC) (GLuint index, GLenum pname); -typedef GLboolean (APIENTRYP PFNGLISVERTEXATTRIBENABLEDAPPLEPROC) (GLuint index, GLenum pname); -typedef void (APIENTRYP PFNGLMAPVERTEXATTRIB1DAPPLEPROC) (GLuint index, GLuint size, GLdouble u1, GLdouble u2, GLint stride, GLint order, const GLdouble *points); -typedef void (APIENTRYP PFNGLMAPVERTEXATTRIB1FAPPLEPROC) (GLuint index, GLuint size, GLfloat u1, GLfloat u2, GLint stride, GLint order, const GLfloat *points); -typedef void (APIENTRYP PFNGLMAPVERTEXATTRIB2DAPPLEPROC) (GLuint index, GLuint size, GLdouble u1, GLdouble u2, GLint ustride, GLint uorder, GLdouble v1, GLdouble v2, GLint vstride, GLint vorder, const GLdouble *points); -typedef void (APIENTRYP PFNGLMAPVERTEXATTRIB2FAPPLEPROC) (GLuint index, GLuint size, GLfloat u1, GLfloat u2, GLint ustride, GLint uorder, GLfloat v1, GLfloat v2, GLint vstride, GLint vorder, const GLfloat *points); -#ifdef GL_GLEXT_PROTOTYPES -GLAPI void APIENTRY glEnableVertexAttribAPPLE (GLuint index, GLenum pname); -GLAPI void APIENTRY glDisableVertexAttribAPPLE (GLuint index, GLenum pname); -GLAPI GLboolean APIENTRY glIsVertexAttribEnabledAPPLE (GLuint index, GLenum pname); -GLAPI void APIENTRY glMapVertexAttrib1dAPPLE (GLuint index, GLuint size, GLdouble u1, GLdouble u2, GLint stride, GLint order, const GLdouble *points); -GLAPI void APIENTRY glMapVertexAttrib1fAPPLE (GLuint index, GLuint size, GLfloat u1, GLfloat u2, GLint stride, GLint order, const GLfloat *points); -GLAPI void APIENTRY glMapVertexAttrib2dAPPLE (GLuint index, GLuint size, GLdouble u1, GLdouble u2, GLint ustride, GLint uorder, GLdouble v1, GLdouble v2, GLint vstride, GLint vorder, const GLdouble *points); -GLAPI void APIENTRY glMapVertexAttrib2fAPPLE (GLuint index, GLuint size, GLfloat u1, GLfloat u2, GLint ustride, GLint uorder, GLfloat v1, GLfloat v2, GLint vstride, GLint vorder, const GLfloat *points); -#endif -#endif /* GL_APPLE_vertex_program_evaluators */ - -#ifndef GL_APPLE_ycbcr_422 -#define GL_APPLE_ycbcr_422 1 -#define GL_YCBCR_422_APPLE 0x85B9 -#endif /* GL_APPLE_ycbcr_422 */ - -#ifndef GL_ATI_draw_buffers -#define GL_ATI_draw_buffers 1 -#define GL_MAX_DRAW_BUFFERS_ATI 0x8824 -#define GL_DRAW_BUFFER0_ATI 0x8825 -#define GL_DRAW_BUFFER1_ATI 0x8826 -#define GL_DRAW_BUFFER2_ATI 0x8827 -#define GL_DRAW_BUFFER3_ATI 0x8828 -#define GL_DRAW_BUFFER4_ATI 0x8829 -#define GL_DRAW_BUFFER5_ATI 0x882A -#define GL_DRAW_BUFFER6_ATI 0x882B -#define GL_DRAW_BUFFER7_ATI 0x882C -#define GL_DRAW_BUFFER8_ATI 0x882D -#define GL_DRAW_BUFFER9_ATI 0x882E -#define GL_DRAW_BUFFER10_ATI 0x882F -#define GL_DRAW_BUFFER11_ATI 0x8830 -#define GL_DRAW_BUFFER12_ATI 0x8831 -#define GL_DRAW_BUFFER13_ATI 0x8832 -#define GL_DRAW_BUFFER14_ATI 0x8833 -#define GL_DRAW_BUFFER15_ATI 0x8834 -typedef void (APIENTRYP PFNGLDRAWBUFFERSATIPROC) (GLsizei n, const GLenum *bufs); -#ifdef GL_GLEXT_PROTOTYPES -GLAPI void APIENTRY glDrawBuffersATI (GLsizei n, const GLenum *bufs); -#endif -#endif /* GL_ATI_draw_buffers */ - -#ifndef GL_ATI_element_array -#define GL_ATI_element_array 1 -#define GL_ELEMENT_ARRAY_ATI 0x8768 -#define GL_ELEMENT_ARRAY_TYPE_ATI 0x8769 -#define GL_ELEMENT_ARRAY_POINTER_ATI 0x876A -typedef void (APIENTRYP PFNGLELEMENTPOINTERATIPROC) (GLenum type, const void *pointer); -typedef void (APIENTRYP PFNGLDRAWELEMENTARRAYATIPROC) (GLenum mode, GLsizei count); -typedef void (APIENTRYP PFNGLDRAWRANGEELEMENTARRAYATIPROC) (GLenum mode, GLuint start, GLuint end, GLsizei count); -#ifdef GL_GLEXT_PROTOTYPES -GLAPI void APIENTRY glElementPointerATI (GLenum type, const void *pointer); -GLAPI void APIENTRY glDrawElementArrayATI (GLenum mode, GLsizei count); -GLAPI void APIENTRY glDrawRangeElementArrayATI (GLenum mode, GLuint start, GLuint end, GLsizei count); -#endif -#endif /* GL_ATI_element_array */ - -#ifndef GL_ATI_envmap_bumpmap -#define GL_ATI_envmap_bumpmap 1 -#define GL_BUMP_ROT_MATRIX_ATI 0x8775 -#define GL_BUMP_ROT_MATRIX_SIZE_ATI 0x8776 -#define GL_BUMP_NUM_TEX_UNITS_ATI 0x8777 -#define GL_BUMP_TEX_UNITS_ATI 0x8778 -#define GL_DUDV_ATI 0x8779 -#define GL_DU8DV8_ATI 0x877A -#define GL_BUMP_ENVMAP_ATI 0x877B -#define GL_BUMP_TARGET_ATI 0x877C -typedef void (APIENTRYP PFNGLTEXBUMPPARAMETERIVATIPROC) (GLenum pname, const GLint *param); -typedef void (APIENTRYP PFNGLTEXBUMPPARAMETERFVATIPROC) (GLenum pname, const GLfloat *param); -typedef void (APIENTRYP PFNGLGETTEXBUMPPARAMETERIVATIPROC) (GLenum pname, GLint *param); -typedef void (APIENTRYP PFNGLGETTEXBUMPPARAMETERFVATIPROC) (GLenum pname, GLfloat *param); -#ifdef GL_GLEXT_PROTOTYPES -GLAPI void APIENTRY glTexBumpParameterivATI (GLenum pname, const GLint *param); -GLAPI void APIENTRY glTexBumpParameterfvATI (GLenum pname, const GLfloat *param); -GLAPI void APIENTRY glGetTexBumpParameterivATI (GLenum pname, GLint *param); -GLAPI void APIENTRY glGetTexBumpParameterfvATI (GLenum pname, GLfloat *param); -#endif -#endif /* GL_ATI_envmap_bumpmap */ - -#ifndef GL_ATI_fragment_shader -#define GL_ATI_fragment_shader 1 -#define GL_FRAGMENT_SHADER_ATI 0x8920 -#define GL_REG_0_ATI 0x8921 -#define GL_REG_1_ATI 0x8922 -#define GL_REG_2_ATI 0x8923 -#define GL_REG_3_ATI 0x8924 -#define GL_REG_4_ATI 0x8925 -#define GL_REG_5_ATI 0x8926 -#define GL_REG_6_ATI 0x8927 -#define GL_REG_7_ATI 0x8928 -#define GL_REG_8_ATI 0x8929 -#define GL_REG_9_ATI 0x892A -#define GL_REG_10_ATI 0x892B -#define GL_REG_11_ATI 0x892C -#define GL_REG_12_ATI 0x892D -#define GL_REG_13_ATI 0x892E -#define GL_REG_14_ATI 0x892F -#define GL_REG_15_ATI 0x8930 -#define GL_REG_16_ATI 0x8931 -#define GL_REG_17_ATI 0x8932 -#define GL_REG_18_ATI 0x8933 -#define GL_REG_19_ATI 0x8934 -#define GL_REG_20_ATI 0x8935 -#define GL_REG_21_ATI 0x8936 -#define GL_REG_22_ATI 0x8937 -#define GL_REG_23_ATI 0x8938 -#define GL_REG_24_ATI 0x8939 -#define GL_REG_25_ATI 0x893A -#define GL_REG_26_ATI 0x893B -#define GL_REG_27_ATI 0x893C -#define GL_REG_28_ATI 0x893D -#define GL_REG_29_ATI 0x893E -#define GL_REG_30_ATI 0x893F -#define GL_REG_31_ATI 0x8940 -#define GL_CON_0_ATI 0x8941 -#define GL_CON_1_ATI 0x8942 -#define GL_CON_2_ATI 0x8943 -#define GL_CON_3_ATI 0x8944 -#define GL_CON_4_ATI 0x8945 -#define GL_CON_5_ATI 0x8946 -#define GL_CON_6_ATI 0x8947 -#define GL_CON_7_ATI 0x8948 -#define GL_CON_8_ATI 0x8949 -#define GL_CON_9_ATI 0x894A -#define GL_CON_10_ATI 0x894B -#define GL_CON_11_ATI 0x894C -#define GL_CON_12_ATI 0x894D -#define GL_CON_13_ATI 0x894E -#define GL_CON_14_ATI 0x894F -#define GL_CON_15_ATI 0x8950 -#define GL_CON_16_ATI 0x8951 -#define GL_CON_17_ATI 0x8952 -#define GL_CON_18_ATI 0x8953 -#define GL_CON_19_ATI 0x8954 -#define GL_CON_20_ATI 0x8955 -#define GL_CON_21_ATI 0x8956 -#define GL_CON_22_ATI 0x8957 -#define GL_CON_23_ATI 0x8958 -#define GL_CON_24_ATI 0x8959 -#define GL_CON_25_ATI 0x895A -#define GL_CON_26_ATI 0x895B -#define GL_CON_27_ATI 0x895C -#define GL_CON_28_ATI 0x895D -#define GL_CON_29_ATI 0x895E -#define GL_CON_30_ATI 0x895F -#define GL_CON_31_ATI 0x8960 -#define GL_MOV_ATI 0x8961 -#define GL_ADD_ATI 0x8963 -#define GL_MUL_ATI 0x8964 -#define GL_SUB_ATI 0x8965 -#define GL_DOT3_ATI 0x8966 -#define GL_DOT4_ATI 0x8967 -#define GL_MAD_ATI 0x8968 -#define GL_LERP_ATI 0x8969 -#define GL_CND_ATI 0x896A -#define GL_CND0_ATI 0x896B -#define GL_DOT2_ADD_ATI 0x896C -#define GL_SECONDARY_INTERPOLATOR_ATI 0x896D -#define GL_NUM_FRAGMENT_REGISTERS_ATI 0x896E -#define GL_NUM_FRAGMENT_CONSTANTS_ATI 0x896F -#define GL_NUM_PASSES_ATI 0x8970 -#define GL_NUM_INSTRUCTIONS_PER_PASS_ATI 0x8971 -#define GL_NUM_INSTRUCTIONS_TOTAL_ATI 0x8972 -#define GL_NUM_INPUT_INTERPOLATOR_COMPONENTS_ATI 0x8973 -#define GL_NUM_LOOPBACK_COMPONENTS_ATI 0x8974 -#define GL_COLOR_ALPHA_PAIRING_ATI 0x8975 -#define GL_SWIZZLE_STR_ATI 0x8976 -#define GL_SWIZZLE_STQ_ATI 0x8977 -#define GL_SWIZZLE_STR_DR_ATI 0x8978 -#define GL_SWIZZLE_STQ_DQ_ATI 0x8979 -#define GL_SWIZZLE_STRQ_ATI 0x897A -#define GL_SWIZZLE_STRQ_DQ_ATI 0x897B -#define GL_RED_BIT_ATI 0x00000001 -#define GL_GREEN_BIT_ATI 0x00000002 -#define GL_BLUE_BIT_ATI 0x00000004 -#define GL_2X_BIT_ATI 0x00000001 -#define GL_4X_BIT_ATI 0x00000002 -#define GL_8X_BIT_ATI 0x00000004 -#define GL_HALF_BIT_ATI 0x00000008 -#define GL_QUARTER_BIT_ATI 0x00000010 -#define GL_EIGHTH_BIT_ATI 0x00000020 -#define GL_SATURATE_BIT_ATI 0x00000040 -#define GL_COMP_BIT_ATI 0x00000002 -#define GL_NEGATE_BIT_ATI 0x00000004 -#define GL_BIAS_BIT_ATI 0x00000008 -typedef GLuint (APIENTRYP PFNGLGENFRAGMENTSHADERSATIPROC) (GLuint range); -typedef void (APIENTRYP PFNGLBINDFRAGMENTSHADERATIPROC) (GLuint id); -typedef void (APIENTRYP PFNGLDELETEFRAGMENTSHADERATIPROC) (GLuint id); -typedef void (APIENTRYP PFNGLBEGINFRAGMENTSHADERATIPROC) (void); -typedef void (APIENTRYP PFNGLENDFRAGMENTSHADERATIPROC) (void); -typedef void (APIENTRYP PFNGLPASSTEXCOORDATIPROC) (GLuint dst, GLuint coord, GLenum swizzle); -typedef void (APIENTRYP PFNGLSAMPLEMAPATIPROC) (GLuint dst, GLuint interp, GLenum swizzle); -typedef void (APIENTRYP PFNGLCOLORFRAGMENTOP1ATIPROC) (GLenum op, GLuint dst, GLuint dstMask, GLuint dstMod, GLuint arg1, GLuint arg1Rep, GLuint arg1Mod); -typedef void (APIENTRYP PFNGLCOLORFRAGMENTOP2ATIPROC) (GLenum op, GLuint dst, GLuint dstMask, GLuint dstMod, GLuint arg1, GLuint arg1Rep, GLuint arg1Mod, GLuint arg2, GLuint arg2Rep, GLuint arg2Mod); -typedef void (APIENTRYP PFNGLCOLORFRAGMENTOP3ATIPROC) (GLenum op, GLuint dst, GLuint dstMask, GLuint dstMod, GLuint arg1, GLuint arg1Rep, GLuint arg1Mod, GLuint arg2, GLuint arg2Rep, GLuint arg2Mod, GLuint arg3, GLuint arg3Rep, GLuint arg3Mod); -typedef void (APIENTRYP PFNGLALPHAFRAGMENTOP1ATIPROC) (GLenum op, GLuint dst, GLuint dstMod, GLuint arg1, GLuint arg1Rep, GLuint arg1Mod); -typedef void (APIENTRYP PFNGLALPHAFRAGMENTOP2ATIPROC) (GLenum op, GLuint dst, GLuint dstMod, GLuint arg1, GLuint arg1Rep, GLuint arg1Mod, GLuint arg2, GLuint arg2Rep, GLuint arg2Mod); -typedef void (APIENTRYP PFNGLALPHAFRAGMENTOP3ATIPROC) (GLenum op, GLuint dst, GLuint dstMod, GLuint arg1, GLuint arg1Rep, GLuint arg1Mod, GLuint arg2, GLuint arg2Rep, GLuint arg2Mod, GLuint arg3, GLuint arg3Rep, GLuint arg3Mod); -typedef void (APIENTRYP PFNGLSETFRAGMENTSHADERCONSTANTATIPROC) (GLuint dst, const GLfloat *value); -#ifdef GL_GLEXT_PROTOTYPES -GLAPI GLuint APIENTRY glGenFragmentShadersATI (GLuint range); -GLAPI void APIENTRY glBindFragmentShaderATI (GLuint id); -GLAPI void APIENTRY glDeleteFragmentShaderATI (GLuint id); -GLAPI void APIENTRY glBeginFragmentShaderATI (void); -GLAPI void APIENTRY glEndFragmentShaderATI (void); -GLAPI void APIENTRY glPassTexCoordATI (GLuint dst, GLuint coord, GLenum swizzle); -GLAPI void APIENTRY glSampleMapATI (GLuint dst, GLuint interp, GLenum swizzle); -GLAPI void APIENTRY glColorFragmentOp1ATI (GLenum op, GLuint dst, GLuint dstMask, GLuint dstMod, GLuint arg1, GLuint arg1Rep, GLuint arg1Mod); -GLAPI void APIENTRY glColorFragmentOp2ATI (GLenum op, GLuint dst, GLuint dstMask, GLuint dstMod, GLuint arg1, GLuint arg1Rep, GLuint arg1Mod, GLuint arg2, GLuint arg2Rep, GLuint arg2Mod); -GLAPI void APIENTRY glColorFragmentOp3ATI (GLenum op, GLuint dst, GLuint dstMask, GLuint dstMod, GLuint arg1, GLuint arg1Rep, GLuint arg1Mod, GLuint arg2, GLuint arg2Rep, GLuint arg2Mod, GLuint arg3, GLuint arg3Rep, GLuint arg3Mod); -GLAPI void APIENTRY glAlphaFragmentOp1ATI (GLenum op, GLuint dst, GLuint dstMod, GLuint arg1, GLuint arg1Rep, GLuint arg1Mod); -GLAPI void APIENTRY glAlphaFragmentOp2ATI (GLenum op, GLuint dst, GLuint dstMod, GLuint arg1, GLuint arg1Rep, GLuint arg1Mod, GLuint arg2, GLuint arg2Rep, GLuint arg2Mod); -GLAPI void APIENTRY glAlphaFragmentOp3ATI (GLenum op, GLuint dst, GLuint dstMod, GLuint arg1, GLuint arg1Rep, GLuint arg1Mod, GLuint arg2, GLuint arg2Rep, GLuint arg2Mod, GLuint arg3, GLuint arg3Rep, GLuint arg3Mod); -GLAPI void APIENTRY glSetFragmentShaderConstantATI (GLuint dst, const GLfloat *value); -#endif -#endif /* GL_ATI_fragment_shader */ - -#ifndef GL_ATI_map_object_buffer -#define GL_ATI_map_object_buffer 1 -typedef void *(APIENTRYP PFNGLMAPOBJECTBUFFERATIPROC) (GLuint buffer); -typedef void (APIENTRYP PFNGLUNMAPOBJECTBUFFERATIPROC) (GLuint buffer); -#ifdef GL_GLEXT_PROTOTYPES -GLAPI void *APIENTRY glMapObjectBufferATI (GLuint buffer); -GLAPI void APIENTRY glUnmapObjectBufferATI (GLuint buffer); -#endif -#endif /* GL_ATI_map_object_buffer */ - -#ifndef GL_ATI_meminfo -#define GL_ATI_meminfo 1 -#define GL_VBO_FREE_MEMORY_ATI 0x87FB -#define GL_TEXTURE_FREE_MEMORY_ATI 0x87FC -#define GL_RENDERBUFFER_FREE_MEMORY_ATI 0x87FD -#endif /* GL_ATI_meminfo */ - -#ifndef GL_ATI_pixel_format_float -#define GL_ATI_pixel_format_float 1 -#define GL_RGBA_FLOAT_MODE_ATI 0x8820 -#define GL_COLOR_CLEAR_UNCLAMPED_VALUE_ATI 0x8835 -#endif /* GL_ATI_pixel_format_float */ - -#ifndef GL_ATI_pn_triangles -#define GL_ATI_pn_triangles 1 -#define GL_PN_TRIANGLES_ATI 0x87F0 -#define GL_MAX_PN_TRIANGLES_TESSELATION_LEVEL_ATI 0x87F1 -#define GL_PN_TRIANGLES_POINT_MODE_ATI 0x87F2 -#define GL_PN_TRIANGLES_NORMAL_MODE_ATI 0x87F3 -#define GL_PN_TRIANGLES_TESSELATION_LEVEL_ATI 0x87F4 -#define GL_PN_TRIANGLES_POINT_MODE_LINEAR_ATI 0x87F5 -#define GL_PN_TRIANGLES_POINT_MODE_CUBIC_ATI 0x87F6 -#define GL_PN_TRIANGLES_NORMAL_MODE_LINEAR_ATI 0x87F7 -#define GL_PN_TRIANGLES_NORMAL_MODE_QUADRATIC_ATI 0x87F8 -typedef void (APIENTRYP PFNGLPNTRIANGLESIATIPROC) (GLenum pname, GLint param); -typedef void (APIENTRYP PFNGLPNTRIANGLESFATIPROC) (GLenum pname, GLfloat param); -#ifdef GL_GLEXT_PROTOTYPES -GLAPI void APIENTRY glPNTrianglesiATI (GLenum pname, GLint param); -GLAPI void APIENTRY glPNTrianglesfATI (GLenum pname, GLfloat param); -#endif -#endif /* GL_ATI_pn_triangles */ - -#ifndef GL_ATI_separate_stencil -#define GL_ATI_separate_stencil 1 -#define GL_STENCIL_BACK_FUNC_ATI 0x8800 -#define GL_STENCIL_BACK_FAIL_ATI 0x8801 -#define GL_STENCIL_BACK_PASS_DEPTH_FAIL_ATI 0x8802 -#define GL_STENCIL_BACK_PASS_DEPTH_PASS_ATI 0x8803 -typedef void (APIENTRYP PFNGLSTENCILOPSEPARATEATIPROC) (GLenum face, GLenum sfail, GLenum dpfail, GLenum dppass); -typedef void (APIENTRYP PFNGLSTENCILFUNCSEPARATEATIPROC) (GLenum frontfunc, GLenum backfunc, GLint ref, GLuint mask); -#ifdef GL_GLEXT_PROTOTYPES -GLAPI void APIENTRY glStencilOpSeparateATI (GLenum face, GLenum sfail, GLenum dpfail, GLenum dppass); -GLAPI void APIENTRY glStencilFuncSeparateATI (GLenum frontfunc, GLenum backfunc, GLint ref, GLuint mask); -#endif -#endif /* GL_ATI_separate_stencil */ - -#ifndef GL_ATI_text_fragment_shader -#define GL_ATI_text_fragment_shader 1 -#define GL_TEXT_FRAGMENT_SHADER_ATI 0x8200 -#endif /* GL_ATI_text_fragment_shader */ - -#ifndef GL_ATI_texture_env_combine3 -#define GL_ATI_texture_env_combine3 1 -#define GL_MODULATE_ADD_ATI 0x8744 -#define GL_MODULATE_SIGNED_ADD_ATI 0x8745 -#define GL_MODULATE_SUBTRACT_ATI 0x8746 -#endif /* GL_ATI_texture_env_combine3 */ - -#ifndef GL_ATI_texture_float -#define GL_ATI_texture_float 1 -#define GL_RGBA_FLOAT32_ATI 0x8814 -#define GL_RGB_FLOAT32_ATI 0x8815 -#define GL_ALPHA_FLOAT32_ATI 0x8816 -#define GL_INTENSITY_FLOAT32_ATI 0x8817 -#define GL_LUMINANCE_FLOAT32_ATI 0x8818 -#define GL_LUMINANCE_ALPHA_FLOAT32_ATI 0x8819 -#define GL_RGBA_FLOAT16_ATI 0x881A -#define GL_RGB_FLOAT16_ATI 0x881B -#define GL_ALPHA_FLOAT16_ATI 0x881C -#define GL_INTENSITY_FLOAT16_ATI 0x881D -#define GL_LUMINANCE_FLOAT16_ATI 0x881E -#define GL_LUMINANCE_ALPHA_FLOAT16_ATI 0x881F -#endif /* GL_ATI_texture_float */ - -#ifndef GL_ATI_texture_mirror_once -#define GL_ATI_texture_mirror_once 1 -#define GL_MIRROR_CLAMP_ATI 0x8742 -#define GL_MIRROR_CLAMP_TO_EDGE_ATI 0x8743 -#endif /* GL_ATI_texture_mirror_once */ - -#ifndef GL_ATI_vertex_array_object -#define GL_ATI_vertex_array_object 1 -#define GL_STATIC_ATI 0x8760 -#define GL_DYNAMIC_ATI 0x8761 -#define GL_PRESERVE_ATI 0x8762 -#define GL_DISCARD_ATI 0x8763 -#define GL_OBJECT_BUFFER_SIZE_ATI 0x8764 -#define GL_OBJECT_BUFFER_USAGE_ATI 0x8765 -#define GL_ARRAY_OBJECT_BUFFER_ATI 0x8766 -#define GL_ARRAY_OBJECT_OFFSET_ATI 0x8767 -typedef GLuint (APIENTRYP PFNGLNEWOBJECTBUFFERATIPROC) (GLsizei size, const void *pointer, GLenum usage); -typedef GLboolean (APIENTRYP PFNGLISOBJECTBUFFERATIPROC) (GLuint buffer); -typedef void (APIENTRYP PFNGLUPDATEOBJECTBUFFERATIPROC) (GLuint buffer, GLuint offset, GLsizei size, const void *pointer, GLenum preserve); -typedef void (APIENTRYP PFNGLGETOBJECTBUFFERFVATIPROC) (GLuint buffer, GLenum pname, GLfloat *params); -typedef void (APIENTRYP PFNGLGETOBJECTBUFFERIVATIPROC) (GLuint buffer, GLenum pname, GLint *params); -typedef void (APIENTRYP PFNGLFREEOBJECTBUFFERATIPROC) (GLuint buffer); -typedef void (APIENTRYP PFNGLARRAYOBJECTATIPROC) (GLenum array, GLint size, GLenum type, GLsizei stride, GLuint buffer, GLuint offset); -typedef void (APIENTRYP PFNGLGETARRAYOBJECTFVATIPROC) (GLenum array, GLenum pname, GLfloat *params); -typedef void (APIENTRYP PFNGLGETARRAYOBJECTIVATIPROC) (GLenum array, GLenum pname, GLint *params); -typedef void (APIENTRYP PFNGLVARIANTARRAYOBJECTATIPROC) (GLuint id, GLenum type, GLsizei stride, GLuint buffer, GLuint offset); -typedef void (APIENTRYP PFNGLGETVARIANTARRAYOBJECTFVATIPROC) (GLuint id, GLenum pname, GLfloat *params); -typedef void (APIENTRYP PFNGLGETVARIANTARRAYOBJECTIVATIPROC) (GLuint id, GLenum pname, GLint *params); -#ifdef GL_GLEXT_PROTOTYPES -GLAPI GLuint APIENTRY glNewObjectBufferATI (GLsizei size, const void *pointer, GLenum usage); -GLAPI GLboolean APIENTRY glIsObjectBufferATI (GLuint buffer); -GLAPI void APIENTRY glUpdateObjectBufferATI (GLuint buffer, GLuint offset, GLsizei size, const void *pointer, GLenum preserve); -GLAPI void APIENTRY glGetObjectBufferfvATI (GLuint buffer, GLenum pname, GLfloat *params); -GLAPI void APIENTRY glGetObjectBufferivATI (GLuint buffer, GLenum pname, GLint *params); -GLAPI void APIENTRY glFreeObjectBufferATI (GLuint buffer); -GLAPI void APIENTRY glArrayObjectATI (GLenum array, GLint size, GLenum type, GLsizei stride, GLuint buffer, GLuint offset); -GLAPI void APIENTRY glGetArrayObjectfvATI (GLenum array, GLenum pname, GLfloat *params); -GLAPI void APIENTRY glGetArrayObjectivATI (GLenum array, GLenum pname, GLint *params); -GLAPI void APIENTRY glVariantArrayObjectATI (GLuint id, GLenum type, GLsizei stride, GLuint buffer, GLuint offset); -GLAPI void APIENTRY glGetVariantArrayObjectfvATI (GLuint id, GLenum pname, GLfloat *params); -GLAPI void APIENTRY glGetVariantArrayObjectivATI (GLuint id, GLenum pname, GLint *params); -#endif -#endif /* GL_ATI_vertex_array_object */ - -#ifndef GL_ATI_vertex_attrib_array_object -#define GL_ATI_vertex_attrib_array_object 1 -typedef void (APIENTRYP PFNGLVERTEXATTRIBARRAYOBJECTATIPROC) (GLuint index, GLint size, GLenum type, GLboolean normalized, GLsizei stride, GLuint buffer, GLuint offset); -typedef void (APIENTRYP PFNGLGETVERTEXATTRIBARRAYOBJECTFVATIPROC) (GLuint index, GLenum pname, GLfloat *params); -typedef void (APIENTRYP PFNGLGETVERTEXATTRIBARRAYOBJECTIVATIPROC) (GLuint index, GLenum pname, GLint *params); -#ifdef GL_GLEXT_PROTOTYPES -GLAPI void APIENTRY glVertexAttribArrayObjectATI (GLuint index, GLint size, GLenum type, GLboolean normalized, GLsizei stride, GLuint buffer, GLuint offset); -GLAPI void APIENTRY glGetVertexAttribArrayObjectfvATI (GLuint index, GLenum pname, GLfloat *params); -GLAPI void APIENTRY glGetVertexAttribArrayObjectivATI (GLuint index, GLenum pname, GLint *params); -#endif -#endif /* GL_ATI_vertex_attrib_array_object */ - -#ifndef GL_ATI_vertex_streams -#define GL_ATI_vertex_streams 1 -#define GL_MAX_VERTEX_STREAMS_ATI 0x876B -#define GL_VERTEX_STREAM0_ATI 0x876C -#define GL_VERTEX_STREAM1_ATI 0x876D -#define GL_VERTEX_STREAM2_ATI 0x876E -#define GL_VERTEX_STREAM3_ATI 0x876F -#define GL_VERTEX_STREAM4_ATI 0x8770 -#define GL_VERTEX_STREAM5_ATI 0x8771 -#define GL_VERTEX_STREAM6_ATI 0x8772 -#define GL_VERTEX_STREAM7_ATI 0x8773 -#define GL_VERTEX_SOURCE_ATI 0x8774 -typedef void (APIENTRYP PFNGLVERTEXSTREAM1SATIPROC) (GLenum stream, GLshort x); -typedef void (APIENTRYP PFNGLVERTEXSTREAM1SVATIPROC) (GLenum stream, const GLshort *coords); -typedef void (APIENTRYP PFNGLVERTEXSTREAM1IATIPROC) (GLenum stream, GLint x); -typedef void (APIENTRYP PFNGLVERTEXSTREAM1IVATIPROC) (GLenum stream, const GLint *coords); -typedef void (APIENTRYP PFNGLVERTEXSTREAM1FATIPROC) (GLenum stream, GLfloat x); -typedef void (APIENTRYP PFNGLVERTEXSTREAM1FVATIPROC) (GLenum stream, const GLfloat *coords); -typedef void (APIENTRYP PFNGLVERTEXSTREAM1DATIPROC) (GLenum stream, GLdouble x); -typedef void (APIENTRYP PFNGLVERTEXSTREAM1DVATIPROC) (GLenum stream, const GLdouble *coords); -typedef void (APIENTRYP PFNGLVERTEXSTREAM2SATIPROC) (GLenum stream, GLshort x, GLshort y); -typedef void (APIENTRYP PFNGLVERTEXSTREAM2SVATIPROC) (GLenum stream, const GLshort *coords); -typedef void (APIENTRYP PFNGLVERTEXSTREAM2IATIPROC) (GLenum stream, GLint x, GLint y); -typedef void (APIENTRYP PFNGLVERTEXSTREAM2IVATIPROC) (GLenum stream, const GLint *coords); -typedef void (APIENTRYP PFNGLVERTEXSTREAM2FATIPROC) (GLenum stream, GLfloat x, GLfloat y); -typedef void (APIENTRYP PFNGLVERTEXSTREAM2FVATIPROC) (GLenum stream, const GLfloat *coords); -typedef void (APIENTRYP PFNGLVERTEXSTREAM2DATIPROC) (GLenum stream, GLdouble x, GLdouble y); -typedef void (APIENTRYP PFNGLVERTEXSTREAM2DVATIPROC) (GLenum stream, const GLdouble *coords); -typedef void (APIENTRYP PFNGLVERTEXSTREAM3SATIPROC) (GLenum stream, GLshort x, GLshort y, GLshort z); -typedef void (APIENTRYP PFNGLVERTEXSTREAM3SVATIPROC) (GLenum stream, const GLshort *coords); -typedef void (APIENTRYP PFNGLVERTEXSTREAM3IATIPROC) (GLenum stream, GLint x, GLint y, GLint z); -typedef void (APIENTRYP PFNGLVERTEXSTREAM3IVATIPROC) (GLenum stream, const GLint *coords); -typedef void (APIENTRYP PFNGLVERTEXSTREAM3FATIPROC) (GLenum stream, GLfloat x, GLfloat y, GLfloat z); -typedef void (APIENTRYP PFNGLVERTEXSTREAM3FVATIPROC) (GLenum stream, const GLfloat *coords); -typedef void (APIENTRYP PFNGLVERTEXSTREAM3DATIPROC) (GLenum stream, GLdouble x, GLdouble y, GLdouble z); -typedef void (APIENTRYP PFNGLVERTEXSTREAM3DVATIPROC) (GLenum stream, const GLdouble *coords); -typedef void (APIENTRYP PFNGLVERTEXSTREAM4SATIPROC) (GLenum stream, GLshort x, GLshort y, GLshort z, GLshort w); -typedef void (APIENTRYP PFNGLVERTEXSTREAM4SVATIPROC) (GLenum stream, const GLshort *coords); -typedef void (APIENTRYP PFNGLVERTEXSTREAM4IATIPROC) (GLenum stream, GLint x, GLint y, GLint z, GLint w); -typedef void (APIENTRYP PFNGLVERTEXSTREAM4IVATIPROC) (GLenum stream, const GLint *coords); -typedef void (APIENTRYP PFNGLVERTEXSTREAM4FATIPROC) (GLenum stream, GLfloat x, GLfloat y, GLfloat z, GLfloat w); -typedef void (APIENTRYP PFNGLVERTEXSTREAM4FVATIPROC) (GLenum stream, const GLfloat *coords); -typedef void (APIENTRYP PFNGLVERTEXSTREAM4DATIPROC) (GLenum stream, GLdouble x, GLdouble y, GLdouble z, GLdouble w); -typedef void (APIENTRYP PFNGLVERTEXSTREAM4DVATIPROC) (GLenum stream, const GLdouble *coords); -typedef void (APIENTRYP PFNGLNORMALSTREAM3BATIPROC) (GLenum stream, GLbyte nx, GLbyte ny, GLbyte nz); -typedef void (APIENTRYP PFNGLNORMALSTREAM3BVATIPROC) (GLenum stream, const GLbyte *coords); -typedef void (APIENTRYP PFNGLNORMALSTREAM3SATIPROC) (GLenum stream, GLshort nx, GLshort ny, GLshort nz); -typedef void (APIENTRYP PFNGLNORMALSTREAM3SVATIPROC) (GLenum stream, const GLshort *coords); -typedef void (APIENTRYP PFNGLNORMALSTREAM3IATIPROC) (GLenum stream, GLint nx, GLint ny, GLint nz); -typedef void (APIENTRYP PFNGLNORMALSTREAM3IVATIPROC) (GLenum stream, const GLint *coords); -typedef void (APIENTRYP PFNGLNORMALSTREAM3FATIPROC) (GLenum stream, GLfloat nx, GLfloat ny, GLfloat nz); -typedef void (APIENTRYP PFNGLNORMALSTREAM3FVATIPROC) (GLenum stream, const GLfloat *coords); -typedef void (APIENTRYP PFNGLNORMALSTREAM3DATIPROC) (GLenum stream, GLdouble nx, GLdouble ny, GLdouble nz); -typedef void (APIENTRYP PFNGLNORMALSTREAM3DVATIPROC) (GLenum stream, const GLdouble *coords); -typedef void (APIENTRYP PFNGLCLIENTACTIVEVERTEXSTREAMATIPROC) (GLenum stream); -typedef void (APIENTRYP PFNGLVERTEXBLENDENVIATIPROC) (GLenum pname, GLint param); -typedef void (APIENTRYP PFNGLVERTEXBLENDENVFATIPROC) (GLenum pname, GLfloat param); -#ifdef GL_GLEXT_PROTOTYPES -GLAPI void APIENTRY glVertexStream1sATI (GLenum stream, GLshort x); -GLAPI void APIENTRY glVertexStream1svATI (GLenum stream, const GLshort *coords); -GLAPI void APIENTRY glVertexStream1iATI (GLenum stream, GLint x); -GLAPI void APIENTRY glVertexStream1ivATI (GLenum stream, const GLint *coords); -GLAPI void APIENTRY glVertexStream1fATI (GLenum stream, GLfloat x); -GLAPI void APIENTRY glVertexStream1fvATI (GLenum stream, const GLfloat *coords); -GLAPI void APIENTRY glVertexStream1dATI (GLenum stream, GLdouble x); -GLAPI void APIENTRY glVertexStream1dvATI (GLenum stream, const GLdouble *coords); -GLAPI void APIENTRY glVertexStream2sATI (GLenum stream, GLshort x, GLshort y); -GLAPI void APIENTRY glVertexStream2svATI (GLenum stream, const GLshort *coords); -GLAPI void APIENTRY glVertexStream2iATI (GLenum stream, GLint x, GLint y); -GLAPI void APIENTRY glVertexStream2ivATI (GLenum stream, const GLint *coords); -GLAPI void APIENTRY glVertexStream2fATI (GLenum stream, GLfloat x, GLfloat y); -GLAPI void APIENTRY glVertexStream2fvATI (GLenum stream, const GLfloat *coords); -GLAPI void APIENTRY glVertexStream2dATI (GLenum stream, GLdouble x, GLdouble y); -GLAPI void APIENTRY glVertexStream2dvATI (GLenum stream, const GLdouble *coords); -GLAPI void APIENTRY glVertexStream3sATI (GLenum stream, GLshort x, GLshort y, GLshort z); -GLAPI void APIENTRY glVertexStream3svATI (GLenum stream, const GLshort *coords); -GLAPI void APIENTRY glVertexStream3iATI (GLenum stream, GLint x, GLint y, GLint z); -GLAPI void APIENTRY glVertexStream3ivATI (GLenum stream, const GLint *coords); -GLAPI void APIENTRY glVertexStream3fATI (GLenum stream, GLfloat x, GLfloat y, GLfloat z); -GLAPI void APIENTRY glVertexStream3fvATI (GLenum stream, const GLfloat *coords); -GLAPI void APIENTRY glVertexStream3dATI (GLenum stream, GLdouble x, GLdouble y, GLdouble z); -GLAPI void APIENTRY glVertexStream3dvATI (GLenum stream, const GLdouble *coords); -GLAPI void APIENTRY glVertexStream4sATI (GLenum stream, GLshort x, GLshort y, GLshort z, GLshort w); -GLAPI void APIENTRY glVertexStream4svATI (GLenum stream, const GLshort *coords); -GLAPI void APIENTRY glVertexStream4iATI (GLenum stream, GLint x, GLint y, GLint z, GLint w); -GLAPI void APIENTRY glVertexStream4ivATI (GLenum stream, const GLint *coords); -GLAPI void APIENTRY glVertexStream4fATI (GLenum stream, GLfloat x, GLfloat y, GLfloat z, GLfloat w); -GLAPI void APIENTRY glVertexStream4fvATI (GLenum stream, const GLfloat *coords); -GLAPI void APIENTRY glVertexStream4dATI (GLenum stream, GLdouble x, GLdouble y, GLdouble z, GLdouble w); -GLAPI void APIENTRY glVertexStream4dvATI (GLenum stream, const GLdouble *coords); -GLAPI void APIENTRY glNormalStream3bATI (GLenum stream, GLbyte nx, GLbyte ny, GLbyte nz); -GLAPI void APIENTRY glNormalStream3bvATI (GLenum stream, const GLbyte *coords); -GLAPI void APIENTRY glNormalStream3sATI (GLenum stream, GLshort nx, GLshort ny, GLshort nz); -GLAPI void APIENTRY glNormalStream3svATI (GLenum stream, const GLshort *coords); -GLAPI void APIENTRY glNormalStream3iATI (GLenum stream, GLint nx, GLint ny, GLint nz); -GLAPI void APIENTRY glNormalStream3ivATI (GLenum stream, const GLint *coords); -GLAPI void APIENTRY glNormalStream3fATI (GLenum stream, GLfloat nx, GLfloat ny, GLfloat nz); -GLAPI void APIENTRY glNormalStream3fvATI (GLenum stream, const GLfloat *coords); -GLAPI void APIENTRY glNormalStream3dATI (GLenum stream, GLdouble nx, GLdouble ny, GLdouble nz); -GLAPI void APIENTRY glNormalStream3dvATI (GLenum stream, const GLdouble *coords); -GLAPI void APIENTRY glClientActiveVertexStreamATI (GLenum stream); -GLAPI void APIENTRY glVertexBlendEnviATI (GLenum pname, GLint param); -GLAPI void APIENTRY glVertexBlendEnvfATI (GLenum pname, GLfloat param); -#endif -#endif /* GL_ATI_vertex_streams */ - -#ifndef GL_EXT_422_pixels -#define GL_EXT_422_pixels 1 -#define GL_422_EXT 0x80CC -#define GL_422_REV_EXT 0x80CD -#define GL_422_AVERAGE_EXT 0x80CE -#define GL_422_REV_AVERAGE_EXT 0x80CF -#endif /* GL_EXT_422_pixels */ - -#ifndef GL_EXT_abgr -#define GL_EXT_abgr 1 -#define GL_ABGR_EXT 0x8000 -#endif /* GL_EXT_abgr */ - -#ifndef GL_EXT_bgra -#define GL_EXT_bgra 1 -#define GL_BGR_EXT 0x80E0 -#define GL_BGRA_EXT 0x80E1 -#endif /* GL_EXT_bgra */ - -#ifndef GL_EXT_bindable_uniform -#define GL_EXT_bindable_uniform 1 -#define GL_MAX_VERTEX_BINDABLE_UNIFORMS_EXT 0x8DE2 -#define GL_MAX_FRAGMENT_BINDABLE_UNIFORMS_EXT 0x8DE3 -#define GL_MAX_GEOMETRY_BINDABLE_UNIFORMS_EXT 0x8DE4 -#define GL_MAX_BINDABLE_UNIFORM_SIZE_EXT 0x8DED -#define GL_UNIFORM_BUFFER_EXT 0x8DEE -#define GL_UNIFORM_BUFFER_BINDING_EXT 0x8DEF -typedef void (APIENTRYP PFNGLUNIFORMBUFFEREXTPROC) (GLuint program, GLint location, GLuint buffer); -typedef GLint (APIENTRYP PFNGLGETUNIFORMBUFFERSIZEEXTPROC) (GLuint program, GLint location); -typedef GLintptr (APIENTRYP PFNGLGETUNIFORMOFFSETEXTPROC) (GLuint program, GLint location); -#ifdef GL_GLEXT_PROTOTYPES -GLAPI void APIENTRY glUniformBufferEXT (GLuint program, GLint location, GLuint buffer); -GLAPI GLint APIENTRY glGetUniformBufferSizeEXT (GLuint program, GLint location); -GLAPI GLintptr APIENTRY glGetUniformOffsetEXT (GLuint program, GLint location); -#endif -#endif /* GL_EXT_bindable_uniform */ - -#ifndef GL_EXT_blend_color -#define GL_EXT_blend_color 1 -#define GL_CONSTANT_COLOR_EXT 0x8001 -#define GL_ONE_MINUS_CONSTANT_COLOR_EXT 0x8002 -#define GL_CONSTANT_ALPHA_EXT 0x8003 -#define GL_ONE_MINUS_CONSTANT_ALPHA_EXT 0x8004 -#define GL_BLEND_COLOR_EXT 0x8005 -typedef void (APIENTRYP PFNGLBLENDCOLOREXTPROC) (GLfloat red, GLfloat green, GLfloat blue, GLfloat alpha); -#ifdef GL_GLEXT_PROTOTYPES -GLAPI void APIENTRY glBlendColorEXT (GLfloat red, GLfloat green, GLfloat blue, GLfloat alpha); -#endif -#endif /* GL_EXT_blend_color */ - -#ifndef GL_EXT_blend_equation_separate -#define GL_EXT_blend_equation_separate 1 -#define GL_BLEND_EQUATION_RGB_EXT 0x8009 -#define GL_BLEND_EQUATION_ALPHA_EXT 0x883D -typedef void (APIENTRYP PFNGLBLENDEQUATIONSEPARATEEXTPROC) (GLenum modeRGB, GLenum modeAlpha); -#ifdef GL_GLEXT_PROTOTYPES -GLAPI void APIENTRY glBlendEquationSeparateEXT (GLenum modeRGB, GLenum modeAlpha); -#endif -#endif /* GL_EXT_blend_equation_separate */ - -#ifndef GL_EXT_blend_func_separate -#define GL_EXT_blend_func_separate 1 -#define GL_BLEND_DST_RGB_EXT 0x80C8 -#define GL_BLEND_SRC_RGB_EXT 0x80C9 -#define GL_BLEND_DST_ALPHA_EXT 0x80CA -#define GL_BLEND_SRC_ALPHA_EXT 0x80CB -typedef void (APIENTRYP PFNGLBLENDFUNCSEPARATEEXTPROC) (GLenum sfactorRGB, GLenum dfactorRGB, GLenum sfactorAlpha, GLenum dfactorAlpha); -#ifdef GL_GLEXT_PROTOTYPES -GLAPI void APIENTRY glBlendFuncSeparateEXT (GLenum sfactorRGB, GLenum dfactorRGB, GLenum sfactorAlpha, GLenum dfactorAlpha); -#endif -#endif /* GL_EXT_blend_func_separate */ - -#ifndef GL_EXT_blend_logic_op -#define GL_EXT_blend_logic_op 1 -#endif /* GL_EXT_blend_logic_op */ - -#ifndef GL_EXT_blend_minmax -#define GL_EXT_blend_minmax 1 -#define GL_MIN_EXT 0x8007 -#define GL_MAX_EXT 0x8008 -#define GL_FUNC_ADD_EXT 0x8006 -#define GL_BLEND_EQUATION_EXT 0x8009 -typedef void (APIENTRYP PFNGLBLENDEQUATIONEXTPROC) (GLenum mode); -#ifdef GL_GLEXT_PROTOTYPES -GLAPI void APIENTRY glBlendEquationEXT (GLenum mode); -#endif -#endif /* GL_EXT_blend_minmax */ - -#ifndef GL_EXT_blend_subtract -#define GL_EXT_blend_subtract 1 -#define GL_FUNC_SUBTRACT_EXT 0x800A -#define GL_FUNC_REVERSE_SUBTRACT_EXT 0x800B -#endif /* GL_EXT_blend_subtract */ - -#ifndef GL_EXT_clip_volume_hint -#define GL_EXT_clip_volume_hint 1 -#define GL_CLIP_VOLUME_CLIPPING_HINT_EXT 0x80F0 -#endif /* GL_EXT_clip_volume_hint */ - -#ifndef GL_EXT_cmyka -#define GL_EXT_cmyka 1 -#define GL_CMYK_EXT 0x800C -#define GL_CMYKA_EXT 0x800D -#define GL_PACK_CMYK_HINT_EXT 0x800E -#define GL_UNPACK_CMYK_HINT_EXT 0x800F -#endif /* GL_EXT_cmyka */ - -#ifndef GL_EXT_color_subtable -#define GL_EXT_color_subtable 1 -typedef void (APIENTRYP PFNGLCOLORSUBTABLEEXTPROC) (GLenum target, GLsizei start, GLsizei count, GLenum format, GLenum type, const void *data); -typedef void (APIENTRYP PFNGLCOPYCOLORSUBTABLEEXTPROC) (GLenum target, GLsizei start, GLint x, GLint y, GLsizei width); -#ifdef GL_GLEXT_PROTOTYPES -GLAPI void APIENTRY glColorSubTableEXT (GLenum target, GLsizei start, GLsizei count, GLenum format, GLenum type, const void *data); -GLAPI void APIENTRY glCopyColorSubTableEXT (GLenum target, GLsizei start, GLint x, GLint y, GLsizei width); -#endif -#endif /* GL_EXT_color_subtable */ - -#ifndef GL_EXT_compiled_vertex_array -#define GL_EXT_compiled_vertex_array 1 -#define GL_ARRAY_ELEMENT_LOCK_FIRST_EXT 0x81A8 -#define GL_ARRAY_ELEMENT_LOCK_COUNT_EXT 0x81A9 -typedef void (APIENTRYP PFNGLLOCKARRAYSEXTPROC) (GLint first, GLsizei count); -typedef void (APIENTRYP PFNGLUNLOCKARRAYSEXTPROC) (void); -#ifdef GL_GLEXT_PROTOTYPES -GLAPI void APIENTRY glLockArraysEXT (GLint first, GLsizei count); -GLAPI void APIENTRY glUnlockArraysEXT (void); -#endif -#endif /* GL_EXT_compiled_vertex_array */ - -#ifndef GL_EXT_convolution -#define GL_EXT_convolution 1 -#define GL_CONVOLUTION_1D_EXT 0x8010 -#define GL_CONVOLUTION_2D_EXT 0x8011 -#define GL_SEPARABLE_2D_EXT 0x8012 -#define GL_CONVOLUTION_BORDER_MODE_EXT 0x8013 -#define GL_CONVOLUTION_FILTER_SCALE_EXT 0x8014 -#define GL_CONVOLUTION_FILTER_BIAS_EXT 0x8015 -#define GL_REDUCE_EXT 0x8016 -#define GL_CONVOLUTION_FORMAT_EXT 0x8017 -#define GL_CONVOLUTION_WIDTH_EXT 0x8018 -#define GL_CONVOLUTION_HEIGHT_EXT 0x8019 -#define GL_MAX_CONVOLUTION_WIDTH_EXT 0x801A -#define GL_MAX_CONVOLUTION_HEIGHT_EXT 0x801B -#define GL_POST_CONVOLUTION_RED_SCALE_EXT 0x801C -#define GL_POST_CONVOLUTION_GREEN_SCALE_EXT 0x801D -#define GL_POST_CONVOLUTION_BLUE_SCALE_EXT 0x801E -#define GL_POST_CONVOLUTION_ALPHA_SCALE_EXT 0x801F -#define GL_POST_CONVOLUTION_RED_BIAS_EXT 0x8020 -#define GL_POST_CONVOLUTION_GREEN_BIAS_EXT 0x8021 -#define GL_POST_CONVOLUTION_BLUE_BIAS_EXT 0x8022 -#define GL_POST_CONVOLUTION_ALPHA_BIAS_EXT 0x8023 -typedef void (APIENTRYP PFNGLCONVOLUTIONFILTER1DEXTPROC) (GLenum target, GLenum internalformat, GLsizei width, GLenum format, GLenum type, const void *image); -typedef void (APIENTRYP PFNGLCONVOLUTIONFILTER2DEXTPROC) (GLenum target, GLenum internalformat, GLsizei width, GLsizei height, GLenum format, GLenum type, const void *image); -typedef void (APIENTRYP PFNGLCONVOLUTIONPARAMETERFEXTPROC) (GLenum target, GLenum pname, GLfloat params); -typedef void (APIENTRYP PFNGLCONVOLUTIONPARAMETERFVEXTPROC) (GLenum target, GLenum pname, const GLfloat *params); -typedef void (APIENTRYP PFNGLCONVOLUTIONPARAMETERIEXTPROC) (GLenum target, GLenum pname, GLint params); -typedef void (APIENTRYP PFNGLCONVOLUTIONPARAMETERIVEXTPROC) (GLenum target, GLenum pname, const GLint *params); -typedef void (APIENTRYP PFNGLCOPYCONVOLUTIONFILTER1DEXTPROC) (GLenum target, GLenum internalformat, GLint x, GLint y, GLsizei width); -typedef void (APIENTRYP PFNGLCOPYCONVOLUTIONFILTER2DEXTPROC) (GLenum target, GLenum internalformat, GLint x, GLint y, GLsizei width, GLsizei height); -typedef void (APIENTRYP PFNGLGETCONVOLUTIONFILTEREXTPROC) (GLenum target, GLenum format, GLenum type, void *image); -typedef void (APIENTRYP PFNGLGETCONVOLUTIONPARAMETERFVEXTPROC) (GLenum target, GLenum pname, GLfloat *params); -typedef void (APIENTRYP PFNGLGETCONVOLUTIONPARAMETERIVEXTPROC) (GLenum target, GLenum pname, GLint *params); -typedef void (APIENTRYP PFNGLGETSEPARABLEFILTEREXTPROC) (GLenum target, GLenum format, GLenum type, void *row, void *column, void *span); -typedef void (APIENTRYP PFNGLSEPARABLEFILTER2DEXTPROC) (GLenum target, GLenum internalformat, GLsizei width, GLsizei height, GLenum format, GLenum type, const void *row, const void *column); -#ifdef GL_GLEXT_PROTOTYPES -GLAPI void APIENTRY glConvolutionFilter1DEXT (GLenum target, GLenum internalformat, GLsizei width, GLenum format, GLenum type, const void *image); -GLAPI void APIENTRY glConvolutionFilter2DEXT (GLenum target, GLenum internalformat, GLsizei width, GLsizei height, GLenum format, GLenum type, const void *image); -GLAPI void APIENTRY glConvolutionParameterfEXT (GLenum target, GLenum pname, GLfloat params); -GLAPI void APIENTRY glConvolutionParameterfvEXT (GLenum target, GLenum pname, const GLfloat *params); -GLAPI void APIENTRY glConvolutionParameteriEXT (GLenum target, GLenum pname, GLint params); -GLAPI void APIENTRY glConvolutionParameterivEXT (GLenum target, GLenum pname, const GLint *params); -GLAPI void APIENTRY glCopyConvolutionFilter1DEXT (GLenum target, GLenum internalformat, GLint x, GLint y, GLsizei width); -GLAPI void APIENTRY glCopyConvolutionFilter2DEXT (GLenum target, GLenum internalformat, GLint x, GLint y, GLsizei width, GLsizei height); -GLAPI void APIENTRY glGetConvolutionFilterEXT (GLenum target, GLenum format, GLenum type, void *image); -GLAPI void APIENTRY glGetConvolutionParameterfvEXT (GLenum target, GLenum pname, GLfloat *params); -GLAPI void APIENTRY glGetConvolutionParameterivEXT (GLenum target, GLenum pname, GLint *params); -GLAPI void APIENTRY glGetSeparableFilterEXT (GLenum target, GLenum format, GLenum type, void *row, void *column, void *span); -GLAPI void APIENTRY glSeparableFilter2DEXT (GLenum target, GLenum internalformat, GLsizei width, GLsizei height, GLenum format, GLenum type, const void *row, const void *column); -#endif -#endif /* GL_EXT_convolution */ - -#ifndef GL_EXT_coordinate_frame -#define GL_EXT_coordinate_frame 1 -#define GL_TANGENT_ARRAY_EXT 0x8439 -#define GL_BINORMAL_ARRAY_EXT 0x843A -#define GL_CURRENT_TANGENT_EXT 0x843B -#define GL_CURRENT_BINORMAL_EXT 0x843C -#define GL_TANGENT_ARRAY_TYPE_EXT 0x843E -#define GL_TANGENT_ARRAY_STRIDE_EXT 0x843F -#define GL_BINORMAL_ARRAY_TYPE_EXT 0x8440 -#define GL_BINORMAL_ARRAY_STRIDE_EXT 0x8441 -#define GL_TANGENT_ARRAY_POINTER_EXT 0x8442 -#define GL_BINORMAL_ARRAY_POINTER_EXT 0x8443 -#define GL_MAP1_TANGENT_EXT 0x8444 -#define GL_MAP2_TANGENT_EXT 0x8445 -#define GL_MAP1_BINORMAL_EXT 0x8446 -#define GL_MAP2_BINORMAL_EXT 0x8447 -typedef void (APIENTRYP PFNGLTANGENT3BEXTPROC) (GLbyte tx, GLbyte ty, GLbyte tz); -typedef void (APIENTRYP PFNGLTANGENT3BVEXTPROC) (const GLbyte *v); -typedef void (APIENTRYP PFNGLTANGENT3DEXTPROC) (GLdouble tx, GLdouble ty, GLdouble tz); -typedef void (APIENTRYP PFNGLTANGENT3DVEXTPROC) (const GLdouble *v); -typedef void (APIENTRYP PFNGLTANGENT3FEXTPROC) (GLfloat tx, GLfloat ty, GLfloat tz); -typedef void (APIENTRYP PFNGLTANGENT3FVEXTPROC) (const GLfloat *v); -typedef void (APIENTRYP PFNGLTANGENT3IEXTPROC) (GLint tx, GLint ty, GLint tz); -typedef void (APIENTRYP PFNGLTANGENT3IVEXTPROC) (const GLint *v); -typedef void (APIENTRYP PFNGLTANGENT3SEXTPROC) (GLshort tx, GLshort ty, GLshort tz); -typedef void (APIENTRYP PFNGLTANGENT3SVEXTPROC) (const GLshort *v); -typedef void (APIENTRYP PFNGLBINORMAL3BEXTPROC) (GLbyte bx, GLbyte by, GLbyte bz); -typedef void (APIENTRYP PFNGLBINORMAL3BVEXTPROC) (const GLbyte *v); -typedef void (APIENTRYP PFNGLBINORMAL3DEXTPROC) (GLdouble bx, GLdouble by, GLdouble bz); -typedef void (APIENTRYP PFNGLBINORMAL3DVEXTPROC) (const GLdouble *v); -typedef void (APIENTRYP PFNGLBINORMAL3FEXTPROC) (GLfloat bx, GLfloat by, GLfloat bz); -typedef void (APIENTRYP PFNGLBINORMAL3FVEXTPROC) (const GLfloat *v); -typedef void (APIENTRYP PFNGLBINORMAL3IEXTPROC) (GLint bx, GLint by, GLint bz); -typedef void (APIENTRYP PFNGLBINORMAL3IVEXTPROC) (const GLint *v); -typedef void (APIENTRYP PFNGLBINORMAL3SEXTPROC) (GLshort bx, GLshort by, GLshort bz); -typedef void (APIENTRYP PFNGLBINORMAL3SVEXTPROC) (const GLshort *v); -typedef void (APIENTRYP PFNGLTANGENTPOINTEREXTPROC) (GLenum type, GLsizei stride, const void *pointer); -typedef void (APIENTRYP PFNGLBINORMALPOINTEREXTPROC) (GLenum type, GLsizei stride, const void *pointer); -#ifdef GL_GLEXT_PROTOTYPES -GLAPI void APIENTRY glTangent3bEXT (GLbyte tx, GLbyte ty, GLbyte tz); -GLAPI void APIENTRY glTangent3bvEXT (const GLbyte *v); -GLAPI void APIENTRY glTangent3dEXT (GLdouble tx, GLdouble ty, GLdouble tz); -GLAPI void APIENTRY glTangent3dvEXT (const GLdouble *v); -GLAPI void APIENTRY glTangent3fEXT (GLfloat tx, GLfloat ty, GLfloat tz); -GLAPI void APIENTRY glTangent3fvEXT (const GLfloat *v); -GLAPI void APIENTRY glTangent3iEXT (GLint tx, GLint ty, GLint tz); -GLAPI void APIENTRY glTangent3ivEXT (const GLint *v); -GLAPI void APIENTRY glTangent3sEXT (GLshort tx, GLshort ty, GLshort tz); -GLAPI void APIENTRY glTangent3svEXT (const GLshort *v); -GLAPI void APIENTRY glBinormal3bEXT (GLbyte bx, GLbyte by, GLbyte bz); -GLAPI void APIENTRY glBinormal3bvEXT (const GLbyte *v); -GLAPI void APIENTRY glBinormal3dEXT (GLdouble bx, GLdouble by, GLdouble bz); -GLAPI void APIENTRY glBinormal3dvEXT (const GLdouble *v); -GLAPI void APIENTRY glBinormal3fEXT (GLfloat bx, GLfloat by, GLfloat bz); -GLAPI void APIENTRY glBinormal3fvEXT (const GLfloat *v); -GLAPI void APIENTRY glBinormal3iEXT (GLint bx, GLint by, GLint bz); -GLAPI void APIENTRY glBinormal3ivEXT (const GLint *v); -GLAPI void APIENTRY glBinormal3sEXT (GLshort bx, GLshort by, GLshort bz); -GLAPI void APIENTRY glBinormal3svEXT (const GLshort *v); -GLAPI void APIENTRY glTangentPointerEXT (GLenum type, GLsizei stride, const void *pointer); -GLAPI void APIENTRY glBinormalPointerEXT (GLenum type, GLsizei stride, const void *pointer); -#endif -#endif /* GL_EXT_coordinate_frame */ - -#ifndef GL_EXT_copy_texture -#define GL_EXT_copy_texture 1 -typedef void (APIENTRYP PFNGLCOPYTEXIMAGE1DEXTPROC) (GLenum target, GLint level, GLenum internalformat, GLint x, GLint y, GLsizei width, GLint border); -typedef void (APIENTRYP PFNGLCOPYTEXIMAGE2DEXTPROC) (GLenum target, GLint level, GLenum internalformat, GLint x, GLint y, GLsizei width, GLsizei height, GLint border); -typedef void (APIENTRYP PFNGLCOPYTEXSUBIMAGE1DEXTPROC) (GLenum target, GLint level, GLint xoffset, GLint x, GLint y, GLsizei width); -typedef void (APIENTRYP PFNGLCOPYTEXSUBIMAGE2DEXTPROC) (GLenum target, GLint level, GLint xoffset, GLint yoffset, GLint x, GLint y, GLsizei width, GLsizei height); -typedef void (APIENTRYP PFNGLCOPYTEXSUBIMAGE3DEXTPROC) (GLenum target, GLint level, GLint xoffset, GLint yoffset, GLint zoffset, GLint x, GLint y, GLsizei width, GLsizei height); -#ifdef GL_GLEXT_PROTOTYPES -GLAPI void APIENTRY glCopyTexImage1DEXT (GLenum target, GLint level, GLenum internalformat, GLint x, GLint y, GLsizei width, GLint border); -GLAPI void APIENTRY glCopyTexImage2DEXT (GLenum target, GLint level, GLenum internalformat, GLint x, GLint y, GLsizei width, GLsizei height, GLint border); -GLAPI void APIENTRY glCopyTexSubImage1DEXT (GLenum target, GLint level, GLint xoffset, GLint x, GLint y, GLsizei width); -GLAPI void APIENTRY glCopyTexSubImage2DEXT (GLenum target, GLint level, GLint xoffset, GLint yoffset, GLint x, GLint y, GLsizei width, GLsizei height); -GLAPI void APIENTRY glCopyTexSubImage3DEXT (GLenum target, GLint level, GLint xoffset, GLint yoffset, GLint zoffset, GLint x, GLint y, GLsizei width, GLsizei height); -#endif -#endif /* GL_EXT_copy_texture */ - -#ifndef GL_EXT_cull_vertex -#define GL_EXT_cull_vertex 1 -#define GL_CULL_VERTEX_EXT 0x81AA -#define GL_CULL_VERTEX_EYE_POSITION_EXT 0x81AB -#define GL_CULL_VERTEX_OBJECT_POSITION_EXT 0x81AC -typedef void (APIENTRYP PFNGLCULLPARAMETERDVEXTPROC) (GLenum pname, GLdouble *params); -typedef void (APIENTRYP PFNGLCULLPARAMETERFVEXTPROC) (GLenum pname, GLfloat *params); -#ifdef GL_GLEXT_PROTOTYPES -GLAPI void APIENTRY glCullParameterdvEXT (GLenum pname, GLdouble *params); -GLAPI void APIENTRY glCullParameterfvEXT (GLenum pname, GLfloat *params); -#endif -#endif /* GL_EXT_cull_vertex */ - -#ifndef GL_EXT_debug_label -#define GL_EXT_debug_label 1 -#define GL_PROGRAM_PIPELINE_OBJECT_EXT 0x8A4F -#define GL_PROGRAM_OBJECT_EXT 0x8B40 -#define GL_SHADER_OBJECT_EXT 0x8B48 -#define GL_BUFFER_OBJECT_EXT 0x9151 -#define GL_QUERY_OBJECT_EXT 0x9153 -#define GL_VERTEX_ARRAY_OBJECT_EXT 0x9154 -typedef void (APIENTRYP PFNGLLABELOBJECTEXTPROC) (GLenum type, GLuint object, GLsizei length, const GLchar *label); -typedef void (APIENTRYP PFNGLGETOBJECTLABELEXTPROC) (GLenum type, GLuint object, GLsizei bufSize, GLsizei *length, GLchar *label); -#ifdef GL_GLEXT_PROTOTYPES -GLAPI void APIENTRY glLabelObjectEXT (GLenum type, GLuint object, GLsizei length, const GLchar *label); -GLAPI void APIENTRY glGetObjectLabelEXT (GLenum type, GLuint object, GLsizei bufSize, GLsizei *length, GLchar *label); -#endif -#endif /* GL_EXT_debug_label */ - -#ifndef GL_EXT_debug_marker -#define GL_EXT_debug_marker 1 -typedef void (APIENTRYP PFNGLINSERTEVENTMARKEREXTPROC) (GLsizei length, const GLchar *marker); -typedef void (APIENTRYP PFNGLPUSHGROUPMARKEREXTPROC) (GLsizei length, const GLchar *marker); -typedef void (APIENTRYP PFNGLPOPGROUPMARKEREXTPROC) (void); -#ifdef GL_GLEXT_PROTOTYPES -GLAPI void APIENTRY glInsertEventMarkerEXT (GLsizei length, const GLchar *marker); -GLAPI void APIENTRY glPushGroupMarkerEXT (GLsizei length, const GLchar *marker); -GLAPI void APIENTRY glPopGroupMarkerEXT (void); -#endif -#endif /* GL_EXT_debug_marker */ - -#ifndef GL_EXT_depth_bounds_test -#define GL_EXT_depth_bounds_test 1 -#define GL_DEPTH_BOUNDS_TEST_EXT 0x8890 -#define GL_DEPTH_BOUNDS_EXT 0x8891 -typedef void (APIENTRYP PFNGLDEPTHBOUNDSEXTPROC) (GLclampd zmin, GLclampd zmax); -#ifdef GL_GLEXT_PROTOTYPES -GLAPI void APIENTRY glDepthBoundsEXT (GLclampd zmin, GLclampd zmax); -#endif -#endif /* GL_EXT_depth_bounds_test */ - -#ifndef GL_EXT_direct_state_access -#define GL_EXT_direct_state_access 1 -#define GL_PROGRAM_MATRIX_EXT 0x8E2D -#define GL_TRANSPOSE_PROGRAM_MATRIX_EXT 0x8E2E -#define GL_PROGRAM_MATRIX_STACK_DEPTH_EXT 0x8E2F -typedef void (APIENTRYP PFNGLMATRIXLOADFEXTPROC) (GLenum mode, const GLfloat *m); -typedef void (APIENTRYP PFNGLMATRIXLOADDEXTPROC) (GLenum mode, const GLdouble *m); -typedef void (APIENTRYP PFNGLMATRIXMULTFEXTPROC) (GLenum mode, const GLfloat *m); -typedef void (APIENTRYP PFNGLMATRIXMULTDEXTPROC) (GLenum mode, const GLdouble *m); -typedef void (APIENTRYP PFNGLMATRIXLOADIDENTITYEXTPROC) (GLenum mode); -typedef void (APIENTRYP PFNGLMATRIXROTATEFEXTPROC) (GLenum mode, GLfloat angle, GLfloat x, GLfloat y, GLfloat z); -typedef void (APIENTRYP PFNGLMATRIXROTATEDEXTPROC) (GLenum mode, GLdouble angle, GLdouble x, GLdouble y, GLdouble z); -typedef void (APIENTRYP PFNGLMATRIXSCALEFEXTPROC) (GLenum mode, GLfloat x, GLfloat y, GLfloat z); -typedef void (APIENTRYP PFNGLMATRIXSCALEDEXTPROC) (GLenum mode, GLdouble x, GLdouble y, GLdouble z); -typedef void (APIENTRYP PFNGLMATRIXTRANSLATEFEXTPROC) (GLenum mode, GLfloat x, GLfloat y, GLfloat z); -typedef void (APIENTRYP PFNGLMATRIXTRANSLATEDEXTPROC) (GLenum mode, GLdouble x, GLdouble y, GLdouble z); -typedef void (APIENTRYP PFNGLMATRIXFRUSTUMEXTPROC) (GLenum mode, GLdouble left, GLdouble right, GLdouble bottom, GLdouble top, GLdouble zNear, GLdouble zFar); -typedef void (APIENTRYP PFNGLMATRIXORTHOEXTPROC) (GLenum mode, GLdouble left, GLdouble right, GLdouble bottom, GLdouble top, GLdouble zNear, GLdouble zFar); -typedef void (APIENTRYP PFNGLMATRIXPOPEXTPROC) (GLenum mode); -typedef void (APIENTRYP PFNGLMATRIXPUSHEXTPROC) (GLenum mode); -typedef void (APIENTRYP PFNGLCLIENTATTRIBDEFAULTEXTPROC) (GLbitfield mask); -typedef void (APIENTRYP PFNGLPUSHCLIENTATTRIBDEFAULTEXTPROC) (GLbitfield mask); -typedef void (APIENTRYP PFNGLTEXTUREPARAMETERFEXTPROC) (GLuint texture, GLenum target, GLenum pname, GLfloat param); -typedef void (APIENTRYP PFNGLTEXTUREPARAMETERFVEXTPROC) (GLuint texture, GLenum target, GLenum pname, const GLfloat *params); -typedef void (APIENTRYP PFNGLTEXTUREPARAMETERIEXTPROC) (GLuint texture, GLenum target, GLenum pname, GLint param); -typedef void (APIENTRYP PFNGLTEXTUREPARAMETERIVEXTPROC) (GLuint texture, GLenum target, GLenum pname, const GLint *params); -typedef void (APIENTRYP PFNGLTEXTUREIMAGE1DEXTPROC) (GLuint texture, GLenum target, GLint level, GLint internalformat, GLsizei width, GLint border, GLenum format, GLenum type, const void *pixels); -typedef void (APIENTRYP PFNGLTEXTUREIMAGE2DEXTPROC) (GLuint texture, GLenum target, GLint level, GLint internalformat, GLsizei width, GLsizei height, GLint border, GLenum format, GLenum type, const void *pixels); -typedef void (APIENTRYP PFNGLTEXTURESUBIMAGE1DEXTPROC) (GLuint texture, GLenum target, GLint level, GLint xoffset, GLsizei width, GLenum format, GLenum type, const void *pixels); -typedef void (APIENTRYP PFNGLTEXTURESUBIMAGE2DEXTPROC) (GLuint texture, GLenum target, GLint level, GLint xoffset, GLint yoffset, GLsizei width, GLsizei height, GLenum format, GLenum type, const void *pixels); -typedef void (APIENTRYP PFNGLCOPYTEXTUREIMAGE1DEXTPROC) (GLuint texture, GLenum target, GLint level, GLenum internalformat, GLint x, GLint y, GLsizei width, GLint border); -typedef void (APIENTRYP PFNGLCOPYTEXTUREIMAGE2DEXTPROC) (GLuint texture, GLenum target, GLint level, GLenum internalformat, GLint x, GLint y, GLsizei width, GLsizei height, GLint border); -typedef void (APIENTRYP PFNGLCOPYTEXTURESUBIMAGE1DEXTPROC) (GLuint texture, GLenum target, GLint level, GLint xoffset, GLint x, GLint y, GLsizei width); -typedef void (APIENTRYP PFNGLCOPYTEXTURESUBIMAGE2DEXTPROC) (GLuint texture, GLenum target, GLint level, GLint xoffset, GLint yoffset, GLint x, GLint y, GLsizei width, GLsizei height); -typedef void (APIENTRYP PFNGLGETTEXTUREIMAGEEXTPROC) (GLuint texture, GLenum target, GLint level, GLenum format, GLenum type, void *pixels); -typedef void (APIENTRYP PFNGLGETTEXTUREPARAMETERFVEXTPROC) (GLuint texture, GLenum target, GLenum pname, GLfloat *params); -typedef void (APIENTRYP PFNGLGETTEXTUREPARAMETERIVEXTPROC) (GLuint texture, GLenum target, GLenum pname, GLint *params); -typedef void (APIENTRYP PFNGLGETTEXTURELEVELPARAMETERFVEXTPROC) (GLuint texture, GLenum target, GLint level, GLenum pname, GLfloat *params); -typedef void (APIENTRYP PFNGLGETTEXTURELEVELPARAMETERIVEXTPROC) (GLuint texture, GLenum target, GLint level, GLenum pname, GLint *params); -typedef void (APIENTRYP PFNGLTEXTUREIMAGE3DEXTPROC) (GLuint texture, GLenum target, GLint level, GLint internalformat, GLsizei width, GLsizei height, GLsizei depth, GLint border, GLenum format, GLenum type, const void *pixels); -typedef void (APIENTRYP PFNGLTEXTURESUBIMAGE3DEXTPROC) (GLuint texture, GLenum target, GLint level, GLint xoffset, GLint yoffset, GLint zoffset, GLsizei width, GLsizei height, GLsizei depth, GLenum format, GLenum type, const void *pixels); -typedef void (APIENTRYP PFNGLCOPYTEXTURESUBIMAGE3DEXTPROC) (GLuint texture, GLenum target, GLint level, GLint xoffset, GLint yoffset, GLint zoffset, GLint x, GLint y, GLsizei width, GLsizei height); -typedef void (APIENTRYP PFNGLBINDMULTITEXTUREEXTPROC) (GLenum texunit, GLenum target, GLuint texture); -typedef void (APIENTRYP PFNGLMULTITEXCOORDPOINTEREXTPROC) (GLenum texunit, GLint size, GLenum type, GLsizei stride, const void *pointer); -typedef void (APIENTRYP PFNGLMULTITEXENVFEXTPROC) (GLenum texunit, GLenum target, GLenum pname, GLfloat param); -typedef void (APIENTRYP PFNGLMULTITEXENVFVEXTPROC) (GLenum texunit, GLenum target, GLenum pname, const GLfloat *params); -typedef void (APIENTRYP PFNGLMULTITEXENVIEXTPROC) (GLenum texunit, GLenum target, GLenum pname, GLint param); -typedef void (APIENTRYP PFNGLMULTITEXENVIVEXTPROC) (GLenum texunit, GLenum target, GLenum pname, const GLint *params); -typedef void (APIENTRYP PFNGLMULTITEXGENDEXTPROC) (GLenum texunit, GLenum coord, GLenum pname, GLdouble param); -typedef void (APIENTRYP PFNGLMULTITEXGENDVEXTPROC) (GLenum texunit, GLenum coord, GLenum pname, const GLdouble *params); -typedef void (APIENTRYP PFNGLMULTITEXGENFEXTPROC) (GLenum texunit, GLenum coord, GLenum pname, GLfloat param); -typedef void (APIENTRYP PFNGLMULTITEXGENFVEXTPROC) (GLenum texunit, GLenum coord, GLenum pname, const GLfloat *params); -typedef void (APIENTRYP PFNGLMULTITEXGENIEXTPROC) (GLenum texunit, GLenum coord, GLenum pname, GLint param); -typedef void (APIENTRYP PFNGLMULTITEXGENIVEXTPROC) (GLenum texunit, GLenum coord, GLenum pname, const GLint *params); -typedef void (APIENTRYP PFNGLGETMULTITEXENVFVEXTPROC) (GLenum texunit, GLenum target, GLenum pname, GLfloat *params); -typedef void (APIENTRYP PFNGLGETMULTITEXENVIVEXTPROC) (GLenum texunit, GLenum target, GLenum pname, GLint *params); -typedef void (APIENTRYP PFNGLGETMULTITEXGENDVEXTPROC) (GLenum texunit, GLenum coord, GLenum pname, GLdouble *params); -typedef void (APIENTRYP PFNGLGETMULTITEXGENFVEXTPROC) (GLenum texunit, GLenum coord, GLenum pname, GLfloat *params); -typedef void (APIENTRYP PFNGLGETMULTITEXGENIVEXTPROC) (GLenum texunit, GLenum coord, GLenum pname, GLint *params); -typedef void (APIENTRYP PFNGLMULTITEXPARAMETERIEXTPROC) (GLenum texunit, GLenum target, GLenum pname, GLint param); -typedef void (APIENTRYP PFNGLMULTITEXPARAMETERIVEXTPROC) (GLenum texunit, GLenum target, GLenum pname, const GLint *params); -typedef void (APIENTRYP PFNGLMULTITEXPARAMETERFEXTPROC) (GLenum texunit, GLenum target, GLenum pname, GLfloat param); -typedef void (APIENTRYP PFNGLMULTITEXPARAMETERFVEXTPROC) (GLenum texunit, GLenum target, GLenum pname, const GLfloat *params); -typedef void (APIENTRYP PFNGLMULTITEXIMAGE1DEXTPROC) (GLenum texunit, GLenum target, GLint level, GLint internalformat, GLsizei width, GLint border, GLenum format, GLenum type, const void *pixels); -typedef void (APIENTRYP PFNGLMULTITEXIMAGE2DEXTPROC) (GLenum texunit, GLenum target, GLint level, GLint internalformat, GLsizei width, GLsizei height, GLint border, GLenum format, GLenum type, const void *pixels); -typedef void (APIENTRYP PFNGLMULTITEXSUBIMAGE1DEXTPROC) (GLenum texunit, GLenum target, GLint level, GLint xoffset, GLsizei width, GLenum format, GLenum type, const void *pixels); -typedef void (APIENTRYP PFNGLMULTITEXSUBIMAGE2DEXTPROC) (GLenum texunit, GLenum target, GLint level, GLint xoffset, GLint yoffset, GLsizei width, GLsizei height, GLenum format, GLenum type, const void *pixels); -typedef void (APIENTRYP PFNGLCOPYMULTITEXIMAGE1DEXTPROC) (GLenum texunit, GLenum target, GLint level, GLenum internalformat, GLint x, GLint y, GLsizei width, GLint border); -typedef void (APIENTRYP PFNGLCOPYMULTITEXIMAGE2DEXTPROC) (GLenum texunit, GLenum target, GLint level, GLenum internalformat, GLint x, GLint y, GLsizei width, GLsizei height, GLint border); -typedef void (APIENTRYP PFNGLCOPYMULTITEXSUBIMAGE1DEXTPROC) (GLenum texunit, GLenum target, GLint level, GLint xoffset, GLint x, GLint y, GLsizei width); -typedef void (APIENTRYP PFNGLCOPYMULTITEXSUBIMAGE2DEXTPROC) (GLenum texunit, GLenum target, GLint level, GLint xoffset, GLint yoffset, GLint x, GLint y, GLsizei width, GLsizei height); -typedef void (APIENTRYP PFNGLGETMULTITEXIMAGEEXTPROC) (GLenum texunit, GLenum target, GLint level, GLenum format, GLenum type, void *pixels); -typedef void (APIENTRYP PFNGLGETMULTITEXPARAMETERFVEXTPROC) (GLenum texunit, GLenum target, GLenum pname, GLfloat *params); -typedef void (APIENTRYP PFNGLGETMULTITEXPARAMETERIVEXTPROC) (GLenum texunit, GLenum target, GLenum pname, GLint *params); -typedef void (APIENTRYP PFNGLGETMULTITEXLEVELPARAMETERFVEXTPROC) (GLenum texunit, GLenum target, GLint level, GLenum pname, GLfloat *params); -typedef void (APIENTRYP PFNGLGETMULTITEXLEVELPARAMETERIVEXTPROC) (GLenum texunit, GLenum target, GLint level, GLenum pname, GLint *params); -typedef void (APIENTRYP PFNGLMULTITEXIMAGE3DEXTPROC) (GLenum texunit, GLenum target, GLint level, GLint internalformat, GLsizei width, GLsizei height, GLsizei depth, GLint border, GLenum format, GLenum type, const void *pixels); -typedef void (APIENTRYP PFNGLMULTITEXSUBIMAGE3DEXTPROC) (GLenum texunit, GLenum target, GLint level, GLint xoffset, GLint yoffset, GLint zoffset, GLsizei width, GLsizei height, GLsizei depth, GLenum format, GLenum type, const void *pixels); -typedef void (APIENTRYP PFNGLCOPYMULTITEXSUBIMAGE3DEXTPROC) (GLenum texunit, GLenum target, GLint level, GLint xoffset, GLint yoffset, GLint zoffset, GLint x, GLint y, GLsizei width, GLsizei height); -typedef void (APIENTRYP PFNGLENABLECLIENTSTATEINDEXEDEXTPROC) (GLenum array, GLuint index); -typedef void (APIENTRYP PFNGLDISABLECLIENTSTATEINDEXEDEXTPROC) (GLenum array, GLuint index); -typedef void (APIENTRYP PFNGLGETFLOATINDEXEDVEXTPROC) (GLenum target, GLuint index, GLfloat *data); -typedef void (APIENTRYP PFNGLGETDOUBLEINDEXEDVEXTPROC) (GLenum target, GLuint index, GLdouble *data); -typedef void (APIENTRYP PFNGLGETPOINTERINDEXEDVEXTPROC) (GLenum target, GLuint index, void **data); -typedef void (APIENTRYP PFNGLENABLEINDEXEDEXTPROC) (GLenum target, GLuint index); -typedef void (APIENTRYP PFNGLDISABLEINDEXEDEXTPROC) (GLenum target, GLuint index); -typedef GLboolean (APIENTRYP PFNGLISENABLEDINDEXEDEXTPROC) (GLenum target, GLuint index); -typedef void (APIENTRYP PFNGLGETINTEGERINDEXEDVEXTPROC) (GLenum target, GLuint index, GLint *data); -typedef void (APIENTRYP PFNGLGETBOOLEANINDEXEDVEXTPROC) (GLenum target, GLuint index, GLboolean *data); -typedef void (APIENTRYP PFNGLCOMPRESSEDTEXTUREIMAGE3DEXTPROC) (GLuint texture, GLenum target, GLint level, GLenum internalformat, GLsizei width, GLsizei height, GLsizei depth, GLint border, GLsizei imageSize, const void *bits); -typedef void (APIENTRYP PFNGLCOMPRESSEDTEXTUREIMAGE2DEXTPROC) (GLuint texture, GLenum target, GLint level, GLenum internalformat, GLsizei width, GLsizei height, GLint border, GLsizei imageSize, const void *bits); -typedef void (APIENTRYP PFNGLCOMPRESSEDTEXTUREIMAGE1DEXTPROC) (GLuint texture, GLenum target, GLint level, GLenum internalformat, GLsizei width, GLint border, GLsizei imageSize, const void *bits); -typedef void (APIENTRYP PFNGLCOMPRESSEDTEXTURESUBIMAGE3DEXTPROC) (GLuint texture, GLenum target, GLint level, GLint xoffset, GLint yoffset, GLint zoffset, GLsizei width, GLsizei height, GLsizei depth, GLenum format, GLsizei imageSize, const void *bits); -typedef void (APIENTRYP PFNGLCOMPRESSEDTEXTURESUBIMAGE2DEXTPROC) (GLuint texture, GLenum target, GLint level, GLint xoffset, GLint yoffset, GLsizei width, GLsizei height, GLenum format, GLsizei imageSize, const void *bits); -typedef void (APIENTRYP PFNGLCOMPRESSEDTEXTURESUBIMAGE1DEXTPROC) (GLuint texture, GLenum target, GLint level, GLint xoffset, GLsizei width, GLenum format, GLsizei imageSize, const void *bits); -typedef void (APIENTRYP PFNGLGETCOMPRESSEDTEXTUREIMAGEEXTPROC) (GLuint texture, GLenum target, GLint lod, void *img); -typedef void (APIENTRYP PFNGLCOMPRESSEDMULTITEXIMAGE3DEXTPROC) (GLenum texunit, GLenum target, GLint level, GLenum internalformat, GLsizei width, GLsizei height, GLsizei depth, GLint border, GLsizei imageSize, const void *bits); -typedef void (APIENTRYP PFNGLCOMPRESSEDMULTITEXIMAGE2DEXTPROC) (GLenum texunit, GLenum target, GLint level, GLenum internalformat, GLsizei width, GLsizei height, GLint border, GLsizei imageSize, const void *bits); -typedef void (APIENTRYP PFNGLCOMPRESSEDMULTITEXIMAGE1DEXTPROC) (GLenum texunit, GLenum target, GLint level, GLenum internalformat, GLsizei width, GLint border, GLsizei imageSize, const void *bits); -typedef void (APIENTRYP PFNGLCOMPRESSEDMULTITEXSUBIMAGE3DEXTPROC) (GLenum texunit, GLenum target, GLint level, GLint xoffset, GLint yoffset, GLint zoffset, GLsizei width, GLsizei height, GLsizei depth, GLenum format, GLsizei imageSize, const void *bits); -typedef void (APIENTRYP PFNGLCOMPRESSEDMULTITEXSUBIMAGE2DEXTPROC) (GLenum texunit, GLenum target, GLint level, GLint xoffset, GLint yoffset, GLsizei width, GLsizei height, GLenum format, GLsizei imageSize, const void *bits); -typedef void (APIENTRYP PFNGLCOMPRESSEDMULTITEXSUBIMAGE1DEXTPROC) (GLenum texunit, GLenum target, GLint level, GLint xoffset, GLsizei width, GLenum format, GLsizei imageSize, const void *bits); -typedef void (APIENTRYP PFNGLGETCOMPRESSEDMULTITEXIMAGEEXTPROC) (GLenum texunit, GLenum target, GLint lod, void *img); -typedef void (APIENTRYP PFNGLMATRIXLOADTRANSPOSEFEXTPROC) (GLenum mode, const GLfloat *m); -typedef void (APIENTRYP PFNGLMATRIXLOADTRANSPOSEDEXTPROC) (GLenum mode, const GLdouble *m); -typedef void (APIENTRYP PFNGLMATRIXMULTTRANSPOSEFEXTPROC) (GLenum mode, const GLfloat *m); -typedef void (APIENTRYP PFNGLMATRIXMULTTRANSPOSEDEXTPROC) (GLenum mode, const GLdouble *m); -typedef void (APIENTRYP PFNGLNAMEDBUFFERDATAEXTPROC) (GLuint buffer, GLsizeiptr size, const void *data, GLenum usage); -typedef void (APIENTRYP PFNGLNAMEDBUFFERSUBDATAEXTPROC) (GLuint buffer, GLintptr offset, GLsizeiptr size, const void *data); -typedef void *(APIENTRYP PFNGLMAPNAMEDBUFFEREXTPROC) (GLuint buffer, GLenum access); -typedef GLboolean (APIENTRYP PFNGLUNMAPNAMEDBUFFEREXTPROC) (GLuint buffer); -typedef void (APIENTRYP PFNGLGETNAMEDBUFFERPARAMETERIVEXTPROC) (GLuint buffer, GLenum pname, GLint *params); -typedef void (APIENTRYP PFNGLGETNAMEDBUFFERPOINTERVEXTPROC) (GLuint buffer, GLenum pname, void **params); -typedef void (APIENTRYP PFNGLGETNAMEDBUFFERSUBDATAEXTPROC) (GLuint buffer, GLintptr offset, GLsizeiptr size, void *data); -typedef void (APIENTRYP PFNGLPROGRAMUNIFORM1FEXTPROC) (GLuint program, GLint location, GLfloat v0); -typedef void (APIENTRYP PFNGLPROGRAMUNIFORM2FEXTPROC) (GLuint program, GLint location, GLfloat v0, GLfloat v1); -typedef void (APIENTRYP PFNGLPROGRAMUNIFORM3FEXTPROC) (GLuint program, GLint location, GLfloat v0, GLfloat v1, GLfloat v2); -typedef void (APIENTRYP PFNGLPROGRAMUNIFORM4FEXTPROC) (GLuint program, GLint location, GLfloat v0, GLfloat v1, GLfloat v2, GLfloat v3); -typedef void (APIENTRYP PFNGLPROGRAMUNIFORM1IEXTPROC) (GLuint program, GLint location, GLint v0); -typedef void (APIENTRYP PFNGLPROGRAMUNIFORM2IEXTPROC) (GLuint program, GLint location, GLint v0, GLint v1); -typedef void (APIENTRYP PFNGLPROGRAMUNIFORM3IEXTPROC) (GLuint program, GLint location, GLint v0, GLint v1, GLint v2); -typedef void (APIENTRYP PFNGLPROGRAMUNIFORM4IEXTPROC) (GLuint program, GLint location, GLint v0, GLint v1, GLint v2, GLint v3); -typedef void (APIENTRYP PFNGLPROGRAMUNIFORM1FVEXTPROC) (GLuint program, GLint location, GLsizei count, const GLfloat *value); -typedef void (APIENTRYP PFNGLPROGRAMUNIFORM2FVEXTPROC) (GLuint program, GLint location, GLsizei count, const GLfloat *value); -typedef void (APIENTRYP PFNGLPROGRAMUNIFORM3FVEXTPROC) (GLuint program, GLint location, GLsizei count, const GLfloat *value); -typedef void (APIENTRYP PFNGLPROGRAMUNIFORM4FVEXTPROC) (GLuint program, GLint location, GLsizei count, const GLfloat *value); -typedef void (APIENTRYP PFNGLPROGRAMUNIFORM1IVEXTPROC) (GLuint program, GLint location, GLsizei count, const GLint *value); -typedef void (APIENTRYP PFNGLPROGRAMUNIFORM2IVEXTPROC) (GLuint program, GLint location, GLsizei count, const GLint *value); -typedef void (APIENTRYP PFNGLPROGRAMUNIFORM3IVEXTPROC) (GLuint program, GLint location, GLsizei count, const GLint *value); -typedef void (APIENTRYP PFNGLPROGRAMUNIFORM4IVEXTPROC) (GLuint program, GLint location, GLsizei count, const GLint *value); -typedef void (APIENTRYP PFNGLPROGRAMUNIFORMMATRIX2FVEXTPROC) (GLuint program, GLint location, GLsizei count, GLboolean transpose, const GLfloat *value); -typedef void (APIENTRYP PFNGLPROGRAMUNIFORMMATRIX3FVEXTPROC) (GLuint program, GLint location, GLsizei count, GLboolean transpose, const GLfloat *value); -typedef void (APIENTRYP PFNGLPROGRAMUNIFORMMATRIX4FVEXTPROC) (GLuint program, GLint location, GLsizei count, GLboolean transpose, const GLfloat *value); -typedef void (APIENTRYP PFNGLPROGRAMUNIFORMMATRIX2X3FVEXTPROC) (GLuint program, GLint location, GLsizei count, GLboolean transpose, const GLfloat *value); -typedef void (APIENTRYP PFNGLPROGRAMUNIFORMMATRIX3X2FVEXTPROC) (GLuint program, GLint location, GLsizei count, GLboolean transpose, const GLfloat *value); -typedef void (APIENTRYP PFNGLPROGRAMUNIFORMMATRIX2X4FVEXTPROC) (GLuint program, GLint location, GLsizei count, GLboolean transpose, const GLfloat *value); -typedef void (APIENTRYP PFNGLPROGRAMUNIFORMMATRIX4X2FVEXTPROC) (GLuint program, GLint location, GLsizei count, GLboolean transpose, const GLfloat *value); -typedef void (APIENTRYP PFNGLPROGRAMUNIFORMMATRIX3X4FVEXTPROC) (GLuint program, GLint location, GLsizei count, GLboolean transpose, const GLfloat *value); -typedef void (APIENTRYP PFNGLPROGRAMUNIFORMMATRIX4X3FVEXTPROC) (GLuint program, GLint location, GLsizei count, GLboolean transpose, const GLfloat *value); -typedef void (APIENTRYP PFNGLTEXTUREBUFFEREXTPROC) (GLuint texture, GLenum target, GLenum internalformat, GLuint buffer); -typedef void (APIENTRYP PFNGLMULTITEXBUFFEREXTPROC) (GLenum texunit, GLenum target, GLenum internalformat, GLuint buffer); -typedef void (APIENTRYP PFNGLTEXTUREPARAMETERIIVEXTPROC) (GLuint texture, GLenum target, GLenum pname, const GLint *params); -typedef void (APIENTRYP PFNGLTEXTUREPARAMETERIUIVEXTPROC) (GLuint texture, GLenum target, GLenum pname, const GLuint *params); -typedef void (APIENTRYP PFNGLGETTEXTUREPARAMETERIIVEXTPROC) (GLuint texture, GLenum target, GLenum pname, GLint *params); -typedef void (APIENTRYP PFNGLGETTEXTUREPARAMETERIUIVEXTPROC) (GLuint texture, GLenum target, GLenum pname, GLuint *params); -typedef void (APIENTRYP PFNGLMULTITEXPARAMETERIIVEXTPROC) (GLenum texunit, GLenum target, GLenum pname, const GLint *params); -typedef void (APIENTRYP PFNGLMULTITEXPARAMETERIUIVEXTPROC) (GLenum texunit, GLenum target, GLenum pname, const GLuint *params); -typedef void (APIENTRYP PFNGLGETMULTITEXPARAMETERIIVEXTPROC) (GLenum texunit, GLenum target, GLenum pname, GLint *params); -typedef void (APIENTRYP PFNGLGETMULTITEXPARAMETERIUIVEXTPROC) (GLenum texunit, GLenum target, GLenum pname, GLuint *params); -typedef void (APIENTRYP PFNGLPROGRAMUNIFORM1UIEXTPROC) (GLuint program, GLint location, GLuint v0); -typedef void (APIENTRYP PFNGLPROGRAMUNIFORM2UIEXTPROC) (GLuint program, GLint location, GLuint v0, GLuint v1); -typedef void (APIENTRYP PFNGLPROGRAMUNIFORM3UIEXTPROC) (GLuint program, GLint location, GLuint v0, GLuint v1, GLuint v2); -typedef void (APIENTRYP PFNGLPROGRAMUNIFORM4UIEXTPROC) (GLuint program, GLint location, GLuint v0, GLuint v1, GLuint v2, GLuint v3); -typedef void (APIENTRYP PFNGLPROGRAMUNIFORM1UIVEXTPROC) (GLuint program, GLint location, GLsizei count, const GLuint *value); -typedef void (APIENTRYP PFNGLPROGRAMUNIFORM2UIVEXTPROC) (GLuint program, GLint location, GLsizei count, const GLuint *value); -typedef void (APIENTRYP PFNGLPROGRAMUNIFORM3UIVEXTPROC) (GLuint program, GLint location, GLsizei count, const GLuint *value); -typedef void (APIENTRYP PFNGLPROGRAMUNIFORM4UIVEXTPROC) (GLuint program, GLint location, GLsizei count, const GLuint *value); -typedef void (APIENTRYP PFNGLNAMEDPROGRAMLOCALPARAMETERS4FVEXTPROC) (GLuint program, GLenum target, GLuint index, GLsizei count, const GLfloat *params); -typedef void (APIENTRYP PFNGLNAMEDPROGRAMLOCALPARAMETERI4IEXTPROC) (GLuint program, GLenum target, GLuint index, GLint x, GLint y, GLint z, GLint w); -typedef void (APIENTRYP PFNGLNAMEDPROGRAMLOCALPARAMETERI4IVEXTPROC) (GLuint program, GLenum target, GLuint index, const GLint *params); -typedef void (APIENTRYP PFNGLNAMEDPROGRAMLOCALPARAMETERSI4IVEXTPROC) (GLuint program, GLenum target, GLuint index, GLsizei count, const GLint *params); -typedef void (APIENTRYP PFNGLNAMEDPROGRAMLOCALPARAMETERI4UIEXTPROC) (GLuint program, GLenum target, GLuint index, GLuint x, GLuint y, GLuint z, GLuint w); -typedef void (APIENTRYP PFNGLNAMEDPROGRAMLOCALPARAMETERI4UIVEXTPROC) (GLuint program, GLenum target, GLuint index, const GLuint *params); -typedef void (APIENTRYP PFNGLNAMEDPROGRAMLOCALPARAMETERSI4UIVEXTPROC) (GLuint program, GLenum target, GLuint index, GLsizei count, const GLuint *params); -typedef void (APIENTRYP PFNGLGETNAMEDPROGRAMLOCALPARAMETERIIVEXTPROC) (GLuint program, GLenum target, GLuint index, GLint *params); -typedef void (APIENTRYP PFNGLGETNAMEDPROGRAMLOCALPARAMETERIUIVEXTPROC) (GLuint program, GLenum target, GLuint index, GLuint *params); -typedef void (APIENTRYP PFNGLENABLECLIENTSTATEIEXTPROC) (GLenum array, GLuint index); -typedef void (APIENTRYP PFNGLDISABLECLIENTSTATEIEXTPROC) (GLenum array, GLuint index); -typedef void (APIENTRYP PFNGLGETFLOATI_VEXTPROC) (GLenum pname, GLuint index, GLfloat *params); -typedef void (APIENTRYP PFNGLGETDOUBLEI_VEXTPROC) (GLenum pname, GLuint index, GLdouble *params); -typedef void (APIENTRYP PFNGLGETPOINTERI_VEXTPROC) (GLenum pname, GLuint index, void **params); -typedef void (APIENTRYP PFNGLNAMEDPROGRAMSTRINGEXTPROC) (GLuint program, GLenum target, GLenum format, GLsizei len, const void *string); -typedef void (APIENTRYP PFNGLNAMEDPROGRAMLOCALPARAMETER4DEXTPROC) (GLuint program, GLenum target, GLuint index, GLdouble x, GLdouble y, GLdouble z, GLdouble w); -typedef void (APIENTRYP PFNGLNAMEDPROGRAMLOCALPARAMETER4DVEXTPROC) (GLuint program, GLenum target, GLuint index, const GLdouble *params); -typedef void (APIENTRYP PFNGLNAMEDPROGRAMLOCALPARAMETER4FEXTPROC) (GLuint program, GLenum target, GLuint index, GLfloat x, GLfloat y, GLfloat z, GLfloat w); -typedef void (APIENTRYP PFNGLNAMEDPROGRAMLOCALPARAMETER4FVEXTPROC) (GLuint program, GLenum target, GLuint index, const GLfloat *params); -typedef void (APIENTRYP PFNGLGETNAMEDPROGRAMLOCALPARAMETERDVEXTPROC) (GLuint program, GLenum target, GLuint index, GLdouble *params); -typedef void (APIENTRYP PFNGLGETNAMEDPROGRAMLOCALPARAMETERFVEXTPROC) (GLuint program, GLenum target, GLuint index, GLfloat *params); -typedef void (APIENTRYP PFNGLGETNAMEDPROGRAMIVEXTPROC) (GLuint program, GLenum target, GLenum pname, GLint *params); -typedef void (APIENTRYP PFNGLGETNAMEDPROGRAMSTRINGEXTPROC) (GLuint program, GLenum target, GLenum pname, void *string); -typedef void (APIENTRYP PFNGLNAMEDRENDERBUFFERSTORAGEEXTPROC) (GLuint renderbuffer, GLenum internalformat, GLsizei width, GLsizei height); -typedef void (APIENTRYP PFNGLGETNAMEDRENDERBUFFERPARAMETERIVEXTPROC) (GLuint renderbuffer, GLenum pname, GLint *params); -typedef void (APIENTRYP PFNGLNAMEDRENDERBUFFERSTORAGEMULTISAMPLEEXTPROC) (GLuint renderbuffer, GLsizei samples, GLenum internalformat, GLsizei width, GLsizei height); -typedef void (APIENTRYP PFNGLNAMEDRENDERBUFFERSTORAGEMULTISAMPLECOVERAGEEXTPROC) (GLuint renderbuffer, GLsizei coverageSamples, GLsizei colorSamples, GLenum internalformat, GLsizei width, GLsizei height); -typedef GLenum (APIENTRYP PFNGLCHECKNAMEDFRAMEBUFFERSTATUSEXTPROC) (GLuint framebuffer, GLenum target); -typedef void (APIENTRYP PFNGLNAMEDFRAMEBUFFERTEXTURE1DEXTPROC) (GLuint framebuffer, GLenum attachment, GLenum textarget, GLuint texture, GLint level); -typedef void (APIENTRYP PFNGLNAMEDFRAMEBUFFERTEXTURE2DEXTPROC) (GLuint framebuffer, GLenum attachment, GLenum textarget, GLuint texture, GLint level); -typedef void (APIENTRYP PFNGLNAMEDFRAMEBUFFERTEXTURE3DEXTPROC) (GLuint framebuffer, GLenum attachment, GLenum textarget, GLuint texture, GLint level, GLint zoffset); -typedef void (APIENTRYP PFNGLNAMEDFRAMEBUFFERRENDERBUFFEREXTPROC) (GLuint framebuffer, GLenum attachment, GLenum renderbuffertarget, GLuint renderbuffer); -typedef void (APIENTRYP PFNGLGETNAMEDFRAMEBUFFERATTACHMENTPARAMETERIVEXTPROC) (GLuint framebuffer, GLenum attachment, GLenum pname, GLint *params); -typedef void (APIENTRYP PFNGLGENERATETEXTUREMIPMAPEXTPROC) (GLuint texture, GLenum target); -typedef void (APIENTRYP PFNGLGENERATEMULTITEXMIPMAPEXTPROC) (GLenum texunit, GLenum target); -typedef void (APIENTRYP PFNGLFRAMEBUFFERDRAWBUFFEREXTPROC) (GLuint framebuffer, GLenum mode); -typedef void (APIENTRYP PFNGLFRAMEBUFFERDRAWBUFFERSEXTPROC) (GLuint framebuffer, GLsizei n, const GLenum *bufs); -typedef void (APIENTRYP PFNGLFRAMEBUFFERREADBUFFEREXTPROC) (GLuint framebuffer, GLenum mode); -typedef void (APIENTRYP PFNGLGETFRAMEBUFFERPARAMETERIVEXTPROC) (GLuint framebuffer, GLenum pname, GLint *params); -typedef void (APIENTRYP PFNGLNAMEDCOPYBUFFERSUBDATAEXTPROC) (GLuint readBuffer, GLuint writeBuffer, GLintptr readOffset, GLintptr writeOffset, GLsizeiptr size); -typedef void (APIENTRYP PFNGLNAMEDFRAMEBUFFERTEXTUREEXTPROC) (GLuint framebuffer, GLenum attachment, GLuint texture, GLint level); -typedef void (APIENTRYP PFNGLNAMEDFRAMEBUFFERTEXTURELAYEREXTPROC) (GLuint framebuffer, GLenum attachment, GLuint texture, GLint level, GLint layer); -typedef void (APIENTRYP PFNGLNAMEDFRAMEBUFFERTEXTUREFACEEXTPROC) (GLuint framebuffer, GLenum attachment, GLuint texture, GLint level, GLenum face); -typedef void (APIENTRYP PFNGLTEXTURERENDERBUFFEREXTPROC) (GLuint texture, GLenum target, GLuint renderbuffer); -typedef void (APIENTRYP PFNGLMULTITEXRENDERBUFFEREXTPROC) (GLenum texunit, GLenum target, GLuint renderbuffer); -typedef void (APIENTRYP PFNGLVERTEXARRAYVERTEXOFFSETEXTPROC) (GLuint vaobj, GLuint buffer, GLint size, GLenum type, GLsizei stride, GLintptr offset); -typedef void (APIENTRYP PFNGLVERTEXARRAYCOLOROFFSETEXTPROC) (GLuint vaobj, GLuint buffer, GLint size, GLenum type, GLsizei stride, GLintptr offset); -typedef void (APIENTRYP PFNGLVERTEXARRAYEDGEFLAGOFFSETEXTPROC) (GLuint vaobj, GLuint buffer, GLsizei stride, GLintptr offset); -typedef void (APIENTRYP PFNGLVERTEXARRAYINDEXOFFSETEXTPROC) (GLuint vaobj, GLuint buffer, GLenum type, GLsizei stride, GLintptr offset); -typedef void (APIENTRYP PFNGLVERTEXARRAYNORMALOFFSETEXTPROC) (GLuint vaobj, GLuint buffer, GLenum type, GLsizei stride, GLintptr offset); -typedef void (APIENTRYP PFNGLVERTEXARRAYTEXCOORDOFFSETEXTPROC) (GLuint vaobj, GLuint buffer, GLint size, GLenum type, GLsizei stride, GLintptr offset); -typedef void (APIENTRYP PFNGLVERTEXARRAYMULTITEXCOORDOFFSETEXTPROC) (GLuint vaobj, GLuint buffer, GLenum texunit, GLint size, GLenum type, GLsizei stride, GLintptr offset); -typedef void (APIENTRYP PFNGLVERTEXARRAYFOGCOORDOFFSETEXTPROC) (GLuint vaobj, GLuint buffer, GLenum type, GLsizei stride, GLintptr offset); -typedef void (APIENTRYP PFNGLVERTEXARRAYSECONDARYCOLOROFFSETEXTPROC) (GLuint vaobj, GLuint buffer, GLint size, GLenum type, GLsizei stride, GLintptr offset); -typedef void (APIENTRYP PFNGLVERTEXARRAYVERTEXATTRIBOFFSETEXTPROC) (GLuint vaobj, GLuint buffer, GLuint index, GLint size, GLenum type, GLboolean normalized, GLsizei stride, GLintptr offset); -typedef void (APIENTRYP PFNGLVERTEXARRAYVERTEXATTRIBIOFFSETEXTPROC) (GLuint vaobj, GLuint buffer, GLuint index, GLint size, GLenum type, GLsizei stride, GLintptr offset); -typedef void (APIENTRYP PFNGLENABLEVERTEXARRAYEXTPROC) (GLuint vaobj, GLenum array); -typedef void (APIENTRYP PFNGLDISABLEVERTEXARRAYEXTPROC) (GLuint vaobj, GLenum array); -typedef void (APIENTRYP PFNGLENABLEVERTEXARRAYATTRIBEXTPROC) (GLuint vaobj, GLuint index); -typedef void (APIENTRYP PFNGLDISABLEVERTEXARRAYATTRIBEXTPROC) (GLuint vaobj, GLuint index); -typedef void (APIENTRYP PFNGLGETVERTEXARRAYINTEGERVEXTPROC) (GLuint vaobj, GLenum pname, GLint *param); -typedef void (APIENTRYP PFNGLGETVERTEXARRAYPOINTERVEXTPROC) (GLuint vaobj, GLenum pname, void **param); -typedef void (APIENTRYP PFNGLGETVERTEXARRAYINTEGERI_VEXTPROC) (GLuint vaobj, GLuint index, GLenum pname, GLint *param); -typedef void (APIENTRYP PFNGLGETVERTEXARRAYPOINTERI_VEXTPROC) (GLuint vaobj, GLuint index, GLenum pname, void **param); -typedef void *(APIENTRYP PFNGLMAPNAMEDBUFFERRANGEEXTPROC) (GLuint buffer, GLintptr offset, GLsizeiptr length, GLbitfield access); -typedef void (APIENTRYP PFNGLFLUSHMAPPEDNAMEDBUFFERRANGEEXTPROC) (GLuint buffer, GLintptr offset, GLsizeiptr length); -typedef void (APIENTRYP PFNGLNAMEDBUFFERSTORAGEEXTPROC) (GLuint buffer, GLsizeiptr size, const void *data, GLbitfield flags); -typedef void (APIENTRYP PFNGLCLEARNAMEDBUFFERDATAEXTPROC) (GLuint buffer, GLenum internalformat, GLenum format, GLenum type, const void *data); -typedef void (APIENTRYP PFNGLCLEARNAMEDBUFFERSUBDATAEXTPROC) (GLuint buffer, GLenum internalformat, GLsizeiptr offset, GLsizeiptr size, GLenum format, GLenum type, const void *data); -typedef void (APIENTRYP PFNGLNAMEDFRAMEBUFFERPARAMETERIEXTPROC) (GLuint framebuffer, GLenum pname, GLint param); -typedef void (APIENTRYP PFNGLGETNAMEDFRAMEBUFFERPARAMETERIVEXTPROC) (GLuint framebuffer, GLenum pname, GLint *params); -typedef void (APIENTRYP PFNGLPROGRAMUNIFORM1DEXTPROC) (GLuint program, GLint location, GLdouble x); -typedef void (APIENTRYP PFNGLPROGRAMUNIFORM2DEXTPROC) (GLuint program, GLint location, GLdouble x, GLdouble y); -typedef void (APIENTRYP PFNGLPROGRAMUNIFORM3DEXTPROC) (GLuint program, GLint location, GLdouble x, GLdouble y, GLdouble z); -typedef void (APIENTRYP PFNGLPROGRAMUNIFORM4DEXTPROC) (GLuint program, GLint location, GLdouble x, GLdouble y, GLdouble z, GLdouble w); -typedef void (APIENTRYP PFNGLPROGRAMUNIFORM1DVEXTPROC) (GLuint program, GLint location, GLsizei count, const GLdouble *value); -typedef void (APIENTRYP PFNGLPROGRAMUNIFORM2DVEXTPROC) (GLuint program, GLint location, GLsizei count, const GLdouble *value); -typedef void (APIENTRYP PFNGLPROGRAMUNIFORM3DVEXTPROC) (GLuint program, GLint location, GLsizei count, const GLdouble *value); -typedef void (APIENTRYP PFNGLPROGRAMUNIFORM4DVEXTPROC) (GLuint program, GLint location, GLsizei count, const GLdouble *value); -typedef void (APIENTRYP PFNGLPROGRAMUNIFORMMATRIX2DVEXTPROC) (GLuint program, GLint location, GLsizei count, GLboolean transpose, const GLdouble *value); -typedef void (APIENTRYP PFNGLPROGRAMUNIFORMMATRIX3DVEXTPROC) (GLuint program, GLint location, GLsizei count, GLboolean transpose, const GLdouble *value); -typedef void (APIENTRYP PFNGLPROGRAMUNIFORMMATRIX4DVEXTPROC) (GLuint program, GLint location, GLsizei count, GLboolean transpose, const GLdouble *value); -typedef void (APIENTRYP PFNGLPROGRAMUNIFORMMATRIX2X3DVEXTPROC) (GLuint program, GLint location, GLsizei count, GLboolean transpose, const GLdouble *value); -typedef void (APIENTRYP PFNGLPROGRAMUNIFORMMATRIX2X4DVEXTPROC) (GLuint program, GLint location, GLsizei count, GLboolean transpose, const GLdouble *value); -typedef void (APIENTRYP PFNGLPROGRAMUNIFORMMATRIX3X2DVEXTPROC) (GLuint program, GLint location, GLsizei count, GLboolean transpose, const GLdouble *value); -typedef void (APIENTRYP PFNGLPROGRAMUNIFORMMATRIX3X4DVEXTPROC) (GLuint program, GLint location, GLsizei count, GLboolean transpose, const GLdouble *value); -typedef void (APIENTRYP PFNGLPROGRAMUNIFORMMATRIX4X2DVEXTPROC) (GLuint program, GLint location, GLsizei count, GLboolean transpose, const GLdouble *value); -typedef void (APIENTRYP PFNGLPROGRAMUNIFORMMATRIX4X3DVEXTPROC) (GLuint program, GLint location, GLsizei count, GLboolean transpose, const GLdouble *value); -typedef void (APIENTRYP PFNGLTEXTUREBUFFERRANGEEXTPROC) (GLuint texture, GLenum target, GLenum internalformat, GLuint buffer, GLintptr offset, GLsizeiptr size); -typedef void (APIENTRYP PFNGLTEXTURESTORAGE1DEXTPROC) (GLuint texture, GLenum target, GLsizei levels, GLenum internalformat, GLsizei width); -typedef void (APIENTRYP PFNGLTEXTURESTORAGE2DEXTPROC) (GLuint texture, GLenum target, GLsizei levels, GLenum internalformat, GLsizei width, GLsizei height); -typedef void (APIENTRYP PFNGLTEXTURESTORAGE3DEXTPROC) (GLuint texture, GLenum target, GLsizei levels, GLenum internalformat, GLsizei width, GLsizei height, GLsizei depth); -typedef void (APIENTRYP PFNGLTEXTURESTORAGE2DMULTISAMPLEEXTPROC) (GLuint texture, GLenum target, GLsizei samples, GLenum internalformat, GLsizei width, GLsizei height, GLboolean fixedsamplelocations); -typedef void (APIENTRYP PFNGLTEXTURESTORAGE3DMULTISAMPLEEXTPROC) (GLuint texture, GLenum target, GLsizei samples, GLenum internalformat, GLsizei width, GLsizei height, GLsizei depth, GLboolean fixedsamplelocations); -typedef void (APIENTRYP PFNGLVERTEXARRAYBINDVERTEXBUFFEREXTPROC) (GLuint vaobj, GLuint bindingindex, GLuint buffer, GLintptr offset, GLsizei stride); -typedef void (APIENTRYP PFNGLVERTEXARRAYVERTEXATTRIBFORMATEXTPROC) (GLuint vaobj, GLuint attribindex, GLint size, GLenum type, GLboolean normalized, GLuint relativeoffset); -typedef void (APIENTRYP PFNGLVERTEXARRAYVERTEXATTRIBIFORMATEXTPROC) (GLuint vaobj, GLuint attribindex, GLint size, GLenum type, GLuint relativeoffset); -typedef void (APIENTRYP PFNGLVERTEXARRAYVERTEXATTRIBLFORMATEXTPROC) (GLuint vaobj, GLuint attribindex, GLint size, GLenum type, GLuint relativeoffset); -typedef void (APIENTRYP PFNGLVERTEXARRAYVERTEXATTRIBBINDINGEXTPROC) (GLuint vaobj, GLuint attribindex, GLuint bindingindex); -typedef void (APIENTRYP PFNGLVERTEXARRAYVERTEXBINDINGDIVISOREXTPROC) (GLuint vaobj, GLuint bindingindex, GLuint divisor); -typedef void (APIENTRYP PFNGLVERTEXARRAYVERTEXATTRIBLOFFSETEXTPROC) (GLuint vaobj, GLuint buffer, GLuint index, GLint size, GLenum type, GLsizei stride, GLintptr offset); -typedef void (APIENTRYP PFNGLTEXTUREPAGECOMMITMENTEXTPROC) (GLuint texture, GLint level, GLint xoffset, GLint yoffset, GLint zoffset, GLsizei width, GLsizei height, GLsizei depth, GLboolean resident); -typedef void (APIENTRYP PFNGLVERTEXARRAYVERTEXATTRIBDIVISOREXTPROC) (GLuint vaobj, GLuint index, GLuint divisor); -#ifdef GL_GLEXT_PROTOTYPES -GLAPI void APIENTRY glMatrixLoadfEXT (GLenum mode, const GLfloat *m); -GLAPI void APIENTRY glMatrixLoaddEXT (GLenum mode, const GLdouble *m); -GLAPI void APIENTRY glMatrixMultfEXT (GLenum mode, const GLfloat *m); -GLAPI void APIENTRY glMatrixMultdEXT (GLenum mode, const GLdouble *m); -GLAPI void APIENTRY glMatrixLoadIdentityEXT (GLenum mode); -GLAPI void APIENTRY glMatrixRotatefEXT (GLenum mode, GLfloat angle, GLfloat x, GLfloat y, GLfloat z); -GLAPI void APIENTRY glMatrixRotatedEXT (GLenum mode, GLdouble angle, GLdouble x, GLdouble y, GLdouble z); -GLAPI void APIENTRY glMatrixScalefEXT (GLenum mode, GLfloat x, GLfloat y, GLfloat z); -GLAPI void APIENTRY glMatrixScaledEXT (GLenum mode, GLdouble x, GLdouble y, GLdouble z); -GLAPI void APIENTRY glMatrixTranslatefEXT (GLenum mode, GLfloat x, GLfloat y, GLfloat z); -GLAPI void APIENTRY glMatrixTranslatedEXT (GLenum mode, GLdouble x, GLdouble y, GLdouble z); -GLAPI void APIENTRY glMatrixFrustumEXT (GLenum mode, GLdouble left, GLdouble right, GLdouble bottom, GLdouble top, GLdouble zNear, GLdouble zFar); -GLAPI void APIENTRY glMatrixOrthoEXT (GLenum mode, GLdouble left, GLdouble right, GLdouble bottom, GLdouble top, GLdouble zNear, GLdouble zFar); -GLAPI void APIENTRY glMatrixPopEXT (GLenum mode); -GLAPI void APIENTRY glMatrixPushEXT (GLenum mode); -GLAPI void APIENTRY glClientAttribDefaultEXT (GLbitfield mask); -GLAPI void APIENTRY glPushClientAttribDefaultEXT (GLbitfield mask); -GLAPI void APIENTRY glTextureParameterfEXT (GLuint texture, GLenum target, GLenum pname, GLfloat param); -GLAPI void APIENTRY glTextureParameterfvEXT (GLuint texture, GLenum target, GLenum pname, const GLfloat *params); -GLAPI void APIENTRY glTextureParameteriEXT (GLuint texture, GLenum target, GLenum pname, GLint param); -GLAPI void APIENTRY glTextureParameterivEXT (GLuint texture, GLenum target, GLenum pname, const GLint *params); -GLAPI void APIENTRY glTextureImage1DEXT (GLuint texture, GLenum target, GLint level, GLint internalformat, GLsizei width, GLint border, GLenum format, GLenum type, const void *pixels); -GLAPI void APIENTRY glTextureImage2DEXT (GLuint texture, GLenum target, GLint level, GLint internalformat, GLsizei width, GLsizei height, GLint border, GLenum format, GLenum type, const void *pixels); -GLAPI void APIENTRY glTextureSubImage1DEXT (GLuint texture, GLenum target, GLint level, GLint xoffset, GLsizei width, GLenum format, GLenum type, const void *pixels); -GLAPI void APIENTRY glTextureSubImage2DEXT (GLuint texture, GLenum target, GLint level, GLint xoffset, GLint yoffset, GLsizei width, GLsizei height, GLenum format, GLenum type, const void *pixels); -GLAPI void APIENTRY glCopyTextureImage1DEXT (GLuint texture, GLenum target, GLint level, GLenum internalformat, GLint x, GLint y, GLsizei width, GLint border); -GLAPI void APIENTRY glCopyTextureImage2DEXT (GLuint texture, GLenum target, GLint level, GLenum internalformat, GLint x, GLint y, GLsizei width, GLsizei height, GLint border); -GLAPI void APIENTRY glCopyTextureSubImage1DEXT (GLuint texture, GLenum target, GLint level, GLint xoffset, GLint x, GLint y, GLsizei width); -GLAPI void APIENTRY glCopyTextureSubImage2DEXT (GLuint texture, GLenum target, GLint level, GLint xoffset, GLint yoffset, GLint x, GLint y, GLsizei width, GLsizei height); -GLAPI void APIENTRY glGetTextureImageEXT (GLuint texture, GLenum target, GLint level, GLenum format, GLenum type, void *pixels); -GLAPI void APIENTRY glGetTextureParameterfvEXT (GLuint texture, GLenum target, GLenum pname, GLfloat *params); -GLAPI void APIENTRY glGetTextureParameterivEXT (GLuint texture, GLenum target, GLenum pname, GLint *params); -GLAPI void APIENTRY glGetTextureLevelParameterfvEXT (GLuint texture, GLenum target, GLint level, GLenum pname, GLfloat *params); -GLAPI void APIENTRY glGetTextureLevelParameterivEXT (GLuint texture, GLenum target, GLint level, GLenum pname, GLint *params); -GLAPI void APIENTRY glTextureImage3DEXT (GLuint texture, GLenum target, GLint level, GLint internalformat, GLsizei width, GLsizei height, GLsizei depth, GLint border, GLenum format, GLenum type, const void *pixels); -GLAPI void APIENTRY glTextureSubImage3DEXT (GLuint texture, GLenum target, GLint level, GLint xoffset, GLint yoffset, GLint zoffset, GLsizei width, GLsizei height, GLsizei depth, GLenum format, GLenum type, const void *pixels); -GLAPI void APIENTRY glCopyTextureSubImage3DEXT (GLuint texture, GLenum target, GLint level, GLint xoffset, GLint yoffset, GLint zoffset, GLint x, GLint y, GLsizei width, GLsizei height); -GLAPI void APIENTRY glBindMultiTextureEXT (GLenum texunit, GLenum target, GLuint texture); -GLAPI void APIENTRY glMultiTexCoordPointerEXT (GLenum texunit, GLint size, GLenum type, GLsizei stride, const void *pointer); -GLAPI void APIENTRY glMultiTexEnvfEXT (GLenum texunit, GLenum target, GLenum pname, GLfloat param); -GLAPI void APIENTRY glMultiTexEnvfvEXT (GLenum texunit, GLenum target, GLenum pname, const GLfloat *params); -GLAPI void APIENTRY glMultiTexEnviEXT (GLenum texunit, GLenum target, GLenum pname, GLint param); -GLAPI void APIENTRY glMultiTexEnvivEXT (GLenum texunit, GLenum target, GLenum pname, const GLint *params); -GLAPI void APIENTRY glMultiTexGendEXT (GLenum texunit, GLenum coord, GLenum pname, GLdouble param); -GLAPI void APIENTRY glMultiTexGendvEXT (GLenum texunit, GLenum coord, GLenum pname, const GLdouble *params); -GLAPI void APIENTRY glMultiTexGenfEXT (GLenum texunit, GLenum coord, GLenum pname, GLfloat param); -GLAPI void APIENTRY glMultiTexGenfvEXT (GLenum texunit, GLenum coord, GLenum pname, const GLfloat *params); -GLAPI void APIENTRY glMultiTexGeniEXT (GLenum texunit, GLenum coord, GLenum pname, GLint param); -GLAPI void APIENTRY glMultiTexGenivEXT (GLenum texunit, GLenum coord, GLenum pname, const GLint *params); -GLAPI void APIENTRY glGetMultiTexEnvfvEXT (GLenum texunit, GLenum target, GLenum pname, GLfloat *params); -GLAPI void APIENTRY glGetMultiTexEnvivEXT (GLenum texunit, GLenum target, GLenum pname, GLint *params); -GLAPI void APIENTRY glGetMultiTexGendvEXT (GLenum texunit, GLenum coord, GLenum pname, GLdouble *params); -GLAPI void APIENTRY glGetMultiTexGenfvEXT (GLenum texunit, GLenum coord, GLenum pname, GLfloat *params); -GLAPI void APIENTRY glGetMultiTexGenivEXT (GLenum texunit, GLenum coord, GLenum pname, GLint *params); -GLAPI void APIENTRY glMultiTexParameteriEXT (GLenum texunit, GLenum target, GLenum pname, GLint param); -GLAPI void APIENTRY glMultiTexParameterivEXT (GLenum texunit, GLenum target, GLenum pname, const GLint *params); -GLAPI void APIENTRY glMultiTexParameterfEXT (GLenum texunit, GLenum target, GLenum pname, GLfloat param); -GLAPI void APIENTRY glMultiTexParameterfvEXT (GLenum texunit, GLenum target, GLenum pname, const GLfloat *params); -GLAPI void APIENTRY glMultiTexImage1DEXT (GLenum texunit, GLenum target, GLint level, GLint internalformat, GLsizei width, GLint border, GLenum format, GLenum type, const void *pixels); -GLAPI void APIENTRY glMultiTexImage2DEXT (GLenum texunit, GLenum target, GLint level, GLint internalformat, GLsizei width, GLsizei height, GLint border, GLenum format, GLenum type, const void *pixels); -GLAPI void APIENTRY glMultiTexSubImage1DEXT (GLenum texunit, GLenum target, GLint level, GLint xoffset, GLsizei width, GLenum format, GLenum type, const void *pixels); -GLAPI void APIENTRY glMultiTexSubImage2DEXT (GLenum texunit, GLenum target, GLint level, GLint xoffset, GLint yoffset, GLsizei width, GLsizei height, GLenum format, GLenum type, const void *pixels); -GLAPI void APIENTRY glCopyMultiTexImage1DEXT (GLenum texunit, GLenum target, GLint level, GLenum internalformat, GLint x, GLint y, GLsizei width, GLint border); -GLAPI void APIENTRY glCopyMultiTexImage2DEXT (GLenum texunit, GLenum target, GLint level, GLenum internalformat, GLint x, GLint y, GLsizei width, GLsizei height, GLint border); -GLAPI void APIENTRY glCopyMultiTexSubImage1DEXT (GLenum texunit, GLenum target, GLint level, GLint xoffset, GLint x, GLint y, GLsizei width); -GLAPI void APIENTRY glCopyMultiTexSubImage2DEXT (GLenum texunit, GLenum target, GLint level, GLint xoffset, GLint yoffset, GLint x, GLint y, GLsizei width, GLsizei height); -GLAPI void APIENTRY glGetMultiTexImageEXT (GLenum texunit, GLenum target, GLint level, GLenum format, GLenum type, void *pixels); -GLAPI void APIENTRY glGetMultiTexParameterfvEXT (GLenum texunit, GLenum target, GLenum pname, GLfloat *params); -GLAPI void APIENTRY glGetMultiTexParameterivEXT (GLenum texunit, GLenum target, GLenum pname, GLint *params); -GLAPI void APIENTRY glGetMultiTexLevelParameterfvEXT (GLenum texunit, GLenum target, GLint level, GLenum pname, GLfloat *params); -GLAPI void APIENTRY glGetMultiTexLevelParameterivEXT (GLenum texunit, GLenum target, GLint level, GLenum pname, GLint *params); -GLAPI void APIENTRY glMultiTexImage3DEXT (GLenum texunit, GLenum target, GLint level, GLint internalformat, GLsizei width, GLsizei height, GLsizei depth, GLint border, GLenum format, GLenum type, const void *pixels); -GLAPI void APIENTRY glMultiTexSubImage3DEXT (GLenum texunit, GLenum target, GLint level, GLint xoffset, GLint yoffset, GLint zoffset, GLsizei width, GLsizei height, GLsizei depth, GLenum format, GLenum type, const void *pixels); -GLAPI void APIENTRY glCopyMultiTexSubImage3DEXT (GLenum texunit, GLenum target, GLint level, GLint xoffset, GLint yoffset, GLint zoffset, GLint x, GLint y, GLsizei width, GLsizei height); -GLAPI void APIENTRY glEnableClientStateIndexedEXT (GLenum array, GLuint index); -GLAPI void APIENTRY glDisableClientStateIndexedEXT (GLenum array, GLuint index); -GLAPI void APIENTRY glGetFloatIndexedvEXT (GLenum target, GLuint index, GLfloat *data); -GLAPI void APIENTRY glGetDoubleIndexedvEXT (GLenum target, GLuint index, GLdouble *data); -GLAPI void APIENTRY glGetPointerIndexedvEXT (GLenum target, GLuint index, void **data); -GLAPI void APIENTRY glEnableIndexedEXT (GLenum target, GLuint index); -GLAPI void APIENTRY glDisableIndexedEXT (GLenum target, GLuint index); -GLAPI GLboolean APIENTRY glIsEnabledIndexedEXT (GLenum target, GLuint index); -GLAPI void APIENTRY glGetIntegerIndexedvEXT (GLenum target, GLuint index, GLint *data); -GLAPI void APIENTRY glGetBooleanIndexedvEXT (GLenum target, GLuint index, GLboolean *data); -GLAPI void APIENTRY glCompressedTextureImage3DEXT (GLuint texture, GLenum target, GLint level, GLenum internalformat, GLsizei width, GLsizei height, GLsizei depth, GLint border, GLsizei imageSize, const void *bits); -GLAPI void APIENTRY glCompressedTextureImage2DEXT (GLuint texture, GLenum target, GLint level, GLenum internalformat, GLsizei width, GLsizei height, GLint border, GLsizei imageSize, const void *bits); -GLAPI void APIENTRY glCompressedTextureImage1DEXT (GLuint texture, GLenum target, GLint level, GLenum internalformat, GLsizei width, GLint border, GLsizei imageSize, const void *bits); -GLAPI void APIENTRY glCompressedTextureSubImage3DEXT (GLuint texture, GLenum target, GLint level, GLint xoffset, GLint yoffset, GLint zoffset, GLsizei width, GLsizei height, GLsizei depth, GLenum format, GLsizei imageSize, const void *bits); -GLAPI void APIENTRY glCompressedTextureSubImage2DEXT (GLuint texture, GLenum target, GLint level, GLint xoffset, GLint yoffset, GLsizei width, GLsizei height, GLenum format, GLsizei imageSize, const void *bits); -GLAPI void APIENTRY glCompressedTextureSubImage1DEXT (GLuint texture, GLenum target, GLint level, GLint xoffset, GLsizei width, GLenum format, GLsizei imageSize, const void *bits); -GLAPI void APIENTRY glGetCompressedTextureImageEXT (GLuint texture, GLenum target, GLint lod, void *img); -GLAPI void APIENTRY glCompressedMultiTexImage3DEXT (GLenum texunit, GLenum target, GLint level, GLenum internalformat, GLsizei width, GLsizei height, GLsizei depth, GLint border, GLsizei imageSize, const void *bits); -GLAPI void APIENTRY glCompressedMultiTexImage2DEXT (GLenum texunit, GLenum target, GLint level, GLenum internalformat, GLsizei width, GLsizei height, GLint border, GLsizei imageSize, const void *bits); -GLAPI void APIENTRY glCompressedMultiTexImage1DEXT (GLenum texunit, GLenum target, GLint level, GLenum internalformat, GLsizei width, GLint border, GLsizei imageSize, const void *bits); -GLAPI void APIENTRY glCompressedMultiTexSubImage3DEXT (GLenum texunit, GLenum target, GLint level, GLint xoffset, GLint yoffset, GLint zoffset, GLsizei width, GLsizei height, GLsizei depth, GLenum format, GLsizei imageSize, const void *bits); -GLAPI void APIENTRY glCompressedMultiTexSubImage2DEXT (GLenum texunit, GLenum target, GLint level, GLint xoffset, GLint yoffset, GLsizei width, GLsizei height, GLenum format, GLsizei imageSize, const void *bits); -GLAPI void APIENTRY glCompressedMultiTexSubImage1DEXT (GLenum texunit, GLenum target, GLint level, GLint xoffset, GLsizei width, GLenum format, GLsizei imageSize, const void *bits); -GLAPI void APIENTRY glGetCompressedMultiTexImageEXT (GLenum texunit, GLenum target, GLint lod, void *img); -GLAPI void APIENTRY glMatrixLoadTransposefEXT (GLenum mode, const GLfloat *m); -GLAPI void APIENTRY glMatrixLoadTransposedEXT (GLenum mode, const GLdouble *m); -GLAPI void APIENTRY glMatrixMultTransposefEXT (GLenum mode, const GLfloat *m); -GLAPI void APIENTRY glMatrixMultTransposedEXT (GLenum mode, const GLdouble *m); -GLAPI void APIENTRY glNamedBufferDataEXT (GLuint buffer, GLsizeiptr size, const void *data, GLenum usage); -GLAPI void APIENTRY glNamedBufferSubDataEXT (GLuint buffer, GLintptr offset, GLsizeiptr size, const void *data); -GLAPI void *APIENTRY glMapNamedBufferEXT (GLuint buffer, GLenum access); -GLAPI GLboolean APIENTRY glUnmapNamedBufferEXT (GLuint buffer); -GLAPI void APIENTRY glGetNamedBufferParameterivEXT (GLuint buffer, GLenum pname, GLint *params); -GLAPI void APIENTRY glGetNamedBufferPointervEXT (GLuint buffer, GLenum pname, void **params); -GLAPI void APIENTRY glGetNamedBufferSubDataEXT (GLuint buffer, GLintptr offset, GLsizeiptr size, void *data); -GLAPI void APIENTRY glProgramUniform1fEXT (GLuint program, GLint location, GLfloat v0); -GLAPI void APIENTRY glProgramUniform2fEXT (GLuint program, GLint location, GLfloat v0, GLfloat v1); -GLAPI void APIENTRY glProgramUniform3fEXT (GLuint program, GLint location, GLfloat v0, GLfloat v1, GLfloat v2); -GLAPI void APIENTRY glProgramUniform4fEXT (GLuint program, GLint location, GLfloat v0, GLfloat v1, GLfloat v2, GLfloat v3); -GLAPI void APIENTRY glProgramUniform1iEXT (GLuint program, GLint location, GLint v0); -GLAPI void APIENTRY glProgramUniform2iEXT (GLuint program, GLint location, GLint v0, GLint v1); -GLAPI void APIENTRY glProgramUniform3iEXT (GLuint program, GLint location, GLint v0, GLint v1, GLint v2); -GLAPI void APIENTRY glProgramUniform4iEXT (GLuint program, GLint location, GLint v0, GLint v1, GLint v2, GLint v3); -GLAPI void APIENTRY glProgramUniform1fvEXT (GLuint program, GLint location, GLsizei count, const GLfloat *value); -GLAPI void APIENTRY glProgramUniform2fvEXT (GLuint program, GLint location, GLsizei count, const GLfloat *value); -GLAPI void APIENTRY glProgramUniform3fvEXT (GLuint program, GLint location, GLsizei count, const GLfloat *value); -GLAPI void APIENTRY glProgramUniform4fvEXT (GLuint program, GLint location, GLsizei count, const GLfloat *value); -GLAPI void APIENTRY glProgramUniform1ivEXT (GLuint program, GLint location, GLsizei count, const GLint *value); -GLAPI void APIENTRY glProgramUniform2ivEXT (GLuint program, GLint location, GLsizei count, const GLint *value); -GLAPI void APIENTRY glProgramUniform3ivEXT (GLuint program, GLint location, GLsizei count, const GLint *value); -GLAPI void APIENTRY glProgramUniform4ivEXT (GLuint program, GLint location, GLsizei count, const GLint *value); -GLAPI void APIENTRY glProgramUniformMatrix2fvEXT (GLuint program, GLint location, GLsizei count, GLboolean transpose, const GLfloat *value); -GLAPI void APIENTRY glProgramUniformMatrix3fvEXT (GLuint program, GLint location, GLsizei count, GLboolean transpose, const GLfloat *value); -GLAPI void APIENTRY glProgramUniformMatrix4fvEXT (GLuint program, GLint location, GLsizei count, GLboolean transpose, const GLfloat *value); -GLAPI void APIENTRY glProgramUniformMatrix2x3fvEXT (GLuint program, GLint location, GLsizei count, GLboolean transpose, const GLfloat *value); -GLAPI void APIENTRY glProgramUniformMatrix3x2fvEXT (GLuint program, GLint location, GLsizei count, GLboolean transpose, const GLfloat *value); -GLAPI void APIENTRY glProgramUniformMatrix2x4fvEXT (GLuint program, GLint location, GLsizei count, GLboolean transpose, const GLfloat *value); -GLAPI void APIENTRY glProgramUniformMatrix4x2fvEXT (GLuint program, GLint location, GLsizei count, GLboolean transpose, const GLfloat *value); -GLAPI void APIENTRY glProgramUniformMatrix3x4fvEXT (GLuint program, GLint location, GLsizei count, GLboolean transpose, const GLfloat *value); -GLAPI void APIENTRY glProgramUniformMatrix4x3fvEXT (GLuint program, GLint location, GLsizei count, GLboolean transpose, const GLfloat *value); -GLAPI void APIENTRY glTextureBufferEXT (GLuint texture, GLenum target, GLenum internalformat, GLuint buffer); -GLAPI void APIENTRY glMultiTexBufferEXT (GLenum texunit, GLenum target, GLenum internalformat, GLuint buffer); -GLAPI void APIENTRY glTextureParameterIivEXT (GLuint texture, GLenum target, GLenum pname, const GLint *params); -GLAPI void APIENTRY glTextureParameterIuivEXT (GLuint texture, GLenum target, GLenum pname, const GLuint *params); -GLAPI void APIENTRY glGetTextureParameterIivEXT (GLuint texture, GLenum target, GLenum pname, GLint *params); -GLAPI void APIENTRY glGetTextureParameterIuivEXT (GLuint texture, GLenum target, GLenum pname, GLuint *params); -GLAPI void APIENTRY glMultiTexParameterIivEXT (GLenum texunit, GLenum target, GLenum pname, const GLint *params); -GLAPI void APIENTRY glMultiTexParameterIuivEXT (GLenum texunit, GLenum target, GLenum pname, const GLuint *params); -GLAPI void APIENTRY glGetMultiTexParameterIivEXT (GLenum texunit, GLenum target, GLenum pname, GLint *params); -GLAPI void APIENTRY glGetMultiTexParameterIuivEXT (GLenum texunit, GLenum target, GLenum pname, GLuint *params); -GLAPI void APIENTRY glProgramUniform1uiEXT (GLuint program, GLint location, GLuint v0); -GLAPI void APIENTRY glProgramUniform2uiEXT (GLuint program, GLint location, GLuint v0, GLuint v1); -GLAPI void APIENTRY glProgramUniform3uiEXT (GLuint program, GLint location, GLuint v0, GLuint v1, GLuint v2); -GLAPI void APIENTRY glProgramUniform4uiEXT (GLuint program, GLint location, GLuint v0, GLuint v1, GLuint v2, GLuint v3); -GLAPI void APIENTRY glProgramUniform1uivEXT (GLuint program, GLint location, GLsizei count, const GLuint *value); -GLAPI void APIENTRY glProgramUniform2uivEXT (GLuint program, GLint location, GLsizei count, const GLuint *value); -GLAPI void APIENTRY glProgramUniform3uivEXT (GLuint program, GLint location, GLsizei count, const GLuint *value); -GLAPI void APIENTRY glProgramUniform4uivEXT (GLuint program, GLint location, GLsizei count, const GLuint *value); -GLAPI void APIENTRY glNamedProgramLocalParameters4fvEXT (GLuint program, GLenum target, GLuint index, GLsizei count, const GLfloat *params); -GLAPI void APIENTRY glNamedProgramLocalParameterI4iEXT (GLuint program, GLenum target, GLuint index, GLint x, GLint y, GLint z, GLint w); -GLAPI void APIENTRY glNamedProgramLocalParameterI4ivEXT (GLuint program, GLenum target, GLuint index, const GLint *params); -GLAPI void APIENTRY glNamedProgramLocalParametersI4ivEXT (GLuint program, GLenum target, GLuint index, GLsizei count, const GLint *params); -GLAPI void APIENTRY glNamedProgramLocalParameterI4uiEXT (GLuint program, GLenum target, GLuint index, GLuint x, GLuint y, GLuint z, GLuint w); -GLAPI void APIENTRY glNamedProgramLocalParameterI4uivEXT (GLuint program, GLenum target, GLuint index, const GLuint *params); -GLAPI void APIENTRY glNamedProgramLocalParametersI4uivEXT (GLuint program, GLenum target, GLuint index, GLsizei count, const GLuint *params); -GLAPI void APIENTRY glGetNamedProgramLocalParameterIivEXT (GLuint program, GLenum target, GLuint index, GLint *params); -GLAPI void APIENTRY glGetNamedProgramLocalParameterIuivEXT (GLuint program, GLenum target, GLuint index, GLuint *params); -GLAPI void APIENTRY glEnableClientStateiEXT (GLenum array, GLuint index); -GLAPI void APIENTRY glDisableClientStateiEXT (GLenum array, GLuint index); -GLAPI void APIENTRY glGetFloati_vEXT (GLenum pname, GLuint index, GLfloat *params); -GLAPI void APIENTRY glGetDoublei_vEXT (GLenum pname, GLuint index, GLdouble *params); -GLAPI void APIENTRY glGetPointeri_vEXT (GLenum pname, GLuint index, void **params); -GLAPI void APIENTRY glNamedProgramStringEXT (GLuint program, GLenum target, GLenum format, GLsizei len, const void *string); -GLAPI void APIENTRY glNamedProgramLocalParameter4dEXT (GLuint program, GLenum target, GLuint index, GLdouble x, GLdouble y, GLdouble z, GLdouble w); -GLAPI void APIENTRY glNamedProgramLocalParameter4dvEXT (GLuint program, GLenum target, GLuint index, const GLdouble *params); -GLAPI void APIENTRY glNamedProgramLocalParameter4fEXT (GLuint program, GLenum target, GLuint index, GLfloat x, GLfloat y, GLfloat z, GLfloat w); -GLAPI void APIENTRY glNamedProgramLocalParameter4fvEXT (GLuint program, GLenum target, GLuint index, const GLfloat *params); -GLAPI void APIENTRY glGetNamedProgramLocalParameterdvEXT (GLuint program, GLenum target, GLuint index, GLdouble *params); -GLAPI void APIENTRY glGetNamedProgramLocalParameterfvEXT (GLuint program, GLenum target, GLuint index, GLfloat *params); -GLAPI void APIENTRY glGetNamedProgramivEXT (GLuint program, GLenum target, GLenum pname, GLint *params); -GLAPI void APIENTRY glGetNamedProgramStringEXT (GLuint program, GLenum target, GLenum pname, void *string); -GLAPI void APIENTRY glNamedRenderbufferStorageEXT (GLuint renderbuffer, GLenum internalformat, GLsizei width, GLsizei height); -GLAPI void APIENTRY glGetNamedRenderbufferParameterivEXT (GLuint renderbuffer, GLenum pname, GLint *params); -GLAPI void APIENTRY glNamedRenderbufferStorageMultisampleEXT (GLuint renderbuffer, GLsizei samples, GLenum internalformat, GLsizei width, GLsizei height); -GLAPI void APIENTRY glNamedRenderbufferStorageMultisampleCoverageEXT (GLuint renderbuffer, GLsizei coverageSamples, GLsizei colorSamples, GLenum internalformat, GLsizei width, GLsizei height); -GLAPI GLenum APIENTRY glCheckNamedFramebufferStatusEXT (GLuint framebuffer, GLenum target); -GLAPI void APIENTRY glNamedFramebufferTexture1DEXT (GLuint framebuffer, GLenum attachment, GLenum textarget, GLuint texture, GLint level); -GLAPI void APIENTRY glNamedFramebufferTexture2DEXT (GLuint framebuffer, GLenum attachment, GLenum textarget, GLuint texture, GLint level); -GLAPI void APIENTRY glNamedFramebufferTexture3DEXT (GLuint framebuffer, GLenum attachment, GLenum textarget, GLuint texture, GLint level, GLint zoffset); -GLAPI void APIENTRY glNamedFramebufferRenderbufferEXT (GLuint framebuffer, GLenum attachment, GLenum renderbuffertarget, GLuint renderbuffer); -GLAPI void APIENTRY glGetNamedFramebufferAttachmentParameterivEXT (GLuint framebuffer, GLenum attachment, GLenum pname, GLint *params); -GLAPI void APIENTRY glGenerateTextureMipmapEXT (GLuint texture, GLenum target); -GLAPI void APIENTRY glGenerateMultiTexMipmapEXT (GLenum texunit, GLenum target); -GLAPI void APIENTRY glFramebufferDrawBufferEXT (GLuint framebuffer, GLenum mode); -GLAPI void APIENTRY glFramebufferDrawBuffersEXT (GLuint framebuffer, GLsizei n, const GLenum *bufs); -GLAPI void APIENTRY glFramebufferReadBufferEXT (GLuint framebuffer, GLenum mode); -GLAPI void APIENTRY glGetFramebufferParameterivEXT (GLuint framebuffer, GLenum pname, GLint *params); -GLAPI void APIENTRY glNamedCopyBufferSubDataEXT (GLuint readBuffer, GLuint writeBuffer, GLintptr readOffset, GLintptr writeOffset, GLsizeiptr size); -GLAPI void APIENTRY glNamedFramebufferTextureEXT (GLuint framebuffer, GLenum attachment, GLuint texture, GLint level); -GLAPI void APIENTRY glNamedFramebufferTextureLayerEXT (GLuint framebuffer, GLenum attachment, GLuint texture, GLint level, GLint layer); -GLAPI void APIENTRY glNamedFramebufferTextureFaceEXT (GLuint framebuffer, GLenum attachment, GLuint texture, GLint level, GLenum face); -GLAPI void APIENTRY glTextureRenderbufferEXT (GLuint texture, GLenum target, GLuint renderbuffer); -GLAPI void APIENTRY glMultiTexRenderbufferEXT (GLenum texunit, GLenum target, GLuint renderbuffer); -GLAPI void APIENTRY glVertexArrayVertexOffsetEXT (GLuint vaobj, GLuint buffer, GLint size, GLenum type, GLsizei stride, GLintptr offset); -GLAPI void APIENTRY glVertexArrayColorOffsetEXT (GLuint vaobj, GLuint buffer, GLint size, GLenum type, GLsizei stride, GLintptr offset); -GLAPI void APIENTRY glVertexArrayEdgeFlagOffsetEXT (GLuint vaobj, GLuint buffer, GLsizei stride, GLintptr offset); -GLAPI void APIENTRY glVertexArrayIndexOffsetEXT (GLuint vaobj, GLuint buffer, GLenum type, GLsizei stride, GLintptr offset); -GLAPI void APIENTRY glVertexArrayNormalOffsetEXT (GLuint vaobj, GLuint buffer, GLenum type, GLsizei stride, GLintptr offset); -GLAPI void APIENTRY glVertexArrayTexCoordOffsetEXT (GLuint vaobj, GLuint buffer, GLint size, GLenum type, GLsizei stride, GLintptr offset); -GLAPI void APIENTRY glVertexArrayMultiTexCoordOffsetEXT (GLuint vaobj, GLuint buffer, GLenum texunit, GLint size, GLenum type, GLsizei stride, GLintptr offset); -GLAPI void APIENTRY glVertexArrayFogCoordOffsetEXT (GLuint vaobj, GLuint buffer, GLenum type, GLsizei stride, GLintptr offset); -GLAPI void APIENTRY glVertexArraySecondaryColorOffsetEXT (GLuint vaobj, GLuint buffer, GLint size, GLenum type, GLsizei stride, GLintptr offset); -GLAPI void APIENTRY glVertexArrayVertexAttribOffsetEXT (GLuint vaobj, GLuint buffer, GLuint index, GLint size, GLenum type, GLboolean normalized, GLsizei stride, GLintptr offset); -GLAPI void APIENTRY glVertexArrayVertexAttribIOffsetEXT (GLuint vaobj, GLuint buffer, GLuint index, GLint size, GLenum type, GLsizei stride, GLintptr offset); -GLAPI void APIENTRY glEnableVertexArrayEXT (GLuint vaobj, GLenum array); -GLAPI void APIENTRY glDisableVertexArrayEXT (GLuint vaobj, GLenum array); -GLAPI void APIENTRY glEnableVertexArrayAttribEXT (GLuint vaobj, GLuint index); -GLAPI void APIENTRY glDisableVertexArrayAttribEXT (GLuint vaobj, GLuint index); -GLAPI void APIENTRY glGetVertexArrayIntegervEXT (GLuint vaobj, GLenum pname, GLint *param); -GLAPI void APIENTRY glGetVertexArrayPointervEXT (GLuint vaobj, GLenum pname, void **param); -GLAPI void APIENTRY glGetVertexArrayIntegeri_vEXT (GLuint vaobj, GLuint index, GLenum pname, GLint *param); -GLAPI void APIENTRY glGetVertexArrayPointeri_vEXT (GLuint vaobj, GLuint index, GLenum pname, void **param); -GLAPI void *APIENTRY glMapNamedBufferRangeEXT (GLuint buffer, GLintptr offset, GLsizeiptr length, GLbitfield access); -GLAPI void APIENTRY glFlushMappedNamedBufferRangeEXT (GLuint buffer, GLintptr offset, GLsizeiptr length); -GLAPI void APIENTRY glNamedBufferStorageEXT (GLuint buffer, GLsizeiptr size, const void *data, GLbitfield flags); -GLAPI void APIENTRY glClearNamedBufferDataEXT (GLuint buffer, GLenum internalformat, GLenum format, GLenum type, const void *data); -GLAPI void APIENTRY glClearNamedBufferSubDataEXT (GLuint buffer, GLenum internalformat, GLsizeiptr offset, GLsizeiptr size, GLenum format, GLenum type, const void *data); -GLAPI void APIENTRY glNamedFramebufferParameteriEXT (GLuint framebuffer, GLenum pname, GLint param); -GLAPI void APIENTRY glGetNamedFramebufferParameterivEXT (GLuint framebuffer, GLenum pname, GLint *params); -GLAPI void APIENTRY glProgramUniform1dEXT (GLuint program, GLint location, GLdouble x); -GLAPI void APIENTRY glProgramUniform2dEXT (GLuint program, GLint location, GLdouble x, GLdouble y); -GLAPI void APIENTRY glProgramUniform3dEXT (GLuint program, GLint location, GLdouble x, GLdouble y, GLdouble z); -GLAPI void APIENTRY glProgramUniform4dEXT (GLuint program, GLint location, GLdouble x, GLdouble y, GLdouble z, GLdouble w); -GLAPI void APIENTRY glProgramUniform1dvEXT (GLuint program, GLint location, GLsizei count, const GLdouble *value); -GLAPI void APIENTRY glProgramUniform2dvEXT (GLuint program, GLint location, GLsizei count, const GLdouble *value); -GLAPI void APIENTRY glProgramUniform3dvEXT (GLuint program, GLint location, GLsizei count, const GLdouble *value); -GLAPI void APIENTRY glProgramUniform4dvEXT (GLuint program, GLint location, GLsizei count, const GLdouble *value); -GLAPI void APIENTRY glProgramUniformMatrix2dvEXT (GLuint program, GLint location, GLsizei count, GLboolean transpose, const GLdouble *value); -GLAPI void APIENTRY glProgramUniformMatrix3dvEXT (GLuint program, GLint location, GLsizei count, GLboolean transpose, const GLdouble *value); -GLAPI void APIENTRY glProgramUniformMatrix4dvEXT (GLuint program, GLint location, GLsizei count, GLboolean transpose, const GLdouble *value); -GLAPI void APIENTRY glProgramUniformMatrix2x3dvEXT (GLuint program, GLint location, GLsizei count, GLboolean transpose, const GLdouble *value); -GLAPI void APIENTRY glProgramUniformMatrix2x4dvEXT (GLuint program, GLint location, GLsizei count, GLboolean transpose, const GLdouble *value); -GLAPI void APIENTRY glProgramUniformMatrix3x2dvEXT (GLuint program, GLint location, GLsizei count, GLboolean transpose, const GLdouble *value); -GLAPI void APIENTRY glProgramUniformMatrix3x4dvEXT (GLuint program, GLint location, GLsizei count, GLboolean transpose, const GLdouble *value); -GLAPI void APIENTRY glProgramUniformMatrix4x2dvEXT (GLuint program, GLint location, GLsizei count, GLboolean transpose, const GLdouble *value); -GLAPI void APIENTRY glProgramUniformMatrix4x3dvEXT (GLuint program, GLint location, GLsizei count, GLboolean transpose, const GLdouble *value); -GLAPI void APIENTRY glTextureBufferRangeEXT (GLuint texture, GLenum target, GLenum internalformat, GLuint buffer, GLintptr offset, GLsizeiptr size); -GLAPI void APIENTRY glTextureStorage1DEXT (GLuint texture, GLenum target, GLsizei levels, GLenum internalformat, GLsizei width); -GLAPI void APIENTRY glTextureStorage2DEXT (GLuint texture, GLenum target, GLsizei levels, GLenum internalformat, GLsizei width, GLsizei height); -GLAPI void APIENTRY glTextureStorage3DEXT (GLuint texture, GLenum target, GLsizei levels, GLenum internalformat, GLsizei width, GLsizei height, GLsizei depth); -GLAPI void APIENTRY glTextureStorage2DMultisampleEXT (GLuint texture, GLenum target, GLsizei samples, GLenum internalformat, GLsizei width, GLsizei height, GLboolean fixedsamplelocations); -GLAPI void APIENTRY glTextureStorage3DMultisampleEXT (GLuint texture, GLenum target, GLsizei samples, GLenum internalformat, GLsizei width, GLsizei height, GLsizei depth, GLboolean fixedsamplelocations); -GLAPI void APIENTRY glVertexArrayBindVertexBufferEXT (GLuint vaobj, GLuint bindingindex, GLuint buffer, GLintptr offset, GLsizei stride); -GLAPI void APIENTRY glVertexArrayVertexAttribFormatEXT (GLuint vaobj, GLuint attribindex, GLint size, GLenum type, GLboolean normalized, GLuint relativeoffset); -GLAPI void APIENTRY glVertexArrayVertexAttribIFormatEXT (GLuint vaobj, GLuint attribindex, GLint size, GLenum type, GLuint relativeoffset); -GLAPI void APIENTRY glVertexArrayVertexAttribLFormatEXT (GLuint vaobj, GLuint attribindex, GLint size, GLenum type, GLuint relativeoffset); -GLAPI void APIENTRY glVertexArrayVertexAttribBindingEXT (GLuint vaobj, GLuint attribindex, GLuint bindingindex); -GLAPI void APIENTRY glVertexArrayVertexBindingDivisorEXT (GLuint vaobj, GLuint bindingindex, GLuint divisor); -GLAPI void APIENTRY glVertexArrayVertexAttribLOffsetEXT (GLuint vaobj, GLuint buffer, GLuint index, GLint size, GLenum type, GLsizei stride, GLintptr offset); -GLAPI void APIENTRY glTexturePageCommitmentEXT (GLuint texture, GLint level, GLint xoffset, GLint yoffset, GLint zoffset, GLsizei width, GLsizei height, GLsizei depth, GLboolean resident); -GLAPI void APIENTRY glVertexArrayVertexAttribDivisorEXT (GLuint vaobj, GLuint index, GLuint divisor); -#endif -#endif /* GL_EXT_direct_state_access */ - -#ifndef GL_EXT_draw_buffers2 -#define GL_EXT_draw_buffers2 1 -typedef void (APIENTRYP PFNGLCOLORMASKINDEXEDEXTPROC) (GLuint index, GLboolean r, GLboolean g, GLboolean b, GLboolean a); -#ifdef GL_GLEXT_PROTOTYPES -GLAPI void APIENTRY glColorMaskIndexedEXT (GLuint index, GLboolean r, GLboolean g, GLboolean b, GLboolean a); -#endif -#endif /* GL_EXT_draw_buffers2 */ - -#ifndef GL_EXT_draw_instanced -#define GL_EXT_draw_instanced 1 -typedef void (APIENTRYP PFNGLDRAWARRAYSINSTANCEDEXTPROC) (GLenum mode, GLint start, GLsizei count, GLsizei primcount); -typedef void (APIENTRYP PFNGLDRAWELEMENTSINSTANCEDEXTPROC) (GLenum mode, GLsizei count, GLenum type, const void *indices, GLsizei primcount); -#ifdef GL_GLEXT_PROTOTYPES -GLAPI void APIENTRY glDrawArraysInstancedEXT (GLenum mode, GLint start, GLsizei count, GLsizei primcount); -GLAPI void APIENTRY glDrawElementsInstancedEXT (GLenum mode, GLsizei count, GLenum type, const void *indices, GLsizei primcount); -#endif -#endif /* GL_EXT_draw_instanced */ - -#ifndef GL_EXT_draw_range_elements -#define GL_EXT_draw_range_elements 1 -#define GL_MAX_ELEMENTS_VERTICES_EXT 0x80E8 -#define GL_MAX_ELEMENTS_INDICES_EXT 0x80E9 -typedef void (APIENTRYP PFNGLDRAWRANGEELEMENTSEXTPROC) (GLenum mode, GLuint start, GLuint end, GLsizei count, GLenum type, const void *indices); -#ifdef GL_GLEXT_PROTOTYPES -GLAPI void APIENTRY glDrawRangeElementsEXT (GLenum mode, GLuint start, GLuint end, GLsizei count, GLenum type, const void *indices); -#endif -#endif /* GL_EXT_draw_range_elements */ - -#ifndef GL_EXT_fog_coord -#define GL_EXT_fog_coord 1 -#define GL_FOG_COORDINATE_SOURCE_EXT 0x8450 -#define GL_FOG_COORDINATE_EXT 0x8451 -#define GL_FRAGMENT_DEPTH_EXT 0x8452 -#define GL_CURRENT_FOG_COORDINATE_EXT 0x8453 -#define GL_FOG_COORDINATE_ARRAY_TYPE_EXT 0x8454 -#define GL_FOG_COORDINATE_ARRAY_STRIDE_EXT 0x8455 -#define GL_FOG_COORDINATE_ARRAY_POINTER_EXT 0x8456 -#define GL_FOG_COORDINATE_ARRAY_EXT 0x8457 -typedef void (APIENTRYP PFNGLFOGCOORDFEXTPROC) (GLfloat coord); -typedef void (APIENTRYP PFNGLFOGCOORDFVEXTPROC) (const GLfloat *coord); -typedef void (APIENTRYP PFNGLFOGCOORDDEXTPROC) (GLdouble coord); -typedef void (APIENTRYP PFNGLFOGCOORDDVEXTPROC) (const GLdouble *coord); -typedef void (APIENTRYP PFNGLFOGCOORDPOINTEREXTPROC) (GLenum type, GLsizei stride, const void *pointer); -#ifdef GL_GLEXT_PROTOTYPES -GLAPI void APIENTRY glFogCoordfEXT (GLfloat coord); -GLAPI void APIENTRY glFogCoordfvEXT (const GLfloat *coord); -GLAPI void APIENTRY glFogCoorddEXT (GLdouble coord); -GLAPI void APIENTRY glFogCoorddvEXT (const GLdouble *coord); -GLAPI void APIENTRY glFogCoordPointerEXT (GLenum type, GLsizei stride, const void *pointer); -#endif -#endif /* GL_EXT_fog_coord */ - -#ifndef GL_EXT_framebuffer_blit -#define GL_EXT_framebuffer_blit 1 -#define GL_READ_FRAMEBUFFER_EXT 0x8CA8 -#define GL_DRAW_FRAMEBUFFER_EXT 0x8CA9 -#define GL_DRAW_FRAMEBUFFER_BINDING_EXT 0x8CA6 -#define GL_READ_FRAMEBUFFER_BINDING_EXT 0x8CAA -typedef void (APIENTRYP PFNGLBLITFRAMEBUFFEREXTPROC) (GLint srcX0, GLint srcY0, GLint srcX1, GLint srcY1, GLint dstX0, GLint dstY0, GLint dstX1, GLint dstY1, GLbitfield mask, GLenum filter); -#ifdef GL_GLEXT_PROTOTYPES -GLAPI void APIENTRY glBlitFramebufferEXT (GLint srcX0, GLint srcY0, GLint srcX1, GLint srcY1, GLint dstX0, GLint dstY0, GLint dstX1, GLint dstY1, GLbitfield mask, GLenum filter); -#endif -#endif /* GL_EXT_framebuffer_blit */ - -#ifndef GL_EXT_framebuffer_multisample -#define GL_EXT_framebuffer_multisample 1 -#define GL_RENDERBUFFER_SAMPLES_EXT 0x8CAB -#define GL_FRAMEBUFFER_INCOMPLETE_MULTISAMPLE_EXT 0x8D56 -#define GL_MAX_SAMPLES_EXT 0x8D57 -typedef void (APIENTRYP PFNGLRENDERBUFFERSTORAGEMULTISAMPLEEXTPROC) (GLenum target, GLsizei samples, GLenum internalformat, GLsizei width, GLsizei height); -#ifdef GL_GLEXT_PROTOTYPES -GLAPI void APIENTRY glRenderbufferStorageMultisampleEXT (GLenum target, GLsizei samples, GLenum internalformat, GLsizei width, GLsizei height); -#endif -#endif /* GL_EXT_framebuffer_multisample */ - -#ifndef GL_EXT_framebuffer_multisample_blit_scaled -#define GL_EXT_framebuffer_multisample_blit_scaled 1 -#define GL_SCALED_RESOLVE_FASTEST_EXT 0x90BA -#define GL_SCALED_RESOLVE_NICEST_EXT 0x90BB -#endif /* GL_EXT_framebuffer_multisample_blit_scaled */ - -#ifndef GL_EXT_framebuffer_object -#define GL_EXT_framebuffer_object 1 -#define GL_INVALID_FRAMEBUFFER_OPERATION_EXT 0x0506 -#define GL_MAX_RENDERBUFFER_SIZE_EXT 0x84E8 -#define GL_FRAMEBUFFER_BINDING_EXT 0x8CA6 -#define GL_RENDERBUFFER_BINDING_EXT 0x8CA7 -#define GL_FRAMEBUFFER_ATTACHMENT_OBJECT_TYPE_EXT 0x8CD0 -#define GL_FRAMEBUFFER_ATTACHMENT_OBJECT_NAME_EXT 0x8CD1 -#define GL_FRAMEBUFFER_ATTACHMENT_TEXTURE_LEVEL_EXT 0x8CD2 -#define GL_FRAMEBUFFER_ATTACHMENT_TEXTURE_CUBE_MAP_FACE_EXT 0x8CD3 -#define GL_FRAMEBUFFER_ATTACHMENT_TEXTURE_3D_ZOFFSET_EXT 0x8CD4 -#define GL_FRAMEBUFFER_COMPLETE_EXT 0x8CD5 -#define GL_FRAMEBUFFER_INCOMPLETE_ATTACHMENT_EXT 0x8CD6 -#define GL_FRAMEBUFFER_INCOMPLETE_MISSING_ATTACHMENT_EXT 0x8CD7 -#define GL_FRAMEBUFFER_INCOMPLETE_DIMENSIONS_EXT 0x8CD9 -#define GL_FRAMEBUFFER_INCOMPLETE_FORMATS_EXT 0x8CDA -#define GL_FRAMEBUFFER_INCOMPLETE_DRAW_BUFFER_EXT 0x8CDB -#define GL_FRAMEBUFFER_INCOMPLETE_READ_BUFFER_EXT 0x8CDC -#define GL_FRAMEBUFFER_UNSUPPORTED_EXT 0x8CDD -#define GL_MAX_COLOR_ATTACHMENTS_EXT 0x8CDF -#define GL_COLOR_ATTACHMENT0_EXT 0x8CE0 -#define GL_COLOR_ATTACHMENT1_EXT 0x8CE1 -#define GL_COLOR_ATTACHMENT2_EXT 0x8CE2 -#define GL_COLOR_ATTACHMENT3_EXT 0x8CE3 -#define GL_COLOR_ATTACHMENT4_EXT 0x8CE4 -#define GL_COLOR_ATTACHMENT5_EXT 0x8CE5 -#define GL_COLOR_ATTACHMENT6_EXT 0x8CE6 -#define GL_COLOR_ATTACHMENT7_EXT 0x8CE7 -#define GL_COLOR_ATTACHMENT8_EXT 0x8CE8 -#define GL_COLOR_ATTACHMENT9_EXT 0x8CE9 -#define GL_COLOR_ATTACHMENT10_EXT 0x8CEA -#define GL_COLOR_ATTACHMENT11_EXT 0x8CEB -#define GL_COLOR_ATTACHMENT12_EXT 0x8CEC -#define GL_COLOR_ATTACHMENT13_EXT 0x8CED -#define GL_COLOR_ATTACHMENT14_EXT 0x8CEE -#define GL_COLOR_ATTACHMENT15_EXT 0x8CEF -#define GL_DEPTH_ATTACHMENT_EXT 0x8D00 -#define GL_STENCIL_ATTACHMENT_EXT 0x8D20 -#define GL_FRAMEBUFFER_EXT 0x8D40 -#define GL_RENDERBUFFER_EXT 0x8D41 -#define GL_RENDERBUFFER_WIDTH_EXT 0x8D42 -#define GL_RENDERBUFFER_HEIGHT_EXT 0x8D43 -#define GL_RENDERBUFFER_INTERNAL_FORMAT_EXT 0x8D44 -#define GL_STENCIL_INDEX1_EXT 0x8D46 -#define GL_STENCIL_INDEX4_EXT 0x8D47 -#define GL_STENCIL_INDEX8_EXT 0x8D48 -#define GL_STENCIL_INDEX16_EXT 0x8D49 -#define GL_RENDERBUFFER_RED_SIZE_EXT 0x8D50 -#define GL_RENDERBUFFER_GREEN_SIZE_EXT 0x8D51 -#define GL_RENDERBUFFER_BLUE_SIZE_EXT 0x8D52 -#define GL_RENDERBUFFER_ALPHA_SIZE_EXT 0x8D53 -#define GL_RENDERBUFFER_DEPTH_SIZE_EXT 0x8D54 -#define GL_RENDERBUFFER_STENCIL_SIZE_EXT 0x8D55 -typedef GLboolean (APIENTRYP PFNGLISRENDERBUFFEREXTPROC) (GLuint renderbuffer); -typedef void (APIENTRYP PFNGLBINDRENDERBUFFEREXTPROC) (GLenum target, GLuint renderbuffer); -typedef void (APIENTRYP PFNGLDELETERENDERBUFFERSEXTPROC) (GLsizei n, const GLuint *renderbuffers); -typedef void (APIENTRYP PFNGLGENRENDERBUFFERSEXTPROC) (GLsizei n, GLuint *renderbuffers); -typedef void (APIENTRYP PFNGLRENDERBUFFERSTORAGEEXTPROC) (GLenum target, GLenum internalformat, GLsizei width, GLsizei height); -typedef void (APIENTRYP PFNGLGETRENDERBUFFERPARAMETERIVEXTPROC) (GLenum target, GLenum pname, GLint *params); -typedef GLboolean (APIENTRYP PFNGLISFRAMEBUFFEREXTPROC) (GLuint framebuffer); -typedef void (APIENTRYP PFNGLBINDFRAMEBUFFEREXTPROC) (GLenum target, GLuint framebuffer); -typedef void (APIENTRYP PFNGLDELETEFRAMEBUFFERSEXTPROC) (GLsizei n, const GLuint *framebuffers); -typedef void (APIENTRYP PFNGLGENFRAMEBUFFERSEXTPROC) (GLsizei n, GLuint *framebuffers); -typedef GLenum (APIENTRYP PFNGLCHECKFRAMEBUFFERSTATUSEXTPROC) (GLenum target); -typedef void (APIENTRYP PFNGLFRAMEBUFFERTEXTURE1DEXTPROC) (GLenum target, GLenum attachment, GLenum textarget, GLuint texture, GLint level); -typedef void (APIENTRYP PFNGLFRAMEBUFFERTEXTURE2DEXTPROC) (GLenum target, GLenum attachment, GLenum textarget, GLuint texture, GLint level); -typedef void (APIENTRYP PFNGLFRAMEBUFFERTEXTURE3DEXTPROC) (GLenum target, GLenum attachment, GLenum textarget, GLuint texture, GLint level, GLint zoffset); -typedef void (APIENTRYP PFNGLFRAMEBUFFERRENDERBUFFEREXTPROC) (GLenum target, GLenum attachment, GLenum renderbuffertarget, GLuint renderbuffer); -typedef void (APIENTRYP PFNGLGETFRAMEBUFFERATTACHMENTPARAMETERIVEXTPROC) (GLenum target, GLenum attachment, GLenum pname, GLint *params); -typedef void (APIENTRYP PFNGLGENERATEMIPMAPEXTPROC) (GLenum target); -#ifdef GL_GLEXT_PROTOTYPES -GLAPI GLboolean APIENTRY glIsRenderbufferEXT (GLuint renderbuffer); -GLAPI void APIENTRY glBindRenderbufferEXT (GLenum target, GLuint renderbuffer); -GLAPI void APIENTRY glDeleteRenderbuffersEXT (GLsizei n, const GLuint *renderbuffers); -GLAPI void APIENTRY glGenRenderbuffersEXT (GLsizei n, GLuint *renderbuffers); -GLAPI void APIENTRY glRenderbufferStorageEXT (GLenum target, GLenum internalformat, GLsizei width, GLsizei height); -GLAPI void APIENTRY glGetRenderbufferParameterivEXT (GLenum target, GLenum pname, GLint *params); -GLAPI GLboolean APIENTRY glIsFramebufferEXT (GLuint framebuffer); -GLAPI void APIENTRY glBindFramebufferEXT (GLenum target, GLuint framebuffer); -GLAPI void APIENTRY glDeleteFramebuffersEXT (GLsizei n, const GLuint *framebuffers); -GLAPI void APIENTRY glGenFramebuffersEXT (GLsizei n, GLuint *framebuffers); -GLAPI GLenum APIENTRY glCheckFramebufferStatusEXT (GLenum target); -GLAPI void APIENTRY glFramebufferTexture1DEXT (GLenum target, GLenum attachment, GLenum textarget, GLuint texture, GLint level); -GLAPI void APIENTRY glFramebufferTexture2DEXT (GLenum target, GLenum attachment, GLenum textarget, GLuint texture, GLint level); -GLAPI void APIENTRY glFramebufferTexture3DEXT (GLenum target, GLenum attachment, GLenum textarget, GLuint texture, GLint level, GLint zoffset); -GLAPI void APIENTRY glFramebufferRenderbufferEXT (GLenum target, GLenum attachment, GLenum renderbuffertarget, GLuint renderbuffer); -GLAPI void APIENTRY glGetFramebufferAttachmentParameterivEXT (GLenum target, GLenum attachment, GLenum pname, GLint *params); -GLAPI void APIENTRY glGenerateMipmapEXT (GLenum target); -#endif -#endif /* GL_EXT_framebuffer_object */ - -#ifndef GL_EXT_framebuffer_sRGB -#define GL_EXT_framebuffer_sRGB 1 -#define GL_FRAMEBUFFER_SRGB_EXT 0x8DB9 -#define GL_FRAMEBUFFER_SRGB_CAPABLE_EXT 0x8DBA -#endif /* GL_EXT_framebuffer_sRGB */ - -#ifndef GL_EXT_geometry_shader4 -#define GL_EXT_geometry_shader4 1 -#define GL_GEOMETRY_SHADER_EXT 0x8DD9 -#define GL_GEOMETRY_VERTICES_OUT_EXT 0x8DDA -#define GL_GEOMETRY_INPUT_TYPE_EXT 0x8DDB -#define GL_GEOMETRY_OUTPUT_TYPE_EXT 0x8DDC -#define GL_MAX_GEOMETRY_TEXTURE_IMAGE_UNITS_EXT 0x8C29 -#define GL_MAX_GEOMETRY_VARYING_COMPONENTS_EXT 0x8DDD -#define GL_MAX_VERTEX_VARYING_COMPONENTS_EXT 0x8DDE -#define GL_MAX_VARYING_COMPONENTS_EXT 0x8B4B -#define GL_MAX_GEOMETRY_UNIFORM_COMPONENTS_EXT 0x8DDF -#define GL_MAX_GEOMETRY_OUTPUT_VERTICES_EXT 0x8DE0 -#define GL_MAX_GEOMETRY_TOTAL_OUTPUT_COMPONENTS_EXT 0x8DE1 -#define GL_LINES_ADJACENCY_EXT 0x000A -#define GL_LINE_STRIP_ADJACENCY_EXT 0x000B -#define GL_TRIANGLES_ADJACENCY_EXT 0x000C -#define GL_TRIANGLE_STRIP_ADJACENCY_EXT 0x000D -#define GL_FRAMEBUFFER_INCOMPLETE_LAYER_TARGETS_EXT 0x8DA8 -#define GL_FRAMEBUFFER_INCOMPLETE_LAYER_COUNT_EXT 0x8DA9 -#define GL_FRAMEBUFFER_ATTACHMENT_LAYERED_EXT 0x8DA7 -#define GL_FRAMEBUFFER_ATTACHMENT_TEXTURE_LAYER_EXT 0x8CD4 -#define GL_PROGRAM_POINT_SIZE_EXT 0x8642 -typedef void (APIENTRYP PFNGLPROGRAMPARAMETERIEXTPROC) (GLuint program, GLenum pname, GLint value); -#ifdef GL_GLEXT_PROTOTYPES -GLAPI void APIENTRY glProgramParameteriEXT (GLuint program, GLenum pname, GLint value); -#endif -#endif /* GL_EXT_geometry_shader4 */ - -#ifndef GL_EXT_gpu_program_parameters -#define GL_EXT_gpu_program_parameters 1 -typedef void (APIENTRYP PFNGLPROGRAMENVPARAMETERS4FVEXTPROC) (GLenum target, GLuint index, GLsizei count, const GLfloat *params); -typedef void (APIENTRYP PFNGLPROGRAMLOCALPARAMETERS4FVEXTPROC) (GLenum target, GLuint index, GLsizei count, const GLfloat *params); -#ifdef GL_GLEXT_PROTOTYPES -GLAPI void APIENTRY glProgramEnvParameters4fvEXT (GLenum target, GLuint index, GLsizei count, const GLfloat *params); -GLAPI void APIENTRY glProgramLocalParameters4fvEXT (GLenum target, GLuint index, GLsizei count, const GLfloat *params); -#endif -#endif /* GL_EXT_gpu_program_parameters */ - -#ifndef GL_EXT_gpu_shader4 -#define GL_EXT_gpu_shader4 1 -#define GL_VERTEX_ATTRIB_ARRAY_INTEGER_EXT 0x88FD -#define GL_SAMPLER_1D_ARRAY_EXT 0x8DC0 -#define GL_SAMPLER_2D_ARRAY_EXT 0x8DC1 -#define GL_SAMPLER_BUFFER_EXT 0x8DC2 -#define GL_SAMPLER_1D_ARRAY_SHADOW_EXT 0x8DC3 -#define GL_SAMPLER_2D_ARRAY_SHADOW_EXT 0x8DC4 -#define GL_SAMPLER_CUBE_SHADOW_EXT 0x8DC5 -#define GL_UNSIGNED_INT_VEC2_EXT 0x8DC6 -#define GL_UNSIGNED_INT_VEC3_EXT 0x8DC7 -#define GL_UNSIGNED_INT_VEC4_EXT 0x8DC8 -#define GL_INT_SAMPLER_1D_EXT 0x8DC9 -#define GL_INT_SAMPLER_2D_EXT 0x8DCA -#define GL_INT_SAMPLER_3D_EXT 0x8DCB -#define GL_INT_SAMPLER_CUBE_EXT 0x8DCC -#define GL_INT_SAMPLER_2D_RECT_EXT 0x8DCD -#define GL_INT_SAMPLER_1D_ARRAY_EXT 0x8DCE -#define GL_INT_SAMPLER_2D_ARRAY_EXT 0x8DCF -#define GL_INT_SAMPLER_BUFFER_EXT 0x8DD0 -#define GL_UNSIGNED_INT_SAMPLER_1D_EXT 0x8DD1 -#define GL_UNSIGNED_INT_SAMPLER_2D_EXT 0x8DD2 -#define GL_UNSIGNED_INT_SAMPLER_3D_EXT 0x8DD3 -#define GL_UNSIGNED_INT_SAMPLER_CUBE_EXT 0x8DD4 -#define GL_UNSIGNED_INT_SAMPLER_2D_RECT_EXT 0x8DD5 -#define GL_UNSIGNED_INT_SAMPLER_1D_ARRAY_EXT 0x8DD6 -#define GL_UNSIGNED_INT_SAMPLER_2D_ARRAY_EXT 0x8DD7 -#define GL_UNSIGNED_INT_SAMPLER_BUFFER_EXT 0x8DD8 -#define GL_MIN_PROGRAM_TEXEL_OFFSET_EXT 0x8904 -#define GL_MAX_PROGRAM_TEXEL_OFFSET_EXT 0x8905 -typedef void (APIENTRYP PFNGLGETUNIFORMUIVEXTPROC) (GLuint program, GLint location, GLuint *params); -typedef void (APIENTRYP PFNGLBINDFRAGDATALOCATIONEXTPROC) (GLuint program, GLuint color, const GLchar *name); -typedef GLint (APIENTRYP PFNGLGETFRAGDATALOCATIONEXTPROC) (GLuint program, const GLchar *name); -typedef void (APIENTRYP PFNGLUNIFORM1UIEXTPROC) (GLint location, GLuint v0); -typedef void (APIENTRYP PFNGLUNIFORM2UIEXTPROC) (GLint location, GLuint v0, GLuint v1); -typedef void (APIENTRYP PFNGLUNIFORM3UIEXTPROC) (GLint location, GLuint v0, GLuint v1, GLuint v2); -typedef void (APIENTRYP PFNGLUNIFORM4UIEXTPROC) (GLint location, GLuint v0, GLuint v1, GLuint v2, GLuint v3); -typedef void (APIENTRYP PFNGLUNIFORM1UIVEXTPROC) (GLint location, GLsizei count, const GLuint *value); -typedef void (APIENTRYP PFNGLUNIFORM2UIVEXTPROC) (GLint location, GLsizei count, const GLuint *value); -typedef void (APIENTRYP PFNGLUNIFORM3UIVEXTPROC) (GLint location, GLsizei count, const GLuint *value); -typedef void (APIENTRYP PFNGLUNIFORM4UIVEXTPROC) (GLint location, GLsizei count, const GLuint *value); -#ifdef GL_GLEXT_PROTOTYPES -GLAPI void APIENTRY glGetUniformuivEXT (GLuint program, GLint location, GLuint *params); -GLAPI void APIENTRY glBindFragDataLocationEXT (GLuint program, GLuint color, const GLchar *name); -GLAPI GLint APIENTRY glGetFragDataLocationEXT (GLuint program, const GLchar *name); -GLAPI void APIENTRY glUniform1uiEXT (GLint location, GLuint v0); -GLAPI void APIENTRY glUniform2uiEXT (GLint location, GLuint v0, GLuint v1); -GLAPI void APIENTRY glUniform3uiEXT (GLint location, GLuint v0, GLuint v1, GLuint v2); -GLAPI void APIENTRY glUniform4uiEXT (GLint location, GLuint v0, GLuint v1, GLuint v2, GLuint v3); -GLAPI void APIENTRY glUniform1uivEXT (GLint location, GLsizei count, const GLuint *value); -GLAPI void APIENTRY glUniform2uivEXT (GLint location, GLsizei count, const GLuint *value); -GLAPI void APIENTRY glUniform3uivEXT (GLint location, GLsizei count, const GLuint *value); -GLAPI void APIENTRY glUniform4uivEXT (GLint location, GLsizei count, const GLuint *value); -#endif -#endif /* GL_EXT_gpu_shader4 */ - -#ifndef GL_EXT_histogram -#define GL_EXT_histogram 1 -#define GL_HISTOGRAM_EXT 0x8024 -#define GL_PROXY_HISTOGRAM_EXT 0x8025 -#define GL_HISTOGRAM_WIDTH_EXT 0x8026 -#define GL_HISTOGRAM_FORMAT_EXT 0x8027 -#define GL_HISTOGRAM_RED_SIZE_EXT 0x8028 -#define GL_HISTOGRAM_GREEN_SIZE_EXT 0x8029 -#define GL_HISTOGRAM_BLUE_SIZE_EXT 0x802A -#define GL_HISTOGRAM_ALPHA_SIZE_EXT 0x802B -#define GL_HISTOGRAM_LUMINANCE_SIZE_EXT 0x802C -#define GL_HISTOGRAM_SINK_EXT 0x802D -#define GL_MINMAX_EXT 0x802E -#define GL_MINMAX_FORMAT_EXT 0x802F -#define GL_MINMAX_SINK_EXT 0x8030 -#define GL_TABLE_TOO_LARGE_EXT 0x8031 -typedef void (APIENTRYP PFNGLGETHISTOGRAMEXTPROC) (GLenum target, GLboolean reset, GLenum format, GLenum type, void *values); -typedef void (APIENTRYP PFNGLGETHISTOGRAMPARAMETERFVEXTPROC) (GLenum target, GLenum pname, GLfloat *params); -typedef void (APIENTRYP PFNGLGETHISTOGRAMPARAMETERIVEXTPROC) (GLenum target, GLenum pname, GLint *params); -typedef void (APIENTRYP PFNGLGETMINMAXEXTPROC) (GLenum target, GLboolean reset, GLenum format, GLenum type, void *values); -typedef void (APIENTRYP PFNGLGETMINMAXPARAMETERFVEXTPROC) (GLenum target, GLenum pname, GLfloat *params); -typedef void (APIENTRYP PFNGLGETMINMAXPARAMETERIVEXTPROC) (GLenum target, GLenum pname, GLint *params); -typedef void (APIENTRYP PFNGLHISTOGRAMEXTPROC) (GLenum target, GLsizei width, GLenum internalformat, GLboolean sink); -typedef void (APIENTRYP PFNGLMINMAXEXTPROC) (GLenum target, GLenum internalformat, GLboolean sink); -typedef void (APIENTRYP PFNGLRESETHISTOGRAMEXTPROC) (GLenum target); -typedef void (APIENTRYP PFNGLRESETMINMAXEXTPROC) (GLenum target); -#ifdef GL_GLEXT_PROTOTYPES -GLAPI void APIENTRY glGetHistogramEXT (GLenum target, GLboolean reset, GLenum format, GLenum type, void *values); -GLAPI void APIENTRY glGetHistogramParameterfvEXT (GLenum target, GLenum pname, GLfloat *params); -GLAPI void APIENTRY glGetHistogramParameterivEXT (GLenum target, GLenum pname, GLint *params); -GLAPI void APIENTRY glGetMinmaxEXT (GLenum target, GLboolean reset, GLenum format, GLenum type, void *values); -GLAPI void APIENTRY glGetMinmaxParameterfvEXT (GLenum target, GLenum pname, GLfloat *params); -GLAPI void APIENTRY glGetMinmaxParameterivEXT (GLenum target, GLenum pname, GLint *params); -GLAPI void APIENTRY glHistogramEXT (GLenum target, GLsizei width, GLenum internalformat, GLboolean sink); -GLAPI void APIENTRY glMinmaxEXT (GLenum target, GLenum internalformat, GLboolean sink); -GLAPI void APIENTRY glResetHistogramEXT (GLenum target); -GLAPI void APIENTRY glResetMinmaxEXT (GLenum target); -#endif -#endif /* GL_EXT_histogram */ - -#ifndef GL_EXT_index_array_formats -#define GL_EXT_index_array_formats 1 -#define GL_IUI_V2F_EXT 0x81AD -#define GL_IUI_V3F_EXT 0x81AE -#define GL_IUI_N3F_V2F_EXT 0x81AF -#define GL_IUI_N3F_V3F_EXT 0x81B0 -#define GL_T2F_IUI_V2F_EXT 0x81B1 -#define GL_T2F_IUI_V3F_EXT 0x81B2 -#define GL_T2F_IUI_N3F_V2F_EXT 0x81B3 -#define GL_T2F_IUI_N3F_V3F_EXT 0x81B4 -#endif /* GL_EXT_index_array_formats */ - -#ifndef GL_EXT_index_func -#define GL_EXT_index_func 1 -#define GL_INDEX_TEST_EXT 0x81B5 -#define GL_INDEX_TEST_FUNC_EXT 0x81B6 -#define GL_INDEX_TEST_REF_EXT 0x81B7 -typedef void (APIENTRYP PFNGLINDEXFUNCEXTPROC) (GLenum func, GLclampf ref); -#ifdef GL_GLEXT_PROTOTYPES -GLAPI void APIENTRY glIndexFuncEXT (GLenum func, GLclampf ref); -#endif -#endif /* GL_EXT_index_func */ - -#ifndef GL_EXT_index_material -#define GL_EXT_index_material 1 -#define GL_INDEX_MATERIAL_EXT 0x81B8 -#define GL_INDEX_MATERIAL_PARAMETER_EXT 0x81B9 -#define GL_INDEX_MATERIAL_FACE_EXT 0x81BA -typedef void (APIENTRYP PFNGLINDEXMATERIALEXTPROC) (GLenum face, GLenum mode); -#ifdef GL_GLEXT_PROTOTYPES -GLAPI void APIENTRY glIndexMaterialEXT (GLenum face, GLenum mode); -#endif -#endif /* GL_EXT_index_material */ - -#ifndef GL_EXT_index_texture -#define GL_EXT_index_texture 1 -#endif /* GL_EXT_index_texture */ - -#ifndef GL_EXT_light_texture -#define GL_EXT_light_texture 1 -#define GL_FRAGMENT_MATERIAL_EXT 0x8349 -#define GL_FRAGMENT_NORMAL_EXT 0x834A -#define GL_FRAGMENT_COLOR_EXT 0x834C -#define GL_ATTENUATION_EXT 0x834D -#define GL_SHADOW_ATTENUATION_EXT 0x834E -#define GL_TEXTURE_APPLICATION_MODE_EXT 0x834F -#define GL_TEXTURE_LIGHT_EXT 0x8350 -#define GL_TEXTURE_MATERIAL_FACE_EXT 0x8351 -#define GL_TEXTURE_MATERIAL_PARAMETER_EXT 0x8352 -typedef void (APIENTRYP PFNGLAPPLYTEXTUREEXTPROC) (GLenum mode); -typedef void (APIENTRYP PFNGLTEXTURELIGHTEXTPROC) (GLenum pname); -typedef void (APIENTRYP PFNGLTEXTUREMATERIALEXTPROC) (GLenum face, GLenum mode); -#ifdef GL_GLEXT_PROTOTYPES -GLAPI void APIENTRY glApplyTextureEXT (GLenum mode); -GLAPI void APIENTRY glTextureLightEXT (GLenum pname); -GLAPI void APIENTRY glTextureMaterialEXT (GLenum face, GLenum mode); -#endif -#endif /* GL_EXT_light_texture */ - -#ifndef GL_EXT_misc_attribute -#define GL_EXT_misc_attribute 1 -#endif /* GL_EXT_misc_attribute */ - -#ifndef GL_EXT_multi_draw_arrays -#define GL_EXT_multi_draw_arrays 1 -typedef void (APIENTRYP PFNGLMULTIDRAWARRAYSEXTPROC) (GLenum mode, const GLint *first, const GLsizei *count, GLsizei primcount); -typedef void (APIENTRYP PFNGLMULTIDRAWELEMENTSEXTPROC) (GLenum mode, const GLsizei *count, GLenum type, const void *const*indices, GLsizei primcount); -#ifdef GL_GLEXT_PROTOTYPES -GLAPI void APIENTRY glMultiDrawArraysEXT (GLenum mode, const GLint *first, const GLsizei *count, GLsizei primcount); -GLAPI void APIENTRY glMultiDrawElementsEXT (GLenum mode, const GLsizei *count, GLenum type, const void *const*indices, GLsizei primcount); -#endif -#endif /* GL_EXT_multi_draw_arrays */ - -#ifndef GL_EXT_multisample -#define GL_EXT_multisample 1 -#define GL_MULTISAMPLE_EXT 0x809D -#define GL_SAMPLE_ALPHA_TO_MASK_EXT 0x809E -#define GL_SAMPLE_ALPHA_TO_ONE_EXT 0x809F -#define GL_SAMPLE_MASK_EXT 0x80A0 -#define GL_1PASS_EXT 0x80A1 -#define GL_2PASS_0_EXT 0x80A2 -#define GL_2PASS_1_EXT 0x80A3 -#define GL_4PASS_0_EXT 0x80A4 -#define GL_4PASS_1_EXT 0x80A5 -#define GL_4PASS_2_EXT 0x80A6 -#define GL_4PASS_3_EXT 0x80A7 -#define GL_SAMPLE_BUFFERS_EXT 0x80A8 -#define GL_SAMPLES_EXT 0x80A9 -#define GL_SAMPLE_MASK_VALUE_EXT 0x80AA -#define GL_SAMPLE_MASK_INVERT_EXT 0x80AB -#define GL_SAMPLE_PATTERN_EXT 0x80AC -#define GL_MULTISAMPLE_BIT_EXT 0x20000000 -typedef void (APIENTRYP PFNGLSAMPLEMASKEXTPROC) (GLclampf value, GLboolean invert); -typedef void (APIENTRYP PFNGLSAMPLEPATTERNEXTPROC) (GLenum pattern); -#ifdef GL_GLEXT_PROTOTYPES -GLAPI void APIENTRY glSampleMaskEXT (GLclampf value, GLboolean invert); -GLAPI void APIENTRY glSamplePatternEXT (GLenum pattern); -#endif -#endif /* GL_EXT_multisample */ - -#ifndef GL_EXT_packed_depth_stencil -#define GL_EXT_packed_depth_stencil 1 -#define GL_DEPTH_STENCIL_EXT 0x84F9 -#define GL_UNSIGNED_INT_24_8_EXT 0x84FA -#define GL_DEPTH24_STENCIL8_EXT 0x88F0 -#define GL_TEXTURE_STENCIL_SIZE_EXT 0x88F1 -#endif /* GL_EXT_packed_depth_stencil */ - -#ifndef GL_EXT_packed_float -#define GL_EXT_packed_float 1 -#define GL_R11F_G11F_B10F_EXT 0x8C3A -#define GL_UNSIGNED_INT_10F_11F_11F_REV_EXT 0x8C3B -#define GL_RGBA_SIGNED_COMPONENTS_EXT 0x8C3C -#endif /* GL_EXT_packed_float */ - -#ifndef GL_EXT_packed_pixels -#define GL_EXT_packed_pixels 1 -#define GL_UNSIGNED_BYTE_3_3_2_EXT 0x8032 -#define GL_UNSIGNED_SHORT_4_4_4_4_EXT 0x8033 -#define GL_UNSIGNED_SHORT_5_5_5_1_EXT 0x8034 -#define GL_UNSIGNED_INT_8_8_8_8_EXT 0x8035 -#define GL_UNSIGNED_INT_10_10_10_2_EXT 0x8036 -#endif /* GL_EXT_packed_pixels */ - -#ifndef GL_EXT_paletted_texture -#define GL_EXT_paletted_texture 1 -#define GL_COLOR_INDEX1_EXT 0x80E2 -#define GL_COLOR_INDEX2_EXT 0x80E3 -#define GL_COLOR_INDEX4_EXT 0x80E4 -#define GL_COLOR_INDEX8_EXT 0x80E5 -#define GL_COLOR_INDEX12_EXT 0x80E6 -#define GL_COLOR_INDEX16_EXT 0x80E7 -#define GL_TEXTURE_INDEX_SIZE_EXT 0x80ED -typedef void (APIENTRYP PFNGLCOLORTABLEEXTPROC) (GLenum target, GLenum internalFormat, GLsizei width, GLenum format, GLenum type, const void *table); -typedef void (APIENTRYP PFNGLGETCOLORTABLEEXTPROC) (GLenum target, GLenum format, GLenum type, void *data); -typedef void (APIENTRYP PFNGLGETCOLORTABLEPARAMETERIVEXTPROC) (GLenum target, GLenum pname, GLint *params); -typedef void (APIENTRYP PFNGLGETCOLORTABLEPARAMETERFVEXTPROC) (GLenum target, GLenum pname, GLfloat *params); -#ifdef GL_GLEXT_PROTOTYPES -GLAPI void APIENTRY glColorTableEXT (GLenum target, GLenum internalFormat, GLsizei width, GLenum format, GLenum type, const void *table); -GLAPI void APIENTRY glGetColorTableEXT (GLenum target, GLenum format, GLenum type, void *data); -GLAPI void APIENTRY glGetColorTableParameterivEXT (GLenum target, GLenum pname, GLint *params); -GLAPI void APIENTRY glGetColorTableParameterfvEXT (GLenum target, GLenum pname, GLfloat *params); -#endif -#endif /* GL_EXT_paletted_texture */ - -#ifndef GL_EXT_pixel_buffer_object -#define GL_EXT_pixel_buffer_object 1 -#define GL_PIXEL_PACK_BUFFER_EXT 0x88EB -#define GL_PIXEL_UNPACK_BUFFER_EXT 0x88EC -#define GL_PIXEL_PACK_BUFFER_BINDING_EXT 0x88ED -#define GL_PIXEL_UNPACK_BUFFER_BINDING_EXT 0x88EF -#endif /* GL_EXT_pixel_buffer_object */ - -#ifndef GL_EXT_pixel_transform -#define GL_EXT_pixel_transform 1 -#define GL_PIXEL_TRANSFORM_2D_EXT 0x8330 -#define GL_PIXEL_MAG_FILTER_EXT 0x8331 -#define GL_PIXEL_MIN_FILTER_EXT 0x8332 -#define GL_PIXEL_CUBIC_WEIGHT_EXT 0x8333 -#define GL_CUBIC_EXT 0x8334 -#define GL_AVERAGE_EXT 0x8335 -#define GL_PIXEL_TRANSFORM_2D_STACK_DEPTH_EXT 0x8336 -#define GL_MAX_PIXEL_TRANSFORM_2D_STACK_DEPTH_EXT 0x8337 -#define GL_PIXEL_TRANSFORM_2D_MATRIX_EXT 0x8338 -typedef void (APIENTRYP PFNGLPIXELTRANSFORMPARAMETERIEXTPROC) (GLenum target, GLenum pname, GLint param); -typedef void (APIENTRYP PFNGLPIXELTRANSFORMPARAMETERFEXTPROC) (GLenum target, GLenum pname, GLfloat param); -typedef void (APIENTRYP PFNGLPIXELTRANSFORMPARAMETERIVEXTPROC) (GLenum target, GLenum pname, const GLint *params); -typedef void (APIENTRYP PFNGLPIXELTRANSFORMPARAMETERFVEXTPROC) (GLenum target, GLenum pname, const GLfloat *params); -typedef void (APIENTRYP PFNGLGETPIXELTRANSFORMPARAMETERIVEXTPROC) (GLenum target, GLenum pname, GLint *params); -typedef void (APIENTRYP PFNGLGETPIXELTRANSFORMPARAMETERFVEXTPROC) (GLenum target, GLenum pname, GLfloat *params); -#ifdef GL_GLEXT_PROTOTYPES -GLAPI void APIENTRY glPixelTransformParameteriEXT (GLenum target, GLenum pname, GLint param); -GLAPI void APIENTRY glPixelTransformParameterfEXT (GLenum target, GLenum pname, GLfloat param); -GLAPI void APIENTRY glPixelTransformParameterivEXT (GLenum target, GLenum pname, const GLint *params); -GLAPI void APIENTRY glPixelTransformParameterfvEXT (GLenum target, GLenum pname, const GLfloat *params); -GLAPI void APIENTRY glGetPixelTransformParameterivEXT (GLenum target, GLenum pname, GLint *params); -GLAPI void APIENTRY glGetPixelTransformParameterfvEXT (GLenum target, GLenum pname, GLfloat *params); -#endif -#endif /* GL_EXT_pixel_transform */ - -#ifndef GL_EXT_pixel_transform_color_table -#define GL_EXT_pixel_transform_color_table 1 -#endif /* GL_EXT_pixel_transform_color_table */ - -#ifndef GL_EXT_point_parameters -#define GL_EXT_point_parameters 1 -#define GL_POINT_SIZE_MIN_EXT 0x8126 -#define GL_POINT_SIZE_MAX_EXT 0x8127 -#define GL_POINT_FADE_THRESHOLD_SIZE_EXT 0x8128 -#define GL_DISTANCE_ATTENUATION_EXT 0x8129 -typedef void (APIENTRYP PFNGLPOINTPARAMETERFEXTPROC) (GLenum pname, GLfloat param); -typedef void (APIENTRYP PFNGLPOINTPARAMETERFVEXTPROC) (GLenum pname, const GLfloat *params); -#ifdef GL_GLEXT_PROTOTYPES -GLAPI void APIENTRY glPointParameterfEXT (GLenum pname, GLfloat param); -GLAPI void APIENTRY glPointParameterfvEXT (GLenum pname, const GLfloat *params); -#endif -#endif /* GL_EXT_point_parameters */ - -#ifndef GL_EXT_polygon_offset -#define GL_EXT_polygon_offset 1 -#define GL_POLYGON_OFFSET_EXT 0x8037 -#define GL_POLYGON_OFFSET_FACTOR_EXT 0x8038 -#define GL_POLYGON_OFFSET_BIAS_EXT 0x8039 -typedef void (APIENTRYP PFNGLPOLYGONOFFSETEXTPROC) (GLfloat factor, GLfloat bias); -#ifdef GL_GLEXT_PROTOTYPES -GLAPI void APIENTRY glPolygonOffsetEXT (GLfloat factor, GLfloat bias); -#endif -#endif /* GL_EXT_polygon_offset */ - -#ifndef GL_EXT_provoking_vertex -#define GL_EXT_provoking_vertex 1 -#define GL_QUADS_FOLLOW_PROVOKING_VERTEX_CONVENTION_EXT 0x8E4C -#define GL_FIRST_VERTEX_CONVENTION_EXT 0x8E4D -#define GL_LAST_VERTEX_CONVENTION_EXT 0x8E4E -#define GL_PROVOKING_VERTEX_EXT 0x8E4F -typedef void (APIENTRYP PFNGLPROVOKINGVERTEXEXTPROC) (GLenum mode); -#ifdef GL_GLEXT_PROTOTYPES -GLAPI void APIENTRY glProvokingVertexEXT (GLenum mode); -#endif -#endif /* GL_EXT_provoking_vertex */ - -#ifndef GL_EXT_rescale_normal -#define GL_EXT_rescale_normal 1 -#define GL_RESCALE_NORMAL_EXT 0x803A -#endif /* GL_EXT_rescale_normal */ - -#ifndef GL_EXT_secondary_color -#define GL_EXT_secondary_color 1 -#define GL_COLOR_SUM_EXT 0x8458 -#define GL_CURRENT_SECONDARY_COLOR_EXT 0x8459 -#define GL_SECONDARY_COLOR_ARRAY_SIZE_EXT 0x845A -#define GL_SECONDARY_COLOR_ARRAY_TYPE_EXT 0x845B -#define GL_SECONDARY_COLOR_ARRAY_STRIDE_EXT 0x845C -#define GL_SECONDARY_COLOR_ARRAY_POINTER_EXT 0x845D -#define GL_SECONDARY_COLOR_ARRAY_EXT 0x845E -typedef void (APIENTRYP PFNGLSECONDARYCOLOR3BEXTPROC) (GLbyte red, GLbyte green, GLbyte blue); -typedef void (APIENTRYP PFNGLSECONDARYCOLOR3BVEXTPROC) (const GLbyte *v); -typedef void (APIENTRYP PFNGLSECONDARYCOLOR3DEXTPROC) (GLdouble red, GLdouble green, GLdouble blue); -typedef void (APIENTRYP PFNGLSECONDARYCOLOR3DVEXTPROC) (const GLdouble *v); -typedef void (APIENTRYP PFNGLSECONDARYCOLOR3FEXTPROC) (GLfloat red, GLfloat green, GLfloat blue); -typedef void (APIENTRYP PFNGLSECONDARYCOLOR3FVEXTPROC) (const GLfloat *v); -typedef void (APIENTRYP PFNGLSECONDARYCOLOR3IEXTPROC) (GLint red, GLint green, GLint blue); -typedef void (APIENTRYP PFNGLSECONDARYCOLOR3IVEXTPROC) (const GLint *v); -typedef void (APIENTRYP PFNGLSECONDARYCOLOR3SEXTPROC) (GLshort red, GLshort green, GLshort blue); -typedef void (APIENTRYP PFNGLSECONDARYCOLOR3SVEXTPROC) (const GLshort *v); -typedef void (APIENTRYP PFNGLSECONDARYCOLOR3UBEXTPROC) (GLubyte red, GLubyte green, GLubyte blue); -typedef void (APIENTRYP PFNGLSECONDARYCOLOR3UBVEXTPROC) (const GLubyte *v); -typedef void (APIENTRYP PFNGLSECONDARYCOLOR3UIEXTPROC) (GLuint red, GLuint green, GLuint blue); -typedef void (APIENTRYP PFNGLSECONDARYCOLOR3UIVEXTPROC) (const GLuint *v); -typedef void (APIENTRYP PFNGLSECONDARYCOLOR3USEXTPROC) (GLushort red, GLushort green, GLushort blue); -typedef void (APIENTRYP PFNGLSECONDARYCOLOR3USVEXTPROC) (const GLushort *v); -typedef void (APIENTRYP PFNGLSECONDARYCOLORPOINTEREXTPROC) (GLint size, GLenum type, GLsizei stride, const void *pointer); -#ifdef GL_GLEXT_PROTOTYPES -GLAPI void APIENTRY glSecondaryColor3bEXT (GLbyte red, GLbyte green, GLbyte blue); -GLAPI void APIENTRY glSecondaryColor3bvEXT (const GLbyte *v); -GLAPI void APIENTRY glSecondaryColor3dEXT (GLdouble red, GLdouble green, GLdouble blue); -GLAPI void APIENTRY glSecondaryColor3dvEXT (const GLdouble *v); -GLAPI void APIENTRY glSecondaryColor3fEXT (GLfloat red, GLfloat green, GLfloat blue); -GLAPI void APIENTRY glSecondaryColor3fvEXT (const GLfloat *v); -GLAPI void APIENTRY glSecondaryColor3iEXT (GLint red, GLint green, GLint blue); -GLAPI void APIENTRY glSecondaryColor3ivEXT (const GLint *v); -GLAPI void APIENTRY glSecondaryColor3sEXT (GLshort red, GLshort green, GLshort blue); -GLAPI void APIENTRY glSecondaryColor3svEXT (const GLshort *v); -GLAPI void APIENTRY glSecondaryColor3ubEXT (GLubyte red, GLubyte green, GLubyte blue); -GLAPI void APIENTRY glSecondaryColor3ubvEXT (const GLubyte *v); -GLAPI void APIENTRY glSecondaryColor3uiEXT (GLuint red, GLuint green, GLuint blue); -GLAPI void APIENTRY glSecondaryColor3uivEXT (const GLuint *v); -GLAPI void APIENTRY glSecondaryColor3usEXT (GLushort red, GLushort green, GLushort blue); -GLAPI void APIENTRY glSecondaryColor3usvEXT (const GLushort *v); -GLAPI void APIENTRY glSecondaryColorPointerEXT (GLint size, GLenum type, GLsizei stride, const void *pointer); -#endif -#endif /* GL_EXT_secondary_color */ - -#ifndef GL_EXT_separate_shader_objects -#define GL_EXT_separate_shader_objects 1 -#define GL_ACTIVE_PROGRAM_EXT 0x8B8D -typedef void (APIENTRYP PFNGLUSESHADERPROGRAMEXTPROC) (GLenum type, GLuint program); -typedef void (APIENTRYP PFNGLACTIVEPROGRAMEXTPROC) (GLuint program); -typedef GLuint (APIENTRYP PFNGLCREATESHADERPROGRAMEXTPROC) (GLenum type, const GLchar *string); -#ifdef GL_GLEXT_PROTOTYPES -GLAPI void APIENTRY glUseShaderProgramEXT (GLenum type, GLuint program); -GLAPI void APIENTRY glActiveProgramEXT (GLuint program); -GLAPI GLuint APIENTRY glCreateShaderProgramEXT (GLenum type, const GLchar *string); -#endif -#endif /* GL_EXT_separate_shader_objects */ - -#ifndef GL_EXT_separate_specular_color -#define GL_EXT_separate_specular_color 1 -#define GL_LIGHT_MODEL_COLOR_CONTROL_EXT 0x81F8 -#define GL_SINGLE_COLOR_EXT 0x81F9 -#define GL_SEPARATE_SPECULAR_COLOR_EXT 0x81FA -#endif /* GL_EXT_separate_specular_color */ - -#ifndef GL_EXT_shader_image_load_store -#define GL_EXT_shader_image_load_store 1 -#define GL_MAX_IMAGE_UNITS_EXT 0x8F38 -#define GL_MAX_COMBINED_IMAGE_UNITS_AND_FRAGMENT_OUTPUTS_EXT 0x8F39 -#define GL_IMAGE_BINDING_NAME_EXT 0x8F3A -#define GL_IMAGE_BINDING_LEVEL_EXT 0x8F3B -#define GL_IMAGE_BINDING_LAYERED_EXT 0x8F3C -#define GL_IMAGE_BINDING_LAYER_EXT 0x8F3D -#define GL_IMAGE_BINDING_ACCESS_EXT 0x8F3E -#define GL_IMAGE_1D_EXT 0x904C -#define GL_IMAGE_2D_EXT 0x904D -#define GL_IMAGE_3D_EXT 0x904E -#define GL_IMAGE_2D_RECT_EXT 0x904F -#define GL_IMAGE_CUBE_EXT 0x9050 -#define GL_IMAGE_BUFFER_EXT 0x9051 -#define GL_IMAGE_1D_ARRAY_EXT 0x9052 -#define GL_IMAGE_2D_ARRAY_EXT 0x9053 -#define GL_IMAGE_CUBE_MAP_ARRAY_EXT 0x9054 -#define GL_IMAGE_2D_MULTISAMPLE_EXT 0x9055 -#define GL_IMAGE_2D_MULTISAMPLE_ARRAY_EXT 0x9056 -#define GL_INT_IMAGE_1D_EXT 0x9057 -#define GL_INT_IMAGE_2D_EXT 0x9058 -#define GL_INT_IMAGE_3D_EXT 0x9059 -#define GL_INT_IMAGE_2D_RECT_EXT 0x905A -#define GL_INT_IMAGE_CUBE_EXT 0x905B -#define GL_INT_IMAGE_BUFFER_EXT 0x905C -#define GL_INT_IMAGE_1D_ARRAY_EXT 0x905D -#define GL_INT_IMAGE_2D_ARRAY_EXT 0x905E -#define GL_INT_IMAGE_CUBE_MAP_ARRAY_EXT 0x905F -#define GL_INT_IMAGE_2D_MULTISAMPLE_EXT 0x9060 -#define GL_INT_IMAGE_2D_MULTISAMPLE_ARRAY_EXT 0x9061 -#define GL_UNSIGNED_INT_IMAGE_1D_EXT 0x9062 -#define GL_UNSIGNED_INT_IMAGE_2D_EXT 0x9063 -#define GL_UNSIGNED_INT_IMAGE_3D_EXT 0x9064 -#define GL_UNSIGNED_INT_IMAGE_2D_RECT_EXT 0x9065 -#define GL_UNSIGNED_INT_IMAGE_CUBE_EXT 0x9066 -#define GL_UNSIGNED_INT_IMAGE_BUFFER_EXT 0x9067 -#define GL_UNSIGNED_INT_IMAGE_1D_ARRAY_EXT 0x9068 -#define GL_UNSIGNED_INT_IMAGE_2D_ARRAY_EXT 0x9069 -#define GL_UNSIGNED_INT_IMAGE_CUBE_MAP_ARRAY_EXT 0x906A -#define GL_UNSIGNED_INT_IMAGE_2D_MULTISAMPLE_EXT 0x906B -#define GL_UNSIGNED_INT_IMAGE_2D_MULTISAMPLE_ARRAY_EXT 0x906C -#define GL_MAX_IMAGE_SAMPLES_EXT 0x906D -#define GL_IMAGE_BINDING_FORMAT_EXT 0x906E -#define GL_VERTEX_ATTRIB_ARRAY_BARRIER_BIT_EXT 0x00000001 -#define GL_ELEMENT_ARRAY_BARRIER_BIT_EXT 0x00000002 -#define GL_UNIFORM_BARRIER_BIT_EXT 0x00000004 -#define GL_TEXTURE_FETCH_BARRIER_BIT_EXT 0x00000008 -#define GL_SHADER_IMAGE_ACCESS_BARRIER_BIT_EXT 0x00000020 -#define GL_COMMAND_BARRIER_BIT_EXT 0x00000040 -#define GL_PIXEL_BUFFER_BARRIER_BIT_EXT 0x00000080 -#define GL_TEXTURE_UPDATE_BARRIER_BIT_EXT 0x00000100 -#define GL_BUFFER_UPDATE_BARRIER_BIT_EXT 0x00000200 -#define GL_FRAMEBUFFER_BARRIER_BIT_EXT 0x00000400 -#define GL_TRANSFORM_FEEDBACK_BARRIER_BIT_EXT 0x00000800 -#define GL_ATOMIC_COUNTER_BARRIER_BIT_EXT 0x00001000 -#define GL_ALL_BARRIER_BITS_EXT 0xFFFFFFFF -typedef void (APIENTRYP PFNGLBINDIMAGETEXTUREEXTPROC) (GLuint index, GLuint texture, GLint level, GLboolean layered, GLint layer, GLenum access, GLint format); -typedef void (APIENTRYP PFNGLMEMORYBARRIEREXTPROC) (GLbitfield barriers); -#ifdef GL_GLEXT_PROTOTYPES -GLAPI void APIENTRY glBindImageTextureEXT (GLuint index, GLuint texture, GLint level, GLboolean layered, GLint layer, GLenum access, GLint format); -GLAPI void APIENTRY glMemoryBarrierEXT (GLbitfield barriers); -#endif -#endif /* GL_EXT_shader_image_load_store */ - -#ifndef GL_EXT_shader_integer_mix -#define GL_EXT_shader_integer_mix 1 -#endif /* GL_EXT_shader_integer_mix */ - -#ifndef GL_EXT_shadow_funcs -#define GL_EXT_shadow_funcs 1 -#endif /* GL_EXT_shadow_funcs */ - -#ifndef GL_EXT_shared_texture_palette -#define GL_EXT_shared_texture_palette 1 -#define GL_SHARED_TEXTURE_PALETTE_EXT 0x81FB -#endif /* GL_EXT_shared_texture_palette */ - -#ifndef GL_EXT_stencil_clear_tag -#define GL_EXT_stencil_clear_tag 1 -#define GL_STENCIL_TAG_BITS_EXT 0x88F2 -#define GL_STENCIL_CLEAR_TAG_VALUE_EXT 0x88F3 -typedef void (APIENTRYP PFNGLSTENCILCLEARTAGEXTPROC) (GLsizei stencilTagBits, GLuint stencilClearTag); -#ifdef GL_GLEXT_PROTOTYPES -GLAPI void APIENTRY glStencilClearTagEXT (GLsizei stencilTagBits, GLuint stencilClearTag); -#endif -#endif /* GL_EXT_stencil_clear_tag */ - -#ifndef GL_EXT_stencil_two_side -#define GL_EXT_stencil_two_side 1 -#define GL_STENCIL_TEST_TWO_SIDE_EXT 0x8910 -#define GL_ACTIVE_STENCIL_FACE_EXT 0x8911 -typedef void (APIENTRYP PFNGLACTIVESTENCILFACEEXTPROC) (GLenum face); -#ifdef GL_GLEXT_PROTOTYPES -GLAPI void APIENTRY glActiveStencilFaceEXT (GLenum face); -#endif -#endif /* GL_EXT_stencil_two_side */ - -#ifndef GL_EXT_stencil_wrap -#define GL_EXT_stencil_wrap 1 -#define GL_INCR_WRAP_EXT 0x8507 -#define GL_DECR_WRAP_EXT 0x8508 -#endif /* GL_EXT_stencil_wrap */ - -#ifndef GL_EXT_subtexture -#define GL_EXT_subtexture 1 -typedef void (APIENTRYP PFNGLTEXSUBIMAGE1DEXTPROC) (GLenum target, GLint level, GLint xoffset, GLsizei width, GLenum format, GLenum type, const void *pixels); -typedef void (APIENTRYP PFNGLTEXSUBIMAGE2DEXTPROC) (GLenum target, GLint level, GLint xoffset, GLint yoffset, GLsizei width, GLsizei height, GLenum format, GLenum type, const void *pixels); -#ifdef GL_GLEXT_PROTOTYPES -GLAPI void APIENTRY glTexSubImage1DEXT (GLenum target, GLint level, GLint xoffset, GLsizei width, GLenum format, GLenum type, const void *pixels); -GLAPI void APIENTRY glTexSubImage2DEXT (GLenum target, GLint level, GLint xoffset, GLint yoffset, GLsizei width, GLsizei height, GLenum format, GLenum type, const void *pixels); -#endif -#endif /* GL_EXT_subtexture */ - -#ifndef GL_EXT_texture -#define GL_EXT_texture 1 -#define GL_ALPHA4_EXT 0x803B -#define GL_ALPHA8_EXT 0x803C -#define GL_ALPHA12_EXT 0x803D -#define GL_ALPHA16_EXT 0x803E -#define GL_LUMINANCE4_EXT 0x803F -#define GL_LUMINANCE8_EXT 0x8040 -#define GL_LUMINANCE12_EXT 0x8041 -#define GL_LUMINANCE16_EXT 0x8042 -#define GL_LUMINANCE4_ALPHA4_EXT 0x8043 -#define GL_LUMINANCE6_ALPHA2_EXT 0x8044 -#define GL_LUMINANCE8_ALPHA8_EXT 0x8045 -#define GL_LUMINANCE12_ALPHA4_EXT 0x8046 -#define GL_LUMINANCE12_ALPHA12_EXT 0x8047 -#define GL_LUMINANCE16_ALPHA16_EXT 0x8048 -#define GL_INTENSITY_EXT 0x8049 -#define GL_INTENSITY4_EXT 0x804A -#define GL_INTENSITY8_EXT 0x804B -#define GL_INTENSITY12_EXT 0x804C -#define GL_INTENSITY16_EXT 0x804D -#define GL_RGB2_EXT 0x804E -#define GL_RGB4_EXT 0x804F -#define GL_RGB5_EXT 0x8050 -#define GL_RGB8_EXT 0x8051 -#define GL_RGB10_EXT 0x8052 -#define GL_RGB12_EXT 0x8053 -#define GL_RGB16_EXT 0x8054 -#define GL_RGBA2_EXT 0x8055 -#define GL_RGBA4_EXT 0x8056 -#define GL_RGB5_A1_EXT 0x8057 -#define GL_RGBA8_EXT 0x8058 -#define GL_RGB10_A2_EXT 0x8059 -#define GL_RGBA12_EXT 0x805A -#define GL_RGBA16_EXT 0x805B -#define GL_TEXTURE_RED_SIZE_EXT 0x805C -#define GL_TEXTURE_GREEN_SIZE_EXT 0x805D -#define GL_TEXTURE_BLUE_SIZE_EXT 0x805E -#define GL_TEXTURE_ALPHA_SIZE_EXT 0x805F -#define GL_TEXTURE_LUMINANCE_SIZE_EXT 0x8060 -#define GL_TEXTURE_INTENSITY_SIZE_EXT 0x8061 -#define GL_REPLACE_EXT 0x8062 -#define GL_PROXY_TEXTURE_1D_EXT 0x8063 -#define GL_PROXY_TEXTURE_2D_EXT 0x8064 -#define GL_TEXTURE_TOO_LARGE_EXT 0x8065 -#endif /* GL_EXT_texture */ - -#ifndef GL_EXT_texture3D -#define GL_EXT_texture3D 1 -#define GL_PACK_SKIP_IMAGES_EXT 0x806B -#define GL_PACK_IMAGE_HEIGHT_EXT 0x806C -#define GL_UNPACK_SKIP_IMAGES_EXT 0x806D -#define GL_UNPACK_IMAGE_HEIGHT_EXT 0x806E -#define GL_TEXTURE_3D_EXT 0x806F -#define GL_PROXY_TEXTURE_3D_EXT 0x8070 -#define GL_TEXTURE_DEPTH_EXT 0x8071 -#define GL_TEXTURE_WRAP_R_EXT 0x8072 -#define GL_MAX_3D_TEXTURE_SIZE_EXT 0x8073 -typedef void (APIENTRYP PFNGLTEXIMAGE3DEXTPROC) (GLenum target, GLint level, GLenum internalformat, GLsizei width, GLsizei height, GLsizei depth, GLint border, GLenum format, GLenum type, const void *pixels); -typedef void (APIENTRYP PFNGLTEXSUBIMAGE3DEXTPROC) (GLenum target, GLint level, GLint xoffset, GLint yoffset, GLint zoffset, GLsizei width, GLsizei height, GLsizei depth, GLenum format, GLenum type, const void *pixels); -#ifdef GL_GLEXT_PROTOTYPES -GLAPI void APIENTRY glTexImage3DEXT (GLenum target, GLint level, GLenum internalformat, GLsizei width, GLsizei height, GLsizei depth, GLint border, GLenum format, GLenum type, const void *pixels); -GLAPI void APIENTRY glTexSubImage3DEXT (GLenum target, GLint level, GLint xoffset, GLint yoffset, GLint zoffset, GLsizei width, GLsizei height, GLsizei depth, GLenum format, GLenum type, const void *pixels); -#endif -#endif /* GL_EXT_texture3D */ - -#ifndef GL_EXT_texture_array -#define GL_EXT_texture_array 1 -#define GL_TEXTURE_1D_ARRAY_EXT 0x8C18 -#define GL_PROXY_TEXTURE_1D_ARRAY_EXT 0x8C19 -#define GL_TEXTURE_2D_ARRAY_EXT 0x8C1A -#define GL_PROXY_TEXTURE_2D_ARRAY_EXT 0x8C1B -#define GL_TEXTURE_BINDING_1D_ARRAY_EXT 0x8C1C -#define GL_TEXTURE_BINDING_2D_ARRAY_EXT 0x8C1D -#define GL_MAX_ARRAY_TEXTURE_LAYERS_EXT 0x88FF -#define GL_COMPARE_REF_DEPTH_TO_TEXTURE_EXT 0x884E -#endif /* GL_EXT_texture_array */ - -#ifndef GL_EXT_texture_buffer_object -#define GL_EXT_texture_buffer_object 1 -#define GL_TEXTURE_BUFFER_EXT 0x8C2A -#define GL_MAX_TEXTURE_BUFFER_SIZE_EXT 0x8C2B -#define GL_TEXTURE_BINDING_BUFFER_EXT 0x8C2C -#define GL_TEXTURE_BUFFER_DATA_STORE_BINDING_EXT 0x8C2D -#define GL_TEXTURE_BUFFER_FORMAT_EXT 0x8C2E -typedef void (APIENTRYP PFNGLTEXBUFFEREXTPROC) (GLenum target, GLenum internalformat, GLuint buffer); -#ifdef GL_GLEXT_PROTOTYPES -GLAPI void APIENTRY glTexBufferEXT (GLenum target, GLenum internalformat, GLuint buffer); -#endif -#endif /* GL_EXT_texture_buffer_object */ - -#ifndef GL_EXT_texture_compression_latc -#define GL_EXT_texture_compression_latc 1 -#define GL_COMPRESSED_LUMINANCE_LATC1_EXT 0x8C70 -#define GL_COMPRESSED_SIGNED_LUMINANCE_LATC1_EXT 0x8C71 -#define GL_COMPRESSED_LUMINANCE_ALPHA_LATC2_EXT 0x8C72 -#define GL_COMPRESSED_SIGNED_LUMINANCE_ALPHA_LATC2_EXT 0x8C73 -#endif /* GL_EXT_texture_compression_latc */ - -#ifndef GL_EXT_texture_compression_rgtc -#define GL_EXT_texture_compression_rgtc 1 -#define GL_COMPRESSED_RED_RGTC1_EXT 0x8DBB -#define GL_COMPRESSED_SIGNED_RED_RGTC1_EXT 0x8DBC -#define GL_COMPRESSED_RED_GREEN_RGTC2_EXT 0x8DBD -#define GL_COMPRESSED_SIGNED_RED_GREEN_RGTC2_EXT 0x8DBE -#endif /* GL_EXT_texture_compression_rgtc */ - -#ifndef GL_EXT_texture_compression_s3tc -#define GL_EXT_texture_compression_s3tc 1 -#define GL_COMPRESSED_RGB_S3TC_DXT1_EXT 0x83F0 -#define GL_COMPRESSED_RGBA_S3TC_DXT1_EXT 0x83F1 -#define GL_COMPRESSED_RGBA_S3TC_DXT3_EXT 0x83F2 -#define GL_COMPRESSED_RGBA_S3TC_DXT5_EXT 0x83F3 -#endif /* GL_EXT_texture_compression_s3tc */ - -#ifndef GL_EXT_texture_cube_map -#define GL_EXT_texture_cube_map 1 -#define GL_NORMAL_MAP_EXT 0x8511 -#define GL_REFLECTION_MAP_EXT 0x8512 -#define GL_TEXTURE_CUBE_MAP_EXT 0x8513 -#define GL_TEXTURE_BINDING_CUBE_MAP_EXT 0x8514 -#define GL_TEXTURE_CUBE_MAP_POSITIVE_X_EXT 0x8515 -#define GL_TEXTURE_CUBE_MAP_NEGATIVE_X_EXT 0x8516 -#define GL_TEXTURE_CUBE_MAP_POSITIVE_Y_EXT 0x8517 -#define GL_TEXTURE_CUBE_MAP_NEGATIVE_Y_EXT 0x8518 -#define GL_TEXTURE_CUBE_MAP_POSITIVE_Z_EXT 0x8519 -#define GL_TEXTURE_CUBE_MAP_NEGATIVE_Z_EXT 0x851A -#define GL_PROXY_TEXTURE_CUBE_MAP_EXT 0x851B -#define GL_MAX_CUBE_MAP_TEXTURE_SIZE_EXT 0x851C -#endif /* GL_EXT_texture_cube_map */ - -#ifndef GL_EXT_texture_env_add -#define GL_EXT_texture_env_add 1 -#endif /* GL_EXT_texture_env_add */ - -#ifndef GL_EXT_texture_env_combine -#define GL_EXT_texture_env_combine 1 -#define GL_COMBINE_EXT 0x8570 -#define GL_COMBINE_RGB_EXT 0x8571 -#define GL_COMBINE_ALPHA_EXT 0x8572 -#define GL_RGB_SCALE_EXT 0x8573 -#define GL_ADD_SIGNED_EXT 0x8574 -#define GL_INTERPOLATE_EXT 0x8575 -#define GL_CONSTANT_EXT 0x8576 -#define GL_PRIMARY_COLOR_EXT 0x8577 -#define GL_PREVIOUS_EXT 0x8578 -#define GL_SOURCE0_RGB_EXT 0x8580 -#define GL_SOURCE1_RGB_EXT 0x8581 -#define GL_SOURCE2_RGB_EXT 0x8582 -#define GL_SOURCE0_ALPHA_EXT 0x8588 -#define GL_SOURCE1_ALPHA_EXT 0x8589 -#define GL_SOURCE2_ALPHA_EXT 0x858A -#define GL_OPERAND0_RGB_EXT 0x8590 -#define GL_OPERAND1_RGB_EXT 0x8591 -#define GL_OPERAND2_RGB_EXT 0x8592 -#define GL_OPERAND0_ALPHA_EXT 0x8598 -#define GL_OPERAND1_ALPHA_EXT 0x8599 -#define GL_OPERAND2_ALPHA_EXT 0x859A -#endif /* GL_EXT_texture_env_combine */ - -#ifndef GL_EXT_texture_env_dot3 -#define GL_EXT_texture_env_dot3 1 -#define GL_DOT3_RGB_EXT 0x8740 -#define GL_DOT3_RGBA_EXT 0x8741 -#endif /* GL_EXT_texture_env_dot3 */ - -#ifndef GL_EXT_texture_filter_anisotropic -#define GL_EXT_texture_filter_anisotropic 1 -#define GL_TEXTURE_MAX_ANISOTROPY_EXT 0x84FE -#define GL_MAX_TEXTURE_MAX_ANISOTROPY_EXT 0x84FF -#endif /* GL_EXT_texture_filter_anisotropic */ - -#ifndef GL_EXT_texture_integer -#define GL_EXT_texture_integer 1 -#define GL_RGBA32UI_EXT 0x8D70 -#define GL_RGB32UI_EXT 0x8D71 -#define GL_ALPHA32UI_EXT 0x8D72 -#define GL_INTENSITY32UI_EXT 0x8D73 -#define GL_LUMINANCE32UI_EXT 0x8D74 -#define GL_LUMINANCE_ALPHA32UI_EXT 0x8D75 -#define GL_RGBA16UI_EXT 0x8D76 -#define GL_RGB16UI_EXT 0x8D77 -#define GL_ALPHA16UI_EXT 0x8D78 -#define GL_INTENSITY16UI_EXT 0x8D79 -#define GL_LUMINANCE16UI_EXT 0x8D7A -#define GL_LUMINANCE_ALPHA16UI_EXT 0x8D7B -#define GL_RGBA8UI_EXT 0x8D7C -#define GL_RGB8UI_EXT 0x8D7D -#define GL_ALPHA8UI_EXT 0x8D7E -#define GL_INTENSITY8UI_EXT 0x8D7F -#define GL_LUMINANCE8UI_EXT 0x8D80 -#define GL_LUMINANCE_ALPHA8UI_EXT 0x8D81 -#define GL_RGBA32I_EXT 0x8D82 -#define GL_RGB32I_EXT 0x8D83 -#define GL_ALPHA32I_EXT 0x8D84 -#define GL_INTENSITY32I_EXT 0x8D85 -#define GL_LUMINANCE32I_EXT 0x8D86 -#define GL_LUMINANCE_ALPHA32I_EXT 0x8D87 -#define GL_RGBA16I_EXT 0x8D88 -#define GL_RGB16I_EXT 0x8D89 -#define GL_ALPHA16I_EXT 0x8D8A -#define GL_INTENSITY16I_EXT 0x8D8B -#define GL_LUMINANCE16I_EXT 0x8D8C -#define GL_LUMINANCE_ALPHA16I_EXT 0x8D8D -#define GL_RGBA8I_EXT 0x8D8E -#define GL_RGB8I_EXT 0x8D8F -#define GL_ALPHA8I_EXT 0x8D90 -#define GL_INTENSITY8I_EXT 0x8D91 -#define GL_LUMINANCE8I_EXT 0x8D92 -#define GL_LUMINANCE_ALPHA8I_EXT 0x8D93 -#define GL_RED_INTEGER_EXT 0x8D94 -#define GL_GREEN_INTEGER_EXT 0x8D95 -#define GL_BLUE_INTEGER_EXT 0x8D96 -#define GL_ALPHA_INTEGER_EXT 0x8D97 -#define GL_RGB_INTEGER_EXT 0x8D98 -#define GL_RGBA_INTEGER_EXT 0x8D99 -#define GL_BGR_INTEGER_EXT 0x8D9A -#define GL_BGRA_INTEGER_EXT 0x8D9B -#define GL_LUMINANCE_INTEGER_EXT 0x8D9C -#define GL_LUMINANCE_ALPHA_INTEGER_EXT 0x8D9D -#define GL_RGBA_INTEGER_MODE_EXT 0x8D9E -typedef void (APIENTRYP PFNGLTEXPARAMETERIIVEXTPROC) (GLenum target, GLenum pname, const GLint *params); -typedef void (APIENTRYP PFNGLTEXPARAMETERIUIVEXTPROC) (GLenum target, GLenum pname, const GLuint *params); -typedef void (APIENTRYP PFNGLGETTEXPARAMETERIIVEXTPROC) (GLenum target, GLenum pname, GLint *params); -typedef void (APIENTRYP PFNGLGETTEXPARAMETERIUIVEXTPROC) (GLenum target, GLenum pname, GLuint *params); -typedef void (APIENTRYP PFNGLCLEARCOLORIIEXTPROC) (GLint red, GLint green, GLint blue, GLint alpha); -typedef void (APIENTRYP PFNGLCLEARCOLORIUIEXTPROC) (GLuint red, GLuint green, GLuint blue, GLuint alpha); -#ifdef GL_GLEXT_PROTOTYPES -GLAPI void APIENTRY glTexParameterIivEXT (GLenum target, GLenum pname, const GLint *params); -GLAPI void APIENTRY glTexParameterIuivEXT (GLenum target, GLenum pname, const GLuint *params); -GLAPI void APIENTRY glGetTexParameterIivEXT (GLenum target, GLenum pname, GLint *params); -GLAPI void APIENTRY glGetTexParameterIuivEXT (GLenum target, GLenum pname, GLuint *params); -GLAPI void APIENTRY glClearColorIiEXT (GLint red, GLint green, GLint blue, GLint alpha); -GLAPI void APIENTRY glClearColorIuiEXT (GLuint red, GLuint green, GLuint blue, GLuint alpha); -#endif -#endif /* GL_EXT_texture_integer */ - -#ifndef GL_EXT_texture_lod_bias -#define GL_EXT_texture_lod_bias 1 -#define GL_MAX_TEXTURE_LOD_BIAS_EXT 0x84FD -#define GL_TEXTURE_FILTER_CONTROL_EXT 0x8500 -#define GL_TEXTURE_LOD_BIAS_EXT 0x8501 -#endif /* GL_EXT_texture_lod_bias */ - -#ifndef GL_EXT_texture_mirror_clamp -#define GL_EXT_texture_mirror_clamp 1 -#define GL_MIRROR_CLAMP_EXT 0x8742 -#define GL_MIRROR_CLAMP_TO_EDGE_EXT 0x8743 -#define GL_MIRROR_CLAMP_TO_BORDER_EXT 0x8912 -#endif /* GL_EXT_texture_mirror_clamp */ - -#ifndef GL_EXT_texture_object -#define GL_EXT_texture_object 1 -#define GL_TEXTURE_PRIORITY_EXT 0x8066 -#define GL_TEXTURE_RESIDENT_EXT 0x8067 -#define GL_TEXTURE_1D_BINDING_EXT 0x8068 -#define GL_TEXTURE_2D_BINDING_EXT 0x8069 -#define GL_TEXTURE_3D_BINDING_EXT 0x806A -typedef GLboolean (APIENTRYP PFNGLARETEXTURESRESIDENTEXTPROC) (GLsizei n, const GLuint *textures, GLboolean *residences); -typedef void (APIENTRYP PFNGLBINDTEXTUREEXTPROC) (GLenum target, GLuint texture); -typedef void (APIENTRYP PFNGLDELETETEXTURESEXTPROC) (GLsizei n, const GLuint *textures); -typedef void (APIENTRYP PFNGLGENTEXTURESEXTPROC) (GLsizei n, GLuint *textures); -typedef GLboolean (APIENTRYP PFNGLISTEXTUREEXTPROC) (GLuint texture); -typedef void (APIENTRYP PFNGLPRIORITIZETEXTURESEXTPROC) (GLsizei n, const GLuint *textures, const GLclampf *priorities); -#ifdef GL_GLEXT_PROTOTYPES -GLAPI GLboolean APIENTRY glAreTexturesResidentEXT (GLsizei n, const GLuint *textures, GLboolean *residences); -GLAPI void APIENTRY glBindTextureEXT (GLenum target, GLuint texture); -GLAPI void APIENTRY glDeleteTexturesEXT (GLsizei n, const GLuint *textures); -GLAPI void APIENTRY glGenTexturesEXT (GLsizei n, GLuint *textures); -GLAPI GLboolean APIENTRY glIsTextureEXT (GLuint texture); -GLAPI void APIENTRY glPrioritizeTexturesEXT (GLsizei n, const GLuint *textures, const GLclampf *priorities); -#endif -#endif /* GL_EXT_texture_object */ - -#ifndef GL_EXT_texture_perturb_normal -#define GL_EXT_texture_perturb_normal 1 -#define GL_PERTURB_EXT 0x85AE -#define GL_TEXTURE_NORMAL_EXT 0x85AF -typedef void (APIENTRYP PFNGLTEXTURENORMALEXTPROC) (GLenum mode); -#ifdef GL_GLEXT_PROTOTYPES -GLAPI void APIENTRY glTextureNormalEXT (GLenum mode); -#endif -#endif /* GL_EXT_texture_perturb_normal */ - -#ifndef GL_EXT_texture_sRGB -#define GL_EXT_texture_sRGB 1 -#define GL_SRGB_EXT 0x8C40 -#define GL_SRGB8_EXT 0x8C41 -#define GL_SRGB_ALPHA_EXT 0x8C42 -#define GL_SRGB8_ALPHA8_EXT 0x8C43 -#define GL_SLUMINANCE_ALPHA_EXT 0x8C44 -#define GL_SLUMINANCE8_ALPHA8_EXT 0x8C45 -#define GL_SLUMINANCE_EXT 0x8C46 -#define GL_SLUMINANCE8_EXT 0x8C47 -#define GL_COMPRESSED_SRGB_EXT 0x8C48 -#define GL_COMPRESSED_SRGB_ALPHA_EXT 0x8C49 -#define GL_COMPRESSED_SLUMINANCE_EXT 0x8C4A -#define GL_COMPRESSED_SLUMINANCE_ALPHA_EXT 0x8C4B -#define GL_COMPRESSED_SRGB_S3TC_DXT1_EXT 0x8C4C -#define GL_COMPRESSED_SRGB_ALPHA_S3TC_DXT1_EXT 0x8C4D -#define GL_COMPRESSED_SRGB_ALPHA_S3TC_DXT3_EXT 0x8C4E -#define GL_COMPRESSED_SRGB_ALPHA_S3TC_DXT5_EXT 0x8C4F -#endif /* GL_EXT_texture_sRGB */ - -#ifndef GL_EXT_texture_sRGB_decode -#define GL_EXT_texture_sRGB_decode 1 -#define GL_TEXTURE_SRGB_DECODE_EXT 0x8A48 -#define GL_DECODE_EXT 0x8A49 -#define GL_SKIP_DECODE_EXT 0x8A4A -#endif /* GL_EXT_texture_sRGB_decode */ - -#ifndef GL_EXT_texture_shared_exponent -#define GL_EXT_texture_shared_exponent 1 -#define GL_RGB9_E5_EXT 0x8C3D -#define GL_UNSIGNED_INT_5_9_9_9_REV_EXT 0x8C3E -#define GL_TEXTURE_SHARED_SIZE_EXT 0x8C3F -#endif /* GL_EXT_texture_shared_exponent */ - -#ifndef GL_EXT_texture_snorm -#define GL_EXT_texture_snorm 1 -#define GL_ALPHA_SNORM 0x9010 -#define GL_LUMINANCE_SNORM 0x9011 -#define GL_LUMINANCE_ALPHA_SNORM 0x9012 -#define GL_INTENSITY_SNORM 0x9013 -#define GL_ALPHA8_SNORM 0x9014 -#define GL_LUMINANCE8_SNORM 0x9015 -#define GL_LUMINANCE8_ALPHA8_SNORM 0x9016 -#define GL_INTENSITY8_SNORM 0x9017 -#define GL_ALPHA16_SNORM 0x9018 -#define GL_LUMINANCE16_SNORM 0x9019 -#define GL_LUMINANCE16_ALPHA16_SNORM 0x901A -#define GL_INTENSITY16_SNORM 0x901B -#define GL_RED_SNORM 0x8F90 -#define GL_RG_SNORM 0x8F91 -#define GL_RGB_SNORM 0x8F92 -#define GL_RGBA_SNORM 0x8F93 -#endif /* GL_EXT_texture_snorm */ - -#ifndef GL_EXT_texture_swizzle -#define GL_EXT_texture_swizzle 1 -#define GL_TEXTURE_SWIZZLE_R_EXT 0x8E42 -#define GL_TEXTURE_SWIZZLE_G_EXT 0x8E43 -#define GL_TEXTURE_SWIZZLE_B_EXT 0x8E44 -#define GL_TEXTURE_SWIZZLE_A_EXT 0x8E45 -#define GL_TEXTURE_SWIZZLE_RGBA_EXT 0x8E46 -#endif /* GL_EXT_texture_swizzle */ - -#ifndef GL_EXT_timer_query -#define GL_EXT_timer_query 1 -#define GL_TIME_ELAPSED_EXT 0x88BF -typedef void (APIENTRYP PFNGLGETQUERYOBJECTI64VEXTPROC) (GLuint id, GLenum pname, GLint64 *params); -typedef void (APIENTRYP PFNGLGETQUERYOBJECTUI64VEXTPROC) (GLuint id, GLenum pname, GLuint64 *params); -#ifdef GL_GLEXT_PROTOTYPES -GLAPI void APIENTRY glGetQueryObjecti64vEXT (GLuint id, GLenum pname, GLint64 *params); -GLAPI void APIENTRY glGetQueryObjectui64vEXT (GLuint id, GLenum pname, GLuint64 *params); -#endif -#endif /* GL_EXT_timer_query */ - -#ifndef GL_EXT_transform_feedback -#define GL_EXT_transform_feedback 1 -#define GL_TRANSFORM_FEEDBACK_BUFFER_EXT 0x8C8E -#define GL_TRANSFORM_FEEDBACK_BUFFER_START_EXT 0x8C84 -#define GL_TRANSFORM_FEEDBACK_BUFFER_SIZE_EXT 0x8C85 -#define GL_TRANSFORM_FEEDBACK_BUFFER_BINDING_EXT 0x8C8F -#define GL_INTERLEAVED_ATTRIBS_EXT 0x8C8C -#define GL_SEPARATE_ATTRIBS_EXT 0x8C8D -#define GL_PRIMITIVES_GENERATED_EXT 0x8C87 -#define GL_TRANSFORM_FEEDBACK_PRIMITIVES_WRITTEN_EXT 0x8C88 -#define GL_RASTERIZER_DISCARD_EXT 0x8C89 -#define GL_MAX_TRANSFORM_FEEDBACK_INTERLEAVED_COMPONENTS_EXT 0x8C8A -#define GL_MAX_TRANSFORM_FEEDBACK_SEPARATE_ATTRIBS_EXT 0x8C8B -#define GL_MAX_TRANSFORM_FEEDBACK_SEPARATE_COMPONENTS_EXT 0x8C80 -#define GL_TRANSFORM_FEEDBACK_VARYINGS_EXT 0x8C83 -#define GL_TRANSFORM_FEEDBACK_BUFFER_MODE_EXT 0x8C7F -#define GL_TRANSFORM_FEEDBACK_VARYING_MAX_LENGTH_EXT 0x8C76 -typedef void (APIENTRYP PFNGLBEGINTRANSFORMFEEDBACKEXTPROC) (GLenum primitiveMode); -typedef void (APIENTRYP PFNGLENDTRANSFORMFEEDBACKEXTPROC) (void); -typedef void (APIENTRYP PFNGLBINDBUFFERRANGEEXTPROC) (GLenum target, GLuint index, GLuint buffer, GLintptr offset, GLsizeiptr size); -typedef void (APIENTRYP PFNGLBINDBUFFEROFFSETEXTPROC) (GLenum target, GLuint index, GLuint buffer, GLintptr offset); -typedef void (APIENTRYP PFNGLBINDBUFFERBASEEXTPROC) (GLenum target, GLuint index, GLuint buffer); -typedef void (APIENTRYP PFNGLTRANSFORMFEEDBACKVARYINGSEXTPROC) (GLuint program, GLsizei count, const GLchar *const*varyings, GLenum bufferMode); -typedef void (APIENTRYP PFNGLGETTRANSFORMFEEDBACKVARYINGEXTPROC) (GLuint program, GLuint index, GLsizei bufSize, GLsizei *length, GLsizei *size, GLenum *type, GLchar *name); -#ifdef GL_GLEXT_PROTOTYPES -GLAPI void APIENTRY glBeginTransformFeedbackEXT (GLenum primitiveMode); -GLAPI void APIENTRY glEndTransformFeedbackEXT (void); -GLAPI void APIENTRY glBindBufferRangeEXT (GLenum target, GLuint index, GLuint buffer, GLintptr offset, GLsizeiptr size); -GLAPI void APIENTRY glBindBufferOffsetEXT (GLenum target, GLuint index, GLuint buffer, GLintptr offset); -GLAPI void APIENTRY glBindBufferBaseEXT (GLenum target, GLuint index, GLuint buffer); -GLAPI void APIENTRY glTransformFeedbackVaryingsEXT (GLuint program, GLsizei count, const GLchar *const*varyings, GLenum bufferMode); -GLAPI void APIENTRY glGetTransformFeedbackVaryingEXT (GLuint program, GLuint index, GLsizei bufSize, GLsizei *length, GLsizei *size, GLenum *type, GLchar *name); -#endif -#endif /* GL_EXT_transform_feedback */ - -#ifndef GL_EXT_vertex_array -#define GL_EXT_vertex_array 1 -#define GL_VERTEX_ARRAY_EXT 0x8074 -#define GL_NORMAL_ARRAY_EXT 0x8075 -#define GL_COLOR_ARRAY_EXT 0x8076 -#define GL_INDEX_ARRAY_EXT 0x8077 -#define GL_TEXTURE_COORD_ARRAY_EXT 0x8078 -#define GL_EDGE_FLAG_ARRAY_EXT 0x8079 -#define GL_VERTEX_ARRAY_SIZE_EXT 0x807A -#define GL_VERTEX_ARRAY_TYPE_EXT 0x807B -#define GL_VERTEX_ARRAY_STRIDE_EXT 0x807C -#define GL_VERTEX_ARRAY_COUNT_EXT 0x807D -#define GL_NORMAL_ARRAY_TYPE_EXT 0x807E -#define GL_NORMAL_ARRAY_STRIDE_EXT 0x807F -#define GL_NORMAL_ARRAY_COUNT_EXT 0x8080 -#define GL_COLOR_ARRAY_SIZE_EXT 0x8081 -#define GL_COLOR_ARRAY_TYPE_EXT 0x8082 -#define GL_COLOR_ARRAY_STRIDE_EXT 0x8083 -#define GL_COLOR_ARRAY_COUNT_EXT 0x8084 -#define GL_INDEX_ARRAY_TYPE_EXT 0x8085 -#define GL_INDEX_ARRAY_STRIDE_EXT 0x8086 -#define GL_INDEX_ARRAY_COUNT_EXT 0x8087 -#define GL_TEXTURE_COORD_ARRAY_SIZE_EXT 0x8088 -#define GL_TEXTURE_COORD_ARRAY_TYPE_EXT 0x8089 -#define GL_TEXTURE_COORD_ARRAY_STRIDE_EXT 0x808A -#define GL_TEXTURE_COORD_ARRAY_COUNT_EXT 0x808B -#define GL_EDGE_FLAG_ARRAY_STRIDE_EXT 0x808C -#define GL_EDGE_FLAG_ARRAY_COUNT_EXT 0x808D -#define GL_VERTEX_ARRAY_POINTER_EXT 0x808E -#define GL_NORMAL_ARRAY_POINTER_EXT 0x808F -#define GL_COLOR_ARRAY_POINTER_EXT 0x8090 -#define GL_INDEX_ARRAY_POINTER_EXT 0x8091 -#define GL_TEXTURE_COORD_ARRAY_POINTER_EXT 0x8092 -#define GL_EDGE_FLAG_ARRAY_POINTER_EXT 0x8093 -typedef void (APIENTRYP PFNGLARRAYELEMENTEXTPROC) (GLint i); -typedef void (APIENTRYP PFNGLCOLORPOINTEREXTPROC) (GLint size, GLenum type, GLsizei stride, GLsizei count, const void *pointer); -typedef void (APIENTRYP PFNGLDRAWARRAYSEXTPROC) (GLenum mode, GLint first, GLsizei count); -typedef void (APIENTRYP PFNGLEDGEFLAGPOINTEREXTPROC) (GLsizei stride, GLsizei count, const GLboolean *pointer); -typedef void (APIENTRYP PFNGLGETPOINTERVEXTPROC) (GLenum pname, void **params); -typedef void (APIENTRYP PFNGLINDEXPOINTEREXTPROC) (GLenum type, GLsizei stride, GLsizei count, const void *pointer); -typedef void (APIENTRYP PFNGLNORMALPOINTEREXTPROC) (GLenum type, GLsizei stride, GLsizei count, const void *pointer); -typedef void (APIENTRYP PFNGLTEXCOORDPOINTEREXTPROC) (GLint size, GLenum type, GLsizei stride, GLsizei count, const void *pointer); -typedef void (APIENTRYP PFNGLVERTEXPOINTEREXTPROC) (GLint size, GLenum type, GLsizei stride, GLsizei count, const void *pointer); -#ifdef GL_GLEXT_PROTOTYPES -GLAPI void APIENTRY glArrayElementEXT (GLint i); -GLAPI void APIENTRY glColorPointerEXT (GLint size, GLenum type, GLsizei stride, GLsizei count, const void *pointer); -GLAPI void APIENTRY glDrawArraysEXT (GLenum mode, GLint first, GLsizei count); -GLAPI void APIENTRY glEdgeFlagPointerEXT (GLsizei stride, GLsizei count, const GLboolean *pointer); -GLAPI void APIENTRY glGetPointervEXT (GLenum pname, void **params); -GLAPI void APIENTRY glIndexPointerEXT (GLenum type, GLsizei stride, GLsizei count, const void *pointer); -GLAPI void APIENTRY glNormalPointerEXT (GLenum type, GLsizei stride, GLsizei count, const void *pointer); -GLAPI void APIENTRY glTexCoordPointerEXT (GLint size, GLenum type, GLsizei stride, GLsizei count, const void *pointer); -GLAPI void APIENTRY glVertexPointerEXT (GLint size, GLenum type, GLsizei stride, GLsizei count, const void *pointer); -#endif -#endif /* GL_EXT_vertex_array */ - -#ifndef GL_EXT_vertex_array_bgra -#define GL_EXT_vertex_array_bgra 1 -#endif /* GL_EXT_vertex_array_bgra */ - -#ifndef GL_EXT_vertex_attrib_64bit -#define GL_EXT_vertex_attrib_64bit 1 -#define GL_DOUBLE_VEC2_EXT 0x8FFC -#define GL_DOUBLE_VEC3_EXT 0x8FFD -#define GL_DOUBLE_VEC4_EXT 0x8FFE -#define GL_DOUBLE_MAT2_EXT 0x8F46 -#define GL_DOUBLE_MAT3_EXT 0x8F47 -#define GL_DOUBLE_MAT4_EXT 0x8F48 -#define GL_DOUBLE_MAT2x3_EXT 0x8F49 -#define GL_DOUBLE_MAT2x4_EXT 0x8F4A -#define GL_DOUBLE_MAT3x2_EXT 0x8F4B -#define GL_DOUBLE_MAT3x4_EXT 0x8F4C -#define GL_DOUBLE_MAT4x2_EXT 0x8F4D -#define GL_DOUBLE_MAT4x3_EXT 0x8F4E -typedef void (APIENTRYP PFNGLVERTEXATTRIBL1DEXTPROC) (GLuint index, GLdouble x); -typedef void (APIENTRYP PFNGLVERTEXATTRIBL2DEXTPROC) (GLuint index, GLdouble x, GLdouble y); -typedef void (APIENTRYP PFNGLVERTEXATTRIBL3DEXTPROC) (GLuint index, GLdouble x, GLdouble y, GLdouble z); -typedef void (APIENTRYP PFNGLVERTEXATTRIBL4DEXTPROC) (GLuint index, GLdouble x, GLdouble y, GLdouble z, GLdouble w); -typedef void (APIENTRYP PFNGLVERTEXATTRIBL1DVEXTPROC) (GLuint index, const GLdouble *v); -typedef void (APIENTRYP PFNGLVERTEXATTRIBL2DVEXTPROC) (GLuint index, const GLdouble *v); -typedef void (APIENTRYP PFNGLVERTEXATTRIBL3DVEXTPROC) (GLuint index, const GLdouble *v); -typedef void (APIENTRYP PFNGLVERTEXATTRIBL4DVEXTPROC) (GLuint index, const GLdouble *v); -typedef void (APIENTRYP PFNGLVERTEXATTRIBLPOINTEREXTPROC) (GLuint index, GLint size, GLenum type, GLsizei stride, const void *pointer); -typedef void (APIENTRYP PFNGLGETVERTEXATTRIBLDVEXTPROC) (GLuint index, GLenum pname, GLdouble *params); -#ifdef GL_GLEXT_PROTOTYPES -GLAPI void APIENTRY glVertexAttribL1dEXT (GLuint index, GLdouble x); -GLAPI void APIENTRY glVertexAttribL2dEXT (GLuint index, GLdouble x, GLdouble y); -GLAPI void APIENTRY glVertexAttribL3dEXT (GLuint index, GLdouble x, GLdouble y, GLdouble z); -GLAPI void APIENTRY glVertexAttribL4dEXT (GLuint index, GLdouble x, GLdouble y, GLdouble z, GLdouble w); -GLAPI void APIENTRY glVertexAttribL1dvEXT (GLuint index, const GLdouble *v); -GLAPI void APIENTRY glVertexAttribL2dvEXT (GLuint index, const GLdouble *v); -GLAPI void APIENTRY glVertexAttribL3dvEXT (GLuint index, const GLdouble *v); -GLAPI void APIENTRY glVertexAttribL4dvEXT (GLuint index, const GLdouble *v); -GLAPI void APIENTRY glVertexAttribLPointerEXT (GLuint index, GLint size, GLenum type, GLsizei stride, const void *pointer); -GLAPI void APIENTRY glGetVertexAttribLdvEXT (GLuint index, GLenum pname, GLdouble *params); -#endif -#endif /* GL_EXT_vertex_attrib_64bit */ - -#ifndef GL_EXT_vertex_shader -#define GL_EXT_vertex_shader 1 -#define GL_VERTEX_SHADER_EXT 0x8780 -#define GL_VERTEX_SHADER_BINDING_EXT 0x8781 -#define GL_OP_INDEX_EXT 0x8782 -#define GL_OP_NEGATE_EXT 0x8783 -#define GL_OP_DOT3_EXT 0x8784 -#define GL_OP_DOT4_EXT 0x8785 -#define GL_OP_MUL_EXT 0x8786 -#define GL_OP_ADD_EXT 0x8787 -#define GL_OP_MADD_EXT 0x8788 -#define GL_OP_FRAC_EXT 0x8789 -#define GL_OP_MAX_EXT 0x878A -#define GL_OP_MIN_EXT 0x878B -#define GL_OP_SET_GE_EXT 0x878C -#define GL_OP_SET_LT_EXT 0x878D -#define GL_OP_CLAMP_EXT 0x878E -#define GL_OP_FLOOR_EXT 0x878F -#define GL_OP_ROUND_EXT 0x8790 -#define GL_OP_EXP_BASE_2_EXT 0x8791 -#define GL_OP_LOG_BASE_2_EXT 0x8792 -#define GL_OP_POWER_EXT 0x8793 -#define GL_OP_RECIP_EXT 0x8794 -#define GL_OP_RECIP_SQRT_EXT 0x8795 -#define GL_OP_SUB_EXT 0x8796 -#define GL_OP_CROSS_PRODUCT_EXT 0x8797 -#define GL_OP_MULTIPLY_MATRIX_EXT 0x8798 -#define GL_OP_MOV_EXT 0x8799 -#define GL_OUTPUT_VERTEX_EXT 0x879A -#define GL_OUTPUT_COLOR0_EXT 0x879B -#define GL_OUTPUT_COLOR1_EXT 0x879C -#define GL_OUTPUT_TEXTURE_COORD0_EXT 0x879D -#define GL_OUTPUT_TEXTURE_COORD1_EXT 0x879E -#define GL_OUTPUT_TEXTURE_COORD2_EXT 0x879F -#define GL_OUTPUT_TEXTURE_COORD3_EXT 0x87A0 -#define GL_OUTPUT_TEXTURE_COORD4_EXT 0x87A1 -#define GL_OUTPUT_TEXTURE_COORD5_EXT 0x87A2 -#define GL_OUTPUT_TEXTURE_COORD6_EXT 0x87A3 -#define GL_OUTPUT_TEXTURE_COORD7_EXT 0x87A4 -#define GL_OUTPUT_TEXTURE_COORD8_EXT 0x87A5 -#define GL_OUTPUT_TEXTURE_COORD9_EXT 0x87A6 -#define GL_OUTPUT_TEXTURE_COORD10_EXT 0x87A7 -#define GL_OUTPUT_TEXTURE_COORD11_EXT 0x87A8 -#define GL_OUTPUT_TEXTURE_COORD12_EXT 0x87A9 -#define GL_OUTPUT_TEXTURE_COORD13_EXT 0x87AA -#define GL_OUTPUT_TEXTURE_COORD14_EXT 0x87AB -#define GL_OUTPUT_TEXTURE_COORD15_EXT 0x87AC -#define GL_OUTPUT_TEXTURE_COORD16_EXT 0x87AD -#define GL_OUTPUT_TEXTURE_COORD17_EXT 0x87AE -#define GL_OUTPUT_TEXTURE_COORD18_EXT 0x87AF -#define GL_OUTPUT_TEXTURE_COORD19_EXT 0x87B0 -#define GL_OUTPUT_TEXTURE_COORD20_EXT 0x87B1 -#define GL_OUTPUT_TEXTURE_COORD21_EXT 0x87B2 -#define GL_OUTPUT_TEXTURE_COORD22_EXT 0x87B3 -#define GL_OUTPUT_TEXTURE_COORD23_EXT 0x87B4 -#define GL_OUTPUT_TEXTURE_COORD24_EXT 0x87B5 -#define GL_OUTPUT_TEXTURE_COORD25_EXT 0x87B6 -#define GL_OUTPUT_TEXTURE_COORD26_EXT 0x87B7 -#define GL_OUTPUT_TEXTURE_COORD27_EXT 0x87B8 -#define GL_OUTPUT_TEXTURE_COORD28_EXT 0x87B9 -#define GL_OUTPUT_TEXTURE_COORD29_EXT 0x87BA -#define GL_OUTPUT_TEXTURE_COORD30_EXT 0x87BB -#define GL_OUTPUT_TEXTURE_COORD31_EXT 0x87BC -#define GL_OUTPUT_FOG_EXT 0x87BD -#define GL_SCALAR_EXT 0x87BE -#define GL_VECTOR_EXT 0x87BF -#define GL_MATRIX_EXT 0x87C0 -#define GL_VARIANT_EXT 0x87C1 -#define GL_INVARIANT_EXT 0x87C2 -#define GL_LOCAL_CONSTANT_EXT 0x87C3 -#define GL_LOCAL_EXT 0x87C4 -#define GL_MAX_VERTEX_SHADER_INSTRUCTIONS_EXT 0x87C5 -#define GL_MAX_VERTEX_SHADER_VARIANTS_EXT 0x87C6 -#define GL_MAX_VERTEX_SHADER_INVARIANTS_EXT 0x87C7 -#define GL_MAX_VERTEX_SHADER_LOCAL_CONSTANTS_EXT 0x87C8 -#define GL_MAX_VERTEX_SHADER_LOCALS_EXT 0x87C9 -#define GL_MAX_OPTIMIZED_VERTEX_SHADER_INSTRUCTIONS_EXT 0x87CA -#define GL_MAX_OPTIMIZED_VERTEX_SHADER_VARIANTS_EXT 0x87CB -#define GL_MAX_OPTIMIZED_VERTEX_SHADER_LOCAL_CONSTANTS_EXT 0x87CC -#define GL_MAX_OPTIMIZED_VERTEX_SHADER_INVARIANTS_EXT 0x87CD -#define GL_MAX_OPTIMIZED_VERTEX_SHADER_LOCALS_EXT 0x87CE -#define GL_VERTEX_SHADER_INSTRUCTIONS_EXT 0x87CF -#define GL_VERTEX_SHADER_VARIANTS_EXT 0x87D0 -#define GL_VERTEX_SHADER_INVARIANTS_EXT 0x87D1 -#define GL_VERTEX_SHADER_LOCAL_CONSTANTS_EXT 0x87D2 -#define GL_VERTEX_SHADER_LOCALS_EXT 0x87D3 -#define GL_VERTEX_SHADER_OPTIMIZED_EXT 0x87D4 -#define GL_X_EXT 0x87D5 -#define GL_Y_EXT 0x87D6 -#define GL_Z_EXT 0x87D7 -#define GL_W_EXT 0x87D8 -#define GL_NEGATIVE_X_EXT 0x87D9 -#define GL_NEGATIVE_Y_EXT 0x87DA -#define GL_NEGATIVE_Z_EXT 0x87DB -#define GL_NEGATIVE_W_EXT 0x87DC -#define GL_ZERO_EXT 0x87DD -#define GL_ONE_EXT 0x87DE -#define GL_NEGATIVE_ONE_EXT 0x87DF -#define GL_NORMALIZED_RANGE_EXT 0x87E0 -#define GL_FULL_RANGE_EXT 0x87E1 -#define GL_CURRENT_VERTEX_EXT 0x87E2 -#define GL_MVP_MATRIX_EXT 0x87E3 -#define GL_VARIANT_VALUE_EXT 0x87E4 -#define GL_VARIANT_DATATYPE_EXT 0x87E5 -#define GL_VARIANT_ARRAY_STRIDE_EXT 0x87E6 -#define GL_VARIANT_ARRAY_TYPE_EXT 0x87E7 -#define GL_VARIANT_ARRAY_EXT 0x87E8 -#define GL_VARIANT_ARRAY_POINTER_EXT 0x87E9 -#define GL_INVARIANT_VALUE_EXT 0x87EA -#define GL_INVARIANT_DATATYPE_EXT 0x87EB -#define GL_LOCAL_CONSTANT_VALUE_EXT 0x87EC -#define GL_LOCAL_CONSTANT_DATATYPE_EXT 0x87ED -typedef void (APIENTRYP PFNGLBEGINVERTEXSHADEREXTPROC) (void); -typedef void (APIENTRYP PFNGLENDVERTEXSHADEREXTPROC) (void); -typedef void (APIENTRYP PFNGLBINDVERTEXSHADEREXTPROC) (GLuint id); -typedef GLuint (APIENTRYP PFNGLGENVERTEXSHADERSEXTPROC) (GLuint range); -typedef void (APIENTRYP PFNGLDELETEVERTEXSHADEREXTPROC) (GLuint id); -typedef void (APIENTRYP PFNGLSHADEROP1EXTPROC) (GLenum op, GLuint res, GLuint arg1); -typedef void (APIENTRYP PFNGLSHADEROP2EXTPROC) (GLenum op, GLuint res, GLuint arg1, GLuint arg2); -typedef void (APIENTRYP PFNGLSHADEROP3EXTPROC) (GLenum op, GLuint res, GLuint arg1, GLuint arg2, GLuint arg3); -typedef void (APIENTRYP PFNGLSWIZZLEEXTPROC) (GLuint res, GLuint in, GLenum outX, GLenum outY, GLenum outZ, GLenum outW); -typedef void (APIENTRYP PFNGLWRITEMASKEXTPROC) (GLuint res, GLuint in, GLenum outX, GLenum outY, GLenum outZ, GLenum outW); -typedef void (APIENTRYP PFNGLINSERTCOMPONENTEXTPROC) (GLuint res, GLuint src, GLuint num); -typedef void (APIENTRYP PFNGLEXTRACTCOMPONENTEXTPROC) (GLuint res, GLuint src, GLuint num); -typedef GLuint (APIENTRYP PFNGLGENSYMBOLSEXTPROC) (GLenum datatype, GLenum storagetype, GLenum range, GLuint components); -typedef void (APIENTRYP PFNGLSETINVARIANTEXTPROC) (GLuint id, GLenum type, const void *addr); -typedef void (APIENTRYP PFNGLSETLOCALCONSTANTEXTPROC) (GLuint id, GLenum type, const void *addr); -typedef void (APIENTRYP PFNGLVARIANTBVEXTPROC) (GLuint id, const GLbyte *addr); -typedef void (APIENTRYP PFNGLVARIANTSVEXTPROC) (GLuint id, const GLshort *addr); -typedef void (APIENTRYP PFNGLVARIANTIVEXTPROC) (GLuint id, const GLint *addr); -typedef void (APIENTRYP PFNGLVARIANTFVEXTPROC) (GLuint id, const GLfloat *addr); -typedef void (APIENTRYP PFNGLVARIANTDVEXTPROC) (GLuint id, const GLdouble *addr); -typedef void (APIENTRYP PFNGLVARIANTUBVEXTPROC) (GLuint id, const GLubyte *addr); -typedef void (APIENTRYP PFNGLVARIANTUSVEXTPROC) (GLuint id, const GLushort *addr); -typedef void (APIENTRYP PFNGLVARIANTUIVEXTPROC) (GLuint id, const GLuint *addr); -typedef void (APIENTRYP PFNGLVARIANTPOINTEREXTPROC) (GLuint id, GLenum type, GLuint stride, const void *addr); -typedef void (APIENTRYP PFNGLENABLEVARIANTCLIENTSTATEEXTPROC) (GLuint id); -typedef void (APIENTRYP PFNGLDISABLEVARIANTCLIENTSTATEEXTPROC) (GLuint id); -typedef GLuint (APIENTRYP PFNGLBINDLIGHTPARAMETEREXTPROC) (GLenum light, GLenum value); -typedef GLuint (APIENTRYP PFNGLBINDMATERIALPARAMETEREXTPROC) (GLenum face, GLenum value); -typedef GLuint (APIENTRYP PFNGLBINDTEXGENPARAMETEREXTPROC) (GLenum unit, GLenum coord, GLenum value); -typedef GLuint (APIENTRYP PFNGLBINDTEXTUREUNITPARAMETEREXTPROC) (GLenum unit, GLenum value); -typedef GLuint (APIENTRYP PFNGLBINDPARAMETEREXTPROC) (GLenum value); -typedef GLboolean (APIENTRYP PFNGLISVARIANTENABLEDEXTPROC) (GLuint id, GLenum cap); -typedef void (APIENTRYP PFNGLGETVARIANTBOOLEANVEXTPROC) (GLuint id, GLenum value, GLboolean *data); -typedef void (APIENTRYP PFNGLGETVARIANTINTEGERVEXTPROC) (GLuint id, GLenum value, GLint *data); -typedef void (APIENTRYP PFNGLGETVARIANTFLOATVEXTPROC) (GLuint id, GLenum value, GLfloat *data); -typedef void (APIENTRYP PFNGLGETVARIANTPOINTERVEXTPROC) (GLuint id, GLenum value, void **data); -typedef void (APIENTRYP PFNGLGETINVARIANTBOOLEANVEXTPROC) (GLuint id, GLenum value, GLboolean *data); -typedef void (APIENTRYP PFNGLGETINVARIANTINTEGERVEXTPROC) (GLuint id, GLenum value, GLint *data); -typedef void (APIENTRYP PFNGLGETINVARIANTFLOATVEXTPROC) (GLuint id, GLenum value, GLfloat *data); -typedef void (APIENTRYP PFNGLGETLOCALCONSTANTBOOLEANVEXTPROC) (GLuint id, GLenum value, GLboolean *data); -typedef void (APIENTRYP PFNGLGETLOCALCONSTANTINTEGERVEXTPROC) (GLuint id, GLenum value, GLint *data); -typedef void (APIENTRYP PFNGLGETLOCALCONSTANTFLOATVEXTPROC) (GLuint id, GLenum value, GLfloat *data); -#ifdef GL_GLEXT_PROTOTYPES -GLAPI void APIENTRY glBeginVertexShaderEXT (void); -GLAPI void APIENTRY glEndVertexShaderEXT (void); -GLAPI void APIENTRY glBindVertexShaderEXT (GLuint id); -GLAPI GLuint APIENTRY glGenVertexShadersEXT (GLuint range); -GLAPI void APIENTRY glDeleteVertexShaderEXT (GLuint id); -GLAPI void APIENTRY glShaderOp1EXT (GLenum op, GLuint res, GLuint arg1); -GLAPI void APIENTRY glShaderOp2EXT (GLenum op, GLuint res, GLuint arg1, GLuint arg2); -GLAPI void APIENTRY glShaderOp3EXT (GLenum op, GLuint res, GLuint arg1, GLuint arg2, GLuint arg3); -GLAPI void APIENTRY glSwizzleEXT (GLuint res, GLuint in, GLenum outX, GLenum outY, GLenum outZ, GLenum outW); -GLAPI void APIENTRY glWriteMaskEXT (GLuint res, GLuint in, GLenum outX, GLenum outY, GLenum outZ, GLenum outW); -GLAPI void APIENTRY glInsertComponentEXT (GLuint res, GLuint src, GLuint num); -GLAPI void APIENTRY glExtractComponentEXT (GLuint res, GLuint src, GLuint num); -GLAPI GLuint APIENTRY glGenSymbolsEXT (GLenum datatype, GLenum storagetype, GLenum range, GLuint components); -GLAPI void APIENTRY glSetInvariantEXT (GLuint id, GLenum type, const void *addr); -GLAPI void APIENTRY glSetLocalConstantEXT (GLuint id, GLenum type, const void *addr); -GLAPI void APIENTRY glVariantbvEXT (GLuint id, const GLbyte *addr); -GLAPI void APIENTRY glVariantsvEXT (GLuint id, const GLshort *addr); -GLAPI void APIENTRY glVariantivEXT (GLuint id, const GLint *addr); -GLAPI void APIENTRY glVariantfvEXT (GLuint id, const GLfloat *addr); -GLAPI void APIENTRY glVariantdvEXT (GLuint id, const GLdouble *addr); -GLAPI void APIENTRY glVariantubvEXT (GLuint id, const GLubyte *addr); -GLAPI void APIENTRY glVariantusvEXT (GLuint id, const GLushort *addr); -GLAPI void APIENTRY glVariantuivEXT (GLuint id, const GLuint *addr); -GLAPI void APIENTRY glVariantPointerEXT (GLuint id, GLenum type, GLuint stride, const void *addr); -GLAPI void APIENTRY glEnableVariantClientStateEXT (GLuint id); -GLAPI void APIENTRY glDisableVariantClientStateEXT (GLuint id); -GLAPI GLuint APIENTRY glBindLightParameterEXT (GLenum light, GLenum value); -GLAPI GLuint APIENTRY glBindMaterialParameterEXT (GLenum face, GLenum value); -GLAPI GLuint APIENTRY glBindTexGenParameterEXT (GLenum unit, GLenum coord, GLenum value); -GLAPI GLuint APIENTRY glBindTextureUnitParameterEXT (GLenum unit, GLenum value); -GLAPI GLuint APIENTRY glBindParameterEXT (GLenum value); -GLAPI GLboolean APIENTRY glIsVariantEnabledEXT (GLuint id, GLenum cap); -GLAPI void APIENTRY glGetVariantBooleanvEXT (GLuint id, GLenum value, GLboolean *data); -GLAPI void APIENTRY glGetVariantIntegervEXT (GLuint id, GLenum value, GLint *data); -GLAPI void APIENTRY glGetVariantFloatvEXT (GLuint id, GLenum value, GLfloat *data); -GLAPI void APIENTRY glGetVariantPointervEXT (GLuint id, GLenum value, void **data); -GLAPI void APIENTRY glGetInvariantBooleanvEXT (GLuint id, GLenum value, GLboolean *data); -GLAPI void APIENTRY glGetInvariantIntegervEXT (GLuint id, GLenum value, GLint *data); -GLAPI void APIENTRY glGetInvariantFloatvEXT (GLuint id, GLenum value, GLfloat *data); -GLAPI void APIENTRY glGetLocalConstantBooleanvEXT (GLuint id, GLenum value, GLboolean *data); -GLAPI void APIENTRY glGetLocalConstantIntegervEXT (GLuint id, GLenum value, GLint *data); -GLAPI void APIENTRY glGetLocalConstantFloatvEXT (GLuint id, GLenum value, GLfloat *data); -#endif -#endif /* GL_EXT_vertex_shader */ - -#ifndef GL_EXT_vertex_weighting -#define GL_EXT_vertex_weighting 1 -#define GL_MODELVIEW0_STACK_DEPTH_EXT 0x0BA3 -#define GL_MODELVIEW1_STACK_DEPTH_EXT 0x8502 -#define GL_MODELVIEW0_MATRIX_EXT 0x0BA6 -#define GL_MODELVIEW1_MATRIX_EXT 0x8506 -#define GL_VERTEX_WEIGHTING_EXT 0x8509 -#define GL_MODELVIEW0_EXT 0x1700 -#define GL_MODELVIEW1_EXT 0x850A -#define GL_CURRENT_VERTEX_WEIGHT_EXT 0x850B -#define GL_VERTEX_WEIGHT_ARRAY_EXT 0x850C -#define GL_VERTEX_WEIGHT_ARRAY_SIZE_EXT 0x850D -#define GL_VERTEX_WEIGHT_ARRAY_TYPE_EXT 0x850E -#define GL_VERTEX_WEIGHT_ARRAY_STRIDE_EXT 0x850F -#define GL_VERTEX_WEIGHT_ARRAY_POINTER_EXT 0x8510 -typedef void (APIENTRYP PFNGLVERTEXWEIGHTFEXTPROC) (GLfloat weight); -typedef void (APIENTRYP PFNGLVERTEXWEIGHTFVEXTPROC) (const GLfloat *weight); -typedef void (APIENTRYP PFNGLVERTEXWEIGHTPOINTEREXTPROC) (GLint size, GLenum type, GLsizei stride, const void *pointer); -#ifdef GL_GLEXT_PROTOTYPES -GLAPI void APIENTRY glVertexWeightfEXT (GLfloat weight); -GLAPI void APIENTRY glVertexWeightfvEXT (const GLfloat *weight); -GLAPI void APIENTRY glVertexWeightPointerEXT (GLint size, GLenum type, GLsizei stride, const void *pointer); -#endif -#endif /* GL_EXT_vertex_weighting */ - -#ifndef GL_EXT_x11_sync_object -#define GL_EXT_x11_sync_object 1 -#define GL_SYNC_X11_FENCE_EXT 0x90E1 -typedef GLsync (APIENTRYP PFNGLIMPORTSYNCEXTPROC) (GLenum external_sync_type, GLintptr external_sync, GLbitfield flags); -#ifdef GL_GLEXT_PROTOTYPES -GLAPI GLsync APIENTRY glImportSyncEXT (GLenum external_sync_type, GLintptr external_sync, GLbitfield flags); -#endif -#endif /* GL_EXT_x11_sync_object */ - -#ifndef GL_GREMEDY_frame_terminator -#define GL_GREMEDY_frame_terminator 1 -typedef void (APIENTRYP PFNGLFRAMETERMINATORGREMEDYPROC) (void); -#ifdef GL_GLEXT_PROTOTYPES -GLAPI void APIENTRY glFrameTerminatorGREMEDY (void); -#endif -#endif /* GL_GREMEDY_frame_terminator */ - -#ifndef GL_GREMEDY_string_marker -#define GL_GREMEDY_string_marker 1 -typedef void (APIENTRYP PFNGLSTRINGMARKERGREMEDYPROC) (GLsizei len, const void *string); -#ifdef GL_GLEXT_PROTOTYPES -GLAPI void APIENTRY glStringMarkerGREMEDY (GLsizei len, const void *string); -#endif -#endif /* GL_GREMEDY_string_marker */ - -#ifndef GL_HP_convolution_border_modes -#define GL_HP_convolution_border_modes 1 -#define GL_IGNORE_BORDER_HP 0x8150 -#define GL_CONSTANT_BORDER_HP 0x8151 -#define GL_REPLICATE_BORDER_HP 0x8153 -#define GL_CONVOLUTION_BORDER_COLOR_HP 0x8154 -#endif /* GL_HP_convolution_border_modes */ - -#ifndef GL_HP_image_transform -#define GL_HP_image_transform 1 -#define GL_IMAGE_SCALE_X_HP 0x8155 -#define GL_IMAGE_SCALE_Y_HP 0x8156 -#define GL_IMAGE_TRANSLATE_X_HP 0x8157 -#define GL_IMAGE_TRANSLATE_Y_HP 0x8158 -#define GL_IMAGE_ROTATE_ANGLE_HP 0x8159 -#define GL_IMAGE_ROTATE_ORIGIN_X_HP 0x815A -#define GL_IMAGE_ROTATE_ORIGIN_Y_HP 0x815B -#define GL_IMAGE_MAG_FILTER_HP 0x815C -#define GL_IMAGE_MIN_FILTER_HP 0x815D -#define GL_IMAGE_CUBIC_WEIGHT_HP 0x815E -#define GL_CUBIC_HP 0x815F -#define GL_AVERAGE_HP 0x8160 -#define GL_IMAGE_TRANSFORM_2D_HP 0x8161 -#define GL_POST_IMAGE_TRANSFORM_COLOR_TABLE_HP 0x8162 -#define GL_PROXY_POST_IMAGE_TRANSFORM_COLOR_TABLE_HP 0x8163 -typedef void (APIENTRYP PFNGLIMAGETRANSFORMPARAMETERIHPPROC) (GLenum target, GLenum pname, GLint param); -typedef void (APIENTRYP PFNGLIMAGETRANSFORMPARAMETERFHPPROC) (GLenum target, GLenum pname, GLfloat param); -typedef void (APIENTRYP PFNGLIMAGETRANSFORMPARAMETERIVHPPROC) (GLenum target, GLenum pname, const GLint *params); -typedef void (APIENTRYP PFNGLIMAGETRANSFORMPARAMETERFVHPPROC) (GLenum target, GLenum pname, const GLfloat *params); -typedef void (APIENTRYP PFNGLGETIMAGETRANSFORMPARAMETERIVHPPROC) (GLenum target, GLenum pname, GLint *params); -typedef void (APIENTRYP PFNGLGETIMAGETRANSFORMPARAMETERFVHPPROC) (GLenum target, GLenum pname, GLfloat *params); -#ifdef GL_GLEXT_PROTOTYPES -GLAPI void APIENTRY glImageTransformParameteriHP (GLenum target, GLenum pname, GLint param); -GLAPI void APIENTRY glImageTransformParameterfHP (GLenum target, GLenum pname, GLfloat param); -GLAPI void APIENTRY glImageTransformParameterivHP (GLenum target, GLenum pname, const GLint *params); -GLAPI void APIENTRY glImageTransformParameterfvHP (GLenum target, GLenum pname, const GLfloat *params); -GLAPI void APIENTRY glGetImageTransformParameterivHP (GLenum target, GLenum pname, GLint *params); -GLAPI void APIENTRY glGetImageTransformParameterfvHP (GLenum target, GLenum pname, GLfloat *params); -#endif -#endif /* GL_HP_image_transform */ - -#ifndef GL_HP_occlusion_test -#define GL_HP_occlusion_test 1 -#define GL_OCCLUSION_TEST_HP 0x8165 -#define GL_OCCLUSION_TEST_RESULT_HP 0x8166 -#endif /* GL_HP_occlusion_test */ - -#ifndef GL_HP_texture_lighting -#define GL_HP_texture_lighting 1 -#define GL_TEXTURE_LIGHTING_MODE_HP 0x8167 -#define GL_TEXTURE_POST_SPECULAR_HP 0x8168 -#define GL_TEXTURE_PRE_SPECULAR_HP 0x8169 -#endif /* GL_HP_texture_lighting */ - -#ifndef GL_IBM_cull_vertex -#define GL_IBM_cull_vertex 1 -#define GL_CULL_VERTEX_IBM 103050 -#endif /* GL_IBM_cull_vertex */ - -#ifndef GL_IBM_multimode_draw_arrays -#define GL_IBM_multimode_draw_arrays 1 -typedef void (APIENTRYP PFNGLMULTIMODEDRAWARRAYSIBMPROC) (const GLenum *mode, const GLint *first, const GLsizei *count, GLsizei primcount, GLint modestride); -typedef void (APIENTRYP PFNGLMULTIMODEDRAWELEMENTSIBMPROC) (const GLenum *mode, const GLsizei *count, GLenum type, const void *const*indices, GLsizei primcount, GLint modestride); -#ifdef GL_GLEXT_PROTOTYPES -GLAPI void APIENTRY glMultiModeDrawArraysIBM (const GLenum *mode, const GLint *first, const GLsizei *count, GLsizei primcount, GLint modestride); -GLAPI void APIENTRY glMultiModeDrawElementsIBM (const GLenum *mode, const GLsizei *count, GLenum type, const void *const*indices, GLsizei primcount, GLint modestride); -#endif -#endif /* GL_IBM_multimode_draw_arrays */ - -#ifndef GL_IBM_rasterpos_clip -#define GL_IBM_rasterpos_clip 1 -#define GL_RASTER_POSITION_UNCLIPPED_IBM 0x19262 -#endif /* GL_IBM_rasterpos_clip */ - -#ifndef GL_IBM_static_data -#define GL_IBM_static_data 1 -#define GL_ALL_STATIC_DATA_IBM 103060 -#define GL_STATIC_VERTEX_ARRAY_IBM 103061 -typedef void (APIENTRYP PFNGLFLUSHSTATICDATAIBMPROC) (GLenum target); -#ifdef GL_GLEXT_PROTOTYPES -GLAPI void APIENTRY glFlushStaticDataIBM (GLenum target); -#endif -#endif /* GL_IBM_static_data */ - -#ifndef GL_IBM_texture_mirrored_repeat -#define GL_IBM_texture_mirrored_repeat 1 -#define GL_MIRRORED_REPEAT_IBM 0x8370 -#endif /* GL_IBM_texture_mirrored_repeat */ - -#ifndef GL_IBM_vertex_array_lists -#define GL_IBM_vertex_array_lists 1 -#define GL_VERTEX_ARRAY_LIST_IBM 103070 -#define GL_NORMAL_ARRAY_LIST_IBM 103071 -#define GL_COLOR_ARRAY_LIST_IBM 103072 -#define GL_INDEX_ARRAY_LIST_IBM 103073 -#define GL_TEXTURE_COORD_ARRAY_LIST_IBM 103074 -#define GL_EDGE_FLAG_ARRAY_LIST_IBM 103075 -#define GL_FOG_COORDINATE_ARRAY_LIST_IBM 103076 -#define GL_SECONDARY_COLOR_ARRAY_LIST_IBM 103077 -#define GL_VERTEX_ARRAY_LIST_STRIDE_IBM 103080 -#define GL_NORMAL_ARRAY_LIST_STRIDE_IBM 103081 -#define GL_COLOR_ARRAY_LIST_STRIDE_IBM 103082 -#define GL_INDEX_ARRAY_LIST_STRIDE_IBM 103083 -#define GL_TEXTURE_COORD_ARRAY_LIST_STRIDE_IBM 103084 -#define GL_EDGE_FLAG_ARRAY_LIST_STRIDE_IBM 103085 -#define GL_FOG_COORDINATE_ARRAY_LIST_STRIDE_IBM 103086 -#define GL_SECONDARY_COLOR_ARRAY_LIST_STRIDE_IBM 103087 -typedef void (APIENTRYP PFNGLCOLORPOINTERLISTIBMPROC) (GLint size, GLenum type, GLint stride, const void **pointer, GLint ptrstride); -typedef void (APIENTRYP PFNGLSECONDARYCOLORPOINTERLISTIBMPROC) (GLint size, GLenum type, GLint stride, const void **pointer, GLint ptrstride); -typedef void (APIENTRYP PFNGLEDGEFLAGPOINTERLISTIBMPROC) (GLint stride, const GLboolean **pointer, GLint ptrstride); -typedef void (APIENTRYP PFNGLFOGCOORDPOINTERLISTIBMPROC) (GLenum type, GLint stride, const void **pointer, GLint ptrstride); -typedef void (APIENTRYP PFNGLINDEXPOINTERLISTIBMPROC) (GLenum type, GLint stride, const void **pointer, GLint ptrstride); -typedef void (APIENTRYP PFNGLNORMALPOINTERLISTIBMPROC) (GLenum type, GLint stride, const void **pointer, GLint ptrstride); -typedef void (APIENTRYP PFNGLTEXCOORDPOINTERLISTIBMPROC) (GLint size, GLenum type, GLint stride, const void **pointer, GLint ptrstride); -typedef void (APIENTRYP PFNGLVERTEXPOINTERLISTIBMPROC) (GLint size, GLenum type, GLint stride, const void **pointer, GLint ptrstride); -#ifdef GL_GLEXT_PROTOTYPES -GLAPI void APIENTRY glColorPointerListIBM (GLint size, GLenum type, GLint stride, const void **pointer, GLint ptrstride); -GLAPI void APIENTRY glSecondaryColorPointerListIBM (GLint size, GLenum type, GLint stride, const void **pointer, GLint ptrstride); -GLAPI void APIENTRY glEdgeFlagPointerListIBM (GLint stride, const GLboolean **pointer, GLint ptrstride); -GLAPI void APIENTRY glFogCoordPointerListIBM (GLenum type, GLint stride, const void **pointer, GLint ptrstride); -GLAPI void APIENTRY glIndexPointerListIBM (GLenum type, GLint stride, const void **pointer, GLint ptrstride); -GLAPI void APIENTRY glNormalPointerListIBM (GLenum type, GLint stride, const void **pointer, GLint ptrstride); -GLAPI void APIENTRY glTexCoordPointerListIBM (GLint size, GLenum type, GLint stride, const void **pointer, GLint ptrstride); -GLAPI void APIENTRY glVertexPointerListIBM (GLint size, GLenum type, GLint stride, const void **pointer, GLint ptrstride); -#endif -#endif /* GL_IBM_vertex_array_lists */ - -#ifndef GL_INGR_blend_func_separate -#define GL_INGR_blend_func_separate 1 -typedef void (APIENTRYP PFNGLBLENDFUNCSEPARATEINGRPROC) (GLenum sfactorRGB, GLenum dfactorRGB, GLenum sfactorAlpha, GLenum dfactorAlpha); -#ifdef GL_GLEXT_PROTOTYPES -GLAPI void APIENTRY glBlendFuncSeparateINGR (GLenum sfactorRGB, GLenum dfactorRGB, GLenum sfactorAlpha, GLenum dfactorAlpha); -#endif -#endif /* GL_INGR_blend_func_separate */ - -#ifndef GL_INGR_color_clamp -#define GL_INGR_color_clamp 1 -#define GL_RED_MIN_CLAMP_INGR 0x8560 -#define GL_GREEN_MIN_CLAMP_INGR 0x8561 -#define GL_BLUE_MIN_CLAMP_INGR 0x8562 -#define GL_ALPHA_MIN_CLAMP_INGR 0x8563 -#define GL_RED_MAX_CLAMP_INGR 0x8564 -#define GL_GREEN_MAX_CLAMP_INGR 0x8565 -#define GL_BLUE_MAX_CLAMP_INGR 0x8566 -#define GL_ALPHA_MAX_CLAMP_INGR 0x8567 -#endif /* GL_INGR_color_clamp */ - -#ifndef GL_INGR_interlace_read -#define GL_INGR_interlace_read 1 -#define GL_INTERLACE_READ_INGR 0x8568 -#endif /* GL_INGR_interlace_read */ - -#ifndef GL_INTEL_fragment_shader_ordering -#define GL_INTEL_fragment_shader_ordering 1 -#endif /* GL_INTEL_fragment_shader_ordering */ - -#ifndef GL_INTEL_map_texture -#define GL_INTEL_map_texture 1 -#define GL_TEXTURE_MEMORY_LAYOUT_INTEL 0x83FF -#define GL_LAYOUT_DEFAULT_INTEL 0 -#define GL_LAYOUT_LINEAR_INTEL 1 -#define GL_LAYOUT_LINEAR_CPU_CACHED_INTEL 2 -typedef void (APIENTRYP PFNGLSYNCTEXTUREINTELPROC) (GLuint texture); -typedef void (APIENTRYP PFNGLUNMAPTEXTURE2DINTELPROC) (GLuint texture, GLint level); -typedef void *(APIENTRYP PFNGLMAPTEXTURE2DINTELPROC) (GLuint texture, GLint level, GLbitfield access, GLint *stride, GLenum *layout); -#ifdef GL_GLEXT_PROTOTYPES -GLAPI void APIENTRY glSyncTextureINTEL (GLuint texture); -GLAPI void APIENTRY glUnmapTexture2DINTEL (GLuint texture, GLint level); -GLAPI void *APIENTRY glMapTexture2DINTEL (GLuint texture, GLint level, GLbitfield access, GLint *stride, GLenum *layout); -#endif -#endif /* GL_INTEL_map_texture */ - -#ifndef GL_INTEL_parallel_arrays -#define GL_INTEL_parallel_arrays 1 -#define GL_PARALLEL_ARRAYS_INTEL 0x83F4 -#define GL_VERTEX_ARRAY_PARALLEL_POINTERS_INTEL 0x83F5 -#define GL_NORMAL_ARRAY_PARALLEL_POINTERS_INTEL 0x83F6 -#define GL_COLOR_ARRAY_PARALLEL_POINTERS_INTEL 0x83F7 -#define GL_TEXTURE_COORD_ARRAY_PARALLEL_POINTERS_INTEL 0x83F8 -typedef void (APIENTRYP PFNGLVERTEXPOINTERVINTELPROC) (GLint size, GLenum type, const void **pointer); -typedef void (APIENTRYP PFNGLNORMALPOINTERVINTELPROC) (GLenum type, const void **pointer); -typedef void (APIENTRYP PFNGLCOLORPOINTERVINTELPROC) (GLint size, GLenum type, const void **pointer); -typedef void (APIENTRYP PFNGLTEXCOORDPOINTERVINTELPROC) (GLint size, GLenum type, const void **pointer); -#ifdef GL_GLEXT_PROTOTYPES -GLAPI void APIENTRY glVertexPointervINTEL (GLint size, GLenum type, const void **pointer); -GLAPI void APIENTRY glNormalPointervINTEL (GLenum type, const void **pointer); -GLAPI void APIENTRY glColorPointervINTEL (GLint size, GLenum type, const void **pointer); -GLAPI void APIENTRY glTexCoordPointervINTEL (GLint size, GLenum type, const void **pointer); -#endif -#endif /* GL_INTEL_parallel_arrays */ - -#ifndef GL_INTEL_performance_query -#define GL_INTEL_performance_query 1 -#define GL_PERFQUERY_SINGLE_CONTEXT_INTEL 0x00000000 -#define GL_PERFQUERY_GLOBAL_CONTEXT_INTEL 0x00000001 -#define GL_PERFQUERY_WAIT_INTEL 0x83FB -#define GL_PERFQUERY_FLUSH_INTEL 0x83FA -#define GL_PERFQUERY_DONOT_FLUSH_INTEL 0x83F9 -#define GL_PERFQUERY_COUNTER_EVENT_INTEL 0x94F0 -#define GL_PERFQUERY_COUNTER_DURATION_NORM_INTEL 0x94F1 -#define GL_PERFQUERY_COUNTER_DURATION_RAW_INTEL 0x94F2 -#define GL_PERFQUERY_COUNTER_THROUGHPUT_INTEL 0x94F3 -#define GL_PERFQUERY_COUNTER_RAW_INTEL 0x94F4 -#define GL_PERFQUERY_COUNTER_TIMESTAMP_INTEL 0x94F5 -#define GL_PERFQUERY_COUNTER_DATA_UINT32_INTEL 0x94F8 -#define GL_PERFQUERY_COUNTER_DATA_UINT64_INTEL 0x94F9 -#define GL_PERFQUERY_COUNTER_DATA_FLOAT_INTEL 0x94FA -#define GL_PERFQUERY_COUNTER_DATA_DOUBLE_INTEL 0x94FB -#define GL_PERFQUERY_COUNTER_DATA_BOOL32_INTEL 0x94FC -#define GL_PERFQUERY_QUERY_NAME_LENGTH_MAX_INTEL 0x94FD -#define GL_PERFQUERY_COUNTER_NAME_LENGTH_MAX_INTEL 0x94FE -#define GL_PERFQUERY_COUNTER_DESC_LENGTH_MAX_INTEL 0x94FF -#define GL_PERFQUERY_GPA_EXTENDED_COUNTERS_INTEL 0x9500 -typedef void (APIENTRYP PFNGLBEGINPERFQUERYINTELPROC) (GLuint queryHandle); -typedef void (APIENTRYP PFNGLCREATEPERFQUERYINTELPROC) (GLuint queryId, GLuint *queryHandle); -typedef void (APIENTRYP PFNGLDELETEPERFQUERYINTELPROC) (GLuint queryHandle); -typedef void (APIENTRYP PFNGLENDPERFQUERYINTELPROC) (GLuint queryHandle); -typedef void (APIENTRYP PFNGLGETFIRSTPERFQUERYIDINTELPROC) (GLuint *queryId); -typedef void (APIENTRYP PFNGLGETNEXTPERFQUERYIDINTELPROC) (GLuint queryId, GLuint *nextQueryId); -typedef void (APIENTRYP PFNGLGETPERFCOUNTERINFOINTELPROC) (GLuint queryId, GLuint counterId, GLuint counterNameLength, GLchar *counterName, GLuint counterDescLength, GLchar *counterDesc, GLuint *counterOffset, GLuint *counterDataSize, GLuint *counterTypeEnum, GLuint *counterDataTypeEnum, GLuint64 *rawCounterMaxValue); -typedef void (APIENTRYP PFNGLGETPERFQUERYDATAINTELPROC) (GLuint queryHandle, GLuint flags, GLsizei dataSize, GLvoid *data, GLuint *bytesWritten); -typedef void (APIENTRYP PFNGLGETPERFQUERYIDBYNAMEINTELPROC) (GLchar *queryName, GLuint *queryId); -typedef void (APIENTRYP PFNGLGETPERFQUERYINFOINTELPROC) (GLuint queryId, GLuint queryNameLength, GLchar *queryName, GLuint *dataSize, GLuint *noCounters, GLuint *noInstances, GLuint *capsMask); -#ifdef GL_GLEXT_PROTOTYPES -GLAPI void APIENTRY glBeginPerfQueryINTEL (GLuint queryHandle); -GLAPI void APIENTRY glCreatePerfQueryINTEL (GLuint queryId, GLuint *queryHandle); -GLAPI void APIENTRY glDeletePerfQueryINTEL (GLuint queryHandle); -GLAPI void APIENTRY glEndPerfQueryINTEL (GLuint queryHandle); -GLAPI void APIENTRY glGetFirstPerfQueryIdINTEL (GLuint *queryId); -GLAPI void APIENTRY glGetNextPerfQueryIdINTEL (GLuint queryId, GLuint *nextQueryId); -GLAPI void APIENTRY glGetPerfCounterInfoINTEL (GLuint queryId, GLuint counterId, GLuint counterNameLength, GLchar *counterName, GLuint counterDescLength, GLchar *counterDesc, GLuint *counterOffset, GLuint *counterDataSize, GLuint *counterTypeEnum, GLuint *counterDataTypeEnum, GLuint64 *rawCounterMaxValue); -GLAPI void APIENTRY glGetPerfQueryDataINTEL (GLuint queryHandle, GLuint flags, GLsizei dataSize, GLvoid *data, GLuint *bytesWritten); -GLAPI void APIENTRY glGetPerfQueryIdByNameINTEL (GLchar *queryName, GLuint *queryId); -GLAPI void APIENTRY glGetPerfQueryInfoINTEL (GLuint queryId, GLuint queryNameLength, GLchar *queryName, GLuint *dataSize, GLuint *noCounters, GLuint *noInstances, GLuint *capsMask); -#endif -#endif /* GL_INTEL_performance_query */ - -#ifndef GL_MESAX_texture_stack -#define GL_MESAX_texture_stack 1 -#define GL_TEXTURE_1D_STACK_MESAX 0x8759 -#define GL_TEXTURE_2D_STACK_MESAX 0x875A -#define GL_PROXY_TEXTURE_1D_STACK_MESAX 0x875B -#define GL_PROXY_TEXTURE_2D_STACK_MESAX 0x875C -#define GL_TEXTURE_1D_STACK_BINDING_MESAX 0x875D -#define GL_TEXTURE_2D_STACK_BINDING_MESAX 0x875E -#endif /* GL_MESAX_texture_stack */ - -#ifndef GL_MESA_pack_invert -#define GL_MESA_pack_invert 1 -#define GL_PACK_INVERT_MESA 0x8758 -#endif /* GL_MESA_pack_invert */ - -#ifndef GL_MESA_resize_buffers -#define GL_MESA_resize_buffers 1 -typedef void (APIENTRYP PFNGLRESIZEBUFFERSMESAPROC) (void); -#ifdef GL_GLEXT_PROTOTYPES -GLAPI void APIENTRY glResizeBuffersMESA (void); -#endif -#endif /* GL_MESA_resize_buffers */ - -#ifndef GL_MESA_window_pos -#define GL_MESA_window_pos 1 -typedef void (APIENTRYP PFNGLWINDOWPOS2DMESAPROC) (GLdouble x, GLdouble y); -typedef void (APIENTRYP PFNGLWINDOWPOS2DVMESAPROC) (const GLdouble *v); -typedef void (APIENTRYP PFNGLWINDOWPOS2FMESAPROC) (GLfloat x, GLfloat y); -typedef void (APIENTRYP PFNGLWINDOWPOS2FVMESAPROC) (const GLfloat *v); -typedef void (APIENTRYP PFNGLWINDOWPOS2IMESAPROC) (GLint x, GLint y); -typedef void (APIENTRYP PFNGLWINDOWPOS2IVMESAPROC) (const GLint *v); -typedef void (APIENTRYP PFNGLWINDOWPOS2SMESAPROC) (GLshort x, GLshort y); -typedef void (APIENTRYP PFNGLWINDOWPOS2SVMESAPROC) (const GLshort *v); -typedef void (APIENTRYP PFNGLWINDOWPOS3DMESAPROC) (GLdouble x, GLdouble y, GLdouble z); -typedef void (APIENTRYP PFNGLWINDOWPOS3DVMESAPROC) (const GLdouble *v); -typedef void (APIENTRYP PFNGLWINDOWPOS3FMESAPROC) (GLfloat x, GLfloat y, GLfloat z); -typedef void (APIENTRYP PFNGLWINDOWPOS3FVMESAPROC) (const GLfloat *v); -typedef void (APIENTRYP PFNGLWINDOWPOS3IMESAPROC) (GLint x, GLint y, GLint z); -typedef void (APIENTRYP PFNGLWINDOWPOS3IVMESAPROC) (const GLint *v); -typedef void (APIENTRYP PFNGLWINDOWPOS3SMESAPROC) (GLshort x, GLshort y, GLshort z); -typedef void (APIENTRYP PFNGLWINDOWPOS3SVMESAPROC) (const GLshort *v); -typedef void (APIENTRYP PFNGLWINDOWPOS4DMESAPROC) (GLdouble x, GLdouble y, GLdouble z, GLdouble w); -typedef void (APIENTRYP PFNGLWINDOWPOS4DVMESAPROC) (const GLdouble *v); -typedef void (APIENTRYP PFNGLWINDOWPOS4FMESAPROC) (GLfloat x, GLfloat y, GLfloat z, GLfloat w); -typedef void (APIENTRYP PFNGLWINDOWPOS4FVMESAPROC) (const GLfloat *v); -typedef void (APIENTRYP PFNGLWINDOWPOS4IMESAPROC) (GLint x, GLint y, GLint z, GLint w); -typedef void (APIENTRYP PFNGLWINDOWPOS4IVMESAPROC) (const GLint *v); -typedef void (APIENTRYP PFNGLWINDOWPOS4SMESAPROC) (GLshort x, GLshort y, GLshort z, GLshort w); -typedef void (APIENTRYP PFNGLWINDOWPOS4SVMESAPROC) (const GLshort *v); -#ifdef GL_GLEXT_PROTOTYPES -GLAPI void APIENTRY glWindowPos2dMESA (GLdouble x, GLdouble y); -GLAPI void APIENTRY glWindowPos2dvMESA (const GLdouble *v); -GLAPI void APIENTRY glWindowPos2fMESA (GLfloat x, GLfloat y); -GLAPI void APIENTRY glWindowPos2fvMESA (const GLfloat *v); -GLAPI void APIENTRY glWindowPos2iMESA (GLint x, GLint y); -GLAPI void APIENTRY glWindowPos2ivMESA (const GLint *v); -GLAPI void APIENTRY glWindowPos2sMESA (GLshort x, GLshort y); -GLAPI void APIENTRY glWindowPos2svMESA (const GLshort *v); -GLAPI void APIENTRY glWindowPos3dMESA (GLdouble x, GLdouble y, GLdouble z); -GLAPI void APIENTRY glWindowPos3dvMESA (const GLdouble *v); -GLAPI void APIENTRY glWindowPos3fMESA (GLfloat x, GLfloat y, GLfloat z); -GLAPI void APIENTRY glWindowPos3fvMESA (const GLfloat *v); -GLAPI void APIENTRY glWindowPos3iMESA (GLint x, GLint y, GLint z); -GLAPI void APIENTRY glWindowPos3ivMESA (const GLint *v); -GLAPI void APIENTRY glWindowPos3sMESA (GLshort x, GLshort y, GLshort z); -GLAPI void APIENTRY glWindowPos3svMESA (const GLshort *v); -GLAPI void APIENTRY glWindowPos4dMESA (GLdouble x, GLdouble y, GLdouble z, GLdouble w); -GLAPI void APIENTRY glWindowPos4dvMESA (const GLdouble *v); -GLAPI void APIENTRY glWindowPos4fMESA (GLfloat x, GLfloat y, GLfloat z, GLfloat w); -GLAPI void APIENTRY glWindowPos4fvMESA (const GLfloat *v); -GLAPI void APIENTRY glWindowPos4iMESA (GLint x, GLint y, GLint z, GLint w); -GLAPI void APIENTRY glWindowPos4ivMESA (const GLint *v); -GLAPI void APIENTRY glWindowPos4sMESA (GLshort x, GLshort y, GLshort z, GLshort w); -GLAPI void APIENTRY glWindowPos4svMESA (const GLshort *v); -#endif -#endif /* GL_MESA_window_pos */ - -#ifndef GL_MESA_ycbcr_texture -#define GL_MESA_ycbcr_texture 1 -#define GL_UNSIGNED_SHORT_8_8_MESA 0x85BA -#define GL_UNSIGNED_SHORT_8_8_REV_MESA 0x85BB -#define GL_YCBCR_MESA 0x8757 -#endif /* GL_MESA_ycbcr_texture */ - -#ifndef GL_NVX_conditional_render -#define GL_NVX_conditional_render 1 -typedef void (APIENTRYP PFNGLBEGINCONDITIONALRENDERNVXPROC) (GLuint id); -typedef void (APIENTRYP PFNGLENDCONDITIONALRENDERNVXPROC) (void); -#ifdef GL_GLEXT_PROTOTYPES -GLAPI void APIENTRY glBeginConditionalRenderNVX (GLuint id); -GLAPI void APIENTRY glEndConditionalRenderNVX (void); -#endif -#endif /* GL_NVX_conditional_render */ - -#ifndef GL_NV_bindless_multi_draw_indirect -#define GL_NV_bindless_multi_draw_indirect 1 -typedef void (APIENTRYP PFNGLMULTIDRAWARRAYSINDIRECTBINDLESSNVPROC) (GLenum mode, const void *indirect, GLsizei drawCount, GLsizei stride, GLint vertexBufferCount); -typedef void (APIENTRYP PFNGLMULTIDRAWELEMENTSINDIRECTBINDLESSNVPROC) (GLenum mode, GLenum type, const void *indirect, GLsizei drawCount, GLsizei stride, GLint vertexBufferCount); -#ifdef GL_GLEXT_PROTOTYPES -GLAPI void APIENTRY glMultiDrawArraysIndirectBindlessNV (GLenum mode, const void *indirect, GLsizei drawCount, GLsizei stride, GLint vertexBufferCount); -GLAPI void APIENTRY glMultiDrawElementsIndirectBindlessNV (GLenum mode, GLenum type, const void *indirect, GLsizei drawCount, GLsizei stride, GLint vertexBufferCount); -#endif -#endif /* GL_NV_bindless_multi_draw_indirect */ - -#ifndef GL_NV_bindless_texture -#define GL_NV_bindless_texture 1 -typedef GLuint64 (APIENTRYP PFNGLGETTEXTUREHANDLENVPROC) (GLuint texture); -typedef GLuint64 (APIENTRYP PFNGLGETTEXTURESAMPLERHANDLENVPROC) (GLuint texture, GLuint sampler); -typedef void (APIENTRYP PFNGLMAKETEXTUREHANDLERESIDENTNVPROC) (GLuint64 handle); -typedef void (APIENTRYP PFNGLMAKETEXTUREHANDLENONRESIDENTNVPROC) (GLuint64 handle); -typedef GLuint64 (APIENTRYP PFNGLGETIMAGEHANDLENVPROC) (GLuint texture, GLint level, GLboolean layered, GLint layer, GLenum format); -typedef void (APIENTRYP PFNGLMAKEIMAGEHANDLERESIDENTNVPROC) (GLuint64 handle, GLenum access); -typedef void (APIENTRYP PFNGLMAKEIMAGEHANDLENONRESIDENTNVPROC) (GLuint64 handle); -typedef void (APIENTRYP PFNGLUNIFORMHANDLEUI64NVPROC) (GLint location, GLuint64 value); -typedef void (APIENTRYP PFNGLUNIFORMHANDLEUI64VNVPROC) (GLint location, GLsizei count, const GLuint64 *value); -typedef void (APIENTRYP PFNGLPROGRAMUNIFORMHANDLEUI64NVPROC) (GLuint program, GLint location, GLuint64 value); -typedef void (APIENTRYP PFNGLPROGRAMUNIFORMHANDLEUI64VNVPROC) (GLuint program, GLint location, GLsizei count, const GLuint64 *values); -typedef GLboolean (APIENTRYP PFNGLISTEXTUREHANDLERESIDENTNVPROC) (GLuint64 handle); -typedef GLboolean (APIENTRYP PFNGLISIMAGEHANDLERESIDENTNVPROC) (GLuint64 handle); -#ifdef GL_GLEXT_PROTOTYPES -GLAPI GLuint64 APIENTRY glGetTextureHandleNV (GLuint texture); -GLAPI GLuint64 APIENTRY glGetTextureSamplerHandleNV (GLuint texture, GLuint sampler); -GLAPI void APIENTRY glMakeTextureHandleResidentNV (GLuint64 handle); -GLAPI void APIENTRY glMakeTextureHandleNonResidentNV (GLuint64 handle); -GLAPI GLuint64 APIENTRY glGetImageHandleNV (GLuint texture, GLint level, GLboolean layered, GLint layer, GLenum format); -GLAPI void APIENTRY glMakeImageHandleResidentNV (GLuint64 handle, GLenum access); -GLAPI void APIENTRY glMakeImageHandleNonResidentNV (GLuint64 handle); -GLAPI void APIENTRY glUniformHandleui64NV (GLint location, GLuint64 value); -GLAPI void APIENTRY glUniformHandleui64vNV (GLint location, GLsizei count, const GLuint64 *value); -GLAPI void APIENTRY glProgramUniformHandleui64NV (GLuint program, GLint location, GLuint64 value); -GLAPI void APIENTRY glProgramUniformHandleui64vNV (GLuint program, GLint location, GLsizei count, const GLuint64 *values); -GLAPI GLboolean APIENTRY glIsTextureHandleResidentNV (GLuint64 handle); -GLAPI GLboolean APIENTRY glIsImageHandleResidentNV (GLuint64 handle); -#endif -#endif /* GL_NV_bindless_texture */ - -#ifndef GL_NV_blend_equation_advanced -#define GL_NV_blend_equation_advanced 1 -#define GL_BLEND_OVERLAP_NV 0x9281 -#define GL_BLEND_PREMULTIPLIED_SRC_NV 0x9280 -#define GL_BLUE_NV 0x1905 -#define GL_COLORBURN_NV 0x929A -#define GL_COLORDODGE_NV 0x9299 -#define GL_CONJOINT_NV 0x9284 -#define GL_CONTRAST_NV 0x92A1 -#define GL_DARKEN_NV 0x9297 -#define GL_DIFFERENCE_NV 0x929E -#define GL_DISJOINT_NV 0x9283 -#define GL_DST_ATOP_NV 0x928F -#define GL_DST_IN_NV 0x928B -#define GL_DST_NV 0x9287 -#define GL_DST_OUT_NV 0x928D -#define GL_DST_OVER_NV 0x9289 -#define GL_EXCLUSION_NV 0x92A0 -#define GL_GREEN_NV 0x1904 -#define GL_HARDLIGHT_NV 0x929B -#define GL_HARDMIX_NV 0x92A9 -#define GL_HSL_COLOR_NV 0x92AF -#define GL_HSL_HUE_NV 0x92AD -#define GL_HSL_LUMINOSITY_NV 0x92B0 -#define GL_HSL_SATURATION_NV 0x92AE -#define GL_INVERT_OVG_NV 0x92B4 -#define GL_INVERT_RGB_NV 0x92A3 -#define GL_LIGHTEN_NV 0x9298 -#define GL_LINEARBURN_NV 0x92A5 -#define GL_LINEARDODGE_NV 0x92A4 -#define GL_LINEARLIGHT_NV 0x92A7 -#define GL_MINUS_CLAMPED_NV 0x92B3 -#define GL_MINUS_NV 0x929F -#define GL_MULTIPLY_NV 0x9294 -#define GL_OVERLAY_NV 0x9296 -#define GL_PINLIGHT_NV 0x92A8 -#define GL_PLUS_CLAMPED_ALPHA_NV 0x92B2 -#define GL_PLUS_CLAMPED_NV 0x92B1 -#define GL_PLUS_DARKER_NV 0x9292 -#define GL_PLUS_NV 0x9291 -#define GL_RED_NV 0x1903 -#define GL_SCREEN_NV 0x9295 -#define GL_SOFTLIGHT_NV 0x929C -#define GL_SRC_ATOP_NV 0x928E -#define GL_SRC_IN_NV 0x928A -#define GL_SRC_NV 0x9286 -#define GL_SRC_OUT_NV 0x928C -#define GL_SRC_OVER_NV 0x9288 -#define GL_UNCORRELATED_NV 0x9282 -#define GL_VIVIDLIGHT_NV 0x92A6 -#define GL_XOR_NV 0x1506 -typedef void (APIENTRYP PFNGLBLENDPARAMETERINVPROC) (GLenum pname, GLint value); -typedef void (APIENTRYP PFNGLBLENDBARRIERNVPROC) (void); -#ifdef GL_GLEXT_PROTOTYPES -GLAPI void APIENTRY glBlendParameteriNV (GLenum pname, GLint value); -GLAPI void APIENTRY glBlendBarrierNV (void); -#endif -#endif /* GL_NV_blend_equation_advanced */ - -#ifndef GL_NV_blend_equation_advanced_coherent -#define GL_NV_blend_equation_advanced_coherent 1 -#define GL_BLEND_ADVANCED_COHERENT_NV 0x9285 -#endif /* GL_NV_blend_equation_advanced_coherent */ - -#ifndef GL_NV_blend_square -#define GL_NV_blend_square 1 -#endif /* GL_NV_blend_square */ - -#ifndef GL_NV_compute_program5 -#define GL_NV_compute_program5 1 -#define GL_COMPUTE_PROGRAM_NV 0x90FB -#define GL_COMPUTE_PROGRAM_PARAMETER_BUFFER_NV 0x90FC -#endif /* GL_NV_compute_program5 */ - -#ifndef GL_NV_conditional_render -#define GL_NV_conditional_render 1 -#define GL_QUERY_WAIT_NV 0x8E13 -#define GL_QUERY_NO_WAIT_NV 0x8E14 -#define GL_QUERY_BY_REGION_WAIT_NV 0x8E15 -#define GL_QUERY_BY_REGION_NO_WAIT_NV 0x8E16 -typedef void (APIENTRYP PFNGLBEGINCONDITIONALRENDERNVPROC) (GLuint id, GLenum mode); -typedef void (APIENTRYP PFNGLENDCONDITIONALRENDERNVPROC) (void); -#ifdef GL_GLEXT_PROTOTYPES -GLAPI void APIENTRY glBeginConditionalRenderNV (GLuint id, GLenum mode); -GLAPI void APIENTRY glEndConditionalRenderNV (void); -#endif -#endif /* GL_NV_conditional_render */ - -#ifndef GL_NV_copy_depth_to_color -#define GL_NV_copy_depth_to_color 1 -#define GL_DEPTH_STENCIL_TO_RGBA_NV 0x886E -#define GL_DEPTH_STENCIL_TO_BGRA_NV 0x886F -#endif /* GL_NV_copy_depth_to_color */ - -#ifndef GL_NV_copy_image -#define GL_NV_copy_image 1 -typedef void (APIENTRYP PFNGLCOPYIMAGESUBDATANVPROC) (GLuint srcName, GLenum srcTarget, GLint srcLevel, GLint srcX, GLint srcY, GLint srcZ, GLuint dstName, GLenum dstTarget, GLint dstLevel, GLint dstX, GLint dstY, GLint dstZ, GLsizei width, GLsizei height, GLsizei depth); -#ifdef GL_GLEXT_PROTOTYPES -GLAPI void APIENTRY glCopyImageSubDataNV (GLuint srcName, GLenum srcTarget, GLint srcLevel, GLint srcX, GLint srcY, GLint srcZ, GLuint dstName, GLenum dstTarget, GLint dstLevel, GLint dstX, GLint dstY, GLint dstZ, GLsizei width, GLsizei height, GLsizei depth); -#endif -#endif /* GL_NV_copy_image */ - -#ifndef GL_NV_deep_texture3D -#define GL_NV_deep_texture3D 1 -#define GL_MAX_DEEP_3D_TEXTURE_WIDTH_HEIGHT_NV 0x90D0 -#define GL_MAX_DEEP_3D_TEXTURE_DEPTH_NV 0x90D1 -#endif /* GL_NV_deep_texture3D */ - -#ifndef GL_NV_depth_buffer_float -#define GL_NV_depth_buffer_float 1 -#define GL_DEPTH_COMPONENT32F_NV 0x8DAB -#define GL_DEPTH32F_STENCIL8_NV 0x8DAC -#define GL_FLOAT_32_UNSIGNED_INT_24_8_REV_NV 0x8DAD -#define GL_DEPTH_BUFFER_FLOAT_MODE_NV 0x8DAF -typedef void (APIENTRYP PFNGLDEPTHRANGEDNVPROC) (GLdouble zNear, GLdouble zFar); -typedef void (APIENTRYP PFNGLCLEARDEPTHDNVPROC) (GLdouble depth); -typedef void (APIENTRYP PFNGLDEPTHBOUNDSDNVPROC) (GLdouble zmin, GLdouble zmax); -#ifdef GL_GLEXT_PROTOTYPES -GLAPI void APIENTRY glDepthRangedNV (GLdouble zNear, GLdouble zFar); -GLAPI void APIENTRY glClearDepthdNV (GLdouble depth); -GLAPI void APIENTRY glDepthBoundsdNV (GLdouble zmin, GLdouble zmax); -#endif -#endif /* GL_NV_depth_buffer_float */ - -#ifndef GL_NV_depth_clamp -#define GL_NV_depth_clamp 1 -#define GL_DEPTH_CLAMP_NV 0x864F -#endif /* GL_NV_depth_clamp */ - -#ifndef GL_NV_draw_texture -#define GL_NV_draw_texture 1 -typedef void (APIENTRYP PFNGLDRAWTEXTURENVPROC) (GLuint texture, GLuint sampler, GLfloat x0, GLfloat y0, GLfloat x1, GLfloat y1, GLfloat z, GLfloat s0, GLfloat t0, GLfloat s1, GLfloat t1); -#ifdef GL_GLEXT_PROTOTYPES -GLAPI void APIENTRY glDrawTextureNV (GLuint texture, GLuint sampler, GLfloat x0, GLfloat y0, GLfloat x1, GLfloat y1, GLfloat z, GLfloat s0, GLfloat t0, GLfloat s1, GLfloat t1); -#endif -#endif /* GL_NV_draw_texture */ - -#ifndef GL_NV_evaluators -#define GL_NV_evaluators 1 -#define GL_EVAL_2D_NV 0x86C0 -#define GL_EVAL_TRIANGULAR_2D_NV 0x86C1 -#define GL_MAP_TESSELLATION_NV 0x86C2 -#define GL_MAP_ATTRIB_U_ORDER_NV 0x86C3 -#define GL_MAP_ATTRIB_V_ORDER_NV 0x86C4 -#define GL_EVAL_FRACTIONAL_TESSELLATION_NV 0x86C5 -#define GL_EVAL_VERTEX_ATTRIB0_NV 0x86C6 -#define GL_EVAL_VERTEX_ATTRIB1_NV 0x86C7 -#define GL_EVAL_VERTEX_ATTRIB2_NV 0x86C8 -#define GL_EVAL_VERTEX_ATTRIB3_NV 0x86C9 -#define GL_EVAL_VERTEX_ATTRIB4_NV 0x86CA -#define GL_EVAL_VERTEX_ATTRIB5_NV 0x86CB -#define GL_EVAL_VERTEX_ATTRIB6_NV 0x86CC -#define GL_EVAL_VERTEX_ATTRIB7_NV 0x86CD -#define GL_EVAL_VERTEX_ATTRIB8_NV 0x86CE -#define GL_EVAL_VERTEX_ATTRIB9_NV 0x86CF -#define GL_EVAL_VERTEX_ATTRIB10_NV 0x86D0 -#define GL_EVAL_VERTEX_ATTRIB11_NV 0x86D1 -#define GL_EVAL_VERTEX_ATTRIB12_NV 0x86D2 -#define GL_EVAL_VERTEX_ATTRIB13_NV 0x86D3 -#define GL_EVAL_VERTEX_ATTRIB14_NV 0x86D4 -#define GL_EVAL_VERTEX_ATTRIB15_NV 0x86D5 -#define GL_MAX_MAP_TESSELLATION_NV 0x86D6 -#define GL_MAX_RATIONAL_EVAL_ORDER_NV 0x86D7 -typedef void (APIENTRYP PFNGLMAPCONTROLPOINTSNVPROC) (GLenum target, GLuint index, GLenum type, GLsizei ustride, GLsizei vstride, GLint uorder, GLint vorder, GLboolean packed, const void *points); -typedef void (APIENTRYP PFNGLMAPPARAMETERIVNVPROC) (GLenum target, GLenum pname, const GLint *params); -typedef void (APIENTRYP PFNGLMAPPARAMETERFVNVPROC) (GLenum target, GLenum pname, const GLfloat *params); -typedef void (APIENTRYP PFNGLGETMAPCONTROLPOINTSNVPROC) (GLenum target, GLuint index, GLenum type, GLsizei ustride, GLsizei vstride, GLboolean packed, void *points); -typedef void (APIENTRYP PFNGLGETMAPPARAMETERIVNVPROC) (GLenum target, GLenum pname, GLint *params); -typedef void (APIENTRYP PFNGLGETMAPPARAMETERFVNVPROC) (GLenum target, GLenum pname, GLfloat *params); -typedef void (APIENTRYP PFNGLGETMAPATTRIBPARAMETERIVNVPROC) (GLenum target, GLuint index, GLenum pname, GLint *params); -typedef void (APIENTRYP PFNGLGETMAPATTRIBPARAMETERFVNVPROC) (GLenum target, GLuint index, GLenum pname, GLfloat *params); -typedef void (APIENTRYP PFNGLEVALMAPSNVPROC) (GLenum target, GLenum mode); -#ifdef GL_GLEXT_PROTOTYPES -GLAPI void APIENTRY glMapControlPointsNV (GLenum target, GLuint index, GLenum type, GLsizei ustride, GLsizei vstride, GLint uorder, GLint vorder, GLboolean packed, const void *points); -GLAPI void APIENTRY glMapParameterivNV (GLenum target, GLenum pname, const GLint *params); -GLAPI void APIENTRY glMapParameterfvNV (GLenum target, GLenum pname, const GLfloat *params); -GLAPI void APIENTRY glGetMapControlPointsNV (GLenum target, GLuint index, GLenum type, GLsizei ustride, GLsizei vstride, GLboolean packed, void *points); -GLAPI void APIENTRY glGetMapParameterivNV (GLenum target, GLenum pname, GLint *params); -GLAPI void APIENTRY glGetMapParameterfvNV (GLenum target, GLenum pname, GLfloat *params); -GLAPI void APIENTRY glGetMapAttribParameterivNV (GLenum target, GLuint index, GLenum pname, GLint *params); -GLAPI void APIENTRY glGetMapAttribParameterfvNV (GLenum target, GLuint index, GLenum pname, GLfloat *params); -GLAPI void APIENTRY glEvalMapsNV (GLenum target, GLenum mode); -#endif -#endif /* GL_NV_evaluators */ - -#ifndef GL_NV_explicit_multisample -#define GL_NV_explicit_multisample 1 -#define GL_SAMPLE_POSITION_NV 0x8E50 -#define GL_SAMPLE_MASK_NV 0x8E51 -#define GL_SAMPLE_MASK_VALUE_NV 0x8E52 -#define GL_TEXTURE_BINDING_RENDERBUFFER_NV 0x8E53 -#define GL_TEXTURE_RENDERBUFFER_DATA_STORE_BINDING_NV 0x8E54 -#define GL_TEXTURE_RENDERBUFFER_NV 0x8E55 -#define GL_SAMPLER_RENDERBUFFER_NV 0x8E56 -#define GL_INT_SAMPLER_RENDERBUFFER_NV 0x8E57 -#define GL_UNSIGNED_INT_SAMPLER_RENDERBUFFER_NV 0x8E58 -#define GL_MAX_SAMPLE_MASK_WORDS_NV 0x8E59 -typedef void (APIENTRYP PFNGLGETMULTISAMPLEFVNVPROC) (GLenum pname, GLuint index, GLfloat *val); -typedef void (APIENTRYP PFNGLSAMPLEMASKINDEXEDNVPROC) (GLuint index, GLbitfield mask); -typedef void (APIENTRYP PFNGLTEXRENDERBUFFERNVPROC) (GLenum target, GLuint renderbuffer); -#ifdef GL_GLEXT_PROTOTYPES -GLAPI void APIENTRY glGetMultisamplefvNV (GLenum pname, GLuint index, GLfloat *val); -GLAPI void APIENTRY glSampleMaskIndexedNV (GLuint index, GLbitfield mask); -GLAPI void APIENTRY glTexRenderbufferNV (GLenum target, GLuint renderbuffer); -#endif -#endif /* GL_NV_explicit_multisample */ - -#ifndef GL_NV_fence -#define GL_NV_fence 1 -#define GL_ALL_COMPLETED_NV 0x84F2 -#define GL_FENCE_STATUS_NV 0x84F3 -#define GL_FENCE_CONDITION_NV 0x84F4 -typedef void (APIENTRYP PFNGLDELETEFENCESNVPROC) (GLsizei n, const GLuint *fences); -typedef void (APIENTRYP PFNGLGENFENCESNVPROC) (GLsizei n, GLuint *fences); -typedef GLboolean (APIENTRYP PFNGLISFENCENVPROC) (GLuint fence); -typedef GLboolean (APIENTRYP PFNGLTESTFENCENVPROC) (GLuint fence); -typedef void (APIENTRYP PFNGLGETFENCEIVNVPROC) (GLuint fence, GLenum pname, GLint *params); -typedef void (APIENTRYP PFNGLFINISHFENCENVPROC) (GLuint fence); -typedef void (APIENTRYP PFNGLSETFENCENVPROC) (GLuint fence, GLenum condition); -#ifdef GL_GLEXT_PROTOTYPES -GLAPI void APIENTRY glDeleteFencesNV (GLsizei n, const GLuint *fences); -GLAPI void APIENTRY glGenFencesNV (GLsizei n, GLuint *fences); -GLAPI GLboolean APIENTRY glIsFenceNV (GLuint fence); -GLAPI GLboolean APIENTRY glTestFenceNV (GLuint fence); -GLAPI void APIENTRY glGetFenceivNV (GLuint fence, GLenum pname, GLint *params); -GLAPI void APIENTRY glFinishFenceNV (GLuint fence); -GLAPI void APIENTRY glSetFenceNV (GLuint fence, GLenum condition); -#endif -#endif /* GL_NV_fence */ - -#ifndef GL_NV_float_buffer -#define GL_NV_float_buffer 1 -#define GL_FLOAT_R_NV 0x8880 -#define GL_FLOAT_RG_NV 0x8881 -#define GL_FLOAT_RGB_NV 0x8882 -#define GL_FLOAT_RGBA_NV 0x8883 -#define GL_FLOAT_R16_NV 0x8884 -#define GL_FLOAT_R32_NV 0x8885 -#define GL_FLOAT_RG16_NV 0x8886 -#define GL_FLOAT_RG32_NV 0x8887 -#define GL_FLOAT_RGB16_NV 0x8888 -#define GL_FLOAT_RGB32_NV 0x8889 -#define GL_FLOAT_RGBA16_NV 0x888A -#define GL_FLOAT_RGBA32_NV 0x888B -#define GL_TEXTURE_FLOAT_COMPONENTS_NV 0x888C -#define GL_FLOAT_CLEAR_COLOR_VALUE_NV 0x888D -#define GL_FLOAT_RGBA_MODE_NV 0x888E -#endif /* GL_NV_float_buffer */ - -#ifndef GL_NV_fog_distance -#define GL_NV_fog_distance 1 -#define GL_FOG_DISTANCE_MODE_NV 0x855A -#define GL_EYE_RADIAL_NV 0x855B -#define GL_EYE_PLANE_ABSOLUTE_NV 0x855C -#endif /* GL_NV_fog_distance */ - -#ifndef GL_NV_fragment_program -#define GL_NV_fragment_program 1 -#define GL_MAX_FRAGMENT_PROGRAM_LOCAL_PARAMETERS_NV 0x8868 -#define GL_FRAGMENT_PROGRAM_NV 0x8870 -#define GL_MAX_TEXTURE_COORDS_NV 0x8871 -#define GL_MAX_TEXTURE_IMAGE_UNITS_NV 0x8872 -#define GL_FRAGMENT_PROGRAM_BINDING_NV 0x8873 -#define GL_PROGRAM_ERROR_STRING_NV 0x8874 -typedef void (APIENTRYP PFNGLPROGRAMNAMEDPARAMETER4FNVPROC) (GLuint id, GLsizei len, const GLubyte *name, GLfloat x, GLfloat y, GLfloat z, GLfloat w); -typedef void (APIENTRYP PFNGLPROGRAMNAMEDPARAMETER4FVNVPROC) (GLuint id, GLsizei len, const GLubyte *name, const GLfloat *v); -typedef void (APIENTRYP PFNGLPROGRAMNAMEDPARAMETER4DNVPROC) (GLuint id, GLsizei len, const GLubyte *name, GLdouble x, GLdouble y, GLdouble z, GLdouble w); -typedef void (APIENTRYP PFNGLPROGRAMNAMEDPARAMETER4DVNVPROC) (GLuint id, GLsizei len, const GLubyte *name, const GLdouble *v); -typedef void (APIENTRYP PFNGLGETPROGRAMNAMEDPARAMETERFVNVPROC) (GLuint id, GLsizei len, const GLubyte *name, GLfloat *params); -typedef void (APIENTRYP PFNGLGETPROGRAMNAMEDPARAMETERDVNVPROC) (GLuint id, GLsizei len, const GLubyte *name, GLdouble *params); -#ifdef GL_GLEXT_PROTOTYPES -GLAPI void APIENTRY glProgramNamedParameter4fNV (GLuint id, GLsizei len, const GLubyte *name, GLfloat x, GLfloat y, GLfloat z, GLfloat w); -GLAPI void APIENTRY glProgramNamedParameter4fvNV (GLuint id, GLsizei len, const GLubyte *name, const GLfloat *v); -GLAPI void APIENTRY glProgramNamedParameter4dNV (GLuint id, GLsizei len, const GLubyte *name, GLdouble x, GLdouble y, GLdouble z, GLdouble w); -GLAPI void APIENTRY glProgramNamedParameter4dvNV (GLuint id, GLsizei len, const GLubyte *name, const GLdouble *v); -GLAPI void APIENTRY glGetProgramNamedParameterfvNV (GLuint id, GLsizei len, const GLubyte *name, GLfloat *params); -GLAPI void APIENTRY glGetProgramNamedParameterdvNV (GLuint id, GLsizei len, const GLubyte *name, GLdouble *params); -#endif -#endif /* GL_NV_fragment_program */ - -#ifndef GL_NV_fragment_program2 -#define GL_NV_fragment_program2 1 -#define GL_MAX_PROGRAM_EXEC_INSTRUCTIONS_NV 0x88F4 -#define GL_MAX_PROGRAM_CALL_DEPTH_NV 0x88F5 -#define GL_MAX_PROGRAM_IF_DEPTH_NV 0x88F6 -#define GL_MAX_PROGRAM_LOOP_DEPTH_NV 0x88F7 -#define GL_MAX_PROGRAM_LOOP_COUNT_NV 0x88F8 -#endif /* GL_NV_fragment_program2 */ - -#ifndef GL_NV_fragment_program4 -#define GL_NV_fragment_program4 1 -#endif /* GL_NV_fragment_program4 */ - -#ifndef GL_NV_fragment_program_option -#define GL_NV_fragment_program_option 1 -#endif /* GL_NV_fragment_program_option */ - -#ifndef GL_NV_framebuffer_multisample_coverage -#define GL_NV_framebuffer_multisample_coverage 1 -#define GL_RENDERBUFFER_COVERAGE_SAMPLES_NV 0x8CAB -#define GL_RENDERBUFFER_COLOR_SAMPLES_NV 0x8E10 -#define GL_MAX_MULTISAMPLE_COVERAGE_MODES_NV 0x8E11 -#define GL_MULTISAMPLE_COVERAGE_MODES_NV 0x8E12 -typedef void (APIENTRYP PFNGLRENDERBUFFERSTORAGEMULTISAMPLECOVERAGENVPROC) (GLenum target, GLsizei coverageSamples, GLsizei colorSamples, GLenum internalformat, GLsizei width, GLsizei height); -#ifdef GL_GLEXT_PROTOTYPES -GLAPI void APIENTRY glRenderbufferStorageMultisampleCoverageNV (GLenum target, GLsizei coverageSamples, GLsizei colorSamples, GLenum internalformat, GLsizei width, GLsizei height); -#endif -#endif /* GL_NV_framebuffer_multisample_coverage */ - -#ifndef GL_NV_geometry_program4 -#define GL_NV_geometry_program4 1 -#define GL_GEOMETRY_PROGRAM_NV 0x8C26 -#define GL_MAX_PROGRAM_OUTPUT_VERTICES_NV 0x8C27 -#define GL_MAX_PROGRAM_TOTAL_OUTPUT_COMPONENTS_NV 0x8C28 -typedef void (APIENTRYP PFNGLPROGRAMVERTEXLIMITNVPROC) (GLenum target, GLint limit); -typedef void (APIENTRYP PFNGLFRAMEBUFFERTEXTUREEXTPROC) (GLenum target, GLenum attachment, GLuint texture, GLint level); -typedef void (APIENTRYP PFNGLFRAMEBUFFERTEXTURELAYEREXTPROC) (GLenum target, GLenum attachment, GLuint texture, GLint level, GLint layer); -typedef void (APIENTRYP PFNGLFRAMEBUFFERTEXTUREFACEEXTPROC) (GLenum target, GLenum attachment, GLuint texture, GLint level, GLenum face); -#ifdef GL_GLEXT_PROTOTYPES -GLAPI void APIENTRY glProgramVertexLimitNV (GLenum target, GLint limit); -GLAPI void APIENTRY glFramebufferTextureEXT (GLenum target, GLenum attachment, GLuint texture, GLint level); -GLAPI void APIENTRY glFramebufferTextureLayerEXT (GLenum target, GLenum attachment, GLuint texture, GLint level, GLint layer); -GLAPI void APIENTRY glFramebufferTextureFaceEXT (GLenum target, GLenum attachment, GLuint texture, GLint level, GLenum face); -#endif -#endif /* GL_NV_geometry_program4 */ - -#ifndef GL_NV_geometry_shader4 -#define GL_NV_geometry_shader4 1 -#endif /* GL_NV_geometry_shader4 */ - -#ifndef GL_NV_gpu_program4 -#define GL_NV_gpu_program4 1 -#define GL_MIN_PROGRAM_TEXEL_OFFSET_NV 0x8904 -#define GL_MAX_PROGRAM_TEXEL_OFFSET_NV 0x8905 -#define GL_PROGRAM_ATTRIB_COMPONENTS_NV 0x8906 -#define GL_PROGRAM_RESULT_COMPONENTS_NV 0x8907 -#define GL_MAX_PROGRAM_ATTRIB_COMPONENTS_NV 0x8908 -#define GL_MAX_PROGRAM_RESULT_COMPONENTS_NV 0x8909 -#define GL_MAX_PROGRAM_GENERIC_ATTRIBS_NV 0x8DA5 -#define GL_MAX_PROGRAM_GENERIC_RESULTS_NV 0x8DA6 -typedef void (APIENTRYP PFNGLPROGRAMLOCALPARAMETERI4INVPROC) (GLenum target, GLuint index, GLint x, GLint y, GLint z, GLint w); -typedef void (APIENTRYP PFNGLPROGRAMLOCALPARAMETERI4IVNVPROC) (GLenum target, GLuint index, const GLint *params); -typedef void (APIENTRYP PFNGLPROGRAMLOCALPARAMETERSI4IVNVPROC) (GLenum target, GLuint index, GLsizei count, const GLint *params); -typedef void (APIENTRYP PFNGLPROGRAMLOCALPARAMETERI4UINVPROC) (GLenum target, GLuint index, GLuint x, GLuint y, GLuint z, GLuint w); -typedef void (APIENTRYP PFNGLPROGRAMLOCALPARAMETERI4UIVNVPROC) (GLenum target, GLuint index, const GLuint *params); -typedef void (APIENTRYP PFNGLPROGRAMLOCALPARAMETERSI4UIVNVPROC) (GLenum target, GLuint index, GLsizei count, const GLuint *params); -typedef void (APIENTRYP PFNGLPROGRAMENVPARAMETERI4INVPROC) (GLenum target, GLuint index, GLint x, GLint y, GLint z, GLint w); -typedef void (APIENTRYP PFNGLPROGRAMENVPARAMETERI4IVNVPROC) (GLenum target, GLuint index, const GLint *params); -typedef void (APIENTRYP PFNGLPROGRAMENVPARAMETERSI4IVNVPROC) (GLenum target, GLuint index, GLsizei count, const GLint *params); -typedef void (APIENTRYP PFNGLPROGRAMENVPARAMETERI4UINVPROC) (GLenum target, GLuint index, GLuint x, GLuint y, GLuint z, GLuint w); -typedef void (APIENTRYP PFNGLPROGRAMENVPARAMETERI4UIVNVPROC) (GLenum target, GLuint index, const GLuint *params); -typedef void (APIENTRYP PFNGLPROGRAMENVPARAMETERSI4UIVNVPROC) (GLenum target, GLuint index, GLsizei count, const GLuint *params); -typedef void (APIENTRYP PFNGLGETPROGRAMLOCALPARAMETERIIVNVPROC) (GLenum target, GLuint index, GLint *params); -typedef void (APIENTRYP PFNGLGETPROGRAMLOCALPARAMETERIUIVNVPROC) (GLenum target, GLuint index, GLuint *params); -typedef void (APIENTRYP PFNGLGETPROGRAMENVPARAMETERIIVNVPROC) (GLenum target, GLuint index, GLint *params); -typedef void (APIENTRYP PFNGLGETPROGRAMENVPARAMETERIUIVNVPROC) (GLenum target, GLuint index, GLuint *params); -#ifdef GL_GLEXT_PROTOTYPES -GLAPI void APIENTRY glProgramLocalParameterI4iNV (GLenum target, GLuint index, GLint x, GLint y, GLint z, GLint w); -GLAPI void APIENTRY glProgramLocalParameterI4ivNV (GLenum target, GLuint index, const GLint *params); -GLAPI void APIENTRY glProgramLocalParametersI4ivNV (GLenum target, GLuint index, GLsizei count, const GLint *params); -GLAPI void APIENTRY glProgramLocalParameterI4uiNV (GLenum target, GLuint index, GLuint x, GLuint y, GLuint z, GLuint w); -GLAPI void APIENTRY glProgramLocalParameterI4uivNV (GLenum target, GLuint index, const GLuint *params); -GLAPI void APIENTRY glProgramLocalParametersI4uivNV (GLenum target, GLuint index, GLsizei count, const GLuint *params); -GLAPI void APIENTRY glProgramEnvParameterI4iNV (GLenum target, GLuint index, GLint x, GLint y, GLint z, GLint w); -GLAPI void APIENTRY glProgramEnvParameterI4ivNV (GLenum target, GLuint index, const GLint *params); -GLAPI void APIENTRY glProgramEnvParametersI4ivNV (GLenum target, GLuint index, GLsizei count, const GLint *params); -GLAPI void APIENTRY glProgramEnvParameterI4uiNV (GLenum target, GLuint index, GLuint x, GLuint y, GLuint z, GLuint w); -GLAPI void APIENTRY glProgramEnvParameterI4uivNV (GLenum target, GLuint index, const GLuint *params); -GLAPI void APIENTRY glProgramEnvParametersI4uivNV (GLenum target, GLuint index, GLsizei count, const GLuint *params); -GLAPI void APIENTRY glGetProgramLocalParameterIivNV (GLenum target, GLuint index, GLint *params); -GLAPI void APIENTRY glGetProgramLocalParameterIuivNV (GLenum target, GLuint index, GLuint *params); -GLAPI void APIENTRY glGetProgramEnvParameterIivNV (GLenum target, GLuint index, GLint *params); -GLAPI void APIENTRY glGetProgramEnvParameterIuivNV (GLenum target, GLuint index, GLuint *params); -#endif -#endif /* GL_NV_gpu_program4 */ - -#ifndef GL_NV_gpu_program5 -#define GL_NV_gpu_program5 1 -#define GL_MAX_GEOMETRY_PROGRAM_INVOCATIONS_NV 0x8E5A -#define GL_MIN_FRAGMENT_INTERPOLATION_OFFSET_NV 0x8E5B -#define GL_MAX_FRAGMENT_INTERPOLATION_OFFSET_NV 0x8E5C -#define GL_FRAGMENT_PROGRAM_INTERPOLATION_OFFSET_BITS_NV 0x8E5D -#define GL_MIN_PROGRAM_TEXTURE_GATHER_OFFSET_NV 0x8E5E -#define GL_MAX_PROGRAM_TEXTURE_GATHER_OFFSET_NV 0x8E5F -#define GL_MAX_PROGRAM_SUBROUTINE_PARAMETERS_NV 0x8F44 -#define GL_MAX_PROGRAM_SUBROUTINE_NUM_NV 0x8F45 -typedef void (APIENTRYP PFNGLPROGRAMSUBROUTINEPARAMETERSUIVNVPROC) (GLenum target, GLsizei count, const GLuint *params); -typedef void (APIENTRYP PFNGLGETPROGRAMSUBROUTINEPARAMETERUIVNVPROC) (GLenum target, GLuint index, GLuint *param); -#ifdef GL_GLEXT_PROTOTYPES -GLAPI void APIENTRY glProgramSubroutineParametersuivNV (GLenum target, GLsizei count, const GLuint *params); -GLAPI void APIENTRY glGetProgramSubroutineParameteruivNV (GLenum target, GLuint index, GLuint *param); -#endif -#endif /* GL_NV_gpu_program5 */ - -#ifndef GL_NV_gpu_program5_mem_extended -#define GL_NV_gpu_program5_mem_extended 1 -#endif /* GL_NV_gpu_program5_mem_extended */ - -#ifndef GL_NV_gpu_shader5 -#define GL_NV_gpu_shader5 1 -typedef int64_t GLint64EXT; -#define GL_INT64_NV 0x140E -#define GL_UNSIGNED_INT64_NV 0x140F -#define GL_INT8_NV 0x8FE0 -#define GL_INT8_VEC2_NV 0x8FE1 -#define GL_INT8_VEC3_NV 0x8FE2 -#define GL_INT8_VEC4_NV 0x8FE3 -#define GL_INT16_NV 0x8FE4 -#define GL_INT16_VEC2_NV 0x8FE5 -#define GL_INT16_VEC3_NV 0x8FE6 -#define GL_INT16_VEC4_NV 0x8FE7 -#define GL_INT64_VEC2_NV 0x8FE9 -#define GL_INT64_VEC3_NV 0x8FEA -#define GL_INT64_VEC4_NV 0x8FEB -#define GL_UNSIGNED_INT8_NV 0x8FEC -#define GL_UNSIGNED_INT8_VEC2_NV 0x8FED -#define GL_UNSIGNED_INT8_VEC3_NV 0x8FEE -#define GL_UNSIGNED_INT8_VEC4_NV 0x8FEF -#define GL_UNSIGNED_INT16_NV 0x8FF0 -#define GL_UNSIGNED_INT16_VEC2_NV 0x8FF1 -#define GL_UNSIGNED_INT16_VEC3_NV 0x8FF2 -#define GL_UNSIGNED_INT16_VEC4_NV 0x8FF3 -#define GL_UNSIGNED_INT64_VEC2_NV 0x8FF5 -#define GL_UNSIGNED_INT64_VEC3_NV 0x8FF6 -#define GL_UNSIGNED_INT64_VEC4_NV 0x8FF7 -#define GL_FLOAT16_NV 0x8FF8 -#define GL_FLOAT16_VEC2_NV 0x8FF9 -#define GL_FLOAT16_VEC3_NV 0x8FFA -#define GL_FLOAT16_VEC4_NV 0x8FFB -typedef void (APIENTRYP PFNGLUNIFORM1I64NVPROC) (GLint location, GLint64EXT x); -typedef void (APIENTRYP PFNGLUNIFORM2I64NVPROC) (GLint location, GLint64EXT x, GLint64EXT y); -typedef void (APIENTRYP PFNGLUNIFORM3I64NVPROC) (GLint location, GLint64EXT x, GLint64EXT y, GLint64EXT z); -typedef void (APIENTRYP PFNGLUNIFORM4I64NVPROC) (GLint location, GLint64EXT x, GLint64EXT y, GLint64EXT z, GLint64EXT w); -typedef void (APIENTRYP PFNGLUNIFORM1I64VNVPROC) (GLint location, GLsizei count, const GLint64EXT *value); -typedef void (APIENTRYP PFNGLUNIFORM2I64VNVPROC) (GLint location, GLsizei count, const GLint64EXT *value); -typedef void (APIENTRYP PFNGLUNIFORM3I64VNVPROC) (GLint location, GLsizei count, const GLint64EXT *value); -typedef void (APIENTRYP PFNGLUNIFORM4I64VNVPROC) (GLint location, GLsizei count, const GLint64EXT *value); -typedef void (APIENTRYP PFNGLUNIFORM1UI64NVPROC) (GLint location, GLuint64EXT x); -typedef void (APIENTRYP PFNGLUNIFORM2UI64NVPROC) (GLint location, GLuint64EXT x, GLuint64EXT y); -typedef void (APIENTRYP PFNGLUNIFORM3UI64NVPROC) (GLint location, GLuint64EXT x, GLuint64EXT y, GLuint64EXT z); -typedef void (APIENTRYP PFNGLUNIFORM4UI64NVPROC) (GLint location, GLuint64EXT x, GLuint64EXT y, GLuint64EXT z, GLuint64EXT w); -typedef void (APIENTRYP PFNGLUNIFORM1UI64VNVPROC) (GLint location, GLsizei count, const GLuint64EXT *value); -typedef void (APIENTRYP PFNGLUNIFORM2UI64VNVPROC) (GLint location, GLsizei count, const GLuint64EXT *value); -typedef void (APIENTRYP PFNGLUNIFORM3UI64VNVPROC) (GLint location, GLsizei count, const GLuint64EXT *value); -typedef void (APIENTRYP PFNGLUNIFORM4UI64VNVPROC) (GLint location, GLsizei count, const GLuint64EXT *value); -typedef void (APIENTRYP PFNGLGETUNIFORMI64VNVPROC) (GLuint program, GLint location, GLint64EXT *params); -typedef void (APIENTRYP PFNGLPROGRAMUNIFORM1I64NVPROC) (GLuint program, GLint location, GLint64EXT x); -typedef void (APIENTRYP PFNGLPROGRAMUNIFORM2I64NVPROC) (GLuint program, GLint location, GLint64EXT x, GLint64EXT y); -typedef void (APIENTRYP PFNGLPROGRAMUNIFORM3I64NVPROC) (GLuint program, GLint location, GLint64EXT x, GLint64EXT y, GLint64EXT z); -typedef void (APIENTRYP PFNGLPROGRAMUNIFORM4I64NVPROC) (GLuint program, GLint location, GLint64EXT x, GLint64EXT y, GLint64EXT z, GLint64EXT w); -typedef void (APIENTRYP PFNGLPROGRAMUNIFORM1I64VNVPROC) (GLuint program, GLint location, GLsizei count, const GLint64EXT *value); -typedef void (APIENTRYP PFNGLPROGRAMUNIFORM2I64VNVPROC) (GLuint program, GLint location, GLsizei count, const GLint64EXT *value); -typedef void (APIENTRYP PFNGLPROGRAMUNIFORM3I64VNVPROC) (GLuint program, GLint location, GLsizei count, const GLint64EXT *value); -typedef void (APIENTRYP PFNGLPROGRAMUNIFORM4I64VNVPROC) (GLuint program, GLint location, GLsizei count, const GLint64EXT *value); -typedef void (APIENTRYP PFNGLPROGRAMUNIFORM1UI64NVPROC) (GLuint program, GLint location, GLuint64EXT x); -typedef void (APIENTRYP PFNGLPROGRAMUNIFORM2UI64NVPROC) (GLuint program, GLint location, GLuint64EXT x, GLuint64EXT y); -typedef void (APIENTRYP PFNGLPROGRAMUNIFORM3UI64NVPROC) (GLuint program, GLint location, GLuint64EXT x, GLuint64EXT y, GLuint64EXT z); -typedef void (APIENTRYP PFNGLPROGRAMUNIFORM4UI64NVPROC) (GLuint program, GLint location, GLuint64EXT x, GLuint64EXT y, GLuint64EXT z, GLuint64EXT w); -typedef void (APIENTRYP PFNGLPROGRAMUNIFORM1UI64VNVPROC) (GLuint program, GLint location, GLsizei count, const GLuint64EXT *value); -typedef void (APIENTRYP PFNGLPROGRAMUNIFORM2UI64VNVPROC) (GLuint program, GLint location, GLsizei count, const GLuint64EXT *value); -typedef void (APIENTRYP PFNGLPROGRAMUNIFORM3UI64VNVPROC) (GLuint program, GLint location, GLsizei count, const GLuint64EXT *value); -typedef void (APIENTRYP PFNGLPROGRAMUNIFORM4UI64VNVPROC) (GLuint program, GLint location, GLsizei count, const GLuint64EXT *value); -#ifdef GL_GLEXT_PROTOTYPES -GLAPI void APIENTRY glUniform1i64NV (GLint location, GLint64EXT x); -GLAPI void APIENTRY glUniform2i64NV (GLint location, GLint64EXT x, GLint64EXT y); -GLAPI void APIENTRY glUniform3i64NV (GLint location, GLint64EXT x, GLint64EXT y, GLint64EXT z); -GLAPI void APIENTRY glUniform4i64NV (GLint location, GLint64EXT x, GLint64EXT y, GLint64EXT z, GLint64EXT w); -GLAPI void APIENTRY glUniform1i64vNV (GLint location, GLsizei count, const GLint64EXT *value); -GLAPI void APIENTRY glUniform2i64vNV (GLint location, GLsizei count, const GLint64EXT *value); -GLAPI void APIENTRY glUniform3i64vNV (GLint location, GLsizei count, const GLint64EXT *value); -GLAPI void APIENTRY glUniform4i64vNV (GLint location, GLsizei count, const GLint64EXT *value); -GLAPI void APIENTRY glUniform1ui64NV (GLint location, GLuint64EXT x); -GLAPI void APIENTRY glUniform2ui64NV (GLint location, GLuint64EXT x, GLuint64EXT y); -GLAPI void APIENTRY glUniform3ui64NV (GLint location, GLuint64EXT x, GLuint64EXT y, GLuint64EXT z); -GLAPI void APIENTRY glUniform4ui64NV (GLint location, GLuint64EXT x, GLuint64EXT y, GLuint64EXT z, GLuint64EXT w); -GLAPI void APIENTRY glUniform1ui64vNV (GLint location, GLsizei count, const GLuint64EXT *value); -GLAPI void APIENTRY glUniform2ui64vNV (GLint location, GLsizei count, const GLuint64EXT *value); -GLAPI void APIENTRY glUniform3ui64vNV (GLint location, GLsizei count, const GLuint64EXT *value); -GLAPI void APIENTRY glUniform4ui64vNV (GLint location, GLsizei count, const GLuint64EXT *value); -GLAPI void APIENTRY glGetUniformi64vNV (GLuint program, GLint location, GLint64EXT *params); -GLAPI void APIENTRY glProgramUniform1i64NV (GLuint program, GLint location, GLint64EXT x); -GLAPI void APIENTRY glProgramUniform2i64NV (GLuint program, GLint location, GLint64EXT x, GLint64EXT y); -GLAPI void APIENTRY glProgramUniform3i64NV (GLuint program, GLint location, GLint64EXT x, GLint64EXT y, GLint64EXT z); -GLAPI void APIENTRY glProgramUniform4i64NV (GLuint program, GLint location, GLint64EXT x, GLint64EXT y, GLint64EXT z, GLint64EXT w); -GLAPI void APIENTRY glProgramUniform1i64vNV (GLuint program, GLint location, GLsizei count, const GLint64EXT *value); -GLAPI void APIENTRY glProgramUniform2i64vNV (GLuint program, GLint location, GLsizei count, const GLint64EXT *value); -GLAPI void APIENTRY glProgramUniform3i64vNV (GLuint program, GLint location, GLsizei count, const GLint64EXT *value); -GLAPI void APIENTRY glProgramUniform4i64vNV (GLuint program, GLint location, GLsizei count, const GLint64EXT *value); -GLAPI void APIENTRY glProgramUniform1ui64NV (GLuint program, GLint location, GLuint64EXT x); -GLAPI void APIENTRY glProgramUniform2ui64NV (GLuint program, GLint location, GLuint64EXT x, GLuint64EXT y); -GLAPI void APIENTRY glProgramUniform3ui64NV (GLuint program, GLint location, GLuint64EXT x, GLuint64EXT y, GLuint64EXT z); -GLAPI void APIENTRY glProgramUniform4ui64NV (GLuint program, GLint location, GLuint64EXT x, GLuint64EXT y, GLuint64EXT z, GLuint64EXT w); -GLAPI void APIENTRY glProgramUniform1ui64vNV (GLuint program, GLint location, GLsizei count, const GLuint64EXT *value); -GLAPI void APIENTRY glProgramUniform2ui64vNV (GLuint program, GLint location, GLsizei count, const GLuint64EXT *value); -GLAPI void APIENTRY glProgramUniform3ui64vNV (GLuint program, GLint location, GLsizei count, const GLuint64EXT *value); -GLAPI void APIENTRY glProgramUniform4ui64vNV (GLuint program, GLint location, GLsizei count, const GLuint64EXT *value); -#endif -#endif /* GL_NV_gpu_shader5 */ - -#ifndef GL_NV_half_float -#define GL_NV_half_float 1 -typedef unsigned short GLhalfNV; -#define GL_HALF_FLOAT_NV 0x140B -typedef void (APIENTRYP PFNGLVERTEX2HNVPROC) (GLhalfNV x, GLhalfNV y); -typedef void (APIENTRYP PFNGLVERTEX2HVNVPROC) (const GLhalfNV *v); -typedef void (APIENTRYP PFNGLVERTEX3HNVPROC) (GLhalfNV x, GLhalfNV y, GLhalfNV z); -typedef void (APIENTRYP PFNGLVERTEX3HVNVPROC) (const GLhalfNV *v); -typedef void (APIENTRYP PFNGLVERTEX4HNVPROC) (GLhalfNV x, GLhalfNV y, GLhalfNV z, GLhalfNV w); -typedef void (APIENTRYP PFNGLVERTEX4HVNVPROC) (const GLhalfNV *v); -typedef void (APIENTRYP PFNGLNORMAL3HNVPROC) (GLhalfNV nx, GLhalfNV ny, GLhalfNV nz); -typedef void (APIENTRYP PFNGLNORMAL3HVNVPROC) (const GLhalfNV *v); -typedef void (APIENTRYP PFNGLCOLOR3HNVPROC) (GLhalfNV red, GLhalfNV green, GLhalfNV blue); -typedef void (APIENTRYP PFNGLCOLOR3HVNVPROC) (const GLhalfNV *v); -typedef void (APIENTRYP PFNGLCOLOR4HNVPROC) (GLhalfNV red, GLhalfNV green, GLhalfNV blue, GLhalfNV alpha); -typedef void (APIENTRYP PFNGLCOLOR4HVNVPROC) (const GLhalfNV *v); -typedef void (APIENTRYP PFNGLTEXCOORD1HNVPROC) (GLhalfNV s); -typedef void (APIENTRYP PFNGLTEXCOORD1HVNVPROC) (const GLhalfNV *v); -typedef void (APIENTRYP PFNGLTEXCOORD2HNVPROC) (GLhalfNV s, GLhalfNV t); -typedef void (APIENTRYP PFNGLTEXCOORD2HVNVPROC) (const GLhalfNV *v); -typedef void (APIENTRYP PFNGLTEXCOORD3HNVPROC) (GLhalfNV s, GLhalfNV t, GLhalfNV r); -typedef void (APIENTRYP PFNGLTEXCOORD3HVNVPROC) (const GLhalfNV *v); -typedef void (APIENTRYP PFNGLTEXCOORD4HNVPROC) (GLhalfNV s, GLhalfNV t, GLhalfNV r, GLhalfNV q); -typedef void (APIENTRYP PFNGLTEXCOORD4HVNVPROC) (const GLhalfNV *v); -typedef void (APIENTRYP PFNGLMULTITEXCOORD1HNVPROC) (GLenum target, GLhalfNV s); -typedef void (APIENTRYP PFNGLMULTITEXCOORD1HVNVPROC) (GLenum target, const GLhalfNV *v); -typedef void (APIENTRYP PFNGLMULTITEXCOORD2HNVPROC) (GLenum target, GLhalfNV s, GLhalfNV t); -typedef void (APIENTRYP PFNGLMULTITEXCOORD2HVNVPROC) (GLenum target, const GLhalfNV *v); -typedef void (APIENTRYP PFNGLMULTITEXCOORD3HNVPROC) (GLenum target, GLhalfNV s, GLhalfNV t, GLhalfNV r); -typedef void (APIENTRYP PFNGLMULTITEXCOORD3HVNVPROC) (GLenum target, const GLhalfNV *v); -typedef void (APIENTRYP PFNGLMULTITEXCOORD4HNVPROC) (GLenum target, GLhalfNV s, GLhalfNV t, GLhalfNV r, GLhalfNV q); -typedef void (APIENTRYP PFNGLMULTITEXCOORD4HVNVPROC) (GLenum target, const GLhalfNV *v); -typedef void (APIENTRYP PFNGLFOGCOORDHNVPROC) (GLhalfNV fog); -typedef void (APIENTRYP PFNGLFOGCOORDHVNVPROC) (const GLhalfNV *fog); -typedef void (APIENTRYP PFNGLSECONDARYCOLOR3HNVPROC) (GLhalfNV red, GLhalfNV green, GLhalfNV blue); -typedef void (APIENTRYP PFNGLSECONDARYCOLOR3HVNVPROC) (const GLhalfNV *v); -typedef void (APIENTRYP PFNGLVERTEXWEIGHTHNVPROC) (GLhalfNV weight); -typedef void (APIENTRYP PFNGLVERTEXWEIGHTHVNVPROC) (const GLhalfNV *weight); -typedef void (APIENTRYP PFNGLVERTEXATTRIB1HNVPROC) (GLuint index, GLhalfNV x); -typedef void (APIENTRYP PFNGLVERTEXATTRIB1HVNVPROC) (GLuint index, const GLhalfNV *v); -typedef void (APIENTRYP PFNGLVERTEXATTRIB2HNVPROC) (GLuint index, GLhalfNV x, GLhalfNV y); -typedef void (APIENTRYP PFNGLVERTEXATTRIB2HVNVPROC) (GLuint index, const GLhalfNV *v); -typedef void (APIENTRYP PFNGLVERTEXATTRIB3HNVPROC) (GLuint index, GLhalfNV x, GLhalfNV y, GLhalfNV z); -typedef void (APIENTRYP PFNGLVERTEXATTRIB3HVNVPROC) (GLuint index, const GLhalfNV *v); -typedef void (APIENTRYP PFNGLVERTEXATTRIB4HNVPROC) (GLuint index, GLhalfNV x, GLhalfNV y, GLhalfNV z, GLhalfNV w); -typedef void (APIENTRYP PFNGLVERTEXATTRIB4HVNVPROC) (GLuint index, const GLhalfNV *v); -typedef void (APIENTRYP PFNGLVERTEXATTRIBS1HVNVPROC) (GLuint index, GLsizei n, const GLhalfNV *v); -typedef void (APIENTRYP PFNGLVERTEXATTRIBS2HVNVPROC) (GLuint index, GLsizei n, const GLhalfNV *v); -typedef void (APIENTRYP PFNGLVERTEXATTRIBS3HVNVPROC) (GLuint index, GLsizei n, const GLhalfNV *v); -typedef void (APIENTRYP PFNGLVERTEXATTRIBS4HVNVPROC) (GLuint index, GLsizei n, const GLhalfNV *v); -#ifdef GL_GLEXT_PROTOTYPES -GLAPI void APIENTRY glVertex2hNV (GLhalfNV x, GLhalfNV y); -GLAPI void APIENTRY glVertex2hvNV (const GLhalfNV *v); -GLAPI void APIENTRY glVertex3hNV (GLhalfNV x, GLhalfNV y, GLhalfNV z); -GLAPI void APIENTRY glVertex3hvNV (const GLhalfNV *v); -GLAPI void APIENTRY glVertex4hNV (GLhalfNV x, GLhalfNV y, GLhalfNV z, GLhalfNV w); -GLAPI void APIENTRY glVertex4hvNV (const GLhalfNV *v); -GLAPI void APIENTRY glNormal3hNV (GLhalfNV nx, GLhalfNV ny, GLhalfNV nz); -GLAPI void APIENTRY glNormal3hvNV (const GLhalfNV *v); -GLAPI void APIENTRY glColor3hNV (GLhalfNV red, GLhalfNV green, GLhalfNV blue); -GLAPI void APIENTRY glColor3hvNV (const GLhalfNV *v); -GLAPI void APIENTRY glColor4hNV (GLhalfNV red, GLhalfNV green, GLhalfNV blue, GLhalfNV alpha); -GLAPI void APIENTRY glColor4hvNV (const GLhalfNV *v); -GLAPI void APIENTRY glTexCoord1hNV (GLhalfNV s); -GLAPI void APIENTRY glTexCoord1hvNV (const GLhalfNV *v); -GLAPI void APIENTRY glTexCoord2hNV (GLhalfNV s, GLhalfNV t); -GLAPI void APIENTRY glTexCoord2hvNV (const GLhalfNV *v); -GLAPI void APIENTRY glTexCoord3hNV (GLhalfNV s, GLhalfNV t, GLhalfNV r); -GLAPI void APIENTRY glTexCoord3hvNV (const GLhalfNV *v); -GLAPI void APIENTRY glTexCoord4hNV (GLhalfNV s, GLhalfNV t, GLhalfNV r, GLhalfNV q); -GLAPI void APIENTRY glTexCoord4hvNV (const GLhalfNV *v); -GLAPI void APIENTRY glMultiTexCoord1hNV (GLenum target, GLhalfNV s); -GLAPI void APIENTRY glMultiTexCoord1hvNV (GLenum target, const GLhalfNV *v); -GLAPI void APIENTRY glMultiTexCoord2hNV (GLenum target, GLhalfNV s, GLhalfNV t); -GLAPI void APIENTRY glMultiTexCoord2hvNV (GLenum target, const GLhalfNV *v); -GLAPI void APIENTRY glMultiTexCoord3hNV (GLenum target, GLhalfNV s, GLhalfNV t, GLhalfNV r); -GLAPI void APIENTRY glMultiTexCoord3hvNV (GLenum target, const GLhalfNV *v); -GLAPI void APIENTRY glMultiTexCoord4hNV (GLenum target, GLhalfNV s, GLhalfNV t, GLhalfNV r, GLhalfNV q); -GLAPI void APIENTRY glMultiTexCoord4hvNV (GLenum target, const GLhalfNV *v); -GLAPI void APIENTRY glFogCoordhNV (GLhalfNV fog); -GLAPI void APIENTRY glFogCoordhvNV (const GLhalfNV *fog); -GLAPI void APIENTRY glSecondaryColor3hNV (GLhalfNV red, GLhalfNV green, GLhalfNV blue); -GLAPI void APIENTRY glSecondaryColor3hvNV (const GLhalfNV *v); -GLAPI void APIENTRY glVertexWeighthNV (GLhalfNV weight); -GLAPI void APIENTRY glVertexWeighthvNV (const GLhalfNV *weight); -GLAPI void APIENTRY glVertexAttrib1hNV (GLuint index, GLhalfNV x); -GLAPI void APIENTRY glVertexAttrib1hvNV (GLuint index, const GLhalfNV *v); -GLAPI void APIENTRY glVertexAttrib2hNV (GLuint index, GLhalfNV x, GLhalfNV y); -GLAPI void APIENTRY glVertexAttrib2hvNV (GLuint index, const GLhalfNV *v); -GLAPI void APIENTRY glVertexAttrib3hNV (GLuint index, GLhalfNV x, GLhalfNV y, GLhalfNV z); -GLAPI void APIENTRY glVertexAttrib3hvNV (GLuint index, const GLhalfNV *v); -GLAPI void APIENTRY glVertexAttrib4hNV (GLuint index, GLhalfNV x, GLhalfNV y, GLhalfNV z, GLhalfNV w); -GLAPI void APIENTRY glVertexAttrib4hvNV (GLuint index, const GLhalfNV *v); -GLAPI void APIENTRY glVertexAttribs1hvNV (GLuint index, GLsizei n, const GLhalfNV *v); -GLAPI void APIENTRY glVertexAttribs2hvNV (GLuint index, GLsizei n, const GLhalfNV *v); -GLAPI void APIENTRY glVertexAttribs3hvNV (GLuint index, GLsizei n, const GLhalfNV *v); -GLAPI void APIENTRY glVertexAttribs4hvNV (GLuint index, GLsizei n, const GLhalfNV *v); -#endif -#endif /* GL_NV_half_float */ - -#ifndef GL_NV_light_max_exponent -#define GL_NV_light_max_exponent 1 -#define GL_MAX_SHININESS_NV 0x8504 -#define GL_MAX_SPOT_EXPONENT_NV 0x8505 -#endif /* GL_NV_light_max_exponent */ - -#ifndef GL_NV_multisample_coverage -#define GL_NV_multisample_coverage 1 -#define GL_COLOR_SAMPLES_NV 0x8E20 -#endif /* GL_NV_multisample_coverage */ - -#ifndef GL_NV_multisample_filter_hint -#define GL_NV_multisample_filter_hint 1 -#define GL_MULTISAMPLE_FILTER_HINT_NV 0x8534 -#endif /* GL_NV_multisample_filter_hint */ - -#ifndef GL_NV_occlusion_query -#define GL_NV_occlusion_query 1 -#define GL_PIXEL_COUNTER_BITS_NV 0x8864 -#define GL_CURRENT_OCCLUSION_QUERY_ID_NV 0x8865 -#define GL_PIXEL_COUNT_NV 0x8866 -#define GL_PIXEL_COUNT_AVAILABLE_NV 0x8867 -typedef void (APIENTRYP PFNGLGENOCCLUSIONQUERIESNVPROC) (GLsizei n, GLuint *ids); -typedef void (APIENTRYP PFNGLDELETEOCCLUSIONQUERIESNVPROC) (GLsizei n, const GLuint *ids); -typedef GLboolean (APIENTRYP PFNGLISOCCLUSIONQUERYNVPROC) (GLuint id); -typedef void (APIENTRYP PFNGLBEGINOCCLUSIONQUERYNVPROC) (GLuint id); -typedef void (APIENTRYP PFNGLENDOCCLUSIONQUERYNVPROC) (void); -typedef void (APIENTRYP PFNGLGETOCCLUSIONQUERYIVNVPROC) (GLuint id, GLenum pname, GLint *params); -typedef void (APIENTRYP PFNGLGETOCCLUSIONQUERYUIVNVPROC) (GLuint id, GLenum pname, GLuint *params); -#ifdef GL_GLEXT_PROTOTYPES -GLAPI void APIENTRY glGenOcclusionQueriesNV (GLsizei n, GLuint *ids); -GLAPI void APIENTRY glDeleteOcclusionQueriesNV (GLsizei n, const GLuint *ids); -GLAPI GLboolean APIENTRY glIsOcclusionQueryNV (GLuint id); -GLAPI void APIENTRY glBeginOcclusionQueryNV (GLuint id); -GLAPI void APIENTRY glEndOcclusionQueryNV (void); -GLAPI void APIENTRY glGetOcclusionQueryivNV (GLuint id, GLenum pname, GLint *params); -GLAPI void APIENTRY glGetOcclusionQueryuivNV (GLuint id, GLenum pname, GLuint *params); -#endif -#endif /* GL_NV_occlusion_query */ - -#ifndef GL_NV_packed_depth_stencil -#define GL_NV_packed_depth_stencil 1 -#define GL_DEPTH_STENCIL_NV 0x84F9 -#define GL_UNSIGNED_INT_24_8_NV 0x84FA -#endif /* GL_NV_packed_depth_stencil */ - -#ifndef GL_NV_parameter_buffer_object -#define GL_NV_parameter_buffer_object 1 -#define GL_MAX_PROGRAM_PARAMETER_BUFFER_BINDINGS_NV 0x8DA0 -#define GL_MAX_PROGRAM_PARAMETER_BUFFER_SIZE_NV 0x8DA1 -#define GL_VERTEX_PROGRAM_PARAMETER_BUFFER_NV 0x8DA2 -#define GL_GEOMETRY_PROGRAM_PARAMETER_BUFFER_NV 0x8DA3 -#define GL_FRAGMENT_PROGRAM_PARAMETER_BUFFER_NV 0x8DA4 -typedef void (APIENTRYP PFNGLPROGRAMBUFFERPARAMETERSFVNVPROC) (GLenum target, GLuint bindingIndex, GLuint wordIndex, GLsizei count, const GLfloat *params); -typedef void (APIENTRYP PFNGLPROGRAMBUFFERPARAMETERSIIVNVPROC) (GLenum target, GLuint bindingIndex, GLuint wordIndex, GLsizei count, const GLint *params); -typedef void (APIENTRYP PFNGLPROGRAMBUFFERPARAMETERSIUIVNVPROC) (GLenum target, GLuint bindingIndex, GLuint wordIndex, GLsizei count, const GLuint *params); -#ifdef GL_GLEXT_PROTOTYPES -GLAPI void APIENTRY glProgramBufferParametersfvNV (GLenum target, GLuint bindingIndex, GLuint wordIndex, GLsizei count, const GLfloat *params); -GLAPI void APIENTRY glProgramBufferParametersIivNV (GLenum target, GLuint bindingIndex, GLuint wordIndex, GLsizei count, const GLint *params); -GLAPI void APIENTRY glProgramBufferParametersIuivNV (GLenum target, GLuint bindingIndex, GLuint wordIndex, GLsizei count, const GLuint *params); -#endif -#endif /* GL_NV_parameter_buffer_object */ - -#ifndef GL_NV_parameter_buffer_object2 -#define GL_NV_parameter_buffer_object2 1 -#endif /* GL_NV_parameter_buffer_object2 */ - -#ifndef GL_NV_path_rendering -#define GL_NV_path_rendering 1 -#define GL_PATH_FORMAT_SVG_NV 0x9070 -#define GL_PATH_FORMAT_PS_NV 0x9071 -#define GL_STANDARD_FONT_NAME_NV 0x9072 -#define GL_SYSTEM_FONT_NAME_NV 0x9073 -#define GL_FILE_NAME_NV 0x9074 -#define GL_PATH_STROKE_WIDTH_NV 0x9075 -#define GL_PATH_END_CAPS_NV 0x9076 -#define GL_PATH_INITIAL_END_CAP_NV 0x9077 -#define GL_PATH_TERMINAL_END_CAP_NV 0x9078 -#define GL_PATH_JOIN_STYLE_NV 0x9079 -#define GL_PATH_MITER_LIMIT_NV 0x907A -#define GL_PATH_DASH_CAPS_NV 0x907B -#define GL_PATH_INITIAL_DASH_CAP_NV 0x907C -#define GL_PATH_TERMINAL_DASH_CAP_NV 0x907D -#define GL_PATH_DASH_OFFSET_NV 0x907E -#define GL_PATH_CLIENT_LENGTH_NV 0x907F -#define GL_PATH_FILL_MODE_NV 0x9080 -#define GL_PATH_FILL_MASK_NV 0x9081 -#define GL_PATH_FILL_COVER_MODE_NV 0x9082 -#define GL_PATH_STROKE_COVER_MODE_NV 0x9083 -#define GL_PATH_STROKE_MASK_NV 0x9084 -#define GL_COUNT_UP_NV 0x9088 -#define GL_COUNT_DOWN_NV 0x9089 -#define GL_PATH_OBJECT_BOUNDING_BOX_NV 0x908A -#define GL_CONVEX_HULL_NV 0x908B -#define GL_BOUNDING_BOX_NV 0x908D -#define GL_TRANSLATE_X_NV 0x908E -#define GL_TRANSLATE_Y_NV 0x908F -#define GL_TRANSLATE_2D_NV 0x9090 -#define GL_TRANSLATE_3D_NV 0x9091 -#define GL_AFFINE_2D_NV 0x9092 -#define GL_AFFINE_3D_NV 0x9094 -#define GL_TRANSPOSE_AFFINE_2D_NV 0x9096 -#define GL_TRANSPOSE_AFFINE_3D_NV 0x9098 -#define GL_UTF8_NV 0x909A -#define GL_UTF16_NV 0x909B -#define GL_BOUNDING_BOX_OF_BOUNDING_BOXES_NV 0x909C -#define GL_PATH_COMMAND_COUNT_NV 0x909D -#define GL_PATH_COORD_COUNT_NV 0x909E -#define GL_PATH_DASH_ARRAY_COUNT_NV 0x909F -#define GL_PATH_COMPUTED_LENGTH_NV 0x90A0 -#define GL_PATH_FILL_BOUNDING_BOX_NV 0x90A1 -#define GL_PATH_STROKE_BOUNDING_BOX_NV 0x90A2 -#define GL_SQUARE_NV 0x90A3 -#define GL_ROUND_NV 0x90A4 -#define GL_TRIANGULAR_NV 0x90A5 -#define GL_BEVEL_NV 0x90A6 -#define GL_MITER_REVERT_NV 0x90A7 -#define GL_MITER_TRUNCATE_NV 0x90A8 -#define GL_SKIP_MISSING_GLYPH_NV 0x90A9 -#define GL_USE_MISSING_GLYPH_NV 0x90AA -#define GL_PATH_ERROR_POSITION_NV 0x90AB -#define GL_PATH_FOG_GEN_MODE_NV 0x90AC -#define GL_ACCUM_ADJACENT_PAIRS_NV 0x90AD -#define GL_ADJACENT_PAIRS_NV 0x90AE -#define GL_FIRST_TO_REST_NV 0x90AF -#define GL_PATH_GEN_MODE_NV 0x90B0 -#define GL_PATH_GEN_COEFF_NV 0x90B1 -#define GL_PATH_GEN_COLOR_FORMAT_NV 0x90B2 -#define GL_PATH_GEN_COMPONENTS_NV 0x90B3 -#define GL_PATH_STENCIL_FUNC_NV 0x90B7 -#define GL_PATH_STENCIL_REF_NV 0x90B8 -#define GL_PATH_STENCIL_VALUE_MASK_NV 0x90B9 -#define GL_PATH_STENCIL_DEPTH_OFFSET_FACTOR_NV 0x90BD -#define GL_PATH_STENCIL_DEPTH_OFFSET_UNITS_NV 0x90BE -#define GL_PATH_COVER_DEPTH_FUNC_NV 0x90BF -#define GL_PATH_DASH_OFFSET_RESET_NV 0x90B4 -#define GL_MOVE_TO_RESETS_NV 0x90B5 -#define GL_MOVE_TO_CONTINUES_NV 0x90B6 -#define GL_CLOSE_PATH_NV 0x00 -#define GL_MOVE_TO_NV 0x02 -#define GL_RELATIVE_MOVE_TO_NV 0x03 -#define GL_LINE_TO_NV 0x04 -#define GL_RELATIVE_LINE_TO_NV 0x05 -#define GL_HORIZONTAL_LINE_TO_NV 0x06 -#define GL_RELATIVE_HORIZONTAL_LINE_TO_NV 0x07 -#define GL_VERTICAL_LINE_TO_NV 0x08 -#define GL_RELATIVE_VERTICAL_LINE_TO_NV 0x09 -#define GL_QUADRATIC_CURVE_TO_NV 0x0A -#define GL_RELATIVE_QUADRATIC_CURVE_TO_NV 0x0B -#define GL_CUBIC_CURVE_TO_NV 0x0C -#define GL_RELATIVE_CUBIC_CURVE_TO_NV 0x0D -#define GL_SMOOTH_QUADRATIC_CURVE_TO_NV 0x0E -#define GL_RELATIVE_SMOOTH_QUADRATIC_CURVE_TO_NV 0x0F -#define GL_SMOOTH_CUBIC_CURVE_TO_NV 0x10 -#define GL_RELATIVE_SMOOTH_CUBIC_CURVE_TO_NV 0x11 -#define GL_SMALL_CCW_ARC_TO_NV 0x12 -#define GL_RELATIVE_SMALL_CCW_ARC_TO_NV 0x13 -#define GL_SMALL_CW_ARC_TO_NV 0x14 -#define GL_RELATIVE_SMALL_CW_ARC_TO_NV 0x15 -#define GL_LARGE_CCW_ARC_TO_NV 0x16 -#define GL_RELATIVE_LARGE_CCW_ARC_TO_NV 0x17 -#define GL_LARGE_CW_ARC_TO_NV 0x18 -#define GL_RELATIVE_LARGE_CW_ARC_TO_NV 0x19 -#define GL_RESTART_PATH_NV 0xF0 -#define GL_DUP_FIRST_CUBIC_CURVE_TO_NV 0xF2 -#define GL_DUP_LAST_CUBIC_CURVE_TO_NV 0xF4 -#define GL_RECT_NV 0xF6 -#define GL_CIRCULAR_CCW_ARC_TO_NV 0xF8 -#define GL_CIRCULAR_CW_ARC_TO_NV 0xFA -#define GL_CIRCULAR_TANGENT_ARC_TO_NV 0xFC -#define GL_ARC_TO_NV 0xFE -#define GL_RELATIVE_ARC_TO_NV 0xFF -#define GL_BOLD_BIT_NV 0x01 -#define GL_ITALIC_BIT_NV 0x02 -#define GL_GLYPH_WIDTH_BIT_NV 0x01 -#define GL_GLYPH_HEIGHT_BIT_NV 0x02 -#define GL_GLYPH_HORIZONTAL_BEARING_X_BIT_NV 0x04 -#define GL_GLYPH_HORIZONTAL_BEARING_Y_BIT_NV 0x08 -#define GL_GLYPH_HORIZONTAL_BEARING_ADVANCE_BIT_NV 0x10 -#define GL_GLYPH_VERTICAL_BEARING_X_BIT_NV 0x20 -#define GL_GLYPH_VERTICAL_BEARING_Y_BIT_NV 0x40 -#define GL_GLYPH_VERTICAL_BEARING_ADVANCE_BIT_NV 0x80 -#define GL_GLYPH_HAS_KERNING_BIT_NV 0x100 -#define GL_FONT_X_MIN_BOUNDS_BIT_NV 0x00010000 -#define GL_FONT_Y_MIN_BOUNDS_BIT_NV 0x00020000 -#define GL_FONT_X_MAX_BOUNDS_BIT_NV 0x00040000 -#define GL_FONT_Y_MAX_BOUNDS_BIT_NV 0x00080000 -#define GL_FONT_UNITS_PER_EM_BIT_NV 0x00100000 -#define GL_FONT_ASCENDER_BIT_NV 0x00200000 -#define GL_FONT_DESCENDER_BIT_NV 0x00400000 -#define GL_FONT_HEIGHT_BIT_NV 0x00800000 -#define GL_FONT_MAX_ADVANCE_WIDTH_BIT_NV 0x01000000 -#define GL_FONT_MAX_ADVANCE_HEIGHT_BIT_NV 0x02000000 -#define GL_FONT_UNDERLINE_POSITION_BIT_NV 0x04000000 -#define GL_FONT_UNDERLINE_THICKNESS_BIT_NV 0x08000000 -#define GL_FONT_HAS_KERNING_BIT_NV 0x10000000 -#define GL_PRIMARY_COLOR_NV 0x852C -#define GL_SECONDARY_COLOR_NV 0x852D -typedef GLuint (APIENTRYP PFNGLGENPATHSNVPROC) (GLsizei range); -typedef void (APIENTRYP PFNGLDELETEPATHSNVPROC) (GLuint path, GLsizei range); -typedef GLboolean (APIENTRYP PFNGLISPATHNVPROC) (GLuint path); -typedef void (APIENTRYP PFNGLPATHCOMMANDSNVPROC) (GLuint path, GLsizei numCommands, const GLubyte *commands, GLsizei numCoords, GLenum coordType, const void *coords); -typedef void (APIENTRYP PFNGLPATHCOORDSNVPROC) (GLuint path, GLsizei numCoords, GLenum coordType, const void *coords); -typedef void (APIENTRYP PFNGLPATHSUBCOMMANDSNVPROC) (GLuint path, GLsizei commandStart, GLsizei commandsToDelete, GLsizei numCommands, const GLubyte *commands, GLsizei numCoords, GLenum coordType, const void *coords); -typedef void (APIENTRYP PFNGLPATHSUBCOORDSNVPROC) (GLuint path, GLsizei coordStart, GLsizei numCoords, GLenum coordType, const void *coords); -typedef void (APIENTRYP PFNGLPATHSTRINGNVPROC) (GLuint path, GLenum format, GLsizei length, const void *pathString); -typedef void (APIENTRYP PFNGLPATHGLYPHSNVPROC) (GLuint firstPathName, GLenum fontTarget, const void *fontName, GLbitfield fontStyle, GLsizei numGlyphs, GLenum type, const void *charcodes, GLenum handleMissingGlyphs, GLuint pathParameterTemplate, GLfloat emScale); -typedef void (APIENTRYP PFNGLPATHGLYPHRANGENVPROC) (GLuint firstPathName, GLenum fontTarget, const void *fontName, GLbitfield fontStyle, GLuint firstGlyph, GLsizei numGlyphs, GLenum handleMissingGlyphs, GLuint pathParameterTemplate, GLfloat emScale); -typedef void (APIENTRYP PFNGLWEIGHTPATHSNVPROC) (GLuint resultPath, GLsizei numPaths, const GLuint *paths, const GLfloat *weights); -typedef void (APIENTRYP PFNGLCOPYPATHNVPROC) (GLuint resultPath, GLuint srcPath); -typedef void (APIENTRYP PFNGLINTERPOLATEPATHSNVPROC) (GLuint resultPath, GLuint pathA, GLuint pathB, GLfloat weight); -typedef void (APIENTRYP PFNGLTRANSFORMPATHNVPROC) (GLuint resultPath, GLuint srcPath, GLenum transformType, const GLfloat *transformValues); -typedef void (APIENTRYP PFNGLPATHPARAMETERIVNVPROC) (GLuint path, GLenum pname, const GLint *value); -typedef void (APIENTRYP PFNGLPATHPARAMETERINVPROC) (GLuint path, GLenum pname, GLint value); -typedef void (APIENTRYP PFNGLPATHPARAMETERFVNVPROC) (GLuint path, GLenum pname, const GLfloat *value); -typedef void (APIENTRYP PFNGLPATHPARAMETERFNVPROC) (GLuint path, GLenum pname, GLfloat value); -typedef void (APIENTRYP PFNGLPATHDASHARRAYNVPROC) (GLuint path, GLsizei dashCount, const GLfloat *dashArray); -typedef void (APIENTRYP PFNGLPATHSTENCILFUNCNVPROC) (GLenum func, GLint ref, GLuint mask); -typedef void (APIENTRYP PFNGLPATHSTENCILDEPTHOFFSETNVPROC) (GLfloat factor, GLfloat units); -typedef void (APIENTRYP PFNGLSTENCILFILLPATHNVPROC) (GLuint path, GLenum fillMode, GLuint mask); -typedef void (APIENTRYP PFNGLSTENCILSTROKEPATHNVPROC) (GLuint path, GLint reference, GLuint mask); -typedef void (APIENTRYP PFNGLSTENCILFILLPATHINSTANCEDNVPROC) (GLsizei numPaths, GLenum pathNameType, const void *paths, GLuint pathBase, GLenum fillMode, GLuint mask, GLenum transformType, const GLfloat *transformValues); -typedef void (APIENTRYP PFNGLSTENCILSTROKEPATHINSTANCEDNVPROC) (GLsizei numPaths, GLenum pathNameType, const void *paths, GLuint pathBase, GLint reference, GLuint mask, GLenum transformType, const GLfloat *transformValues); -typedef void (APIENTRYP PFNGLPATHCOVERDEPTHFUNCNVPROC) (GLenum func); -typedef void (APIENTRYP PFNGLPATHCOLORGENNVPROC) (GLenum color, GLenum genMode, GLenum colorFormat, const GLfloat *coeffs); -typedef void (APIENTRYP PFNGLPATHTEXGENNVPROC) (GLenum texCoordSet, GLenum genMode, GLint components, const GLfloat *coeffs); -typedef void (APIENTRYP PFNGLPATHFOGGENNVPROC) (GLenum genMode); -typedef void (APIENTRYP PFNGLCOVERFILLPATHNVPROC) (GLuint path, GLenum coverMode); -typedef void (APIENTRYP PFNGLCOVERSTROKEPATHNVPROC) (GLuint path, GLenum coverMode); -typedef void (APIENTRYP PFNGLCOVERFILLPATHINSTANCEDNVPROC) (GLsizei numPaths, GLenum pathNameType, const void *paths, GLuint pathBase, GLenum coverMode, GLenum transformType, const GLfloat *transformValues); -typedef void (APIENTRYP PFNGLCOVERSTROKEPATHINSTANCEDNVPROC) (GLsizei numPaths, GLenum pathNameType, const void *paths, GLuint pathBase, GLenum coverMode, GLenum transformType, const GLfloat *transformValues); -typedef void (APIENTRYP PFNGLGETPATHPARAMETERIVNVPROC) (GLuint path, GLenum pname, GLint *value); -typedef void (APIENTRYP PFNGLGETPATHPARAMETERFVNVPROC) (GLuint path, GLenum pname, GLfloat *value); -typedef void (APIENTRYP PFNGLGETPATHCOMMANDSNVPROC) (GLuint path, GLubyte *commands); -typedef void (APIENTRYP PFNGLGETPATHCOORDSNVPROC) (GLuint path, GLfloat *coords); -typedef void (APIENTRYP PFNGLGETPATHDASHARRAYNVPROC) (GLuint path, GLfloat *dashArray); -typedef void (APIENTRYP PFNGLGETPATHMETRICSNVPROC) (GLbitfield metricQueryMask, GLsizei numPaths, GLenum pathNameType, const void *paths, GLuint pathBase, GLsizei stride, GLfloat *metrics); -typedef void (APIENTRYP PFNGLGETPATHMETRICRANGENVPROC) (GLbitfield metricQueryMask, GLuint firstPathName, GLsizei numPaths, GLsizei stride, GLfloat *metrics); -typedef void (APIENTRYP PFNGLGETPATHSPACINGNVPROC) (GLenum pathListMode, GLsizei numPaths, GLenum pathNameType, const void *paths, GLuint pathBase, GLfloat advanceScale, GLfloat kerningScale, GLenum transformType, GLfloat *returnedSpacing); -typedef void (APIENTRYP PFNGLGETPATHCOLORGENIVNVPROC) (GLenum color, GLenum pname, GLint *value); -typedef void (APIENTRYP PFNGLGETPATHCOLORGENFVNVPROC) (GLenum color, GLenum pname, GLfloat *value); -typedef void (APIENTRYP PFNGLGETPATHTEXGENIVNVPROC) (GLenum texCoordSet, GLenum pname, GLint *value); -typedef void (APIENTRYP PFNGLGETPATHTEXGENFVNVPROC) (GLenum texCoordSet, GLenum pname, GLfloat *value); -typedef GLboolean (APIENTRYP PFNGLISPOINTINFILLPATHNVPROC) (GLuint path, GLuint mask, GLfloat x, GLfloat y); -typedef GLboolean (APIENTRYP PFNGLISPOINTINSTROKEPATHNVPROC) (GLuint path, GLfloat x, GLfloat y); -typedef GLfloat (APIENTRYP PFNGLGETPATHLENGTHNVPROC) (GLuint path, GLsizei startSegment, GLsizei numSegments); -typedef GLboolean (APIENTRYP PFNGLPOINTALONGPATHNVPROC) (GLuint path, GLsizei startSegment, GLsizei numSegments, GLfloat distance, GLfloat *x, GLfloat *y, GLfloat *tangentX, GLfloat *tangentY); -#ifdef GL_GLEXT_PROTOTYPES -GLAPI GLuint APIENTRY glGenPathsNV (GLsizei range); -GLAPI void APIENTRY glDeletePathsNV (GLuint path, GLsizei range); -GLAPI GLboolean APIENTRY glIsPathNV (GLuint path); -GLAPI void APIENTRY glPathCommandsNV (GLuint path, GLsizei numCommands, const GLubyte *commands, GLsizei numCoords, GLenum coordType, const void *coords); -GLAPI void APIENTRY glPathCoordsNV (GLuint path, GLsizei numCoords, GLenum coordType, const void *coords); -GLAPI void APIENTRY glPathSubCommandsNV (GLuint path, GLsizei commandStart, GLsizei commandsToDelete, GLsizei numCommands, const GLubyte *commands, GLsizei numCoords, GLenum coordType, const void *coords); -GLAPI void APIENTRY glPathSubCoordsNV (GLuint path, GLsizei coordStart, GLsizei numCoords, GLenum coordType, const void *coords); -GLAPI void APIENTRY glPathStringNV (GLuint path, GLenum format, GLsizei length, const void *pathString); -GLAPI void APIENTRY glPathGlyphsNV (GLuint firstPathName, GLenum fontTarget, const void *fontName, GLbitfield fontStyle, GLsizei numGlyphs, GLenum type, const void *charcodes, GLenum handleMissingGlyphs, GLuint pathParameterTemplate, GLfloat emScale); -GLAPI void APIENTRY glPathGlyphRangeNV (GLuint firstPathName, GLenum fontTarget, const void *fontName, GLbitfield fontStyle, GLuint firstGlyph, GLsizei numGlyphs, GLenum handleMissingGlyphs, GLuint pathParameterTemplate, GLfloat emScale); -GLAPI void APIENTRY glWeightPathsNV (GLuint resultPath, GLsizei numPaths, const GLuint *paths, const GLfloat *weights); -GLAPI void APIENTRY glCopyPathNV (GLuint resultPath, GLuint srcPath); -GLAPI void APIENTRY glInterpolatePathsNV (GLuint resultPath, GLuint pathA, GLuint pathB, GLfloat weight); -GLAPI void APIENTRY glTransformPathNV (GLuint resultPath, GLuint srcPath, GLenum transformType, const GLfloat *transformValues); -GLAPI void APIENTRY glPathParameterivNV (GLuint path, GLenum pname, const GLint *value); -GLAPI void APIENTRY glPathParameteriNV (GLuint path, GLenum pname, GLint value); -GLAPI void APIENTRY glPathParameterfvNV (GLuint path, GLenum pname, const GLfloat *value); -GLAPI void APIENTRY glPathParameterfNV (GLuint path, GLenum pname, GLfloat value); -GLAPI void APIENTRY glPathDashArrayNV (GLuint path, GLsizei dashCount, const GLfloat *dashArray); -GLAPI void APIENTRY glPathStencilFuncNV (GLenum func, GLint ref, GLuint mask); -GLAPI void APIENTRY glPathStencilDepthOffsetNV (GLfloat factor, GLfloat units); -GLAPI void APIENTRY glStencilFillPathNV (GLuint path, GLenum fillMode, GLuint mask); -GLAPI void APIENTRY glStencilStrokePathNV (GLuint path, GLint reference, GLuint mask); -GLAPI void APIENTRY glStencilFillPathInstancedNV (GLsizei numPaths, GLenum pathNameType, const void *paths, GLuint pathBase, GLenum fillMode, GLuint mask, GLenum transformType, const GLfloat *transformValues); -GLAPI void APIENTRY glStencilStrokePathInstancedNV (GLsizei numPaths, GLenum pathNameType, const void *paths, GLuint pathBase, GLint reference, GLuint mask, GLenum transformType, const GLfloat *transformValues); -GLAPI void APIENTRY glPathCoverDepthFuncNV (GLenum func); -GLAPI void APIENTRY glPathColorGenNV (GLenum color, GLenum genMode, GLenum colorFormat, const GLfloat *coeffs); -GLAPI void APIENTRY glPathTexGenNV (GLenum texCoordSet, GLenum genMode, GLint components, const GLfloat *coeffs); -GLAPI void APIENTRY glPathFogGenNV (GLenum genMode); -GLAPI void APIENTRY glCoverFillPathNV (GLuint path, GLenum coverMode); -GLAPI void APIENTRY glCoverStrokePathNV (GLuint path, GLenum coverMode); -GLAPI void APIENTRY glCoverFillPathInstancedNV (GLsizei numPaths, GLenum pathNameType, const void *paths, GLuint pathBase, GLenum coverMode, GLenum transformType, const GLfloat *transformValues); -GLAPI void APIENTRY glCoverStrokePathInstancedNV (GLsizei numPaths, GLenum pathNameType, const void *paths, GLuint pathBase, GLenum coverMode, GLenum transformType, const GLfloat *transformValues); -GLAPI void APIENTRY glGetPathParameterivNV (GLuint path, GLenum pname, GLint *value); -GLAPI void APIENTRY glGetPathParameterfvNV (GLuint path, GLenum pname, GLfloat *value); -GLAPI void APIENTRY glGetPathCommandsNV (GLuint path, GLubyte *commands); -GLAPI void APIENTRY glGetPathCoordsNV (GLuint path, GLfloat *coords); -GLAPI void APIENTRY glGetPathDashArrayNV (GLuint path, GLfloat *dashArray); -GLAPI void APIENTRY glGetPathMetricsNV (GLbitfield metricQueryMask, GLsizei numPaths, GLenum pathNameType, const void *paths, GLuint pathBase, GLsizei stride, GLfloat *metrics); -GLAPI void APIENTRY glGetPathMetricRangeNV (GLbitfield metricQueryMask, GLuint firstPathName, GLsizei numPaths, GLsizei stride, GLfloat *metrics); -GLAPI void APIENTRY glGetPathSpacingNV (GLenum pathListMode, GLsizei numPaths, GLenum pathNameType, const void *paths, GLuint pathBase, GLfloat advanceScale, GLfloat kerningScale, GLenum transformType, GLfloat *returnedSpacing); -GLAPI void APIENTRY glGetPathColorGenivNV (GLenum color, GLenum pname, GLint *value); -GLAPI void APIENTRY glGetPathColorGenfvNV (GLenum color, GLenum pname, GLfloat *value); -GLAPI void APIENTRY glGetPathTexGenivNV (GLenum texCoordSet, GLenum pname, GLint *value); -GLAPI void APIENTRY glGetPathTexGenfvNV (GLenum texCoordSet, GLenum pname, GLfloat *value); -GLAPI GLboolean APIENTRY glIsPointInFillPathNV (GLuint path, GLuint mask, GLfloat x, GLfloat y); -GLAPI GLboolean APIENTRY glIsPointInStrokePathNV (GLuint path, GLfloat x, GLfloat y); -GLAPI GLfloat APIENTRY glGetPathLengthNV (GLuint path, GLsizei startSegment, GLsizei numSegments); -GLAPI GLboolean APIENTRY glPointAlongPathNV (GLuint path, GLsizei startSegment, GLsizei numSegments, GLfloat distance, GLfloat *x, GLfloat *y, GLfloat *tangentX, GLfloat *tangentY); -#endif -#endif /* GL_NV_path_rendering */ - -#ifndef GL_NV_pixel_data_range -#define GL_NV_pixel_data_range 1 -#define GL_WRITE_PIXEL_DATA_RANGE_NV 0x8878 -#define GL_READ_PIXEL_DATA_RANGE_NV 0x8879 -#define GL_WRITE_PIXEL_DATA_RANGE_LENGTH_NV 0x887A -#define GL_READ_PIXEL_DATA_RANGE_LENGTH_NV 0x887B -#define GL_WRITE_PIXEL_DATA_RANGE_POINTER_NV 0x887C -#define GL_READ_PIXEL_DATA_RANGE_POINTER_NV 0x887D -typedef void (APIENTRYP PFNGLPIXELDATARANGENVPROC) (GLenum target, GLsizei length, const void *pointer); -typedef void (APIENTRYP PFNGLFLUSHPIXELDATARANGENVPROC) (GLenum target); -#ifdef GL_GLEXT_PROTOTYPES -GLAPI void APIENTRY glPixelDataRangeNV (GLenum target, GLsizei length, const void *pointer); -GLAPI void APIENTRY glFlushPixelDataRangeNV (GLenum target); -#endif -#endif /* GL_NV_pixel_data_range */ - -#ifndef GL_NV_point_sprite -#define GL_NV_point_sprite 1 -#define GL_POINT_SPRITE_NV 0x8861 -#define GL_COORD_REPLACE_NV 0x8862 -#define GL_POINT_SPRITE_R_MODE_NV 0x8863 -typedef void (APIENTRYP PFNGLPOINTPARAMETERINVPROC) (GLenum pname, GLint param); -typedef void (APIENTRYP PFNGLPOINTPARAMETERIVNVPROC) (GLenum pname, const GLint *params); -#ifdef GL_GLEXT_PROTOTYPES -GLAPI void APIENTRY glPointParameteriNV (GLenum pname, GLint param); -GLAPI void APIENTRY glPointParameterivNV (GLenum pname, const GLint *params); -#endif -#endif /* GL_NV_point_sprite */ - -#ifndef GL_NV_present_video -#define GL_NV_present_video 1 -#define GL_FRAME_NV 0x8E26 -#define GL_FIELDS_NV 0x8E27 -#define GL_CURRENT_TIME_NV 0x8E28 -#define GL_NUM_FILL_STREAMS_NV 0x8E29 -#define GL_PRESENT_TIME_NV 0x8E2A -#define GL_PRESENT_DURATION_NV 0x8E2B -typedef void (APIENTRYP PFNGLPRESENTFRAMEKEYEDNVPROC) (GLuint video_slot, GLuint64EXT minPresentTime, GLuint beginPresentTimeId, GLuint presentDurationId, GLenum type, GLenum target0, GLuint fill0, GLuint key0, GLenum target1, GLuint fill1, GLuint key1); -typedef void (APIENTRYP PFNGLPRESENTFRAMEDUALFILLNVPROC) (GLuint video_slot, GLuint64EXT minPresentTime, GLuint beginPresentTimeId, GLuint presentDurationId, GLenum type, GLenum target0, GLuint fill0, GLenum target1, GLuint fill1, GLenum target2, GLuint fill2, GLenum target3, GLuint fill3); -typedef void (APIENTRYP PFNGLGETVIDEOIVNVPROC) (GLuint video_slot, GLenum pname, GLint *params); -typedef void (APIENTRYP PFNGLGETVIDEOUIVNVPROC) (GLuint video_slot, GLenum pname, GLuint *params); -typedef void (APIENTRYP PFNGLGETVIDEOI64VNVPROC) (GLuint video_slot, GLenum pname, GLint64EXT *params); -typedef void (APIENTRYP PFNGLGETVIDEOUI64VNVPROC) (GLuint video_slot, GLenum pname, GLuint64EXT *params); -#ifdef GL_GLEXT_PROTOTYPES -GLAPI void APIENTRY glPresentFrameKeyedNV (GLuint video_slot, GLuint64EXT minPresentTime, GLuint beginPresentTimeId, GLuint presentDurationId, GLenum type, GLenum target0, GLuint fill0, GLuint key0, GLenum target1, GLuint fill1, GLuint key1); -GLAPI void APIENTRY glPresentFrameDualFillNV (GLuint video_slot, GLuint64EXT minPresentTime, GLuint beginPresentTimeId, GLuint presentDurationId, GLenum type, GLenum target0, GLuint fill0, GLenum target1, GLuint fill1, GLenum target2, GLuint fill2, GLenum target3, GLuint fill3); -GLAPI void APIENTRY glGetVideoivNV (GLuint video_slot, GLenum pname, GLint *params); -GLAPI void APIENTRY glGetVideouivNV (GLuint video_slot, GLenum pname, GLuint *params); -GLAPI void APIENTRY glGetVideoi64vNV (GLuint video_slot, GLenum pname, GLint64EXT *params); -GLAPI void APIENTRY glGetVideoui64vNV (GLuint video_slot, GLenum pname, GLuint64EXT *params); -#endif -#endif /* GL_NV_present_video */ - -#ifndef GL_NV_primitive_restart -#define GL_NV_primitive_restart 1 -#define GL_PRIMITIVE_RESTART_NV 0x8558 -#define GL_PRIMITIVE_RESTART_INDEX_NV 0x8559 -typedef void (APIENTRYP PFNGLPRIMITIVERESTARTNVPROC) (void); -typedef void (APIENTRYP PFNGLPRIMITIVERESTARTINDEXNVPROC) (GLuint index); -#ifdef GL_GLEXT_PROTOTYPES -GLAPI void APIENTRY glPrimitiveRestartNV (void); -GLAPI void APIENTRY glPrimitiveRestartIndexNV (GLuint index); -#endif -#endif /* GL_NV_primitive_restart */ - -#ifndef GL_NV_register_combiners -#define GL_NV_register_combiners 1 -#define GL_REGISTER_COMBINERS_NV 0x8522 -#define GL_VARIABLE_A_NV 0x8523 -#define GL_VARIABLE_B_NV 0x8524 -#define GL_VARIABLE_C_NV 0x8525 -#define GL_VARIABLE_D_NV 0x8526 -#define GL_VARIABLE_E_NV 0x8527 -#define GL_VARIABLE_F_NV 0x8528 -#define GL_VARIABLE_G_NV 0x8529 -#define GL_CONSTANT_COLOR0_NV 0x852A -#define GL_CONSTANT_COLOR1_NV 0x852B -#define GL_SPARE0_NV 0x852E -#define GL_SPARE1_NV 0x852F -#define GL_DISCARD_NV 0x8530 -#define GL_E_TIMES_F_NV 0x8531 -#define GL_SPARE0_PLUS_SECONDARY_COLOR_NV 0x8532 -#define GL_UNSIGNED_IDENTITY_NV 0x8536 -#define GL_UNSIGNED_INVERT_NV 0x8537 -#define GL_EXPAND_NORMAL_NV 0x8538 -#define GL_EXPAND_NEGATE_NV 0x8539 -#define GL_HALF_BIAS_NORMAL_NV 0x853A -#define GL_HALF_BIAS_NEGATE_NV 0x853B -#define GL_SIGNED_IDENTITY_NV 0x853C -#define GL_SIGNED_NEGATE_NV 0x853D -#define GL_SCALE_BY_TWO_NV 0x853E -#define GL_SCALE_BY_FOUR_NV 0x853F -#define GL_SCALE_BY_ONE_HALF_NV 0x8540 -#define GL_BIAS_BY_NEGATIVE_ONE_HALF_NV 0x8541 -#define GL_COMBINER_INPUT_NV 0x8542 -#define GL_COMBINER_MAPPING_NV 0x8543 -#define GL_COMBINER_COMPONENT_USAGE_NV 0x8544 -#define GL_COMBINER_AB_DOT_PRODUCT_NV 0x8545 -#define GL_COMBINER_CD_DOT_PRODUCT_NV 0x8546 -#define GL_COMBINER_MUX_SUM_NV 0x8547 -#define GL_COMBINER_SCALE_NV 0x8548 -#define GL_COMBINER_BIAS_NV 0x8549 -#define GL_COMBINER_AB_OUTPUT_NV 0x854A -#define GL_COMBINER_CD_OUTPUT_NV 0x854B -#define GL_COMBINER_SUM_OUTPUT_NV 0x854C -#define GL_MAX_GENERAL_COMBINERS_NV 0x854D -#define GL_NUM_GENERAL_COMBINERS_NV 0x854E -#define GL_COLOR_SUM_CLAMP_NV 0x854F -#define GL_COMBINER0_NV 0x8550 -#define GL_COMBINER1_NV 0x8551 -#define GL_COMBINER2_NV 0x8552 -#define GL_COMBINER3_NV 0x8553 -#define GL_COMBINER4_NV 0x8554 -#define GL_COMBINER5_NV 0x8555 -#define GL_COMBINER6_NV 0x8556 -#define GL_COMBINER7_NV 0x8557 -typedef void (APIENTRYP PFNGLCOMBINERPARAMETERFVNVPROC) (GLenum pname, const GLfloat *params); -typedef void (APIENTRYP PFNGLCOMBINERPARAMETERFNVPROC) (GLenum pname, GLfloat param); -typedef void (APIENTRYP PFNGLCOMBINERPARAMETERIVNVPROC) (GLenum pname, const GLint *params); -typedef void (APIENTRYP PFNGLCOMBINERPARAMETERINVPROC) (GLenum pname, GLint param); -typedef void (APIENTRYP PFNGLCOMBINERINPUTNVPROC) (GLenum stage, GLenum portion, GLenum variable, GLenum input, GLenum mapping, GLenum componentUsage); -typedef void (APIENTRYP PFNGLCOMBINEROUTPUTNVPROC) (GLenum stage, GLenum portion, GLenum abOutput, GLenum cdOutput, GLenum sumOutput, GLenum scale, GLenum bias, GLboolean abDotProduct, GLboolean cdDotProduct, GLboolean muxSum); -typedef void (APIENTRYP PFNGLFINALCOMBINERINPUTNVPROC) (GLenum variable, GLenum input, GLenum mapping, GLenum componentUsage); -typedef void (APIENTRYP PFNGLGETCOMBINERINPUTPARAMETERFVNVPROC) (GLenum stage, GLenum portion, GLenum variable, GLenum pname, GLfloat *params); -typedef void (APIENTRYP PFNGLGETCOMBINERINPUTPARAMETERIVNVPROC) (GLenum stage, GLenum portion, GLenum variable, GLenum pname, GLint *params); -typedef void (APIENTRYP PFNGLGETCOMBINEROUTPUTPARAMETERFVNVPROC) (GLenum stage, GLenum portion, GLenum pname, GLfloat *params); -typedef void (APIENTRYP PFNGLGETCOMBINEROUTPUTPARAMETERIVNVPROC) (GLenum stage, GLenum portion, GLenum pname, GLint *params); -typedef void (APIENTRYP PFNGLGETFINALCOMBINERINPUTPARAMETERFVNVPROC) (GLenum variable, GLenum pname, GLfloat *params); -typedef void (APIENTRYP PFNGLGETFINALCOMBINERINPUTPARAMETERIVNVPROC) (GLenum variable, GLenum pname, GLint *params); -#ifdef GL_GLEXT_PROTOTYPES -GLAPI void APIENTRY glCombinerParameterfvNV (GLenum pname, const GLfloat *params); -GLAPI void APIENTRY glCombinerParameterfNV (GLenum pname, GLfloat param); -GLAPI void APIENTRY glCombinerParameterivNV (GLenum pname, const GLint *params); -GLAPI void APIENTRY glCombinerParameteriNV (GLenum pname, GLint param); -GLAPI void APIENTRY glCombinerInputNV (GLenum stage, GLenum portion, GLenum variable, GLenum input, GLenum mapping, GLenum componentUsage); -GLAPI void APIENTRY glCombinerOutputNV (GLenum stage, GLenum portion, GLenum abOutput, GLenum cdOutput, GLenum sumOutput, GLenum scale, GLenum bias, GLboolean abDotProduct, GLboolean cdDotProduct, GLboolean muxSum); -GLAPI void APIENTRY glFinalCombinerInputNV (GLenum variable, GLenum input, GLenum mapping, GLenum componentUsage); -GLAPI void APIENTRY glGetCombinerInputParameterfvNV (GLenum stage, GLenum portion, GLenum variable, GLenum pname, GLfloat *params); -GLAPI void APIENTRY glGetCombinerInputParameterivNV (GLenum stage, GLenum portion, GLenum variable, GLenum pname, GLint *params); -GLAPI void APIENTRY glGetCombinerOutputParameterfvNV (GLenum stage, GLenum portion, GLenum pname, GLfloat *params); -GLAPI void APIENTRY glGetCombinerOutputParameterivNV (GLenum stage, GLenum portion, GLenum pname, GLint *params); -GLAPI void APIENTRY glGetFinalCombinerInputParameterfvNV (GLenum variable, GLenum pname, GLfloat *params); -GLAPI void APIENTRY glGetFinalCombinerInputParameterivNV (GLenum variable, GLenum pname, GLint *params); -#endif -#endif /* GL_NV_register_combiners */ - -#ifndef GL_NV_register_combiners2 -#define GL_NV_register_combiners2 1 -#define GL_PER_STAGE_CONSTANTS_NV 0x8535 -typedef void (APIENTRYP PFNGLCOMBINERSTAGEPARAMETERFVNVPROC) (GLenum stage, GLenum pname, const GLfloat *params); -typedef void (APIENTRYP PFNGLGETCOMBINERSTAGEPARAMETERFVNVPROC) (GLenum stage, GLenum pname, GLfloat *params); -#ifdef GL_GLEXT_PROTOTYPES -GLAPI void APIENTRY glCombinerStageParameterfvNV (GLenum stage, GLenum pname, const GLfloat *params); -GLAPI void APIENTRY glGetCombinerStageParameterfvNV (GLenum stage, GLenum pname, GLfloat *params); -#endif -#endif /* GL_NV_register_combiners2 */ - -#ifndef GL_NV_shader_atomic_counters -#define GL_NV_shader_atomic_counters 1 -#endif /* GL_NV_shader_atomic_counters */ - -#ifndef GL_NV_shader_atomic_float -#define GL_NV_shader_atomic_float 1 -#endif /* GL_NV_shader_atomic_float */ - -#ifndef GL_NV_shader_buffer_load -#define GL_NV_shader_buffer_load 1 -#define GL_BUFFER_GPU_ADDRESS_NV 0x8F1D -#define GL_GPU_ADDRESS_NV 0x8F34 -#define GL_MAX_SHADER_BUFFER_ADDRESS_NV 0x8F35 -typedef void (APIENTRYP PFNGLMAKEBUFFERRESIDENTNVPROC) (GLenum target, GLenum access); -typedef void (APIENTRYP PFNGLMAKEBUFFERNONRESIDENTNVPROC) (GLenum target); -typedef GLboolean (APIENTRYP PFNGLISBUFFERRESIDENTNVPROC) (GLenum target); -typedef void (APIENTRYP PFNGLMAKENAMEDBUFFERRESIDENTNVPROC) (GLuint buffer, GLenum access); -typedef void (APIENTRYP PFNGLMAKENAMEDBUFFERNONRESIDENTNVPROC) (GLuint buffer); -typedef GLboolean (APIENTRYP PFNGLISNAMEDBUFFERRESIDENTNVPROC) (GLuint buffer); -typedef void (APIENTRYP PFNGLGETBUFFERPARAMETERUI64VNVPROC) (GLenum target, GLenum pname, GLuint64EXT *params); -typedef void (APIENTRYP PFNGLGETNAMEDBUFFERPARAMETERUI64VNVPROC) (GLuint buffer, GLenum pname, GLuint64EXT *params); -typedef void (APIENTRYP PFNGLGETINTEGERUI64VNVPROC) (GLenum value, GLuint64EXT *result); -typedef void (APIENTRYP PFNGLUNIFORMUI64NVPROC) (GLint location, GLuint64EXT value); -typedef void (APIENTRYP PFNGLUNIFORMUI64VNVPROC) (GLint location, GLsizei count, const GLuint64EXT *value); -typedef void (APIENTRYP PFNGLGETUNIFORMUI64VNVPROC) (GLuint program, GLint location, GLuint64EXT *params); -typedef void (APIENTRYP PFNGLPROGRAMUNIFORMUI64NVPROC) (GLuint program, GLint location, GLuint64EXT value); -typedef void (APIENTRYP PFNGLPROGRAMUNIFORMUI64VNVPROC) (GLuint program, GLint location, GLsizei count, const GLuint64EXT *value); -#ifdef GL_GLEXT_PROTOTYPES -GLAPI void APIENTRY glMakeBufferResidentNV (GLenum target, GLenum access); -GLAPI void APIENTRY glMakeBufferNonResidentNV (GLenum target); -GLAPI GLboolean APIENTRY glIsBufferResidentNV (GLenum target); -GLAPI void APIENTRY glMakeNamedBufferResidentNV (GLuint buffer, GLenum access); -GLAPI void APIENTRY glMakeNamedBufferNonResidentNV (GLuint buffer); -GLAPI GLboolean APIENTRY glIsNamedBufferResidentNV (GLuint buffer); -GLAPI void APIENTRY glGetBufferParameterui64vNV (GLenum target, GLenum pname, GLuint64EXT *params); -GLAPI void APIENTRY glGetNamedBufferParameterui64vNV (GLuint buffer, GLenum pname, GLuint64EXT *params); -GLAPI void APIENTRY glGetIntegerui64vNV (GLenum value, GLuint64EXT *result); -GLAPI void APIENTRY glUniformui64NV (GLint location, GLuint64EXT value); -GLAPI void APIENTRY glUniformui64vNV (GLint location, GLsizei count, const GLuint64EXT *value); -GLAPI void APIENTRY glGetUniformui64vNV (GLuint program, GLint location, GLuint64EXT *params); -GLAPI void APIENTRY glProgramUniformui64NV (GLuint program, GLint location, GLuint64EXT value); -GLAPI void APIENTRY glProgramUniformui64vNV (GLuint program, GLint location, GLsizei count, const GLuint64EXT *value); -#endif -#endif /* GL_NV_shader_buffer_load */ - -#ifndef GL_NV_shader_buffer_store -#define GL_NV_shader_buffer_store 1 -#define GL_SHADER_GLOBAL_ACCESS_BARRIER_BIT_NV 0x00000010 -#endif /* GL_NV_shader_buffer_store */ - -#ifndef GL_NV_shader_storage_buffer_object -#define GL_NV_shader_storage_buffer_object 1 -#endif /* GL_NV_shader_storage_buffer_object */ - -#ifndef GL_NV_tessellation_program5 -#define GL_NV_tessellation_program5 1 -#define GL_MAX_PROGRAM_PATCH_ATTRIBS_NV 0x86D8 -#define GL_TESS_CONTROL_PROGRAM_NV 0x891E -#define GL_TESS_EVALUATION_PROGRAM_NV 0x891F -#define GL_TESS_CONTROL_PROGRAM_PARAMETER_BUFFER_NV 0x8C74 -#define GL_TESS_EVALUATION_PROGRAM_PARAMETER_BUFFER_NV 0x8C75 -#endif /* GL_NV_tessellation_program5 */ - -#ifndef GL_NV_texgen_emboss -#define GL_NV_texgen_emboss 1 -#define GL_EMBOSS_LIGHT_NV 0x855D -#define GL_EMBOSS_CONSTANT_NV 0x855E -#define GL_EMBOSS_MAP_NV 0x855F -#endif /* GL_NV_texgen_emboss */ - -#ifndef GL_NV_texgen_reflection -#define GL_NV_texgen_reflection 1 -#define GL_NORMAL_MAP_NV 0x8511 -#define GL_REFLECTION_MAP_NV 0x8512 -#endif /* GL_NV_texgen_reflection */ - -#ifndef GL_NV_texture_barrier -#define GL_NV_texture_barrier 1 -typedef void (APIENTRYP PFNGLTEXTUREBARRIERNVPROC) (void); -#ifdef GL_GLEXT_PROTOTYPES -GLAPI void APIENTRY glTextureBarrierNV (void); -#endif -#endif /* GL_NV_texture_barrier */ - -#ifndef GL_NV_texture_compression_vtc -#define GL_NV_texture_compression_vtc 1 -#endif /* GL_NV_texture_compression_vtc */ - -#ifndef GL_NV_texture_env_combine4 -#define GL_NV_texture_env_combine4 1 -#define GL_COMBINE4_NV 0x8503 -#define GL_SOURCE3_RGB_NV 0x8583 -#define GL_SOURCE3_ALPHA_NV 0x858B -#define GL_OPERAND3_RGB_NV 0x8593 -#define GL_OPERAND3_ALPHA_NV 0x859B -#endif /* GL_NV_texture_env_combine4 */ - -#ifndef GL_NV_texture_expand_normal -#define GL_NV_texture_expand_normal 1 -#define GL_TEXTURE_UNSIGNED_REMAP_MODE_NV 0x888F -#endif /* GL_NV_texture_expand_normal */ - -#ifndef GL_NV_texture_multisample -#define GL_NV_texture_multisample 1 -#define GL_TEXTURE_COVERAGE_SAMPLES_NV 0x9045 -#define GL_TEXTURE_COLOR_SAMPLES_NV 0x9046 -typedef void (APIENTRYP PFNGLTEXIMAGE2DMULTISAMPLECOVERAGENVPROC) (GLenum target, GLsizei coverageSamples, GLsizei colorSamples, GLint internalFormat, GLsizei width, GLsizei height, GLboolean fixedSampleLocations); -typedef void (APIENTRYP PFNGLTEXIMAGE3DMULTISAMPLECOVERAGENVPROC) (GLenum target, GLsizei coverageSamples, GLsizei colorSamples, GLint internalFormat, GLsizei width, GLsizei height, GLsizei depth, GLboolean fixedSampleLocations); -typedef void (APIENTRYP PFNGLTEXTUREIMAGE2DMULTISAMPLENVPROC) (GLuint texture, GLenum target, GLsizei samples, GLint internalFormat, GLsizei width, GLsizei height, GLboolean fixedSampleLocations); -typedef void (APIENTRYP PFNGLTEXTUREIMAGE3DMULTISAMPLENVPROC) (GLuint texture, GLenum target, GLsizei samples, GLint internalFormat, GLsizei width, GLsizei height, GLsizei depth, GLboolean fixedSampleLocations); -typedef void (APIENTRYP PFNGLTEXTUREIMAGE2DMULTISAMPLECOVERAGENVPROC) (GLuint texture, GLenum target, GLsizei coverageSamples, GLsizei colorSamples, GLint internalFormat, GLsizei width, GLsizei height, GLboolean fixedSampleLocations); -typedef void (APIENTRYP PFNGLTEXTUREIMAGE3DMULTISAMPLECOVERAGENVPROC) (GLuint texture, GLenum target, GLsizei coverageSamples, GLsizei colorSamples, GLint internalFormat, GLsizei width, GLsizei height, GLsizei depth, GLboolean fixedSampleLocations); -#ifdef GL_GLEXT_PROTOTYPES -GLAPI void APIENTRY glTexImage2DMultisampleCoverageNV (GLenum target, GLsizei coverageSamples, GLsizei colorSamples, GLint internalFormat, GLsizei width, GLsizei height, GLboolean fixedSampleLocations); -GLAPI void APIENTRY glTexImage3DMultisampleCoverageNV (GLenum target, GLsizei coverageSamples, GLsizei colorSamples, GLint internalFormat, GLsizei width, GLsizei height, GLsizei depth, GLboolean fixedSampleLocations); -GLAPI void APIENTRY glTextureImage2DMultisampleNV (GLuint texture, GLenum target, GLsizei samples, GLint internalFormat, GLsizei width, GLsizei height, GLboolean fixedSampleLocations); -GLAPI void APIENTRY glTextureImage3DMultisampleNV (GLuint texture, GLenum target, GLsizei samples, GLint internalFormat, GLsizei width, GLsizei height, GLsizei depth, GLboolean fixedSampleLocations); -GLAPI void APIENTRY glTextureImage2DMultisampleCoverageNV (GLuint texture, GLenum target, GLsizei coverageSamples, GLsizei colorSamples, GLint internalFormat, GLsizei width, GLsizei height, GLboolean fixedSampleLocations); -GLAPI void APIENTRY glTextureImage3DMultisampleCoverageNV (GLuint texture, GLenum target, GLsizei coverageSamples, GLsizei colorSamples, GLint internalFormat, GLsizei width, GLsizei height, GLsizei depth, GLboolean fixedSampleLocations); -#endif -#endif /* GL_NV_texture_multisample */ - -#ifndef GL_NV_texture_rectangle -#define GL_NV_texture_rectangle 1 -#define GL_TEXTURE_RECTANGLE_NV 0x84F5 -#define GL_TEXTURE_BINDING_RECTANGLE_NV 0x84F6 -#define GL_PROXY_TEXTURE_RECTANGLE_NV 0x84F7 -#define GL_MAX_RECTANGLE_TEXTURE_SIZE_NV 0x84F8 -#endif /* GL_NV_texture_rectangle */ - -#ifndef GL_NV_texture_shader -#define GL_NV_texture_shader 1 -#define GL_OFFSET_TEXTURE_RECTANGLE_NV 0x864C -#define GL_OFFSET_TEXTURE_RECTANGLE_SCALE_NV 0x864D -#define GL_DOT_PRODUCT_TEXTURE_RECTANGLE_NV 0x864E -#define GL_RGBA_UNSIGNED_DOT_PRODUCT_MAPPING_NV 0x86D9 -#define GL_UNSIGNED_INT_S8_S8_8_8_NV 0x86DA -#define GL_UNSIGNED_INT_8_8_S8_S8_REV_NV 0x86DB -#define GL_DSDT_MAG_INTENSITY_NV 0x86DC -#define GL_SHADER_CONSISTENT_NV 0x86DD -#define GL_TEXTURE_SHADER_NV 0x86DE -#define GL_SHADER_OPERATION_NV 0x86DF -#define GL_CULL_MODES_NV 0x86E0 -#define GL_OFFSET_TEXTURE_MATRIX_NV 0x86E1 -#define GL_OFFSET_TEXTURE_SCALE_NV 0x86E2 -#define GL_OFFSET_TEXTURE_BIAS_NV 0x86E3 -#define GL_OFFSET_TEXTURE_2D_MATRIX_NV 0x86E1 -#define GL_OFFSET_TEXTURE_2D_SCALE_NV 0x86E2 -#define GL_OFFSET_TEXTURE_2D_BIAS_NV 0x86E3 -#define GL_PREVIOUS_TEXTURE_INPUT_NV 0x86E4 -#define GL_CONST_EYE_NV 0x86E5 -#define GL_PASS_THROUGH_NV 0x86E6 -#define GL_CULL_FRAGMENT_NV 0x86E7 -#define GL_OFFSET_TEXTURE_2D_NV 0x86E8 -#define GL_DEPENDENT_AR_TEXTURE_2D_NV 0x86E9 -#define GL_DEPENDENT_GB_TEXTURE_2D_NV 0x86EA -#define GL_DOT_PRODUCT_NV 0x86EC -#define GL_DOT_PRODUCT_DEPTH_REPLACE_NV 0x86ED -#define GL_DOT_PRODUCT_TEXTURE_2D_NV 0x86EE -#define GL_DOT_PRODUCT_TEXTURE_CUBE_MAP_NV 0x86F0 -#define GL_DOT_PRODUCT_DIFFUSE_CUBE_MAP_NV 0x86F1 -#define GL_DOT_PRODUCT_REFLECT_CUBE_MAP_NV 0x86F2 -#define GL_DOT_PRODUCT_CONST_EYE_REFLECT_CUBE_MAP_NV 0x86F3 -#define GL_HILO_NV 0x86F4 -#define GL_DSDT_NV 0x86F5 -#define GL_DSDT_MAG_NV 0x86F6 -#define GL_DSDT_MAG_VIB_NV 0x86F7 -#define GL_HILO16_NV 0x86F8 -#define GL_SIGNED_HILO_NV 0x86F9 -#define GL_SIGNED_HILO16_NV 0x86FA -#define GL_SIGNED_RGBA_NV 0x86FB -#define GL_SIGNED_RGBA8_NV 0x86FC -#define GL_SIGNED_RGB_NV 0x86FE -#define GL_SIGNED_RGB8_NV 0x86FF -#define GL_SIGNED_LUMINANCE_NV 0x8701 -#define GL_SIGNED_LUMINANCE8_NV 0x8702 -#define GL_SIGNED_LUMINANCE_ALPHA_NV 0x8703 -#define GL_SIGNED_LUMINANCE8_ALPHA8_NV 0x8704 -#define GL_SIGNED_ALPHA_NV 0x8705 -#define GL_SIGNED_ALPHA8_NV 0x8706 -#define GL_SIGNED_INTENSITY_NV 0x8707 -#define GL_SIGNED_INTENSITY8_NV 0x8708 -#define GL_DSDT8_NV 0x8709 -#define GL_DSDT8_MAG8_NV 0x870A -#define GL_DSDT8_MAG8_INTENSITY8_NV 0x870B -#define GL_SIGNED_RGB_UNSIGNED_ALPHA_NV 0x870C -#define GL_SIGNED_RGB8_UNSIGNED_ALPHA8_NV 0x870D -#define GL_HI_SCALE_NV 0x870E -#define GL_LO_SCALE_NV 0x870F -#define GL_DS_SCALE_NV 0x8710 -#define GL_DT_SCALE_NV 0x8711 -#define GL_MAGNITUDE_SCALE_NV 0x8712 -#define GL_VIBRANCE_SCALE_NV 0x8713 -#define GL_HI_BIAS_NV 0x8714 -#define GL_LO_BIAS_NV 0x8715 -#define GL_DS_BIAS_NV 0x8716 -#define GL_DT_BIAS_NV 0x8717 -#define GL_MAGNITUDE_BIAS_NV 0x8718 -#define GL_VIBRANCE_BIAS_NV 0x8719 -#define GL_TEXTURE_BORDER_VALUES_NV 0x871A -#define GL_TEXTURE_HI_SIZE_NV 0x871B -#define GL_TEXTURE_LO_SIZE_NV 0x871C -#define GL_TEXTURE_DS_SIZE_NV 0x871D -#define GL_TEXTURE_DT_SIZE_NV 0x871E -#define GL_TEXTURE_MAG_SIZE_NV 0x871F -#endif /* GL_NV_texture_shader */ - -#ifndef GL_NV_texture_shader2 -#define GL_NV_texture_shader2 1 -#define GL_DOT_PRODUCT_TEXTURE_3D_NV 0x86EF -#endif /* GL_NV_texture_shader2 */ - -#ifndef GL_NV_texture_shader3 -#define GL_NV_texture_shader3 1 -#define GL_OFFSET_PROJECTIVE_TEXTURE_2D_NV 0x8850 -#define GL_OFFSET_PROJECTIVE_TEXTURE_2D_SCALE_NV 0x8851 -#define GL_OFFSET_PROJECTIVE_TEXTURE_RECTANGLE_NV 0x8852 -#define GL_OFFSET_PROJECTIVE_TEXTURE_RECTANGLE_SCALE_NV 0x8853 -#define GL_OFFSET_HILO_TEXTURE_2D_NV 0x8854 -#define GL_OFFSET_HILO_TEXTURE_RECTANGLE_NV 0x8855 -#define GL_OFFSET_HILO_PROJECTIVE_TEXTURE_2D_NV 0x8856 -#define GL_OFFSET_HILO_PROJECTIVE_TEXTURE_RECTANGLE_NV 0x8857 -#define GL_DEPENDENT_HILO_TEXTURE_2D_NV 0x8858 -#define GL_DEPENDENT_RGB_TEXTURE_3D_NV 0x8859 -#define GL_DEPENDENT_RGB_TEXTURE_CUBE_MAP_NV 0x885A -#define GL_DOT_PRODUCT_PASS_THROUGH_NV 0x885B -#define GL_DOT_PRODUCT_TEXTURE_1D_NV 0x885C -#define GL_DOT_PRODUCT_AFFINE_DEPTH_REPLACE_NV 0x885D -#define GL_HILO8_NV 0x885E -#define GL_SIGNED_HILO8_NV 0x885F -#define GL_FORCE_BLUE_TO_ONE_NV 0x8860 -#endif /* GL_NV_texture_shader3 */ - -#ifndef GL_NV_transform_feedback -#define GL_NV_transform_feedback 1 -#define GL_BACK_PRIMARY_COLOR_NV 0x8C77 -#define GL_BACK_SECONDARY_COLOR_NV 0x8C78 -#define GL_TEXTURE_COORD_NV 0x8C79 -#define GL_CLIP_DISTANCE_NV 0x8C7A -#define GL_VERTEX_ID_NV 0x8C7B -#define GL_PRIMITIVE_ID_NV 0x8C7C -#define GL_GENERIC_ATTRIB_NV 0x8C7D -#define GL_TRANSFORM_FEEDBACK_ATTRIBS_NV 0x8C7E -#define GL_TRANSFORM_FEEDBACK_BUFFER_MODE_NV 0x8C7F -#define GL_MAX_TRANSFORM_FEEDBACK_SEPARATE_COMPONENTS_NV 0x8C80 -#define GL_ACTIVE_VARYINGS_NV 0x8C81 -#define GL_ACTIVE_VARYING_MAX_LENGTH_NV 0x8C82 -#define GL_TRANSFORM_FEEDBACK_VARYINGS_NV 0x8C83 -#define GL_TRANSFORM_FEEDBACK_BUFFER_START_NV 0x8C84 -#define GL_TRANSFORM_FEEDBACK_BUFFER_SIZE_NV 0x8C85 -#define GL_TRANSFORM_FEEDBACK_RECORD_NV 0x8C86 -#define GL_PRIMITIVES_GENERATED_NV 0x8C87 -#define GL_TRANSFORM_FEEDBACK_PRIMITIVES_WRITTEN_NV 0x8C88 -#define GL_RASTERIZER_DISCARD_NV 0x8C89 -#define GL_MAX_TRANSFORM_FEEDBACK_INTERLEAVED_COMPONENTS_NV 0x8C8A -#define GL_MAX_TRANSFORM_FEEDBACK_SEPARATE_ATTRIBS_NV 0x8C8B -#define GL_INTERLEAVED_ATTRIBS_NV 0x8C8C -#define GL_SEPARATE_ATTRIBS_NV 0x8C8D -#define GL_TRANSFORM_FEEDBACK_BUFFER_NV 0x8C8E -#define GL_TRANSFORM_FEEDBACK_BUFFER_BINDING_NV 0x8C8F -#define GL_LAYER_NV 0x8DAA -#define GL_NEXT_BUFFER_NV -2 -#define GL_SKIP_COMPONENTS4_NV -3 -#define GL_SKIP_COMPONENTS3_NV -4 -#define GL_SKIP_COMPONENTS2_NV -5 -#define GL_SKIP_COMPONENTS1_NV -6 -typedef void (APIENTRYP PFNGLBEGINTRANSFORMFEEDBACKNVPROC) (GLenum primitiveMode); -typedef void (APIENTRYP PFNGLENDTRANSFORMFEEDBACKNVPROC) (void); -typedef void (APIENTRYP PFNGLTRANSFORMFEEDBACKATTRIBSNVPROC) (GLuint count, const GLint *attribs, GLenum bufferMode); -typedef void (APIENTRYP PFNGLBINDBUFFERRANGENVPROC) (GLenum target, GLuint index, GLuint buffer, GLintptr offset, GLsizeiptr size); -typedef void (APIENTRYP PFNGLBINDBUFFEROFFSETNVPROC) (GLenum target, GLuint index, GLuint buffer, GLintptr offset); -typedef void (APIENTRYP PFNGLBINDBUFFERBASENVPROC) (GLenum target, GLuint index, GLuint buffer); -typedef void (APIENTRYP PFNGLTRANSFORMFEEDBACKVARYINGSNVPROC) (GLuint program, GLsizei count, const GLint *locations, GLenum bufferMode); -typedef void (APIENTRYP PFNGLACTIVEVARYINGNVPROC) (GLuint program, const GLchar *name); -typedef GLint (APIENTRYP PFNGLGETVARYINGLOCATIONNVPROC) (GLuint program, const GLchar *name); -typedef void (APIENTRYP PFNGLGETACTIVEVARYINGNVPROC) (GLuint program, GLuint index, GLsizei bufSize, GLsizei *length, GLsizei *size, GLenum *type, GLchar *name); -typedef void (APIENTRYP PFNGLGETTRANSFORMFEEDBACKVARYINGNVPROC) (GLuint program, GLuint index, GLint *location); -typedef void (APIENTRYP PFNGLTRANSFORMFEEDBACKSTREAMATTRIBSNVPROC) (GLsizei count, const GLint *attribs, GLsizei nbuffers, const GLint *bufstreams, GLenum bufferMode); -#ifdef GL_GLEXT_PROTOTYPES -GLAPI void APIENTRY glBeginTransformFeedbackNV (GLenum primitiveMode); -GLAPI void APIENTRY glEndTransformFeedbackNV (void); -GLAPI void APIENTRY glTransformFeedbackAttribsNV (GLuint count, const GLint *attribs, GLenum bufferMode); -GLAPI void APIENTRY glBindBufferRangeNV (GLenum target, GLuint index, GLuint buffer, GLintptr offset, GLsizeiptr size); -GLAPI void APIENTRY glBindBufferOffsetNV (GLenum target, GLuint index, GLuint buffer, GLintptr offset); -GLAPI void APIENTRY glBindBufferBaseNV (GLenum target, GLuint index, GLuint buffer); -GLAPI void APIENTRY glTransformFeedbackVaryingsNV (GLuint program, GLsizei count, const GLint *locations, GLenum bufferMode); -GLAPI void APIENTRY glActiveVaryingNV (GLuint program, const GLchar *name); -GLAPI GLint APIENTRY glGetVaryingLocationNV (GLuint program, const GLchar *name); -GLAPI void APIENTRY glGetActiveVaryingNV (GLuint program, GLuint index, GLsizei bufSize, GLsizei *length, GLsizei *size, GLenum *type, GLchar *name); -GLAPI void APIENTRY glGetTransformFeedbackVaryingNV (GLuint program, GLuint index, GLint *location); -GLAPI void APIENTRY glTransformFeedbackStreamAttribsNV (GLsizei count, const GLint *attribs, GLsizei nbuffers, const GLint *bufstreams, GLenum bufferMode); -#endif -#endif /* GL_NV_transform_feedback */ - -#ifndef GL_NV_transform_feedback2 -#define GL_NV_transform_feedback2 1 -#define GL_TRANSFORM_FEEDBACK_NV 0x8E22 -#define GL_TRANSFORM_FEEDBACK_BUFFER_PAUSED_NV 0x8E23 -#define GL_TRANSFORM_FEEDBACK_BUFFER_ACTIVE_NV 0x8E24 -#define GL_TRANSFORM_FEEDBACK_BINDING_NV 0x8E25 -typedef void (APIENTRYP PFNGLBINDTRANSFORMFEEDBACKNVPROC) (GLenum target, GLuint id); -typedef void (APIENTRYP PFNGLDELETETRANSFORMFEEDBACKSNVPROC) (GLsizei n, const GLuint *ids); -typedef void (APIENTRYP PFNGLGENTRANSFORMFEEDBACKSNVPROC) (GLsizei n, GLuint *ids); -typedef GLboolean (APIENTRYP PFNGLISTRANSFORMFEEDBACKNVPROC) (GLuint id); -typedef void (APIENTRYP PFNGLPAUSETRANSFORMFEEDBACKNVPROC) (void); -typedef void (APIENTRYP PFNGLRESUMETRANSFORMFEEDBACKNVPROC) (void); -typedef void (APIENTRYP PFNGLDRAWTRANSFORMFEEDBACKNVPROC) (GLenum mode, GLuint id); -#ifdef GL_GLEXT_PROTOTYPES -GLAPI void APIENTRY glBindTransformFeedbackNV (GLenum target, GLuint id); -GLAPI void APIENTRY glDeleteTransformFeedbacksNV (GLsizei n, const GLuint *ids); -GLAPI void APIENTRY glGenTransformFeedbacksNV (GLsizei n, GLuint *ids); -GLAPI GLboolean APIENTRY glIsTransformFeedbackNV (GLuint id); -GLAPI void APIENTRY glPauseTransformFeedbackNV (void); -GLAPI void APIENTRY glResumeTransformFeedbackNV (void); -GLAPI void APIENTRY glDrawTransformFeedbackNV (GLenum mode, GLuint id); -#endif -#endif /* GL_NV_transform_feedback2 */ - -#ifndef GL_NV_vdpau_interop -#define GL_NV_vdpau_interop 1 -typedef GLintptr GLvdpauSurfaceNV; -#define GL_SURFACE_STATE_NV 0x86EB -#define GL_SURFACE_REGISTERED_NV 0x86FD -#define GL_SURFACE_MAPPED_NV 0x8700 -#define GL_WRITE_DISCARD_NV 0x88BE -typedef void (APIENTRYP PFNGLVDPAUINITNVPROC) (const void *vdpDevice, const void *getProcAddress); -typedef void (APIENTRYP PFNGLVDPAUFININVPROC) (void); -typedef GLvdpauSurfaceNV (APIENTRYP PFNGLVDPAUREGISTERVIDEOSURFACENVPROC) (const void *vdpSurface, GLenum target, GLsizei numTextureNames, const GLuint *textureNames); -typedef GLvdpauSurfaceNV (APIENTRYP PFNGLVDPAUREGISTEROUTPUTSURFACENVPROC) (const void *vdpSurface, GLenum target, GLsizei numTextureNames, const GLuint *textureNames); -typedef GLboolean (APIENTRYP PFNGLVDPAUISSURFACENVPROC) (GLvdpauSurfaceNV surface); -typedef void (APIENTRYP PFNGLVDPAUUNREGISTERSURFACENVPROC) (GLvdpauSurfaceNV surface); -typedef void (APIENTRYP PFNGLVDPAUGETSURFACEIVNVPROC) (GLvdpauSurfaceNV surface, GLenum pname, GLsizei bufSize, GLsizei *length, GLint *values); -typedef void (APIENTRYP PFNGLVDPAUSURFACEACCESSNVPROC) (GLvdpauSurfaceNV surface, GLenum access); -typedef void (APIENTRYP PFNGLVDPAUMAPSURFACESNVPROC) (GLsizei numSurfaces, const GLvdpauSurfaceNV *surfaces); -typedef void (APIENTRYP PFNGLVDPAUUNMAPSURFACESNVPROC) (GLsizei numSurface, const GLvdpauSurfaceNV *surfaces); -#ifdef GL_GLEXT_PROTOTYPES -GLAPI void APIENTRY glVDPAUInitNV (const void *vdpDevice, const void *getProcAddress); -GLAPI void APIENTRY glVDPAUFiniNV (void); -GLAPI GLvdpauSurfaceNV APIENTRY glVDPAURegisterVideoSurfaceNV (const void *vdpSurface, GLenum target, GLsizei numTextureNames, const GLuint *textureNames); -GLAPI GLvdpauSurfaceNV APIENTRY glVDPAURegisterOutputSurfaceNV (const void *vdpSurface, GLenum target, GLsizei numTextureNames, const GLuint *textureNames); -GLAPI GLboolean APIENTRY glVDPAUIsSurfaceNV (GLvdpauSurfaceNV surface); -GLAPI void APIENTRY glVDPAUUnregisterSurfaceNV (GLvdpauSurfaceNV surface); -GLAPI void APIENTRY glVDPAUGetSurfaceivNV (GLvdpauSurfaceNV surface, GLenum pname, GLsizei bufSize, GLsizei *length, GLint *values); -GLAPI void APIENTRY glVDPAUSurfaceAccessNV (GLvdpauSurfaceNV surface, GLenum access); -GLAPI void APIENTRY glVDPAUMapSurfacesNV (GLsizei numSurfaces, const GLvdpauSurfaceNV *surfaces); -GLAPI void APIENTRY glVDPAUUnmapSurfacesNV (GLsizei numSurface, const GLvdpauSurfaceNV *surfaces); -#endif -#endif /* GL_NV_vdpau_interop */ - -#ifndef GL_NV_vertex_array_range -#define GL_NV_vertex_array_range 1 -#define GL_VERTEX_ARRAY_RANGE_NV 0x851D -#define GL_VERTEX_ARRAY_RANGE_LENGTH_NV 0x851E -#define GL_VERTEX_ARRAY_RANGE_VALID_NV 0x851F -#define GL_MAX_VERTEX_ARRAY_RANGE_ELEMENT_NV 0x8520 -#define GL_VERTEX_ARRAY_RANGE_POINTER_NV 0x8521 -typedef void (APIENTRYP PFNGLFLUSHVERTEXARRAYRANGENVPROC) (void); -typedef void (APIENTRYP PFNGLVERTEXARRAYRANGENVPROC) (GLsizei length, const void *pointer); -#ifdef GL_GLEXT_PROTOTYPES -GLAPI void APIENTRY glFlushVertexArrayRangeNV (void); -GLAPI void APIENTRY glVertexArrayRangeNV (GLsizei length, const void *pointer); -#endif -#endif /* GL_NV_vertex_array_range */ - -#ifndef GL_NV_vertex_array_range2 -#define GL_NV_vertex_array_range2 1 -#define GL_VERTEX_ARRAY_RANGE_WITHOUT_FLUSH_NV 0x8533 -#endif /* GL_NV_vertex_array_range2 */ - -#ifndef GL_NV_vertex_attrib_integer_64bit -#define GL_NV_vertex_attrib_integer_64bit 1 -typedef void (APIENTRYP PFNGLVERTEXATTRIBL1I64NVPROC) (GLuint index, GLint64EXT x); -typedef void (APIENTRYP PFNGLVERTEXATTRIBL2I64NVPROC) (GLuint index, GLint64EXT x, GLint64EXT y); -typedef void (APIENTRYP PFNGLVERTEXATTRIBL3I64NVPROC) (GLuint index, GLint64EXT x, GLint64EXT y, GLint64EXT z); -typedef void (APIENTRYP PFNGLVERTEXATTRIBL4I64NVPROC) (GLuint index, GLint64EXT x, GLint64EXT y, GLint64EXT z, GLint64EXT w); -typedef void (APIENTRYP PFNGLVERTEXATTRIBL1I64VNVPROC) (GLuint index, const GLint64EXT *v); -typedef void (APIENTRYP PFNGLVERTEXATTRIBL2I64VNVPROC) (GLuint index, const GLint64EXT *v); -typedef void (APIENTRYP PFNGLVERTEXATTRIBL3I64VNVPROC) (GLuint index, const GLint64EXT *v); -typedef void (APIENTRYP PFNGLVERTEXATTRIBL4I64VNVPROC) (GLuint index, const GLint64EXT *v); -typedef void (APIENTRYP PFNGLVERTEXATTRIBL1UI64NVPROC) (GLuint index, GLuint64EXT x); -typedef void (APIENTRYP PFNGLVERTEXATTRIBL2UI64NVPROC) (GLuint index, GLuint64EXT x, GLuint64EXT y); -typedef void (APIENTRYP PFNGLVERTEXATTRIBL3UI64NVPROC) (GLuint index, GLuint64EXT x, GLuint64EXT y, GLuint64EXT z); -typedef void (APIENTRYP PFNGLVERTEXATTRIBL4UI64NVPROC) (GLuint index, GLuint64EXT x, GLuint64EXT y, GLuint64EXT z, GLuint64EXT w); -typedef void (APIENTRYP PFNGLVERTEXATTRIBL1UI64VNVPROC) (GLuint index, const GLuint64EXT *v); -typedef void (APIENTRYP PFNGLVERTEXATTRIBL2UI64VNVPROC) (GLuint index, const GLuint64EXT *v); -typedef void (APIENTRYP PFNGLVERTEXATTRIBL3UI64VNVPROC) (GLuint index, const GLuint64EXT *v); -typedef void (APIENTRYP PFNGLVERTEXATTRIBL4UI64VNVPROC) (GLuint index, const GLuint64EXT *v); -typedef void (APIENTRYP PFNGLGETVERTEXATTRIBLI64VNVPROC) (GLuint index, GLenum pname, GLint64EXT *params); -typedef void (APIENTRYP PFNGLGETVERTEXATTRIBLUI64VNVPROC) (GLuint index, GLenum pname, GLuint64EXT *params); -typedef void (APIENTRYP PFNGLVERTEXATTRIBLFORMATNVPROC) (GLuint index, GLint size, GLenum type, GLsizei stride); -#ifdef GL_GLEXT_PROTOTYPES -GLAPI void APIENTRY glVertexAttribL1i64NV (GLuint index, GLint64EXT x); -GLAPI void APIENTRY glVertexAttribL2i64NV (GLuint index, GLint64EXT x, GLint64EXT y); -GLAPI void APIENTRY glVertexAttribL3i64NV (GLuint index, GLint64EXT x, GLint64EXT y, GLint64EXT z); -GLAPI void APIENTRY glVertexAttribL4i64NV (GLuint index, GLint64EXT x, GLint64EXT y, GLint64EXT z, GLint64EXT w); -GLAPI void APIENTRY glVertexAttribL1i64vNV (GLuint index, const GLint64EXT *v); -GLAPI void APIENTRY glVertexAttribL2i64vNV (GLuint index, const GLint64EXT *v); -GLAPI void APIENTRY glVertexAttribL3i64vNV (GLuint index, const GLint64EXT *v); -GLAPI void APIENTRY glVertexAttribL4i64vNV (GLuint index, const GLint64EXT *v); -GLAPI void APIENTRY glVertexAttribL1ui64NV (GLuint index, GLuint64EXT x); -GLAPI void APIENTRY glVertexAttribL2ui64NV (GLuint index, GLuint64EXT x, GLuint64EXT y); -GLAPI void APIENTRY glVertexAttribL3ui64NV (GLuint index, GLuint64EXT x, GLuint64EXT y, GLuint64EXT z); -GLAPI void APIENTRY glVertexAttribL4ui64NV (GLuint index, GLuint64EXT x, GLuint64EXT y, GLuint64EXT z, GLuint64EXT w); -GLAPI void APIENTRY glVertexAttribL1ui64vNV (GLuint index, const GLuint64EXT *v); -GLAPI void APIENTRY glVertexAttribL2ui64vNV (GLuint index, const GLuint64EXT *v); -GLAPI void APIENTRY glVertexAttribL3ui64vNV (GLuint index, const GLuint64EXT *v); -GLAPI void APIENTRY glVertexAttribL4ui64vNV (GLuint index, const GLuint64EXT *v); -GLAPI void APIENTRY glGetVertexAttribLi64vNV (GLuint index, GLenum pname, GLint64EXT *params); -GLAPI void APIENTRY glGetVertexAttribLui64vNV (GLuint index, GLenum pname, GLuint64EXT *params); -GLAPI void APIENTRY glVertexAttribLFormatNV (GLuint index, GLint size, GLenum type, GLsizei stride); -#endif -#endif /* GL_NV_vertex_attrib_integer_64bit */ - -#ifndef GL_NV_vertex_buffer_unified_memory -#define GL_NV_vertex_buffer_unified_memory 1 -#define GL_VERTEX_ATTRIB_ARRAY_UNIFIED_NV 0x8F1E -#define GL_ELEMENT_ARRAY_UNIFIED_NV 0x8F1F -#define GL_VERTEX_ATTRIB_ARRAY_ADDRESS_NV 0x8F20 -#define GL_VERTEX_ARRAY_ADDRESS_NV 0x8F21 -#define GL_NORMAL_ARRAY_ADDRESS_NV 0x8F22 -#define GL_COLOR_ARRAY_ADDRESS_NV 0x8F23 -#define GL_INDEX_ARRAY_ADDRESS_NV 0x8F24 -#define GL_TEXTURE_COORD_ARRAY_ADDRESS_NV 0x8F25 -#define GL_EDGE_FLAG_ARRAY_ADDRESS_NV 0x8F26 -#define GL_SECONDARY_COLOR_ARRAY_ADDRESS_NV 0x8F27 -#define GL_FOG_COORD_ARRAY_ADDRESS_NV 0x8F28 -#define GL_ELEMENT_ARRAY_ADDRESS_NV 0x8F29 -#define GL_VERTEX_ATTRIB_ARRAY_LENGTH_NV 0x8F2A -#define GL_VERTEX_ARRAY_LENGTH_NV 0x8F2B -#define GL_NORMAL_ARRAY_LENGTH_NV 0x8F2C -#define GL_COLOR_ARRAY_LENGTH_NV 0x8F2D -#define GL_INDEX_ARRAY_LENGTH_NV 0x8F2E -#define GL_TEXTURE_COORD_ARRAY_LENGTH_NV 0x8F2F -#define GL_EDGE_FLAG_ARRAY_LENGTH_NV 0x8F30 -#define GL_SECONDARY_COLOR_ARRAY_LENGTH_NV 0x8F31 -#define GL_FOG_COORD_ARRAY_LENGTH_NV 0x8F32 -#define GL_ELEMENT_ARRAY_LENGTH_NV 0x8F33 -#define GL_DRAW_INDIRECT_UNIFIED_NV 0x8F40 -#define GL_DRAW_INDIRECT_ADDRESS_NV 0x8F41 -#define GL_DRAW_INDIRECT_LENGTH_NV 0x8F42 -typedef void (APIENTRYP PFNGLBUFFERADDRESSRANGENVPROC) (GLenum pname, GLuint index, GLuint64EXT address, GLsizeiptr length); -typedef void (APIENTRYP PFNGLVERTEXFORMATNVPROC) (GLint size, GLenum type, GLsizei stride); -typedef void (APIENTRYP PFNGLNORMALFORMATNVPROC) (GLenum type, GLsizei stride); -typedef void (APIENTRYP PFNGLCOLORFORMATNVPROC) (GLint size, GLenum type, GLsizei stride); -typedef void (APIENTRYP PFNGLINDEXFORMATNVPROC) (GLenum type, GLsizei stride); -typedef void (APIENTRYP PFNGLTEXCOORDFORMATNVPROC) (GLint size, GLenum type, GLsizei stride); -typedef void (APIENTRYP PFNGLEDGEFLAGFORMATNVPROC) (GLsizei stride); -typedef void (APIENTRYP PFNGLSECONDARYCOLORFORMATNVPROC) (GLint size, GLenum type, GLsizei stride); -typedef void (APIENTRYP PFNGLFOGCOORDFORMATNVPROC) (GLenum type, GLsizei stride); -typedef void (APIENTRYP PFNGLVERTEXATTRIBFORMATNVPROC) (GLuint index, GLint size, GLenum type, GLboolean normalized, GLsizei stride); -typedef void (APIENTRYP PFNGLVERTEXATTRIBIFORMATNVPROC) (GLuint index, GLint size, GLenum type, GLsizei stride); -typedef void (APIENTRYP PFNGLGETINTEGERUI64I_VNVPROC) (GLenum value, GLuint index, GLuint64EXT *result); -#ifdef GL_GLEXT_PROTOTYPES -GLAPI void APIENTRY glBufferAddressRangeNV (GLenum pname, GLuint index, GLuint64EXT address, GLsizeiptr length); -GLAPI void APIENTRY glVertexFormatNV (GLint size, GLenum type, GLsizei stride); -GLAPI void APIENTRY glNormalFormatNV (GLenum type, GLsizei stride); -GLAPI void APIENTRY glColorFormatNV (GLint size, GLenum type, GLsizei stride); -GLAPI void APIENTRY glIndexFormatNV (GLenum type, GLsizei stride); -GLAPI void APIENTRY glTexCoordFormatNV (GLint size, GLenum type, GLsizei stride); -GLAPI void APIENTRY glEdgeFlagFormatNV (GLsizei stride); -GLAPI void APIENTRY glSecondaryColorFormatNV (GLint size, GLenum type, GLsizei stride); -GLAPI void APIENTRY glFogCoordFormatNV (GLenum type, GLsizei stride); -GLAPI void APIENTRY glVertexAttribFormatNV (GLuint index, GLint size, GLenum type, GLboolean normalized, GLsizei stride); -GLAPI void APIENTRY glVertexAttribIFormatNV (GLuint index, GLint size, GLenum type, GLsizei stride); -GLAPI void APIENTRY glGetIntegerui64i_vNV (GLenum value, GLuint index, GLuint64EXT *result); -#endif -#endif /* GL_NV_vertex_buffer_unified_memory */ - -#ifndef GL_NV_vertex_program -#define GL_NV_vertex_program 1 -#define GL_VERTEX_PROGRAM_NV 0x8620 -#define GL_VERTEX_STATE_PROGRAM_NV 0x8621 -#define GL_ATTRIB_ARRAY_SIZE_NV 0x8623 -#define GL_ATTRIB_ARRAY_STRIDE_NV 0x8624 -#define GL_ATTRIB_ARRAY_TYPE_NV 0x8625 -#define GL_CURRENT_ATTRIB_NV 0x8626 -#define GL_PROGRAM_LENGTH_NV 0x8627 -#define GL_PROGRAM_STRING_NV 0x8628 -#define GL_MODELVIEW_PROJECTION_NV 0x8629 -#define GL_IDENTITY_NV 0x862A -#define GL_INVERSE_NV 0x862B -#define GL_TRANSPOSE_NV 0x862C -#define GL_INVERSE_TRANSPOSE_NV 0x862D -#define GL_MAX_TRACK_MATRIX_STACK_DEPTH_NV 0x862E -#define GL_MAX_TRACK_MATRICES_NV 0x862F -#define GL_MATRIX0_NV 0x8630 -#define GL_MATRIX1_NV 0x8631 -#define GL_MATRIX2_NV 0x8632 -#define GL_MATRIX3_NV 0x8633 -#define GL_MATRIX4_NV 0x8634 -#define GL_MATRIX5_NV 0x8635 -#define GL_MATRIX6_NV 0x8636 -#define GL_MATRIX7_NV 0x8637 -#define GL_CURRENT_MATRIX_STACK_DEPTH_NV 0x8640 -#define GL_CURRENT_MATRIX_NV 0x8641 -#define GL_VERTEX_PROGRAM_POINT_SIZE_NV 0x8642 -#define GL_VERTEX_PROGRAM_TWO_SIDE_NV 0x8643 -#define GL_PROGRAM_PARAMETER_NV 0x8644 -#define GL_ATTRIB_ARRAY_POINTER_NV 0x8645 -#define GL_PROGRAM_TARGET_NV 0x8646 -#define GL_PROGRAM_RESIDENT_NV 0x8647 -#define GL_TRACK_MATRIX_NV 0x8648 -#define GL_TRACK_MATRIX_TRANSFORM_NV 0x8649 -#define GL_VERTEX_PROGRAM_BINDING_NV 0x864A -#define GL_PROGRAM_ERROR_POSITION_NV 0x864B -#define GL_VERTEX_ATTRIB_ARRAY0_NV 0x8650 -#define GL_VERTEX_ATTRIB_ARRAY1_NV 0x8651 -#define GL_VERTEX_ATTRIB_ARRAY2_NV 0x8652 -#define GL_VERTEX_ATTRIB_ARRAY3_NV 0x8653 -#define GL_VERTEX_ATTRIB_ARRAY4_NV 0x8654 -#define GL_VERTEX_ATTRIB_ARRAY5_NV 0x8655 -#define GL_VERTEX_ATTRIB_ARRAY6_NV 0x8656 -#define GL_VERTEX_ATTRIB_ARRAY7_NV 0x8657 -#define GL_VERTEX_ATTRIB_ARRAY8_NV 0x8658 -#define GL_VERTEX_ATTRIB_ARRAY9_NV 0x8659 -#define GL_VERTEX_ATTRIB_ARRAY10_NV 0x865A -#define GL_VERTEX_ATTRIB_ARRAY11_NV 0x865B -#define GL_VERTEX_ATTRIB_ARRAY12_NV 0x865C -#define GL_VERTEX_ATTRIB_ARRAY13_NV 0x865D -#define GL_VERTEX_ATTRIB_ARRAY14_NV 0x865E -#define GL_VERTEX_ATTRIB_ARRAY15_NV 0x865F -#define GL_MAP1_VERTEX_ATTRIB0_4_NV 0x8660 -#define GL_MAP1_VERTEX_ATTRIB1_4_NV 0x8661 -#define GL_MAP1_VERTEX_ATTRIB2_4_NV 0x8662 -#define GL_MAP1_VERTEX_ATTRIB3_4_NV 0x8663 -#define GL_MAP1_VERTEX_ATTRIB4_4_NV 0x8664 -#define GL_MAP1_VERTEX_ATTRIB5_4_NV 0x8665 -#define GL_MAP1_VERTEX_ATTRIB6_4_NV 0x8666 -#define GL_MAP1_VERTEX_ATTRIB7_4_NV 0x8667 -#define GL_MAP1_VERTEX_ATTRIB8_4_NV 0x8668 -#define GL_MAP1_VERTEX_ATTRIB9_4_NV 0x8669 -#define GL_MAP1_VERTEX_ATTRIB10_4_NV 0x866A -#define GL_MAP1_VERTEX_ATTRIB11_4_NV 0x866B -#define GL_MAP1_VERTEX_ATTRIB12_4_NV 0x866C -#define GL_MAP1_VERTEX_ATTRIB13_4_NV 0x866D -#define GL_MAP1_VERTEX_ATTRIB14_4_NV 0x866E -#define GL_MAP1_VERTEX_ATTRIB15_4_NV 0x866F -#define GL_MAP2_VERTEX_ATTRIB0_4_NV 0x8670 -#define GL_MAP2_VERTEX_ATTRIB1_4_NV 0x8671 -#define GL_MAP2_VERTEX_ATTRIB2_4_NV 0x8672 -#define GL_MAP2_VERTEX_ATTRIB3_4_NV 0x8673 -#define GL_MAP2_VERTEX_ATTRIB4_4_NV 0x8674 -#define GL_MAP2_VERTEX_ATTRIB5_4_NV 0x8675 -#define GL_MAP2_VERTEX_ATTRIB6_4_NV 0x8676 -#define GL_MAP2_VERTEX_ATTRIB7_4_NV 0x8677 -#define GL_MAP2_VERTEX_ATTRIB8_4_NV 0x8678 -#define GL_MAP2_VERTEX_ATTRIB9_4_NV 0x8679 -#define GL_MAP2_VERTEX_ATTRIB10_4_NV 0x867A -#define GL_MAP2_VERTEX_ATTRIB11_4_NV 0x867B -#define GL_MAP2_VERTEX_ATTRIB12_4_NV 0x867C -#define GL_MAP2_VERTEX_ATTRIB13_4_NV 0x867D -#define GL_MAP2_VERTEX_ATTRIB14_4_NV 0x867E -#define GL_MAP2_VERTEX_ATTRIB15_4_NV 0x867F -typedef GLboolean (APIENTRYP PFNGLAREPROGRAMSRESIDENTNVPROC) (GLsizei n, const GLuint *programs, GLboolean *residences); -typedef void (APIENTRYP PFNGLBINDPROGRAMNVPROC) (GLenum target, GLuint id); -typedef void (APIENTRYP PFNGLDELETEPROGRAMSNVPROC) (GLsizei n, const GLuint *programs); -typedef void (APIENTRYP PFNGLEXECUTEPROGRAMNVPROC) (GLenum target, GLuint id, const GLfloat *params); -typedef void (APIENTRYP PFNGLGENPROGRAMSNVPROC) (GLsizei n, GLuint *programs); -typedef void (APIENTRYP PFNGLGETPROGRAMPARAMETERDVNVPROC) (GLenum target, GLuint index, GLenum pname, GLdouble *params); -typedef void (APIENTRYP PFNGLGETPROGRAMPARAMETERFVNVPROC) (GLenum target, GLuint index, GLenum pname, GLfloat *params); -typedef void (APIENTRYP PFNGLGETPROGRAMIVNVPROC) (GLuint id, GLenum pname, GLint *params); -typedef void (APIENTRYP PFNGLGETPROGRAMSTRINGNVPROC) (GLuint id, GLenum pname, GLubyte *program); -typedef void (APIENTRYP PFNGLGETTRACKMATRIXIVNVPROC) (GLenum target, GLuint address, GLenum pname, GLint *params); -typedef void (APIENTRYP PFNGLGETVERTEXATTRIBDVNVPROC) (GLuint index, GLenum pname, GLdouble *params); -typedef void (APIENTRYP PFNGLGETVERTEXATTRIBFVNVPROC) (GLuint index, GLenum pname, GLfloat *params); -typedef void (APIENTRYP PFNGLGETVERTEXATTRIBIVNVPROC) (GLuint index, GLenum pname, GLint *params); -typedef void (APIENTRYP PFNGLGETVERTEXATTRIBPOINTERVNVPROC) (GLuint index, GLenum pname, void **pointer); -typedef GLboolean (APIENTRYP PFNGLISPROGRAMNVPROC) (GLuint id); -typedef void (APIENTRYP PFNGLLOADPROGRAMNVPROC) (GLenum target, GLuint id, GLsizei len, const GLubyte *program); -typedef void (APIENTRYP PFNGLPROGRAMPARAMETER4DNVPROC) (GLenum target, GLuint index, GLdouble x, GLdouble y, GLdouble z, GLdouble w); -typedef void (APIENTRYP PFNGLPROGRAMPARAMETER4DVNVPROC) (GLenum target, GLuint index, const GLdouble *v); -typedef void (APIENTRYP PFNGLPROGRAMPARAMETER4FNVPROC) (GLenum target, GLuint index, GLfloat x, GLfloat y, GLfloat z, GLfloat w); -typedef void (APIENTRYP PFNGLPROGRAMPARAMETER4FVNVPROC) (GLenum target, GLuint index, const GLfloat *v); -typedef void (APIENTRYP PFNGLPROGRAMPARAMETERS4DVNVPROC) (GLenum target, GLuint index, GLsizei count, const GLdouble *v); -typedef void (APIENTRYP PFNGLPROGRAMPARAMETERS4FVNVPROC) (GLenum target, GLuint index, GLsizei count, const GLfloat *v); -typedef void (APIENTRYP PFNGLREQUESTRESIDENTPROGRAMSNVPROC) (GLsizei n, const GLuint *programs); -typedef void (APIENTRYP PFNGLTRACKMATRIXNVPROC) (GLenum target, GLuint address, GLenum matrix, GLenum transform); -typedef void (APIENTRYP PFNGLVERTEXATTRIBPOINTERNVPROC) (GLuint index, GLint fsize, GLenum type, GLsizei stride, const void *pointer); -typedef void (APIENTRYP PFNGLVERTEXATTRIB1DNVPROC) (GLuint index, GLdouble x); -typedef void (APIENTRYP PFNGLVERTEXATTRIB1DVNVPROC) (GLuint index, const GLdouble *v); -typedef void (APIENTRYP PFNGLVERTEXATTRIB1FNVPROC) (GLuint index, GLfloat x); -typedef void (APIENTRYP PFNGLVERTEXATTRIB1FVNVPROC) (GLuint index, const GLfloat *v); -typedef void (APIENTRYP PFNGLVERTEXATTRIB1SNVPROC) (GLuint index, GLshort x); -typedef void (APIENTRYP PFNGLVERTEXATTRIB1SVNVPROC) (GLuint index, const GLshort *v); -typedef void (APIENTRYP PFNGLVERTEXATTRIB2DNVPROC) (GLuint index, GLdouble x, GLdouble y); -typedef void (APIENTRYP PFNGLVERTEXATTRIB2DVNVPROC) (GLuint index, const GLdouble *v); -typedef void (APIENTRYP PFNGLVERTEXATTRIB2FNVPROC) (GLuint index, GLfloat x, GLfloat y); -typedef void (APIENTRYP PFNGLVERTEXATTRIB2FVNVPROC) (GLuint index, const GLfloat *v); -typedef void (APIENTRYP PFNGLVERTEXATTRIB2SNVPROC) (GLuint index, GLshort x, GLshort y); -typedef void (APIENTRYP PFNGLVERTEXATTRIB2SVNVPROC) (GLuint index, const GLshort *v); -typedef void (APIENTRYP PFNGLVERTEXATTRIB3DNVPROC) (GLuint index, GLdouble x, GLdouble y, GLdouble z); -typedef void (APIENTRYP PFNGLVERTEXATTRIB3DVNVPROC) (GLuint index, const GLdouble *v); -typedef void (APIENTRYP PFNGLVERTEXATTRIB3FNVPROC) (GLuint index, GLfloat x, GLfloat y, GLfloat z); -typedef void (APIENTRYP PFNGLVERTEXATTRIB3FVNVPROC) (GLuint index, const GLfloat *v); -typedef void (APIENTRYP PFNGLVERTEXATTRIB3SNVPROC) (GLuint index, GLshort x, GLshort y, GLshort z); -typedef void (APIENTRYP PFNGLVERTEXATTRIB3SVNVPROC) (GLuint index, const GLshort *v); -typedef void (APIENTRYP PFNGLVERTEXATTRIB4DNVPROC) (GLuint index, GLdouble x, GLdouble y, GLdouble z, GLdouble w); -typedef void (APIENTRYP PFNGLVERTEXATTRIB4DVNVPROC) (GLuint index, const GLdouble *v); -typedef void (APIENTRYP PFNGLVERTEXATTRIB4FNVPROC) (GLuint index, GLfloat x, GLfloat y, GLfloat z, GLfloat w); -typedef void (APIENTRYP PFNGLVERTEXATTRIB4FVNVPROC) (GLuint index, const GLfloat *v); -typedef void (APIENTRYP PFNGLVERTEXATTRIB4SNVPROC) (GLuint index, GLshort x, GLshort y, GLshort z, GLshort w); -typedef void (APIENTRYP PFNGLVERTEXATTRIB4SVNVPROC) (GLuint index, const GLshort *v); -typedef void (APIENTRYP PFNGLVERTEXATTRIB4UBNVPROC) (GLuint index, GLubyte x, GLubyte y, GLubyte z, GLubyte w); -typedef void (APIENTRYP PFNGLVERTEXATTRIB4UBVNVPROC) (GLuint index, const GLubyte *v); -typedef void (APIENTRYP PFNGLVERTEXATTRIBS1DVNVPROC) (GLuint index, GLsizei count, const GLdouble *v); -typedef void (APIENTRYP PFNGLVERTEXATTRIBS1FVNVPROC) (GLuint index, GLsizei count, const GLfloat *v); -typedef void (APIENTRYP PFNGLVERTEXATTRIBS1SVNVPROC) (GLuint index, GLsizei count, const GLshort *v); -typedef void (APIENTRYP PFNGLVERTEXATTRIBS2DVNVPROC) (GLuint index, GLsizei count, const GLdouble *v); -typedef void (APIENTRYP PFNGLVERTEXATTRIBS2FVNVPROC) (GLuint index, GLsizei count, const GLfloat *v); -typedef void (APIENTRYP PFNGLVERTEXATTRIBS2SVNVPROC) (GLuint index, GLsizei count, const GLshort *v); -typedef void (APIENTRYP PFNGLVERTEXATTRIBS3DVNVPROC) (GLuint index, GLsizei count, const GLdouble *v); -typedef void (APIENTRYP PFNGLVERTEXATTRIBS3FVNVPROC) (GLuint index, GLsizei count, const GLfloat *v); -typedef void (APIENTRYP PFNGLVERTEXATTRIBS3SVNVPROC) (GLuint index, GLsizei count, const GLshort *v); -typedef void (APIENTRYP PFNGLVERTEXATTRIBS4DVNVPROC) (GLuint index, GLsizei count, const GLdouble *v); -typedef void (APIENTRYP PFNGLVERTEXATTRIBS4FVNVPROC) (GLuint index, GLsizei count, const GLfloat *v); -typedef void (APIENTRYP PFNGLVERTEXATTRIBS4SVNVPROC) (GLuint index, GLsizei count, const GLshort *v); -typedef void (APIENTRYP PFNGLVERTEXATTRIBS4UBVNVPROC) (GLuint index, GLsizei count, const GLubyte *v); -#ifdef GL_GLEXT_PROTOTYPES -GLAPI GLboolean APIENTRY glAreProgramsResidentNV (GLsizei n, const GLuint *programs, GLboolean *residences); -GLAPI void APIENTRY glBindProgramNV (GLenum target, GLuint id); -GLAPI void APIENTRY glDeleteProgramsNV (GLsizei n, const GLuint *programs); -GLAPI void APIENTRY glExecuteProgramNV (GLenum target, GLuint id, const GLfloat *params); -GLAPI void APIENTRY glGenProgramsNV (GLsizei n, GLuint *programs); -GLAPI void APIENTRY glGetProgramParameterdvNV (GLenum target, GLuint index, GLenum pname, GLdouble *params); -GLAPI void APIENTRY glGetProgramParameterfvNV (GLenum target, GLuint index, GLenum pname, GLfloat *params); -GLAPI void APIENTRY glGetProgramivNV (GLuint id, GLenum pname, GLint *params); -GLAPI void APIENTRY glGetProgramStringNV (GLuint id, GLenum pname, GLubyte *program); -GLAPI void APIENTRY glGetTrackMatrixivNV (GLenum target, GLuint address, GLenum pname, GLint *params); -GLAPI void APIENTRY glGetVertexAttribdvNV (GLuint index, GLenum pname, GLdouble *params); -GLAPI void APIENTRY glGetVertexAttribfvNV (GLuint index, GLenum pname, GLfloat *params); -GLAPI void APIENTRY glGetVertexAttribivNV (GLuint index, GLenum pname, GLint *params); -GLAPI void APIENTRY glGetVertexAttribPointervNV (GLuint index, GLenum pname, void **pointer); -GLAPI GLboolean APIENTRY glIsProgramNV (GLuint id); -GLAPI void APIENTRY glLoadProgramNV (GLenum target, GLuint id, GLsizei len, const GLubyte *program); -GLAPI void APIENTRY glProgramParameter4dNV (GLenum target, GLuint index, GLdouble x, GLdouble y, GLdouble z, GLdouble w); -GLAPI void APIENTRY glProgramParameter4dvNV (GLenum target, GLuint index, const GLdouble *v); -GLAPI void APIENTRY glProgramParameter4fNV (GLenum target, GLuint index, GLfloat x, GLfloat y, GLfloat z, GLfloat w); -GLAPI void APIENTRY glProgramParameter4fvNV (GLenum target, GLuint index, const GLfloat *v); -GLAPI void APIENTRY glProgramParameters4dvNV (GLenum target, GLuint index, GLsizei count, const GLdouble *v); -GLAPI void APIENTRY glProgramParameters4fvNV (GLenum target, GLuint index, GLsizei count, const GLfloat *v); -GLAPI void APIENTRY glRequestResidentProgramsNV (GLsizei n, const GLuint *programs); -GLAPI void APIENTRY glTrackMatrixNV (GLenum target, GLuint address, GLenum matrix, GLenum transform); -GLAPI void APIENTRY glVertexAttribPointerNV (GLuint index, GLint fsize, GLenum type, GLsizei stride, const void *pointer); -GLAPI void APIENTRY glVertexAttrib1dNV (GLuint index, GLdouble x); -GLAPI void APIENTRY glVertexAttrib1dvNV (GLuint index, const GLdouble *v); -GLAPI void APIENTRY glVertexAttrib1fNV (GLuint index, GLfloat x); -GLAPI void APIENTRY glVertexAttrib1fvNV (GLuint index, const GLfloat *v); -GLAPI void APIENTRY glVertexAttrib1sNV (GLuint index, GLshort x); -GLAPI void APIENTRY glVertexAttrib1svNV (GLuint index, const GLshort *v); -GLAPI void APIENTRY glVertexAttrib2dNV (GLuint index, GLdouble x, GLdouble y); -GLAPI void APIENTRY glVertexAttrib2dvNV (GLuint index, const GLdouble *v); -GLAPI void APIENTRY glVertexAttrib2fNV (GLuint index, GLfloat x, GLfloat y); -GLAPI void APIENTRY glVertexAttrib2fvNV (GLuint index, const GLfloat *v); -GLAPI void APIENTRY glVertexAttrib2sNV (GLuint index, GLshort x, GLshort y); -GLAPI void APIENTRY glVertexAttrib2svNV (GLuint index, const GLshort *v); -GLAPI void APIENTRY glVertexAttrib3dNV (GLuint index, GLdouble x, GLdouble y, GLdouble z); -GLAPI void APIENTRY glVertexAttrib3dvNV (GLuint index, const GLdouble *v); -GLAPI void APIENTRY glVertexAttrib3fNV (GLuint index, GLfloat x, GLfloat y, GLfloat z); -GLAPI void APIENTRY glVertexAttrib3fvNV (GLuint index, const GLfloat *v); -GLAPI void APIENTRY glVertexAttrib3sNV (GLuint index, GLshort x, GLshort y, GLshort z); -GLAPI void APIENTRY glVertexAttrib3svNV (GLuint index, const GLshort *v); -GLAPI void APIENTRY glVertexAttrib4dNV (GLuint index, GLdouble x, GLdouble y, GLdouble z, GLdouble w); -GLAPI void APIENTRY glVertexAttrib4dvNV (GLuint index, const GLdouble *v); -GLAPI void APIENTRY glVertexAttrib4fNV (GLuint index, GLfloat x, GLfloat y, GLfloat z, GLfloat w); -GLAPI void APIENTRY glVertexAttrib4fvNV (GLuint index, const GLfloat *v); -GLAPI void APIENTRY glVertexAttrib4sNV (GLuint index, GLshort x, GLshort y, GLshort z, GLshort w); -GLAPI void APIENTRY glVertexAttrib4svNV (GLuint index, const GLshort *v); -GLAPI void APIENTRY glVertexAttrib4ubNV (GLuint index, GLubyte x, GLubyte y, GLubyte z, GLubyte w); -GLAPI void APIENTRY glVertexAttrib4ubvNV (GLuint index, const GLubyte *v); -GLAPI void APIENTRY glVertexAttribs1dvNV (GLuint index, GLsizei count, const GLdouble *v); -GLAPI void APIENTRY glVertexAttribs1fvNV (GLuint index, GLsizei count, const GLfloat *v); -GLAPI void APIENTRY glVertexAttribs1svNV (GLuint index, GLsizei count, const GLshort *v); -GLAPI void APIENTRY glVertexAttribs2dvNV (GLuint index, GLsizei count, const GLdouble *v); -GLAPI void APIENTRY glVertexAttribs2fvNV (GLuint index, GLsizei count, const GLfloat *v); -GLAPI void APIENTRY glVertexAttribs2svNV (GLuint index, GLsizei count, const GLshort *v); -GLAPI void APIENTRY glVertexAttribs3dvNV (GLuint index, GLsizei count, const GLdouble *v); -GLAPI void APIENTRY glVertexAttribs3fvNV (GLuint index, GLsizei count, const GLfloat *v); -GLAPI void APIENTRY glVertexAttribs3svNV (GLuint index, GLsizei count, const GLshort *v); -GLAPI void APIENTRY glVertexAttribs4dvNV (GLuint index, GLsizei count, const GLdouble *v); -GLAPI void APIENTRY glVertexAttribs4fvNV (GLuint index, GLsizei count, const GLfloat *v); -GLAPI void APIENTRY glVertexAttribs4svNV (GLuint index, GLsizei count, const GLshort *v); -GLAPI void APIENTRY glVertexAttribs4ubvNV (GLuint index, GLsizei count, const GLubyte *v); -#endif -#endif /* GL_NV_vertex_program */ - -#ifndef GL_NV_vertex_program1_1 -#define GL_NV_vertex_program1_1 1 -#endif /* GL_NV_vertex_program1_1 */ - -#ifndef GL_NV_vertex_program2 -#define GL_NV_vertex_program2 1 -#endif /* GL_NV_vertex_program2 */ - -#ifndef GL_NV_vertex_program2_option -#define GL_NV_vertex_program2_option 1 -#endif /* GL_NV_vertex_program2_option */ - -#ifndef GL_NV_vertex_program3 -#define GL_NV_vertex_program3 1 -#endif /* GL_NV_vertex_program3 */ - -#ifndef GL_NV_vertex_program4 -#define GL_NV_vertex_program4 1 -#define GL_VERTEX_ATTRIB_ARRAY_INTEGER_NV 0x88FD -typedef void (APIENTRYP PFNGLVERTEXATTRIBI1IEXTPROC) (GLuint index, GLint x); -typedef void (APIENTRYP PFNGLVERTEXATTRIBI2IEXTPROC) (GLuint index, GLint x, GLint y); -typedef void (APIENTRYP PFNGLVERTEXATTRIBI3IEXTPROC) (GLuint index, GLint x, GLint y, GLint z); -typedef void (APIENTRYP PFNGLVERTEXATTRIBI4IEXTPROC) (GLuint index, GLint x, GLint y, GLint z, GLint w); -typedef void (APIENTRYP PFNGLVERTEXATTRIBI1UIEXTPROC) (GLuint index, GLuint x); -typedef void (APIENTRYP PFNGLVERTEXATTRIBI2UIEXTPROC) (GLuint index, GLuint x, GLuint y); -typedef void (APIENTRYP PFNGLVERTEXATTRIBI3UIEXTPROC) (GLuint index, GLuint x, GLuint y, GLuint z); -typedef void (APIENTRYP PFNGLVERTEXATTRIBI4UIEXTPROC) (GLuint index, GLuint x, GLuint y, GLuint z, GLuint w); -typedef void (APIENTRYP PFNGLVERTEXATTRIBI1IVEXTPROC) (GLuint index, const GLint *v); -typedef void (APIENTRYP PFNGLVERTEXATTRIBI2IVEXTPROC) (GLuint index, const GLint *v); -typedef void (APIENTRYP PFNGLVERTEXATTRIBI3IVEXTPROC) (GLuint index, const GLint *v); -typedef void (APIENTRYP PFNGLVERTEXATTRIBI4IVEXTPROC) (GLuint index, const GLint *v); -typedef void (APIENTRYP PFNGLVERTEXATTRIBI1UIVEXTPROC) (GLuint index, const GLuint *v); -typedef void (APIENTRYP PFNGLVERTEXATTRIBI2UIVEXTPROC) (GLuint index, const GLuint *v); -typedef void (APIENTRYP PFNGLVERTEXATTRIBI3UIVEXTPROC) (GLuint index, const GLuint *v); -typedef void (APIENTRYP PFNGLVERTEXATTRIBI4UIVEXTPROC) (GLuint index, const GLuint *v); -typedef void (APIENTRYP PFNGLVERTEXATTRIBI4BVEXTPROC) (GLuint index, const GLbyte *v); -typedef void (APIENTRYP PFNGLVERTEXATTRIBI4SVEXTPROC) (GLuint index, const GLshort *v); -typedef void (APIENTRYP PFNGLVERTEXATTRIBI4UBVEXTPROC) (GLuint index, const GLubyte *v); -typedef void (APIENTRYP PFNGLVERTEXATTRIBI4USVEXTPROC) (GLuint index, const GLushort *v); -typedef void (APIENTRYP PFNGLVERTEXATTRIBIPOINTEREXTPROC) (GLuint index, GLint size, GLenum type, GLsizei stride, const void *pointer); -typedef void (APIENTRYP PFNGLGETVERTEXATTRIBIIVEXTPROC) (GLuint index, GLenum pname, GLint *params); -typedef void (APIENTRYP PFNGLGETVERTEXATTRIBIUIVEXTPROC) (GLuint index, GLenum pname, GLuint *params); -#ifdef GL_GLEXT_PROTOTYPES -GLAPI void APIENTRY glVertexAttribI1iEXT (GLuint index, GLint x); -GLAPI void APIENTRY glVertexAttribI2iEXT (GLuint index, GLint x, GLint y); -GLAPI void APIENTRY glVertexAttribI3iEXT (GLuint index, GLint x, GLint y, GLint z); -GLAPI void APIENTRY glVertexAttribI4iEXT (GLuint index, GLint x, GLint y, GLint z, GLint w); -GLAPI void APIENTRY glVertexAttribI1uiEXT (GLuint index, GLuint x); -GLAPI void APIENTRY glVertexAttribI2uiEXT (GLuint index, GLuint x, GLuint y); -GLAPI void APIENTRY glVertexAttribI3uiEXT (GLuint index, GLuint x, GLuint y, GLuint z); -GLAPI void APIENTRY glVertexAttribI4uiEXT (GLuint index, GLuint x, GLuint y, GLuint z, GLuint w); -GLAPI void APIENTRY glVertexAttribI1ivEXT (GLuint index, const GLint *v); -GLAPI void APIENTRY glVertexAttribI2ivEXT (GLuint index, const GLint *v); -GLAPI void APIENTRY glVertexAttribI3ivEXT (GLuint index, const GLint *v); -GLAPI void APIENTRY glVertexAttribI4ivEXT (GLuint index, const GLint *v); -GLAPI void APIENTRY glVertexAttribI1uivEXT (GLuint index, const GLuint *v); -GLAPI void APIENTRY glVertexAttribI2uivEXT (GLuint index, const GLuint *v); -GLAPI void APIENTRY glVertexAttribI3uivEXT (GLuint index, const GLuint *v); -GLAPI void APIENTRY glVertexAttribI4uivEXT (GLuint index, const GLuint *v); -GLAPI void APIENTRY glVertexAttribI4bvEXT (GLuint index, const GLbyte *v); -GLAPI void APIENTRY glVertexAttribI4svEXT (GLuint index, const GLshort *v); -GLAPI void APIENTRY glVertexAttribI4ubvEXT (GLuint index, const GLubyte *v); -GLAPI void APIENTRY glVertexAttribI4usvEXT (GLuint index, const GLushort *v); -GLAPI void APIENTRY glVertexAttribIPointerEXT (GLuint index, GLint size, GLenum type, GLsizei stride, const void *pointer); -GLAPI void APIENTRY glGetVertexAttribIivEXT (GLuint index, GLenum pname, GLint *params); -GLAPI void APIENTRY glGetVertexAttribIuivEXT (GLuint index, GLenum pname, GLuint *params); -#endif -#endif /* GL_NV_vertex_program4 */ - -#ifndef GL_NV_video_capture -#define GL_NV_video_capture 1 -#define GL_VIDEO_BUFFER_NV 0x9020 -#define GL_VIDEO_BUFFER_BINDING_NV 0x9021 -#define GL_FIELD_UPPER_NV 0x9022 -#define GL_FIELD_LOWER_NV 0x9023 -#define GL_NUM_VIDEO_CAPTURE_STREAMS_NV 0x9024 -#define GL_NEXT_VIDEO_CAPTURE_BUFFER_STATUS_NV 0x9025 -#define GL_VIDEO_CAPTURE_TO_422_SUPPORTED_NV 0x9026 -#define GL_LAST_VIDEO_CAPTURE_STATUS_NV 0x9027 -#define GL_VIDEO_BUFFER_PITCH_NV 0x9028 -#define GL_VIDEO_COLOR_CONVERSION_MATRIX_NV 0x9029 -#define GL_VIDEO_COLOR_CONVERSION_MAX_NV 0x902A -#define GL_VIDEO_COLOR_CONVERSION_MIN_NV 0x902B -#define GL_VIDEO_COLOR_CONVERSION_OFFSET_NV 0x902C -#define GL_VIDEO_BUFFER_INTERNAL_FORMAT_NV 0x902D -#define GL_PARTIAL_SUCCESS_NV 0x902E -#define GL_SUCCESS_NV 0x902F -#define GL_FAILURE_NV 0x9030 -#define GL_YCBYCR8_422_NV 0x9031 -#define GL_YCBAYCR8A_4224_NV 0x9032 -#define GL_Z6Y10Z6CB10Z6Y10Z6CR10_422_NV 0x9033 -#define GL_Z6Y10Z6CB10Z6A10Z6Y10Z6CR10Z6A10_4224_NV 0x9034 -#define GL_Z4Y12Z4CB12Z4Y12Z4CR12_422_NV 0x9035 -#define GL_Z4Y12Z4CB12Z4A12Z4Y12Z4CR12Z4A12_4224_NV 0x9036 -#define GL_Z4Y12Z4CB12Z4CR12_444_NV 0x9037 -#define GL_VIDEO_CAPTURE_FRAME_WIDTH_NV 0x9038 -#define GL_VIDEO_CAPTURE_FRAME_HEIGHT_NV 0x9039 -#define GL_VIDEO_CAPTURE_FIELD_UPPER_HEIGHT_NV 0x903A -#define GL_VIDEO_CAPTURE_FIELD_LOWER_HEIGHT_NV 0x903B -#define GL_VIDEO_CAPTURE_SURFACE_ORIGIN_NV 0x903C -typedef void (APIENTRYP PFNGLBEGINVIDEOCAPTURENVPROC) (GLuint video_capture_slot); -typedef void (APIENTRYP PFNGLBINDVIDEOCAPTURESTREAMBUFFERNVPROC) (GLuint video_capture_slot, GLuint stream, GLenum frame_region, GLintptrARB offset); -typedef void (APIENTRYP PFNGLBINDVIDEOCAPTURESTREAMTEXTURENVPROC) (GLuint video_capture_slot, GLuint stream, GLenum frame_region, GLenum target, GLuint texture); -typedef void (APIENTRYP PFNGLENDVIDEOCAPTURENVPROC) (GLuint video_capture_slot); -typedef void (APIENTRYP PFNGLGETVIDEOCAPTUREIVNVPROC) (GLuint video_capture_slot, GLenum pname, GLint *params); -typedef void (APIENTRYP PFNGLGETVIDEOCAPTURESTREAMIVNVPROC) (GLuint video_capture_slot, GLuint stream, GLenum pname, GLint *params); -typedef void (APIENTRYP PFNGLGETVIDEOCAPTURESTREAMFVNVPROC) (GLuint video_capture_slot, GLuint stream, GLenum pname, GLfloat *params); -typedef void (APIENTRYP PFNGLGETVIDEOCAPTURESTREAMDVNVPROC) (GLuint video_capture_slot, GLuint stream, GLenum pname, GLdouble *params); -typedef GLenum (APIENTRYP PFNGLVIDEOCAPTURENVPROC) (GLuint video_capture_slot, GLuint *sequence_num, GLuint64EXT *capture_time); -typedef void (APIENTRYP PFNGLVIDEOCAPTURESTREAMPARAMETERIVNVPROC) (GLuint video_capture_slot, GLuint stream, GLenum pname, const GLint *params); -typedef void (APIENTRYP PFNGLVIDEOCAPTURESTREAMPARAMETERFVNVPROC) (GLuint video_capture_slot, GLuint stream, GLenum pname, const GLfloat *params); -typedef void (APIENTRYP PFNGLVIDEOCAPTURESTREAMPARAMETERDVNVPROC) (GLuint video_capture_slot, GLuint stream, GLenum pname, const GLdouble *params); -#ifdef GL_GLEXT_PROTOTYPES -GLAPI void APIENTRY glBeginVideoCaptureNV (GLuint video_capture_slot); -GLAPI void APIENTRY glBindVideoCaptureStreamBufferNV (GLuint video_capture_slot, GLuint stream, GLenum frame_region, GLintptrARB offset); -GLAPI void APIENTRY glBindVideoCaptureStreamTextureNV (GLuint video_capture_slot, GLuint stream, GLenum frame_region, GLenum target, GLuint texture); -GLAPI void APIENTRY glEndVideoCaptureNV (GLuint video_capture_slot); -GLAPI void APIENTRY glGetVideoCaptureivNV (GLuint video_capture_slot, GLenum pname, GLint *params); -GLAPI void APIENTRY glGetVideoCaptureStreamivNV (GLuint video_capture_slot, GLuint stream, GLenum pname, GLint *params); -GLAPI void APIENTRY glGetVideoCaptureStreamfvNV (GLuint video_capture_slot, GLuint stream, GLenum pname, GLfloat *params); -GLAPI void APIENTRY glGetVideoCaptureStreamdvNV (GLuint video_capture_slot, GLuint stream, GLenum pname, GLdouble *params); -GLAPI GLenum APIENTRY glVideoCaptureNV (GLuint video_capture_slot, GLuint *sequence_num, GLuint64EXT *capture_time); -GLAPI void APIENTRY glVideoCaptureStreamParameterivNV (GLuint video_capture_slot, GLuint stream, GLenum pname, const GLint *params); -GLAPI void APIENTRY glVideoCaptureStreamParameterfvNV (GLuint video_capture_slot, GLuint stream, GLenum pname, const GLfloat *params); -GLAPI void APIENTRY glVideoCaptureStreamParameterdvNV (GLuint video_capture_slot, GLuint stream, GLenum pname, const GLdouble *params); -#endif -#endif /* GL_NV_video_capture */ - -#ifndef GL_OML_interlace -#define GL_OML_interlace 1 -#define GL_INTERLACE_OML 0x8980 -#define GL_INTERLACE_READ_OML 0x8981 -#endif /* GL_OML_interlace */ - -#ifndef GL_OML_resample -#define GL_OML_resample 1 -#define GL_PACK_RESAMPLE_OML 0x8984 -#define GL_UNPACK_RESAMPLE_OML 0x8985 -#define GL_RESAMPLE_REPLICATE_OML 0x8986 -#define GL_RESAMPLE_ZERO_FILL_OML 0x8987 -#define GL_RESAMPLE_AVERAGE_OML 0x8988 -#define GL_RESAMPLE_DECIMATE_OML 0x8989 -#endif /* GL_OML_resample */ - -#ifndef GL_OML_subsample -#define GL_OML_subsample 1 -#define GL_FORMAT_SUBSAMPLE_24_24_OML 0x8982 -#define GL_FORMAT_SUBSAMPLE_244_244_OML 0x8983 -#endif /* GL_OML_subsample */ - -#ifndef GL_PGI_misc_hints -#define GL_PGI_misc_hints 1 -#define GL_PREFER_DOUBLEBUFFER_HINT_PGI 0x1A1F8 -#define GL_CONSERVE_MEMORY_HINT_PGI 0x1A1FD -#define GL_RECLAIM_MEMORY_HINT_PGI 0x1A1FE -#define GL_NATIVE_GRAPHICS_HANDLE_PGI 0x1A202 -#define GL_NATIVE_GRAPHICS_BEGIN_HINT_PGI 0x1A203 -#define GL_NATIVE_GRAPHICS_END_HINT_PGI 0x1A204 -#define GL_ALWAYS_FAST_HINT_PGI 0x1A20C -#define GL_ALWAYS_SOFT_HINT_PGI 0x1A20D -#define GL_ALLOW_DRAW_OBJ_HINT_PGI 0x1A20E -#define GL_ALLOW_DRAW_WIN_HINT_PGI 0x1A20F -#define GL_ALLOW_DRAW_FRG_HINT_PGI 0x1A210 -#define GL_ALLOW_DRAW_MEM_HINT_PGI 0x1A211 -#define GL_STRICT_DEPTHFUNC_HINT_PGI 0x1A216 -#define GL_STRICT_LIGHTING_HINT_PGI 0x1A217 -#define GL_STRICT_SCISSOR_HINT_PGI 0x1A218 -#define GL_FULL_STIPPLE_HINT_PGI 0x1A219 -#define GL_CLIP_NEAR_HINT_PGI 0x1A220 -#define GL_CLIP_FAR_HINT_PGI 0x1A221 -#define GL_WIDE_LINE_HINT_PGI 0x1A222 -#define GL_BACK_NORMALS_HINT_PGI 0x1A223 -typedef void (APIENTRYP PFNGLHINTPGIPROC) (GLenum target, GLint mode); -#ifdef GL_GLEXT_PROTOTYPES -GLAPI void APIENTRY glHintPGI (GLenum target, GLint mode); -#endif -#endif /* GL_PGI_misc_hints */ - -#ifndef GL_PGI_vertex_hints -#define GL_PGI_vertex_hints 1 -#define GL_VERTEX_DATA_HINT_PGI 0x1A22A -#define GL_VERTEX_CONSISTENT_HINT_PGI 0x1A22B -#define GL_MATERIAL_SIDE_HINT_PGI 0x1A22C -#define GL_MAX_VERTEX_HINT_PGI 0x1A22D -#define GL_COLOR3_BIT_PGI 0x00010000 -#define GL_COLOR4_BIT_PGI 0x00020000 -#define GL_EDGEFLAG_BIT_PGI 0x00040000 -#define GL_INDEX_BIT_PGI 0x00080000 -#define GL_MAT_AMBIENT_BIT_PGI 0x00100000 -#define GL_MAT_AMBIENT_AND_DIFFUSE_BIT_PGI 0x00200000 -#define GL_MAT_DIFFUSE_BIT_PGI 0x00400000 -#define GL_MAT_EMISSION_BIT_PGI 0x00800000 -#define GL_MAT_COLOR_INDEXES_BIT_PGI 0x01000000 -#define GL_MAT_SHININESS_BIT_PGI 0x02000000 -#define GL_MAT_SPECULAR_BIT_PGI 0x04000000 -#define GL_NORMAL_BIT_PGI 0x08000000 -#define GL_TEXCOORD1_BIT_PGI 0x10000000 -#define GL_TEXCOORD2_BIT_PGI 0x20000000 -#define GL_TEXCOORD3_BIT_PGI 0x40000000 -#define GL_TEXCOORD4_BIT_PGI 0x80000000 -#define GL_VERTEX23_BIT_PGI 0x00000004 -#define GL_VERTEX4_BIT_PGI 0x00000008 -#endif /* GL_PGI_vertex_hints */ - -#ifndef GL_REND_screen_coordinates -#define GL_REND_screen_coordinates 1 -#define GL_SCREEN_COORDINATES_REND 0x8490 -#define GL_INVERTED_SCREEN_W_REND 0x8491 -#endif /* GL_REND_screen_coordinates */ - -#ifndef GL_S3_s3tc -#define GL_S3_s3tc 1 -#define GL_RGB_S3TC 0x83A0 -#define GL_RGB4_S3TC 0x83A1 -#define GL_RGBA_S3TC 0x83A2 -#define GL_RGBA4_S3TC 0x83A3 -#define GL_RGBA_DXT5_S3TC 0x83A4 -#define GL_RGBA4_DXT5_S3TC 0x83A5 -#endif /* GL_S3_s3tc */ - -#ifndef GL_SGIS_detail_texture -#define GL_SGIS_detail_texture 1 -#define GL_DETAIL_TEXTURE_2D_SGIS 0x8095 -#define GL_DETAIL_TEXTURE_2D_BINDING_SGIS 0x8096 -#define GL_LINEAR_DETAIL_SGIS 0x8097 -#define GL_LINEAR_DETAIL_ALPHA_SGIS 0x8098 -#define GL_LINEAR_DETAIL_COLOR_SGIS 0x8099 -#define GL_DETAIL_TEXTURE_LEVEL_SGIS 0x809A -#define GL_DETAIL_TEXTURE_MODE_SGIS 0x809B -#define GL_DETAIL_TEXTURE_FUNC_POINTS_SGIS 0x809C -typedef void (APIENTRYP PFNGLDETAILTEXFUNCSGISPROC) (GLenum target, GLsizei n, const GLfloat *points); -typedef void (APIENTRYP PFNGLGETDETAILTEXFUNCSGISPROC) (GLenum target, GLfloat *points); -#ifdef GL_GLEXT_PROTOTYPES -GLAPI void APIENTRY glDetailTexFuncSGIS (GLenum target, GLsizei n, const GLfloat *points); -GLAPI void APIENTRY glGetDetailTexFuncSGIS (GLenum target, GLfloat *points); -#endif -#endif /* GL_SGIS_detail_texture */ - -#ifndef GL_SGIS_fog_function -#define GL_SGIS_fog_function 1 -#define GL_FOG_FUNC_SGIS 0x812A -#define GL_FOG_FUNC_POINTS_SGIS 0x812B -#define GL_MAX_FOG_FUNC_POINTS_SGIS 0x812C -typedef void (APIENTRYP PFNGLFOGFUNCSGISPROC) (GLsizei n, const GLfloat *points); -typedef void (APIENTRYP PFNGLGETFOGFUNCSGISPROC) (GLfloat *points); -#ifdef GL_GLEXT_PROTOTYPES -GLAPI void APIENTRY glFogFuncSGIS (GLsizei n, const GLfloat *points); -GLAPI void APIENTRY glGetFogFuncSGIS (GLfloat *points); -#endif -#endif /* GL_SGIS_fog_function */ - -#ifndef GL_SGIS_generate_mipmap -#define GL_SGIS_generate_mipmap 1 -#define GL_GENERATE_MIPMAP_SGIS 0x8191 -#define GL_GENERATE_MIPMAP_HINT_SGIS 0x8192 -#endif /* GL_SGIS_generate_mipmap */ - -#ifndef GL_SGIS_multisample -#define GL_SGIS_multisample 1 -#define GL_MULTISAMPLE_SGIS 0x809D -#define GL_SAMPLE_ALPHA_TO_MASK_SGIS 0x809E -#define GL_SAMPLE_ALPHA_TO_ONE_SGIS 0x809F -#define GL_SAMPLE_MASK_SGIS 0x80A0 -#define GL_1PASS_SGIS 0x80A1 -#define GL_2PASS_0_SGIS 0x80A2 -#define GL_2PASS_1_SGIS 0x80A3 -#define GL_4PASS_0_SGIS 0x80A4 -#define GL_4PASS_1_SGIS 0x80A5 -#define GL_4PASS_2_SGIS 0x80A6 -#define GL_4PASS_3_SGIS 0x80A7 -#define GL_SAMPLE_BUFFERS_SGIS 0x80A8 -#define GL_SAMPLES_SGIS 0x80A9 -#define GL_SAMPLE_MASK_VALUE_SGIS 0x80AA -#define GL_SAMPLE_MASK_INVERT_SGIS 0x80AB -#define GL_SAMPLE_PATTERN_SGIS 0x80AC -typedef void (APIENTRYP PFNGLSAMPLEMASKSGISPROC) (GLclampf value, GLboolean invert); -typedef void (APIENTRYP PFNGLSAMPLEPATTERNSGISPROC) (GLenum pattern); -#ifdef GL_GLEXT_PROTOTYPES -GLAPI void APIENTRY glSampleMaskSGIS (GLclampf value, GLboolean invert); -GLAPI void APIENTRY glSamplePatternSGIS (GLenum pattern); -#endif -#endif /* GL_SGIS_multisample */ - -#ifndef GL_SGIS_pixel_texture -#define GL_SGIS_pixel_texture 1 -#define GL_PIXEL_TEXTURE_SGIS 0x8353 -#define GL_PIXEL_FRAGMENT_RGB_SOURCE_SGIS 0x8354 -#define GL_PIXEL_FRAGMENT_ALPHA_SOURCE_SGIS 0x8355 -#define GL_PIXEL_GROUP_COLOR_SGIS 0x8356 -typedef void (APIENTRYP PFNGLPIXELTEXGENPARAMETERISGISPROC) (GLenum pname, GLint param); -typedef void (APIENTRYP PFNGLPIXELTEXGENPARAMETERIVSGISPROC) (GLenum pname, const GLint *params); -typedef void (APIENTRYP PFNGLPIXELTEXGENPARAMETERFSGISPROC) (GLenum pname, GLfloat param); -typedef void (APIENTRYP PFNGLPIXELTEXGENPARAMETERFVSGISPROC) (GLenum pname, const GLfloat *params); -typedef void (APIENTRYP PFNGLGETPIXELTEXGENPARAMETERIVSGISPROC) (GLenum pname, GLint *params); -typedef void (APIENTRYP PFNGLGETPIXELTEXGENPARAMETERFVSGISPROC) (GLenum pname, GLfloat *params); -#ifdef GL_GLEXT_PROTOTYPES -GLAPI void APIENTRY glPixelTexGenParameteriSGIS (GLenum pname, GLint param); -GLAPI void APIENTRY glPixelTexGenParameterivSGIS (GLenum pname, const GLint *params); -GLAPI void APIENTRY glPixelTexGenParameterfSGIS (GLenum pname, GLfloat param); -GLAPI void APIENTRY glPixelTexGenParameterfvSGIS (GLenum pname, const GLfloat *params); -GLAPI void APIENTRY glGetPixelTexGenParameterivSGIS (GLenum pname, GLint *params); -GLAPI void APIENTRY glGetPixelTexGenParameterfvSGIS (GLenum pname, GLfloat *params); -#endif -#endif /* GL_SGIS_pixel_texture */ - -#ifndef GL_SGIS_point_line_texgen -#define GL_SGIS_point_line_texgen 1 -#define GL_EYE_DISTANCE_TO_POINT_SGIS 0x81F0 -#define GL_OBJECT_DISTANCE_TO_POINT_SGIS 0x81F1 -#define GL_EYE_DISTANCE_TO_LINE_SGIS 0x81F2 -#define GL_OBJECT_DISTANCE_TO_LINE_SGIS 0x81F3 -#define GL_EYE_POINT_SGIS 0x81F4 -#define GL_OBJECT_POINT_SGIS 0x81F5 -#define GL_EYE_LINE_SGIS 0x81F6 -#define GL_OBJECT_LINE_SGIS 0x81F7 -#endif /* GL_SGIS_point_line_texgen */ - -#ifndef GL_SGIS_point_parameters -#define GL_SGIS_point_parameters 1 -#define GL_POINT_SIZE_MIN_SGIS 0x8126 -#define GL_POINT_SIZE_MAX_SGIS 0x8127 -#define GL_POINT_FADE_THRESHOLD_SIZE_SGIS 0x8128 -#define GL_DISTANCE_ATTENUATION_SGIS 0x8129 -typedef void (APIENTRYP PFNGLPOINTPARAMETERFSGISPROC) (GLenum pname, GLfloat param); -typedef void (APIENTRYP PFNGLPOINTPARAMETERFVSGISPROC) (GLenum pname, const GLfloat *params); -#ifdef GL_GLEXT_PROTOTYPES -GLAPI void APIENTRY glPointParameterfSGIS (GLenum pname, GLfloat param); -GLAPI void APIENTRY glPointParameterfvSGIS (GLenum pname, const GLfloat *params); -#endif -#endif /* GL_SGIS_point_parameters */ - -#ifndef GL_SGIS_sharpen_texture -#define GL_SGIS_sharpen_texture 1 -#define GL_LINEAR_SHARPEN_SGIS 0x80AD -#define GL_LINEAR_SHARPEN_ALPHA_SGIS 0x80AE -#define GL_LINEAR_SHARPEN_COLOR_SGIS 0x80AF -#define GL_SHARPEN_TEXTURE_FUNC_POINTS_SGIS 0x80B0 -typedef void (APIENTRYP PFNGLSHARPENTEXFUNCSGISPROC) (GLenum target, GLsizei n, const GLfloat *points); -typedef void (APIENTRYP PFNGLGETSHARPENTEXFUNCSGISPROC) (GLenum target, GLfloat *points); -#ifdef GL_GLEXT_PROTOTYPES -GLAPI void APIENTRY glSharpenTexFuncSGIS (GLenum target, GLsizei n, const GLfloat *points); -GLAPI void APIENTRY glGetSharpenTexFuncSGIS (GLenum target, GLfloat *points); -#endif -#endif /* GL_SGIS_sharpen_texture */ - -#ifndef GL_SGIS_texture4D -#define GL_SGIS_texture4D 1 -#define GL_PACK_SKIP_VOLUMES_SGIS 0x8130 -#define GL_PACK_IMAGE_DEPTH_SGIS 0x8131 -#define GL_UNPACK_SKIP_VOLUMES_SGIS 0x8132 -#define GL_UNPACK_IMAGE_DEPTH_SGIS 0x8133 -#define GL_TEXTURE_4D_SGIS 0x8134 -#define GL_PROXY_TEXTURE_4D_SGIS 0x8135 -#define GL_TEXTURE_4DSIZE_SGIS 0x8136 -#define GL_TEXTURE_WRAP_Q_SGIS 0x8137 -#define GL_MAX_4D_TEXTURE_SIZE_SGIS 0x8138 -#define GL_TEXTURE_4D_BINDING_SGIS 0x814F -typedef void (APIENTRYP PFNGLTEXIMAGE4DSGISPROC) (GLenum target, GLint level, GLenum internalformat, GLsizei width, GLsizei height, GLsizei depth, GLsizei size4d, GLint border, GLenum format, GLenum type, const void *pixels); -typedef void (APIENTRYP PFNGLTEXSUBIMAGE4DSGISPROC) (GLenum target, GLint level, GLint xoffset, GLint yoffset, GLint zoffset, GLint woffset, GLsizei width, GLsizei height, GLsizei depth, GLsizei size4d, GLenum format, GLenum type, const void *pixels); -#ifdef GL_GLEXT_PROTOTYPES -GLAPI void APIENTRY glTexImage4DSGIS (GLenum target, GLint level, GLenum internalformat, GLsizei width, GLsizei height, GLsizei depth, GLsizei size4d, GLint border, GLenum format, GLenum type, const void *pixels); -GLAPI void APIENTRY glTexSubImage4DSGIS (GLenum target, GLint level, GLint xoffset, GLint yoffset, GLint zoffset, GLint woffset, GLsizei width, GLsizei height, GLsizei depth, GLsizei size4d, GLenum format, GLenum type, const void *pixels); -#endif -#endif /* GL_SGIS_texture4D */ - -#ifndef GL_SGIS_texture_border_clamp -#define GL_SGIS_texture_border_clamp 1 -#define GL_CLAMP_TO_BORDER_SGIS 0x812D -#endif /* GL_SGIS_texture_border_clamp */ - -#ifndef GL_SGIS_texture_color_mask -#define GL_SGIS_texture_color_mask 1 -#define GL_TEXTURE_COLOR_WRITEMASK_SGIS 0x81EF -typedef void (APIENTRYP PFNGLTEXTURECOLORMASKSGISPROC) (GLboolean red, GLboolean green, GLboolean blue, GLboolean alpha); -#ifdef GL_GLEXT_PROTOTYPES -GLAPI void APIENTRY glTextureColorMaskSGIS (GLboolean red, GLboolean green, GLboolean blue, GLboolean alpha); -#endif -#endif /* GL_SGIS_texture_color_mask */ - -#ifndef GL_SGIS_texture_edge_clamp -#define GL_SGIS_texture_edge_clamp 1 -#define GL_CLAMP_TO_EDGE_SGIS 0x812F -#endif /* GL_SGIS_texture_edge_clamp */ - -#ifndef GL_SGIS_texture_filter4 -#define GL_SGIS_texture_filter4 1 -#define GL_FILTER4_SGIS 0x8146 -#define GL_TEXTURE_FILTER4_SIZE_SGIS 0x8147 -typedef void (APIENTRYP PFNGLGETTEXFILTERFUNCSGISPROC) (GLenum target, GLenum filter, GLfloat *weights); -typedef void (APIENTRYP PFNGLTEXFILTERFUNCSGISPROC) (GLenum target, GLenum filter, GLsizei n, const GLfloat *weights); -#ifdef GL_GLEXT_PROTOTYPES -GLAPI void APIENTRY glGetTexFilterFuncSGIS (GLenum target, GLenum filter, GLfloat *weights); -GLAPI void APIENTRY glTexFilterFuncSGIS (GLenum target, GLenum filter, GLsizei n, const GLfloat *weights); -#endif -#endif /* GL_SGIS_texture_filter4 */ - -#ifndef GL_SGIS_texture_lod -#define GL_SGIS_texture_lod 1 -#define GL_TEXTURE_MIN_LOD_SGIS 0x813A -#define GL_TEXTURE_MAX_LOD_SGIS 0x813B -#define GL_TEXTURE_BASE_LEVEL_SGIS 0x813C -#define GL_TEXTURE_MAX_LEVEL_SGIS 0x813D -#endif /* GL_SGIS_texture_lod */ - -#ifndef GL_SGIS_texture_select -#define GL_SGIS_texture_select 1 -#define GL_DUAL_ALPHA4_SGIS 0x8110 -#define GL_DUAL_ALPHA8_SGIS 0x8111 -#define GL_DUAL_ALPHA12_SGIS 0x8112 -#define GL_DUAL_ALPHA16_SGIS 0x8113 -#define GL_DUAL_LUMINANCE4_SGIS 0x8114 -#define GL_DUAL_LUMINANCE8_SGIS 0x8115 -#define GL_DUAL_LUMINANCE12_SGIS 0x8116 -#define GL_DUAL_LUMINANCE16_SGIS 0x8117 -#define GL_DUAL_INTENSITY4_SGIS 0x8118 -#define GL_DUAL_INTENSITY8_SGIS 0x8119 -#define GL_DUAL_INTENSITY12_SGIS 0x811A -#define GL_DUAL_INTENSITY16_SGIS 0x811B -#define GL_DUAL_LUMINANCE_ALPHA4_SGIS 0x811C -#define GL_DUAL_LUMINANCE_ALPHA8_SGIS 0x811D -#define GL_QUAD_ALPHA4_SGIS 0x811E -#define GL_QUAD_ALPHA8_SGIS 0x811F -#define GL_QUAD_LUMINANCE4_SGIS 0x8120 -#define GL_QUAD_LUMINANCE8_SGIS 0x8121 -#define GL_QUAD_INTENSITY4_SGIS 0x8122 -#define GL_QUAD_INTENSITY8_SGIS 0x8123 -#define GL_DUAL_TEXTURE_SELECT_SGIS 0x8124 -#define GL_QUAD_TEXTURE_SELECT_SGIS 0x8125 -#endif /* GL_SGIS_texture_select */ - -#ifndef GL_SGIX_async -#define GL_SGIX_async 1 -#define GL_ASYNC_MARKER_SGIX 0x8329 -typedef void (APIENTRYP PFNGLASYNCMARKERSGIXPROC) (GLuint marker); -typedef GLint (APIENTRYP PFNGLFINISHASYNCSGIXPROC) (GLuint *markerp); -typedef GLint (APIENTRYP PFNGLPOLLASYNCSGIXPROC) (GLuint *markerp); -typedef GLuint (APIENTRYP PFNGLGENASYNCMARKERSSGIXPROC) (GLsizei range); -typedef void (APIENTRYP PFNGLDELETEASYNCMARKERSSGIXPROC) (GLuint marker, GLsizei range); -typedef GLboolean (APIENTRYP PFNGLISASYNCMARKERSGIXPROC) (GLuint marker); -#ifdef GL_GLEXT_PROTOTYPES -GLAPI void APIENTRY glAsyncMarkerSGIX (GLuint marker); -GLAPI GLint APIENTRY glFinishAsyncSGIX (GLuint *markerp); -GLAPI GLint APIENTRY glPollAsyncSGIX (GLuint *markerp); -GLAPI GLuint APIENTRY glGenAsyncMarkersSGIX (GLsizei range); -GLAPI void APIENTRY glDeleteAsyncMarkersSGIX (GLuint marker, GLsizei range); -GLAPI GLboolean APIENTRY glIsAsyncMarkerSGIX (GLuint marker); -#endif -#endif /* GL_SGIX_async */ - -#ifndef GL_SGIX_async_histogram -#define GL_SGIX_async_histogram 1 -#define GL_ASYNC_HISTOGRAM_SGIX 0x832C -#define GL_MAX_ASYNC_HISTOGRAM_SGIX 0x832D -#endif /* GL_SGIX_async_histogram */ - -#ifndef GL_SGIX_async_pixel -#define GL_SGIX_async_pixel 1 -#define GL_ASYNC_TEX_IMAGE_SGIX 0x835C -#define GL_ASYNC_DRAW_PIXELS_SGIX 0x835D -#define GL_ASYNC_READ_PIXELS_SGIX 0x835E -#define GL_MAX_ASYNC_TEX_IMAGE_SGIX 0x835F -#define GL_MAX_ASYNC_DRAW_PIXELS_SGIX 0x8360 -#define GL_MAX_ASYNC_READ_PIXELS_SGIX 0x8361 -#endif /* GL_SGIX_async_pixel */ - -#ifndef GL_SGIX_blend_alpha_minmax -#define GL_SGIX_blend_alpha_minmax 1 -#define GL_ALPHA_MIN_SGIX 0x8320 -#define GL_ALPHA_MAX_SGIX 0x8321 -#endif /* GL_SGIX_blend_alpha_minmax */ - -#ifndef GL_SGIX_calligraphic_fragment -#define GL_SGIX_calligraphic_fragment 1 -#define GL_CALLIGRAPHIC_FRAGMENT_SGIX 0x8183 -#endif /* GL_SGIX_calligraphic_fragment */ - -#ifndef GL_SGIX_clipmap -#define GL_SGIX_clipmap 1 -#define GL_LINEAR_CLIPMAP_LINEAR_SGIX 0x8170 -#define GL_TEXTURE_CLIPMAP_CENTER_SGIX 0x8171 -#define GL_TEXTURE_CLIPMAP_FRAME_SGIX 0x8172 -#define GL_TEXTURE_CLIPMAP_OFFSET_SGIX 0x8173 -#define GL_TEXTURE_CLIPMAP_VIRTUAL_DEPTH_SGIX 0x8174 -#define GL_TEXTURE_CLIPMAP_LOD_OFFSET_SGIX 0x8175 -#define GL_TEXTURE_CLIPMAP_DEPTH_SGIX 0x8176 -#define GL_MAX_CLIPMAP_DEPTH_SGIX 0x8177 -#define GL_MAX_CLIPMAP_VIRTUAL_DEPTH_SGIX 0x8178 -#define GL_NEAREST_CLIPMAP_NEAREST_SGIX 0x844D -#define GL_NEAREST_CLIPMAP_LINEAR_SGIX 0x844E -#define GL_LINEAR_CLIPMAP_NEAREST_SGIX 0x844F -#endif /* GL_SGIX_clipmap */ - -#ifndef GL_SGIX_convolution_accuracy -#define GL_SGIX_convolution_accuracy 1 -#define GL_CONVOLUTION_HINT_SGIX 0x8316 -#endif /* GL_SGIX_convolution_accuracy */ - -#ifndef GL_SGIX_depth_pass_instrument -#define GL_SGIX_depth_pass_instrument 1 -#endif /* GL_SGIX_depth_pass_instrument */ - -#ifndef GL_SGIX_depth_texture -#define GL_SGIX_depth_texture 1 -#define GL_DEPTH_COMPONENT16_SGIX 0x81A5 -#define GL_DEPTH_COMPONENT24_SGIX 0x81A6 -#define GL_DEPTH_COMPONENT32_SGIX 0x81A7 -#endif /* GL_SGIX_depth_texture */ - -#ifndef GL_SGIX_flush_raster -#define GL_SGIX_flush_raster 1 -typedef void (APIENTRYP PFNGLFLUSHRASTERSGIXPROC) (void); -#ifdef GL_GLEXT_PROTOTYPES -GLAPI void APIENTRY glFlushRasterSGIX (void); -#endif -#endif /* GL_SGIX_flush_raster */ - -#ifndef GL_SGIX_fog_offset -#define GL_SGIX_fog_offset 1 -#define GL_FOG_OFFSET_SGIX 0x8198 -#define GL_FOG_OFFSET_VALUE_SGIX 0x8199 -#endif /* GL_SGIX_fog_offset */ - -#ifndef GL_SGIX_fragment_lighting -#define GL_SGIX_fragment_lighting 1 -#define GL_FRAGMENT_LIGHTING_SGIX 0x8400 -#define GL_FRAGMENT_COLOR_MATERIAL_SGIX 0x8401 -#define GL_FRAGMENT_COLOR_MATERIAL_FACE_SGIX 0x8402 -#define GL_FRAGMENT_COLOR_MATERIAL_PARAMETER_SGIX 0x8403 -#define GL_MAX_FRAGMENT_LIGHTS_SGIX 0x8404 -#define GL_MAX_ACTIVE_LIGHTS_SGIX 0x8405 -#define GL_CURRENT_RASTER_NORMAL_SGIX 0x8406 -#define GL_LIGHT_ENV_MODE_SGIX 0x8407 -#define GL_FRAGMENT_LIGHT_MODEL_LOCAL_VIEWER_SGIX 0x8408 -#define GL_FRAGMENT_LIGHT_MODEL_TWO_SIDE_SGIX 0x8409 -#define GL_FRAGMENT_LIGHT_MODEL_AMBIENT_SGIX 0x840A -#define GL_FRAGMENT_LIGHT_MODEL_NORMAL_INTERPOLATION_SGIX 0x840B -#define GL_FRAGMENT_LIGHT0_SGIX 0x840C -#define GL_FRAGMENT_LIGHT1_SGIX 0x840D -#define GL_FRAGMENT_LIGHT2_SGIX 0x840E -#define GL_FRAGMENT_LIGHT3_SGIX 0x840F -#define GL_FRAGMENT_LIGHT4_SGIX 0x8410 -#define GL_FRAGMENT_LIGHT5_SGIX 0x8411 -#define GL_FRAGMENT_LIGHT6_SGIX 0x8412 -#define GL_FRAGMENT_LIGHT7_SGIX 0x8413 -typedef void (APIENTRYP PFNGLFRAGMENTCOLORMATERIALSGIXPROC) (GLenum face, GLenum mode); -typedef void (APIENTRYP PFNGLFRAGMENTLIGHTFSGIXPROC) (GLenum light, GLenum pname, GLfloat param); -typedef void (APIENTRYP PFNGLFRAGMENTLIGHTFVSGIXPROC) (GLenum light, GLenum pname, const GLfloat *params); -typedef void (APIENTRYP PFNGLFRAGMENTLIGHTISGIXPROC) (GLenum light, GLenum pname, GLint param); -typedef void (APIENTRYP PFNGLFRAGMENTLIGHTIVSGIXPROC) (GLenum light, GLenum pname, const GLint *params); -typedef void (APIENTRYP PFNGLFRAGMENTLIGHTMODELFSGIXPROC) (GLenum pname, GLfloat param); -typedef void (APIENTRYP PFNGLFRAGMENTLIGHTMODELFVSGIXPROC) (GLenum pname, const GLfloat *params); -typedef void (APIENTRYP PFNGLFRAGMENTLIGHTMODELISGIXPROC) (GLenum pname, GLint param); -typedef void (APIENTRYP PFNGLFRAGMENTLIGHTMODELIVSGIXPROC) (GLenum pname, const GLint *params); -typedef void (APIENTRYP PFNGLFRAGMENTMATERIALFSGIXPROC) (GLenum face, GLenum pname, GLfloat param); -typedef void (APIENTRYP PFNGLFRAGMENTMATERIALFVSGIXPROC) (GLenum face, GLenum pname, const GLfloat *params); -typedef void (APIENTRYP PFNGLFRAGMENTMATERIALISGIXPROC) (GLenum face, GLenum pname, GLint param); -typedef void (APIENTRYP PFNGLFRAGMENTMATERIALIVSGIXPROC) (GLenum face, GLenum pname, const GLint *params); -typedef void (APIENTRYP PFNGLGETFRAGMENTLIGHTFVSGIXPROC) (GLenum light, GLenum pname, GLfloat *params); -typedef void (APIENTRYP PFNGLGETFRAGMENTLIGHTIVSGIXPROC) (GLenum light, GLenum pname, GLint *params); -typedef void (APIENTRYP PFNGLGETFRAGMENTMATERIALFVSGIXPROC) (GLenum face, GLenum pname, GLfloat *params); -typedef void (APIENTRYP PFNGLGETFRAGMENTMATERIALIVSGIXPROC) (GLenum face, GLenum pname, GLint *params); -typedef void (APIENTRYP PFNGLLIGHTENVISGIXPROC) (GLenum pname, GLint param); -#ifdef GL_GLEXT_PROTOTYPES -GLAPI void APIENTRY glFragmentColorMaterialSGIX (GLenum face, GLenum mode); -GLAPI void APIENTRY glFragmentLightfSGIX (GLenum light, GLenum pname, GLfloat param); -GLAPI void APIENTRY glFragmentLightfvSGIX (GLenum light, GLenum pname, const GLfloat *params); -GLAPI void APIENTRY glFragmentLightiSGIX (GLenum light, GLenum pname, GLint param); -GLAPI void APIENTRY glFragmentLightivSGIX (GLenum light, GLenum pname, const GLint *params); -GLAPI void APIENTRY glFragmentLightModelfSGIX (GLenum pname, GLfloat param); -GLAPI void APIENTRY glFragmentLightModelfvSGIX (GLenum pname, const GLfloat *params); -GLAPI void APIENTRY glFragmentLightModeliSGIX (GLenum pname, GLint param); -GLAPI void APIENTRY glFragmentLightModelivSGIX (GLenum pname, const GLint *params); -GLAPI void APIENTRY glFragmentMaterialfSGIX (GLenum face, GLenum pname, GLfloat param); -GLAPI void APIENTRY glFragmentMaterialfvSGIX (GLenum face, GLenum pname, const GLfloat *params); -GLAPI void APIENTRY glFragmentMaterialiSGIX (GLenum face, GLenum pname, GLint param); -GLAPI void APIENTRY glFragmentMaterialivSGIX (GLenum face, GLenum pname, const GLint *params); -GLAPI void APIENTRY glGetFragmentLightfvSGIX (GLenum light, GLenum pname, GLfloat *params); -GLAPI void APIENTRY glGetFragmentLightivSGIX (GLenum light, GLenum pname, GLint *params); -GLAPI void APIENTRY glGetFragmentMaterialfvSGIX (GLenum face, GLenum pname, GLfloat *params); -GLAPI void APIENTRY glGetFragmentMaterialivSGIX (GLenum face, GLenum pname, GLint *params); -GLAPI void APIENTRY glLightEnviSGIX (GLenum pname, GLint param); -#endif -#endif /* GL_SGIX_fragment_lighting */ - -#ifndef GL_SGIX_framezoom -#define GL_SGIX_framezoom 1 -#define GL_FRAMEZOOM_SGIX 0x818B -#define GL_FRAMEZOOM_FACTOR_SGIX 0x818C -#define GL_MAX_FRAMEZOOM_FACTOR_SGIX 0x818D -typedef void (APIENTRYP PFNGLFRAMEZOOMSGIXPROC) (GLint factor); -#ifdef GL_GLEXT_PROTOTYPES -GLAPI void APIENTRY glFrameZoomSGIX (GLint factor); -#endif -#endif /* GL_SGIX_framezoom */ - -#ifndef GL_SGIX_igloo_interface -#define GL_SGIX_igloo_interface 1 -typedef void (APIENTRYP PFNGLIGLOOINTERFACESGIXPROC) (GLenum pname, const void *params); -#ifdef GL_GLEXT_PROTOTYPES -GLAPI void APIENTRY glIglooInterfaceSGIX (GLenum pname, const void *params); -#endif -#endif /* GL_SGIX_igloo_interface */ - -#ifndef GL_SGIX_instruments -#define GL_SGIX_instruments 1 -#define GL_INSTRUMENT_BUFFER_POINTER_SGIX 0x8180 -#define GL_INSTRUMENT_MEASUREMENTS_SGIX 0x8181 -typedef GLint (APIENTRYP PFNGLGETINSTRUMENTSSGIXPROC) (void); -typedef void (APIENTRYP PFNGLINSTRUMENTSBUFFERSGIXPROC) (GLsizei size, GLint *buffer); -typedef GLint (APIENTRYP PFNGLPOLLINSTRUMENTSSGIXPROC) (GLint *marker_p); -typedef void (APIENTRYP PFNGLREADINSTRUMENTSSGIXPROC) (GLint marker); -typedef void (APIENTRYP PFNGLSTARTINSTRUMENTSSGIXPROC) (void); -typedef void (APIENTRYP PFNGLSTOPINSTRUMENTSSGIXPROC) (GLint marker); -#ifdef GL_GLEXT_PROTOTYPES -GLAPI GLint APIENTRY glGetInstrumentsSGIX (void); -GLAPI void APIENTRY glInstrumentsBufferSGIX (GLsizei size, GLint *buffer); -GLAPI GLint APIENTRY glPollInstrumentsSGIX (GLint *marker_p); -GLAPI void APIENTRY glReadInstrumentsSGIX (GLint marker); -GLAPI void APIENTRY glStartInstrumentsSGIX (void); -GLAPI void APIENTRY glStopInstrumentsSGIX (GLint marker); -#endif -#endif /* GL_SGIX_instruments */ - -#ifndef GL_SGIX_interlace -#define GL_SGIX_interlace 1 -#define GL_INTERLACE_SGIX 0x8094 -#endif /* GL_SGIX_interlace */ - -#ifndef GL_SGIX_ir_instrument1 -#define GL_SGIX_ir_instrument1 1 -#define GL_IR_INSTRUMENT1_SGIX 0x817F -#endif /* GL_SGIX_ir_instrument1 */ - -#ifndef GL_SGIX_list_priority -#define GL_SGIX_list_priority 1 -#define GL_LIST_PRIORITY_SGIX 0x8182 -typedef void (APIENTRYP PFNGLGETLISTPARAMETERFVSGIXPROC) (GLuint list, GLenum pname, GLfloat *params); -typedef void (APIENTRYP PFNGLGETLISTPARAMETERIVSGIXPROC) (GLuint list, GLenum pname, GLint *params); -typedef void (APIENTRYP PFNGLLISTPARAMETERFSGIXPROC) (GLuint list, GLenum pname, GLfloat param); -typedef void (APIENTRYP PFNGLLISTPARAMETERFVSGIXPROC) (GLuint list, GLenum pname, const GLfloat *params); -typedef void (APIENTRYP PFNGLLISTPARAMETERISGIXPROC) (GLuint list, GLenum pname, GLint param); -typedef void (APIENTRYP PFNGLLISTPARAMETERIVSGIXPROC) (GLuint list, GLenum pname, const GLint *params); -#ifdef GL_GLEXT_PROTOTYPES -GLAPI void APIENTRY glGetListParameterfvSGIX (GLuint list, GLenum pname, GLfloat *params); -GLAPI void APIENTRY glGetListParameterivSGIX (GLuint list, GLenum pname, GLint *params); -GLAPI void APIENTRY glListParameterfSGIX (GLuint list, GLenum pname, GLfloat param); -GLAPI void APIENTRY glListParameterfvSGIX (GLuint list, GLenum pname, const GLfloat *params); -GLAPI void APIENTRY glListParameteriSGIX (GLuint list, GLenum pname, GLint param); -GLAPI void APIENTRY glListParameterivSGIX (GLuint list, GLenum pname, const GLint *params); -#endif -#endif /* GL_SGIX_list_priority */ - -#ifndef GL_SGIX_pixel_texture -#define GL_SGIX_pixel_texture 1 -#define GL_PIXEL_TEX_GEN_SGIX 0x8139 -#define GL_PIXEL_TEX_GEN_MODE_SGIX 0x832B -typedef void (APIENTRYP PFNGLPIXELTEXGENSGIXPROC) (GLenum mode); -#ifdef GL_GLEXT_PROTOTYPES -GLAPI void APIENTRY glPixelTexGenSGIX (GLenum mode); -#endif -#endif /* GL_SGIX_pixel_texture */ - -#ifndef GL_SGIX_pixel_tiles -#define GL_SGIX_pixel_tiles 1 -#define GL_PIXEL_TILE_BEST_ALIGNMENT_SGIX 0x813E -#define GL_PIXEL_TILE_CACHE_INCREMENT_SGIX 0x813F -#define GL_PIXEL_TILE_WIDTH_SGIX 0x8140 -#define GL_PIXEL_TILE_HEIGHT_SGIX 0x8141 -#define GL_PIXEL_TILE_GRID_WIDTH_SGIX 0x8142 -#define GL_PIXEL_TILE_GRID_HEIGHT_SGIX 0x8143 -#define GL_PIXEL_TILE_GRID_DEPTH_SGIX 0x8144 -#define GL_PIXEL_TILE_CACHE_SIZE_SGIX 0x8145 -#endif /* GL_SGIX_pixel_tiles */ - -#ifndef GL_SGIX_polynomial_ffd -#define GL_SGIX_polynomial_ffd 1 -#define GL_TEXTURE_DEFORMATION_BIT_SGIX 0x00000001 -#define GL_GEOMETRY_DEFORMATION_BIT_SGIX 0x00000002 -#define GL_GEOMETRY_DEFORMATION_SGIX 0x8194 -#define GL_TEXTURE_DEFORMATION_SGIX 0x8195 -#define GL_DEFORMATIONS_MASK_SGIX 0x8196 -#define GL_MAX_DEFORMATION_ORDER_SGIX 0x8197 -typedef void (APIENTRYP PFNGLDEFORMATIONMAP3DSGIXPROC) (GLenum target, GLdouble u1, GLdouble u2, GLint ustride, GLint uorder, GLdouble v1, GLdouble v2, GLint vstride, GLint vorder, GLdouble w1, GLdouble w2, GLint wstride, GLint worder, const GLdouble *points); -typedef void (APIENTRYP PFNGLDEFORMATIONMAP3FSGIXPROC) (GLenum target, GLfloat u1, GLfloat u2, GLint ustride, GLint uorder, GLfloat v1, GLfloat v2, GLint vstride, GLint vorder, GLfloat w1, GLfloat w2, GLint wstride, GLint worder, const GLfloat *points); -typedef void (APIENTRYP PFNGLDEFORMSGIXPROC) (GLbitfield mask); -typedef void (APIENTRYP PFNGLLOADIDENTITYDEFORMATIONMAPSGIXPROC) (GLbitfield mask); -#ifdef GL_GLEXT_PROTOTYPES -GLAPI void APIENTRY glDeformationMap3dSGIX (GLenum target, GLdouble u1, GLdouble u2, GLint ustride, GLint uorder, GLdouble v1, GLdouble v2, GLint vstride, GLint vorder, GLdouble w1, GLdouble w2, GLint wstride, GLint worder, const GLdouble *points); -GLAPI void APIENTRY glDeformationMap3fSGIX (GLenum target, GLfloat u1, GLfloat u2, GLint ustride, GLint uorder, GLfloat v1, GLfloat v2, GLint vstride, GLint vorder, GLfloat w1, GLfloat w2, GLint wstride, GLint worder, const GLfloat *points); -GLAPI void APIENTRY glDeformSGIX (GLbitfield mask); -GLAPI void APIENTRY glLoadIdentityDeformationMapSGIX (GLbitfield mask); -#endif -#endif /* GL_SGIX_polynomial_ffd */ - -#ifndef GL_SGIX_reference_plane -#define GL_SGIX_reference_plane 1 -#define GL_REFERENCE_PLANE_SGIX 0x817D -#define GL_REFERENCE_PLANE_EQUATION_SGIX 0x817E -typedef void (APIENTRYP PFNGLREFERENCEPLANESGIXPROC) (const GLdouble *equation); -#ifdef GL_GLEXT_PROTOTYPES -GLAPI void APIENTRY glReferencePlaneSGIX (const GLdouble *equation); -#endif -#endif /* GL_SGIX_reference_plane */ - -#ifndef GL_SGIX_resample -#define GL_SGIX_resample 1 -#define GL_PACK_RESAMPLE_SGIX 0x842C -#define GL_UNPACK_RESAMPLE_SGIX 0x842D -#define GL_RESAMPLE_REPLICATE_SGIX 0x842E -#define GL_RESAMPLE_ZERO_FILL_SGIX 0x842F -#define GL_RESAMPLE_DECIMATE_SGIX 0x8430 -#endif /* GL_SGIX_resample */ - -#ifndef GL_SGIX_scalebias_hint -#define GL_SGIX_scalebias_hint 1 -#define GL_SCALEBIAS_HINT_SGIX 0x8322 -#endif /* GL_SGIX_scalebias_hint */ - -#ifndef GL_SGIX_shadow -#define GL_SGIX_shadow 1 -#define GL_TEXTURE_COMPARE_SGIX 0x819A -#define GL_TEXTURE_COMPARE_OPERATOR_SGIX 0x819B -#define GL_TEXTURE_LEQUAL_R_SGIX 0x819C -#define GL_TEXTURE_GEQUAL_R_SGIX 0x819D -#endif /* GL_SGIX_shadow */ - -#ifndef GL_SGIX_shadow_ambient -#define GL_SGIX_shadow_ambient 1 -#define GL_SHADOW_AMBIENT_SGIX 0x80BF -#endif /* GL_SGIX_shadow_ambient */ - -#ifndef GL_SGIX_sprite -#define GL_SGIX_sprite 1 -#define GL_SPRITE_SGIX 0x8148 -#define GL_SPRITE_MODE_SGIX 0x8149 -#define GL_SPRITE_AXIS_SGIX 0x814A -#define GL_SPRITE_TRANSLATION_SGIX 0x814B -#define GL_SPRITE_AXIAL_SGIX 0x814C -#define GL_SPRITE_OBJECT_ALIGNED_SGIX 0x814D -#define GL_SPRITE_EYE_ALIGNED_SGIX 0x814E -typedef void (APIENTRYP PFNGLSPRITEPARAMETERFSGIXPROC) (GLenum pname, GLfloat param); -typedef void (APIENTRYP PFNGLSPRITEPARAMETERFVSGIXPROC) (GLenum pname, const GLfloat *params); -typedef void (APIENTRYP PFNGLSPRITEPARAMETERISGIXPROC) (GLenum pname, GLint param); -typedef void (APIENTRYP PFNGLSPRITEPARAMETERIVSGIXPROC) (GLenum pname, const GLint *params); -#ifdef GL_GLEXT_PROTOTYPES -GLAPI void APIENTRY glSpriteParameterfSGIX (GLenum pname, GLfloat param); -GLAPI void APIENTRY glSpriteParameterfvSGIX (GLenum pname, const GLfloat *params); -GLAPI void APIENTRY glSpriteParameteriSGIX (GLenum pname, GLint param); -GLAPI void APIENTRY glSpriteParameterivSGIX (GLenum pname, const GLint *params); -#endif -#endif /* GL_SGIX_sprite */ - -#ifndef GL_SGIX_subsample -#define GL_SGIX_subsample 1 -#define GL_PACK_SUBSAMPLE_RATE_SGIX 0x85A0 -#define GL_UNPACK_SUBSAMPLE_RATE_SGIX 0x85A1 -#define GL_PIXEL_SUBSAMPLE_4444_SGIX 0x85A2 -#define GL_PIXEL_SUBSAMPLE_2424_SGIX 0x85A3 -#define GL_PIXEL_SUBSAMPLE_4242_SGIX 0x85A4 -#endif /* GL_SGIX_subsample */ - -#ifndef GL_SGIX_tag_sample_buffer -#define GL_SGIX_tag_sample_buffer 1 -typedef void (APIENTRYP PFNGLTAGSAMPLEBUFFERSGIXPROC) (void); -#ifdef GL_GLEXT_PROTOTYPES -GLAPI void APIENTRY glTagSampleBufferSGIX (void); -#endif -#endif /* GL_SGIX_tag_sample_buffer */ - -#ifndef GL_SGIX_texture_add_env -#define GL_SGIX_texture_add_env 1 -#define GL_TEXTURE_ENV_BIAS_SGIX 0x80BE -#endif /* GL_SGIX_texture_add_env */ - -#ifndef GL_SGIX_texture_coordinate_clamp -#define GL_SGIX_texture_coordinate_clamp 1 -#define GL_TEXTURE_MAX_CLAMP_S_SGIX 0x8369 -#define GL_TEXTURE_MAX_CLAMP_T_SGIX 0x836A -#define GL_TEXTURE_MAX_CLAMP_R_SGIX 0x836B -#endif /* GL_SGIX_texture_coordinate_clamp */ - -#ifndef GL_SGIX_texture_lod_bias -#define GL_SGIX_texture_lod_bias 1 -#define GL_TEXTURE_LOD_BIAS_S_SGIX 0x818E -#define GL_TEXTURE_LOD_BIAS_T_SGIX 0x818F -#define GL_TEXTURE_LOD_BIAS_R_SGIX 0x8190 -#endif /* GL_SGIX_texture_lod_bias */ - -#ifndef GL_SGIX_texture_multi_buffer -#define GL_SGIX_texture_multi_buffer 1 -#define GL_TEXTURE_MULTI_BUFFER_HINT_SGIX 0x812E -#endif /* GL_SGIX_texture_multi_buffer */ - -#ifndef GL_SGIX_texture_scale_bias -#define GL_SGIX_texture_scale_bias 1 -#define GL_POST_TEXTURE_FILTER_BIAS_SGIX 0x8179 -#define GL_POST_TEXTURE_FILTER_SCALE_SGIX 0x817A -#define GL_POST_TEXTURE_FILTER_BIAS_RANGE_SGIX 0x817B -#define GL_POST_TEXTURE_FILTER_SCALE_RANGE_SGIX 0x817C -#endif /* GL_SGIX_texture_scale_bias */ - -#ifndef GL_SGIX_vertex_preclip -#define GL_SGIX_vertex_preclip 1 -#define GL_VERTEX_PRECLIP_SGIX 0x83EE -#define GL_VERTEX_PRECLIP_HINT_SGIX 0x83EF -#endif /* GL_SGIX_vertex_preclip */ - -#ifndef GL_SGIX_ycrcb -#define GL_SGIX_ycrcb 1 -#define GL_YCRCB_422_SGIX 0x81BB -#define GL_YCRCB_444_SGIX 0x81BC -#endif /* GL_SGIX_ycrcb */ - -#ifndef GL_SGIX_ycrcb_subsample -#define GL_SGIX_ycrcb_subsample 1 -#endif /* GL_SGIX_ycrcb_subsample */ - -#ifndef GL_SGIX_ycrcba -#define GL_SGIX_ycrcba 1 -#define GL_YCRCB_SGIX 0x8318 -#define GL_YCRCBA_SGIX 0x8319 -#endif /* GL_SGIX_ycrcba */ - -#ifndef GL_SGI_color_matrix -#define GL_SGI_color_matrix 1 -#define GL_COLOR_MATRIX_SGI 0x80B1 -#define GL_COLOR_MATRIX_STACK_DEPTH_SGI 0x80B2 -#define GL_MAX_COLOR_MATRIX_STACK_DEPTH_SGI 0x80B3 -#define GL_POST_COLOR_MATRIX_RED_SCALE_SGI 0x80B4 -#define GL_POST_COLOR_MATRIX_GREEN_SCALE_SGI 0x80B5 -#define GL_POST_COLOR_MATRIX_BLUE_SCALE_SGI 0x80B6 -#define GL_POST_COLOR_MATRIX_ALPHA_SCALE_SGI 0x80B7 -#define GL_POST_COLOR_MATRIX_RED_BIAS_SGI 0x80B8 -#define GL_POST_COLOR_MATRIX_GREEN_BIAS_SGI 0x80B9 -#define GL_POST_COLOR_MATRIX_BLUE_BIAS_SGI 0x80BA -#define GL_POST_COLOR_MATRIX_ALPHA_BIAS_SGI 0x80BB -#endif /* GL_SGI_color_matrix */ - -#ifndef GL_SGI_color_table -#define GL_SGI_color_table 1 -#define GL_COLOR_TABLE_SGI 0x80D0 -#define GL_POST_CONVOLUTION_COLOR_TABLE_SGI 0x80D1 -#define GL_POST_COLOR_MATRIX_COLOR_TABLE_SGI 0x80D2 -#define GL_PROXY_COLOR_TABLE_SGI 0x80D3 -#define GL_PROXY_POST_CONVOLUTION_COLOR_TABLE_SGI 0x80D4 -#define GL_PROXY_POST_COLOR_MATRIX_COLOR_TABLE_SGI 0x80D5 -#define GL_COLOR_TABLE_SCALE_SGI 0x80D6 -#define GL_COLOR_TABLE_BIAS_SGI 0x80D7 -#define GL_COLOR_TABLE_FORMAT_SGI 0x80D8 -#define GL_COLOR_TABLE_WIDTH_SGI 0x80D9 -#define GL_COLOR_TABLE_RED_SIZE_SGI 0x80DA -#define GL_COLOR_TABLE_GREEN_SIZE_SGI 0x80DB -#define GL_COLOR_TABLE_BLUE_SIZE_SGI 0x80DC -#define GL_COLOR_TABLE_ALPHA_SIZE_SGI 0x80DD -#define GL_COLOR_TABLE_LUMINANCE_SIZE_SGI 0x80DE -#define GL_COLOR_TABLE_INTENSITY_SIZE_SGI 0x80DF -typedef void (APIENTRYP PFNGLCOLORTABLESGIPROC) (GLenum target, GLenum internalformat, GLsizei width, GLenum format, GLenum type, const void *table); -typedef void (APIENTRYP PFNGLCOLORTABLEPARAMETERFVSGIPROC) (GLenum target, GLenum pname, const GLfloat *params); -typedef void (APIENTRYP PFNGLCOLORTABLEPARAMETERIVSGIPROC) (GLenum target, GLenum pname, const GLint *params); -typedef void (APIENTRYP PFNGLCOPYCOLORTABLESGIPROC) (GLenum target, GLenum internalformat, GLint x, GLint y, GLsizei width); -typedef void (APIENTRYP PFNGLGETCOLORTABLESGIPROC) (GLenum target, GLenum format, GLenum type, void *table); -typedef void (APIENTRYP PFNGLGETCOLORTABLEPARAMETERFVSGIPROC) (GLenum target, GLenum pname, GLfloat *params); -typedef void (APIENTRYP PFNGLGETCOLORTABLEPARAMETERIVSGIPROC) (GLenum target, GLenum pname, GLint *params); -#ifdef GL_GLEXT_PROTOTYPES -GLAPI void APIENTRY glColorTableSGI (GLenum target, GLenum internalformat, GLsizei width, GLenum format, GLenum type, const void *table); -GLAPI void APIENTRY glColorTableParameterfvSGI (GLenum target, GLenum pname, const GLfloat *params); -GLAPI void APIENTRY glColorTableParameterivSGI (GLenum target, GLenum pname, const GLint *params); -GLAPI void APIENTRY glCopyColorTableSGI (GLenum target, GLenum internalformat, GLint x, GLint y, GLsizei width); -GLAPI void APIENTRY glGetColorTableSGI (GLenum target, GLenum format, GLenum type, void *table); -GLAPI void APIENTRY glGetColorTableParameterfvSGI (GLenum target, GLenum pname, GLfloat *params); -GLAPI void APIENTRY glGetColorTableParameterivSGI (GLenum target, GLenum pname, GLint *params); -#endif -#endif /* GL_SGI_color_table */ - -#ifndef GL_SGI_texture_color_table -#define GL_SGI_texture_color_table 1 -#define GL_TEXTURE_COLOR_TABLE_SGI 0x80BC -#define GL_PROXY_TEXTURE_COLOR_TABLE_SGI 0x80BD -#endif /* GL_SGI_texture_color_table */ - -#ifndef GL_SUNX_constant_data -#define GL_SUNX_constant_data 1 -#define GL_UNPACK_CONSTANT_DATA_SUNX 0x81D5 -#define GL_TEXTURE_CONSTANT_DATA_SUNX 0x81D6 -typedef void (APIENTRYP PFNGLFINISHTEXTURESUNXPROC) (void); -#ifdef GL_GLEXT_PROTOTYPES -GLAPI void APIENTRY glFinishTextureSUNX (void); -#endif -#endif /* GL_SUNX_constant_data */ - -#ifndef GL_SUN_convolution_border_modes -#define GL_SUN_convolution_border_modes 1 -#define GL_WRAP_BORDER_SUN 0x81D4 -#endif /* GL_SUN_convolution_border_modes */ - -#ifndef GL_SUN_global_alpha -#define GL_SUN_global_alpha 1 -#define GL_GLOBAL_ALPHA_SUN 0x81D9 -#define GL_GLOBAL_ALPHA_FACTOR_SUN 0x81DA -typedef void (APIENTRYP PFNGLGLOBALALPHAFACTORBSUNPROC) (GLbyte factor); -typedef void (APIENTRYP PFNGLGLOBALALPHAFACTORSSUNPROC) (GLshort factor); -typedef void (APIENTRYP PFNGLGLOBALALPHAFACTORISUNPROC) (GLint factor); -typedef void (APIENTRYP PFNGLGLOBALALPHAFACTORFSUNPROC) (GLfloat factor); -typedef void (APIENTRYP PFNGLGLOBALALPHAFACTORDSUNPROC) (GLdouble factor); -typedef void (APIENTRYP PFNGLGLOBALALPHAFACTORUBSUNPROC) (GLubyte factor); -typedef void (APIENTRYP PFNGLGLOBALALPHAFACTORUSSUNPROC) (GLushort factor); -typedef void (APIENTRYP PFNGLGLOBALALPHAFACTORUISUNPROC) (GLuint factor); -#ifdef GL_GLEXT_PROTOTYPES -GLAPI void APIENTRY glGlobalAlphaFactorbSUN (GLbyte factor); -GLAPI void APIENTRY glGlobalAlphaFactorsSUN (GLshort factor); -GLAPI void APIENTRY glGlobalAlphaFactoriSUN (GLint factor); -GLAPI void APIENTRY glGlobalAlphaFactorfSUN (GLfloat factor); -GLAPI void APIENTRY glGlobalAlphaFactordSUN (GLdouble factor); -GLAPI void APIENTRY glGlobalAlphaFactorubSUN (GLubyte factor); -GLAPI void APIENTRY glGlobalAlphaFactorusSUN (GLushort factor); -GLAPI void APIENTRY glGlobalAlphaFactoruiSUN (GLuint factor); -#endif -#endif /* GL_SUN_global_alpha */ - -#ifndef GL_SUN_mesh_array -#define GL_SUN_mesh_array 1 -#define GL_QUAD_MESH_SUN 0x8614 -#define GL_TRIANGLE_MESH_SUN 0x8615 -typedef void (APIENTRYP PFNGLDRAWMESHARRAYSSUNPROC) (GLenum mode, GLint first, GLsizei count, GLsizei width); -#ifdef GL_GLEXT_PROTOTYPES -GLAPI void APIENTRY glDrawMeshArraysSUN (GLenum mode, GLint first, GLsizei count, GLsizei width); -#endif -#endif /* GL_SUN_mesh_array */ - -#ifndef GL_SUN_slice_accum -#define GL_SUN_slice_accum 1 -#define GL_SLICE_ACCUM_SUN 0x85CC -#endif /* GL_SUN_slice_accum */ - -#ifndef GL_SUN_triangle_list -#define GL_SUN_triangle_list 1 -#define GL_RESTART_SUN 0x0001 -#define GL_REPLACE_MIDDLE_SUN 0x0002 -#define GL_REPLACE_OLDEST_SUN 0x0003 -#define GL_TRIANGLE_LIST_SUN 0x81D7 -#define GL_REPLACEMENT_CODE_SUN 0x81D8 -#define GL_REPLACEMENT_CODE_ARRAY_SUN 0x85C0 -#define GL_REPLACEMENT_CODE_ARRAY_TYPE_SUN 0x85C1 -#define GL_REPLACEMENT_CODE_ARRAY_STRIDE_SUN 0x85C2 -#define GL_REPLACEMENT_CODE_ARRAY_POINTER_SUN 0x85C3 -#define GL_R1UI_V3F_SUN 0x85C4 -#define GL_R1UI_C4UB_V3F_SUN 0x85C5 -#define GL_R1UI_C3F_V3F_SUN 0x85C6 -#define GL_R1UI_N3F_V3F_SUN 0x85C7 -#define GL_R1UI_C4F_N3F_V3F_SUN 0x85C8 -#define GL_R1UI_T2F_V3F_SUN 0x85C9 -#define GL_R1UI_T2F_N3F_V3F_SUN 0x85CA -#define GL_R1UI_T2F_C4F_N3F_V3F_SUN 0x85CB -typedef void (APIENTRYP PFNGLREPLACEMENTCODEUISUNPROC) (GLuint code); -typedef void (APIENTRYP PFNGLREPLACEMENTCODEUSSUNPROC) (GLushort code); -typedef void (APIENTRYP PFNGLREPLACEMENTCODEUBSUNPROC) (GLubyte code); -typedef void (APIENTRYP PFNGLREPLACEMENTCODEUIVSUNPROC) (const GLuint *code); -typedef void (APIENTRYP PFNGLREPLACEMENTCODEUSVSUNPROC) (const GLushort *code); -typedef void (APIENTRYP PFNGLREPLACEMENTCODEUBVSUNPROC) (const GLubyte *code); -typedef void (APIENTRYP PFNGLREPLACEMENTCODEPOINTERSUNPROC) (GLenum type, GLsizei stride, const void **pointer); -#ifdef GL_GLEXT_PROTOTYPES -GLAPI void APIENTRY glReplacementCodeuiSUN (GLuint code); -GLAPI void APIENTRY glReplacementCodeusSUN (GLushort code); -GLAPI void APIENTRY glReplacementCodeubSUN (GLubyte code); -GLAPI void APIENTRY glReplacementCodeuivSUN (const GLuint *code); -GLAPI void APIENTRY glReplacementCodeusvSUN (const GLushort *code); -GLAPI void APIENTRY glReplacementCodeubvSUN (const GLubyte *code); -GLAPI void APIENTRY glReplacementCodePointerSUN (GLenum type, GLsizei stride, const void **pointer); -#endif -#endif /* GL_SUN_triangle_list */ - -#ifndef GL_SUN_vertex -#define GL_SUN_vertex 1 -typedef void (APIENTRYP PFNGLCOLOR4UBVERTEX2FSUNPROC) (GLubyte r, GLubyte g, GLubyte b, GLubyte a, GLfloat x, GLfloat y); -typedef void (APIENTRYP PFNGLCOLOR4UBVERTEX2FVSUNPROC) (const GLubyte *c, const GLfloat *v); -typedef void (APIENTRYP PFNGLCOLOR4UBVERTEX3FSUNPROC) (GLubyte r, GLubyte g, GLubyte b, GLubyte a, GLfloat x, GLfloat y, GLfloat z); -typedef void (APIENTRYP PFNGLCOLOR4UBVERTEX3FVSUNPROC) (const GLubyte *c, const GLfloat *v); -typedef void (APIENTRYP PFNGLCOLOR3FVERTEX3FSUNPROC) (GLfloat r, GLfloat g, GLfloat b, GLfloat x, GLfloat y, GLfloat z); -typedef void (APIENTRYP PFNGLCOLOR3FVERTEX3FVSUNPROC) (const GLfloat *c, const GLfloat *v); -typedef void (APIENTRYP PFNGLNORMAL3FVERTEX3FSUNPROC) (GLfloat nx, GLfloat ny, GLfloat nz, GLfloat x, GLfloat y, GLfloat z); -typedef void (APIENTRYP PFNGLNORMAL3FVERTEX3FVSUNPROC) (const GLfloat *n, const GLfloat *v); -typedef void (APIENTRYP PFNGLCOLOR4FNORMAL3FVERTEX3FSUNPROC) (GLfloat r, GLfloat g, GLfloat b, GLfloat a, GLfloat nx, GLfloat ny, GLfloat nz, GLfloat x, GLfloat y, GLfloat z); -typedef void (APIENTRYP PFNGLCOLOR4FNORMAL3FVERTEX3FVSUNPROC) (const GLfloat *c, const GLfloat *n, const GLfloat *v); -typedef void (APIENTRYP PFNGLTEXCOORD2FVERTEX3FSUNPROC) (GLfloat s, GLfloat t, GLfloat x, GLfloat y, GLfloat z); -typedef void (APIENTRYP PFNGLTEXCOORD2FVERTEX3FVSUNPROC) (const GLfloat *tc, const GLfloat *v); -typedef void (APIENTRYP PFNGLTEXCOORD4FVERTEX4FSUNPROC) (GLfloat s, GLfloat t, GLfloat p, GLfloat q, GLfloat x, GLfloat y, GLfloat z, GLfloat w); -typedef void (APIENTRYP PFNGLTEXCOORD4FVERTEX4FVSUNPROC) (const GLfloat *tc, const GLfloat *v); -typedef void (APIENTRYP PFNGLTEXCOORD2FCOLOR4UBVERTEX3FSUNPROC) (GLfloat s, GLfloat t, GLubyte r, GLubyte g, GLubyte b, GLubyte a, GLfloat x, GLfloat y, GLfloat z); -typedef void (APIENTRYP PFNGLTEXCOORD2FCOLOR4UBVERTEX3FVSUNPROC) (const GLfloat *tc, const GLubyte *c, const GLfloat *v); -typedef void (APIENTRYP PFNGLTEXCOORD2FCOLOR3FVERTEX3FSUNPROC) (GLfloat s, GLfloat t, GLfloat r, GLfloat g, GLfloat b, GLfloat x, GLfloat y, GLfloat z); -typedef void (APIENTRYP PFNGLTEXCOORD2FCOLOR3FVERTEX3FVSUNPROC) (const GLfloat *tc, const GLfloat *c, const GLfloat *v); -typedef void (APIENTRYP PFNGLTEXCOORD2FNORMAL3FVERTEX3FSUNPROC) (GLfloat s, GLfloat t, GLfloat nx, GLfloat ny, GLfloat nz, GLfloat x, GLfloat y, GLfloat z); -typedef void (APIENTRYP PFNGLTEXCOORD2FNORMAL3FVERTEX3FVSUNPROC) (const GLfloat *tc, const GLfloat *n, const GLfloat *v); -typedef void (APIENTRYP PFNGLTEXCOORD2FCOLOR4FNORMAL3FVERTEX3FSUNPROC) (GLfloat s, GLfloat t, GLfloat r, GLfloat g, GLfloat b, GLfloat a, GLfloat nx, GLfloat ny, GLfloat nz, GLfloat x, GLfloat y, GLfloat z); -typedef void (APIENTRYP PFNGLTEXCOORD2FCOLOR4FNORMAL3FVERTEX3FVSUNPROC) (const GLfloat *tc, const GLfloat *c, const GLfloat *n, const GLfloat *v); -typedef void (APIENTRYP PFNGLTEXCOORD4FCOLOR4FNORMAL3FVERTEX4FSUNPROC) (GLfloat s, GLfloat t, GLfloat p, GLfloat q, GLfloat r, GLfloat g, GLfloat b, GLfloat a, GLfloat nx, GLfloat ny, GLfloat nz, GLfloat x, GLfloat y, GLfloat z, GLfloat w); -typedef void (APIENTRYP PFNGLTEXCOORD4FCOLOR4FNORMAL3FVERTEX4FVSUNPROC) (const GLfloat *tc, const GLfloat *c, const GLfloat *n, const GLfloat *v); -typedef void (APIENTRYP PFNGLREPLACEMENTCODEUIVERTEX3FSUNPROC) (GLuint rc, GLfloat x, GLfloat y, GLfloat z); -typedef void (APIENTRYP PFNGLREPLACEMENTCODEUIVERTEX3FVSUNPROC) (const GLuint *rc, const GLfloat *v); -typedef void (APIENTRYP PFNGLREPLACEMENTCODEUICOLOR4UBVERTEX3FSUNPROC) (GLuint rc, GLubyte r, GLubyte g, GLubyte b, GLubyte a, GLfloat x, GLfloat y, GLfloat z); -typedef void (APIENTRYP PFNGLREPLACEMENTCODEUICOLOR4UBVERTEX3FVSUNPROC) (const GLuint *rc, const GLubyte *c, const GLfloat *v); -typedef void (APIENTRYP PFNGLREPLACEMENTCODEUICOLOR3FVERTEX3FSUNPROC) (GLuint rc, GLfloat r, GLfloat g, GLfloat b, GLfloat x, GLfloat y, GLfloat z); -typedef void (APIENTRYP PFNGLREPLACEMENTCODEUICOLOR3FVERTEX3FVSUNPROC) (const GLuint *rc, const GLfloat *c, const GLfloat *v); -typedef void (APIENTRYP PFNGLREPLACEMENTCODEUINORMAL3FVERTEX3FSUNPROC) (GLuint rc, GLfloat nx, GLfloat ny, GLfloat nz, GLfloat x, GLfloat y, GLfloat z); -typedef void (APIENTRYP PFNGLREPLACEMENTCODEUINORMAL3FVERTEX3FVSUNPROC) (const GLuint *rc, const GLfloat *n, const GLfloat *v); -typedef void (APIENTRYP PFNGLREPLACEMENTCODEUICOLOR4FNORMAL3FVERTEX3FSUNPROC) (GLuint rc, GLfloat r, GLfloat g, GLfloat b, GLfloat a, GLfloat nx, GLfloat ny, GLfloat nz, GLfloat x, GLfloat y, GLfloat z); -typedef void (APIENTRYP PFNGLREPLACEMENTCODEUICOLOR4FNORMAL3FVERTEX3FVSUNPROC) (const GLuint *rc, const GLfloat *c, const GLfloat *n, const GLfloat *v); -typedef void (APIENTRYP PFNGLREPLACEMENTCODEUITEXCOORD2FVERTEX3FSUNPROC) (GLuint rc, GLfloat s, GLfloat t, GLfloat x, GLfloat y, GLfloat z); -typedef void (APIENTRYP PFNGLREPLACEMENTCODEUITEXCOORD2FVERTEX3FVSUNPROC) (const GLuint *rc, const GLfloat *tc, const GLfloat *v); -typedef void (APIENTRYP PFNGLREPLACEMENTCODEUITEXCOORD2FNORMAL3FVERTEX3FSUNPROC) (GLuint rc, GLfloat s, GLfloat t, GLfloat nx, GLfloat ny, GLfloat nz, GLfloat x, GLfloat y, GLfloat z); -typedef void (APIENTRYP PFNGLREPLACEMENTCODEUITEXCOORD2FNORMAL3FVERTEX3FVSUNPROC) (const GLuint *rc, const GLfloat *tc, const GLfloat *n, const GLfloat *v); -typedef void (APIENTRYP PFNGLREPLACEMENTCODEUITEXCOORD2FCOLOR4FNORMAL3FVERTEX3FSUNPROC) (GLuint rc, GLfloat s, GLfloat t, GLfloat r, GLfloat g, GLfloat b, GLfloat a, GLfloat nx, GLfloat ny, GLfloat nz, GLfloat x, GLfloat y, GLfloat z); -typedef void (APIENTRYP PFNGLREPLACEMENTCODEUITEXCOORD2FCOLOR4FNORMAL3FVERTEX3FVSUNPROC) (const GLuint *rc, const GLfloat *tc, const GLfloat *c, const GLfloat *n, const GLfloat *v); -#ifdef GL_GLEXT_PROTOTYPES -GLAPI void APIENTRY glColor4ubVertex2fSUN (GLubyte r, GLubyte g, GLubyte b, GLubyte a, GLfloat x, GLfloat y); -GLAPI void APIENTRY glColor4ubVertex2fvSUN (const GLubyte *c, const GLfloat *v); -GLAPI void APIENTRY glColor4ubVertex3fSUN (GLubyte r, GLubyte g, GLubyte b, GLubyte a, GLfloat x, GLfloat y, GLfloat z); -GLAPI void APIENTRY glColor4ubVertex3fvSUN (const GLubyte *c, const GLfloat *v); -GLAPI void APIENTRY glColor3fVertex3fSUN (GLfloat r, GLfloat g, GLfloat b, GLfloat x, GLfloat y, GLfloat z); -GLAPI void APIENTRY glColor3fVertex3fvSUN (const GLfloat *c, const GLfloat *v); -GLAPI void APIENTRY glNormal3fVertex3fSUN (GLfloat nx, GLfloat ny, GLfloat nz, GLfloat x, GLfloat y, GLfloat z); -GLAPI void APIENTRY glNormal3fVertex3fvSUN (const GLfloat *n, const GLfloat *v); -GLAPI void APIENTRY glColor4fNormal3fVertex3fSUN (GLfloat r, GLfloat g, GLfloat b, GLfloat a, GLfloat nx, GLfloat ny, GLfloat nz, GLfloat x, GLfloat y, GLfloat z); -GLAPI void APIENTRY glColor4fNormal3fVertex3fvSUN (const GLfloat *c, const GLfloat *n, const GLfloat *v); -GLAPI void APIENTRY glTexCoord2fVertex3fSUN (GLfloat s, GLfloat t, GLfloat x, GLfloat y, GLfloat z); -GLAPI void APIENTRY glTexCoord2fVertex3fvSUN (const GLfloat *tc, const GLfloat *v); -GLAPI void APIENTRY glTexCoord4fVertex4fSUN (GLfloat s, GLfloat t, GLfloat p, GLfloat q, GLfloat x, GLfloat y, GLfloat z, GLfloat w); -GLAPI void APIENTRY glTexCoord4fVertex4fvSUN (const GLfloat *tc, const GLfloat *v); -GLAPI void APIENTRY glTexCoord2fColor4ubVertex3fSUN (GLfloat s, GLfloat t, GLubyte r, GLubyte g, GLubyte b, GLubyte a, GLfloat x, GLfloat y, GLfloat z); -GLAPI void APIENTRY glTexCoord2fColor4ubVertex3fvSUN (const GLfloat *tc, const GLubyte *c, const GLfloat *v); -GLAPI void APIENTRY glTexCoord2fColor3fVertex3fSUN (GLfloat s, GLfloat t, GLfloat r, GLfloat g, GLfloat b, GLfloat x, GLfloat y, GLfloat z); -GLAPI void APIENTRY glTexCoord2fColor3fVertex3fvSUN (const GLfloat *tc, const GLfloat *c, const GLfloat *v); -GLAPI void APIENTRY glTexCoord2fNormal3fVertex3fSUN (GLfloat s, GLfloat t, GLfloat nx, GLfloat ny, GLfloat nz, GLfloat x, GLfloat y, GLfloat z); -GLAPI void APIENTRY glTexCoord2fNormal3fVertex3fvSUN (const GLfloat *tc, const GLfloat *n, const GLfloat *v); -GLAPI void APIENTRY glTexCoord2fColor4fNormal3fVertex3fSUN (GLfloat s, GLfloat t, GLfloat r, GLfloat g, GLfloat b, GLfloat a, GLfloat nx, GLfloat ny, GLfloat nz, GLfloat x, GLfloat y, GLfloat z); -GLAPI void APIENTRY glTexCoord2fColor4fNormal3fVertex3fvSUN (const GLfloat *tc, const GLfloat *c, const GLfloat *n, const GLfloat *v); -GLAPI void APIENTRY glTexCoord4fColor4fNormal3fVertex4fSUN (GLfloat s, GLfloat t, GLfloat p, GLfloat q, GLfloat r, GLfloat g, GLfloat b, GLfloat a, GLfloat nx, GLfloat ny, GLfloat nz, GLfloat x, GLfloat y, GLfloat z, GLfloat w); -GLAPI void APIENTRY glTexCoord4fColor4fNormal3fVertex4fvSUN (const GLfloat *tc, const GLfloat *c, const GLfloat *n, const GLfloat *v); -GLAPI void APIENTRY glReplacementCodeuiVertex3fSUN (GLuint rc, GLfloat x, GLfloat y, GLfloat z); -GLAPI void APIENTRY glReplacementCodeuiVertex3fvSUN (const GLuint *rc, const GLfloat *v); -GLAPI void APIENTRY glReplacementCodeuiColor4ubVertex3fSUN (GLuint rc, GLubyte r, GLubyte g, GLubyte b, GLubyte a, GLfloat x, GLfloat y, GLfloat z); -GLAPI void APIENTRY glReplacementCodeuiColor4ubVertex3fvSUN (const GLuint *rc, const GLubyte *c, const GLfloat *v); -GLAPI void APIENTRY glReplacementCodeuiColor3fVertex3fSUN (GLuint rc, GLfloat r, GLfloat g, GLfloat b, GLfloat x, GLfloat y, GLfloat z); -GLAPI void APIENTRY glReplacementCodeuiColor3fVertex3fvSUN (const GLuint *rc, const GLfloat *c, const GLfloat *v); -GLAPI void APIENTRY glReplacementCodeuiNormal3fVertex3fSUN (GLuint rc, GLfloat nx, GLfloat ny, GLfloat nz, GLfloat x, GLfloat y, GLfloat z); -GLAPI void APIENTRY glReplacementCodeuiNormal3fVertex3fvSUN (const GLuint *rc, const GLfloat *n, const GLfloat *v); -GLAPI void APIENTRY glReplacementCodeuiColor4fNormal3fVertex3fSUN (GLuint rc, GLfloat r, GLfloat g, GLfloat b, GLfloat a, GLfloat nx, GLfloat ny, GLfloat nz, GLfloat x, GLfloat y, GLfloat z); -GLAPI void APIENTRY glReplacementCodeuiColor4fNormal3fVertex3fvSUN (const GLuint *rc, const GLfloat *c, const GLfloat *n, const GLfloat *v); -GLAPI void APIENTRY glReplacementCodeuiTexCoord2fVertex3fSUN (GLuint rc, GLfloat s, GLfloat t, GLfloat x, GLfloat y, GLfloat z); -GLAPI void APIENTRY glReplacementCodeuiTexCoord2fVertex3fvSUN (const GLuint *rc, const GLfloat *tc, const GLfloat *v); -GLAPI void APIENTRY glReplacementCodeuiTexCoord2fNormal3fVertex3fSUN (GLuint rc, GLfloat s, GLfloat t, GLfloat nx, GLfloat ny, GLfloat nz, GLfloat x, GLfloat y, GLfloat z); -GLAPI void APIENTRY glReplacementCodeuiTexCoord2fNormal3fVertex3fvSUN (const GLuint *rc, const GLfloat *tc, const GLfloat *n, const GLfloat *v); -GLAPI void APIENTRY glReplacementCodeuiTexCoord2fColor4fNormal3fVertex3fSUN (GLuint rc, GLfloat s, GLfloat t, GLfloat r, GLfloat g, GLfloat b, GLfloat a, GLfloat nx, GLfloat ny, GLfloat nz, GLfloat x, GLfloat y, GLfloat z); -GLAPI void APIENTRY glReplacementCodeuiTexCoord2fColor4fNormal3fVertex3fvSUN (const GLuint *rc, const GLfloat *tc, const GLfloat *c, const GLfloat *n, const GLfloat *v); -#endif -#endif /* GL_SUN_vertex */ - -#ifndef GL_WIN_phong_shading -#define GL_WIN_phong_shading 1 -#define GL_PHONG_WIN 0x80EA -#define GL_PHONG_HINT_WIN 0x80EB -#endif /* GL_WIN_phong_shading */ - -#ifndef GL_WIN_specular_fog -#define GL_WIN_specular_fog 1 -#define GL_FOG_SPECULAR_TEXTURE_WIN 0x80EC -#endif /* GL_WIN_specular_fog */ - -#ifdef __cplusplus -} -#endif - -#endif diff --git a/impeller/third_party/stb/stb/tests/caveview/glext_list.h b/impeller/third_party/stb/stb/tests/caveview/glext_list.h deleted file mode 100644 index 5cdbca51f29ab..0000000000000 --- a/impeller/third_party/stb/stb/tests/caveview/glext_list.h +++ /dev/null @@ -1,34 +0,0 @@ -GLARB(ActiveTexture,ACTIVETEXTURE) -GLARB(ClientActiveTexture,CLIENTACTIVETEXTURE) -GLARB(MultiTexCoord2f,MULTITEXCOORD2F) -GLEXT(TexImage3D,TEXIMAGE3D) -GLEXT(TexSubImage3D,TEXSUBIMAGE3D) -GLEXT(GenerateMipmap,GENERATEMIPMAP) -GLARB(DebugMessageCallback,DEBUGMESSAGECALLBACK) - -GLCORE(VertexAttribIPointer,VERTEXATTRIBIPOINTER) - -GLEXT(BindFramebuffer,BINDFRAMEBUFFER) -GLEXT(DeleteFramebuffers,DELETEFRAMEBUFFERS) -GLEXT(GenFramebuffers,GENFRAMEBUFFERS) -GLEXT(CheckFramebufferStatus,CHECKFRAMEBUFFERSTATUS) -GLEXT(FramebufferTexture2D,FRAMEBUFFERTEXTURE2D) -GLEXT(BindRenderBuffer,BINDRENDERBUFFER) -GLEXT(RenderbufferStorage,RENDERBUFFERSTORAGE) -GLEXT(GenRenderbuffers,GENRENDERBUFFERS) -GLEXT(BindRenderbuffer,BINDRENDERBUFFER) -GLEXT(FramebufferRenderbuffer,FRAMEBUFFERRENDERBUFFER) -GLEXT(GenerateMipmap,GENERATEMIPMAP) - -GLARB(BindBuffer ,BINDBUFFER,) -GLARB(GenBuffers ,GENBUFFERS ) -GLARB(DeleteBuffers,DELETEBUFFERS) -GLARB(BufferData ,BUFFERDATA ) -GLARB(BufferSubData,BUFFERSUBDATA) -GLARB(MapBuffer ,MAPBUFFER ) -GLARB(UnmapBuffer ,UNMAPBUFFER ) -GLARB(TexBuffer ,TEXBUFFER ) - -GLEXT(NamedBufferStorage,NAMEDBUFFERSTORAGE) -GLE(BufferStorage,BUFFERSTORAGE) -GLE(GetStringi,GETSTRINGI) \ No newline at end of file diff --git a/impeller/third_party/stb/stb/tests/caveview/main.c b/impeller/third_party/stb/stb/tests/caveview/main.c deleted file mode 100644 index e69de29bb2d1d..0000000000000 diff --git a/impeller/third_party/stb/stb/tests/caveview/stb_gl.h b/impeller/third_party/stb/stb/tests/caveview/stb_gl.h deleted file mode 100644 index 6498e28d8d9ea..0000000000000 --- a/impeller/third_party/stb/stb/tests/caveview/stb_gl.h +++ /dev/null @@ -1,1103 +0,0 @@ -// stbgl - v0.04 - Sean Barrett 2008 - public domain -// -// Note that the gl extensions support requires glext.h. In fact, it works -// if you just concatenate glext.h onto the end of this file. In that case, -// this file is covered by the SGI FreeB license, and is not public domain. -// -// Extension usage: -// -// 1. Make a file called something like "extlist.txt" which contains stuff like: -// GLE(ShaderSourceARB,SHADERSOURCEARB) -// GLE(Uniform1iARB,UNIFORM1IARB) -// GLARB(ActiveTexture,ACTIVETEXTURE) // same as GLE(ActiveTextureARB,ACTIVETEXTUREARB) -// GLARB(ClientActiveTexture,CLIENTACTIVETEXTURE) -// GLE(MultiTexCoord2f,MULTITEXCOORD2F) -// -// 2. To declare functions (to make a header file), do this: -// #define STB_GLEXT_DECLARE "extlist.txt" -// #include "stb_gl.h" -// -// A good way to do this is to define STB_GLEXT_DECLARE project-wide. -// -// 3. To define functions (implement), do this in some C file: -// #define STB_GLEXT_DEFINE "extlist.txt" -// #include "stb_gl.h" -// -// If you've already defined STB_GLEXT_DECLARE, you can just do: -// #define STB_GLEXT_DEFINE_DECLARE -// #include "stb_gl.h" -// -// 4. Now you need to initialize: -// -// stbgl_initExtensions(); - - -#ifndef INCLUDE_STB_GL_H -#define INCLUDE_STB_GL_H - -#define STB_GL - -#ifdef _WIN32 -#ifndef WINGDIAPI -#define CALLBACK __stdcall -#define WINGDIAPI __declspec(dllimport) -#define APIENTRY __stdcall -#endif -#endif //_WIN32 - -#include - -#include -#include - -#ifndef M_PI -#define M_PI 3.14159265358979323846f -#endif - -#ifdef __cplusplus -extern "C" { -#endif - -// like gluPerspective, but: -// fov is chosen to satisfy both hfov <= max_hfov & vfov <= max_vfov; -// set one to 179 or 0 to ignore it -// zoom is applied separately, so you can do linear zoom without -// mucking with trig with fov; 1 -> use exact fov -// 'aspect' is inferred from the current viewport, and ignores the -// possibility of non-square pixels -extern void stbgl_Perspective(float zoom, float max_hfov, float max_vfov, float znear, float zfar); -extern void stbgl_PerspectiveViewport(int x, int y, int w, int h, float zoom, float max_hfov, float max_vfov, float znear, float zfar); -extern void stbgl_initCamera_zup_facing_x(void); -extern void stbgl_initCamera_zup_facing_y(void); -extern void stbgl_positionCameraWithEulerAngles(float *loc, float *ang); -extern void stbgl_drawRect(float x0, float y0, float x1, float y1); -extern void stbgl_drawRectTC(float x0, float y0, float x1, float y1, float s0, float t0, float s1, float t1); -extern void stbgl_drawBox(float x, float y, float z, float sx, float sy, float sz, int cw); - -extern int stbgl_hasExtension(char *ext); -extern void stbgl_SimpleLight(int index, float bright, float x, float y, float z); -extern void stbgl_GlobalAmbient(float r, float g, float b); - -extern int stbgl_LoadTexture(char *filename, char *props); // only if stb_image is available - -extern int stbgl_TestTexture(int w); -extern int stbgl_TestTextureEx(int w, char *scale_table, int checks_log2, int r1,int g1,int b1, int r2, int b2, int g2); -extern unsigned int stbgl_rand(void); // internal, but exposed just in case; LCG, so use middle bits - -extern int stbgl_TexImage2D(int texid, int w, int h, void *data, char *props); -extern int stbgl_TexImage2D_Extra(int texid, int w, int h, void *data, int chan, char *props, int preserve_data); -// "props" is a series of characters (and blocks of characters), a la fopen()'s mode, -// e.g.: -// GLuint texid = stbgl_LoadTexture("myfile.jpg", "mbc") -// means: load the image "myfile.jpg", and do the following: -// generate mipmaps -// use bilinear filtering (not trilinear) -// use clamp-to-edge on both channels -// -// input descriptor: AT MOST ONE -// TEXT MEANING -// 1 1 channel of input (intensity/alpha) -// 2 2 channels of input (luminance, alpha) -// 3 3 channels of input (RGB) -// 4 4 channels of input (RGBA) -// l 1 channel of input (luminance) -// a 1 channel of input (alpha) -// la 2 channels of input (lum/alpha) -// rgb 3 channels of input (RGB) -// ycocg 3 channels of input (YCoCg - forces YCoCg output) -// ycocgj 4 channels of input (YCoCgJunk - forces YCoCg output) -// rgba 4 channels of input (RGBA) -// -// output descriptor: AT MOST ONE -// TEXT MEANING -// A 1 channel of output (alpha) -// I 1 channel of output (intensity) -// LA 2 channels of output (lum/alpha) -// RGB 3 channels of output (RGB) -// RGBA 4 channels of output (RGBA) -// DXT1 encode as a DXT1 texture (RGB unless input has RGBA) -// DXT3 encode as a DXT3 texture -// DXT5 encode as a DXT5 texture -// YCoCg encode as a DXT5 texture with Y in alpha, CoCg in RG -// D GL_DEPTH_COMPONENT -// NONE no input/output, don't call TexImage2D at all -// -// when reading from a file or using another interface with an explicit -// channel count, the input descriptor is ignored and instead the channel -// count is used as the input descriptor. if the file read is a DXT DDS, -// then it is passed directly to OpenGL in the file format. -// -// if an input descriptor is supplied but no output descriptor, the output -// is assumed to be the same as the input. if an output descriptor is supplied -// but no input descriptor, the input is assumed to be the same as the -// output. if neither is supplied, the input is assumed to be 4-channel. -// If DXT1 or YCoCG output is requested with no input, the input is assumed -// to be 4-channel but the alpha channel is ignored. -// -// filtering descriptor (default is no mipmaps) -// TEXT MEANING -// m generate mipmaps -// M mipmaps are provided, concatenated at end of data (from largest to smallest) -// t use trilinear filtering (default if mipmapped) -// b use bilinear filtering (default if not-mipmapped) -// n use nearest-neighbor sampling -// -// wrapping descriptor -// TEXT MEANING -// w wrap (default) -// c clamp-to-edge -// C GL_CLAMP (uses border color) -// -// If only one wrapping descriptor is supplied, it is applied to both channels. -// -// special: -// TEXT MEANING -// f input data is floats (default unsigned bytes) -// F input&output data is floats (default unsigned bytes) -// p explicitly pre-multiply the alpha -// P pad to power-of-two (default stretches) -// NP2 non-power-of-two -// + can overwrite the texture data with temp data -// ! free the texture data with "free" -// -// the properties string can also include spaces - -#ifdef __cplusplus -} -#endif - - -#ifdef STB_GL_IMPLEMENTATION -#include -#include -#include -#include - -int stbgl_hasExtension(char *ext) -{ - const char *s = glGetString(GL_EXTENSIONS); - for(;;) { - char *e = ext; - for (;;) { - if (*e == 0) { - if (*s == 0 || *s == ' ') return 1; - break; - } - if (*s != *e) - break; - ++s, ++e; - } - while (*s && *s != ' ') ++s; - if (!*s) return 0; - ++s; // skip space - } -} - -void stbgl_drawRect(float x0, float y0, float x1, float y1) -{ - glBegin(GL_POLYGON); - glTexCoord2f(0,0); glVertex2f(x0,y0); - glTexCoord2f(1,0); glVertex2f(x1,y0); - glTexCoord2f(1,1); glVertex2f(x1,y1); - glTexCoord2f(0,1); glVertex2f(x0,y1); - glEnd(); -} - -void stbgl_drawRectTC(float x0, float y0, float x1, float y1, float s0, float t0, float s1, float t1) -{ - glBegin(GL_POLYGON); - glTexCoord2f(s0,t0); glVertex2f(x0,y0); - glTexCoord2f(s1,t0); glVertex2f(x1,y0); - glTexCoord2f(s1,t1); glVertex2f(x1,y1); - glTexCoord2f(s0,t1); glVertex2f(x0,y1); - glEnd(); -} - -void stbgl_drawBox(float x, float y, float z, float sx, float sy, float sz, int cw) -{ - float x0,y0,z0,x1,y1,z1; - sx /=2, sy/=2, sz/=2; - x0 = x-sx; y0 = y-sy; z0 = z-sz; - x1 = x+sx; y1 = y+sy; z1 = z+sz; - - glBegin(GL_QUADS); - if (cw) { - glNormal3f(0,0,-1); - glTexCoord2f(0,0); glVertex3f(x0,y0,z0); - glTexCoord2f(1,0); glVertex3f(x1,y0,z0); - glTexCoord2f(1,1); glVertex3f(x1,y1,z0); - glTexCoord2f(0,1); glVertex3f(x0,y1,z0); - - glNormal3f(0,0,1); - glTexCoord2f(0,0); glVertex3f(x1,y0,z1); - glTexCoord2f(1,0); glVertex3f(x0,y0,z1); - glTexCoord2f(1,1); glVertex3f(x0,y1,z1); - glTexCoord2f(0,1); glVertex3f(x1,y1,z1); - - glNormal3f(-1,0,0); - glTexCoord2f(0,0); glVertex3f(x0,y1,z1); - glTexCoord2f(1,0); glVertex3f(x0,y0,z1); - glTexCoord2f(1,1); glVertex3f(x0,y0,z0); - glTexCoord2f(0,1); glVertex3f(x0,y1,z0); - - glNormal3f(1,0,0); - glTexCoord2f(0,0); glVertex3f(x1,y0,z1); - glTexCoord2f(1,0); glVertex3f(x1,y1,z1); - glTexCoord2f(1,1); glVertex3f(x1,y1,z0); - glTexCoord2f(0,1); glVertex3f(x1,y0,z0); - - glNormal3f(0,-1,0); - glTexCoord2f(0,0); glVertex3f(x0,y0,z1); - glTexCoord2f(1,0); glVertex3f(x1,y0,z1); - glTexCoord2f(1,1); glVertex3f(x1,y0,z0); - glTexCoord2f(0,1); glVertex3f(x0,y0,z0); - - glNormal3f(0,1,0); - glTexCoord2f(0,0); glVertex3f(x1,y1,z1); - glTexCoord2f(1,0); glVertex3f(x0,y1,z1); - glTexCoord2f(1,1); glVertex3f(x0,y1,z0); - glTexCoord2f(0,1); glVertex3f(x1,y1,z0); - } else { - glNormal3f(0,0,-1); - glTexCoord2f(0,0); glVertex3f(x0,y0,z0); - glTexCoord2f(0,1); glVertex3f(x0,y1,z0); - glTexCoord2f(1,1); glVertex3f(x1,y1,z0); - glTexCoord2f(1,0); glVertex3f(x1,y0,z0); - - glNormal3f(0,0,1); - glTexCoord2f(0,0); glVertex3f(x1,y0,z1); - glTexCoord2f(0,1); glVertex3f(x1,y1,z1); - glTexCoord2f(1,1); glVertex3f(x0,y1,z1); - glTexCoord2f(1,0); glVertex3f(x0,y0,z1); - - glNormal3f(-1,0,0); - glTexCoord2f(0,0); glVertex3f(x0,y1,z1); - glTexCoord2f(0,1); glVertex3f(x0,y1,z0); - glTexCoord2f(1,1); glVertex3f(x0,y0,z0); - glTexCoord2f(1,0); glVertex3f(x0,y0,z1); - - glNormal3f(1,0,0); - glTexCoord2f(0,0); glVertex3f(x1,y0,z1); - glTexCoord2f(0,1); glVertex3f(x1,y0,z0); - glTexCoord2f(1,1); glVertex3f(x1,y1,z0); - glTexCoord2f(1,0); glVertex3f(x1,y1,z1); - - glNormal3f(0,-1,0); - glTexCoord2f(0,0); glVertex3f(x0,y0,z1); - glTexCoord2f(0,1); glVertex3f(x0,y0,z0); - glTexCoord2f(1,1); glVertex3f(x1,y0,z0); - glTexCoord2f(1,0); glVertex3f(x1,y0,z1); - - glNormal3f(0,1,0); - glTexCoord2f(0,0); glVertex3f(x1,y1,z1); - glTexCoord2f(0,1); glVertex3f(x1,y1,z0); - glTexCoord2f(1,1); glVertex3f(x0,y1,z0); - glTexCoord2f(1,0); glVertex3f(x0,y1,z1); - } - glEnd(); -} - -void stbgl_SimpleLight(int index, float bright, float x, float y, float z) -{ - float d = (float) (1.0f/sqrt(x*x+y*y+z*z)); - float dir[4] = { x*d,y*d,z*d,0 }, zero[4] = { 0,0,0,0 }; - float c[4] = { bright,bright,bright,0 }; - GLuint light = GL_LIGHT0 + index; - glLightfv(light, GL_POSITION, dir); - glLightfv(light, GL_DIFFUSE, c); - glLightfv(light, GL_AMBIENT, zero); - glLightfv(light, GL_SPECULAR, zero); - glEnable(light); - glColorMaterial(GL_FRONT, GL_AMBIENT_AND_DIFFUSE); - glEnable(GL_COLOR_MATERIAL); -} - -void stbgl_GlobalAmbient(float r, float g, float b) -{ - float v[4] = { r,g,b,0 }; - glLightModelfv(GL_LIGHT_MODEL_AMBIENT, v); -} - - -#define stbgl_rad2deg(r) ((r)*180.0f / M_PI) -#define stbgl_deg2rad(r) ((r)/180.0f * M_PI) - -void stbgl_Perspective(float zoom, float max_hfov, float max_vfov, float znear, float zfar) -{ - float unit_width, unit_height, aspect, vfov; - int data[4],w,h; - glGetIntegerv(GL_VIEWPORT, data); - w = data[2]; - h = data[3]; - aspect = (float) w / h; - - if (max_hfov <= 0) max_hfov = 179; - if (max_vfov <= 0) max_vfov = 179; - - // convert max_hfov, max_vfov to worldspace width at depth=1 - unit_width = (float) tan(stbgl_deg2rad(max_hfov/2)) * 2; - unit_height = (float) tan(stbgl_deg2rad(max_vfov/2)) * 2; - // check if hfov = max_hfov is enough to satisfy it - if (unit_width <= aspect * unit_height) { - float height = unit_width / aspect; - vfov = (float) atan(( height/2) / zoom); - } else { - vfov = (float) atan((unit_height/2) / zoom); - } - vfov = (float) stbgl_rad2deg(vfov * 2); - gluPerspective(vfov, aspect, znear, zfar); -} - -void stbgl_PerspectiveViewport(int x, int y, int w, int h, float zoom, float min_hfov, float min_vfov, float znear, float zfar) -{ - if (znear <= 0.0001f) znear = 0.0001f; - glViewport(x,y,w,h); - glScissor(x,y,w,h); - glMatrixMode(GL_PROJECTION); - glLoadIdentity(); - stbgl_Perspective(zoom, min_hfov, min_vfov, znear, zfar); - glMatrixMode(GL_MODELVIEW); -} - -// point the camera along the positive X axis, Z-up -void stbgl_initCamera_zup_facing_x(void) -{ - glRotatef(-90, 1,0,0); - glRotatef( 90, 0,0,1); -} - -// point the camera along the positive Y axis, Z-up -void stbgl_initCamera_zup_facing_y(void) -{ - glRotatef(-90, 1,0,0); -} - -// setup a camera using Euler angles -void stbgl_positionCameraWithEulerAngles(float *loc, float *ang) -{ - glRotatef(-ang[1], 0,1,0); - glRotatef(-ang[0], 1,0,0); - glRotatef(-ang[2], 0,0,1); - glTranslatef(-loc[0], -loc[1], -loc[2]); -} - -static int stbgl_m(char *a, char *b) -{ - // skip first character - do { ++a,++b; } while (*b && *a == *b); - return *b == 0; -} - -#ifdef STBI_VERSION -#ifndef STBI_NO_STDIO -int stbgl_LoadTexture(char *filename, char *props) -{ - // @TODO: handle DDS files directly - int res; - void *data; - int w,h,c; - #ifndef STBI_NO_HDR - if (stbi_is_hdr(filename)) { - data = stbi_loadf(filename, &w, &h, &c, 0); - if (!data) return 0; - res = stbgl_TexImage2D_Extra(0, w,h,data, -c, props, 0); - free(data); - return res; - } - #endif - - data = stbi_load(filename, &w, &h, &c, 0); - if (!data) return 0; - res = stbgl_TexImage2D_Extra(0, w,h,data, c, props, 0); - free(data); - return res; -} -#endif -#endif // STBI_VERSION - -int stbgl_TexImage2D(int texid, int w, int h, void *data, char *props) -{ - return stbgl_TexImage2D_Extra(texid, w, h, data, 0, props,1); -} - -int stbgl_TestTexture(int w) -{ - char scale_table[] = { 10,20,30,30,35,40,5,18,25,13,7,5,3,3,2,2,2,2,1,1,1,1,1,0,0,0,0,0,0,0,0,0,0 }; - return stbgl_TestTextureEx(w, scale_table, 2, 140,130,200, 180,200,170); -} - -unsigned int stbgl_rand(void) -{ - static unsigned int stbgl__rand_seed = 3248980923; // random typing - return stbgl__rand_seed = stbgl__rand_seed * 2147001325 + 715136305; // BCPL generator -} - -// wish this could be smaller, since it's so frivolous -int stbgl_TestTextureEx(int w, char *scale_table, int checks_log2, int r1,int g1,int b1, int r2, int b2, int g2) -{ - int rt[2] = {r1,r2}, gt[2] = {g1,g2}, bt[2] = {b1,b2}; - signed char modded[256]; - int i,j, m = w-1, s,k,scale; - unsigned char *data = (unsigned char *) malloc(w*w*3); - assert((m & w) == 0); - data[0] = 128; - for (s=0; s < 16; ++s) if ((1 << s) == w) break; - assert(w == (1 << s)); - // plasma fractal noise - for (k=s-1; k >= 0; --k) { - int step = 1 << k; - // interpolate from "parents" - for (j=0; j < w; j += step*2) { - for (i=0; i < w; i += step*2) { - int i1 = i+step, j1=j+step; - int i2 = (i+step*2)&m, j2 = (j+step*2)&m; - int p00 = data[(j*w+i )*3], p01 = data[(j2*w+i )*3]; - int p10 = data[(j*w+i2)*3], p11 = data[(j2*w+i2)*3]; - data[(j*w+i1)*3] = (p00+p10)>>1; - data[(j1*w+i)*3] = (p00+p01)>>1; - data[(j1*w+i1)*3]= (p00+p01+p10+p11)>>2; - } - } - scale = scale_table[s-k+1]; - if (!scale) continue; // just interpolate down the remaining data - for (j=0,i=0; i < 256; i += 2, j == scale ? j=0 : ++j) - modded[i] = j, modded[i+1] = -j; // precompute i%scale (plus sign) - for (j=0; j < w; j += step) - for (i=0; i < w; i += step) { - int x = data[(j*w+i)*3] + modded[(stbgl_rand() >> 12) & 255]; - data[(j*w+i)*3] = x < 0 ? 0 : x > 255 ? 255 : x; - } - } - for (j=0; j < w; ++j) - for (i=0; i < w; ++i) { - int check = ((i^j) & (1 << (s-checks_log2))) == 0; - int v = data[(j*w+i)*3] >> 2; - data[(j*w+i)*3+0] = rt[check]-v; - data[(j*w+i)*3+1] = gt[check]-v; - data[(j*w+i)*3+2] = bt[check]-v; - } - return stbgl_TexImage2D(0, w, w, data, "3m!"); // 3 channels, mipmap, free -} - -#ifdef _WIN32 -#ifndef WINGDIAPI -typedef int (__stdcall *stbgl__voidfunc)(void); -__declspec(dllimport) stbgl__voidfunc wglGetProcAddress(char *); -#endif -#define STB__HAS_WGLPROC -static void (__stdcall *stbgl__CompressedTexImage2DARB)(int target, int level, - int internalformat, int width, - int height, int border, - int imageSize, void *data); -static void stbgl__initCompTex(void) -{ - *((void **) &stbgl__CompressedTexImage2DARB) = (void *) wglGetProcAddress("glCompressedTexImage2DARB"); -} -#else -static void (*stbgl__CompressedTexImage2DARB)(int target, int level, - int internalformat, int width, - int height, int border, - int imageSize, void *data); -static void stbgl__initCompTex(void) -{ -} -#endif // _WIN32 - -#define STBGL_COMPRESSED_RGB_S3TC_DXT1 0x83F0 -#define STBGL_COMPRESSED_RGBA_S3TC_DXT1 0x83F1 -#define STBGL_COMPRESSED_RGBA_S3TC_DXT3 0x83F2 -#define STBGL_COMPRESSED_RGBA_S3TC_DXT5 0x83F3 - -#ifdef STB_COMPRESS_DXT_BLOCK -static void stbgl__convert(uint8 *p, uint8 *q, int n, int input_desc, uint8 *end) -{ - int i; - switch (input_desc) { - case GL_RED: - case GL_LUMINANCE: for (i=0; i < n; ++i,p+=4) p[0] = p[1] = p[2] = q[0], p[3]=255, q+=1; break; - case GL_ALPHA: for (i=0; i < n; ++i,p+=4) p[0] = p[1] = p[2] = 0, p[3] = q[0], q+=1; break; - case GL_LUMINANCE_ALPHA: for (i=0; i < n; ++i,p+=4) p[0] = p[1] = p[2] = q[0], p[3]=q[1], q+=2; break; - case GL_RGB: for (i=0; i < n; ++i,p+=4) p[0]=q[0],p[1]=q[1],p[2]=q[2],p[3]=255,q+=3; break; - case GL_RGBA: memcpy(p, q, n*4); break; - case GL_INTENSITY: for (i=0; i < n; ++i,p+=4) p[0] = p[1] = p[2] = p[3] = q[0], q+=1; break; - } - assert(p <= end); -} - -static void stbgl__compress(uint8 *p, uint8 *rgba, int w, int h, int output_desc, uint8 *end) -{ - int i,j,y,y2; - int alpha = (output_desc == STBGL_COMPRESSED_RGBA_S3TC_DXT5); - for (j=0; j < w; j += 4) { - int x=4; - for (i=0; i < h; i += 4) { - uint8 block[16*4]; - if (i+3 >= w) x = w-i; - for (y=0; y < 4; ++y) { - if (j+y >= h) break; - memcpy(block+y*16, rgba + w*4*(j+y) + i*4, x*4); - } - if (x < 4) { - switch (x) { - case 0: assert(0); - case 1: - for (y2=0; y2 < y; ++y2) { - memcpy(block+y2*16+1*4, block+y2*16+0*4, 4); - memcpy(block+y2*16+2*4, block+y2*16+0*4, 8); - } - break; - case 2: - for (y2=0; y2 < y; ++y2) - memcpy(block+y2*16+2*4, block+y2*16+0*4, 8); - break; - case 3: - for (y2=0; y2 < y; ++y2) - memcpy(block+y2*16+3*4, block+y2*16+1*4, 4); - break; - } - } - y2 = 0; - for(; y<4; ++y,++y2) - memcpy(block+y*16, block+y2*16, 4*4); - stb_compress_dxt_block(p, block, alpha, 10); - p += alpha ? 16 : 8; - } - } - assert(p <= end); -} -#endif // STB_COMPRESS_DXT_BLOCK - -// use the reserved temporary-use enumerant range, since no -// OpenGL enumerants should fall in that range -enum -{ - STBGL_UNDEFINED = 0x6000, - STBGL_YCOCG, - STBGL_YCOCGJ, - STBGL_GEN_MIPMAPS, - STBGL_MIPMAPS, - STBGL_NO_DOWNLOAD, -}; - -#define STBGL_CLAMP_TO_EDGE 0x812F -#define STBGL_CLAMP_TO_BORDER 0x812D - -#define STBGL_DEPTH_COMPONENT16 0x81A5 -#define STBGL_DEPTH_COMPONENT24 0x81A6 -#define STBGL_DEPTH_COMPONENT32 0x81A7 - -int stbgl_TexImage2D_Extra(int texid, int w, int h, void *data, int chan, char *props, int preserve_data) -{ - static int has_s3tc = -1; // haven't checked yet - int free_data = 0, is_compressed = 0; - int pad_to_power_of_two = 0, non_power_of_two = 0; - int premultiply_alpha = 0; // @TODO - int float_tex = 0; // @TODO - int input_type = GL_UNSIGNED_BYTE; - int input_desc = STBGL_UNDEFINED; - int output_desc = STBGL_UNDEFINED; - int mipmaps = STBGL_UNDEFINED; - int filter = STBGL_UNDEFINED, mag_filter; - int wrap_s = STBGL_UNDEFINED, wrap_t = STBGL_UNDEFINED; - - // parse out the properties - if (props == NULL) props = ""; - while (*props) { - switch (*props) { - case '1' : input_desc = GL_LUMINANCE; break; - case '2' : input_desc = GL_LUMINANCE_ALPHA; break; - case '3' : input_desc = GL_RGB; break; - case '4' : input_desc = GL_RGBA; break; - case 'l' : if (props[1] == 'a') { input_desc = GL_LUMINANCE_ALPHA; ++props; } - else input_desc = GL_LUMINANCE; - break; - case 'a' : input_desc = GL_ALPHA; break; - case 'r' : if (stbgl_m(props, "rgba")) { input_desc = GL_RGBA; props += 3; break; } - if (stbgl_m(props, "rgb")) { input_desc = GL_RGB; props += 2; break; } - input_desc = GL_RED; - break; - case 'y' : if (stbgl_m(props, "ycocg")) { - if (props[5] == 'j') { props += 5; input_desc = STBGL_YCOCGJ; } - else { props += 4; input_desc = STBGL_YCOCG; } - break; - } - return 0; - case 'L' : if (props[1] == 'A') { output_desc = GL_LUMINANCE_ALPHA; ++props; } - else output_desc = GL_LUMINANCE; - break; - case 'I' : output_desc = GL_INTENSITY; break; - case 'A' : output_desc = GL_ALPHA; break; - case 'R' : if (stbgl_m(props, "RGBA")) { output_desc = GL_RGBA; props += 3; break; } - if (stbgl_m(props, "RGB")) { output_desc = GL_RGB; props += 2; break; } - output_desc = GL_RED; - break; - case 'Y' : if (stbgl_m(props, "YCoCg") || stbgl_m(props, "YCOCG")) { - props += 4; - output_desc = STBGL_YCOCG; - break; - } - return 0; - case 'D' : if (stbgl_m(props, "DXT")) { - switch (props[3]) { - case '1': output_desc = STBGL_COMPRESSED_RGB_S3TC_DXT1; break; - case '3': output_desc = STBGL_COMPRESSED_RGBA_S3TC_DXT3; break; - case '5': output_desc = STBGL_COMPRESSED_RGBA_S3TC_DXT5; break; - default: return 0; - } - props += 3; - } else if (stbgl_m(props, "D16")) { - output_desc = STBGL_DEPTH_COMPONENT16; - input_desc = GL_DEPTH_COMPONENT; - props += 2; - } else if (stbgl_m(props, "D24")) { - output_desc = STBGL_DEPTH_COMPONENT24; - input_desc = GL_DEPTH_COMPONENT; - props += 2; - } else if (stbgl_m(props, "D32")) { - output_desc = STBGL_DEPTH_COMPONENT32; - input_desc = GL_DEPTH_COMPONENT; - props += 2; - } else { - output_desc = GL_DEPTH_COMPONENT; - input_desc = GL_DEPTH_COMPONENT; - } - break; - case 'N' : if (stbgl_m(props, "NONE")) { - props += 3; - input_desc = STBGL_NO_DOWNLOAD; - output_desc = STBGL_NO_DOWNLOAD; - break; - } - if (stbgl_m(props, "NP2")) { - non_power_of_two = 1; - props += 2; - break; - } - return 0; - case 'm' : mipmaps = STBGL_GEN_MIPMAPS; break; - case 'M' : mipmaps = STBGL_MIPMAPS; break; - case 't' : filter = GL_LINEAR_MIPMAP_LINEAR; break; - case 'b' : filter = GL_LINEAR; break; - case 'n' : filter = GL_NEAREST; break; - case 'w' : if (wrap_s == STBGL_UNDEFINED) wrap_s = GL_REPEAT; else wrap_t = GL_REPEAT; break; - case 'C' : if (wrap_s == STBGL_UNDEFINED) wrap_s = STBGL_CLAMP_TO_BORDER; else wrap_t = STBGL_CLAMP_TO_BORDER; break; - case 'c' : if (wrap_s == STBGL_UNDEFINED) wrap_s = STBGL_CLAMP_TO_EDGE; else wrap_t = STBGL_CLAMP_TO_EDGE; break; - case 'f' : input_type = GL_FLOAT; break; - case 'F' : input_type = GL_FLOAT; float_tex = 1; break; - case 'p' : premultiply_alpha = 1; break; - case 'P' : pad_to_power_of_two = 1; break; - case '+' : preserve_data = 0; break; - case '!' : preserve_data = 0; free_data = 1; break; - case ' ' : break; - case '-' : break; - default : if (free_data) free(data); - return 0; - } - ++props; - } - - // override input_desc based on channel count - if (output_desc != STBGL_NO_DOWNLOAD) { - switch (abs(chan)) { - case 1: input_desc = GL_LUMINANCE; break; - case 2: input_desc = GL_LUMINANCE_ALPHA; break; - case 3: input_desc = GL_RGB; break; - case 4: input_desc = GL_RGBA; break; - case 0: break; - default: return 0; - } - } - - // override input_desc based on channel info - if (chan > 0) { input_type = GL_UNSIGNED_BYTE; } - if (chan < 0) { input_type = GL_FLOAT; } - - if (output_desc == GL_ALPHA) { - if (input_desc == GL_LUMINANCE) - input_desc = GL_ALPHA; - if (input_desc == GL_RGB) { - // force a presumably-mono image to alpha - // @TODO handle 'preserve_data' case? - if (data && !preserve_data && input_type == GL_UNSIGNED_BYTE) { - int i; - unsigned char *p = (unsigned char *) data, *q = p; - for (i=0; i < w*h; ++i) { - *q = (p[0] + 2*p[1] + p[2]) >> 2; - p += 3; - q += 1; - } - input_desc = GL_ALPHA; - } - } - } - - // set undefined input/output based on the other - if (input_desc == STBGL_UNDEFINED && output_desc == STBGL_UNDEFINED) { - input_desc = output_desc = GL_RGBA; - } else if (output_desc == STBGL_UNDEFINED) { - switch (input_desc) { - case GL_LUMINANCE: - case GL_ALPHA: - case GL_LUMINANCE_ALPHA: - case GL_RGB: - case GL_RGBA: - output_desc = input_desc; - break; - case GL_RED: - output_desc = GL_INTENSITY; - break; - case STBGL_YCOCG: - case STBGL_YCOCGJ: - output_desc = STBGL_YCOCG; - break; - default: assert(0); return 0; - } - } else if (input_desc == STBGL_UNDEFINED) { - switch (output_desc) { - case GL_LUMINANCE: - case GL_ALPHA: - case GL_LUMINANCE_ALPHA: - case GL_RGB: - case GL_RGBA: - input_desc = output_desc; - break; - case GL_INTENSITY: - input_desc = GL_RED; - break; - case STBGL_YCOCG: - case STBGL_COMPRESSED_RGB_S3TC_DXT1: - case STBGL_COMPRESSED_RGBA_S3TC_DXT3: - case STBGL_COMPRESSED_RGBA_S3TC_DXT5: - input_desc = GL_RGBA; - break; - } - } else { - if (output_desc == STBGL_COMPRESSED_RGB_S3TC_DXT1) { - // if input has alpha, force output alpha - switch (input_desc) { - case GL_ALPHA: - case GL_LUMINANCE_ALPHA: - case GL_RGBA: - output_desc = STBGL_COMPRESSED_RGBA_S3TC_DXT5; - break; - } - } - } - - switch(input_desc) { - case GL_LUMINANCE: - case GL_RED: - case GL_ALPHA: - chan = 1; - break; - case GL_LUMINANCE_ALPHA: - chan = 2; - break; - case GL_RGB: - chan = 3; - break; - case GL_RGBA: - chan = 4; - break; - } - - if (pad_to_power_of_two && ((w & (w-1)) || (h & (h-1)))) { - if (output_desc != STBGL_NO_DOWNLOAD && input_type == GL_UNSIGNED_BYTE && chan > 0) { - unsigned char *new_data; - int w2 = w, h2 = h, j; - while (w & (w-1)) - w = (w | (w>>1))+1; - while (h & (h-1)) - h = (h | (h>>1))+1; - new_data = malloc(w * h * chan); - for (j=0; j < h2; ++j) { - memcpy(new_data + j * w * chan, (char *) data+j*w2*chan, w2*chan); - memset(new_data + (j * w+w2) * chan, 0, (w-w2)*chan); - } - for (; j < h; ++j) - memset(new_data + j*w*chan, 0, w*chan); - if (free_data) - free(data); - data = new_data; - free_data = 1; - } - } - - switch (output_desc) { - case STBGL_COMPRESSED_RGB_S3TC_DXT1: - case STBGL_COMPRESSED_RGBA_S3TC_DXT1: - case STBGL_COMPRESSED_RGBA_S3TC_DXT3: - case STBGL_COMPRESSED_RGBA_S3TC_DXT5: - is_compressed = 1; - if (has_s3tc == -1) { - has_s3tc = stbgl_hasExtension("GL_EXT_texture_compression_s3tc"); - if (has_s3tc) stbgl__initCompTex(); - } - if (!has_s3tc) { - is_compressed = 0; - if (output_desc == STBGL_COMPRESSED_RGB_S3TC_DXT1) - output_desc = GL_RGB; - else - output_desc = GL_RGBA; - } - } - - if (output_desc == STBGL_YCOCG) { - assert(0); - output_desc = GL_RGB; // @TODO! - if (free_data) free(data); - return 0; - } - - mag_filter = 0; - if (mipmaps != STBGL_UNDEFINED) { - switch (filter) { - case STBGL_UNDEFINED: filter = GL_LINEAR_MIPMAP_LINEAR; break; - case GL_NEAREST : mag_filter = GL_NEAREST; filter = GL_LINEAR_MIPMAP_LINEAR; break; - case GL_LINEAR : filter = GL_LINEAR_MIPMAP_NEAREST; break; - } - } else { - if (filter == STBGL_UNDEFINED) - filter = GL_LINEAR; - } - - // update filtering - if (!mag_filter) { - if (filter == GL_NEAREST) - mag_filter = GL_NEAREST; - else - mag_filter = GL_LINEAR; - } - - // update wrap/clamp - if (wrap_s == STBGL_UNDEFINED) wrap_s = GL_REPEAT; - if (wrap_t == STBGL_UNDEFINED) wrap_t = wrap_s; - - // if no texture id, generate one - if (texid == 0) { - GLuint tex; - glGenTextures(1, &tex); - if (tex == 0) { if (free_data) free(data); return 0; } - texid = tex; - } - - if (data == NULL && mipmaps == STBGL_GEN_MIPMAPS) - mipmaps = STBGL_MIPMAPS; - - if (output_desc == STBGL_NO_DOWNLOAD) - mipmaps = STBGL_NO_DOWNLOAD; - - glBindTexture(GL_TEXTURE_2D, texid); - -#ifdef STB_COMPRESS_DXT_BLOCK - if (!is_compressed || !stbgl__CompressedTexImage2DARB || output_desc == STBGL_COMPRESSED_RGBA_S3TC_DXT3 || data == NULL) -#endif - { - switch (mipmaps) { - case STBGL_NO_DOWNLOAD: - break; - - case STBGL_UNDEFINED: - // check if actually power-of-two - if (non_power_of_two || ((w & (w-1)) == 0 && (h & (h-1)) == 0)) - glTexImage2D(GL_TEXTURE_2D, 0, output_desc, w, h, 0, input_desc, input_type, data); - else - gluBuild2DMipmaps(GL_TEXTURE_2D, output_desc, w, h, input_desc, input_type, data); - // not power of two, so use glu to resize (generates mipmaps needlessly) - break; - - case STBGL_MIPMAPS: { - int level = 0; - int size = input_type == GL_FLOAT ? sizeof(float) : 1; - if (data == NULL) size = 0; // reuse same block of memory for all mipmaps - assert((w & (w-1)) == 0 && (h & (h-1)) == 0); // verify power-of-two - while (w > 1 && h > 1) { - glTexImage2D(GL_TEXTURE_2D, level, output_desc, w, h, 0, input_desc, input_type, data); - data = (void *) ((char *) data + w * h * size * chan); - if (w > 1) w >>= 1; - if (h > 1) h >>= 1; - ++level; - } - break; - } - case STBGL_GEN_MIPMAPS: - gluBuild2DMipmaps(GL_TEXTURE_2D, output_desc, w, h, input_desc, input_type, data); - break; - - default: - assert(0); - if (free_data) free(data); - return 0; - } -#ifdef STB_COMPRESS_DXT_BLOCK - } else { - uint8 *out, *rgba=0, *end_out, *end_rgba; - int level = 0, alpha = (output_desc != STBGL_COMPRESSED_RGB_S3TC_DXT1); - int size = input_type == GL_FLOAT ? sizeof(float) : 1; - int osize = alpha ? 16 : 8; - if (!free_data && mipmaps == STBGL_GEN_MIPMAPS) { - uint8 *temp = malloc(w*h*chan); - if (!temp) { if (free_data) free(data); return 0; } - memcpy(temp, data, w*h*chan); - if (free_data) free(data); - free_data = 1; - data = temp; - } - if (chan != 4 || size != 1) { - rgba = malloc(w*h*4); - if (!rgba) return 0; - end_rgba = rgba+w*h*4; - } - out = malloc((w+3)*(h+3)/16*osize); // enough storage for the s3tc data - if (!out) return 0; - end_out = out + ((w+3)*(h+3))/16*osize; - - for(;;) { - if (chan != 4) - stbgl__convert(rgba, data, w*h, input_desc, end_rgba); - stbgl__compress(out, rgba ? rgba : data, w, h, output_desc, end_out); - stbgl__CompressedTexImage2DARB(GL_TEXTURE_2D, level, output_desc, w, h, 0, ((w+3)&~3)*((h+3)&~3)/16*osize, out); - //glTexImage2D(GL_TEXTURE_2D, level, alpha?GL_RGBA:GL_RGB, w, h, 0, GL_RGBA, GL_UNSIGNED_BYTE, rgba ? rgba : data); - - if (mipmaps == STBGL_UNDEFINED) break; - if (w <= 1 && h <= 1) break; - if (mipmaps == STBGL_MIPMAPS) data = (void *) ((char *) data + w * h * size * chan); - if (mipmaps == STBGL_GEN_MIPMAPS) { - int w2 = w>>1, h2=h>>1, i,j,k, s=w*chan; - uint8 *p = data, *q=data; - if (w == 1) { - for (j=0; j < h2; ++j) { - for (k=0; k < chan; ++k) - *p++ = (q[k] + q[s+k] + 1) >> 1; - q += s*2; - } - } else if (h == 1) { - for (i=0; i < w2; ++i) { - for (k=0; k < chan; ++k) - *p++ = (q[k] + q[k+chan] + 1) >> 1; - q += chan*2; - } - } else { - for (j=0; j < h2; ++j) { - for (i=0; i < w2; ++i) { - for (k=0; k < chan; ++k) - *p++ = (q[k] + q[k+chan] + q[s+k] + q[s+k+chan] + 2) >> 2; - q += chan*2; - } - q += s; - } - } - } - if (w > 1) w >>= 1; - if (h > 1) h >>= 1; - ++level; - } - if (out) free(out); - if (rgba) free(rgba); -#endif // STB_COMPRESS_DXT_BLOCK - } - - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, wrap_s); - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, wrap_t); - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, mag_filter); - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, filter); - - if (free_data) free(data); - return texid; -} - -#endif // STB_DEFINE -#undef STB_EXTERN - -#endif //INCLUDE_STB_GL_H - -// Extension handling... must be outside the INCLUDE_ brackets - -#if defined(STB_GLEXT_DEFINE) || defined(STB_GLEXT_DECLARE) - -#ifndef STB_GLEXT_SKIP_DURING_RECURSION - -#ifndef GL_GLEXT_VERSION - - // First check if glext.h is concatenated on the end of this file - // (if it's concatenated on the beginning, we'll have GL_GLEXT_VERSION) - - #define STB_GLEXT_SKIP_DURING_RECURSION - #include __FILE__ - #undef STB_GLEXT_SKIP_DURING_RECURSION - - // now check if it's still undefined; if so, try going for it by name; - // if this errors, that's fine, since we can't compile without it - - #ifndef GL_GLEXT_VERSION - #include "glext.h" - #endif -#endif - -#define GLARB(a,b) GLE(a##ARB,b##ARB) -#define GLEXT(a,b) GLE(a##EXT,b##EXT) -#define GLNV(a,b) GLE(a##NV ,b##NV) -#define GLATI(a,b) GLE(a##ATI,b##ATI) -#define GLCORE(a,b) GLE(a,b) - -#ifdef STB_GLEXT_DEFINE_DECLARE -#define STB_GLEXT_DEFINE STB_GLEXT_DECLARE -#endif - -#if defined(STB_GLEXT_DECLARE) && defined(STB_GLEXT_DEFINE) -#undef STB_GLEXT_DECLARE -#endif - -#if defined(STB_GLEXT_DECLARE) && !defined(STB_GLEXT_DEFINE) - #define GLE(a,b) extern PFNGL##b##PROC gl##a; - - #ifdef __cplusplus - extern "C" { - #endif - - extern void stbgl_initExtensions(void); - - #include STB_GLEXT_DECLARE - - #ifdef __cplusplus - }; - #endif - -#else - - #ifndef STB_GLEXT_DEFINE - #error "Header file is screwed up somehow" - #endif - - #ifdef _WIN32 - #ifndef WINGDIAPI - #ifndef STB__HAS_WGLPROC - typedef int (__stdcall *stbgl__voidfunc)(void); - __declspec(dllimport) stbgl__voidfunc wglGetProcAddress(char *); - #endif - #endif - #define STBGL__GET_FUNC(x) wglGetProcAddress(x) - #endif - - #ifdef GLE - #undef GLE - #endif - - #define GLE(a,b) PFNGL##b##PROC gl##a; - #include STB_GLEXT_DEFINE - - #undef GLE - #define GLE(a,b) gl##a = (PFNGL##b##PROC) STBGL__GET_FUNC("gl" #a ); - - void stbgl_initExtensions(void) - { - #include STB_GLEXT_DEFINE - } - - #undef GLE - -#endif // STB_GLEXT_DECLARE - -#endif // STB_GLEXT_SKIP - -#endif // STB_GLEXT_DEFINE || STB_GLEXT_DECLARE diff --git a/impeller/third_party/stb/stb/tests/caveview/stb_glprog.h b/impeller/third_party/stb/stb/tests/caveview/stb_glprog.h deleted file mode 100644 index 8883a3ebe9124..0000000000000 --- a/impeller/third_party/stb/stb/tests/caveview/stb_glprog.h +++ /dev/null @@ -1,504 +0,0 @@ -// stb_glprog v0.02 public domain functions to reduce GLSL boilerplate -// http://nothings.org/stb/stb_glprog.h especially with GL1 + ARB extensions -// -// Following defines *before* including have following effects: -// -// STB_GLPROG_IMPLEMENTATION -// creates the implementation -// -// STB_GLPROG_STATIC -// forces the implementation to be static (private to file that creates it) -// -// STB_GLPROG_ARB -// uses ARB extension names for GLSL functions and enumerants instead of core names -// -// STB_GLPROG_ARB_DEFINE_EXTENSIONS -// instantiates function pointers needed, static to implementing file -// to avoid collisions (but will collide if implementing file also -// defines any; best to isolate this to its own file in this case). -// This will try to automatically #include glext.h, but if it's not -// in the default include directories you'll need to include it -// yourself and define the next macro. -// -// STB_GLPROG_SUPPRESS_GLEXT_INCLUDE -// disables the automatic #include of glext.h which is normally -// forced by STB_GLPROG_ARB_DEFINE_EXTENSIONS -// -// So, e.g., sample usage on an old Windows compiler: -// -// #define STB_GLPROG_IMPLEMENTATION -// #define STB_GLPROG_ARB_DEFINE_EXTENSIONS -// #include -// #include "gl/gl.h" -// #include "stb_glprog.h" -// -// Note though that the header-file version of this (when you don't define -// STB_GLPROG_IMPLEMENTATION) still uses GLint and such, so you basically -// can only include it in places where you're already including GL, especially -// on Windows where including "gl.h" requires (some of) "windows.h". -// -// See following comment blocks for function documentation. -// -// Version history: -// 2013-12-08 v0.02 slightly simplified API and reduced GL resource usage (@rygorous) -// 2013-12-08 v0.01 initial release - - -// header file section starts here -#if !defined(INCLUDE_STB_GLPROG_H) -#define INCLUDE_STB_GLPROG_H - -#ifndef STB_GLPROG_STATIC -#ifdef __cplusplus -extern "C" { -#endif - -////////////////////////////////////////////////////////////////////////////// - -///////////// SHADER CREATION - - -/// EASY API - -extern GLuint stbgl_create_program(char const **vertex_source, char const **frag_source, char const **binds, char *error, int error_buflen); -// This function returns a compiled program or 0 if there's an error. -// To free the created program, call stbgl_delete_program. -// -// stbgl_create_program( -// char **vertex_source, // NULL or one or more strings with the vertex shader source, with a final NULL -// char **frag_source, // NULL or one or more strings with the fragment shader source, with a final NULL -// char **binds, // NULL or zero or more strings with attribute bind names, with a final NULL -// char *error, // output location where compile error message is placed -// int error_buflen) // length of error output buffer -// -// Returns a GLuint with the GL program object handle. -// -// If an individual bind string is "", no name is bound to that slot (this -// allows you to create binds that aren't continuous integers starting at 0). -// -// If the vertex shader is NULL, then fixed-function vertex pipeline -// is used, if that's legal in your version of GL. -// -// If the fragment shader is NULL, then fixed-function fragment pipeline -// is used, if that's legal in your version of GL. - -extern void stgbl_delete_program(GLuint program); -// deletes a program created by stbgl_create_program or stbgl_link_program - - -/// FLEXIBLE API - -extern GLuint stbgl_compile_shader(GLenum type, char const **sources, int num_sources, char *error, int error_buflen); -// compiles a shader. returns the shader on success or 0 on failure. -// -// type either: GL_VERTEX_SHADER or GL_FRAGMENT_SHADER -// or GL_VERTEX_SHADER_ARB or GL_FRAGMENT_SHADER_ARB -// or STBGL_VERTEX_SHADER or STBGL_FRAGMENT_SHADER -// sources array of strings containing the shader source -// num_sources number of string in sources, or -1 meaning sources is NULL-terminated -// error string to output compiler error to -// error_buflen length of error buffer in chars - -extern GLuint stbgl_link_program(GLuint vertex_shader, GLuint fragment_shader, char const **binds, int num_binds, char *error, int error_buflen); -// links a shader. returns the linked program on success or 0 on failure. -// -// vertex_shader a compiled vertex shader from stbgl_compile_shader, or 0 for fixed-function (if legal) -// fragment_shader a compiled fragment shader from stbgl_compile_shader, or 0 for fixed-function (if legal) -// - -extern void stbgl_delete_shader(GLuint shader); -// deletes a shader created by stbgl_compile_shader - - -///////////// RENDERING WITH SHADERS - -extern GLint stbgl_find_uniform(GLuint prog, char *uniform); - -extern void stbgl_find_uniforms(GLuint prog, GLint *locations, char const **uniforms, int num_uniforms); -// Given the locations array that is num_uniforms long, fills out -// the locations of each of those uniforms for the specified program. -// If num_uniforms is -1, then uniforms[] must be NULL-terminated - -// the following functions just wrap the difference in naming between GL2+ and ARB, -// so you don't need them unless you're using both ARB and GL2+ in the same codebase, -// or you're relying on this lib to provide the extensions -extern void stbglUseProgram(GLuint program); -extern void stbglVertexAttribPointer(GLuint index, GLint size, GLenum type, GLboolean normalized, GLsizei stride, const GLvoid * pointer); -extern void stbglEnableVertexAttribArray(GLuint index); -extern void stbglDisableVertexAttribArray(GLuint index); -extern void stbglUniform1fv(GLint loc, GLsizei count, const GLfloat *v); -extern void stbglUniform2fv(GLint loc, GLsizei count, const GLfloat *v); -extern void stbglUniform3fv(GLint loc, GLsizei count, const GLfloat *v); -extern void stbglUniform4fv(GLint loc, GLsizei count, const GLfloat *v); -extern void stbglUniform1iv(GLint loc, GLsizei count, const GLint *v); -extern void stbglUniform2iv(GLint loc, GLsizei count, const GLint *v); -extern void stbglUniform3iv(GLint loc, GLsizei count, const GLint *v); -extern void stbglUniform4iv(GLint loc, GLsizei count, const GLint *v); -extern void stbglUniform1f(GLint loc, float v0); -extern void stbglUniform2f(GLint loc, float v0, float v1); -extern void stbglUniform3f(GLint loc, float v0, float v1, float v2); -extern void stbglUniform4f(GLint loc, float v0, float v1, float v2, float v3); -extern void stbglUniform1i(GLint loc, GLint v0); -extern void stbglUniform2i(GLint loc, GLint v0, GLint v1); -extern void stbglUniform3i(GLint loc, GLint v0, GLint v1, GLint v2); -extern void stbglUniform4i(GLint loc, GLint v0, GLint v1, GLint v2, GLint v3); - - -////////////// END OF FUNCTIONS - -////////////////////////////////////////////////////////////////////////////// - -#ifdef __cplusplus -} -#endif -#endif // STB_GLPROG_STATIC - -#ifdef STB_GLPROG_ARB -#define STBGL_VERTEX_SHADER GL_VERTEX_SHADER_ARB -#define STBGL_FRAGMENT_SHADER GL_FRAGMENT_SHADER_ARB -#else -#define STBGL_VERTEX_SHADER GL_VERTEX_SHADER -#define STBGL_FRAGMENT_SHADER GL_FRAGMENT_SHADER -#endif - -#endif // INCLUDE_STB_GLPROG_H - - -///////// header file section ends here - - -#ifdef STB_GLPROG_IMPLEMENTATION -#include // strncpy - -#ifdef STB_GLPROG_STATIC -#define STB_GLPROG_DECLARE static -#else -#define STB_GLPROG_DECLARE extern -#endif - -// check if user wants this file to define the GL extensions itself -#ifdef STB_GLPROG_ARB_DEFINE_EXTENSIONS -#define STB_GLPROG_ARB // make sure later code uses the extensions - -#ifndef STB_GLPROG_SUPPRESS_GLEXT_INCLUDE -#include "glext.h" -#endif - -#define STB_GLPROG_EXTENSIONS \ - STB_GLPROG_FUNC(ATTACHOBJECT , AttachObject ) \ - STB_GLPROG_FUNC(BINDATTRIBLOCATION , BindAttribLocation ) \ - STB_GLPROG_FUNC(COMPILESHADER , CompileShader ) \ - STB_GLPROG_FUNC(CREATEPROGRAMOBJECT , CreateProgramObject ) \ - STB_GLPROG_FUNC(CREATESHADEROBJECT , CreateShaderObject ) \ - STB_GLPROG_FUNC(DELETEOBJECT , DeleteObject ) \ - STB_GLPROG_FUNC(DETACHOBJECT , DetachObject ) \ - STB_GLPROG_FUNC(DISABLEVERTEXATTRIBARRAY, DisableVertexAttribArray) \ - STB_GLPROG_FUNC(ENABLEVERTEXATTRIBARRAY, EnableVertexAttribArray ) \ - STB_GLPROG_FUNC(GETATTACHEDOBJECTS , GetAttachedObjects ) \ - STB_GLPROG_FUNC(GETOBJECTPARAMETERIV, GetObjectParameteriv) \ - STB_GLPROG_FUNC(GETINFOLOG , GetInfoLog ) \ - STB_GLPROG_FUNC(GETUNIFORMLOCATION , GetUniformLocation ) \ - STB_GLPROG_FUNC(LINKPROGRAM , LinkProgram ) \ - STB_GLPROG_FUNC(SHADERSOURCE , ShaderSource ) \ - STB_GLPROG_FUNC(UNIFORM1F , Uniform1f ) \ - STB_GLPROG_FUNC(UNIFORM2F , Uniform2f ) \ - STB_GLPROG_FUNC(UNIFORM3F , Uniform3f ) \ - STB_GLPROG_FUNC(UNIFORM4F , Uniform4f ) \ - STB_GLPROG_FUNC(UNIFORM1I , Uniform1i ) \ - STB_GLPROG_FUNC(UNIFORM2I , Uniform2i ) \ - STB_GLPROG_FUNC(UNIFORM3I , Uniform3i ) \ - STB_GLPROG_FUNC(UNIFORM4I , Uniform4i ) \ - STB_GLPROG_FUNC(UNIFORM1FV , Uniform1fv ) \ - STB_GLPROG_FUNC(UNIFORM2FV , Uniform2fv ) \ - STB_GLPROG_FUNC(UNIFORM3FV , Uniform3fv ) \ - STB_GLPROG_FUNC(UNIFORM4FV , Uniform4fv ) \ - STB_GLPROG_FUNC(UNIFORM1IV , Uniform1iv ) \ - STB_GLPROG_FUNC(UNIFORM2IV , Uniform2iv ) \ - STB_GLPROG_FUNC(UNIFORM3IV , Uniform3iv ) \ - STB_GLPROG_FUNC(UNIFORM4IV , Uniform4iv ) \ - STB_GLPROG_FUNC(USEPROGRAMOBJECT , UseProgramObject ) \ - STB_GLPROG_FUNC(VERTEXATTRIBPOINTER , VertexAttribPointer ) - -// define the static function pointers - -#define STB_GLPROG_FUNC(x,y) static PFNGL##x##ARBPROC gl##y##ARB; -STB_GLPROG_EXTENSIONS -#undef STB_GLPROG_FUNC - -// define the GetProcAddress - -#ifdef _WIN32 -#ifndef WINGDIAPI -#ifndef STB__HAS_WGLPROC -typedef int (__stdcall *stbgl__voidfunc)(void); -static __declspec(dllimport) stbgl__voidfunc wglGetProcAddress(char *); -#endif -#endif -#define STBGL__GET_FUNC(x) wglGetProcAddress(x) -#else -#error "need to define how this platform gets extensions" -#endif - -// create a function that fills out the function pointers - -static void stb_glprog_init(void) -{ - static int initialized = 0; // not thread safe! - if (initialized) return; - #define STB_GLPROG_FUNC(x,y) gl##y##ARB = (PFNGL##x##ARBPROC) STBGL__GET_FUNC("gl" #y "ARB"); - STB_GLPROG_EXTENSIONS - #undef STB_GLPROG_FUNC -} -#undef STB_GLPROG_EXTENSIONS - -#else -static void stb_glprog_init(void) -{ -} -#endif - - -// define generic names for many of the gl functions or extensions for later use; -// note that in some cases there are two functions in core and one function in ARB -#ifdef STB_GLPROG_ARB -#define stbglCreateShader glCreateShaderObjectARB -#define stbglDeleteShader glDeleteObjectARB -#define stbglAttachShader glAttachObjectARB -#define stbglDetachShader glDetachObjectARB -#define stbglShaderSource glShaderSourceARB -#define stbglCompileShader glCompileShaderARB -#define stbglGetShaderStatus(a,b) glGetObjectParameterivARB(a, GL_OBJECT_COMPILE_STATUS_ARB, b) -#define stbglGetShaderInfoLog glGetInfoLogARB -#define stbglCreateProgram glCreateProgramObjectARB -#define stbglDeleteProgram glDeleteObjectARB -#define stbglLinkProgram glLinkProgramARB -#define stbglGetProgramStatus(a,b) glGetObjectParameterivARB(a, GL_OBJECT_LINK_STATUS_ARB, b) -#define stbglGetProgramInfoLog glGetInfoLogARB -#define stbglGetAttachedShaders glGetAttachedObjectsARB -#define stbglBindAttribLocation glBindAttribLocationARB -#define stbglGetUniformLocation glGetUniformLocationARB -#define stbgl_UseProgram glUseProgramObjectARB -#else -#define stbglCreateShader glCreateShader -#define stbglDeleteShader glDeleteShader -#define stbglAttachShader glAttachShader -#define stbglDetachShader glDetachShader -#define stbglShaderSource glShaderSource -#define stbglCompileShader glCompileShader -#define stbglGetShaderStatus(a,b) glGetShaderiv(a, GL_COMPILE_STATUS, b) -#define stbglGetShaderInfoLog glGetShaderInfoLog -#define stbglCreateProgram glCreateProgram -#define stbglDeleteProgram glDeleteProgram -#define stbglLinkProgram glLinkProgram -#define stbglGetProgramStatus(a,b) glGetProgramiv(a, GL_LINK_STATUS, b) -#define stbglGetProgramInfoLog glGetProgramInfoLog -#define stbglGetAttachedShaders glGetAttachedShaders -#define stbglBindAttribLocation glBindAttribLocation -#define stbglGetUniformLocation glGetUniformLocation -#define stbgl_UseProgram glUseProgram -#endif - - -// perform a safe strcat of 3 strings, given that we can't rely on portable snprintf -// if you need to break on error, this is the best place to place a breakpoint -static void stb_glprog_error(char *error, int error_buflen, char *str1, char *str2, char *str3) -{ - int n = strlen(str1); - strncpy(error, str1, error_buflen); - if (n < error_buflen && str2) { - strncpy(error+n, str2, error_buflen - n); - n += strlen(str2); - if (n < error_buflen && str3) { - strncpy(error+n, str3, error_buflen - n); - } - } - error[error_buflen-1] = 0; -} - -STB_GLPROG_DECLARE GLuint stbgl_compile_shader(GLenum type, char const **sources, int num_sources, char *error, int error_buflen) -{ - char *typename = (type == STBGL_VERTEX_SHADER ? "vertex" : "fragment"); - int len; - GLint result; - GLuint shader; - - // initialize the extensions if we haven't already - stb_glprog_init(); - - // allocate - - shader = stbglCreateShader(type); - if (!shader) { - stb_glprog_error(error, error_buflen, "Couldn't allocate shader object in stbgl_compile_shader for ", typename, NULL); - return 0; - } - - // compile - - // if num_sources is negative, assume source is NULL-terminated and count the non-NULL ones - if (num_sources < 0) - for (num_sources = 0; sources[num_sources] != NULL; ++num_sources) - ; - stbglShaderSource(shader, num_sources, sources, NULL); - stbglCompileShader(shader); - stbglGetShaderStatus(shader, &result); - if (result) - return shader; - - // errors - - stb_glprog_error(error, error_buflen, "Compile error for ", typename, " shader: "); - len = strlen(error); - if (len < error_buflen) - stbglGetShaderInfoLog(shader, error_buflen-len, NULL, error+len); - - stbglDeleteShader(shader); - return 0; -} - -STB_GLPROG_DECLARE GLuint stbgl_link_program(GLuint vertex_shader, GLuint fragment_shader, char const **binds, int num_binds, char *error, int error_buflen) -{ - int len; - GLint result; - - // allocate - - GLuint prog = stbglCreateProgram(); - if (!prog) { - stb_glprog_error(error, error_buflen, "Couldn't allocate program object in stbgl_link_program", NULL, NULL); - return 0; - } - - // attach - - if (vertex_shader) - stbglAttachShader(prog, vertex_shader); - if (fragment_shader) - stbglAttachShader(prog, fragment_shader); - - // attribute binds - - if (binds) { - int i; - // if num_binds is negative, then it is NULL terminated - if (num_binds < 0) - for (num_binds=0; binds[num_binds]; ++num_binds) - ; - for (i=0; i < num_binds; ++i) - if (binds[i] && binds[i][0]) // empty binds can be NULL or "" - stbglBindAttribLocation(prog, i, binds[i]); - } - - // link - - stbglLinkProgram(prog); - - // detach - - if (vertex_shader) - stbglDetachShader(prog, vertex_shader); - if (fragment_shader) - stbglDetachShader(prog, fragment_shader); - - // errors - - stbglGetProgramStatus(prog, &result); - if (result) - return prog; - - stb_glprog_error(error, error_buflen, "Link error: ", NULL, NULL); - len = strlen(error); - if (len < error_buflen) - stbglGetProgramInfoLog(prog, error_buflen-len, NULL, error+len); - - stbglDeleteProgram(prog); - return 0; -} - -STB_GLPROG_DECLARE GLuint stbgl_create_program(char const **vertex_source, char const **frag_source, char const **binds, char *error, int error_buflen) -{ - GLuint vertex, fragment, prog=0; - vertex = stbgl_compile_shader(STBGL_VERTEX_SHADER, vertex_source, -1, error, error_buflen); - if (vertex) { - fragment = stbgl_compile_shader(STBGL_FRAGMENT_SHADER, frag_source, -1, error, error_buflen); - if (fragment) - prog = stbgl_link_program(vertex, fragment, binds, -1, error, error_buflen); - if (fragment) - stbglDeleteShader(fragment); - stbglDeleteShader(vertex); - } - return prog; -} - -STB_GLPROG_DECLARE void stbgl_delete_shader(GLuint shader) -{ - stbglDeleteShader(shader); -} - -STB_GLPROG_DECLARE void stgbl_delete_program(GLuint program) -{ - stbglDeleteProgram(program); -} - -GLint stbgl_find_uniform(GLuint prog, char *uniform) -{ - return stbglGetUniformLocation(prog, uniform); -} - -STB_GLPROG_DECLARE void stbgl_find_uniforms(GLuint prog, GLint *locations, char const **uniforms, int num_uniforms) -{ - int i; - if (num_uniforms < 0) - num_uniforms = 999999; - for (i=0; i < num_uniforms && uniforms[i]; ++i) - locations[i] = stbglGetUniformLocation(prog, uniforms[i]); -} - -STB_GLPROG_DECLARE void stbglUseProgram(GLuint program) -{ - stbgl_UseProgram(program); -} - -#ifdef STB_GLPROG_ARB -#define STBGL_ARBIFY(name) name##ARB -#else -#define STBGL_ARBIFY(name) name -#endif - -STB_GLPROG_DECLARE void stbglVertexAttribPointer(GLuint index, GLint size, GLenum type, GLboolean normalized, GLsizei stride, const GLvoid * pointer) -{ - STBGL_ARBIFY(glVertexAttribPointer)(index, size, type, normalized, stride, pointer); -} - -STB_GLPROG_DECLARE void stbglEnableVertexAttribArray (GLuint index) { STBGL_ARBIFY(glEnableVertexAttribArray )(index); } -STB_GLPROG_DECLARE void stbglDisableVertexAttribArray(GLuint index) { STBGL_ARBIFY(glDisableVertexAttribArray)(index); } - -STB_GLPROG_DECLARE void stbglUniform1fv(GLint loc, GLsizei count, const GLfloat *v) { STBGL_ARBIFY(glUniform1fv)(loc,count,v); } -STB_GLPROG_DECLARE void stbglUniform2fv(GLint loc, GLsizei count, const GLfloat *v) { STBGL_ARBIFY(glUniform2fv)(loc,count,v); } -STB_GLPROG_DECLARE void stbglUniform3fv(GLint loc, GLsizei count, const GLfloat *v) { STBGL_ARBIFY(glUniform3fv)(loc,count,v); } -STB_GLPROG_DECLARE void stbglUniform4fv(GLint loc, GLsizei count, const GLfloat *v) { STBGL_ARBIFY(glUniform4fv)(loc,count,v); } - -STB_GLPROG_DECLARE void stbglUniform1iv(GLint loc, GLsizei count, const GLint *v) { STBGL_ARBIFY(glUniform1iv)(loc,count,v); } -STB_GLPROG_DECLARE void stbglUniform2iv(GLint loc, GLsizei count, const GLint *v) { STBGL_ARBIFY(glUniform2iv)(loc,count,v); } -STB_GLPROG_DECLARE void stbglUniform3iv(GLint loc, GLsizei count, const GLint *v) { STBGL_ARBIFY(glUniform3iv)(loc,count,v); } -STB_GLPROG_DECLARE void stbglUniform4iv(GLint loc, GLsizei count, const GLint *v) { STBGL_ARBIFY(glUniform4iv)(loc,count,v); } - -STB_GLPROG_DECLARE void stbglUniform1f(GLint loc, float v0) - { STBGL_ARBIFY(glUniform1f)(loc,v0); } -STB_GLPROG_DECLARE void stbglUniform2f(GLint loc, float v0, float v1) - { STBGL_ARBIFY(glUniform2f)(loc,v0,v1); } -STB_GLPROG_DECLARE void stbglUniform3f(GLint loc, float v0, float v1, float v2) - { STBGL_ARBIFY(glUniform3f)(loc,v0,v1,v2); } -STB_GLPROG_DECLARE void stbglUniform4f(GLint loc, float v0, float v1, float v2, float v3) - { STBGL_ARBIFY(glUniform4f)(loc,v0,v1,v2,v3); } - -STB_GLPROG_DECLARE void stbglUniform1i(GLint loc, GLint v0) - { STBGL_ARBIFY(glUniform1i)(loc,v0); } -STB_GLPROG_DECLARE void stbglUniform2i(GLint loc, GLint v0, GLint v1) - { STBGL_ARBIFY(glUniform2i)(loc,v0,v1); } -STB_GLPROG_DECLARE void stbglUniform3i(GLint loc, GLint v0, GLint v1, GLint v2) - { STBGL_ARBIFY(glUniform3i)(loc,v0,v1,v2); } -STB_GLPROG_DECLARE void stbglUniform4i(GLint loc, GLint v0, GLint v1, GLint v2, GLint v3) - { STBGL_ARBIFY(glUniform4i)(loc,v0,v1,v2,v3); } - -#endif diff --git a/impeller/third_party/stb/stb/tests/caveview/win32/SDL_windows_main.c b/impeller/third_party/stb/stb/tests/caveview/win32/SDL_windows_main.c deleted file mode 100644 index 32e316b55701c..0000000000000 --- a/impeller/third_party/stb/stb/tests/caveview/win32/SDL_windows_main.c +++ /dev/null @@ -1,224 +0,0 @@ -/* - SDL_windows_main.c, placed in the public domain by Sam Lantinga 4/13/98 - - The WinMain function -- calls your program's main() function -*/ -#include "SDL_config.h" - -#ifdef __WIN32__ - -//#include "../../core/windows/SDL_windows.h" - -/* Include this so we define UNICODE properly */ -#if defined(__WIN32__) -#define WIN32_LEAN_AND_MEAN -#define STRICT -#ifndef UNICODE -#define UNICODE 1 -#endif -#undef _WIN32_WINNT -#define _WIN32_WINNT 0x501 /* Need 0x410 for AlphaBlend() and 0x500 for EnumDisplayDevices(), 0x501 for raw input */ -#endif - -#include - -/* Routines to convert from UTF8 to native Windows text */ -#if UNICODE -#define WIN_StringToUTF8(S) SDL_iconv_string("UTF-8", "UTF-16LE", (char *)(S), (SDL_wcslen(S)+1)*sizeof(WCHAR)) -#define WIN_UTF8ToString(S) (WCHAR *)SDL_iconv_string("UTF-16LE", "UTF-8", (char *)(S), SDL_strlen(S)+1) -#else -/* !!! FIXME: UTF8ToString() can just be a SDL_strdup() here. */ -#define WIN_StringToUTF8(S) SDL_iconv_string("UTF-8", "ASCII", (char *)(S), (SDL_strlen(S)+1)) -#define WIN_UTF8ToString(S) SDL_iconv_string("ASCII", "UTF-8", (char *)(S), SDL_strlen(S)+1) -#endif - -/* Sets an error message based on a given HRESULT */ -extern int WIN_SetErrorFromHRESULT(const char *prefix, HRESULT hr); - -/* Sets an error message based on GetLastError(). Always return -1. */ -extern int WIN_SetError(const char *prefix); - -/* Wrap up the oddities of CoInitialize() into a common function. */ -extern HRESULT WIN_CoInitialize(void); -extern void WIN_CoUninitialize(void); - -/* Returns SDL_TRUE if we're running on Windows Vista and newer */ -extern BOOL WIN_IsWindowsVistaOrGreater(); - -#include -#include - -/* Include the SDL main definition header */ -#include "SDL.h" -#include "SDL_main.h" - -#ifdef main -# undef main -#endif /* main */ - -static void -UnEscapeQuotes(char *arg) -{ - char *last = NULL; - - while (*arg) { - if (*arg == '"' && (last != NULL && *last == '\\')) { - char *c_curr = arg; - char *c_last = last; - - while (*c_curr) { - *c_last = *c_curr; - c_last = c_curr; - c_curr++; - } - *c_last = '\0'; - } - last = arg; - arg++; - } -} - -/* Parse a command line buffer into arguments */ -static int -ParseCommandLine(char *cmdline, char **argv) -{ - char *bufp; - char *lastp = NULL; - int argc, last_argc; - - argc = last_argc = 0; - for (bufp = cmdline; *bufp;) { - /* Skip leading whitespace */ - while (SDL_isspace(*bufp)) { - ++bufp; - } - /* Skip over argument */ - if (*bufp == '"') { - ++bufp; - if (*bufp) { - if (argv) { - argv[argc] = bufp; - } - ++argc; - } - /* Skip over word */ - lastp = bufp; - while (*bufp && (*bufp != '"' || *lastp == '\\')) { - lastp = bufp; - ++bufp; - } - } else { - if (*bufp) { - if (argv) { - argv[argc] = bufp; - } - ++argc; - } - /* Skip over word */ - while (*bufp && !SDL_isspace(*bufp)) { - ++bufp; - } - } - if (*bufp) { - if (argv) { - *bufp = '\0'; - } - ++bufp; - } - - /* Strip out \ from \" sequences */ - if (argv && last_argc != argc) { - UnEscapeQuotes(argv[last_argc]); - } - last_argc = argc; - } - if (argv) { - argv[argc] = NULL; - } - return (argc); -} - -/* Show an error message */ -static void -ShowError(const char *title, const char *message) -{ -/* If USE_MESSAGEBOX is defined, you need to link with user32.lib */ -#ifdef USE_MESSAGEBOX - MessageBox(NULL, message, title, MB_ICONEXCLAMATION | MB_OK); -#else - fprintf(stderr, "%s: %s\n", title, message); -#endif -} - -/* Pop up an out of memory message, returns to Windows */ -static BOOL -OutOfMemory(void) -{ - ShowError("Fatal Error", "Out of memory - aborting"); - return FALSE; -} - -#if defined(_MSC_VER) -/* The VC++ compiler needs main defined */ -#define console_main main -#endif - -/* This is where execution begins [console apps] */ -int -console_main(int argc, char *argv[]) -{ - int status; - - SDL_SetMainReady(); - - /* Run the application main() code */ - status = SDL_main(argc, argv); - - /* Exit cleanly, calling atexit() functions */ - exit(status); - - /* Hush little compiler, don't you cry... */ - return 0; -} - -/* This is where execution begins [windowed apps] */ -int WINAPI -WinMain(HINSTANCE hInst, HINSTANCE hPrev, LPSTR szCmdLine, int sw) -{ - char **argv; - int argc; - char *cmdline; - - /* Grab the command line */ - TCHAR *text = GetCommandLine(); -#if UNICODE - cmdline = SDL_iconv_string("UTF-8", "UCS-2-INTERNAL", (char *)(text), (SDL_wcslen(text)+1)*sizeof(WCHAR)); -#else - cmdline = SDL_strdup(text); -#endif - if (cmdline == NULL) { - return OutOfMemory(); - } - - /* Parse it into argv and argc */ - argc = ParseCommandLine(cmdline, NULL); - argv = SDL_stack_alloc(char *, argc + 1); - if (argv == NULL) { - return OutOfMemory(); - } - ParseCommandLine(cmdline, argv); - - /* Run the main program */ - console_main(argc, argv); - - SDL_stack_free(argv); - - SDL_free(cmdline); - - /* Hush little compiler, don't you cry... */ - return 0; -} - -#endif /* __WIN32__ */ - -/* vi: set ts=4 sw=4 expandtab: */ diff --git a/impeller/third_party/stb/stb/tests/herringbone.dsp b/impeller/third_party/stb/stb/tests/herringbone.dsp deleted file mode 100644 index b82fee45aa8ca..0000000000000 --- a/impeller/third_party/stb/stb/tests/herringbone.dsp +++ /dev/null @@ -1,95 +0,0 @@ -# Microsoft Developer Studio Project File - Name="herringbone" - Package Owner=<4> -# Microsoft Developer Studio Generated Build File, Format Version 6.00 -# ** DO NOT EDIT ** - -# TARGTYPE "Win32 (x86) Console Application" 0x0103 - -CFG=herringbone - Win32 Debug -!MESSAGE This is not a valid makefile. To build this project using NMAKE, -!MESSAGE use the Export Makefile command and run -!MESSAGE -!MESSAGE NMAKE /f "herringbone.mak". -!MESSAGE -!MESSAGE You can specify a configuration when running NMAKE -!MESSAGE by defining the macro CFG on the command line. For example: -!MESSAGE -!MESSAGE NMAKE /f "herringbone.mak" CFG="herringbone - Win32 Debug" -!MESSAGE -!MESSAGE Possible choices for configuration are: -!MESSAGE -!MESSAGE "herringbone - Win32 Release" (based on "Win32 (x86) Console Application") -!MESSAGE "herringbone - Win32 Debug" (based on "Win32 (x86) Console Application") -!MESSAGE - -# Begin Project -# PROP AllowPerConfigDependencies 0 -# PROP Scc_ProjName "" -# PROP Scc_LocalPath "" -CPP=cl.exe -RSC=rc.exe - -!IF "$(CFG)" == "herringbone - Win32 Release" - -# PROP BASE Use_MFC 0 -# PROP BASE Use_Debug_Libraries 0 -# PROP BASE Output_Dir "Release" -# PROP BASE Intermediate_Dir "Release" -# PROP BASE Target_Dir "" -# PROP Use_MFC 0 -# PROP Use_Debug_Libraries 0 -# PROP Output_Dir "Release" -# PROP Intermediate_Dir "Release" -# PROP Ignore_Export_Lib 0 -# PROP Target_Dir "" -# ADD BASE CPP /nologo /W3 /GX /O2 /D "WIN32" /D "NDEBUG" /D "_CONSOLE" /D "_MBCS" /YX /FD /c -# ADD CPP /nologo /W3 /GX /O2 /I ".." /D "WIN32" /D "NDEBUG" /D "_CONSOLE" /D "_MBCS" /YX /FD /c -# ADD BASE RSC /l 0x409 /d "NDEBUG" -# ADD RSC /l 0x409 /d "NDEBUG" -BSC32=bscmake.exe -# ADD BASE BSC32 /nologo -# ADD BSC32 /nologo -LINK32=link.exe -# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:console /machine:I386 -# ADD LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:console /machine:I386 - -!ELSEIF "$(CFG)" == "herringbone - Win32 Debug" - -# PROP BASE Use_MFC 0 -# PROP BASE Use_Debug_Libraries 1 -# PROP BASE Output_Dir "herringbone___Win32_Debug" -# PROP BASE Intermediate_Dir "herringbone___Win32_Debug" -# PROP BASE Target_Dir "" -# PROP Use_MFC 0 -# PROP Use_Debug_Libraries 1 -# PROP Output_Dir "Debug" -# PROP Intermediate_Dir "Debug" -# PROP Ignore_Export_Lib 0 -# PROP Target_Dir "" -# ADD BASE CPP /nologo /W3 /Gm /GX /ZI /Od /D "WIN32" /D "_DEBUG" /D "_CONSOLE" /D "_MBCS" /YX /FD /GZ /c -# ADD CPP /nologo /W3 /Gm /GX /ZI /Od /I ".." /D "WIN32" /D "_DEBUG" /D "_CONSOLE" /D "_MBCS" /FD /GZ /c -# SUBTRACT CPP /YX -# ADD BASE RSC /l 0x409 /d "_DEBUG" -# ADD RSC /l 0x409 /d "_DEBUG" -BSC32=bscmake.exe -# ADD BASE BSC32 /nologo -# ADD BSC32 /nologo -LINK32=link.exe -# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:console /debug /machine:I386 /pdbtype:sept -# ADD LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:console /debug /machine:I386 /pdbtype:sept - -!ENDIF - -# Begin Target - -# Name "herringbone - Win32 Release" -# Name "herringbone - Win32 Debug" -# Begin Source File - -SOURCE=.\herringbone_generator.c -# End Source File -# Begin Source File - -SOURCE=..\stb_herringbone_wang_tile.h -# End Source File -# End Target -# End Project diff --git a/impeller/third_party/stb/stb/tests/herringbone_generator.c b/impeller/third_party/stb/stb/tests/herringbone_generator.c deleted file mode 100644 index cf2a99eb98454..0000000000000 --- a/impeller/third_party/stb/stb/tests/herringbone_generator.c +++ /dev/null @@ -1,87 +0,0 @@ -#define STB_HERRINGBONE_WANG_TILE_IMPLEMENTATION -#include "stb_herringbone_wang_tile.h" - -#define STB_IMAGE_WRITE_IMPLEMENTATION -#include "stb_image_write.h" - -// e 12 1 1 1 1 1 1 4 4 - -int main(int argc, char **argv) -{ - stbhw_config c = { 0 }; - int w,h, num_colors,i; - unsigned char *data; - - if (argc == 1) goto usage; - if (argc < 3) goto error; - - switch (argv[2][0]) { - case 'c': - if (argc < 8 || argc > 10) - goto error; - num_colors = 4; - c.is_corner = 1; - break; - - case 'e': - if (argc < 10 || argc > 12) - goto error; - num_colors = 6; - c.is_corner = 0; - break; - - default: - goto error; - } - - c.short_side_len = atoi(argv[3]); - for (i=0; i < num_colors; ++i) - c.num_color[i] = atoi(argv[4+i]); - - c.num_vary_x = 1; - c.num_vary_y = 1; - - if (argc > 4+i) - c.num_vary_x = atoi(argv[4+i]); - if (argc > 5+i) - c.num_vary_y = atoi(argv[5+i]); - - stbhw_get_template_size(&c, &w, &h); - - data = (unsigned char *) malloc(w*h*3); - - if (stbhw_make_template(&c, data, w, h, w*3)) - stbi_write_png(argv[1], w, h, 3, data, w*3); - else - fprintf(stderr, "Error: %s\n", stbhw_get_last_error()); - return 0; - - error: - fputs("Invalid command-line arguments\n\n", stderr); - usage: - fputs("Usage (see source for corner & edge type definitions):\n\n", stderr); - fputs("herringbone_generator {outfile} c {sidelen} {c0} {c1} {c2} {c3} [{vx} {vy}]\n" - " {outfile} -- filename that template will be written to as PNG\n" - " {sidelen} -- length of short side of rectangle in pixels\n" - " {c0} -- number of colors for corner type 0\n" - " {c1} -- number of colors for corner type 1\n" - " {c2} -- number of colors for corner type 2\n" - " {c3} -- number of colors for corner type 3\n" - " {vx} -- number of color-duplicating variations horizontally in template\n" - " {vy} -- number of color-duplicating variations vertically in template\n" - "\n" - , stderr); - fputs("herringbone_generator {outfile} e {sidelen} {e0} {e1} {e2} {e3} {e4} {e5} [{vx} {vy}]\n" - " {outfile} -- filename that template will be written to as PNG\n" - " {sidelen} -- length of short side of rectangle in pixels\n" - " {e0} -- number of colors for edge type 0\n" - " {e1} -- number of colors for edge type 1\n" - " {e2} -- number of colors for edge type 2\n" - " {e3} -- number of colors for edge type 3\n" - " {e4} -- number of colors for edge type 4\n" - " {e5} -- number of colors for edge type 5\n" - " {vx} -- number of color-duplicating variations horizontally in template\n" - " {vy} -- number of color-duplicating variations vertically in template\n" - , stderr); - return 1; -} diff --git a/impeller/third_party/stb/stb/tests/herringbone_map.c b/impeller/third_party/stb/stb/tests/herringbone_map.c deleted file mode 100644 index 22cc013100375..0000000000000 --- a/impeller/third_party/stb/stb/tests/herringbone_map.c +++ /dev/null @@ -1,83 +0,0 @@ -#include - -#define STB_HBWANG_MAX_X 500 -#define STB_HBWANG_MAX_Y 500 - -#define STB_HERRINGBONE_WANG_TILE_IMPLEMENTATION -#include "stb_herringbone_wang_tile.h" - -#define STB_IMAGE_IMPLEMENTATION -#include "stb_image.h" - -#define STB_IMAGE_WRITE_IMPLEMENTATION -#include "stb_image_write.h" - -int main(int argc, char **argv) -{ - if (argc < 5) { - fprintf(stderr, "Usage: herringbone_map {inputfile} {output-width} {output-height} {outputfile}\n"); - return 1; - } else { - char *filename = argv[1]; - int out_w = atoi(argv[2]); - int out_h = atoi(argv[3]); - char *outfile = argv[4]; - - unsigned char *pixels, *out_pixels; - stbhw_tileset ts; - int w,h; - - pixels = stbi_load(filename, &w, &h, 0, 3); - if (pixels == 0) { - fprintf(stderr, "Couldn't open input file '%s'\n", filename); - exit(1); - } - - if (!stbhw_build_tileset_from_image(&ts, pixels, w*3, w, h)) { - fprintf(stderr, "Error: %s\n", stbhw_get_last_error()); - return 1; - } - - free(pixels); - - #ifdef DEBUG_OUTPUT - { - int i,j,k; - // add blue borders to top-left edges of the tiles - int hstride = (ts.short_side_len*2)*3; - int vstride = (ts.short_side_len )*3; - for (i=0; i < ts.num_h_tiles; ++i) { - unsigned char *pix = ts.h_tiles[i]->pixels; - for (j=0; j < ts.short_side_len*2; ++j) - for (k=0; k < 3; ++k) - pix[j*3+k] = (pix[j*3+k]*0.5+100+k*75)/1.5; - for (j=1; j < ts.short_side_len; ++j) - for (k=0; k < 3; ++k) - pix[j*hstride+k] = (pix[j*hstride+k]*0.5+100+k*75)/1.5; - } - for (i=0; i < ts.num_v_tiles; ++i) { - unsigned char *pix = ts.v_tiles[i]->pixels; - for (j=0; j < ts.short_side_len; ++j) - for (k=0; k < 3; ++k) - pix[j*3+k] = (pix[j*3+k]*0.5+100+k*75)/1.5; - for (j=1; j < ts.short_side_len*2; ++j) - for (k=0; k < 3; ++k) - pix[j*vstride+k] = (pix[j*vstride+k]*0.5+100+k*75)/1.5; - } - } - #endif - - out_pixels = malloc(out_w * out_h * 3); - - if (!stbhw_generate_image(&ts, NULL, out_pixels, out_w*3, out_w, out_h)) { - fprintf(stderr, "Error: %s\n", stbhw_get_last_error()); - return 1; - } - - stbi_write_png(argv[4], out_w, out_h, 3, out_pixels, out_w*3); - free(out_pixels); - - stbhw_free_tileset(&ts); - return 0; - } -} \ No newline at end of file diff --git a/impeller/third_party/stb/stb/tests/herringbone_map.dsp b/impeller/third_party/stb/stb/tests/herringbone_map.dsp deleted file mode 100644 index 3e26d6d78cd1b..0000000000000 --- a/impeller/third_party/stb/stb/tests/herringbone_map.dsp +++ /dev/null @@ -1,94 +0,0 @@ -# Microsoft Developer Studio Project File - Name="herringbone_map" - Package Owner=<4> -# Microsoft Developer Studio Generated Build File, Format Version 6.00 -# ** DO NOT EDIT ** - -# TARGTYPE "Win32 (x86) Console Application" 0x0103 - -CFG=herringbone_map - Win32 Debug -!MESSAGE This is not a valid makefile. To build this project using NMAKE, -!MESSAGE use the Export Makefile command and run -!MESSAGE -!MESSAGE NMAKE /f "herringbone_map.mak". -!MESSAGE -!MESSAGE You can specify a configuration when running NMAKE -!MESSAGE by defining the macro CFG on the command line. For example: -!MESSAGE -!MESSAGE NMAKE /f "herringbone_map.mak" CFG="herringbone_map - Win32 Debug" -!MESSAGE -!MESSAGE Possible choices for configuration are: -!MESSAGE -!MESSAGE "herringbone_map - Win32 Release" (based on "Win32 (x86) Console Application") -!MESSAGE "herringbone_map - Win32 Debug" (based on "Win32 (x86) Console Application") -!MESSAGE - -# Begin Project -# PROP AllowPerConfigDependencies 0 -# PROP Scc_ProjName "" -# PROP Scc_LocalPath "" -CPP=cl.exe -RSC=rc.exe - -!IF "$(CFG)" == "herringbone_map - Win32 Release" - -# PROP BASE Use_MFC 0 -# PROP BASE Use_Debug_Libraries 0 -# PROP BASE Output_Dir "Release" -# PROP BASE Intermediate_Dir "Release" -# PROP BASE Target_Dir "" -# PROP Use_MFC 0 -# PROP Use_Debug_Libraries 0 -# PROP Output_Dir "Release" -# PROP Intermediate_Dir "Release" -# PROP Ignore_Export_Lib 0 -# PROP Target_Dir "" -# ADD BASE CPP /nologo /W3 /GX /O2 /D "WIN32" /D "NDEBUG" /D "_CONSOLE" /D "_MBCS" /YX /FD /c -# ADD CPP /nologo /W3 /GX /O2 /I ".." /D "WIN32" /D "NDEBUG" /D "_CONSOLE" /D "_MBCS" /YX /FD /c -# ADD BASE RSC /l 0x409 /d "NDEBUG" -# ADD RSC /l 0x409 /d "NDEBUG" -BSC32=bscmake.exe -# ADD BASE BSC32 /nologo -# ADD BSC32 /nologo -LINK32=link.exe -# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:console /machine:I386 -# ADD LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:console /machine:I386 - -!ELSEIF "$(CFG)" == "herringbone_map - Win32 Debug" - -# PROP BASE Use_MFC 0 -# PROP BASE Use_Debug_Libraries 1 -# PROP BASE Output_Dir "herringbone_map___Win32_Debug" -# PROP BASE Intermediate_Dir "herringbone_map___Win32_Debug" -# PROP BASE Target_Dir "" -# PROP Use_MFC 0 -# PROP Use_Debug_Libraries 1 -# PROP Output_Dir "Debug" -# PROP Intermediate_Dir "Debug" -# PROP Target_Dir "" -# ADD BASE CPP /nologo /W3 /Gm /GX /ZI /Od /D "WIN32" /D "_DEBUG" /D "_CONSOLE" /D "_MBCS" /YX /FD /GZ /c -# ADD CPP /nologo /W3 /Gm /GX /ZI /Od /I ".." /D "WIN32" /D "_DEBUG" /D "_CONSOLE" /D "_MBCS" /FD /GZ /c -# SUBTRACT CPP /YX -# ADD BASE RSC /l 0x409 /d "_DEBUG" -# ADD RSC /l 0x409 /d "_DEBUG" -BSC32=bscmake.exe -# ADD BASE BSC32 /nologo -# ADD BSC32 /nologo -LINK32=link.exe -# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:console /debug /machine:I386 /pdbtype:sept -# ADD LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:console /debug /machine:I386 /pdbtype:sept - -!ENDIF - -# Begin Target - -# Name "herringbone_map - Win32 Release" -# Name "herringbone_map - Win32 Debug" -# Begin Source File - -SOURCE=.\herringbone_map.c -# End Source File -# Begin Source File - -SOURCE=..\stb_herringbone_wang_tile.h -# End Source File -# End Target -# End Project diff --git a/impeller/third_party/stb/stb/tests/image_test.c b/impeller/third_party/stb/stb/tests/image_test.c deleted file mode 100644 index b4497443273bf..0000000000000 --- a/impeller/third_party/stb/stb/tests/image_test.c +++ /dev/null @@ -1,166 +0,0 @@ -#define STB_IMAGE_WRITE_IMPLEMENTATION -#include "stb_image_write.h" - -#define STB_IMAGE_IMPLEMENTATION -#include "stb_image.h" - -#define STB_DEFINE -#include "stb.h" - -//#define PNGSUITE_PRIMARY - -#if 0 -void test_ycbcr(void) -{ - STBI_SIMD_ALIGN(unsigned char, y[256]); - STBI_SIMD_ALIGN(unsigned char, cb[256]); - STBI_SIMD_ALIGN(unsigned char, cr[256]); - STBI_SIMD_ALIGN(unsigned char, out1[256][4]); - STBI_SIMD_ALIGN(unsigned char, out2[256][4]); - - int i,j,k; - int count = 0, bigcount=0, total=0; - - for (i=0; i < 256; ++i) { - for (j=0; j < 256; ++j) { - for (k=0; k < 256; ++k) { - y [k] = k; - cb[k] = j; - cr[k] = i; - } - stbi__YCbCr_to_RGB_row(out1[0], y, cb, cr, 256, 4); - stbi__YCbCr_to_RGB_sse2(out2[0], y, cb, cr, 256, 4); - for (k=0; k < 256; ++k) { - // inaccurate proxy for values outside of RGB cube - if (out1[k][0] == 0 || out1[k][1] == 0 || out1[k][2] == 0 || out1[k][0] == 255 || out1[k][1] == 255 || out1[k][2] == 255) - continue; - ++total; - if (out1[k][0] != out2[k][0] || out1[k][1] != out2[k][1] || out1[k][2] != out2[k][2]) { - int dist1 = abs(out1[k][0] - out2[k][0]); - int dist2 = abs(out1[k][1] - out2[k][1]); - int dist3 = abs(out1[k][2] - out2[k][2]); - ++count; - if (out1[k][1] > out2[k][1]) - ++bigcount; - } - } - } - printf("So far: %d (%d big) of %d\n", count, bigcount, total); - } - printf("Final: %d (%d big) of %d\n", count, bigcount, total); -} -#endif - -float hdr_data[200][200][3]; - -void dummy_write(void *context, void *data, int len) -{ - static char dummy[1024]; - if (len > 1024) len = 1024; - memcpy(dummy, data, len); -} - -int main(int argc, char **argv) -{ - int w,h; - //test_ycbcr(); - - #if 0 - // test hdr asserts - for (h=0; h < 100; h += 2) - for (w=0; w < 200; ++w) - hdr_data[h][w][0] = (float) rand(), - hdr_data[h][w][1] = (float) rand(), - hdr_data[h][w][2] = (float) rand(); - - stbi_write_hdr("output/test.hdr", 200,200,3,hdr_data[0][0]); - #endif - - if (argc > 1) { - int i, n; - - for (i=1; i < argc; ++i) { - int res; - int w2,h2,n2; - unsigned char *data; - printf("%s\n", argv[i]); - res = stbi_info(argv[1], &w2, &h2, &n2); - data = stbi_load(argv[i], &w, &h, &n, 4); if (data) free(data); else printf("Failed &n\n"); - data = stbi_load(argv[i], &w, &h, 0, 1); if (data) free(data); else printf("Failed 1\n"); - data = stbi_load(argv[i], &w, &h, 0, 2); if (data) free(data); else printf("Failed 2\n"); - data = stbi_load(argv[i], &w, &h, 0, 3); if (data) free(data); else printf("Failed 3\n"); - data = stbi_load(argv[i], &w, &h, &n, 4); - assert(data); - assert(w == w2 && h == h2 && n == n2); - assert(res); - if (data) { - char fname[512]; - stb_splitpath(fname, argv[i], STB_FILE); - stbi_write_png(stb_sprintf("output/%s.png", fname), w, h, 4, data, w*4); - stbi_write_bmp(stb_sprintf("output/%s.bmp", fname), w, h, 4, data); - stbi_write_tga(stb_sprintf("output/%s.tga", fname), w, h, 4, data); - stbi_write_png_to_func(dummy_write,0, w, h, 4, data, w*4); - stbi_write_bmp_to_func(dummy_write,0, w, h, 4, data); - stbi_write_tga_to_func(dummy_write,0, w, h, 4, data); - free(data); - } else - printf("FAILED 4\n"); - } - } else { - int i, nope=0; - #ifdef PNGSUITE_PRIMARY - char **files = stb_readdir_files("pngsuite/primary"); - #else - char **files = stb_readdir_files("images"); - #endif - for (i=0; i < stb_arr_len(files); ++i) { - int n; - char **failed = NULL; - unsigned char *data; - printf("."); - //printf("%s\n", files[i]); - data = stbi_load(files[i], &w, &h, &n, 0); if (data) free(data); else stb_arr_push(failed, "&n"); - data = stbi_load(files[i], &w, &h, 0, 1); if (data) free(data); else stb_arr_push(failed, "1"); - data = stbi_load(files[i], &w, &h, 0, 2); if (data) free(data); else stb_arr_push(failed, "2"); - data = stbi_load(files[i], &w, &h, 0, 3); if (data) free(data); else stb_arr_push(failed, "3"); - data = stbi_load(files[i], &w, &h, 0, 4); if (data) ; else stb_arr_push(failed, "4"); - if (data) { - char fname[512]; - - #ifdef PNGSUITE_PRIMARY - int w2,h2; - unsigned char *data2; - stb_splitpath(fname, files[i], STB_FILE_EXT); - data2 = stbi_load(stb_sprintf("pngsuite/primary_check/%s", fname), &w2, &h2, 0, 4); - if (!data2) - printf("FAILED: couldn't load 'pngsuite/primary_check/%s\n", fname); - else { - if (w != w2 || h != w2 || 0 != memcmp(data, data2, w*h*4)) { - int x,y,c; - if (w == w2 && h == h2) - for (y=0; y < h; ++y) - for (x=0; x < w; ++x) - for (c=0; c < 4; ++c) - assert(data[y*w*4+x*4+c] == data2[y*w*4+x*4+c]); - printf("FAILED: %s loaded but didn't match PRIMARY_check 32-bit version\n", files[i]); - } - free(data2); - } - #else - stb_splitpath(fname, files[i], STB_FILE); - stbi_write_png(stb_sprintf("output/%s.png", fname), w, h, 4, data, w*4); - #endif - free(data); - } - if (failed) { - int j; - printf("FAILED: "); - for (j=0; j < stb_arr_len(failed); ++j) - printf("%s ", failed[j]); - printf(" -- %s\n", files[i]); - } - } - printf("Tested %d files.\n", i); - } - return 0; -} diff --git a/impeller/third_party/stb/stb/tests/image_test.dsp b/impeller/third_party/stb/stb/tests/image_test.dsp deleted file mode 100644 index 840ea16dbe893..0000000000000 --- a/impeller/third_party/stb/stb/tests/image_test.dsp +++ /dev/null @@ -1,97 +0,0 @@ -# Microsoft Developer Studio Project File - Name="image_test" - Package Owner=<4> -# Microsoft Developer Studio Generated Build File, Format Version 6.00 -# ** DO NOT EDIT ** - -# TARGTYPE "Win32 (x86) Console Application" 0x0103 - -CFG=image_test - Win32 Debug -!MESSAGE This is not a valid makefile. To build this project using NMAKE, -!MESSAGE use the Export Makefile command and run -!MESSAGE -!MESSAGE NMAKE /f "image_test.mak". -!MESSAGE -!MESSAGE You can specify a configuration when running NMAKE -!MESSAGE by defining the macro CFG on the command line. For example: -!MESSAGE -!MESSAGE NMAKE /f "image_test.mak" CFG="image_test - Win32 Debug" -!MESSAGE -!MESSAGE Possible choices for configuration are: -!MESSAGE -!MESSAGE "image_test - Win32 Release" (based on "Win32 (x86) Console Application") -!MESSAGE "image_test - Win32 Debug" (based on "Win32 (x86) Console Application") -!MESSAGE - -# Begin Project -# PROP AllowPerConfigDependencies 0 -# PROP Scc_ProjName "" -# PROP Scc_LocalPath "" -CPP=cl.exe -RSC=rc.exe - -!IF "$(CFG)" == "image_test - Win32 Release" - -# PROP BASE Use_MFC 0 -# PROP BASE Use_Debug_Libraries 0 -# PROP BASE Output_Dir "Release" -# PROP BASE Intermediate_Dir "Release" -# PROP BASE Target_Dir "" -# PROP Use_MFC 0 -# PROP Use_Debug_Libraries 0 -# PROP Output_Dir "Release" -# PROP Intermediate_Dir "Release" -# PROP Target_Dir "" -# ADD BASE CPP /nologo /W3 /GX /O2 /D "WIN32" /D "NDEBUG" /D "_CONSOLE" /D "_MBCS" /YX /FD /c -# ADD CPP /nologo /W3 /GX /O2 /I ".." /D "WIN32" /D "NDEBUG" /D "_CONSOLE" /D "_MBCS" /YX /FD /c -# ADD BASE RSC /l 0x409 /d "NDEBUG" -# ADD RSC /l 0x409 /d "NDEBUG" -BSC32=bscmake.exe -# ADD BASE BSC32 /nologo -# ADD BSC32 /nologo -LINK32=link.exe -# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:console /machine:I386 -# ADD LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:console /machine:I386 - -!ELSEIF "$(CFG)" == "image_test - Win32 Debug" - -# PROP BASE Use_MFC 0 -# PROP BASE Use_Debug_Libraries 1 -# PROP BASE Output_Dir "Debug" -# PROP BASE Intermediate_Dir "Debug" -# PROP BASE Target_Dir "" -# PROP Use_MFC 0 -# PROP Use_Debug_Libraries 1 -# PROP Output_Dir "Debug" -# PROP Intermediate_Dir "Debug\image_test" -# PROP Target_Dir "" -# ADD BASE CPP /nologo /W3 /Gm /GX /ZI /Od /D "WIN32" /D "_DEBUG" /D "_CONSOLE" /D "_MBCS" /YX /FD /GZ /c -# ADD CPP /nologo /W3 /Gm /GX /ZI /Od /I ".." /D "WIN32" /D "_DEBUG" /D "_CONSOLE" /D "_MBCS" /FD /GZ /c -# SUBTRACT CPP /YX -# ADD BASE RSC /l 0x409 /d "_DEBUG" -# ADD RSC /l 0x409 /d "_DEBUG" -BSC32=bscmake.exe -# ADD BASE BSC32 /nologo -# ADD BSC32 /nologo -LINK32=link.exe -# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:console /debug /machine:I386 /pdbtype:sept -# ADD LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:console /debug /machine:I386 /pdbtype:sept - -!ENDIF - -# Begin Target - -# Name "image_test - Win32 Release" -# Name "image_test - Win32 Debug" -# Begin Source File - -SOURCE=.\image_test.c -# End Source File -# Begin Source File - -SOURCE=..\stb_image.h -# End Source File -# Begin Source File - -SOURCE=..\stb_image_write.h -# End Source File -# End Target -# End Project diff --git a/impeller/third_party/stb/stb/tests/oversample/README.md b/impeller/third_party/stb/stb/tests/oversample/README.md deleted file mode 100644 index cdfdfff5322cb..0000000000000 --- a/impeller/third_party/stb/stb/tests/oversample/README.md +++ /dev/null @@ -1,94 +0,0 @@ -# Font character oversampling for rendering from atlas textures - -TL,DR: Run oversample.exe on a windows machine to see the -benefits of oversampling. It will try to use arial.ttf from the -Windows font directory unless you type the name of a .ttf file as -a command-line argument. - -## Benefits of oversampling - -Oversampling is a mechanism for improving subpixel rendering of characters. - -Improving subpixel has a few benefits: - -* With horizontal-oversampling, text can remain sharper while still being sub-pixel positioned for better kerning -* Horizontally-oversampled text significantly reduces aliasing when text animates horizontally -* Vertically-oversampled text significantly reduces aliasing when text animates vertically -* Text oversampled in both directions significantly reduces aliasing when text rotates - -## What text oversampling is - -A common strategy for rendering text is to cache character bitmaps -and reuse them. For hinted characters, every instance of a given -character is always identical, so this works fine. However, stb_truetype -doesn't do hinting. - -For anti-aliased characters, you can actually position the characters -with subpixel precision, and get different bitmaps based on that positioning -if you re-render the vector data. - -However, if you simply cache a single version of the bitmap and -draw it at different subpixel positions with a GPU, you will get -either the exact same result (if you use point-sampling on the -texture) or linear filtering. Linear filtering will cause a sub-pixel -positioned bitmap to blur further, causing a visible de-sharpening -of the character. (And, since the character wasn't hinted, it was -already blurrier than a hinted one would be, and now it gets even -more blurry.) - -You can avoid this by caching multiple variants of a character which -were rendered independently from the vector data. For example, you -might cache 3 versions of a char, at 0, 1/3, and 2/3rds of a pixel -horizontal offset, and always require characters to fall on integer -positions vertically. - -When creating a texture atlas for use on GPUs, which support bilinear -filtering, there is a better approach than caching several independent -positions, which is to allow lerping between the versions to allow -finer subpixel positioning. You can achieve these by interleaving -each of the cached bitmaps, but this turns out to be mathematically -equivalent to a simpler operation: oversampling and prefiltering the -characters. - -So, setting oversampling of 2x2 in stb_truetype is equivalent to caching -each character in 4 different variations, 1 for each subpixel position -in a 2x2 set. - -An advantage of this formulation is that no changes are required to -the rendering code; the exact same quad-rendering code works, it just -uses different texture coordinates. (Note this does potentially increase -texture bandwidth for text rendering since we end up minifying the texture -without using mipmapping, but you probably are not going to be fill-bound -by your text rendering.) - -## What about gamma? - -Gamma-correction for fonts just doesn't work. This doesn't seem to make -much sense -- it's physically correct, it simulates what we'd see if you -shrunk a font down really far, right? - -But you can play with it in the oversample.exe app. If you turn it on, -white-on-black fonts become too thick (i.e. they become too bright), and -black-on-white fonts become too thin (i.e. they are insufficiently dark). There is -no way to adjust the font's inherent thickness (i.e. by switching to -bold) to fix this for both; making the font thicker will make white -text worse, and making the font thinner will make black text worse. -Obviously you could use different fonts for light and dark cases, but -this doesn't seem like a very good way for fonts to work. - -Multiple people who have experimented with this independently (me, -Fabian Giesen,and Maxim Shemanarev of Anti-Grain Geometry) have all -concluded that correct gamma-correction does not produce the best -results for fonts. Font rendering just generally looks better without -gamma correction (or possibly with some arbitrary power stuck in -there, but it's not really correcting for gamma at that point). Maybe -this is in part a product of how we're used to fonts being on screens -which has changed how we expect them to look (e.g. perhaps hinting -oversharpens them and prevents the real-world thinning you'd see in -a black-on-white text). - -(AGG link on text rendering, including mention of gamma: - http://www.antigrain.com/research/font_rasterization/ ) - -Nevertheless, even if you turn on gamma-correction, you will find that -oversampling still helps in many cases for small fonts. diff --git a/impeller/third_party/stb/stb/tests/oversample/main.c b/impeller/third_party/stb/stb/tests/oversample/main.c deleted file mode 100644 index bc6bd0f34e0c9..0000000000000 --- a/impeller/third_party/stb/stb/tests/oversample/main.c +++ /dev/null @@ -1,332 +0,0 @@ -#pragma warning(disable:4244; disable:4305; disable:4018) -#include -#include - -#define STB_WINMAIN -#include "stb_wingraph.h" - -#define STB_TRUETYPE_IMPLEMENTATION -#define STB_RECT_PACK_IMPLEMENTATION -#include "stb_rect_pack.h" -#include "stb_truetype.h" - -#ifndef WINGDIAPI -#define CALLBACK __stdcall -#define WINGDIAPI __declspec(dllimport) -#define APIENTRY __stdcall -#endif - -#include -#include - -#define GL_FRAMEBUFFER_SRGB_EXT 0x8DB9 - -#define SIZE_X 1024 -#define SIZE_Y 768 - -stbtt_packedchar chardata[6][128]; - -int sx=SIZE_X, sy=SIZE_Y; - -#define BITMAP_W 512 -#define BITMAP_H 512 -unsigned char temp_bitmap[BITMAP_W][BITMAP_H]; -unsigned char ttf_buffer[1 << 25]; -GLuint font_tex; - -float scale[2] = { 24.0f, 14.0f }; - -int sf[6] = { 0,1,2, 0,1,2 }; - -void load_fonts(void) -{ - stbtt_pack_context pc; - int i; - FILE *f; - char filename[256]; - char *win = getenv("windir"); - if (win == NULL) win = getenv("SystemRoot"); - - f = fopen(stb_wingraph_commandline, "rb"); - if (!f) { - if (win == NULL) - sprintf(filename, "arial.ttf", win); - else - sprintf(filename, "%s/fonts/arial.ttf", win); - f = fopen(filename, "rb"); - if (!f) exit(0); - } - - fread(ttf_buffer, 1, 1<<25, f); - - stbtt_PackBegin(&pc, temp_bitmap[0], BITMAP_W, BITMAP_H, 0, 1, NULL); - for (i=0; i < 2; ++i) { - stbtt_PackSetOversampling(&pc, 1, 1); - stbtt_PackFontRange(&pc, ttf_buffer, 0, scale[i], 32, 95, chardata[i*3+0]+32); - stbtt_PackSetOversampling(&pc, 2, 2); - stbtt_PackFontRange(&pc, ttf_buffer, 0, scale[i], 32, 95, chardata[i*3+1]+32); - stbtt_PackSetOversampling(&pc, 3, 1); - stbtt_PackFontRange(&pc, ttf_buffer, 0, scale[i], 32, 95, chardata[i*3+2]+32); - } - stbtt_PackEnd(&pc); - - glGenTextures(1, &font_tex); - glBindTexture(GL_TEXTURE_2D, font_tex); - glTexImage2D(GL_TEXTURE_2D, 0, GL_ALPHA, BITMAP_W, BITMAP_H, 0, GL_ALPHA, GL_UNSIGNED_BYTE, temp_bitmap); - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); -} - -int black_on_white; - -void draw_init(void) -{ - glDisable(GL_CULL_FACE); - glDisable(GL_TEXTURE_2D); - glDisable(GL_LIGHTING); - glDisable(GL_DEPTH_TEST); - - glViewport(0,0,sx,sy); - if (black_on_white) - glClearColor(255,255,255,0); - else - glClearColor(0,0,0,0); - glClear(GL_COLOR_BUFFER_BIT); - - glMatrixMode(GL_PROJECTION); - glLoadIdentity(); - gluOrtho2D(0,sx,sy,0); - glMatrixMode(GL_MODELVIEW); - glLoadIdentity(); -} - - -void drawBoxTC(float x0, float y0, float x1, float y1, float s0, float t0, float s1, float t1) -{ - glTexCoord2f(s0,t0); glVertex2f(x0,y0); - glTexCoord2f(s1,t0); glVertex2f(x1,y0); - glTexCoord2f(s1,t1); glVertex2f(x1,y1); - glTexCoord2f(s0,t1); glVertex2f(x0,y1); -} - -int integer_align; - -void print(float x, float y, int font, char *text) -{ - glEnable(GL_TEXTURE_2D); - glBindTexture(GL_TEXTURE_2D, font_tex); - glBegin(GL_QUADS); - while (*text) { - stbtt_aligned_quad q; - stbtt_GetPackedQuad(chardata[font], BITMAP_W, BITMAP_H, *text++, &x, &y, &q, font ? 0 : integer_align); - drawBoxTC(q.x0,q.y0,q.x1,q.y1, q.s0,q.t0,q.s1,q.t1); - } - glEnd(); -} - -int font=3; -int translating; -int rotating=0; -int srgb=0; -float rotate_t, translate_t; -int show_tex; - -void draw_world(void) -{ - int sfont = sf[font]; - float x = 20; - glEnable(GL_BLEND); - glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); - - if (black_on_white) - glColor3f(0,0,0); - else - glColor3f(1,1,1); - - - print(80, 30, sfont, "Controls:"); - print(100, 60, sfont, "S: toggle font size"); - print(100, 85, sfont, "O: toggle oversampling"); - print(100,110, sfont, "T: toggle translation"); - print(100,135, sfont, "R: toggle rotation"); - print(100,160, sfont, "P: toggle pixel-snap (only non-oversampled)"); - print(100,185, sfont, "G: toggle srgb gamma-correction"); - if (black_on_white) - print(100,210, sfont, "B: toggle to white-on-black"); - else - print(100,210, sfont, "B: toggle to black-on-white"); - print(100,235, sfont, "V: view font texture"); - - print(80, 300, sfont, "Current font:"); - - if (!show_tex) { - if (font < 3) - print(100, 350, sfont, "Font height: 24 pixels"); - else - print(100, 350, sfont, "Font height: 14 pixels"); - } - - if (font%3==1) - print(100, 325, sfont, "2x2 oversampled text at 1:1"); - else if (font%3 == 2) - print(100, 325, sfont, "3x1 oversampled text at 1:1"); - else if (integer_align) - print(100, 325, sfont, "1:1 text, one texel = one pixel, snapped to integer coordinates"); - else - print(100, 325, sfont, "1:1 text, one texel = one pixel"); - - if (show_tex) { - glBegin(GL_QUADS); - drawBoxTC(200,400, 200+BITMAP_W,300+BITMAP_H, 0,0,1,1); - glEnd(); - } else { - glMatrixMode(GL_MODELVIEW); - glTranslatef(200,350,0); - - if (translating) - x += fmod(translate_t*8,30); - - if (rotating) { - glTranslatef(100,150,0); - glRotatef(rotate_t*2,0,0,1); - glTranslatef(-100,-150,0); - } - print(x,100, font, "This is a test"); - print(x,130, font, "Now is the time for all good men to come to the aid of their country."); - print(x,160, font, "The quick brown fox jumps over the lazy dog."); - print(x,190, font, "0123456789"); - } -} - -void draw(void) -{ - draw_init(); - draw_world(); - stbwingraph_SwapBuffers(NULL); -} - -static int initialized=0; -static float last_dt; - -int move[4]; -int raw_mouse_x, raw_mouse_y; - -int loopmode(float dt, int real, int in_client) -{ - float actual_dt = dt; - - if (!initialized) return 0; - - rotate_t += dt; - translate_t += dt; - -// music_sim(); - if (!real) - return 0; - - if (dt > 0.25) dt = 0.25; - if (dt < 0.01) dt = 0.01; - - draw(); - - return 0; -} - -int winproc(void *data, stbwingraph_event *e) -{ - switch (e->type) { - case STBWGE_create: - break; - - case STBWGE_char: - switch(e->key) { - case 27: - stbwingraph_ShowCursor(NULL,1); - return STBWINGRAPH_winproc_exit; - break; - case 'o': case 'O': - font = (font+1) % 3 + (font/3)*3; - break; - case 's': case 'S': - font = (font+3) % 6; - break; - case 't': case 'T': - translating = !translating; - translate_t = 0; - break; - case 'r': case 'R': - rotating = !rotating; - rotate_t = 0; - break; - case 'p': case 'P': - integer_align = !integer_align; - break; - case 'g': case 'G': - srgb = !srgb; - if (srgb) - glEnable(GL_FRAMEBUFFER_SRGB_EXT); - else - glDisable(GL_FRAMEBUFFER_SRGB_EXT); - break; - case 'v': case 'V': - show_tex = !show_tex; - break; - case 'b': case 'B': - black_on_white = !black_on_white; - break; - } - break; - - case STBWGE_mousemove: - raw_mouse_x = e->mx; - raw_mouse_y = e->my; - break; - -#if 0 - case STBWGE_mousewheel: do_mouse(e,0,0); break; - case STBWGE_leftdown: do_mouse(e, 1,0); break; - case STBWGE_leftup: do_mouse(e,-1,0); break; - case STBWGE_rightdown: do_mouse(e,0, 1); break; - case STBWGE_rightup: do_mouse(e,0,-1); break; -#endif - - case STBWGE_keydown: - if (e->key == VK_RIGHT) move[0] = 1; - if (e->key == VK_LEFT) move[1] = 1; - if (e->key == VK_UP) move[2] = 1; - if (e->key == VK_DOWN) move[3] = 1; - break; - case STBWGE_keyup: - if (e->key == VK_RIGHT) move[0] = 0; - if (e->key == VK_LEFT) move[1] = 0; - if (e->key == VK_UP) move[2] = 0; - if (e->key == VK_DOWN) move[3] = 0; - break; - - case STBWGE_size: - sx = e->width; - sy = e->height; - loopmode(0,1,0); - break; - - case STBWGE_draw: - if (initialized) - loopmode(0,1,0); - break; - - default: - return STBWINGRAPH_unprocessed; - } - return 0; -} - -void stbwingraph_main(void) -{ - stbwingraph_Priority(2); - stbwingraph_CreateWindow(1, winproc, NULL, "tt", SIZE_X,SIZE_Y, 0, 1, 0, 0); - stbwingraph_ShowCursor(NULL, 0); - load_fonts(); - initialized = 1; - stbwingraph_MainLoop(loopmode, 0.016f); // 30 fps = 0.33 -} - diff --git a/impeller/third_party/stb/stb/tests/oversample/oversample.dsp b/impeller/third_party/stb/stb/tests/oversample/oversample.dsp deleted file mode 100644 index cc1edc3f5f19c..0000000000000 --- a/impeller/third_party/stb/stb/tests/oversample/oversample.dsp +++ /dev/null @@ -1,97 +0,0 @@ -# Microsoft Developer Studio Project File - Name="oversample" - Package Owner=<4> -# Microsoft Developer Studio Generated Build File, Format Version 6.00 -# ** DO NOT EDIT ** - -# TARGTYPE "Win32 (x86) Application" 0x0101 - -CFG=oversample - Win32 Debug -!MESSAGE This is not a valid makefile. To build this project using NMAKE, -!MESSAGE use the Export Makefile command and run -!MESSAGE -!MESSAGE NMAKE /f "oversample.mak". -!MESSAGE -!MESSAGE You can specify a configuration when running NMAKE -!MESSAGE by defining the macro CFG on the command line. For example: -!MESSAGE -!MESSAGE NMAKE /f "oversample.mak" CFG="oversample - Win32 Debug" -!MESSAGE -!MESSAGE Possible choices for configuration are: -!MESSAGE -!MESSAGE "oversample - Win32 Release" (based on "Win32 (x86) Application") -!MESSAGE "oversample - Win32 Debug" (based on "Win32 (x86) Application") -!MESSAGE - -# Begin Project -# PROP AllowPerConfigDependencies 0 -# PROP Scc_ProjName "" -# PROP Scc_LocalPath "" -CPP=cl.exe -MTL=midl.exe -RSC=rc.exe - -!IF "$(CFG)" == "oversample - Win32 Release" - -# PROP BASE Use_MFC 0 -# PROP BASE Use_Debug_Libraries 0 -# PROP BASE Output_Dir "Release" -# PROP BASE Intermediate_Dir "Release" -# PROP BASE Target_Dir "" -# PROP Use_MFC 0 -# PROP Use_Debug_Libraries 0 -# PROP Output_Dir "Release" -# PROP Intermediate_Dir "Release" -# PROP Ignore_Export_Lib 0 -# PROP Target_Dir "" -# ADD BASE CPP /nologo /W3 /GX /O2 /D "WIN32" /D "NDEBUG" /D "_WINDOWS" /D "_MBCS" /YX /FD /c -# ADD CPP /nologo /W3 /WX /GX /O2 /I "c:\sean\prj\stb" /D "WIN32" /D "NDEBUG" /D "_WINDOWS" /D "_MBCS" /FD /c -# SUBTRACT CPP /YX -# ADD BASE MTL /nologo /D "NDEBUG" /mktyplib203 /win32 -# ADD MTL /nologo /D "NDEBUG" /mktyplib203 /win32 -# ADD BASE RSC /l 0x409 /d "NDEBUG" -# ADD RSC /l 0x409 /d "NDEBUG" -BSC32=bscmake.exe -# ADD BASE BSC32 /nologo -# ADD BSC32 /nologo -LINK32=link.exe -# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:windows /machine:I386 -# ADD LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:windows /machine:I386 -# SUBTRACT LINK32 /map /debug - -!ELSEIF "$(CFG)" == "oversample - Win32 Debug" - -# PROP BASE Use_MFC 0 -# PROP BASE Use_Debug_Libraries 1 -# PROP BASE Output_Dir "Debug" -# PROP BASE Intermediate_Dir "Debug" -# PROP BASE Target_Dir "" -# PROP Use_MFC 0 -# PROP Use_Debug_Libraries 1 -# PROP Output_Dir "Debug" -# PROP Intermediate_Dir "Debug" -# PROP Ignore_Export_Lib 0 -# PROP Target_Dir "" -# ADD BASE CPP /nologo /W3 /Gm /GX /ZI /Od /D "WIN32" /D "_DEBUG" /D "_WINDOWS" /D "_MBCS" /YX /FD /GZ /c -# ADD CPP /nologo /W3 /WX /Gm /GX /Zi /Od /I "c:\sean\prj\stb" /D "WIN32" /D "_DEBUG" /D "_WINDOWS" /D "_MBCS" /FD /GZ /c -# ADD BASE MTL /nologo /D "_DEBUG" /mktyplib203 /win32 -# ADD MTL /nologo /D "_DEBUG" /mktyplib203 /win32 -# ADD BASE RSC /l 0x409 /d "_DEBUG" -# ADD RSC /l 0x409 /d "_DEBUG" -BSC32=bscmake.exe -# ADD BASE BSC32 /nologo -# ADD BSC32 /nologo -LINK32=link.exe -# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:windows /debug /machine:I386 /pdbtype:sept -# ADD LINK32 kernel32.lib user32.lib gdi32.lib advapi32.lib winspool.lib comdlg32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:windows /incremental:no /debug /machine:I386 /pdbtype:sept - -!ENDIF - -# Begin Target - -# Name "oversample - Win32 Release" -# Name "oversample - Win32 Debug" -# Begin Source File - -SOURCE=.\main.c -# End Source File -# End Target -# End Project diff --git a/impeller/third_party/stb/stb/tests/oversample/oversample.dsw b/impeller/third_party/stb/stb/tests/oversample/oversample.dsw deleted file mode 100644 index 0f5aa7fae61ff..0000000000000 --- a/impeller/third_party/stb/stb/tests/oversample/oversample.dsw +++ /dev/null @@ -1,29 +0,0 @@ -Microsoft Developer Studio Workspace File, Format Version 6.00 -# WARNING: DO NOT EDIT OR DELETE THIS WORKSPACE FILE! - -############################################################################### - -Project: "oversample"=.\oversample.dsp - Package Owner=<4> - -Package=<5> -{{{ -}}} - -Package=<4> -{{{ -}}} - -############################################################################### - -Global: - -Package=<5> -{{{ -}}} - -Package=<3> -{{{ -}}} - -############################################################################### - diff --git a/impeller/third_party/stb/stb/tests/oversample/oversample.exe b/impeller/third_party/stb/stb/tests/oversample/oversample.exe deleted file mode 100644 index 004069318671fd3a7d13d393cd70a9c7b77a2207..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 54272 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 fYY$1 -#endif -#include -#include -#include -#include -#include -#endif - -typedef void * stbwingraph_hwnd; -typedef void * stbwingraph_hinstance; - -enum -{ - STBWINGRAPH_unprocessed = -(1 << 24), - STBWINGRAPH_do_not_show, - STBWINGRAPH_winproc_exit, - STBWINGRAPH_winproc_update, - STBWINGRAPH_update_exit, - STBWINGRAPH_update_pause, -}; - -typedef enum -{ - STBWGE__none=0, - - STBWGE_create, - STBWGE_create_postshow, - STBWGE_draw, - STBWGE_destroy, - STBWGE_char, - STBWGE_keydown, - STBWGE_syskeydown, - STBWGE_keyup, - STBWGE_syskeyup, - STBWGE_deactivate, - STBWGE_activate, - STBWGE_size, - - STBWGE_mousemove , - STBWGE_leftdown , STBWGE_leftup , - STBWGE_middledown, STBWGE_middleup, - STBWGE_rightdown , STBWGE_rightup , - STBWGE_mousewheel, -} stbwingraph_event_type; - -typedef struct -{ - stbwingraph_event_type type; - - // for input events (mouse, keyboard) - int mx,my; // mouse x & y - int dx,dy; - int shift, ctrl, alt; - - // for keyboard events - int key; - - // for STBWGE_size: - int width, height; - - // for STBWGE_crate - int did_share_lists; // if true, wglShareLists succeeded - - void *handle; - -} stbwingraph_event; - -typedef int (*stbwingraph_window_proc)(void *data, stbwingraph_event *event); - -extern stbwingraph_hinstance stbwingraph_app; -extern stbwingraph_hwnd stbwingraph_primary_window; -extern int stbwingraph_request_fullscreen; -extern int stbwingraph_request_windowed; - -STB_EXTERN void stbwingraph_ods(char *str, ...); -STB_EXTERN int stbwingraph_MessageBox(stbwingraph_hwnd win, unsigned int type, - char *caption, char *text, ...); -STB_EXTERN int stbwingraph_ChangeResolution(unsigned int w, unsigned int h, - unsigned int bits, int use_message_box); -STB_EXTERN int stbwingraph_SetPixelFormat(stbwingraph_hwnd win, int color_bits, - int alpha_bits, int depth_bits, int stencil_bits, int accum_bits); -STB_EXTERN int stbwingraph_DefineClass(void *hinstance, char *iconname); -STB_EXTERN void stbwingraph_SwapBuffers(void *win); -STB_EXTERN void stbwingraph_Priority(int n); - -STB_EXTERN void stbwingraph_MakeFonts(void *window, int font_base); -STB_EXTERN void stbwingraph_ShowWindow(void *window); -STB_EXTERN void *stbwingraph_CreateWindow(int primary, stbwingraph_window_proc func, void *data, char *text, int width, int height, int fullscreen, int resizeable, int dest_alpha, int stencil); -STB_EXTERN void *stbwingraph_CreateWindowSimple(stbwingraph_window_proc func, int width, int height); -STB_EXTERN void *stbwingraph_CreateWindowSimpleFull(stbwingraph_window_proc func, int fullscreen, int ww, int wh, int fw, int fh); -STB_EXTERN void stbwingraph_DestroyWindow(void *window); -STB_EXTERN void stbwingraph_ShowCursor(void *window, int visible); -STB_EXTERN float stbwingraph_GetTimestep(float minimum_time); -STB_EXTERN void stbwingraph_SetGLWindow(void *win); -typedef int (*stbwingraph_update)(float timestep, int real, int in_client); -STB_EXTERN int stbwingraph_MainLoop(stbwingraph_update func, float mintime); - -#ifdef STB_DEFINE -stbwingraph_hinstance stbwingraph_app; -stbwingraph_hwnd stbwingraph_primary_window; -int stbwingraph_request_fullscreen; -int stbwingraph_request_windowed; - -void stbwingraph_ods(char *str, ...) -{ - char buffer[1024]; - va_list v; - va_start(v,str); - vsprintf(buffer, str, v); - va_end(v); - OutputDebugString(buffer); -} - -int stbwingraph_MessageBox(stbwingraph_hwnd win, unsigned int type, char *caption, char *text, ...) -{ - va_list v; - char buffer[1024]; - va_start(v, text); - vsprintf(buffer, text, v); - va_end(v); - return MessageBox(win, buffer, caption, type); -} - -void stbwingraph_Priority(int n) -{ - int p; - switch (n) { - case -1: p = THREAD_PRIORITY_BELOW_NORMAL; break; - case 0: p = THREAD_PRIORITY_NORMAL; break; - case 1: p = THREAD_PRIORITY_ABOVE_NORMAL; break; - default: - if (n < 0) p = THREAD_PRIORITY_LOWEST; - else p = THREAD_PRIORITY_HIGHEST; - } - SetThreadPriority(GetCurrentThread(), p); -} - -static void stbwingraph_ResetResolution(void) -{ - ChangeDisplaySettings(NULL, 0); -} - -static void stbwingraph_RegisterResetResolution(void) -{ - static int done=0; - if (!done) { - done = 1; - atexit(stbwingraph_ResetResolution); - } -} - -int stbwingraph_ChangeResolution(unsigned int w, unsigned int h, unsigned int bits, int use_message_box) -{ - DEVMODE mode; - int res; - - int i, tries=0; - for (i=0; ; ++i) { - int success = EnumDisplaySettings(NULL, i, &mode); - if (!success) break; - if (mode.dmBitsPerPel == bits && mode.dmPelsWidth == w && mode.dmPelsHeight == h) { - ++tries; - success = ChangeDisplaySettings(&mode, CDS_FULLSCREEN); - if (success == DISP_CHANGE_SUCCESSFUL) { - stbwingraph_RegisterResetResolution(); - return TRUE; - } - break; - } - } - - if (!tries) { - if (use_message_box) - stbwingraph_MessageBox(stbwingraph_primary_window, MB_ICONERROR, NULL, "The resolution %d x %d x %d-bits is not supported.", w, h, bits); - return FALSE; - } - - // we tried but failed, so try explicitly doing it without specifying refresh rate - - // Win95 support logic - mode.dmBitsPerPel = bits; - mode.dmPelsWidth = w; - mode.dmPelsHeight = h; - mode.dmFields = DM_BITSPERPEL | DM_PELSWIDTH | DM_PELSHEIGHT; - - res = ChangeDisplaySettings(&mode, CDS_FULLSCREEN); - - switch (res) { - case DISP_CHANGE_SUCCESSFUL: - stbwingraph_RegisterResetResolution(); - return TRUE; - - case DISP_CHANGE_RESTART: - if (use_message_box) - stbwingraph_MessageBox(stbwingraph_primary_window, MB_ICONERROR, NULL, "Please set your desktop to %d-bit color and then try again."); - return FALSE; - - case DISP_CHANGE_FAILED: - if (use_message_box) - stbwingraph_MessageBox(stbwingraph_primary_window, MB_ICONERROR, NULL, "The hardware failed to change modes."); - return FALSE; - - case DISP_CHANGE_BADMODE: - if (use_message_box) - stbwingraph_MessageBox(stbwingraph_primary_window, MB_ICONERROR, NULL, "The resolution %d x %d x %d-bits is not supported.", w, h, bits); - return FALSE; - - default: - if (use_message_box) - stbwingraph_MessageBox(stbwingraph_primary_window, MB_ICONERROR, NULL, "An unknown error prevented a change to a %d x %d x %d-bit display.", w, h, bits); - return FALSE; - } -} - -int stbwingraph_SetPixelFormat(stbwingraph_hwnd win, int color_bits, int alpha_bits, int depth_bits, int stencil_bits, int accum_bits) -{ - HDC dc = GetDC(win); - PIXELFORMATDESCRIPTOR pfd = { sizeof(pfd) }; - int pixel_format; - - pfd.nVersion = 1; - pfd.dwFlags = PFD_SUPPORT_OPENGL | PFD_DRAW_TO_WINDOW | PFD_DOUBLEBUFFER; - pfd.dwLayerMask = PFD_MAIN_PLANE; - pfd.iPixelType = PFD_TYPE_RGBA; - pfd.cColorBits = color_bits; - pfd.cAlphaBits = alpha_bits; - pfd.cDepthBits = depth_bits; - pfd.cStencilBits = stencil_bits; - pfd.cAccumBits = accum_bits; - - pixel_format = ChoosePixelFormat(dc, &pfd); - if (!pixel_format) return FALSE; - - if (!DescribePixelFormat(dc, pixel_format, sizeof(PIXELFORMATDESCRIPTOR), &pfd)) - return FALSE; - SetPixelFormat(dc, pixel_format, &pfd); - - return TRUE; -} - -typedef struct -{ - // app data - stbwingraph_window_proc func; - void *data; - // creation parameters - int color, alpha, depth, stencil, accum; - HWND share_window; - HWND window; - // internal data - HGLRC rc; - HDC dc; - int hide_mouse; - int in_client; - int active; - int did_share_lists; - int mx,my; // last mouse positions -} stbwingraph__window; - -static void stbwingraph__inclient(stbwingraph__window *win, int state) -{ - if (state != win->in_client) { - win->in_client = state; - if (win->hide_mouse) - ShowCursor(!state); - } -} - -static void stbwingraph__key(stbwingraph_event *e, int type, int key, stbwingraph__window *z) -{ - e->type = type; - e->key = key; - e->shift = (GetKeyState(VK_SHIFT) < 0); - e->ctrl = (GetKeyState(VK_CONTROL) < 0); - e->alt = (GetKeyState(VK_MENU) < 0); - if (z) { - e->mx = z->mx; - e->my = z->my; - } else { - e->mx = e->my = 0; - } - e->dx = e->dy = 0; -} - -static void stbwingraph__mouse(stbwingraph_event *e, int type, WPARAM wparam, LPARAM lparam, int capture, void *wnd, stbwingraph__window *z) -{ - static int captured = 0; - e->type = type; - e->mx = (short) LOWORD(lparam); - e->my = (short) HIWORD(lparam); - if (!z || z->mx == -(1 << 30)) { - e->dx = e->dy = 0; - } else { - e->dx = e->mx - z->mx; - e->dy = e->my - z->my; - } - e->shift = (wparam & MK_SHIFT) != 0; - e->ctrl = (wparam & MK_CONTROL) != 0; - e->alt = (wparam & MK_ALT) != 0; - if (z) { - z->mx = e->mx; - z->my = e->my; - } - if (capture) { - if (!captured && capture == 1) - SetCapture(wnd); - captured += capture; - if (!captured && capture == -1) - ReleaseCapture(); - if (captured < 0) captured = 0; - } -} - -static void stbwingraph__mousewheel(stbwingraph_event *e, int type, WPARAM wparam, LPARAM lparam, int capture, void *wnd, stbwingraph__window *z) -{ - // lparam seems bogus! - static int captured = 0; - e->type = type; - if (z) { - e->mx = z->mx; - e->my = z->my; - } - e->dx = e->dy = 0; - e->shift = (wparam & MK_SHIFT) != 0; - e->ctrl = (wparam & MK_CONTROL) != 0; - e->alt = (GetKeyState(VK_MENU) < 0); - e->key = ((int) wparam >> 16); -} - -int stbwingraph_force_update; -static int WINAPI stbwingraph_WinProc(HWND wnd, UINT msg, WPARAM wparam, LPARAM lparam) -{ - int allow_default = TRUE; - stbwingraph_event e = { STBWGE__none }; - // the following line is wrong for 64-bit windows, but VC6 doesn't have GetWindowLongPtr - stbwingraph__window *z = (stbwingraph__window *) GetWindowLong(wnd, GWL_USERDATA); - - switch (msg) { - - case WM_CREATE: - { - LPCREATESTRUCT lpcs = (LPCREATESTRUCT) lparam; - assert(z == NULL); - z = (stbwingraph__window *) lpcs->lpCreateParams; - SetWindowLong(wnd, GWL_USERDATA, (LONG) z); - z->dc = GetDC(wnd); - if (stbwingraph_SetPixelFormat(wnd, z->color, z->alpha, z->depth, z->stencil, z->accum)) { - z->rc = wglCreateContext(z->dc); - if (z->rc) { - e.type = STBWGE_create; - z->did_share_lists = FALSE; - if (z->share_window) { - stbwingraph__window *y = (stbwingraph__window *) GetWindowLong(z->share_window, GWL_USERDATA); - if (wglShareLists(z->rc, y->rc)) - z->did_share_lists = TRUE; - } - wglMakeCurrent(z->dc, z->rc); - return 0; - } - } - return -1; - } - - case WM_PAINT: { - PAINTSTRUCT ps; - HDC hdc = BeginPaint(wnd, &ps); - SelectObject(hdc, GetStockObject(NULL_BRUSH)); - e.type = STBWGE_draw; - e.handle = wnd; - z->func(z->data, &e); - EndPaint(wnd, &ps); - return 0; - } - - case WM_DESTROY: - e.type = STBWGE_destroy; - e.handle = wnd; - if (z && z->func) - z->func(z->data, &e); - wglMakeCurrent(NULL, NULL) ; - if (z) { - if (z->rc) wglDeleteContext(z->rc); - z->dc = 0; - z->rc = 0; - } - if (wnd == stbwingraph_primary_window) - PostQuitMessage (0); - return 0; - - case WM_CHAR: stbwingraph__key(&e, STBWGE_char , wparam, z); break; - case WM_KEYDOWN: stbwingraph__key(&e, STBWGE_keydown, wparam, z); break; - case WM_KEYUP: stbwingraph__key(&e, STBWGE_keyup , wparam, z); break; - - case WM_NCMOUSEMOVE: stbwingraph__inclient(z,0); break; - case WM_MOUSEMOVE: stbwingraph__inclient(z,1); stbwingraph__mouse(&e, STBWGE_mousemove, wparam, lparam,0,wnd, z); break; - case WM_LBUTTONDOWN: stbwingraph__mouse(&e, STBWGE_leftdown, wparam, lparam,1,wnd, z); break; - case WM_MBUTTONDOWN: stbwingraph__mouse(&e, STBWGE_middledown, wparam, lparam,1,wnd, z); break; - case WM_RBUTTONDOWN: stbwingraph__mouse(&e, STBWGE_rightdown, wparam, lparam,1,wnd, z); break; - case WM_LBUTTONUP: stbwingraph__mouse(&e, STBWGE_leftup, wparam, lparam,-1,wnd, z); break; - case WM_MBUTTONUP: stbwingraph__mouse(&e, STBWGE_middleup, wparam, lparam,-1,wnd, z); break; - case WM_RBUTTONUP: stbwingraph__mouse(&e, STBWGE_rightup, wparam, lparam,-1,wnd, z); break; - case WM_MOUSEWHEEL: stbwingraph__mousewheel(&e, STBWGE_mousewheel, wparam, lparam,0,wnd, z); break; - - case WM_ACTIVATE: - allow_default = FALSE; - if (LOWORD(wparam)==WA_INACTIVE ) { - wglMakeCurrent(z->dc, NULL); - e.type = STBWGE_deactivate; - z->active = FALSE; - } else { - wglMakeCurrent(z->dc, z->rc); - e.type = STBWGE_activate; - z->active = TRUE; - } - e.handle = wnd; - z->func(z->data, &e); - return 0; - - case WM_SIZE: { - RECT rect; - allow_default = FALSE; - GetClientRect(wnd, &rect); - e.type = STBWGE_size; - e.width = rect.right; - e.height = rect.bottom; - e.handle = wnd; - z->func(z->data, &e); - return 0; - } - - default: - return DefWindowProc (wnd, msg, wparam, lparam); - } - - if (e.type != STBWGE__none) { - int n; - e.handle = wnd; - n = z->func(z->data, &e); - if (n == STBWINGRAPH_winproc_exit) { - PostQuitMessage(0); - n = 0; - } - if (n == STBWINGRAPH_winproc_update) { - stbwingraph_force_update = TRUE; - return 1; - } - if (n != STBWINGRAPH_unprocessed) - return n; - } - return DefWindowProc (wnd, msg, wparam, lparam); -} - -int stbwingraph_DefineClass(HINSTANCE hInstance, char *iconname) -{ - WNDCLASSEX wndclass; - - stbwingraph_app = hInstance; - - wndclass.cbSize = sizeof(wndclass); - wndclass.style = CS_OWNDC; - wndclass.lpfnWndProc = (WNDPROC) stbwingraph_WinProc; - wndclass.cbClsExtra = 0; - wndclass.cbWndExtra = 0; - wndclass.hInstance = hInstance; - wndclass.hIcon = LoadIcon(hInstance, iconname); - wndclass.hCursor = LoadCursor(NULL,IDC_ARROW); - wndclass.hbrBackground = GetStockObject(NULL_BRUSH); - wndclass.lpszMenuName = "zwingraph"; - wndclass.lpszClassName = "zwingraph"; - wndclass.hIconSm = NULL; - - if (!RegisterClassEx(&wndclass)) - return FALSE; - return TRUE; -} - -void stbwingraph_ShowWindow(void *window) -{ - stbwingraph_event e = { STBWGE_create_postshow }; - stbwingraph__window *z = (stbwingraph__window *) GetWindowLong(window, GWL_USERDATA); - ShowWindow(window, SW_SHOWNORMAL); - InvalidateRect(window, NULL, TRUE); - UpdateWindow(window); - e.handle = window; - z->func(z->data, &e); -} - -void *stbwingraph_CreateWindow(int primary, stbwingraph_window_proc func, void *data, char *text, - int width, int height, int fullscreen, int resizeable, int dest_alpha, int stencil) -{ - HWND win; - DWORD dwstyle; - stbwingraph__window *z = (stbwingraph__window *) malloc(sizeof(*z)); - - if (z == NULL) return NULL; - memset(z, 0, sizeof(*z)); - z->color = 24; - z->depth = 24; - z->alpha = dest_alpha; - z->stencil = stencil; - z->func = func; - z->data = data; - z->mx = -(1 << 30); - z->my = 0; - - if (primary) { - if (stbwingraph_request_windowed) - fullscreen = FALSE; - else if (stbwingraph_request_fullscreen) - fullscreen = TRUE; - } - - if (fullscreen) { - #ifdef STB_SIMPLE - stbwingraph_ChangeResolution(width, height, 32, 1); - #else - if (!stbwingraph_ChangeResolution(width, height, 32, 0)) - return NULL; - #endif - dwstyle = WS_POPUP | WS_CLIPSIBLINGS; - } else { - RECT rect; - dwstyle = WS_OVERLAPPED | WS_CAPTION | WS_SYSMENU | WS_MINIMIZEBOX; - if (resizeable) - dwstyle |= WS_SIZEBOX | WS_MAXIMIZEBOX; - rect.top = 0; - rect.left = 0; - rect.right = width; - rect.bottom = height; - AdjustWindowRect(&rect, dwstyle, FALSE); - width = rect.right - rect.left; - height = rect.bottom - rect.top; - } - - win = CreateWindow("zwingraph", text ? text : "sample", dwstyle, - CW_USEDEFAULT,0, width, height, - NULL, NULL, stbwingraph_app, z); - - if (win == NULL) return win; - - if (primary) { - if (stbwingraph_primary_window) - stbwingraph_DestroyWindow(stbwingraph_primary_window); - stbwingraph_primary_window = win; - } - - { - stbwingraph_event e = { STBWGE_create }; - stbwingraph__window *z = (stbwingraph__window *) GetWindowLong(win, GWL_USERDATA); - z->window = win; - e.did_share_lists = z->did_share_lists; - e.handle = win; - if (z->func(z->data, &e) != STBWINGRAPH_do_not_show) - stbwingraph_ShowWindow(win); - } - - return win; -} - -void *stbwingraph_CreateWindowSimple(stbwingraph_window_proc func, int width, int height) -{ - int fullscreen = 0; - #ifndef _DEBUG - if (width == 640 && height == 480) fullscreen = 1; - if (width == 800 && height == 600) fullscreen = 1; - if (width == 1024 && height == 768) fullscreen = 1; - if (width == 1280 && height == 1024) fullscreen = 1; - if (width == 1600 && height == 1200) fullscreen = 1; - //@TODO: widescreen widths - #endif - return stbwingraph_CreateWindow(1, func, NULL, NULL, width, height, fullscreen, 1, 0, 0); -} - -void *stbwingraph_CreateWindowSimpleFull(stbwingraph_window_proc func, int fullscreen, int ww, int wh, int fw, int fh) -{ - if (fullscreen == -1) { - #ifdef _DEBUG - fullscreen = 0; - #else - fullscreen = 1; - #endif - } - - if (fullscreen) { - if (fw) ww = fw; - if (fh) wh = fh; - } - return stbwingraph_CreateWindow(1, func, NULL, NULL, ww, wh, fullscreen, 1, 0, 0); -} - -void stbwingraph_DestroyWindow(void *window) -{ - stbwingraph__window *z = (stbwingraph__window *) GetWindowLong(window, GWL_USERDATA); - DestroyWindow(window); - free(z); - if (stbwingraph_primary_window == window) - stbwingraph_primary_window = NULL; -} - -void stbwingraph_ShowCursor(void *window, int visible) -{ - int hide; - stbwingraph__window *win; - if (!window) - window = stbwingraph_primary_window; - win = (stbwingraph__window *) GetWindowLong((HWND) window, GWL_USERDATA); - hide = !visible; - if (hide != win->hide_mouse) { - win->hide_mouse = hide; - if (!hide) - ShowCursor(TRUE); - else if (win->in_client) - ShowCursor(FALSE); - } -} - -float stbwingraph_GetTimestep(float minimum_time) -{ - float elapsedTime; - double thisTime; - static double lastTime = -1; - - if (lastTime == -1) - lastTime = timeGetTime() / 1000.0 - minimum_time; - - for(;;) { - thisTime = timeGetTime() / 1000.0; - elapsedTime = (float) (thisTime - lastTime); - if (elapsedTime >= minimum_time) { - lastTime = thisTime; - return elapsedTime; - } - #if 1 - Sleep(2); - #endif - } -} - -void stbwingraph_SetGLWindow(void *win) -{ - stbwingraph__window *z = (stbwingraph__window *) GetWindowLong(win, GWL_USERDATA); - if (z) - wglMakeCurrent(z->dc, z->rc); -} - -void stbwingraph_MakeFonts(void *window, int font_base) -{ - wglUseFontBitmaps(GetDC(window ? window : stbwingraph_primary_window), 0, 256, font_base); -} - -// returns 1 if WM_QUIT, 0 if 'func' returned 0 -int stbwingraph_MainLoop(stbwingraph_update func, float mintime) -{ - int needs_drawing = FALSE; - MSG msg; - - int is_animating = TRUE; - if (mintime <= 0) mintime = 0.01f; - - for(;;) { - int n; - - is_animating = TRUE; - // wait for a message if: (a) we're animating and there's already a message - // or (b) we're not animating - if (!is_animating || PeekMessage(&msg, NULL, 0, 0, PM_NOREMOVE)) { - stbwingraph_force_update = FALSE; - if (GetMessage(&msg, NULL, 0, 0)) { - TranslateMessage(&msg); - DispatchMessage(&msg); - } else { - return 1; // WM_QUIT - } - - // only force a draw for certain messages... - // if I don't do this, we peg at 50% for some reason... must - // be a bug somewhere, because we peg at 100% when rendering... - // very weird... looks like NVIDIA is pumping some messages - // through our pipeline? well, ok, I guess if we can get - // non-user-generated messages we have to do this - if (!stbwingraph_force_update) { - switch (msg.message) { - case WM_MOUSEMOVE: - case WM_NCMOUSEMOVE: - break; - case WM_CHAR: - case WM_KEYDOWN: - case WM_KEYUP: - case WM_LBUTTONDOWN: - case WM_MBUTTONDOWN: - case WM_RBUTTONDOWN: - case WM_LBUTTONUP: - case WM_MBUTTONUP: - case WM_RBUTTONUP: - case WM_TIMER: - case WM_SIZE: - case WM_ACTIVATE: - needs_drawing = TRUE; - break; - } - } else - needs_drawing = TRUE; - } - - // if another message, process that first - // @TODO: i don't think this is working, because I can't key ahead - // in the SVT demo app - if (PeekMessage(&msg, NULL, 0,0, PM_NOREMOVE)) - continue; - - // and now call update - if (needs_drawing || is_animating) { - int real=1, in_client=1; - if (stbwingraph_primary_window) { - stbwingraph__window *z = (stbwingraph__window *) GetWindowLong(stbwingraph_primary_window, GWL_USERDATA); - if (z && !z->active) { - real = 0; - } - if (z) - in_client = z->in_client; - } - - if (stbwingraph_primary_window) - stbwingraph_SetGLWindow(stbwingraph_primary_window); - n = func(stbwingraph_GetTimestep(mintime), real, in_client); - if (n == STBWINGRAPH_update_exit) - return 0; // update_quit - - is_animating = (n != STBWINGRAPH_update_pause); - - needs_drawing = FALSE; - } - } -} - -void stbwingraph_SwapBuffers(void *win) -{ - stbwingraph__window *z; - if (win == NULL) win = stbwingraph_primary_window; - z = (stbwingraph__window *) GetWindowLong(win, GWL_USERDATA); - if (z && z->dc) - SwapBuffers(z->dc); -} -#endif - -#ifdef STB_WINMAIN -void stbwingraph_main(void); - -char *stb_wingraph_commandline; - -int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow) -{ - { - char buffer[1024]; - // add spaces to either side of the string - buffer[0] = ' '; - strcpy(buffer+1, lpCmdLine); - strcat(buffer, " "); - if (strstr(buffer, " -reset ")) { - ChangeDisplaySettings(NULL, 0); - exit(0); - } - if (strstr(buffer, " -window ") || strstr(buffer, " -windowed ")) - stbwingraph_request_windowed = TRUE; - else if (strstr(buffer, " -full ") || strstr(buffer, " -fullscreen ")) - stbwingraph_request_fullscreen = TRUE; - } - stb_wingraph_commandline = lpCmdLine; - - stbwingraph_DefineClass(hInstance, "appicon"); - stbwingraph_main(); - - return 0; -} -#endif - -#undef STB_EXTERN -#ifdef STB_WINGRAPH_DISABLE_DEFINE_AT_END -#undef STB_DEFINE -#endif - -#endif // INCLUDE_STB_WINGRAPH_H diff --git a/impeller/third_party/stb/stb/tests/pngsuite/16bit/basi0g16.png b/impeller/third_party/stb/stb/tests/pngsuite/16bit/basi0g16.png deleted file mode 100644 index a9f28165efd4a6eaab890f3f46eaab0812614046..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 299 zcmV+`0o4A9P)kMw|y20kXn4P1fk&um!5HR4i(o|AwZ6~LzrfePT$2C@a*GO>pzD`W+JMY+o{~OMQc&lm=3iqj(vZoAg56v)&@pF xpiv;U26q|-VrxJe1@ZNXf_(7)M8WX7;00Eg?uiut|NZ~~002ovPDHLkV1i{YdSUv}5QRU_7&y#fp@D&gr9M`8f1y92FoiBVSa`IsurPtS ze{e7d3sdH>SD>hoE4z_nx$z0??7UgMea|aJNhAr@Zvg8Z*gUmKePu;z>y8jWkvlsq z1W-(qZPlsA;>PQ-xbb>e=+HyjR`72RpJL@9duyYiw{L8ondoV1Dstk)`yNe2E|Tn) zQn&|5Z4$O-o8S$zjgb_v!ImC3RAHrGc4#Vv!)L8;XfO3ToGLXjk#F*79m4!RxgIe_tfnTuJcqEWy)4g2yKb%DDu0za_YxN$~Sgf+CmT>X!ubOoF_W z03=B5#`sg62L3e6{2RJyFI>13c>MtF{pl)#xkJ@t(o7&`wRIOmYhI*_#{U5rtXn@HNfb7oz6Bu5!i$}Tx-z(a!?=gYPiFWah6BvhR7Y~@g_(Z#S%LK+P h+Qpp-{6C^y{2ine-m~8mME3vy002ovPDHLkV1lO|5~BbB diff --git a/impeller/third_party/stb/stb/tests/pngsuite/16bit/basi4a16.png b/impeller/third_party/stb/stb/tests/pngsuite/16bit/basi4a16.png deleted file mode 100644 index 51192e731106e77cec52a3acda3d0c3cd54a16d5..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 2855 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 FV1na*GhqM# diff --git a/impeller/third_party/stb/stb/tests/pngsuite/16bit/basi6a16.png b/impeller/third_party/stb/stb/tests/pngsuite/16bit/basi6a16.png deleted file mode 100644 index 4181533ad81b739fb7b4f3b024f632cd7baa85c5..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 4180 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 N1)i>cF6*2UngAzTID!BG diff --git a/impeller/third_party/stb/stb/tests/pngsuite/16bit/basn2c16.png b/impeller/third_party/stb/stb/tests/pngsuite/16bit/basn2c16.png deleted file mode 100644 index 50c1cb91a0171e9f34991b079fe932f5f0bb16d6..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 302 zcmV+}0nz@6P)NTxus~-YE?|ew94KIo9tHTv?hhRR zwrA%J^h9UxCeRmyPjW#d?oxNFL9(uFDZ1gBle+D$rIj`J+5;}Xa zfF63WfGT3xy1iYa$zve>zUI)9x>;M1&07*qoM6N<$g8PGj A5dZ)H diff --git a/impeller/third_party/stb/stb/tests/pngsuite/16bit/basn4a16.png b/impeller/third_party/stb/stb/tests/pngsuite/16bit/basn4a16.png deleted file mode 100644 index 8243644d0743ebd1fdacb7923069fefbb01dab0e..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 2206 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 AY5)KL diff --git a/impeller/third_party/stb/stb/tests/pngsuite/16bit/bgai4a16.png b/impeller/third_party/stb/stb/tests/pngsuite/16bit/bgai4a16.png deleted file mode 100644 index 51192e731106e77cec52a3acda3d0c3cd54a16d5..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 2855 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 FV1na*GhqM# diff --git a/impeller/third_party/stb/stb/tests/pngsuite/16bit/bgan6a16.png b/impeller/third_party/stb/stb/tests/pngsuite/16bit/bgan6a16.png deleted file mode 100644 index 984a99525f5246cbc5d7083dd79006c7efa0ab0b..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 3435 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 AY5)KL diff --git a/impeller/third_party/stb/stb/tests/pngsuite/16bit/bggn4a16.png b/impeller/third_party/stb/stb/tests/pngsuite/16bit/bggn4a16.png deleted file mode 100644 index 13fd85ba193369dbd84a0dc07f73d3340485fa6a..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 2220 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 TFaLS`e*&EE@`;w61 diff --git a/impeller/third_party/stb/stb/tests/pngsuite/16bit/oi1n0g16.png b/impeller/third_party/stb/stb/tests/pngsuite/16bit/oi1n0g16.png deleted file mode 100644 index e7c82f78eb954be5be51c35bd287e00018c69d82..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 167 zcmeAS@N?(olHy`uVBq!ia0vp^3Lq>1BpBEle`W(ImUKs7M+U~W1%@xC#RK_qo-U3d z6?3j$GUPg7z~dZf8U4Qg*qsHF4`@^yUDaY)e6!!)qG9=taAD!(1y)T7vK%uR?lygM zIK}cp><*ign#1-5wiApPcd>47oWOZOH-mqPPlA0u`y2l+k{0Ld8N_aQ--utQJ^wn$ N1)i>cF6*2UngAzTID!BG diff --git a/impeller/third_party/stb/stb/tests/pngsuite/16bit/oi1n2c16.png b/impeller/third_party/stb/stb/tests/pngsuite/16bit/oi1n2c16.png deleted file mode 100644 index 50c1cb91a0171e9f34991b079fe932f5f0bb16d6..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 302 zcmV+}0nz@6P)NTxus~-YE?|ew94KIo9tHTv?hhRR zwrA%J^h9UxCeRmyPjW#d?oxNFL9(uFDZ1gBle+D$rIj`J+5;}Xa zfF63WfGT3xy1iYa$zve>zUI)9x>;M1&07*qoM6N<$g8PGj A5dZ)H diff --git a/impeller/third_party/stb/stb/tests/pngsuite/16bit/oi2n0g16.png b/impeller/third_party/stb/stb/tests/pngsuite/16bit/oi2n0g16.png deleted file mode 100644 index 14d64c583db347908d4e107b49bdaf88e3857b8d..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 179 zcmeAS@N?(olHy`uVBq!ia0vp^3Lq>1BpBEle`W(ImUKs7M+U~W1%@xC#RK^co-U3d z6?3j$GUPg7z~dZf8U4Qg*qsHF4`@^yUDaY)e6!!)qG9=taAD!(1y)T7vK%uR?lygM zIK}cp><*ign#1-5wiAr3HXJYpnk5G^i*Xn0_QnaE7j!fDm-r;u=d-`@{~~E|zMets XhWCy5O;z!=K$Q%hu6{1-oD!MJY5_^ zD&}0Bc95&tK)_}B1IPN)b=&{0{WeXGjd@a=TH53@RT6EkPi(I(=ofHeu+x7q>wQX5 zah-&%x^euuN1vn0w==yySE5m{$awnax1S1PFES>4-qaZX;>{9*dzc{lpeF5*>&z{m79Ma`yb}T3cx+N9l7QKY_b21tUy>hHS zj=$rVW!RSfocD`M`g^qt@_T0=(DrKPRwV_Ejec_{h{I9JdPiZ{w#YI8t;GR^e@i0d|ndJLIzJ) KKbLh*2~7a%CxW>E diff --git a/impeller/third_party/stb/stb/tests/pngsuite/16bit/oi4n0g16.png b/impeller/third_party/stb/stb/tests/pngsuite/16bit/oi4n0g16.png deleted file mode 100644 index 69e73ede311c4a846a8f818330708658a1e0fe77..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 203 zcmeAS@N?(olHy`uVBq!ia0vp^3Lq>1BpBEle`W(ImUKs7M+U~W1%@xC#RK{Bo-U3d z6?3j$GUPg7z~dZf8U4Qg*qsHF4`@^yUDaY)e6!!)qCI!=eVEdQwQX5 zah-&%x^euuN1vn0w==yySE5m{$awnax1S1PFES>4-qaZX;>{Y_l!4PL3!@ULbQoiNtWY z=b;I!`Z?mk+pi~%T4uHFkQ%qs0PKuo=U#gjgXq@dQ#YX;uR zyDEzTqN3i8Ri+t9;=Io`4iKjuJ*jdoASoRATB86`e`o#L4-kt!*XOIIkPy|$Z!G18 zk=$_R1cm^jDHwGE*;OPH+pecw0=8F|cWD7)9r{V(>YZh7s}#xX@Y-V@a8$R# zz7Nzll=T^a-D91a7GNOU#!3RDRppIxfH+g4`7VUA7;JpD{z%Gl^hz`rR!C-7WjBz2w=IKp0oL zbO>k?lt*0#RAuh<9{?e*sV()uX|32M9JssnY{osHpuWUu5cv8Oo7WFKnf5e90$&!U zD|&#%lN#4PfLz=wKl=n%AbF;bH~a_iR2$1}rLn7htMc}f7XqbjYTgrI+dp1QF98{q zx~>x7qmaPfO~8-eOc`>46CZ0QegN!tCr*3|kka8h&MF@x8JDN?qySsUVf_vKy0476 z9%wM?M@--UJ8OATkeUmSDcRFHm6nLa%*FSV76{aF1);#sVqI_>Kql)G-?4S=NapNh zVg_*djmhC=Ag9J7dkM%L^-MbokZB}6AQ$H>b5|DQ7>`=5gAqs{oMFj;fyD8u{}=gK zMu71K5`$&sNDOfBa_Q}rTV!=k^EBM^c>q~R=6AduZXuc8qS|W*kl#6Wnxp&)iHoCb zXd@uZXIuUPD6CbzZUDK2+Pw*)4M@m|&Mp?KwVGjB$JzxnI1su>k|SW z;K-2xP8~qD0I!0IuDeLqZiu^T&h0G>BMn)Ym=lSRkOKQt;)MxF$jp%`={{CrJA+}Q z0NGudCGHuG7?!!Lm%ady&A}>J%1A4M0{%q_gjthe+%dX`L1TnYm#8ctAc9 z@}pa>>=HU6*)gvdIs;@`N_gX0SxCrj<`CfL)P#i0DJdZ9W{Ka!FtV*beIQ<%iDapc zzwQpuzunh$8PLB|-d+oAxGoVK2OOgUZCC)=Ex%&=XT>2Qmra%!>Ggb-Hfk2fkY^() zKsHtKl(~0px+MSwNPBxOXxkqksf(TP*$R+5i!_m(4oOIRA3ry`IR({7L&)Y`5g+dy zjA3LNm(5d>Pa+|cbrc|HP1XT<>~@^s)UP}#S04qW zJ7f!3yE~lF1UCm1Ag?RrHNta!r8Eu0$jtfWu@_WGT8_ru{1AB9l%}=?3U3!0mB7Z| zi!2)ef4yhpK|nM&HhOB*ztD(3v5YRNcL0EJE diff --git a/impeller/third_party/stb/stb/tests/pngsuite/16bit/tbbn2c16.png b/impeller/third_party/stb/stb/tests/pngsuite/16bit/tbbn2c16.png deleted file mode 100644 index dd3168e5c864a81f3284856f2b00fd1e52eb312e..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 2041 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 diff --git a/impeller/third_party/stb/stb/tests/pngsuite/corrupt/xc9n2c08.png b/impeller/third_party/stb/stb/tests/pngsuite/corrupt/xc9n2c08.png deleted file mode 100644 index b11c2a7b4049475d967a8cc76b93ef1039684a3a..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 145 zcmeAS@N?(olHy`uVBq!ia0vp^3LwnE2_&^8vlamc;6;z7cmE{-7; zb9B#amdK II;Vst0B-g)L;wH) diff --git a/impeller/third_party/stb/stb/tests/pngsuite/corrupt/xd0n2c08.png b/impeller/third_party/stb/stb/tests/pngsuite/corrupt/xd0n2c08.png deleted file mode 100644 index 2f001610a85a662d8baa0c1f02c06adc02cfa20f..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 145 zcmeAS@N?(olHy`uVBq!ia0vp^3Si6xB+q0lO9E0X>5jgR42*3H3|~x(2l72UT^vIy z=DfXnkdwiHhsmM!&BVXki-fIRw7)F;aqgk^7G4(X1}>2wG7H{5sr9LFXk_B|YL`FM q!Y-omVL^$7@=Axs-38@~4=^@u{29mZaNZkeCWEJ|pUXO@geCyFeJ}z5 diff --git a/impeller/third_party/stb/stb/tests/pngsuite/corrupt/xd3n2c08.png b/impeller/third_party/stb/stb/tests/pngsuite/corrupt/xd3n2c08.png deleted file mode 100644 index 9e4a3ff7accf4453ddcd58318f7048b326b44ad2..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 145 zcmeAS@N?(olHy`uVBq!ia0vp^3LwnP1SGpp+}Q-ASkfJR9T^zg78t&m77yfmc)B=- zRLpsM^&lsM0S}Wy>zj#xw-*UpyJ&w|_~YC|?Jc}4)(u=DKV%lXeNyXF;n2v$@6|4U rsD)ibzj#xw-*UpyJ&w|_~YC|?Jc}4)(u=DKV%lXeNyXF;n2v$@6|4U rsD)ibc;6;z8n`u6{1- HoD!M<9fb>g diff --git a/impeller/third_party/stb/stb/tests/pngsuite/corrupt/xhdn0g08.png b/impeller/third_party/stb/stb/tests/pngsuite/corrupt/xhdn0g08.png deleted file mode 100644 index fcb8737fa2505b43955e995d95c3d6972b85fe32..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 138 zcmeAS@N?(olHy`uVBq!ia0vp^3Lwk@Bp957Lw$i1OS+@4BLidG0>c;6;(>fePZ!6K ziaE(C2`UUC949vOu(I~>b7=nfE^nVuX&@tFa7u7Oddi~87#=p(p8gM~{{27yqy1T- hp#)Io!PL|g7KVVQ{|ZeX-G~HP;_2$=vd$@?2>>eWDsTV* diff --git a/impeller/third_party/stb/stb/tests/pngsuite/corrupt/xlfn0g04.png b/impeller/third_party/stb/stb/tests/pngsuite/corrupt/xlfn0g04.png deleted file mode 100644 index d9ec53ed94b34f50eeabf3029465e1106725ea2a..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 145 zcmeAS@N?(llHy`uVBqrfa0vp^3Lwk^Bp4c;6;z7cmE{-7; zb9B#azopr048WOXaE2J diff --git a/impeller/third_party/stb/stb/tests/pngsuite/corrupt/xs2n0g01.png b/impeller/third_party/stb/stb/tests/pngsuite/corrupt/xs2n0g01.png deleted file mode 100644 index b8147f2a84b861b559fd52fc0ded6971debdc6f1..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 164 zcmeAS^mFIslHy`uVBq!ia0vp^3Lwk~Bp9L@-6Me%OS+@4BLidG0>c;6;z7cmE{-7; zb9B#azopr0C@{E=>Px# diff --git a/impeller/third_party/stb/stb/tests/pngsuite/corrupt/xs4n0g01.png b/impeller/third_party/stb/stb/tests/pngsuite/corrupt/xs4n0g01.png deleted file mode 100644 index 45237a1d294f7432c06ae45769727d9745250221..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 164 zcmeAS@Jr|AlHy`uVBq!ia0vp^3Lwk~Bp9L@-6Me%OS+@4BLidG0>c;6;z7cmE{-7; zb9B#azopr0E~z=2mk;8 diff --git a/impeller/third_party/stb/stb/tests/pngsuite/corrupt/xs7n0g01.png b/impeller/third_party/stb/stb/tests/pngsuite/corrupt/xs7n0g01.png deleted file mode 100644 index 3f307f14ea5ed37b9f74b896a98ee4a5d2c9e111..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 164 zcmeAS@N?(oQs81>VBq!ia0vp^3Lwk~Bp9L@-6Me%OS+@4BLidG0>c;6;z7cmE{-7; zb9B#azopr0DK%Y?f?J) diff --git a/impeller/third_party/stb/stb/tests/pngsuite/primary/basi0g01.png b/impeller/third_party/stb/stb/tests/pngsuite/primary/basi0g01.png deleted file mode 100644 index 556fa72704084920c07066054bb57adc5a250b27..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 217 zcmeAS@N?(olHy`uVBq!ia0vp^3Lwk~Bp7wr%FhF7mUKs7M+U~W1%@xC#RK^hJY5_^ zD(2|+8uA@7;Bmcwtm%lTL>0plJ;|yaa?KmnAMk`(G6*LxZMYGklX_CXw8pz)!~XsE z{R_l;B%iO@S+Mxi#d|k1W1jAhOum0pYrW6>o9eGz=B2dXkDg=peCAH+`yAT7-d7m? zpD=neuC5Ya_iW0gUZXDEgTe~DWM4fgQ!vM diff --git a/impeller/third_party/stb/stb/tests/pngsuite/primary/basi0g02.png b/impeller/third_party/stb/stb/tests/pngsuite/primary/basi0g02.png deleted file mode 100644 index ce09821ef101b65b2aa653931416c72923ccab09..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 154 zcmeAS@N?(olHy`uVBq!ia0vp^3Lwk`Bp9=o@yY{fmUKs7M+U~W1%@xC#RK_)o-U3d z6?2jUl3pY*JS=M4oiJze;et03Q>IK~Gc?O{N#DFKJ@&l7SH)M%%o7ftVn}g$DCX>; zkRs^i5p-N&*DHI8!;%tAJO+*|%*(hAE|_G=Fej+Nw{_diTA<+!p00i_>zopr0NF1v AeE`KLv!pxvIx;Y}EiimBEgr~U=jq}Y zQZXmBFHrE1fq;v+j0h02$h25#lnT63xZ-%g@gNW$aGWTh62d&wbH=%|b$8yp-97vE z;Z29Oxt6;AKCTefnOkLOe zceV31xk&buuP3aOvK_8v?mNu>)5yI*ti8x;zv2JpMH=2qx2(%fUX!Y@wwPzh+iJE; se9X+5d(#!(S{O|*V`01GC)_Bn-uQauWnXqNpvxINUHx3vIVCg!0H8fu|I&j2dyvG3;`TGSdQ=U^x!+b$IgQ- z%948O0A52IVpo?-TFZBK`N@L-n zZIKuYb8U;nSX9Mrl^A36r{K12&k3mj^Z%lhjX;s*Jg960iexztW-CxinU-MA*#;OZ zngOW30HtIErtRT^u_8M_CF3kj<3j{vMRtJ6I<2P3n{$R*t1rNmk`0g=AU|E-;g=04 zMQ4L1&-<_M{L=>-dBi{f3 diff --git a/impeller/third_party/stb/stb/tests/pngsuite/primary/basi3p02.png b/impeller/third_party/stb/stb/tests/pngsuite/primary/basi3p02.png deleted file mode 100644 index bb16b44b30907b832b678a3b441278df340edb9f..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 193 zcmeAS@N?(olHy`uVBq!ia0vp^3LwnH%)r1{DZJ<-ki(Mh=EaktF(C3l8oA>Me*wxTz-(}y$c;d$?hLjJF#hg79QUtv`g1+0Ah)O6+%y3%0 kK!=4{>!bX^1(PfpwnWvwlrMbmdKI;Vst08#Hgh5!Hn diff --git a/impeller/third_party/stb/stb/tests/pngsuite/primary/basi3p04.png b/impeller/third_party/stb/stb/tests/pngsuite/primary/basi3p04.png deleted file mode 100644 index b4e888e2477d4fbaa3c8ed16a8007d40e58cd52c..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 327 zcmeAS@N?(olHy`uVBq!ia0vp^3LwnF%)r3-EjT<7$YDu$^mSxlY+GRXVp=?q&s^-} z8N$NCQvPG>Vjx#Hz$e62iQzxP|Nk8f|CRnTFwFe_KaJro!~b%i7*N6gnRh{q-T!wp zFt`9&E|)fV?gff(^K@|xshE@eVr8<$zX=QdragS9+NR;%b~IQ*M^<9l2E!vd+h?C( zII@q&;mi?+BR6^0F7JwWQfc53?9iD|$he5*L_qV3*BrK+SKaw>*N06*_XNYzScwH+ zV%a3lGAx}cv7khXdCI+B1NO@fK9ddC9@xTtImqX>;oQaofA@?RGmTe=O`R?+y}#PL zSZx`DR(OCwFvHZ>kD4?aPVKE04`%2+-2h~U1|1S{c)Dz9BG-bV^7qFC7#KE}y~^Mz Rxc~|Y22WQ%mvv4FO#pr5f6M>? diff --git a/impeller/third_party/stb/stb/tests/pngsuite/primary/basi3p08.png b/impeller/third_party/stb/stb/tests/pngsuite/primary/basi3p08.png deleted file mode 100644 index 50a6d1cac7a111d53cd3aec0d35dc4d09311baab..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1527 zcmWkt4Nw$y6n}dd;vwQ8DFIG$3@GI2c^hLQV*{=V2t>jrDk_FvDg&7$%b-@G;h|=k zpdN>Gek8Jdh#y$4REHQNu9O-n$@*~yld0Q~CgKPCdUtQ`_P_uC|GnSGdsmv8GRH?B zs0RR_gm`WqJ;%AfULF7pdq_&9mmwd-CdUF(;+S%8&r$#nNKHzgW#r%qssai!qCpSQ z0vL_7LIcnOu#UGNg#{Tj`hkLY32hvQ!1G1`Sw@Zn9(h>?HW2Wkq9XR7jEce`M@cLI zX9QKH0)U4(L{U}UX&T}n(htKT%SQskpw)^XiVOoBfYl1vBTI|Gh(?PU5N)D}JkUAF z*+A4fQ52$qu$3IJkUnIraqtk890gF3fMr1iHJ=i#wnVy*0swAiP?EIuK(HVK+H9oK zO8az3*a`*`R}fi5uu;&5F%xGAi8P7`R6^54fJ7UmSdgp%+jcbYbn0eLh5#4{O2a|z zFlncK3j#}*c@!wwN`=4x0@Wv9Pz46?iR#Xv1EQkRnl2%Lup5n_C@hN_Xp)3BL=u!O zgCs#6RU@v*uGicWd7h||*~k+*SQG^)HX|blsFAzW2%aZ!O(VrJ31D;t3x3pAg5Wqi zve-$Qc$9#ED~5p_E`A&DTySs_gXBPgvEi4GHiIsCXqG@a(*{? zqG+gzY2=omW89DcEUHMQNrOtO^VViET%@j9XuK5ipN55EJB^!6bHJ02)DH{^ z28HH;T8~zS28YEnJAZp0KzA^Ki%l=ATyc3-rZ)t5_Dwa^e3e*sHMuT3A|j#p=*X*F z#y0Vd=-McX6_VY&k>*>Z%e|vX1^&KXtnjHm_kp z{Oz$(O(8S=UuD^|XKQO8JeWScy}iAy&FQ@UT~n0V9QLiaO{Ytot3K50Bj4!g*tp@* zcGj*hPH>pOmLs;xabA)>UnI5F|W2|vHO`X8G8{qNq5UvQ>+yva2AorH*qN%N=1 zRO-F+*Y4O692_0}_3qrW zV_8*GU40<7^H$aV#hJ=gqrL3swI3}rKihD;tJ~gc@LGOoZ}}hHIjh6Ni;61tbzVDC zEf)+M(cfo^N={x^khe0+l%Bp^o)#S#*w$83a{u1Ivl?+Wcj0ty-sn>0W|&`1nH z58GA$jXh^Y`JN;7H@hZGKGv`#E%e2gv6o*jI5)Y=Gw4px%OQatp)Q!Xsl()5;fH&w40HKUbx zj4}D7oEu&UXI&wcy60YxT`|F*cQ@)MIhQzY_v!Om7VgvKXAK=bq{#ES%PVuszm5L= z&bJ*WO#!E!Apy*TI{`1@hW^84hM29NG>%Pa9pm_Xjc<61X<)<)m$})aVkqnKe7Wgq z-l@|1T-)pTTjSfo?;6Ga8{R(IDkmelNXRj)ZH9oA|PM{kg$m&gMT07S3iqx$gt>#1~b*D~%?ISJ;?`E{!%Qxx!#mpQpGpa(A8H z_iZ|MVh@5=q!mUuvSsYMxvKp95>weV2Kff8ej5&Q`t30L(azh))$_*RmQyw|G#2PE N22WQ%mvv4FO#oeUP~QLm diff --git a/impeller/third_party/stb/stb/tests/pngsuite/primary/basi6a08.png b/impeller/third_party/stb/stb/tests/pngsuite/primary/basi6a08.png deleted file mode 100644 index aecb32e0d9e347ccdcab5d7fdad2dde7aef9da8a..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 361 zcmV-v0ha!WP)!N7@Xl>n0JWRT0P1ZZ0~F7IAF8qd>UAgssMo0sP&xyjf=W~LX-;!O00000NkvXX Hu0mjfr8|%> diff --git a/impeller/third_party/stb/stb/tests/pngsuite/primary/basn0g01.png b/impeller/third_party/stb/stb/tests/pngsuite/primary/basn0g01.png deleted file mode 100644 index 1d722423aa5157fe2b029af4e2bb07f478ebe8bb..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 164 zcmeAS@N?(olHy`uVBq!ia0vp^3Lwk~Bp9L@-6Me%OS+@4BLidG0>c;6;z7cmE{-7; zb9B#azopr0C;FL=l}o! diff --git a/impeller/third_party/stb/stb/tests/pngsuite/primary/basn0g02.png b/impeller/third_party/stb/stb/tests/pngsuite/primary/basn0g02.png deleted file mode 100644 index 508332418fa86637d39e95268ec1d3658d120cae..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 104 zcmeAS@N?(olHy`uVBq!ia0vp^3Lwk`Bp75C+I9jdmUKs7M+U~W1%@xC#RK{Bo-U3d z6?2jkIAXub_k1+y2sp$L+}6)%%qgEdVJADoUeEf*ZqCk?AR|0o{an^LB{Ts5miHSU diff --git a/impeller/third_party/stb/stb/tests/pngsuite/primary/basn0g04.png b/impeller/third_party/stb/stb/tests/pngsuite/primary/basn0g04.png deleted file mode 100644 index 0bf3687863d8a1f53bef0aa24b841b21b0e04d9e..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 145 zcmeAS@N?(olHy`uVBq!ia0vp^3Lwk^Bp4c;6;(>fePZ!6K ziaE(C2`UUC949vOu(I~>b7=nfE^nVuX&@tFa7u7Oddi~87#=p(p8gM~{{27yqy1T- hp#)Io!PL|g7KVVQ{|ZeX-G~HP;_2$=vd$@?2>|oDDeM3M diff --git a/impeller/third_party/stb/stb/tests/pngsuite/primary/basn2c08.png b/impeller/third_party/stb/stb/tests/pngsuite/primary/basn2c08.png deleted file mode 100644 index db5ad15865f56e48e4bae5b43661d2dbc4e847e3..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 145 zcmeAS@N?(olHy`uVBq!ia0vp^3LwnE1SJ1Ryj={WSkfJR9T^zg78t&m77yfmc)B=- zRLpsM^&lsM0S}Wy>zj#xw-*UpyJ&w|_~YC|?Jc}4)(u=DKV%lXeNyXF;n2v$@6|4U rsD)ibtrx; diff --git a/impeller/third_party/stb/stb/tests/pngsuite/primary/basn3p01.png b/impeller/third_party/stb/stb/tests/pngsuite/primary/basn3p01.png deleted file mode 100644 index b145c2b8eff1f4298e540bfae5c1351d015a3592..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 112 zcmeAS@N?(olHy`uVBq!ia0vp^3LwnL3?x0byx0z;SkfJR9T^zg78t&m77ygJ1^9%x zzWcAFl=eSI>XI5zMAXy8F{ENn@&k4zHj_up0tD0@h%3W?AY}Lt#0>va?X`CSX(dk=$B>FS$v@Vz>816FsF9(VN75txh7sS~8e>Vez z3y|e3}y1F=z>r=LEEca@LnZf*~BA0ROZ5~$d#4)lIa>n+tGAx#s~NCh;!h=Q?&7t5~~Lk>mL*|1x+L`Ivp6mn?kJ^kK$c6a~%|NrOteLPR)ru^*4 zh?oc>ipx)wHw!xP<||VqG2mh2yNQ1IZKr30Xr(I7PBXU z(o_;ftk^?X5O*qmM)+A^aUR*s!>r3PlcI=3mc_D63M-aHQVj83+hHDKi)~wG8A%eb zMRFVzAa0kL4aW(lxy(-(A3@r7{KRdCcI^9^ zBwSWlMY4uMp(p@%T`0C7K$rnonuoRmLY9xP#5bTx(RKJi zCYfG^*s^*-{Ro^e4Kgw{Dlmv)JpjQ5bKwF@CLI|%59=nhA>e#HJh5GNjRLr(&Jco- zL=roU($L^w70~)&xPh+uFV&-K!K0w3W-9Hy@g@HWXL3ML4|%5ULen8 zlT@|D2@;VI*iada4446_ptp_#Sul*Cx7Y6>PlSiq8TyN-q=y}cXZX&@20)p=ihIP{CfA$Z*4W@ zEr$zzJ<~t_(0G1&!T9U{_Iz>DPFy@Dgq4(i*+1}U*ZiG1m)32#<4sRFJ5!AP>yZ}L zzdq~5lB&|C6*=qgy?nj!is#SN$*Gwo6M?qJgKJtd1_tvb_xS3qJuRh&eH)f1f0J@< z)4uq(Z_li{H~+s=+3L-xjU7X)ZvEUC-CLA+t^7d7$jI)tsK~;c&XdnBOC5Txp}1)@ zusm-vWl2G`{dnN}K>pDE=;yi{rVB@Y3B-HrtGOePxNz#}j}FD`z3|nlwikX+ZZ56= zAY&lKTUG9w&2^VN89D}o5=^>%~&6ID5>`0fk%7K&zZcY&F#znJTW|E zB5&cFwM~ly35&;HdZq5*>W9rM<&V4Wr%mNt{B`@CxYpR8I;^7!?Tc=wu6t&Du2Ec< zKIiz-xXN3dttqGahAW20Qo6MzgSk7e_{!(?CKyw`*?j|NY=7wG%F`JaWZ(VOm8CUr z$$no_)1uMK%W6)nN!IrL6EB>puUj#i+1xg}<&qUPnY6#j4&NW^MpZNn{t+Cq+^l@L JEAySt{s-!kV`~5a diff --git a/impeller/third_party/stb/stb/tests/pngsuite/primary/basn4a08.png b/impeller/third_party/stb/stb/tests/pngsuite/primary/basn4a08.png deleted file mode 100644 index 3e13052201c9f8b90172ea2491b18d6d121c2786..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 126 zcmeAS@N?(olHy`uVBq!ia0vp^3LwnE0wix1Z>k4UEa{HEjtq=#3k+XOiwE*eJzX3_ zD&{2rIe*{)W9ys@6GNprI`<_tn04&i_AxF_G>~EoF-$NKa6jCj#n!|2C_^IPFayJV XErr@6uUk(54Pfwe^>bP0l+XkK+-D@M diff --git a/impeller/third_party/stb/stb/tests/pngsuite/primary/basn6a08.png b/impeller/third_party/stb/stb/tests/pngsuite/primary/basn6a08.png deleted file mode 100644 index e6087387639b9cefaaafb696e29a8bd910ee0680..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 184 zcmeAS@N?(olHy`uVBq!ia0vp^3LwnE1|*BCs=fdzmUKs7M+U~W1%@xC#RK{Io-U3d z6?5KRGvsP8;BkAXo|56e@Vk8|OTXfgyGLCsWP@G=X#A1))wtQkXb@LjH|PH@<7U}X zR(Ix8Y%6~9Ml4|WGnG@SZHQ?Kc`Uubi9hC!9z&%<$E5#{7BHGEsCHnVVk5wElR+}T f%%Pr9`U69dg47%_{vAF*s~J39{an^LB{Ts5$SFGl diff --git a/impeller/third_party/stb/stb/tests/pngsuite/primary/bgai4a08.png b/impeller/third_party/stb/stb/tests/pngsuite/primary/bgai4a08.png deleted file mode 100644 index 398132be5faadf83e159ac59c212213bcd43a894..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 214 zcmeAS@N?(olHy`uVBq!ia0vp^3LwnE!oa||uB7QDki(Mh=l@|1T-)pTTjSfo?;6Ga8{R(IDkmelNXRj)ZH9oA|PM{kg$m&gMT07S3iqx$gt>#1~b*D~%?ISJ;?`E{!%Qxx!#mpQpGpa(A8H z_iZ|MVh@5=q!mUuvSsYMxvKp95>weV2Kff8ej5&Q`t30L(azh))$_*RmQyw|G#2PE N22WQ%mvv4FO#oeUP~QLm diff --git a/impeller/third_party/stb/stb/tests/pngsuite/primary/bgan6a08.png b/impeller/third_party/stb/stb/tests/pngsuite/primary/bgan6a08.png deleted file mode 100644 index e6087387639b9cefaaafb696e29a8bd910ee0680..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 184 zcmeAS@N?(olHy`uVBq!ia0vp^3LwnE1|*BCs=fdzmUKs7M+U~W1%@xC#RK{Io-U3d z6?5KRGvsP8;BkAXo|56e@Vk8|OTXfgyGLCsWP@G=X#A1))wtQkXb@LjH|PH@<7U}X zR(Ix8Y%6~9Ml4|WGnG@SZHQ?Kc`Uubi9hC!9z&%<$E5#{7BHGEsCHnVVk5wElR+}T f%%Pr9`U69dg47%_{vAF*s~J39{an^LB{Ts5$SFGl diff --git a/impeller/third_party/stb/stb/tests/pngsuite/primary/bgbn4a08.png b/impeller/third_party/stb/stb/tests/pngsuite/primary/bgbn4a08.png deleted file mode 100644 index 7cbefc3bff08a9d91666d6b0f8b5cb1c896b7987..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 140 zcmeAS@N?(olHy`uVBq!ia0vp^3LwnE0wix1Z>k4UEa{HEjtq=#3k+XOiwE+VlDyqr z7#LRdDjNZLrk*a2Ar*6y|C~Q?fU$K>hKZrl9G&};8q7NOZTlD(CmKkxg%~E72)G|^ k&|>Rhdz2v&aF~JNzLrAmk=Ly!fCeyly85}Sb4q9e08Xzba{vGU diff --git a/impeller/third_party/stb/stb/tests/pngsuite/primary/bgwn6a08.png b/impeller/third_party/stb/stb/tests/pngsuite/primary/bgwn6a08.png deleted file mode 100644 index a67ff205bba91cc8f391a0b59110ae6c038539ac..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 202 zcmeAS@N?(olHy`uVBq!ia0vp^3LwnE1|*BCs=fdzmUKs7M+U~W1%@xC#RK_lN#5=* z4F5rJ!QSPQfg<^yE{-7;bKYJvrKjw}eL#0B;r2mf= uFq$o>c3_`kBfxT#K{CM1p`KCt14EF4)EqJX9X>#-89ZJ6T-G@yGywoV#zE8o diff --git a/impeller/third_party/stb/stb/tests/pngsuite/primary/s01i3p01.png b/impeller/third_party/stb/stb/tests/pngsuite/primary/s01i3p01.png deleted file mode 100644 index 6c0fad1fc982e54aea994e12efd3fe3584cabdbc..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 113 zcmeAS@N?(olHy`uVBq!ia0vp^j3CU&3?zc?q{RR^Ea{HEjtq=#3k+XOiwE+Vi=8|} zSXfxfe{5Y0;s*GHxH2&O@2a>I4&-uqx;TbZ%t=lFvY8kdJ=QNN1hN=BUHx3vIVCg! E01FBk(f|Me diff --git a/impeller/third_party/stb/stb/tests/pngsuite/primary/s01n3p01.png b/impeller/third_party/stb/stb/tests/pngsuite/primary/s01n3p01.png deleted file mode 100644 index cb2c8c78261e509e7ef2c352306618963954a84a..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 113 zcmeAS@N?(olHy`uVBq!ia0vp^j3CU&3?x-=hn)gaEa{HEjtq=#3k+XOiwE+Vi=8|} zSXfxfe{5Y0;s*GHxH2&O@2a>I4&-uqx;TbZ%t=lFvY8kdJ=QNN1hN=BUHx3vIVCg! E02xdg0RR91 diff --git a/impeller/third_party/stb/stb/tests/pngsuite/primary/s02i3p01.png b/impeller/third_party/stb/stb/tests/pngsuite/primary/s02i3p01.png deleted file mode 100644 index 2defaed911a29507f745bd7183a9819b29cc53de..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 114 zcmeAS@N?(olHy`uVBq!ia0vp^Od!n2%)r2CU%&n%ki(Mh=)Kx2KC^NX4Aw1O`S11~vx9e?93ZfWi!(u6{1- HoD!M4t diff --git a/impeller/third_party/stb/stb/tests/pngsuite/primary/s02n3p01.png b/impeller/third_party/stb/stb/tests/pngsuite/primary/s02n3p01.png deleted file mode 100644 index 2b1b669643540f182c425fb67869b7f97fe75f10..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 115 zcmeAS@N?(olHy`uVBq!ia0vp^Od!n23?w}&=BEQGmUKs7M+U~W1%@xC#RK`w#ZI0f zEG#VLKejFgaRYopTp9lVmyFR@1#)>jT^vIy<|HR1Bmj9V42<8zEkc1T22WQ%mvv4F FO#mnG8JGY7 diff --git a/impeller/third_party/stb/stb/tests/pngsuite/primary/s03i3p01.png b/impeller/third_party/stb/stb/tests/pngsuite/primary/s03i3p01.png deleted file mode 100644 index c23fdc463170faf97e53fccb4799386700b21a15..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 118 zcmeAS@N?(olHy`uVBq!ia0vp^%plANBpIb2ie~{iEa{HEjtq=#3k+XOiw8*-J9&n% zu&|W>*t!_VWee~Lab@_=@V}g49;WoqQ$(6ld^s L^>bP0l+XkKxV;@Z diff --git a/impeller/third_party/stb/stb/tests/pngsuite/primary/s03n3p01.png b/impeller/third_party/stb/stb/tests/pngsuite/primary/s03n3p01.png deleted file mode 100644 index 6d96ee4f873baf1df3652a8d70994eeea799c30b..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 120 zcmeAS@N?(olHy`uVBq!ia0vp^%plANB6FUp{{d1g>5jgR42*3H3|~x(2T2w?d4{mC zu$2GUx){i13-AeXW%$qVzno#?)1~u)B7B}Mjv*Cuk`odN5)y#?V+@X#hCJRt7K5j& KpUXO@geCw&86D36 diff --git a/impeller/third_party/stb/stb/tests/pngsuite/primary/s04i3p01.png b/impeller/third_party/stb/stb/tests/pngsuite/primary/s04i3p01.png deleted file mode 100644 index 0e710c2c397e371e4feab66add6a9f9763ce0c27..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 126 zcmeAS@N?(olHy`uVBq!ia0vp^EFjFt%)r3-#KLAZh?DN<>&U>^w!rYkw0Iz&x!B1w zgoTBr{KwYCKrUN=Pl)S(hVuXa8D@QV-U$>D@pN$vshE?Tk-%_}nTL-@Si+8hL2?}n Uzopr0P-Lp3;+NC diff --git a/impeller/third_party/stb/stb/tests/pngsuite/primary/s04n3p01.png b/impeller/third_party/stb/stb/tests/pngsuite/primary/s04n3p01.png deleted file mode 100644 index 956396c45b5103d3c38dd8906be14002e5bee48f..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 121 zcmeAS@N?(olHy`uVBq!ia0vp^EFjFt3?wJp^Voto>5jgR42*3H3|~x(2lAPVojgNW zSXjz`Y+VfGvIY2rxc+A-|Noz1)_3QfKoNdV7srr_Imtf`7%+0!GcfRQFf%r7-v+XT N!PC{xWt~$(695jV9kT!c diff --git a/impeller/third_party/stb/stb/tests/pngsuite/primary/s05i3p02.png b/impeller/third_party/stb/stb/tests/pngsuite/primary/s05i3p02.png deleted file mode 100644 index d14cbd351ac11022eefcfa3bb2af528c3aadae41..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 134 zcmeAS@N?(olHy`uVBq!ia0vp^tRT$9%)r3d&i3yCki(Mh=b^8f!C{x5dr%K!>0dAc};RLn`vNXST-!El6+N0&!i n!bU<#VwswOk4HkpRtAR1>$j<%)r2S&bt2^ki(Mh==jv*Cuk}VDtG(2QH%-+V! svtII_@t;Hw#zXcz;mHztXBa0j9DK~v7~1r+6R3f~)78&qol`;+01$C3h5!Hn diff --git a/impeller/third_party/stb/stb/tests/pngsuite/primary/s07n3p02.png b/impeller/third_party/stb/stb/tests/pngsuite/primary/s07n3p02.png deleted file mode 100644 index 6a582593d654c8d43aa8c8dfa8f6516e4f24c8c4..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 138 zcmeAS@N?(olHy`uVBq!ia0vp^>>$j<3?z5j>~{cCEa{HEjtq=#3k+XOiwE+Vi=8|} zSXfxfe{5Y08<>}%WQZXm_$N%<+Sv-f=TJo4b eYw%F`{a>D;u##)1Tx>4LCI(MeKbLh*2~7YjwJ1CQ diff --git a/impeller/third_party/stb/stb/tests/pngsuite/primary/s08i3p02.png b/impeller/third_party/stb/stb/tests/pngsuite/primary/s08i3p02.png deleted file mode 100644 index acf74f3fc4132609443b0555d56e5b314644bf23..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 149 zcmeAS@N?(olHy`uVBq!ia0vp^93afZ%)r2SE-kGO$YDu$^mSxlY+GRXVp=?q&s^-} z8N$NCQvPG>Vj!0%z$e6&;s5_~hX3XN8U8actlAYR0~A;Fba4!+n3J5qz{IFfcOc>T u0@fwp4bCJ+99*!q$#Hv=FS$q5Mw7orX>_}}cf hovEdPM`3y+BZF-itL;x80b!sr22WQ%mvv4FO#lIoCTIWv diff --git a/impeller/third_party/stb/stb/tests/pngsuite/primary/s09i3p02.png b/impeller/third_party/stb/stb/tests/pngsuite/primary/s09i3p02.png deleted file mode 100644 index 0bfae8e45678282b23bed2760c0dbbd736be9df8..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 147 zcmeAS@N?(olHy`uVBq!ia0vp^oFL4^%)r3->c>4%AcrO0(btiIv2B6ji)rydK69~? zX9x=mOZktji-BC80G|+7hW`u<<^TUP{QqCh@IQEaktF()~}fr+~zg1bjT rBF;F5J&BF&_kSLqX%_`}QkWS^|FgS^^-Kp^#=zj|>gTe~DWM4fSkxz) diff --git a/impeller/third_party/stb/stb/tests/pngsuite/primary/s09n3p02.png b/impeller/third_party/stb/stb/tests/pngsuite/primary/s09n3p02.png deleted file mode 100644 index 711ab8245189b4d5118b4dcd49ef9771bf924fb8..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 143 zcmeAS@N?(olHy`uVBq!ia0vp^oFL4^3?%3Nf7cA8SkfJR9T^zg78t&m77yez7dv@| zu&}U{|Jb@1$mI#}32|lk&%jXr|3Ab3|K$w-!x&P4;_{v@jv*Cuk`ox1e*fp;30o~G lp|iL#LdvQ6heDwO14Cvi>)|ZtdS#$e22WQ%mvv4FO#lN*C*c49 diff --git a/impeller/third_party/stb/stb/tests/pngsuite/primary/s32i3p04.png b/impeller/third_party/stb/stb/tests/pngsuite/primary/s32i3p04.png deleted file mode 100644 index 0841910b72779aa7571cce45e56447eeb3de4520..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 355 zcmeAS@N?(olHy`uVBq!ia0vp^3LwnF%)r3-EjT<7$YDu$^mSxlY+GRXVp=?q&s^-} z8N$NCQvPG>Vjx#Nz$e5NNdIRjXZZggLNS#8F9$J!e4s=*NVput`nsRZ8c5&pba4!+ zm{U7pFJF^^h?~7@-J{?g64%>Ss_=bq+ht{1xSCJrO-tjMmhSi` nkfZu#wXNcYdz+%H|96_V1YK&G^7e=_(2oqBu6{1-oD!M>$PW;WtqGsLq6}$jpDV7@>y0l)ibBXvN}dz6L`X{!W|QK qt4`9uP~=9WliB7OneY3z7cem=9`<-$rKSgT2ZN`ppUXO@geCyfYFs4% diff --git a/impeller/third_party/stb/stb/tests/pngsuite/primary/s33i3p04.png b/impeller/third_party/stb/stb/tests/pngsuite/primary/s33i3p04.png deleted file mode 100644 index ab0dc14aba444d3f59f0bf77808ee7ee78ab5a48..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 385 zcmeAS@N?(olHy`uVBq!ia0vp^iXhCw%)r2S>FoPcKn_c~qpu?aW7`757t`W_eCA>& z&kz9yJIm7?|5Q?Gve>sQ=2z#A&*U>UfA#bB(&kXT4uKE5vkQo zUQ-!gbN(`3qRGqhce6z7%EakmS0Y8FPq}6+_@ir6xO2`=y*VFuJPs^Aejwdi=Zl4) zY)`yQC*Lu~AJ4vKi$Bd@7qys2yXwnUHmtVq{n7=4!I#bZtC8xk9FB%)(-P6#}_whaYKv zG;#N1*N-P`3zt?rkunQiXB;>sd*S~OJ*$Vjx#Nz$e5NNdIRjXZZggLNS#8F9$J!e4s=*NVput`nsRZ8c1LAba4!+ zm{Z%gm#f)8#BG1p-0Bz*xXo g-$=Io|APMvAEo}ki@M*T3G^LmdKI;Vst0JTexR{#J2 diff --git a/impeller/third_party/stb/stb/tests/pngsuite/primary/s34n3p04.png b/impeller/third_party/stb/stb/tests/pngsuite/primary/s34n3p04.png deleted file mode 100644 index 9cbc68b3b9d5f263eb64bca9ad8bdfeae8205f63..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 248 zcmeAS@N?(olHy`uVBq!ia0vp^N+8U_3?xrvihTr9Ea{HEjtq=#3k+XOiwE+Vi=8|} zSXfxfe{5Y00D12$B>FS zQ_pYYZBP(ky%4X^8GYlzZKG+@7dEqqPBxHOE~fV1{h+qq6V*Svna^1j^)9?Ct99+6 zN12I`jtXnzk)}u!%RD37KjJGY{QS1{OXvRBqt2SFsin;wQV|yPQK!t*<5f${SKY|E by&mjqj=t@%(q8BXw3xxu)z4*}Q$iB}CDT{I diff --git a/impeller/third_party/stb/stb/tests/pngsuite/primary/s35i3p04.png b/impeller/third_party/stb/stb/tests/pngsuite/primary/s35i3p04.png deleted file mode 100644 index e2a5e0a6595f100edc1f79a3e0864ad6ea0d0121..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 399 zcmeAS@N?(olHy`uVBq!ia0vp^${@_b%)r1n`@_6HKn_c~qpu?aW7`757t`W_eCA>& z&kz9yJIm7?|5Q?Gve>sQ= z+doX6Y5h};;jD1&3&kR*!|w$e_FSCK!TZ;uv#nAtd0y%gzGN#yUNMtr9o7cw{d`v} znPPXp_t6M&TexoXtroF))1FRQrM1j3tI<%mZHpV5tekks+WAwqdfa)ug=wLE+m7oy zMQ7hT>LK{?oy%sWdO^0lW9vk3*7N!L?zPr!EpIN@4l($9_RGP?h0kp-%f1)C*BQ6t zSkfJR9T^zg78t&m77yez7dv@| zu&}U{|Jb@1$W;&U32_C|{~5{|{{M$i4CVjJK};YYC{Yd)E(fu`?q{2n^e{*@3Z79)POXW#oDtF1yr`=+itVv=OktFdG6g{K)mRoAX+EY7>z zmhXBt<&Eu!6r*o!cV_nNo&Ccp*=?%nxjo?-PXbeTw3r{SnJ?StNM`T3!o6nKZi}v&Xt(4rKl7t^ V?&lUg^#FQ_!PC{xWt~$(696v@lAZtn diff --git a/impeller/third_party/stb/stb/tests/pngsuite/primary/s36i3p04.png b/impeller/third_party/stb/stb/tests/pngsuite/primary/s36i3p04.png deleted file mode 100644 index eb61b6f9a325db7d967bd796d3a65494bf6b7754..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 356 zcmeAS@N?(olHy`uVBq!ia0vp^Dj>|l%)r2yqPf);$YDu$^mSxlY+GRXVp=?q&s^-} z8N$NCQvPG>Vjx#Nz$e5NNdIRjXZZggLNS#8F9$J!e4s=*NVput`nsRZ8c5&tba4!+ znA1CPFITgHh@1WGUzfMPP>Mb3d2r#rz};nBJ2KhD%L@z2iugh<9M4MfUF6B#_T+KJ zyS#u&i*AX_`$J4FwOGR3+lYu(xyz9^Us?I$Nzg8@_uT%B&fwPAv<_h$1 zKV4X~uHW!2nO zw^mgdcYZzlI&jNId!5cjyapMo+SY3yv3S|MG5=1ghQ&wM_et*;#P6@Vu`=r)^ENJ> nM6Fzb-Rz~M-&QV*`e$h`)grF|l3?zm1T2})pmUKs7M+U~W1%@xC#RK`w#ZI0f zEG#VLKejFga@7NTLR^9Le}-~~|NkKrL;3%55EIA;N|b|y%R#KK``N64bh)RCV@SoE zeX02TU7fN^(b&f@AC0IF4)lp>A zamtZ6X3lW8^&N+fpX;N5a1N0dE1b9+oO)I`@yuDpE^(oyK}Bq#(;+7|2{WUFjT0Lq lq)!ST`XV5f-^|R;z~ISp;_}2b`+!bh@O1TaS?83{1OP(LROkQz diff --git a/impeller/third_party/stb/stb/tests/pngsuite/primary/s37i3p04.png b/impeller/third_party/stb/stb/tests/pngsuite/primary/s37i3p04.png deleted file mode 100644 index 6e2b1e9b79ba8ded32f713506b543f95c8720615..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 393 zcmeAS@N?(olHy`uVBq!ia0vp^svyk5%)r3tu$^}qki(Mh=sg+HIV-2>Eakt zF(-9`wNR6RK-+&o*^hxQcxC(!_~<1(WXyQ8cE!t#;Gj#lmM+`0{Ul?NhPm`7gP#(U z&TKlpT{>aX1vlpoG2ywrj9K?;d1~@l8+XmqNYD|GnW?j3_LDi&_Gk5ZpVL(C5Y*hb z;_)GkI~OeSB#!?*!4ts2={s4)Kw+AwsIZx0;sW~{6Jr*4M-?k-J?sdRs5Q&mv0%AZ zytIqJyc-jCPuY6yD{r}_O<9KFm9UNB@vD7vZe722&ATSp?!|BMy_))a()awI`Eg15 zcb}IZ6e>9H6slHm#xw|b7U^#ds^C5mec`FsvsuL_-L{JOW$W&>5V^;FS zsTXzmnjHk%9_laj{iXhZjaU6f3%5cQ1CLzmy47pf9C;MBe%0{;2EMB|HJ5HS=D#U8 zTcE<&;mf3XHLadi$}J7S6KsC%TdZ8VLMtWoP@;^%GPWsSI*kflz0dEPDy4JU==ewT z$8HrlSvDN~XJuI*Zalg3>X&(%0bi?r*mu~p# z*kbkGd%RM6vU;~}yPmpD-2FE9?b`>wofE(7z53qT-=ZIGukYz~KPva4hT&JMP+%DE SUKyZ=7(8A5T-G@yGywpP!HFRN diff --git a/impeller/third_party/stb/stb/tests/pngsuite/primary/s38i3p04.png b/impeller/third_party/stb/stb/tests/pngsuite/primary/s38i3p04.png deleted file mode 100644 index a0a8a140ad7ec7f78f5b8cb398f54233e790fe7c..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 357 zcmeAS@N?(olHy`uVBq!ia0vp^Y9P$Q%)r2?D{_Pz$YDu$^mSxlY+GRXVp=?q&s^-} z8N$NCQvPG>Vjx#Nz$e5NNdIRjXZZggLNS#8F9$J!e4s=*NVput`nsRZ8c5&rba4!+ zn6q@EJ$I9XOxyf_5svYTT%(q*Y;ODGw$)5_M`m;PZW9x8Q*O>*8?9X5^PP9_c=!F& zj}P}&avWz``7H13GiA$f!58}0_y?X%x||YHuGu}U@%Fz$#r)47t&6p-nF}}c@lTBs z^1PZ{v*FFH8(9sEQqM(?B}LEJyE$O3_FS z$q5XNhu)=5Tjidh!fa-!aP~lxV%mWM7U!NuQRf`SX00}f3tT>iIb{)A35DHSJCk^B z9JyieHiKao|6$!5cY4l9TNo}lec>oiL(-j$qysxN6CA{uC#7^J8uHKH+k8^@!3HLV YPnp`y=hnD91lr2r>FVdQ&MBb@0IdL5LI3~& diff --git a/impeller/third_party/stb/stb/tests/pngsuite/primary/s39i3p04.png b/impeller/third_party/stb/stb/tests/pngsuite/primary/s39i3p04.png deleted file mode 100644 index 04fee93eae400e745534756b48f5420cbe4a1d91..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 420 zcmeAS@N?(olHy`uVBq!ia0vp^>LAR*%)r3N*;O+E$YDu$^mSxlY+GRXVp=?q&s^-} z8N$NCQvPG>VvyPZpAc6d{hy(n;s1XK#Zdmg9K;0jffD5);c^h`>wY$Cpuv)!E{-7; zb8;sb7Bw3PxXwR5nPsxbIbI3B13h{Zl-LU%&pWl%I?8Km-rd09oy#9GScQZM32{6% zJn&)b?*IA=s?&@YhcsVH;1O9g(`9z%6oU2EhY-&tz!*HGHjC7g8ZRKA?r5euu=`tFBX7AyHo&^hw&tWyi;3*RQj<5NC7 zD$9QAmr-d~@vfp^>g%_&3Lb_R^iP%F$ND|O^6>H9g zXp(yF?QYZiJMI0h)x3XK|Fr(u@2&iL4zKdoR{mw=nP&Qp=ij3Tz`$bgboFyt=akR{ E0PqU5-v9sr diff --git a/impeller/third_party/stb/stb/tests/pngsuite/primary/s39n3p04.png b/impeller/third_party/stb/stb/tests/pngsuite/primary/s39n3p04.png deleted file mode 100644 index c750100d55fbd07d216bcc5af538a83b9f7772a3..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 352 zcmeAS@N?(olHy`uVBq!ia0vp^>LAR*3?%D%eb|5$OS+@4BLidG0>c;6;(>hTVkgfK z78aKBA6plL)CTy3xB}__4CM^}|3fH-^8e)^CXf%5Crf5MCykT z!yOH&Kayq_xt_}isYhMd#%sHX&9!mIgr_sq&#dB<+^ar$Z{X$9)vls_k8f1|*}8bi zpB=vzEdR6P)rUoYc6jZ*eQSQo)o-o)F5h}-kYN6lt?pKC%J$H%zv=qU-|SMRztaeP zGf~`5;@h^18+60tYao5g)5S5Q zV$RyWja;A|lo{eZ(@L`p} zS{LWU6$LrdObg}*USsKY346=g=`j6{lFBTGU2~>go4o6%r;zgB(0Rug*KVDo#;EA% pAydNHT#*-Kd$uF{lI;G>I4-d)zk<)z6+mAyc)I$ztaD0e0ssYck!}D0 diff --git a/impeller/third_party/stb/stb/tests/pngsuite/primary/s40n3p04.png b/impeller/third_party/stb/stb/tests/pngsuite/primary/s40n3p04.png deleted file mode 100644 index 864b6b9673b3b331f2956ad2299b7854210cdb41..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 256 zcmeAS@N?(olHy`uVBq!ia0vp^8X(NV3?%C=ER6$FEa{HEjtq=#3k+XOiwE+Vi=8|} zSXfxfe{5Y0FS zXD@8zZ7|?*x#--bqd09rVn>dvna6z96-Ml=&4Mcg{<*&Jc=+gKnBVfCtuiaG{Fsq+ zN5I`d(XC(dB13ART()`KdT#Ey^-ixof4?!Sz`}fsxz-Y$_FSPy%NW9)eC5gyx=V3r k20fUweGf1AoYPn=%bN?oV&q3&xZP#{- zd#wGeV8okcyH@o)cboHb1rrf^jB^?sG2T2?@G1n)CJc@hyhCf)&E8%^0~1oF;v>7S z;6sBJiji@r0Yq=mmR;)Ojr>$W46R-Sau7HO7xZFtI1{|4z%-nLbW=B z3o0EAC)h$;8WIR1rohQ#w444G0#1m$Dj66xet|L#R>4C36e7xMM0T)(aSAa2XfV)D zcEU#my$(kCaRx~QL^LgQ_;=)3gHxFOXBZJd{maEJLqY3H<>rDuET^1Yth7z0>+1gi X*waT#qxC!3? zz50+^bb0-!+_LIFVlkOn3uCR$oXN=hfy)oYZM=LTO5_V)JX0F)aUb#`{Px3~8K z45Cm8G#cZ?2?d2q03|3C!N$f8gK>%=kzAFOs;a7HXJsOhsJOUTC=>!T78DfJ0p#W7 zO#|fS<`x6+csvb_-PzgMH*VaxejVY*&B(||Pfw37i%v;TNli^nPEKZQ-I|z~7@ruA z92Sce8ykD3^-OehbYx^?czAeNSXdrFNJvO(?(>_pT_EMw{BQ#m>$Sn!qg3OkVyw9#4k*05u5X^#Wxw8I%DBhvS=>0kTjoKsJnz zkHa;PN~LfWvNvv&lMD4H5CQz1ooNFD1Ml8_0AO}^ckkGN(bYw&Ct|UTmX?<3ckj?> zcNB^Yu-39J8}w6t>_9>;(#;tUCqEAdt`EKrw%G5dUucga(q-6VG498*{K%rij4wv2 zoEfW%jzHYO{1#TLQQ`9W=k@mzoI;b9QinWz_A0mp3_C3m`>c%tC$KR#Xus#^maPqG>$Ip&r<7PP}+lo3xrqrK0bX_;b9>)$CG zYiDOPWRFqn6UcGDpA~Oj9{Dz;6-NwxejzAm$n^KEA< zw~yjS7UP676`?(gNA_%Ti&?JTRiQ)ukN>niNvk-4K4Y-(eYokz+n%p00U^e=@{X9+ z)rI4_;#GQ*EM)A{+^B9WciOLZ)vuJAK75`z`)0L0X_*pJxWq7Myp(uq&jr<7|o zLnIyhWR@l$6GSYpXJsSmv@J23K`FjDRPrN<`4!1UJg1FhP}`#*W-iAZNy`g6qaeJ622^AeZriDs|F)fz44lCrgvCaK9(i)p7!%%LO z6LCs$SZ>MfJV}wPPOIFlR1QvYe%|MK-}ia{df)f+{c@b0>@`sOC;-qn<$!ZVI(j`f zDj^kjGph?}%2e!GEGVRlO(*lc0TkamJGz}*TU$#c5*r&IR@vH?l$6w=(b>AXMMXv1 zx04DB3jtEEU%$?+^=WBow*aI7Nl8fw2?+qjY&IL9hsk8d#>O5wLZs8_(b3V7k&zD74p)(r z6#W5wi5%j-eJnyYfy3casnmdg0P-cW&PSbeL&Go(2FJts`1l+vI)>xmtn;h^Zo*f9 zFF+!ZJUl$y-Q5WUf_a*`i;K%{;ch%0Z)Jtm(t1?CX%ln~ww zvI4CLn;>X`*DqdZX)#srsxB=p0rJcP5Q$0`7Z=q_)!{xs9cb~i;5IBQEI>6tCCtyy z&(6+*5T>W60ceneP(Tm>WPquush*y(ii(Z4cCD)F{LssxzP`RZfO2EwuCA_*j*dQn zArvZ!Mq?a5uB3Dcpag{?+Sxf^v1cR6WET~cs;a8FIk{LYE-o$>i9`TRH*Vah2Ph~g zm;uPo&o2hx^Z7bDJ92Y#uU)%#^(w-Tmz9;3nVA_=7L%5lmY$xTnwrYkv?)0`IX*ca z2^NbL7Z-QB?Q~2`OjJ}7l7z-JS_iG7g0sVN0Uz}3~&+1VKez@Wfj%a%*<6dWBL zfByUl>%J=rqtT`{H60urpc%{q%@q~D69^Qz2T+SJUKdaPBUi= zZ?YsBS+ans&A`7&dFaENu<+a*|IhOD!Fy5FU2($py#tmf$d>!3__F$7Rx`n0Xd`(x zXNPrB#Hf{`Do)y8Y}67x>Fw{qlr}T!YF_4_IUZux-oC*~jH=jeIQPaX!`r{y1WzBl z(m6wK7D&e$Zg(V>@2C;Wf~LRR?X8$9#=q?zc-|P5aetE9NbV#QTKGtM8suGqBBx#3 zxo@Q}hx+TEERS}!iYl~OZDE->d_uXdy?`@|M5G4?j`=QYAGR)#`a zvNJkHwy@!`Y;;2I`Sq)+{L9pEnB}|hM@{;rXnr^QKIxlagyn~gH+uDas6JD-A_hPD*sh|5-pZZezZNI zTBjQ-?c5``GX0PwW_dm>8`Y+1#_9&A`Q}k652Y3f(u)LcJ9%$iuabnh9D6vUApEqF nBzr1Qcj<%PHtn77gx$)ZQM<+~5{$JW7KKwMop80bl;r;b+-;m} diff --git a/impeller/third_party/stb/stb/tests/pngsuite/primary/tbrn2c08.png b/impeller/third_party/stb/stb/tests/pngsuite/primary/tbrn2c08.png deleted file mode 100644 index 5cca0d621047cacf1637cf7d77e997d51cc6b15a..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1633 zcmV-n2A=teP))6_}Akuqmh*5X4X^rIbM;BH-v?$kU01p_ErCl=u4~ zL7fbz&H-~9!(}3aFu`RZW&unzb50QkgK2!RYsr=o3t~2tT^d3tzx&+X@9uj)%)Vj6 z{|IpB&Yj-g-pR?y{}^!Z-o3WAwzRagh=_<-fB!T`$I~7jJzDL9k&%A|7#|<+>gvkP z&5eqRQiO&cb#=`%Gt&SnAO*+-su`xq!J$j392p#Z_UzeG0aH^`SFc_zD=UkSkAGjG zNZGY3d;R({hG_tbfDeIqE;r1MUWVZftDkNW!h zs;jG$lamt?5{`w2W;;5Ta5(kAS>OWD3k(34fhLB@x3Eyz+ozkGX8`3uA)t^*Vq;_J zARVu%si6?50P_I-{r&0b>GXAy-pZYvJ_kMn+JSRiZt1pd#bWVg8=D~(>oza}`~v)> zM}7e)8HRFA`iDhFM^mix0K>z>DwQfHCnqyAGc`3;6%f#2ZhqFvO5^8uBq=H7FUwFU zT6XLhvbG*uwdz+cchJo2l$~8_P|)B15EmC05)$I?@88+kIS+9E{(W+tk&&UwQ56&v z(A`o(AH0rH_Tk~-0RaJGvDnww_wL=h^8j=?>6z)a6}IeCY}aO2b*Z|fq@=L0@WhD| zsSDdty^(jSiA*M&bM5WzEtks;bWvVj9&?GoHDC(zc6o7eG4)bKMMY_8X?Aw@u?6h# z;lshf`|a#hY<3Bkd&JHz$kWs3`0?Wg0OXPTF&CY{G^|Igi;9ZKH36!ss;DvOr&_J1 zx~1t&sZ>(M4;(lkk?c1!`x@w{b_L!AqU`OZt*xyF0OXE$lBa8(+d4*aQ!-UaQ=##! z_w+pDSx{e4UteEaTT3@Y0IErFaIj1kyl&n1z;8e!i?yF&L_nC8m3(Mu$N-?Zxp_n7 z2DAX*=>Wn$A?4)L>VvOzv*1+iR99Em(9l2wMRh0dROro{PXe>RH6x>UEG*vQa@TOV zQlU`t^yyOrfcExwL7@Om)cL>za2|4U%W^%=c<5%V3NB?X6h0+EjKsu5@-7xf85_?~ z_(n!?E-s$Cckkx$Tm=FtNq~V#ChO_xan5qaMSv9N2jF-3*7yqRh3Ek)Au1KoO;8fV zprIvzKyXeU{%KoVxrc`zkCz5CFib&h?McIcn>TN|Dcy8{RDkAq5^L3Kg|$LmUsXU< zBBJ4(W03sGWO5UeaSET!R&3c4%;9JOl9gkorlAz5VF1mVSE84$J}Q`0nqU|>1Di@4 ze5+F&_hR?T%1UBTp~Aw#cJ55qhku#R59RT^^+74-!fSSE~<7VgEJh?uUa0qm; z8Ez47t52=QkANEJ0t(=x^_iQy^73*TT0XxG7zGZwyYCSQ1YB+maEUU=$|_zAke{D# zTyIS3>>TO5U9}y3y6WrNG#}mt-ZU1(ApHmo3|zUggX&{s6zS#V<>Yh_y5_4hF$wDK z?p_S=`P`~WeyGw#>B+X26iZPz^D41kPE3=O0g4z6qf*N)=*PsGE4-+(Dp2{ zw2Zxeeeh*hh5=+_V`EuaSwV3@IIpj54kWn}jYdNZ8e`JrO`E(~ENUAnPWZ-+iMMY3 zY|zhwfLVQ0wY0S8#Df-Q7G&8n5<6nh7)vA)kx0bn+nAbqczMM?d^oX?lf?jYc4O#< zON|S4tujMN`;Hh?Jz7|PetuiGZY4pbYg^pItKUTv6B8tGw0Wp##Go?l diff --git a/impeller/third_party/stb/stb/tests/pngsuite/primary/tbwn3p08.png b/impeller/third_party/stb/stb/tests/pngsuite/primary/tbwn3p08.png deleted file mode 100644 index eacab7a144cb54be28f7a5952a2fc63fd883766e..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1496 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 diff --git a/impeller/third_party/stb/stb/tests/pngsuite/primary/tm3n3p02.png b/impeller/third_party/stb/stb/tests/pngsuite/primary/tm3n3p02.png deleted file mode 100644 index fb3ef1d0c5aa4658cf1c1383dc6e74fcf09116d3..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 116 zcmeAS@N?(olHy`uVBq!ia0vp^3LwnH3?%tPCZz)@o&cW^R|bavaI|&4^Ese6b4ie2 zFhl4n?w#tEK(2_Vi(^PdTyhFSQ-Fe;PETlaHzR|#xM-r7v7RGPp25@A&t;ucLK6T+ CZ5hr0 diff --git a/impeller/third_party/stb/stb/tests/pngsuite/primary/tp0n0g08.png b/impeller/third_party/stb/stb/tests/pngsuite/primary/tp0n0g08.png deleted file mode 100644 index 333465fcdc9f10f7f25b2d999b558f5421983ecc..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 719 zcmV;=0xe{x-64Dw5ISbDJfGPNXYF1fsnx~?7~M3eMD*m6rOMo0;2$rUW>-c(^**S*Ycd0zuX+u!1|S9WB+#F78QX<>lo? ziQz$h-hRnIWoQ!H=elq>2fX`s$g4Md}>$Ugu1e*ygBpcmqZ3DNb_5HNEsBtg8Fz$MsRgsRcUEt!R)#7 z@*=Hdg|%Ijgaqfp6x>M8@W|(*DezA>78ATBV zGiJ?6^pq7`pj8TmNNXtGQ7M(fr#kNOGpUKtO+a# zE}lAlTDXoRkAt?j2v0jgK~H>oU`AO)UQprWsr_L}qI~YALPA1E5ej}SPEJnGOv?!@ zojAEOR7Ob9QdC4?8%mP9keiy8ksVOlKd~`Xk)KCEK=mA2ntaoimYx|<+}mFjqR+?8 zZTJ8q(f(PLp6*}V-CGiDD=qBv7F)8vQWR0&(VZLQZ(jZjd%k$Tpt-FhJuqOwUu^jT z1pXapZA%Q=jx2U BP!RwC diff --git a/impeller/third_party/stb/stb/tests/pngsuite/primary/tp0n2c08.png b/impeller/third_party/stb/stb/tests/pngsuite/primary/tp0n2c08.png deleted file mode 100644 index fc6e42cb420fd08ecd78340256157fa5f8a22934..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1594 zcmV-A2F3Y_P)u542F>)wJ;-zU<0Ta5X4X^rIbM; zBH-v?$kU01p_ErCl=u4~L7fbz&H-~9!(}3aFu`RZW&unzb50QkgK2zirzKlPEQpyV zJ88Z&UwhB*+;h+QpNot)Z1^7mZr{G$*Vi{SHT9YSckkY9Z*NaePmhd@j0*@zcXT}E z>DjB%+#enNPk@PuiSF+1yu7^V=xBLZ*b!IPd^0mOpafEZETD#AngxPxg<^DQ=;_m^ zO9f0%PhYumrM$d6AtB*?xjc2}&YX4Y${D5+CG4W_vSdOD(DVN&-oB_@QeZU}a320`R0!vG! zgF}Y7c_vT+6ajLvI4&-Z7Si(C+FA;c5-<-iFfc$*`Z-x=6;4i{1D^pMz*&|p+q$(x zB)Vj4J8WQZ3z!6c0e;dUzW@{rLwimBm&L@yP^|L+BO@b9r7|}+H!CYEEiFwM7}#lU ze#Y8b?eBj$IXU%j%aqGow{IV|u^C^v@>iA}GBZ1AZ=V(%{LdfavLmWItxn1d>CixU>w z(z&H&q%@~cmeiGMuLdu#(_V!Qg$)f2b#--gLIj|kgoK1hr6FtAeh>TxG#MD|V;DCe z+}c_;JUpxi(9+VfzG^*Mf$y{cVZV^}t ze*#wx4d1b}e2ZmQv#dlY6hC?LL=T{&qk~_>M>ADEa39m2fL{qvo7rko-xdG82;t3ZKJ~Z{8fjKg!o$ON?8wlCe~HHnv#{{d1tq7Cl~vHp z%#3~jNv`NUkyhKvAxIVA8=wbFDWm}$)k06v92)E2}b{Rj#QTCt*&@?&Ti zD zl4Z+2B9B~dgtK!fhf@n&;PLjfw||-Og6SEVLqkKcv9UCP1w{gUs+$RUkfbMoF`VR)x@ASCQaVB z(Z|4m%7&5?v0+2f&6_{#^|K%#LswL-t*u(|pn;hUX^xb{ju_O&VzJoG&5g&iH8u70 z_D*>4U~(ZRivi~B`tWs^S{JHXMW%x09Wf|-G_d^r{kLq{LV`-iwz!9v--{+ECrRLF z@=(%OV07*qoM6N<$f`h#14gdfE diff --git a/impeller/third_party/stb/stb/tests/pngsuite/primary/tp0n3p08.png b/impeller/third_party/stb/stb/tests/pngsuite/primary/tp0n3p08.png deleted file mode 100644 index 69a69e5872234d96cab4cbb017a0933fbfedba81..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1476 zcmWlXc~H^`6vv-Om}J3dn5OOMOgg5aIm=3#R_~R8*9gm+#ueDlIJqsFg@0;tH{|b8}HqQ5Jw$Ebasl zi9{wQqx{s={QP`?_T1du%K+m5+1c6W&Yc6O&&;#KYi za0xyG{0*U@p&=n5OeT{~rxS7s!NI{ht9Awk26}jqu-LA432IQH$P*->1%a<=E7fhf7KmpiNEL?|$g$1|@ z&=;ZU0fm$KWEP7BFz+mN_CD$D>KXyBfJUPQ1qHz$U@Noz`t>V3 z0Y5*#_4Rf5=AAr|%l))v3zbTRZXmD;`udA>dIa18XhRr(3s9%iK@&&<11kswKn|(| zCKfuzi4TRu*jp4GWDgcp#{666_U|?<$;@lL6fyrT=v@~MF43;U zZ$6b_)kkb?TQRlFx@L1DFHuvCdiGRV@qSb5o3SyX=dqWIo~SD}lgtU8-0hinIc|l^ zwZpyIy{+Kp3qku~jyuQS{wOJTffqk}ylg!rGf@@8WYp~qRTEe@i_6M#f}U2WR@4MfM)1HJRjPCMx~ z7uM(-p3*y>&A)fhi1A3;cS(L+J1;&pqWL&it>CX0-+eBqO{=g-lKzNGvG(`sNqtRF zeg3%mDzvL|F3Eho_0DyETn8H%dc!?dA!#0>Q=Vr9#{-Ga&|1#6CAW+Q0sfY$`N-&; z^0c=#@%4)8jy6X3lL6u3JAW=?c#tm>K2a$#(Q?|Uz}T^+pWADD@MB|q8>&Xr948aj zsY1|Z7&ad@u4wr4W%TE{WS7U&Rh^yI#(OsCtq$vQ3|A*M_uLm%-A~M)I@BiVUyS#YR$&g!`CNx58tIE z|3D@=NLJY$Q%;J12ZU?1w#&^2c5EzDN05DZLoa2szJ*SEwwg0|Mem<$Yn1cWcz^cq z-}lxrB6iz%k1R&3=>`|KS%};k)l(YskH!jI!g#-(&O>3hx)5(GX8X3fe_&q16?&Zr z)72bEymR&OySDOI3mZ0C3tSEiO=8X3u^BG}uBukDWrMyZ$LQ%Y4sr7@il=hRi%!IK N0G~qvls0mN@PBi2fm#3n diff --git a/impeller/third_party/stb/stb/tests/pngsuite/primary/tp1n3p08.png b/impeller/third_party/stb/stb/tests/pngsuite/primary/tp1n3p08.png deleted file mode 100644 index a6c9f35a86271c056e955f637a8769381eb03827..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1483 zcmWlXdpOez7{_0ijw~Xj6ZLfH5mEEBP|@KrEtkTuS}m>SI;>1?8{6D(v$V!$$}kk^ z;zXQM92O%H=ShmnbXw(ZQ8_r3^Xq+{_x=9)zVGMz<@$KL>7tBL0HEvPj`Ks*k)Uu?Q@xn6z>Z3lMM-Q#6tfUZ$jg9xOySS8oYSdsZ?58S}Ku90Gh5`xl#{MR8%wt zP*_-43Lq2;*RS7_pPzr}(xrd%E)}>Vg+~H{0|EmB;S209 z+JEZQsgoy9!bij)vJ2CNNF)M$vKCsqg}Grc6c_?OKR+KIALs+qBGU~U&cS2w^z{7x z{X6{3T?w5=n_Ror-Q69U!8X)ZUHvP8K!G~|wFu*%2C7slr~p15FS4}-9)W zgA1TkDj@~(fB8jCjUG%S0R(${)B5}SM@HTPu)4duH*Un5nj+Sda5!dbYwOg=2pWA9 zg`xngv~oCl+wa{*R;b1Lc@x0-<4`CPvRn@w_UQh+Ac}Mk^OKb>f7-E19*W;}xs}~! zRlIciNyF__FM9f7*5I)-+cbPa1|k-TrQvsONlsb*)pnz$h0|UcNA3@z)MNfwE}Uv0 zIbFH+_Zk14HEp3y&g@1-ft^E-J#CK+WhE5TXruYlt&^-N^P3g#I=H!w`R}L=sT9s{ zCuD1uhCdG)aY*zhVd3F}HZMyY{g2C62iLwz8h1Q_?PFhSvbid>^O4P?YqRlh((eZd z#_aspCH=EBx%*CRO=qIG-J;L_AlZKBgiu)@&Tb|Ii=AapW?Zqp>gYA)sH#J5XB+h; zk9wYVqsyC_bvIrV9&w7WX>VWUAVpP~na{j&$UYNXX^m&}Uyx5RnnjA&4L3VdD!1H_ zD#Iq{ZueA8l;TIb`kpn$X5SsBHj?FpV!HrYcY~@^RN}pDGk;X^V&G~0qorYaOP*rv z^qN~4t3n!C(Ta6lUVkRto3S5$_!4xZm;=Uxwl-UlXk<^TD! zwrXx-kZ%7r>OqrnxrwlgbC>i*G{g?VCRx6GHc+-!v-|w+aMq(Z)3<$!sgDrNgW zt{66;txYft&kQV}Qtm74k`-qO{C2WMU5|#0wUn?oyC~|ghKx56YPk5`Xp_O#x8g2M X(5+n&l!#upArS?SL*BSr7fRay;V71Q diff --git a/impeller/third_party/stb/stb/tests/pngsuite/primary/z00n2c08.png b/impeller/third_party/stb/stb/tests/pngsuite/primary/z00n2c08.png deleted file mode 100644 index 7669eb8385172325c399f3229cfe834f886fecb2..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 3172 zcmeAS@N?(olHy`uVBq!ia0vp^3LwnE1SJ1Ryj{$|z@zQy;uunKi&25+{%6Mj{~3NT zfY5g^iOI$zg{%daG8T~8Gz1(pwSa?x0cZixpELm=(-{~zY2*W_>0lpFKMoM4)4~E2 z)2SW@=%!OQ4zQR`#UR3NI<+joX*yLbz-Kz;kxamJ3L}}Y>68Q!QKpk^0kNi&7Y8Jm zPDUJ%W;&@sM6&55SwN2I#K!@drV|+lP)Gd0N=r*M+Cfdcn4n{+d)GIuBfe}0=haRfYbdY4|HeAW8JeNk!}u< z>b4FPx}gJ|u3eE@R|Qyfc?U*a*nwS_tjMJg1H3vP0MMC|F?z3LnBH6wQvU~l^-l+Q iz3w1HzgHBc=YRuMlTL2m+d~-u0000811+tMcYXzcm?aHZ#?gdL8ij-oNLTu@go{<42!=C2J(%l`2yyuQm;^LM9XM_s7MQs$ow Yp_Bdc41*5p0G-U>>FVdQ&MBb@0O=}Ly8r+H diff --git a/impeller/third_party/stb/stb/tests/pngsuite/primary/z09n2c08.png b/impeller/third_party/stb/stb/tests/pngsuite/primary/z09n2c08.png deleted file mode 100644 index 5f191a78ee5601a45f1add2a3ad7a77b7b1ae0f1..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 224 zcmeAS@N?(olHy`uVBq!ia0vp^3LwnE1SJ1Ryj={WmV3H5hE&|zdeNJ&K|z4|!mJGk ze#?uM?J%qpNVJm;SDAGELf+biOrjbV{C6uqc^By}RQhy!A*198PSpVRZwtidR0X(h zmlOPO>811+tMcYXzcm?aHZ#?gdL8ij-oNLTu@go{<42!=C2J(%l`2yyuQm;^LM9XM_s7MQs$ow Yp_Bdct_kk@3Uo4qr>mdKI;Vst043R2BLDyZ diff --git a/impeller/third_party/stb/stb/tests/pngsuite/primary_check/basi0g01.png b/impeller/third_party/stb/stb/tests/pngsuite/primary_check/basi0g01.png deleted file mode 100644 index 96ed62dbed7614edfd0d3cb58b1713c0a067eb9c..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 391 zcmeAS@N?(olHy`uVBq!ia0vp^3LwnE1|*BCs=fdzmUKs7M+U~W1%@xC#RK_lN#5=* z4F5rJ!QSPQfg-0oT^vIq4j-NF$k*b)!7}^ozyGJ-oIDabg|X_u>_Q(?)khkuR!La7 zu*~chsLPP|}S^f!skB7DWF?ZskqvJ`CRNW>pLl^tak&^VnrV)#A>)9kel<=2?=1C*pefxc2Lag8WRNi0dVN-jzTQVd20h9bP0l+XkKm%fiA diff --git a/impeller/third_party/stb/stb/tests/pngsuite/primary_check/basi0g02.png b/impeller/third_party/stb/stb/tests/pngsuite/primary_check/basi0g02.png deleted file mode 100644 index bb5309885175369e7a44a82b3d71c503ee0e6230..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 283 zcmeAS@N?(olHy`uVBq!ia0vp^3LwnE1|*BCs=fdzmUKs7M+U~W1%@xC#RK_lN#5=* z4F5rJ!QSPQfg*99E{-7)hu==~<~m@&;gasP?#18!r0uz8iWB6Y>=YJ}>w6Iq^h#K8 z;k-L#;k9$^Z^>}_zukc`I~jKX*zo!^M()1+nFD*GaTS& zXpm=kzseGVKswJ)wB`Jv|saDBFsX&Us$iUD<*U(7U&^W}%*vi1t3dlCF zure?xzopr04@Jou>b%7 diff --git a/impeller/third_party/stb/stb/tests/pngsuite/primary_check/basi0g04.png b/impeller/third_party/stb/stb/tests/pngsuite/primary_check/basi0g04.png deleted file mode 100644 index 2efd4876b56cb502102b73a24c678199e64f646d..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 252 zcmeAS@N?(olHy`uVBq!ia0vp^3LwnE1|*BCs=fdzmUKs7M+U~W1%@xC#RK_lN#5=* z4F5rJ!QSPQfg<*vE{-7)hu>b>$jhL>!|d1`q41lZTUVH+t9bgmC4W3N6|Y@&sq(yy z+<{mBcCzhb&R}q0W{_gIz|g?PV8T$qppwOVi}ypuVxUo~C9V-ADTyViR>?)FK#IZ0 zz|ch3&`8(NIK;@<%D~bJ$TqOBGB7CQxvq+$AvZrIGp&-r(9+UE*T6*Az%<0j$jZRT a$^^(Y2Wqf8*4_!!z~JfX=d#Wzp$P!N$w>GB diff --git a/impeller/third_party/stb/stb/tests/pngsuite/primary_check/basi0g08.png b/impeller/third_party/stb/stb/tests/pngsuite/primary_check/basi0g08.png deleted file mode 100644 index 23952137c9a6eff813346fa4db89480e77c87880..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 293 zcmeAS@N?(olHy`uVBq!ia0vp^3LwnE1|*BCs=fdzmUKs7M+U~W1%@xC#RK_lN#5=* z4F5rJ!QSPQfg%~6E{-7)hu>b?$jhL}!|Wh!$maCMUNossom zTJ?)FK#IZ0z|ch3&`8(NIK;@< z%D~bJ$TqOBGB7CQxvq+$AvZrIGp&-r(9+UE*T6*Az%<0j$jZRT$^^(Y2Wqf8*4_!! Oz~JfX=d#Wzp$P!Y-&#@t diff --git a/impeller/third_party/stb/stb/tests/pngsuite/primary_check/basi2c08.png b/impeller/third_party/stb/stb/tests/pngsuite/primary_check/basi2c08.png deleted file mode 100644 index 64ef3f844312534c6c9dc0643ef8df95f07fb676..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 274 zcmeAS@N?(olHy`uVBq!ia0vp^3LwnE1|*BCs=fdzmUKs7M+U~W1%@xC#RK_lN#5=* z4F5rJ!QSPQfg+)vE{-7)hu>b=$a}zmhuKl;g514&?c7hAlVoQ+;=X1hFoDm(DQad) z_ERPn4v!Oz8sE}SF>?qgJow9e^q)^910xelO+CY x-29Zxv`Pj;OG^t~0~1{X(-0#gD+41dQy|+MsKNBi+hm{y22WQ%mvv4FO#tY{R$u@C diff --git a/impeller/third_party/stb/stb/tests/pngsuite/primary_check/basi3p01.png b/impeller/third_party/stb/stb/tests/pngsuite/primary_check/basi3p01.png deleted file mode 100644 index a8599e9a5504cf33f6de68849961c0eb59e77883..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 273 zcmeAS@N?(olHy`uVBq!ia0vp^3LwnE1|*BCs=fdzmUKs7M+U~W1%@xC#RK_lN#5=* z4F5rJ!QSPQfg&NEE{-7)hm&i*$G@~^7BcYM-+d<4!Z>(l diff --git a/impeller/third_party/stb/stb/tests/pngsuite/primary_check/basi3p02.png b/impeller/third_party/stb/stb/tests/pngsuite/primary_check/basi3p02.png deleted file mode 100644 index c911ea9cc2d8793dd5be36477cf563ef6f592650..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 286 zcmeAS@N?(olHy`uVBq!ia0vp^3LwnE1|*BCs=fdzmUKs7M+U~W1%@xC#RK_lN#5=* z4F5rJ!QSPQfg*{XE{-7)hu=K|~gNTDg(5*9HR@U8VWIC~tIe-6W>%TjgStl4D?f?JpO22gg!&d%3atmq~ zFuY}E&5+kPz}f!9M*YHa>7_u+R7+eVN>UO_QmvAUQh^kMk%6I!uAz~xp>c?jv6X?P z6_9OUVP#-Y$a7s4MMG|WN@iLmgQ2CRg|2~#u7PQYk&%^wk(DWsYYx=#DW_c=sDZ)L L)z4*}Q$iB}ib!A; diff --git a/impeller/third_party/stb/stb/tests/pngsuite/primary_check/basi3p04.png b/impeller/third_party/stb/stb/tests/pngsuite/primary_check/basi3p04.png deleted file mode 100644 index 750ef69dc5a1f1433a83616453aa1144a11f8ea5..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 331 zcmeAS@N?(olHy`uVBq!ia0vp^3LwnE1|*BCs=fdzmUKs7M+U~W1%@xC#RK_lN#5=* z4F5rJ!QSPQfg*jLE{-7)hu=;=$lGAR!z_En)5<$Mfwe7()vY1)h1NswKM$_AwSVru z5~Xh46;*uT<2v4r*KT|h`#rZ${z~~4|AVY2RU3?~i{&7%6Eugk`*|>Tz=5t zT+U{T-$0kCmbgZgq$HN4S|t~y0x1R~149#CLnB>7;}9cbD+5a_Altyg%D|wI=ejD2 yhTQy=%(O}dLrY5wT>}$c1Je*ABP#Y~u z*B$5i8g-Kbl@=^GmZjsDF@@uXVnashqQHO7tU?NmX6z^Yeq=M;;pBB_u%jNt$P diff --git a/impeller/third_party/stb/stb/tests/pngsuite/primary_check/basi4a08.png b/impeller/third_party/stb/stb/tests/pngsuite/primary_check/basi4a08.png deleted file mode 100644 index 1b7b3a5821bc99816cb5508e544986596a951e51..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 263 zcmeAS@N?(olHy`uVBq!ia0vp^3LwnE1|*BCs=fdzmUKs7M+U~W1%@xC#RK_lN#5=* z4F5rJ!QSPQfg)a>E{-7)hu>aW$i-m5;d;^B;LQL35xMKuui=T?+2m+2r{MY-k4yW^ zp4a+XIofu^dKxJHzuB$lLFB^RXv zDF!10Lla#?BV9w|5F=wN14}C)+rYxgz@U)lx+;o>-29Zxv`Pj;OG^t~0~1{X(-0#g cD+8b&Aln?MVbZ$}OP~e@Pgg&ebxsLQ07Pz2rT_o{ diff --git a/impeller/third_party/stb/stb/tests/pngsuite/primary_check/basi6a08.png b/impeller/third_party/stb/stb/tests/pngsuite/primary_check/basi6a08.png deleted file mode 100644 index c12484fc58fd2ea0ff4709c6c5093af224580faa..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 298 zcmeAS@N?(olHy`uVBq!ia0vp^3LwnE1|*BCs=fdzmUKs7M+U~W1%@xC#RK_lN#5=* z4F5rJ!QSPQfg-t{E{-7)hu>Z^MG{133*2~v=nBdG1=3ba_Y#5JNMC9x#cD!C{XNHG{07@FuB8tEDu zhZq@K8CY5Y*#;I?1_p&Z*Huw8-vH$=8 diff --git a/impeller/third_party/stb/stb/tests/pngsuite/primary_check/basn0g01.png b/impeller/third_party/stb/stb/tests/pngsuite/primary_check/basn0g01.png deleted file mode 100644 index 20f6404a201c49da0a7118c9484403cbb606132b..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 391 zcmeAS@N?(olHy`uVBq!ia0vp^3LwnE1|*BCs=fdzmUKs7M+U~W1%@xC#RK_lN#5=* z4F5rJ!QSPQfg-0oT^vIq4j-NF$k*b)!7}^ozyGJ-oIDabg|X_u>_Q(?)khkuR!La7 zu*~chsLPP|}S^f!skB7DWF?ZskqvJ`CRNW>pLl^tak&^VnrV)#A>)9kel<=2?=1C*pefxc2Lag8WRNi0dVN-jzTQVd20h9=YJ}>w6Iq^h#K8 z;k-L#;k9$^Z^>}_zukc`I~jKX*zo!^M()1+nFD*GaTS& zXpm=kzseGVKswJ)wB`Jv|saDBFsX&Us$iUD<*U(7U&^W}%*vi1t3dlCF zure?xgTe~ HDWM4f9Rpd5 diff --git a/impeller/third_party/stb/stb/tests/pngsuite/primary_check/basn0g04.png b/impeller/third_party/stb/stb/tests/pngsuite/primary_check/basn0g04.png deleted file mode 100644 index 166e7db2193ad12dc53cc431d58a9cf93b036ebd..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 252 zcmeAS@N?(olHy`uVBq!ia0vp^3LwnE1|*BCs=fdzmUKs7M+U~W1%@xC#RK_lN#5=* z4F5rJ!QSPQfg<*vE{-7)hu>b>$jhL>!|d1`q41lZTUVH+t9bgmC4W3N6|Y@&sq(yy z+<{mBcCzhb&R}q0W{_gIz|g?PV8T$qppwOVi}ypuVxUo~C9V-ADTyViR>?)FK#IZ0 zz|ch3&`8(NIK;@<%D~bJ$TqOBGB7CQxvq+$AvZrIGp&-r(9+UE*T6*Az%<0j$jZRT a$`Hsk2Wlu&>wF8;z~JfX=d#Wzp$P!M(Mab?$jhL}!|Wh!$maCMUNossom zTJ?)FK#IZ0z|ch3&`8(NIK;@< z%D~bJ$TqOBGB7CQxvq+$AvZrIGp&-r(9+UE*T6*Az%<0j$jZRT%E&<1z#ORIwA+D9 Ppaup{S3j3^P6b=$a}zmhuKl;g514&?c7hAlVoQ+;=X1hFoDm(DQad) z_ERPn4v!Oz8sE}SF>?qgJow9e^q)^910xelO+CY z-29Zxv`Pj;OG^t~0~1{X(-0#gD+41dBQsqCbD)M5)4m1+H86O(`njxgN@xNA>`_*f diff --git a/impeller/third_party/stb/stb/tests/pngsuite/primary_check/basn3p01.png b/impeller/third_party/stb/stb/tests/pngsuite/primary_check/basn3p01.png deleted file mode 100644 index 77c580b00ae6c173933e5f6f8ae3fd9d50c7cc26..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 273 zcmeAS@N?(olHy`uVBq!ia0vp^3LwnE1|*BCs=fdzmUKs7M+U~W1%@xC#RK_lN#5=* z4F5rJ!QSPQfg&NEE{-7)hm&i*$G@~^7BcYM-+d<4!Z>(lK|~gNTDg(5*9HR@U8VWIC~tIe-6W>%TjgStl4D?f?JpO22gg!&d%3atmq~ zFuY}E&5+kPz}f!9M*YHa>7_u+R7+eVN>UO_QmvAUQh^kMk%6I!uAz~xp>c?jv6X?P z6_9OUVP#-Y$a7s4MMG|WN@iLmgQ2CRg|2~#u7PQYk&%^wk(IH5u7NpF1N)l;-+>w! NJYD@<);T3K0RWJ$V7UMQ diff --git a/impeller/third_party/stb/stb/tests/pngsuite/primary_check/basn3p04.png b/impeller/third_party/stb/stb/tests/pngsuite/primary_check/basn3p04.png deleted file mode 100644 index f08c6e99d4b477e8ec7defd7f7cbb58cb815d2b9..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 331 zcmeAS@N?(olHy`uVBq!ia0vp^3LwnE1|*BCs=fdzmUKs7M+U~W1%@xC#RK_lN#5=* z4F5rJ!QSPQfg*jLE{-7)hu=;=$lGAR!z_En)5<$Mfwe7()vY1)h1NswKM$_AwSVru z5~Xh46;*uT<2v4r*KT|h`#rZ${z~~4|AVY2RU3?~i{&7%6Eugk`*|>Tz=5t zT+U{T-$0kCmbgZgq$HN4S|t~y0x1R~149#CLnB>7;}9cbD+5a_Altyg%D|wI=ejD2 zhTQy=%(O}dLrY5wT>}$c1Je*ABP#Y~u z*B$5i8g-Kbl@=^GmZjsDF@@uXVnashqQHO7tU?NmX6z^Yeq=M;;pBBE{-7)hu>aW$i-m5;d;^B;LQL35xMKuui=T?+2m+2r{MY-k4yW^ zp4a+XIofu^dKxJHzuB$lLFB^RXv zDF!10Lla#?BV9w|5F=wN14}C)+rYxgz@U)lx+;o>-29Zxv`Pj;OG^t~0~1{X(-0#g fD+41dV>4X?bD)M4o3Ceq8W=oX{an^LB{Ts5MD|b1 diff --git a/impeller/third_party/stb/stb/tests/pngsuite/primary_check/basn6a08.png b/impeller/third_party/stb/stb/tests/pngsuite/primary_check/basn6a08.png deleted file mode 100644 index 1f54e565df630fc54cf973677e9b7344d639cf25..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 298 zcmeAS@N?(olHy`uVBq!ia0vp^3LwnE1|*BCs=fdzmUKs7M+U~W1%@xC#RK_lN#5=* z4F5rJ!QSPQfg-t{E{-7)hu>Z^MG{133*2~v=nBdG1=3ba_Y#5JNMC9x#cD!C{XNHG{07@FuB8tEDu zhZq@K8CY5Y*#;I?1_p&Z*Huw8E{-7)hu>aW$i-m5;d;^B;LQL35xMKuui=T?+2m+2r{MY-k4yW^ zp4a+XIofu^dKxJHzuB$lLFB^RXv zDF!10Lla#?BV9w|5F=wN14}C)+rYxgz@U)lx+;o>-29Zxv`Pj;OG^t~0~1{X(-0#g dD+6OI6Cl?dsNo{Zo9#di44$rjF6*2UngB;aPvrmr diff --git a/impeller/third_party/stb/stb/tests/pngsuite/primary_check/bgan6a08.png b/impeller/third_party/stb/stb/tests/pngsuite/primary_check/bgan6a08.png deleted file mode 100644 index 6cb76f2b43ccd41aeca1eb05eb9863ba4b4129db..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 298 zcmeAS@N?(olHy`uVBq!ia0vp^3LwnE1|*BCs=fdzmUKs7M+U~W1%@xC#RK_lN#5=* z4F5rJ!QSPQfg-t{E{-7)hu>Z^MG{133*2~v=nBdG1=3ba_Y#5JNMC9x#cD!C{XNHG{07@FuB8tEDu zhZq@K8CY5Y*#;I?1_p&Z*Huw8)WG2B>gTe~DWM4f4eVQZ diff --git a/impeller/third_party/stb/stb/tests/pngsuite/primary_check/bgbn4a08.png b/impeller/third_party/stb/stb/tests/pngsuite/primary_check/bgbn4a08.png deleted file mode 100644 index 1086ccc09b46ecd656fecd5d459c657c15d5ae05..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 263 zcmeAS@N?(olHy`uVBq!ia0vp^3LwnE1|*BCs=fdzmUKs7M+U~W1%@xC#RK_lN#5=* zKpF^sI`6IrQeK`ejv*0;-(FhC#bChUdePkA%>Vxpx$D-i;fdSX%rmB{>MwFx^mZVxG7o`Fz z1|tJQ6J0|iT|?s#BV#KAODiDTz{1MFppfUfDvE~O{FKbJN(MtqOAB2C6I}z-5F;Zi c17j-_AlDqI;UdeM?LZ9-p00i_>zopr0M9&3#{d8T diff --git a/impeller/third_party/stb/stb/tests/pngsuite/primary_check/bgwn6a08.png b/impeller/third_party/stb/stb/tests/pngsuite/primary_check/bgwn6a08.png deleted file mode 100644 index 03a0a303d69431d20dfc9d6c7e4ed346303eb62e..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 298 zcmeAS@N?(olHy`uVBq!ia0vp^3LwnE1|*BCs=fdzmUKs7M+U~W1%@xC#RK_lN#5=* z4F5rJ!QSPQfg-t{E{-7)hu>Z^MG{133*2~v=nBdG1=3ba_Y#5JNMC9x#cD!C{XNHG{07@FuB8tEDu zhZq@K8CY5Y*#;I?1_p&Z*Huw8|fFnGH9xvXzm0Xkxq!^40 z3{7+mjdTr-LyU~A3@ojHYy%4`1A{`I>#8Uka`RI%(<&JZEiEl{4NPzm0Xkxq!^40 z3{7+mjdTr-LyU~A3@ojHYy%4`1A{`I>#8Uka`RI%(<&JZEiEl{4NP6%z+wC-k#Y9)WG2B>gTe~DWM4fysR}V diff --git a/impeller/third_party/stb/stb/tests/pngsuite/primary_check/s02i3p01.png b/impeller/third_party/stb/stb/tests/pngsuite/primary_check/s02i3p01.png deleted file mode 100644 index d84f40613e6b2aed5b1c3c0a638ea8599b57327c..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 210 zcmeAS@N?(olHy`uVBq!ia0vp^Od!m`1|*BN@u~nRmUKs7M+U~W1%@xC#RK_lN#5=* z4F5rJ!QSPQfg+-wE{-7_*OOCz{Qqyy3p*F|> diff --git a/impeller/third_party/stb/stb/tests/pngsuite/primary_check/s03i3p01.png b/impeller/third_party/stb/stb/tests/pngsuite/primary_check/s03i3p01.png deleted file mode 100644 index 51367f7f8e0bf32bbf096160b69349bf1ddf2c2c..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 216 zcmeAS@N?(olHy`uVBq!ia0vp^%plCc1|-8Yw(bW~Ea{HEjtq=#3k+XOiwE-AlDyqr z82*Fcg1yTp14X1gT^vI=t|zDbIR9ZjBi9TGAov&VV9|5Jh@q;EwPMO`W;>uV)e_f; zl9a@fRIB8oR3OD*WMF8bYiOivXdGf>Y-M0+1!NmoSQ!`;@?2L%(U6;;l9^V?U}$M+ op=)5GYhW5;WMpMvY-MDiYhVu4pwhBB9H@c8)78&qol`;+0Pxj18vpuV)e_f; zl9a@fRIB8oR3OD*WMF8bYiOivXdGf>Y-M0+1!NmoSQ!`;@?2L%(U6;;l9^V?U}$M+ op=)5GYhW5;WMpMvY-MPsYhVu4aPs!dKA;8$Pgg&ebxsLQ00NFXK>z>% diff --git a/impeller/third_party/stb/stb/tests/pngsuite/primary_check/s04i3p01.png b/impeller/third_party/stb/stb/tests/pngsuite/primary_check/s04i3p01.png deleted file mode 100644 index ae326c1c4bbdb9c7e51e1054ac3d79df26bbeb00..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 221 zcmeAS@N?(olHy`uVBq!ia0vp^EFjFm1|(O0oL2|pq&xaLGBCC+FnlpB9>`})@^*J& z_z!{$_AZ|c6jAVWaSY+Op8V(k{D=0;LJ3I;AfVHpu!BJ;!R0y=gT!IBkCHlBQ-Nw# zOI#yLQW8s2t&)pUffR$0fuV`6p^>hkafp$zm4T%dkZoXLWnfUqb6pihLvDUbW?ChK rp{1pTu7QcJfoX`5k(Gh5m63t2fjLlvO3UhSpaup{S3j3^P6`})@^*J& z_z!{$_AZ|c6jAVWaSY+Op8V(k{D=0;LJ3I;AfVHpu!BJ;!R0y=gT!IBkCHlBQ-Nw# zOI#yLQW8s2t&)pUffR$0fuV`6p^>hkafp$zm4T%dkZoXLWnfUqb6pihLvDUbW?ChK sp{1pTu7QcJfoX`5k(Gh5m7$rgfjLma$=fsgfEpM)UHx3vIVCg!0P%4>xBvhE diff --git a/impeller/third_party/stb/stb/tests/pngsuite/primary_check/s05i3p02.png b/impeller/third_party/stb/stb/tests/pngsuite/primary_check/s05i3p02.png deleted file mode 100644 index fd41d1d8115a06d935284c91f40b932178007608..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 232 zcmeAS@N?(olHy`uVBq!ia0vp^tRT$61|)m))t&+=mUKs7M+U~W1%@xC#RK_lN#5=* z4F5rJ!QSPQfg;+TE{-7_*OUL8Kk#8bBku+SAP7^=`%DCCQ!R0gC`m~yNwrEYN(E93Mh1o^x`sx&hQ=XA##RQFRzS9ag_VIp zA^=`%DCCQ!R0gC`m~yNwrEYN(E93Mh1o^x`sx&hQ=XA##RQFRzS9ag_VIp zA;r0G@O1TaS?83{ F1OP&2LP-Ds diff --git a/impeller/third_party/stb/stb/tests/pngsuite/primary_check/s06i3p02.png b/impeller/third_party/stb/stb/tests/pngsuite/primary_check/s06i3p02.png deleted file mode 100644 index 73a7b0c64ffebe6b62296460854bb97e53e878f0..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 239 zcmeAS@N?(olHy`uVBq!ia0vp^Y#_`5A|IT2?*XJ((j9#r85r9Z7`~Vm50Xssc6VX; z4}uH!E}sk(G4gbA4B@z*{OA0K|Mtvc2M!$of`~q?ANwaaHgcYOozRk?k&twNG2+1* zu8`Fdg^_#=dDplmi8BXw0d=dExJHzuB$lLFB^RXvDF!10Lla#?BV9w|5F=wN14}C) z+rYxgz@U)lx+;o>-29Zxv`Pj;OG^t~0~1{X(-0#gD+6OIBLiInbD##5met`v4Gf;H KelF{r5}E)-K1fFZ diff --git a/impeller/third_party/stb/stb/tests/pngsuite/primary_check/s06n3p02.png b/impeller/third_party/stb/stb/tests/pngsuite/primary_check/s06n3p02.png deleted file mode 100644 index e85eac8e8f8618ac6f320da65e51072f6de12003..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 239 zcmeAS@N?(olHy`uVBq!ia0vp^Y#_`5A|IT2?*XJ((j9#r85r9Z7`~Vm50Xssc6VX; z4}uH!E}sk(G4gbA4B@z*{OA0K|Mtvc2M!$of`~q?ANwaaHgcYOozRk?k&twNG2+1* zu8`Fdg^_#=dDplmi8BXw0d=dExJHzuB$lLFB^RXvDF!10Lla#?BV9w|5F=wN14}C) z+rYxgz@U)lx+;o>-29Zxv`Pj;OG^t~0~1{X(-0#gD+6OILm<~2sA1-lN0)&b7(8A5 KT-G@yGywoy97)yy diff --git a/impeller/third_party/stb/stb/tests/pngsuite/primary_check/s07i3p02.png b/impeller/third_party/stb/stb/tests/pngsuite/primary_check/s07i3p02.png deleted file mode 100644 index 08f61804f4ba73b862d9bf31e5aada044dae1ee9..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 249 zcmeAS@N?(olHy`uVBq!ia0vp^>>$j+1|*LJg>$j+1|*LJgbP0l+XkKiN;L> diff --git a/impeller/third_party/stb/stb/tests/pngsuite/primary_check/s08i3p02.png b/impeller/third_party/stb/stb/tests/pngsuite/primary_check/s08i3p02.png deleted file mode 100644 index 23d16c71e25c8df5e1f17bad4a7832e89bd4b8df..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 255 zcmeAS@N?(olHy`uVBq!ia0vp^93afW1|*O0@9PFqEa{HEjtq=#3k+XOiwE-AlDyqr z82*Fcg1yTp14W!XT^vIsF1Pl3@-ZlIFtb0N|CICBtEvkUrh0oBCts49RH4D7@}@#( z1tUXc@}A#qshtJa`WyQn**tNc#`t4t_us6rWy|>f*Z%rB4QQHbiEBhjN@7W>RdP`( zkYX@0Ff`FMG}1LR4ly#eGO)A)vJEV(3=9f+uB)PG$jwj5Osixtw6wI)H89aNFby#> evNABXG6HHa2Wr?Xy{itWfx*+&&t;ucLK6TtHBZ<8 diff --git a/impeller/third_party/stb/stb/tests/pngsuite/primary_check/s08n3p02.png b/impeller/third_party/stb/stb/tests/pngsuite/primary_check/s08n3p02.png deleted file mode 100644 index 4a46016665abd286a25d958ae4b1c5b9a470b46d..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 255 zcmeAS@N?(olHy`uVBq!ia0vp^93afW1|*O0@9PFqEa{HEjtq=#3k+XOiwE-AlDyqr z82*Fcg1yTp14W!XT^vIsF1Pl3@-ZlIFtb0N|CICBtEvkUrh0oBCts49RH4D7@}@#( z1tUXc@}A#qshtJa`WyQn**tNc#`t4t_us6rWy|>f*Z%rB4QQHbiEBhjN@7W>RdP`( zkYX@0Ff`FMG}1LR4ly#eGO)A)vJEV(3=9f+uB)PG$jwj5Osixtw6wI)H89aNFby#> evNABXG6ZtXff{B$d2|`5fx*+&&t;ucLK6T;Pf-Z~ diff --git a/impeller/third_party/stb/stb/tests/pngsuite/primary_check/s09i3p02.png b/impeller/third_party/stb/stb/tests/pngsuite/primary_check/s09i3p02.png deleted file mode 100644 index ea14f9be0e000d9079a2305b26259234b6f9e810..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 263 zcmeAS@N?(olHy`uVBq!ia0vp^oFL4>1|%O$WD@{VEa{HEjtq=#3k+XOiwE-AlDyqr z82*Fcg1yTp14X<%T^vIsF85AwdcG!ScDmhme{?4J9NNta?@PFXlDefiG+N2H84PR{%Hg&|=ZpZJGjrlmkrRZCnW zN>UO_QmvAUQh^kMk%6I!uAz~xp>c?jv6X?P6_9OUVP#-Y$a7s4MMG|WN@iLmgQ2CR ng|2~#u7PQYk&%^wv6T@}gE>&cX6apZKn)C@u6{1-oD!M<7}iv9 diff --git a/impeller/third_party/stb/stb/tests/pngsuite/primary_check/s09n3p02.png b/impeller/third_party/stb/stb/tests/pngsuite/primary_check/s09n3p02.png deleted file mode 100644 index 7a822537f73bf0cd78f93f717d98e3a658f5ae2b..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 263 zcmeAS@N?(olHy`uVBq!ia0vp^oFL4>1|%O$WD@{VEa{HEjtq=#3k+XOiwE-AlDyqr z82*Fcg1yTp14X<%T^vIsF85AwdcG!ScDmhme{?4J9NNta?@PFXlDefiG+N2H84PR{%Hg&|=ZpZJGjrlmkrRZCnW zN>UO_QmvAUQh^kMk%6I!uAz~xp>c?jv6X?P6_9OUVP#-Y$a7s4MMG|WN@iLmgQ2CR ng|2~#u7PQYk&%^wv6Uf^YYx;f^U0&jKn)C@u6{1-oD!Mb3K)h=a~;X zoA_yLw#%*AR)m*4g=~I*oznCEXTim0e7<*qydRxMEjY%hjgc*~fP7 zxX%5s)HvAOMsvUYgbq88b3K)h=a~;X zoA_yLw#%*AR)m*4g=~I*oznCEXTim0e7<*qydRxMEjY%hjgc*~fP7 zxX%5s)HvAOMsvUYgbq88zopr0B;tb)Bpeg diff --git a/impeller/third_party/stb/stb/tests/pngsuite/primary_check/s33i3p04.png b/impeller/third_party/stb/stb/tests/pngsuite/primary_check/s33i3p04.png deleted file mode 100644 index 0faaa74108eebe50057ae9499393d39730e86096..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 470 zcmeAS@N?(olHy`uVBq!ia0vp^iXhCv1|-9u9Lfh$Ea{HEjtq=#3k+XOiwE-AlDyqr z82*Fcg1yTpGcYhpdb&7xXE?#OpofoHSozcc#|tlsRr@bsYtYqFa9vsHSMAN4S> za7SqE>f91DQ`YmugRUh(2ZPxd&Ro)t6={6q!@$7d?Ic)W+T`>r%dA`JK}J(t=dHqA zCfdz>$^6g6=$b; z9(?oL*-OK0*~hA!idjuc6E1dSaGdTlQrM(!ZKm+kZ1C7HKHUXu_V$ diff --git a/impeller/third_party/stb/stb/tests/pngsuite/primary_check/s33n3p04.png b/impeller/third_party/stb/stb/tests/pngsuite/primary_check/s33n3p04.png deleted file mode 100644 index 599171c7b1a3569a121ed3c7b751c66c92325751..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 470 zcmeAS@N?(olHy`uVBq!ia0vp^iXhCv1|-9u9Lfh$Ea{HEjtq=#3k+XOiwE-AlDyqr z82*Fcg1yTpGcYhpdb&7xXE?#OpofoHSozcc#|tlsRr@bsYtYqFa9vsHSMAN4S> za7SqE>f91DQ`YmugRUh(2ZPxd&Ro)t6={6q!@$7d?Ic)W+T`>r%dA`JK}J(t=dHqA zCfdz>$^6g6=$b; z9(?oL*-OK0*~hA!idjuc6E1dSaGdTlQrM(!ZKm+kZ1C7HKHUXu_Vh&(&(E z@>ArT>|ms#D119{?Ys5I^lenc7>y3iT^k82~$if4~t!7e0Iw{E%(~Yk1`V+9>#BW_||>lR{ury z6x?a$u{rKaRh&(&(E z@>ArT>|ms#D119{?Ys5I^lenc7>y3iT^k82~$if4~t!7e0Iw{E%(~Yk1`V+9>#BW_||>lR{ury z6x?a$u{rKaRxXlEs(F-fX7*OW%;{x7lNzp-*Rl&HuD9~zgcceXTF)( z&Zxk|DZ6Wih}}%q)EhffH@`I&O=e7(`MiF?4yDKt76zWzL)bPY+ zrdLb*5qDOehL-COXR~=SANOJF>&=hl4v?DcI3>!%aY7+e>s6x=A2#)js?K@44J1P( zEG!Im?K6@$mpr%n=z3XJ6Stb>-}9XH=hUm%p<`>G6Fdt}Vh}qbIGI$vbQB`?`BVP)t{QGbebpY&X%(esuzB9 z*w5JbH-q{5vYsMf=&6>tMwFx^mZVxG7o`Fz1|tJQ6J0|iT|?s#BV#KAODiDTz{1MF zppfUfDvE~O{FKbJN(MtqOAB2C6I}z-5F;Zi17j;=GhG97poZ>$^F9MLFnGH9xvXxXlEs(F-fX7*OW%;{x7lNzp-*Rl&HuD9~zgcceXTF)( z&Zxk|DZ6Wih}}%q)EhffH@`I&O=e7(`MiF?4yDKt76zWzL)bPY+ zrdLb*5qDOehL-COXR~=SANOJF>&=hl4v?DcI3>!%aY7+e>s6x=A2#)js?K@44J1P( zEG!Im?K6@$mpr%n=z3XJ6Stb>-}9XH=hUm%p<`>G6Fdt}Vh}qbIGI$vbQB`?`BVP)t{QGbebpY&X%(esuzB9 z*w5JbH-q{5vYsMf=&6>tMwFx^mZVxG7o`Fz1|tJQ6J0|iT|?s#BV#KAODiDTz{1MF zppfUfDvE~O{FKbJN(MtqOAB2C6I}z-5F;Zi17j;AAlDqIK|K4Z0Z;>jr>mdKI;Vst E00wZd8UO$Q diff --git a/impeller/third_party/stb/stb/tests/pngsuite/primary_check/s36i3p04.png b/impeller/third_party/stb/stb/tests/pngsuite/primary_check/s36i3p04.png deleted file mode 100644 index d61491fac8816dce998f63673cc1983e193534a6..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 448 zcmeAS@N?(olHy`uVBq!ia0vp^Dj>|k1|%Oc%$NbBSkfJR9T^zg78t&m77ygJC3(BM zF#HF>1$&oIW?*1s_H=O!i8%ardMsbF0Z;SXPV479wp+hDbUR%xedC@fXW*p8dQiPX zmWyG}CSjF@Dh^s6O*2O++{-vy2@$}Q#e_SOy&iI8)by!i!w^r{J&nb457exi80n#T{ zx43Y%@kp%TPZy6%4Ykdu;sRl!3(D69(sS985lsSC9V-ADTyViR>?)FK#IZ0 zz|ch3&`8(NIK;@<%D~bJ$TqOBGB7CQxvq+$AvZrIGp&-r(9+UE*T6*Az%<0j$jZRj a${5Hs2Wq%|@aa0B1_n=8KbLh*2~7aHMW@gJ diff --git a/impeller/third_party/stb/stb/tests/pngsuite/primary_check/s36n3p04.png b/impeller/third_party/stb/stb/tests/pngsuite/primary_check/s36n3p04.png deleted file mode 100644 index 1f50479e48ebcbf5c106e1baa714a47fcbee2448..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 448 zcmeAS@N?(olHy`uVBq!ia0vp^Dj>|k1|%Oc%$NbBSkfJR9T^zg78t&m77ygJC3(BM zF#HF>1$&oIW?*1s_H=O!i8%ardMsbF0Z;SXPV479wp+hDbUR%xedC@fXW*p8dQiPX zmWyG}CSjF@Dh^s6O*2O++{-vy2@$}Q#e_SOy&iI8)by!i!w^r{J&nb457exi80n#T{ zx43Y%@kp%TPZy6%4Ykdu;sRl!3(D69(sS985lsSC9V-ADTyViR>?)FK#IZ0 zz|ch3&`8(NIK;@<%D~bJ$TqOBGB7CQxvq+$AvZrIGp&-r(9+UE*T6*Az%<0j$jZRj a$_U6c2Wk+{erf>Jz~JfX=d#Wzp$Pz@eWo`6 diff --git a/impeller/third_party/stb/stb/tests/pngsuite/primary_check/s37i3p04.png b/impeller/third_party/stb/stb/tests/pngsuite/primary_check/s37i3p04.png deleted file mode 100644 index 2906fa311661d670be012c178ea9b6c57798af70..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 478 zcmeAS@N?(olHy`uVBq!ia0vp^svyk41|*NpQ(y*CEa{HEjtq=#3k+XOiwE-AlDyqr z82*Fcg1yTpGcYhJdb&7xXlJ(ll~0Z((TX8FB!7lPZTPirmv>)5lT&PgCM>|5e? zmIVzn?A8|H9T0ePg6j2gGc!Wi#S)F1S2)~iJJ`l53LZ+nBlwf`LYWRS1-T1^IpkF z$4Wxt??3*%yZbX1pFB2AJtU;{o6;(cHNS(7-;lezz43^7~~k zT7LiRLfw78b$lW>-VHw-Qgf{A{$Jh$TYs;~;lCyqv-|zygU!F6h=1Rw&U>>y=J%fo z?`GdsjW7O`s=!^WrvnPx64!{5l*E!$tK_0oAjM#0U}&OiXryas9Aac_WngIqWE)sm z85k7uTn9%6TtjYtN@iLmgQ2CRg|2~#u7PQYk&%^wv6YE|u7NpF!=u-WE&y#{@O1Ta JS?83{1OQL@!BYSL diff --git a/impeller/third_party/stb/stb/tests/pngsuite/primary_check/s37n3p04.png b/impeller/third_party/stb/stb/tests/pngsuite/primary_check/s37n3p04.png deleted file mode 100644 index 8931b859daf0a13f2c648a566023d0cb1ecafd61..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 478 zcmeAS@N?(olHy`uVBq!ia0vp^svyk41|*NpQ(y*CEa{HEjtq=#3k+XOiwE-AlDyqr z82*Fcg1yTpGcYhJdb&7xXlJ(ll~0Z((TX8FB!7lPZTPirmv>)5lT&PgCM>|5e? zmIVzn?A8|H9T0ePg6j2gGc!Wi#S)F1S2)~iJJ`l53LZ+nBlwf`LYWRS1-T1^IpkF z$4Wxt??3*%yZbX1pFB2AJtU;{o6;(cHNS(7-;lezz43^7~~k zT7LiRLfw78b$lW>-VHw-Qgf{A{$Jh$TYs;~;lCyqv-|zygU!F6h=1Rw&U>>y=J%fo z?`GdsjW7O`s=!^WrvnPx64!{5l*E!$tK_0oAjM#0U}&OiXryas9Aac_WngIqWE)sm z85k7uTn9%6TtjYtN@iLmgQ2CRg|2~#u7PQYk&%^wv6Zobu7NpF!wbFDCxJFFc)I$z JtaD0e0suu(z?}d9 diff --git a/impeller/third_party/stb/stb/tests/pngsuite/primary_check/s38i3p04.png b/impeller/third_party/stb/stb/tests/pngsuite/primary_check/s38i3p04.png deleted file mode 100644 index becf5a1df6935d3524bed7d0b279df4e338b1b8a..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 439 zcmeAS@N?(olHy`uVBq!ia0vp^Y9P$P1|(P5zFY^SSkfJR9T^zg78t&m77ygJC3(BM zF#HF>1$&oI28#Uhba4!cIQ(|{MZRVQ9#{YAe~b4wuHRSf__jqi&+Rnx^$7|BML}NP zj3rArJ$JB7Qs&&bV*dAK{%h+ER>(0uQD|KM?c;XWzIW`e`=U7QzdL_DsPn~^eHEj& zpjTx8!}+Dj+h#|yyIGz(_VewBwCN3Nw;S<=Ol8^ezA#7RxxDA&Cdu6uRvls8k>~e& zpX+UzrG4K*OqlJ`mYAKVZ(6Ie-*{Gfk!738hKnW#)oxs^ns@(^f2&1gR1ipoT}U S7hM2qVDNPHb6Mw<&;$Vcd#phK diff --git a/impeller/third_party/stb/stb/tests/pngsuite/primary_check/s38n3p04.png b/impeller/third_party/stb/stb/tests/pngsuite/primary_check/s38n3p04.png deleted file mode 100644 index 43f8c983f111c2e7d7812f99aedfc787b68680f8..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 439 zcmeAS@N?(olHy`uVBq!ia0vp^Y9P$P1|(P5zFY^SSkfJR9T^zg78t&m77ygJC3(BM zF#HF>1$&oI28#Uhba4!cIQ(|{MZRVQ9#{YAe~b4wuHRSf__jqi&+Rnx^$7|BML}NP zj3rArJ$JB7Qs&&bV*dAK{%h+ER>(0uQD|KM?c;XWzIW`e`=U7QzdL_DsPn~^eHEj& zpjTx8!}+Dj+h#|yyIGz(_VewBwCN3Nw;S<=Ol8^ezA#7RxxDA&Cdu6uRvls8k>~e& zpX+UzrG4K*OqlJ`mYAKVZ(6Ie-*{Gfk!738hKnW#)oxs^ns@(^f2&1gR1ipoSNE St4{(oFnGH9xvXLAR)1|)kH2buyYmUKs7M+U~W1%@xC#RK_lN#5=* z4F5rJ!QSPQ85kJNJY5_^A`YLO5xwrPf`D^wr|o6k4O#c))s}iz#AvSl7=BdY>a2)j z1||WqEn8kDE`2)pW`f_`X%pVxFWSP(uz6kl8k1w2PD(Hcq!;%ZdS8%Q+HbJY4;?wdEGtk}LMy>|1O$GL5pd9-b^4bpV=o?Z# zS5tx_Zdg?{W@d+&HSX_ueM)Vc9Gkj-Q~jpJg_F#W@{|C>RJFu4q9i4;B-JXpC>2OC z7#SFv=o%X78XAWf8Cw}xS^?Px7FGrZg*?|)Q8eV{r(~v8G8kG~TId>>=o*-Y7#Ud^ c7+aYDHJAf6l$LD%3Dm&g>FVdQ&MBb@0C};)`v3p{ diff --git a/impeller/third_party/stb/stb/tests/pngsuite/primary_check/s39n3p04.png b/impeller/third_party/stb/stb/tests/pngsuite/primary_check/s39n3p04.png deleted file mode 100644 index d37d66d44129066477311007b621745cee75fd73..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 499 zcmeAS@N?(olHy`uVBq!ia0vp^>LAR)1|)kH2buyYmUKs7M+U~W1%@xC#RK_lN#5=* z4F5rJ!QSPQ85kJNJY5_^A`YLO5xwrPf`D^wr|o6k4O#c))s}iz#AvSl7=BdY>a2)j z1||WqEn8kDE`2)pW`f_`X%pVxFWSP(uz6kl8k1w2PD(Hcq!;%ZdS8%Q+HbJY4;?wdEGtk}LMy>|1O$GL5pd9-b^4bpV=o?Z# zS5tx_Zdg?{W@d+&HSX_ueM)Vc9Gkj-Q~jpJg_F#W@{|C>RJFu4q9i4;B-JXpC>2OC z7#SFv=o%X78XAWf8Cw}xS^?Px7FGrZg*?|)Q8eV{r(~v8G8kG~TId>>=o*-Y7#Ud^ c7+VzuQ<002!1)diDrSIp2&+xDP9Voi?+@_5alfNvva4X2} z+=@;G2P=;zKYJvJs->WRWJaZS5oNMZu=I%|~=4`WNbdjGPwtIhkafp$zm4T%dkZoXLWnfUqb6pihLvDUbW?ChK qp{1pTu7QcJfoX`5k(Gh5l?hOTIZ#7s$>yIx4Gf;HelF{r5}E*qORR7J diff --git a/impeller/third_party/stb/stb/tests/pngsuite/primary_check/s40n3p04.png b/impeller/third_party/stb/stb/tests/pngsuite/primary_check/s40n3p04.png deleted file mode 100644 index 6f8596ca14510022eb45b8a6e05fca280b354e32..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 463 zcmeAS@N?(olHy`uVBq!ia0vp^8X(NU1|)m_?Z^dEEa{HEjtq=#3k+XOiwE-AlDyqr z82*Fcg1yTpGcYg;dAc};L>zuQ<002!1)diDrSIp2&+xDP9Voi?+@_5alfNvva4X2} z+=@;G2P=;zKYJvJs->WRWJaZS5oNMZu=I%|~=4`WNbdjGPwtIhkafp$zm4T%dkZoXLWnfUqb6pihLvDUbW?ChK rp{1pTu7QcJfoX`5k(Gh5l`&9*IZ#9W#$6A98W=oX{an^LB{Ts5ii51A diff --git a/impeller/third_party/stb/stb/tests/pngsuite/primary_check/tbbn0g04.png b/impeller/third_party/stb/stb/tests/pngsuite/primary_check/tbbn0g04.png deleted file mode 100644 index 8d9f7d52b0446496c9db04c1ab90dd967dbdbf71..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 762 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 diff --git a/impeller/third_party/stb/stb/tests/pngsuite/primary_check/tbgn3p08.png b/impeller/third_party/stb/stb/tests/pngsuite/primary_check/tbgn3p08.png deleted file mode 100644 index fa5cdbc95a44cf76abbf44527dbbd7e12d72c268..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1911 zcmV--2Z;EIP)%G*ochBz3{Q9u#ja*|cB~p&`lXidl&7RNso^yV4RuV$+bDI3KdVdjs zbUH0&W@bdWTz(-5ZO?N!lgWs0zfCedeVOIuWkj(;1j2vy1FWo$s{u~Gb}DH;;Ge8@82ghKaY~jQ3H&2Bw=9N$Oye}y+zO4Zxb0FMo|>$ zxdF=Mvbb^M2FYZSyLazm>N>8QH}PdMc<u>Q!_=1)6?852H~UJv~H{Ne(P6K^qF74E(1J_Ag-dgAcH~-5VmVR;wfu z2_`2er42J=v!Do1Oj{?kFzo~gpgZ}lNlc7dZofI=I7aW zvK}x z6yy3hN7s%Lc84(xgKD)(ypF&EU>F90K!EP2qgXpqb0 zC>AX)UVIl#^8;7Vv;vwI1Yl@rXlnpjmQgAS*ntuh`~@Bb9?@6qqheLKu_5C~8zmC!VeLZN`BX_%(q!i7Ijsq_GUL6)zgst?ii2&Iz3zF+R!R3{w^hr{fs z?|?PnL2GZWe-0r87sD6%YS&kM$(MXu_!J=oilPt<1~Ci+P16uUFhBnWmC6Wk8(IDb zJ9p0F@uYCOEA;l7#D-#919-jL@fu#J13$D>_~KVzpes6tZh!|^1-Gr$G3%?*Xdr~3 zS`9Kc_XbczQU1umgK6UNeiS9j?%gmx-n;2)-T)8?_^=o4;0NvjnbwI*(k1H6I<#+X zwW)3b09ac3HJ0TD&ePwY$KmjB<;p18?7Pg*k25k7e{8G%m;#>1yjwb zc8}445JF&CR_E@pELd2WXh%QI_U#V7`Q`|kHVC9}yRR`a65Rx_p$NBkb+ta2fCD@k z50&K#T9?L(w$j>f16Y=YEXy4Q3WeR&>jB_zba&@@^eBU2^Z;60U7bWC@ni*p!62%p zLKE=Az;^?%0`x`aLuIps8ab&p${F`8~iK46^%PUBd4M;FL8pP#t zNlye&6h#_z4z>V%u&cNW&yoj1fZj?6%^wx8EDOUhFin$m+JR*?fi&T;PItG1V9zO&u|@HhNeb_<}>g0frzR&hEF za=C79-W&sb^z{6Rk3W8oLx&DMb~4(efU2s}hrjz!`sBzbc-^fxeRMh68VIb`QY@vT zz}nhcJ0-KgGHYu#u3g)UVKfMb|IYdIr#NzCV#~2?%c(e#NJytopJrfSfcWirD|(~d zB(k|71ps}0w^5W8EDKS=+{B;I011*UEQJ~Ri zU|AMkZ-K6^i#Qx9UVXKTsi}{MM0P)QLVoI0yl!*zb7JzJlT`0jd2`#Fxb1EfMd9JY zhosYKOw;7Sg9jWwe3&z5&Y-I5GmYoZ3;>{MnmBv*Ea`L_S(cGynS1x{k3v)jE;^zxAPYQ0I1jNqEIN%+uQqpIe%e*pThnN3zbN{>Y)K>0000bbVXQnWMOn= zI%9HWVRU5xGB7bTEip1JF*8&$GdeIiIx#XWFgQ9eFmVjmB>(^bC3HntbYx+4Wjbwd xWNBu305UK!F)c7OEio`uF)}(bF*-0bEigAaFffPyJZAs^002ovPDHLkV1hJDb{GHv diff --git a/impeller/third_party/stb/stb/tests/pngsuite/primary_check/tbrn2c08.png b/impeller/third_party/stb/stb/tests/pngsuite/primary_check/tbrn2c08.png deleted file mode 100644 index bbe748fd32d611cece365935600b1cbc400ae247..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1901 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 diff --git a/impeller/third_party/stb/stb/tests/pngsuite/primary_check/tbyn3p08.png b/impeller/third_party/stb/stb/tests/pngsuite/primary_check/tbyn3p08.png deleted file mode 100644 index 4fbdb36a33d7f3c3c82addd028dffed7a0d3f0ed..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1911 zcmV--2Z;EIP)%G*ochBz3{Q9u#ja*|cB~p&`lXidl&7RNso^yV4RuV$+bDI3KdVdjs zbUH0&W@bdWTz(-5ZO?N!lgWs0zfCedeVOIuWkj(;1j2vy1FWo$s{u~Gb}DH;;Ge8@82ghKaY~jQ3H&2Bw=9N$Oye}y+zO4Zxb0FMo|>$ zxdF=Mvbb^M2FYZSyLazm>N>8QH}PdMc<u>Q!_=1)6?852H~UJv~H{Ne(P6K^qF74E(1J_Ag-dgAcH~-5VmVR;wfu z2_`2er42J=v!Do1Oj{?kFzo~gpgZ}lNlc7dZofI=I7aW zvK}x z6yy3hN7s%Lc84(xgKD)(ypF&EU>F90K!EP2qgXpqb0 zC>AX)UVIl#^8;7Vv;vwI1Yl@rXlnpjmQgAS*ntuh`~@Bb9?@6qqheLKu_5C~8zmC!VeLZN`BX_%(q!i7Ijsq_GUL6)zgst?ii2&Iz3zF+R!R3{w^hr{fs z?|?PnL2GZWe-0r87sD6%YS&kM$(MXu_!J=oilPt<1~Ci+P16uUFhBnWmC6Wk8(IDb zJ9p0F@uYCOEA;l7#D-#919-jL@fu#J13$D>_~KVzpes6tZh!|^1-Gr$G3%?*Xdr~3 zS`9Kc_XbczQU1umgK6UNeiS9j?%gmx-n;2)-T)8?_^=o4;0NvjnbwI*(k1H6I<#+X zwW)3b09ac3HJ0TD&ePwY$KmjB<;p18?7Pg*k25k7e{8G%m;#>1yjwb zc8}445JF&CR_E@pELd2WXh%QI_U#V7`Q`|kHVC9}yRR`a65Rx_p$NBkb+ta2fCD@k z50&K#T9?L(w$j>f16Y=YEXy4Q3WeR&>jB_zba&@@^eBU2^Z;60U7bWC@ni*p!62%p zLKE=Az;^?%0`x`aLuIps8ab&p${F`8~iK46^%PUBd4M;FL8pP#t zNlye&6h#_z4z>V%u&cNW&yoj1fZj?6%^wx8EDOUhFin$m+JR*?fi&T;PItG1V9zO&u|@HhNeb_<}>g0frzR&hEF za=C79-W&sb^z{6Rk3W8oLx&DMb~4(efU2s}hrjz!`sBzbc-^fxeRMh68VIb`QY@vT zz}nhcJ0-KgGHYu#u3g)UVKfMb|IYdIr#NzCV#~2?%c(e#NJytopJrfSfcWirD|(~d zB(k|71ps}0w^5W8EDKS=+{B;I011*UEQJ~Ri zU|AMkZ-K6^i#Qx9UVXKTsi}{MM0P)QLVoI0yl!*zb7JzJlT`0jd2`#Fxb1EfMd9JY zhosYKOw;7Sg9jWwe3&z5&Y-I5GmYoZ3;>{MnmBv*Ea`L_S(cGynS1x{k3v)jE;^zxAPYQ0I1jNqEIN%+uQqpIe%e*pThnN3zbN{>Y)K>0000bbVXQnWMOn= zI%9HWVRU5xGB7bTEip1JF*8&$GdeIiIx#XWFgQ9eFmVjmB>(^bC3HntbYx+4Wjbwd xWNBu305UK!F)c7OEio`uF)}(bF*-0bEigAaFffPyJZAs^002ovPDHLkV1myucDn!o diff --git a/impeller/third_party/stb/stb/tests/pngsuite/primary_check/tm3n3p02.png b/impeller/third_party/stb/stb/tests/pngsuite/primary_check/tm3n3p02.png deleted file mode 100644 index babdebefb5f83c6d969414485fe31d9a3b8486b3..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 306 zcmeAS@N?(olHy`uVBq!ia0vp^3LwnE1|*BCs=fdzmUKs7M+SzC{oH>NS%G}U;vjb? zhIQv;UV>C6dj$D1FjT2AFf_CjA5L~c#`DCDoji-xaNW|f{*EaGpCRdP`(kYX@0Ff`FMG}1LR z4ly#eGO)A)vJEV(3=9f+uB)PG$jwj5OsfQGFxE9N1Zgldurji=GJi71_B}=a*15q{<+oF{a$y~*RS9EzE_>==0a6eII94I z!4zpu6b7(5-zQuitkH>y8^Fkh+0pG_N3zTC3;6+HFCTP?N`W1IA7xEdJa9m#QS3Z@ zI=Tw_!%t0~?!HLF7Gc@LSNQqw9Lt@`+3cl?7Yz2(!%PHIrPDzLEqt*U{1}byEV!?8 z`$L1IovV;xY<%k-<8ftkXY-?TH2x#rqoTT9zkb$(e}t=~45kNKm~SVqoS%*!XcEoL z1h%DKT{#>+pSDqT@dOh2+wZ?q{QT5MY61=qVa4Rw+m?}25FX#w(*w`4Pxmx(h>kXI zYHHfp+TyHEH8GF8h#PJ#G7l>(F8*tCX(V~)iyU3-RLQ8Wt}YWA<%b5U$>(8odkLnd zrW>1^Q5y^HV`F1#nwpa~t@Y8;gu??H9*>uNuy+Qau)OLgPyW?^C8}{!EjwMbk5TJQp^I%#>OTlKK|6b?lbD@<60luvk=a7`r68*)%^TCMCWn2 zit|#b@8!#m@bvE4w&gheUP45jI$ew_=#~B1ZZG9xk+Zy)*V<09PrgHZyycs#=$er` z-LvJ-pWjcgc1cRI0x4I&ZQa-)x<0Tj_cYSYY+a74efiQE%WGTOlyK@lCpHm(_`%)7 z<42?WdZmLNOePmYD-_V^bj3CniZ1R6A>eLjnDoNnx`ibrbIap(8l>y2$MEp*a1izD zehRXq%+ts{Ai(vhPv`8~(8Po@@od)|=ZKrPcaCVOx~9g%MDA`i{fa>hz#Q5@cXY(r|>^)%(AauDg4CQ%M?F)jQ?vF;_WEFM-SDwxEmH z{6}Ior(PpFkfkQSmQY-9E+9hgHj*Cz~9X3&3~RenHwmv^7DW!EF2-V&N)swPC-#o5w44}|FFJu%kkk|2$9vk6*|WO zB>6CzbF3^J8orjU7Ybg_&tru`Nn9g4GBWI^pZ;9)#UmynEb~Ll+5Fl|>fo`; zxv~fZ08}tP0dQYmAL*u@I~v&mj`T8hik$QMuYi&>M~-LNCp29KDoNV<1SAog1aojl z*_SOf)ZRiJ?R_>BTOt%1{jHKASE!k|yT891Z_-=gZzdicrFnawE_b`-W8&!Qsssu> zrKO{s%ks?^|MwHFS|lPX{u#kIl!pPCy9@+BRZF)`8y)qvu(YIMlw)FIuAN*~h531c z1pLi%P#F|G_nDy-lU`0@PN*+vCYGpKe{KT5`)hquUsW;M__5zNP?v`oLQnqH01+!@po%}ab?&76%n}KD=cf-T&wPAw^dtdP_&jO|p z5!226Lqn+j{r$VMsnk{%JCa7+K*r6RH?30-ig&kHYkA8F_%oWCp^1t7wl+KICvVXF zY`)4#H*}Vd$*a~@*zD}=L_=&iRSJ7BH8qu*BJEJpzHt3u?}oFO;^h_QUnORTakMU` z6oar4i`%Ix>DtS$xa!5EsEm#rB`f|AQL&)6>ow1C=i8>SzP^5=FK0Wx<5GdynxqSH zSJTQ-2W6XwF}wV-=dpK9TqFAP>G+OgN?H(+NWt=ERwf%$8~RFRVE#>sn~^EUKBUT( z>Dnp1n!FFb8iN=IpO}!qm{4*sClnYM0m2ha@dQ)6xhKJ#44o$fA0m+FPFp$Z;fz z#vD6|lZ%|}p&cfn_GO)6ei`4}oEAB-w2LcMBxHm3djB_!tI|miUogs?| zn&4@Yn8XC5F&*ukI9z1wJYilcsL;~~74B35s zSciwPs45m<9q<8Q0%ibjD8$T*7pVF9#JpZ)pAQG1#$p6~K6-k37#JA9U@%ZxT8hi% zqO!7*bLY;z3NSc0$hB+Nh(sa?A&_O64gLLW?(at<*)9>QmGKWhOePa%vl&g(h(sdz zOG^>sk03Gyx2tcnsdE$c zBlVa}CbF`!NF)+0E-n%Zg;sAXNg@;q;r06I=_zGo#6^1g1o`=66c#Sva=Az(60f+F ziuCk!EKv(Yl5cL8-$n?L`d4XbX=G((QBY8T&1S=3Fd)k^vMeJ>67hJP$;nBcKlk&~ zPX~GQXb+ku2nGW@dSqjK+(Kn#<%%@De2`_7u#yCrgR$H(CL9w8AqW%ZyR`54#`+DR zv=Et@nG_ThV7J==@cDdrJRU-!C|zBDW^QgP@DN$!TB`K4Cd!}Ro4U4G&ZGkxpn!SI471?P z_cE$;Ars(8Jn$)cf~?CdZG!xXYS4Wt9LeDu)!}&e?{@&xz;o>OC{AY_MNxp=zys>*H}kq!u_r9~ zpoFwo8dxDLg{dB^PPV0$WD0Z;`bz*+RS`lE3dz6|3u_{fzOxC;NW(^#)b`7 z`1adB($UfJZUAjSJRRk{Mo?JDS9FTQ61+ct8Su8`q6tS30cXtJv z7A8OcIzRl-!o7Q)tQMeb7LBJK%IWiN#{<-o1^MmKNID+TJSuP5{gC z{Xf6w$G`l@)vH(Wcs%I34v=y{k|Y`$8aQy^0L{(K?>g+-j%H`ip5@@dgY4S1D>WCy zVlgC1;_%_axZUnGiLV&|;N;1Z+_-Ur{rmT$C<;cSkrO9QaOcjQwGIXRg0tbJOP6SD zY-Db3j*AyBzV9&a4*+oL)Tv+cIRO6!LpN@{*Spv$0000bbVXQnWMOn=I%9HWVRU5x zGB7bTEip1JF*8&$GdeIiIx#XWFgQ9eFmVjmB>(^bC3HntbYx+4WjbwdWNBu305UK! pF)c7OEio`uF)}(bF*-0bEigAaFffPyJZAs^002ovPDHLkV1nTUg_!^V diff --git a/impeller/third_party/stb/stb/tests/pngsuite/primary_check/tp0n3p08.png b/impeller/third_party/stb/stb/tests/pngsuite/primary_check/tp0n3p08.png deleted file mode 100644 index e5a29d6d627bee342ee10dfc9140169e96a05163..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1959 zcmV;Y2Uz%tP)r| z0M6{t%uG}ytJi7LDy?d@s!}gnX#%Q=N?Z&D2@M1nfD%x|1rn59pn`;i)=pE46e^T9 zTA3oDsDwdNldOhdAT%F1{%9Q%+d0R_=iP;kS(XqQF4jjnvR}*Zd4A{re4OVs96EGJ z@DrN!Q#uDQKJUgIJ9aQJF+ngG+qNYG7@m~_ZEbBtA`z}#yT-kH_ps-3m>xW!CK^RfCdmO>SxPUktf`6BZ@UF>BHX=um!hIjH8_Yf9>*~}%rYPgq=8wNi+p`O-wh8} z_HR}coMtoe_uixN<(CPCLIi_BIy*b(0aS8e|Ni|%A`v2y2uF?_p;#nQ3Oe{+hwTL1bCR@Anf725D+) z;^fJba{-2ihUo6@CZEqEgg}xc)<&aj7#>CeOhCcqV$|>F?y6O+i$rK18Nm-Yl1WG= znFhWugM9@oTkgSZu>c^1V0d_#fq?-wZQ3+n0k_+W*=(k&stQfh$mjDU*R8{M=MKW{ z=2j@gWMd;jD&=iF5a64c8GaUxvhnIwRv3-gfvl#HSi2Tpd=abNUU8%-3aM0zTrP*- z?`M2`d@g{)VM7Rkq9_#@x`okUt%h_tjUm|zl6EEY+n zQcO=zQz#VBG>t$YKyPpFTmYxjiK?r7T>CLGM~tu%=B?Sc@Y=j+nnpI8C7;hfv#f*= zmCvcEDbndQilU%t8k(lzcDp%!`tJu(FFOYNQsHv&J=krlr zU0o{4behr8QL@=QJw1OWli31%izHpgVz~vVJbEOv@#h<#4&e2AS)N}Gv%q)d@~j_1 z2*G9VWxibbC13Cb=hEj8LLkdB9*+l2)5v5p2qB0@-(+T{3Al|U{gV|dZeX{^u-Rr< zy}C%SG5B-&}(gDGQz0^ov%ZyR6|sE1e~ z20EaDQ+8II>$;AjC?pagBq;~{3y-IOEKedylNby}U=y1+dpLIN*x~>k9UZK?3xl0hCfWC_GU>*L5^aqgX65HfBNB3&0p&uR>j&1&^l+A$|*dLpZ#Fg>2qL zwBEPA5_H1qQLCzY&j%1fQQV^8W|Ya zfTpQ0;;N_>pBicn%dgSSS&GKeYKXp zzCD~je~xF_Gsp6djt-*HD4+fMGg{-VG-sL#C;^JaVg*15!Sdx!>gp`)-rYiXcRL?^ zaN?QHVF?G5PyhZYpYQ&hix)34Ha3P{(z;Rw27`gNwl=nI-Aa3V`;Wct(hepkPMl!- z_U(kj;Yt%yRTYE5!29p-(^bC3HntbYx+4WjbwdWNBu3 t05UK!F)c7OEio`uF)}(bF*-0bEigAaFffPyJZAs^002ovPDHLkV1m3UeQ^K) diff --git a/impeller/third_party/stb/stb/tests/pngsuite/primary_check/tp1n3p08.png b/impeller/third_party/stb/stb/tests/pngsuite/primary_check/tp1n3p08.png deleted file mode 100644 index 9ecd40470c4d39cdedd3d6dcb0960f955a731b5c..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1911 zcmV--2Z;EIP)%G*ochBz3{Q9u#ja*|cB~p&`lXidl&7RNso^yV4RuV$+bDI3KdVdjs zbUH0&W@bdWTz(-5ZO?N!lgWs0zfCedeVOIuWkj(;1j2vy1FWo$s{u~Gb}DH;;Ge8@82ghKaY~jQ3H&2Bw=9N$Oye}y+zO4Zxb0FMo|>$ zxdF=Mvbb^M2FYZSyLazm>N>8QH}PdMc<u>Q!_=1)6?852H~UJv~H{Ne(P6K^qF74E(1J_Ag-dgAcH~-5VmVR;wfu z2_`2er42J=v!Do1Oj{?kFzo~gpgZ}lNlc7dZofI=I7aW zvK}x z6yy3hN7s%Lc84(xgKD)(ypF&EU>F90K!EP2qgXpqb0 zC>AX)UVIl#^8;7Vv;vwI1Yl@rXlnpjmQgAS*ntuh`~@Bb9?@6qqheLKu_5C~8zmC!VeLZN`BX_%(q!i7Ijsq_GUL6)zgst?ii2&Iz3zF+R!R3{w^hr{fs z?|?PnL2GZWe-0r87sD6%YS&kM$(MXu_!J=oilPt<1~Ci+P16uUFhBnWmC6Wk8(IDb zJ9p0F@uYCOEA;l7#D-#919-jL@fu#J13$D>_~KVzpes6tZh!|^1-Gr$G3%?*Xdr~3 zS`9Kc_XbczQU1umgK6UNeiS9j?%gmx-n;2)-T)8?_^=o4;0NvjnbwI*(k1H6I<#+X zwW)3b09ac3HJ0TD&ePwY$KmjB<;p18?7Pg*k25k7e{8G%m;#>1yjwb zc8}445JF&CR_E@pELd2WXh%QI_U#V7`Q`|kHVC9}yRR`a65Rx_p$NBkb+ta2fCD@k z50&K#T9?L(w$j>f16Y=YEXy4Q3WeR&>jB_zba&@@^eBU2^Z;60U7bWC@ni*p!62%p zLKE=Az;^?%0`x`aLuIps8ab&p${F`8~iK46^%PUBd4M;FL8pP#t zNlye&6h#_z4z>V%u&cNW&yoj1fZj?6%^wx8EDOUhFin$m+JR*?fi&T;PItG1V9zO&u|@HhNeb_<}>g0frzR&hEF za=C79-W&sb^z{6Rk3W8oLx&DMb~4(efU2s}hrjz!`sBzbc-^fxeRMh68VIb`QY@vT zz}nhcJ0-KgGHYu#u3g)UVKfMb|IYdIr#NzCV#~2?%c(e#NJytopJrfSfcWirD|(~d zB(k|71ps}0w^5W8EDKS=+{B;I011*UEQJ~Ri zU|AMkZ-K6^i#Qx9UVXKTsi}{MM0P)QLVoI0yl!*zb7JzJlT`0jd2`#Fxb1EfMd9JY zhosYKOw;7Sg9jWwe3&z5&Y-I5GmYoZ3;>{MnmBv*Ea`L_S(cGynS1x{k3v)jE;^zxAPYQ0I1jNqEIN%+uQqpIe%e*pThnN3zbN{>Y)K>0000bbVXQnWMOn= zI%9HWVRU5xGB7bTEip1JF*8&$GdeIiIx#XWFgQ9eFmVjmB>(^bC3HntbYx+4Wjbwd xWNBu305UK!F)c7OEio`uF)}(bF*-0bEigAaFffPyJZAs^002ovPDHLkV1jhUc%J|O diff --git a/impeller/third_party/stb/stb/tests/pngsuite/primary_check/z00n2c08.png b/impeller/third_party/stb/stb/tests/pngsuite/primary_check/z00n2c08.png deleted file mode 100644 index ecaa0d8bab72b7e37df0e032d9d28b2479f59ce1..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 422 zcmeAS@N?(olHy`uVBq!ia0vp^3LwnE1|*BCs=fdzmUKs7M+SzC{oH>NS%G}U;vjb? zhIQv;UV>C6dj$D1FjT2AFf_CjA5L~c#`DCET22U5qkch)aFYV-NFc4t5AgJ@`fBl-5Nx~maZQOnHuo_33xy9zG zO_LZ?7}9O4-%rWkQC^n9Vy9{3u!`}Vg^Y#LzXQGOAs>#-W&TrmoOes%a^5e6?Oexp z6gF7qNi?)c_fF`O?wQc89e&`rDD#PerMxE!TY1+!>=s?~n47Vr;33!629nO4bQ qXlZGoYha>lU>ag%WMyD#WeU__4%D#az+nZT1_n=8KbLh*2~7Z8tc<<@ diff --git a/impeller/third_party/stb/stb/tests/pngsuite/primary_check/z03n2c08.png b/impeller/third_party/stb/stb/tests/pngsuite/primary_check/z03n2c08.png deleted file mode 100644 index ecaa0d8bab72b7e37df0e032d9d28b2479f59ce1..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 422 zcmeAS@N?(olHy`uVBq!ia0vp^3LwnE1|*BCs=fdzmUKs7M+SzC{oH>NS%G}U;vjb? zhIQv;UV>C6dj$D1FjT2AFf_CjA5L~c#`DCET22U5qkch)aFYV-NFc4t5AgJ@`fBl-5Nx~maZQOnHuo_33xy9zG zO_LZ?7}9O4-%rWkQC^n9Vy9{3u!`}Vg^Y#LzXQGOAs>#-W&TrmoOes%a^5e6?Oexp z6gF7qNi?)c_fF`O?wQc89e&`rDD#PerMxE!TY1+!>=s?~n47Vr;33!629nO4bQ qXlZGoYha>lU>ag%WMyD#WeU__4%D#az+nZT1_n=8KbLh*2~7Z8tc<<@ diff --git a/impeller/third_party/stb/stb/tests/pngsuite/primary_check/z06n2c08.png b/impeller/third_party/stb/stb/tests/pngsuite/primary_check/z06n2c08.png deleted file mode 100644 index ecaa0d8bab72b7e37df0e032d9d28b2479f59ce1..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 422 zcmeAS@N?(olHy`uVBq!ia0vp^3LwnE1|*BCs=fdzmUKs7M+SzC{oH>NS%G}U;vjb? zhIQv;UV>C6dj$D1FjT2AFf_CjA5L~c#`DCET22U5qkch)aFYV-NFc4t5AgJ@`fBl-5Nx~maZQOnHuo_33xy9zG zO_LZ?7}9O4-%rWkQC^n9Vy9{3u!`}Vg^Y#LzXQGOAs>#-W&TrmoOes%a^5e6?Oexp z6gF7qNi?)c_fF`O?wQc89e&`rDD#PerMxE!TY1+!>=s?~n47Vr;33!629nO4bQ qXlZGoYha>lU>ag%WMyD#WeU__4%D#az+nZT1_n=8KbLh*2~7Z8tc<<@ diff --git a/impeller/third_party/stb/stb/tests/pngsuite/primary_check/z09n2c08.png b/impeller/third_party/stb/stb/tests/pngsuite/primary_check/z09n2c08.png deleted file mode 100644 index d869f992f80e61f073ea0f55a05aef941bfb6842..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 422 zcmeAS@N?(olHy`uVBq!ia0vp^3LwnE1|*BCs=fdzmUKs7M+SzC{oH>NS%G}U;vjb? zhIQv;UV>C6dj$D1FjT2AFf_CjA5L~c#`DCET22U5qkch)aFYV-NFc4t5AgJ@`fBl-5Nx~maZQOnHuo_33xy9zG zO_LZ?7}9O4-%rWkQC^n9Vy9{3u!`}Vg^Y#LzXQGOAs>#-W&TrmoOes%a^5e6?Oexp z6gF7qNi?)c_fF`O?wQc89e&`rDD#PerMxE!TY1+!>=s?~n47Vr;33!629nO4bQ qXlZGoYha>lU>ag%WMyD#WeQ}Q12tS1_$CF^z~JfX=d#Wzp$Pz2HjH2Z diff --git a/impeller/third_party/stb/stb/tests/pngsuite/unused/ccwn2c08.png b/impeller/third_party/stb/stb/tests/pngsuite/unused/ccwn2c08.png deleted file mode 100644 index 47c24817b79c1e2df8eb3f4fe43798f249a063d6..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1514 zcmV004R>004l5008;`004mK004C`008P>0026e000+ooVrmw000Gb zNkl1!`#jIX%gn1pggd$P9Zd(E8@>1o|NOaq`y>7F!|ZsTJzwK126G#Ly6%XM z@b-^5>QWhU%dqD1!_57RBTumVe+8~Hq9eTb8gGRJAwh?*OV}a2z_~^C|CZMU`+s$C zf=f^GMjIPqJ*EzYFsfNGT)Lap?PPm7_!7VJ=-g%pukx;sAJU$c-=j0zF+ofa5q!)a ze#6rhquT+FvGf6bpLReH((V#=343&VbR#+uVMHe)7;@qr4%Ccq1-RxEz0QX|?KUbv zg-D3?a5PfW@#w_lF{X>|p$bm?jc2~d`wI6~f#a;6WITuOqXKjXI#{2ULDi%_otUzR z>7gTt5je5J^Sd~Dle2ye@C#0R_#V2AZlgm~2OH2hsG43tH)a~4d#D*O2R?ta&G)tW z>8Ar@roZFTCSclIO*Ag1ArI+AG!Z7+1hzr17QOyU)Mwc7+NS`==$DuP*T?vqO>|IQ ztcPnUI`m>BLib<}*@i@om~~+l){c| zVM7=SBVi=G`3mjN^Uzb*0scX@ir!A!MZ4%0+DG}A03D)xh@t5bN9Y)dtvG|t!e{A2 zVJr-VH(rqM{a!t^`;)+*NnHXL-9oj{tt~?bm>$Z7h&)1VrgJrYXc=0@ma%1G8KuoD z&+_AUoChDj1~`crIu6P~xu_ZAq5Z4pRfm`!Wn^(jTpi2MGMYsT(bw1_m z-{DWcW?RoW2Sx1%q6`?p5#3d|Bp`vxK|FMT+rjJ7PN`Fx6q_I^ zHsSO@o_kdt`-!tWMURz18H)xjXp0u5EYvg!dKD%@(vVc71xZelk*1^>Sw>ormt<42 zhFmB#g+t{FiXi4}-?><#$4U#@DlI4r4PvoVssT2+X;jvbP04Gr zio7B#DN2fhGApO!Ash15FNBNxItO+uXX|?4s1n(L1}xIhR7?x<%j`IhKSXD!3`rZZ zhRl*T6jO>R>MU|4RaMv=y0F$9} zR4$TPn>VItC~Z@fNxj~;XU>`>Z{Pj*Eq?Q{1ADY{!yAorm%^@LYwChBA?vgK9NhuV zEWJQVYm2(9vPpCPqMTgf?}Pc|ffx6F>Cg}2g?nz4y9M0qW3N!>6l2^z@iOrMH$#`G zQl@pW(kx$*<@%1VVyS645OM(au{s2+Lb!Cm*v-(Kz~fjhVU4fw3PM64Z& QC;$Ke07*qoM6N<$g6Hha`2YX_ diff --git a/impeller/third_party/stb/stb/tests/pngsuite/unused/ccwn3p08.png b/impeller/third_party/stb/stb/tests/pngsuite/unused/ccwn3p08.png deleted file mode 100644 index 8bb2c10981594a83b7a8981436225d0b2241d27b..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1554 zcmWkudo+{@9Q~wiYbxpxyQH*PvQk+jg_3M0kMUetMjk0^ibC6FT8+ng>|mu<;>-?$ zm5gBE76za z`V2rL-V2)_s6XvdI|05qg3CyUFdEKU;xM`LGPZ>qA>#tN;_w)^vQv<0j}{EP(no$i z`mo0!AQSgK>Bh#O1Uo1c%9qgC08SDbT*Rc~7NA^cjuaHHr)sr@p!{AFnbKlF+h0Dke2lZ=BJFa3_1Pd-&6R(7q2>7?e`@` z2+zSi67nJu@aa}f)U3dTH8`j}NbZC|_wRhnwqpDkdIZnq^Mw#AAXh1rk|hC1a$`e# zV-YlQO+>2x#2+~ArqX?p(+hMM6oVzgAuTW+d5|lVcJnG(%P3BD)?#ZSHf%!Zby(CP zbrt$P!C~!psuiF|p!y)PNs5DXbQ*5lJ!*k+RpmZhEtn@Gs0a|;pZ zu~mOaE5qA45~jygEACV%-f(amkFH*arp)TM<_{!(td=Z&Rf=QC&qd6qVjPAfD<3nC zh_J^_V+ezxPwstYijw1t$B&EU&4{COWt zVR8e2Z{dXU?a)tyb#R1Iirg-^pTWKhiHJp<1{^AoqaTColG2eP?A-^hB^piMsr0bp zJVwAA0PXX=m^&{c!SpeNZ7$J;AsYN{&O*xN0eV#q7~bv=)=g+*8hLJE5TG$3=Ore@ zlDBF%NJ@Yi>YMI%3XnaBS#bVnhFOQ!p^C+C=TZ+i`hL#|-?g9Ev8Jly(eTN>(I6qk zs-@Z6yPEsqewkrTVN==Wzva^UFWypuE-|9M5ejGMwBbk&+nr}rsz)C=6Mg;7!p`W{ z*3~{qezB>k(KS802c-H%MJtOML$`!vXDrv4dA&B^?EA>Laq)wJ`AA9-`)?bXpnYtZ z**8#nC9r6}lYe*V7X^NmwdatS!q&aTv2GK}t$pbuI?lXOqlte1o8O&f1YZsfs{L&1 zZoz%=oa51tY^i+zB#M=_j$sXzMB?(7N*Xj(?)<=@b}om6+jNTRdt1f!@iLHfQk z`Im$Q_8Q769>r)~P>^hRDA_iy_5h*$Kk5ptj>x+)kf| zUCti|wcqQxc@1b(iexLF45xdH9&YI{Rtz*|(b+mwZY$GfOU5Nji;Hn)EvInIyw&H6VI7pV{6m$_g05>{kkUx-_69w3hFaYQorBj zDX_=;D|R(DV{iZR3fwcX5XUT6UNQ*_n^{<5*q_TfRA5^dnUj}yFa2X1o3rN@lnb*< z!|vRg*cA5kc1HTa*M$`o>FG@i|HPJ@sFVg?HagxLeoW@3zx4@sz5b)(>a_fdH;*5O z)i?OfjnGg^KQwW7n2!*3?6#5{%QaA_=g+OQ^R1n-BcAPeo8I7%7X;;=;1%j1i zZ)bCl8v&97rVft|P_iC!Ccs>)Nfzav-)4vs=ljCt>^(xPQBFB!f~}2 z33c`L-70SCvR3s|4U=$oHH9TFzjO1%!~RfY+0z-VM(^3 zbZpsM^d4*^09(kX9^w*ZmI5XJmB^)Ql!H<7e6ZY) zc`+QeUEqwtD6lI*MoSDvI5{}WC480&PtuOH*-2AVaV?E<`JCD_tpv-j#9PIaBFEN! ya5NvsJwe(+v{{WEX}38N*-?K$b^NQk+{YgmGj*nOAn>;U0000d_tM6?Z=$-SxY}-ICRK&4y|1 zVwc#f!dHA1xV88oBj*}@g$RWy_hMZ-KIm-D*-?;qSET3(i^4(8Km2Jkn4dfq@Se&3 zTKj@moXkY~bFu#0PPnZ(e1e0$_)enlnWhXC=IqbUUkI-WEMadnm^fjTNZTC0CDs-P zO1_Hd{`ll}k%^K0x`I<0XS2iy+u$b!SFTK5^fGI1n`4Un+K*`hy-NEv><+L^T&{2S kcJ9aK_IoSqvM<+joQ+wPvG4a1ptl)3UHx3vIVCg!05sc)VE_OC diff --git a/impeller/third_party/stb/stb/tests/pngsuite/unused/cdsn2c08.png b/impeller/third_party/stb/stb/tests/pngsuite/unused/cdsn2c08.png deleted file mode 100644 index 076c32cc082066dc870e1282b97c15ec22192407..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 232 zcmeAS@N?(olHy`uVBq!ia0vp^93afW1SGw4HSYi^mUKs7M+U~W1%@xC#RK`w#ZI0f zEG#VLKejFgaybh;B8!1EBN+Ru9=Qf&RC~HOhE&XPJr~G($U(&AVsKMqdZO+XCv8UV z2nJ4tMy~^h!kDxdYA>9+p~j^3=S&}?ojYTl4JO=Jai&15=FGw*d!IX*;+1!`qwm#r zn`WQ%3^IuS{KD+pZL<&+b@wcnUV-Cw0nJZSYySkqa&Hnkl_)Oke>c9oU4*NKQEP+# V1@X9R>wwN+@O1TaS?83{1OP3BOWptg diff --git a/impeller/third_party/stb/stb/tests/pngsuite/unused/cdun2c08.png b/impeller/third_party/stb/stb/tests/pngsuite/unused/cdun2c08.png deleted file mode 100644 index 846033be6b4fdec08cc010aa0abb35428b6659d0..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 724 zcmV;_0xSKAP)9p$>iIc zM9BG}R?-GyZPG$D`^%XGf`^qAr06IYS+9p{fK&k*ZoATR1wD8|WCM=~607$j`vY7L zV8zwkB3t?J3uaPidN3!<*KK9Z0BA!^-Kg`Xnb*QIyQ{+A355#ayA z0cpp|nK|8N%npZU=nH)H(bhrG@~;!&M3?XV2(`}^e@baTp03|x0oH!)@bHo=j4KQ0 zuY|$dbUuo5Y|+T(H30UBc@<4A(dF}I3{b5$!14M1g8c&!K-bPLY`YQw0000Vz>816FsF9(VN75txh7sS~8e>Vez z3y|esg8)MVP)qc0!95k9*}y}5sLX|@B3D-GMbkBlwnNjonHwIy z9EcJJF%{78L==oIJhA+`7;;cFolQHd7RYGxEQK6edY*pw@2LCip7ZW~-{<*#JkP!5 zYYTEB!lT29C?YRceiiRU!SD1?JmuDki+F_~O=B_)K_Hn((}+hbb3KVH`hCQ#8wM+c zbFwxPsNG;gO(S62+sIEOu%KnpUADK`#I{M2L=HJ6NYwOv+-H@D_X=#9?oI+MS;V>y zl^XVO2yC09!Icb&nREm`f)AWc25B~!P{MT$qI?}Geug!qSF%UJX^=e(A@Tsq&y_e(IV5N zc2ombmUg;9iHZVhFq;*ili#)pv6lr^Wf$(E5sCtE*M(vW0)!C2(rmO95VCxjCB6Yo zimt;CGRgER#Fo_!>WATkX^@enQK3N$>j4Nhm?Bc~Cc@4FTut;EC7;Rd?q$9UQBAH#yE#JE|Q0|h>!DU_)gvGGGRzl1C&lfZjR|WWg|k-fq8#JmDTaBI#^STUG9+W$DsWo438O zx%khhimTsDdcDtYX^8l8`@k~2aals#tw5h$TvR^EzC~aJtyXxM{*Nd)reoq~rm{KwpXnEYf zqB*0lzd&-2CvWIFQhLa@dP&k(Dd*Pij(dA}O2xf7|D4KEZ$_?Z8(4n($A+lx;)H8u z)fq!W+gl8UebBY<#K*_xetq?5tDJr_srucF zRWSz=YaUiV+I4>B_%&@-PtL)FX`y5J^WLm!j19!cj=uCt?f&G4P0QquI_{@U+1wXV|N8?-P52vntW^|UZqc(l!@kKG^x7(XjPW24#8yrdL)E4&VZN1_vo8296 zO!#K>^qsN&VVBEKXIzkd_mj&@tKO3RzQo4)w=OTPI2+x=IZaHg(y>8-4$mKjGb gSyRUo_cYqm_AGEC_tp3R7W~7>%Px>RvflahUvG13wg3PC diff --git a/impeller/third_party/stb/stb/tests/pngsuite/unused/cm0n0g04.png b/impeller/third_party/stb/stb/tests/pngsuite/unused/cm0n0g04.png deleted file mode 100644 index 9fba5db3b82ca7725816efca47adfb44d61292f0..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 292 zcmeAS@N?(olHy`uVBq!ia0vp^3Lwk^Bp4a_!xt_BxxKPq}K;y)F)+dv$%Po8B#%=u~U{}NXebp5O&tsV_+x6KC z`n6Ov-e|q7d~>yV!S#P~w_ZMf;qcvgkAe_q@|8lddEI`uLzP=J4Gp*J^)`wY2|W3b o#w`BCU_;}R^bC!qjU|5>nikk@kH_`o5%P%0-tcHxz2242t zZVAjc7P#3?WtxA)Gw_j&*7KvKZ>J=PsTO88@_NksIICmj$1M%HOyBFza{Sr);QoBh zi@mlwB0JLF_+GF!ImvlsMh zsc5{>dRh7AYV(5Y|Kx7HeE!1WyYn6eA`4jdmiWEU!L;8G5Gbb{58QQCkh zC%`R%`Nje_+o??Rk9Y_%RXc^_wWto*p8A(!cU{aKDbTOZt? z&v~)eR!3w<+8cjwuIKCvE|l~g&^Ym)^~t2`a?9Sjaa+F#*wwIpUv)*n^H^rfc767O zek~P^H(D<%-&}28aQ&a$t(VVVIDB{BqaehYe5FuqUbo-vP~{d)L&NQQy^W$p0#81q oF^fMj*wFYSJwsz@W656zxtoVvfBf&v2KtG?)78&qol`;+0Fu^nQ~&?~ diff --git a/impeller/third_party/stb/stb/tests/pngsuite/unused/cs3n2c16.png b/impeller/third_party/stb/stb/tests/pngsuite/unused/cs3n2c16.png deleted file mode 100644 index bf5fd20a20445fb7c9cf5d38a6ac4cd0fb28de29..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 214 zcmeAS@N?(olHy`uVBq!ia0vp^3Lq@N1SHpV7(M_}Ea{HEjtq=#3k+XOiwE+Vi=8|} zczJm*nD-t8a_c-@978JRyq$iK_lN@rGdt(YQy#z8YiT}?Tf!kXZS7nxn;(ocNRJS_ct9{9$oSsu1^4G$9qOxF^ITqWf!_v|1&zuJmL90#Z}{$h z;X$oW3rB}@4oBt#ju#pq{&7g{v2S8~pRDBMFgMZFh1uQVB)dVdT4qdrSvt@q44$rj JF6*2UngG#&N@f56 diff --git a/impeller/third_party/stb/stb/tests/pngsuite/unused/cs3n3p08.png b/impeller/third_party/stb/stb/tests/pngsuite/unused/cs3n3p08.png deleted file mode 100644 index f4a66237bfb3a113b7874d569367932762cd0ba8..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 259 zcmeAS@N?(olHy`uVBq!ia0vp^3LwnE3?yBabR7dyEa{HEjtq=#3k+XOiwE+Vi=8|} zn3vvNsN)kh^hn zQh3OQxBHhXtGq91lkmGBUgcb!!F~1uvkcz_zD4pSt$G)@WaeGq$X&o%(w=ugNaorF hmc0uYt(@e4Fe^D2KP>;UP7P=dgQu&X%Q~loCIF6>U!?#5 diff --git a/impeller/third_party/stb/stb/tests/pngsuite/unused/cs5n2c08.png b/impeller/third_party/stb/stb/tests/pngsuite/unused/cs5n2c08.png deleted file mode 100644 index 40f947c33e477af3d4c13e3a56efcf32e6871be2..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 186 zcmeAS@N?(olHy`uVBq!ia0vp^3LwnE1SJ1Ryj={WSkfJR9T^zg78t&m77yez7dv@| zu(GmBsNHi0a+5q=978JRyq&sHu)%@n@Dyu(NA}wKUA0-b10Ep zFS_6slkGtcsjSnkCm!EZ->w_6&B5QGsqfG`#!m_?Kf_rn1^#&$H05(u3N+bpIQ|n& cya`qx_w<}dzJS2a44`ccp00i_>zopr06uR!@c;k- diff --git a/impeller/third_party/stb/stb/tests/pngsuite/unused/cs5n3p08.png b/impeller/third_party/stb/stb/tests/pngsuite/unused/cs5n3p08.png deleted file mode 100644 index dfd6e6e6ecfcf1d0be69730fb34292c8b6561376..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 271 zcmeAS@N?(olHy`uVBq!ia0vp^3LwnE3?yBabR7dyEa{HEjtq=#3k+XOiwE+Vi=8|} zSXo&m)b2S0xd{P2A+G-=8Jzwz9R1J0@V}bj|6Ydw{~3U6Ab;uq|M!3d14Huv|8p1^ z6#xH^0;;k4Zv_-!cm`I*@IOisq;U=dQ0cS(K&=cd|AA%zO#~X&0+Nz+*Khp_w9DJm z#WAE}PU}Hd#sdrt4F>Ptz55$&$0R-3;cHXa2ac?Rsv0cq2_h{`K_9r-92Fv1Rz46e wX^Ir!40F`E!P1!^p3)Rkz@>FiH{%HZu|hk>x7R-I1{%xY>FVdQ&MBb@0QH?@PXGV_ diff --git a/impeller/third_party/stb/stb/tests/pngsuite/unused/cs8n2c08.png b/impeller/third_party/stb/stb/tests/pngsuite/unused/cs8n2c08.png deleted file mode 100644 index 8e01d3294ff71ea83918aa7f99589c7f88da81c4..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 149 zcmeAS@N?(olHy`uVBq!ia0vp^3LwnE1SJ1Ryj={WSkfJR9T^zg78t&m77yh6c)B=- zRLpsM)sdIMfX69t)`R5F>$8F{b!Pm_R9NudWu=&O_QeBy0tqv+c5v)_Q~Q&J!@#%m wUE>4C4G-c4A22Uq;csH)Zz^SCk$A@V`i@Ax+rlTSfaWrIy85}Sb4q9e0Oq;wP*-)CT0@c;jQ z2B6XdAldyud4`1l|LYkT9R35fGAR598n(G{b288_Z%-G;kcv622U!^p0CgC=d-v{d zv>lW5Rt15YCienPzJp3LSeh3I<}^i2;MQ`~516SSqSE9Pz!h~+uY+af0^y#f$PHY6 d2X!ls@@qb_WBltft&4Aruj!a10Ts~ zJwICdc1nVnYGHOGugAQPvpQCO+|rQC^u7Kp$Dgea?$77E*lVjJvLo${zc<%&_5~M8 z`VMHEc+dJ|(sj9IZ{4`9Uj*!GSii5jqTqQfvt_$JdqKaJipCqQmz8g>HZQpTPwv*s z=Pw+-JMU2t;!M6$C^oO#?{=tii>9IBcD>$4(ISB-AJUk`pBQXte3G7_v9z(|FN568 VL#{vmcV>h9>gnp|vd$@?2>`oyZOi}w diff --git a/impeller/third_party/stb/stb/tests/pngsuite/unused/ct1n0g04.png b/impeller/third_party/stb/stb/tests/pngsuite/unused/ct1n0g04.png deleted file mode 100644 index 3ba110aa764ff4e5ee642b7c0788229a4924d383..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 792 zcmYL_OH30%7{`}afYlm95ECCT0X3y5D?Mmv3<;G|C`zfeL@oq|?R0nSc4yh0QW_6Z zB`Pss;_F}x)Pp8ol!HcNj8URV4;m%W;Nzf@CWQndoG|e@TZkTZXTJH*|M$)JANBiM zE&Mi~<2Z}A&3%-u!2Al?kGu3WUCWk2$<^WF^3J3j^H+OWy@|GT(;%h_;{2)(`~)QFCN!|B{t=iEQyFKl3pA11?%3{l4##YE*?fOXKY0?i7L zFo3{7)ZE}Gl#l^L9YKmhfD%BFK>?K!KL~Y9VoW0n(d%eJWA~VYNr1%#!bFckDgdu4 zDzOmrLLKq_3KS!I;xY@vzGk!o4JwE-xE;hXl>t;V83H!9sv>3WjHfzO4UGX*F|iId z1mIE>(2r?d2x(L{m{B2dvyUheJ;X-m&DXNm7#b4I?m*L#fmAAVlv*Mz2B#TAfP`tB ziCpvgh%5jIe5gBU(6u;n^MRPbh@e_iqm*c>R4p7Y3ndt&JeMUlL>pcHB)>|a4ucs$lG3@ulPNE1@BXy#w+=g z&%HGh>bR@`vHY&O>~* zIQ52(KeeX66=g2YR^D3jY&bCQe>8me$%!E?+f(2?ySG@oVB2L~v@IkS-pGsyV*?Kcce{q!`rdz)M R88w6Bye&TWor8U+{sI?(Ab diff --git a/impeller/third_party/stb/stb/tests/pngsuite/unused/cten0g04.png b/impeller/third_party/stb/stb/tests/pngsuite/unused/cten0g04.png deleted file mode 100644 index a6a56faf2b3e8655f88c028070ac174823ccefd1..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 742 zcmZXRO=uHA6vrnJKT;a(wL;~!h$N6MrYd;wkTqLtKw~wL_E6C2W|AE`J2UJ|4DsMW zRM3MWg?cGs!J~Lk1VIoBT0AKzdTK$zg5c4M2SMLvLo0aLnfLj7^Zt8tx;m8|*gHT7 z$yQE!=kSL3OXEQnzrM}mm2SDGU6Q)-p!j|10{X+eK5OgT3Wg4oBr&IDtJdb0eR}j} zAI6WyMQ+;$DoU#Q_6!$-g>Z>VsQCfq3y|ydLM7949bbj{$r*TvU2e=ME8UHFE3gS> z+ugF@KV|{SRMG~8iG#)BhaZPl2}upcFpn8O;@Wa06WqT67jU-bg2ri3O`u?yjSU{s z7Bk>Sj9S(JtH3g2!3GQ$iMVQGP6-u_+pfks?1)n+Ei_dWQtLoP`l8*yUK%l&7t~)k z&zCi*cOQE4;+q~Gm>jRUR_FwgCHgJ~NWY!s>! z&+&Z~HMnf`UW>0Wn)$uyI%0-N>s&dK^-zSVSeKe=+76NvQMSBte_@n^LY2+djZMVq zZ3}G^^T)Idtu?rFVds1`b3(7;*|Nq{$ee}L~xhLtN m^^F(rmq*W#)$5mU+Z5S&`t$7h&xh|JmsBRJ-s9sJul@x+i~KYI diff --git a/impeller/third_party/stb/stb/tests/pngsuite/unused/ctfn0g04.png b/impeller/third_party/stb/stb/tests/pngsuite/unused/ctfn0g04.png deleted file mode 100644 index 353873ebbd0ca66c57ec7f6488e8f18a1c3517ac..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 716 zcmZXRL1+^}6ow}dghq?tNtEi?QzFS~JO~9rW1}X-)@p**gXox@CNDF)v+m4n$W41t z#Dj>S9;8}`R}Y>7A|4{OAV@F0t5^`c)r&$!-zHWNVP}W8^WOLG|Ih82=~JVrT`5Ay zXt`88hdYf|5?|#0$K?!e$wpzSKoU2f=D$3+jP-7)&6*k*$+4INa>i&7LXjE2QMF+B zx4p|{Jei2w3f44LIP9x&0j++QTmUJ#M6jjrPF;DKsTYq{ajXKnB>NAH%plenk*N$M+~b-foIWHJh@4$U`PV!RWLc-i8>vZ zYmJf~ieQMENeRYSMs>jJvF4}ickq$@uJ-#~=k4KZ7t!O<*mhOTnKlbIZ?4%wub_=G zpi=KsN5hy&~?ec@1Ie>$uaZM$zGZ32aTXT=*4DPUBk9b(-q_gZk&Rz>KfHfjULdK5 zH;yEd?bZG3$(5cvLVGXUPbMZ8$oxB!=x!zCN4Q?tvGV2>GD-Qwbn*G|EAzhrgC78w diff --git a/impeller/third_party/stb/stb/tests/pngsuite/unused/ctgn0g04.png b/impeller/third_party/stb/stb/tests/pngsuite/unused/ctgn0g04.png deleted file mode 100644 index 453f2b0a49ee544fcc6b297a59dd685a06285005..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1182 zcma)5T}V@57(R*-XciSoc=e?OF6PLfpo_retRxc2LEc1;b=(f_kL{>MM01;(Fe5Xf zEW;>*KsS#z?6{55l|t}-Nl;fsffq#=K@ibZJ>R!Yx(T}2`Tn23_uY6`=gHdY`f5c{ zYCBFj&SGTZSA~x<^=Zw5p~_=FV^=ChXInnsxgZWTXOHgGbf1ca>Qm?mrFnv~RNyhC zE8yuK&~%lJwVF6SA@14-bZ;Su$l0PA*pU0OeD z8j@SB5xKOvgMQ1~14sIRF)k3>f}zV{&EwTeptF>x41jZhOOQz`G{@+It7 zq5r*E79uniV^~U#1TBk;1#FwRoMJp7F=Q|ZpC|cn5p+yWa&RSYG-H^6B1xDi66P=rqhKk9 zjX zV6q@FSka1Dl36}0OPELY;915{wj$|8Qs^d#OUh)SAWLbaQD~L|kr3iUf+qY8(>!LG zl3p2k4w1mJU>90&RA7;i>J~Lfi+y;)8$&u0PT|J(^txqlxhuPaefp3qT>e|!m(?|S z9B{IR3f=xk*}xVT_Dc=084&AKdz=y7C3;Hc2G?;}%>tAlWaKC_M-~mCfL{-W0-gq2 z+oe}uA*xMwr517fT$*1k{{bh(OZhr2(Uu{LkBj1n6h&sDDgTmfvp!7w0C&E_VRtr7 z&X9elvTs}4y7y&e-9mJWwf9loLRU>sYfatao0<1tj;;RQeotYipJg&1R`2evblkc& zt=)|G^=q%|e_c&vDuCong?_VK6m>q(xv*Kj1}5zZ0E5m^qKHu!ynhmu$Vm7IxEZTxmWRJtOe)8nzKoFYPwIw!K*RB7e%yF z@Pvro5`0PUQPvpnINCH_b^NYfLeNRNVQtgB!EokEQbcBf)_^0b>7mDF0+E zS1ONHR48xVEehSw6nHuI1 zi1Qsu5>)3!^d{h7A_y#l*kM_gy_ooqopK`X8Q03EeQC7humk5Lwq3+zxN zH!h@?2)YxXDI?9PD2VK17?dF4c6$^4GEKO z3p|9D^*}JNT7@ZQc;-ZOK=8YA0%9oy zs*K(bI-FqXKGv&E&WP@!2~|o$0lyv$1w6`;Y_sJHNzVYy6H1~P${jFi_z33btHz1 zF3grRu&4GtmL4{+t8(L6@usQu?YouYqOb1{)iP$O`Sxb_^IwmVi`h@t+vciUyZ!(g CG05To diff --git a/impeller/third_party/stb/stb/tests/pngsuite/unused/ctjn0g04.png b/impeller/third_party/stb/stb/tests/pngsuite/unused/ctjn0g04.png deleted file mode 100644 index a77b8d2fee4c377c20b8e751317d094cc2be3cf4..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 941 zcmZWn$xB;77$27w5yewMq=%qTvTX0TTOEITtVlMs?! zQ(g4}zdG@xg~SC8ec3O_n1t!KbW{qL<7NeP=JUpscJ>oaWV0xVW3II$>gcJ z_fgZ}RzA!so>ibTT7l3RJ(vP#dLKsmEMuq_C|DnIPUEw8uhw1t1EWw?)+7$Vz;TLs-nJd z+}PZFnUj8UQe5owNs^*uwPa*u-Yxo*$bn9ItiZd}V*Z_YZ^iJn%Q4lWAD-c diff --git a/impeller/third_party/stb/stb/tests/pngsuite/unused/ctzn0g04.png b/impeller/third_party/stb/stb/tests/pngsuite/unused/ctzn0g04.png deleted file mode 100644 index b4401c9cfca842537838cec131e416ce9d7b0c26..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 753 zcmeAS@N?(olHy`uVBq!ia0vp^3Lwk^Bp4&$Z`J6 zNzb#P8s2AuIsDFfoH=tXkmKALZ-1Q=`d*<1Teg^7-D<+j(4EM?Bq(Z%5YVvQAj4cz zi<65o3raHc^FYSw_HE=lWWeL{z3!;5&(i+~S|SAYC&V}_-LdfE3H>h@z9Dj7b$L0{ z?V!z*(zQ3RbHvuze{%AF`9ST7(P3V_yAM6v__yY~zu>c0ws!SHZY7mF>(Xu3=spqF zYyE1=?(N&QTWCLD_9?x`{4#-_N0n=i%(89VqQ<1I<@)8j^q#<%rc7M<{~y>rT+&tj z?Xk|)VE%yIyVWz+p51BFUYon-WoSjdd+w#BKfbWae+pyylvLFL^o9e-8^QT$CFO}l zso)^d^YYd4^bI_5KG-AVgLf~7rkwsp-f`cTpI5{UVGdHyeY?`*0udct2*HvZ)!BFOeE9Eakt zF(>xoL0({Ru?EO%IdFX7kX@*}flE2$(Fs;VMri}4oB+23<{Jy#Y^O5KKjInqNJi`V z(bBh562w#svm1Fm=6#&ivGU`VhFqra^=CQ$Y<+NlKIg?=TOE-dX>a_!xt_BxxKPq} zK;y)F)+dv$%Po8B#%=u~U{}NXebp5O&tsV_+x6KC`n6Ov-e|q7d~>yV!S#P~w_ZMf z;qcvgkAe_q@|8lddEI`uLzP=J4Gp*J^)`wY2|W3b#w`BCU_;}R^bC!qjU|5>N8=ISR z4CLwk<95#Vg>&ou?4IBK-OKf4I=h+Q-Yu4^`-jJ;=a<*F_w~o;*Jiu>-k0SMV?!`9 z1Vck`We6?}!N3q)7=l6z!b$|W5>ORnBHWgrk_!4FAm#v-gL4rOlDZRAQhFl5C-ouJ zT{shgf4PuS-IlHh@Hud2s6Ob305=7HTh$nC5#jVOMRm%l2)`fuP_1(!!tE!01i`Tg zNcxC^mI$IC_VFh;N<~=u^=Vc`@;_)KBFl?^KZrv^P&WiMLvUaSEP*Ak#4p5s%2-gQ R&b9ym002ovPDHLkV1h+qhR^^2 diff --git a/impeller/third_party/stb/stb/tests/pngsuite/unused/f00n2c08.png b/impeller/third_party/stb/stb/tests/pngsuite/unused/f00n2c08.png deleted file mode 100644 index d6a1ffff62eb0f1f1dcdb4ba2b1d30473d3adacd..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 2475 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 diff --git a/impeller/third_party/stb/stb/tests/pngsuite/unused/f01n0g08.png b/impeller/third_party/stb/stb/tests/pngsuite/unused/f01n0g08.png deleted file mode 100644 index 4a1107b4637642335b8ec7b8e1856dc0ba7dab70..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 321 zcmV-H0lxl;P)}T4b;N0E z3!a!0=lJ%5$Eg%oz?c7nM_Pz6fDfO-!!(E~0KEA*c%UmW2WDfu_({31E3v>N-{Hyc z!o4_%Y@;b2{kGgyCmt~K_ux)+Vvc)%F>XgE7N{D;Epg%*RfD)GPQ0RO5I3R|@2DEY z_29%Oss?dQ2JwUcSEewBLHsU3nZgtbQQmYZ7(-i7*cJ?I3zBU?bOg~6L`VDql~F8% T6Mw4;00000NkvXXu0mjfmy?8r diff --git a/impeller/third_party/stb/stb/tests/pngsuite/unused/f01n2c08.png b/impeller/third_party/stb/stb/tests/pngsuite/unused/f01n2c08.png deleted file mode 100644 index 26fee958ce7e62a59843205ad34f86a525718595..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1180 zcmV;N1Y`S&P)Qj4`pI zFjkU)u{F?Akfo*B!a}174J9nsV(#3X+5PQ?pp&O~&o{|^n{V!Obi%NV*_mQj=dbpv zm_B(tFHYo4wlii|=dbiix4xT4UZ{eGTtw$J**>tSy6N}n|vqif78L&$byRK9f1$SzrLzUf{!*^r3k$774rXm_7!8f1Q2+ z=C=8j8BA;b>93ttmB!R4Gqr2#)Il4ngL#p|9QHl^o9+4V^!;`CWzciKaJiBz*2s-A z%Q~e~hNeLo)I~S+8N27*(|0^}KLTZc)b*DYt70WpQX@6;tlY_+JY<94KKc3jyKkHx zc~$=bes_90V0+^67$}#lCtg&T@Kv!ADxnb@aaQcaP8xdm`scunQ;gUj^(pYH(^C;! zNn5y!L|KC#`k`FGPq->y@fF|j4L>V%d?yTrfgi+0@98t($EU{=wvxAS)ubd$_PkV3 z!A+>5imOt?H8kTouFDPFK!bmL?KbeO)596d+rNC?VU*+1#52gCK!FJ=R8*lsgBd#N zFko=|Nq}~GkP6E2D88M@l8GXUm_!xTsfy-kVs>rq&*Z3{>ZqRPX`b1u2fF{_*^dDC$unBQ(Ew)&lzW^YzR^hz^JI^CidP9to*K@y zNDItSJ$~`R-vM{)Cyb~i-{XO3q;i~yCk82yJm$&k5=WK56seJ#sF9XvnK_+adK+*z zf5tFI)>7;!5IcJyUb1-f+PNSFERrJ@DN!XcC2FQ7s?jpdPCtJOaPPm$D2$0wv{X9^ zWaZ)t@yIozCq+^qMJ$rnCC-%0l&Qw=-{Go9xBkqiT4p;6#CFmU*E%r}BWWdJiJY-a zF^Vx6Z~f)!f}4M1bjGY@*ij%CA&7a#dtxL;QaWQsUcZ|{DNGZ!QmbmEH8z2;34}4U zEce==fUtrOcu$DLNKC}F#?F@|D_EgSluD^gm7PG?1j0Nt7TXI1ibI5i_qeUYM`9wZ zHAc+m{muoO$dyvD%1$6`0-+Ag%UwbcF5n#U_<-~Hh>wJXPsDV-gPixfkS9{*C=fP* zFb&F3UG{zuavL5Oa2^-&5ueTk!)M~U--S34D@TE_2?QJDp|V`eAXY9N+By{R372qb zf6pNl!i29J1;Qo}(jX4E_qp%Fz4^!GobzySIy|o?)B5V>_Wt4V>G|dL?fv8P>$@@5`4A$18fipC zj4{?)>#TR)dmloGF-Am6L9i(ZNIDP1wql`V1IXeb3GF@N^}7L$OLT?eW035 z>s-(z0SrY%YCz?nO#+xF1yoYnBznj5E`aJnr^KQQpxV+di7ue_plgdhP^rb}TH<8U z-_)#002ovPDHLkV1l$} BmFfTh diff --git a/impeller/third_party/stb/stb/tests/pngsuite/unused/f02n2c08.png b/impeller/third_party/stb/stb/tests/pngsuite/unused/f02n2c08.png deleted file mode 100644 index e590f1234816946731c677d0b353e2db5a08d71b..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1729 zcmV;y20r-x*}n2M`}#}vjc?j3 z-?s05&wlVj`|(fh7r(S$|JMHaC;Q7^?O*?~d&{=IVSD>_5?Nc!ER3!g3n}x;oHDCS zE7M9tnNlW|No7L0po}Y{%BV7;)RZA*NEuWHlu$uIF{MyCN=M0+Oi>NpMWV~bQp%#T zpv)_?%8W9jOe<4Ly-ylf#ww|%3|Gvc5-LF-)9o>#n>V*ev}9aNS<tNOUgyIH` zG-q7aJ|<;Bs%p}n(NLyR>cV6>o0KtOR2fOBDK%kO8B8li!+3&t(p()kWp0LO#<-$= zOvf4WkAB#DDgP)1#^?QNplUNhH+K4K#)MH}M8lng?S#!1>#?oI+;=QHd&_Y* zvQ2ag-A04p0Z)8E@cI#Yj2@#AdWt&Kp)E8)6V#)Eh6mnu6Sfn!T5QC&9<$>3pR;Ah zn~`mxSA%AGv*B#jvEq6SSw}a}hv?(JmFIitK6;2Y(MP>%q^zct!qYa9w`0$dr1uwhW0Zhu#4`YC3A2lof%qhmB?1mex?MvA0uiC*F<|9wjtewjbMGY&*`j zoNYQ=i;4%x3bKm)ELbUlhe-cy&Q~LkkWkfBOvHTK5^pVU&KE8zqNF%!6-RN=jJt=f zyYIStuDcy|)=2IKN9FDBuxB>$Tlj7K4t|&RE`A?>fIq}HX*X$yq{F5)WCR(dJ%%60 zUobyOQYUH9nkJsLcn-IKTf$xH1ii7(+IyZFAaXuHLUf39$aL7GW-`)eOjt5$$&|Gk z7SC8bXKudx>~4AcfL#|Bg#}?=m=$J(8L0w9n3UeL9uLU|&_U86bQr0b)M$_5M@xoe zf}~Ds%Hl?G{lDey;l^hyT6)`NFITQS5g;L1KsJC5kPe`O$dJh}?HYch$0Xw<6ZZU{ zd$-?t0XK_VuxLS_2oUXUsK?Ay2?S&T8dk=j$q?;fe2=lO{-L*jZof!e$2D-%xH*gF z^n8Gvsp5t2h@l@X8zXow7$3|f20{E)r!yT=R8p8tOF$?p``zg4{drQ+FJ#q^ho zXy)BBQMAcvVzRW z3^h_v5Ric9p5w+1ZrERCgI8Y-UVAOLeLHyLjo{85+yt&}QC+$AHbz)d7DMt5 zd6zUtb0njk;WJuFoZ^&7I1!H8G(892_Pp&Rww>5!%hqFCjqSd(WoK`>;%;>AP46PR z$UY&RE}af}mn)!IyrepUKyIwR{8>j(~{{02>UU6F*}Ye$EIT~(g*w> XLp~IzS7kjt00000NkvXXu0mjfY;#J> diff --git a/impeller/third_party/stb/stb/tests/pngsuite/unused/f03n0g08.png b/impeller/third_party/stb/stb/tests/pngsuite/unused/f03n0g08.png deleted file mode 100644 index ed01e2923c92fd8b8786524c6d6841365a44b9de..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 389 zcmV;00eb$4P)4Zd45D^U_MS@TiGz2aVLCXUNp&Fi;&<&!6t`aRYNwm-e(L$HR7llUs_2v2Lp?04i2#w>5 zCjFHgoS}>OqWhKs;-;4%0XoyCYd;H{ChLTAkL z`Nnmc1cXl84@#vy84@~$6^JKJlZ=Ubk`d9uF>y~YB0kwMamU0Lt#&uLFSbqGG0ztr z+6kZrX%qLj_#)n+{rCF=MU%cN0ReDq=R?P^VtFOGwjP)u6|4v*CxG_LFwq4OLVH6N jD*7N)_#jLSK#2GUV`p4Ky#5P000000NkvXXu0mjfx;dtp diff --git a/impeller/third_party/stb/stb/tests/pngsuite/unused/f03n2c08.png b/impeller/third_party/stb/stb/tests/pngsuite/unused/f03n2c08.png deleted file mode 100644 index 758115059d78260f7485521b83a864d4a930be84..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1291 zcmV+m1@!ufP)=&S9{TIAi*MDSax9$V&z4Gt?ul6?wozPIOm-?GqbzHVVL0@-rxKGKhOL8k5O_q zSFpv3t*o}I*V_9Zvg_8{M;@~qp0pb`*{3(#=eF8yFWQ%0wy%ua*LT{9H|_38yXP&t z_Z_?MUHjhq_P_`B;30eDsQu(qd+fM9al)Sb+AZ}0v>o^z(58vARqwd=u@6n@Pmke1XD_3x{N{!lS^S@Mz)_} zdJOa7^M*h)z+aJKlpUap>FUU*babUGt;6i!o}sK%&_Ml_0}Q1o&Mu`O$?Cq66Q@JT z3RxIYM!*w1@Du|XA%l7Q)eI$}3>DD;e|4H6l$4TCgo02|@B=KrQ z@Wkm5Gr226kv_(nB^X7=$b&B_9m1$G>KKWRgkdJ$$Rs9|g_2{8Ez5|#T1H2TVa9h$ z(j;+7%SwO->aXp@3<>@U&;J{qzZIUl8J_zyJbOJn`+IoiYIx>K`1Q0sy`|S$qy4Ql z0v=}L_DlonP=h(73bQba+*O)>Gz;H^EtdX#W}j_9D|OAoJhWjRA~5T>Ndu`v9p<2x zV$3LYKl;cP4%*<5UG=dYJ8U03Vjuo4ycMs0P50HI0S#zE(=pl(GY{sz&C+xa)e;>z z1~aNPzjM$1t-?9LR-*64P#mG-1*D8@fZ(*yqnYop#)_J&`RW&@^-8p-T}(?iQxc|RDc*( z<`4s3eE1|ejyabvT}B)cWe8L&9Y4_UCg9tx=>U_B7``SQ&NPmDBN;&XQlhP3n_*jascABYwWa1geh;>x-+i4(U_Y8$pQBrmp@_v#) zgF2F8aQOzn)E_eOLYJ|y`CQzx3-$nmLD=V&A(sP5g05dja_!i}_ zgIDirr|I{mZhX~}7YtBA6@@6ph=h6QI6&Kt{O(YUr$lPFM|Gd{aIa#L7cd2c1x1(E z(Q=Gs10<^TF-)&&>U0cv)zz`6SBo;LJ%(}H?`NdF>L`))0fxOAVsvH`hUsg)nABw~ zucMi=tL~(v8Fzi|4FCoh!(J`gSItz-2XxH%{{y9tm%W$Hmi_<$002ovPDHLkV1gMt BbC>`C diff --git a/impeller/third_party/stb/stb/tests/pngsuite/unused/f04n0g08.png b/impeller/third_party/stb/stb/tests/pngsuite/unused/f04n0g08.png deleted file mode 100644 index 663fdae3e7d906df7379d92621a7a951313705cc..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 269 zcmV+o0rLKdP){e2x4DAd1)!+NJ)f5B{T9psOdMM) z%F+|8FY)0+3~^>3vs1_&)hG|SqQp{zQhWvt^p*^yfsjOBNTM$!(HFu%2m|o|7A8yw TA(>!-00000NkvXXu0mjf-K%M) diff --git a/impeller/third_party/stb/stb/tests/pngsuite/unused/f04n2c08.png b/impeller/third_party/stb/stb/tests/pngsuite/unused/f04n2c08.png deleted file mode 100644 index 3c8b5116e72a63a3de8e0c1892c147071d0269e7..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 985 zcmV;~119{5P)hvyJ!8utT1VEc>UN*1Tj!qJPN}IY>N=sRACLdrt)_N=Jy&nmRP0foi1T3q z1vs!^NS8tnTiC(|3Cm|k`D(`N4+4W;;{3xH5HR3=UuuoS4K!$rOa`(R z?R*&X`GvfR1n&u#ACUWVNrjF@+CUA>kh2DD{mP;k>FRtL8*-Oa0-8az@Zi?_D?6u0xgjeiedR*>(yJr`MN%ucekr3 zWi?2)HMNKXi#p_|&$f}^7s7eRdN|{jt0Vw}4uOnAlX>OyYx{;(h32S_hezhCE(Fab znDm|v`WGp9zt{od@3wLWe5y}twl%v9eNEpS4jt=K5jm_s#Yobzz%^iBf5O6mQ4IVB z1gSS#SEq}Q@Y^iry7D|7u$1$9vV7%>V~{k^6C{`$4zP=FjwZm`$rGS zvDv1V?}l%U4QyZyYY4D{53qy=jUGER5T*mg4DDFWy$X8(2FG?szK8L%=^pMOKo~6M z$j~Pc+wCNgX-nw*aZjf2VMtF87IxrAsgQ{`|D+q<6)@}&$53fBJv+1hHpj`E}Y00000NkvXX Hu0mjft5eU! diff --git a/impeller/third_party/stb/stb/tests/pngsuite/unused/f99n0g04.png b/impeller/third_party/stb/stb/tests/pngsuite/unused/f99n0g04.png deleted file mode 100644 index 0b521c1d56ac6ba14d82547eea5c87dae391cfba..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 426 zcmV;b0agBqP)R*H$WiHScT#vd?>UqJiKVd^cv0p+pyQ zGH|gKM+__s5&6}tHkZ;`DS5Y9+rZhQ zwP@N_D-FoTR{3vHO$x#=5Cz~@tXf4-D8*gDQ#65;*9uNSx9chJUE%)%R{#^#^^XN{toLo}DWI?N<>C;~ z04ksXP5}*|#RS)@U%=Ob*$h}N)l>-ed1V5+W2XZEdA?ey6A(g5buxi@h4uQqlhPtM zpG^V)m`*nvwSYxHS5VI&O?NvOiV039lVCCd(iGV5g^==n1tvHiO#xM<`sZb@9~=`L r4yJ(5#RO&P7r+GDZJ=PW2opR2brp<;D@5J500000NkvXXu0mjfFu#U? diff --git a/impeller/third_party/stb/stb/tests/pngsuite/unused/g03n2c08.png b/impeller/third_party/stb/stb/tests/pngsuite/unused/g03n2c08.png deleted file mode 100644 index a9354dbee6f36cf88f7b6277d3fd43b6cabfd84a..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 370 zcmV-&0ge8NP)yO^U)m5Jsyp{zL>36n8GX#CQmg>mm9SE(8}cf*52*XGY_~ zh{+_6px=OtfYFFnf&gIz^!p-! z4E~_M*AoG%LANUc)ER`K2v7|=9TA|;pxqV$s(}np4O%S`KnAq|RD)(y1iYU?5QqRW z_=SEnxIkSs2+80F>Z$=A5dp}6`d>Z+8Ssq3bn47(xy-IHGcz-PeHq)$ diff --git a/impeller/third_party/stb/stb/tests/pngsuite/unused/g03n3p04.png b/impeller/third_party/stb/stb/tests/pngsuite/unused/g03n3p04.png deleted file mode 100644 index 60396c95af19fc5f14aff9f83d391331a32b3dab..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 214 zcmeAS@N?(olHy`uVBq!ia0vp^3LwnF3?v&v(vJfvmUKs7M+SzD9ea;@ZUFM-0(?ST zfi%Pa|Nj~OGn_cFb}hr*yC5EjfA|0Ywf_xwDRcl8Bzw9zhE&W+)?jB+RWM*uY zOI4Q~40T|+ae#?Kl#?gtjDxO+)}>1i-Z_XV1hX;(T`t)BOm^#Pkb^v3{an^LB{Ts5 DR8dIo diff --git a/impeller/third_party/stb/stb/tests/pngsuite/unused/g04n0g16.png b/impeller/third_party/stb/stb/tests/pngsuite/unused/g04n0g16.png deleted file mode 100644 index 32395b76c9c0e1cb962743e8a9c8c41f319a26bd..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 363 zcmV-x0hIoUP)k zMP!AUF&SCLg{ApYo}a%I2W<;z1p%J5#`A86=diAEu-(Goq6aWQ91jDiarl1YV72lz zSS}q6C}6R0GJYI*6ZWZ>;Ot> zZFbS1yTa-8J$*mz%z(DscaWy%bDwwTTMVko4oH&Ag@0}_C`%l49mFv}k^p3xQd%q3 z*T@Wt!e(&2nptb1gFLri;n!jYw;K*dqhSCu$TClZ{oV~g4L&I}i72HGV}Jkv002ov JPDHLkV1oB;ijV*R diff --git a/impeller/third_party/stb/stb/tests/pngsuite/unused/g04n2c08.png b/impeller/third_party/stb/stb/tests/pngsuite/unused/g04n2c08.png deleted file mode 100644 index a652b0ce87ed2824e2320154aeeb92915dab7be4..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 377 zcmV-<0fzpGP)=$qmFdR}P2oOfV zU|<4VgMQxxxCU`-0$hV$&jh#zHh>I1V1Ku30$c+d;Lf1aF#%*iSJZBs0Cxue-@pdA z2CbF}AcGU^M}sfOy9O~C5WqFSD>^`)3~T@e862Ae{Ka543z8O#a*afilwMz5gDA=u z(`i^Pv*IGJo|YG6Kr_1mR;%nWd2jp{WI%v#Gnh=mdi~I3QXrAR9su`drSUjyHa}Ju z8`J_uqi|tJ+HSMFZgOR)ZU*mlfZeWqG%Y-fK?ZMifZqlgyfy_OgO{cNWI%t*O&Od4 Xkxf^INQ0u+00000NkvXXu0mjffu*e-u`Y1b?z zzWXd(t64Wrb6MRl-MPm3%bnSi&5lTH;%2!xfw$vO?LWcx>5tgDx7P8NE*G2%a+Rm6 KpUXO@geCxuo>m_K diff --git a/impeller/third_party/stb/stb/tests/pngsuite/unused/g05n0g16.png b/impeller/third_party/stb/stb/tests/pngsuite/unused/g05n0g16.png deleted file mode 100644 index 70b37f01e2b9e13a30a6e9d3c51a114a2f0953ca..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 339 zcmV-Z0j&OsP)?t@NnF8DcSAbJ+I4}jD4==ze*zcJFZU7PBBkXpb0$0HQ1>69qAc{N%IA9o<0{k+y ztwZpB+XXm)2wVYpI0a#74`2wgEYEeVl>WRDLD#JdZnwEs3P4|pU@M>~u2)?HSOmDs z%O>jDD!>8DCayJ<2$}{h$mxDQ3z4SJr&0jRBM<=&(7Tr9be|rZe{e)l*V{)}8$|GW l@dAjTs?dUD3LXzX!4K_Ugk}Okv)}*#002ovPDHLkV1mh&gfRdB diff --git a/impeller/third_party/stb/stb/tests/pngsuite/unused/g05n2c08.png b/impeller/third_party/stb/stb/tests/pngsuite/unused/g05n2c08.png deleted file mode 100644 index 932c1365364fead1166e4772f29dcb7657f73f39..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 350 zcmV-k0iphhP)yIS#@w5JksQ*g`}>M?;BdsB#iGXl_D}xBv~JKVfNIcci2&6=2B-$jrU)Q|6XY8W5%7NoGC(z`*F^vs1lyuu23P2l z!4dKa0rt=jWC9bvghS52BVSt=-A}B^lR{o z!EBZfhpyn1c^lB7D8J-F$KT*fK)J|Z=K<*ToSF6eZn4N8k6hj>3?mmdm%hv(n&R wWU%o7coi9}As&7nsP&a50$ri)KG u9IC*rureVboiWQXC@_w%oP86+Of7~h92M4HZx@7u9OCKf=d#Wzp$PyhYDC`v diff --git a/impeller/third_party/stb/stb/tests/pngsuite/unused/g07n0g16.png b/impeller/third_party/stb/stb/tests/pngsuite/unused/g07n0g16.png deleted file mode 100644 index d6a47c2d5746fc93d252900c0b2286878b14a71b..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 321 zcmV-H0lxl;P)ic&7(nC-rHzraP|afnxc^RYsd`d`5u7poSz$l82kp{Tq3Z6JE6!C z<~cx~GsZc95JJcub)b0oLy=wE_B`cklP8(MI6l9^UC0cE!3;nQ`rgu@>+B3Z0>z=! T&@6K-00000NkvXXu0mjfZ=Zkq diff --git a/impeller/third_party/stb/stb/tests/pngsuite/unused/g07n2c08.png b/impeller/third_party/stb/stb/tests/pngsuite/unused/g07n2c08.png deleted file mode 100644 index 597346460f1999fb225a46c941751a063ce744f4..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 340 zcmV-a0jvIrP)Ec1Z7JAAD^)rl zGc;g3%p!~-&*xWAO~jWXaK$Npe&EOP4FJlLpC7?~0$d0v3LyXpLqMLRlR!xgvP=k& z8i)Z>gESQaqy}ODGPs5U>}G%fNg@Ow1N4Z*fS)rkMhK9;MgMOA0e3K$8Wd6hG8n;r zXmA1Z-7-K1S_=WdAR39-HfGgLqn?XbwVVOhUIuloww+K0g9o7Nrm9jK0F(g&e1^=# z-V(Wa{fz-XY1a&35Di3ZYyEC(6%kphw4Tp(^SMg{u4DAwEbq7P7ygd;HOjyT;8iGt mb0`2ZIE4Zr1N1W+%HRPOfLawl;h_ou0000jY(XgVYP$Ug2i0{3Ntua3PevZO<6g^Ah?s$m6bVyVK1BpBEle`W(ImUKs7M+U~W1%@xC#RK_!JzX3_ zD(1YMYRK23AmC~*&8=hG7IXO0C1yqTPaJWFFL_6JBpnoMIXcsY>1ImurhiOn%Vt#m z5fC`n=)o~7+xK(flFC$FU60NsKN=pf&Dn9QJZi=3cwrm4I|rNBb9(vyap&(_JYhlR zCEhOE8{3?ktnW4_&R8ZOEj;gf4*xH^W5+UNujMpVzT34~{@9PX!h3Wk|Ka$Mox;Fy ze2R(Tv5(acSr+AHDoQLDVbJLg`uAs{=eP4));+&@o#p&$o;{4OIA-jB`e#}P$b+7) KelF{r5}E)9`D>*B diff --git a/impeller/third_party/stb/stb/tests/pngsuite/unused/g10n2c08.png b/impeller/third_party/stb/stb/tests/pngsuite/unused/g10n2c08.png deleted file mode 100644 index b3039970c10e786659f9752913c41b8a26b1179a..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 285 zcmV+&0pk9NP)@();|)eY}Flu=NxRyTwAba19zW zNcaI2oGKYjCc{&aErrDdt_X+yXV}i(ARy1f{s-JoAU8t`q60wT=m4lx4WLj1#Goi5 z0}g9&GjO^Y?CP)v$sk{c!58?k4IU`q1^i?TP{4Hwg$wQL5H j&i)?k>vRoL{Z;$`)U8ca-rtX&00000NkvXXu0mjffk|^a diff --git a/impeller/third_party/stb/stb/tests/pngsuite/unused/g10n3p04.png b/impeller/third_party/stb/stb/tests/pngsuite/unused/g10n3p04.png deleted file mode 100644 index 1b6a6be2ca571956ce45bae5470c03cd2266fd89..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 214 zcmeAS@N?(olHy`uVBq!ia0vp^3LwnF3?v&v(vJfvmUKs7M+U~W1%@xC#RK_r0X`wF zKzij$28RFt|F8NV62kBw2K)ELwKW(gS)7nLjn&=f#?aQDJu;O{JayK!wx3QIX10fSyM0LA|{qo zr@2pEmRO+mfXPAELrdbu&4xCml^Xi(yO|>!d_)=C9<`pFX(h-9w2r~k)z4*}Q$iB} Dmq$l3 diff --git a/impeller/third_party/stb/stb/tests/pngsuite/unused/g25n0g16.png b/impeller/third_party/stb/stb/tests/pngsuite/unused/g25n0g16.png deleted file mode 100644 index a9f6787c7ab50968e37514529f53a6249762ca17..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 383 zcmV-_0f7FAP)$PJx&n)kH3|svhZZ(r0ivJ>sO?q;pvG#q^Pdnj*zaWkW*`MH zgTvuZ0}6;^p~3Np8;yq0Knh?6r_-MX=QC+2p~2;X8_lNB;Cht-m;nW}T0(={4L90t zp~3y`2b5*NfC2^sT+`Rg3?2{M=yd!5X7GIa0W_Ea1$4W905hON>h)}1tpH{-dv8qr zYh?yig&X}o0Kj6ArtTUrpa891&a%3ygBrvY39Yr(8X(K&^KXDL&H#scy)e;z2g~Jm zWsI8yn*n_$yCu)3Q-D0T+mfUnB%1+k42L$a*EYNPyBsudfL{?b7>)b@WOU@^z{4wh)7YG#lngB zpO4to4PwXAUtl<^EZyItpI$A4c0ho^z$dGfn@-I=c}&df(}j0HTdM#fvR(&W;o}K& zG9bWk2p1G&K>eeU5^p%h%I3sGP!q+00000NkvXXu0mjf+0UW; diff --git a/impeller/third_party/stb/stb/tests/pngsuite/unused/g25n3p04.png b/impeller/third_party/stb/stb/tests/pngsuite/unused/g25n3p04.png deleted file mode 100644 index 4f943c6175f31c3609bf4458fff9d3c591f112f7..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 215 zcmeAS@N?(olHy`uVBq!ia0vp^3LwnF3?v&v(vJfvmUKs7M+W8#6Mlqr_yhTJ0X`wF zK$<~Um*M|^0Re{pF+l47e;^$L6aWH-pup=-feKPQT^vIy=IlMUk+;Es$0ab@YvMKS z4NTlSPH?R`z_n#cTw`u=ph~f+$IqufMW?aPJ#nF}g2zjg8cRnT4C8a-ev#F_0RlCh$*z86iP| zaVyJO7(suuAQvVX1O;6NThywBgyle%p;kiF?Y!^7%*UPg-nqYX?l%`38+JvCR~8dd zB)L1WAJ5hPQxeAagYN1HJPOnCJ#h+k^;hS6n~B2RjrC1+_??J}9FY(yk@yB>bT*DpgTKuCNOVY>KHEazxSy09-hZpy3u#A`f8V zHsSyi48tL)AqHsN1Qs~Z7}}6FKO%n`+o-V&6GM>jhqkEH%nvXazo!T$>gO2@$wLLX z`|$=*dd46fKkdM#M|Gt4gY=kt2bIyaHXr#UDa%9SKVaIhyKeYwX|*h#PykDCr-AN)~w;?+^4yN zr-s8{bD3D{UA{K=ZMOCByHINGNoR9@dNzC}GcY)|Ju@)5BDQntP)ot4{Pc$K&wPET zb0n|#)BTaYxn$Ykj&#Mk)P>xtSgI$wXyNturRDT2Jr$Wxq-QF}D@JdO#pYwF-{qy3 za({X@R~7V)9!N#APsdv3V~y=a8^^ai{@Af>>)D}>Xm+S$ZFc#a+o9u`9GCs*$lY49 f5Z_z){0_hPQv2`7;*nR={=b*3Ye+n*O`ZD>%pJzM diff --git a/impeller/third_party/stb/stb/tests/pngsuite/unused/pp0n6a08.png b/impeller/third_party/stb/stb/tests/pngsuite/unused/pp0n6a08.png deleted file mode 100644 index 4ed7a30e4d16e0527c2695bf0493ec1ba156d8c4..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 818 zcmW-fy=zoa5XDDVMV1W~5)ehSNEfm%xBU z2&4*#Vrk)5qd16#e?&m+j8-!K=3aRF?(EE*Ip@rKwzzO@s(ZRCB2&ZbgJs_5>p6am z-v^V)Z(c|4^l$d%(39=C{jFOfho3IqSefU45fw2J7YUK9NLAECT{J|qlBQxN=3*h1 zRqHBl;w~QIS*@WGCgBnxkswK`q)EDDND_bzG*~oKoi(S9sk&;YW{o8>Q**UY%VI&& zP2JT)J&P2zVH&Ox8VN^QHBHksLzAhn1`N`$gcdmlgAFuTG-nB_nB!()mZgueMDFHc zo+TF+B*QJjB2f=1YAxL|ERlyF(4-qX&<_a|!WuA0!;(^XFc@s0!OC*3(!`u_&oUBy zj3tWjNPI;uEJ%8WCqCjd2EdDQj7BY1;s-S8#tt|k7=^F~4AOK$3myyx8|bVRRW)g1 zPO>qKL?2^`BC{6Q)?X~zMr)sKNkyvpUh8dE7RTmJZMz9B%1uKpztJiMZ4%IY4r~eY z(8Q?Quih^4C60%K{>u7`U*G4t?=SSOE=^ya`TK7B-cIk^kKIxK>$!`kdXxRvvtNF0 z{=1(CZ$@{A&yUj9`FybVvBPCeK0F$)bCK1Jv!jQXxa@3n&V2eaJ~@4&3S>CHFnD=o G?eTv$Q+DJ4 diff --git a/impeller/third_party/stb/stb/tests/pngsuite/unused/ps1n0g08.png b/impeller/third_party/stb/stb/tests/pngsuite/unused/ps1n0g08.png deleted file mode 100644 index 99625fa4ba1c6964446797075c47ee05dcae22f5..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1456 zcmXxkK}(cj5Ww*#aAOfA1P>uWA_;XUl%aEMY}7)@A`%EeORi%bMFPEcC<37_1p^(# z58$OQAu`alqXdS>Zh3N=`9rY#yvw`)eV+Lp_Wd|qc|1KiH`!X79z5x<=5@Z_-CWx9 znZ?_Anb_(*>$NNI50=lrzHF^Cw=;aYw)1B9?#A0!o2|;IT3D6USe-R$6&j%t8le#y zp-~#8Q5vOD8l^EBqcIwzF&d+B8mDm@r*Rsm37Vh@nxF}qs77!JE@{yjc z!KJtqm*P@fc~mxr%WxSk!)3T~kZN!lF2iNG3|Ed>4KBlFxD1!!N<=ld442_DT!t$@ zR)foM87{+RxH7B;m*Fy8hRbl}HrY5X$K|*jm*dJqs=?*B9GByATscBDxEz<`a$Jrp z2doB{<8oY%%W>uS)!=emj>~a5u3W0Y<+vP|<8oY?Wb^-3|G~f&xB^#hQ1@{KuD}(z z0#|Nd4X(fyxB^$;%EPO{6}SRd;0jzhS~a)=SKtaFjV?4UmX1mAVr#hYeYu!5+e{Vh*?JeDyT{@Zhyfr@Ddogvjv;XPO$z}Yw k`0#ss`FehH`ek!G(S~Q2+xLI{T(5`g;L%F|=;7$yKLBk?PXGV_ diff --git a/impeller/third_party/stb/stb/tests/pngsuite/unused/ps1n2c16.png b/impeller/third_party/stb/stb/tests/pngsuite/unused/ps1n2c16.png deleted file mode 100644 index 0c7a6b380e9a2e60c887a260d43a41553260fc74..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1620 zcmXxlUue~37{Kx0Hvcggjqqa5wnZ0q2=d=kL4nm-M{-~`!ka?f!Rk2%2GSHdS8n`@?ayH~ad{hwEnjwy0Kqsa!OTO|EN93>3xU&cXi8TL<6T z_xS63cfMIH^CJ6I2&=LtR%6Yv3XRYRjnD{<&?t@4D2>u6jnX8VM3ZO|O`=IOMq@Na zV>Cu%G?^yTWSUHqX>v4zOK?dMjTF&H5sial#U;1|m*5gy{x}+3f=h4-F2Us=iUyb9 z5?q2yaQT+e;1XPdOK=G;KQJ0xf=h4-F2UvFc%!%!m*P@fipz(M2AASeT#8F^IVBog zic4`RF2&^!qQRxO6qn*sT%JdROK~YK#ih7>vuJQBF2$v|6qg_6O~RFMC0q$t!sUZR zgDc@mxDu{}%g2lcSHhKWC0q%YBcj2Ta3x#`SHk6QqrsJMC0q$t!sTH!xDu{}E8$AG zd>d~Jm*Fy8hRbmIA<^J6T!zbV87?0o8eE3Ua2YPc%WxSkPrUj672m;(E91(ze1mw7E91(zGOmovw~q!_#+7kpTp5=i9u2OH zE91(zGAr)-96iXX{yejIXKXmyW6~f=;W#C=Y~#RUr~DD=C<7}Pd4V( zH~-n#+;niNVPDrK+m6lmtvvO7t!H)hgZe|I>Uj6!dzWuN&{h0UyVC!@}`gd2y$Mt{4pIX~;?82+n?%}i3yBA9R2bMfB^Yr&uM<0CVz0;%J!>30d8(#j? tiKe%Q>ay~W(faXa_sTCXI{$_Ic%%2f9ZRP!&BY%;-^MMK?|Q3;?gI^1pmYEL diff --git a/impeller/third_party/stb/stb/tests/pngsuite/unused/ps2n0g08.png b/impeller/third_party/stb/stb/tests/pngsuite/unused/ps2n0g08.png deleted file mode 100644 index 90b2979685772423e875e3504fcfee1d406d6a37..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 2320 zcmYk4K}%Fo7=}-vF^E!vi-@2W1+@s2p><45)If=Z1VNCbj)cC8aMZEGd0S>5Cs( zJyVm_Gd0qcmznjVR@Ql^~~0? zdZrdvOdOUJmK0Z{u%x&m$?BPHW%W!=R?o4*q*$@9zh`Rp)iX8wW@^~XkYryy^VYt4 zP9=Ty%sW{>) zq|8hTOUle7Sv}KLR?pOA^_=N)gPxgFR?pOA^_+de>Y2@C^-N7x&oK~I&$y7)Gc{Q~ z$Jzf|XMb5e<3v`^vD~r;U-oueZ@odkx4iv!b#3Khnd^M|I6XPJf2wok=+F7zj<`#D diff --git a/impeller/third_party/stb/stb/tests/pngsuite/unused/ps2n2c16.png b/impeller/third_party/stb/stb/tests/pngsuite/unused/ps2n2c16.png deleted file mode 100644 index a4a181e4ecfc378abcba1a2af79c0aa96eae57e1..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 2484 zcmYk4Z)ntY9LInAqm9Avh#oBGwjSvrRL)-t1*>k|sDbH-Jt?dmsdoql;x5t)bSqee zNKkNb>TLSJ5KOlMePH5(prGSe59(15hG-tNTI`k%!mfMs?)AmNe&6?fe)sx*KA#u& z(eTa}JKEQ`M^V%<^iukj#`&236U~k1#Si-y8b@m`^>Qj|nw;*d7DuC~tuQvcV`Oaq z;m7y9{pP+X(fEe`>OP`4q-;o~kjf#||4(sXlMo>hazvubuF(laZWRr1VAW|cg%5-gt86&BB` z!Q$D|`35{|PFOsv28(C!V;0Za3>MF-!Q$Bl%;H%tVDYRPES~M`!L7YNES}{A7SFca zV2ks|CmY^w52aHhhfn`p{I>o5Es69O3#ns29=&@1%e%#%wJo1aZp?mKE_6-~-s=Bl z`s2hv^z4%hCztjf8avwAzbPKA+^oI!&ei7MEBWNXbMa@jKb8-^{!>$?_SM8_b#b}* zNdElH+;jQ!w;oNtaA)^{mTlF=t<8T|cQj30tClb1raPu;LtQh^=X#&Wd{Eht%$(?1 zv-$;;WxjP*z>Dy^=4^c9w}F4(c<9=9i;ab& Np~0Q$O9Pqr{sYFSp`QQ% diff --git a/impeller/third_party/stb/stb/tests/resample_test.cpp b/impeller/third_party/stb/stb/tests/resample_test.cpp deleted file mode 100644 index 2c4a56ac0c4aa..0000000000000 --- a/impeller/third_party/stb/stb/tests/resample_test.cpp +++ /dev/null @@ -1,1116 +0,0 @@ -#include -#include - -#if defined(_WIN32) && _MSC_VER > 1200 -#define STBIR_ASSERT(x) \ - if (!(x)) { \ - __debugbreak(); \ - } else -#else -#include -#define STBIR_ASSERT(x) assert(x) -#endif - -#define STBIR_MALLOC stbir_malloc -#define STBIR_FREE stbir_free - -class stbir_context { -public: - stbir_context() - { - size = 1000000; - memory = malloc(size); - } - - ~stbir_context() - { - free(memory); - } - - size_t size; - void* memory; -} g_context; - -void* stbir_malloc(size_t size, void* context) -{ - if (!context) - return malloc(size); - - stbir_context* real_context = (stbir_context*)context; - if (size > real_context->size) - return 0; - - return real_context->memory; -} - -void stbir_free(void* memory, void* context) -{ - if (!context) - free(memory); -} - -//#include -void stbir_progress(float p) -{ - //printf("%f\n", p); - STBIR_ASSERT(p >= 0 && p <= 1); -} - -#define STBIR_PROGRESS_REPORT stbir_progress - -#define STB_IMAGE_RESIZE_IMPLEMENTATION -#define STB_IMAGE_RESIZE_STATIC -#include "stb_image_resize.h" - -#define STB_IMAGE_WRITE_IMPLEMENTATION -#include "stb_image_write.h" - -#define STB_IMAGE_IMPLEMENTATION -#include "stb_image.h" - -#ifdef _WIN32 -#include -#endif - -#include - -#define MT_SIZE 624 -static size_t g_aiMT[MT_SIZE]; -static size_t g_iMTI = 0; - -// Mersenne Twister implementation from Wikipedia. -// Avoiding use of the system rand() to be sure that our tests generate the same test data on any system. -void mtsrand(size_t iSeed) -{ - g_aiMT[0] = iSeed; - for (size_t i = 1; i < MT_SIZE; i++) - { - size_t inner1 = g_aiMT[i - 1]; - size_t inner2 = (g_aiMT[i - 1] >> 30); - size_t inner = inner1 ^ inner2; - g_aiMT[i] = (0x6c078965 * inner) + i; - } - - g_iMTI = 0; -} - -size_t mtrand() -{ - if (g_iMTI == 0) - { - for (size_t i = 0; i < MT_SIZE; i++) - { - size_t y = (0x80000000 & (g_aiMT[i])) + (0x7fffffff & (g_aiMT[(i + 1) % MT_SIZE])); - g_aiMT[i] = g_aiMT[(i + 397) % MT_SIZE] ^ (y >> 1); - if ((y % 2) == 1) - g_aiMT[i] = g_aiMT[i] ^ 0x9908b0df; - } - } - - size_t y = g_aiMT[g_iMTI]; - y = y ^ (y >> 11); - y = y ^ ((y << 7) & (0x9d2c5680)); - y = y ^ ((y << 15) & (0xefc60000)); - y = y ^ (y >> 18); - - g_iMTI = (g_iMTI + 1) % MT_SIZE; - - return y; -} - - -inline float mtfrand() -{ - const int ninenine = 999999; - return (float)(mtrand() % ninenine)/ninenine; -} - -static void resizer(int argc, char **argv) -{ - unsigned char* input_pixels; - unsigned char* output_pixels; - int w, h; - int n; - int out_w, out_h; - input_pixels = stbi_load(argv[1], &w, &h, &n, 0); - out_w = w*3; - out_h = h*3; - output_pixels = (unsigned char*) malloc(out_w*out_h*n); - //stbir_resize_uint8_srgb(input_pixels, w, h, 0, output_pixels, out_w, out_h, 0, n, -1,0); - stbir_resize_uint8(input_pixels, w, h, 0, output_pixels, out_w, out_h, 0, n); - stbi_write_png("output.png", out_w, out_h, n, output_pixels, 0); - exit(0); -} - -static void performance(int argc, char **argv) -{ - unsigned char* input_pixels; - unsigned char* output_pixels; - int w, h, count; - int n, i; - int out_w, out_h, srgb=1; - input_pixels = stbi_load(argv[1], &w, &h, &n, 0); - #if 0 - out_w = w/4; out_h = h/4; count=100; // 1 - #elif 0 - out_w = w*2; out_h = h/4; count=20; // 2 // note this is structured pessimily, would be much faster to downsample vertically first - #elif 0 - out_w = w/4; out_h = h*2; count=50; // 3 - #elif 0 - out_w = w*3; out_h = h*3; count=2; srgb=0; // 4 - #else - out_w = w*3; out_h = h*3; count=2; // 5 // this is dominated by linear->sRGB conversion - #endif - - output_pixels = (unsigned char*) malloc(out_w*out_h*n); - for (i=0; i < count; ++i) - if (srgb) - stbir_resize_uint8_srgb(input_pixels, w, h, 0, output_pixels, out_w, out_h, 0, n,-1,0); - else - stbir_resize(input_pixels, w, h, 0, output_pixels, out_w, out_h, 0, STBIR_TYPE_UINT8, n,-1, 0, STBIR_EDGE_CLAMP, STBIR_EDGE_CLAMP, STBIR_FILTER_DEFAULT, STBIR_FILTER_DEFAULT, STBIR_COLORSPACE_LINEAR, NULL); - exit(0); -} - -void test_suite(int argc, char **argv); - -int main(int argc, char** argv) -{ - //resizer(argc, argv); - //performance(argc, argv); - - test_suite(argc, argv); - return 0; -} - -void resize_image(const char* filename, float width_percent, float height_percent, stbir_filter filter, stbir_edge edge, stbir_colorspace colorspace, const char* output_filename) -{ - int w, h, n; - - unsigned char* input_data = stbi_load(filename, &w, &h, &n, 0); - if (!input_data) - { - printf("Input image could not be loaded\n"); - return; - } - - int out_w = (int)(w * width_percent); - int out_h = (int)(h * height_percent); - - unsigned char* output_data = (unsigned char*)malloc(out_w * out_h * n); - - stbir_resize(input_data, w, h, 0, output_data, out_w, out_h, 0, STBIR_TYPE_UINT8, n, STBIR_ALPHA_CHANNEL_NONE, 0, edge, edge, filter, filter, colorspace, &g_context); - - stbi_image_free(input_data); - - stbi_write_png(output_filename, out_w, out_h, n, output_data, 0); - - free(output_data); -} - -template -void convert_image(const F* input, T* output, int length) -{ - double f = (pow(2.0, 8.0 * sizeof(T)) - 1) / (pow(2.0, 8.0 * sizeof(F)) - 1); - for (int i = 0; i < length; i++) - output[i] = (T)(((double)input[i]) * f); -} - -template -void test_format(const char* file, float width_percent, float height_percent, stbir_datatype type, stbir_colorspace colorspace) -{ - int w, h, n; - unsigned char* input_data = stbi_load(file, &w, &h, &n, 0); - - if (input_data == NULL) - return; - - - int new_w = (int)(w * width_percent); - int new_h = (int)(h * height_percent); - - T* T_data = (T*)malloc(w * h * n * sizeof(T)); - memset(T_data, 0, w*h*n*sizeof(T)); - convert_image(input_data, T_data, w * h * n); - - T* output_data = (T*)malloc(new_w * new_h * n * sizeof(T)); - - stbir_resize(T_data, w, h, 0, output_data, new_w, new_h, 0, type, n, STBIR_ALPHA_CHANNEL_NONE, 0, STBIR_EDGE_CLAMP, STBIR_EDGE_CLAMP, STBIR_FILTER_CATMULLROM, STBIR_FILTER_CATMULLROM, colorspace, &g_context); - - free(T_data); - stbi_image_free(input_data); - - unsigned char* char_data = (unsigned char*)malloc(new_w * new_h * n * sizeof(char)); - convert_image(output_data, char_data, new_w * new_h * n); - - char output[200]; - sprintf(output, "test-output/type-%d-%d-%d-%d-%s", type, colorspace, new_w, new_h, file); - stbi_write_png(output, new_w, new_h, n, char_data, 0); - - free(char_data); - free(output_data); -} - -void convert_image_float(const unsigned char* input, float* output, int length) -{ - for (int i = 0; i < length; i++) - output[i] = ((float)input[i])/255; -} - -void convert_image_float(const float* input, unsigned char* output, int length) -{ - for (int i = 0; i < length; i++) - output[i] = (unsigned char)(stbir__saturate(input[i]) * 255); -} - -void test_float(const char* file, float width_percent, float height_percent, stbir_datatype type, stbir_colorspace colorspace) -{ - int w, h, n; - unsigned char* input_data = stbi_load(file, &w, &h, &n, 0); - - if (input_data == NULL) - return; - - int new_w = (int)(w * width_percent); - int new_h = (int)(h * height_percent); - - float* T_data = (float*)malloc(w * h * n * sizeof(float)); - convert_image_float(input_data, T_data, w * h * n); - - float* output_data = (float*)malloc(new_w * new_h * n * sizeof(float)); - - stbir_resize_float_generic(T_data, w, h, 0, output_data, new_w, new_h, 0, n, STBIR_ALPHA_CHANNEL_NONE, 0, STBIR_EDGE_CLAMP, STBIR_FILTER_CATMULLROM, colorspace, &g_context); - - free(T_data); - stbi_image_free(input_data); - - unsigned char* char_data = (unsigned char*)malloc(new_w * new_h * n * sizeof(char)); - convert_image_float(output_data, char_data, new_w * new_h * n); - - char output[200]; - sprintf(output, "test-output/type-%d-%d-%d-%d-%s", type, colorspace, new_w, new_h, file); - stbi_write_png(output, new_w, new_h, n, char_data, 0); - - free(char_data); - free(output_data); -} - -void test_channels(const char* file, float width_percent, float height_percent, int channels) -{ - int w, h, n; - unsigned char* input_data = stbi_load(file, &w, &h, &n, 0); - - if (input_data == NULL) - return; - - int new_w = (int)(w * width_percent); - int new_h = (int)(h * height_percent); - - unsigned char* channels_data = (unsigned char*)malloc(w * h * channels * sizeof(unsigned char)); - - for (int i = 0; i < w * h; i++) - { - int input_position = i * n; - int output_position = i * channels; - - for (int c = 0; c < channels; c++) - channels_data[output_position + c] = input_data[input_position + stbir__min(c, n)]; - } - - unsigned char* output_data = (unsigned char*)malloc(new_w * new_h * channels * sizeof(unsigned char)); - - stbir_resize_uint8_srgb(channels_data, w, h, 0, output_data, new_w, new_h, 0, channels, STBIR_ALPHA_CHANNEL_NONE, 0); - - free(channels_data); - stbi_image_free(input_data); - - char output[200]; - sprintf(output, "test-output/channels-%d-%d-%d-%s", channels, new_w, new_h, file); - stbi_write_png(output, new_w, new_h, channels, output_data, 0); - - free(output_data); -} - -void test_subpixel(const char* file, float width_percent, float height_percent, float s1, float t1) -{ - int w, h, n; - unsigned char* input_data = stbi_load(file, &w, &h, &n, 0); - - if (input_data == NULL) - return; - - s1 = ((float)w - 1 + s1)/w; - t1 = ((float)h - 1 + t1)/h; - - int new_w = (int)(w * width_percent); - int new_h = (int)(h * height_percent); - - unsigned char* output_data = (unsigned char*)malloc(new_w * new_h * n * sizeof(unsigned char)); - - stbir_resize_region(input_data, w, h, 0, output_data, new_w, new_h, 0, STBIR_TYPE_UINT8, n, STBIR_ALPHA_CHANNEL_NONE, 0, STBIR_EDGE_CLAMP, STBIR_EDGE_CLAMP, STBIR_FILTER_BOX, STBIR_FILTER_CATMULLROM, STBIR_COLORSPACE_SRGB, &g_context, 0, 0, s1, t1); - - stbi_image_free(input_data); - - char output[200]; - sprintf(output, "test-output/subpixel-%d-%d-%f-%f-%s", new_w, new_h, s1, t1, file); - stbi_write_png(output, new_w, new_h, n, output_data, 0); - - free(output_data); -} - -void test_subpixel_region(const char* file, float width_percent, float height_percent, float s0, float t0, float s1, float t1) -{ - int w, h, n; - unsigned char* input_data = stbi_load(file, &w, &h, &n, 0); - - if (input_data == NULL) - return; - - int new_w = (int)(w * width_percent); - int new_h = (int)(h * height_percent); - - unsigned char* output_data = (unsigned char*)malloc(new_w * new_h * n * sizeof(unsigned char)); - - stbir_resize_region(input_data, w, h, 0, output_data, new_w, new_h, 0, STBIR_TYPE_UINT8, n, STBIR_ALPHA_CHANNEL_NONE, 0, STBIR_EDGE_CLAMP, STBIR_EDGE_CLAMP, STBIR_FILTER_BOX, STBIR_FILTER_CATMULLROM, STBIR_COLORSPACE_SRGB, &g_context, s0, t0, s1, t1); - - stbi_image_free(input_data); - - char output[200]; - sprintf(output, "test-output/subpixel-region-%d-%d-%f-%f-%f-%f-%s", new_w, new_h, s0, t0, s1, t1, file); - stbi_write_png(output, new_w, new_h, n, output_data, 0); - - free(output_data); -} - -void test_subpixel_command(const char* file, float width_percent, float height_percent, float x_scale, float y_scale, float x_offset, float y_offset) -{ - int w, h, n; - unsigned char* input_data = stbi_load(file, &w, &h, &n, 0); - - if (input_data == NULL) - return; - - int new_w = (int)(w * width_percent); - int new_h = (int)(h * height_percent); - - unsigned char* output_data = (unsigned char*)malloc(new_w * new_h * n * sizeof(unsigned char)); - - stbir_resize_subpixel(input_data, w, h, 0, output_data, new_w, new_h, 0, STBIR_TYPE_UINT8, n, STBIR_ALPHA_CHANNEL_NONE, 0, STBIR_EDGE_CLAMP, STBIR_EDGE_CLAMP, STBIR_FILTER_BOX, STBIR_FILTER_CATMULLROM, STBIR_COLORSPACE_SRGB, &g_context, x_scale, y_scale, x_offset, y_offset); - - stbi_image_free(input_data); - - char output[200]; - sprintf(output, "test-output/subpixel-command-%d-%d-%f-%f-%f-%f-%s", new_w, new_h, x_scale, y_scale, x_offset, y_offset, file); - stbi_write_png(output, new_w, new_h, n, output_data, 0); - - free(output_data); -} - -unsigned int* pixel(unsigned int* buffer, int x, int y, int c, int w, int n) -{ - return &buffer[y*w*n + x*n + c]; -} - -void test_premul() -{ - unsigned int input[2 * 2 * 4]; - unsigned int output[1 * 1 * 4]; - unsigned int output2[2 * 2 * 4]; - - memset(input, 0, sizeof(input)); - - // First a test to make sure premul is working properly. - - // Top left - solid red - *pixel(input, 0, 0, 0, 2, 4) = 255; - *pixel(input, 0, 0, 3, 2, 4) = 255; - - // Bottom left - solid red - *pixel(input, 0, 1, 0, 2, 4) = 255; - *pixel(input, 0, 1, 3, 2, 4) = 255; - - // Top right - transparent green - *pixel(input, 1, 0, 1, 2, 4) = 255; - *pixel(input, 1, 0, 3, 2, 4) = 25; - - // Bottom right - transparent green - *pixel(input, 1, 1, 1, 2, 4) = 255; - *pixel(input, 1, 1, 3, 2, 4) = 25; - - stbir_resize(input, 2, 2, 0, output, 1, 1, 0, STBIR_TYPE_UINT32, 4, 3, 0, STBIR_EDGE_CLAMP, STBIR_EDGE_CLAMP, STBIR_FILTER_BOX, STBIR_FILTER_BOX, STBIR_COLORSPACE_LINEAR, &g_context); - - float r = (float)255 / 4294967296; - float g = (float)255 / 4294967296; - float ra = (float)255 / 4294967296; - float ga = (float)25 / 4294967296; - float a = (ra + ga) / 2; - - STBIR_ASSERT(output[0] == (unsigned int)(r * ra / 2 / a * 4294967296 + 0.5f)); // 232 - STBIR_ASSERT(output[1] == (unsigned int)(g * ga / 2 / a * 4294967296 + 0.5f)); // 23 - STBIR_ASSERT(output[2] == 0); - STBIR_ASSERT(output[3] == (unsigned int)(a * 4294967296 + 0.5f)); // 140 - - // Now a test to make sure it doesn't clobber existing values. - - // Top right - completely transparent green - *pixel(input, 1, 0, 1, 2, 4) = 255; - *pixel(input, 1, 0, 3, 2, 4) = 0; - - // Bottom right - completely transparent green - *pixel(input, 1, 1, 1, 2, 4) = 255; - *pixel(input, 1, 1, 3, 2, 4) = 0; - - stbir_resize(input, 2, 2, 0, output2, 2, 2, 0, STBIR_TYPE_UINT32, 4, 3, 0, STBIR_EDGE_CLAMP, STBIR_EDGE_CLAMP, STBIR_FILTER_BOX, STBIR_FILTER_BOX, STBIR_COLORSPACE_LINEAR, &g_context); - - STBIR_ASSERT(*pixel(output2, 0, 0, 0, 2, 4) == 255); - STBIR_ASSERT(*pixel(output2, 0, 0, 1, 2, 4) == 0); - STBIR_ASSERT(*pixel(output2, 0, 0, 2, 2, 4) == 0); - STBIR_ASSERT(*pixel(output2, 0, 0, 3, 2, 4) == 255); - - STBIR_ASSERT(*pixel(output2, 0, 1, 0, 2, 4) == 255); - STBIR_ASSERT(*pixel(output2, 0, 1, 1, 2, 4) == 0); - STBIR_ASSERT(*pixel(output2, 0, 1, 2, 2, 4) == 0); - STBIR_ASSERT(*pixel(output2, 0, 1, 3, 2, 4) == 255); - - STBIR_ASSERT(*pixel(output2, 1, 0, 0, 2, 4) == 0); - STBIR_ASSERT(*pixel(output2, 1, 0, 1, 2, 4) == 255); - STBIR_ASSERT(*pixel(output2, 1, 0, 2, 2, 4) == 0); - STBIR_ASSERT(*pixel(output2, 1, 0, 3, 2, 4) == 0); - - STBIR_ASSERT(*pixel(output2, 1, 1, 0, 2, 4) == 0); - STBIR_ASSERT(*pixel(output2, 1, 1, 1, 2, 4) == 255); - STBIR_ASSERT(*pixel(output2, 1, 1, 2, 2, 4) == 0); - STBIR_ASSERT(*pixel(output2, 1, 1, 3, 2, 4) == 0); -} - -// test that splitting a pow-2 image into tiles produces identical results -void test_subpixel_1() -{ - unsigned char image[8 * 8]; - - mtsrand(0); - - for (int i = 0; i < sizeof(image); i++) - image[i] = mtrand() & 255; - - unsigned char output_data[16 * 16]; - - stbir_resize_region(image, 8, 8, 0, output_data, 16, 16, 0, STBIR_TYPE_UINT8, 1, STBIR_ALPHA_CHANNEL_NONE, 0, STBIR_EDGE_CLAMP, STBIR_EDGE_CLAMP, STBIR_FILTER_CATMULLROM, STBIR_FILTER_CATMULLROM, STBIR_COLORSPACE_SRGB, &g_context, 0, 0, 1, 1); - - unsigned char output_left[8 * 16]; - unsigned char output_right[8 * 16]; - - stbir_resize_region(image, 8, 8, 0, output_left, 8, 16, 0, STBIR_TYPE_UINT8, 1, STBIR_ALPHA_CHANNEL_NONE, 0, STBIR_EDGE_CLAMP, STBIR_EDGE_CLAMP, STBIR_FILTER_CATMULLROM, STBIR_FILTER_CATMULLROM, STBIR_COLORSPACE_SRGB, &g_context, 0, 0, 0.5f, 1); - stbir_resize_region(image, 8, 8, 0, output_right, 8, 16, 0, STBIR_TYPE_UINT8, 1, STBIR_ALPHA_CHANNEL_NONE, 0, STBIR_EDGE_CLAMP, STBIR_EDGE_CLAMP, STBIR_FILTER_CATMULLROM, STBIR_FILTER_CATMULLROM, STBIR_COLORSPACE_SRGB, &g_context, 0.5f, 0, 1, 1); - - for (int x = 0; x < 8; x++) - { - for (int y = 0; y < 16; y++) - { - STBIR_ASSERT(output_data[y * 16 + x] == output_left[y * 8 + x]); - STBIR_ASSERT(output_data[y * 16 + x + 8] == output_right[y * 8 + x]); - } - } - - stbir_resize_subpixel(image, 8, 8, 0, output_left, 8, 16, 0, STBIR_TYPE_UINT8, 1, STBIR_ALPHA_CHANNEL_NONE, 0, STBIR_EDGE_CLAMP, STBIR_EDGE_CLAMP, STBIR_FILTER_CATMULLROM, STBIR_FILTER_CATMULLROM, STBIR_COLORSPACE_SRGB, &g_context, 2, 2, 0, 0); - stbir_resize_subpixel(image, 8, 8, 0, output_right, 8, 16, 0, STBIR_TYPE_UINT8, 1, STBIR_ALPHA_CHANNEL_NONE, 0, STBIR_EDGE_CLAMP, STBIR_EDGE_CLAMP, STBIR_FILTER_CATMULLROM, STBIR_FILTER_CATMULLROM, STBIR_COLORSPACE_SRGB, &g_context, 2, 2, 8, 0); - - {for (int x = 0; x < 8; x++) - { - for (int y = 0; y < 16; y++) - { - STBIR_ASSERT(output_data[y * 16 + x] == output_left[y * 8 + x]); - STBIR_ASSERT(output_data[y * 16 + x + 8] == output_right[y * 8 + x]); - } - }} -} - -// test that replicating an image and using a subtile of it produces same results as wraparound -void test_subpixel_2() -{ - unsigned char image[8 * 8]; - - mtsrand(0); - - for (int i = 0; i < sizeof(image); i++) - image[i] = mtrand() & 255; - - unsigned char large_image[32 * 32]; - - for (int x = 0; x < 8; x++) - { - for (int y = 0; y < 8; y++) - { - for (int i = 0; i < 4; i++) - { - for (int j = 0; j < 4; j++) - large_image[j*4*8*8 + i*8 + y*4*8 + x] = image[y*8 + x]; - } - } - } - - unsigned char output_data_1[16 * 16]; - unsigned char output_data_2[16 * 16]; - - stbir_resize(image, 8, 8, 0, output_data_1, 16, 16, 0, STBIR_TYPE_UINT8, 1, STBIR_ALPHA_CHANNEL_NONE, 0, STBIR_EDGE_WRAP, STBIR_EDGE_WRAP, STBIR_FILTER_CATMULLROM, STBIR_FILTER_CATMULLROM, STBIR_COLORSPACE_SRGB, &g_context); - stbir_resize_region(large_image, 32, 32, 0, output_data_2, 16, 16, 0, STBIR_TYPE_UINT8, 1, STBIR_ALPHA_CHANNEL_NONE, 0, STBIR_EDGE_WRAP, STBIR_EDGE_WRAP, STBIR_FILTER_CATMULLROM, STBIR_FILTER_CATMULLROM, STBIR_COLORSPACE_SRGB, &g_context, 0.25f, 0.25f, 0.5f, 0.5f); - - {for (int x = 0; x < 16; x++) - { - for (int y = 0; y < 16; y++) - STBIR_ASSERT(output_data_1[y * 16 + x] == output_data_2[y * 16 + x]); - }} - - stbir_resize_subpixel(large_image, 32, 32, 0, output_data_2, 16, 16, 0, STBIR_TYPE_UINT8, 1, STBIR_ALPHA_CHANNEL_NONE, 0, STBIR_EDGE_WRAP, STBIR_EDGE_WRAP, STBIR_FILTER_CATMULLROM, STBIR_FILTER_CATMULLROM, STBIR_COLORSPACE_SRGB, &g_context, 2, 2, 16, 16); - - {for (int x = 0; x < 16; x++) - { - for (int y = 0; y < 16; y++) - STBIR_ASSERT(output_data_1[y * 16 + x] == output_data_2[y * 16 + x]); - }} -} - -// test that 0,0,1,1 subpixel produces same result as no-rect -void test_subpixel_3() -{ - unsigned char image[8 * 8]; - - mtsrand(0); - - for (int i = 0; i < sizeof(image); i++) - image[i] = mtrand() & 255; - - unsigned char output_data_1[32 * 32]; - unsigned char output_data_2[32 * 32]; - - stbir_resize_region(image, 8, 8, 0, output_data_1, 32, 32, 0, STBIR_TYPE_UINT8, 1, 0, STBIR_ALPHA_CHANNEL_NONE, STBIR_EDGE_CLAMP, STBIR_EDGE_CLAMP, STBIR_FILTER_CATMULLROM, STBIR_FILTER_CATMULLROM, STBIR_COLORSPACE_LINEAR, NULL, 0, 0, 1, 1); - stbir_resize_uint8(image, 8, 8, 0, output_data_2, 32, 32, 0, 1); - - for (int x = 0; x < 32; x++) - { - for (int y = 0; y < 32; y++) - STBIR_ASSERT(output_data_1[y * 32 + x] == output_data_2[y * 32 + x]); - } - - stbir_resize_subpixel(image, 8, 8, 0, output_data_1, 32, 32, 0, STBIR_TYPE_UINT8, 1, 0, STBIR_ALPHA_CHANNEL_NONE, STBIR_EDGE_CLAMP, STBIR_EDGE_CLAMP, STBIR_FILTER_CATMULLROM, STBIR_FILTER_CATMULLROM, STBIR_COLORSPACE_LINEAR, NULL, 4, 4, 0, 0); - - {for (int x = 0; x < 32; x++) - { - for (int y = 0; y < 32; y++) - STBIR_ASSERT(output_data_1[y * 32 + x] == output_data_2[y * 32 + x]); - }} -} - -// test that 1:1 resample using s,t=0,0,1,1 with bilinear produces original image -void test_subpixel_4() -{ - unsigned char image[8 * 8]; - - mtsrand(0); - - for (int i = 0; i < sizeof(image); i++) - image[i] = mtrand() & 255; - - unsigned char output[8 * 8]; - - stbir_resize_region(image, 8, 8, 0, output, 8, 8, 0, STBIR_TYPE_UINT8, 1, STBIR_ALPHA_CHANNEL_NONE, 0, STBIR_EDGE_CLAMP, STBIR_EDGE_CLAMP, STBIR_FILTER_TRIANGLE, STBIR_FILTER_TRIANGLE, STBIR_COLORSPACE_LINEAR, &g_context, 0, 0, 1, 1); - STBIR_ASSERT(memcmp(image, output, 8 * 8) == 0); - - stbir_resize_subpixel(image, 8, 8, 0, output, 8, 8, 0, STBIR_TYPE_UINT8, 1, STBIR_ALPHA_CHANNEL_NONE, 0, STBIR_EDGE_CLAMP, STBIR_EDGE_CLAMP, STBIR_FILTER_TRIANGLE, STBIR_FILTER_TRIANGLE, STBIR_COLORSPACE_LINEAR, &g_context, 1, 1, 0, 0); - STBIR_ASSERT(memcmp(image, output, 8 * 8) == 0); -} - -static unsigned int image88_int[8][8]; -static unsigned char image88 [8][8]; -static unsigned char output88[8][8]; -static unsigned char output44[4][4]; -static unsigned char output22[2][2]; -static unsigned char output11[1][1]; - -void resample_88(stbir_filter filter) -{ - stbir_resize_uint8_generic(image88[0],8,8,0, output88[0],8,8,0, 1,-1,0, STBIR_EDGE_CLAMP, filter, STBIR_COLORSPACE_LINEAR, NULL); - stbir_resize_uint8_generic(image88[0],8,8,0, output44[0],4,4,0, 1,-1,0, STBIR_EDGE_CLAMP, filter, STBIR_COLORSPACE_LINEAR, NULL); - stbir_resize_uint8_generic(image88[0],8,8,0, output22[0],2,2,0, 1,-1,0, STBIR_EDGE_CLAMP, filter, STBIR_COLORSPACE_LINEAR, NULL); - stbir_resize_uint8_generic(image88[0],8,8,0, output11[0],1,1,0, 1,-1,0, STBIR_EDGE_CLAMP, filter, STBIR_COLORSPACE_LINEAR, NULL); -} - -void verify_box(void) -{ - int i,j,t; - - resample_88(STBIR_FILTER_BOX); - - for (i=0; i < sizeof(image88); ++i) - STBIR_ASSERT(image88[0][i] == output88[0][i]); - - t = 0; - for (j=0; j < 4; ++j) - for (i=0; i < 4; ++i) { - int n = image88[j*2+0][i*2+0] - + image88[j*2+0][i*2+1] - + image88[j*2+1][i*2+0] - + image88[j*2+1][i*2+1]; - STBIR_ASSERT(output44[j][i] == ((n+2)>>2) || output44[j][i] == ((n+1)>>2)); // can't guarantee exact rounding due to numerical precision - t += n; - } - STBIR_ASSERT(output11[0][0] == ((t+32)>>6) || output11[0][0] == ((t+31)>>6)); // can't guarantee exact rounding due to numerical precision -} - -void verify_filter_normalized(stbir_filter filter, int output_size, unsigned int value) -{ - int i, j; - unsigned int output[64]; - - stbir_resize(image88_int[0], 8, 8, 0, output, output_size, output_size, 0, STBIR_TYPE_UINT32, 1, STBIR_ALPHA_CHANNEL_NONE, 0, STBIR_EDGE_CLAMP, STBIR_EDGE_CLAMP, filter, filter, STBIR_COLORSPACE_LINEAR, NULL); - - for (j = 0; j < output_size; ++j) - for (i = 0; i < output_size; ++i) - STBIR_ASSERT(value == output[j*output_size + i]); -} - -float round2(float f) -{ - return (float) floor(f+0.5f); // round() isn't C standard pre-C99 -} - -void test_filters(void) -{ - int i,j; - - mtsrand(0); - - for (i=0; i < sizeof(image88); ++i) - image88[0][i] = mtrand() & 255; - verify_box(); - - for (i=0; i < sizeof(image88); ++i) - image88[0][i] = 0; - image88[4][4] = 255; - verify_box(); - - for (j=0; j < 8; ++j) - for (i=0; i < 8; ++i) - image88[j][i] = (j^i)&1 ? 255 : 0; - verify_box(); - - for (j=0; j < 8; ++j) - for (i=0; i < 8; ++i) - image88[j][i] = i&2 ? 255 : 0; - verify_box(); - - int value = 64; - - for (j = 0; j < 8; ++j) - for (i = 0; i < 8; ++i) - image88_int[j][i] = value; - - verify_filter_normalized(STBIR_FILTER_BOX, 8, value); - verify_filter_normalized(STBIR_FILTER_TRIANGLE, 8, value); - verify_filter_normalized(STBIR_FILTER_CUBICBSPLINE, 8, value); - verify_filter_normalized(STBIR_FILTER_CATMULLROM, 8, value); - verify_filter_normalized(STBIR_FILTER_MITCHELL, 8, value); - - verify_filter_normalized(STBIR_FILTER_BOX, 4, value); - verify_filter_normalized(STBIR_FILTER_TRIANGLE, 4, value); - verify_filter_normalized(STBIR_FILTER_CUBICBSPLINE, 4, value); - verify_filter_normalized(STBIR_FILTER_CATMULLROM, 4, value); - verify_filter_normalized(STBIR_FILTER_MITCHELL, 4, value); - - verify_filter_normalized(STBIR_FILTER_BOX, 2, value); - verify_filter_normalized(STBIR_FILTER_TRIANGLE, 2, value); - verify_filter_normalized(STBIR_FILTER_CUBICBSPLINE, 2, value); - verify_filter_normalized(STBIR_FILTER_CATMULLROM, 2, value); - verify_filter_normalized(STBIR_FILTER_MITCHELL, 2, value); - - verify_filter_normalized(STBIR_FILTER_BOX, 1, value); - verify_filter_normalized(STBIR_FILTER_TRIANGLE, 1, value); - verify_filter_normalized(STBIR_FILTER_CUBICBSPLINE, 1, value); - verify_filter_normalized(STBIR_FILTER_CATMULLROM, 1, value); - verify_filter_normalized(STBIR_FILTER_MITCHELL, 1, value); - - { - // This test is designed to produce coefficients that are very badly denormalized. - unsigned int v = 556; - - unsigned int input[100 * 100]; - unsigned int output[11 * 11]; - - for (j = 0; j < 100 * 100; ++j) - input[j] = v; - - stbir_resize(input, 100, 100, 0, output, 11, 11, 0, STBIR_TYPE_UINT32, 1, STBIR_ALPHA_CHANNEL_NONE, 0, STBIR_EDGE_CLAMP, STBIR_EDGE_CLAMP, STBIR_FILTER_TRIANGLE, STBIR_FILTER_TRIANGLE, STBIR_COLORSPACE_LINEAR, NULL); - - for (j = 0; j < 11 * 11; ++j) - STBIR_ASSERT(v == output[j]); - } - - { - // Now test the trapezoid filter for downsampling. - unsigned int input[3 * 1]; - unsigned int output[2 * 1]; - - input[0] = 0; - input[1] = 255; - input[2] = 127; - - stbir_resize(input, 3, 1, 0, output, 2, 1, 0, STBIR_TYPE_UINT32, 1, STBIR_ALPHA_CHANNEL_NONE, 0, STBIR_EDGE_CLAMP, STBIR_EDGE_CLAMP, STBIR_FILTER_BOX, STBIR_FILTER_BOX, STBIR_COLORSPACE_LINEAR, NULL); - - STBIR_ASSERT(output[0] == (unsigned int)round2((float)(input[0] * 2 + input[1]) / 3)); - STBIR_ASSERT(output[1] == (unsigned int)round2((float)(input[2] * 2 + input[1]) / 3)); - - stbir_resize(input, 1, 3, 0, output, 1, 2, 0, STBIR_TYPE_UINT32, 1, STBIR_ALPHA_CHANNEL_NONE, 0, STBIR_EDGE_CLAMP, STBIR_EDGE_CLAMP, STBIR_FILTER_BOX, STBIR_FILTER_BOX, STBIR_COLORSPACE_LINEAR, NULL); - - STBIR_ASSERT(output[0] == (unsigned int)round2((float)(input[0] * 2 + input[1]) / 3)); - STBIR_ASSERT(output[1] == (unsigned int)round2((float)(input[2] * 2 + input[1]) / 3)); - } - - { - // Now test the trapezoid filter for upsampling. - unsigned int input[2 * 1]; - unsigned int output[3 * 1]; - - input[0] = 0; - input[1] = 255; - - stbir_resize(input, 2, 1, 0, output, 3, 1, 0, STBIR_TYPE_UINT32, 1, STBIR_ALPHA_CHANNEL_NONE, 0, STBIR_EDGE_CLAMP, STBIR_EDGE_CLAMP, STBIR_FILTER_BOX, STBIR_FILTER_BOX, STBIR_COLORSPACE_LINEAR, NULL); - - STBIR_ASSERT(output[0] == input[0]); - STBIR_ASSERT(output[1] == (input[0] + input[1]) / 2); - STBIR_ASSERT(output[2] == input[1]); - - stbir_resize(input, 1, 2, 0, output, 1, 3, 0, STBIR_TYPE_UINT32, 1, STBIR_ALPHA_CHANNEL_NONE, 0, STBIR_EDGE_CLAMP, STBIR_EDGE_CLAMP, STBIR_FILTER_BOX, STBIR_FILTER_BOX, STBIR_COLORSPACE_LINEAR, NULL); - - STBIR_ASSERT(output[0] == input[0]); - STBIR_ASSERT(output[1] == (input[0] + input[1]) / 2); - STBIR_ASSERT(output[2] == input[1]); - } - - // checkerboard - { - unsigned char input[64][64]; - unsigned char output[16][16]; - int i,j; - for (j=0; j < 64; ++j) - for (i=0; i < 64; ++i) - input[j][i] = (i^j)&1 ? 255 : 0; - stbir_resize_uint8_generic(input[0], 64, 64, 0, output[0],16,16,0, 1,-1,0,STBIR_EDGE_WRAP,STBIR_FILTER_DEFAULT,STBIR_COLORSPACE_LINEAR,0); - for (j=0; j < 16; ++j) - for (i=0; i < 16; ++i) - STBIR_ASSERT(output[j][i] == 128); - stbir_resize_uint8_srgb_edgemode(input[0], 64, 64, 0, output[0],16,16,0, 1,-1,0,STBIR_EDGE_WRAP); - for (j=0; j < 16; ++j) - for (i=0; i < 16; ++i) - STBIR_ASSERT(output[j][i] == 188); - - - } - - { - // Test trapezoid box filter - unsigned char input[2 * 1]; - unsigned char output[127 * 1]; - - input[0] = 0; - input[1] = 255; - - stbir_resize(input, 2, 1, 0, output, 127, 1, 0, STBIR_TYPE_UINT8, 1, STBIR_ALPHA_CHANNEL_NONE, 0, STBIR_EDGE_CLAMP, STBIR_EDGE_CLAMP, STBIR_FILTER_BOX, STBIR_FILTER_BOX, STBIR_COLORSPACE_LINEAR, NULL); - STBIR_ASSERT(output[0] == 0); - STBIR_ASSERT(output[127 / 2 - 1] == 0); - STBIR_ASSERT(output[127 / 2] == 128); - STBIR_ASSERT(output[127 / 2 + 1] == 255); - STBIR_ASSERT(output[126] == 255); - stbi_write_png("test-output/trapezoid-upsample-horizontal.png", 127, 1, 1, output, 0); - - stbir_resize(input, 1, 2, 0, output, 1, 127, 0, STBIR_TYPE_UINT8, 1, STBIR_ALPHA_CHANNEL_NONE, 0, STBIR_EDGE_CLAMP, STBIR_EDGE_CLAMP, STBIR_FILTER_BOX, STBIR_FILTER_BOX, STBIR_COLORSPACE_LINEAR, NULL); - STBIR_ASSERT(output[0] == 0); - STBIR_ASSERT(output[127 / 2 - 1] == 0); - STBIR_ASSERT(output[127 / 2] == 128); - STBIR_ASSERT(output[127 / 2 + 1] == 255); - STBIR_ASSERT(output[126] == 255); - stbi_write_png("test-output/trapezoid-upsample-vertical.png", 1, 127, 1, output, 0); - } -} - -#define UMAX32 4294967295U - -static void write32(char *filename, stbir_uint32 *output, int w, int h) -{ - stbir_uint8 *data = (stbir_uint8*) malloc(w*h*3); - for (int i=0; i < w*h*3; ++i) - data[i] = output[i]>>24; - stbi_write_png(filename, w, h, 3, data, 0); - free(data); -} - -static void test_32(void) -{ - int w=100,h=120,x,y, out_w,out_h; - stbir_uint32 *input = (stbir_uint32*) malloc(4 * 3 * w * h); - stbir_uint32 *output = (stbir_uint32*) malloc(4 * 3 * 3*w * 3*h); - for (y=0; y < h; ++y) { - for (x=0; x < w; ++x) { - input[y*3*w + x*3 + 0] = x * ( UMAX32/w ); - input[y*3*w + x*3 + 1] = y * ( UMAX32/h ); - input[y*3*w + x*3 + 2] = UMAX32/2; - } - } - out_w = w*33/16; - out_h = h*33/16; - stbir_resize(input,w,h,0,output,out_w,out_h,0,STBIR_TYPE_UINT32,3,-1,0,STBIR_EDGE_CLAMP,STBIR_EDGE_CLAMP,STBIR_FILTER_DEFAULT,STBIR_FILTER_DEFAULT,STBIR_COLORSPACE_LINEAR,NULL); - write32("test-output/seantest_1.png", output,out_w,out_h); - - out_w = w*16/33; - out_h = h*16/33; - stbir_resize(input,w,h,0,output,out_w,out_h,0,STBIR_TYPE_UINT32,3,-1,0,STBIR_EDGE_CLAMP,STBIR_EDGE_CLAMP,STBIR_FILTER_DEFAULT,STBIR_FILTER_DEFAULT,STBIR_COLORSPACE_LINEAR,NULL); - write32("test-output/seantest_2.png", output,out_w,out_h); -} - - -void test_suite(int argc, char **argv) -{ - int i; - char *barbara; - - _mkdir("test-output"); - - if (argc > 1) - barbara = argv[1]; - else - barbara = "barbara.png"; - - // check what cases we need normalization for -#if 1 - { - float x, y; - for (x = -1; x < 1; x += 0.05f) { - float sums[5] = { 0 }; - float o; - for (o = -5; o <= 5; ++o) { - sums[0] += stbir__filter_mitchell(x + o, 1); - sums[1] += stbir__filter_catmullrom(x + o, 1); - sums[2] += stbir__filter_cubic(x + o, 1); - sums[3] += stbir__filter_triangle(x + o, 1); - sums[4] += stbir__filter_trapezoid(x + o, 0.5f); - } - for (i = 0; i < 5; ++i) - STBIR_ASSERT(sums[i] >= 1.0 - 0.001 && sums[i] <= 1.0 + 0.001); - } - -#if 1 - for (y = 0.11f; y < 1; y += 0.01f) { // Step - for (x = -1; x < 1; x += 0.05f) { // Phase - float sums[5] = { 0 }; - float o; - for (o = -5; o <= 5; o += y) { - sums[0] += y * stbir__filter_mitchell(x + o, 1); - sums[1] += y * stbir__filter_catmullrom(x + o, 1); - sums[2] += y * stbir__filter_cubic(x + o, 1); - sums[4] += y * stbir__filter_trapezoid(x + o, 0.5f); - sums[3] += y * stbir__filter_triangle(x + o, 1); - } - for (i = 0; i < 3; ++i) - STBIR_ASSERT(sums[i] >= 1.0 - 0.0170 && sums[i] <= 1.0 + 0.0170); - } - } -#endif - } -#endif - -#if 0 // linear_to_srgb_uchar table - for (i=0; i < 256; ++i) { - float f = stbir__srgb_to_linear((i-0.5f)/255.0f); - printf("%9d, ", (int) ((f) * (1<<28))); - if ((i & 7) == 7) - printf("\n"); - } -#endif - - // old tests that hacky fix worked on - test that - // every uint8 maps to itself - for (i = 0; i < 256; i++) { - float f = stbir__srgb_to_linear(float(i) / 255); - int n = stbir__linear_to_srgb_uchar(f); - STBIR_ASSERT(n == i); - } - - // new tests that hacky fix failed for - test that - // values adjacent to uint8 round to nearest uint8 - for (i = 0; i < 256; i++) { - for (float y = -0.42f; y <= 0.42f; y += 0.01f) { - float f = stbir__srgb_to_linear((i+y) / 255.0f); - int n = stbir__linear_to_srgb_uchar(f); - STBIR_ASSERT(n == i); - } - } - - test_filters(); - - test_subpixel_1(); - test_subpixel_2(); - test_subpixel_3(); - test_subpixel_4(); - - test_premul(); - - test_32(); - - // Some tests to make sure errors don't pop up with strange filter/dimension combinations. - stbir_resize(image88, 8, 8, 0, output88, 4, 16, 0, STBIR_TYPE_UINT8, 1, STBIR_ALPHA_CHANNEL_NONE, 0, STBIR_EDGE_CLAMP, STBIR_EDGE_CLAMP, STBIR_FILTER_BOX, STBIR_FILTER_CATMULLROM, STBIR_COLORSPACE_SRGB, &g_context); - stbir_resize(image88, 8, 8, 0, output88, 4, 16, 0, STBIR_TYPE_UINT8, 1, STBIR_ALPHA_CHANNEL_NONE, 0, STBIR_EDGE_CLAMP, STBIR_EDGE_CLAMP, STBIR_FILTER_CATMULLROM, STBIR_FILTER_BOX, STBIR_COLORSPACE_SRGB, &g_context); - stbir_resize(image88, 8, 8, 0, output88, 16, 4, 0, STBIR_TYPE_UINT8, 1, STBIR_ALPHA_CHANNEL_NONE, 0, STBIR_EDGE_CLAMP, STBIR_EDGE_CLAMP, STBIR_FILTER_BOX, STBIR_FILTER_CATMULLROM, STBIR_COLORSPACE_SRGB, &g_context); - stbir_resize(image88, 8, 8, 0, output88, 16, 4, 0, STBIR_TYPE_UINT8, 1, STBIR_ALPHA_CHANNEL_NONE, 0, STBIR_EDGE_CLAMP, STBIR_EDGE_CLAMP, STBIR_FILTER_CATMULLROM, STBIR_FILTER_BOX, STBIR_COLORSPACE_SRGB, &g_context); - - int barbara_width, barbara_height, barbara_channels; - stbi_image_free(stbi_load(barbara, &barbara_width, &barbara_height, &barbara_channels, 0)); - - int res = 10; - // Downscaling - {for (int i = 0; i <= res; i++) - { - float t = (float)i/res; - float scale = 0.5; - float out_scale = 2.0f/3; - float x_shift = (barbara_width*out_scale - barbara_width*scale) * t; - float y_shift = (barbara_height*out_scale - barbara_height*scale) * t; - - test_subpixel_command(barbara, scale, scale, out_scale, out_scale, x_shift, y_shift); - }} - - // Upscaling - {for (int i = 0; i <= res; i++) - { - float t = (float)i/res; - float scale = 2; - float out_scale = 3; - float x_shift = (barbara_width*out_scale - barbara_width*scale) * t; - float y_shift = (barbara_height*out_scale - barbara_height*scale) * t; - - test_subpixel_command(barbara, scale, scale, out_scale, out_scale, x_shift, y_shift); - }} - - // Downscaling - {for (int i = 0; i <= res; i++) - { - float t = (float)i/res / 2; - test_subpixel_region(barbara, 0.25f, 0.25f, t, t, t+0.5f, t+0.5f); - }} - - // No scaling - {for (int i = 0; i <= res; i++) - { - float t = (float)i/res / 2; - test_subpixel_region(barbara, 0.5f, 0.5f, t, t, t+0.5f, t+0.5f); - }} - - // Upscaling - {for (int i = 0; i <= res; i++) - { - float t = (float)i/res / 2; - test_subpixel_region(barbara, 1, 1, t, t, t+0.5f, t+0.5f); - }} - - {for (i = 0; i < 10; i++) - test_subpixel(barbara, 0.5f, 0.5f, (float)i / 10, 1); - } - - {for (i = 0; i < 10; i++) - test_subpixel(barbara, 0.5f, 0.5f, 1, (float)i / 10); - } - - {for (i = 0; i < 10; i++) - test_subpixel(barbara, 2, 2, (float)i / 10, 1); - } - - {for (i = 0; i < 10; i++) - test_subpixel(barbara, 2, 2, 1, (float)i / 10); - } - - // Channels test - test_channels(barbara, 0.5f, 0.5f, 1); - test_channels(barbara, 0.5f, 0.5f, 2); - test_channels(barbara, 0.5f, 0.5f, 3); - test_channels(barbara, 0.5f, 0.5f, 4); - - test_channels(barbara, 2, 2, 1); - test_channels(barbara, 2, 2, 2); - test_channels(barbara, 2, 2, 3); - test_channels(barbara, 2, 2, 4); - - // filter tests - resize_image(barbara, 2, 2, STBIR_FILTER_BOX , STBIR_EDGE_CLAMP, STBIR_COLORSPACE_SRGB, "test-output/barbara-upsample-nearest.png"); - resize_image(barbara, 2, 2, STBIR_FILTER_TRIANGLE , STBIR_EDGE_CLAMP, STBIR_COLORSPACE_SRGB, "test-output/barbara-upsample-bilinear.png"); - resize_image(barbara, 2, 2, STBIR_FILTER_CUBICBSPLINE, STBIR_EDGE_CLAMP, STBIR_COLORSPACE_SRGB, "test-output/barbara-upsample-bicubic.png"); - resize_image(barbara, 2, 2, STBIR_FILTER_CATMULLROM , STBIR_EDGE_CLAMP, STBIR_COLORSPACE_SRGB, "test-output/barbara-upsample-catmullrom.png"); - resize_image(barbara, 2, 2, STBIR_FILTER_MITCHELL , STBIR_EDGE_CLAMP, STBIR_COLORSPACE_SRGB, "test-output/barbara-upsample-mitchell.png"); - - resize_image(barbara, 0.5f, 0.5f, STBIR_FILTER_BOX , STBIR_EDGE_CLAMP, STBIR_COLORSPACE_SRGB, "test-output/barbara-downsample-nearest.png"); - resize_image(barbara, 0.5f, 0.5f, STBIR_FILTER_TRIANGLE , STBIR_EDGE_CLAMP, STBIR_COLORSPACE_SRGB, "test-output/barbara-downsample-bilinear.png"); - resize_image(barbara, 0.5f, 0.5f, STBIR_FILTER_CUBICBSPLINE, STBIR_EDGE_CLAMP, STBIR_COLORSPACE_SRGB, "test-output/barbara-downsample-bicubic.png"); - resize_image(barbara, 0.5f, 0.5f, STBIR_FILTER_CATMULLROM , STBIR_EDGE_CLAMP, STBIR_COLORSPACE_SRGB, "test-output/barbara-downsample-catmullrom.png"); - resize_image(barbara, 0.5f, 0.5f, STBIR_FILTER_MITCHELL , STBIR_EDGE_CLAMP, STBIR_COLORSPACE_SRGB, "test-output/barbara-downsample-mitchell.png"); - - {for (i = 10; i < 100; i++) - { - char outname[200]; - sprintf(outname, "test-output/barbara-width-%d.jpg", i); - resize_image(barbara, (float)i / 100, 1, STBIR_FILTER_CATMULLROM, STBIR_EDGE_CLAMP, STBIR_COLORSPACE_SRGB, outname); - }} - - {for (i = 110; i < 500; i += 10) - { - char outname[200]; - sprintf(outname, "test-output/barbara-width-%d.jpg", i); - resize_image(barbara, (float)i / 100, 1, STBIR_FILTER_CATMULLROM, STBIR_EDGE_CLAMP, STBIR_COLORSPACE_SRGB, outname); - }} - - {for (i = 10; i < 100; i++) - { - char outname[200]; - sprintf(outname, "test-output/barbara-height-%d.jpg", i); - resize_image(barbara, 1, (float)i / 100, STBIR_FILTER_CATMULLROM, STBIR_EDGE_CLAMP, STBIR_COLORSPACE_SRGB, outname); - }} - - {for (i = 110; i < 500; i += 10) - { - char outname[200]; - sprintf(outname, "test-output/barbara-height-%d.jpg", i); - resize_image(barbara, 1, (float)i / 100, STBIR_FILTER_CATMULLROM, STBIR_EDGE_CLAMP, STBIR_COLORSPACE_SRGB, outname); - }} - - {for (i = 50; i < 200; i += 10) - { - char outname[200]; - sprintf(outname, "test-output/barbara-width-height-%d.jpg", i); - resize_image(barbara, 100 / (float)i, (float)i / 100, STBIR_FILTER_CATMULLROM, STBIR_EDGE_CLAMP, STBIR_COLORSPACE_SRGB, outname); - }} - - test_format(barbara, 0.5, 2.0, STBIR_TYPE_UINT16, STBIR_COLORSPACE_SRGB); - test_format(barbara, 0.5, 2.0, STBIR_TYPE_UINT16, STBIR_COLORSPACE_LINEAR); - test_format(barbara, 2.0, 0.5, STBIR_TYPE_UINT16, STBIR_COLORSPACE_SRGB); - test_format(barbara, 2.0, 0.5, STBIR_TYPE_UINT16, STBIR_COLORSPACE_LINEAR); - - test_format(barbara, 0.5, 2.0, STBIR_TYPE_UINT32, STBIR_COLORSPACE_SRGB); - test_format(barbara, 0.5, 2.0, STBIR_TYPE_UINT32, STBIR_COLORSPACE_LINEAR); - test_format(barbara, 2.0, 0.5, STBIR_TYPE_UINT32, STBIR_COLORSPACE_SRGB); - test_format(barbara, 2.0, 0.5, STBIR_TYPE_UINT32, STBIR_COLORSPACE_LINEAR); - - test_float(barbara, 0.5, 2.0, STBIR_TYPE_FLOAT, STBIR_COLORSPACE_SRGB); - test_float(barbara, 0.5, 2.0, STBIR_TYPE_FLOAT, STBIR_COLORSPACE_LINEAR); - test_float(barbara, 2.0, 0.5, STBIR_TYPE_FLOAT, STBIR_COLORSPACE_SRGB); - test_float(barbara, 2.0, 0.5, STBIR_TYPE_FLOAT, STBIR_COLORSPACE_LINEAR); - - // Edge behavior tests - resize_image("hgradient.png", 2, 2, STBIR_FILTER_CATMULLROM, STBIR_EDGE_CLAMP, STBIR_COLORSPACE_LINEAR, "test-output/hgradient-clamp.png"); - resize_image("hgradient.png", 2, 2, STBIR_FILTER_CATMULLROM, STBIR_EDGE_WRAP, STBIR_COLORSPACE_LINEAR, "test-output/hgradient-wrap.png"); - - resize_image("vgradient.png", 2, 2, STBIR_FILTER_CATMULLROM, STBIR_EDGE_CLAMP, STBIR_COLORSPACE_LINEAR, "test-output/vgradient-clamp.png"); - resize_image("vgradient.png", 2, 2, STBIR_FILTER_CATMULLROM, STBIR_EDGE_WRAP, STBIR_COLORSPACE_LINEAR, "test-output/vgradient-wrap.png"); - - resize_image("1px-border.png", 2, 2, STBIR_FILTER_CATMULLROM, STBIR_EDGE_REFLECT, STBIR_COLORSPACE_LINEAR, "test-output/1px-border-reflect.png"); - resize_image("1px-border.png", 2, 2, STBIR_FILTER_CATMULLROM, STBIR_EDGE_CLAMP, STBIR_COLORSPACE_LINEAR, "test-output/1px-border-clamp.png"); - - // sRGB tests - resize_image("gamma_colors.jpg", .5f, .5f, STBIR_FILTER_CATMULLROM, STBIR_EDGE_REFLECT, STBIR_COLORSPACE_SRGB, "test-output/gamma_colors.jpg"); - resize_image("gamma_2.2.jpg", .5f, .5f, STBIR_FILTER_CATMULLROM, STBIR_EDGE_REFLECT, STBIR_COLORSPACE_SRGB, "test-output/gamma_2.2.jpg"); - resize_image("gamma_dalai_lama_gray.jpg", .5f, .5f, STBIR_FILTER_CATMULLROM, STBIR_EDGE_REFLECT, STBIR_COLORSPACE_SRGB, "test-output/gamma_dalai_lama_gray.jpg"); -} diff --git a/impeller/third_party/stb/stb/tests/resample_test_c.c b/impeller/third_party/stb/stb/tests/resample_test_c.c deleted file mode 100644 index 50520c60a9358..0000000000000 --- a/impeller/third_party/stb/stb/tests/resample_test_c.c +++ /dev/null @@ -1,5 +0,0 @@ -#define STB_IMAGE_RESIZE_IMPLEMENTATION -#define STB_IMAGE_RESIZE_STATIC -#include "stb_image_resize.h" - -// Just to make sure it will build properly with a c compiler diff --git a/impeller/third_party/stb/stb/tests/resize.dsp b/impeller/third_party/stb/stb/tests/resize.dsp deleted file mode 100644 index 0aa1bbaf9cae1..0000000000000 --- a/impeller/third_party/stb/stb/tests/resize.dsp +++ /dev/null @@ -1,94 +0,0 @@ -# Microsoft Developer Studio Project File - Name="resize" - Package Owner=<4> -# Microsoft Developer Studio Generated Build File, Format Version 6.00 -# ** DO NOT EDIT ** - -# TARGTYPE "Win32 (x86) Console Application" 0x0103 - -CFG=resize - Win32 Debug -!MESSAGE This is not a valid makefile. To build this project using NMAKE, -!MESSAGE use the Export Makefile command and run -!MESSAGE -!MESSAGE NMAKE /f "resize.mak". -!MESSAGE -!MESSAGE You can specify a configuration when running NMAKE -!MESSAGE by defining the macro CFG on the command line. For example: -!MESSAGE -!MESSAGE NMAKE /f "resize.mak" CFG="resize - Win32 Debug" -!MESSAGE -!MESSAGE Possible choices for configuration are: -!MESSAGE -!MESSAGE "resize - Win32 Release" (based on "Win32 (x86) Console Application") -!MESSAGE "resize - Win32 Debug" (based on "Win32 (x86) Console Application") -!MESSAGE - -# Begin Project -# PROP AllowPerConfigDependencies 0 -# PROP Scc_ProjName "" -# PROP Scc_LocalPath "" -CPP=cl.exe -RSC=rc.exe - -!IF "$(CFG)" == "resize - Win32 Release" - -# PROP BASE Use_MFC 0 -# PROP BASE Use_Debug_Libraries 0 -# PROP BASE Output_Dir "Release" -# PROP BASE Intermediate_Dir "Release" -# PROP BASE Target_Dir "" -# PROP Use_MFC 0 -# PROP Use_Debug_Libraries 0 -# PROP Output_Dir "Release" -# PROP Intermediate_Dir "Release" -# PROP Ignore_Export_Lib 0 -# PROP Target_Dir "" -# ADD BASE CPP /nologo /W3 /GX /O2 /D "WIN32" /D "NDEBUG" /D "_CONSOLE" /D "_MBCS" /YX /FD /c -# ADD CPP /nologo /G6 /W3 /GX /Z7 /O2 /I ".." /D "WIN32" /D "NDEBUG" /D "_CONSOLE" /D "_MBCS" /YX /FD /c -# ADD BASE RSC /l 0x409 /d "NDEBUG" -# ADD RSC /l 0x409 /d "NDEBUG" -BSC32=bscmake.exe -# ADD BASE BSC32 /nologo -# ADD BSC32 /nologo -LINK32=link.exe -# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:console /machine:I386 -# ADD LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:console /debug /machine:I386 - -!ELSEIF "$(CFG)" == "resize - Win32 Debug" - -# PROP BASE Use_MFC 0 -# PROP BASE Use_Debug_Libraries 1 -# PROP BASE Output_Dir "Debug" -# PROP BASE Intermediate_Dir "Debug" -# PROP BASE Target_Dir "" -# PROP Use_MFC 0 -# PROP Use_Debug_Libraries 1 -# PROP Output_Dir "Debug" -# PROP Intermediate_Dir "Debug" -# PROP Target_Dir "" -# ADD BASE CPP /nologo /W3 /Gm /GX /ZI /Od /D "WIN32" /D "_DEBUG" /D "_CONSOLE" /D "_MBCS" /YX /FD /GZ /c -# ADD CPP /nologo /W3 /WX /Gm /GX /ZI /Od /I ".." /D "WIN32" /D "_DEBUG" /D "_CONSOLE" /D "_MBCS" /FD /GZ /c -# SUBTRACT CPP /YX -# ADD BASE RSC /l 0x409 /d "_DEBUG" -# ADD RSC /l 0x409 /d "_DEBUG" -BSC32=bscmake.exe -# ADD BASE BSC32 /nologo -# ADD BSC32 /nologo -LINK32=link.exe -# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:console /debug /machine:I386 /pdbtype:sept -# ADD LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:console /debug /machine:I386 /pdbtype:sept - -!ENDIF - -# Begin Target - -# Name "resize - Win32 Release" -# Name "resize - Win32 Debug" -# Begin Source File - -SOURCE=.\resample_test.cpp -# End Source File -# Begin Source File - -SOURCE=..\stb_image_resize.h -# End Source File -# End Target -# End Project diff --git a/impeller/third_party/stb/stb/tests/stb.c b/impeller/third_party/stb/stb/tests/stb.c deleted file mode 100644 index e0731d6c30b2e..0000000000000 --- a/impeller/third_party/stb/stb/tests/stb.c +++ /dev/null @@ -1,3323 +0,0 @@ -/* - * Unit tests for "stb.h" - */ - -//#include -#include -#include -#include -#include -#include - -#ifdef _WIN32 -#include -#endif - -#define STB_STUA -//#define STB_FASTMALLOC -#ifdef _DEBUG -#define STB_MALLOC_WRAPPER_DEBUG -#endif -#define STB_NPTR -#define STB_DEFINE -#include "stb.h" - -//#include "stb_file.h" -//#include "stb_pixel32.h" - -//#define DEBUG_BLOCK -#ifdef DEBUG_BLOCK -#include -#endif - -#ifdef STB_FASTMALLOC -#error "can't use FASTMALLOC with threads" -#endif - -int count; -void c(int truth, char *error) -{ - if (!truth) { - fprintf(stderr, "Test failed: %s\n", error); - ++count; - } -} - - -#if 0 -void show(void) -{ - #ifdef _WIN32 - SYSTEM_INFO x; - GetSystemInfo(&x); - printf("%d\n", x.dwPageSize); - #endif -} -#endif - -void test_classes(void) -{ - unsigned char size_base[32], size_shift[32]; - int class_to_pages[256]; - int class_to_size[256], cl; - int lg, size, wasted_pages; - int kAlignShift = 3; - int kAlignment = 1 << kAlignShift; - int kMaxSize = 8 * 4096; - int kPageShift = 12; - int kPageSize = (1 << kPageShift); - int next_class = 1; - int alignshift = kAlignShift; - int last_lg = -1; - - for (lg = 0; lg < kAlignShift; lg++) { - size_base[lg] = 1; - size_shift[lg] = kAlignShift; - } - - for (size = kAlignment; size <= kMaxSize; size += (1 << alignshift)) { - int lg = stb_log2_floor(size); - if (lg > last_lg) { - // Increase alignment every so often. - // - // Since we double the alignment every time size doubles and - // size >= 128, this means that space wasted due to alignment is - // at most 16/128 i.e., 12.5%. Plus we cap the alignment at 256 - // bytes, so the space wasted as a percentage starts falling for - // sizes > 2K. - if ((lg >= 7) && (alignshift < 8)) { - alignshift++; - } - size_base[lg] = next_class - ((size-1) >> alignshift); - size_shift[lg] = alignshift; - } - - class_to_size[next_class] = size; - last_lg = lg; - - next_class++; - } - - // Initialize the number of pages we should allocate to split into - // small objects for a given class. - wasted_pages = 0; - for (cl = 1; cl < next_class; cl++) { - // Allocate enough pages so leftover is less than 1/8 of total. - // This bounds wasted space to at most 12.5%. - size_t psize = kPageSize; - const size_t s = class_to_size[cl]; - while ((psize % s) > (psize >> 3)) { - psize += kPageSize; - } - class_to_pages[cl] = psize >> kPageShift; - wasted_pages += psize; - } - - printf("TCMalloc can waste as much as %d memory on one-shot allocations\n", wasted_pages); - - - return; -} - - -void test_script(void) -{ - stua_run_script( - "var g = (2+3)*5 + 3*(2+1) + ((7)); \n" - "func sprint(x) _print(x) _print(' ') x end;\n" - "func foo(y) var q = func(x) sprint(x) end; q end;\n " - "var z=foo(5); z(77);\n" - "func counter(z) func(x) z=z+1 end end\n" - "var q=counter(0), p=counter(5);\n" - "sprint(q()) sprint(p()) sprint(q()) sprint(p()) sprint(q()) sprint(p())\n" - "var x=2222;\n" - "if 1 == 2 then 3333 else 4444 end; => x; sprint(x);\n" - "var x1 = sprint(1.5e3); \n" - "var x2 = sprint(.5); \n" - "var x3 = sprint(1.); \n" - "var x4 = sprint(1.e3); \n" - "var x5 = sprint(1e3); \n" - "var x6 = sprint(0.5e3); \n" - "var x7 = sprint(.5e3); \n" - " func sum(x,y) x+y end \n" - " func sumfunc(a) sum+{x=a} end \n" - " var q = sumfunc(3) \n" - " var p = sumfunc(20) \n" - " var d = sprint(q(5)) - sprint(q(8)) \n" - " var e = sprint(p(5)) - sprint(p(8)) \n" - " func test3(x) \n" - " sprint(x) \n" - " x = x+3 \n" - " sprint(x) \n" - " x+5 \n" - " end \n" - " var y = test3(4); \n" - " func fib(x) \n" - " if x < 3 then \n" - " 1 \n" - " else \n" - " fib(x-1) + fib(x-2); \n" - " end \n" - " end \n" - " \n" - " func fib2(x) \n" - " var a=1 \n" - " var b=1 \n" - " sprint(a) \n" - " sprint(b) \n" - " while x > 2 do \n" - " var c=a+b \n" - " a=b \n" - " b=c \n" - " sprint(b) \n" - " x=x-1 \n" - " end \n" - " b \n" - " end \n" - " \n" - " func assign(z) \n" - " var y = { 'this', 'is', 'a', 'lame', 'day', 'to', 'die'} \n" - " y[3] = z \n" - " var i = 0 \n" - " while y[i] != nil do \n" - " sprint(y[i]) \n" - " i = i+1 \n" - " end \n" - " end \n" - " \n" - " sprint(fib(12)); \n" - " assign(\"good\"); \n" - " fib2(20); \n" - " sprint('ok'); \n" - " sprint(-5); \n" - " // final comment with no newline" - ); -} - -#ifdef STB_THREADS -extern void __stdcall Sleep(unsigned long); - -void * thread_1(void *x) -{ - Sleep(80); - printf("thread 1\n"); fflush(stdout); - return (void *) 2; -} - -void * thread_2(void *y) -{ - stb_work(thread_1, NULL, y); - Sleep(50); - printf("thread 2\n"); fflush(stdout); - return (void *) 3; -} - -stb_semaphore stest; -stb_mutex mutex; -volatile int tc1, tc2; - -void *thread_3(void *p) -{ - stb_mutex_begin(mutex); - ++tc1; - stb_mutex_end(mutex); - stb_sem_waitfor(stest); - stb_mutex_begin(mutex); - ++tc2; - stb_mutex_end(mutex); - return NULL; -} - -void test_threads(void) -{ - volatile int a=0,b=0; - //stb_work_numthreads(2); - stb_work(thread_2, (void *) &a, (void *) &b); - while (a==0 || b==0) { - Sleep(10); - //printf("a=%d b=%d\n", a, b); - } - c(a==2 && b == 3, "stb_thread"); - stb_work_numthreads(4); - stest = stb_sem_new(8); - mutex = stb_mutex_new(); - stb_work(thread_3, NULL, NULL); - stb_work(thread_3, NULL, NULL); - stb_work(thread_3, NULL, NULL); - stb_work(thread_3, NULL, NULL); - stb_work(thread_3, NULL, NULL); - stb_work(thread_3, NULL, NULL); - stb_work(thread_3, NULL, NULL); - stb_work(thread_3, NULL, NULL); - while (tc1 < 4) - Sleep(10); - c(tc1 == 4, "stb_work 1"); - stb_sem_release(stest); - stb_sem_release(stest); - stb_sem_release(stest); - stb_sem_release(stest); - stb_sem_release(stest); - stb_sem_release(stest); - stb_sem_release(stest); - stb_sem_release(stest); - Sleep(40); - while (tc1 != 8 || tc2 != 8) - Sleep(10); - c(tc1 == 8 && tc2 == 8, "stb_work 2"); - stb_work_numthreads(2); - stb_work(thread_3, NULL, NULL); - stb_work(thread_3, NULL, NULL); - stb_work(thread_3, NULL, NULL); - stb_work(thread_3, NULL, NULL); - while (tc1 < 10) - Sleep(10); - c(tc1 == 10, "stb_work 1"); - stb_sem_release(stest); - stb_sem_release(stest); - stb_sem_release(stest); - stb_sem_release(stest); - - Sleep(100); - stb_sem_delete(stest); - stb_mutex_delete(mutex); -} -#else -void test_threads(void) -{ -} -#endif - -void *thread4(void *p) -{ - return NULL; -} - -#ifdef STB_THREADS -stb_threadqueue *tq; -stb_sync synch; -stb_mutex msum; - -volatile int thread_sum; - -void *consume1(void *p) -{ - volatile int *q = (volatile int *) p; - for(;;) { - int z; - stb_threadq_get_block(tq, &z); - stb_mutex_begin(msum); - thread_sum += z; - *q += z; - stb_mutex_end(msum); - stb_sync_reach(synch); - } -} - -void test_threads2(void) -{ - int array[256],i,n=0; - volatile int which[4]; - synch = stb_sync_new(); - stb_sync_set_target(synch,2); - stb_work_reach(thread4, NULL, NULL, synch); - stb_sync_reach_and_wait(synch); - printf("ok\n"); - - tq = stb_threadq_new(4, 1, TRUE,TRUE); - msum = stb_mutex_new(); - thread_sum = 0; - stb_sync_set_target(synch, 65); - for (i=0; i < 4; ++i) { - which[i] = 0; - stb_create_thread(consume1, (int *) &which[i]); - } - for (i=1; i <= 64; ++i) { - array[i] = i; - n += i; - stb_threadq_add_block(tq, &array[i]); - } - stb_sync_reach_and_wait(synch); - stb_barrier(); - c(thread_sum == n, "stb_threadq 1"); - c(which[0] + which[1] + which[2] + which[3] == n, "stb_threadq 2"); - printf("(Distribution: %d %d %d %d)\n", which[0], which[1], which[2], which[3]); - - stb_sync_delete(synch); - stb_threadq_delete(tq); - stb_mutex_delete(msum); -} -#else -void test_threads2(void) -{ -} -#endif - -char tc[] = "testing compression test quick test voila woohoo what the hell"; - -char storage1[1 << 23]; -int test_compression(char *buffer, int length) -{ - char *storage2; - int c_len = stb_compress(storage1, buffer, length); - int dc_len; - printf("Compressed %d to %d\n", length, c_len); - dc_len = stb_decompress_length(storage1); - storage2 = malloc(dc_len); - dc_len = stb_decompress(storage2, storage1, c_len); - if (dc_len != length) { free(storage2); return -1; } - if (memcmp(buffer, storage2, length) != 0) { free(storage2); return -1; } - free(storage2); - return c_len; -} - -#if 0 -int test_en_compression(char *buffer, int length) -{ - int c_len = stb_en_compress(storage1, buffer, length); - int dc_len; - printf("Encompressed %d to %d\n", length, c_len); - dc_len = stb_en_decompress(storage2, storage1, c_len); - if (dc_len != length) return -1; - if (memcmp(buffer, storage2, length) != 0) return -1; - return c_len; -} -#endif - -#define STR_x "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxx" -#define STR_y "yyyyyyyyyyyyyyyyyy" - -#define STR_xy STR_x STR_y -#define STR_xyyxy STR_xy STR_y STR_xy - -#define STR_1 "testing" -#define STR_2 STR_xyyxy STR_xy STR_xyyxy STR_xyyxy STR_xy STR_xyyxy -#define STR_3 "buh" - -char buffer[] = STR_1 "\r\n" STR_2 STR_2 STR_2 "\n" STR_3; -char str1[] = STR_1; -char str2[] = STR_2 STR_2 STR_2; -char str3[] = STR_3; - -int sum(short *s) -{ - int i,total=0; - for (i=0; i < stb_arr_len(s); ++i) - total += s[i]; - return total; -} - -stb_uint stb_adler32_old(stb_uint adler32, stb_uchar *buffer, stb_uint buflen) -{ - const stb_uint ADLER_MOD = 65521; - stb_uint s1 = adler32 & 0xffff; - stb_uint s2 = adler32 >> 16; - - while (buflen-- > 0) { // NOTE: much faster implementations are possible! - s1 += *buffer++; if (s1 > ADLER_MOD) s1 -= ADLER_MOD; - s2 += s1 ; if (s2 > ADLER_MOD) s2 -= ADLER_MOD; - } - return (s2 << 16) + s1; -} - -static int sample_test[3][5] = -{ - { 1,2,3,4,5 }, - { 6,7,8,9,10, }, - { 11,12,13,14,15 }, -}; - -typedef struct { unsigned short x,y,z; } struct1; -typedef struct { double a; int x,y,z; } struct2; - -char *args_raw[] = { "foo", "-dxrf", "bar", "-ts" }; -char *args[8]; - -void do_compressor(int,char**); -void test_sha1(void); - -int alloc_num, alloc_size; -void dumpfunc(void *ptr, int sz, char *file, int line) -{ - printf("%p (%6d) -- %3d:%s\n", ptr, sz, line, file); - alloc_size += sz; - alloc_num += 1; -} - -char *expects(stb_matcher *m, char *s, int result, int len, char *str) -{ - int res2,len2=0; - res2 = stb_lex(m, s, &len2); - c(result == res2 && len == len2, str); - return s + len; -} - -void test_lex(void) -{ - stb_matcher *m = stb_lex_matcher(); - // tok_en5 .3 20.1 20. .20 .1 - char *s = "tok_en5.3 20.1 20. .20.1"; - - stb_lex_item(m, "[a-zA-Z_][a-zA-Z0-9_]*", 1 ); - stb_lex_item(m, "[0-9]*\\.?[0-9]*" , 2 ); - stb_lex_item(m, "[\r\n\t ]+" , 3 ); - stb_lex_item(m, "." , -99 ); - s=expects(m,s,1,7, "stb_lex 1"); - s=expects(m,s,2,2, "stb_lex 2"); - s=expects(m,s,3,1, "stb_lex 3"); - s=expects(m,s,2,4, "stb_lex 4"); - s=expects(m,s,3,1, "stb_lex 5"); - s=expects(m,s,2,3, "stb_lex 6"); - s=expects(m,s,3,1, "stb_lex 7"); - s=expects(m,s,2,3, "stb_lex 8"); - s=expects(m,s,2,2, "stb_lex 9"); - s=expects(m,s,0,0, "stb_lex 10"); - stb_matcher_free(m); -} - -typedef struct Btest -{ - struct Btest stb_bst_fields(btest_); - int v; -} Btest; - -stb_bst(Btest, btest_, BT2,bt2,v, int, a - b) - -void bst_test(void) -{ - Btest *root = NULL, *t; - int items[500], sorted[500]; - int i,j,z; - for (z=0; z < 10; ++z) { - for (i=0; i < 500; ++i) - items[i] = stb_rand() & 0xfffffff; - - // check for collisions, and retrry if so - memcpy(sorted, items, sizeof(sorted)); - qsort(sorted, 500, sizeof(sorted[0]), stb_intcmp(0)); - for (i=1; i < 500; ++i) - if (sorted[i-1] == sorted[i]) - break; - if (i != 500) { --z; break; } - - for (i=0; i < 500; ++i) { - t = malloc(sizeof(*t)); - t->v = items[i]; - root = btest_insert(root, t); - #ifdef STB_DEBUG - btest__validate(root,1); - #endif - for (j=0; j <= i; ++j) - c(btest_find(root, items[j]) != NULL, "stb_bst 1"); - for ( ; j < 500; ++j) - c(btest_find(root, items[j]) == NULL, "stb_bst 2"); - } - - t = btest_first(root); - for (i=0; i < 500; ++i) - t = btest_next(root,t); - c(t == NULL, "stb_bst 5"); - t = btest_last(root); - for (i=0; i < 500; ++i) - t = btest_prev(root,t); - c(t == NULL, "stb_bst 6"); - - memcpy(sorted, items, sizeof(sorted)); - qsort(sorted, 500, sizeof(sorted[0]), stb_intcmp(0)); - t = btest_first(root); - for (i=0; i < 500; ++i) { - assert(t->v == sorted[i]); - t = btest_next(root, t); - } - assert(t == NULL); - - if (z==1) - stb_reverse(items, 500, sizeof(items[0])); - else if (z) - stb_shuffle(items, 500, sizeof(items[0]), stb_rand()); - - for (i=0; i < 500; ++i) { - t = btest_find(root, items[i]); - assert(t != NULL); - root = btest_remove(root, t); - c(btest_find(root, items[i]) == NULL, "stb_bst 5"); - #ifdef STB_DEBUG - btest__validate(root, 1); - #endif - for (j=0; j <= i; ++j) - c(btest_find(root, items[j]) == NULL, "stb_bst 3"); - for ( ; j < 500; ++j) - c(btest_find(root, items[j]) != NULL, "stb_bst 4"); - free(t); - } - } -} - -extern void stu_uninit(void); - -stb_define_sort(sort_int, int, *a < *b) - -stb_rand_define(prime_rand, 1) -void test_packed_floats(void); -void test_parser_generator(void); - -void rec_print(stb_dirtree2 *d, int depth) -{ - int i; - for (i=0; i < depth; ++i) printf(" "); - printf("%s (%d)\n", d->relpath, stb_arr_len(d->files)); - for (i=0; i < stb_arr_len(d->subdirs); ++i) - rec_print(d->subdirs[i], depth+1); - d->weight = (float) stb_arr_len(d->files); -} - -#ifdef MAIN_TEST -int main(int argc, char **argv) -{ - char *z; - stb__wchar buffer7[1024],buffer9[1024]; - char buffer8[4096]; - FILE *f; - char *p1 = "foo/bar\\baz/test.xyz"; - char *p2 = "foo/.bar"; - char *p3 = "foo.bar"; - char *p4 = "foo/bar"; - char *wildcards[] = { "*foo*", "*bar", "baz", "*1*2*3*", "*/CVS/repository", "*oof*" }; - char **s; - char buf[256], *p; - int n,len2,*q,i; - stb_matcher *mt=NULL; - - if (argc > 1) { - do_compressor(argc,argv); - return 0; - } - test_classes(); - //show(); - - //stb_malloc_check_counter(2,2); - //_CrtSetBreakAlloc(10398); - - stbprint("Checking {!if} the {$fancy} print function {#works}? - should\n"); - stbprint(" - align\n"); - stbprint("But {#3this}} {one}} - shouldn't\n"); - - #if 0 - { - int i; - char **s = stb_readdir_recursive("/sean", NULL); - stb_dirtree *d = stb_dirtree_from_files_relative("", s, stb_arr_len(s)); - stb_dirtree **e; - rec_print(d, 0); - e = stb_summarize_tree(d,12,4); - for (i=0; i < stb_arr_len(e); ++i) { - printf("%s\n", e[i]->fullpath); - } - stb_arr_free(e); - - stb_fatal("foo"); - } - #endif - - stb_("Started stb.c"); - test_threads2(); - test_threads(); - - for (i=0; i < 1023 && 5+77*i < 0xd800; ++i) - buffer7[i] = 5+77*i; - buffer7[i++] = 0xd801; - buffer7[i++] = 0xdc02; - buffer7[i++] = 0xdbff; - buffer7[i++] = 0xdfff; - buffer7[i] = 0; - p = stb_to_utf8(buffer8, buffer7, sizeof(buffer8)); - c(p != NULL, "stb_to_utf8"); - if (p != NULL) { - stb_from_utf8(buffer9, buffer8, sizeof(buffer9)/2); - c(!memcmp(buffer7, buffer9, i*2), "stb_from_utf8"); - } - - z = "foo.*[bd]ak?r"; - c( stb_regex(z, "muggle man food is barfy") == 1, "stb_regex 1"); - c( stb_regex("foo.*bar", "muggle man food is farfy") == 0, "stb_regex 2"); - c( stb_regex("[^a-zA-Z]foo[^a-zA-Z]", "dfoobar xfood") == 0, "stb_regex 3"); - c( stb_regex(z, "muman foob is bakrfy") == 1, "stb_regex 4"); - z = "foo.*[bd]bk?r"; - c( stb_regex(z, "muman foob is bakrfy") == 0, "stb_regex 5"); - c( stb_regex(z, "muman foob is bbkrfy") == 1, "stb_regex 6"); - - stb_regex(NULL,NULL); - - #if 0 - test_parser_generator(); - stb_wrapper_listall(dumpfunc); - if (alloc_num) - printf("Memory still in use: %d allocations of %d bytes.\n", alloc_num, alloc_size); - #endif - - test_script(); - p = stb_file("sieve.stua", NULL); - if (p) { - stua_run_script(p); - free(p); - } - stua_uninit(); - - //stb_wrapper_listall(dumpfunc); - printf("Memory still in use: %d allocations of %d bytes.\n", alloc_num, alloc_size); - - c(stb_alloc_count_alloc == stb_alloc_count_free, "stb_alloc 0"); - - bst_test(); - - c(stb_alloc_count_alloc == stb_alloc_count_free, "stb_alloc 0"); - -#if 0 - // stb_block - { - int inuse=0, freespace=0; - int *x = malloc(10000*sizeof(*x)); - stb_block *b = stb_block_new(1, 10000); - #define BLOCK_COUNT 1000 - int *p = malloc(sizeof(*p) * BLOCK_COUNT); - int *l = malloc(sizeof(*l) * BLOCK_COUNT); - int i, n, k = 0; - - memset(x, 0, 10000 * sizeof(*x)); - - n = 0; - while (n < BLOCK_COUNT && k < 1000) { - l[n] = 16 + (rand() & 31); - p[n] = stb_block_alloc(b, l[n], 0); - if (p[n] == 0) - break; - inuse += l[n]; - - freespace = 0; - for (i=0; i < b->len; ++i) - freespace += b->freelist[i].len; - assert(freespace + inuse == 9999); - - for (i=0; i < l[n]; ++i) - x[ p[n]+i ] = p[n]; - ++n; - - if (k > 20) { - int sz; - i = (stb_rand() % n); - sz = l[i]; - stb_block_free(b, p[i], sz); - inuse -= sz; - p[i] = p[n-1]; - l[i] = l[n-1]; - --n; - - freespace = 0; - for (i=0; i < b->len; ++i) - freespace += b->freelist[i].len; - assert(freespace + inuse == 9999); - } - - - ++k; - - // validate - if ((k % 50) == 0) { - int j; - for (j=0; j < n; ++j) { - for (i=0; i < l[j]; ++i) - assert(x[ p[j]+i ] == p[j]); - } - } - - if ((k % 200) == 0) { - stb_block_compact_freelist(b); - } - } - - for (i=0; i < n; ++i) - stb_block_free(b, p[i], l[i]); - - stb_block_destroy(b); - free(p); - free(l); - free(x); - } - - blockfile_test(); -#endif - - mt = stb_lex_matcher(); - for (i=0; i < 5; ++i) - stb_lex_item_wild(mt, wildcards[i], i+1); - - c(1==stb_lex(mt, "this is a foo in the middle",NULL), "stb_matcher_match 1"); - c(0==stb_lex(mt, "this is a bar in the middle",NULL), "stb_matcher_match 2"); - c(0==stb_lex(mt, "this is a baz in the middle",NULL), "stb_matcher_match 3"); - c(2==stb_lex(mt, "this is a bar",NULL), "stb_matcher_match 4"); - c(0==stb_lex(mt, "this is a baz",NULL), "stb_matcher_match 5"); - c(3==stb_lex(mt, "baz",NULL), "stb_matcher_match 6"); - c(4==stb_lex(mt, "1_2_3_4",NULL), "stb_matcher_match 7"); - c(0==stb_lex(mt, "1 3 3 3 3 2 ",NULL), "stb_matcher_match 8"); - c(4==stb_lex(mt, "1 3 3 3 2 3 ",NULL), "stb_matcher_match 9"); - c(5==stb_lex(mt, "C:/sean/prj/old/gdmag/mipmap/hqp/adol-c/CVS/Repository",NULL), "stb_matcher_match 10"); - stb_matcher_free(mt); - - { - #define SSIZE 500000 - static int arr[SSIZE],arr2[SSIZE]; - int i,good; - for (i=0; i < SSIZE; ++i) - arr2[i] = stb_rand(); - memcpy(arr,arr2,sizeof(arr)); - printf("stb_define_sort:\n"); - sort_int(arr, SSIZE); - good = 1; - for (i=0; i+1 < SSIZE; ++i) - if (arr[i] > arr[i+1]) - good = 0; - c(good, "stb_define_sort"); - printf("qsort:\n"); - qsort(arr2, SSIZE, sizeof(arr2[0]), stb_intcmp(0)); - printf("done\n"); - // check for bugs - memset(arr, 0, sizeof(arr[0]) * 1000); - sort_int(arr, 1000); - } - - - c(stb_alloc_count_alloc == stb_alloc_count_free, "stb_alloc -2"); - - c( stb_is_prime( 2), "stb_is_prime 1"); - c( stb_is_prime( 3), "stb_is_prime 2"); - c( stb_is_prime( 5), "stb_is_prime 3"); - c( stb_is_prime( 7), "stb_is_prime 4"); - c(!stb_is_prime( 9), "stb_is_prime 5"); - c( stb_is_prime(11), "stb_is_prime 6"); - c(!stb_is_prime(25), "stb_is_prime 7"); - c(!stb_is_prime(27), "stb_is_prime 8"); - c( stb_is_prime(29), "stb_is_prime 9"); - c( stb_is_prime(31), "stb_is_prime a"); - c(!stb_is_prime(33), "stb_is_prime b"); - c(!stb_is_prime(35), "stb_is_prime c"); - c(!stb_is_prime(36), "stb_is_prime d"); - - for (n=7; n < 64; n += 3) { - int i; - stb_perfect s; - unsigned int *p = malloc(n * sizeof(*p)); - for (i=0; i < n; ++i) - p[i] = i*i; - c(stb_perfect_create(&s, p, n), "stb_perfect_hash 1"); - stb_perfect_destroy(&s); - for (i=0; i < n; ++i) - p[i] = stb_rand(); - c(stb_perfect_create(&s, p, n), "stb_perfect_hash 2"); - stb_perfect_destroy(&s); - for (i=0; i < n; ++i) - p[i] = (0x80000000 >> stb_log2_ceil(n>>1)) * i; - c(stb_perfect_create(&s, p, n), "stb_perfect_hash 2"); - stb_perfect_destroy(&s); - for (i=0; i < n; ++i) - p[i] = (int) malloc(1024); - c(stb_perfect_create(&s, p, n), "stb_perfect_hash 3"); - stb_perfect_destroy(&s); - for (i=0; i < n; ++i) - free((void *) p[i]); - free(p); - } - printf("Maximum attempts required to find perfect hash: %d\n", - stb_perfect_hash_max_failures); - - p = "abcdefghijklmnopqrstuvwxyz"; - c(stb_ischar('c', p), "stb_ischar 1"); - c(stb_ischar('x', p), "stb_ischar 2"); - c(!stb_ischar('#', p), "stb_ischar 3"); - c(!stb_ischar('X', p), "stb_ischar 4"); - p = "0123456789"; - c(!stb_ischar('c', p), "stb_ischar 5"); - c(!stb_ischar('x', p), "stb_ischar 6"); - c(!stb_ischar('#', p), "stb_ischar 7"); - c(!stb_ischar('X', p), "stb_ischar 8"); - p = "#####"; - c(!stb_ischar('c', p), "stb_ischar a"); - c(!stb_ischar('x', p), "stb_ischar b"); - c(stb_ischar('#', p), "stb_ischar c"); - c(!stb_ischar('X', p), "stb_ischar d"); - p = "xXyY"; - c(!stb_ischar('c', p), "stb_ischar e"); - c(stb_ischar('x', p), "stb_ischar f"); - c(!stb_ischar('#', p), "stb_ischar g"); - c(stb_ischar('X', p), "stb_ischar h"); - - c(stb_alloc_count_alloc == stb_alloc_count_free, "stb_alloc 1"); - - q = stb_wordwrapalloc(15, "How now brown cow. Testinglishously. Okey dokey"); - // How now brown - // cow. Testinglis - // hously. Okey - // dokey - c(stb_arr_len(q) == 8, "stb_wordwrap 8"); - c(q[2] == 14 && q[3] == 15, "stb_wordwrap 9"); - c(q[4] == 29 && q[5] == 12, "stb_wordwrap 10"); - stb_arr_free(q); - - q = stb_wordwrapalloc(20, "How now brown cow. Testinglishously. Okey dokey"); - // How now brown cow. - // Testinglishously. - // Okey dokey - c(stb_arr_len(q) == 6, "stb_wordwrap 1"); - c(q[0] == 0 && q[1] == 18, "stb_wordwrap 2"); - c(q[2] == 19 && q[3] == 17, "stb_wordwrap 3"); - c(q[4] == 37 && q[5] == 10, "stb_wordwrap 4"); - stb_arr_free(q); - - q = stb_wordwrapalloc(12, "How now brown cow. Testinglishously. Okey dokey"); - // How now - // brown cow. - // Testinglisho - // usly. Okey - // dokey - c(stb_arr_len(q) == 10, "stb_wordwrap 5"); - c(q[4] == 19 && q[5] == 12, "stb_wordwrap 6"); - c(q[6] == 31 && q[3] == 10, "stb_wordwrap 7"); - stb_arr_free(q); - - //test_script(); - - //test_packed_floats(); - - c(stb_alloc_count_alloc == stb_alloc_count_free, "stb_alloc 0"); - if (stb_alloc_count_alloc != stb_alloc_count_free) { - printf("%d allocs, %d frees\n", stb_alloc_count_alloc, stb_alloc_count_free); - } - test_lex(); - - mt = stb_regex_matcher(".*foo.*bar.*"); - c(stb_matcher_match(mt, "foobarx") == 1, "stb_matcher_match 1"); - c(stb_matcher_match(mt, "foobar") == 1, "stb_matcher_match 2"); - c(stb_matcher_match(mt, "foo bar") == 1, "stb_matcher_match 3"); - c(stb_matcher_match(mt, "fo foo ba ba bar ba") == 1, "stb_matcher_match 4"); - c(stb_matcher_match(mt, "fo oo oo ba ba bar foo") == 0, "stb_matcher_match 5"); - stb_free(mt); - - mt = stb_regex_matcher(".*foo.?bar.*"); - c(stb_matcher_match(mt, "abfoobarx") == 1, "stb_matcher_match 6"); - c(stb_matcher_match(mt, "abfoobar") == 1, "stb_matcher_match 7"); - c(stb_matcher_match(mt, "abfoo bar") == 1, "stb_matcher_match 8"); - c(stb_matcher_match(mt, "abfoo bar") == 0, "stb_matcher_match 9"); - c(stb_matcher_match(mt, "abfo foo ba ba bar ba") == 0, "stb_matcher_match 10"); - c(stb_matcher_match(mt, "abfo oo oo ba ba bar foo") == 0, "stb_matcher_match 11"); - stb_free(mt); - - mt = stb_regex_matcher(".*m((foo|bar)*baz)m.*"); - c(stb_matcher_match(mt, "abfoobarx") == 0, "stb_matcher_match 12"); - c(stb_matcher_match(mt, "a mfoofoofoobazm d") == 1, "stb_matcher_match 13"); - c(stb_matcher_match(mt, "a mfoobarbazfoom d") == 0, "stb_matcher_match 14"); - c(stb_matcher_match(mt, "a mbarbarfoobarbazm d") == 1, "stb_matcher_match 15"); - c(stb_matcher_match(mt, "a mfoobarfoo bazm d") == 0, "stb_matcher_match 16"); - c(stb_matcher_match(mt, "a mm foobarfoobarfoobar ") == 0, "stb_matcher_match 17"); - stb_free(mt); - - mt = stb_regex_matcher("f*|z"); - c(stb_matcher_match(mt, "fz") == 0, "stb_matcher_match 0a"); - c(stb_matcher_match(mt, "ff") == 1, "stb_matcher_match 0b"); - c(stb_matcher_match(mt, "z") == 1, "stb_matcher_match 0c"); - stb_free(mt); - - mt = stb_regex_matcher("m(f|z*)n"); - c(stb_matcher_match(mt, "mfzn") == 0, "stb_matcher_match 0d"); - c(stb_matcher_match(mt, "mffn") == 0, "stb_matcher_match 0e"); - c(stb_matcher_match(mt, "mzn") == 1, "stb_matcher_match 0f"); - c(stb_matcher_match(mt, "mn") == 1, "stb_matcher_match 0g"); - c(stb_matcher_match(mt, "mzfn") == 0, "stb_matcher_match 0f"); - - c(stb_matcher_find(mt, "manmanmannnnnnnmmmmmmmmm ") == 0, "stb_matcher_find 1"); - c(stb_matcher_find(mt, "manmanmannnnnnnmmmmmmmmm ") == 0, "stb_matcher_find 2"); - c(stb_matcher_find(mt, "manmanmannnnnnnmmmmmmmmmffzzz ") == 0, "stb_matcher_find 3"); - c(stb_matcher_find(mt, "manmanmannnnnnnmmmmmmmmmnfzzz ") == 1, "stb_matcher_find 4"); - c(stb_matcher_find(mt, "mmmfn aanmannnnnnnmmmmmm fzzz ") == 1, "stb_matcher_find 5"); - c(stb_matcher_find(mt, "mmmzzn anmannnnnnnmmmmmm fzzz ") == 1, "stb_matcher_find 6"); - c(stb_matcher_find(mt, "mm anmannnnnnnmmmmmm fzmzznzz ") == 1, "stb_matcher_find 7"); - c(stb_matcher_find(mt, "mm anmannnnnnnmmmmmm fzmzzfnzz ") == 0, "stb_matcher_find 8"); - c(stb_matcher_find(mt, "manmfnmannnnnnnmmmmmmmmmffzzz ") == 1, "stb_matcher_find 9"); - stb_free(mt); - - mt = stb_regex_matcher(".*m((foo|bar)*|baz)m.*"); - c(stb_matcher_match(mt, "abfoobarx") == 0, "stb_matcher_match 18"); - c(stb_matcher_match(mt, "a mfoofoofoobazm d") == 0, "stb_matcher_match 19"); - c(stb_matcher_match(mt, "a mfoobarbazfoom d") == 0, "stb_matcher_match 20"); - c(stb_matcher_match(mt, "a mbazm d") == 1, "stb_matcher_match 21"); - c(stb_matcher_match(mt, "a mfoobarfoom d") == 1, "stb_matcher_match 22"); - c(stb_matcher_match(mt, "a mm foobarfoobarfoobar ") == 1, "stb_matcher_match 23"); - stb_free(mt); - - mt = stb_regex_matcher("[a-fA-F]..[^]a-zA-Z]"); - c(stb_matcher_match(mt, "Axx1") == 1, "stb_matcher_match 24"); - c(stb_matcher_match(mt, "Fxx1") == 1, "stb_matcher_match 25"); - c(stb_matcher_match(mt, "Bxx]") == 0, "stb_matcher_match 26"); - c(stb_matcher_match(mt, "Cxxz") == 0, "stb_matcher_match 27"); - c(stb_matcher_match(mt, "gxx[") == 0, "stb_matcher_match 28"); - c(stb_matcher_match(mt, "-xx0") == 0, "stb_matcher_match 29"); - stb_free(mt); - - c(stb_wildmatch("foo*bar", "foobarx") == 0, "stb_wildmatch 0a"); - c(stb_wildmatch("foo*bar", "foobar") == 1, "stb_wildmatch 1a"); - c(stb_wildmatch("foo*bar", "foo bar") == 1, "stb_wildmatch 2a"); - c(stb_wildmatch("foo*bar", "fo foo ba ba bar ba") == 0, "stb_wildmatch 3a"); - c(stb_wildmatch("foo*bar", "fo oo oo ba ba ar foo") == 0, "stb_wildmatch 4a"); - - c(stb_wildmatch("*foo*bar*", "foobar") == 1, "stb_wildmatch 1b"); - c(stb_wildmatch("*foo*bar*", "foo bar") == 1, "stb_wildmatch 2b"); - c(stb_wildmatch("*foo*bar*", "fo foo ba ba bar ba") == 1, "stb_wildmatch 3b"); - c(stb_wildmatch("*foo*bar*", "fo oo oo ba ba ar foo") == 0, "stb_wildmatch 4b"); - - c(stb_wildmatch("foo*bar*", "foobarx") == 1, "stb_wildmatch 1c"); - c(stb_wildmatch("foo*bar*", "foobabar") == 1, "stb_wildmatch 2c"); - c(stb_wildmatch("foo*bar*", "fo foo ba ba bar ba") == 0, "stb_wildmatch 3c"); - c(stb_wildmatch("foo*bar*", "fo oo oo ba ba ar foo") == 0, "stb_wildmatch 4c"); - - c(stb_wildmatch("*foo*bar", "foobar") == 1, "stb_wildmatch 1d"); - c(stb_wildmatch("*foo*bar", "foo bar") == 1, "stb_wildmatch 2d"); - c(stb_wildmatch("*foo*bar", "fo foo ba ba bar ba") == 0, "stb_wildmatch 3d"); - c(stb_wildmatch("*foo*bar", "fo oo oo ba ba ar foo") == 0, "stb_wildmatch 4d"); - - c(stb_wildfind("foo*bar", "xyfoobarx") == 2, "stb_wildfind 0a"); - c(stb_wildfind("foo*bar", "aaafoobar") == 3, "stb_wildfind 1a"); - c(stb_wildfind("foo*bar", "foo bar") == 0, "stb_wildfind 2a"); - c(stb_wildfind("foo*bar", "fo foo ba ba bar ba") == 3, "stb_wildfind 3a"); - c(stb_wildfind("foo*bar", "fo oo oo ba ba ar foo") == -1, "stb_wildfind 4a"); - - c(stb_wildmatch("*foo*;*bar*", "foobar") == 1, "stb_wildmatch 1e"); - c(stb_wildmatch("*foo*;*bar*", "afooa") == 1, "stb_wildmatch 2e"); - c(stb_wildmatch("*foo*;*bar*", "abara") == 1, "stb_wildmatch 3e"); - c(stb_wildmatch("*foo*;*bar*", "abaza") == 0, "stb_wildmatch 4e"); - c(stb_wildmatch("*foo*;*bar*", "foboar") == 0, "stb_wildmatch 5e"); - - test_sha1(); - - n = sizeof(args_raw)/sizeof(args_raw[0]); - memcpy(args, args_raw, sizeof(args_raw)); - s = stb_getopt(&n, args); - c(n >= 1 && !strcmp(args[1], "bar" ), "stb_getopt 1"); - c(stb_arr_len(s) >= 2 && !strcmp(s[2] , "r" ), "stb_getopt 2"); - stb_getopt_free(s); - - n = sizeof(args_raw)/sizeof(args_raw[0]); - memcpy(args, args_raw, sizeof(args_raw)); - s = stb_getopt_param(&n, args, "f"); - c(stb_arr_len(s) >= 3 && !strcmp(s[3] , "fbar"), "stb_getopt 3"); - stb_getopt_free(s); - - n = sizeof(args_raw)/sizeof(args_raw[0]); - memcpy(args, args_raw, sizeof(args_raw)); - s = stb_getopt_param(&n, args, "x"); - c(stb_arr_len(s) >= 2 && !strcmp(s[1] , "xrf" ), "stb_getopt 4"); - stb_getopt_free(s); - - n = sizeof(args_raw)/sizeof(args_raw[0]); - memcpy(args, args_raw, sizeof(args_raw)); - s = stb_getopt_param(&n, args, "s"); - c(s == NULL && n == 0 , "stb_getopt 5"); - stb_getopt_free(s); - -#if 0 - c(*stb_csample_int(sample_test[0], 1, 5, 5, 3, -1, -1) == 1, "stb_csample_int 1"); - c(*stb_csample_int(sample_test[0], 1, 5, 5, 3, 1, -3) == 2, "stb_csample_int 2"); - c(*stb_csample_int(sample_test[0], 1, 5, 5, 3, 12, -2) == 5, "stb_csample_int 3"); - c(*stb_csample_int(sample_test[0], 1, 5, 5, 3, 15, 1) == 10, "stb_csample_int 4"); - c(*stb_csample_int(sample_test[0], 1, 5, 5, 3, 5, 4) == 15, "stb_csample_int 5"); - c(*stb_csample_int(sample_test[0], 1, 5, 5, 3, 3, 3) == 14, "stb_csample_int 6"); - c(*stb_csample_int(sample_test[0], 1, 5, 5, 3, -2, 5) == 11, "stb_csample_int 7"); - c(*stb_csample_int(sample_test[0], 1, 5, 5, 3, -7, 0) == 1, "stb_csample_int 8"); - c(*stb_csample_int(sample_test[0], 1, 5, 5, 3, 2, 1) == 8, "stb_csample_int 9"); -#endif - - c(!strcmp(stb_splitpath(buf, p1, STB_PATH ), "foo/bar\\baz/"), "stb_splitpath 1"); - c(!strcmp(stb_splitpath(buf, p1, STB_FILE ), "test"), "stb_splitpath 2"); - c(!strcmp(stb_splitpath(buf, p1, STB_EXT ), ".xyz"), "stb_splitpath 3"); - c(!strcmp(stb_splitpath(buf, p1, STB_PATH_FILE ), "foo/bar\\baz/test"), "stb_splitpath 4"); - c(!strcmp(stb_splitpath(buf, p1, STB_FILE_EXT ), "test.xyz"), "stb_splitpath 5"); - - c(!strcmp(stb_splitpath(buf, p2, STB_PATH ), "foo/"), "stb_splitpath 6"); - c(!strcmp(stb_splitpath(buf, p2, STB_FILE ), ""), "stb_splitpath 7"); - c(!strcmp(stb_splitpath(buf, p2, STB_EXT ), ".bar"), "stb_splitpath 8"); - c(!strcmp(stb_splitpath(buf, p2, STB_PATH_FILE ), "foo/"), "stb_splitpath 9"); - c(!strcmp(stb_splitpath(buf, p2, STB_FILE_EXT ), ".bar"), "stb_splitpath 10"); - - c(!strcmp(stb_splitpath(buf, p3, STB_PATH ), "./"), "stb_splitpath 11"); - c(!strcmp(stb_splitpath(buf, p3, STB_FILE ), "foo"), "stb_splitpath 12"); - c(!strcmp(stb_splitpath(buf, p3, STB_EXT ), ".bar"), "stb_splitpath 13"); - c(!strcmp(stb_splitpath(buf, p3, STB_PATH_FILE ), "foo"), "stb_splitpath 14"); - - c(!strcmp(stb_splitpath(buf, p4, STB_PATH ), "foo/"), "stb_splitpath 16"); - c(!strcmp(stb_splitpath(buf, p4, STB_FILE ), "bar"), "stb_splitpath 17"); - c(!strcmp(stb_splitpath(buf, p4, STB_EXT ), ""), "stb_splitpath 18"); - c(!strcmp(stb_splitpath(buf, p4, STB_PATH_FILE ), "foo/bar"), "stb_splitpath 19"); - c(!strcmp(stb_splitpath(buf, p4, STB_FILE_EXT ), "bar"), "stb_splitpath 20"); - - c(!strcmp(p=stb_dupreplace("testfootffooo foo fox", "foo", "brap"), "testbraptfbrapo brap fox"), "stb_dupreplace 1"); free(p); - c(!strcmp(p=stb_dupreplace("testfootffooo foo fox", "foo", "" ), "testtfo fox" ), "stb_dupreplace 2"); free(p); - c(!strcmp(p=stb_dupreplace("abacab", "a", "aba"), "abababacabab" ), "stb_dupreplace 3"); free(p); - - -#if 0 - m = stb_mml_parse("xy<&f>"); - c(m != NULL, "stb_mml_parse 1"); - if (m) { - c(!strcmp(m->child[0]->child[0]->child[1]->tag, "d"), "stb_mml_parse 2"); - c(!strcmp(m->child[0]->child[1]->leaf_data, "<&f>"), "stb_mml_parse 3"); - } - if (m) - stb_mml_free(m); - c(stb_alloc_count_alloc == stb_alloc_count_free, "stb_alloc 1"); - if (stb_alloc_count_alloc != stb_alloc_count_free) { - printf("%d allocs, %d frees\n", stb_alloc_count_alloc, stb_alloc_count_free); - } -#endif - - c(stb_linear_remap(3.0f,0,8,1,2) == 1.375, "stb_linear_remap()"); - - c(stb_bitreverse(0x1248fec8) == 0x137f1248, "stb_bitreverse() 1"); - c(stb_bitreverse8(0x4e) == 0x72, "stb_bitreverse8() 1"); - c(stb_bitreverse8(0x31) == 0x8c, "stb_bitreverse8() 2"); - for (n=1; n < 255; ++n) { - unsigned int m = stb_bitreverse8((uint8) n); - c(stb_bitreverse8((uint8) m) == (unsigned int) n, "stb_bitreverse8() 3"); - } - - for (n=2; n <= 31; ++n) { - c(stb_is_pow2 ((1 << n) ) == 1 , "stb_is_pow2() 1"); - c(stb_is_pow2 ((1 << n)+1) == 0 , "stb_is_pow2() 2"); - c(stb_is_pow2 ((1 << n)-1) == 0 , "stb_is_pow2() 3"); - - c(stb_log2_floor((1 << n) ) == n , "stb_log2_floor() 1"); - c(stb_log2_floor((1 << n)+1) == n , "stb_log2_floor() 2"); - c(stb_log2_floor((1 << n)-1) == n-1, "stb_log2_floor() 3"); - - c(stb_log2_ceil ((1 << n) ) == n , "stb_log2_ceil() 1"); - c(stb_log2_ceil ((1 << n)+1) == n+1, "stb_log2_ceil() 2"); - c(stb_log2_ceil ((1 << n)-1) == n , "stb_log2_ceil() 3"); - - c(stb_bitreverse(1 << n) == 1U << (31-n), "stb_bitreverse() 2"); - } - - c(stb_log2_floor(0) == -1, "stb_log2_floor() 4"); - c(stb_log2_ceil (0) == -1, "stb_log2_ceil () 4"); - - c(stb_log2_floor(-1) == 31, "stb_log2_floor() 5"); - c(stb_log2_ceil (-1) == 32, "stb_log2_ceil () 5"); - - c(stb_bitcount(0xffffffff) == 32, "stb_bitcount() 1"); - c(stb_bitcount(0xaaaaaaaa) == 16, "stb_bitcount() 2"); - c(stb_bitcount(0x55555555) == 16, "stb_bitcount() 3"); - c(stb_bitcount(0x00000000) == 0, "stb_bitcount() 4"); - - c(stb_lowbit8(0xf0) == 4, "stb_lowbit8 1"); - c(stb_lowbit8(0x10) == 4, "stb_lowbit8 2"); - c(stb_lowbit8(0xf3) == 0, "stb_lowbit8 3"); - c(stb_lowbit8(0xf8) == 3, "stb_lowbit8 4"); - c(stb_lowbit8(0x60) == 5, "stb_lowbit8 5"); - - for (n=0; n < sizeof(buf); ++n) - buf[n] = 0; - - for (n = 0; n < 200000; ++n) { - unsigned int k = stb_rand(); - int i,z=0; - for (i=0; i < 32; ++i) - if (k & (1 << i)) ++z; - c(stb_bitcount(k) == z, "stb_bitcount() 5"); - - buf[k >> 24] = 1; - - if (k != 0) { - if (stb_is_pow2(k)) { - c(stb_log2_floor(k) == stb_log2_ceil(k), "stb_is_pow2() 1"); - c(k == 1U << stb_log2_floor(k), "stb_is_pow2() 2"); - } else { - c(stb_log2_floor(k) == stb_log2_ceil(k)-1, "stb_is_pow2() 3"); - } - } - - c(stb_bitreverse(stb_bitreverse(n)) == (uint32) n, "stb_bitreverse() 3"); - } - - // make sure reasonable coverage from stb_rand() - for (n=0; n < sizeof(buf); ++n) - c(buf[n] != 0, "stb_rand()"); - - for (n=0; n < sizeof(buf); ++n) - buf[n] = 0; - - for (n=0; n < 60000; ++n) { - float z = (float) stb_frand(); - int n = (int) (z * sizeof(buf)); - c(z >= 0 && z < 1, "stb_frand() 1"); - c(n >= 0 && n < sizeof(buf), "stb_frand() 2"); - buf[n] = 1; - } - - // make sure reasonable coverage from stb_frand(), - // e.g. that the range remap isn't incorrect - for (n=0; n < sizeof(buf); ++n) - c(buf[n] != 0, "stb_frand()"); - - - // stb_arr - { - short *s = NULL; - - c(sum(s) == 0, "stb_arr 1"); - - stb_arr_add(s); s[0] = 3; - stb_arr_push(s,7); - - c( stb_arr_valid(s,1), "stb_arr 2"); - c(!stb_arr_valid(s,2), "stb_arr 3"); - - // force a realloc - stb_arr_push(s,0); - stb_arr_push(s,0); - stb_arr_push(s,0); - stb_arr_push(s,0); - - c(sum(s) == 10, "stb_arr 4"); - stb_arr_push(s,0); - s[0] = 1; s[1] = 5; s[2] = 20; - c(sum(s) == 26, "stb_arr 5"); - stb_arr_setlen(s,2); - c(sum(s) == 6, "stb_arr 6"); - stb_arr_setlen(s,1); - c(sum(s) == 1, "stb_arr 7"); - stb_arr_setlen(s,0); - c(sum(s) == 0, "stb_arr 8"); - - stb_arr_push(s,3); - stb_arr_push(s,4); - stb_arr_push(s,5); - stb_arr_push(s,6); - stb_arr_push(s,7); - stb_arr_deleten(s,1,3); - c(stb_arr_len(s)==2 && sum(s) == 10, "stb_arr_9"); - - stb_arr_push(s,2); - // 3 7 2 - stb_arr_insertn(s,2,2); - // 3 7 x x 2 - s[2] = 5; - s[3] = 6; - c(s[0]==3 && s[1] == 7 && s[2] == 5 && s[3] == 6 && s[4] == 2, "stb_arr 10"); - stb_arr_free(s); - } - - #if 1 - f= stb_fopen("data/stb.test", "wb"); - fwrite(buffer, 1, sizeof(buffer)-1, f); - stb_fclose(f, stb_keep_yes); - #ifndef WIN32 - sleep(1); // andLinux has some synchronization problem here - #endif - #else - f= fopen("data/stb.test", "wb"); - fwrite(buffer, 1, sizeof(buffer)-1, f); - fclose(f); - #endif - if (!stb_fexists("data/stb.test")) { - fprintf(stderr, "Error: couldn't open file just written, or stb_fexists() is broken.\n"); - } - - f = fopen("data/stb.test", "rb"); - // f = NULL; // test stb_fatal() - if (!f) { stb_fatal("Error: couldn't open file just written\n"); } - else { - char temp[4]; - int len1 = stb_filelen(f), len2; - int n1,n2; - if (fread(temp,1,4,f) == 0) { - int n = ferror(f); - if (n) { stb_fatal("Error reading from stream: %d", n); } - if (feof(f)) stb_fatal("Weird, read 0 bytes and hit eof"); - stb_fatal("Read 0, but neither feof nor ferror is true"); - } - fclose(f); - p = stb_file("data/stb.test", &len2); - if (p == NULL) stb_fatal("Error: stb_file() failed"); - c(len1 == sizeof(buffer)-1, "stb_filelen()"); - c(len2 == sizeof(buffer)-1, "stb_file():n"); - c(memcmp(p, buffer, sizeof(buffer)-1) == 0, "stb_file()"); - c(strcmp(p, buffer)==0, "stb_file() terminated"); - free(p); - - s = stb_stringfile("data/stb.test", &n1); - c(n1 == 3, "stb_stringfile():n"); - n2 = 0; - while (s[n2]) ++n2; - c(n1 == n2, "stb_stringfile():n length matches the non-NULL strings"); - if (n2 == 3) { - c(strcmp(s[0],str1)==0, "stb_stringfile()[0]"); - c(strcmp(s[1],str2)==0, "stb_stringfile()[1]"); - c(strcmp(s[2],str3)==0, "stb_stringfile()[2] (no terminating newlines)"); - } - free(s); - - f = fopen("data/stb.test", "rb"); - stb_fgets(buf, sizeof(buf), f); - //c(strcmp(buf, str1)==0, "stb_fgets()"); - p = stb_fgets_malloc(f); - n1 = strlen(p); - n2 = strlen(str2); - c(strcmp(p, str2)==0, "stb_fgets_malloc()"); - free(p); - stb_fgets(buf, sizeof(buf), f); - c(strcmp(buf, str3)==0, "stb_fgets()3"); - } - - c( stb_prefix("foobar", "foo"), "stb_prefix() 1"); - c(!stb_prefix("foo", "foobar"), "stb_prefix() 2"); - c( stb_prefix("foob", "foob" ), "stb_prefix() 3"); - - stb_strncpy(buf, "foobar", 6); c(strcmp(buf,"fooba" )==0, "stb_strncpy() 1"); - stb_strncpy(buf, "foobar", 8); c(strcmp(buf,"foobar")==0, "stb_strncpy() 2"); - - c(!strcmp(p=stb_duplower("FooBar"), "foobar"), "stb_duplower()"); free(p); - strcpy(buf, "FooBar"); - stb_tolower(buf); - c(!strcmp(buf, "foobar"), "stb_tolower()"); - - p = stb_strtok(buf, "foo=ba*r", "#=*"); - c(!strcmp(buf, "foo" ), "stb_strtok() 1"); - c(!strcmp(p , "ba*r"), "stb_strtok() 2"); - p = stb_strtok(buf, "foobar", "#=*"); - c(*p == 0, "stb_strtok() 3"); - - c(!strcmp(stb_skipwhite(" \t\n foo"), "foo"), "stb_skipwhite()"); - - s = stb_tokens("foo == ba*r", "#=*", NULL); - c(!strcmp(s[0], "foo "), "stb_tokens() 1"); - c(!strcmp(s[1], " ba"), "stb_tokens() 2"); - c(!strcmp(s[2], "r"), "stb_tokens() 3"); - c(s[3] == 0, "stb_tokens() 4"); - free(s); - - s = stb_tokens_allowempty("foo == ba*r", "#=*", NULL); - c(!strcmp(s[0], "foo "), "stb_tokens_allowempty() 1"); - c(!strcmp(s[1], "" ), "stb_tokens_allowempty() 2"); - c(!strcmp(s[2], " ba"), "stb_tokens_allowempty() 3"); - c(!strcmp(s[3], "r"), "stb_tokens_allowempty() 4"); - c(s[4] == 0, "stb_tokens_allowempty() 5"); - free(s); - - s = stb_tokens_stripwhite("foo == ba*r", "#=*", NULL); - c(!strcmp(s[0], "foo"), "stb_tokens_stripwhite() 1"); - c(!strcmp(s[1], "" ), "stb_tokens_stripwhite() 2"); - c(!strcmp(s[2], "ba"), "stb_tokens_stripwhite() 3"); - c(!strcmp(s[3], "r"), "stb_tokens_stripwhite() 4"); - c(s[4] == 0, "stb_tokens_stripwhite() 5"); - free(s); - - s = stb_tokens_quoted("foo =\"=\" ba*\"\"r \" foo\" bah ", "#=*", NULL); - c(!strcmp(s[0], "foo"), "stb_tokens_quoted() 1"); - c(!strcmp(s[1], "= ba"), "stb_tokens_quoted() 2"); - c(!strcmp(s[2], "\"r foo bah"), "stb_tokens_quoted() 3"); - c(s[3] == 0, "stb_tokens_quoted() 4"); - free(s); - - - p = stb_file("stb.h", &len2); - if (p) { - uint32 z = stb_adler32_old(1, p, len2); - uint32 x = stb_adler32 (1, p, len2); - c(z == x, "stb_adler32() 1"); - memset(p,0xff,len2); - z = stb_adler32_old((65520<<16) + 65520, p, len2); - x = stb_adler32 ((65520<<16) + 65520, p, len2); - c(z == x, "stb_adler32() 2"); - free(p); - } - - // stb_hheap - { - #define HHEAP_COUNT 100000 - void **p = malloc(sizeof(*p) * HHEAP_COUNT); - int i, j; - #if 0 - stb_hheap *h2, *h = stb_newhheap(sizeof(struct1),0); - - for (i=0; i < HHEAP_COUNT; ++i) - p[i] = stb_halloc(h); - stb_shuffle(p, HHEAP_COUNT, sizeof(*p), stb_rand()); - for (i=0; i < HHEAP_COUNT; ++i) - stb_hfree(p[i]); - - c(h->num_alloc == 0, "stb_hheap 1"); - stb_delhheap(h); - - h = stb_newhheap(sizeof(struct1),0); - h2 = stb_newhheap(sizeof(struct2),8); - - for (i=0; i < HHEAP_COUNT; ++i) { - if (i & 1) - p[i] = stb_halloc(h); - else { - p[i] = stb_halloc(h2); - c((((int) p[i]) & 4) == 0, "stb_hheap 2"); - } - } - - stb_shuffle(p, HHEAP_COUNT, sizeof(*p), stb_rand()); - for (i=0; i < HHEAP_COUNT; ++i) - stb_hfree(p[i]); - - c(h->num_alloc == 0, "stb_hheap 3"); - c(h2->num_alloc == 0, "stb_hheap 4"); - - stb_delhheap(h); - stb_delhheap(h2); - #else - for (i=0; i < HHEAP_COUNT; ++i) - p[i] = malloc(32); - stb_shuffle(p, HHEAP_COUNT, sizeof(*p), stb_rand()); - for (i=0; i < HHEAP_COUNT; ++i) - free(p[i]); - #endif - - // now use the same array of pointers to do pointer set operations - for (j=100; j < HHEAP_COUNT; j += 25000) { - stb_ps *ps = NULL; - for (i=0; i < j; ++i) - ps = stb_ps_add(ps, p[i]); - - for (i=0; i < HHEAP_COUNT; ++i) - c(stb_ps_find(ps, p[i]) == (i < j), "stb_ps 1"); - c(stb_ps_count(ps) == j, "stb_ps 1b"); - - for (i=j; i < HHEAP_COUNT; ++i) - ps = stb_ps_add(ps, p[i]); - - for (i=0; i < j; ++i) - ps = stb_ps_remove(ps, p[i]); - - for (i=0; i < HHEAP_COUNT; ++i) - c(stb_ps_find(ps, p[i]) == !(i < j), "stb_ps 2"); - - stb_ps_delete(ps); - } - - #define HHEAP_COUNT2 100 - // now use the same array of pointers to do pointer set operations - for (j=1; j < 40; ++j) { - stb_ps *ps = NULL; - for (i=0; i < j; ++i) - ps = stb_ps_add(ps, p[i]); - - for (i=0; i < HHEAP_COUNT2; ++i) - c(stb_ps_find(ps, p[i]) == (i < j), "stb_ps 3"); - c(stb_ps_count(ps) == j, "stb_ps 3b"); - - for (i=j; i < HHEAP_COUNT2; ++i) - ps = stb_ps_add(ps, p[i]); - - for (i=0; i < j; ++i) - ps = stb_ps_remove(ps, p[i]); - - for (i=0; i < HHEAP_COUNT2; ++i) - c(stb_ps_find(ps, p[i]) == !(i < j), "stb_ps 4"); - - stb_ps_delete(ps); - } - - free(p); - } - - - n = test_compression(tc, sizeof(tc)); - c(n >= 0, "stb_compress()/stb_decompress() 1"); - - p = stb_file("stb.h", &len2); - if (p) { - FILE *f = fopen("data/stb_h.z", "wb"); - if (stb_compress_stream_start(f)) { - int i; - void *q; - int len3; - - for (i=0; i < len2; ) { - int n = stb_rand() % 10; - if (n <= 6) n = 1 + stb_rand()%16; - else if (n <= 8) n = 20 + stb_rand() % 1000; - else n = 15000; - if (i + n > len2) n = len2 - i; - stb_write(p + i, n); - i += n; - } - stb_compress_stream_end(1); - - q = stb_decompress_fromfile("data/stb_h.z", &len3); - c(len3 == len2, "stb_compress_stream 2"); - if (len2 == len3) - c(!memcmp(p,q,len2), "stb_compress_stream 3"); - if (q) free(q); - } else { - c(0, "stb_compress_stream 1"); - } - - free(p); - stb_compress_window(65536*4); - } - - p = stb_file("stb.h", &len2); - if (p) { - n = test_compression(p, len2); - c(n >= 0, "stb_compress()/stb_decompress() 2"); - #if 0 - n = test_en_compression(p, len2); - c(n >= 0, "stb_en_compress()/stb_en_decompress() 2"); - #endif - free(p); - } else { - fprintf(stderr, "No stb.h to compression test.\n"); - } - - p = stb_file("data/test.bmp", &len2); - if (p) { - n = test_compression(p, len2); - c(n == 106141, "stb_compress()/stb_decompress() 4"); - #if 0 - n = test_en_compression(p, len2); - c(n >= 0, "stb_en_compress()/stb_en_decompress() 4"); - #endif - free(p); - } - - // the hardcoded compressed lengths being verified _could_ - // change if you changed the compresser parameters; but pure - // performance optimizations shouldn't change them - p = stb_file("data/cantrbry.zip", &len2); - if (p) { - n = test_compression(p, len2); - c(n == 642787, "stb_compress()/stb_decompress() 3"); - #if 0 - n = test_en_compression(p, len2); - c(n >= 0, "stb_en_compress()/stb_en_decompress() 3"); - #endif - free(p); - } - - p = stb_file("data/bible.txt", &len2); - if (p) { - n = test_compression(p, len2); - c(n == 2022520, "stb_compress()/stb_decompress() 4"); - #if 0 - n = test_en_compression(p, len2); - c(n >= 0, "stb_en_compress()/stb_en_decompress() 4"); - #endif - free(p); - } - - { - int len = 1 << 25, o=0; // 32MB - char *buffer = malloc(len); - int i; - for (i=0; i < 8192; ++i) - buffer[o++] = (char) stb_rand(); - for (i=0; i < (1 << 15); ++i) - buffer[o++] = 1; - for (i=0; i < 64; ++i) - buffer[o++] = buffer[i]; - for (i=0; i < (1 << 21); ++i) - buffer[o++] = 2; - for (i=0; i < 64; ++i) - buffer[o++] = buffer[i]; - for (i=0; i < (1 << 21); ++i) - buffer[o++] = 3; - for (i=0; i < 8192; ++i) - buffer[o++] = buffer[i]; - for (i=0; i < (1 << 21); ++i) - buffer[o++] = 4; - assert(o < len); - stb_compress_window(1 << 24); - i = test_compression(buffer, len); - c(n >= 0, "stb_compress() 6"); - free(buffer); - } - - #ifdef STB_THREADS - stb_thread_cleanup(); - #endif - stb_ischar(0,NULL); - stb_wrapper_listall(dumpfunc); - printf("Memory still in use: %d allocations of %d bytes.\n", alloc_num, alloc_size); - - // force some memory checking - for (n=1; n < 20; ++n) - malloc(1 << n); - - printf("Finished stb.c with %d errors.\n", count); - - #ifdef _MSC_VER - if (count) - __asm int 3; - #endif - - return 0; -} - -#endif - - - - - -// NIST test vectors - -struct -{ - int length; - char *message; - char *digest; -} sha1_tests[] = -{ - 24, -"616263", -"a9993e364706816aba3e25717850c26c9cd0d89d", - - 1304, -"ec29561244ede706b6eb30a1c371d74450a105c3f9735f7fa9fe38cf67f304a5736a106e" -"92e17139a6813b1c81a4f3d3fb9546ab4296fa9f722826c066869edacd73b25480351858" -"13e22634a9da44000d95a281ff9f264ecce0a931222162d021cca28db5f3c2aa24945ab1" -"e31cb413ae29810fd794cad5dfaf29ec43cb38d198fe4ae1da2359780221405bd6712a53" -"05da4b1b737fce7cd21c0eb7728d08235a9011", -"970111c4e77bcc88cc20459c02b69b4aa8f58217", - - 2096, -"5fc2c3f6a7e79dc94be526e5166a238899d54927ce470018fbfd668fd9dd97cbf64e2c91" -"584d01da63be3cc9fdff8adfefc3ac728e1e335b9cdc87f069172e323d094b47fa1e652a" -"fe4d6aa147a9f46fda33cacb65f3aa12234746b9007a8c85fe982afed7815221e43dba55" -"3d8fe8a022cdac1b99eeeea359e5a9d2e72e382dffa6d19f359f4f27dc3434cd27daeeda" -"8e38594873398678065fbb23665aba9309d946135da0e4a4afdadff14db18e85e71dd93c" -"3bf9faf7f25c8194c4269b1ee3d9934097ab990025d9c3aaf63d5109f52335dd3959d38a" -"e485050e4bbb6235574fc0102be8f7a306d6e8de6ba6becf80f37415b57f9898a5824e77" -"414197422be3d36a6080", - "0423dc76a8791107d14e13f5265b343f24cc0f19", - - 2888, -"0f865f46a8f3aed2da18482aa09a8f390dc9da07d51d1bd10fe0bf5f3928d5927d08733d" -"32075535a6d1c8ac1b2dc6ba0f2f633dc1af68e3f0fa3d85e6c60cb7b56c239dc1519a00" -"7ea536a07b518ecca02a6c31b46b76f021620ef3fc6976804018380e5ab9c558ebfc5cb1" -"c9ed2d974722bf8ab6398f1f2b82fa5083f85c16a5767a3a07271d67743f00850ce8ec42" -"8c7f22f1cf01f99895c0c844845b06a06cecb0c6cf83eb55a1d4ebc44c2c13f6f7aa5e0e" -"08abfd84e7864279057abc471ee4a45dbbb5774afa24e51791a0eada11093b88681fe30b" -"aa3b2e94113dc63342c51ca5d1a6096d0897b626e42cb91761058008f746f35465465540" -"ad8c6b8b60f7e1461b3ce9e6529625984cb8c7d46f07f735be067588a0117f23e34ff578" -"00e2bbe9a1605fde6087fb15d22c5d3ac47566b8c448b0cee40373e5ba6eaa21abee7136" -"6afbb27dbbd300477d70c371e7b8963812f5ed4fb784fb2f3bd1d3afe883cdd47ef32bea" -"ea", - "6692a71d73e00f27df976bc56df4970650d90e45", - - 3680, -"4893f1c763625f2c6ce53aacf28026f14b3cd8687e1a1d3b60a81e80fcd1e2b038f9145a" -"b64a0718f948f7c3c9ac92e3d86fb669a5257da1a18c776291653688338210a3242120f1" -"01788e8acc9110db9258b1554bf3d26602516ea93606a25a7f566c0c758fb39ecd9d876b" -"c5d8abc1c3205095382c2474cb1f8bbdb45c2c0e659cb0fc703ec607a5de6bcc7a28687d" -"b1ee1c8f34797bb2441d5706d210df8c2d7d65dbded36414d063c117b52a51f7a4eb9cac" -"0782e008b47459ed5acac0bc1f20121087f992ad985511b33c866d18e63f585478ee5a5e" -"654b19d81231d98683ae3f0533565aba43dce408d7e3c4c6be11d8f05165f29c9dcb2030" -"c4ee31d3a04e7421aa92c3231a1fc07e50e95fea7389a5e65891afaba51cf55e36a9d089" -"bf293accb356d5d06547307d6e41456d4ed146a056179971c56521c83109bf922866186e" -"184a99a96c7bb96df8937e35970e438412a2b8d744cf2ad87cb605d4232e976f9f151697" -"76e4e5b6b786132c966b25fc56d815c56c819af5e159aa39f8a93d38115f5580cda93bc0" -"73c30b39920e726fe861b72483a3f886269ab7a8eefe952f35d25c4eb7f443f4f3f26e43" -"d51fb54591e6a6dad25fcdf5142033084e5624bdd51435e77dea86b8", - "dc5859dd5163c4354d5d577b855fa98e37f04384", - - 4472, -"cf494c18a4e17bf03910631471bca5ba7edea8b9a63381e3463517961749848eb03abefd" -"4ce676dece3740860255f57c261a558aa9c7f11432f549a9e4ce31d8e17c79450ce2ccfc" -"148ad904aedfb138219d7052088520495355dadd90f72e6f69f9c6176d3d45f113f275b7" -"fbc2a295784d41384cd7d629b23d1459a22e45fd5097ec9bf65fa965d3555ec77367903c" -"32141065fc24da5c56963d46a2da3c279e4035fb2fb1c0025d9dda5b9e3443d457d92401" -"a0d3f58b48469ecb1862dc975cdbe75ca099526db8b0329b03928206f084c633c04eef5e" -"8e377f118d30edf592504be9d2802651ec78aeb02aea167a03fc3e23e5fc907c324f283f" -"89ab37e84687a9c74ccf055402db95c29ba2c8d79b2bd4fa96459f8e3b78e07e923b8119" -"8267492196ecb71e01c331f8df245ec5bdf8d0e05c91e63bb299f0f6324895304dda721d" -"39410458f117c87b7dd6a0ee734b79fcbe482b2c9e9aa0cef03a39d4b0c86de3bc34b4aa" -"dabfa373fd2258f7c40c187744d237080762382f547a36adb117839ca72f8ebbc5a20a07" -"e86f4c8bb923f5787698d278f6db0040e76e54645bb0f97083995b34b9aa445fc4244550" -"58795828dd00c32471ec402a307f5aa1b37b1a86d6dae3bcbfbe9ba41cab0beeabf489af" -"0073d4b3837d3f14b815120bc3602d072b5aeefcdec655fe756b660eba7dcf34675acbce" -"317746270599424b9248791a0780449c1eabbb9459cc1e588bfd74df9b1b711c85c09d8a" -"a171b309281947e8f4b6ac438753158f4f36fa", - "4c17926feb6e87f5bca7890d8a5cde744f231dab", - - 5264, -"8236153781bd2f1b81ffe0def1beb46f5a70191142926651503f1b3bb1016acdb9e7f7ac" -"ced8dd168226f118ff664a01a8800116fd023587bfba52a2558393476f5fc69ce9c65001" -"f23e70476d2cc81c97ea19caeb194e224339bcb23f77a83feac5096f9b3090c51a6ee6d2" -"04b735aa71d7e996d380b80822e4dfd43683af9c7442498cacbea64842dfda238cb09992" -"7c6efae07fdf7b23a4e4456e0152b24853fe0d5de4179974b2b9d4a1cdbefcbc01d8d311" -"b5dda059136176ea698ab82acf20dd490be47130b1235cb48f8a6710473cfc923e222d94" -"b582f9ae36d4ca2a32d141b8e8cc36638845fbc499bce17698c3fecae2572dbbd4705524" -"30d7ef30c238c2124478f1f780483839b4fb73d63a9460206824a5b6b65315b21e3c2f24" -"c97ee7c0e78faad3df549c7ca8ef241876d9aafe9a309f6da352bec2caaa92ee8dca3928" -"99ba67dfed90aef33d41fc2494b765cb3e2422c8e595dabbfaca217757453fb322a13203" -"f425f6073a9903e2dc5818ee1da737afc345f0057744e3a56e1681c949eb12273a3bfc20" -"699e423b96e44bd1ff62e50a848a890809bfe1611c6787d3d741103308f849a790f9c015" -"098286dbacfc34c1718b2c2b77e32194a75dda37954a320fa68764027852855a7e5b5274" -"eb1e2cbcd27161d98b59ad245822015f48af82a45c0ed59be94f9af03d9736048570d6e3" -"ef63b1770bc98dfb77de84b1bb1708d872b625d9ab9b06c18e5dbbf34399391f0f8aa26e" -"c0dac7ff4cb8ec97b52bcb942fa6db2385dcd1b3b9d567aaeb425d567b0ebe267235651a" -"1ed9bf78fd93d3c1dd077fe340bb04b00529c58f45124b717c168d07e9826e33376988bc" -"5cf62845c2009980a4dfa69fbc7e5a0b1bb20a5958ca967aec68eb31dd8fccca9afcd30a" -"26bab26279f1bf6724ff", - "11863b483809ef88413ca9b0084ac4a5390640af", - - 6056, -"31ec3c3636618c7141441294fde7e72366a407fa7ec6a64a41a7c8dfda150ca417fac868" -"1b3c5be253e3bff3ab7a5e2c01b72790d95ee09b5362be835b4d33bd20e307c3c702aa15" -"60cdc97d190a1f98b1c78e9230446e31d60d25155167f73e33ed20cea27b2010514b57ba" -"b05ed16f601e6388ea41f714b0f0241d2429022e37623c11156f66dd0fa59131d8401dba" -"f502cffb6f1d234dcb53e4243b5cf9951688821586a524848123a06afa76ab8058bcfa72" -"27a09ce30d7e8cb100c8877bb7a81b615ee6010b8e0daced7cc922c971940b757a9107de" -"60b8454dda3452e902092e7e06faa57c20aadc43c8012b9d28d12a8cd0ba0f47ab4b377f" -"316902e6dff5e4f2e4a9b9de1e4359f344e66d0565bd814091e15a25d67d89cf6e30407b" -"36b2654762bbe53a6f204b855a3f9108109e351825cf9080c89764c5f74fb4afef89d804" -"e7f7d097fd89d98171d63eaf11bd719df44c5a606be0efea358e058af2c265b2da2623fd" -"afc62b70f0711d0150625b55672060cea6a253c590b7db1427a536d8a51085756d1e6ada" -"41d9d506b5d51bcae41249d16123b7df7190e056777a70feaf7d9f051fdbbe45cbd60fc6" -"295dda84d4ebbd7284ad44be3ee3ba57c8883ead603519b8ad434e3bf630734a9243c00a" -"a07366b8f88621ec6176111f0418c66b20ff9a93009f43432aaea899dad0f4e3ae72e9ab" -"a3f678f140118eb7117230c357a5caa0fe36c4e6cf1957bbe7499f9a68b0f1536e476e53" -"457ed826d9dea53a6ded52e69052faaa4d3927b9a3f9e8b435f424b941bf2d9cd6849874" -"42a44d5acaa0da6d9f390d1a0dd6c19af427f8bb7c082ae405a8dd535dea76aa360b4faa" -"d786093e113424bb75b8cc66c41af637a7b2acdca048a501417919cf9c5cd3b2fa668860" -"d08b6717eea6f125fa1b0bae1dbb52aafce8ae2deaf92aeb5be003fb9c09fedbc286ffb5" -"e16ad8e07e725faa46ebc35500cf205fc03250075ddc050c263814b8d16d141db4ca289f" -"386719b28a09a8e5934722202beb3429899b016dfeb972fee487cdd8d18f8a681042624f" -"51", - "f43937922444421042f76756fbed0338b354516f", - - 6848, -"21b9a9686ec200456c414f2e6963e2d59e8b57e654eced3d4b57fe565b51c9045c697566" -"44c953178f0a64a6e44d1b46f58763c6a71ce4c373b0821c0b3927a64159c32125ec916b" -"6edd9bf41c3d80725b9675d6a97c8a7e3b662fac9dbcf6379a319a805b5341a8d360fe00" -"5a5c9ac1976094fea43566d66d220aee5901bd1f2d98036b2d27eb36843e94b2e5d1f09c" -"738ec826de6e0034cf8b1dca873104c5c33704cae290177d491d65f307c50a69b5c81936" -"a050e1fe2b4a6f296e73549323b6a885c3b54ee5eca67aa90660719126b590163203909e" -"470608f157f033f017bcf48518bf17d63380dabe2bc9ac7d8efe34aedcae957aeb68f10c" -"8ad02c4465f1f2b029d5fbb8e8538d18be294394b54b0ee6e67a79fce11731604f3ac4f8" -"d6ffa9ef3d2081f3d1c99ca107a7bf3d624324a7978ec38af0bcd0d7ee568572328b212b" -"9dc831efb7880e3f4d6ca7e25f8e80d73913fb8edfffd758ae4df61b4140634a92f49314" -"6138ebdcdaa083ea72d52a601230aa6f77874dcad9479f5bcac3763662cc30cb99823c5f" -"f469dcbd64c028286b0e579580fd3a17b56b099b97bf62d555798f7a250e08b0e4f238c3" -"fcf684198bd48a68c208a6268be2bb416eda3011b523388bce8357b7f26122640420461a" -"bcabcb5004519adfa2d43db718bce7d0c8f1b4645c89315c65df1f0842e5741244bba3b5" -"10801d2a446818635d0e8ffcd80c8a6f97ca9f878793b91780ee18eb6c2b99ffac3c38ef" -"b7c6d3af0478317c2b9c421247eba8209ea677f984e2398c7c243696a12df2164417f602" -"d7a1d33809c865b73397550ff33fe116166ae0ddbccd00e2b6fc538733830ac39c328018" -"bcb87ac52474ad3cce8780d6002e14c6734f814cb551632bcc31965c1cd23d048b9509a4" -"e22ab88f76a6dba209d5dd2febd1413a64d32be8574a22341f2a14e4bd879abb35627ef1" -"35c37be0f80843006a7cc7158d2bb2a71bf536b36de20ca09bb5b674e5c408485106e6fa" -"966e4f2139779b46f6010051615b5b41cda12d206d48e436b9f75d7e1398a656abb0087a" -"a0eb453368fc1ecc71a31846080f804d7b67ad6a7aa48579c3a1435eff7577f4e6004d46" -"aac4130293f6f62ae6d50c0d0c3b9876f0728923a94843785966a27555dd3ce68602e7d9" -"0f7c7c552f9bda4969ec2dc3e30a70620db6300e822a93e633ab9a7a", - "5d4d18b24b877092188a44be4f2e80ab1d41e795", - - 7640, -"1c87f48f4409c3682e2cf34c63286dd52701b6c14e08669851a6dc8fa15530ad3bef692c" -"7d2bf02238644561069df19bdec3bccae5311fce877afc58c7628d08d32d9bd2dc1df0a6" -"24360e505944219d211f33bff62e9ff2342ac86070240a420ccaf14908e6a93c1b27b6e2" -"0324e522199e83692805cc4c7f3ea66f45a490a50d4dd558aa8e052c45c1a5dfad452674" -"edc7149024c09024913f004ceee90577ff3eaec96a1eebbdc98b440ffeb0cad9c6224efc" -"9267d2c192b53dc012fb53010926e362ef9d4238d00df9399f6cbb9acc389a7418007a6c" -"a926c59359e3608b548bdeece213f4e581d02d273781dffe26905ec161956f6dfe1c008d" -"6da8165d08f8062eea88e80c055b499f6ff8204ffdb303ab132d9b0cba1e5675f3525bbe" -"4cf2c3f2b00506f58336b36aefd865d37827f2fad7d1e59105b52f1596ea19f848037dfe" -"dc9136e824ead5505e2995d4c0769276548835430667f333fc77375125b29c1b1535602c" -"10fe161864f49a98fc274ae7335a736be6bf0a98cd019d120b87881103f86c0a6efadd8c" -"aa405b6855c384141b4f8751cc42dc0cb2913382210baaa84fe242ca66679472d815c08b" -"f3d1a7c6b5705a3de17ad157522de1eb90c568a8a1fbcbb422cca293967bb14bfdd91bc5" -"a9c4d2774dee524057e08f937f3e2bd8a04ced0fc7b16fb78a7b16ee9c6447d99e53d846" -"3726c59066af25c317fc5c01f5dc9125809e63a55f1cd7bdf7f995ca3c2655f4c7ab940f" -"2aa48bc3808961eb48b3a03c731ce627bb67dd0037206c5f2c442fc72704258548c6a9db" -"e16da45e40da009dc8b2600347620eff8361346116b550087cb9e2ba6b1d6753622e8b22" -"85589b90a8e93902aa17530104455699a1829efef153327639b2ae722d5680fec035575c" -"3b48d9ec8c8e9550e15338cc76b203f3ab597c805a8c6482676deabc997a1e4ba857a889" -"97ceba32431443c53d4d662dd5532aa177b373c93bf93122b72ed7a3189e0fa171dfabf0" -"520edf4b9d5caef595c9a3a13830c190cf84bcf9c3596aadb2a674fbc2c951d135cb7525" -"3ee6c59313444f48440a381e4b95f5086403beb19ff640603394931f15d36b1cc9f3924f" -"794c965d4449bfbdd8b543194335bdf70616dc986b49582019ac2bf8e68cfd71ec67e0aa" -"dff63db39e6a0ef207f74ec6108fae6b13f08a1e6ae01b813cb7ee40961f95f5be189c49" -"c43fbf5c594f5968e4e820a1d38f105f2ff7a57e747e4d059ffb1d0788b7c3c772b9bc1f" -"e147c723aca999015230d22c917730b935e902092f83e0a8e6db9a75d2626e0346e67e40" -"8d5b815439dab8ccb8ea23f828fff6916c4047", - "32e0f5d40ceec1fbe45ddd151c76c0b3fef1c938", - - 8432, -"084f04f8d44b333dca539ad2f45f1d94065fbb1d86d2ccf32f9486fe98f7c64011160ec0" -"cd66c9c7478ed74fde7945b9c2a95cbe14cedea849978cc2d0c8eb0df48d4834030dfac2" -"b043e793b6094a88be76b37f836a4f833467693f1aa331b97a5bbc3dbd694d96ce19d385" -"c439b26bc16fc64919d0a5eab7ad255fbdb01fac6b2872c142a24aac69b9a20c4f2f07c9" -"923c9f0220256b479c11c90903193d4e8f9e70a9dbdf796a49ca5c12a113d00afa844694" -"de942601a93a5c2532031308ad63c0ded048633935f50a7e000e9695c1efc1e59c426080" -"a7d1e69a93982a408f1f6a4769078f82f6e2b238b548e0d4af271adfa15aa02c5d7d7052" -"6e00095ffb7b74cbee4185ab54385f2707e8362e8bd1596937026f6d95e700340b6338ce" -"ba1ee854a621ce1e17a016354016200b1f98846aa46254ab15b7a128b1e840f494b2cdc9" -"daccf14107c1e149a7fc27d33121a5cc31a4d74ea6945816a9b7a83850dc2c11d26d767e" -"ec44c74b83bfd2ef8a17c37626ed80be10262fe63cf9f804b8460c16d62ae63c8dd0d124" -"1d8aaac5f220e750cb68d8631b162d80afd6b9bf929875bf2e2bc8e2b30e05babd8336be" -"31e41842673a66a68f0c5acd4d7572d0a77970f42199a4da26a56df6aad2fe420e0d5e34" -"448eb2ed33afbfb35dffaba1bf92039df89c038bae3e11c02ea08aba5240c10ea88a45a1" -"d0a8631b269bec99a28b39a3fc5b6b5d1381f7018f15638cc5274ab8dc56a62b2e9e4fee" -"f172be20170b17ec72ff67b81c15299f165810222f6a001a281b5df1153a891206aca89e" -"e7baa761a5af7c0493a3af840b9219e358b1ec1dd301f35d4d241b71ad70337bda42f0ea" -"dc9434a93ed28f96b6ea073608a314a7272fefd69d030cf22ee6e520b848fa705ed6160f" -"e54bd3bf5e89608506e882a16aced9c3cf80657cd03749f34977ced9749caa9f52b683e6" -"4d96af371b293ef4e5053a8ea9422df9dd8be45d5574730f660e79bf4cbaa5f3c93a79b4" -"0f0e4e86e0fd999ef4f26c509b0940c7a3eaf1f87c560ad89aff43cd1b9d4863aa3ebc41" -"a3dd7e5b77372b6953dae497fc7f517efe99e553052e645e8be6a3aeb362900c75ce712d" -"fcba712c4c25583728db9a883302939655ef118d603e13fcf421d0cea0f8fb7c49224681" -"d013250defa7d4fd64b69b0b52e95142e4cc1fb6332486716a82a3b02818b25025ccd283" -"198b07c7d9e08519c3c52c655db94f423912b9dc1c95f2315e44be819477e7ff6d2e3ccd" -"daa6da27722aaadf142c2b09ce9472f7fd586f68b64d71fc653decebb4397bf7af30219f" -"25c1d496514e3c73b952b8aa57f4a2bbf7dcd4a9e0456aaeb653ca2d9fa7e2e8a532b173" -"5c4609e9c4f393dd70901393e898ed704db8e9b03b253357f333a66aba24495e7c3d1ad1" -"b5200b7892554b59532ac63af3bdef590b57bd5df4fbf38d2b3fa540fa5bf89455802963" -"036bd173fe3967ed1b7d", - "ee976e4ad3cad933b283649eff9ffdb41fcccb18", - - 9224, -"bd8320703d0cac96a96aeefa3abf0f757456bf42b3e56f62070fc03e412d3b8f4e4e427b" -"c47c4600bb423b96de6b4910c20bc5c476c45feb5b429d4b35088813836fa5060ceb26db" -"bb9162e4acd683ef879a7e6a0d6549caf0f0482de8e7083d03ed2f583de1b3ef505f4b2c" -"cd8a23d86c09d47ba05093c56f21a82c815223d777d0cabb7ee4550423b5deb6690f9394" -"1862ae41590ea7a580dda79229d141a786215d75f77e74e1db9a03c9a7eb39eb35adf302" -"5e26eb31ca2d2ca507edca77d9e7cfcfd136784f2117a2afafa87fa468f08d07d720c933" -"f61820af442d260d172a0a113494ca169d33a3aeaacdcc895b356398ed85a871aba769f6" -"071abd31e9f2f5834721d0fef6f6ee0fc0e38760b6835dfcc7dbefb592e1f0c3793af7ad" -"f748786d3364f3cfd5686b1a18711af220e3637d8fad08c553ce9d5dc1183d48e8337b16" -"1fe69b50e1920316dbffec07425b5d616a805a699576590e0939f5c965bce6c7342d314a" -"c37b9c4d30166567c4f633f182de4d6b00e20a1c762789f915eaa1c89ac31b85222b1f05" -"403dedd94db9ce75ff4e49923d1999d032695fa0a1c595617830c3c9a7ab758732fcec26" -"85ae14350959b6a5f423ef726587e186b055a8daf6fa8fdefa02841b2fdbca1616dcee78" -"c685fc6dcc09f24a36097572eba3c37a3eabe98bc23836085f63ef71a54b4488615d83b2" -"6ed28c9fce78852df9b6cf8a75ca3899a7567298e91bc4ffdd04ffab0066b43b8286a4bb" -"555c78808496b252c6e0e4d153631f11f68baf88630e052acc2af5d2af2e22e4f23bb630" -"314c561a577455f86b6727bcad3c19d3e271404dec30af3d9dd0ed63cd9fa708aadfa12a" -"500ef2d99a6b71e137b56ba90036975b88004b45f577ef800f0fb3cf97577dc9da37253b" -"8675e5c8bb7e0bd26564f19eca232fb25f280f82e014424c9fbdd1411d7556e5d7906bb8" -"62206316ba03385cd820c54c82ed35b36735bc486b1885d84053eba036c1ebfb5422d93d" -"a71c53deda7f74db07cd4959cdfa898ba37080d76b564d344b124dd7b80cd70ed3b52a6c" -"f9c9a32695d134bd39eb11ddeecdac86c808e469bd8a7995b667c452e7d9a54d5c85bcf6" -"d5ffdc27d491bc06f438f02c7cf018073431587c78ba08d18a8daccb2d3b26136f612ade" -"c673f3cd5eb83412b29652d55a10d0d6238d0b5365db272c917349450aff062c36191cfc" -"d45660819083f89cd42ecae9e26934a020cafeb9b2b68d544edf59574c0ca159fd195dbf" -"3e3e74244d942fffdbd4ed7f626219bab88b5a07e50b09a832d3e8ad82091114e54f2c35" -"6b48e55e36589ebad3ac6077cb7b1827748b00670df65bbf0a2e65caad3f8a97d654d64e" -"1c7dad171cafbc37110d2f7ca66524dc08fe60593e914128bd95f41137bfe819b5ca835f" -"e5741344b5c907ce20a35f4f48726141c6398e753ed9d46d3692050628c78859d5014fe4" -"dd3708e58d4d9807f8dac540492e32fa579491717ad4145c9efc24cf95605660b2e09b89" -"9369b74d3ebff41e707917ff314d93e6ac8dfd643ef2c087cd9912005b4b2681da01a369" -"42a756a3e22123cbf38c429373c6a8663130c24b24b2690b000013960b1c46a32d1d5397" -"47", - "2df09b10933afedfcd3f2532dfd29e7cb6213859", - - 10016, -"7a94978bec7f5034b12c96b86498068db28cd2726b676f54d81d8d7350804cc106bead8a" -"252b465a1f413b1c41e5697f8cece49ec0dea4dfb9fa7b1bfe7a4a00981875b420d094bb" -"1ce86c1b8c2e1dbebf819c176b926409fdec69042e324e71d7a8d75006f5a11f512811fe" -"6af88a12f450e327950b18994dfc3f740631beda6c78bca5fe23d54e6509120e05cd1842" -"d3639f1466cf26585030e5b4aefe0404fe900afc31e1980f0193579085342f1803c1ba27" -"0568f80eaf92440c4f2186b736f6ab9dc7b7522ccdcfc8cf12b6375a2d721aa89b5ef482" -"112a42c31123aebabcb485d0e72d6b6b70c44e12d2da98d1f87fa9df4f37847e1ffec823" -"1b8be3d737d282ddb9cc4b95937acfa0f028ba450def4d134a7d0fc88119bf7296e18cd4" -"4f56890b661b5b72ddfa34c29228067e13caf08eb3b7fd29de800df9a9ae137aad4a81a4" -"16a301c9f74b66c0e163e243b3187996b36eb569de3d9c007d78df91f9b554eef0eaa663" -"88754ce20460b75d95e2d0747229a1502a5652cf39ca58e1daa0e9321d7ab3093981cd70" -"23a7ee956030dd70177028a66ad619ad0629e631f91228b7c5db8e81b276d3b168c1edb1" -"bc0888d1cbcbb23245c2d8e40c1ff14bfe13f9c70e93a1939a5c45eef9351e795374b9e1" -"b5c3a7bd642477ba7233e1f590ab44a8232c53099a3c0a6ffe8be8b7ca7b58e6fedf700f" -"6f03dd7861ee1ef857e3f1a32a2e0baa591d0c7ca04cb231cc254d29cda873f00d68f465" -"00d6101cfdc2e8004c1f333d8007325d06ffe6b0ff7b80f24ba51928e65aa3cb78752028" -"27511207b089328bb60264595a2cebfc0b84d9899f5eca7ea3e1d2f0f053b4e67f975500" -"7ff3705ca4178ab9c15b29dd99494135f35befbcec05691d91f6361cad9c9a32e0e65577" -"f14d8dc66515081b51d09e3f6c25eea868cf519a83e80c935968cae6fce949a646ad53c5" -"6ee1f07dda23daef3443310bc04670afedb1a0132a04cb64fa84b4af4b3dc501044849cd" -"dd4adb8d733d1eac9c73afa4f7d75864c87787f4033ffe5ba707cbc14dd17bd1014b8b61" -"509c1f55a25cf6c0cbe49e4ddcc9e4de3fa38f7203134e4c7404ee52ef30d0b3f4e69bcc" -"7d0b2e4d8e60d9970e02cc69d537cfbc066734eb9f690a174e0194ca87a6fadad3883d91" -"6bd1700a052b26deee832701590d67e6f78938eac7c4beef3061a3474dd90dd588c1cd6e" -"6a4cda85b110fd08a30dcd85a3ebde910283366a17a100db920885600db7578be46bcfa6" -"4765ba9a8d6d5010cb1766d5a645e48365ed785e4b1d8c7c233c76291c92ef89d70bc77f" -"bf37d7ce9996367e5b13b08242ce73971f1e0c6ff2d7920fb9c821768a888a7fe0734908" -"33efb854cbf482aed5cb594fb715ec82a110130664164db488666d6198279006c1aa521f" -"9cf04250476c934eba0914fd586f62d6c5825b8cf82cd7ef915d93106c506ea6760fd8b0" -"bf39875cd1036b28417de54783173446026330ef701c3a6e5b6873b2025a2c1666bb9e41" -"a40adb4a81c1052047dabe2ad092df2ae06d6d67b87ac90be7d826ca647940c4da264cad" -"43c32a2bb8d5e27f87414e6887561444a80ed879ce91af13e0fbd6af1b5fa497ad0cbd2e" -"7f0f898f52f9e4710de2174e55ad07c45f8ead3b02cac6c811becc51e72324f2439099a0" -"5740090c1b165ecae7dec0b341d60a88f46d7ad8624aac231a90c93fad61fcfbbea12503" -"59fcd203862a6b0f1d71ac43db6c58a6b60c2c546edc12dd658998e8", - "f32e70862a16e3e8b199e9d81a9949d66f812cad", - - 10808, -"88dd7f273acbe799219c23184782ac0b07bade2bc46b4f8adbd25ed3d59c0fd3e2931638" -"837d31998641bbb7374c7f03d533ca60439ac4290054ff7659cc519bdda3dff2129a7bdb" -"66b3300068931ade382b7b813c970c8e15469187d25cb04b635403dc50ea6c65ab38a97c" -"431f28a41ae81c16192bd0c103f03b8fa815d6ea5bf0aa7fa534ad413b194eb12eb74f5d" -"62b3d3a7411eb8c8b09a261542bf6880acbdfb617a42e577009e482992253712f8d4c8bd" -"1c386bad068c7aa10a22111640041f0c35dabd0de00ebf6cd82f89cbc49325df12419278" -"ec0d5ebb670577b2fe0c3e0840c5dd0dc5b3da00669eed8ead380f968b00d42f4967faec" -"c131425fce1f7edb01cbec7e96d3c26fa6390a659e0ab069ef3edadc07e077bb816f1b22" -"98830a0fe2b393693bb79f41feca89577c5230e0a6c34b860dc1fdb10d85aa054481082c" -"494779d59ba798fcd817116c3059b7831857d0364352b354ce3b960fbb61a1b8a04d47ca" -"a0ead52a9bea4bada2646cdbaec211f391dac22f2c5b8748e36bfc3d4e8ea45131ca7f52" -"af09df21babe776fcecbb5c5dfa352c790ab27b9a5e74242bbd23970368dbefd7c3c74d1" -"61ae01c7e13c65b415f38aa660f51b69ea1c9a504fe1ad31987cb9b26a4db2c37d7b326c" -"50dbc8c91b13925306ff0e6098532dee7282a99c3ddf99f9e1024301f76e31e58271870b" -"d94b9356e892a6a798d422a48c7fd5b80efe855a4925cc93b8cf27badec5498338e2b538" -"70758b45d3e7a2fa059ed88df320a65e0a7cf87fa7e63b74cea1b7371e221f8004726642" -"30d4d57945a85b23d58f248c8cd06ccfabfa969ab8cb78317451fab60e4fdfa796e2e2a8" -"b46405839a91266d37e8d38bae545fb4060c357923b86d62f5d59d7bef5af20fbb9c7fb4" -"2c6fd487748ed3b9973dbf4b1f2c9615129fa10d21cc49c622842c37c01670be71715765" -"a98814634efbdee66bf3420f284dbd3efafc8a9117a8b9a72d9b81aa53ded78c409f3f90" -"bad6e30d5229e26f4f0cea7ee82c09e3b60ec0e768f35a7fb9007b869f9bfc49c518f648" -"3c951d3b6e22505453266ec4e7fe6a80dbe6a2458a1d6cd93044f2955607412091009c7d" -"6cd81648a3b0603c92bfdff9ec3c0104b07ed2105962ca7c56ede91cb932073c337665e2" -"409387549f9a46da05bc21c5126bd4b084bc2c06ab1019c51df30581aa4464ab92978c13" -"f6d7c7ac8d30a78f982b9a43181bbe3c3eb9f7a1230b3e53b98a3c2a028317827fbe8cf6" -"ec5e3e6b2a084d517d472b25f72fab3a34415bba488f14e7f621cfa72396ba40890e8c60" -"b04815601a0819c9bebc5e18b95e04be3f9c156bd7375d8cc8a97c13ce0a3976123419fa" -"592631317ca638c1182be06886f9663d0e8e6839573df8f52219eeb5381482a6a1681a64" -"173660bfbb6d98bf06ee31e601ee99b4b99b5671ed0253260b3077ed5b977c6a79b4ff9a" -"08efd3cba5c39bec1a1e9807d40bbf0c988e0fd071cf2155ed7b014c88683cd869783a95" -"4cbfced9c0e80c3a92d45b508985cbbc533ba868c0dc4f112e99400345cf7524e42bf234" -"5a129e53da4051c429af2ef09aba33ae3c820ec1529132a203bd2b81534f2e865265f55c" -"9395caf0e0d3e1762c95eaaec935e765dc963b3e0d0a04b28373ab560fa9ba5ca71ced5d" -"17bb8b56f314f6f0d0bc8104b3f1835eca7eaac15adf912cf9a6945cfd1de392342dd596" -"d67e7ffcb7e086a6c1ea318aa2e0c2b5c2da079078232c637de0d317a1f26640bc1dac5b" -"e8699b53edc86e4bfdfaf797a2ae350bf4ea29790face675c4d2e85b8f37a694c91f6a14" -"1fd561274392ee6ee1a14424d5c134a69bcb4333079400f03615952fc4c99bf03f5733a8" -"dc71524269fc5c648371f5f3098314d9d10258", - "08632c75676571a5db5971f5d99cb8de6bf1792a", - - 11600, -"85d43615942fcaa449329fd1fe9efb17545eb252cac752228f1e9d90955a3cf4e72cb116" -"3c3d8e93ccb7e4826206ff58b3e05009ee82ab70943db3f18a32925d6d5aed1525c91673" -"bd33846571af815b09bb236466807d935b5816a8be8e9becbe65d05d765bcc0bc3ae66c2" -"5320ebe9fff712aa5b4931548b76b0fd58f6be6b83554435587b1725873172e130e1a3ca" -"3d9d0425f4632d79cca0683780f266a0633230e4f3b25f87b0c390092f7b13c66ab5e31b" -"5a58dbcac8dd26a0600bf85507057bb36e870dfae76da8847875a1a52e4596d5b4b0a211" -"2435d27e1dc8dd5016d60feaf2838746d436a2983457b72e3357059b2bf1e9148bb0551a" -"e2b27d5a39abd3d1a62c36331e26668e8baabc2a1ef218b5e7a51a9ca35795bcd54f403a" -"188eafafb30d82896e45ddaea4f418629a1fb76a0f539c7114317bac1e2a8fba5a868bce" -"40abd40f6b9ced3fa8c0329b4de5ca03cc84d75b8746ef31e6c8d0a0a79b4f747690928e" -"be327f8bbe9374a0df4c39c845bf3322a49fda9455b36db5a9d6e4ea7d4326cf0e0f7cd8" -"0ff74538f95cec01a38c188d1243221e9272ccc1053e30787c4cf697043cca6fc3730d2a" -"431ecbf60d73ee667a3ab114c68d578c66dc1c659b346cb148c053980190353f6499bfef" -"acfd1d73838d6dc1188c74dd72b690fb0481eee481a3fd9af1d4233f05d5ae33a7b10d7d" -"d643406cb1f88d7dd1d77580dcbee6f757eeb2bfbcc940f2cddb820b2718264b1a64115c" -"b85909352c44b13d4e70bbb374a8594d8af7f41f65b221bf54b8d1a7f8f9c7da563550cb" -"2b062e7a7f21d5e07dd9da8d82e5a89074627597551c745718094c2eb316ca077526d27f" -"9a589c461d891dc7cd1bc20ba3f464da53c97924219c87a0f683dfb3b3ac8793c59e78ac" -"fac109439221ac599a6fd8d2754946d6bcba60784805f7958c9e34ff287ad1dbbc888848" -"fa80cc4200dbb8c5e4224535906cbffdd0237a77a906c10ced740f9c0ce7821f2dbf8c8d" -"7d41ecfcc7dfdc0846b98c78b765d01fb1eb15ff39149ab592e5dd1152665304bba85bbf" -"4705751985aaaf31245361554d561a2337e3daeef58a826492fd886d5f18ef568c1e772e" -"f6461170407695e3254eb7bf0c683811ddde5960140d959114998f08bdb24a104095987d" -"3255d590e0dbd41ae32b1ae4f4ea4a4f011de1388034231e034756870c9f2d9f23788723" -"27055a7de2b5e931dfb53e7780b6d4294bf094e08567025b026db9203b681565a1d52f30" -"318d0ebe49471b22ba5fd62e1ed6c8966c99b853c9062246a1ace51ef7523c7bf93bef53" -"d8a9cb96d6a04f0da1eca888df66e0380a72525a7ecc6115d08569a66248f6ba34e2341b" -"fd01a78f7b3c1cfe0754e0d26cba2fa3f951ef14d5749ff8933b8aba06fa40fb570b467c" -"54ce0d3f0bed21e998e5a36b3bc2f9e1ae29c4bab59c121af6fad67c0b45959cd6a86194" -"14b90b4535fb95f86ca7e64502acc135eff4f8a3abe9dde84238fab7a7d402454a3f07ad" -"ec05ec94b2891e0879037fae6acaa31dcecf3f85236ade946f5ad69ad4077beb65099285" -"38ee09f2bc38e5704da67b5006b5e39cd765aafcd740c7dadb99d0c547126e1324610fcb" -"7353dac2c110e803fca2b17485b1c4b78690bc4f867e6f043b2568889f67985a465a48eb" -"ee915200589e915756d4968d26529c3ffe3dbe70e84c682ad08a0c68db571634fbb0210d" -"c1b16b8b725886465c8c51f36a5e27d0f78e5643e051d3bddd512ce511f6bdf3dfe42759" -"00c5fea9d248c2b3f36911ed0ff41a19f6445521f251724657ea8f795b3ead0928a1657f" -"308dd7c7c1e7e490d9849df43becfa5cc25ed09ef614fd69ddc7e5e3147623901d647876" -"fb60077ffc48c51ed7d02b35f6802e3715fc708a0c88b82fe9cba0a442d38d09ca5ae483" -"21487bdef1794e7636bf7457dd2b51a391880c34d229438347e5fec8555fe263f08ba87b" -"b16dcde529248a477628067d13d0cb3bf51776f4d39fb3fbc5f669e91019323e40360e4b" -"78b6584f077bf9e03b66", - "ab7213f6becb980d40dc89fbda0ca39f225a2d33", - - 12392, -"7ae3ca60b3a96be914d24980fb5652eb68451fed5fa47abe8e771db8301fbd5331e64753" -"93d96a4010d6551701e5c23f7ecb33bec7dd7bade21381e9865d410c383a139cb4863082" -"8e9372bd197c5b5788b6599853e8487bddfd395e537772fdd706b6a1de59c695d63427da" -"0dc3261bce2e1ae3cd6de90ec45ecd7e5f14580f5672b6ccd8f9336330dffcd6a3612a74" -"975afc08fb136450e25dc6b071ddfc28fca89d846c107fd2e4bd7a19a4ff6f482d62896d" -"a583c3277e23ab5e537a653112cdf2306043b3cc39f5280bd744fe81d66f497b95650e7d" -"dfd704efcb929b13e00c3e3a7d3cd53878af8f1506d9de05dba9c39a92604b394ea25acb" -"a2cda7b4ae8b08098ba3f0fdea15359df76517be84377f33631c844313ac335aa0d590fe" -"c472d805521f0905d44ca40d7391b292184105acd142c083761c1a038c4f5ed869ea3696" -"99592a37817f64cb4205b66be1f1de6fa47a08e1bf1a94312fe61a29e71bab242af95a7b" -"38d4fb412c682b30256d91e2a46b634535d02b495240cbdb842cbe17cba6a2b94073f3d5" -"f9621ac92ddda66f98bed997216466b4bb0579d58945f8d7450808d9e285d4f1709d8a1d" -"416aa57d4a1a72bfcbfecdda33de2cff3e90e0cc60c897c4663224fc5bbe8316a83c1773" -"802837a57bc7e9238173ed41ea32fe5fe38e546014a16d5e80700d9bac7a84bb03902f31" -"79e641f86f6bc383d656daf69801499633fb367ea7593195934c72bc9bf9624c0c845ebf" -"c36eb7ad4b22fdfb45ca7d4f0d6708c69a21f6eaa6db6bde0f0bd9dc7ec9c6e24626d0a7" -"8fbeeed4b391f871e80e6a9d207165832d4ff689296f9bca15dc03c7c0381659ea5335eb" -"aafdc3e50d18e46b00f1844870d09c25afcdb0ff1ae69dd8f94f91aca6095ba6f2b6e594" -"c4acfe9903485d21b684e31a6acc2162d40e1a7bb8114a860a07e76f5265666555f2418d" -"f11ef8f7499656d12215f5da8d7d041ac72648d15d7661ad93b24f3f071334b0921d5bb0" -"6f2c7ab09f5034518b5ae21cec379373e87d51c77d44a70c2337606aadeb9036716fd920" -"a824e7ae18ce3de9f0ec3456f3454027d8c476b3f1854b240c309f6f9786fa8a073915d9" -"7a019ce99aec3260c5f6b6346cd9c41cb9267f4475958e45289965548238c6b9f91a8784" -"b4e0957ba8b73956012c9a2fc3428434b1c1679f6ed2a3e8e2c90238df428622046f668e" -"e2b053f55e64ffd45600e05a885e3264af573bacee93d23d72a0222b5442ac80bc0a8b79" -"4c2afcf3bc881d20c111f57e3450b50a703f3db1fc5de2076a006f3b7eed694b93269874" -"3b03c2ed2684bad445e69a692e744c7ac3a04f1e0e52b7a6708076d1fbffdb3f1c995828" -"7d5f884e29407030f2db06811092efd80ae08da9daec39744c5ecd3ca771663b8f4968d4" -"2a88c2c9821c73ae2a5a4d9e2551f82c03583b9c4dea775423b4748d24eb604e8ee3159b" -"a6de9bea5b22eed6264e011734ed02b2c74ce06dda890b8604ed7ba49e7bf30e28c9871b" -"e90f5cead67eaf52b5d3181c822b10701219b28ef6f6bebfa278e38acf863e2a1d4b1e40" -"fd8a0ac6ce31054446301046148bf10dc3ae3385e2026e7762bdc8003ffebc4263191a59" -"c72f4f90db03e7d52808506b33bfe1dfa53f1a3daa152e83974fbe56cfd4e8f4e7f7806a" -"084b9d0795b858100eced0b5355a72446f37779d6c67ade60a627b8077ae1f3996b03bc3" -"a5c290651c8609f0d879fbf578cbab35086e1159dd6ddbe3bf7fb5654edcc8f09e4f80d0" -"258c9376d7c53fb68f78d333b18b70170d9a11070790c956f5744c78c986b1baf08b7631" -"7a65c5f07ae6f57eb0e65488659324d29709e3735623d0426e90aa8c4629bb080881150c" -"02be1c004da84414ac001c2eb6138c26388f5a36d594f3acef0e69e2cb43b870efa84da0" -"cff9c923a9880202aed64ad76260f53c45bb1584b3e388a909d13586094b924680006a1d" -"25d4dd36c579a8ec9d3fa63c082d977a5a5021440b5314b51850f2daa6e6af6ae88cb5b1" -"44242bceb1d4771e641101f8abfc3a9b19f2de64e35e76458ad22072ba57925d73015de5" -"66c66fcaa28fdc656f90de967ad51afd331e246d74ed469d63dd7d219935c59984bd9629" -"09d1af296eb3121d782650e7d038063bab5fa854aac77de5ffebeb53d263f521e3fc02ac" -"70", - "b0e15d39025f5263e3efa255c1868d4a37041382", - - 13184, -"fa922061131282d91217a9ff07463843ae34ff7f8c28b6d93b23f1ea031d5020aa92f660" -"8c3d3df0ee24a8958fd41af880ee454e36e26438defb2de8f09c018607c967d2f0e8b80a" -"00c91c0eabe5b4c253e319b45e6106ff8bf0516f866020e5ba3f59fd669c5aeff310ebb3" -"85007069d01c64f72d2b02f4ec0b45c5ecf313056afcb52b17e08b666d01fecc42adb5b4" -"9ea00c60cacac2e0a953f1324bdd44aec00964a22a3cb33916a33da10d74ec6c6577fb37" -"5dc6ac8a6ad13e00cba419a8636d4daac8383a2e98fe90790cde7b59cfaa17c410a52abc" -"d68b127593d2fcbafd30578d195d890e981ae09e6772cb4382404a4e09f1a33c958b57db" -"ccee54ae335b6c91443206a0c100135647b844f226417a1f70317fd350d9f3789d81894a" -"aff4730072401aaeb8b713ead4394e2e64b6917d6eee2549af7bd0952f12035719065320" -"ca0d2dfe2847c6a2357c52bee4a676b12bafff66597bd479aa29299c1896f63a7523a85a" -"b7b916c5930ab66b4d191103cefc74f2f7e0e96e354f65e355ae43959a0af1880d14ea9d" -"1569e4fd47174aba7f5decb430b3f6baf80a1ef27855227b62487250d3602970e423423c" -"7ca90920685bcf75adfbe2a61ce5bd9228947b32f567927cb1a5bd8727c03aef91d6367b" -"ae7d86fd15c0977ac965a88b0d7236037aefb8d24eec8d2a07c633e031a7b9147c4c7714" -"110bfc7e261448a5d0f73c3619664e1c533c81a0acbf95d502227a33f84f0b8249e3f9fa" -"5c7905a8192b7313fc56bb20679e81333d32c797ac5162204a0eaa0e64507635921c485b" -"8f17c4e2484667a733197529e2a833eed83c57229b11bd820b5a5b78f1867787dbc217ea" -"28bfba785fb545cbc5a840a12eea428213e1aaa4e50a900ba13efcf4a5345574c2481c5d" -"927ada610bba567a55630c89d905db3d9b67fe36c9cc3d6a947664c83e69f51c74711a33" -"df66dd3ff6af9b7c1605b614d4798b4192b9a4b1508f2e2ec5aaad7eaea1ee8867353db9" -"b8d7d9a6f16aa5f339492073238c979082879aee7f94ac4be8133eaacbaedfb044e2ad4e" -"93ba0fa071dea615a5cd80d1d2678f4f93ae5a4bc9cdf3df345a29ec41d8febb23805ce4" -"2541036f3f05c63ae736f79a29802045fad9f370cabf843458c1b636ca41f387fd7821c9" -"1abbd1946afcb9186b936403233f28a5b467595131a6bc07b0873e51a08de66b5d7709a6" -"02c1bd0e7f6e8f4beb0579c51bda0e0c738ef876fcd9a40ab7873c9c31c1d63a588eebc7" -"8d9a0ae6fa35cd1a269e0d2bc68252cbd7c08d79e96f0aa6be22a016136a2b8abe9d3c9c" -"f9d60eeafe3dbc76d489b24d68c36167df4c38cf2b21cf03dc5e659e39018c3490f1237e" -"ca3f85b742ab0045d86a899c4126ad60a147cbc95b71814c274d6478668df41eb32acfb4" -"bbf024fb4e3d6be0b60653a0471afc3037ab67dcb00a2b2e24b26911e1880136e56106b7" -"f3c570fbe6f311d94624cb001914ff96fbbf481f71686aa17be0850568058fc1ee8900b4" -"7af5cf51c5ed9e00a8b532c131f42513f6b8df14a9bbc2e9ede5a560681184d41a147552" -"edfbdef98d95e6a7793229d25ba9b0b395a020aa1c0731de89e662246d59ec22e5d8f4b4" -"6fbc048efcffbc234744c5c66417070f9c751c81788f04691ccb1a09d60c46f6f73375bf" -"e2e646cf6290069541a8dfe216374c925e94d06ece72e851e81d3e8acd011f82526c2f9f" -"55955c6752dc10e93153ab58627e30fa2c573e4042954337982eec1f741be058c85bad86" -"bf3a02ed96d3201dadd48bd4de8105200dfcbcc400c3c3dd717abfc562ebe338b14b1eb5" -"ecbe9227661e49c58bf8233770d813faafc78b05711135adcc4ce4c65095ca0bdc1debc0" -"b6e5d195dbc582ce94b3afa14a422edf9b69abd7ae869a78c3a26fb50ef7122ec5af8d0c" -"78ef082ca114f8817c3d93b31809870caea2eb9533fa767c2954efb9ba07e4f1077e9f9b" -"be845661eabea2c91079321477a7c167c7234528d63d6aabbe723e0e337b2e61138a310a" -"3fd04368aa4215b7af9d0334a8a74681bcb86b4af87a0329a1ed9dc7c9aef14521785eda" -"0eeb97bdff8c9945fd0ee04e84d0dae091a69c0bfcdcd4150878fed839c0db6565fc1fed" -"0e7d6ae2efde7a59d58a9fb3b07e6f7cea51ba93f771c18b2eafa252d7fe171085776052" -"a6a17e6858f0a20b7e8be54413523989bf20a028a84d9ce98b78e6ee0b8362df49de5344" -"b409cc322354672a21ea383e870d047551a3af71aaf2f44f49a859cf001e61b592dd036f" -"c6625bf7b91ea0fb78c1563cceb8c4345bf4a9fbe6ee2b6bf5e81083", - "8b6d59106f04300cb57c7c961945cd77f3536b0a", - - 13976, -"162cca41155de90f6e4b76a34261be6666ef65bdb92b5831b47604ce42e0c6c8d2eda265" -"ab9a3716809bf2e745e7831a41768d0f6349a268d9ac6e6adfb832a5d51b75d7951cf60e" -"03d9e40de6d351f1f6ade5143531cf32839401ca6dfb9dc7473daa607aeb0c3d1e8eb3db" -"cc2f1231ad1dd394d7eac9d8dab726b895b1ee774fdcabc8031063ecfa41c71a9f03ad23" -"904cc056f17c76a1059c43faffe30dfd157fdfd7d792e162bf7a889109550a0fc4c41523" -"2af0c0d72dcbc2595299e1a1c2aeae549f7970e994c15e0ab02f113d740d38c32a4d8ec0" -"79cd099d37d954ab7ef2800902cdf7c7a19fb14b3c98aaf4c6ad93fe9a9bc7a61229828e" -"55ad4d6270d1bdbca9975d450f9be91e5699bd7ee22e8c9c22e355cf1f6793f3551cb510" -"c1d5cd363bdf8cab063e6e49a6383221f1188d64692c1f84c910a696de2e72fb9886193f" -"61ab6b41ad0ea894d37ff1261bf1fd1f187e0d0c38ab223d99ec6d6b1e6b079fc305e24e" -"2d9500c98676e2d587434495d6e107b193c06fb12d5d8eaa7b0c001d08f4d91cae5bdcae" -"6624ee755e95ca8e4c5ef5b903d7f5ba438abeffd6f16d82d88138f157e7a50d1c91fb50" -"c770f6d222fcbf6daf791b1f8379e3b157a3b496ddb2e71650c1c4ac4fc5f2aceb5b3228" -"ffc44e15c02d4baa9434e60928a93f21bc91cfd3c2719f53a8c9bcd2f2dee65a8bbc88f9" -"5d7ced211fc3b04f6e8b74eb2026d66fd57fa0cccea43f0a0381782e6fee5660afed674d" -"cb2c28cb54d2bdbbaf78e534b0742ede6b5e659b46cd516d5362a194dd0822f6417935c4" -"ff05815b118fe5687cd8b050240015cfe449d9dfde1f4fdb105586e429b2c1849aac2791" -"ef73bc54603190eba39037ec057e784bb92d497e705dfcde2addb3514b4f1926f12d5440" -"850935779019b23bd0f2977a8c9478c424a7eaaeec04f3743a77bee2bec3937412e707bc" -"92a070046e2f9c35fe5cc3f755bbb91a182e683591ab7e8cff40633730546e81522f588f" -"07bdf142b78e115d2a22d2eb5664fcdb7574c1ee5ba9abd307d7d29078cd5223c222fc69" -"60324c40cc639be84dad96b01059efce7b08538ebef89bafab834609c7e82774a14e5be6" -"62067edba6111efa8ae270f5066442b17e3f31a793581c8a3f96d92921ec26981594e28a" -"08987d020b97ad2ba5c662836e35fd3fd954bcec52b579528913959d0d942fbf1c4b9910" -"ba010c3700359a4eb7616541257f0f7727cc71b580cc903f718ecc408a315b6bbfa7f6e3" -"beb9d258804bd2731ee2fb75e763281baf1effc4690a23d5f952ab5d4311d4f5885af2eb" -"f27cad9f6d84692cb903064bbd11ca751f919b4811b7722c6ec80c360521e34d357b5c8b" -"ba6d42e5c632730f53add99ab8aa9c607b6796216753086ede158bc670d04900aca66ce8" -"357bd72d19fb147b5fde8ee4df6a0184573a2e65ba3fd3a0cb04dac5eb36d17d2f639a6e" -"b602645f3ab4da9de4c9999d6506e8e242a5a3216f9e79a4202558ecdc74249ad3caaf90" -"71b4e653338b48b3ba3e9daf1e51e49384268d63f37ce87c6335de79175cdf542d661bcd" -"74b8f5107d6ab492f54b7c3c31257ecb0b426b77ed2e2ed22bbfdaf49653e1d54e5988fa" -"d71397546f9955659f22b3a4117fc823a1e87d6fb6fb8ab7d302a1316975e8baf0c0adbd" -"35455655f6a596b6ac3be7c9a8ea34166119d5e70dfbc1aa6e14ff98eff95e94ef576656" -"5d368ec8857fb0b029bcb990d420a5ca6bc7ab08053eb4dbfc4612a345d56faefc5e03a4" -"43520b224de776a5b618e1aa16edc513d5fcefcd413031b0ddc958a6fca45d108fbde065" -"3cf2d11cb00a71cd35f57993875598b4e33e2384623a0986859105d511c717c21d6534bf" -"69fd3d7cf1682e4fc25298d90df951e77a316996beac61bb7078988118c906548af92cfe" -"72cd4b102ffad584e5e721a0cdb5621ed07dda8955d84bea57a5afa4ba06289ddfac3a9e" -"765538fd9392fc7904cedb65e38cd90967f01845ff819777a22d199f608e62c13e6ba98b" -"40824b38c784bdb41d62c4014fc7e8d93be52695e975e54d1ff92b412f451177143d74a6" -"bde0ee53a986043ab465a1ef315ac4c538e775ef4178fde5f2ea560a364de18b8fe9578a" -"ad80027c3fd32dcf0967d9d03789b1cdf19040762f626289cf3af8afe5a8e0a152d9258e" -"981872c1ec95cd7f8d65812e55cb5cbd8db61b3f068a23d9652372dfbf18d43a663c5a0d" -"026b0898e383ce5c95b0ba7fb5ed6b7304c7c9d3ba64f38d1dc579465148ccfa7271f2e3" -"e0e97e9ddac7c0874f0f396cf07851638a734df393687b7b0343afd1652ff32a2da17b3a" -"4c99d79c02256c73f32625527e5666594a8a42a12135eddb022e743371b3ab7b12ad6785" -"7635eed03558ac673d17280769b2368056276d5d72f5dbc75525f8a7558bd90b544aa6cb" -"dd964e6c70be79441969bfdf471f17a2dc0c92", - "6144c4786145852e2a01b20604c369d1b9721019", - - 14768, -"c9bed88d93806b89c2d028866842e6542ab88c895228c96c1f9f05125f8697c7402538b0" -"6465b7ae33daef847500f73d20c598c86e4804e633e1c4466e61f3ed1e9baadc5723bbed" -"9455a2ff4f99b852cfe6aa3442852ade0b18e4995ddab4250928165a9441de108d4a293d" -"1d95935de022aa17f366a31d4f4c4c54557a4235a9d56473444787ddc5c06c87087aef24" -"fa8280b7ac74d76ba685e4be7dc705e5a8a97c6c8fbd201ee5bf522438d23371c60c155d" -"93352f8fb8cc9421fe4b66ffabad46909c2c1099944fc55ed424c90aecca4f50d0331153" -"2e2844c3ff8ecb495de7ab26941cbf177b79ad7b05f918b713c417da8cf6e67db0a2dcee" -"a9179d8d636191759e13955f4244f0c4f2d88842e3015641ef0417d6e54144e8246e4591" -"6823e2c6e39bfa3b90b97781c44981710689f2ce20e70a26760d65f9971b291e12338461" -"8b3b56710dde2afaa2d46b0e2164d5c9482729350a0e256b2aa6b3fb099b618ebd7c11ca" -"62bdf176b502aedfdf9be57a8e4adbca4a4d6d8407984af2f6635f95a1e4930e375eb53f" -"245ab2ade5340c281bda87afded1268e537955c9819168bd60fd440533c75c9b1865e03f" -"de3a301d165f97aa6da236cf39cf3e49512f6350224f8d76ff02d0d3b9a99e5f70b23b9f" -"a85f72849fc98790df246c3a0f4437940e60d42b4317f72e2eb055d343a614f7f9648005" -"1e4dff186dff476462d9ced24dbb82eaa60cbbf6a0026e64001da36d30f529f48f3688b1" -"0ce9378ef3f50f5106e5007cd0eb037136254fda4f20d048769bd51a9d8d09a1e469a482" -"6aa0e25b6267b5a96abcb6e919a362fdd7b683d2f2dcec40ee5969311c07f6066ee22f36" -"89ca08381c85bea470040e9541e7a451cd43d62c2aa292a9dc4b95e3a7c4de2ba29663f3" -"8d5002eb64ceba6934bb1b0e2e55fba7fa706b514ebeeae1be4dd882d6512da066246a05" -"1d8bd042593bd0513e9cc47806ccdc7097e75bc75b8603834c85cd084e0ade3cc2c2b7e8" -"586eac62249f9769f5bdcd50e24e515f257548762db9adf3ee0846d67cfcd723d85d9588" -"09e6dd406f4c2637557c356fc52490a2a0763429ee298a1c72c098bb810e740c15faffc6" -"1e80cf6e18f86dc0e29bc150ce43ca71f5729356cd966277fd8b32366f6263c3a761b13d" -"544a631a25e1c4c8dea8d794abed47ccb4069d20f1dcb54e40a673ffb5f7b2eb31fb7d44" -"36fd8252f92dc35bb9a18fc55099b17e0807e79caf4f9641ee4bbbc2d6922508bcfae236" -"475bf78bc796548bc8d60659e816af68e5e43352fa64b5086c97c22c60ddcbbbefb9d9ef" -"7cd57c64454604793910f4f90aedb4fb824a86061a93bb79c9b0272a1ad0d24e8165f099" -"ef6f14a6a4fea09845f280022e061804090d7ab79f7bddcbef264b6f7d4e9971eddb9ca7" -"d0e79a8dbe7cff2fa59f514a608d66ae8c44d5e69745aa1b19995e366812064567d3ca20" -"9e12994c901d1b1f489be7253615f7c339b5581afd4d262e879ab8480ecb18990d3db61f" -"96895dcde9c065e645f52baafefcbe34d072dba373fd1c786fd56c3f3284be7260eaff9a" -"6a8348b762ed59e20ea443313b1164db53c3989c32fcae5b366f190b9548e8cff46df961" -"350369b490354ed8e530a91f5072967eff45c63540862fb2deab02b3ae05deac65414368" -"ac3549f277da92b692947de47cba9c1579526931e31c3490c1d3605f9bafcf468c2e9b47" -"981407ea40b0b59754621943095a2d4f4ba266ac545fe7447e54f69555a7ac9ff1e8f001" -"834fa65f2d4523061726e4d3bf4680519032dc21b7389e9f3229e4c2295d354482f8b803" -"b06ca3a8cb3ff786e60f6bc59dd3a5bfed63b0aa493bab78e97bbefb6633534d84de826f" -"4e2ccc3069050d50a2caace6c9de15ffc2656988d94b736e5688df0351a3a6a4c875cd99" -"ef304f3cc7a0585df2b0b3e6c62f86bba0d43de47b80c4eec1c4f98e60a36188219919cf" -"36dc10ee11e174a67d226ad9e71f02a7fca26ad67a4862773f3defc6a747545314063e5f" -"ce7a3f890ec57daa5532acfd027739832437c8a58dcbe11c2842e60e8ca64979d081fbd5" -"a1a028f59317212fb5869abc689a156171d69e4f4c93b949c3459904c00192d3603cd184" -"48d64b843c57f34aee7830f313e58e2abc41b44be46a96c845ffebcb7120e21d1d751046" -"c072adf65dd901a39c8019742054be5e159ea88d0885ee05fcd4c189bafe5abb68603186" -"5dc570b9342fa7f41fd5c1c87e68371ab19a83c82ae1d890c678102d5da8e6c29845657c" -"027ba07362cba4d24950ab38e747925e22ce8df9eaec1ae2c6d23374b360c8352feb6cb9" -"913e4fc49bde6caf5293030d0d234a8ecd616023cc668262591f812de208738e5336a9e6" -"9f9be2479b86be1e1369761518dfc93797ed3a55308878a944581eba50bc9c7f7a0e75c7" -"6a28acd95b277857726f3f684eefc215e0a696f47d65d30431d710d957c08ef96682b385" -"0ee5ba1c8417aafc1af2846a127ec155b4b7fb369e90eb3a5c3793a3389bbc6b532ca32b" -"f5e1f03c2280e71c6e1ae21312d4ff163eee16ebb1fdee8e887bb0d453829b4e6ed5fa70" -"8f2053f29b81e277be46", - "a757ead499a6ec3d8ab9814f839117354ae563c8" -}; - -void test_sha1(void) -{ - unsigned char buffer[4000]; - int i; - for (i=0; i < sizeof(sha1_tests) / sizeof(sha1_tests[0]); ++i) { - stb_uint len = sha1_tests[i].length / 8; - unsigned char digest[20], fdig[20]; - unsigned int h; - assert(len <= sizeof(buffer)); - assert(strlen(sha1_tests[i].message) == len*2); - assert(strlen(sha1_tests[i].digest) == 20 * 2); - for (h=0; h < len; ++h) { - char v[3]; - v[0] = sha1_tests[i].message[h*2]; - v[1] = sha1_tests[i].message[h*2+1]; - v[2] = 0; - buffer[h] = (unsigned char) strtol(v, NULL, 16); - } - stb_sha1(digest, buffer, len); - for (h=0; h < 20; ++h) { - char v[3]; - int res; - v[0] = sha1_tests[i].digest[h*2]; - v[1] = sha1_tests[i].digest[h*2+1]; - v[2] = 0; - res = digest[h] == strtol(v, NULL, 16); - c(res, sha1_tests[i].digest); - if (!res) - break; - } - { - int z; - FILE *f = fopen("data/test.bin", "wb"); - if (!f) stb_fatal("Couldn't write to test.bin"); - fwrite(buffer, len, 1, f); - fclose(f); - #ifdef _WIN32 - z = stb_sha1_file(fdig, "data/test.bin"); - if (!z) stb_fatal("Couldn't digest test.bin"); - c(memcmp(digest, fdig, 20)==0, "stb_sh1_file"); - #endif - } - } -} - - -#if 0 - -stb__obj zero, one; - -void test_packed_floats(void) -{ - stb__obj *p; - float x,y,*q; - clock_t a,b,c; - int i; - stb_float_init(); - for (i=-10; i < 10; ++i) { - float f = (float) pow(10,i); - float g = f * 10; - float delta = (g - f) / 10000; - while (f < g) { - stb__obj z = stb_float(f); - float k = stb_getfloat(z); - float p = stb_getfloat_table(z); - assert((z & 1) == 1); - assert(f == k); - assert(k == p); - f += delta; - } - } - - zero = stb_float(0); - one = stb_float(1); - - p = malloc(8192 * 4); - for (i=0; i < 8192; ++i) - p[i] = stb_rand(); - for (i=0; i < 8192; ++i) - if ((stb_rand() & 31) < 28) - p[i] = zero; - - q = malloc(4 * 1024); - - a = clock(); - - x = y = 0; - for (i=0; i < 200000000; ++i) - q[i&1023] = stb_getfloat_table(p[i&8191]); - b = clock(); - for (i=0; i < 200000000; ++i) - q[i&1023] = stb_getfloat_table2(p[i&8191]); - c = clock(); - free(p); - - free(q); - - printf("Table: %d\nIFs: %d\n", b-a, c-b); -} -#endif - - -void do_compressor(int argc,char**argv) -{ - char *p; - int len; - - int window; - if (argc == 2) { - p = stb_file(argv[1], &len); - if (p) { - int dlen, clen = stb_compress_tofile("data/dummy.bin", p, len); - char *q = stb_decompress_fromfile("data/dummy.bin", &dlen); - - if (len != dlen) { - printf("FAILED %d -> %d\n", len, clen); - } else { - int z = memcmp(q,p,dlen); - if (z != 0) - printf("FAILED %d -> %d\n", len, clen); - else - printf("%d -> %d\n", len, clen); - } - } - return; - } - - window = atoi(argv[1]); - if (window && argc == 4) { - p = stb_file(argv[3], &len); - if (p) { - stb_compress_hashsize(window); - stb_compress_tofile(argv[2], p, len); - } - } else if (argc == 3) { - p = stb_decompress_fromfile(argv[2], &len); - if (p) { - FILE *f = fopen(argv[1], "wb"); - fwrite(p,1,len,f); - fclose(f); - } else { - fprintf(stderr, "FAILED.\n"); - } - } else { - fprintf(stderr, "Usage: stb \n" - " or stb \n"); - } -} - -#if 0 -// naive backtracking implementation -int wildmatch(char *expr, char *candidate) -{ - while(*expr) { - if (*expr == '?') { - if (!*candidate) return 0; - ++candidate; - ++expr; - } else if (*expr == '*') { - ++expr; - while (*expr == '*' || *expr =='?') ++expr; - // '*' at end of expression matches anything - if (!*expr) return 1; - // now scan candidate 'til first match - while (*candidate) { - if (*candidate == *expr) { - // check this candidate - if (stb_wildmatch(expr+1, candidate+1)) - return 1; - // if not, then backtrack - } - ++candidate; - } - } else { - if (*expr != *candidate) - return 0; - ++expr, ++candidate; - } - } - return *candidate != 0; -} - -int stb_matcher_find_slow(stb_matcher *m, char *str) -{ - int result = 1; - int i,j,y,z; - uint16 *previous = NULL; - uint16 *current = NULL; - uint16 *temp; - - stb_arr_setsize(previous, 4); - stb_arr_setsize(current, 4); - - previous = stb__add_if_inactive(m, previous, m->start_node); - previous = stb__eps_closure(m,previous); - if (stb__clear_goalcheck(m, previous)) - goto done; - - while (*str) { - y = stb_arr_len(previous); - for (i=0; i < y; ++i) { - stb_nfa_node *n = &m->nodes[previous[i]]; - z = stb_arr_len(n->out); - for (j=0; j < z; ++j) { - if (n->out[j].match == *str) - current = stb__add_if_inactive(m, current, n->out[j].node); - else if (n->out[j].match == -1) { - if (*str != '\n') - current = stb__add_if_inactive(m, current, n->out[j].node); - } else if (n->out[j].match < -1) { - int z = -n->out[j].match - 2; - if (m->charset[(uint8) *str] & (1 << z)) - current = stb__add_if_inactive(m, current, n->out[j].node); - } - } - } - ++str; - stb_arr_setlen(previous, 0); - - temp = previous; - previous = current; - current = temp; - - if (!m->match_start) - previous = stb__add_if_inactive(m, previous, m->start_node); - previous = stb__eps_closure(m,previous); - if (stb__clear_goalcheck(m, previous)) - goto done; - } - - result=0; - -done: - stb_arr_free(previous); - stb_arr_free(current); - - return result; -} -#endif - - - - - - -////////////////////////////////////////////////////////////////////////// -// -// stb_parser -// -// Generates an LR(1) parser from a grammar, and can parse with it - - - -// Symbol representations -// -// Client: Internal: -// - c=0 e aka epsilon -// - c=1 $ aka end of string -// > 0 2<=c= 0 ? encode_term(x) : encode_nonterm(x)) - -stb_bitset **compute_first(short ** productions) -{ - int i, changed; - stb_bitset **first = malloc(sizeof(*first) * num_symbols); - - assert(symset); - for (i=0; i < num_symbols; ++i) - first[i] = stb_bitset_new(0, symset); - - for (i=END; i < first_nonterm; ++i) - stb_bitset_setbit(first[i], i); - - for (i=0; i < stb_arr_len(productions); ++i) { - if (productions[i][2] == 0) { - int nt = encode_nonterm(productions[i][0]); - stb_bitset_setbit(first[nt], EPS); - } - } - - do { - changed = 0; - for (i=0; i < stb_arr_len(productions); ++i) { - int j, nt = encode_nonterm(productions[i][0]); - for (j=2; productions[i][j]; ++j) { - int z = encode_symbol(productions[i][j]); - changed |= stb_bitset_unioneq_changed(first[nt], first[z], symset); - if (!stb_bitset_testbit(first[z], EPS)) - break; - } - if (!productions[i][j] && !stb_bitset_testbit(first[nt], EPS)) { - stb_bitset_setbit(first[nt], EPS); - changed = 1; - } - } - } while (changed); - return first; -} - -stb_bitset **compute_follow(short ** productions, stb_bitset **first, int start) -{ - int i,j,changed; - stb_bitset **follow = malloc(sizeof(*follow) * num_symbols); - - assert(symset); - for (i=0; i < num_symbols; ++i) - follow[i] = (i >= first_nonterm ? stb_bitset_new(0, symset) : NULL); - - stb_bitset_setbit(follow[start], END); - do { - changed = 0; - for (i=0; i < stb_arr_len(productions); ++i) { - int nt = encode_nonterm(productions[i][0]); - for (j=2; productions[i][j]; ++j) { - if (productions[i][j] < 0) { - int k,z = encode_nonterm(productions[i][j]); - for (k=j+1; productions[i][k]; ++k) { - int q = encode_symbol(productions[i][k]); - changed |= stb_bitset_unioneq_changed(follow[z], first[q], symset); - if (!stb_bitset_testbit(first[q], EPS)) - break; - } - if (!productions[i][k] == 0) - changed |= stb_bitset_unioneq_changed(follow[z], follow[nt], symset); - } - } - } - } while (changed); - - for (i=first_nonterm; i < num_symbols; ++i) - stb_bitset_clearbit(follow[i], EPS); - - return follow; -} - -void first_for_prod_plus_sym(stb_bitset **first, stb_bitset *out, short *prod, int symbol) -{ - stb_bitset_clearall(out, symset); - for(;*prod;++prod) { - int z = encode_symbol(*prod); - stb_bitset_unioneq_changed(out, first[z], symset); - if (!stb_bitset_testbit(first[z], EPS)) - return; - } - stb_bitset_unioneq_changed(out, first[symbol], symset); -} - -#define Item(p,c,t) ((void *) (((t) << 18) + ((c) << 12) + ((p) << 2))) -#define ItemProd(i) ((((uint32) (i)) >> 2) & 1023) -#define ItemCursor(i) ((((uint32) (i)) >> 12) & 63) -#define ItemLookahead(i) (((uint32) (i)) >> 18) - -static void pc(stb_ps *p) -{ -} - -typedef struct -{ - short *prod; - int prod_num; -} ProdRef; - -typedef struct -{ - stb_bitset **first; - stb_bitset **follow; - short ** prod; - ProdRef ** prod_by_nt; -} Grammar; - -stb_ps *itemset_closure(Grammar g, stb_ps *set) -{ - stb_bitset *lookahead; - int changed,i,j,k, list_len; - if (set == NULL) return set; - lookahead = stb_bitset_new(0, symset); - do { - void **list = stb_ps_getlist(set, &list_len); - changed = 0; - for (i=0; i < list_len; ++i) { - ProdRef *prod; - int nt, *looklist; - int p = ItemProd(list[i]), c = ItemCursor(list[i]), t = ItemLookahead(list[i]); - if (g.prod[p][c] >= 0) continue; - nt = encode_nonterm(g.prod[p][c]); - first_for_prod_plus_sym(g.first, lookahead, g.prod[p]+c+1, t); - looklist = stb_bitset_getlist(lookahead, 1, first_nonterm); - - prod = g.prod_by_nt[nt]; - for (j=0; j < stb_arr_len(prod); ++j) { - assert(prod[j].prod[0] == g.prod[p][c]); - // matched production; now iterate terminals - for (k=0; k < stb_arr_len(looklist); ++k) { - void *item = Item(prod[j].prod_num,2,looklist[k]); - if (!stb_ps_find(set, item)) { - changed = 1; - set = stb_ps_add(set, item); - pc(set); - } - } - } - stb_arr_free(looklist); - } - free(list); - } while (changed); - free(lookahead); - return set; -} - -stb_ps *itemset_goto(Grammar g, stb_ps *set, int sym) -{ - int i, listlen; - void **list = stb_ps_fastlist(set, &listlen); - stb_ps *out = NULL; - for (i=0; i < listlen; ++i) { - int p,c; - if (!stb_ps_fastlist_valid(list[i])) continue; - p = ItemProd(list[i]), c = ItemCursor(list[i]); - if (encode_symbol(g.prod[p][c]) == sym) { - void *z = Item(p,c+1,ItemLookahead(list[i])); - if (!stb_ps_find(out, z)) - out = stb_ps_add(out, z); - pc(out); - } - } - return itemset_closure(g, out); -} - -void itemset_all_nextsym(Grammar g, stb_bitset *out, stb_ps *set) -{ - int i, listlen; - void **list = stb_ps_fastlist(set, &listlen); - stb_bitset_clearall(out, symset); - pc(set); - for (i=0; i < listlen; ++i) { - if (stb_ps_fastlist_valid(list[i])) { - int p = ItemProd(list[i]); - int c = ItemCursor(list[i]); - if (g.prod[p][c]) - stb_bitset_setbit(out, encode_symbol(g.prod[p][c])); - } - } -} - -stb_ps ** generate_items(Grammar g, int start_prod) -{ - stb_ps ** all=NULL; - int i,j,k; - stb_bitset *try = stb_bitset_new(0,symset); - stb_ps *set = NULL; - void *item = Item(start_prod, 2, END); - set = stb_ps_add(set, item); - pc(set); - set = itemset_closure(g, set); - pc(set); - stb_arr_push(all, set); - for (i = 0; i < stb_arr_len(all); ++i) { - // only try symbols that appear in all[i]... there's a smarter way to do this, - // which is to take all[i], and divide it up by symbol - pc(all[i]); - itemset_all_nextsym(g, try, all[i]); - for (j = 1; j < num_symbols; ++j) { - if (stb_bitset_testbit(try, j)) { - stb_ps *out; - if (stb_arr_len(all) > 4) pc(all[4]); - if (i == 1 && j == 29) { - if (stb_arr_len(all) > 4) pc(all[4]); - out = itemset_goto(g, all[i], j); - if (stb_arr_len(all) > 4) pc(all[4]); - } else - out = itemset_goto(g, all[i], j); - pc(out); - if (stb_arr_len(all) > 4) pc(all[4]); - if (out != NULL) { - // add it to the array if it's not already there - for (k=0; k < stb_arr_len(all); ++k) - if (stb_ps_eq(all[k], out)) - break; - if (k == stb_arr_len(all)) { - stb_arr_push(all, out); - pc(out); - if (stb_arr_len(all) > 4) pc(all[4]); - } else - stb_ps_delete(out); - } - } - } - } - free(try); - return all; -} - -typedef struct -{ - int num_stack; - int function; -} Reduction; - -typedef struct -{ - short *encode_term; - Reduction *reductions; - short **action_goto; // terminals are action, nonterminals are goto - int start; - int end_term; -} Parser; - -enum -{ - A_error, A_accept, A_shift, A_reduce, A_conflict -}; - -typedef struct -{ - uint8 type; - uint8 cursor; - short prod; - short value; -} Action; - -Parser *parser_create(short **productions, int num_prod, int start_nt, int end_term) -{ - short *mini_rule = malloc(4 * sizeof(mini_rule[0])); - Action *actions; - Grammar g; - stb_ps ** sets; - Parser *p = malloc(sizeof(*p)); - int i,j,n; - stb_bitset *mapped; - int min_s=0, max_s=0, termset, ntset, num_states, num_reductions, init_prod; - - int synth_start; - - // remap sparse terminals and nonterminals - - for (i=0; i < num_prod; ++i) { - for (j=2; productions[i][j]; ++j) { - if (productions[i][j] < min_s) min_s = productions[i][j]; - if (productions[i][j] > max_s) max_s = productions[i][j]; - } - } - synth_start = --min_s; - - termset = (max_s + 32) >> 5; - ntset = (~min_s + 32) >> 5; - memset(encode_term, 0, sizeof(encode_term)); - memset(encode_nonterm, 0, sizeof(encode_nonterm)); - - mapped = stb_bitset_new(0, termset); - n = 2; - for (i=0; i < num_prod; ++i) - for (j=2; productions[i][j]; ++j) - if (productions[i][j] > 0) - if (!stb_bitset_testbit(mapped, productions[i][j])) { - stb_bitset_setbit(mapped, productions[i][j]); - encode_term[productions[i][j]] = n++; - } - free(mapped); - - first_nonterm = n; - - mapped = stb_bitset_new(0, ntset); - for (i=0; i < num_prod; ++i) - for (j=2; productions[i][j]; ++j) - if (productions[i][j] < 0) - if (!stb_bitset_testbit(mapped, ~productions[i][j])) { - stb_bitset_setbit(mapped, ~productions[i][j]); - encode_nonterm[~productions[i][j]] = n++; - } - free(mapped); - - // add a special start state for internal processing - p->start = n++; - encode_nonterm[synth_start] = p->start; - mini_rule[0] = synth_start; - mini_rule[1] = -32768; - mini_rule[2] = start_nt; - mini_rule[3] = 0; - - p->end_term = end_term; - - num_symbols = n; - - // create tables - g.prod = NULL; - g.prod_by_nt = malloc(num_symbols * sizeof(g.prod_by_nt[0])); - for (i=0; i < num_symbols; ++i) - g.prod_by_nt[i] = NULL; - - for (i=0; i < num_prod; ++i) { - stb_arr_push(g.prod, productions[i]); - } - init_prod = stb_arr_len(g.prod); - stb_arr_push(g.prod, mini_rule); - - num_reductions = stb_arr_len(g.prod); - p->reductions = malloc(num_reductions * sizeof(*p->reductions)); - - symset = (num_symbols + 31) >> 5; - g.first = compute_first(g.prod); - g.follow = compute_follow(g.prod, g.first, p->start); - - for (i=0; i < stb_arr_len(g.prod); ++i) { - ProdRef pr = { g.prod[i], i }; - stb_arr_push(g.prod_by_nt[encode_nonterm(g.prod[i][0])], pr); - } - - sets = generate_items(g, init_prod); - - num_states = stb_arr_len(sets); - // now generate tables - - actions = malloc(sizeof(*actions) * first_nonterm); - p->action_goto = (short **) stb_array_block_alloc(num_states, sizeof(short) * num_symbols); - for (i=0; i < num_states; ++i) { - int j,n; - void **list = stb_ps_getlist(sets[i], &n); - memset(actions, 0, sizeof(*actions) * first_nonterm); - for (j=0; j < n; ++j) { - int p = ItemProd(list[j]), c = ItemCursor(list[j]), t = ItemLookahead(list[j]); - if (g.prod[p][c] == 0) { - if (p == init_prod) { - // @TODO: check for conflicts - assert(actions[t].type == A_error || actions[t].type == A_accept); - actions[t].type = A_accept; - } else { - // reduce production p - if (actions[t].type == A_reduce) { - // is it the same reduction we already have? - if (actions[t].prod != p) { - // no, it's a reduce-reduce conflict! - printf("Reduce-reduce conflict for rule %d and %d, lookahead %d\n", p, actions[t].prod, t); - // @TODO: use precedence - actions[t].type = A_conflict; - } - } else if (actions[t].type == A_shift) { - printf("Shift-reduce conflict for rule %d and %d, lookahead %d\n", actions[t].prod, p, t); - actions[t].type = A_conflict; - } else if (actions[t].type == A_accept) { - assert(0); - } else if (actions[t].type == A_error) { - actions[t].type = A_reduce; - actions[t].prod = p; - } - } - } else if (g.prod[p][c] > 0) { - int a = encode_symbol(g.prod[p][c]), k; - stb_ps *out = itemset_goto(g, sets[i], a); - for (k=0; k < stb_arr_len(sets); ++k) - if (stb_ps_eq(sets[k], out)) - break; - assert(k < stb_arr_len(sets)); - // shift k - if (actions[a].type == A_shift) { - if (actions[a].value != k) { - printf("Shift-shift conflict! Rule %d and %d with lookahead %d/%d\n", actions[a].prod, p, a,t); - actions[a].type = A_conflict; - } - } else if (actions[a].type == A_reduce) { - printf("Shift-reduce conflict for rule %d and %d, lookahead %d/%d\n", p, actions[a].prod, a,t); - actions[a].type = A_conflict; - } else if (actions[a].type == A_accept) { - assert(0); - } else if (actions[a].type == A_error) { - actions[a].type = A_shift; - actions[a].prod = p; - actions[a].cursor = c; - actions[a].value = k; - } - } - } - // @TODO: recompile actions into p->action_goto - } - - free(mini_rule); - stb_pointer_array_free(g.first , num_symbols); free(g.first ); - stb_pointer_array_free(g.follow, num_symbols); free(g.follow); - stb_arr_free(g.prod); - for (i=0; i < num_symbols; ++i) - stb_arr_free(g.prod_by_nt[i]); - free(g.prod_by_nt); - for (i=0; i < stb_arr_len(sets); ++i) - stb_ps_delete(sets[i]); - stb_arr_free(sets); - - return p; -} - -void parser_destroy(Parser *p) -{ - free(p); -} - -#if 0 -enum nonterm -{ - N_globals = -50, - N_global, N_vardef, N_varinitlist, N_varinit, N_funcdef, N_optid, N_optparamlist, - N_paramlist, N_param, N_optinit, N_optcomma, N_statements, N_statement, - N_optexpr, N_assign, N_if, N_ifcore, N_else, N_dictdef, N_dictdef2, - N_dictdefitem, N_expr, - N__last -}; - -short grammar[][10] = -{ - { N_globals , 0, N_globals, N_global }, - { N_globals , 0 }, - { N_global , 0, N_vardef }, - { N_global , 0, N_funcdef }, - { N_vardef , 0, ST_var, N_varinitlist, }, - { N_varinitlist, 0, N_varinitlist, ',', N_varinit }, - { N_varinitlist, 0, N_varinit, }, - { N_varinit , 0, ST_id, N_optinit, }, - { N_funcdef , 0, ST_func, N_optid, '(', N_optparamlist, ')', N_statements, ST_end }, - { N_optid , 0, ST_id }, - { N_optid , 0, }, - { N_optparamlist, 0, }, - { N_optparamlist, 0, N_paramlist, N_optcomma }, - { N_paramlist , 0, N_paramlist, ',', N_param }, - { N_paramlist , 0, N_param }, - { N_param , 0, ST_id, N_optinit }, - { N_optinit , 0, '=', N_expr }, - { N_optinit , 0, }, - { N_optcomma , 0, ',' }, - { N_optcomma , 0, }, - { N_statements , 0, N_statements, N_statement }, - { N_statement , 0, N_statement, ';' }, - { N_statement , 0, N_varinit }, - { N_statement , 0, ST_return, N_expr }, - { N_statement , 0, ST_break , N_optexpr }, - { N_optexpr , 0, N_expr }, - { N_optexpr , 0, }, - { N_statement , 0, ST_continue }, - { N_statement , 0, N_assign }, - { N_assign , 0, N_expr, '=', N_assign }, - //{ N_assign , 0, N_expr }, - { N_statement , 0, ST_while, N_expr, N_statements, ST_end }, - { N_statement , 0, ST_if, N_if, }, - { N_if , 0, N_ifcore, ST_end, }, - { N_ifcore , 0, N_expr, ST_then, N_statements, N_else, ST_end }, - { N_else , 0, ST_elseif, N_ifcore }, - { N_else , 0, ST_else, N_statements }, - { N_else , 0, }, - { N_dictdef , 0, N_dictdef2, N_optcomma }, - { N_dictdef2 , 0, N_dictdef2, ',', N_dictdefitem }, - { N_dictdef2 , 0, N_dictdefitem }, - { N_dictdefitem, 0, ST_id, '=', N_expr }, - { N_dictdefitem, 0, N_expr }, - { N_expr , 0, ST_number }, - { N_expr , 0, ST_string }, - { N_expr , 0, ST_id }, - { N_expr , 0, N_funcdef }, - { N_expr , 0, '-', N_expr }, - { N_expr , 0, '{', N_dictdef, '}' }, - { N_expr , 0, '(', N_expr, ')' }, - { N_expr , 0, N_expr, '.', ST_id }, - { N_expr , 0, N_expr, '[', N_expr, ']' }, - { N_expr , 0, N_expr, '(', N_dictdef, ')' }, -#if 0 -#define BINOP(op) { N_expr, 0, N_expr, op, N_expr } - BINOP(ST_and), BINOP(ST_or), BINOP(ST_eq), BINOP(ST_ne), - BINOP(ST_le), BINOP(ST_ge), BINOP('>') , BINOP('<' ), - BINOP('&'), BINOP('|'), BINOP('^'), BINOP('+'), BINOP('-'), - BINOP('*'), BINOP('/'), BINOP('%'), -#undef BINOP -#endif -}; - -short *grammar_list[stb_arrcount(grammar)]; - -void test_parser_generator(void) -{ - Parser *p; - int i; - assert(N__last <= 0); - for (i=0; i < stb_arrcount(grammar); ++i) - grammar_list[i] = grammar[i]; - p = parser_create(grammar_list, stb_arrcount(grammar), N_globals, 0); - parser_destroy(p); -} -#endif - - -#if 0 -// stb_threadtest.c - - -#include -#define STB_DEFINE -//#define STB_THREAD_TEST -#include "../stb.h" - -#define NUM_WORK 100 - -void *work_consumer(void *p) -{ - stb__thread_sleep(20); - return NULL; -} - -int pass; -stb_threadqueue *tq1, *tq2, *tq3, *tq4; -volatile float t1,t2; - -// with windows.h -// Worked correctly with 100,000,000 enqueue/dequeue WAITLESS -// (770 passes, 170000 per pass) -// Worked correctly with 2,500,000 enqueue/dequeue !WAITLESS -// (15 passes, 170000 per pass) -// Worked correctly with 1,500,000 enqueue/dequeue WAITLESS && STB_THREAD_TEST -// (9 passes, 170000 per pass) -// without windows.h -// Worked correctly with 1,000,000 enqueue/dequeue WAITLESS && STB_THREAD_TEST -// (6 passes, 170000 per pass) -// Worked correctly with 500,000 enqueue/dequeue !WAITLESS && STB_THREAD_TEST -// (3 passes, 170000 per pass) -// Worked correctly with 1,000,000 enqueue/dequeue WAITLESS -// (15 passes, 170000 per pass) -#define WAITLESS - -volatile int table[1000*1000*10]; - -void wait(int n) -{ -#ifndef WAITLESS - int j; - float y; - for (j=0; j < n; ++j) - y += 1 / (t1+j); - t2 = y; -#endif -} - -void *tq1_consumer(void *p) -{ - for(;;) { - int z; - float y = 0; - stb_threadq_get_block(tq1, &z); - wait(5000); - table[z] = pass; - } -} - -void *tq2_consumer(void *p) -{ - for(;;) { - int z; - if (stb_threadq_get(tq2, &z)) - table[z] = pass; - wait(1000); - } -} - -void *tq3_consumer(void *p) -{ - for(;;) { - int z; - stb_threadq_get_block(tq3, &z); - table[z] = pass; - wait(500); - } -} - -void *tq4_consumer(void *p) -{ - for (;;) { - int z; - stb_threadq_get_block(tq4, &z); - table[z] = pass; - wait(500); - } -} - -typedef struct -{ - int start, end; - stb_threadqueue *tq; - int delay; -} write_data; - -void *writer(void *q) -{ - int i; - write_data *p = (write_data *) q; - for (i=p->start; i < p->end; ++i) { - stb_threadq_add_block(p->tq, &i); - #ifndef WAITLESS - if (p->delay) stb__thread_sleep(p->delay); - else { - int j; - float z = 0; - for (j=0; j <= 20; ++j) - z += 1 / (t1+j); - t2 = z; - } - #endif - } - return NULL; -} - -write_data info[256]; -int pos; - -void start_writer(int z, int count, stb_threadqueue *tq, int delay) -{ - info[z].start = pos; - info[z].end = pos+count; - info[z].tq = tq; - info[z].delay = delay; - stb_create_thread(writer, &info[z]); - pos += count; -} - -int main(int argc, char **argv) -{ - int i; - stb_sync s = stb_sync_new(); - stb_sync_set_target(s, NUM_WORK+1); - stb_work_numthreads(2); - for (i=0; i < NUM_WORK; ++i) { - stb_work_reach(work_consumer, NULL, NULL, s); - } - printf("Started stb_work test.\n"); - - t1 = 1; - - // create the queues - tq1 = stb_threadq_new(4, 4, TRUE , TRUE); - tq2 = stb_threadq_new(4, 4, TRUE , FALSE); - tq3 = stb_threadq_new(4, 4, FALSE, TRUE); - tq4 = stb_threadq_new(4, 4, FALSE, FALSE); - - // start the consumers - stb_create_thread(tq1_consumer, NULL); - stb_create_thread(tq1_consumer, NULL); - stb_create_thread(tq1_consumer, NULL); - - stb_create_thread(tq2_consumer, NULL); - - stb_create_thread(tq3_consumer, NULL); - stb_create_thread(tq3_consumer, NULL); - stb_create_thread(tq3_consumer, NULL); - stb_create_thread(tq3_consumer, NULL); - stb_create_thread(tq3_consumer, NULL); - stb_create_thread(tq3_consumer, NULL); - stb_create_thread(tq3_consumer, NULL); - - stb_create_thread(tq4_consumer, NULL); - - for (pass=1; pass <= 5000; ++pass) { - int z = 0; - int last_n = -1; - int identical = 0; - pos = 0; - start_writer(z++, 50000, tq1, 0); - start_writer(z++, 50000, tq1, 0); - start_writer(z++, 50000, tq1, 0); - - start_writer(z++, 5000, tq2, 1); - start_writer(z++, 3000, tq2, 3); - start_writer(z++, 2000, tq2, 5); - - start_writer(z++, 5000, tq3, 3); - - start_writer(z++, 5000, tq4, 3); - #ifndef WAITLESS - stb__thread_sleep(8000); - #endif - for(;;) { - int n =0; - for (i=0; i < pos; ++i) { - if (table[i] == pass) - ++n; - } - if (n == pos) break; - if (n == last_n) { - ++identical; - if (identical == 3) { - printf("Problem slots:\n"); - for (i=0; i < pos; ++i) { - if (table[i] != pass) printf("%d ", i); - } - printf("\n"); - } else { - if (identical < 3) - printf("Processed %d of %d\n", n, pos); - else - printf("."); - } - } else { - identical = 0; - printf("Processed %d of %d\n", n, pos); - } - last_n = n; - #ifdef WAITLESS - stb__thread_sleep(750); - #else - stb__thread_sleep(3000); - #endif - } - printf("Finished pass %d\n", pass); - } - - stb_sync_reach_and_wait(s); - printf("stb_work test completed ok.\n"); - return 0; -} -#endif - - -#if 0 -////////////////////////////////////////////////////////////////////////////// -// -// collapse tree leaves up to parents until we only have N nodes -// useful for cmirror summaries - -typedef struct stb_summary_tree -{ - struct stb_summary_tree **children; - int num_children; - float weight; -} stb_summary_tree; - -STB_EXTERN void *stb_summarize_tree(void *tree, int limit, float reweight); - -#ifdef STB_DEFINE - -typedef struct stb_summary_tree2 -{ - STB__ARR(struct stb_summary_tree2 *) children; - int num_children; - float weight; - float weight_with_all_children; - float makes_target_weight; - float weight_at_target; - stb_summary_tree *original; - struct stb_summary_tree2 *target; - STB__ARR(struct stb_summary_tree2 *) targeters; -} stb_summary_tree2; - -static stb_summary_tree2 *stb__summarize_clone(stb_summary_tree *t) -{ - int i; - stb_summary_tree2 *s; - s = (stb_summary_tree2 *) malloc(sizeof(*s)); - s->original = t; - s->weight = t->weight; - s->weight_with_all_children = 0; - s->weight_at_target = 0; - s->target = NULL; - s->targeters = NULL; - s->num_children = t->num_children; - s->children = NULL; - for (i=0; i < s->num_children; ++i) - stb_arr_push(s->children, stb__summarize_clone(t->children[i])); - return s; -} - -static float stb__summarize_compute_targets(stb_summary_tree2 *parent, stb_summary_tree2 *node, float reweight, float weight) -{ - float total = 0; - if (node->weight == 0 && node->num_children == 1 && parent) { - node->target = parent; - return stb__summarize_compute_targets(parent, node->children[0], reweight, weight*reweight); - } else { - float total=0; - int i; - for (i=0; i < node->num_children; ++i) - total += stb__summarize_compute_targets(node, node->children[i], reweight, reweight); - node->weight_with_all_children = total + node->weight; - if (parent && node->weight_with_all_children) { - node->target = parent; - node->weight_at_target = node->weight_with_all_children * weight; - node->makes_target_weight = node->weight_at_target + parent->weight; - stb_arr_push(parent->targeters, node); - } else { - node->target = NULL; - node->weight_at_target = node->weight; - node->makes_target_weight = 0; - } - return node->weight_with_all_children * weight; - } -} - -static stb_summary_tree2 ** stb__summarize_make_array(STB__ARR(stb_summary_tree2 *) all, stb_summary_tree2 *tree) -{ - int i; - stb_arr_push(all, tree); - for (i=0; i < tree->num_children; ++i) - all = stb__summarize_make_array(all, tree->children[i]); - return all; -} - -typedef stb_summary_tree2 * stb__stree2; -stb_define_sort(stb__summarysort, stb__stree2, (*a)->makes_target_weight < (*b)->makes_target_weight) - -void *stb_summarize_tree(void *tree, int limit, float reweight) -{ - int i,j,k; - STB__ARR(stb_summary_tree *) ret=NULL; - STB__ARR(stb_summary_tree2 *) all=NULL; - - // first clone the tree so we can manipulate it - stb_summary_tree2 *t = stb__summarize_clone((stb_summary_tree *) tree); - if (reweight < 1) reweight = 1; - - // now compute how far up the tree each node would get pushed - // there's no value in pushing a node up to an empty node with - // only one child, so we keep pushing it up - stb__summarize_compute_targets(NULL, t, reweight, 1); - - all = stb__summarize_make_array(all, t); - - // now we want to iteratively find the smallest 'makes_target_weight', - // update that, and then fix all the others (which will be all descendents) - // to do this efficiently, we need a heap or a sorted binary tree - // what we have is an array. maybe we can insertion sort the array? - stb__summarysort(all, stb_arr_len(all)); - - for (i=0; i < stb_arr_len(all) - limit; ++i) { - stb_summary_tree2 *src, *dest; - src = all[i]; - dest = all[i]->target; - if (src->makes_target_weight == 0) continue; - assert(dest != NULL); - - for (k=0; k < stb_arr_len(all); ++k) - if (all[k] == dest) - break; - assert(k != stb_arr_len(all)); - assert(i < k); - - // move weight from all[i] to target - src->weight = dest->makes_target_weight; - src->weight = 0; - src->makes_target_weight = 0; - // recompute effect of other descendents - for (j=0; j < stb_arr_len(dest->targeters); ++j) { - if (dest->targeters[j]->weight) { - dest->targeters[j]->makes_target_weight = dest->weight + dest->targeters[j]->weight_at_target; - assert(dest->targeters[j]->makes_target_weight <= dest->weight_with_all_children); - } - } - STB_(stb__summarysort,_ins_sort)(all+i, stb_arr_len(all)-i); - } - // now the elements in [ i..stb_arr_len(all) ) are the relevant ones - for (; i < stb_arr_len(all); ++i) - stb_arr_push(ret, all[i]->original); - - // now free all our temp data - for (i=0; i < stb_arr_len(all); ++i) { - stb_arr_free(all[i]->children); - free(all[i]); - } - stb_arr_free(all); - return ret; -} -#endif - -#endif diff --git a/impeller/third_party/stb/stb/tests/stb.dsp b/impeller/third_party/stb/stb/tests/stb.dsp deleted file mode 100644 index 68c18069e3f48..0000000000000 --- a/impeller/third_party/stb/stb/tests/stb.dsp +++ /dev/null @@ -1,192 +0,0 @@ -# Microsoft Developer Studio Project File - Name="stb" - Package Owner=<4> -# Microsoft Developer Studio Generated Build File, Format Version 6.00 -# ** DO NOT EDIT ** - -# TARGTYPE "Win32 (x86) Console Application" 0x0103 - -CFG=stb - Win32 Debug -!MESSAGE This is not a valid makefile. To build this project using NMAKE, -!MESSAGE use the Export Makefile command and run -!MESSAGE -!MESSAGE NMAKE /f "stb.mak". -!MESSAGE -!MESSAGE You can specify a configuration when running NMAKE -!MESSAGE by defining the macro CFG on the command line. For example: -!MESSAGE -!MESSAGE NMAKE /f "stb.mak" CFG="stb - Win32 Debug" -!MESSAGE -!MESSAGE Possible choices for configuration are: -!MESSAGE -!MESSAGE "stb - Win32 Release" (based on "Win32 (x86) Console Application") -!MESSAGE "stb - Win32 Debug" (based on "Win32 (x86) Console Application") -!MESSAGE - -# Begin Project -# PROP AllowPerConfigDependencies 0 -# PROP Scc_ProjName "" -# PROP Scc_LocalPath "" -CPP=cl.exe -RSC=rc.exe - -!IF "$(CFG)" == "stb - Win32 Release" - -# PROP BASE Use_MFC 0 -# PROP BASE Use_Debug_Libraries 0 -# PROP BASE Output_Dir "Release" -# PROP BASE Intermediate_Dir "Release" -# PROP BASE Target_Dir "" -# PROP Use_MFC 0 -# PROP Use_Debug_Libraries 0 -# PROP Output_Dir "Release" -# PROP Intermediate_Dir "Release" -# PROP Ignore_Export_Lib 0 -# PROP Target_Dir "" -# ADD BASE CPP /nologo /W3 /GX /O2 /D "WIN32" /D "NDEBUG" /D "_CONSOLE" /D "_MBCS" /YX /FD /c -# ADD CPP /nologo /G6 /MT /W3 /GX /Z7 /O2 /Ob2 /I ".." /D "WIN32" /D "NDEBUG" /D "_CONSOLE" /D "_MBCS" /D "TT_TEST" /FD /c -# ADD BASE RSC /l 0x409 /d "NDEBUG" -# ADD RSC /l 0x409 /d "NDEBUG" -BSC32=bscmake.exe -# ADD BASE BSC32 /nologo -# ADD BSC32 /nologo -LINK32=link.exe -# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:console /machine:I386 -# ADD LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:console /debug /machine:I386 - -!ELSEIF "$(CFG)" == "stb - Win32 Debug" - -# PROP BASE Use_MFC 0 -# PROP BASE Use_Debug_Libraries 1 -# PROP BASE Output_Dir "Debug" -# PROP BASE Intermediate_Dir "Debug" -# PROP BASE Target_Dir "" -# PROP Use_MFC 0 -# PROP Use_Debug_Libraries 1 -# PROP Output_Dir "Debug" -# PROP Intermediate_Dir "Debug\stb" -# PROP Ignore_Export_Lib 0 -# PROP Target_Dir "" -# ADD BASE CPP /nologo /W3 /Gm /GX /ZI /Od /D "WIN32" /D "_DEBUG" /D "_CONSOLE" /D "_MBCS" /YX /FD /GZ /c -# ADD CPP /nologo /MTd /W3 /GX /Zi /Od /I ".." /D "WIN32" /D "_DEBUG" /D "_CONSOLE" /D "_MBCS" /D "MAIN_TEST" /FR /FD /GZ /c -# SUBTRACT CPP /YX -# ADD BASE RSC /l 0x409 /d "_DEBUG" -# ADD RSC /l 0x409 /d "_DEBUG" -BSC32=bscmake.exe -# ADD BASE BSC32 /nologo -# ADD BSC32 /nologo -LINK32=link.exe -# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:console /debug /machine:I386 /pdbtype:sept -# ADD LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:console /incremental:no /debug /machine:I386 /pdbtype:sept -# SUBTRACT LINK32 /force - -!ENDIF - -# Begin Target - -# Name "stb - Win32 Release" -# Name "stb - Win32 Debug" -# Begin Source File - -SOURCE=..\docs\other_libs.md -# End Source File -# Begin Source File - -SOURCE=.\stb.c -# End Source File -# Begin Source File - -SOURCE=..\stb.h -# End Source File -# Begin Source File - -SOURCE=..\stb_c_lexer.h -# End Source File -# Begin Source File - -SOURCE=..\stb_divide.h -# End Source File -# Begin Source File - -SOURCE=..\stb_dxt.h -# End Source File -# Begin Source File - -SOURCE=..\stb_easy_font.h -# End Source File -# Begin Source File - -SOURCE=..\stb_herringbone_wang_tile.h -# End Source File -# Begin Source File - -SOURCE=..\stb_image.h -# End Source File -# Begin Source File - -SOURCE=..\stb_image_resize.h -# End Source File -# Begin Source File - -SOURCE=..\stb_image_write.h -# End Source File -# Begin Source File - -SOURCE=..\stb_leakcheck.h -# End Source File -# Begin Source File - -SOURCE=..\stb_malloc.h -# End Source File -# Begin Source File - -SOURCE=..\stb_perlin.h -# End Source File -# Begin Source File - -SOURCE=..\stb_rect_pack.h -# End Source File -# Begin Source File - -SOURCE=..\stb_textedit.h -# End Source File -# Begin Source File - -SOURCE=..\stb_tilemap_editor.h -# End Source File -# Begin Source File - -SOURCE=..\stb_truetype.h -# End Source File -# Begin Source File - -SOURCE=..\stb_vorbis.c -# End Source File -# Begin Source File - -SOURCE=..\stb_voxel_render.h -# End Source File -# Begin Source File - -SOURCE=..\stretchy_buffer.h -# End Source File -# Begin Source File - -SOURCE=.\stretchy_buffer_test.c -# End Source File -# Begin Source File - -SOURCE=.\test_c_compilation.c -# End Source File -# Begin Source File - -SOURCE=.\test_truetype.c -# End Source File -# Begin Source File - -SOURCE=.\test_vorbis.c -# End Source File -# Begin Source File - -SOURCE=.\textedit_sample.c -# End Source File -# End Target -# End Project diff --git a/impeller/third_party/stb/stb/tests/stb.dsw b/impeller/third_party/stb/stb/tests/stb.dsw deleted file mode 100644 index f73f9b1817a15..0000000000000 --- a/impeller/third_party/stb/stb/tests/stb.dsw +++ /dev/null @@ -1,161 +0,0 @@ -Microsoft Developer Studio Workspace File, Format Version 6.00 -# WARNING: DO NOT EDIT OR DELETE THIS WORKSPACE FILE! - -############################################################################### - -Project: "c_lexer_test"=.\c_lexer_test.dsp - Package Owner=<4> - -Package=<5> -{{{ -}}} - -Package=<4> -{{{ -}}} - -############################################################################### - -Project: "herringbone"=.\herringbone.dsp - Package Owner=<4> - -Package=<5> -{{{ -}}} - -Package=<4> -{{{ -}}} - -############################################################################### - -Project: "herringbone_map"=.\herringbone_map.dsp - Package Owner=<4> - -Package=<5> -{{{ -}}} - -Package=<4> -{{{ -}}} - -############################################################################### - -Project: "image_test"=.\image_test.dsp - Package Owner=<4> - -Package=<5> -{{{ -}}} - -Package=<4> -{{{ -}}} - -############################################################################### - -Project: "make_readme"=..\tools\make_readme.dsp - Package Owner=<4> - -Package=<5> -{{{ -}}} - -Package=<4> -{{{ -}}} - -############################################################################### - -Project: "resize"=.\resize.dsp - Package Owner=<4> - -Package=<5> -{{{ -}}} - -Package=<4> -{{{ -}}} - -############################################################################### - -Project: "stb"=.\stb.dsp - Package Owner=<4> - -Package=<5> -{{{ -}}} - -Package=<4> -{{{ - Begin Project Dependency - Project_Dep_Name stb_cpp - End Project Dependency - Begin Project Dependency - Project_Dep_Name image_test - End Project Dependency - Begin Project Dependency - Project_Dep_Name stretch_test - End Project Dependency - Begin Project Dependency - Project_Dep_Name c_lexer_test - End Project Dependency -}}} - -############################################################################### - -Project: "stb_cpp"=.\stb_cpp.dsp - Package Owner=<4> - -Package=<5> -{{{ -}}} - -Package=<4> -{{{ -}}} - -############################################################################### - -Project: "stretch_test"=.\stretch_test.dsp - Package Owner=<4> - -Package=<5> -{{{ -}}} - -Package=<4> -{{{ -}}} - -############################################################################### - -Project: "unicode"=..\tools\unicode\unicode.dsp - Package Owner=<4> - -Package=<5> -{{{ -}}} - -Package=<4> -{{{ -}}} - -############################################################################### - -Project: "vorbseek"=.\vorbseek\vorbseek.dsp - Package Owner=<4> - -Package=<5> -{{{ -}}} - -Package=<4> -{{{ -}}} - -############################################################################### - -Global: - -Package=<5> -{{{ -}}} - -Package=<3> -{{{ -}}} - -############################################################################### - diff --git a/impeller/third_party/stb/stb/tests/stb_cpp.cpp b/impeller/third_party/stb/stb/tests/stb_cpp.cpp deleted file mode 100644 index 57f2cfa14bcef..0000000000000 --- a/impeller/third_party/stb/stb/tests/stb_cpp.cpp +++ /dev/null @@ -1,82 +0,0 @@ -#define WIN32_MEAN_AND_LEAN -#define WIN32_LEAN_AND_MEAN -//#include -#include -#define STB_STUA -#define STB_DEFINE -#define STB_NPTR -#define STB_ONLY -#include "stb.h" -//#include "stb_file.h" - -int count; -void c(int truth, char *error) -{ - if (!truth) { - fprintf(stderr, "Test failed: %s\n", error); - ++count; - } -} - -char *expects(stb_matcher *m, char *s, int result, int len, char *str) -{ - int res2,len2=0; - res2 = stb_lex(m, s, &len2); - c(result == res2 && len == len2, str); - return s + len; -} - -void test_lex(void) -{ - stb_matcher *m = stb_lex_matcher(); - // tok_en5 .3 20.1 20. .20 .1 - char *s = "tok_en5.3 20.1 20. .20.1"; - - stb_lex_item(m, "[a-zA-Z_][a-zA-Z0-9_]*", 1 ); - stb_lex_item(m, "[0-9]*\\.?[0-9]*" , 2 ); - stb_lex_item(m, "[\r\n\t ]+" , 3 ); - stb_lex_item(m, "." , -99 ); - s=expects(m,s,1,7, "stb_lex 1"); - s=expects(m,s,2,2, "stb_lex 2"); - s=expects(m,s,3,1, "stb_lex 3"); - s=expects(m,s,2,4, "stb_lex 4"); - s=expects(m,s,3,1, "stb_lex 5"); - s=expects(m,s,2,3, "stb_lex 6"); - s=expects(m,s,3,1, "stb_lex 7"); - s=expects(m,s,2,3, "stb_lex 8"); - s=expects(m,s,2,2, "stb_lex 9"); - s=expects(m,s,0,0, "stb_lex 10"); - stb_matcher_free(m); -} - -int main(int argc, char **argv) -{ - char *p; - p = "abcdefghijklmnopqrstuvwxyz"; - c(stb_ischar('c', p), "stb_ischar 1"); - c(stb_ischar('x', p), "stb_ischar 2"); - c(!stb_ischar('#', p), "stb_ischar 3"); - c(!stb_ischar('X', p), "stb_ischar 4"); - p = "0123456789"; - c(!stb_ischar('c', p), "stb_ischar 5"); - c(!stb_ischar('x', p), "stb_ischar 6"); - c(!stb_ischar('#', p), "stb_ischar 7"); - c(!stb_ischar('X', p), "stb_ischar 8"); - p = "#####"; - c(!stb_ischar('c', p), "stb_ischar a"); - c(!stb_ischar('x', p), "stb_ischar b"); - c(stb_ischar('#', p), "stb_ischar c"); - c(!stb_ischar('X', p), "stb_ischar d"); - p = "xXyY"; - c(!stb_ischar('c', p), "stb_ischar e"); - c(stb_ischar('x', p), "stb_ischar f"); - c(!stb_ischar('#', p), "stb_ischar g"); - c(stb_ischar('X', p), "stb_ischar h"); - - test_lex(); - - if (count) { - _getch(); - } - return 0; -} diff --git a/impeller/third_party/stb/stb/tests/stb_cpp.dsp b/impeller/third_party/stb/stb/tests/stb_cpp.dsp deleted file mode 100644 index 8bf9975aa73d1..0000000000000 --- a/impeller/third_party/stb/stb/tests/stb_cpp.dsp +++ /dev/null @@ -1,98 +0,0 @@ -# Microsoft Developer Studio Project File - Name="stb_cpp" - Package Owner=<4> -# Microsoft Developer Studio Generated Build File, Format Version 6.00 -# ** DO NOT EDIT ** - -# TARGTYPE "Win32 (x86) Console Application" 0x0103 - -CFG=stb_cpp - Win32 Debug -!MESSAGE This is not a valid makefile. To build this project using NMAKE, -!MESSAGE use the Export Makefile command and run -!MESSAGE -!MESSAGE NMAKE /f "stb_cpp.mak". -!MESSAGE -!MESSAGE You can specify a configuration when running NMAKE -!MESSAGE by defining the macro CFG on the command line. For example: -!MESSAGE -!MESSAGE NMAKE /f "stb_cpp.mak" CFG="stb_cpp - Win32 Debug" -!MESSAGE -!MESSAGE Possible choices for configuration are: -!MESSAGE -!MESSAGE "stb_cpp - Win32 Release" (based on "Win32 (x86) Console Application") -!MESSAGE "stb_cpp - Win32 Debug" (based on "Win32 (x86) Console Application") -!MESSAGE - -# Begin Project -# PROP AllowPerConfigDependencies 0 -# PROP Scc_ProjName "" -# PROP Scc_LocalPath "" -CPP=cl.exe -RSC=rc.exe - -!IF "$(CFG)" == "stb_cpp - Win32 Release" - -# PROP BASE Use_MFC 0 -# PROP BASE Use_Debug_Libraries 0 -# PROP BASE Output_Dir "Release" -# PROP BASE Intermediate_Dir "Release" -# PROP BASE Target_Dir "" -# PROP Use_MFC 0 -# PROP Use_Debug_Libraries 0 -# PROP Output_Dir "Release" -# PROP Intermediate_Dir "Release" -# PROP Target_Dir "" -# ADD BASE CPP /nologo /W3 /GX /O2 /D "WIN32" /D "NDEBUG" /D "_CONSOLE" /D "_MBCS" /YX /FD /c -# ADD CPP /nologo /MT /W3 /GX /O2 /I ".." /D "WIN32" /D "NDEBUG" /D "_CONSOLE" /D "_MBCS" /YX /FD /c -# ADD BASE RSC /l 0x409 /d "NDEBUG" -# ADD RSC /l 0x409 /d "NDEBUG" -BSC32=bscmake.exe -# ADD BASE BSC32 /nologo -# ADD BSC32 /nologo -LINK32=link.exe -# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:console /machine:I386 -# ADD LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:console /machine:I386 - -!ELSEIF "$(CFG)" == "stb_cpp - Win32 Debug" - -# PROP BASE Use_MFC 0 -# PROP BASE Use_Debug_Libraries 1 -# PROP BASE Output_Dir "Debug" -# PROP BASE Intermediate_Dir "Debug" -# PROP BASE Target_Dir "" -# PROP Use_MFC 0 -# PROP Use_Debug_Libraries 1 -# PROP Output_Dir "Debug" -# PROP Intermediate_Dir "Debug\stb_cpp" -# PROP Ignore_Export_Lib 0 -# PROP Target_Dir "" -# ADD BASE CPP /nologo /W3 /Gm /GX /ZI /Od /D "WIN32" /D "_DEBUG" /D "_CONSOLE" /D "_MBCS" /YX /FD /GZ /c -# ADD CPP /nologo /MTd /W3 /GX /Zd /Od /I ".." /D "WIN32" /D "_DEBUG" /D "_CONSOLE" /D "_MBCS" /FD /GZ /c -# SUBTRACT CPP /YX -# ADD BASE RSC /l 0x409 /d "_DEBUG" -# ADD RSC /l 0x409 /d "_DEBUG" -BSC32=bscmake.exe -# ADD BASE BSC32 /nologo -# ADD BSC32 /nologo -LINK32=link.exe -# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:console /debug /machine:I386 /pdbtype:sept -# ADD LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:console /debug /machine:I386 /pdbtype:sept - -!ENDIF - -# Begin Target - -# Name "stb_cpp - Win32 Release" -# Name "stb_cpp - Win32 Debug" -# Begin Source File - -SOURCE=.\stb_cpp.cpp -# End Source File -# Begin Source File - -SOURCE=..\stb_vorbis.c -# End Source File -# Begin Source File - -SOURCE=.\test_cpp_compilation.cpp -# End Source File -# End Target -# End Project diff --git a/impeller/third_party/stb/stb/tests/stretch_test.c b/impeller/third_party/stb/stb/tests/stretch_test.c deleted file mode 100644 index 8caf43f645cdf..0000000000000 --- a/impeller/third_party/stb/stb/tests/stretch_test.c +++ /dev/null @@ -1,28 +0,0 @@ -// check that stb_truetype compiles with no stb_rect_pack.h -#define STB_TRUETYPE_IMPLEMENTATION -#include "stb_truetype.h" - -#include "stretchy_buffer.h" -#include - -int main(int arg, char **argv) -{ - int i; - int *arr = NULL; - - for (i=0; i < 1000000; ++i) - sb_push(arr, i); - - assert(sb_count(arr) == 1000000); - for (i=0; i < 1000000; ++i) - assert(arr[i] == i); - - sb_free(arr); - arr = NULL; - - for (i=0; i < 1000; ++i) - sb_add(arr, 1000); - assert(sb_count(arr) == 1000000); - - return 0; -} \ No newline at end of file diff --git a/impeller/third_party/stb/stb/tests/stretch_test.dsp b/impeller/third_party/stb/stb/tests/stretch_test.dsp deleted file mode 100644 index dd0442c26c57f..0000000000000 --- a/impeller/third_party/stb/stb/tests/stretch_test.dsp +++ /dev/null @@ -1,89 +0,0 @@ -# Microsoft Developer Studio Project File - Name="stretch_test" - Package Owner=<4> -# Microsoft Developer Studio Generated Build File, Format Version 6.00 -# ** DO NOT EDIT ** - -# TARGTYPE "Win32 (x86) Console Application" 0x0103 - -CFG=stretch_test - Win32 Debug -!MESSAGE This is not a valid makefile. To build this project using NMAKE, -!MESSAGE use the Export Makefile command and run -!MESSAGE -!MESSAGE NMAKE /f "stretch_test.mak". -!MESSAGE -!MESSAGE You can specify a configuration when running NMAKE -!MESSAGE by defining the macro CFG on the command line. For example: -!MESSAGE -!MESSAGE NMAKE /f "stretch_test.mak" CFG="stretch_test - Win32 Debug" -!MESSAGE -!MESSAGE Possible choices for configuration are: -!MESSAGE -!MESSAGE "stretch_test - Win32 Release" (based on "Win32 (x86) Console Application") -!MESSAGE "stretch_test - Win32 Debug" (based on "Win32 (x86) Console Application") -!MESSAGE - -# Begin Project -# PROP AllowPerConfigDependencies 0 -# PROP Scc_ProjName "" -# PROP Scc_LocalPath "" -CPP=cl.exe -RSC=rc.exe - -!IF "$(CFG)" == "stretch_test - Win32 Release" - -# PROP BASE Use_MFC 0 -# PROP BASE Use_Debug_Libraries 0 -# PROP BASE Output_Dir "Release" -# PROP BASE Intermediate_Dir "Release" -# PROP BASE Target_Dir "" -# PROP Use_MFC 0 -# PROP Use_Debug_Libraries 0 -# PROP Output_Dir "Release" -# PROP Intermediate_Dir "Release" -# PROP Target_Dir "" -# ADD BASE CPP /nologo /W3 /GX /O2 /D "WIN32" /D "NDEBUG" /D "_CONSOLE" /D "_MBCS" /YX /FD /c -# ADD CPP /nologo /W3 /GX /O2 /I "..\.." /I ".." /D "WIN32" /D "NDEBUG" /D "_CONSOLE" /D "_MBCS" /D "TT_TEST" /YX /FD /c -# ADD BASE RSC /l 0x409 /d "NDEBUG" -# ADD RSC /l 0x409 /d "NDEBUG" -BSC32=bscmake.exe -# ADD BASE BSC32 /nologo -# ADD BSC32 /nologo -LINK32=link.exe -# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:console /machine:I386 -# ADD LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:console /machine:I386 - -!ELSEIF "$(CFG)" == "stretch_test - Win32 Debug" - -# PROP BASE Use_MFC 0 -# PROP BASE Use_Debug_Libraries 1 -# PROP BASE Output_Dir "stretch_test___Win32_Debug" -# PROP BASE Intermediate_Dir "stretch_test___Win32_Debug" -# PROP BASE Target_Dir "" -# PROP Use_MFC 0 -# PROP Use_Debug_Libraries 1 -# PROP Output_Dir "Debug" -# PROP Intermediate_Dir "Debug\stretch_test" -# PROP Target_Dir "" -# ADD BASE CPP /nologo /W3 /Gm /GX /ZI /Od /D "WIN32" /D "_DEBUG" /D "_CONSOLE" /D "_MBCS" /YX /FD /GZ /c -# ADD CPP /nologo /W3 /Gm /GX /ZI /Od /I ".." /D "WIN32" /D "_DEBUG" /D "_CONSOLE" /D "_MBCS" /FD /GZ /c -# SUBTRACT CPP /YX -# ADD BASE RSC /l 0x409 /d "_DEBUG" -# ADD RSC /l 0x409 /d "_DEBUG" -BSC32=bscmake.exe -# ADD BASE BSC32 /nologo -# ADD BSC32 /nologo -LINK32=link.exe -# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:console /debug /machine:I386 /pdbtype:sept -# ADD LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:console /debug /machine:I386 /pdbtype:sept - -!ENDIF - -# Begin Target - -# Name "stretch_test - Win32 Release" -# Name "stretch_test - Win32 Debug" -# Begin Source File - -SOURCE=.\stretch_test.c -# End Source File -# End Target -# End Project diff --git a/impeller/third_party/stb/stb/tests/stretchy_buffer_test.c b/impeller/third_party/stb/stb/tests/stretchy_buffer_test.c deleted file mode 100644 index 5ced5bc3abdc2..0000000000000 --- a/impeller/third_party/stb/stb/tests/stretchy_buffer_test.c +++ /dev/null @@ -1 +0,0 @@ -#include "stretchy_buffer.h" \ No newline at end of file diff --git a/impeller/third_party/stb/stb/tests/test_c_compilation.c b/impeller/third_party/stb/stb/tests/test_c_compilation.c deleted file mode 100644 index de25330ae6432..0000000000000 --- a/impeller/third_party/stb/stb/tests/test_c_compilation.c +++ /dev/null @@ -1,30 +0,0 @@ -#define STB_PERLIN_IMPLEMENTATION -#define STB_IMAGE_WRITE_IMPLEMENTATION -#define STB_DXT_IMPLEMENATION -#define STB_C_LEXER_IMPLEMENTATIOn -#define STB_DIVIDE_IMPLEMENTATION -#define STB_IMAGE_IMPLEMENTATION -#define STB_HERRINGBONE_WANG_TILE_IMEPLEMENTATIOn -#define STB_IMAGE_RESIZE_IMPLEMENTATION -#define STB_RECT_PACK_IMPLEMENTATION -#define STB_VOXEL_RENDER_IMPLEMENTATION -#define STB_EASY_FONT_IMPLEMENTATION - -#include "stb_easy_font.h" -#include "stb_herringbone_wang_tile.h" -#include "stb_image.h" -#include "stb_image_write.h" -#include "stb_perlin.h" -#include "stb_dxt.h" -#include "stb_c_lexer.h" -#include "stb_divide.h" -#include "stb_image_resize.h" -#include "stb_rect_pack.h" - -#define STBVOX_CONFIG_MODE 1 -#include "stb_voxel_render.h" - -#define STBTE_DRAW_RECT(x0,y0,x1,y1,color) 0 -#define STBTE_DRAW_TILE(x,y,id,highlight,data) 0 -#define STB_TILEMAP_EDITOR_IMPLEMENTATION -#include "stb_tilemap_editor.h" diff --git a/impeller/third_party/stb/stb/tests/test_cpp_compilation.cpp b/impeller/third_party/stb/stb/tests/test_cpp_compilation.cpp deleted file mode 100644 index 97bd2c2b94ede..0000000000000 --- a/impeller/third_party/stb/stb/tests/test_cpp_compilation.cpp +++ /dev/null @@ -1,137 +0,0 @@ -#define STB_TRUETYPE_IMPLEMENTATION -#define STB_PERLIN_IMPLEMENTATION -#define STB_IMAGE_WRITE_IMPLEMENTATION -#define STB_DXT_IMPLEMENATION -#define STB_C_LEXER_IMPLEMENTATIOn -#define STB_DIVIDE_IMPLEMENTATION -#define STB_IMAGE_IMPLEMENTATION -#define STB_HERRINGBONE_WANG_TILE_IMPLEMENTATION -#define STB_RECT_PACK_IMPLEMENTATION -#define STB_VOXEL_RENDER_IMPLEMENTATION - -#define STBI_MALLOC my_malloc -#define STBI_FREE my_free -#define STBI_REALLOC my_realloc - -void *my_malloc(size_t) { return 0; } -void *my_realloc(void *, size_t) { return 0; } -void my_free(void *) { } - -#include "stb_image.h" -#include "stb_rect_pack.h" -#include "stb_truetype.h" -#include "stb_image_write.h" -#include "stb_perlin.h" -#include "stb_dxt.h" -#include "stb_c_lexer.h" -#include "stb_divide.h" -#include "stb_herringbone_wang_tile.h" - -#define STBVOX_CONFIG_MODE 1 -#include "stb_voxel_render.h" - -#define STBTE_DRAW_RECT(x0,y0,x1,y1,color) do ; while(0) -#define STBTE_DRAW_TILE(x,y,id,highlight,data) do ; while(0) -#define STB_TILEMAP_EDITOR_IMPLEMENTATION -#include "stb_tilemap_editor.h" - -#include "stb_easy_font.h" - -#define STB_LEAKCHECK_IMPLEMENTATION -#include "stb_leakcheck.h" - -#define STB_IMAGE_RESIZE_IMPLEMENTATION -#include "stb_image_resize.h" - -#include "stretchy_buffer.h" - - - -//////////////////////////////////////////////////////////// -// -// text edit - -#include -#include // memmove -#include // isspace - -#define STB_TEXTEDIT_CHARTYPE char -#define STB_TEXTEDIT_STRING text_control - -// get the base type -#include "stb_textedit.h" - -// define our editor structure -typedef struct -{ - char *string; - int stringlen; - STB_TexteditState state; -} text_control; - -// define the functions we need -void layout_func(StbTexteditRow *row, STB_TEXTEDIT_STRING *str, int start_i) -{ - int remaining_chars = str->stringlen - start_i; - row->num_chars = remaining_chars > 20 ? 20 : remaining_chars; // should do real word wrap here - row->x0 = 0; - row->x1 = 20; // need to account for actual size of characters - row->baseline_y_delta = 1.25; - row->ymin = -1; - row->ymax = 0; -} - -int delete_chars(STB_TEXTEDIT_STRING *str, int pos, int num) -{ - memmove(&str->string[pos], &str->string[pos+num], str->stringlen - (pos+num)); - str->stringlen -= num; - return 1; // always succeeds -} - -int insert_chars(STB_TEXTEDIT_STRING *str, int pos, STB_TEXTEDIT_CHARTYPE *newtext, int num) -{ - str->string = (char *) realloc(str->string, str->stringlen + num); - memmove(&str->string[pos+num], &str->string[pos], str->stringlen - pos); - memcpy(&str->string[pos], newtext, num); - str->stringlen += num; - return 1; // always succeeds -} - -// define all the #defines needed - -#define KEYDOWN_BIT 0x80000000 - -#define STB_TEXTEDIT_STRINGLEN(tc) ((tc)->stringlen) -#define STB_TEXTEDIT_LAYOUTROW layout_func -#define STB_TEXTEDIT_GETWIDTH(tc,n,i) (1) // quick hack for monospaced -#define STB_TEXTEDIT_KEYTOTEXT(key) (((key) & KEYDOWN_BIT) ? 0 : (key)) -#define STB_TEXTEDIT_GETCHAR(tc,i) ((tc)->string[i]) -#define STB_TEXTEDIT_NEWLINE '\n' -#define STB_TEXTEDIT_IS_SPACE(ch) isspace(ch) -#define STB_TEXTEDIT_DELETECHARS delete_chars -#define STB_TEXTEDIT_INSERTCHARS insert_chars - -#define STB_TEXTEDIT_K_SHIFT 0x40000000 -#define STB_TEXTEDIT_K_CONTROL 0x20000000 -#define STB_TEXTEDIT_K_LEFT (KEYDOWN_BIT | 1) // actually use VK_LEFT, SDLK_LEFT, etc -#define STB_TEXTEDIT_K_RIGHT (KEYDOWN_BIT | 2) // VK_RIGHT -#define STB_TEXTEDIT_K_UP (KEYDOWN_BIT | 3) // VK_UP -#define STB_TEXTEDIT_K_DOWN (KEYDOWN_BIT | 4) // VK_DOWN -#define STB_TEXTEDIT_K_LINESTART (KEYDOWN_BIT | 5) // VK_HOME -#define STB_TEXTEDIT_K_LINEEND (KEYDOWN_BIT | 6) // VK_END -#define STB_TEXTEDIT_K_TEXTSTART (STB_TEXTEDIT_K_LINESTART | STB_TEXTEDIT_K_CONTROL) -#define STB_TEXTEDIT_K_TEXTEND (STB_TEXTEDIT_K_LINEEND | STB_TEXTEDIT_K_CONTROL) -#define STB_TEXTEDIT_K_DELETE (KEYDOWN_BIT | 7) // VK_DELETE -#define STB_TEXTEDIT_K_BACKSPACE (KEYDOWN_BIT | 8) // VK_BACKSPACE -#define STB_TEXTEDIT_K_UNDO (KEYDOWN_BIT | STB_TEXTEDIT_K_CONTROL | 'z') -#define STB_TEXTEDIT_K_REDO (KEYDOWN_BIT | STB_TEXTEDIT_K_CONTROL | 'y') -#define STB_TEXTEDIT_K_INSERT (KEYDOWN_BIT | 9) // VK_INSERT -#define STB_TEXTEDIT_K_WORDLEFT (STB_TEXTEDIT_K_LEFT | STB_TEXTEDIT_K_CONTROL) -#define STB_TEXTEDIT_K_WORDRIGHT (STB_TEXTEDIT_K_RIGHT | STB_TEXTEDIT_K_CONTROL) -#define STB_TEXTEDIT_K_PGUP (KEYDOWN_BIT | 10) // VK_PGUP -- not implemented -#define STB_TEXTEDIT_K_PGDOWN (KEYDOWN_BIT | 11) // VK_PGDOWN -- not implemented - -#define STB_TEXTEDIT_IMPLEMENTATION -#include "stb_textedit.h" - - diff --git a/impeller/third_party/stb/stb/tests/test_truetype.c b/impeller/third_party/stb/stb/tests/test_truetype.c deleted file mode 100644 index 4b7815232fc98..0000000000000 --- a/impeller/third_party/stb/stb/tests/test_truetype.c +++ /dev/null @@ -1,95 +0,0 @@ -#include "stb_rect_pack.h" -#define STB_TRUETYPE_IMPLEMENTATION -#include "stb_truetype.h" -#include "stb_image_write.h" - -#include - -char ttf_buffer[1<<25]; -unsigned char output[512*100]; - -#ifdef TT_TEST - -void debug(void) -{ - stbtt_fontinfo font; - fread(ttf_buffer, 1, 1<<25, fopen("c:/x/lm/LiberationMono-Regular.ttf", "rb")); - stbtt_InitFont(&font, ttf_buffer, 0); - - stbtt_MakeGlyphBitmap(&font, output, 6, 9, 512, 5.172414E-03f, 5.172414E-03f, 54); -} - -#define BITMAP_W 256 -#define BITMAP_H 512 -unsigned char temp_bitmap[BITMAP_H][BITMAP_W]; -stbtt_bakedchar cdata[256*2]; // ASCII 32..126 is 95 glyphs -stbtt_packedchar pdata[256*2]; - -int main(int argc, char **argv) -{ - stbtt_fontinfo font; - unsigned char *bitmap; - int w,h,i,j,c = (argc > 1 ? atoi(argv[1]) : 34807), s = (argc > 2 ? atoi(argv[2]) : 32); - - //debug(); - - // @TODO: why is minglui.ttc failing? - fread(ttf_buffer, 1, 1<<25, fopen(argc > 3 ? argv[3] : "c:/windows/fonts/mingliu.ttc", "rb")); - - //fread(ttf_buffer, 1, 1<<25, fopen(argc > 3 ? argv[3] : "c:/x/DroidSansMono.ttf", "rb")); - { - static stbtt_pack_context pc; - static stbtt_packedchar cd[256]; - static unsigned char atlas[1024*1024]; - - stbtt_PackBegin(&pc, atlas, 1024,1024,1024,1,NULL); - stbtt_PackFontRange(&pc, ttf_buffer, 0, 32.0, 0, 256, cd); - stbtt_PackEnd(&pc); - } - -#if 0 - stbtt_BakeFontBitmap(ttf_buffer,stbtt_GetFontOffsetForIndex(ttf_buffer,0), 40.0, temp_bitmap[0],BITMAP_W,BITMAP_H, 32,96, cdata); // no guarantee this fits! - stbi_write_png("fonttest1.png", BITMAP_W, BITMAP_H, 1, temp_bitmap, 0); - - { - stbtt_pack_context pc; - stbtt_PackBegin(&pc, temp_bitmap[0], BITMAP_W, BITMAP_H, 0, 1, NULL); - stbtt_PackFontRange(&pc, ttf_buffer, 0, 20.0, 32, 95, pdata); - stbtt_PackFontRange(&pc, ttf_buffer, 0, 20.0, 0xa0, 0x100-0xa0, pdata); - stbtt_PackEnd(&pc); - stbi_write_png("fonttest2.png", BITMAP_W, BITMAP_H, 1, temp_bitmap, 0); - } - - { - stbtt_pack_context pc; - stbtt_pack_range pr[2]; - stbtt_PackBegin(&pc, temp_bitmap[0], BITMAP_W, BITMAP_H, 0, 1, NULL); - - pr[0].chardata_for_range = pdata; - pr[0].first_unicode_char_in_range = 32; - pr[0].num_chars_in_range = 95; - pr[0].font_size = 20.0f; - pr[1].chardata_for_range = pdata+256; - pr[1].first_unicode_char_in_range = 0xa0; - pr[1].num_chars_in_range = 0x100 - 0xa0; - pr[1].font_size = 20.0f; - - stbtt_PackSetOversampling(&pc, 2, 2); - stbtt_PackFontRanges(&pc, ttf_buffer, 0, pr, 2); - stbtt_PackEnd(&pc); - stbi_write_png("fonttest3.png", BITMAP_W, BITMAP_H, 1, temp_bitmap, 0); - } - return 0; -#endif - - stbtt_InitFont(&font, ttf_buffer, stbtt_GetFontOffsetForIndex(ttf_buffer,0)); - bitmap = stbtt_GetCodepointBitmap(&font, 0,stbtt_ScaleForPixelHeight(&font, (float)s), c, &w, &h, 0,0); - - for (j=0; j < h; ++j) { - for (i=0; i < w; ++i) - putchar(" .:ioVM@"[bitmap[j*w+i]>>5]); - putchar('\n'); - } - return 0; -} -#endif diff --git a/impeller/third_party/stb/stb/tests/test_vorbis.c b/impeller/third_party/stb/stb/tests/test_vorbis.c deleted file mode 100644 index d54ed23171fda..0000000000000 --- a/impeller/third_party/stb/stb/tests/test_vorbis.c +++ /dev/null @@ -1,18 +0,0 @@ -#define STB_VORBIS_HEADER_ONLY -#include "stb_vorbis.c" -#include "stb.h" - -extern void stb_vorbis_dumpmem(void); - -#ifdef VORBIS_TEST -int main(int argc, char **argv) -{ - size_t memlen; - unsigned char *mem = stb_fileu("c:/x/sketch008.ogg", &memlen); - int chan, samplerate; - short *output; - int samples = stb_vorbis_decode_memory(mem, memlen, &chan, &samplerate, &output); - stb_filewrite("c:/x/sketch008.raw", output, samples*4); - return 0; -} -#endif diff --git a/impeller/third_party/stb/stb/tests/textedit_sample.c b/impeller/third_party/stb/stb/tests/textedit_sample.c deleted file mode 100644 index 04dc31a887bd3..0000000000000 --- a/impeller/third_party/stb/stb/tests/textedit_sample.c +++ /dev/null @@ -1,84 +0,0 @@ -// I haven't actually tested this yet, this is just to make sure it compiles - -#include -#include // memmove -#include // isspace - -#define STB_TEXTEDIT_CHARTYPE char -#define STB_TEXTEDIT_STRING text_control - -// get the base type -#include "stb_textedit.h" - -// define our editor structure -typedef struct -{ - char *string; - int stringlen; - STB_TexteditState state; -} text_control; - -// define the functions we need -void layout_func(StbTexteditRow *row, STB_TEXTEDIT_STRING *str, int start_i) -{ - int remaining_chars = str->stringlen - start_i; - row->num_chars = remaining_chars > 20 ? 20 : remaining_chars; // should do real word wrap here - row->x0 = 0; - row->x1 = 20; // need to account for actual size of characters - row->baseline_y_delta = 1.25; - row->ymin = -1; - row->ymax = 0; -} - -int delete_chars(STB_TEXTEDIT_STRING *str, int pos, int num) -{ - memmove(&str->string[pos], &str->string[pos+num], str->stringlen - (pos+num)); - str->stringlen -= num; - return 1; // always succeeds -} - -int insert_chars(STB_TEXTEDIT_STRING *str, int pos, STB_TEXTEDIT_CHARTYPE *newtext, int num) -{ - str->string = realloc(str->string, str->stringlen + num); - memmove(&str->string[pos+num], &str->string[pos], str->stringlen - pos); - memcpy(&str->string[pos], newtext, num); - str->stringlen += num; - return 1; // always succeeds -} - -// define all the #defines needed - -#define KEYDOWN_BIT 0x80000000 - -#define STB_TEXTEDIT_STRINGLEN(tc) ((tc)->stringlen) -#define STB_TEXTEDIT_LAYOUTROW layout_func -#define STB_TEXTEDIT_GETWIDTH(tc,n,i) (1) // quick hack for monospaced -#define STB_TEXTEDIT_KEYTOTEXT(key) (((key) & KEYDOWN_BIT) ? 0 : (key)) -#define STB_TEXTEDIT_GETCHAR(tc,i) ((tc)->string[i]) -#define STB_TEXTEDIT_NEWLINE '\n' -#define STB_TEXTEDIT_IS_SPACE(ch) isspace(ch) -#define STB_TEXTEDIT_DELETECHARS delete_chars -#define STB_TEXTEDIT_INSERTCHARS insert_chars - -#define STB_TEXTEDIT_K_SHIFT 0x40000000 -#define STB_TEXTEDIT_K_CONTROL 0x20000000 -#define STB_TEXTEDIT_K_LEFT (KEYDOWN_BIT | 1) // actually use VK_LEFT, SDLK_LEFT, etc -#define STB_TEXTEDIT_K_RIGHT (KEYDOWN_BIT | 2) // VK_RIGHT -#define STB_TEXTEDIT_K_UP (KEYDOWN_BIT | 3) // VK_UP -#define STB_TEXTEDIT_K_DOWN (KEYDOWN_BIT | 4) // VK_DOWN -#define STB_TEXTEDIT_K_LINESTART (KEYDOWN_BIT | 5) // VK_HOME -#define STB_TEXTEDIT_K_LINEEND (KEYDOWN_BIT | 6) // VK_END -#define STB_TEXTEDIT_K_TEXTSTART (STB_TEXTEDIT_K_LINESTART | STB_TEXTEDIT_K_CONTROL) -#define STB_TEXTEDIT_K_TEXTEND (STB_TEXTEDIT_K_LINEEND | STB_TEXTEDIT_K_CONTROL) -#define STB_TEXTEDIT_K_DELETE (KEYDOWN_BIT | 7) // VK_DELETE -#define STB_TEXTEDIT_K_BACKSPACE (KEYDOWN_BIT | 8) // VK_BACKSPACE -#define STB_TEXTEDIT_K_UNDO (KEYDOWN_BIT | STB_TEXTEDIT_K_CONTROL | 'z') -#define STB_TEXTEDIT_K_REDO (KEYDOWN_BIT | STB_TEXTEDIT_K_CONTROL | 'y') -#define STB_TEXTEDIT_K_INSERT (KEYDOWN_BIT | 9) // VK_INSERT -#define STB_TEXTEDIT_K_WORDLEFT (STB_TEXTEDIT_K_LEFT | STB_TEXTEDIT_K_CONTROL) -#define STB_TEXTEDIT_K_WORDRIGHT (STB_TEXTEDIT_K_RIGHT | STB_TEXTEDIT_K_CONTROL) -#define STB_TEXTEDIT_K_PGUP (KEYDOWN_BIT | 10) // VK_PGUP -- not implemented -#define STB_TEXTEDIT_K_PGDOWN (KEYDOWN_BIT | 11) // VK_PGDOWN -- not implemented - -#define STB_TEXTEDIT_IMPLEMENTATION -#include "stb_textedit.h" diff --git a/impeller/third_party/stb/stb/tests/tilemap_editor_integration_example.c b/impeller/third_party/stb/stb/tests/tilemap_editor_integration_example.c deleted file mode 100644 index ea5dee775c87e..0000000000000 --- a/impeller/third_party/stb/stb/tests/tilemap_editor_integration_example.c +++ /dev/null @@ -1,193 +0,0 @@ -// This isn't compilable as-is, as it was extracted from a working -// integration-in-a-game and makes reference to symbols from that game. - -#include -#include -#include "game.h" -#include "SDL.h" -#include "stb_tilemap_editor.h" - -extern void editor_draw_tile(int x, int y, unsigned short tile, int mode, float *props); -extern void editor_draw_rect(int x0, int y0, int x1, int y1, unsigned char r, unsigned char g, unsigned char b); - -static int is_platform(short *tiles); -static unsigned int prop_type(int n, short *tiles); -static char *prop_name(int n, short *tiles); -static float prop_range(int n, short *tiles, int is_max); -static int allow_link(short *src, short *dest); - -#define STBTE_MAX_PROPERTIES 8 - -#define STBTE_PROP_TYPE(n, tiledata, p) prop_type(n,tiledata) -#define STBTE_PROP_NAME(n, tiledata, p) prop_name(n,tiledata) -#define STBTE_PROP_MIN(n, tiledata, p) prop_range(n,tiledata,0) -#define STBTE_PROP_MAX(n, tiledata, p) prop_range(n,tiledata,1) -#define STBTE_PROP_FLOAT_SCALE(n,td,p) (0.1) - -#define STBTE_ALLOW_LINK(srctile, srcprop, desttile, destprop) \ - allow_link(srctile, desttile) - -#define STBTE_LINK_COLOR(srctile, srcprop, desttile, destprop) \ - (is_platform(srctile) ? 0xff80ff : 0x808040) - -#define STBTE_DRAW_RECT(x0,y0,x1,y1,c) \ - editor_draw_rect(x0,y0,x1,y1,(c)>>16,((c)>>8)&255,(c)&255) - -#define STBTE_DRAW_TILE(x,y,id,highlight,props) \ - editor_draw_tile(x,y,id,highlight,props) - - - -#define STB_TILEMAP_EDITOR_IMPLEMENTATION -#include "stb_tilemap_editor.h" - -stbte_tilemap *edit_map; - -void editor_key(enum stbte_action act) -{ - stbte_action(edit_map, act); -} - -void editor_process_sdl_event(SDL_Event *e) -{ - switch (e->type) { - case SDL_MOUSEMOTION: - case SDL_MOUSEBUTTONDOWN: - case SDL_MOUSEBUTTONUP: - case SDL_MOUSEWHEEL: - stbte_mouse_sdl(edit_map, e, 1.0f/editor_scale,1.0f/editor_scale,0,0); - break; - - case SDL_KEYDOWN: - if (in_editor) { - switch (e->key.keysym.sym) { - case SDLK_RIGHT: editor_key(STBTE_scroll_right); break; - case SDLK_LEFT : editor_key(STBTE_scroll_left ); break; - case SDLK_UP : editor_key(STBTE_scroll_up ); break; - case SDLK_DOWN : editor_key(STBTE_scroll_down ); break; - } - switch (e->key.keysym.scancode) { - case SDL_SCANCODE_S: editor_key(STBTE_tool_select); break; - case SDL_SCANCODE_B: editor_key(STBTE_tool_brush ); break; - case SDL_SCANCODE_E: editor_key(STBTE_tool_erase ); break; - case SDL_SCANCODE_R: editor_key(STBTE_tool_rectangle ); break; - case SDL_SCANCODE_I: editor_key(STBTE_tool_eyedropper); break; - case SDL_SCANCODE_L: editor_key(STBTE_tool_link); break; - case SDL_SCANCODE_G: editor_key(STBTE_act_toggle_grid); break; - } - if ((e->key.keysym.mod & KMOD_CTRL) && !(e->key.keysym.mod & ~KMOD_CTRL)) { - switch (e->key.keysym.scancode) { - case SDL_SCANCODE_X: editor_key(STBTE_act_cut ); break; - case SDL_SCANCODE_C: editor_key(STBTE_act_copy ); break; - case SDL_SCANCODE_V: editor_key(STBTE_act_paste); break; - case SDL_SCANCODE_Z: editor_key(STBTE_act_undo ); break; - case SDL_SCANCODE_Y: editor_key(STBTE_act_redo ); break; - } - } - } - break; - } -} - -void editor_init(void) -{ - int i; - edit_map = stbte_create_map(20,14, 8, 16,16, 100); - - stbte_set_background_tile(edit_map, T_empty); - - for (i=0; i < T__num_types; ++i) { - if (i != T_reserved1 && i != T_entry && i != T_doorframe) - stbte_define_tile(edit_map, 0+i, 1, "Background"); - } - stbte_define_tile(edit_map, 256+O_player , 8, "Char"); - stbte_define_tile(edit_map, 256+O_robot , 8, "Char"); - for (i=O_lockeddoor; i < O__num_types-2; ++i) - if (i == O_platform || i == O_vplatform) - stbte_define_tile(edit_map, 256+i, 4, "Object"); - else - stbte_define_tile(edit_map, 256+i, 2, "Object"); - - //stbte_set_layername(edit_map, 0, "background"); - //stbte_set_layername(edit_map, 1, "objects"); - //stbte_set_layername(edit_map, 2, "platforms"); - //stbte_set_layername(edit_map, 3, "characters"); -} - -static int is_platform(short *tiles) -{ - // platforms are only on layer #2 - return tiles[2] == 256 + O_platform || tiles[2] == 256 + O_vplatform; -} - -static int is_object(short *tiles) -{ - return (tiles[1] >= 256 || tiles[2] >= 256 || tiles[3] >= 256); -} - -static unsigned int prop_type(int n, short *tiles) -{ - if (is_platform(tiles)) { - static unsigned int platform_types[STBTE_MAX_PROPERTIES] = { - STBTE_PROP_bool, // phantom - STBTE_PROP_int, // x_adjust - STBTE_PROP_int, // y_adjust - STBTE_PROP_float, // width - STBTE_PROP_float, // lspeed - STBTE_PROP_float, // rspeed - STBTE_PROP_bool, // autoreturn - STBTE_PROP_bool, // one-shot - // remainder get 0, means 'no property in this slot' - }; - return platform_types[n]; - } else if (is_object(tiles)) { - if (n == 0) - return STBTE_PROP_bool; - } - return 0; -} - -static char *prop_name(int n, short *tiles) -{ - if (is_platform(tiles)) { - static char *platform_vars[STBTE_MAX_PROPERTIES] = { - "phantom", - "x_adjust", - "y_adjust", - "width", - "lspeed", - "rspeed", - "autoreturn", - "one-shot", - }; - return platform_vars[n]; - } - return "phantom"; -} - -static float prop_range(int n, short *tiles, int is_max) -{ - if (is_platform(tiles)) { - static float ranges[8][2] = { - { 0, 1 }, // phantom-flag, range is ignored - { -15, 15 }, // x_adjust - { -15, 15 }, // y_adjust - { 0, 6 }, // width - { 0, 10 }, // lspeed - { 0, 10 }, // rspeed - { 0, 1 }, // autoreturn, range is ignored - { 0, 1 }, // one-shot, range is ignored - }; - return ranges[n][is_max]; - } - return 0; -} - -static int allow_link(short *src, short *dest) -{ - if (is_platform(src)) - return dest[1] == 256+O_lever; - if (src[1] == 256+O_endpoint) - return is_platform(dest); - return 0; -} diff --git a/impeller/third_party/stb/stb/tests/vorbseek/vorbseek.c b/impeller/third_party/stb/stb/tests/vorbseek/vorbseek.c deleted file mode 100644 index f3460ad7f6905..0000000000000 --- a/impeller/third_party/stb/stb/tests/vorbseek/vorbseek.c +++ /dev/null @@ -1,125 +0,0 @@ -#include -#include -#include -#include - -#define STB_VORBIS_HEADER_ONLY -#include "stb_vorbis.c" - -#define SAMPLES_TO_TEST 3000 - -int test_count [5] = { 5000, 3000, 2000, 50000, 50000 }; -int test_spacing[5] = { 1, 111, 3337, 7779, 72717 }; - -int try_seeking(stb_vorbis *v, unsigned int pos, short *output, unsigned int num_samples) -{ - int count; - short samples[SAMPLES_TO_TEST*2]; - assert(pos <= num_samples); - - if (!stb_vorbis_seek(v, pos)) { - fprintf(stderr, "Seek to %u returned error from stb_vorbis\n", pos); - return 0; - } - - count = stb_vorbis_get_samples_short_interleaved(v, 2, samples, SAMPLES_TO_TEST*2); - - if (count > (int) (num_samples - pos)) { - fprintf(stderr, "Seek to %u allowed decoding %d samples when only %d should have been valid.\n", - pos, count, (int) (num_samples - pos)); - return 0; - } - - if (count < SAMPLES_TO_TEST && count < (int) (num_samples - pos)) { - fprintf(stderr, "Seek to %u only decoded %d samples of %d attempted when at least %d should have been valid.\n", - pos, count, SAMPLES_TO_TEST, num_samples - pos); - return 0; - } - - if (0 != memcmp(samples, output + pos*2, count*2)) { - int k; - for (k=0; k < SAMPLES_TO_TEST*2; ++k) { - if (samples[k] != output[k]) { - fprintf(stderr, "Seek to %u produced incorrect samples starting at sample %u (short #%d in buffer).\n", - pos, pos + (k/2), k); - break; - } - } - assert(k != SAMPLES_TO_TEST*2); - return 0; - } - - return 1; -} - -int main(int argc, char **argv) -{ - int num_chan, samprate; - int i, j, test, phase; - short *output; - - if (argc == 1) { - fprintf(stderr, "Usage: vorbseek {vorbisfile} [{vorbisfile]*]\n"); - fprintf(stderr, "Tests various seek offsets to make sure they're sample exact.\n"); - return 0; - } - - #if 0 - { - // check that outofmem occurs correctly - stb_vorbis_alloc va; - va.alloc_buffer = malloc(1024*1024); - for (i=0; i < 1024*1024; i += 10) { - int error=0; - stb_vorbis *v; - va.alloc_buffer_length_in_bytes = i; - v = stb_vorbis_open_filename(argv[1], &error, &va); - if (v != NULL) - break; - printf("Error %d at %d\n", error, i); - } - } - #endif - - for (j=1; j < argc; ++j) { - unsigned int successes=0, attempts = 0; - unsigned int num_samples = stb_vorbis_decode_filename(argv[j], &num_chan, &samprate, &output); - - break; - - if (num_samples == 0xffffffff) { - fprintf(stderr, "Error: couldn't open file or not vorbis file: %s\n", argv[j]); - goto fail; - } - - if (num_chan != 2) { - fprintf(stderr, "vorbseek testing only works with files with 2 channels, %s has %d\n", argv[j], num_chan); - goto fail; - } - - for (test=0; test < 5; ++test) { - int error; - stb_vorbis *v = stb_vorbis_open_filename(argv[j], &error, NULL); - if (v == NULL) { - fprintf(stderr, "Couldn't re-open %s for test #%d\n", argv[j], test); - goto fail; - } - for (phase=0; phase < 3; ++phase) { - unsigned int base = phase == 0 ? 0 : phase == 1 ? num_samples - test_count[test]*test_spacing[test] : num_samples/3; - for (i=0; i < test_count[test]; ++i) { - unsigned int pos = base + i*test_spacing[test]; - if (pos > num_samples) // this also catches underflows - continue; - successes += try_seeking(v, pos, output, num_samples); - attempts += 1; - } - } - stb_vorbis_close(v); - } - printf("%d of %d seeks failed in %s (%d samples)\n", attempts-successes, attempts, argv[j], num_samples); - free(output); - } - return 0; - fail: - return 1; -} \ No newline at end of file diff --git a/impeller/third_party/stb/stb/tests/vorbseek/vorbseek.dsp b/impeller/third_party/stb/stb/tests/vorbseek/vorbseek.dsp deleted file mode 100644 index 5eaf5795ffb7d..0000000000000 --- a/impeller/third_party/stb/stb/tests/vorbseek/vorbseek.dsp +++ /dev/null @@ -1,96 +0,0 @@ -# Microsoft Developer Studio Project File - Name="vorbseek" - Package Owner=<4> -# Microsoft Developer Studio Generated Build File, Format Version 6.00 -# ** DO NOT EDIT ** - -# TARGTYPE "Win32 (x86) Console Application" 0x0103 - -CFG=vorbseek - Win32 Debug -!MESSAGE This is not a valid makefile. To build this project using NMAKE, -!MESSAGE use the Export Makefile command and run -!MESSAGE -!MESSAGE NMAKE /f "vorbseek.mak". -!MESSAGE -!MESSAGE You can specify a configuration when running NMAKE -!MESSAGE by defining the macro CFG on the command line. For example: -!MESSAGE -!MESSAGE NMAKE /f "vorbseek.mak" CFG="vorbseek - Win32 Debug" -!MESSAGE -!MESSAGE Possible choices for configuration are: -!MESSAGE -!MESSAGE "vorbseek - Win32 Release" (based on "Win32 (x86) Console Application") -!MESSAGE "vorbseek - Win32 Debug" (based on "Win32 (x86) Console Application") -!MESSAGE - -# Begin Project -# PROP AllowPerConfigDependencies 0 -# PROP Scc_ProjName "" -# PROP Scc_LocalPath "" -CPP=cl.exe -RSC=rc.exe - -!IF "$(CFG)" == "vorbseek - Win32 Release" - -# PROP BASE Use_MFC 0 -# PROP BASE Use_Debug_Libraries 0 -# PROP BASE Output_Dir "Release" -# PROP BASE Intermediate_Dir "Release" -# PROP BASE Target_Dir "" -# PROP Use_MFC 0 -# PROP Use_Debug_Libraries 0 -# PROP Output_Dir "Release" -# PROP Intermediate_Dir "Release" -# PROP Ignore_Export_Lib 0 -# PROP Target_Dir "" -# ADD BASE CPP /nologo /W3 /GX /O2 /D "WIN32" /D "NDEBUG" /D "_CONSOLE" /D "_MBCS" /YX /FD /c -# ADD CPP /nologo /W3 /GX /Zd /O2 /I "..\.." /D "WIN32" /D "NDEBUG" /D "_CONSOLE" /D "_MBCS" /FD /c -# SUBTRACT CPP /YX -# ADD BASE RSC /l 0x409 /d "NDEBUG" -# ADD RSC /l 0x409 /d "NDEBUG" -BSC32=bscmake.exe -# ADD BASE BSC32 /nologo -# ADD BSC32 /nologo -LINK32=link.exe -# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:console /machine:I386 -# ADD LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:console /debug /machine:I386 - -!ELSEIF "$(CFG)" == "vorbseek - Win32 Debug" - -# PROP BASE Use_MFC 0 -# PROP BASE Use_Debug_Libraries 1 -# PROP BASE Output_Dir "Debug" -# PROP BASE Intermediate_Dir "Debug" -# PROP BASE Target_Dir "" -# PROP Use_MFC 0 -# PROP Use_Debug_Libraries 1 -# PROP Output_Dir "Debug" -# PROP Intermediate_Dir "Debug" -# PROP Ignore_Export_Lib 0 -# PROP Target_Dir "" -# ADD BASE CPP /nologo /W3 /Gm /GX /ZI /Od /D "WIN32" /D "_DEBUG" /D "_CONSOLE" /D "_MBCS" /YX /FD /GZ /c -# ADD CPP /nologo /W3 /Gm /GX /ZI /Od /I "..\.." /D "WIN32" /D "_DEBUG" /D "_CONSOLE" /D "_MBCS" /FD /GZ /c -# SUBTRACT CPP /YX -# ADD BASE RSC /l 0x409 /d "_DEBUG" -# ADD RSC /l 0x409 /d "_DEBUG" -BSC32=bscmake.exe -# ADD BASE BSC32 /nologo -# ADD BSC32 /nologo -LINK32=link.exe -# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:console /debug /machine:I386 /pdbtype:sept -# ADD LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:console /debug /machine:I386 /pdbtype:sept - -!ENDIF - -# Begin Target - -# Name "vorbseek - Win32 Release" -# Name "vorbseek - Win32 Debug" -# Begin Source File - -SOURCE=..\..\stb_vorbis.c -# End Source File -# Begin Source File - -SOURCE=.\vorbseek.c -# End Source File -# End Target -# End Project diff --git a/impeller/third_party/stb/stb/tools/README.footer.md b/impeller/third_party/stb/stb/tools/README.footer.md deleted file mode 100644 index b9a52bf4cc1a5..0000000000000 --- a/impeller/third_party/stb/stb/tools/README.footer.md +++ /dev/null @@ -1,100 +0,0 @@ - -FAQ ---- - -#### What's the license? - -These libraries are in the public domain (or the equivalent where that is not -possible). You can do anything you want with them. You have no legal obligation -to do anything else, although I appreciate attribution. - -#### Are there other single-file public-domain/open source libraries with minimal dependencies out there? - -[Yes.](https://github.com/nothings/stb/blob/master/docs/other_libs.md) - -#### If I wrap an stb library in a new library, does the new library have to be public domain? - -No. - -#### Some of these libraries seem redundant to existing open source libraries. Are they better somehow? - -Generally they're only better in that they're easier to integrate, -easier to use, and easier to release (single file; good API; no -attribution requirement). They may be less featureful, slower, -and/or use more memory. If you're already using an equivalent -library, there's probably no good reason to switch. - -###### Can I link directly to the table of stb libraries? - -You can use [this URL](https://github.com/nothings/stb#stb_libs) to link directly to that list. - -#### Why do you list "lines of code"? It's a terrible metric. - -Just to give you some idea of the internal complexity of the library, -to help you manage your expectations, or to let you know what you're -getting into. While not all the libraries are written in the same -style, they're certainly similar styles, and so comparisons between -the libraries are probably still meaningful. - -Note though that the lines do include both the implementation, the -part that corresponds to a header file, and the documentation. - -#### Why single-file headers? - -Windows doesn't have standard directories where libraries -live. That makes deploying libraries in Windows a lot more -painful than open source developers on Unix-derivates generally -realize. (It also makes library dependencies a lot worse in Windows.) - -There's also a common problem in Windows where a library was built -against a different version of the runtime library, which causes -link conflicts and confusion. Shipping the libs as headers means -you normally just compile them straight into your project without -making libraries, thus sidestepping that problem. - -Making them a single file makes it very easy to just -drop them into a project that needs them. (Of course you can -still put them in a proper shared library tree if you want.) - -Why not two files, one a header and one an implementation? -The difference between 10 files and 9 files is not a big deal, -but the difference between 2 files and 1 file is a big deal. -You don't need to zip or tar the files up, you don't have to -remember to attach *two* files, etc. - -#### Why "stb"? Is this something to do with Set-Top Boxes? - -No, they are just the initials for my name, Sean T. Barrett. -This was not chosen out of egomania, but as a moderately sane -way of namespacing the filenames and source function names. - -#### Will you add more image types to stb_image.h? - -If people submit them, I generally add them, but the goal of stb_image -is less for applications like image viewer apps (which need to support -every type of image under the sun) and more for things like games which -can choose what images to use, so I may decline to add them if they're -too rare or if the size of implementation vs. apparent benefit is too low. - -#### Do you have any advice on how to create my own single-file library? - -Yes. https://github.com/nothings/stb/blob/master/docs/stb_howto.txt - -#### Why public domain? - -I prefer it over GPL, LGPL, BSD, zlib, etc. for many reasons. -Some of them are listed here: -https://github.com/nothings/stb/blob/master/docs/why_public_domain.md - -#### Why C? - -Primarily, because I use C, not C++. But it does also make it easier -for other people to use them from other languages. - -#### Why not C99? stdint.h, declare-anywhere, etc. - -I still use MSVC 6 (1998) as my IDE because it has better human factors -for me than later versions of MSVC. - - - diff --git a/impeller/third_party/stb/stb/tools/README.header.md b/impeller/third_party/stb/stb/tools/README.header.md deleted file mode 100644 index 71e765bd05872..0000000000000 --- a/impeller/third_party/stb/stb/tools/README.header.md +++ /dev/null @@ -1,7 +0,0 @@ -stb -=== - -single-file public domain libraries for C/C++ - -library | lastest version | category | LoC | description ---------------------- | ---- | -------- | --- | -------------------------------- diff --git a/impeller/third_party/stb/stb/tools/README.list b/impeller/third_party/stb/stb/tools/README.list deleted file mode 100644 index 5ad58a1af178c..0000000000000 --- a/impeller/third_party/stb/stb/tools/README.list +++ /dev/null @@ -1,18 +0,0 @@ -stb_vorbis.c | audio | decode ogg vorbis files from file/memory to float/16-bit signed output -stb_image.h | graphics | image loading/decoding from file/memory: JPG, PNG, TGA, BMP, PSD, GIF, HDR, PIC -stb_truetype.h | graphics | parse, decode, and rasterize characters from truetype fonts -stb_image_write.h | graphics | image writing to disk: PNG, TGA, BMP -stb_image_resize.h | graphics | resize images larger/smaller with good quality -stb_rect_pack.h | graphics | simple 2D rectangle packer with decent quality -stretchy_buffer.h | utility | typesafe dynamic array for C (i.e. approximation to vector<>), doesn't compile as C++ -stb_textedit.h | user interface | guts of a text editor for games etc implementing them from scratch -stb_voxel_render.h | 3D graphics | Minecraft-esque voxel rendering "engine" with many more features -stb_dxt.h | 3D graphics | Fabian "ryg" Giesen's real-time DXT compressor -stb_perlin.h | 3D graphics | revised Perlin noise (3D input, 1D output) -stb_easy_font.h | 3D graphics | quick-and-dirty easy-to-deploy bitmap font for printing frame rate, etc -stb_tilemap_editor.h | game dev | embeddable tilemap editor -stb_herringbone_wang_tile.h | game dev | herringbone Wang tile map generator -stb_c_lexer.h | parsing | simplify writing parsers for C-like languages -stb_divide.h | math | more useful 32-bit modulus e.g. "euclidean divide" -stb.h | misc | helper functions for C, mostly redundant in C++; basically author's personal stuff -stb_leakcheck.h | misc | quick-and-dirty malloc/free leak-checking diff --git a/impeller/third_party/stb/stb/tools/easy_font_maker.c b/impeller/third_party/stb/stb/tools/easy_font_maker.c deleted file mode 100644 index f1b4836d3c4bd..0000000000000 --- a/impeller/third_party/stb/stb/tools/easy_font_maker.c +++ /dev/null @@ -1,211 +0,0 @@ -// This program was used to encode the data for stb_simple_font.h - -#define STB_DEFINE -#include "stb.h" -#define STB_IMAGE_IMPLEMENTATION -#include "stb_image.h" - -int w,h; -uint8 *data; - -int last_x[2], last_y[2]; -int num_seg[2], non_empty; -#if 0 -typedef struct -{ - unsigned short first_segment; - unsigned char advance; -} chardata; - -typedef struct -{ - unsigned char x:4; - unsigned char y:4; - unsigned char len:3; - unsigned char dir:1; -} segment; - -segment *segments; - -void add_seg(int x, int y, int len, int horizontal) -{ - segment s; - s.x = x; - s.y = y; - s.len = len; - s.dir = horizontal; - assert(s.x == x); - assert(s.y == y); - assert(s.len == len); - stb_arr_push(segments, s); -} -#else -typedef struct -{ - unsigned char first_segment:8; - unsigned char first_v_segment:8; - unsigned char advance:5; - unsigned char voff:1; -} chardata; - -#define X_LIMIT 1 -#define LEN_LIMIT 7 - -typedef struct -{ - unsigned char dx:1; - unsigned char y:4; - unsigned char len:3; -} segment; - -segment *segments; -segment *vsegments; - -void add_seg(int x, int y, int len, int horizontal) -{ - segment s; - - while (x - last_x[horizontal] > X_LIMIT) { - add_seg(last_x[horizontal] + X_LIMIT, 0, 0, horizontal); - } - while (len > LEN_LIMIT) { - add_seg(x, y, LEN_LIMIT, horizontal); - len -= LEN_LIMIT; - x += LEN_LIMIT*horizontal; - y += LEN_LIMIT*!horizontal; - } - - s.dx = x - last_x[horizontal]; - s.y = y; - s.len = len; - non_empty += len != 0; - //assert(s.x == x); - assert(s.y == y); - assert(s.len == len); - ++num_seg[horizontal]; - if (horizontal) - stb_arr_push(segments, s); - else - stb_arr_push(vsegments, s); - last_x[horizontal] = x; -} - -void print_segments(segment *s) -{ - int i, hpos; - printf(" "); - hpos = 4; - for (i=0; i < stb_arr_len(s); ++i) { - // repack for portability - unsigned char seg = s[i].len + s[i].dx*8 + s[i].y*16; - hpos += printf("%d,", seg); - if (hpos > 72 && i+1 < stb_arr_len(s)) { - hpos = 4; - printf("\n "); - } - } - printf("\n"); -} - -#endif - -chardata charinfo[128]; - -int parse_char(int x, chardata *c, int offset) -{ - int start_x = x, end_x, top_y = 0, y; - - c->first_segment = stb_arr_len(segments); - c->first_v_segment = stb_arr_len(vsegments) - offset; - assert(c->first_segment == stb_arr_len(segments)); - assert(c->first_v_segment + offset == stb_arr_len(vsegments)); - - // find advance distance - end_x = x+1; - while (data[end_x*3] == 255) - ++end_x; - c->advance = end_x - start_x + 1; - - last_x[0] = last_x[1] = 0; - last_y[0] = last_y[1] = 0; - - for (y=2; y < h; ++y) { - for (x=start_x; x < end_x; ++x) { - if (data[y*3*w+x*3+1] < 255) { - top_y = y; - break; - } - } - if (top_y) - break; - } - c->voff = top_y > 2; - if (top_y > 2) - top_y = 3; - - for (x=start_x; x < end_x; ++x) { - int y; - for (y=2; y < h; ++y) { - if (data[y*3*w+x*3+1] < 255) { - if (data[y*3*w+x*3+0] == 255) { // red - int len=0; - while (y+len < h && data[(y+len)*3*w+x*3+0] == 255 && data[(y+len)*3*w+x*3+1] == 0) { - data[(y+len)*3*w+x*3+0] = 0; - ++len; - } - add_seg(x-start_x,y-top_y,len,0); - } - if (data[y*3*w+x*3+2] == 255) { // blue - int len=0; - while (x+len < end_x && data[y*3*w+(x+len)*3+2] == 255 && data[y*3*w+(x+len)*3+1] == 0) { - data[y*3*w+(x+len)*3+2] = 0; - ++len; - } - add_seg(x-start_x,y-top_y,len,1); - } - } - } - } - return end_x; -} - - -int main(int argc, char **argv) -{ - int c, x=0; - data = stbi_load("easy_font_raw.png", &w, &h, 0, 3); - for (c=32; c < 127; ++c) { - x = parse_char(x, &charinfo[c], 0); - printf("%3d -- %3d %3d\n", c, charinfo[c].first_segment, charinfo[c].first_v_segment); - } - printf("===\n"); - printf("%d %d %d\n", num_seg[0], num_seg[1], non_empty); - printf("%d\n", sizeof(segments[0]) * stb_arr_len(segments)); - printf("%d\n", sizeof(segments[0]) * stb_arr_len(segments) + sizeof(segments[0]) * stb_arr_len(vsegments) + sizeof(charinfo[32])*95); - - printf("struct {\n" - " unsigned char advance;\n" - " unsigned char h_seg;\n" - " unsigned char v_seg;\n" - "} stb_easy_font_charinfo[96] = {\n"); - charinfo[c].first_segment = stb_arr_len(segments); - charinfo[c].first_v_segment = stb_arr_len(vsegments); - for (c=32; c < 128; ++c) { - if ((c & 3) == 0) printf(" "); - printf("{ %2d,%3d,%3d },", - charinfo[c].advance + 16*charinfo[c].voff, - charinfo[c].first_segment, - charinfo[c].first_v_segment); - if ((c & 3) == 3) printf("\n"); else printf(" "); - } - printf("};\n\n"); - - printf("unsigned char stb_easy_font_hseg[%d] = {\n", stb_arr_len(segments)); - print_segments(segments); - printf("};\n\n"); - - printf("unsigned char stb_easy_font_vseg[%d] = {\n", stb_arr_len(vsegments)); - print_segments(vsegments); - printf("};\n"); - return 0; -} diff --git a/impeller/third_party/stb/stb/tools/make_readme.c b/impeller/third_party/stb/stb/tools/make_readme.c deleted file mode 100644 index 224f28974fd8d..0000000000000 --- a/impeller/third_party/stb/stb/tools/make_readme.c +++ /dev/null @@ -1,61 +0,0 @@ -#define STB_DEFINE -#include "../stb.h" - -int main(int argc, char **argv) -{ - int i; - int hlen, flen, listlen, total_lines = 0; - char *header = stb_file("README.header.md", &hlen); // stb_file - read file into malloc()ed buffer - char *footer = stb_file("README.footer.md", &flen); // stb_file - read file into malloc()ed buffer - char **list = stb_stringfile("README.list", &listlen); // stb_stringfile - read file lines into malloced array of strings - - FILE *f = fopen("../README.md", "wb"); - - fprintf(f, "\n\n"); - fwrite(header, 1, hlen, f); - - for (i=0; i < listlen; ++i) { - int num,j; - char **tokens = stb_tokens_stripwhite(list[i], "|", &num); // stb_tokens -- tokenize string into malloced array of strings - int num_lines; - char **lines = stb_stringfile(stb_sprintf("../%s", tokens[0]), &num_lines); - char *s1, *s2,*s3; - s1 = strchr(lines[0], '-'); - if (!s1) stb_fatal("Couldn't find '-' before version number in %s", tokens[0]); // stb_fatal -- print error message & exit - s2 = strchr(s1+2, '-'); - if (!s2) stb_fatal("Couldn't find '-' after version number in %s", tokens[0]); // stb_fatal -- print error message & exit - *s2 = 0; - s1 += 1; - s1 = stb_trimwhite(s1); // stb_trimwhite -- advance pointer to after whitespace & delete trailing whitespace - if (*s1 == 'v') ++s1; - s3 = tokens[0]; - stb_trimwhite(s3); - if (strlen(s3) < 21) { - fprintf(f, "**%s** | %s", tokens[0], s1); - } else { - char buffer[256]; - strncpy(buffer, s3, 18); - buffer[18] = 0; - strcat(buffer, "..."); - fprintf(f, "**%s** | %s", buffer, s1); - } - s1 = stb_trimwhite(tokens[1]); // stb_trimwhite -- advance pointer to after whitespace & delete trailing whitespace - s2 = stb_dupreplace(s1, " ", " "); // stb_dupreplace -- search & replace string and malloc result - fprintf(f, " | %s", s2); - free(s2); - fprintf(f, " | %d", num_lines); - total_lines += num_lines; - for (j=2; j < num; ++j) - fprintf(f, " | %s", tokens[j]); - fprintf(f, "\n"); - } - - fprintf(f, "\n"); - fprintf(f, "Total libraries: %d \n", listlen); - fprintf(f, "Total lines of C code: %d\n\n", total_lines); - - fwrite(footer, 1, flen, f); - fclose(f); - - return 0; -} diff --git a/impeller/third_party/stb/stb/tools/make_readme.dsp b/impeller/third_party/stb/stb/tools/make_readme.dsp deleted file mode 100644 index 232dd86944c68..0000000000000 --- a/impeller/third_party/stb/stb/tools/make_readme.dsp +++ /dev/null @@ -1,97 +0,0 @@ -# Microsoft Developer Studio Project File - Name="make_readme" - Package Owner=<4> -# Microsoft Developer Studio Generated Build File, Format Version 6.00 -# ** DO NOT EDIT ** - -# TARGTYPE "Win32 (x86) Console Application" 0x0103 - -CFG=make_readme - Win32 Debug -!MESSAGE This is not a valid makefile. To build this project using NMAKE, -!MESSAGE use the Export Makefile command and run -!MESSAGE -!MESSAGE NMAKE /f "make_readme.mak". -!MESSAGE -!MESSAGE You can specify a configuration when running NMAKE -!MESSAGE by defining the macro CFG on the command line. For example: -!MESSAGE -!MESSAGE NMAKE /f "make_readme.mak" CFG="make_readme - Win32 Debug" -!MESSAGE -!MESSAGE Possible choices for configuration are: -!MESSAGE -!MESSAGE "make_readme - Win32 Release" (based on "Win32 (x86) Console Application") -!MESSAGE "make_readme - Win32 Debug" (based on "Win32 (x86) Console Application") -!MESSAGE - -# Begin Project -# PROP AllowPerConfigDependencies 0 -# PROP Scc_ProjName "" -# PROP Scc_LocalPath "" -CPP=cl.exe -RSC=rc.exe - -!IF "$(CFG)" == "make_readme - Win32 Release" - -# PROP BASE Use_MFC 0 -# PROP BASE Use_Debug_Libraries 0 -# PROP BASE Output_Dir "Release" -# PROP BASE Intermediate_Dir "Release" -# PROP BASE Target_Dir "" -# PROP Use_MFC 0 -# PROP Use_Debug_Libraries 0 -# PROP Output_Dir "Release" -# PROP Intermediate_Dir "Release" -# PROP Target_Dir "" -# ADD BASE CPP /nologo /W3 /GX /O2 /D "WIN32" /D "NDEBUG" /D "_CONSOLE" /D "_MBCS" /YX /FD /c -# ADD CPP /nologo /W3 /GX /O2 /D "WIN32" /D "NDEBUG" /D "_CONSOLE" /D "_MBCS" /YX /FD /c -# ADD BASE RSC /l 0x409 /d "NDEBUG" -# ADD RSC /l 0x409 /d "NDEBUG" -BSC32=bscmake.exe -# ADD BASE BSC32 /nologo -# ADD BSC32 /nologo -LINK32=link.exe -# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:console /machine:I386 -# ADD LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:console /machine:I386 - -!ELSEIF "$(CFG)" == "make_readme - Win32 Debug" - -# PROP BASE Use_MFC 0 -# PROP BASE Use_Debug_Libraries 1 -# PROP BASE Output_Dir "Debug" -# PROP BASE Intermediate_Dir "Debug" -# PROP BASE Target_Dir "" -# PROP Use_MFC 0 -# PROP Use_Debug_Libraries 1 -# PROP Output_Dir "Debug" -# PROP Intermediate_Dir "Debug\make_readme" -# PROP Target_Dir "" -# ADD BASE CPP /nologo /W3 /Gm /GX /ZI /Od /D "WIN32" /D "_DEBUG" /D "_CONSOLE" /D "_MBCS" /YX /FD /GZ /c -# ADD CPP /nologo /W3 /Gm /GX /ZI /Od /D "WIN32" /D "_DEBUG" /D "_CONSOLE" /D "_MBCS" /FD /GZ /c -# SUBTRACT CPP /YX -# ADD BASE RSC /l 0x409 /d "_DEBUG" -# ADD RSC /l 0x409 /d "_DEBUG" -BSC32=bscmake.exe -# ADD BASE BSC32 /nologo -# ADD BSC32 /nologo -LINK32=link.exe -# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:console /debug /machine:I386 /pdbtype:sept -# ADD LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:console /debug /machine:I386 /pdbtype:sept - -!ENDIF - -# Begin Target - -# Name "make_readme - Win32 Release" -# Name "make_readme - Win32 Debug" -# Begin Source File - -SOURCE=.\make_readme.c -# End Source File -# Begin Source File - -SOURCE=.\README.header.md -# End Source File -# Begin Source File - -SOURCE=.\README.list -# End Source File -# End Target -# End Project diff --git a/impeller/third_party/stb/stb/tools/mr.bat b/impeller/third_party/stb/stb/tools/mr.bat deleted file mode 100644 index 475bc4f4ff407..0000000000000 --- a/impeller/third_party/stb/stb/tools/mr.bat +++ /dev/null @@ -1 +0,0 @@ -debug\make_readme diff --git a/impeller/third_party/stb/stb/tools/unicode.c b/impeller/third_party/stb/stb/tools/unicode.c deleted file mode 100644 index 8b9d8da91b45a..0000000000000 --- a/impeller/third_party/stb/stb/tools/unicode.c +++ /dev/null @@ -1,749 +0,0 @@ -#define STB_DEFINE -#include "../stb.h" - -// create unicode mappings -// -// Two kinds of mappings: -// map to a number -// map to a bit -// -// For mapping to a number, we use the following strategy: -// -// User supplies: -// 1. a table of numbers (for now we use uint16, so full Unicode table is 4MB) -// 2. a "don't care" value -// 3. define a 'fallback' value (typically 0) -// 4. define a fast-path range (typically 0..255 or 0..1023) [@TODO: automate detecting this] -// -// Code: -// 1. Determine range of *end* of unicode codepoints (U+10FFFF and down) which -// all have the same value (or don't care). If large enough, emit this as a -// special case in the code. -// 2. Repeat above, limited to at most U+FFFF. -// 3. Cluster the data into intervals of 8,16,32,64,128,256 numeric values. -// 3a. If all the values in an interval are fallback/dont-care, no further processing -// 3b. Find the "trimmed range" outside which all the values are the fallback or don't care -// 3c. Find the "special trimmed range" outside which all the values are some constant or don't care -// 4. Pack the clusters into continuous memory, and find previous instances of -// the cluster. Repeat for trimmed & special-trimmed. In the first case, find -// previous instances of the cluster (allow don't-care to match in either -// direction), both aligned and mis-aligned; in the latter, starting where -// things start or mis-aligned. Build an index table specifiying the -// location of each cluster (and its length). Allow an extra indirection here; -// the full-sized index can index a smaller table which has the actual offset -// (and lengths). -// 5. Associate with each packed continuous memory above the amount of memory -// required to store the data w/ smallest datatype (of uint8, uint16, uint32). -// Discard the continuous memory. Recurse on each index table, but avoid the -// smaller packing. -// -// For mapping to a bit, we pack the results for 8 characters into a byte, and then apply -// the above strategy. Note that there may be more optimal approaches with e.g. packing -// 8 different bits into a single structure, though, which we should explore eventually. - - -// currently we limit *indices* to being 2^16, and we pack them as -// index + end_trim*2^16 + start_trim*2^24; specials have to go in a separate table -typedef uint32 uval; -#define UVAL_DONT_CARE_DEFAULT 0xffffffff - -typedef struct -{ - uval *input; - uint32 dont_care; - uint32 fallback; - int fastpath; - int length; - int depth; - int has_sign; - int splittable; - int replace_fallback_with_codepoint; - size_t input_size; - size_t inherited_storage; -} table; - -typedef struct -{ - int split_log2; - table result; // index into not-returned table - int storage; -} output; - -typedef struct -{ - table t; - char **output_name; -} info; - -typedef struct -{ - size_t path; - size_t size; -} result; - -typedef struct -{ - uint8 trim_end; - uint8 trim_start; - uint8 special; - uint8 aligned; - uint8 indirect; - - uint16 overhead; // add some forced overhead for each mode to avoid getting complex encoding when it doesn't save much - -} mode_info; - -mode_info modes[] = -{ - { 0,0,0,0,0, 32, }, - { 0,0,0,0,1, 100, }, - { 0,0,0,1,0, 32, }, - { 0,0,0,1,1, 100, }, - { 0,0,1,0,1, 100, }, - { 0,0,1,1,0, 32, }, - { 0,0,1,1,1, 200, }, - { 1,0,0,0,0, 100, }, - { 1,0,0,0,1, 120, }, - { 1,1,0,0,0, 100, }, - { 1,1,0,0,1, 130, }, - { 1,0,1,0,0, 130, }, - { 1,0,1,0,1, 180, }, - { 1,1,1,0,0, 180, }, - { 1,1,1,0,1, 200, }, -}; - -#define MODECOUNT (sizeof(modes)/sizeof(modes[0])) -#define CLUSTERSIZECOUNT 6 // 8,16, 32,64, 128,256 - -size_t size_for_max_number(uint32 number) -{ - if (number == 0) return 0; - if (number < 256) return 1; - if (number < 256*256) return 2; - if (number < 256*256*256) return 3; - return 4; -} - -size_t size_for_max_number_aligned(uint32 number) -{ - size_t n = size_for_max_number(number); - return n == 3 ? 4 : n; -} - -uval get_data(uval *data, int offset, uval *end) -{ - if (data + offset >= end) - return 0; - else - return data[offset]; -} - -int safe_len(uval *data, int len, uval *end) -{ - if (len > end - data) - return end - data; - return len; -} - -uval tempdata[256]; -int dirty=0; - -size_t find_packed(uval **packed, uval *data, int len, int aligned, int fastpath, uval *end, int offset, int replace) -{ - int packlen = stb_arr_len(*packed); - int i,p; - - if (data+len > end || replace) { - int safelen = safe_len(data, len, end); - memset(tempdata, 0, dirty*sizeof(tempdata[0])); - memcpy(tempdata, data, safelen * sizeof(data[0])); - data = tempdata; - dirty = len; - } - if (replace) { - int i; - int safelen = safe_len(data, len, end); - for (i=0; i < safelen; ++i) - if (data[i] == 0) - data[i] = offset+i; - } - - if (len <= 0) - return 0; - if (!fastpath) { - if (aligned) { - for (i=0; i < packlen; i += len) - if ((*packed)[i] == data[0] && 0==memcmp(&(*packed)[i], data, len * sizeof(uval))) - return i / len; - } else { - for (i=0; i < packlen-len+1; i += 1 ) - if ((*packed)[i] == data[0] && 0==memcmp(&(*packed)[i], data, len * sizeof(uval))) - return i; - } - } - p = stb_arr_len(*packed); - for (i=0; i < len; ++i) - stb_arr_push(*packed, data[i]); - return p; -} - -void output_table(char *name1, char *name2, uval *data, int length, int sign, char **names) -{ - char temp[20]; - uval maxv = 0; - int bytes, numlen, at_newline; - int linelen = 79; // @TODO: make table more readable by choosing a length that's a multiple? - int i,pos, do_split=0; - for (i=0; i < length; ++i) - if (sign) - maxv = stb_max(maxv, (uval)abs((int)data[i])); - else - maxv = stb_max(maxv, data[i]); - bytes = size_for_max_number_aligned(maxv); - sprintf(temp, "%d", maxv); - numlen=strlen(temp); - if (sign) - ++numlen; - - if (bytes == 0) - return; - - printf("uint%d %s%s[%d] = {\n", bytes*8, name1, name2, length); - at_newline = 1; - for (i=0; i < length; ++i) { - if (pos + numlen + 2 > linelen) { - printf("\n"); - at_newline = 1; - pos = 0; - } - if (at_newline) { - printf(" "); - pos = 2; - at_newline = 0; - } else { - printf(" "); - ++pos; - } - printf("%*d,", numlen, data[i]); - pos += numlen+1; - } - if (!at_newline) printf("\n"); - printf("};\n"); -} - -void output_table_with_trims(char *name1, char *name2, uval *data, int length) -{ - uval maxt=0, maxp=0; - int i,d,s,e, count; - // split the table into two pieces - uval *trims = NULL; - - if (length == 0) - return; - - for (i=0; i < stb_arr_len(data); ++i) { - stb_arr_push(trims, data[i] >> 16); - data[i] &= 0xffff; - maxt = stb_max(maxt, trims[i]); - maxp = stb_max(maxp, data[i]); - } - - d=s=e=1; - if (maxt >= 256) { - // need to output start & end values - if (maxp >= 256) { - // can pack into a single table - printf("struct { uint16 val; uint8 start, end; } %s%s[%d] = {\n", name1, name2, length); - } else { - output_table(name1, name2, data, length, 0, 0); - d=0; - printf("struct { uint8 start, end; } %s%s_trim[%d] = {\n", name1, name2, length); - } - } else if (maxt > 0) { - if (maxp >= 256) { - output_table(name1, name2, data, length, 0, 0); - output_table(name1, stb_sprintf("%s_end", name2), trims, length, 0, 0); - return; - } else { - printf("struct { uint8 val, end; } %s%s[%d] = {\n", name1, name2, length); - s=0; - } - } else { - output_table(name1, name2, data, length, 0, 0); - return; - } - // d or s can be zero (but not both), e is always present and last - count = d + s + e; - assert(count >= 2 && count <= 3); - - { - char temp[60]; - uval maxv = 0; - int numlen, at_newline, len; - int linelen = 79; // @TODO: make table more readable by choosing a length that's a multiple? - int i,pos, do_split=0; - numlen = 0; - for (i=0; i < length; ++i) { - if (count == 2) - sprintf(temp, "{%d,%d}", d ? data[i] : (trims[i]>>8), trims[i]&255); - else - sprintf(temp, "{%d,%d,%d}", data[i], trims[i]>>8, trims[i]&255); - len = strlen(temp); - numlen = stb_max(len, numlen); - } - - at_newline = 1; - for (i=0; i < length; ++i) { - if (pos + numlen + 2 > linelen) { - printf("\n"); - at_newline = 1; - pos = 0; - } - if (at_newline) { - printf(" "); - pos = 2; - at_newline = 0; - } else { - printf(" "); - ++pos; - } - if (count == 2) - sprintf(temp, "{%d,%d}", d ? data[i] : (trims[i]>>8), trims[i]&255); - else - sprintf(temp, "{%d,%d,%d}", data[i], trims[i]>>8, trims[i]&255); - printf("%*s,", numlen, temp); - pos += numlen+1; - } - if (!at_newline) printf("\n"); - printf("};\n"); - } -} - -int weight=1; - -table pack_for_mode(table *t, int mode, char *table_name) -{ - size_t extra_size; - int i; - uval maxv; - mode_info mi = modes[mode % MODECOUNT]; - int size = 8 << (mode / MODECOUNT); - table newtab; - uval *packed = NULL; - uval *index = NULL; - uval *indirect = NULL; - uval *specials = NULL; - newtab.dont_care = UVAL_DONT_CARE_DEFAULT; - if (table_name) - printf("// clusters of %d\n", size); - for (i=0; i < t->length; i += size) { - uval newval; - int fastpath = (i < t->fastpath); - if (mi.special) { - int end_trim = size-1; - int start_trim = 0; - uval special; - // @TODO: pick special from start or end instead of only end depending on which is longer - for(;;) { - special = t->input[i + end_trim]; - if (special != t->dont_care || end_trim == 0) - break; - --end_trim; - } - // at this point, special==inp[end_trim], and end_trim >= 0 - if (special == t->dont_care && !fastpath) { - // entire block is don't care, so OUTPUT don't care - stb_arr_push(index, newtab.dont_care); - continue; - } else { - uval pos, trim; - if (mi.trim_end && !fastpath) { - while (end_trim >= 0) { - if (t->input[i + end_trim] == special || t->input[i + end_trim] == t->dont_care) - --end_trim; - else - break; - } - } - - if (mi.trim_start && !fastpath) { - while (start_trim < end_trim) { - if (t->input[i + start_trim] == special || t->input[i + start_trim] == t->dont_care) - ++start_trim; - else - break; - } - } - - // end_trim points to the last character we have to output - - // find the first match, or add it - pos = find_packed(&packed, &t->input[i+start_trim], end_trim-start_trim+1, mi.aligned, fastpath, &t->input[t->length], i+start_trim, t->replace_fallback_with_codepoint); - - // encode as a uval - if (!mi.trim_end) { - if (end_trim == 0) - pos = special; - else - pos = pos | 0x80000000; - } else { - assert(end_trim < size && end_trim >= -1); - if (!fastpath) assert(end_trim < size-1); // special always matches last one - assert(end_trim < size && end_trim+1 >= 0); - if (!fastpath) assert(end_trim+1 < size); - - if (mi.trim_start) - trim = start_trim*256 + (end_trim+1); - else - trim = end_trim+1; - - assert(pos < 65536); // @TODO: if this triggers, just bail on this search path - pos = pos + (trim << 16); - } - - newval = pos; - - stb_arr_push(specials, special); - } - } else if (mi.trim_end) { - int end_trim = size-1; - int start_trim = 0; - uval pos, trim; - - while (end_trim >= 0 && !fastpath) - if (t->input[i + end_trim] == t->fallback || t->input[i + end_trim] == t->dont_care) - --end_trim; - else - break; - - if (mi.trim_start && !fastpath) { - while (start_trim < end_trim) { - if (t->input[i + start_trim] == t->fallback || t->input[i + start_trim] == t->dont_care) - ++start_trim; - else - break; - } - } - - // end_trim points to the last character we have to output, and can be -1 - ++end_trim; // make exclusive at end - - if (end_trim == 0 && size == 256) - start_trim = end_trim = 1; // we can't make encode a length from 0..256 in 8 bits, so restrict end_trim to 1..256 - - // find the first match, or add it - pos = find_packed(&packed, &t->input[i+start_trim], end_trim - start_trim, mi.aligned, fastpath, &t->input[t->length], i+start_trim, t->replace_fallback_with_codepoint); - - assert(end_trim <= size && end_trim >= 0); - if (size == 256) - assert(end_trim-1 < 256 && end_trim-1 >= 0); - else - assert(end_trim < 256 && end_trim >= 0); - if (size == 256) - --end_trim; - - if (mi.trim_start) - trim = start_trim*256 + end_trim; - else - trim = end_trim; - - assert(pos < 65536); // @TODO: if this triggers, just bail on this search path - pos = pos + (trim << 16); - - newval = pos; - } else { - newval = find_packed(&packed, &t->input[i], size, mi.aligned, fastpath, &t->input[t->length], i, t->replace_fallback_with_codepoint); - } - - if (mi.indirect) { - int j; - for (j=0; j < stb_arr_len(indirect); ++j) - if (indirect[j] == newval) - break; - if (j == stb_arr_len(indirect)) - stb_arr_push(indirect, newval); - stb_arr_push(index, j); - } else { - stb_arr_push(index, newval); - } - } - - // total up the new size for everything but the index table - extra_size = mi.overhead * weight; // not the actual overhead cost; a penalty to avoid excessive complexity - extra_size += 150; // per indirection - if (table_name) - extra_size = 0; - - if (t->has_sign) { - // 'packed' contains two values, which should be packed positive & negative for size - uval maxv2; - for (i=0; i < stb_arr_len(packed); ++i) - if (packed[i] & 0x80000000) - maxv2 = stb_max(maxv2, packed[i]); - else - maxv = stb_max(maxv, packed[i]); - maxv = stb_max(maxv, maxv2) << 1; - } else { - maxv = 0; - for (i=0; i < stb_arr_len(packed); ++i) - if (packed[i] > maxv && packed[i] != t->dont_care) - maxv = packed[i]; - } - extra_size += stb_arr_len(packed) * (t->splittable ? size_for_max_number(maxv) : size_for_max_number_aligned(maxv)); - if (table_name) { - if (t->splittable) - output_table_with_trims(table_name, "", packed, stb_arr_len(packed)); - else - output_table(table_name, "", packed, stb_arr_len(packed), t->has_sign, NULL); - } - - maxv = 0; - for (i=0; i < stb_arr_len(specials); ++i) - if (specials[i] > maxv) - maxv = specials[i]; - extra_size += stb_arr_len(specials) * size_for_max_number_aligned(maxv); - if (table_name) - output_table(table_name, "_default", specials, stb_arr_len(specials), 0, NULL); - - maxv = 0; - for (i=0; i < stb_arr_len(indirect); ++i) - if (indirect[i] > maxv) - maxv = indirect[i]; - extra_size += stb_arr_len(indirect) * size_for_max_number(maxv); - - if (table_name && stb_arr_len(indirect)) { - if (mi.trim_end) - output_table_with_trims(table_name, "_index", indirect, stb_arr_len(indirect)); - else { - assert(0); // this case should only trigger in very extreme circumstances - output_table(table_name, "_index", indirect, stb_arr_len(indirect), 0, NULL); - } - mi.trim_end = mi.special = 0; - } - - if (table_name) - printf("// above tables should be %d bytes\n", extra_size); - - maxv = 0; - for (i=0; i < stb_arr_len(index); ++i) - if (index[i] > maxv && index[i] != t->dont_care) - maxv = index[i]; - newtab.splittable = mi.trim_end; - newtab.input_size = newtab.splittable ? size_for_max_number(maxv) : size_for_max_number_aligned(maxv); - newtab.input = index; - newtab.length = stb_arr_len(index); - newtab.inherited_storage = t->inherited_storage + extra_size; - newtab.fastpath = 0; - newtab.depth = t->depth+1; - stb_arr_free(indirect); - stb_arr_free(packed); - stb_arr_free(specials); - - return newtab; -} - -result pack_table(table *t, size_t path, int min_storage) -{ - int i; - result best; - best.size = t->inherited_storage + t->input_size * t->length; - best.path = path; - - if ((int) t->inherited_storage > min_storage) { - best.size = stb_max(best.size, t->inherited_storage); - return best; - } - - if (t->length <= 256 || t->depth >= 4) { - //printf("%08x: %7d\n", best.path, best.size); - return best; - } - - path <<= 7; - for (i=0; i < MODECOUNT * CLUSTERSIZECOUNT; ++i) { - table newtab; - result r; - newtab = pack_for_mode(t, i, 0); - r = pack_table(&newtab, path+i+1, min_storage); - if (r.size < best.size) - best = r; - stb_arr_free(newtab.input); - //printf("Size: %6d + %6d\n", newtab.inherited_storage, newtab.input_size * newtab.length); - } - return best; -} - -int pack_table_by_modes(table *t, int *modes) -{ - table s = *t; - while (*modes > -1) { - table newtab; - newtab = pack_for_mode(&s, *modes, 0); - if (s.input != t->input) - stb_arr_free(s.input); - s = newtab; - ++modes; - } - return s.inherited_storage + s.input_size * s.length; -} - -int strip_table(table *t, int exceptions) -{ - uval terminal_value; - int p = t->length-1; - while (t->input[p] == t->dont_care) - --p; - terminal_value = t->input[p]; - - while (p >= 0x10000) { - if (t->input[p] != terminal_value && t->input[p] != t->dont_care) { - if (exceptions) - --exceptions; - else - break; - } - --p; - } - return p+1; // p is a character we must output -} - -void optimize_table(table *t, char *table_name) -{ - int modelist[3] = { 85, -1 }; - int modes[8]; - int num_modes = 0; - int decent_size; - result r; - size_t path; - table s; - - // strip tail end of table - int orig_length = t->length; - int threshhold = 0xffff; - int p = strip_table(t, 2); - int len_saved = t->length - p; - if (len_saved >= threshhold) { - t->length = p; - while (p > 0x10000) { - p = strip_table(t, 0); - len_saved = t->length - p; - if (len_saved < 0x10000) - break; - len_saved = orig_length - p; - if (len_saved < threshhold) - break; - threshhold *= 2; - } - } - - t->depth = 1; - - - // find size of table if we use path 86 - decent_size = pack_table_by_modes(t, modelist); - - - #if 1 - // find best packing of remainder of table by exploring tree of packings - r = pack_table(t, 0, decent_size); - // use the computed 'path' to evaluate and output tree - path = r.path; - #else - path = 86;//90;//132097; - #endif - - while (path) { - modes[num_modes++] = (path & 127) - 1; - path >>= 7; - } - - printf("// modes: %d\n", r.path); - s = *t; - while (num_modes > 0) { - char name[256]; - sprintf(name, "%s_%d", table_name, num_modes+1); - --num_modes; - s = pack_for_mode(&s, modes[num_modes], name); - } - // output the final table as-is - if (s.splittable) - output_table_with_trims(table_name, "_1", s.input, s.length); - else - output_table(table_name, "_1", s.input, s.length, 0, NULL); -} - -uval unicode_table[0x110000]; - -typedef struct -{ - uval lo,hi; -} char_range; - -char_range get_range(char *str) -{ - char_range cr; - char *p; - cr.lo = strtol(str, &p, 16); - p = stb_skipwhite(p); - if (*p == '.') - cr.hi = strtol(p+2, NULL, 16); - else - cr.hi = cr.lo; - return cr; -} - -char *skip_semi(char *s, int count) -{ - while (count) { - s = strchr(s, ';'); - assert(s != NULL); - ++s; - --count; - } - return s; -} - -int main(int argc, char **argv) -{ - table t; - uval maxv=0; - int i,n=0; - char **s = stb_stringfile("../../data/UnicodeData.txt", &n); - assert(s); - for (i=0; i < n; ++i) { - if (s[i][0] == '#' || s[i][0] == '\n' || s[i][0] == 0) - ; - else { - char_range cr = get_range(s[i]); - char *t = skip_semi(s[i], 13); - uval j, v; - if (*t == ';' || *t == '\n' || *t == 0) - v = 0; - else { - v = strtol(t, NULL, 16); - if (v < 65536) { - maxv = stb_max(v, maxv); - for (j=cr.lo; j <= cr.hi; ++j) { - unicode_table[j] = v; - //printf("%06x => %06x\n", j, v); - } - } - } - } - } - - t.depth = 0; - t.dont_care = UVAL_DONT_CARE_DEFAULT; - t.fallback = 0; - t.fastpath = 256; - t.inherited_storage = 0; - t.has_sign = 0; - t.splittable = 0; - t.input = unicode_table; - t.input_size = size_for_max_number(maxv); - t.length = 0x110000; - t.replace_fallback_with_codepoint = 1; - - optimize_table(&t, "stbu_upppercase"); - return 0; -} diff --git a/impeller/third_party/stb/stb/tools/unicode/unicode.dsp b/impeller/third_party/stb/stb/tools/unicode/unicode.dsp deleted file mode 100644 index 78e6a5ba67bf0..0000000000000 --- a/impeller/third_party/stb/stb/tools/unicode/unicode.dsp +++ /dev/null @@ -1,88 +0,0 @@ -# Microsoft Developer Studio Project File - Name="unicode" - Package Owner=<4> -# Microsoft Developer Studio Generated Build File, Format Version 6.00 -# ** DO NOT EDIT ** - -# TARGTYPE "Win32 (x86) Console Application" 0x0103 - -CFG=unicode - Win32 Debug -!MESSAGE This is not a valid makefile. To build this project using NMAKE, -!MESSAGE use the Export Makefile command and run -!MESSAGE -!MESSAGE NMAKE /f "unicode.mak". -!MESSAGE -!MESSAGE You can specify a configuration when running NMAKE -!MESSAGE by defining the macro CFG on the command line. For example: -!MESSAGE -!MESSAGE NMAKE /f "unicode.mak" CFG="unicode - Win32 Debug" -!MESSAGE -!MESSAGE Possible choices for configuration are: -!MESSAGE -!MESSAGE "unicode - Win32 Release" (based on "Win32 (x86) Console Application") -!MESSAGE "unicode - Win32 Debug" (based on "Win32 (x86) Console Application") -!MESSAGE - -# Begin Project -# PROP AllowPerConfigDependencies 0 -# PROP Scc_ProjName "" -# PROP Scc_LocalPath "" -CPP=cl.exe -RSC=rc.exe - -!IF "$(CFG)" == "unicode - Win32 Release" - -# PROP BASE Use_MFC 0 -# PROP BASE Use_Debug_Libraries 0 -# PROP BASE Output_Dir "Release" -# PROP BASE Intermediate_Dir "Release" -# PROP BASE Target_Dir "" -# PROP Use_MFC 0 -# PROP Use_Debug_Libraries 0 -# PROP Output_Dir "Release" -# PROP Intermediate_Dir "Release" -# PROP Target_Dir "" -# ADD BASE CPP /nologo /W3 /GX /O2 /D "WIN32" /D "NDEBUG" /D "_CONSOLE" /D "_MBCS" /YX /FD /c -# ADD CPP /nologo /W3 /GX /O2 /D "WIN32" /D "NDEBUG" /D "_CONSOLE" /D "_MBCS" /YX /FD /c -# ADD BASE RSC /l 0x409 /d "NDEBUG" -# ADD RSC /l 0x409 /d "NDEBUG" -BSC32=bscmake.exe -# ADD BASE BSC32 /nologo -# ADD BSC32 /nologo -LINK32=link.exe -# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:console /machine:I386 -# ADD LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:console /machine:I386 - -!ELSEIF "$(CFG)" == "unicode - Win32 Debug" - -# PROP BASE Use_MFC 0 -# PROP BASE Use_Debug_Libraries 1 -# PROP BASE Output_Dir "Debug" -# PROP BASE Intermediate_Dir "Debug" -# PROP BASE Target_Dir "" -# PROP Use_MFC 0 -# PROP Use_Debug_Libraries 1 -# PROP Output_Dir "Debug" -# PROP Intermediate_Dir "Debug" -# PROP Target_Dir "" -# ADD BASE CPP /nologo /W3 /Gm /GX /ZI /Od /D "WIN32" /D "_DEBUG" /D "_CONSOLE" /D "_MBCS" /YX /FD /GZ /c -# ADD CPP /nologo /W3 /Gm /GX /ZI /Od /D "WIN32" /D "_DEBUG" /D "_CONSOLE" /D "_MBCS" /YX /FD /GZ /c -# ADD BASE RSC /l 0x409 /d "_DEBUG" -# ADD RSC /l 0x409 /d "_DEBUG" -BSC32=bscmake.exe -# ADD BASE BSC32 /nologo -# ADD BSC32 /nologo -LINK32=link.exe -# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:console /debug /machine:I386 /pdbtype:sept -# ADD LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:console /debug /machine:I386 /pdbtype:sept - -!ENDIF - -# Begin Target - -# Name "unicode - Win32 Release" -# Name "unicode - Win32 Debug" -# Begin Source File - -SOURCE=..\unicode.c -# End Source File -# End Target -# End Project From ed59229a45e69c4a00b6e4ce1df0b21fa3d8e701 Mon Sep 17 00:00:00 2001 From: Brandon DeRosier Date: Mon, 28 Feb 2022 16:32:50 -0800 Subject: [PATCH 321/433] Add square cap (#48) --- impeller/entity/contents.cc | 18 +++++++- impeller/entity/entity_unittests.cc | 70 ++++++++++++++++++++++++++--- 2 files changed, 81 insertions(+), 7 deletions(-) diff --git a/impeller/entity/contents.cc b/impeller/entity/contents.cc index d5a2610245aba..62c6c89984c33 100644 --- a/impeller/entity/contents.cc +++ b/impeller/entity/contents.cc @@ -459,7 +459,23 @@ void SolidStrokeContents::SetStrokeCap(Cap cap) { FML_DLOG(ERROR) << "Unimplemented."; break; case Cap::kSquare: - FML_DLOG(ERROR) << "Unimplemented."; + cap_proc_ = [](VertexBufferBuilder& vtx_builder, + const Point& position, const Point& normal) { + SolidStrokeVertexShader::PerVertexData vtx; + vtx.vertex_position = position; + vtx.pen_down = 1.0; + + Point forward(normal.y, -normal.x); + + vtx.vertex_normal = normal; + vtx_builder.AppendVertex(vtx); + vtx.vertex_normal = -normal; + vtx_builder.AppendVertex(vtx); + vtx.vertex_normal = normal + forward; + vtx_builder.AppendVertex(vtx); + vtx.vertex_normal = -normal + forward; + vtx_builder.AppendVertex(vtx); + }; break; } } diff --git a/impeller/entity/entity_unittests.cc b/impeller/entity/entity_unittests.cc index 704e64b4379d1..25610122880b1 100644 --- a/impeller/entity/entity_unittests.cc +++ b/impeller/entity/entity_unittests.cc @@ -79,6 +79,51 @@ TEST_F(EntityTest, TriangleInsideASquare) { ASSERT_TRUE(OpenPlaygroundHere(callback)); } +TEST_F(EntityTest, StrokeCapAndJoinTest) { + auto callback = [&](ContentContext& context, RenderPass& pass) { + Entity entity; + + auto create_contents = [](SolidStrokeContents::Cap cap) { + auto contents = std::make_unique(); + contents->SetColor(Color::Red()); + contents->SetStrokeSize(20.0); + contents->SetStrokeCap(cap); + return contents; + }; + + const Point a_def(100, 100), b_def(100, 150), c_def(200, 100), + d_def(200, 50); + const Scalar r = 10; + + { + Point off(0, 0); + Point a, b, c, d; + std::tie(a, b) = IMPELLER_PLAYGROUND_LINE(off + a_def, off + b_def, r, + Color::Black(), Color::White()); + std::tie(c, d) = IMPELLER_PLAYGROUND_LINE(off + c_def, off + d_def, r, + Color::Black(), Color::White()); + entity.SetPath(PathBuilder{}.AddCubicCurve(a, b, d, c).TakePath()); + entity.SetContents(create_contents(SolidStrokeContents::Cap::kButt)); + entity.Render(context, pass); + } + + { + Point off(0, 100); + Point a, b, c, d; + std::tie(a, b) = IMPELLER_PLAYGROUND_LINE(off + a_def, off + b_def, r, + Color::Black(), Color::White()); + std::tie(c, d) = IMPELLER_PLAYGROUND_LINE(off + c_def, off + d_def, r, + Color::Black(), Color::White()); + entity.SetPath(PathBuilder{}.AddCubicCurve(a, b, d, c).TakePath()); + entity.SetContents(create_contents(SolidStrokeContents::Cap::kSquare)); + entity.Render(context, pass); + } + + return true; + }; + ASSERT_TRUE(OpenPlaygroundHere(callback)); +} + TEST_F(EntityTest, CubicCurveTest) { // Compare with https://fiddle.skia.org/c/b3625f26122c9de7afe7794fcf25ead3 Path path = @@ -329,12 +374,25 @@ TEST_F(EntityTest, CubicCurveAndOverlapTest) { ASSERT_TRUE(OpenPlaygroundHere(entity)); } -TEST_F(EntityTest, SolidStrokeContentsSetStrokeDefaults) { - SolidStrokeContents stroke; - ASSERT_EQ(stroke.GetStrokeCap(), SolidStrokeContents::Cap::kButt); - ASSERT_EQ(stroke.GetStrokeJoin(), SolidStrokeContents::Join::kBevel); - // TODO(99089): Test that SetStroke[Cap|Join] works once there are multiple - // caps and joins. +TEST_F(EntityTest, SolidStrokeContentsSetStrokeCapsAndJoins) { + { + SolidStrokeContents stroke; + // Defaults. + ASSERT_EQ(stroke.GetStrokeCap(), SolidStrokeContents::Cap::kButt); + ASSERT_EQ(stroke.GetStrokeJoin(), SolidStrokeContents::Join::kBevel); + } + + { + SolidStrokeContents stroke; + stroke.SetStrokeCap(SolidStrokeContents::Cap::kSquare); + ASSERT_EQ(stroke.GetStrokeCap(), SolidStrokeContents::Cap::kSquare); + } + + { + SolidStrokeContents stroke; + stroke.SetStrokeCap(SolidStrokeContents::Cap::kRound); + ASSERT_EQ(stroke.GetStrokeCap(), SolidStrokeContents::Cap::kRound); + } } } // namespace testing From b9de1e53578fb8daef26a79f871e01a5c40e4dd7 Mon Sep 17 00:00:00 2001 From: Brandon DeRosier Date: Wed, 2 Mar 2022 12:39:34 -0800 Subject: [PATCH 322/433] Point: Add reflect and make scalar ops more flexible (#51) --- impeller/geometry/geometry_unittests.cc | 58 +++++++++++++++++++++++++ impeller/geometry/point.h | 33 +++++++++++--- 2 files changed, 84 insertions(+), 7 deletions(-) diff --git a/impeller/geometry/geometry_unittests.cc b/impeller/geometry/geometry_unittests.cc index c663764512f57..3c3d9314ee486 100644 --- a/impeller/geometry/geometry_unittests.cc +++ b/impeller/geometry/geometry_unittests.cc @@ -3,6 +3,7 @@ // found in the LICENSE file. #include "impeller/geometry/geometry_unittests.h" +#include #include "flutter/testing/testing.h" #include "impeller/geometry/path.h" #include "impeller/geometry/path_builder.h" @@ -279,6 +280,38 @@ TEST(GeometryTest, CanPerformAlgebraicPointOps) { } } +TEST(GeometryTest, CanPerformAlgebraicPointOpsWithArithmeticTypes) { + // LHS + { + IPoint p1(1, 2); + IPoint p2 = p1 * 2.0f; + ASSERT_EQ(p2.x, 2u); + ASSERT_EQ(p2.y, 4u); + } + + { + IPoint p1(2, 6); + IPoint p2 = p1 / 2.0f; + ASSERT_EQ(p2.x, 1u); + ASSERT_EQ(p2.y, 3u); + } + + // RHS + { + IPoint p1(1, 2); + IPoint p2 = 2.0f * p1; + ASSERT_EQ(p2.x, 2u); + ASSERT_EQ(p2.y, 4u); + } + + { + IPoint p1(2, 6); + IPoint p2 = 12.0f / p1; + ASSERT_EQ(p2.x, 6u); + ASSERT_EQ(p2.y, 2u); + } +} + TEST(GeometryTest, PointIntegerCoercesToFloat) { // Integer on LHS, float on RHS { @@ -499,6 +532,31 @@ TEST(GeometryTest, PointCrossProduct) { } } +TEST(GeometryTest, PointReflect) { + { + Point axis = Point(0, 1); + Point a(2, 3); + auto reflected = a.Reflect(axis); + auto expected = Point(2, -3); + ASSERT_POINT_NEAR(reflected, expected); + } + + { + Point axis = Point(1, 1).Normalize(); + Point a(1, 0); + auto reflected = a.Reflect(axis); + auto expected = Point(0, -1); + ASSERT_POINT_NEAR(reflected, expected); + } + + { + Point axis = Point(1, 1).Normalize(); + Point a(-1, -1); + auto reflected = a.Reflect(axis); + ASSERT_POINT_NEAR(reflected, -a); + } +} + TEST(GeometryTest, CanConvertBetweenDegressAndRadians) { { auto deg = Degrees{90.0}; diff --git a/impeller/geometry/point.h b/impeller/geometry/point.h index f41184f0b867e..c334993c2de68 100644 --- a/impeller/geometry/point.h +++ b/impeller/geometry/point.h @@ -8,6 +8,7 @@ #include #include #include +#include #include "impeller/geometry/scalar.h" #include "impeller/geometry/size.h" @@ -121,8 +122,9 @@ struct TPoint { return {x - static_cast(s.width), y - static_cast(s.height)}; } - constexpr TPoint operator*(Scalar scale) const { - return {x * scale, y * scale}; + template >> + constexpr TPoint operator*(U scale) const { + return {static_cast(x * scale), static_cast(y * scale)}; } constexpr TPoint operator*(const TPoint& p) const { @@ -134,7 +136,10 @@ struct TPoint { return {x * static_cast(s.width), y * static_cast(s.height)}; } - constexpr TPoint operator/(Scalar d) const { return {x / d, y / d}; } + template >> + constexpr TPoint operator/(U d) const { + return {static_cast(x / d), static_cast(y / d)}; + } constexpr TPoint operator/(const TPoint& p) const { return {x / p.x, y / p.y}; @@ -175,11 +180,13 @@ struct TPoint { return {x / length, y / length}; } - constexpr Scalar Cross(const TPoint& p) const { - return (x * p.y) - (y * p.x); - } + constexpr Type Cross(const TPoint& p) const { return (x * p.y) - (y * p.x); } - constexpr Scalar Dot(const TPoint& p) const { return (x * p.x) + (y * p.y); } + constexpr Type Dot(const TPoint& p) const { return (x * p.x) + (y * p.y); } + + constexpr TPoint Reflect(const TPoint& axis) const { + return *this - axis * this->Dot(axis) * 2; + } constexpr bool IsZero() const { return x == 0 && y == 0; } }; @@ -226,6 +233,18 @@ constexpr TPoint operator/(const TPoint& p1, const TPoint& p2) { return {static_cast(p1.x) / p2.x, static_cast(p1.y) / p2.y}; } +// RHS algebraic operations with arithmetic types. + +template >> +constexpr TPoint operator*(U s, const TPoint& p) { + return p * s; +} + +template >> +constexpr TPoint operator/(U s, const TPoint& p) { + return {static_cast(s) / p.x, static_cast(s) / p.y}; +} + // RHS algebraic operations with TSize. template From 36a085691874f50c6fdf4908bf3534fb0266404e Mon Sep 17 00:00:00 2001 From: Brandon DeRosier Date: Wed, 2 Mar 2022 12:49:40 -0800 Subject: [PATCH 323/433] Add miter join (#49) --- impeller/entity/contents.cc | 83 ++++++++++++++++++------- impeller/entity/contents.h | 9 +-- impeller/entity/entity_unittests.cc | 65 +++++++++++++++++-- impeller/geometry/geometry_unittests.cc | 10 +++ impeller/geometry/scalar.h | 9 +++ 5 files changed, 145 insertions(+), 31 deletions(-) diff --git a/impeller/entity/contents.cc b/impeller/entity/contents.cc index 62c6c89984c33..3e99da63f950e 100644 --- a/impeller/entity/contents.cc +++ b/impeller/entity/contents.cc @@ -11,6 +11,7 @@ #include "impeller/entity/content_context.h" #include "impeller/entity/entity.h" #include "impeller/geometry/path_builder.h" +#include "impeller/geometry/scalar.h" #include "impeller/geometry/vector.h" #include "impeller/renderer/render_pass.h" #include "impeller/renderer/sampler_library.h" @@ -284,8 +285,7 @@ const IRect& TextureContents::GetSourceRect() const { SolidStrokeContents::SolidStrokeContents() { SetStrokeCap(Cap::kButt); - // TODO(99089): Change this to kMiter once implemented. - SetStrokeJoin(Join::kBevel); + SetStrokeJoin(Join::kMiter); } SolidStrokeContents::~SolidStrokeContents() = default; @@ -302,7 +302,8 @@ static VertexBuffer CreateSolidStrokeVertices( const Path& path, HostBuffer& buffer, const SolidStrokeContents::CapProc& cap_proc, - const SolidStrokeContents::JoinProc& join_proc) { + const SolidStrokeContents::JoinProc& join_proc, + Scalar miter_limit) { using VS = SolidStrokeVertexShader; VertexBufferBuilder vtx_builder; @@ -380,7 +381,7 @@ static VertexBuffer CreateSolidStrokeVertices( // Generate join from the current line to the next line. join_proc(vtx_builder, polyline.points[point_i], previous_normal, - normal); + normal, miter_limit); } } } @@ -390,7 +391,7 @@ static VertexBuffer CreateSolidStrokeVertices( cap_proc(vtx_builder, polyline.points[contour_end_point_i - 1], normal); } else { join_proc(vtx_builder, polyline.points[contour_start_point_i], normal, - contour_first_normal); + contour_first_normal, miter_limit); } } @@ -419,8 +420,9 @@ bool SolidStrokeContents::Render(const ContentContext& renderer, cmd.label = "SolidStroke"; cmd.pipeline = renderer.GetSolidStrokePipeline(OptionsFromPass(pass)); cmd.stencil_reference = entity.GetStencilDepth(); - cmd.BindVertices(CreateSolidStrokeVertices( - entity.GetPath(), pass.GetTransientsBuffer(), cap_proc_, join_proc_)); + cmd.BindVertices( + CreateSolidStrokeVertices(entity.GetPath(), pass.GetTransientsBuffer(), + cap_proc_, join_proc_, miter_limit_)); VS::BindFrameInfo(cmd, pass.GetTransientsBuffer().EmplaceUniform(frame_info)); VS::BindStrokeInfo(cmd, pass.GetTransientsBuffer().EmplaceUniform(stroke_info)); @@ -438,12 +440,15 @@ Scalar SolidStrokeContents::GetStrokeSize() const { return stroke_size_; } -void SolidStrokeContents::SetStrokeMiter(Scalar miter) { - miter_ = miter; +void SolidStrokeContents::SetStrokeMiter(Scalar miter_limit) { + if (miter_limit < 0) { + return; // Skia behaves like this. + } + miter_limit_ = miter_limit; } -Scalar SolidStrokeContents::GetStrokeMiter(Scalar miter) { - return miter_; +Scalar SolidStrokeContents::GetStrokeMiter() { + return miter_limit_; } void SolidStrokeContents::SetStrokeCap(Cap cap) { @@ -484,6 +489,26 @@ SolidStrokeContents::Cap SolidStrokeContents::GetStrokeCap() { return cap_; } +static Scalar CreateBevelAndGetDirection( + VertexBufferBuilder& vtx_builder, + const Point& position, + const Point& start_normal, + const Point& end_normal) { + SolidStrokeVertexShader::PerVertexData vtx; + vtx.vertex_position = position; + vtx.pen_down = 1.0; + vtx.vertex_normal = {}; + vtx_builder.AppendVertex(vtx); + + Scalar dir = start_normal.Cross(end_normal) > 0 ? -1 : 1; + vtx.vertex_normal = start_normal * dir; + vtx_builder.AppendVertex(vtx); + vtx.vertex_normal = end_normal * dir; + vtx_builder.AppendVertex(vtx); + + return dir; +} + void SolidStrokeContents::SetStrokeJoin(Join join) { join_ = join; @@ -492,23 +517,37 @@ void SolidStrokeContents::SetStrokeJoin(Join join) { case Join::kBevel: join_proc_ = [](VertexBufferBuilder& vtx_builder, const Point& position, const Point& start_normal, - const Point& end_normal) { + const Point& end_normal, Scalar miter_limit) { + CreateBevelAndGetDirection(vtx_builder, position, start_normal, end_normal); + }; + break; + case Join::kMiter: + join_proc_ = [](VertexBufferBuilder& vtx_builder, + const Point& position, const Point& start_normal, + const Point& end_normal, Scalar miter_limit) { + // 1 for no joint (straight line), 0 for max joint (180 degrees). + Scalar alignment = (start_normal.Dot(end_normal) + 1) / 2; + if (ScalarNearlyEqual(alignment, 1)) { + return; + } + + Scalar dir = + CreateBevelAndGetDirection(vtx_builder, position, start_normal, end_normal); + + Point miter_point = (start_normal + end_normal) / 2 / alignment; + if (miter_point.GetDistanceSquared({0, 0}) > + miter_limit * miter_limit) { + return; // Convert to bevel when we exceed the miter limit. + } + + // Outer miter point. SolidStrokeVertexShader::PerVertexData vtx; vtx.vertex_position = position; vtx.pen_down = 1.0; - vtx.vertex_normal = {}; - vtx_builder.AppendVertex(vtx); - - Scalar dir = start_normal.Cross(end_normal) > 0 ? -1 : 1; - vtx.vertex_normal = start_normal * dir; - vtx_builder.AppendVertex(vtx); - vtx.vertex_normal = end_normal * dir; + vtx.vertex_normal = miter_point * dir; vtx_builder.AppendVertex(vtx); }; break; - case Join::kMiter: - FML_DLOG(ERROR) << "Unimplemented."; - break; case Join::kRound: FML_DLOG(ERROR) << "Unimplemented."; break; diff --git a/impeller/entity/contents.h b/impeller/entity/contents.h index 2e03096d78e40..4178dfb6838fc 100644 --- a/impeller/entity/contents.h +++ b/impeller/entity/contents.h @@ -137,7 +137,8 @@ class SolidStrokeContents final : public Contents { VertexBufferBuilder& vtx_builder, const Point& position, const Point& start_normal, - const Point& end_normal)>; + const Point& end_normal, + Scalar miter_limit)>; SolidStrokeContents(); @@ -151,9 +152,9 @@ class SolidStrokeContents final : public Contents { Scalar GetStrokeSize() const; - void SetStrokeMiter(Scalar miter); + void SetStrokeMiter(Scalar miter_limit); - Scalar GetStrokeMiter(Scalar miter); + Scalar GetStrokeMiter(); void SetStrokeCap(Cap cap); @@ -171,7 +172,7 @@ class SolidStrokeContents final : public Contents { private: Color color_; Scalar stroke_size_ = 0.0; - Scalar miter_ = 0.0; + Scalar miter_limit_ = 4.0; Cap cap_; CapProc cap_proc_; diff --git a/impeller/entity/entity_unittests.cc b/impeller/entity/entity_unittests.cc index 25610122880b1..9db0270f832ac 100644 --- a/impeller/entity/entity_unittests.cc +++ b/impeller/entity/entity_unittests.cc @@ -4,6 +4,7 @@ #include "entity/contents.h" #include "flutter/testing/testing.h" +#include "imgui.h" #include "impeller/entity/entity.h" #include "impeller/entity/entity_playground.h" #include "impeller/geometry/path_builder.h" @@ -83,18 +84,32 @@ TEST_F(EntityTest, StrokeCapAndJoinTest) { auto callback = [&](ContentContext& context, RenderPass& pass) { Entity entity; - auto create_contents = [](SolidStrokeContents::Cap cap) { + ImGui::SetNextWindowSize({300, 60}); + ImGui::SetNextWindowPos({100, 300}); + ImGui::Begin("Controls"); + // Slightly above sqrt(2) by default, so that right angles are just below + // the limit and acute angles are over the limit (causing them to get + // beveled). + static Scalar miter_limit = 1.41421357; + ImGui::SliderFloat("Miter limit", &miter_limit, 0, 30); + ImGui::End(); + + auto create_contents = [](SolidStrokeContents::Cap cap, + SolidStrokeContents::Join join) { auto contents = std::make_unique(); contents->SetColor(Color::Red()); contents->SetStrokeSize(20.0); contents->SetStrokeCap(cap); + contents->SetStrokeJoin(join); + contents->SetStrokeMiter(miter_limit); return contents; }; const Point a_def(100, 100), b_def(100, 150), c_def(200, 100), - d_def(200, 50); + d_def(200, 50), e_def(150, 150); const Scalar r = 10; + // Cap::kButt demo. { Point off(0, 0); Point a, b, c, d; @@ -103,10 +118,12 @@ TEST_F(EntityTest, StrokeCapAndJoinTest) { std::tie(c, d) = IMPELLER_PLAYGROUND_LINE(off + c_def, off + d_def, r, Color::Black(), Color::White()); entity.SetPath(PathBuilder{}.AddCubicCurve(a, b, d, c).TakePath()); - entity.SetContents(create_contents(SolidStrokeContents::Cap::kButt)); + entity.SetContents(create_contents(SolidStrokeContents::Cap::kButt, + SolidStrokeContents::Join::kBevel)); entity.Render(context, pass); } + // Cap::kSquare demo. { Point off(0, 100); Point a, b, c, d; @@ -115,7 +132,34 @@ TEST_F(EntityTest, StrokeCapAndJoinTest) { std::tie(c, d) = IMPELLER_PLAYGROUND_LINE(off + c_def, off + d_def, r, Color::Black(), Color::White()); entity.SetPath(PathBuilder{}.AddCubicCurve(a, b, d, c).TakePath()); - entity.SetContents(create_contents(SolidStrokeContents::Cap::kSquare)); + entity.SetContents(create_contents(SolidStrokeContents::Cap::kSquare, + SolidStrokeContents::Join::kBevel)); + entity.Render(context, pass); + } + + // Join::kBevel demo. + { + Point off(200, 0); + Point a = IMPELLER_PLAYGROUND_POINT(off + a_def, r, Color::White()); + Point b = IMPELLER_PLAYGROUND_POINT(off + e_def, r, Color::White()); + Point c = IMPELLER_PLAYGROUND_POINT(off + c_def, r, Color::White()); + entity.SetPath( + PathBuilder{}.MoveTo(a).LineTo(b).LineTo(c).Close().TakePath()); + entity.SetContents(create_contents(SolidStrokeContents::Cap::kButt, + SolidStrokeContents::Join::kBevel)); + entity.Render(context, pass); + } + + // Join::kMiter demo. + { + Point off(200, 100); + Point a = IMPELLER_PLAYGROUND_POINT(off + a_def, r, Color::White()); + Point b = IMPELLER_PLAYGROUND_POINT(off + e_def, r, Color::White()); + Point c = IMPELLER_PLAYGROUND_POINT(off + c_def, r, Color::White()); + entity.SetPath( + PathBuilder{}.MoveTo(a).LineTo(b).LineTo(c).Close().TakePath()); + entity.SetContents(create_contents(SolidStrokeContents::Cap::kButt, + SolidStrokeContents::Join::kMiter)); entity.Render(context, pass); } @@ -379,7 +423,7 @@ TEST_F(EntityTest, SolidStrokeContentsSetStrokeCapsAndJoins) { SolidStrokeContents stroke; // Defaults. ASSERT_EQ(stroke.GetStrokeCap(), SolidStrokeContents::Cap::kButt); - ASSERT_EQ(stroke.GetStrokeJoin(), SolidStrokeContents::Join::kBevel); + ASSERT_EQ(stroke.GetStrokeJoin(), SolidStrokeContents::Join::kMiter); } { @@ -395,5 +439,16 @@ TEST_F(EntityTest, SolidStrokeContentsSetStrokeCapsAndJoins) { } } +TEST_F(EntityTest, SolidStrokeContentsSetMiter) { + SolidStrokeContents contents; + ASSERT_FLOAT_EQ(contents.GetStrokeMiter(), 4); + + contents.SetStrokeMiter(8); + ASSERT_FLOAT_EQ(contents.GetStrokeMiter(), 8); + + contents.SetStrokeMiter(-1); + ASSERT_FLOAT_EQ(contents.GetStrokeMiter(), 8); +} + } // namespace testing } // namespace impeller diff --git a/impeller/geometry/geometry_unittests.cc b/impeller/geometry/geometry_unittests.cc index 3c3d9314ee486..6c4f2a867754c 100644 --- a/impeller/geometry/geometry_unittests.cc +++ b/impeller/geometry/geometry_unittests.cc @@ -2,6 +2,7 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. +#include #include "impeller/geometry/geometry_unittests.h" #include #include "flutter/testing/testing.h" @@ -10,11 +11,20 @@ #include "impeller/geometry/path_component.h" #include "impeller/geometry/point.h" #include "impeller/geometry/rect.h" +#include "impeller/geometry/scalar.h" #include "impeller/geometry/size.h" namespace impeller { namespace testing { +TEST(GeometryTest, ScalarNearlyEqual) { + ASSERT_FALSE(ScalarNearlyEqual(0.002f, 0.001f)); + ASSERT_TRUE(ScalarNearlyEqual(0.002f, 0.001f, 0.0011f)); + ASSERT_FALSE(ScalarNearlyEqual(0.002f, 0.001f, 0.0009f)); + ASSERT_TRUE( + ScalarNearlyEqual(1.0f, 1.0f + std::numeric_limits::epsilon()*4)); +} + TEST(GeometryTest, RotationMatrix) { auto rotation = Matrix::MakeRotationZ(Radians{M_PI_4}); auto expect = Matrix{0.707, 0.707, 0, 0, // diff --git a/impeller/geometry/scalar.h b/impeller/geometry/scalar.h index 9e8cee0d58d50..918e1491cd12e 100644 --- a/impeller/geometry/scalar.h +++ b/impeller/geometry/scalar.h @@ -5,13 +5,22 @@ #pragma once #include +#include +#include "flutter/fml/logging.h" #include "impeller/geometry/constants.h" namespace impeller { using Scalar = float; +constexpr inline bool ScalarNearlyEqual(Scalar x, + Scalar y, + Scalar tolerance = 1e-3) { + FML_DCHECK(tolerance >= 0); + return std::abs(x - y) <= tolerance; +} + struct Degrees; struct Radians { From 53e59a11c3e7767f0a3ad01cddf5dd930daebcce Mon Sep 17 00:00:00 2001 From: Brandon DeRosier Date: Wed, 2 Mar 2022 13:04:27 -0800 Subject: [PATCH 324/433] Add round caps and joins (#52) --- impeller/entity/contents.cc | 104 +++++++++++++++++++++++----- impeller/entity/contents.h | 9 ++- impeller/entity/entity_unittests.cc | 70 +++++++++++++++---- impeller/geometry/path_builder.cc | 2 - impeller/geometry/path_builder.h | 9 +++ 5 files changed, 158 insertions(+), 36 deletions(-) diff --git a/impeller/entity/contents.cc b/impeller/entity/contents.cc index 3e99da63f950e..a1f5bcfa789a4 100644 --- a/impeller/entity/contents.cc +++ b/impeller/entity/contents.cc @@ -11,6 +11,7 @@ #include "impeller/entity/content_context.h" #include "impeller/entity/entity.h" #include "impeller/geometry/path_builder.h" +#include "impeller/geometry/path_component.h" #include "impeller/geometry/scalar.h" #include "impeller/geometry/vector.h" #include "impeller/renderer/render_pass.h" @@ -303,7 +304,8 @@ static VertexBuffer CreateSolidStrokeVertices( HostBuffer& buffer, const SolidStrokeContents::CapProc& cap_proc, const SolidStrokeContents::JoinProc& join_proc, - Scalar miter_limit) { + Scalar miter_limit, + const SmoothingApproximation& smoothing) { using VS = SolidStrokeVertexShader; VertexBufferBuilder vtx_builder; @@ -356,7 +358,8 @@ static VertexBuffer CreateSolidStrokeVertices( // Generate start cap. if (!polyline.contours[contour_i].is_closed) { - cap_proc(vtx_builder, polyline.points[contour_start_point_i], -normal); + cap_proc(vtx_builder, polyline.points[contour_start_point_i], -normal, + smoothing); } // Generate contour geometry. @@ -381,17 +384,18 @@ static VertexBuffer CreateSolidStrokeVertices( // Generate join from the current line to the next line. join_proc(vtx_builder, polyline.points[point_i], previous_normal, - normal, miter_limit); + normal, miter_limit, smoothing); } } } // Generate end cap or join. if (!polyline.contours[contour_i].is_closed) { - cap_proc(vtx_builder, polyline.points[contour_end_point_i - 1], normal); + cap_proc(vtx_builder, polyline.points[contour_end_point_i - 1], normal, + smoothing); } else { join_proc(vtx_builder, polyline.points[contour_start_point_i], normal, - contour_first_normal, miter_limit); + contour_first_normal, miter_limit, smoothing); } } @@ -420,9 +424,9 @@ bool SolidStrokeContents::Render(const ContentContext& renderer, cmd.label = "SolidStroke"; cmd.pipeline = renderer.GetSolidStrokePipeline(OptionsFromPass(pass)); cmd.stencil_reference = entity.GetStencilDepth(); - cmd.BindVertices( - CreateSolidStrokeVertices(entity.GetPath(), pass.GetTransientsBuffer(), - cap_proc_, join_proc_, miter_limit_)); + cmd.BindVertices(CreateSolidStrokeVertices( + entity.GetPath(), pass.GetTransientsBuffer(), cap_proc_, join_proc_, + miter_limit_, arc_smoothing_approximation_)); VS::BindFrameInfo(cmd, pass.GetTransientsBuffer().EmplaceUniform(frame_info)); VS::BindStrokeInfo(cmd, pass.GetTransientsBuffer().EmplaceUniform(stroke_info)); @@ -434,6 +438,7 @@ bool SolidStrokeContents::Render(const ContentContext& renderer, void SolidStrokeContents::SetStrokeSize(Scalar size) { stroke_size_ = size; + arc_smoothing_approximation_ = SmoothingApproximation(5.0 / size, 0.0, 0.0); } Scalar SolidStrokeContents::GetStrokeSize() const { @@ -458,14 +463,41 @@ void SolidStrokeContents::SetStrokeCap(Cap cap) { switch (cap) { case Cap::kButt: cap_proc_ = [](VertexBufferBuilder& vtx_builder, - const Point& position, const Point& normal) {}; + const Point& position, const Point& normal, + const SmoothingApproximation& smoothing) {}; break; case Cap::kRound: - FML_DLOG(ERROR) << "Unimplemented."; + cap_proc_ = [](VertexBufferBuilder& vtx_builder, + const Point& position, const Point& normal, + const SmoothingApproximation& smoothing) { + SolidStrokeVertexShader::PerVertexData vtx; + vtx.vertex_position = position; + vtx.pen_down = 1.0; + + Point forward(normal.y, -normal.x); + + auto arc_points = + CubicPathComponent( + normal, normal + forward * PathBuilder::kArcApproximationMagic, + forward + normal * PathBuilder::kArcApproximationMagic, forward) + .CreatePolyline(smoothing); + + vtx.vertex_normal = normal; + vtx_builder.AppendVertex(vtx); + vtx.vertex_normal = -normal; + vtx_builder.AppendVertex(vtx); + for (const auto& point : arc_points) { + vtx.vertex_normal = point; + vtx_builder.AppendVertex(vtx); + vtx.vertex_normal = (-point).Reflect(forward); + vtx_builder.AppendVertex(vtx); + } + }; break; case Cap::kSquare: cap_proc_ = [](VertexBufferBuilder& vtx_builder, - const Point& position, const Point& normal) { + const Point& position, const Point& normal, + const SmoothingApproximation& smoothing) { SolidStrokeVertexShader::PerVertexData vtx; vtx.vertex_position = position; vtx.pen_down = 1.0; @@ -517,22 +549,25 @@ void SolidStrokeContents::SetStrokeJoin(Join join) { case Join::kBevel: join_proc_ = [](VertexBufferBuilder& vtx_builder, const Point& position, const Point& start_normal, - const Point& end_normal, Scalar miter_limit) { - CreateBevelAndGetDirection(vtx_builder, position, start_normal, end_normal); + const Point& end_normal, Scalar miter_limit, + const SmoothingApproximation& smoothing) { + CreateBevelAndGetDirection(vtx_builder, position, start_normal, + end_normal); }; break; case Join::kMiter: join_proc_ = [](VertexBufferBuilder& vtx_builder, const Point& position, const Point& start_normal, - const Point& end_normal, Scalar miter_limit) { + const Point& end_normal, Scalar miter_limit, + const SmoothingApproximation& smoothing) { // 1 for no joint (straight line), 0 for max joint (180 degrees). Scalar alignment = (start_normal.Dot(end_normal) + 1) / 2; if (ScalarNearlyEqual(alignment, 1)) { return; } - Scalar dir = - CreateBevelAndGetDirection(vtx_builder, position, start_normal, end_normal); + Scalar dir = CreateBevelAndGetDirection(vtx_builder, position, + start_normal, end_normal); Point miter_point = (start_normal + end_normal) / 2 / alignment; if (miter_point.GetDistanceSquared({0, 0}) > @@ -549,7 +584,42 @@ void SolidStrokeContents::SetStrokeJoin(Join join) { }; break; case Join::kRound: - FML_DLOG(ERROR) << "Unimplemented."; + join_proc_ = [](VertexBufferBuilder& vtx_builder, + const Point& position, const Point& start_normal, + const Point& end_normal, Scalar miter_limit, + const SmoothingApproximation& smoothing) { + // 0 for no joint (straight line), 1 for max joint (180 degrees). + Scalar alignment = 1 - (start_normal.Dot(end_normal) + 1) / 2; + if (ScalarNearlyEqual(alignment, 0)) { + return; + } + + Scalar dir = + CreateBevel(vtx_builder, position, start_normal, end_normal); + + Point middle = (start_normal + end_normal).Normalize(); + Point middle_handle = middle + Point(-middle.y, middle.x) * + PathBuilder::kArcApproximationMagic * + alignment * dir; + Point start_handle = + start_normal + Point(start_normal.y, -start_normal.x) * + PathBuilder::kArcApproximationMagic * alignment * + dir; + + auto arc_points = CubicPathComponent(start_normal, start_handle, + middle_handle, middle) + .CreatePolyline(smoothing); + + SolidStrokeVertexShader::PerVertexData vtx; + vtx.vertex_position = position; + vtx.pen_down = 1.0; + for (const auto& point : arc_points) { + vtx.vertex_normal = point * dir; + vtx_builder.AppendVertex(vtx); + vtx.vertex_normal = (-point * dir).Reflect(middle); + vtx_builder.AppendVertex(vtx); + } + }; break; } } diff --git a/impeller/entity/contents.h b/impeller/entity/contents.h index 4178dfb6838fc..f3803ff4d0222 100644 --- a/impeller/entity/contents.h +++ b/impeller/entity/contents.h @@ -11,6 +11,7 @@ #include "flutter/fml/macros.h" #include "impeller/entity/solid_stroke.vert.h" #include "impeller/geometry/color.h" +#include "impeller/geometry/path_component.h" #include "impeller/geometry/point.h" #include "impeller/geometry/rect.h" #include "impeller/renderer/texture.h" @@ -132,13 +133,15 @@ class SolidStrokeContents final : public Contents { using CapProc = std::function& vtx_builder, const Point& position, - const Point& normal)>; + const Point& normal, + const SmoothingApproximation& smoothing)>; using JoinProc = std::function& vtx_builder, const Point& position, const Point& start_normal, const Point& end_normal, - Scalar miter_limit)>; + Scalar miter_limit, + const SmoothingApproximation& smoothing)>; SolidStrokeContents(); @@ -170,6 +173,8 @@ class SolidStrokeContents final : public Contents { RenderPass& pass) const override; private: + SmoothingApproximation arc_smoothing_approximation_; + Color color_; Scalar stroke_size_ = 0.0; Scalar miter_limit_ = 4.0; diff --git a/impeller/entity/entity_unittests.cc b/impeller/entity/entity_unittests.cc index 9db0270f832ac..927a32e3b8d31 100644 --- a/impeller/entity/entity_unittests.cc +++ b/impeller/entity/entity_unittests.cc @@ -4,12 +4,12 @@ #include "entity/contents.h" #include "flutter/testing/testing.h" -#include "imgui.h" #include "impeller/entity/entity.h" #include "impeller/entity/entity_playground.h" #include "impeller/geometry/path_builder.h" #include "impeller/playground/playground.h" #include "impeller/playground/widgets.h" +#include "third_party/imgui/imgui.h" namespace impeller { namespace testing { @@ -81,37 +81,50 @@ TEST_F(EntityTest, TriangleInsideASquare) { } TEST_F(EntityTest, StrokeCapAndJoinTest) { - auto callback = [&](ContentContext& context, RenderPass& pass) { - Entity entity; + const Point padding(300, 250); + const Point margin(140, 180); - ImGui::SetNextWindowSize({300, 60}); - ImGui::SetNextWindowPos({100, 300}); + bool first_frame = true; + auto callback = [&](ContentContext& context, RenderPass& pass) { + if (first_frame) { + first_frame = false; + ImGui::SetNextWindowSize({300, 100}); + ImGui::SetNextWindowPos( + {0 * padding.x + margin.x, 1.7f * padding.y + margin.y}); + } ImGui::Begin("Controls"); // Slightly above sqrt(2) by default, so that right angles are just below // the limit and acute angles are over the limit (causing them to get // beveled). static Scalar miter_limit = 1.41421357; + static Scalar width = 30; ImGui::SliderFloat("Miter limit", &miter_limit, 0, 30); + ImGui::SliderFloat("Stroke width", &width, 0, 100); + if (ImGui::Button("Reset")) { + miter_limit = 1.41421357; + width = 30; + } ImGui::End(); - auto create_contents = [](SolidStrokeContents::Cap cap, - SolidStrokeContents::Join join) { + auto create_contents = [width = width](SolidStrokeContents::Cap cap, + SolidStrokeContents::Join join) { auto contents = std::make_unique(); contents->SetColor(Color::Red()); - contents->SetStrokeSize(20.0); + contents->SetStrokeSize(width); contents->SetStrokeCap(cap); contents->SetStrokeJoin(join); contents->SetStrokeMiter(miter_limit); return contents; }; - const Point a_def(100, 100), b_def(100, 150), c_def(200, 100), - d_def(200, 50), e_def(150, 150); - const Scalar r = 10; + Entity entity; + const Point a_def(0, 0), b_def(0, 100), c_def(150, 0), d_def(150, -100), + e_def(75, 75); + const Scalar r = 30; // Cap::kButt demo. { - Point off(0, 0); + Point off = Point(0, 0) * padding + margin; Point a, b, c, d; std::tie(a, b) = IMPELLER_PLAYGROUND_LINE(off + a_def, off + b_def, r, Color::Black(), Color::White()); @@ -125,7 +138,7 @@ TEST_F(EntityTest, StrokeCapAndJoinTest) { // Cap::kSquare demo. { - Point off(0, 100); + Point off = Point(1, 0) * padding + margin; Point a, b, c, d; std::tie(a, b) = IMPELLER_PLAYGROUND_LINE(off + a_def, off + b_def, r, Color::Black(), Color::White()); @@ -137,9 +150,23 @@ TEST_F(EntityTest, StrokeCapAndJoinTest) { entity.Render(context, pass); } + // Cap::kRound demo. + { + Point off = Point(2, 0) * padding + margin; + Point a, b, c, d; + std::tie(a, b) = IMPELLER_PLAYGROUND_LINE(off + a_def, off + b_def, r, + Color::Black(), Color::White()); + std::tie(c, d) = IMPELLER_PLAYGROUND_LINE(off + c_def, off + d_def, r, + Color::Black(), Color::White()); + entity.SetPath(PathBuilder{}.AddCubicCurve(a, b, d, c).TakePath()); + entity.SetContents(create_contents(SolidStrokeContents::Cap::kRound, + SolidStrokeContents::Join::kBevel)); + entity.Render(context, pass); + } + // Join::kBevel demo. { - Point off(200, 0); + Point off = Point(0, 1) * padding + margin; Point a = IMPELLER_PLAYGROUND_POINT(off + a_def, r, Color::White()); Point b = IMPELLER_PLAYGROUND_POINT(off + e_def, r, Color::White()); Point c = IMPELLER_PLAYGROUND_POINT(off + c_def, r, Color::White()); @@ -152,7 +179,7 @@ TEST_F(EntityTest, StrokeCapAndJoinTest) { // Join::kMiter demo. { - Point off(200, 100); + Point off = Point(1, 1) * padding + margin; Point a = IMPELLER_PLAYGROUND_POINT(off + a_def, r, Color::White()); Point b = IMPELLER_PLAYGROUND_POINT(off + e_def, r, Color::White()); Point c = IMPELLER_PLAYGROUND_POINT(off + c_def, r, Color::White()); @@ -163,6 +190,19 @@ TEST_F(EntityTest, StrokeCapAndJoinTest) { entity.Render(context, pass); } + // Join::kRound demo. + { + Point off = Point(2, 1) * padding + margin; + Point a = IMPELLER_PLAYGROUND_POINT(off + a_def, r, Color::White()); + Point b = IMPELLER_PLAYGROUND_POINT(off + e_def, r, Color::White()); + Point c = IMPELLER_PLAYGROUND_POINT(off + c_def, r, Color::White()); + entity.SetPath( + PathBuilder{}.MoveTo(a).LineTo(b).LineTo(c).Close().TakePath()); + entity.SetContents(create_contents(SolidStrokeContents::Cap::kButt, + SolidStrokeContents::Join::kRound)); + entity.Render(context, pass); + } + return true; }; ASSERT_TRUE(OpenPlaygroundHere(callback)); diff --git a/impeller/geometry/path_builder.cc b/impeller/geometry/path_builder.cc index 1e97b52afba6f..81e269806c234 100644 --- a/impeller/geometry/path_builder.cc +++ b/impeller/geometry/path_builder.cc @@ -6,8 +6,6 @@ namespace impeller { -static const Scalar kArcApproximationMagic = 0.551915024494; - PathBuilder::PathBuilder() = default; PathBuilder::~PathBuilder() = default; diff --git a/impeller/geometry/path_builder.h b/impeller/geometry/path_builder.h index 09bd3b825665e..3c495e4f872a6 100644 --- a/impeller/geometry/path_builder.h +++ b/impeller/geometry/path_builder.h @@ -13,6 +13,15 @@ namespace impeller { class PathBuilder { public: + /// Used for approximating quarter circle arcs with cubic curves. This is the + /// control point distance which results in the smallest possible unit circle + /// integration for a right angle arc. It can be used to approximate arcs less + /// than 90 degrees to great effect by simply reducing it proportionally to + /// the angle. However, accuracy rapidly diminishes if magnified for obtuse + /// angle arcs, and so multiple cubic curves should be used when approximating + /// arcs greater than 90 degrees. + constexpr static const Scalar kArcApproximationMagic = 0.551915024494; + PathBuilder(); ~PathBuilder(); From 7e1764d4a595644eb7f4bc8232aa3640bdcabd48 Mon Sep 17 00:00:00 2001 From: Brandon DeRosier Date: Wed, 2 Mar 2022 17:22:51 -0800 Subject: [PATCH 325/433] Move Contents classes into separate translation units (#53) --- impeller/aiks/aiks_context.h | 2 +- impeller/aiks/canvas.cc | 3 + impeller/aiks/canvas.h | 1 + impeller/aiks/paint.cc | 2 + impeller/aiks/paint.h | 2 +- impeller/aiks/paint_pass_delegate.cc | 3 +- .../display_list/display_list_dispatcher.cc | 1 + impeller/entity/BUILD.gn | 20 +- impeller/entity/contents.cc | 826 ------------------ impeller/entity/contents.h | 248 ------ impeller/entity/contents/clip_contents.cc | 88 ++ impeller/entity/contents/clip_contents.h | 51 ++ .../entity/{ => contents}/content_context.cc | 2 +- .../entity/{ => contents}/content_context.h | 64 +- impeller/entity/contents/contents.cc | 22 + impeller/entity/contents/contents.h | 37 + .../contents/linear_gradient_contents.cc | 79 ++ .../contents/linear_gradient_contents.h | 43 + .../entity/contents/solid_color_contents.cc | 83 ++ .../entity/contents/solid_color_contents.h | 47 + .../entity/contents/solid_stroke_contents.cc | 358 ++++++++ .../entity/contents/solid_stroke_contents.h | 92 ++ impeller/entity/contents/text_contents.cc | 134 +++ impeller/entity/contents/text_contents.h | 45 + impeller/entity/contents/texture_contents.cc | 115 +++ impeller/entity/contents/texture_contents.h | 48 + impeller/entity/entity.cc | 2 +- impeller/entity/entity.h | 2 +- impeller/entity/entity_pass.cc | 2 +- impeller/entity/entity_pass.h | 2 +- impeller/entity/entity_pass_delegate.h | 2 +- impeller/entity/entity_playground.cc | 2 +- impeller/entity/entity_playground.h | 2 +- impeller/entity/entity_unittests.cc | 3 +- 34 files changed, 1315 insertions(+), 1118 deletions(-) delete mode 100644 impeller/entity/contents.cc delete mode 100644 impeller/entity/contents.h create mode 100644 impeller/entity/contents/clip_contents.cc create mode 100644 impeller/entity/contents/clip_contents.h rename impeller/entity/{ => contents}/content_context.cc (98%) rename impeller/entity/{ => contents}/content_context.h (72%) create mode 100644 impeller/entity/contents/contents.cc create mode 100644 impeller/entity/contents/contents.h create mode 100644 impeller/entity/contents/linear_gradient_contents.cc create mode 100644 impeller/entity/contents/linear_gradient_contents.h create mode 100644 impeller/entity/contents/solid_color_contents.cc create mode 100644 impeller/entity/contents/solid_color_contents.h create mode 100644 impeller/entity/contents/solid_stroke_contents.cc create mode 100644 impeller/entity/contents/solid_stroke_contents.h create mode 100644 impeller/entity/contents/text_contents.cc create mode 100644 impeller/entity/contents/text_contents.h create mode 100644 impeller/entity/contents/texture_contents.cc create mode 100644 impeller/entity/contents/texture_contents.h diff --git a/impeller/aiks/aiks_context.h b/impeller/aiks/aiks_context.h index edbdd604a491a..cbdd93ea2e2de 100644 --- a/impeller/aiks/aiks_context.h +++ b/impeller/aiks/aiks_context.h @@ -7,7 +7,7 @@ #include #include "flutter/fml/macros.h" -#include "impeller/entity/content_context.h" +#include "impeller/entity/contents/content_context.h" #include "impeller/renderer/context.h" namespace impeller { diff --git a/impeller/aiks/canvas.cc b/impeller/aiks/canvas.cc index f9c19fe3cc2d4..311655ffdfaa6 100644 --- a/impeller/aiks/canvas.cc +++ b/impeller/aiks/canvas.cc @@ -8,6 +8,9 @@ #include "flutter/fml/logging.h" #include "impeller/aiks/paint_pass_delegate.h" +#include "impeller/entity/contents/clip_contents.h" +#include "impeller/entity/contents/text_contents.h" +#include "impeller/entity/contents/texture_contents.h" #include "impeller/geometry/path_builder.h" namespace impeller { diff --git a/impeller/aiks/canvas.h b/impeller/aiks/canvas.h index 56a23aaa9426e..0dc6d810924b7 100644 --- a/impeller/aiks/canvas.h +++ b/impeller/aiks/canvas.h @@ -18,6 +18,7 @@ #include "impeller/geometry/path.h" #include "impeller/geometry/point.h" #include "impeller/geometry/vector.h" +#include "impeller/typographer/glyph_atlas.h" #include "impeller/typographer/text_frame.h" namespace impeller { diff --git a/impeller/aiks/paint.cc b/impeller/aiks/paint.cc index 6f2eea50cce7d..06de4e6399f9a 100644 --- a/impeller/aiks/paint.cc +++ b/impeller/aiks/paint.cc @@ -3,6 +3,8 @@ // found in the LICENSE file. #include "impeller/aiks/paint.h" +#include "impeller/entity/contents/solid_color_contents.h" +#include "impeller/entity/contents/solid_stroke_contents.h" namespace impeller { diff --git a/impeller/aiks/paint.h b/impeller/aiks/paint.h index 7a941474a7125..35ef71b7fccd1 100644 --- a/impeller/aiks/paint.h +++ b/impeller/aiks/paint.h @@ -7,7 +7,7 @@ #include #include "flutter/fml/macros.h" -#include "impeller/entity/contents.h" +#include "impeller/entity/contents/contents.h" #include "impeller/geometry/color.h" namespace impeller { diff --git a/impeller/aiks/paint_pass_delegate.cc b/impeller/aiks/paint_pass_delegate.cc index f0f09cd5c674a..f17ba655540cf 100644 --- a/impeller/aiks/paint_pass_delegate.cc +++ b/impeller/aiks/paint_pass_delegate.cc @@ -4,7 +4,8 @@ #include "impeller/aiks/paint_pass_delegate.h" -#include "impeller/entity/contents.h" +#include "impeller/entity/contents/contents.h" +#include "impeller/entity/contents/texture_contents.h" namespace impeller { diff --git a/impeller/display_list/display_list_dispatcher.cc b/impeller/display_list/display_list_dispatcher.cc index ddeabcffa3739..a8e500ac89c23 100644 --- a/impeller/display_list/display_list_dispatcher.cc +++ b/impeller/display_list/display_list_dispatcher.cc @@ -5,6 +5,7 @@ #include "impeller/display_list/display_list_dispatcher.h" #include "flutter/fml/trace_event.h" +#include "impeller/entity/contents/linear_gradient_contents.h" #include "impeller/geometry/path_builder.h" #include "impeller/typographer/backends/skia/text_frame_skia.h" #include "third_party/skia/include/core/SkColor.h" diff --git a/impeller/entity/BUILD.gn b/impeller/entity/BUILD.gn index 6a01accfba421..0181edfd2c34f 100644 --- a/impeller/entity/BUILD.gn +++ b/impeller/entity/BUILD.gn @@ -23,10 +23,22 @@ impeller_shaders("entity_shaders") { impeller_component("entity") { sources = [ - "content_context.cc", - "content_context.h", - "contents.cc", - "contents.h", + "contents/clip_contents.cc", + "contents/clip_contents.h", + "contents/content_context.cc", + "contents/content_context.h", + "contents/contents.cc", + "contents/contents.h", + "contents/linear_gradient_contents.cc", + "contents/linear_gradient_contents.h", + "contents/solid_color_contents.cc", + "contents/solid_color_contents.h", + "contents/solid_stroke_contents.cc", + "contents/solid_stroke_contents.h", + "contents/text_contents.cc", + "contents/text_contents.h", + "contents/texture_contents.cc", + "contents/texture_contents.h", "entity.cc", "entity.h", "entity_pass.cc", diff --git a/impeller/entity/contents.cc b/impeller/entity/contents.cc deleted file mode 100644 index a1f5bcfa789a4..0000000000000 --- a/impeller/entity/contents.cc +++ /dev/null @@ -1,826 +0,0 @@ -// Copyright 2013 The Flutter Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#include "impeller/entity/contents.h" - -#include -#include - -#include "flutter/fml/logging.h" -#include "impeller/entity/content_context.h" -#include "impeller/entity/entity.h" -#include "impeller/geometry/path_builder.h" -#include "impeller/geometry/path_component.h" -#include "impeller/geometry/scalar.h" -#include "impeller/geometry/vector.h" -#include "impeller/renderer/render_pass.h" -#include "impeller/renderer/sampler_library.h" -#include "impeller/renderer/surface.h" -#include "impeller/renderer/tessellator.h" -#include "impeller/renderer/vertex_buffer.h" -#include "impeller/renderer/vertex_buffer_builder.h" - -namespace impeller { - -static ContentContext::Options OptionsFromPass(const RenderPass& pass) { - ContentContext::Options opts; - opts.sample_count = pass.GetRenderTarget().GetSampleCount(); - return opts; -} - -/******************************************************************************* - ******* Contents - ******************************************************************************/ - -Contents::Contents() = default; - -Contents::~Contents() = default; - -/******************************************************************************* - ******* Linear Gradient Contents - ******************************************************************************/ - -LinearGradientContents::LinearGradientContents() = default; - -LinearGradientContents::~LinearGradientContents() = default; - -void LinearGradientContents::SetEndPoints(Point start_point, Point end_point) { - start_point_ = start_point; - end_point_ = end_point; -} - -void LinearGradientContents::SetColors(std::vector colors) { - colors_ = std::move(colors); - if (colors_.empty()) { - colors_.push_back(Color::Black()); - colors_.push_back(Color::Black()); - } else if (colors_.size() < 2u) { - colors_.push_back(colors_.back()); - } -} - -const std::vector& LinearGradientContents::GetColors() const { - return colors_; -} - -bool LinearGradientContents::Render(const ContentContext& renderer, - const Entity& entity, - RenderPass& pass) const { - using VS = GradientFillPipeline::VertexShader; - using FS = GradientFillPipeline::FragmentShader; - - auto vertices_builder = VertexBufferBuilder(); - { - auto result = Tessellator{entity.GetPath().GetFillType()}.Tessellate( - entity.GetPath().CreatePolyline(), [&vertices_builder](Point point) { - VS::PerVertexData vtx; - vtx.vertices = point; - vertices_builder.AppendVertex(vtx); - }); - if (!result) { - return false; - } - } - - VS::FrameInfo frame_info; - frame_info.mvp = Matrix::MakeOrthographic(pass.GetRenderTargetSize()) * - entity.GetTransformation(); - - FS::GradientInfo gradient_info; - gradient_info.start_point = start_point_; - gradient_info.end_point = end_point_; - gradient_info.start_color = colors_[0]; - gradient_info.end_color = colors_[1]; - - Command cmd; - cmd.label = "LinearGradientFill"; - cmd.pipeline = renderer.GetGradientFillPipeline(OptionsFromPass(pass)); - cmd.stencil_reference = entity.GetStencilDepth(); - cmd.BindVertices( - vertices_builder.CreateVertexBuffer(pass.GetTransientsBuffer())); - cmd.primitive_type = PrimitiveType::kTriangle; - FS::BindGradientInfo( - cmd, pass.GetTransientsBuffer().EmplaceUniform(gradient_info)); - VS::BindFrameInfo(cmd, pass.GetTransientsBuffer().EmplaceUniform(frame_info)); - return pass.AddCommand(std::move(cmd)); -} - -/******************************************************************************* - ******* SolidColorContents - ******************************************************************************/ - -SolidColorContents::SolidColorContents() = default; - -SolidColorContents::~SolidColorContents() = default; - -void SolidColorContents::SetColor(Color color) { - color_ = color; -} - -const Color& SolidColorContents::GetColor() const { - return color_; -} - -static VertexBuffer CreateSolidFillVertices(const Path& path, - HostBuffer& buffer) { - using VS = SolidFillPipeline::VertexShader; - - VertexBufferBuilder vtx_builder; - - auto tesselation_result = Tessellator{path.GetFillType()}.Tessellate( - path.CreatePolyline(), [&vtx_builder](auto point) { - VS::PerVertexData vtx; - vtx.vertices = point; - vtx_builder.AppendVertex(vtx); - }); - if (!tesselation_result) { - return {}; - } - - return vtx_builder.CreateVertexBuffer(buffer); -} - -bool SolidColorContents::Render(const ContentContext& renderer, - const Entity& entity, - RenderPass& pass) const { - if (color_.IsTransparent()) { - return true; - } - - using VS = SolidFillPipeline::VertexShader; - - Command cmd; - cmd.label = "SolidFill"; - cmd.pipeline = renderer.GetSolidFillPipeline(OptionsFromPass(pass)); - cmd.stencil_reference = entity.GetStencilDepth(); - cmd.BindVertices( - CreateSolidFillVertices(entity.GetPath(), pass.GetTransientsBuffer())); - - VS::FrameInfo frame_info; - frame_info.mvp = Matrix::MakeOrthographic(pass.GetRenderTargetSize()) * - entity.GetTransformation(); - frame_info.color = color_; - VS::BindFrameInfo(cmd, pass.GetTransientsBuffer().EmplaceUniform(frame_info)); - - cmd.primitive_type = PrimitiveType::kTriangle; - - if (!pass.AddCommand(std::move(cmd))) { - return false; - } - - return true; -} - -std::unique_ptr SolidColorContents::Make(Color color) { - auto contents = std::make_unique(); - contents->SetColor(color); - return contents; -} - -/******************************************************************************* - ******* TextureContents - ******************************************************************************/ - -TextureContents::TextureContents() = default; - -TextureContents::~TextureContents() = default; - -void TextureContents::SetTexture(std::shared_ptr texture) { - texture_ = std::move(texture); -} - -std::shared_ptr TextureContents::GetTexture() const { - return texture_; -} - -void TextureContents::SetOpacity(Scalar opacity) { - opacity_ = opacity; -} - -bool TextureContents::Render(const ContentContext& renderer, - const Entity& entity, - RenderPass& pass) const { - if (texture_ == nullptr) { - return true; - } - - using VS = TextureFillVertexShader; - using FS = TextureFillFragmentShader; - - const auto coverage_rect = entity.GetPath().GetBoundingBox(); - - if (!coverage_rect.has_value()) { - return true; - } - - if (coverage_rect->size.IsEmpty()) { - return true; - } - - const auto texture_size = texture_->GetSize(); - if (texture_size.IsEmpty()) { - return true; - } - - if (source_rect_.IsEmpty()) { - return true; - } - - VertexBufferBuilder vertex_builder; - { - const auto tess_result = - Tessellator{entity.GetPath().GetFillType()}.Tessellate( - entity.GetPath().CreatePolyline(), - [this, &vertex_builder, &coverage_rect, &texture_size](Point vtx) { - VS::PerVertexData data; - data.vertices = vtx; - auto coverage_coords = - (vtx - coverage_rect->origin) / coverage_rect->size; - data.texture_coords = - (source_rect_.origin + source_rect_.size * coverage_coords) / - texture_size; - vertex_builder.AppendVertex(data); - }); - if (!tess_result) { - return false; - } - } - - if (!vertex_builder.HasVertices()) { - return true; - } - - auto& host_buffer = pass.GetTransientsBuffer(); - - VS::FrameInfo frame_info; - frame_info.mvp = Matrix::MakeOrthographic(pass.GetRenderTargetSize()) * - entity.GetTransformation(); - frame_info.alpha = opacity_; - - Command cmd; - cmd.label = "TextureFill"; - cmd.pipeline = renderer.GetTexturePipeline(OptionsFromPass(pass)); - cmd.stencil_reference = entity.GetStencilDepth(); - cmd.BindVertices(vertex_builder.CreateVertexBuffer(host_buffer)); - VS::BindFrameInfo(cmd, host_buffer.EmplaceUniform(frame_info)); - FS::BindTextureSampler( - cmd, texture_, - renderer.GetContext()->GetSamplerLibrary()->GetSampler({})); - pass.AddCommand(std::move(cmd)); - - return true; -} - -void TextureContents::SetSourceRect(const IRect& source_rect) { - source_rect_ = source_rect; -} - -const IRect& TextureContents::GetSourceRect() const { - return source_rect_; -} - -/******************************************************************************* - ******* SolidStrokeContents - ******************************************************************************/ - -SolidStrokeContents::SolidStrokeContents() { - SetStrokeCap(Cap::kButt); - SetStrokeJoin(Join::kMiter); -} - -SolidStrokeContents::~SolidStrokeContents() = default; - -void SolidStrokeContents::SetColor(Color color) { - color_ = color; -} - -const Color& SolidStrokeContents::GetColor() const { - return color_; -} - -static VertexBuffer CreateSolidStrokeVertices( - const Path& path, - HostBuffer& buffer, - const SolidStrokeContents::CapProc& cap_proc, - const SolidStrokeContents::JoinProc& join_proc, - Scalar miter_limit, - const SmoothingApproximation& smoothing) { - using VS = SolidStrokeVertexShader; - - VertexBufferBuilder vtx_builder; - auto polyline = path.CreatePolyline(); - - if (polyline.points.size() < 2) { - return {}; // Nothing to render. - } - - VS::PerVertexData vtx; - - // Normal state. - Point normal; - Point previous_normal; // Used for computing joins. - - auto compute_normal = [&polyline, &normal, &previous_normal](size_t point_i) { - previous_normal = normal; - Point direction = - (polyline.points[point_i] - polyline.points[point_i - 1]).Normalize(); - normal = {-direction.y, direction.x}; - }; - - for (size_t contour_i = 0; contour_i < polyline.contours.size(); - contour_i++) { - size_t contour_start_point_i, contour_end_point_i; - std::tie(contour_start_point_i, contour_end_point_i) = - polyline.GetContourPointBounds(contour_i); - - if (contour_end_point_i - contour_start_point_i < 2) { - continue; // This contour has no renderable content. - } - - // The first point's normal is always the same as - compute_normal(contour_start_point_i + 1); - const Point contour_first_normal = normal; - - if (contour_i > 0) { - // This branch only executes when we've just finished drawing a contour - // and are switching to a new one. - // We're drawing a triangle strip, so we need to "pick up the pen" by - // appending transparent vertices between the end of the previous contour - // and the beginning of the new contour. - vtx.vertex_position = polyline.points[contour_start_point_i - 1]; - vtx.vertex_normal = {}; - vtx.pen_down = 0.0; - vtx_builder.AppendVertex(vtx); - vtx.vertex_position = polyline.points[contour_start_point_i]; - vtx_builder.AppendVertex(vtx); - } - - // Generate start cap. - if (!polyline.contours[contour_i].is_closed) { - cap_proc(vtx_builder, polyline.points[contour_start_point_i], -normal, - smoothing); - } - - // Generate contour geometry. - for (size_t point_i = contour_start_point_i; point_i < contour_end_point_i; - point_i++) { - if (point_i > contour_start_point_i) { - // Generate line rect. - vtx.vertex_position = polyline.points[point_i - 1]; - vtx.pen_down = 1.0; - vtx.vertex_normal = normal; - vtx_builder.AppendVertex(vtx); - vtx.vertex_normal = -normal; - vtx_builder.AppendVertex(vtx); - vtx.vertex_position = polyline.points[point_i]; - vtx.vertex_normal = normal; - vtx_builder.AppendVertex(vtx); - vtx.vertex_normal = -normal; - vtx_builder.AppendVertex(vtx); - - if (point_i < contour_end_point_i - 1) { - compute_normal(point_i + 1); - - // Generate join from the current line to the next line. - join_proc(vtx_builder, polyline.points[point_i], previous_normal, - normal, miter_limit, smoothing); - } - } - } - - // Generate end cap or join. - if (!polyline.contours[contour_i].is_closed) { - cap_proc(vtx_builder, polyline.points[contour_end_point_i - 1], normal, - smoothing); - } else { - join_proc(vtx_builder, polyline.points[contour_start_point_i], normal, - contour_first_normal, miter_limit, smoothing); - } - } - - return vtx_builder.CreateVertexBuffer(buffer); -} - -bool SolidStrokeContents::Render(const ContentContext& renderer, - const Entity& entity, - RenderPass& pass) const { - if (color_.IsTransparent() || stroke_size_ <= 0.0) { - return true; - } - - using VS = SolidStrokeVertexShader; - - VS::FrameInfo frame_info; - frame_info.mvp = Matrix::MakeOrthographic(pass.GetRenderTargetSize()) * - entity.GetTransformation(); - - VS::StrokeInfo stroke_info; - stroke_info.color = color_; - stroke_info.size = stroke_size_; - - Command cmd; - cmd.primitive_type = PrimitiveType::kTriangleStrip; - cmd.label = "SolidStroke"; - cmd.pipeline = renderer.GetSolidStrokePipeline(OptionsFromPass(pass)); - cmd.stencil_reference = entity.GetStencilDepth(); - cmd.BindVertices(CreateSolidStrokeVertices( - entity.GetPath(), pass.GetTransientsBuffer(), cap_proc_, join_proc_, - miter_limit_, arc_smoothing_approximation_)); - VS::BindFrameInfo(cmd, pass.GetTransientsBuffer().EmplaceUniform(frame_info)); - VS::BindStrokeInfo(cmd, - pass.GetTransientsBuffer().EmplaceUniform(stroke_info)); - - pass.AddCommand(std::move(cmd)); - - return true; -} - -void SolidStrokeContents::SetStrokeSize(Scalar size) { - stroke_size_ = size; - arc_smoothing_approximation_ = SmoothingApproximation(5.0 / size, 0.0, 0.0); -} - -Scalar SolidStrokeContents::GetStrokeSize() const { - return stroke_size_; -} - -void SolidStrokeContents::SetStrokeMiter(Scalar miter_limit) { - if (miter_limit < 0) { - return; // Skia behaves like this. - } - miter_limit_ = miter_limit; -} - -Scalar SolidStrokeContents::GetStrokeMiter() { - return miter_limit_; -} - -void SolidStrokeContents::SetStrokeCap(Cap cap) { - cap_ = cap; - - using VS = SolidStrokeVertexShader; - switch (cap) { - case Cap::kButt: - cap_proc_ = [](VertexBufferBuilder& vtx_builder, - const Point& position, const Point& normal, - const SmoothingApproximation& smoothing) {}; - break; - case Cap::kRound: - cap_proc_ = [](VertexBufferBuilder& vtx_builder, - const Point& position, const Point& normal, - const SmoothingApproximation& smoothing) { - SolidStrokeVertexShader::PerVertexData vtx; - vtx.vertex_position = position; - vtx.pen_down = 1.0; - - Point forward(normal.y, -normal.x); - - auto arc_points = - CubicPathComponent( - normal, normal + forward * PathBuilder::kArcApproximationMagic, - forward + normal * PathBuilder::kArcApproximationMagic, forward) - .CreatePolyline(smoothing); - - vtx.vertex_normal = normal; - vtx_builder.AppendVertex(vtx); - vtx.vertex_normal = -normal; - vtx_builder.AppendVertex(vtx); - for (const auto& point : arc_points) { - vtx.vertex_normal = point; - vtx_builder.AppendVertex(vtx); - vtx.vertex_normal = (-point).Reflect(forward); - vtx_builder.AppendVertex(vtx); - } - }; - break; - case Cap::kSquare: - cap_proc_ = [](VertexBufferBuilder& vtx_builder, - const Point& position, const Point& normal, - const SmoothingApproximation& smoothing) { - SolidStrokeVertexShader::PerVertexData vtx; - vtx.vertex_position = position; - vtx.pen_down = 1.0; - - Point forward(normal.y, -normal.x); - - vtx.vertex_normal = normal; - vtx_builder.AppendVertex(vtx); - vtx.vertex_normal = -normal; - vtx_builder.AppendVertex(vtx); - vtx.vertex_normal = normal + forward; - vtx_builder.AppendVertex(vtx); - vtx.vertex_normal = -normal + forward; - vtx_builder.AppendVertex(vtx); - }; - break; - } -} - -SolidStrokeContents::Cap SolidStrokeContents::GetStrokeCap() { - return cap_; -} - -static Scalar CreateBevelAndGetDirection( - VertexBufferBuilder& vtx_builder, - const Point& position, - const Point& start_normal, - const Point& end_normal) { - SolidStrokeVertexShader::PerVertexData vtx; - vtx.vertex_position = position; - vtx.pen_down = 1.0; - vtx.vertex_normal = {}; - vtx_builder.AppendVertex(vtx); - - Scalar dir = start_normal.Cross(end_normal) > 0 ? -1 : 1; - vtx.vertex_normal = start_normal * dir; - vtx_builder.AppendVertex(vtx); - vtx.vertex_normal = end_normal * dir; - vtx_builder.AppendVertex(vtx); - - return dir; -} - -void SolidStrokeContents::SetStrokeJoin(Join join) { - join_ = join; - - using VS = SolidStrokeVertexShader; - switch (join) { - case Join::kBevel: - join_proc_ = [](VertexBufferBuilder& vtx_builder, - const Point& position, const Point& start_normal, - const Point& end_normal, Scalar miter_limit, - const SmoothingApproximation& smoothing) { - CreateBevelAndGetDirection(vtx_builder, position, start_normal, - end_normal); - }; - break; - case Join::kMiter: - join_proc_ = [](VertexBufferBuilder& vtx_builder, - const Point& position, const Point& start_normal, - const Point& end_normal, Scalar miter_limit, - const SmoothingApproximation& smoothing) { - // 1 for no joint (straight line), 0 for max joint (180 degrees). - Scalar alignment = (start_normal.Dot(end_normal) + 1) / 2; - if (ScalarNearlyEqual(alignment, 1)) { - return; - } - - Scalar dir = CreateBevelAndGetDirection(vtx_builder, position, - start_normal, end_normal); - - Point miter_point = (start_normal + end_normal) / 2 / alignment; - if (miter_point.GetDistanceSquared({0, 0}) > - miter_limit * miter_limit) { - return; // Convert to bevel when we exceed the miter limit. - } - - // Outer miter point. - SolidStrokeVertexShader::PerVertexData vtx; - vtx.vertex_position = position; - vtx.pen_down = 1.0; - vtx.vertex_normal = miter_point * dir; - vtx_builder.AppendVertex(vtx); - }; - break; - case Join::kRound: - join_proc_ = [](VertexBufferBuilder& vtx_builder, - const Point& position, const Point& start_normal, - const Point& end_normal, Scalar miter_limit, - const SmoothingApproximation& smoothing) { - // 0 for no joint (straight line), 1 for max joint (180 degrees). - Scalar alignment = 1 - (start_normal.Dot(end_normal) + 1) / 2; - if (ScalarNearlyEqual(alignment, 0)) { - return; - } - - Scalar dir = - CreateBevel(vtx_builder, position, start_normal, end_normal); - - Point middle = (start_normal + end_normal).Normalize(); - Point middle_handle = middle + Point(-middle.y, middle.x) * - PathBuilder::kArcApproximationMagic * - alignment * dir; - Point start_handle = - start_normal + Point(start_normal.y, -start_normal.x) * - PathBuilder::kArcApproximationMagic * alignment * - dir; - - auto arc_points = CubicPathComponent(start_normal, start_handle, - middle_handle, middle) - .CreatePolyline(smoothing); - - SolidStrokeVertexShader::PerVertexData vtx; - vtx.vertex_position = position; - vtx.pen_down = 1.0; - for (const auto& point : arc_points) { - vtx.vertex_normal = point * dir; - vtx_builder.AppendVertex(vtx); - vtx.vertex_normal = (-point * dir).Reflect(middle); - vtx_builder.AppendVertex(vtx); - } - }; - break; - } -} - -SolidStrokeContents::Join SolidStrokeContents::GetStrokeJoin() { - return join_; -} - -/******************************************************************************* - ******* ClipContents - ******************************************************************************/ - -ClipContents::ClipContents() = default; - -ClipContents::~ClipContents() = default; - -bool ClipContents::Render(const ContentContext& renderer, - const Entity& entity, - RenderPass& pass) const { - using VS = ClipPipeline::VertexShader; - - Command cmd; - cmd.label = "Clip"; - cmd.pipeline = renderer.GetClipPipeline(OptionsFromPass(pass)); - cmd.stencil_reference = entity.GetStencilDepth(); - cmd.BindVertices( - CreateSolidFillVertices(entity.GetPath(), pass.GetTransientsBuffer())); - - VS::FrameInfo info; - // The color really doesn't matter. - info.color = Color::SkyBlue(); - info.mvp = Matrix::MakeOrthographic(pass.GetRenderTargetSize()); - - VS::BindFrameInfo(cmd, pass.GetTransientsBuffer().EmplaceUniform(info)); - - pass.AddCommand(std::move(cmd)); - return true; -} - -/******************************************************************************* - ******* ClipRestoreContents - ******************************************************************************/ - -ClipRestoreContents::ClipRestoreContents() = default; - -ClipRestoreContents::~ClipRestoreContents() = default; - -bool ClipRestoreContents::Render(const ContentContext& renderer, - const Entity& entity, - RenderPass& pass) const { - using VS = ClipPipeline::VertexShader; - - Command cmd; - cmd.label = "Clip Restore"; - cmd.pipeline = renderer.GetClipRestorePipeline(OptionsFromPass(pass)); - cmd.stencil_reference = entity.GetStencilDepth(); - - // Create a rect that covers the whole render target. - auto size = pass.GetRenderTargetSize(); - VertexBufferBuilder vtx_builder; - vtx_builder.AddVertices({ - {Point(0.0, 0.0)}, - {Point(size.width, 0.0)}, - {Point(size.width, size.height)}, - {Point(0.0, 0.0)}, - {Point(size.width, size.height)}, - {Point(0.0, size.height)}, - }); - cmd.BindVertices(vtx_builder.CreateVertexBuffer(pass.GetTransientsBuffer())); - - VS::FrameInfo info; - // The color really doesn't matter. - info.color = Color::SkyBlue(); - info.mvp = Matrix::MakeOrthographic(pass.GetRenderTargetSize()); - - VS::BindFrameInfo(cmd, pass.GetTransientsBuffer().EmplaceUniform(info)); - - pass.AddCommand(std::move(cmd)); - return true; -} - -/******************************************************************************* - ******* TextContents - ******************************************************************************/ - -TextContents::TextContents() = default; - -TextContents::~TextContents() = default; - -void TextContents::SetTextFrame(TextFrame frame) { - frame_ = std::move(frame); -} - -void TextContents::SetGlyphAtlas(std::shared_ptr atlas) { - atlas_ = std::move(atlas); -} - -void TextContents::SetColor(Color color) { - color_ = color; -} - -bool TextContents::Render(const ContentContext& renderer, - const Entity& entity, - RenderPass& pass) const { - if (color_.IsTransparent()) { - return true; - } - - if (!atlas_ || !atlas_->IsValid()) { - VALIDATION_LOG << "Cannot render glyphs without prepared atlas."; - return false; - } - - using VS = GlyphAtlasPipeline::VertexShader; - using FS = GlyphAtlasPipeline::FragmentShader; - - // Information shared by all glyph draw calls. - Command cmd; - cmd.label = "Glyph"; - cmd.primitive_type = PrimitiveType::kTriangle; - cmd.pipeline = renderer.GetGlyphAtlasPipeline(OptionsFromPass(pass)); - cmd.stencil_reference = entity.GetStencilDepth(); - - // Common vertex uniforms for all glyphs. - VS::FrameInfo frame_info; - frame_info.mvp = Matrix::MakeOrthographic(pass.GetRenderTargetSize()) * - entity.GetTransformation(); - frame_info.atlas_size = - Point{static_cast(atlas_->GetTexture()->GetSize().width), - static_cast(atlas_->GetTexture()->GetSize().height)}; - frame_info.text_color = ToVector(color_); - VS::BindFrameInfo(cmd, pass.GetTransientsBuffer().EmplaceUniform(frame_info)); - - // Common fragment uniforms for all glyphs. - FS::BindGlyphAtlasSampler( - cmd, // command - atlas_->GetTexture(), // texture - renderer.GetContext()->GetSamplerLibrary()->GetSampler({}) // sampler - ); - - // Common vertex information for all glyphs. - // Currently, glyphs are being drawn individually. This can be batched later. - // But we don't want to give each glyph unique vertex information. So all - // glyphs are given the same vertex information in the form of a unit-sized - // quad. The size of the glyph is specified in uniform data and the vertex - // shader uses this to size the glyph correctly. The interpolated vertex - // information is also used in the fragment shader to sample from the glyph - // atlas. - { - VertexBufferBuilder vertex_builder; - if (!Tessellator{FillType::kPositive}.Tessellate( - PathBuilder{} - .AddRect(Rect::MakeXYWH(0.0, 0.0, 1.0, 1.0)) - .TakePath() - .CreatePolyline(), - [&vertex_builder](Point point) { - VS::PerVertexData vtx; - vtx.unit_vertex = point; - vertex_builder.AppendVertex(std::move(vtx)); - })) { - return false; - } - auto dummy = vertex_builder.CreateVertexBuffer(pass.GetTransientsBuffer()); - auto vertex_buffer = dummy; - if (!vertex_buffer) { - return false; - } - cmd.BindVertices(std::move(vertex_buffer)); - } - - // Iterate through all the runs in the blob. - for (const auto& run : frame_.GetRuns()) { - auto font = run.GetFont(); - auto glyph_size = ISize::Ceil(font.GetMetrics().GetBoundingBox().size); - // Draw each glyph individually. This should probably be batched. - for (const auto& glyph_position : run.GetGlyphPositions()) { - FontGlyphPair font_glyph_pair{font, glyph_position.glyph}; - auto atlas_glyph_pos = atlas_->FindFontGlyphPosition(font_glyph_pair); - if (!atlas_glyph_pos.has_value()) { - VALIDATION_LOG << "Could not find glyph position in the atlas."; - return false; - } - - VS::GlyphInfo glyph_info; - glyph_info.position = glyph_position.position.Translate( - {font.GetMetrics().min_extent.x, font.GetMetrics().ascent, 0.0}); - glyph_info.glyph_size = {static_cast(glyph_size.width), - static_cast(glyph_size.height)}; - glyph_info.atlas_position = atlas_glyph_pos->origin; - glyph_info.atlas_glyph_size = {atlas_glyph_pos->size.width, - atlas_glyph_pos->size.height}; - VS::BindGlyphInfo(cmd, - pass.GetTransientsBuffer().EmplaceUniform(glyph_info)); - - if (!pass.AddCommand(cmd)) { - return false; - } - } - } - - return true; -} - -} // namespace impeller diff --git a/impeller/entity/contents.h b/impeller/entity/contents.h deleted file mode 100644 index f3803ff4d0222..0000000000000 --- a/impeller/entity/contents.h +++ /dev/null @@ -1,248 +0,0 @@ -// Copyright 2013 The Flutter Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#pragma once - -#include -#include -#include - -#include "flutter/fml/macros.h" -#include "impeller/entity/solid_stroke.vert.h" -#include "impeller/geometry/color.h" -#include "impeller/geometry/path_component.h" -#include "impeller/geometry/point.h" -#include "impeller/geometry/rect.h" -#include "impeller/renderer/texture.h" -#include "impeller/typographer/glyph_atlas.h" -#include "impeller/typographer/text_frame.h" - -namespace impeller { - -class ContentContext; -class Entity; -class Surface; -class RenderPass; - -class Contents { - public: - Contents(); - - virtual ~Contents(); - - virtual bool Render(const ContentContext& renderer, - const Entity& entity, - RenderPass& pass) const = 0; - - private: - FML_DISALLOW_COPY_AND_ASSIGN(Contents); -}; - -class LinearGradientContents final : public Contents { - public: - LinearGradientContents(); - - ~LinearGradientContents() override; - - // |Contents| - bool Render(const ContentContext& renderer, - const Entity& entity, - RenderPass& pass) const override; - - void SetEndPoints(Point start_point, Point end_point); - - void SetColors(std::vector colors); - - const std::vector& GetColors() const; - - private: - Point start_point_; - Point end_point_; - std::vector colors_; - - FML_DISALLOW_COPY_AND_ASSIGN(LinearGradientContents); -}; - -class SolidColorContents final : public Contents { - public: - SolidColorContents(); - - ~SolidColorContents() override; - - static std::unique_ptr Make(Color color); - - void SetColor(Color color); - - const Color& GetColor() const; - - // |Contents| - bool Render(const ContentContext& renderer, - const Entity& entity, - RenderPass& pass) const override; - - private: - Color color_; - - FML_DISALLOW_COPY_AND_ASSIGN(SolidColorContents); -}; - -class TextureContents final : public Contents { - public: - TextureContents(); - - ~TextureContents() override; - - void SetTexture(std::shared_ptr texture); - - std::shared_ptr GetTexture() const; - - void SetSourceRect(const IRect& source_rect); - - void SetOpacity(Scalar opacity); - - const IRect& GetSourceRect() const; - - // |Contents| - bool Render(const ContentContext& renderer, - const Entity& entity, - RenderPass& pass) const override; - - public: - std::shared_ptr texture_; - IRect source_rect_; - Scalar opacity_ = 1.0f; - - FML_DISALLOW_COPY_AND_ASSIGN(TextureContents); -}; - -class SolidStrokeContents final : public Contents { - public: - enum class Cap { - kButt, - kRound, - kSquare, - }; - - enum class Join { - kMiter, - kRound, - kBevel, - }; - - using CapProc = std::function& vtx_builder, - const Point& position, - const Point& normal, - const SmoothingApproximation& smoothing)>; - using JoinProc = std::function& vtx_builder, - const Point& position, - const Point& start_normal, - const Point& end_normal, - Scalar miter_limit, - const SmoothingApproximation& smoothing)>; - - SolidStrokeContents(); - - ~SolidStrokeContents() override; - - void SetColor(Color color); - - const Color& GetColor() const; - - void SetStrokeSize(Scalar size); - - Scalar GetStrokeSize() const; - - void SetStrokeMiter(Scalar miter_limit); - - Scalar GetStrokeMiter(); - - void SetStrokeCap(Cap cap); - - Cap GetStrokeCap(); - - void SetStrokeJoin(Join join); - - Join GetStrokeJoin(); - - // |Contents| - bool Render(const ContentContext& renderer, - const Entity& entity, - RenderPass& pass) const override; - - private: - SmoothingApproximation arc_smoothing_approximation_; - - Color color_; - Scalar stroke_size_ = 0.0; - Scalar miter_limit_ = 4.0; - - Cap cap_; - CapProc cap_proc_; - - Join join_; - JoinProc join_proc_; - - FML_DISALLOW_COPY_AND_ASSIGN(SolidStrokeContents); -}; - -class ClipContents final : public Contents { - public: - ClipContents(); - - ~ClipContents(); - - // |Contents| - bool Render(const ContentContext& renderer, - const Entity& entity, - RenderPass& pass) const override; - - private: - FML_DISALLOW_COPY_AND_ASSIGN(ClipContents); -}; - -class ClipRestoreContents final : public Contents { - public: - ClipRestoreContents(); - - ~ClipRestoreContents(); - - void SetGlyphAtlas(std::shared_ptr atlas); - - // |Contents| - bool Render(const ContentContext& renderer, - const Entity& entity, - RenderPass& pass) const override; - - private: - FML_DISALLOW_COPY_AND_ASSIGN(ClipRestoreContents); -}; - -class TextContents final : public Contents { - public: - TextContents(); - - ~TextContents(); - - void SetTextFrame(TextFrame frame); - - void SetGlyphAtlas(std::shared_ptr atlas); - - void SetColor(Color color); - - // |Contents| - bool Render(const ContentContext& renderer, - const Entity& entity, - RenderPass& pass) const override; - - private: - TextFrame frame_; - Color color_; - std::shared_ptr atlas_; - - FML_DISALLOW_COPY_AND_ASSIGN(TextContents); -}; - -} // namespace impeller diff --git a/impeller/entity/contents/clip_contents.cc b/impeller/entity/contents/clip_contents.cc new file mode 100644 index 0000000000000..07921a3356799 --- /dev/null +++ b/impeller/entity/contents/clip_contents.cc @@ -0,0 +1,88 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "linear_gradient_contents.h" + +#include "impeller/entity/contents/clip_contents.h" +#include "impeller/entity/contents/content_context.h" +#include "impeller/entity/contents/solid_color_contents.h" +#include "impeller/entity/entity.h" +#include "impeller/renderer/render_pass.h" + +namespace impeller { + +/******************************************************************************* + ******* ClipContents + ******************************************************************************/ + +ClipContents::ClipContents() = default; + +ClipContents::~ClipContents() = default; + +bool ClipContents::Render(const ContentContext& renderer, + const Entity& entity, + RenderPass& pass) const { + using VS = ClipPipeline::VertexShader; + + Command cmd; + cmd.label = "Clip"; + cmd.pipeline = renderer.GetClipPipeline(OptionsFromPass(pass)); + cmd.stencil_reference = entity.GetStencilDepth(); + cmd.BindVertices(SolidColorContents::CreateSolidFillVertices( + entity.GetPath(), pass.GetTransientsBuffer())); + + VS::FrameInfo info; + // The color really doesn't matter. + info.color = Color::SkyBlue(); + info.mvp = Matrix::MakeOrthographic(pass.GetRenderTargetSize()); + + VS::BindFrameInfo(cmd, pass.GetTransientsBuffer().EmplaceUniform(info)); + + pass.AddCommand(std::move(cmd)); + return true; +} + +/******************************************************************************* + ******* ClipRestoreContents + ******************************************************************************/ + +ClipRestoreContents::ClipRestoreContents() = default; + +ClipRestoreContents::~ClipRestoreContents() = default; + +bool ClipRestoreContents::Render(const ContentContext& renderer, + const Entity& entity, + RenderPass& pass) const { + using VS = ClipPipeline::VertexShader; + + Command cmd; + cmd.label = "Clip Restore"; + cmd.pipeline = renderer.GetClipRestorePipeline(OptionsFromPass(pass)); + cmd.stencil_reference = entity.GetStencilDepth(); + + // Create a rect that covers the whole render target. + auto size = pass.GetRenderTargetSize(); + VertexBufferBuilder vtx_builder; + vtx_builder.AddVertices({ + {Point(0.0, 0.0)}, + {Point(size.width, 0.0)}, + {Point(size.width, size.height)}, + {Point(0.0, 0.0)}, + {Point(size.width, size.height)}, + {Point(0.0, size.height)}, + }); + cmd.BindVertices(vtx_builder.CreateVertexBuffer(pass.GetTransientsBuffer())); + + VS::FrameInfo info; + // The color really doesn't matter. + info.color = Color::SkyBlue(); + info.mvp = Matrix::MakeOrthographic(pass.GetRenderTargetSize()); + + VS::BindFrameInfo(cmd, pass.GetTransientsBuffer().EmplaceUniform(info)); + + pass.AddCommand(std::move(cmd)); + return true; +} + +}; // namespace impeller diff --git a/impeller/entity/contents/clip_contents.h b/impeller/entity/contents/clip_contents.h new file mode 100644 index 0000000000000..41fe7cf4a53e2 --- /dev/null +++ b/impeller/entity/contents/clip_contents.h @@ -0,0 +1,51 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#pragma once + +#include +#include +#include + +#include "flutter/fml/macros.h" +#include "impeller/entity/contents/contents.h" +#include "typographer/glyph_atlas.h" + +namespace impeller { + +class GlyphAtlas; + +class ClipContents final : public Contents { + public: + ClipContents(); + + ~ClipContents(); + + // |Contents| + bool Render(const ContentContext& renderer, + const Entity& entity, + RenderPass& pass) const override; + + private: + FML_DISALLOW_COPY_AND_ASSIGN(ClipContents); +}; + +class ClipRestoreContents final : public Contents { + public: + ClipRestoreContents(); + + ~ClipRestoreContents(); + + void SetGlyphAtlas(std::shared_ptr atlas); + + // |Contents| + bool Render(const ContentContext& renderer, + const Entity& entity, + RenderPass& pass) const override; + + private: + FML_DISALLOW_COPY_AND_ASSIGN(ClipRestoreContents); +}; + +} // namespace impeller diff --git a/impeller/entity/content_context.cc b/impeller/entity/contents/content_context.cc similarity index 98% rename from impeller/entity/content_context.cc rename to impeller/entity/contents/content_context.cc index 0d4b23de76da5..0570a940b89a3 100644 --- a/impeller/entity/content_context.cc +++ b/impeller/entity/contents/content_context.cc @@ -2,7 +2,7 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -#include "impeller/entity/content_context.h" +#include "impeller/entity/contents/content_context.h" #include diff --git a/impeller/entity/content_context.h b/impeller/entity/contents/content_context.h similarity index 72% rename from impeller/entity/content_context.h rename to impeller/entity/contents/content_context.h index a3b0ec270e70c..7217bf3344f19 100644 --- a/impeller/entity/content_context.h +++ b/impeller/entity/contents/content_context.h @@ -19,7 +19,6 @@ #include "flutter/impeller/entity/solid_stroke.vert.h" #include "flutter/impeller/entity/texture_fill.frag.h" #include "flutter/impeller/entity/texture_fill.vert.h" -#include "impeller/renderer/pipeline.h" namespace impeller { @@ -37,55 +36,62 @@ using GlyphAtlasPipeline = // to redirect writing to the stencil instead of color attachments. using ClipPipeline = PipelineT; -class ContentContext { - public: - struct Options { - SampleCount sample_count = SampleCount::kCount1; - - struct Hash { - constexpr std::size_t operator()(const Options& o) const { - return fml::HashCombine(o.sample_count); - } - }; - - struct Equal { - constexpr bool operator()(const Options& lhs, const Options& rhs) const { - return lhs.sample_count == rhs.sample_count; - } - }; +struct ContentContextOptions { + SampleCount sample_count = SampleCount::kCount1; + + struct Hash { + constexpr std::size_t operator()(const ContentContextOptions& o) const { + return fml::HashCombine(o.sample_count); + } + }; + + struct Equal { + constexpr bool operator()(const ContentContextOptions& lhs, + const ContentContextOptions& rhs) const { + return lhs.sample_count == rhs.sample_count; + } }; +}; +class ContentContext { + public: ContentContext(std::shared_ptr context); ~ContentContext(); bool IsValid() const; - std::shared_ptr GetGradientFillPipeline(Options opts) const { + std::shared_ptr GetGradientFillPipeline( + ContentContextOptions opts) const { return GetPipeline(gradient_fill_pipelines_, opts); } - std::shared_ptr GetSolidFillPipeline(Options opts) const { + std::shared_ptr GetSolidFillPipeline( + ContentContextOptions opts) const { return GetPipeline(solid_fill_pipelines_, opts); } - std::shared_ptr GetTexturePipeline(Options opts) const { + std::shared_ptr GetTexturePipeline( + ContentContextOptions opts) const { return GetPipeline(texture_pipelines_, opts); } - std::shared_ptr GetSolidStrokePipeline(Options opts) const { + std::shared_ptr GetSolidStrokePipeline( + ContentContextOptions opts) const { return GetPipeline(solid_stroke_pipelines_, opts); } - std::shared_ptr GetClipPipeline(Options opts) const { + std::shared_ptr GetClipPipeline(ContentContextOptions opts) const { return GetPipeline(clip_pipelines_, opts); } - std::shared_ptr GetClipRestorePipeline(Options opts) const { + std::shared_ptr GetClipRestorePipeline( + ContentContextOptions opts) const { return GetPipeline(clip_restoration_pipelines_, opts); } - std::shared_ptr GetGlyphAtlasPipeline(Options opts) const { + std::shared_ptr GetGlyphAtlasPipeline( + ContentContextOptions opts) const { return GetPipeline(glyph_atlas_pipelines_, opts); } @@ -95,8 +101,10 @@ class ContentContext { std::shared_ptr context_; template - using Variants = std:: - unordered_map, Options::Hash, Options::Equal>; + using Variants = std::unordered_map, + ContentContextOptions::Hash, + ContentContextOptions::Equal>; // These are mutable because while the prototypes are created eagerly, any // variants requested from that are lazily created and cached in the variants @@ -110,13 +118,13 @@ class ContentContext { mutable Variants glyph_atlas_pipelines_; static void ApplyOptionsToDescriptor(PipelineDescriptor& desc, - const Options& options) { + const ContentContextOptions& options) { desc.SetSampleCount(options.sample_count); } template std::shared_ptr GetPipeline(Variants& container, - Options opts) const { + ContentContextOptions opts) const { if (!IsValid()) { return nullptr; } diff --git a/impeller/entity/contents/contents.cc b/impeller/entity/contents/contents.cc new file mode 100644 index 0000000000000..9969170852645 --- /dev/null +++ b/impeller/entity/contents/contents.cc @@ -0,0 +1,22 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "impeller/entity/contents/contents.h" + +#include "impeller/entity/contents/content_context.h" +#include "impeller/renderer/render_pass.h" + +namespace impeller { + +ContentContextOptions OptionsFromPass(const RenderPass& pass) { + ContentContextOptions opts; + opts.sample_count = pass.GetRenderTarget().GetSampleCount(); + return opts; +} + +Contents::Contents() = default; + +Contents::~Contents() = default; + +} // namespace impeller diff --git a/impeller/entity/contents/contents.h b/impeller/entity/contents/contents.h new file mode 100644 index 0000000000000..8bc297736ba73 --- /dev/null +++ b/impeller/entity/contents/contents.h @@ -0,0 +1,37 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#pragma once + +#include +#include +#include + +#include "flutter/fml/macros.h" + +namespace impeller { + +class ContentContext; +struct ContentContextOptions; +class Entity; +class Surface; +class RenderPass; + +ContentContextOptions OptionsFromPass(const RenderPass& pass); + +class Contents { + public: + Contents(); + + virtual ~Contents(); + + virtual bool Render(const ContentContext& renderer, + const Entity& entity, + RenderPass& pass) const = 0; + + private: + FML_DISALLOW_COPY_AND_ASSIGN(Contents); +}; + +} // namespace impeller diff --git a/impeller/entity/contents/linear_gradient_contents.cc b/impeller/entity/contents/linear_gradient_contents.cc new file mode 100644 index 0000000000000..14ba73123be15 --- /dev/null +++ b/impeller/entity/contents/linear_gradient_contents.cc @@ -0,0 +1,79 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "linear_gradient_contents.h" + +#include "impeller/entity/contents/content_context.h" +#include "impeller/entity/entity.h" +#include "impeller/renderer/render_pass.h" +#include "impeller/renderer/tessellator.h" + +namespace impeller { + +LinearGradientContents::LinearGradientContents() = default; + +LinearGradientContents::~LinearGradientContents() = default; + +void LinearGradientContents::SetEndPoints(Point start_point, Point end_point) { + start_point_ = start_point; + end_point_ = end_point; +} + +void LinearGradientContents::SetColors(std::vector colors) { + colors_ = std::move(colors); + if (colors_.empty()) { + colors_.push_back(Color::Black()); + colors_.push_back(Color::Black()); + } else if (colors_.size() < 2u) { + colors_.push_back(colors_.back()); + } +} + +const std::vector& LinearGradientContents::GetColors() const { + return colors_; +} + +bool LinearGradientContents::Render(const ContentContext& renderer, + const Entity& entity, + RenderPass& pass) const { + using VS = GradientFillPipeline::VertexShader; + using FS = GradientFillPipeline::FragmentShader; + + auto vertices_builder = VertexBufferBuilder(); + { + auto result = Tessellator{entity.GetPath().GetFillType()}.Tessellate( + entity.GetPath().CreatePolyline(), [&vertices_builder](Point point) { + VS::PerVertexData vtx; + vtx.vertices = point; + vertices_builder.AppendVertex(vtx); + }); + if (!result) { + return false; + } + } + + VS::FrameInfo frame_info; + frame_info.mvp = Matrix::MakeOrthographic(pass.GetRenderTargetSize()) * + entity.GetTransformation(); + + FS::GradientInfo gradient_info; + gradient_info.start_point = start_point_; + gradient_info.end_point = end_point_; + gradient_info.start_color = colors_[0]; + gradient_info.end_color = colors_[1]; + + Command cmd; + cmd.label = "LinearGradientFill"; + cmd.pipeline = renderer.GetGradientFillPipeline(OptionsFromPass(pass)); + cmd.stencil_reference = entity.GetStencilDepth(); + cmd.BindVertices( + vertices_builder.CreateVertexBuffer(pass.GetTransientsBuffer())); + cmd.primitive_type = PrimitiveType::kTriangle; + FS::BindGradientInfo( + cmd, pass.GetTransientsBuffer().EmplaceUniform(gradient_info)); + VS::BindFrameInfo(cmd, pass.GetTransientsBuffer().EmplaceUniform(frame_info)); + return pass.AddCommand(std::move(cmd)); +} + +} // namespace impeller diff --git a/impeller/entity/contents/linear_gradient_contents.h b/impeller/entity/contents/linear_gradient_contents.h new file mode 100644 index 0000000000000..0467f8fb3a603 --- /dev/null +++ b/impeller/entity/contents/linear_gradient_contents.h @@ -0,0 +1,43 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#pragma once + +#include +#include +#include + +#include "flutter/fml/macros.h" +#include "impeller/entity/contents/contents.h" +#include "impeller/geometry/color.h" +#include "impeller/geometry/point.h" + +namespace impeller { + +class LinearGradientContents final : public Contents { + public: + LinearGradientContents(); + + ~LinearGradientContents() override; + + // |Contents| + bool Render(const ContentContext& renderer, + const Entity& entity, + RenderPass& pass) const override; + + void SetEndPoints(Point start_point, Point end_point); + + void SetColors(std::vector colors); + + const std::vector& GetColors() const; + + private: + Point start_point_; + Point end_point_; + std::vector colors_; + + FML_DISALLOW_COPY_AND_ASSIGN(LinearGradientContents); +}; + +} // namespace impeller diff --git a/impeller/entity/contents/solid_color_contents.cc b/impeller/entity/contents/solid_color_contents.cc new file mode 100644 index 0000000000000..ef61a1adab88a --- /dev/null +++ b/impeller/entity/contents/solid_color_contents.cc @@ -0,0 +1,83 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "solid_color_contents.h" + +#include "impeller/entity/contents/content_context.h" +#include "impeller/entity/entity.h" +#include "impeller/geometry/path.h" +#include "impeller/renderer/render_pass.h" +#include "impeller/renderer/tessellator.h" + +namespace impeller { + +SolidColorContents::SolidColorContents() = default; + +SolidColorContents::~SolidColorContents() = default; + +void SolidColorContents::SetColor(Color color) { + color_ = color; +} + +const Color& SolidColorContents::GetColor() const { + return color_; +} + +VertexBuffer SolidColorContents::CreateSolidFillVertices(const Path& path, + HostBuffer& buffer) { + using VS = SolidFillPipeline::VertexShader; + + VertexBufferBuilder vtx_builder; + + auto tesselation_result = Tessellator{path.GetFillType()}.Tessellate( + path.CreatePolyline(), [&vtx_builder](auto point) { + VS::PerVertexData vtx; + vtx.vertices = point; + vtx_builder.AppendVertex(vtx); + }); + if (!tesselation_result) { + return {}; + } + + return vtx_builder.CreateVertexBuffer(buffer); +} + +bool SolidColorContents::Render(const ContentContext& renderer, + const Entity& entity, + RenderPass& pass) const { + if (color_.IsTransparent()) { + return true; + } + + using VS = SolidFillPipeline::VertexShader; + + Command cmd; + cmd.label = "SolidFill"; + cmd.pipeline = renderer.GetSolidFillPipeline(OptionsFromPass(pass)); + cmd.stencil_reference = entity.GetStencilDepth(); + cmd.BindVertices( + CreateSolidFillVertices(entity.GetPath(), pass.GetTransientsBuffer())); + + VS::FrameInfo frame_info; + frame_info.mvp = Matrix::MakeOrthographic(pass.GetRenderTargetSize()) * + entity.GetTransformation(); + frame_info.color = color_; + VS::BindFrameInfo(cmd, pass.GetTransientsBuffer().EmplaceUniform(frame_info)); + + cmd.primitive_type = PrimitiveType::kTriangle; + + if (!pass.AddCommand(std::move(cmd))) { + return false; + } + + return true; +} + +std::unique_ptr SolidColorContents::Make(Color color) { + auto contents = std::make_unique(); + contents->SetColor(color); + return contents; +} + +} // namespace impeller diff --git a/impeller/entity/contents/solid_color_contents.h b/impeller/entity/contents/solid_color_contents.h new file mode 100644 index 0000000000000..8f2551ca2b3d5 --- /dev/null +++ b/impeller/entity/contents/solid_color_contents.h @@ -0,0 +1,47 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#pragma once + +#include +#include +#include + +#include "flutter/fml/macros.h" +#include "impeller/entity/contents/contents.h" +#include "impeller/geometry/color.h" + +namespace impeller { + +class Path; +class HostBuffer; +struct VertexBuffer; + +class SolidColorContents final : public Contents { + public: + SolidColorContents(); + + ~SolidColorContents() override; + + static std::unique_ptr Make(Color color); + + static VertexBuffer CreateSolidFillVertices(const Path& path, + HostBuffer& buffer); + + void SetColor(Color color); + + const Color& GetColor() const; + + // |Contents| + bool Render(const ContentContext& renderer, + const Entity& entity, + RenderPass& pass) const override; + + private: + Color color_; + + FML_DISALLOW_COPY_AND_ASSIGN(SolidColorContents); +}; + +} // namespace impeller diff --git a/impeller/entity/contents/solid_stroke_contents.cc b/impeller/entity/contents/solid_stroke_contents.cc new file mode 100644 index 0000000000000..35f5cd2ba0cbb --- /dev/null +++ b/impeller/entity/contents/solid_stroke_contents.cc @@ -0,0 +1,358 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "solid_stroke_contents.h" + +#include "impeller/entity/contents/content_context.h" +#include "impeller/entity/entity.h" +#include "impeller/geometry/path_builder.h" +#include "impeller/renderer/render_pass.h" + +namespace impeller { + +SolidStrokeContents::SolidStrokeContents() { + SetStrokeCap(Cap::kButt); + SetStrokeJoin(Join::kMiter); +} + +SolidStrokeContents::~SolidStrokeContents() = default; + +void SolidStrokeContents::SetColor(Color color) { + color_ = color; +} + +const Color& SolidStrokeContents::GetColor() const { + return color_; +} + +static VertexBuffer CreateSolidStrokeVertices( + const Path& path, + HostBuffer& buffer, + const SolidStrokeContents::CapProc& cap_proc, + const SolidStrokeContents::JoinProc& join_proc, + Scalar miter_limit, + const SmoothingApproximation& smoothing) { + using VS = SolidStrokeVertexShader; + + VertexBufferBuilder vtx_builder; + auto polyline = path.CreatePolyline(); + + if (polyline.points.size() < 2) { + return {}; // Nothing to render. + } + + VS::PerVertexData vtx; + + // Normal state. + Point normal; + Point previous_normal; // Used for computing joins. + + auto compute_normal = [&polyline, &normal, &previous_normal](size_t point_i) { + previous_normal = normal; + Point direction = + (polyline.points[point_i] - polyline.points[point_i - 1]).Normalize(); + normal = {-direction.y, direction.x}; + }; + + for (size_t contour_i = 0; contour_i < polyline.contours.size(); + contour_i++) { + size_t contour_start_point_i, contour_end_point_i; + std::tie(contour_start_point_i, contour_end_point_i) = + polyline.GetContourPointBounds(contour_i); + + if (contour_end_point_i - contour_start_point_i < 2) { + continue; // This contour has no renderable content. + } + + // The first point's normal is always the same as + compute_normal(contour_start_point_i + 1); + const Point contour_first_normal = normal; + + if (contour_i > 0) { + // This branch only executes when we've just finished drawing a contour + // and are switching to a new one. + // We're drawing a triangle strip, so we need to "pick up the pen" by + // appending transparent vertices between the end of the previous contour + // and the beginning of the new contour. + vtx.vertex_position = polyline.points[contour_start_point_i - 1]; + vtx.vertex_normal = {}; + vtx.pen_down = 0.0; + vtx_builder.AppendVertex(vtx); + vtx.vertex_position = polyline.points[contour_start_point_i]; + vtx_builder.AppendVertex(vtx); + } + + // Generate start cap. + if (!polyline.contours[contour_i].is_closed) { + cap_proc(vtx_builder, polyline.points[contour_start_point_i], -normal, + smoothing); + } + + // Generate contour geometry. + for (size_t point_i = contour_start_point_i; point_i < contour_end_point_i; + point_i++) { + if (point_i > contour_start_point_i) { + // Generate line rect. + vtx.vertex_position = polyline.points[point_i - 1]; + vtx.pen_down = 1.0; + vtx.vertex_normal = normal; + vtx_builder.AppendVertex(vtx); + vtx.vertex_normal = -normal; + vtx_builder.AppendVertex(vtx); + vtx.vertex_position = polyline.points[point_i]; + vtx.vertex_normal = normal; + vtx_builder.AppendVertex(vtx); + vtx.vertex_normal = -normal; + vtx_builder.AppendVertex(vtx); + + if (point_i < contour_end_point_i - 1) { + compute_normal(point_i + 1); + + // Generate join from the current line to the next line. + join_proc(vtx_builder, polyline.points[point_i], previous_normal, + normal, miter_limit, smoothing); + } + } + } + + // Generate end cap or join. + if (!polyline.contours[contour_i].is_closed) { + cap_proc(vtx_builder, polyline.points[contour_end_point_i - 1], normal, + smoothing); + } else { + join_proc(vtx_builder, polyline.points[contour_start_point_i], normal, + contour_first_normal, miter_limit, smoothing); + } + } + + return vtx_builder.CreateVertexBuffer(buffer); +} + +bool SolidStrokeContents::Render(const ContentContext& renderer, + const Entity& entity, + RenderPass& pass) const { + if (color_.IsTransparent() || stroke_size_ <= 0.0) { + return true; + } + + using VS = SolidStrokeVertexShader; + + VS::FrameInfo frame_info; + frame_info.mvp = Matrix::MakeOrthographic(pass.GetRenderTargetSize()) * + entity.GetTransformation(); + + VS::StrokeInfo stroke_info; + stroke_info.color = color_; + stroke_info.size = stroke_size_; + + Command cmd; + cmd.primitive_type = PrimitiveType::kTriangleStrip; + cmd.label = "SolidStroke"; + cmd.pipeline = renderer.GetSolidStrokePipeline(OptionsFromPass(pass)); + cmd.stencil_reference = entity.GetStencilDepth(); + cmd.BindVertices(CreateSolidStrokeVertices( + entity.GetPath(), pass.GetTransientsBuffer(), cap_proc_, join_proc_, + miter_limit_, arc_smoothing_approximation_)); + VS::BindFrameInfo(cmd, pass.GetTransientsBuffer().EmplaceUniform(frame_info)); + VS::BindStrokeInfo(cmd, + pass.GetTransientsBuffer().EmplaceUniform(stroke_info)); + + pass.AddCommand(std::move(cmd)); + + return true; +} + +void SolidStrokeContents::SetStrokeSize(Scalar size) { + stroke_size_ = size; + arc_smoothing_approximation_ = SmoothingApproximation(5.0 / size, 0.0, 0.0); +} + +Scalar SolidStrokeContents::GetStrokeSize() const { + return stroke_size_; +} + +void SolidStrokeContents::SetStrokeMiter(Scalar miter_limit) { + if (miter_limit < 0) { + return; // Skia behaves like this. + } + miter_limit_ = miter_limit; +} + +Scalar SolidStrokeContents::GetStrokeMiter() { + return miter_limit_; +} + +void SolidStrokeContents::SetStrokeCap(Cap cap) { + cap_ = cap; + + using VS = SolidStrokeVertexShader; + switch (cap) { + case Cap::kButt: + cap_proc_ = [](VertexBufferBuilder& vtx_builder, + const Point& position, const Point& normal, + const SmoothingApproximation& smoothing) {}; + break; + case Cap::kRound: + cap_proc_ = [](VertexBufferBuilder& vtx_builder, + const Point& position, const Point& normal, + const SmoothingApproximation& smoothing) { + SolidStrokeVertexShader::PerVertexData vtx; + vtx.vertex_position = position; + vtx.pen_down = 1.0; + + Point forward(normal.y, -normal.x); + + auto arc_points = + CubicPathComponent( + normal, normal + forward * PathBuilder::kArcApproximationMagic, + forward + normal * PathBuilder::kArcApproximationMagic, forward) + .CreatePolyline(smoothing); + + vtx.vertex_normal = normal; + vtx_builder.AppendVertex(vtx); + vtx.vertex_normal = -normal; + vtx_builder.AppendVertex(vtx); + for (const auto& point : arc_points) { + vtx.vertex_normal = point; + vtx_builder.AppendVertex(vtx); + vtx.vertex_normal = (-point).Reflect(forward); + vtx_builder.AppendVertex(vtx); + } + }; + break; + case Cap::kSquare: + cap_proc_ = [](VertexBufferBuilder& vtx_builder, + const Point& position, const Point& normal, + const SmoothingApproximation& smoothing) { + SolidStrokeVertexShader::PerVertexData vtx; + vtx.vertex_position = position; + vtx.pen_down = 1.0; + + Point forward(normal.y, -normal.x); + + vtx.vertex_normal = normal; + vtx_builder.AppendVertex(vtx); + vtx.vertex_normal = -normal; + vtx_builder.AppendVertex(vtx); + vtx.vertex_normal = normal + forward; + vtx_builder.AppendVertex(vtx); + vtx.vertex_normal = -normal + forward; + vtx_builder.AppendVertex(vtx); + }; + break; + } +} + +SolidStrokeContents::Cap SolidStrokeContents::GetStrokeCap() { + return cap_; +} + +static Scalar CreateBevelAndGetDirection( + VertexBufferBuilder& vtx_builder, + const Point& position, + const Point& start_normal, + const Point& end_normal) { + SolidStrokeVertexShader::PerVertexData vtx; + vtx.vertex_position = position; + vtx.pen_down = 1.0; + vtx.vertex_normal = {}; + vtx_builder.AppendVertex(vtx); + + Scalar dir = start_normal.Cross(end_normal) > 0 ? -1 : 1; + vtx.vertex_normal = start_normal * dir; + vtx_builder.AppendVertex(vtx); + vtx.vertex_normal = end_normal * dir; + vtx_builder.AppendVertex(vtx); + + return dir; +} + +void SolidStrokeContents::SetStrokeJoin(Join join) { + join_ = join; + + using VS = SolidStrokeVertexShader; + switch (join) { + case Join::kBevel: + join_proc_ = [](VertexBufferBuilder& vtx_builder, + const Point& position, const Point& start_normal, + const Point& end_normal, Scalar miter_limit, + const SmoothingApproximation& smoothing) { + CreateBevelAndGetDirection(vtx_builder, position, start_normal, + end_normal); + }; + break; + case Join::kMiter: + join_proc_ = [](VertexBufferBuilder& vtx_builder, + const Point& position, const Point& start_normal, + const Point& end_normal, Scalar miter_limit, + const SmoothingApproximation& smoothing) { + // 1 for no joint (straight line), 0 for max joint (180 degrees). + Scalar alignment = (start_normal.Dot(end_normal) + 1) / 2; + if (ScalarNearlyEqual(alignment, 1)) { + return; + } + + Scalar dir = CreateBevelAndGetDirection(vtx_builder, position, + start_normal, end_normal); + + Point miter_point = (start_normal + end_normal) / 2 / alignment; + if (miter_point.GetDistanceSquared({0, 0}) > + miter_limit * miter_limit) { + return; // Convert to bevel when we exceed the miter limit. + } + + // Outer miter point. + SolidStrokeVertexShader::PerVertexData vtx; + vtx.vertex_position = position; + vtx.pen_down = 1.0; + vtx.vertex_normal = miter_point * dir; + vtx_builder.AppendVertex(vtx); + }; + break; + case Join::kRound: + join_proc_ = [](VertexBufferBuilder& vtx_builder, + const Point& position, const Point& start_normal, + const Point& end_normal, Scalar miter_limit, + const SmoothingApproximation& smoothing) { + // 0 for no joint (straight line), 1 for max joint (180 degrees). + Scalar alignment = 1 - (start_normal.Dot(end_normal) + 1) / 2; + if (ScalarNearlyEqual(alignment, 0)) { + return; + } + + Scalar dir = CreateBevelAndGetDirection(vtx_builder, position, + start_normal, end_normal); + + Point middle = (start_normal + end_normal).Normalize(); + Point middle_handle = middle + Point(-middle.y, middle.x) * + PathBuilder::kArcApproximationMagic * + alignment * dir; + Point start_handle = + start_normal + Point(start_normal.y, -start_normal.x) * + PathBuilder::kArcApproximationMagic * alignment * + dir; + + auto arc_points = CubicPathComponent(start_normal, start_handle, + middle_handle, middle) + .CreatePolyline(smoothing); + + SolidStrokeVertexShader::PerVertexData vtx; + vtx.vertex_position = position; + vtx.pen_down = 1.0; + for (const auto& point : arc_points) { + vtx.vertex_normal = point * dir; + vtx_builder.AppendVertex(vtx); + vtx.vertex_normal = (-point * dir).Reflect(middle); + vtx_builder.AppendVertex(vtx); + } + }; + break; + } +} + +SolidStrokeContents::Join SolidStrokeContents::GetStrokeJoin() { + return join_; +} + +} // namespace impeller diff --git a/impeller/entity/contents/solid_stroke_contents.h b/impeller/entity/contents/solid_stroke_contents.h new file mode 100644 index 0000000000000..8bb1501e0434e --- /dev/null +++ b/impeller/entity/contents/solid_stroke_contents.h @@ -0,0 +1,92 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#pragma once + +#include +#include +#include + +#include "flutter/fml/macros.h" +#include "impeller/entity/contents/contents.h" +#include "impeller/entity/solid_stroke.vert.h" +#include "impeller/geometry/color.h" +#include "impeller/geometry/path_component.h" +#include "impeller/geometry/point.h" + +namespace impeller { + +class SolidStrokeContents final : public Contents { + public: + enum class Cap { + kButt, + kRound, + kSquare, + }; + + enum class Join { + kMiter, + kRound, + kBevel, + }; + + using CapProc = std::function& vtx_builder, + const Point& position, + const Point& normal, + const SmoothingApproximation& smoothing)>; + using JoinProc = std::function& vtx_builder, + const Point& position, + const Point& start_normal, + const Point& end_normal, + Scalar miter_limit, + const SmoothingApproximation& smoothing)>; + + SolidStrokeContents(); + + ~SolidStrokeContents() override; + + void SetColor(Color color); + + const Color& GetColor() const; + + void SetStrokeSize(Scalar size); + + Scalar GetStrokeSize() const; + + void SetStrokeMiter(Scalar miter_limit); + + Scalar GetStrokeMiter(); + + void SetStrokeCap(Cap cap); + + Cap GetStrokeCap(); + + void SetStrokeJoin(Join join); + + Join GetStrokeJoin(); + + // |Contents| + bool Render(const ContentContext& renderer, + const Entity& entity, + RenderPass& pass) const override; + + private: + SmoothingApproximation arc_smoothing_approximation_; + + Color color_; + Scalar stroke_size_ = 0.0; + Scalar miter_limit_ = 4.0; + + Cap cap_; + CapProc cap_proc_; + + Join join_; + JoinProc join_proc_; + + FML_DISALLOW_COPY_AND_ASSIGN(SolidStrokeContents); +}; + +} // namespace impeller diff --git a/impeller/entity/contents/text_contents.cc b/impeller/entity/contents/text_contents.cc new file mode 100644 index 0000000000000..8e8d7fa5c768a --- /dev/null +++ b/impeller/entity/contents/text_contents.cc @@ -0,0 +1,134 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "text_contents.h" + +#include "impeller/entity/contents/content_context.h" +#include "impeller/entity/entity.h" +#include "impeller/geometry/path_builder.h" +#include "impeller/renderer/render_pass.h" +#include "impeller/renderer/sampler_library.h" +#include "impeller/renderer/tessellator.h" +#include "impeller/typographer/glyph_atlas.h" + +namespace impeller { +TextContents::TextContents() = default; + +TextContents::~TextContents() = default; + +void TextContents::SetTextFrame(TextFrame frame) { + frame_ = std::move(frame); +} + +void TextContents::SetGlyphAtlas(std::shared_ptr atlas) { + atlas_ = std::move(atlas); +} + +void TextContents::SetColor(Color color) { + color_ = color; +} + +bool TextContents::Render(const ContentContext& renderer, + const Entity& entity, + RenderPass& pass) const { + if (color_.IsTransparent()) { + return true; + } + + if (!atlas_ || !atlas_->IsValid()) { + VALIDATION_LOG << "Cannot render glyphs without prepared atlas."; + return false; + } + + using VS = GlyphAtlasPipeline::VertexShader; + using FS = GlyphAtlasPipeline::FragmentShader; + + // Information shared by all glyph draw calls. + Command cmd; + cmd.label = "Glyph"; + cmd.primitive_type = PrimitiveType::kTriangle; + cmd.pipeline = renderer.GetGlyphAtlasPipeline(OptionsFromPass(pass)); + cmd.stencil_reference = entity.GetStencilDepth(); + + // Common vertex uniforms for all glyphs. + VS::FrameInfo frame_info; + frame_info.mvp = Matrix::MakeOrthographic(pass.GetRenderTargetSize()) * + entity.GetTransformation(); + frame_info.atlas_size = + Point{static_cast(atlas_->GetTexture()->GetSize().width), + static_cast(atlas_->GetTexture()->GetSize().height)}; + frame_info.text_color = ToVector(color_); + VS::BindFrameInfo(cmd, pass.GetTransientsBuffer().EmplaceUniform(frame_info)); + + // Common fragment uniforms for all glyphs. + FS::BindGlyphAtlasSampler( + cmd, // command + atlas_->GetTexture(), // texture + renderer.GetContext()->GetSamplerLibrary()->GetSampler({}) // sampler + ); + + // Common vertex information for all glyphs. + // Currently, glyphs are being drawn individually. This can be batched later. + // But we don't want to give each glyph unique vertex information. So all + // glyphs are given the same vertex information in the form of a unit-sized + // quad. The size of the glyph is specified in uniform data and the vertex + // shader uses this to size the glyph correctly. The interpolated vertex + // information is also used in the fragment shader to sample from the glyph + // atlas. + { + VertexBufferBuilder vertex_builder; + if (!Tessellator{FillType::kPositive}.Tessellate( + PathBuilder{} + .AddRect(Rect::MakeXYWH(0.0, 0.0, 1.0, 1.0)) + .TakePath() + .CreatePolyline(), + [&vertex_builder](Point point) { + VS::PerVertexData vtx; + vtx.unit_vertex = point; + vertex_builder.AppendVertex(std::move(vtx)); + })) { + return false; + } + auto dummy = vertex_builder.CreateVertexBuffer(pass.GetTransientsBuffer()); + auto vertex_buffer = dummy; + if (!vertex_buffer) { + return false; + } + cmd.BindVertices(std::move(vertex_buffer)); + } + + // Iterate through all the runs in the blob. + for (const auto& run : frame_.GetRuns()) { + auto font = run.GetFont(); + auto glyph_size = ISize::Ceil(font.GetMetrics().GetBoundingBox().size); + // Draw each glyph individually. This should probably be batched. + for (const auto& glyph_position : run.GetGlyphPositions()) { + FontGlyphPair font_glyph_pair{font, glyph_position.glyph}; + auto atlas_glyph_pos = atlas_->FindFontGlyphPosition(font_glyph_pair); + if (!atlas_glyph_pos.has_value()) { + VALIDATION_LOG << "Could not find glyph position in the atlas."; + return false; + } + + VS::GlyphInfo glyph_info; + glyph_info.position = glyph_position.position.Translate( + {font.GetMetrics().min_extent.x, font.GetMetrics().ascent, 0.0}); + glyph_info.glyph_size = {static_cast(glyph_size.width), + static_cast(glyph_size.height)}; + glyph_info.atlas_position = atlas_glyph_pos->origin; + glyph_info.atlas_glyph_size = {atlas_glyph_pos->size.width, + atlas_glyph_pos->size.height}; + VS::BindGlyphInfo(cmd, + pass.GetTransientsBuffer().EmplaceUniform(glyph_info)); + + if (!pass.AddCommand(cmd)) { + return false; + } + } + } + + return true; +} + +} // namespace impeller diff --git a/impeller/entity/contents/text_contents.h b/impeller/entity/contents/text_contents.h new file mode 100644 index 0000000000000..370bdd1973d2e --- /dev/null +++ b/impeller/entity/contents/text_contents.h @@ -0,0 +1,45 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#pragma once + +#include +#include +#include + +#include "flutter/fml/macros.h" +#include "impeller/entity/contents/contents.h" +#include "impeller/geometry/color.h" +#include "impeller/typographer/text_frame.h" + +namespace impeller { + +class GlyphAtlas; + +class TextContents final : public Contents { + public: + TextContents(); + + ~TextContents(); + + void SetTextFrame(TextFrame frame); + + void SetGlyphAtlas(std::shared_ptr atlas); + + void SetColor(Color color); + + // |Contents| + bool Render(const ContentContext& renderer, + const Entity& entity, + RenderPass& pass) const override; + + private: + TextFrame frame_; + Color color_; + std::shared_ptr atlas_; + + FML_DISALLOW_COPY_AND_ASSIGN(TextContents); +}; + +} // namespace impeller diff --git a/impeller/entity/contents/texture_contents.cc b/impeller/entity/contents/texture_contents.cc new file mode 100644 index 0000000000000..9717df6bfca6d --- /dev/null +++ b/impeller/entity/contents/texture_contents.cc @@ -0,0 +1,115 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "texture_contents.h" + +#include "impeller/entity/contents/content_context.h" +#include "impeller/entity/entity.h" +#include "impeller/entity/texture_fill.frag.h" +#include "impeller/entity/texture_fill.vert.h" +#include "impeller/renderer/render_pass.h" +#include "impeller/renderer/sampler_library.h" +#include "impeller/renderer/tessellator.h" + +namespace impeller { + +TextureContents::TextureContents() = default; + +TextureContents::~TextureContents() = default; + +void TextureContents::SetTexture(std::shared_ptr texture) { + texture_ = std::move(texture); +} + +std::shared_ptr TextureContents::GetTexture() const { + return texture_; +} + +void TextureContents::SetOpacity(Scalar opacity) { + opacity_ = opacity; +} + +bool TextureContents::Render(const ContentContext& renderer, + const Entity& entity, + RenderPass& pass) const { + if (texture_ == nullptr) { + return true; + } + + using VS = TextureFillVertexShader; + using FS = TextureFillFragmentShader; + + const auto coverage_rect = entity.GetPath().GetBoundingBox(); + + if (!coverage_rect.has_value()) { + return true; + } + + if (coverage_rect->size.IsEmpty()) { + return true; + } + + const auto texture_size = texture_->GetSize(); + if (texture_size.IsEmpty()) { + return true; + } + + if (source_rect_.IsEmpty()) { + return true; + } + + VertexBufferBuilder vertex_builder; + { + const auto tess_result = + Tessellator{entity.GetPath().GetFillType()}.Tessellate( + entity.GetPath().CreatePolyline(), + [this, &vertex_builder, &coverage_rect, &texture_size](Point vtx) { + VS::PerVertexData data; + data.vertices = vtx; + auto coverage_coords = + (vtx - coverage_rect->origin) / coverage_rect->size; + data.texture_coords = + (source_rect_.origin + source_rect_.size * coverage_coords) / + texture_size; + vertex_builder.AppendVertex(data); + }); + if (!tess_result) { + return false; + } + } + + if (!vertex_builder.HasVertices()) { + return true; + } + + auto& host_buffer = pass.GetTransientsBuffer(); + + VS::FrameInfo frame_info; + frame_info.mvp = Matrix::MakeOrthographic(pass.GetRenderTargetSize()) * + entity.GetTransformation(); + frame_info.alpha = opacity_; + + Command cmd; + cmd.label = "TextureFill"; + cmd.pipeline = renderer.GetTexturePipeline(OptionsFromPass(pass)); + cmd.stencil_reference = entity.GetStencilDepth(); + cmd.BindVertices(vertex_builder.CreateVertexBuffer(host_buffer)); + VS::BindFrameInfo(cmd, host_buffer.EmplaceUniform(frame_info)); + FS::BindTextureSampler( + cmd, texture_, + renderer.GetContext()->GetSamplerLibrary()->GetSampler({})); + pass.AddCommand(std::move(cmd)); + + return true; +} + +void TextureContents::SetSourceRect(const IRect& source_rect) { + source_rect_ = source_rect; +} + +const IRect& TextureContents::GetSourceRect() const { + return source_rect_; +} + +} // namespace impeller diff --git a/impeller/entity/contents/texture_contents.h b/impeller/entity/contents/texture_contents.h new file mode 100644 index 0000000000000..fd3ff9c13200b --- /dev/null +++ b/impeller/entity/contents/texture_contents.h @@ -0,0 +1,48 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#pragma once + +#include +#include +#include + +#include "flutter/fml/macros.h" +#include "impeller/entity/contents/contents.h" +#include "impeller/geometry/rect.h" + +namespace impeller { + +class Texture; + +class TextureContents final : public Contents { + public: + TextureContents(); + + ~TextureContents() override; + + void SetTexture(std::shared_ptr texture); + + std::shared_ptr GetTexture() const; + + void SetSourceRect(const IRect& source_rect); + + void SetOpacity(Scalar opacity); + + const IRect& GetSourceRect() const; + + // |Contents| + bool Render(const ContentContext& renderer, + const Entity& entity, + RenderPass& pass) const override; + + public: + std::shared_ptr texture_; + IRect source_rect_; + Scalar opacity_ = 1.0f; + + FML_DISALLOW_COPY_AND_ASSIGN(TextureContents); +}; + +} // namespace impeller diff --git a/impeller/entity/entity.cc b/impeller/entity/entity.cc index 701702b6690ec..dc976aa6874d3 100644 --- a/impeller/entity/entity.cc +++ b/impeller/entity/entity.cc @@ -4,7 +4,7 @@ #include "impeller/entity/entity.h" -#include "impeller/entity/content_context.h" +#include "impeller/entity/contents/content_context.h" #include "impeller/renderer/render_pass.h" namespace impeller { diff --git a/impeller/entity/entity.h b/impeller/entity/entity.h index 13cc918ace955..4cad573c49136 100644 --- a/impeller/entity/entity.h +++ b/impeller/entity/entity.h @@ -4,7 +4,7 @@ #pragma once -#include "impeller/entity/contents.h" +#include "impeller/entity/contents/contents.h" #include "impeller/geometry/color.h" #include "impeller/geometry/matrix.h" #include "impeller/geometry/path.h" diff --git a/impeller/entity/entity_pass.cc b/impeller/entity/entity_pass.cc index 69c14c888a17b..b33f44400227c 100644 --- a/impeller/entity/entity_pass.cc +++ b/impeller/entity/entity_pass.cc @@ -5,7 +5,7 @@ #include "impeller/entity/entity_pass.h" #include "flutter/fml/trace_event.h" -#include "impeller/entity/content_context.h" +#include "impeller/entity/contents/content_context.h" #include "impeller/geometry/path_builder.h" #include "impeller/renderer/command_buffer.h" #include "impeller/renderer/render_pass.h" diff --git a/impeller/entity/entity_pass.h b/impeller/entity/entity_pass.h index cff63df7ac68a..e2305f92fcd28 100644 --- a/impeller/entity/entity_pass.h +++ b/impeller/entity/entity_pass.h @@ -9,7 +9,7 @@ #include #include "flutter/fml/macros.h" -#include "impeller/entity/contents.h" +#include "impeller/entity/contents/contents.h" #include "impeller/entity/entity.h" #include "impeller/entity/entity_pass_delegate.h" #include "impeller/renderer/render_target.h" diff --git a/impeller/entity/entity_pass_delegate.h b/impeller/entity/entity_pass_delegate.h index 1325bcd920227..19dd4f01f8fdc 100644 --- a/impeller/entity/entity_pass_delegate.h +++ b/impeller/entity/entity_pass_delegate.h @@ -7,7 +7,7 @@ #include #include "flutter/fml/macros.h" -#include "impeller/entity/contents.h" +#include "impeller/entity/contents/contents.h" #include "impeller/renderer/texture.h" namespace impeller { diff --git a/impeller/entity/entity_playground.cc b/impeller/entity/entity_playground.cc index e65451ac8659d..da4f72d7982bb 100644 --- a/impeller/entity/entity_playground.cc +++ b/impeller/entity/entity_playground.cc @@ -4,7 +4,7 @@ #include "impeller/entity/entity_playground.h" -#include "impeller/entity/content_context.h" +#include "impeller/entity/contents/content_context.h" namespace impeller { diff --git a/impeller/entity/entity_playground.h b/impeller/entity/entity_playground.h index c506b72dc5384..68b6bad83145e 100644 --- a/impeller/entity/entity_playground.h +++ b/impeller/entity/entity_playground.h @@ -5,7 +5,7 @@ #pragma once #include "flutter/fml/macros.h" -#include "impeller/entity/content_context.h" +#include "impeller/entity/contents/content_context.h" #include "impeller/entity/entity.h" #include "impeller/playground/playground.h" diff --git a/impeller/entity/entity_unittests.cc b/impeller/entity/entity_unittests.cc index 927a32e3b8d31..f442fa6907ccc 100644 --- a/impeller/entity/entity_unittests.cc +++ b/impeller/entity/entity_unittests.cc @@ -2,8 +2,9 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -#include "entity/contents.h" #include "flutter/testing/testing.h" +#include "impeller/entity/contents/solid_color_contents.h" +#include "impeller/entity/contents/solid_stroke_contents.h" #include "impeller/entity/entity.h" #include "impeller/entity/entity_playground.h" #include "impeller/geometry/path_builder.h" From 149a0f264217121b084f6d5c99fbd750ead5019c Mon Sep 17 00:00:00 2001 From: Dan Field Date: Fri, 4 Mar 2022 20:02:50 -0800 Subject: [PATCH 326/433] Expose C API for tessellator, move to own subdir (#18) --- impeller/BUILD.gn | 1 + .../contents/linear_gradient_contents.cc | 2 +- .../entity/contents/solid_color_contents.cc | 2 +- impeller/entity/contents/text_contents.cc | 2 +- impeller/entity/contents/texture_contents.cc | 2 +- impeller/renderer/BUILD.gn | 5 +- impeller/renderer/command.h | 1 + impeller/renderer/formats.h | 5 - impeller/renderer/renderer_unittests.cc | 2 +- impeller/renderer/tessellator.cc | 2 +- impeller/tessellator/BUILD.gn | 33 ++++ impeller/tessellator/c/tessellator.cc | 68 ++++++++ impeller/tessellator/c/tessellator.h | 44 +++++ .../tessellator/dart/lib/tessellator.dart | 155 ++++++++++++++++++ impeller/tessellator/dart/pubspec.yaml | 11 ++ impeller/tessellator/tessellator.cc | 121 ++++++++++++++ .../{renderer => tessellator}/tessellator.h | 6 +- 17 files changed, 446 insertions(+), 16 deletions(-) create mode 100644 impeller/tessellator/BUILD.gn create mode 100644 impeller/tessellator/c/tessellator.cc create mode 100644 impeller/tessellator/c/tessellator.h create mode 100644 impeller/tessellator/dart/lib/tessellator.dart create mode 100644 impeller/tessellator/dart/pubspec.yaml create mode 100644 impeller/tessellator/tessellator.cc rename impeller/{renderer => tessellator}/tessellator.h (95%) diff --git a/impeller/BUILD.gn b/impeller/BUILD.gn index d3b6bb6de7192..aa258109d90be 100644 --- a/impeller/BUILD.gn +++ b/impeller/BUILD.gn @@ -21,6 +21,7 @@ group("impeller") { "geometry", "image", "renderer", + "tessellator", "typographer", ] diff --git a/impeller/entity/contents/linear_gradient_contents.cc b/impeller/entity/contents/linear_gradient_contents.cc index 14ba73123be15..55b9416c86269 100644 --- a/impeller/entity/contents/linear_gradient_contents.cc +++ b/impeller/entity/contents/linear_gradient_contents.cc @@ -7,7 +7,7 @@ #include "impeller/entity/contents/content_context.h" #include "impeller/entity/entity.h" #include "impeller/renderer/render_pass.h" -#include "impeller/renderer/tessellator.h" +#include "impeller/tessellator/tessellator.h" namespace impeller { diff --git a/impeller/entity/contents/solid_color_contents.cc b/impeller/entity/contents/solid_color_contents.cc index ef61a1adab88a..71318f8cb6dc5 100644 --- a/impeller/entity/contents/solid_color_contents.cc +++ b/impeller/entity/contents/solid_color_contents.cc @@ -8,7 +8,7 @@ #include "impeller/entity/entity.h" #include "impeller/geometry/path.h" #include "impeller/renderer/render_pass.h" -#include "impeller/renderer/tessellator.h" +#include "impeller/tessellator/tessellator.h" namespace impeller { diff --git a/impeller/entity/contents/text_contents.cc b/impeller/entity/contents/text_contents.cc index 8e8d7fa5c768a..149f142e930ad 100644 --- a/impeller/entity/contents/text_contents.cc +++ b/impeller/entity/contents/text_contents.cc @@ -9,7 +9,7 @@ #include "impeller/geometry/path_builder.h" #include "impeller/renderer/render_pass.h" #include "impeller/renderer/sampler_library.h" -#include "impeller/renderer/tessellator.h" +#include "impeller/tessellator/tessellator.h" #include "impeller/typographer/glyph_atlas.h" namespace impeller { diff --git a/impeller/entity/contents/texture_contents.cc b/impeller/entity/contents/texture_contents.cc index 9717df6bfca6d..7e57278afb019 100644 --- a/impeller/entity/contents/texture_contents.cc +++ b/impeller/entity/contents/texture_contents.cc @@ -10,7 +10,7 @@ #include "impeller/entity/texture_fill.vert.h" #include "impeller/renderer/render_pass.h" #include "impeller/renderer/sampler_library.h" -#include "impeller/renderer/tessellator.h" +#include "impeller/tessellator/tessellator.h" namespace impeller { diff --git a/impeller/renderer/BUILD.gn b/impeller/renderer/BUILD.gn index 8e208638f22a4..540e1e45eb3f2 100644 --- a/impeller/renderer/BUILD.gn +++ b/impeller/renderer/BUILD.gn @@ -91,8 +91,6 @@ impeller_component("renderer") { "shader_types.h", "surface.h", "surface.cc", - "tessellator.cc", - "tessellator.h", "texture.h", "texture.cc", "texture_descriptor.h", @@ -109,10 +107,9 @@ impeller_component("renderer") { "../base", "../geometry", "../image", + "../tessellator", ] - deps = [ "//third_party/libtess2" ] - frameworks = [ "Metal.framework" ] } diff --git a/impeller/renderer/command.h b/impeller/renderer/command.h index 0e4b1c99ef5ab..3fdb0ee1716bd 100644 --- a/impeller/renderer/command.h +++ b/impeller/renderer/command.h @@ -20,6 +20,7 @@ #include "impeller/renderer/texture.h" #include "impeller/renderer/vertex_buffer.h" #include "impeller/renderer/vertex_buffer_builder.h" +#include "impeller/tessellator/tessellator.h" namespace impeller { diff --git a/impeller/renderer/formats.h b/impeller/renderer/formats.h index dedc080a80541..f1083e3e7c459 100644 --- a/impeller/renderer/formats.h +++ b/impeller/renderer/formats.h @@ -131,11 +131,6 @@ enum class TextureUsage : TextureUsageMask { kRenderTarget = 1 << 2, }; -enum class WindingOrder { - kClockwise, - kCounterClockwise, -}; - enum class CullMode { kNone, kFrontFace, diff --git a/impeller/renderer/renderer_unittests.cc b/impeller/renderer/renderer_unittests.cc index ca08dab43b8cd..01cc768328f40 100644 --- a/impeller/renderer/renderer_unittests.cc +++ b/impeller/renderer/renderer_unittests.cc @@ -21,7 +21,7 @@ #include "impeller/renderer/sampler_descriptor.h" #include "impeller/renderer/sampler_library.h" #include "impeller/renderer/surface.h" -#include "impeller/renderer/tessellator.h" +#include "impeller/tessellator/tessellator.h" #include "impeller/renderer/vertex_buffer_builder.h" namespace impeller { diff --git a/impeller/renderer/tessellator.cc b/impeller/renderer/tessellator.cc index 44f18306e0bb8..eb8bb3ec43a2c 100644 --- a/impeller/renderer/tessellator.cc +++ b/impeller/renderer/tessellator.cc @@ -2,7 +2,7 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -#include "impeller/renderer/tessellator.h" +#include "impeller/tessellator/tessellator.h" #include "flutter/fml/logging.h" #include "flutter/fml/trace_event.h" diff --git a/impeller/tessellator/BUILD.gn b/impeller/tessellator/BUILD.gn new file mode 100644 index 0000000000000..cde82096ffba8 --- /dev/null +++ b/impeller/tessellator/BUILD.gn @@ -0,0 +1,33 @@ +# Copyright 2013 The Flutter Authors. All rights reserved. +# Use of this source code is governed by a BSD-style license that can be +# found in the LICENSE file. + +import("//flutter/impeller/tools/impeller.gni") + +impeller_component("tessellator") { + sources = [ + "tessellator.cc", + "tessellator.h", + ] + + public_deps = [ "../geometry" ] + + deps = [ "//third_party/libtess2" ] +} + +shared_library("tessellator_shared") { + output_name = "tessellator" + + sources = [ + "c/tessellator.h", + "c/tessellator.cc", + "tessellator.cc", + "tessellator.h", + ] + + deps = [ + "../geometry", + "//flutter/fml", + "//third_party/libtess2", + ] +} diff --git a/impeller/tessellator/c/tessellator.cc b/impeller/tessellator/c/tessellator.cc new file mode 100644 index 0000000000000..119835dd3fb80 --- /dev/null +++ b/impeller/tessellator/c/tessellator.cc @@ -0,0 +1,68 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "tessellator.h" + +#include + +namespace impeller { +PathBuilder* CreatePathBuilder() { + return new PathBuilder(); +} + +void DestroyPathBuilder(PathBuilder* builder) { + delete builder; +} + +void MoveTo(PathBuilder* builder, Scalar x, Scalar y) { + builder->MoveTo(Point(x, y)); +} + +void LineTo(PathBuilder* builder, Scalar x, Scalar y) { + builder->LineTo(Point(x, y)); +} + +void CubicTo(PathBuilder* builder, + Scalar x1, + Scalar y1, + Scalar x2, + Scalar y2, + Scalar x3, + Scalar y3) { + builder->CubicCurveTo(Point(x1, y1), Point(x2, y2), Point(x3, y3)); +} + +void Close(PathBuilder* builder) { + builder->Close(); +} + +struct Vertices* Tessellate(PathBuilder* builder) { + auto path = builder->CopyPath(); + auto polyline = path.CreatePolyline(); + + std::vector points; + if (!Tessellator{path.GetFillType()}.Tessellate(polyline, + [&points](Point vertex) { + points.push_back(vertex.x); + points.push_back(vertex.y); + })) { + return nullptr; + } + + Vertices* vertices = new Vertices(); + vertices->points = new float[points.size()]; + if (!vertices->points) { + return nullptr; + } + vertices->length = points.size(); + std::copy(points.begin(), points.end(), vertices->points); + return vertices; +} + +void DestroyVertices(Vertices* vertices) { + delete vertices->points; + delete vertices; +} + +} // namespace impeller diff --git a/impeller/tessellator/c/tessellator.h b/impeller/tessellator/c/tessellator.h new file mode 100644 index 0000000000000..1ca6efd88002f --- /dev/null +++ b/impeller/tessellator/c/tessellator.h @@ -0,0 +1,44 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#pragma once + +#include "impeller/geometry/path_builder.h" +#include "impeller/tessellator/tessellator.h" + +#define IMPELLER_API __attribute__((visibility("default"))) + +extern "C" { + +namespace impeller { + +struct IMPELLER_API Vertices { + float* points; + uint32_t length; +}; + +IMPELLER_API PathBuilder* CreatePathBuilder(); + +IMPELLER_API void DestroyPathBuilder(PathBuilder* builder); + +IMPELLER_API void MoveTo(PathBuilder* builder, Scalar x, Scalar y); + +IMPELLER_API void LineTo(PathBuilder* builder, Scalar x, Scalar y); + +IMPELLER_API void CubicTo(PathBuilder* builder, + Scalar x1, + Scalar y1, + Scalar x2, + Scalar y2, + Scalar x3, + Scalar y3); + +IMPELLER_API void Close(PathBuilder* builder); + +IMPELLER_API struct Vertices* Tessellate(PathBuilder* builder); + +IMPELLER_API void DestroyVertices(Vertices* vertices); + +} // namespace impeller +} diff --git a/impeller/tessellator/dart/lib/tessellator.dart b/impeller/tessellator/dart/lib/tessellator.dart new file mode 100644 index 0000000000000..2cc9358ebb85e --- /dev/null +++ b/impeller/tessellator/dart/lib/tessellator.dart @@ -0,0 +1,155 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +// ignore_for_file: camel_case_types +import 'dart:ffi' as ffi; +import 'dart:io'; +import 'dart:typed_data'; + +/// Creates vertices from path commands. +/// +/// First, build up the path contours with the [moveTo], [lineTo], [cubicTo], +/// and [close] methods. All methods expect absolute coordinates. +/// +/// Then, use the [tessellate] method to create a [Float32List] of vertex pairs. +/// +/// Finally, use the [dispose] method to clean up native resources. After +/// [dispose] has been called, this class must not be used again. +class VerticesBuilder { + VerticesBuilder() : _builder = _createPathFn(); + + ffi.Pointer<_PathBuilder>? _builder; + final List> _vertices = >[]; + + /// Adds a move verb to the absolute coordinates x,y. + void moveTo(double x, double y) { + assert(_builder != null); + _moveToFn(_builder!, x, y); + } + + /// Adds a line verb to the absolute coordinates x,y. + void lineTo(double x, double y) { + assert(_builder != null); + _lineToFn(_builder!, x, y); + } + + /// Adds a cubic Bezier curve with x1,y1 as the first control point, x2,y2 as + /// the second control point, and end point x3,y3. + void cubicTo( + double x1, + double y1, + double x2, + double y2, + double x3, + double y3, + ) { + assert(_builder != null); + _cubicToFn(_builder!, x1, y1, x2, y2, x3, y3); + } + + /// Adds a close command to the start of the current contour. + void close() { + assert(_builder != null); + closeFn(_builder!, true); + } + + /// Tessellates the path created by the previous method calls into a list of + /// vertices. + Float32List tessellate() { + assert(_vertices.isEmpty); + assert(_builder != null); + final ffi.Pointer<_Vertices> vertices = _tessellateFn(_builder!); + _vertices.add(vertices); + return vertices.ref.points.asTypedList(vertices.ref.size); + } + + /// Releases native resources. + /// + /// After calling dispose, this class must not be used again. + void dispose() { + assert(_builder != null); + _vertices.forEach(_destroyVerticesFn); + _destroyFn(_builder!); + _vertices.clear(); + _builder = null; + } +} + +// TODO(dnfield): Figure out where to put this. +// https://github.com/flutter/flutter/issues/99563 +final ffi.DynamicLibrary _dylib = () { + if (Platform.isWindows) { + return ffi.DynamicLibrary.open('tessellator.dll'); + } else if (Platform.isIOS || Platform.isMacOS) { + return ffi.DynamicLibrary.open('libtessellator.dylib'); + } else if (Platform.isAndroid || Platform.isLinux) { + return ffi.DynamicLibrary.open('libtessellator.so'); + } + throw UnsupportedError('Unknown platform: ${Platform.operatingSystem}'); +}(); + +class _Vertices extends ffi.Struct { + external ffi.Pointer points; + + @ffi.Uint32() + external int size; +} + +class _PathBuilder extends ffi.Opaque {} + +typedef _CreatePathBuilderType = ffi.Pointer<_PathBuilder> Function(); +typedef _create_path_builder_type = ffi.Pointer<_PathBuilder> Function(); + +final _CreatePathBuilderType _createPathFn = + _dylib.lookupFunction<_create_path_builder_type, _CreatePathBuilderType>( + 'CreatePathBuilder'); + +typedef _MoveToType = void Function(ffi.Pointer<_PathBuilder>, double, double); +typedef _move_to_type = ffi.Void Function( + ffi.Pointer<_PathBuilder>, ffi.Float, ffi.Float); + +final _MoveToType _moveToFn = + _dylib.lookupFunction<_move_to_type, _MoveToType>('MoveTo'); + +typedef _LineToType = void Function(ffi.Pointer<_PathBuilder>, double, double); +typedef _line_to_type = ffi.Void Function( + ffi.Pointer<_PathBuilder>, ffi.Float, ffi.Float); + +final _LineToType _lineToFn = + _dylib.lookupFunction<_line_to_type, _LineToType>('LineTo'); + +typedef _CubicToType = void Function( + ffi.Pointer<_PathBuilder>, double, double, double, double, double, double); +typedef _cubic_to_type = ffi.Void Function(ffi.Pointer<_PathBuilder>, ffi.Float, + ffi.Float, ffi.Float, ffi.Float, ffi.Float, ffi.Float); + +final _CubicToType _cubicToFn = + _dylib.lookupFunction<_cubic_to_type, _CubicToType>('CubicTo'); + +typedef _CloseType = void Function(ffi.Pointer<_PathBuilder>, bool); +typedef _close_type = ffi.Void Function(ffi.Pointer<_PathBuilder>, ffi.Bool); + +final _CloseType closeFn = + _dylib.lookupFunction<_close_type, _CloseType>('Close'); + +typedef _TessellateType = ffi.Pointer<_Vertices> Function( + ffi.Pointer<_PathBuilder>); +typedef _tessellate_type = ffi.Pointer<_Vertices> Function( + ffi.Pointer<_PathBuilder>); + +final _TessellateType _tessellateFn = + _dylib.lookupFunction<_tessellate_type, _TessellateType>('Tessellate'); + +typedef _DestroyType = void Function(ffi.Pointer<_PathBuilder>); +typedef _destroy_type = ffi.Void Function(ffi.Pointer<_PathBuilder>); + +final _DestroyType _destroyFn = + _dylib.lookupFunction<_destroy_type, _DestroyType>('DestroyPathBuilder'); + +typedef _DestroyVerticesType = void Function(ffi.Pointer<_Vertices>); +typedef _destroy_vertices_type = ffi.Void Function(ffi.Pointer<_Vertices>); + +final _DestroyVerticesType _destroyVerticesFn = + _dylib.lookupFunction<_destroy_vertices_type, _DestroyVerticesType>( + 'DestroyVertices'); diff --git a/impeller/tessellator/dart/pubspec.yaml b/impeller/tessellator/dart/pubspec.yaml new file mode 100644 index 0000000000000..4f09e8272da5c --- /dev/null +++ b/impeller/tessellator/dart/pubspec.yaml @@ -0,0 +1,11 @@ +# Copyright 2013 The Flutter Authors. All rights reserved. +# Use of this source code is governed by a BSD-style license that can be +# found in the LICENSE file. +name: tessellator +description: A Dart FFI wrapper for Impeller's tessellator. +version: 0.0.0 +publish_to: none +homepage: https://github.com/flutter/impeller/tree/main/tessellator/dart + +environment: + sdk: '>=2.12.0 <3.0.0' diff --git a/impeller/tessellator/tessellator.cc b/impeller/tessellator/tessellator.cc new file mode 100644 index 0000000000000..eb8bb3ec43a2c --- /dev/null +++ b/impeller/tessellator/tessellator.cc @@ -0,0 +1,121 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "impeller/tessellator/tessellator.h" + +#include "flutter/fml/logging.h" +#include "flutter/fml/trace_event.h" +#include "third_party/libtess2/Include/tesselator.h" + +namespace impeller { + +Tessellator::Tessellator(FillType type) : fill_type_(type) {} + +Tessellator::~Tessellator() = default; + +static int ToTessWindingRule(FillType fill_type) { + switch (fill_type) { + case FillType::kOdd: + return TESS_WINDING_ODD; + case FillType::kNonZero: + return TESS_WINDING_NONZERO; + case FillType::kPositive: + return TESS_WINDING_POSITIVE; + case FillType::kNegative: + return TESS_WINDING_NEGATIVE; + case FillType::kAbsGeqTwo: + return TESS_WINDING_ABS_GEQ_TWO; + } + return TESS_WINDING_ODD; +} + +static void DestroyTessellator(TESStesselator* tessellator) { + if (tessellator != nullptr) { + ::tessDeleteTess(tessellator); + } +} + +bool Tessellator::Tessellate(const Path::Polyline& polyline, + VertexCallback callback) const { + TRACE_EVENT0("impeller", "Tessellator::Tessellate"); + if (!callback) { + return false; + } + + using CTessellator = + std::unique_ptr; + + CTessellator tessellator( + ::tessNewTess(nullptr /* the default ::malloc based allocator */), + DestroyTessellator); + + if (!tessellator) { + return false; + } + + constexpr int kVertexSize = 2; + constexpr int kPolygonSize = 3; + + //---------------------------------------------------------------------------- + /// Feed contour information to the tessellator. + /// + static_assert(sizeof(Point) == 2 * sizeof(float)); + for (size_t contour_i = 0; contour_i < polyline.contours.size(); + contour_i++) { + size_t start_point_index, end_point_index; + std::tie(start_point_index, end_point_index) = + polyline.GetContourPointBounds(contour_i); + + ::tessAddContour(tessellator.get(), // the C tessellator + kVertexSize, // + polyline.points.data() + start_point_index, // + sizeof(Point), // + end_point_index - start_point_index // + ); + } + + //---------------------------------------------------------------------------- + /// Let's tessellate. + /// + auto result = ::tessTesselate(tessellator.get(), // tessellator + ToTessWindingRule(fill_type_), // winding + TESS_POLYGONS, // element type + kPolygonSize, // polygon size + kVertexSize, // vertex size + nullptr // normal (null is automatic) + ); + + if (result != 1) { + return false; + } + + // TODO(csg): This copy can be elided entirely for the current use case. + std::vector points; + std::vector indices; + + int vertexItemCount = tessGetVertexCount(tessellator.get()) * kVertexSize; + auto vertices = tessGetVertices(tessellator.get()); + for (int i = 0; i < vertexItemCount; i += 2) { + points.emplace_back(vertices[i], vertices[i + 1]); + } + + int elementItemCount = tessGetElementCount(tessellator.get()) * kPolygonSize; + auto elements = tessGetElements(tessellator.get()); + for (int i = 0; i < elementItemCount; i++) { + indices.emplace_back(elements[i]); + } + + for (auto index : indices) { + auto vtx = points[index]; + callback(vtx); + } + + return true; +} + +WindingOrder Tessellator::GetFrontFaceWinding() const { + return WindingOrder::kClockwise; +} + +} // namespace impeller diff --git a/impeller/renderer/tessellator.h b/impeller/tessellator/tessellator.h similarity index 95% rename from impeller/renderer/tessellator.h rename to impeller/tessellator/tessellator.h index db8a1f29100c0..d60e078315334 100644 --- a/impeller/renderer/tessellator.h +++ b/impeller/tessellator/tessellator.h @@ -10,10 +10,14 @@ #include "flutter/fml/macros.h" #include "impeller/geometry/path.h" #include "impeller/geometry/point.h" -#include "impeller/renderer/formats.h" namespace impeller { +enum class WindingOrder { + kClockwise, + kCounterClockwise, +}; + //------------------------------------------------------------------------------ /// @brief A utility that generates triangles of the specified fill type /// given a polyline. This happens on the CPU. From a85214d118b31dcd24804489f80cba656018740d Mon Sep 17 00:00:00 2001 From: Brandon DeRosier Date: Mon, 7 Mar 2022 11:01:05 -0800 Subject: [PATCH 327/433] Fix ScalarNearlyEqual test (#56) --- impeller/geometry/geometry_unittests.cc | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/impeller/geometry/geometry_unittests.cc b/impeller/geometry/geometry_unittests.cc index 6c4f2a867754c..8299c45eb4873 100644 --- a/impeller/geometry/geometry_unittests.cc +++ b/impeller/geometry/geometry_unittests.cc @@ -18,11 +18,12 @@ namespace impeller { namespace testing { TEST(GeometryTest, ScalarNearlyEqual) { - ASSERT_FALSE(ScalarNearlyEqual(0.002f, 0.001f)); + ASSERT_FALSE(ScalarNearlyEqual(0.0021f, 0.001f)); + ASSERT_TRUE(ScalarNearlyEqual(0.0019f, 0.001f)); ASSERT_TRUE(ScalarNearlyEqual(0.002f, 0.001f, 0.0011f)); ASSERT_FALSE(ScalarNearlyEqual(0.002f, 0.001f, 0.0009f)); - ASSERT_TRUE( - ScalarNearlyEqual(1.0f, 1.0f + std::numeric_limits::epsilon()*4)); + ASSERT_TRUE(ScalarNearlyEqual( + 1.0f, 1.0f + std::numeric_limits::epsilon() * 4)); } TEST(GeometryTest, RotationMatrix) { From 46f95c3712d9e0465fde806e6839a58274e75d32 Mon Sep 17 00:00:00 2001 From: Brandon DeRosier Date: Mon, 7 Mar 2022 12:53:05 -0800 Subject: [PATCH 328/433] Add color premultiply/unpremultiply (#57) --- impeller/geometry/color.h | 11 +++++++++++ impeller/geometry/geometry_unittests.cc | 24 +++++++++++++++++++++++- impeller/geometry/geometry_unittests.h | 10 ++++++++++ 3 files changed, 44 insertions(+), 1 deletion(-) diff --git a/impeller/geometry/color.h b/impeller/geometry/color.h index 8f5a83885e1b9..ad324e7fd55b3 100644 --- a/impeller/geometry/color.h +++ b/impeller/geometry/color.h @@ -49,6 +49,17 @@ struct Color { alpha == c.alpha; } + constexpr Color Premultiply() const { + return {red * alpha, green * alpha, blue * alpha, alpha}; + } + + constexpr Color Unpremultiply() const { + if (ScalarNearlyEqual(alpha, 0.0)) { + return Color::BlackTransparent(); + } + return {red / alpha, green / alpha, blue / alpha, alpha}; + } + static constexpr Color White() { return {1.0, 1.0, 1.0, 1.0}; } static constexpr Color Black() { return {0.0, 0.0, 0.0, 1.0}; } diff --git a/impeller/geometry/geometry_unittests.cc b/impeller/geometry/geometry_unittests.cc index 8299c45eb4873..3ec5d5ab99211 100644 --- a/impeller/geometry/geometry_unittests.cc +++ b/impeller/geometry/geometry_unittests.cc @@ -2,7 +2,6 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -#include #include "impeller/geometry/geometry_unittests.h" #include #include "flutter/testing/testing.h" @@ -568,6 +567,29 @@ TEST(GeometryTest, PointReflect) { } } +TEST(GeometryTest, ColorPremultiply) { + { + Color a(1.0, 0.5, 0.2, 0.5); + Color premultiplied = a.Premultiply(); + Color expected = Color(0.5, 0.25, 0.1, 0.5); + ASSERT_COLOR_NEAR(premultiplied, expected); + } + + { + Color a(0.5, 0.25, 0.1, 0.5); + Color unpremultiplied = a.Unpremultiply(); + Color expected = Color(1.0, 0.5, 0.2, 0.5); + ASSERT_COLOR_NEAR(unpremultiplied, expected); + } + + { + Color a(0.5, 0.25, 0.1, 0.0); + Color unpremultiplied = a.Unpremultiply(); + Color expected = Color(0.0, 0.0, 0.0, 0.0); + ASSERT_COLOR_NEAR(unpremultiplied, expected); + } +} + TEST(GeometryTest, CanConvertBetweenDegressAndRadians) { { auto deg = Degrees{90.0}; diff --git a/impeller/geometry/geometry_unittests.h b/impeller/geometry/geometry_unittests.h index 746df49df6075..61502f1b38dbe 100644 --- a/impeller/geometry/geometry_unittests.h +++ b/impeller/geometry/geometry_unittests.h @@ -58,6 +58,15 @@ inline ::testing::AssertionResult RectNear(impeller::Rect a, impeller::Rect b) { : ::testing::AssertionFailure() << "Rects are not equal."; } +inline ::testing::AssertionResult ColorNear(impeller::Color a, + impeller::Color b) { + auto equal = NumberNear(a.red, b.red) && NumberNear(a.green, b.green) && + NumberNear(a.blue, b.blue) && NumberNear(a.alpha, b.alpha); + + return equal ? ::testing::AssertionSuccess() + : ::testing::AssertionFailure() << "Colors are not equal."; +} + inline ::testing::AssertionResult PointNear(impeller::Point a, impeller::Point b) { auto equal = NumberNear(a.x, b.x) && NumberNear(a.y, b.y); @@ -76,5 +85,6 @@ inline ::testing::AssertionResult SizeNear(impeller::Size a, impeller::Size b) { #define ASSERT_MATRIX_NEAR(a, b) ASSERT_PRED2(&::MatrixNear, a, b) #define ASSERT_QUATERNION_NEAR(a, b) ASSERT_PRED2(&::QuaternionNear, a, b) #define ASSERT_RECT_NEAR(a, b) ASSERT_PRED2(&::RectNear, a, b) +#define ASSERT_COLOR_NEAR(a, b) ASSERT_PRED2(&::ColorNear, a, b) #define ASSERT_POINT_NEAR(a, b) ASSERT_PRED2(&::PointNear, a, b) #define ASSERT_SIZE_NEAR(a, b) ASSERT_PRED2(&::SizeNear, a, b) From d0b9f2182b1ef26c4da6a95bc9f248fe4ca4d0e3 Mon Sep 17 00:00:00 2001 From: Brandon DeRosier Date: Mon, 7 Mar 2022 16:35:20 -0800 Subject: [PATCH 329/433] Add pipeline blend modes & demo (#55) --- impeller/aiks/canvas.cc | 2 +- impeller/aiks/paint.cc | 4 +- impeller/entity/contents/content_context.h | 45 +++++++- impeller/entity/entity.h | 11 ++ impeller/entity/entity_unittests.cc | 116 ++++++++++++++++++++- 5 files changed, 170 insertions(+), 8 deletions(-) diff --git a/impeller/aiks/canvas.cc b/impeller/aiks/canvas.cc index 311655ffdfaa6..775d80a8eee19 100644 --- a/impeller/aiks/canvas.cc +++ b/impeller/aiks/canvas.cc @@ -266,7 +266,7 @@ void Canvas::DrawTextFrame(TextFrame text_frame, auto text_contents = std::make_shared(); text_contents->SetTextFrame(std::move(text_frame)); text_contents->SetGlyphAtlas(std::move(atlas)); - text_contents->SetColor(paint.color); + text_contents->SetColor(paint.color.Premultiply()); Entity entity; entity.SetTransformation(GetCurrentTransformation() * diff --git a/impeller/aiks/paint.cc b/impeller/aiks/paint.cc index 06de4e6399f9a..d79a67b71453a 100644 --- a/impeller/aiks/paint.cc +++ b/impeller/aiks/paint.cc @@ -16,12 +16,12 @@ std::shared_ptr Paint::CreateContentsForEntity() const { switch (style) { case Style::kFill: { auto solid_color = std::make_shared(); - solid_color->SetColor(color); + solid_color->SetColor(color.Premultiply()); return solid_color; } case Style::kStroke: { auto solid_stroke = std::make_shared(); - solid_stroke->SetColor(color); + solid_stroke->SetColor(color.Premultiply()); solid_stroke->SetStrokeSize(stroke_width); return solid_stroke; } diff --git a/impeller/entity/contents/content_context.h b/impeller/entity/contents/content_context.h index 7217bf3344f19..0a3db38541c8e 100644 --- a/impeller/entity/contents/content_context.h +++ b/impeller/entity/contents/content_context.h @@ -19,6 +19,8 @@ #include "flutter/impeller/entity/solid_stroke.vert.h" #include "flutter/impeller/entity/texture_fill.frag.h" #include "flutter/impeller/entity/texture_fill.vert.h" +#include "impeller/entity/entity.h" +#include "impeller/renderer/formats.h" namespace impeller { @@ -38,17 +40,19 @@ using ClipPipeline = PipelineT; struct ContentContextOptions { SampleCount sample_count = SampleCount::kCount1; + Entity::BlendMode blend_mode = Entity::BlendMode::kSource; struct Hash { constexpr std::size_t operator()(const ContentContextOptions& o) const { - return fml::HashCombine(o.sample_count); + return fml::HashCombine(o.sample_count, o.blend_mode); } }; struct Equal { constexpr bool operator()(const ContentContextOptions& lhs, const ContentContextOptions& rhs) const { - return lhs.sample_count == rhs.sample_count; + return lhs.sample_count == rhs.sample_count && + lhs.blend_mode == rhs.blend_mode; } }; }; @@ -120,6 +124,43 @@ class ContentContext { static void ApplyOptionsToDescriptor(PipelineDescriptor& desc, const ContentContextOptions& options) { desc.SetSampleCount(options.sample_count); + + ColorAttachmentDescriptor color0 = *desc.GetColorAttachmentDescriptor(0u); + color0.alpha_blend_op = BlendOperation::kAdd; + color0.color_blend_op = BlendOperation::kAdd; + switch (options.blend_mode) { + case Entity::BlendMode::kClear: + color0.dst_alpha_blend_factor = BlendFactor::kZero; + color0.dst_color_blend_factor = BlendFactor::kZero; + color0.src_alpha_blend_factor = BlendFactor::kZero; + color0.src_color_blend_factor = BlendFactor::kZero; + break; + case Entity::BlendMode::kSource: + color0.dst_alpha_blend_factor = BlendFactor::kZero; + color0.dst_color_blend_factor = BlendFactor::kZero; + color0.src_alpha_blend_factor = BlendFactor::kSourceAlpha; + color0.src_color_blend_factor = BlendFactor::kOne; + break; + case Entity::BlendMode::kDestination: + color0.dst_alpha_blend_factor = BlendFactor::kDestinationAlpha; + color0.dst_color_blend_factor = BlendFactor::kOne; + color0.src_alpha_blend_factor = BlendFactor::kZero; + color0.src_color_blend_factor = BlendFactor::kZero; + break; + case Entity::BlendMode::kSourceOver: + color0.dst_alpha_blend_factor = BlendFactor::kOneMinusSourceAlpha; + color0.dst_color_blend_factor = BlendFactor::kOneMinusSourceAlpha; + color0.src_alpha_blend_factor = BlendFactor::kSourceAlpha; + color0.src_color_blend_factor = BlendFactor::kOne; + break; + case Entity::BlendMode::kDestinationOver: + color0.dst_alpha_blend_factor = BlendFactor::kDestinationAlpha; + color0.dst_color_blend_factor = BlendFactor::kOne; + color0.src_alpha_blend_factor = BlendFactor::kOneMinusDestinationAlpha; + color0.src_color_blend_factor = BlendFactor::kOneMinusDestinationAlpha; + break; + } + desc.SetColorAttachmentDescriptor(0u, std::move(color0)); } template diff --git a/impeller/entity/entity.h b/impeller/entity/entity.h index 4cad573c49136..c43b549df6316 100644 --- a/impeller/entity/entity.h +++ b/impeller/entity/entity.h @@ -18,6 +18,17 @@ class RenderPass; class Entity { public: + /// All pipeline blend mode presets assume that both the source (fragment + /// output) and destination (first color attachment) have colors with + /// premultiplied alpha. + enum class BlendMode { + kClear, + kSource, + kDestination, + kSourceOver, + kDestinationOver, + }; + Entity(); ~Entity(); diff --git a/impeller/entity/entity_unittests.cc b/impeller/entity/entity_unittests.cc index f442fa6907ccc..149ded9942af5 100644 --- a/impeller/entity/entity_unittests.cc +++ b/impeller/entity/entity_unittests.cc @@ -10,6 +10,8 @@ #include "impeller/geometry/path_builder.h" #include "impeller/playground/playground.h" #include "impeller/playground/widgets.h" +#include "impeller/renderer/render_pass.h" +#include "impeller/renderer/vertex_buffer_builder.h" #include "third_party/imgui/imgui.h" namespace impeller { @@ -42,7 +44,7 @@ TEST_F(EntityTest, ThreeStrokesInOnePath) { Entity entity; entity.SetPath(path); auto contents = std::make_unique(); - contents->SetColor(Color::Red()); + contents->SetColor(Color::Red().Premultiply()); contents->SetStrokeSize(5.0); entity.SetContents(std::move(contents)); ASSERT_TRUE(OpenPlaygroundHere(entity)); @@ -72,7 +74,7 @@ TEST_F(EntityTest, TriangleInsideASquare) { Entity entity; entity.SetPath(path); auto contents = std::make_unique(); - contents->SetColor(Color::Red()); + contents->SetColor(Color::Red().Premultiply()); contents->SetStrokeSize(20.0); entity.SetContents(std::move(contents)); @@ -110,7 +112,7 @@ TEST_F(EntityTest, StrokeCapAndJoinTest) { auto create_contents = [width = width](SolidStrokeContents::Cap cap, SolidStrokeContents::Join join) { auto contents = std::make_unique(); - contents->SetColor(Color::Red()); + contents->SetColor(Color::Red().Premultiply()); contents->SetStrokeSize(width); contents->SetStrokeCap(cap); contents->SetStrokeJoin(join); @@ -491,5 +493,113 @@ TEST_F(EntityTest, SolidStrokeContentsSetMiter) { ASSERT_FLOAT_EQ(contents.GetStrokeMiter(), 8); } +TEST_F(EntityTest, BlendingModeOptions) { + std::vector blend_mode_names; + std::vector blend_mode_values; + { + // Force an exhausiveness check with a switch. When adding blend modes, + // update this switch with a new name/value to to make it selectable in the + // test GUI. + + const Entity::BlendMode b{}; + static_assert( + b == Entity::BlendMode::kClear); // Ensure the first item in + // the switch is the first + // item in the enum. + switch (b) { + case Entity::BlendMode::kClear: + blend_mode_names.push_back("Clear"); + blend_mode_values.push_back(Entity::BlendMode::kClear); + case Entity::BlendMode::kSource: + blend_mode_names.push_back("Source"); + blend_mode_values.push_back(Entity::BlendMode::kSource); + case Entity::BlendMode::kDestination: + blend_mode_names.push_back("Destination"); + blend_mode_values.push_back(Entity::BlendMode::kDestination); + case Entity::BlendMode::kSourceOver: + blend_mode_names.push_back("SourceOver"); + blend_mode_values.push_back(Entity::BlendMode::kSourceOver); + case Entity::BlendMode::kDestinationOver: + blend_mode_names.push_back("DestinationOver"); + blend_mode_values.push_back( + Entity::BlendMode::kDestinationOver); + }; + } + + bool first_frame = true; + auto callback = [&](ContentContext& context, RenderPass& pass) { + if (first_frame) { + first_frame = false; + ImGui::SetNextWindowSize({350, 200}); + ImGui::SetNextWindowPos({200, 450}); + } + + auto draw_rect = [&context, &pass]( + Rect rect, Color color, + Entity::BlendMode blend_mode) -> bool { + using VS = SolidFillPipeline::VertexShader; + VertexBufferBuilder vtx_builder; + { + auto r = rect.GetLTRB(); + vtx_builder.AddVertices({ + {Point(r[0], r[1])}, + {Point(r[2], r[1])}, + {Point(r[2], r[3])}, + {Point(r[0], r[1])}, + {Point(r[2], r[3])}, + {Point(r[0], r[3])}, + }); + } + + Command cmd; + cmd.label = "Blended Rectangle"; + auto options = OptionsFromPass(pass); + options.blend_mode = blend_mode; + cmd.pipeline = context.GetSolidFillPipeline(options); + cmd.BindVertices( + vtx_builder.CreateVertexBuffer(pass.GetTransientsBuffer())); + + VS::FrameInfo frame_info; + frame_info.mvp = Matrix::MakeOrthographic(pass.GetRenderTargetSize()); + frame_info.color = color.Premultiply(); + VS::BindFrameInfo(cmd, + pass.GetTransientsBuffer().EmplaceUniform(frame_info)); + + cmd.primitive_type = PrimitiveType::kTriangle; + + return pass.AddCommand(std::move(cmd)); + }; + + ImGui::Begin("Controls"); + static Color color1(1, 0, 0, 0.5), color2(0, 1, 0, 0.5); + ImGui::ColorEdit4("Color 1", reinterpret_cast(&color1)); + ImGui::ColorEdit4("Color 2", reinterpret_cast(&color2)); + static int current_blend_index = 3; + ImGui::ListBox("Blending mode", ¤t_blend_index, + blend_mode_names.data(), blend_mode_names.size()); + ImGui::End(); + + Entity::BlendMode selected_mode = + blend_mode_values[current_blend_index]; + + Point a, b, c, d; + std::tie(a, b) = IMPELLER_PLAYGROUND_LINE( + Point(400, 100), Point(200, 300), 20, Color::White(), Color::White()); + std::tie(c, d) = IMPELLER_PLAYGROUND_LINE( + Point(470, 190), Point(270, 390), 20, Color::White(), Color::White()); + + bool result = true; + result = result && draw_rect(Rect(0, 0, pass.GetRenderTargetSize().width, + pass.GetRenderTargetSize().height), + Color(), Entity::BlendMode::kClear); + result = result && draw_rect(Rect::MakeLTRB(a.x, a.y, b.x, b.y), color1, + Entity::BlendMode::kSourceOver); + result = result && draw_rect(Rect::MakeLTRB(c.x, c.y, d.x, d.y), color2, + selected_mode); + return result; + }; + ASSERT_TRUE(OpenPlaygroundHere(callback)); +} + } // namespace testing } // namespace impeller From a7d5e62bfc75b6903c676b29c317c2d3c7397b80 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 7 Mar 2022 16:47:36 -0800 Subject: [PATCH 330/433] Bump github/codeql-action from 1.1.3 to 1.1.4 (#58) Bumps [github/codeql-action](https://github.com/github/codeql-action) from 1.1.3 to 1.1.4. - [Release notes](https://github.com/github/codeql-action/releases) - [Changelog](https://github.com/github/codeql-action/blob/main/CHANGELOG.md) - [Commits](https://github.com/github/codeql-action/compare/75f07e7ab2ee63cba88752d8c696324e4df67466...f5d822707ee6e8fb81b04a5c0040b736da22e587) --- updated-dependencies: - dependency-name: github/codeql-action dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- impeller/.github/workflows/scorecards-analysis.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/impeller/.github/workflows/scorecards-analysis.yml b/impeller/.github/workflows/scorecards-analysis.yml index 6c6812bf36441..b14f55ee8ff77 100644 --- a/impeller/.github/workflows/scorecards-analysis.yml +++ b/impeller/.github/workflows/scorecards-analysis.yml @@ -48,6 +48,6 @@ jobs: # Upload the results to GitHub's code scanning dashboard. - name: "Upload to code-scanning" - uses: github/codeql-action/upload-sarif@75f07e7ab2ee63cba88752d8c696324e4df67466 + uses: github/codeql-action/upload-sarif@f5d822707ee6e8fb81b04a5c0040b736da22e587 with: sarif_file: results.sarif From a360f63c38dedcdc6c27d037270191caca4f3b6c Mon Sep 17 00:00:00 2001 From: Dan Field Date: Mon, 7 Mar 2022 17:52:30 -0800 Subject: [PATCH 331/433] Remove FML dependency on geometry, tessellator (#59) * Remove FML dependency on geometry, tessellator * update readme --- impeller/README.md | 4 +++- impeller/aiks/BUILD.gn | 8 ++++++++ impeller/archivist/BUILD.gn | 9 ++++++++- impeller/base/BUILD.gn | 8 ++++++++ impeller/base/config.h | 2 -- impeller/entity/BUILD.gn | 9 +++++++++ impeller/geometry/matrix_decomposition.h | 1 - impeller/geometry/path.cc | 3 --- impeller/geometry/path_builder.h | 4 ++-- impeller/geometry/scalar.h | 2 -- impeller/image/BUILD.gn | 11 +++++++++-- impeller/renderer/BUILD.gn | 8 ++++++++ impeller/tessellator/BUILD.gn | 1 - impeller/tessellator/tessellator.cc | 3 --- impeller/tools/impeller.gni | 7 ------- impeller/typographer/BUILD.gn | 8 ++++++++ 16 files changed, 63 insertions(+), 25 deletions(-) diff --git a/impeller/README.md b/impeller/README.md index 6c2980751a146..6e0ebe684573a 100644 --- a/impeller/README.md +++ b/impeller/README.md @@ -38,7 +38,9 @@ Impeller itself may not depend on anything in `//flutter` except `//flutter/fml` and `flutter/display_list`. FML is a base library for C++ projects and Impeller implements the display list dispatcher interface to make it easy for Flutter to swap the renderer with Impeller. Impeller is meant to be used by the Flow -(`//flutter/flow`) subsystem. Hence the name. +(`//flutter/flow`) subsystem. Hence the name. The tessellator and geometry +libraries are exceptions - they unconditionally may not depend on anything from +`//flutter`. An overview of the major sub-frameworks, their responsibilities, and, relative states of completion: diff --git a/impeller/aiks/BUILD.gn b/impeller/aiks/BUILD.gn index 8f7df7aa66a46..db0e733258490 100644 --- a/impeller/aiks/BUILD.gn +++ b/impeller/aiks/BUILD.gn @@ -27,6 +27,14 @@ impeller_component("aiks") { "../entity", "../geometry", ] + + deps = [ + "//flutter/fml", + + # FML depends on the Dart VM for tracing and getting the current + # timepoint. + "//flutter/runtime:libdart", + ] } impeller_component("aiks_unittests") { diff --git a/impeller/archivist/BUILD.gn b/impeller/archivist/BUILD.gn index 3d3fce98e2264..ab25781748c45 100644 --- a/impeller/archivist/BUILD.gn +++ b/impeller/archivist/BUILD.gn @@ -31,7 +31,14 @@ impeller_component("archivist") { public_deps = [ "../base" ] - deps = [ "//third_party/sqlite" ] + deps = [ + "//flutter/fml", + + # FML depends on the Dart VM for tracing and getting the current + # timepoint. + "//flutter/runtime:libdart", + "//third_party/sqlite", + ] } impeller_component("archivist_unittests") { diff --git a/impeller/base/BUILD.gn b/impeller/base/BUILD.gn index 0a4ba5fcec5f4..489459bc5fc4d 100644 --- a/impeller/base/BUILD.gn +++ b/impeller/base/BUILD.gn @@ -18,6 +18,14 @@ impeller_component("base") { "validation.cc", "validation.h", ] + + deps = [ + "//flutter/fml", + + # FML depends on the Dart VM for tracing and getting the current + # timepoint. + "//flutter/runtime:libdart", + ] } impeller_component("base_unittests") { diff --git a/impeller/base/config.h b/impeller/base/config.h index c0077d983b892..a0750d5db17fe 100644 --- a/impeller/base/config.h +++ b/impeller/base/config.h @@ -4,8 +4,6 @@ #pragma once -#include "flutter/fml/build_config.h" - #if defined(__GNUC__) || defined(__clang__) #define IMPELLER_COMPILER_CLANG 1 #else // defined(__GNUC__) || defined(__clang__) diff --git a/impeller/entity/BUILD.gn b/impeller/entity/BUILD.gn index 0181edfd2c34f..e069fdde3cf0a 100644 --- a/impeller/entity/BUILD.gn +++ b/impeller/entity/BUILD.gn @@ -54,6 +54,15 @@ impeller_component("entity") { "../renderer", "../typographer", ] + + + deps = [ + "//flutter/fml", + + # FML depends on the Dart VM for tracing and getting the current + # timepoint. + "//flutter/runtime:libdart", + ] } impeller_component("entity_unittests") { diff --git a/impeller/geometry/matrix_decomposition.h b/impeller/geometry/matrix_decomposition.h index def9029ea7369..31c01ec5c6266 100644 --- a/impeller/geometry/matrix_decomposition.h +++ b/impeller/geometry/matrix_decomposition.h @@ -4,7 +4,6 @@ #pragma once -#include "flutter/fml/macros.h" #include "impeller/geometry/quaternion.h" #include "impeller/geometry/scalar.h" #include "impeller/geometry/shear.h" diff --git a/impeller/geometry/path.cc b/impeller/geometry/path.cc index 3a31ac61b96d7..981969461709d 100644 --- a/impeller/geometry/path.cc +++ b/impeller/geometry/path.cc @@ -6,7 +6,6 @@ #include -#include "flutter/fml/logging.h" #include "impeller/geometry/path_component.h" namespace impeller { @@ -19,8 +18,6 @@ Path::~Path() = default; std::tuple Path::Polyline::GetContourPointBounds( size_t contour_index) const { - FML_DCHECK(contour_index < contours.size()); - const size_t start_index = contours[contour_index].start_index; const size_t end_index = (contour_index >= contours.size() - 1) ? points.size() diff --git a/impeller/geometry/path_builder.h b/impeller/geometry/path_builder.h index 3c495e4f872a6..038a9f49f3219 100644 --- a/impeller/geometry/path_builder.h +++ b/impeller/geometry/path_builder.h @@ -4,7 +4,6 @@ #pragma once -#include "flutter/fml/macros.h" #include "impeller/geometry/path.h" #include "impeller/geometry/rect.h" #include "impeller/geometry/scalar.h" @@ -109,7 +108,8 @@ class PathBuilder { Point ReflectedCubicControlPoint1() const; - FML_DISALLOW_COPY_AND_ASSIGN(PathBuilder); + PathBuilder(const PathBuilder&) = delete; + PathBuilder& operator=(const PathBuilder&&) = delete; }; } // namespace impeller diff --git a/impeller/geometry/scalar.h b/impeller/geometry/scalar.h index 918e1491cd12e..9350f825114f6 100644 --- a/impeller/geometry/scalar.h +++ b/impeller/geometry/scalar.h @@ -7,7 +7,6 @@ #include #include -#include "flutter/fml/logging.h" #include "impeller/geometry/constants.h" namespace impeller { @@ -17,7 +16,6 @@ using Scalar = float; constexpr inline bool ScalarNearlyEqual(Scalar x, Scalar y, Scalar tolerance = 1e-3) { - FML_DCHECK(tolerance >= 0); return std::abs(x - y) <= tolerance; } diff --git a/impeller/image/BUILD.gn b/impeller/image/BUILD.gn index 0d61d2deeb7ea..3d2b787c2740f 100644 --- a/impeller/image/BUILD.gn +++ b/impeller/image/BUILD.gn @@ -17,12 +17,19 @@ impeller_component("image") { "decompressed_image.cc", ] - deps = [ "//third_party/skia" ] - public_deps = [ "../base", "../geometry", ] + + deps = [ + "//flutter/fml", + + # FML depends on the Dart VM for tracing and getting the current + # timepoint. + "//flutter/runtime:libdart", + "//third_party/skia", + ] } impeller_component("image_unittests") { diff --git a/impeller/renderer/BUILD.gn b/impeller/renderer/BUILD.gn index 540e1e45eb3f2..0520d394d387b 100644 --- a/impeller/renderer/BUILD.gn +++ b/impeller/renderer/BUILD.gn @@ -110,6 +110,14 @@ impeller_component("renderer") { "../tessellator", ] + deps = [ + "//flutter/fml", + + # FML depends on the Dart VM for tracing and getting the current + # timepoint. + "//flutter/runtime:libdart", + ] + frameworks = [ "Metal.framework" ] } diff --git a/impeller/tessellator/BUILD.gn b/impeller/tessellator/BUILD.gn index cde82096ffba8..4dba135b99bf1 100644 --- a/impeller/tessellator/BUILD.gn +++ b/impeller/tessellator/BUILD.gn @@ -27,7 +27,6 @@ shared_library("tessellator_shared") { deps = [ "../geometry", - "//flutter/fml", "//third_party/libtess2", ] } diff --git a/impeller/tessellator/tessellator.cc b/impeller/tessellator/tessellator.cc index eb8bb3ec43a2c..d63ac18461bb3 100644 --- a/impeller/tessellator/tessellator.cc +++ b/impeller/tessellator/tessellator.cc @@ -4,8 +4,6 @@ #include "impeller/tessellator/tessellator.h" -#include "flutter/fml/logging.h" -#include "flutter/fml/trace_event.h" #include "third_party/libtess2/Include/tesselator.h" namespace impeller { @@ -38,7 +36,6 @@ static void DestroyTessellator(TESStesselator* tessellator) { bool Tessellator::Tessellate(const Path::Polyline& polyline, VertexCallback callback) const { - TRACE_EVENT0("impeller", "Tessellator::Tessellate"); if (!callback) { return false; } diff --git a/impeller/tools/impeller.gni b/impeller/tools/impeller.gni index 2af17e3490405..3594914db1c2b 100644 --- a/impeller/tools/impeller.gni +++ b/impeller/tools/impeller.gni @@ -36,13 +36,6 @@ template("impeller_component") { cflags_objcc += flutter_cflags_objcc_arc + objc_warning_flags public_configs += [ "//flutter/impeller:impeller_public_config" ] - - deps += [ - "//flutter/fml", - - # For tracing, seems to be pulled in by FML. Must be removed. - "//flutter/runtime:libdart", - ] } } diff --git a/impeller/typographer/BUILD.gn b/impeller/typographer/BUILD.gn index 3e06926491f76..10d95c452589b 100644 --- a/impeller/typographer/BUILD.gn +++ b/impeller/typographer/BUILD.gn @@ -36,6 +36,14 @@ impeller_component("typographer") { "../renderer", "//third_party/skia", ] + + deps = [ + "//flutter/fml", + + # FML depends on the Dart VM for tracing and getting the current + # timepoint. + "//flutter/runtime:libdart", + ] } impeller_component("typographer_unittests") { From 5d99bb98a106132c6ccaa4673b8baf788e9d0221 Mon Sep 17 00:00:00 2001 From: Dan Field Date: Mon, 7 Mar 2022 21:37:45 -0800 Subject: [PATCH 332/433] Make Path::Polyline::GetContourPointBounds safe for OOB (#60) --- impeller/geometry/geometry_unittests.cc | 7 ++ impeller/geometry/path.cc | 7 +- impeller/geometry/path.h | 2 + impeller/renderer/tessellator.cc | 121 ------------------------ 4 files changed, 14 insertions(+), 123 deletions(-) delete mode 100644 impeller/renderer/tessellator.cc diff --git a/impeller/geometry/geometry_unittests.cc b/impeller/geometry/geometry_unittests.cc index 3ec5d5ab99211..ef431240f6954 100644 --- a/impeller/geometry/geometry_unittests.cc +++ b/impeller/geometry/geometry_unittests.cc @@ -853,5 +853,12 @@ TEST(GeometryTest, PolylineGetContourPointBoundsReturnsCorrectRanges) { ASSERT_EQ(b2, 6u); } +TEST(GeometryTest, PolylineGetContourOutOfBoundsAborts) { + Path::Polyline polyline = + PathBuilder{}.AddLine({100, 100}, {200, 100}).TakePath().CreatePolyline(); + ASSERT_EQ(polyline.GetContourPointBounds(0), std::make_tuple(2u, 2u)); + ASSERT_EQ(polyline.GetContourPointBounds(14), std::make_tuple(2u, 2u)); +} + } // namespace testing } // namespace impeller diff --git a/impeller/geometry/path.cc b/impeller/geometry/path.cc index 981969461709d..d74a15fe4b882 100644 --- a/impeller/geometry/path.cc +++ b/impeller/geometry/path.cc @@ -18,10 +18,13 @@ Path::~Path() = default; std::tuple Path::Polyline::GetContourPointBounds( size_t contour_index) const { - const size_t start_index = contours[contour_index].start_index; + if (contour_index >= contours.size()) { + return {points.size(), points.size()}; + } + const size_t start_index = contours.at(contour_index).start_index; const size_t end_index = (contour_index >= contours.size() - 1) ? points.size() - : contours[contour_index + 1].start_index; + : contours.at(contour_index + 1).start_index; return std::make_tuple(start_index, end_index); } diff --git a/impeller/geometry/path.h b/impeller/geometry/path.h index 061664742f4a6..7add50e1783c3 100644 --- a/impeller/geometry/path.h +++ b/impeller/geometry/path.h @@ -61,6 +61,8 @@ class Path { /// Convenience method to compute the start (inclusive) and end (exclusive) /// point of the given contour index. + /// + /// The contour_index parameter is clamped to contours.size(). std::tuple GetContourPointBounds( size_t contour_index) const; }; diff --git a/impeller/renderer/tessellator.cc b/impeller/renderer/tessellator.cc deleted file mode 100644 index eb8bb3ec43a2c..0000000000000 --- a/impeller/renderer/tessellator.cc +++ /dev/null @@ -1,121 +0,0 @@ -// Copyright 2013 The Flutter Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#include "impeller/tessellator/tessellator.h" - -#include "flutter/fml/logging.h" -#include "flutter/fml/trace_event.h" -#include "third_party/libtess2/Include/tesselator.h" - -namespace impeller { - -Tessellator::Tessellator(FillType type) : fill_type_(type) {} - -Tessellator::~Tessellator() = default; - -static int ToTessWindingRule(FillType fill_type) { - switch (fill_type) { - case FillType::kOdd: - return TESS_WINDING_ODD; - case FillType::kNonZero: - return TESS_WINDING_NONZERO; - case FillType::kPositive: - return TESS_WINDING_POSITIVE; - case FillType::kNegative: - return TESS_WINDING_NEGATIVE; - case FillType::kAbsGeqTwo: - return TESS_WINDING_ABS_GEQ_TWO; - } - return TESS_WINDING_ODD; -} - -static void DestroyTessellator(TESStesselator* tessellator) { - if (tessellator != nullptr) { - ::tessDeleteTess(tessellator); - } -} - -bool Tessellator::Tessellate(const Path::Polyline& polyline, - VertexCallback callback) const { - TRACE_EVENT0("impeller", "Tessellator::Tessellate"); - if (!callback) { - return false; - } - - using CTessellator = - std::unique_ptr; - - CTessellator tessellator( - ::tessNewTess(nullptr /* the default ::malloc based allocator */), - DestroyTessellator); - - if (!tessellator) { - return false; - } - - constexpr int kVertexSize = 2; - constexpr int kPolygonSize = 3; - - //---------------------------------------------------------------------------- - /// Feed contour information to the tessellator. - /// - static_assert(sizeof(Point) == 2 * sizeof(float)); - for (size_t contour_i = 0; contour_i < polyline.contours.size(); - contour_i++) { - size_t start_point_index, end_point_index; - std::tie(start_point_index, end_point_index) = - polyline.GetContourPointBounds(contour_i); - - ::tessAddContour(tessellator.get(), // the C tessellator - kVertexSize, // - polyline.points.data() + start_point_index, // - sizeof(Point), // - end_point_index - start_point_index // - ); - } - - //---------------------------------------------------------------------------- - /// Let's tessellate. - /// - auto result = ::tessTesselate(tessellator.get(), // tessellator - ToTessWindingRule(fill_type_), // winding - TESS_POLYGONS, // element type - kPolygonSize, // polygon size - kVertexSize, // vertex size - nullptr // normal (null is automatic) - ); - - if (result != 1) { - return false; - } - - // TODO(csg): This copy can be elided entirely for the current use case. - std::vector points; - std::vector indices; - - int vertexItemCount = tessGetVertexCount(tessellator.get()) * kVertexSize; - auto vertices = tessGetVertices(tessellator.get()); - for (int i = 0; i < vertexItemCount; i += 2) { - points.emplace_back(vertices[i], vertices[i + 1]); - } - - int elementItemCount = tessGetElementCount(tessellator.get()) * kPolygonSize; - auto elements = tessGetElements(tessellator.get()); - for (int i = 0; i < elementItemCount; i++) { - indices.emplace_back(elements[i]); - } - - for (auto index : indices) { - auto vtx = points[index]; - callback(vtx); - } - - return true; -} - -WindingOrder Tessellator::GetFrontFaceWinding() const { - return WindingOrder::kClockwise; -} - -} // namespace impeller From 40e6f0b8d29702c791f3b525f39967eb77679d57 Mon Sep 17 00:00:00 2001 From: Chinmay Garde Date: Tue, 8 Mar 2022 11:08:37 -0800 Subject: [PATCH 333/433] Add LICENSE file. (#61) Matches the one in the engine exactly as described in the [umbrella issue](https://github.com/flutter/flutter/issues/97686). --- impeller/LICENSE | 25 +++++++++++++++++++++++++ 1 file changed, 25 insertions(+) create mode 100644 impeller/LICENSE diff --git a/impeller/LICENSE b/impeller/LICENSE new file mode 100644 index 0000000000000..c6823b81eb845 --- /dev/null +++ b/impeller/LICENSE @@ -0,0 +1,25 @@ +Copyright 2013 The Flutter Authors. All rights reserved. + +Redistribution and use in source and binary forms, with or without modification, +are permitted provided that the following conditions are met: + + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above + copyright notice, this list of conditions and the following + disclaimer in the documentation and/or other materials provided + with the distribution. + * Neither the name of Google Inc. nor the names of its + contributors may be used to endorse or promote products derived + from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR +ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON +ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. From 63c925c25fab30f2d7fedff6ff1733d73f8aaf3c Mon Sep 17 00:00:00 2001 From: Chinmay Garde Date: Tue, 8 Mar 2022 11:13:38 -0800 Subject: [PATCH 334/433] Move impeller::Comparable to //impeller/base. (#62) Just some minor cleanup. Fixes https://github.com/flutter/flutter/issues/98686. --- impeller/base/BUILD.gn | 2 ++ impeller/{renderer => base}/comparable.cc | 2 +- impeller/{renderer => base}/comparable.h | 0 impeller/renderer/BUILD.gn | 2 -- impeller/renderer/backend/metal/sampler_library_mtl.h | 2 +- impeller/renderer/backend/metal/shader_library_mtl.h | 2 +- impeller/renderer/pipeline_descriptor.h | 2 +- impeller/renderer/sampler_descriptor.h | 2 +- impeller/renderer/shader_function.h | 2 +- impeller/renderer/vertex_descriptor.h | 2 +- impeller/typographer/font.h | 2 +- impeller/typographer/typeface.h | 2 +- 12 files changed, 11 insertions(+), 11 deletions(-) rename impeller/{renderer => base}/comparable.cc (87%) rename impeller/{renderer => base}/comparable.h (100%) diff --git a/impeller/base/BUILD.gn b/impeller/base/BUILD.gn index 489459bc5fc4d..19fc548aeff90 100644 --- a/impeller/base/BUILD.gn +++ b/impeller/base/BUILD.gn @@ -10,6 +10,8 @@ impeller_component("base") { "allocation.h", "backend_cast.h", "base.h", + "comparable.cc", + "comparable.h", "config.h", "promise.cc", "promise.h", diff --git a/impeller/renderer/comparable.cc b/impeller/base/comparable.cc similarity index 87% rename from impeller/renderer/comparable.cc rename to impeller/base/comparable.cc index aac17598ac657..96463d6e719e1 100644 --- a/impeller/renderer/comparable.cc +++ b/impeller/base/comparable.cc @@ -2,7 +2,7 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -#include "impeller/renderer/comparable.h" +#include "impeller/base/comparable.h" namespace impeller { diff --git a/impeller/renderer/comparable.h b/impeller/base/comparable.h similarity index 100% rename from impeller/renderer/comparable.h rename to impeller/base/comparable.h diff --git a/impeller/renderer/BUILD.gn b/impeller/renderer/BUILD.gn index 0520d394d387b..e719ebd89e3dd 100644 --- a/impeller/renderer/BUILD.gn +++ b/impeller/renderer/BUILD.gn @@ -49,8 +49,6 @@ impeller_component("renderer") { "command.cc", "command_buffer.h", "command_buffer.cc", - "comparable.cc", - "comparable.h", "context.h", "context.cc", "device_buffer.h", diff --git a/impeller/renderer/backend/metal/sampler_library_mtl.h b/impeller/renderer/backend/metal/sampler_library_mtl.h index c8195320e8347..e4c3f99aaa8e3 100644 --- a/impeller/renderer/backend/metal/sampler_library_mtl.h +++ b/impeller/renderer/backend/metal/sampler_library_mtl.h @@ -11,7 +11,7 @@ #include "flutter/fml/macros.h" #include "impeller/base/backend_cast.h" -#include "impeller/renderer/comparable.h" +#include "impeller/base/comparable.h" #include "impeller/renderer/sampler_descriptor.h" #include "impeller/renderer/sampler_library.h" diff --git a/impeller/renderer/backend/metal/shader_library_mtl.h b/impeller/renderer/backend/metal/shader_library_mtl.h index b09b4cda33a1b..ec9abbedebcf0 100644 --- a/impeller/renderer/backend/metal/shader_library_mtl.h +++ b/impeller/renderer/backend/metal/shader_library_mtl.h @@ -12,7 +12,7 @@ #include #include "flutter/fml/macros.h" -#include "impeller/renderer/comparable.h" +#include "impeller/base/comparable.h" #include "impeller/renderer/shader_library.h" namespace impeller { diff --git a/impeller/renderer/pipeline_descriptor.h b/impeller/renderer/pipeline_descriptor.h index c26579b020a6f..cfe3d33a679dd 100644 --- a/impeller/renderer/pipeline_descriptor.h +++ b/impeller/renderer/pipeline_descriptor.h @@ -13,7 +13,7 @@ #include "flutter/fml/hash_combine.h" #include "flutter/fml/macros.h" -#include "impeller/renderer/comparable.h" +#include "impeller/base/comparable.h" #include "impeller/renderer/formats.h" #include "impeller/renderer/shader_types.h" diff --git a/impeller/renderer/sampler_descriptor.h b/impeller/renderer/sampler_descriptor.h index bb2fffc61f120..1cdbd3e6f3a16 100644 --- a/impeller/renderer/sampler_descriptor.h +++ b/impeller/renderer/sampler_descriptor.h @@ -5,7 +5,7 @@ #pragma once #include "flutter/fml/macros.h" -#include "impeller/renderer/comparable.h" +#include "impeller/base/comparable.h" #include "impeller/renderer/formats.h" namespace impeller { diff --git a/impeller/renderer/shader_function.h b/impeller/renderer/shader_function.h index 682a67e21ee23..273b280f068b2 100644 --- a/impeller/renderer/shader_function.h +++ b/impeller/renderer/shader_function.h @@ -6,7 +6,7 @@ #include "flutter/fml/hash_combine.h" #include "flutter/fml/macros.h" -#include "impeller/renderer/comparable.h" +#include "impeller/base/comparable.h" #include "impeller/renderer/shader_types.h" namespace impeller { diff --git a/impeller/renderer/vertex_descriptor.h b/impeller/renderer/vertex_descriptor.h index f0018c4566b97..4bef9b86d3644 100644 --- a/impeller/renderer/vertex_descriptor.h +++ b/impeller/renderer/vertex_descriptor.h @@ -7,7 +7,7 @@ #include #include "flutter/fml/macros.h" -#include "impeller/renderer/comparable.h" +#include "impeller/base/comparable.h" #include "impeller/renderer/shader_types.h" namespace impeller { diff --git a/impeller/typographer/font.h b/impeller/typographer/font.h index 691b5d59483f0..e23d8908fa444 100644 --- a/impeller/typographer/font.h +++ b/impeller/typographer/font.h @@ -8,7 +8,7 @@ #include #include "flutter/fml/macros.h" -#include "impeller/renderer/comparable.h" +#include "impeller/base/comparable.h" #include "impeller/typographer/glyph.h" #include "impeller/typographer/typeface.h" diff --git a/impeller/typographer/typeface.h b/impeller/typographer/typeface.h index 5c2a8574ee84a..f3a4fe265af7f 100644 --- a/impeller/typographer/typeface.h +++ b/impeller/typographer/typeface.h @@ -5,8 +5,8 @@ #pragma once #include "flutter/fml/macros.h" +#include "impeller/base/comparable.h" #include "impeller/geometry/rect.h" -#include "impeller/renderer/comparable.h" namespace impeller { From aac4baa676b09cdbf7125ab4b2813830ef1c24cf Mon Sep 17 00:00:00 2001 From: Chinmay Garde Date: Tue, 8 Mar 2022 13:03:03 -0800 Subject: [PATCH 335/433] Add a script that checks that all source files have a valid license block. (#63) Also fixes the files with missing licenses. This check is somewhat easy with Impeller than in the engine because all source files must have the same license block. Resolves an action item in the umbrella issue https://github.com/flutter/flutter/issues/97686. --- impeller/fixtures/sample.vert | 4 + impeller/geometry/BUILD.gn | 2 + impeller/geometry/constants.cc | 11 +++ impeller/playground/imgui/BUILD.gn | 4 + .../playground/imgui/imgui_impl_impeller.cc | 4 + .../playground/imgui/imgui_impl_impeller.h | 6 ++ impeller/playground/imgui/imgui_raster.frag | 4 + impeller/playground/imgui/imgui_raster.vert | 4 + impeller/tools/check_licenses.py | 77 +++++++++++++++++++ 9 files changed, 116 insertions(+) create mode 100644 impeller/tools/check_licenses.py diff --git a/impeller/fixtures/sample.vert b/impeller/fixtures/sample.vert index ba455ccb8660d..2c1e03a481d7b 100644 --- a/impeller/fixtures/sample.vert +++ b/impeller/fixtures/sample.vert @@ -1,3 +1,7 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + #include "types.h" uniform UniformBufferObject { diff --git a/impeller/geometry/BUILD.gn b/impeller/geometry/BUILD.gn index becb1677f99b3..cc3bad32b8bac 100644 --- a/impeller/geometry/BUILD.gn +++ b/impeller/geometry/BUILD.gn @@ -8,6 +8,8 @@ impeller_component("geometry") { sources = [ "color.cc", "color.h", + "constants.cc", + "constants.h", "matrix.cc", "matrix.h", "matrix_decomposition.cc", diff --git a/impeller/geometry/constants.cc b/impeller/geometry/constants.cc index e69de29bb2d1d..115a24da4a3d1 100644 --- a/impeller/geometry/constants.cc +++ b/impeller/geometry/constants.cc @@ -0,0 +1,11 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "impeller/geometry/constants.h" + +namespace impeller { + +// + +} // namespace impeller diff --git a/impeller/playground/imgui/BUILD.gn b/impeller/playground/imgui/BUILD.gn index 9f1e0d3b89d0f..c56377f7301f6 100644 --- a/impeller/playground/imgui/BUILD.gn +++ b/impeller/playground/imgui/BUILD.gn @@ -1,3 +1,7 @@ +# Copyright 2013 The Flutter Authors. All rights reserved. +# Use of this source code is governed by a BSD-style license that can be +# found in the LICENSE file. + import("//flutter/impeller/tools/impeller.gni") impeller_shaders("imgui_shaders") { diff --git a/impeller/playground/imgui/imgui_impl_impeller.cc b/impeller/playground/imgui/imgui_impl_impeller.cc index 0d898b151a22b..245397fc0f450 100644 --- a/impeller/playground/imgui/imgui_impl_impeller.cc +++ b/impeller/playground/imgui/imgui_impl_impeller.cc @@ -1,3 +1,7 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + #include "imgui_impl_impeller.h" #include diff --git a/impeller/playground/imgui/imgui_impl_impeller.h b/impeller/playground/imgui/imgui_impl_impeller.h index ffdaaa072d5a2..585e8b2f2dfdc 100644 --- a/impeller/playground/imgui/imgui_impl_impeller.h +++ b/impeller/playground/imgui/imgui_impl_impeller.h @@ -1,3 +1,7 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + #pragma once #include @@ -5,8 +9,10 @@ #include "third_party/imgui/imgui.h" namespace impeller { + class Context; class RenderPass; + } // namespace impeller IMGUI_IMPL_API bool ImGui_ImplImpeller_Init( diff --git a/impeller/playground/imgui/imgui_raster.frag b/impeller/playground/imgui/imgui_raster.frag index 890a784245885..f159e20d69845 100644 --- a/impeller/playground/imgui/imgui_raster.frag +++ b/impeller/playground/imgui/imgui_raster.frag @@ -1,3 +1,7 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + in vec2 frag_texture_coordinates; in vec4 frag_vertex_color; diff --git a/impeller/playground/imgui/imgui_raster.vert b/impeller/playground/imgui/imgui_raster.vert index 391c14eb3d005..1afd790022518 100644 --- a/impeller/playground/imgui/imgui_raster.vert +++ b/impeller/playground/imgui/imgui_raster.vert @@ -1,3 +1,7 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + uniform UniformBuffer { mat4 mvp; } diff --git a/impeller/tools/check_licenses.py b/impeller/tools/check_licenses.py new file mode 100644 index 0000000000000..50dc921012277 --- /dev/null +++ b/impeller/tools/check_licenses.py @@ -0,0 +1,77 @@ +# Copyright 2013 The Flutter Authors. All rights reserved. +# Use of this source code is governed by a BSD-style license that can be +# found in the LICENSE file. + +import argparse +import os + + +def ContainsLicenseBlock(source_file): + # This check is somewhat easier than in the engine because all sources need to + # have the same license. + py_license = '''# Copyright 2013 The Flutter Authors. All rights reserved. +# Use of this source code is governed by a BSD-style license that can be +# found in the LICENSE file.''' + c_license = py_license.replace("#", "//") + + # Make sure we don't read the entire file into memory. + read_size = (max(len(py_license), len(c_license))) + + for license in [c_license, py_license]: + with open(source_file) as source: + if source.read(read_size).startswith(license): + return True + + return False + + +def IsSourceFile(path): + known_extensions = [ + ".cc", + ".cpp", + ".c", + ".h", + ".hpp", + ".py", + ".sh", + ".gn", + ".gni", + ".glsl", + ".sl.h", + ".vert", + ".frag", + ".tesc", + ".tese", + ".yaml", + ".dart", + ] + for extension in known_extensions: + if os.path.basename(path).endswith(extension): + return True + return False; + + +# Checks that all source files have the same license preamble. +def Main(): + parser = argparse.ArgumentParser() + parser.add_argument("--source-root", + type=str, required=True, + help="The source root.") + args = parser.parse_args() + + assert(os.path.exists(args.source_root)) + + source_files = set() + + for root, dirs, files in os.walk(os.path.abspath(args.source_root)): + for file in files: + file_path = os.path.join(root, file) + if IsSourceFile(file_path): + source_files.add(file_path) + + for source_file in source_files: + if not ContainsLicenseBlock(source_file): + raise Exception("Could not find valid license block in source ", source_file) + +if __name__ == '__main__': + Main() From 62ede220a12ad2a3fa94343cdb963b278a1d88b2 Mon Sep 17 00:00:00 2001 From: Brandon DeRosier Date: Tue, 8 Mar 2022 13:41:35 -0800 Subject: [PATCH 336/433] Correct polyline bounds test (#64) --- impeller/geometry/geometry_unittests.cc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/impeller/geometry/geometry_unittests.cc b/impeller/geometry/geometry_unittests.cc index ef431240f6954..843b6b59ff5c0 100644 --- a/impeller/geometry/geometry_unittests.cc +++ b/impeller/geometry/geometry_unittests.cc @@ -856,7 +856,7 @@ TEST(GeometryTest, PolylineGetContourPointBoundsReturnsCorrectRanges) { TEST(GeometryTest, PolylineGetContourOutOfBoundsAborts) { Path::Polyline polyline = PathBuilder{}.AddLine({100, 100}, {200, 100}).TakePath().CreatePolyline(); - ASSERT_EQ(polyline.GetContourPointBounds(0), std::make_tuple(2u, 2u)); + ASSERT_EQ(polyline.GetContourPointBounds(0), std::make_tuple(0u, 2u)); ASSERT_EQ(polyline.GetContourPointBounds(14), std::make_tuple(2u, 2u)); } From cb6949c41221d663bd78ca897cbb2500fbc16624 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 8 Mar 2022 20:58:36 -0800 Subject: [PATCH 337/433] Bump actions/upload-artifact from 2.3.1 to 3 (#54) Bumps [actions/upload-artifact](https://github.com/actions/upload-artifact) from 2.3.1 to 3. - [Release notes](https://github.com/actions/upload-artifact/releases) - [Commits](https://github.com/actions/upload-artifact/compare/82c141cc518b40d92cc801eee768e7aafc9c2fa2...6673cd052c4cd6fcf4b4e6e60ea986c889389535) --- updated-dependencies: - dependency-name: actions/upload-artifact dependency-type: direct:production update-type: version-update:semver-major ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- impeller/.github/workflows/scorecards-analysis.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/impeller/.github/workflows/scorecards-analysis.yml b/impeller/.github/workflows/scorecards-analysis.yml index b14f55ee8ff77..4b47f979faa69 100644 --- a/impeller/.github/workflows/scorecards-analysis.yml +++ b/impeller/.github/workflows/scorecards-analysis.yml @@ -40,7 +40,7 @@ jobs: # Upload the results as artifacts (optional). - name: "Upload artifact" - uses: actions/upload-artifact@82c141cc518b40d92cc801eee768e7aafc9c2fa2 + uses: actions/upload-artifact@6673cd052c4cd6fcf4b4e6e60ea986c889389535 with: name: SARIF file path: results.sarif From a02dd2ea79005f49f8a697cbbbac3ad51f4118d7 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 8 Mar 2022 20:59:04 -0800 Subject: [PATCH 338/433] Bump actions/checkout from 2.4.0 to 3 (#50) Bumps [actions/checkout](https://github.com/actions/checkout) from 2.4.0 to 3. - [Release notes](https://github.com/actions/checkout/releases) - [Changelog](https://github.com/actions/checkout/blob/main/CHANGELOG.md) - [Commits](https://github.com/actions/checkout/compare/ec3a7ce113134d7a93b817d10a8272cb61118579...a12a3943b4bdde767164f792f33f40b04645d846) --- updated-dependencies: - dependency-name: actions/checkout dependency-type: direct:production update-type: version-update:semver-major ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- impeller/.github/workflows/scorecards-analysis.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/impeller/.github/workflows/scorecards-analysis.yml b/impeller/.github/workflows/scorecards-analysis.yml index 4b47f979faa69..32c80cd247a21 100644 --- a/impeller/.github/workflows/scorecards-analysis.yml +++ b/impeller/.github/workflows/scorecards-analysis.yml @@ -20,7 +20,7 @@ jobs: steps: - name: "Checkout code" - uses: actions/checkout@ec3a7ce113134d7a93b817d10a8272cb61118579 + uses: actions/checkout@a12a3943b4bdde767164f792f33f40b04645d846 with: persist-credentials: false From 42bea84dceb5c2e5b9cd2651b1bb4c44569ae00c Mon Sep 17 00:00:00 2001 From: Dan Field Date: Wed, 9 Mar 2022 08:32:24 -0800 Subject: [PATCH 339/433] [tessellator]Expose and document smoothing approximation via the C/Dart API. Minor refactor of tessellator to supply fill type to method rather than to object. (#65) --- .../contents/linear_gradient_contents.cc | 13 +- .../entity/contents/solid_color_contents.cc | 4 +- impeller/entity/contents/text_contents.cc | 3 +- impeller/entity/contents/texture_contents.cc | 25 ++-- impeller/entity/entity_unittests.cc | 42 ++++-- impeller/geometry/path_component.cc | 2 +- impeller/geometry/path_component.h | 21 +++ impeller/tessellator/c/tessellator.cc | 21 +-- impeller/tessellator/c/tessellator.h | 10 +- .../tessellator/dart/lib/tessellator.dart | 124 +++++++++++++++--- impeller/tessellator/tessellator.cc | 19 ++- impeller/tessellator/tessellator.h | 14 +- 12 files changed, 220 insertions(+), 78 deletions(-) diff --git a/impeller/entity/contents/linear_gradient_contents.cc b/impeller/entity/contents/linear_gradient_contents.cc index 55b9416c86269..bed837a8aa6df 100644 --- a/impeller/entity/contents/linear_gradient_contents.cc +++ b/impeller/entity/contents/linear_gradient_contents.cc @@ -42,12 +42,13 @@ bool LinearGradientContents::Render(const ContentContext& renderer, auto vertices_builder = VertexBufferBuilder(); { - auto result = Tessellator{entity.GetPath().GetFillType()}.Tessellate( - entity.GetPath().CreatePolyline(), [&vertices_builder](Point point) { - VS::PerVertexData vtx; - vtx.vertices = point; - vertices_builder.AppendVertex(vtx); - }); + auto result = Tessellator{}.Tessellate(entity.GetPath().GetFillType(), + entity.GetPath().CreatePolyline(), + [&vertices_builder](Point point) { + VS::PerVertexData vtx; + vtx.vertices = point; + vertices_builder.AppendVertex(vtx); + }); if (!result) { return false; } diff --git a/impeller/entity/contents/solid_color_contents.cc b/impeller/entity/contents/solid_color_contents.cc index 71318f8cb6dc5..5d2d63dc19452 100644 --- a/impeller/entity/contents/solid_color_contents.cc +++ b/impeller/entity/contents/solid_color_contents.cc @@ -30,8 +30,8 @@ VertexBuffer SolidColorContents::CreateSolidFillVertices(const Path& path, VertexBufferBuilder vtx_builder; - auto tesselation_result = Tessellator{path.GetFillType()}.Tessellate( - path.CreatePolyline(), [&vtx_builder](auto point) { + auto tesselation_result = Tessellator{}.Tessellate( + path.GetFillType(), path.CreatePolyline(), [&vtx_builder](auto point) { VS::PerVertexData vtx; vtx.vertices = point; vtx_builder.AppendVertex(vtx); diff --git a/impeller/entity/contents/text_contents.cc b/impeller/entity/contents/text_contents.cc index 149f142e930ad..1abbd1f340166 100644 --- a/impeller/entity/contents/text_contents.cc +++ b/impeller/entity/contents/text_contents.cc @@ -78,7 +78,8 @@ bool TextContents::Render(const ContentContext& renderer, // atlas. { VertexBufferBuilder vertex_builder; - if (!Tessellator{FillType::kPositive}.Tessellate( + if (!Tessellator{}.Tessellate( + FillType::kPositive, PathBuilder{} .AddRect(Rect::MakeXYWH(0.0, 0.0, 1.0, 1.0)) .TakePath() diff --git a/impeller/entity/contents/texture_contents.cc b/impeller/entity/contents/texture_contents.cc index 7e57278afb019..79bea6651cb6b 100644 --- a/impeller/entity/contents/texture_contents.cc +++ b/impeller/entity/contents/texture_contents.cc @@ -61,19 +61,18 @@ bool TextureContents::Render(const ContentContext& renderer, VertexBufferBuilder vertex_builder; { - const auto tess_result = - Tessellator{entity.GetPath().GetFillType()}.Tessellate( - entity.GetPath().CreatePolyline(), - [this, &vertex_builder, &coverage_rect, &texture_size](Point vtx) { - VS::PerVertexData data; - data.vertices = vtx; - auto coverage_coords = - (vtx - coverage_rect->origin) / coverage_rect->size; - data.texture_coords = - (source_rect_.origin + source_rect_.size * coverage_coords) / - texture_size; - vertex_builder.AppendVertex(data); - }); + const auto tess_result = Tessellator{}.Tessellate( + entity.GetPath().GetFillType(), entity.GetPath().CreatePolyline(), + [this, &vertex_builder, &coverage_rect, &texture_size](Point vtx) { + VS::PerVertexData data; + data.vertices = vtx; + auto coverage_coords = + (vtx - coverage_rect->origin) / coverage_rect->size; + data.texture_coords = + (source_rect_.origin + source_rect_.size * coverage_coords) / + texture_size; + vertex_builder.AppendVertex(data); + }); if (!tess_result) { return false; } diff --git a/impeller/entity/entity_unittests.cc b/impeller/entity/entity_unittests.cc index 149ded9942af5..b0fca2aa83139 100644 --- a/impeller/entity/entity_unittests.cc +++ b/impeller/entity/entity_unittests.cc @@ -502,10 +502,9 @@ TEST_F(EntityTest, BlendingModeOptions) { // test GUI. const Entity::BlendMode b{}; - static_assert( - b == Entity::BlendMode::kClear); // Ensure the first item in - // the switch is the first - // item in the enum. + static_assert(b == Entity::BlendMode::kClear); // Ensure the first item in + // the switch is the first + // item in the enum. switch (b) { case Entity::BlendMode::kClear: blend_mode_names.push_back("Clear"); @@ -521,8 +520,7 @@ TEST_F(EntityTest, BlendingModeOptions) { blend_mode_values.push_back(Entity::BlendMode::kSourceOver); case Entity::BlendMode::kDestinationOver: blend_mode_names.push_back("DestinationOver"); - blend_mode_values.push_back( - Entity::BlendMode::kDestinationOver); + blend_mode_values.push_back(Entity::BlendMode::kDestinationOver); }; } @@ -534,9 +532,8 @@ TEST_F(EntityTest, BlendingModeOptions) { ImGui::SetNextWindowPos({200, 450}); } - auto draw_rect = [&context, &pass]( - Rect rect, Color color, - Entity::BlendMode blend_mode) -> bool { + auto draw_rect = [&context, &pass](Rect rect, Color color, + Entity::BlendMode blend_mode) -> bool { using VS = SolidFillPipeline::VertexShader; VertexBufferBuilder vtx_builder; { @@ -579,8 +576,7 @@ TEST_F(EntityTest, BlendingModeOptions) { blend_mode_names.data(), blend_mode_names.size()); ImGui::End(); - Entity::BlendMode selected_mode = - blend_mode_values[current_blend_index]; + Entity::BlendMode selected_mode = blend_mode_values[current_blend_index]; Point a, b, c, d; std::tie(a, b) = IMPELLER_PLAYGROUND_LINE( @@ -601,5 +597,29 @@ TEST_F(EntityTest, BlendingModeOptions) { ASSERT_TRUE(OpenPlaygroundHere(callback)); } +TEST_F(EntityTest, BezierCircleScaled) { + Entity entity; + auto path = PathBuilder{} + .MoveTo({97.325, 34.818}) + .CubicCurveTo({98.50862885295136, 34.81812293973836}, + {99.46822048142015, 33.85863261475589}, + {99.46822048142015, 32.67499810206613}) + .CubicCurveTo({99.46822048142015, 31.491363589376355}, + {98.50862885295136, 30.53187326439389}, + {97.32499434685802, 30.531998226542708}) + .CubicCurveTo({96.14153655073771, 30.532123170035373}, + {95.18222070648729, 31.491540299350355}, + {95.18222070648729, 32.67499810206613}) + .CubicCurveTo({95.18222070648729, 33.85845590478189}, + {96.14153655073771, 34.81787303409686}, + {97.32499434685802, 34.81799797758954}) + .Close() + .TakePath(); + entity.SetPath(path); + entity.SetTransformation(Matrix::MakeScale({20.0, 20.0, 1.0}).Translate({-80, -15, 0})); + entity.SetContents(SolidColorContents::Make(Color::Red())); + ASSERT_TRUE(OpenPlaygroundHere(entity)); +} + } // namespace testing } // namespace impeller diff --git a/impeller/geometry/path_component.cc b/impeller/geometry/path_component.cc index eb842e509e9c1..70f83c3f1a18c 100644 --- a/impeller/geometry/path_component.cc +++ b/impeller/geometry/path_component.cc @@ -112,7 +112,7 @@ Point CubicPathComponent::SolveDerivative(Scalar time) const { /* * Paul de Casteljau's subdivision with modifications as described in - * http://www.antigrain.com/research/adaptive_bezier/index.html. + * http://agg.sourceforge.net/antigrain.com/research/adaptive_bezier/index.html. * Refer to the diagram on that page for a description of the points. */ static void CubicPathSmoothenRecursive(const SmoothingApproximation& approx, diff --git a/impeller/geometry/path_component.h b/impeller/geometry/path_component.h index 60331ef899232..4fd513370cd2c 100644 --- a/impeller/geometry/path_component.h +++ b/impeller/geometry/path_component.h @@ -12,10 +12,31 @@ namespace impeller { +/// Information about how to approximate points on a curved path segment. +/// +/// In particular, the values in this object control how many vertices to +/// generate when approximating curves, and what tolerances to use when +/// calculating the sharpness of curves. struct SmoothingApproximation { + /// The scaling coefficient to use when translating to screen coordinates. + /// + /// Values approaching 0.0 will generate smoother looking curves with a + /// greater number of vertices, and will be more expensive to calculate. Scalar scale; + + /// The tolerance value in radians for calculating sharp angles. + /// + /// Values approaching 0.0 will provide more accurate approximation of sharp + /// turns. A 0.0 value means angle conditions are not considered at all. Scalar angle_tolerance; + + /// An angle in radians at which to introduce bevel cuts. + /// + /// Values greater than zero will restirct the sharpness of bevel cuts on + /// turns. Scalar cusp_limit; + + /// Used to more quickly detect colinear cases. Scalar distance_tolerance_square; SmoothingApproximation(/* default */) diff --git a/impeller/tessellator/c/tessellator.cc b/impeller/tessellator/c/tessellator.cc index 119835dd3fb80..e6c4661ebae7e 100644 --- a/impeller/tessellator/c/tessellator.cc +++ b/impeller/tessellator/c/tessellator.cc @@ -37,16 +37,21 @@ void Close(PathBuilder* builder) { builder->Close(); } -struct Vertices* Tessellate(PathBuilder* builder) { - auto path = builder->CopyPath(); - auto polyline = path.CreatePolyline(); +struct Vertices* Tessellate(PathBuilder* builder, + int fill_type, + Scalar scale, + Scalar angle_tolerance, + Scalar cusp_limit) { + auto path = builder->CopyPath(static_cast(fill_type)); + auto smoothing = SmoothingApproximation(scale, angle_tolerance, cusp_limit); + auto polyline = path.CreatePolyline(smoothing); std::vector points; - if (!Tessellator{path.GetFillType()}.Tessellate(polyline, - [&points](Point vertex) { - points.push_back(vertex.x); - points.push_back(vertex.y); - })) { + if (!Tessellator{}.Tessellate(path.GetFillType(), polyline, + [&points](Point vertex) { + points.push_back(vertex.x); + points.push_back(vertex.y); + })) { return nullptr; } diff --git a/impeller/tessellator/c/tessellator.h b/impeller/tessellator/c/tessellator.h index 1ca6efd88002f..23a24e1f9603f 100644 --- a/impeller/tessellator/c/tessellator.h +++ b/impeller/tessellator/c/tessellator.h @@ -7,7 +7,11 @@ #include "impeller/geometry/path_builder.h" #include "impeller/tessellator/tessellator.h" +#ifdef _WIN32 +#define IMPELLER_API __declspec(dllexport) +#else #define IMPELLER_API __attribute__((visibility("default"))) +#endif extern "C" { @@ -36,7 +40,11 @@ IMPELLER_API void CubicTo(PathBuilder* builder, IMPELLER_API void Close(PathBuilder* builder); -IMPELLER_API struct Vertices* Tessellate(PathBuilder* builder); +IMPELLER_API struct Vertices* Tessellate(PathBuilder* builder, + int fill_type, + Scalar scale, + Scalar angle_tolerance, + Scalar cusp_limit); IMPELLER_API void DestroyVertices(Vertices* vertices); diff --git a/impeller/tessellator/dart/lib/tessellator.dart b/impeller/tessellator/dart/lib/tessellator.dart index 2cc9358ebb85e..1e88d49253d1f 100644 --- a/impeller/tessellator/dart/lib/tessellator.dart +++ b/impeller/tessellator/dart/lib/tessellator.dart @@ -7,6 +7,53 @@ import 'dart:ffi' as ffi; import 'dart:io'; import 'dart:typed_data'; +/// Determines the winding rule that decides how the interior of a Path is +/// calculated. +/// +/// This enum is used by the [VerticesBuilder.tessellate] method. +// must match ordering in geometry/path.h +enum FillType { + /// The interior is defined by a non-zero sum of signed edge crossings. + nonZero, + + /// The interior is defined by an odd number of edge crossings. + evenOdd, +} + +/// Information about how to approximate points on a curved path segment. +/// +/// In particular, the values in this object control how many vertices to +/// generate when approximating curves, and what tolerances to use when +/// calculating the sharpness of curves. +/// +/// Used by [VerticesBuilder.tessellate]. +class SmoothingApproximation { + /// Creates a new smoothing approximation instance with default values. + const SmoothingApproximation({ + this.scale = 1.0, + this.angleTolerance = 0.0, + this.cuspLimit = 0.0, + }); + + /// The scaling coefficient to use when translating to screen coordinates. + /// + /// Values approaching 0.0 will generate smoother looking curves with a + /// greater number of vertices, and will be more expensive to calculate. + final double scale; + + /// The tolerance value in radians for calculating sharp angles. + /// + /// Values approaching 0.0 will provide more accurate approximation of sharp + /// turns. A 0.0 value means angle conditions are not considered at all. + final double angleTolerance; + + /// An angle in radians at which to introduce bevel cuts. + /// + /// Values greater than zero will restirct the sharpness of bevel cuts on + /// turns. + final double cuspLimit; +} + /// Creates vertices from path commands. /// /// First, build up the path contours with the [moveTo], [lineTo], [cubicTo], @@ -56,10 +103,19 @@ class VerticesBuilder { /// Tessellates the path created by the previous method calls into a list of /// vertices. - Float32List tessellate() { + Float32List tessellate({ + FillType fillType = FillType.nonZero, + SmoothingApproximation smoothing = const SmoothingApproximation(), + }) { assert(_vertices.isEmpty); assert(_builder != null); - final ffi.Pointer<_Vertices> vertices = _tessellateFn(_builder!); + final ffi.Pointer<_Vertices> vertices = _tessellateFn( + _builder!, + fillType.index, + smoothing.scale, + smoothing.angleTolerance, + smoothing.cuspLimit, + ); _vertices.add(vertices); return vertices.ref.points.asTypedList(vertices.ref.size); } @@ -103,26 +159,49 @@ typedef _create_path_builder_type = ffi.Pointer<_PathBuilder> Function(); final _CreatePathBuilderType _createPathFn = _dylib.lookupFunction<_create_path_builder_type, _CreatePathBuilderType>( - 'CreatePathBuilder'); + 'CreatePathBuilder', +); typedef _MoveToType = void Function(ffi.Pointer<_PathBuilder>, double, double); typedef _move_to_type = ffi.Void Function( - ffi.Pointer<_PathBuilder>, ffi.Float, ffi.Float); + ffi.Pointer<_PathBuilder>, + ffi.Float, + ffi.Float, +); -final _MoveToType _moveToFn = - _dylib.lookupFunction<_move_to_type, _MoveToType>('MoveTo'); +final _MoveToType _moveToFn = _dylib.lookupFunction<_move_to_type, _MoveToType>( + 'MoveTo', +); typedef _LineToType = void Function(ffi.Pointer<_PathBuilder>, double, double); typedef _line_to_type = ffi.Void Function( - ffi.Pointer<_PathBuilder>, ffi.Float, ffi.Float); + ffi.Pointer<_PathBuilder>, + ffi.Float, + ffi.Float, +); -final _LineToType _lineToFn = - _dylib.lookupFunction<_line_to_type, _LineToType>('LineTo'); +final _LineToType _lineToFn = _dylib.lookupFunction<_line_to_type, _LineToType>( + 'LineTo', +); typedef _CubicToType = void Function( - ffi.Pointer<_PathBuilder>, double, double, double, double, double, double); -typedef _cubic_to_type = ffi.Void Function(ffi.Pointer<_PathBuilder>, ffi.Float, - ffi.Float, ffi.Float, ffi.Float, ffi.Float, ffi.Float); + ffi.Pointer<_PathBuilder>, + double, + double, + double, + double, + double, + double, +); +typedef _cubic_to_type = ffi.Void Function( + ffi.Pointer<_PathBuilder>, + ffi.Float, + ffi.Float, + ffi.Float, + ffi.Float, + ffi.Float, + ffi.Float, +); final _CubicToType _cubicToFn = _dylib.lookupFunction<_cubic_to_type, _CubicToType>('CubicTo'); @@ -134,9 +213,19 @@ final _CloseType closeFn = _dylib.lookupFunction<_close_type, _CloseType>('Close'); typedef _TessellateType = ffi.Pointer<_Vertices> Function( - ffi.Pointer<_PathBuilder>); + ffi.Pointer<_PathBuilder>, + int, + double, + double, + double, +); typedef _tessellate_type = ffi.Pointer<_Vertices> Function( - ffi.Pointer<_PathBuilder>); + ffi.Pointer<_PathBuilder>, + ffi.Int, + ffi.Float, + ffi.Float, + ffi.Float, +); final _TessellateType _tessellateFn = _dylib.lookupFunction<_tessellate_type, _TessellateType>('Tessellate'); @@ -145,11 +234,14 @@ typedef _DestroyType = void Function(ffi.Pointer<_PathBuilder>); typedef _destroy_type = ffi.Void Function(ffi.Pointer<_PathBuilder>); final _DestroyType _destroyFn = - _dylib.lookupFunction<_destroy_type, _DestroyType>('DestroyPathBuilder'); + _dylib.lookupFunction<_destroy_type, _DestroyType>( + 'DestroyPathBuilder', +); typedef _DestroyVerticesType = void Function(ffi.Pointer<_Vertices>); typedef _destroy_vertices_type = ffi.Void Function(ffi.Pointer<_Vertices>); final _DestroyVerticesType _destroyVerticesFn = _dylib.lookupFunction<_destroy_vertices_type, _DestroyVerticesType>( - 'DestroyVertices'); + 'DestroyVertices', +); diff --git a/impeller/tessellator/tessellator.cc b/impeller/tessellator/tessellator.cc index d63ac18461bb3..542cefbbb2737 100644 --- a/impeller/tessellator/tessellator.cc +++ b/impeller/tessellator/tessellator.cc @@ -8,7 +8,7 @@ namespace impeller { -Tessellator::Tessellator(FillType type) : fill_type_(type) {} +Tessellator::Tessellator() = default; Tessellator::~Tessellator() = default; @@ -34,7 +34,8 @@ static void DestroyTessellator(TESStesselator* tessellator) { } } -bool Tessellator::Tessellate(const Path::Polyline& polyline, +bool Tessellator::Tessellate(FillType fill_type, + const Path::Polyline& polyline, VertexCallback callback) const { if (!callback) { return false; @@ -75,11 +76,11 @@ bool Tessellator::Tessellate(const Path::Polyline& polyline, //---------------------------------------------------------------------------- /// Let's tessellate. /// - auto result = ::tessTesselate(tessellator.get(), // tessellator - ToTessWindingRule(fill_type_), // winding - TESS_POLYGONS, // element type - kPolygonSize, // polygon size - kVertexSize, // vertex size + auto result = ::tessTesselate(tessellator.get(), // tessellator + ToTessWindingRule(fill_type), // winding + TESS_POLYGONS, // element type + kPolygonSize, // polygon size + kVertexSize, // vertex size nullptr // normal (null is automatic) ); @@ -111,8 +112,4 @@ bool Tessellator::Tessellate(const Path::Polyline& polyline, return true; } -WindingOrder Tessellator::GetFrontFaceWinding() const { - return WindingOrder::kClockwise; -} - } // namespace impeller diff --git a/impeller/tessellator/tessellator.h b/impeller/tessellator/tessellator.h index d60e078315334..d7c00132eee94 100644 --- a/impeller/tessellator/tessellator.h +++ b/impeller/tessellator/tessellator.h @@ -26,28 +26,26 @@ enum class WindingOrder { /// class Tessellator { public: - explicit Tessellator(FillType type); + Tessellator(); ~Tessellator(); - WindingOrder GetFrontFaceWinding() const; - using VertexCallback = std::function; //---------------------------------------------------------------------------- - /// @brief Generates triangles from the polyline. A callback is invoked - /// for each vertex of the triangle. + /// @brief Generates filled triangles from the polyline. A callback is + /// invoked for each vertex of the triangle. /// + /// @param[in] fill_type The fill rule to use when filling. /// @param[in] polyline The polyline /// @param[in] callback The callback /// /// @return If tessellation was successful. /// - bool Tessellate(const Path::Polyline& polyline, + bool Tessellate(FillType fill_type, + const Path::Polyline& polyline, VertexCallback callback) const; private: - const FillType fill_type_ = FillType::kNonZero; - FML_DISALLOW_COPY_AND_ASSIGN(Tessellator); }; From 0b8e95c528a744655091646772cb738326fe67fb Mon Sep 17 00:00:00 2001 From: Chinmay Garde Date: Wed, 9 Mar 2022 11:07:27 -0800 Subject: [PATCH 340/433] Fix top-level build group to not include the compiler and fixup headers. (#66) Targets in the engine had to individually depend on each sub-target in Impeller when all they cared about was all the client libs. The compiler is a build time implicit dependency. So that has been removed from the top-level group. Also fixed all the headers so that TUs within Impeller don't care about where Impeller itself sits in the source tree. --- impeller/BUILD.gn | 11 +---------- impeller/aiks/image.cc | 2 +- impeller/aiks/picture_recorder.cc | 4 ++-- impeller/base/validation.cc | 2 +- impeller/compiler/compiler.cc | 4 ++-- impeller/compiler/compiler.h | 4 ++-- impeller/compiler/compiler_unittests.cc | 2 +- impeller/compiler/impellerc_main.cc | 6 +++--- impeller/compiler/reflector.cc | 10 +++++----- impeller/compiler/switches.cc | 2 +- impeller/compiler/switches.h | 4 ++-- impeller/compiler/utilities.cc | 2 +- impeller/entity/contents/clip_contents.h | 2 +- impeller/entity/contents/content_context.h | 20 ++++++++++---------- impeller/playground/playground.mm | 4 ++-- impeller/renderer/renderer_unittests.cc | 10 +++++----- 16 files changed, 40 insertions(+), 49 deletions(-) diff --git a/impeller/BUILD.gn b/impeller/BUILD.gn index aa258109d90be..ff9e7be985f60 100644 --- a/impeller/BUILD.gn +++ b/impeller/BUILD.gn @@ -3,14 +3,9 @@ # found in the LICENSE file. config("impeller_public_config") { - include_dirs = [ - "..", - ".", - ] + include_dirs = [ ".." ] } -is_host = is_mac || is_linux || is_win - group("impeller") { public_deps = [ "aiks", @@ -24,10 +19,6 @@ group("impeller") { "tessellator", "typographer", ] - - if (is_host) { - public_deps += [ "compiler" ] - } } executable("impeller_unittests") { diff --git a/impeller/aiks/image.cc b/impeller/aiks/image.cc index 9e0c8fdf8fee6..342c4eee6a47d 100644 --- a/impeller/aiks/image.cc +++ b/impeller/aiks/image.cc @@ -2,7 +2,7 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -#include "flutter/impeller/aiks/image.h" +#include "impeller/aiks/image.h" namespace impeller { diff --git a/impeller/aiks/picture_recorder.cc b/impeller/aiks/picture_recorder.cc index c4040ca19e522..0d458468989db 100644 --- a/impeller/aiks/picture_recorder.cc +++ b/impeller/aiks/picture_recorder.cc @@ -2,9 +2,9 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -#include "flutter/impeller/aiks/picture_recorder.h" +#include "impeller/aiks/picture_recorder.h" -#include "flutter/impeller/aiks/canvas.h" +#include "impeller/aiks/canvas.h" namespace impeller { diff --git a/impeller/base/validation.cc b/impeller/base/validation.cc index d0b113aee1244..1394cbc421874 100644 --- a/impeller/base/validation.cc +++ b/impeller/base/validation.cc @@ -2,7 +2,7 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -#include "flutter/impeller/base/validation.h" +#include "impeller/base/validation.h" #include "flutter/fml/logging.h" diff --git a/impeller/compiler/compiler.cc b/impeller/compiler/compiler.cc index f427aeba7b9c3..b6ac4b614d0f9 100644 --- a/impeller/compiler/compiler.cc +++ b/impeller/compiler/compiler.cc @@ -2,14 +2,14 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -#include "flutter/impeller/compiler/compiler.h" +#include "impeller/compiler/compiler.h" #include #include #include #include "flutter/fml/paths.h" -#include "flutter/impeller/compiler/logger.h" +#include "impeller/compiler/logger.h" namespace impeller { namespace compiler { diff --git a/impeller/compiler/compiler.h b/impeller/compiler/compiler.h index 689ec08183b87..9310bdc9bcf93 100644 --- a/impeller/compiler/compiler.h +++ b/impeller/compiler/compiler.h @@ -10,8 +10,8 @@ #include "flutter/fml/macros.h" #include "flutter/fml/mapping.h" -#include "flutter/impeller/compiler/include_dir.h" -#include "flutter/impeller/compiler/reflector.h" +#include "impeller/compiler/include_dir.h" +#include "impeller/compiler/reflector.h" #include "shaderc/shaderc.hpp" #include "third_party/spirv_cross/spirv_msl.hpp" #include "third_party/spirv_cross/spirv_parser.hpp" diff --git a/impeller/compiler/compiler_unittests.cc b/impeller/compiler/compiler_unittests.cc index f681d34281d33..eca4561887772 100644 --- a/impeller/compiler/compiler_unittests.cc +++ b/impeller/compiler/compiler_unittests.cc @@ -2,10 +2,10 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -#include "flutter/impeller/compiler/compiler.h" #include "flutter/testing/testing.h" #include "gtest/gtest.h" #include "impeller/base/validation.h" +#include "impeller/compiler/compiler.h" namespace impeller { namespace compiler { diff --git a/impeller/compiler/impellerc_main.cc b/impeller/compiler/impellerc_main.cc index de9f762238e1f..8dd49a9a25951 100644 --- a/impeller/compiler/impellerc_main.cc +++ b/impeller/compiler/impellerc_main.cc @@ -8,9 +8,9 @@ #include "flutter/fml/file.h" #include "flutter/fml/macros.h" #include "flutter/fml/mapping.h" -#include "flutter/impeller/compiler/compiler.h" -#include "flutter/impeller/compiler/switches.h" -#include "flutter/impeller/compiler/utilities.h" +#include "impeller/compiler/compiler.h" +#include "impeller/compiler/switches.h" +#include "impeller/compiler/utilities.h" #include "third_party/shaderc/libshaderc/include/shaderc/shaderc.hpp" namespace impeller { diff --git a/impeller/compiler/reflector.cc b/impeller/compiler/reflector.cc index b7f3f190e4627..b0f91d70e39ee 100644 --- a/impeller/compiler/reflector.cc +++ b/impeller/compiler/reflector.cc @@ -2,7 +2,7 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -#include "flutter/impeller/compiler/reflector.h" +#include "impeller/compiler/reflector.h" #include #include @@ -11,12 +11,12 @@ #include "flutter/fml/closure.h" #include "flutter/fml/logging.h" -#include "flutter/impeller/compiler/code_gen_template.h" -#include "flutter/impeller/compiler/utilities.h" -#include "flutter/impeller/geometry/matrix.h" -#include "flutter/impeller/geometry/scalar.h" #include "impeller/base/strings.h" #include "impeller/base/validation.h" +#include "impeller/compiler/code_gen_template.h" +#include "impeller/compiler/utilities.h" +#include "impeller/geometry/matrix.h" +#include "impeller/geometry/scalar.h" namespace impeller { namespace compiler { diff --git a/impeller/compiler/switches.cc b/impeller/compiler/switches.cc index 30f20a3006f8e..5ab2bf7453cdb 100644 --- a/impeller/compiler/switches.cc +++ b/impeller/compiler/switches.cc @@ -2,7 +2,7 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -#include "flutter/impeller/compiler/switches.h" +#include "impeller/compiler/switches.h" #include #include diff --git a/impeller/compiler/switches.h b/impeller/compiler/switches.h index 2380ca21521e6..3b7fac89d2e15 100644 --- a/impeller/compiler/switches.h +++ b/impeller/compiler/switches.h @@ -10,8 +10,8 @@ #include "flutter/fml/command_line.h" #include "flutter/fml/macros.h" #include "flutter/fml/unique_fd.h" -#include "flutter/impeller/compiler/compiler.h" -#include "flutter/impeller/compiler/include_dir.h" +#include "impeller/compiler/compiler.h" +#include "impeller/compiler/include_dir.h" namespace impeller { namespace compiler { diff --git a/impeller/compiler/utilities.cc b/impeller/compiler/utilities.cc index 3ba9ef533dd7e..78fa5df5e4703 100644 --- a/impeller/compiler/utilities.cc +++ b/impeller/compiler/utilities.cc @@ -2,7 +2,7 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -#include "flutter/impeller/compiler/utilities.h" +#include "impeller/compiler/utilities.h" #include #include diff --git a/impeller/entity/contents/clip_contents.h b/impeller/entity/contents/clip_contents.h index 41fe7cf4a53e2..1481873ae736b 100644 --- a/impeller/entity/contents/clip_contents.h +++ b/impeller/entity/contents/clip_contents.h @@ -10,7 +10,7 @@ #include "flutter/fml/macros.h" #include "impeller/entity/contents/contents.h" -#include "typographer/glyph_atlas.h" +#include "impeller/typographer/glyph_atlas.h" namespace impeller { diff --git a/impeller/entity/contents/content_context.h b/impeller/entity/contents/content_context.h index 0a3db38541c8e..45580647faa7d 100644 --- a/impeller/entity/contents/content_context.h +++ b/impeller/entity/contents/content_context.h @@ -9,17 +9,17 @@ #include "flutter/fml/hash_combine.h" #include "flutter/fml/macros.h" -#include "flutter/impeller/entity/glyph_atlas.frag.h" -#include "flutter/impeller/entity/glyph_atlas.vert.h" -#include "flutter/impeller/entity/gradient_fill.frag.h" -#include "flutter/impeller/entity/gradient_fill.vert.h" -#include "flutter/impeller/entity/solid_fill.frag.h" -#include "flutter/impeller/entity/solid_fill.vert.h" -#include "flutter/impeller/entity/solid_stroke.frag.h" -#include "flutter/impeller/entity/solid_stroke.vert.h" -#include "flutter/impeller/entity/texture_fill.frag.h" -#include "flutter/impeller/entity/texture_fill.vert.h" #include "impeller/entity/entity.h" +#include "impeller/entity/glyph_atlas.frag.h" +#include "impeller/entity/glyph_atlas.vert.h" +#include "impeller/entity/gradient_fill.frag.h" +#include "impeller/entity/gradient_fill.vert.h" +#include "impeller/entity/solid_fill.frag.h" +#include "impeller/entity/solid_fill.vert.h" +#include "impeller/entity/solid_stroke.frag.h" +#include "impeller/entity/solid_stroke.vert.h" +#include "impeller/entity/texture_fill.frag.h" +#include "impeller/entity/texture_fill.vert.h" #include "impeller/renderer/formats.h" namespace impeller { diff --git a/impeller/playground/playground.mm b/impeller/playground/playground.mm index be65b582ad6e5..9b13acc5e6683 100644 --- a/impeller/playground/playground.mm +++ b/impeller/playground/playground.mm @@ -5,10 +5,10 @@ #include #include "flutter/fml/paths.h" -#include "flutter/impeller/entity/entity_shaders.h" -#include "flutter/impeller/fixtures/shader_fixtures.h" #include "flutter/testing/testing.h" #include "impeller/base/validation.h" +#include "impeller/entity/entity_shaders.h" +#include "impeller/fixtures/shader_fixtures.h" #include "impeller/image/compressed_image.h" #include "impeller/playground/imgui/imgui_impl_impeller.h" #include "impeller/playground/imgui/imgui_shaders.h" diff --git a/impeller/renderer/renderer_unittests.cc b/impeller/renderer/renderer_unittests.cc index 01cc768328f40..d4a3482416380 100644 --- a/impeller/renderer/renderer_unittests.cc +++ b/impeller/renderer/renderer_unittests.cc @@ -3,11 +3,11 @@ // found in the LICENSE file. #include "flutter/fml/time/time_point.h" -#include "flutter/impeller/fixtures/box_fade.frag.h" -#include "flutter/impeller/fixtures/box_fade.vert.h" -#include "flutter/impeller/fixtures/test_texture.frag.h" -#include "flutter/impeller/fixtures/test_texture.vert.h" #include "flutter/testing/testing.h" +#include "impeller/fixtures/box_fade.frag.h" +#include "impeller/fixtures/box_fade.vert.h" +#include "impeller/fixtures/test_texture.frag.h" +#include "impeller/fixtures/test_texture.vert.h" #include "impeller/geometry/path_builder.h" #include "impeller/image/compressed_image.h" #include "impeller/image/decompressed_image.h" @@ -21,8 +21,8 @@ #include "impeller/renderer/sampler_descriptor.h" #include "impeller/renderer/sampler_library.h" #include "impeller/renderer/surface.h" -#include "impeller/tessellator/tessellator.h" #include "impeller/renderer/vertex_buffer_builder.h" +#include "impeller/tessellator/tessellator.h" namespace impeller { namespace testing { From 5e12e7deb8f40c7c2f2717a70eabad2a6d3da8b3 Mon Sep 17 00:00:00 2001 From: Brandon DeRosier Date: Thu, 10 Mar 2022 10:54:50 -0800 Subject: [PATCH 341/433] Hack to prevent back-to-back playground tests from hanging (#68) --- impeller/playground/playground.mm | 25 ++++++++++++++++++++++--- 1 file changed, 22 insertions(+), 3 deletions(-) diff --git a/impeller/playground/playground.mm b/impeller/playground/playground.mm index 9b13acc5e6683..c8a157af957db 100644 --- a/impeller/playground/playground.mm +++ b/impeller/playground/playground.mm @@ -107,10 +107,29 @@ static void PlaygroundKeyCallback(GLFWwindow* window, ImGui::StyleColorsDark(); ImGui::GetIO().IniFilename = nullptr; - if (::glfwInit() != GLFW_TRUE) { - return false; + // This guard is a hack to work around a problem where glfwCreateWindow + // hangs when opening a second window after GLFW has been reinitialized (for + // example, when flipping through multiple playground tests). + // + // Explanation: + // * glfwCreateWindow calls [NSApp run], which begins running the event loop + // on the current thread. + // * GLFW then immediately stops the loop when applicationDidFinishLaunching + // is fired. + // * applicationDidFinishLaunching is only ever fired once during the + // application's lifetime, so subsequent calls to [NSApp run] will always + // hang with this setup. + // * glfwInit resets the flag that guards against [NSApp run] being + // called a second time, which causes the subsequent `glfwCreateWindow` to + // hang indefinitely in the event loop, because + // applicationDidFinishLaunching is never fired. + static bool first_run = true; + if (first_run) { + first_run = false; + if (::glfwInit() != GLFW_TRUE) { + return false; + } } - fml::ScopedCleanupClosure terminate([]() { ::glfwTerminate(); }); ::glfwWindowHint(GLFW_CLIENT_API, GLFW_NO_API); From a91403f037446fa5b34232530c55916ad426749f Mon Sep 17 00:00:00 2001 From: Brandon DeRosier Date: Thu, 10 Mar 2022 10:54:59 -0800 Subject: [PATCH 342/433] Correct default blend mode, fix solid stroke shader to respect premultiplied source colors (#69) --- impeller/entity/contents/content_context.h | 2 +- impeller/entity/shaders/solid_stroke.frag | 3 +-- 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/impeller/entity/contents/content_context.h b/impeller/entity/contents/content_context.h index 45580647faa7d..042dc6aea3562 100644 --- a/impeller/entity/contents/content_context.h +++ b/impeller/entity/contents/content_context.h @@ -40,7 +40,7 @@ using ClipPipeline = PipelineT; struct ContentContextOptions { SampleCount sample_count = SampleCount::kCount1; - Entity::BlendMode blend_mode = Entity::BlendMode::kSource; + Entity::BlendMode blend_mode = Entity::BlendMode::kSourceOver; struct Hash { constexpr std::size_t operator()(const ContentContextOptions& o) const { diff --git a/impeller/entity/shaders/solid_stroke.frag b/impeller/entity/shaders/solid_stroke.frag index 3d997a40ee4a6..3a3cb61e3cfb8 100644 --- a/impeller/entity/shaders/solid_stroke.frag +++ b/impeller/entity/shaders/solid_stroke.frag @@ -8,6 +8,5 @@ in float v_pen_down; out vec4 frag_color; void main() { - frag_color = stroke_color; - frag_color.a *= floor(v_pen_down); + frag_color = stroke_color * floor(v_pen_down); } From ae818a66b8ad50d15d47a84f4f8150e86974d5a7 Mon Sep 17 00:00:00 2001 From: Brandon DeRosier Date: Thu, 10 Mar 2022 12:35:53 -0800 Subject: [PATCH 343/433] Chainable texture filters (#67) --- impeller/entity/BUILD.gn | 3 +- impeller/entity/contents/content_context.h | 6 + impeller/entity/contents/filter_contents.cc | 203 ++++++++++++++++++++ impeller/entity/contents/filter_contents.h | 94 +++++++++ impeller/entity/entity.cc | 3 +- impeller/entity/entity.h | 3 +- impeller/entity/entity_pass.cc | 2 +- impeller/entity/entity_unittests.cc | 30 ++- 8 files changed, 339 insertions(+), 5 deletions(-) create mode 100644 impeller/entity/contents/filter_contents.cc create mode 100644 impeller/entity/contents/filter_contents.h diff --git a/impeller/entity/BUILD.gn b/impeller/entity/BUILD.gn index e069fdde3cf0a..4585aed2e3102 100644 --- a/impeller/entity/BUILD.gn +++ b/impeller/entity/BUILD.gn @@ -29,6 +29,8 @@ impeller_component("entity") { "contents/content_context.h", "contents/contents.cc", "contents/contents.h", + "contents/filter_contents.cc", + "contents/filter_contents.h", "contents/linear_gradient_contents.cc", "contents/linear_gradient_contents.h", "contents/solid_color_contents.cc", @@ -55,7 +57,6 @@ impeller_component("entity") { "../typographer", ] - deps = [ "//flutter/fml", diff --git a/impeller/entity/contents/content_context.h b/impeller/entity/contents/content_context.h index 042dc6aea3562..e09e85e400ebf 100644 --- a/impeller/entity/contents/content_context.h +++ b/impeller/entity/contents/content_context.h @@ -159,6 +159,12 @@ class ContentContext { color0.src_alpha_blend_factor = BlendFactor::kOneMinusDestinationAlpha; color0.src_color_blend_factor = BlendFactor::kOneMinusDestinationAlpha; break; + case Entity::BlendMode::kPlus: + color0.dst_alpha_blend_factor = BlendFactor::kOneMinusSourceAlpha; + color0.dst_color_blend_factor = BlendFactor::kOne; + color0.src_alpha_blend_factor = BlendFactor::kSourceAlpha; + color0.src_color_blend_factor = BlendFactor::kOne; + break; } desc.SetColorAttachmentDescriptor(0u, std::move(color0)); } diff --git a/impeller/entity/contents/filter_contents.cc b/impeller/entity/contents/filter_contents.cc new file mode 100644 index 0000000000000..d043a6b3334d1 --- /dev/null +++ b/impeller/entity/contents/filter_contents.cc @@ -0,0 +1,203 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "filter_contents.h" + +#include +#include +#include + +#include "flutter/fml/logging.h" +#include "impeller/base/validation.h" +#include "impeller/entity/contents/content_context.h" +#include "impeller/entity/contents/solid_color_contents.h" +#include "impeller/entity/contents/texture_contents.h" +#include "impeller/entity/entity.h" +#include "impeller/geometry/path_builder.h" +#include "impeller/renderer/command_buffer.h" +#include "impeller/renderer/render_pass.h" +#include "impeller/renderer/sampler_library.h" + +namespace impeller { + +/******************************************************************************* + ******* FilterContents + ******************************************************************************/ + +std::shared_ptr FilterContents::MakeBlend( + Entity::BlendMode blend_mode, + InputTextures input_textures) { + auto blend = std::make_shared(); + blend->SetInputTextures(input_textures); + blend->SetBlendMode(blend_mode); + return blend; +} + +FilterContents::FilterContents() = default; + +FilterContents::~FilterContents() = default; + +void FilterContents::SetInputTextures(InputTextures input_textures) { + input_textures_ = std::move(input_textures); +} + +bool FilterContents::Render(const ContentContext& renderer, + const Entity& entity, + RenderPass& pass) const { + // Run the filter. + + auto maybe_texture = RenderFilterToTexture(renderer, entity, pass); + if (!maybe_texture.has_value()) { + return false; + } + auto& texture = maybe_texture.value(); + + // Draw the resulting texture to the given destination rect, respecting the + // transform and clip stack. + + auto contents = std::make_shared(); + contents->SetTexture(texture); + contents->SetSourceRect(IRect::MakeSize(texture->GetSize())); + + return contents->Render(renderer, entity, pass); +} + +std::optional> FilterContents::RenderFilterToTexture( + const ContentContext& renderer, + const Entity& entity, + RenderPass& pass) const { + auto output_size = GetOutputSize(); + if (output_size.IsZero()) { + return std::nullopt; + } + + // Resolve all inputs as textures. + + std::vector> input_textures; + input_textures.reserve(input_textures_.size()); + for (const auto& input : input_textures_) { + if (auto filter = std::get_if>(&input)) { + auto texture = + filter->get()->RenderFilterToTexture(renderer, entity, pass); + if (!texture.has_value()) { + return std::nullopt; + } + input_textures.push_back(std::move(texture.value())); + } else if (auto texture = std::get_if>(&input)) { + input_textures.push_back(*texture); + } else { + FML_UNREACHABLE(); + } + } + + // Create a new texture and render the filter to it. + + auto context = renderer.GetContext(); + + auto subpass_target = RenderTarget::CreateOffscreen(*context, output_size); + auto subpass_texture = subpass_target.GetRenderTargetTexture(); + if (!subpass_texture) { + return std::nullopt; + } + + auto sub_command_buffer = context->CreateRenderCommandBuffer(); + sub_command_buffer->SetLabel("Offscreen Filter Command Buffer"); + if (!sub_command_buffer) { + return std::nullopt; + } + + auto sub_renderpass = sub_command_buffer->CreateRenderPass(subpass_target); + if (!sub_renderpass) { + return std::nullopt; + } + sub_renderpass->SetLabel("OffscreenFilterPass"); + + if (!RenderFilter(input_textures, renderer, *sub_renderpass)) { + return std::nullopt; + } + + if (!sub_renderpass->EncodeCommands(*context->GetTransientsAllocator())) { + return std::nullopt; + } + + if (!sub_command_buffer->SubmitCommands()) { + return std::nullopt; + } + + return subpass_texture; +} + +ISize FilterContents::GetOutputSize() const { + if (input_textures_.empty()) { + return {}; + } + + if (auto filter = + std::get_if>(&input_textures_[0])) { + return filter->get()->GetOutputSize(); + } + + if (auto texture = + std::get_if>(&input_textures_[0])) { + return texture->get()->GetSize(); + } + + FML_UNREACHABLE(); +} + +/******************************************************************************* + ******* BlendFilterContents + ******************************************************************************/ + +BlendFilterContents::BlendFilterContents() = default; + +BlendFilterContents::~BlendFilterContents() = default; + +void BlendFilterContents::SetBlendMode(Entity::BlendMode blend_mode) { + blend_mode_ = blend_mode; +} + +bool BlendFilterContents::RenderFilter( + const std::vector>& input_textures, + const ContentContext& renderer, + RenderPass& pass) const { + using VS = TexturePipeline::VertexShader; + using FS = TexturePipeline::FragmentShader; + + auto& host_buffer = pass.GetTransientsBuffer(); + + VertexBufferBuilder vtx_builder; + vtx_builder.AddVertices({ + {Point(0, 0), Point(0, 0)}, + {Point(1, 0), Point(1, 0)}, + {Point(1, 1), Point(1, 1)}, + {Point(0, 0), Point(0, 0)}, + {Point(1, 1), Point(1, 1)}, + {Point(0, 1), Point(0, 1)}, + }); + auto vtx_buffer = vtx_builder.CreateVertexBuffer(host_buffer); + + VS::FrameInfo frame_info; + frame_info.mvp = Matrix::MakeOrthographic(ISize(1, 1)); + frame_info.alpha = 1; + + auto uniform_view = host_buffer.EmplaceUniform(frame_info); + auto sampler = renderer.GetContext()->GetSamplerLibrary()->GetSampler({}); + + Command cmd; + cmd.label = "Blend Filter"; + auto options = OptionsFromPass(pass); + options.blend_mode = blend_mode_; + cmd.pipeline = renderer.GetTexturePipeline(options); + cmd.BindVertices(vtx_buffer); + VS::BindFrameInfo(cmd, uniform_view); + for (const auto& texture : input_textures) { + FS::BindTextureSampler(cmd, texture, sampler); + pass.AddCommand(cmd); + } + + return true; +} + +} // namespace impeller diff --git a/impeller/entity/contents/filter_contents.h b/impeller/entity/contents/filter_contents.h new file mode 100644 index 0000000000000..b1839cff4efbd --- /dev/null +++ b/impeller/entity/contents/filter_contents.h @@ -0,0 +1,94 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#pragma once + +#include +#include +#include + +#include "impeller/entity/contents/contents.h" +#include "impeller/entity/entity.h" +#include "impeller/renderer/formats.h" + +namespace impeller { + +/******************************************************************************* + ******* FilterContents + ******************************************************************************/ + +class FilterContents : public Contents { + public: + using InputVariant = + std::variant, std::shared_ptr>; + using InputTextures = std::vector; + + static std::shared_ptr MakeBlend( + Entity::BlendMode blend_mode, + InputTextures input_textures); + + FilterContents(); + + ~FilterContents() override; + + /// @brief The input texture sources for this filter. All texture sources are + /// expected to have or produce premultiplied alpha colors. + /// Any input can either be a `Texture` or another `FilterContents`. + /// + /// The number of required or optional textures depends on the + /// particular filter's implementation. + void SetInputTextures(InputTextures input_textures); + + // |Contents| + bool Render(const ContentContext& renderer, + const Entity& entity, + RenderPass& pass) const override; + + /// @brief Renders dependency filters, creates a subpass, and calls the + /// `RenderFilter` defined by the subclasses. + std::optional> RenderFilterToTexture( + const ContentContext& renderer, + const Entity& entity, + RenderPass& pass) const; + + private: + /// @brief Takes a set of zero or more input textures and writes to an output + /// texture. + virtual bool RenderFilter( + const std::vector>& input_textures, + const ContentContext& renderer, + RenderPass& pass) const = 0; + + /// @brief Determines the size of the output texture. + virtual ISize GetOutputSize() const; + + InputTextures input_textures_; + Rect destination_; + + FML_DISALLOW_COPY_AND_ASSIGN(FilterContents); +}; + +/******************************************************************************* + ******* BlendFilterContents + ******************************************************************************/ + +class BlendFilterContents : public FilterContents { + public: + BlendFilterContents(); + + ~BlendFilterContents() override; + + void SetBlendMode(Entity::BlendMode blend_mode); + + private: + bool RenderFilter(const std::vector>& input_textures, + const ContentContext& renderer, + RenderPass& pass) const override; + + Entity::BlendMode blend_mode_ = Entity::BlendMode::kSourceOver; + + FML_DISALLOW_COPY_AND_ASSIGN(BlendFilterContents); +}; + +} // namespace impeller diff --git a/impeller/entity/entity.cc b/impeller/entity/entity.cc index dc976aa6874d3..7f595024f989c 100644 --- a/impeller/entity/entity.cc +++ b/impeller/entity/entity.cc @@ -65,7 +65,8 @@ void Entity::IncrementStencilDepth(uint32_t increment) { stencil_depth_ += increment; } -bool Entity::Render(ContentContext& renderer, RenderPass& parent_pass) const { +bool Entity::Render(const ContentContext& renderer, + RenderPass& parent_pass) const { if (!contents_) { return true; } diff --git a/impeller/entity/entity.h b/impeller/entity/entity.h index c43b549df6316..7d22c2bfd5ad7 100644 --- a/impeller/entity/entity.h +++ b/impeller/entity/entity.h @@ -27,6 +27,7 @@ class Entity { kDestination, kSourceOver, kDestinationOver, + kPlus, }; Entity(); @@ -57,7 +58,7 @@ class Entity { uint32_t GetStencilDepth() const; - bool Render(ContentContext& renderer, RenderPass& parent_pass) const; + bool Render(const ContentContext& renderer, RenderPass& parent_pass) const; private: Matrix transformation_; diff --git a/impeller/entity/entity_pass.cc b/impeller/entity/entity_pass.cc index b33f44400227c..6840c2b30e896 100644 --- a/impeller/entity/entity_pass.cc +++ b/impeller/entity/entity_pass.cc @@ -75,7 +75,7 @@ std::optional EntityPass::GetSubpassCoverage( return entities_coverage; } - // If the delete tells us the coverage is smaller than it needs to be, then + // If the delegate tells us the coverage is smaller than it needs to be, then // great. OTOH, if the delegate is being wasteful, limit coverage to what is // actually needed. return entities_coverage->Intersection(delegate_coverage.value()); diff --git a/impeller/entity/entity_unittests.cc b/impeller/entity/entity_unittests.cc index b0fca2aa83139..c418ff7cb62f2 100644 --- a/impeller/entity/entity_unittests.cc +++ b/impeller/entity/entity_unittests.cc @@ -3,6 +3,7 @@ // found in the LICENSE file. #include "flutter/testing/testing.h" +#include "impeller/entity/contents/filter_contents.h" #include "impeller/entity/contents/solid_color_contents.h" #include "impeller/entity/contents/solid_stroke_contents.h" #include "impeller/entity/entity.h" @@ -521,6 +522,9 @@ TEST_F(EntityTest, BlendingModeOptions) { case Entity::BlendMode::kDestinationOver: blend_mode_names.push_back("DestinationOver"); blend_mode_values.push_back(Entity::BlendMode::kDestinationOver); + case Entity::BlendMode::kPlus: + blend_mode_names.push_back("Plus"); + blend_mode_values.push_back(Entity::BlendMode::kPlus); }; } @@ -616,10 +620,34 @@ TEST_F(EntityTest, BezierCircleScaled) { .Close() .TakePath(); entity.SetPath(path); - entity.SetTransformation(Matrix::MakeScale({20.0, 20.0, 1.0}).Translate({-80, -15, 0})); + entity.SetTransformation( + Matrix::MakeScale({20.0, 20.0, 1.0}).Translate({-80, -15, 0})); entity.SetContents(SolidColorContents::Make(Color::Red())); ASSERT_TRUE(OpenPlaygroundHere(entity)); } +TEST_F(EntityTest, Filters) { + auto bridge = CreateTextureForFixture("bay_bridge.jpg"); + auto boston = CreateTextureForFixture("boston.jpg"); + auto kalimba = CreateTextureForFixture("kalimba.jpg"); + ASSERT_TRUE(bridge && boston && kalimba); + + auto callback = [&](ContentContext& context, RenderPass& pass) -> bool { + // Draws kalimba and overwrites it with boston. + auto blend0 = FilterContents::MakeBlend( + Entity::BlendMode::kSourceOver, {kalimba, boston}); + + // Adds bridge*3 to boston. + auto blend1 = FilterContents::MakeBlend( + Entity::BlendMode::kPlus, {bridge, bridge, blend0, bridge}); + + Entity entity; + entity.SetPath(PathBuilder{}.AddRect({100, 100, 300, 300}).TakePath()); + entity.SetContents(blend1); + return entity.Render(context, pass); + }; + ASSERT_TRUE(OpenPlaygroundHere(callback)); +} + } // namespace testing } // namespace impeller From 6763e96966ebce85e462acef35059979783eb94a Mon Sep 17 00:00:00 2001 From: Chinmay Garde Date: Thu, 10 Mar 2022 13:36:49 -0800 Subject: [PATCH 344/433] Document GN rules and make targets that cannot be built on the platform be no-ops. (#70) --- impeller/compiler/BUILD.gn | 4 +- .../display_list/display_list_dispatcher.cc | 12 +- .../renderer/backend/metal/render_pass_mtl.mm | 7 +- impeller/renderer/renderer.h | 2 - impeller/tools/impeller.gni | 103 ++++++++++++++---- 5 files changed, 94 insertions(+), 34 deletions(-) diff --git a/impeller/compiler/BUILD.gn b/impeller/compiler/BUILD.gn index 363377b10d5b7..77050e2a18f03 100644 --- a/impeller/compiler/BUILD.gn +++ b/impeller/compiler/BUILD.gn @@ -30,7 +30,9 @@ impeller_component("compiler_lib") { ] } -executable("impellerc") { +impeller_component("impellerc") { + target_type = "executable" + sources = [ "impellerc_main.cc" ] deps = [ ":compiler_lib" ] diff --git a/impeller/display_list/display_list_dispatcher.cc b/impeller/display_list/display_list_dispatcher.cc index a8e500ac89c23..fef486e321270 100644 --- a/impeller/display_list/display_list_dispatcher.cc +++ b/impeller/display_list/display_list_dispatcher.cc @@ -539,17 +539,7 @@ void DisplayListDispatcher::drawDisplayList( void DisplayListDispatcher::drawTextBlob(const sk_sp blob, SkScalar x, SkScalar y) { - if (!blob) { - return; - } - - auto bounds = blob->bounds(); - bounds.fLeft += x; - bounds.fTop += y; - - impeller::Paint paint; - paint.color = impeller::Color::Random().WithAlpha(0.2); - canvas_.DrawRect(ToRect(bounds), paint); + UNIMPLEMENTED; } // |flutter::Dispatcher| diff --git a/impeller/renderer/backend/metal/render_pass_mtl.mm b/impeller/renderer/backend/metal/render_pass_mtl.mm index 068dd67ba1dc0..ef1dd15b1d273 100644 --- a/impeller/renderer/backend/metal/render_pass_mtl.mm +++ b/impeller/renderer/backend/metal/render_pass_mtl.mm @@ -397,7 +397,6 @@ static bool Bind(PassBindingsCache& pass, fml::closure pop_debug_marker = [encoder]() { [encoder popDebugGroup]; }; for (const auto& command : commands_) { if (command.index_count == 0u) { - VALIDATION_LOG << "Zero index count in render pass command."; continue; } @@ -505,6 +504,12 @@ static bool Bind(PassBindingsCache& pass, } } + if (command.index_count == 0u) { + // Essentially a no-op. Don't record the command but this is not necessary + // an error either. + return true; + } + commands_.emplace_back(std::move(command)); return true; } diff --git a/impeller/renderer/renderer.h b/impeller/renderer/renderer.h index 3ba709923b28e..08be51856aac2 100644 --- a/impeller/renderer/renderer.h +++ b/impeller/renderer/renderer.h @@ -4,8 +4,6 @@ #pragma once -#include - #include #include diff --git a/impeller/tools/impeller.gni b/impeller/tools/impeller.gni index 3594914db1c2b..717f6bd19aec9 100644 --- a/impeller/tools/impeller.gni +++ b/impeller/tools/impeller.gni @@ -8,38 +8,69 @@ import("//flutter/common/config.gni") declare_args() { # Whether playgrounds are enabled for unit tests. impeller_enable_playground = false + + # Whether Impeller is supported on the platform. + impeller_supports_platform = is_mac || is_ios } +# ------------------------------------------------------------------------------ +# @brief Define an Impeller component. Components are different +# Impeller subsystems part of the umbrella framework. +# +# @param[optional] target_type The type of the component. This can be any of +# the target types supported by GN. Defaults to +# source_set. If Impeller is not supported on the +# target platform, this target is a no-op. +# template("impeller_component") { - source_set(target_name) { - forward_variables_from(invoker, "*") - - if (!defined(invoker.public_configs)) { - public_configs = [] + if (impeller_supports_platform) { + target_type = "source_set" + if (defined(invoker.target_type)) { + target_type = invoker.target_type } + target(target_type, target_name) { + forward_variables_from(invoker, "*") - if (!defined(invoker.cflags_objc)) { - cflags_objc = [] - } + if (!defined(invoker.public_configs)) { + public_configs = [] + } - if (!defined(invoker.cflags_objcc)) { - cflags_objcc = [] - } + public_configs += [ "//flutter/impeller:impeller_public_config" ] - if (!defined(invoker.deps)) { - deps = [] - } + if (!defined(invoker.cflags_objc)) { + cflags_objc = [] + } - objc_warning_flags = [ "-Wno-unused-private-field" ] + if (is_ios || is_mac) { + cflags_objc += flutter_cflags_objc_arc + } - cflags_objc += flutter_cflags_objc_arc + objc_warning_flags - cflags_objcc += flutter_cflags_objcc_arc + objc_warning_flags + if (!defined(invoker.cflags_objcc)) { + cflags_objcc = [] + } - public_configs += [ "//flutter/impeller:impeller_public_config" ] + if (is_ios || is_mac) { + cflags_objcc += flutter_cflags_objcc_arc + } + } + } else { + group(target_name) { + not_needed(invoker, "*") + } } } +# ------------------------------------------------------------------------------ +# @brief Build a Metal Library. The output is a single file. Use +# get_target_outputs to get its location on build. +# +# @param[required] name The name of the Metal library. +# +# @param[required] sources The GLSL (4.60) sources to compiled into the Metal +# library. +# template("metal_library") { + assert(is_ios || is_mac) assert(defined(invoker.name), "Metal library name must be specified.") assert(defined(invoker.sources), "Metal source files must be specified.") @@ -94,7 +125,17 @@ template("metal_library") { } } -template("impeller_shaders") { +# ------------------------------------------------------------------------------ +# @brief Build and reflect shader information. Reflected shader +# information will be added to a generated source set along +# with the shader contents. +# +# @param[required] name The name of the shader library. +# +# @param[required] sources The GLSL (4.60) sources to compiled into the shader +# library. +# +template("impeller_shaders_real") { assert(defined(invoker.shaders), "Impeller shaders must be specified.") assert(defined(invoker.name), "Name of the shader library must be specified.") @@ -229,3 +270,27 @@ template("impeller_shaders") { ] } } + +# ------------------------------------------------------------------------------ +# @brief Builds the shader library from shader sources, generates +# reflected shader information as source set, and, generates a +# translation unit added as a source set that allows embedding +# shaders into the final binary. On platforms where Impeller is +# not supported, this is a no-op. +# +# @note For additional information about parameters, see +# `impeller_shaders_real` +# +# @see impeller_shaders_real +# +template("impeller_shaders") { + if (impeller_supports_platform) { + impeller_shaders_real(target_name) { + forward_variables_from(invoker, "*") + } + } else { + group(target_name) { + not_needed(invoker, "*") + } + } +} From aabab0d25ad4f95f0f5c346323042f4e563626d4 Mon Sep 17 00:00:00 2001 From: Chinmay Garde Date: Thu, 10 Mar 2022 15:34:46 -0800 Subject: [PATCH 345/433] Fix diagnostic on iOS simulators. (#71) --- impeller/renderer/backend/metal/surface_mtl.h | 7 +++++++ impeller/renderer/backend/metal/surface_mtl.mm | 4 ++++ 2 files changed, 11 insertions(+) diff --git a/impeller/renderer/backend/metal/surface_mtl.h b/impeller/renderer/backend/metal/surface_mtl.h index 44f2c49410991..ae6cd8da0a32a 100644 --- a/impeller/renderer/backend/metal/surface_mtl.h +++ b/impeller/renderer/backend/metal/surface_mtl.h @@ -14,6 +14,12 @@ namespace impeller { class SurfaceMTL final : public Surface { public: +#pragma GCC diagnostic push + // Disable the diagnostic for iOS Simulators. Metal without emulation isn't + // available prior to iOS 13 and that's what the simulator headers say when + // support for CAMetalLayer begins. CAMetalLayer is available on iOS 8.0 and + // above which is well below Flutters support level. +#pragma GCC diagnostic ignored "-Wunguarded-availability-new" //---------------------------------------------------------------------------- /// @brief Wraps the current drawable of the given Metal layer to create /// a surface Impeller can render to. The surface must be created @@ -29,6 +35,7 @@ class SurfaceMTL final : public Surface { static std::unique_ptr WrapCurrentMetalLayerDrawable( std::shared_ptr context, CAMetalLayer* layer); +#pragma GCC diagnostic pop // |Surface| ~SurfaceMTL() override; diff --git a/impeller/renderer/backend/metal/surface_mtl.mm b/impeller/renderer/backend/metal/surface_mtl.mm index 3dbd602ab0ad2..42826b1b9e54b 100644 --- a/impeller/renderer/backend/metal/surface_mtl.mm +++ b/impeller/renderer/backend/metal/surface_mtl.mm @@ -12,6 +12,9 @@ namespace impeller { +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wunguarded-availability-new" + std::unique_ptr SurfaceMTL::WrapCurrentMetalLayerDrawable( std::shared_ptr context, CAMetalLayer* layer) { @@ -114,5 +117,6 @@ [drawable_ present]; return true; } +#pragma GCC diagnostic pop } // namespace impeller From 565cfdc5ab823d4ba03b3b0f81cb92109c881546 Mon Sep 17 00:00:00 2001 From: Chinmay Garde Date: Thu, 10 Mar 2022 16:26:52 -0800 Subject: [PATCH 346/433] Only depend on FML for tracing when building executables. (#72) Otherwise, depending on Impeller will cause an explicit dependency on whatever VM variant :libdart picks. And, some other unit-test in the engine explicitly link in the JIT variant which leads to duplicate symbol errors in profile and release modes. --- impeller/BUILD.gn | 4 ++++ impeller/aiks/BUILD.gn | 8 +------- impeller/archivist/BUILD.gn | 4 ---- impeller/base/BUILD.gn | 8 +------- impeller/compiler/BUILD.gn | 9 +++++++-- impeller/entity/BUILD.gn | 8 +------- impeller/image/BUILD.gn | 4 ---- impeller/renderer/BUILD.gn | 8 +------- impeller/typographer/BUILD.gn | 8 +------- 9 files changed, 16 insertions(+), 45 deletions(-) diff --git a/impeller/BUILD.gn b/impeller/BUILD.gn index ff9e7be985f60..8e372e58f630c 100644 --- a/impeller/BUILD.gn +++ b/impeller/BUILD.gn @@ -37,5 +37,9 @@ executable("impeller_unittests") { "playground", "renderer:renderer_unittests", "typographer:typographer_unittests", + + # FML depends on the Dart VM for tracing and getting the current + # timepoint. + "//flutter/runtime:libdart", ] } diff --git a/impeller/aiks/BUILD.gn b/impeller/aiks/BUILD.gn index db0e733258490..03267c45c9830 100644 --- a/impeller/aiks/BUILD.gn +++ b/impeller/aiks/BUILD.gn @@ -28,13 +28,7 @@ impeller_component("aiks") { "../geometry", ] - deps = [ - "//flutter/fml", - - # FML depends on the Dart VM for tracing and getting the current - # timepoint. - "//flutter/runtime:libdart", - ] + deps = [ "//flutter/fml" ] } impeller_component("aiks_unittests") { diff --git a/impeller/archivist/BUILD.gn b/impeller/archivist/BUILD.gn index ab25781748c45..92b9402c9e3d3 100644 --- a/impeller/archivist/BUILD.gn +++ b/impeller/archivist/BUILD.gn @@ -33,10 +33,6 @@ impeller_component("archivist") { deps = [ "//flutter/fml", - - # FML depends on the Dart VM for tracing and getting the current - # timepoint. - "//flutter/runtime:libdart", "//third_party/sqlite", ] } diff --git a/impeller/base/BUILD.gn b/impeller/base/BUILD.gn index 19fc548aeff90..939f50a8a3b47 100644 --- a/impeller/base/BUILD.gn +++ b/impeller/base/BUILD.gn @@ -21,13 +21,7 @@ impeller_component("base") { "validation.h", ] - deps = [ - "//flutter/fml", - - # FML depends on the Dart VM for tracing and getting the current - # timepoint. - "//flutter/runtime:libdart", - ] + deps = [ "//flutter/fml" ] } impeller_component("base_unittests") { diff --git a/impeller/compiler/BUILD.gn b/impeller/compiler/BUILD.gn index 77050e2a18f03..64155540c403f 100644 --- a/impeller/compiler/BUILD.gn +++ b/impeller/compiler/BUILD.gn @@ -23,7 +23,6 @@ impeller_component("compiler_lib") { "../base", "../geometry", "//flutter/fml", - "//flutter/runtime:libdart", "//third_party/inja", "//third_party/shaderc_flutter", "//third_party/spirv_cross_flutter", @@ -35,7 +34,13 @@ impeller_component("impellerc") { sources = [ "impellerc_main.cc" ] - deps = [ ":compiler_lib" ] + deps = [ + ":compiler_lib", + + # FML depends on the Dart VM for tracing and getting the current + # timepoint. + "//flutter/runtime:libdart", + ] } group("compiler") { diff --git a/impeller/entity/BUILD.gn b/impeller/entity/BUILD.gn index 4585aed2e3102..4e12078c2c999 100644 --- a/impeller/entity/BUILD.gn +++ b/impeller/entity/BUILD.gn @@ -57,13 +57,7 @@ impeller_component("entity") { "../typographer", ] - deps = [ - "//flutter/fml", - - # FML depends on the Dart VM for tracing and getting the current - # timepoint. - "//flutter/runtime:libdart", - ] + deps = [ "//flutter/fml" ] } impeller_component("entity_unittests") { diff --git a/impeller/image/BUILD.gn b/impeller/image/BUILD.gn index 3d2b787c2740f..0343f0604c5f5 100644 --- a/impeller/image/BUILD.gn +++ b/impeller/image/BUILD.gn @@ -24,10 +24,6 @@ impeller_component("image") { deps = [ "//flutter/fml", - - # FML depends on the Dart VM for tracing and getting the current - # timepoint. - "//flutter/runtime:libdart", "//third_party/skia", ] } diff --git a/impeller/renderer/BUILD.gn b/impeller/renderer/BUILD.gn index e719ebd89e3dd..49cb4d6d4aa7d 100644 --- a/impeller/renderer/BUILD.gn +++ b/impeller/renderer/BUILD.gn @@ -108,13 +108,7 @@ impeller_component("renderer") { "../tessellator", ] - deps = [ - "//flutter/fml", - - # FML depends on the Dart VM for tracing and getting the current - # timepoint. - "//flutter/runtime:libdart", - ] + deps = [ "//flutter/fml" ] frameworks = [ "Metal.framework" ] } diff --git a/impeller/typographer/BUILD.gn b/impeller/typographer/BUILD.gn index 10d95c452589b..1c10a77a8deaf 100644 --- a/impeller/typographer/BUILD.gn +++ b/impeller/typographer/BUILD.gn @@ -37,13 +37,7 @@ impeller_component("typographer") { "//third_party/skia", ] - deps = [ - "//flutter/fml", - - # FML depends on the Dart VM for tracing and getting the current - # timepoint. - "//flutter/runtime:libdart", - ] + deps = [ "//flutter/fml" ] } impeller_component("typographer_unittests") { From 32705a11e7c95295f511161b7f5ee8456c59d11a Mon Sep 17 00:00:00 2001 From: Chinmay Garde Date: Mon, 14 Mar 2022 13:28:36 -0700 Subject: [PATCH 347/433] Fix loop-range-construct warnings added in newer versions of Clang. (#75) --- impeller/renderer/backend/metal/render_pass_mtl.mm | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/impeller/renderer/backend/metal/render_pass_mtl.mm b/impeller/renderer/backend/metal/render_pass_mtl.mm index ef1dd15b1d273..77a70ebbf5d12 100644 --- a/impeller/renderer/backend/metal/render_pass_mtl.mm +++ b/impeller/renderer/backend/metal/render_pass_mtl.mm @@ -374,17 +374,17 @@ static bool Bind(PassBindingsCache& pass, auto bind_stage_resources = [&allocator, &pass_bindings]( const Bindings& bindings, ShaderStage stage) -> bool { - for (const auto buffer : bindings.buffers) { + for (const auto& buffer : bindings.buffers) { if (!Bind(pass_bindings, allocator, stage, buffer.first, buffer.second)) { return false; } } - for (const auto texture : bindings.textures) { + for (const auto& texture : bindings.textures) { if (!Bind(pass_bindings, stage, texture.first, *texture.second)) { return false; } } - for (const auto sampler : bindings.samplers) { + for (const auto& sampler : bindings.samplers) { if (!Bind(pass_bindings, stage, sampler.first, *sampler.second)) { return false; } From 121f454dfd06fd34c4a48a46a1b379010116b7ae Mon Sep 17 00:00:00 2001 From: Brandon DeRosier Date: Mon, 14 Mar 2022 14:28:04 -0700 Subject: [PATCH 348/433] Add remaining additive blends (#76) --- impeller/entity/contents/content_context.h | 53 +++++++++++++++++++++- impeller/entity/entity.h | 8 ++++ impeller/entity/entity_unittests.cc | 24 ++++++++++ 3 files changed, 83 insertions(+), 2 deletions(-) diff --git a/impeller/entity/contents/content_context.h b/impeller/entity/contents/content_context.h index e09e85e400ebf..5003623f85c4c 100644 --- a/impeller/entity/contents/content_context.h +++ b/impeller/entity/contents/content_context.h @@ -159,12 +159,61 @@ class ContentContext { color0.src_alpha_blend_factor = BlendFactor::kOneMinusDestinationAlpha; color0.src_color_blend_factor = BlendFactor::kOneMinusDestinationAlpha; break; - case Entity::BlendMode::kPlus: + case Entity::BlendMode::kSourceIn: + color0.dst_alpha_blend_factor = BlendFactor::kZero; + color0.dst_color_blend_factor = BlendFactor::kZero; + color0.src_alpha_blend_factor = BlendFactor::kDestinationAlpha; + color0.src_color_blend_factor = BlendFactor::kDestinationAlpha; + break; + case Entity::BlendMode::kDestinationIn: + color0.dst_alpha_blend_factor = BlendFactor::kSourceAlpha; + color0.dst_color_blend_factor = BlendFactor::kSourceAlpha; + color0.src_alpha_blend_factor = BlendFactor::kZero; + color0.src_color_blend_factor = BlendFactor::kZero; + break; + case Entity::BlendMode::kSourceOut: + color0.dst_alpha_blend_factor = BlendFactor::kZero; + color0.dst_color_blend_factor = BlendFactor::kZero; + color0.src_alpha_blend_factor = BlendFactor::kOneMinusDestinationAlpha; + color0.src_color_blend_factor = BlendFactor::kOneMinusDestinationAlpha; + break; + case Entity::BlendMode::kDestinationOut: + color0.dst_alpha_blend_factor = BlendFactor::kOneMinusSourceAlpha; + color0.dst_color_blend_factor = BlendFactor::kOneMinusSourceAlpha; + color0.src_alpha_blend_factor = BlendFactor::kZero; + color0.src_color_blend_factor = BlendFactor::kZero; + break; + case Entity::BlendMode::kSourceATop: color0.dst_alpha_blend_factor = BlendFactor::kOneMinusSourceAlpha; + color0.dst_color_blend_factor = BlendFactor::kOneMinusSourceAlpha; + color0.src_alpha_blend_factor = BlendFactor::kDestinationAlpha; + color0.src_color_blend_factor = BlendFactor::kDestinationAlpha; + break; + case Entity::BlendMode::kDestinationATop: + color0.dst_alpha_blend_factor = BlendFactor::kSourceAlpha; + color0.dst_color_blend_factor = BlendFactor::kSourceAlpha; + color0.src_alpha_blend_factor = BlendFactor::kOneMinusDestinationAlpha; + color0.src_color_blend_factor = BlendFactor::kOneMinusDestinationAlpha; + break; + case Entity::BlendMode::kXor: + color0.dst_alpha_blend_factor = BlendFactor::kOneMinusSourceAlpha; + color0.dst_color_blend_factor = BlendFactor::kOneMinusSourceAlpha; + color0.src_alpha_blend_factor = BlendFactor::kOneMinusDestinationAlpha; + color0.src_color_blend_factor = BlendFactor::kOneMinusDestinationAlpha; + break; + case Entity::BlendMode::kPlus: + color0.dst_alpha_blend_factor = BlendFactor::kOne; color0.dst_color_blend_factor = BlendFactor::kOne; - color0.src_alpha_blend_factor = BlendFactor::kSourceAlpha; + color0.src_alpha_blend_factor = BlendFactor::kOne; color0.src_color_blend_factor = BlendFactor::kOne; break; + case Entity::BlendMode::kModulate: + // kSourceColor and kDestinationColor override the alpha blend factor. + color0.dst_alpha_blend_factor = BlendFactor::kZero; + color0.dst_color_blend_factor = BlendFactor::kSourceColor; + color0.src_alpha_blend_factor = BlendFactor::kZero; + color0.src_color_blend_factor = BlendFactor::kZero; + break; } desc.SetColorAttachmentDescriptor(0u, std::move(color0)); } diff --git a/impeller/entity/entity.h b/impeller/entity/entity.h index 7d22c2bfd5ad7..36d965f36e28e 100644 --- a/impeller/entity/entity.h +++ b/impeller/entity/entity.h @@ -27,7 +27,15 @@ class Entity { kDestination, kSourceOver, kDestinationOver, + kSourceIn, + kDestinationIn, + kSourceOut, + kDestinationOut, + kSourceATop, + kDestinationATop, + kXor, kPlus, + kModulate, }; Entity(); diff --git a/impeller/entity/entity_unittests.cc b/impeller/entity/entity_unittests.cc index c418ff7cb62f2..87cf91a69056f 100644 --- a/impeller/entity/entity_unittests.cc +++ b/impeller/entity/entity_unittests.cc @@ -522,9 +522,33 @@ TEST_F(EntityTest, BlendingModeOptions) { case Entity::BlendMode::kDestinationOver: blend_mode_names.push_back("DestinationOver"); blend_mode_values.push_back(Entity::BlendMode::kDestinationOver); + case Entity::BlendMode::kSourceIn: + blend_mode_names.push_back("SourceIn"); + blend_mode_values.push_back(Entity::BlendMode::kSourceIn); + case Entity::BlendMode::kDestinationIn: + blend_mode_names.push_back("DestinationIn"); + blend_mode_values.push_back(Entity::BlendMode::kDestinationIn); + case Entity::BlendMode::kSourceOut: + blend_mode_names.push_back("SourceOut"); + blend_mode_values.push_back(Entity::BlendMode::kSourceOut); + case Entity::BlendMode::kDestinationOut: + blend_mode_names.push_back("DestinationOut"); + blend_mode_values.push_back(Entity::BlendMode::kDestinationOut); + case Entity::BlendMode::kSourceATop: + blend_mode_names.push_back("SourceATop"); + blend_mode_values.push_back(Entity::BlendMode::kSourceATop); + case Entity::BlendMode::kDestinationATop: + blend_mode_names.push_back("DestinationATop"); + blend_mode_values.push_back(Entity::BlendMode::kDestinationATop); + case Entity::BlendMode::kXor: + blend_mode_names.push_back("Xor"); + blend_mode_values.push_back(Entity::BlendMode::kXor); case Entity::BlendMode::kPlus: blend_mode_names.push_back("Plus"); blend_mode_values.push_back(Entity::BlendMode::kPlus); + case Entity::BlendMode::kModulate: + blend_mode_names.push_back("Modulate"); + blend_mode_values.push_back(Entity::BlendMode::kModulate); }; } From 61c6d48f308d685e60b03eb0dfd76fd0c4d42216 Mon Sep 17 00:00:00 2001 From: Chinmay Garde Date: Mon, 14 Mar 2022 15:03:11 -0700 Subject: [PATCH 349/433] Fix issues with constexpr correctness. (#77) Some like std::abs are not available till C++23. The others were real warnings. --- impeller/geometry/scalar.h | 8 +++++++- impeller/geometry/vector.h | 4 ++-- impeller/renderer/backend/metal/formats_mtl.h | 2 +- 3 files changed, 10 insertions(+), 4 deletions(-) diff --git a/impeller/geometry/scalar.h b/impeller/geometry/scalar.h index 9350f825114f6..3fc11c400be7a 100644 --- a/impeller/geometry/scalar.h +++ b/impeller/geometry/scalar.h @@ -5,6 +5,7 @@ #pragma once #include +#include #include #include "impeller/geometry/constants.h" @@ -13,10 +14,15 @@ namespace impeller { using Scalar = float; +template >> +constexpr T Absolute(const T& val) { + return val >= T{} ? val : -val; +} + constexpr inline bool ScalarNearlyEqual(Scalar x, Scalar y, Scalar tolerance = 1e-3) { - return std::abs(x - y) <= tolerance; + return Absolute(x - y) <= tolerance; } struct Degrees; diff --git a/impeller/geometry/vector.h b/impeller/geometry/vector.h index 6c5071bfc15d9..cd6d982e11dd3 100644 --- a/impeller/geometry/vector.h +++ b/impeller/geometry/vector.h @@ -41,7 +41,7 @@ struct Vector3 { * * @return the calculated length. */ - constexpr Scalar Length() const { return sqrt(x * x + y * y + z * z); } + Scalar Length() const { return sqrt(x * x + y * y + z * z); } constexpr Vector3 Normalize() const { const auto len = Length(); @@ -125,7 +125,7 @@ struct Vector4 { constexpr Vector4(const Point& p) : x(p.x), y(p.y) {} - constexpr Vector4 Normalize() const { + Vector4 Normalize() const { const Scalar inverse = 1.0 / sqrt(x * x + y * y + z * z + w * w); return Vector4(x * inverse, y * inverse, z * inverse, w * inverse); } diff --git a/impeller/renderer/backend/metal/formats_mtl.h b/impeller/renderer/backend/metal/formats_mtl.h index dd91136924c1e..8fb4fe2040d33 100644 --- a/impeller/renderer/backend/metal/formats_mtl.h +++ b/impeller/renderer/backend/metal/formats_mtl.h @@ -294,7 +294,7 @@ constexpr MTLSamplerAddressMode ToMTLSamplerAddressMode( return MTLSamplerAddressModeClampToEdge; } -constexpr MTLClearColor ToMTLClearColor(const Color& color) { +inline MTLClearColor ToMTLClearColor(const Color& color) { return MTLClearColorMake(color.red, color.green, color.blue, color.alpha); } From 5d80431389676f2600716f9f44e767908eb5db53 Mon Sep 17 00:00:00 2001 From: Chinmay Garde Date: Tue, 15 Mar 2022 13:27:45 -0700 Subject: [PATCH 350/433] Add case necessary on 32-bit arm builds. (#79) Offset is a uint64_t that needs a cast. --- impeller/renderer/backend/metal/render_pass_mtl.mm | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/impeller/renderer/backend/metal/render_pass_mtl.mm b/impeller/renderer/backend/metal/render_pass_mtl.mm index 77a70ebbf5d12..937bca3dd0f75 100644 --- a/impeller/renderer/backend/metal/render_pass_mtl.mm +++ b/impeller/renderer/backend/metal/render_pass_mtl.mm @@ -243,7 +243,7 @@ bool SetBuffer(ShaderStage stage, } return true; } - buffers_map[index] = {buffer, offset}; + buffers_map[index] = {buffer, static_cast(offset)}; switch (stage) { case ShaderStage::kVertex: [encoder_ setVertexBuffer:buffer offset:offset atIndex:index]; From c89d49fd993ec7c4b0247e228f3512550e799870 Mon Sep 17 00:00:00 2001 From: Jim Graham Date: Tue, 15 Mar 2022 13:47:09 -0700 Subject: [PATCH 351/433] Use new DlColorSource objects (#73) --- .../display_list/display_list_dispatcher.cc | 62 +++++++++---------- .../display_list/display_list_dispatcher.h | 2 +- 2 files changed, 32 insertions(+), 32 deletions(-) diff --git a/impeller/display_list/display_list_dispatcher.cc b/impeller/display_list/display_list_dispatcher.cc index fef486e321270..3ae258e025835 100644 --- a/impeller/display_list/display_list_dispatcher.cc +++ b/impeller/display_list/display_list_dispatcher.cc @@ -9,7 +9,6 @@ #include "impeller/geometry/path_builder.h" #include "impeller/typographer/backends/skia/text_frame_skia.h" #include "third_party/skia/include/core/SkColor.h" -#include "third_party/skia/include/core/SkShader.h" namespace impeller { @@ -90,40 +89,41 @@ static Color ToColor(const SkColor& color) { } // |flutter::Dispatcher| -void DisplayListDispatcher::setShader(sk_sp shader) { - if (!shader) { +void DisplayListDispatcher::setColorSource(const flutter::DlColorSource* source) { + if (!source) { + paint_.contents = nullptr; return; } - { - SkShader::GradientInfo info = {}; - constexpr auto kColorsArrayCount = 2u; - info.fColorCount = kColorsArrayCount; - SkColor sk_colors[kColorsArrayCount]; - info.fColors = sk_colors; - auto gradient_type = shader->asAGradient(&info); - switch (gradient_type) { - case SkShader::kLinear_GradientType: { - auto contents = std::make_shared(); - contents->SetEndPoints(ToPoint(info.fPoint[0]), - ToPoint(info.fPoint[1])); - std::vector colors; - for (auto i = 0; i < info.fColorCount; i++) { - colors.emplace_back(ToColor(sk_colors[i])); - } - contents->SetColors(std::move(colors)); - paint_.contents = std::move(contents); - return; - } break; - case SkShader::kNone_GradientType: - case SkShader::kColor_GradientType: - case SkShader::kRadial_GradientType: - case SkShader::kSweep_GradientType: - case SkShader::kConical_GradientType: - default: - UNIMPLEMENTED; - break; + switch(source->type()) { + case flutter::DlColorSourceType::kColor: { + const flutter::DlColorColorSource* color = source->asColor(); + paint_.contents = nullptr; + setColor(color->color()); + FML_DCHECK(color); + return; } + case flutter::DlColorSourceType::kLinearGradient: { + const flutter::DlLinearGradientColorSource* linear = source->asLinearGradient(); + FML_DCHECK(linear); + auto contents = std::make_shared(); + contents->SetEndPoints(ToPoint(linear->p0()), + ToPoint(linear->p1())); + std::vector colors; + for (auto i = 0; i < linear->stop_count(); i++) { + colors.emplace_back(ToColor(linear->colors()[i])); + } + contents->SetColors(std::move(colors)); + paint_.contents = std::move(contents); + return; + } + case flutter::DlColorSourceType::kImage: + case flutter::DlColorSourceType::kRadialGradient: + case flutter::DlColorSourceType::kConicalGradient: + case flutter::DlColorSourceType::kSweepGradient: + case flutter::DlColorSourceType::kUnknown: + UNIMPLEMENTED; + break; } // Needs https://github.com/flutter/flutter/issues/95434 diff --git a/impeller/display_list/display_list_dispatcher.h b/impeller/display_list/display_list_dispatcher.h index 8995a3d803493..660ab1c07ecda 100644 --- a/impeller/display_list/display_list_dispatcher.h +++ b/impeller/display_list/display_list_dispatcher.h @@ -45,7 +45,7 @@ class DisplayListDispatcher final : public flutter::Dispatcher { void setStrokeJoin(SkPaint::Join join) override; // |flutter::Dispatcher| - void setShader(sk_sp shader) override; + void setColorSource(const flutter::DlColorSource* source) override; // |flutter::Dispatcher| void setColorFilter(const flutter::DlColorFilter* filter) override; From 9559a0087fe6ad694c727212530cd8d33d74f557 Mon Sep 17 00:00:00 2001 From: Jim Graham Date: Tue, 15 Mar 2022 13:55:30 -0700 Subject: [PATCH 352/433] update the linear gradient code to the new accessor names (#80) --- impeller/display_list/display_list_dispatcher.cc | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/impeller/display_list/display_list_dispatcher.cc b/impeller/display_list/display_list_dispatcher.cc index 3ae258e025835..186fefdbbb3e2 100644 --- a/impeller/display_list/display_list_dispatcher.cc +++ b/impeller/display_list/display_list_dispatcher.cc @@ -107,8 +107,8 @@ void DisplayListDispatcher::setColorSource(const flutter::DlColorSource* source) const flutter::DlLinearGradientColorSource* linear = source->asLinearGradient(); FML_DCHECK(linear); auto contents = std::make_shared(); - contents->SetEndPoints(ToPoint(linear->p0()), - ToPoint(linear->p1())); + contents->SetEndPoints(ToPoint(linear->start_point()), + ToPoint(linear->end_point())); std::vector colors; for (auto i = 0; i < linear->stop_count(); i++) { colors.emplace_back(ToColor(linear->colors()[i])); From 680530912df0fdcc315e5bfea449ac9185b3abbb Mon Sep 17 00:00:00 2001 From: Chinmay Garde Date: Wed, 16 Mar 2022 09:37:00 -0700 Subject: [PATCH 353/433] Impelement DisplayListDispatcher::transformReset. (#82) Added in https://github.com/flutter/engine/pull/32050 --- impeller/display_list/display_list_dispatcher.cc | 5 +++++ impeller/display_list/display_list_dispatcher.h | 3 +++ 2 files changed, 8 insertions(+) diff --git a/impeller/display_list/display_list_dispatcher.cc b/impeller/display_list/display_list_dispatcher.cc index 186fefdbbb3e2..01b78197c9d1b 100644 --- a/impeller/display_list/display_list_dispatcher.cc +++ b/impeller/display_list/display_list_dispatcher.cc @@ -281,6 +281,11 @@ void DisplayListDispatcher::transformFullPerspective(SkScalar mxx, canvas_.Transform(xformation); } +// |flutter::Dispatcher| +void DisplayListDispatcher::transformReset() { + canvas_.ResetTransform(); +} + static Rect ToRect(const SkRect& rect) { return Rect::MakeLTRB(rect.fLeft, rect.fTop, rect.fRight, rect.fBottom); } diff --git a/impeller/display_list/display_list_dispatcher.h b/impeller/display_list/display_list_dispatcher.h index 660ab1c07ecda..4df89468eaa66 100644 --- a/impeller/display_list/display_list_dispatcher.h +++ b/impeller/display_list/display_list_dispatcher.h @@ -116,6 +116,9 @@ class DisplayListDispatcher final : public flutter::Dispatcher { SkScalar mwz, SkScalar mwt) override; + // |flutter::Dispatcher| + void transformReset() override; + // |flutter::Dispatcher| void clipRect(const SkRect& rect, SkClipOp clip_op, bool is_aa) override; From 7c16c5d358e7dc96eae09fb36b24c1c487158845 Mon Sep 17 00:00:00 2001 From: Brandon DeRosier Date: Wed, 16 Mar 2022 16:47:08 -0700 Subject: [PATCH 354/433] Don't use `Add[Thing]` operations when emulating Skia path components (#86) --- .../display_list/display_list_dispatcher.cc | 33 +++++++++---------- 1 file changed, 16 insertions(+), 17 deletions(-) diff --git a/impeller/display_list/display_list_dispatcher.cc b/impeller/display_list/display_list_dispatcher.cc index 01b78197c9d1b..f5e5a39bd98e8 100644 --- a/impeller/display_list/display_list_dispatcher.cc +++ b/impeller/display_list/display_list_dispatcher.cc @@ -89,13 +89,14 @@ static Color ToColor(const SkColor& color) { } // |flutter::Dispatcher| -void DisplayListDispatcher::setColorSource(const flutter::DlColorSource* source) { +void DisplayListDispatcher::setColorSource( + const flutter::DlColorSource* source) { if (!source) { paint_.contents = nullptr; return; } - switch(source->type()) { + switch (source->type()) { case flutter::DlColorSourceType::kColor: { const flutter::DlColorColorSource* color = source->asColor(); paint_.contents = nullptr; @@ -104,7 +105,8 @@ void DisplayListDispatcher::setColorSource(const flutter::DlColorSource* source) return; } case flutter::DlColorSourceType::kLinearGradient: { - const flutter::DlLinearGradientColorSource* linear = source->asLinearGradient(); + const flutter::DlLinearGradientColorSource* linear = + source->asLinearGradient(); FML_DCHECK(linear); auto contents = std::make_shared(); contents->SetEndPoints(ToPoint(linear->start_point()), @@ -327,13 +329,13 @@ static Path ToPath(const SkPath& path) { builder.MoveTo(ToPoint(data.points[0])); break; case SkPath::kLine_Verb: - builder.AddLine(ToPoint(data.points[0]), ToPoint(data.points[1])); + builder.LineTo(ToPoint(data.points[0])); + builder.LineTo(ToPoint(data.points[1])); break; case SkPath::kQuad_Verb: - builder.AddQuadraticCurve(ToPoint(data.points[0]), // p1 - ToPoint(data.points[1]), // cp - ToPoint(data.points[2]) // p2 - ); + builder.LineTo(ToPoint(data.points[0])); + builder.QuadraticCurveTo(ToPoint(data.points[1]), + ToPoint(data.points[2])); break; case SkPath::kConic_Verb: { constexpr auto kPow2 = 1; // Only works for sweeps up to 90 degrees. @@ -352,18 +354,15 @@ static Path ToPath(const SkPath& path) { curve_index < curve_count; // curve_index++, point_index += 2 // ) { - builder.AddQuadraticCurve(ToPoint(points[point_index + 0]), // p1 - ToPoint(points[point_index + 1]), // cp - ToPoint(points[point_index + 2]) // p2 - ); + builder.LineTo(ToPoint(points[point_index + 0])); + builder.QuadraticCurveTo(ToPoint(points[point_index + 1]), + ToPoint(points[point_index + 2])); } } break; case SkPath::kCubic_Verb: - builder.AddCubicCurve(ToPoint(data.points[0]), // p1 - ToPoint(data.points[1]), // cp1 - ToPoint(data.points[2]), // cp2 - ToPoint(data.points[3]) // p2 - ); + builder.LineTo(ToPoint(data.points[0])); + builder.CubicCurveTo(ToPoint(data.points[1]), ToPoint(data.points[2]), + ToPoint(data.points[3])); break; case SkPath::kClose_Verb: builder.Close(); From c645362ff295fced9572e404558acb9001023c7b Mon Sep 17 00:00:00 2001 From: Brandon DeRosier Date: Thu, 17 Mar 2022 10:23:03 -0700 Subject: [PATCH 355/433] Transform clips by the entity's transform (#87) --- impeller/aiks/aiks_unittests.cc | 21 +++++++++++++++++++++ impeller/entity/contents/clip_contents.cc | 3 ++- 2 files changed, 23 insertions(+), 1 deletion(-) diff --git a/impeller/aiks/aiks_unittests.cc b/impeller/aiks/aiks_unittests.cc index e8ab4e8b8d085..a16e727c5a181 100644 --- a/impeller/aiks/aiks_unittests.cc +++ b/impeller/aiks/aiks_unittests.cc @@ -2,6 +2,7 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. +#include #include "flutter/testing/testing.h" #include "impeller/aiks/aiks_playground.h" #include "impeller/aiks/canvas.h" @@ -122,6 +123,26 @@ TEST_F(AiksTest, CanRenderNestedClips) { ASSERT_TRUE(OpenPlaygroundHere(canvas.EndRecordingAsPicture())); } +TEST_F(AiksTest, ClipsUseCurrentTransform) { + std::array colors = {Color::White(), Color::Black(), + Color::SkyBlue(), Color::Red(), + Color::Yellow()}; + Canvas canvas; + Paint paint; + + canvas.Translate(Vector3(300, 300)); + for (int i = 0; i < 15; i++) { + canvas.Translate(-Vector3(300, 300)); + canvas.Scale(Vector3(0.8, 0.8)); + canvas.Translate(Vector3(300, 300)); + + paint.color = colors[i % colors.size()]; + canvas.ClipPath(PathBuilder{}.AddCircle({0, 0}, 300).TakePath()); + canvas.DrawRect(Rect(-300, -300, 600, 600), paint); + } + ASSERT_TRUE(OpenPlaygroundHere(canvas.EndRecordingAsPicture())); +} + TEST_F(AiksTest, CanSaveLayerStandalone) { Canvas canvas; diff --git a/impeller/entity/contents/clip_contents.cc b/impeller/entity/contents/clip_contents.cc index 07921a3356799..df82089264cff 100644 --- a/impeller/entity/contents/clip_contents.cc +++ b/impeller/entity/contents/clip_contents.cc @@ -35,7 +35,8 @@ bool ClipContents::Render(const ContentContext& renderer, VS::FrameInfo info; // The color really doesn't matter. info.color = Color::SkyBlue(); - info.mvp = Matrix::MakeOrthographic(pass.GetRenderTargetSize()); + info.mvp = Matrix::MakeOrthographic(pass.GetRenderTargetSize()) * + entity.GetTransformation(); VS::BindFrameInfo(cmd, pass.GetTransientsBuffer().EmplaceUniform(info)); From 65fc02e00f2359c8214368a86ddefd1626c7ca8c Mon Sep 17 00:00:00 2001 From: Brandon DeRosier Date: Thu, 17 Mar 2022 10:51:28 -0700 Subject: [PATCH 356/433] Add blend filter support for advanced blends (#81) --- impeller/entity/BUILD.gn | 4 + impeller/entity/contents/content_context.cc | 4 + impeller/entity/contents/content_context.h | 38 +++- impeller/entity/contents/filter_contents.cc | 180 ++++++++++++++++-- impeller/entity/contents/filter_contents.h | 10 +- impeller/entity/entity.h | 16 +- impeller/entity/entity_unittests.cc | 10 +- impeller/entity/shaders/texture_blend.frag | 13 ++ impeller/entity/shaders/texture_blend.vert | 17 ++ .../entity/shaders/texture_blend_screen.frag | 16 ++ .../entity/shaders/texture_blend_screen.vert | 17 ++ 11 files changed, 298 insertions(+), 27 deletions(-) create mode 100644 impeller/entity/shaders/texture_blend.frag create mode 100644 impeller/entity/shaders/texture_blend.vert create mode 100644 impeller/entity/shaders/texture_blend_screen.frag create mode 100644 impeller/entity/shaders/texture_blend_screen.vert diff --git a/impeller/entity/BUILD.gn b/impeller/entity/BUILD.gn index 4e12078c2c999..9e5f951d407fd 100644 --- a/impeller/entity/BUILD.gn +++ b/impeller/entity/BUILD.gn @@ -14,6 +14,10 @@ impeller_shaders("entity_shaders") { "shaders/solid_fill.vert", "shaders/solid_stroke.frag", "shaders/solid_stroke.vert", + "shaders/texture_blend.frag", + "shaders/texture_blend.vert", + "shaders/texture_blend_screen.frag", + "shaders/texture_blend_screen.vert", "shaders/texture_fill.frag", "shaders/texture_fill.vert", "shaders/glyph_atlas.frag", diff --git a/impeller/entity/contents/content_context.cc b/impeller/entity/contents/content_context.cc index 0570a940b89a3..ad22a5e474a39 100644 --- a/impeller/entity/contents/content_context.cc +++ b/impeller/entity/contents/content_context.cc @@ -18,6 +18,10 @@ ContentContext::ContentContext(std::shared_ptr context) gradient_fill_pipelines_[{}] = std::make_unique(*context_); solid_fill_pipelines_[{}] = std::make_unique(*context_); + texture_blend_pipelines_[{}] = + std::make_unique(*context_); + texture_blend_screen_pipelines_[{}] = + std::make_unique(*context_); texture_pipelines_[{}] = std::make_unique(*context_); solid_stroke_pipelines_[{}] = std::make_unique(*context_); diff --git a/impeller/entity/contents/content_context.h b/impeller/entity/contents/content_context.h index 5003623f85c4c..7caa6aa01e104 100644 --- a/impeller/entity/contents/content_context.h +++ b/impeller/entity/contents/content_context.h @@ -9,6 +9,8 @@ #include "flutter/fml/hash_combine.h" #include "flutter/fml/macros.h" +#include "fml/logging.h" +#include "impeller/base/validation.h" #include "impeller/entity/entity.h" #include "impeller/entity/glyph_atlas.frag.h" #include "impeller/entity/glyph_atlas.vert.h" @@ -21,6 +23,10 @@ #include "impeller/entity/texture_fill.frag.h" #include "impeller/entity/texture_fill.vert.h" #include "impeller/renderer/formats.h" +#include "texture_blend.frag.h" +#include "texture_blend.vert.h" +#include "texture_blend_screen.frag.h" +#include "texture_blend_screen.vert.h" namespace impeller { @@ -28,6 +34,10 @@ using GradientFillPipeline = PipelineT; using SolidFillPipeline = PipelineT; +using TextureBlendPipeline = + PipelineT; +using TextureBlendScreenPipeline = + PipelineT; using TexturePipeline = PipelineT; using SolidStrokePipeline = @@ -75,6 +85,16 @@ class ContentContext { return GetPipeline(solid_fill_pipelines_, opts); } + std::shared_ptr GetTextureBlendPipeline( + ContentContextOptions opts) const { + return GetPipeline(texture_blend_pipelines_, opts); + } + + std::shared_ptr GetTextureBlendScreenPipeline( + ContentContextOptions opts) const { + return GetPipeline(texture_blend_screen_pipelines_, opts); + } + std::shared_ptr GetTexturePipeline( ContentContextOptions opts) const { return GetPipeline(texture_pipelines_, opts); @@ -115,6 +135,8 @@ class ContentContext { // map. mutable Variants gradient_fill_pipelines_; mutable Variants solid_fill_pipelines_; + mutable Variants texture_blend_pipelines_; + mutable Variants texture_blend_screen_pipelines_; mutable Variants texture_pipelines_; mutable Variants solid_stroke_pipelines_; mutable Variants clip_pipelines_; @@ -123,12 +145,24 @@ class ContentContext { static void ApplyOptionsToDescriptor(PipelineDescriptor& desc, const ContentContextOptions& options) { + auto blend_mode = options.blend_mode; + if (blend_mode > Entity::BlendMode::kLastPipelineBlendMode) { + VALIDATION_LOG << "Cannot use blend mode " + << static_cast(options.blend_mode) + << " as a pipeline blend."; + blend_mode = Entity::BlendMode::kSourceOver; + } + desc.SetSampleCount(options.sample_count); ColorAttachmentDescriptor color0 = *desc.GetColorAttachmentDescriptor(0u); color0.alpha_blend_op = BlendOperation::kAdd; color0.color_blend_op = BlendOperation::kAdd; - switch (options.blend_mode) { + + static_assert(Entity::BlendMode::kLastPipelineBlendMode == + Entity::BlendMode::kModulate); + + switch (blend_mode) { case Entity::BlendMode::kClear: color0.dst_alpha_blend_factor = BlendFactor::kZero; color0.dst_color_blend_factor = BlendFactor::kZero; @@ -214,6 +248,8 @@ class ContentContext { color0.src_alpha_blend_factor = BlendFactor::kZero; color0.src_color_blend_factor = BlendFactor::kZero; break; + default: + FML_UNREACHABLE(); } desc.SetColorAttachmentDescriptor(0u, std::move(color0)); } diff --git a/impeller/entity/contents/filter_contents.cc b/impeller/entity/contents/filter_contents.cc index d043a6b3334d1..d1c22cfeac04e 100644 --- a/impeller/entity/contents/filter_contents.cc +++ b/impeller/entity/contents/filter_contents.cc @@ -28,10 +28,33 @@ namespace impeller { std::shared_ptr FilterContents::MakeBlend( Entity::BlendMode blend_mode, InputTextures input_textures) { - auto blend = std::make_shared(); - blend->SetInputTextures(input_textures); - blend->SetBlendMode(blend_mode); - return blend; + if (blend_mode > Entity::BlendMode::kLastAdvancedBlendMode) { + VALIDATION_LOG << "Invalid blend mode " << static_cast(blend_mode) + << " passed to FilterContents::MakeBlend."; + return nullptr; + } + + if (input_textures.size() < 2 || + blend_mode <= Entity::BlendMode::kLastPipelineBlendMode) { + auto blend = std::make_shared(); + blend->SetInputTextures(input_textures); + blend->SetBlendMode(blend_mode); + return blend; + } + + if (blend_mode <= Entity::BlendMode::kLastAdvancedBlendMode) { + InputVariant blend = input_textures[0]; + for (auto in_i = input_textures.begin() + 1; in_i < input_textures.end(); + in_i++) { + auto new_blend = std::make_shared(); + new_blend->SetInputTextures({blend, *in_i}); + new_blend->SetBlendMode(blend_mode); + blend = new_blend; + } + return std::get>(blend); + } + + FML_UNREACHABLE(); } FilterContents::FilterContents() = default; @@ -150,20 +173,106 @@ ISize FilterContents::GetOutputSize() const { ******* BlendFilterContents ******************************************************************************/ -BlendFilterContents::BlendFilterContents() = default; +BlendFilterContents::BlendFilterContents() { + SetBlendMode(Entity::BlendMode::kSourceOver); +} BlendFilterContents::~BlendFilterContents() = default; +using PipelineProc = + std::shared_ptr (ContentContext::*)(ContentContextOptions) const; + +template +static void AdvancedBlendPass(std::shared_ptr input_d, + std::shared_ptr input_s, + std::shared_ptr sampler, + const ContentContext& renderer, + RenderPass& pass, + Command& cmd) {} + +template +static bool AdvancedBlend( + const std::vector>& input_textures, + const ContentContext& renderer, + RenderPass& pass, + PipelineProc pipeline_proc) { + if (input_textures.size() < 2) { + return false; + } + + auto& host_buffer = pass.GetTransientsBuffer(); + + VertexBufferBuilder vtx_builder; + vtx_builder.AddVertices({ + {Point(0, 0), Point(0, 0)}, + {Point(1, 0), Point(1, 0)}, + {Point(1, 1), Point(1, 1)}, + {Point(0, 0), Point(0, 0)}, + {Point(1, 1), Point(1, 1)}, + {Point(0, 1), Point(0, 1)}, + }); + auto vtx_buffer = vtx_builder.CreateVertexBuffer(host_buffer); + + typename VS::FrameInfo frame_info; + frame_info.mvp = Matrix::MakeOrthographic(ISize(1, 1)); + + auto uniform_view = host_buffer.EmplaceUniform(frame_info); + auto sampler = renderer.GetContext()->GetSamplerLibrary()->GetSampler({}); + + auto options = OptionsFromPass(pass); + options.blend_mode = Entity::BlendMode::kSource; + std::shared_ptr pipeline = + std::invoke(pipeline_proc, renderer, options); + + Command cmd; + cmd.label = "Advanced Blend Filter"; + cmd.BindVertices(vtx_buffer); + cmd.pipeline = std::move(pipeline); + VS::BindFrameInfo(cmd, uniform_view); + + FS::BindTextureSamplerDst(cmd, input_textures[0], sampler); + FS::BindTextureSamplerSrc(cmd, input_textures[1], sampler); + pass.AddCommand(cmd); + + return true; +} + void BlendFilterContents::SetBlendMode(Entity::BlendMode blend_mode) { + if (blend_mode > Entity::BlendMode::kLastAdvancedBlendMode) { + VALIDATION_LOG << "Invalid blend mode " << static_cast(blend_mode) + << " assigned to BlendFilterContents."; + } + blend_mode_ = blend_mode; + + if (blend_mode > Entity::BlendMode::kLastPipelineBlendMode) { + static_assert(Entity::BlendMode::kLastAdvancedBlendMode == + Entity::BlendMode::kScreen); + + switch (blend_mode) { + case Entity::BlendMode::kScreen: + advanced_blend_proc_ = + [](const std::vector>& input_textures, + const ContentContext& renderer, RenderPass& pass) { + PipelineProc p = &ContentContext::GetTextureBlendScreenPipeline; + return AdvancedBlend( + input_textures, renderer, pass, p); + }; + break; + default: + FML_UNREACHABLE(); + } + } } -bool BlendFilterContents::RenderFilter( +static bool BasicBlend( const std::vector>& input_textures, const ContentContext& renderer, - RenderPass& pass) const { - using VS = TexturePipeline::VertexShader; - using FS = TexturePipeline::FragmentShader; + RenderPass& pass, + Entity::BlendMode basic_blend) { + using VS = TextureBlendPipeline::VertexShader; + using FS = TextureBlendPipeline::FragmentShader; auto& host_buffer = pass.GetTransientsBuffer(); @@ -180,24 +289,63 @@ bool BlendFilterContents::RenderFilter( VS::FrameInfo frame_info; frame_info.mvp = Matrix::MakeOrthographic(ISize(1, 1)); - frame_info.alpha = 1; auto uniform_view = host_buffer.EmplaceUniform(frame_info); auto sampler = renderer.GetContext()->GetSamplerLibrary()->GetSampler({}); + // Draw the first texture using kSource. + Command cmd; - cmd.label = "Blend Filter"; - auto options = OptionsFromPass(pass); - options.blend_mode = blend_mode_; - cmd.pipeline = renderer.GetTexturePipeline(options); + cmd.label = "Basic Blend Filter"; cmd.BindVertices(vtx_buffer); + auto options = OptionsFromPass(pass); + options.blend_mode = Entity::BlendMode::kSource; + cmd.pipeline = renderer.GetTextureBlendPipeline(options); + FS::BindTextureSamplerSrc(cmd, input_textures[0], sampler); VS::BindFrameInfo(cmd, uniform_view); - for (const auto& texture : input_textures) { - FS::BindTextureSampler(cmd, texture, sampler); + pass.AddCommand(cmd); + + if (input_textures.size() < 2) { + return true; + } + + // Write subsequent textures using the selected blend mode. + + options.blend_mode = basic_blend; + cmd.pipeline = renderer.GetTextureBlendPipeline(options); + + for (auto texture_i = input_textures.begin() + 1; + texture_i < input_textures.end(); texture_i++) { + FS::BindTextureSamplerSrc(cmd, *texture_i, sampler); pass.AddCommand(cmd); } return true; } +bool BlendFilterContents::RenderFilter( + const std::vector>& input_textures, + const ContentContext& renderer, + RenderPass& pass) const { + if (input_textures.empty()) { + return true; + } + + if (input_textures.size() == 1) { + // Nothing to blend. + return BasicBlend(input_textures, renderer, pass, + Entity::BlendMode::kSource); + } + + if (blend_mode_ <= Entity::BlendMode::kLastPipelineBlendMode) { + return BasicBlend(input_textures, renderer, pass, blend_mode_); + } + + if (blend_mode_ <= Entity::BlendMode::kLastAdvancedBlendMode) { + return advanced_blend_proc_(input_textures, renderer, pass); + } + + FML_UNREACHABLE(); +} + } // namespace impeller diff --git a/impeller/entity/contents/filter_contents.h b/impeller/entity/contents/filter_contents.h index b1839cff4efbd..bcee31d0e55a6 100644 --- a/impeller/entity/contents/filter_contents.h +++ b/impeller/entity/contents/filter_contents.h @@ -14,6 +14,8 @@ namespace impeller { +class Pipeline; + /******************************************************************************* ******* FilterContents ******************************************************************************/ @@ -75,6 +77,11 @@ class FilterContents : public Contents { class BlendFilterContents : public FilterContents { public: + using AdvancedBlendProc = std::function>& input_textures, + const ContentContext& renderer, + RenderPass& pass)>; + BlendFilterContents(); ~BlendFilterContents() override; @@ -86,7 +93,8 @@ class BlendFilterContents : public FilterContents { const ContentContext& renderer, RenderPass& pass) const override; - Entity::BlendMode blend_mode_ = Entity::BlendMode::kSourceOver; + Entity::BlendMode blend_mode_; + AdvancedBlendProc advanced_blend_proc_; FML_DISALLOW_COPY_AND_ASSIGN(BlendFilterContents); }; diff --git a/impeller/entity/entity.h b/impeller/entity/entity.h index 36d965f36e28e..efe072f64dca6 100644 --- a/impeller/entity/entity.h +++ b/impeller/entity/entity.h @@ -4,6 +4,7 @@ #pragma once +#include #include "impeller/entity/contents/contents.h" #include "impeller/geometry/color.h" #include "impeller/geometry/matrix.h" @@ -18,10 +19,11 @@ class RenderPass; class Entity { public: - /// All pipeline blend mode presets assume that both the source (fragment - /// output) and destination (first color attachment) have colors with - /// premultiplied alpha. + /// All blend modes assume that both the source (fragment output) and + /// destination (first color attachment) have colors with premultiplied alpha. enum class BlendMode { + // The following blend modes are able to be used as pipeline blend modes or + // via `BlendFilterContents`. kClear, kSource, kDestination, @@ -36,6 +38,14 @@ class Entity { kXor, kPlus, kModulate, + + // The following blend modes use equations that are not available for + // pipelines on most graphics devices without extensions, and so they are + // only able to be used via `BlendFilterContents`. + kScreen, + + kLastPipelineBlendMode = kModulate, + kLastAdvancedBlendMode = kScreen, }; Entity(); diff --git a/impeller/entity/entity_unittests.cc b/impeller/entity/entity_unittests.cc index 87cf91a69056f..f20f811c26c77 100644 --- a/impeller/entity/entity_unittests.cc +++ b/impeller/entity/entity_unittests.cc @@ -657,13 +657,11 @@ TEST_F(EntityTest, Filters) { ASSERT_TRUE(bridge && boston && kalimba); auto callback = [&](ContentContext& context, RenderPass& pass) -> bool { - // Draws kalimba and overwrites it with boston. - auto blend0 = FilterContents::MakeBlend( - Entity::BlendMode::kSourceOver, {kalimba, boston}); + auto blend0 = FilterContents::MakeBlend(Entity::BlendMode::kModulate, + {kalimba, boston}); - // Adds bridge*3 to boston. - auto blend1 = FilterContents::MakeBlend( - Entity::BlendMode::kPlus, {bridge, bridge, blend0, bridge}); + auto blend1 = FilterContents::MakeBlend(Entity::BlendMode::kScreen, + {bridge, blend0, bridge, bridge}); Entity entity; entity.SetPath(PathBuilder{}.AddRect({100, 100, 300, 300}).TakePath()); diff --git a/impeller/entity/shaders/texture_blend.frag b/impeller/entity/shaders/texture_blend.frag new file mode 100644 index 0000000000000..debce522d6b43 --- /dev/null +++ b/impeller/entity/shaders/texture_blend.frag @@ -0,0 +1,13 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +uniform sampler2D texture_sampler_src; + +in vec2 v_texture_coords; + +out vec4 frag_color; + +void main() { + frag_color = texture(texture_sampler_src, v_texture_coords); +} diff --git a/impeller/entity/shaders/texture_blend.vert b/impeller/entity/shaders/texture_blend.vert new file mode 100644 index 0000000000000..daa30f5650a3f --- /dev/null +++ b/impeller/entity/shaders/texture_blend.vert @@ -0,0 +1,17 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +uniform FrameInfo { + mat4 mvp; +} frame_info; + +in vec2 vertices; +in vec2 texture_coords; + +out vec2 v_texture_coords; + +void main() { + gl_Position = frame_info.mvp * vec4(vertices, 0.0, 1.0); + v_texture_coords = texture_coords; +} diff --git a/impeller/entity/shaders/texture_blend_screen.frag b/impeller/entity/shaders/texture_blend_screen.frag new file mode 100644 index 0000000000000..4594592efff9c --- /dev/null +++ b/impeller/entity/shaders/texture_blend_screen.frag @@ -0,0 +1,16 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +uniform sampler2D texture_sampler_dst; +uniform sampler2D texture_sampler_src; + +in vec2 v_texture_coords; + +out vec4 frag_color; + +void main() { + vec4 dst = texture(texture_sampler_dst, v_texture_coords); + vec4 src = texture(texture_sampler_src, v_texture_coords); + frag_color = src + dst - src * dst; +} diff --git a/impeller/entity/shaders/texture_blend_screen.vert b/impeller/entity/shaders/texture_blend_screen.vert new file mode 100644 index 0000000000000..daa30f5650a3f --- /dev/null +++ b/impeller/entity/shaders/texture_blend_screen.vert @@ -0,0 +1,17 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +uniform FrameInfo { + mat4 mvp; +} frame_info; + +in vec2 vertices; +in vec2 texture_coords; + +out vec2 v_texture_coords; + +void main() { + gl_Position = frame_info.mvp * vec4(vertices, 0.0, 1.0); + v_texture_coords = texture_coords; +} From 97a9ba751f1dbe349ece953a39fd26dbee0f1a75 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 17 Mar 2022 10:51:40 -0700 Subject: [PATCH 357/433] Bump github/codeql-action from 1.1.4 to 1.1.5 (#83) Bumps [github/codeql-action](https://github.com/github/codeql-action) from 1.1.4 to 1.1.5. - [Release notes](https://github.com/github/codeql-action/releases) - [Changelog](https://github.com/github/codeql-action/blob/main/CHANGELOG.md) - [Commits](https://github.com/github/codeql-action/compare/f5d822707ee6e8fb81b04a5c0040b736da22e587...883476649888a9e8e219d5b2e6b789dc024f690c) --- updated-dependencies: - dependency-name: github/codeql-action dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- impeller/.github/workflows/scorecards-analysis.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/impeller/.github/workflows/scorecards-analysis.yml b/impeller/.github/workflows/scorecards-analysis.yml index 32c80cd247a21..0d979e781ad53 100644 --- a/impeller/.github/workflows/scorecards-analysis.yml +++ b/impeller/.github/workflows/scorecards-analysis.yml @@ -48,6 +48,6 @@ jobs: # Upload the results to GitHub's code scanning dashboard. - name: "Upload to code-scanning" - uses: github/codeql-action/upload-sarif@f5d822707ee6e8fb81b04a5c0040b736da22e587 + uses: github/codeql-action/upload-sarif@883476649888a9e8e219d5b2e6b789dc024f690c with: sarif_file: results.sarif From 8510dff52e37a7bd08b419f9511fbccefdc078d7 Mon Sep 17 00:00:00 2001 From: Brandon DeRosier Date: Thu, 17 Mar 2022 11:24:35 -0700 Subject: [PATCH 358/433] Add directional + 2D gaussian blur (#84) --- impeller/entity/BUILD.gn | 2 + impeller/entity/contents/content_context.cc | 2 + impeller/entity/contents/content_context.h | 18 ++- impeller/entity/contents/filter_contents.cc | 129 +++++++++++++++++++- impeller/entity/contents/filter_contents.h | 50 +++++++- impeller/entity/entity_unittests.cc | 44 +++++++ impeller/entity/shaders/gaussian_blur.frag | 49 ++++++++ impeller/entity/shaders/gaussian_blur.vert | 27 ++++ impeller/geometry/point.h | 1 + 9 files changed, 313 insertions(+), 9 deletions(-) create mode 100644 impeller/entity/shaders/gaussian_blur.frag create mode 100644 impeller/entity/shaders/gaussian_blur.vert diff --git a/impeller/entity/BUILD.gn b/impeller/entity/BUILD.gn index 9e5f951d407fd..3d0c58cbcad54 100644 --- a/impeller/entity/BUILD.gn +++ b/impeller/entity/BUILD.gn @@ -18,6 +18,8 @@ impeller_shaders("entity_shaders") { "shaders/texture_blend.vert", "shaders/texture_blend_screen.frag", "shaders/texture_blend_screen.vert", + "shaders/gaussian_blur.frag", + "shaders/gaussian_blur.vert", "shaders/texture_fill.frag", "shaders/texture_fill.vert", "shaders/glyph_atlas.frag", diff --git a/impeller/entity/contents/content_context.cc b/impeller/entity/contents/content_context.cc index ad22a5e474a39..ae42a008691e3 100644 --- a/impeller/entity/contents/content_context.cc +++ b/impeller/entity/contents/content_context.cc @@ -23,6 +23,8 @@ ContentContext::ContentContext(std::shared_ptr context) texture_blend_screen_pipelines_[{}] = std::make_unique(*context_); texture_pipelines_[{}] = std::make_unique(*context_); + gaussian_blur_pipelines_[{}] = + std::make_unique(*context_); solid_stroke_pipelines_[{}] = std::make_unique(*context_); glyph_atlas_pipelines_[{}] = std::make_unique(*context_); diff --git a/impeller/entity/contents/content_context.h b/impeller/entity/contents/content_context.h index 7caa6aa01e104..d5ff659d5ea6d 100644 --- a/impeller/entity/contents/content_context.h +++ b/impeller/entity/contents/content_context.h @@ -12,6 +12,8 @@ #include "fml/logging.h" #include "impeller/base/validation.h" #include "impeller/entity/entity.h" +#include "impeller/entity/gaussian_blur.frag.h" +#include "impeller/entity/gaussian_blur.vert.h" #include "impeller/entity/glyph_atlas.frag.h" #include "impeller/entity/glyph_atlas.vert.h" #include "impeller/entity/gradient_fill.frag.h" @@ -20,13 +22,13 @@ #include "impeller/entity/solid_fill.vert.h" #include "impeller/entity/solid_stroke.frag.h" #include "impeller/entity/solid_stroke.vert.h" +#include "impeller/entity/texture_blend.frag.h" +#include "impeller/entity/texture_blend.vert.h" +#include "impeller/entity/texture_blend_screen.frag.h" +#include "impeller/entity/texture_blend_screen.vert.h" #include "impeller/entity/texture_fill.frag.h" #include "impeller/entity/texture_fill.vert.h" #include "impeller/renderer/formats.h" -#include "texture_blend.frag.h" -#include "texture_blend.vert.h" -#include "texture_blend_screen.frag.h" -#include "texture_blend_screen.vert.h" namespace impeller { @@ -40,6 +42,8 @@ using TextureBlendScreenPipeline = PipelineT; using TexturePipeline = PipelineT; +using GaussianBlurPipeline = + PipelineT; using SolidStrokePipeline = PipelineT; using GlyphAtlasPipeline = @@ -100,6 +104,11 @@ class ContentContext { return GetPipeline(texture_pipelines_, opts); } + std::shared_ptr GetGaussianBlurPipeline( + ContentContextOptions opts) const { + return GetPipeline(gaussian_blur_pipelines_, opts); + } + std::shared_ptr GetSolidStrokePipeline( ContentContextOptions opts) const { return GetPipeline(solid_stroke_pipelines_, opts); @@ -138,6 +147,7 @@ class ContentContext { mutable Variants texture_blend_pipelines_; mutable Variants texture_blend_screen_pipelines_; mutable Variants texture_pipelines_; + mutable Variants gaussian_blur_pipelines_; mutable Variants solid_stroke_pipelines_; mutable Variants clip_pipelines_; mutable Variants clip_restoration_pipelines_; diff --git a/impeller/entity/contents/filter_contents.cc b/impeller/entity/contents/filter_contents.cc index d1c22cfeac04e..579318d51bf30 100644 --- a/impeller/entity/contents/filter_contents.cc +++ b/impeller/entity/contents/filter_contents.cc @@ -4,6 +4,8 @@ #include "filter_contents.h" +#include +#include #include #include #include @@ -16,7 +18,9 @@ #include "impeller/entity/entity.h" #include "impeller/geometry/path_builder.h" #include "impeller/renderer/command_buffer.h" +#include "impeller/renderer/formats.h" #include "impeller/renderer/render_pass.h" +#include "impeller/renderer/sampler_descriptor.h" #include "impeller/renderer/sampler_library.h" namespace impeller { @@ -57,6 +61,28 @@ std::shared_ptr FilterContents::MakeBlend( FML_UNREACHABLE(); } +std::shared_ptr FilterContents::MakeDirectionalGaussianBlur( + InputVariant input_texture, + Scalar radius, + Vector2 direction, + bool clip_border) { + auto blur = std::make_shared(); + blur->SetInputTextures({input_texture}); + blur->SetRadius(radius); + blur->SetDirection(direction); + blur->SetClipBorder(clip_border); + return blur; +} + +std::shared_ptr FilterContents::MakeGaussianBlur( + InputVariant input_texture, + Scalar radius, + bool clip_border) { + auto x_blur = MakeDirectionalGaussianBlur(input_texture, radius, Point(1, 0), + clip_border); + return MakeDirectionalGaussianBlur(x_blur, radius, Point(0, 1), false); +} + FilterContents::FilterContents() = default; FilterContents::~FilterContents() = default; @@ -90,7 +116,7 @@ std::optional> FilterContents::RenderFilterToTexture( const ContentContext& renderer, const Entity& entity, RenderPass& pass) const { - auto output_size = GetOutputSize(); + auto output_size = GetOutputSize(input_textures_); if (output_size.IsZero()) { return std::nullopt; } @@ -155,14 +181,17 @@ ISize FilterContents::GetOutputSize() const { if (input_textures_.empty()) { return {}; } + return GetOutputSize(input_textures_); +} +ISize FilterContents::GetOutputSize(const InputTextures& input_textures) const { if (auto filter = - std::get_if>(&input_textures_[0])) { - return filter->get()->GetOutputSize(); + std::get_if>(&input_textures[0])) { + return filter->get()->GetOutputSize(input_textures); } if (auto texture = - std::get_if>(&input_textures_[0])) { + std::get_if>(&input_textures[0])) { return texture->get()->GetSize(); } @@ -348,4 +377,96 @@ bool BlendFilterContents::RenderFilter( FML_UNREACHABLE(); } +/******************************************************************************* + ******* DirectionalGaussianBlurFilterContents + ******************************************************************************/ + +DirectionalGaussianBlurFilterContents::DirectionalGaussianBlurFilterContents() = + default; + +DirectionalGaussianBlurFilterContents:: + ~DirectionalGaussianBlurFilterContents() = default; + +void DirectionalGaussianBlurFilterContents::SetRadius(Scalar radius) { + radius_ = std::max(radius, 1e-3f); +} + +void DirectionalGaussianBlurFilterContents::SetDirection(Vector2 direction) { + direction_ = direction.Normalize(); +} + +void DirectionalGaussianBlurFilterContents::SetClipBorder(bool clip) { + clip_ = clip; +} + +bool DirectionalGaussianBlurFilterContents::RenderFilter( + const std::vector>& input_textures, + const ContentContext& renderer, + RenderPass& pass) const { + using VS = GaussianBlurPipeline::VertexShader; + using FS = GaussianBlurPipeline::FragmentShader; + + auto& host_buffer = pass.GetTransientsBuffer(); + + ISize size = FilterContents::GetOutputSize(); + Point uv_offset = clip_ ? (Point(radius_, radius_) / size) : Point(); + // LTRB + Scalar uv[4] = { + -uv_offset.x, + -uv_offset.y, + 1 + uv_offset.x, + 1 + uv_offset.y, + }; + + VertexBufferBuilder vtx_builder; + vtx_builder.AddVertices({ + {Point(0, 0), Point(uv[0], uv[1])}, + {Point(size.width, 0), Point(uv[2], uv[1])}, + {Point(size.width, size.height), Point(uv[2], uv[3])}, + {Point(0, 0), Point(uv[0], uv[1])}, + {Point(size.width, size.height), Point(uv[2], uv[3])}, + {Point(0, size.height), Point(uv[0], uv[3])}, + }); + auto vtx_buffer = vtx_builder.CreateVertexBuffer(host_buffer); + + VS::FrameInfo frame_info; + frame_info.mvp = Matrix::MakeOrthographic(size); + frame_info.texture_size = Point(size); + frame_info.blur_radius = radius_; + frame_info.blur_direction = direction_; + + auto uniform_view = host_buffer.EmplaceUniform(frame_info); + auto sampler = renderer.GetContext()->GetSamplerLibrary()->GetSampler({}); + + Command cmd; + cmd.label = "Gaussian Blur Filter"; + auto options = OptionsFromPass(pass); + options.blend_mode = Entity::BlendMode::kSource; + cmd.pipeline = renderer.GetGaussianBlurPipeline(options); + cmd.BindVertices(vtx_buffer); + VS::BindFrameInfo(cmd, uniform_view); + for (const auto& texture : input_textures) { + FS::BindTextureSampler(cmd, texture, sampler); + pass.AddCommand(cmd); + } + + return true; +} + +ISize DirectionalGaussianBlurFilterContents::GetOutputSize( + const InputTextures& input_textures) const { + ISize size; + if (auto filter = + std::get_if>(&input_textures[0])) { + size = filter->get()->GetOutputSize(); + } else if (auto texture = + std::get_if>(&input_textures[0])) { + size = texture->get()->GetSize(); + } else { + FML_UNREACHABLE(); + } + + return size + (clip_ ? ISize(radius_ * 2, radius_ * 2) : ISize()); +} + } // namespace impeller diff --git a/impeller/entity/contents/filter_contents.h b/impeller/entity/contents/filter_contents.h index bcee31d0e55a6..eca9c0aa1a783 100644 --- a/impeller/entity/contents/filter_contents.h +++ b/impeller/entity/contents/filter_contents.h @@ -30,6 +30,17 @@ class FilterContents : public Contents { Entity::BlendMode blend_mode, InputTextures input_textures); + static std::shared_ptr MakeDirectionalGaussianBlur( + InputVariant input_texture, + Scalar radius, + Vector2 direction, + bool clip_border = false); + + static std::shared_ptr MakeGaussianBlur( + InputVariant input_texture, + Scalar radius, + bool clip_border = false); + FilterContents(); ~FilterContents() override; @@ -54,6 +65,9 @@ class FilterContents : public Contents { const Entity& entity, RenderPass& pass) const; + /// @brief Fetch the size of the output texture. + ISize GetOutputSize() const; + private: /// @brief Takes a set of zero or more input textures and writes to an output /// texture. @@ -63,7 +77,7 @@ class FilterContents : public Contents { RenderPass& pass) const = 0; /// @brief Determines the size of the output texture. - virtual ISize GetOutputSize() const; + virtual ISize GetOutputSize(const InputTextures& input_textures) const; InputTextures input_textures_; Rect destination_; @@ -89,6 +103,7 @@ class BlendFilterContents : public FilterContents { void SetBlendMode(Entity::BlendMode blend_mode); private: + // |FilterContents| bool RenderFilter(const std::vector>& input_textures, const ContentContext& renderer, RenderPass& pass) const override; @@ -99,4 +114,37 @@ class BlendFilterContents : public FilterContents { FML_DISALLOW_COPY_AND_ASSIGN(BlendFilterContents); }; +/******************************************************************************* + ******* DirectionalGaussionBlurFilterContents + ******************************************************************************/ + +class DirectionalGaussianBlurFilterContents final : public FilterContents { + public: + DirectionalGaussianBlurFilterContents(); + + ~DirectionalGaussianBlurFilterContents() override; + + void SetRadius(Scalar radius); + + void SetDirection(Vector2 direction); + + void SetClipBorder(bool clip); + + private: + // |FilterContents| + bool RenderFilter(const std::vector>& input_textures, + const ContentContext& renderer, + RenderPass& pass) const override; + + // |FilterContents| + virtual ISize GetOutputSize( + const InputTextures& input_textures) const override; + + Scalar radius_ = 0; + Vector2 direction_; + bool clip_ = false; + + FML_DISALLOW_COPY_AND_ASSIGN(DirectionalGaussianBlurFilterContents); +}; + } // namespace impeller diff --git a/impeller/entity/entity_unittests.cc b/impeller/entity/entity_unittests.cc index f20f811c26c77..5c182cbf0c1d9 100644 --- a/impeller/entity/entity_unittests.cc +++ b/impeller/entity/entity_unittests.cc @@ -671,5 +671,49 @@ TEST_F(EntityTest, Filters) { ASSERT_TRUE(OpenPlaygroundHere(callback)); } +TEST_F(EntityTest, GaussianBlurFilter) { + auto bridge = CreateTextureForFixture("bay_bridge.jpg"); + auto boston = CreateTextureForFixture("boston.jpg"); + auto kalimba = CreateTextureForFixture("kalimba.jpg"); + ASSERT_TRUE(bridge && boston && kalimba); + + bool first_frame = true; + auto callback = [&](ContentContext& context, RenderPass& pass) -> bool { + if (first_frame) { + first_frame = false; + ImGui::SetNextWindowSize({450, 150}); + ImGui::SetNextWindowPos({200, 450}); + } + + ImGui::Begin("Controls"); + static float offset[2] = {500, 400}; + ImGui::SliderFloat2("Position offset", &offset[0], 0, 1000); + static float scale = 1; + ImGui::SliderFloat("Scale", &scale, 0, 1); + static float blur_radius = 20; + ImGui::SliderFloat("Blur radius", &blur_radius, 0, 200); + static bool clip_border = true; + ImGui::Checkbox("Clip", &clip_border); + + auto blend = FilterContents::MakeBlend(Entity::BlendMode::kPlus, + {boston, bridge, bridge}); + + auto blur = + FilterContents::MakeGaussianBlur(blend, blur_radius, clip_border); + + auto output_size = Size(blur->GetOutputSize()); + Rect bounds(Point(offset[0], offset[1]) - output_size / 2 * scale, + output_size * scale); + + ImGui::End(); + + Entity entity; + entity.SetPath(PathBuilder{}.AddRect(bounds).TakePath()); + entity.SetContents(blur); + return entity.Render(context, pass); + }; + ASSERT_TRUE(OpenPlaygroundHere(callback)); +} + } // namespace testing } // namespace impeller diff --git a/impeller/entity/shaders/gaussian_blur.frag b/impeller/entity/shaders/gaussian_blur.frag new file mode 100644 index 0000000000000..5edfafae7e970 --- /dev/null +++ b/impeller/entity/shaders/gaussian_blur.frag @@ -0,0 +1,49 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +// 1D (directional) gaussian blur. +// +// Paths for future optimization: +// * Remove the uv bounds check branch in SampleColor by adding optional +// support for SamplerAddressMode::ClampToBorder in the texture sampler. +// * Sample from higher mipmap levels when the blur radius is high enough. + +uniform sampler2D texture_sampler; + +in vec2 v_texture_coords; +in vec2 v_texture_size; +in vec2 v_blur_direction; +in float v_blur_radius; + +out vec4 frag_color; + +const float kTwoPi = 6.283185307179586; + +float Gaussian(float x) { + float stddev = v_blur_radius * 0.5; + float xnorm = x / stddev; + return exp(-0.5 * xnorm * xnorm) / (kTwoPi * stddev * stddev); +} + +// Emulate SamplerAddressMode::ClampToBorder. +vec4 SampleWithBorder(vec2 uv) { + if (uv.x > 0 && uv.y > 0 && uv.x < 1 && uv.y < 1) { + return texture(texture_sampler, uv); + } + return vec4(0); +} + +void main() { + vec2 blur_radius_uv = vec2(v_blur_radius) / v_texture_size; + + vec4 total = vec4(0); + float total_gaussian = 0; + for (float i = -v_blur_radius; i <= v_blur_radius; i++) { + float gaussian = Gaussian(i); + total_gaussian += gaussian; + total += gaussian * SampleWithBorder(v_texture_coords + + v_blur_direction * i / v_texture_size); + } + frag_color = total / total_gaussian; +} diff --git a/impeller/entity/shaders/gaussian_blur.vert b/impeller/entity/shaders/gaussian_blur.vert new file mode 100644 index 0000000000000..8d7d1db931de1 --- /dev/null +++ b/impeller/entity/shaders/gaussian_blur.vert @@ -0,0 +1,27 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +uniform FrameInfo { + mat4 mvp; + vec2 texture_size; + vec2 blur_direction; + float blur_radius; +} +frame_info; + +in vec2 vertices; +in vec2 texture_coords; + +out vec2 v_texture_coords; +out vec2 v_texture_size; +out vec2 v_blur_direction; +out float v_blur_radius; + +void main() { + gl_Position = frame_info.mvp * vec4(vertices, 0.0, 1.0); + v_texture_coords = texture_coords; + v_texture_size = frame_info.texture_size; + v_blur_direction = frame_info.blur_direction; + v_blur_radius = frame_info.blur_radius; +} diff --git a/impeller/geometry/point.h b/impeller/geometry/point.h index c334993c2de68..194bc1815d1df 100644 --- a/impeller/geometry/point.h +++ b/impeller/geometry/point.h @@ -269,6 +269,7 @@ constexpr TPoint operator/(const TSize& s, const TPoint& p) { using Point = TPoint; using IPoint = TPoint; +using Vector2 = Point; } // namespace impeller From 19c47afba32f3b96513d306305b123d2751823d6 Mon Sep 17 00:00:00 2001 From: Chinmay Garde Date: Thu, 17 Mar 2022 11:59:59 -0700 Subject: [PATCH 359/433] Add ClearContents to apply specified contents to the entire render target. (#88) --- impeller/aiks/aiks_unittests.cc | 8 +++++ impeller/aiks/canvas.cc | 11 ++++++ impeller/aiks/canvas.h | 2 ++ .../display_list/display_list_dispatcher.cc | 7 ++-- impeller/entity/BUILD.gn | 2 ++ impeller/entity/contents/clear_contents.cc | 34 +++++++++++++++++++ impeller/entity/contents/clear_contents.h | 33 ++++++++++++++++++ 7 files changed, 95 insertions(+), 2 deletions(-) create mode 100644 impeller/entity/contents/clear_contents.cc create mode 100644 impeller/entity/contents/clear_contents.h diff --git a/impeller/aiks/aiks_unittests.cc b/impeller/aiks/aiks_unittests.cc index a16e727c5a181..3defa7f6d9b0a 100644 --- a/impeller/aiks/aiks_unittests.cc +++ b/impeller/aiks/aiks_unittests.cc @@ -390,5 +390,13 @@ TEST_F(AiksTest, CanRenderEmojiTextFrame) { ASSERT_TRUE(OpenPlaygroundHere(canvas.EndRecordingAsPicture())); } +TEST_F(AiksTest, CanDrawPaint) { + Paint paint; + paint.color = Color::MediumTurquoise(); + Canvas canvas; + canvas.DrawPaint(paint); + ASSERT_TRUE(OpenPlaygroundHere(canvas.EndRecordingAsPicture())); +} + } // namespace testing } // namespace impeller diff --git a/impeller/aiks/canvas.cc b/impeller/aiks/canvas.cc index 775d80a8eee19..c7c2e390c26dd 100644 --- a/impeller/aiks/canvas.cc +++ b/impeller/aiks/canvas.cc @@ -8,6 +8,7 @@ #include "flutter/fml/logging.h" #include "impeller/aiks/paint_pass_delegate.h" +#include "impeller/entity/contents/clear_contents.h" #include "impeller/entity/contents/clip_contents.h" #include "impeller/entity/contents/text_contents.h" #include "impeller/entity/contents/texture_contents.h" @@ -113,6 +114,16 @@ void Canvas::DrawPath(Path path, Paint paint) { GetCurrentPass().AddEntity(std::move(entity)); } +void Canvas::DrawPaint(Paint paint) { + Entity entity; + entity.SetTransformation(GetCurrentTransformation()); + entity.SetStencilDepth(GetStencilDepth()); + entity.SetContents( + std::make_shared(paint.CreateContentsForEntity())); + + GetCurrentPass().AddEntity(std::move(entity)); +} + void Canvas::DrawRect(Rect rect, Paint paint) { DrawPath(PathBuilder{}.AddRect(rect).TakePath(), std::move(paint)); } diff --git a/impeller/aiks/canvas.h b/impeller/aiks/canvas.h index 0dc6d810924b7..c407915953abf 100644 --- a/impeller/aiks/canvas.h +++ b/impeller/aiks/canvas.h @@ -59,6 +59,8 @@ class Canvas { void DrawPath(Path path, Paint paint); + void DrawPaint(Paint paint); + void DrawRect(Rect rect, Paint paint); void DrawCircle(Point center, Scalar radius, Paint paint); diff --git a/impeller/display_list/display_list_dispatcher.cc b/impeller/display_list/display_list_dispatcher.cc index f5e5a39bd98e8..980eda1a8a4d0 100644 --- a/impeller/display_list/display_list_dispatcher.cc +++ b/impeller/display_list/display_list_dispatcher.cc @@ -397,12 +397,15 @@ void DisplayListDispatcher::clipPath(const SkPath& path, // |flutter::Dispatcher| void DisplayListDispatcher::drawColor(SkColor color, SkBlendMode mode) { - UNIMPLEMENTED; + Paint paint; + paint.color = ToColor(color); + FML_LOG(ERROR) << "Blend mode on drawColor ignored."; + canvas_.DrawPaint(paint); } // |flutter::Dispatcher| void DisplayListDispatcher::drawPaint() { - UNIMPLEMENTED; + canvas_.DrawPaint(paint_); } // |flutter::Dispatcher| diff --git a/impeller/entity/BUILD.gn b/impeller/entity/BUILD.gn index 3d0c58cbcad54..39e828ff5e03d 100644 --- a/impeller/entity/BUILD.gn +++ b/impeller/entity/BUILD.gn @@ -29,6 +29,8 @@ impeller_shaders("entity_shaders") { impeller_component("entity") { sources = [ + "contents/clear_contents.cc", + "contents/clear_contents.h", "contents/clip_contents.cc", "contents/clip_contents.h", "contents/content_context.cc", diff --git a/impeller/entity/contents/clear_contents.cc b/impeller/entity/contents/clear_contents.cc new file mode 100644 index 0000000000000..6222244f1b56c --- /dev/null +++ b/impeller/entity/contents/clear_contents.cc @@ -0,0 +1,34 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "impeller/entity/contents/clear_contents.h" + +#include "impeller/entity/entity.h" +#include "impeller/geometry/path_builder.h" +#include "impeller/renderer/render_pass.h" + +namespace impeller { + +ClearContents::ClearContents(std::shared_ptr contents) + : contents_(std::move(contents)) {} + +ClearContents::~ClearContents() = default; + +// |Contents| +bool ClearContents::Render(const ContentContext& renderer, + const Entity& entity, + RenderPass& pass) const { + if (contents_ == nullptr) { + return false; + } + // Instead of an entity that doesn't know its size because the render target + // size was unknown to it at construction time, create a copy but substitute + // the contents with the replacements. + Entity clear_entity = entity; + clear_entity.SetPath( + PathBuilder{}.AddRect(Size(pass.GetRenderTargetSize())).TakePath()); + return contents_->Render(renderer, clear_entity, pass); +} + +} // namespace impeller diff --git a/impeller/entity/contents/clear_contents.h b/impeller/entity/contents/clear_contents.h new file mode 100644 index 0000000000000..d151b544ca27a --- /dev/null +++ b/impeller/entity/contents/clear_contents.h @@ -0,0 +1,33 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#pragma once + +#include "flutter/fml/macros.h" +#include "impeller/entity/contents/contents.h" + +namespace impeller { + +//------------------------------------------------------------------------------ +/// @brief Disregard existing contents on the render target and use the +/// provided filter to render to the entire target. +/// +class ClearContents final : public Contents { + public: + ClearContents(std::shared_ptr contents); + + ~ClearContents(); + + // |Contents| + bool Render(const ContentContext& renderer, + const Entity& entity, + RenderPass& pass) const override; + + private: + std::shared_ptr contents_; + + FML_DISALLOW_COPY_AND_ASSIGN(ClearContents); +}; + +} // namespace impeller From 769306b9dd9538ce2783c4a36c7caa264322824b Mon Sep 17 00:00:00 2001 From: Brandon DeRosier Date: Thu, 17 Mar 2022 14:14:44 -0700 Subject: [PATCH 360/433] Add blend mode setting to Entity, Aiks paint, and the dispatcher (#89) --- impeller/aiks/aiks_unittests.cc | 19 ++++++ impeller/aiks/canvas.cc | 4 ++ impeller/aiks/paint.h | 2 + .../display_list/display_list_dispatcher.cc | 65 ++++++++++++++++++- impeller/entity/contents/clip_contents.cc | 6 +- impeller/entity/contents/contents.cc | 8 +++ impeller/entity/contents/contents.h | 3 + .../contents/linear_gradient_contents.cc | 3 +- .../entity/contents/solid_color_contents.cc | 3 +- .../entity/contents/solid_stroke_contents.cc | 3 +- impeller/entity/contents/text_contents.cc | 3 +- impeller/entity/contents/texture_contents.cc | 3 +- impeller/entity/entity.cc | 13 ++++ impeller/entity/entity.h | 5 ++ impeller/entity/entity_unittests.cc | 7 ++ 15 files changed, 139 insertions(+), 8 deletions(-) diff --git a/impeller/aiks/aiks_unittests.cc b/impeller/aiks/aiks_unittests.cc index 3defa7f6d9b0a..9b9acfa73eb6e 100644 --- a/impeller/aiks/aiks_unittests.cc +++ b/impeller/aiks/aiks_unittests.cc @@ -398,5 +398,24 @@ TEST_F(AiksTest, CanDrawPaint) { ASSERT_TRUE(OpenPlaygroundHere(canvas.EndRecordingAsPicture())); } +TEST_F(AiksTest, PaintBlendModeIsRespected) { + Paint paint; + Canvas canvas; + // Default is kSourceOver. + paint.color = Color(1, 0, 0, 0.5); + canvas.DrawCircle(Point(150, 200), 100, paint); + paint.color = Color(0, 1, 0, 0.5); + canvas.DrawCircle(Point(250, 200), 100, paint); + + paint.blend_mode = Entity::BlendMode::kPlus; + paint.color = Color::Red(); + canvas.DrawCircle(Point(450, 250), 100, paint); + paint.color = Color::Green(); + canvas.DrawCircle(Point(550, 250), 100, paint); + paint.color = Color::Blue(); + canvas.DrawCircle(Point(500, 150), 100, paint); + ASSERT_TRUE(OpenPlaygroundHere(canvas.EndRecordingAsPicture())); +} + } // namespace testing } // namespace impeller diff --git a/impeller/aiks/canvas.cc b/impeller/aiks/canvas.cc index c7c2e390c26dd..5eb1d1b12f682 100644 --- a/impeller/aiks/canvas.cc +++ b/impeller/aiks/canvas.cc @@ -109,6 +109,7 @@ void Canvas::DrawPath(Path path, Paint paint) { entity.SetTransformation(GetCurrentTransformation()); entity.SetPath(std::move(path)); entity.SetStencilDepth(GetStencilDepth()); + entity.SetBlendMode(paint.blend_mode); entity.SetContents(paint.CreateContentsForEntity()); GetCurrentPass().AddEntity(std::move(entity)); @@ -118,6 +119,7 @@ void Canvas::DrawPaint(Paint paint) { Entity entity; entity.SetTransformation(GetCurrentTransformation()); entity.SetStencilDepth(GetStencilDepth()); + entity.SetBlendMode(paint.blend_mode); entity.SetContents( std::make_shared(paint.CreateContentsForEntity())); @@ -227,6 +229,7 @@ void Canvas::DrawImageRect(std::shared_ptr image, Entity entity; entity.SetPath(PathBuilder{}.AddRect(dest).TakePath()); + entity.SetBlendMode(paint.blend_mode); entity.SetContents(contents); entity.SetTransformation(GetCurrentTransformation()); @@ -284,6 +287,7 @@ void Canvas::DrawTextFrame(TextFrame text_frame, Matrix::MakeTranslation(position)); entity.SetPath({}); entity.SetStencilDepth(GetStencilDepth()); + entity.SetBlendMode(paint.blend_mode); entity.SetContents(std::move(text_contents)); GetCurrentPass().AddEntity(std::move(entity)); diff --git a/impeller/aiks/paint.h b/impeller/aiks/paint.h index 35ef71b7fccd1..a559c4c1a9fa9 100644 --- a/impeller/aiks/paint.h +++ b/impeller/aiks/paint.h @@ -8,6 +8,7 @@ #include "flutter/fml/macros.h" #include "impeller/entity/contents/contents.h" +#include "impeller/entity/entity.h" #include "impeller/geometry/color.h" namespace impeller { @@ -21,6 +22,7 @@ struct Paint { Color color = Color::Black(); Scalar stroke_width = 0.0; Style style = Style::kFill; + Entity::BlendMode blend_mode = Entity::BlendMode::kSourceOver; std::shared_ptr contents; std::shared_ptr CreateContentsForEntity() const; diff --git a/impeller/display_list/display_list_dispatcher.cc b/impeller/display_list/display_list_dispatcher.cc index 980eda1a8a4d0..49ee4a5286202 100644 --- a/impeller/display_list/display_list_dispatcher.cc +++ b/impeller/display_list/display_list_dispatcher.cc @@ -6,6 +6,7 @@ #include "flutter/fml/trace_event.h" #include "impeller/entity/contents/linear_gradient_contents.h" +#include "impeller/entity/entity.h" #include "impeller/geometry/path_builder.h" #include "impeller/typographer/backends/skia/text_frame_skia.h" #include "third_party/skia/include/core/SkColor.h" @@ -158,7 +159,69 @@ void DisplayListDispatcher::setInvertColors(bool invert) { // |flutter::Dispatcher| void DisplayListDispatcher::setBlendMode(SkBlendMode mode) { - UNIMPLEMENTED; + switch (mode) { + case SkBlendMode::kClear: + paint_.blend_mode = Entity::BlendMode::kClear; + break; + case SkBlendMode::kSrc: + paint_.blend_mode = Entity::BlendMode::kSource; + break; + case SkBlendMode::kDst: + paint_.blend_mode = Entity::BlendMode::kDestination; + break; + case SkBlendMode::kSrcOver: + paint_.blend_mode = Entity::BlendMode::kSourceOver; + break; + case SkBlendMode::kDstOver: + paint_.blend_mode = Entity::BlendMode::kDestinationOver; + break; + case SkBlendMode::kSrcIn: + paint_.blend_mode = Entity::BlendMode::kSourceIn; + break; + case SkBlendMode::kDstIn: + paint_.blend_mode = Entity::BlendMode::kDestinationIn; + break; + case SkBlendMode::kSrcOut: + paint_.blend_mode = Entity::BlendMode::kSourceOut; + break; + case SkBlendMode::kDstOut: + paint_.blend_mode = Entity::BlendMode::kDestinationOut; + break; + case SkBlendMode::kSrcATop: + paint_.blend_mode = Entity::BlendMode::kSourceATop; + break; + case SkBlendMode::kDstATop: + paint_.blend_mode = Entity::BlendMode::kDestinationATop; + break; + case SkBlendMode::kXor: + paint_.blend_mode = Entity::BlendMode::kXor; + break; + case SkBlendMode::kPlus: + paint_.blend_mode = Entity::BlendMode::kPlus; + break; + case SkBlendMode::kModulate: + paint_.blend_mode = Entity::BlendMode::kModulate; + break; + case SkBlendMode::kScreen: + case SkBlendMode::kOverlay: + case SkBlendMode::kDarken: + case SkBlendMode::kLighten: + case SkBlendMode::kColorDodge: + case SkBlendMode::kColorBurn: + case SkBlendMode::kHardLight: + case SkBlendMode::kSoftLight: + case SkBlendMode::kDifference: + case SkBlendMode::kExclusion: + case SkBlendMode::kMultiply: + case SkBlendMode::kHue: + case SkBlendMode::kSaturation: + case SkBlendMode::kColor: + case SkBlendMode::kLuminosity: + // Non-pipeline-friendly blend modes are not supported by setBlendMode + // yet. + UNIMPLEMENTED; + break; + } } // |flutter::Dispatcher| diff --git a/impeller/entity/contents/clip_contents.cc b/impeller/entity/contents/clip_contents.cc index df82089264cff..864ccc855fbdd 100644 --- a/impeller/entity/contents/clip_contents.cc +++ b/impeller/entity/contents/clip_contents.cc @@ -27,7 +27,8 @@ bool ClipContents::Render(const ContentContext& renderer, Command cmd; cmd.label = "Clip"; - cmd.pipeline = renderer.GetClipPipeline(OptionsFromPass(pass)); + cmd.pipeline = + renderer.GetClipPipeline(OptionsFromPassAndEntity(pass, entity)); cmd.stencil_reference = entity.GetStencilDepth(); cmd.BindVertices(SolidColorContents::CreateSolidFillVertices( entity.GetPath(), pass.GetTransientsBuffer())); @@ -59,7 +60,8 @@ bool ClipRestoreContents::Render(const ContentContext& renderer, Command cmd; cmd.label = "Clip Restore"; - cmd.pipeline = renderer.GetClipRestorePipeline(OptionsFromPass(pass)); + cmd.pipeline = + renderer.GetClipRestorePipeline(OptionsFromPassAndEntity(pass, entity)); cmd.stencil_reference = entity.GetStencilDepth(); // Create a rect that covers the whole render target. diff --git a/impeller/entity/contents/contents.cc b/impeller/entity/contents/contents.cc index 9969170852645..43116365d9e45 100644 --- a/impeller/entity/contents/contents.cc +++ b/impeller/entity/contents/contents.cc @@ -15,6 +15,14 @@ ContentContextOptions OptionsFromPass(const RenderPass& pass) { return opts; } +ContentContextOptions OptionsFromPassAndEntity(const RenderPass& pass, + const Entity& entity) { + ContentContextOptions opts; + opts.sample_count = pass.GetRenderTarget().GetSampleCount(); + opts.blend_mode = entity.GetBlendMode(); + return opts; +} + Contents::Contents() = default; Contents::~Contents() = default; diff --git a/impeller/entity/contents/contents.h b/impeller/entity/contents/contents.h index 8bc297736ba73..1f1d08b9290a1 100644 --- a/impeller/entity/contents/contents.h +++ b/impeller/entity/contents/contents.h @@ -20,6 +20,9 @@ class RenderPass; ContentContextOptions OptionsFromPass(const RenderPass& pass); +ContentContextOptions OptionsFromPassAndEntity(const RenderPass& pass, + const Entity& entity); + class Contents { public: Contents(); diff --git a/impeller/entity/contents/linear_gradient_contents.cc b/impeller/entity/contents/linear_gradient_contents.cc index bed837a8aa6df..10a2c08f86198 100644 --- a/impeller/entity/contents/linear_gradient_contents.cc +++ b/impeller/entity/contents/linear_gradient_contents.cc @@ -66,7 +66,8 @@ bool LinearGradientContents::Render(const ContentContext& renderer, Command cmd; cmd.label = "LinearGradientFill"; - cmd.pipeline = renderer.GetGradientFillPipeline(OptionsFromPass(pass)); + cmd.pipeline = + renderer.GetGradientFillPipeline(OptionsFromPassAndEntity(pass, entity)); cmd.stencil_reference = entity.GetStencilDepth(); cmd.BindVertices( vertices_builder.CreateVertexBuffer(pass.GetTransientsBuffer())); diff --git a/impeller/entity/contents/solid_color_contents.cc b/impeller/entity/contents/solid_color_contents.cc index 5d2d63dc19452..41b09b85842a9 100644 --- a/impeller/entity/contents/solid_color_contents.cc +++ b/impeller/entity/contents/solid_color_contents.cc @@ -54,7 +54,8 @@ bool SolidColorContents::Render(const ContentContext& renderer, Command cmd; cmd.label = "SolidFill"; - cmd.pipeline = renderer.GetSolidFillPipeline(OptionsFromPass(pass)); + cmd.pipeline = + renderer.GetSolidFillPipeline(OptionsFromPassAndEntity(pass, entity)); cmd.stencil_reference = entity.GetStencilDepth(); cmd.BindVertices( CreateSolidFillVertices(entity.GetPath(), pass.GetTransientsBuffer())); diff --git a/impeller/entity/contents/solid_stroke_contents.cc b/impeller/entity/contents/solid_stroke_contents.cc index 35f5cd2ba0cbb..592c7792c8b93 100644 --- a/impeller/entity/contents/solid_stroke_contents.cc +++ b/impeller/entity/contents/solid_stroke_contents.cc @@ -149,7 +149,8 @@ bool SolidStrokeContents::Render(const ContentContext& renderer, Command cmd; cmd.primitive_type = PrimitiveType::kTriangleStrip; cmd.label = "SolidStroke"; - cmd.pipeline = renderer.GetSolidStrokePipeline(OptionsFromPass(pass)); + cmd.pipeline = + renderer.GetSolidStrokePipeline(OptionsFromPassAndEntity(pass, entity)); cmd.stencil_reference = entity.GetStencilDepth(); cmd.BindVertices(CreateSolidStrokeVertices( entity.GetPath(), pass.GetTransientsBuffer(), cap_proc_, join_proc_, diff --git a/impeller/entity/contents/text_contents.cc b/impeller/entity/contents/text_contents.cc index 1abbd1f340166..c58855039f31d 100644 --- a/impeller/entity/contents/text_contents.cc +++ b/impeller/entity/contents/text_contents.cc @@ -48,7 +48,8 @@ bool TextContents::Render(const ContentContext& renderer, Command cmd; cmd.label = "Glyph"; cmd.primitive_type = PrimitiveType::kTriangle; - cmd.pipeline = renderer.GetGlyphAtlasPipeline(OptionsFromPass(pass)); + cmd.pipeline = + renderer.GetGlyphAtlasPipeline(OptionsFromPassAndEntity(pass, entity)); cmd.stencil_reference = entity.GetStencilDepth(); // Common vertex uniforms for all glyphs. diff --git a/impeller/entity/contents/texture_contents.cc b/impeller/entity/contents/texture_contents.cc index 79bea6651cb6b..e2397aaff6a20 100644 --- a/impeller/entity/contents/texture_contents.cc +++ b/impeller/entity/contents/texture_contents.cc @@ -91,7 +91,8 @@ bool TextureContents::Render(const ContentContext& renderer, Command cmd; cmd.label = "TextureFill"; - cmd.pipeline = renderer.GetTexturePipeline(OptionsFromPass(pass)); + cmd.pipeline = + renderer.GetTexturePipeline(OptionsFromPassAndEntity(pass, entity)); cmd.stencil_reference = entity.GetStencilDepth(); cmd.BindVertices(vertex_builder.CreateVertexBuffer(host_buffer)); VS::BindFrameInfo(cmd, host_buffer.EmplaceUniform(frame_info)); diff --git a/impeller/entity/entity.cc b/impeller/entity/entity.cc index 7f595024f989c..37e410960172c 100644 --- a/impeller/entity/entity.cc +++ b/impeller/entity/entity.cc @@ -4,6 +4,7 @@ #include "impeller/entity/entity.h" +#include "impeller/base/validation.h" #include "impeller/entity/contents/content_context.h" #include "impeller/renderer/render_pass.h" @@ -65,6 +66,18 @@ void Entity::IncrementStencilDepth(uint32_t increment) { stencil_depth_ += increment; } +void Entity::SetBlendMode(BlendMode blend_mode) { + if (blend_mode_ > BlendMode::kLastPipelineBlendMode) { + VALIDATION_LOG << "Non-pipeline blend modes are not supported by the " + "entity blend mode setting."; + } + blend_mode_ = blend_mode; +} + +Entity::BlendMode Entity::GetBlendMode() const { + return blend_mode_; +} + bool Entity::Render(const ContentContext& renderer, RenderPass& parent_pass) const { if (!contents_) { diff --git a/impeller/entity/entity.h b/impeller/entity/entity.h index efe072f64dca6..80e755e781f09 100644 --- a/impeller/entity/entity.h +++ b/impeller/entity/entity.h @@ -76,11 +76,16 @@ class Entity { uint32_t GetStencilDepth() const; + void SetBlendMode(BlendMode blend_mode); + + BlendMode GetBlendMode() const; + bool Render(const ContentContext& renderer, RenderPass& parent_pass) const; private: Matrix transformation_; std::shared_ptr contents_; + BlendMode blend_mode_ = BlendMode::kSourceOver; Path path_; uint32_t stencil_depth_ = 0u; bool adds_to_coverage_ = true; diff --git a/impeller/entity/entity_unittests.cc b/impeller/entity/entity_unittests.cc index 5c182cbf0c1d9..1208b8073d571 100644 --- a/impeller/entity/entity_unittests.cc +++ b/impeller/entity/entity_unittests.cc @@ -715,5 +715,12 @@ TEST_F(EntityTest, GaussianBlurFilter) { ASSERT_TRUE(OpenPlaygroundHere(callback)); } +TEST_F(EntityTest, SetBlendMode) { + Entity entity; + ASSERT_EQ(entity.GetBlendMode(), Entity::BlendMode::kSourceOver); + entity.SetBlendMode(Entity::BlendMode::kClear); + ASSERT_EQ(entity.GetBlendMode(), Entity::BlendMode::kClear); +} + } // namespace testing } // namespace impeller From 477c9c1db2df9c9619374adc4628449367751360 Mon Sep 17 00:00:00 2001 From: Chinmay Garde Date: Thu, 17 Mar 2022 15:14:11 -0700 Subject: [PATCH 361/433] Forward blend mode from the dispatchers drawColor call. (#90) --- .../display_list/display_list_dispatcher.cc | 71 +++++++++---------- 1 file changed, 35 insertions(+), 36 deletions(-) diff --git a/impeller/display_list/display_list_dispatcher.cc b/impeller/display_list/display_list_dispatcher.cc index 49ee4a5286202..58426b04297a3 100644 --- a/impeller/display_list/display_list_dispatcher.cc +++ b/impeller/display_list/display_list_dispatcher.cc @@ -4,6 +4,8 @@ #include "impeller/display_list/display_list_dispatcher.h" +#include + #include "flutter/fml/trace_event.h" #include "impeller/entity/contents/linear_gradient_contents.h" #include "impeller/entity/entity.h" @@ -157,51 +159,36 @@ void DisplayListDispatcher::setInvertColors(bool invert) { UNIMPLEMENTED; } -// |flutter::Dispatcher| -void DisplayListDispatcher::setBlendMode(SkBlendMode mode) { +std::optional ToBlendMode(SkBlendMode mode) { switch (mode) { case SkBlendMode::kClear: - paint_.blend_mode = Entity::BlendMode::kClear; - break; + return Entity::BlendMode::kClear; case SkBlendMode::kSrc: - paint_.blend_mode = Entity::BlendMode::kSource; - break; + return Entity::BlendMode::kSource; case SkBlendMode::kDst: - paint_.blend_mode = Entity::BlendMode::kDestination; - break; + return Entity::BlendMode::kDestination; case SkBlendMode::kSrcOver: - paint_.blend_mode = Entity::BlendMode::kSourceOver; - break; + return Entity::BlendMode::kSourceOver; case SkBlendMode::kDstOver: - paint_.blend_mode = Entity::BlendMode::kDestinationOver; - break; + return Entity::BlendMode::kDestinationOver; case SkBlendMode::kSrcIn: - paint_.blend_mode = Entity::BlendMode::kSourceIn; - break; + return Entity::BlendMode::kSourceIn; case SkBlendMode::kDstIn: - paint_.blend_mode = Entity::BlendMode::kDestinationIn; - break; + return Entity::BlendMode::kDestinationIn; case SkBlendMode::kSrcOut: - paint_.blend_mode = Entity::BlendMode::kSourceOut; - break; + return Entity::BlendMode::kSourceOut; case SkBlendMode::kDstOut: - paint_.blend_mode = Entity::BlendMode::kDestinationOut; - break; + return Entity::BlendMode::kDestinationOut; case SkBlendMode::kSrcATop: - paint_.blend_mode = Entity::BlendMode::kSourceATop; - break; + return Entity::BlendMode::kSourceATop; case SkBlendMode::kDstATop: - paint_.blend_mode = Entity::BlendMode::kDestinationATop; - break; + return Entity::BlendMode::kDestinationATop; case SkBlendMode::kXor: - paint_.blend_mode = Entity::BlendMode::kXor; - break; + return Entity::BlendMode::kXor; case SkBlendMode::kPlus: - paint_.blend_mode = Entity::BlendMode::kPlus; - break; + return Entity::BlendMode::kPlus; case SkBlendMode::kModulate: - paint_.blend_mode = Entity::BlendMode::kModulate; - break; + return Entity::BlendMode::kModulate; case SkBlendMode::kScreen: case SkBlendMode::kOverlay: case SkBlendMode::kDarken: @@ -217,10 +204,18 @@ void DisplayListDispatcher::setBlendMode(SkBlendMode mode) { case SkBlendMode::kSaturation: case SkBlendMode::kColor: case SkBlendMode::kLuminosity: - // Non-pipeline-friendly blend modes are not supported by setBlendMode - // yet. - UNIMPLEMENTED; - break; + return std::nullopt; + } + + return std::nullopt; +} + +// |flutter::Dispatcher| +void DisplayListDispatcher::setBlendMode(SkBlendMode sk_mode) { + if (auto mode = ToBlendMode(sk_mode); mode.has_value()) { + paint_.blend_mode = mode.value(); + } else { + UNIMPLEMENTED; } } @@ -459,10 +454,14 @@ void DisplayListDispatcher::clipPath(const SkPath& path, } // |flutter::Dispatcher| -void DisplayListDispatcher::drawColor(SkColor color, SkBlendMode mode) { +void DisplayListDispatcher::drawColor(SkColor color, SkBlendMode sk_mode) { Paint paint; paint.color = ToColor(color); - FML_LOG(ERROR) << "Blend mode on drawColor ignored."; + if (auto mode = ToBlendMode(sk_mode); mode.has_value()) { + paint.blend_mode = mode.value(); + } else { + FML_DLOG(ERROR) << "Unimplemented blend mode in " << __FUNCTION__; + } canvas_.DrawPaint(paint); } From 563656227ede7b3097f73d364e128aaf5568fb68 Mon Sep 17 00:00:00 2001 From: Brandon DeRosier Date: Fri, 18 Mar 2022 17:25:53 -0700 Subject: [PATCH 362/433] Apply Aiks transforms in the canvas space, not world space (#91) --- impeller/aiks/aiks_unittests.cc | 42 ++++++++++++++++++++++++++++++--- impeller/aiks/canvas.cc | 2 +- 2 files changed, 40 insertions(+), 4 deletions(-) diff --git a/impeller/aiks/aiks_unittests.cc b/impeller/aiks/aiks_unittests.cc index 9b9acfa73eb6e..738abc6651056 100644 --- a/impeller/aiks/aiks_unittests.cc +++ b/impeller/aiks/aiks_unittests.cc @@ -132,9 +132,7 @@ TEST_F(AiksTest, ClipsUseCurrentTransform) { canvas.Translate(Vector3(300, 300)); for (int i = 0; i < 15; i++) { - canvas.Translate(-Vector3(300, 300)); canvas.Scale(Vector3(0.8, 0.8)); - canvas.Translate(Vector3(300, 300)); paint.color = colors[i % colors.size()]; canvas.ClipPath(PathBuilder{}.AddCircle({0, 0}, 300).TakePath()); @@ -202,7 +200,7 @@ TEST_F(AiksTest, CanPerformSkew) { Paint red; red.color = Color::Red(); - canvas.Skew(10, 125); + canvas.Skew(2, 5); canvas.DrawRect(Rect::MakeXYWH(0, 0, 100, 100), red); ASSERT_TRUE(OpenPlaygroundHere(canvas.EndRecordingAsPicture())); @@ -417,5 +415,43 @@ TEST_F(AiksTest, PaintBlendModeIsRespected) { ASSERT_TRUE(OpenPlaygroundHere(canvas.EndRecordingAsPicture())); } +TEST_F(AiksTest, TransformMultipliesCorrectly) { + Canvas canvas; + ASSERT_MATRIX_NEAR(canvas.GetCurrentTransformation(), Matrix()); + + // clang-format off + canvas.Translate(Vector3(100, 200)); + ASSERT_MATRIX_NEAR( + canvas.GetCurrentTransformation(), + Matrix( 1, 0, 0, 0, + 0, 1, 0, 0, + 0, 0, 1, 0, + 100, 200, 0, 1)); + + canvas.Rotate(Radians(kPiOver2)); + ASSERT_MATRIX_NEAR( + canvas.GetCurrentTransformation(), + Matrix( 0, 1, 0, 0, + -1, 0, 0, 0, + 0, 0, 1, 0, + 100, 200, 0, 1)); + + canvas.Scale(Vector3(2, 3)); + ASSERT_MATRIX_NEAR( + canvas.GetCurrentTransformation(), + Matrix( 0, 2, 0, 0, + -3, 0, 0, 0, + 0, 0, 0, 0, + 100, 200, 0, 1)); + + canvas.Translate(Vector3(100, 200)); + ASSERT_MATRIX_NEAR( + canvas.GetCurrentTransformation(), + Matrix( 0, 2, 0, 0, + -3, 0, 0, 0, + 0, 0, 0, 0, + -500, 400, 0, 1)); +} + } // namespace testing } // namespace impeller diff --git a/impeller/aiks/canvas.cc b/impeller/aiks/canvas.cc index 5eb1d1b12f682..b77beb800b949 100644 --- a/impeller/aiks/canvas.cc +++ b/impeller/aiks/canvas.cc @@ -61,7 +61,7 @@ bool Canvas::Restore() { } void Canvas::Concat(const Matrix& xformation) { - xformation_stack_.back().xformation = xformation * GetCurrentTransformation(); + xformation_stack_.back().xformation = GetCurrentTransformation() * xformation; } void Canvas::ResetTransform() { From f9c352b094d39210321f5025e5307a66c683e387 Mon Sep 17 00:00:00 2001 From: JsouLiang <1129584401@qq.com> Date: Sat, 19 Mar 2022 21:12:15 +0800 Subject: [PATCH 363/433] Use new DlBlendMode object (#78) --- .../display_list/display_list_dispatcher.cc | 72 +++++++++---------- .../display_list/display_list_dispatcher.h | 9 +-- 2 files changed, 41 insertions(+), 40 deletions(-) diff --git a/impeller/display_list/display_list_dispatcher.cc b/impeller/display_list/display_list_dispatcher.cc index 58426b04297a3..46e23199f5e01 100644 --- a/impeller/display_list/display_list_dispatcher.cc +++ b/impeller/display_list/display_list_dispatcher.cc @@ -159,51 +159,51 @@ void DisplayListDispatcher::setInvertColors(bool invert) { UNIMPLEMENTED; } -std::optional ToBlendMode(SkBlendMode mode) { +std::optional ToBlendMode(flutter::DlBlendMode mode) { switch (mode) { - case SkBlendMode::kClear: + case flutter::DlBlendMode::kClear: return Entity::BlendMode::kClear; - case SkBlendMode::kSrc: + case flutter::DlBlendMode::kSrc: return Entity::BlendMode::kSource; - case SkBlendMode::kDst: + case flutter::DlBlendMode::kDst: return Entity::BlendMode::kDestination; - case SkBlendMode::kSrcOver: + case flutter::DlBlendMode::kSrcOver: return Entity::BlendMode::kSourceOver; - case SkBlendMode::kDstOver: + case flutter::DlBlendMode::kDstOver: return Entity::BlendMode::kDestinationOver; - case SkBlendMode::kSrcIn: + case flutter::DlBlendMode::kSrcIn: return Entity::BlendMode::kSourceIn; - case SkBlendMode::kDstIn: + case flutter::DlBlendMode::kDstIn: return Entity::BlendMode::kDestinationIn; - case SkBlendMode::kSrcOut: + case flutter::DlBlendMode::kSrcOut: return Entity::BlendMode::kSourceOut; - case SkBlendMode::kDstOut: + case flutter::DlBlendMode::kDstOut: return Entity::BlendMode::kDestinationOut; - case SkBlendMode::kSrcATop: + case flutter::DlBlendMode::kSrcATop: return Entity::BlendMode::kSourceATop; - case SkBlendMode::kDstATop: + case flutter::DlBlendMode::kDstATop: return Entity::BlendMode::kDestinationATop; - case SkBlendMode::kXor: + case flutter::DlBlendMode::kXor: return Entity::BlendMode::kXor; - case SkBlendMode::kPlus: + case flutter::DlBlendMode::kPlus: return Entity::BlendMode::kPlus; - case SkBlendMode::kModulate: + case flutter::DlBlendMode::kModulate: return Entity::BlendMode::kModulate; - case SkBlendMode::kScreen: - case SkBlendMode::kOverlay: - case SkBlendMode::kDarken: - case SkBlendMode::kLighten: - case SkBlendMode::kColorDodge: - case SkBlendMode::kColorBurn: - case SkBlendMode::kHardLight: - case SkBlendMode::kSoftLight: - case SkBlendMode::kDifference: - case SkBlendMode::kExclusion: - case SkBlendMode::kMultiply: - case SkBlendMode::kHue: - case SkBlendMode::kSaturation: - case SkBlendMode::kColor: - case SkBlendMode::kLuminosity: + case flutter::DlBlendMode::kScreen: + case flutter::DlBlendMode::kOverlay: + case flutter::DlBlendMode::kDarken: + case flutter::DlBlendMode::kLighten: + case flutter::DlBlendMode::kColorDodge: + case flutter::DlBlendMode::kColorBurn: + case flutter::DlBlendMode::kHardLight: + case flutter::DlBlendMode::kSoftLight: + case flutter::DlBlendMode::kDifference: + case flutter::DlBlendMode::kExclusion: + case flutter::DlBlendMode::kMultiply: + case flutter::DlBlendMode::kHue: + case flutter::DlBlendMode::kSaturation: + case flutter::DlBlendMode::kColor: + case flutter::DlBlendMode::kLuminosity: return std::nullopt; } @@ -211,8 +211,8 @@ std::optional ToBlendMode(SkBlendMode mode) { } // |flutter::Dispatcher| -void DisplayListDispatcher::setBlendMode(SkBlendMode sk_mode) { - if (auto mode = ToBlendMode(sk_mode); mode.has_value()) { +void DisplayListDispatcher::setBlendMode(flutter::DlBlendMode dl_mode) { + if (auto mode = ToBlendMode(dl_mode); mode.has_value()) { paint_.blend_mode = mode.value(); } else { UNIMPLEMENTED; @@ -454,10 +454,10 @@ void DisplayListDispatcher::clipPath(const SkPath& path, } // |flutter::Dispatcher| -void DisplayListDispatcher::drawColor(SkColor color, SkBlendMode sk_mode) { +void DisplayListDispatcher::drawColor(SkColor color, flutter::DlBlendMode dl_mode) { Paint paint; paint.color = ToColor(color); - if (auto mode = ToBlendMode(sk_mode); mode.has_value()) { + if (auto mode = ToBlendMode(dl_mode); mode.has_value()) { paint.blend_mode = mode.value(); } else { FML_DLOG(ERROR) << "Unimplemented blend mode in " << __FUNCTION__; @@ -530,7 +530,7 @@ void DisplayListDispatcher::drawPoints(SkCanvas::PointMode mode, // |flutter::Dispatcher| void DisplayListDispatcher::drawVertices(const sk_sp vertices, - SkBlendMode mode) { + flutter::DlBlendMode mode) { // Needs https://github.com/flutter/flutter/issues/95434 UNIMPLEMENTED; } @@ -582,7 +582,7 @@ void DisplayListDispatcher::drawAtlas(const sk_sp atlas, const SkRect tex[], const SkColor colors[], int count, - SkBlendMode mode, + flutter::DlBlendMode mode, const SkSamplingOptions& sampling, const SkRect* cull_rect, bool render_with_attributes) { diff --git a/impeller/display_list/display_list_dispatcher.h b/impeller/display_list/display_list_dispatcher.h index 4df89468eaa66..0a510e344b0c9 100644 --- a/impeller/display_list/display_list_dispatcher.h +++ b/impeller/display_list/display_list_dispatcher.h @@ -5,6 +5,7 @@ #pragma once #include "flutter/display_list/display_list.h" +#include "flutter/display_list/display_list_blend_mode.h" #include "flutter/display_list/display_list_dispatcher.h" #include "flutter/fml/macros.h" #include "impeller/aiks/canvas.h" @@ -54,7 +55,7 @@ class DisplayListDispatcher final : public flutter::Dispatcher { void setInvertColors(bool invert) override; // |flutter::Dispatcher| - void setBlendMode(SkBlendMode mode) override; + void setBlendMode(flutter::DlBlendMode mode) override; // |flutter::Dispatcher| void setBlender(sk_sp blender) override; @@ -129,7 +130,7 @@ class DisplayListDispatcher final : public flutter::Dispatcher { void clipPath(const SkPath& path, SkClipOp clip_op, bool is_aa) override; // |flutter::Dispatcher| - void drawColor(SkColor color, SkBlendMode mode) override; + void drawColor(SkColor color, flutter::DlBlendMode mode) override; // |flutter::Dispatcher| void drawPaint() override; @@ -168,7 +169,7 @@ class DisplayListDispatcher final : public flutter::Dispatcher { // |flutter::Dispatcher| void drawVertices(const sk_sp vertices, - SkBlendMode mode) override; + flutter::DlBlendMode mode) override; // |flutter::Dispatcher| void drawImage(const sk_sp image, @@ -204,7 +205,7 @@ class DisplayListDispatcher final : public flutter::Dispatcher { const SkRect tex[], const SkColor colors[], int count, - SkBlendMode mode, + flutter::DlBlendMode mode, const SkSamplingOptions& sampling, const SkRect* cull_rect, bool render_with_attributes) override; From b2447f6eb4de18220a2efa37fcd8df4ffbf899dd Mon Sep 17 00:00:00 2001 From: Jim Graham Date: Mon, 21 Mar 2022 12:37:19 -0700 Subject: [PATCH 364/433] switch dispatcher methods to new DlImageFilter objects (#92) --- impeller/display_list/display_list_dispatcher.cc | 2 +- impeller/display_list/display_list_dispatcher.h | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/impeller/display_list/display_list_dispatcher.cc b/impeller/display_list/display_list_dispatcher.cc index 46e23199f5e01..5d1bf440d703b 100644 --- a/impeller/display_list/display_list_dispatcher.cc +++ b/impeller/display_list/display_list_dispatcher.cc @@ -247,7 +247,7 @@ void DisplayListDispatcher::setMaskFilter(const flutter::DlMaskFilter* filter) { } // |flutter::Dispatcher| -void DisplayListDispatcher::setImageFilter(sk_sp filter) { +void DisplayListDispatcher::setImageFilter(const flutter::DlImageFilter* filter) { UNIMPLEMENTED; } diff --git a/impeller/display_list/display_list_dispatcher.h b/impeller/display_list/display_list_dispatcher.h index 0a510e344b0c9..4e84ee69afd81 100644 --- a/impeller/display_list/display_list_dispatcher.h +++ b/impeller/display_list/display_list_dispatcher.h @@ -67,7 +67,7 @@ class DisplayListDispatcher final : public flutter::Dispatcher { void setMaskFilter(const flutter::DlMaskFilter* filter) override; // |flutter::Dispatcher| - void setImageFilter(sk_sp filter) override; + void setImageFilter(const flutter::DlImageFilter* filter) override; // |flutter::Dispatcher| void save() override; From 5a2cc446b9b0944af474527942cc4c584511910f Mon Sep 17 00:00:00 2001 From: Brandon DeRosier Date: Mon, 21 Mar 2022 17:48:28 -0700 Subject: [PATCH 365/433] Separate filters into different TUs (#93) --- impeller/entity/BUILD.gn | 8 +- impeller/entity/contents/filter_contents.cc | 472 ------------------ .../contents/filters/blend_filter_contents.cc | 188 +++++++ .../contents/filters/blend_filter_contents.h | 36 ++ .../contents/filters/filter_contents.cc | 194 +++++++ .../contents/{ => filters}/filter_contents.h | 67 --- .../filters/gaussian_blur_filter_contents.cc | 101 ++++ .../filters/gaussian_blur_filter_contents.h | 40 ++ impeller/entity/entity_unittests.cc | 2 +- 9 files changed, 566 insertions(+), 542 deletions(-) delete mode 100644 impeller/entity/contents/filter_contents.cc create mode 100644 impeller/entity/contents/filters/blend_filter_contents.cc create mode 100644 impeller/entity/contents/filters/blend_filter_contents.h create mode 100644 impeller/entity/contents/filters/filter_contents.cc rename impeller/entity/contents/{ => filters}/filter_contents.h (54%) create mode 100644 impeller/entity/contents/filters/gaussian_blur_filter_contents.cc create mode 100644 impeller/entity/contents/filters/gaussian_blur_filter_contents.h diff --git a/impeller/entity/BUILD.gn b/impeller/entity/BUILD.gn index 39e828ff5e03d..e04288155e803 100644 --- a/impeller/entity/BUILD.gn +++ b/impeller/entity/BUILD.gn @@ -37,8 +37,12 @@ impeller_component("entity") { "contents/content_context.h", "contents/contents.cc", "contents/contents.h", - "contents/filter_contents.cc", - "contents/filter_contents.h", + "contents/filters/blend_filter_contents.cc", + "contents/filters/blend_filter_contents.h", + "contents/filters/filter_contents.cc", + "contents/filters/filter_contents.h", + "contents/filters/gaussian_blur_filter_contents.cc", + "contents/filters/gaussian_blur_filter_contents.h", "contents/linear_gradient_contents.cc", "contents/linear_gradient_contents.h", "contents/solid_color_contents.cc", diff --git a/impeller/entity/contents/filter_contents.cc b/impeller/entity/contents/filter_contents.cc deleted file mode 100644 index 579318d51bf30..0000000000000 --- a/impeller/entity/contents/filter_contents.cc +++ /dev/null @@ -1,472 +0,0 @@ -// Copyright 2013 The Flutter Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#include "filter_contents.h" - -#include -#include -#include -#include -#include - -#include "flutter/fml/logging.h" -#include "impeller/base/validation.h" -#include "impeller/entity/contents/content_context.h" -#include "impeller/entity/contents/solid_color_contents.h" -#include "impeller/entity/contents/texture_contents.h" -#include "impeller/entity/entity.h" -#include "impeller/geometry/path_builder.h" -#include "impeller/renderer/command_buffer.h" -#include "impeller/renderer/formats.h" -#include "impeller/renderer/render_pass.h" -#include "impeller/renderer/sampler_descriptor.h" -#include "impeller/renderer/sampler_library.h" - -namespace impeller { - -/******************************************************************************* - ******* FilterContents - ******************************************************************************/ - -std::shared_ptr FilterContents::MakeBlend( - Entity::BlendMode blend_mode, - InputTextures input_textures) { - if (blend_mode > Entity::BlendMode::kLastAdvancedBlendMode) { - VALIDATION_LOG << "Invalid blend mode " << static_cast(blend_mode) - << " passed to FilterContents::MakeBlend."; - return nullptr; - } - - if (input_textures.size() < 2 || - blend_mode <= Entity::BlendMode::kLastPipelineBlendMode) { - auto blend = std::make_shared(); - blend->SetInputTextures(input_textures); - blend->SetBlendMode(blend_mode); - return blend; - } - - if (blend_mode <= Entity::BlendMode::kLastAdvancedBlendMode) { - InputVariant blend = input_textures[0]; - for (auto in_i = input_textures.begin() + 1; in_i < input_textures.end(); - in_i++) { - auto new_blend = std::make_shared(); - new_blend->SetInputTextures({blend, *in_i}); - new_blend->SetBlendMode(blend_mode); - blend = new_blend; - } - return std::get>(blend); - } - - FML_UNREACHABLE(); -} - -std::shared_ptr FilterContents::MakeDirectionalGaussianBlur( - InputVariant input_texture, - Scalar radius, - Vector2 direction, - bool clip_border) { - auto blur = std::make_shared(); - blur->SetInputTextures({input_texture}); - blur->SetRadius(radius); - blur->SetDirection(direction); - blur->SetClipBorder(clip_border); - return blur; -} - -std::shared_ptr FilterContents::MakeGaussianBlur( - InputVariant input_texture, - Scalar radius, - bool clip_border) { - auto x_blur = MakeDirectionalGaussianBlur(input_texture, radius, Point(1, 0), - clip_border); - return MakeDirectionalGaussianBlur(x_blur, radius, Point(0, 1), false); -} - -FilterContents::FilterContents() = default; - -FilterContents::~FilterContents() = default; - -void FilterContents::SetInputTextures(InputTextures input_textures) { - input_textures_ = std::move(input_textures); -} - -bool FilterContents::Render(const ContentContext& renderer, - const Entity& entity, - RenderPass& pass) const { - // Run the filter. - - auto maybe_texture = RenderFilterToTexture(renderer, entity, pass); - if (!maybe_texture.has_value()) { - return false; - } - auto& texture = maybe_texture.value(); - - // Draw the resulting texture to the given destination rect, respecting the - // transform and clip stack. - - auto contents = std::make_shared(); - contents->SetTexture(texture); - contents->SetSourceRect(IRect::MakeSize(texture->GetSize())); - - return contents->Render(renderer, entity, pass); -} - -std::optional> FilterContents::RenderFilterToTexture( - const ContentContext& renderer, - const Entity& entity, - RenderPass& pass) const { - auto output_size = GetOutputSize(input_textures_); - if (output_size.IsZero()) { - return std::nullopt; - } - - // Resolve all inputs as textures. - - std::vector> input_textures; - input_textures.reserve(input_textures_.size()); - for (const auto& input : input_textures_) { - if (auto filter = std::get_if>(&input)) { - auto texture = - filter->get()->RenderFilterToTexture(renderer, entity, pass); - if (!texture.has_value()) { - return std::nullopt; - } - input_textures.push_back(std::move(texture.value())); - } else if (auto texture = std::get_if>(&input)) { - input_textures.push_back(*texture); - } else { - FML_UNREACHABLE(); - } - } - - // Create a new texture and render the filter to it. - - auto context = renderer.GetContext(); - - auto subpass_target = RenderTarget::CreateOffscreen(*context, output_size); - auto subpass_texture = subpass_target.GetRenderTargetTexture(); - if (!subpass_texture) { - return std::nullopt; - } - - auto sub_command_buffer = context->CreateRenderCommandBuffer(); - sub_command_buffer->SetLabel("Offscreen Filter Command Buffer"); - if (!sub_command_buffer) { - return std::nullopt; - } - - auto sub_renderpass = sub_command_buffer->CreateRenderPass(subpass_target); - if (!sub_renderpass) { - return std::nullopt; - } - sub_renderpass->SetLabel("OffscreenFilterPass"); - - if (!RenderFilter(input_textures, renderer, *sub_renderpass)) { - return std::nullopt; - } - - if (!sub_renderpass->EncodeCommands(*context->GetTransientsAllocator())) { - return std::nullopt; - } - - if (!sub_command_buffer->SubmitCommands()) { - return std::nullopt; - } - - return subpass_texture; -} - -ISize FilterContents::GetOutputSize() const { - if (input_textures_.empty()) { - return {}; - } - return GetOutputSize(input_textures_); -} - -ISize FilterContents::GetOutputSize(const InputTextures& input_textures) const { - if (auto filter = - std::get_if>(&input_textures[0])) { - return filter->get()->GetOutputSize(input_textures); - } - - if (auto texture = - std::get_if>(&input_textures[0])) { - return texture->get()->GetSize(); - } - - FML_UNREACHABLE(); -} - -/******************************************************************************* - ******* BlendFilterContents - ******************************************************************************/ - -BlendFilterContents::BlendFilterContents() { - SetBlendMode(Entity::BlendMode::kSourceOver); -} - -BlendFilterContents::~BlendFilterContents() = default; - -using PipelineProc = - std::shared_ptr (ContentContext::*)(ContentContextOptions) const; - -template -static void AdvancedBlendPass(std::shared_ptr input_d, - std::shared_ptr input_s, - std::shared_ptr sampler, - const ContentContext& renderer, - RenderPass& pass, - Command& cmd) {} - -template -static bool AdvancedBlend( - const std::vector>& input_textures, - const ContentContext& renderer, - RenderPass& pass, - PipelineProc pipeline_proc) { - if (input_textures.size() < 2) { - return false; - } - - auto& host_buffer = pass.GetTransientsBuffer(); - - VertexBufferBuilder vtx_builder; - vtx_builder.AddVertices({ - {Point(0, 0), Point(0, 0)}, - {Point(1, 0), Point(1, 0)}, - {Point(1, 1), Point(1, 1)}, - {Point(0, 0), Point(0, 0)}, - {Point(1, 1), Point(1, 1)}, - {Point(0, 1), Point(0, 1)}, - }); - auto vtx_buffer = vtx_builder.CreateVertexBuffer(host_buffer); - - typename VS::FrameInfo frame_info; - frame_info.mvp = Matrix::MakeOrthographic(ISize(1, 1)); - - auto uniform_view = host_buffer.EmplaceUniform(frame_info); - auto sampler = renderer.GetContext()->GetSamplerLibrary()->GetSampler({}); - - auto options = OptionsFromPass(pass); - options.blend_mode = Entity::BlendMode::kSource; - std::shared_ptr pipeline = - std::invoke(pipeline_proc, renderer, options); - - Command cmd; - cmd.label = "Advanced Blend Filter"; - cmd.BindVertices(vtx_buffer); - cmd.pipeline = std::move(pipeline); - VS::BindFrameInfo(cmd, uniform_view); - - FS::BindTextureSamplerDst(cmd, input_textures[0], sampler); - FS::BindTextureSamplerSrc(cmd, input_textures[1], sampler); - pass.AddCommand(cmd); - - return true; -} - -void BlendFilterContents::SetBlendMode(Entity::BlendMode blend_mode) { - if (blend_mode > Entity::BlendMode::kLastAdvancedBlendMode) { - VALIDATION_LOG << "Invalid blend mode " << static_cast(blend_mode) - << " assigned to BlendFilterContents."; - } - - blend_mode_ = blend_mode; - - if (blend_mode > Entity::BlendMode::kLastPipelineBlendMode) { - static_assert(Entity::BlendMode::kLastAdvancedBlendMode == - Entity::BlendMode::kScreen); - - switch (blend_mode) { - case Entity::BlendMode::kScreen: - advanced_blend_proc_ = - [](const std::vector>& input_textures, - const ContentContext& renderer, RenderPass& pass) { - PipelineProc p = &ContentContext::GetTextureBlendScreenPipeline; - return AdvancedBlend( - input_textures, renderer, pass, p); - }; - break; - default: - FML_UNREACHABLE(); - } - } -} - -static bool BasicBlend( - const std::vector>& input_textures, - const ContentContext& renderer, - RenderPass& pass, - Entity::BlendMode basic_blend) { - using VS = TextureBlendPipeline::VertexShader; - using FS = TextureBlendPipeline::FragmentShader; - - auto& host_buffer = pass.GetTransientsBuffer(); - - VertexBufferBuilder vtx_builder; - vtx_builder.AddVertices({ - {Point(0, 0), Point(0, 0)}, - {Point(1, 0), Point(1, 0)}, - {Point(1, 1), Point(1, 1)}, - {Point(0, 0), Point(0, 0)}, - {Point(1, 1), Point(1, 1)}, - {Point(0, 1), Point(0, 1)}, - }); - auto vtx_buffer = vtx_builder.CreateVertexBuffer(host_buffer); - - VS::FrameInfo frame_info; - frame_info.mvp = Matrix::MakeOrthographic(ISize(1, 1)); - - auto uniform_view = host_buffer.EmplaceUniform(frame_info); - auto sampler = renderer.GetContext()->GetSamplerLibrary()->GetSampler({}); - - // Draw the first texture using kSource. - - Command cmd; - cmd.label = "Basic Blend Filter"; - cmd.BindVertices(vtx_buffer); - auto options = OptionsFromPass(pass); - options.blend_mode = Entity::BlendMode::kSource; - cmd.pipeline = renderer.GetTextureBlendPipeline(options); - FS::BindTextureSamplerSrc(cmd, input_textures[0], sampler); - VS::BindFrameInfo(cmd, uniform_view); - pass.AddCommand(cmd); - - if (input_textures.size() < 2) { - return true; - } - - // Write subsequent textures using the selected blend mode. - - options.blend_mode = basic_blend; - cmd.pipeline = renderer.GetTextureBlendPipeline(options); - - for (auto texture_i = input_textures.begin() + 1; - texture_i < input_textures.end(); texture_i++) { - FS::BindTextureSamplerSrc(cmd, *texture_i, sampler); - pass.AddCommand(cmd); - } - - return true; -} - -bool BlendFilterContents::RenderFilter( - const std::vector>& input_textures, - const ContentContext& renderer, - RenderPass& pass) const { - if (input_textures.empty()) { - return true; - } - - if (input_textures.size() == 1) { - // Nothing to blend. - return BasicBlend(input_textures, renderer, pass, - Entity::BlendMode::kSource); - } - - if (blend_mode_ <= Entity::BlendMode::kLastPipelineBlendMode) { - return BasicBlend(input_textures, renderer, pass, blend_mode_); - } - - if (blend_mode_ <= Entity::BlendMode::kLastAdvancedBlendMode) { - return advanced_blend_proc_(input_textures, renderer, pass); - } - - FML_UNREACHABLE(); -} - -/******************************************************************************* - ******* DirectionalGaussianBlurFilterContents - ******************************************************************************/ - -DirectionalGaussianBlurFilterContents::DirectionalGaussianBlurFilterContents() = - default; - -DirectionalGaussianBlurFilterContents:: - ~DirectionalGaussianBlurFilterContents() = default; - -void DirectionalGaussianBlurFilterContents::SetRadius(Scalar radius) { - radius_ = std::max(radius, 1e-3f); -} - -void DirectionalGaussianBlurFilterContents::SetDirection(Vector2 direction) { - direction_ = direction.Normalize(); -} - -void DirectionalGaussianBlurFilterContents::SetClipBorder(bool clip) { - clip_ = clip; -} - -bool DirectionalGaussianBlurFilterContents::RenderFilter( - const std::vector>& input_textures, - const ContentContext& renderer, - RenderPass& pass) const { - using VS = GaussianBlurPipeline::VertexShader; - using FS = GaussianBlurPipeline::FragmentShader; - - auto& host_buffer = pass.GetTransientsBuffer(); - - ISize size = FilterContents::GetOutputSize(); - Point uv_offset = clip_ ? (Point(radius_, radius_) / size) : Point(); - // LTRB - Scalar uv[4] = { - -uv_offset.x, - -uv_offset.y, - 1 + uv_offset.x, - 1 + uv_offset.y, - }; - - VertexBufferBuilder vtx_builder; - vtx_builder.AddVertices({ - {Point(0, 0), Point(uv[0], uv[1])}, - {Point(size.width, 0), Point(uv[2], uv[1])}, - {Point(size.width, size.height), Point(uv[2], uv[3])}, - {Point(0, 0), Point(uv[0], uv[1])}, - {Point(size.width, size.height), Point(uv[2], uv[3])}, - {Point(0, size.height), Point(uv[0], uv[3])}, - }); - auto vtx_buffer = vtx_builder.CreateVertexBuffer(host_buffer); - - VS::FrameInfo frame_info; - frame_info.mvp = Matrix::MakeOrthographic(size); - frame_info.texture_size = Point(size); - frame_info.blur_radius = radius_; - frame_info.blur_direction = direction_; - - auto uniform_view = host_buffer.EmplaceUniform(frame_info); - auto sampler = renderer.GetContext()->GetSamplerLibrary()->GetSampler({}); - - Command cmd; - cmd.label = "Gaussian Blur Filter"; - auto options = OptionsFromPass(pass); - options.blend_mode = Entity::BlendMode::kSource; - cmd.pipeline = renderer.GetGaussianBlurPipeline(options); - cmd.BindVertices(vtx_buffer); - VS::BindFrameInfo(cmd, uniform_view); - for (const auto& texture : input_textures) { - FS::BindTextureSampler(cmd, texture, sampler); - pass.AddCommand(cmd); - } - - return true; -} - -ISize DirectionalGaussianBlurFilterContents::GetOutputSize( - const InputTextures& input_textures) const { - ISize size; - if (auto filter = - std::get_if>(&input_textures[0])) { - size = filter->get()->GetOutputSize(); - } else if (auto texture = - std::get_if>(&input_textures[0])) { - size = texture->get()->GetSize(); - } else { - FML_UNREACHABLE(); - } - - return size + (clip_ ? ISize(radius_ * 2, radius_ * 2) : ISize()); -} - -} // namespace impeller diff --git a/impeller/entity/contents/filters/blend_filter_contents.cc b/impeller/entity/contents/filters/blend_filter_contents.cc new file mode 100644 index 0000000000000..5f1e89c0eb7de --- /dev/null +++ b/impeller/entity/contents/filters/blend_filter_contents.cc @@ -0,0 +1,188 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "impeller/entity/contents/filters/blend_filter_contents.h" + +#include "impeller/entity/contents/content_context.h" +#include "impeller/renderer/render_pass.h" +#include "impeller/renderer/sampler_library.h" + +namespace impeller { + +BlendFilterContents::BlendFilterContents() { + SetBlendMode(Entity::BlendMode::kSourceOver); +} + +BlendFilterContents::~BlendFilterContents() = default; + +using PipelineProc = + std::shared_ptr (ContentContext::*)(ContentContextOptions) const; + +template +static void AdvancedBlendPass(std::shared_ptr input_d, + std::shared_ptr input_s, + std::shared_ptr sampler, + const ContentContext& renderer, + RenderPass& pass, + Command& cmd) {} + +template +static bool AdvancedBlend( + const std::vector>& input_textures, + const ContentContext& renderer, + RenderPass& pass, + PipelineProc pipeline_proc) { + if (input_textures.size() < 2) { + return false; + } + + auto& host_buffer = pass.GetTransientsBuffer(); + + VertexBufferBuilder vtx_builder; + vtx_builder.AddVertices({ + {Point(0, 0), Point(0, 0)}, + {Point(1, 0), Point(1, 0)}, + {Point(1, 1), Point(1, 1)}, + {Point(0, 0), Point(0, 0)}, + {Point(1, 1), Point(1, 1)}, + {Point(0, 1), Point(0, 1)}, + }); + auto vtx_buffer = vtx_builder.CreateVertexBuffer(host_buffer); + + typename VS::FrameInfo frame_info; + frame_info.mvp = Matrix::MakeOrthographic(ISize(1, 1)); + + auto uniform_view = host_buffer.EmplaceUniform(frame_info); + auto sampler = renderer.GetContext()->GetSamplerLibrary()->GetSampler({}); + + auto options = OptionsFromPass(pass); + options.blend_mode = Entity::BlendMode::kSource; + std::shared_ptr pipeline = + std::invoke(pipeline_proc, renderer, options); + + Command cmd; + cmd.label = "Advanced Blend Filter"; + cmd.BindVertices(vtx_buffer); + cmd.pipeline = std::move(pipeline); + VS::BindFrameInfo(cmd, uniform_view); + + FS::BindTextureSamplerDst(cmd, input_textures[0], sampler); + FS::BindTextureSamplerSrc(cmd, input_textures[1], sampler); + pass.AddCommand(cmd); + + return true; +} + +void BlendFilterContents::SetBlendMode(Entity::BlendMode blend_mode) { + if (blend_mode > Entity::BlendMode::kLastAdvancedBlendMode) { + VALIDATION_LOG << "Invalid blend mode " << static_cast(blend_mode) + << " assigned to BlendFilterContents."; + } + + blend_mode_ = blend_mode; + + if (blend_mode > Entity::BlendMode::kLastPipelineBlendMode) { + static_assert(Entity::BlendMode::kLastAdvancedBlendMode == + Entity::BlendMode::kScreen); + + switch (blend_mode) { + case Entity::BlendMode::kScreen: + advanced_blend_proc_ = + [](const std::vector>& input_textures, + const ContentContext& renderer, RenderPass& pass) { + PipelineProc p = &ContentContext::GetTextureBlendScreenPipeline; + return AdvancedBlend( + input_textures, renderer, pass, p); + }; + break; + default: + FML_UNREACHABLE(); + } + } +} + +static bool BasicBlend( + const std::vector>& input_textures, + const ContentContext& renderer, + RenderPass& pass, + Entity::BlendMode basic_blend) { + using VS = TextureBlendPipeline::VertexShader; + using FS = TextureBlendPipeline::FragmentShader; + + auto& host_buffer = pass.GetTransientsBuffer(); + + VertexBufferBuilder vtx_builder; + vtx_builder.AddVertices({ + {Point(0, 0), Point(0, 0)}, + {Point(1, 0), Point(1, 0)}, + {Point(1, 1), Point(1, 1)}, + {Point(0, 0), Point(0, 0)}, + {Point(1, 1), Point(1, 1)}, + {Point(0, 1), Point(0, 1)}, + }); + auto vtx_buffer = vtx_builder.CreateVertexBuffer(host_buffer); + + VS::FrameInfo frame_info; + frame_info.mvp = Matrix::MakeOrthographic(ISize(1, 1)); + + auto uniform_view = host_buffer.EmplaceUniform(frame_info); + auto sampler = renderer.GetContext()->GetSamplerLibrary()->GetSampler({}); + + // Draw the first texture using kSource. + + Command cmd; + cmd.label = "Basic Blend Filter"; + cmd.BindVertices(vtx_buffer); + auto options = OptionsFromPass(pass); + options.blend_mode = Entity::BlendMode::kSource; + cmd.pipeline = renderer.GetTextureBlendPipeline(options); + FS::BindTextureSamplerSrc(cmd, input_textures[0], sampler); + VS::BindFrameInfo(cmd, uniform_view); + pass.AddCommand(cmd); + + if (input_textures.size() < 2) { + return true; + } + + // Write subsequent textures using the selected blend mode. + + options.blend_mode = basic_blend; + cmd.pipeline = renderer.GetTextureBlendPipeline(options); + + for (auto texture_i = input_textures.begin() + 1; + texture_i < input_textures.end(); texture_i++) { + FS::BindTextureSamplerSrc(cmd, *texture_i, sampler); + pass.AddCommand(cmd); + } + + return true; +} + +bool BlendFilterContents::RenderFilter( + const std::vector>& input_textures, + const ContentContext& renderer, + RenderPass& pass) const { + if (input_textures.empty()) { + return true; + } + + if (input_textures.size() == 1) { + // Nothing to blend. + return BasicBlend(input_textures, renderer, pass, + Entity::BlendMode::kSource); + } + + if (blend_mode_ <= Entity::BlendMode::kLastPipelineBlendMode) { + return BasicBlend(input_textures, renderer, pass, blend_mode_); + } + + if (blend_mode_ <= Entity::BlendMode::kLastAdvancedBlendMode) { + return advanced_blend_proc_(input_textures, renderer, pass); + } + + FML_UNREACHABLE(); +} + +} // namespace impeller diff --git a/impeller/entity/contents/filters/blend_filter_contents.h b/impeller/entity/contents/filters/blend_filter_contents.h new file mode 100644 index 0000000000000..442cc54b3b05e --- /dev/null +++ b/impeller/entity/contents/filters/blend_filter_contents.h @@ -0,0 +1,36 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#pragma once + +#include "impeller/entity/contents/filters/filter_contents.h" + +namespace impeller { + +class BlendFilterContents : public FilterContents { + public: + using AdvancedBlendProc = std::function>& input_textures, + const ContentContext& renderer, + RenderPass& pass)>; + + BlendFilterContents(); + + ~BlendFilterContents() override; + + void SetBlendMode(Entity::BlendMode blend_mode); + + private: + // |FilterContents| + bool RenderFilter(const std::vector>& input_textures, + const ContentContext& renderer, + RenderPass& pass) const override; + + Entity::BlendMode blend_mode_; + AdvancedBlendProc advanced_blend_proc_; + + FML_DISALLOW_COPY_AND_ASSIGN(BlendFilterContents); +}; + +} // namespace impeller diff --git a/impeller/entity/contents/filters/filter_contents.cc b/impeller/entity/contents/filters/filter_contents.cc new file mode 100644 index 0000000000000..033fdeed3183e --- /dev/null +++ b/impeller/entity/contents/filters/filter_contents.cc @@ -0,0 +1,194 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "impeller/entity/contents/filters/filter_contents.h" + +#include +#include +#include + +#include "flutter/fml/logging.h" +#include "impeller/base/validation.h" +#include "impeller/entity/contents/content_context.h" +#include "impeller/entity/contents/filters/blend_filter_contents.h" +#include "impeller/entity/contents/filters/gaussian_blur_filter_contents.h" +#include "impeller/entity/contents/texture_contents.h" +#include "impeller/entity/entity.h" +#include "impeller/geometry/path_builder.h" +#include "impeller/renderer/command_buffer.h" +#include "impeller/renderer/formats.h" +#include "impeller/renderer/render_pass.h" + +namespace impeller { + +std::shared_ptr FilterContents::MakeBlend( + Entity::BlendMode blend_mode, + InputTextures input_textures) { + if (blend_mode > Entity::BlendMode::kLastAdvancedBlendMode) { + VALIDATION_LOG << "Invalid blend mode " << static_cast(blend_mode) + << " passed to FilterContents::MakeBlend."; + return nullptr; + } + + if (input_textures.size() < 2 || + blend_mode <= Entity::BlendMode::kLastPipelineBlendMode) { + auto blend = std::make_shared(); + blend->SetInputTextures(input_textures); + blend->SetBlendMode(blend_mode); + return blend; + } + + if (blend_mode <= Entity::BlendMode::kLastAdvancedBlendMode) { + InputVariant blend = input_textures[0]; + for (auto in_i = input_textures.begin() + 1; in_i < input_textures.end(); + in_i++) { + auto new_blend = std::make_shared(); + new_blend->SetInputTextures({blend, *in_i}); + new_blend->SetBlendMode(blend_mode); + blend = new_blend; + } + return std::get>(blend); + } + + FML_UNREACHABLE(); +} + +std::shared_ptr FilterContents::MakeDirectionalGaussianBlur( + InputVariant input_texture, + Scalar radius, + Vector2 direction, + bool clip_border) { + auto blur = std::make_shared(); + blur->SetInputTextures({input_texture}); + blur->SetRadius(radius); + blur->SetDirection(direction); + blur->SetClipBorder(clip_border); + return blur; +} + +std::shared_ptr FilterContents::MakeGaussianBlur( + InputVariant input_texture, + Scalar radius, + bool clip_border) { + auto x_blur = MakeDirectionalGaussianBlur(input_texture, radius, Point(1, 0), + clip_border); + return MakeDirectionalGaussianBlur(x_blur, radius, Point(0, 1), false); +} + +FilterContents::FilterContents() = default; + +FilterContents::~FilterContents() = default; + +void FilterContents::SetInputTextures(InputTextures input_textures) { + input_textures_ = std::move(input_textures); +} + +bool FilterContents::Render(const ContentContext& renderer, + const Entity& entity, + RenderPass& pass) const { + // Run the filter. + + auto maybe_texture = RenderFilterToTexture(renderer, entity, pass); + if (!maybe_texture.has_value()) { + return false; + } + auto& texture = maybe_texture.value(); + + // Draw the resulting texture to the given destination rect, respecting the + // transform and clip stack. + + auto contents = std::make_shared(); + contents->SetTexture(texture); + contents->SetSourceRect(IRect::MakeSize(texture->GetSize())); + + return contents->Render(renderer, entity, pass); +} + +std::optional> FilterContents::RenderFilterToTexture( + const ContentContext& renderer, + const Entity& entity, + RenderPass& pass) const { + auto output_size = GetOutputSize(input_textures_); + if (output_size.IsZero()) { + return std::nullopt; + } + + // Resolve all inputs as textures. + + std::vector> input_textures; + input_textures.reserve(input_textures_.size()); + for (const auto& input : input_textures_) { + if (auto filter = std::get_if>(&input)) { + auto texture = + filter->get()->RenderFilterToTexture(renderer, entity, pass); + if (!texture.has_value()) { + return std::nullopt; + } + input_textures.push_back(std::move(texture.value())); + } else if (auto texture = std::get_if>(&input)) { + input_textures.push_back(*texture); + } else { + FML_UNREACHABLE(); + } + } + + // Create a new texture and render the filter to it. + + auto context = renderer.GetContext(); + + auto subpass_target = RenderTarget::CreateOffscreen(*context, output_size); + auto subpass_texture = subpass_target.GetRenderTargetTexture(); + if (!subpass_texture) { + return std::nullopt; + } + + auto sub_command_buffer = context->CreateRenderCommandBuffer(); + sub_command_buffer->SetLabel("Offscreen Filter Command Buffer"); + if (!sub_command_buffer) { + return std::nullopt; + } + + auto sub_renderpass = sub_command_buffer->CreateRenderPass(subpass_target); + if (!sub_renderpass) { + return std::nullopt; + } + sub_renderpass->SetLabel("OffscreenFilterPass"); + + if (!RenderFilter(input_textures, renderer, *sub_renderpass)) { + return std::nullopt; + } + + if (!sub_renderpass->EncodeCommands(*context->GetTransientsAllocator())) { + return std::nullopt; + } + + if (!sub_command_buffer->SubmitCommands()) { + return std::nullopt; + } + + return subpass_texture; +} + +ISize FilterContents::GetOutputSize() const { + if (input_textures_.empty()) { + return {}; + } + return GetOutputSize(input_textures_); +} + +ISize FilterContents::GetOutputSize(const InputTextures& input_textures) const { + if (auto filter = + std::get_if>(&input_textures[0])) { + return filter->get()->GetOutputSize(input_textures); + } + + if (auto texture = + std::get_if>(&input_textures[0])) { + return texture->get()->GetSize(); + } + + FML_UNREACHABLE(); +} + +} // namespace impeller diff --git a/impeller/entity/contents/filter_contents.h b/impeller/entity/contents/filters/filter_contents.h similarity index 54% rename from impeller/entity/contents/filter_contents.h rename to impeller/entity/contents/filters/filter_contents.h index eca9c0aa1a783..7c5b29ff2858e 100644 --- a/impeller/entity/contents/filter_contents.h +++ b/impeller/entity/contents/filters/filter_contents.h @@ -8,7 +8,6 @@ #include #include -#include "impeller/entity/contents/contents.h" #include "impeller/entity/entity.h" #include "impeller/renderer/formats.h" @@ -16,10 +15,6 @@ namespace impeller { class Pipeline; -/******************************************************************************* - ******* FilterContents - ******************************************************************************/ - class FilterContents : public Contents { public: using InputVariant = @@ -85,66 +80,4 @@ class FilterContents : public Contents { FML_DISALLOW_COPY_AND_ASSIGN(FilterContents); }; -/******************************************************************************* - ******* BlendFilterContents - ******************************************************************************/ - -class BlendFilterContents : public FilterContents { - public: - using AdvancedBlendProc = std::function>& input_textures, - const ContentContext& renderer, - RenderPass& pass)>; - - BlendFilterContents(); - - ~BlendFilterContents() override; - - void SetBlendMode(Entity::BlendMode blend_mode); - - private: - // |FilterContents| - bool RenderFilter(const std::vector>& input_textures, - const ContentContext& renderer, - RenderPass& pass) const override; - - Entity::BlendMode blend_mode_; - AdvancedBlendProc advanced_blend_proc_; - - FML_DISALLOW_COPY_AND_ASSIGN(BlendFilterContents); -}; - -/******************************************************************************* - ******* DirectionalGaussionBlurFilterContents - ******************************************************************************/ - -class DirectionalGaussianBlurFilterContents final : public FilterContents { - public: - DirectionalGaussianBlurFilterContents(); - - ~DirectionalGaussianBlurFilterContents() override; - - void SetRadius(Scalar radius); - - void SetDirection(Vector2 direction); - - void SetClipBorder(bool clip); - - private: - // |FilterContents| - bool RenderFilter(const std::vector>& input_textures, - const ContentContext& renderer, - RenderPass& pass) const override; - - // |FilterContents| - virtual ISize GetOutputSize( - const InputTextures& input_textures) const override; - - Scalar radius_ = 0; - Vector2 direction_; - bool clip_ = false; - - FML_DISALLOW_COPY_AND_ASSIGN(DirectionalGaussianBlurFilterContents); -}; - } // namespace impeller diff --git a/impeller/entity/contents/filters/gaussian_blur_filter_contents.cc b/impeller/entity/contents/filters/gaussian_blur_filter_contents.cc new file mode 100644 index 0000000000000..f677f37cf362a --- /dev/null +++ b/impeller/entity/contents/filters/gaussian_blur_filter_contents.cc @@ -0,0 +1,101 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "impeller/entity/contents/filters/gaussian_blur_filter_contents.h" + +#include "impeller/entity/contents/content_context.h" +#include "impeller/renderer/render_pass.h" +#include "impeller/renderer/sampler_library.h" + +namespace impeller { + +DirectionalGaussianBlurFilterContents::DirectionalGaussianBlurFilterContents() = + default; + +DirectionalGaussianBlurFilterContents:: + ~DirectionalGaussianBlurFilterContents() = default; + +void DirectionalGaussianBlurFilterContents::SetRadius(Scalar radius) { + radius_ = std::max(radius, 1e-3f); +} + +void DirectionalGaussianBlurFilterContents::SetDirection(Vector2 direction) { + direction_ = direction.Normalize(); +} + +void DirectionalGaussianBlurFilterContents::SetClipBorder(bool clip) { + clip_ = clip; +} + +bool DirectionalGaussianBlurFilterContents::RenderFilter( + const std::vector>& input_textures, + const ContentContext& renderer, + RenderPass& pass) const { + using VS = GaussianBlurPipeline::VertexShader; + using FS = GaussianBlurPipeline::FragmentShader; + + auto& host_buffer = pass.GetTransientsBuffer(); + + ISize size = FilterContents::GetOutputSize(); + Point uv_offset = clip_ ? (Point(radius_, radius_) / size) : Point(); + // LTRB + Scalar uv[4] = { + -uv_offset.x, + -uv_offset.y, + 1 + uv_offset.x, + 1 + uv_offset.y, + }; + + VertexBufferBuilder vtx_builder; + vtx_builder.AddVertices({ + {Point(0, 0), Point(uv[0], uv[1])}, + {Point(size.width, 0), Point(uv[2], uv[1])}, + {Point(size.width, size.height), Point(uv[2], uv[3])}, + {Point(0, 0), Point(uv[0], uv[1])}, + {Point(size.width, size.height), Point(uv[2], uv[3])}, + {Point(0, size.height), Point(uv[0], uv[3])}, + }); + auto vtx_buffer = vtx_builder.CreateVertexBuffer(host_buffer); + + VS::FrameInfo frame_info; + frame_info.mvp = Matrix::MakeOrthographic(size); + frame_info.texture_size = Point(size); + frame_info.blur_radius = radius_; + frame_info.blur_direction = direction_; + + auto uniform_view = host_buffer.EmplaceUniform(frame_info); + auto sampler = renderer.GetContext()->GetSamplerLibrary()->GetSampler({}); + + Command cmd; + cmd.label = "Gaussian Blur Filter"; + auto options = OptionsFromPass(pass); + options.blend_mode = Entity::BlendMode::kSource; + cmd.pipeline = renderer.GetGaussianBlurPipeline(options); + cmd.BindVertices(vtx_buffer); + VS::BindFrameInfo(cmd, uniform_view); + for (const auto& texture : input_textures) { + FS::BindTextureSampler(cmd, texture, sampler); + pass.AddCommand(cmd); + } + + return true; +} + +ISize DirectionalGaussianBlurFilterContents::GetOutputSize( + const InputTextures& input_textures) const { + ISize size; + if (auto filter = + std::get_if>(&input_textures[0])) { + size = filter->get()->GetOutputSize(); + } else if (auto texture = + std::get_if>(&input_textures[0])) { + size = texture->get()->GetSize(); + } else { + FML_UNREACHABLE(); + } + + return size + (clip_ ? ISize(radius_ * 2, radius_ * 2) : ISize()); +} + +} // namespace impeller diff --git a/impeller/entity/contents/filters/gaussian_blur_filter_contents.h b/impeller/entity/contents/filters/gaussian_blur_filter_contents.h new file mode 100644 index 0000000000000..cdf6c470dba7e --- /dev/null +++ b/impeller/entity/contents/filters/gaussian_blur_filter_contents.h @@ -0,0 +1,40 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#pragma once + +#include "impeller/entity/contents/filters/filter_contents.h" + +namespace impeller { + +class DirectionalGaussianBlurFilterContents final : public FilterContents { + public: + DirectionalGaussianBlurFilterContents(); + + ~DirectionalGaussianBlurFilterContents() override; + + void SetRadius(Scalar radius); + + void SetDirection(Vector2 direction); + + void SetClipBorder(bool clip); + + private: + // |FilterContents| + bool RenderFilter(const std::vector>& input_textures, + const ContentContext& renderer, + RenderPass& pass) const override; + + // |FilterContents| + virtual ISize GetOutputSize( + const InputTextures& input_textures) const override; + + Scalar radius_ = 0; + Vector2 direction_; + bool clip_ = false; + + FML_DISALLOW_COPY_AND_ASSIGN(DirectionalGaussianBlurFilterContents); +}; + +} // namespace impeller diff --git a/impeller/entity/entity_unittests.cc b/impeller/entity/entity_unittests.cc index 1208b8073d571..6f02cf6353cd2 100644 --- a/impeller/entity/entity_unittests.cc +++ b/impeller/entity/entity_unittests.cc @@ -3,7 +3,7 @@ // found in the LICENSE file. #include "flutter/testing/testing.h" -#include "impeller/entity/contents/filter_contents.h" +#include "impeller/entity/contents/filters/filter_contents.h" #include "impeller/entity/contents/solid_color_contents.h" #include "impeller/entity/contents/solid_stroke_contents.h" #include "impeller/entity/entity.h" From 7916cfbeab3d6c474f66e3c736c7b9bfdbd98982 Mon Sep 17 00:00:00 2001 From: Chinmay Garde Date: Tue, 22 Mar 2022 09:39:28 -0700 Subject: [PATCH 366/433] Implement impeller::DisplayListDispatcher::drawTextBlob. (#94) --- impeller/aiks/aiks_unittests.cc | 11 +-- impeller/aiks/canvas.cc | 13 ++-- impeller/aiks/canvas.h | 5 +- impeller/display_list/BUILD.gn | 8 ++- .../display_list/display_list_dispatcher.cc | 8 ++- .../display_list/display_list_playground.cc | 71 +++++++++++++++++++ .../display_list/display_list_playground.h | 33 +++++++++ .../display_list/display_list_unittests.cc | 19 ++++- impeller/entity/contents/text_contents.cc | 33 +++++++-- impeller/entity/contents/text_contents.h | 12 +++- impeller/entity/entity_pass.cc | 4 ++ impeller/entity/entity_pass.h | 5 ++ impeller/typographer/BUILD.gn | 2 + .../backends/skia/text_render_context_skia.cc | 21 +++--- .../backends/skia/text_render_context_skia.h | 2 +- impeller/typographer/lazy_glyph_atlas.cc | 49 +++++++++++++ impeller/typographer/lazy_glyph_atlas.h | 32 +++++++++ impeller/typographer/text_render_context.cc | 21 ++++++ impeller/typographer/text_render_context.h | 29 ++++---- impeller/typographer/typographer_unittests.cc | 8 +-- 20 files changed, 323 insertions(+), 63 deletions(-) create mode 100644 impeller/display_list/display_list_playground.cc create mode 100644 impeller/display_list/display_list_playground.h create mode 100644 impeller/typographer/lazy_glyph_atlas.cc create mode 100644 impeller/typographer/lazy_glyph_atlas.h diff --git a/impeller/aiks/aiks_unittests.cc b/impeller/aiks/aiks_unittests.cc index 738abc6651056..5ea7fb9f5507b 100644 --- a/impeller/aiks/aiks_unittests.cc +++ b/impeller/aiks/aiks_unittests.cc @@ -347,19 +347,10 @@ bool RenderTextInCanvas(std::shared_ptr context, // Create the Impeller text frame and draw it at the designated baseline. auto frame = TextFrameFromTextBlob(blob); - TextRenderContextSkia text_context(context); - if (!text_context.IsValid()) { - return false; - } - auto atlas = text_context.CreateGlyphAtlas(frame); - if (!atlas) { - return false; - } Paint text_paint; text_paint.color = Color::Yellow(); - canvas.DrawTextFrame(std::move(frame), std::move(atlas), text_position, - text_paint); + canvas.DrawTextFrame(std::move(frame), text_position, text_paint); return true; } diff --git a/impeller/aiks/canvas.cc b/impeller/aiks/canvas.cc index b77beb800b949..d9464e8f4ef6b 100644 --- a/impeller/aiks/canvas.cc +++ b/impeller/aiks/canvas.cc @@ -269,17 +269,14 @@ void Canvas::Save(bool create_subpass) { xformation_stack_.emplace_back(std::move(entry)); } -void Canvas::DrawTextFrame(TextFrame text_frame, - std::shared_ptr atlas, - Point position, - Paint paint) { - if (!atlas || !atlas->IsValid()) { - return; - } +void Canvas::DrawTextFrame(TextFrame text_frame, Point position, Paint paint) { + auto lazy_glyph_atlas = GetCurrentPass().GetLazyGlyphAtlas(); + + lazy_glyph_atlas->AddTextFrame(std::move(text_frame)); auto text_contents = std::make_shared(); text_contents->SetTextFrame(std::move(text_frame)); - text_contents->SetGlyphAtlas(std::move(atlas)); + text_contents->SetGlyphAtlas(std::move(lazy_glyph_atlas)); text_contents->SetColor(paint.color.Premultiply()); Entity entity; diff --git a/impeller/aiks/canvas.h b/impeller/aiks/canvas.h index c407915953abf..06ebb0f88fa1b 100644 --- a/impeller/aiks/canvas.h +++ b/impeller/aiks/canvas.h @@ -78,10 +78,7 @@ class Canvas { void DrawPicture(Picture picture); - void DrawTextFrame(TextFrame text_frame, - std::shared_ptr atlas, - Point position, - Paint paint); + void DrawTextFrame(TextFrame text_frame, Point position, Paint paint); Picture EndRecordingAsPicture(); diff --git a/impeller/display_list/BUILD.gn b/impeller/display_list/BUILD.gn index ca5a54bc253b0..e3f51afb3554a 100644 --- a/impeller/display_list/BUILD.gn +++ b/impeller/display_list/BUILD.gn @@ -10,7 +10,7 @@ impeller_component("display_list") { "display_list_dispatcher.h", ] - deps = [ + public_deps = [ "../aiks", "//flutter/display_list", "//flutter/fml", @@ -21,7 +21,11 @@ impeller_component("display_list") { impeller_component("display_list_unittests") { testonly = true - sources = [ "display_list_unittests.cc" ] + sources = [ + "display_list_playground.cc", + "display_list_playground.h", + "display_list_unittests.cc", + ] deps = [ ":display_list", diff --git a/impeller/display_list/display_list_dispatcher.cc b/impeller/display_list/display_list_dispatcher.cc index 5d1bf440d703b..b1e23010cda72 100644 --- a/impeller/display_list/display_list_dispatcher.cc +++ b/impeller/display_list/display_list_dispatcher.cc @@ -454,7 +454,8 @@ void DisplayListDispatcher::clipPath(const SkPath& path, } // |flutter::Dispatcher| -void DisplayListDispatcher::drawColor(SkColor color, flutter::DlBlendMode dl_mode) { +void DisplayListDispatcher::drawColor(SkColor color, + flutter::DlBlendMode dl_mode) { Paint paint; paint.color = ToColor(color); if (auto mode = ToBlendMode(dl_mode); mode.has_value()) { @@ -608,7 +609,10 @@ void DisplayListDispatcher::drawDisplayList( void DisplayListDispatcher::drawTextBlob(const sk_sp blob, SkScalar x, SkScalar y) { - UNIMPLEMENTED; + canvas_.DrawTextFrame(TextFrameFromTextBlob(blob), // + impeller::Point{x, y}, // + paint_ // + ); } // |flutter::Dispatcher| diff --git a/impeller/display_list/display_list_playground.cc b/impeller/display_list/display_list_playground.cc new file mode 100644 index 0000000000000..4bbcb07fe410e --- /dev/null +++ b/impeller/display_list/display_list_playground.cc @@ -0,0 +1,71 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "impeller/display_list/display_list_playground.h" + +#include "flutter/testing/testing.h" +#include "impeller/aiks/aiks_context.h" +#include "impeller/display_list/display_list_dispatcher.h" +#include "third_party/skia/include/core/SkData.h" + +namespace impeller { + +DisplayListPlayground::DisplayListPlayground() = default; + +DisplayListPlayground::~DisplayListPlayground() = default; + +bool DisplayListPlayground::OpenPlaygroundHere( + flutter::DisplayListBuilder& builder) { + return OpenPlaygroundHere(builder.Build()); +} + +bool DisplayListPlayground::OpenPlaygroundHere( + sk_sp list) { + if (!Playground::is_enabled()) { + return true; + } + + if (!list) { + return false; + } + + DisplayListDispatcher dispatcher; + list->Dispatch(dispatcher); + auto picture = dispatcher.EndRecordingAsPicture(); + + AiksContext context(GetContext()); + if (!context.IsValid()) { + return false; + } + return Playground::OpenPlaygroundHere( + [&picture, &context](RenderPass& pass) -> bool { + return context.Render(picture, pass); + }); +} + +static sk_sp OpenFixtureAsSkData(const char* fixture_name) { + auto mapping = flutter::testing::OpenFixtureAsMapping(fixture_name); + if (!mapping) { + return nullptr; + } + return SkData::MakeWithProc( + mapping->GetMapping(), mapping->GetSize(), + [](const void* ptr, void* context) { + delete reinterpret_cast(context); + }, + mapping.release()); +} + +SkFont DisplayListPlayground::CreateTestFontOfSize(SkScalar scalar) { + static constexpr const char* kTestFontFixture = "Roboto-Regular.ttf"; + auto mapping = OpenFixtureAsSkData(kTestFontFixture); + FML_CHECK(mapping); + return SkFont{SkTypeface::MakeFromData(mapping), scalar}; +} + +SkFont DisplayListPlayground::CreateTestFont() { + return CreateTestFontOfSize(50); +} + +} // namespace impeller diff --git a/impeller/display_list/display_list_playground.h b/impeller/display_list/display_list_playground.h new file mode 100644 index 0000000000000..f0028a50acf2b --- /dev/null +++ b/impeller/display_list/display_list_playground.h @@ -0,0 +1,33 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#pragma once + +#include "flutter/display_list/display_list.h" +#include "flutter/display_list/display_list_builder.h" +#include "flutter/fml/macros.h" +#include "impeller/playground/playground.h" +#include "third_party/skia/include/core/SkFont.h" + +namespace impeller { + +class DisplayListPlayground : public Playground { + public: + DisplayListPlayground(); + + ~DisplayListPlayground(); + + bool OpenPlaygroundHere(flutter::DisplayListBuilder& builder); + + bool OpenPlaygroundHere(sk_sp list); + + SkFont CreateTestFontOfSize(SkScalar scalar); + + SkFont CreateTestFont(); + + private: + FML_DISALLOW_COPY_AND_ASSIGN(DisplayListPlayground); +}; + +} // namespace impeller diff --git a/impeller/display_list/display_list_unittests.cc b/impeller/display_list/display_list_unittests.cc index 4a1ad3f52f11c..6fef7b37f5c89 100644 --- a/impeller/display_list/display_list_unittests.cc +++ b/impeller/display_list/display_list_unittests.cc @@ -2,12 +2,29 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. +#include "flutter/display_list/display_list_builder.h" #include "flutter/testing/testing.h" +#include "impeller/display_list/display_list_playground.h" namespace impeller { namespace testing { -TEST(DisplayListTest, CanCreateDisatcher) {} +using DisplayListTest = DisplayListPlayground; + +TEST_F(DisplayListTest, CanDrawRect) { + flutter::DisplayListBuilder builder; + builder.setColor(SK_ColorBLUE); + builder.drawRect(SkRect::MakeXYWH(10, 10, 100, 100)); + ASSERT_TRUE(OpenPlaygroundHere(builder.Build())); +} + +TEST_F(DisplayListTest, CanDrawTextBlob) { + flutter::DisplayListBuilder builder; + builder.setColor(SK_ColorBLUE); + builder.drawTextBlob(SkTextBlob::MakeFromString("Hello", CreateTestFont()), + 100, 100); + ASSERT_TRUE(OpenPlaygroundHere(builder.Build())); +} } // namespace testing } // namespace impeller diff --git a/impeller/entity/contents/text_contents.cc b/impeller/entity/contents/text_contents.cc index c58855039f31d..8b4e698183025 100644 --- a/impeller/entity/contents/text_contents.cc +++ b/impeller/entity/contents/text_contents.cc @@ -2,7 +2,7 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -#include "text_contents.h" +#include "impeller/entity/contents/text_contents.h" #include "impeller/entity/contents/content_context.h" #include "impeller/entity/entity.h" @@ -11,8 +11,10 @@ #include "impeller/renderer/sampler_library.h" #include "impeller/tessellator/tessellator.h" #include "impeller/typographer/glyph_atlas.h" +#include "impeller/typographer/lazy_glyph_atlas.h" namespace impeller { + TextContents::TextContents() = default; TextContents::~TextContents() = default; @@ -25,6 +27,23 @@ void TextContents::SetGlyphAtlas(std::shared_ptr atlas) { atlas_ = std::move(atlas); } +void TextContents::SetGlyphAtlas(std::shared_ptr atlas) { + atlas_ = std::move(atlas); +} + +std::shared_ptr TextContents::ResolveAtlas( + std::shared_ptr context) const { + if (auto lazy_atlas = std::get_if>(&atlas_)) { + return lazy_atlas->get()->CreateOrGetGlyphAtlas(context); + } + + if (auto atlas = std::get_if>(&atlas_)) { + return *atlas; + } + + return nullptr; +} + void TextContents::SetColor(Color color) { color_ = color; } @@ -36,7 +55,9 @@ bool TextContents::Render(const ContentContext& renderer, return true; } - if (!atlas_ || !atlas_->IsValid()) { + auto atlas = ResolveAtlas(renderer.GetContext()); + + if (!atlas || !atlas->IsValid()) { VALIDATION_LOG << "Cannot render glyphs without prepared atlas."; return false; } @@ -57,15 +78,15 @@ bool TextContents::Render(const ContentContext& renderer, frame_info.mvp = Matrix::MakeOrthographic(pass.GetRenderTargetSize()) * entity.GetTransformation(); frame_info.atlas_size = - Point{static_cast(atlas_->GetTexture()->GetSize().width), - static_cast(atlas_->GetTexture()->GetSize().height)}; + Point{static_cast(atlas->GetTexture()->GetSize().width), + static_cast(atlas->GetTexture()->GetSize().height)}; frame_info.text_color = ToVector(color_); VS::BindFrameInfo(cmd, pass.GetTransientsBuffer().EmplaceUniform(frame_info)); // Common fragment uniforms for all glyphs. FS::BindGlyphAtlasSampler( cmd, // command - atlas_->GetTexture(), // texture + atlas->GetTexture(), // texture renderer.GetContext()->GetSamplerLibrary()->GetSampler({}) // sampler ); @@ -107,7 +128,7 @@ bool TextContents::Render(const ContentContext& renderer, // Draw each glyph individually. This should probably be batched. for (const auto& glyph_position : run.GetGlyphPositions()) { FontGlyphPair font_glyph_pair{font, glyph_position.glyph}; - auto atlas_glyph_pos = atlas_->FindFontGlyphPosition(font_glyph_pair); + auto atlas_glyph_pos = atlas->FindFontGlyphPosition(font_glyph_pair); if (!atlas_glyph_pos.has_value()) { VALIDATION_LOG << "Could not find glyph position in the atlas."; return false; diff --git a/impeller/entity/contents/text_contents.h b/impeller/entity/contents/text_contents.h index 370bdd1973d2e..238de9a43e77e 100644 --- a/impeller/entity/contents/text_contents.h +++ b/impeller/entity/contents/text_contents.h @@ -6,6 +6,7 @@ #include #include +#include #include #include "flutter/fml/macros.h" @@ -16,6 +17,8 @@ namespace impeller { class GlyphAtlas; +class LazyGlyphAtlas; +class Context; class TextContents final : public Contents { public: @@ -27,6 +30,8 @@ class TextContents final : public Contents { void SetGlyphAtlas(std::shared_ptr atlas); + void SetGlyphAtlas(std::shared_ptr atlas); + void SetColor(Color color); // |Contents| @@ -37,7 +42,12 @@ class TextContents final : public Contents { private: TextFrame frame_; Color color_; - std::shared_ptr atlas_; + mutable std::variant, + std::shared_ptr> + atlas_; + + std::shared_ptr ResolveAtlas( + std::shared_ptr context) const; FML_DISALLOW_COPY_AND_ASSIGN(TextContents); }; diff --git a/impeller/entity/entity_pass.cc b/impeller/entity/entity_pass.cc index 6840c2b30e896..914da3ebedcd7 100644 --- a/impeller/entity/entity_pass.cc +++ b/impeller/entity/entity_pass.cc @@ -44,6 +44,10 @@ size_t EntityPass::GetSubpassesDepth() const { return max_subpass_depth + 1u; } +const std::shared_ptr& EntityPass::GetLazyGlyphAtlas() const { + return lazy_glyph_atlas_; +} + std::optional EntityPass::GetEntitiesCoverage() const { std::optional result; for (const auto& entity : entities_) { diff --git a/impeller/entity/entity_pass.h b/impeller/entity/entity_pass.h index e2305f92fcd28..f4dc5cf424d61 100644 --- a/impeller/entity/entity_pass.h +++ b/impeller/entity/entity_pass.h @@ -13,6 +13,7 @@ #include "impeller/entity/entity.h" #include "impeller/entity/entity_pass_delegate.h" #include "impeller/renderer/render_target.h" +#include "impeller/typographer/lazy_glyph_atlas.h" namespace impeller { @@ -41,6 +42,8 @@ class EntityPass { const Subpasses& GetSubpasses() const; + const std::shared_ptr& GetLazyGlyphAtlas() const; + EntityPass* AddSubpass(std::unique_ptr pass); EntityPass* GetSuperpass() const; @@ -61,6 +64,8 @@ class EntityPass { size_t stencil_depth_ = 0u; std::unique_ptr delegate_ = EntityPassDelegate::MakeDefault(); + std::shared_ptr lazy_glyph_atlas_ = + std::make_shared(); std::optional GetSubpassCoverage(const EntityPass& subpass) const; diff --git a/impeller/typographer/BUILD.gn b/impeller/typographer/BUILD.gn index 1c10a77a8deaf..2c0a21fa90c7f 100644 --- a/impeller/typographer/BUILD.gn +++ b/impeller/typographer/BUILD.gn @@ -20,6 +20,8 @@ impeller_component("typographer") { "glyph.h", "glyph_atlas.cc", "glyph_atlas.h", + "lazy_glyph_atlas.cc", + "lazy_glyph_atlas.h", "text_frame.cc", "text_frame.h", "text_render_context.cc", diff --git a/impeller/typographer/backends/skia/text_render_context_skia.cc b/impeller/typographer/backends/skia/text_render_context_skia.cc index ca7b93bdad01e..3c67bea2612b2 100644 --- a/impeller/typographer/backends/skia/text_render_context_skia.cc +++ b/impeller/typographer/backends/skia/text_render_context_skia.cc @@ -25,22 +25,24 @@ TextRenderContextSkia::TextRenderContextSkia(std::shared_ptr context) TextRenderContextSkia::~TextRenderContextSkia() = default; static FontGlyphPair::Set CollectUniqueFontGlyphPairsSet( - const std::vector& runs) { + TextRenderContext::FrameIterator frame_iterator) { FontGlyphPair::Set set; - for (const auto& run : runs) { - auto font = run.GetFont(); - for (const auto& glyph_position : run.GetGlyphPositions()) { - set.insert({font, glyph_position.glyph}); + while (auto frame = frame_iterator()) { + for (const auto& run : frame->GetRuns()) { + auto font = run.GetFont(); + for (const auto& glyph_position : run.GetGlyphPositions()) { + set.insert({font, glyph_position.glyph}); + } } } return set; } static FontGlyphPair::Vector CollectUniqueFontGlyphPairs( - const std::vector& runs) { + TextRenderContext::FrameIterator frame_iterator) { TRACE_EVENT0("impeller", __FUNCTION__); FontGlyphPair::Vector vector; - auto set = CollectUniqueFontGlyphPairsSet(runs); + auto set = CollectUniqueFontGlyphPairsSet(frame_iterator); vector.reserve(set.size()); for (const auto& item : set) { vector.emplace_back(std::move(item)); @@ -202,7 +204,7 @@ static std::shared_ptr UploadGlyphTextureAtlas( } std::shared_ptr TextRenderContextSkia::CreateGlyphAtlas( - const TextFrame& frame) const { + FrameIterator frame_iterator) const { TRACE_EVENT0("impeller", __FUNCTION__); if (!IsValid()) { return nullptr; @@ -213,7 +215,8 @@ std::shared_ptr TextRenderContextSkia::CreateGlyphAtlas( // --------------------------------------------------------------------------- // Step 1: Collect unique font-glyph pairs in the frame. // --------------------------------------------------------------------------- - auto font_glyph_pairs = CollectUniqueFontGlyphPairs(frame.GetRuns()); + + auto font_glyph_pairs = CollectUniqueFontGlyphPairs(frame_iterator); if (font_glyph_pairs.empty()) { return glyph_atlas; } diff --git a/impeller/typographer/backends/skia/text_render_context_skia.h b/impeller/typographer/backends/skia/text_render_context_skia.h index 936b64bfde448..26dfb19a80788 100644 --- a/impeller/typographer/backends/skia/text_render_context_skia.h +++ b/impeller/typographer/backends/skia/text_render_context_skia.h @@ -17,7 +17,7 @@ class TextRenderContextSkia : public TextRenderContext { // |TextRenderContext| std::shared_ptr CreateGlyphAtlas( - const TextFrame& frame) const override; + FrameIterator iterator) const override; private: FML_DISALLOW_COPY_AND_ASSIGN(TextRenderContextSkia); diff --git a/impeller/typographer/lazy_glyph_atlas.cc b/impeller/typographer/lazy_glyph_atlas.cc new file mode 100644 index 0000000000000..e2b2631b926e3 --- /dev/null +++ b/impeller/typographer/lazy_glyph_atlas.cc @@ -0,0 +1,49 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "impeller/typographer/lazy_glyph_atlas.h" + +#include "impeller/base/validation.h" +#include "impeller/typographer/text_render_context.h" + +namespace impeller { + +LazyGlyphAtlas::LazyGlyphAtlas() = default; + +LazyGlyphAtlas::~LazyGlyphAtlas() = default; + +void LazyGlyphAtlas::AddTextFrame(TextFrame frame) { + FML_DCHECK(!atlas_); + frames_.emplace_back(std::move(frame)); +} + +std::shared_ptr LazyGlyphAtlas::CreateOrGetGlyphAtlas( + std::shared_ptr context) const { + if (atlas_) { + return atlas_; + } + + auto text_context = TextRenderContext::Create(std::move(context)); + if (!text_context || !text_context->IsValid()) { + return nullptr; + } + size_t i = 0; + TextRenderContext::FrameIterator iterator = [&]() -> const TextFrame* { + if (i >= frames_.size()) { + return nullptr; + } + const auto& result = frames_[i]; + i++; + return &result; + }; + auto atlas = text_context->CreateGlyphAtlas(iterator); + if (!atlas || !atlas->IsValid()) { + VALIDATION_LOG << "Could not create valid atlas."; + return nullptr; + } + atlas_ = std::move(atlas); + return atlas_; +} + +} // namespace impeller diff --git a/impeller/typographer/lazy_glyph_atlas.h b/impeller/typographer/lazy_glyph_atlas.h new file mode 100644 index 0000000000000..8d36e05cc4d03 --- /dev/null +++ b/impeller/typographer/lazy_glyph_atlas.h @@ -0,0 +1,32 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#pragma once + +#include "flutter/fml/macros.h" +#include "impeller/renderer/context.h" +#include "impeller/typographer/glyph_atlas.h" +#include "impeller/typographer/text_frame.h" + +namespace impeller { + +class LazyGlyphAtlas { + public: + LazyGlyphAtlas(); + + ~LazyGlyphAtlas(); + + void AddTextFrame(TextFrame frame); + + std::shared_ptr CreateOrGetGlyphAtlas( + std::shared_ptr context) const; + + private: + std::vector frames_; + mutable std::shared_ptr atlas_; + + FML_DISALLOW_COPY_AND_ASSIGN(LazyGlyphAtlas); +}; + +} // namespace impeller diff --git a/impeller/typographer/text_render_context.cc b/impeller/typographer/text_render_context.cc index 98b74047670f9..beecdaae51699 100644 --- a/impeller/typographer/text_render_context.cc +++ b/impeller/typographer/text_render_context.cc @@ -4,8 +4,16 @@ #include "impeller/typographer/text_render_context.h" +#include "impeller/typographer/backends/skia/text_render_context_skia.h" + namespace impeller { +std::unique_ptr TextRenderContext::Create( + std::shared_ptr context) { + // There is only one backend today. + return std::make_unique(std::move(context)); +} + TextRenderContext::TextRenderContext(std::shared_ptr context) : context_(std::move(context)) { if (!context_ || !context_->IsValid()) { @@ -24,4 +32,17 @@ const std::shared_ptr& TextRenderContext::GetContext() const { return context_; } +std::shared_ptr TextRenderContext::CreateGlyphAtlas( + const TextFrame& frame) const { + size_t count = 0; + FrameIterator iterator = [&]() -> const TextFrame* { + count++; + if (count == 1) { + return &frame; + } + return nullptr; + }; + return CreateGlyphAtlas(iterator); +} + } // namespace impeller diff --git a/impeller/typographer/text_render_context.h b/impeller/typographer/text_render_context.h index 8d5ac6fa133ab..c725c03c5b128 100644 --- a/impeller/typographer/text_render_context.h +++ b/impeller/typographer/text_render_context.h @@ -4,6 +4,7 @@ #pragma once +#include #include #include "flutter/fml/macros.h" @@ -19,18 +20,11 @@ namespace impeller { /// This is necessary to create and reference resources related to /// rendering text on the GPU. /// -/// It is caller responsibility to create as few of these and keep -/// these around for as long possible. /// class TextRenderContext { public: - //---------------------------------------------------------------------------- - /// @brief Create a new context to render text that talks to an - /// underlying graphics context. - /// - /// @param[in] context The graphics context - /// - TextRenderContext(std::shared_ptr context); + static std::unique_ptr Create( + std::shared_ptr context); virtual ~TextRenderContext(); @@ -43,15 +37,20 @@ class TextRenderContext { /// const std::shared_ptr& GetContext() const; + using FrameIterator = std::function; + virtual std::shared_ptr CreateGlyphAtlas( + FrameIterator iterator) const = 0; + + std::shared_ptr CreateGlyphAtlas(const TextFrame& frame) const; + + protected: //---------------------------------------------------------------------------- - /// @brief Create a new glyph atlas for the specified text frame. - /// - /// @param[in] frame The text frame + /// @brief Create a new context to render text that talks to an + /// underlying graphics context. /// - /// @return A valid glyph atlas or null. + /// @param[in] context The graphics context /// - virtual std::shared_ptr CreateGlyphAtlas( - const TextFrame& frame) const = 0; + TextRenderContext(std::shared_ptr context); private: std::shared_ptr context_; diff --git a/impeller/typographer/typographer_unittests.cc b/impeller/typographer/typographer_unittests.cc index 1f59d633bc7dd..0cb41841aec25 100644 --- a/impeller/typographer/typographer_unittests.cc +++ b/impeller/typographer/typographer_unittests.cc @@ -27,13 +27,13 @@ TEST_F(TypographerTest, CanConvertTextBlob) { } TEST_F(TypographerTest, CanCreateRenderContext) { - auto context = std::make_shared(GetContext()); - ASSERT_TRUE(context->IsValid()); + auto context = TextRenderContext::Create(GetContext()); + ASSERT_TRUE(context && context->IsValid()); } TEST_F(TypographerTest, CanCreateGlyphAtlas) { - auto context = std::make_shared(GetContext()); - ASSERT_TRUE(context->IsValid()); + auto context = TextRenderContext::Create(GetContext()); + ASSERT_TRUE(context && context->IsValid()); SkFont sk_font; auto blob = SkTextBlob::MakeFromString("hello", sk_font); ASSERT_TRUE(blob); From 370da62128d4a6315e706527e19c655ee4fc7a92 Mon Sep 17 00:00:00 2001 From: Chinmay Garde Date: Tue, 22 Mar 2022 14:35:26 -0700 Subject: [PATCH 367/433] Add support for instanced rendering and shader storage buffers. (#95) --- impeller/compiler/code_gen_template.h | 16 ++++-- impeller/compiler/reflector.cc | 45 +++++++++++++-- impeller/fixtures/BUILD.gn | 2 + impeller/fixtures/instanced_draw.frag | 11 ++++ impeller/fixtures/instanced_draw.vert | 24 ++++++++ .../renderer/backend/metal/render_pass_mtl.mm | 2 +- impeller/renderer/command.h | 1 + impeller/renderer/host_buffer.h | 23 ++++++++ impeller/renderer/renderer_unittests.cc | 57 +++++++++++++++++++ 9 files changed, 169 insertions(+), 12 deletions(-) create mode 100644 impeller/fixtures/instanced_draw.frag create mode 100644 impeller/fixtures/instanced_draw.vert diff --git a/impeller/compiler/code_gen_template.h b/impeller/compiler/code_gen_template.h index 20afb9e2714f6..835c2b8cc4c28 100644 --- a/impeller/compiler/code_gen_template.h +++ b/impeller/compiler/code_gen_template.h @@ -44,16 +44,16 @@ struct {{camel_case(shader_name)}}{{camel_case(shader_stage)}}Shader { }; // struct {{def.name}} (size {{def.byte_length}}) {% endfor %} {% endif %} -{% if length(uniform_buffers) > 0 %} +{% if length(buffers) > 0 %} // =========================================================================== - // Stage Uniforms ============================================================ + // Stage Uniform & Storage Buffers =========================================== // =========================================================================== -{% for uniform in uniform_buffers %} +{% for buffer in buffers %} - static constexpr auto kResource{{camel_case(uniform.name)}} = ShaderUniformSlot<{{uniform.name}}> { // {{uniform.name}} - "{{uniform.name}}", // name - {{uniform.msl_res_0}}u, // binding + static constexpr auto kResource{{camel_case(buffer.name)}} = ShaderUniformSlot<{{buffer.name}}> { // {{buffer.name}} + "{{buffer.name}}", // name + {{buffer.msl_res_0}}u, // binding }; {% endfor %} {% endif %} @@ -119,6 +119,10 @@ struct {{camel_case(shader_name)}}{{camel_case(shader_stage)}}Shader { }; {% endif %} + // =========================================================================== + // Resource Binding Utilities ================================================ + // =========================================================================== + {% for proto in bind_prototypes %} /// {{proto.docstring}} static {{proto.return_type}} Bind{{proto.name}}({% for arg in proto.args %} diff --git a/impeller/compiler/reflector.cc b/impeller/compiler/reflector.cc index b0f91d70e39ee..d7786dcc7d904 100644 --- a/impeller/compiler/reflector.cc +++ b/impeller/compiler/reflector.cc @@ -166,11 +166,27 @@ std::optional Reflector::GenerateTemplateArguments() const { const auto shader_resources = compiler_->get_shader_resources(); - if (auto uniform_buffers = ReflectResources(shader_resources.uniform_buffers); - uniform_buffers.has_value()) { - root["uniform_buffers"] = std::move(uniform_buffers.value()); - } else { - return std::nullopt; + // Uniform and storage buffers. + { + auto& buffers = root["buffers"] = nlohmann::json::array_t{}; + if (auto uniform_buffers_json = + ReflectResources(shader_resources.uniform_buffers); + uniform_buffers_json.has_value()) { + for (const auto& uniform_buffer : uniform_buffers_json.value()) { + buffers.emplace_back(std::move(uniform_buffer)); + } + } else { + return std::nullopt; + } + if (auto storage_buffers_json = + ReflectResources(shader_resources.storage_buffers); + storage_buffers_json.has_value()) { + for (const auto& uniform_buffer : storage_buffers_json.value()) { + buffers.emplace_back(std::move(uniform_buffer)); + } + } else { + return std::nullopt; + } } { @@ -742,6 +758,25 @@ std::vector Reflector::ReflectBindPrototypes( .argument_name = "view", }); } + for (const auto& storage_buffer : resources.storage_buffers) { + auto& proto = prototypes.emplace_back(BindPrototype{}); + proto.return_type = "bool"; + proto.name = ConvertToCamelCase(storage_buffer.name); + { + std::stringstream stream; + stream << "Bind storage buffer for resource named " << storage_buffer.name + << "."; + proto.docstring = stream.str(); + } + proto.args.push_back(BindPrototypeArgument{ + .type_name = "Command&", + .argument_name = "command", + }); + proto.args.push_back(BindPrototypeArgument{ + .type_name = "BufferView", + .argument_name = "view", + }); + } for (const auto& sampled_image : resources.sampled_images) { auto& proto = prototypes.emplace_back(BindPrototype{}); proto.return_type = "bool"; diff --git a/impeller/fixtures/BUILD.gn b/impeller/fixtures/BUILD.gn index db56ad66fb679..07bbc65499781 100644 --- a/impeller/fixtures/BUILD.gn +++ b/impeller/fixtures/BUILD.gn @@ -12,6 +12,8 @@ impeller_shaders("shader_fixtures") { shaders = [ "box_fade.vert", "box_fade.frag", + "instanced_draw.vert", + "instanced_draw.frag", "test_texture.vert", "test_texture.frag", ] diff --git a/impeller/fixtures/instanced_draw.frag b/impeller/fixtures/instanced_draw.frag new file mode 100644 index 0000000000000..ccad5b7d3e141 --- /dev/null +++ b/impeller/fixtures/instanced_draw.frag @@ -0,0 +1,11 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +in vec4 v_color; + +out vec4 frag_color; + +void main() { + frag_color = v_color; +} diff --git a/impeller/fixtures/instanced_draw.vert b/impeller/fixtures/instanced_draw.vert new file mode 100644 index 0000000000000..ff35d518e7221 --- /dev/null +++ b/impeller/fixtures/instanced_draw.vert @@ -0,0 +1,24 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +uniform FrameInfo { + mat4 mvp; +} frame_info; + +readonly buffer InstanceInfo { + vec4 colors[]; +} instance_info; + +in vec2 vtx; + +out vec4 v_color; + +void main () { + gl_Position = frame_info.mvp * + vec4(vtx.x + 105.0 * gl_InstanceIndex, + vtx.y + 105.0 * gl_InstanceIndex, + 0.0, + 1.0); + v_color = instance_info.colors[gl_InstanceIndex]; +} diff --git a/impeller/renderer/backend/metal/render_pass_mtl.mm b/impeller/renderer/backend/metal/render_pass_mtl.mm index 937bca3dd0f75..ac55199e1ca36 100644 --- a/impeller/renderer/backend/metal/render_pass_mtl.mm +++ b/impeller/renderer/backend/metal/render_pass_mtl.mm @@ -482,7 +482,7 @@ static bool Bind(PassBindingsCache& pass, indexType:ToMTLIndexType(command.index_type) indexBuffer:mtl_index_buffer indexBufferOffset:command.index_buffer.range.offset - instanceCount:1u + instanceCount:command.instance_count baseVertex:command.base_vertex baseInstance:0u]; } diff --git a/impeller/renderer/command.h b/impeller/renderer/command.h index 3fdb0ee1716bd..d0299c4d5d31a 100644 --- a/impeller/renderer/command.h +++ b/impeller/renderer/command.h @@ -90,6 +90,7 @@ struct Command { /// If unset, no scissor is applied. /// std::optional scissor; + size_t instance_count = 1u; bool BindVertices(const VertexBuffer& buffer); diff --git a/impeller/renderer/host_buffer.h b/impeller/renderer/host_buffer.h index 82be86cbcba0f..91eba4fbcdb83 100644 --- a/impeller/renderer/host_buffer.h +++ b/impeller/renderer/host_buffer.h @@ -49,6 +49,29 @@ class HostBuffer final : public std::enable_shared_from_this, ); } + //---------------------------------------------------------------------------- + /// @brief Emplace storage buffer data onto the host buffer. Ensure that + /// backend specific uniform alignment requirements are respected. + /// + /// @param[in] uniform The storage buffer to emplace onto the buffer. + /// + /// @tparam StorageBufferType The type of the shader storage buffer. + /// + /// @return The buffer view. + /// + template < + class StorageBufferType, + class = std::enable_if_t>> + [[nodiscard]] BufferView EmplaceStorageBuffer( + const std::vector& buffer) { + const auto alignment = + std::max(alignof(StorageBufferType), DefaultUniformAlignment()); + return Emplace(buffer.data(), // buffer + buffer.size() * sizeof(StorageBufferType), // size + alignment // alignment + ); + } + //---------------------------------------------------------------------------- /// @brief Emplace non-uniform data (like contiguous vertices) onto the /// host buffer. diff --git a/impeller/renderer/renderer_unittests.cc b/impeller/renderer/renderer_unittests.cc index d4a3482416380..40fa47ecadf0d 100644 --- a/impeller/renderer/renderer_unittests.cc +++ b/impeller/renderer/renderer_unittests.cc @@ -6,6 +6,8 @@ #include "flutter/testing/testing.h" #include "impeller/fixtures/box_fade.frag.h" #include "impeller/fixtures/box_fade.vert.h" +#include "impeller/fixtures/instanced_draw.frag.h" +#include "impeller/fixtures/instanced_draw.vert.h" #include "impeller/fixtures/test_texture.frag.h" #include "impeller/fixtures/test_texture.vert.h" #include "impeller/geometry/path_builder.h" @@ -270,5 +272,60 @@ TEST_F(RendererTest, CanRenderToTexture) { ASSERT_TRUE(r2t_pass->EncodeCommands(*context->GetTransientsAllocator())); } +TEST_F(RendererTest, CanRenderInstanced) { + using VS = InstancedDrawVertexShader; + using FS = InstancedDrawFragmentShader; + + VertexBufferBuilder builder; + + ASSERT_TRUE( + Tessellator{}.Tessellate(FillType::kPositive, + PathBuilder{} + .AddRect(Rect::MakeXYWH(10, 10, 100, 100)) + .TakePath() + .CreatePolyline(), + [&builder](Point vtx) { + VS::PerVertexData data; + data.vtx = vtx; + builder.AppendVertex(data); + })); + + auto pipeline = + GetContext() + ->GetPipelineLibrary() + ->GetRenderPipeline( + PipelineBuilder::MakeDefaultPipelineDescriptor( + *GetContext()) + ->SetSampleCount(SampleCount::kCount4)) + .get(); + ASSERT_TRUE(pipeline && pipeline->IsValid()); + + Command cmd; + cmd.pipeline = pipeline; + cmd.label = "InstancedDraw"; + + static constexpr size_t kInstancesCount = 5u; + std::vector instances; + for (size_t i = 0; i < kInstancesCount; i++) { + VS::InstanceInfo info; + info.colors = Color::Random(); + instances.emplace_back(info); + } + + ASSERT_TRUE(OpenPlaygroundHere([&](RenderPass& pass) -> bool { + VS::FrameInfo frame_info; + frame_info.mvp = Matrix::MakeOrthographic(pass.GetRenderTargetSize()); + VS::BindFrameInfo(cmd, + pass.GetTransientsBuffer().EmplaceUniform(frame_info)); + VS::BindInstanceInfo( + cmd, pass.GetTransientsBuffer().EmplaceStorageBuffer(instances)); + cmd.BindVertices(builder.CreateVertexBuffer(pass.GetTransientsBuffer())); + + cmd.instance_count = kInstancesCount; + pass.AddCommand(cmd); + return true; + })); +} + } // namespace testing } // namespace impeller From e943d0dde5132163c590fd0b337187e4a89e47e7 Mon Sep 17 00:00:00 2001 From: Chinmay Garde Date: Tue, 22 Mar 2022 16:17:18 -0700 Subject: [PATCH 368/433] Use 8bpp glyph atlases. (#96) Also remove the debugging goop as that's only relevant if you can visualize the background. --- impeller/entity/shaders/glyph_atlas.frag | 2 +- .../backends/skia/text_render_context_skia.cc | 26 ++----------------- 2 files changed, 3 insertions(+), 25 deletions(-) diff --git a/impeller/entity/shaders/glyph_atlas.frag b/impeller/entity/shaders/glyph_atlas.frag index f07e591f19d10..f1dcc45686cb3 100644 --- a/impeller/entity/shaders/glyph_atlas.frag +++ b/impeller/entity/shaders/glyph_atlas.frag @@ -19,5 +19,5 @@ void main() { frag_color = texture( glyph_atlas_sampler, v_unit_vertex * scale_perspective + offset - ) * v_text_color; + ).rrrr * v_text_color; } diff --git a/impeller/typographer/backends/skia/text_render_context_skia.cc b/impeller/typographer/backends/skia/text_render_context_skia.cc index 3c67bea2612b2..94d686e0bdb80 100644 --- a/impeller/typographer/backends/skia/text_render_context_skia.cc +++ b/impeller/typographer/backends/skia/text_render_context_skia.cc @@ -105,7 +105,7 @@ static std::optional CreateAtlasBitmap(const GlyphAtlas& atlas, size_t atlas_size) { TRACE_EVENT0("impeller", __FUNCTION__); SkBitmap bitmap; - auto image_info = SkImageInfo::MakeN32Premul(atlas_size, atlas_size); + auto image_info = SkImageInfo::MakeA8(atlas_size, atlas_size); if (!bitmap.tryAllocPixels(image_info)) { return std::nullopt; } @@ -131,28 +131,6 @@ static std::optional CreateAtlasBitmap(const GlyphAtlas& atlas, auto glyph_color = SK_ColorWHITE; -#if 0 - { - glyph_color = SkColorSetARGB(255, // - std::rand() % 255, // - std::rand() % 255, // - std::rand() % 255 // - ); - SkPaint debug_paint; - debug_paint.setARGB(255 / 4, // - std::rand() % 255, // - std::rand() % 255, // - std::rand() % 255 // - ); - canvas->drawRect(SkRect::MakeXYWH(location.origin.x, // - location.origin.y, // - location.size.width, // - location.size.height // - ), - debug_paint); - } -#endif - SkPaint glyph_paint; glyph_paint.setColor(glyph_color); canvas->drawGlyphs(1u, // count @@ -181,7 +159,7 @@ static std::shared_ptr UploadGlyphTextureAtlas( const auto& pixmap = bitmap.pixmap(); TextureDescriptor texture_descriptor; - texture_descriptor.format = PixelFormat::kR8G8B8A8UNormInt; + texture_descriptor.format = PixelFormat::kR8UNormInt; texture_descriptor.size = ISize::MakeWH(atlas_size, atlas_size); if (pixmap.rowBytes() * pixmap.height() != From 634fdc23656d19c965a11a8183c51cd0db704615 Mon Sep 17 00:00:00 2001 From: Chinmay Garde Date: Tue, 22 Mar 2022 18:51:35 -0700 Subject: [PATCH 369/433] Draw all glyphs in a text run using a single draw call. (#97) --- impeller/entity/contents/text_contents.cc | 50 +++++++++++++------ impeller/entity/shaders/glyph_atlas.vert | 46 ++++++++++------- .../renderer/backend/metal/render_pass_mtl.mm | 9 ++++ 3 files changed, 73 insertions(+), 32 deletions(-) diff --git a/impeller/entity/contents/text_contents.cc b/impeller/entity/contents/text_contents.cc index 8b4e698183025..d61c0d6a0733f 100644 --- a/impeller/entity/contents/text_contents.cc +++ b/impeller/entity/contents/text_contents.cc @@ -67,7 +67,7 @@ bool TextContents::Render(const ContentContext& renderer, // Information shared by all glyph draw calls. Command cmd; - cmd.label = "Glyph"; + cmd.label = "TextRun"; cmd.primitive_type = PrimitiveType::kTriangle; cmd.pipeline = renderer.GetGlyphAtlasPipeline(OptionsFromPassAndEntity(pass, entity)); @@ -121,8 +121,20 @@ bool TextContents::Render(const ContentContext& renderer, cmd.BindVertices(std::move(vertex_buffer)); } + size_t instance_count = 0u; + std::vector glyph_positions; + std::vector glyph_sizes; + std::vector atlas_positions; + std::vector atlas_glyph_sizes; + // Iterate through all the runs in the blob. for (const auto& run : frame_.GetRuns()) { + instance_count = 0u; + glyph_positions.clear(); + glyph_sizes.clear(); + atlas_positions.clear(); + atlas_glyph_sizes.clear(); + auto font = run.GetFont(); auto glyph_size = ISize::Ceil(font.GetMetrics().GetBoundingBox().size); // Draw each glyph individually. This should probably be batched. @@ -133,21 +145,29 @@ bool TextContents::Render(const ContentContext& renderer, VALIDATION_LOG << "Could not find glyph position in the atlas."; return false; } + instance_count++; + glyph_positions.emplace_back(glyph_position.position.Translate( + {font.GetMetrics().min_extent.x, font.GetMetrics().ascent, 0.0})); + glyph_sizes.emplace_back(Point{static_cast(glyph_size.width), + static_cast(glyph_size.height)}); + atlas_positions.emplace_back(atlas_glyph_pos->origin); + atlas_glyph_sizes.emplace_back( + Point{atlas_glyph_pos->size.width, atlas_glyph_pos->size.height}); + } - VS::GlyphInfo glyph_info; - glyph_info.position = glyph_position.position.Translate( - {font.GetMetrics().min_extent.x, font.GetMetrics().ascent, 0.0}); - glyph_info.glyph_size = {static_cast(glyph_size.width), - static_cast(glyph_size.height)}; - glyph_info.atlas_position = atlas_glyph_pos->origin; - glyph_info.atlas_glyph_size = {atlas_glyph_pos->size.width, - atlas_glyph_pos->size.height}; - VS::BindGlyphInfo(cmd, - pass.GetTransientsBuffer().EmplaceUniform(glyph_info)); - - if (!pass.AddCommand(cmd)) { - return false; - } + cmd.instance_count = instance_count; + VS::BindGlyphPositions( + cmd, pass.GetTransientsBuffer().EmplaceStorageBuffer(glyph_positions)); + VS::BindGlyphSizes( + cmd, pass.GetTransientsBuffer().EmplaceStorageBuffer(glyph_sizes)); + VS::BindAtlasPositions( + cmd, pass.GetTransientsBuffer().EmplaceStorageBuffer(atlas_positions)); + VS::BindAtlasGlyphSizes( + cmd, + pass.GetTransientsBuffer().EmplaceStorageBuffer(atlas_glyph_sizes)); + + if (!pass.AddCommand(cmd)) { + return false; } } diff --git a/impeller/entity/shaders/glyph_atlas.vert b/impeller/entity/shaders/glyph_atlas.vert index aff822fdd4aad..9320a3465d0b4 100644 --- a/impeller/entity/shaders/glyph_atlas.vert +++ b/impeller/entity/shaders/glyph_atlas.vert @@ -8,12 +8,21 @@ uniform FrameInfo { vec4 text_color; } frame_info; -uniform GlyphInfo { - mat4 position; - vec2 glyph_size; - vec2 atlas_position; - vec2 atlas_glyph_size; -} glyph_info; +readonly buffer GlyphPositions { + mat4 position[]; +} glyph_positions; + +readonly buffer GlyphSizes { + vec2 size[]; +} glyph_sizes; + +readonly buffer AtlasPositions { + vec2 position[]; +} atlas_positions; + +readonly buffer AtlasGlyphSizes { + vec2 size[]; +} atlas_glyph_sizes; in vec2 unit_vertex; @@ -24,20 +33,23 @@ out vec2 v_atlas_size; out vec4 v_text_color; void main() { - mat4 scale = mat4( - glyph_info.glyph_size.x, 0.0, 0.0, 0.0, - 0.0, glyph_info.glyph_size.y, 0.0, 0.0, - 0.0, 0.0, 1.0 , 0.0, - 0.0, 0.0, 0.0 , 1.0 - ); + // The position to place the glyph. + mat4 glyph_position = glyph_positions.position[gl_InstanceIndex]; + // The size of the glyph. + vec2 glyph_size = glyph_sizes.size[gl_InstanceIndex]; + // The location of the glyph in the atlas. + vec2 glyph_atlas_position = atlas_positions.position[gl_InstanceIndex]; + // The size of the glyph within the atlas. + vec2 glyph_atlas_size = atlas_glyph_sizes.size[gl_InstanceIndex]; + gl_Position = frame_info.mvp - * glyph_info.position - * scale - * vec4(unit_vertex, 0.0, 1.0); + * glyph_position + * vec4(unit_vertex.x * glyph_size.x, + unit_vertex.y * glyph_size.y, 0.0, 1.0); v_unit_vertex = unit_vertex; - v_atlas_position = glyph_info.atlas_position; - v_atlas_glyph_size = glyph_info.atlas_glyph_size; + v_atlas_position = glyph_atlas_position; + v_atlas_glyph_size = glyph_atlas_size; v_atlas_size = frame_info.atlas_size; v_text_color = frame_info.text_color; } diff --git a/impeller/renderer/backend/metal/render_pass_mtl.mm b/impeller/renderer/backend/metal/render_pass_mtl.mm index ac55199e1ca36..895f1a15089b2 100644 --- a/impeller/renderer/backend/metal/render_pass_mtl.mm +++ b/impeller/renderer/backend/metal/render_pass_mtl.mm @@ -399,6 +399,9 @@ static bool Bind(PassBindingsCache& pass, if (command.index_count == 0u) { continue; } + if (command.instance_count == 0u) { + continue; + } fml::ScopedCleanupClosure auto_pop_debug_marker(pop_debug_marker); if (!command.label.empty()) { @@ -510,6 +513,12 @@ static bool Bind(PassBindingsCache& pass, return true; } + if (command.instance_count == 0u) { + // Essentially a no-op. Don't record the command but this is not necessary + // an error either. + return true; + } + commands_.emplace_back(std::move(command)); return true; } From e9df4f3cde0aca7a801421a2fd8dfad130a98703 Mon Sep 17 00:00:00 2001 From: Chinmay Garde Date: Wed, 23 Mar 2022 12:53:01 -0700 Subject: [PATCH 370/433] Batch all draw calls in the entire text frame. (#100) Also updates comments that were first written when this wasn't batched. For some reason, in the original implementation, I had assumed that styling information was not already in a uniform. --- impeller/entity/contents/text_contents.cc | 49 +++++++++-------------- 1 file changed, 19 insertions(+), 30 deletions(-) diff --git a/impeller/entity/contents/text_contents.cc b/impeller/entity/contents/text_contents.cc index d61c0d6a0733f..018cebb661eab 100644 --- a/impeller/entity/contents/text_contents.cc +++ b/impeller/entity/contents/text_contents.cc @@ -67,7 +67,7 @@ bool TextContents::Render(const ContentContext& renderer, // Information shared by all glyph draw calls. Command cmd; - cmd.label = "TextRun"; + cmd.label = "TextFrame"; cmd.primitive_type = PrimitiveType::kTriangle; cmd.pipeline = renderer.GetGlyphAtlasPipeline(OptionsFromPassAndEntity(pass, entity)); @@ -91,13 +91,11 @@ bool TextContents::Render(const ContentContext& renderer, ); // Common vertex information for all glyphs. - // Currently, glyphs are being drawn individually. This can be batched later. - // But we don't want to give each glyph unique vertex information. So all - // glyphs are given the same vertex information in the form of a unit-sized - // quad. The size of the glyph is specified in uniform data and the vertex - // shader uses this to size the glyph correctly. The interpolated vertex - // information is also used in the fragment shader to sample from the glyph - // atlas. + // All glyphs are given the same vertex information in the form of a + // unit-sized quad. The size of the glyph is specified in per instance data + // and the vertex shader uses this to size the glyph correctly. The + // interpolated vertex information is also used in the fragment shader to + // sample from the glyph atlas. { VertexBufferBuilder vertex_builder; if (!Tessellator{}.Tessellate( @@ -127,17 +125,9 @@ bool TextContents::Render(const ContentContext& renderer, std::vector atlas_positions; std::vector atlas_glyph_sizes; - // Iterate through all the runs in the blob. for (const auto& run : frame_.GetRuns()) { - instance_count = 0u; - glyph_positions.clear(); - glyph_sizes.clear(); - atlas_positions.clear(); - atlas_glyph_sizes.clear(); - auto font = run.GetFont(); auto glyph_size = ISize::Ceil(font.GetMetrics().GetBoundingBox().size); - // Draw each glyph individually. This should probably be batched. for (const auto& glyph_position : run.GetGlyphPositions()) { FontGlyphPair font_glyph_pair{font, glyph_position.glyph}; auto atlas_glyph_pos = atlas->FindFontGlyphPosition(font_glyph_pair); @@ -154,21 +144,20 @@ bool TextContents::Render(const ContentContext& renderer, atlas_glyph_sizes.emplace_back( Point{atlas_glyph_pos->size.width, atlas_glyph_pos->size.height}); } + } - cmd.instance_count = instance_count; - VS::BindGlyphPositions( - cmd, pass.GetTransientsBuffer().EmplaceStorageBuffer(glyph_positions)); - VS::BindGlyphSizes( - cmd, pass.GetTransientsBuffer().EmplaceStorageBuffer(glyph_sizes)); - VS::BindAtlasPositions( - cmd, pass.GetTransientsBuffer().EmplaceStorageBuffer(atlas_positions)); - VS::BindAtlasGlyphSizes( - cmd, - pass.GetTransientsBuffer().EmplaceStorageBuffer(atlas_glyph_sizes)); - - if (!pass.AddCommand(cmd)) { - return false; - } + cmd.instance_count = instance_count; + VS::BindGlyphPositions( + cmd, pass.GetTransientsBuffer().EmplaceStorageBuffer(glyph_positions)); + VS::BindGlyphSizes( + cmd, pass.GetTransientsBuffer().EmplaceStorageBuffer(glyph_sizes)); + VS::BindAtlasPositions( + cmd, pass.GetTransientsBuffer().EmplaceStorageBuffer(atlas_positions)); + VS::BindAtlasGlyphSizes( + cmd, pass.GetTransientsBuffer().EmplaceStorageBuffer(atlas_glyph_sizes)); + + if (!pass.AddCommand(cmd)) { + return false; } return true; From 2ecb8e8aa11fed8f7244bc7d0db5efb20d1a34ab Mon Sep 17 00:00:00 2001 From: Brandon DeRosier Date: Thu, 24 Mar 2022 11:49:59 -0700 Subject: [PATCH 371/433] Make the filter DAG render all textures at the correct resolution (#99) --- impeller/aiks/aiks_unittests.cc | 1 + impeller/entity/contents/contents.cc | 77 +++++++ impeller/entity/contents/contents.h | 30 +++ .../contents/filters/blend_filter_contents.cc | 125 +++++++----- .../contents/filters/blend_filter_contents.h | 13 +- .../contents/filters/filter_contents.cc | 191 ++++++++++-------- .../entity/contents/filters/filter_contents.h | 40 ++-- .../filters/gaussian_blur_filter_contents.cc | 81 ++++---- .../filters/gaussian_blur_filter_contents.h | 21 +- impeller/entity/contents/texture_contents.h | 1 - impeller/entity/entity.cc | 4 +- impeller/entity/entity_unittests.cc | 51 +++-- .../entity/shaders/texture_blend_screen.frag | 15 +- .../entity/shaders/texture_blend_screen.vert | 10 +- impeller/geometry/geometry_unittests.cc | 92 +++++++++ impeller/geometry/geometry_unittests.h | 20 ++ impeller/geometry/matrix.cc | 2 +- impeller/geometry/matrix.h | 44 +++- impeller/geometry/point.h | 2 + impeller/geometry/rect.h | 27 +++ impeller/geometry/scalar.h | 4 +- impeller/geometry/size.h | 4 + 22 files changed, 609 insertions(+), 246 deletions(-) diff --git a/impeller/aiks/aiks_unittests.cc b/impeller/aiks/aiks_unittests.cc index 5ea7fb9f5507b..23fd0d4dfafc5 100644 --- a/impeller/aiks/aiks_unittests.cc +++ b/impeller/aiks/aiks_unittests.cc @@ -442,6 +442,7 @@ TEST_F(AiksTest, TransformMultipliesCorrectly) { -3, 0, 0, 0, 0, 0, 0, 0, -500, 400, 0, 1)); + // clang-format on } } // namespace testing diff --git a/impeller/entity/contents/contents.cc b/impeller/entity/contents/contents.cc index 43116365d9e45..4f0ba354c4ca7 100644 --- a/impeller/entity/contents/contents.cc +++ b/impeller/entity/contents/contents.cc @@ -3,8 +3,10 @@ // found in the LICENSE file. #include "impeller/entity/contents/contents.h" +#include #include "impeller/entity/contents/content_context.h" +#include "impeller/renderer/command_buffer.h" #include "impeller/renderer/render_pass.h" namespace impeller { @@ -27,4 +29,79 @@ Contents::Contents() = default; Contents::~Contents() = default; +Rect Contents::GetBounds(const Entity& entity) const { + const auto& transform = entity.GetTransformation(); + auto points = entity.GetPath().GetBoundingBox()->GetPoints(); + for (uint i = 0; i < points.size(); i++) { + points[i] = transform * points[i]; + } + return Rect::MakePointBounds({points.begin(), points.end()}); +} + +std::optional Contents::RenderToTexture( + const ContentContext& renderer, + const Entity& entity) const { + auto bounds = GetBounds(entity); + + auto texture = MakeSubpass( + renderer, ISize(bounds.size), + [&contents = *this, &entity, &bounds](const ContentContext& renderer, + RenderPass& pass) -> bool { + Entity sub_entity; + sub_entity.SetPath(entity.GetPath()); + sub_entity.SetBlendMode(Entity::BlendMode::kSource); + sub_entity.SetTransformation( + Matrix::MakeTranslation(Vector3(-bounds.origin)) * + entity.GetTransformation()); + return contents.Render(renderer, sub_entity, pass); + }); + + if (!texture.has_value()) { + return std::nullopt; + } + + return Snapshot{.texture = texture.value(), .position = bounds.origin}; +} + +using SubpassCallback = std::function; + +std::optional> Contents::MakeSubpass( + const ContentContext& renderer, + ISize texture_size, + SubpassCallback subpass_callback) { + auto context = renderer.GetContext(); + + auto subpass_target = RenderTarget::CreateOffscreen(*context, texture_size); + auto subpass_texture = subpass_target.GetRenderTargetTexture(); + if (!subpass_texture) { + return std::nullopt; + } + + auto sub_command_buffer = context->CreateRenderCommandBuffer(); + sub_command_buffer->SetLabel("Offscreen Contents Command Buffer"); + if (!sub_command_buffer) { + return std::nullopt; + } + + auto sub_renderpass = sub_command_buffer->CreateRenderPass(subpass_target); + if (!sub_renderpass) { + return std::nullopt; + } + sub_renderpass->SetLabel("OffscreenContentsPass"); + + if (!subpass_callback(renderer, *sub_renderpass)) { + return std::nullopt; + } + + if (!sub_renderpass->EncodeCommands(*context->GetTransientsAllocator())) { + return std::nullopt; + } + + if (!sub_command_buffer->SubmitCommands()) { + return std::nullopt; + } + + return subpass_texture; +} + } // namespace impeller diff --git a/impeller/entity/contents/contents.h b/impeller/entity/contents/contents.h index 1f1d08b9290a1..915b89c34c308 100644 --- a/impeller/entity/contents/contents.h +++ b/impeller/entity/contents/contents.h @@ -9,6 +9,8 @@ #include #include "flutter/fml/macros.h" +#include "impeller/geometry/rect.h" +#include "impeller/renderer/texture.h" namespace impeller { @@ -25,6 +27,14 @@ ContentContextOptions OptionsFromPassAndEntity(const RenderPass& pass, class Contents { public: + /// Represents a screen space texture and it's intended draw position. + struct Snapshot { + std::shared_ptr texture; + /// The offset from the origin where this texture is intended to be + /// rendered. + Vector2 position; + }; + Contents(); virtual ~Contents(); @@ -33,6 +43,26 @@ class Contents { const Entity& entity, RenderPass& pass) const = 0; + /// @brief Get the bounding rectangle that this contents modifies in screen + /// space. + virtual Rect GetBounds(const Entity& entity) const; + + /// @brief Render this contents to a texture, respecting the entity's + /// transform, path, stencil depth, blend mode, etc. + /// The result texture size is always the size of `GetBounds(entity)`. + virtual std::optional RenderToTexture( + const ContentContext& renderer, + const Entity& entity) const; + + using SubpassCallback = + std::function; + static std::optional> MakeSubpass( + const ContentContext& renderer, + ISize texture_size, + SubpassCallback subpass_callback); + + protected: + private: FML_DISALLOW_COPY_AND_ASSIGN(Contents); }; diff --git a/impeller/entity/contents/filters/blend_filter_contents.cc b/impeller/entity/contents/filters/blend_filter_contents.cc index 5f1e89c0eb7de..81a048e5a1f35 100644 --- a/impeller/entity/contents/filters/blend_filter_contents.cc +++ b/impeller/entity/contents/filters/blend_filter_contents.cc @@ -20,42 +20,28 @@ using PipelineProc = std::shared_ptr (ContentContext::*)(ContentContextOptions) const; template -static void AdvancedBlendPass(std::shared_ptr input_d, - std::shared_ptr input_s, - std::shared_ptr sampler, - const ContentContext& renderer, - RenderPass& pass, - Command& cmd) {} - -template -static bool AdvancedBlend( - const std::vector>& input_textures, - const ContentContext& renderer, - RenderPass& pass, - PipelineProc pipeline_proc) { +static bool AdvancedBlend(const std::vector& input_textures, + const ContentContext& renderer, + RenderPass& pass, + PipelineProc pipeline_proc) { if (input_textures.size() < 2) { return false; } auto& host_buffer = pass.GetTransientsBuffer(); + auto size = pass.GetRenderTargetSize(); VertexBufferBuilder vtx_builder; vtx_builder.AddVertices({ {Point(0, 0), Point(0, 0)}, - {Point(1, 0), Point(1, 0)}, - {Point(1, 1), Point(1, 1)}, + {Point(size.width, 0), Point(1, 0)}, + {Point(size.width, size.height), Point(1, 1)}, {Point(0, 0), Point(0, 0)}, - {Point(1, 1), Point(1, 1)}, - {Point(0, 1), Point(0, 1)}, + {Point(size.width, size.height), Point(1, 1)}, + {Point(0, size.height), Point(0, 1)}, }); auto vtx_buffer = vtx_builder.CreateVertexBuffer(host_buffer); - typename VS::FrameInfo frame_info; - frame_info.mvp = Matrix::MakeOrthographic(ISize(1, 1)); - - auto uniform_view = host_buffer.EmplaceUniform(frame_info); - auto sampler = renderer.GetContext()->GetSamplerLibrary()->GetSampler({}); - auto options = OptionsFromPass(pass); options.blend_mode = Entity::BlendMode::kSource; std::shared_ptr pipeline = @@ -65,10 +51,27 @@ static bool AdvancedBlend( cmd.label = "Advanced Blend Filter"; cmd.BindVertices(vtx_buffer); cmd.pipeline = std::move(pipeline); - VS::BindFrameInfo(cmd, uniform_view); - FS::BindTextureSamplerDst(cmd, input_textures[0], sampler); - FS::BindTextureSamplerSrc(cmd, input_textures[1], sampler); + auto sampler = renderer.GetContext()->GetSamplerLibrary()->GetSampler({}); + typename VS::FrameInfo frame_info; + frame_info.mvp = Matrix::MakeOrthographic(size); + + auto dst_snapshot = input_textures[1]; + FS::BindTextureSamplerSrc(cmd, dst_snapshot.texture, sampler); + frame_info.dst_uv_transform = + Matrix::MakeTranslation(-dst_snapshot.position / size) * + Matrix::MakeScale( + Vector3(Size(size) / Size(dst_snapshot.texture->GetSize()))); + + auto src_snapshot = input_textures[0]; + FS::BindTextureSamplerDst(cmd, src_snapshot.texture, sampler); + frame_info.src_uv_transform = + Matrix::MakeTranslation(-src_snapshot.position / size) * + Matrix::MakeScale( + Vector3(Size(size) / Size(src_snapshot.texture->GetSize()))); + + auto uniform_view = host_buffer.EmplaceUniform(frame_info); + VS::BindFrameInfo(cmd, uniform_view); pass.AddCommand(cmd); return true; @@ -88,14 +91,14 @@ void BlendFilterContents::SetBlendMode(Entity::BlendMode blend_mode) { switch (blend_mode) { case Entity::BlendMode::kScreen: - advanced_blend_proc_ = - [](const std::vector>& input_textures, - const ContentContext& renderer, RenderPass& pass) { - PipelineProc p = &ContentContext::GetTextureBlendScreenPipeline; - return AdvancedBlend( - input_textures, renderer, pass, p); - }; + advanced_blend_proc_ = [](const std::vector& input_textures, + const ContentContext& renderer, + RenderPass& pass) { + PipelineProc p = &ContentContext::GetTextureBlendScreenPipeline; + return AdvancedBlend( + input_textures, renderer, pass, p); + }; break; default: FML_UNREACHABLE(); @@ -103,31 +106,27 @@ void BlendFilterContents::SetBlendMode(Entity::BlendMode blend_mode) { } } -static bool BasicBlend( - const std::vector>& input_textures, - const ContentContext& renderer, - RenderPass& pass, - Entity::BlendMode basic_blend) { +static bool BasicBlend(const std::vector& input_textures, + const ContentContext& renderer, + RenderPass& pass, + Entity::BlendMode basic_blend) { using VS = TextureBlendPipeline::VertexShader; using FS = TextureBlendPipeline::FragmentShader; auto& host_buffer = pass.GetTransientsBuffer(); + auto size = pass.GetRenderTargetSize(); VertexBufferBuilder vtx_builder; vtx_builder.AddVertices({ {Point(0, 0), Point(0, 0)}, - {Point(1, 0), Point(1, 0)}, - {Point(1, 1), Point(1, 1)}, + {Point(size.width, 0), Point(1, 0)}, + {Point(size.width, size.height), Point(1, 1)}, {Point(0, 0), Point(0, 0)}, - {Point(1, 1), Point(1, 1)}, - {Point(0, 1), Point(0, 1)}, + {Point(size.width, size.height), Point(1, 1)}, + {Point(0, size.height), Point(0, 1)}, }); auto vtx_buffer = vtx_builder.CreateVertexBuffer(host_buffer); - VS::FrameInfo frame_info; - frame_info.mvp = Matrix::MakeOrthographic(ISize(1, 1)); - - auto uniform_view = host_buffer.EmplaceUniform(frame_info); auto sampler = renderer.GetContext()->GetSamplerLibrary()->GetSampler({}); // Draw the first texture using kSource. @@ -138,8 +137,19 @@ static bool BasicBlend( auto options = OptionsFromPass(pass); options.blend_mode = Entity::BlendMode::kSource; cmd.pipeline = renderer.GetTextureBlendPipeline(options); - FS::BindTextureSamplerSrc(cmd, input_textures[0], sampler); - VS::BindFrameInfo(cmd, uniform_view); + { + auto input = input_textures[0]; + FS::BindTextureSamplerSrc(cmd, input.texture, sampler); + + VS::FrameInfo frame_info; + frame_info.mvp = + Matrix::MakeOrthographic(size) * + Matrix::MakeTranslation(input.position) * + Matrix::MakeScale(Size(input.texture->GetSize()) / Size(size)); + + auto uniform_view = host_buffer.EmplaceUniform(frame_info); + VS::BindFrameInfo(cmd, uniform_view); + } pass.AddCommand(cmd); if (input_textures.size() < 2) { @@ -153,7 +163,17 @@ static bool BasicBlend( for (auto texture_i = input_textures.begin() + 1; texture_i < input_textures.end(); texture_i++) { - FS::BindTextureSamplerSrc(cmd, *texture_i, sampler); + auto input = *texture_i; + FS::BindTextureSamplerSrc(cmd, input.texture, sampler); + + VS::FrameInfo frame_info; + frame_info.mvp = frame_info.mvp = + Matrix::MakeOrthographic(size) * + Matrix::MakeTranslation(input.position) * + Matrix::MakeScale(Size(input.texture->GetSize()) / Size(size)); + + auto uniform_view = host_buffer.EmplaceUniform(frame_info); + VS::BindFrameInfo(cmd, uniform_view); pass.AddCommand(cmd); } @@ -161,9 +181,10 @@ static bool BasicBlend( } bool BlendFilterContents::RenderFilter( - const std::vector>& input_textures, + const std::vector& input_textures, const ContentContext& renderer, - RenderPass& pass) const { + RenderPass& pass, + const Matrix& transform) const { if (input_textures.empty()) { return true; } diff --git a/impeller/entity/contents/filters/blend_filter_contents.h b/impeller/entity/contents/filters/blend_filter_contents.h index 442cc54b3b05e..2346037f7b282 100644 --- a/impeller/entity/contents/filters/blend_filter_contents.h +++ b/impeller/entity/contents/filters/blend_filter_contents.h @@ -10,10 +10,10 @@ namespace impeller { class BlendFilterContents : public FilterContents { public: - using AdvancedBlendProc = std::function>& input_textures, - const ContentContext& renderer, - RenderPass& pass)>; + using AdvancedBlendProc = + std::function& input_textures, + const ContentContext& renderer, + RenderPass& pass)>; BlendFilterContents(); @@ -23,9 +23,10 @@ class BlendFilterContents : public FilterContents { private: // |FilterContents| - bool RenderFilter(const std::vector>& input_textures, + bool RenderFilter(const std::vector& input_textures, const ContentContext& renderer, - RenderPass& pass) const override; + RenderPass& pass, + const Matrix& transform) const override; Entity::BlendMode blend_mode_; AdvancedBlendProc advanced_blend_proc_; diff --git a/impeller/entity/contents/filters/filter_contents.cc b/impeller/entity/contents/filters/filter_contents.cc index 033fdeed3183e..b799fc2ae4be2 100644 --- a/impeller/entity/contents/filters/filter_contents.cc +++ b/impeller/entity/contents/filters/filter_contents.cc @@ -6,7 +6,10 @@ #include #include +#include +#include #include +#include #include "flutter/fml/logging.h" #include "impeller/base/validation.h" @@ -48,7 +51,9 @@ std::shared_ptr FilterContents::MakeBlend( new_blend->SetBlendMode(blend_mode); blend = new_blend; } - return std::get>(blend); + auto contents = std::get>(blend); + // This downcast is safe because we know blend is a BlendFilterContents. + return std::static_pointer_cast(contents); } FML_UNREACHABLE(); @@ -56,24 +61,19 @@ std::shared_ptr FilterContents::MakeBlend( std::shared_ptr FilterContents::MakeDirectionalGaussianBlur( InputVariant input_texture, - Scalar radius, - Vector2 direction, - bool clip_border) { + Vector2 blur_vector) { auto blur = std::make_shared(); blur->SetInputTextures({input_texture}); - blur->SetRadius(radius); - blur->SetDirection(direction); - blur->SetClipBorder(clip_border); + blur->SetBlurVector(blur_vector); return blur; } std::shared_ptr FilterContents::MakeGaussianBlur( InputVariant input_texture, - Scalar radius, - bool clip_border) { - auto x_blur = MakeDirectionalGaussianBlur(input_texture, radius, Point(1, 0), - clip_border); - return MakeDirectionalGaussianBlur(x_blur, radius, Point(0, 1), false); + Scalar sigma_x, + Scalar sigma_y) { + auto x_blur = MakeDirectionalGaussianBlur(input_texture, Point(sigma_x, 0)); + return MakeDirectionalGaussianBlur(x_blur, Point(0, sigma_y)); } FilterContents::FilterContents() = default; @@ -89,106 +89,139 @@ bool FilterContents::Render(const ContentContext& renderer, RenderPass& pass) const { // Run the filter. - auto maybe_texture = RenderFilterToTexture(renderer, entity, pass); - if (!maybe_texture.has_value()) { + auto maybe_snapshot = RenderToTexture(renderer, entity); + if (!maybe_snapshot.has_value()) { return false; } - auto& texture = maybe_texture.value(); + auto& snapshot = maybe_snapshot.value(); - // Draw the resulting texture to the given destination rect, respecting the - // transform and clip stack. + // Draw the result texture, respecting the transform and clip stack. auto contents = std::make_shared(); - contents->SetTexture(texture); - contents->SetSourceRect(IRect::MakeSize(texture->GetSize())); - - return contents->Render(renderer, entity, pass); + contents->SetTexture(snapshot.texture); + contents->SetSourceRect(IRect::MakeSize(snapshot.texture->GetSize())); + + Entity e; + e.SetPath(PathBuilder{}.AddRect(GetBounds(entity)).GetCurrentPath()); + e.SetBlendMode(entity.GetBlendMode()); + e.SetStencilDepth(entity.GetStencilDepth()); + return contents->Render(renderer, e, pass); } -std::optional> FilterContents::RenderFilterToTexture( - const ContentContext& renderer, - const Entity& entity, - RenderPass& pass) const { - auto output_size = GetOutputSize(input_textures_); - if (output_size.IsZero()) { - return std::nullopt; +Rect FilterContents::GetBoundsForInput(const Entity& entity, + const InputVariant& input) { + if (auto contents = std::get_if>(&input)) { + return contents->get()->GetBounds(entity); } - // Resolve all inputs as textures. + if (auto texture = std::get_if>(&input)) { + auto points = entity.GetPath().GetBoundingBox()->GetPoints(); - std::vector> input_textures; - input_textures.reserve(input_textures_.size()); - for (const auto& input : input_textures_) { - if (auto filter = std::get_if>(&input)) { - auto texture = - filter->get()->RenderFilterToTexture(renderer, entity, pass); - if (!texture.has_value()) { - return std::nullopt; - } - input_textures.push_back(std::move(texture.value())); - } else if (auto texture = std::get_if>(&input)) { - input_textures.push_back(*texture); - } else { - FML_UNREACHABLE(); + const auto& transform = entity.GetTransformation(); + for (uint i = 0; i < points.size(); i++) { + points[i] = transform * points[i]; } + return Rect::MakePointBounds({points.begin(), points.end()}); } - // Create a new texture and render the filter to it. + FML_UNREACHABLE(); +} - auto context = renderer.GetContext(); +Rect FilterContents::GetBounds(const Entity& entity) const { + // The default bounds of FilterContents is just the union of its inputs. + // FilterContents implementations may choose to increase the bounds in any + // direction, but it should never - auto subpass_target = RenderTarget::CreateOffscreen(*context, output_size); - auto subpass_texture = subpass_target.GetRenderTargetTexture(); - if (!subpass_texture) { - return std::nullopt; + if (input_textures_.empty()) { + return Rect(); } - auto sub_command_buffer = context->CreateRenderCommandBuffer(); - sub_command_buffer->SetLabel("Offscreen Filter Command Buffer"); - if (!sub_command_buffer) { - return std::nullopt; + Rect result = GetBoundsForInput(entity, input_textures_.front()); + for (auto input_i = input_textures_.begin() + 1; + input_i < input_textures_.end(); input_i++) { + result.Union(GetBoundsForInput(entity, *input_i)); } - auto sub_renderpass = sub_command_buffer->CreateRenderPass(subpass_target); - if (!sub_renderpass) { - return std::nullopt; - } - sub_renderpass->SetLabel("OffscreenFilterPass"); + return result; +} - if (!RenderFilter(input_textures, renderer, *sub_renderpass)) { - return std::nullopt; +static std::optional ResolveSnapshotForInput( + const ContentContext& renderer, + const Entity& entity, + FilterContents::InputVariant input) { + if (auto contents = std::get_if>(&input)) { + return contents->get()->RenderToTexture(renderer, entity); } - if (!sub_renderpass->EncodeCommands(*context->GetTransientsAllocator())) { - return std::nullopt; - } + if (auto input_texture = std::get_if>(&input)) { + auto input_bounds = FilterContents::GetBoundsForInput(entity, input); + // If the input is a texture, render the version of it which is transformed. + auto texture = Contents::MakeSubpass( + renderer, ISize(input_bounds.size), + [texture = *input_texture, entity, input_bounds]( + const ContentContext& renderer, RenderPass& pass) -> bool { + TextureContents contents; + contents.SetTexture(texture); + contents.SetSourceRect(IRect::MakeSize(texture->GetSize())); + Entity sub_entity; + sub_entity.SetPath(entity.GetPath()); + sub_entity.SetBlendMode(Entity::BlendMode::kSource); + sub_entity.SetTransformation( + Matrix::MakeTranslation(Vector3(-input_bounds.origin)) * + entity.GetTransformation()); + return contents.Render(renderer, sub_entity, pass); + }); + if (!texture.has_value()) { + return std::nullopt; + } - if (!sub_command_buffer->SubmitCommands()) { - return std::nullopt; + return Contents::Snapshot{.texture = texture.value(), + .position = input_bounds.origin}; } - return subpass_texture; + FML_UNREACHABLE(); } -ISize FilterContents::GetOutputSize() const { - if (input_textures_.empty()) { - return {}; +std::optional FilterContents::RenderToTexture( + const ContentContext& renderer, + const Entity& entity) const { + auto bounds = GetBounds(entity); + if (bounds.IsZero()) { + return std::nullopt; } - return GetOutputSize(input_textures_); -} -ISize FilterContents::GetOutputSize(const InputTextures& input_textures) const { - if (auto filter = - std::get_if>(&input_textures[0])) { - return filter->get()->GetOutputSize(input_textures); + // Resolve all inputs as textures. + + std::vector input_textures; + + input_textures.reserve(input_textures_.size()); + for (const auto& input : input_textures_) { + auto texture_and_offset = ResolveSnapshotForInput(renderer, entity, input); + if (!texture_and_offset.has_value()) { + continue; + } + + // Make the position of all input snapshots relative to this filter's + // snapshot position. + texture_and_offset->position -= bounds.origin; + + input_textures.push_back(texture_and_offset.value()); } - if (auto texture = - std::get_if>(&input_textures[0])) { - return texture->get()->GetSize(); + // Create a new texture and render the filter to it. + + auto texture = MakeSubpass( + renderer, ISize(GetBounds(entity).size), + [=](const ContentContext& renderer, RenderPass& pass) -> bool { + return RenderFilter(input_textures, renderer, pass, + entity.GetTransformation()); + }); + + if (!texture.has_value()) { + return std::nullopt; } - FML_UNREACHABLE(); + return Snapshot{.texture = texture.value(), .position = bounds.origin}; } } // namespace impeller diff --git a/impeller/entity/contents/filters/filter_contents.h b/impeller/entity/contents/filters/filter_contents.h index 7c5b29ff2858e..fda5b8a7afd78 100644 --- a/impeller/entity/contents/filters/filter_contents.h +++ b/impeller/entity/contents/filters/filter_contents.h @@ -18,7 +18,7 @@ class Pipeline; class FilterContents : public Contents { public: using InputVariant = - std::variant, std::shared_ptr>; + std::variant, std::shared_ptr>; using InputTextures = std::vector; static std::shared_ptr MakeBlend( @@ -27,14 +27,13 @@ class FilterContents : public Contents { static std::shared_ptr MakeDirectionalGaussianBlur( InputVariant input_texture, - Scalar radius, - Vector2 direction, - bool clip_border = false); + Vector2 blur_vector); - static std::shared_ptr MakeGaussianBlur( - InputVariant input_texture, - Scalar radius, - bool clip_border = false); + static std::shared_ptr + MakeGaussianBlur(InputVariant input_texture, Scalar sigma_x, Scalar sigma_y); + + static Rect GetBoundsForInput(const Entity& entity, + const InputVariant& input); FilterContents(); @@ -53,26 +52,21 @@ class FilterContents : public Contents { const Entity& entity, RenderPass& pass) const override; - /// @brief Renders dependency filters, creates a subpass, and calls the - /// `RenderFilter` defined by the subclasses. - std::optional> RenderFilterToTexture( - const ContentContext& renderer, - const Entity& entity, - RenderPass& pass) const; + // |Contents| + Rect GetBounds(const Entity& entity) const override; - /// @brief Fetch the size of the output texture. - ISize GetOutputSize() const; + // |Contents| + virtual std::optional RenderToTexture( + const ContentContext& renderer, + const Entity& entity) const override; private: /// @brief Takes a set of zero or more input textures and writes to an output /// texture. - virtual bool RenderFilter( - const std::vector>& input_textures, - const ContentContext& renderer, - RenderPass& pass) const = 0; - - /// @brief Determines the size of the output texture. - virtual ISize GetOutputSize(const InputTextures& input_textures) const; + virtual bool RenderFilter(const std::vector& input_textures, + const ContentContext& renderer, + RenderPass& pass, + const Matrix& transform) const = 0; InputTextures input_textures_; Rect destination_; diff --git a/impeller/entity/contents/filters/gaussian_blur_filter_contents.cc b/impeller/entity/contents/filters/gaussian_blur_filter_contents.cc index f677f37cf362a..f83b580430179 100644 --- a/impeller/entity/contents/filters/gaussian_blur_filter_contents.cc +++ b/impeller/entity/contents/filters/gaussian_blur_filter_contents.cc @@ -4,7 +4,12 @@ #include "impeller/entity/contents/filters/gaussian_blur_filter_contents.h" +#include + #include "impeller/entity/contents/content_context.h" +#include "impeller/entity/contents/filters/filter_contents.h" +#include "impeller/geometry/rect.h" +#include "impeller/geometry/scalar.h" #include "impeller/renderer/render_pass.h" #include "impeller/renderer/sampler_library.h" @@ -16,29 +21,38 @@ DirectionalGaussianBlurFilterContents::DirectionalGaussianBlurFilterContents() = DirectionalGaussianBlurFilterContents:: ~DirectionalGaussianBlurFilterContents() = default; -void DirectionalGaussianBlurFilterContents::SetRadius(Scalar radius) { - radius_ = std::max(radius, 1e-3f); -} - -void DirectionalGaussianBlurFilterContents::SetDirection(Vector2 direction) { - direction_ = direction.Normalize(); -} - -void DirectionalGaussianBlurFilterContents::SetClipBorder(bool clip) { - clip_ = clip; +void DirectionalGaussianBlurFilterContents::SetBlurVector(Vector2 blur_vector) { + if (blur_vector.GetLengthSquared() < kEhCloseEnough) { + blur_vector_ = Vector2(0, kEhCloseEnough); + return; + } + blur_vector_ = blur_vector; } bool DirectionalGaussianBlurFilterContents::RenderFilter( - const std::vector>& input_textures, + const std::vector& input_textures, const ContentContext& renderer, - RenderPass& pass) const { + RenderPass& pass, + const Matrix& transform) const { + if (input_textures.empty()) { + return true; + } + using VS = GaussianBlurPipeline::VertexShader; using FS = GaussianBlurPipeline::FragmentShader; auto& host_buffer = pass.GetTransientsBuffer(); - ISize size = FilterContents::GetOutputSize(); - Point uv_offset = clip_ ? (Point(radius_, radius_) / size) : Point(); + // Because this filter is intended to be used with only one input parameter, + // and GetBounds just increases the input size by a factor of the direction, + // we we can just scale up the UVs by the same amount and don't need to worry + // about mapping the UVs to destination rect (like we do in + // BlendFilterContents). + + auto size = pass.GetRenderTargetSize(); + auto transformed_blur = transform.TransformDirection(blur_vector_); + auto uv_offset = transformed_blur.Abs() / size; + // LTRB Scalar uv[4] = { -uv_offset.x, @@ -59,12 +73,10 @@ bool DirectionalGaussianBlurFilterContents::RenderFilter( auto vtx_buffer = vtx_builder.CreateVertexBuffer(host_buffer); VS::FrameInfo frame_info; - frame_info.mvp = Matrix::MakeOrthographic(size); frame_info.texture_size = Point(size); - frame_info.blur_radius = radius_; - frame_info.blur_direction = direction_; + frame_info.blur_radius = transformed_blur.GetLength(); + frame_info.blur_direction = transformed_blur.Normalize(); - auto uniform_view = host_buffer.EmplaceUniform(frame_info); auto sampler = renderer.GetContext()->GetSamplerLibrary()->GetSampler({}); Command cmd; @@ -73,29 +85,26 @@ bool DirectionalGaussianBlurFilterContents::RenderFilter( options.blend_mode = Entity::BlendMode::kSource; cmd.pipeline = renderer.GetGaussianBlurPipeline(options); cmd.BindVertices(vtx_buffer); + + const auto& [texture, _] = input_textures[0]; + FS::BindTextureSampler(cmd, texture, sampler); + + frame_info.mvp = Matrix::MakeOrthographic(size); + auto uniform_view = host_buffer.EmplaceUniform(frame_info); VS::BindFrameInfo(cmd, uniform_view); - for (const auto& texture : input_textures) { - FS::BindTextureSampler(cmd, texture, sampler); - pass.AddCommand(cmd); - } + + pass.AddCommand(cmd); return true; } -ISize DirectionalGaussianBlurFilterContents::GetOutputSize( - const InputTextures& input_textures) const { - ISize size; - if (auto filter = - std::get_if>(&input_textures[0])) { - size = filter->get()->GetOutputSize(); - } else if (auto texture = - std::get_if>(&input_textures[0])) { - size = texture->get()->GetSize(); - } else { - FML_UNREACHABLE(); - } - - return size + (clip_ ? ISize(radius_ * 2, radius_ * 2) : ISize()); +Rect DirectionalGaussianBlurFilterContents::GetBounds( + const Entity& entity) const { + auto bounds = FilterContents::GetBounds(entity); + auto transformed_blur = + entity.GetTransformation().TransformDirection(blur_vector_).Abs(); + auto extent = bounds.size + transformed_blur * 2; + return Rect(bounds.origin - transformed_blur, Size(extent.x, extent.y)); } } // namespace impeller diff --git a/impeller/entity/contents/filters/gaussian_blur_filter_contents.h b/impeller/entity/contents/filters/gaussian_blur_filter_contents.h index cdf6c470dba7e..ad2e428457416 100644 --- a/impeller/entity/contents/filters/gaussian_blur_filter_contents.h +++ b/impeller/entity/contents/filters/gaussian_blur_filter_contents.h @@ -5,6 +5,7 @@ #pragma once #include "impeller/entity/contents/filters/filter_contents.h" +#include "impeller/geometry/matrix.h" namespace impeller { @@ -14,25 +15,19 @@ class DirectionalGaussianBlurFilterContents final : public FilterContents { ~DirectionalGaussianBlurFilterContents() override; - void SetRadius(Scalar radius); + void SetBlurVector(Vector2 blur_vector); - void SetDirection(Vector2 direction); - - void SetClipBorder(bool clip); + // |Contents| + Rect GetBounds(const Entity& entity) const override; private: // |FilterContents| - bool RenderFilter(const std::vector>& input_textures, + bool RenderFilter(const std::vector& input_textures, const ContentContext& renderer, - RenderPass& pass) const override; - - // |FilterContents| - virtual ISize GetOutputSize( - const InputTextures& input_textures) const override; + RenderPass& pass, + const Matrix& transform) const override; - Scalar radius_ = 0; - Vector2 direction_; - bool clip_ = false; + Vector2 blur_vector_; FML_DISALLOW_COPY_AND_ASSIGN(DirectionalGaussianBlurFilterContents); }; diff --git a/impeller/entity/contents/texture_contents.h b/impeller/entity/contents/texture_contents.h index fd3ff9c13200b..c94be1f39a5ff 100644 --- a/impeller/entity/contents/texture_contents.h +++ b/impeller/entity/contents/texture_contents.h @@ -10,7 +10,6 @@ #include "flutter/fml/macros.h" #include "impeller/entity/contents/contents.h" -#include "impeller/geometry/rect.h" namespace impeller { diff --git a/impeller/entity/entity.cc b/impeller/entity/entity.cc index 37e410960172c..df13ef0bfe685 100644 --- a/impeller/entity/entity.cc +++ b/impeller/entity/entity.cc @@ -39,11 +39,11 @@ bool Entity::AddsToCoverage() const { } std::optional Entity::GetCoverage() const { - if (!adds_to_coverage_) { + if (!adds_to_coverage_ || !contents_) { return std::nullopt; } - return path_.GetBoundingBox(); + return contents_->GetBounds(*this); } void Entity::SetContents(std::shared_ptr contents) { diff --git a/impeller/entity/entity_unittests.cc b/impeller/entity/entity_unittests.cc index 6f02cf6353cd2..b6d75a3527917 100644 --- a/impeller/entity/entity_unittests.cc +++ b/impeller/entity/entity_unittests.cc @@ -681,36 +681,53 @@ TEST_F(EntityTest, GaussianBlurFilter) { auto callback = [&](ContentContext& context, RenderPass& pass) -> bool { if (first_frame) { first_frame = false; - ImGui::SetNextWindowSize({450, 150}); - ImGui::SetNextWindowPos({200, 450}); + ImGui::SetNextWindowSize({500, 170}); + ImGui::SetNextWindowPos({300, 550}); } ImGui::Begin("Controls"); + static float blur_amount[2] = {20, 20}; + ImGui::SliderFloat2("Blur", &blur_amount[0], 0, 200); + static Color cover_color(1, 0, 0, 0.2); + ImGui::ColorEdit4("Cover color", reinterpret_cast(&cover_color)); static float offset[2] = {500, 400}; - ImGui::SliderFloat2("Position offset", &offset[0], 0, 1000); - static float scale = 1; - ImGui::SliderFloat("Scale", &scale, 0, 1); - static float blur_radius = 20; - ImGui::SliderFloat("Blur radius", &blur_radius, 0, 200); - static bool clip_border = true; - ImGui::Checkbox("Clip", &clip_border); + ImGui::SliderFloat2("Translation", &offset[0], 0, + pass.GetRenderTargetSize().width); + static float rotation = 0; + ImGui::SliderFloat("Rotation", &rotation, 0, kPi * 2); + static float scale[2] = {0.8, 0.8}; + ImGui::SliderFloat2("Scale", &scale[0], 0, 3); + static float skew[2] = {0, 0}; + ImGui::SliderFloat2("Skew", &skew[0], -3, 3); + ImGui::End(); auto blend = FilterContents::MakeBlend(Entity::BlendMode::kPlus, {boston, bridge, bridge}); auto blur = - FilterContents::MakeGaussianBlur(blend, blur_radius, clip_border); + FilterContents::MakeGaussianBlur(blend, blur_amount[0], blur_amount[1]); - auto output_size = Size(blur->GetOutputSize()); - Rect bounds(Point(offset[0], offset[1]) - output_size / 2 * scale, - output_size * scale); - - ImGui::End(); + auto rect = Rect(-Point(boston->GetSize()) / 2, Size(boston->GetSize())); + auto ctm = Matrix::MakeTranslation(Vector3(offset[0], offset[1])) * + Matrix::MakeRotation(rotation, Vector4(0, 0, 1, 1)) * + Matrix::MakeScale(Vector3(scale[0], scale[1])) * + Matrix::MakeSkew(skew[0], skew[1]); Entity entity; - entity.SetPath(PathBuilder{}.AddRect(bounds).TakePath()); + entity.SetPath(PathBuilder{}.AddRect(rect).TakePath()); entity.SetContents(blur); - return entity.Render(context, pass); + entity.SetTransformation(ctm); + + entity.Render(context, pass); + + // The following entity renders the expected transformed input. + Entity cover_entity; + cover_entity.SetPath(PathBuilder{}.AddRect(rect).TakePath()); + cover_entity.SetContents(SolidColorContents::Make(cover_color)); + cover_entity.SetTransformation(ctm); + + cover_entity.Render(context, pass); + return true; }; ASSERT_TRUE(OpenPlaygroundHere(callback)); } diff --git a/impeller/entity/shaders/texture_blend_screen.frag b/impeller/entity/shaders/texture_blend_screen.frag index 4594592efff9c..90fb52c8f66d8 100644 --- a/impeller/entity/shaders/texture_blend_screen.frag +++ b/impeller/entity/shaders/texture_blend_screen.frag @@ -5,12 +5,21 @@ uniform sampler2D texture_sampler_dst; uniform sampler2D texture_sampler_src; -in vec2 v_texture_coords; +in vec2 v_dst_texture_coords; +in vec2 v_src_texture_coords; out vec4 frag_color; +// Emulate SamplerAddressMode::ClampToBorder. +vec4 SampleWithBorder(sampler2D tex, vec2 uv) { + if (uv.x > 0 && uv.y > 0 && uv.x < 1 && uv.y < 1) { + return texture(tex, uv); + } + return vec4(0); +} + void main() { - vec4 dst = texture(texture_sampler_dst, v_texture_coords); - vec4 src = texture(texture_sampler_src, v_texture_coords); + vec4 dst = SampleWithBorder(texture_sampler_dst, v_dst_texture_coords); + vec4 src = SampleWithBorder(texture_sampler_src, v_src_texture_coords); frag_color = src + dst - src * dst; } diff --git a/impeller/entity/shaders/texture_blend_screen.vert b/impeller/entity/shaders/texture_blend_screen.vert index daa30f5650a3f..c84a1c03ca770 100644 --- a/impeller/entity/shaders/texture_blend_screen.vert +++ b/impeller/entity/shaders/texture_blend_screen.vert @@ -4,14 +4,20 @@ uniform FrameInfo { mat4 mvp; + mat4 dst_uv_transform; + mat4 src_uv_transform; } frame_info; in vec2 vertices; in vec2 texture_coords; -out vec2 v_texture_coords; +out vec2 v_dst_texture_coords; +out vec2 v_src_texture_coords; void main() { gl_Position = frame_info.mvp * vec4(vertices, 0.0, 1.0); - v_texture_coords = texture_coords; + v_dst_texture_coords = + (frame_info.dst_uv_transform * vec4(texture_coords, 1.0, 1.0)).xy; + v_src_texture_coords = + (frame_info.src_uv_transform * vec4(texture_coords, 1.0, 1.0)).xy; } diff --git a/impeller/geometry/geometry_unittests.cc b/impeller/geometry/geometry_unittests.cc index 843b6b59ff5c0..35b6641b574fb 100644 --- a/impeller/geometry/geometry_unittests.cc +++ b/impeller/geometry/geometry_unittests.cc @@ -142,6 +142,76 @@ TEST(GeometryTest, TestRecomposition2) { ASSERT_MATRIX_NEAR(matrix, Matrix{result.value()}); } +TEST(GeometryTest, MatrixVectorMultiplication) { + { + auto matrix = Matrix::MakeTranslation({100, 100, 100}) * + Matrix::MakeRotationZ(Radians{M_PI_2}) * + Matrix::MakeScale({2.0, 2.0, 2.0}); + auto vector = Vector4(10, 20, 30, 2); + + Vector4 result = matrix * vector; + auto expected = Vector4(160, 220, 260, 2); + ASSERT_VECTOR4_NEAR(result, expected); + } + + { + auto matrix = Matrix::MakeTranslation({100, 100, 100}) * + Matrix::MakeRotationZ(Radians{M_PI_2}) * + Matrix::MakeScale({2.0, 2.0, 2.0}); + auto vector = Vector3(10, 20, 30); + + Vector3 result = matrix * vector; + auto expected = Vector3(60, 120, 160); + ASSERT_VECTOR3_NEAR(result, expected); + } + + { + auto matrix = Matrix::MakeTranslation({100, 100, 100}) * + Matrix::MakeRotationZ(Radians{M_PI_2}) * + Matrix::MakeScale({2.0, 2.0, 2.0}); + auto vector = Point(10, 20); + + Point result = matrix * vector; + auto expected = Point(60, 120); + ASSERT_POINT_NEAR(result, expected); + } +} + +TEST(GeometryTest, MatrixTransformDirection) { + { + auto matrix = Matrix::MakeTranslation({100, 100, 100}) * + Matrix::MakeRotationZ(Radians{M_PI_2}) * + Matrix::MakeScale({2.0, 2.0, 2.0}); + auto vector = Vector4(10, 20, 30, 2); + + Vector4 result = matrix.TransformDirection(vector); + auto expected = Vector4(-40, 20, 60, 2); + ASSERT_VECTOR4_NEAR(result, expected); + } + + { + auto matrix = Matrix::MakeTranslation({100, 100, 100}) * + Matrix::MakeRotationZ(Radians{M_PI_2}) * + Matrix::MakeScale({2.0, 2.0, 2.0}); + auto vector = Vector3(10, 20, 30); + + Vector3 result = matrix.TransformDirection(vector); + auto expected = Vector3(-40, 20, 60); + ASSERT_VECTOR3_NEAR(result, expected); + } + + { + auto matrix = Matrix::MakeTranslation({100, 100, 100}) * + Matrix::MakeRotationZ(Radians{M_PI_2}) * + Matrix::MakeScale({2.0, 2.0, 2.0}); + auto vector = Point(10, 20); + + Point result = matrix.TransformDirection(vector); + auto expected = Point(-40, 20); + ASSERT_POINT_NEAR(result, expected); + } +} + TEST(GeometryTest, QuaternionLerp) { auto q1 = Quaternion{{0.0, 0.0, 1.0}, 0.0}; auto q2 = Quaternion{{0.0, 0.0, 1.0}, M_PI_4}; @@ -567,6 +637,13 @@ TEST(GeometryTest, PointReflect) { } } +TEST(GeometryTest, PointAbs) { + Point a(-1, -2); + auto a_abs = a.Abs(); + auto expected = Point(1, 2); + ASSERT_POINT_NEAR(a_abs, expected); +} + TEST(GeometryTest, ColorPremultiply) { { Color a(1.0, 0.5, 0.2, 0.5); @@ -722,6 +799,21 @@ TEST(GeometryTest, RectContainsRect) { } } +TEST(GeometryTest, RectGetPoints) { + Rect r(100, 200, 300, 400); + auto points = r.GetPoints(); + ASSERT_POINT_NEAR(points[0], Point(100, 200)); + ASSERT_POINT_NEAR(points[1], Point(400, 200)); + ASSERT_POINT_NEAR(points[2], Point(100, 600)); + ASSERT_POINT_NEAR(points[3], Point(400, 600)); +} + +TEST(GeometryTest, RectMakePointBounds) { + auto r = Rect::MakePointBounds({Point(1, 5), Point(4, -1), Point(0, 6)}); + auto expected = Rect(0, -1, 4, 7); + ASSERT_RECT_NEAR(r, expected); +} + TEST(GeometryTest, CubicPathComponentPolylineDoesNotIncludePointOne) { CubicPathComponent component({10, 10}, {20, 35}, {35, 20}, {40, 40}); SmoothingApproximation approximation; diff --git a/impeller/geometry/geometry_unittests.h b/impeller/geometry/geometry_unittests.h index 61502f1b38dbe..aea0ab80bb7d1 100644 --- a/impeller/geometry/geometry_unittests.h +++ b/impeller/geometry/geometry_unittests.h @@ -75,6 +75,24 @@ inline ::testing::AssertionResult PointNear(impeller::Point a, : ::testing::AssertionFailure() << "Points are not equal."; } +inline ::testing::AssertionResult Vector3Near(impeller::Vector3 a, + impeller::Vector3 b) { + auto equal = + NumberNear(a.x, b.x) && NumberNear(a.y, b.y) && NumberNear(a.z, b.z); + + return equal ? ::testing::AssertionSuccess() + : ::testing::AssertionFailure() << "Vector3s are not equal."; +} + +inline ::testing::AssertionResult Vector4Near(impeller::Vector4 a, + impeller::Vector4 b) { + auto equal = NumberNear(a.x, b.x) && NumberNear(a.y, b.y) && + NumberNear(a.z, b.z) && NumberNear(a.w, b.w); + + return equal ? ::testing::AssertionSuccess() + : ::testing::AssertionFailure() << "Vector4s are not equal."; +} + inline ::testing::AssertionResult SizeNear(impeller::Size a, impeller::Size b) { auto equal = NumberNear(a.width, b.width) && NumberNear(a.height, b.height); @@ -87,4 +105,6 @@ inline ::testing::AssertionResult SizeNear(impeller::Size a, impeller::Size b) { #define ASSERT_RECT_NEAR(a, b) ASSERT_PRED2(&::RectNear, a, b) #define ASSERT_COLOR_NEAR(a, b) ASSERT_PRED2(&::ColorNear, a, b) #define ASSERT_POINT_NEAR(a, b) ASSERT_PRED2(&::PointNear, a, b) +#define ASSERT_VECTOR3_NEAR(a, b) ASSERT_PRED2(&::Vector3Near, a, b) +#define ASSERT_VECTOR4_NEAR(a, b) ASSERT_PRED2(&::Vector4Near, a, b) #define ASSERT_SIZE_NEAR(a, b) ASSERT_PRED2(&::SizeNear, a, b) diff --git a/impeller/geometry/matrix.cc b/impeller/geometry/matrix.cc index f26411039a097..f5aaa3458b500 100644 --- a/impeller/geometry/matrix.cc +++ b/impeller/geometry/matrix.cc @@ -249,7 +249,7 @@ std::optional Matrix::Decompose() const { * prhs by the inverse. */ - result.perspective = rightHandSide * perpectiveMatrix.Invert().Transpose(); + result.perspective = perpectiveMatrix.Invert().Transpose() * rightHandSide; /* * Clear the perspective partition. diff --git a/impeller/geometry/matrix.h b/impeller/geometry/matrix.h index 6b7e4453645e3..c3178d71953e1 100644 --- a/impeller/geometry/matrix.h +++ b/impeller/geometry/matrix.h @@ -259,12 +259,44 @@ struct Matrix { Matrix operator-(const Vector3& t) const { return Translate(-t); } - Matrix operator*(const Vector3& s) const { return Scale(s); } - Matrix operator*(const Matrix& m) const { return Multiply(m); } Matrix operator+(const Matrix& m) const; + constexpr Vector4 operator*(const Vector4& v) const { + return Vector4(v.x * m[0] + v.y * m[4] + v.z * m[8] + v.w * m[12], + v.x * m[1] + v.y * m[5] + v.z * m[9] + v.w * m[13], + v.x * m[2] + v.y * m[6] + v.z * m[10] + v.w * m[14], + v.x * m[3] + v.y * m[7] + v.z * m[11] + v.w * m[15]); + } + + constexpr Vector3 operator*(const Vector3& v) const { + return Vector3(v.x * m[0] + v.y * m[4] + v.z * m[8] + m[12], + v.x * m[1] + v.y * m[5] + v.z * m[9] + m[13], + v.x * m[2] + v.y * m[6] + v.z * m[10] + m[14]); + } + + constexpr Point operator*(const Point& v) const { + return Point(v.x * m[0] + v.y * m[4] + m[12], + v.x * m[1] + v.y * m[5] + m[13]); + } + + constexpr Vector4 TransformDirection(const Vector4& v) const { + return Vector4(v.x * m[0] + v.y * m[4] + v.z * m[8], + v.x * m[1] + v.y * m[5] + v.z * m[9], + v.x * m[2] + v.y * m[6] + v.z * m[10], v.w); + } + + constexpr Vector3 TransformDirection(const Vector3& v) const { + return Vector3(v.x * m[0] + v.y * m[4] + v.z * m[8], + v.x * m[1] + v.y * m[5] + v.z * m[9], + v.x * m[2] + v.y * m[6] + v.z * m[10]); + } + + constexpr Vector2 TransformDirection(const Vector2& v) const { + return Vector2(v.x * m[0] + v.y * m[4], v.x * m[1] + v.y * m[5]); + } + template static constexpr Matrix MakeOrthographic(TSize size) { // Per assumptions about NDC documented above. @@ -279,17 +311,9 @@ struct Matrix { static_assert(sizeof(struct Matrix) == sizeof(Scalar) * 16, "The matrix must be of consistent size."); -inline Vector4 operator*(const Vector4& v, const Matrix& m) { - return Vector4(v.x * m.m[0] + v.y * m.m[4] + v.z * m.m[8] + v.w * m.m[12], - v.x * m.m[1] + v.y * m.m[5] + v.z * m.m[9] + v.w * m.m[13], - v.x * m.m[2] + v.y * m.m[6] + v.z * m.m[10] + v.w * m.m[14], - v.x * m.m[3] + v.y * m.m[7] + v.z * m.m[11] + v.w * m.m[15]); -} - } // namespace impeller namespace std { - inline std::ostream& operator<<(std::ostream& out, const impeller::Matrix& m) { out << "("; for (size_t i = 0; i < 4u; i++) { diff --git a/impeller/geometry/point.h b/impeller/geometry/point.h index 194bc1815d1df..957ab2ad32d26 100644 --- a/impeller/geometry/point.h +++ b/impeller/geometry/point.h @@ -180,6 +180,8 @@ struct TPoint { return {x / length, y / length}; } + constexpr TPoint Abs() const { return {std::fabs(x), std::fabs(y)}; } + constexpr Type Cross(const TPoint& p) const { return (x * p.y) - (y * p.x); } constexpr Type Dot(const TPoint& p) const { return (x * p.x) + (y * p.y); } diff --git a/impeller/geometry/rect.h b/impeller/geometry/rect.h index bb39b4bcd4759..5d5f4224b1996 100644 --- a/impeller/geometry/rect.h +++ b/impeller/geometry/rect.h @@ -4,6 +4,7 @@ #pragma once +#include #include #include #include @@ -50,6 +51,23 @@ struct TRect { return TRect(0.0, 0.0, size.width, size.height); } + constexpr static TRect MakePointBounds( + const std::vector>& points) { + auto left = points[0].x; + auto top = points[0].y; + auto right = points[0].x; + auto bottom = points[0].y; + if (points.size() > 1) { + for (uint i = 1; i < points.size(); i++) { + left = std::min(left, points[i].x); + top = std::min(top, points[i].y); + right = std::max(right, points[i].x); + bottom = std::max(bottom, points[i].y); + } + } + return TRect::MakeLTRB(left, top, right, bottom); + } + template constexpr explicit TRect(const TRect& other) : origin(static_cast>(other.origin)), @@ -116,6 +134,15 @@ struct TRect { return {left, top, right, bottom}; } + constexpr std::array, 4> GetPoints() const { + const auto left = std::min(origin.x, origin.x + size.width); + const auto top = std::min(origin.y, origin.y + size.height); + const auto right = std::max(origin.x, origin.x + size.width); + const auto bottom = std::max(origin.y, origin.y + size.height); + return {TPoint(left, top), TPoint(right, top), TPoint(left, bottom), + TPoint(right, bottom)}; + } + constexpr TRect Union(const TRect& o) const { auto this_ltrb = GetLTRB(); auto other_ltrb = o.GetLTRB(); diff --git a/impeller/geometry/scalar.h b/impeller/geometry/scalar.h index 3fc11c400be7a..1a27bb3aa5e49 100644 --- a/impeller/geometry/scalar.h +++ b/impeller/geometry/scalar.h @@ -14,6 +14,8 @@ namespace impeller { using Scalar = float; +constexpr Scalar kEhCloseEnough = 1e-3f; + template >> constexpr T Absolute(const T& val) { return val >= T{} ? val : -val; @@ -21,7 +23,7 @@ constexpr T Absolute(const T& val) { constexpr inline bool ScalarNearlyEqual(Scalar x, Scalar y, - Scalar tolerance = 1e-3) { + Scalar tolerance = kEhCloseEnough) { return Absolute(x - y) <= tolerance; } diff --git a/impeller/geometry/size.h b/impeller/geometry/size.h index 9a58f32f68b49..b3dee0cec7140 100644 --- a/impeller/geometry/size.h +++ b/impeller/geometry/size.h @@ -47,6 +47,10 @@ struct TSize { static_cast(height) / scale}; } + constexpr TSize operator/(const TSize& s) const { + return {width / s.width, height / s.height}; + } + constexpr bool operator==(const TSize& s) const { return s.width == width && s.height == height; } From e1dffc94abed0d170a997bd8e874b5af27f4f684 Mon Sep 17 00:00:00 2001 From: Brandon DeRosier Date: Thu, 24 Mar 2022 23:13:24 -0700 Subject: [PATCH 372/433] Render glyphs at the correct resolution with respect to the current transform (#101) --- .../display_list/display_list_dispatcher.cc | 10 ++++--- impeller/geometry/constants.h | 3 ++ impeller/geometry/geometry_unittests.cc | 30 +++++++++++++++++++ impeller/geometry/matrix.cc | 9 ++++++ impeller/geometry/matrix.h | 2 ++ impeller/geometry/point.h | 14 +++++++++ impeller/geometry/scalar.h | 2 -- .../backends/skia/text_frame_skia.cc | 8 +++-- .../backends/skia/text_frame_skia.h | 2 +- .../backends/skia/text_render_context_skia.cc | 21 +++++++------ impeller/typographer/font.h | 14 ++++++--- impeller/typographer/glyph_atlas.h | 2 +- 12 files changed, 93 insertions(+), 24 deletions(-) diff --git a/impeller/display_list/display_list_dispatcher.cc b/impeller/display_list/display_list_dispatcher.cc index b1e23010cda72..52950a8de4e8b 100644 --- a/impeller/display_list/display_list_dispatcher.cc +++ b/impeller/display_list/display_list_dispatcher.cc @@ -247,7 +247,8 @@ void DisplayListDispatcher::setMaskFilter(const flutter::DlMaskFilter* filter) { } // |flutter::Dispatcher| -void DisplayListDispatcher::setImageFilter(const flutter::DlImageFilter* filter) { +void DisplayListDispatcher::setImageFilter( + const flutter::DlImageFilter* filter) { UNIMPLEMENTED; } @@ -609,9 +610,10 @@ void DisplayListDispatcher::drawDisplayList( void DisplayListDispatcher::drawTextBlob(const sk_sp blob, SkScalar x, SkScalar y) { - canvas_.DrawTextFrame(TextFrameFromTextBlob(blob), // - impeller::Point{x, y}, // - paint_ // + Scalar scale = canvas_.GetCurrentTransformation().GetMaxBasisLength(); + canvas_.DrawTextFrame(TextFrameFromTextBlob(blob, scale), // + impeller::Point{x, y}, // + paint_ // ); } diff --git a/impeller/geometry/constants.h b/impeller/geometry/constants.h index 8385b7eccb06e..a0cadfffcc483 100644 --- a/impeller/geometry/constants.h +++ b/impeller/geometry/constants.h @@ -43,4 +43,7 @@ constexpr float kSqrt2 = 1.41421356237309504880; // 1/sqrt(2) constexpr float k1OverSqrt2 = 0.70710678118654752440; +// 0.001 +constexpr float kEhCloseEnough = 1e-3; + } // namespace impeller diff --git a/impeller/geometry/geometry_unittests.cc b/impeller/geometry/geometry_unittests.cc index 35b6641b574fb..fbdd2c9c7d6b1 100644 --- a/impeller/geometry/geometry_unittests.cc +++ b/impeller/geometry/geometry_unittests.cc @@ -212,6 +212,21 @@ TEST(GeometryTest, MatrixTransformDirection) { } } +TEST(GeometryTest, MatrixGetMaxBasisLength) { + { + auto m = Matrix::MakeScale({3, 1, 1}); + ASSERT_EQ(m.GetMaxBasisLength(), 3); + + m = m * Matrix::MakeSkew(0, 4); + ASSERT_EQ(m.GetMaxBasisLength(), 5); + } + + { + auto m = Matrix::MakeScale({-3, 4, 2}); + ASSERT_EQ(m.GetMaxBasisLength(), 4); + } +} + TEST(GeometryTest, QuaternionLerp) { auto q1 = Quaternion{{0.0, 0.0, 1.0}, 0.0}; auto q2 = Quaternion{{0.0, 0.0, 1.0}, M_PI_4}; @@ -570,6 +585,21 @@ TEST(GeometryTest, CanUsePointAssignmentOperators) { ASSERT_EQ(p.x, 1u); ASSERT_EQ(p.y, 2u); } + + // Arithmetic type on RHS + { + IPoint p(1, 2); + p *= 3; + ASSERT_EQ(p.x, 3u); + ASSERT_EQ(p.y, 6u); + } + + { + IPoint p(3, 6); + p /= 3; + ASSERT_EQ(p.x, 1u); + ASSERT_EQ(p.y, 2u); + } } TEST(GeometryTest, PointDotProduct) { diff --git a/impeller/geometry/matrix.cc b/impeller/geometry/matrix.cc index f5aaa3458b500..70ede5866aee8 100644 --- a/impeller/geometry/matrix.cc +++ b/impeller/geometry/matrix.cc @@ -193,6 +193,15 @@ Scalar Matrix::GetDeterminant() const { return b00 * b11 - b01 * b10 + b02 * b09 + b03 * b08 - b04 * b07 + b05 * b06; } +Scalar Matrix::GetMaxBasisLength() const { + Scalar max = 0; + for (int i = 0; i < 3; i++) { + max = std::max(max, + e[i][0] * e[i][0] + e[i][1] * e[i][1] + e[i][2] * e[i][2]); + } + return std::sqrt(max); +} + /* * Adapted for Impeller from Graphics Gems: * http://www.realtimerendering.com/resources/GraphicsGems/gemsii/unmatrix.c diff --git a/impeller/geometry/matrix.h b/impeller/geometry/matrix.h index c3178d71953e1..539ae84ae37bd 100644 --- a/impeller/geometry/matrix.h +++ b/impeller/geometry/matrix.h @@ -219,6 +219,8 @@ struct Matrix { Scalar GetDeterminant() const; + Scalar GetMaxBasisLength() const; + constexpr bool IsAffine() const { return (m[2] == 0 && m[3] == 0 && m[6] == 0 && m[7] == 0 && m[8] == 0 && m[9] == 0 && m[10] == 1 && m[11] == 0 && m[14] == 0 && m[15] == 1); diff --git a/impeller/geometry/point.h b/impeller/geometry/point.h index 957ab2ad32d26..7b511ed9d64a6 100644 --- a/impeller/geometry/point.h +++ b/impeller/geometry/point.h @@ -88,6 +88,13 @@ struct TPoint { return *this; } + template >> + inline TPoint operator*=(U scale) { + x *= static_cast(scale); + y *= static_cast(scale); + return *this; + } + template inline TPoint operator/=(const TPoint& p) { x /= static_cast(p.x); @@ -102,6 +109,13 @@ struct TPoint { return *this; } + template >> + inline TPoint operator/=(U scale) { + x /= static_cast(scale); + y /= static_cast(scale); + return *this; + } + constexpr TPoint operator-() const { return {-x, -y}; } constexpr TPoint operator+(const TPoint& p) const { diff --git a/impeller/geometry/scalar.h b/impeller/geometry/scalar.h index 1a27bb3aa5e49..3ca2f1dab6638 100644 --- a/impeller/geometry/scalar.h +++ b/impeller/geometry/scalar.h @@ -14,8 +14,6 @@ namespace impeller { using Scalar = float; -constexpr Scalar kEhCloseEnough = 1e-3f; - template >> constexpr T Absolute(const T& val) { return val >= T{} ? val : -val; diff --git a/impeller/typographer/backends/skia/text_frame_skia.cc b/impeller/typographer/backends/skia/text_frame_skia.cc index d9db8e36f4757..4f92b300fb309 100644 --- a/impeller/typographer/backends/skia/text_frame_skia.cc +++ b/impeller/typographer/backends/skia/text_frame_skia.cc @@ -12,13 +12,14 @@ namespace impeller { -static Font ToFont(const SkFont& font) { +static Font ToFont(const SkFont& font, Scalar scale) { auto typeface = std::make_shared(font.refTypefaceOrDefault()); SkFontMetrics sk_metrics; font.getMetrics(&sk_metrics); Font::Metrics metrics; + metrics.scale = scale; metrics.point_size = font.getSize(); metrics.ascent = sk_metrics.fAscent; metrics.descent = sk_metrics.fDescent; @@ -28,7 +29,8 @@ static Font ToFont(const SkFont& font) { return Font{std::move(typeface), std::move(metrics)}; } -TextFrame TextFrameFromTextBlob(sk_sp blob) { +TextFrame TextFrameFromTextBlob(sk_sp blob, + Scalar scale) { if (!blob) { return {}; } @@ -36,7 +38,7 @@ TextFrame TextFrameFromTextBlob(sk_sp blob) { TextFrame frame; for (SkTextBlobRunIterator run(blob.get()); !run.done(); run.next()) { - TextRun text_run(ToFont(run.font())); + TextRun text_run(ToFont(run.font(), scale)); const auto glyph_count = run.glyphCount(); const auto* glyphs = run.glyphs(); switch (run.positioning()) { diff --git a/impeller/typographer/backends/skia/text_frame_skia.h b/impeller/typographer/backends/skia/text_frame_skia.h index ca15f01881712..a7b8074135c3f 100644 --- a/impeller/typographer/backends/skia/text_frame_skia.h +++ b/impeller/typographer/backends/skia/text_frame_skia.h @@ -10,6 +10,6 @@ namespace impeller { -TextFrame TextFrameFromTextBlob(sk_sp blob); +TextFrame TextFrameFromTextBlob(sk_sp blob, Scalar scale = 1.0f); } // namespace impeller diff --git a/impeller/typographer/backends/skia/text_render_context_skia.cc b/impeller/typographer/backends/skia/text_render_context_skia.cc index 94d686e0bdb80..c93906d501ba7 100644 --- a/impeller/typographer/backends/skia/text_render_context_skia.cc +++ b/impeller/typographer/backends/skia/text_render_context_skia.cc @@ -65,7 +65,8 @@ static bool PairsFitInAtlasOfSize(const FontGlyphPair::Vector& pairs, for (const auto& pair : pairs) { const auto glyph_size = - ISize::Ceil(pair.font.GetMetrics().GetBoundingBox().size); + ISize::Ceil(pair.font.GetMetrics().GetBoundingBox().size * + pair.font.GetMetrics().scale); SkIPoint16 location_in_atlas; if (!rect_packer->addRect(glyph_size.width, // glyph_size.height, // @@ -125,7 +126,8 @@ static std::optional CreateAtlasBitmap(const GlyphAtlas& atlas, SkFont sk_font( TypefaceSkia::Cast(*font_glyph.font.GetTypeface()).GetSkiaTypeface(), - font_glyph.font.GetMetrics().point_size); + font_glyph.font.GetMetrics().point_size * + font_glyph.font.GetMetrics().scale); const auto& metrics = font_glyph.font.GetMetrics(); @@ -133,13 +135,14 @@ static std::optional CreateAtlasBitmap(const GlyphAtlas& atlas, SkPaint glyph_paint; glyph_paint.setColor(glyph_color); - canvas->drawGlyphs(1u, // count - &glyph_id, // glyphs - &position, // positions - SkPoint::Make(-metrics.min_extent.x, - -metrics.ascent), // origin - sk_font, // font - glyph_paint // paint + canvas->drawGlyphs( + 1u, // count + &glyph_id, // glyphs + &position, // positions + SkPoint::Make(-metrics.min_extent.x * metrics.scale, + -metrics.ascent * metrics.scale), // origin + sk_font, // font + glyph_paint // paint ); return true; }); diff --git a/impeller/typographer/font.h b/impeller/typographer/font.h index e23d8908fa444..2830900b7de9f 100644 --- a/impeller/typographer/font.h +++ b/impeller/typographer/font.h @@ -28,6 +28,12 @@ class Font : public Comparable { /// the baseline with an upper-left-origin coordinate system. /// struct Metrics { + //-------------------------------------------------------------------------- + /// The scaling factor that should be used when rendering this font to an + /// atlas. This should normally be set in accordance with the transformation + /// matrix that will be used to position glyph geometry. + /// + Scalar scale = 1.0f; //-------------------------------------------------------------------------- /// The point size of the font. /// @@ -69,9 +75,9 @@ class Font : public Comparable { } constexpr bool operator==(const Metrics& o) const { - return point_size == o.point_size && ascent == o.ascent && - descent == o.descent && min_extent == o.min_extent && - max_extent == o.max_extent; + return scale == o.scale && point_size == o.point_size && + ascent == o.ascent && descent == o.descent && + min_extent == o.min_extent && max_extent == o.max_extent; } }; @@ -107,6 +113,6 @@ class Font : public Comparable { template <> struct std::hash { constexpr std::size_t operator()(const impeller::Font::Metrics& m) const { - return fml::HashCombine(m.point_size, m.ascent, m.descent); + return fml::HashCombine(m.scale, m.point_size, m.ascent, m.descent); } }; diff --git a/impeller/typographer/glyph_atlas.h b/impeller/typographer/glyph_atlas.h index e72c3b83e1553..896ca7c8f2d6e 100644 --- a/impeller/typographer/glyph_atlas.h +++ b/impeller/typographer/glyph_atlas.h @@ -47,7 +47,7 @@ class GlyphAtlas { const std::shared_ptr& GetTexture() const; //---------------------------------------------------------------------------- - /// @brief Record there location of a specific font-glyph pair within the + /// @brief Record the location of a specific font-glyph pair within the /// atlas. /// /// @param[in] pair The font-glyph pair From 2eaae68c2554ff04faadbf6889475063113bbca5 Mon Sep 17 00:00:00 2001 From: Brandon DeRosier Date: Mon, 28 Mar 2022 11:44:27 -0700 Subject: [PATCH 373/433] Add difference clipping (#104) --- impeller/aiks/aiks_unittests.cc | 37 +++++++++++ impeller/aiks/canvas.cc | 7 +- impeller/aiks/canvas.h | 4 +- .../display_list/display_list_dispatcher.cc | 15 ++++- impeller/entity/contents/clip_contents.cc | 64 ++++++++++++++++--- impeller/entity/contents/clip_contents.h | 10 +-- impeller/entity/contents/content_context.cc | 45 ++++--------- impeller/entity/contents/content_context.h | 25 +++++--- impeller/entity/entity.h | 5 ++ 9 files changed, 149 insertions(+), 63 deletions(-) diff --git a/impeller/aiks/aiks_unittests.cc b/impeller/aiks/aiks_unittests.cc index 23fd0d4dfafc5..bc7bbb9f5c3dc 100644 --- a/impeller/aiks/aiks_unittests.cc +++ b/impeller/aiks/aiks_unittests.cc @@ -123,6 +123,43 @@ TEST_F(AiksTest, CanRenderNestedClips) { ASSERT_TRUE(OpenPlaygroundHere(canvas.EndRecordingAsPicture())); } +TEST_F(AiksTest, CanRenderDifferenceClips) { + Paint paint; + Canvas canvas; + canvas.Translate({400, 400}); + + // Limit drawing to face circle with a clip. + canvas.ClipPath(PathBuilder{}.AddCircle(Point(), 200).TakePath()); + canvas.Save(); + + // Cut away eyes/mouth using difference clips. + canvas.ClipPath(PathBuilder{}.AddCircle({-100, -50}, 30).TakePath(), + Entity::ClipOperation::kDifference); + canvas.ClipPath(PathBuilder{}.AddCircle({100, -50}, 30).TakePath(), + Entity::ClipOperation::kDifference); + canvas.ClipPath(PathBuilder{} + .AddQuadraticCurve({-100, 50}, {0, 150}, {100, 50}) + .TakePath(), + Entity::ClipOperation::kDifference); + + // Draw a huge yellow rectangle to prove the clipping works. + paint.color = Color::Yellow(); + canvas.DrawRect(Rect::MakeXYWH(-1000, -1000, 2000, 2000), paint); + + // Remove the difference clips and draw hair that partially covers the eyes. + canvas.Restore(); + paint.color = Color::Maroon(); + canvas.DrawPath(PathBuilder{} + .MoveTo({200, -200}) + .HorizontalLineTo(-200) + .VerticalLineTo(-40) + .CubicCurveTo({0, -40}, {0, -80}, {200, -80}) + .TakePath(), + paint); + + ASSERT_TRUE(OpenPlaygroundHere(canvas.EndRecordingAsPicture())); +} + TEST_F(AiksTest, ClipsUseCurrentTransform) { std::array colors = {Color::White(), Color::Black(), Color::SkyBlue(), Color::Red(), diff --git a/impeller/aiks/canvas.cc b/impeller/aiks/canvas.cc index d9464e8f4ef6b..550fd48b4ad67 100644 --- a/impeller/aiks/canvas.cc +++ b/impeller/aiks/canvas.cc @@ -151,11 +151,14 @@ void Canvas::SaveLayer(Paint paint, std::optional bounds) { } } -void Canvas::ClipPath(Path path) { +void Canvas::ClipPath(Path path, Entity::ClipOperation clip_op) { + auto contents = std::make_shared(); + contents->SetClipOperation(clip_op); + Entity entity; entity.SetTransformation(GetCurrentTransformation()); entity.SetPath(std::move(path)); - entity.SetContents(std::make_shared()); + entity.SetContents(std::move(contents)); entity.SetStencilDepth(GetStencilDepth()); entity.SetAddsToCoverage(false); diff --git a/impeller/aiks/canvas.h b/impeller/aiks/canvas.h index 06ebb0f88fa1b..295d3ea6fe5fd 100644 --- a/impeller/aiks/canvas.h +++ b/impeller/aiks/canvas.h @@ -72,7 +72,9 @@ class Canvas { Rect dest, Paint paint); - void ClipPath(Path path); + void ClipPath( + Path path, + Entity::ClipOperation clip_op = Entity::ClipOperation::kIntersect); void DrawShadow(Path path, Color color, Scalar elevation); diff --git a/impeller/display_list/display_list_dispatcher.cc b/impeller/display_list/display_list_dispatcher.cc index 52950a8de4e8b..6ca0db6fc682a 100644 --- a/impeller/display_list/display_list_dispatcher.cc +++ b/impeller/display_list/display_list_dispatcher.cc @@ -159,7 +159,7 @@ void DisplayListDispatcher::setInvertColors(bool invert) { UNIMPLEMENTED; } -std::optional ToBlendMode(flutter::DlBlendMode mode) { +static std::optional ToBlendMode(flutter::DlBlendMode mode) { switch (mode) { case flutter::DlBlendMode::kClear: return Entity::BlendMode::kClear; @@ -351,12 +351,21 @@ static Rect ToRect(const SkRect& rect) { return Rect::MakeLTRB(rect.fLeft, rect.fTop, rect.fRight, rect.fBottom); } +static Entity::ClipOperation ToClipOperation(SkClipOp clip_op) { + switch (clip_op) { + case SkClipOp::kDifference: + return Entity::ClipOperation::kDifference; + case SkClipOp::kIntersect: + return Entity::ClipOperation::kIntersect; + } +} + // |flutter::Dispatcher| void DisplayListDispatcher::clipRect(const SkRect& rect, SkClipOp clip_op, bool is_aa) { auto path = PathBuilder{}.AddRect(ToRect(rect)).TakePath(); - canvas_.ClipPath(std::move(path)); + canvas_.ClipPath(std::move(path), ToClipOperation(clip_op)); } static PathBuilder::RoundingRadii ToRoundingRadii(const SkRRect& rrect) { @@ -444,7 +453,7 @@ static Path ToPath(const SkRRect& rrect) { void DisplayListDispatcher::clipRRect(const SkRRect& rrect, SkClipOp clip_op, bool is_aa) { - canvas_.ClipPath(ToPath(rrect)); + canvas_.ClipPath(ToPath(rrect), ToClipOperation(clip_op)); } // |flutter::Dispatcher| diff --git a/impeller/entity/contents/clip_contents.cc b/impeller/entity/contents/clip_contents.cc index 864ccc855fbdd..df9f89ad0b2ad 100644 --- a/impeller/entity/contents/clip_contents.cc +++ b/impeller/entity/contents/clip_contents.cc @@ -2,6 +2,9 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. +#include "impeller/geometry/path_builder.h" +#include "impeller/renderer/formats.h" +#include "impeller/renderer/vertex_buffer_builder.h" #include "linear_gradient_contents.h" #include "impeller/entity/contents/clip_contents.h" @@ -20,25 +23,64 @@ ClipContents::ClipContents() = default; ClipContents::~ClipContents() = default; +void ClipContents::SetClipOperation(Entity::ClipOperation clip_op) { + clip_op_ = clip_op; +} + bool ClipContents::Render(const ContentContext& renderer, const Entity& entity, RenderPass& pass) const { using VS = ClipPipeline::VertexShader; + VS::FrameInfo info; + // The color really doesn't matter. + info.color = Color::SkyBlue(); + Command cmd; - cmd.label = "Clip"; - cmd.pipeline = - renderer.GetClipPipeline(OptionsFromPassAndEntity(pass, entity)); + auto options = OptionsFromPassAndEntity(pass, entity); cmd.stencil_reference = entity.GetStencilDepth(); + options.stencil_compare = CompareFunction::kEqual; + options.stencil_operation = StencilOperation::kIncrementClamp; + + if (clip_op_ == Entity::ClipOperation::kDifference) { + { + cmd.label = "Difference Clip (Increment)"; + + cmd.primitive_type = PrimitiveType::kTriangleStrip; + auto points = Rect(Size(pass.GetRenderTargetSize())).GetPoints(); + auto vertices = + VertexBufferBuilder{} + .AddVertices({{points[0]}, {points[1]}, {points[2]}, {points[3]}}) + .CreateVertexBuffer(pass.GetTransientsBuffer()); + cmd.BindVertices(std::move(vertices)); + + info.mvp = Matrix::MakeOrthographic(pass.GetRenderTargetSize()); + VS::BindFrameInfo(cmd, pass.GetTransientsBuffer().EmplaceUniform(info)); + + cmd.pipeline = renderer.GetClipPipeline(options); + pass.AddCommand(cmd); + } + + { + cmd.label = "Difference Clip (Punch)"; + + cmd.primitive_type = PrimitiveType::kTriangle; + cmd.stencil_reference = entity.GetStencilDepth() + 1; + options.stencil_compare = CompareFunction::kEqual; + options.stencil_operation = StencilOperation::kDecrementClamp; + } + } else { + cmd.label = "Intersect Clip"; + options.stencil_compare = CompareFunction::kEqual; + options.stencil_operation = StencilOperation::kIncrementClamp; + } + + cmd.pipeline = renderer.GetClipPipeline(options); cmd.BindVertices(SolidColorContents::CreateSolidFillVertices( entity.GetPath(), pass.GetTransientsBuffer())); - VS::FrameInfo info; - // The color really doesn't matter. - info.color = Color::SkyBlue(); info.mvp = Matrix::MakeOrthographic(pass.GetRenderTargetSize()) * entity.GetTransformation(); - VS::BindFrameInfo(cmd, pass.GetTransientsBuffer().EmplaceUniform(info)); pass.AddCommand(std::move(cmd)); @@ -59,9 +101,11 @@ bool ClipRestoreContents::Render(const ContentContext& renderer, using VS = ClipPipeline::VertexShader; Command cmd; - cmd.label = "Clip Restore"; - cmd.pipeline = - renderer.GetClipRestorePipeline(OptionsFromPassAndEntity(pass, entity)); + cmd.label = "Restore Clip"; + auto options = OptionsFromPassAndEntity(pass, entity); + options.stencil_compare = CompareFunction::kLess; + options.stencil_operation = StencilOperation::kSetToReferenceValue; + cmd.pipeline = renderer.GetClipPipeline(options); cmd.stencil_reference = entity.GetStencilDepth(); // Create a rect that covers the whole render target. diff --git a/impeller/entity/contents/clip_contents.h b/impeller/entity/contents/clip_contents.h index 1481873ae736b..722b35776cc1c 100644 --- a/impeller/entity/contents/clip_contents.h +++ b/impeller/entity/contents/clip_contents.h @@ -10,24 +10,26 @@ #include "flutter/fml/macros.h" #include "impeller/entity/contents/contents.h" -#include "impeller/typographer/glyph_atlas.h" +#include "impeller/entity/entity.h" namespace impeller { -class GlyphAtlas; - class ClipContents final : public Contents { public: ClipContents(); ~ClipContents(); + void SetClipOperation(Entity::ClipOperation clip_op); + // |Contents| bool Render(const ContentContext& renderer, const Entity& entity, RenderPass& pass) const override; private: + Entity::ClipOperation clip_op_ = Entity::ClipOperation::kIntersect; + FML_DISALLOW_COPY_AND_ASSIGN(ClipContents); }; @@ -37,8 +39,6 @@ class ClipRestoreContents final : public Contents { ~ClipRestoreContents(); - void SetGlyphAtlas(std::shared_ptr atlas); - // |Contents| bool Render(const ContentContext& renderer, const Entity& entity, diff --git a/impeller/entity/contents/content_context.cc b/impeller/entity/contents/content_context.cc index ae42a008691e3..36dc90ca0bb0f 100644 --- a/impeller/entity/contents/content_context.cc +++ b/impeller/entity/contents/content_context.cc @@ -33,41 +33,20 @@ ContentContext::ContentContext(std::shared_ptr context) // TODO(98684): Rework this API to allow fetching the descriptor without // waiting for the pipeline to build. if (auto solid_fill_pipeline = solid_fill_pipelines_[{}]->WaitAndGet()) { - // Clip pipeline. - { - auto clip_pipeline_descriptor = solid_fill_pipeline->GetDescriptor(); - clip_pipeline_descriptor.SetLabel("Clip Pipeline"); - // Write to the stencil buffer. - StencilAttachmentDescriptor stencil0; - stencil0.stencil_compare = CompareFunction::kEqual; - stencil0.depth_stencil_pass = StencilOperation::kIncrementClamp; - clip_pipeline_descriptor.SetStencilAttachmentDescriptors(stencil0); - // Disable write to all color attachments. - auto color_attachments = - clip_pipeline_descriptor.GetColorAttachmentDescriptors(); - for (auto& color_attachment : color_attachments) { - color_attachment.second.write_mask = - static_cast(ColorWriteMask::kNone); - } - clip_pipeline_descriptor.SetColorAttachmentDescriptors( - std::move(color_attachments)); - clip_pipelines_[{}] = - std::make_unique(*context_, clip_pipeline_descriptor); + auto clip_pipeline_descriptor = solid_fill_pipeline->GetDescriptor(); + clip_pipeline_descriptor.SetLabel("Clip Pipeline"); + // Disable write to all color attachments. + auto color_attachments = + clip_pipeline_descriptor.GetColorAttachmentDescriptors(); + for (auto& color_attachment : color_attachments) { + color_attachment.second.write_mask = + static_cast(ColorWriteMask::kNone); } + clip_pipeline_descriptor.SetColorAttachmentDescriptors( + std::move(color_attachments)); + clip_pipelines_[{}] = + std::make_unique(*context_, clip_pipeline_descriptor); - // Clip restoration pipeline. - { - auto clip_pipeline_descriptor = - clip_pipelines_[{}]->WaitAndGet()->GetDescriptor(); - clip_pipeline_descriptor.SetLabel("Clip Restoration Pipeline"); - // Write to the stencil buffer. - StencilAttachmentDescriptor stencil0; - stencil0.stencil_compare = CompareFunction::kLess; - stencil0.depth_stencil_pass = StencilOperation::kSetToReferenceValue; - clip_pipeline_descriptor.SetStencilAttachmentDescriptors(stencil0); - clip_restoration_pipelines_[{}] = std::make_unique( - *context_, std::move(clip_pipeline_descriptor)); - } } else { return; } diff --git a/impeller/entity/contents/content_context.h b/impeller/entity/contents/content_context.h index d5ff659d5ea6d..fc406d4f3dc1d 100644 --- a/impeller/entity/contents/content_context.h +++ b/impeller/entity/contents/content_context.h @@ -48,17 +48,20 @@ using SolidStrokePipeline = PipelineT; using GlyphAtlasPipeline = PipelineT; -// Instead of requiring new shaders for clips, the solid fill stages are used +// Instead of requiring new shaders for clips, the solid fill stages are used // to redirect writing to the stencil instead of color attachments. using ClipPipeline = PipelineT; struct ContentContextOptions { SampleCount sample_count = SampleCount::kCount1; Entity::BlendMode blend_mode = Entity::BlendMode::kSourceOver; + CompareFunction stencil_compare = CompareFunction::kEqual; + StencilOperation stencil_operation = StencilOperation::kKeep; struct Hash { constexpr std::size_t operator()(const ContentContextOptions& o) const { - return fml::HashCombine(o.sample_count, o.blend_mode); + return fml::HashCombine(o.sample_count, o.blend_mode, o.stencil_compare, + o.stencil_operation); } }; @@ -66,7 +69,9 @@ struct ContentContextOptions { constexpr bool operator()(const ContentContextOptions& lhs, const ContentContextOptions& rhs) const { return lhs.sample_count == rhs.sample_count && - lhs.blend_mode == rhs.blend_mode; + lhs.blend_mode == rhs.blend_mode && + lhs.stencil_compare == rhs.stencil_compare && + lhs.stencil_operation == rhs.stencil_operation; } }; }; @@ -118,11 +123,6 @@ class ContentContext { return GetPipeline(clip_pipelines_, opts); } - std::shared_ptr GetClipRestorePipeline( - ContentContextOptions opts) const { - return GetPipeline(clip_restoration_pipelines_, opts); - } - std::shared_ptr GetGlyphAtlasPipeline( ContentContextOptions opts) const { return GetPipeline(glyph_atlas_pipelines_, opts); @@ -150,7 +150,6 @@ class ContentContext { mutable Variants gaussian_blur_pipelines_; mutable Variants solid_stroke_pipelines_; mutable Variants clip_pipelines_; - mutable Variants clip_restoration_pipelines_; mutable Variants glyph_atlas_pipelines_; static void ApplyOptionsToDescriptor(PipelineDescriptor& desc, @@ -262,6 +261,14 @@ class ContentContext { FML_UNREACHABLE(); } desc.SetColorAttachmentDescriptor(0u, std::move(color0)); + + if (desc.GetFrontStencilAttachmentDescriptor().has_value()) { + StencilAttachmentDescriptor stencil = + desc.GetFrontStencilAttachmentDescriptor().value(); + stencil.stencil_compare = options.stencil_compare; + stencil.depth_stencil_pass = options.stencil_operation; + desc.SetStencilAttachmentDescriptors(stencil); + } } template diff --git a/impeller/entity/entity.h b/impeller/entity/entity.h index 80e755e781f09..0f13dca6469a8 100644 --- a/impeller/entity/entity.h +++ b/impeller/entity/entity.h @@ -48,6 +48,11 @@ class Entity { kLastAdvancedBlendMode = kScreen, }; + enum class ClipOperation { + kDifference, + kIntersect, + }; + Entity(); ~Entity(); From b00d7c6eb0d4413eed09cd6f8ce2da3f5cc27653 Mon Sep 17 00:00:00 2001 From: Brandon DeRosier Date: Mon, 28 Mar 2022 11:54:09 -0700 Subject: [PATCH 374/433] Wire up stroke cap/join/miter limit display list ops (#105) --- impeller/aiks/paint.cc | 3 ++ impeller/aiks/paint.h | 4 ++ .../display_list/display_list_dispatcher.cc | 31 ++++++++++--- .../display_list/display_list_unittests.cc | 46 +++++++++++++++++++ 4 files changed, 77 insertions(+), 7 deletions(-) diff --git a/impeller/aiks/paint.cc b/impeller/aiks/paint.cc index d79a67b71453a..ac1f712908fc5 100644 --- a/impeller/aiks/paint.cc +++ b/impeller/aiks/paint.cc @@ -23,6 +23,9 @@ std::shared_ptr Paint::CreateContentsForEntity() const { auto solid_stroke = std::make_shared(); solid_stroke->SetColor(color.Premultiply()); solid_stroke->SetStrokeSize(stroke_width); + solid_stroke->SetStrokeMiter(stroke_miter); + solid_stroke->SetStrokeCap(stroke_cap); + solid_stroke->SetStrokeJoin(stroke_join); return solid_stroke; } } diff --git a/impeller/aiks/paint.h b/impeller/aiks/paint.h index a559c4c1a9fa9..be9262d0225ac 100644 --- a/impeller/aiks/paint.h +++ b/impeller/aiks/paint.h @@ -8,6 +8,7 @@ #include "flutter/fml/macros.h" #include "impeller/entity/contents/contents.h" +#include "impeller/entity/contents/solid_stroke_contents.h" #include "impeller/entity/entity.h" #include "impeller/geometry/color.h" @@ -21,6 +22,9 @@ struct Paint { Color color = Color::Black(); Scalar stroke_width = 0.0; + SolidStrokeContents::Cap stroke_cap = SolidStrokeContents::Cap::kButt; + SolidStrokeContents::Join stroke_join = SolidStrokeContents::Join::kMiter; + Scalar stroke_miter = 4.0; Style style = Style::kFill; Entity::BlendMode blend_mode = Entity::BlendMode::kSourceOver; std::shared_ptr contents; diff --git a/impeller/display_list/display_list_dispatcher.cc b/impeller/display_list/display_list_dispatcher.cc index 6ca0db6fc682a..6dba4f2fbd0b9 100644 --- a/impeller/display_list/display_list_dispatcher.cc +++ b/impeller/display_list/display_list_dispatcher.cc @@ -8,6 +8,7 @@ #include "flutter/fml/trace_event.h" #include "impeller/entity/contents/linear_gradient_contents.h" +#include "impeller/entity/contents/solid_stroke_contents.h" #include "impeller/entity/entity.h" #include "impeller/geometry/path_builder.h" #include "impeller/typographer/backends/skia/text_frame_skia.h" @@ -65,17 +66,37 @@ void DisplayListDispatcher::setStrokeWidth(SkScalar width) { // |flutter::Dispatcher| void DisplayListDispatcher::setStrokeMiter(SkScalar limit) { - UNIMPLEMENTED; + paint_.stroke_miter = limit; } // |flutter::Dispatcher| void DisplayListDispatcher::setStrokeCap(SkPaint::Cap cap) { - UNIMPLEMENTED; + switch (cap) { + case SkPaint::kButt_Cap: + paint_.stroke_cap = SolidStrokeContents::Cap::kButt; + break; + case SkPaint::kRound_Cap: + paint_.stroke_cap = SolidStrokeContents::Cap::kRound; + break; + case SkPaint::kSquare_Cap: + paint_.stroke_cap = SolidStrokeContents::Cap::kSquare; + break; + } } // |flutter::Dispatcher| void DisplayListDispatcher::setStrokeJoin(SkPaint::Join join) { - UNIMPLEMENTED; + switch (join) { + case SkPaint::kMiter_Join: + paint_.stroke_join = SolidStrokeContents::Join::kMiter; + break; + case SkPaint::kRound_Join: + paint_.stroke_join = SolidStrokeContents::Join::kRound; + break; + case SkPaint::kBevel_Join: + paint_.stroke_join = SolidStrokeContents::Join::kBevel; + break; + } } static Point ToPoint(const SkPoint& point) { @@ -397,11 +418,9 @@ static Path ToPath(const SkPath& path) { builder.MoveTo(ToPoint(data.points[0])); break; case SkPath::kLine_Verb: - builder.LineTo(ToPoint(data.points[0])); builder.LineTo(ToPoint(data.points[1])); break; case SkPath::kQuad_Verb: - builder.LineTo(ToPoint(data.points[0])); builder.QuadraticCurveTo(ToPoint(data.points[1]), ToPoint(data.points[2])); break; @@ -422,13 +441,11 @@ static Path ToPath(const SkPath& path) { curve_index < curve_count; // curve_index++, point_index += 2 // ) { - builder.LineTo(ToPoint(points[point_index + 0])); builder.QuadraticCurveTo(ToPoint(points[point_index + 1]), ToPoint(points[point_index + 2])); } } break; case SkPath::kCubic_Verb: - builder.LineTo(ToPoint(data.points[0])); builder.CubicCurveTo(ToPoint(data.points[1]), ToPoint(data.points[2]), ToPoint(data.points[3])); break; diff --git a/impeller/display_list/display_list_unittests.cc b/impeller/display_list/display_list_unittests.cc index 6fef7b37f5c89..ecfaf36e23fe9 100644 --- a/impeller/display_list/display_list_unittests.cc +++ b/impeller/display_list/display_list_unittests.cc @@ -5,6 +5,7 @@ #include "flutter/display_list/display_list_builder.h" #include "flutter/testing/testing.h" #include "impeller/display_list/display_list_playground.h" +#include "third_party/skia/include/core/SkPathBuilder.h" namespace impeller { namespace testing { @@ -26,5 +27,50 @@ TEST_F(DisplayListTest, CanDrawTextBlob) { ASSERT_TRUE(OpenPlaygroundHere(builder.Build())); } +TEST_F(DisplayListTest, CapsAndJoins) { + flutter::DisplayListBuilder builder; + + builder.setStyle(SkPaint::Style::kStroke_Style); + builder.setStrokeWidth(30); + builder.setColor(SK_ColorRED); + + auto path = + SkPathBuilder{}.moveTo(-50, 0).lineTo(0, -50).lineTo(50, 0).snapshot(); + + builder.translate(100, 100); + { + builder.setStrokeCap(SkPaint::Cap::kButt_Cap); + builder.setStrokeJoin(SkPaint::Join::kMiter_Join); + builder.setStrokeMiter(4); + builder.drawPath(path); + } + + { + builder.save(); + builder.translate(0, 100); + // The joint in the path is 45 degrees. A miter length of 1 convert to a + // bevel in this case. + builder.setStrokeMiter(1); + builder.drawPath(path); + builder.restore(); + } + + builder.translate(150, 0); + { + builder.setStrokeCap(SkPaint::Cap::kSquare_Cap); + builder.setStrokeJoin(SkPaint::Join::kBevel_Join); + builder.drawPath(path); + } + + builder.translate(150, 0); + { + builder.setStrokeCap(SkPaint::Cap::kRound_Cap); + builder.setStrokeJoin(SkPaint::Join::kRound_Join); + builder.drawPath(path); + } + + ASSERT_TRUE(OpenPlaygroundHere(builder.Build())); +} + } // namespace testing } // namespace impeller From 4f28758a4d24c72121999ff749c45ffe0b64a3f6 Mon Sep 17 00:00:00 2001 From: Brandon DeRosier Date: Mon, 28 Mar 2022 12:31:38 -0700 Subject: [PATCH 375/433] Premultiply gradient colors (#106) --- impeller/entity/contents/linear_gradient_contents.cc | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/impeller/entity/contents/linear_gradient_contents.cc b/impeller/entity/contents/linear_gradient_contents.cc index 10a2c08f86198..092eee4ff18bc 100644 --- a/impeller/entity/contents/linear_gradient_contents.cc +++ b/impeller/entity/contents/linear_gradient_contents.cc @@ -61,8 +61,8 @@ bool LinearGradientContents::Render(const ContentContext& renderer, FS::GradientInfo gradient_info; gradient_info.start_point = start_point_; gradient_info.end_point = end_point_; - gradient_info.start_color = colors_[0]; - gradient_info.end_color = colors_[1]; + gradient_info.start_color = colors_[0].Premultiply(); + gradient_info.end_color = colors_[1].Premultiply(); Command cmd; cmd.label = "LinearGradientFill"; From d33a9a048d9db8c2c8bc3dd25f2bd0a3e72a0cb4 Mon Sep 17 00:00:00 2001 From: Chinmay Garde Date: Tue, 29 Mar 2022 13:22:31 -0700 Subject: [PATCH 376/433] Use display list images. (#102) --- impeller/BUILD.gn | 8 +++ impeller/aiks/aiks_unittests.cc | 2 +- impeller/aiks/canvas.cc | 14 ++-- impeller/aiks/canvas.h | 11 ++- impeller/aiks/paint_pass_delegate.cc | 2 +- impeller/display_list/BUILD.gn | 2 + .../display_list/display_list_dispatcher.cc | 70 +++++++++++++++---- .../display_list/display_list_dispatcher.h | 10 +-- .../display_list_image_impeller.cc | 53 ++++++++++++++ .../display_list_image_impeller.h | 43 ++++++++++++ .../display_list/display_list_unittests.cc | 9 +++ .../contents/filters/filter_contents.cc | 4 +- impeller/entity/contents/texture_contents.cc | 18 +++-- impeller/entity/contents/texture_contents.h | 14 ++-- impeller/geometry/rect.h | 2 +- .../playground/imgui/imgui_impl_impeller.cc | 2 +- .../backend/metal/device_buffer_mtl.mm | 2 +- .../renderer/backend/metal/texture_mtl.mm | 2 +- impeller/renderer/texture_descriptor.h | 2 +- .../backends/skia/text_render_context_skia.cc | 2 +- 20 files changed, 227 insertions(+), 45 deletions(-) create mode 100644 impeller/display_list/display_list_image_impeller.cc create mode 100644 impeller/display_list/display_list_image_impeller.h diff --git a/impeller/BUILD.gn b/impeller/BUILD.gn index 8e372e58f630c..c7ae5aa956f67 100644 --- a/impeller/BUILD.gn +++ b/impeller/BUILD.gn @@ -2,8 +2,16 @@ # Use of this source code is governed by a BSD-style license that can be # found in the LICENSE file. +import("tools/impeller.gni") + config("impeller_public_config") { include_dirs = [ ".." ] + + defines = [] + + if (impeller_supports_platform) { + defines += [ "IMPELLER_SUPPORTS_PLATFORM=1" ] + } } group("impeller") { diff --git a/impeller/aiks/aiks_unittests.cc b/impeller/aiks/aiks_unittests.cc index bc7bbb9f5c3dc..e554bb30acaa8 100644 --- a/impeller/aiks/aiks_unittests.cc +++ b/impeller/aiks/aiks_unittests.cc @@ -67,7 +67,7 @@ TEST_F(AiksTest, CanRenderImageRect) { Canvas canvas; Paint paint; auto image = std::make_shared(CreateTextureForFixture("kalimba.jpg")); - auto source_rect = IRect::MakeSize(image->GetSize()); + auto source_rect = Rect::MakeSize(Size(image->GetSize())); // Render the bottom right quarter of the source image in a stretched rect. source_rect.size.width /= 2; diff --git a/impeller/aiks/canvas.cc b/impeller/aiks/canvas.cc index 550fd48b4ad67..a4afb84e09b94 100644 --- a/impeller/aiks/canvas.cc +++ b/impeller/aiks/canvas.cc @@ -200,22 +200,24 @@ void Canvas::DrawPicture(Picture picture) { void Canvas::DrawImage(std::shared_ptr image, Point offset, - Paint paint) { + Paint paint, + SamplerDescriptor sampler) { if (!image) { return; } - const auto source = IRect::MakeSize(image->GetSize()); + const auto source = Rect::MakeSize(Size(image->GetSize())); const auto dest = Rect::MakeXYWH(offset.x, offset.y, source.size.width, source.size.height); - DrawImageRect(image, source, dest, std::move(paint)); + DrawImageRect(image, source, dest, std::move(paint), std::move(sampler)); } void Canvas::DrawImageRect(std::shared_ptr image, - IRect source, + Rect source, Rect dest, - Paint paint) { + Paint paint, + SamplerDescriptor sampler) { if (!image || source.size.IsEmpty() || dest.size.IsEmpty()) { return; } @@ -229,10 +231,12 @@ void Canvas::DrawImageRect(std::shared_ptr image, auto contents = std::make_shared(); contents->SetTexture(image->GetTexture()); contents->SetSourceRect(source); + contents->SetSamplerDescriptor(std::move(sampler)); Entity entity; entity.SetPath(PathBuilder{}.AddRect(dest).TakePath()); entity.SetBlendMode(paint.blend_mode); + entity.SetStencilDepth(GetStencilDepth()); entity.SetContents(contents); entity.SetTransformation(GetCurrentTransformation()); diff --git a/impeller/aiks/canvas.h b/impeller/aiks/canvas.h index 295d3ea6fe5fd..30eed9b6f07a5 100644 --- a/impeller/aiks/canvas.h +++ b/impeller/aiks/canvas.h @@ -18,6 +18,7 @@ #include "impeller/geometry/path.h" #include "impeller/geometry/point.h" #include "impeller/geometry/vector.h" +#include "impeller/renderer/sampler_descriptor.h" #include "impeller/typographer/glyph_atlas.h" #include "impeller/typographer/text_frame.h" @@ -65,12 +66,16 @@ class Canvas { void DrawCircle(Point center, Scalar radius, Paint paint); - void DrawImage(std::shared_ptr image, Point offset, Paint paint); + void DrawImage(std::shared_ptr image, + Point offset, + Paint paint, + SamplerDescriptor sampler = {}); void DrawImageRect(std::shared_ptr image, - IRect source, + Rect source, Rect dest, - Paint paint); + Paint paint, + SamplerDescriptor sampler = {}); void ClipPath( Path path, diff --git a/impeller/aiks/paint_pass_delegate.cc b/impeller/aiks/paint_pass_delegate.cc index f17ba655540cf..1e7b38cbd5c89 100644 --- a/impeller/aiks/paint_pass_delegate.cc +++ b/impeller/aiks/paint_pass_delegate.cc @@ -35,7 +35,7 @@ std::shared_ptr PaintPassDelegate::CreateContentsForSubpassTarget( std::shared_ptr target) { auto contents = std::make_shared(); contents->SetTexture(target); - contents->SetSourceRect(IRect::MakeSize(target->GetSize())); + contents->SetSourceRect(Rect::MakeSize(Size(target->GetSize()))); contents->SetOpacity(paint_.color.alpha); return contents; } diff --git a/impeller/display_list/BUILD.gn b/impeller/display_list/BUILD.gn index e3f51afb3554a..79d711c5a81f4 100644 --- a/impeller/display_list/BUILD.gn +++ b/impeller/display_list/BUILD.gn @@ -8,6 +8,8 @@ impeller_component("display_list") { sources = [ "display_list_dispatcher.cc", "display_list_dispatcher.h", + "display_list_image_impeller.cc", + "display_list_image_impeller.h", ] public_deps = [ diff --git a/impeller/display_list/display_list_dispatcher.cc b/impeller/display_list/display_list_dispatcher.cc index 6dba4f2fbd0b9..c4d61dafc8fb2 100644 --- a/impeller/display_list/display_list_dispatcher.cc +++ b/impeller/display_list/display_list_dispatcher.cc @@ -564,28 +564,71 @@ void DisplayListDispatcher::drawVertices(const sk_sp vertices, } // |flutter::Dispatcher| -void DisplayListDispatcher::drawImage(const sk_sp image, +void DisplayListDispatcher::drawImage(const sk_sp image, const SkPoint point, const SkSamplingOptions& sampling, bool render_with_attributes) { - // Needs https://github.com/flutter/flutter/issues/95434 - UNIMPLEMENTED; + if (!image) { + return; + } + + auto texture = image->impeller_texture(); + if (!texture) { + return; + } + + const auto size = texture->GetSize(); + const auto src = SkRect::MakeWH(size.width, size.height); + const auto dest = + SkRect::MakeXYWH(point.fX, point.fY, size.width, size.height); + + drawImageRect( + image, // image + src, // source rect + dest, // destination rect + sampling, // sampling options + render_with_attributes, // render with attributes + SkCanvas::SrcRectConstraint::kStrict_SrcRectConstraint // constraint + ); +} + +static impeller::SamplerDescriptor ToSamplerDescriptor( + const SkSamplingOptions& options) { + impeller::SamplerDescriptor desc; + switch (options.filter) { + case SkFilterMode::kNearest: + desc.min_filter = desc.mag_filter = impeller::MinMagFilter::kNearest; + desc.label = "Nearest Sampler"; + break; + case SkFilterMode::kLinear: + desc.min_filter = desc.mag_filter = impeller::MinMagFilter::kLinear; + desc.label = "Linear Sampler"; + break; + default: + break; + } + return desc; } // |flutter::Dispatcher| void DisplayListDispatcher::drawImageRect( - const sk_sp image, + const sk_sp image, const SkRect& src, const SkRect& dst, const SkSamplingOptions& sampling, bool render_with_attributes, SkCanvas::SrcRectConstraint constraint) { - // Needs https://github.com/flutter/flutter/issues/95434 - UNIMPLEMENTED; + canvas_.DrawImageRect( + std::make_shared(image->impeller_texture()), // image + ToRect(src), // source rect + ToRect(dst), // destination rect + paint_, // paint + ToSamplerDescriptor(sampling) // sampling + ); } // |flutter::Dispatcher| -void DisplayListDispatcher::drawImageNine(const sk_sp image, +void DisplayListDispatcher::drawImageNine(const sk_sp image, const SkIRect& center, const SkRect& dst, SkFilterMode filter, @@ -595,17 +638,18 @@ void DisplayListDispatcher::drawImageNine(const sk_sp image, } // |flutter::Dispatcher| -void DisplayListDispatcher::drawImageLattice(const sk_sp image, - const SkCanvas::Lattice& lattice, - const SkRect& dst, - SkFilterMode filter, - bool render_with_attributes) { +void DisplayListDispatcher::drawImageLattice( + const sk_sp image, + const SkCanvas::Lattice& lattice, + const SkRect& dst, + SkFilterMode filter, + bool render_with_attributes) { // Needs https://github.com/flutter/flutter/issues/95434 UNIMPLEMENTED; } // |flutter::Dispatcher| -void DisplayListDispatcher::drawAtlas(const sk_sp atlas, +void DisplayListDispatcher::drawAtlas(const sk_sp atlas, const SkRSXform xform[], const SkRect tex[], const SkColor colors[], diff --git a/impeller/display_list/display_list_dispatcher.h b/impeller/display_list/display_list_dispatcher.h index 4e84ee69afd81..1c15eed83c145 100644 --- a/impeller/display_list/display_list_dispatcher.h +++ b/impeller/display_list/display_list_dispatcher.h @@ -172,13 +172,13 @@ class DisplayListDispatcher final : public flutter::Dispatcher { flutter::DlBlendMode mode) override; // |flutter::Dispatcher| - void drawImage(const sk_sp image, + void drawImage(const sk_sp image, const SkPoint point, const SkSamplingOptions& sampling, bool render_with_attributes) override; // |flutter::Dispatcher| - void drawImageRect(const sk_sp image, + void drawImageRect(const sk_sp image, const SkRect& src, const SkRect& dst, const SkSamplingOptions& sampling, @@ -186,21 +186,21 @@ class DisplayListDispatcher final : public flutter::Dispatcher { SkCanvas::SrcRectConstraint constraint) override; // |flutter::Dispatcher| - void drawImageNine(const sk_sp image, + void drawImageNine(const sk_sp image, const SkIRect& center, const SkRect& dst, SkFilterMode filter, bool render_with_attributes) override; // |flutter::Dispatcher| - void drawImageLattice(const sk_sp image, + void drawImageLattice(const sk_sp image, const SkCanvas::Lattice& lattice, const SkRect& dst, SkFilterMode filter, bool render_with_attributes) override; // |flutter::Dispatcher| - void drawAtlas(const sk_sp atlas, + void drawAtlas(const sk_sp atlas, const SkRSXform xform[], const SkRect tex[], const SkColor colors[], diff --git a/impeller/display_list/display_list_image_impeller.cc b/impeller/display_list/display_list_image_impeller.cc new file mode 100644 index 0000000000000..b4627c0da6776 --- /dev/null +++ b/impeller/display_list/display_list_image_impeller.cc @@ -0,0 +1,53 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "impeller/display_list/display_list_image_impeller.h" + +namespace impeller { + +sk_sp DlImageImpeller::Make(std::shared_ptr texture) { + if (!texture) { + return nullptr; + } + return sk_sp(new DlImageImpeller(std::move(texture))); +} + +DlImageImpeller::DlImageImpeller(std::shared_ptr texture) + : texture_(std::move(texture)) {} + +// |DlImage| +DlImageImpeller::~DlImageImpeller() = default; + +// |DlImage| +sk_sp DlImageImpeller::skia_image() const { + return nullptr; +}; + +// |DlImage| +std::shared_ptr DlImageImpeller::impeller_texture() const { + return texture_; +} + +// |DlImage| +bool DlImageImpeller::isTextureBacked() const { + // Impeller textures are always ... textures :/ + return true; +} + +// |DlImage| +SkISize DlImageImpeller::dimensions() const { + const auto size = texture_ ? texture_->GetSize() : ISize{}; + return SkISize::Make(size.width, size.height); +} + +// |DlImage| +size_t DlImageImpeller::GetApproximateByteSize() const { + auto size = sizeof(this); + if (texture_) { + size += texture_->GetTextureDescriptor().GetByteSizeOfBaseMipLevel(); + } + return size; +} + +} // namespace impeller diff --git a/impeller/display_list/display_list_image_impeller.h b/impeller/display_list/display_list_image_impeller.h new file mode 100644 index 0000000000000..652b861f85c77 --- /dev/null +++ b/impeller/display_list/display_list_image_impeller.h @@ -0,0 +1,43 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#pragma once + +#include "flutter/display_list/display_list_image.h" +#include "flutter/fml/macros.h" +#include "impeller/renderer/texture.h" + +namespace impeller { + +class DlImageImpeller final : public flutter::DlImage { + public: + static sk_sp Make(std::shared_ptr texture); + + // |DlImage| + ~DlImageImpeller() override; + + // |DlImage| + sk_sp skia_image() const override; + + // |DlImage| + std::shared_ptr impeller_texture() const override; + + // |DlImage| + bool isTextureBacked() const override; + + // |DlImage| + SkISize dimensions() const override; + + // |DlImage| + size_t GetApproximateByteSize() const override; + + private: + std::shared_ptr texture_; + + explicit DlImageImpeller(std::shared_ptr texture); + + FML_DISALLOW_COPY_AND_ASSIGN(DlImageImpeller); +}; + +} // namespace impeller diff --git a/impeller/display_list/display_list_unittests.cc b/impeller/display_list/display_list_unittests.cc index ecfaf36e23fe9..d8487de313523 100644 --- a/impeller/display_list/display_list_unittests.cc +++ b/impeller/display_list/display_list_unittests.cc @@ -4,6 +4,7 @@ #include "flutter/display_list/display_list_builder.h" #include "flutter/testing/testing.h" +#include "impeller/display_list/display_list_image_impeller.h" #include "impeller/display_list/display_list_playground.h" #include "third_party/skia/include/core/SkPathBuilder.h" @@ -27,6 +28,14 @@ TEST_F(DisplayListTest, CanDrawTextBlob) { ASSERT_TRUE(OpenPlaygroundHere(builder.Build())); } +TEST_F(DisplayListTest, CanDrawImage) { + auto texture = CreateTextureForFixture("embarcadero.jpg"); + flutter::DisplayListBuilder builder; + builder.drawImage(DlImageImpeller::Make(texture), SkPoint::Make(100, 100), + SkSamplingOptions{}, true); + ASSERT_TRUE(OpenPlaygroundHere(builder.Build())); +} + TEST_F(DisplayListTest, CapsAndJoins) { flutter::DisplayListBuilder builder; diff --git a/impeller/entity/contents/filters/filter_contents.cc b/impeller/entity/contents/filters/filter_contents.cc index b799fc2ae4be2..e270857d81f6c 100644 --- a/impeller/entity/contents/filters/filter_contents.cc +++ b/impeller/entity/contents/filters/filter_contents.cc @@ -99,7 +99,7 @@ bool FilterContents::Render(const ContentContext& renderer, auto contents = std::make_shared(); contents->SetTexture(snapshot.texture); - contents->SetSourceRect(IRect::MakeSize(snapshot.texture->GetSize())); + contents->SetSourceRect(Rect::MakeSize(Size(snapshot.texture->GetSize()))); Entity e; e.SetPath(PathBuilder{}.AddRect(GetBounds(entity)).GetCurrentPath()); @@ -162,7 +162,7 @@ static std::optional ResolveSnapshotForInput( const ContentContext& renderer, RenderPass& pass) -> bool { TextureContents contents; contents.SetTexture(texture); - contents.SetSourceRect(IRect::MakeSize(texture->GetSize())); + contents.SetSourceRect(Rect::MakeSize(Size(texture->GetSize()))); Entity sub_entity; sub_entity.SetPath(entity.GetPath()); sub_entity.SetBlendMode(Entity::BlendMode::kSource); diff --git a/impeller/entity/contents/texture_contents.cc b/impeller/entity/contents/texture_contents.cc index e2397aaff6a20..024140093b994 100644 --- a/impeller/entity/contents/texture_contents.cc +++ b/impeller/entity/contents/texture_contents.cc @@ -96,20 +96,28 @@ bool TextureContents::Render(const ContentContext& renderer, cmd.stencil_reference = entity.GetStencilDepth(); cmd.BindVertices(vertex_builder.CreateVertexBuffer(host_buffer)); VS::BindFrameInfo(cmd, host_buffer.EmplaceUniform(frame_info)); - FS::BindTextureSampler( - cmd, texture_, - renderer.GetContext()->GetSamplerLibrary()->GetSampler({})); + FS::BindTextureSampler(cmd, texture_, + renderer.GetContext()->GetSamplerLibrary()->GetSampler( + sampler_descriptor_)); pass.AddCommand(std::move(cmd)); return true; } -void TextureContents::SetSourceRect(const IRect& source_rect) { +void TextureContents::SetSourceRect(const Rect& source_rect) { source_rect_ = source_rect; } -const IRect& TextureContents::GetSourceRect() const { +const Rect& TextureContents::GetSourceRect() const { return source_rect_; } +void TextureContents::SetSamplerDescriptor(SamplerDescriptor desc) { + sampler_descriptor_ = std::move(desc); +} + +const SamplerDescriptor& TextureContents::GetSamplerDescriptor() const { + return sampler_descriptor_; +} + } // namespace impeller diff --git a/impeller/entity/contents/texture_contents.h b/impeller/entity/contents/texture_contents.h index c94be1f39a5ff..7b994a6c06e0e 100644 --- a/impeller/entity/contents/texture_contents.h +++ b/impeller/entity/contents/texture_contents.h @@ -10,6 +10,7 @@ #include "flutter/fml/macros.h" #include "impeller/entity/contents/contents.h" +#include "impeller/renderer/sampler_descriptor.h" namespace impeller { @@ -25,11 +26,15 @@ class TextureContents final : public Contents { std::shared_ptr GetTexture() const; - void SetSourceRect(const IRect& source_rect); + void SetSamplerDescriptor(SamplerDescriptor desc); - void SetOpacity(Scalar opacity); + const SamplerDescriptor& GetSamplerDescriptor() const; + + void SetSourceRect(const Rect& source_rect); - const IRect& GetSourceRect() const; + const Rect& GetSourceRect() const; + + void SetOpacity(Scalar opacity); // |Contents| bool Render(const ContentContext& renderer, @@ -38,7 +43,8 @@ class TextureContents final : public Contents { public: std::shared_ptr texture_; - IRect source_rect_; + SamplerDescriptor sampler_descriptor_ = {}; + Rect source_rect_; Scalar opacity_ = 1.0f; FML_DISALLOW_COPY_AND_ASSIGN(TextureContents); diff --git a/impeller/geometry/rect.h b/impeller/geometry/rect.h index 5d5f4224b1996..dd12a5c69a697 100644 --- a/impeller/geometry/rect.h +++ b/impeller/geometry/rect.h @@ -58,7 +58,7 @@ struct TRect { auto right = points[0].x; auto bottom = points[0].y; if (points.size() > 1) { - for (uint i = 1; i < points.size(); i++) { + for (size_t i = 1; i < points.size(); i++) { left = std::min(left, points[i].x); top = std::min(top, points[i].y); right = std::max(right, points[i].x); diff --git a/impeller/playground/imgui/imgui_impl_impeller.cc b/impeller/playground/imgui/imgui_impl_impeller.cc index 245397fc0f450..682ee9bb38163 100644 --- a/impeller/playground/imgui/imgui_impl_impeller.cc +++ b/impeller/playground/imgui/imgui_impl_impeller.cc @@ -78,7 +78,7 @@ bool ImGui_ImplImpeller_Init(std::shared_ptr context) { "Could not allocate ImGui font texture."); [[maybe_unused]] bool uploaded = bd->font_texture->SetContents( - pixels, texture_descriptor.GetSizeOfBaseMipLevel()); + pixels, texture_descriptor.GetByteSizeOfBaseMipLevel()); IM_ASSERT(uploaded && "Could not upload ImGui font texture to device memory."); } diff --git a/impeller/renderer/backend/metal/device_buffer_mtl.mm b/impeller/renderer/backend/metal/device_buffer_mtl.mm index 3f47629aa27f4..58e74fefd8ffe 100644 --- a/impeller/renderer/backend/metal/device_buffer_mtl.mm +++ b/impeller/renderer/backend/metal/device_buffer_mtl.mm @@ -29,7 +29,7 @@ } // Avoid overruns. - if (offset + desc.GetSizeOfBaseMipLevel() > size_) { + if (offset + desc.GetByteSizeOfBaseMipLevel() > size_) { VALIDATION_LOG << "Avoiding buffer overrun when creating texture."; return nullptr; } diff --git a/impeller/renderer/backend/metal/texture_mtl.mm b/impeller/renderer/backend/metal/texture_mtl.mm index ec6040f8ad73a..a2cfab40809f9 100644 --- a/impeller/renderer/backend/metal/texture_mtl.mm +++ b/impeller/renderer/backend/metal/texture_mtl.mm @@ -38,7 +38,7 @@ const auto& desc = GetTextureDescriptor(); // Out of bounds access. - if (length != desc.GetSizeOfBaseMipLevel()) { + if (length != desc.GetByteSizeOfBaseMipLevel()) { return false; } diff --git a/impeller/renderer/texture_descriptor.h b/impeller/renderer/texture_descriptor.h index 20b9a367dddc3..38bf7db7e3260 100644 --- a/impeller/renderer/texture_descriptor.h +++ b/impeller/renderer/texture_descriptor.h @@ -25,7 +25,7 @@ struct TextureDescriptor { static_cast(TextureUsage::kShaderRead); SampleCount sample_count = SampleCount::kCount1; - constexpr size_t GetSizeOfBaseMipLevel() const { + constexpr size_t GetByteSizeOfBaseMipLevel() const { if (!IsValid()) { return 0u; } diff --git a/impeller/typographer/backends/skia/text_render_context_skia.cc b/impeller/typographer/backends/skia/text_render_context_skia.cc index c93906d501ba7..d5b82a6124dab 100644 --- a/impeller/typographer/backends/skia/text_render_context_skia.cc +++ b/impeller/typographer/backends/skia/text_render_context_skia.cc @@ -166,7 +166,7 @@ static std::shared_ptr UploadGlyphTextureAtlas( texture_descriptor.size = ISize::MakeWH(atlas_size, atlas_size); if (pixmap.rowBytes() * pixmap.height() != - texture_descriptor.GetSizeOfBaseMipLevel()) { + texture_descriptor.GetByteSizeOfBaseMipLevel()) { return nullptr; } From 6b6b3a4c08566262f00f06c0e05614783bd331ab Mon Sep 17 00:00:00 2001 From: Brandon DeRosier Date: Wed, 30 Mar 2022 12:47:33 -0700 Subject: [PATCH 377/433] Make filter inputs lazy and sharable (#103) --- impeller/entity/BUILD.gn | 4 + impeller/entity/contents/content_context.cc | 42 ++++++ impeller/entity/contents/content_context.h | 8 + impeller/entity/contents/contents.cc | 51 +------ impeller/entity/contents/contents.h | 16 +- .../contents/filters/blend_filter_contents.cc | 78 +++++----- .../contents/filters/blend_filter_contents.h | 12 +- .../contents/filters/filter_contents.cc | 139 ++++-------------- .../entity/contents/filters/filter_contents.h | 31 ++-- .../entity/contents/filters/filter_input.cc | 77 ++++++++++ .../entity/contents/filters/filter_input.h | 60 ++++++++ .../filters/gaussian_blur_filter_contents.cc | 31 ++-- .../filters/gaussian_blur_filter_contents.h | 6 +- impeller/entity/contents/snapshot.cc | 40 +++++ impeller/entity/contents/snapshot.h | 36 +++++ impeller/entity/entity.cc | 10 ++ impeller/entity/entity.h | 2 + impeller/entity/entity_unittests.cc | 65 +++++--- impeller/entity/shaders/gaussian_blur.frag | 2 - 19 files changed, 447 insertions(+), 263 deletions(-) create mode 100644 impeller/entity/contents/filters/filter_input.cc create mode 100644 impeller/entity/contents/filters/filter_input.h create mode 100644 impeller/entity/contents/snapshot.cc create mode 100644 impeller/entity/contents/snapshot.h diff --git a/impeller/entity/BUILD.gn b/impeller/entity/BUILD.gn index e04288155e803..d5062f3038971 100644 --- a/impeller/entity/BUILD.gn +++ b/impeller/entity/BUILD.gn @@ -41,10 +41,14 @@ impeller_component("entity") { "contents/filters/blend_filter_contents.h", "contents/filters/filter_contents.cc", "contents/filters/filter_contents.h", + "contents/filters/filter_input.cc", + "contents/filters/filter_input.h", "contents/filters/gaussian_blur_filter_contents.cc", "contents/filters/gaussian_blur_filter_contents.h", "contents/linear_gradient_contents.cc", "contents/linear_gradient_contents.h", + "contents/snapshot.cc", + "contents/snapshot.h", "contents/solid_color_contents.cc", "contents/solid_color_contents.h", "contents/solid_stroke_contents.cc", diff --git a/impeller/entity/contents/content_context.cc b/impeller/entity/contents/content_context.cc index 36dc90ca0bb0f..016e0b3d57674 100644 --- a/impeller/entity/contents/content_context.cc +++ b/impeller/entity/contents/content_context.cc @@ -6,6 +6,10 @@ #include +#include "impeller/renderer/command_buffer.h" +#include "impeller/renderer/render_pass.h" +#include "impeller/renderer/render_target.h" + namespace impeller { ContentContext::ContentContext(std::shared_ptr context) @@ -60,6 +64,44 @@ bool ContentContext::IsValid() const { return is_valid_; } +std::shared_ptr ContentContext::MakeSubpass( + ISize texture_size, + SubpassCallback subpass_callback) const { + auto context = GetContext(); + + auto subpass_target = RenderTarget::CreateOffscreen(*context, texture_size); + auto subpass_texture = subpass_target.GetRenderTargetTexture(); + if (!subpass_texture) { + return nullptr; + } + + auto sub_command_buffer = context->CreateRenderCommandBuffer(); + sub_command_buffer->SetLabel("Offscreen Contents Command Buffer"); + if (!sub_command_buffer) { + return nullptr; + } + + auto sub_renderpass = sub_command_buffer->CreateRenderPass(subpass_target); + if (!sub_renderpass) { + return nullptr; + } + sub_renderpass->SetLabel("OffscreenContentsPass"); + + if (!subpass_callback(*this, *sub_renderpass)) { + return nullptr; + } + + if (!sub_renderpass->EncodeCommands(*context->GetTransientsAllocator())) { + return nullptr; + } + + if (!sub_command_buffer->SubmitCommands()) { + return nullptr; + } + + return subpass_texture; +} + std::shared_ptr ContentContext::GetContext() const { return context_; } diff --git a/impeller/entity/contents/content_context.h b/impeller/entity/contents/content_context.h index fc406d4f3dc1d..b1194cc9cd062 100644 --- a/impeller/entity/contents/content_context.h +++ b/impeller/entity/contents/content_context.h @@ -130,6 +130,14 @@ class ContentContext { std::shared_ptr GetContext() const; + using SubpassCallback = + std::function; + + /// @brief Creates a new texture of size `texture_size` and calls + /// `subpass_callback` with a `RenderPass` for drawing to the texture. + std::shared_ptr MakeSubpass(ISize texture_size, + SubpassCallback subpass_callback) const; + private: std::shared_ptr context_; diff --git a/impeller/entity/contents/contents.cc b/impeller/entity/contents/contents.cc index 4f0ba354c4ca7..83fa02514ca84 100644 --- a/impeller/entity/contents/contents.cc +++ b/impeller/entity/contents/contents.cc @@ -38,13 +38,13 @@ Rect Contents::GetBounds(const Entity& entity) const { return Rect::MakePointBounds({points.begin(), points.end()}); } -std::optional Contents::RenderToTexture( +std::optional Contents::RenderToTexture( const ContentContext& renderer, const Entity& entity) const { auto bounds = GetBounds(entity); - auto texture = MakeSubpass( - renderer, ISize(bounds.size), + auto texture = renderer.MakeSubpass( + ISize::Ceil(bounds.size), [&contents = *this, &entity, &bounds](const ContentContext& renderer, RenderPass& pass) -> bool { Entity sub_entity; @@ -56,52 +56,11 @@ std::optional Contents::RenderToTexture( return contents.Render(renderer, sub_entity, pass); }); - if (!texture.has_value()) { + if (!texture) { return std::nullopt; } - return Snapshot{.texture = texture.value(), .position = bounds.origin}; -} - -using SubpassCallback = std::function; - -std::optional> Contents::MakeSubpass( - const ContentContext& renderer, - ISize texture_size, - SubpassCallback subpass_callback) { - auto context = renderer.GetContext(); - - auto subpass_target = RenderTarget::CreateOffscreen(*context, texture_size); - auto subpass_texture = subpass_target.GetRenderTargetTexture(); - if (!subpass_texture) { - return std::nullopt; - } - - auto sub_command_buffer = context->CreateRenderCommandBuffer(); - sub_command_buffer->SetLabel("Offscreen Contents Command Buffer"); - if (!sub_command_buffer) { - return std::nullopt; - } - - auto sub_renderpass = sub_command_buffer->CreateRenderPass(subpass_target); - if (!sub_renderpass) { - return std::nullopt; - } - sub_renderpass->SetLabel("OffscreenContentsPass"); - - if (!subpass_callback(renderer, *sub_renderpass)) { - return std::nullopt; - } - - if (!sub_renderpass->EncodeCommands(*context->GetTransientsAllocator())) { - return std::nullopt; - } - - if (!sub_command_buffer->SubmitCommands()) { - return std::nullopt; - } - - return subpass_texture; + return Snapshot{.texture = texture, .position = bounds.origin}; } } // namespace impeller diff --git a/impeller/entity/contents/contents.h b/impeller/entity/contents/contents.h index 915b89c34c308..ee9b0b0535fb5 100644 --- a/impeller/entity/contents/contents.h +++ b/impeller/entity/contents/contents.h @@ -9,6 +9,7 @@ #include #include "flutter/fml/macros.h" +#include "impeller/entity/contents/snapshot.h" #include "impeller/geometry/rect.h" #include "impeller/renderer/texture.h" @@ -27,14 +28,6 @@ ContentContextOptions OptionsFromPassAndEntity(const RenderPass& pass, class Contents { public: - /// Represents a screen space texture and it's intended draw position. - struct Snapshot { - std::shared_ptr texture; - /// The offset from the origin where this texture is intended to be - /// rendered. - Vector2 position; - }; - Contents(); virtual ~Contents(); @@ -54,13 +47,6 @@ class Contents { const ContentContext& renderer, const Entity& entity) const; - using SubpassCallback = - std::function; - static std::optional> MakeSubpass( - const ContentContext& renderer, - ISize texture_size, - SubpassCallback subpass_callback); - protected: private: diff --git a/impeller/entity/contents/filters/blend_filter_contents.cc b/impeller/entity/contents/filters/blend_filter_contents.cc index 81a048e5a1f35..293065e679002 100644 --- a/impeller/entity/contents/filters/blend_filter_contents.cc +++ b/impeller/entity/contents/filters/blend_filter_contents.cc @@ -5,6 +5,7 @@ #include "impeller/entity/contents/filters/blend_filter_contents.h" #include "impeller/entity/contents/content_context.h" +#include "impeller/entity/contents/filters/filter_input.h" #include "impeller/renderer/render_pass.h" #include "impeller/renderer/sampler_library.h" @@ -20,11 +21,13 @@ using PipelineProc = std::shared_ptr (ContentContext::*)(ContentContextOptions) const; template -static bool AdvancedBlend(const std::vector& input_textures, +static bool AdvancedBlend(const FilterInput::Vector& inputs, const ContentContext& renderer, + const Entity& entity, RenderPass& pass, + const Rect& bounds, PipelineProc pipeline_proc) { - if (input_textures.size() < 2) { + if (inputs.size() < 2) { return false; } @@ -56,19 +59,21 @@ static bool AdvancedBlend(const std::vector& input_textures, typename VS::FrameInfo frame_info; frame_info.mvp = Matrix::MakeOrthographic(size); - auto dst_snapshot = input_textures[1]; - FS::BindTextureSamplerSrc(cmd, dst_snapshot.texture, sampler); + auto dst_snapshot = inputs[1]->GetSnapshot(renderer, entity); + FS::BindTextureSamplerSrc(cmd, dst_snapshot->texture, sampler); frame_info.dst_uv_transform = - Matrix::MakeTranslation(-dst_snapshot.position / size) * + Matrix::MakeTranslation(-(dst_snapshot->position - bounds.origin) / + size) * Matrix::MakeScale( - Vector3(Size(size) / Size(dst_snapshot.texture->GetSize()))); + Vector3(Size(size) / Size(dst_snapshot->texture->GetSize()))); - auto src_snapshot = input_textures[0]; - FS::BindTextureSamplerDst(cmd, src_snapshot.texture, sampler); + auto src_snapshot = inputs[0]->GetSnapshot(renderer, entity); + FS::BindTextureSamplerDst(cmd, src_snapshot->texture, sampler); frame_info.src_uv_transform = - Matrix::MakeTranslation(-src_snapshot.position / size) * + Matrix::MakeTranslation(-(src_snapshot->position - bounds.origin) / + size) * Matrix::MakeScale( - Vector3(Size(size) / Size(src_snapshot.texture->GetSize()))); + Vector3(Size(size) / Size(src_snapshot->texture->GetSize()))); auto uniform_view = host_buffer.EmplaceUniform(frame_info); VS::BindFrameInfo(cmd, uniform_view); @@ -91,13 +96,14 @@ void BlendFilterContents::SetBlendMode(Entity::BlendMode blend_mode) { switch (blend_mode) { case Entity::BlendMode::kScreen: - advanced_blend_proc_ = [](const std::vector& input_textures, + advanced_blend_proc_ = [](const FilterInput::Vector& inputs, const ContentContext& renderer, - RenderPass& pass) { + const Entity& entity, RenderPass& pass, + const Rect& bounds) { PipelineProc p = &ContentContext::GetTextureBlendScreenPipeline; return AdvancedBlend( - input_textures, renderer, pass, p); + inputs, renderer, entity, pass, bounds, p); }; break; default: @@ -106,9 +112,11 @@ void BlendFilterContents::SetBlendMode(Entity::BlendMode blend_mode) { } } -static bool BasicBlend(const std::vector& input_textures, +static bool BasicBlend(const FilterInput::Vector& inputs, const ContentContext& renderer, + const Entity& entity, RenderPass& pass, + const Rect& bounds, Entity::BlendMode basic_blend) { using VS = TextureBlendPipeline::VertexShader; using FS = TextureBlendPipeline::FragmentShader; @@ -138,21 +146,21 @@ static bool BasicBlend(const std::vector& input_textures, options.blend_mode = Entity::BlendMode::kSource; cmd.pipeline = renderer.GetTextureBlendPipeline(options); { - auto input = input_textures[0]; - FS::BindTextureSamplerSrc(cmd, input.texture, sampler); + auto input = inputs[0]->GetSnapshot(renderer, entity); + FS::BindTextureSamplerSrc(cmd, input->texture, sampler); VS::FrameInfo frame_info; frame_info.mvp = Matrix::MakeOrthographic(size) * - Matrix::MakeTranslation(input.position) * - Matrix::MakeScale(Size(input.texture->GetSize()) / Size(size)); + Matrix::MakeTranslation(input->position - bounds.origin) * + Matrix::MakeScale(Size(input->texture->GetSize()) / Size(size)); auto uniform_view = host_buffer.EmplaceUniform(frame_info); VS::BindFrameInfo(cmd, uniform_view); } pass.AddCommand(cmd); - if (input_textures.size() < 2) { + if (inputs.size() < 2) { return true; } @@ -161,16 +169,16 @@ static bool BasicBlend(const std::vector& input_textures, options.blend_mode = basic_blend; cmd.pipeline = renderer.GetTextureBlendPipeline(options); - for (auto texture_i = input_textures.begin() + 1; - texture_i < input_textures.end(); texture_i++) { - auto input = *texture_i; - FS::BindTextureSamplerSrc(cmd, input.texture, sampler); + for (auto texture_i = inputs.begin() + 1; texture_i < inputs.end(); + texture_i++) { + auto input = texture_i->get()->GetSnapshot(renderer, entity); + FS::BindTextureSamplerSrc(cmd, input->texture, sampler); VS::FrameInfo frame_info; frame_info.mvp = frame_info.mvp = Matrix::MakeOrthographic(size) * - Matrix::MakeTranslation(input.position) * - Matrix::MakeScale(Size(input.texture->GetSize()) / Size(size)); + Matrix::MakeTranslation(input->position - bounds.origin) * + Matrix::MakeScale(Size(input->texture->GetSize()) / Size(size)); auto uniform_view = host_buffer.EmplaceUniform(frame_info); VS::BindFrameInfo(cmd, uniform_view); @@ -180,27 +188,27 @@ static bool BasicBlend(const std::vector& input_textures, return true; } -bool BlendFilterContents::RenderFilter( - const std::vector& input_textures, - const ContentContext& renderer, - RenderPass& pass, - const Matrix& transform) const { - if (input_textures.empty()) { +bool BlendFilterContents::RenderFilter(const FilterInput::Vector& inputs, + const ContentContext& renderer, + const Entity& entity, + RenderPass& pass, + const Rect& bounds) const { + if (inputs.empty()) { return true; } - if (input_textures.size() == 1) { + if (inputs.size() == 1) { // Nothing to blend. - return BasicBlend(input_textures, renderer, pass, + return BasicBlend(inputs, renderer, entity, pass, bounds, Entity::BlendMode::kSource); } if (blend_mode_ <= Entity::BlendMode::kLastPipelineBlendMode) { - return BasicBlend(input_textures, renderer, pass, blend_mode_); + return BasicBlend(inputs, renderer, entity, pass, bounds, blend_mode_); } if (blend_mode_ <= Entity::BlendMode::kLastAdvancedBlendMode) { - return advanced_blend_proc_(input_textures, renderer, pass); + return advanced_blend_proc_(inputs, renderer, entity, pass, bounds); } FML_UNREACHABLE(); diff --git a/impeller/entity/contents/filters/blend_filter_contents.h b/impeller/entity/contents/filters/blend_filter_contents.h index 2346037f7b282..6baf5ec5be0de 100644 --- a/impeller/entity/contents/filters/blend_filter_contents.h +++ b/impeller/entity/contents/filters/blend_filter_contents.h @@ -5,15 +5,18 @@ #pragma once #include "impeller/entity/contents/filters/filter_contents.h" +#include "impeller/entity/contents/filters/filter_input.h" namespace impeller { class BlendFilterContents : public FilterContents { public: using AdvancedBlendProc = - std::function& input_textures, + std::function; + const Entity& entity, + RenderPass& pass, + const Rect& bounds)>; BlendFilterContents(); @@ -23,10 +26,11 @@ class BlendFilterContents : public FilterContents { private: // |FilterContents| - bool RenderFilter(const std::vector& input_textures, + bool RenderFilter(const FilterInput::Vector& inputs, const ContentContext& renderer, + const Entity& entity, RenderPass& pass, - const Matrix& transform) const override; + const Rect& bounds) const override; Entity::BlendMode blend_mode_; AdvancedBlendProc advanced_blend_proc_; diff --git a/impeller/entity/contents/filters/filter_contents.cc b/impeller/entity/contents/filters/filter_contents.cc index e270857d81f6c..a038b0e8d6316 100644 --- a/impeller/entity/contents/filters/filter_contents.cc +++ b/impeller/entity/contents/filters/filter_contents.cc @@ -15,6 +15,7 @@ #include "impeller/base/validation.h" #include "impeller/entity/contents/content_context.h" #include "impeller/entity/contents/filters/blend_filter_contents.h" +#include "impeller/entity/contents/filters/filter_input.h" #include "impeller/entity/contents/filters/gaussian_blur_filter_contents.h" #include "impeller/entity/contents/texture_contents.h" #include "impeller/entity/entity.h" @@ -27,61 +28,62 @@ namespace impeller { std::shared_ptr FilterContents::MakeBlend( Entity::BlendMode blend_mode, - InputTextures input_textures) { + FilterInput::Vector inputs) { if (blend_mode > Entity::BlendMode::kLastAdvancedBlendMode) { VALIDATION_LOG << "Invalid blend mode " << static_cast(blend_mode) << " passed to FilterContents::MakeBlend."; return nullptr; } - if (input_textures.size() < 2 || + if (inputs.size() < 2 || blend_mode <= Entity::BlendMode::kLastPipelineBlendMode) { auto blend = std::make_shared(); - blend->SetInputTextures(input_textures); + blend->SetInputs(inputs); blend->SetBlendMode(blend_mode); return blend; } if (blend_mode <= Entity::BlendMode::kLastAdvancedBlendMode) { - InputVariant blend = input_textures[0]; - for (auto in_i = input_textures.begin() + 1; in_i < input_textures.end(); - in_i++) { - auto new_blend = std::make_shared(); - new_blend->SetInputTextures({blend, *in_i}); + auto blend_input = inputs[0]; + std::shared_ptr new_blend; + for (auto in_i = inputs.begin() + 1; in_i < inputs.end(); in_i++) { + new_blend = std::make_shared(); + new_blend->SetInputs({blend_input, *in_i}); new_blend->SetBlendMode(blend_mode); - blend = new_blend; + blend_input = FilterInput::Make(new_blend); } - auto contents = std::get>(blend); - // This downcast is safe because we know blend is a BlendFilterContents. - return std::static_pointer_cast(contents); + // new_blend will always be assigned because inputs.size() >= 2. + return new_blend; } FML_UNREACHABLE(); } std::shared_ptr FilterContents::MakeDirectionalGaussianBlur( - InputVariant input_texture, + FilterInput::Ref input, Vector2 blur_vector) { auto blur = std::make_shared(); - blur->SetInputTextures({input_texture}); + blur->SetInputs({input}); blur->SetBlurVector(blur_vector); return blur; } std::shared_ptr FilterContents::MakeGaussianBlur( - InputVariant input_texture, + FilterInput::Ref input, Scalar sigma_x, Scalar sigma_y) { - auto x_blur = MakeDirectionalGaussianBlur(input_texture, Point(sigma_x, 0)); - return MakeDirectionalGaussianBlur(x_blur, Point(0, sigma_y)); + auto x_blur = MakeDirectionalGaussianBlur(input, Point(sigma_x, 0)); + auto y_blur = + MakeDirectionalGaussianBlur(FilterInput::Make(x_blur), Point(0, sigma_y)); + return y_blur; } FilterContents::FilterContents() = default; FilterContents::~FilterContents() = default; -void FilterContents::SetInputTextures(InputTextures input_textures) { - input_textures_ = std::move(input_textures); +void FilterContents::SetInputs(FilterInput::Vector inputs) { + inputs_ = std::move(inputs); } bool FilterContents::Render(const ContentContext& renderer, @@ -108,81 +110,24 @@ bool FilterContents::Render(const ContentContext& renderer, return contents->Render(renderer, e, pass); } -Rect FilterContents::GetBoundsForInput(const Entity& entity, - const InputVariant& input) { - if (auto contents = std::get_if>(&input)) { - return contents->get()->GetBounds(entity); - } - - if (auto texture = std::get_if>(&input)) { - auto points = entity.GetPath().GetBoundingBox()->GetPoints(); - - const auto& transform = entity.GetTransformation(); - for (uint i = 0; i < points.size(); i++) { - points[i] = transform * points[i]; - } - return Rect::MakePointBounds({points.begin(), points.end()}); - } - - FML_UNREACHABLE(); -} - Rect FilterContents::GetBounds(const Entity& entity) const { // The default bounds of FilterContents is just the union of its inputs. // FilterContents implementations may choose to increase the bounds in any // direction, but it should never - if (input_textures_.empty()) { + if (inputs_.empty()) { return Rect(); } - Rect result = GetBoundsForInput(entity, input_textures_.front()); - for (auto input_i = input_textures_.begin() + 1; - input_i < input_textures_.end(); input_i++) { - result.Union(GetBoundsForInput(entity, *input_i)); + Rect result = inputs_.front()->GetBounds(entity); + for (auto input_i = inputs_.begin() + 1; input_i < inputs_.end(); input_i++) { + result.Union(input_i->get()->GetBounds(entity)); } return result; } -static std::optional ResolveSnapshotForInput( - const ContentContext& renderer, - const Entity& entity, - FilterContents::InputVariant input) { - if (auto contents = std::get_if>(&input)) { - return contents->get()->RenderToTexture(renderer, entity); - } - - if (auto input_texture = std::get_if>(&input)) { - auto input_bounds = FilterContents::GetBoundsForInput(entity, input); - // If the input is a texture, render the version of it which is transformed. - auto texture = Contents::MakeSubpass( - renderer, ISize(input_bounds.size), - [texture = *input_texture, entity, input_bounds]( - const ContentContext& renderer, RenderPass& pass) -> bool { - TextureContents contents; - contents.SetTexture(texture); - contents.SetSourceRect(Rect::MakeSize(Size(texture->GetSize()))); - Entity sub_entity; - sub_entity.SetPath(entity.GetPath()); - sub_entity.SetBlendMode(Entity::BlendMode::kSource); - sub_entity.SetTransformation( - Matrix::MakeTranslation(Vector3(-input_bounds.origin)) * - entity.GetTransformation()); - return contents.Render(renderer, sub_entity, pass); - }); - if (!texture.has_value()) { - return std::nullopt; - } - - return Contents::Snapshot{.texture = texture.value(), - .position = input_bounds.origin}; - } - - FML_UNREACHABLE(); -} - -std::optional FilterContents::RenderToTexture( +std::optional FilterContents::RenderToTexture( const ContentContext& renderer, const Entity& entity) const { auto bounds = GetBounds(entity); @@ -190,38 +135,18 @@ std::optional FilterContents::RenderToTexture( return std::nullopt; } - // Resolve all inputs as textures. - - std::vector input_textures; - - input_textures.reserve(input_textures_.size()); - for (const auto& input : input_textures_) { - auto texture_and_offset = ResolveSnapshotForInput(renderer, entity, input); - if (!texture_and_offset.has_value()) { - continue; - } - - // Make the position of all input snapshots relative to this filter's - // snapshot position. - texture_and_offset->position -= bounds.origin; - - input_textures.push_back(texture_and_offset.value()); - } - - // Create a new texture and render the filter to it. - - auto texture = MakeSubpass( - renderer, ISize(GetBounds(entity).size), + // Render the filter into a new texture. + auto texture = renderer.MakeSubpass( + ISize(GetBounds(entity).size), [=](const ContentContext& renderer, RenderPass& pass) -> bool { - return RenderFilter(input_textures, renderer, pass, - entity.GetTransformation()); + return RenderFilter(inputs_, renderer, entity, pass, bounds); }); - if (!texture.has_value()) { + if (!texture) { return std::nullopt; } - return Snapshot{.texture = texture.value(), .position = bounds.origin}; + return Snapshot{.texture = texture, .position = bounds.origin}; } } // namespace impeller diff --git a/impeller/entity/contents/filters/filter_contents.h b/impeller/entity/contents/filters/filter_contents.h index fda5b8a7afd78..2088163277735 100644 --- a/impeller/entity/contents/filters/filter_contents.h +++ b/impeller/entity/contents/filters/filter_contents.h @@ -8,6 +8,7 @@ #include #include +#include "impeller/entity/contents/filters/filter_input.h" #include "impeller/entity/entity.h" #include "impeller/renderer/formats.h" @@ -17,35 +18,26 @@ class Pipeline; class FilterContents : public Contents { public: - using InputVariant = - std::variant, std::shared_ptr>; - using InputTextures = std::vector; - - static std::shared_ptr MakeBlend( - Entity::BlendMode blend_mode, - InputTextures input_textures); + static std::shared_ptr MakeBlend(Entity::BlendMode blend_mode, + FilterInput::Vector inputs); static std::shared_ptr MakeDirectionalGaussianBlur( - InputVariant input_texture, + FilterInput::Ref input, Vector2 blur_vector); static std::shared_ptr - MakeGaussianBlur(InputVariant input_texture, Scalar sigma_x, Scalar sigma_y); - - static Rect GetBoundsForInput(const Entity& entity, - const InputVariant& input); + MakeGaussianBlur(FilterInput::Ref input, Scalar sigma_x, Scalar sigma_y); FilterContents(); ~FilterContents() override; - /// @brief The input texture sources for this filter. All texture sources are - /// expected to have or produce premultiplied alpha colors. - /// Any input can either be a `Texture` or another `FilterContents`. + /// @brief The input texture sources for this filter. Each input's emitted + /// texture is expected to have premultiplied alpha colors. /// /// The number of required or optional textures depends on the /// particular filter's implementation. - void SetInputTextures(InputTextures input_textures); + void SetInputs(FilterInput::Vector inputs); // |Contents| bool Render(const ContentContext& renderer, @@ -63,12 +55,13 @@ class FilterContents : public Contents { private: /// @brief Takes a set of zero or more input textures and writes to an output /// texture. - virtual bool RenderFilter(const std::vector& input_textures, + virtual bool RenderFilter(const FilterInput::Vector& inputs, const ContentContext& renderer, + const Entity& entity, RenderPass& pass, - const Matrix& transform) const = 0; + const Rect& bounds) const = 0; - InputTextures input_textures_; + FilterInput::Vector inputs_; Rect destination_; FML_DISALLOW_COPY_AND_ASSIGN(FilterContents); diff --git a/impeller/entity/contents/filters/filter_input.cc b/impeller/entity/contents/filters/filter_input.cc new file mode 100644 index 0000000000000..eaf75a982ab6a --- /dev/null +++ b/impeller/entity/contents/filters/filter_input.cc @@ -0,0 +1,77 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "impeller/entity/contents/filters/filter_input.h" + +#include +#include +#include + +#include "impeller/entity/contents/snapshot.h" +#include "impeller/entity/entity.h" + +namespace impeller { + +FilterInput::Ref FilterInput::Make(Variant input) { + return std::shared_ptr(new FilterInput(input)); +} + +FilterInput::Vector FilterInput::Make(std::initializer_list inputs) { + FilterInput::Vector result; + result.reserve(inputs.size()); + for (const auto& input : inputs) { + result.push_back(Make(input)); + } + return result; +} + +FilterInput::Variant FilterInput::GetInput() const { + return input_; +} + +Rect FilterInput::GetBounds(const Entity& entity) const { + if (snapshot_) { + return Rect(snapshot_->position, Size(snapshot_->texture->GetSize())); + } + + if (auto contents = std::get_if>(&input_)) { + return contents->get()->GetBounds(entity); + } + + if (auto texture = std::get_if>(&input_)) { + return entity.GetTransformedPathBounds(); + } + + FML_UNREACHABLE(); +} + +std::optional FilterInput::GetSnapshot(const ContentContext& renderer, + const Entity& entity) const { + if (snapshot_) { + return snapshot_; + } + snapshot_ = RenderToTexture(renderer, entity); + + return snapshot_; +} + +FilterInput::FilterInput(Variant input) : input_(input) {} + +FilterInput::~FilterInput() = default; + +std::optional FilterInput::RenderToTexture( + const ContentContext& renderer, + const Entity& entity) const { + if (auto contents = std::get_if>(&input_)) { + return contents->get()->RenderToTexture(renderer, entity); + } + + if (auto texture = std::get_if>(&input_)) { + return Snapshot::FromTransformedTexture(renderer, entity, *texture); + } + + FML_UNREACHABLE(); +} + +} // namespace impeller diff --git a/impeller/entity/contents/filters/filter_input.h b/impeller/entity/contents/filters/filter_input.h new file mode 100644 index 0000000000000..b96e7203cc112 --- /dev/null +++ b/impeller/entity/contents/filters/filter_input.h @@ -0,0 +1,60 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#pragma once + +#include +#include +#include +#include + +#include "impeller/entity/contents/contents.h" +#include "impeller/geometry/rect.h" +#include "impeller/renderer/formats.h" + +namespace impeller { + +class ContentContext; +class Entity; + +/// `FilterInput` is a lazy/single eval `Snapshot` which may be shared across +/// filter parameters and used to evaluate input bounds. +/// +/// A `FilterInput` can be created from either a `Texture` or any `Contents` +/// class (including `FilterContents`), and can be re-used for any filter inputs +/// across an entity's filter graph without repeating subpasses unnecessarily. +/// +/// Filters may decide to not evaluate inputs in situations where they won't +/// contribute to the filter's output texture. +class FilterInput final { + public: + using Ref = std::shared_ptr; + using Vector = std::vector; + using Variant = + std::variant, std::shared_ptr>; + + ~FilterInput(); + + static FilterInput::Ref Make(Variant input); + + static FilterInput::Vector Make(std::initializer_list inputs); + + Variant GetInput() const; + + Rect GetBounds(const Entity& entity) const; + + std::optional GetSnapshot(const ContentContext& renderer, + const Entity& entity) const; + + private: + FilterInput(Variant input); + + std::optional RenderToTexture(const ContentContext& renderer, + const Entity& entity) const; + + Variant input_; + mutable std::optional snapshot_; +}; + +} // namespace impeller diff --git a/impeller/entity/contents/filters/gaussian_blur_filter_contents.cc b/impeller/entity/contents/filters/gaussian_blur_filter_contents.cc index f83b580430179..3f7f572ffa662 100644 --- a/impeller/entity/contents/filters/gaussian_blur_filter_contents.cc +++ b/impeller/entity/contents/filters/gaussian_blur_filter_contents.cc @@ -6,6 +6,7 @@ #include +#include "impeller/base/validation.h" #include "impeller/entity/contents/content_context.h" #include "impeller/entity/contents/filters/filter_contents.h" #include "impeller/geometry/rect.h" @@ -30,11 +31,12 @@ void DirectionalGaussianBlurFilterContents::SetBlurVector(Vector2 blur_vector) { } bool DirectionalGaussianBlurFilterContents::RenderFilter( - const std::vector& input_textures, + const FilterInput::Vector& inputs, const ContentContext& renderer, + const Entity& entity, RenderPass& pass, - const Matrix& transform) const { - if (input_textures.empty()) { + const Rect& bounds) const { + if (inputs.empty()) { return true; } @@ -43,16 +45,19 @@ bool DirectionalGaussianBlurFilterContents::RenderFilter( auto& host_buffer = pass.GetTransientsBuffer(); - // Because this filter is intended to be used with only one input parameter, + // Because this filter is intended to be used with only one input parameter, // and GetBounds just increases the input size by a factor of the direction, // we we can just scale up the UVs by the same amount and don't need to worry - // about mapping the UVs to destination rect (like we do in + // about mapping the UVs to the destination rect (like we do in // BlendFilterContents). - auto size = pass.GetRenderTargetSize(); - auto transformed_blur = transform.TransformDirection(blur_vector_); - auto uv_offset = transformed_blur.Abs() / size; + auto input = inputs[0]->GetSnapshot(renderer, entity); + auto input_size = input->texture->GetSize(); + + auto transformed_blur = + entity.GetTransformation().TransformDirection(blur_vector_); + auto uv_offset = transformed_blur.Abs() / input_size; // LTRB Scalar uv[4] = { -uv_offset.x, @@ -62,6 +67,7 @@ bool DirectionalGaussianBlurFilterContents::RenderFilter( }; VertexBufferBuilder vtx_builder; + auto size = pass.GetRenderTargetSize(); vtx_builder.AddVertices({ {Point(0, 0), Point(uv[0], uv[1])}, {Point(size.width, 0), Point(uv[2], uv[1])}, @@ -73,7 +79,7 @@ bool DirectionalGaussianBlurFilterContents::RenderFilter( auto vtx_buffer = vtx_builder.CreateVertexBuffer(host_buffer); VS::FrameInfo frame_info; - frame_info.texture_size = Point(size); + frame_info.texture_size = Point(input_size); frame_info.blur_radius = transformed_blur.GetLength(); frame_info.blur_direction = transformed_blur.Normalize(); @@ -86,16 +92,13 @@ bool DirectionalGaussianBlurFilterContents::RenderFilter( cmd.pipeline = renderer.GetGaussianBlurPipeline(options); cmd.BindVertices(vtx_buffer); - const auto& [texture, _] = input_textures[0]; - FS::BindTextureSampler(cmd, texture, sampler); + FS::BindTextureSampler(cmd, input->texture, sampler); frame_info.mvp = Matrix::MakeOrthographic(size); auto uniform_view = host_buffer.EmplaceUniform(frame_info); VS::BindFrameInfo(cmd, uniform_view); - pass.AddCommand(cmd); - - return true; + return pass.AddCommand(cmd); } Rect DirectionalGaussianBlurFilterContents::GetBounds( diff --git a/impeller/entity/contents/filters/gaussian_blur_filter_contents.h b/impeller/entity/contents/filters/gaussian_blur_filter_contents.h index ad2e428457416..17aa92af792fe 100644 --- a/impeller/entity/contents/filters/gaussian_blur_filter_contents.h +++ b/impeller/entity/contents/filters/gaussian_blur_filter_contents.h @@ -5,6 +5,7 @@ #pragma once #include "impeller/entity/contents/filters/filter_contents.h" +#include "impeller/entity/contents/filters/filter_input.h" #include "impeller/geometry/matrix.h" namespace impeller { @@ -22,10 +23,11 @@ class DirectionalGaussianBlurFilterContents final : public FilterContents { private: // |FilterContents| - bool RenderFilter(const std::vector& input_textures, + bool RenderFilter(const FilterInput::Vector& input_textures, const ContentContext& renderer, + const Entity& entity, RenderPass& pass, - const Matrix& transform) const override; + const Rect& bounds) const override; Vector2 blur_vector_; diff --git a/impeller/entity/contents/snapshot.cc b/impeller/entity/contents/snapshot.cc new file mode 100644 index 0000000000000..32dd34b5ef50b --- /dev/null +++ b/impeller/entity/contents/snapshot.cc @@ -0,0 +1,40 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "impeller/entity/contents/snapshot.h" + +#include "impeller/entity/contents/content_context.h" +#include "impeller/entity/contents/texture_contents.h" + +namespace impeller { + +std::optional Snapshot::FromTransformedTexture( + const ContentContext& renderer, + const Entity& entity, + std::shared_ptr texture) { + Rect bounds = entity.GetTransformedPathBounds(); + + auto result = renderer.MakeSubpass( + ISize(bounds.size), + [&texture, &entity, bounds](const ContentContext& renderer, + RenderPass& pass) -> bool { + TextureContents contents; + contents.SetTexture(texture); + contents.SetSourceRect(Rect::MakeSize(Size(texture->GetSize()))); + Entity sub_entity; + sub_entity.SetPath(entity.GetPath()); + sub_entity.SetBlendMode(Entity::BlendMode::kSource); + sub_entity.SetTransformation( + Matrix::MakeTranslation(Vector3(-bounds.origin)) * + entity.GetTransformation()); + return contents.Render(renderer, sub_entity, pass); + }); + if (!result) { + return std::nullopt; + } + + return Snapshot{.texture = result, .position = bounds.origin}; +} + +} // namespace impeller diff --git a/impeller/entity/contents/snapshot.h b/impeller/entity/contents/snapshot.h new file mode 100644 index 0000000000000..4169758ee1f94 --- /dev/null +++ b/impeller/entity/contents/snapshot.h @@ -0,0 +1,36 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#pragma once + +#include +#include +#include + +#include "flutter/fml/macros.h" +#include "impeller/geometry/matrix.h" +#include "impeller/geometry/rect.h" +#include "impeller/renderer/texture.h" + +namespace impeller { + +class ContentContext; +class Entity; + +/// Represents a texture and its intended draw position. +struct Snapshot { + std::shared_ptr texture; + /// The offset from the origin where this texture is intended to be + /// rendered. + Vector2 position; + + /// Transform a texture by the given `entity`'s transformation matrix to a new + /// texture. + static std::optional FromTransformedTexture( + const ContentContext& renderer, + const Entity& entity, + std::shared_ptr texture); +}; + +} // namespace impeller diff --git a/impeller/entity/entity.cc b/impeller/entity/entity.cc index df13ef0bfe685..417d61636d902 100644 --- a/impeller/entity/entity.cc +++ b/impeller/entity/entity.cc @@ -30,6 +30,16 @@ void Entity::SetPath(Path path) { path_ = std::move(path); } +Rect Entity::GetTransformedPathBounds() const { + auto points = GetPath().GetBoundingBox()->GetPoints(); + + const auto& transform = GetTransformation(); + for (uint i = 0; i < points.size(); i++) { + points[i] = transform * points[i]; + } + return Rect::MakePointBounds({points.begin(), points.end()}); +} + void Entity::SetAddsToCoverage(bool adds) { adds_to_coverage_ = adds; } diff --git a/impeller/entity/entity.h b/impeller/entity/entity.h index 0f13dca6469a8..5c60967157185 100644 --- a/impeller/entity/entity.h +++ b/impeller/entity/entity.h @@ -65,6 +65,8 @@ class Entity { void SetPath(Path path); + Rect GetTransformedPathBounds() const; + void SetAddsToCoverage(bool adds); bool AddsToCoverage() const; diff --git a/impeller/entity/entity_unittests.cc b/impeller/entity/entity_unittests.cc index b6d75a3527917..b2c9779df029c 100644 --- a/impeller/entity/entity_unittests.cc +++ b/impeller/entity/entity_unittests.cc @@ -2,8 +2,11 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. +#include + #include "flutter/testing/testing.h" #include "impeller/entity/contents/filters/filter_contents.h" +#include "impeller/entity/contents/filters/filter_input.h" #include "impeller/entity/contents/solid_color_contents.h" #include "impeller/entity/contents/solid_stroke_contents.h" #include "impeller/entity/entity.h" @@ -657,11 +660,16 @@ TEST_F(EntityTest, Filters) { ASSERT_TRUE(bridge && boston && kalimba); auto callback = [&](ContentContext& context, RenderPass& pass) -> bool { + auto fi_bridge = FilterInput::Make(bridge); + auto fi_boston = FilterInput::Make(boston); + auto fi_kalimba = FilterInput::Make(kalimba); + auto blend0 = FilterContents::MakeBlend(Entity::BlendMode::kModulate, - {kalimba, boston}); + {fi_kalimba, fi_boston}); - auto blend1 = FilterContents::MakeBlend(Entity::BlendMode::kScreen, - {bridge, blend0, bridge, bridge}); + auto blend1 = FilterContents::MakeBlend( + Entity::BlendMode::kScreen, + {fi_bridge, FilterInput::Make(blend0), fi_bridge, fi_bridge}); Entity entity; entity.SetPath(PathBuilder{}.AddRect({100, 100, 300, 300}).TakePath()); @@ -681,52 +689,71 @@ TEST_F(EntityTest, GaussianBlurFilter) { auto callback = [&](ContentContext& context, RenderPass& pass) -> bool { if (first_frame) { first_frame = false; - ImGui::SetNextWindowSize({500, 170}); + ImGui::SetNextWindowSize({500, 190}); ImGui::SetNextWindowPos({300, 550}); } - ImGui::Begin("Controls"); static float blur_amount[2] = {20, 20}; - ImGui::SliderFloat2("Blur", &blur_amount[0], 0, 200); static Color cover_color(1, 0, 0, 0.2); - ImGui::ColorEdit4("Cover color", reinterpret_cast(&cover_color)); + static Color bounds_color(0, 1, 0, 0.1); static float offset[2] = {500, 400}; + static float rotation = 0; + static float scale[2] = {0.8, 0.8}; + static float skew[2] = {0, 0}; + + ImGui::Begin("Controls"); + ImGui::SliderFloat2("Blur", &blur_amount[0], 0, 200); + ImGui::ColorEdit4("Cover color", reinterpret_cast(&cover_color)); + ImGui::ColorEdit4("Bounds color", reinterpret_cast(&bounds_color)); ImGui::SliderFloat2("Translation", &offset[0], 0, pass.GetRenderTargetSize().width); - static float rotation = 0; ImGui::SliderFloat("Rotation", &rotation, 0, kPi * 2); - static float scale[2] = {0.8, 0.8}; ImGui::SliderFloat2("Scale", &scale[0], 0, 3); - static float skew[2] = {0, 0}; ImGui::SliderFloat2("Skew", &skew[0], -3, 3); ImGui::End(); - auto blend = FilterContents::MakeBlend(Entity::BlendMode::kPlus, - {boston, bridge, bridge}); + auto blend = FilterContents::MakeBlend( + Entity::BlendMode::kPlus, FilterInput::Make({boston, bridge, bridge})); - auto blur = - FilterContents::MakeGaussianBlur(blend, blur_amount[0], blur_amount[1]); + auto blur = FilterContents::MakeGaussianBlur( + FilterInput::Make(blend), blur_amount[0], blur_amount[1]); - auto rect = Rect(-Point(boston->GetSize()) / 2, Size(boston->GetSize())); + ISize input_size = boston->GetSize(); + auto rect = Rect(-Point(input_size) / 2, Size(input_size)); auto ctm = Matrix::MakeTranslation(Vector3(offset[0], offset[1])) * - Matrix::MakeRotation(rotation, Vector4(0, 0, 1, 1)) * + Matrix::MakeRotationZ(Radians(rotation)) * Matrix::MakeScale(Vector3(scale[0], scale[1])) * Matrix::MakeSkew(skew[0], skew[1]); + auto target_contents = blur; + Entity entity; entity.SetPath(PathBuilder{}.AddRect(rect).TakePath()); - entity.SetContents(blur); + entity.SetContents(target_contents); entity.SetTransformation(ctm); entity.Render(context, pass); - // The following entity renders the expected transformed input. + // Renders a red "cover" rectangle that shows the original position of the + // unfiltered input. Entity cover_entity; cover_entity.SetPath(PathBuilder{}.AddRect(rect).TakePath()); - cover_entity.SetContents(SolidColorContents::Make(cover_color)); + cover_entity.SetContents( + SolidColorContents::Make(cover_color.Premultiply())); cover_entity.SetTransformation(ctm); cover_entity.Render(context, pass); + + // Renders a green bounding rect of the target filter. + Entity bounds_entity; + bounds_entity.SetPath( + PathBuilder{}.AddRect(target_contents->GetBounds(entity)).TakePath()); + bounds_entity.SetContents( + SolidColorContents::Make(bounds_color.Premultiply())); + bounds_entity.SetTransformation(Matrix()); + + bounds_entity.Render(context, pass); + return true; }; ASSERT_TRUE(OpenPlaygroundHere(callback)); diff --git a/impeller/entity/shaders/gaussian_blur.frag b/impeller/entity/shaders/gaussian_blur.frag index 5edfafae7e970..61fa0f060d5b9 100644 --- a/impeller/entity/shaders/gaussian_blur.frag +++ b/impeller/entity/shaders/gaussian_blur.frag @@ -35,8 +35,6 @@ vec4 SampleWithBorder(vec2 uv) { } void main() { - vec2 blur_radius_uv = vec2(v_blur_radius) / v_texture_size; - vec4 total = vec4(0); float total_gaussian = 0; for (float i = -v_blur_radius; i <= v_blur_radius; i++) { From 4810c617664f6f098b34c9b7501519812b634361 Mon Sep 17 00:00:00 2001 From: Brandon DeRosier Date: Wed, 30 Mar 2022 13:14:02 -0700 Subject: [PATCH 378/433] Implement drawArc (#109) --- .../display_list/display_list_dispatcher.cc | 6 +- .../display_list/display_list_playground.cc | 21 +++--- .../display_list/display_list_playground.h | 5 ++ .../display_list/display_list_unittests.cc | 50 +++++++++++++- impeller/geometry/constants.h | 5 +- impeller/geometry/path_builder.cc | 65 ++++++++++++++++++- impeller/geometry/path_builder.h | 5 ++ 7 files changed, 142 insertions(+), 15 deletions(-) diff --git a/impeller/display_list/display_list_dispatcher.cc b/impeller/display_list/display_list_dispatcher.cc index c4d61dafc8fb2..a1da89a19dd03 100644 --- a/impeller/display_list/display_list_dispatcher.cc +++ b/impeller/display_list/display_list_dispatcher.cc @@ -11,6 +11,7 @@ #include "impeller/entity/contents/solid_stroke_contents.h" #include "impeller/entity/entity.h" #include "impeller/geometry/path_builder.h" +#include "impeller/geometry/scalar.h" #include "impeller/typographer/backends/skia/text_frame_skia.h" #include "third_party/skia/include/core/SkColor.h" @@ -546,7 +547,10 @@ void DisplayListDispatcher::drawArc(const SkRect& oval_bounds, SkScalar start_degrees, SkScalar sweep_degrees, bool use_center) { - UNIMPLEMENTED; + PathBuilder builder; + builder.AddArc(ToRect(oval_bounds), Degrees(start_degrees), + Degrees(sweep_degrees), use_center); + canvas_.DrawPath(builder.TakePath(), paint_); } // |flutter::Dispatcher| diff --git a/impeller/display_list/display_list_playground.cc b/impeller/display_list/display_list_playground.cc index 4bbcb07fe410e..508af2fcb206c 100644 --- a/impeller/display_list/display_list_playground.cc +++ b/impeller/display_list/display_list_playground.cc @@ -22,24 +22,27 @@ bool DisplayListPlayground::OpenPlaygroundHere( bool DisplayListPlayground::OpenPlaygroundHere( sk_sp list) { + return OpenPlaygroundHere([&list]() { return list; }); +} + +bool DisplayListPlayground::OpenPlaygroundHere( + DisplayListPlaygroundCallback callback) { if (!Playground::is_enabled()) { return true; } - if (!list) { - return false; - } - - DisplayListDispatcher dispatcher; - list->Dispatch(dispatcher); - auto picture = dispatcher.EndRecordingAsPicture(); - AiksContext context(GetContext()); if (!context.IsValid()) { return false; } return Playground::OpenPlaygroundHere( - [&picture, &context](RenderPass& pass) -> bool { + [&context, &callback](RenderPass& pass) -> bool { + auto list = callback(); + + DisplayListDispatcher dispatcher; + list->Dispatch(dispatcher); + auto picture = dispatcher.EndRecordingAsPicture(); + return context.Render(picture, pass); }); } diff --git a/impeller/display_list/display_list_playground.h b/impeller/display_list/display_list_playground.h index f0028a50acf2b..fac3aef91795a 100644 --- a/impeller/display_list/display_list_playground.h +++ b/impeller/display_list/display_list_playground.h @@ -14,6 +14,9 @@ namespace impeller { class DisplayListPlayground : public Playground { public: + using DisplayListPlaygroundCallback = + std::function()>; + DisplayListPlayground(); ~DisplayListPlayground(); @@ -22,6 +25,8 @@ class DisplayListPlayground : public Playground { bool OpenPlaygroundHere(sk_sp list); + bool OpenPlaygroundHere(DisplayListPlaygroundCallback callback); + SkFont CreateTestFontOfSize(SkScalar scalar); SkFont CreateTestFont(); diff --git a/impeller/display_list/display_list_unittests.cc b/impeller/display_list/display_list_unittests.cc index d8487de313523..a0b975f05e6d2 100644 --- a/impeller/display_list/display_list_unittests.cc +++ b/impeller/display_list/display_list_unittests.cc @@ -2,11 +2,17 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. +#include "gtest/gtest.h" +#include "third_party/imgui/imgui.h" +#include "third_party/skia/include/core/SkColor.h" +#include "third_party/skia/include/core/SkPathBuilder.h" + #include "flutter/display_list/display_list_builder.h" #include "flutter/testing/testing.h" #include "impeller/display_list/display_list_image_impeller.h" #include "impeller/display_list/display_list_playground.h" -#include "third_party/skia/include/core/SkPathBuilder.h" +#include "impeller/geometry/point.h" +#include "impeller/playground/widgets.h" namespace impeller { namespace testing { @@ -36,7 +42,7 @@ TEST_F(DisplayListTest, CanDrawImage) { ASSERT_TRUE(OpenPlaygroundHere(builder.Build())); } -TEST_F(DisplayListTest, CapsAndJoins) { +TEST_F(DisplayListTest, CanDrawCapsAndJoins) { flutter::DisplayListBuilder builder; builder.setStyle(SkPaint::Style::kStroke_Style); @@ -81,5 +87,45 @@ TEST_F(DisplayListTest, CapsAndJoins) { ASSERT_TRUE(OpenPlaygroundHere(builder.Build())); } +TEST_F(DisplayListTest, CanDrawArc) { + bool first_frame = true; + auto callback = [&]() { + if (first_frame) { + first_frame = false; + ImGui::SetNextWindowSize({400, 100}); + ImGui::SetNextWindowPos({300, 550}); + } + + static float start_angle = 45; + static float sweep_angle = 270; + static bool use_center = true; + + ImGui::Begin("Controls"); + ImGui::SliderFloat("Start angle", &start_angle, -360, 360); + ImGui::SliderFloat("Sweep angle", &sweep_angle, -360, 360); + ImGui::Checkbox("Use center", &use_center); + ImGui::End(); + + auto [p1, p2] = IMPELLER_PLAYGROUND_LINE( + Point(200, 200), Point(400, 400), 20, Color::White(), Color::White()); + + flutter::DisplayListBuilder builder; + builder.setStyle(SkPaint::Style::kStroke_Style); + builder.setStrokeCap(SkPaint::Cap::kRound_Cap); + builder.setStrokeJoin(SkPaint::Join::kMiter_Join); + builder.setStrokeMiter(10); + auto rect = SkRect::MakeLTRB(p1.x, p1.y, p2.x, p2.y); + builder.setColor(SK_ColorGREEN); + builder.setStrokeWidth(2); + builder.drawRect(rect); + builder.setColor(SK_ColorRED); + builder.setStrokeWidth(10); + builder.drawArc(rect, start_angle, sweep_angle, use_center); + + return builder.Build(); + }; + ASSERT_TRUE(OpenPlaygroundHere(callback)); +} + } // namespace testing } // namespace impeller diff --git a/impeller/geometry/constants.h b/impeller/geometry/constants.h index a0cadfffcc483..b297c575ceb4d 100644 --- a/impeller/geometry/constants.h +++ b/impeller/geometry/constants.h @@ -19,9 +19,12 @@ constexpr float klogE_2 = 0.69314718055994530942; // log_e 10 constexpr float klogE_10 = 2.30258509299404568402; -// pi */ +// pi constexpr float kPi = 3.14159265358979323846; +// pi*2 +constexpr float k2Pi = 6.28318530717958647693; + // pi/2 constexpr float kPiOver2 = 1.57079632679489661923; diff --git a/impeller/geometry/path_builder.cc b/impeller/geometry/path_builder.cc index 81e269806c234..9ddb29690060f 100644 --- a/impeller/geometry/path_builder.cc +++ b/impeller/geometry/path_builder.cc @@ -4,6 +4,8 @@ #include "path_builder.h" +#include + namespace impeller { PathBuilder::PathBuilder() = default; @@ -288,10 +290,69 @@ PathBuilder& PathBuilder::AddRoundedRect(Rect rect, RoundingRadii radii) { return *this; } +PathBuilder& PathBuilder::AddArc(const Rect& oval_bounds, + Radians start, + Radians sweep, + bool use_center) { + if (sweep.radians < 0) { + start.radians += sweep.radians; + sweep.radians *= -1; + } + sweep.radians = std::min(k2Pi, sweep.radians); + start.radians = std::fmod(start.radians, k2Pi); + + const Point radius = {oval_bounds.size.width * 0.5f, + oval_bounds.size.height * 0.5f}; + const Point center = {oval_bounds.origin.x + radius.x, + oval_bounds.origin.y + radius.y}; + + Vector2 p1_unit(std::cos(start.radians), std::sin(start.radians)); + + if (use_center) { + MoveTo(center); + LineTo(center + p1_unit * radius); + } else { + MoveTo(center + p1_unit * radius); + } + + while (sweep.radians > 0) { + Vector2 p2_unit; + Scalar quadrant_angle; + if (sweep.radians < kPiOver2) { + quadrant_angle = sweep.radians; + p2_unit = Vector2(std::cos(start.radians + quadrant_angle), + std::sin(start.radians + quadrant_angle)); + } else { + quadrant_angle = kPiOver2; + p2_unit = Vector2(-p1_unit.y, p1_unit.x); + } + + Vector2 arc_cp_lengths = + (quadrant_angle / kPiOver2) * kArcApproximationMagic * radius; + + Point p1 = center + p1_unit * radius; + Point p2 = center + p2_unit * radius; + Point cp1 = p1 + Vector2(-p1_unit.y, p1_unit.x) * arc_cp_lengths; + Point cp2 = p2 + Vector2(p2_unit.y, -p2_unit.x) * arc_cp_lengths; + + prototype_.AddCubicComponent(p1, cp1, cp2, p2); + current_ = p2; + + start.radians += quadrant_angle; + sweep.radians -= quadrant_angle; + p1_unit = p2_unit; + } + + if (use_center) { + Close(); + } + + return *this; +} + PathBuilder& PathBuilder::AddOval(const Rect& container) { const Point r = {container.size.width * 0.5f, container.size.height * 0.5f}; - const Point c = {container.origin.x + (container.size.width * 0.5f), - container.origin.y + (container.size.height * 0.5f)}; + const Point c = {container.origin.x + r.x, container.origin.y + r.y}; const Point m = {kArcApproximationMagic * r.x, kArcApproximationMagic * r.y}; MoveTo({c.x, c.y - r.y}); diff --git a/impeller/geometry/path_builder.h b/impeller/geometry/path_builder.h index 038a9f49f3219..13e9240d68600 100644 --- a/impeller/geometry/path_builder.h +++ b/impeller/geometry/path_builder.h @@ -60,6 +60,11 @@ class PathBuilder { PathBuilder& AddCircle(const Point& center, Scalar radius); + PathBuilder& AddArc(const Rect& oval_bounds, + Radians start, + Radians sweep, + bool use_center = false); + PathBuilder& AddOval(const Rect& rect); PathBuilder& AddLine(const Point& p1, const Point& p2); From 2f2acb183302778164548ed9b84ae94e6d2e48c1 Mon Sep 17 00:00:00 2001 From: Jim Graham Date: Wed, 30 Mar 2022 14:15:42 -0700 Subject: [PATCH 379/433] support new DlVertices object in dispatcher (#108) --- impeller/display_list/display_list_dispatcher.cc | 9 ++++++++- impeller/display_list/display_list_dispatcher.h | 6 +++++- 2 files changed, 13 insertions(+), 2 deletions(-) diff --git a/impeller/display_list/display_list_dispatcher.cc b/impeller/display_list/display_list_dispatcher.cc index a1da89a19dd03..2f9abb0010bc3 100644 --- a/impeller/display_list/display_list_dispatcher.cc +++ b/impeller/display_list/display_list_dispatcher.cc @@ -561,7 +561,14 @@ void DisplayListDispatcher::drawPoints(SkCanvas::PointMode mode, } // |flutter::Dispatcher| -void DisplayListDispatcher::drawVertices(const sk_sp vertices, +void DisplayListDispatcher::drawSkVertices(const sk_sp vertices, + SkBlendMode mode) { + // Needs https://github.com/flutter/flutter/issues/95434 + UNIMPLEMENTED; +} + +// |flutter::Dispatcher| +void DisplayListDispatcher::drawVertices(const flutter::DlVertices* vertices, flutter::DlBlendMode mode) { // Needs https://github.com/flutter/flutter/issues/95434 UNIMPLEMENTED; diff --git a/impeller/display_list/display_list_dispatcher.h b/impeller/display_list/display_list_dispatcher.h index 1c15eed83c145..28dc72f0947a0 100644 --- a/impeller/display_list/display_list_dispatcher.h +++ b/impeller/display_list/display_list_dispatcher.h @@ -168,7 +168,11 @@ class DisplayListDispatcher final : public flutter::Dispatcher { const SkPoint points[]) override; // |flutter::Dispatcher| - void drawVertices(const sk_sp vertices, + void drawSkVertices(const sk_sp vertices, + SkBlendMode mode) override; + + // |flutter::Dispatcher| + void drawVertices(const flutter::DlVertices* vertices, flutter::DlBlendMode mode) override; // |flutter::Dispatcher| From e95ebb5d4bfa620773acccf792cb0ba6292bdb85 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 1 Apr 2022 13:15:47 -0700 Subject: [PATCH 380/433] Bump github/codeql-action from 1.1.5 to 2.1.6 (#110) Bumps [github/codeql-action](https://github.com/github/codeql-action) from 1.1.5 to 2.1.6. - [Release notes](https://github.com/github/codeql-action/releases) - [Changelog](https://github.com/github/codeql-action/blob/main/CHANGELOG.md) - [Commits](https://github.com/github/codeql-action/compare/883476649888a9e8e219d5b2e6b789dc024f690c...28eead240834b314f7def40f6fcba65d100d99b1) --- updated-dependencies: - dependency-name: github/codeql-action dependency-type: direct:production update-type: version-update:semver-major ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- impeller/.github/workflows/scorecards-analysis.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/impeller/.github/workflows/scorecards-analysis.yml b/impeller/.github/workflows/scorecards-analysis.yml index 0d979e781ad53..3e17223ef12cb 100644 --- a/impeller/.github/workflows/scorecards-analysis.yml +++ b/impeller/.github/workflows/scorecards-analysis.yml @@ -48,6 +48,6 @@ jobs: # Upload the results to GitHub's code scanning dashboard. - name: "Upload to code-scanning" - uses: github/codeql-action/upload-sarif@883476649888a9e8e219d5b2e6b789dc024f690c + uses: github/codeql-action/upload-sarif@28eead240834b314f7def40f6fcba65d100d99b1 with: sarif_file: results.sarif From 5ba3e4eaa7d229bf3216814c48533e23d592d8de Mon Sep 17 00:00:00 2001 From: Brandon DeRosier Date: Mon, 4 Apr 2022 16:30:21 -0700 Subject: [PATCH 381/433] Add blur styles to gaussian blur (#107) --- .../contents/filters/filter_contents.cc | 16 +++- .../entity/contents/filters/filter_contents.h | 22 ++++- .../filters/gaussian_blur_filter_contents.cc | 91 ++++++++++++++----- .../filters/gaussian_blur_filter_contents.h | 11 +++ impeller/entity/entity_unittests.cc | 33 +++++-- impeller/entity/shaders/gaussian_blur.frag | 29 ++++-- impeller/entity/shaders/gaussian_blur.vert | 14 +++ 7 files changed, 168 insertions(+), 48 deletions(-) diff --git a/impeller/entity/contents/filters/filter_contents.cc b/impeller/entity/contents/filters/filter_contents.cc index a038b0e8d6316..59a7149fc0021 100644 --- a/impeller/entity/contents/filters/filter_contents.cc +++ b/impeller/entity/contents/filters/filter_contents.cc @@ -61,20 +61,26 @@ std::shared_ptr FilterContents::MakeBlend( std::shared_ptr FilterContents::MakeDirectionalGaussianBlur( FilterInput::Ref input, - Vector2 blur_vector) { + Vector2 blur_vector, + BlurStyle blur_style, + FilterInput::Ref source_override) { auto blur = std::make_shared(); blur->SetInputs({input}); blur->SetBlurVector(blur_vector); + blur->SetBlurStyle(blur_style); + blur->SetSourceOverride(source_override); return blur; } std::shared_ptr FilterContents::MakeGaussianBlur( FilterInput::Ref input, Scalar sigma_x, - Scalar sigma_y) { - auto x_blur = MakeDirectionalGaussianBlur(input, Point(sigma_x, 0)); - auto y_blur = - MakeDirectionalGaussianBlur(FilterInput::Make(x_blur), Point(0, sigma_y)); + Scalar sigma_y, + BlurStyle blur_style) { + auto x_blur = + MakeDirectionalGaussianBlur(input, Point(sigma_x, 0), BlurStyle::kNormal); + auto y_blur = MakeDirectionalGaussianBlur( + FilterInput::Make(x_blur), Point(0, sigma_y), blur_style, input); return y_blur; } diff --git a/impeller/entity/contents/filters/filter_contents.h b/impeller/entity/contents/filters/filter_contents.h index 2088163277735..63c60bcc44eac 100644 --- a/impeller/entity/contents/filters/filter_contents.h +++ b/impeller/entity/contents/filters/filter_contents.h @@ -18,15 +18,31 @@ class Pipeline; class FilterContents : public Contents { public: + enum class BlurStyle { + /// Blurred inside and outside. + kNormal, + /// Solid inside, blurred outside. + kSolid, + /// Nothing inside, blurred outside. + kOuter, + /// Blurred inside, nothing outside. + kInner, + }; + static std::shared_ptr MakeBlend(Entity::BlendMode blend_mode, FilterInput::Vector inputs); static std::shared_ptr MakeDirectionalGaussianBlur( FilterInput::Ref input, - Vector2 blur_vector); + Vector2 blur_vector, + BlurStyle blur_style = BlurStyle::kNormal, + FilterInput::Ref alpha_mask = nullptr); - static std::shared_ptr - MakeGaussianBlur(FilterInput::Ref input, Scalar sigma_x, Scalar sigma_y); + static std::shared_ptr MakeGaussianBlur( + FilterInput::Ref input, + Scalar sigma_x, + Scalar sigma_y, + BlurStyle blur_style = BlurStyle::kNormal); FilterContents(); diff --git a/impeller/entity/contents/filters/gaussian_blur_filter_contents.cc b/impeller/entity/contents/filters/gaussian_blur_filter_contents.cc index 3f7f572ffa662..34441ddff9fd4 100644 --- a/impeller/entity/contents/filters/gaussian_blur_filter_contents.cc +++ b/impeller/entity/contents/filters/gaussian_blur_filter_contents.cc @@ -30,6 +30,38 @@ void DirectionalGaussianBlurFilterContents::SetBlurVector(Vector2 blur_vector) { blur_vector_ = blur_vector; } +void DirectionalGaussianBlurFilterContents::SetBlurStyle(BlurStyle blur_style) { + blur_style_ = blur_style; + + switch (blur_style) { + case FilterContents::BlurStyle::kNormal: + src_color_factor_ = false; + inner_blur_factor_ = true; + outer_blur_factor_ = true; + break; + case FilterContents::BlurStyle::kSolid: + src_color_factor_ = true; + inner_blur_factor_ = false; + outer_blur_factor_ = true; + break; + case FilterContents::BlurStyle::kOuter: + src_color_factor_ = false; + inner_blur_factor_ = false; + outer_blur_factor_ = true; + break; + case FilterContents::BlurStyle::kInner: + src_color_factor_ = false; + inner_blur_factor_ = true; + outer_blur_factor_ = false; + break; + } +} + +void DirectionalGaussianBlurFilterContents::SetSourceOverride( + FilterInput::Ref alpha_mask) { + source_override_ = alpha_mask; +} + bool DirectionalGaussianBlurFilterContents::RenderFilter( const FilterInput::Vector& inputs, const ContentContext& renderer, @@ -45,43 +77,59 @@ bool DirectionalGaussianBlurFilterContents::RenderFilter( auto& host_buffer = pass.GetTransientsBuffer(); - // Because this filter is intended to be used with only one input parameter, - // and GetBounds just increases the input size by a factor of the direction, - // we we can just scale up the UVs by the same amount and don't need to worry - // about mapping the UVs to the destination rect (like we do in - // BlendFilterContents). - auto input = inputs[0]->GetSnapshot(renderer, entity); - auto input_size = input->texture->GetSize(); + auto input_bounds = inputs[0]->GetBounds(entity); + auto filter_bounds = GetBounds(entity); auto transformed_blur = entity.GetTransformation().TransformDirection(blur_vector_); - auto uv_offset = transformed_blur.Abs() / input_size; // LTRB Scalar uv[4] = { - -uv_offset.x, - -uv_offset.y, - 1 + uv_offset.x, - 1 + uv_offset.y, + (filter_bounds.GetLeft() - input_bounds.GetLeft()) / + input_bounds.size.width, + (filter_bounds.GetTop() - input_bounds.GetTop()) / + input_bounds.size.height, + 1 + (filter_bounds.GetRight() - input_bounds.GetRight()) / + input_bounds.size.width, + 1 + (filter_bounds.GetBottom() - input_bounds.GetBottom()) / + input_bounds.size.height, + }; + + auto source = source_override_ ? source_override_ : inputs[0]; + auto source_texture = source->GetSnapshot(renderer, entity); + auto source_bounds = source->GetBounds(entity); + + // LTRB + Scalar uv_src[4] = { + (filter_bounds.GetLeft() - source_bounds.GetLeft()) / + source_bounds.size.width, + (filter_bounds.GetTop() - source_bounds.GetTop()) / + source_bounds.size.height, + 1 + (filter_bounds.GetRight() - source_bounds.GetRight()) / + source_bounds.size.width, + 1 + (filter_bounds.GetBottom() - source_bounds.GetBottom()) / + source_bounds.size.height, }; VertexBufferBuilder vtx_builder; - auto size = pass.GetRenderTargetSize(); vtx_builder.AddVertices({ - {Point(0, 0), Point(uv[0], uv[1])}, - {Point(size.width, 0), Point(uv[2], uv[1])}, - {Point(size.width, size.height), Point(uv[2], uv[3])}, - {Point(0, 0), Point(uv[0], uv[1])}, - {Point(size.width, size.height), Point(uv[2], uv[3])}, - {Point(0, size.height), Point(uv[0], uv[3])}, + {Point(0, 0), Point(uv[0], uv[1]), Point(uv_src[0], uv_src[1])}, + {Point(1, 0), Point(uv[2], uv[1]), Point(uv_src[2], uv_src[1])}, + {Point(1, 1), Point(uv[2], uv[3]), Point(uv_src[2], uv_src[3])}, + {Point(0, 0), Point(uv[0], uv[1]), Point(uv_src[0], uv_src[1])}, + {Point(1, 1), Point(uv[2], uv[3]), Point(uv_src[2], uv_src[3])}, + {Point(0, 1), Point(uv[0], uv[3]), Point(uv_src[0], uv_src[3])}, }); auto vtx_buffer = vtx_builder.CreateVertexBuffer(host_buffer); VS::FrameInfo frame_info; - frame_info.texture_size = Point(input_size); + frame_info.texture_size = Point(input_bounds.size); frame_info.blur_radius = transformed_blur.GetLength(); frame_info.blur_direction = transformed_blur.Normalize(); + frame_info.src_factor = src_color_factor_; + frame_info.inner_blur_factor = inner_blur_factor_; + frame_info.outer_blur_factor = outer_blur_factor_; auto sampler = renderer.GetContext()->GetSamplerLibrary()->GetSampler({}); @@ -93,8 +141,9 @@ bool DirectionalGaussianBlurFilterContents::RenderFilter( cmd.BindVertices(vtx_buffer); FS::BindTextureSampler(cmd, input->texture, sampler); + FS::BindAlphaMaskSampler(cmd, source_texture->texture, sampler); - frame_info.mvp = Matrix::MakeOrthographic(size); + frame_info.mvp = Matrix::MakeOrthographic(ISize(1, 1)); auto uniform_view = host_buffer.EmplaceUniform(frame_info); VS::BindFrameInfo(cmd, uniform_view); diff --git a/impeller/entity/contents/filters/gaussian_blur_filter_contents.h b/impeller/entity/contents/filters/gaussian_blur_filter_contents.h index 17aa92af792fe..0b401d2cc0c43 100644 --- a/impeller/entity/contents/filters/gaussian_blur_filter_contents.h +++ b/impeller/entity/contents/filters/gaussian_blur_filter_contents.h @@ -4,6 +4,8 @@ #pragma once +#include +#include #include "impeller/entity/contents/filters/filter_contents.h" #include "impeller/entity/contents/filters/filter_input.h" #include "impeller/geometry/matrix.h" @@ -18,6 +20,10 @@ class DirectionalGaussianBlurFilterContents final : public FilterContents { void SetBlurVector(Vector2 blur_vector); + void SetBlurStyle(BlurStyle blur_style); + + void SetSourceOverride(FilterInput::Ref alpha_mask); + // |Contents| Rect GetBounds(const Entity& entity) const override; @@ -30,6 +36,11 @@ class DirectionalGaussianBlurFilterContents final : public FilterContents { const Rect& bounds) const override; Vector2 blur_vector_; + BlurStyle blur_style_ = BlurStyle::kNormal; + bool src_color_factor_ = false; + bool inner_blur_factor_ = true; + bool outer_blur_factor_ = true; + FilterInput::Ref source_override_; FML_DISALLOW_COPY_AND_ASSIGN(DirectionalGaussianBlurFilterContents); }; diff --git a/impeller/entity/entity_unittests.cc b/impeller/entity/entity_unittests.cc index b2c9779df029c..c4804d0567700 100644 --- a/impeller/entity/entity_unittests.cc +++ b/impeller/entity/entity_unittests.cc @@ -689,11 +689,18 @@ TEST_F(EntityTest, GaussianBlurFilter) { auto callback = [&](ContentContext& context, RenderPass& pass) -> bool { if (first_frame) { first_frame = false; - ImGui::SetNextWindowSize({500, 190}); + ImGui::SetNextWindowSize({500, 220}); ImGui::SetNextWindowPos({300, 550}); } + const char* blur_style_names[] = {"Normal", "Solid", "Outer", "Inner"}; + const FilterContents::BlurStyle blur_styles[] = { + FilterContents::BlurStyle::kNormal, FilterContents::BlurStyle::kSolid, + FilterContents::BlurStyle::kOuter, FilterContents::BlurStyle::kInner}; + + // UI state. static float blur_amount[2] = {20, 20}; + static int selected_blur_style = 0; static Color cover_color(1, 0, 0, 0.2); static Color bounds_color(0, 1, 0, 0.1); static float offset[2] = {500, 400}; @@ -702,21 +709,27 @@ TEST_F(EntityTest, GaussianBlurFilter) { static float skew[2] = {0, 0}; ImGui::Begin("Controls"); - ImGui::SliderFloat2("Blur", &blur_amount[0], 0, 200); - ImGui::ColorEdit4("Cover color", reinterpret_cast(&cover_color)); - ImGui::ColorEdit4("Bounds color", reinterpret_cast(&bounds_color)); - ImGui::SliderFloat2("Translation", &offset[0], 0, - pass.GetRenderTargetSize().width); - ImGui::SliderFloat("Rotation", &rotation, 0, kPi * 2); - ImGui::SliderFloat2("Scale", &scale[0], 0, 3); - ImGui::SliderFloat2("Skew", &skew[0], -3, 3); + { + ImGui::SliderFloat2("Blur", &blur_amount[0], 0, 200); + ImGui::Combo("Blur style", &selected_blur_style, blur_style_names, + sizeof(blur_style_names) / sizeof(char*)); + ImGui::ColorEdit4("Cover color", reinterpret_cast(&cover_color)); + ImGui::ColorEdit4("Bounds color", + reinterpret_cast(&bounds_color)); + ImGui::SliderFloat2("Translation", &offset[0], 0, + pass.GetRenderTargetSize().width); + ImGui::SliderFloat("Rotation", &rotation, 0, kPi * 2); + ImGui::SliderFloat2("Scale", &scale[0], 0, 3); + ImGui::SliderFloat2("Skew", &skew[0], -3, 3); + } ImGui::End(); auto blend = FilterContents::MakeBlend( Entity::BlendMode::kPlus, FilterInput::Make({boston, bridge, bridge})); auto blur = FilterContents::MakeGaussianBlur( - FilterInput::Make(blend), blur_amount[0], blur_amount[1]); + FilterInput::Make(blend), blur_amount[0], blur_amount[1], + blur_styles[selected_blur_style]); ISize input_size = boston->GetSize(); auto rect = Rect(-Point(input_size) / 2, Size(input_size)); diff --git a/impeller/entity/shaders/gaussian_blur.frag b/impeller/entity/shaders/gaussian_blur.frag index 61fa0f060d5b9..939ecf9e20c78 100644 --- a/impeller/entity/shaders/gaussian_blur.frag +++ b/impeller/entity/shaders/gaussian_blur.frag @@ -5,16 +5,21 @@ // 1D (directional) gaussian blur. // // Paths for future optimization: -// * Remove the uv bounds check branch in SampleColor by adding optional +// * Remove the uv bounds multiplier in SampleColor by adding optional // support for SamplerAddressMode::ClampToBorder in the texture sampler. // * Sample from higher mipmap levels when the blur radius is high enough. uniform sampler2D texture_sampler; +uniform sampler2D alpha_mask_sampler; in vec2 v_texture_coords; +in vec2 v_src_texture_coords; in vec2 v_texture_size; in vec2 v_blur_direction; in float v_blur_radius; +in float v_src_factor; +in float v_inner_blur_factor; +in float v_outer_blur_factor; out vec4 frag_color; @@ -27,21 +32,27 @@ float Gaussian(float x) { } // Emulate SamplerAddressMode::ClampToBorder. -vec4 SampleWithBorder(vec2 uv) { - if (uv.x > 0 && uv.y > 0 && uv.x < 1 && uv.y < 1) { - return texture(texture_sampler, uv); - } - return vec4(0); +vec4 SampleWithBorder(sampler2D tex, vec2 uv) { + float within_bounds = float(uv.x >= 0 && uv.y >= 0 && uv.x < 1 && uv.y < 1); + return texture(tex, uv) * within_bounds; } void main() { vec4 total = vec4(0); float total_gaussian = 0; + vec2 blur_uv_offset = v_blur_direction / v_texture_size; for (float i = -v_blur_radius; i <= v_blur_radius; i++) { float gaussian = Gaussian(i); total_gaussian += gaussian; - total += gaussian * SampleWithBorder(v_texture_coords + - v_blur_direction * i / v_texture_size); + total += gaussian * SampleWithBorder(texture_sampler, + v_texture_coords + blur_uv_offset * i); } - frag_color = total / total_gaussian; + + vec4 blur_color = total / total_gaussian; + + vec4 src_color = SampleWithBorder(alpha_mask_sampler, v_src_texture_coords); + float blur_factor = v_inner_blur_factor * float(src_color.a > 0) + + v_outer_blur_factor * float(src_color.a == 0); + + frag_color = blur_color * blur_factor + src_color * v_src_factor; } diff --git a/impeller/entity/shaders/gaussian_blur.vert b/impeller/entity/shaders/gaussian_blur.vert index 8d7d1db931de1..a358a2b5719be 100644 --- a/impeller/entity/shaders/gaussian_blur.vert +++ b/impeller/entity/shaders/gaussian_blur.vert @@ -5,23 +5,37 @@ uniform FrameInfo { mat4 mvp; vec2 texture_size; + vec2 blur_direction; float blur_radius; + + float src_factor; + float inner_blur_factor; + float outer_blur_factor; } frame_info; in vec2 vertices; in vec2 texture_coords; +in vec2 src_texture_coords; out vec2 v_texture_coords; +out vec2 v_src_texture_coords; out vec2 v_texture_size; out vec2 v_blur_direction; out float v_blur_radius; +out float v_src_factor; +out float v_inner_blur_factor; +out float v_outer_blur_factor; void main() { gl_Position = frame_info.mvp * vec4(vertices, 0.0, 1.0); v_texture_coords = texture_coords; + v_src_texture_coords = src_texture_coords; v_texture_size = frame_info.texture_size; v_blur_direction = frame_info.blur_direction; v_blur_radius = frame_info.blur_radius; + v_src_factor = frame_info.src_factor; + v_inner_blur_factor = frame_info.inner_blur_factor; + v_outer_blur_factor = frame_info.outer_blur_factor; } From e4db675c9562b79b07f9f826e867a1c46ccd2264 Mon Sep 17 00:00:00 2001 From: Brandon DeRosier Date: Tue, 5 Apr 2022 00:37:03 -0700 Subject: [PATCH 382/433] Fix solid stroke contour switching + round cap smoothing, and add transparent path overdraw example (#112) --- impeller/aiks/aiks_unittests.cc | 38 +++++++++++++++++++ .../entity/contents/solid_stroke_contents.cc | 11 +++++- .../entity/contents/solid_stroke_contents.h | 2 - 3 files changed, 47 insertions(+), 4 deletions(-) diff --git a/impeller/aiks/aiks_unittests.cc b/impeller/aiks/aiks_unittests.cc index e554bb30acaa8..73b5039e032c7 100644 --- a/impeller/aiks/aiks_unittests.cc +++ b/impeller/aiks/aiks_unittests.cc @@ -482,5 +482,43 @@ TEST_F(AiksTest, TransformMultipliesCorrectly) { // clang-format on } +TEST_F(AiksTest, PathsShouldHaveUniformAlpha) { + // Compare with https://fiddle.skia.org/c/027392122bec8ac2b5d5de00a4b9bbe2 + Canvas canvas; + Paint paint; + + paint.color = Color::White(); + canvas.DrawPaint(paint); + + paint.color = Color::Black().WithAlpha(0.5); + paint.style = Paint::Style::kStroke; + paint.stroke_width = 10; + + Path path = PathBuilder{} + .MoveTo({20, 20}) + .QuadraticCurveTo({60, 20}, {60, 60}) + .Close() + .MoveTo({60, 20}) + .QuadraticCurveTo({60, 60}, {20, 60}) + .TakePath(); + + canvas.Scale({3, 3}); + for (auto join : + {SolidStrokeContents::Join::kBevel, SolidStrokeContents::Join::kRound, + SolidStrokeContents::Join::kMiter}) { + paint.stroke_join = join; + for (auto cap : + {SolidStrokeContents::Cap::kButt, SolidStrokeContents::Cap::kSquare, + SolidStrokeContents::Cap::kRound}) { + paint.stroke_cap = cap; + canvas.DrawPath(path, paint); + canvas.Translate({80, 0}); + } + canvas.Translate({-240, 60}); + } + + ASSERT_TRUE(OpenPlaygroundHere(canvas.EndRecordingAsPicture())); +} + } // namespace testing } // namespace impeller diff --git a/impeller/entity/contents/solid_stroke_contents.cc b/impeller/entity/contents/solid_stroke_contents.cc index 592c7792c8b93..abbc96687ce47 100644 --- a/impeller/entity/contents/solid_stroke_contents.cc +++ b/impeller/entity/contents/solid_stroke_contents.cc @@ -79,7 +79,11 @@ static VertexBuffer CreateSolidStrokeVertices( vtx.vertex_normal = {}; vtx.pen_down = 0.0; vtx_builder.AppendVertex(vtx); + vtx.vertex_position = polyline.points[contour_start_point_i]; + // Append two transparent vertices at the beginning of the new contour + // because it's a triangle strip. + vtx_builder.AppendVertex(vtx); vtx_builder.AppendVertex(vtx); } @@ -152,9 +156,13 @@ bool SolidStrokeContents::Render(const ContentContext& renderer, cmd.pipeline = renderer.GetSolidStrokePipeline(OptionsFromPassAndEntity(pass, entity)); cmd.stencil_reference = entity.GetStencilDepth(); + + auto smoothing = SmoothingApproximation( + 5.0 / (stroke_size_ * entity.GetTransformation().GetMaxBasisLength()), + 0.0, 0.0); cmd.BindVertices(CreateSolidStrokeVertices( entity.GetPath(), pass.GetTransientsBuffer(), cap_proc_, join_proc_, - miter_limit_, arc_smoothing_approximation_)); + miter_limit_, smoothing)); VS::BindFrameInfo(cmd, pass.GetTransientsBuffer().EmplaceUniform(frame_info)); VS::BindStrokeInfo(cmd, pass.GetTransientsBuffer().EmplaceUniform(stroke_info)); @@ -166,7 +174,6 @@ bool SolidStrokeContents::Render(const ContentContext& renderer, void SolidStrokeContents::SetStrokeSize(Scalar size) { stroke_size_ = size; - arc_smoothing_approximation_ = SmoothingApproximation(5.0 / size, 0.0, 0.0); } Scalar SolidStrokeContents::GetStrokeSize() const { diff --git a/impeller/entity/contents/solid_stroke_contents.h b/impeller/entity/contents/solid_stroke_contents.h index 8bb1501e0434e..66f81cc002e85 100644 --- a/impeller/entity/contents/solid_stroke_contents.h +++ b/impeller/entity/contents/solid_stroke_contents.h @@ -74,8 +74,6 @@ class SolidStrokeContents final : public Contents { RenderPass& pass) const override; private: - SmoothingApproximation arc_smoothing_approximation_; - Color color_; Scalar stroke_size_ = 0.0; Scalar miter_limit_ = 4.0; From ed720e8fafe30343dac331a3bae231fbe9b63a34 Mon Sep 17 00:00:00 2001 From: Brandon DeRosier Date: Tue, 5 Apr 2022 13:12:17 -0700 Subject: [PATCH 383/433] Fix SaveLayer/entity subpass behavioral problems (#111) --- impeller/aiks/aiks_playground.cc | 11 +++++++-- impeller/aiks/aiks_playground.h | 5 ++++ impeller/aiks/aiks_unittests.cc | 33 ++++++++++++++++++++++++- impeller/aiks/canvas.cc | 5 ++-- impeller/entity/contents/contents.cc | 7 +----- impeller/entity/entity.cc | 10 +++----- impeller/entity/entity_pass.cc | 33 ++++++++++++++++++++----- impeller/entity/entity_pass.h | 4 ++- impeller/entity/entity_unittests.cc | 8 ++++++ impeller/geometry/geometry_unittests.cc | 13 +++++++--- impeller/geometry/rect.h | 16 +++++++++++- 11 files changed, 116 insertions(+), 29 deletions(-) diff --git a/impeller/aiks/aiks_playground.cc b/impeller/aiks/aiks_playground.cc index 572b761f83ab5..ff6d3585b9a8b 100644 --- a/impeller/aiks/aiks_playground.cc +++ b/impeller/aiks/aiks_playground.cc @@ -13,6 +13,13 @@ AiksPlayground::AiksPlayground() = default; AiksPlayground::~AiksPlayground() = default; bool AiksPlayground::OpenPlaygroundHere(const Picture& picture) { + return OpenPlaygroundHere( + [&picture](AiksContext& renderer, RenderPass& pass) -> bool { + return renderer.Render(picture, pass); + }); +} + +bool AiksPlayground::OpenPlaygroundHere(AiksPlaygroundCallback callback) { if (!Playground::is_enabled()) { return true; } @@ -24,8 +31,8 @@ bool AiksPlayground::OpenPlaygroundHere(const Picture& picture) { } return Playground::OpenPlaygroundHere( - [&renderer, &picture](RenderPass& pass) -> bool { - return renderer.Render(picture, pass); + [&renderer, &callback](RenderPass& pass) -> bool { + return callback(renderer, pass); }); } diff --git a/impeller/aiks/aiks_playground.h b/impeller/aiks/aiks_playground.h index a77622b012ad9..4a7288fd18471 100644 --- a/impeller/aiks/aiks_playground.h +++ b/impeller/aiks/aiks_playground.h @@ -5,6 +5,7 @@ #pragma once #include "flutter/fml/macros.h" +#include "impeller/aiks/aiks_context.h" #include "impeller/aiks/picture.h" #include "impeller/playground/playground.h" @@ -12,12 +13,16 @@ namespace impeller { class AiksPlayground : public Playground { public: + using AiksPlaygroundCallback = std::function; + AiksPlayground(); ~AiksPlayground(); bool OpenPlaygroundHere(const Picture& picture); + bool OpenPlaygroundHere(AiksPlaygroundCallback callback); + private: FML_DISALLOW_COPY_AND_ASSIGN(AiksPlayground); }; diff --git a/impeller/aiks/aiks_unittests.cc b/impeller/aiks/aiks_unittests.cc index 73b5039e032c7..2b74ae21d6650 100644 --- a/impeller/aiks/aiks_unittests.cc +++ b/impeller/aiks/aiks_unittests.cc @@ -9,6 +9,7 @@ #include "impeller/aiks/image.h" #include "impeller/geometry/geometry_unittests.h" #include "impeller/geometry/path_builder.h" +#include "impeller/playground/widgets.h" #include "impeller/typographer/backends/skia/text_frame_skia.h" #include "impeller/typographer/backends/skia/text_render_context_skia.h" #include "third_party/skia/include/core/SkData.h" @@ -516,8 +517,38 @@ TEST_F(AiksTest, PathsShouldHaveUniformAlpha) { } canvas.Translate({-240, 60}); } +} - ASSERT_TRUE(OpenPlaygroundHere(canvas.EndRecordingAsPicture())); +TEST_F(AiksTest, CoverageOriginShouldBeAccountedForInSubpasses) { + auto callback = [](AiksContext& renderer, RenderPass& pass) { + Canvas canvas; + Paint alpha; + alpha.color = Color::Red().WithAlpha(0.5); + + auto current = Point{25, 25}; + const auto offset = Point{25, 25}; + const auto size = Size(100, 100); + + auto [b0, b1] = IMPELLER_PLAYGROUND_LINE(Point(40, 40), Point(160, 160), 10, + Color::White(), Color::White()); + auto bounds = Rect::MakeLTRB(b0.x, b0.y, b1.x, b1.y); + + canvas.DrawRect(bounds, Paint{.color = Color::Yellow(), + .stroke_width = 5.0f, + .style = Paint::Style::kStroke}); + + canvas.SaveLayer(alpha, bounds); + + canvas.DrawRect({current, size}, Paint{.color = Color::Red()}); + canvas.DrawRect({current += offset, size}, Paint{.color = Color::Green()}); + canvas.DrawRect({current += offset, size}, Paint{.color = Color::Blue()}); + + canvas.Restore(); + + return renderer.Render(canvas.EndRecordingAsPicture(), pass); + }; + + ASSERT_TRUE(OpenPlaygroundHere(callback)); } } // namespace testing diff --git a/impeller/aiks/canvas.cc b/impeller/aiks/canvas.cc index a4afb84e09b94..8a7e001a214a9 100644 --- a/impeller/aiks/canvas.cc +++ b/impeller/aiks/canvas.cc @@ -264,14 +264,13 @@ size_t Canvas::GetStencilDepth() const { void Canvas::Save(bool create_subpass) { auto entry = CanvasStackEntry{}; + entry.xformation = xformation_stack_.back().xformation; + entry.stencil_depth = xformation_stack_.back().stencil_depth; if (create_subpass) { entry.is_subpass = true; current_pass_ = GetCurrentPass().AddSubpass(std::make_unique()); current_pass_->SetTransformation(xformation_stack_.back().xformation); current_pass_->SetStencilDepth(xformation_stack_.back().stencil_depth); - } else { - entry.xformation = xformation_stack_.back().xformation; - entry.stencil_depth = xformation_stack_.back().stencil_depth; } xformation_stack_.emplace_back(std::move(entry)); } diff --git a/impeller/entity/contents/contents.cc b/impeller/entity/contents/contents.cc index 83fa02514ca84..33c489efb1be4 100644 --- a/impeller/entity/contents/contents.cc +++ b/impeller/entity/contents/contents.cc @@ -30,12 +30,7 @@ Contents::Contents() = default; Contents::~Contents() = default; Rect Contents::GetBounds(const Entity& entity) const { - const auto& transform = entity.GetTransformation(); - auto points = entity.GetPath().GetBoundingBox()->GetPoints(); - for (uint i = 0; i < points.size(); i++) { - points[i] = transform * points[i]; - } - return Rect::MakePointBounds({points.begin(), points.end()}); + return entity.GetTransformedPathBounds(); } std::optional Contents::RenderToTexture( diff --git a/impeller/entity/entity.cc b/impeller/entity/entity.cc index 417d61636d902..95d0e66528458 100644 --- a/impeller/entity/entity.cc +++ b/impeller/entity/entity.cc @@ -31,13 +31,11 @@ void Entity::SetPath(Path path) { } Rect Entity::GetTransformedPathBounds() const { - auto points = GetPath().GetBoundingBox()->GetPoints(); - - const auto& transform = GetTransformation(); - for (uint i = 0; i < points.size(); i++) { - points[i] = transform * points[i]; + auto bounds = GetPath().GetBoundingBox(); + if (!bounds.has_value()) { + return Rect(); } - return Rect::MakePointBounds({points.begin(), points.end()}); + return bounds->TransformBounds(GetTransformation()); } void Entity::SetAddsToCoverage(bool adds) { diff --git a/impeller/entity/entity_pass.cc b/impeller/entity/entity_pass.cc index 914da3ebedcd7..a0e4878ece11b 100644 --- a/impeller/entity/entity_pass.cc +++ b/impeller/entity/entity_pass.cc @@ -5,10 +5,12 @@ #include "impeller/entity/entity_pass.h" #include "flutter/fml/trace_event.h" +#include "impeller/base/validation.h" #include "impeller/entity/contents/content_context.h" #include "impeller/geometry/path_builder.h" #include "impeller/renderer/command_buffer.h" #include "impeller/renderer/render_pass.h" +#include "impeller/renderer/texture.h" namespace impeller { @@ -78,6 +80,9 @@ std::optional EntityPass::GetSubpassCoverage( if (!delegate_coverage.has_value()) { return entities_coverage; } + // The delegate coverage hint is in given in local space, so apply the subpass + // transformation. + delegate_coverage = delegate_coverage->TransformBounds(subpass.xformation_); // If the delegate tells us the coverage is smaller than it needs to be, then // great. OTOH, if the delegate is being wasteful, limit coverage to what is @@ -103,14 +108,23 @@ EntityPass* EntityPass::AddSubpass(std::unique_ptr pass) { } bool EntityPass::Render(ContentContext& renderer, - RenderPass& parent_pass) const { + RenderPass& parent_pass, + Point position) const { TRACE_EVENT0("impeller", "EntityPass::Render"); - for (const auto& entity : entities_) { + for (Entity entity : entities_) { + if (!position.IsZero()) { + // If the pass image is going to be rendered with a non-zero position, + // apply the negative translation to entity copies before rendering them + // so that they'll end up rendering to the correct on-screen position. + entity.SetTransformation(Matrix::MakeTranslation(Vector3(-position)) * + entity.GetTransformation()); + } if (!entity.Render(renderer, parent_pass)) { return false; } } + for (const auto& subpass : subpasses_) { if (delegate_->CanElide()) { continue; @@ -118,7 +132,7 @@ bool EntityPass::Render(ContentContext& renderer, if (delegate_->CanCollapseIntoParentPass()) { // Directly render into the parent pass and move on. - if (!subpass->Render(renderer, parent_pass)) { + if (!subpass->Render(renderer, parent_pass, position)) { return false; } continue; @@ -178,7 +192,7 @@ bool EntityPass::Render(ContentContext& renderer, sub_renderpass->SetLabel("OffscreenPass"); - if (!subpass->Render(renderer, *sub_renderpass)) { + if (!subpass->Render(renderer, *sub_renderpass, subpass_coverage->origin)) { return false; } @@ -191,10 +205,17 @@ bool EntityPass::Render(ContentContext& renderer, } Entity entity; - entity.SetPath(PathBuilder{}.AddRect(subpass_coverage.value()).TakePath()); + entity.SetPath(PathBuilder{} + .AddRect(Rect::MakeSize(subpass_coverage->size)) + .TakePath()); entity.SetContents(std::move(offscreen_texture_contents)); entity.SetStencilDepth(stencil_depth_); - entity.SetTransformation(xformation_); + // Once we have filters being applied for SaveLayer, some special sauce + // may be needed here (or in PaintPassDelegate) to ensure the filter + // parameters are transformed by the `xformation_` matrix, while continuing + // to apply only the subpass offset to the offscreen texture. + entity.SetTransformation( + Matrix::MakeTranslation(Vector3(subpass_coverage->origin - position))); if (!entity.Render(renderer, parent_pass)) { return false; } diff --git a/impeller/entity/entity_pass.h b/impeller/entity/entity_pass.h index f4dc5cf424d61..07a48e3530ef0 100644 --- a/impeller/entity/entity_pass.h +++ b/impeller/entity/entity_pass.h @@ -48,7 +48,9 @@ class EntityPass { EntityPass* GetSuperpass() const; - bool Render(ContentContext& renderer, RenderPass& parent_pass) const; + bool Render(ContentContext& renderer, + RenderPass& parent_pass, + Point position = Point()) const; void IterateAllEntities(std::function iterator); diff --git a/impeller/entity/entity_unittests.cc b/impeller/entity/entity_unittests.cc index c4804d0567700..31a68a1aea1f6 100644 --- a/impeller/entity/entity_unittests.cc +++ b/impeller/entity/entity_unittests.cc @@ -779,5 +779,13 @@ TEST_F(EntityTest, SetBlendMode) { ASSERT_EQ(entity.GetBlendMode(), Entity::BlendMode::kClear); } +TEST_F(EntityTest, ContentsGetBoundsForEmptyPathReturnsZero) { + Entity entity; + entity.SetContents(std::make_shared()); + entity.SetPath({}); + ASSERT_TRUE(entity.GetCoverage()->IsZero()); + ASSERT_TRUE(entity.GetTransformedPathBounds().IsZero()); +} + } // namespace testing } // namespace impeller diff --git a/impeller/geometry/geometry_unittests.cc b/impeller/geometry/geometry_unittests.cc index fbdd2c9c7d6b1..b43d1ffdf53c0 100644 --- a/impeller/geometry/geometry_unittests.cc +++ b/impeller/geometry/geometry_unittests.cc @@ -839,9 +839,16 @@ TEST(GeometryTest, RectGetPoints) { } TEST(GeometryTest, RectMakePointBounds) { - auto r = Rect::MakePointBounds({Point(1, 5), Point(4, -1), Point(0, 6)}); - auto expected = Rect(0, -1, 4, 7); - ASSERT_RECT_NEAR(r, expected); + { + Rect r = + Rect::MakePointBounds({Point(1, 5), Point(4, -1), Point(0, 6)}).value(); + auto expected = Rect(0, -1, 4, 7); + ASSERT_RECT_NEAR(r, expected); + } + { + std::optional r = Rect::MakePointBounds({}); + ASSERT_FALSE(r.has_value()); + } } TEST(GeometryTest, CubicPathComponentPolylineDoesNotIncludePointOne) { diff --git a/impeller/geometry/rect.h b/impeller/geometry/rect.h index dd12a5c69a697..514cb9c90ffff 100644 --- a/impeller/geometry/rect.h +++ b/impeller/geometry/rect.h @@ -9,6 +9,7 @@ #include #include +#include "impeller/geometry/matrix.h" #include "impeller/geometry/point.h" #include "impeller/geometry/scalar.h" #include "impeller/geometry/size.h" @@ -51,8 +52,11 @@ struct TRect { return TRect(0.0, 0.0, size.width, size.height); } - constexpr static TRect MakePointBounds( + constexpr static std::optional MakePointBounds( const std::vector>& points) { + if (points.empty()) { + return std::nullopt; + } auto left = points[0].x; auto top = points[0].y; auto right = points[0].x; @@ -143,6 +147,16 @@ struct TRect { TPoint(right, bottom)}; } + /// @brief Creates a new bounding box that contains this transformed + /// rectangle. + constexpr TRect TransformBounds(const Matrix& transform) const { + auto points = GetPoints(); + for (uint i = 0; i < points.size(); i++) { + points[i] = transform * points[i]; + } + return TRect::MakePointBounds({points.begin(), points.end()}).value(); + } + constexpr TRect Union(const TRect& o) const { auto this_ltrb = GetLTRB(); auto other_ltrb = o.GetLTRB(); From af7ab8049a369431cfffd3814dad01dd51c9c8bd Mon Sep 17 00:00:00 2001 From: Chinmay Garde Date: Tue, 5 Apr 2022 13:22:12 -0700 Subject: [PATCH 384/433] Wire up enhanced command buffer error reporting. (#113) --- .../backend/metal/command_buffer_mtl.mm | 117 +++++++++++++++++- impeller/renderer/renderer.cc | 2 + 2 files changed, 118 insertions(+), 1 deletion(-) diff --git a/impeller/renderer/backend/metal/command_buffer_mtl.mm b/impeller/renderer/backend/metal/command_buffer_mtl.mm index 672edfa0a36d2..1f2b8576ca4a1 100644 --- a/impeller/renderer/backend/metal/command_buffer_mtl.mm +++ b/impeller/renderer/backend/metal/command_buffer_mtl.mm @@ -8,8 +8,19 @@ namespace impeller { +id CreateCommandBuffer(id queue) { + if (@available(iOS 14.0, macOS 11.0, *)) { + auto desc = [[MTLCommandBufferDescriptor alloc] init]; + // Degrades CPU performance slightly but is well worth the cost for typical + // Impeller workloads. + desc.errorOptions = MTLCommandBufferErrorOptionEncoderExecutionStatus; + return [queue commandBufferWithDescriptor:desc]; + } + return [queue commandBuffer]; +} + CommandBufferMTL::CommandBufferMTL(id queue) - : buffer_([queue commandBuffer]) { + : buffer_(CreateCommandBuffer(queue)) { if (!buffer_) { return; } @@ -42,6 +53,109 @@ return CommandBufferMTL::Status::kError; } +API_AVAILABLE(ios(14.0), macos(11.0)) +NSString* MTLCommandEncoderErrorStateToString( + MTLCommandEncoderErrorState state) { + switch (state) { + case MTLCommandEncoderErrorStateUnknown: + return @"unknown"; + case MTLCommandEncoderErrorStateCompleted: + return @"completed"; + case MTLCommandEncoderErrorStateAffected: + return @"affected"; + case MTLCommandEncoderErrorStatePending: + return @"pending"; + case MTLCommandEncoderErrorStateFaulted: + return @"faulted"; + } + return @"unknown"; +} + +static NSString* MTLCommandBufferErrorToString(MTLCommandBufferError code) { + switch (code) { + case MTLCommandBufferErrorNone: + return @"none"; + case MTLCommandBufferErrorInternal: + return @"internal"; + case MTLCommandBufferErrorTimeout: + return @"timeout"; + case MTLCommandBufferErrorPageFault: + return @"page fault"; + case MTLCommandBufferErrorAccessRevoked: + return @"access revoked / blacklisted"; + case MTLCommandBufferErrorNotPermitted: + return @"not permitted"; + case MTLCommandBufferErrorOutOfMemory: + return @"out of memory"; + case MTLCommandBufferErrorInvalidResource: + return @"invalid resource"; + case MTLCommandBufferErrorMemoryless: + return @"memory-less"; + case MTLCommandBufferErrorStackOverflow: + return @"stack overflow"; + default: + break; + } + + return [NSString stringWithFormat:@" %zu", code]; +} + +static void LogMTLCommandBufferErrorIfPresent(id buffer) { + if (!buffer) { + return; + } + + if (buffer.status == MTLCommandBufferStatusCompleted) { + return; + } + + std::stringstream stream; + stream << ">>>>>>>" << std::endl; + stream << "Impeller command buffer could not be committed!" << std::endl; + + if (auto desc = buffer.error.localizedDescription) { + stream << desc.UTF8String << std::endl; + } + + if (buffer.error) { + stream << "Domain: " + << (buffer.error.domain.length > 0u ? buffer.error.domain.UTF8String + : "") + << " Code: " + << MTLCommandBufferErrorToString( + static_cast(buffer.error.code)) + .UTF8String + << std::endl; + } + + if (@available(iOS 14.0, macOS 11.0, *)) { + NSArray>* infos = + buffer.error.userInfo[MTLCommandBufferEncoderInfoErrorKey]; + for (id info in infos) { + stream << (info.label.length > 0u ? info.label.UTF8String + : "") + << ": " + << MTLCommandEncoderErrorStateToString(info.errorState).UTF8String + << std::endl; + + auto signposts = [info.debugSignposts componentsJoinedByString:@", "]; + if (signposts.length > 0u) { + stream << signposts.UTF8String << std::endl; + } + } + + for (id log in buffer.logs) { + auto desc = log.description; + if (desc.length > 0u) { + stream << desc.UTF8String << std::endl; + } + } + } + + stream << "<<<<<<<"; + VALIDATION_LOG << stream.str(); +} + bool CommandBufferMTL::SubmitCommands(CompletionCallback callback) { if (!buffer_) { // Already committed. This is caller error. @@ -53,6 +167,7 @@ if (callback) { [buffer_ addCompletedHandler:^(id buffer) { + LogMTLCommandBufferErrorIfPresent(buffer); callback(ToCommitResult(buffer.status)); }]; } diff --git a/impeller/renderer/renderer.cc b/impeller/renderer/renderer.cc index 5507314fe87ef..fab86fcd8d9f6 100644 --- a/impeller/renderer/renderer.cc +++ b/impeller/renderer/renderer.cc @@ -57,6 +57,8 @@ bool Renderer::Render(std::unique_ptr surface, return false; } + render_pass->SetLabel("Onscreen Render Pass"); + if (render_callback && !render_callback(*render_pass)) { return false; } From 6708397205534cea129a44e1322a8dd66ccd2466 Mon Sep 17 00:00:00 2001 From: Brandon DeRosier Date: Tue, 5 Apr 2022 13:36:56 -0700 Subject: [PATCH 385/433] Remove extra point from DrawRect; skip over duplicate contour points when generating polylines (#114) --- impeller/aiks/aiks_unittests.cc | 14 ++++++ .../display_list/display_list_unittests.cc | 46 ++++++++++++++++++ impeller/geometry/geometry_unittests.cc | 48 +++++++++++++++++-- impeller/geometry/path.cc | 24 ++++++++-- impeller/geometry/path_builder.cc | 3 +- 5 files changed, 125 insertions(+), 10 deletions(-) diff --git a/impeller/aiks/aiks_unittests.cc b/impeller/aiks/aiks_unittests.cc index 2b74ae21d6650..1a7b009d53e00 100644 --- a/impeller/aiks/aiks_unittests.cc +++ b/impeller/aiks/aiks_unittests.cc @@ -551,5 +551,19 @@ TEST_F(AiksTest, CoverageOriginShouldBeAccountedForInSubpasses) { ASSERT_TRUE(OpenPlaygroundHere(callback)); } +TEST_F(AiksTest, DrawRectStrokesRenderCorrectly) { + Canvas canvas; + Paint paint; + paint.color = Color::Red(); + paint.style = Paint::Style::kStroke; + paint.stroke_width = 10; + + canvas.Translate({100, 100}); + canvas.DrawPath(PathBuilder{}.AddRect(Rect::MakeSize({100, 100})).TakePath(), + paint); + + ASSERT_TRUE(OpenPlaygroundHere(canvas.EndRecordingAsPicture())); +} + } // namespace testing } // namespace impeller diff --git a/impeller/display_list/display_list_unittests.cc b/impeller/display_list/display_list_unittests.cc index a0b975f05e6d2..13160d39f5002 100644 --- a/impeller/display_list/display_list_unittests.cc +++ b/impeller/display_list/display_list_unittests.cc @@ -127,5 +127,51 @@ TEST_F(DisplayListTest, CanDrawArc) { ASSERT_TRUE(OpenPlaygroundHere(callback)); } +TEST_F(DisplayListTest, StrokedPathsDrawCorrectly) { + flutter::DisplayListBuilder builder; + builder.setColor(SK_ColorRED); + builder.setStyle(SkPaint::Style::kStroke_Style); + builder.setStrokeWidth(10); + + // Rectangle + builder.translate(100, 100); + builder.drawRect(SkRect::MakeSize({100, 100})); + + // Rounded rectangle + builder.translate(150, 0); + builder.drawRRect(SkRRect::MakeRectXY(SkRect::MakeSize({100, 50}), 10, 10)); + + // Double rounded rectangle + builder.translate(150, 0); + builder.drawDRRect( + SkRRect::MakeRectXY(SkRect::MakeSize({100, 50}), 10, 10), + SkRRect::MakeRectXY(SkRect::MakeXYWH(10, 10, 80, 30), 10, 10)); + + // Contour with duplicate join points + { + builder.translate(150, 0); + SkPath path; + path.lineTo({100, 0}); + path.lineTo({100, 0}); + path.lineTo({100, 100}); + builder.drawPath(path); + } + + // Contour with duplicate end points + { + builder.setStrokeCap(SkPaint::Cap::kRound_Cap); + builder.translate(150, 0); + SkPath path; + path.moveTo(0, 0); + path.lineTo({0, 0}); + path.lineTo({50, 50}); + path.lineTo({100, 0}); + path.lineTo({100, 0}); + builder.drawPath(path); + } + + ASSERT_TRUE(OpenPlaygroundHere(builder.Build())); +} + } // namespace testing } // namespace impeller diff --git a/impeller/geometry/geometry_unittests.cc b/impeller/geometry/geometry_unittests.cc index b43d1ffdf53c0..111dd541a7445 100644 --- a/impeller/geometry/geometry_unittests.cc +++ b/impeller/geometry/geometry_unittests.cc @@ -3,7 +3,9 @@ // found in the LICENSE file. #include "impeller/geometry/geometry_unittests.h" + #include + #include "flutter/testing/testing.h" #include "impeller/geometry/path.h" #include "impeller/geometry/path_builder.h" @@ -982,11 +984,49 @@ TEST(GeometryTest, PolylineGetContourPointBoundsReturnsCorrectRanges) { ASSERT_EQ(b2, 6u); } -TEST(GeometryTest, PolylineGetContourOutOfBoundsAborts) { +TEST(GeometryTest, PathAddRectPolylineHasCorrectContourData) { + Path::Polyline polyline = PathBuilder{} + .AddRect(Rect::MakeLTRB(50, 60, 70, 80)) + .TakePath() + .CreatePolyline(); + ASSERT_EQ(polyline.contours.size(), 1u); + ASSERT_TRUE(polyline.contours[0].is_closed); + ASSERT_EQ(polyline.contours[0].start_index, 0u); + ASSERT_EQ(polyline.points.size(), 5u); + ASSERT_EQ(polyline.points[0], Point(50, 60)); + ASSERT_EQ(polyline.points[1], Point(70, 60)); + ASSERT_EQ(polyline.points[2], Point(70, 80)); + ASSERT_EQ(polyline.points[3], Point(50, 80)); + ASSERT_EQ(polyline.points[4], Point(50, 60)); +} + +TEST(GeometryTest, PathPolylineDuplicatesAreRemovedForSameContour) { Path::Polyline polyline = - PathBuilder{}.AddLine({100, 100}, {200, 100}).TakePath().CreatePolyline(); - ASSERT_EQ(polyline.GetContourPointBounds(0), std::make_tuple(0u, 2u)); - ASSERT_EQ(polyline.GetContourPointBounds(14), std::make_tuple(2u, 2u)); + PathBuilder{} + .MoveTo({50, 50}) + .LineTo({50, 50}) // Insert duplicate at beginning of contour. + .LineTo({100, 50}) + .LineTo({100, 50}) // Insert duplicate at contour join. + .LineTo({100, 100}) + .Close() // Implicitly insert duplicate {50, 50} across contours. + .LineTo({0, 50}) + .LineTo({0, 100}) + .LineTo({0, 100}) // Insert duplicate at end of contour. + .TakePath() + .CreatePolyline(); + ASSERT_EQ(polyline.contours.size(), 2u); + ASSERT_EQ(polyline.contours[0].start_index, 0u); + ASSERT_TRUE(polyline.contours[0].is_closed); + ASSERT_EQ(polyline.contours[1].start_index, 4u); + ASSERT_FALSE(polyline.contours[1].is_closed); + ASSERT_EQ(polyline.points.size(), 7u); + ASSERT_EQ(polyline.points[0], Point(50, 50)); + ASSERT_EQ(polyline.points[1], Point(100, 50)); + ASSERT_EQ(polyline.points[2], Point(100, 100)); + ASSERT_EQ(polyline.points[3], Point(50, 50)); + ASSERT_EQ(polyline.points[4], Point(50, 50)); + ASSERT_EQ(polyline.points[5], Point(0, 50)); + ASSERT_EQ(polyline.points[6], Point(0, 100)); } } // namespace testing diff --git a/impeller/geometry/path.cc b/impeller/geometry/path.cc index d74a15fe4b882..47a674ee15bd5 100644 --- a/impeller/geometry/path.cc +++ b/impeller/geometry/path.cc @@ -224,12 +224,27 @@ bool Path::UpdateContourComponentAtIndex(size_t index, Path::Polyline Path::CreatePolyline( const SmoothingApproximation& approximation) const { Polyline polyline; - auto collect_points = [&polyline](const std::vector& collection) { + + std::optional previous_contour_point; + auto collect_points = [&polyline, &previous_contour_point]( + const std::vector& collection) { + if (collection.empty()) { + return; + } + polyline.points.reserve(polyline.points.size() + collection.size()); - polyline.points.insert(polyline.points.end(), collection.begin(), - collection.end()); + + for (const auto& point : collection) { + if (previous_contour_point.has_value() && + previous_contour_point.value() == point) { + // Slip over duplicate points in the same contour. + continue; + } + previous_contour_point = point; + polyline.points.push_back(point); + } }; - // for (const auto& component : components_) { + for (size_t component_i = 0; component_i < components_.size(); component_i++) { const auto& component = components_[component_i]; @@ -252,6 +267,7 @@ Path::Polyline Path::CreatePolyline( const auto& contour = contours_[component.index]; polyline.contours.push_back({.start_index = polyline.points.size(), .is_closed = contour.is_closed}); + previous_contour_point = std::nullopt; collect_points({contour.destination}); break; } diff --git a/impeller/geometry/path_builder.cc b/impeller/geometry/path_builder.cc index 9ddb29690060f..1456b7c745b7b 100644 --- a/impeller/geometry/path_builder.cc +++ b/impeller/geometry/path_builder.cc @@ -179,8 +179,7 @@ PathBuilder& PathBuilder::AddRect(Rect rect) { MoveTo(tl); prototype_.AddLinearComponent(tl, tr) .AddLinearComponent(tr, br) - .AddLinearComponent(br, bl) - .AddLinearComponent(bl, tl); + .AddLinearComponent(br, bl); Close(); return *this; From 2ff900bfa567acbecfdfdc8c04256dbae4ab5c8b Mon Sep 17 00:00:00 2001 From: Chinmay Garde Date: Tue, 5 Apr 2022 15:25:16 -0700 Subject: [PATCH 386/433] Explicitly wait for subpass buffers to be scheduled. (#115) This is pending the addition of explicit synchronization primitives to Impeller. It is unclear what the addition of such primitives would mean to the future OpenGL ES backend. --- impeller/renderer/backend/metal/command_buffer_mtl.mm | 1 + 1 file changed, 1 insertion(+) diff --git a/impeller/renderer/backend/metal/command_buffer_mtl.mm b/impeller/renderer/backend/metal/command_buffer_mtl.mm index 1f2b8576ca4a1..ebdd3d2d75242 100644 --- a/impeller/renderer/backend/metal/command_buffer_mtl.mm +++ b/impeller/renderer/backend/metal/command_buffer_mtl.mm @@ -173,6 +173,7 @@ static void LogMTLCommandBufferErrorIfPresent(id buffer) { } [buffer_ commit]; + [buffer_ waitUntilScheduled]; buffer_ = nil; return true; } From be9f4f773af7af8ea742d0731e456b06f224f21c Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 5 Apr 2022 16:32:56 -0700 Subject: [PATCH 387/433] Bump github/codeql-action from 2.1.6 to 2.1.7 (#116) Bumps [github/codeql-action](https://github.com/github/codeql-action) from 2.1.6 to 2.1.7. - [Release notes](https://github.com/github/codeql-action/releases) - [Changelog](https://github.com/github/codeql-action/blob/main/CHANGELOG.md) - [Commits](https://github.com/github/codeql-action/compare/28eead240834b314f7def40f6fcba65d100d99b1...0182a2c78c8a55b763909348834ed54d735ab3e2) --- updated-dependencies: - dependency-name: github/codeql-action dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- impeller/.github/workflows/scorecards-analysis.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/impeller/.github/workflows/scorecards-analysis.yml b/impeller/.github/workflows/scorecards-analysis.yml index 3e17223ef12cb..f0245bae97f1e 100644 --- a/impeller/.github/workflows/scorecards-analysis.yml +++ b/impeller/.github/workflows/scorecards-analysis.yml @@ -48,6 +48,6 @@ jobs: # Upload the results to GitHub's code scanning dashboard. - name: "Upload to code-scanning" - uses: github/codeql-action/upload-sarif@28eead240834b314f7def40f6fcba65d100d99b1 + uses: github/codeql-action/upload-sarif@0182a2c78c8a55b763909348834ed54d735ab3e2 with: sarif_file: results.sarif From ac82d238116947c6916b050bf2a3e5dd4408b436 Mon Sep 17 00:00:00 2001 From: Chinmay Garde Date: Tue, 5 Apr 2022 18:09:04 -0700 Subject: [PATCH 388/433] Add static thread safety analysis ready synchronization primitives. (#117) --- impeller/base/BUILD.gn | 6 +- impeller/base/base_unittests.cc | 72 ++++++++++++++ impeller/base/{base.h => thread.cc} | 10 +- impeller/base/thread.h | 93 +++++++++++++++++++ impeller/base/thread_safety.cc | 11 +++ impeller/base/thread_safety.h | 69 ++++++++++++++ .../renderer/backend/metal/render_pass_mtl.mm | 2 +- impeller/renderer/pipeline.cc | 2 +- impeller/renderer/pipeline_builder.h | 3 +- impeller/renderer/render_target.cc | 3 +- impeller/renderer/vertex_buffer_builder.h | 2 +- impeller/tools/impeller.gni | 5 + 12 files changed, 268 insertions(+), 10 deletions(-) create mode 100644 impeller/base/base_unittests.cc rename impeller/base/{base.h => thread.cc} (57%) create mode 100644 impeller/base/thread.h create mode 100644 impeller/base/thread_safety.cc create mode 100644 impeller/base/thread_safety.h diff --git a/impeller/base/BUILD.gn b/impeller/base/BUILD.gn index 939f50a8a3b47..1c4387834d79d 100644 --- a/impeller/base/BUILD.gn +++ b/impeller/base/BUILD.gn @@ -17,6 +17,10 @@ impeller_component("base") { "promise.h", "strings.cc", "strings.h", + "thread.cc", + "thread.h", + "thread_safety.cc", + "thread_safety.h", "validation.cc", "validation.h", ] @@ -26,7 +30,7 @@ impeller_component("base") { impeller_component("base_unittests") { testonly = true - sources = [] + sources = [ "base_unittests.cc" ] deps = [ ":base", "//flutter/testing", diff --git a/impeller/base/base_unittests.cc b/impeller/base/base_unittests.cc new file mode 100644 index 0000000000000..4dbff57fb2bab --- /dev/null +++ b/impeller/base/base_unittests.cc @@ -0,0 +1,72 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "flutter/testing/testing.h" +#include "impeller/base/thread.h" + +namespace impeller { +namespace testing { + +struct Foo { + Mutex mtx; + int a IPLR_GUARDED_BY(mtx); +}; + +struct RWFoo { + RWMutex mtx; + int a IPLR_GUARDED_BY(mtx); +}; + +TEST(ThreadTest, CanCreateMutex) { + Foo f = {}; + + // f.a = 100; <--- Static analysis error. + f.mtx.Lock(); + f.a = 100; + f.mtx.Unlock(); +} + +TEST(ThreadTest, CanCreateMutexLock) { + Foo f = {}; + + // f.a = 100; <--- Static analysis error. + auto a = Lock(f.mtx); + f.a = 100; +} + +TEST(ThreadTest, CanCreateRWMutex) { + RWFoo f = {}; + + // f.a = 100; <--- Static analysis error. + f.mtx.LockWriter(); + f.a = 100; + f.mtx.UnlockWriter(); + // int b = f.a; <--- Static analysis error. + f.mtx.LockReader(); + int b = f.a; + FML_ALLOW_UNUSED_LOCAL(b); + f.mtx.UnlockReader(); +} + +TEST(ThreadTest, CanCreateRWMutexLock) { + RWFoo f = {}; + + // f.a = 100; <--- Static analysis error. + { + auto write_lock = WriterLock{f.mtx}; + f.a = 100; + } + + // int b = f.a; <--- Static analysis error. + { + auto read_lock = ReaderLock(f.mtx); + int b = f.a; + FML_ALLOW_UNUSED_LOCAL(b); + } + + // f.mtx.UnlockReader(); <--- Static analysis error. +} + +} // namespace testing +} // namespace impeller diff --git a/impeller/base/base.h b/impeller/base/thread.cc similarity index 57% rename from impeller/base/base.h rename to impeller/base/thread.cc index a55a765f092fd..0403b19cd6a81 100644 --- a/impeller/base/base.h +++ b/impeller/base/thread.cc @@ -2,8 +2,10 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -#pragma once +#include "impeller/base/thread.h" -#include "impeller/base/promise.h" -#include "impeller/base/strings.h" -#include "impeller/base/validation.h" +namespace impeller { + +// + +} // namespace impeller diff --git a/impeller/base/thread.h b/impeller/base/thread.h new file mode 100644 index 0000000000000..da200bc8d9266 --- /dev/null +++ b/impeller/base/thread.h @@ -0,0 +1,93 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#pragma once + +#include +#include + +#include "flutter/fml/macros.h" +#include "flutter/fml/synchronization/shared_mutex.h" +#include "impeller/base/thread_safety.h" + +namespace impeller { + +class IPLR_CAPABILITY("mutex") Mutex { + public: + Mutex() = default; + + ~Mutex() = default; + + void Lock() IPLR_ACQUIRE() { mutex_.lock(); } + + void Unlock() IPLR_RELEASE() { mutex_.unlock(); } + + private: + std::mutex mutex_; + + FML_DISALLOW_COPY_ASSIGN_AND_MOVE(Mutex); +}; + +class IPLR_CAPABILITY("mutex") RWMutex { + public: + RWMutex() + : mutex_(std::unique_ptr(fml::SharedMutex::Create())) {} + + ~RWMutex() = default; + + void LockWriter() IPLR_ACQUIRE() { mutex_->Lock(); } + + void UnlockWriter() IPLR_RELEASE() { mutex_->Unlock(); } + + void LockReader() IPLR_ACQUIRE_SHARED() { mutex_->LockShared(); } + + void UnlockReader() IPLR_RELEASE_SHARED() { mutex_->UnlockShared(); } + + private: + std::unique_ptr mutex_; + + FML_DISALLOW_COPY_ASSIGN_AND_MOVE(RWMutex); +}; + +class IPLR_SCOPED_CAPABILITY Lock { + public: + Lock(Mutex& mutex) IPLR_ACQUIRE(mutex) : mutex_(mutex) { mutex_.Lock(); } + + ~Lock() IPLR_RELEASE() { mutex_.Unlock(); } + + private: + Mutex& mutex_; + + FML_DISALLOW_COPY_ASSIGN_AND_MOVE(Lock); +}; + +class IPLR_SCOPED_CAPABILITY ReaderLock { + public: + ReaderLock(RWMutex& mutex) IPLR_ACQUIRE_SHARED(mutex) : mutex_(mutex) { + mutex_.LockReader(); + } + + ~ReaderLock() IPLR_RELEASE() { mutex_.UnlockReader(); } + + private: + RWMutex& mutex_; + + FML_DISALLOW_COPY_ASSIGN_AND_MOVE(ReaderLock); +}; + +class IPLR_SCOPED_CAPABILITY WriterLock { + public: + WriterLock(RWMutex& mutex) IPLR_ACQUIRE(mutex) : mutex_(mutex) { + mutex_.LockWriter(); + } + + ~WriterLock() IPLR_RELEASE() { mutex_.UnlockWriter(); } + + private: + RWMutex& mutex_; + + FML_DISALLOW_COPY_ASSIGN_AND_MOVE(WriterLock); +}; + +} // namespace impeller diff --git a/impeller/base/thread_safety.cc b/impeller/base/thread_safety.cc new file mode 100644 index 0000000000000..e4188f669a4da --- /dev/null +++ b/impeller/base/thread_safety.cc @@ -0,0 +1,11 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "impeller/base/thread_safety.h" + +namespace impeller { + +// + +} // namespace impeller diff --git a/impeller/base/thread_safety.h b/impeller/base/thread_safety.h new file mode 100644 index 0000000000000..8d1f0aa02ccf2 --- /dev/null +++ b/impeller/base/thread_safety.h @@ -0,0 +1,69 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#pragma once + +#if defined(__clang__) +#define IPLR_THREAD_ANNOTATION_ATTRIBUTE__(x) __attribute__((x)) +#else +#define IPLR_THREAD_ANNOTATION_ATTRIBUTE__(x) // no-op +#endif + +#define IPLR_CAPABILITY(x) IPLR_THREAD_ANNOTATION_ATTRIBUTE__(capability(x)) + +#define IPLR_SCOPED_CAPABILITY \ + IPLR_THREAD_ANNOTATION_ATTRIBUTE__(scoped_lockable) + +#define IPLR_GUARDED_BY(x) IPLR_THREAD_ANNOTATION_ATTRIBUTE__(guarded_by(x)) + +#define IPLR_PT_GUARDED_BY(x) \ + IPLR_THREAD_ANNOTATION_ATTRIBUTE__(pt_guarded_by(x)) + +#define IPLR_ACQUIRED_BEFORE(...) \ + IPLR_THREAD_ANNOTATION_ATTRIBUTE__(acquired_before(__VA_ARGS__)) + +#define IPLR_ACQUIRED_AFTER(...) \ + IPLR_THREAD_ANNOTATION_ATTRIBUTE__(acquired_after(__VA_ARGS__)) + +#define IPLR_REQUIRES(...) \ + IPLR_THREAD_ANNOTATION_ATTRIBUTE__(requires_capability(__VA_ARGS__)) + +#define IPLR_REQUIRES_SHARED(...) \ + IPLR_THREAD_ANNOTATION_ATTRIBUTE__(requires_shared_capability(__VA_ARGS__)) + +#define IPLR_ACQUIRE(...) \ + IPLR_THREAD_ANNOTATION_ATTRIBUTE__(acquire_capability(__VA_ARGS__)) + +#define IPLR_ACQUIRE_SHARED(...) \ + IPLR_THREAD_ANNOTATION_ATTRIBUTE__(acquire_shared_capability(__VA_ARGS__)) + +#define IPLR_RELEASE(...) \ + IPLR_THREAD_ANNOTATION_ATTRIBUTE__(release_capability(__VA_ARGS__)) + +#define IPLR_RELEASE_SHARED(...) \ + IPLR_THREAD_ANNOTATION_ATTRIBUTE__(release_shared_capability(__VA_ARGS__)) + +#define IPLR_RELEASE_GENERIC(...) \ + IPLR_THREAD_ANNOTATION_ATTRIBUTE__(release_generic_capability(__VA_ARGS__)) + +#define IPLR_TRY_ACQUIRE(...) \ + IPLR_THREAD_ANNOTATION_ATTRIBUTE__(try_acquire_capability(__VA_ARGS__)) + +#define IPLR_TRY_ACQUIRE_SHARED(...) \ + IPLR_THREAD_ANNOTATION_ATTRIBUTE__(try_acquire_shared_capability(__VA_ARGS__)) + +#define IPLR_EXCLUDES(...) \ + IPLR_THREAD_ANNOTATION_ATTRIBUTE__(locks_excluded(__VA_ARGS__)) + +#define IPLR_ASSERT_CAPABILITY(x) \ + IPLR_THREAD_ANNOTATION_ATTRIBUTE__(assert_capability(x)) + +#define IPLR_ASSERT_SHARED_CAPABILITY(x) \ + IPLR_THREAD_ANNOTATION_ATTRIBUTE__(assert_shared_capability(x)) + +#define IPLR_RETURN_CAPABILITY(x) \ + IPLR_THREAD_ANNOTATION_ATTRIBUTE__(lock_returned(x)) + +#define IPLR_NO_THREAD_SAFETY_ANALYSIS \ + IPLR_THREAD_ANNOTATION_ATTRIBUTE__(no_thread_safety_analysis) diff --git a/impeller/renderer/backend/metal/render_pass_mtl.mm b/impeller/renderer/backend/metal/render_pass_mtl.mm index 895f1a15089b2..897c40a72bb4c 100644 --- a/impeller/renderer/backend/metal/render_pass_mtl.mm +++ b/impeller/renderer/backend/metal/render_pass_mtl.mm @@ -7,7 +7,7 @@ #include "flutter/fml/closure.h" #include "flutter/fml/logging.h" #include "flutter/fml/trace_event.h" -#include "impeller/base/base.h" +#include "impeller/base/backend_cast.h" #include "impeller/renderer/backend/metal/device_buffer_mtl.h" #include "impeller/renderer/backend/metal/formats_mtl.h" #include "impeller/renderer/backend/metal/pipeline_mtl.h" diff --git a/impeller/renderer/pipeline.cc b/impeller/renderer/pipeline.cc index ee0f574a69e50..2e6d5657d849f 100644 --- a/impeller/renderer/pipeline.cc +++ b/impeller/renderer/pipeline.cc @@ -4,7 +4,7 @@ #include "impeller/renderer/pipeline.h" -#include "impeller/base/base.h" +#include "impeller/base/promise.h" #include "impeller/renderer/context.h" #include "impeller/renderer/pipeline_library.h" diff --git a/impeller/renderer/pipeline_builder.h b/impeller/renderer/pipeline_builder.h index 090cd813b79b3..1cb8206d94f0a 100644 --- a/impeller/renderer/pipeline_builder.h +++ b/impeller/renderer/pipeline_builder.h @@ -6,7 +6,8 @@ #include "flutter/fml/logging.h" #include "flutter/fml/macros.h" -#include "impeller/base/base.h" +#include "impeller/base/strings.h" +#include "impeller/base/validation.h" #include "impeller/renderer/context.h" #include "impeller/renderer/formats.h" #include "impeller/renderer/pipeline_descriptor.h" diff --git a/impeller/renderer/render_target.cc b/impeller/renderer/render_target.cc index d7087f8cd0078..71eff0fb327ab 100644 --- a/impeller/renderer/render_target.cc +++ b/impeller/renderer/render_target.cc @@ -4,7 +4,8 @@ #include "impeller/renderer/render_target.h" -#include "impeller/base/base.h" +#include "impeller/base/strings.h" +#include "impeller/base/validation.h" #include "impeller/renderer/allocator.h" #include "impeller/renderer/context.h" #include "impeller/renderer/texture.h" diff --git a/impeller/renderer/vertex_buffer_builder.h b/impeller/renderer/vertex_buffer_builder.h index 1caac9032ae8f..7222537b91f31 100644 --- a/impeller/renderer/vertex_buffer_builder.h +++ b/impeller/renderer/vertex_buffer_builder.h @@ -9,7 +9,7 @@ #include #include "flutter/fml/macros.h" -#include "impeller/base/base.h" +#include "impeller/base/strings.h" #include "impeller/geometry/vector.h" #include "impeller/renderer/allocator.h" #include "impeller/renderer/device_buffer.h" diff --git a/impeller/tools/impeller.gni b/impeller/tools/impeller.gni index 717f6bd19aec9..eba266c881c8f 100644 --- a/impeller/tools/impeller.gni +++ b/impeller/tools/impeller.gni @@ -37,6 +37,11 @@ template("impeller_component") { public_configs += [ "//flutter/impeller:impeller_public_config" ] + if (!defined(invoker.cflags)) { + cflags = [] + } + cflags += [ "-Wthread-safety-analysis" ] + if (!defined(invoker.cflags_objc)) { cflags_objc = [] } From f732f90b8c1f709630cf383aed95255f9c40e29e Mon Sep 17 00:00:00 2001 From: Brandon DeRosier Date: Wed, 6 Apr 2022 18:39:19 -0700 Subject: [PATCH 389/433] Standardize around "coverage" terminology; make coverage optional everywhere (#118) --- impeller/entity/contents/contents.cc | 15 ++-- impeller/entity/contents/contents.h | 8 +-- .../contents/filters/filter_contents.cc | 42 +++++++---- .../entity/contents/filters/filter_contents.h | 2 +- .../entity/contents/filters/filter_input.cc | 6 +- .../entity/contents/filters/filter_input.h | 2 +- .../filters/gaussian_blur_filter_contents.cc | 69 ++++++++++++------- .../filters/gaussian_blur_filter_contents.h | 2 +- impeller/entity/contents/snapshot.cc | 17 +++-- impeller/entity/entity.cc | 8 ++- impeller/entity/entity.h | 2 +- impeller/entity/entity_unittests.cc | 11 +-- 12 files changed, 115 insertions(+), 69 deletions(-) diff --git a/impeller/entity/contents/contents.cc b/impeller/entity/contents/contents.cc index 33c489efb1be4..e18eb7d3e15c3 100644 --- a/impeller/entity/contents/contents.cc +++ b/impeller/entity/contents/contents.cc @@ -29,24 +29,27 @@ Contents::Contents() = default; Contents::~Contents() = default; -Rect Contents::GetBounds(const Entity& entity) const { - return entity.GetTransformedPathBounds(); +std::optional Contents::GetCoverage(const Entity& entity) const { + return entity.GetPathCoverage(); } std::optional Contents::RenderToTexture( const ContentContext& renderer, const Entity& entity) const { - auto bounds = GetBounds(entity); + auto bounds = GetCoverage(entity); + if (!bounds.has_value()) { + return std::nullopt; + } auto texture = renderer.MakeSubpass( - ISize::Ceil(bounds.size), + ISize::Ceil(bounds->size), [&contents = *this, &entity, &bounds](const ContentContext& renderer, RenderPass& pass) -> bool { Entity sub_entity; sub_entity.SetPath(entity.GetPath()); sub_entity.SetBlendMode(Entity::BlendMode::kSource); sub_entity.SetTransformation( - Matrix::MakeTranslation(Vector3(-bounds.origin)) * + Matrix::MakeTranslation(Vector3(-bounds->origin)) * entity.GetTransformation()); return contents.Render(renderer, sub_entity, pass); }); @@ -55,7 +58,7 @@ std::optional Contents::RenderToTexture( return std::nullopt; } - return Snapshot{.texture = texture, .position = bounds.origin}; + return Snapshot{.texture = texture, .position = bounds->origin}; } } // namespace impeller diff --git a/impeller/entity/contents/contents.h b/impeller/entity/contents/contents.h index ee9b0b0535fb5..d915891b501bb 100644 --- a/impeller/entity/contents/contents.h +++ b/impeller/entity/contents/contents.h @@ -36,13 +36,13 @@ class Contents { const Entity& entity, RenderPass& pass) const = 0; - /// @brief Get the bounding rectangle that this contents modifies in screen - /// space. - virtual Rect GetBounds(const Entity& entity) const; + /// @brief Get the screen space bounding rectangle that this contents affects. + virtual std::optional GetCoverage(const Entity& entity) const; /// @brief Render this contents to a texture, respecting the entity's /// transform, path, stencil depth, blend mode, etc. - /// The result texture size is always the size of `GetBounds(entity)`. + /// The result texture size is always the size of + /// `GetCoverage(entity)`. virtual std::optional RenderToTexture( const ContentContext& renderer, const Entity& entity) const; diff --git a/impeller/entity/contents/filters/filter_contents.cc b/impeller/entity/contents/filters/filter_contents.cc index 59a7149fc0021..4829403f2d43c 100644 --- a/impeller/entity/contents/filters/filter_contents.cc +++ b/impeller/entity/contents/filters/filter_contents.cc @@ -95,6 +95,11 @@ void FilterContents::SetInputs(FilterInput::Vector inputs) { bool FilterContents::Render(const ContentContext& renderer, const Entity& entity, RenderPass& pass) const { + auto filter_coverage = GetCoverage(entity); + if (!filter_coverage.has_value()) { + return true; + } + // Run the filter. auto maybe_snapshot = RenderToTexture(renderer, entity); @@ -110,49 +115,56 @@ bool FilterContents::Render(const ContentContext& renderer, contents->SetSourceRect(Rect::MakeSize(Size(snapshot.texture->GetSize()))); Entity e; - e.SetPath(PathBuilder{}.AddRect(GetBounds(entity)).GetCurrentPath()); + e.SetPath(PathBuilder{}.AddRect(filter_coverage.value()).GetCurrentPath()); e.SetBlendMode(entity.GetBlendMode()); e.SetStencilDepth(entity.GetStencilDepth()); return contents->Render(renderer, e, pass); } -Rect FilterContents::GetBounds(const Entity& entity) const { - // The default bounds of FilterContents is just the union of its inputs. - // FilterContents implementations may choose to increase the bounds in any - // direction, but it should never +std::optional FilterContents::GetCoverage(const Entity& entity) const { + // The default coverage of FilterContents is just the union of its inputs' + // coverage. FilterContents implementations may choose to adjust this + // coverage depending on the use case. if (inputs_.empty()) { - return Rect(); + return std::nullopt; } - Rect result = inputs_.front()->GetBounds(entity); - for (auto input_i = inputs_.begin() + 1; input_i < inputs_.end(); input_i++) { - result.Union(input_i->get()->GetBounds(entity)); + std::optional result; + for (const auto& input : inputs_) { + auto coverage = input->GetCoverage(entity); + if (!coverage.has_value()) { + continue; + } + if (!result.has_value()) { + result = coverage; + continue; + } + result = result->Union(result.value()); } - return result; } std::optional FilterContents::RenderToTexture( const ContentContext& renderer, const Entity& entity) const { - auto bounds = GetBounds(entity); - if (bounds.IsZero()) { + auto bounds = GetCoverage(entity); + if (!bounds.has_value() || bounds->IsEmpty()) { return std::nullopt; } // Render the filter into a new texture. auto texture = renderer.MakeSubpass( - ISize(GetBounds(entity).size), + ISize(bounds->size), [=](const ContentContext& renderer, RenderPass& pass) -> bool { - return RenderFilter(inputs_, renderer, entity, pass, bounds); + return RenderFilter(inputs_, renderer, entity, pass, bounds.value()); }); if (!texture) { return std::nullopt; } - return Snapshot{.texture = texture, .position = bounds.origin}; + return Snapshot{.texture = texture, .position = bounds->origin}; } } // namespace impeller diff --git a/impeller/entity/contents/filters/filter_contents.h b/impeller/entity/contents/filters/filter_contents.h index 63c60bcc44eac..275ea4d52b399 100644 --- a/impeller/entity/contents/filters/filter_contents.h +++ b/impeller/entity/contents/filters/filter_contents.h @@ -61,7 +61,7 @@ class FilterContents : public Contents { RenderPass& pass) const override; // |Contents| - Rect GetBounds(const Entity& entity) const override; + std::optional GetCoverage(const Entity& entity) const override; // |Contents| virtual std::optional RenderToTexture( diff --git a/impeller/entity/contents/filters/filter_input.cc b/impeller/entity/contents/filters/filter_input.cc index eaf75a982ab6a..c3585e6cdc1e1 100644 --- a/impeller/entity/contents/filters/filter_input.cc +++ b/impeller/entity/contents/filters/filter_input.cc @@ -30,17 +30,17 @@ FilterInput::Variant FilterInput::GetInput() const { return input_; } -Rect FilterInput::GetBounds(const Entity& entity) const { +std::optional FilterInput::GetCoverage(const Entity& entity) const { if (snapshot_) { return Rect(snapshot_->position, Size(snapshot_->texture->GetSize())); } if (auto contents = std::get_if>(&input_)) { - return contents->get()->GetBounds(entity); + return contents->get()->GetCoverage(entity); } if (auto texture = std::get_if>(&input_)) { - return entity.GetTransformedPathBounds(); + return entity.GetPathCoverage(); } FML_UNREACHABLE(); diff --git a/impeller/entity/contents/filters/filter_input.h b/impeller/entity/contents/filters/filter_input.h index b96e7203cc112..32b440a8f3456 100644 --- a/impeller/entity/contents/filters/filter_input.h +++ b/impeller/entity/contents/filters/filter_input.h @@ -42,7 +42,7 @@ class FilterInput final { Variant GetInput() const; - Rect GetBounds(const Entity& entity) const; + std::optional GetCoverage(const Entity& entity) const; std::optional GetSnapshot(const ContentContext& renderer, const Entity& entity) const; diff --git a/impeller/entity/contents/filters/gaussian_blur_filter_contents.cc b/impeller/entity/contents/filters/gaussian_blur_filter_contents.cc index 34441ddff9fd4..79a77b4d0a140 100644 --- a/impeller/entity/contents/filters/gaussian_blur_filter_contents.cc +++ b/impeller/entity/contents/filters/gaussian_blur_filter_contents.cc @@ -78,38 +78,55 @@ bool DirectionalGaussianBlurFilterContents::RenderFilter( auto& host_buffer = pass.GetTransientsBuffer(); auto input = inputs[0]->GetSnapshot(renderer, entity); - auto input_bounds = inputs[0]->GetBounds(entity); - auto filter_bounds = GetBounds(entity); + if (!input.has_value()) { + return true; + } + + auto input_bounds = inputs[0]->GetCoverage(entity); + if (!input_bounds.has_value() || input_bounds->IsEmpty()) { + return true; + } + auto filter_bounds = GetCoverage(entity); + if (!filter_bounds.has_value() || filter_bounds->IsEmpty()) { + FML_LOG(ERROR) << "The gaussian blur filter coverage is missing or empty " + "even though the filter's input has coverage."; + return false; + } auto transformed_blur = entity.GetTransformation().TransformDirection(blur_vector_); // LTRB Scalar uv[4] = { - (filter_bounds.GetLeft() - input_bounds.GetLeft()) / - input_bounds.size.width, - (filter_bounds.GetTop() - input_bounds.GetTop()) / - input_bounds.size.height, - 1 + (filter_bounds.GetRight() - input_bounds.GetRight()) / - input_bounds.size.width, - 1 + (filter_bounds.GetBottom() - input_bounds.GetBottom()) / - input_bounds.size.height, + (filter_bounds->GetLeft() - input_bounds->GetLeft()) / + input_bounds->size.width, + (filter_bounds->GetTop() - input_bounds->GetTop()) / + input_bounds->size.height, + 1 + (filter_bounds->GetRight() - input_bounds->GetRight()) / + input_bounds->size.width, + 1 + (filter_bounds->GetBottom() - input_bounds->GetBottom()) / + input_bounds->size.height, }; auto source = source_override_ ? source_override_ : inputs[0]; auto source_texture = source->GetSnapshot(renderer, entity); - auto source_bounds = source->GetBounds(entity); + auto source_bounds = source->GetCoverage(entity); + if (!source_texture.has_value() || !source_bounds.has_value() || + source_bounds->IsEmpty()) { + VALIDATION_LOG << "The gaussian blur source override has no coverage."; + return false; + } // LTRB Scalar uv_src[4] = { - (filter_bounds.GetLeft() - source_bounds.GetLeft()) / - source_bounds.size.width, - (filter_bounds.GetTop() - source_bounds.GetTop()) / - source_bounds.size.height, - 1 + (filter_bounds.GetRight() - source_bounds.GetRight()) / - source_bounds.size.width, - 1 + (filter_bounds.GetBottom() - source_bounds.GetBottom()) / - source_bounds.size.height, + (filter_bounds->GetLeft() - source_bounds->GetLeft()) / + source_bounds->size.width, + (filter_bounds->GetTop() - source_bounds->GetTop()) / + source_bounds->size.height, + 1 + (filter_bounds->GetRight() - source_bounds->GetRight()) / + source_bounds->size.width, + 1 + (filter_bounds->GetBottom() - source_bounds->GetBottom()) / + source_bounds->size.height, }; VertexBufferBuilder vtx_builder; @@ -124,7 +141,7 @@ bool DirectionalGaussianBlurFilterContents::RenderFilter( auto vtx_buffer = vtx_builder.CreateVertexBuffer(host_buffer); VS::FrameInfo frame_info; - frame_info.texture_size = Point(input_bounds.size); + frame_info.texture_size = Point(input_bounds->size); frame_info.blur_radius = transformed_blur.GetLength(); frame_info.blur_direction = transformed_blur.Normalize(); frame_info.src_factor = src_color_factor_; @@ -150,13 +167,17 @@ bool DirectionalGaussianBlurFilterContents::RenderFilter( return pass.AddCommand(cmd); } -Rect DirectionalGaussianBlurFilterContents::GetBounds( +std::optional DirectionalGaussianBlurFilterContents::GetCoverage( const Entity& entity) const { - auto bounds = FilterContents::GetBounds(entity); + auto bounds = FilterContents::GetCoverage(entity); + if (!bounds.has_value()) { + return std::nullopt; + } + auto transformed_blur = entity.GetTransformation().TransformDirection(blur_vector_).Abs(); - auto extent = bounds.size + transformed_blur * 2; - return Rect(bounds.origin - transformed_blur, Size(extent.x, extent.y)); + auto extent = bounds->size + transformed_blur * 2; + return Rect(bounds->origin - transformed_blur, Size(extent.x, extent.y)); } } // namespace impeller diff --git a/impeller/entity/contents/filters/gaussian_blur_filter_contents.h b/impeller/entity/contents/filters/gaussian_blur_filter_contents.h index 0b401d2cc0c43..1ec3a710a42db 100644 --- a/impeller/entity/contents/filters/gaussian_blur_filter_contents.h +++ b/impeller/entity/contents/filters/gaussian_blur_filter_contents.h @@ -25,7 +25,7 @@ class DirectionalGaussianBlurFilterContents final : public FilterContents { void SetSourceOverride(FilterInput::Ref alpha_mask); // |Contents| - Rect GetBounds(const Entity& entity) const override; + std::optional GetCoverage(const Entity& entity) const override; private: // |FilterContents| diff --git a/impeller/entity/contents/snapshot.cc b/impeller/entity/contents/snapshot.cc index 32dd34b5ef50b..76809d7cb5f0a 100644 --- a/impeller/entity/contents/snapshot.cc +++ b/impeller/entity/contents/snapshot.cc @@ -4,6 +4,8 @@ #include "impeller/entity/contents/snapshot.h" +#include + #include "impeller/entity/contents/content_context.h" #include "impeller/entity/contents/texture_contents.h" @@ -13,12 +15,15 @@ std::optional Snapshot::FromTransformedTexture( const ContentContext& renderer, const Entity& entity, std::shared_ptr texture) { - Rect bounds = entity.GetTransformedPathBounds(); + auto bounds = entity.GetPathCoverage(); + if (!bounds.has_value()) { + return std::nullopt; + } auto result = renderer.MakeSubpass( - ISize(bounds.size), - [&texture, &entity, bounds](const ContentContext& renderer, - RenderPass& pass) -> bool { + ISize(bounds->size), + [&texture, &entity, &bounds](const ContentContext& renderer, + RenderPass& pass) -> bool { TextureContents contents; contents.SetTexture(texture); contents.SetSourceRect(Rect::MakeSize(Size(texture->GetSize()))); @@ -26,7 +31,7 @@ std::optional Snapshot::FromTransformedTexture( sub_entity.SetPath(entity.GetPath()); sub_entity.SetBlendMode(Entity::BlendMode::kSource); sub_entity.SetTransformation( - Matrix::MakeTranslation(Vector3(-bounds.origin)) * + Matrix::MakeTranslation(Vector3(-bounds->origin)) * entity.GetTransformation()); return contents.Render(renderer, sub_entity, pass); }); @@ -34,7 +39,7 @@ std::optional Snapshot::FromTransformedTexture( return std::nullopt; } - return Snapshot{.texture = result, .position = bounds.origin}; + return Snapshot{.texture = result, .position = bounds->origin}; } } // namespace impeller diff --git a/impeller/entity/entity.cc b/impeller/entity/entity.cc index 95d0e66528458..5412df2a14c38 100644 --- a/impeller/entity/entity.cc +++ b/impeller/entity/entity.cc @@ -4,6 +4,8 @@ #include "impeller/entity/entity.h" +#include + #include "impeller/base/validation.h" #include "impeller/entity/contents/content_context.h" #include "impeller/renderer/render_pass.h" @@ -30,10 +32,10 @@ void Entity::SetPath(Path path) { path_ = std::move(path); } -Rect Entity::GetTransformedPathBounds() const { +std::optional Entity::GetPathCoverage() const { auto bounds = GetPath().GetBoundingBox(); if (!bounds.has_value()) { - return Rect(); + return std::nullopt; } return bounds->TransformBounds(GetTransformation()); } @@ -51,7 +53,7 @@ std::optional Entity::GetCoverage() const { return std::nullopt; } - return contents_->GetBounds(*this); + return contents_->GetCoverage(*this); } void Entity::SetContents(std::shared_ptr contents) { diff --git a/impeller/entity/entity.h b/impeller/entity/entity.h index 5c60967157185..b4979bb3ed2af 100644 --- a/impeller/entity/entity.h +++ b/impeller/entity/entity.h @@ -65,7 +65,7 @@ class Entity { void SetPath(Path path); - Rect GetTransformedPathBounds() const; + std::optional GetPathCoverage() const; void SetAddsToCoverage(bool adds); diff --git a/impeller/entity/entity_unittests.cc b/impeller/entity/entity_unittests.cc index 31a68a1aea1f6..ff1d07d278d4a 100644 --- a/impeller/entity/entity_unittests.cc +++ b/impeller/entity/entity_unittests.cc @@ -5,6 +5,7 @@ #include #include "flutter/testing/testing.h" +#include "impeller/entity/contents/filters/blend_filter_contents.h" #include "impeller/entity/contents/filters/filter_contents.h" #include "impeller/entity/contents/filters/filter_input.h" #include "impeller/entity/contents/solid_color_contents.h" @@ -760,7 +761,9 @@ TEST_F(EntityTest, GaussianBlurFilter) { // Renders a green bounding rect of the target filter. Entity bounds_entity; bounds_entity.SetPath( - PathBuilder{}.AddRect(target_contents->GetBounds(entity)).TakePath()); + PathBuilder{} + .AddRect(target_contents->GetCoverage(entity).value()) + .TakePath()); bounds_entity.SetContents( SolidColorContents::Make(bounds_color.Premultiply())); bounds_entity.SetTransformation(Matrix()); @@ -779,12 +782,12 @@ TEST_F(EntityTest, SetBlendMode) { ASSERT_EQ(entity.GetBlendMode(), Entity::BlendMode::kClear); } -TEST_F(EntityTest, ContentsGetBoundsForEmptyPathReturnsZero) { +TEST_F(EntityTest, ContentsGetBoundsForEmptyPathReturnsNullopt) { Entity entity; entity.SetContents(std::make_shared()); entity.SetPath({}); - ASSERT_TRUE(entity.GetCoverage()->IsZero()); - ASSERT_TRUE(entity.GetTransformedPathBounds().IsZero()); + ASSERT_FALSE(entity.GetCoverage().has_value()); + ASSERT_FALSE(entity.GetPathCoverage().has_value()); } } // namespace testing From 474ae886a4f80fa9fb9da42ce21c6b72c1d69241 Mon Sep 17 00:00:00 2001 From: Brandon DeRosier Date: Wed, 6 Apr 2022 21:27:21 -0700 Subject: [PATCH 390/433] Add explicit scaling methods for Point/Vector2 (#119) --- impeller/aiks/canvas.cc | 6 +++++- impeller/aiks/canvas.h | 2 ++ impeller/entity/contents/text_contents.cc | 2 +- impeller/geometry/geometry_unittests.cc | 25 ++++++++++++++++------- impeller/geometry/matrix.h | 4 ++++ 5 files changed, 30 insertions(+), 9 deletions(-) diff --git a/impeller/aiks/canvas.cc b/impeller/aiks/canvas.cc index 8a7e001a214a9..1949ad8a40d00 100644 --- a/impeller/aiks/canvas.cc +++ b/impeller/aiks/canvas.cc @@ -80,6 +80,10 @@ void Canvas::Translate(const Vector3& offset) { Concat(Matrix::MakeTranslation(offset)); } +void Canvas::Scale(const Vector2& scale) { + Concat(Matrix::MakeScale(scale)); +} + void Canvas::Scale(const Vector3& scale) { Concat(Matrix::MakeScale(scale)); } @@ -283,7 +287,7 @@ void Canvas::DrawTextFrame(TextFrame text_frame, Point position, Paint paint) { auto text_contents = std::make_shared(); text_contents->SetTextFrame(std::move(text_frame)); text_contents->SetGlyphAtlas(std::move(lazy_glyph_atlas)); - text_contents->SetColor(paint.color.Premultiply()); + text_contents->SetColor(paint.color); Entity entity; entity.SetTransformation(GetCurrentTransformation() * diff --git a/impeller/aiks/canvas.h b/impeller/aiks/canvas.h index 30eed9b6f07a5..71486def8e95e 100644 --- a/impeller/aiks/canvas.h +++ b/impeller/aiks/canvas.h @@ -52,6 +52,8 @@ class Canvas { void Translate(const Vector3& offset); + void Scale(const Vector2& scale); + void Scale(const Vector3& scale); void Skew(Scalar sx, Scalar sy); diff --git a/impeller/entity/contents/text_contents.cc b/impeller/entity/contents/text_contents.cc index 018cebb661eab..f64957f58f14d 100644 --- a/impeller/entity/contents/text_contents.cc +++ b/impeller/entity/contents/text_contents.cc @@ -80,7 +80,7 @@ bool TextContents::Render(const ContentContext& renderer, frame_info.atlas_size = Point{static_cast(atlas->GetTexture()->GetSize().width), static_cast(atlas->GetTexture()->GetSize().height)}; - frame_info.text_color = ToVector(color_); + frame_info.text_color = ToVector(color_.Premultiply()); VS::BindFrameInfo(cmd, pass.GetTransientsBuffer().EmplaceUniform(frame_info)); // Common fragment uniforms for all glyphs. diff --git a/impeller/geometry/geometry_unittests.cc b/impeller/geometry/geometry_unittests.cc index 111dd541a7445..705c54c4ca6cd 100644 --- a/impeller/geometry/geometry_unittests.cc +++ b/impeller/geometry/geometry_unittests.cc @@ -37,13 +37,24 @@ TEST(GeometryTest, RotationMatrix) { } TEST(GeometryTest, InvertMultMatrix) { - auto rotation = Matrix::MakeRotationZ(Radians{M_PI_4}); - auto invert = rotation.Invert(); - auto expect = Matrix{0.707, -0.707, 0, 0, // - 0.707, 0.707, 0, 0, // - 0, 0, 1, 0, // - 0, 0, 0, 1}; - ASSERT_MATRIX_NEAR(invert, expect); + { + auto rotation = Matrix::MakeRotationZ(Radians{M_PI_4}); + auto invert = rotation.Invert(); + auto expect = Matrix{0.707, -0.707, 0, 0, // + 0.707, 0.707, 0, 0, // + 0, 0, 1, 0, // + 0, 0, 0, 1}; + ASSERT_MATRIX_NEAR(invert, expect); + } + { + auto scale = Matrix::MakeScale(Vector2{2, 4}); + auto invert = scale.Invert(); + auto expect = Matrix{0.5, 0, 0, 0, // + 0, 0.25, 0, 0, // + 0, 0, 1, 0, // + 0, 0, 0, 1}; + ASSERT_MATRIX_NEAR(invert, expect); + } } TEST(GeometryTest, MutliplicationMatrix) { diff --git a/impeller/geometry/matrix.h b/impeller/geometry/matrix.h index 539ae84ae37bd..10051467f45dd 100644 --- a/impeller/geometry/matrix.h +++ b/impeller/geometry/matrix.h @@ -80,6 +80,10 @@ struct Matrix { // clang-format on } + static constexpr Matrix MakeScale(const Vector2& s) { + return MakeScale(Vector3(s.x, s.y, 1.0)); + } + static constexpr Matrix MakeSkew(Scalar sx, Scalar sy) { // clang-format off return Matrix(1.0, sy , 0.0, 0.0, From 51e87dd759d428566e6b0f76e56756009cb83d48 Mon Sep 17 00:00:00 2001 From: Brandon DeRosier Date: Wed, 6 Apr 2022 21:46:03 -0700 Subject: [PATCH 391/433] Ignore the stencil in the imgui pipeline (#120) --- impeller/playground/imgui/imgui_impl_impeller.cc | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/impeller/playground/imgui/imgui_impl_impeller.cc b/impeller/playground/imgui/imgui_impl_impeller.cc index 682ee9bb38163..b42696c4dc6e4 100644 --- a/impeller/playground/imgui/imgui_impl_impeller.cc +++ b/impeller/playground/imgui/imgui_impl_impeller.cc @@ -89,6 +89,13 @@ bool ImGui_ImplImpeller_Init(std::shared_ptr context) { impeller::ImguiRasterFragmentShader>:: MakeDefaultPipelineDescriptor(*context); desc->SetSampleCount(impeller::SampleCount::kCount4); + auto stencil = desc->GetFrontStencilAttachmentDescriptor(); + if (stencil.has_value()) { + stencil->stencil_compare = impeller::CompareFunction::kAlways; + stencil->depth_stencil_pass = impeller::StencilOperation::kKeep; + desc->SetStencilAttachmentDescriptors(stencil.value()); + } + bd->pipeline = context->GetPipelineLibrary()->GetRenderPipeline(std::move(desc)).get(); IM_ASSERT(bd->pipeline != nullptr && "Could not create ImGui pipeline."); From 479a609a4642802fa6383ea4760722a8a3ce3f21 Mon Sep 17 00:00:00 2001 From: Brandon DeRosier Date: Wed, 6 Apr 2022 22:25:18 -0700 Subject: [PATCH 392/433] Use the stencil buffer to avoid overdraw when rendering non-opaque solid strokes. (#121) Fixes https://github.com/flutter/flutter/issues/101330 --- impeller/aiks/aiks_unittests.cc | 96 +++++++++++++------ .../entity/contents/solid_color_contents.cc | 4 +- .../entity/contents/solid_stroke_contents.cc | 30 ++++-- impeller/entity/entity_unittests.cc | 6 +- 4 files changed, 94 insertions(+), 42 deletions(-) diff --git a/impeller/aiks/aiks_unittests.cc b/impeller/aiks/aiks_unittests.cc index 1a7b009d53e00..0df8ff3ce6cae 100644 --- a/impeller/aiks/aiks_unittests.cc +++ b/impeller/aiks/aiks_unittests.cc @@ -3,6 +3,7 @@ // found in the LICENSE file. #include + #include "flutter/testing/testing.h" #include "impeller/aiks/aiks_playground.h" #include "impeller/aiks/canvas.h" @@ -483,40 +484,77 @@ TEST_F(AiksTest, TransformMultipliesCorrectly) { // clang-format on } -TEST_F(AiksTest, PathsShouldHaveUniformAlpha) { +TEST_F(AiksTest, SolidStrokesRenderCorrectly) { // Compare with https://fiddle.skia.org/c/027392122bec8ac2b5d5de00a4b9bbe2 - Canvas canvas; - Paint paint; + bool first_frame = true; + auto callback = [&](AiksContext& renderer, RenderPass& pass) { + if (first_frame) { + first_frame = false; + ImGui::SetNextWindowSize({480, 100}); + ImGui::SetNextWindowPos({100, 550}); + } - paint.color = Color::White(); - canvas.DrawPaint(paint); + static Color color = Color::Black().WithAlpha(0.5); + static float scale = 3; + static bool add_circle_clip = true; - paint.color = Color::Black().WithAlpha(0.5); - paint.style = Paint::Style::kStroke; - paint.stroke_width = 10; + ImGui::Begin("Controls"); + ImGui::ColorEdit4("Color", reinterpret_cast(&color)); + ImGui::SliderFloat("Scale", &scale, 0, 6); + ImGui::Checkbox("Circle clip", &add_circle_clip); + ImGui::End(); + + Canvas canvas; + Paint paint; + + paint.color = Color::White(); + canvas.DrawPaint(paint); + + paint.color = color; + paint.style = Paint::Style::kStroke; + paint.stroke_width = 10; + + Path path = PathBuilder{} + .MoveTo({20, 20}) + .QuadraticCurveTo({60, 20}, {60, 60}) + .Close() + .MoveTo({60, 20}) + .QuadraticCurveTo({60, 60}, {20, 60}) + .TakePath(); - Path path = PathBuilder{} - .MoveTo({20, 20}) - .QuadraticCurveTo({60, 20}, {60, 60}) - .Close() - .MoveTo({60, 20}) - .QuadraticCurveTo({60, 60}, {20, 60}) - .TakePath(); - - canvas.Scale({3, 3}); - for (auto join : - {SolidStrokeContents::Join::kBevel, SolidStrokeContents::Join::kRound, - SolidStrokeContents::Join::kMiter}) { - paint.stroke_join = join; - for (auto cap : - {SolidStrokeContents::Cap::kButt, SolidStrokeContents::Cap::kSquare, - SolidStrokeContents::Cap::kRound}) { - paint.stroke_cap = cap; - canvas.DrawPath(path, paint); - canvas.Translate({80, 0}); + canvas.Scale(Vector2(scale, scale)); + + if (add_circle_clip) { + auto [handle_a, handle_b] = IMPELLER_PLAYGROUND_LINE( + Point(60, 300), Point(600, 300), 20, Color::Red(), Color::Red()); + + auto screen_to_canvas = canvas.GetCurrentTransformation().Invert(); + Point point_a = screen_to_canvas * handle_a; + Point point_b = screen_to_canvas * handle_b; + + Point middle = (point_a + point_b) / 2; + auto radius = point_a.GetDistance(middle); + canvas.ClipPath(PathBuilder{}.AddCircle(middle, radius).TakePath()); } - canvas.Translate({-240, 60}); - } + + for (auto join : + {SolidStrokeContents::Join::kBevel, SolidStrokeContents::Join::kRound, + SolidStrokeContents::Join::kMiter}) { + paint.stroke_join = join; + for (auto cap : + {SolidStrokeContents::Cap::kButt, SolidStrokeContents::Cap::kSquare, + SolidStrokeContents::Cap::kRound}) { + paint.stroke_cap = cap; + canvas.DrawPath(path, paint); + canvas.Translate({80, 0}); + } + canvas.Translate({-240, 60}); + } + + return renderer.Render(canvas.EndRecordingAsPicture(), pass); + }; + + ASSERT_TRUE(OpenPlaygroundHere(callback)); } TEST_F(AiksTest, CoverageOriginShouldBeAccountedForInSubpasses) { diff --git a/impeller/entity/contents/solid_color_contents.cc b/impeller/entity/contents/solid_color_contents.cc index 41b09b85842a9..c809f037ca1c3 100644 --- a/impeller/entity/contents/solid_color_contents.cc +++ b/impeller/entity/contents/solid_color_contents.cc @@ -53,7 +53,7 @@ bool SolidColorContents::Render(const ContentContext& renderer, using VS = SolidFillPipeline::VertexShader; Command cmd; - cmd.label = "SolidFill"; + cmd.label = "Solid Fill"; cmd.pipeline = renderer.GetSolidFillPipeline(OptionsFromPassAndEntity(pass, entity)); cmd.stencil_reference = entity.GetStencilDepth(); @@ -63,7 +63,7 @@ bool SolidColorContents::Render(const ContentContext& renderer, VS::FrameInfo frame_info; frame_info.mvp = Matrix::MakeOrthographic(pass.GetRenderTargetSize()) * entity.GetTransformation(); - frame_info.color = color_; + frame_info.color = color_.Premultiply(); VS::BindFrameInfo(cmd, pass.GetTransientsBuffer().EmplaceUniform(frame_info)); cmd.primitive_type = PrimitiveType::kTriangle; diff --git a/impeller/entity/contents/solid_stroke_contents.cc b/impeller/entity/contents/solid_stroke_contents.cc index abbc96687ce47..7e22f80e41f56 100644 --- a/impeller/entity/contents/solid_stroke_contents.cc +++ b/impeller/entity/contents/solid_stroke_contents.cc @@ -4,6 +4,7 @@ #include "solid_stroke_contents.h" +#include "impeller/entity/contents/clip_contents.h" #include "impeller/entity/contents/content_context.h" #include "impeller/entity/entity.h" #include "impeller/geometry/path_builder.h" @@ -78,12 +79,19 @@ static VertexBuffer CreateSolidStrokeVertices( vtx.vertex_position = polyline.points[contour_start_point_i - 1]; vtx.vertex_normal = {}; vtx.pen_down = 0.0; + // Append two transparent vertices when "picking up" the pen so that the + // triangle drawn when moving to the beginning of the new contour will + // have zero volume. This is necessary because strokes with a transparent + // color affect the stencil buffer to prevent overdraw. + vtx_builder.AppendVertex(vtx); vtx_builder.AppendVertex(vtx); vtx.vertex_position = polyline.points[contour_start_point_i]; - // Append two transparent vertices at the beginning of the new contour - // because it's a triangle strip. + // Append two vertices at the beginning of the new contour + // so that the next appended vertex will create a triangle with zero + // volume. vtx_builder.AppendVertex(vtx); + vtx.pen_down = 1.0; vtx_builder.AppendVertex(vtx); } @@ -147,14 +155,18 @@ bool SolidStrokeContents::Render(const ContentContext& renderer, entity.GetTransformation(); VS::StrokeInfo stroke_info; - stroke_info.color = color_; + stroke_info.color = color_.Premultiply(); stroke_info.size = stroke_size_; Command cmd; cmd.primitive_type = PrimitiveType::kTriangleStrip; - cmd.label = "SolidStroke"; - cmd.pipeline = - renderer.GetSolidStrokePipeline(OptionsFromPassAndEntity(pass, entity)); + cmd.label = "Solid Stroke"; + auto options = OptionsFromPassAndEntity(pass, entity); + if (!color_.IsOpaque()) { + options.stencil_compare = CompareFunction::kEqual; + options.stencil_operation = StencilOperation::kIncrementClamp; + } + cmd.pipeline = renderer.GetSolidStrokePipeline(options); cmd.stencil_reference = entity.GetStencilDepth(); auto smoothing = SmoothingApproximation( @@ -167,7 +179,11 @@ bool SolidStrokeContents::Render(const ContentContext& renderer, VS::BindStrokeInfo(cmd, pass.GetTransientsBuffer().EmplaceUniform(stroke_info)); - pass.AddCommand(std::move(cmd)); + pass.AddCommand(cmd); + + if (!color_.IsOpaque()) { + return ClipRestoreContents().Render(renderer, entity, pass); + } return true; } diff --git a/impeller/entity/entity_unittests.cc b/impeller/entity/entity_unittests.cc index ff1d07d278d4a..68db263d73384 100644 --- a/impeller/entity/entity_unittests.cc +++ b/impeller/entity/entity_unittests.cc @@ -752,8 +752,7 @@ TEST_F(EntityTest, GaussianBlurFilter) { // unfiltered input. Entity cover_entity; cover_entity.SetPath(PathBuilder{}.AddRect(rect).TakePath()); - cover_entity.SetContents( - SolidColorContents::Make(cover_color.Premultiply())); + cover_entity.SetContents(SolidColorContents::Make(cover_color)); cover_entity.SetTransformation(ctm); cover_entity.Render(context, pass); @@ -764,8 +763,7 @@ TEST_F(EntityTest, GaussianBlurFilter) { PathBuilder{} .AddRect(target_contents->GetCoverage(entity).value()) .TakePath()); - bounds_entity.SetContents( - SolidColorContents::Make(bounds_color.Premultiply())); + bounds_entity.SetContents(SolidColorContents::Make(bounds_color)); bounds_entity.SetTransformation(Matrix()); bounds_entity.Render(context, pass); From 38d75446be048abcd6b9359774eea40a9ca22283 Mon Sep 17 00:00:00 2001 From: Brandon DeRosier Date: Thu, 7 Apr 2022 11:23:02 -0700 Subject: [PATCH 393/433] Remove base.h from base/BUILD (#122) --- impeller/base/BUILD.gn | 1 - 1 file changed, 1 deletion(-) diff --git a/impeller/base/BUILD.gn b/impeller/base/BUILD.gn index 1c4387834d79d..5bb267d2a0127 100644 --- a/impeller/base/BUILD.gn +++ b/impeller/base/BUILD.gn @@ -9,7 +9,6 @@ impeller_component("base") { "allocation.cc", "allocation.h", "backend_cast.h", - "base.h", "comparable.cc", "comparable.h", "config.h", From 2f5a2908cb5b927b4d1fbe556bcc02418619b6f4 Mon Sep 17 00:00:00 2001 From: Brandon DeRosier Date: Thu, 7 Apr 2022 13:58:23 -0700 Subject: [PATCH 394/433] Standardize around blur sigma<->radius conversion factor (#123) --- .../contents/filters/filter_contents.cc | 18 +++--- .../entity/contents/filters/filter_contents.h | 58 ++++++++++++++++++- .../filters/gaussian_blur_filter_contents.cc | 38 ++++++++---- .../filters/gaussian_blur_filter_contents.h | 8 ++- impeller/entity/entity_unittests.cc | 3 +- impeller/entity/shaders/gaussian_blur.frag | 22 +++---- impeller/entity/shaders/gaussian_blur.vert | 3 + 7 files changed, 114 insertions(+), 36 deletions(-) diff --git a/impeller/entity/contents/filters/filter_contents.cc b/impeller/entity/contents/filters/filter_contents.cc index 4829403f2d43c..adb25ff305bff 100644 --- a/impeller/entity/contents/filters/filter_contents.cc +++ b/impeller/entity/contents/filters/filter_contents.cc @@ -61,12 +61,14 @@ std::shared_ptr FilterContents::MakeBlend( std::shared_ptr FilterContents::MakeDirectionalGaussianBlur( FilterInput::Ref input, - Vector2 blur_vector, + Sigma sigma, + Vector2 direction, BlurStyle blur_style, FilterInput::Ref source_override) { auto blur = std::make_shared(); blur->SetInputs({input}); - blur->SetBlurVector(blur_vector); + blur->SetSigma(sigma); + blur->SetDirection(direction); blur->SetBlurStyle(blur_style); blur->SetSourceOverride(source_override); return blur; @@ -74,13 +76,13 @@ std::shared_ptr FilterContents::MakeDirectionalGaussianBlur( std::shared_ptr FilterContents::MakeGaussianBlur( FilterInput::Ref input, - Scalar sigma_x, - Scalar sigma_y, + Sigma sigma_x, + Sigma sigma_y, BlurStyle blur_style) { - auto x_blur = - MakeDirectionalGaussianBlur(input, Point(sigma_x, 0), BlurStyle::kNormal); - auto y_blur = MakeDirectionalGaussianBlur( - FilterInput::Make(x_blur), Point(0, sigma_y), blur_style, input); + auto x_blur = MakeDirectionalGaussianBlur(input, sigma_x, Point(1, 0), + BlurStyle::kNormal); + auto y_blur = MakeDirectionalGaussianBlur(FilterInput::Make(x_blur), sigma_y, + Point(0, 1), blur_style, input); return y_blur; } diff --git a/impeller/entity/contents/filters/filter_contents.h b/impeller/entity/contents/filters/filter_contents.h index 275ea4d52b399..25bae0f0a5310 100644 --- a/impeller/entity/contents/filters/filter_contents.h +++ b/impeller/entity/contents/filters/filter_contents.h @@ -29,19 +29,71 @@ class FilterContents : public Contents { kInner, }; + /// For filters that use a Gaussian distribution, this is the `Radius` size to + /// use per `Sigma` (standard deviation). + /// + /// This cutoff (sqrt(3)) is taken from Flutter and Skia (where the + /// multiplicative inverse of this constant is used (1 / sqrt(3)): + /// https://api.flutter.dev/flutter/dart-ui/Shadow/convertRadiusToSigma.html + /// + /// In practice, this value is somewhat arbitrary, and can be changed to a + /// higher number to integrate more of the Gaussian function and render higher + /// quality blurs (with exponentially diminishing returns for the same sigma + /// input). Making this value any lower results in a noticable loss of + /// quality in the blur. + constexpr static float kKernelRadiusPerSigma = 1.73205080757; + + struct Radius; + + /// @brief In filters that use Gaussian distributions, "sigma" is a size of + /// one standard deviation in terms of the local space pixel grid of + /// the filter input. In other words, this determines how wide the + /// distribution stretches. + struct Sigma { + Scalar sigma = 0.0; + + constexpr Sigma() = default; + + explicit constexpr Sigma(Scalar p_sigma) : sigma(p_sigma) {} + + constexpr operator Radius() const { + return Radius{sigma > 0.5f ? (sigma - 0.5f) * kKernelRadiusPerSigma + : 0.0f}; + }; + }; + + /// @brief For convolution filters, the "radius" is the size of the + /// convolution kernel to use on the local space pixel grid of the + /// filter input. + /// For Gaussian blur kernels, this unit has a linear + /// relationship with `Sigma`. See `kKernelRadiusPerSigma` for + /// details on how this relationship works. + struct Radius { + Scalar radius = 0.0; + + constexpr Radius() = default; + + explicit constexpr Radius(Scalar p_radius) : radius(p_radius) {} + + constexpr operator Sigma() const { + return Sigma{radius > 0 ? radius / kKernelRadiusPerSigma + 0.5f : 0.0f}; + }; + }; + static std::shared_ptr MakeBlend(Entity::BlendMode blend_mode, FilterInput::Vector inputs); static std::shared_ptr MakeDirectionalGaussianBlur( FilterInput::Ref input, - Vector2 blur_vector, + Sigma sigma, + Vector2 direction, BlurStyle blur_style = BlurStyle::kNormal, FilterInput::Ref alpha_mask = nullptr); static std::shared_ptr MakeGaussianBlur( FilterInput::Ref input, - Scalar sigma_x, - Scalar sigma_y, + Sigma sigma_x, + Sigma sigma_y, BlurStyle blur_style = BlurStyle::kNormal); FilterContents(); diff --git a/impeller/entity/contents/filters/gaussian_blur_filter_contents.cc b/impeller/entity/contents/filters/gaussian_blur_filter_contents.cc index 79a77b4d0a140..0b2c19770dc0f 100644 --- a/impeller/entity/contents/filters/gaussian_blur_filter_contents.cc +++ b/impeller/entity/contents/filters/gaussian_blur_filter_contents.cc @@ -22,12 +22,23 @@ DirectionalGaussianBlurFilterContents::DirectionalGaussianBlurFilterContents() = DirectionalGaussianBlurFilterContents:: ~DirectionalGaussianBlurFilterContents() = default; -void DirectionalGaussianBlurFilterContents::SetBlurVector(Vector2 blur_vector) { - if (blur_vector.GetLengthSquared() < kEhCloseEnough) { - blur_vector_ = Vector2(0, kEhCloseEnough); +void DirectionalGaussianBlurFilterContents::SetSigma(Sigma sigma) { + if (sigma.sigma < kEhCloseEnough) { + // This cutoff is an implementation detail of the blur that's tied to the + // fragment shader. When the blur is set to 0, having a value slightly above + // zero makes the shader do 1 finite sample to pass the image through with + // no blur (while retaining correct alpha mask behavior). + blur_sigma_ = Sigma{kEhCloseEnough}; return; } - blur_vector_ = blur_vector; + blur_sigma_ = sigma; +} + +void DirectionalGaussianBlurFilterContents::SetDirection(Vector2 direction) { + blur_direction_ = direction.Normalize(); + if (blur_direction_.IsZero()) { + blur_direction_ = Vector2(0, 1); + } } void DirectionalGaussianBlurFilterContents::SetBlurStyle(BlurStyle blur_style) { @@ -93,8 +104,8 @@ bool DirectionalGaussianBlurFilterContents::RenderFilter( return false; } - auto transformed_blur = - entity.GetTransformation().TransformDirection(blur_vector_); + auto transformed_blur = entity.GetTransformation().TransformDirection( + blur_direction_ * blur_sigma_.sigma); // LTRB Scalar uv[4] = { @@ -142,7 +153,8 @@ bool DirectionalGaussianBlurFilterContents::RenderFilter( VS::FrameInfo frame_info; frame_info.texture_size = Point(input_bounds->size); - frame_info.blur_radius = transformed_blur.GetLength(); + frame_info.blur_sigma = transformed_blur.GetLength(); + frame_info.blur_radius = Radius{Sigma{frame_info.blur_sigma}}.radius; frame_info.blur_direction = transformed_blur.Normalize(); frame_info.src_factor = src_color_factor_; frame_info.inner_blur_factor = inner_blur_factor_; @@ -174,10 +186,14 @@ std::optional DirectionalGaussianBlurFilterContents::GetCoverage( return std::nullopt; } - auto transformed_blur = - entity.GetTransformation().TransformDirection(blur_vector_).Abs(); - auto extent = bounds->size + transformed_blur * 2; - return Rect(bounds->origin - transformed_blur, Size(extent.x, extent.y)); + auto transformed_blur_vector = + entity.GetTransformation() + .TransformDirection(blur_direction_ * + ceil(Radius{blur_sigma_}.radius)) + .Abs(); + auto extent = bounds->size + transformed_blur_vector * 2; + return Rect(bounds->origin - transformed_blur_vector, + Size(extent.x, extent.y)); } } // namespace impeller diff --git a/impeller/entity/contents/filters/gaussian_blur_filter_contents.h b/impeller/entity/contents/filters/gaussian_blur_filter_contents.h index 1ec3a710a42db..098cab0f9de5e 100644 --- a/impeller/entity/contents/filters/gaussian_blur_filter_contents.h +++ b/impeller/entity/contents/filters/gaussian_blur_filter_contents.h @@ -18,7 +18,9 @@ class DirectionalGaussianBlurFilterContents final : public FilterContents { ~DirectionalGaussianBlurFilterContents() override; - void SetBlurVector(Vector2 blur_vector); + void SetSigma(Sigma sigma); + + void SetDirection(Vector2 direction); void SetBlurStyle(BlurStyle blur_style); @@ -34,8 +36,8 @@ class DirectionalGaussianBlurFilterContents final : public FilterContents { const Entity& entity, RenderPass& pass, const Rect& bounds) const override; - - Vector2 blur_vector_; + Sigma blur_sigma_; + Vector2 blur_direction_; BlurStyle blur_style_ = BlurStyle::kNormal; bool src_color_factor_ = false; bool inner_blur_factor_ = true; diff --git a/impeller/entity/entity_unittests.cc b/impeller/entity/entity_unittests.cc index 68db263d73384..b3904fba06bd8 100644 --- a/impeller/entity/entity_unittests.cc +++ b/impeller/entity/entity_unittests.cc @@ -729,7 +729,8 @@ TEST_F(EntityTest, GaussianBlurFilter) { Entity::BlendMode::kPlus, FilterInput::Make({boston, bridge, bridge})); auto blur = FilterContents::MakeGaussianBlur( - FilterInput::Make(blend), blur_amount[0], blur_amount[1], + FilterInput::Make(blend), FilterContents::Sigma{blur_amount[0]}, + FilterContents::Sigma{blur_amount[1]}, blur_styles[selected_blur_style]); ISize input_size = boston->GetSize(); diff --git a/impeller/entity/shaders/gaussian_blur.frag b/impeller/entity/shaders/gaussian_blur.frag index 939ecf9e20c78..501acb3df78cb 100644 --- a/impeller/entity/shaders/gaussian_blur.frag +++ b/impeller/entity/shaders/gaussian_blur.frag @@ -16,6 +16,7 @@ in vec2 v_texture_coords; in vec2 v_src_texture_coords; in vec2 v_texture_size; in vec2 v_blur_direction; +in float v_blur_sigma; in float v_blur_radius; in float v_src_factor; in float v_inner_blur_factor; @@ -23,12 +24,11 @@ in float v_outer_blur_factor; out vec4 frag_color; -const float kTwoPi = 6.283185307179586; +const float kSqrtTwoPi = 2.50662827463; float Gaussian(float x) { - float stddev = v_blur_radius * 0.5; - float xnorm = x / stddev; - return exp(-0.5 * xnorm * xnorm) / (kTwoPi * stddev * stddev); + float variance = v_blur_sigma * v_blur_sigma; + return exp(-0.5 * x * x / variance) / (kSqrtTwoPi * v_blur_sigma); } // Emulate SamplerAddressMode::ClampToBorder. @@ -38,17 +38,19 @@ vec4 SampleWithBorder(sampler2D tex, vec2 uv) { } void main() { - vec4 total = vec4(0); - float total_gaussian = 0; + vec4 total_color = vec4(0); + float gaussian_integral = 0; vec2 blur_uv_offset = v_blur_direction / v_texture_size; + for (float i = -v_blur_radius; i <= v_blur_radius; i++) { float gaussian = Gaussian(i); - total_gaussian += gaussian; - total += gaussian * SampleWithBorder(texture_sampler, - v_texture_coords + blur_uv_offset * i); + gaussian_integral += gaussian; + total_color += + gaussian * SampleWithBorder(texture_sampler, + v_texture_coords + blur_uv_offset * i); } - vec4 blur_color = total / total_gaussian; + vec4 blur_color = total_color / gaussian_integral; vec4 src_color = SampleWithBorder(alpha_mask_sampler, v_src_texture_coords); float blur_factor = v_inner_blur_factor * float(src_color.a > 0) + diff --git a/impeller/entity/shaders/gaussian_blur.vert b/impeller/entity/shaders/gaussian_blur.vert index a358a2b5719be..1a66dc0eb5a86 100644 --- a/impeller/entity/shaders/gaussian_blur.vert +++ b/impeller/entity/shaders/gaussian_blur.vert @@ -7,6 +7,7 @@ uniform FrameInfo { vec2 texture_size; vec2 blur_direction; + float blur_sigma; float blur_radius; float src_factor; @@ -23,6 +24,7 @@ out vec2 v_texture_coords; out vec2 v_src_texture_coords; out vec2 v_texture_size; out vec2 v_blur_direction; +out float v_blur_sigma; out float v_blur_radius; out float v_src_factor; out float v_inner_blur_factor; @@ -34,6 +36,7 @@ void main() { v_src_texture_coords = src_texture_coords; v_texture_size = frame_info.texture_size; v_blur_direction = frame_info.blur_direction; + v_blur_sigma = frame_info.blur_sigma; v_blur_radius = frame_info.blur_radius; v_src_factor = frame_info.src_factor; v_inner_blur_factor = frame_info.inner_blur_factor; From cdf4e844e10664f9acbcbe76e00957517c0396c8 Mon Sep 17 00:00:00 2001 From: Dan Field Date: Thu, 7 Apr 2022 20:38:56 -0700 Subject: [PATCH 395/433] No newline in depfile output, which seems to confuse ninja (#125) --- impeller/compiler/compiler.cc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/impeller/compiler/compiler.cc b/impeller/compiler/compiler.cc index b6ac4b614d0f9..7ae15aab5806d 100644 --- a/impeller/compiler/compiler.cc +++ b/impeller/compiler/compiler.cc @@ -458,7 +458,7 @@ std::unique_ptr Compiler::CreateDepfileContents( const auto dependencies = GetDependencyNames(" "); std::stringstream stream; - stream << targets << ":\n\t" << dependencies << "\n"; + stream << targets << ": " << dependencies << "\n"; auto contents = std::make_shared(stream.str()); return std::make_unique( From 0b278bcb7d92c41e3aec67a11f5dcec4eaff1b96 Mon Sep 17 00:00:00 2001 From: Brandon DeRosier Date: Fri, 8 Apr 2022 18:01:53 -0700 Subject: [PATCH 396/433] Fix Path::GetBoundingBox crash for cubics with no local min/max (#126) --- impeller/geometry/geometry_unittests.cc | 11 +++++++++++ impeller/geometry/path.cc | 4 ++++ impeller/geometry/path_component.cc | 2 +- 3 files changed, 16 insertions(+), 1 deletion(-) diff --git a/impeller/geometry/geometry_unittests.cc b/impeller/geometry/geometry_unittests.cc index 705c54c4ca6cd..de6902c9d9602 100644 --- a/impeller/geometry/geometry_unittests.cc +++ b/impeller/geometry/geometry_unittests.cc @@ -315,6 +315,17 @@ TEST(GeometryTest, BoundingBoxOfCompositePathIsCorrect) { ASSERT_RECT_NEAR(actual.value(), expected); } +TEST(GeometryTest, PathGetBoundingBoxForCubicWithNoDerivativeRootsIsCorrect) { + PathBuilder builder; + // Straight diagonal line. + builder.AddCubicCurve({0, 1}, {2, 3}, {4, 5}, {6, 7}); + auto path = builder.TakePath(); + auto actual = path.GetBoundingBox(); + auto expected = Rect::MakeLTRB(0, 1, 6, 7); + ASSERT_TRUE(actual.has_value()); + ASSERT_RECT_NEAR(actual.value(), expected); +} + TEST(GeometryTest, CanGenerateMipCounts) { ASSERT_EQ((Size{128, 128}.MipCount()), 7u); ASSERT_EQ((Size{128, 256}.MipCount()), 8u); diff --git a/impeller/geometry/path.cc b/impeller/geometry/path.cc index 47a674ee15bd5..b71996e76d02f 100644 --- a/impeller/geometry/path.cc +++ b/impeller/geometry/path.cc @@ -322,6 +322,10 @@ std::optional> Path::GetMinMaxCoveragePoints() const { clamp(cubic.Extrema()); } + if (!min.has_value() || !max.has_value()) { + return std::nullopt; + } + return std::make_pair(min.value(), max.value()); } diff --git a/impeller/geometry/path_component.cc b/impeller/geometry/path_component.cc index 70f83c3f1a18c..b880f7000606a 100644 --- a/impeller/geometry/path_component.cc +++ b/impeller/geometry/path_component.cc @@ -408,7 +408,7 @@ std::vector CubicPathComponent::Extrema() const { CubicPathBoundingPopulateValues(values, p1.x, cp1.x, cp2.x, p2.x); CubicPathBoundingPopulateValues(values, p1.y, cp1.y, cp2.y, p2.y); - std::vector points; + std::vector points = {p1, p2}; for (const auto& value : values) { points.emplace_back(Solve(value)); From 78afffd43854ec24efcf9004ac946bf604156bb3 Mon Sep 17 00:00:00 2001 From: Brandon DeRosier Date: Fri, 8 Apr 2022 18:15:39 -0700 Subject: [PATCH 397/433] Add solid stroke coverage override (#127) --- .../entity/contents/solid_stroke_contents.cc | 24 +++ .../entity/contents/solid_stroke_contents.h | 3 + impeller/entity/entity_unittests.cc | 162 ++++++++++++------ 3 files changed, 136 insertions(+), 53 deletions(-) diff --git a/impeller/entity/contents/solid_stroke_contents.cc b/impeller/entity/contents/solid_stroke_contents.cc index 7e22f80e41f56..cee7da91438bb 100644 --- a/impeller/entity/contents/solid_stroke_contents.cc +++ b/impeller/entity/contents/solid_stroke_contents.cc @@ -4,6 +4,8 @@ #include "solid_stroke_contents.h" +#include + #include "impeller/entity/contents/clip_contents.h" #include "impeller/entity/contents/content_context.h" #include "impeller/entity/entity.h" @@ -27,6 +29,28 @@ const Color& SolidStrokeContents::GetColor() const { return color_; } +std::optional SolidStrokeContents::GetCoverage( + const Entity& entity) const { + auto path_coverage = entity.GetPathCoverage(); + if (!path_coverage.has_value()) { + return std::nullopt; + } + + Scalar max_radius = 0.5; + if (cap_ == Cap::kSquare) { + max_radius = max_radius * kSqrt2; + } + if (join_ == Join::kMiter) { + max_radius = std::max(max_radius, miter_limit_ * 0.5f); + } + Vector2 max_radius_xy = entity.GetTransformation().TransformDirection( + Vector2(max_radius, max_radius) * stroke_size_); + + return Rect(path_coverage->origin - max_radius_xy, + Size(path_coverage->size.width + max_radius_xy.x * 2, + path_coverage->size.height + max_radius_xy.y * 2)); +} + static VertexBuffer CreateSolidStrokeVertices( const Path& path, HostBuffer& buffer, diff --git a/impeller/entity/contents/solid_stroke_contents.h b/impeller/entity/contents/solid_stroke_contents.h index 66f81cc002e85..bde9f332f9b54 100644 --- a/impeller/entity/contents/solid_stroke_contents.h +++ b/impeller/entity/contents/solid_stroke_contents.h @@ -68,6 +68,9 @@ class SolidStrokeContents final : public Contents { Join GetStrokeJoin(); + // |Contents| + std::optional GetCoverage(const Entity& entity) const override; + // |Contents| bool Render(const ContentContext& renderer, const Entity& entity, diff --git a/impeller/entity/entity_unittests.cc b/impeller/entity/entity_unittests.cc index b3904fba06bd8..b3cc1d0dfd5b1 100644 --- a/impeller/entity/entity_unittests.cc +++ b/impeller/entity/entity_unittests.cc @@ -12,6 +12,7 @@ #include "impeller/entity/contents/solid_stroke_contents.h" #include "impeller/entity/entity.h" #include "impeller/entity/entity_playground.h" +#include "impeller/geometry/geometry_unittests.h" #include "impeller/geometry/path_builder.h" #include "impeller/playground/playground.h" #include "impeller/playground/widgets.h" @@ -100,32 +101,51 @@ TEST_F(EntityTest, StrokeCapAndJoinTest) { ImGui::SetNextWindowPos( {0 * padding.x + margin.x, 1.7f * padding.y + margin.y}); } - ImGui::Begin("Controls"); + // Slightly above sqrt(2) by default, so that right angles are just below // the limit and acute angles are over the limit (causing them to get // beveled). static Scalar miter_limit = 1.41421357; static Scalar width = 30; - ImGui::SliderFloat("Miter limit", &miter_limit, 0, 30); - ImGui::SliderFloat("Stroke width", &width, 0, 100); - if (ImGui::Button("Reset")) { - miter_limit = 1.41421357; - width = 30; + + ImGui::Begin("Controls"); + { + ImGui::SliderFloat("Miter limit", &miter_limit, 0, 30); + ImGui::SliderFloat("Stroke width", &width, 0, 100); + if (ImGui::Button("Reset")) { + miter_limit = 1.41421357; + width = 30; + } } ImGui::End(); - auto create_contents = [width = width](SolidStrokeContents::Cap cap, - SolidStrokeContents::Join join) { + auto render_path = [width = width, &context, &pass]( + Path path, SolidStrokeContents::Cap cap, + SolidStrokeContents::Join join) { auto contents = std::make_unique(); contents->SetColor(Color::Red().Premultiply()); contents->SetStrokeSize(width); contents->SetStrokeCap(cap); contents->SetStrokeJoin(join); contents->SetStrokeMiter(miter_limit); - return contents; - }; - Entity entity; + Entity entity; + entity.SetPath(path); + entity.SetContents(std::move(contents)); + + auto coverage = entity.GetCoverage(); + if (coverage.has_value()) { + auto bounds_contents = std::make_unique(); + bounds_contents->SetColor(Color::Green().WithAlpha(0.5)); + Entity bounds_entity; + bounds_entity.SetPath( + PathBuilder{}.AddRect(entity.GetCoverage().value()).TakePath()); + bounds_entity.SetContents(std::move(bounds_contents)); + bounds_entity.Render(context, pass); + } + + entity.Render(context, pass); + }; const Point a_def(0, 0), b_def(0, 100), c_def(150, 0), d_def(150, -100), e_def(75, 75); @@ -133,43 +153,37 @@ TEST_F(EntityTest, StrokeCapAndJoinTest) { // Cap::kButt demo. { Point off = Point(0, 0) * padding + margin; - Point a, b, c, d; - std::tie(a, b) = IMPELLER_PLAYGROUND_LINE(off + a_def, off + b_def, r, - Color::Black(), Color::White()); - std::tie(c, d) = IMPELLER_PLAYGROUND_LINE(off + c_def, off + d_def, r, - Color::Black(), Color::White()); - entity.SetPath(PathBuilder{}.AddCubicCurve(a, b, d, c).TakePath()); - entity.SetContents(create_contents(SolidStrokeContents::Cap::kButt, - SolidStrokeContents::Join::kBevel)); - entity.Render(context, pass); + auto [a, b] = IMPELLER_PLAYGROUND_LINE(off + a_def, off + b_def, r, + Color::Black(), Color::White()); + auto [c, d] = IMPELLER_PLAYGROUND_LINE(off + c_def, off + d_def, r, + Color::Black(), Color::White()); + render_path(PathBuilder{}.AddCubicCurve(a, b, d, c).TakePath(), + SolidStrokeContents::Cap::kButt, + SolidStrokeContents::Join::kBevel); } // Cap::kSquare demo. { Point off = Point(1, 0) * padding + margin; - Point a, b, c, d; - std::tie(a, b) = IMPELLER_PLAYGROUND_LINE(off + a_def, off + b_def, r, - Color::Black(), Color::White()); - std::tie(c, d) = IMPELLER_PLAYGROUND_LINE(off + c_def, off + d_def, r, - Color::Black(), Color::White()); - entity.SetPath(PathBuilder{}.AddCubicCurve(a, b, d, c).TakePath()); - entity.SetContents(create_contents(SolidStrokeContents::Cap::kSquare, - SolidStrokeContents::Join::kBevel)); - entity.Render(context, pass); + auto [a, b] = IMPELLER_PLAYGROUND_LINE(off + a_def, off + b_def, r, + Color::Black(), Color::White()); + auto [c, d] = IMPELLER_PLAYGROUND_LINE(off + c_def, off + d_def, r, + Color::Black(), Color::White()); + render_path(PathBuilder{}.AddCubicCurve(a, b, d, c).TakePath(), + SolidStrokeContents::Cap::kSquare, + SolidStrokeContents::Join::kBevel); } // Cap::kRound demo. { Point off = Point(2, 0) * padding + margin; - Point a, b, c, d; - std::tie(a, b) = IMPELLER_PLAYGROUND_LINE(off + a_def, off + b_def, r, - Color::Black(), Color::White()); - std::tie(c, d) = IMPELLER_PLAYGROUND_LINE(off + c_def, off + d_def, r, - Color::Black(), Color::White()); - entity.SetPath(PathBuilder{}.AddCubicCurve(a, b, d, c).TakePath()); - entity.SetContents(create_contents(SolidStrokeContents::Cap::kRound, - SolidStrokeContents::Join::kBevel)); - entity.Render(context, pass); + auto [a, b] = IMPELLER_PLAYGROUND_LINE(off + a_def, off + b_def, r, + Color::Black(), Color::White()); + auto [c, d] = IMPELLER_PLAYGROUND_LINE(off + c_def, off + d_def, r, + Color::Black(), Color::White()); + render_path(PathBuilder{}.AddCubicCurve(a, b, d, c).TakePath(), + SolidStrokeContents::Cap::kRound, + SolidStrokeContents::Join::kBevel); } // Join::kBevel demo. @@ -178,11 +192,9 @@ TEST_F(EntityTest, StrokeCapAndJoinTest) { Point a = IMPELLER_PLAYGROUND_POINT(off + a_def, r, Color::White()); Point b = IMPELLER_PLAYGROUND_POINT(off + e_def, r, Color::White()); Point c = IMPELLER_PLAYGROUND_POINT(off + c_def, r, Color::White()); - entity.SetPath( - PathBuilder{}.MoveTo(a).LineTo(b).LineTo(c).Close().TakePath()); - entity.SetContents(create_contents(SolidStrokeContents::Cap::kButt, - SolidStrokeContents::Join::kBevel)); - entity.Render(context, pass); + render_path( + PathBuilder{}.MoveTo(a).LineTo(b).LineTo(c).Close().TakePath(), + SolidStrokeContents::Cap::kButt, SolidStrokeContents::Join::kBevel); } // Join::kMiter demo. @@ -191,11 +203,9 @@ TEST_F(EntityTest, StrokeCapAndJoinTest) { Point a = IMPELLER_PLAYGROUND_POINT(off + a_def, r, Color::White()); Point b = IMPELLER_PLAYGROUND_POINT(off + e_def, r, Color::White()); Point c = IMPELLER_PLAYGROUND_POINT(off + c_def, r, Color::White()); - entity.SetPath( - PathBuilder{}.MoveTo(a).LineTo(b).LineTo(c).Close().TakePath()); - entity.SetContents(create_contents(SolidStrokeContents::Cap::kButt, - SolidStrokeContents::Join::kMiter)); - entity.Render(context, pass); + render_path( + PathBuilder{}.MoveTo(a).LineTo(b).LineTo(c).Close().TakePath(), + SolidStrokeContents::Cap::kButt, SolidStrokeContents::Join::kMiter); } // Join::kRound demo. @@ -204,11 +214,9 @@ TEST_F(EntityTest, StrokeCapAndJoinTest) { Point a = IMPELLER_PLAYGROUND_POINT(off + a_def, r, Color::White()); Point b = IMPELLER_PLAYGROUND_POINT(off + e_def, r, Color::White()); Point c = IMPELLER_PLAYGROUND_POINT(off + c_def, r, Color::White()); - entity.SetPath( - PathBuilder{}.MoveTo(a).LineTo(b).LineTo(c).Close().TakePath()); - entity.SetContents(create_contents(SolidStrokeContents::Cap::kButt, - SolidStrokeContents::Join::kRound)); - entity.Render(context, pass); + render_path( + PathBuilder{}.MoveTo(a).LineTo(b).LineTo(c).Close().TakePath(), + SolidStrokeContents::Cap::kButt, SolidStrokeContents::Join::kRound); } return true; @@ -789,5 +797,53 @@ TEST_F(EntityTest, ContentsGetBoundsForEmptyPathReturnsNullopt) { ASSERT_FALSE(entity.GetPathCoverage().has_value()); } +TEST_F(EntityTest, SolidStrokeCoverageIsCorrect) { + { + Entity entity; + auto contents = std::make_unique(); + contents->SetStrokeCap(SolidStrokeContents::Cap::kButt); + contents->SetStrokeJoin(SolidStrokeContents::Join::kBevel); + contents->SetStrokeSize(4); + entity.SetPath(PathBuilder{}.AddLine({0, 0}, {10, 10}).TakePath()); + entity.SetContents(std::move(contents)); + auto actual = entity.GetCoverage(); + auto expected = Rect::MakeLTRB(-2, -2, 12, 12); + ASSERT_TRUE(actual.has_value()); + ASSERT_RECT_NEAR(actual.value(), expected); + } + + // Cover the Cap::kSquare case. + { + Entity entity; + auto contents = std::make_unique(); + contents->SetStrokeCap(SolidStrokeContents::Cap::kSquare); + contents->SetStrokeJoin(SolidStrokeContents::Join::kBevel); + contents->SetStrokeSize(4); + entity.SetPath(PathBuilder{}.AddLine({0, 0}, {10, 10}).TakePath()); + entity.SetContents(std::move(contents)); + auto actual = entity.GetCoverage(); + auto expected = + Rect::MakeLTRB(-sqrt(8), -sqrt(8), 10 + sqrt(8), 10 + sqrt(8)); + ASSERT_TRUE(actual.has_value()); + ASSERT_RECT_NEAR(actual.value(), expected); + } + + // Cover the Join::kMiter case. + { + Entity entity; + auto contents = std::make_unique(); + contents->SetStrokeCap(SolidStrokeContents::Cap::kSquare); + contents->SetStrokeJoin(SolidStrokeContents::Join::kMiter); + contents->SetStrokeSize(4); + contents->SetStrokeMiter(2); + entity.SetPath(PathBuilder{}.AddLine({0, 0}, {10, 10}).TakePath()); + entity.SetContents(std::move(contents)); + auto actual = entity.GetCoverage(); + auto expected = Rect::MakeLTRB(-4, -4, 14, 14); + ASSERT_TRUE(actual.has_value()); + ASSERT_RECT_NEAR(actual.value(), expected); + } +} + } // namespace testing } // namespace impeller From 5056cbd9aa8700e1a8717457846481eb23718b3f Mon Sep 17 00:00:00 2001 From: Dan Field Date: Wed, 13 Apr 2022 13:04:13 -0700 Subject: [PATCH 398/433] Allow building targets that do not create metal shaders on all platforms. (#133) --- impeller/BUILD.gn | 4 ++++ impeller/tessellator/BUILD.gn | 10 ++++++++-- impeller/tools/impeller.gni | 7 +++++-- 3 files changed, 17 insertions(+), 4 deletions(-) diff --git a/impeller/BUILD.gn b/impeller/BUILD.gn index c7ae5aa956f67..92ee51f93eaaf 100644 --- a/impeller/BUILD.gn +++ b/impeller/BUILD.gn @@ -12,6 +12,10 @@ config("impeller_public_config") { if (impeller_supports_platform) { defines += [ "IMPELLER_SUPPORTS_PLATFORM=1" ] } + + if (is_win) { + defines += [ "_USE_MATH_DEFINES" ] + } } group("impeller") { diff --git a/impeller/tessellator/BUILD.gn b/impeller/tessellator/BUILD.gn index 4dba135b99bf1..7a615464ac5da 100644 --- a/impeller/tessellator/BUILD.gn +++ b/impeller/tessellator/BUILD.gn @@ -15,8 +15,14 @@ impeller_component("tessellator") { deps = [ "//third_party/libtess2" ] } -shared_library("tessellator_shared") { - output_name = "tessellator" +impeller_component("tessellator_shared") { + target_type = "shared_library" + if (is_win) { + output_name = "libtessellator" + } else { + output_name = "tessellator" + } + sources = [ "c/tessellator.h", diff --git a/impeller/tools/impeller.gni b/impeller/tools/impeller.gni index eba266c881c8f..5fa10511ad9cd 100644 --- a/impeller/tools/impeller.gni +++ b/impeller/tools/impeller.gni @@ -10,7 +10,10 @@ declare_args() { impeller_enable_playground = false # Whether Impeller is supported on the platform. - impeller_supports_platform = is_mac || is_ios + impeller_supports_platform = true + + # Whether Impeller shaders are supported on the platform. + impeller_shaders_supports_platform = is_mac || is_ios } # ------------------------------------------------------------------------------ @@ -289,7 +292,7 @@ template("impeller_shaders_real") { # @see impeller_shaders_real # template("impeller_shaders") { - if (impeller_supports_platform) { + if (impeller_shaders_supports_platform) { impeller_shaders_real(target_name) { forward_variables_from(invoker, "*") } From 56d743993dbd8cde39d5a5504a776e4c8bfd1fb6 Mon Sep 17 00:00:00 2001 From: Dan Field Date: Wed, 13 Apr 2022 15:12:53 -0700 Subject: [PATCH 399/433] Move rendering TUs behind own flag (#134) --- impeller/BUILD.gn | 36 +++++++++++++++++++++++------------- impeller/tools/impeller.gni | 3 +++ 2 files changed, 26 insertions(+), 13 deletions(-) diff --git a/impeller/BUILD.gn b/impeller/BUILD.gn index 92ee51f93eaaf..01feaa41eb27a 100644 --- a/impeller/BUILD.gn +++ b/impeller/BUILD.gn @@ -20,38 +20,48 @@ config("impeller_public_config") { group("impeller") { public_deps = [ - "aiks", "archivist", "base", - "display_list", - "entity", "geometry", - "image", - "renderer", "tessellator", - "typographer", ] + + if (impeller_supports_rendering) { + public_deps += [ + "aiks", + "display_list", + "entity", + "image", + "renderer", + "typographer", + ] + } } executable("impeller_unittests") { testonly = true deps = [ - "aiks:aiks_unittests", "archivist:archivist_unittests", "base:base_unittests", "compiler:compiler_unittests", - "display_list:display_list_unittests", - "entity:entity_unittests", "fixtures", "geometry:geometry_unittests", - "image:image_unittests", - "playground", - "renderer:renderer_unittests", - "typographer:typographer_unittests", # FML depends on the Dart VM for tracing and getting the current # timepoint. "//flutter/runtime:libdart", ] + + if (impeller_supports_rendering) { + deps += [ + "aiks:aiks_unittests", + "display_list:display_list_unittests", + "entity:entity_unittests", + "image:image_unittests", + "playground", + "renderer:renderer_unittests", + "typographer:typographer_unittests", + ] + } } diff --git a/impeller/tools/impeller.gni b/impeller/tools/impeller.gni index 5fa10511ad9cd..f82595760bc30 100644 --- a/impeller/tools/impeller.gni +++ b/impeller/tools/impeller.gni @@ -12,6 +12,9 @@ declare_args() { # Whether Impeller is supported on the platform. impeller_supports_platform = true + # Whether Impeller supports rendering on the platform. + impeller_supports_rendering = is_mac || is_ios + # Whether Impeller shaders are supported on the platform. impeller_shaders_supports_platform = is_mac || is_ios } From aa36cee62f7d1832e4417abc1215cb4f70e5d6db Mon Sep 17 00:00:00 2001 From: Dan Field Date: Wed, 13 Apr 2022 16:33:26 -0700 Subject: [PATCH 400/433] Build fixes for roll (#135) * Add define for rendering * Fix metal enum for older SDKs, add missing dep --- impeller/BUILD.gn | 4 ++++ impeller/entity/BUILD.gn | 1 + .../renderer/backend/metal/command_buffer_mtl.mm | 12 ++++++++++++ 3 files changed, 17 insertions(+) diff --git a/impeller/BUILD.gn b/impeller/BUILD.gn index 01feaa41eb27a..b206c99e9f584 100644 --- a/impeller/BUILD.gn +++ b/impeller/BUILD.gn @@ -13,6 +13,10 @@ config("impeller_public_config") { defines += [ "IMPELLER_SUPPORTS_PLATFORM=1" ] } + if (impeller_supports_rendering) { + defines += [ "IMPELLER_SUPPORTS_RENDERING=1" ] + } + if (is_win) { defines += [ "_USE_MATH_DEFINES" ] } diff --git a/impeller/entity/BUILD.gn b/impeller/entity/BUILD.gn index d5062f3038971..51c2766dbc99b 100644 --- a/impeller/entity/BUILD.gn +++ b/impeller/entity/BUILD.gn @@ -87,6 +87,7 @@ impeller_component("entity_unittests") { deps = [ ":entity", + "../geometry:geometry_unittests", "../playground", ] } diff --git a/impeller/renderer/backend/metal/command_buffer_mtl.mm b/impeller/renderer/backend/metal/command_buffer_mtl.mm index ebdd3d2d75242..f5a9667479756 100644 --- a/impeller/renderer/backend/metal/command_buffer_mtl.mm +++ b/impeller/renderer/backend/metal/command_buffer_mtl.mm @@ -53,6 +53,18 @@ return CommandBufferMTL::Status::kError; } +// TODO(dnfield): remove this declaration when we no longer need to build on +// machines with lower SDK versions than 11.0.s +#if !defined(MAC_OS_X_VERSION_11_0) || MAC_OS_X_VERSION_MAX_ALLOWED < MAC_OS_X_VERSION_11_0 +typedef enum MTLCommandEncoderErrorState : NSInteger { + MTLCommandEncoderErrorStateUnknown = 0, + MTLCommandEncoderErrorStateCompleted = 1, + MTLCommandEncoderErrorStateAffected = 2, + MTLCommandEncoderErrorStatePending = 3, + MTLCommandEncoderErrorStateFaulted = 4, +} API_AVAILABLE(macos(11.0), ios(14.0)); +#endif + API_AVAILABLE(ios(14.0), macos(11.0)) NSString* MTLCommandEncoderErrorStateToString( MTLCommandEncoderErrorState state) { From 21f8eb93ddb9773ec42f4c45813323a496606700 Mon Sep 17 00:00:00 2001 From: Brandon DeRosier Date: Wed, 13 Apr 2022 16:40:46 -0700 Subject: [PATCH 401/433] Remove extra premultiply from solid stroke/fill (#124) --- impeller/aiks/paint.cc | 4 ++-- impeller/entity/entity_unittests.cc | 6 +++--- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/impeller/aiks/paint.cc b/impeller/aiks/paint.cc index ac1f712908fc5..46a14b5ab36ac 100644 --- a/impeller/aiks/paint.cc +++ b/impeller/aiks/paint.cc @@ -16,12 +16,12 @@ std::shared_ptr Paint::CreateContentsForEntity() const { switch (style) { case Style::kFill: { auto solid_color = std::make_shared(); - solid_color->SetColor(color.Premultiply()); + solid_color->SetColor(color); return solid_color; } case Style::kStroke: { auto solid_stroke = std::make_shared(); - solid_stroke->SetColor(color.Premultiply()); + solid_stroke->SetColor(color); solid_stroke->SetStrokeSize(stroke_width); solid_stroke->SetStrokeMiter(stroke_miter); solid_stroke->SetStrokeCap(stroke_cap); diff --git a/impeller/entity/entity_unittests.cc b/impeller/entity/entity_unittests.cc index b3cc1d0dfd5b1..11f12e6043648 100644 --- a/impeller/entity/entity_unittests.cc +++ b/impeller/entity/entity_unittests.cc @@ -50,7 +50,7 @@ TEST_F(EntityTest, ThreeStrokesInOnePath) { Entity entity; entity.SetPath(path); auto contents = std::make_unique(); - contents->SetColor(Color::Red().Premultiply()); + contents->SetColor(Color::Red()); contents->SetStrokeSize(5.0); entity.SetContents(std::move(contents)); ASSERT_TRUE(OpenPlaygroundHere(entity)); @@ -80,7 +80,7 @@ TEST_F(EntityTest, TriangleInsideASquare) { Entity entity; entity.SetPath(path); auto contents = std::make_unique(); - contents->SetColor(Color::Red().Premultiply()); + contents->SetColor(Color::Red()); contents->SetStrokeSize(20.0); entity.SetContents(std::move(contents)); @@ -123,7 +123,7 @@ TEST_F(EntityTest, StrokeCapAndJoinTest) { Path path, SolidStrokeContents::Cap cap, SolidStrokeContents::Join join) { auto contents = std::make_unique(); - contents->SetColor(Color::Red().Premultiply()); + contents->SetColor(Color::Red()); contents->SetStrokeSize(width); contents->SetStrokeCap(cap); contents->SetStrokeJoin(join); From ac71cebcdab0cbfcb0dd94ea59eaa49f311ab46a Mon Sep 17 00:00:00 2001 From: Dan Field Date: Wed, 13 Apr 2022 21:53:57 -0700 Subject: [PATCH 402/433] Fix enum typedef for older mac SDKs (#136) --- impeller/renderer/backend/metal/command_buffer_mtl.mm | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/impeller/renderer/backend/metal/command_buffer_mtl.mm b/impeller/renderer/backend/metal/command_buffer_mtl.mm index f5a9667479756..4382c6d8c8d64 100644 --- a/impeller/renderer/backend/metal/command_buffer_mtl.mm +++ b/impeller/renderer/backend/metal/command_buffer_mtl.mm @@ -55,8 +55,8 @@ // TODO(dnfield): remove this declaration when we no longer need to build on // machines with lower SDK versions than 11.0.s -#if !defined(MAC_OS_X_VERSION_11_0) || MAC_OS_X_VERSION_MAX_ALLOWED < MAC_OS_X_VERSION_11_0 -typedef enum MTLCommandEncoderErrorState : NSInteger { +#if !defined(MAC_OS_VERSION_11_0) || MAC_OS_X_VERSION_MAX_ALLOWED < MAC_OS_VERSION_11_0 +typedef NS_ENUM(NSInteger, MTLCommandEncoderErrorState) { MTLCommandEncoderErrorStateUnknown = 0, MTLCommandEncoderErrorStateCompleted = 1, MTLCommandEncoderErrorStateAffected = 2, @@ -65,6 +65,12 @@ } API_AVAILABLE(macos(11.0), ios(14.0)); #endif + +#if !defined(MAC_OS_VERSION_12_0) || MAC_OS_X_VERSION_MAX_ALLOWED < MAC_OS_VERSION_12_0 +constexpr int MTLCommandBufferErrorAccessRevoked = 4; +constexpr int MTLCommandBufferErrorStackOverflow = 12; +#endif + API_AVAILABLE(ios(14.0), macos(11.0)) NSString* MTLCommandEncoderErrorStateToString( MTLCommandEncoderErrorState state) { From 254db27ab030db28bb68d9a59a07746a0bc798ff Mon Sep 17 00:00:00 2001 From: Brandon DeRosier Date: Thu, 14 Apr 2022 02:04:42 -0700 Subject: [PATCH 403/433] Filters: Replace Snapshot position with a full transform (#130) --- impeller/entity/contents/contents.cc | 5 +- impeller/entity/contents/contents.h | 4 +- .../contents/filters/blend_filter_contents.cc | 142 ++++++++++-------- .../contents/filters/blend_filter_contents.h | 4 +- .../contents/filters/filter_contents.cc | 11 +- .../entity/contents/filters/filter_contents.h | 2 +- .../entity/contents/filters/filter_input.cc | 22 ++- .../entity/contents/filters/filter_input.h | 6 +- .../filters/gaussian_blur_filter_contents.cc | 106 ++++++------- .../filters/gaussian_blur_filter_contents.h | 2 +- impeller/entity/contents/snapshot.cc | 39 ++--- impeller/entity/contents/snapshot.h | 23 +-- impeller/entity/entity_unittests.cc | 7 +- .../entity/shaders/texture_blend_screen.vert | 14 +- impeller/geometry/geometry_unittests.cc | 22 +++ impeller/geometry/matrix.h | 11 ++ impeller/geometry/rect.h | 17 ++- 17 files changed, 241 insertions(+), 196 deletions(-) diff --git a/impeller/entity/contents/contents.cc b/impeller/entity/contents/contents.cc index e18eb7d3e15c3..a2c0764e23607 100644 --- a/impeller/entity/contents/contents.cc +++ b/impeller/entity/contents/contents.cc @@ -33,7 +33,7 @@ std::optional Contents::GetCoverage(const Entity& entity) const { return entity.GetPathCoverage(); } -std::optional Contents::RenderToTexture( +std::optional Contents::RenderToSnapshot( const ContentContext& renderer, const Entity& entity) const { auto bounds = GetCoverage(entity); @@ -58,7 +58,8 @@ std::optional Contents::RenderToTexture( return std::nullopt; } - return Snapshot{.texture = texture, .position = bounds->origin}; + return Snapshot{.texture = texture, + .transform = Matrix::MakeTranslation(bounds->origin)}; } } // namespace impeller diff --git a/impeller/entity/contents/contents.h b/impeller/entity/contents/contents.h index d915891b501bb..a72ae642a5841 100644 --- a/impeller/entity/contents/contents.h +++ b/impeller/entity/contents/contents.h @@ -39,11 +39,11 @@ class Contents { /// @brief Get the screen space bounding rectangle that this contents affects. virtual std::optional GetCoverage(const Entity& entity) const; - /// @brief Render this contents to a texture, respecting the entity's + /// @brief Render this contents to a snapshot, respecting the entity's /// transform, path, stencil depth, blend mode, etc. /// The result texture size is always the size of /// `GetCoverage(entity)`. - virtual std::optional RenderToTexture( + virtual std::optional RenderToSnapshot( const ContentContext& renderer, const Entity& entity) const; diff --git a/impeller/entity/contents/filters/blend_filter_contents.cc b/impeller/entity/contents/filters/blend_filter_contents.cc index 293065e679002..05a744a422d9f 100644 --- a/impeller/entity/contents/filters/blend_filter_contents.cc +++ b/impeller/entity/contents/filters/blend_filter_contents.cc @@ -25,23 +25,43 @@ static bool AdvancedBlend(const FilterInput::Vector& inputs, const ContentContext& renderer, const Entity& entity, RenderPass& pass, - const Rect& bounds, + const Rect& coverage, PipelineProc pipeline_proc) { if (inputs.size() < 2) { return false; } + auto dst_snapshot = inputs[1]->GetSnapshot(renderer, entity); + if (!dst_snapshot.has_value()) { + return true; + } + auto maybe_dst_uvs = dst_snapshot->GetCoverageUVs(coverage); + if (!maybe_dst_uvs.has_value()) { + return true; + } + auto dst_uvs = maybe_dst_uvs.value(); + + auto src_snapshot = inputs[0]->GetSnapshot(renderer, entity); + if (!src_snapshot.has_value()) { + return true; + } + auto maybe_src_uvs = src_snapshot->GetCoverageUVs(coverage); + if (!maybe_src_uvs.has_value()) { + return true; + } + auto src_uvs = maybe_src_uvs.value(); + auto& host_buffer = pass.GetTransientsBuffer(); auto size = pass.GetRenderTargetSize(); VertexBufferBuilder vtx_builder; vtx_builder.AddVertices({ - {Point(0, 0), Point(0, 0)}, - {Point(size.width, 0), Point(1, 0)}, - {Point(size.width, size.height), Point(1, 1)}, - {Point(0, 0), Point(0, 0)}, - {Point(size.width, size.height), Point(1, 1)}, - {Point(0, size.height), Point(0, 1)}, + {Point(0, 0), dst_uvs[0], src_uvs[0]}, + {Point(size.width, 0), dst_uvs[1], src_uvs[1]}, + {Point(size.width, size.height), dst_uvs[3], src_uvs[3]}, + {Point(0, 0), dst_uvs[0], src_uvs[0]}, + {Point(size.width, size.height), dst_uvs[3], src_uvs[3]}, + {Point(0, size.height), dst_uvs[2], src_uvs[2]}, }); auto vtx_buffer = vtx_builder.CreateVertexBuffer(host_buffer); @@ -56,25 +76,12 @@ static bool AdvancedBlend(const FilterInput::Vector& inputs, cmd.pipeline = std::move(pipeline); auto sampler = renderer.GetContext()->GetSamplerLibrary()->GetSampler({}); + FS::BindTextureSamplerDst(cmd, dst_snapshot->texture, sampler); + FS::BindTextureSamplerSrc(cmd, src_snapshot->texture, sampler); + typename VS::FrameInfo frame_info; frame_info.mvp = Matrix::MakeOrthographic(size); - auto dst_snapshot = inputs[1]->GetSnapshot(renderer, entity); - FS::BindTextureSamplerSrc(cmd, dst_snapshot->texture, sampler); - frame_info.dst_uv_transform = - Matrix::MakeTranslation(-(dst_snapshot->position - bounds.origin) / - size) * - Matrix::MakeScale( - Vector3(Size(size) / Size(dst_snapshot->texture->GetSize()))); - - auto src_snapshot = inputs[0]->GetSnapshot(renderer, entity); - FS::BindTextureSamplerDst(cmd, src_snapshot->texture, sampler); - frame_info.src_uv_transform = - Matrix::MakeTranslation(-(src_snapshot->position - bounds.origin) / - size) * - Matrix::MakeScale( - Vector3(Size(size) / Size(src_snapshot->texture->GetSize()))); - auto uniform_view = host_buffer.EmplaceUniform(frame_info); VS::BindFrameInfo(cmd, uniform_view); pass.AddCommand(cmd); @@ -99,11 +106,11 @@ void BlendFilterContents::SetBlendMode(Entity::BlendMode blend_mode) { advanced_blend_proc_ = [](const FilterInput::Vector& inputs, const ContentContext& renderer, const Entity& entity, RenderPass& pass, - const Rect& bounds) { + const Rect& coverage) { PipelineProc p = &ContentContext::GetTextureBlendScreenPipeline; return AdvancedBlend( - inputs, renderer, entity, pass, bounds, p); + inputs, renderer, entity, pass, coverage, p); }; break; default: @@ -116,49 +123,62 @@ static bool BasicBlend(const FilterInput::Vector& inputs, const ContentContext& renderer, const Entity& entity, RenderPass& pass, - const Rect& bounds, + const Rect& coverage, Entity::BlendMode basic_blend) { using VS = TextureBlendPipeline::VertexShader; using FS = TextureBlendPipeline::FragmentShader; auto& host_buffer = pass.GetTransientsBuffer(); - auto size = pass.GetRenderTargetSize(); - VertexBufferBuilder vtx_builder; - vtx_builder.AddVertices({ - {Point(0, 0), Point(0, 0)}, - {Point(size.width, 0), Point(1, 0)}, - {Point(size.width, size.height), Point(1, 1)}, - {Point(0, 0), Point(0, 0)}, - {Point(size.width, size.height), Point(1, 1)}, - {Point(0, size.height), Point(0, 1)}, - }); - auto vtx_buffer = vtx_builder.CreateVertexBuffer(host_buffer); - auto sampler = renderer.GetContext()->GetSamplerLibrary()->GetSampler({}); - // Draw the first texture using kSource. - Command cmd; cmd.label = "Basic Blend Filter"; - cmd.BindVertices(vtx_buffer); auto options = OptionsFromPass(pass); - options.blend_mode = Entity::BlendMode::kSource; - cmd.pipeline = renderer.GetTextureBlendPipeline(options); - { - auto input = inputs[0]->GetSnapshot(renderer, entity); + + auto add_blend_command = [&](std::optional input) { + if (!input.has_value()) { + return false; + } + auto input_coverage = input->GetCoverage(); + if (!input_coverage.has_value()) { + return false; + } + FS::BindTextureSamplerSrc(cmd, input->texture, sampler); + auto size = input->texture->GetSize(); + VertexBufferBuilder vtx_builder; + vtx_builder.AddVertices({ + {Point(0, 0), Point(0, 0)}, + {Point(size.width, 0), Point(1, 0)}, + {Point(size.width, size.height), Point(1, 1)}, + {Point(0, 0), Point(0, 0)}, + {Point(size.width, size.height), Point(1, 1)}, + {Point(0, size.height), Point(0, 1)}, + }); + auto vtx_buffer = vtx_builder.CreateVertexBuffer(host_buffer); + cmd.BindVertices(vtx_buffer); + VS::FrameInfo frame_info; - frame_info.mvp = - Matrix::MakeOrthographic(size) * - Matrix::MakeTranslation(input->position - bounds.origin) * - Matrix::MakeScale(Size(input->texture->GetSize()) / Size(size)); + frame_info.mvp = Matrix::MakeOrthographic(pass.GetRenderTargetSize()) * + Matrix::MakeTranslation(-coverage.origin) * + input->transform; auto uniform_view = host_buffer.EmplaceUniform(frame_info); VS::BindFrameInfo(cmd, uniform_view); + + pass.AddCommand(cmd); + return true; + }; + + // Draw the first texture using kSource. + + options.blend_mode = Entity::BlendMode::kSource; + cmd.pipeline = renderer.GetTextureBlendPipeline(options); + if (!add_blend_command(inputs[0]->GetSnapshot(renderer, entity))) { + return true; } - pass.AddCommand(cmd); if (inputs.size() < 2) { return true; @@ -172,17 +192,9 @@ static bool BasicBlend(const FilterInput::Vector& inputs, for (auto texture_i = inputs.begin() + 1; texture_i < inputs.end(); texture_i++) { auto input = texture_i->get()->GetSnapshot(renderer, entity); - FS::BindTextureSamplerSrc(cmd, input->texture, sampler); - - VS::FrameInfo frame_info; - frame_info.mvp = frame_info.mvp = - Matrix::MakeOrthographic(size) * - Matrix::MakeTranslation(input->position - bounds.origin) * - Matrix::MakeScale(Size(input->texture->GetSize()) / Size(size)); - - auto uniform_view = host_buffer.EmplaceUniform(frame_info); - VS::BindFrameInfo(cmd, uniform_view); - pass.AddCommand(cmd); + if (!add_blend_command(input)) { + return true; + } } return true; @@ -192,23 +204,23 @@ bool BlendFilterContents::RenderFilter(const FilterInput::Vector& inputs, const ContentContext& renderer, const Entity& entity, RenderPass& pass, - const Rect& bounds) const { + const Rect& coverage) const { if (inputs.empty()) { return true; } if (inputs.size() == 1) { // Nothing to blend. - return BasicBlend(inputs, renderer, entity, pass, bounds, + return BasicBlend(inputs, renderer, entity, pass, coverage, Entity::BlendMode::kSource); } if (blend_mode_ <= Entity::BlendMode::kLastPipelineBlendMode) { - return BasicBlend(inputs, renderer, entity, pass, bounds, blend_mode_); + return BasicBlend(inputs, renderer, entity, pass, coverage, blend_mode_); } if (blend_mode_ <= Entity::BlendMode::kLastAdvancedBlendMode) { - return advanced_blend_proc_(inputs, renderer, entity, pass, bounds); + return advanced_blend_proc_(inputs, renderer, entity, pass, coverage); } FML_UNREACHABLE(); diff --git a/impeller/entity/contents/filters/blend_filter_contents.h b/impeller/entity/contents/filters/blend_filter_contents.h index 6baf5ec5be0de..9a10a933560bd 100644 --- a/impeller/entity/contents/filters/blend_filter_contents.h +++ b/impeller/entity/contents/filters/blend_filter_contents.h @@ -16,7 +16,7 @@ class BlendFilterContents : public FilterContents { const ContentContext& renderer, const Entity& entity, RenderPass& pass, - const Rect& bounds)>; + const Rect& coverage)>; BlendFilterContents(); @@ -30,7 +30,7 @@ class BlendFilterContents : public FilterContents { const ContentContext& renderer, const Entity& entity, RenderPass& pass, - const Rect& bounds) const override; + const Rect& coverage) const override; Entity::BlendMode blend_mode_; AdvancedBlendProc advanced_blend_proc_; diff --git a/impeller/entity/contents/filters/filter_contents.cc b/impeller/entity/contents/filters/filter_contents.cc index adb25ff305bff..5b820b09a5188 100644 --- a/impeller/entity/contents/filters/filter_contents.cc +++ b/impeller/entity/contents/filters/filter_contents.cc @@ -50,7 +50,9 @@ std::shared_ptr FilterContents::MakeBlend( new_blend = std::make_shared(); new_blend->SetInputs({blend_input, *in_i}); new_blend->SetBlendMode(blend_mode); - blend_input = FilterInput::Make(new_blend); + if (in_i < inputs.end() - 1) { + blend_input = FilterInput::Make(new_blend); + } } // new_blend will always be assigned because inputs.size() >= 2. return new_blend; @@ -104,7 +106,7 @@ bool FilterContents::Render(const ContentContext& renderer, // Run the filter. - auto maybe_snapshot = RenderToTexture(renderer, entity); + auto maybe_snapshot = RenderToSnapshot(renderer, entity); if (!maybe_snapshot.has_value()) { return false; } @@ -147,7 +149,7 @@ std::optional FilterContents::GetCoverage(const Entity& entity) const { return result; } -std::optional FilterContents::RenderToTexture( +std::optional FilterContents::RenderToSnapshot( const ContentContext& renderer, const Entity& entity) const { auto bounds = GetCoverage(entity); @@ -166,7 +168,8 @@ std::optional FilterContents::RenderToTexture( return std::nullopt; } - return Snapshot{.texture = texture, .position = bounds->origin}; + return Snapshot{.texture = texture, + .transform = Matrix::MakeTranslation(bounds->origin)}; } } // namespace impeller diff --git a/impeller/entity/contents/filters/filter_contents.h b/impeller/entity/contents/filters/filter_contents.h index 25bae0f0a5310..236bf1193b836 100644 --- a/impeller/entity/contents/filters/filter_contents.h +++ b/impeller/entity/contents/filters/filter_contents.h @@ -116,7 +116,7 @@ class FilterContents : public Contents { std::optional GetCoverage(const Entity& entity) const override; // |Contents| - virtual std::optional RenderToTexture( + virtual std::optional RenderToSnapshot( const ContentContext& renderer, const Entity& entity) const override; diff --git a/impeller/entity/contents/filters/filter_input.cc b/impeller/entity/contents/filters/filter_input.cc index c3585e6cdc1e1..51ff74a58b32c 100644 --- a/impeller/entity/contents/filters/filter_input.cc +++ b/impeller/entity/contents/filters/filter_input.cc @@ -7,6 +7,7 @@ #include #include #include +#include #include "impeller/entity/contents/snapshot.h" #include "impeller/entity/entity.h" @@ -32,7 +33,7 @@ FilterInput::Variant FilterInput::GetInput() const { std::optional FilterInput::GetCoverage(const Entity& entity) const { if (snapshot_) { - return Rect(snapshot_->position, Size(snapshot_->texture->GetSize())); + return snapshot_->GetCoverage(); } if (auto contents = std::get_if>(&input_)) { @@ -51,7 +52,7 @@ std::optional FilterInput::GetSnapshot(const ContentContext& renderer, if (snapshot_) { return snapshot_; } - snapshot_ = RenderToTexture(renderer, entity); + snapshot_ = MakeSnapshot(renderer, entity); return snapshot_; } @@ -60,15 +61,26 @@ FilterInput::FilterInput(Variant input) : input_(input) {} FilterInput::~FilterInput() = default; -std::optional FilterInput::RenderToTexture( +std::optional FilterInput::MakeSnapshot( const ContentContext& renderer, const Entity& entity) const { if (auto contents = std::get_if>(&input_)) { - return contents->get()->RenderToTexture(renderer, entity); + return contents->get()->RenderToSnapshot(renderer, entity); } if (auto texture = std::get_if>(&input_)) { - return Snapshot::FromTransformedTexture(renderer, entity, *texture); + // Rendered textures stretch to fit the entity path coverage, so we + // incorporate this behavior by translating and scaling the snapshot + // transform. + auto path_bounds = entity.GetPath().GetBoundingBox(); + if (!path_bounds.has_value()) { + return std::nullopt; + } + auto transform = entity.GetTransformation() * + Matrix::MakeTranslation(path_bounds->origin) * + Matrix::MakeScale(Vector2(path_bounds->size) / + texture->get()->GetSize()); + return Snapshot{.texture = *texture, .transform = transform}; } FML_UNREACHABLE(); diff --git a/impeller/entity/contents/filters/filter_input.h b/impeller/entity/contents/filters/filter_input.h index 32b440a8f3456..eee31df26510b 100644 --- a/impeller/entity/contents/filters/filter_input.h +++ b/impeller/entity/contents/filters/filter_input.h @@ -50,8 +50,10 @@ class FilterInput final { private: FilterInput(Variant input); - std::optional RenderToTexture(const ContentContext& renderer, - const Entity& entity) const; + std::optional MakeSnapshot(const ContentContext& renderer, + const Entity& entity) const; + + std::optional MakeSnapshotForTexture(const Entity& entity) const; Variant input_; mutable std::optional snapshot_; diff --git a/impeller/entity/contents/filters/gaussian_blur_filter_contents.cc b/impeller/entity/contents/filters/gaussian_blur_filter_contents.cc index 0b2c19770dc0f..140ded547bb06 100644 --- a/impeller/entity/contents/filters/gaussian_blur_filter_contents.cc +++ b/impeller/entity/contents/filters/gaussian_blur_filter_contents.cc @@ -11,7 +11,9 @@ #include "impeller/entity/contents/filters/filter_contents.h" #include "impeller/geometry/rect.h" #include "impeller/geometry/scalar.h" +#include "impeller/renderer/formats.h" #include "impeller/renderer/render_pass.h" +#include "impeller/renderer/sampler_descriptor.h" #include "impeller/renderer/sampler_library.h" namespace impeller { @@ -69,8 +71,8 @@ void DirectionalGaussianBlurFilterContents::SetBlurStyle(BlurStyle blur_style) { } void DirectionalGaussianBlurFilterContents::SetSourceOverride( - FilterInput::Ref alpha_mask) { - source_override_ = alpha_mask; + FilterInput::Ref source_override) { + source_override_ = source_override; } bool DirectionalGaussianBlurFilterContents::RenderFilter( @@ -78,7 +80,7 @@ bool DirectionalGaussianBlurFilterContents::RenderFilter( const ContentContext& renderer, const Entity& entity, RenderPass& pass, - const Rect& bounds) const { + const Rect& coverage) const { if (inputs.empty()) { return true; } @@ -88,79 +90,61 @@ bool DirectionalGaussianBlurFilterContents::RenderFilter( auto& host_buffer = pass.GetTransientsBuffer(); - auto input = inputs[0]->GetSnapshot(renderer, entity); - if (!input.has_value()) { - return true; - } + // Input 0 snapshot and UV mapping. - auto input_bounds = inputs[0]->GetCoverage(entity); - if (!input_bounds.has_value() || input_bounds->IsEmpty()) { + auto input_snapshot = inputs[0]->GetSnapshot(renderer, entity); + if (!input_snapshot.has_value()) { return true; } - auto filter_bounds = GetCoverage(entity); - if (!filter_bounds.has_value() || filter_bounds->IsEmpty()) { - FML_LOG(ERROR) << "The gaussian blur filter coverage is missing or empty " - "even though the filter's input has coverage."; - return false; + auto maybe_input_uvs = input_snapshot->GetCoverageUVs(coverage); + if (!maybe_input_uvs.has_value()) { + return true; } + auto input_uvs = maybe_input_uvs.value(); - auto transformed_blur = entity.GetTransformation().TransformDirection( - blur_direction_ * blur_sigma_.sigma); - - // LTRB - Scalar uv[4] = { - (filter_bounds->GetLeft() - input_bounds->GetLeft()) / - input_bounds->size.width, - (filter_bounds->GetTop() - input_bounds->GetTop()) / - input_bounds->size.height, - 1 + (filter_bounds->GetRight() - input_bounds->GetRight()) / - input_bounds->size.width, - 1 + (filter_bounds->GetBottom() - input_bounds->GetBottom()) / - input_bounds->size.height, - }; + // Source override snapshot and UV mapping. auto source = source_override_ ? source_override_ : inputs[0]; - auto source_texture = source->GetSnapshot(renderer, entity); - auto source_bounds = source->GetCoverage(entity); - if (!source_texture.has_value() || !source_bounds.has_value() || - source_bounds->IsEmpty()) { - VALIDATION_LOG << "The gaussian blur source override has no coverage."; - return false; + auto source_snapshot = source->GetSnapshot(renderer, entity); + if (!source_snapshot.has_value()) { + return true; } - - // LTRB - Scalar uv_src[4] = { - (filter_bounds->GetLeft() - source_bounds->GetLeft()) / - source_bounds->size.width, - (filter_bounds->GetTop() - source_bounds->GetTop()) / - source_bounds->size.height, - 1 + (filter_bounds->GetRight() - source_bounds->GetRight()) / - source_bounds->size.width, - 1 + (filter_bounds->GetBottom() - source_bounds->GetBottom()) / - source_bounds->size.height, - }; + auto maybe_source_uvs = source_snapshot->GetCoverageUVs(coverage); + if (!maybe_source_uvs.has_value()) { + return true; + } + auto source_uvs = maybe_source_uvs.value(); VertexBufferBuilder vtx_builder; vtx_builder.AddVertices({ - {Point(0, 0), Point(uv[0], uv[1]), Point(uv_src[0], uv_src[1])}, - {Point(1, 0), Point(uv[2], uv[1]), Point(uv_src[2], uv_src[1])}, - {Point(1, 1), Point(uv[2], uv[3]), Point(uv_src[2], uv_src[3])}, - {Point(0, 0), Point(uv[0], uv[1]), Point(uv_src[0], uv_src[1])}, - {Point(1, 1), Point(uv[2], uv[3]), Point(uv_src[2], uv_src[3])}, - {Point(0, 1), Point(uv[0], uv[3]), Point(uv_src[0], uv_src[3])}, + {Point(0, 0), input_uvs[0], source_uvs[0]}, + {Point(1, 0), input_uvs[1], source_uvs[1]}, + {Point(1, 1), input_uvs[3], source_uvs[3]}, + {Point(0, 0), input_uvs[0], source_uvs[0]}, + {Point(1, 1), input_uvs[3], source_uvs[3]}, + {Point(0, 1), input_uvs[2], source_uvs[2]}, }); auto vtx_buffer = vtx_builder.CreateVertexBuffer(host_buffer); + auto transformed_blur = entity.GetTransformation().TransformDirection( + blur_direction_ * blur_sigma_.sigma); + VS::FrameInfo frame_info; - frame_info.texture_size = Point(input_bounds->size); + frame_info.texture_size = Point(input_snapshot->GetCoverage().value().size); frame_info.blur_sigma = transformed_blur.GetLength(); frame_info.blur_radius = Radius{Sigma{frame_info.blur_sigma}}.radius; - frame_info.blur_direction = transformed_blur.Normalize(); + frame_info.blur_direction = input_snapshot->transform.Invert() + .TransformDirection(transformed_blur) + .Normalize(); frame_info.src_factor = src_color_factor_; frame_info.inner_blur_factor = inner_blur_factor_; frame_info.outer_blur_factor = outer_blur_factor_; - auto sampler = renderer.GetContext()->GetSamplerLibrary()->GetSampler({}); + SamplerDescriptor sampler_desc; + sampler_desc.min_filter = MinMagFilter::kLinear; + sampler_desc.mag_filter = MinMagFilter::kLinear; + auto sampler = + renderer.GetContext()->GetSamplerLibrary()->GetSampler(sampler_desc); Command cmd; cmd.label = "Gaussian Blur Filter"; @@ -169,8 +153,8 @@ bool DirectionalGaussianBlurFilterContents::RenderFilter( cmd.pipeline = renderer.GetGaussianBlurPipeline(options); cmd.BindVertices(vtx_buffer); - FS::BindTextureSampler(cmd, input->texture, sampler); - FS::BindAlphaMaskSampler(cmd, source_texture->texture, sampler); + FS::BindTextureSampler(cmd, input_snapshot->texture, sampler); + FS::BindAlphaMaskSampler(cmd, source_snapshot->texture, sampler); frame_info.mvp = Matrix::MakeOrthographic(ISize(1, 1)); auto uniform_view = host_buffer.EmplaceUniform(frame_info); @@ -181,8 +165,8 @@ bool DirectionalGaussianBlurFilterContents::RenderFilter( std::optional DirectionalGaussianBlurFilterContents::GetCoverage( const Entity& entity) const { - auto bounds = FilterContents::GetCoverage(entity); - if (!bounds.has_value()) { + auto coverage = FilterContents::GetCoverage(entity); + if (!coverage.has_value()) { return std::nullopt; } @@ -191,8 +175,8 @@ std::optional DirectionalGaussianBlurFilterContents::GetCoverage( .TransformDirection(blur_direction_ * ceil(Radius{blur_sigma_}.radius)) .Abs(); - auto extent = bounds->size + transformed_blur_vector * 2; - return Rect(bounds->origin - transformed_blur_vector, + auto extent = coverage->size + transformed_blur_vector * 2; + return Rect(coverage->origin - transformed_blur_vector, Size(extent.x, extent.y)); } diff --git a/impeller/entity/contents/filters/gaussian_blur_filter_contents.h b/impeller/entity/contents/filters/gaussian_blur_filter_contents.h index 098cab0f9de5e..cc686c0459c2b 100644 --- a/impeller/entity/contents/filters/gaussian_blur_filter_contents.h +++ b/impeller/entity/contents/filters/gaussian_blur_filter_contents.h @@ -35,7 +35,7 @@ class DirectionalGaussianBlurFilterContents final : public FilterContents { const ContentContext& renderer, const Entity& entity, RenderPass& pass, - const Rect& bounds) const override; + const Rect& coverage) const override; Sigma blur_sigma_; Vector2 blur_direction_; BlurStyle blur_style_ = BlurStyle::kNormal; diff --git a/impeller/entity/contents/snapshot.cc b/impeller/entity/contents/snapshot.cc index 76809d7cb5f0a..a980c2b838c12 100644 --- a/impeller/entity/contents/snapshot.cc +++ b/impeller/entity/contents/snapshot.cc @@ -11,35 +11,28 @@ namespace impeller { -std::optional Snapshot::FromTransformedTexture( - const ContentContext& renderer, - const Entity& entity, - std::shared_ptr texture) { - auto bounds = entity.GetPathCoverage(); - if (!bounds.has_value()) { +std::optional Snapshot::GetCoverage() const { + if (!texture) { return std::nullopt; } + return Rect(Size(texture->GetSize())).TransformBounds(transform); +} - auto result = renderer.MakeSubpass( - ISize(bounds->size), - [&texture, &entity, &bounds](const ContentContext& renderer, - RenderPass& pass) -> bool { - TextureContents contents; - contents.SetTexture(texture); - contents.SetSourceRect(Rect::MakeSize(Size(texture->GetSize()))); - Entity sub_entity; - sub_entity.SetPath(entity.GetPath()); - sub_entity.SetBlendMode(Entity::BlendMode::kSource); - sub_entity.SetTransformation( - Matrix::MakeTranslation(Vector3(-bounds->origin)) * - entity.GetTransformation()); - return contents.Render(renderer, sub_entity, pass); - }); - if (!result) { +std::optional Snapshot::GetUVTransform() const { + if (!texture || texture->GetSize().IsZero()) { return std::nullopt; } + return Matrix::MakeScale(1 / Vector2(texture->GetSize())) * + transform.Invert(); +} - return Snapshot{.texture = result, .position = bounds->origin}; +std::optional> Snapshot::GetCoverageUVs( + const Rect& coverage) const { + auto uv_transform = GetUVTransform(); + if (!uv_transform.has_value()) { + return std::nullopt; + } + return coverage.GetTransformedPoints(uv_transform.value()); } } // namespace impeller diff --git a/impeller/entity/contents/snapshot.h b/impeller/entity/contents/snapshot.h index 4169758ee1f94..a0fe419b069bf 100644 --- a/impeller/entity/contents/snapshot.h +++ b/impeller/entity/contents/snapshot.h @@ -21,16 +21,19 @@ class Entity; /// Represents a texture and its intended draw position. struct Snapshot { std::shared_ptr texture; - /// The offset from the origin where this texture is intended to be - /// rendered. - Vector2 position; - - /// Transform a texture by the given `entity`'s transformation matrix to a new - /// texture. - static std::optional FromTransformedTexture( - const ContentContext& renderer, - const Entity& entity, - std::shared_ptr texture); + /// The transform that should be applied to this texture for rendering. + Matrix transform; + + std::optional GetCoverage() const; + + /// @brief Get the transform that converts screen space coordinates to the UV + /// space of this snapshot. + std::optional GetUVTransform() const; + + /// @brief Map a coverage rect to this filter input's UV space. + /// Result order: Top left, top right, bottom left, bottom right. + std::optional> GetCoverageUVs( + const Rect& coverage) const; }; } // namespace impeller diff --git a/impeller/entity/entity_unittests.cc b/impeller/entity/entity_unittests.cc index 11f12e6043648..b2ce20529d1b3 100644 --- a/impeller/entity/entity_unittests.cc +++ b/impeller/entity/entity_unittests.cc @@ -733,8 +733,9 @@ TEST_F(EntityTest, GaussianBlurFilter) { } ImGui::End(); - auto blend = FilterContents::MakeBlend( - Entity::BlendMode::kPlus, FilterInput::Make({boston, bridge, bridge})); + auto blend = + FilterContents::MakeBlend(Entity::BlendMode::kScreen, + FilterInput::Make({boston, bridge, kalimba})); auto blur = FilterContents::MakeGaussianBlur( FilterInput::Make(blend), FilterContents::Sigma{blur_amount[0]}, @@ -745,7 +746,7 @@ TEST_F(EntityTest, GaussianBlurFilter) { auto rect = Rect(-Point(input_size) / 2, Size(input_size)); auto ctm = Matrix::MakeTranslation(Vector3(offset[0], offset[1])) * Matrix::MakeRotationZ(Radians(rotation)) * - Matrix::MakeScale(Vector3(scale[0], scale[1])) * + Matrix::MakeScale(Vector2(scale[0], scale[1])) * Matrix::MakeSkew(skew[0], skew[1]); auto target_contents = blur; diff --git a/impeller/entity/shaders/texture_blend_screen.vert b/impeller/entity/shaders/texture_blend_screen.vert index c84a1c03ca770..715dd3dead9fe 100644 --- a/impeller/entity/shaders/texture_blend_screen.vert +++ b/impeller/entity/shaders/texture_blend_screen.vert @@ -4,20 +4,18 @@ uniform FrameInfo { mat4 mvp; - mat4 dst_uv_transform; - mat4 src_uv_transform; -} frame_info; +} +frame_info; in vec2 vertices; -in vec2 texture_coords; +in vec2 dst_texture_coords; +in vec2 src_texture_coords; out vec2 v_dst_texture_coords; out vec2 v_src_texture_coords; void main() { gl_Position = frame_info.mvp * vec4(vertices, 0.0, 1.0); - v_dst_texture_coords = - (frame_info.dst_uv_transform * vec4(texture_coords, 1.0, 1.0)).xy; - v_src_texture_coords = - (frame_info.src_uv_transform * vec4(texture_coords, 1.0, 1.0)).xy; + v_dst_texture_coords = dst_texture_coords; + v_src_texture_coords = src_texture_coords; } diff --git a/impeller/geometry/geometry_unittests.cc b/impeller/geometry/geometry_unittests.cc index de6902c9d9602..82eb5633b50b1 100644 --- a/impeller/geometry/geometry_unittests.cc +++ b/impeller/geometry/geometry_unittests.cc @@ -57,6 +57,19 @@ TEST(GeometryTest, InvertMultMatrix) { } } +TEST(GeometryTest, MatrixBasis) { + auto matrix = Matrix{1, 2, 3, 4, // + 5, 6, 7, 8, // + 9, 10, 11, 12, // + 13, 14, 15, 16}; + auto basis = matrix.Basis(); + auto expect = Matrix{1, 2, 3, 0, // + 5, 6, 7, 0, // + 9, 10, 11, 0, // + 0, 0, 0, 1}; + ASSERT_MATRIX_NEAR(basis, expect); +} + TEST(GeometryTest, MutliplicationMatrix) { auto rotation = Matrix::MakeRotationZ(Radians{M_PI_4}); auto invert = rotation.Invert(); @@ -862,6 +875,15 @@ TEST(GeometryTest, RectGetPoints) { ASSERT_POINT_NEAR(points[3], Point(400, 600)); } +TEST(GeometryTest, RectGetTransformedPoints) { + Rect r(100, 200, 300, 400); + auto points = r.GetTransformedPoints(Matrix::MakeTranslation({10, 20})); + ASSERT_POINT_NEAR(points[0], Point(110, 220)); + ASSERT_POINT_NEAR(points[1], Point(410, 220)); + ASSERT_POINT_NEAR(points[2], Point(110, 620)); + ASSERT_POINT_NEAR(points[3], Point(410, 620)); +} + TEST(GeometryTest, RectMakePointBounds) { { Rect r = diff --git a/impeller/geometry/matrix.h b/impeller/geometry/matrix.h index 10051467f45dd..8938816fa9c72 100644 --- a/impeller/geometry/matrix.h +++ b/impeller/geometry/matrix.h @@ -165,6 +165,17 @@ struct Matrix { // clang-format on } + constexpr Matrix Basis() const { + // clang-format off + return Matrix( + m[0], m[1], m[2], 0.0, + m[4], m[5], m[6], 0.0, + m[8], m[9], m[10], 0.0, + 0.0, 0.0, 0.0, 1.0 + ); + // clang-format on + } + constexpr Matrix Translate(const Vector3& t) const { // clang-format off return Matrix(m[0], m[1], m[2], m[3], diff --git a/impeller/geometry/rect.h b/impeller/geometry/rect.h index 514cb9c90ffff..bd08001b11a0e 100644 --- a/impeller/geometry/rect.h +++ b/impeller/geometry/rect.h @@ -139,21 +139,24 @@ struct TRect { } constexpr std::array, 4> GetPoints() const { - const auto left = std::min(origin.x, origin.x + size.width); - const auto top = std::min(origin.y, origin.y + size.height); - const auto right = std::max(origin.x, origin.x + size.width); - const auto bottom = std::max(origin.y, origin.y + size.height); + auto [left, top, right, bottom] = GetLTRB(); return {TPoint(left, top), TPoint(right, top), TPoint(left, bottom), TPoint(right, bottom)}; } - /// @brief Creates a new bounding box that contains this transformed - /// rectangle. - constexpr TRect TransformBounds(const Matrix& transform) const { + constexpr std::array, 4> GetTransformedPoints( + const Matrix& transform) const { auto points = GetPoints(); for (uint i = 0; i < points.size(); i++) { points[i] = transform * points[i]; } + return points; + } + + /// @brief Creates a new bounding box that contains this transformed + /// rectangle. + constexpr TRect TransformBounds(const Matrix& transform) const { + auto points = GetTransformedPoints(transform); return TRect::MakePointBounds({points.begin(), points.end()}).value(); } From c34c0dccbbdbc6da132c4c77f628a25dfef61bb7 Mon Sep 17 00:00:00 2001 From: Dan Field Date: Thu, 14 Apr 2022 10:30:42 -0700 Subject: [PATCH 404/433] Move static methods to anonymous namespace (#137) --- .../backend/metal/command_buffer_mtl.mm | 112 +++++++++--------- 1 file changed, 58 insertions(+), 54 deletions(-) diff --git a/impeller/renderer/backend/metal/command_buffer_mtl.mm b/impeller/renderer/backend/metal/command_buffer_mtl.mm index 4382c6d8c8d64..7e5cacfb3df17 100644 --- a/impeller/renderer/backend/metal/command_buffer_mtl.mm +++ b/impeller/renderer/backend/metal/command_buffer_mtl.mm @@ -7,55 +7,11 @@ #include "impeller/renderer/backend/metal/render_pass_mtl.h" namespace impeller { - -id CreateCommandBuffer(id queue) { - if (@available(iOS 14.0, macOS 11.0, *)) { - auto desc = [[MTLCommandBufferDescriptor alloc] init]; - // Degrades CPU performance slightly but is well worth the cost for typical - // Impeller workloads. - desc.errorOptions = MTLCommandBufferErrorOptionEncoderExecutionStatus; - return [queue commandBufferWithDescriptor:desc]; - } - return [queue commandBuffer]; -} - -CommandBufferMTL::CommandBufferMTL(id queue) - : buffer_(CreateCommandBuffer(queue)) { - if (!buffer_) { - return; - } - is_valid_ = true; -} - -CommandBufferMTL::~CommandBufferMTL() = default; - -bool CommandBufferMTL::IsValid() const { - return is_valid_; -} - -void CommandBufferMTL::SetLabel(const std::string& label) const { - if (label.empty()) { - return; - } - - [buffer_ setLabel:@(label.data())]; -} - -static CommandBuffer::Status ToCommitResult(MTLCommandBufferStatus status) { - switch (status) { - case MTLCommandBufferStatusCompleted: - return CommandBufferMTL::Status::kCompleted; - case MTLCommandBufferStatusEnqueued: - return CommandBufferMTL::Status::kPending; - default: - break; - } - return CommandBufferMTL::Status::kError; -} - +namespace { // TODO(dnfield): remove this declaration when we no longer need to build on -// machines with lower SDK versions than 11.0.s -#if !defined(MAC_OS_VERSION_11_0) || MAC_OS_X_VERSION_MAX_ALLOWED < MAC_OS_VERSION_11_0 +// machines with lower SDK versions than 11.0. +#if !defined(MAC_OS_VERSION_11_0) || \ + MAC_OS_X_VERSION_MAX_ALLOWED < MAC_OS_VERSION_11_0 typedef NS_ENUM(NSInteger, MTLCommandEncoderErrorState) { MTLCommandEncoderErrorStateUnknown = 0, MTLCommandEncoderErrorStateCompleted = 1, @@ -65,12 +21,6 @@ typedef NS_ENUM(NSInteger, MTLCommandEncoderErrorState) { } API_AVAILABLE(macos(11.0), ios(14.0)); #endif - -#if !defined(MAC_OS_VERSION_12_0) || MAC_OS_X_VERSION_MAX_ALLOWED < MAC_OS_VERSION_12_0 -constexpr int MTLCommandBufferErrorAccessRevoked = 4; -constexpr int MTLCommandBufferErrorStackOverflow = 12; -#endif - API_AVAILABLE(ios(14.0), macos(11.0)) NSString* MTLCommandEncoderErrorStateToString( MTLCommandEncoderErrorState state) { @@ -89,6 +39,14 @@ typedef NS_ENUM(NSInteger, MTLCommandEncoderErrorState) { return @"unknown"; } +// TODO(dnfield): This can be removed when all bots have been sufficiently +// upgraded for MAC_OS_VERSION_12_0. +#if !defined(MAC_OS_VERSION_12_0) || \ + MAC_OS_X_VERSION_MAX_ALLOWED < MAC_OS_VERSION_12_0 +constexpr int MTLCommandBufferErrorAccessRevoked = 4; +constexpr int MTLCommandBufferErrorStackOverflow = 12; +#endif + static NSString* MTLCommandBufferErrorToString(MTLCommandBufferError code) { switch (code) { case MTLCommandBufferErrorNone: @@ -173,6 +131,52 @@ static void LogMTLCommandBufferErrorIfPresent(id buffer) { stream << "<<<<<<<"; VALIDATION_LOG << stream.str(); } +} // namespace + +id CreateCommandBuffer(id queue) { + if (@available(iOS 14.0, macOS 11.0, *)) { + auto desc = [[MTLCommandBufferDescriptor alloc] init]; + // Degrades CPU performance slightly but is well worth the cost for typical + // Impeller workloads. + desc.errorOptions = MTLCommandBufferErrorOptionEncoderExecutionStatus; + return [queue commandBufferWithDescriptor:desc]; + } + return [queue commandBuffer]; +} + +CommandBufferMTL::CommandBufferMTL(id queue) + : buffer_(CreateCommandBuffer(queue)) { + if (!buffer_) { + return; + } + is_valid_ = true; +} + +CommandBufferMTL::~CommandBufferMTL() = default; + +bool CommandBufferMTL::IsValid() const { + return is_valid_; +} + +void CommandBufferMTL::SetLabel(const std::string& label) const { + if (label.empty()) { + return; + } + + [buffer_ setLabel:@(label.data())]; +} + +static CommandBuffer::Status ToCommitResult(MTLCommandBufferStatus status) { + switch (status) { + case MTLCommandBufferStatusCompleted: + return CommandBufferMTL::Status::kCompleted; + case MTLCommandBufferStatusEnqueued: + return CommandBufferMTL::Status::kPending; + default: + break; + } + return CommandBufferMTL::Status::kError; +} bool CommandBufferMTL::SubmitCommands(CompletionCallback callback) { if (!buffer_) { From ef93d060c2e1407a6a95138cc798821af7d99479 Mon Sep 17 00:00:00 2001 From: Zachary Anderson Date: Thu, 14 Apr 2022 12:14:27 -0700 Subject: [PATCH 405/433] Adds --sksl target to impellerc (#131) --- impeller/compiler/compiler.cc | 49 ++++++++++++++-- impeller/compiler/compiler.h | 5 ++ impeller/compiler/compiler_unittests.cc | 13 ++++- impeller/compiler/impellerc_main.cc | 76 +++++++++++++++---------- impeller/compiler/switches.cc | 3 +- impeller/fixtures/BUILD.gn | 5 +- 6 files changed, 109 insertions(+), 42 deletions(-) diff --git a/impeller/compiler/compiler.cc b/impeller/compiler/compiler.cc index 7ae15aab5806d..8e1150d4cf376 100644 --- a/impeller/compiler/compiler.cc +++ b/impeller/compiler/compiler.cc @@ -206,6 +206,7 @@ CompilerTargetPlatformToCompilerMSLTargetPlatform( return spirv_cross::CompilerMSL::Options::Platform::iOS; case Compiler::TargetPlatform::kMacOS: // Unknown should not happen due to prior validation. + case Compiler::TargetPlatform::kFlutterSPIRV: case Compiler::TargetPlatform::kUnknown: return spirv_cross::CompilerMSL::Options::Platform::macOS; } @@ -239,18 +240,29 @@ Compiler::Compiler(const fml::Mapping& source_mapping, // will be processed later by backend specific compilers. So optimizations // here are irrelevant and get in the way of generating reflection code. options.SetGenerateDebugInfo(); - options.SetOptimizationLevel( - shaderc_optimization_level::shaderc_optimization_level_zero); // Expects GLSL 4.60 (Core Profile). // https://www.khronos.org/registry/OpenGL/specs/gl/GLSLangSpec.4.60.pdf options.SetSourceLanguage( shaderc_source_language::shaderc_source_language_glsl); options.SetForcedVersionProfile(460, shaderc_profile::shaderc_profile_core); - options.SetTargetEnvironment( - shaderc_target_env::shaderc_target_env_vulkan, - shaderc_env_version::shaderc_env_version_vulkan_1_1); - options.SetTargetSpirv(shaderc_spirv_version::shaderc_spirv_version_1_3); + if (source_options.target_platform == TargetPlatform::kFlutterSPIRV) { + options.SetOptimizationLevel( + shaderc_optimization_level::shaderc_optimization_level_size); + options.SetTargetEnvironment( + shaderc_target_env::shaderc_target_env_opengl, + shaderc_env_version::shaderc_env_version_opengl_4_5 + ); + options.SetTargetSpirv(shaderc_spirv_version::shaderc_spirv_version_1_0); + } else { + options.SetOptimizationLevel( + shaderc_optimization_level::shaderc_optimization_level_zero); + options.SetTargetEnvironment( + shaderc_target_env::shaderc_target_env_vulkan, + shaderc_env_version::shaderc_env_version_vulkan_1_1 + ); + options.SetTargetSpirv(shaderc_spirv_version::shaderc_spirv_version_1_3); + } options.SetAutoBindUniforms(true); options.SetAutoMapLocations(true); @@ -295,6 +307,11 @@ Compiler::Compiler(const fml::Mapping& source_mapping, included_file_names_ = std::move(included_file_names); } + if (!TargetPlatformNeedsMSL(source_options.target_platform)) { + is_valid_ = true; + return; + } + // MSL Generation. spirv_cross::Parser parser(spv_result_->cbegin(), spv_result_->cend() - spv_result_->cbegin()); @@ -418,6 +435,26 @@ std::string Compiler::EntryPointFromSourceName(const std::string& file_name, return stream.str(); } +bool Compiler::TargetPlatformNeedsMSL(TargetPlatform platform) { + switch (platform) { + case TargetPlatform::kIPhoneOS: + case TargetPlatform::kMacOS: + return true; + default: + return false; + } +} + +bool Compiler::TargetPlatformNeedsReflection(TargetPlatform platform) { + switch (platform) { + case TargetPlatform::kIPhoneOS: + case TargetPlatform::kMacOS: + return true; + default: + return false; + } +} + std::string Compiler::GetSourcePrefix() const { std::stringstream stream; stream << options_.file_name << ": "; diff --git a/impeller/compiler/compiler.h b/impeller/compiler/compiler.h index 9310bdc9bcf93..6acbde58e618d 100644 --- a/impeller/compiler/compiler.h +++ b/impeller/compiler/compiler.h @@ -31,6 +31,7 @@ class Compiler { kUnknown, kMacOS, kIPhoneOS, + kFlutterSPIRV, }; static SourceType SourceTypeFromFileName(const std::string& file_name); @@ -38,6 +39,10 @@ class Compiler { static std::string EntryPointFromSourceName(const std::string& file_name, SourceType type); + static bool TargetPlatformNeedsMSL(TargetPlatform platform); + + static bool TargetPlatformNeedsReflection(TargetPlatform platform); + struct SourceOptions { SourceType type = SourceType::kUnknown; TargetPlatform target_platform = TargetPlatform::kUnknown; diff --git a/impeller/compiler/compiler_unittests.cc b/impeller/compiler/compiler_unittests.cc index eca4561887772..3ca5da7ca8613 100644 --- a/impeller/compiler/compiler_unittests.cc +++ b/impeller/compiler/compiler_unittests.cc @@ -23,14 +23,15 @@ class CompilerTest : public ::testing::Test { ~CompilerTest() = default; - bool CanCompileFixture(const char* fixture_name) const { + bool CanCompileFixture(const char* fixture_name, + Compiler::TargetPlatform target_platform) const { auto fixture = flutter::testing::OpenFixtureAsMapping(fixture_name); if (!fixture->GetMapping()) { VALIDATION_LOG << "Could not find shader in fixtures: " << fixture_name; return false; } Compiler::SourceOptions compiler_options(fixture_name); - compiler_options.target_platform = Compiler::TargetPlatform::kMacOS; + compiler_options.target_platform = target_platform; compiler_options.working_directory = std::make_shared( flutter::testing::OpenFixturesDirectory()); Reflector::Options reflector_options; @@ -60,7 +61,13 @@ TEST_F(CompilerTest, ShaderKindMatchingIsSuccessful) { } TEST_F(CompilerTest, CanCompileSample) { - ASSERT_TRUE(CanCompileFixture("sample.vert")); + ASSERT_TRUE(CanCompileFixture( + "sample.vert", Compiler::TargetPlatform::kMacOS)); +} + +TEST_F(CompilerTest, CanTargetFlutterSPIRV) { + ASSERT_TRUE(CanCompileFixture( + "test_texture.frag", Compiler::TargetPlatform::kFlutterSPIRV)); } } // namespace testing diff --git a/impeller/compiler/impellerc_main.cc b/impeller/compiler/impellerc_main.cc index 8dd49a9a25951..bb25d8daa59e3 100644 --- a/impeller/compiler/impellerc_main.cc +++ b/impeller/compiler/impellerc_main.cc @@ -69,49 +69,65 @@ bool Main(const fml::CommandLine& command_line) { return false; } - if (!fml::WriteAtomically(*switches.working_directory, - switches.metal_file_name.c_str(), - *compiler.GetMSLShaderSource())) { - std::cerr << "Could not write file to " << switches.spirv_file_name - << std::endl; - return false; - } - - if (!switches.reflection_json_name.empty()) { + if (Compiler::TargetPlatformNeedsMSL(options.target_platform)) { if (!fml::WriteAtomically(*switches.working_directory, - switches.reflection_json_name.c_str(), - *compiler.GetReflector()->GetReflectionJSON())) { - std::cerr << "Could not write reflection json to " - << switches.reflection_json_name << std::endl; + switches.metal_file_name.c_str(), + *compiler.GetMSLShaderSource())) { + std::cerr << "Could not write file to " << switches.spirv_file_name + << std::endl; return false; } } - if (!switches.reflection_header_name.empty()) { - if (!fml::WriteAtomically( - *switches.working_directory, - switches.reflection_header_name.c_str(), - *compiler.GetReflector()->GetReflectionHeader())) { - std::cerr << "Could not write reflection header to " - << switches.reflection_header_name << std::endl; - return false; + + if (Compiler::TargetPlatformNeedsReflection(options.target_platform)) { + if (!switches.reflection_json_name.empty()) { + if (!fml::WriteAtomically(*switches.working_directory, + switches.reflection_json_name.c_str(), + *compiler.GetReflector()->GetReflectionJSON())) { + std::cerr << "Could not write reflection json to " + << switches.reflection_json_name << std::endl; + return false; + } } - } - if (!switches.reflection_cc_name.empty()) { - if (!fml::WriteAtomically(*switches.working_directory, - switches.reflection_cc_name.c_str(), - *compiler.GetReflector()->GetReflectionCC())) { - std::cerr << "Could not write reflection CC to " - << switches.reflection_cc_name << std::endl; - return false; + if (!switches.reflection_header_name.empty()) { + if (!fml::WriteAtomically( + *switches.working_directory, + switches.reflection_header_name.c_str(), + *compiler.GetReflector()->GetReflectionHeader())) { + std::cerr << "Could not write reflection header to " + << switches.reflection_header_name << std::endl; + return false; + } + } + + if (!switches.reflection_cc_name.empty()) { + if (!fml::WriteAtomically(*switches.working_directory, + switches.reflection_cc_name.c_str(), + *compiler.GetReflector()->GetReflectionCC())) { + std::cerr << "Could not write reflection CC to " + << switches.reflection_cc_name << std::endl; + return false; + } } } if (!switches.depfile_path.empty()) { + std::string result_file; + switch (switches.target_platform) { + case Compiler::TargetPlatform::kMacOS: + case Compiler::TargetPlatform::kIPhoneOS: + result_file = switches.metal_file_name; + break; + case Compiler::TargetPlatform::kFlutterSPIRV: + case Compiler::TargetPlatform::kUnknown: + result_file = switches.spirv_file_name; + break; + } if (!fml::WriteAtomically( *switches.working_directory, switches.depfile_path.c_str(), - *compiler.CreateDepfileContents({switches.metal_file_name}))) { + *compiler.CreateDepfileContents({result_file}))) { std::cerr << "Could not write depfile to " << switches.depfile_path << std::endl; return false; diff --git a/impeller/compiler/switches.cc b/impeller/compiler/switches.cc index 5ab2bf7453cdb..ef55ae07522e3 100644 --- a/impeller/compiler/switches.cc +++ b/impeller/compiler/switches.cc @@ -15,6 +15,7 @@ namespace compiler { static const std::map kKnownPlatforms = { {"macos", Compiler::TargetPlatform::kMacOS}, {"ios", Compiler::TargetPlatform::kIPhoneOS}, + {"flutter-spirv", Compiler::TargetPlatform::kFlutterSPIRV}, }; void Switches::PrintHelp(std::ostream& stream) { @@ -112,7 +113,7 @@ bool Switches::AreValid(std::ostream& explain) const { valid = false; } - if (metal_file_name.empty()) { + if (metal_file_name.empty() && Compiler::TargetPlatformNeedsMSL(target_platform)) { explain << "Metal file name was empty." << std::endl; valid = false; } diff --git a/impeller/fixtures/BUILD.gn b/impeller/fixtures/BUILD.gn index 07bbc65499781..64bf53e9e5b54 100644 --- a/impeller/fixtures/BUILD.gn +++ b/impeller/fixtures/BUILD.gn @@ -21,13 +21,14 @@ impeller_shaders("shader_fixtures") { test_fixtures("file_fixtures") { fixtures = [ - "sample.vert", - "types.h", "airplane.jpg", "bay_bridge.jpg", "boston.jpg", "embarcadero.jpg", "kalimba.jpg", + "sample.vert", + "types.h", + "test_texture.frag", "//flutter/third_party/txt/third_party/fonts/Roboto-Regular.ttf", "//flutter/third_party/txt/third_party/fonts/NotoColorEmoji.ttf", "//flutter/third_party/txt/third_party/fonts/HomemadeApple.ttf", From f55545d077bde8971cf7a4b1dff627864e36bb96 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 14 Apr 2022 12:26:55 -0700 Subject: [PATCH 406/433] Bump github/codeql-action from 2.1.7 to 2.1.8 (#128) Bumps [github/codeql-action](https://github.com/github/codeql-action) from 2.1.7 to 2.1.8. - [Release notes](https://github.com/github/codeql-action/releases) - [Changelog](https://github.com/github/codeql-action/blob/main/CHANGELOG.md) - [Commits](https://github.com/github/codeql-action/compare/0182a2c78c8a55b763909348834ed54d735ab3e2...1ed1437484560351c5be56cf73a48a279d116b78) --- updated-dependencies: - dependency-name: github/codeql-action dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- impeller/.github/workflows/scorecards-analysis.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/impeller/.github/workflows/scorecards-analysis.yml b/impeller/.github/workflows/scorecards-analysis.yml index f0245bae97f1e..92f3bc928f368 100644 --- a/impeller/.github/workflows/scorecards-analysis.yml +++ b/impeller/.github/workflows/scorecards-analysis.yml @@ -48,6 +48,6 @@ jobs: # Upload the results to GitHub's code scanning dashboard. - name: "Upload to code-scanning" - uses: github/codeql-action/upload-sarif@0182a2c78c8a55b763909348834ed54d735ab3e2 + uses: github/codeql-action/upload-sarif@1ed1437484560351c5be56cf73a48a279d116b78 with: sarif_file: results.sarif From aa4c2563a6b5209750514c81e07883bf2d623388 Mon Sep 17 00:00:00 2001 From: Brandon DeRosier Date: Thu, 14 Apr 2022 16:33:14 -0700 Subject: [PATCH 407/433] Border mask blur (#132) --- impeller/entity/BUILD.gn | 4 + impeller/entity/contents/content_context.cc | 2 + impeller/entity/contents/content_context.h | 10 ++ .../border_mask_blur_filter_contents.cc | 131 ++++++++++++++++++ .../border_mask_blur_filter_contents.h | 44 ++++++ .../contents/filters/filter_contents.cc | 13 ++ .../entity/contents/filters/filter_contents.h | 6 + .../filters/gaussian_blur_filter_contents.h | 1 - impeller/entity/entity_unittests.cc | 15 +- impeller/entity/shaders/border_mask_blur.frag | 60 ++++++++ impeller/entity/shaders/border_mask_blur.vert | 32 +++++ impeller/geometry/matrix.h | 6 + 12 files changed, 320 insertions(+), 4 deletions(-) create mode 100644 impeller/entity/contents/filters/border_mask_blur_filter_contents.cc create mode 100644 impeller/entity/contents/filters/border_mask_blur_filter_contents.h create mode 100644 impeller/entity/shaders/border_mask_blur.frag create mode 100644 impeller/entity/shaders/border_mask_blur.vert diff --git a/impeller/entity/BUILD.gn b/impeller/entity/BUILD.gn index 51c2766dbc99b..3add3ae8c27f7 100644 --- a/impeller/entity/BUILD.gn +++ b/impeller/entity/BUILD.gn @@ -20,6 +20,8 @@ impeller_shaders("entity_shaders") { "shaders/texture_blend_screen.vert", "shaders/gaussian_blur.frag", "shaders/gaussian_blur.vert", + "shaders/border_mask_blur.frag", + "shaders/border_mask_blur.vert", "shaders/texture_fill.frag", "shaders/texture_fill.vert", "shaders/glyph_atlas.frag", @@ -45,6 +47,8 @@ impeller_component("entity") { "contents/filters/filter_input.h", "contents/filters/gaussian_blur_filter_contents.cc", "contents/filters/gaussian_blur_filter_contents.h", + "contents/filters/border_mask_blur_filter_contents.cc", + "contents/filters/border_mask_blur_filter_contents.h", "contents/linear_gradient_contents.cc", "contents/linear_gradient_contents.h", "contents/snapshot.cc", diff --git a/impeller/entity/contents/content_context.cc b/impeller/entity/contents/content_context.cc index 016e0b3d57674..f7a584f4af126 100644 --- a/impeller/entity/contents/content_context.cc +++ b/impeller/entity/contents/content_context.cc @@ -29,6 +29,8 @@ ContentContext::ContentContext(std::shared_ptr context) texture_pipelines_[{}] = std::make_unique(*context_); gaussian_blur_pipelines_[{}] = std::make_unique(*context_); + border_mask_blur_pipelines_[{}] = + std::make_unique(*context_); solid_stroke_pipelines_[{}] = std::make_unique(*context_); glyph_atlas_pipelines_[{}] = std::make_unique(*context_); diff --git a/impeller/entity/contents/content_context.h b/impeller/entity/contents/content_context.h index b1194cc9cd062..9898b217add98 100644 --- a/impeller/entity/contents/content_context.h +++ b/impeller/entity/contents/content_context.h @@ -11,6 +11,8 @@ #include "flutter/fml/macros.h" #include "fml/logging.h" #include "impeller/base/validation.h" +#include "impeller/entity/border_mask_blur.frag.h" +#include "impeller/entity/border_mask_blur.vert.h" #include "impeller/entity/entity.h" #include "impeller/entity/gaussian_blur.frag.h" #include "impeller/entity/gaussian_blur.vert.h" @@ -44,6 +46,8 @@ using TexturePipeline = PipelineT; using GaussianBlurPipeline = PipelineT; +using BorderMaskBlurPipeline = + PipelineT; using SolidStrokePipeline = PipelineT; using GlyphAtlasPipeline = @@ -114,6 +118,11 @@ class ContentContext { return GetPipeline(gaussian_blur_pipelines_, opts); } + std::shared_ptr GetBorderMaskBlurPipeline( + ContentContextOptions opts) const { + return GetPipeline(border_mask_blur_pipelines_, opts); + } + std::shared_ptr GetSolidStrokePipeline( ContentContextOptions opts) const { return GetPipeline(solid_stroke_pipelines_, opts); @@ -156,6 +165,7 @@ class ContentContext { mutable Variants texture_blend_screen_pipelines_; mutable Variants texture_pipelines_; mutable Variants gaussian_blur_pipelines_; + mutable Variants border_mask_blur_pipelines_; mutable Variants solid_stroke_pipelines_; mutable Variants clip_pipelines_; mutable Variants glyph_atlas_pipelines_; diff --git a/impeller/entity/contents/filters/border_mask_blur_filter_contents.cc b/impeller/entity/contents/filters/border_mask_blur_filter_contents.cc new file mode 100644 index 0000000000000..db1d5272acce1 --- /dev/null +++ b/impeller/entity/contents/filters/border_mask_blur_filter_contents.cc @@ -0,0 +1,131 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "impeller/entity/contents/filters/border_mask_blur_filter_contents.h" +#include "impeller/entity/contents/content_context.h" + +#include "impeller/entity/contents/contents.h" +#include "impeller/renderer/render_pass.h" +#include "impeller/renderer/sampler_library.h" + +namespace impeller { + +BorderMaskBlurFilterContents::BorderMaskBlurFilterContents() = default; + +BorderMaskBlurFilterContents::~BorderMaskBlurFilterContents() = default; + +void BorderMaskBlurFilterContents::SetSigma(Sigma sigma_x, Sigma sigma_y) { + sigma_x_ = sigma_x; + sigma_y_ = sigma_y; +} + +void BorderMaskBlurFilterContents::SetBlurStyle(BlurStyle blur_style) { + blur_style_ = blur_style; + + switch (blur_style) { + case FilterContents::BlurStyle::kNormal: + src_color_factor_ = false; + inner_blur_factor_ = true; + outer_blur_factor_ = true; + break; + case FilterContents::BlurStyle::kSolid: + src_color_factor_ = true; + inner_blur_factor_ = false; + outer_blur_factor_ = true; + break; + case FilterContents::BlurStyle::kOuter: + src_color_factor_ = false; + inner_blur_factor_ = false; + outer_blur_factor_ = true; + break; + case FilterContents::BlurStyle::kInner: + src_color_factor_ = false; + inner_blur_factor_ = true; + outer_blur_factor_ = false; + break; + } +} + +bool BorderMaskBlurFilterContents::RenderFilter( + const FilterInput::Vector& inputs, + const ContentContext& renderer, + const Entity& entity, + RenderPass& pass, + const Rect& coverage) const { + if (inputs.empty()) { + return true; + } + + using VS = BorderMaskBlurPipeline::VertexShader; + using FS = BorderMaskBlurPipeline::FragmentShader; + + auto& host_buffer = pass.GetTransientsBuffer(); + + auto input_snapshot = inputs[0]->GetSnapshot(renderer, entity); + if (!input_snapshot.has_value()) { + return true; + } + auto maybe_input_uvs = input_snapshot->GetCoverageUVs(coverage); + if (!maybe_input_uvs.has_value()) { + return true; + } + auto input_uvs = maybe_input_uvs.value(); + + VertexBufferBuilder vtx_builder; + vtx_builder.AddVertices({ + {Point(0, 0), input_uvs[0]}, + {Point(1, 0), input_uvs[1]}, + {Point(1, 1), input_uvs[3]}, + {Point(0, 0), input_uvs[0]}, + {Point(1, 1), input_uvs[3]}, + {Point(0, 1), input_uvs[2]}, + }); + auto vtx_buffer = vtx_builder.CreateVertexBuffer(host_buffer); + + Command cmd; + cmd.label = "Border Mask Blur Filter"; + auto options = OptionsFromPass(pass); + options.blend_mode = Entity::BlendMode::kSource; + cmd.pipeline = renderer.GetBorderMaskBlurPipeline(options); + cmd.BindVertices(vtx_buffer); + + VS::FrameInfo frame_info; + frame_info.mvp = Matrix::MakeOrthographic(ISize(1, 1)); + auto scale = entity.GetTransformation().GetScale(); + frame_info.sigma_uv = Vector2(scale.x, scale.y) * + Vector2(sigma_x_.sigma, sigma_y_.sigma).Abs() / + input_snapshot->texture->GetSize(); + frame_info.src_factor = src_color_factor_; + frame_info.inner_blur_factor = inner_blur_factor_; + frame_info.outer_blur_factor = outer_blur_factor_; + auto uniform_view = host_buffer.EmplaceUniform(frame_info); + VS::BindFrameInfo(cmd, uniform_view); + + auto sampler = renderer.GetContext()->GetSamplerLibrary()->GetSampler({}); + FS::BindTextureSampler(cmd, input_snapshot->texture, sampler); + + return pass.AddCommand(std::move(cmd)); +} + +std::optional BorderMaskBlurFilterContents::GetCoverage( + const Entity& entity) const { + auto coverage = FilterContents::GetCoverage(entity); + if (!coverage.has_value()) { + return std::nullopt; + } + + // Technically this works with all of our current filters, but this should be + // using the input[0] transform, not the entity transform! + // See: https://github.com/flutter/impeller/pull/130#issuecomment-1098892423 + auto transformed_blur_vector = + entity.GetTransformation() + .TransformDirection( + Vector2(Radius{sigma_x_}.radius, Radius{sigma_y_}.radius)) + .Abs(); + auto extent = coverage->size + transformed_blur_vector * 2; + return Rect(coverage->origin - transformed_blur_vector, + Size(extent.x, extent.y)); +} + +} // namespace impeller diff --git a/impeller/entity/contents/filters/border_mask_blur_filter_contents.h b/impeller/entity/contents/filters/border_mask_blur_filter_contents.h new file mode 100644 index 0000000000000..dc327dc3f5edb --- /dev/null +++ b/impeller/entity/contents/filters/border_mask_blur_filter_contents.h @@ -0,0 +1,44 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#pragma once + +#include +#include +#include "impeller/entity/contents/filters/filter_contents.h" +#include "impeller/entity/contents/filters/filter_input.h" + +namespace impeller { + +class BorderMaskBlurFilterContents final : public FilterContents { + public: + BorderMaskBlurFilterContents(); + + ~BorderMaskBlurFilterContents() override; + + void SetSigma(Sigma sigma_x, Sigma sigma_y); + + void SetBlurStyle(BlurStyle blur_style); + + // |Contents| + std::optional GetCoverage(const Entity& entity) const override; + + private: + // |FilterContents| + bool RenderFilter(const FilterInput::Vector& input_textures, + const ContentContext& renderer, + const Entity& entity, + RenderPass& pass, + const Rect& coverage) const override; + Sigma sigma_x_; + Sigma sigma_y_; + BlurStyle blur_style_ = BlurStyle::kNormal; + bool src_color_factor_ = false; + bool inner_blur_factor_ = true; + bool outer_blur_factor_ = true; + + FML_DISALLOW_COPY_AND_ASSIGN(BorderMaskBlurFilterContents); +}; + +} // namespace impeller diff --git a/impeller/entity/contents/filters/filter_contents.cc b/impeller/entity/contents/filters/filter_contents.cc index 5b820b09a5188..790faf4a8bb16 100644 --- a/impeller/entity/contents/filters/filter_contents.cc +++ b/impeller/entity/contents/filters/filter_contents.cc @@ -15,6 +15,7 @@ #include "impeller/base/validation.h" #include "impeller/entity/contents/content_context.h" #include "impeller/entity/contents/filters/blend_filter_contents.h" +#include "impeller/entity/contents/filters/border_mask_blur_filter_contents.h" #include "impeller/entity/contents/filters/filter_input.h" #include "impeller/entity/contents/filters/gaussian_blur_filter_contents.h" #include "impeller/entity/contents/texture_contents.h" @@ -88,6 +89,18 @@ std::shared_ptr FilterContents::MakeGaussianBlur( return y_blur; } +std::shared_ptr FilterContents::MakeBorderMaskBlur( + FilterInput::Ref input, + Sigma sigma_x, + Sigma sigma_y, + BlurStyle blur_style) { + auto filter = std::make_shared(); + filter->SetInputs({input}); + filter->SetSigma(sigma_x, sigma_y); + filter->SetBlurStyle(blur_style); + return filter; +} + FilterContents::FilterContents() = default; FilterContents::~FilterContents() = default; diff --git a/impeller/entity/contents/filters/filter_contents.h b/impeller/entity/contents/filters/filter_contents.h index 236bf1193b836..0917d5532b54d 100644 --- a/impeller/entity/contents/filters/filter_contents.h +++ b/impeller/entity/contents/filters/filter_contents.h @@ -96,6 +96,12 @@ class FilterContents : public Contents { Sigma sigma_y, BlurStyle blur_style = BlurStyle::kNormal); + static std::shared_ptr MakeBorderMaskBlur( + FilterInput::Ref input, + Sigma sigma_x, + Sigma sigma_y, + BlurStyle blur_style = BlurStyle::kNormal); + FilterContents(); ~FilterContents() override; diff --git a/impeller/entity/contents/filters/gaussian_blur_filter_contents.h b/impeller/entity/contents/filters/gaussian_blur_filter_contents.h index cc686c0459c2b..b39ec2ceb6d5b 100644 --- a/impeller/entity/contents/filters/gaussian_blur_filter_contents.h +++ b/impeller/entity/contents/filters/gaussian_blur_filter_contents.h @@ -8,7 +8,6 @@ #include #include "impeller/entity/contents/filters/filter_contents.h" #include "impeller/entity/contents/filters/filter_input.h" -#include "impeller/geometry/matrix.h" namespace impeller { diff --git a/impeller/entity/entity_unittests.cc b/impeller/entity/entity_unittests.cc index b2ce20529d1b3..2978d2dd8018a 100644 --- a/impeller/entity/entity_unittests.cc +++ b/impeller/entity/entity_unittests.cc @@ -698,16 +698,18 @@ TEST_F(EntityTest, GaussianBlurFilter) { auto callback = [&](ContentContext& context, RenderPass& pass) -> bool { if (first_frame) { first_frame = false; - ImGui::SetNextWindowSize({500, 220}); - ImGui::SetNextWindowPos({300, 550}); + ImGui::SetNextWindowSize({500, 250}); + ImGui::SetNextWindowPos({300, 500}); } + const char* blur_type_names[] = {"Image blur", "Mask blur"}; const char* blur_style_names[] = {"Normal", "Solid", "Outer", "Inner"}; const FilterContents::BlurStyle blur_styles[] = { FilterContents::BlurStyle::kNormal, FilterContents::BlurStyle::kSolid, FilterContents::BlurStyle::kOuter, FilterContents::BlurStyle::kInner}; // UI state. + static int selected_blur_type = 0; static float blur_amount[2] = {20, 20}; static int selected_blur_style = 0; static Color cover_color(1, 0, 0, 0.2); @@ -719,6 +721,8 @@ TEST_F(EntityTest, GaussianBlurFilter) { ImGui::Begin("Controls"); { + ImGui::Combo("Blur type", &selected_blur_type, blur_type_names, + sizeof(blur_type_names) / sizeof(char*)); ImGui::SliderFloat2("Blur", &blur_amount[0], 0, 200); ImGui::Combo("Blur style", &selected_blur_style, blur_style_names, sizeof(blur_style_names) / sizeof(char*)); @@ -742,6 +746,11 @@ TEST_F(EntityTest, GaussianBlurFilter) { FilterContents::Sigma{blur_amount[1]}, blur_styles[selected_blur_style]); + auto mask_blur = FilterContents::MakeBorderMaskBlur( + FilterInput::Make(boston), FilterContents::Sigma{blur_amount[0]}, + FilterContents::Sigma{blur_amount[1]}, + blur_styles[selected_blur_style]); + ISize input_size = boston->GetSize(); auto rect = Rect(-Point(input_size) / 2, Size(input_size)); auto ctm = Matrix::MakeTranslation(Vector3(offset[0], offset[1])) * @@ -749,7 +758,7 @@ TEST_F(EntityTest, GaussianBlurFilter) { Matrix::MakeScale(Vector2(scale[0], scale[1])) * Matrix::MakeSkew(skew[0], skew[1]); - auto target_contents = blur; + auto target_contents = selected_blur_type == 0 ? blur : mask_blur; Entity entity; entity.SetPath(PathBuilder{}.AddRect(rect).TakePath()); diff --git a/impeller/entity/shaders/border_mask_blur.frag b/impeller/entity/shaders/border_mask_blur.frag new file mode 100644 index 0000000000000..365d9eaa10284 --- /dev/null +++ b/impeller/entity/shaders/border_mask_blur.frag @@ -0,0 +1,60 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +// Constant time mask blur for image borders. +// +// This mask blur extends the geometry of the source image (with clamp border +// sampling) and applies a Gaussian blur to the alpha mask at the edges. +// +// The blur itself works by mapping the Gaussian distribution's indefinite +// integral (using an erf approximation) to the 4 edges of the UV rectangle and +// multiplying them. + +uniform sampler2D texture_sampler; + +in vec2 v_texture_coords; +in vec2 v_sigma_uv; +in float v_src_factor; +in float v_inner_blur_factor; +in float v_outer_blur_factor; + +out vec4 frag_color; + +// Abramowitz and Stegun erf approximation. +float erf(float x) { + float a = abs(x); + // 0.278393*x + 0.230389*x^2 + 0.078108*x^4 + 1 + float b = (0.278393 + (0.230389 + 0.078108 * a * a) * a) * a + 1.0; + return sign(x) * (1 - 1 / (b * b * b * b)); +} + +const float kHalfSqrtTwo = 0.70710678118; + +// Indefinite integral of the Gaussian function (with constant range 0->1). +float GaussianIntegral(float x, float sigma) { + return 0.5 + 0.5 * erf(x * (kHalfSqrtTwo / sigma)); +} + +float BoxBlurMask(vec2 uv) { + // LTRB + return GaussianIntegral(uv.x, v_sigma_uv.x) * // + GaussianIntegral(uv.y, v_sigma_uv.y) * // + GaussianIntegral(1 - uv.x, v_sigma_uv.x) * // + GaussianIntegral(1 - uv.y, v_sigma_uv.y); +} + +void main() { + vec4 image_color = texture(texture_sampler, v_texture_coords); + float blur_factor = BoxBlurMask(v_texture_coords); + + float within_bounds = + float(v_texture_coords.x >= 0 && v_texture_coords.y >= 0 && + v_texture_coords.x < 1 && v_texture_coords.y < 1); + float inner_factor = + (v_inner_blur_factor * blur_factor + v_src_factor) * within_bounds; + float outer_factor = v_outer_blur_factor * blur_factor * (1 - within_bounds); + + float mask_factor = inner_factor + outer_factor; + frag_color = image_color * mask_factor; +} diff --git a/impeller/entity/shaders/border_mask_blur.vert b/impeller/entity/shaders/border_mask_blur.vert new file mode 100644 index 0000000000000..3851a60aeb7fe --- /dev/null +++ b/impeller/entity/shaders/border_mask_blur.vert @@ -0,0 +1,32 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +uniform FrameInfo { + mat4 mvp; + + vec2 sigma_uv; + + float src_factor; + float inner_blur_factor; + float outer_blur_factor; +} +frame_info; + +in vec2 vertices; +in vec2 texture_coords; + +out vec2 v_texture_coords; +out vec2 v_sigma_uv; +out float v_src_factor; +out float v_inner_blur_factor; +out float v_outer_blur_factor; + +void main() { + gl_Position = frame_info.mvp * vec4(vertices, 0.0, 1.0); + v_texture_coords = texture_coords; + v_sigma_uv = frame_info.sigma_uv; + v_src_factor = frame_info.src_factor; + v_inner_blur_factor = frame_info.inner_blur_factor; + v_outer_blur_factor = frame_info.outer_blur_factor; +} diff --git a/impeller/geometry/matrix.h b/impeller/geometry/matrix.h index 8938816fa9c72..0284507e58d8e 100644 --- a/impeller/geometry/matrix.h +++ b/impeller/geometry/matrix.h @@ -236,6 +236,12 @@ struct Matrix { Scalar GetMaxBasisLength() const; + constexpr Vector3 GetScale() const { + return Vector3(Vector3(m[0], m[1], m[2]).Length(), + Vector3(m[4], m[5], m[6]).Length(), + Vector3(m[8], m[9], m[10]).Length()); + } + constexpr bool IsAffine() const { return (m[2] == 0 && m[3] == 0 && m[6] == 0 && m[7] == 0 && m[8] == 0 && m[9] == 0 && m[10] == 1 && m[11] == 0 && m[14] == 0 && m[15] == 1); From 85390522c3239e11b3443b618c35442846dfdb12 Mon Sep 17 00:00:00 2001 From: Chinmay Garde Date: Sat, 16 Apr 2022 19:14:50 -0700 Subject: [PATCH 408/433] Add impellerc options to compile OpenGL Desktop and ES variant shaders. (#139) Also parameterize all unit-tests so all backends are automatically tested. --- impeller/compiler/BUILD.gn | 14 +- impeller/compiler/code_gen_template.h | 6 +- impeller/compiler/compiler.cc | 400 ++++++------------------ impeller/compiler/compiler.h | 42 +-- impeller/compiler/compiler_backend.cc | 84 +++++ impeller/compiler/compiler_backend.h | 57 ++++ impeller/compiler/compiler_test.cc | 167 ++++++++++ impeller/compiler/compiler_test.h | 34 ++ impeller/compiler/compiler_unittests.cc | 77 ++--- impeller/compiler/impellerc_main.cc | 44 +-- impeller/compiler/includer.cc | 117 +++++++ impeller/compiler/includer.h | 59 ++++ impeller/compiler/logger.h | 5 + impeller/compiler/reflector.cc | 19 +- impeller/compiler/reflector.h | 6 +- impeller/compiler/source_options.cc | 18 ++ impeller/compiler/source_options.h | 35 +++ impeller/compiler/switches.cc | 28 +- impeller/compiler/switches.h | 4 +- impeller/compiler/types.cc | 216 +++++++++++++ impeller/compiler/types.h | 58 ++++ impeller/tools/impeller.gni | 6 +- 22 files changed, 1041 insertions(+), 455 deletions(-) create mode 100644 impeller/compiler/compiler_backend.cc create mode 100644 impeller/compiler/compiler_backend.h create mode 100644 impeller/compiler/compiler_test.cc create mode 100644 impeller/compiler/compiler_test.h create mode 100644 impeller/compiler/includer.cc create mode 100644 impeller/compiler/includer.h create mode 100644 impeller/compiler/source_options.cc create mode 100644 impeller/compiler/source_options.h create mode 100644 impeller/compiler/types.cc create mode 100644 impeller/compiler/types.h diff --git a/impeller/compiler/BUILD.gn b/impeller/compiler/BUILD.gn index 64155540c403f..249944ca64abc 100644 --- a/impeller/compiler/BUILD.gn +++ b/impeller/compiler/BUILD.gn @@ -9,12 +9,20 @@ impeller_component("compiler_lib") { "code_gen_template.h", "compiler.cc", "compiler.h", + "compiler_backend.cc", + "compiler_backend.h", "include_dir.h", + "includer.cc", + "includer.h", "logger.h", "reflector.cc", "reflector.h", + "source_options.cc", + "source_options.h", "switches.cc", "switches.h", + "types.cc", + "types.h", "utilities.cc", "utilities.h", ] @@ -52,7 +60,11 @@ impeller_component("compiler_unittests") { output_name = "impellerc_unittests" - sources = [ "compiler_unittests.cc" ] + sources = [ + "compiler_test.cc", + "compiler_test.h", + "compiler_unittests.cc", + ] deps = [ ":compiler_lib", diff --git a/impeller/compiler/code_gen_template.h b/impeller/compiler/code_gen_template.h index 835c2b8cc4c28..dc699e8445558 100644 --- a/impeller/compiler/code_gen_template.h +++ b/impeller/compiler/code_gen_template.h @@ -53,7 +53,7 @@ struct {{camel_case(shader_name)}}{{camel_case(shader_stage)}}Shader { static constexpr auto kResource{{camel_case(buffer.name)}} = ShaderUniformSlot<{{buffer.name}}> { // {{buffer.name}} "{{buffer.name}}", // name - {{buffer.msl_res_0}}u, // binding + {{buffer.ext_res_0}}u, // binding }; {% endfor %} {% endif %} @@ -91,8 +91,8 @@ struct {{camel_case(shader_name)}}{{camel_case(shader_stage)}}Shader { static constexpr auto kResource{{camel_case(sampled_image.name)}} = SampledImageSlot { // {{sampled_image.name}} "{{sampled_image.name}}", // name - {{sampled_image.msl_res_0}}u, // texture - {{sampled_image.msl_res_1}}u, // sampler + {{sampled_image.ext_res_0}}u, // texture + {{sampled_image.ext_res_1}}u, // sampler }; {% endfor %} {% endif %} diff --git a/impeller/compiler/compiler.cc b/impeller/compiler/compiler.cc index 8e1150d4cf376..11cc3c73890f7 100644 --- a/impeller/compiler/compiler.cc +++ b/impeller/compiler/compiler.cc @@ -9,207 +9,62 @@ #include #include "flutter/fml/paths.h" +#include "impeller/compiler/compiler_backend.h" +#include "impeller/compiler/includer.h" #include "impeller/compiler/logger.h" namespace impeller { namespace compiler { -#define COMPILER_ERROR \ - ::impeller::compiler::AutoLogger(error_stream_) << GetSourcePrefix() - -#define COMPILER_ERROR_NO_PREFIX ::impeller::compiler::AutoLogger(error_stream_) - -struct IncluderData { - std::string file_name; - std::unique_ptr mapping; - - IncluderData(std::string p_file_name, std::unique_ptr p_mapping) - : file_name(std::move(p_file_name)), mapping(std::move(p_mapping)) {} -}; - -class Includer final : public shaderc::CompileOptions::IncluderInterface { - public: - Includer(std::shared_ptr working_directory, - std::vector include_dirs, - std::function on_file_included) - : working_directory_(std::move(working_directory)), - include_dirs_(std::move(include_dirs)), - on_file_included_(std::move(on_file_included)) {} - - // |shaderc::CompileOptions::IncluderInterface| - ~Includer() override = default; - - std::unique_ptr TryOpenMapping( - const IncludeDir& dir, - const char* requested_source) { - if (!dir.dir || !dir.dir->is_valid()) { - return nullptr; - } - - if (requested_source == nullptr) { - return nullptr; - } - - std::string source(requested_source); - if (source.empty()) { - return nullptr; - } - - auto mapping = fml::FileMapping::CreateReadOnly(*dir.dir, requested_source); - if (!mapping || !mapping->IsValid()) { - return nullptr; - } - - on_file_included_(fml::paths::JoinPaths({dir.name, requested_source})); - - return mapping; - } - - std::unique_ptr FindFirstMapping( - const char* requested_source) { - // Always try the working directory first no matter what the include - // directories are. - { - IncludeDir dir; - dir.name = "."; - dir.dir = working_directory_; - if (auto mapping = TryOpenMapping(dir, requested_source)) { - return mapping; - } - } - - for (const auto& include_dir : include_dirs_) { - if (auto mapping = TryOpenMapping(include_dir, requested_source)) { - return mapping; - } - } - return nullptr; - } - - // |shaderc::CompileOptions::IncluderInterface| - shaderc_include_result* GetInclude(const char* requested_source, - shaderc_include_type type, - const char* requesting_source, - size_t include_depth) override { - auto result = std::make_unique(); - - // Default initialize to failed inclusion. - result->source_name = ""; - result->source_name_length = 0; - - constexpr const char* kFileNotFoundMessage = "Included file not found."; - result->content = kFileNotFoundMessage; - result->content_length = ::strlen(kFileNotFoundMessage); - result->user_data = nullptr; - - if (!working_directory_ || !working_directory_->is_valid()) { - return result.release(); - } - - if (requested_source == nullptr) { - return result.release(); - } - - auto file = FindFirstMapping(requested_source); - - if (!file || file->GetMapping() == nullptr) { - return result.release(); - } - - auto includer_data = - std::make_unique(requested_source, std::move(file)); - - result->source_name = includer_data->file_name.c_str(); - result->source_name_length = includer_data->file_name.length(); - result->content = reinterpret_castcontent)>( - includer_data->mapping->GetMapping()); - result->content_length = includer_data->mapping->GetSize(); - result->user_data = includer_data.release(); - - return result.release(); - } - - // |shaderc::CompileOptions::IncluderInterface| - void ReleaseInclude(shaderc_include_result* data) override { - delete reinterpret_cast(data->user_data); - delete data; - } - - private: - std::shared_ptr working_directory_; - std::vector include_dirs_; - std::function on_file_included_; - - FML_DISALLOW_COPY_AND_ASSIGN(Includer); -}; - -static std::string ShaderCErrorToString(shaderc_compilation_status status) { - switch (status) { - case shaderc_compilation_status::shaderc_compilation_status_success: - return "Success"; - case shaderc_compilation_status::shaderc_compilation_status_invalid_stage: - return "Invalid Shader Stage Specified"; - case shaderc_compilation_status:: - shaderc_compilation_status_compilation_error: - return "Compilation Error"; - case shaderc_compilation_status::shaderc_compilation_status_internal_error: - return "Internal Error"; - case shaderc_compilation_status:: - shaderc_compilation_status_null_result_object: - return "Internal error. Null Result Object"; - case shaderc_compilation_status:: - shaderc_compilation_status_invalid_assembly: - return "Invalid Assembly"; - case shaderc_compilation_status:: - shaderc_compilation_status_validation_error: - return "Validation Error"; - case shaderc_compilation_status:: - shaderc_compilation_status_transformation_error: - return "Transformation Error"; - case shaderc_compilation_status:: - shaderc_compilation_status_configuration_error: - return "Configuration Error"; - } - return "Unknown Internal Error"; +static CompilerBackend CreateMSLCompiler(const spirv_cross::ParsedIR& ir, + const SourceOptions& source_options) { + auto sl_compiler = std::make_shared(ir); + spirv_cross::CompilerMSL::Options sl_options; + sl_options.platform = + TargetPlatformToMSLPlatform(source_options.target_platform); + // If this version specification changes, the GN rules that process the + // Metal to AIR must be updated as well. + sl_options.msl_version = + spirv_cross::CompilerMSL::Options::make_msl_version(1, 2); + sl_compiler->set_msl_options(sl_options); + return sl_compiler; } -static shaderc_shader_kind ToShaderCShaderKind(Compiler::SourceType type) { - switch (type) { - case Compiler::SourceType::kVertexShader: - return shaderc_shader_kind::shaderc_vertex_shader; - case Compiler::SourceType::kFragmentShader: - return shaderc_shader_kind::shaderc_fragment_shader; - case Compiler::SourceType::kUnknown: - break; +static CompilerBackend CreateGLSLCompiler(const spirv_cross::ParsedIR& ir, + const SourceOptions& source_options) { + auto gl_compiler = std::make_shared(ir); + spirv_cross::CompilerGLSL::Options sl_options; + sl_options.force_zero_initialized_variables = true; + if (source_options.target_platform == TargetPlatform::kOpenGLES) { + sl_options.version = 100; + sl_options.es = true; } - return shaderc_shader_kind::shaderc_glsl_infer_from_source; + gl_compiler->set_common_options(sl_options); + return gl_compiler; } -static spv::ExecutionModel ToExecutionModel(Compiler::SourceType type) { - switch (type) { - case Compiler::SourceType::kVertexShader: - return spv::ExecutionModel::ExecutionModelVertex; - case Compiler::SourceType::kFragmentShader: - return spv::ExecutionModel::ExecutionModelFragment; - case Compiler::SourceType::kUnknown: +static CompilerBackend CreateCompiler(const spirv_cross::ParsedIR& ir, + const SourceOptions& source_options) { + CompilerBackend compiler; + switch (source_options.target_platform) { + case TargetPlatform::kMetalDesktop: + case TargetPlatform::kMetalIOS: + compiler = CreateMSLCompiler(ir, source_options); + break; + case TargetPlatform::kUnknown: + case TargetPlatform::kFlutterSPIRV: + case TargetPlatform::kOpenGLES: + case TargetPlatform::kOpenGLDesktop: + compiler = CreateGLSLCompiler(ir, source_options); break; } - return spv::ExecutionModel::ExecutionModelMax; - ; -} - -static spirv_cross::CompilerMSL::Options::Platform -CompilerTargetPlatformToCompilerMSLTargetPlatform( - Compiler::TargetPlatform platform) { - switch (platform) { - case Compiler::TargetPlatform::kIPhoneOS: - return spirv_cross::CompilerMSL::Options::Platform::iOS; - case Compiler::TargetPlatform::kMacOS: - // Unknown should not happen due to prior validation. - case Compiler::TargetPlatform::kFlutterSPIRV: - case Compiler::TargetPlatform::kUnknown: - return spirv_cross::CompilerMSL::Options::Platform::macOS; + if (!compiler) { + return {}; } + auto* backend = compiler.GetCompiler(); + backend->rename_entry_point("main", source_options.entry_point_name, + ToExecutionModel(source_options.type)); + return compiler; } Compiler::Compiler(const fml::Mapping& source_mapping, @@ -234,43 +89,51 @@ Compiler::Compiler(const fml::Mapping& source_mapping, return; } - shaderc::CompileOptions options; + shaderc::CompileOptions spirv_options; // Make sure reflection is as effective as possible. The generated shaders // will be processed later by backend specific compilers. So optimizations // here are irrelevant and get in the way of generating reflection code. - options.SetGenerateDebugInfo(); + spirv_options.SetGenerateDebugInfo(); // Expects GLSL 4.60 (Core Profile). // https://www.khronos.org/registry/OpenGL/specs/gl/GLSLangSpec.4.60.pdf - options.SetSourceLanguage( + spirv_options.SetSourceLanguage( shaderc_source_language::shaderc_source_language_glsl); - options.SetForcedVersionProfile(460, shaderc_profile::shaderc_profile_core); - if (source_options.target_platform == TargetPlatform::kFlutterSPIRV) { - options.SetOptimizationLevel( - shaderc_optimization_level::shaderc_optimization_level_size); - options.SetTargetEnvironment( - shaderc_target_env::shaderc_target_env_opengl, - shaderc_env_version::shaderc_env_version_opengl_4_5 - ); - options.SetTargetSpirv(shaderc_spirv_version::shaderc_spirv_version_1_0); - } else { - options.SetOptimizationLevel( - shaderc_optimization_level::shaderc_optimization_level_zero); - options.SetTargetEnvironment( - shaderc_target_env::shaderc_target_env_vulkan, - shaderc_env_version::shaderc_env_version_vulkan_1_1 - ); - options.SetTargetSpirv(shaderc_spirv_version::shaderc_spirv_version_1_3); + spirv_options.SetForcedVersionProfile(460, + shaderc_profile::shaderc_profile_core); + switch (source_options.target_platform) { + case TargetPlatform::kMetalDesktop: + case TargetPlatform::kMetalIOS: + case TargetPlatform::kOpenGLES: + case TargetPlatform::kOpenGLDesktop: + spirv_options.SetOptimizationLevel( + shaderc_optimization_level::shaderc_optimization_level_zero); + spirv_options.SetTargetEnvironment( + shaderc_target_env::shaderc_target_env_vulkan, + shaderc_env_version::shaderc_env_version_vulkan_1_1); + spirv_options.SetTargetSpirv( + shaderc_spirv_version::shaderc_spirv_version_1_3); + break; + case TargetPlatform::kFlutterSPIRV: + spirv_options.SetOptimizationLevel( + shaderc_optimization_level::shaderc_optimization_level_size); + spirv_options.SetTargetEnvironment( + shaderc_target_env::shaderc_target_env_opengl, + shaderc_env_version::shaderc_env_version_opengl_4_5); + spirv_options.SetTargetSpirv( + shaderc_spirv_version::shaderc_spirv_version_1_0); + break; + case TargetPlatform::kUnknown: + COMPILER_ERROR << "Target platform invalid."; + return; } - options.SetAutoBindUniforms(true); - options.SetAutoMapLocations(true); - - options.AddMacroDefinition("IMPELLER_DEVICE"); + spirv_options.SetAutoBindUniforms(true); + spirv_options.SetAutoMapLocations(true); std::vector included_file_names; - options.SetIncluder(std::make_unique( + spirv_options.SetIncluder(std::make_unique( options_.working_directory, options_.include_dirs, [&included_file_names](auto included_name) { included_file_names.emplace_back(std::move(included_name)); @@ -291,7 +154,7 @@ Compiler::Compiler(const fml::Mapping& source_mapping, shader_kind, // shader_kind source_options.file_name.c_str(), // input_file_name source_options.entry_point_name.c_str(), // entry_point_name - options // options + spirv_options // options )); if (spv_result_->GetCompilationStatus() != shaderc_compilation_status::shaderc_compilation_status_success) { @@ -307,7 +170,7 @@ Compiler::Compiler(const fml::Mapping& source_mapping, included_file_names_ = std::move(included_file_names); } - if (!TargetPlatformNeedsMSL(source_options.target_platform)) { + if (!TargetPlatformNeedsSL(source_options.target_platform)) { is_valid_ = true; return; } @@ -322,34 +185,25 @@ Compiler::Compiler(const fml::Mapping& source_mapping, const auto parsed_ir = std::make_shared(parser.get_parsed_ir()); - const auto msl_compiler = - std::make_shared(*parsed_ir); + auto sl_compiler = CreateCompiler(*parsed_ir, options_); - { - msl_compiler->rename_entry_point("main", options_.entry_point_name, - ToExecutionModel(options_.type)); - } - - { - spirv_cross::CompilerMSL::Options msl_options; - msl_options.platform = CompilerTargetPlatformToCompilerMSLTargetPlatform( - options_.target_platform); - // If this version specification changes, the GN rules that process the - // Metal to AIR must be updated as well. - msl_options.msl_version = - spirv_cross::CompilerMSL::Options::make_msl_version(1, 2); - msl_compiler->set_msl_options(msl_options); + if (!sl_compiler) { + COMPILER_ERROR << "Could not create compiler for target platform."; + return; } - msl_string_ = std::make_shared(msl_compiler->compile()); + sl_string_ = + std::make_shared(sl_compiler.GetCompiler()->compile()); - if (!msl_string_) { + if (!sl_string_) { COMPILER_ERROR << "Could not generate MSL from SPIRV"; return; } - reflector_ = std::make_unique(std::move(reflector_options), - parsed_ir, msl_compiler); + reflector_ = std::make_unique(std::move(reflector_options), // + parsed_ir, // + sl_compiler // + ); if (!reflector_->IsValid()) { COMPILER_ERROR << "Could not complete reflection on generated shader."; @@ -374,87 +228,21 @@ std::unique_ptr Compiler::GetSPIRVAssembly() const { [result = spv_result_](auto, auto) mutable { result.reset(); }); } -std::unique_ptr Compiler::GetMSLShaderSource() const { - if (!msl_string_) { +std::unique_ptr Compiler::GetSLShaderSource() const { + if (!sl_string_) { return nullptr; } return std::make_unique( - reinterpret_cast(msl_string_->c_str()), - msl_string_->length(), - [string = msl_string_](auto, auto) mutable { string.reset(); }); + reinterpret_cast(sl_string_->c_str()), + sl_string_->length(), + [string = sl_string_](auto, auto) mutable { string.reset(); }); } bool Compiler::IsValid() const { return is_valid_; } -static bool StringEndWith(const std::string& string, - const std::string& suffix) { - if (suffix.size() > string.size()) { - return false; - } - - if (suffix.empty() || suffix.empty()) { - return false; - } - - return string.rfind(suffix) == (string.size() - suffix.size()); -} - -Compiler::SourceType Compiler::SourceTypeFromFileName( - const std::string& file_name) { - if (StringEndWith(file_name, ".vert")) { - return Compiler::SourceType::kVertexShader; - } - - if (StringEndWith(file_name, ".frag")) { - return Compiler::SourceType::kFragmentShader; - } - - return Compiler::SourceType::kUnknown; -} - -std::string Compiler::EntryPointFromSourceName(const std::string& file_name, - SourceType type) { - std::stringstream stream; - std::filesystem::path file_path(file_name); - stream << file_path.stem().native() << "_"; - switch (type) { - case SourceType::kUnknown: - stream << "unknown"; - break; - case SourceType::kVertexShader: - stream << "vertex"; - break; - case SourceType::kFragmentShader: - stream << "fragment"; - break; - } - stream << "_main"; - return stream.str(); -} - -bool Compiler::TargetPlatformNeedsMSL(TargetPlatform platform) { - switch (platform) { - case TargetPlatform::kIPhoneOS: - case TargetPlatform::kMacOS: - return true; - default: - return false; - } -} - -bool Compiler::TargetPlatformNeedsReflection(TargetPlatform platform) { - switch (platform) { - case TargetPlatform::kIPhoneOS: - case TargetPlatform::kMacOS: - return true; - default: - return false; - } -} - std::string Compiler::GetSourcePrefix() const { std::stringstream stream; stream << options_.file_name << ": "; diff --git a/impeller/compiler/compiler.h b/impeller/compiler/compiler.h index 6acbde58e618d..635c778f285a6 100644 --- a/impeller/compiler/compiler.h +++ b/impeller/compiler/compiler.h @@ -12,6 +12,8 @@ #include "flutter/fml/mapping.h" #include "impeller/compiler/include_dir.h" #include "impeller/compiler/reflector.h" +#include "impeller/compiler/source_options.h" +#include "impeller/compiler/types.h" #include "shaderc/shaderc.hpp" #include "third_party/spirv_cross/spirv_msl.hpp" #include "third_party/spirv_cross/spirv_parser.hpp" @@ -21,42 +23,6 @@ namespace compiler { class Compiler { public: - enum class SourceType { - kUnknown, - kVertexShader, - kFragmentShader, - }; - - enum class TargetPlatform { - kUnknown, - kMacOS, - kIPhoneOS, - kFlutterSPIRV, - }; - - static SourceType SourceTypeFromFileName(const std::string& file_name); - - static std::string EntryPointFromSourceName(const std::string& file_name, - SourceType type); - - static bool TargetPlatformNeedsMSL(TargetPlatform platform); - - static bool TargetPlatformNeedsReflection(TargetPlatform platform); - - struct SourceOptions { - SourceType type = SourceType::kUnknown; - TargetPlatform target_platform = TargetPlatform::kUnknown; - std::shared_ptr working_directory; - std::vector include_dirs; - std::string file_name = "main.glsl"; - std::string entry_point_name = "main"; - - SourceOptions() = default; - - SourceOptions(const std::string& file_name) - : type(SourceTypeFromFileName(file_name)), file_name(file_name) {} - }; - Compiler(const fml::Mapping& source_mapping, SourceOptions options, Reflector::Options reflector_options); @@ -67,7 +33,7 @@ class Compiler { std::unique_ptr GetSPIRVAssembly() const; - std::unique_ptr GetMSLShaderSource() const; + std::unique_ptr GetSLShaderSource() const; std::string GetErrorMessages() const; @@ -81,7 +47,7 @@ class Compiler { private: SourceOptions options_; std::shared_ptr spv_result_; - std::shared_ptr msl_string_; + std::shared_ptr sl_string_; std::stringstream error_stream_; std::unique_ptr reflector_; std::vector included_file_names_; diff --git a/impeller/compiler/compiler_backend.cc b/impeller/compiler/compiler_backend.cc new file mode 100644 index 0000000000000..14a6d7868e71e --- /dev/null +++ b/impeller/compiler/compiler_backend.cc @@ -0,0 +1,84 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "impeller/compiler/compiler_backend.h" + +namespace impeller { +namespace compiler { + +CompilerBackend::CompilerBackend(MSLCompiler compiler) : compiler_(compiler) {} + +CompilerBackend::CompilerBackend(GLSLCompiler compiler) : compiler_(compiler) {} + +CompilerBackend::CompilerBackend() = default; + +CompilerBackend::CompilerBackend(Compiler compiler) + : compiler_(std::move(compiler)){}; + +CompilerBackend::~CompilerBackend() = default; + +const spirv_cross::Compiler* CompilerBackend::operator->() const { + return GetCompiler(); +} + +uint32_t CompilerBackend::GetExtendedMSLResourceBinding( + ExtendedResourceIndex index, + spirv_cross::ID id) const { + const auto kOOBIndex = static_cast(-1); + auto compiler = GetMSLCompiler(); + if (!compiler) { + return kOOBIndex; + } + switch (index) { + case ExtendedResourceIndex::kPrimary: + return compiler->get_automatic_msl_resource_binding(id); + case ExtendedResourceIndex::kSecondary: + return compiler->get_automatic_msl_resource_binding_secondary(id); + break; + } + return kOOBIndex; +} + +const spirv_cross::Compiler* CompilerBackend::GetCompiler() const { + if (auto compiler = GetGLSLCompiler()) { + return compiler; + } + + if (auto compiler = GetMSLCompiler()) { + return compiler; + } + + return nullptr; +} + +spirv_cross::Compiler* CompilerBackend::GetCompiler() { + if (auto* msl = std::get_if(&compiler_)) { + return msl->get(); + } + if (auto* glsl = std::get_if(&compiler_)) { + return glsl->get(); + } + return nullptr; +} + +const spirv_cross::CompilerMSL* CompilerBackend::GetMSLCompiler() const { + if (auto* msl = std::get_if(&compiler_)) { + return msl->get(); + } + return nullptr; +} + +const spirv_cross::CompilerGLSL* CompilerBackend::GetGLSLCompiler() const { + if (auto* glsl = std::get_if(&compiler_)) { + return glsl->get(); + } + return nullptr; +} + +CompilerBackend::operator bool() const { + return !!GetCompiler(); +} + +} // namespace compiler +} // namespace impeller diff --git a/impeller/compiler/compiler_backend.h b/impeller/compiler/compiler_backend.h new file mode 100644 index 0000000000000..14686b748fc6f --- /dev/null +++ b/impeller/compiler/compiler_backend.h @@ -0,0 +1,57 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#pragma once + +#include +#include + +#include "flutter/fml/logging.h" +#include "flutter/fml/macros.h" +#include "third_party/spirv_cross/spirv_glsl.hpp" +#include "third_party/spirv_cross/spirv_msl.hpp" + +namespace impeller { +namespace compiler { + +struct CompilerBackend { + using MSLCompiler = std::shared_ptr; + using GLSLCompiler = std::shared_ptr; + using Compiler = std::variant; + + CompilerBackend(MSLCompiler compiler); + + CompilerBackend(GLSLCompiler compiler); + + CompilerBackend(Compiler compiler); + + CompilerBackend(); + + ~CompilerBackend(); + + const spirv_cross::Compiler* operator->() const; + + operator bool() const; + + enum class ExtendedResourceIndex { + kPrimary, + kSecondary, + }; + uint32_t GetExtendedMSLResourceBinding(ExtendedResourceIndex index, + spirv_cross::ID id) const; + + const spirv_cross::Compiler* GetCompiler() const; + + spirv_cross::Compiler* GetCompiler(); + + private: + Compiler compiler_; + + const spirv_cross::CompilerMSL* GetMSLCompiler() const; + + const spirv_cross::CompilerGLSL* GetGLSLCompiler() const; +}; + +} // namespace compiler +} // namespace impeller diff --git a/impeller/compiler/compiler_test.cc b/impeller/compiler/compiler_test.cc new file mode 100644 index 0000000000000..c2ead9c7d961c --- /dev/null +++ b/impeller/compiler/compiler_test.cc @@ -0,0 +1,167 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "impeller/compiler/compiler_test.h" + +#include + +namespace impeller { +namespace compiler { +namespace testing { + +static fml::UniqueFD CreateIntermediatesDirectory() { + auto test_name = flutter::testing::GetCurrentTestName(); + std::replace(test_name.begin(), test_name.end(), '/', '_'); + std::replace(test_name.begin(), test_name.end(), '.', '_'); + return fml::OpenDirectory(flutter::testing::OpenFixturesDirectory(), + test_name.c_str(), + true, // create if necessary + fml::FilePermission::kReadWrite); +} + +CompilerTest::CompilerTest() + : intermediates_directory_(CreateIntermediatesDirectory()) { + FML_CHECK(intermediates_directory_.is_valid()); +} + +CompilerTest::~CompilerTest() = default; + +static std::string ReflectionHeaderName(const char* fixture_name) { + std::stringstream stream; + stream << fixture_name << ".h"; + return stream.str(); +} + +static std::string ReflectionCCName(const char* fixture_name) { + std::stringstream stream; + stream << fixture_name << ".cc"; + return stream.str(); +} + +static std::string ReflectionJSONName(const char* fixture_name) { + std::stringstream stream; + stream << fixture_name << ".json"; + return stream.str(); +} + +static std::string SPIRVFileName(const char* fixture_name) { + std::stringstream stream; + stream << fixture_name << ".spv"; + return stream.str(); +} + +static std::string SLFileName(const char* fixture_name, + TargetPlatform platform) { + std::stringstream stream; + stream << fixture_name << "." << TargetPlatformSLExtension(platform); + return stream.str(); +} + +bool CompilerTest::CanCompileAndReflect(const char* fixture_name) const { + auto fixture = flutter::testing::OpenFixtureAsMapping(fixture_name); + if (!fixture->GetMapping()) { + VALIDATION_LOG << "Could not find shader in fixtures: " << fixture_name; + return false; + } + + SourceOptions source_options(fixture_name); + source_options.target_platform = GetParam(); + source_options.working_directory = std::make_shared( + flutter::testing::OpenFixturesDirectory()); + source_options.entry_point_name = EntryPointFunctionNameFromSourceName( + fixture_name, SourceTypeFromFileName(fixture_name), GetParam()); + + Reflector::Options reflector_options; + reflector_options.header_file_name = ReflectionHeaderName(fixture_name); + reflector_options.shader_name = "shader_name"; + + Compiler compiler(*fixture.get(), source_options, reflector_options); + if (!compiler.IsValid()) { + VALIDATION_LOG << "Compilation failed: " << compiler.GetErrorMessages(); + return false; + } + + auto spirv_assembly = compiler.GetSPIRVAssembly(); + if (!spirv_assembly) { + VALIDATION_LOG << "No spirv was generated."; + return false; + } + + if (!fml::WriteAtomically(intermediates_directory_, + SPIRVFileName(fixture_name).c_str(), + *spirv_assembly)) { + VALIDATION_LOG << "Could not write SPIRV intermediates."; + return false; + } + + if (TargetPlatformNeedsSL(GetParam())) { + auto sl_source = compiler.GetSLShaderSource(); + if (!sl_source) { + VALIDATION_LOG << "No SL source was generated."; + return false; + } + + if (!fml::WriteAtomically(intermediates_directory_, + SLFileName(fixture_name, GetParam()).c_str(), + *sl_source)) { + VALIDATION_LOG << "Could not write SL intermediates."; + return false; + } + } + + if (TargetPlatformNeedsReflection(GetParam())) { + auto reflector = compiler.GetReflector(); + if (!reflector) { + VALIDATION_LOG + << "No reflector was found for target platform SL compiler."; + return false; + } + + auto reflection_json = reflector->GetReflectionJSON(); + auto reflection_header = reflector->GetReflectionHeader(); + auto reflection_source = reflector->GetReflectionCC(); + + if (!reflection_json) { + VALIDATION_LOG << "Reflection JSON was not found."; + return false; + } + + if (!reflection_header) { + VALIDATION_LOG << "Reflection header was not found."; + return false; + } + + if (!reflection_source) { + VALIDATION_LOG << "Reflection source was not found."; + return false; + } + + if (!fml::WriteAtomically(intermediates_directory_, + ReflectionHeaderName(fixture_name).c_str(), + *reflection_header)) { + VALIDATION_LOG << "Could not write reflection header intermediates."; + return false; + } + + if (!fml::WriteAtomically(intermediates_directory_, + ReflectionCCName(fixture_name).c_str(), + *reflection_header)) { + VALIDATION_LOG << "Could not write reflection CC intermediates."; + return false; + } + + if (!fml::WriteAtomically(intermediates_directory_, + ReflectionJSONName(fixture_name).c_str(), + *reflection_header)) { + VALIDATION_LOG << "Could not write reflection json intermediates."; + return false; + } + } + + return true; +} + +} // namespace testing +} // namespace compiler +} // namespace impeller diff --git a/impeller/compiler/compiler_test.h b/impeller/compiler/compiler_test.h new file mode 100644 index 0000000000000..f3141c2135749 --- /dev/null +++ b/impeller/compiler/compiler_test.h @@ -0,0 +1,34 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#pragma once + +#include "flutter/fml/macros.h" +#include "flutter/testing/testing.h" +#include "impeller/base/validation.h" +#include "impeller/compiler/compiler.h" +#include "impeller/compiler/source_options.h" +#include "impeller/compiler/types.h" + +namespace impeller { +namespace compiler { +namespace testing { + +class CompilerTest : public ::testing::TestWithParam { + public: + CompilerTest(); + + ~CompilerTest(); + + bool CanCompileAndReflect(const char* fixture_name) const; + + private: + fml::UniqueFD intermediates_directory_; + + FML_DISALLOW_COPY_AND_ASSIGN(CompilerTest); +}; + +} // namespace testing +} // namespace compiler +} // namespace impeller diff --git a/impeller/compiler/compiler_unittests.cc b/impeller/compiler/compiler_unittests.cc index 3ca5da7ca8613..6d598fa431229 100644 --- a/impeller/compiler/compiler_unittests.cc +++ b/impeller/compiler/compiler_unittests.cc @@ -3,72 +3,39 @@ // found in the LICENSE file. #include "flutter/testing/testing.h" -#include "gtest/gtest.h" #include "impeller/base/validation.h" #include "impeller/compiler/compiler.h" +#include "impeller/compiler/compiler_test.h" +#include "impeller/compiler/source_options.h" +#include "impeller/compiler/types.h" namespace impeller { namespace compiler { namespace testing { -class CompilerTest : public ::testing::Test { - public: - CompilerTest() - : intermediates_directory_( - fml::OpenDirectory(flutter::testing::GetFixturesPath(), - false, - fml::FilePermission::kRead)) { - FML_CHECK(intermediates_directory_.is_valid()); - } - - ~CompilerTest() = default; - - bool CanCompileFixture(const char* fixture_name, - Compiler::TargetPlatform target_platform) const { - auto fixture = flutter::testing::OpenFixtureAsMapping(fixture_name); - if (!fixture->GetMapping()) { - VALIDATION_LOG << "Could not find shader in fixtures: " << fixture_name; - return false; - } - Compiler::SourceOptions compiler_options(fixture_name); - compiler_options.target_platform = target_platform; - compiler_options.working_directory = std::make_shared( - flutter::testing::OpenFixturesDirectory()); - Reflector::Options reflector_options; - Compiler compiler(*fixture.get(), compiler_options, reflector_options); - if (!compiler.IsValid()) { - VALIDATION_LOG << "Compilation failed: " << compiler.GetErrorMessages(); - return false; - } - return true; - } - - private: - fml::UniqueFD intermediates_directory_; - - FML_DISALLOW_COPY_AND_ASSIGN(CompilerTest); -}; - -TEST_F(CompilerTest, ShaderKindMatchingIsSuccessful) { - ASSERT_EQ(Compiler::SourceTypeFromFileName("hello.vert"), - Compiler::SourceType::kVertexShader); - ASSERT_EQ(Compiler::SourceTypeFromFileName("hello.frag"), - Compiler::SourceType::kFragmentShader); - ASSERT_EQ(Compiler::SourceTypeFromFileName("hello.msl"), - Compiler::SourceType::kUnknown); - ASSERT_EQ(Compiler::SourceTypeFromFileName("hello.glsl"), - Compiler::SourceType::kUnknown); +TEST(CompilerTest, ShaderKindMatchingIsSuccessful) { + ASSERT_EQ(SourceTypeFromFileName("hello.vert"), SourceType::kVertexShader); + ASSERT_EQ(SourceTypeFromFileName("hello.frag"), SourceType::kFragmentShader); + ASSERT_EQ(SourceTypeFromFileName("hello.msl"), SourceType::kUnknown); + ASSERT_EQ(SourceTypeFromFileName("hello.glsl"), SourceType::kUnknown); } -TEST_F(CompilerTest, CanCompileSample) { - ASSERT_TRUE(CanCompileFixture( - "sample.vert", Compiler::TargetPlatform::kMacOS)); +TEST_P(CompilerTest, CanCompile) { + ASSERT_TRUE(CanCompileAndReflect("sample.vert")); } -TEST_F(CompilerTest, CanTargetFlutterSPIRV) { - ASSERT_TRUE(CanCompileFixture( - "test_texture.frag", Compiler::TargetPlatform::kFlutterSPIRV)); -} +#define INSTANTIATE_TARGET_PLATFORM_TEST_SUITE_P(suite_name) \ + INSTANTIATE_TEST_SUITE_P( \ + suite_name, CompilerTest, \ + ::testing::Values( \ + TargetPlatform::kOpenGLES, TargetPlatform::kOpenGLDesktop, \ + TargetPlatform::kMetalDesktop, TargetPlatform::kMetalIOS, \ + TargetPlatform::kFlutterSPIRV), \ + [](const ::testing::TestParamInfo& info) { \ + return TargetPlatformToString(info.param); \ + }); + +INSTANTIATE_TARGET_PLATFORM_TEST_SUITE_P(CompilerSuite); } // namespace testing } // namespace compiler diff --git a/impeller/compiler/impellerc_main.cc b/impeller/compiler/impellerc_main.cc index bb25d8daa59e3..f61b112604125 100644 --- a/impeller/compiler/impellerc_main.cc +++ b/impeller/compiler/impellerc_main.cc @@ -9,6 +9,7 @@ #include "flutter/fml/macros.h" #include "flutter/fml/mapping.h" #include "impeller/compiler/compiler.h" +#include "impeller/compiler/source_options.h" #include "impeller/compiler/switches.h" #include "impeller/compiler/utilities.h" #include "third_party/shaderc/libshaderc/include/shaderc/shaderc.hpp" @@ -36,15 +37,16 @@ bool Main(const fml::CommandLine& command_line) { return false; } - Compiler::SourceOptions options; + SourceOptions options; options.target_platform = switches.target_platform; - options.type = Compiler::SourceTypeFromFileName(switches.source_file_name); + options.type = SourceTypeFromFileName(switches.source_file_name); options.working_directory = switches.working_directory; options.file_name = switches.source_file_name; options.include_dirs = switches.include_directories; - options.entry_point_name = Compiler::EntryPointFromSourceName( + options.entry_point_name = EntryPointFunctionNameFromSourceName( switches.source_file_name, - Compiler::SourceTypeFromFileName(switches.source_file_name)); + SourceTypeFromFileName(switches.source_file_name), + switches.target_platform); Reflector::Options reflector_options; reflector_options.shader_name = @@ -69,22 +71,22 @@ bool Main(const fml::CommandLine& command_line) { return false; } - if (Compiler::TargetPlatformNeedsMSL(options.target_platform)) { + if (TargetPlatformNeedsSL(options.target_platform)) { if (!fml::WriteAtomically(*switches.working_directory, - switches.metal_file_name.c_str(), - *compiler.GetMSLShaderSource())) { + switches.sl_file_name.c_str(), + *compiler.GetSLShaderSource())) { std::cerr << "Could not write file to " << switches.spirv_file_name << std::endl; return false; } } - - if (Compiler::TargetPlatformNeedsReflection(options.target_platform)) { + if (TargetPlatformNeedsReflection(options.target_platform)) { if (!switches.reflection_json_name.empty()) { - if (!fml::WriteAtomically(*switches.working_directory, - switches.reflection_json_name.c_str(), - *compiler.GetReflector()->GetReflectionJSON())) { + if (!fml::WriteAtomically( + *switches.working_directory, + switches.reflection_json_name.c_str(), + *compiler.GetReflector()->GetReflectionJSON())) { std::cerr << "Could not write reflection json to " << switches.reflection_json_name << std::endl; return false; @@ -116,18 +118,20 @@ bool Main(const fml::CommandLine& command_line) { if (!switches.depfile_path.empty()) { std::string result_file; switch (switches.target_platform) { - case Compiler::TargetPlatform::kMacOS: - case Compiler::TargetPlatform::kIPhoneOS: - result_file = switches.metal_file_name; + case TargetPlatform::kMetalDesktop: + case TargetPlatform::kMetalIOS: + case TargetPlatform::kOpenGLES: + case TargetPlatform::kOpenGLDesktop: + result_file = switches.sl_file_name; break; - case Compiler::TargetPlatform::kFlutterSPIRV: - case Compiler::TargetPlatform::kUnknown: + case TargetPlatform::kFlutterSPIRV: + case TargetPlatform::kUnknown: result_file = switches.spirv_file_name; break; } - if (!fml::WriteAtomically( - *switches.working_directory, switches.depfile_path.c_str(), - *compiler.CreateDepfileContents({result_file}))) { + if (!fml::WriteAtomically(*switches.working_directory, + switches.depfile_path.c_str(), + *compiler.CreateDepfileContents({result_file}))) { std::cerr << "Could not write depfile to " << switches.depfile_path << std::endl; return false; diff --git a/impeller/compiler/includer.cc b/impeller/compiler/includer.cc new file mode 100644 index 0000000000000..3be266f33ace5 --- /dev/null +++ b/impeller/compiler/includer.cc @@ -0,0 +1,117 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "impeller/compiler/includer.h" + +#include "flutter/fml/paths.h" + +namespace impeller { +namespace compiler { + +Includer::Includer(std::shared_ptr working_directory, + std::vector include_dirs, + std::function on_file_included) + : working_directory_(std::move(working_directory)), + include_dirs_(std::move(include_dirs)), + on_file_included_(std::move(on_file_included)) {} + +// |shaderc::CompileOptions::IncluderInterface| +Includer::~Includer() = default; + +std::unique_ptr Includer::TryOpenMapping( + const IncludeDir& dir, + const char* requested_source) { + if (!dir.dir || !dir.dir->is_valid()) { + return nullptr; + } + + if (requested_source == nullptr) { + return nullptr; + } + + std::string source(requested_source); + if (source.empty()) { + return nullptr; + } + + auto mapping = fml::FileMapping::CreateReadOnly(*dir.dir, requested_source); + if (!mapping || !mapping->IsValid()) { + return nullptr; + } + + on_file_included_(fml::paths::JoinPaths({dir.name, requested_source})); + + return mapping; +} + +std::unique_ptr Includer::FindFirstMapping( + const char* requested_source) { + // Always try the working directory first no matter what the include + // directories are. + { + IncludeDir dir; + dir.name = "."; + dir.dir = working_directory_; + if (auto mapping = TryOpenMapping(dir, requested_source)) { + return mapping; + } + } + + for (const auto& include_dir : include_dirs_) { + if (auto mapping = TryOpenMapping(include_dir, requested_source)) { + return mapping; + } + } + return nullptr; +} + +shaderc_include_result* Includer::GetInclude(const char* requested_source, + shaderc_include_type type, + const char* requesting_source, + size_t include_depth) { + auto result = std::make_unique(); + + // Default initialize to failed inclusion. + result->source_name = ""; + result->source_name_length = 0; + + constexpr const char* kFileNotFoundMessage = "Included file not found."; + result->content = kFileNotFoundMessage; + result->content_length = ::strlen(kFileNotFoundMessage); + result->user_data = nullptr; + + if (!working_directory_ || !working_directory_->is_valid()) { + return result.release(); + } + + if (requested_source == nullptr) { + return result.release(); + } + + auto file = FindFirstMapping(requested_source); + + if (!file || file->GetMapping() == nullptr) { + return result.release(); + } + + auto includer_data = + std::make_unique(requested_source, std::move(file)); + + result->source_name = includer_data->file_name.c_str(); + result->source_name_length = includer_data->file_name.length(); + result->content = reinterpret_castcontent)>( + includer_data->mapping->GetMapping()); + result->content_length = includer_data->mapping->GetSize(); + result->user_data = includer_data.release(); + + return result.release(); +} + +void Includer::ReleaseInclude(shaderc_include_result* data) { + delete reinterpret_cast(data->user_data); + delete data; +} + +} // namespace compiler +} // namespace impeller diff --git a/impeller/compiler/includer.h b/impeller/compiler/includer.h new file mode 100644 index 0000000000000..0c373a6b3dd2f --- /dev/null +++ b/impeller/compiler/includer.h @@ -0,0 +1,59 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#pragma once + +#include + +#include "flutter/fml/macros.h" +#include "flutter/fml/mapping.h" +#include "impeller/compiler/include_dir.h" +#include "shaderc/shaderc.hpp" + +namespace impeller { +namespace compiler { + +struct IncluderData { + std::string file_name; + std::unique_ptr mapping; + + IncluderData(std::string p_file_name, std::unique_ptr p_mapping) + : file_name(std::move(p_file_name)), mapping(std::move(p_mapping)) {} +}; + +class Includer final : public shaderc::CompileOptions::IncluderInterface { + public: + Includer(std::shared_ptr working_directory, + std::vector include_dirs, + std::function on_file_included); + + // |shaderc::CompileOptions::IncluderInterface| + ~Includer() override; + + // |shaderc::CompileOptions::IncluderInterface| + shaderc_include_result* GetInclude(const char* requested_source, + shaderc_include_type type, + const char* requesting_source, + size_t include_depth) override; + + // |shaderc::CompileOptions::IncluderInterface| + void ReleaseInclude(shaderc_include_result* data) override; + + private: + std::shared_ptr working_directory_; + std::vector include_dirs_; + std::function on_file_included_; + + std::unique_ptr TryOpenMapping( + const IncludeDir& dir, + const char* requested_source); + + std::unique_ptr FindFirstMapping( + const char* requested_source); + + FML_DISALLOW_COPY_AND_ASSIGN(Includer); +}; + +} // namespace compiler +} // namespace impeller diff --git a/impeller/compiler/logger.h b/impeller/compiler/logger.h index b4938a5f46c8b..fb07dd079bd23 100644 --- a/impeller/compiler/logger.h +++ b/impeller/compiler/logger.h @@ -34,5 +34,10 @@ class AutoLogger { FML_DISALLOW_COPY_AND_ASSIGN(AutoLogger); }; +#define COMPILER_ERROR \ + ::impeller::compiler::AutoLogger(error_stream_) << GetSourcePrefix() + +#define COMPILER_ERROR_NO_PREFIX ::impeller::compiler::AutoLogger(error_stream_) + } // namespace compiler } // namespace impeller diff --git a/impeller/compiler/reflector.cc b/impeller/compiler/reflector.cc index d7786dcc7d904..c3896aea55784 100644 --- a/impeller/compiler/reflector.cc +++ b/impeller/compiler/reflector.cc @@ -90,7 +90,7 @@ static std::string StringToShaderStage(std::string str) { Reflector::Reflector(Options options, std::shared_ptr ir, - std::shared_ptr compiler) + CompilerBackend compiler) : options_(std::move(options)), ir_(std::move(ir)), compiler_(std::move(compiler)) { @@ -305,14 +305,10 @@ std::optional Reflector::ReflectResource( resource.id, spv::Decoration::DecorationLocation); result["index"] = compiler_->get_decoration(resource.id, spv::Decoration::DecorationIndex); - result["msl_res_0"] = - compiler_->get_automatic_msl_resource_binding(resource.id); - result["msl_res_1"] = - compiler_->get_automatic_msl_resource_binding_secondary(resource.id); - result["msl_res_2"] = - compiler_->get_automatic_msl_resource_binding_tertiary(resource.id); - result["msl_res_3"] = - compiler_->get_automatic_msl_resource_binding_quaternary(resource.id); + result["ext_res_0"] = compiler_.GetExtendedMSLResourceBinding( + CompilerBackend::ExtendedResourceIndex::kPrimary, resource.id); + result["ext_res_1"] = compiler_.GetExtendedMSLResourceBinding( + CompilerBackend::ExtendedResourceIndex::kSecondary, resource.id); auto type = ReflectType(resource.type_id); if (!type.has_value()) { return std::nullopt; @@ -607,7 +603,7 @@ struct VertexType { }; static VertexType VertexTypeFromInputResource( - const spirv_cross::CompilerMSL& compiler, + const spirv_cross::Compiler& compiler, const spirv_cross::Resource* resource) { VertexType result; result.variable_name = resource->name; @@ -692,7 +688,8 @@ Reflector::ReflectPerVertexStructDefinition( if (resource == nullptr) { return std::nullopt; } - const auto vertex_type = VertexTypeFromInputResource(*compiler_, resource); + const auto vertex_type = + VertexTypeFromInputResource(*compiler_.GetCompiler(), resource); StructMember member; member.name = vertex_type.variable_name; diff --git a/impeller/compiler/reflector.h b/impeller/compiler/reflector.h index 302b26456c690..cacaa7c4ea5c3 100644 --- a/impeller/compiler/reflector.h +++ b/impeller/compiler/reflector.h @@ -9,6 +9,7 @@ #include "flutter/fml/macros.h" #include "flutter/fml/mapping.h" +#include "impeller/compiler/compiler_backend.h" #include "inja/inja.hpp" #include "third_party/spirv_cross/spirv_msl.hpp" #include "third_party/spirv_cross/spirv_parser.hpp" @@ -32,7 +33,7 @@ class Reflector { Reflector(Options options, std::shared_ptr ir, - std::shared_ptr compiler); + CompilerBackend compiler); ~Reflector(); @@ -65,8 +66,7 @@ class Reflector { const Options options_; const std::shared_ptr ir_; - // TODO(csg): There is no reason this needs to the MSL subtype. - const std::shared_ptr compiler_; + const CompilerBackend compiler_; std::unique_ptr template_arguments_; std::shared_ptr reflection_header_; std::shared_ptr reflection_cc_; diff --git a/impeller/compiler/source_options.cc b/impeller/compiler/source_options.cc new file mode 100644 index 0000000000000..eeb1bcf8b7023 --- /dev/null +++ b/impeller/compiler/source_options.cc @@ -0,0 +1,18 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "impeller/compiler/source_options.h" + +namespace impeller { +namespace compiler { + +SourceOptions::SourceOptions() = default; + +SourceOptions::SourceOptions(const std::string& file_name) + : type(SourceTypeFromFileName(file_name)), file_name(file_name) {} + +SourceOptions::~SourceOptions() = default; + +} // namespace compiler +} // namespace impeller diff --git a/impeller/compiler/source_options.h b/impeller/compiler/source_options.h new file mode 100644 index 0000000000000..e5a3dc54a9713 --- /dev/null +++ b/impeller/compiler/source_options.h @@ -0,0 +1,35 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#pragma once + +#include +#include +#include + +#include "flutter/fml/macros.h" +#include "flutter/fml/unique_fd.h" +#include "impeller/compiler/include_dir.h" +#include "impeller/compiler/types.h" + +namespace impeller { +namespace compiler { + +struct SourceOptions { + SourceType type = SourceType::kUnknown; + TargetPlatform target_platform = TargetPlatform::kUnknown; + std::shared_ptr working_directory; + std::vector include_dirs; + std::string file_name = "main.glsl"; + std::string entry_point_name = "main"; + + SourceOptions(); + + ~SourceOptions(); + + SourceOptions(const std::string& file_name); +}; + +} // namespace compiler +} // namespace impeller diff --git a/impeller/compiler/switches.cc b/impeller/compiler/switches.cc index ef55ae07522e3..f3caea51d2f59 100644 --- a/impeller/compiler/switches.cc +++ b/impeller/compiler/switches.cc @@ -12,10 +12,12 @@ namespace impeller { namespace compiler { -static const std::map kKnownPlatforms = { - {"macos", Compiler::TargetPlatform::kMacOS}, - {"ios", Compiler::TargetPlatform::kIPhoneOS}, - {"flutter-spirv", Compiler::TargetPlatform::kFlutterSPIRV}, +static const std::map kKnownPlatforms = { + {"metal-desktop", TargetPlatform::kMetalDesktop}, + {"metal-ios", TargetPlatform::kMetalIOS}, + {"opengl-es", TargetPlatform::kOpenGLES}, + {"opengl-desktop", TargetPlatform::kOpenGLDesktop}, + {"flutter-spirv", TargetPlatform::kFlutterSPIRV}, }; void Switches::PrintHelp(std::ostream& stream) { @@ -26,7 +28,7 @@ void Switches::PrintHelp(std::ostream& stream) { } stream << " ]" << std::endl; stream << "--input=" << std::endl; - stream << "--metal=" << std::endl; + stream << "--sl=" << std::endl; stream << "--spirv=" << std::endl; stream << "[optional] --reflection-json=" << std::endl; stream << "[optional] --reflection-header=" @@ -40,16 +42,16 @@ Switches::Switches() = default; Switches::~Switches() = default; -static Compiler::TargetPlatform TargetPlatformFromCommandLine( +static TargetPlatform TargetPlatformFromCommandLine( const fml::CommandLine& command_line) { - auto target = Compiler::TargetPlatform::kUnknown; + auto target = TargetPlatform::kUnknown; for (const auto& platform : kKnownPlatforms) { if (command_line.HasOption(platform.first)) { // If the platform has already been determined, the caller may have // specified multiple platforms. This is an error and only one must be // selected. - if (target != Compiler::TargetPlatform::kUnknown) { - return Compiler::TargetPlatform::kUnknown; + if (target != TargetPlatform::kUnknown) { + return TargetPlatform::kUnknown; } target = platform.second; // Keep going to detect duplicates. @@ -65,7 +67,7 @@ Switches::Switches(const fml::CommandLine& command_line) false, // create if necessary, fml::FilePermission::kRead))), source_file_name(command_line.GetOptionValueWithDefault("input", "")), - metal_file_name(command_line.GetOptionValueWithDefault("metal", "")), + sl_file_name(command_line.GetOptionValueWithDefault("sl", "")), spirv_file_name(command_line.GetOptionValueWithDefault("spirv", "")), reflection_json_name( command_line.GetOptionValueWithDefault("reflection-json", "")), @@ -98,7 +100,7 @@ Switches::Switches(const fml::CommandLine& command_line) bool Switches::AreValid(std::ostream& explain) const { bool valid = true; - if (target_platform == Compiler::TargetPlatform::kUnknown) { + if (target_platform == TargetPlatform::kUnknown) { explain << "The target platform (only one) was not specified." << std::endl; valid = false; } @@ -113,8 +115,8 @@ bool Switches::AreValid(std::ostream& explain) const { valid = false; } - if (metal_file_name.empty() && Compiler::TargetPlatformNeedsMSL(target_platform)) { - explain << "Metal file name was empty." << std::endl; + if (sl_file_name.empty() && TargetPlatformNeedsSL(target_platform)) { + explain << "Target shading language file name was empty." << std::endl; valid = false; } diff --git a/impeller/compiler/switches.h b/impeller/compiler/switches.h index 3b7fac89d2e15..173ac20838a73 100644 --- a/impeller/compiler/switches.h +++ b/impeller/compiler/switches.h @@ -17,11 +17,11 @@ namespace impeller { namespace compiler { struct Switches { - Compiler::TargetPlatform target_platform = Compiler::TargetPlatform::kUnknown; + TargetPlatform target_platform = TargetPlatform::kUnknown; std::shared_ptr working_directory; std::vector include_directories; std::string source_file_name; - std::string metal_file_name; + std::string sl_file_name; std::string spirv_file_name; std::string reflection_json_name; std::string reflection_header_name; diff --git a/impeller/compiler/types.cc b/impeller/compiler/types.cc new file mode 100644 index 0000000000000..adbf277dfae9a --- /dev/null +++ b/impeller/compiler/types.cc @@ -0,0 +1,216 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "impeller/compiler/types.h" + +#include +#include + +#include "flutter/fml/logging.h" + +namespace impeller { +namespace compiler { + +static bool StringEndWith(const std::string& string, + const std::string& suffix) { + if (suffix.size() > string.size()) { + return false; + } + + if (suffix.empty() || suffix.empty()) { + return false; + } + + return string.rfind(suffix) == (string.size() - suffix.size()); +} + +SourceType SourceTypeFromFileName(const std::string& file_name) { + if (StringEndWith(file_name, ".vert")) { + return SourceType::kVertexShader; + } + + if (StringEndWith(file_name, ".frag")) { + return SourceType::kFragmentShader; + } + + return SourceType::kUnknown; +} + +std::string TargetPlatformToString(TargetPlatform platform) { + switch (platform) { + case TargetPlatform::kUnknown: + return "Unknown"; + case TargetPlatform::kMetalDesktop: + return "MetalDesktop"; + case TargetPlatform::kMetalIOS: + return "MetaliOS"; + case TargetPlatform::kFlutterSPIRV: + return "FlutterSPIRV"; + case TargetPlatform::kOpenGLES: + return "OpenGLES"; + case TargetPlatform::kOpenGLDesktop: + return "OpenGLDesktop"; + } + FML_UNREACHABLE(); +} + +static std::string UniqueEntryPointFunctionNameFromSourceName( + const std::string& file_name, + SourceType type) { + std::stringstream stream; + std::filesystem::path file_path(file_name); + stream << file_path.stem().native() << "_"; + switch (type) { + case SourceType::kUnknown: + stream << "unknown"; + break; + case SourceType::kVertexShader: + stream << "vertex"; + break; + case SourceType::kFragmentShader: + stream << "fragment"; + break; + } + stream << "_main"; + return stream.str(); +} + +std::string EntryPointFunctionNameFromSourceName(const std::string& file_name, + SourceType type, + TargetPlatform platform) { + switch (platform) { + case TargetPlatform::kMetalDesktop: + case TargetPlatform::kMetalIOS: + return UniqueEntryPointFunctionNameFromSourceName(file_name, type); + case TargetPlatform::kUnknown: + case TargetPlatform::kFlutterSPIRV: + case TargetPlatform::kOpenGLES: + case TargetPlatform::kOpenGLDesktop: + return "main"; + } + FML_UNREACHABLE(); +} + +bool TargetPlatformNeedsSL(TargetPlatform platform) { + switch (platform) { + case TargetPlatform::kMetalIOS: + case TargetPlatform::kMetalDesktop: + case TargetPlatform::kOpenGLES: + case TargetPlatform::kOpenGLDesktop: + return true; + case TargetPlatform::kUnknown: + case TargetPlatform::kFlutterSPIRV: + return false; + } + FML_UNREACHABLE(); +} + +bool TargetPlatformNeedsReflection(TargetPlatform platform) { + switch (platform) { + case TargetPlatform::kMetalIOS: + case TargetPlatform::kMetalDesktop: + case TargetPlatform::kOpenGLES: + case TargetPlatform::kOpenGLDesktop: + return true; + case TargetPlatform::kUnknown: + case TargetPlatform::kFlutterSPIRV: + return false; + } + FML_UNREACHABLE(); +} + +std::string ShaderCErrorToString(shaderc_compilation_status status) { + using Status = shaderc_compilation_status; + switch (status) { + case Status::shaderc_compilation_status_success: + return "Success"; + case Status::shaderc_compilation_status_invalid_stage: + return "Invalid shader stage specified"; + case Status::shaderc_compilation_status_compilation_error: + return "Compilation error"; + case Status::shaderc_compilation_status_internal_error: + return "Internal error"; + case Status::shaderc_compilation_status_null_result_object: + return "Internal error. Null result object"; + case Status::shaderc_compilation_status_invalid_assembly: + return "Invalid assembly"; + case Status::shaderc_compilation_status_validation_error: + return "Validation error"; + case Status::shaderc_compilation_status_transformation_error: + return "Transformation error"; + case Status::shaderc_compilation_status_configuration_error: + return "Configuration error"; + } + return "Unknown internal error"; +} + +shaderc_shader_kind ToShaderCShaderKind(SourceType type) { + switch (type) { + case SourceType::kVertexShader: + return shaderc_shader_kind::shaderc_vertex_shader; + case SourceType::kFragmentShader: + return shaderc_shader_kind::shaderc_fragment_shader; + case SourceType::kUnknown: + break; + } + return shaderc_shader_kind::shaderc_glsl_infer_from_source; +} + +spv::ExecutionModel ToExecutionModel(SourceType type) { + switch (type) { + case SourceType::kVertexShader: + return spv::ExecutionModel::ExecutionModelVertex; + case SourceType::kFragmentShader: + return spv::ExecutionModel::ExecutionModelFragment; + case SourceType::kUnknown: + break; + } + return spv::ExecutionModel::ExecutionModelMax; +} + +spirv_cross::CompilerMSL::Options::Platform TargetPlatformToMSLPlatform( + TargetPlatform platform) { + switch (platform) { + case TargetPlatform::kMetalIOS: + return spirv_cross::CompilerMSL::Options::Platform::iOS; + case TargetPlatform::kMetalDesktop: + return spirv_cross::CompilerMSL::Options::Platform::macOS; + case TargetPlatform::kFlutterSPIRV: + case TargetPlatform::kOpenGLES: + case TargetPlatform::kOpenGLDesktop: + case TargetPlatform::kUnknown: + return spirv_cross::CompilerMSL::Options::Platform::macOS; + } + FML_UNREACHABLE(); +} + +std::string SourceTypeToString(SourceType type) { + switch (type) { + case SourceType::kUnknown: + return "unknown"; + case SourceType::kVertexShader: + return "vert"; + case SourceType::kFragmentShader: + return "frag"; + } + FML_UNREACHABLE(); +} + +std::string TargetPlatformSLExtension(TargetPlatform platform) { + switch (platform) { + case TargetPlatform::kUnknown: + return "unknown"; + case TargetPlatform::kMetalDesktop: + case TargetPlatform::kMetalIOS: + return "metal"; + case TargetPlatform::kFlutterSPIRV: + case TargetPlatform::kOpenGLES: + case TargetPlatform::kOpenGLDesktop: + return "glsl"; + } + FML_UNREACHABLE(); +} + +} // namespace compiler +} // namespace impeller diff --git a/impeller/compiler/types.h b/impeller/compiler/types.h new file mode 100644 index 0000000000000..b760122b38f9a --- /dev/null +++ b/impeller/compiler/types.h @@ -0,0 +1,58 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#pragma once + +#include + +#include "flutter/fml/macros.h" +#include "shaderc/shaderc.hpp" +#include "third_party/spirv_cross/spirv_cross.hpp" +#include "third_party/spirv_cross/spirv_msl.hpp" + +namespace impeller { +namespace compiler { + +enum class SourceType { + kUnknown, + kVertexShader, + kFragmentShader, +}; + +enum class TargetPlatform { + kUnknown, + kMetalDesktop, + kMetalIOS, + kFlutterSPIRV, + kOpenGLES, + kOpenGLDesktop, +}; + +SourceType SourceTypeFromFileName(const std::string& file_name); + +std::string SourceTypeToString(SourceType type); + +std::string TargetPlatformToString(TargetPlatform platform); + +std::string TargetPlatformSLExtension(TargetPlatform platform); + +std::string EntryPointFunctionNameFromSourceName(const std::string& file_name, + SourceType type, + TargetPlatform platform); + +bool TargetPlatformNeedsSL(TargetPlatform platform); + +bool TargetPlatformNeedsReflection(TargetPlatform platform); + +std::string ShaderCErrorToString(shaderc_compilation_status status); + +shaderc_shader_kind ToShaderCShaderKind(SourceType type); + +spv::ExecutionModel ToExecutionModel(SourceType type); + +spirv_cross::CompilerMSL::Options::Platform TargetPlatformToMSLPlatform( + TargetPlatform platform); + +} // namespace compiler +} // namespace impeller diff --git a/impeller/tools/impeller.gni b/impeller/tools/impeller.gni index f82595760bc30..e4430563b0fb5 100644 --- a/impeller/tools/impeller.gni +++ b/impeller/tools/impeller.gni @@ -185,7 +185,7 @@ template("impeller_shaders_real") { args = [ "--input={{source}}", - "--metal=$metal_intermediate_path", + "--sl=$metal_intermediate_path", "--spirv=$spirv_intermediate_path", "--reflection-json=$reflection_json_path", "--reflection-header=$reflection_header_path", @@ -195,10 +195,10 @@ template("impeller_shaders_real") { ] if (is_mac) { - args += [ "--macos" ] + args += [ "--metal-desktop" ] } if (is_ios) { - args += [ "--ios" ] + args += [ "--metal-ios" ] } } From 830695161a4da19441b968f7b2182a767b3ff64d Mon Sep 17 00:00:00 2001 From: Brandon DeRosier Date: Mon, 18 Apr 2022 14:37:49 -0700 Subject: [PATCH 409/433] Compute text coverage; use blend mode in savelayer; conservative pass collapse/elision behavior (#129) --- impeller/aiks/aiks_unittests.cc | 21 +++++++++++++++++++ impeller/aiks/canvas.cc | 3 ++- impeller/aiks/paint_pass_delegate.cc | 4 ++-- impeller/entity/contents/text_contents.cc | 10 +++++++++ impeller/entity/contents/text_contents.h | 3 +++ impeller/entity/entity_pass.cc | 5 +++++ impeller/entity/entity_pass.h | 3 +++ .../backends/skia/text_frame_skia.cc | 3 +-- impeller/typographer/text_frame.cc | 15 +++++++++++++ impeller/typographer/text_frame.h | 8 +++++++ 10 files changed, 70 insertions(+), 5 deletions(-) diff --git a/impeller/aiks/aiks_unittests.cc b/impeller/aiks/aiks_unittests.cc index 0df8ff3ce6cae..13d3160eb7806 100644 --- a/impeller/aiks/aiks_unittests.cc +++ b/impeller/aiks/aiks_unittests.cc @@ -418,6 +418,27 @@ TEST_F(AiksTest, CanRenderEmojiTextFrame) { ASSERT_TRUE(OpenPlaygroundHere(canvas.EndRecordingAsPicture())); } +TEST_F(AiksTest, CanRenderTextInSaveLayer) { + Canvas canvas; + canvas.DrawPaint({.color = Color::White()}); + canvas.Translate({100, 100}); + canvas.Scale(Vector2{0.5, 0.5}); + + // Blend the layer with the parent pass using kClear to expose the coverage. + canvas.SaveLayer({.blend_mode = Entity::BlendMode::kClear}); + ASSERT_TRUE(RenderTextInCanvas( + GetContext(), canvas, "the quick brown fox jumped over the lazy dog!.?", + "Roboto-Regular.ttf")); + canvas.Restore(); + + // Render the text again over the cleared coverage rect. + ASSERT_TRUE(RenderTextInCanvas( + GetContext(), canvas, "the quick brown fox jumped over the lazy dog!.?", + "Roboto-Regular.ttf")); + + ASSERT_TRUE(OpenPlaygroundHere(canvas.EndRecordingAsPicture())); +} + TEST_F(AiksTest, CanDrawPaint) { Paint paint; paint.color = Color::MediumTurquoise(); diff --git a/impeller/aiks/canvas.cc b/impeller/aiks/canvas.cc index 1949ad8a40d00..c45bc42a5f63b 100644 --- a/impeller/aiks/canvas.cc +++ b/impeller/aiks/canvas.cc @@ -141,9 +141,10 @@ void Canvas::DrawCircle(Point center, Scalar radius, Paint paint) { void Canvas::SaveLayer(Paint paint, std::optional bounds) { GetCurrentPass().SetDelegate( - std::make_unique(std::move(paint), bounds)); + std::make_unique(paint, bounds)); Save(true); + GetCurrentPass().SetBlendMode(paint.blend_mode); if (bounds.has_value()) { // Render target switches due to a save layer can be elided. In such cases diff --git a/impeller/aiks/paint_pass_delegate.cc b/impeller/aiks/paint_pass_delegate.cc index 1e7b38cbd5c89..34791be892086 100644 --- a/impeller/aiks/paint_pass_delegate.cc +++ b/impeller/aiks/paint_pass_delegate.cc @@ -22,12 +22,12 @@ std::optional PaintPassDelegate::GetCoverageRect() { // |EntityPassDelgate| bool PaintPassDelegate::CanElide() { - return paint_.color.IsTransparent(); + return paint_.blend_mode == Entity::BlendMode::kDestination; } // |EntityPassDelgate| bool PaintPassDelegate::CanCollapseIntoParentPass() { - return paint_.color.IsOpaque(); + return false; } // |EntityPassDelgate| diff --git a/impeller/entity/contents/text_contents.cc b/impeller/entity/contents/text_contents.cc index f64957f58f14d..f7a435cb913d1 100644 --- a/impeller/entity/contents/text_contents.cc +++ b/impeller/entity/contents/text_contents.cc @@ -4,6 +4,8 @@ #include "impeller/entity/contents/text_contents.h" +#include + #include "impeller/entity/contents/content_context.h" #include "impeller/entity/entity.h" #include "impeller/geometry/path_builder.h" @@ -48,6 +50,14 @@ void TextContents::SetColor(Color color) { color_ = color; } +std::optional TextContents::GetCoverage(const Entity& entity) const { + auto bounds = frame_.GetBounds(); + if (!bounds.has_value()) { + return std::nullopt; + } + return bounds->TransformBounds(entity.GetTransformation()); +} + bool TextContents::Render(const ContentContext& renderer, const Entity& entity, RenderPass& pass) const { diff --git a/impeller/entity/contents/text_contents.h b/impeller/entity/contents/text_contents.h index 238de9a43e77e..1caa38b77ad0b 100644 --- a/impeller/entity/contents/text_contents.h +++ b/impeller/entity/contents/text_contents.h @@ -34,6 +34,9 @@ class TextContents final : public Contents { void SetColor(Color color); + // |Contents| + std::optional GetCoverage(const Entity& entity) const override; + // |Contents| bool Render(const ContentContext& renderer, const Entity& entity, diff --git a/impeller/entity/entity_pass.cc b/impeller/entity/entity_pass.cc index a0e4878ece11b..be77768093dc7 100644 --- a/impeller/entity/entity_pass.cc +++ b/impeller/entity/entity_pass.cc @@ -210,6 +210,7 @@ bool EntityPass::Render(ContentContext& renderer, .TakePath()); entity.SetContents(std::move(offscreen_texture_contents)); entity.SetStencilDepth(stencil_depth_); + entity.SetBlendMode(subpass->blend_mode_); // Once we have filters being applied for SaveLayer, some special sauce // may be needed here (or in PaintPassDelegate) to ensure the filter // parameters are transformed by the `xformation_` matrix, while continuing @@ -257,4 +258,8 @@ void EntityPass::SetStencilDepth(size_t stencil_depth) { stencil_depth_ = stencil_depth; } +void EntityPass::SetBlendMode(Entity::BlendMode blend_mode) { + blend_mode_ = blend_mode; +} + } // namespace impeller diff --git a/impeller/entity/entity_pass.h b/impeller/entity/entity_pass.h index 07a48e3530ef0..c8b8c117bda2a 100644 --- a/impeller/entity/entity_pass.h +++ b/impeller/entity/entity_pass.h @@ -58,12 +58,15 @@ class EntityPass { void SetStencilDepth(size_t stencil_depth); + void SetBlendMode(Entity::BlendMode blend_mode); + private: Entities entities_; Subpasses subpasses_; EntityPass* superpass_ = nullptr; Matrix xformation_; size_t stencil_depth_ = 0u; + Entity::BlendMode blend_mode_ = Entity::BlendMode::kSourceOver; std::unique_ptr delegate_ = EntityPassDelegate::MakeDefault(); std::shared_ptr lazy_glyph_atlas_ = diff --git a/impeller/typographer/backends/skia/text_frame_skia.cc b/impeller/typographer/backends/skia/text_frame_skia.cc index 4f92b300fb309..13191b556d7c0 100644 --- a/impeller/typographer/backends/skia/text_frame_skia.cc +++ b/impeller/typographer/backends/skia/text_frame_skia.cc @@ -29,8 +29,7 @@ static Font ToFont(const SkFont& font, Scalar scale) { return Font{std::move(typeface), std::move(metrics)}; } -TextFrame TextFrameFromTextBlob(sk_sp blob, - Scalar scale) { +TextFrame TextFrameFromTextBlob(sk_sp blob, Scalar scale) { if (!blob) { return {}; } diff --git a/impeller/typographer/text_frame.cc b/impeller/typographer/text_frame.cc index f6681e5e4671c..4fe00ce615b80 100644 --- a/impeller/typographer/text_frame.cc +++ b/impeller/typographer/text_frame.cc @@ -10,6 +10,21 @@ TextFrame::TextFrame() = default; TextFrame::~TextFrame() = default; +std::optional TextFrame::GetBounds() const { + std::optional result; + + for (const auto& run : runs_) { + const auto glyph_bounds = run.GetFont().GetMetrics().GetBoundingBox(); + for (const auto& glyph_position : run.GetGlyphPositions()) { + Vector2 position = glyph_position.position * Vector2(); + Rect glyph_rect = Rect(position + glyph_bounds.origin, glyph_bounds.size); + result = result.has_value() ? result->Union(glyph_rect) : glyph_rect; + } + } + + return result; +} + bool TextFrame::AddTextRun(TextRun run) { if (!run.IsValid()) { return false; diff --git a/impeller/typographer/text_frame.h b/impeller/typographer/text_frame.h index a6d86b01d068b..854b6a2fb0bb5 100644 --- a/impeller/typographer/text_frame.h +++ b/impeller/typographer/text_frame.h @@ -21,6 +21,14 @@ class TextFrame { ~TextFrame(); + //---------------------------------------------------------------------------- + /// @brief The conservative bounding box for this text frame. + /// + /// @return The bounds rectangle. If there are no glyphs in this text + /// frame, std::nullopt is returned. + /// + std::optional GetBounds() const; + //---------------------------------------------------------------------------- /// @brief The number of runs in this text frame. /// From 1f45d8794578421fa0b6fcc1860032ebbbd03c57 Mon Sep 17 00:00:00 2001 From: Chinmay Garde Date: Mon, 18 Apr 2022 16:19:57 -0700 Subject: [PATCH 410/433] Add an OpenGL ES stub and parameterize all playgrounds on rendering backend. (#141) As we add more rendering backends, adding a new enum value to a single macro `INSTANTIATE_PLAYGROUND_SUITE` in `playground.h` will create a new test variant in any suite that uses playgrounds. The invocations will look like the following: ``` [ RUN ] Play/TypographerTest.CanCreateGlyphAtlas/Metal [ OK ] Play/TypographerTest.CanCreateGlyphAtlas/Metal (210 ms) [ RUN ] Play/TypographerTest.CanCreateGlyphAtlas/OpenGLES [ OK ] Play/TypographerTest.CanCreateGlyphAtlas/OpenGLES (xxx ms) ``` If you want to test just one backend, you may add a filter like so `--gtest_filter="*/Metal"` Right now, I have not added a the OpenGLES variant to the default test suite instantiation since there are so many failures (that is just a stub ATM). But, if the need arises to skip specific tests based on the backend in use (we won't support instancing in OpenGLES for example), the backend for the playground may be queried before deciding to GTEST_SKIP the invocation. One additional change in the patch that will be reworked soon is the Metal specificity of the source set generated after reflection. This will be made agnostic in the coming few patches. Right now, these headers are in the `mtl` folder. --- impeller/aiks/aiks_unittests.cc | 59 ++--- impeller/base/config.h | 19 ++ .../display_list/display_list_unittests.cc | 13 +- impeller/entity/contents/content_context.h | 36 +-- .../entity/contents/solid_stroke_contents.h | 2 +- impeller/entity/contents/texture_contents.cc | 4 +- impeller/entity/entity_unittests.cc | 33 +-- impeller/playground/BUILD.gn | 18 +- .../backend/gles/playground_impl_gles.cc | 37 +++ .../backend/gles/playground_impl_gles.h | 37 +++ .../backend/metal/playground_impl_mtl.h | 45 ++++ .../backend/metal/playground_impl_mtl.mm | 95 +++++++ .../playground/imgui/imgui_impl_impeller.cc | 4 +- .../{playground.mm => playground.cc} | 83 +++---- impeller/playground/playground.h | 24 +- impeller/playground/playground_impl.cc | 27 ++ impeller/playground/playground_impl.h | 42 ++++ impeller/renderer/BUILD.gn | 232 +++++++++++------- .../renderer/backend/gles/allocator_gles.cc | 11 + .../renderer/backend/gles/allocator_gles.h | 45 ++++ .../backend/gles/command_buffer_gles.cc | 39 +++ .../backend/gles/command_buffer_gles.h | 42 ++++ .../renderer/backend/gles/context_gles.cc | 64 +++++ impeller/renderer/backend/gles/context_gles.h | 62 +++++ .../backend/gles/device_buffer_gles.cc | 11 + .../backend/gles/device_buffer_gles.h | 0 .../renderer/backend/gles/formats_gles.cc | 11 + impeller/renderer/backend/gles/formats_gles.h | 0 .../renderer/backend/gles/pipeline_gles.cc | 11 + .../renderer/backend/gles/pipeline_gles.h | 0 .../backend/gles/pipeline_library_gles.cc | 11 + .../backend/gles/pipeline_library_gles.h | 30 +++ .../renderer/backend/gles/proc_table_gles.cc | 17 ++ .../renderer/backend/gles/proc_table_gles.h | 25 ++ .../renderer/backend/gles/reactor_gles.cc | 28 +++ impeller/renderer/backend/gles/reactor_gles.h | 29 +++ .../renderer/backend/gles/render_pass_gles.cc | 11 + .../renderer/backend/gles/render_pass_gles.h | 0 .../renderer/backend/gles/sampler_gles.cc | 11 + impeller/renderer/backend/gles/sampler_gles.h | 0 .../backend/gles/sampler_library_gles.cc | 11 + .../backend/gles/sampler_library_gles.h | 0 .../backend/gles/shader_function_gles.cc | 11 + .../backend/gles/shader_function_gles.h | 0 .../backend/gles/shader_library_gles.cc | 11 + .../backend/gles/shader_library_gles.h | 33 +++ .../renderer/backend/gles/surface_gles.cc | 11 + impeller/renderer/backend/gles/surface_gles.h | 0 .../renderer/backend/gles/texture_gles.cc | 11 + impeller/renderer/backend/gles/texture_gles.h | 0 .../backend/gles/vertex_descriptor_gles.cc | 11 + .../backend/gles/vertex_descriptor_gles.h | 0 impeller/renderer/renderer.cc | 2 +- impeller/renderer/renderer_unittests.cc | 22 +- impeller/tools/impeller.gni | 46 +++- impeller/typographer/typographer_unittests.cc | 7 +- 56 files changed, 1204 insertions(+), 240 deletions(-) create mode 100644 impeller/playground/backend/gles/playground_impl_gles.cc create mode 100644 impeller/playground/backend/gles/playground_impl_gles.h create mode 100644 impeller/playground/backend/metal/playground_impl_mtl.h create mode 100644 impeller/playground/backend/metal/playground_impl_mtl.mm rename impeller/playground/{playground.mm => playground.cc} (77%) create mode 100644 impeller/playground/playground_impl.cc create mode 100644 impeller/playground/playground_impl.h create mode 100644 impeller/renderer/backend/gles/allocator_gles.cc create mode 100644 impeller/renderer/backend/gles/allocator_gles.h create mode 100644 impeller/renderer/backend/gles/command_buffer_gles.cc create mode 100644 impeller/renderer/backend/gles/command_buffer_gles.h create mode 100644 impeller/renderer/backend/gles/context_gles.cc create mode 100644 impeller/renderer/backend/gles/context_gles.h create mode 100644 impeller/renderer/backend/gles/device_buffer_gles.cc create mode 100644 impeller/renderer/backend/gles/device_buffer_gles.h create mode 100644 impeller/renderer/backend/gles/formats_gles.cc create mode 100644 impeller/renderer/backend/gles/formats_gles.h create mode 100644 impeller/renderer/backend/gles/pipeline_gles.cc create mode 100644 impeller/renderer/backend/gles/pipeline_gles.h create mode 100644 impeller/renderer/backend/gles/pipeline_library_gles.cc create mode 100644 impeller/renderer/backend/gles/pipeline_library_gles.h create mode 100644 impeller/renderer/backend/gles/proc_table_gles.cc create mode 100644 impeller/renderer/backend/gles/proc_table_gles.h create mode 100644 impeller/renderer/backend/gles/reactor_gles.cc create mode 100644 impeller/renderer/backend/gles/reactor_gles.h create mode 100644 impeller/renderer/backend/gles/render_pass_gles.cc create mode 100644 impeller/renderer/backend/gles/render_pass_gles.h create mode 100644 impeller/renderer/backend/gles/sampler_gles.cc create mode 100644 impeller/renderer/backend/gles/sampler_gles.h create mode 100644 impeller/renderer/backend/gles/sampler_library_gles.cc create mode 100644 impeller/renderer/backend/gles/sampler_library_gles.h create mode 100644 impeller/renderer/backend/gles/shader_function_gles.cc create mode 100644 impeller/renderer/backend/gles/shader_function_gles.h create mode 100644 impeller/renderer/backend/gles/shader_library_gles.cc create mode 100644 impeller/renderer/backend/gles/shader_library_gles.h create mode 100644 impeller/renderer/backend/gles/surface_gles.cc create mode 100644 impeller/renderer/backend/gles/surface_gles.h create mode 100644 impeller/renderer/backend/gles/texture_gles.cc create mode 100644 impeller/renderer/backend/gles/texture_gles.h create mode 100644 impeller/renderer/backend/gles/vertex_descriptor_gles.cc create mode 100644 impeller/renderer/backend/gles/vertex_descriptor_gles.h diff --git a/impeller/aiks/aiks_unittests.cc b/impeller/aiks/aiks_unittests.cc index 13d3160eb7806..016ca50c2f4e9 100644 --- a/impeller/aiks/aiks_unittests.cc +++ b/impeller/aiks/aiks_unittests.cc @@ -19,8 +19,9 @@ namespace impeller { namespace testing { using AiksTest = AiksPlayground; +INSTANTIATE_PLAYGROUND_SUITE(AiksTest); -TEST_F(AiksTest, CanvasCTMCanBeUpdated) { +TEST_P(AiksTest, CanvasCTMCanBeUpdated) { Canvas canvas; Matrix identity; ASSERT_MATRIX_NEAR(canvas.GetCurrentTransformation(), identity); @@ -29,7 +30,7 @@ TEST_F(AiksTest, CanvasCTMCanBeUpdated) { Matrix::MakeTranslation({100.0, 100.0, 0.0})); } -TEST_F(AiksTest, CanvasCanPushPopCTM) { +TEST_P(AiksTest, CanvasCanPushPopCTM) { Canvas canvas; ASSERT_EQ(canvas.GetSaveCount(), 1u); ASSERT_EQ(canvas.Restore(), false); @@ -45,7 +46,7 @@ TEST_F(AiksTest, CanvasCanPushPopCTM) { Matrix::MakeTranslation({100.0, 100.0, 0.0})); } -TEST_F(AiksTest, CanRenderColoredRect) { +TEST_P(AiksTest, CanRenderColoredRect) { Canvas canvas; Paint paint; paint.color = Color::Red(); @@ -56,7 +57,7 @@ TEST_F(AiksTest, CanRenderColoredRect) { ASSERT_TRUE(OpenPlaygroundHere(canvas.EndRecordingAsPicture())); } -TEST_F(AiksTest, CanRenderImage) { +TEST_P(AiksTest, CanRenderImage) { Canvas canvas; Paint paint; auto image = std::make_shared(CreateTextureForFixture("kalimba.jpg")); @@ -65,7 +66,7 @@ TEST_F(AiksTest, CanRenderImage) { ASSERT_TRUE(OpenPlaygroundHere(canvas.EndRecordingAsPicture())); } -TEST_F(AiksTest, CanRenderImageRect) { +TEST_P(AiksTest, CanRenderImageRect) { Canvas canvas; Paint paint; auto image = std::make_shared(CreateTextureForFixture("kalimba.jpg")); @@ -81,7 +82,7 @@ TEST_F(AiksTest, CanRenderImageRect) { ASSERT_TRUE(OpenPlaygroundHere(canvas.EndRecordingAsPicture())); } -TEST_F(AiksTest, CanRenderStrokes) { +TEST_P(AiksTest, CanRenderStrokes) { Canvas canvas; Paint paint; paint.color = Color::Red(); @@ -92,7 +93,7 @@ TEST_F(AiksTest, CanRenderStrokes) { ASSERT_TRUE(OpenPlaygroundHere(canvas.EndRecordingAsPicture())); } -TEST_F(AiksTest, CanRenderCurvedStrokes) { +TEST_P(AiksTest, CanRenderCurvedStrokes) { Canvas canvas; Paint paint; paint.color = Color::Red(); @@ -102,7 +103,7 @@ TEST_F(AiksTest, CanRenderCurvedStrokes) { ASSERT_TRUE(OpenPlaygroundHere(canvas.EndRecordingAsPicture())); } -TEST_F(AiksTest, CanRenderClips) { +TEST_P(AiksTest, CanRenderClips) { Canvas canvas; Paint paint; paint.color = Color::Fuchsia(); @@ -112,7 +113,7 @@ TEST_F(AiksTest, CanRenderClips) { ASSERT_TRUE(OpenPlaygroundHere(canvas.EndRecordingAsPicture())); } -TEST_F(AiksTest, CanRenderNestedClips) { +TEST_P(AiksTest, CanRenderNestedClips) { Canvas canvas; Paint paint; paint.color = Color::Fuchsia(); @@ -125,7 +126,7 @@ TEST_F(AiksTest, CanRenderNestedClips) { ASSERT_TRUE(OpenPlaygroundHere(canvas.EndRecordingAsPicture())); } -TEST_F(AiksTest, CanRenderDifferenceClips) { +TEST_P(AiksTest, CanRenderDifferenceClips) { Paint paint; Canvas canvas; canvas.Translate({400, 400}); @@ -162,7 +163,7 @@ TEST_F(AiksTest, CanRenderDifferenceClips) { ASSERT_TRUE(OpenPlaygroundHere(canvas.EndRecordingAsPicture())); } -TEST_F(AiksTest, ClipsUseCurrentTransform) { +TEST_P(AiksTest, ClipsUseCurrentTransform) { std::array colors = {Color::White(), Color::Black(), Color::SkyBlue(), Color::Red(), Color::Yellow()}; @@ -180,7 +181,7 @@ TEST_F(AiksTest, ClipsUseCurrentTransform) { ASSERT_TRUE(OpenPlaygroundHere(canvas.EndRecordingAsPicture())); } -TEST_F(AiksTest, CanSaveLayerStandalone) { +TEST_P(AiksTest, CanSaveLayerStandalone) { Canvas canvas; Paint red; @@ -198,7 +199,7 @@ TEST_F(AiksTest, CanSaveLayerStandalone) { ASSERT_TRUE(OpenPlaygroundHere(canvas.EndRecordingAsPicture())); } -TEST_F(AiksTest, CanRenderGroupOpacity) { +TEST_P(AiksTest, CanRenderGroupOpacity) { Canvas canvas; Paint red; @@ -222,7 +223,7 @@ TEST_F(AiksTest, CanRenderGroupOpacity) { ASSERT_TRUE(OpenPlaygroundHere(canvas.EndRecordingAsPicture())); } -TEST_F(AiksTest, CanPerformFullScreenMSAA) { +TEST_P(AiksTest, CanPerformFullScreenMSAA) { Canvas canvas; Paint red; @@ -233,7 +234,7 @@ TEST_F(AiksTest, CanPerformFullScreenMSAA) { ASSERT_TRUE(OpenPlaygroundHere(canvas.EndRecordingAsPicture())); } -TEST_F(AiksTest, CanPerformSkew) { +TEST_P(AiksTest, CanPerformSkew) { Canvas canvas; Paint red; @@ -245,7 +246,7 @@ TEST_F(AiksTest, CanPerformSkew) { ASSERT_TRUE(OpenPlaygroundHere(canvas.EndRecordingAsPicture())); } -TEST_F(AiksTest, CanPerformSaveLayerWithBounds) { +TEST_P(AiksTest, CanPerformSaveLayerWithBounds) { Canvas canvas; Paint red; @@ -271,7 +272,7 @@ TEST_F(AiksTest, CanPerformSaveLayerWithBounds) { ASSERT_TRUE(OpenPlaygroundHere(canvas.EndRecordingAsPicture())); } -TEST_F(AiksTest, +TEST_P(AiksTest, CanPerformSaveLayerWithBoundsAndLargerIntermediateIsNotAllocated) { Canvas canvas; @@ -298,7 +299,7 @@ TEST_F(AiksTest, ASSERT_TRUE(OpenPlaygroundHere(canvas.EndRecordingAsPicture())); } -TEST_F(AiksTest, CanRenderRoundedRectWithNonUniformRadii) { +TEST_P(AiksTest, CanRenderRoundedRectWithNonUniformRadii) { Canvas canvas; Paint paint; @@ -318,7 +319,7 @@ TEST_F(AiksTest, CanRenderRoundedRectWithNonUniformRadii) { ASSERT_TRUE(OpenPlaygroundHere(canvas.EndRecordingAsPicture())); } -TEST_F(AiksTest, CanRenderDifferencePaths) { +TEST_P(AiksTest, CanRenderDifferencePaths) { Canvas canvas; Paint paint; @@ -393,7 +394,7 @@ bool RenderTextInCanvas(std::shared_ptr context, return true; } -TEST_F(AiksTest, CanRenderTextFrame) { +TEST_P(AiksTest, CanRenderTextFrame) { Canvas canvas; ASSERT_TRUE(RenderTextInCanvas( GetContext(), canvas, "the quick brown fox jumped over the lazy dog!.?", @@ -401,7 +402,7 @@ TEST_F(AiksTest, CanRenderTextFrame) { ASSERT_TRUE(OpenPlaygroundHere(canvas.EndRecordingAsPicture())); } -TEST_F(AiksTest, CanRenderItalicizedText) { +TEST_P(AiksTest, CanRenderItalicizedText) { Canvas canvas; ASSERT_TRUE(RenderTextInCanvas( GetContext(), canvas, "the quick brown fox jumped over the lazy dog!.?", @@ -409,7 +410,7 @@ TEST_F(AiksTest, CanRenderItalicizedText) { ASSERT_TRUE(OpenPlaygroundHere(canvas.EndRecordingAsPicture())); } -TEST_F(AiksTest, CanRenderEmojiTextFrame) { +TEST_P(AiksTest, CanRenderEmojiTextFrame) { Canvas canvas; ASSERT_TRUE(RenderTextInCanvas( GetContext(), canvas, @@ -418,7 +419,7 @@ TEST_F(AiksTest, CanRenderEmojiTextFrame) { ASSERT_TRUE(OpenPlaygroundHere(canvas.EndRecordingAsPicture())); } -TEST_F(AiksTest, CanRenderTextInSaveLayer) { +TEST_P(AiksTest, CanRenderTextInSaveLayer) { Canvas canvas; canvas.DrawPaint({.color = Color::White()}); canvas.Translate({100, 100}); @@ -439,7 +440,7 @@ TEST_F(AiksTest, CanRenderTextInSaveLayer) { ASSERT_TRUE(OpenPlaygroundHere(canvas.EndRecordingAsPicture())); } -TEST_F(AiksTest, CanDrawPaint) { +TEST_P(AiksTest, CanDrawPaint) { Paint paint; paint.color = Color::MediumTurquoise(); Canvas canvas; @@ -447,7 +448,7 @@ TEST_F(AiksTest, CanDrawPaint) { ASSERT_TRUE(OpenPlaygroundHere(canvas.EndRecordingAsPicture())); } -TEST_F(AiksTest, PaintBlendModeIsRespected) { +TEST_P(AiksTest, PaintBlendModeIsRespected) { Paint paint; Canvas canvas; // Default is kSourceOver. @@ -466,7 +467,7 @@ TEST_F(AiksTest, PaintBlendModeIsRespected) { ASSERT_TRUE(OpenPlaygroundHere(canvas.EndRecordingAsPicture())); } -TEST_F(AiksTest, TransformMultipliesCorrectly) { +TEST_P(AiksTest, TransformMultipliesCorrectly) { Canvas canvas; ASSERT_MATRIX_NEAR(canvas.GetCurrentTransformation(), Matrix()); @@ -505,7 +506,7 @@ TEST_F(AiksTest, TransformMultipliesCorrectly) { // clang-format on } -TEST_F(AiksTest, SolidStrokesRenderCorrectly) { +TEST_P(AiksTest, SolidStrokesRenderCorrectly) { // Compare with https://fiddle.skia.org/c/027392122bec8ac2b5d5de00a4b9bbe2 bool first_frame = true; auto callback = [&](AiksContext& renderer, RenderPass& pass) { @@ -578,7 +579,7 @@ TEST_F(AiksTest, SolidStrokesRenderCorrectly) { ASSERT_TRUE(OpenPlaygroundHere(callback)); } -TEST_F(AiksTest, CoverageOriginShouldBeAccountedForInSubpasses) { +TEST_P(AiksTest, CoverageOriginShouldBeAccountedForInSubpasses) { auto callback = [](AiksContext& renderer, RenderPass& pass) { Canvas canvas; Paint alpha; @@ -610,7 +611,7 @@ TEST_F(AiksTest, CoverageOriginShouldBeAccountedForInSubpasses) { ASSERT_TRUE(OpenPlaygroundHere(callback)); } -TEST_F(AiksTest, DrawRectStrokesRenderCorrectly) { +TEST_P(AiksTest, DrawRectStrokesRenderCorrectly) { Canvas canvas; Paint paint; paint.color = Color::Red(); diff --git a/impeller/base/config.h b/impeller/base/config.h index a0750d5db17fe..610661a48db33 100644 --- a/impeller/base/config.h +++ b/impeller/base/config.h @@ -4,6 +4,10 @@ #pragma once +#include + +#include "flutter/fml/logging.h" + #if defined(__GNUC__) || defined(__clang__) #define IMPELLER_COMPILER_CLANG 1 #else // defined(__GNUC__) || defined(__clang__) @@ -16,3 +20,18 @@ #else // IMPELLER_COMPILER_CLANG #define IMPELLER_PRINTF_FORMAT(format_number, args_number) #endif // IMPELLER_COMPILER_CLANG + +#define IMPELLER_UNIMPLEMENTED \ + impeller::ImpellerUnimplemented(__FUNCTION__, __FILE__, __LINE__); + +namespace impeller { + +[[noreturn]] inline void ImpellerUnimplemented(const char* method, + const char* file, + int line) { + FML_CHECK(false) << "Unimplemented: " << method << " in " << file << ":" + << line; + std::abort(); +} + +} // namespace impeller diff --git a/impeller/display_list/display_list_unittests.cc b/impeller/display_list/display_list_unittests.cc index 13160d39f5002..321832e721393 100644 --- a/impeller/display_list/display_list_unittests.cc +++ b/impeller/display_list/display_list_unittests.cc @@ -18,15 +18,16 @@ namespace impeller { namespace testing { using DisplayListTest = DisplayListPlayground; +INSTANTIATE_PLAYGROUND_SUITE(DisplayListTest); -TEST_F(DisplayListTest, CanDrawRect) { +TEST_P(DisplayListTest, CanDrawRect) { flutter::DisplayListBuilder builder; builder.setColor(SK_ColorBLUE); builder.drawRect(SkRect::MakeXYWH(10, 10, 100, 100)); ASSERT_TRUE(OpenPlaygroundHere(builder.Build())); } -TEST_F(DisplayListTest, CanDrawTextBlob) { +TEST_P(DisplayListTest, CanDrawTextBlob) { flutter::DisplayListBuilder builder; builder.setColor(SK_ColorBLUE); builder.drawTextBlob(SkTextBlob::MakeFromString("Hello", CreateTestFont()), @@ -34,7 +35,7 @@ TEST_F(DisplayListTest, CanDrawTextBlob) { ASSERT_TRUE(OpenPlaygroundHere(builder.Build())); } -TEST_F(DisplayListTest, CanDrawImage) { +TEST_P(DisplayListTest, CanDrawImage) { auto texture = CreateTextureForFixture("embarcadero.jpg"); flutter::DisplayListBuilder builder; builder.drawImage(DlImageImpeller::Make(texture), SkPoint::Make(100, 100), @@ -42,7 +43,7 @@ TEST_F(DisplayListTest, CanDrawImage) { ASSERT_TRUE(OpenPlaygroundHere(builder.Build())); } -TEST_F(DisplayListTest, CanDrawCapsAndJoins) { +TEST_P(DisplayListTest, CanDrawCapsAndJoins) { flutter::DisplayListBuilder builder; builder.setStyle(SkPaint::Style::kStroke_Style); @@ -87,7 +88,7 @@ TEST_F(DisplayListTest, CanDrawCapsAndJoins) { ASSERT_TRUE(OpenPlaygroundHere(builder.Build())); } -TEST_F(DisplayListTest, CanDrawArc) { +TEST_P(DisplayListTest, CanDrawArc) { bool first_frame = true; auto callback = [&]() { if (first_frame) { @@ -127,7 +128,7 @@ TEST_F(DisplayListTest, CanDrawArc) { ASSERT_TRUE(OpenPlaygroundHere(callback)); } -TEST_F(DisplayListTest, StrokedPathsDrawCorrectly) { +TEST_P(DisplayListTest, StrokedPathsDrawCorrectly) { flutter::DisplayListBuilder builder; builder.setColor(SK_ColorRED); builder.setStyle(SkPaint::Style::kStroke_Style); diff --git a/impeller/entity/contents/content_context.h b/impeller/entity/contents/content_context.h index 9898b217add98..4fc0fa74ad512 100644 --- a/impeller/entity/contents/content_context.h +++ b/impeller/entity/contents/content_context.h @@ -11,25 +11,25 @@ #include "flutter/fml/macros.h" #include "fml/logging.h" #include "impeller/base/validation.h" -#include "impeller/entity/border_mask_blur.frag.h" -#include "impeller/entity/border_mask_blur.vert.h" #include "impeller/entity/entity.h" -#include "impeller/entity/gaussian_blur.frag.h" -#include "impeller/entity/gaussian_blur.vert.h" -#include "impeller/entity/glyph_atlas.frag.h" -#include "impeller/entity/glyph_atlas.vert.h" -#include "impeller/entity/gradient_fill.frag.h" -#include "impeller/entity/gradient_fill.vert.h" -#include "impeller/entity/solid_fill.frag.h" -#include "impeller/entity/solid_fill.vert.h" -#include "impeller/entity/solid_stroke.frag.h" -#include "impeller/entity/solid_stroke.vert.h" -#include "impeller/entity/texture_blend.frag.h" -#include "impeller/entity/texture_blend.vert.h" -#include "impeller/entity/texture_blend_screen.frag.h" -#include "impeller/entity/texture_blend_screen.vert.h" -#include "impeller/entity/texture_fill.frag.h" -#include "impeller/entity/texture_fill.vert.h" +#include "impeller/entity/mtl/border_mask_blur.frag.h" +#include "impeller/entity/mtl/border_mask_blur.vert.h" +#include "impeller/entity/mtl/gaussian_blur.frag.h" +#include "impeller/entity/mtl/gaussian_blur.vert.h" +#include "impeller/entity/mtl/glyph_atlas.frag.h" +#include "impeller/entity/mtl/glyph_atlas.vert.h" +#include "impeller/entity/mtl/gradient_fill.frag.h" +#include "impeller/entity/mtl/gradient_fill.vert.h" +#include "impeller/entity/mtl/solid_fill.frag.h" +#include "impeller/entity/mtl/solid_fill.vert.h" +#include "impeller/entity/mtl/solid_stroke.frag.h" +#include "impeller/entity/mtl/solid_stroke.vert.h" +#include "impeller/entity/mtl/texture_blend.frag.h" +#include "impeller/entity/mtl/texture_blend.vert.h" +#include "impeller/entity/mtl/texture_blend_screen.frag.h" +#include "impeller/entity/mtl/texture_blend_screen.vert.h" +#include "impeller/entity/mtl/texture_fill.frag.h" +#include "impeller/entity/mtl/texture_fill.vert.h" #include "impeller/renderer/formats.h" namespace impeller { diff --git a/impeller/entity/contents/solid_stroke_contents.h b/impeller/entity/contents/solid_stroke_contents.h index bde9f332f9b54..e0d3f3abee260 100644 --- a/impeller/entity/contents/solid_stroke_contents.h +++ b/impeller/entity/contents/solid_stroke_contents.h @@ -10,7 +10,7 @@ #include "flutter/fml/macros.h" #include "impeller/entity/contents/contents.h" -#include "impeller/entity/solid_stroke.vert.h" +#include "impeller/entity/mtl/solid_stroke.vert.h" #include "impeller/geometry/color.h" #include "impeller/geometry/path_component.h" #include "impeller/geometry/point.h" diff --git a/impeller/entity/contents/texture_contents.cc b/impeller/entity/contents/texture_contents.cc index 024140093b994..77e0a16d06439 100644 --- a/impeller/entity/contents/texture_contents.cc +++ b/impeller/entity/contents/texture_contents.cc @@ -6,8 +6,8 @@ #include "impeller/entity/contents/content_context.h" #include "impeller/entity/entity.h" -#include "impeller/entity/texture_fill.frag.h" -#include "impeller/entity/texture_fill.vert.h" +#include "impeller/entity/mtl/texture_fill.frag.h" +#include "impeller/entity/mtl/texture_fill.vert.h" #include "impeller/renderer/render_pass.h" #include "impeller/renderer/sampler_library.h" #include "impeller/tessellator/tessellator.h" diff --git a/impeller/entity/entity_unittests.cc b/impeller/entity/entity_unittests.cc index 2978d2dd8018a..7401ddb187b9d 100644 --- a/impeller/entity/entity_unittests.cc +++ b/impeller/entity/entity_unittests.cc @@ -24,20 +24,21 @@ namespace impeller { namespace testing { using EntityTest = EntityPlayground; +INSTANTIATE_PLAYGROUND_SUITE(EntityTest); -TEST_F(EntityTest, CanCreateEntity) { +TEST_P(EntityTest, CanCreateEntity) { Entity entity; ASSERT_TRUE(entity.GetTransformation().IsIdentity()); } -TEST_F(EntityTest, CanDrawRect) { +TEST_P(EntityTest, CanDrawRect) { Entity entity; entity.SetPath(PathBuilder{}.AddRect({100, 100, 100, 100}).TakePath()); entity.SetContents(SolidColorContents::Make(Color::Red())); ASSERT_TRUE(OpenPlaygroundHere(entity)); } -TEST_F(EntityTest, ThreeStrokesInOnePath) { +TEST_P(EntityTest, ThreeStrokesInOnePath) { Path path = PathBuilder{} .MoveTo({100, 100}) .LineTo({100, 200}) @@ -56,7 +57,7 @@ TEST_F(EntityTest, ThreeStrokesInOnePath) { ASSERT_TRUE(OpenPlaygroundHere(entity)); } -TEST_F(EntityTest, TriangleInsideASquare) { +TEST_P(EntityTest, TriangleInsideASquare) { auto callback = [&](ContentContext& context, RenderPass& pass) { Point a = IMPELLER_PLAYGROUND_POINT(Point(10, 10), 20, Color::White()); Point b = IMPELLER_PLAYGROUND_POINT(Point(210, 10), 20, Color::White()); @@ -89,7 +90,7 @@ TEST_F(EntityTest, TriangleInsideASquare) { ASSERT_TRUE(OpenPlaygroundHere(callback)); } -TEST_F(EntityTest, StrokeCapAndJoinTest) { +TEST_P(EntityTest, StrokeCapAndJoinTest) { const Point padding(300, 250); const Point margin(140, 180); @@ -224,7 +225,7 @@ TEST_F(EntityTest, StrokeCapAndJoinTest) { ASSERT_TRUE(OpenPlaygroundHere(callback)); } -TEST_F(EntityTest, CubicCurveTest) { +TEST_P(EntityTest, CubicCurveTest) { // Compare with https://fiddle.skia.org/c/b3625f26122c9de7afe7794fcf25ead3 Path path = PathBuilder{} @@ -246,7 +247,7 @@ TEST_F(EntityTest, CubicCurveTest) { ASSERT_TRUE(OpenPlaygroundHere(entity)); } -TEST_F(EntityTest, CubicCurveAndOverlapTest) { +TEST_P(EntityTest, CubicCurveAndOverlapTest) { // Compare with https://fiddle.skia.org/c/7a05a3e186c65a8dfb732f68020aae06 Path path = PathBuilder{} @@ -474,7 +475,7 @@ TEST_F(EntityTest, CubicCurveAndOverlapTest) { ASSERT_TRUE(OpenPlaygroundHere(entity)); } -TEST_F(EntityTest, SolidStrokeContentsSetStrokeCapsAndJoins) { +TEST_P(EntityTest, SolidStrokeContentsSetStrokeCapsAndJoins) { { SolidStrokeContents stroke; // Defaults. @@ -495,7 +496,7 @@ TEST_F(EntityTest, SolidStrokeContentsSetStrokeCapsAndJoins) { } } -TEST_F(EntityTest, SolidStrokeContentsSetMiter) { +TEST_P(EntityTest, SolidStrokeContentsSetMiter) { SolidStrokeContents contents; ASSERT_FLOAT_EQ(contents.GetStrokeMiter(), 4); @@ -506,7 +507,7 @@ TEST_F(EntityTest, SolidStrokeContentsSetMiter) { ASSERT_FLOAT_EQ(contents.GetStrokeMiter(), 8); } -TEST_F(EntityTest, BlendingModeOptions) { +TEST_P(EntityTest, BlendingModeOptions) { std::vector blend_mode_names; std::vector blend_mode_values; { @@ -637,7 +638,7 @@ TEST_F(EntityTest, BlendingModeOptions) { ASSERT_TRUE(OpenPlaygroundHere(callback)); } -TEST_F(EntityTest, BezierCircleScaled) { +TEST_P(EntityTest, BezierCircleScaled) { Entity entity; auto path = PathBuilder{} .MoveTo({97.325, 34.818}) @@ -662,7 +663,7 @@ TEST_F(EntityTest, BezierCircleScaled) { ASSERT_TRUE(OpenPlaygroundHere(entity)); } -TEST_F(EntityTest, Filters) { +TEST_P(EntityTest, Filters) { auto bridge = CreateTextureForFixture("bay_bridge.jpg"); auto boston = CreateTextureForFixture("boston.jpg"); auto kalimba = CreateTextureForFixture("kalimba.jpg"); @@ -688,7 +689,7 @@ TEST_F(EntityTest, Filters) { ASSERT_TRUE(OpenPlaygroundHere(callback)); } -TEST_F(EntityTest, GaussianBlurFilter) { +TEST_P(EntityTest, GaussianBlurFilter) { auto bridge = CreateTextureForFixture("bay_bridge.jpg"); auto boston = CreateTextureForFixture("boston.jpg"); auto kalimba = CreateTextureForFixture("kalimba.jpg"); @@ -792,14 +793,14 @@ TEST_F(EntityTest, GaussianBlurFilter) { ASSERT_TRUE(OpenPlaygroundHere(callback)); } -TEST_F(EntityTest, SetBlendMode) { +TEST_P(EntityTest, SetBlendMode) { Entity entity; ASSERT_EQ(entity.GetBlendMode(), Entity::BlendMode::kSourceOver); entity.SetBlendMode(Entity::BlendMode::kClear); ASSERT_EQ(entity.GetBlendMode(), Entity::BlendMode::kClear); } -TEST_F(EntityTest, ContentsGetBoundsForEmptyPathReturnsNullopt) { +TEST_P(EntityTest, ContentsGetBoundsForEmptyPathReturnsNullopt) { Entity entity; entity.SetContents(std::make_shared()); entity.SetPath({}); @@ -807,7 +808,7 @@ TEST_F(EntityTest, ContentsGetBoundsForEmptyPathReturnsNullopt) { ASSERT_FALSE(entity.GetPathCoverage().has_value()); } -TEST_F(EntityTest, SolidStrokeCoverageIsCorrect) { +TEST_P(EntityTest, SolidStrokeCoverageIsCorrect) { { Entity entity; auto contents = std::make_unique(); diff --git a/impeller/playground/BUILD.gn b/impeller/playground/BUILD.gn index 5f9618e98db35..dc4d452ecfb59 100644 --- a/impeller/playground/BUILD.gn +++ b/impeller/playground/BUILD.gn @@ -9,12 +9,28 @@ impeller_component("playground") { testonly = true sources = [ + "playground.cc", "playground.h", - "playground.mm", + "playground_impl.cc", + "playground_impl.h", "widgets.cc", "widgets.h", ] + if (impeller_enable_metal) { + sources += [ + "backend/metal/playground_impl_mtl.h", + "backend/metal/playground_impl_mtl.mm", + ] + } + + if (impeller_enable_opengles) { + sources += [ + "backend/gles/playground_impl_gles.cc", + "backend/gles/playground_impl_gles.h", + ] + } + public_deps = [ "../entity:entity_shaders", "../fixtures:shader_fixtures", diff --git a/impeller/playground/backend/gles/playground_impl_gles.cc b/impeller/playground/backend/gles/playground_impl_gles.cc new file mode 100644 index 0000000000000..b5a47e2bc514b --- /dev/null +++ b/impeller/playground/backend/gles/playground_impl_gles.cc @@ -0,0 +1,37 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "impeller/playground/backend/gles/playground_impl_gles.h" +#include "impeller/renderer/backend/gles/context_gles.h" + +namespace impeller { + +PlaygroundImplGLES::PlaygroundImplGLES() = default; + +PlaygroundImplGLES::~PlaygroundImplGLES() = default; + +// |PlaygroundImpl| +std::shared_ptr PlaygroundImplGLES::CreateContext() const { + return std::make_shared(); +} + +// |PlaygroundImpl| +bool PlaygroundImplGLES::SetupWindow(WindowHandle handle, + std::shared_ptr context) { + return true; +} + +// |PlaygroundImpl| +bool PlaygroundImplGLES::TeardownWindow(WindowHandle handle, + std::shared_ptr context) { + return true; +} + +// |PlaygroundImpl| +std::unique_ptr PlaygroundImplGLES::AcquireSurfaceFrame( + std::shared_ptr context) { + return nullptr; +} + +} // namespace impeller diff --git a/impeller/playground/backend/gles/playground_impl_gles.h b/impeller/playground/backend/gles/playground_impl_gles.h new file mode 100644 index 0000000000000..246d099ea188b --- /dev/null +++ b/impeller/playground/backend/gles/playground_impl_gles.h @@ -0,0 +1,37 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#pragma once + +#include "flutter/fml/macros.h" +#include "impeller/playground/playground_impl.h" + +namespace impeller { + +class PlaygroundImplGLES final : public PlaygroundImpl { + public: + PlaygroundImplGLES(); + + ~PlaygroundImplGLES(); + + private: + // |PlaygroundImpl| + std::shared_ptr CreateContext() const override; + + // |PlaygroundImpl| + bool SetupWindow(WindowHandle handle, + std::shared_ptr context) override; + + // |PlaygroundImpl| + bool TeardownWindow(WindowHandle handle, + std::shared_ptr context) override; + + // |PlaygroundImpl| + std::unique_ptr AcquireSurfaceFrame( + std::shared_ptr context) override; + + FML_DISALLOW_COPY_AND_ASSIGN(PlaygroundImplGLES); +}; + +} // namespace impeller diff --git a/impeller/playground/backend/metal/playground_impl_mtl.h b/impeller/playground/backend/metal/playground_impl_mtl.h new file mode 100644 index 0000000000000..75f9ef5ba62de --- /dev/null +++ b/impeller/playground/backend/metal/playground_impl_mtl.h @@ -0,0 +1,45 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#pragma once + +#include + +#include "flutter/fml/macros.h" +#include "impeller/playground/playground_impl.h" + +namespace impeller { + +class PlaygroundImplMTL final : public PlaygroundImpl { + public: + PlaygroundImplMTL(); + + ~PlaygroundImplMTL(); + + private: + struct Data; + + WindowHandle handle_ = nullptr; + // To ensure that ObjC stuff doesn't leak into C++ TUs. + std::unique_ptr data_; + + // |PlaygroundImpl| + std::shared_ptr CreateContext() const override; + + // |PlaygroundImpl| + bool SetupWindow(WindowHandle handle, + std::shared_ptr context) override; + + // |PlaygroundImpl| + bool TeardownWindow(WindowHandle handle, + std::shared_ptr context) override; + + // |PlaygroundImpl| + std::unique_ptr AcquireSurfaceFrame( + std::shared_ptr context) override; + + FML_DISALLOW_COPY_AND_ASSIGN(PlaygroundImplMTL); +}; + +} // namespace impeller diff --git a/impeller/playground/backend/metal/playground_impl_mtl.mm b/impeller/playground/backend/metal/playground_impl_mtl.mm new file mode 100644 index 0000000000000..f791df7f16c95 --- /dev/null +++ b/impeller/playground/backend/metal/playground_impl_mtl.mm @@ -0,0 +1,95 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "impeller/playground/backend/metal/playground_impl_mtl.h" + +#define GLFW_INCLUDE_NONE +#import "third_party/glfw/include/GLFW/glfw3.h" + +#define GLFW_EXPOSE_NATIVE_COCOA +#import "third_party/glfw/include/GLFW/glfw3native.h" + +#include +#include + +#include "flutter/fml/mapping.h" +#include "impeller/entity/mtl/entity_shaders.h" +#include "impeller/fixtures/mtl/shader_fixtures.h" +#include "impeller/playground/imgui/mtl/imgui_shaders.h" +#include "impeller/renderer/backend/metal/context_mtl.h" +#include "impeller/renderer/backend/metal/formats_mtl.h" +#include "impeller/renderer/backend/metal/surface_mtl.h" +#include "impeller/renderer/backend/metal/texture_mtl.h" + +namespace impeller { + +struct PlaygroundImplMTL::Data { + CAMetalLayer* metal_layer = nil; +}; + +static std::vector> +ShaderLibraryMappingsForPlayground() { + return { + std::make_shared(impeller_entity_shaders_data, + impeller_entity_shaders_length), + std::make_shared(impeller_shader_fixtures_data, + impeller_shader_fixtures_length), + std::make_shared(impeller_imgui_shaders_data, + impeller_imgui_shaders_length), + + }; +} + +PlaygroundImplMTL::PlaygroundImplMTL() : data_(std::make_unique()) {} + +PlaygroundImplMTL::~PlaygroundImplMTL() = default; + +std::shared_ptr PlaygroundImplMTL::CreateContext() const { + return ContextMTL::Create(ShaderLibraryMappingsForPlayground(), + "Playground Library"); +} + +bool PlaygroundImplMTL::SetupWindow(WindowHandle handle, + std::shared_ptr context) { + if (handle_ != nullptr) { + return false; + } + + handle_ = handle; + + NSWindow* cocoa_window = + ::glfwGetCocoaWindow(reinterpret_cast(handle_)); + data_->metal_layer = [CAMetalLayer layer]; + data_->metal_layer.device = ContextMTL::Cast(*context).GetMTLDevice(); + // This pixel format is one of the documented supported formats. + data_->metal_layer.pixelFormat = ToMTLPixelFormat(PixelFormat::kDefaultColor); + cocoa_window.contentView.layer = data_->metal_layer; + cocoa_window.contentView.wantsLayer = YES; + return true; +} + +bool PlaygroundImplMTL::TeardownWindow(WindowHandle handle, + std::shared_ptr context) { + if (handle_ != handle) { + return false; + } + handle_ = nullptr; + data_->metal_layer = nil; + return true; +} + +std::unique_ptr PlaygroundImplMTL::AcquireSurfaceFrame( + std::shared_ptr context) { + if (!data_->metal_layer) { + return nullptr; + } + + const auto layer_size = data_->metal_layer.bounds.size; + const auto layer_scale = data_->metal_layer.contentsScale; + data_->metal_layer.drawableSize = CGSizeMake(layer_size.width * layer_scale, + layer_size.height * layer_scale); + return SurfaceMTL::WrapCurrentMetalLayerDrawable(context, data_->metal_layer); +} + +} // namespace impeller diff --git a/impeller/playground/imgui/imgui_impl_impeller.cc b/impeller/playground/imgui/imgui_impl_impeller.cc index b42696c4dc6e4..5703e2be59a2c 100644 --- a/impeller/playground/imgui/imgui_impl_impeller.cc +++ b/impeller/playground/imgui/imgui_impl_impeller.cc @@ -9,8 +9,8 @@ #include #include -#include "imgui_raster.frag.h" -#include "imgui_raster.vert.h" +#include "impeller/playground/imgui/mtl/imgui_raster.frag.h" +#include "impeller/playground/imgui/mtl/imgui_raster.vert.h" #include "third_party/imgui/imgui.h" #include "impeller/geometry/matrix.h" diff --git a/impeller/playground/playground.mm b/impeller/playground/playground.cc similarity index 77% rename from impeller/playground/playground.mm rename to impeller/playground/playground.cc index c8a157af957db..e843a6ee5c64b 100644 --- a/impeller/playground/playground.mm +++ b/impeller/playground/playground.cc @@ -4,20 +4,17 @@ #include +#define GLFW_INCLUDE_NONE +#import "third_party/glfw/include/GLFW/glfw3.h" + #include "flutter/fml/paths.h" #include "flutter/testing/testing.h" #include "impeller/base/validation.h" -#include "impeller/entity/entity_shaders.h" -#include "impeller/fixtures/shader_fixtures.h" #include "impeller/image/compressed_image.h" #include "impeller/playground/imgui/imgui_impl_impeller.h" -#include "impeller/playground/imgui/imgui_shaders.h" #include "impeller/playground/playground.h" +#include "impeller/playground/playground_impl.h" #include "impeller/renderer/allocator.h" -#include "impeller/renderer/backend/metal/context_mtl.h" -#include "impeller/renderer/backend/metal/formats_mtl.h" -#include "impeller/renderer/backend/metal/surface_mtl.h" -#include "impeller/renderer/backend/metal/texture_mtl.h" #include "impeller/renderer/context.h" #include "impeller/renderer/formats.h" #include "impeller/renderer/render_pass.h" @@ -25,37 +22,35 @@ #include "third_party/imgui/backends/imgui_impl_glfw.h" #include "third_party/imgui/imgui.h" -#define GLFW_INCLUDE_NONE -#import "third_party/glfw/include/GLFW/glfw3.h" -#define GLFW_EXPOSE_NATIVE_COCOA -#import "third_party/glfw/include/GLFW/glfw3native.h" - -#include -#include - namespace impeller { -static std::vector> -ShaderLibraryMappingsForPlayground() { - return { - std::make_shared(impeller_entity_shaders_data, - impeller_entity_shaders_length), - std::make_shared(impeller_shader_fixtures_data, - impeller_shader_fixtures_length), - std::make_shared(impeller_imgui_shaders_data, - impeller_imgui_shaders_length), - - }; +std::string PlaygroundBackendToString(PlaygroundBackend backend) { + switch (backend) { + case PlaygroundBackend::kMetal: + return "Metal"; + case PlaygroundBackend::kOpenGLES: + return "OpenGLES"; + } + FML_UNREACHABLE(); } Playground::Playground() - : renderer_(ContextMTL::Create(ShaderLibraryMappingsForPlayground(), - "Playground Library")) {} + : impl_(PlaygroundImpl::Create(GetParam())), + renderer_(impl_->CreateContext()), + is_valid_(Playground::is_enabled() && renderer_.IsValid()) {} Playground::~Playground() = default; +bool Playground::IsValid() const { + return is_valid_; +} + +PlaygroundBackend Playground::GetBackend() const { + return GetParam(); +} + std::shared_ptr Playground::GetContext() const { - return renderer_.IsValid() ? renderer_.GetContext() : nullptr; + return IsValid() ? renderer_.GetContext() : nullptr; } static void PlaygroundKeyCallback(GLFWwindow* window, @@ -92,6 +87,10 @@ static void PlaygroundKeyCallback(GLFWwindow* window, return true; } + if (!IsValid()) { + return false; + } + if (!render_callback) { return true; } @@ -169,13 +168,9 @@ static void PlaygroundKeyCallback(GLFWwindow* window, fml::ScopedCleanupClosure shutdown_imgui_impeller( []() { ImGui_ImplImpeller_Shutdown(); }); - NSWindow* cocoa_window = ::glfwGetCocoaWindow(window); - CAMetalLayer* layer = [CAMetalLayer layer]; - layer.device = ContextMTL::Cast(*renderer_.GetContext()).GetMTLDevice(); - // This pixel format is one of the documented supported formats. - layer.pixelFormat = ToMTLPixelFormat(PixelFormat::kDefaultColor); - cocoa_window.contentView.layer = layer; - cocoa_window.contentView.wantsLayer = YES; + if (!impl_->SetupWindow(window, renderer_.GetContext())) { + return false; + } while (true) { ::glfwWaitEventsTimeout(1.0 / 30.0); @@ -186,11 +181,6 @@ static void PlaygroundKeyCallback(GLFWwindow* window, ImGui_ImplGlfw_NewFrame(); - const auto layer_size = layer.bounds.size; - const auto layer_scale = layer.contentsScale; - layer.drawableSize = CGSizeMake(layer_size.width * layer_scale, - layer_size.height * layer_scale); - Renderer::RenderCallback wrapped_callback = [render_callback](auto& pass) { pass.SetLabel("Playground Main Render Pass"); @@ -201,19 +191,26 @@ static void PlaygroundKeyCallback(GLFWwindow* window, return result; }; - if (!renderer_.Render(SurfaceMTL::WrapCurrentMetalLayerDrawable( - renderer_.GetContext(), layer), + if (!renderer_.Render(impl_->AcquireSurfaceFrame(renderer_.GetContext()), wrapped_callback)) { VALIDATION_LOG << "Could not render into the surface."; return false; } } + if (!impl_->TeardownWindow(window, renderer_.GetContext())) { + return false; + } + return true; } std::shared_ptr Playground::CreateTextureForFixture( const char* fixture_name) const { + if (!IsValid()) { + return nullptr; + } + auto compressed_image = CompressedImage::Create( flutter::testing::OpenFixtureAsMapping(fixture_name)); if (!compressed_image) { diff --git a/impeller/playground/playground.h b/impeller/playground/playground.h index b1779a624d956..dc382b0a456c4 100644 --- a/impeller/playground/playground.h +++ b/impeller/playground/playground.h @@ -13,7 +13,16 @@ namespace impeller { -class Playground : public ::testing::Test { +class PlaygroundImpl; + +enum class PlaygroundBackend { + kMetal, + kOpenGLES, +}; + +std::string PlaygroundBackendToString(PlaygroundBackend backend); + +class Playground : public ::testing::TestWithParam { public: Playground(); @@ -21,6 +30,10 @@ class Playground : public ::testing::Test { static constexpr bool is_enabled() { return is_enabled_; } + PlaygroundBackend GetBackend() const; + + bool IsValid() const; + Point GetCursorPosition() const; ISize GetWindowSize() const; @@ -39,9 +52,11 @@ class Playground : public ::testing::Test { static const bool is_enabled_ = false; #endif // IMPELLER_ENABLE_PLAYGROUND + std::unique_ptr impl_; Renderer renderer_; Point cursor_position_; ISize window_size_ = ISize{1024, 768}; + bool is_valid_ = false; void SetCursorPosition(Point pos); @@ -50,4 +65,11 @@ class Playground : public ::testing::Test { FML_DISALLOW_COPY_AND_ASSIGN(Playground); }; +#define INSTANTIATE_PLAYGROUND_SUITE(playground) \ + INSTANTIATE_TEST_SUITE_P( \ + Play, playground, ::testing::Values(PlaygroundBackend::kMetal), \ + [](const ::testing::TestParamInfo& info) { \ + return PlaygroundBackendToString(info.param); \ + }); + } // namespace impeller diff --git a/impeller/playground/playground_impl.cc b/impeller/playground/playground_impl.cc new file mode 100644 index 0000000000000..276b0f9e6cb65 --- /dev/null +++ b/impeller/playground/playground_impl.cc @@ -0,0 +1,27 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "impeller/playground/playground_impl.h" + +#include "impeller/playground/backend/gles/playground_impl_gles.h" +#include "impeller/playground/backend/metal/playground_impl_mtl.h" + +namespace impeller { + +std::unique_ptr PlaygroundImpl::Create( + PlaygroundBackend backend) { + switch (backend) { + case PlaygroundBackend::kMetal: + return std::make_unique(); + case PlaygroundBackend::kOpenGLES: + return std::make_unique(); + } + FML_UNREACHABLE(); +} + +PlaygroundImpl::PlaygroundImpl() = default; + +PlaygroundImpl::~PlaygroundImpl() = default; + +} // namespace impeller diff --git a/impeller/playground/playground_impl.h b/impeller/playground/playground_impl.h new file mode 100644 index 0000000000000..c28376e3fe804 --- /dev/null +++ b/impeller/playground/playground_impl.h @@ -0,0 +1,42 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#pragma once + +#include + +#include "flutter/fml/macros.h" +#include "impeller/playground/playground.h" +#include "impeller/renderer/context.h" +#include "impeller/renderer/surface.h" + +namespace impeller { + +class PlaygroundImpl { + public: + static std::unique_ptr Create(PlaygroundBackend backend); + + virtual ~PlaygroundImpl(); + + virtual std::shared_ptr CreateContext() const = 0; + + using WindowHandle = void*; + + virtual bool SetupWindow(WindowHandle handle, + std::shared_ptr context) = 0; + + virtual bool TeardownWindow(WindowHandle handle, + std::shared_ptr context) = 0; + + virtual std::unique_ptr AcquireSurfaceFrame( + std::shared_ptr context) = 0; + + protected: + PlaygroundImpl(); + + private: + FML_DISALLOW_COPY_AND_ASSIGN(PlaygroundImpl); +}; + +} // namespace impeller diff --git a/impeller/renderer/BUILD.gn b/impeller/renderer/BUILD.gn index 49cb4d6d4aa7d..1c0f72794a4fa 100644 --- a/impeller/renderer/BUILD.gn +++ b/impeller/renderer/BUILD.gn @@ -5,101 +5,142 @@ import("//flutter/impeller/tools/impeller.gni") impeller_component("renderer") { - metal_backend_sources = [ - "backend/metal/allocator_mtl.h", - "backend/metal/allocator_mtl.mm", - "backend/metal/command_buffer_mtl.h", - "backend/metal/command_buffer_mtl.mm", - "backend/metal/context_mtl.h", - "backend/metal/context_mtl.mm", - "backend/metal/device_buffer_mtl.h", - "backend/metal/device_buffer_mtl.mm", - "backend/metal/formats_mtl.h", - "backend/metal/formats_mtl.mm", - "backend/metal/pipeline_library_mtl.h", - "backend/metal/pipeline_library_mtl.mm", - "backend/metal/pipeline_mtl.h", - "backend/metal/pipeline_mtl.mm", - "backend/metal/render_pass_mtl.h", - "backend/metal/render_pass_mtl.mm", - "backend/metal/sampler_library_mtl.h", - "backend/metal/sampler_library_mtl.mm", - "backend/metal/sampler_mtl.h", - "backend/metal/sampler_mtl.mm", - "backend/metal/shader_function_mtl.h", - "backend/metal/shader_function_mtl.mm", - "backend/metal/shader_library_mtl.h", - "backend/metal/shader_library_mtl.mm", - "backend/metal/surface_mtl.h", - "backend/metal/surface_mtl.mm", - "backend/metal/texture_mtl.h", - "backend/metal/texture_mtl.mm", - "backend/metal/vertex_descriptor_mtl.h", - "backend/metal/vertex_descriptor_mtl.mm", + sources = [ + "allocator.cc", + "allocator.h", + "buffer.cc", + "buffer.h", + "buffer_view.cc", + "buffer_view.h", + "command.cc", + "command.h", + "command_buffer.cc", + "command_buffer.h", + "context.cc", + "context.h", + "device_buffer.cc", + "device_buffer.h", + "formats.cc", + "formats.h", + "host_buffer.cc", + "host_buffer.h", + "pipeline.cc", + "pipeline.h", + "pipeline_builder.cc", + "pipeline_builder.h", + "pipeline_descriptor.cc", + "pipeline_descriptor.h", + "pipeline_library.cc", + "pipeline_library.h", + "platform.cc", + "platform.h", + "range.cc", + "range.h", + "render_pass.cc", + "render_pass.h", + "render_target.cc", + "render_target.h", + "renderer.cc", + "renderer.h", + "sampler.cc", + "sampler.h", + "sampler_descriptor.cc", + "sampler_descriptor.h", + "sampler_library.cc", + "sampler_library.h", + "shader_function.cc", + "shader_function.h", + "shader_library.cc", + "shader_library.h", + "shader_types.cc", + "shader_types.h", + "surface.cc", + "surface.h", + "texture.cc", + "texture.h", + "texture_descriptor.cc", + "texture_descriptor.h", + "vertex_buffer.cc", + "vertex_buffer.h", + "vertex_buffer_builder.cc", + "vertex_buffer_builder.h", + "vertex_descriptor.cc", + "vertex_descriptor.h", ] - sources = [ - "allocator.h", - "allocator.cc", - "buffer.h", - "buffer.cc", - "buffer_view.h", - "buffer_view.cc", - "command.h", - "command.cc", - "command_buffer.h", - "command_buffer.cc", - "context.h", - "context.cc", - "device_buffer.h", - "device_buffer.cc", - "formats.cc", - "formats.h", - "host_buffer.h", - "host_buffer.cc", - "pipeline.h", - "pipeline.cc", - "pipeline_builder.h", - "pipeline_builder.cc", - "pipeline_descriptor.h", - "pipeline_descriptor.cc", - "pipeline_library.h", - "pipeline_library.cc", - "platform.h", - "platform.cc", - "range.cc", - "range.h", - "render_pass.h", - "render_pass.cc", - "render_target.h", - "render_target.cc", - "renderer.h", - "renderer.cc", - "sampler.h", - "sampler.cc", - "sampler_descriptor.h", - "sampler_descriptor.cc", - "sampler_library.h", - "sampler_library.cc", - "shader_function.h", - "shader_function.cc", - "shader_library.h", - "shader_library.cc", - "shader_types.cc", - "shader_types.h", - "surface.h", - "surface.cc", - "texture.h", - "texture.cc", - "texture_descriptor.h", - "texture_descriptor.cc", - "vertex_buffer.h", - "vertex_buffer.cc", - "vertex_buffer_builder.h", - "vertex_buffer_builder.cc", - "vertex_descriptor.h", - "vertex_descriptor.cc", - ] + metal_backend_sources + if (impeller_enable_metal) { + sources += [ + "backend/metal/allocator_mtl.h", + "backend/metal/allocator_mtl.mm", + "backend/metal/command_buffer_mtl.h", + "backend/metal/command_buffer_mtl.mm", + "backend/metal/context_mtl.h", + "backend/metal/context_mtl.mm", + "backend/metal/device_buffer_mtl.h", + "backend/metal/device_buffer_mtl.mm", + "backend/metal/formats_mtl.h", + "backend/metal/formats_mtl.mm", + "backend/metal/pipeline_library_mtl.h", + "backend/metal/pipeline_library_mtl.mm", + "backend/metal/pipeline_mtl.h", + "backend/metal/pipeline_mtl.mm", + "backend/metal/render_pass_mtl.h", + "backend/metal/render_pass_mtl.mm", + "backend/metal/sampler_library_mtl.h", + "backend/metal/sampler_library_mtl.mm", + "backend/metal/sampler_mtl.h", + "backend/metal/sampler_mtl.mm", + "backend/metal/shader_function_mtl.h", + "backend/metal/shader_function_mtl.mm", + "backend/metal/shader_library_mtl.h", + "backend/metal/shader_library_mtl.mm", + "backend/metal/surface_mtl.h", + "backend/metal/surface_mtl.mm", + "backend/metal/texture_mtl.h", + "backend/metal/texture_mtl.mm", + "backend/metal/vertex_descriptor_mtl.h", + "backend/metal/vertex_descriptor_mtl.mm", + ] + } + + if (impeller_enable_opengles) { + sources += [ + "backend/gles/allocator_gles.cc", + "backend/gles/allocator_gles.h", + "backend/gles/command_buffer_gles.cc", + "backend/gles/command_buffer_gles.h", + "backend/gles/context_gles.cc", + "backend/gles/context_gles.h", + "backend/gles/device_buffer_gles.cc", + "backend/gles/device_buffer_gles.h", + "backend/gles/formats_gles.cc", + "backend/gles/formats_gles.h", + "backend/gles/pipeline_gles.cc", + "backend/gles/pipeline_gles.h", + "backend/gles/pipeline_library_gles.cc", + "backend/gles/pipeline_library_gles.h", + "backend/gles/proc_table_gles.cc", + "backend/gles/proc_table_gles.h", + "backend/gles/reactor_gles.cc", + "backend/gles/reactor_gles.h", + "backend/gles/render_pass_gles.cc", + "backend/gles/render_pass_gles.h", + "backend/gles/sampler_gles.cc", + "backend/gles/sampler_gles.h", + "backend/gles/sampler_library_gles.cc", + "backend/gles/sampler_library_gles.h", + "backend/gles/shader_function_gles.cc", + "backend/gles/shader_function_gles.h", + "backend/gles/shader_library_gles.cc", + "backend/gles/shader_library_gles.h", + "backend/gles/surface_gles.cc", + "backend/gles/surface_gles.h", + "backend/gles/texture_gles.cc", + "backend/gles/texture_gles.h", + "backend/gles/vertex_descriptor_gles.cc", + "backend/gles/vertex_descriptor_gles.h", + ] + } public_deps = [ "../base", @@ -109,8 +150,11 @@ impeller_component("renderer") { ] deps = [ "//flutter/fml" ] + frameworks = [] - frameworks = [ "Metal.framework" ] + if (impeller_enable_metal) { + frameworks = [ "Metal.framework" ] + } } source_set("renderer_unittests") { diff --git a/impeller/renderer/backend/gles/allocator_gles.cc b/impeller/renderer/backend/gles/allocator_gles.cc new file mode 100644 index 0000000000000..d4f2c4e726ce8 --- /dev/null +++ b/impeller/renderer/backend/gles/allocator_gles.cc @@ -0,0 +1,11 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "impeller/renderer/backend/gles/allocator_gles.h" + +namespace impeller { + +// + +} // namespace impeller diff --git a/impeller/renderer/backend/gles/allocator_gles.h b/impeller/renderer/backend/gles/allocator_gles.h new file mode 100644 index 0000000000000..9a4cdf09172e8 --- /dev/null +++ b/impeller/renderer/backend/gles/allocator_gles.h @@ -0,0 +1,45 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#pragma once + +#include "flutter/fml/macros.h" +#include "impeller/renderer/allocator.h" + +namespace impeller { + +class AllocatorGLES final : public Allocator { + public: + // |Allocator| + ~AllocatorGLES() override; + + private: + friend class ContextGLES; + + AllocatorGLES(); + + // |Allocator| + bool IsValid() const; + + // |Allocator| + std::shared_ptr CreateBuffer(StorageMode mode, + size_t length) override; + + // |Allocator| + std::shared_ptr CreateTexture( + StorageMode mode, + const TextureDescriptor& desc) override; + + // |Allocator| + std::shared_ptr CreateBufferWithCopy(const uint8_t* buffer, + size_t length) override; + + // |Allocator| + std::shared_ptr CreateBufferWithCopy( + const fml::Mapping& mapping) override; + + FML_DISALLOW_COPY_AND_ASSIGN(AllocatorGLES); +}; + +} // namespace impeller diff --git a/impeller/renderer/backend/gles/command_buffer_gles.cc b/impeller/renderer/backend/gles/command_buffer_gles.cc new file mode 100644 index 0000000000000..c0c6fc694ac7c --- /dev/null +++ b/impeller/renderer/backend/gles/command_buffer_gles.cc @@ -0,0 +1,39 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "impeller/renderer/backend/gles/command_buffer_gles.h" + +#include "impeller/base/config.h" + +namespace impeller { + +CommandBufferGLES::CommandBufferGLES() = default; + +CommandBufferGLES::~CommandBufferGLES() = default; + +// |CommandBuffer| +void CommandBufferGLES::SetLabel(const std::string& label) const {} + +// |CommandBuffer| +bool CommandBufferGLES::IsValid() const { + IMPELLER_UNIMPLEMENTED; +} + +// |CommandBuffer| +bool CommandBufferGLES::SubmitCommands(CompletionCallback callback) { + IMPELLER_UNIMPLEMENTED; +} + +// |CommandBuffer| +void CommandBufferGLES::ReserveSpotInQueue() { + IMPELLER_UNIMPLEMENTED; +} + +// |CommandBuffer| +std::shared_ptr CommandBufferGLES::CreateRenderPass( + RenderTarget target) const { + IMPELLER_UNIMPLEMENTED; +} + +} // namespace impeller diff --git a/impeller/renderer/backend/gles/command_buffer_gles.h b/impeller/renderer/backend/gles/command_buffer_gles.h new file mode 100644 index 0000000000000..6819f0f50b5b9 --- /dev/null +++ b/impeller/renderer/backend/gles/command_buffer_gles.h @@ -0,0 +1,42 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#pragma once + +#include "flutter/fml/macros.h" +#include "impeller/renderer/command_buffer.h" +#include "impeller/renderer/render_target.h" + +namespace impeller { + +class CommandBufferGLES final : public CommandBuffer { + public: + // |CommandBuffer| + ~CommandBufferGLES() override; + + private: + friend class ContextGLES; + + CommandBufferGLES(); + + // |CommandBuffer| + void SetLabel(const std::string& label) const override; + + // |CommandBuffer| + bool IsValid() const override; + + // |CommandBuffer| + bool SubmitCommands(CompletionCallback callback) override; + + // |CommandBuffer| + void ReserveSpotInQueue() override; + + // |CommandBuffer| + std::shared_ptr CreateRenderPass( + RenderTarget target) const override; + + FML_DISALLOW_COPY_AND_ASSIGN(CommandBufferGLES); +}; + +} // namespace impeller diff --git a/impeller/renderer/backend/gles/context_gles.cc b/impeller/renderer/backend/gles/context_gles.cc new file mode 100644 index 0000000000000..efb762ddacc6f --- /dev/null +++ b/impeller/renderer/backend/gles/context_gles.cc @@ -0,0 +1,64 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "impeller/renderer/backend/gles/context_gles.h" + +#include "impeller/base/config.h" +#include "impeller/base/validation.h" + +namespace impeller { + +ContextGLES::ContextGLES() { + auto reactor = std::make_shared(); + if (!reactor->IsValid()) { + VALIDATION_LOG << "Could not create valid reactor."; + return; + } + + is_valid_ = true; +} + +ContextGLES::~ContextGLES() = default; + +bool ContextGLES::IsValid() const { + return is_valid_; +} + +std::shared_ptr ContextGLES::GetPermanentsAllocator() const { + IMPELLER_UNIMPLEMENTED; + return permanents_allocator_; +} + +std::shared_ptr ContextGLES::GetTransientsAllocator() const { + IMPELLER_UNIMPLEMENTED; + return transients_allocator_; +} + +std::shared_ptr ContextGLES::GetShaderLibrary() const { + IMPELLER_UNIMPLEMENTED; + return shader_library_; +} + +std::shared_ptr ContextGLES::GetSamplerLibrary() const { + IMPELLER_UNIMPLEMENTED; + return sampler_library_; +} + +std::shared_ptr ContextGLES::GetPipelineLibrary() const { + IMPELLER_UNIMPLEMENTED; + return pipeline_library_; +} + +std::shared_ptr ContextGLES::CreateRenderCommandBuffer() const { + IMPELLER_UNIMPLEMENTED; + return std::shared_ptr(new CommandBufferGLES()); +} + +std::shared_ptr ContextGLES::CreateTransferCommandBuffer() + const { + IMPELLER_UNIMPLEMENTED; + return std::shared_ptr(new CommandBufferGLES()); +} + +} // namespace impeller diff --git a/impeller/renderer/backend/gles/context_gles.h b/impeller/renderer/backend/gles/context_gles.h new file mode 100644 index 0000000000000..15e090752c59c --- /dev/null +++ b/impeller/renderer/backend/gles/context_gles.h @@ -0,0 +1,62 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#pragma once + +#include "flutter/fml/macros.h" +#include "impeller/base/backend_cast.h" +#include "impeller/renderer/backend/gles/allocator_gles.h" +#include "impeller/renderer/backend/gles/command_buffer_gles.h" +#include "impeller/renderer/backend/gles/pipeline_library_gles.h" +#include "impeller/renderer/backend/gles/reactor_gles.h" +#include "impeller/renderer/backend/gles/shader_library_gles.h" +#include "impeller/renderer/context.h" + +namespace impeller { + +class ContextGLES final : public Context, + public BackendCast { + public: + ContextGLES(); + + // |Context| + ~ContextGLES() override; + + private: + std::shared_ptr reactor_; + std::shared_ptr shader_library_; + std::shared_ptr pipeline_library_; + std::shared_ptr sampler_library_; + std::shared_ptr permanents_allocator_; + std::shared_ptr transients_allocator_; + bool is_valid_ = false; + + // |Context| + bool IsValid() const override; + + // |Context| + std::shared_ptr GetPermanentsAllocator() const override; + + // |Context| + std::shared_ptr GetTransientsAllocator() const override; + + // |Context| + std::shared_ptr GetShaderLibrary() const override; + + // |Context| + std::shared_ptr GetSamplerLibrary() const override; + + // |Context| + std::shared_ptr GetPipelineLibrary() const override; + + // |Context| + std::shared_ptr CreateRenderCommandBuffer() const override; + + // |Context| + std::shared_ptr CreateTransferCommandBuffer() const override; + + FML_DISALLOW_COPY_AND_ASSIGN(ContextGLES); +}; + +} // namespace impeller diff --git a/impeller/renderer/backend/gles/device_buffer_gles.cc b/impeller/renderer/backend/gles/device_buffer_gles.cc new file mode 100644 index 0000000000000..1a4392a71b757 --- /dev/null +++ b/impeller/renderer/backend/gles/device_buffer_gles.cc @@ -0,0 +1,11 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "impeller/renderer/backend/gles/device_buffer_gles.h" + +namespace impeller { + +// + +} // namespace impeller diff --git a/impeller/renderer/backend/gles/device_buffer_gles.h b/impeller/renderer/backend/gles/device_buffer_gles.h new file mode 100644 index 0000000000000..e69de29bb2d1d diff --git a/impeller/renderer/backend/gles/formats_gles.cc b/impeller/renderer/backend/gles/formats_gles.cc new file mode 100644 index 0000000000000..7d5f3b832d946 --- /dev/null +++ b/impeller/renderer/backend/gles/formats_gles.cc @@ -0,0 +1,11 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "impeller/renderer/backend/gles/formats_gles.h" + +namespace impeller { + +// + +} // namespace impeller diff --git a/impeller/renderer/backend/gles/formats_gles.h b/impeller/renderer/backend/gles/formats_gles.h new file mode 100644 index 0000000000000..e69de29bb2d1d diff --git a/impeller/renderer/backend/gles/pipeline_gles.cc b/impeller/renderer/backend/gles/pipeline_gles.cc new file mode 100644 index 0000000000000..3148ec1b176a9 --- /dev/null +++ b/impeller/renderer/backend/gles/pipeline_gles.cc @@ -0,0 +1,11 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "impeller/renderer/backend/gles/pipeline_gles.h" + +namespace impeller { + +// + +} // namespace impeller diff --git a/impeller/renderer/backend/gles/pipeline_gles.h b/impeller/renderer/backend/gles/pipeline_gles.h new file mode 100644 index 0000000000000..e69de29bb2d1d diff --git a/impeller/renderer/backend/gles/pipeline_library_gles.cc b/impeller/renderer/backend/gles/pipeline_library_gles.cc new file mode 100644 index 0000000000000..270a77f16bfff --- /dev/null +++ b/impeller/renderer/backend/gles/pipeline_library_gles.cc @@ -0,0 +1,11 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "impeller/renderer/backend/gles/pipeline_library_gles.h" + +namespace impeller { + +// + +} // namespace impeller diff --git a/impeller/renderer/backend/gles/pipeline_library_gles.h b/impeller/renderer/backend/gles/pipeline_library_gles.h new file mode 100644 index 0000000000000..a608e19eb85a0 --- /dev/null +++ b/impeller/renderer/backend/gles/pipeline_library_gles.h @@ -0,0 +1,30 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#pragma once + +#include "flutter/fml/macros.h" +#include "impeller/renderer/pipeline_library.h" + +namespace impeller { + +class ContextGLES; + +class PipelineLibraryGLES final : public PipelineLibrary { + public: + // |PipelineLibrary| + ~PipelineLibraryGLES() override; + + private: + friend ContextGLES; + + PipelineLibraryGLES(); + + // |PipelineLibrary| + PipelineFuture GetRenderPipeline(PipelineDescriptor descriptor) override; + + FML_DISALLOW_COPY_AND_ASSIGN(PipelineLibraryGLES); +}; + +} // namespace impeller diff --git a/impeller/renderer/backend/gles/proc_table_gles.cc b/impeller/renderer/backend/gles/proc_table_gles.cc new file mode 100644 index 0000000000000..643bc0ecc2d68 --- /dev/null +++ b/impeller/renderer/backend/gles/proc_table_gles.cc @@ -0,0 +1,17 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "impeller/renderer/backend/gles/proc_table_gles.h" + +namespace impeller { + +ProcTableGLES::ProcTableGLES() : is_valid_(true) {} + +ProcTableGLES::~ProcTableGLES() = default; + +bool ProcTableGLES::IsValid() const { + return is_valid_; +} + +} // namespace impeller diff --git a/impeller/renderer/backend/gles/proc_table_gles.h b/impeller/renderer/backend/gles/proc_table_gles.h new file mode 100644 index 0000000000000..cabb5d10c43ca --- /dev/null +++ b/impeller/renderer/backend/gles/proc_table_gles.h @@ -0,0 +1,25 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#pragma once + +#include "flutter/fml/macros.h" + +namespace impeller { + +class ProcTableGLES { + public: + ProcTableGLES(); + + ~ProcTableGLES(); + + bool IsValid() const; + + private: + bool is_valid_ = false; + + FML_DISALLOW_COPY_AND_ASSIGN(ProcTableGLES); +}; + +} // namespace impeller diff --git a/impeller/renderer/backend/gles/reactor_gles.cc b/impeller/renderer/backend/gles/reactor_gles.cc new file mode 100644 index 0000000000000..e673632dab9ff --- /dev/null +++ b/impeller/renderer/backend/gles/reactor_gles.cc @@ -0,0 +1,28 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "impeller/renderer/backend/gles/reactor_gles.h" + +#include "impeller/base/validation.h" + +namespace impeller { + +ReactorGLES::ReactorGLES() { + proc_table_ = std::make_unique(); + + if (!proc_table_->IsValid()) { + VALIDATION_LOG << "Could not create valid proc table."; + return; + } + + is_valid_ = true; +} + +ReactorGLES::~ReactorGLES() = default; + +bool ReactorGLES::IsValid() const { + return is_valid_; +} + +} // namespace impeller diff --git a/impeller/renderer/backend/gles/reactor_gles.h b/impeller/renderer/backend/gles/reactor_gles.h new file mode 100644 index 0000000000000..f3a0d1bd0161d --- /dev/null +++ b/impeller/renderer/backend/gles/reactor_gles.h @@ -0,0 +1,29 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#pragma once + +#include + +#include "flutter/fml/macros.h" +#include "impeller/renderer/backend/gles/proc_table_gles.h" + +namespace impeller { + +class ReactorGLES { + public: + ReactorGLES(); + + ~ReactorGLES(); + + bool IsValid() const; + + private: + std::unique_ptr proc_table_; + bool is_valid_ = false; + + FML_DISALLOW_COPY_AND_ASSIGN(ReactorGLES); +}; + +} // namespace impeller diff --git a/impeller/renderer/backend/gles/render_pass_gles.cc b/impeller/renderer/backend/gles/render_pass_gles.cc new file mode 100644 index 0000000000000..06771a3128558 --- /dev/null +++ b/impeller/renderer/backend/gles/render_pass_gles.cc @@ -0,0 +1,11 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "impeller/renderer/backend/gles/render_pass_gles.h" + +namespace impeller { + +// + +} // namespace impeller diff --git a/impeller/renderer/backend/gles/render_pass_gles.h b/impeller/renderer/backend/gles/render_pass_gles.h new file mode 100644 index 0000000000000..e69de29bb2d1d diff --git a/impeller/renderer/backend/gles/sampler_gles.cc b/impeller/renderer/backend/gles/sampler_gles.cc new file mode 100644 index 0000000000000..cd8c1ba103add --- /dev/null +++ b/impeller/renderer/backend/gles/sampler_gles.cc @@ -0,0 +1,11 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "impeller/renderer/backend/gles/sampler_gles.h" + +namespace impeller { + +// + +} // namespace impeller diff --git a/impeller/renderer/backend/gles/sampler_gles.h b/impeller/renderer/backend/gles/sampler_gles.h new file mode 100644 index 0000000000000..e69de29bb2d1d diff --git a/impeller/renderer/backend/gles/sampler_library_gles.cc b/impeller/renderer/backend/gles/sampler_library_gles.cc new file mode 100644 index 0000000000000..bff27cd6e133c --- /dev/null +++ b/impeller/renderer/backend/gles/sampler_library_gles.cc @@ -0,0 +1,11 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "impeller/renderer/backend/gles/sampler_library_gles.h" + +namespace impeller { + +// + +} // namespace impeller diff --git a/impeller/renderer/backend/gles/sampler_library_gles.h b/impeller/renderer/backend/gles/sampler_library_gles.h new file mode 100644 index 0000000000000..e69de29bb2d1d diff --git a/impeller/renderer/backend/gles/shader_function_gles.cc b/impeller/renderer/backend/gles/shader_function_gles.cc new file mode 100644 index 0000000000000..db71e891ef58d --- /dev/null +++ b/impeller/renderer/backend/gles/shader_function_gles.cc @@ -0,0 +1,11 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "impeller/renderer/backend/gles/shader_function_gles.h" + +namespace impeller { + +// + +} // namespace impeller diff --git a/impeller/renderer/backend/gles/shader_function_gles.h b/impeller/renderer/backend/gles/shader_function_gles.h new file mode 100644 index 0000000000000..e69de29bb2d1d diff --git a/impeller/renderer/backend/gles/shader_library_gles.cc b/impeller/renderer/backend/gles/shader_library_gles.cc new file mode 100644 index 0000000000000..7074095de2220 --- /dev/null +++ b/impeller/renderer/backend/gles/shader_library_gles.cc @@ -0,0 +1,11 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "impeller/renderer/backend/gles/shader_library_gles.h" + +namespace impeller { + +// + +} // namespace impeller diff --git a/impeller/renderer/backend/gles/shader_library_gles.h b/impeller/renderer/backend/gles/shader_library_gles.h new file mode 100644 index 0000000000000..fcc3e546c3c26 --- /dev/null +++ b/impeller/renderer/backend/gles/shader_library_gles.h @@ -0,0 +1,33 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#pragma once + +#include "flutter/fml/macros.h" +#include "impeller/renderer/shader_library.h" + +namespace impeller { + +class ShaderLibraryGLES final : public ShaderLibrary { + public: + ShaderLibraryGLES(); + + // |ShaderLibrary| + ~ShaderLibraryGLES() override; + + // |ShaderLibrary| + bool IsValid() const override; + + private: + friend class ContextGLES; + + // |ShaderLibrary| + std::shared_ptr GetFunction( + const std::string_view& name, + ShaderStage stage) override; + + FML_DISALLOW_COPY_AND_ASSIGN(ShaderLibraryGLES); +}; + +} // namespace impeller diff --git a/impeller/renderer/backend/gles/surface_gles.cc b/impeller/renderer/backend/gles/surface_gles.cc new file mode 100644 index 0000000000000..b80a2519cb757 --- /dev/null +++ b/impeller/renderer/backend/gles/surface_gles.cc @@ -0,0 +1,11 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "impeller/renderer/backend/gles/surface_gles.h" + +namespace impeller { + +// + +} // namespace impeller diff --git a/impeller/renderer/backend/gles/surface_gles.h b/impeller/renderer/backend/gles/surface_gles.h new file mode 100644 index 0000000000000..e69de29bb2d1d diff --git a/impeller/renderer/backend/gles/texture_gles.cc b/impeller/renderer/backend/gles/texture_gles.cc new file mode 100644 index 0000000000000..0ebe05e47658d --- /dev/null +++ b/impeller/renderer/backend/gles/texture_gles.cc @@ -0,0 +1,11 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "impeller/renderer/backend/gles/texture_gles.h" + +namespace impeller { + +// + +} // namespace impeller diff --git a/impeller/renderer/backend/gles/texture_gles.h b/impeller/renderer/backend/gles/texture_gles.h new file mode 100644 index 0000000000000..e69de29bb2d1d diff --git a/impeller/renderer/backend/gles/vertex_descriptor_gles.cc b/impeller/renderer/backend/gles/vertex_descriptor_gles.cc new file mode 100644 index 0000000000000..677e729f2cc5f --- /dev/null +++ b/impeller/renderer/backend/gles/vertex_descriptor_gles.cc @@ -0,0 +1,11 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "impeller/renderer/backend/gles/vertex_descriptor_gles.h" + +namespace impeller { + +// + +} // namespace impeller diff --git a/impeller/renderer/backend/gles/vertex_descriptor_gles.h b/impeller/renderer/backend/gles/vertex_descriptor_gles.h new file mode 100644 index 0000000000000..e69de29bb2d1d diff --git a/impeller/renderer/renderer.cc b/impeller/renderer/renderer.cc index fab86fcd8d9f6..849e4fc0fa3ba 100644 --- a/impeller/renderer/renderer.cc +++ b/impeller/renderer/renderer.cc @@ -19,7 +19,7 @@ Renderer::Renderer(std::shared_ptr context, : frames_in_flight_sema_(std::make_shared( std::max(1u, max_frames_in_flight))), context_(std::move(context)) { - if (!context_->IsValid()) { + if (!context_ || !context_->IsValid()) { return; } diff --git a/impeller/renderer/renderer_unittests.cc b/impeller/renderer/renderer_unittests.cc index 40fa47ecadf0d..0bd6640223221 100644 --- a/impeller/renderer/renderer_unittests.cc +++ b/impeller/renderer/renderer_unittests.cc @@ -4,12 +4,12 @@ #include "flutter/fml/time/time_point.h" #include "flutter/testing/testing.h" -#include "impeller/fixtures/box_fade.frag.h" -#include "impeller/fixtures/box_fade.vert.h" -#include "impeller/fixtures/instanced_draw.frag.h" -#include "impeller/fixtures/instanced_draw.vert.h" -#include "impeller/fixtures/test_texture.frag.h" -#include "impeller/fixtures/test_texture.vert.h" +#include "impeller/fixtures/mtl/box_fade.frag.h" +#include "impeller/fixtures/mtl/box_fade.vert.h" +#include "impeller/fixtures/mtl/instanced_draw.frag.h" +#include "impeller/fixtures/mtl/instanced_draw.vert.h" +#include "impeller/fixtures/mtl/test_texture.frag.h" +#include "impeller/fixtures/mtl/test_texture.vert.h" #include "impeller/geometry/path_builder.h" #include "impeller/image/compressed_image.h" #include "impeller/image/decompressed_image.h" @@ -30,8 +30,9 @@ namespace impeller { namespace testing { using RendererTest = Playground; +INSTANTIATE_PLAYGROUND_SUITE(RendererTest); -TEST_F(RendererTest, CanCreateBoxPrimitive) { +TEST_P(RendererTest, CanCreateBoxPrimitive) { using VS = BoxFadeVertexShader; using FS = BoxFadeFragmentShader; auto context = GetContext(); @@ -96,7 +97,7 @@ TEST_F(RendererTest, CanCreateBoxPrimitive) { OpenPlaygroundHere(callback); } -TEST_F(RendererTest, CanRenderMultiplePrimitives) { +TEST_P(RendererTest, CanRenderMultiplePrimitives) { using VS = BoxFadeVertexShader; using FS = BoxFadeFragmentShader; auto context = GetContext(); @@ -168,7 +169,7 @@ TEST_F(RendererTest, CanRenderMultiplePrimitives) { OpenPlaygroundHere(callback); } -TEST_F(RendererTest, CanRenderToTexture) { +TEST_P(RendererTest, CanRenderToTexture) { using VS = BoxFadeVertexShader; using FS = BoxFadeFragmentShader; auto context = GetContext(); @@ -272,7 +273,7 @@ TEST_F(RendererTest, CanRenderToTexture) { ASSERT_TRUE(r2t_pass->EncodeCommands(*context->GetTransientsAllocator())); } -TEST_F(RendererTest, CanRenderInstanced) { +TEST_P(RendererTest, CanRenderInstanced) { using VS = InstancedDrawVertexShader; using FS = InstancedDrawFragmentShader; @@ -290,6 +291,7 @@ TEST_F(RendererTest, CanRenderInstanced) { builder.AppendVertex(data); })); + ASSERT_NE(GetContext(), nullptr); auto pipeline = GetContext() ->GetPipelineLibrary() diff --git a/impeller/tools/impeller.gni b/impeller/tools/impeller.gni index e4430563b0fb5..1b60ddcc47b44 100644 --- a/impeller/tools/impeller.gni +++ b/impeller/tools/impeller.gni @@ -12,11 +12,21 @@ declare_args() { # Whether Impeller is supported on the platform. impeller_supports_platform = true - # Whether Impeller supports rendering on the platform. - impeller_supports_rendering = is_mac || is_ios + # Whether the Metal backend is enabled. + impeller_enable_metal = is_mac || is_ios + + # Whether the OpenGLES backend is enabled. + impeller_enable_opengles = is_mac +} +declare_args() { # Whether Impeller shaders are supported on the platform. - impeller_shaders_supports_platform = is_mac || is_ios + impeller_shaders_supports_platform = + impeller_enable_metal || impeller_enable_opengles + + # Whether Impeller supports rendering on the platform. + impeller_supports_rendering = + impeller_enable_metal || impeller_enable_opengles } # ------------------------------------------------------------------------------ @@ -106,7 +116,7 @@ template("metal_library") { script = "//flutter/impeller/tools/build_metal_library.py" - depfile = "$target_gen_dir/shader_deps/$metal_library_name.depfile" + depfile = "$target_gen_dir/mtl/$metal_library_name.depfile" args = [ "--output", @@ -151,17 +161,19 @@ template("impeller_shaders_real") { assert(defined(invoker.name), "Name of the shader library must be specified.") base_target_name = target_name - impellerc_target_name = "impellerc_$target_name" + impellerc_target_name = "impellerc_mtl_$target_name" compiled_action_foreach(impellerc_target_name) { tool = "//flutter/impeller/compiler:impellerc" sources = invoker.shaders - metal_intermediate = "$target_gen_dir/{{source_file_part}}.metal" - spirv_intermediate = "$target_gen_dir/{{source_file_part}}.spirv" - reflection_json_intermediate = "$target_gen_dir/{{source_file_part}}.json" - reflection_header_intermediate = "$target_gen_dir/{{source_file_part}}.h" - reflection_cc_intermediate = "$target_gen_dir/{{source_file_part}}.mm" + metal_intermediate = "$target_gen_dir/mtl/{{source_file_part}}.metal" + spirv_intermediate = "$target_gen_dir/mtl/{{source_file_part}}.spirv" + reflection_json_intermediate = + "$target_gen_dir/mtl/{{source_file_part}}.json" + reflection_header_intermediate = + "$target_gen_dir/mtl/{{source_file_part}}.h" + reflection_cc_intermediate = "$target_gen_dir/mtl/{{source_file_part}}.cc" outputs = [ metal_intermediate, @@ -169,7 +181,7 @@ template("impeller_shaders_real") { reflection_cc_intermediate, ] - depfile_path = "$target_gen_dir/{{source_file_part}}.d" + depfile_path = "$target_gen_dir/mtl/{{source_file_part}}.d" metal_intermediate_path = rebase_path(metal_intermediate, root_build_dir) spirv_intermediate_path = rebase_path(spirv_intermediate, root_build_dir) @@ -223,6 +235,14 @@ template("impeller_shaders_real") { target_gen_dir, impeller_root_gen_dir, ] + + if (impeller_enable_metal) { + include_dirs += [ "$impeller_root_gen_dir/mtl" ] + } + + if (impeller_enable_opengles) { + include_dirs += [ "$impeller_root_gen_dir/gles" ] + } } source_set(shader_glue_target_name) { @@ -248,8 +268,8 @@ template("impeller_shaders_real") { metal_library_files = get_target_outputs(":$metal_library_target_name") metal_library_file = metal_library_files[0] inputs = [ metal_library_file ] - output_header = "$target_gen_dir/$base_target_name.h" - output_source = "$target_gen_dir/$base_target_name.c" + output_header = "$target_gen_dir/mtl/$base_target_name.h" + output_source = "$target_gen_dir/mtl/$base_target_name.c" outputs = [ output_header, output_source, diff --git a/impeller/typographer/typographer_unittests.cc b/impeller/typographer/typographer_unittests.cc index 0cb41841aec25..41cb7c1ca9705 100644 --- a/impeller/typographer/typographer_unittests.cc +++ b/impeller/typographer/typographer_unittests.cc @@ -12,8 +12,9 @@ namespace impeller { namespace testing { using TypographerTest = Playground; +INSTANTIATE_PLAYGROUND_SUITE(TypographerTest); -TEST_F(TypographerTest, CanConvertTextBlob) { +TEST_P(TypographerTest, CanConvertTextBlob) { SkFont font; auto blob = SkTextBlob::MakeFromString( "the quick brown fox jumped over the lazy dog.", font); @@ -26,12 +27,12 @@ TEST_F(TypographerTest, CanConvertTextBlob) { } } -TEST_F(TypographerTest, CanCreateRenderContext) { +TEST_P(TypographerTest, CanCreateRenderContext) { auto context = TextRenderContext::Create(GetContext()); ASSERT_TRUE(context && context->IsValid()); } -TEST_F(TypographerTest, CanCreateGlyphAtlas) { +TEST_P(TypographerTest, CanCreateGlyphAtlas) { auto context = TextRenderContext::Create(GetContext()); ASSERT_TRUE(context && context->IsValid()); SkFont sk_font; From 64630812762b879b7df54a909097dcd43e63eedf Mon Sep 17 00:00:00 2001 From: Brandon DeRosier Date: Tue, 19 Apr 2022 11:33:08 -0700 Subject: [PATCH 411/433] Fix subpass ordering (#143) --- impeller/aiks/aiks_unittests.cc | 26 ++- impeller/entity/entity_pass.cc | 273 ++++++++++++++++++-------------- impeller/entity/entity_pass.h | 15 +- 3 files changed, 188 insertions(+), 126 deletions(-) diff --git a/impeller/aiks/aiks_unittests.cc b/impeller/aiks/aiks_unittests.cc index 016ca50c2f4e9..7c5450096545e 100644 --- a/impeller/aiks/aiks_unittests.cc +++ b/impeller/aiks/aiks_unittests.cc @@ -620,7 +620,31 @@ TEST_P(AiksTest, DrawRectStrokesRenderCorrectly) { canvas.Translate({100, 100}); canvas.DrawPath(PathBuilder{}.AddRect(Rect::MakeSize({100, 100})).TakePath(), - paint); + {paint}); + + ASSERT_TRUE(OpenPlaygroundHere(canvas.EndRecordingAsPicture())); +} + +TEST_P(AiksTest, SaveLayerDrawsBehindSubsequentEntities) { + // Compare with https://fiddle.skia.org/c/9e03de8567ffb49e7e83f53b64bcf636 + Canvas canvas; + Paint paint; + + paint.color = Color::Black(); + Rect rect(25, 25, 25, 25); + canvas.DrawRect(rect, paint); + + canvas.Translate({10, 10}); + canvas.SaveLayer({}); + + paint.color = Color::Green(); + canvas.DrawRect(rect, paint); + + canvas.Restore(); + + canvas.Translate({10, 10}); + paint.color = Color::Red(); + canvas.DrawRect(rect, paint); ASSERT_TRUE(OpenPlaygroundHere(canvas.EndRecordingAsPicture())); } diff --git a/impeller/entity/entity_pass.cc b/impeller/entity/entity_pass.cc index be77768093dc7..c9f4d4f71f041 100644 --- a/impeller/entity/entity_pass.cc +++ b/impeller/entity/entity_pass.cc @@ -3,7 +3,9 @@ // found in the LICENSE file. #include "impeller/entity/entity_pass.h" +#include +#include "flutter/fml/logging.h" #include "flutter/fml/trace_event.h" #include "impeller/base/validation.h" #include "impeller/entity/contents/content_context.h" @@ -26,22 +28,20 @@ void EntityPass::SetDelegate(std::unique_ptr delegate) { } void EntityPass::AddEntity(Entity entity) { - entities_.emplace_back(std::move(entity)); + elements_.emplace_back(std::move(entity)); } -const std::vector& EntityPass::GetEntities() const { - return entities_; -} - -void EntityPass::SetEntities(Entities entities) { - entities_ = std::move(entities); +void EntityPass::SetElements(std::vector elements) { + elements_ = std::move(elements); } size_t EntityPass::GetSubpassesDepth() const { size_t max_subpass_depth = 0u; - for (const auto& subpass : subpasses_) { - max_subpass_depth = - std::max(max_subpass_depth, subpass->GetSubpassesDepth()); + for (const auto& element : elements_) { + if (auto subpass = std::get_if>(&element)) { + max_subpass_depth = + std::max(max_subpass_depth, subpass->get()->GetSubpassesDepth()); + } } return max_subpass_depth + 1u; } @@ -50,10 +50,20 @@ const std::shared_ptr& EntityPass::GetLazyGlyphAtlas() const { return lazy_glyph_atlas_; } -std::optional EntityPass::GetEntitiesCoverage() const { +std::optional EntityPass::GetElementsCoverage() const { std::optional result; - for (const auto& entity : entities_) { - auto coverage = entity.GetCoverage(); + for (const auto& element : elements_) { + std::optional coverage; + + if (auto entity = std::get_if(&element)) { + coverage = entity->GetCoverage(); + } else if (auto subpass = + std::get_if>(&element)) { + coverage = subpass->get()->GetElementsCoverage(); + } else { + FML_UNREACHABLE(); + } + if (!result.has_value() && coverage.has_value()) { result = coverage; continue; @@ -68,7 +78,7 @@ std::optional EntityPass::GetEntitiesCoverage() const { std::optional EntityPass::GetSubpassCoverage( const EntityPass& subpass) const { - auto entities_coverage = subpass.GetEntitiesCoverage(); + auto entities_coverage = subpass.GetElementsCoverage(); // The entities don't cover anything. There is nothing to do. if (!entities_coverage.has_value()) { return std::nullopt; @@ -94,17 +104,15 @@ EntityPass* EntityPass::GetSuperpass() const { return superpass_; } -const EntityPass::Subpasses& EntityPass::GetSubpasses() const { - return subpasses_; -} - EntityPass* EntityPass::AddSubpass(std::unique_ptr pass) { if (!pass) { return nullptr; } FML_DCHECK(pass->superpass_ == nullptr); pass->superpass_ = this; - return subpasses_.emplace_back(std::move(pass)).get(); + auto subpass_pointer = pass.get(); + elements_.emplace_back(std::move(pass)); + return subpass_pointer; } bool EntityPass::Render(ContentContext& renderer, @@ -112,114 +120,133 @@ bool EntityPass::Render(ContentContext& renderer, Point position) const { TRACE_EVENT0("impeller", "EntityPass::Render"); - for (Entity entity : entities_) { - if (!position.IsZero()) { - // If the pass image is going to be rendered with a non-zero position, - // apply the negative translation to entity copies before rendering them - // so that they'll end up rendering to the correct on-screen position. - entity.SetTransformation(Matrix::MakeTranslation(Vector3(-position)) * - entity.GetTransformation()); - } - if (!entity.Render(renderer, parent_pass)) { - return false; - } - } - - for (const auto& subpass : subpasses_) { - if (delegate_->CanElide()) { - continue; - } - - if (delegate_->CanCollapseIntoParentPass()) { - // Directly render into the parent pass and move on. - if (!subpass->Render(renderer, parent_pass, position)) { + for (const auto& element : elements_) { + // ========================================================================= + // Entity rendering ======================================================== + // ========================================================================= + if (const auto& entity = std::get_if(&element)) { + Entity e = *entity; + if (!position.IsZero()) { + // If the pass image is going to be rendered with a non-zero position, + // apply the negative translation to entity copies before rendering them + // so that they'll end up rendering to the correct on-screen position. + e.SetTransformation(Matrix::MakeTranslation(Vector3(-position)) * + e.GetTransformation()); + } + if (!e.Render(renderer, parent_pass)) { return false; } continue; } - const auto subpass_coverage = GetSubpassCoverage(*subpass); + // ========================================================================= + // Subpass rendering ======================================================= + // ========================================================================= + if (const auto& subpass_ptr = + std::get_if>(&element)) { + auto subpass = subpass_ptr->get(); - if (!subpass_coverage.has_value()) { - continue; - } + if (delegate_->CanElide()) { + continue; + } - if (subpass_coverage->size.IsEmpty()) { - // It is not an error to have an empty subpass. But subpasses that can't - // create their intermediates must trip errors. - continue; - } + if (delegate_->CanCollapseIntoParentPass()) { + // Directly render into the parent pass and move on. + if (!subpass->Render(renderer, parent_pass, position)) { + return false; + } + continue; + } - auto context = renderer.GetContext(); + const auto subpass_coverage = GetSubpassCoverage(*subpass); - auto subpass_target = RenderTarget::CreateOffscreen( - *context, ISize::Ceil(subpass_coverage->size)); + if (!subpass_coverage.has_value()) { + continue; + } - auto subpass_texture = subpass_target.GetRenderTargetTexture(); + if (subpass_coverage->size.IsEmpty()) { + // It is not an error to have an empty subpass. But subpasses that can't + // create their intermediates must trip errors. + continue; + } - if (!subpass_texture) { - return false; - } + auto context = renderer.GetContext(); - auto offscreen_texture_contents = - delegate_->CreateContentsForSubpassTarget(subpass_texture); - - if (!offscreen_texture_contents) { - // This is an error because the subpass delegate said the pass couldn't be - // collapsed into its parent. Yet, when asked how it want's to postprocess - // the offscreen texture, it couldn't give us an answer. - // - // Theoretically, we could collapse the pass now. But that would be - // wasteful as we already have the offscreen texture and we don't want to - // discard it without ever using it. Just make the delegate do the right - // thing. - return false; - } + auto subpass_target = RenderTarget::CreateOffscreen( + *context, ISize::Ceil(subpass_coverage->size)); - auto sub_command_buffer = context->CreateRenderCommandBuffer(); + auto subpass_texture = subpass_target.GetRenderTargetTexture(); - sub_command_buffer->SetLabel("Offscreen Command Buffer"); + if (!subpass_texture) { + return false; + } - if (!sub_command_buffer) { - return false; - } + auto offscreen_texture_contents = + delegate_->CreateContentsForSubpassTarget(subpass_texture); + + if (!offscreen_texture_contents) { + // This is an error because the subpass delegate said the pass couldn't + // be collapsed into its parent. Yet, when asked how it want's to + // postprocess the offscreen texture, it couldn't give us an answer. + // + // Theoretically, we could collapse the pass now. But that would be + // wasteful as we already have the offscreen texture and we don't want + // to discard it without ever using it. Just make the delegate do the + // right thing. + return false; + } - auto sub_renderpass = sub_command_buffer->CreateRenderPass(subpass_target); + auto sub_command_buffer = context->CreateRenderCommandBuffer(); - if (!sub_renderpass) { - return false; - } + sub_command_buffer->SetLabel("Offscreen Command Buffer"); + + if (!sub_command_buffer) { + return false; + } - sub_renderpass->SetLabel("OffscreenPass"); + auto sub_renderpass = + sub_command_buffer->CreateRenderPass(subpass_target); - if (!subpass->Render(renderer, *sub_renderpass, subpass_coverage->origin)) { - return false; - } + if (!sub_renderpass) { + return false; + } - if (!sub_renderpass->EncodeCommands(*context->GetTransientsAllocator())) { - return false; - } + sub_renderpass->SetLabel("OffscreenPass"); - if (!sub_command_buffer->SubmitCommands()) { - return false; - } + if (!subpass->Render(renderer, *sub_renderpass, + subpass_coverage->origin)) { + return false; + } + + if (!sub_renderpass->EncodeCommands(*context->GetTransientsAllocator())) { + return false; + } + + if (!sub_command_buffer->SubmitCommands()) { + return false; + } + + Entity entity; + entity.SetPath(PathBuilder{} + .AddRect(Rect::MakeSize(subpass_coverage->size)) + .TakePath()); + entity.SetContents(std::move(offscreen_texture_contents)); + entity.SetStencilDepth(stencil_depth_); + entity.SetBlendMode(subpass->blend_mode_); + // Once we have filters being applied for SaveLayer, some special sauce + // may be needed here (or in PaintPassDelegate) to ensure the filter + // parameters are transformed by the `xformation_` matrix, while + // continuing to apply only the subpass offset to the offscreen texture. + entity.SetTransformation(Matrix::MakeTranslation( + Vector3(subpass_coverage->origin - position))); + if (!entity.Render(renderer, parent_pass)) { + return false; + } - Entity entity; - entity.SetPath(PathBuilder{} - .AddRect(Rect::MakeSize(subpass_coverage->size)) - .TakePath()); - entity.SetContents(std::move(offscreen_texture_contents)); - entity.SetStencilDepth(stencil_depth_); - entity.SetBlendMode(subpass->blend_mode_); - // Once we have filters being applied for SaveLayer, some special sauce - // may be needed here (or in PaintPassDelegate) to ensure the filter - // parameters are transformed by the `xformation_` matrix, while continuing - // to apply only the subpass offset to the offscreen texture. - entity.SetTransformation( - Matrix::MakeTranslation(Vector3(subpass_coverage->origin - position))); - if (!entity.Render(renderer, parent_pass)) { - return false; + continue; } + + FML_UNREACHABLE(); } return true; @@ -230,23 +257,39 @@ void EntityPass::IterateAllEntities(std::function iterator) { return; } - for (auto& entity : entities_) { - if (!iterator(entity)) { - return; + for (auto& element : elements_) { + if (auto entity = std::get_if(&element)) { + if (!iterator(*entity)) { + return; + } + continue; } - } - - for (auto& subpass : subpasses_) { - subpass->IterateAllEntities(iterator); + if (auto subpass = std::get_if>(&element)) { + subpass->get()->IterateAllEntities(iterator); + continue; + } + FML_UNREACHABLE(); } } std::unique_ptr EntityPass::Clone() const { - auto pass = std::make_unique(); - pass->SetEntities(entities_); - for (const auto& subpass : subpasses_) { - pass->AddSubpass(subpass->Clone()); + std::vector new_elements; + new_elements.reserve(elements_.size()); + + for (const auto& element : elements_) { + if (auto entity = std::get_if(&element)) { + new_elements.push_back(*entity); + continue; + } + if (auto subpass = std::get_if>(&element)) { + new_elements.push_back(subpass->get()->Clone()); + continue; + } + FML_UNREACHABLE(); } + + auto pass = std::make_unique(); + pass->SetElements(std::move(new_elements)); return pass; } diff --git a/impeller/entity/entity_pass.h b/impeller/entity/entity_pass.h index c8b8c117bda2a..545eaf536fa4d 100644 --- a/impeller/entity/entity_pass.h +++ b/impeller/entity/entity_pass.h @@ -21,8 +21,7 @@ class ContentContext; class EntityPass { public: - using Entities = std::vector; - using Subpasses = std::vector>; + using Element = std::variant>; EntityPass(); @@ -36,11 +35,7 @@ class EntityPass { void AddEntity(Entity entity); - void SetEntities(Entities entities); - - const std::vector& GetEntities() const; - - const Subpasses& GetSubpasses() const; + void SetElements(std::vector elements); const std::shared_ptr& GetLazyGlyphAtlas() const; @@ -61,8 +56,8 @@ class EntityPass { void SetBlendMode(Entity::BlendMode blend_mode); private: - Entities entities_; - Subpasses subpasses_; + std::vector elements_; + EntityPass* superpass_ = nullptr; Matrix xformation_; size_t stencil_depth_ = 0u; @@ -74,7 +69,7 @@ class EntityPass { std::optional GetSubpassCoverage(const EntityPass& subpass) const; - std::optional GetEntitiesCoverage() const; + std::optional GetElementsCoverage() const; FML_DISALLOW_COPY_AND_ASSIGN(EntityPass); }; From 862f6d01eaf88d3062a2ef78b61d4578d03c8afe Mon Sep 17 00:00:00 2001 From: Dan Field Date: Tue, 19 Apr 2022 21:04:03 -0700 Subject: [PATCH 412/433] Windows (#144) Speculative fixes for Windows build --- impeller/base/comparable.cc | 2 ++ impeller/base/thread.h | 1 + impeller/compiler/impellerc_main.cc | 6 +++--- impeller/compiler/switches.cc | 8 ++++---- impeller/compiler/switches.h | 1 + impeller/compiler/types.cc | 11 ++++++++++- impeller/compiler/types.h | 6 ++++++ impeller/compiler/utilities.cc | 1 + impeller/geometry/rect.h | 2 +- impeller/geometry/size.h | 1 + 10 files changed, 30 insertions(+), 9 deletions(-) diff --git a/impeller/base/comparable.cc b/impeller/base/comparable.cc index 96463d6e719e1..58585c393fbd7 100644 --- a/impeller/base/comparable.cc +++ b/impeller/base/comparable.cc @@ -4,6 +4,8 @@ #include "impeller/base/comparable.h" +#include + namespace impeller { static std::atomic_size_t sLastID; diff --git a/impeller/base/thread.h b/impeller/base/thread.h index da200bc8d9266..9d44b7681cc93 100644 --- a/impeller/base/thread.h +++ b/impeller/base/thread.h @@ -5,6 +5,7 @@ #pragma once #include +#include #include #include "flutter/fml/macros.h" diff --git a/impeller/compiler/impellerc_main.cc b/impeller/compiler/impellerc_main.cc index f61b112604125..ca19e15536990 100644 --- a/impeller/compiler/impellerc_main.cc +++ b/impeller/compiler/impellerc_main.cc @@ -52,9 +52,9 @@ bool Main(const fml::CommandLine& command_line) { reflector_options.shader_name = InferShaderNameFromPath(switches.source_file_name); reflector_options.header_file_name = - std::filesystem::path{switches.reflection_header_name} - .filename() - .native(); + ToUtf8(std::filesystem::path{switches.reflection_header_name} + .filename() + .native()); Compiler compiler(*source_file_mapping, options, reflector_options); if (!compiler.IsValid()) { diff --git a/impeller/compiler/switches.cc b/impeller/compiler/switches.cc index f3caea51d2f59..b29dcac31a4cd 100644 --- a/impeller/compiler/switches.cc +++ b/impeller/compiler/switches.cc @@ -62,10 +62,10 @@ static TargetPlatform TargetPlatformFromCommandLine( Switches::Switches(const fml::CommandLine& command_line) : target_platform(TargetPlatformFromCommandLine(command_line)), - working_directory(std::make_shared( - fml::OpenDirectory(std::filesystem::current_path().native().c_str(), - false, // create if necessary, - fml::FilePermission::kRead))), + working_directory(std::make_shared(fml::OpenDirectory( + ToUtf8(std::filesystem::current_path().native()).c_str(), + false, // create if necessary, + fml::FilePermission::kRead))), source_file_name(command_line.GetOptionValueWithDefault("input", "")), sl_file_name(command_line.GetOptionValueWithDefault("sl", "")), spirv_file_name(command_line.GetOptionValueWithDefault("spirv", "")), diff --git a/impeller/compiler/switches.h b/impeller/compiler/switches.h index 173ac20838a73..5e1979d026024 100644 --- a/impeller/compiler/switches.h +++ b/impeller/compiler/switches.h @@ -12,6 +12,7 @@ #include "flutter/fml/unique_fd.h" #include "impeller/compiler/compiler.h" #include "impeller/compiler/include_dir.h" +#include "impeller/compiler/types.h" namespace impeller { namespace compiler { diff --git a/impeller/compiler/types.cc b/impeller/compiler/types.cc index adbf277dfae9a..f8a03459ecd00 100644 --- a/impeller/compiler/types.cc +++ b/impeller/compiler/types.cc @@ -60,7 +60,7 @@ static std::string UniqueEntryPointFunctionNameFromSourceName( SourceType type) { std::stringstream stream; std::filesystem::path file_path(file_name); - stream << file_path.stem().native() << "_"; + stream << ToUtf8(file_path.stem().native()) << "_"; switch (type) { case SourceType::kUnknown: stream << "unknown"; @@ -212,5 +212,14 @@ std::string TargetPlatformSLExtension(TargetPlatform platform) { FML_UNREACHABLE(); } +std::string ToUtf8(const std::wstring& wstring) { + std::wstring_convert> myconv; + return myconv.to_bytes(wstring); +} + +std::string ToUtf8(const std::string& string) { + return string; +} + } // namespace compiler } // namespace impeller diff --git a/impeller/compiler/types.h b/impeller/compiler/types.h index b760122b38f9a..5ad762e0a31c7 100644 --- a/impeller/compiler/types.h +++ b/impeller/compiler/types.h @@ -4,6 +4,8 @@ #pragma once +#include +#include #include #include "flutter/fml/macros.h" @@ -54,5 +56,9 @@ spv::ExecutionModel ToExecutionModel(SourceType type); spirv_cross::CompilerMSL::Options::Platform TargetPlatformToMSLPlatform( TargetPlatform platform); +std::string ToUtf8(const std::wstring& wstring); + +std::string ToUtf8(const std::string& string); + } // namespace compiler } // namespace impeller diff --git a/impeller/compiler/utilities.cc b/impeller/compiler/utilities.cc index 78fa5df5e4703..a191eb9a7520e 100644 --- a/impeller/compiler/utilities.cc +++ b/impeller/compiler/utilities.cc @@ -6,6 +6,7 @@ #include #include +#include namespace impeller { namespace compiler { diff --git a/impeller/geometry/rect.h b/impeller/geometry/rect.h index bd08001b11a0e..1a5675a790ef0 100644 --- a/impeller/geometry/rect.h +++ b/impeller/geometry/rect.h @@ -147,7 +147,7 @@ struct TRect { constexpr std::array, 4> GetTransformedPoints( const Matrix& transform) const { auto points = GetPoints(); - for (uint i = 0; i < points.size(); i++) { + for (int i = 0; i < points.size(); i++) { points[i] = transform * points[i]; } return points; diff --git a/impeller/geometry/size.h b/impeller/geometry/size.h index b3dee0cec7140..6cb55cab47472 100644 --- a/impeller/geometry/size.h +++ b/impeller/geometry/size.h @@ -4,6 +4,7 @@ #pragma once +#include #include #include #include From 191d3bf52c83045c7a1ce3855b9b95e2abff1911 Mon Sep 17 00:00:00 2001 From: Chinmay Garde Date: Wed, 20 Apr 2022 11:22:40 -0700 Subject: [PATCH 413/433] Compile and package all shaders for the OpenGL ES backend. (#146) * Dries up GN rules for Metal and OpenGL ES shader compilation and embedding in a target binary. * Adds support for shader compile time macro definitions. This is so that workarounds for specific shader backends can be implemented. In the case of this patch, there are temporary OpenGLES workaround for users of instancing and SSBOs. These will be removed when I rework glyph rendering to not use these features that are missing in legacy targets. * Since there is no concept of an OpenGLES shader library akin to a `.metallib`, adds a target called `blobcat` that concatenates shader blobs into single blob that can be embedded into a target binary. No parsing or data copying is necessary. * `imgui_raster.vert` has been rewritten to work around the absence of unsigned integer types in legacy backends. --- impeller/BUILD.gn | 1 + impeller/blobcat/BUILD.gn | 49 +++ impeller/blobcat/blob.cc | 11 + impeller/blobcat/blob.h | 43 +++ impeller/blobcat/blob_library.cc | 94 +++++ impeller/blobcat/blob_library.h | 63 ++++ impeller/blobcat/blob_writer.cc | 144 ++++++++ impeller/blobcat/blob_writer.h | 37 ++ impeller/blobcat/blobcat_main.cc | 52 +++ impeller/blobcat/blobcat_unittests.cc | 57 +++ impeller/compiler/compiler.cc | 7 + impeller/compiler/impellerc_main.cc | 1 + impeller/compiler/source_options.h | 1 + impeller/compiler/switches.cc | 5 + impeller/compiler/switches.h | 1 + impeller/entity/shaders/glyph_atlas.vert | 11 + impeller/fixtures/BUILD.gn | 2 +- impeller/fixtures/instanced_draw.vert | 11 + .../backend/metal/playground_impl_mtl.mm | 6 +- impeller/playground/imgui/BUILD.gn | 2 +- impeller/playground/imgui/imgui_raster.vert | 14 +- impeller/tools/impeller.gni | 332 ++++++++++++------ 22 files changed, 828 insertions(+), 116 deletions(-) create mode 100644 impeller/blobcat/BUILD.gn create mode 100644 impeller/blobcat/blob.cc create mode 100644 impeller/blobcat/blob.h create mode 100644 impeller/blobcat/blob_library.cc create mode 100644 impeller/blobcat/blob_library.h create mode 100644 impeller/blobcat/blob_writer.cc create mode 100644 impeller/blobcat/blob_writer.h create mode 100644 impeller/blobcat/blobcat_main.cc create mode 100644 impeller/blobcat/blobcat_unittests.cc diff --git a/impeller/BUILD.gn b/impeller/BUILD.gn index b206c99e9f584..76e0d6c249875 100644 --- a/impeller/BUILD.gn +++ b/impeller/BUILD.gn @@ -48,6 +48,7 @@ executable("impeller_unittests") { deps = [ "archivist:archivist_unittests", "base:base_unittests", + "blobcat:blobcat_unittests", "compiler:compiler_unittests", "fixtures", "geometry:geometry_unittests", diff --git a/impeller/blobcat/BUILD.gn b/impeller/blobcat/BUILD.gn new file mode 100644 index 0000000000000..4899e5f9708d2 --- /dev/null +++ b/impeller/blobcat/BUILD.gn @@ -0,0 +1,49 @@ +# Copyright 2013 The Flutter Authors. All rights reserved. +# Use of this source code is governed by a BSD-style license that can be +# found in the LICENSE file. + +import("../tools/impeller.gni") + +impeller_component("blobcat_lib") { + sources = [ + "blob.cc", + "blob.h", + "blob_library.cc", + "blob_library.h", + "blob_writer.cc", + "blob_writer.h", + ] + + deps = [ + "../base", + "//flutter/fml", + ] +} + +impeller_component("blobcat") { + target_type = "executable" + + sources = [ "blobcat_main.cc" ] + + deps = [ + ":blobcat_lib", + "../base", + "//flutter/fml", + + # FML depends on the Dart VM for tracing and getting the current + # timepoint. + "//flutter/runtime:libdart", + ] +} + +impeller_component("blobcat_unittests") { + testonly = true + + sources = [ "blobcat_unittests.cc" ] + + deps = [ + ":blobcat_lib", + "//flutter/fml", + "//flutter/testing", + ] +} diff --git a/impeller/blobcat/blob.cc b/impeller/blobcat/blob.cc new file mode 100644 index 0000000000000..e7f97177af870 --- /dev/null +++ b/impeller/blobcat/blob.cc @@ -0,0 +1,11 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "impeller/blobcat/blob.h" + +namespace impeller { + +// + +} // namespace impeller diff --git a/impeller/blobcat/blob.h b/impeller/blobcat/blob.h new file mode 100644 index 0000000000000..70e864191aff9 --- /dev/null +++ b/impeller/blobcat/blob.h @@ -0,0 +1,43 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#pragma once + +#include +#include +#include +#include + +#include "flutter/fml/macros.h" +#include "flutter/fml/mapping.h" + +namespace impeller { + +constexpr const uint32_t kBlobCatMagic = 0x0B10BCA7; +struct BlobHeader { + uint32_t magic = kBlobCatMagic; + uint32_t blob_count = 0u; +}; + +struct Blob { + enum class ShaderType : uint8_t { + kVertex, + kFragment, + }; + + static constexpr size_t kMaxNameLength = 24u; + + ShaderType type = ShaderType::kVertex; + uint64_t offset = 0; + uint64_t length = 0; + uint8_t name[kMaxNameLength] = {}; +}; + +struct BlobDescription { + Blob::ShaderType type; + std::string name; + std::shared_ptr mapping; +}; + +} // namespace impeller diff --git a/impeller/blobcat/blob_library.cc b/impeller/blobcat/blob_library.cc new file mode 100644 index 0000000000000..809f40ec344b3 --- /dev/null +++ b/impeller/blobcat/blob_library.cc @@ -0,0 +1,94 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "impeller/blobcat/blob_library.h" + +#include + +namespace impeller { + +BlobLibrary::BlobLibrary(std::shared_ptr mapping) + : mapping_(std::move(mapping)) { + if (!mapping_ || mapping_->GetMapping() == nullptr) { + FML_LOG(ERROR) << "Invalid mapping."; + return; + } + + BlobHeader header; + std::vector blobs; + + size_t offset = 0u; + + // Read the header. + { + const size_t read_size = sizeof(BlobHeader); + if (mapping_->GetSize() < offset + read_size) { + return; + } + std::memcpy(&header, mapping_->GetMapping() + offset, read_size); + offset += read_size; + + // Validate the header. + if (header.magic != kBlobCatMagic) { + FML_LOG(ERROR) << "Invalid blob magic."; + return; + } + + blobs.resize(header.blob_count); + } + + // Read the blob descriptions. + { + const size_t read_size = sizeof(Blob) * header.blob_count; + ::memcpy(blobs.data(), mapping_->GetMapping() + offset, read_size); + offset += read_size; + } + + // Read the blobs. + { + for (size_t i = 0; i < header.blob_count; i++) { + const auto& blob = blobs[i]; + + BlobKey key; + key.type = blob.type; + key.name = std::string{reinterpret_cast(blob.name)}; + auto mapping = std::make_shared( + mapping_->GetMapping() + blob.offset, // offset + blob.length, // length + [mapping = mapping_](const uint8_t* data, size_t size) {} + // release proc + ); + + auto inserted = blobs_.insert({key, mapping}); + if (!inserted.second) { + FML_LOG(ERROR) << "Shader library had duplicate shader named " + << key.name; + return; + } + } + } + + is_valid_ = true; +} + +BlobLibrary::~BlobLibrary() = default; + +bool BlobLibrary::IsValid() const { + return is_valid_; +} + +size_t BlobLibrary::GetShaderCount() const { + return blobs_.size(); +} + +std::shared_ptr BlobLibrary::GetMapping(Blob::ShaderType type, + std::string name) const { + BlobKey key; + key.type = type; + key.name = name; + auto found = blobs_.find(key); + return found == blobs_.end() ? nullptr : found->second; +} + +} // namespace impeller diff --git a/impeller/blobcat/blob_library.h b/impeller/blobcat/blob_library.h new file mode 100644 index 0000000000000..200d775dc8424 --- /dev/null +++ b/impeller/blobcat/blob_library.h @@ -0,0 +1,63 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#pragma once + +#include +#include +#include + +#include "flutter/fml/hash_combine.h" +#include "flutter/fml/macros.h" +#include "flutter/fml/mapping.h" +#include "impeller/blobcat/blob.h" + +namespace impeller { + +class BlobLibrary { + public: + BlobLibrary(std::shared_ptr mapping); + + ~BlobLibrary(); + + bool IsValid() const; + + size_t GetShaderCount() const; + + std::shared_ptr GetMapping(Blob::ShaderType type, + std::string name) const; + + private: + struct BlobKey { + Blob::ShaderType type = Blob::ShaderType::kFragment; + std::string name; + + struct Hash { + size_t operator()(const BlobKey& key) const { + return fml::HashCombine( + static_cast>(key.type), + key.name); + } + }; + + struct Equal { + bool operator()(const BlobKey& lhs, const BlobKey& rhs) const { + return lhs.type == rhs.type && lhs.name == rhs.name; + } + }; + }; + + using Blobs = std::unordered_map, + BlobKey::Hash, + BlobKey::Equal>; + + std::shared_ptr mapping_; + Blobs blobs_; + bool is_valid_ = false; + + FML_DISALLOW_COPY_AND_ASSIGN(BlobLibrary); +}; + +} // namespace impeller diff --git a/impeller/blobcat/blob_writer.cc b/impeller/blobcat/blob_writer.cc new file mode 100644 index 0000000000000..82a62edfe0485 --- /dev/null +++ b/impeller/blobcat/blob_writer.cc @@ -0,0 +1,144 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "impeller/blobcat/blob_writer.h" + +#include +#include + +namespace impeller { + +BlobWriter::BlobWriter() = default; + +BlobWriter::~BlobWriter() = default; + +std::optional InferShaderTypefromFileExtension( + const std::filesystem::path& path) { + if (path == ".vert") { + return Blob::ShaderType::kVertex; + } else if (path == ".frag") { + return Blob::ShaderType::kFragment; + } + return std::nullopt; +} + +bool BlobWriter::AddBlobAtPath(const std::string& std_path) { + std::filesystem::path path(std_path); + + if (path.stem().empty()) { + FML_LOG(ERROR) << "File path stem was empty for " << path; + return false; + } + + if (path.extension() != ".gles") { + FML_LOG(ERROR) << "File path doesn't have a known shader extension " + << path; + return false; + } + + // Get rid of .gles + path = path.replace_extension(); + + auto shader_type = InferShaderTypefromFileExtension(path.extension()); + + if (!shader_type.has_value()) { + FML_LOG(ERROR) << "Could not infer shader type from file extension."; + return false; + } + + // Get rid of the shader type extension (.vert, .frag, etc..). + path = path.replace_extension(); + + const auto shader_name = path.stem().string(); + if (shader_name.empty()) { + FML_LOG(ERROR) << "Shader name was empty."; + return false; + } + + auto file_mapping = fml::FileMapping::CreateReadOnly(std_path); + if (!file_mapping) { + FML_LOG(ERROR) << "File doesn't exist at path: " << path; + return false; + } + + return AddBlob(shader_type.value(), std::move(shader_name), + std::move(file_mapping)); +} + +bool BlobWriter::AddBlob(Blob::ShaderType type, + std::string name, + std::shared_ptr mapping) { + if (name.empty() || !mapping || mapping->GetMapping() == nullptr) { + return false; + } + + if (name.length() >= Blob::kMaxNameLength) { + FML_LOG(ERROR) << "Blob name length was too long."; + return false; + } + + blob_descriptions_.emplace_back( + BlobDescription{type, std::move(name), std::move(mapping)}); + return true; +} + +std::shared_ptr BlobWriter::CreateMapping() const { + BlobHeader header; + header.blob_count = blob_descriptions_.size(); + + uint64_t offset = sizeof(BlobHeader) + (sizeof(Blob) * header.blob_count); + + std::vector blobs; + { + blobs.resize(header.blob_count); + for (size_t i = 0; i < header.blob_count; i++) { + const auto& desc = blob_descriptions_[i]; + blobs[i].type = desc.type; + blobs[i].offset = offset; + blobs[i].length = desc.mapping->GetSize(); + std::memcpy(reinterpret_cast(blobs[i].name), desc.name.data(), + desc.name.size()); + offset += blobs[i].length; + } + } + + { + auto buffer = std::make_shared>(); + buffer->resize(offset, 0); + + size_t write_offset = 0u; + + // Write the header. + { + const size_t write_length = sizeof(header); + std::memcpy(buffer->data() + write_offset, &header, write_length); + write_offset += write_length; + } + + // Write the blob descriptions. + { + const size_t write_length = blobs.size() * sizeof(Blob); + std::memcpy(buffer->data() + write_offset, blobs.data(), write_length); + write_offset += write_length; + } + + // Write the blobs themselves. + { + for (size_t i = 0; i < header.blob_count; i++) { + const auto& desc = blob_descriptions_[i]; + const size_t write_length = desc.mapping->GetSize(); + std::memcpy(buffer->data() + write_offset, desc.mapping->GetMapping(), + write_length); + write_offset += write_length; + } + } + FML_CHECK(write_offset == offset); + return std::make_shared( + buffer->data(), buffer->size(), + [buffer](const uint8_t* data, size_t size) {}); + } + return nullptr; +} + +} // namespace impeller diff --git a/impeller/blobcat/blob_writer.h b/impeller/blobcat/blob_writer.h new file mode 100644 index 0000000000000..bbf60213ac73a --- /dev/null +++ b/impeller/blobcat/blob_writer.h @@ -0,0 +1,37 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#pragma once + +#include +#include +#include + +#include "flutter/fml/macros.h" +#include "flutter/fml/mapping.h" +#include "impeller/blobcat/blob.h" + +namespace impeller { + +class BlobWriter { + public: + BlobWriter(); + + ~BlobWriter(); + + [[nodiscard]] bool AddBlobAtPath(const std::string& path); + + [[nodiscard]] bool AddBlob(Blob::ShaderType type, + std::string name, + std::shared_ptr mapping); + + std::shared_ptr CreateMapping() const; + + private: + std::vector blob_descriptions_; + + FML_DISALLOW_COPY_AND_ASSIGN(BlobWriter); +}; + +} // namespace impeller diff --git a/impeller/blobcat/blobcat_main.cc b/impeller/blobcat/blobcat_main.cc new file mode 100644 index 0000000000000..65480a6fe258d --- /dev/null +++ b/impeller/blobcat/blobcat_main.cc @@ -0,0 +1,52 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include +#include + +#include "flutter/fml/command_line.h" +#include "impeller/blobcat/blob_writer.h" + +namespace impeller { + +bool Main(const fml::CommandLine& command_line) { + BlobWriter writer; + + std::string output; + if (!command_line.GetOptionValue("output", &output)) { + std::cerr << "Output path not specified." << std::endl; + return false; + } + + for (const auto& input : command_line.GetOptionValues("input")) { + if (!writer.AddBlobAtPath(std::string{input})) { + std::cerr << "Could not add blob at path: " << input << std::endl; + return false; + } + } + + auto blob = writer.CreateMapping(); + if (!blob) { + std::cerr << "Could not create combined shader blob." << std::endl; + return false; + } + + auto current_directory = + fml::OpenDirectory(std::filesystem::current_path().native().c_str(), + false, fml::FilePermission::kReadWrite); + if (!fml::WriteAtomically(current_directory, output.c_str(), *blob)) { + std::cerr << "Could not write shader blob to path " << output << std::endl; + return false; + } + + return true; +} + +} // namespace impeller + +int main(int argc, char const* argv[]) { + return impeller::Main(fml::CommandLineFromArgcArgv(argc, argv)) + ? EXIT_SUCCESS + : EXIT_FAILURE; +} diff --git a/impeller/blobcat/blobcat_unittests.cc b/impeller/blobcat/blobcat_unittests.cc new file mode 100644 index 0000000000000..bc5bab227d667 --- /dev/null +++ b/impeller/blobcat/blobcat_unittests.cc @@ -0,0 +1,57 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include + +#include "flutter/fml/mapping.h" +#include "flutter/testing/testing.h" +#include "impeller/blobcat/blob_library.h" +#include "impeller/blobcat/blob_writer.h" + +namespace impeller { +namespace testing { + +static std::shared_ptr CreateMappingFromString( + std::string p_string) { + auto string = std::make_shared(std::move(p_string)); + return std::make_shared( + reinterpret_cast(string->data()), string->size(), + [string](auto, auto) {}); +} + +const std::string CreateStringFromMapping(const fml::Mapping& mapping) { + return std::string{reinterpret_cast(mapping.GetMapping()), + mapping.GetSize()}; +} + +TEST(BlobTest, CanReadAndWriteBlobs) { + BlobWriter writer; + ASSERT_TRUE(writer.AddBlob(Blob::ShaderType::kVertex, "Hello", + CreateMappingFromString("World"))); + ASSERT_TRUE(writer.AddBlob(Blob::ShaderType::kFragment, "Foo", + CreateMappingFromString("Bar"))); + ASSERT_TRUE(writer.AddBlob(Blob::ShaderType::kVertex, "Baz", + CreateMappingFromString("Bang"))); + ASSERT_TRUE(writer.AddBlob(Blob::ShaderType::kVertex, "Ping", + CreateMappingFromString("Pong"))); + ASSERT_TRUE(writer.AddBlob(Blob::ShaderType::kFragment, "Pang", + CreateMappingFromString("World"))); + + auto mapping = writer.CreateMapping(); + ASSERT_NE(mapping, nullptr); + + BlobLibrary library(mapping); + ASSERT_TRUE(library.IsValid()); + ASSERT_EQ(library.GetShaderCount(), 5u); + + // Wrong type. + ASSERT_EQ(library.GetMapping(Blob::ShaderType::kFragment, "Hello"), nullptr); + + auto hello_vtx = library.GetMapping(Blob::ShaderType::kVertex, "Hello"); + ASSERT_NE(hello_vtx, nullptr); + ASSERT_EQ(CreateStringFromMapping(*hello_vtx), "World"); +} + +} // namespace testing +} // namespace impeller diff --git a/impeller/compiler/compiler.cc b/impeller/compiler/compiler.cc index 11cc3c73890f7..b7bf327c606ad 100644 --- a/impeller/compiler/compiler.cc +++ b/impeller/compiler/compiler.cc @@ -129,6 +129,13 @@ Compiler::Compiler(const fml::Mapping& source_mapping, return; } + // Implicit definition that indicates that this compilation is for the device + // (instead of the host). + spirv_options.AddMacroDefinition("IMPELLER_DEVICE"); + for (const auto& define : source_options.defines) { + spirv_options.AddMacroDefinition(define); + } + spirv_options.SetAutoBindUniforms(true); spirv_options.SetAutoMapLocations(true); diff --git a/impeller/compiler/impellerc_main.cc b/impeller/compiler/impellerc_main.cc index ca19e15536990..2ce1b4ac6b61e 100644 --- a/impeller/compiler/impellerc_main.cc +++ b/impeller/compiler/impellerc_main.cc @@ -43,6 +43,7 @@ bool Main(const fml::CommandLine& command_line) { options.working_directory = switches.working_directory; options.file_name = switches.source_file_name; options.include_dirs = switches.include_directories; + options.defines = switches.defines; options.entry_point_name = EntryPointFunctionNameFromSourceName( switches.source_file_name, SourceTypeFromFileName(switches.source_file_name), diff --git a/impeller/compiler/source_options.h b/impeller/compiler/source_options.h index e5a3dc54a9713..636c0a5bf9754 100644 --- a/impeller/compiler/source_options.h +++ b/impeller/compiler/source_options.h @@ -23,6 +23,7 @@ struct SourceOptions { std::vector include_dirs; std::string file_name = "main.glsl"; std::string entry_point_name = "main"; + std::vector defines; SourceOptions(); diff --git a/impeller/compiler/switches.cc b/impeller/compiler/switches.cc index b29dcac31a4cd..d7e7d3beebc7f 100644 --- a/impeller/compiler/switches.cc +++ b/impeller/compiler/switches.cc @@ -35,6 +35,7 @@ void Switches::PrintHelp(std::ostream& stream) { << std::endl; stream << "[optional] --reflection-cc=" << std::endl; stream << "[optional,multiple] --include=" << std::endl; + stream << "[optional,multiple] --define=" << std::endl; stream << "[optional] --depfile=" << std::endl; } @@ -96,6 +97,10 @@ Switches::Switches(const fml::CommandLine& command_line) include_directories.emplace_back(std::move(dir_entry)); } + + for (const auto& define : command_line.GetOptionValues("define")) { + defines.emplace_back(define); + } } bool Switches::AreValid(std::ostream& explain) const { diff --git a/impeller/compiler/switches.h b/impeller/compiler/switches.h index 5e1979d026024..38a0aab318727 100644 --- a/impeller/compiler/switches.h +++ b/impeller/compiler/switches.h @@ -28,6 +28,7 @@ struct Switches { std::string reflection_header_name; std::string reflection_cc_name; std::string depfile_path; + std::vector defines; Switches(); diff --git a/impeller/entity/shaders/glyph_atlas.vert b/impeller/entity/shaders/glyph_atlas.vert index 9320a3465d0b4..9c83d8524adc8 100644 --- a/impeller/entity/shaders/glyph_atlas.vert +++ b/impeller/entity/shaders/glyph_atlas.vert @@ -2,6 +2,14 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. +#ifdef IMPELLER_TARGET_OPENGLES + +void main() { + // Unimplemented because the implementation uses instancing and SSBOs. +} + +#else // IMPELLER_TARGET_OPENGLES + uniform FrameInfo { mat4 mvp; vec2 atlas_size; @@ -53,3 +61,6 @@ void main() { v_atlas_size = frame_info.atlas_size; v_text_color = frame_info.text_color; } + +#endif // IMPELLER_TARGET_OPENGLES + diff --git a/impeller/fixtures/BUILD.gn b/impeller/fixtures/BUILD.gn index 64bf53e9e5b54..d113b2ed4ff82 100644 --- a/impeller/fixtures/BUILD.gn +++ b/impeller/fixtures/BUILD.gn @@ -8,7 +8,7 @@ import("//flutter/testing/testing.gni") import("//flutter/impeller/tools/impeller.gni") impeller_shaders("shader_fixtures") { - name = "shader_fixtures" + name = "fixtures" shaders = [ "box_fade.vert", "box_fade.frag", diff --git a/impeller/fixtures/instanced_draw.vert b/impeller/fixtures/instanced_draw.vert index ff35d518e7221..90890b198e575 100644 --- a/impeller/fixtures/instanced_draw.vert +++ b/impeller/fixtures/instanced_draw.vert @@ -2,6 +2,15 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. + +#ifdef IMPELLER_TARGET_OPENGLES + +void main() { + // Instancing is not supported on legacy targets and test will be disabled. +} + +#else // IMPELLER_TARGET_OPENGLES + uniform FrameInfo { mat4 mvp; } frame_info; @@ -22,3 +31,5 @@ void main () { 1.0); v_color = instance_info.colors[gl_InstanceIndex]; } + +#endif // IMPELLER_TARGET_OPENGLES diff --git a/impeller/playground/backend/metal/playground_impl_mtl.mm b/impeller/playground/backend/metal/playground_impl_mtl.mm index f791df7f16c95..9d81f562134cb 100644 --- a/impeller/playground/backend/metal/playground_impl_mtl.mm +++ b/impeller/playground/backend/metal/playground_impl_mtl.mm @@ -15,7 +15,7 @@ #include "flutter/fml/mapping.h" #include "impeller/entity/mtl/entity_shaders.h" -#include "impeller/fixtures/mtl/shader_fixtures.h" +#include "impeller/fixtures/mtl/fixtures_shaders.h" #include "impeller/playground/imgui/mtl/imgui_shaders.h" #include "impeller/renderer/backend/metal/context_mtl.h" #include "impeller/renderer/backend/metal/formats_mtl.h" @@ -33,8 +33,8 @@ return { std::make_shared(impeller_entity_shaders_data, impeller_entity_shaders_length), - std::make_shared(impeller_shader_fixtures_data, - impeller_shader_fixtures_length), + std::make_shared(impeller_fixtures_shaders_data, + impeller_fixtures_shaders_length), std::make_shared(impeller_imgui_shaders_data, impeller_imgui_shaders_length), diff --git a/impeller/playground/imgui/BUILD.gn b/impeller/playground/imgui/BUILD.gn index c56377f7301f6..fcce7aed70814 100644 --- a/impeller/playground/imgui/BUILD.gn +++ b/impeller/playground/imgui/BUILD.gn @@ -5,7 +5,7 @@ import("//flutter/impeller/tools/impeller.gni") impeller_shaders("imgui_shaders") { - name = "imgui_shaders" + name = "imgui" shaders = [ "imgui_raster.vert", "imgui_raster.frag", diff --git a/impeller/playground/imgui/imgui_raster.vert b/impeller/playground/imgui/imgui_raster.vert index 1afd790022518..038bd579b8f19 100644 --- a/impeller/playground/imgui/imgui_raster.vert +++ b/impeller/playground/imgui/imgui_raster.vert @@ -9,13 +9,23 @@ uniforms; in vec2 vertex_position; in vec2 texture_coordinates; -in uint vertex_color; +in int vertex_color; out vec2 frag_texture_coordinates; out vec4 frag_vertex_color; +vec4 ImVertexColorToVec4(int color) { + const float kScale = 1.0f / 255.0f; + return vec4( + ((color >> 0) & 0xFF) * kScale, + ((color >> 8) & 0xFF) * kScale, + ((color >> 16) & 0xFF) * kScale, + ((color >> 24) & 0xFF) * kScale + ); +} + void main() { gl_Position = uniforms.mvp * vec4(vertex_position.xy, 0.0, 1.0); frag_texture_coordinates = texture_coordinates; - frag_vertex_color = unpackUnorm4x8(vertex_color); + frag_vertex_color = ImVertexColorToVec4(vertex_color); } diff --git a/impeller/tools/impeller.gni b/impeller/tools/impeller.gni index 1b60ddcc47b44..b037af41c07b4 100644 --- a/impeller/tools/impeller.gni +++ b/impeller/tools/impeller.gni @@ -146,44 +146,88 @@ template("metal_library") { } } -# ------------------------------------------------------------------------------ -# @brief Build and reflect shader information. Reflected shader -# information will be added to a generated source set along -# with the shader contents. -# -# @param[required] name The name of the shader library. -# -# @param[required] sources The GLSL (4.60) sources to compiled into the shader -# library. -# -template("impeller_shaders_real") { +template("embed_blob") { + assert(defined(invoker.symbol_name), "The symbol name must be specified.") + assert(defined(invoker.blob), "The blob file to embed must be specified") + assert(defined(invoker.hdr), + "The header file containing the symbol name must be specified.") + assert(defined(invoker.cc), + "The CC file containing the symbol data must be specified.") + assert(defined(invoker.deps), "The target dependencies must be specified") + + gen_blob_target_name = "gen_blob_$target_name" + action(gen_blob_target_name) { + inputs = [ invoker.blob ] + outputs = [ + invoker.hdr, + invoker.cc, + ] + args = [ + "--symbol-name", + invoker.symbol_name, + "--output-header", + rebase_path(invoker.hdr), + "--output-source", + rebase_path(invoker.cc), + "--source", + rebase_path(invoker.blob), + ] + script = "//flutter/impeller/tools/xxd.py" + deps = invoker.deps + } + + embed_config = "embed_$target_name" + config(embed_config) { + include_dirs = [ get_path_info( + get_label_info("//flutter/impeller:impeller", "target_gen_dir"), + "dir") ] + } + + source_set(target_name) { + public_configs = [ ":$embed_config" ] + sources = get_target_outputs(":$gen_blob_target_name") + deps = [ ":$gen_blob_target_name" ] + } +} + +template("impellerc") { + # Optional: invoker.defines specifies a list of valueless macro definitions. assert(defined(invoker.shaders), "Impeller shaders must be specified.") - assert(defined(invoker.name), "Name of the shader library must be specified.") + assert(defined(invoker.sl_file_extension), + "The extension of the SL file must be specified (metal, glsl, etc..).") + assert(defined(invoker.intermediates_subdir), + "The subdirectory in which to put intermediates must be specified.") + assert(defined(invoker.shader_target_flag), + "The flag to impellerc for target selection must be specified.") + + sl_file_extension = invoker.sl_file_extension - base_target_name = target_name - impellerc_target_name = "impellerc_mtl_$target_name" - compiled_action_foreach(impellerc_target_name) { + compiled_action_foreach(target_name) { tool = "//flutter/impeller/compiler:impellerc" sources = invoker.shaders + subdir = invoker.intermediates_subdir + shader_target_flag = invoker.shader_target_flag - metal_intermediate = "$target_gen_dir/mtl/{{source_file_part}}.metal" - spirv_intermediate = "$target_gen_dir/mtl/{{source_file_part}}.spirv" + sl_intermediate = + "$target_gen_dir/$subdir/{{source_file_part}}.$sl_file_extension" + spirv_intermediate = "$target_gen_dir/$subdir/{{source_file_part}}.spirv" reflection_json_intermediate = - "$target_gen_dir/mtl/{{source_file_part}}.json" + "$target_gen_dir/$subdir/{{source_file_part}}.json" reflection_header_intermediate = - "$target_gen_dir/mtl/{{source_file_part}}.h" - reflection_cc_intermediate = "$target_gen_dir/mtl/{{source_file_part}}.cc" + "$target_gen_dir/$subdir/{{source_file_part}}.h" + reflection_cc_intermediate = + "$target_gen_dir/$subdir/{{source_file_part}}.cc" outputs = [ - metal_intermediate, + sl_intermediate, reflection_header_intermediate, reflection_cc_intermediate, ] - depfile_path = "$target_gen_dir/mtl/{{source_file_part}}.d" + depfile_path = "$target_gen_dir/$subdir/{{source_file_part}}.d" - metal_intermediate_path = rebase_path(metal_intermediate, root_build_dir) + sl_intermediate_path = rebase_path(sl_intermediate, root_build_dir) spirv_intermediate_path = rebase_path(spirv_intermediate, root_build_dir) depfile_intermediate_path = rebase_path(depfile_path, root_build_dir) @@ -197,60 +241,41 @@ template("impeller_shaders_real") { args = [ "--input={{source}}", - "--sl=$metal_intermediate_path", + "--sl=$sl_intermediate_path", "--spirv=$spirv_intermediate_path", "--reflection-json=$reflection_json_path", "--reflection-header=$reflection_header_path", "--reflection-cc=$reflection_cc_path", "--include={{source_dir}}", "--depfile=$depfile_intermediate_path", + "$shader_target_flag", ] - - if (is_mac) { - args += [ "--metal-desktop" ] - } - if (is_ios) { - args += [ "--metal-ios" ] + if (defined(invoker.defines)) { + foreach(def, invoker.defines) { + args += [ "--define=$def" ] + } } } +} - metal_library_target_name = "metal_library_$target_name" - metal_library(metal_library_target_name) { - name = invoker.name - sources = filter_include(get_target_outputs(":$impellerc_target_name"), - [ "*.metal" ]) - deps = [ ":$impellerc_target_name" ] - } - - shader_glue_target_name = "glue_$target_name" - shader_glue_config_name = "glue_config_$target_name" +template("impellerc_reflect") { + assert( + defined(invoker.impellerc_invocation), + "The target that specifies the ImpellerC invocation to reflect must be defined.") - config(shader_glue_config_name) { - impeller_root_gen_dir = get_path_info( + reflect_config = "reflect_$target_name" + config(reflect_config) { + include_dirs = [ get_path_info( get_label_info("//flutter/impeller:impeller", "target_gen_dir"), - "dir") - - # Contains the generated header headers. - include_dirs = [ - target_gen_dir, - impeller_root_gen_dir, - ] - - if (impeller_enable_metal) { - include_dirs += [ "$impeller_root_gen_dir/mtl" ] - } - - if (impeller_enable_opengles) { - include_dirs += [ "$impeller_root_gen_dir/gles" ] - } + "dir") ] } - source_set(shader_glue_target_name) { - public_configs = [ ":$shader_glue_config_name" ] + impellerc_invocation = invoker.impellerc_invocation - public = - filter_include(get_target_outputs(":$impellerc_target_name"), [ "*.h" ]) - sources = filter_include(get_target_outputs(":$impellerc_target_name"), + source_set(target_name) { + public_configs = [ ":$reflect_config" ] + public = filter_include(get_target_outputs(impellerc_invocation), [ "*.h" ]) + sources = filter_include(get_target_outputs(impellerc_invocation), [ "*.h", "*.cc", @@ -258,70 +283,159 @@ template("impeller_shaders_real") { ]) deps = [ - ":$impellerc_target_name", "//flutter/impeller/renderer", + impellerc_invocation, ] } +} - generate_embedder_data_sources = "embedded_data_gen_sources_$target_name" - action(generate_embedder_data_sources) { - metal_library_files = get_target_outputs(":$metal_library_target_name") - metal_library_file = metal_library_files[0] - inputs = [ metal_library_file ] - output_header = "$target_gen_dir/mtl/$base_target_name.h" - output_source = "$target_gen_dir/mtl/$base_target_name.c" - outputs = [ - output_header, - output_source, - ] - args = [ - "--symbol-name", - base_target_name, - "--output-header", - rebase_path(output_header), - "--output-source", - rebase_path(output_source), - "--source", - rebase_path(metal_library_file), +template("impeller_shaders_metal") { + assert(defined(invoker.shaders), "Impeller shaders must be specified.") + assert(defined(invoker.name), "Name of the shader library must be specified.") + + shaders_base_name = string_join("", + [ + invoker.name, + "_shaders", + ]) + impellerc_mtl = "impellerc_$target_name" + impellerc(impellerc_mtl) { + shaders = invoker.shaders + sl_file_extension = "metal" + intermediates_subdir = "mtl" + shader_target_flag = "" + defines = [ "IMPELLER_TARGET_METAL" ] + if (is_ios) { + shader_target_flag = "--metal-ios" + defines += [ "IMPELLER_TARGET_METAL_IOS" ] + } else if (is_mac) { + shader_target_flag = "--metal-desktop" + defines += [ "IMPELLER_TARGET_METAL_DESKTOP" ] + } else { + assert(false, "Metal not supported on this platform.") + } + } + + mtl_lib = "genlib_$target_name" + metal_library(mtl_lib) { + name = invoker.name + sources = + filter_include(get_target_outputs(":$impellerc_mtl"), [ "*.metal" ]) + deps = [ ":$impellerc_mtl" ] + } + + reflect_mtl = "reflect_$target_name" + impellerc_reflect(reflect_mtl) { + impellerc_invocation = ":$impellerc_mtl" + } + + embed_mtl_lib = "embed_$target_name" + embed_blob(embed_mtl_lib) { + metal_library_files = get_target_outputs(":$mtl_lib") + symbol_name = shaders_base_name + blob = metal_library_files[0] + hdr = "$target_gen_dir/mtl/$shaders_base_name.h" + cc = "$target_gen_dir/mtl/$shaders_base_name.c" + deps = [ ":$mtl_lib" ] + } + + group(target_name) { + public_deps = [ + ":$embed_mtl_lib", + ":$reflect_mtl", ] - script = "//flutter/impeller/tools/xxd.py" - deps = [ ":$metal_library_target_name" ] } +} + +template("blobcat_library") { + assert(defined(invoker.shaders), + "The shaders to build the library from must be specified.") + assert(defined(invoker.deps), "Target dependencies must be specified.") + + output_file = "$target_gen_dir/$target_name.shaderblob" + compiled_action(target_name) { + tool = "//flutter/impeller/blobcat" + inputs = invoker.shaders + outputs = [ output_file ] + output_path_rebased = rebase_path(output_file, root_build_dir) + args = [ "--output=$output_path_rebased" ] + foreach(shader, invoker.shaders) { + shader_path = rebase_path(shader, root_out_dir) + args += [ "--input=$shader_path" ] + } + deps = invoker.deps + } +} + +template("impeller_shaders_gles") { + assert(defined(invoker.shaders), "Impeller shaders must be specified.") + assert(defined(invoker.name), "Name of the shader library must be specified.") - shader_embedded_data_target_name = "embedded_data_$target_name" - source_set(shader_embedded_data_target_name) { - sources = get_target_outputs(":$generate_embedder_data_sources") - deps = [ ":$generate_embedder_data_sources" ] + shaders_base_name = string_join("", + [ + invoker.name, + "_shaders_gles", + ]) + impellerc_gles = "impellerc_$target_name" + impellerc(impellerc_gles) { + shaders = invoker.shaders + sl_file_extension = "gles" + intermediates_subdir = "gles" + shader_target_flag = "--opengl-es" + defines = [ "IMPELLER_TARGET_OPENGLES" ] + } + + gles_lib = "genlib_$target_name" + blobcat_library(gles_lib) { + shaders = + filter_include(get_target_outputs(":$impellerc_gles"), [ "*.gles" ]) + deps = [ ":$impellerc_gles" ] + } + + reflect_gles = "reflect_$target_name" + impellerc_reflect(reflect_gles) { + impellerc_invocation = ":$impellerc_gles" + } + + embed_gles_lib = "embed_$target_name" + embed_blob(embed_gles_lib) { + gles_library_files = get_target_outputs(":$gles_lib") + symbol_name = shaders_base_name + blob = gles_library_files[0] + hdr = "$target_gen_dir/gles/$shaders_base_name.h" + cc = "$target_gen_dir/gles/$shaders_base_name.c" + deps = [ ":$gles_lib" ] } group(target_name) { public_deps = [ - ":$shader_embedded_data_target_name", - ":$shader_glue_target_name", + ":$embed_gles_lib", + ":$reflect_gles", ] } } -# ------------------------------------------------------------------------------ -# @brief Builds the shader library from shader sources, generates -# reflected shader information as source set, and, generates a -# translation unit added as a source set that allows embedding -# shaders into the final binary. On platforms where Impeller is -# not supported, this is a no-op. -# -# @note For additional information about parameters, see -# `impeller_shaders_real` -# -# @see impeller_shaders_real -# template("impeller_shaders") { - if (impeller_shaders_supports_platform) { - impeller_shaders_real(target_name) { - forward_variables_from(invoker, "*") + mtl_shaders = "mtl_$target_name" + impeller_shaders_metal(mtl_shaders) { + name = invoker.name + shaders = invoker.shaders + } + + gles_shaders = "gles_$target_name" + impeller_shaders_gles(gles_shaders) { + name = invoker.name + shaders = invoker.shaders + } + + group(target_name) { + public_deps = [] + if (impeller_enable_metal) { + public_deps += [ ":$mtl_shaders" ] } - } else { - group(target_name) { - not_needed(invoker, "*") + + if (impeller_enable_opengles) { + public_deps += [ ":$gles_shaders" ] } } } From 03e9a79257b72b64ea7cd9cf945399e2ef64423e Mon Sep 17 00:00:00 2001 From: Brandon DeRosier Date: Wed, 20 Apr 2022 11:29:22 -0700 Subject: [PATCH 414/433] Implement mask blur in display list dispatcher (#142) --- impeller/aiks/canvas.cc | 6 ++-- impeller/aiks/paint.cc | 20 +++++++++++ impeller/aiks/paint.h | 22 +++++++++++++ .../display_list/display_list_dispatcher.cc | 23 +++++++++++-- .../display_list/display_list_unittests.cc | 33 +++++++++++++++++++ impeller/entity/contents/contents.cc | 2 +- 6 files changed, 100 insertions(+), 6 deletions(-) diff --git a/impeller/aiks/canvas.cc b/impeller/aiks/canvas.cc index c45bc42a5f63b..ada6d50cb0f5c 100644 --- a/impeller/aiks/canvas.cc +++ b/impeller/aiks/canvas.cc @@ -114,7 +114,7 @@ void Canvas::DrawPath(Path path, Paint paint) { entity.SetPath(std::move(path)); entity.SetStencilDepth(GetStencilDepth()); entity.SetBlendMode(paint.blend_mode); - entity.SetContents(paint.CreateContentsForEntity()); + entity.SetContents(paint.WithFilters(paint.CreateContentsForEntity())); GetCurrentPass().AddEntity(std::move(entity)); } @@ -242,7 +242,7 @@ void Canvas::DrawImageRect(std::shared_ptr image, entity.SetPath(PathBuilder{}.AddRect(dest).TakePath()); entity.SetBlendMode(paint.blend_mode); entity.SetStencilDepth(GetStencilDepth()); - entity.SetContents(contents); + entity.SetContents(paint.WithFilters(contents, false)); entity.SetTransformation(GetCurrentTransformation()); GetCurrentPass().AddEntity(std::move(entity)); @@ -296,7 +296,7 @@ void Canvas::DrawTextFrame(TextFrame text_frame, Point position, Paint paint) { entity.SetPath({}); entity.SetStencilDepth(GetStencilDepth()); entity.SetBlendMode(paint.blend_mode); - entity.SetContents(std::move(text_contents)); + entity.SetContents(paint.WithFilters(std::move(text_contents), true)); GetCurrentPass().AddEntity(std::move(entity)); } diff --git a/impeller/aiks/paint.cc b/impeller/aiks/paint.cc index 46a14b5ab36ac..e6afd0f419694 100644 --- a/impeller/aiks/paint.cc +++ b/impeller/aiks/paint.cc @@ -33,4 +33,24 @@ std::shared_ptr Paint::CreateContentsForEntity() const { return nullptr; } +std::shared_ptr Paint::WithFilters( + std::shared_ptr input, + std::optional is_solid_color) const { + bool is_solid_color_val = is_solid_color.value_or(!contents); + + if (mask_blur.has_value()) { + if (is_solid_color_val) { + input = FilterContents::MakeGaussianBlur( + FilterInput::Make(input), mask_blur->sigma, mask_blur->sigma, + mask_blur->blur_style); + } else { + input = FilterContents::MakeBorderMaskBlur( + FilterInput::Make(input), mask_blur->sigma, mask_blur->sigma, + mask_blur->blur_style); + } + } + + return input; +} + } // namespace impeller diff --git a/impeller/aiks/paint.h b/impeller/aiks/paint.h index be9262d0225ac..07af116b2a2c8 100644 --- a/impeller/aiks/paint.h +++ b/impeller/aiks/paint.h @@ -8,12 +8,18 @@ #include "flutter/fml/macros.h" #include "impeller/entity/contents/contents.h" +#include "impeller/entity/contents/filters/filter_contents.h" #include "impeller/entity/contents/solid_stroke_contents.h" #include "impeller/entity/entity.h" #include "impeller/geometry/color.h" namespace impeller { +struct MaskBlur { + FilterContents::BlurStyle blur_style; + FilterContents::Sigma sigma; +}; + struct Paint { enum class Style { kFill, @@ -27,9 +33,25 @@ struct Paint { Scalar stroke_miter = 4.0; Style style = Style::kFill; Entity::BlendMode blend_mode = Entity::BlendMode::kSourceOver; + std::optional mask_blur; std::shared_ptr contents; std::shared_ptr CreateContentsForEntity() const; + + /// @brief Wrap this paint's configured filters to the given contents. + /// @param[in] input The contents to wrap with paint's filters. + /// @param[in] is_solid_color Affects mask blurring behavior. If false, use + /// the image border for mask blurring. If true, + /// do a Gaussian blur to achieve the mask + /// blurring effect for arbitrary paths. If unset, + /// use the current paint configuration to infer + /// the result. + /// @return The filter-wrapped contents. If there are no filters that need + /// to be wrapped for the current paint configuration, the + /// original contents is returned. + std::shared_ptr WithFilters( + std::shared_ptr input, + std::optional is_solid_color = std::nullopt) const; }; } // namespace impeller diff --git a/impeller/display_list/display_list_dispatcher.cc b/impeller/display_list/display_list_dispatcher.cc index 2f9abb0010bc3..fc8142dcf16ec 100644 --- a/impeller/display_list/display_list_dispatcher.cc +++ b/impeller/display_list/display_list_dispatcher.cc @@ -7,6 +7,7 @@ #include #include "flutter/fml/trace_event.h" +#include "impeller/entity/contents/filters/filter_contents.h" #include "impeller/entity/contents/linear_gradient_contents.h" #include "impeller/entity/contents/solid_stroke_contents.h" #include "impeller/entity/entity.h" @@ -253,15 +254,33 @@ void DisplayListDispatcher::setPathEffect(sk_sp effect) { UNIMPLEMENTED; } +static FilterContents::BlurStyle ToBlurStyle(SkBlurStyle blur_style) { + switch (blur_style) { + case kNormal_SkBlurStyle: + return FilterContents::BlurStyle::kNormal; + case kSolid_SkBlurStyle: + return FilterContents::BlurStyle::kSolid; + case kOuter_SkBlurStyle: + return FilterContents::BlurStyle::kOuter; + case kInner_SkBlurStyle: + return FilterContents::BlurStyle::kInner; + } +} + // |flutter::Dispatcher| void DisplayListDispatcher::setMaskFilter(const flutter::DlMaskFilter* filter) { // Needs https://github.com/flutter/flutter/issues/95434 if (filter == nullptr) { - // Reset everything + paint_.mask_blur = std::nullopt; return; } switch (filter->type()) { - case flutter::DlMaskFilterType::kBlur: + case flutter::DlMaskFilterType::kBlur: { + auto blur = filter->asBlur(); + paint_.mask_blur = {.blur_style = ToBlurStyle(blur->style()), + .sigma = FilterContents::Sigma(blur->sigma())}; + break; + } case flutter::DlMaskFilterType::kUnknown: UNIMPLEMENTED; break; diff --git a/impeller/display_list/display_list_unittests.cc b/impeller/display_list/display_list_unittests.cc index 321832e721393..425e7dfe4af7b 100644 --- a/impeller/display_list/display_list_unittests.cc +++ b/impeller/display_list/display_list_unittests.cc @@ -8,6 +8,8 @@ #include "third_party/skia/include/core/SkPathBuilder.h" #include "flutter/display_list/display_list_builder.h" +#include "flutter/display_list/display_list_mask_filter.h" +#include "flutter/display_list/types.h" #include "flutter/testing/testing.h" #include "impeller/display_list/display_list_image_impeller.h" #include "impeller/display_list/display_list_playground.h" @@ -174,5 +176,36 @@ TEST_P(DisplayListTest, StrokedPathsDrawCorrectly) { ASSERT_TRUE(OpenPlaygroundHere(builder.Build())); } +TEST_F(DisplayListTest, CanDrawWithMaskBlur) { + auto texture = CreateTextureForFixture("embarcadero.jpg"); + flutter::DisplayListBuilder builder; + + // Mask blurred image. + { + auto filter = flutter::DlBlurMaskFilter(kNormal_SkBlurStyle, 10.0f); + builder.setMaskFilter(&filter); + builder.drawImage(DlImageImpeller::Make(texture), SkPoint::Make(100, 100), + SkSamplingOptions{}, true); + } + + // Mask blurred filled path. + { + builder.setColor(SK_ColorYELLOW); + auto filter = flutter::DlBlurMaskFilter(kOuter_SkBlurStyle, 10.0f); + builder.setMaskFilter(&filter); + builder.drawArc(SkRect::MakeXYWH(410, 110, 100, 100), 45, 270, true); + } + + // Mask blurred text. + { + auto filter = flutter::DlBlurMaskFilter(kSolid_SkBlurStyle, 10.0f); + builder.setMaskFilter(&filter); + builder.drawTextBlob( + SkTextBlob::MakeFromString("Testing", CreateTestFont()), 220, 170); + } + + ASSERT_TRUE(OpenPlaygroundHere(builder.Build())); +} + } // namespace testing } // namespace impeller diff --git a/impeller/entity/contents/contents.cc b/impeller/entity/contents/contents.cc index a2c0764e23607..98c5a584dcf5a 100644 --- a/impeller/entity/contents/contents.cc +++ b/impeller/entity/contents/contents.cc @@ -47,7 +47,7 @@ std::optional Contents::RenderToSnapshot( RenderPass& pass) -> bool { Entity sub_entity; sub_entity.SetPath(entity.GetPath()); - sub_entity.SetBlendMode(Entity::BlendMode::kSource); + sub_entity.SetBlendMode(Entity::BlendMode::kSourceOver); sub_entity.SetTransformation( Matrix::MakeTranslation(Vector3(-bounds->origin)) * entity.GetTransformation()); From bd80be683877165a46fd7f4f888d9a1f9342127c Mon Sep 17 00:00:00 2001 From: Chinmay Garde Date: Wed, 20 Apr 2022 12:36:40 -0700 Subject: [PATCH 415/433] Fix Mac compilation issue. (#147) --- impeller/geometry/rect.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/impeller/geometry/rect.h b/impeller/geometry/rect.h index 1a5675a790ef0..ae2f89f905d68 100644 --- a/impeller/geometry/rect.h +++ b/impeller/geometry/rect.h @@ -147,7 +147,7 @@ struct TRect { constexpr std::array, 4> GetTransformedPoints( const Matrix& transform) const { auto points = GetPoints(); - for (int i = 0; i < points.size(); i++) { + for (size_t i = 0; i < points.size(); i++) { points[i] = transform * points[i]; } return points; From 397d0fa1b2a5aff4af5865e77580f4a0913c88b1 Mon Sep 17 00:00:00 2001 From: Dan Field Date: Wed, 20 Apr 2022 16:58:55 -0700 Subject: [PATCH 416/433] Fix up build/test issues when building on or off of mac (#148) --- impeller/BUILD.gn | 8 ++++++-- impeller/compiler/utilities.cc | 2 +- impeller/display_list/display_list_unittests.cc | 2 +- impeller/playground/playground.cc | 2 +- 4 files changed, 9 insertions(+), 5 deletions(-) diff --git a/impeller/BUILD.gn b/impeller/BUILD.gn index 76e0d6c249875..11c78699bda27 100644 --- a/impeller/BUILD.gn +++ b/impeller/BUILD.gn @@ -18,7 +18,11 @@ config("impeller_public_config") { } if (is_win) { - defines += [ "_USE_MATH_DEFINES" ] + defines += [ + "_USE_MATH_DEFINES", + # TODO(dnfield): https://github.com/flutter/flutter/issues/50053 + "_SILENCE_CXX17_CODECVT_HEADER_DEPRECATION_WARNING", + ] } } @@ -50,7 +54,6 @@ executable("impeller_unittests") { "base:base_unittests", "blobcat:blobcat_unittests", "compiler:compiler_unittests", - "fixtures", "geometry:geometry_unittests", # FML depends on the Dart VM for tracing and getting the current @@ -63,6 +66,7 @@ executable("impeller_unittests") { "aiks:aiks_unittests", "display_list:display_list_unittests", "entity:entity_unittests", + "fixtures", "image:image_unittests", "playground", "renderer:renderer_unittests", diff --git a/impeller/compiler/utilities.cc b/impeller/compiler/utilities.cc index a191eb9a7520e..11b9dd23cfd52 100644 --- a/impeller/compiler/utilities.cc +++ b/impeller/compiler/utilities.cc @@ -4,9 +4,9 @@ #include "impeller/compiler/utilities.h" +#include #include #include -#include namespace impeller { namespace compiler { diff --git a/impeller/display_list/display_list_unittests.cc b/impeller/display_list/display_list_unittests.cc index 425e7dfe4af7b..a9bf5b4335df4 100644 --- a/impeller/display_list/display_list_unittests.cc +++ b/impeller/display_list/display_list_unittests.cc @@ -176,7 +176,7 @@ TEST_P(DisplayListTest, StrokedPathsDrawCorrectly) { ASSERT_TRUE(OpenPlaygroundHere(builder.Build())); } -TEST_F(DisplayListTest, CanDrawWithMaskBlur) { +TEST_P(DisplayListTest, CanDrawWithMaskBlur) { auto texture = CreateTextureForFixture("embarcadero.jpg"); flutter::DisplayListBuilder builder; diff --git a/impeller/playground/playground.cc b/impeller/playground/playground.cc index e843a6ee5c64b..f92aa0654dfe2 100644 --- a/impeller/playground/playground.cc +++ b/impeller/playground/playground.cc @@ -37,7 +37,7 @@ std::string PlaygroundBackendToString(PlaygroundBackend backend) { Playground::Playground() : impl_(PlaygroundImpl::Create(GetParam())), renderer_(impl_->CreateContext()), - is_valid_(Playground::is_enabled() && renderer_.IsValid()) {} + is_valid_(renderer_.IsValid()) {} Playground::~Playground() = default; From 528e68d2a299df32230179a69e3c928a5bd6c331 Mon Sep 17 00:00:00 2001 From: Brandon DeRosier Date: Wed, 20 Apr 2022 19:16:39 -0700 Subject: [PATCH 417/433] Filters: Add local transforms (#140) --- impeller/entity/contents/contents.h | 2 +- .../border_mask_blur_filter_contents.cc | 15 +- .../border_mask_blur_filter_contents.h | 6 +- .../contents/filters/filter_contents.cc | 39 ++++- .../entity/contents/filters/filter_contents.h | 22 ++- .../entity/contents/filters/filter_input.cc | 157 +++++++++++++----- .../entity/contents/filters/filter_input.h | 123 ++++++++++++-- .../filters/gaussian_blur_filter_contents.cc | 15 +- .../filters/gaussian_blur_filter_contents.h | 5 +- 9 files changed, 298 insertions(+), 86 deletions(-) diff --git a/impeller/entity/contents/contents.h b/impeller/entity/contents/contents.h index a72ae642a5841..9cf59ca3770e3 100644 --- a/impeller/entity/contents/contents.h +++ b/impeller/entity/contents/contents.h @@ -40,7 +40,7 @@ class Contents { virtual std::optional GetCoverage(const Entity& entity) const; /// @brief Render this contents to a snapshot, respecting the entity's - /// transform, path, stencil depth, blend mode, etc. + /// transform, path, stencil depth, and blend mode. /// The result texture size is always the size of /// `GetCoverage(entity)`. virtual std::optional RenderToSnapshot( diff --git a/impeller/entity/contents/filters/border_mask_blur_filter_contents.cc b/impeller/entity/contents/filters/border_mask_blur_filter_contents.cc index db1d5272acce1..328f7d78e3cc0 100644 --- a/impeller/entity/contents/filters/border_mask_blur_filter_contents.cc +++ b/impeller/entity/contents/filters/border_mask_blur_filter_contents.cc @@ -108,18 +108,21 @@ bool BorderMaskBlurFilterContents::RenderFilter( return pass.AddCommand(std::move(cmd)); } -std::optional BorderMaskBlurFilterContents::GetCoverage( +std::optional BorderMaskBlurFilterContents::GetFilterCoverage( + const FilterInput::Vector& inputs, const Entity& entity) const { - auto coverage = FilterContents::GetCoverage(entity); + if (inputs.empty()) { + return std::nullopt; + } + + auto coverage = inputs[0]->GetCoverage(entity); if (!coverage.has_value()) { return std::nullopt; } - // Technically this works with all of our current filters, but this should be - // using the input[0] transform, not the entity transform! - // See: https://github.com/flutter/impeller/pull/130#issuecomment-1098892423 auto transformed_blur_vector = - entity.GetTransformation() + inputs[0] + ->GetTransform(entity) .TransformDirection( Vector2(Radius{sigma_x_}.radius, Radius{sigma_y_}.radius)) .Abs(); diff --git a/impeller/entity/contents/filters/border_mask_blur_filter_contents.h b/impeller/entity/contents/filters/border_mask_blur_filter_contents.h index dc327dc3f5edb..335edd797e117 100644 --- a/impeller/entity/contents/filters/border_mask_blur_filter_contents.h +++ b/impeller/entity/contents/filters/border_mask_blur_filter_contents.h @@ -21,8 +21,9 @@ class BorderMaskBlurFilterContents final : public FilterContents { void SetBlurStyle(BlurStyle blur_style); - // |Contents| - std::optional GetCoverage(const Entity& entity) const override; + // |FilterContents| + std::optional GetFilterCoverage(const FilterInput::Vector& inputs, + const Entity& entity) const override; private: // |FilterContents| @@ -31,6 +32,7 @@ class BorderMaskBlurFilterContents final : public FilterContents { const Entity& entity, RenderPass& pass, const Rect& coverage) const override; + Sigma sigma_x_; Sigma sigma_y_; BlurStyle blur_style_ = BlurStyle::kNormal; diff --git a/impeller/entity/contents/filters/filter_contents.cc b/impeller/entity/contents/filters/filter_contents.cc index 790faf4a8bb16..4a63da973c412 100644 --- a/impeller/entity/contents/filters/filter_contents.cc +++ b/impeller/entity/contents/filters/filter_contents.cc @@ -52,7 +52,8 @@ std::shared_ptr FilterContents::MakeBlend( new_blend->SetInputs({blend_input, *in_i}); new_blend->SetBlendMode(blend_mode); if (in_i < inputs.end() - 1) { - blend_input = FilterInput::Make(new_blend); + blend_input = FilterInput::Make( + std::static_pointer_cast(new_blend)); } } // new_blend will always be assigned because inputs.size() >= 2. @@ -139,6 +140,15 @@ bool FilterContents::Render(const ContentContext& renderer, } std::optional FilterContents::GetCoverage(const Entity& entity) const { + Entity entity_with_local_transform = entity; + entity_with_local_transform.SetTransformation( + GetTransform(entity.GetTransformation())); + return GetFilterCoverage(inputs_, entity_with_local_transform); +} + +std::optional FilterContents::GetFilterCoverage( + const FilterInput::Vector& inputs, + const Entity& entity) const { // The default coverage of FilterContents is just the union of its inputs' // coverage. FilterContents implementations may choose to adjust this // coverage depending on the use case. @@ -148,7 +158,7 @@ std::optional FilterContents::GetCoverage(const Entity& entity) const { } std::optional result; - for (const auto& input : inputs_) { + for (const auto& input : inputs) { auto coverage = input->GetCoverage(entity); if (!coverage.has_value()) { continue; @@ -157,7 +167,7 @@ std::optional FilterContents::GetCoverage(const Entity& entity) const { result = coverage; continue; } - result = result->Union(result.value()); + result = result->Union(coverage.value()); } return result; } @@ -165,16 +175,21 @@ std::optional FilterContents::GetCoverage(const Entity& entity) const { std::optional FilterContents::RenderToSnapshot( const ContentContext& renderer, const Entity& entity) const { - auto bounds = GetCoverage(entity); - if (!bounds.has_value() || bounds->IsEmpty()) { + Entity entity_with_local_transform = entity; + entity_with_local_transform.SetTransformation( + GetTransform(entity.GetTransformation())); + + auto coverage = GetFilterCoverage(inputs_, entity_with_local_transform); + if (!coverage.has_value() || coverage->IsEmpty()) { return std::nullopt; } // Render the filter into a new texture. auto texture = renderer.MakeSubpass( - ISize(bounds->size), + ISize(coverage->size), [=](const ContentContext& renderer, RenderPass& pass) -> bool { - return RenderFilter(inputs_, renderer, entity, pass, bounds.value()); + return RenderFilter(inputs_, renderer, entity_with_local_transform, + pass, coverage.value()); }); if (!texture) { @@ -182,7 +197,15 @@ std::optional FilterContents::RenderToSnapshot( } return Snapshot{.texture = texture, - .transform = Matrix::MakeTranslation(bounds->origin)}; + .transform = Matrix::MakeTranslation(coverage->origin)}; +} + +Matrix FilterContents::GetLocalTransform() const { + return Matrix(); +} + +Matrix FilterContents::GetTransform(const Matrix& parent_transform) const { + return parent_transform * GetLocalTransform(); } } // namespace impeller diff --git a/impeller/entity/contents/filters/filter_contents.h b/impeller/entity/contents/filters/filter_contents.h index 0917d5532b54d..6f6ab02c23425 100644 --- a/impeller/entity/contents/filters/filter_contents.h +++ b/impeller/entity/contents/filters/filter_contents.h @@ -122,23 +122,31 @@ class FilterContents : public Contents { std::optional GetCoverage(const Entity& entity) const override; // |Contents| - virtual std::optional RenderToSnapshot( - const ContentContext& renderer, - const Entity& entity) const override; + std::optional RenderToSnapshot(const ContentContext& renderer, + const Entity& entity) const override; + + virtual Matrix GetLocalTransform() const; + + Matrix GetTransform(const Matrix& parent_transform) const; private: - /// @brief Takes a set of zero or more input textures and writes to an output - /// texture. + virtual std::optional GetFilterCoverage( + const FilterInput::Vector& inputs, + const Entity& entity) const; + + /// @brief Takes a set of zero or more input textures and writes to an output + /// texture. virtual bool RenderFilter(const FilterInput::Vector& inputs, const ContentContext& renderer, const Entity& entity, RenderPass& pass, - const Rect& bounds) const = 0; + const Rect& coverage) const = 0; FilterInput::Vector inputs_; - Rect destination_; FML_DISALLOW_COPY_AND_ASSIGN(FilterContents); + + friend FilterContentsFilterInput; }; } // namespace impeller diff --git a/impeller/entity/contents/filters/filter_input.cc b/impeller/entity/contents/filters/filter_input.cc index 51ff74a58b32c..32db85b66eca1 100644 --- a/impeller/entity/contents/filters/filter_input.cc +++ b/impeller/entity/contents/filters/filter_input.cc @@ -8,14 +8,38 @@ #include #include #include +#include +#include "fml/logging.h" +#include "impeller/entity/contents/filters/filter_contents.h" #include "impeller/entity/contents/snapshot.h" #include "impeller/entity/entity.h" namespace impeller { +/******************************************************************************* + ******* FilterInput + ******************************************************************************/ + FilterInput::Ref FilterInput::Make(Variant input) { - return std::shared_ptr(new FilterInput(input)); + if (auto filter = std::get_if>(&input)) { + return std::static_pointer_cast( + std::shared_ptr( + new FilterContentsFilterInput(*filter))); + } + + if (auto contents = std::get_if>(&input)) { + return std::static_pointer_cast( + std::shared_ptr( + new ContentsFilterInput(*contents))); + } + + if (auto texture = std::get_if>(&input)) { + return std::static_pointer_cast( + std::shared_ptr(new TextureFilterInput(*texture))); + } + + FML_UNREACHABLE(); } FilterInput::Vector FilterInput::Make(std::initializer_list inputs) { @@ -27,63 +51,118 @@ FilterInput::Vector FilterInput::Make(std::initializer_list inputs) { return result; } -FilterInput::Variant FilterInput::GetInput() const { - return input_; +Matrix FilterInput::GetLocalTransform(const Entity& entity) const { + return Matrix(); } -std::optional FilterInput::GetCoverage(const Entity& entity) const { - if (snapshot_) { - return snapshot_->GetCoverage(); - } +Matrix FilterInput::GetTransform(const Entity& entity) const { + return entity.GetTransformation() * GetLocalTransform(entity); +} - if (auto contents = std::get_if>(&input_)) { - return contents->get()->GetCoverage(entity); - } +FilterInput::~FilterInput() = default; - if (auto texture = std::get_if>(&input_)) { - return entity.GetPathCoverage(); - } +/******************************************************************************* + ******* FilterContentsFilterInput + ******************************************************************************/ - FML_UNREACHABLE(); +FilterContentsFilterInput::FilterContentsFilterInput( + std::shared_ptr filter) + : filter_(filter) {} + +FilterContentsFilterInput::~FilterContentsFilterInput() = default; + +FilterInput::Variant FilterContentsFilterInput::GetInput() const { + return filter_; } -std::optional FilterInput::GetSnapshot(const ContentContext& renderer, - const Entity& entity) const { - if (snapshot_) { - return snapshot_; +std::optional FilterContentsFilterInput::GetSnapshot( + const ContentContext& renderer, + const Entity& entity) const { + if (!snapshot_.has_value()) { + snapshot_ = filter_->RenderToSnapshot(renderer, entity); } - snapshot_ = MakeSnapshot(renderer, entity); - return snapshot_; } -FilterInput::FilterInput(Variant input) : input_(input) {} +std::optional FilterContentsFilterInput::GetCoverage( + const Entity& entity) const { + return filter_->GetCoverage(entity); +} -FilterInput::~FilterInput() = default; +Matrix FilterContentsFilterInput::GetLocalTransform( + const Entity& entity) const { + return filter_->GetLocalTransform(); +} -std::optional FilterInput::MakeSnapshot( +Matrix FilterContentsFilterInput::GetTransform(const Entity& entity) const { + return filter_->GetTransform(entity.GetTransformation()); +} + +/******************************************************************************* + ******* ContentsFilterInput + ******************************************************************************/ + +ContentsFilterInput::ContentsFilterInput(std::shared_ptr contents) + : contents_(contents) {} + +ContentsFilterInput::~ContentsFilterInput() = default; + +FilterInput::Variant ContentsFilterInput::GetInput() const { + return contents_; +} + +std::optional ContentsFilterInput::GetSnapshot( const ContentContext& renderer, const Entity& entity) const { - if (auto contents = std::get_if>(&input_)) { - return contents->get()->RenderToSnapshot(renderer, entity); + if (!snapshot_.has_value()) { + snapshot_ = contents_->RenderToSnapshot(renderer, entity); } + return snapshot_; +} + +std::optional ContentsFilterInput::GetCoverage( + const Entity& entity) const { + return contents_->GetCoverage(entity); +} + +/******************************************************************************* + ******* TextureFilterInput + ******************************************************************************/ - if (auto texture = std::get_if>(&input_)) { - // Rendered textures stretch to fit the entity path coverage, so we - // incorporate this behavior by translating and scaling the snapshot - // transform. - auto path_bounds = entity.GetPath().GetBoundingBox(); - if (!path_bounds.has_value()) { - return std::nullopt; - } - auto transform = entity.GetTransformation() * - Matrix::MakeTranslation(path_bounds->origin) * - Matrix::MakeScale(Vector2(path_bounds->size) / - texture->get()->GetSize()); - return Snapshot{.texture = *texture, .transform = transform}; +TextureFilterInput::TextureFilterInput(std::shared_ptr texture) + : texture_(texture) {} + +TextureFilterInput::~TextureFilterInput() = default; + +FilterInput::Variant TextureFilterInput::GetInput() const { + return texture_; +} + +std::optional TextureFilterInput::GetSnapshot( + const ContentContext& renderer, + const Entity& entity) const { + return Snapshot{.texture = texture_, .transform = GetTransform(entity)}; +} + +std::optional TextureFilterInput::GetCoverage( + const Entity& entity) const { + auto path_bounds = entity.GetPath().GetBoundingBox(); + if (!path_bounds.has_value()) { + return std::nullopt; } + return Rect::MakeSize(Size(texture_->GetSize())) + .TransformBounds(GetTransform(entity)); +} - FML_UNREACHABLE(); +Matrix TextureFilterInput::GetLocalTransform(const Entity& entity) const { + // Compute the local transform such that the texture will cover the entity + // path bounding box. + auto path_bounds = entity.GetPath().GetBoundingBox(); + if (!path_bounds.has_value()) { + return Matrix(); + } + return Matrix::MakeTranslation(path_bounds->origin) * + Matrix::MakeScale(Vector2(path_bounds->size) / texture_->GetSize()); } } // namespace impeller diff --git a/impeller/entity/contents/filters/filter_input.h b/impeller/entity/contents/filters/filter_input.h index eee31df26510b..dd1f907a6df70 100644 --- a/impeller/entity/contents/filters/filter_input.h +++ b/impeller/entity/contents/filters/filter_input.h @@ -17,46 +17,137 @@ namespace impeller { class ContentContext; class Entity; +class FilterContents; + +/******************************************************************************* + ******* FilterInput + ******************************************************************************/ /// `FilterInput` is a lazy/single eval `Snapshot` which may be shared across -/// filter parameters and used to evaluate input bounds. +/// filter parameters and used to evaluate input coverage. /// -/// A `FilterInput` can be created from either a `Texture` or any `Contents` -/// class (including `FilterContents`), and can be re-used for any filter inputs -/// across an entity's filter graph without repeating subpasses unnecessarily. +/// A `FilterInput` can be re-used for any filter inputs across an entity's +/// filter graph without repeating subpasses unnecessarily. /// /// Filters may decide to not evaluate inputs in situations where they won't /// contribute to the filter's output texture. -class FilterInput final { +class FilterInput { public: using Ref = std::shared_ptr; using Vector = std::vector; - using Variant = - std::variant, std::shared_ptr>; + using Variant = std::variant, + std::shared_ptr, + std::shared_ptr>; - ~FilterInput(); + virtual ~FilterInput(); static FilterInput::Ref Make(Variant input); static FilterInput::Vector Make(std::initializer_list inputs); - Variant GetInput() const; + virtual Variant GetInput() const = 0; + + virtual std::optional GetSnapshot(const ContentContext& renderer, + const Entity& entity) const = 0; + + virtual std::optional GetCoverage(const Entity& entity) const = 0; + + /// @brief Get the local transform of this filter input. This transform is + /// relative to the `Entity` transform space. + virtual Matrix GetLocalTransform(const Entity& entity) const; + + /// @brief Get the transform of this `FilterInput`. This is equivalent to + /// calling `entity.GetTransformation() * GetLocalTransform()`. + virtual Matrix GetTransform(const Entity& entity) const; +}; + +/******************************************************************************* + ******* FilterContentsFilterInput + ******************************************************************************/ - std::optional GetCoverage(const Entity& entity) const; +class FilterContentsFilterInput final : public FilterInput { + public: + ~FilterContentsFilterInput() override; + // |FilterInput| + Variant GetInput() const override; + + // |FilterInput| std::optional GetSnapshot(const ContentContext& renderer, - const Entity& entity) const; + const Entity& entity) const override; + + // |FilterInput| + std::optional GetCoverage(const Entity& entity) const override; + + // |FilterInput| + Matrix GetLocalTransform(const Entity& entity) const override; + + // |FilterInput| + Matrix GetTransform(const Entity& entity) const override; private: - FilterInput(Variant input); + FilterContentsFilterInput(std::shared_ptr filter); - std::optional MakeSnapshot(const ContentContext& renderer, - const Entity& entity) const; + std::shared_ptr filter_; + mutable std::optional snapshot_; + + friend FilterInput; +}; - std::optional MakeSnapshotForTexture(const Entity& entity) const; +/******************************************************************************* + ******* ContentsFilterInput + ******************************************************************************/ - Variant input_; +class ContentsFilterInput final : public FilterInput { + public: + ~ContentsFilterInput() override; + + // |FilterInput| + Variant GetInput() const override; + + // |FilterInput| + std::optional GetSnapshot(const ContentContext& renderer, + const Entity& entity) const override; + + // |FilterInput| + std::optional GetCoverage(const Entity& entity) const override; + + private: + ContentsFilterInput(std::shared_ptr contents); + + std::shared_ptr contents_; mutable std::optional snapshot_; + + friend FilterInput; +}; + +/******************************************************************************* + ******* TextureFilterInput + ******************************************************************************/ + +class TextureFilterInput final : public FilterInput { + public: + ~TextureFilterInput() override; + + // |FilterInput| + Variant GetInput() const override; + + // |FilterInput| + std::optional GetSnapshot(const ContentContext& renderer, + const Entity& entity) const override; + + // |FilterInput| + std::optional GetCoverage(const Entity& entity) const override; + + // |FilterInput| + Matrix GetLocalTransform(const Entity& entity) const override; + + private: + TextureFilterInput(std::shared_ptr texture); + + std::shared_ptr texture_; + + friend FilterInput; }; } // namespace impeller diff --git a/impeller/entity/contents/filters/gaussian_blur_filter_contents.cc b/impeller/entity/contents/filters/gaussian_blur_filter_contents.cc index 140ded547bb06..1be110668a569 100644 --- a/impeller/entity/contents/filters/gaussian_blur_filter_contents.cc +++ b/impeller/entity/contents/filters/gaussian_blur_filter_contents.cc @@ -163,17 +163,22 @@ bool DirectionalGaussianBlurFilterContents::RenderFilter( return pass.AddCommand(cmd); } -std::optional DirectionalGaussianBlurFilterContents::GetCoverage( +std::optional DirectionalGaussianBlurFilterContents::GetFilterCoverage( + const FilterInput::Vector& inputs, const Entity& entity) const { - auto coverage = FilterContents::GetCoverage(entity); + if (inputs.empty()) { + return std::nullopt; + } + + auto coverage = inputs[0]->GetCoverage(entity); if (!coverage.has_value()) { return std::nullopt; } auto transformed_blur_vector = - entity.GetTransformation() - .TransformDirection(blur_direction_ * - ceil(Radius{blur_sigma_}.radius)) + inputs[0] + ->GetTransform(entity) + .TransformDirection(blur_direction_ * Radius{blur_sigma_}.radius) .Abs(); auto extent = coverage->size + transformed_blur_vector * 2; return Rect(coverage->origin - transformed_blur_vector, diff --git a/impeller/entity/contents/filters/gaussian_blur_filter_contents.h b/impeller/entity/contents/filters/gaussian_blur_filter_contents.h index b39ec2ceb6d5b..6b95d52a7a228 100644 --- a/impeller/entity/contents/filters/gaussian_blur_filter_contents.h +++ b/impeller/entity/contents/filters/gaussian_blur_filter_contents.h @@ -25,8 +25,9 @@ class DirectionalGaussianBlurFilterContents final : public FilterContents { void SetSourceOverride(FilterInput::Ref alpha_mask); - // |Contents| - std::optional GetCoverage(const Entity& entity) const override; + // |FilterContents| + std::optional GetFilterCoverage(const FilterInput::Vector& inputs, + const Entity& entity) const override; private: // |FilterContents| From 4a5c87563b14d49e2f5c3819a0f0e8611cd1ca25 Mon Sep 17 00:00:00 2001 From: Brandon DeRosier Date: Thu, 21 Apr 2022 13:20:12 -0700 Subject: [PATCH 418/433] Fix minor mask blur issues (#149) --- .../contents/filters/border_mask_blur_filter_contents.cc | 4 +--- impeller/entity/shaders/border_mask_blur.frag | 5 ++++- 2 files changed, 5 insertions(+), 4 deletions(-) diff --git a/impeller/entity/contents/filters/border_mask_blur_filter_contents.cc b/impeller/entity/contents/filters/border_mask_blur_filter_contents.cc index 328f7d78e3cc0..a14e2da07f4cf 100644 --- a/impeller/entity/contents/filters/border_mask_blur_filter_contents.cc +++ b/impeller/entity/contents/filters/border_mask_blur_filter_contents.cc @@ -92,9 +92,7 @@ bool BorderMaskBlurFilterContents::RenderFilter( VS::FrameInfo frame_info; frame_info.mvp = Matrix::MakeOrthographic(ISize(1, 1)); - auto scale = entity.GetTransformation().GetScale(); - frame_info.sigma_uv = Vector2(scale.x, scale.y) * - Vector2(sigma_x_.sigma, sigma_y_.sigma).Abs() / + frame_info.sigma_uv = Vector2(sigma_x_.sigma, sigma_y_.sigma).Abs() / input_snapshot->texture->GetSize(); frame_info.src_factor = src_color_factor_; frame_info.inner_blur_factor = inner_blur_factor_; diff --git a/impeller/entity/shaders/border_mask_blur.frag b/impeller/entity/shaders/border_mask_blur.frag index 365d9eaa10284..4c018bf3ad367 100644 --- a/impeller/entity/shaders/border_mask_blur.frag +++ b/impeller/entity/shaders/border_mask_blur.frag @@ -33,7 +33,10 @@ const float kHalfSqrtTwo = 0.70710678118; // Indefinite integral of the Gaussian function (with constant range 0->1). float GaussianIntegral(float x, float sigma) { - return 0.5 + 0.5 * erf(x * (kHalfSqrtTwo / sigma)); + // ( 1 + erf( x * (sqrt(2) / (2 * sigma) ) ) / 2 + // Because this sigmoid is always > 1, we remap it (n * 1.07 - 0.07) + // so that it always fades to zero before it reaches the blur radius. + return 0.535 * erf(x * (kHalfSqrtTwo / sigma)) + 0.465; } float BoxBlurMask(vec2 uv) { From 68bd71e9aac010c3b58138c03db63a9befcb2a92 Mon Sep 17 00:00:00 2001 From: Brandon DeRosier Date: Thu, 21 Apr 2022 15:18:47 -0700 Subject: [PATCH 419/433] Untie paths from entities (#145) --- impeller/aiks/canvas.cc | 13 ++--- impeller/aiks/paint.cc | 6 ++- impeller/aiks/paint.h | 5 +- impeller/aiks/paint_pass_delegate.cc | 4 ++ impeller/entity/BUILD.gn | 2 - impeller/entity/contents/clear_contents.cc | 34 ------------ impeller/entity/contents/clear_contents.h | 33 ------------ impeller/entity/contents/clip_contents.cc | 16 +++++- impeller/entity/contents/clip_contents.h | 9 ++++ impeller/entity/contents/contents.cc | 5 -- impeller/entity/contents/contents.h | 2 +- .../contents/filters/filter_contents.cc | 3 +- .../entity/contents/filters/filter_input.cc | 14 +---- .../contents/linear_gradient_contents.cc | 23 +++++--- .../contents/linear_gradient_contents.h | 7 +++ .../entity/contents/solid_color_contents.cc | 26 +++++++-- .../entity/contents/solid_color_contents.h | 13 ++++- .../entity/contents/solid_stroke_contents.cc | 17 +++--- .../entity/contents/solid_stroke_contents.h | 4 ++ impeller/entity/contents/texture_contents.cc | 12 ++++- impeller/entity/contents/texture_contents.h | 8 +++ impeller/entity/entity.cc | 16 ------ impeller/entity/entity.h | 7 --- impeller/entity/entity_pass.cc | 4 -- impeller/entity/entity_unittests.cc | 53 +++++++++---------- impeller/geometry/path.cc | 8 +++ impeller/geometry/path.h | 2 + 27 files changed, 171 insertions(+), 175 deletions(-) delete mode 100644 impeller/entity/contents/clear_contents.cc delete mode 100644 impeller/entity/contents/clear_contents.h diff --git a/impeller/aiks/canvas.cc b/impeller/aiks/canvas.cc index ada6d50cb0f5c..05bc7c06d06f4 100644 --- a/impeller/aiks/canvas.cc +++ b/impeller/aiks/canvas.cc @@ -8,7 +8,6 @@ #include "flutter/fml/logging.h" #include "impeller/aiks/paint_pass_delegate.h" -#include "impeller/entity/contents/clear_contents.h" #include "impeller/entity/contents/clip_contents.h" #include "impeller/entity/contents/text_contents.h" #include "impeller/entity/contents/texture_contents.h" @@ -111,10 +110,9 @@ void Canvas::RestoreToCount(size_t count) { void Canvas::DrawPath(Path path, Paint paint) { Entity entity; entity.SetTransformation(GetCurrentTransformation()); - entity.SetPath(std::move(path)); entity.SetStencilDepth(GetStencilDepth()); entity.SetBlendMode(paint.blend_mode); - entity.SetContents(paint.WithFilters(paint.CreateContentsForEntity())); + entity.SetContents(paint.WithFilters(paint.CreateContentsForEntity(std::move(path)))); GetCurrentPass().AddEntity(std::move(entity)); } @@ -124,8 +122,7 @@ void Canvas::DrawPaint(Paint paint) { entity.SetTransformation(GetCurrentTransformation()); entity.SetStencilDepth(GetStencilDepth()); entity.SetBlendMode(paint.blend_mode); - entity.SetContents( - std::make_shared(paint.CreateContentsForEntity())); + entity.SetContents(paint.CreateContentsForEntity({}, true)); GetCurrentPass().AddEntity(std::move(entity)); } @@ -158,11 +155,11 @@ void Canvas::SaveLayer(Paint paint, std::optional bounds) { void Canvas::ClipPath(Path path, Entity::ClipOperation clip_op) { auto contents = std::make_shared(); + contents->SetPath(std::move(path)); contents->SetClipOperation(clip_op); Entity entity; entity.SetTransformation(GetCurrentTransformation()); - entity.SetPath(std::move(path)); entity.SetContents(std::move(contents)); entity.SetStencilDepth(GetStencilDepth()); entity.SetAddsToCoverage(false); @@ -178,7 +175,6 @@ void Canvas::RestoreClip() { entity.SetTransformation(GetCurrentTransformation()); // This path is empty because ClipRestoreContents just generates a quad that // takes up the full render target. - entity.SetPath({}); entity.SetContents(std::make_shared()); entity.SetStencilDepth(GetStencilDepth()); entity.SetAddsToCoverage(false); @@ -234,12 +230,12 @@ void Canvas::DrawImageRect(std::shared_ptr image, } auto contents = std::make_shared(); + contents->SetPath(PathBuilder{}.AddRect(dest).TakePath()); contents->SetTexture(image->GetTexture()); contents->SetSourceRect(source); contents->SetSamplerDescriptor(std::move(sampler)); Entity entity; - entity.SetPath(PathBuilder{}.AddRect(dest).TakePath()); entity.SetBlendMode(paint.blend_mode); entity.SetStencilDepth(GetStencilDepth()); entity.SetContents(paint.WithFilters(contents, false)); @@ -293,7 +289,6 @@ void Canvas::DrawTextFrame(TextFrame text_frame, Point position, Paint paint) { Entity entity; entity.SetTransformation(GetCurrentTransformation() * Matrix::MakeTranslation(position)); - entity.SetPath({}); entity.SetStencilDepth(GetStencilDepth()); entity.SetBlendMode(paint.blend_mode); entity.SetContents(paint.WithFilters(std::move(text_contents), true)); diff --git a/impeller/aiks/paint.cc b/impeller/aiks/paint.cc index e6afd0f419694..b34b6787e56f6 100644 --- a/impeller/aiks/paint.cc +++ b/impeller/aiks/paint.cc @@ -8,7 +8,8 @@ namespace impeller { -std::shared_ptr Paint::CreateContentsForEntity() const { +std::shared_ptr Paint::CreateContentsForEntity(Path path, + bool cover) const { if (contents) { return contents; } @@ -16,11 +17,14 @@ std::shared_ptr Paint::CreateContentsForEntity() const { switch (style) { case Style::kFill: { auto solid_color = std::make_shared(); + solid_color->SetPath(std::move(path)); solid_color->SetColor(color); + solid_color->SetCover(cover); return solid_color; } case Style::kStroke: { auto solid_stroke = std::make_shared(); + solid_stroke->SetPath(std::move(path)); solid_stroke->SetColor(color); solid_stroke->SetStrokeSize(stroke_width); solid_stroke->SetStrokeMiter(stroke_miter); diff --git a/impeller/aiks/paint.h b/impeller/aiks/paint.h index 07af116b2a2c8..a6650de47a342 100644 --- a/impeller/aiks/paint.h +++ b/impeller/aiks/paint.h @@ -36,8 +36,6 @@ struct Paint { std::optional mask_blur; std::shared_ptr contents; - std::shared_ptr CreateContentsForEntity() const; - /// @brief Wrap this paint's configured filters to the given contents. /// @param[in] input The contents to wrap with paint's filters. /// @param[in] is_solid_color Affects mask blurring behavior. If false, use @@ -52,6 +50,9 @@ struct Paint { std::shared_ptr WithFilters( std::shared_ptr input, std::optional is_solid_color = std::nullopt) const; + + std::shared_ptr CreateContentsForEntity(Path path = {}, + bool cover = false) const; }; } // namespace impeller diff --git a/impeller/aiks/paint_pass_delegate.cc b/impeller/aiks/paint_pass_delegate.cc index 34791be892086..dddb6538e3e99 100644 --- a/impeller/aiks/paint_pass_delegate.cc +++ b/impeller/aiks/paint_pass_delegate.cc @@ -6,6 +6,7 @@ #include "impeller/entity/contents/contents.h" #include "impeller/entity/contents/texture_contents.h" +#include "impeller/geometry/path_builder.h" namespace impeller { @@ -34,6 +35,9 @@ bool PaintPassDelegate::CanCollapseIntoParentPass() { std::shared_ptr PaintPassDelegate::CreateContentsForSubpassTarget( std::shared_ptr target) { auto contents = std::make_shared(); + contents->SetPath(PathBuilder{} + .AddRect(Rect::MakeSize(Size(target->GetSize()))) + .TakePath()); contents->SetTexture(target); contents->SetSourceRect(Rect::MakeSize(Size(target->GetSize()))); contents->SetOpacity(paint_.color.alpha); diff --git a/impeller/entity/BUILD.gn b/impeller/entity/BUILD.gn index 3add3ae8c27f7..4f942c39a2c3e 100644 --- a/impeller/entity/BUILD.gn +++ b/impeller/entity/BUILD.gn @@ -31,8 +31,6 @@ impeller_shaders("entity_shaders") { impeller_component("entity") { sources = [ - "contents/clear_contents.cc", - "contents/clear_contents.h", "contents/clip_contents.cc", "contents/clip_contents.h", "contents/content_context.cc", diff --git a/impeller/entity/contents/clear_contents.cc b/impeller/entity/contents/clear_contents.cc deleted file mode 100644 index 6222244f1b56c..0000000000000 --- a/impeller/entity/contents/clear_contents.cc +++ /dev/null @@ -1,34 +0,0 @@ -// Copyright 2013 The Flutter Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#include "impeller/entity/contents/clear_contents.h" - -#include "impeller/entity/entity.h" -#include "impeller/geometry/path_builder.h" -#include "impeller/renderer/render_pass.h" - -namespace impeller { - -ClearContents::ClearContents(std::shared_ptr contents) - : contents_(std::move(contents)) {} - -ClearContents::~ClearContents() = default; - -// |Contents| -bool ClearContents::Render(const ContentContext& renderer, - const Entity& entity, - RenderPass& pass) const { - if (contents_ == nullptr) { - return false; - } - // Instead of an entity that doesn't know its size because the render target - // size was unknown to it at construction time, create a copy but substitute - // the contents with the replacements. - Entity clear_entity = entity; - clear_entity.SetPath( - PathBuilder{}.AddRect(Size(pass.GetRenderTargetSize())).TakePath()); - return contents_->Render(renderer, clear_entity, pass); -} - -} // namespace impeller diff --git a/impeller/entity/contents/clear_contents.h b/impeller/entity/contents/clear_contents.h deleted file mode 100644 index d151b544ca27a..0000000000000 --- a/impeller/entity/contents/clear_contents.h +++ /dev/null @@ -1,33 +0,0 @@ -// Copyright 2013 The Flutter Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#pragma once - -#include "flutter/fml/macros.h" -#include "impeller/entity/contents/contents.h" - -namespace impeller { - -//------------------------------------------------------------------------------ -/// @brief Disregard existing contents on the render target and use the -/// provided filter to render to the entire target. -/// -class ClearContents final : public Contents { - public: - ClearContents(std::shared_ptr contents); - - ~ClearContents(); - - // |Contents| - bool Render(const ContentContext& renderer, - const Entity& entity, - RenderPass& pass) const override; - - private: - std::shared_ptr contents_; - - FML_DISALLOW_COPY_AND_ASSIGN(ClearContents); -}; - -} // namespace impeller diff --git a/impeller/entity/contents/clip_contents.cc b/impeller/entity/contents/clip_contents.cc index df9f89ad0b2ad..532b00c1a74b2 100644 --- a/impeller/entity/contents/clip_contents.cc +++ b/impeller/entity/contents/clip_contents.cc @@ -2,6 +2,7 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. +#include #include "impeller/geometry/path_builder.h" #include "impeller/renderer/formats.h" #include "impeller/renderer/vertex_buffer_builder.h" @@ -23,10 +24,18 @@ ClipContents::ClipContents() = default; ClipContents::~ClipContents() = default; +void ClipContents::SetPath(Path path) { + path_ = std::move(path); +} + void ClipContents::SetClipOperation(Entity::ClipOperation clip_op) { clip_op_ = clip_op; } +std::optional ClipContents::GetCoverage(const Entity& entity) const { + return path_.GetTransformedBoundingBox(entity.GetTransformation()); +}; + bool ClipContents::Render(const ContentContext& renderer, const Entity& entity, RenderPass& pass) const { @@ -77,7 +86,7 @@ bool ClipContents::Render(const ContentContext& renderer, cmd.pipeline = renderer.GetClipPipeline(options); cmd.BindVertices(SolidColorContents::CreateSolidFillVertices( - entity.GetPath(), pass.GetTransientsBuffer())); + path_, pass.GetTransientsBuffer())); info.mvp = Matrix::MakeOrthographic(pass.GetRenderTargetSize()) * entity.GetTransformation(); @@ -95,6 +104,11 @@ ClipRestoreContents::ClipRestoreContents() = default; ClipRestoreContents::~ClipRestoreContents() = default; +std::optional ClipRestoreContents::GetCoverage( + const Entity& entity) const { + return std::nullopt; +}; + bool ClipRestoreContents::Render(const ContentContext& renderer, const Entity& entity, RenderPass& pass) const { diff --git a/impeller/entity/contents/clip_contents.h b/impeller/entity/contents/clip_contents.h index 722b35776cc1c..9153ebb5c0a5a 100644 --- a/impeller/entity/contents/clip_contents.h +++ b/impeller/entity/contents/clip_contents.h @@ -20,14 +20,20 @@ class ClipContents final : public Contents { ~ClipContents(); + void SetPath(Path path); + void SetClipOperation(Entity::ClipOperation clip_op); + // |Contents| + std::optional GetCoverage(const Entity& entity) const override; + // |Contents| bool Render(const ContentContext& renderer, const Entity& entity, RenderPass& pass) const override; private: + Path path_; Entity::ClipOperation clip_op_ = Entity::ClipOperation::kIntersect; FML_DISALLOW_COPY_AND_ASSIGN(ClipContents); @@ -39,6 +45,9 @@ class ClipRestoreContents final : public Contents { ~ClipRestoreContents(); + // |Contents| + std::optional GetCoverage(const Entity& entity) const override; + // |Contents| bool Render(const ContentContext& renderer, const Entity& entity, diff --git a/impeller/entity/contents/contents.cc b/impeller/entity/contents/contents.cc index 98c5a584dcf5a..d5c95a8cbf6f6 100644 --- a/impeller/entity/contents/contents.cc +++ b/impeller/entity/contents/contents.cc @@ -29,10 +29,6 @@ Contents::Contents() = default; Contents::~Contents() = default; -std::optional Contents::GetCoverage(const Entity& entity) const { - return entity.GetPathCoverage(); -} - std::optional Contents::RenderToSnapshot( const ContentContext& renderer, const Entity& entity) const { @@ -46,7 +42,6 @@ std::optional Contents::RenderToSnapshot( [&contents = *this, &entity, &bounds](const ContentContext& renderer, RenderPass& pass) -> bool { Entity sub_entity; - sub_entity.SetPath(entity.GetPath()); sub_entity.SetBlendMode(Entity::BlendMode::kSourceOver); sub_entity.SetTransformation( Matrix::MakeTranslation(Vector3(-bounds->origin)) * diff --git a/impeller/entity/contents/contents.h b/impeller/entity/contents/contents.h index 9cf59ca3770e3..5f9bf4805c313 100644 --- a/impeller/entity/contents/contents.h +++ b/impeller/entity/contents/contents.h @@ -37,7 +37,7 @@ class Contents { RenderPass& pass) const = 0; /// @brief Get the screen space bounding rectangle that this contents affects. - virtual std::optional GetCoverage(const Entity& entity) const; + virtual std::optional GetCoverage(const Entity& entity) const = 0; /// @brief Render this contents to a snapshot, respecting the entity's /// transform, path, stencil depth, and blend mode. diff --git a/impeller/entity/contents/filters/filter_contents.cc b/impeller/entity/contents/filters/filter_contents.cc index 4a63da973c412..a054aa80383a0 100644 --- a/impeller/entity/contents/filters/filter_contents.cc +++ b/impeller/entity/contents/filters/filter_contents.cc @@ -129,11 +129,12 @@ bool FilterContents::Render(const ContentContext& renderer, // Draw the result texture, respecting the transform and clip stack. auto contents = std::make_shared(); + contents->SetPath( + PathBuilder{}.AddRect(filter_coverage.value()).GetCurrentPath()); contents->SetTexture(snapshot.texture); contents->SetSourceRect(Rect::MakeSize(Size(snapshot.texture->GetSize()))); Entity e; - e.SetPath(PathBuilder{}.AddRect(filter_coverage.value()).GetCurrentPath()); e.SetBlendMode(entity.GetBlendMode()); e.SetStencilDepth(entity.GetStencilDepth()); return contents->Render(renderer, e, pass); diff --git a/impeller/entity/contents/filters/filter_input.cc b/impeller/entity/contents/filters/filter_input.cc index 32db85b66eca1..6719886467e0c 100644 --- a/impeller/entity/contents/filters/filter_input.cc +++ b/impeller/entity/contents/filters/filter_input.cc @@ -146,23 +146,13 @@ std::optional TextureFilterInput::GetSnapshot( std::optional TextureFilterInput::GetCoverage( const Entity& entity) const { - auto path_bounds = entity.GetPath().GetBoundingBox(); - if (!path_bounds.has_value()) { - return std::nullopt; - } return Rect::MakeSize(Size(texture_->GetSize())) .TransformBounds(GetTransform(entity)); } Matrix TextureFilterInput::GetLocalTransform(const Entity& entity) const { - // Compute the local transform such that the texture will cover the entity - // path bounding box. - auto path_bounds = entity.GetPath().GetBoundingBox(); - if (!path_bounds.has_value()) { - return Matrix(); - } - return Matrix::MakeTranslation(path_bounds->origin) * - Matrix::MakeScale(Vector2(path_bounds->size) / texture_->GetSize()); + // Compute the local transform such that the texture is centered. + return Matrix::MakeTranslation(-Point(texture_->GetSize()) / 2); } } // namespace impeller diff --git a/impeller/entity/contents/linear_gradient_contents.cc b/impeller/entity/contents/linear_gradient_contents.cc index 092eee4ff18bc..91e7912117842 100644 --- a/impeller/entity/contents/linear_gradient_contents.cc +++ b/impeller/entity/contents/linear_gradient_contents.cc @@ -15,6 +15,10 @@ LinearGradientContents::LinearGradientContents() = default; LinearGradientContents::~LinearGradientContents() = default; +void LinearGradientContents::SetPath(Path path) { + path_ = std::move(path); +} + void LinearGradientContents::SetEndPoints(Point start_point, Point end_point) { start_point_ = start_point; end_point_ = end_point; @@ -34,6 +38,11 @@ const std::vector& LinearGradientContents::GetColors() const { return colors_; } +std::optional LinearGradientContents::GetCoverage( + const Entity& entity) const { + return path_.GetTransformedBoundingBox(entity.GetTransformation()); +}; + bool LinearGradientContents::Render(const ContentContext& renderer, const Entity& entity, RenderPass& pass) const { @@ -42,13 +51,13 @@ bool LinearGradientContents::Render(const ContentContext& renderer, auto vertices_builder = VertexBufferBuilder(); { - auto result = Tessellator{}.Tessellate(entity.GetPath().GetFillType(), - entity.GetPath().CreatePolyline(), - [&vertices_builder](Point point) { - VS::PerVertexData vtx; - vtx.vertices = point; - vertices_builder.AppendVertex(vtx); - }); + auto result = + Tessellator{}.Tessellate(path_.GetFillType(), path_.CreatePolyline(), + [&vertices_builder](Point point) { + VS::PerVertexData vtx; + vtx.vertices = point; + vertices_builder.AppendVertex(vtx); + }); if (!result) { return false; } diff --git a/impeller/entity/contents/linear_gradient_contents.h b/impeller/entity/contents/linear_gradient_contents.h index 0467f8fb3a603..5fafed5f96ee6 100644 --- a/impeller/entity/contents/linear_gradient_contents.h +++ b/impeller/entity/contents/linear_gradient_contents.h @@ -11,6 +11,7 @@ #include "flutter/fml/macros.h" #include "impeller/entity/contents/contents.h" #include "impeller/geometry/color.h" +#include "impeller/geometry/path.h" #include "impeller/geometry/point.h" namespace impeller { @@ -21,6 +22,11 @@ class LinearGradientContents final : public Contents { ~LinearGradientContents() override; + void SetPath(Path path); + + // |Contents| + std::optional GetCoverage(const Entity& entity) const override; + // |Contents| bool Render(const ContentContext& renderer, const Entity& entity, @@ -33,6 +39,7 @@ class LinearGradientContents final : public Contents { const std::vector& GetColors() const; private: + Path path_; Point start_point_; Point end_point_; std::vector colors_; diff --git a/impeller/entity/contents/solid_color_contents.cc b/impeller/entity/contents/solid_color_contents.cc index c809f037ca1c3..fce8d7ab7f868 100644 --- a/impeller/entity/contents/solid_color_contents.cc +++ b/impeller/entity/contents/solid_color_contents.cc @@ -7,6 +7,7 @@ #include "impeller/entity/contents/content_context.h" #include "impeller/entity/entity.h" #include "impeller/geometry/path.h" +#include "impeller/geometry/path_builder.h" #include "impeller/renderer/render_pass.h" #include "impeller/tessellator/tessellator.h" @@ -24,6 +25,19 @@ const Color& SolidColorContents::GetColor() const { return color_; } +void SolidColorContents::SetPath(Path path) { + path_ = std::move(path); +} + +void SolidColorContents::SetCover(bool cover) { + cover_ = cover; +} + +std::optional SolidColorContents::GetCoverage( + const Entity& entity) const { + return path_.GetTransformedBoundingBox(entity.GetTransformation()); +}; + VertexBuffer SolidColorContents::CreateSolidFillVertices(const Path& path, HostBuffer& buffer) { using VS = SolidFillPipeline::VertexShader; @@ -57,8 +71,12 @@ bool SolidColorContents::Render(const ContentContext& renderer, cmd.pipeline = renderer.GetSolidFillPipeline(OptionsFromPassAndEntity(pass, entity)); cmd.stencil_reference = entity.GetStencilDepth(); - cmd.BindVertices( - CreateSolidFillVertices(entity.GetPath(), pass.GetTransientsBuffer())); + + cmd.BindVertices(CreateSolidFillVertices( + cover_ + ? PathBuilder{}.AddRect(Size(pass.GetRenderTargetSize())).TakePath() + : path_, + pass.GetTransientsBuffer())); VS::FrameInfo frame_info; frame_info.mvp = Matrix::MakeOrthographic(pass.GetRenderTargetSize()) * @@ -75,8 +93,10 @@ bool SolidColorContents::Render(const ContentContext& renderer, return true; } -std::unique_ptr SolidColorContents::Make(Color color) { +std::unique_ptr SolidColorContents::Make(Path path, + Color color) { auto contents = std::make_unique(); + contents->SetPath(std::move(path)); contents->SetColor(color); return contents; } diff --git a/impeller/entity/contents/solid_color_contents.h b/impeller/entity/contents/solid_color_contents.h index 8f2551ca2b3d5..470b018051f44 100644 --- a/impeller/entity/contents/solid_color_contents.h +++ b/impeller/entity/contents/solid_color_contents.h @@ -11,6 +11,7 @@ #include "flutter/fml/macros.h" #include "impeller/entity/contents/contents.h" #include "impeller/geometry/color.h" +#include "impeller/geometry/path.h" namespace impeller { @@ -24,21 +25,31 @@ class SolidColorContents final : public Contents { ~SolidColorContents() override; - static std::unique_ptr Make(Color color); + static std::unique_ptr Make(Path path, Color color); static VertexBuffer CreateSolidFillVertices(const Path& path, HostBuffer& buffer); + void SetPath(Path path); + + void SetCover(bool cover); + void SetColor(Color color); const Color& GetColor() const; + // |Contents| + std::optional GetCoverage(const Entity& entity) const override; + // |Contents| bool Render(const ContentContext& renderer, const Entity& entity, RenderPass& pass) const override; private: + Path path_; + bool cover_ = false; + Color color_; FML_DISALLOW_COPY_AND_ASSIGN(SolidColorContents); diff --git a/impeller/entity/contents/solid_stroke_contents.cc b/impeller/entity/contents/solid_stroke_contents.cc index cee7da91438bb..1cfbfb9f8bdc8 100644 --- a/impeller/entity/contents/solid_stroke_contents.cc +++ b/impeller/entity/contents/solid_stroke_contents.cc @@ -29,12 +29,17 @@ const Color& SolidStrokeContents::GetColor() const { return color_; } +void SolidStrokeContents::SetPath(Path path) { + path_ = std::move(path); +} + std::optional SolidStrokeContents::GetCoverage( const Entity& entity) const { - auto path_coverage = entity.GetPathCoverage(); - if (!path_coverage.has_value()) { + auto path_bounds = path_.GetBoundingBox(); + if (!path_bounds.has_value()) { return std::nullopt; } + auto path_coverage = path_bounds->TransformBounds(entity.GetTransformation()); Scalar max_radius = 0.5; if (cap_ == Cap::kSquare) { @@ -46,9 +51,9 @@ std::optional SolidStrokeContents::GetCoverage( Vector2 max_radius_xy = entity.GetTransformation().TransformDirection( Vector2(max_radius, max_radius) * stroke_size_); - return Rect(path_coverage->origin - max_radius_xy, - Size(path_coverage->size.width + max_radius_xy.x * 2, - path_coverage->size.height + max_radius_xy.y * 2)); + return Rect(path_coverage.origin - max_radius_xy, + Size(path_coverage.size.width + max_radius_xy.x * 2, + path_coverage.size.height + max_radius_xy.y * 2)); } static VertexBuffer CreateSolidStrokeVertices( @@ -197,7 +202,7 @@ bool SolidStrokeContents::Render(const ContentContext& renderer, 5.0 / (stroke_size_ * entity.GetTransformation().GetMaxBasisLength()), 0.0, 0.0); cmd.BindVertices(CreateSolidStrokeVertices( - entity.GetPath(), pass.GetTransientsBuffer(), cap_proc_, join_proc_, + path_, pass.GetTransientsBuffer(), cap_proc_, join_proc_, miter_limit_, smoothing)); VS::BindFrameInfo(cmd, pass.GetTransientsBuffer().EmplaceUniform(frame_info)); VS::BindStrokeInfo(cmd, diff --git a/impeller/entity/contents/solid_stroke_contents.h b/impeller/entity/contents/solid_stroke_contents.h index e0d3f3abee260..8b38657141e7d 100644 --- a/impeller/entity/contents/solid_stroke_contents.h +++ b/impeller/entity/contents/solid_stroke_contents.h @@ -48,6 +48,8 @@ class SolidStrokeContents final : public Contents { ~SolidStrokeContents() override; + void SetPath(Path path); + void SetColor(Color color); const Color& GetColor() const; @@ -77,6 +79,8 @@ class SolidStrokeContents final : public Contents { RenderPass& pass) const override; private: + Path path_; + Color color_; Scalar stroke_size_ = 0.0; Scalar miter_limit_ = 4.0; diff --git a/impeller/entity/contents/texture_contents.cc b/impeller/entity/contents/texture_contents.cc index 77e0a16d06439..f66936c6b3704 100644 --- a/impeller/entity/contents/texture_contents.cc +++ b/impeller/entity/contents/texture_contents.cc @@ -18,6 +18,10 @@ TextureContents::TextureContents() = default; TextureContents::~TextureContents() = default; +void TextureContents::SetPath(Path path) { + path_ = std::move(path); +} + void TextureContents::SetTexture(std::shared_ptr texture) { texture_ = std::move(texture); } @@ -30,6 +34,10 @@ void TextureContents::SetOpacity(Scalar opacity) { opacity_ = opacity; } +std::optional TextureContents::GetCoverage(const Entity& entity) const { + return path_.GetTransformedBoundingBox(entity.GetTransformation()); +}; + bool TextureContents::Render(const ContentContext& renderer, const Entity& entity, RenderPass& pass) const { @@ -40,7 +48,7 @@ bool TextureContents::Render(const ContentContext& renderer, using VS = TextureFillVertexShader; using FS = TextureFillFragmentShader; - const auto coverage_rect = entity.GetPath().GetBoundingBox(); + const auto coverage_rect = path_.GetBoundingBox(); if (!coverage_rect.has_value()) { return true; @@ -62,7 +70,7 @@ bool TextureContents::Render(const ContentContext& renderer, VertexBufferBuilder vertex_builder; { const auto tess_result = Tessellator{}.Tessellate( - entity.GetPath().GetFillType(), entity.GetPath().CreatePolyline(), + path_.GetFillType(), path_.CreatePolyline(), [this, &vertex_builder, &coverage_rect, &texture_size](Point vtx) { VS::PerVertexData data; data.vertices = vtx; diff --git a/impeller/entity/contents/texture_contents.h b/impeller/entity/contents/texture_contents.h index 7b994a6c06e0e..4b02d88c0b69e 100644 --- a/impeller/entity/contents/texture_contents.h +++ b/impeller/entity/contents/texture_contents.h @@ -10,6 +10,7 @@ #include "flutter/fml/macros.h" #include "impeller/entity/contents/contents.h" +#include "impeller/geometry/path.h" #include "impeller/renderer/sampler_descriptor.h" namespace impeller { @@ -22,6 +23,8 @@ class TextureContents final : public Contents { ~TextureContents() override; + void SetPath(Path path); + void SetTexture(std::shared_ptr texture); std::shared_ptr GetTexture() const; @@ -36,12 +39,17 @@ class TextureContents final : public Contents { void SetOpacity(Scalar opacity); + // |Contents| + std::optional GetCoverage(const Entity& entity) const override; + // |Contents| bool Render(const ContentContext& renderer, const Entity& entity, RenderPass& pass) const override; public: + Path path_; + std::shared_ptr texture_; SamplerDescriptor sampler_descriptor_ = {}; Rect source_rect_; diff --git a/impeller/entity/entity.cc b/impeller/entity/entity.cc index 5412df2a14c38..920fad3e39b0c 100644 --- a/impeller/entity/entity.cc +++ b/impeller/entity/entity.cc @@ -24,22 +24,6 @@ void Entity::SetTransformation(const Matrix& transformation) { transformation_ = transformation; } -const Path& Entity::GetPath() const { - return path_; -} - -void Entity::SetPath(Path path) { - path_ = std::move(path); -} - -std::optional Entity::GetPathCoverage() const { - auto bounds = GetPath().GetBoundingBox(); - if (!bounds.has_value()) { - return std::nullopt; - } - return bounds->TransformBounds(GetTransformation()); -} - void Entity::SetAddsToCoverage(bool adds) { adds_to_coverage_ = adds; } diff --git a/impeller/entity/entity.h b/impeller/entity/entity.h index b4979bb3ed2af..8566b99bddd45 100644 --- a/impeller/entity/entity.h +++ b/impeller/entity/entity.h @@ -61,12 +61,6 @@ class Entity { void SetTransformation(const Matrix& transformation); - const Path& GetPath() const; - - void SetPath(Path path); - - std::optional GetPathCoverage() const; - void SetAddsToCoverage(bool adds); bool AddsToCoverage() const; @@ -93,7 +87,6 @@ class Entity { Matrix transformation_; std::shared_ptr contents_; BlendMode blend_mode_ = BlendMode::kSourceOver; - Path path_; uint32_t stencil_depth_ = 0u; bool adds_to_coverage_ = true; }; diff --git a/impeller/entity/entity_pass.cc b/impeller/entity/entity_pass.cc index c9f4d4f71f041..382cef3a03386 100644 --- a/impeller/entity/entity_pass.cc +++ b/impeller/entity/entity_pass.cc @@ -159,7 +159,6 @@ bool EntityPass::Render(ContentContext& renderer, } const auto subpass_coverage = GetSubpassCoverage(*subpass); - if (!subpass_coverage.has_value()) { continue; } @@ -227,9 +226,6 @@ bool EntityPass::Render(ContentContext& renderer, } Entity entity; - entity.SetPath(PathBuilder{} - .AddRect(Rect::MakeSize(subpass_coverage->size)) - .TakePath()); entity.SetContents(std::move(offscreen_texture_contents)); entity.SetStencilDepth(stencil_depth_); entity.SetBlendMode(subpass->blend_mode_); diff --git a/impeller/entity/entity_unittests.cc b/impeller/entity/entity_unittests.cc index 7401ddb187b9d..ba154b2af9800 100644 --- a/impeller/entity/entity_unittests.cc +++ b/impeller/entity/entity_unittests.cc @@ -33,8 +33,8 @@ TEST_P(EntityTest, CanCreateEntity) { TEST_P(EntityTest, CanDrawRect) { Entity entity; - entity.SetPath(PathBuilder{}.AddRect({100, 100, 100, 100}).TakePath()); - entity.SetContents(SolidColorContents::Make(Color::Red())); + entity.SetContents(SolidColorContents::Make( + PathBuilder{}.AddRect({100, 100, 100, 100}).TakePath(), Color::Red())); ASSERT_TRUE(OpenPlaygroundHere(entity)); } @@ -49,8 +49,8 @@ TEST_P(EntityTest, ThreeStrokesInOnePath) { .TakePath(); Entity entity; - entity.SetPath(path); auto contents = std::make_unique(); + contents->SetPath(std::move(path)); contents->SetColor(Color::Red()); contents->SetStrokeSize(5.0); entity.SetContents(std::move(contents)); @@ -79,8 +79,8 @@ TEST_P(EntityTest, TriangleInsideASquare) { .TakePath(); Entity entity; - entity.SetPath(path); auto contents = std::make_unique(); + contents->SetPath(std::move(path)); contents->SetColor(Color::Red()); contents->SetStrokeSize(20.0); entity.SetContents(std::move(contents)); @@ -124,6 +124,7 @@ TEST_P(EntityTest, StrokeCapAndJoinTest) { Path path, SolidStrokeContents::Cap cap, SolidStrokeContents::Join join) { auto contents = std::make_unique(); + contents->SetPath(path); contents->SetColor(Color::Red()); contents->SetStrokeSize(width); contents->SetStrokeCap(cap); @@ -131,16 +132,15 @@ TEST_P(EntityTest, StrokeCapAndJoinTest) { contents->SetStrokeMiter(miter_limit); Entity entity; - entity.SetPath(path); entity.SetContents(std::move(contents)); auto coverage = entity.GetCoverage(); if (coverage.has_value()) { auto bounds_contents = std::make_unique(); + bounds_contents->SetPath( + PathBuilder{}.AddRect(entity.GetCoverage().value()).TakePath()); bounds_contents->SetColor(Color::Green().WithAlpha(0.5)); Entity bounds_entity; - bounds_entity.SetPath( - PathBuilder{}.AddRect(entity.GetCoverage().value()).TakePath()); bounds_entity.SetContents(std::move(bounds_contents)); bounds_entity.Render(context, pass); } @@ -242,8 +242,7 @@ TEST_P(EntityTest, CubicCurveTest) { .Close() .TakePath(); Entity entity; - entity.SetPath(path); - entity.SetContents(SolidColorContents::Make(Color::Red())); + entity.SetContents(SolidColorContents::Make(path, Color::Red())); ASSERT_TRUE(OpenPlaygroundHere(entity)); } @@ -470,8 +469,7 @@ TEST_P(EntityTest, CubicCurveAndOverlapTest) { .Close() .TakePath(); Entity entity; - entity.SetPath(path); - entity.SetContents(SolidColorContents::Make(Color::Red())); + entity.SetContents(SolidColorContents::Make(path, Color::Red())); ASSERT_TRUE(OpenPlaygroundHere(entity)); } @@ -656,10 +654,9 @@ TEST_P(EntityTest, BezierCircleScaled) { {97.32499434685802, 34.81799797758954}) .Close() .TakePath(); - entity.SetPath(path); entity.SetTransformation( Matrix::MakeScale({20.0, 20.0, 1.0}).Translate({-80, -15, 0})); - entity.SetContents(SolidColorContents::Make(Color::Red())); + entity.SetContents(SolidColorContents::Make(path, Color::Red())); ASSERT_TRUE(OpenPlaygroundHere(entity)); } @@ -682,7 +679,8 @@ TEST_P(EntityTest, Filters) { {fi_bridge, FilterInput::Make(blend0), fi_bridge, fi_bridge}); Entity entity; - entity.SetPath(PathBuilder{}.AddRect({100, 100, 300, 300}).TakePath()); + entity.SetTransformation(Matrix::MakeTranslation({500, 300}) * + Matrix::MakeScale(Vector2{0.5, 0.5})); entity.SetContents(blend1); return entity.Render(context, pass); }; @@ -717,7 +715,7 @@ TEST_P(EntityTest, GaussianBlurFilter) { static Color bounds_color(0, 1, 0, 0.1); static float offset[2] = {500, 400}; static float rotation = 0; - static float scale[2] = {0.8, 0.8}; + static float scale[2] = {0.5, 0.5}; static float skew[2] = {0, 0}; ImGui::Begin("Controls"); @@ -752,8 +750,6 @@ TEST_P(EntityTest, GaussianBlurFilter) { FilterContents::Sigma{blur_amount[1]}, blur_styles[selected_blur_style]); - ISize input_size = boston->GetSize(); - auto rect = Rect(-Point(input_size) / 2, Size(input_size)); auto ctm = Matrix::MakeTranslation(Vector3(offset[0], offset[1])) * Matrix::MakeRotationZ(Radians(rotation)) * Matrix::MakeScale(Vector2(scale[0], scale[1])) * @@ -762,7 +758,6 @@ TEST_P(EntityTest, GaussianBlurFilter) { auto target_contents = selected_blur_type == 0 ? blur : mask_blur; Entity entity; - entity.SetPath(PathBuilder{}.AddRect(rect).TakePath()); entity.SetContents(target_contents); entity.SetTransformation(ctm); @@ -771,19 +766,23 @@ TEST_P(EntityTest, GaussianBlurFilter) { // Renders a red "cover" rectangle that shows the original position of the // unfiltered input. Entity cover_entity; - cover_entity.SetPath(PathBuilder{}.AddRect(rect).TakePath()); - cover_entity.SetContents(SolidColorContents::Make(cover_color)); + cover_entity.SetContents( + SolidColorContents::Make(PathBuilder{} + .AddRect(Rect(-Point(bridge->GetSize()) / 2, + Size(bridge->GetSize()))) + .TakePath(), + cover_color)); cover_entity.SetTransformation(ctm); cover_entity.Render(context, pass); // Renders a green bounding rect of the target filter. Entity bounds_entity; - bounds_entity.SetPath( + bounds_entity.SetContents(SolidColorContents::Make( PathBuilder{} .AddRect(target_contents->GetCoverage(entity).value()) - .TakePath()); - bounds_entity.SetContents(SolidColorContents::Make(bounds_color)); + .TakePath(), + bounds_color)); bounds_entity.SetTransformation(Matrix()); bounds_entity.Render(context, pass); @@ -803,19 +802,17 @@ TEST_P(EntityTest, SetBlendMode) { TEST_P(EntityTest, ContentsGetBoundsForEmptyPathReturnsNullopt) { Entity entity; entity.SetContents(std::make_shared()); - entity.SetPath({}); ASSERT_FALSE(entity.GetCoverage().has_value()); - ASSERT_FALSE(entity.GetPathCoverage().has_value()); } TEST_P(EntityTest, SolidStrokeCoverageIsCorrect) { { Entity entity; auto contents = std::make_unique(); + contents->SetPath(PathBuilder{}.AddLine({0, 0}, {10, 10}).TakePath()); contents->SetStrokeCap(SolidStrokeContents::Cap::kButt); contents->SetStrokeJoin(SolidStrokeContents::Join::kBevel); contents->SetStrokeSize(4); - entity.SetPath(PathBuilder{}.AddLine({0, 0}, {10, 10}).TakePath()); entity.SetContents(std::move(contents)); auto actual = entity.GetCoverage(); auto expected = Rect::MakeLTRB(-2, -2, 12, 12); @@ -827,10 +824,10 @@ TEST_P(EntityTest, SolidStrokeCoverageIsCorrect) { { Entity entity; auto contents = std::make_unique(); + contents->SetPath(PathBuilder{}.AddLine({0, 0}, {10, 10}).TakePath()); contents->SetStrokeCap(SolidStrokeContents::Cap::kSquare); contents->SetStrokeJoin(SolidStrokeContents::Join::kBevel); contents->SetStrokeSize(4); - entity.SetPath(PathBuilder{}.AddLine({0, 0}, {10, 10}).TakePath()); entity.SetContents(std::move(contents)); auto actual = entity.GetCoverage(); auto expected = @@ -843,11 +840,11 @@ TEST_P(EntityTest, SolidStrokeCoverageIsCorrect) { { Entity entity; auto contents = std::make_unique(); + contents->SetPath(PathBuilder{}.AddLine({0, 0}, {10, 10}).TakePath()); contents->SetStrokeCap(SolidStrokeContents::Cap::kSquare); contents->SetStrokeJoin(SolidStrokeContents::Join::kMiter); contents->SetStrokeSize(4); contents->SetStrokeMiter(2); - entity.SetPath(PathBuilder{}.AddLine({0, 0}, {10, 10}).TakePath()); entity.SetContents(std::move(contents)); auto actual = entity.GetCoverage(); auto expected = Rect::MakeLTRB(-4, -4, 14, 14); diff --git a/impeller/geometry/path.cc b/impeller/geometry/path.cc index b71996e76d02f..9e6c52e0ab7a8 100644 --- a/impeller/geometry/path.cc +++ b/impeller/geometry/path.cc @@ -286,6 +286,14 @@ std::optional Path::GetBoundingBox() const { return Rect{min.x, min.y, difference.x, difference.y}; } +std::optional Path::GetTransformedBoundingBox(const Matrix& transform) const { + auto bounds = GetBoundingBox(); + if (!bounds.has_value()) { + return std::nullopt; + } + return bounds->TransformBounds(transform); +} + std::optional> Path::GetMinMaxCoveragePoints() const { if (linears_.empty() && quads_.empty() && cubics_.empty()) { return std::nullopt; diff --git a/impeller/geometry/path.h b/impeller/geometry/path.h index 7add50e1783c3..2faa4e5241d52 100644 --- a/impeller/geometry/path.h +++ b/impeller/geometry/path.h @@ -121,6 +121,8 @@ class Path { std::optional GetBoundingBox() const; + std::optional GetTransformedBoundingBox(const Matrix& transform) const; + std::optional> GetMinMaxCoveragePoints() const; private: From 73900c02f4c55ee29bb77da597a29f36a5d91236 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 21 Apr 2022 16:59:57 -0700 Subject: [PATCH 420/433] Bump actions/checkout from 3.0.0 to 3.0.2 (#151) Bumps [actions/checkout](https://github.com/actions/checkout) from 3.0.0 to 3.0.2. - [Release notes](https://github.com/actions/checkout/releases) - [Changelog](https://github.com/actions/checkout/blob/main/CHANGELOG.md) - [Commits](https://github.com/actions/checkout/compare/a12a3943b4bdde767164f792f33f40b04645d846...2541b1294d2704b0964813337f33b291d3f8596b) --- updated-dependencies: - dependency-name: actions/checkout dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- impeller/.github/workflows/scorecards-analysis.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/impeller/.github/workflows/scorecards-analysis.yml b/impeller/.github/workflows/scorecards-analysis.yml index 92f3bc928f368..40a8d9ac33263 100644 --- a/impeller/.github/workflows/scorecards-analysis.yml +++ b/impeller/.github/workflows/scorecards-analysis.yml @@ -20,7 +20,7 @@ jobs: steps: - name: "Checkout code" - uses: actions/checkout@a12a3943b4bdde767164f792f33f40b04645d846 + uses: actions/checkout@2541b1294d2704b0964813337f33b291d3f8596b with: persist-credentials: false From 057e57dab892ed2ec75993c97912eb2ded6ca787 Mon Sep 17 00:00:00 2001 From: Dan Field Date: Thu, 21 Apr 2022 19:23:28 -0700 Subject: [PATCH 421/433] Remove dependency on Dart, add crash handler to impellerc (#150) --- impeller/BUILD.gn | 4 ---- impeller/blobcat/BUILD.gn | 4 ---- impeller/compiler/BUILD.gn | 4 ---- impeller/compiler/impellerc_main.cc | 2 ++ 4 files changed, 2 insertions(+), 12 deletions(-) diff --git a/impeller/BUILD.gn b/impeller/BUILD.gn index 11c78699bda27..46bd75f3f4e2e 100644 --- a/impeller/BUILD.gn +++ b/impeller/BUILD.gn @@ -55,10 +55,6 @@ executable("impeller_unittests") { "blobcat:blobcat_unittests", "compiler:compiler_unittests", "geometry:geometry_unittests", - - # FML depends on the Dart VM for tracing and getting the current - # timepoint. - "//flutter/runtime:libdart", ] if (impeller_supports_rendering) { diff --git a/impeller/blobcat/BUILD.gn b/impeller/blobcat/BUILD.gn index 4899e5f9708d2..b38d29bdcfe9d 100644 --- a/impeller/blobcat/BUILD.gn +++ b/impeller/blobcat/BUILD.gn @@ -29,10 +29,6 @@ impeller_component("blobcat") { ":blobcat_lib", "../base", "//flutter/fml", - - # FML depends on the Dart VM for tracing and getting the current - # timepoint. - "//flutter/runtime:libdart", ] } diff --git a/impeller/compiler/BUILD.gn b/impeller/compiler/BUILD.gn index 249944ca64abc..501a8bb5efd27 100644 --- a/impeller/compiler/BUILD.gn +++ b/impeller/compiler/BUILD.gn @@ -44,10 +44,6 @@ impeller_component("impellerc") { deps = [ ":compiler_lib", - - # FML depends on the Dart VM for tracing and getting the current - # timepoint. - "//flutter/runtime:libdart", ] } diff --git a/impeller/compiler/impellerc_main.cc b/impeller/compiler/impellerc_main.cc index 2ce1b4ac6b61e..67d8616f896e0 100644 --- a/impeller/compiler/impellerc_main.cc +++ b/impeller/compiler/impellerc_main.cc @@ -4,6 +4,7 @@ #include +#include "flutter/fml/backtrace.h" #include "flutter/fml/command_line.h" #include "flutter/fml/file.h" #include "flutter/fml/macros.h" @@ -18,6 +19,7 @@ namespace impeller { namespace compiler { bool Main(const fml::CommandLine& command_line) { + fml::InstallCrashHandler(); if (command_line.HasOption("help")) { Switches::PrintHelp(std::cout); return true; From 615de639cd17c68743e6f8f9e87b883b2b20e958 Mon Sep 17 00:00:00 2001 From: JsouLiang Date: Sat, 23 Apr 2022 05:28:01 +0800 Subject: [PATCH 422/433] Use New DlPathEffect Object (#98) --- impeller/display_list/display_list_dispatcher.cc | 2 +- impeller/display_list/display_list_dispatcher.h | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/impeller/display_list/display_list_dispatcher.cc b/impeller/display_list/display_list_dispatcher.cc index fc8142dcf16ec..972ed22ee45c3 100644 --- a/impeller/display_list/display_list_dispatcher.cc +++ b/impeller/display_list/display_list_dispatcher.cc @@ -249,7 +249,7 @@ void DisplayListDispatcher::setBlender(sk_sp blender) { } // |flutter::Dispatcher| -void DisplayListDispatcher::setPathEffect(sk_sp effect) { +void DisplayListDispatcher::setPathEffect(const flutter::DlPathEffect* effect) { // Needs https://github.com/flutter/flutter/issues/95434 UNIMPLEMENTED; } diff --git a/impeller/display_list/display_list_dispatcher.h b/impeller/display_list/display_list_dispatcher.h index 28dc72f0947a0..ad9b7d2723984 100644 --- a/impeller/display_list/display_list_dispatcher.h +++ b/impeller/display_list/display_list_dispatcher.h @@ -61,7 +61,7 @@ class DisplayListDispatcher final : public flutter::Dispatcher { void setBlender(sk_sp blender) override; // |flutter::Dispatcher| - void setPathEffect(sk_sp effect) override; + void setPathEffect(const flutter::DlPathEffect* effect) override; // |flutter::Dispatcher| void setMaskFilter(const flutter::DlMaskFilter* filter) override; From 59f264cc53f40538fca966f051d2f4f41bca435d Mon Sep 17 00:00:00 2001 From: Jim Graham Date: Fri, 22 Apr 2022 14:45:52 -0700 Subject: [PATCH 423/433] implement drawDisplayList in impeller dispatcher (#153) --- impeller/display_list/display_list_dispatcher.cc | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/impeller/display_list/display_list_dispatcher.cc b/impeller/display_list/display_list_dispatcher.cc index 972ed22ee45c3..57e1246c5e910 100644 --- a/impeller/display_list/display_list_dispatcher.cc +++ b/impeller/display_list/display_list_dispatcher.cc @@ -703,7 +703,12 @@ void DisplayListDispatcher::drawPicture(const sk_sp picture, // |flutter::Dispatcher| void DisplayListDispatcher::drawDisplayList( const sk_sp display_list) { - UNIMPLEMENTED; + int saveCount = canvas_.GetSaveCount(); + Paint savePaint = paint_; + paint_ = Paint(); + display_list->Dispatch(*this); + paint_ = savePaint; + canvas_.RestoreToCount(saveCount); } // |flutter::Dispatcher| From 471ca0cff86d16854f6016bc1040f10307be27cf Mon Sep 17 00:00:00 2001 From: Jim Graham Date: Fri, 22 Apr 2022 16:06:45 -0700 Subject: [PATCH 424/433] Revert "Use New DlPathEffect Object (#98)" (#154) This reverts commit 615de639cd17c68743e6f8f9e87b883b2b20e958. --- impeller/display_list/display_list_dispatcher.cc | 2 +- impeller/display_list/display_list_dispatcher.h | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/impeller/display_list/display_list_dispatcher.cc b/impeller/display_list/display_list_dispatcher.cc index 57e1246c5e910..25f4b7ab5f340 100644 --- a/impeller/display_list/display_list_dispatcher.cc +++ b/impeller/display_list/display_list_dispatcher.cc @@ -249,7 +249,7 @@ void DisplayListDispatcher::setBlender(sk_sp blender) { } // |flutter::Dispatcher| -void DisplayListDispatcher::setPathEffect(const flutter::DlPathEffect* effect) { +void DisplayListDispatcher::setPathEffect(sk_sp effect) { // Needs https://github.com/flutter/flutter/issues/95434 UNIMPLEMENTED; } diff --git a/impeller/display_list/display_list_dispatcher.h b/impeller/display_list/display_list_dispatcher.h index ad9b7d2723984..28dc72f0947a0 100644 --- a/impeller/display_list/display_list_dispatcher.h +++ b/impeller/display_list/display_list_dispatcher.h @@ -61,7 +61,7 @@ class DisplayListDispatcher final : public flutter::Dispatcher { void setBlender(sk_sp blender) override; // |flutter::Dispatcher| - void setPathEffect(const flutter::DlPathEffect* effect) override; + void setPathEffect(sk_sp effect) override; // |flutter::Dispatcher| void setMaskFilter(const flutter::DlMaskFilter* filter) override; From 0586ecbb853bd0b1f937b2ed6bc7185583a14921 Mon Sep 17 00:00:00 2001 From: Brandon DeRosier Date: Sun, 24 Apr 2022 16:31:41 -0700 Subject: [PATCH 425/433] Separate FilterInput variants into their own TUs (#152) --- impeller/entity/BUILD.gn | 14 +- .../contents/filters/blend_filter_contents.cc | 2 +- .../contents/filters/blend_filter_contents.h | 2 +- .../border_mask_blur_filter_contents.h | 2 +- .../contents/filters/filter_contents.cc | 2 +- .../entity/contents/filters/filter_contents.h | 4 +- .../entity/contents/filters/filter_input.cc | 158 ------------------ .../entity/contents/filters/filter_input.h | 153 ----------------- .../filters/gaussian_blur_filter_contents.h | 2 +- .../filters/inputs/contents_filter_input.cc | 32 ++++ .../filters/inputs/contents_filter_input.h | 34 ++++ .../inputs/filter_contents_filter_input.cc | 44 +++++ .../inputs/filter_contents_filter_input.h | 40 +++++ .../contents/filters/inputs/filter_input.cc | 55 ++++++ .../contents/filters/inputs/filter_input.h | 59 +++++++ .../filters/inputs/texture_filter_input.cc | 35 ++++ .../filters/inputs/texture_filter_input.h | 36 ++++ impeller/entity/entity_unittests.cc | 2 +- 18 files changed, 352 insertions(+), 324 deletions(-) delete mode 100644 impeller/entity/contents/filters/filter_input.cc delete mode 100644 impeller/entity/contents/filters/filter_input.h create mode 100644 impeller/entity/contents/filters/inputs/contents_filter_input.cc create mode 100644 impeller/entity/contents/filters/inputs/contents_filter_input.h create mode 100644 impeller/entity/contents/filters/inputs/filter_contents_filter_input.cc create mode 100644 impeller/entity/contents/filters/inputs/filter_contents_filter_input.h create mode 100644 impeller/entity/contents/filters/inputs/filter_input.cc create mode 100644 impeller/entity/contents/filters/inputs/filter_input.h create mode 100644 impeller/entity/contents/filters/inputs/texture_filter_input.cc create mode 100644 impeller/entity/contents/filters/inputs/texture_filter_input.h diff --git a/impeller/entity/BUILD.gn b/impeller/entity/BUILD.gn index 4f942c39a2c3e..ec4dd55104732 100644 --- a/impeller/entity/BUILD.gn +++ b/impeller/entity/BUILD.gn @@ -39,14 +39,20 @@ impeller_component("entity") { "contents/contents.h", "contents/filters/blend_filter_contents.cc", "contents/filters/blend_filter_contents.h", + "contents/filters/border_mask_blur_filter_contents.cc", + "contents/filters/border_mask_blur_filter_contents.h", "contents/filters/filter_contents.cc", "contents/filters/filter_contents.h", - "contents/filters/filter_input.cc", - "contents/filters/filter_input.h", "contents/filters/gaussian_blur_filter_contents.cc", "contents/filters/gaussian_blur_filter_contents.h", - "contents/filters/border_mask_blur_filter_contents.cc", - "contents/filters/border_mask_blur_filter_contents.h", + "contents/filters/inputs/contents_filter_input.cc", + "contents/filters/inputs/contents_filter_input.h", + "contents/filters/inputs/filter_contents_filter_input.cc", + "contents/filters/inputs/filter_contents_filter_input.h", + "contents/filters/inputs/filter_input.cc", + "contents/filters/inputs/filter_input.h", + "contents/filters/inputs/texture_filter_input.cc", + "contents/filters/inputs/texture_filter_input.h", "contents/linear_gradient_contents.cc", "contents/linear_gradient_contents.h", "contents/snapshot.cc", diff --git a/impeller/entity/contents/filters/blend_filter_contents.cc b/impeller/entity/contents/filters/blend_filter_contents.cc index 05a744a422d9f..5ff7e746770b1 100644 --- a/impeller/entity/contents/filters/blend_filter_contents.cc +++ b/impeller/entity/contents/filters/blend_filter_contents.cc @@ -5,7 +5,7 @@ #include "impeller/entity/contents/filters/blend_filter_contents.h" #include "impeller/entity/contents/content_context.h" -#include "impeller/entity/contents/filters/filter_input.h" +#include "impeller/entity/contents/filters/inputs/filter_input.h" #include "impeller/renderer/render_pass.h" #include "impeller/renderer/sampler_library.h" diff --git a/impeller/entity/contents/filters/blend_filter_contents.h b/impeller/entity/contents/filters/blend_filter_contents.h index 9a10a933560bd..8bc833878cf3a 100644 --- a/impeller/entity/contents/filters/blend_filter_contents.h +++ b/impeller/entity/contents/filters/blend_filter_contents.h @@ -5,7 +5,7 @@ #pragma once #include "impeller/entity/contents/filters/filter_contents.h" -#include "impeller/entity/contents/filters/filter_input.h" +#include "impeller/entity/contents/filters/inputs/filter_input.h" namespace impeller { diff --git a/impeller/entity/contents/filters/border_mask_blur_filter_contents.h b/impeller/entity/contents/filters/border_mask_blur_filter_contents.h index 335edd797e117..6aa95d87046e4 100644 --- a/impeller/entity/contents/filters/border_mask_blur_filter_contents.h +++ b/impeller/entity/contents/filters/border_mask_blur_filter_contents.h @@ -7,7 +7,7 @@ #include #include #include "impeller/entity/contents/filters/filter_contents.h" -#include "impeller/entity/contents/filters/filter_input.h" +#include "impeller/entity/contents/filters/inputs/filter_input.h" namespace impeller { diff --git a/impeller/entity/contents/filters/filter_contents.cc b/impeller/entity/contents/filters/filter_contents.cc index a054aa80383a0..3196c42678561 100644 --- a/impeller/entity/contents/filters/filter_contents.cc +++ b/impeller/entity/contents/filters/filter_contents.cc @@ -16,7 +16,7 @@ #include "impeller/entity/contents/content_context.h" #include "impeller/entity/contents/filters/blend_filter_contents.h" #include "impeller/entity/contents/filters/border_mask_blur_filter_contents.h" -#include "impeller/entity/contents/filters/filter_input.h" +#include "impeller/entity/contents/filters/inputs/filter_input.h" #include "impeller/entity/contents/filters/gaussian_blur_filter_contents.h" #include "impeller/entity/contents/texture_contents.h" #include "impeller/entity/entity.h" diff --git a/impeller/entity/contents/filters/filter_contents.h b/impeller/entity/contents/filters/filter_contents.h index 6f6ab02c23425..fff4133fd5a3a 100644 --- a/impeller/entity/contents/filters/filter_contents.h +++ b/impeller/entity/contents/filters/filter_contents.h @@ -8,7 +8,7 @@ #include #include -#include "impeller/entity/contents/filters/filter_input.h" +#include "impeller/entity/contents/filters/inputs/filter_input.h" #include "impeller/entity/entity.h" #include "impeller/renderer/formats.h" @@ -145,8 +145,6 @@ class FilterContents : public Contents { FilterInput::Vector inputs_; FML_DISALLOW_COPY_AND_ASSIGN(FilterContents); - - friend FilterContentsFilterInput; }; } // namespace impeller diff --git a/impeller/entity/contents/filters/filter_input.cc b/impeller/entity/contents/filters/filter_input.cc deleted file mode 100644 index 6719886467e0c..0000000000000 --- a/impeller/entity/contents/filters/filter_input.cc +++ /dev/null @@ -1,158 +0,0 @@ -// Copyright 2013 The Flutter Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#include "impeller/entity/contents/filters/filter_input.h" - -#include -#include -#include -#include -#include - -#include "fml/logging.h" -#include "impeller/entity/contents/filters/filter_contents.h" -#include "impeller/entity/contents/snapshot.h" -#include "impeller/entity/entity.h" - -namespace impeller { - -/******************************************************************************* - ******* FilterInput - ******************************************************************************/ - -FilterInput::Ref FilterInput::Make(Variant input) { - if (auto filter = std::get_if>(&input)) { - return std::static_pointer_cast( - std::shared_ptr( - new FilterContentsFilterInput(*filter))); - } - - if (auto contents = std::get_if>(&input)) { - return std::static_pointer_cast( - std::shared_ptr( - new ContentsFilterInput(*contents))); - } - - if (auto texture = std::get_if>(&input)) { - return std::static_pointer_cast( - std::shared_ptr(new TextureFilterInput(*texture))); - } - - FML_UNREACHABLE(); -} - -FilterInput::Vector FilterInput::Make(std::initializer_list inputs) { - FilterInput::Vector result; - result.reserve(inputs.size()); - for (const auto& input : inputs) { - result.push_back(Make(input)); - } - return result; -} - -Matrix FilterInput::GetLocalTransform(const Entity& entity) const { - return Matrix(); -} - -Matrix FilterInput::GetTransform(const Entity& entity) const { - return entity.GetTransformation() * GetLocalTransform(entity); -} - -FilterInput::~FilterInput() = default; - -/******************************************************************************* - ******* FilterContentsFilterInput - ******************************************************************************/ - -FilterContentsFilterInput::FilterContentsFilterInput( - std::shared_ptr filter) - : filter_(filter) {} - -FilterContentsFilterInput::~FilterContentsFilterInput() = default; - -FilterInput::Variant FilterContentsFilterInput::GetInput() const { - return filter_; -} - -std::optional FilterContentsFilterInput::GetSnapshot( - const ContentContext& renderer, - const Entity& entity) const { - if (!snapshot_.has_value()) { - snapshot_ = filter_->RenderToSnapshot(renderer, entity); - } - return snapshot_; -} - -std::optional FilterContentsFilterInput::GetCoverage( - const Entity& entity) const { - return filter_->GetCoverage(entity); -} - -Matrix FilterContentsFilterInput::GetLocalTransform( - const Entity& entity) const { - return filter_->GetLocalTransform(); -} - -Matrix FilterContentsFilterInput::GetTransform(const Entity& entity) const { - return filter_->GetTransform(entity.GetTransformation()); -} - -/******************************************************************************* - ******* ContentsFilterInput - ******************************************************************************/ - -ContentsFilterInput::ContentsFilterInput(std::shared_ptr contents) - : contents_(contents) {} - -ContentsFilterInput::~ContentsFilterInput() = default; - -FilterInput::Variant ContentsFilterInput::GetInput() const { - return contents_; -} - -std::optional ContentsFilterInput::GetSnapshot( - const ContentContext& renderer, - const Entity& entity) const { - if (!snapshot_.has_value()) { - snapshot_ = contents_->RenderToSnapshot(renderer, entity); - } - return snapshot_; -} - -std::optional ContentsFilterInput::GetCoverage( - const Entity& entity) const { - return contents_->GetCoverage(entity); -} - -/******************************************************************************* - ******* TextureFilterInput - ******************************************************************************/ - -TextureFilterInput::TextureFilterInput(std::shared_ptr texture) - : texture_(texture) {} - -TextureFilterInput::~TextureFilterInput() = default; - -FilterInput::Variant TextureFilterInput::GetInput() const { - return texture_; -} - -std::optional TextureFilterInput::GetSnapshot( - const ContentContext& renderer, - const Entity& entity) const { - return Snapshot{.texture = texture_, .transform = GetTransform(entity)}; -} - -std::optional TextureFilterInput::GetCoverage( - const Entity& entity) const { - return Rect::MakeSize(Size(texture_->GetSize())) - .TransformBounds(GetTransform(entity)); -} - -Matrix TextureFilterInput::GetLocalTransform(const Entity& entity) const { - // Compute the local transform such that the texture is centered. - return Matrix::MakeTranslation(-Point(texture_->GetSize()) / 2); -} - -} // namespace impeller diff --git a/impeller/entity/contents/filters/filter_input.h b/impeller/entity/contents/filters/filter_input.h deleted file mode 100644 index dd1f907a6df70..0000000000000 --- a/impeller/entity/contents/filters/filter_input.h +++ /dev/null @@ -1,153 +0,0 @@ -// Copyright 2013 The Flutter Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#pragma once - -#include -#include -#include -#include - -#include "impeller/entity/contents/contents.h" -#include "impeller/geometry/rect.h" -#include "impeller/renderer/formats.h" - -namespace impeller { - -class ContentContext; -class Entity; -class FilterContents; - -/******************************************************************************* - ******* FilterInput - ******************************************************************************/ - -/// `FilterInput` is a lazy/single eval `Snapshot` which may be shared across -/// filter parameters and used to evaluate input coverage. -/// -/// A `FilterInput` can be re-used for any filter inputs across an entity's -/// filter graph without repeating subpasses unnecessarily. -/// -/// Filters may decide to not evaluate inputs in situations where they won't -/// contribute to the filter's output texture. -class FilterInput { - public: - using Ref = std::shared_ptr; - using Vector = std::vector; - using Variant = std::variant, - std::shared_ptr, - std::shared_ptr>; - - virtual ~FilterInput(); - - static FilterInput::Ref Make(Variant input); - - static FilterInput::Vector Make(std::initializer_list inputs); - - virtual Variant GetInput() const = 0; - - virtual std::optional GetSnapshot(const ContentContext& renderer, - const Entity& entity) const = 0; - - virtual std::optional GetCoverage(const Entity& entity) const = 0; - - /// @brief Get the local transform of this filter input. This transform is - /// relative to the `Entity` transform space. - virtual Matrix GetLocalTransform(const Entity& entity) const; - - /// @brief Get the transform of this `FilterInput`. This is equivalent to - /// calling `entity.GetTransformation() * GetLocalTransform()`. - virtual Matrix GetTransform(const Entity& entity) const; -}; - -/******************************************************************************* - ******* FilterContentsFilterInput - ******************************************************************************/ - -class FilterContentsFilterInput final : public FilterInput { - public: - ~FilterContentsFilterInput() override; - - // |FilterInput| - Variant GetInput() const override; - - // |FilterInput| - std::optional GetSnapshot(const ContentContext& renderer, - const Entity& entity) const override; - - // |FilterInput| - std::optional GetCoverage(const Entity& entity) const override; - - // |FilterInput| - Matrix GetLocalTransform(const Entity& entity) const override; - - // |FilterInput| - Matrix GetTransform(const Entity& entity) const override; - - private: - FilterContentsFilterInput(std::shared_ptr filter); - - std::shared_ptr filter_; - mutable std::optional snapshot_; - - friend FilterInput; -}; - -/******************************************************************************* - ******* ContentsFilterInput - ******************************************************************************/ - -class ContentsFilterInput final : public FilterInput { - public: - ~ContentsFilterInput() override; - - // |FilterInput| - Variant GetInput() const override; - - // |FilterInput| - std::optional GetSnapshot(const ContentContext& renderer, - const Entity& entity) const override; - - // |FilterInput| - std::optional GetCoverage(const Entity& entity) const override; - - private: - ContentsFilterInput(std::shared_ptr contents); - - std::shared_ptr contents_; - mutable std::optional snapshot_; - - friend FilterInput; -}; - -/******************************************************************************* - ******* TextureFilterInput - ******************************************************************************/ - -class TextureFilterInput final : public FilterInput { - public: - ~TextureFilterInput() override; - - // |FilterInput| - Variant GetInput() const override; - - // |FilterInput| - std::optional GetSnapshot(const ContentContext& renderer, - const Entity& entity) const override; - - // |FilterInput| - std::optional GetCoverage(const Entity& entity) const override; - - // |FilterInput| - Matrix GetLocalTransform(const Entity& entity) const override; - - private: - TextureFilterInput(std::shared_ptr texture); - - std::shared_ptr texture_; - - friend FilterInput; -}; - -} // namespace impeller diff --git a/impeller/entity/contents/filters/gaussian_blur_filter_contents.h b/impeller/entity/contents/filters/gaussian_blur_filter_contents.h index 6b95d52a7a228..e85ad20dc5f0c 100644 --- a/impeller/entity/contents/filters/gaussian_blur_filter_contents.h +++ b/impeller/entity/contents/filters/gaussian_blur_filter_contents.h @@ -7,7 +7,7 @@ #include #include #include "impeller/entity/contents/filters/filter_contents.h" -#include "impeller/entity/contents/filters/filter_input.h" +#include "impeller/entity/contents/filters/inputs/filter_input.h" namespace impeller { diff --git a/impeller/entity/contents/filters/inputs/contents_filter_input.cc b/impeller/entity/contents/filters/inputs/contents_filter_input.cc new file mode 100644 index 0000000000000..0d462f78de7f3 --- /dev/null +++ b/impeller/entity/contents/filters/inputs/contents_filter_input.cc @@ -0,0 +1,32 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "impeller/entity/contents/filters/inputs/contents_filter_input.h" + +namespace impeller { + +ContentsFilterInput::ContentsFilterInput(std::shared_ptr contents) + : contents_(contents) {} + +ContentsFilterInput::~ContentsFilterInput() = default; + +FilterInput::Variant ContentsFilterInput::GetInput() const { + return contents_; +} + +std::optional ContentsFilterInput::GetSnapshot( + const ContentContext& renderer, + const Entity& entity) const { + if (!snapshot_.has_value()) { + snapshot_ = contents_->RenderToSnapshot(renderer, entity); + } + return snapshot_; +} + +std::optional ContentsFilterInput::GetCoverage( + const Entity& entity) const { + return contents_->GetCoverage(entity); +} + +} // namespace impeller diff --git a/impeller/entity/contents/filters/inputs/contents_filter_input.h b/impeller/entity/contents/filters/inputs/contents_filter_input.h new file mode 100644 index 0000000000000..7309c17ffa5f0 --- /dev/null +++ b/impeller/entity/contents/filters/inputs/contents_filter_input.h @@ -0,0 +1,34 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#pragma once + +#include "impeller/entity/contents/filters/inputs/filter_input.h" + +namespace impeller { + +class ContentsFilterInput final : public FilterInput { + public: + ~ContentsFilterInput() override; + + // |FilterInput| + Variant GetInput() const override; + + // |FilterInput| + std::optional GetSnapshot(const ContentContext& renderer, + const Entity& entity) const override; + + // |FilterInput| + std::optional GetCoverage(const Entity& entity) const override; + + private: + ContentsFilterInput(std::shared_ptr contents); + + std::shared_ptr contents_; + mutable std::optional snapshot_; + + friend FilterInput; +}; + +} // namespace impeller diff --git a/impeller/entity/contents/filters/inputs/filter_contents_filter_input.cc b/impeller/entity/contents/filters/inputs/filter_contents_filter_input.cc new file mode 100644 index 0000000000000..bbeb3842f476c --- /dev/null +++ b/impeller/entity/contents/filters/inputs/filter_contents_filter_input.cc @@ -0,0 +1,44 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "impeller/entity/contents/filters/inputs/filter_contents_filter_input.h" + +#include "impeller/entity/contents/filters/filter_contents.h" + +namespace impeller { + +FilterContentsFilterInput::FilterContentsFilterInput( + std::shared_ptr filter) + : filter_(filter) {} + +FilterContentsFilterInput::~FilterContentsFilterInput() = default; + +FilterInput::Variant FilterContentsFilterInput::GetInput() const { + return filter_; +} + +std::optional FilterContentsFilterInput::GetSnapshot( + const ContentContext& renderer, + const Entity& entity) const { + if (!snapshot_.has_value()) { + snapshot_ = filter_->RenderToSnapshot(renderer, entity); + } + return snapshot_; +} + +std::optional FilterContentsFilterInput::GetCoverage( + const Entity& entity) const { + return filter_->GetCoverage(entity); +} + +Matrix FilterContentsFilterInput::GetLocalTransform( + const Entity& entity) const { + return filter_->GetLocalTransform(); +} + +Matrix FilterContentsFilterInput::GetTransform(const Entity& entity) const { + return filter_->GetTransform(entity.GetTransformation()); +} + +} // namespace impeller diff --git a/impeller/entity/contents/filters/inputs/filter_contents_filter_input.h b/impeller/entity/contents/filters/inputs/filter_contents_filter_input.h new file mode 100644 index 0000000000000..9562484a8f07a --- /dev/null +++ b/impeller/entity/contents/filters/inputs/filter_contents_filter_input.h @@ -0,0 +1,40 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#pragma once + +#include "impeller/entity/contents/filters/inputs/filter_input.h" + +namespace impeller { + +class FilterContentsFilterInput final : public FilterInput { + public: + ~FilterContentsFilterInput() override; + + // |FilterInput| + Variant GetInput() const override; + + // |FilterInput| + std::optional GetSnapshot(const ContentContext& renderer, + const Entity& entity) const override; + + // |FilterInput| + std::optional GetCoverage(const Entity& entity) const override; + + // |FilterInput| + Matrix GetLocalTransform(const Entity& entity) const override; + + // |FilterInput| + Matrix GetTransform(const Entity& entity) const override; + + private: + FilterContentsFilterInput(std::shared_ptr filter); + + std::shared_ptr filter_; + mutable std::optional snapshot_; + + friend FilterInput; +}; + +} // namespace impeller diff --git a/impeller/entity/contents/filters/inputs/filter_input.cc b/impeller/entity/contents/filters/inputs/filter_input.cc new file mode 100644 index 0000000000000..f69986c8b8217 --- /dev/null +++ b/impeller/entity/contents/filters/inputs/filter_input.cc @@ -0,0 +1,55 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "impeller/entity/contents/filters/inputs/filter_input.h" + +#include "flutter/fml/logging.h" +#include "impeller/entity/contents/filters/filter_contents.h" +#include "impeller/entity/contents/filters/inputs/contents_filter_input.h" +#include "impeller/entity/contents/filters/inputs/filter_contents_filter_input.h" +#include "impeller/entity/contents/filters/inputs/texture_filter_input.h" + +namespace impeller { + +FilterInput::Ref FilterInput::Make(Variant input) { + if (auto filter = std::get_if>(&input)) { + return std::static_pointer_cast( + std::shared_ptr( + new FilterContentsFilterInput(*filter))); + } + + if (auto contents = std::get_if>(&input)) { + return std::static_pointer_cast( + std::shared_ptr( + new ContentsFilterInput(*contents))); + } + + if (auto texture = std::get_if>(&input)) { + return std::static_pointer_cast( + std::shared_ptr(new TextureFilterInput(*texture))); + } + + FML_UNREACHABLE(); +} + +FilterInput::Vector FilterInput::Make(std::initializer_list inputs) { + FilterInput::Vector result; + result.reserve(inputs.size()); + for (const auto& input : inputs) { + result.push_back(Make(input)); + } + return result; +} + +Matrix FilterInput::GetLocalTransform(const Entity& entity) const { + return Matrix(); +} + +Matrix FilterInput::GetTransform(const Entity& entity) const { + return entity.GetTransformation() * GetLocalTransform(entity); +} + +FilterInput::~FilterInput() = default; + +} // namespace impeller diff --git a/impeller/entity/contents/filters/inputs/filter_input.h b/impeller/entity/contents/filters/inputs/filter_input.h new file mode 100644 index 0000000000000..6203a0d0245ce --- /dev/null +++ b/impeller/entity/contents/filters/inputs/filter_input.h @@ -0,0 +1,59 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#pragma once + +#include +#include +#include +#include + +#include "impeller/entity/contents/contents.h" +#include "impeller/geometry/rect.h" + +namespace impeller { + +class ContentContext; +class Entity; +class FilterContents; + +/// `FilterInput` is a lazy/single eval `Snapshot` which may be shared across +/// filter parameters and used to evaluate input coverage. +/// +/// A `FilterInput` can be re-used for any filter inputs across an entity's +/// filter graph without repeating subpasses unnecessarily. +/// +/// Filters may decide to not evaluate inputs in situations where they won't +/// contribute to the filter's output texture. +class FilterInput { + public: + using Ref = std::shared_ptr; + using Vector = std::vector; + using Variant = std::variant, + std::shared_ptr, + std::shared_ptr>; + + virtual ~FilterInput(); + + static FilterInput::Ref Make(Variant input); + + static FilterInput::Vector Make(std::initializer_list inputs); + + virtual Variant GetInput() const = 0; + + virtual std::optional GetSnapshot(const ContentContext& renderer, + const Entity& entity) const = 0; + + virtual std::optional GetCoverage(const Entity& entity) const = 0; + + /// @brief Get the local transform of this filter input. This transform is + /// relative to the `Entity` transform space. + virtual Matrix GetLocalTransform(const Entity& entity) const; + + /// @brief Get the transform of this `FilterInput`. This is equivalent to + /// calling `entity.GetTransformation() * GetLocalTransform()`. + virtual Matrix GetTransform(const Entity& entity) const; +}; + +} // namespace impeller diff --git a/impeller/entity/contents/filters/inputs/texture_filter_input.cc b/impeller/entity/contents/filters/inputs/texture_filter_input.cc new file mode 100644 index 0000000000000..5ee11edb20f89 --- /dev/null +++ b/impeller/entity/contents/filters/inputs/texture_filter_input.cc @@ -0,0 +1,35 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "impeller/entity/contents/filters/inputs/texture_filter_input.h" + +namespace impeller { + +TextureFilterInput::TextureFilterInput(std::shared_ptr texture) + : texture_(texture) {} + +TextureFilterInput::~TextureFilterInput() = default; + +FilterInput::Variant TextureFilterInput::GetInput() const { + return texture_; +} + +std::optional TextureFilterInput::GetSnapshot( + const ContentContext& renderer, + const Entity& entity) const { + return Snapshot{.texture = texture_, .transform = GetTransform(entity)}; +} + +std::optional TextureFilterInput::GetCoverage( + const Entity& entity) const { + return Rect::MakeSize(Size(texture_->GetSize())) + .TransformBounds(GetTransform(entity)); +} + +Matrix TextureFilterInput::GetLocalTransform(const Entity& entity) const { + // Compute the local transform such that the texture is centered. + return Matrix::MakeTranslation(-Point(texture_->GetSize()) / 2); +} + +} // namespace impeller diff --git a/impeller/entity/contents/filters/inputs/texture_filter_input.h b/impeller/entity/contents/filters/inputs/texture_filter_input.h new file mode 100644 index 0000000000000..24fc642a1d0f5 --- /dev/null +++ b/impeller/entity/contents/filters/inputs/texture_filter_input.h @@ -0,0 +1,36 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#pragma once + +#include "impeller/entity/contents/filters/inputs/filter_input.h" + +namespace impeller { + +class TextureFilterInput final : public FilterInput { + public: + ~TextureFilterInput() override; + + // |FilterInput| + Variant GetInput() const override; + + // |FilterInput| + std::optional GetSnapshot(const ContentContext& renderer, + const Entity& entity) const override; + + // |FilterInput| + std::optional GetCoverage(const Entity& entity) const override; + + // |FilterInput| + Matrix GetLocalTransform(const Entity& entity) const override; + + private: + TextureFilterInput(std::shared_ptr texture); + + std::shared_ptr texture_; + + friend FilterInput; +}; + +} // namespace impeller diff --git a/impeller/entity/entity_unittests.cc b/impeller/entity/entity_unittests.cc index ba154b2af9800..00fbb41989451 100644 --- a/impeller/entity/entity_unittests.cc +++ b/impeller/entity/entity_unittests.cc @@ -7,7 +7,7 @@ #include "flutter/testing/testing.h" #include "impeller/entity/contents/filters/blend_filter_contents.h" #include "impeller/entity/contents/filters/filter_contents.h" -#include "impeller/entity/contents/filters/filter_input.h" +#include "impeller/entity/contents/filters/inputs/filter_input.h" #include "impeller/entity/contents/solid_color_contents.h" #include "impeller/entity/contents/solid_stroke_contents.h" #include "impeller/entity/entity.h" From c720de55276d95076767e44b283a59db84daa581 Mon Sep 17 00:00:00 2001 From: Brandon DeRosier Date: Mon, 25 Apr 2022 12:23:38 -0700 Subject: [PATCH 426/433] Another coverage fix for border mask blur (#158) --- .../border_mask_blur_filter_contents.cc | 9 ++--- impeller/entity/entity_unittests.cc | 40 ++++++++++++++++--- 2 files changed, 37 insertions(+), 12 deletions(-) diff --git a/impeller/entity/contents/filters/border_mask_blur_filter_contents.cc b/impeller/entity/contents/filters/border_mask_blur_filter_contents.cc index a14e2da07f4cf..2901b76060bf1 100644 --- a/impeller/entity/contents/filters/border_mask_blur_filter_contents.cc +++ b/impeller/entity/contents/filters/border_mask_blur_filter_contents.cc @@ -117,13 +117,10 @@ std::optional BorderMaskBlurFilterContents::GetFilterCoverage( if (!coverage.has_value()) { return std::nullopt; } - + auto transform = inputs[0]->GetTransform(entity); auto transformed_blur_vector = - inputs[0] - ->GetTransform(entity) - .TransformDirection( - Vector2(Radius{sigma_x_}.radius, Radius{sigma_y_}.radius)) - .Abs(); + transform.TransformDirection(Vector2(Radius{sigma_x_}.radius, 0)).Abs() + + transform.TransformDirection(Vector2(0, Radius{sigma_y_}.radius)).Abs(); auto extent = coverage->size + transformed_blur_vector * 2; return Rect(coverage->origin - transformed_blur_vector, Size(extent.x, extent.y)); diff --git a/impeller/entity/entity_unittests.cc b/impeller/entity/entity_unittests.cc index 00fbb41989451..549e5b4fb4876 100644 --- a/impeller/entity/entity_unittests.cc +++ b/impeller/entity/entity_unittests.cc @@ -766,12 +766,12 @@ TEST_P(EntityTest, GaussianBlurFilter) { // Renders a red "cover" rectangle that shows the original position of the // unfiltered input. Entity cover_entity; - cover_entity.SetContents( - SolidColorContents::Make(PathBuilder{} - .AddRect(Rect(-Point(bridge->GetSize()) / 2, - Size(bridge->GetSize()))) - .TakePath(), - cover_color)); + cover_entity.SetContents(SolidColorContents::Make( + PathBuilder{} + .AddRect( + Rect(-Point(bridge->GetSize()) / 2, Size(bridge->GetSize()))) + .TakePath(), + cover_color)); cover_entity.SetTransformation(ctm); cover_entity.Render(context, pass); @@ -853,5 +853,33 @@ TEST_P(EntityTest, SolidStrokeCoverageIsCorrect) { } } +TEST_P(EntityTest, BorderMaskBlurCoverageIsCorrect) { + auto fill = std::make_shared(); + fill->SetPath( + PathBuilder{}.AddRect(Rect::MakeXYWH(0, 0, 300, 400)).TakePath()); + fill->SetColor(Color::CornflowerBlue()); + auto border_mask_blur = FilterContents::MakeBorderMaskBlur( + FilterInput::Make(fill), FilterContents::Radius{3}, + FilterContents::Radius{4}); + + { + Entity e; + e.SetTransformation(Matrix()); + auto actual = border_mask_blur->GetCoverage(e); + auto expected = Rect::MakeXYWH(-3, -4, 306, 408); + ASSERT_TRUE(actual.has_value()); + ASSERT_RECT_NEAR(actual.value(), expected); + } + + { + Entity e; + e.SetTransformation(Matrix::MakeRotationZ(Radians{kPi / 4})); + auto actual = border_mask_blur->GetCoverage(e); + auto expected = Rect::MakeXYWH(-287.792, -4.94975, 504.874, 504.874); + ASSERT_TRUE(actual.has_value()); + ASSERT_RECT_NEAR(actual.value(), expected); + } +} + } // namespace testing } // namespace impeller From 3c6e7a9fd0a0f1f8d6043e8d6e2e301a3271606d Mon Sep 17 00:00:00 2001 From: Chinmay Garde Date: Mon, 25 Apr 2022 14:06:34 -0700 Subject: [PATCH 427/433] Make it an error for a stage input to take more than one slot. (#161) Earlier, no PerVertexData struct would be generated. The shader is useless without reflection information. Fixes https://github.com/flutter/flutter/issues/102521. --- impeller/compiler/compiler_unittests.cc | 8 ++++++ impeller/compiler/reflector.cc | 13 ++++++++-- impeller/fixtures/BUILD.gn | 1 + impeller/fixtures/struct_def_bug.vert | 34 +++++++++++++++++++++++++ 4 files changed, 54 insertions(+), 2 deletions(-) create mode 100644 impeller/fixtures/struct_def_bug.vert diff --git a/impeller/compiler/compiler_unittests.cc b/impeller/compiler/compiler_unittests.cc index 6d598fa431229..5ac7495b09836 100644 --- a/impeller/compiler/compiler_unittests.cc +++ b/impeller/compiler/compiler_unittests.cc @@ -24,6 +24,14 @@ TEST_P(CompilerTest, CanCompile) { ASSERT_TRUE(CanCompileAndReflect("sample.vert")); } +TEST_P(CompilerTest, MustFailDueToMultipleLocationPerStructMember) { + if (GetParam() == TargetPlatform::kFlutterSPIRV) { + // This is a failure of reflection which this target doesn't perform. + GTEST_SKIP(); + } + ASSERT_FALSE(CanCompileAndReflect("struct_def_bug.vert")); +} + #define INSTANTIATE_TARGET_PLATFORM_TEST_SUITE_P(suite_name) \ INSTANTIATE_TEST_SUITE_P( \ suite_name, CompilerTest, \ diff --git a/impeller/compiler/reflector.cc b/impeller/compiler/reflector.cc index c3896aea55784..6fca48dbf763d 100644 --- a/impeller/compiler/reflector.cc +++ b/impeller/compiler/reflector.cc @@ -232,11 +232,16 @@ std::optional Reflector::GenerateTemplateArguments() const { auto& struct_definitions = root["struct_definitions"] = nlohmann::json::array_t{}; if (entrypoints.front().execution_model == - spv::ExecutionModel::ExecutionModelVertex) { + spv::ExecutionModel::ExecutionModelVertex && + !shader_resources.stage_inputs.empty()) { if (auto struc = ReflectPerVertexStructDefinition(shader_resources.stage_inputs); struc.has_value()) { struct_definitions.emplace_back(EmitStructDefinition(struc.value())); + } else { + // If there are stage inputs, it is an error to not generate a per + // vertex data struct for a vertex like shader stage. + return std::nullopt; } } @@ -662,7 +667,10 @@ Reflector::ReflectPerVertexStructDefinition( for (size_t i = 0; i < locations.size(); i++) { if (locations.count(i) != 1) { - // Locations are not contiguous. Bail. + // Locations are not contiguous. This usually happens when a single stage + // input takes multiple input slots. No reflection information can be + // generated for such cases anyway. So bail! It is up to the shader author + // to make sure one stage input maps to a single input slot. return std::nullopt; } } @@ -677,6 +685,7 @@ Reflector::ReflectPerVertexStructDefinition( } } // This really cannot happen with all the validation above. + FML_UNREACHABLE(); return nullptr; }; diff --git a/impeller/fixtures/BUILD.gn b/impeller/fixtures/BUILD.gn index d113b2ed4ff82..81c0bc60ca71e 100644 --- a/impeller/fixtures/BUILD.gn +++ b/impeller/fixtures/BUILD.gn @@ -27,6 +27,7 @@ test_fixtures("file_fixtures") { "embarcadero.jpg", "kalimba.jpg", "sample.vert", + "struct_def_bug.vert", "types.h", "test_texture.frag", "//flutter/third_party/txt/third_party/fonts/Roboto-Regular.ttf", diff --git a/impeller/fixtures/struct_def_bug.vert b/impeller/fixtures/struct_def_bug.vert new file mode 100644 index 0000000000000..800dc5358ef17 --- /dev/null +++ b/impeller/fixtures/struct_def_bug.vert @@ -0,0 +1,34 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +uniform FrameInfo { + mat4 mvp; + vec2 atlas_size; + vec4 text_color; +} frame_info; + +in vec2 unit_vertex; +in mat4 glyph_position; // <--- Causes multiple slots to be used and is a failure. +in vec2 glyph_size; +in vec2 atlas_position; +in vec2 atlas_glyph_size; + +out vec2 v_unit_vertex; +out vec2 v_atlas_position; +out vec2 v_atlas_glyph_size; +out vec2 v_atlas_size; +out vec4 v_text_color; + +void main() { + gl_Position = frame_info.mvp + * glyph_position + * vec4(unit_vertex.x * glyph_size.x, + unit_vertex.y * glyph_size.y, 0.0, 1.0); + + v_unit_vertex = unit_vertex; + v_atlas_position = atlas_position; + v_atlas_glyph_size = atlas_glyph_size; + v_atlas_size = frame_info.atlas_size; + v_text_color = frame_info.text_color; +} From ecdbd5e9524fa8e9885190b8f10e6d94cdae46a2 Mon Sep 17 00:00:00 2001 From: Dan Field Date: Mon, 25 Apr 2022 14:12:44 -0700 Subject: [PATCH 428/433] Avoid instancing/SSBOs for text to support GLES (#160) --- impeller/entity/contents/text_contents.cc | 76 +++++++------------ impeller/entity/shaders/glyph_atlas.vert | 59 +++++--------- .../backends/skia/text_frame_skia.cc | 3 +- impeller/typographer/text_frame.cc | 4 +- impeller/typographer/text_run.cc | 2 +- impeller/typographer/text_run.h | 6 +- 6 files changed, 52 insertions(+), 98 deletions(-) diff --git a/impeller/entity/contents/text_contents.cc b/impeller/entity/contents/text_contents.cc index f7a435cb913d1..17b5f9948ac6a 100644 --- a/impeller/entity/contents/text_contents.cc +++ b/impeller/entity/contents/text_contents.cc @@ -106,65 +106,41 @@ bool TextContents::Render(const ContentContext& renderer, // and the vertex shader uses this to size the glyph correctly. The // interpolated vertex information is also used in the fragment shader to // sample from the glyph atlas. - { - VertexBufferBuilder vertex_builder; - if (!Tessellator{}.Tessellate( - FillType::kPositive, - PathBuilder{} - .AddRect(Rect::MakeXYWH(0.0, 0.0, 1.0, 1.0)) - .TakePath() - .CreatePolyline(), - [&vertex_builder](Point point) { - VS::PerVertexData vtx; - vtx.unit_vertex = point; - vertex_builder.AppendVertex(std::move(vtx)); - })) { - return false; - } - auto dummy = vertex_builder.CreateVertexBuffer(pass.GetTransientsBuffer()); - auto vertex_buffer = dummy; - if (!vertex_buffer) { - return false; - } - cmd.BindVertices(std::move(vertex_buffer)); - } - size_t instance_count = 0u; - std::vector glyph_positions; - std::vector glyph_sizes; - std::vector atlas_positions; - std::vector atlas_glyph_sizes; + const std::vector unit_vertex_points = { + {0, 0}, {1, 0}, {0, 1}, {1, 0}, {0, 1}, {1, 1}, + }; + VertexBufferBuilder vertex_builder; for (const auto& run : frame_.GetRuns()) { auto font = run.GetFont(); auto glyph_size = ISize::Ceil(font.GetMetrics().GetBoundingBox().size); for (const auto& glyph_position : run.GetGlyphPositions()) { - FontGlyphPair font_glyph_pair{font, glyph_position.glyph}; - auto atlas_glyph_pos = atlas->FindFontGlyphPosition(font_glyph_pair); - if (!atlas_glyph_pos.has_value()) { - VALIDATION_LOG << "Could not find glyph position in the atlas."; - return false; + for (const auto& point : unit_vertex_points) { + VS::PerVertexData vtx; + vtx.unit_vertex = point; + + FontGlyphPair font_glyph_pair{font, glyph_position.glyph}; + auto atlas_glyph_pos = atlas->FindFontGlyphPosition(font_glyph_pair); + if (!atlas_glyph_pos.has_value()) { + VALIDATION_LOG << "Could not find glyph position in the atlas."; + return false; + } + vtx.glyph_position = + glyph_position.position + + Point{font.GetMetrics().min_extent.x, font.GetMetrics().ascent}; + vtx.glyph_size = Point{static_cast(glyph_size.width), + static_cast(glyph_size.height)}; + vtx.atlas_position = atlas_glyph_pos->origin; + vtx.atlas_glyph_size = + Point{atlas_glyph_pos->size.width, atlas_glyph_pos->size.height}; + vertex_builder.AppendVertex(std::move(vtx)); } - instance_count++; - glyph_positions.emplace_back(glyph_position.position.Translate( - {font.GetMetrics().min_extent.x, font.GetMetrics().ascent, 0.0})); - glyph_sizes.emplace_back(Point{static_cast(glyph_size.width), - static_cast(glyph_size.height)}); - atlas_positions.emplace_back(atlas_glyph_pos->origin); - atlas_glyph_sizes.emplace_back( - Point{atlas_glyph_pos->size.width, atlas_glyph_pos->size.height}); } } - - cmd.instance_count = instance_count; - VS::BindGlyphPositions( - cmd, pass.GetTransientsBuffer().EmplaceStorageBuffer(glyph_positions)); - VS::BindGlyphSizes( - cmd, pass.GetTransientsBuffer().EmplaceStorageBuffer(glyph_sizes)); - VS::BindAtlasPositions( - cmd, pass.GetTransientsBuffer().EmplaceStorageBuffer(atlas_positions)); - VS::BindAtlasGlyphSizes( - cmd, pass.GetTransientsBuffer().EmplaceStorageBuffer(atlas_glyph_sizes)); + auto vertex_buffer = + vertex_builder.CreateVertexBuffer(pass.GetTransientsBuffer()); + cmd.BindVertices(std::move(vertex_buffer)); if (!pass.AddCommand(cmd)) { return false; diff --git a/impeller/entity/shaders/glyph_atlas.vert b/impeller/entity/shaders/glyph_atlas.vert index 9c83d8524adc8..1e1b04b637fc4 100644 --- a/impeller/entity/shaders/glyph_atlas.vert +++ b/impeller/entity/shaders/glyph_atlas.vert @@ -2,37 +2,17 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -#ifdef IMPELLER_TARGET_OPENGLES - -void main() { - // Unimplemented because the implementation uses instancing and SSBOs. -} - -#else // IMPELLER_TARGET_OPENGLES - uniform FrameInfo { mat4 mvp; vec2 atlas_size; vec4 text_color; } frame_info; -readonly buffer GlyphPositions { - mat4 position[]; -} glyph_positions; - -readonly buffer GlyphSizes { - vec2 size[]; -} glyph_sizes; - -readonly buffer AtlasPositions { - vec2 position[]; -} atlas_positions; - -readonly buffer AtlasGlyphSizes { - vec2 size[]; -} atlas_glyph_sizes; - in vec2 unit_vertex; +in vec2 glyph_position; +in vec2 glyph_size; +in vec2 atlas_position; +in vec2 atlas_glyph_size; out vec2 v_unit_vertex; out vec2 v_atlas_position; @@ -41,26 +21,25 @@ out vec2 v_atlas_size; out vec4 v_text_color; void main() { - // The position to place the glyph. - mat4 glyph_position = glyph_positions.position[gl_InstanceIndex]; - // The size of the glyph. - vec2 glyph_size = glyph_sizes.size[gl_InstanceIndex]; - // The location of the glyph in the atlas. - vec2 glyph_atlas_position = atlas_positions.position[gl_InstanceIndex]; - // The size of the glyph within the atlas. - vec2 glyph_atlas_size = atlas_glyph_sizes.size[gl_InstanceIndex]; - - gl_Position = frame_info.mvp - * glyph_position + vec4 translate = frame_info.mvp[0] * glyph_position.x + + frame_info.mvp[1] * glyph_position.y + + frame_info.mvp[3]; + mat4 translated_mvp = mat4( + frame_info.mvp[0], + frame_info.mvp[1], + frame_info.mvp[2], + vec4( + translate.xyz, + frame_info.mvp[3].w + ) + ); + gl_Position = translated_mvp * vec4(unit_vertex.x * glyph_size.x, unit_vertex.y * glyph_size.y, 0.0, 1.0); v_unit_vertex = unit_vertex; - v_atlas_position = glyph_atlas_position; - v_atlas_glyph_size = glyph_atlas_size; + v_atlas_position = atlas_position; + v_atlas_glyph_size = atlas_glyph_size; v_atlas_size = frame_info.atlas_size; v_text_color = frame_info.text_color; } - -#endif // IMPELLER_TARGET_OPENGLES - diff --git a/impeller/typographer/backends/skia/text_frame_skia.cc b/impeller/typographer/backends/skia/text_frame_skia.cc index 13191b556d7c0..fa3e7406844b5 100644 --- a/impeller/typographer/backends/skia/text_frame_skia.cc +++ b/impeller/typographer/backends/skia/text_frame_skia.cc @@ -52,8 +52,7 @@ TextFrame TextFrameFromTextBlob(sk_sp blob, Scalar scale) { // kFull_Positioning has two scalars per glyph. const SkPoint* glyph_points = run.points(); const auto* point = glyph_points + i; - text_run.AddGlyph(glyphs[i], - Matrix::MakeTranslation({point->x(), point->y()})); + text_run.AddGlyph(glyphs[i], Point{point->x(), point->y()}); } break; case SkTextBlobRunIterator::kRSXform_Positioning: diff --git a/impeller/typographer/text_frame.cc b/impeller/typographer/text_frame.cc index 4fe00ce615b80..fc0f622344bc6 100644 --- a/impeller/typographer/text_frame.cc +++ b/impeller/typographer/text_frame.cc @@ -16,8 +16,8 @@ std::optional TextFrame::GetBounds() const { for (const auto& run : runs_) { const auto glyph_bounds = run.GetFont().GetMetrics().GetBoundingBox(); for (const auto& glyph_position : run.GetGlyphPositions()) { - Vector2 position = glyph_position.position * Vector2(); - Rect glyph_rect = Rect(position + glyph_bounds.origin, glyph_bounds.size); + Rect glyph_rect = Rect(glyph_position.position + glyph_bounds.origin, + glyph_bounds.size); result = result.has_value() ? result->Union(glyph_rect) : glyph_rect; } } diff --git a/impeller/typographer/text_run.cc b/impeller/typographer/text_run.cc index dc21b3865b0ae..abdade6aef645 100644 --- a/impeller/typographer/text_run.cc +++ b/impeller/typographer/text_run.cc @@ -15,7 +15,7 @@ TextRun::TextRun(Font font) : font_(std::move(font)) { TextRun::~TextRun() = default; -bool TextRun::AddGlyph(Glyph glyph, Matrix position) { +bool TextRun::AddGlyph(Glyph glyph, Point position) { glyphs_.emplace_back(GlyphPosition{glyph, position}); return true; } diff --git a/impeller/typographer/text_run.h b/impeller/typographer/text_run.h index 10c5e3ef71289..f8891ba204651 100644 --- a/impeller/typographer/text_run.h +++ b/impeller/typographer/text_run.h @@ -20,9 +20,9 @@ class TextRun { public: struct GlyphPosition { Glyph glyph; - Matrix position; + Point position; - GlyphPosition(Glyph p_glyph, Matrix p_position) + GlyphPosition(Glyph p_glyph, Point p_position) : glyph(p_glyph), position(p_position) {} }; @@ -45,7 +45,7 @@ class TextRun { /// /// @return If the glyph could be added to the run. /// - bool AddGlyph(Glyph glyph, Matrix position); + bool AddGlyph(Glyph glyph, Point position); //---------------------------------------------------------------------------- /// @brief Get the number of glyphs in the run. From 63533c5568791f69b63b9273686a3030520875bf Mon Sep 17 00:00:00 2001 From: Brandon DeRosier Date: Tue, 26 Apr 2022 01:05:08 -0700 Subject: [PATCH 429/433] Set path for linear gradient contents; don't fail renders for failed tessellations (#162) --- impeller/BUILD.gn | 2 + impeller/aiks/paint.cc | 1 + impeller/aiks/paint.h | 4 +- .../contents/linear_gradient_contents.cc | 7 ++- .../entity/contents/solid_color_contents.cc | 2 +- .../entity/contents/solid_stroke_contents.cc | 6 +-- impeller/entity/contents/texture_contents.cc | 6 ++- impeller/entity/entity_unittests.cc | 1 + impeller/geometry/geometry_unittests.cc | 13 +++++ impeller/renderer/renderer_unittests.cc | 3 +- impeller/tessellator/BUILD.gn | 12 ++++- impeller/tessellator/c/tessellator.cc | 10 ++-- impeller/tessellator/tessellator.cc | 18 ++++--- impeller/tessellator/tessellator.h | 14 ++++-- impeller/tessellator/tessellator_unittests.cc | 50 +++++++++++++++++++ 15 files changed, 123 insertions(+), 26 deletions(-) create mode 100644 impeller/tessellator/tessellator_unittests.cc diff --git a/impeller/BUILD.gn b/impeller/BUILD.gn index 46bd75f3f4e2e..4bb5bc1d040ea 100644 --- a/impeller/BUILD.gn +++ b/impeller/BUILD.gn @@ -20,6 +20,7 @@ config("impeller_public_config") { if (is_win) { defines += [ "_USE_MATH_DEFINES", + # TODO(dnfield): https://github.com/flutter/flutter/issues/50053 "_SILENCE_CXX17_CODECVT_HEADER_DEPRECATION_WARNING", ] @@ -55,6 +56,7 @@ executable("impeller_unittests") { "blobcat:blobcat_unittests", "compiler:compiler_unittests", "geometry:geometry_unittests", + "tessellator:tessellator_unittests", ] if (impeller_supports_rendering) { diff --git a/impeller/aiks/paint.cc b/impeller/aiks/paint.cc index b34b6787e56f6..f06129536366d 100644 --- a/impeller/aiks/paint.cc +++ b/impeller/aiks/paint.cc @@ -11,6 +11,7 @@ namespace impeller { std::shared_ptr Paint::CreateContentsForEntity(Path path, bool cover) const { if (contents) { + contents->SetPath(std::move(path)); return contents; } diff --git a/impeller/aiks/paint.h b/impeller/aiks/paint.h index a6650de47a342..3817bab0da623 100644 --- a/impeller/aiks/paint.h +++ b/impeller/aiks/paint.h @@ -9,6 +9,7 @@ #include "flutter/fml/macros.h" #include "impeller/entity/contents/contents.h" #include "impeller/entity/contents/filters/filter_contents.h" +#include "impeller/entity/contents/linear_gradient_contents.h" #include "impeller/entity/contents/solid_stroke_contents.h" #include "impeller/entity/entity.h" #include "impeller/geometry/color.h" @@ -27,6 +28,8 @@ struct Paint { }; Color color = Color::Black(); + std::shared_ptr contents; + Scalar stroke_width = 0.0; SolidStrokeContents::Cap stroke_cap = SolidStrokeContents::Cap::kButt; SolidStrokeContents::Join stroke_join = SolidStrokeContents::Join::kMiter; @@ -34,7 +37,6 @@ struct Paint { Style style = Style::kFill; Entity::BlendMode blend_mode = Entity::BlendMode::kSourceOver; std::optional mask_blur; - std::shared_ptr contents; /// @brief Wrap this paint's configured filters to the given contents. /// @param[in] input The contents to wrap with paint's filters. diff --git a/impeller/entity/contents/linear_gradient_contents.cc b/impeller/entity/contents/linear_gradient_contents.cc index 91e7912117842..d4188efbecad4 100644 --- a/impeller/entity/contents/linear_gradient_contents.cc +++ b/impeller/entity/contents/linear_gradient_contents.cc @@ -4,6 +4,7 @@ #include "linear_gradient_contents.h" +#include "flutter/fml/logging.h" #include "impeller/entity/contents/content_context.h" #include "impeller/entity/entity.h" #include "impeller/renderer/render_pass.h" @@ -58,7 +59,11 @@ bool LinearGradientContents::Render(const ContentContext& renderer, vtx.vertices = point; vertices_builder.AppendVertex(vtx); }); - if (!result) { + + if (result == Tessellator::Result::kInputError) { + return true; + } + if (result == Tessellator::Result::kTessellationError) { return false; } } diff --git a/impeller/entity/contents/solid_color_contents.cc b/impeller/entity/contents/solid_color_contents.cc index fce8d7ab7f868..0fe07b756c73a 100644 --- a/impeller/entity/contents/solid_color_contents.cc +++ b/impeller/entity/contents/solid_color_contents.cc @@ -50,7 +50,7 @@ VertexBuffer SolidColorContents::CreateSolidFillVertices(const Path& path, vtx.vertices = point; vtx_builder.AppendVertex(vtx); }); - if (!tesselation_result) { + if (tesselation_result != Tessellator::Result::kSuccess) { return {}; } diff --git a/impeller/entity/contents/solid_stroke_contents.cc b/impeller/entity/contents/solid_stroke_contents.cc index 1cfbfb9f8bdc8..a7b8507b977b0 100644 --- a/impeller/entity/contents/solid_stroke_contents.cc +++ b/impeller/entity/contents/solid_stroke_contents.cc @@ -201,9 +201,9 @@ bool SolidStrokeContents::Render(const ContentContext& renderer, auto smoothing = SmoothingApproximation( 5.0 / (stroke_size_ * entity.GetTransformation().GetMaxBasisLength()), 0.0, 0.0); - cmd.BindVertices(CreateSolidStrokeVertices( - path_, pass.GetTransientsBuffer(), cap_proc_, join_proc_, - miter_limit_, smoothing)); + cmd.BindVertices(CreateSolidStrokeVertices(path_, pass.GetTransientsBuffer(), + cap_proc_, join_proc_, + miter_limit_, smoothing)); VS::BindFrameInfo(cmd, pass.GetTransientsBuffer().EmplaceUniform(frame_info)); VS::BindStrokeInfo(cmd, pass.GetTransientsBuffer().EmplaceUniform(stroke_info)); diff --git a/impeller/entity/contents/texture_contents.cc b/impeller/entity/contents/texture_contents.cc index f66936c6b3704..ffc926b6b7edc 100644 --- a/impeller/entity/contents/texture_contents.cc +++ b/impeller/entity/contents/texture_contents.cc @@ -81,7 +81,11 @@ bool TextureContents::Render(const ContentContext& renderer, texture_size; vertex_builder.AppendVertex(data); }); - if (!tess_result) { + + if (tess_result == Tessellator::Result::kInputError) { + return true; + } + if (tess_result == Tessellator::Result::kTessellationError) { return false; } } diff --git a/impeller/entity/entity_unittests.cc b/impeller/entity/entity_unittests.cc index 549e5b4fb4876..50998e6dcc26e 100644 --- a/impeller/entity/entity_unittests.cc +++ b/impeller/entity/entity_unittests.cc @@ -18,6 +18,7 @@ #include "impeller/playground/widgets.h" #include "impeller/renderer/render_pass.h" #include "impeller/renderer/vertex_buffer_builder.h" +#include "impeller/tessellator/tessellator.h" #include "third_party/imgui/imgui.h" namespace impeller { diff --git a/impeller/geometry/geometry_unittests.cc b/impeller/geometry/geometry_unittests.cc index 82eb5633b50b1..b840572799bac 100644 --- a/impeller/geometry/geometry_unittests.cc +++ b/impeller/geometry/geometry_unittests.cc @@ -264,6 +264,19 @@ TEST(GeometryTest, QuaternionLerp) { ASSERT_QUATERNION_NEAR(q3, expected); } +TEST(GeometryTest, EmptyPath) { + auto path = PathBuilder{}.TakePath(); + ASSERT_EQ(path.GetComponentCount(), 1u); + + ContourComponent c; + path.GetContourComponentAtIndex(0, c); + ASSERT_POINT_NEAR(c.destination, Point()); + + Path::Polyline polyline = path.CreatePolyline(); + ASSERT_TRUE(polyline.points.empty()); + ASSERT_TRUE(polyline.contours.empty()); +} + TEST(GeometryTest, SimplePath) { Path path; diff --git a/impeller/renderer/renderer_unittests.cc b/impeller/renderer/renderer_unittests.cc index 0bd6640223221..721d1f478502c 100644 --- a/impeller/renderer/renderer_unittests.cc +++ b/impeller/renderer/renderer_unittests.cc @@ -279,7 +279,8 @@ TEST_P(RendererTest, CanRenderInstanced) { VertexBufferBuilder builder; - ASSERT_TRUE( + ASSERT_EQ( + Tessellator::Result::kSuccess, Tessellator{}.Tessellate(FillType::kPositive, PathBuilder{} .AddRect(Rect::MakeXYWH(10, 10, 100, 100)) diff --git a/impeller/tessellator/BUILD.gn b/impeller/tessellator/BUILD.gn index 7a615464ac5da..a3d1b3ef1c42e 100644 --- a/impeller/tessellator/BUILD.gn +++ b/impeller/tessellator/BUILD.gn @@ -23,10 +23,9 @@ impeller_component("tessellator_shared") { output_name = "tessellator" } - sources = [ - "c/tessellator.h", "c/tessellator.cc", + "c/tessellator.h", "tessellator.cc", "tessellator.h", ] @@ -36,3 +35,12 @@ impeller_component("tessellator_shared") { "//third_party/libtess2", ] } + +impeller_component("tessellator_unittests") { + testonly = true + sources = [ "tessellator_unittests.cc" ] + deps = [ + ":tessellator", + "//flutter/testing", + ] +} diff --git a/impeller/tessellator/c/tessellator.cc b/impeller/tessellator/c/tessellator.cc index e6c4661ebae7e..bb11fb145c84d 100644 --- a/impeller/tessellator/c/tessellator.cc +++ b/impeller/tessellator/c/tessellator.cc @@ -47,11 +47,11 @@ struct Vertices* Tessellate(PathBuilder* builder, auto polyline = path.CreatePolyline(smoothing); std::vector points; - if (!Tessellator{}.Tessellate(path.GetFillType(), polyline, - [&points](Point vertex) { - points.push_back(vertex.x); - points.push_back(vertex.y); - })) { + if (Tessellator{}.Tessellate(path.GetFillType(), polyline, + [&points](Point vertex) { + points.push_back(vertex.x); + points.push_back(vertex.y); + }) != Tessellator::Result::kSuccess) { return nullptr; } diff --git a/impeller/tessellator/tessellator.cc b/impeller/tessellator/tessellator.cc index 542cefbbb2737..fe1569365e3ca 100644 --- a/impeller/tessellator/tessellator.cc +++ b/impeller/tessellator/tessellator.cc @@ -34,11 +34,15 @@ static void DestroyTessellator(TESStesselator* tessellator) { } } -bool Tessellator::Tessellate(FillType fill_type, - const Path::Polyline& polyline, - VertexCallback callback) const { +Tessellator::Result Tessellator::Tessellate(FillType fill_type, + const Path::Polyline& polyline, + VertexCallback callback) const { if (!callback) { - return false; + return Result::kInputError; + } + + if (polyline.points.empty()) { + return Result::kInputError; } using CTessellator = @@ -49,7 +53,7 @@ bool Tessellator::Tessellate(FillType fill_type, DestroyTessellator); if (!tessellator) { - return false; + return Result::kTessellationError; } constexpr int kVertexSize = 2; @@ -85,7 +89,7 @@ bool Tessellator::Tessellate(FillType fill_type, ); if (result != 1) { - return false; + return Result::kTessellationError; } // TODO(csg): This copy can be elided entirely for the current use case. @@ -109,7 +113,7 @@ bool Tessellator::Tessellate(FillType fill_type, callback(vtx); } - return true; + return Result::kSuccess; } } // namespace impeller diff --git a/impeller/tessellator/tessellator.h b/impeller/tessellator/tessellator.h index d7c00132eee94..e42dfcbe0766c 100644 --- a/impeller/tessellator/tessellator.h +++ b/impeller/tessellator/tessellator.h @@ -26,6 +26,12 @@ enum class WindingOrder { /// class Tessellator { public: + enum class Result { + kSuccess, + kInputError, + kTessellationError, + }; + Tessellator(); ~Tessellator(); @@ -39,11 +45,11 @@ class Tessellator { /// @param[in] polyline The polyline /// @param[in] callback The callback /// - /// @return If tessellation was successful. + /// @return The result status of the tessellation. /// - bool Tessellate(FillType fill_type, - const Path::Polyline& polyline, - VertexCallback callback) const; + Tessellator::Result Tessellate(FillType fill_type, + const Path::Polyline& polyline, + VertexCallback callback) const; private: FML_DISALLOW_COPY_AND_ASSIGN(Tessellator); diff --git a/impeller/tessellator/tessellator_unittests.cc b/impeller/tessellator/tessellator_unittests.cc new file mode 100644 index 0000000000000..6f0b6bab804a5 --- /dev/null +++ b/impeller/tessellator/tessellator_unittests.cc @@ -0,0 +1,50 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "flutter/testing/testing.h" +#include "gtest/gtest.h" +#include "impeller/geometry/path_builder.h" +#include "impeller/tessellator/tessellator.h" + +namespace impeller { +namespace testing { + +TEST(TessellatorTest, TessellatorReturnsCorrectResultStatus) { + // Zero points. + { + Tessellator t; + auto polyline = PathBuilder{}.TakePath().CreatePolyline(); + Tessellator::Result result = + t.Tessellate(FillType::kPositive, polyline, [](Point point) {}); + + ASSERT_EQ(polyline.points.size(), 0u); + ASSERT_EQ(result, Tessellator::Result::kInputError); + } + + // One point. + { + Tessellator t; + auto polyline = PathBuilder{}.LineTo({0, 0}).TakePath().CreatePolyline(); + Tessellator::Result result = + t.Tessellate(FillType::kPositive, polyline, [](Point point) {}); + + ASSERT_EQ(polyline.points.size(), 1u); + ASSERT_EQ(result, Tessellator::Result::kSuccess); + } + + // Two points. + { + Tessellator t; + auto polyline = + PathBuilder{}.AddLine({0, 0}, {0, 1}).TakePath().CreatePolyline(); + Tessellator::Result result = + t.Tessellate(FillType::kPositive, polyline, [](Point point) {}); + + ASSERT_EQ(polyline.points.size(), 2u); + ASSERT_EQ(result, Tessellator::Result::kSuccess); + } +} + +} // namespace testing +} // namespace impeller From b790919ef15f9da139a3a40bd473f579cca34db8 Mon Sep 17 00:00:00 2001 From: Kaushik Iska Date: Tue, 26 Apr 2022 11:12:01 -0400 Subject: [PATCH 430/433] Check if sub_command_buffer is null before setting label (#163) --- impeller/entity/entity_pass.cc | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/impeller/entity/entity_pass.cc b/impeller/entity/entity_pass.cc index 382cef3a03386..eec666bc84d9e 100644 --- a/impeller/entity/entity_pass.cc +++ b/impeller/entity/entity_pass.cc @@ -197,12 +197,12 @@ bool EntityPass::Render(ContentContext& renderer, auto sub_command_buffer = context->CreateRenderCommandBuffer(); - sub_command_buffer->SetLabel("Offscreen Command Buffer"); - if (!sub_command_buffer) { return false; } + sub_command_buffer->SetLabel("Offscreen Command Buffer"); + auto sub_renderpass = sub_command_buffer->CreateRenderPass(subpass_target); From 9436240a293cab790e3bff7c02b95968db2320a4 Mon Sep 17 00:00:00 2001 From: Dan Field Date: Tue, 26 Apr 2022 12:44:38 -0700 Subject: [PATCH 431/433] format --- impeller/aiks/aiks_playground.h | 3 ++- impeller/aiks/canvas.cc | 3 ++- impeller/compiler/BUILD.gn | 4 +--- impeller/entity/contents/filters/filter_contents.cc | 2 +- impeller/geometry/path.cc | 3 ++- 5 files changed, 8 insertions(+), 7 deletions(-) diff --git a/impeller/aiks/aiks_playground.h b/impeller/aiks/aiks_playground.h index 4a7288fd18471..4f6157de40f59 100644 --- a/impeller/aiks/aiks_playground.h +++ b/impeller/aiks/aiks_playground.h @@ -13,7 +13,8 @@ namespace impeller { class AiksPlayground : public Playground { public: - using AiksPlaygroundCallback = std::function; + using AiksPlaygroundCallback = + std::function; AiksPlayground(); diff --git a/impeller/aiks/canvas.cc b/impeller/aiks/canvas.cc index 05bc7c06d06f4..a6f881553ea40 100644 --- a/impeller/aiks/canvas.cc +++ b/impeller/aiks/canvas.cc @@ -112,7 +112,8 @@ void Canvas::DrawPath(Path path, Paint paint) { entity.SetTransformation(GetCurrentTransformation()); entity.SetStencilDepth(GetStencilDepth()); entity.SetBlendMode(paint.blend_mode); - entity.SetContents(paint.WithFilters(paint.CreateContentsForEntity(std::move(path)))); + entity.SetContents( + paint.WithFilters(paint.CreateContentsForEntity(std::move(path)))); GetCurrentPass().AddEntity(std::move(entity)); } diff --git a/impeller/compiler/BUILD.gn b/impeller/compiler/BUILD.gn index 501a8bb5efd27..1ef64be8f7735 100644 --- a/impeller/compiler/BUILD.gn +++ b/impeller/compiler/BUILD.gn @@ -42,9 +42,7 @@ impeller_component("impellerc") { sources = [ "impellerc_main.cc" ] - deps = [ - ":compiler_lib", - ] + deps = [ ":compiler_lib" ] } group("compiler") { diff --git a/impeller/entity/contents/filters/filter_contents.cc b/impeller/entity/contents/filters/filter_contents.cc index 3196c42678561..1f50d5ae47080 100644 --- a/impeller/entity/contents/filters/filter_contents.cc +++ b/impeller/entity/contents/filters/filter_contents.cc @@ -16,8 +16,8 @@ #include "impeller/entity/contents/content_context.h" #include "impeller/entity/contents/filters/blend_filter_contents.h" #include "impeller/entity/contents/filters/border_mask_blur_filter_contents.h" -#include "impeller/entity/contents/filters/inputs/filter_input.h" #include "impeller/entity/contents/filters/gaussian_blur_filter_contents.h" +#include "impeller/entity/contents/filters/inputs/filter_input.h" #include "impeller/entity/contents/texture_contents.h" #include "impeller/entity/entity.h" #include "impeller/geometry/path_builder.h" diff --git a/impeller/geometry/path.cc b/impeller/geometry/path.cc index 9e6c52e0ab7a8..69b1cd46a4927 100644 --- a/impeller/geometry/path.cc +++ b/impeller/geometry/path.cc @@ -286,7 +286,8 @@ std::optional Path::GetBoundingBox() const { return Rect{min.x, min.y, difference.x, difference.y}; } -std::optional Path::GetTransformedBoundingBox(const Matrix& transform) const { +std::optional Path::GetTransformedBoundingBox( + const Matrix& transform) const { auto bounds = GetBoundingBox(); if (!bounds.has_value()) { return std::nullopt; From d0985869e8afcc935bedf910f37ad95d580e858b Mon Sep 17 00:00:00 2001 From: Dan Field Date: Tue, 26 Apr 2022 13:14:20 -0700 Subject: [PATCH 432/433] Missing files --- .gitignore | 3 --- DEPS | 3 --- impeller/LICENSE | 25 ------------------------- 3 files changed, 31 deletions(-) delete mode 100644 impeller/LICENSE diff --git a/.gitignore b/.gitignore index edb6b24beba6f..ed1194e1d9e5b 100644 --- a/.gitignore +++ b/.gitignore @@ -133,6 +133,3 @@ app.*.symbols # Prebuilt binaries. /prebuilts/ - -# DEPS'd in impeller -/impeller/ diff --git a/DEPS b/DEPS index 12b17fdea2555..6b0811d39bccf 100644 --- a/DEPS +++ b/DEPS @@ -114,9 +114,6 @@ allowed_hosts = [ deps = { 'src': 'https://github.com/flutter/buildroot.git' + '@' + '79aa4b0233325e6590dcd45a9af1526d9d2ad13b', - 'src/flutter/impeller': - Var('github_git') + '/flutter/impeller' + '@' + '3fc18967345f5e4971259a9efabb11731b751df3', - # Fuchsia compatibility # # The dependencies in this section should match the layout in the Fuchsia gn diff --git a/impeller/LICENSE b/impeller/LICENSE deleted file mode 100644 index c6823b81eb845..0000000000000 --- a/impeller/LICENSE +++ /dev/null @@ -1,25 +0,0 @@ -Copyright 2013 The Flutter Authors. All rights reserved. - -Redistribution and use in source and binary forms, with or without modification, -are permitted provided that the following conditions are met: - - * Redistributions of source code must retain the above copyright - notice, this list of conditions and the following disclaimer. - * Redistributions in binary form must reproduce the above - copyright notice, this list of conditions and the following - disclaimer in the documentation and/or other materials provided - with the distribution. - * Neither the name of Google Inc. nor the names of its - contributors may be used to endorse or promote products derived - from this software without specific prior written permission. - -THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND -ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED -WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE -DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR -ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES -(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; -LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON -ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS -SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. From 7621fb2a9a02479fedcf567ddfdd4de6d7a90009 Mon Sep 17 00:00:00 2001 From: Dan Field Date: Tue, 26 Apr 2022 14:08:47 -0700 Subject: [PATCH 433/433] temporarily increase clang_tidy test limits --- tools/clang_tidy/test/clang_tidy_test.dart | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tools/clang_tidy/test/clang_tidy_test.dart b/tools/clang_tidy/test/clang_tidy_test.dart index 76e0e7c775f13..1dfc9d067dd30 100644 --- a/tools/clang_tidy/test/clang_tidy_test.dart +++ b/tools/clang_tidy/test/clang_tidy_test.dart @@ -148,7 +148,7 @@ Future main(List args) async { errSink: errBuffer, ); final List fileList = await clangTidy.computeChangedFiles(); - expect(fileList.length, lessThan(300)); + expect(fileList.length, lessThan(500)); }); test('No Commands are produced when no files changed', () async {

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